diff --git a/Engine/lib/.gitignore b/Engine/lib/.gitignore new file mode 100644 index 000000000..23d0cfd6e --- /dev/null +++ b/Engine/lib/.gitignore @@ -0,0 +1,4 @@ +############# +## Torque +############# +compiled/ diff --git a/Engine/lib/Torque3D/msvc/torque3d.def b/Engine/lib/Torque3D/msvc/torque3d.def new file mode 100644 index 000000000..63c646047 --- /dev/null +++ b/Engine/lib/Torque3D/msvc/torque3d.def @@ -0,0 +1,48 @@ +EXPORTS + +torque_engineinit +torque_enginetick +torque_enginesignalshutdown +torque_engineshutdown +torque_setwebdeployment +torque_reset +torque_getvariable +torque_setvariable +torque_evaluate +torque_exportstringcallback +torque_callscriptfunction +torque_addsecurefunction +torque_callsecurefunction +torque_resizewindow +torque_winmain +torque_gethwnd +torque_directmessage + +D3D_GetBackBufferNoRef + +SimObject_GetName +SimObject_GetId +SimObject_GetClassName +SimObject_GetFieldList +SimObject_IsLocked +SimObject_SetDataField +SimObject_GetDataField +SimObject_InspectPreApply +SimObject_InspectPostApply +Con_AddConsumer +Con_RemoveConsumer +Con_AddCommand_String +ConsoleBaseType_GetTypeById +ConsoleBaseType_GetTypeId +ConsoleBaseType_GetTypeSize +ConsoleBaseType_GetTypeName +ConsoleBaseType_GetInspectorFieldType +ConsoleBaseType_SetData +ConsoleBaseType_GetData +AbstractClassRep_GetCommonParent +AbstractClassRep_FindClassRep +AbstractClassRep_GetFieldStructSize +Sim_FindObjectByString +Sim_FindObjectById +SimSet_Begin +SimSet_End \ No newline at end of file diff --git a/Engine/lib/Torque3D/msvc/torque3d_scripting.def b/Engine/lib/Torque3D/msvc/torque3d_scripting.def new file mode 100644 index 000000000..28461727c --- /dev/null +++ b/Engine/lib/Torque3D/msvc/torque3d_scripting.def @@ -0,0 +1,79 @@ +EXPORTS + +torque_engineinit +torque_enginetick +torque_enginesignalshutdown +torque_engineshutdown +torque_setwebdeployment +torque_reset +torque_getvariable +torque_setvariable +torque_evaluate +torque_exportstringcallback +torque_callscriptfunction +torque_addsecurefunction +torque_callsecurefunction +torque_resizewindow +torque_winmain +torque_gethwnd +torque_directmessage + +script_getconsolexml + +script_get_namespace_entry +script_get_stringtable_entry + +script_call_namespace_entry_string +script_call_namespace_entry_void +script_call_namespace_entry_bool +script_call_namespace_entry_float +script_call_namespace_entry_int + +script_simobject_get_id +script_simobject_find + +script_simobject_getfield_string +script_simobject_setfield_string +script_simobject_getfield_bool +script_simobject_setfield_bool +script_simobject_getfield_int +script_simobject_setfield_int +script_simobject_getfield_float +script_simobject_setfield_float + +script_export_callback_void +script_export_callback_string +script_export_callback_int +script_export_callback_float +script_export_callback_bool + +script_input_event + +D3D_GetBackBufferNoRef + +SimObject_GetName +SimObject_GetId +SimObject_GetClassName +SimObject_GetFieldList +SimObject_IsLocked +SimObject_SetDataField +SimObject_GetDataField +SimObject_InspectPreApply +SimObject_InspectPostApply +Con_AddConsumer +Con_RemoveConsumer +Con_AddCommand_String +ConsoleBaseType_GetTypeById +ConsoleBaseType_GetTypeId +ConsoleBaseType_GetTypeSize +ConsoleBaseType_GetTypeName +ConsoleBaseType_GetInspectorFieldType +ConsoleBaseType_SetData +ConsoleBaseType_GetData +AbstractClassRep_GetCommonParent +AbstractClassRep_FindClassRep +AbstractClassRep_GetFieldStructSize +Sim_FindObjectByString +Sim_FindObjectById +SimSet_Begin +SimSet_End \ No newline at end of file diff --git a/Engine/lib/buildFiles/VisualStudio 2005/projects/readme.txt b/Engine/lib/buildFiles/VisualStudio 2005/projects/readme.txt new file mode 100644 index 000000000..5ec93ea5b --- /dev/null +++ b/Engine/lib/buildFiles/VisualStudio 2005/projects/readme.txt @@ -0,0 +1 @@ +Ready to hold your generated projects diff --git a/Engine/lib/buildFiles/VisualStudio 2008/projects/readme.txt b/Engine/lib/buildFiles/VisualStudio 2008/projects/readme.txt new file mode 100644 index 000000000..5ec93ea5b --- /dev/null +++ b/Engine/lib/buildFiles/VisualStudio 2008/projects/readme.txt @@ -0,0 +1 @@ +Ready to hold your generated projects diff --git a/Engine/lib/buildFiles/Xcode/projects/readme.txt b/Engine/lib/buildFiles/Xcode/projects/readme.txt new file mode 100644 index 000000000..5ec93ea5b --- /dev/null +++ b/Engine/lib/buildFiles/Xcode/projects/readme.txt @@ -0,0 +1 @@ +Ready to hold your generated projects diff --git a/Engine/lib/buildFiles/compile.bat b/Engine/lib/buildFiles/compile.bat new file mode 100644 index 000000000..639332035 --- /dev/null +++ b/Engine/lib/buildFiles/compile.bat @@ -0,0 +1,66 @@ +@echo off + +REM Handle our optional parameters +SET COMPILER=%1 +SET CONFIG=%2 + +IF NOT DEFINED COMPILER SET COMPILER=VS2008 +IF NOT DEFINED CONFIG SET CONFIG=Release + +REM Setting up some variables + +REM Detecting the correct Program Files +IF DEFINED PROGRAMFILES(X86) SET PROGRAMROOT=%ProgramFiles(x86)% +IF NOT DEFINED PROGRAMROOT SET PROGRAMROOT=%ProgramFiles% + +REM First the defaults (set up for VS2008 by default) +SET ENVVAR="%PROGRAMROOT%\Microsoft Visual Studio 9.0\VC\vcvarsall.bat" +SET BUILDCMD=devenv.com +SET OPTIONS= /build "%CONFIG%|Win32" +SET BUILDDIR="VisualStudio 2008" + +REM Handle the non-defaults +IF %COMPILER% == VS2005 SET ENVVAR="%PROGRAMROOT%\Microsoft Visual Studio 8\VC\vcvarsall.bat" +IF %COMPILER% == VS2010 SET ENVVAR="%PROGRAMROOT%\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" + +IF EXIST "%PROGRAMROOT%\Xoreax\IncrediBuild\BuildConsole.exe" SET BUILDCMD="%PROGRAMROOT%\Xoreax\IncrediBuild\BuildConsole.exe" +IF EXIST "%PROGRAMROOT%\Xoreax\IncrediBuild\BuildConsole.exe" SET OPTIONS=/build "%CONFIG%|Win32" + +IF %COMPILER% == VS2005 SET BUILDDIR="VisualStudio 2005" +IF %COMPILER% == VS2010 SET BUILDDIR="VisualStudio 2010" + + +echo Building all solutions under %COMPILER% with the %CONFIG% configuration + +echo Initializing %COMPILER% environment variables... +call %ENVVAR% + +echo Initializing the DirectX SDK environment variables... + +IF "%DXSDK_DIR%" == "" goto error_no_DXSDK_DIR +call "%DXSDK_DIR%Utilities\Bin\dx_setenv.cmd" x86 + +echo Moving to our build directory +cd %BUILDDIR% + +echo - Building +for %%a in (*.sln) do %BUILDCMD% "%%a" %OPTIONS% & IF ERRORLEVEL 1 goto error_compile + +REM It is just polite for a batch file to leave you in the same dir you started in +cd .. + +REM We were successful in everything so go to the end +goto :end + +:error_no_DXSDK_DIR +@echo ERROR: DXSDK_DIR variable is not set. Make sure the DirectX SDK is installed properly. +@goto end_error + +:error_compile +@echo ERROR: There was an error compiling a solution in %CD% +@goto end_error + +:end_error +EXIT 1 + +:end \ No newline at end of file diff --git a/Engine/lib/buildFiles/config/libs.conf b/Engine/lib/buildFiles/config/libs.conf new file mode 100644 index 000000000..f84e8c8f5 --- /dev/null +++ b/Engine/lib/buildFiles/config/libs.conf @@ -0,0 +1,82 @@ + diff --git a/Engine/lib/buildFiles/config/libs.mac.conf b/Engine/lib/buildFiles/config/libs.mac.conf new file mode 100644 index 000000000..2363ebdba --- /dev/null +++ b/Engine/lib/buildFiles/config/libs.mac.conf @@ -0,0 +1,80 @@ + diff --git a/Engine/lib/bullet/AUTHORS b/Engine/lib/bullet/AUTHORS new file mode 100644 index 000000000..f2cc86dd7 --- /dev/null +++ b/Engine/lib/bullet/AUTHORS @@ -0,0 +1,22 @@ + +Bullet Physics Library is an open source project with help from the community at the Physics Forum +See the forum at http://bulletphysics.com + +The project was started by Erwin Coumans + +Following people contributed to Bullet +(random order, please let us know on the forum if your name should be in this list) + +Gino van den Bergen: LinearMath classes +Christer Ericson: parts of the voronoi simplex solver +Simon Hobbs: 3d axis sweep and prune, Extras/SATCollision, separating axis theorem + SIMD code +Dirk Gregorius: generic D6 constraint +Erin Catto: accumulated impulse in sequential impulse +Nathanael Presson: EPA penetration depth calculation +Francisco Leon: GIMPACT Concave Concave collision +Joerg Henrichs: make buildsystem (work in progress) +Eric Sunshine: jam + msvcgen buildsystem +Steve Baker: GPU physics and general implementation improvements +Jay Lee: Double precision support +KleMiX, aka Vsevolod Klementjev, managed version, rewritten in C# for XNA +Erwin Coumans: most other source code diff --git a/Engine/lib/bullet/BulletLicense.txt b/Engine/lib/bullet/BulletLicense.txt new file mode 100644 index 000000000..c3ec68c21 --- /dev/null +++ b/Engine/lib/bullet/BulletLicense.txt @@ -0,0 +1,17 @@ +/* +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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. +*/ + + +Free for commercial use, but please mail bullet@erwincoumans.com to report projects, and join the forum at +www.continuousphysics.com/Bullet/phpBB2 diff --git a/Engine/lib/bullet/Bullet_Faq.pdf b/Engine/lib/bullet/Bullet_Faq.pdf new file mode 100644 index 000000000..c4e8da94f Binary files /dev/null and b/Engine/lib/bullet/Bullet_Faq.pdf differ diff --git a/Engine/lib/bullet/CMakeLists.txt b/Engine/lib/bullet/CMakeLists.txt new file mode 100644 index 000000000..1acafce20 --- /dev/null +++ b/Engine/lib/bullet/CMakeLists.txt @@ -0,0 +1,72 @@ +cmake_minimum_required(VERSION 2.4) + +PROJECT(BULLET_PHYSICS) +SET(BULLET_VERSION 2.75) + +IF (NOT CMAKE_BUILD_TYPE) +# SET(CMAKE_BUILD_TYPE "Debug") + SET(CMAKE_BUILD_TYPE "Release") +ENDIF (NOT CMAKE_BUILD_TYPE) + +# string (REPLACE "/D_WINDOWS" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) +remove_definitions(-D_WINDOWS ) + +IF(COMMAND cmake_policy) + cmake_policy(SET CMP0003 NEW) +ENDIF(COMMAND cmake_policy) + + +# This is the shortcut to finding GLU, GLUT and OpenGL if they are properly installed on your system +# This should be the case. + +FIND_PACKAGE(OpenGL) +IF (OPENGL_FOUND) +MESSAGE("OPENGL FOUND") +MESSAGE(${OPENGL_LIBRARIES}) +ELSE (OPENGL_FOUND) +MESSAGE("OPENGL NOT FOUND") +SET(OPENGL_gl_LIBRARY opengl32) +SET(OPENGL_glu_LIBRARY glu32) +ENDIF (OPENGL_FOUND) + +# ADD_DEFINITIONS(-DBT_USE_FREEGLUT) + +FIND_PACKAGE(GLU) + +FIND_PACKAGE(GLUT) +IF (GLUT_FOUND) +MESSAGE("GLUT FOUND") +MESSAGE(${GLUT_glut_LIBRARY}) +ELSE (GLUT_FOUND) + +IF (MINGW) +MESSAGE ("GLUT NOT FOUND not found, trying to use MINGW glut32") +SET(GLUT_glut_LIBRARY glut32) +ENDIF (MINGW) + +IF (MSVC) +MESSAGE ("GLUT NOT FOUND, trying to use Bullet/Glut/glut32.lib for MSVC") +SET(GLUT_glut_LIBRARY ${BULLET_PHYSICS_SOURCE_DIR}/Glut/glut32.lib) +ENDIF (MSVC) +ENDIF (GLUT_FOUND) + + +IF (WIN32) + INCLUDE_DIRECTORIES(${BULLET_PHYSICS_SOURCE_DIR}/Glut) +ELSE (WIN32) + # This is the lines for linux. This should always work if everything is installed and working fine. + INCLUDE_DIRECTORIES(/usr/include /usr/local/include ${GLUT_INCLUDE_DIR}) +ENDIF (WIN32) + + +OPTION(BUILD_DEMOS "Set when you want to build the demos" ON) +IF(BUILD_DEMOS) + SUBDIRS(Demos) +ENDIF(BUILD_DEMOS) + +OPTION(BUILD_EXTRAS "Set when you want to build the extras" ON) +IF(BUILD_EXTRAS) + SUBDIRS(Extras) +ENDIF(BUILD_EXTRAS) + +SUBDIRS(src) diff --git a/Engine/lib/bullet/ChangeLog b/Engine/lib/bullet/ChangeLog new file mode 100644 index 000000000..8d154dc09 --- /dev/null +++ b/Engine/lib/bullet/ChangeLog @@ -0,0 +1,745 @@ +Bullet Continuous Collision Detection and Physics Library +Primary author and maintainer: Erwin Coumans + +Please see http://code.google.com/p/bullet/source/list for more complete log in Subversion + +2009 September 17 + - Minor update to Bullet 2.75 release, revision 1770 + - Support for btConvex2dShape, check out Bullet/Demos/Box2dDemo + - Minor fix in btGjkPairDetector + - Initialize world transform for btCollisionShape in constructor + + +2009 September 6 + - Bullet 2.75 release + - Added SPH fluid simulation in Extras, not integrated with rigid body / soft body yet + Thanks to Rama Hoetzlein to make this contribution available under the ZLib license + - add special capsule-capsule collider code in btConvexConvexCollisionAlgorithm, to speed up capsule-ragdolls + - soft body improvement: faster building of bending constraints + - soft body improvement: allow to disable/enable cluster self-collision + - soft body fix: 'exploding' soft bodies when using cluster collision + - fix some degenerate cases in continuous convex cast, could impact ray cast/convex cast + Thanks to Jacob Langford for the report and reproduction cases, see http://code.google.com/p/bullet/issues/detail?id=250&can=1&start=200 + - re-enabled split impulse + - added btHinge2Constraint, btUniversalConstraint, btGeneric6DofSpringConstraint + - demonstrate 2D physics with 2D/3D object interaction + + +2008 December 2 + - Fix contact refresh issues with btCompoundShape, introduced with btDbvt acceleration structure in btCompoundCollisionAlgorithm + - Made btSequentialImpulseConstraintSolver 100% compatible with ODE quickstep + constraints can use 'solveConstraint' method or 'getInfo/getInfo2' + +2008 November 30 + - Add highly optimized SIMD branchless PGS/SI solver innerloop + +2008 November 12 + - Add compound shape export to BulletColladaConverter + Thanks to JamesH for the report: http://www.bulletphysics.com/Bullet/phpBB3/viewtopic.php?f=12&t=2840 + - Fix compiler build for Visual Studio 6 + Thanks to JoF for the report: http://www.bulletphysics.com/Bullet/phpBB3/viewtopic.php?f=9&t=2841 + +2008 November 11 + - Add CProfileManager::dumpAll() to dump performance statistics to console using printf. + - Add support for interaction between btSoftBody and btCollisionObject/btGhostObject + +2008 November 8 + - Fix PosixThreadSupport + - Add improved btHeightfieldTerrainShape support and new Demos/TerrainDemo + Thanks to tomva, http://code.google.com/p/bullet/issues/detail?id=63&can=1 + - Moved kinematic character controller from Demos/CharacterDemo into src/BulletDynamics/Character/btKinematicCharacterController.cpp + +2008 November 6 + - reduced default memory pool allocation from 40Mb to 3Mb. This should be more suitable for all platforms, including iPhone + - improved CUDA broadphase + - IBM Cell SDK 3.x support, fix ibmsdk Makefiles + - improved CMake support with 'install' and 'framework option + +2008 November 4 + - add btAxisSweep::resetPool to avoid non-determinism due to shuffled linked list + Thanks to Ole for the contribution, + +2008 October 30 + - disabled btTriangleMesh duplicate search by default, it is extremely slow + - added Extras/IFF binary chunk serialization library as preparation for in-game native platform serialization (planned COLLADA DOM -> IFF converter) + +2008 October 20 + - added SCE Physics Effects box-box collision detection for SPU/BulletMultiThreaded version + See Bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/boxBoxDistance.cpp + Thanks to Sony Computer Entertainment Japan, SCEI for the contribution + +2008 October 17 + - Added btGhostObject support, this helps character controller, explosions, triggers and other local spatial queries + +2008 October 10 + - Moved aabb to btBroadphaseProxy, improves rayTest dramatically. Further raytest improvements using the broadphase acceleration structures are planned + - Moved BulletMultiThreaded from Extras to /src/BulletMultiThreaded for better integration + + +2008 October 3 + - Add support for autoconf automake + ./autogen.sh and ./configure will create both Makefile and Jamfile. CMake and autogenerated Visual Studio projectfiles remain supported too. + - Improved ColladaConverter: plane shape export, and callback for shape construction to allow deletion of memory + +2008 Sept 30 + - Improved Soft Body support, fixed issues related to soft body colliding against concave triangle meshes + - Shared more code between regular version and SPU/BulletMultiThreaded, in particular GJK/EPA + +2008 Sept 28 + - Fixed rotation issues in Dynamic Maya Plugin + +2008 Sept 11 + - Enable CCD motion clamping for btDiscreteDynamicsWorld, to avoid tunneling. A more advanced solution will be implemented in btContinuousDynamicsWorld. + +2008 Sept 7 + - Add btScaledBvhTriangleMeshShape, to allow re-use of btBvhTriangleMeshShape of different sizes, without copying of the BVH data. + +2008 Sept 5 + - Enabled Demos/ForkLiftDemo + Thanks Roman Ponomarev. + +2008 Sept 4 + - Added btCudaBroadphase in Extras/CUDA: some research into accelerating Bullet using CUDA. + Thanks to the particle demo from the NVidia CUDA SDK. + +2008 Sept 3 + - Several bug fixes and contributions related to inertia tensor, memory leaks etc. + Thanks to Ole K. + +2008 Sept 1 + - Updated CDTestFramework, with latest version of OPCODE Array SAP. See Extras/CDTestFramework + Thanks to Pierre Terdiman for the update + +2008 August 25 + - Walt Disney Studios contributes their in-house Maya Plugin for simulating Bullet physics, with options for other engines such as PhysBam or PhysX. + Thanks to Nicola Candussi and Arthur Shek + +2008 August 14 + - Improved performance for btDbvtBroadphase, based on dual dynamic AABB trees (one for static, one for dynamic objects, where objects can move from one to the other tree) + Thanks to Nathanael Presson again, for all his work. + +2008 July 31 + - Added Havok .hkx to COLLADA Physics .dae converter patch+information + - Fix btSubsimplexConvexCast + Thanks to Nacho, http://www.bulletphysics.com/Bullet/phpBB3/viewtopic.php?f=9&t=2422) + - Fix in rendering, GL_STENCIL + - btTriangleIndexVertexArray indices should be unsigned int/unsigned short int, + - Made InternalProcessAllTriangles virtual, thanks to + Both thank to Fullmetalcoder, http://www.bulletphysics.com/Bullet/phpBB3/viewtopic.php?f=9&t=2401 + - clamp impulse for btPoint2PointConstraint + Thanks to Martijn Reuvers, http://www.bulletphysics.com/Bullet/phpBB3/viewtopic.php?f=9&t=2418 + - Free memory of bvh, pass in scaling factor (optional) + Thanks to Roy Eltham, http://www.bulletphysics.com/Bullet/phpBB3/viewtopic.php?f=9&t=2375 + +2008 July 27 + +btDbvtBroadphase: + - Fixed a performance issues reported by 'reltham' + - Added btDbvtBroadphase::optimize() for people who want good performances right +away or don't do dynamics. + - fixed compilation issues when DBVT_BP_PROFILE was set. +btSoftBody: + - Fixed singular matrix issues related to polar decomposition (flat meshes). +DemoApplication: + - Shadows (enable/disable through 'g' or DemoApplication::setShadows(bool)). + - Texture can be enable/disable through 'u' +CDFramework: + - fixed compilation issues. + All thanks to Nathanael Presson + +2008 July 10 + - Added btMultimaterialTriangleMeshShape and MultiMaterialDemo + Thanks to Alex Silverman for the contribution + +2008 June 30 + - Added initial support for kinematic character controller + Thanks to John McCutchan + +2008 April 14 + - Added ray cast support for Soft Bodies + Thanks to Nathanael Presson for the contribution + +2008 April 9 + - Cleanup of Stan Melax ConvexHull, removed Extras/ConvexHull, moved sources into LinearMath/BulletCollision + +2008 April 4 + - Added btSliderConstraint and demo + Thanks Roman Ponomarev + +2008 April 3 + - Fixed btMinkowskiSumShape, and added hitpoint to btSubsimplexConvexCast + +2008 April 2 + - Added Extras/CdTestFrameWork + Thanks Pierre Terdiman + +2008 April 1 + - Added posix thread (pthread) support + Thanks Enrico + +2008 March 30 + - Added Soft Body, cloth, rope and deformable volumes, including demos and interaction + Thanks Nathanael Presson for this great contribution + + 2008 March 17 + - Improved BulletColladaConverter + Thanks John McCutchan + +2008 March 15 + - btMultiSapBroadphase in a working state. Needs more optimizations to be fully useable. + - Allow btOptimizedBvh to be used for arbitrary objects, not just triangles + - added quicksort to btAlignedObjectArray + - removed btTypedUserInfo, added btHashMap + +2008 March 30 + - Moved quickstep solver and boxbox into Bullet/src folder + Thanks Russell L. Smith for permission to redistribute Open Dynamics Engine quickstep and box-box under the ZLib license + +2008 Feb 27 + - Added initial version for Character Control Demo + - Applied fixes to IBM Cell SDK 3.0 build makefiles + Thanks Jochen and mojo for reporting/providing patch: http://www.bulletphysics.com/Bullet/phpBB3/viewtopic.php?f=9&t=1922 + +2008 Feb 8 + - Bugfixes in ConvexCast support against the world. + Thanks to Isgmasa for reporting/providing fix: http://www.bulletphysics.com/Bullet/phpBB3/viewtopic.php?f=9&t=1823 + +2008 Feb 6 + - Added btCapsuleShapeX and btCapsuleShapeZ for capsules around X and Z axis (default capsule is around Y) + +2008 Feb 3 + - Added btTypedUserInfo, useful for serialization + +2008 Jan 31 + - Add support for 16 and 32-bit indices for SPU / BulletMultiThreaded version. + +2008 Jan 29 + - Added COLLADA Physics export/serialization/snapshot from any Bullet btDynamicsWorld. Saving the physics world into a text .xml file is useful for debugging etc. + +2008 Jan 23 + - Added Stan Melax Convex Hull utility library in Extras/ConvexHull. This is useful to render non-polyhedral convex objects, and to simplify convex polyhedra. + +2008 Jan 14 + - Add support for batch raycasting on SPU / BulletMultiThreaded + +2007 Dec 16 + - Added btRigidBodyConstructionInfo, to make it easier to set individual setting (and leave other untouched) during rigid body construction. + Thanks Vangelis Kokkevis for pointing this out. + - Fixed memoryleak in the ConstraintDemo and Raytracer demo. + - Fixed issue with clearing forces/gravity at the end of the stepSimulation, instead of during internalSingleStepSimulation. + Thanks chunky for pointing this out: http://www.bulletphysics.com/Bullet/phpBB3/viewtopic.php?f=9&t=1780 + - Disabled additional damping in rigid body by default, but enable it in most demos. Set btRigidBodyConstructionInfo m_additionalDamping to true to enable this. + - Removed obsolete QUICKPROF BEGIN/END_PROFILE, and enabled BT_PROFILE. Profiling is enabled by default (see Bullet/Demos/OpenGL/DemoApplication.cpp how to use this). + User can switch off profiling by enabling define BT_NO_PROFILE in Bullet/src/btQuickprof.h. + +2007 Dec 14 + - Added Hello World and BulletMultiThreaded demos + - Add portable version of BulletMultiThreaded, through SequentialThreadSupport (non-parallel but sharing the same code-path) + - Add Cmake support for AllBulletDemos + + +2007 Dec 11 + - Moved the 'btRigidBody::clearForce' to the end of the stepSimulation, instead of in each substep. + See discussion http://www.bulletphysics.com/Bullet/phpBB3/viewtopic.php?f=9&t=1601 + - Added btConvexPlaneCollisionAlgorithm, makes planes perform better, and prevents tunneling + Thanks Andy O'Neil for reporting the performance/functionality issue + - Fixes for IBM Cell SDK 3.0 + Thanks to Jochen Roth for the patch. + +2007 Dec 10 + - Fixes in btHeightfieldTerrainShape + Thanks to Jay Lee for the patch. + +2007 Dec 9 + - Only update aabb of active objects + Thanks Peter Tchernev for reporting (http://bulletphysics.com/Bullet/phpBB3/viewtopic.php?f=9&t=1764 ) + - Added workaround to compile libxml under Visual Studio 2008 Beta 2 + - Make glui compile under MSVC 9.0 beta (vsnprintf is already defined) + +2007 Dec 6 + - Added DynamicControlDemo, showing dynamic control through constraint motors + Thanks to Eddy Boxerman + - Add support for generic concave shapes for convex cast. + - Added convex cast query to collision world. + - Added workaround for OpenGL bug in Mac OS X 10.5.0 (Leopard) + - Added concave raycast demo + All above thanks to John McCutchan (JMC) + - Fixed issues that prevent Linux version to compile. + Thanks to Enrico for reporting and patch, see + - Fixed misleading name 'numTriangleIndices' into 'numTriangles' + Thanks Sean Tasker for reporting: + +2007 Nov 28: + - Added raycast against trianglemesh. Will be extended to object cast soon. + Thanks John McCutchan (JMC) + - make getNumPoints const correct, add const getPoints(). + Thanks Dirk Gregorius + - Bugfix: allow btCollisionObjects (non-btRigidBody) to interact properly with btRigidBody for cache-friendly btSequentialImpulseConstraintSolver. + Thanks Andy O'Neil for pointing this out. + - Bugfix: don't fail if spheres have identical center, use arbitrary separating normal (1,0,0) + Thanks Sean Tasker for reporting! http://www.bulletphysics.com/Bullet/phpBB3/viewtopic.php?f=9&t=1681 + + +2007, November 20 + - Added hierarchical profiling + - Fixed memory leak in btMultiSapBroadphase, + - Fixed hash function (typo, should use 2 proxies) + Thanks to Stephen (shatcher) for reporting and fixes! http://www.bulletphysics.com/Bullet/phpBB3/viewtopic.php?f=9&t=1696 + +2007 Nov 11 + - Fixed parallel solver (BulletMultiThreaded) friction issue + - Terminate Win32 Threads when closing the CcdPhysicsDemo (when USE_PARALLEL_SOLVER/USE_PARALLEL_DISPATCHER is defined) + +2007 Nov 6 + - Added support for 16-bit indices for triangle meshes + - Added support for multiple mesh parts using btBvhTriangleMeshShape. + Thanks to Tim Johansson + +2007 Oct 22 + - All memory allocations go through btAlignedAlloc/btAlignedFree. User can override this to verify memory leaks + - added a few more demos to AllBulletDemos + - fix for one of the constructors of btHingeConstraint + Thanks Marcus Hennix + +2007 Oct 20 + - included glui, a GLUT/OpenGL based toolkit for some graphical user elements + Removed dynamic_cast from glui, to allow linkage without rtti + - added Box2D framework using glui, allowing all demos to run within one executable + Thanks Erin Catto for the FrameWork skeleton (http://www.box2d.org) + +2007 Ocy 17 + - Allow user to pass in their own memory (stack and pool) allocators, through collisionConfiguration. See demos how to use this + +2007 Oct 14 + - Included working version of Cell SPU parallel optimized version for Libspe2 SPU task scheduler. + This version compiles and runs on Playstation 3 Linux and IBM CellBlade, see BulletSpuOptimized.pdf for build instructions + (Official Playstation 3 developers can request a SPURS version through Sony PS3 Devnet.) + Thanks to IBM 'Extreme Blue' project for the contribution + http://www-913.ibm.com/employment/us/extremeblue/ + Thanks Minh Cuong Tran, Benjamin Hoeferlin, Frederick Roth and Martina Huellmann + for various contributions to get this initial Libspe2 parallel version up and running. + +2007 Oct 13 + - made 'btCollisionShape::calculateLocalInertia' const + Thanks to cgripeos, see http://www.bulletphysics.com/Bullet/phpBB3/viewtopic.php?f=9&t=1514 + - applied a large patch to remove warnings + Thanks to Enrico, see http://www.bulletphysics.com/Bullet/phpBB3/viewtopic.php?f=9&t=1568 + - removed SSE includes, added #incude for memset in Extras/quickstep, thanks Eternl Knight + +2007 Oct 11 + - added Hashed Overlapping Pair Cache, recommended by Pierre Terdiman. It works like a charm, thanks Pierre and Erin Catto (code from Box2D) + - modified some margins inside btBoxShape, btCylinderShape and btSphereShape + - added cone debug rendering (for cones with x, y and z up-axis) + - added improvements for optional Extra/quickstep, thanks to Remotion + - some performance improvements for Bullet constraint solver + +2007 Sept 28 + - upgraded GIMPACT to version 0.3 + Thanks to Francisco Leon + +2007 Sept 27 + - added contribution from IBM Extreme Blue project for Libspe2 support. This allow to execute BulletMultiThreaded on Cell SPU under PS3 Linux and Cell Blade. See http://www-913.ibm.com/employment/us/extremeblue + Thanks to Minh Cuong Tran, Frederick Roth, Martina Heullmann and Benjamin Hoeferlin. + +2007 Sept 13 + - Improved btGenericD6Constraint. It can be used to create ragdolls (similar to the new btConeTwistConstraint). See GenericJointDemo + - Added support for Bullet constraints in the optional Extras/quickstep ODE solver. See CcdPhysicsDemo, enable #COMPARE_WITH_QUICKSTEP and add libquickstep to the dependencies. + For both patches/improvements thanks Francisco Leon/projectileman + +2007 Sept 10 + - removed union from btQuadWordStorage, it caused issues under certain version of gcc/Linux + +2007 Sept 10 + - Reverted constraint solver, due to some issues. Need to review the recent memory allocation changes. + - Fixed issue with kinematic objects rotating at low speed: quaternion was de-normalized, passing value > 1 into acosf returns #IND00 invalid values + - 16 byte memory alignment for BVH serialization + - memory cleanup for btPoolAllocator + +2007 Sept 9 + - Added serialization for BVH/btBvhTriangleMeshShape, including endian swapping. See ConcaveDemo for an example. + Thanks to Phil Knight for the contribution. + - Fixed issues related to stack allocator/compound collision algorithm + Thanks Proctoid, http://www.bulletphysics.com/Bullet/phpBB3/viewtopic.php?f=18&t=1460 + - Increase some default memory pool settings, and added a fallback for the constraints solver to use heap memory + - Removed accidential testing code in btScalar.h related to operator new. + - Enable btAxis3Sweep and bt32BitAxis3Sweep to be linked in at the same time, using template + +2007 Sept 7 + - Replaced several dynamic memory allocations by stack allocation and pool allocations + - Added branch-free quantized aabb bounding box overlap check, works better on Playstation 3 and XBox 360 + Thanks to Phil Knight. Also see www.cellperformance.com for related articles + - Collision algorithms and settings for the memory/stack allocator can be done using btDefaultCollisionConfiguration + This is an API change. See demos how to modify existing implementations with a one-liner. + - Register several collision algorithms by default (sphere-sphere, sphere-box, sphere-triangle) + - Use other traveral method for BVH by default, this improves triangle mesh collision performance. + +2007 Aug 31 + - fixed MSVC 6 build + Thanks Proctoid, http://www.continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=1375 + - fixed double precision build issues + Thanks Alex Silverman, http://www.continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=1434 + +2007 Aug 24 + - fixed bug in btMatrix3x3::transposeTimes(const btMatrix3x3& m) const. Luckily it wasn't used in core parts of the library (yet). + Thanks to Jay Lee + +2007 Aug 15 + - fixed bug in Extras/GIMPACT 0.2 related to moving triangle meshes + Thanks Thomas, http://www.continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=1368 + +2007 Aug 14 + - added parallel constraint solver. Works on Playstation 3 Cell SPU and multi core (Win Threads on PC and XBox 360). + See Extras/BulletMultiThreaded for SpuSolverTask subfolder and SpuParallelSolver.cpp + Thanks Marten Svanfeldt (Starbreeze Studios) + - fixed some bugs related to parallel collision detection (Extras/BulletMultiThreaded) + Thanks Marten Svanfeldt (Starbreeze Studios) + +2007 Aug 2 + - added compound and concave-convex (swapped) case for BulletMultiThreaded collision detection, thanks to Marten Svanfeldt + - refactored broadphase and overlapping pair cache. This allows performance improvement by combining multiple broadphases. This helps add/remove of large batches of objects and large worlds. See also Pierre Terdiman forum topic: + http://www.continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=1329 + + +2007 July 27 + - added Ragdoll Demo + Thanks to Marten Svanfeldt (Starbreeze Studios) + + - added Vector Math library for SIMD 3D graphics linear algebra (vector, matrix, quaternion) + See Bullet/Extras/vectormathlibrary + Supports SIMD SSE, PowerPC PPU and Cell SPU (including PS3 Linux and CellBlade), as well as generic portable scalar version + Will be used to improve BulletMultiThreaded performance + Open Sourced by Sony Computer Entertainment Inc. under the new BSD license + - added SIMD math library + 4-way SIMD for common math functions like atan2f4, cosf4, floorf4, fabsf4, rsqrtf4 etc. Used by Vector Math library under PPU and SPU. + Supports PowerPC (PPU) and Cell SPU, including PS3 Linux and CellBlade. + See Bullet/Extras/simdmathlibrary + Open sourced by Sony Computer Entertainment Inc. under the new BSD license + + +2007 July 25 + - added several patches: per-rigidbody sleeping threshold. added Assert to prevent deletion of rigidbody while constraints are still pointing at it + Thanks to Marten Svanfeldt (Starbreeze Studios) + +2007 July 13 + - fixed relative #include paths again. We can't use "../" relative paths: some compilers choke on it (it causes extreme long paths) + Within the libraries, we always need to start with "BulletCollision/" or "BulletDynamics/ or "LinearMath/" + +2007 July 10 + - Updated Bullet User Manual + +2007 July 5 + - added btConeTwistConstraint, especially useful for ragdolls. See Demos/RagdollDemo + Thanks to Marten Svanfeldt (Starbreeze Studios) + +2007 June 29 + - btHeightfieldTerrainShape: Added heightfield support, with customizations + - Upgraded to GIMPACT 0.2, see Extras/GIMPACT and MovingConcaveDemo + - Several patches from Marten Svanfeldt (Starbreeze Studios) + Improved collision filtering (in broadphase and rigidbody) + Improved debug rendering + Allow to set collision filter group/mask in addRigidBody + + +2007 June 15 + - Changed btAlignedObjectArray to call copy constructor/replacement new for duplication, rather then assignment operator (operator=). + +2007 June 11 + - Added multi-threading. Originally for Playstation 3 Cell SPU, but the same code can run using Win32 Threads using fake DMA transfers (memcpy) + Libspe2 support for Cell Blade / PS3 Linux is upcoming + See Extras/BulletMultiThreaded. Usage: replace btCollisionDispatcher by btSpuGatheringCollisionDispatcher + + - Added managed Bullet library, entirely rewritten in C# for Windows and XBox 360 XNA + See Extras/BulletX + Thanks to KleMiX, aka Vsevolod Klementjev + +2007 May 31 + - sign-bit went wrong in case of 32-bit broadphase, causing quantization problems. + Thanks DevO for reporting. + +2007 May 23 + - Fixed quantization problem for planar triangle meshes in btOptimizedBvh + Thanks Phil Knight for reporting and helping to fix this bug. + +2007 May 20 + - btAxisSweep3: Fixed a bug in btAxisSweep3 (sweep and prune) related to object removal. Only showed up when at least one btStaticPlaneShape was inserted. + Thanks tbp for more details on reproducing case. + - btAxisSweep3: Fixed issue with full 32bit precision btAxisSweep3 (define BP_USE_FIXEDPOINT_INT_32), it used only 0xffff/65536 for quantization instead of full integer space (0xffffffff) + - btRaycastVehicle: Added 'getForwardVector' and getCurrentSpeedKmHour utility functions + - Fixed local scaling issues (btConvexTriangleMeshShape, btBvhTriangleMeshShape, removed scaling from btMatrix3x3). + Thanks Volker for reporting! + - Added second filename search, so that starting BspDemo and ConvexDecompositionDemo from within Visual Studio (without setting the starting path) still works + +2007 April 22 + - Added braking functionality to btRaycastVehicle + - Removed tons of warnings, under MSVC 2005 compilation in -W4 + +2007 March 21 + - Fixed issues: comma at end of enum causes errors for some compilers + - Fixed initialization bug in LocalRayResult ( m_localShapeInfo(localShapeInfo) ) + +2007 March 20 + - Added refit tree to quantized stackless tree, and updated ConcaveDemo as example. + +2007 March 17 + - Added constraint solver optimizations, avoiding cross products during iterations, and gather rigidbody/constraint info in contiguous memory (btSolverBody/btSolverConstraint) + - These optimizations don't give large benefit yet, but it has good potential. Turned on by default. Can be switched off using solver->setSolverMode(SOLVER_RANDMIZE_ORDER). + - Enabled anti-jitter for rigid bodies. This is experimental, and can be switched off by setting a global (it is experimental so no proper interface) gJitterVelocityDampingFactor = 1.0; + - Fixed bug in islandmanifold.heapSort(btPersistentManifoldSortPredicate()); , thanks Noehrgel for reporting this (affected Sun Solaris) + +2007 March 12 + - Added compile-time toggle between on 16-bit and 32-bit fixed-point SAP broadphase. + This allows the number of bodies to exceed 32767 + - Enable useQuantizedAabbCompression on btTriangleMesh, see ColladaDemo + +2007 March 8 + - Fixed bug in constraint/island sorting (caused by replacing STL by dedicated btAlignedObjectArray with heapSort) + Thanks Clemens Unterkofler for pointing this out! + +2007 March 6 + - removed STL from the Bullet library: replace std::vector by btAlignedObjectArray. Also removed the std::set for overlapping pair set, and turned it into an overlapping pair array. The SAP only adds objects, never removed. Removal is postponed for during traversal of overlapping pairs (duplicates and non-overlapping pairs are removed during that traversal). + - added heap sort and binary search/linear search to btAlignedObjectArray + - fixed wrong cast, thanks Hamstray, http://www.continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=1015 + + +2007 Feb 25 + - Improved performance of convex collision shapes, cache local AABB instead of recomputation. This fixes issue with very slow performance in larger .bsp levels + +2007 Feb 24 + - Added compressed/quantized AABB tree, 16 bytes per node, while supporting 32-bit (triangle) indices. + Should be faster and smaller then original version (quantized aabb check is done in integer space) + Original aabb tree nodes are still supported. They are 44 bytes, with full floating point precision and additional subPart index. + - added meter-unit scaling support in ColladaConverter.cpp + +2007 Feb 21 + - Build system: updated bullet.pc.in library names + - Updated EPA comparison integration (missing parameter) + +2007 Jan 04 + - fixed optimized AABB tree building: in some cases the tree building fails due to unbalanced trees, which generated stack overflow + +2006 Dec 15 + - added contribution to allow double precision collision detection/dynamics. Define BT_USE_DOUBLE_PRECISION in your project and libraries that include Bullet + +2006 Dec 14 + - merged contact and non-contact constraint solving into one loop, will improve stability of jointed bodies during collisions + - added first draft for hingeConstraint motor + +2006 Dec 8, Erwin Coumans + - preparation for SIMD: added btAlignedAllocator and btAlignedObjectArray, to replace stl std::vector, same interface, but compatible with 16 byte alignment + - cleaned up dependencies in autogenerated msvc projectfiles + - aligned btVector3 on 16 bytes boundary, under win32. see if developers will come up with problems + +2006 Dec 04, Erwin Coumans + Added btNearCallback. This is similar to Open Dynamics Engine (ODE) dNearCallback, but important differences: + - contact points are persistent (lifetime more then one frame, for warmstarting/incremental contact point management) + - continuous collision detection, time of impact + Added btRigidBody::isInWorld(), returns true if btRigidBody is inside a btCollisionWorld/btDynamicsWorld derived class + Added angularFactor to btRigidbody, this helps some character control (no angular impulse applied) + + +2006 Nov 28 + Moved StackAlloc from EPA into LinearMath/btStackAlloc + renamed internal class ConcaveShape into btConcaveShape + added btHeightfieldTerrainShape (not completed yet) + +2006 Nov 15 Nathanael Presson + Added EPA penetration depth algorithm, Expanding Polytope Algorithm + Added Pierre Terdiman penetration depth comparison/test DEMO + Fixed Bullet's Minkowski sampling penetration depth solver + Contributed by Nathanael Presson + +2006 Nov 11 Francisco León Nájera + Added GIMPACT trimesh collision detection: concave versus concave, + Contributed by Francisco León Nájera + +2006 Nov 2 + Minor refactoring: btCollisionObject changes from struct into class, added accessor methods + Force use of btMotionState to synchronize graphics transform, disabled old btRigidBody constructor that accepts btTransform + Renamed treshold into threshold throughout the code + +2006 Oct 30 + Enable decoupling of physics and graphics framerate using interpolation and internal fixed timestep, based on btMotionState + Enabled raycast vehicle demo (still needs tuning) + Refresh contact points, even when they are already persistent. + Fixed debugDraw colors (thanks pc0de for reporting) + Use Dispatcher in ConcaveConvexCollisionAlgorithm (so it uses the registered collision algorithm, not hardcoded convexconcave) + Improved performance of constraint solver by precalculating the cross product/impulse arm + Added collision comparison code: ODE box-box, also sphere-triangle + Added safety check into GJK, and an assert for AABB's that are very large + Fixed kinematic support (deriving velocities for animated objects) + Updated comparison/optional quickstep solver in Extras + UserCollisionAlgorithm demonstrates btTriangleMesh usage (easier trimesh compared to index array version) + Removed scaling from btTransform (we only want to deal with rigid transforms) + +2006 Oct 4 + Fixed minor leak in btOptimizeBVH + Cleanup of btRigidBody construction + added getW() in btQuaternion + assert when setLinearVelocity is called on btRigidBody + renamed projectfile library from collada-dom to colladadom (to make VC6 happy) + +2006 Sept 27 + Big Refactoring: renamed and moved files, create a replacement for CcdPhysicsEnvironment/CcdPhysicsController. + All Bullet classes in LinearMath, BulletCollision and BulletDynamics start with bt, and methods start with lowercase. + Moved classes into src folder, which is the only include folder needed. + Added 2 headerfiles in src: btBulletCollisionCommon.h and btBulletDynamicsCommon.h + +2006 Sept 23 + Fixed 2 bugs, causing crashes when removing objects. Should do better unit-testing. UnionFind and 3D SAP were involved. + +2006 Sept 19 + Allow programmable friction and contact solver model. User can register their own functions for several interaction types. + Improved performance, and removed hardcoded maximum overlaps (switched from C-array to stl::set) + +2006 Sept 16 + Added Bullet 2.0 User Manual + Allow registration of custom user collision algorithms + +2006 Sept 10 + Started cleaning up demos + +2006 Sept 4 + Fixed concave collision bug (caused instability/missing collisions in meshes/compounds) + Fixed memoryleak in OptimizedBvh, added RayTestSingle to CollisionWorld + Prepared for VehicleDemo + Increased Performance (island generation for sleeping objects took too much time) + Better COLLADA 1.4.1 physics conformance in ColladaDemo + +2006 August 11 + Added Quake BspDemo + Improved CCD for compound and non-convex objects + +2006 August 10 + Added per-triangle material (friction/restitution) support for non-convex meshes. See ConcaveDemo for usage. + +2006 August 9 + Added CMake support (see http://cmake.org) + This can autogenerate makefiles, projectfiles cross platform (including MacOS X Xcode ) + Just run cmake . in the root folder and it will autogenerate build files + +2006 July 26 Erwin Coumans + Upgraded to COLLADA-DOM 1.4.1, latest SVN version + ColladaDemo can export snapshots to .dae + +2006 July 24 Erwin Coumans + Added Compound CollisionShape support + (this is still low performance -> requires stackless tree-versus-tree traversal for better performance) + +2006 July 15 Erwin Coumans + Added initial support for Parallel execution (collision detection, constraint solving) + See ParallelPhysicsEnvironment in Extras\PhysicsInterface\CcdPhysics + +2006 July 10 Erwin Coumans + Added MacOS X support (some build issues mainly) + +2006 July 5 Erwin Coumans + Improved COLLADA 1.4 physics import, both COLLADA-DOM and FCollada + +2006 June 29 Erwin Coumans + Refactoring of the broadphase + Moved some optional files to Extras: Algebraic ccd and EPA, quickstep + Moved the limits on bodies/overlap to 32k and 65k + +2006 June 25 Erwin Coumans + Added basic Collision Filtering, during broadphase + Allow adding meshes to the TriangleIndexVertexArray, + (input for TriangleMeshShape) + Preparation for CompoundShape + +2006 June 19 Erwin Coumans + Added support for COLLADA Physics Import. + Both jam and Visual Studio can compile ColladaDemo + +2006 June 18 Dirk Gregorius + Started implementing Generic6DOF joint and setup basic interface + + +2006 June 17 Frank Richter + Bumped version in configure.ac to 1.5.6 (assuming that "1.5f" is + the next version released). + Updated files in mk/autoconf and mk/jam with copies from CS; fixes a + GLU detection issue on MinGW. + Set msvc/bullet_ico.ico as the default application icon. + Disabled exceptions for gcc builds. + Applied a patch from Michael D. Adams to fix a warning with gcc. +2006 jUNE 16 Erwin Coumans + Constraints now merge simulation islands. + +2006 May 24 + Improved GJK accuracy, fixed GjkConvexCast issue, thanks to ~MyXa~ for reporting + +2006 May 19 + Added restitution support + Moved out Friction and Dynamics info from ManifoldPoint (removed logical dependency) + Added a void* m_userPersistentData in ManifoldPoint. + Added a ContactDestroyedCallback, to allow user to handle destruction of m_userPersistentData + +2006 May 13 + Fixed some bugs in friction / jacobian calculations. Reported by Dirk Gregorius. Thanks! + +2006 May 9 + Fixed raycasting filtering + Moved repository to SVN at https://svn.sourceforge.net/svnroot/bullet + +2006 April 27 + Moved raycasting to CollisionWorld, to make it more generic + Added basic CCD option in the CcdCollisionDemo + Fixed 'noResponse' mode, for triggering rigidbodies (useful for Artificial Intelligence queries) + Improved Bullet/ODE sample (in Extras) + +2006 April 10 + Separating Axis Test (SAT) convex hull collision detector, contribution by Simon Hobbs + Added SIMD SSE Math classes (for above SAT) + Added Mouse picking in CcdPhysicsDemo + Improved penetration depth estimation in MinkowskiPenetrationDepthSolver, both accuracy and performance + Added Hinge constraint + Added quickprof profiling (see http://sourceforge.net/projects/quickprof ) + +2006 March 21 Frank Richter + Removed VC manifest files. + Removed superfluous "grpplugins" projects. + +2006 March 20 Erwin Coumans + Clamped the acculumated impulse rather then intermediate impulse (within the iteration) + Use the persistent contacts for reusing the impulse + Separated friction and normal solving for better stability + Decreased the default number of iterations of the constraint solver from 10 to 4 + +2006 March 19 Frank Richter + Removed a couple of CSisms from the VC projects. + Fixed VC include & lib paths to go to the Addtional* options + instead the command line arguments. + Added pkgconfig support. + +2006 March 14 Frank Richter + Added support for shipped GLUT on MinGW. + Fixed GLUT support on MinGW. + +2006 March 13 Frank Richter + Bolted on Jam-based build system. + Generated VC project files. + Fixed GCC warnings. + Fixed Linux build issues. + +2006 March 13 +Added 3D Sweep and Prune Broadphase Collision Detection, Contribution from Simon Hobbs. + +2006 March 2 + Minor change in license to ZLib/LibPNG + This makes it legally a bit easier to deploy on Playstation 3 + Prepared for more generic constraints, added ConstraintsDemo + +2006 Feb 23 + Rearranged files and dependencies to allow for easier standalone Collision Detection without Bullet Dynamics. + See Demos/CollisionInterfaceDemo and Extras/ode/ode/test/test_BulletGjk.cpp for examples how to use. + +2005 August 6 + Bullet 0.2 release with demos, sources, doxygen, draft manual + +2005 June 1 + First public release of Bullet + + +... todo: add history + +2003 Initial version (continuous collision detection) diff --git a/Engine/lib/bullet/Doxyfile b/Engine/lib/bullet/Doxyfile new file mode 100644 index 000000000..2f9a6b27e --- /dev/null +++ b/Engine/lib/bullet/Doxyfile @@ -0,0 +1,763 @@ +# Doxyfile 1.2.4 + +# This file describes the settings to be used by doxygen for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. +PROJECT_NAME = "Bullet Collision Detection & Physics Library" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Dutch, French, Italian, Czech, Swedish, German, Finnish, Japanese, +# Korean, Hungarian, Norwegian, Spanish, Romanian, Russian, Croatian, +# Polish, Portuguese and Slovene. + +OUTPUT_LANGUAGE = English + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these class will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. It is allowed to use relative paths in the argument list. + +STRIP_FROM_PATH = + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a class diagram (in Html and LaTeX) for classes with base or +# super classes. Setting the tag to NO turns the diagrams off. + +CLASS_DIAGRAMS = YES + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower case letters. If set to YES upper case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# users are adviced to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explict @brief command for a brief description. + +JAVADOC_AUTOBRIEF = YES + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# reimplements. + +INHERIT_DOCS = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# The ENABLE_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = NO + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = src + + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +FILE_PATTERNS = *.h *.cpp *.c + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. + +INPUT_FILTER = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse. + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = YES + +HHC_LOCATION = "C:\Program Files\HTML Help Workshop\hhc.exe" + +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +#HTML_STYLESHEET = "\\server\exchange\Software Development\Documentation\DoxyGen\doxygen.css" + +CHM_FILE = BulletDocs.chm +HHC_LOCATION = "c:\program files\HTML Help Workshop\hhc.exe" +GENERATE_CHI = YES +BINARY_TOC = YES + +TOC_EXPAND = YES + +SHOW_DIRECTORIES = YES + + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 1 + +# If the GENERATE_TREEVIEW tag is set to YES, a side pannel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript and frames is required (for instance Netscape 4.0+ +# or Internet explorer 4.0+). + +GENERATE_TREEVIEW = YES + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimised for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using a WORD or other. +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assigments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. Warning: This feature +# is still experimental and very incomplete. + +GENERATE_XML = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_PREDEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = src + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_PREDEF_ONLY tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +#--------------------------------------------------------------------------- +# Configuration::addtions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tagfiles. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the ENABLE_PREPROCESSING, INCLUDE_GRAPH, and HAVE_DOT tags are set to +# YES then doxygen will generate a graph for each documented file showing +# the direct and indirect include dependencies of the file with other +# documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, INCLUDED_BY_GRAPH, and HAVE_DOT tags are set to +# YES then doxygen will generate a graph for each documented header file showing +# the documented files that directly or indirectly include this file + +INCLUDED_BY_GRAPH = YES + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found on the path. + +DOT_PATH = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO + +# The CGI_NAME tag should be the name of the CGI script that +# starts the search engine (doxysearch) with the correct parameters. +# A script with this name will be generated by doxygen. + +CGI_NAME = search.cgi + +# The CGI_URL tag should be the absolute URL to the directory where the +# cgi binaries are located. See the documentation of your http daemon for +# details. + +CGI_URL = + +# The DOC_URL tag should be the absolute URL to the directory where the +# documentation is located. If left blank the absolute path to the +# documentation, with file:// prepended to it, will be used. + +DOC_URL = + +# The DOC_ABSPATH tag should be the absolute path to the directory where the +# documentation is located. If left blank the directory on the local machine +# will be used. + +DOC_ABSPATH = + +# The BIN_ABSPATH tag must point to the directory where the doxysearch binary +# is installed. + +BIN_ABSPATH = c:\program files\doxygen\bin + +# The EXT_DOC_PATHS tag can be used to specify one or more paths to +# documentation generated for other projects. This allows doxysearch to search +# the documentation for these projects as well. + +EXT_DOC_PATHS = diff --git a/Engine/lib/bullet/INSTALL b/Engine/lib/bullet/INSTALL new file mode 100644 index 000000000..0752e2816 --- /dev/null +++ b/Engine/lib/bullet/INSTALL @@ -0,0 +1,100 @@ +Bullet Collision Detection and Physics Library + +** Windows Compilation ** + +Under Windows, projectfiles for Visual Studio version 6,7,7.1 and 8 are +available in msvc/. For example, for Visual Studio 2005, open +msvc/8/wksbullet.sln + +The ColladaDemo and ConvexDecomposition demo needs to be able to locate the +data files (jenga.dae and file.obj) in the current directory. Make sure Visual +Studio points to the right folder (..\..). + +Alternatively use CMake to autogenerate a build system for Windows: + + - Download/install CMake from www.cmake.org or package manager + - List available build systems by running 'cmake' in the Bullet root folder + - Create a build system using the -G option for example: + + cmake . -G "Visual Studio 9 2008" or + cmake . -G "Visual Studio 9 2008 Win64" + + +** Linux Compilation ** + + - Download/install CMake from www.cmake.org or package manager + CMake is like autoconf in that it will create build scripts which are then + used for the actual compilation + + - There are some options for cmake builds: + BUILD_SHARED_LIBS: default 'OFF', set to 'ON' to build .so libraries + BUILD_EXTRAS: default 'ON', compiles additional libraries in 'Extras' + BUILD_DEMOS: default 'ON', compiles applications found in 'Demos' + CMAKE_INSTALL_PREFIX: default '/usr/local', the installation path. + CMAKE_INSTALL_RPATH: if you install outside a standard ld search path, + then you should set this to the installation lib path. + Other options may be discovered by 'cmake --help-variable-list' and + 'cmake --help-variable OPTION' + + - Run 'cmake' with desired options of the form -DOPTION=VALUE + By default this will create the usual Makefile build system, but CMake can + also produce Eclipse or KDevelop project files. See 'cmake --help' to see + what "generators" are available in your environment, selected via '-G'. + For example: + cmake -DBUILD_SHARED_LIBS=ON + + - Assuming using the default Makefile output from cmake, run 'make' to + build, and then 'make install' if you wish to install. + + +** Mac OS X Compilation ** + + - Download/install CMake from www.cmake.org or package manager + CMake is like autoconf in that it will create build scripts which are then + used for the actual compilation + + - There are some options for cmake builds: + BUILD_SHARED_LIBS: default 'OFF', set to 'ON' to build .dylib libraries + BUILD_EXTRAS: default 'ON', compiles additional libraries in 'Extras' + BUILD_DEMOS: default 'ON', compiles applications found in 'Demos' + CMAKE_INSTALL_PREFIX: default '/usr/local', the installation path. + CMAKE_INSTALL_NAME_DIR: if you install outside a standard ld search + path, then you should set this to the installation lib/framework path. + + To build framework bundles: + FRAMEWORK: default 'OFF', also requires 'BUILD_SHARED_LIBS' set ON + If both FRAMEWORK and BUILD_SHARED_LIBS are set, will create + OS X style Framework Bundles which can be placed in + linked via the -framework gcc argument or drag into Xcode projects. + (If not framework, then UNIX style 'include' and 'lib' will be produced) + + Other options may be discovered by 'cmake --help-variable-list' and + 'cmake --help-variable OPTION' + + - Run 'cmake' with desired options of the form -DOPTION=VALUE + By default this will create the usual Makefile build system, but CMake can + also produce Eclipse or KDevelop project files. See 'cmake --help' to see + what "generators" are available in your environment, selected via '-G'. + For example: + cmake -DBUILD_SHARED_LIBS=ON -DFRAMEWORK=ON \ + -DCMAKE_INSTALL_PREFIX=/Library/Frameworks \ + -DCMAKE_INSTALL_NAME_DIR=/Library/Frameworks + + - Assuming using the default Makefile output from cmake, run 'make' to build + and then 'make install'. + + +** Alternative Mac OS X and Linux via 'jam' or autoconf/make ** + - at the command line: + ./autogen.sh + ./configure + - 'jam' or 'make' depending on preference + - If jam is not available for your system, you can compile it, jam sources + are included with the Bullet sources in jam-2.5 + - compiling jam: + cd jam-2.5 + make + sudo make install + + +** For more help, visit http://www.bulletphysics.com ** diff --git a/Engine/lib/bullet/Jamfile.in b/Engine/lib/bullet/Jamfile.in new file mode 100644 index 000000000..eb49df511 --- /dev/null +++ b/Engine/lib/bullet/Jamfile.in @@ -0,0 +1,66 @@ +TOP ?= "@top_srcdir@" ; +BUILDTOP ?= "@top_builddir@" ; + +SubDir TOP ; + +IncludeDir ; +IncludeDir src ; + +IncludeDir $(BUILDTOP) : : literal transient ; + +CleanDir clean : + out ; +Clean distclean : + aclocal.m4 + config.h + config.h.in~ + config.log + config.status + config.status.lineno + config.cache + configure.lineno + Jamconfig + Jamfile ; +CleanDir distclean : + autom4te.cache ; +Depends distclean : clean ; + +Clean maintainerclean : + config.h.in + configure ; +Depends maintainerclean : distclean ; + +Help distclean : "Remove built targets and configuration" ; +Help maintainerclean : + "Remove built targets, configuration, and generated files." ; + +ApplicationIconDefault win32 : all : bullet_ico.ico : $(TOP) msvc ; + +MsvcGenSubDir TOP msvc : common ; +MsvcGenSubDir TOP msvc 6 : 6 ; +MsvcGenSubDir TOP msvc 7 : 7 ; +MsvcGenSubDir TOP msvc 71 : 71 ; +MsvcGenSubDir TOP msvc sn71 : sn71 ; +MsvcGenSubDir TOP msvc 8 : 8 ; +MsvcGenSubDir TOP msvc xenon8 : xenon8 ; +MsvcGenTemplateDir TOP mk msvcgen ; +MsvcGenWorkspace bullet : : "grp.+_(?!bullet$)" ; +MsvcGenWorkspace bullet_corelib : libbulletcollision libbulletdynamics libbulletmath libbulletmultithreaded : "grp.+_(?!bullet_corelib$)" ; + +# Set project-specific compiler and linker options for msvcgen. +MsvcGenConfig GL.AVAILABLE : yes ; +MsvcGenConfig GL.LFLAGS : ; +MsvcGenConfig GL.LIBS : opengl32.lib ; +MsvcGenConfig GLUT.AVAILABLE : yes ; +MsvcGenConfig GLUT.CFLAGS : ; +MsvcGenConfig GLUT.LFLAGS : ; +MsvcGenConfig GLUT.INCDIRS : "../../Glut" ; +MsvcGenConfig GLUT.LIBDIRS : "../../Glut" ; +MsvcGenConfig GLUT.LIBS : glut32.lib ; +MsvcGenConfig GLEW.LIBS : glew32.lib ; + +SubInclude TOP src ; +SubInclude TOP Extras ; +SubInclude TOP Demos ; + +Depends install_config : [ DoInstall bullet.pc : $(libdir)/pkgconfig ] ; diff --git a/Engine/lib/bullet/Jamrules b/Engine/lib/bullet/Jamrules new file mode 100644 index 000000000..e486c3345 --- /dev/null +++ b/Engine/lib/bullet/Jamrules @@ -0,0 +1,21 @@ +if ! $(BUILDTOP) +{ +BUILDTOP = . ; +} + +# Include configuration. +JAMCONFIG ?= $(BUILDTOP)/Jamconfig ; +include $(JAMCONFIG) ; + +# Set up compiler flags. +# Unfortunately, we can not use FDefines here since Boost Jam does not have it, +# and we have not yet included mk/jam/build.jam which provides an emulation +# layer for Boost. We can not include build.jam earlier because these flags +# need to be defined before build.jam is included. :-( +COMPILER.CFLAGS += -Wall -Wno-unknown-pragmas ; +COMPILER.CFLAGS.optimize += -O3 -fomit-frame-pointer -ffast-math ; +COMPILER.CFLAGS.debug += -g3 ; +COMPILER.CFLAGS.profile += -gp -O3 ; + +# Include CS build rules +include $(TOP)/mk/jam/build.jam ; diff --git a/Engine/lib/bullet/LICENSE b/Engine/lib/bullet/LICENSE new file mode 100644 index 000000000..ba24a53c2 --- /dev/null +++ b/Engine/lib/bullet/LICENSE @@ -0,0 +1,19 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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. +*/ + +All files in the Bullet/src folder are under this Zlib license. +Optional Extras/GIMPACT and Extras/GIMPACTBullet is also under ZLib license. Other optional external libraries in Extras/Demos have own license,see respective files. + +This means Bullet can freely be used in any software, including commercial and console software. A Playstation 3 optimized version is available through Sony. diff --git a/Engine/lib/bullet/Makefile.am b/Engine/lib/bullet/Makefile.am new file mode 100644 index 000000000..b243b33a3 --- /dev/null +++ b/Engine/lib/bullet/Makefile.am @@ -0,0 +1,7 @@ +if CONDITIONAL_BUILD_DEMOS +SUBDIRS=src Extras Demos +else +SUBDIRS=src Extras +endif +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = bullet.pc diff --git a/Engine/lib/bullet/NEWS b/Engine/lib/bullet/NEWS new file mode 100644 index 000000000..d976ae431 --- /dev/null +++ b/Engine/lib/bullet/NEWS @@ -0,0 +1,4 @@ + +For news, visit the Bullet Physics Forum at +http://www.continuousphysics.com/Bullet/phpBB2/viewforum.php?f=9 + diff --git a/Engine/lib/bullet/README b/Engine/lib/bullet/README new file mode 100644 index 000000000..8f3705388 --- /dev/null +++ b/Engine/lib/bullet/README @@ -0,0 +1,7 @@ + +Bullet is a 3D Collision Detection and Rigid Body Dynamics Library for games and animation. +Free for commercial use, including Playstation 3, open source under the ZLib License. +Discrete and continuous collision detection, integrated into Blender 3D, and COLLADA 1.4 Physics import. + +See the Bullet_User_Manual.pdf for more info and visit the Bullet Physics Forum at +http://bulletphysics.com diff --git a/Engine/lib/bullet/RELEASING.TXT b/Engine/lib/bullet/RELEASING.TXT new file mode 100644 index 000000000..8fba4a5a8 --- /dev/null +++ b/Engine/lib/bullet/RELEASING.TXT @@ -0,0 +1,34 @@ +This document details the steps necessary to package a release of Bullet. + +1) Preparing for release: + +update VERSION in several places +update ChangeLog +regenerate MSVC project files + +2) Generating the release .zip: +Do an SVN export on a Windows machine into the directory: bullet-X.YY +prepare a zip file containing the directory + +3) Generating the release .tar.gz: +Do an SVN export on a Unix machine into the directory: bullet-X.YY +prepare a .tar.gz file containing the directory + +4) Uploading release to google code: + +Google Code Bullet downloads URL: http://code.google.com/p/bullet/downloads/list + +Title of release should follow this guide line: Bullet Physics SDK (revision) + +It is better to upload the .tar.gz before the .zip so that the .zip appears first in the list + +If the release is an Alpha/Beta or RC the tags should be: Type-Source, OpSys-ALL +If the release is a final release the tags should be: Type-Source, OpSys-ALL, Featured + +5) Obsoleting old releases + +Edit the tags on old releases and add the 'Deprecated' tag + +6) Announcing final releases: + +Final release announcements are done here: http://bulletphysics.com/Bullet/phpBB3/viewforum.php?f=18 diff --git a/Engine/lib/bullet/VERSION b/Engine/lib/bullet/VERSION new file mode 100644 index 000000000..8f5752a10 --- /dev/null +++ b/Engine/lib/bullet/VERSION @@ -0,0 +1 @@ +2.75 diff --git a/Engine/lib/bullet/acinclude.m4 b/Engine/lib/bullet/acinclude.m4 new file mode 100644 index 000000000..6adb73841 --- /dev/null +++ b/Engine/lib/bullet/acinclude.m4 @@ -0,0 +1,3056 @@ +# checkbuild.m4 -*- Autoconf -*- +#============================================================================== +# Copyright (C)2003 by Eric Sunshine +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +#============================================================================== +AC_PREREQ([2.56]) + +#------------------------------------------------------------------------------ +# CS_SPLIT_TUPLE(TUPLE, OUTPUT-VARIABLES) +# Split a build-tuple into its component parts. A build tuple is +# constructed by CS_CREATE_TUPLE() and is comprised of compiler flags, +# linker flags, and library references. OUTPUT-VARIABLES is a +# comma-delimited list of shell variables which should receive the +# extracted compiler flags, linker flags, and library references, +# respectively. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_SPLIT_TUPLE], + [CS_SPLIT([$1], [cs_dummy,$2], [@]) + m4_map([_CS_SPLIT_TUPLE], [$2])]) + +AC_DEFUN([_CS_SPLIT_TUPLE], + [$1=`echo $$1 | sed 'y%@%:@% %'` + ]) + + + +#------------------------------------------------------------------------------ +# CS_CREATE_TUPLE([CFLAGS], [LFLAGS], [LIBS]) +# Construct a build-tuple which is comprised of compiler flags, linker +# flags, and library references. Build tuples are encoded so as to +# preserve whitespace in each component. This makes it possible for +# macros (such as CS_BUILD_IFELSE) which employ build tuples to accept +# whitespace-delimited lists of tuples, and for shell "for" statements to +# iterate over tuple lists without compromising whitespace embedded +# within individual flags or library references. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_CREATE_TUPLE], [`echo @$1@$2@$3 | sed 'y% %@%:@%'`]) + + + +#------------------------------------------------------------------------------ +# CS_LANG_CFLAGS +# Return the literal string CFLAGS if the current language is C. Return +# the literal string CXXFLAGS if the current language is C++. Generic +# compiler test macros which need to modify or save the compiler flags +# can invoke this macro to get the name of the compiler flags environment +# variable (either CFLAGS or CXXFLAGS) depending upon the current +# language. For example: +# CS_LANG_CFLAGS="$CS_LANG_CFLAGS -Wall" +# With C, this expands to: +# CFLAGS="$CFLAGS -Wall" +# With C++, it expands to: +# CXXFLAGS="$CXXFLAGS -Wall" +#------------------------------------------------------------------------------ +AC_DEFUN([CS_LANG_CFLAGS], [AC_LANG_CASE([C], [CFLAGS], [C++], [CXXFLAGS])]) + + + +#------------------------------------------------------------------------------ +# CS_BUILD_IFELSE([PROGRAM], [FLAGS], [LANGUAGE], [ACTION-IF-BUILT], +# [ACTION-IF-NOT-BUILT], [OTHER-CFLAGS], [OTHER-LFLAGS], +# [OTHER-LIBS], [INHIBIT-OTHER-FLAGS], [ERROR-REGEX]) +# Try building a program using the supplied compiler flags, linker flags, +# and library references. PROGRAM is typically a program composed via +# AC_LANG_PROGRAM(). PROGRAM may be omitted if you are interested only +# in learning if the compiler or linker respects certain flags. LANGUAGE +# is typically either C or C++ and specifies which compiler to use for +# the test. If LANGUAGE is omitted, C is used. FLAGS is a whitespace +# delimited list of build tuples. Tuples are created with +# CS_CREATE_TUPLE() and are composed of up to three elements each. The +# first element represents compiler flags, the second linker flags, and +# the third libraries used when linking the program. Each tuple from +# FLAGS is attempted in order. If you want a build attempted with no +# special flags prior to builds with specialized flags, create an empty +# tuple with CS_CREATE_TUPLE() at the start of the FLAGS list. If the +# build is successful, then the shell variables cs_build_ok is set to +# "yes", cs_build_cflags, cs_build_lflags, and cs_build_libs are set to +# the tuple elements which resulted in the successful build, and +# ACTION-IF-BUILT is invoked. Upon successful build, no further tuples +# are consulted. If no tuple results in a successful build, then +# cs_build_ok is set to "no" and ACTION-IF-NOT-BUILT is invoked. +# OTHER-CFLAGS, OTHER-LFLAGS, and OTHER-LIBS specify additional compiler +# flags, linker flags, and libraries which should be used with each tuple +# build attempt. Upon successful build, these additional flags are also +# reflected in the variables cs_build_cflags, cs_build_lflags, and +# cs_build_libs unless INHIBIT-OTHER-FLAGS is a non-empty string. The +# optional ERROR-REGEX places an additional constraint upon the build +# check. If specified, ERROR-REGEX, which is a standard `grep' regular +# expression, is applied to output captured from the compiler and linker. +# If ERROR-REGEX matches, then the build is deemed a failure, and +# cs_build_ok is set to "no". This facility is useful for broken build +# tools which emit an error message yet still return success as a result. +# In such cases, it should be possible to detect the failure by scanning +# the tools' output. +# +# IMPLEMENTATION NOTES +# +# In Autoconf 2.57 and earlier, AC_LINK_IFELSE() invokes AC_TRY_EVAL(), +# which does not provide access to the captured output. To work around +# this limitation, we temporarily re-define AC_TRY_EVAL() as +# _AC_EVAL_STDERR(), which leaves the captured output in conftest.err +# (which we must also delete). In Autoconf 2.58, however, +# AC_LINK_IFELSE() instead already invokes _AC_EVAL_STDERR() on our +# behalf, however we must be careful to apply ERROR-REGEX within the +# invocation AC_LINK_IFELSE(), since AC_LINK_IFELSE() deletes +# conftest.err before it returns. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_BUILD_IFELSE], + [AC_LANG_PUSH(m4_default([$3],[C])) + cs_cflags_save="$CS_LANG_CFLAGS" + cs_lflags_save="$LDFLAGS" + cs_libs_save="$LIBS" + cs_build_ok=no + m4_ifval([$10], [m4_pushdef([AC_TRY_EVAL], [_AC_EVAL_STDERR]($$[1]))]) + + for cs_build_item in m4_default([$2],[CS_CREATE_TUPLE()]) + do + CS_SPLIT_TUPLE( + [$cs_build_item],[cs_cflags_test,cs_lflags_test,cs_libs_test]) + CS_LANG_CFLAGS="$cs_cflags_test $6 $cs_cflags_save" + LDFLAGS="$cs_lflags_test $7 $cs_lflags_save" + LIBS="$cs_libs_test $8 $cs_libs_save" + AC_LINK_IFELSE(m4_default([$1], [AC_LANG_PROGRAM([],[])]), + [m4_ifval([$10], + [AS_IF([AC_TRY_COMMAND( + [grep "AS_ESCAPE([$10])" conftest.err >/dev/null 2>&1])], + [cs_build_ok=no], [cs_build_ok=yes])], + [cs_build_ok=yes])]) + AS_IF([test $cs_build_ok = yes], [break]) + done + + m4_ifval([$10], [m4_popdef([AC_TRY_EVAL]) rm -f conftest.err]) + CS_LANG_CFLAGS=$cs_cflags_save + LDFLAGS=$cs_lflags_save + LIBS=$cs_libs_save + AC_LANG_POP(m4_default([$3],[C])) + + AS_IF([test $cs_build_ok = yes], + [cs_build_cflags=CS_TRIM([$cs_cflags_test[]m4_ifval([$9],[],[ $6])]) + cs_build_lflags=CS_TRIM([$cs_lflags_test[]m4_ifval([$9],[],[ $7])]) + cs_build_libs=CS_TRIM([$cs_libs_test[]m4_ifval([$9],[],[ $8])]) + $4], + [$5])]) + + + +#------------------------------------------------------------------------------ +# CS_CHECK_BUILD(MESSAGE, CACHE-VAR, [PROGRAM], [FLAGS], [LANGUAGE], +# [ACTION-IF-BUILT], [ACTION-IF-NOT-BUILT], [IGNORE-CACHE], +# [OTHER-CFLAGS], [OTHER-LFLAGS], [OTHER-LIBS], +# [INHIBIT-OTHER-FLAGS], [ERROR-REGEX]) +# Like CS_BUILD_IFELSE() but also prints "checking" and result messages, +# and optionally respects the cache. Sets CACHE-VAR to "yes" upon +# success, else "no" upon failure. Additionally, sets CACHE-VAR_cflags, +# CACHE-VAR_lflags, and CACHE-VAR_libs to the values which resulted in a +# successful build. If IGNORE-CACHE is "yes", then the cache variables +# are ignored upon entry to this macro, however they are still set to +# appropriate values upon exit. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_CHECK_BUILD], + [AS_IF([test "$8" != yes], + [AC_CACHE_CHECK([$1], [$2], + [CS_BUILD_IFELSE([$3], [$4], [$5], + [$2=yes + $2_cflags=$cs_build_cflags + $2_lflags=$cs_build_lflags + $2_libs=$cs_build_libs], + [$2=no], [$9], [$10], [$11], [$12], [$13])])], + [AC_MSG_CHECKING([$1]) + CS_BUILD_IFELSE([$3], [$4], [$5], + [$2=yes + $2_cflags=$cs_build_cflags + $2_lflags=$cs_build_lflags + $2_libs=$cs_build_libs], + [$2=no], [$9], [$10], [$11], [$12], [$13]) + AC_MSG_RESULT([$$2])]) + AS_IF([test $$2 = yes], [$6], + [$2_cflags='' + $2_lflags='' + $2_libs='' + $7])]) + + + +#------------------------------------------------------------------------------ +# CS_CHECK_BUILD_FLAGS(MESSAGE, CACHE-VAR, FLAGS, [LANGUAGE], +# [ACTION-IF-RECOGNIZED], [ACTION-IF-NOT-RECOGNIZED], +# [OTHER-CFLAGS], [OTHER-LFLAGS], [OTHER-LIBS], +# [ERROR-REGEX]) +# Like CS_CHECK_BUILD(), but checks only if the compiler or linker +# recognizes a command-line option or options. MESSAGE is the "checking" +# message. CACHE-VAR is the shell cache variable which receives the flag +# or flags recognized by the compiler or linker. FLAGS is a +# whitespace-delimited list of build tuples created with +# CS_CREATE_TUPLE(). Each tuple from FLAGS is attempted in order until +# one is found which is recognized by the compiler. After that, no +# further flags are checked. LANGUAGE is typically either C or C++ and +# specifies which compiler to use for the test. If LANGUAGE is omitted, +# C is used. If a command-line option is recognized, then CACHE-VAR is +# set to the composite value of $cs_build_cflags, $cs_build_lflags, and +# $cs_build_libs of the FLAGS element which succeeded (not including the +# "other" flags) and ACTION-IF-RECOGNIZED is invoked. If no options are +# recognized, then CACHE-VAR is set to the empty string, and +# ACTION-IF-NOT-RECOGNIZED is invoked. As a convenience, in case +# comparing CACHE-VAR against the empty string to test for failure is +# undesirable, a second variable named CACHE-VAR_ok is set to the literal +# "no" upon failure, and to the same value as CACHE-VAR upon success. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_CHECK_BUILD_FLAGS], + [AC_CACHE_CHECK([$1], [$2_ok], + [CS_BUILD_IFELSE([], [$3], [$4], + [$2=CS_TRIM([$cs_build_cflags $cs_build_lflags $cs_build_libs]) + $2_ok="$$2"], + [$2='' + $2_ok=no], [$7], [$8], [$9], [Y], [$10])]) + AS_IF([test "$$2_ok" != no], [$5], [$6])]) +#============================================================================== +# Copyright (C)2003-2006 by Eric Sunshine +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +#============================================================================== +AC_PREREQ([2.56]) + +#------------------------------------------------------------------------------ +# CS_CHECK_COMMON_TOOLS_LINK +# Checks for common tools related to linking. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_CHECK_COMMON_TOOLS_LINK], + [ + # The default RANLIB in Jambase is wrong on some platforms, and is also + # unsuitable during cross-compilation, so we set the value unconditionally + # (sixth argument of CS_EMIT_BUILD_PROPERTY). + AC_PROG_RANLIB + CS_EMIT_BUILD_PROPERTY([RANLIB], [$RANLIB], [], [], [], [Y]) + + CS_CHECK_TOOLS([DLLTOOL], [dlltool]) + CS_EMIT_BUILD_PROPERTY([CMD.DLLTOOL], [$DLLTOOL]) + + CS_CHECK_TOOLS([DLLWRAP], [dllwrap]) + CS_EMIT_BUILD_PROPERTY([CMD.DLLWRAP], [$DLLWRAP]) + + CS_CHECK_TOOLS([WINDRES], [windres]) + CS_EMIT_BUILD_PROPERTY([CMD.WINDRES], [$WINDRES]) + + CS_CHECK_TOOLS([STRINGS], [strings]) + CS_EMIT_BUILD_PROPERTY([CMD.STRINGS], [$STRINGS]) + + CS_CHECK_TOOLS([OBJCOPY], [objcopy]) + CS_EMIT_BUILD_PROPERTY([CMD.OBJCOPY], [$OBJCOPY]) + + CS_CHECK_LIBTOOL + CS_EMIT_BUILD_PROPERTY([LIBTOOL], [$LIBTOOL]) + CS_EMIT_BUILD_PROPERTY([APPLE_LIBTOOL], [$APPLE_LIBTOOL]) + ]) + + +#------------------------------------------------------------------------------ +# CS_CHECK_COMMON_TOOLS_BASIC +# Checks for basic tools for building things. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_CHECK_COMMON_TOOLS_BASIC], + [CS_CHECK_MKDIR + CS_EMIT_BUILD_PROPERTY([CMD.MKDIR], [$MKDIR]) + CS_EMIT_BUILD_PROPERTY([CMD.MKDIRS], [$MKDIRS]) + + CS_CHECK_PROGS([INSTALL], [install]) + CS_EMIT_BUILD_PROPERTY([INSTALL], [$INSTALL])]) + + +#------------------------------------------------------------------------------ +# CS_CHECK_COMMON_TOOLS_DOC_TEXINFO +# Checks for tools to generate documentation from texinfo files. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_CHECK_COMMON_TOOLS_DOC_TEXINFO], + [CS_CHECK_PROGS([TEXI2DVI], [texi2dvi]) + CS_EMIT_BUILD_PROPERTY([CMD.TEXI2DVI], [$TEXI2DVI]) + + CS_CHECK_PROGS([TEXI2PDF], [texi2pdf]) + CS_EMIT_BUILD_PROPERTY([CMD.TEXI2PDF], [$TEXI2PDF]) + + CS_CHECK_PROGS([DVIPS], [dvips]) + CS_EMIT_BUILD_PROPERTY([CMD.DVIPS], [$DVIPS]) + + CS_CHECK_PROGS([DVIPDF], [dvipdf]) + CS_EMIT_BUILD_PROPERTY([CMD.DVIPDF], [$DVIPDF]) + + CS_CHECK_PROGS([MAKEINFO], [makeinfo]) + CS_EMIT_BUILD_PROPERTY([CMD.MAKEINFO], [$MAKEINFO])]) + + +#------------------------------------------------------------------------------ +# CS_CHECK_COMMON_TOOLS_DOC_DOXYGEN +# Checks for tools to generate source documentation via doxygen. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_CHECK_COMMON_TOOLS_DOC_DOXYGEN], + [CS_CHECK_PROGS([DOXYGEN], [doxygen]) + CS_EMIT_BUILD_PROPERTY([CMD.DOXYGEN], [$DOXYGEN]) + + CS_CHECK_TOOLS([DOT], [dot]) + CS_EMIT_BUILD_PROPERTY([CMD.DOT], [$DOT])]) + + +#------------------------------------------------------------------------------ +# CS_CHECK_COMMON_LIBS +# Check for typical required libraries (libm, libmx, libdl, libnsl). +#------------------------------------------------------------------------------ +AC_DEFUN([CS_CHECK_COMMON_LIBS], + [AC_LANG_PUSH([C]) + AC_CHECK_LIB([m], [pow], [cs_cv_libm_libs=-lm], [cs_cv_libm_libs=]) + AC_CHECK_LIB([m], [cosf], [cs_cv_libm_libs=-lm]) + AC_CHECK_LIB([mx], [cosf]) + AC_CHECK_LIB([dl], [dlopen], [cs_cv_libdl_libs=-ldl], [cs_cv_libdl_libs=]) + AC_CHECK_LIB([nsl], [gethostbyname]) + AC_LANG_POP([C])]) +# checkcppunit.m4 -*- Autoconf -*- +#============================================================================== +# Copyright (C)2005 by Eric Sunshine +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +#============================================================================== +AC_PREREQ([2.56]) + +#------------------------------------------------------------------------------ +# CS_CHECK_CPPUNIT([EMITTER]) +# Check if CppUnit (http://cppunit.sourceforge.net/), the unit-testing +# framework is available. The shell variable cs_cv_libcppunit is set to +# "yes" if CppUnit is discovered, else "no". If available, then the +# variables cs_cv_libcppunit_cflags, cs_cv_libcppunit_lflags, and +# cs_cv_libcppunit_libs are set. If EMITTER is provided, then +# CS_EMIT_BUILD_RESULT() is invoked with EMITTER in order to record the +# results in an output file. As a convenience, if EMITTER is the literal +# value "emit" or "yes", then CS_EMIT_BUILD_RESULT()'s default emitter +# will be used. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_CHECK_CPPUNIT], + [CS_CHECK_LIB_WITH([cppunit], + [AC_LANG_PROGRAM([[#include ]], + [CppUnit::TextUi::TestRunner r; r.run();])], + [], [C++]) + + AS_IF([test $cs_cv_libcppunit = yes], + [CS_CHECK_BUILD([if cppunit is sufficiently recent], + [cs_cv_libcppunit_recent], + [AC_LANG_PROGRAM( + [[#include ]], + [CppUnit::BriefTestProgressListener b; b.startTest(0);])], + [], [C++], + [CS_EMIT_BUILD_RESULT([cs_cv_libcppunit], [CPPUNIT], + CS_EMITTER_OPTIONAL([$1]))], [], [], + [$cs_cv_libcppunit_cflags], + [$cs_cv_libcppunit_lflags], + [$cs_cv_libcppunit_libs])])]) +# checklib.m4 -*- Autoconf -*- +#============================================================================== +# Copyright (C)2003-2005 by Eric Sunshine +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +#============================================================================== +AC_PREREQ([2.56]) + +#------------------------------------------------------------------------------ +# cs_lib_paths_default +# Whitespace delimited list of directory tuples in which to search, by +# default, for external libraries. Each list item can specify an +# include|library directory tuple (for example, "/usr/include|/usr/lib"), +# or a single directory (for example, "/usr"). If the second form is +# used, then "include" and "lib" subdirectories of the directory are +# searched. If the library resources are not found, then the directory +# itself is searched. Thus, "/proj" is shorthand for +# "/proj/include|/proj/lib /proj|/proj". +# +# Present Cases: +# /usr/local -- Not all compilers search here by default, so we specify +# it manually. +# /sw -- Fink, the MacOS/X manager of Unix packages, installs here by +# default. +# /opt/local -- DarwinPorts installs here by default. +#------------------------------------------------------------------------------ +m4_define([cs_lib_paths_default], + [/usr/local/include|/usr/local/lib \ + /sw/include|/sw/lib \ + /opt/local/include|/opt/local/lib \ + /opt/include|/opt/lib]) + + + +#------------------------------------------------------------------------------ +# cs_pkg_paths_default +# Comma delimited list of additional directories in which the +# `pkg-config' command should search for its `.pc' files. +# +# Present Cases: +# /usr/local/lib/pkgconfig -- Although a common location for .pc files +# installed by "make install", many `pkg-config' commands neglect +# to search here automatically. +# /sw/lib/pkgconfig -- Fink, the MacOS/X manager of Unix packages, +# installs .pc files here by default. +# /opt/local/lib/pkgconfig -- DarwinPorts installs .pc files here by +# default. +#------------------------------------------------------------------------------ +m4_define([cs_pkg_paths_default], + [/usr/local/lib/pkgconfig, + /sw/lib/pkgconfig, + /opt/local/lib/pkgconfig, + /opt/lib/pkgconfig]) + + + +#------------------------------------------------------------------------------ +# CS_CHECK_LIB_WITH(LIBRARY, PROGRAM, [SEARCH-LIST], [LANGUAGE], +# [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND], [OTHER-CFLAGS], +# [OTHER-LFLAGS], [OTHER-LIBS], [ALIASES]) +# Very roughly similar in concept to AC_CHECK_LIB(), but allows caller to +# to provide list of directories in which to search for LIBRARY; allows +# user to override library location via --with-LIBRARY=dir; and consults +# `pkg-config' (if present) and `LIBRARY-config' (if present, i.e. +# `sdl-config') in order to obtain compiler and linker flags. LIBRARY is +# the name of the library or MacOS/X framework which is to be located +# (for example, "readline" for `libreadline.a' or `readline.framework'). +# PROGRAM, which is typically composed with AC_LANG_PROGRAM(), is a +# program which references at least one function or symbol in LIBRARY. +# SEARCH-LIST is a whitespace-delimited list of paths in which to search +# for the library and its header files, in addition to those searched by +# the compiler and linker by default, and those referenced by the +# cs_lib_paths_default macro. Each list item can specify an +# `include|library' directory tuple (for example, +# "/usr/include|/usr/lib"), or a single directory (for example, "/usr"). +# If the second form is used, then "include" and "lib" subdirectories of +# the directory are searched. If the library resources are not found, +# then the directory itself is searched. Thus, "/proj" is shorthand for +# "/proj/include|/proj/lib /proj|/proj". Items in the search list can +# include wildcards. SEARCH-LIST can be overridden by the user with the +# --with-LIBRARY=dir option, in which case only "dir/include|dir/lib" and +# "dir|dir" are searched. If SEARCH-LIST is omitted and the user did not +# override the search list via --with-LIBRARY=dir, then only the +# directories normally searched by the compiler and the directories +# mentioned via cs_lib_paths_default are searched. LANGUAGE is typically +# either C or C++ and specifies which compiler to use for the test. If +# LANGUAGE is omitted, C is used. OTHER-CFLAGS, OTHER-LFLAGS, and +# OTHER-LIBS can specify additional compiler flags, linker flags, and +# libraries needed to successfully link with LIBRARY. The optional +# ALIASES is a comma-delimited list of library names for which to search +# in case LIBRARY is not located (for example "[sdl1.2, sdl12]" for +# libsdl1.2.a, sdl1.2.framework, libsdl12.a, and sdl12.framework). If +# the library or one of its aliases is found and can be successfully +# linked into a program, then the shell cache variable cs_cv_libLIBRARY +# is set to "yes"; cs_cv_libLIBRARY_cflags, cs_cv_libLIBRARY_lflags, and +# cs_cv_libLIBRARY_libs are set, respectively, to the compiler flags +# (including OTHER-CFLAGS), linker flags (including OTHER-LFLAGS), and +# library references (including OTHER-LIBS) which resulted in a +# successful build; and ACTION-IF-FOUND is invoked. If the library was +# not found or was unlinkable, or if the user disabled the library via +# --without-LIBRARY, then cs_cv_libLIBRARY is set to "no" and +# ACTION-IF-NOT-FOUND is invoked. Note that the exported shell variable +# names are always composed from LIBRARY regardless of whether the test +# succeeded because the primary library was discovered or one of the +# aliases. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_CHECK_LIB_WITH], + [AC_ARG_WITH([$1], [AC_HELP_STRING([--with-$1=dir], + [specify location of lib$1 if not detected automatically; searches + dir/include, dir/lib, and dir])]) + + # Backward compatibility: Recognize --with-lib$1 as alias for --with-$1. + AS_IF([test -n "$with_lib$1" && test -z "$with_$1"], + [with_$1="$with_lib$1"]) + + AS_IF([test -z "$with_$1"], [with_$1=yes]) + AS_IF([test "$with_$1" != no], + [# If --with-$1 value is same as cached value, then assume other + # cached values are also valid; otherwise, ignore all cached values. + AS_IF([test "$with_$1" != "$cs_cv_with_$1"], + [cs_ignore_cache=yes], [cs_ignore_cache=no]) + + cs_check_lib_flags='' + AS_IF([test $with_$1 = yes], + [m4_foreach([cs_check_lib_alias], [$1, $10], + [_CS_CHECK_LIB_PKG_CONFIG_FLAGS([cs_check_lib_flags], + cs_check_lib_alias) + _CS_CHECK_LIB_CONFIG_FLAGS([cs_check_lib_flags], + cs_check_lib_alias) + ])]) + + AS_IF([test $with_$1 != yes], + [cs_check_lib_paths=$with_$1], + [cs_check_lib_paths="| cs_lib_paths_default $3"]) + m4_foreach([cs_check_lib_alias], [$1, $10], + [_CS_CHECK_LIB_CREATE_FLAGS([cs_check_lib_flags], + cs_check_lib_alias, [$cs_check_lib_paths]) + ]) + + CS_CHECK_BUILD([for lib$1], [cs_cv_lib$1], [$2], [$cs_check_lib_flags], + [$4], [], [], [$cs_ignore_cache], [$7], [$8], [$9])], + [cs_cv_lib$1=no]) + + cs_cv_with_$1="$with_$1" + AS_IF([test "$cs_cv_lib$1" = yes], [$5], [$6])]) + + + +#------------------------------------------------------------------------------ +# CS_CHECK_PKG_CONFIG +# Check if the `pkg-config' command is available and reasonably recent. +# This program acts as a central repository of build flags for various +# packages. For example, to determine the compiler flags for FreeType2 +# use, "pkg-config --cflags freetype2"; and "pkg-config --libs freetype2" +# to determine the linker flags. If `pkg-config' is found and is +# sufficiently recent, PKG_CONFIG is set and AC_SUBST() invoked. +#------------------------------------------------------------------------------ +m4_define([CS_PKG_CONFIG_MIN], [0.9.0]) +AC_DEFUN([CS_CHECK_PKG_CONFIG], + [AS_IF([test "$cs_prog_pkg_config_checked" != yes], + [CS_CHECK_TOOLS([PKG_CONFIG], [pkg-config]) + _CS_CHECK_PKG_CONFIG_PREPARE_PATH + cs_prog_pkg_config_checked=yes]) + AS_IF([test -z "$cs_cv_prog_pkg_config_ok"], + [AS_IF([test -n "$PKG_CONFIG"], + [AS_IF([$PKG_CONFIG --atleast-pkgconfig-version=CS_PKG_CONFIG_MIN], + [cs_cv_prog_pkg_config_ok=yes], + [cs_cv_prog_pkg_config_ok=no])], + [cs_cv_prog_pkg_config_ok=no])])]) + +AC_DEFUN([_CS_CHECK_PKG_CONFIG_PREPARE_PATH], + [PKG_CONFIG_PATH="m4_foreach([cs_pkg_path], [cs_pkg_paths_default], + [cs_pkg_path$PATH_SEPARATOR])$PKG_CONFIG_PATH" + export PKG_CONFIG_PATH]) + + + +#------------------------------------------------------------------------------ +# _CS_CHECK_LIB_PKG_CONFIG_FLAGS(VARIABLE, LIBRARY) +# Helper macro for CS_CHECK_LIB_WITH(). Checks if `pkg-config' knows +# about LIBRARY and, if so, appends a build tuple consisting of the +# compiler and linker flags reported by `pkg-config' to the list of +# tuples stored in the shell variable VARIABLE. +#------------------------------------------------------------------------------ +AC_DEFUN([_CS_CHECK_LIB_PKG_CONFIG_FLAGS], + [CS_CHECK_PKG_CONFIG + AS_IF([test $cs_cv_prog_pkg_config_ok = yes], + [AC_CACHE_CHECK([if $PKG_CONFIG recognizes $2], [_CS_CLPCF_CVAR([$2])], + [AS_IF([$PKG_CONFIG --exists $2], + [_CS_CLPCF_CVAR([$2])=yes], [_CS_CLPCF_CVAR([$2])=no])]) + AS_IF([test $_CS_CLPCF_CVAR([$2]) = yes], + [_CS_CHECK_LIB_CONFIG_PROG_FLAGS([$1], [pkg_config_$2], + [$PKG_CONFIG], [$2])])])]) + +AC_DEFUN([_CS_CLPCF_CVAR], [AS_TR_SH([cs_cv_prog_pkg_config_$1])]) + + + +#------------------------------------------------------------------------------ +# _CS_CHECK_LIB_CONFIG_FLAGS(VARIABLE, LIBRARY) +# Helper macro for CS_CHECK_LIB_WITH(). Checks if `LIBRARY-config' +# (i.e. `sdl-config') exists and, if so, appends a build tuple consisting +# of the compiler and linker flags reported by `LIBRARY-config' to the +# list of tuples stored in the shell variable VARIABLE. +#------------------------------------------------------------------------------ +AC_DEFUN([_CS_CHECK_LIB_CONFIG_FLAGS], + [CS_CHECK_TOOLS(_CS_CLCF_SHVAR([$2]), [$2-config]) + AS_IF([test -n "$_CS_CLCF_SHVAR([$2])"], + [AS_IF([test -z "$_CS_CLCF_CVAR([$2])"], + [AS_IF([$_CS_CLCF_SHVAR([$2]) --cflags --libs >/dev/null 2>&1], + [_CS_CLCF_CVAR([$2])=yes], [_CS_CLCF_CVAR([$2])=no])]) + AS_IF([test $_CS_CLCF_CVAR([$2]) = yes], + [_CS_CHECK_LIB_CONFIG_PROG_FLAGS([$1], [config_$2], + [$_CS_CLCF_SHVAR([$2])])])])]) + +AC_DEFUN([_CS_CLCF_CVAR], [AS_TR_SH([cs_cv_prog_config_$1_ok])]) +AC_DEFUN([_CS_CLCF_SHVAR], [m4_toupper(AS_TR_SH([CONFIG_$1]))]) + + + +#------------------------------------------------------------------------------ +# _CS_CHECK_LIB_CONFIG_PROG_FLAGS(VARIABLE, TAG, CONFIG-PROGRAM, [ARGS]) +# Helper macro for _CS_CHECK_LIB_PKG_CONFIG_FLAGS() and +# _CS_CHECK_LIB_CONFIG_FLAGS(). CONFIG-PROGRAM is a command which +# responds to the --cflags and --libs options and returns suitable +# compiler and linker flags for some package. ARGS, if supplied, is +# passed to CONFIG-PROGRAM after the --cflags or --libs argument. The +# results of the --cflags and --libs options are packed into a build +# tuple and appended to the list of tuples stored in the shell variable +# VARIABLE. TAG is used to compose the name of the cache variable. A good +# choice for TAG is some unique combination of the library name and +# configuration program. +#------------------------------------------------------------------------------ +AC_DEFUN([_CS_CHECK_LIB_CONFIG_PROG_FLAGS], + [AS_IF([test -z "$_CS_CLCPF_CVAR([$2])"], + [cs_check_lib_cflag=CS_RUN_PATH_NORMALIZE([$3 --cflags $4]) + cs_check_lib_lflag='' + cs_check_lib_libs=CS_RUN_PATH_NORMALIZE([$3 --libs $4]) + _CS_CLCPF_CVAR([$2])=CS_CREATE_TUPLE( + [$cs_check_lib_cflag], + [$cs_check_lib_lflag], + [$cs_check_lib_libs])]) + $1="$$1 $_CS_CLCPF_CVAR([$2])"]) + +AC_DEFUN([_CS_CLCPF_CVAR], [AS_TR_SH([cs_cv_prog_$1_flags])]) + + + +#------------------------------------------------------------------------------ +# _CS_CHECK_LIB_CREATE_FLAGS(VARIABLE, LIBRARY, PATHS) +# Helper macro for CS_CHECK_LIB_WITH(). Constructs a list of build +# tuples suitable for CS_CHECK_BUILD() and appends the tuple list to the +# shell variable VARIABLE. LIBRARY and PATHS have the same meanings as +# the like-named arguments of CS_CHECK_LIB_WITH(). +#------------------------------------------------------------------------------ +AC_DEFUN([_CS_CHECK_LIB_CREATE_FLAGS], + [for cs_lib_item in $3 + do + case $cs_lib_item in + *\|*) CS_SPLIT( + [$cs_lib_item], [cs_check_incdir,cs_check_libdir], [|]) + _CS_CHECK_LIB_CREATE_FLAG([$1], + [$cs_check_incdir], [$cs_check_libdir], [$2]) + ;; + *) _CS_CHECK_LIB_CREATE_FLAG([$1], + [$cs_lib_item/include], [$cs_lib_item/lib], [$2]) + _CS_CHECK_LIB_CREATE_FLAG( + [$1], [$cs_lib_item], [$cs_lib_item], [$2]) + ;; + esac + done]) + + + +#------------------------------------------------------------------------------ +# _CS_CHECK_LIB_CREATE_FLAG(VARIABLE, HEADER-DIR, LIBRARY-DIR, LIBRARY) +# Helper macro for _CS_CHECK_LIB_CREATE_FLAGS(). Constructs build tuples +# suitable for CS_CHECK_BUILD() for given header and library directories, +# and appends the tuples to the shell variable VARIABLE. Synthesizes +# tuples which check for LIBRARY as a MacOS/X framework, and a standard +# link library. +#------------------------------------------------------------------------------ +AC_DEFUN([_CS_CHECK_LIB_CREATE_FLAG], + [AS_IF([test -n "$2"], [cs_check_lib_cflag="-I$2"], [cs_check_lib_cflag='']) + AS_IF([test -n "$3"], [cs_check_lib_lflag="-L$3"], [cs_check_lib_lflag='']) + AS_IF([test -n "$4"], + [cs_check_lib_libs="-l$4" + cs_check_lib_framework="-framework $4"], + [cs_check_lib_libs='' + cs_check_lib_framework='']) + $1="$$1 + CS_CREATE_TUPLE( + [$cs_check_lib_cflag], + [$cs_check_lib_lflag], + [$cs_check_lib_framework]) + CS_CREATE_TUPLE( + [$cs_check_lib_cflag], + [$cs_check_lib_lflag], + [$cs_check_lib_libs])"]) +# checklibtool.m4 -*- Autoconf -*- +#============================================================================== +# Copyright (C)2004 by Eric Sunshine +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +#============================================================================== +AC_PREREQ([2.56]) + +#------------------------------------------------------------------------------ +# CS_CHECK_LIBTOOL +# Find and identify the various implementations of libtool. In +# particular, this macro is aware of GNU libtool and Apple's libtool +# (which serves a completely different purpose). On MacOS/X, GNU libtool +# is typically named glibtool, however a user might also use Fink to +# install the unadorned libtool; and the Fink-installed version might +# shadow Apple's own libtool if it appears in the PATH before the Apple +# tool. This macro jumps through the necessary hoops to distinguish and +# locate the various implementations. Sets the shell variable LIBTOOL to +# the located GNU libtool (if any), and APPLE_LIBTOOL to the located +# Apple libtool. Invokes AC_SUBST() for LIBTOOL and APPLE_LIBTOOL. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_CHECK_LIBTOOL], +[# GNU: Search for libtool before glibtool since Fink version is likely newer. +m4_define([cs_lt_path_gnu], + [/sw/bin$PATH_SEPARATOR/usr/local/bin$PATH_SEPARATOR$PATH]) +AS_IF([test -z "$LIBTOOL"], + [CS_CHECK_TOOLS([LIBTOOL_TEST], [libtool glibtool gnulibtool], [], + [cs_lt_path_gnu]) + AS_IF([test -n "$LIBTOOL_TEST"], + [CS_PATH_PROG([LIBTOOL_PATH], [$LIBTOOL_TEST], [], [cs_lt_path_gnu]) + CS_LIBTOOL_CLASSIFY([$LIBTOOL_PATH], + [LIBTOOL="$LIBTOOL_PATH"], + [AS_IF([test -z "$APPLE_LIBTOOL"], [APPLE_LIBTOOL="$LIBTOOL_PATH"]) + CS_CHECK_TOOLS([LIBTOOL], [glibtool gnulibtool])])])]) +AC_SUBST([LIBTOOL]) + +# Apple: Ensure that Apple libtool will be found before GNU libtool from Fink. +m4_define([cs_lt_path_apple],[/bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH]) +AS_IF([test -z "$APPLE_LIBTOOL"], + [CS_PATH_PROG([CS_LT_APPLE], [libtool], [], [cs_lt_path_apple]) + CS_LIBTOOL_CLASSIFY([$CS_LT_APPLE], [], + [APPLE_LIBTOOL="$CS_LT_APPLE"])]) +AC_SUBST([APPLE_LIBTOOL])]) + +AC_DEFUN([CS_LIBTOOL_CLASSIFY], + [AS_IF([test -n "$1"], + [AC_MSG_CHECKING([classification of $1]) + CS_LIBTOOL_GNU_IFELSE([$1], + [AC_MSG_RESULT([gnu]) + $2], + [AC_MSG_RESULT([apple]) + $3])])]) + +AC_DEFUN([CS_LIBTOOL_GNU_IFELSE], + [AS_IF([AC_RUN_LOG([$1 --version 1>&2])], [$2], [$3])]) +#============================================================================== +# Copyright (C)2003-2006 by Eric Sunshine +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +#============================================================================== +AC_PREREQ([2.56]) + +#------------------------------------------------------------------------------ +# CS_CHECK_OPENGL +# Check for OpenGL. +# +# IMPLEMENTATION NOTES +# +# Some Mesa installations require pthread, so pthread flags are employed if +# available. +# +# The check for opengl32 needs to precede other checks because Cygwin users +# often have Mesa installed, and Mesa's OpenGL library is compiled without the +# __stdcall flags which results in link errors, whereas Microsoft's native +# opengl32 works fine. Conversely, some Unix implementations have Wine +# installed (Windows emulation layer) which includes an opengl32.so library. +# We need to avoid detection of this library on Unix since it would cause an +# undesirable dependence upon Wine. +# +# Many OpenGL libraries on Unix already contain GLX, so there is no separate +# GLX library, thus we first check for GLX using the discovered OpenGL library +# before attempting to locate a separate GLX-specific library. +# +# On MacOS/X, some users have XFree86 installed which creates a link from +# /usr/include/GL to /usr/X11R6/include/GL. We want to ignore this directory +# and instead check for Apple's OpenGL.framework, if we are not cross-building +# for Darwin. We accomplish this by placing the OpenGL.framework test ahead of +# the other tests. +# +# At least one user (Jorrit) has a strange installation in which inclusion of +# fails if an int32 is not present, thus we must take this into +# account. +#------------------------------------------------------------------------------ +m4_define([cs_define_int32], + [[#if !HAVE_TYPE_INT32 + typedef long int32; + #endif + ]]) + +# CS_GL_INCLUDE(CPP-MACRO,FALLBACK,HEADER) +AC_DEFUN([CS_GL_INCLUDE], + [[#if HAVE_WINDOWS_H + #if !HAVE_TYPE_INT32 + typedef long int32; + #endif + #include + #endif + #ifndef CS_HEADER_GLOBAL + #define CS_HEADER_GLOBAL(X,Y) CS_HEADER_GLOBAL_COMPOSE(X,Y) + #define CS_HEADER_GLOBAL_COMPOSE(X,Y) + #endif + #ifdef $1 + #include CS_HEADER_GLOBAL($1,$3) + #else + #include <$2/$3> + #endif]]) + +AC_DEFUN([CS_CHECK_OPENGL], + [AC_REQUIRE([CS_CHECK_HOST]) + AC_REQUIRE([CS_CHECK_COMMON_LIBS]) + AC_REQUIRE([CS_CHECK_PTHREAD]) + AC_REQUIRE([AC_PATH_X]) + AC_REQUIRE([AC_PATH_XTRA]) + AC_CHECK_TYPE([int32], [AC_DEFINE([HAVE_TYPE_INT32], [], + [Whether the int32 type is available])], []) + AC_CHECK_HEADERS([windows.h], [], [], [cs_define_int32]) + + # Apply plaform-specific flags if necessary. + cs_gl_plat_cflags='' + cs_gl_plat_lflags='' + cs_gl_plat_libs='' + AS_IF([test -n "$cs_cv_libm_cflags$cs_cv_libm_lflags$cs_cv_libm_libs"], + [cs_gl_plat_cflags="$cs_cv_libm_cflags $cs_gl_plat_cflags" + cs_gl_plat_lflags="$cs_cv_libm_lflags $cs_gl_plat_lflags" + cs_gl_plat_libs="$cs_cv_libm_libs $cs_gl_plat_libs"]) + AS_IF([test $cs_cv_sys_pthread = yes], + [cs_gl_plat_cflags="$cs_cv_sys_pthread_cflags $cs_gl_plat_cflags" + cs_gl_plat_lflags="$cs_cv_sys_pthread_lflags $cs_gl_plat_lflags" + cs_gl_plat_libs="$cs_cv_sys_pthread_libs $cs_gl_plat_libs"]) + AS_IF([test "$no_x" != yes], + [cs_gl_plat_cflags="$X_CFLAGS $cs_gl_plat_cflags" + cs_gl_plat_lflags="$cs_gl_plat_lflags" + cs_gl_plat_libs=" + $X_PRE_LIBS $X_LIBS -lX11 -lXext $X_EXTRA_LIBS $cs_gl_plat_libs"]) + + # Mesa requested? + AC_ARG_WITH([mesa], [AC_HELP_STRING([--with-mesa], + [use Mesa OpenGL library if available (default YES)])], + [], [with_mesa=yes]) + + AS_IF([test $with_mesa != no], + [cs_mesa_gl=CS_CREATE_TUPLE([],[],[-lMesaGL])]) + + # MacOS/X or Darwin? + AS_IF([test "x$cs_host_macosx" = "xyes"], + [cs_osx_gl=CS_CREATE_TUPLE([-DCS_OPENGL_PATH=OpenGL],[],[-framework OpenGL])]) + AS_IF([test "x$cs_host_macosx" = "xyes"], + [cs_gl_plat_lflags="$cs_plat_lflags -Wl,-dylib_file,/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib:/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib"]) + + # Windows? + AS_IF([test $cs_host_family = windows], + [cs_win32_gl=CS_CREATE_TUPLE([],[],[-lopengl32])]) + + # Check for OpenGL. + CS_CHECK_BUILD([for OpenGL], [cs_cv_libgl], + [AC_LANG_PROGRAM([CS_GL_INCLUDE([CS_OPENGL_PATH],[GL],[gl.h])],[glEnd()])], + [$cs_win32_gl \ + $cs_osx_gl \ + CS_CREATE_TUPLE([],[],[-lGL]) \ + CS_CREATE_TUPLE([],[],[-lgl]) \ + $cs_mesa_gl], [], + [CS_EMIT_BUILD_RESULT([cs_cv_libgl], [GL])], [], [], + [$cs_gl_plat_cflags], [$cs_gl_plat_lflags], [$cs_gl_plat_libs])]) + + +#------------------------------------------------------------------------------ +# CS_CHECK_GLU +# Check for GLU. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_CHECK_GLU], + [AC_REQUIRE([CS_CHECK_OPENGL]) + AS_IF([test $cs_cv_libgl = yes], + [AS_IF([test $with_mesa != no], + [cs_mesa_glu=CS_CREATE_TUPLE([],[],[-lMesaGLU])]) + + # MacOS/X or Darwin? + AS_IF([test "x$cs_host_macosx" = "xyes"], + [cs_osx_glu=CS_CREATE_TUPLE([-DCS_GLU_PATH=OpenGL],[],[-framework OpenGL])]) + + # Windows? + AS_IF([test $cs_host_family = windows], + [cs_win32_glu=CS_CREATE_TUPLE([],[],[-lglu32])]) + + # Check for GLU. + CS_CHECK_BUILD([for GLU], [cs_cv_libglu], + [AC_LANG_PROGRAM( + [CS_GL_INCLUDE([CS_GLU_PATH],[GL],[glu.h])], [gluNewQuadric()])], + [$cs_osx_glu \ + CS_CREATE_TUPLE() \ + $cs_win32_glu \ + CS_CREATE_TUPLE([],[],[-lGLU]) \ + CS_CREATE_TUPLE([],[],[-lglu]) \ + $cs_mesa_glu], [], + [CS_EMIT_BUILD_RESULT([cs_cv_libglu], [GLU])], [], [], + [$cs_cv_libgl_cflags], [$cs_cv_libgl_lflags], [$cs_cv_libgl_libs])])]) + + +#------------------------------------------------------------------------------ +# CS_CHECK_GLX +# Check for GLX. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_CHECK_GLX], + [AC_REQUIRE([CS_CHECK_OPENGL]) + AS_IF([test $cs_cv_libgl = yes], + [AS_IF([test $with_mesa != no], + [cs_mesa_glx=CS_CREATE_TUPLE([],[],[-lMesaGLX])]) + + # Check for GLX. + AS_IF([test "$no_x" != yes], + [CS_CHECK_BUILD([for GLX], [cs_cv_libglx], + [AC_LANG_PROGRAM([[#include ]], [glXWaitGL()])], + [CS_CREATE_TUPLE() \ + CS_CREATE_TUPLE([],[],[-lGLX]) \ + CS_CREATE_TUPLE([],[],[-lglx]) \ + $cs_mesa_glx], [], + [CS_EMIT_BUILD_RESULT([cs_cv_libglx], [GLX])], [], [], + [$cs_cv_libgl_cflags], [$cs_cv_libgl_lflags], [$cs_cv_libgl_libs])])])]) + + +#------------------------------------------------------------------------------ +# CS_CHECK_GLXEXT([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# Check for GLX extensions. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_CHECK_GLXEXT], + [AC_REQUIRE([CS_CHECK_GLX]) + AS_IF([test x$cs_cv_libglx = "xyes"], + [# Check for GLX extensions. + CS_CHECK_BUILD([for GLX extensions], [cs_cv_libglx_extensions], + [AC_LANG_PROGRAM( + [[#define GLX_GLXEXT_PROTOTYPES + #include ]], + [glXGetProcAddressARB(0)])], + [CS_CREATE_TUPLE( + [$cs_cv_libglx_cflags], + [$cs_cv_libglx_lflags], + [$cs_cv_libglx_libs])], + [], [$1], [$2])])]) + + + +#------------------------------------------------------------------------------ +# CS_CHECK_GLUT +# Check for GLUT. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_CHECK_GLUT], + [AC_REQUIRE([CS_CHECK_GLU]) + AS_IF([test x$cs_cv_libglu = "xyes"], + [# MacOS/X or Darwin? + AS_IF([test "x$cs_host_macosx" = "xyes"], + [cs_osx_glut=CS_CREATE_TUPLE([-DCS_GLUT_PATH=GLUT],[],[-framework GLUT])]) + + # Windows? + AS_IF([test $cs_host_family = windows], + [cs_win32_glut=CS_CREATE_TUPLE([],[],[-lglut32])]) + + # Check for GLUT. + CS_CHECK_BUILD([for GLUT], [cs_cv_libglut], + [AC_LANG_PROGRAM( + [CS_GL_INCLUDE([CS_GLUT_PATH],[GL],[glut.h])], [glutSwapBuffers()])], + [$cs_osx_glut \ + CS_CREATE_TUPLE() \ + $cs_win32_glut \ + CS_CREATE_TUPLE([],[],[-lGLUT]) \ + CS_CREATE_TUPLE([],[],[-lglut])], [], + [CS_EMIT_BUILD_RESULT([cs_cv_libglut], [GLUT])], [], [], + [$cs_cv_libgl_cflags $cs_cv_libglu_cflags], + [$cs_cv_libgl_lflags $cs_cv_libglu_lflags], + [$cs_cv_libgl_libs $cs_cv_libglu_libs])])]) + +# checkpic.m4 -*- Autoconf -*- +#============================================================================== +# Copyright (C)2005 by Eric Sunshine +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +#============================================================================== +AC_PREREQ([2.56]) + +#------------------------------------------------------------------------------ +# CS_COMPILER_PIC([LANGUAGE], [CACHE-VAR], [ACTION-IF-FOUND], +# [ACTION-IF-NOT-FOUND]) +# Check if compiler can be instructed to produce +# position-independent-code (PIC). This feature is required by some +# platforms when building plugin modules and shared libraries. If +# LANGUAGE is not provided, then `C' is assumed (other options include +# `C++'). If CACHE-VAR is not provided, then it defaults to the name +# "cs_cv_prog_compiler_pic". If a PIC-enabling option (such as `-fPIC') +# is discovered, then it is assigned to CACHE-VAR and ACTION-IF-FOUND is +# invoked; otherwise the empty string is assigned to CACHE-VAR and +# ACTION-IF-NOT-FOUND is invoked. +# +# IMPLEMENTATION NOTES +# +# On some platforms (such as Windows), the -fPIC option is superfluous +# and emits a warning "-fPIC ignored for target (all code is position +# independent)", despite the fact that the compiler accepts the option +# and returns a success code. We want to re-interpret the warning as a +# failure in order to avoid unnecessary compiler diagnostics in case the +# client inserts the result of this check into CFLAGS, for instance. We +# do so by attempting to promote warnings to errors using the result of +# CS_COMPILER_ERRORS(). As an extra safe-guard, we also scan the compiler +# output for an appropriate diagnostic because some gcc warnings fail to +# promote to error status despite use of -Werror. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_COMPILER_PIC], + [CS_COMPILER_ERRORS([$1], + [m4_default([$2_werror],[cs_cv_prog_compiler_pic_werror])]) + CS_CHECK_BUILD_FLAGS( + [how to enable m4_default([$1],[C]) PIC generation], + [m4_default([$2],[cs_cv_prog_compiler_pic])], + [CS_CREATE_TUPLE([-fPIC])], [$1], [$3], [$4], + [m4_default([$$2_werror],[$cs_cv_prog_compiler_pic_werror])], [], [], + [fPIC])]) + +# Backward-compatiblity alias. +AC_DEFUN([CS_CHECK_COMPILER_PIC], [CS_COMPILER_PIC([$1],[$2],[$3],[$4])]) +# checkprog.m4 -*- Autoconf -*- +#============================================================================== +# Copyright (C)2004 by Eric Sunshine +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +#============================================================================== +AC_PREREQ([2.56]) + +#------------------------------------------------------------------------------ +# cs_bin_paths_default +# Comma delimited list of additional directories in which tools and +# commands might be found. +# +# Present Cases: +# /usr/local/bin -- Although a common location for executables, it is +# now-and-then absent from the default PATH setting. +# /sw/bin -- Fink, the MacOS/X manager of Unix packages, installs +# executables here. +#------------------------------------------------------------------------------ +m4_define([cs_bin_paths_default], [/usr/local/bin, /sw/bin]) + + +#------------------------------------------------------------------------------ +# CS_CHECK_PROG(VARIABLE, PROGRAM, VALUE-IF-FOUND, [VALUE-IF-NOT-FOUND], +# [PATH], [REJECT]) +# Simple wrapper for AC_CHECK_PROG() which ensures that the search path +# is augmented by the directories mentioned in cs_bin_paths_default. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_CHECK_PROG], + [_CS_PROG_PATH_PREPARE + AC_CHECK_PROG([$1], [$2], [$3], [$4], + m4_ifval([$5], [_CS_PROG_CLIENT_PATH([$5])]), [$6])]) + + +#------------------------------------------------------------------------------ +# CS_CHECK_PROGS(VARIABLE, PROGRAMS, [VALUE-IF-NOT-FOUND], [PATH]) +# Simple wrapper for AC_CHECK_PROGS() which ensures that the search path +# is augmented by the directories mentioned in cs_bin_paths_default. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_CHECK_PROGS], + [_CS_PROG_PATH_PREPARE + AC_CHECK_PROGS([$1], [$2], [$3], + m4_ifval([$4], [_CS_PROG_CLIENT_PATH([$4])]))]) + + +#------------------------------------------------------------------------------ +# CS_CHECK_TOOL(VARIABLE, TOOL, [VALUE-IF-NOT-FOUND], [PATH]) +# Simple wrapper for AC_CHECK_TOOL() which ensures that the search path +# is augmented by the directories mentioned in cs_bin_paths_default. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_CHECK_TOOL], + [_CS_PROG_PATH_PREPARE + AC_CHECK_TOOL([$1], [$2], [$3], + m4_ifval([$4], [_CS_PROG_CLIENT_PATH([$4])]))]) + + +#------------------------------------------------------------------------------ +# CS_CHECK_TOOLS(VARIABLE, TOOLS, [VALUE-IF-NOT-FOUND], [PATH]) +# Simple wrapper for AC_CHECK_TOOLS() which ensures that the search path +# is augmented by the directories mentioned in cs_bin_paths_default. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_CHECK_TOOLS], + [_CS_PROG_PATH_PREPARE + AC_CHECK_TOOLS([$1], [$2], [$3], + m4_ifval([$4], [_CS_PROG_CLIENT_PATH([$4])]))]) + + +#------------------------------------------------------------------------------ +# CS_PATH_PROG(VARIABLE, PROGRAM, [VALUE-IF-NOT-FOUND], [PATH]) +# Simple wrapper for AC_PATH_PROG() which ensures that the search path +# is augmented by the directories mentioned in cs_bin_paths_default. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_PATH_PROG], + [_CS_PROG_PATH_PREPARE + AC_PATH_PROG([$1], [$2], [$3], + m4_ifval([$4], [_CS_PROG_CLIENT_PATH([$4])]))]) + + +#------------------------------------------------------------------------------ +# CS_PATH_PROGS(VARIABLE, PROGRAMS, [VALUE-IF-NOT-FOUND], [PATH]) +# Simple wrapper for AC_PATH_PROGS() which ensures that the search path +# is augmented by the directories mentioned in cs_bin_paths_default. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_PATH_PROGS], + [_CS_PROG_PATH_PREPARE + AC_PATH_PROGS([$1], [$2], [$3], + m4_ifval([$4], [_CS_PROG_CLIENT_PATH([$4])]))]) + + +#------------------------------------------------------------------------------ +# CS_PATH_TOOL(VARIABLE, TOOL, [VALUE-IF-NOT-FOUND], [PATH]) +# Simple wrapper for AC_PATH_TOOL() which ensures that the search path +# is augmented by the directories mentioned in cs_bin_paths_default. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_PATH_TOOL], + [_CS_PROG_PATH_PREPARE + AC_PATH_TOOL([$1], [$2], [$3], + m4_ifval([$4], [_CS_PROG_CLIENT_PATH([$4])]))]) + + +#------------------------------------------------------------------------------ +# _CS_PROG_PATH_PREPARE +# Ensure that the PATH environment variable mentions the set of +# directories listed in cs_bin_paths_default. These directories may not +# appear by default in the typical PATH, yet they might be common +# locations for tools and commands. +#------------------------------------------------------------------------------ +AC_DEFUN([_CS_PROG_PATH_PREPARE], + [AS_REQUIRE([_AS_PATH_SEPARATOR_PREPARE]) + AS_IF([test "$cs_prog_path_prepared" != yes], + [cs_prog_path_prepared=yes + PATH="$PATH[]m4_foreach([cs_bin_path], [cs_bin_paths_default], + [$PATH_SEPARATOR[]cs_bin_path])" + export PATH])]) + + +#------------------------------------------------------------------------------ +# _CS_PROG_CLIENT_PATH(CLIENT-PATH) +# Given a client-supplied replacement for PATH, augment the list by +# appending the locations mentioned in cs_bin_paths_default. +#------------------------------------------------------------------------------ +AC_DEFUN([_CS_PROG_CLIENT_PATH], + [AS_REQUIRE([_AS_PATH_SEPARATOR_PREPARE])dnl + $1[]m4_foreach([cs_bin_path], [cs_bin_paths_default], + [$PATH_SEPARATOR[]cs_bin_path])]) +# checkpthread.m4 -*- Autoconf -*- +#============================================================================== +# Copyright (C)2003-2005 by Eric Sunshine +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +#============================================================================== +AC_PREREQ([2.56]) + +#------------------------------------------------------------------------------ +# CS_CHECK_PTHREAD([REJECT-MASK]) +# Check for pthread. Also check if the pthread implementation supports +# the recursive and timed mutex extensions. (Timed mutexes are needed for +# the NPTL: New Posix Thread Library on GNU/Linux if the mutex is going +# to be used with any of the timed condition-wait functions.) The shell +# variable cs_cv_sys_pthread is set to "yes" if pthread is available, +# else "no". If available, then the variables cs_cv_sys_pthread_cflags, +# cs_cv_sys_pthread_lflags, and cs_cv_sys_pthread_libs are set. (As a +# convenience, these variables can be emitted to an output file with +# CS_EMIT_BUILD_RESULT() by passing "cs_cv_sys_pthread" as its CACHE-VAR +# argument.) If the recursive mutex extension is supported, then +# cs_cv_sys_pthread_mutex_recursive will be set with the literal name of +# the constant which must be passed to pthread_mutexattr_settype() to +# enable this feature. The constant name will be typically +# PTHREAD_MUTEX_RECURSIVE or PTHREAD_MUTEX_RECURSIVE_NP. If the recursive +# mutex extension is not available, then +# cs_cv_sys_pthread_mutex_recursive will be set to "no". If the timed +# mutex extension is supported, then cs_cv_sys_pthread_mutex_timed will +# be set with the literal name of the constant which must be passed to +# pthread_mutexattr_settype() to enable this feature. The constant name +# will be typically PTHREAD_MUTEX_TIMED or PTHREAD_MUTEX_TIMED_NP. If the +# timed mutex extension is not available, then +# cs_cv_sys_pthread_mutex_timed will be set to "no". REJECT-MASK can be +# used to limit the platforms on which the pthread test is performed. It +# is compared against $host_os; matches are rejected. If omitted, then +# the test is performed on all platforms. Examples: To avoid testing on +# Cygwin, use "cygwin*"; to avoid testing on Cygwin and AIX, use +# "cygwin*|aix*". +#------------------------------------------------------------------------------ +AC_DEFUN([CS_CHECK_PTHREAD], + [AC_REQUIRE([AC_CANONICAL_HOST]) + case $host_os in + m4_ifval([$1], + [$1) + cs_cv_sys_pthread=no + ;; + ]) + *) + CS_CHECK_BUILD([for pthread], [cs_cv_sys_pthread], + [AC_LANG_PROGRAM( + [[#include + #include + void* worker(void* p) { (void)p; return p; }]], + [pthread_t tid; + sem_t sem; + pthread_create(&tid, 0, worker, 0); + sem_init(&sem, 0, 0); + sem_destroy(&sem);])], + [cs_pthread_flags]) + ;; + esac + _CS_CHECK_MUTEX_FEATURE([PTHREAD_MUTEX_RECURSIVE], + [cs_cv_sys_pthread_mutex_recursive], [for pthread recursive mutexes])]) + +# _CS_CHECK_MUTEX_FEATURE(FEATURE, CACHE-VAR, MESSAGE) +AC_DEFUN([_CS_CHECK_MUTEX_FEATURE], + [AS_IF([test $cs_cv_sys_pthread = yes], + [AC_CACHE_CHECK([$3], [$2], + [CS_BUILD_IFELSE( + [AC_LANG_PROGRAM( + [[#include ]], + [pthread_mutexattr_t attr; + pthread_mutexattr_settype(&attr, CS_MUTEX_FEATURE);])], + [CS_CREATE_TUPLE([-DCS_MUTEX_FEATURE=$1]) \ + CS_CREATE_TUPLE([-DCS_MUTEX_FEATURE=$1_NP])], + [], + [$2=`echo $cs_build_cflags | sed 's/.*\($1_*N*P*\).*/\1/'`], + [$2=no], + [$cs_cv_sys_pthread_cflags -D_GNU_SOURCE], + [$cs_cv_sys_pthread_lflags], + [$cs_cv_sys_pthread_libs])])], + [$2=no])]) + +#------------------------------------------------------------------------------ +# CS_CHECK_PTHREAD_ATFORK(CACHE-VAR) +# Checks whether the pthread library contains pthread_atfork(). Sets +# CACHE-VAR to "yes" or "no", according to the test result. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_CHECK_PTHREAD_ATFORK], + [AS_IF([test $cs_cv_sys_pthread = yes], + [AC_CACHE_CHECK([for pthread_atfork support], [$1], + [CS_BUILD_IFELSE( + [AC_LANG_PROGRAM( + [[#include ]], + [pthread_atfork (0, 0, 0);])], + [], [], + [$1=yes], [$1=no], + [$cs_cv_sys_pthread_cflags -D_GNU_SOURCE], + [$cs_cv_sys_pthread_lflags], + [$cs_cv_sys_pthread_libs])])], + [$1=no])]) + +m4_define([cs_pthread_flags], + [CS_CREATE_TUPLE() \ + CS_CREATE_TUPLE([], [], [-lpthread]) \ + CS_CREATE_TUPLE([], [], [-lpthread -lrt]) \ + CS_CREATE_TUPLE([-pthread], [-pthread], []) \ + CS_CREATE_TUPLE([-pthread], [-pthread], [-lpthread]) \ + CS_CREATE_TUPLE([-pthread], [-pthread], [-lc_r])]) +# checktt2.m4 -*- Autoconf -*- +#============================================================================== +# Copyright (C)2004,2005 by Eric Sunshine +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +#============================================================================== +AC_PREREQ([2.56]) + +#------------------------------------------------------------------------------ +# CS_CHECK_TEMPLATE_TOOLKIT2([EMITTER]) +# Check if Template Toolkit 2 (http://www.tt2.org/) is available. The +# shell variable cs_cv_perl_tt2 is set to "yes" if the package is +# discovered, else "no". Also sets the shell variable TTREE to the name +# path of the 'ttree' utility program and invokes AC_SUBST(). If EMITTER +# is provided and the package was discovered, then +# CS_EMIT_BUILD_PROPERTY() is invoked with EMITTER in order to record the +# value of the TTREE variable in an output file. As a convenience, if +# EMITTER is the literal value "emit" or "yes", then +# CS_EMIT_BUILD_RESULT()'s default emitter will be used. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_CHECK_TEMPLATE_TOOLKIT2], + [CS_CHECK_PROGS([PERL], [perl5 perl]) + AS_IF([test -n "$PERL"], + [AC_CACHE_CHECK([for TemplateToolkit], [cs_cv_perl_tt2], + [AS_IF([AC_RUN_LOG( + [$PERL -M'Template 2.11' -MTemplate::Plugin -e 0 1>&2])], + [cs_cv_perl_tt2=yes], + [cs_cv_perl_tt2=no])]) + CS_PATH_PROGS([TTREE], [ttree]) + AS_IF([test $cs_cv_perl_tt2 = yes && test -n "$TTREE"], + [CS_EMIT_BUILD_PROPERTY([TTREE], [$TTREE], [], [], + CS_EMITTER_OPTIONAL([$1]))])])]) +# compiler.m4 -*- Autoconf -*- +#============================================================================= +# Copyright (C)2003 by Matze Braun +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +#============================================================================= + +#----------------------------------------------------------------------------- +# Detection of C and C++ compilers and setting flags +# +# CS_PROG_CC +# Detects the C compiler. Also takes care of the CFLAGS, CPPFLAGS and CC +# environment variables. This will filter out all -g and -O from the +# CFLAGS variable because Autoconf's -g and -O defaults are not always +# desired. This will also set the CMD.CC and COMPILER.CFLAGS variables +# in Jamconfig +# CS_PROG_CXX +# Detects the C++ compiler. Also takes care of the CXXFLAGS, CPPFLAGS +# and CXX environment variables. This will filter out all -g and -O from +# the CXXFLAGS variable because Autoconf's -g and -O defaults are not +# always desired. This will also set the CMD.C++ and COMPILER.C++FLAGS +# variables in Jamconfig +# CS_PROG_LINK +# Tries to determine a linker. This is done by checking if a C++ or +# Objecctive-C++ compiler is available in which case it is used for +# linking; otherwise the C or Objective-C compiler is used. This also +# sets the CMD.LINK and COMPILER.LFLAGS variables in Jamconfig and +# respects the LDFLAGS environment variable. Finally, checks if linker +# recognizes -shared and sets PLUGIN.LFLAGS; and checks if linker +# recognizes -soname and sets PLUGIN.LFLAGS.USE_SONAME to "yes". +#----------------------------------------------------------------------------- +AC_DEFUN([CS_PROG_CC],[ + CFLAGS="$CFLAGS" # Filter undesired flags + AC_PROG_CC + AS_IF([test -n "$CC"],[ + CS_EMIT_BUILD_PROPERTY([CMD.CC], [$CC]) + CS_EMIT_BUILD_PROPERTY([COMPILER.CFLAGS], [$CPPFLAGS $CFLAGS], [+]) + + # Check if compiler recognizes -pipe directive. + CS_EMIT_BUILD_FLAGS([if $CC accepts -pipe], [cs_cv_prog_cc_pipe], + [CS_CREATE_TUPLE([-pipe])], [C], [COMPILER.CFLAGS], [+]) + ]) +]) + +AC_DEFUN([CS_PROG_CXX],[ + CXXFLAGS="$CXXFLAGS" # Filter undesired flags + AC_PROG_CXX + AS_IF([test -n "$CXX"],[ + CS_EMIT_BUILD_PROPERTY([CMD.C++], [$CXX]) + + CS_EMIT_BUILD_PROPERTY([COMPILER.C++FLAGS], [$CPPFLAGS $CXXFLAGS], [+]) + + # Check if compiler can be instructed to produce position-independent-code + # (PIC). This feature is required by some platforms when building plugin + # modules and shared libraries. + CS_COMPILER_PIC([C++], [cs_cv_prog_cxx_pic], + [CS_EMIT_BUILD_PROPERTY([COMPILER.C++FLAGS.PIC], + [$cs_cv_prog_cxx_pic])]) + ]) +]) + +AC_DEFUN([CS_PROG_LINK],[ + AC_REQUIRE([CS_PROG_CXX]) + AS_IF([test -n "$CXX"], + [CS_EMIT_BUILD_PROPERTY([CMD.LINK], [AS_ESCAPE([$(CMD.C++)])])], + [CS_EMIT_BUILD_PROPERTY([CMD.LINK], [AS_ESCAPE([$(CMD.CC)])])]) + + CS_EMIT_BUILD_PROPERTY([COMPILER.LFLAGS], [$LDFLAGS], [+]) + + # Check if compiler/linker recognizes -shared directive which is needed for + # linking plugin modules. Unfortunately, the Apple compiler (and possibly + # others) requires extra effort. Even though the compiler does not recognize + # the -shared option, it nevertheless returns a "success" result after emitting + # the warning "unrecognized option `-shared'". Worse, even -Werror fails to + # promote the warning to an error, so we must instead scan the compiler's + # output for an appropriate diagnostic. + CS_CHECK_BUILD_FLAGS([if -shared is accepted], [cs_cv_prog_link_shared], + [CS_CREATE_TUPLE([-shared $cs_cv_prog_cxx_pic])], [C++], + [CS_EMIT_BUILD_PROPERTY([PLUGIN.LFLAGS], [-shared], [+])], [], + [], [], [], [shared]) + + # Check if linker recognizes -soname which is used to assign a name internally + # to plugin modules. + CS_CHECK_BUILD([if -soname is accepted], [cs_cv_prog_link_soname], [], + [CS_CREATE_TUPLE([-Wl,-soname,foobar])], [C++], + [CS_EMIT_BUILD_PROPERTY([PLUGIN.LFLAGS.USE_SONAME], [yes])]) +]) +#------------------------------------------------------------------------------ +# Determine host platform. Recognized families: Unix, Windows, MacOS/X. +# Orginial Macros Copyright (C)2003 Eric Sunshine +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +#------------------------------------------------------------------------------ + +#------------------------------------------------------------------------------ +# Determine host CPU. +# +# CS_CHECK_HOST_CPU +# Set the shell variable cs_host_cpu to a normalized form of the CPU name +# returned by config.guess/config.sub. Typically, Crystal Space's +# conception of CPU name is the same as that returned by +# config.guess/config.sub, but there may be exceptions as seen in the +# `case' statement. Also takes the normalized name, uppercases it to +# form a name suitable for the C preprocessor. Additionally sets the +# TARGET.PROCESSOR Jamconfig property. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_CHECK_HOST_CPU], + [AC_REQUIRE([AC_CANONICAL_HOST]) + case $host_cpu in + [[Ii][3-9]86*|[Xx]86*]) cs_host_cpu=x86 ;; + *) cs_host_cpu=$host_cpu ;; + esac + cs_host_cpu_normalized="AS_TR_CPP([$cs_host_cpu])" + CS_JAMCONFIG_PROPERTY([TARGET.PROCESSOR], [$cs_host_cpu_normalized]) + ]) + + +#------------------------------------------------------------------------------ +# CS_CHECK_HOST +# Sets the shell variables cs_host_target cs_host_family, +# cs_host_os_normalized, and cs_host_os_normalized_uc. Emits appropriate +# CS_PLATFORM_UNIX, CS_PLATFORM_WIN32, CS_PLATFORM_MACOSX via +# AC_DEFINE(), and TARGET.OS and TARGET.OS.NORMALIZED to Jamconfig. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_CHECK_HOST], + [AC_REQUIRE([AC_CANONICAL_HOST]) + CS_CHECK_HOST_CPU + cs_host_os_normalized='' + case $host_os in + mingw*|cygwin*) + cs_host_target=win32gcc + cs_host_family=windows + ;; + darwin*) + _CS_CHECK_HOST_DARWIN + ;; + *) + # Everything else is assumed to be Unix or Unix-like. + cs_host_target=unix + cs_host_family=unix + ;; + esac + + case $cs_host_family in + windows) + AC_DEFINE([CS_PLATFORM_WIN32], [], + [Define when compiling for Win32]) + AS_IF([test -z "$cs_host_os_normalized"], + [cs_host_os_normalized='Win32']) + ;; + unix) + AC_DEFINE([CS_PLATFORM_UNIX], [], + [Define when compiling for Unix and Unix-like (i.e. MacOS/X)]) + AS_IF([test -z "$cs_host_os_normalized"], + [cs_host_os_normalized='Unix']) + ;; + esac + + cs_host_os_normalized_uc="AS_TR_CPP([$cs_host_os_normalized])" + CS_JAMCONFIG_PROPERTY([TARGET.OS], [$cs_host_os_normalized_uc]) + CS_JAMCONFIG_PROPERTY([TARGET.OS.NORMALIZED], [$cs_host_os_normalized]) +]) + +AC_DEFUN([_CS_CHECK_HOST_DARWIN], + [AC_REQUIRE([CS_PROG_CC]) + AC_REQUIRE([CS_PROG_CXX]) + + # Both MacOS/X and Darwin are identified via $host_os as "darwin". We need + # a way to distinguish between the two. If Carbon.h is present, then + # assume MacOX/S; if not, assume Darwin. If --with-x=yes was invoked, and + # Carbon.h is present, then assume that user wants to cross-build for + # Darwin even though build host is MacOS/X. + # IMPLEMENTATION NOTE *1* + # The QuickTime 7.0 installer removes , which + # causes #include to fail unconditionally. Re-installing + # the QuickTime SDK should restore the header, however not all developers + # know to do this, so we work around the problem of the missing + # CarbonSound.h by #defining __CARBONSOUND__ in the test in order to + # prevent Carbon.h from attempting to #include the missing header. + # IMPLEMENTATION NOTE *2* + # At least one MacOS/X user switches between gcc 2.95 and gcc 3.3 with a + # script which toggles the values of CC, CXX, and CPP. Unfortunately, CPP + # was being set to run the preprocessor directly ("cpp", for instance) + # rather than running it via the compiler ("gcc -E", for instance). The + # problem with running the preprocessor directly is that __APPLE__ and + # __GNUC__ are not defined, which causes the Carbon.h check to fail. We + # avoid this problem by supplying a non-empty fourth argument to + # AC_CHECK_HEADER(), which causes it to test compile the header only (which + # is a more robust test), rather than also testing it via the preprocessor. + + AC_DEFINE([__CARBONSOUND__], [], + [Avoid problem caused by missing ]) + AC_CHECK_HEADER([Carbon/Carbon.h], + [cs_host_macosx=yes], [cs_host_macosx=no], [/* force compile */]) + + AS_IF([test $cs_host_macosx = yes], + [AC_MSG_CHECKING([for --with-x]) + AS_IF([test "${with_x+set}" = set && test "$with_x" = "yes"], + [AC_MSG_RESULT([yes (assume Darwin)]) + cs_host_macosx=no], + [AC_MSG_RESULT([no])])]) + + AS_IF([test $cs_host_macosx = yes], + [cs_host_target=macosx + cs_host_family=unix + cs_host_os_normalized='MacOS/X' + AC_DEFINE([CS_PLATFORM_MACOSX], [], + [Define when compiling for MacOS/X]) + + AC_CACHE_CHECK([for Objective-C compiler], [cs_cv_prog_objc], + [cs_cv_prog_objc="$CC"]) + CS_JAMCONFIG_PROPERTY([CMD.OBJC], [$cs_cv_prog_objc]) + AC_CACHE_CHECK([for Objective-C++ compiler], [cs_cv_prog_objcxx], + [cs_cv_prog_objcxx="$CXX"]) + CS_JAMCONFIG_PROPERTY([CMD.OBJC++], [$cs_cv_prog_objcxx])], + + [cs_host_target=unix + cs_host_family=unix])]) +# diagnose.m4 -*- Autoconf -*- +#============================================================================== +# Copyright (C)2003 by Eric Sunshine +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +#============================================================================== +AC_PREREQ([2.56]) + +#------------------------------------------------------------------------------ +# CS_MSG_ERROR(ERROR-DESCRIPTION, [EXIT-STATUS]) +# A convenience wrapper for AC_MSG_ERROR() which invokes AC_CACHE_SAVE() +# before aborting the script. Saving the cache should make subsequent +# re-invocations of the configure script faster once the user has +# corrected the problem(s) which caused the failure. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_MSG_ERROR], + [AC_CACHE_SAVE + AC_MSG_ERROR([$1], [$2])]) +# embed.m4 -*- Autoconf -*- +#============================================================================== +# Copyright (C)2003,2005 by Eric Sunshine +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +#============================================================================== +AC_PREREQ([2.56]) + +#------------------------------------------------------------------------------ +# CS_META_INFO_EMBED([EMITTER], [GPL-OKAY]) +# Determine if plugin meta-information should be embedded or if it should +# exist in a stand-alone .csplugin file, and check if necessary tools and +# libraries are present. Sets the shell variable +# enable_meta_info_embedding to "yes" if the user requested embedding or +# if it was enabled by default; otherwise sets it to "no". +# +# If EMITTER is provided, then a subset of the following variables +# (depending upon platform and availability) are recorded by invoking +# CS_EMIT_BUILD_PROPERTY() with EMITTER. As a convenience, if EMITTER is +# the literal value "emit" or "yes", then CS_EMIT_BUILD_RESULT()'s +# default emitter will be used. +# +# EMBED_META := yes or no +# EMBED_META.CFLAGS := compiler flags +# EMBED_META.LFLAGS := linker flags +# CMD.WINDRES := windres.exe +# OBJCOPY.AVAILABLE := yes or no +# CMD.OBJCOPY := objcopy.exe +# LIBBFD.AVAILABLE := yes or no +# LIBBFD.CFLAGS := libbfd compiler flags +# LIBBFD.LFLAGS := libbfd linker flags +# ELF.AVAILABLE := yes or no +# +# In general, clients need only concern themselves with the various +# EMBED_META-related variables. For building plugin modules, utilize +# EMBED_META.CFLAGS when compiling, and EMBED_META.LFLAGS when linking. +# +# On Unix, when CS' own ELF metadata reader can't be used (because the +# necessary header file elf.h was not found) embedding is accomplished +# via libbfd, which carries a GPL license. Projects which carry licenses +# not compatible with GPL should consider carefully before enabling +# embedding on Unix. If your project is GPL-compatible, then set GPL-OKAY +# to "yes". This will indicate that it is safe to use libbfd if the ELF +# reader can not be used. If your project is not GPL-compatible, then +# set it to "no" in order to disable embedding on Unix if the ELF reader +# is not usable. (The user can still manually override the setting via +# the --enable-meta-info-embedding option.) +# +# IMPLEMENTATION NOTES +# +# Recent versions of Mingw supply libbfd and libiberty. Since Crystal +# Space uses native Win32 API for meta-information embedding on Windows, +# we do not require these libraries on Windows. More importantly, users +# do not want to see these GPL-licensed libraries appear in the link +# statement for plugin modules, thus we explicitly disable the libbfd +# test on Windows. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_META_INFO_EMBED], + [AC_REQUIRE([AC_CANONICAL_HOST]) + _CS_META_INFO_EMBED_ENABLE([$1], [$2]) + AS_IF([test $enable_meta_info_embedding = yes], + [_CS_META_INFO_EMBED_TOOLS([$1]) + AS_IF([test $cs_header_elf_h = yes], + [CS_EMIT_BUILD_PROPERTY([ELF.AVAILABLE], [yes], [], [], + CS_EMITTER_OPTIONAL([$1]))], + [case $host_os in + mingw*|cygwin*) ;; + *) + CS_CHECK_LIBBFD([$1], + [CS_EMIT_BUILD_PROPERTY([EMBED_META.CFLAGS], + [$cs_cv_libbfd_ok_cflags], [+], [], + CS_EMITTER_OPTIONAL([$1])) + CS_EMIT_BUILD_PROPERTY([EMBED_META.LFLAGS], + [$cs_cv_libbfd_ok_lflags $cs_cv_libbfd_ok_libs], + [+], [], CS_EMITTER_OPTIONAL([$1]))]) + ;; + esac])])]) + + +#------------------------------------------------------------------------------ +# _CS_META_INFO_EMBED_ENABLE([EMITTER], [GPL-OKAY]) +# Helper for CS_META_INFO_EMBED which adds an +# --enable-meta-info-embedding option to the configure script allowing +# the user to control embedding. Sets the shell variable +# enable_meta_info_embedding to yes or no. +# +# IMPLEMENTATION NOTES +# +# On Unix, embedding is enabled by default if elf.h is found and disabled +# by default unless overridden via GPL-OKAY because libbfd carries a GPL +# license which may be incompatible with a project's own license (such as +# LGPL). +#------------------------------------------------------------------------------ +AC_DEFUN([_CS_META_INFO_EMBED_ENABLE], + [AC_REQUIRE([CS_CHECK_HOST]) + AC_CHECK_HEADERS([elf.h], [cs_header_elf_h=yes], [cs_header_elf_h=no]) + AC_MSG_CHECKING([whether to embed plugin meta-information]) + case $cs_host_target in + unix) AS_IF([test $cs_header_elf_h = yes], + [cs_embed_meta_info_default=yes], + [cs_embed_meta_info_default=m4_ifval([$2],[$2],[no])]) ;; + *) cs_embed_meta_info_default=yes ;; + esac + AC_ARG_ENABLE([meta-info-embedding], + [AC_HELP_STRING([--enable-meta-info-embedding], + [store plugin meta-information directly inside plugin modules if + supported by platform; if disabled, meta-information is stored in + stand-alone .csplugin files; this option is enabled by default for + non-Unix platforms and on Unix platforms with ELF-format object + files; it is disabled by default on Unix platforms if ELF is not + available and the project uses a non-GPL-compatible license (such + as LGPL) since the non-ELF Unix embedding technology requires the + GPL-licensed libbfd library; if ELF is not available, enable this + option on Unix only if you are certain you want a GPL-licensed + library infecting your project])], + [], [enable_meta_info_embedding=$cs_embed_meta_info_default]) + AC_MSG_RESULT([$enable_meta_info_embedding]) + CS_EMIT_BUILD_PROPERTY([EMBED_META], [$enable_meta_info_embedding], + [], [], CS_EMITTER_OPTIONAL([$1]))]) + + + +#------------------------------------------------------------------------------ +# _CS_META_INFO_EMBED_TOOLS([EMITTER]) +# Helper for CS_META_INFO_EMBED() which searches for tools required for +# plugin meta-info embedding. +#------------------------------------------------------------------------------ +AC_DEFUN([_CS_META_INFO_EMBED_TOOLS], + [CS_CHECK_TOOLS([WINDRES], [windres]) + CS_EMIT_BUILD_PROPERTY([CMD.WINDRES], [$WINDRES], [], [], + CS_EMITTER_OPTIONAL([$1])) + + CS_CHECK_TOOLS([OBJCOPY], [objcopy]) + AS_IF([test -n "$OBJCOPY"], + [CS_EMIT_BUILD_PROPERTY([OBJCOPY.AVAILABLE], [yes], [], [], + CS_EMITTER_OPTIONAL([$1])) + CS_EMIT_BUILD_PROPERTY([CMD.OBJCOPY], [$OBJCOPY], [], [], + CS_EMITTER_OPTIONAL([$1]))])]) + + + +#------------------------------------------------------------------------------ +# CS_CHECK_LIBBFD([EMITTER], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# Exhaustive check for a usable GPL-licensed libbfd, the Binary File +# Descriptor library, a component of binutils, which allows low-level +# manipulation of executable and object files. If EMITTER is provided, +# then the following variables are recorded by invoking +# CS_EMIT_BUILD_PROPERTY() with EMITTER. As a convenience, if EMITTER is +# the literal value "emit" or "yes", then CS_EMIT_BUILD_RESULT()'s +# default emitter will be used. +# +# LIBBFD.AVAILABLE := yes or no +# LIBBFD.CFLAGS := libbfd compiler flags +# LIBBFD.LFLAGS := libbfd linker flags +# +# The shell variable cs_cv_libbfd_ok is set to yes if a usable libbfd was +# discovered, else no. If found, the additional shell variables +# cs_cv_libbfd_ok_cflags, cs_cv_libbfd_ok_lflags, and +# cs_cv_libbfd_ok_libs are also set. +# +# WARNING +# +# libbfd carries a GPL license which is incompatible with the LGPL +# license of Crystal Space. Do not use this library with projects under +# less restrictive licenses, such as LGPL. +# +# IMPLEMENTATION NOTES +# +# It seems that some platforms have two version of libiberty installed: +# one from binutils and one from gcc. The binutils version resides in +# /usr/lib, whereas the gcc version resides in the gcc installation +# directory. The gcc version, by default, takes precedence at link time +# over the binutils version. Unfortunately, in broken cases, the gcc +# version of libiberty is missing htab_create_alloc() which is required +# by some libbfd functions. The extensive secondary check of libbfd +# catches this anomalous case of broken gcc libiberty. It turns out that +# it is possible to make the linker prefer the binutils version by +# specifying -L/usr/lib, thus the extensive test attempts to do so in an +# effort to resolve this unfortunate issue. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_CHECK_LIBBFD], + [CS_CHECK_LIB_WITH([bfd], + [AC_LANG_PROGRAM([[#include ]], [bfd_init();])], + [], [], [], [], [], [], [-liberty]) + + AS_IF([test $cs_cv_libbfd = yes], + [CS_CHECK_BUILD([if libbfd is usable], [cs_cv_libbfd_ok], + [AC_LANG_PROGRAM([[#include ]], + [bfd* p; + asection* s; + bfd_init(); + p = bfd_openr(0,0); + bfd_check_format(p,bfd_object); + bfd_get_section_by_name(p,0); + bfd_section_size(p,s); + bfd_get_section_contents(p,s,0,0,0); + bfd_close(p);])], + [CS_CREATE_TUPLE() CS_CREATE_TUPLE([],[-L/usr/lib],[])], + [], [], [], [], + [$cs_cv_libbfd_cflags], + [$cs_cv_libbfd_lflags], + [$cs_cv_libbfd_libs])], + [cs_cv_libbfd_ok=no]) + + AS_IF([test $cs_cv_libbfd_ok = yes], + [CS_EMIT_BUILD_RESULT([cs_cv_libbfd_ok], [LIBBFD], + CS_EMITTER_OPTIONAL([$1])) + $2], + [$3])]) +# emit.m4 -*- Autoconf -*- +#============================================================================== +# Copyright (C)2003-2005 by Eric Sunshine +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +#============================================================================== +AC_PREREQ([2.56]) + +#------------------------------------------------------------------------------ +# CS_EMIT_BUILD_PROPERTY(KEY, VALUE, [APPEND], [EMPTY-OKAY], [EMITTER], +# [UNCONDITIONAL]) +# A utility function which invokes an emitter to record the KEY/VALUE +# tuple if VALUE is not the empty string (after leading and trailing +# whitespace is stripped). If EMPTY-OKAY is not an empty string, then the +# property is emitted even if VALUE is empty; that is, it is emitted +# unconditionally. If APPEND is the empty string, then the emitter sets +# the key's value directly (though it may be overridden by the +# environment), otherwise the emitter appends VALUE to the existing value +# of the key. EMITTER is a macro name, such as CS_JAMCONFIG_PROPERTY or +# CS_MAKEFILE_PROPERTY, which performs the actual task of emitting the +# KEY/VALUE tuple; it should also accept APPEND as an optional third +# argument. If EMITTER is omitted, CS_JAMCONFIG_PROPERTY is used. Some +# emitters accept an optional fourth argument, UNCONDITIONAL, which +# instructs it to set KEY's value unconditionally, even if KEY already +# had been assigned a value via some other mechanism (such as imported +# from the environment, or from Jambase, in the case of +# CS_JAMCONFIG_PROPERTY). +#------------------------------------------------------------------------------ +AC_DEFUN([CS_EMIT_BUILD_PROPERTY], + [cs_build_prop_val="$2" + cs_build_prop_val=CS_TRIM([$cs_build_prop_val]) + m4_ifval([$4], + [CS_JAMCONFIG_PROPERTY([$1], [$cs_build_prop_val], [$3])], + AS_IF([test -n "$cs_build_prop_val"], + [m4_default([$5],[CS_JAMCONFIG_PROPERTY])( + [$1], [$cs_build_prop_val], [$3], [$6])]))]) + + + +#------------------------------------------------------------------------------ +# CS_EMIT_BUILD_RESULT(CACHE-VAR, PREFIX, [EMITTER]) +# Record the results of CS_CHECK_BUILD() or CS_CHECK_LIB_WITH() via some +# emitter. If CACHE-VAR indicates that the build succeeded, then the +# following properties are emitted: +# +# PREFIX.AVAILABLE = yes +# PREFIX.CFLAGS = $CACHE-VAR_cflags +# PREFIX.LFLAGS = $CACHE-VAR_lflags $CACHE-VAR_libs +# +# EMITTER is a macro name, such as CS_JAMCONFIG_PROPERTY or +# CS_MAKEFILE_PROPERTY, which performs the actual task of emitting the +# KEY/VALUE tuple. If EMITTER is omitted, CS_JAMCONFIG_PROPERTY is used. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_EMIT_BUILD_RESULT], + [AS_IF([test "$$1" = yes], + [CS_EMIT_BUILD_PROPERTY([$2.AVAILABLE], [yes], [], [], [$3]) + CS_EMIT_BUILD_PROPERTY([$2.CFLAGS], [$$1_cflags], [], [], [$3]) + CS_EMIT_BUILD_PROPERTY([$2.LFLAGS], [$$1_lflags $$1_libs], + [], [], [$3])])]) + + + +#------------------------------------------------------------------------------ +# CS_EMIT_BUILD_FLAGS(MESSAGE, CACHE-VAR, FLAGS, [LANGUAGE], EMITTER-KEY, +# [APPEND], [ACTION-IF-RECOGNIZED], +# [ACTION-IF-NOT-RECOGNIZED], [EMITTER]) +# A convenience wrapper for CS_CHECK_BUILD_FLAGS() which also records the +# results via CS_EMIT_BUILD_PROPERTY(). Checks if the compiler or linker +# recognizes a command-line option. MESSAGE is the "checking" message. +# CACHE-VAR is the shell cache variable which receives the flag +# recognized by the compiler or linker, or "no" if the flag was not +# recognized. FLAGS is a whitespace- delimited list of build tuples +# created with CS_CREATE_TUPLE(). Each tuple from FLAGS is attempted in +# order until one is found which is recognized by the compiler. After +# that, no further flags are checked. LANGUAGE is typically either C or +# C++ and specifies which compiler to use for the test. If LANGUAGE is +# omitted, C is used. EMITTER-KEY is the name to pass as the emitter's +# "key" argument if a usable flag is encountered. If APPEND is not the +# empty string, then the discovered flag is appended to the existing +# value of the EMITTER-KEY. If the command-line option was recognized, +# then ACTION-IF-RECOGNIZED is invoked, otherwise +# ACTION-IF-NOT-RECOGNIZED is invoked. EMITTER is a macro name, such as +# CS_JAMCONFIG_PROPERTY or CS_MAKEFILE_PROPERTY, which performs the +# actual task of emitting the KEY/VALUE tuple; it should also accept +# APPEND as an optional third argument. If EMITTER is omitted, +# CS_JAMCONFIG_PROPERTY is used. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_EMIT_BUILD_FLAGS], + [CS_CHECK_BUILD_FLAGS([$1], [$2], [$3], [$4], + [CS_EMIT_BUILD_PROPERTY([$5], [$$2], [$6], [], [$9]) + $7], + [$8])]) + + + +#------------------------------------------------------------------------------ +# CS_EMITTER_OPTIONAL([EMITTER]) +# The CS_EMIT_FOO() macros optionally accept an emitter. If no emitter is +# supplied to those macros, then a default emitter is chosen. Other +# macros, however, which perform testing and optionally emit the results +# may wish to interpret an omitted EMITTER as a request not to emit the +# results. CS_EMITTER_OPTIONAL() is a convenience macro to help in these +# cases. It should be passed to one of the CS_EMIT_FOO() macros in place +# of the literal EMITTER argument. It functions by re-interpretating +# EMITTER as follows: +# +# - If EMITTER is omitted, then CS_NULL_EMITTER is returned, effectively +# disabling output by the CS_EMIT_FOO() macro. +# - If EMITTER is the literal string "emit" or "yes", then it returns an +# empty string, which signals to the CS_EMIT_FOO() macro that is should +# use its default emitter. +# - Any other value for EMITTER is passed along as-is to the +# CS_EMIT_FOO() macro. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_EMITTER_OPTIONAL], + [m4_case([$1], + [], [[CS_NULL_EMITTER]], + [emit], [], + [yes], [], + [[$1]])]) + + + +#------------------------------------------------------------------------------ +# CS_NULL_EMITTER(KEY, VALUE, [APPEND]) +# A do-nothing emitter suitable for use as the EMITTER argument of one of +# the CS_EMIT_FOO() macros. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_NULL_EMITTER], [: +]) + + + +#------------------------------------------------------------------------------ +# CS_SUBST_EMITTER(KEY, VALUE, [APPEND]) +# An emitter wrapped around AC_SUBST(). Invokes +# AC_SUBST(AS_TR_SH(KEY),VALUE). The APPEND argument is ignored. +# Suitable for use as the EMITTER argument of one of the CS_EMIT_FOO() +# macros. The call to AS_TR_SH() ensures that KEY is transformed into a +# valid shell variable. For instance, if a macro attempts to emit +# MYLIB.CFLAGS and MYLIB.LFLAGS via CS_SUBST_EMITTER(), then the names +# will be transformed to MYLIB_CFLAGS and MYLIB_LFLAGS, respectively, for +# the invocation of AC_SUBST(). +#------------------------------------------------------------------------------ +AC_DEFUN([CS_SUBST_EMITTER], [AC_SUBST(AS_TR_SH([$1]),[$2])]) + + + +#------------------------------------------------------------------------------ +# CS_DEFINE_EMITTER(KEY, VALUE, [APPEND]) +# An emitter wrapped around AC_DEFINE_UNQUOTED(). Invokes +# AC_DEFINE_UNQUOTED(AS_TR_CPP(KEY),VALUE). The APPEND argument is +# ignored. Suitable for use as the EMITTER argument of one of the +# CS_EMIT_FOO() macros. The call to AS_TR_CPP() ensures that KEY is a +# well-formed token for the C-preprocessor. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_DEFINE_EMITTER], + [AC_DEFINE_UNQUOTED(AS_TR_CPP([$1]),[$2], + [Define when feature is available])]) +# headercache.m4 -*- Autoconf -*- +#============================================================================== +# Copyright (C)2003 by Eric Sunshine +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +#============================================================================== +AC_PREREQ([2.56]) + +#------------------------------------------------------------------------------ +# Text cache facility for C-style #define properties. The cache is stored in +# the shell variable cs_header_text. +# +# CS_HEADER_APPEND(TEXT) +# Append text to the C header text cache. This is a cover for +# CS_TEXT_CACHE_APPEND(). +# +# CS_HEADER_PREPEND(TEXT) +# Prepend text to the C header text cache. This is a cover for +# CS_TEXT_CACHE_PREPEND(). +# +# CS_HEADER_PROPERTY(KEY, [VALUE]) +# Append a line of the form "#define KEY VALUE" to the C header text +# cache. If the VALUE argument is omitted, then the appended line has +# the simplified form "#define KEY". +# +# CS_HEADER_OUTPUT(FILENAME) +# Instruct config.status to write the C header text cache to the given +# filename. This is a cover for CS_TEXT_CACHE_OUTPUT(). +#------------------------------------------------------------------------------ +AC_DEFUN([CS_HEADER_APPEND], [CS_TEXT_CACHE_APPEND([cs_header_text], [$1])]) +AC_DEFUN([CS_HEADER_PREPEND], [CS_TEXT_CACHE_PREPEND([cs_header_text], [$1])]) +AC_DEFUN([CS_HEADER_PROPERTY], +[CS_HEADER_APPEND([@%:@define $1[]m4_ifval([$2], [ $2], []) +])]) +AC_DEFUN([CS_HEADER_OUTPUT], [CS_TEXT_CACHE_OUTPUT([cs_header_text], [$1])]) +#----------------------------------------------------------------------------- +# installdirs.m4 (c) Matze Braun +# Macro for emitting the installation paths gathered by Autoconf. +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# CS_OUTPUT_INSTALLDIRS([EMITTER], [RAW-BACKSLASHES]) +# Emit installation directories collected by Autoconf. EMITTER is a macro +# name, such as CS_JAMCONFIG_PROPERTY or CS_MAKEFILE_PROPERTY, which performs +# the actual task of emitting the KEY/VALUE tuple. If EMITTER is omitted, +# CS_JAMCONFIG_PROPERTY is used. If RAW-BACKSLASHES is not provided, then +# backslashes in emitted values are each escaped with an additional +# backslash. If RAW-BACKSLASHES is not the null value, then backslashes are +# emitted raw. The following properties are emitted: +# +# prefix +# exec_prefix +# bindir +# sbindir +# libexecdir +# datadir +# sysconfdir +# sharedstatedir +# localstatedir +# libdir +# includedir +# oldincludedir +# infodir +# mandir +#----------------------------------------------------------------------------- +AC_DEFUN([CS_OUTPUT_INSTALLDIRS],[ +# Handle the case when no prefix is given, and the special case when a path +# contains more than 2 slashes, these paths seem to be correct but Jam fails +# on them. +AS_IF([test $prefix = NONE], + [cs_install_prefix="$ac_default_prefix"], + [cs_install_prefix=`echo "$prefix" | sed -e 's:///*:/:g'`]) +AS_IF([test $exec_prefix = NONE], + [cs_install_exec_prefix="AS_ESCAPE([$(prefix)])"], + [cs_install_exec_prefix=`echo "$exec_prefix" | sed -e 's:///*:/:g'`]) + +_CS_OUTPUT_INSTALL_DIRS([$1], [prefix], + [CS_PREPARE_INSTALLPATH([$cs_install_prefix], [$2])]) +_CS_OUTPUT_INSTALL_DIRS([$1], [exec_prefix], + [CS_PREPARE_INSTALLPATH([$cs_install_exec_prefix], [$2])]) +_CS_OUTPUT_INSTALL_DIRS([$1], [bindir], + [CS_PREPARE_INSTALLPATH([$bindir], [$2])]) +_CS_OUTPUT_INSTALL_DIRS([$1], [sbindir], + [CS_PREPARE_INSTALLPATH([$sbindir], [$2])]) +_CS_OUTPUT_INSTALL_DIRS([$1], [libexecdir], + [CS_PREPARE_INSTALLPATH([$libexecdir], [$2])]) +_CS_OUTPUT_INSTALL_DIRS([$1], [datadir], + [CS_PREPARE_INSTALLPATH([$datadir], [$2])]) +_CS_OUTPUT_INSTALL_DIRS([$1], [sysconfdir], + [CS_PREPARE_INSTALLPATH([$sysconfdir], [$2])]) +_CS_OUTPUT_INSTALL_DIRS([$1], [sharedstatedir], + [CS_PREPARE_INSTALLPATH([$sharedstatedir], [$2])]) +_CS_OUTPUT_INSTALL_DIRS([$1], [localstatedir], + [CS_PREPARE_INSTALLPATH([$localstatedir], [$2])]) +_CS_OUTPUT_INSTALL_DIRS([$1], [libdir], + [CS_PREPARE_INSTALLPATH([$libdir], [$2])]) +_CS_OUTPUT_INSTALL_DIRS([$1], [includedir], + [CS_PREPARE_INSTALLPATH([$includedir], [$2])]) +_CS_OUTPUT_INSTALL_DIRS([$1], [oldincludedir], + [CS_PREPARE_INSTALLPATH([$oldincludedir], [$2])]) +_CS_OUTPUT_INSTALL_DIRS([$1], [infodir], + [CS_PREPARE_INSTALLPATH([$infodir], [$2])]) +_CS_OUTPUT_INSTALL_DIRS([$1], [mandir], + [CS_PREPARE_INSTALLPATH([$mandir], [$2])]) +]) + +AC_DEFUN([_CS_OUTPUT_INSTALL_DIRS], + [m4_default([$1], [CS_JAMCONFIG_PROPERTY])([$2], [$3])]) + + +#----------------------------------------------------------------------------- +# CS_PREPARE_INSTALLPATH(VALUE, [RAW-BACKSLASHES]) +# Transform variable references of the form ${bla} to $(bla) in VALUE and +# correctly quotes backslashes. This is needed if you need to emit some of +# the paths from Autoconf. RAW-BACKSLASHES has the same meaning as in +# CS_OUTPUT_INSTALLDIRS. +#----------------------------------------------------------------------------- +AC_DEFUN([CS_PREPARE_INSTALLPATH], +[`echo "$1" | sed 's/\${\([[a-zA-Z_][a-zA-Z_]]*\)}/$(\1)/g;m4_ifval([$2], + [s/\\/\\\\/g], [s/\\\\/\\\\\\\\/g])'`]) +# jamcache.m4 -*- Autoconf -*- +#============================================================================== +# Copyright (C)2003 by Eric Sunshine +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +#============================================================================== +AC_PREREQ([2.56]) + +#------------------------------------------------------------------------------ +# Text cache facility for Jam-style properties. The cache is stored in +# the shell variable cs_jamfile_text. +# +# CS_JAMCONFIG_APPEND(TEXT) +# Append text to the Jam text cache. This is a cover for +# CS_TEXT_CACHE_APPEND(). +# +# CS_JAMCONFIG_PREPEND(TEXT) +# Prepend text to the Jam text cache. This is a cover for +# CS_TEXT_CACHE_PREPEND(). +# +# CS_JAMCONFIG_PROPERTY(KEY, VALUE, [APPEND], [UNCONDITIONAL]) +# Append a line of the form "KEY ?= VALUE" to the Jam text cache. If the +# APPEND argument is not the empty string, then VALUE is appended to the +# existing value of KEY using the form "KEY += VALUE". If the +# UNCONDITIONAL argument is not empty, then the value of KEY is set +# unconditionally "KEY = VALUE", rather than via "KEY ?= VALUE". APPEND +# takes precedence over UNCONDITIONAL. Note that if VALUE references +# other Jam variables, for example $(OBJS), then be sure to protect the +# value with AS_ESCAPE(). For example: +# CS_JAMCONFIG_PROPERTY([ALLOBJS], [AS_ESCAPE([$(OBJS) $(LIBOBJS)])]) +# +# CS_JAMCONFIG_OUTPUT(FILENAME) +# Instruct config.status to write the Jam text cache to the given +# filename. This is a cover for CS_TEXT_CACHE_OUTPUT(). +#------------------------------------------------------------------------------ +AC_DEFUN([CS_JAMCONFIG_APPEND], + [CS_TEXT_CACHE_APPEND([cs_jamconfig_text], [$1])]) +AC_DEFUN([CS_JAMCONFIG_PREPEND], + [CS_TEXT_CACHE_PREPEND([cs_jamconfig_text], [$1])]) +AC_DEFUN([CS_JAMCONFIG_PROPERTY], + [CS_JAMCONFIG_APPEND( + [$1 m4_ifval([$3], [+=], m4_ifval([$4], [=], [?=])) \"$2\" ; +])]) +AC_DEFUN([CS_JAMCONFIG_OUTPUT], + [CS_TEXT_CACHE_OUTPUT([cs_jamconfig_text], [$1])]) +# makecache.m4 -*- Autoconf -*- +#============================================================================== +# Copyright (C)2003 by Eric Sunshine +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +#============================================================================== +AC_PREREQ([2.56]) + +#------------------------------------------------------------------------------ +# Text cache facility for makefile-style properties. The cache is stored in +# the shell variable cs_makefile_text. +# +# CS_MAKEFILE_APPEND(TEXT) +# Append text to the makefile text cache. This is a cover for +# CS_TEXT_CACHE_APPEND(). +# +# CS_MAKEFILE_PREPEND(TEXT) +# Prepend text to the makefile text cache. This is a cover for +# CS_TEXT_CACHE_PREPEND(). +# +# CS_MAKEFILE_PROPERTY(KEY, VALUE, [APPEND]) +# Append a line of the form "KEY = VALUE" to the makefile text cache. If +# the APPEND argument is not the empty string, then VALUE is appended to +# the existing value of KEY using the form "KEY += VALUE". Note that if +# VALUE references other makefile variables, for example $(OBJS), then be +# sure to protect the value with AS_ESCAPE(). For example: +# CS_MAKEFILE_PROPERTY([ALLOBJS], [AS_ESCAPE([$(OBJS) $(LIBOBJS)])]) +# +# CS_MAKEFILE_OUTPUT(FILENAME) +# Instruct config.status to write the makefile text cache to the given +# filename. This is a cover for CS_TEXT_CACHE_OUTPUT(). +#------------------------------------------------------------------------------ +AC_DEFUN([CS_MAKEFILE_APPEND], + [CS_TEXT_CACHE_APPEND([cs_makefile_text], [$1])]) +AC_DEFUN([CS_MAKEFILE_PREPEND], + [CS_TEXT_CACHE_PREPEND([cs_makefile_text], [$1])]) +AC_DEFUN([CS_MAKEFILE_PROPERTY], + [CS_MAKEFILE_APPEND([$1 m4_ifval([$3], [+=], [=]) $2 +])]) +AC_DEFUN([CS_MAKEFILE_OUTPUT],[CS_TEXT_CACHE_OUTPUT([cs_makefile_text], [$1])]) +# mkdir.m4 -*- Autoconf -*- +#============================================================================== +# Copyright (C)2003 by Eric Sunshine +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +#============================================================================== +AC_PREREQ([2.56]) + +#------------------------------------------------------------------------------ +# CS_CHECK_MKDIR +# Determine how to create a directory and a directory tree. Sets the +# shell variable MKDIR to the command which creates a directory, and +# MKDIRS to the command which creates a directory tree. Invokes +# AC_SUBST() for MKDIR and MKDIRS. +# +# IMPLEMENTATION NOTES +# We need to know the exact commands, so that we can emit them, thus the +# AS_MKDIR_P function is not what we want to use here since it does not +# provide access to the commands (and might not even discover suitable +# commands). First try "mkdir -p", then try the older "mkdirs". +# Finally, if the mkdir command failed to recognize -p, then it might +# have created a directory named "-p", so clean up that bogus directory. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_CHECK_MKDIR], + [AC_CACHE_CHECK([how to create a directory], [cs_cv_shell_mkdir], + [cs_cv_shell_mkdir='mkdir']) + AC_SUBST([MKDIR], [$cs_cv_shell_mkdir]) + + AC_CACHE_CHECK([how to create a directory tree], [cs_cv_shell_mkdir_p], + [if $cs_cv_shell_mkdir -p . 2>/dev/null; then + cs_cv_shell_mkdir_p='mkdir -p' + elif mkdirs . 2>/dev/null; then + cs_cv_shell_mkdir_p='mkdirs' + fi + test -d ./-p && rmdir ./-p]) + AS_VAR_SET_IF([cs_cv_shell_mkdir_p], + [AC_SUBST([MKDIRS], [$cs_cv_shell_mkdir_p])], + [CS_MSG_ERROR([do not know how to create a directory tree])])]) + + + +#------------------------------------------------------------------------------ +# Replacement for AS_MKDIR_P() from m4sugar/m4sh.m4 which fixes two problems +# which are present in Autoconf 2.57 and probably all earlier 2.5x versions. +# This bug, along with a patch, was submitted to the Autoconf GNATS database by +# Eric Sunshine as #227 on 17-Dec-2002. The bogus "-p" directory bug was fixed +# for Autoconf 2.58 on 26-Sep-2003. The "mkdirs" optimization was not accepted +# (since it is unnecessary; it's only an optimization). +# +# 1) Removes bogus "-p" directory which the stock AS_MKDIR_P() leaves laying +# around in the working directory if the mkdir command does not recognize +# the -p option. +# 2) Takes advantage of the older "mkdirs" program if it exists and if "mkdir +# -p" does not work. +#------------------------------------------------------------------------------ +m4_defun([_AS_MKDIR_P_PREPARE], +[if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p' +elif mkdirs . 2>/dev/null; then + as_mkdir_p='mkdirs' +else + as_mkdir_p='' +fi +test -d ./-p && rmdir ./-p +])# _AS_MKDIR_P_PREPARE + +m4_define([AS_MKDIR_P], +[AS_REQUIRE([_$0_PREPARE])dnl +{ if test -n "$as_mkdir_p"; then + $as_mkdir_p $1 + else + as_dir=$1 + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`AS_DIRNAME("$as_dir")` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || AS_ERROR([cannot create directory $1]); } +])# AS_MKDIR_P +#============================================================================== +# packageinfo.m4 +# Macros for setting general info on the package, such as name and version +# numbers and propagate them to the generated make and Jam property files. +# +# Copyright (C)2003 by Matthias Braun +# Copyright (C)2003,2004 by Eric Sunshine +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +#============================================================================== + +#------------------------------------------------------------------------------ +# CS_PACKAGEINFO([LONGNAME], [COPYRIGHT, [HOMEPAGE]) +# Set additional information for the package. Note that the version +# number of your application should only contain numbers, because on +# Windows you can only set numerical values in some of the file +# properties (such as versioninfo .rc files). +#------------------------------------------------------------------------------ +AC_DEFUN([CS_PACKAGEINFO], + [PACKAGE_LONGNAME="[$1]" + PACKAGE_COPYRIGHT="[$2]" + PACKAGE_HOMEPAGE="[$3]" +]) + + +#------------------------------------------------------------------------------ +# CS_EMIT_PACKAGEINFO([EMITTER]) +# Emit extended package information using the provided EMITTER. EMITTER +# is a macro name, such as CS_JAMCONFIG_PROPERTY or CS_MAKEFILE_PROPERTY, +# which performs the actual task of emitting the KEY/VALUE tuple. If +# EMITTER is omitted, CS_JAMCONFIG_PROPERTY is used. For backward +# compatibility, if EMITTER is the literal value "jam", then +# CS_JAMCONFIG_PROPERTY is used; if it is "make", then +# CS_MAKEFILE_PROPERTY is used; however use of these literal names is +# highly discouraged. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_EMIT_PACKAGEINFO], + [_CS_EMIT_PACKAGEINFO([$1], [PACKAGE_NAME], [$PACKAGE_NAME]) + _CS_EMIT_PACKAGEINFO([$1], [PACKAGE_VERSION], [$PACKAGE_VERSION]) + _CS_EMIT_PACKAGEINFO([$1], [PACKAGE_STRING], [$PACKAGE_STRING]) + _CS_EMIT_PACKAGEINFO([$1], [PACKAGE_BUGREPORT], [$PACKAGE_BUGREPORT]) + _CS_EMIT_PACKAGEINFO([$1], [PACKAGE_LONGNAME], [$PACKAGE_LONGNAME]) + _CS_EMIT_PACKAGEINFO([$1], [PACKAGE_HOMEPAGE], [$PACKAGE_HOMEPAGE]) + _CS_EMIT_PACKAGEINFO([$1], [PACKAGE_COPYRIGHT], [$PACKAGE_COPYRIGHT]) + for cs_veritem in m4_translit(AC_PACKAGE_VERSION, [.], [ ]); do + _CS_EMIT_PACKAGEINFO([$1], [PACKAGE_VERSION_LIST], [$cs_veritem], [+]) + done + ]) + +AC_DEFUN([_CS_EMIT_PACKAGEINFO], + [m4_case([$1], + [make], [CS_MAKEFILE_PROPERTY([$2], [$3], [$4])], + [jam], [CS_JAMCONFIG_PROPERTY([$2], [$3], [$4])], + [], [CS_JAMCONFIG_PROPERTY([$2], [$3], [$4])], + [$1([$2], [$3], [$4])])]) +# path.m4 -*- Autoconf -*- +#============================================================================== +# Copyright (C)2004 by Eric Sunshine +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +#============================================================================== +AC_PREREQ([2.56]) + +#------------------------------------------------------------------------------ +# CS_PATH_NORMALIZE(STRING) +# Normalize a pathname at run-time by transliterating Windows/DOS +# backslashes to forward slashes. Also collapses whitespace. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_PATH_NORMALIZE], +[`echo "x$1" | tr '\\\\' '/' | sed 's/^x//;s/ */ /g;s/^ //;s/ $//'`]) + + +#------------------------------------------------------------------------------ +# CS_RUN_PATH_NORMALIZE(COMMAND) +# Normalize the pathname emitted by COMMAND by transliterating +# Windows/DOS backslashes to forward slashes. Also collapses whitespace. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_RUN_PATH_NORMALIZE], +[`AC_RUN_LOG([$1]) | tr '\\\\' '/' | sed 's/^x//;s/ */ /g;s/^ //;s/ $//'`]) +############################################################################### +# progver.m4 +# Written by Norman Kramer +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +############################################################################### +# +# From the input pattern we create regular expressions we send through sed +# to extract the version information from the standard input to sed. +# Then we extract from the resulting version string subparts. +# The same happens with the supplied version string. It too is split into its +# subparts according to the pattern. +# Then the subparts from the gathered version string and the supplied one are +# compared. +# +# How does the pattern look like ? +# It is a sequence of 9s and _s and separators. +# 9 denotes a non empty sequence of digits. +# _ denotes a non empty sequence of characters from the class [a-zA-Z]. +# | everything behind is optional +# Everything else is treated as a separator. +# Consecutive 9s and _s are compressed to contain only one of each type. +# For instance "99_.9.__abc9_" will become "9_.9._abc9_". +# +# How we find the parts we compare ? +# From this transformed string we yield the parts we will later compare. +# We break up the string as follows: +# Any sequence of separators represent one breakup. Additional breakups are +# placed behind every 9 and _ . +# So the example from above will give: +# +# "99_.9.__abc9_" ===compress==> "9_.9._abc9_" ===breakup==> "9" "_" "9" "_" "9" "_" +# +# How we create the regular expressions ? +# We take the compressed pattern and quote every separator. +# The we replace the 9s with [0-9][0-9]* +# and the _s with [a-zA-Z][a-zA-Z]* . +# The above example will become: +# +# "99_.9.__abc9_" ===compress==> "9_.9._abc9_" ===rexify==> +# [0-9][0-9]*[a-zA-Z][a-zA-Z]*\.[0-9][0-9]*\.[a-zA-Z][a-zA-Z]*\a\b\c[0-9][0-9]*[a-zA-Z][a-zA-Z]* +# +# Voila. +# +# To yield the subparts from the string we additionally enclose the +# 9s and _s with \( and \). +# +############################################################################### + +# **************************************************************** +# ** helper definitions ** +# **************************************************************** +m4_define([CS_VCHK_RUNTH], [m4_pushdef([i], [$1])m4_if($1,0,,[CS_VCHK_RUNTH(m4_decr($1), [$2])][$2])m4_popdef([i])]) +m4_define([CS_VCHK_PREFIX], []) +m4_define([CS_VCHK_SUFFIX], []) +m4_define([CS_VCHK_GROUPPREFIX], [\(]) +m4_define([CS_VCHK_GROUPSUFFIX], [\)]) +m4_define([CS_VCHK_CHAR], [[[[a-zA-Z]]]]) +m4_define([CS_VCHK_DIGIT], [[[0-9]]]) +m4_define([CS_VCHK_SEQUENCE], [CS_VCHK_PREFIX[]CS_VCHK_SINGLE[]CS_VCHK_SINGLE[]*CS_VCHK_SUFFIX[]]) +m4_define([CS_VCHK_OPTSEQUENCE], [CS_VCHK_PREFIX[]CS_VCHK_SINGLE[]*CS_VCHK_SUFFIX[]]) +m4_define([CS_VCHK_REXSEQ], [m4_bpatsubst($1, [$2], [[]CS_VCHK_SEQUENCE[]])]) +m4_define([CS_VCHK_GROUPINGON], [m4_pushdef([CS_VCHK_PREFIX], [CS_VCHK_GROUPPREFIX])m4_pushdef([CS_VCHK_SUFFIX], [CS_VCHK_GROUPSUFFIX])]) +m4_define([CS_VCHK_GROUPINGOFF], [m4_popdef([CS_VCHK_SUFFIX])m4_popdef([CS_VCHK_PREFIX])]) +m4_define([CS_VCHK_OPTON], [m4_pushdef([CS_VCHK_SEQUENCE], [CS_VCHK_OPTSEQUENCE])]) +m4_define([CS_VCHK_OPTOFF], [m4_popdef([CS_VCHK_SEQUENCE])]) +m4_define([CS_VCHK_RMOPT], [CS_VCHK_RMCHAR([$1], m4_index([$1], [|]))]) +m4_define([CS_VCHK_RMCHAR], [m4_if($2,-1,[$1],m4_substr([$1], 0, $2)[]m4_substr([$1], m4_incr($2)))]) +m4_define([CS_VCHK_RMALL], [m4_translit([$1], [|], [])]) +m4_define([CS_VCHK_CUTOFF], [m4_if(m4_index($1,[|]),-1, [$1], [m4_substr($1, 0, m4_index($1,[|]))])]) +m4_define([CS_VCHK_CYCLEOPT], [ +m4_if($2,-1,, [m4_pushdef([i], CS_VCHK_CUTOFF([$1])) m4_pushdef([j], CS_VCHK_DUMMY_TAIL([$1])) CS_VCHK_CYCLEOPT( CS_VCHK_RMOPT([$1]), m4_index($1, [|]), [$3])$3 m4_popdef([i]) m4_popdef([j])]) +]) +m4_define([CS_VCHK_TAIL], [m4_if(m4_index($1,[|]),-1, [], [m4_substr($1, m4_incr(m4_index($1,[|])))])]) +m4_define([CS_VCHK_DUMMY_COMPRESS], [m4_bpatsubst(m4_bpatsubst([$1], [__*], [A]), [99*], [0])]) +m4_define([CS_VCHK_DUMMY_TAIL], [CS_VCHK_DUMMY_COMPRESS(m4_translit(CS_VCHK_TAIL([$1]), [|], []))]) + +# **************************************************************** +# ** FlagsOn / FlagsOff ** +# **************************************************************** +m4_define([CS_VCHK_FLAGSON], +[m4_if($#, 0, [], + $1, [], [], + [$1], [group], [CS_VCHK_GROUPINGON[]], + [$1], [opt], [CS_VCHK_OPTON[]])dnl +m4_if($#, 0, [], $1, [], [], [CS_VCHK_FLAGSON(m4_shift($@))])]) + +m4_define([CS_VCHK_FLAGSOFF], +[m4_if($#, 0, [], + $1, [], [], + $1, [group], [CS_VCHK_GROUPINGOFF[]], + [$1], [opt], [CS_VCHK_OPTOFF[]])dnl +m4_if($#, 0, [], $1, [], [], [CS_VCHK_FLAGSOFF(m4_shift($@))])]) + +# **************************************************************** +# ** rexify / sedify ** +# **************************************************************** +m4_define([CS_VCHK_REXIFY], +[m4_pushdef([CS_VCHK_SINGLE], [$1])dnl +CS_VCHK_FLAGSON(m4_shift(m4_shift(m4_shift($@))))dnl +CS_VCHK_REXSEQ([$3], [$2])dnl +CS_VCHK_FLAGSOFF(m4_shift(m4_shift(m4_shift($@))))dnl +m4_popdef([CS_VCHK_SINGLE])]) + +m4_define([CS_VCHK_QUOTESEP], [m4_bpatsubst($1, [[^9_]], [\\\&])]) + +m4_define([CS_VCHK_REXCHAR], [CS_VCHK_REXIFY([CS_VCHK_CHAR], [__*], $@)]) +m4_define([CS_VCHK_REXDIGIT], [CS_VCHK_REXIFY([CS_VCHK_DIGIT], [99*], $@)]) +m4_define([CS_VCHK_SEDIFY], [CS_VCHK_REXDIGIT([CS_VCHK_REXCHAR([CS_VCHK_QUOTESEP([$1])], m4_shift($@))], m4_shift($@))]) +m4_define([CS_VCHK_SEDEXPRALL], [/CS_VCHK_SEDIFY([$1])/!d;s/.*\(CS_VCHK_SEDIFY([$1])\).*/\1/;q]) +m4_define([CS_VCHK_SEDEXPRNTH], [/CS_VCHK_SEDIFY([$1])/!d;s/.*CS_VCHK_SEDIFY([$1],[group]).*/\$2/]) + +# **************************************************************** +# ** Pattern splitting ** +# **************************************************************** +m4_define([CS_VCHK_SPLITSEP], [CS_VCHK_REXIFY([s], [[^9_][^9_]*], $@)]) +m4_define([CS_VCHK_SPLITDIGIT], [CS_VCHK_REXIFY([d], [99*], $@)]) +m4_define([CS_VCHK_SPLITCHAR], [CS_VCHK_REXIFY([c], [__*], $@)]) + +# **************************************************************** +# ** return a list of 's' 'd' 'c' 'e' chars denoting the kind ** +# ** pattern parts: separator, digit, char, end ** +# **************************************************************** +m4_define([CS_VCHK_PATTERNLIST], [m4_pushdef([CS_VCHK_SEQUENCE], [CS_VCHK_SINGLE ])dnl +m4_translit(CS_VCHK_SPLITDIGIT([CS_VCHK_SPLITCHAR([CS_VCHK_SPLITSEP([$1])])]), [ ], m4_if([$2],[],[ ],[$2]))e[]dnl +m4_popdef([CS_VCHK_SEQUENCE])]) + +# **************************************************************** +# ** Build the shell commands we emit to the configure script. ** +# **************************************************************** +m4_define([CS_VCHK_PATCOUNT], [m4_len(m4_bpatsubst(CS_VCHK_PATTERNLIST([$1]), [[^dc]]))]) + +# **************************************************************************************** +# ** CS_VCHK_EXTRACTVERSION(EXTRACT_CALL, MIN_VERSION, PATTERN, PRGPREFIX, COMPARISION) ** +# **************************************************************************************** +m4_define([CS_VCHK_EXTRACTVERSION], +[cs_prog_$4_is_version= +cs_prog_$4_min_version= +cs_prog_$4_is_suffix= +cs_prog_$4_min_suffix= +cs_prog_$4_is_suffix_done= +cs_prog_$4_min_suffix_done= +CS_VCHK_CYCLEOPT([$3], [], +[test -z $cs_prog_$4_is_version && cs_prog_$4_is_version=`$1 | sed 'CS_VCHK_SEDEXPRALL([i])'` +test -n "$cs_prog_$4_is_version" && test -z $cs_prog_$4_is_suffix_done && { cs_prog_$4_is_suffix_done=yes ; cs_prog_$4_is_suffix=j ; } +]) +CS_VCHK_CYCLEOPT([$3], , +[test -z $cs_prog_$4_min_version && cs_prog_$4_min_version=`echo $2 | sed 'CS_VCHK_SEDEXPRALL([i])'` +test -n "$cs_prog_$4_min_version" && test -z $cs_prog_$4_min_suffix_done && { cs_prog_$4_min_suffix_done=yes ; cs_prog_$4_min_suffix=j ; } +]) +CS_VCHK_RUNTH([CS_VCHK_PATCOUNT([$3])], + [cs_prog_$4_is_ver_[]i=`echo ${cs_prog_$4_is_version}${cs_prog_$4_is_suffix} | sed 'CS_VCHK_SEDEXPRNTH([CS_VCHK_RMALL([$3])], [i])'` +]) +CS_VCHK_RUNTH([CS_VCHK_PATCOUNT([$3])], + [cs_prog_$4_min_ver_[]i=`echo $cs_prog_$4_min_version${cs_prog_$4_min_suffix} | sed 'CS_VCHK_SEDEXPRNTH([CS_VCHK_RMALL([$3])], [i])'` +]) +cs_cv_prog_$4_version_ok='' +CS_VCHK_RUNTH([CS_VCHK_PATCOUNT([$3])], +[test -z "$cs_cv_prog_$4_version_ok" && { expr "$cs_prog_$4_is_ver_[]i" "$5" "$cs_prog_$4_min_ver_[]i" >/dev/null || cs_cv_prog_$4_version_ok=no ; } +test -z "$cs_cv_prog_$4_version_ok" && { expr "$cs_prog_$4_min_ver_[]i" "$5" "$cs_prog_$4_is_ver_[]i" >/dev/null || cs_cv_prog_$4_version_ok=yes ; } +]) +AS_IF([test -z "$cs_cv_prog_$4_version_ok"], [cs_cv_prog_$4_version_ok=yes]) +cs_cv_prog_$4_version_ok_annotated="$cs_cv_prog_$4_version_ok" +AS_IF([test -n "$cs_prog_$4_is_version"], + [cs_cv_prog_$4_version_ok_annotated="$cs_cv_prog_$4_version_ok_annotated (version $cs_prog_$4_is_version)"]) +]) + +############################################################################## +# CS_CHECK_PROG_VERSION(PROG, EXTRACT_CALL, VERSION, PATTERN, +# [ACTION-IF-OKAY], [ACTION-IF-NOT-OKAY], [CMP]) +# Check the version of a program PROG. +# Version information is emitted by EXTRACT_CALL (for instance "bison -V"). +# The discovered program version is compared against VERSION. +# The pattern of the version string matches PATTERN +# The extracted version and the supplied version are compared with the CMP +# operator. i.e. EXTRACTED_VERSION CMP SUPPLIED_VERSION +# CMP defaults to >= if not specified. +# ACTION-IF-OKAY is invoked if comparision yields true, otherwise +# ACTION-IF-NOT-OKAY is invoked. +# +# PATTERN literals: 9 .. marks a non empty sequence of digits +# _ .. marks a non empty sequence of characters from [a-zA-Z] +# | .. everything behind is optional +# .. everything else is taken as separator - it is better +# to not try stuff like space, slash or comma. +# +# The test results in cs_cv_prog_PROG_version_ok being either yes or no. +############################################################################## +AC_DEFUN([CS_CHECK_PROG_VERSION], +[AC_CACHE_CHECK([if $1 version m4_default([$7],[>=]) $3], + [AS_TR_SH([cs_cv_prog_$1_version_ok_annotated])], + [CS_VCHK_EXTRACTVERSION([$2], [$3], [$4], AS_TR_SH([$1]), + m4_default([$7],[>=]))]) +AS_IF([test "$AS_TR_SH([cs_cv_prog_$1_version_ok])" = yes], [$5], [$6])]) +# qualify.m4 -*- Autoconf -*- +#============================================================================== +# Copyright (C)2005 by Eric Sunshine +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +#============================================================================== +AC_PREREQ([2.56]) + +#------------------------------------------------------------------------------ +# CS_SYMBOL_QUALIFIER(MESSAGE, CACHE-VAR, QUALIFIERS, [SYMBOL], [LANG], +# [ACTION-IF-ACCEPTED], [ACTION-IF-NOT-ACCEPTED]) +# Test if a symbol can be qualified by one of the elements of the +# comma-separated list of QUALIFIERS. Examples of qualifiers include +# __attribute__((deprecated)), __declspec(dllimport), etc. MESSAGE is the +# "checking" message. CACHE-VAR is the variable which receives the +# qualifier which succeeded, or the the literal "no" if none were +# accepted. SYMBOL is the symbol to which the qualifier should be +# applied. If omitted, then SYMBOL defaults to "void f();". LANG is the +# language of the test, typically "C" or "C++". It defaults to "C" if +# omitted. ACTION-IF-ACCEPTED is invoked after CACHE-VAR is set if one of +# the qualifiers is accepted, else ACTION-IF-NOT-ACCEPTED is invoked. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_SYMBOL_QUALIFIER], + [AC_CACHE_CHECK([$1], [$2], + [$2='no' + m4_foreach([cs_symbol_qualifier], [$3], + [AS_IF([test "$$2" = no], + [CS_BUILD_IFELSE( + [AC_LANG_PROGRAM( + [cs_symbol_qualifier m4_default([$4],[void f()]);], + [])], + [], [$5], [$2='cs_symbol_qualifier'], [$2='no'])])])]) + AS_IF([test $$2 != no], [$6], [$7])]) +# split.m4 -*- Autoconf -*- +#============================================================================== +# Copyright (C)2003 by Eric Sunshine +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +#============================================================================== +AC_PREREQ([2.56]) + +#------------------------------------------------------------------------------ +# CS_SPLIT(LINE, [OUTPUT-VARIABLES], [DELIMITER], [FILLER]) +# Split LINE into individual tokens. Tokens are delimited by DELIMITER, +# which is the space character if omitted. OUTPUT-VARIABLES is a +# comma-delimited list of shell variables which should receive the +# extracted tokens. If there are too few tokens to fill the output +# variables, then the excess variables will be assigned the empty string. +# If there are too few output variables, then the excess tokens will be +# ignored. If OUTPUT-VARIABLES is omitted, then the split tokens will be +# assigned to the shell meta-variables $1, $2, $3, etc. When +# OUTPUT-VARIABLES is omitted, FILLER is assigned to meta-variables in +# cases where DELIMITER delimits a zero-length token. FILLER defaults +# to "filler". For example, if DELIMITER is "+" and OUTPUT-VARIABLES is +# omitted, given the line "one++three", $1 will be "one", $2 will be +# "filler", and $3 will be "three". +#------------------------------------------------------------------------------ +AC_DEFUN([CS_SPLIT], + [m4_define([cs_split_filler], m4_default([$4],[filler])) + set cs_split_filler `echo "$1" | awk 'BEGIN { FS="m4_default([$3],[ ])" } + { for (i=1; i <= NF; ++i) + { if ($i == "") print "cs_split_filler"; else print $i } }'` + shift + m4_map([_CS_SPLIT], [$2])]) + +AC_DEFUN([_CS_SPLIT], + [AS_IF([test $[@%:@] -eq 0], [$1=''], + [AS_IF([test "$[1]" = cs_split_filler], [$1=''], [$1=$[1]]) + shift])]) +# textcache.m4 -*- Autoconf -*- +#============================================================================== +# Copyright (C)2003 by Eric Sunshine +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +#============================================================================== +AC_PREREQ([2.56]) + +#------------------------------------------------------------------------------ +# Text cache facility. These macros provide a way to incrementally store +# arbitrary text in a shell variable, and to write the saved text to a file. +# +# CS_TEXT_CACHE_APPEND(VARIABLE, TEXT) +# Append text to the contents of the named shell variable. If the text +# contains references to shell variables (such as $foo), then those +# references will be expanded. If expansion is not desired, then protect +# the text with AS_ESCAPE(). +# +# CS_TEXT_CACHE_PREPEND(VARIABLE, TEXT) +# Prepend text to the contents of the named shell variable. If the text +# contains references to shell variables (such as $foo), then those +# references will be expanded. If expansion is not desired, then protect +# the text with AS_ESCAPE(). +# +# CS_TEXT_CACHE_OUTPUT(VARIABLE, FILENAME) +# Instruct config.status to write the contents of the named shell +# variable to the given filename. If the file resides in a directory, +# the directory will be created, if necessary. If the output file +# already exists, and if the cached text is identical to the contents of +# the existing file, then the existing file is left alone, thus its time +# stamp remains unmolested. This heuristic may help to minimize rebuilds +# when the file is listed as a dependency in a makefile. +# +# *NOTE* +# There is a bug in Autoconf 2.57 and probably all earlier 2.5x versions +# which results in errors if AC_CONFIG_COMMANDS is invoked for a `tag' +# which represents a file in a directory which does not yet exist. +# Unfortunately, even invoking AS_MKDIR_P in the `cmd' portion of +# AC_CONFIG_COMMANDS does not solve the problem because the generated +# configure script attempts to access information about the directory +# before AS_MKDIR_P has a chance to create it. This forces us to invoke +# AS_MKDIR_P in the third argument to AC_CONFIG_COMMANDS (the +# `init-cmds') rather than the second (the `cmds'). This is undesirable +# because it means that the directory will be created anytime +# config.status is invoked (even for a simple --help), rather than being +# created only when requested to output the text cache. This bug was +# submitted to the Autoconf GNATS database by Eric Sunshine as #228 on +# 27-Dec-2002. It was fixed for Autoconf 2.58 on 26-Sep-2003. The +# official fix makes the assumption that `tag' always represents a file +# (as opposed to some generic target), and creates the file's directory +# is not present. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_TEXT_CACHE_APPEND], [$1="${$1}$2"]) +AC_DEFUN([CS_TEXT_CACHE_PREPEND], [$1="$2${$1}"]) +AC_DEFUN([CS_TEXT_CACHE_OUTPUT], + [AC_CONFIG_COMMANDS([$2], + [echo $ECHO_N "$$1$ECHO_C" > $tmp/tcache + AS_IF([diff $2 $tmp/tcache >/dev/null 2>&1], + [AC_MSG_NOTICE([$2 is unchanged])], + [rm -f $2 + cp $tmp/tcache $2]) + rm -f $tmp/tcache], + [$1='$$1' + cs_dir=`AS_DIRNAME([$2])` + AS_ESCAPE(AS_MKDIR_P([$cs_dir]), [$`\])])]) +# trim.m4 -*- Autoconf -*- +#============================================================================== +# Copyright (C)2003 by Eric Sunshine +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +#============================================================================== +AC_PREREQ([2.56]) + +#------------------------------------------------------------------------------ +# CS_TRIM(STRING) +# Strip leading and trailing spaces from STRING and collapse internal +# runs of multiple spaces to a single space. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_TRIM], [`echo x$1 | sed 's/^x//;s/ */ /g;s/^ //;s/ $//'`]) +# warnings.m4 -*- Autoconf -*- +#============================================================================== +# Copyright (C)2005 by Eric Sunshine +# +# This library is free software; you can redistribute it and/or modify it +# under the terms of the GNU Library General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This library is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public +# License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this library; if not, write to the Free Software Foundation, +# Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +#============================================================================== +AC_PREREQ([2.56]) + +#------------------------------------------------------------------------------ +# CS_COMPILER_WARNINGS([LANGUAGE], [CACHE-VAR], [ACTION-IF-FOUND], +# [ACTION-IF-NOT-FOUND]) +# Check how to enable compilation warnings. If LANGUAGE is not provided, +# then `C' is assumed (other options include `C++'). If CACHE-VAR is not +# provided, then it defaults to the name +# "cs_cv_prog_compiler_enable_warnings". If an option for enabling +# warnings (such as `-Wall') is discovered, then it is assigned to +# CACHE-VAR and ACTION-IF-FOUND is invoked; otherwise the empty string is +# assigned to CACHE-VAR and ACTION-IF-NOT-FOUND is invoked. +# +# IMPLEMENTATION NOTES +# +# On some platforms, it is more appropriate to use -Wmost rather than +# -Wall even if the compiler understands both, thus we attempt -Wmost +# before -Wall. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_COMPILER_WARNINGS], + [CS_CHECK_BUILD_FLAGS( + [how to enable m4_default([$1],[C]) compilation warnings], + [m4_default([$2],[cs_cv_prog_compiler_enable_warnings])], + [CS_CREATE_TUPLE([-Wmost]) CS_CREATE_TUPLE([-Wall])], + [$1], [$3], [$4])]) + + + +#------------------------------------------------------------------------------ +# CS_COMPILER_ERRORS([LANGUAGE], [CACHE-VAR], [ACTION-IF-FOUND], +# [ACTION-IF-NOT-FOUND]) +# Check how to promote compilation diganostics from warning to error +# status. If LANGUAGE is not provided, then `C' is assumed (other options +# include `C++'). If CACHE-VAR is not provided, then it defaults to the +# name "cs_cv_prog_compiler_enable_errors". If an option for performing +# this promotion (such as `-Werror') is discovered, then it is assigned +# to CACHE-VAR and ACTION-IF-FOUND is invoked; otherwise the empty string +# is assigned to CACHE-VAR and ACTION-IF-NOT-FOUND is invoked. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_COMPILER_ERRORS], + [CS_CHECK_BUILD_FLAGS( + [how to treat m4_default([$1],[C]) warnings as errors], + [m4_default([$2],[cs_cv_prog_compiler_enable_errors])], + [CS_CREATE_TUPLE([-Werror])], [$1], [$3], [$4])]) + + + +#------------------------------------------------------------------------------ +# CS_COMPILER_IGNORE_UNUSED([LANGUAGE], [CACHE-VAR], [ACTION-IF-FOUND], +# [ACTION-IF-NOT-FOUND]) +# Check how to instruct compiler to ignore unused variables and +# arguments. This option may be useful for code generated by tools, such +# as Swig, Bison, and Flex, over which the client has no control, yet +# wishes to compile without excessive diagnostic spew. If LANGUAGE is +# not provided, then `C' is assumed (other options include `C++'). If +# CACHE-VAR is not provided, then it defaults to the name +# "cs_cv_prog_compiler_ignore_unused". If an option (such as +# `-Wno-unused') is discovered, then it is assigned to CACHE-VAR and +# ACTION-IF-FOUND is invoked; otherwise the empty string is assigned to +# CACHE-VAR and ACTION-IF-NOT-FOUND is invoked. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_COMPILER_IGNORE_UNUSED], + [CS_CHECK_BUILD_FLAGS( + [how to suppress m4_default([$1],[C]) unused variable warnings], + [m4_default([$2],[cs_cv_prog_compiler_ignore_unused])], + [CS_CREATE_TUPLE([-Wno-unused])], [$1], [$3], [$4])]) + + + +#------------------------------------------------------------------------------ +# CS_COMPILER_IGNORE_UNINITIALIZED([LANGUAGE], [CACHE-VAR], [ACTION-IF-FOUND], +# [ACTION-IF-NOT-FOUND]) +# Check how to instruct compiler to ignore uninitialized variables. This +# option may be useful for code generated by tools, such as Swig, Bison, +# and Flex, over which the client has no control, yet wishes to compile +# without excessive diagnostic spew. If LANGUAGE is not provided, then +# `C' is assumed (other options include `C++'). If CACHE-VAR is not +# provided, then it defaults to the name +# "cs_cv_prog_compiler_ignore_uninitialized". If an option (such as +# `-Wno-uninitialized') is discovered, then it is assigned to CACHE-VAR +# and ACTION-IF-FOUND is invoked; otherwise the empty string is assigned +# to CACHE-VAR and ACTION-IF-NOT-FOUND is invoked. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_COMPILER_IGNORE_UNINITIALIZED], + [CS_CHECK_BUILD_FLAGS( + [how to suppress m4_default([$1],[C]) uninitialized warnings], + [m4_default([$2], + [cs_cv_prog_compiler_ignore_uninitialized_variables])], + [CS_CREATE_TUPLE([-Wno-uninitialized])], [$1], [$3], [$4])]) + + + +#------------------------------------------------------------------------------ +# CS_COMPILER_IGNORE_PRAGMAS([LANGUAGE], [CACHE-VAR], [ACTION-IF-FOUND], +# [ACTION-IF-NOT-FOUND]) +# Check how to instruct compiler to ignore unrecognized #pragma +# directives. This option may be useful for code which contains +# unprotected #pragmas which are not understood by all compilers. If +# LANGUAGE is not provided, then `C' is assumed (other options include +# `C++'). If CACHE-VAR is not provided, then it defaults to the name +# "cs_cv_prog_compiler_ignore_unknown_pragmas". If an option (such as +# `-Wno-unknown-pragmas') is discovered, then it is assigned to CACHE-VAR +# and ACTION-IF-FOUND is invoked; otherwise the empty string is assigned +# to CACHE-VAR and ACTION-IF-NOT-FOUND is invoked. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_COMPILER_IGNORE_PRAGMAS], + [CS_CHECK_BUILD_FLAGS( + [how to suppress m4_default([$1],[C]) unknown [#pragma] warnings], + [m4_default([$2],[cs_cv_prog_compiler_ignore_unknown_pragmas])], + [CS_CREATE_TUPLE([-Wno-unknown-pragmas])], [$1], [$3], [$4])]) + + + +#------------------------------------------------------------------------------ +# CS_COMPILER_IGNORE_LONG_DOUBLE([LANGUAGE], [CACHE-VAR], [ACTION-IF-FOUND], +# [ACTION-IF-NOT-FOUND]) +# Check how to instruct compiler to suppress warnings about `long double' +# usage. This option may be useful for code generated by tools, such as +# Swig, Bison, and Flex, over which the client has no control, yet wishes +# to compile without excessive diagnostic spew. If LANGUAGE is not +# provided, then `C' is assumed (other options include `C++'). If +# CACHE-VAR is not provided, then it defaults to the name +# "cs_cv_prog_compiler_ignore_long_double". If an option (such as +# `-Wno-long-double') is discovered, then it is assigned to CACHE-VAR and +# ACTION-IF-FOUND is invoked; otherwise the empty string is assigned to +# CACHE-VAR and ACTION-IF-NOT-FOUND is invoked. +#------------------------------------------------------------------------------ +AC_DEFUN([CS_COMPILER_IGNORE_LONG_DOUBLE], + [CS_CHECK_BUILD_FLAGS( + [how to suppress m4_default([$1],[C]) `long double' warnings], + [m4_default([$2],[cs_cv_prog_compiler_ignore_long_double])], + [CS_CREATE_TUPLE([-Wno-long-double])], [$1], [$3], [$4])]) diff --git a/Engine/lib/bullet/autogen.sh b/Engine/lib/bullet/autogen.sh new file mode 100644 index 000000000..35623facf --- /dev/null +++ b/Engine/lib/bullet/autogen.sh @@ -0,0 +1,61 @@ +#! /bin/sh + +if [ "$USER" = "root" ]; then + echo "*** You cannot do this as "$USER" please use a normal user account." + exit 1 +fi +if test ! -f configure.ac ; then + echo "*** Please invoke this script from directory containing configure.ac." + exit 1 +fi + +echo "running aclocal" +aclocal +rc=$? + +if test $rc -eq 0; then + echo "running libtool" + libtoolize --force --automake --copy + rc=$? +else + echo "An error occured, autogen.sh stopping." + exit $rc +fi + +if test $rc -eq 0; then + echo "libtool worked." +else + echo "libtool not found. trying glibtool." + glibtoolize --force --automake --copy + rc=$? +fi + +if test $rc -eq 0; then + echo "running automake" + automake --add-missing --copy + rc=$? +else + echo "An error occured, autogen.sh stopping." + exit $rc +fi + +if test $rc -eq 0; then + echo "running autoheader" + autoheader + rc=$? +else + echo "An error occured, autogen.sh stopping." + exit $rc +fi + +if test $rc -eq 0; then + echo "running autoconf" + autoconf + rc=$? +else + echo "An error occured, autogen.sh stopping." + exit $rc +fi + +echo "autogen.sh complete" +exit $rc diff --git a/Engine/lib/bullet/bullet.pc.in b/Engine/lib/bullet/bullet.pc.in new file mode 100644 index 000000000..3b86d0aec --- /dev/null +++ b/Engine/lib/bullet/bullet.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: bullet +Description: Bullet Continuous Collision Detection and Physics Library +Requires: +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lbulletdynamics -lbulletcollision -lbulletmath +Cflags: -I${includedir}/bullet diff --git a/Engine/lib/bullet/config.h.in b/Engine/lib/bullet/config.h.in new file mode 100644 index 000000000..2f10be9b4 --- /dev/null +++ b/Engine/lib/bullet/config.h.in @@ -0,0 +1,108 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Architecture is PowerPC */ +#undef ARCH_PPC + +/* Architecture is x86 */ +#undef ARCH_X86 + +/* Architecture is x86-64 */ +#undef ARCH_X86_64 + +/* Define when compiling for MacOS/X */ +#undef CS_PLATFORM_MACOSX + +/* Define when compiling for Unix and Unix-like (i.e. MacOS/X) */ +#undef CS_PLATFORM_UNIX + +/* Define when compiling for Win32 */ +#undef CS_PLATFORM_WIN32 + +/* Define to 1 if you have the header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `mx' library (-lmx). */ +#undef HAVE_LIBMX + +/* Define to 1 if you have the `nsl' library (-lnsl). */ +#undef HAVE_LIBNSL + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Whether the int32 type is available */ +#undef HAVE_TYPE_INT32 + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_WINDOWS_H + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#undef LT_OBJDIR + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Platform is Apple */ +#undef PLATFORM_APPLE + +/* Platform is Linux */ +#undef PLATFORM_LINUX + +/* Platform is Win32 */ +#undef PLATFORM_WIN32 + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION + +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +#undef WORDS_BIGENDIAN + +/* Define to 1 if the X Window System is missing or not being used. */ +#undef X_DISPLAY_MISSING + +/* Avoid problem caused by missing */ +#undef __CARBONSOUND__ diff --git a/Engine/lib/bullet/configure.ac b/Engine/lib/bullet/configure.ac new file mode 100644 index 000000000..4b3cb5b59 --- /dev/null +++ b/Engine/lib/bullet/configure.ac @@ -0,0 +1,192 @@ +#---------------------------------------------------------------------------- +# Autoconf input script. Invoke the ./autogen.sh script to generate a +# configure script from this file. +#---------------------------------------------------------------------------- +AC_PREREQ([2.54]) + +#---------------------------------------------------------------------------- +# Initialize Autoconf. +#---------------------------------------------------------------------------- +AC_INIT( + [bullet], + [2.75], + [bullet@erwincoumans.com]) +AC_CANONICAL_HOST +CS_PACKAGEINFO( + [Bullet Continuous Collision Detection and Physics Library], + [Copyright (c) 2005-2008 Erwin Coumans], + [http://www.bulletphysics.com]) +AC_CONFIG_SRCDIR([configure.ac]) +AM_INIT_AUTOMAKE +AC_PROG_CC +AC_PROG_CXX +AC_PROG_LIBTOOL + +case "$host" in + *-*-mingw*|*-*-cygwin*) + AC_DEFINE(PLATFORM_WIN32, 1, [Platform is Win32]) + opengl_LIBS="-lunsupported_platform" + PLATFORM_STRING="Win32" + ;; + *-*-linux*) + AC_DEFINE(PLATFORM_LINUX, 1, [Platform is Linux]) + opengl_LIBS="-lGL -lGLU -lglut" + PLATFORM_STRING="Linux" + ;; + *-*-darwin*) + AC_DEFINE(PLATFORM_APPLE, 1, [Platform is Apple]) + opengl_LIBS="-framework AGL -framework OpenGL -framework GLUT" + PLATFORM_STRING="Apple" + ;; + *) + AC_MSG_WARN([*** Please add $host to configure.ac checks!]) + ;; +esac +AC_SUBST(opengl_LIBS) + +case "$host" in + i?86-* | k?-* | athlon-* | pentium*-) + AC_DEFINE(ARCH_X86, 1, [Architecture is x86]) + ARCH_SPECIFIC_CFLAGS="" + ARCH_STRING="X86" + ;; + x86_64-*) + AC_DEFINE(ARCH_X86_64, 1, [Architecture is x86-64]) + ARCH_SPECIFIC_CFLAGS="-DUSE_ADDR64" + ARCH_STRING="X86-64" + ;; + ppc-* | powerpc-*) + AC_DEFINE(ARCH_PPC, 1, [Architecture is PowerPC]) + ARCH_SPECIFIC_CFLAGS="" + ARCH_STRING="PowerPC" + ;; + *) + AC_MSG_ERROR([Unknown Architecture]) + ;; +esac +AC_C_BIGENDIAN + + +#---------------------------------------------------------------------------- +# Setup for the configuration header. +#---------------------------------------------------------------------------- +AC_CONFIG_HEADERS([config.h]) + +#---------------------------------------------------------------------------- +# Check for tools. +#---------------------------------------------------------------------------- +CS_PROG_CC +AS_IF([test -z "$CC"], + [AC_MSG_ERROR([Could not find a usable C compiler.])]) +CS_PROG_CXX +AS_IF([test -z "$CXX"], + [AC_MSG_ERROR([Could not find a usable C++ compiler.])]) +CS_PROG_LINK + +CS_CHECK_COMMON_TOOLS_LINK +CS_CHECK_COMMON_TOOLS_BASIC +CS_CHECK_COMMON_TOOLS_DOC_DOXYGEN + +CS_CHECK_PROGS([PERL], [perl5 perl]) +CS_EMIT_BUILD_PROPERTY([PERL], [$PERL]) + +CS_CHECK_TEMPLATE_TOOLKIT2([emit]) + +#---------------------------------------------------------------------------- +# Check if C++ exceptions can be disabled. +#---------------------------------------------------------------------------- +CS_EMIT_BUILD_FLAGS([how to disable C++ exceptions], + [cs_cv_prog_cxx_disable_exceptions], [CS_CREATE_TUPLE([-fno-exceptions])], + [C++], [COMPILER.C++FLAGS.EXCEPTIONS.DISABLE], [], + [CS_EMIT_BUILD_PROPERTY([COMPILER.C++FLAGS], + [$cs_cv_prog_cxx_disable_exceptions], [+])]) + + +#---------------------------------------------------------------------------- +# Determine system type +#---------------------------------------------------------------------------- +CS_CHECK_HOST + +#---------------------------------------------------------------------------- +# Check for syntax problems / header files +#---------------------------------------------------------------------------- +# Nothing yet. + +#---------------------------------------------------------------------------- +# Check for GLUT. +#---------------------------------------------------------------------------- +AS_IF([test $cs_host_family = windows], + [# Tack the GLUT that comes with bullet onto compiler & linker flags. + _AC_SRCDIRS(["."]) + glut_cflags="-I$ac_top_srcdir/Glut" + glut_lflags="-L$ac_top_srcdir/Glut" + CFLAGS="$CFLAGS $glut_cflags" + LDFLAGS="$LDFLAGS $glut_lflags" + CS_EMIT_BUILD_PROPERTY([COMPILER.CFLAGS], [$glut_cflags], [+]) + CS_EMIT_BUILD_PROPERTY([COMPILER.LFLAGS], [$glut_lflags], [+]) + ]) +CS_CHECK_GLUT + + +#---------------------------------------------------------------------------- +# Package configuration switches. +#---------------------------------------------------------------------------- +AC_ARG_ENABLE([multithreaded], + [AC_HELP_STRING([--enable-multithreaded], + [build BulletMultiThreaded (default NO)])], + [disable_multithreaded=no], [disable_multithreaded=yes]) +AC_MSG_CHECKING([BulletMultiThreaded]) +AS_IF([test "$disable_multithreaded" = yes], [build_multithreaded=no], [build_multithreaded=yes]) +AC_MSG_RESULT([$build_multithreaded]) +AM_CONDITIONAL([CONDITIONAL_BUILD_MULTITHREADED], [test "$build_multithreaded" = yes]) + +AC_ARG_ENABLE([demos], + [AS_HELP_STRING([--disable-demos], + [disable Bullet demos])], + [], + [enable_demos=yes]) +AM_CONDITIONAL([CONDITIONAL_BUILD_DEMOS], [false]) +if test "x$enable_demos" != xno; then + AC_MSG_NOTICE([Building Bullet demos]) + AM_CONDITIONAL([CONDITIONAL_BUILD_DEMOS],[true]) +fi + + + +AC_ARG_ENABLE([debug], + [AC_HELP_STRING([--enable-debug], + [build with debugging information (default NO)])], + [], [enable_debug=no]) + +AC_MSG_CHECKING([build mode]) +AS_IF([test $enable_debug = yes], [build_mode=debug], [build_mode=optimize]) +AC_MSG_RESULT([$build_mode]) + +CS_EMIT_BUILD_PROPERTY([MODE], [$build_mode]) + +#----------------------------------------------------------------------------- +# Emit install paths and package information. +#----------------------------------------------------------------------------- +CS_OUTPUT_INSTALLDIRS +CS_EMIT_PACKAGEINFO + + +CFLAGS="$ARCH_SPECIFIC_CFLAGS $CFLAGS" +CXXFLAGS="$ARCH_SPECIFIC_CFLAGS $CXXFLAGS $CFLAGS" +#---------------------------------------------------------------------------- +# Emit generated files. +#---------------------------------------------------------------------------- +CS_JAMCONFIG_OUTPUT([Jamconfig]) +AC_CONFIG_FILES([bullet.pc Jamfile Makefile Demos/Makefile Demos/SoftDemo/Makefile Demos/AllBulletDemos/Makefile Demos/MultiThreadedDemo/Makefile Demos/ColladaDemo/Makefile Demos/OpenGL/Makefile Demos/BasicDemo/Makefile Demos/CcdPhysicsDemo/Makefile Demos/VehicleDemo/Makefile Demos/TerrainDemo/Makefile src/Makefile Extras/Makefile]) +AC_OUTPUT + +AC_MSG_NOTICE([ +You can type 'make' or 'jam' to build Bullet. +Alternatively, you can use cmake or use the wksbullet.sln visual studio x solutions in the msvc/x folder. + +CMake home:http://cmake.org +Jam home: http://www.perforce.com/jam/jam.html +Jam source: ftp://ftp.perforce.com/jam/ + +Please type 'make' to build Bullet +]) diff --git a/Engine/lib/bullet/install-sh b/Engine/lib/bullet/install-sh new file mode 100644 index 000000000..b777f1244 --- /dev/null +++ b/Engine/lib/bullet/install-sh @@ -0,0 +1,322 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2004-07-05.00 + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +chmodcmd="$chmodprog 0755" +chowncmd= +chgrpcmd= +stripcmd= +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src= +dst= +dir_arg= +dstarg= +no_target_directory= + +usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: +-c (ignored) +-d create directories instead of installing files. +-g GROUP $chgrpprog installed files to GROUP. +-m MODE $chmodprog installed files to MODE. +-o USER $chownprog installed files to USER. +-s $stripprog installed files. +-t DIRECTORY install into DIRECTORY. +-T report an error if DSTFILE is a directory. +--help display this help and exit. +--version display version info and exit. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG +" + +while test -n "$1"; do + case $1 in + -c) shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + --help) echo "$usage"; exit 0;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -s) stripcmd=$stripprog + shift + continue;; + + -t) dstarg=$2 + shift + shift + continue;; + + -T) no_target_directory=true + shift + continue;; + + --version) echo "$0 $scriptversion"; exit 0;; + + *) # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + test -n "$dir_arg$dstarg" && break + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dstarg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dstarg" + shift # fnord + fi + shift # arg + dstarg=$arg + done + break;; + esac +done + +if test -z "$1"; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call `install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +for src +do + # Protect names starting with `-'. + case $src in + -*) src=./$src ;; + esac + + if test -n "$dir_arg"; then + dst=$src + src= + + if test -d "$dst"; then + mkdircmd=: + chmodcmd= + else + mkdircmd=$mkdirprog + fi + else + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dstarg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + + dst=$dstarg + # Protect names starting with `-'. + case $dst in + -*) dst=./$dst ;; + esac + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dstarg: Is a directory" >&2 + exit 1 + fi + dst=$dst/`basename "$src"` + fi + fi + + # This sed command emulates the dirname command. + dstdir=`echo "$dst" | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + + # Make sure that the destination directory exists. + + # Skip lots of stat calls in the usual case. + if test ! -d "$dstdir"; then + defaultIFS=' + ' + IFS="${IFS-$defaultIFS}" + + oIFS=$IFS + # Some sh's can't handle IFS=/ for some reason. + IFS='%' + set - `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'` + IFS=$oIFS + + pathcomp= + + while test $# -ne 0 ; do + pathcomp=$pathcomp$1 + shift + if test ! -d "$pathcomp"; then + $mkdirprog "$pathcomp" + # mkdir can fail with a `File exist' error in case several + # install-sh are creating the directory concurrently. This + # is OK. + test -d "$pathcomp" || exit + fi + pathcomp=$pathcomp/ + done + fi + + if test -n "$dir_arg"; then + $doit $mkdircmd "$dst" \ + && { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \ + && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \ + && { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \ + && { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; } + + else + dstfile=`basename "$dst"` + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'status=$?; rm -f "$dsttmp" "$rmtmp" && exit $status' 0 + trap '(exit $?); exit' 1 2 13 15 + + # Copy the file name to the temp name. + $doit $cpprog "$src" "$dsttmp" && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \ + && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \ + && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \ + && { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } && + + # Now rename the file to the real destination. + { $doit $mvcmd -f "$dsttmp" "$dstdir/$dstfile" 2>/dev/null \ + || { + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + if test -f "$dstdir/$dstfile"; then + $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \ + || $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \ + || { + echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2 + (exit 1); exit + } + else + : + fi + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dstdir/$dstfile" + } + } + fi || { (exit 1); exit; } +done + +# The final little trick to "correctly" pass the exit status to the exit trap. +{ + (exit 0); exit +} + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/Engine/lib/bullet/src/Bullet-C-Api.h b/Engine/lib/bullet/src/Bullet-C-Api.h new file mode 100644 index 000000000..f309aba28 --- /dev/null +++ b/Engine/lib/bullet/src/Bullet-C-Api.h @@ -0,0 +1,176 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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. +*/ + +/* + Draft high-level generic physics C-API. For low-level access, use the physics SDK native API's. + Work in progress, functionality will be added on demand. + + If possible, use the richer Bullet C++ API, by including "btBulletDynamicsCommon.h" +*/ + +#ifndef BULLET_C_API_H +#define BULLET_C_API_H + +#define PL_DECLARE_HANDLE(name) typedef struct name##__ { int unused; } *name + +#ifdef BT_USE_DOUBLE_PRECISION +typedef double plReal; +#else +typedef float plReal; +#endif + +typedef plReal plVector3[3]; +typedef plReal plQuaternion[4]; + +#ifdef __cplusplus +extern "C" { +#endif + +/** Particular physics SDK (C-API) */ + PL_DECLARE_HANDLE(plPhysicsSdkHandle); + +/** Dynamics world, belonging to some physics SDK (C-API)*/ + PL_DECLARE_HANDLE(plDynamicsWorldHandle); + +/** Rigid Body that can be part of a Dynamics World (C-API)*/ + PL_DECLARE_HANDLE(plRigidBodyHandle); + +/** Collision Shape/Geometry, property of a Rigid Body (C-API)*/ + PL_DECLARE_HANDLE(plCollisionShapeHandle); + +/** Constraint for Rigid Bodies (C-API)*/ + PL_DECLARE_HANDLE(plConstraintHandle); + +/** Triangle Mesh interface (C-API)*/ + PL_DECLARE_HANDLE(plMeshInterfaceHandle); + +/** Broadphase Scene/Proxy Handles (C-API)*/ + PL_DECLARE_HANDLE(plCollisionBroadphaseHandle); + PL_DECLARE_HANDLE(plBroadphaseProxyHandle); + PL_DECLARE_HANDLE(plCollisionWorldHandle); + +/** + Create and Delete a Physics SDK +*/ + + extern plPhysicsSdkHandle plNewBulletSdk(); //this could be also another sdk, like ODE, PhysX etc. + extern void plDeletePhysicsSdk(plPhysicsSdkHandle physicsSdk); + +/** Collision World, not strictly necessary, you can also just create a Dynamics World with Rigid Bodies which internally manages the Collision World with Collision Objects */ + + typedef void(*btBroadphaseCallback)(void* clientData, void* object1,void* object2); + + extern plCollisionBroadphaseHandle plCreateSapBroadphase(btBroadphaseCallback beginCallback,btBroadphaseCallback endCallback); + + extern void plDestroyBroadphase(plCollisionBroadphaseHandle bp); + + extern plBroadphaseProxyHandle plCreateProxy(plCollisionBroadphaseHandle bp, void* clientData, plReal minX,plReal minY,plReal minZ, plReal maxX,plReal maxY, plReal maxZ); + + extern void plDestroyProxy(plCollisionBroadphaseHandle bp, plBroadphaseProxyHandle proxyHandle); + + extern void plSetBoundingBox(plBroadphaseProxyHandle proxyHandle, plReal minX,plReal minY,plReal minZ, plReal maxX,plReal maxY, plReal maxZ); + +/* todo: add pair cache support with queries like add/remove/find pair */ + + extern plCollisionWorldHandle plCreateCollisionWorld(plPhysicsSdkHandle physicsSdk); + +/* todo: add/remove objects */ + + +/* Dynamics World */ + + extern plDynamicsWorldHandle plCreateDynamicsWorld(plPhysicsSdkHandle physicsSdk); + + extern void plDeleteDynamicsWorld(plDynamicsWorldHandle world); + + extern void plStepSimulation(plDynamicsWorldHandle, plReal timeStep); + + extern void plAddRigidBody(plDynamicsWorldHandle world, plRigidBodyHandle object); + + extern void plRemoveRigidBody(plDynamicsWorldHandle world, plRigidBodyHandle object); + + +/* Rigid Body */ + + extern plRigidBodyHandle plCreateRigidBody( void* user_data, float mass, plCollisionShapeHandle cshape ); + + extern void plDeleteRigidBody(plRigidBodyHandle body); + + +/* Collision Shape definition */ + + extern plCollisionShapeHandle plNewSphereShape(plReal radius); + extern plCollisionShapeHandle plNewBoxShape(plReal x, plReal y, plReal z); + extern plCollisionShapeHandle plNewCapsuleShape(plReal radius, plReal height); + extern plCollisionShapeHandle plNewConeShape(plReal radius, plReal height); + extern plCollisionShapeHandle plNewCylinderShape(plReal radius, plReal height); + extern plCollisionShapeHandle plNewCompoundShape(); + extern void plAddChildShape(plCollisionShapeHandle compoundShape,plCollisionShapeHandle childShape, plVector3 childPos,plQuaternion childOrn); + + extern void plDeleteShape(plCollisionShapeHandle shape); + + /* Convex Meshes */ + extern plCollisionShapeHandle plNewConvexHullShape(); + extern void plAddVertex(plCollisionShapeHandle convexHull, plReal x,plReal y,plReal z); +/* Concave static triangle meshes */ + extern plMeshInterfaceHandle plNewMeshInterface(); + extern void plAddTriangle(plMeshInterfaceHandle meshHandle, plVector3 v0,plVector3 v1,plVector3 v2); + extern plCollisionShapeHandle plNewStaticTriangleMeshShape(plMeshInterfaceHandle); + + extern void plSetScaling(plCollisionShapeHandle shape, plVector3 scaling); + +/* SOLID has Response Callback/Table/Management */ +/* PhysX has Triggers, User Callbacks and filtering */ +/* ODE has the typedef void dNearCallback (void *data, dGeomID o1, dGeomID o2); */ + +/* typedef void plUpdatedPositionCallback(void* userData, plRigidBodyHandle rbHandle, plVector3 pos); */ +/* typedef void plUpdatedOrientationCallback(void* userData, plRigidBodyHandle rbHandle, plQuaternion orientation); */ + + /* get world transform */ + extern void plGetOpenGLMatrix(plRigidBodyHandle object, plReal* matrix); + extern void plGetPosition(plRigidBodyHandle object,plVector3 position); + extern void plGetOrientation(plRigidBodyHandle object,plQuaternion orientation); + + /* set world transform (position/orientation) */ + extern void plSetPosition(plRigidBodyHandle object, const plVector3 position); + extern void plSetOrientation(plRigidBodyHandle object, const plQuaternion orientation); + extern void plSetEuler(plReal yaw,plReal pitch,plReal roll, plQuaternion orient); + extern void plSetOpenGLMatrix(plRigidBodyHandle object, plReal* matrix); + + typedef struct plRayCastResult { + plRigidBodyHandle m_body; + plCollisionShapeHandle m_shape; + plVector3 m_positionWorld; + plVector3 m_normalWorld; + } plRayCastResult; + + extern int plRayCast(plDynamicsWorldHandle world, const plVector3 rayStart, const plVector3 rayEnd, plRayCastResult res); + + /* Sweep API */ + + /* extern plRigidBodyHandle plObjectCast(plDynamicsWorldHandle world, const plVector3 rayStart, const plVector3 rayEnd, plVector3 hitpoint, plVector3 normal); */ + + /* Continuous Collision Detection API */ + + // needed for source/blender/blenkernel/intern/collision.c + double plNearestPoints(float p1[3], float p2[3], float p3[3], float q1[3], float q2[3], float q3[3], float *pa, float *pb, float normal[3]); + +#ifdef __cplusplus +} +#endif + + +#endif //BULLET_C_API_H + diff --git a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btAxisSweep3.cpp b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btAxisSweep3.cpp new file mode 100644 index 000000000..77763305b --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btAxisSweep3.cpp @@ -0,0 +1,37 @@ + +//Bullet Continuous Collision Detection and Physics Library +//Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + + +// +// btAxisSweep3 +// +// Copyright (c) 2006 Simon Hobbs +// +// 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 "btAxisSweep3.h" + + +btAxisSweep3::btAxisSweep3(const btVector3& worldAabbMin,const btVector3& worldAabbMax, unsigned short int maxHandles, btOverlappingPairCache* pairCache, bool disableRaycastAccelerator) +:btAxisSweep3Internal(worldAabbMin,worldAabbMax,0xfffe,0xffff,maxHandles,pairCache,disableRaycastAccelerator) +{ + // 1 handle is reserved as sentinel + btAssert(maxHandles > 1 && maxHandles < 32767); + +} + + +bt32BitAxisSweep3::bt32BitAxisSweep3(const btVector3& worldAabbMin,const btVector3& worldAabbMax, unsigned int maxHandles , btOverlappingPairCache* pairCache , bool disableRaycastAccelerator) +:btAxisSweep3Internal(worldAabbMin,worldAabbMax,0xfffffffe,0x7fffffff,maxHandles,pairCache,disableRaycastAccelerator) +{ + // 1 handle is reserved as sentinel + btAssert(maxHandles > 1 && maxHandles < 2147483647); +} diff --git a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btAxisSweep3.h b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btAxisSweep3.h new file mode 100644 index 000000000..cad21b4ca --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btAxisSweep3.h @@ -0,0 +1,1024 @@ +//Bullet Continuous Collision Detection and Physics Library +//Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +// +// btAxisSweep3.h +// +// Copyright (c) 2006 Simon Hobbs +// +// 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 AXIS_SWEEP_3_H +#define AXIS_SWEEP_3_H + +#include "LinearMath/btVector3.h" +#include "btOverlappingPairCache.h" +#include "btBroadphaseInterface.h" +#include "btBroadphaseProxy.h" +#include "btOverlappingPairCallback.h" +#include "btDbvtBroadphase.h" + +//#define DEBUG_BROADPHASE 1 +#define USE_OVERLAP_TEST_ON_REMOVES 1 + +/// The internal templace class btAxisSweep3Internal implements the sweep and prune broadphase. +/// It uses quantized integers to represent the begin and end points for each of the 3 axis. +/// Dont use this class directly, use btAxisSweep3 or bt32BitAxisSweep3 instead. +template +class btAxisSweep3Internal : public btBroadphaseInterface +{ +protected: + + BP_FP_INT_TYPE m_bpHandleMask; + BP_FP_INT_TYPE m_handleSentinel; + +public: + + BT_DECLARE_ALIGNED_ALLOCATOR(); + + class Edge + { + public: + BP_FP_INT_TYPE m_pos; // low bit is min/max + BP_FP_INT_TYPE m_handle; + + BP_FP_INT_TYPE IsMax() const {return static_cast(m_pos & 1);} + }; + +public: + class Handle : public btBroadphaseProxy + { + public: + BT_DECLARE_ALIGNED_ALLOCATOR(); + + // indexes into the edge arrays + BP_FP_INT_TYPE m_minEdges[3], m_maxEdges[3]; // 6 * 2 = 12 +// BP_FP_INT_TYPE m_uniqueId; + btBroadphaseProxy* m_dbvtProxy;//for faster raycast + //void* m_pOwner; this is now in btBroadphaseProxy.m_clientObject + + SIMD_FORCE_INLINE void SetNextFree(BP_FP_INT_TYPE next) {m_minEdges[0] = next;} + SIMD_FORCE_INLINE BP_FP_INT_TYPE GetNextFree() const {return m_minEdges[0];} + }; // 24 bytes + 24 for Edge structures = 44 bytes total per entry + + +protected: + btVector3 m_worldAabbMin; // overall system bounds + btVector3 m_worldAabbMax; // overall system bounds + + btVector3 m_quantize; // scaling factor for quantization + + BP_FP_INT_TYPE m_numHandles; // number of active handles + BP_FP_INT_TYPE m_maxHandles; // max number of handles + Handle* m_pHandles; // handles pool + + BP_FP_INT_TYPE m_firstFreeHandle; // free handles list + + Edge* m_pEdges[3]; // edge arrays for the 3 axes (each array has m_maxHandles * 2 + 2 sentinel entries) + void* m_pEdgesRawPtr[3]; + + btOverlappingPairCache* m_pairCache; + + ///btOverlappingPairCallback is an additional optional user callback for adding/removing overlapping pairs, similar interface to btOverlappingPairCache. + btOverlappingPairCallback* m_userPairCallback; + + bool m_ownsPairCache; + + int m_invalidPair; + + ///additional dynamic aabb structure, used to accelerate ray cast queries. + ///can be disabled using a optional argument in the constructor + btDbvtBroadphase* m_raycastAccelerator; + btOverlappingPairCache* m_nullPairCache; + + + // allocation/deallocation + BP_FP_INT_TYPE allocHandle(); + void freeHandle(BP_FP_INT_TYPE handle); + + + bool testOverlap2D(const Handle* pHandleA, const Handle* pHandleB,int axis0,int axis1); + +#ifdef DEBUG_BROADPHASE + void debugPrintAxis(int axis,bool checkCardinality=true); +#endif //DEBUG_BROADPHASE + + //Overlap* AddOverlap(BP_FP_INT_TYPE handleA, BP_FP_INT_TYPE handleB); + //void RemoveOverlap(BP_FP_INT_TYPE handleA, BP_FP_INT_TYPE handleB); + + + + void sortMinDown(int axis, BP_FP_INT_TYPE edge, btDispatcher* dispatcher, bool updateOverlaps ); + void sortMinUp(int axis, BP_FP_INT_TYPE edge, btDispatcher* dispatcher, bool updateOverlaps ); + void sortMaxDown(int axis, BP_FP_INT_TYPE edge, btDispatcher* dispatcher, bool updateOverlaps ); + void sortMaxUp(int axis, BP_FP_INT_TYPE edge, btDispatcher* dispatcher, bool updateOverlaps ); + +public: + + btAxisSweep3Internal(const btVector3& worldAabbMin,const btVector3& worldAabbMax, BP_FP_INT_TYPE handleMask, BP_FP_INT_TYPE handleSentinel, BP_FP_INT_TYPE maxHandles = 16384, btOverlappingPairCache* pairCache=0,bool disableRaycastAccelerator = false); + + virtual ~btAxisSweep3Internal(); + + BP_FP_INT_TYPE getNumHandles() const + { + return m_numHandles; + } + + virtual void calculateOverlappingPairs(btDispatcher* dispatcher); + + BP_FP_INT_TYPE addHandle(const btVector3& aabbMin,const btVector3& aabbMax, void* pOwner,short int collisionFilterGroup,short int collisionFilterMask,btDispatcher* dispatcher,void* multiSapProxy); + void removeHandle(BP_FP_INT_TYPE handle,btDispatcher* dispatcher); + void updateHandle(BP_FP_INT_TYPE handle, const btVector3& aabbMin,const btVector3& aabbMax,btDispatcher* dispatcher); + SIMD_FORCE_INLINE Handle* getHandle(BP_FP_INT_TYPE index) const {return m_pHandles + index;} + + virtual void resetPool(btDispatcher* dispatcher); + + void processAllOverlappingPairs(btOverlapCallback* callback); + + //Broadphase Interface + virtual btBroadphaseProxy* createProxy( const btVector3& aabbMin, const btVector3& aabbMax,int shapeType,void* userPtr ,short int collisionFilterGroup,short int collisionFilterMask,btDispatcher* dispatcher,void* multiSapProxy); + virtual void destroyProxy(btBroadphaseProxy* proxy,btDispatcher* dispatcher); + virtual void setAabb(btBroadphaseProxy* proxy,const btVector3& aabbMin,const btVector3& aabbMax,btDispatcher* dispatcher); + virtual void getAabb(btBroadphaseProxy* proxy,btVector3& aabbMin, btVector3& aabbMax ) const; + + virtual void rayTest(const btVector3& rayFrom,const btVector3& rayTo, btBroadphaseRayCallback& rayCallback, const btVector3& aabbMin=btVector3(0,0,0), const btVector3& aabbMax = btVector3(0,0,0)); + + void quantize(BP_FP_INT_TYPE* out, const btVector3& point, int isMax) const; + ///unQuantize should be conservative: aabbMin/aabbMax should be larger then 'getAabb' result + void unQuantize(btBroadphaseProxy* proxy,btVector3& aabbMin, btVector3& aabbMax ) const; + + bool testAabbOverlap(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1); + + btOverlappingPairCache* getOverlappingPairCache() + { + return m_pairCache; + } + const btOverlappingPairCache* getOverlappingPairCache() const + { + return m_pairCache; + } + + void setOverlappingPairUserCallback(btOverlappingPairCallback* pairCallback) + { + m_userPairCallback = pairCallback; + } + const btOverlappingPairCallback* getOverlappingPairUserCallback() const + { + return m_userPairCallback; + } + + ///getAabb returns the axis aligned bounding box in the 'global' coordinate frame + ///will add some transform later + virtual void getBroadphaseAabb(btVector3& aabbMin,btVector3& aabbMax) const + { + aabbMin = m_worldAabbMin; + aabbMax = m_worldAabbMax; + } + + virtual void printStats() + { +/* printf("btAxisSweep3.h\n"); + printf("numHandles = %d, maxHandles = %d\n",m_numHandles,m_maxHandles); + printf("aabbMin=%f,%f,%f,aabbMax=%f,%f,%f\n",m_worldAabbMin.getX(),m_worldAabbMin.getY(),m_worldAabbMin.getZ(), + m_worldAabbMax.getX(),m_worldAabbMax.getY(),m_worldAabbMax.getZ()); + */ + + } + +}; + +//////////////////////////////////////////////////////////////////// + + + + +#ifdef DEBUG_BROADPHASE +#include + +template +void btAxisSweep3::debugPrintAxis(int axis, bool checkCardinality) +{ + int numEdges = m_pHandles[0].m_maxEdges[axis]; + printf("SAP Axis %d, numEdges=%d\n",axis,numEdges); + + int i; + for (i=0;im_handle); + int handleIndex = pEdge->IsMax()? pHandlePrev->m_maxEdges[axis] : pHandlePrev->m_minEdges[axis]; + char beginOrEnd; + beginOrEnd=pEdge->IsMax()?'E':'B'; + printf(" [%c,h=%d,p=%x,i=%d]\n",beginOrEnd,pEdge->m_handle,pEdge->m_pos,handleIndex); + } + + if (checkCardinality) + btAssert(numEdges == m_numHandles*2+1); +} +#endif //DEBUG_BROADPHASE + +template +btBroadphaseProxy* btAxisSweep3Internal::createProxy( const btVector3& aabbMin, const btVector3& aabbMax,int shapeType,void* userPtr,short int collisionFilterGroup,short int collisionFilterMask,btDispatcher* dispatcher,void* multiSapProxy) +{ + (void)shapeType; + BP_FP_INT_TYPE handleId = addHandle(aabbMin,aabbMax, userPtr,collisionFilterGroup,collisionFilterMask,dispatcher,multiSapProxy); + + Handle* handle = getHandle(handleId); + + if (m_raycastAccelerator) + { + btBroadphaseProxy* rayProxy = m_raycastAccelerator->createProxy(aabbMin,aabbMax,shapeType,userPtr,collisionFilterGroup,collisionFilterMask,dispatcher,0); + handle->m_dbvtProxy = rayProxy; + } + return handle; +} + + + +template +void btAxisSweep3Internal::destroyProxy(btBroadphaseProxy* proxy,btDispatcher* dispatcher) +{ + Handle* handle = static_cast(proxy); + if (m_raycastAccelerator) + m_raycastAccelerator->destroyProxy(handle->m_dbvtProxy,dispatcher); + removeHandle(static_cast(handle->m_uniqueId), dispatcher); +} + +template +void btAxisSweep3Internal::setAabb(btBroadphaseProxy* proxy,const btVector3& aabbMin,const btVector3& aabbMax,btDispatcher* dispatcher) +{ + Handle* handle = static_cast(proxy); + handle->m_aabbMin = aabbMin; + handle->m_aabbMax = aabbMax; + updateHandle(static_cast(handle->m_uniqueId), aabbMin, aabbMax,dispatcher); + if (m_raycastAccelerator) + m_raycastAccelerator->setAabb(handle->m_dbvtProxy,aabbMin,aabbMax,dispatcher); + +} + +template +void btAxisSweep3Internal::rayTest(const btVector3& rayFrom,const btVector3& rayTo, btBroadphaseRayCallback& rayCallback,const btVector3& aabbMin,const btVector3& aabbMax) +{ + if (m_raycastAccelerator) + { + m_raycastAccelerator->rayTest(rayFrom,rayTo,rayCallback,aabbMin,aabbMax); + } else + { + //choose axis? + BP_FP_INT_TYPE axis = 0; + //for each proxy + for (BP_FP_INT_TYPE i=1;i +void btAxisSweep3Internal::getAabb(btBroadphaseProxy* proxy,btVector3& aabbMin, btVector3& aabbMax ) const +{ + Handle* pHandle = static_cast(proxy); + aabbMin = pHandle->m_aabbMin; + aabbMax = pHandle->m_aabbMax; +} + + +template +void btAxisSweep3Internal::unQuantize(btBroadphaseProxy* proxy,btVector3& aabbMin, btVector3& aabbMax ) const +{ + Handle* pHandle = static_cast(proxy); + + unsigned short vecInMin[3]; + unsigned short vecInMax[3]; + + vecInMin[0] = m_pEdges[0][pHandle->m_minEdges[0]].m_pos ; + vecInMax[0] = m_pEdges[0][pHandle->m_maxEdges[0]].m_pos +1 ; + vecInMin[1] = m_pEdges[1][pHandle->m_minEdges[1]].m_pos ; + vecInMax[1] = m_pEdges[1][pHandle->m_maxEdges[1]].m_pos +1 ; + vecInMin[2] = m_pEdges[2][pHandle->m_minEdges[2]].m_pos ; + vecInMax[2] = m_pEdges[2][pHandle->m_maxEdges[2]].m_pos +1 ; + + aabbMin.setValue((btScalar)(vecInMin[0]) / (m_quantize.getX()),(btScalar)(vecInMin[1]) / (m_quantize.getY()),(btScalar)(vecInMin[2]) / (m_quantize.getZ())); + aabbMin += m_worldAabbMin; + + aabbMax.setValue((btScalar)(vecInMax[0]) / (m_quantize.getX()),(btScalar)(vecInMax[1]) / (m_quantize.getY()),(btScalar)(vecInMax[2]) / (m_quantize.getZ())); + aabbMax += m_worldAabbMin; +} + + + + +template +btAxisSweep3Internal::btAxisSweep3Internal(const btVector3& worldAabbMin,const btVector3& worldAabbMax, BP_FP_INT_TYPE handleMask, BP_FP_INT_TYPE handleSentinel,BP_FP_INT_TYPE userMaxHandles, btOverlappingPairCache* pairCache , bool disableRaycastAccelerator) +:m_bpHandleMask(handleMask), +m_handleSentinel(handleSentinel), +m_pairCache(pairCache), +m_userPairCallback(0), +m_ownsPairCache(false), +m_invalidPair(0), +m_raycastAccelerator(0) +{ + BP_FP_INT_TYPE maxHandles = static_cast(userMaxHandles+1);//need to add one sentinel handle + + if (!m_pairCache) + { + void* ptr = btAlignedAlloc(sizeof(btHashedOverlappingPairCache),16); + m_pairCache = new(ptr) btHashedOverlappingPairCache(); + m_ownsPairCache = true; + } + + if (!disableRaycastAccelerator) + { + m_nullPairCache = new (btAlignedAlloc(sizeof(btNullPairCache),16)) btNullPairCache(); + m_raycastAccelerator = new (btAlignedAlloc(sizeof(btDbvtBroadphase),16)) btDbvtBroadphase(m_nullPairCache);//m_pairCache); + m_raycastAccelerator->m_deferedcollide = true;//don't add/remove pairs + } + + //btAssert(bounds.HasVolume()); + + // init bounds + m_worldAabbMin = worldAabbMin; + m_worldAabbMax = worldAabbMax; + + btVector3 aabbSize = m_worldAabbMax - m_worldAabbMin; + + BP_FP_INT_TYPE maxInt = m_handleSentinel; + + m_quantize = btVector3(btScalar(maxInt),btScalar(maxInt),btScalar(maxInt)) / aabbSize; + + // allocate handles buffer, using btAlignedAlloc, and put all handles on free list + m_pHandles = new Handle[maxHandles]; + + m_maxHandles = maxHandles; + m_numHandles = 0; + + // handle 0 is reserved as the null index, and is also used as the sentinel + m_firstFreeHandle = 1; + { + for (BP_FP_INT_TYPE i = m_firstFreeHandle; i < maxHandles; i++) + m_pHandles[i].SetNextFree(static_cast(i + 1)); + m_pHandles[maxHandles - 1].SetNextFree(0); + } + + { + // allocate edge buffers + for (int i = 0; i < 3; i++) + { + m_pEdgesRawPtr[i] = btAlignedAlloc(sizeof(Edge)*maxHandles*2,16); + m_pEdges[i] = new(m_pEdgesRawPtr[i]) Edge[maxHandles * 2]; + } + } + //removed overlap management + + // make boundary sentinels + + m_pHandles[0].m_clientObject = 0; + + for (int axis = 0; axis < 3; axis++) + { + m_pHandles[0].m_minEdges[axis] = 0; + m_pHandles[0].m_maxEdges[axis] = 1; + + m_pEdges[axis][0].m_pos = 0; + m_pEdges[axis][0].m_handle = 0; + m_pEdges[axis][1].m_pos = m_handleSentinel; + m_pEdges[axis][1].m_handle = 0; +#ifdef DEBUG_BROADPHASE + debugPrintAxis(axis); +#endif //DEBUG_BROADPHASE + + } + +} + +template +btAxisSweep3Internal::~btAxisSweep3Internal() +{ + if (m_raycastAccelerator) + { + m_nullPairCache->~btOverlappingPairCache(); + btAlignedFree(m_nullPairCache); + m_raycastAccelerator->~btDbvtBroadphase(); + btAlignedFree (m_raycastAccelerator); + } + + for (int i = 2; i >= 0; i--) + { + btAlignedFree(m_pEdgesRawPtr[i]); + } + delete [] m_pHandles; + + if (m_ownsPairCache) + { + m_pairCache->~btOverlappingPairCache(); + btAlignedFree(m_pairCache); + } +} + +template +void btAxisSweep3Internal::quantize(BP_FP_INT_TYPE* out, const btVector3& point, int isMax) const +{ +#ifdef OLD_CLAMPING_METHOD + ///problem with this clamping method is that the floating point during quantization might still go outside the range [(0|isMax) .. (m_handleSentinel&m_bpHandleMask]|isMax] + ///see http://code.google.com/p/bullet/issues/detail?id=87 + btVector3 clampedPoint(point); + clampedPoint.setMax(m_worldAabbMin); + clampedPoint.setMin(m_worldAabbMax); + btVector3 v = (clampedPoint - m_worldAabbMin) * m_quantize; + out[0] = (BP_FP_INT_TYPE)(((BP_FP_INT_TYPE)v.getX() & m_bpHandleMask) | isMax); + out[1] = (BP_FP_INT_TYPE)(((BP_FP_INT_TYPE)v.getY() & m_bpHandleMask) | isMax); + out[2] = (BP_FP_INT_TYPE)(((BP_FP_INT_TYPE)v.getZ() & m_bpHandleMask) | isMax); +#else + btVector3 v = (point - m_worldAabbMin) * m_quantize; + out[0]=(v[0]<=0)?(BP_FP_INT_TYPE)isMax:(v[0]>=m_handleSentinel)?(BP_FP_INT_TYPE)((m_handleSentinel&m_bpHandleMask)|isMax):(BP_FP_INT_TYPE)(((BP_FP_INT_TYPE)v[0]&m_bpHandleMask)|isMax); + out[1]=(v[1]<=0)?(BP_FP_INT_TYPE)isMax:(v[1]>=m_handleSentinel)?(BP_FP_INT_TYPE)((m_handleSentinel&m_bpHandleMask)|isMax):(BP_FP_INT_TYPE)(((BP_FP_INT_TYPE)v[1]&m_bpHandleMask)|isMax); + out[2]=(v[2]<=0)?(BP_FP_INT_TYPE)isMax:(v[2]>=m_handleSentinel)?(BP_FP_INT_TYPE)((m_handleSentinel&m_bpHandleMask)|isMax):(BP_FP_INT_TYPE)(((BP_FP_INT_TYPE)v[2]&m_bpHandleMask)|isMax); +#endif //OLD_CLAMPING_METHOD +} + + +template +BP_FP_INT_TYPE btAxisSweep3Internal::allocHandle() +{ + btAssert(m_firstFreeHandle); + + BP_FP_INT_TYPE handle = m_firstFreeHandle; + m_firstFreeHandle = getHandle(handle)->GetNextFree(); + m_numHandles++; + + return handle; +} + +template +void btAxisSweep3Internal::freeHandle(BP_FP_INT_TYPE handle) +{ + btAssert(handle > 0 && handle < m_maxHandles); + + getHandle(handle)->SetNextFree(m_firstFreeHandle); + m_firstFreeHandle = handle; + + m_numHandles--; +} + + +template +BP_FP_INT_TYPE btAxisSweep3Internal::addHandle(const btVector3& aabbMin,const btVector3& aabbMax, void* pOwner,short int collisionFilterGroup,short int collisionFilterMask,btDispatcher* dispatcher,void* multiSapProxy) +{ + // quantize the bounds + BP_FP_INT_TYPE min[3], max[3]; + quantize(min, aabbMin, 0); + quantize(max, aabbMax, 1); + + // allocate a handle + BP_FP_INT_TYPE handle = allocHandle(); + + + Handle* pHandle = getHandle(handle); + + pHandle->m_uniqueId = static_cast(handle); + //pHandle->m_pOverlaps = 0; + pHandle->m_clientObject = pOwner; + pHandle->m_collisionFilterGroup = collisionFilterGroup; + pHandle->m_collisionFilterMask = collisionFilterMask; + pHandle->m_multiSapParentProxy = multiSapProxy; + + // compute current limit of edge arrays + BP_FP_INT_TYPE limit = static_cast(m_numHandles * 2); + + + // insert new edges just inside the max boundary edge + for (BP_FP_INT_TYPE axis = 0; axis < 3; axis++) + { + + m_pHandles[0].m_maxEdges[axis] += 2; + + m_pEdges[axis][limit + 1] = m_pEdges[axis][limit - 1]; + + m_pEdges[axis][limit - 1].m_pos = min[axis]; + m_pEdges[axis][limit - 1].m_handle = handle; + + m_pEdges[axis][limit].m_pos = max[axis]; + m_pEdges[axis][limit].m_handle = handle; + + pHandle->m_minEdges[axis] = static_cast(limit - 1); + pHandle->m_maxEdges[axis] = limit; + } + + // now sort the new edges to their correct position + sortMinDown(0, pHandle->m_minEdges[0], dispatcher,false); + sortMaxDown(0, pHandle->m_maxEdges[0], dispatcher,false); + sortMinDown(1, pHandle->m_minEdges[1], dispatcher,false); + sortMaxDown(1, pHandle->m_maxEdges[1], dispatcher,false); + sortMinDown(2, pHandle->m_minEdges[2], dispatcher,true); + sortMaxDown(2, pHandle->m_maxEdges[2], dispatcher,true); + + + return handle; +} + + +template +void btAxisSweep3Internal::removeHandle(BP_FP_INT_TYPE handle,btDispatcher* dispatcher) +{ + + Handle* pHandle = getHandle(handle); + + //explicitly remove the pairs containing the proxy + //we could do it also in the sortMinUp (passing true) + ///@todo: compare performance + if (!m_pairCache->hasDeferredRemoval()) + { + m_pairCache->removeOverlappingPairsContainingProxy(pHandle,dispatcher); + } + + // compute current limit of edge arrays + int limit = static_cast(m_numHandles * 2); + + int axis; + + for (axis = 0;axis<3;axis++) + { + m_pHandles[0].m_maxEdges[axis] -= 2; + } + + // remove the edges by sorting them up to the end of the list + for ( axis = 0; axis < 3; axis++) + { + Edge* pEdges = m_pEdges[axis]; + BP_FP_INT_TYPE max = pHandle->m_maxEdges[axis]; + pEdges[max].m_pos = m_handleSentinel; + + sortMaxUp(axis,max,dispatcher,false); + + + BP_FP_INT_TYPE i = pHandle->m_minEdges[axis]; + pEdges[i].m_pos = m_handleSentinel; + + + sortMinUp(axis,i,dispatcher,false); + + pEdges[limit-1].m_handle = 0; + pEdges[limit-1].m_pos = m_handleSentinel; + +#ifdef DEBUG_BROADPHASE + debugPrintAxis(axis,false); +#endif //DEBUG_BROADPHASE + + + } + + + // free the handle + freeHandle(handle); + + +} + +template +void btAxisSweep3Internal::resetPool(btDispatcher* dispatcher) +{ + if (m_numHandles == 0) + { + m_firstFreeHandle = 1; + { + for (BP_FP_INT_TYPE i = m_firstFreeHandle; i < m_maxHandles; i++) + m_pHandles[i].SetNextFree(static_cast(i + 1)); + m_pHandles[m_maxHandles - 1].SetNextFree(0); + } + } +} + + +extern int gOverlappingPairs; +//#include + +template +void btAxisSweep3Internal::calculateOverlappingPairs(btDispatcher* dispatcher) +{ + + if (m_pairCache->hasDeferredRemoval()) + { + + btBroadphasePairArray& overlappingPairArray = m_pairCache->getOverlappingPairArray(); + + //perform a sort, to find duplicates and to sort 'invalid' pairs to the end + overlappingPairArray.quickSort(btBroadphasePairSortPredicate()); + + overlappingPairArray.resize(overlappingPairArray.size() - m_invalidPair); + m_invalidPair = 0; + + + int i; + + btBroadphasePair previousPair; + previousPair.m_pProxy0 = 0; + previousPair.m_pProxy1 = 0; + previousPair.m_algorithm = 0; + + + for (i=0;iprocessOverlap(pair); + } else + { + needsRemoval = true; + } + } else + { + //remove duplicate + needsRemoval = true; + //should have no algorithm + btAssert(!pair.m_algorithm); + } + + if (needsRemoval) + { + m_pairCache->cleanOverlappingPair(pair,dispatcher); + + // m_overlappingPairArray.swap(i,m_overlappingPairArray.size()-1); + // m_overlappingPairArray.pop_back(); + pair.m_pProxy0 = 0; + pair.m_pProxy1 = 0; + m_invalidPair++; + gOverlappingPairs--; + } + + } + + ///if you don't like to skip the invalid pairs in the array, execute following code: + #define CLEAN_INVALID_PAIRS 1 + #ifdef CLEAN_INVALID_PAIRS + + //perform a sort, to sort 'invalid' pairs to the end + overlappingPairArray.quickSort(btBroadphasePairSortPredicate()); + + overlappingPairArray.resize(overlappingPairArray.size() - m_invalidPair); + m_invalidPair = 0; + #endif//CLEAN_INVALID_PAIRS + + //printf("overlappingPairArray.size()=%d\n",overlappingPairArray.size()); + } + +} + + +template +bool btAxisSweep3Internal::testAabbOverlap(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1) +{ + const Handle* pHandleA = static_cast(proxy0); + const Handle* pHandleB = static_cast(proxy1); + + //optimization 1: check the array index (memory address), instead of the m_pos + + for (int axis = 0; axis < 3; axis++) + { + if (pHandleA->m_maxEdges[axis] < pHandleB->m_minEdges[axis] || + pHandleB->m_maxEdges[axis] < pHandleA->m_minEdges[axis]) + { + return false; + } + } + return true; +} + +template +bool btAxisSweep3Internal::testOverlap2D(const Handle* pHandleA, const Handle* pHandleB,int axis0,int axis1) +{ + //optimization 1: check the array index (memory address), instead of the m_pos + + if (pHandleA->m_maxEdges[axis0] < pHandleB->m_minEdges[axis0] || + pHandleB->m_maxEdges[axis0] < pHandleA->m_minEdges[axis0] || + pHandleA->m_maxEdges[axis1] < pHandleB->m_minEdges[axis1] || + pHandleB->m_maxEdges[axis1] < pHandleA->m_minEdges[axis1]) + { + return false; + } + return true; +} + +template +void btAxisSweep3Internal::updateHandle(BP_FP_INT_TYPE handle, const btVector3& aabbMin,const btVector3& aabbMax,btDispatcher* dispatcher) +{ +// btAssert(bounds.IsFinite()); + //btAssert(bounds.HasVolume()); + + Handle* pHandle = getHandle(handle); + + // quantize the new bounds + BP_FP_INT_TYPE min[3], max[3]; + quantize(min, aabbMin, 0); + quantize(max, aabbMax, 1); + + // update changed edges + for (int axis = 0; axis < 3; axis++) + { + BP_FP_INT_TYPE emin = pHandle->m_minEdges[axis]; + BP_FP_INT_TYPE emax = pHandle->m_maxEdges[axis]; + + int dmin = (int)min[axis] - (int)m_pEdges[axis][emin].m_pos; + int dmax = (int)max[axis] - (int)m_pEdges[axis][emax].m_pos; + + m_pEdges[axis][emin].m_pos = min[axis]; + m_pEdges[axis][emax].m_pos = max[axis]; + + // expand (only adds overlaps) + if (dmin < 0) + sortMinDown(axis, emin,dispatcher,true); + + if (dmax > 0) + sortMaxUp(axis, emax,dispatcher,true); + + // shrink (only removes overlaps) + if (dmin > 0) + sortMinUp(axis, emin,dispatcher,true); + + if (dmax < 0) + sortMaxDown(axis, emax,dispatcher,true); + +#ifdef DEBUG_BROADPHASE + debugPrintAxis(axis); +#endif //DEBUG_BROADPHASE + } + + +} + + + + +// sorting a min edge downwards can only ever *add* overlaps +template +void btAxisSweep3Internal::sortMinDown(int axis, BP_FP_INT_TYPE edge, btDispatcher* /* dispatcher */, bool updateOverlaps) +{ + + Edge* pEdge = m_pEdges[axis] + edge; + Edge* pPrev = pEdge - 1; + Handle* pHandleEdge = getHandle(pEdge->m_handle); + + while (pEdge->m_pos < pPrev->m_pos) + { + Handle* pHandlePrev = getHandle(pPrev->m_handle); + + if (pPrev->IsMax()) + { + // if previous edge is a maximum check the bounds and add an overlap if necessary + const int axis1 = (1 << axis) & 3; + const int axis2 = (1 << axis1) & 3; + if (updateOverlaps && testOverlap2D(pHandleEdge, pHandlePrev,axis1,axis2)) + { + m_pairCache->addOverlappingPair(pHandleEdge,pHandlePrev); + if (m_userPairCallback) + m_userPairCallback->addOverlappingPair(pHandleEdge,pHandlePrev); + + //AddOverlap(pEdge->m_handle, pPrev->m_handle); + + } + + // update edge reference in other handle + pHandlePrev->m_maxEdges[axis]++; + } + else + pHandlePrev->m_minEdges[axis]++; + + pHandleEdge->m_minEdges[axis]--; + + // swap the edges + Edge swap = *pEdge; + *pEdge = *pPrev; + *pPrev = swap; + + // decrement + pEdge--; + pPrev--; + } + +#ifdef DEBUG_BROADPHASE + debugPrintAxis(axis); +#endif //DEBUG_BROADPHASE + +} + +// sorting a min edge upwards can only ever *remove* overlaps +template +void btAxisSweep3Internal::sortMinUp(int axis, BP_FP_INT_TYPE edge, btDispatcher* dispatcher, bool updateOverlaps) +{ + Edge* pEdge = m_pEdges[axis] + edge; + Edge* pNext = pEdge + 1; + Handle* pHandleEdge = getHandle(pEdge->m_handle); + + while (pNext->m_handle && (pEdge->m_pos >= pNext->m_pos)) + { + Handle* pHandleNext = getHandle(pNext->m_handle); + + if (pNext->IsMax()) + { + Handle* handle0 = getHandle(pEdge->m_handle); + Handle* handle1 = getHandle(pNext->m_handle); + const int axis1 = (1 << axis) & 3; + const int axis2 = (1 << axis1) & 3; + + // if next edge is maximum remove any overlap between the two handles + if (updateOverlaps +#ifdef USE_OVERLAP_TEST_ON_REMOVES + && testOverlap2D(handle0,handle1,axis1,axis2) +#endif //USE_OVERLAP_TEST_ON_REMOVES + ) + { + + + m_pairCache->removeOverlappingPair(handle0,handle1,dispatcher); + if (m_userPairCallback) + m_userPairCallback->removeOverlappingPair(handle0,handle1,dispatcher); + + } + + + // update edge reference in other handle + pHandleNext->m_maxEdges[axis]--; + } + else + pHandleNext->m_minEdges[axis]--; + + pHandleEdge->m_minEdges[axis]++; + + // swap the edges + Edge swap = *pEdge; + *pEdge = *pNext; + *pNext = swap; + + // increment + pEdge++; + pNext++; + } + + +} + +// sorting a max edge downwards can only ever *remove* overlaps +template +void btAxisSweep3Internal::sortMaxDown(int axis, BP_FP_INT_TYPE edge, btDispatcher* dispatcher, bool updateOverlaps) +{ + + Edge* pEdge = m_pEdges[axis] + edge; + Edge* pPrev = pEdge - 1; + Handle* pHandleEdge = getHandle(pEdge->m_handle); + + while (pEdge->m_pos < pPrev->m_pos) + { + Handle* pHandlePrev = getHandle(pPrev->m_handle); + + if (!pPrev->IsMax()) + { + // if previous edge was a minimum remove any overlap between the two handles + Handle* handle0 = getHandle(pEdge->m_handle); + Handle* handle1 = getHandle(pPrev->m_handle); + const int axis1 = (1 << axis) & 3; + const int axis2 = (1 << axis1) & 3; + + if (updateOverlaps +#ifdef USE_OVERLAP_TEST_ON_REMOVES + && testOverlap2D(handle0,handle1,axis1,axis2) +#endif //USE_OVERLAP_TEST_ON_REMOVES + ) + { + //this is done during the overlappingpairarray iteration/narrowphase collision + + + m_pairCache->removeOverlappingPair(handle0,handle1,dispatcher); + if (m_userPairCallback) + m_userPairCallback->removeOverlappingPair(handle0,handle1,dispatcher); + + + + } + + // update edge reference in other handle + pHandlePrev->m_minEdges[axis]++;; + } + else + pHandlePrev->m_maxEdges[axis]++; + + pHandleEdge->m_maxEdges[axis]--; + + // swap the edges + Edge swap = *pEdge; + *pEdge = *pPrev; + *pPrev = swap; + + // decrement + pEdge--; + pPrev--; + } + + +#ifdef DEBUG_BROADPHASE + debugPrintAxis(axis); +#endif //DEBUG_BROADPHASE + +} + +// sorting a max edge upwards can only ever *add* overlaps +template +void btAxisSweep3Internal::sortMaxUp(int axis, BP_FP_INT_TYPE edge, btDispatcher* /* dispatcher */, bool updateOverlaps) +{ + Edge* pEdge = m_pEdges[axis] + edge; + Edge* pNext = pEdge + 1; + Handle* pHandleEdge = getHandle(pEdge->m_handle); + + while (pNext->m_handle && (pEdge->m_pos >= pNext->m_pos)) + { + Handle* pHandleNext = getHandle(pNext->m_handle); + + const int axis1 = (1 << axis) & 3; + const int axis2 = (1 << axis1) & 3; + + if (!pNext->IsMax()) + { + // if next edge is a minimum check the bounds and add an overlap if necessary + if (updateOverlaps && testOverlap2D(pHandleEdge, pHandleNext,axis1,axis2)) + { + Handle* handle0 = getHandle(pEdge->m_handle); + Handle* handle1 = getHandle(pNext->m_handle); + m_pairCache->addOverlappingPair(handle0,handle1); + if (m_userPairCallback) + m_userPairCallback->addOverlappingPair(handle0,handle1); + } + + // update edge reference in other handle + pHandleNext->m_minEdges[axis]--; + } + else + pHandleNext->m_maxEdges[axis]--; + + pHandleEdge->m_maxEdges[axis]++; + + // swap the edges + Edge swap = *pEdge; + *pEdge = *pNext; + *pNext = swap; + + // increment + pEdge++; + pNext++; + } + +} + + + +//////////////////////////////////////////////////////////////////// + + +/// The btAxisSweep3 is an efficient implementation of the 3d axis sweep and prune broadphase. +/// It uses arrays rather then lists for storage of the 3 axis. Also it operates using 16 bit integer coordinates instead of floats. +/// For large worlds and many objects, use bt32BitAxisSweep3 or btDbvtBroadphase instead. bt32BitAxisSweep3 has higher precision and allows more then 16384 objects at the cost of more memory and bit of performance. +class btAxisSweep3 : public btAxisSweep3Internal +{ +public: + + btAxisSweep3(const btVector3& worldAabbMin,const btVector3& worldAabbMax, unsigned short int maxHandles = 16384, btOverlappingPairCache* pairCache = 0, bool disableRaycastAccelerator = false); + +}; + +/// The bt32BitAxisSweep3 allows higher precision quantization and more objects compared to the btAxisSweep3 sweep and prune. +/// This comes at the cost of more memory per handle, and a bit slower performance. +/// It uses arrays rather then lists for storage of the 3 axis. +class bt32BitAxisSweep3 : public btAxisSweep3Internal +{ +public: + + bt32BitAxisSweep3(const btVector3& worldAabbMin,const btVector3& worldAabbMax, unsigned int maxHandles = 1500000, btOverlappingPairCache* pairCache = 0, bool disableRaycastAccelerator = false); + +}; + +#endif + diff --git a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btBroadphaseInterface.h b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btBroadphaseInterface.h new file mode 100644 index 000000000..b7bbaf512 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btBroadphaseInterface.h @@ -0,0 +1,74 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 BROADPHASE_INTERFACE_H +#define BROADPHASE_INTERFACE_H + + + +struct btDispatcherInfo; +class btDispatcher; +#include "btBroadphaseProxy.h" + +class btOverlappingPairCache; + + + +struct btBroadphaseRayCallback +{ + ///added some cached data to accelerate ray-AABB tests + btVector3 m_rayDirectionInverse; + unsigned int m_signs[3]; + btScalar m_lambda_max; + + virtual ~btBroadphaseRayCallback() {} + virtual bool process(const btBroadphaseProxy* proxy) = 0; +}; + +#include "LinearMath/btVector3.h" + +///The btBroadphaseInterface class provides an interface to detect aabb-overlapping object pairs. +///Some implementations for this broadphase interface include btAxisSweep3, bt32BitAxisSweep3 and btDbvtBroadphase. +///The actual overlapping pair management, storage, adding and removing of pairs is dealt by the btOverlappingPairCache class. +class btBroadphaseInterface +{ +public: + virtual ~btBroadphaseInterface() {} + + virtual btBroadphaseProxy* createProxy( const btVector3& aabbMin, const btVector3& aabbMax,int shapeType,void* userPtr, short int collisionFilterGroup,short int collisionFilterMask, btDispatcher* dispatcher,void* multiSapProxy) =0; + virtual void destroyProxy(btBroadphaseProxy* proxy,btDispatcher* dispatcher)=0; + virtual void setAabb(btBroadphaseProxy* proxy,const btVector3& aabbMin,const btVector3& aabbMax, btDispatcher* dispatcher)=0; + virtual void getAabb(btBroadphaseProxy* proxy,btVector3& aabbMin, btVector3& aabbMax ) const =0; + + virtual void rayTest(const btVector3& rayFrom,const btVector3& rayTo, btBroadphaseRayCallback& rayCallback, const btVector3& aabbMin=btVector3(0,0,0), const btVector3& aabbMax = btVector3(0,0,0)) = 0; + + ///calculateOverlappingPairs is optional: incremental algorithms (sweep and prune) might do it during the set aabb + virtual void calculateOverlappingPairs(btDispatcher* dispatcher)=0; + + virtual btOverlappingPairCache* getOverlappingPairCache()=0; + virtual const btOverlappingPairCache* getOverlappingPairCache() const =0; + + ///getAabb returns the axis aligned bounding box in the 'global' coordinate frame + ///will add some transform later + virtual void getBroadphaseAabb(btVector3& aabbMin,btVector3& aabbMax) const =0; + + ///reset broadphase internal structures, to ensure determinism/reproducability + virtual void resetPool(btDispatcher* dispatcher) {}; + + virtual void printStats() = 0; + +}; + +#endif //BROADPHASE_INTERFACE_H diff --git a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btBroadphaseProxy.cpp b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btBroadphaseProxy.cpp new file mode 100644 index 000000000..f4d7341f8 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btBroadphaseProxy.cpp @@ -0,0 +1,17 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btBroadphaseProxy.h" + diff --git a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btBroadphaseProxy.h b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btBroadphaseProxy.h new file mode 100644 index 000000000..5e7584dbc --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btBroadphaseProxy.h @@ -0,0 +1,259 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 BROADPHASE_PROXY_H +#define BROADPHASE_PROXY_H + +#include "LinearMath/btScalar.h" //for SIMD_FORCE_INLINE +#include "LinearMath/btVector3.h" +#include "LinearMath/btAlignedAllocator.h" + + +/// btDispatcher uses these types +/// IMPORTANT NOTE:The types are ordered polyhedral, implicit convex and concave +/// to facilitate type checking +/// CUSTOM_POLYHEDRAL_SHAPE_TYPE,CUSTOM_CONVEX_SHAPE_TYPE and CUSTOM_CONCAVE_SHAPE_TYPE can be used to extend Bullet without modifying source code +enum BroadphaseNativeTypes +{ + // polyhedral convex shapes + BOX_SHAPE_PROXYTYPE, + TRIANGLE_SHAPE_PROXYTYPE, + TETRAHEDRAL_SHAPE_PROXYTYPE, + CONVEX_TRIANGLEMESH_SHAPE_PROXYTYPE, + CONVEX_HULL_SHAPE_PROXYTYPE, + CONVEX_POINT_CLOUD_SHAPE_PROXYTYPE, + CUSTOM_POLYHEDRAL_SHAPE_TYPE, +//implicit convex shapes +IMPLICIT_CONVEX_SHAPES_START_HERE, + SPHERE_SHAPE_PROXYTYPE, + MULTI_SPHERE_SHAPE_PROXYTYPE, + CAPSULE_SHAPE_PROXYTYPE, + CONE_SHAPE_PROXYTYPE, + CONVEX_SHAPE_PROXYTYPE, + CYLINDER_SHAPE_PROXYTYPE, + UNIFORM_SCALING_SHAPE_PROXYTYPE, + MINKOWSKI_SUM_SHAPE_PROXYTYPE, + MINKOWSKI_DIFFERENCE_SHAPE_PROXYTYPE, + BOX_2D_SHAPE_PROXYTYPE, + CONVEX_2D_SHAPE_PROXYTYPE, + CUSTOM_CONVEX_SHAPE_TYPE, +//concave shapes +CONCAVE_SHAPES_START_HERE, + //keep all the convex shapetype below here, for the check IsConvexShape in broadphase proxy! + TRIANGLE_MESH_SHAPE_PROXYTYPE, + SCALED_TRIANGLE_MESH_SHAPE_PROXYTYPE, + ///used for demo integration FAST/Swift collision library and Bullet + FAST_CONCAVE_MESH_PROXYTYPE, + //terrain + TERRAIN_SHAPE_PROXYTYPE, +///Used for GIMPACT Trimesh integration + GIMPACT_SHAPE_PROXYTYPE, +///Multimaterial mesh + MULTIMATERIAL_TRIANGLE_MESH_PROXYTYPE, + + EMPTY_SHAPE_PROXYTYPE, + STATIC_PLANE_PROXYTYPE, + CUSTOM_CONCAVE_SHAPE_TYPE, +CONCAVE_SHAPES_END_HERE, + + COMPOUND_SHAPE_PROXYTYPE, + + SOFTBODY_SHAPE_PROXYTYPE, + HFFLUID_SHAPE_PROXYTYPE, + HFFLUID_BUOYANT_CONVEX_SHAPE_PROXYTYPE, + INVALID_SHAPE_PROXYTYPE, + + MAX_BROADPHASE_COLLISION_TYPES + +}; + + +///The btBroadphaseProxy is the main class that can be used with the Bullet broadphases. +///It stores collision shape type information, collision filter information and a client object, typically a btCollisionObject or btRigidBody. +ATTRIBUTE_ALIGNED16(struct) btBroadphaseProxy +{ + +BT_DECLARE_ALIGNED_ALLOCATOR(); + + ///optional filtering to cull potential collisions + enum CollisionFilterGroups + { + DefaultFilter = 1, + StaticFilter = 2, + KinematicFilter = 4, + DebrisFilter = 8, + SensorTrigger = 16, + CharacterFilter = 32, + AllFilter = -1 //all bits sets: DefaultFilter | StaticFilter | KinematicFilter | DebrisFilter | SensorTrigger + }; + + //Usually the client btCollisionObject or Rigidbody class + void* m_clientObject; + short int m_collisionFilterGroup; + short int m_collisionFilterMask; + void* m_multiSapParentProxy; + int m_uniqueId;//m_uniqueId is introduced for paircache. could get rid of this, by calculating the address offset etc. + + btVector3 m_aabbMin; + btVector3 m_aabbMax; + + SIMD_FORCE_INLINE int getUid() const + { + return m_uniqueId; + } + + //used for memory pools + btBroadphaseProxy() :m_clientObject(0),m_multiSapParentProxy(0) + { + } + + btBroadphaseProxy(const btVector3& aabbMin,const btVector3& aabbMax,void* userPtr,short int collisionFilterGroup, short int collisionFilterMask,void* multiSapParentProxy=0) + :m_clientObject(userPtr), + m_collisionFilterGroup(collisionFilterGroup), + m_collisionFilterMask(collisionFilterMask), + m_aabbMin(aabbMin), + m_aabbMax(aabbMax) + { + m_multiSapParentProxy = multiSapParentProxy; + } + + + + static SIMD_FORCE_INLINE bool isPolyhedral(int proxyType) + { + return (proxyType < IMPLICIT_CONVEX_SHAPES_START_HERE); + } + + static SIMD_FORCE_INLINE bool isConvex(int proxyType) + { + return (proxyType < CONCAVE_SHAPES_START_HERE); + } + + static SIMD_FORCE_INLINE bool isConcave(int proxyType) + { + return ((proxyType > CONCAVE_SHAPES_START_HERE) && + (proxyType < CONCAVE_SHAPES_END_HERE)); + } + static SIMD_FORCE_INLINE bool isCompound(int proxyType) + { + return (proxyType == COMPOUND_SHAPE_PROXYTYPE); + } + static SIMD_FORCE_INLINE bool isInfinite(int proxyType) + { + return (proxyType == STATIC_PLANE_PROXYTYPE); + } + + static SIMD_FORCE_INLINE bool isConvex2d(int proxyType) + { + return (proxyType == BOX_2D_SHAPE_PROXYTYPE) || (proxyType == CONVEX_2D_SHAPE_PROXYTYPE); + } + + +} +; + +class btCollisionAlgorithm; + +struct btBroadphaseProxy; + + + +///The btBroadphasePair class contains a pair of aabb-overlapping objects. +///A btDispatcher can search a btCollisionAlgorithm that performs exact/narrowphase collision detection on the actual collision shapes. +ATTRIBUTE_ALIGNED16(struct) btBroadphasePair +{ + btBroadphasePair () + : + m_pProxy0(0), + m_pProxy1(0), + m_algorithm(0), + m_internalInfo1(0) + { + } + +BT_DECLARE_ALIGNED_ALLOCATOR(); + + btBroadphasePair(const btBroadphasePair& other) + : m_pProxy0(other.m_pProxy0), + m_pProxy1(other.m_pProxy1), + m_algorithm(other.m_algorithm), + m_internalInfo1(other.m_internalInfo1) + { + } + btBroadphasePair(btBroadphaseProxy& proxy0,btBroadphaseProxy& proxy1) + { + + //keep them sorted, so the std::set operations work + if (proxy0.m_uniqueId < proxy1.m_uniqueId) + { + m_pProxy0 = &proxy0; + m_pProxy1 = &proxy1; + } + else + { + m_pProxy0 = &proxy1; + m_pProxy1 = &proxy0; + } + + m_algorithm = 0; + m_internalInfo1 = 0; + + } + + btBroadphaseProxy* m_pProxy0; + btBroadphaseProxy* m_pProxy1; + + mutable btCollisionAlgorithm* m_algorithm; + union { void* m_internalInfo1; int m_internalTmpValue;};//don't use this data, it will be removed in future version. + +}; + +/* +//comparison for set operation, see Solid DT_Encounter +SIMD_FORCE_INLINE bool operator<(const btBroadphasePair& a, const btBroadphasePair& b) +{ + return a.m_pProxy0 < b.m_pProxy0 || + (a.m_pProxy0 == b.m_pProxy0 && a.m_pProxy1 < b.m_pProxy1); +} +*/ + + + +class btBroadphasePairSortPredicate +{ + public: + + bool operator() ( const btBroadphasePair& a, const btBroadphasePair& b ) + { + const int uidA0 = a.m_pProxy0 ? a.m_pProxy0->m_uniqueId : -1; + const int uidB0 = b.m_pProxy0 ? b.m_pProxy0->m_uniqueId : -1; + const int uidA1 = a.m_pProxy1 ? a.m_pProxy1->m_uniqueId : -1; + const int uidB1 = b.m_pProxy1 ? b.m_pProxy1->m_uniqueId : -1; + + return uidA0 > uidB0 || + (a.m_pProxy0 == b.m_pProxy0 && uidA1 > uidB1) || + (a.m_pProxy0 == b.m_pProxy0 && a.m_pProxy1 == b.m_pProxy1 && a.m_algorithm > b.m_algorithm); + } +}; + + +SIMD_FORCE_INLINE bool operator==(const btBroadphasePair& a, const btBroadphasePair& b) +{ + return (a.m_pProxy0 == b.m_pProxy0) && (a.m_pProxy1 == b.m_pProxy1); +} + + +#endif //BROADPHASE_PROXY_H + diff --git a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btCollisionAlgorithm.cpp b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btCollisionAlgorithm.cpp new file mode 100644 index 000000000..c95d1be0f --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btCollisionAlgorithm.cpp @@ -0,0 +1,23 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btCollisionAlgorithm.h" +#include "btDispatcher.h" + +btCollisionAlgorithm::btCollisionAlgorithm(const btCollisionAlgorithmConstructionInfo& ci) +{ + m_dispatcher = ci.m_dispatcher1; +} + diff --git a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h new file mode 100644 index 000000000..1618ad9fd --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h @@ -0,0 +1,80 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 COLLISION_ALGORITHM_H +#define COLLISION_ALGORITHM_H + +#include "LinearMath/btScalar.h" +#include "LinearMath/btAlignedObjectArray.h" + +struct btBroadphaseProxy; +class btDispatcher; +class btManifoldResult; +class btCollisionObject; +struct btDispatcherInfo; +class btPersistentManifold; + +typedef btAlignedObjectArray btManifoldArray; + +struct btCollisionAlgorithmConstructionInfo +{ + btCollisionAlgorithmConstructionInfo() + :m_dispatcher1(0), + m_manifold(0) + { + } + btCollisionAlgorithmConstructionInfo(btDispatcher* dispatcher,int temp) + :m_dispatcher1(dispatcher) + { + (void)temp; + } + + btDispatcher* m_dispatcher1; + btPersistentManifold* m_manifold; + + int getDispatcherId(); + +}; + + +///btCollisionAlgorithm is an collision interface that is compatible with the Broadphase and btDispatcher. +///It is persistent over frames +class btCollisionAlgorithm +{ + +protected: + + btDispatcher* m_dispatcher; + +protected: + int getDispatcherId(); + +public: + + btCollisionAlgorithm() {}; + + btCollisionAlgorithm(const btCollisionAlgorithmConstructionInfo& ci); + + virtual ~btCollisionAlgorithm() {}; + + virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) = 0; + + virtual btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) = 0; + + virtual void getAllContactManifolds(btManifoldArray& manifoldArray) = 0; +}; + + +#endif //COLLISION_ALGORITHM_H diff --git a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btDbvt.cpp b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btDbvt.cpp new file mode 100644 index 000000000..ff32ec1d4 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btDbvt.cpp @@ -0,0 +1,1295 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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. +*/ +///btDbvt implementation by Nathanael Presson + +#include "btDbvt.h" + +// +typedef btAlignedObjectArray tNodeArray; +typedef btAlignedObjectArray tConstNodeArray; + +// +struct btDbvtNodeEnumerator : btDbvt::ICollide +{ + tConstNodeArray nodes; + void Process(const btDbvtNode* n) { nodes.push_back(n); } +}; + +// +static DBVT_INLINE int indexof(const btDbvtNode* node) +{ + return(node->parent->childs[1]==node); +} + +// +static DBVT_INLINE btDbvtVolume merge( const btDbvtVolume& a, + const btDbvtVolume& b) +{ +#if (DBVT_MERGE_IMPL==DBVT_IMPL_SSE) + ATTRIBUTE_ALIGNED16(char locals[sizeof(btDbvtAabbMm)]); + btDbvtVolume& res=*(btDbvtVolume*)locals; +#else + btDbvtVolume res; +#endif + Merge(a,b,res); + return(res); +} + +// volume+edge lengths +static DBVT_INLINE btScalar size(const btDbvtVolume& a) +{ + const btVector3 edges=a.Lengths(); + return( edges.x()*edges.y()*edges.z()+ + edges.x()+edges.y()+edges.z()); +} + +// +static void getmaxdepth(const btDbvtNode* node,int depth,int& maxdepth) +{ + if(node->isinternal()) + { + getmaxdepth(node->childs[0],depth+1,maxdepth); + getmaxdepth(node->childs[0],depth+1,maxdepth); + } else maxdepth=btMax(maxdepth,depth); +} + +// +static DBVT_INLINE void deletenode( btDbvt* pdbvt, + btDbvtNode* node) +{ + btAlignedFree(pdbvt->m_free); + pdbvt->m_free=node; +} + +// +static void recursedeletenode( btDbvt* pdbvt, + btDbvtNode* node) +{ + if(!node->isleaf()) + { + recursedeletenode(pdbvt,node->childs[0]); + recursedeletenode(pdbvt,node->childs[1]); + } + if(node==pdbvt->m_root) pdbvt->m_root=0; + deletenode(pdbvt,node); +} + +// +static DBVT_INLINE btDbvtNode* createnode( btDbvt* pdbvt, + btDbvtNode* parent, + void* data) +{ + btDbvtNode* node; + if(pdbvt->m_free) + { node=pdbvt->m_free;pdbvt->m_free=0; } + else + { node=new(btAlignedAlloc(sizeof(btDbvtNode),16)) btDbvtNode(); } + node->parent = parent; + node->data = data; + node->childs[1] = 0; + return(node); +} + +// +static DBVT_INLINE btDbvtNode* createnode( btDbvt* pdbvt, + btDbvtNode* parent, + const btDbvtVolume& volume, + void* data) +{ + btDbvtNode* node=createnode(pdbvt,parent,data); + node->volume=volume; + return(node); +} + +// +static DBVT_INLINE btDbvtNode* createnode( btDbvt* pdbvt, + btDbvtNode* parent, + const btDbvtVolume& volume0, + const btDbvtVolume& volume1, + void* data) +{ + btDbvtNode* node=createnode(pdbvt,parent,data); + Merge(volume0,volume1,node->volume); + return(node); +} + +// +static void insertleaf( btDbvt* pdbvt, + btDbvtNode* root, + btDbvtNode* leaf) +{ + if(!pdbvt->m_root) + { + pdbvt->m_root = leaf; + leaf->parent = 0; + } + else + { + if(!root->isleaf()) + { + do { + root=root->childs[Select( leaf->volume, + root->childs[0]->volume, + root->childs[1]->volume)]; + } while(!root->isleaf()); + } + btDbvtNode* prev=root->parent; + btDbvtNode* node=createnode(pdbvt,prev,leaf->volume,root->volume,0); + if(prev) + { + prev->childs[indexof(root)] = node; + node->childs[0] = root;root->parent=node; + node->childs[1] = leaf;leaf->parent=node; + do { + if(!prev->volume.Contain(node->volume)) + Merge(prev->childs[0]->volume,prev->childs[1]->volume,prev->volume); + else + break; + node=prev; + } while(0!=(prev=node->parent)); + } + else + { + node->childs[0] = root;root->parent=node; + node->childs[1] = leaf;leaf->parent=node; + pdbvt->m_root = node; + } + } +} + +// +static btDbvtNode* removeleaf( btDbvt* pdbvt, + btDbvtNode* leaf) +{ + if(leaf==pdbvt->m_root) + { + pdbvt->m_root=0; + return(0); + } + else + { + btDbvtNode* parent=leaf->parent; + btDbvtNode* prev=parent->parent; + btDbvtNode* sibling=parent->childs[1-indexof(leaf)]; + if(prev) + { + prev->childs[indexof(parent)]=sibling; + sibling->parent=prev; + deletenode(pdbvt,parent); + while(prev) + { + const btDbvtVolume pb=prev->volume; + Merge(prev->childs[0]->volume,prev->childs[1]->volume,prev->volume); + if(NotEqual(pb,prev->volume)) + { + prev=prev->parent; + } else break; + } + return(prev?prev:pdbvt->m_root); + } + else + { + pdbvt->m_root=sibling; + sibling->parent=0; + deletenode(pdbvt,parent); + return(pdbvt->m_root); + } + } +} + +// +static void fetchleaves(btDbvt* pdbvt, + btDbvtNode* root, + tNodeArray& leaves, + int depth=-1) +{ + if(root->isinternal()&&depth) + { + fetchleaves(pdbvt,root->childs[0],leaves,depth-1); + fetchleaves(pdbvt,root->childs[1],leaves,depth-1); + deletenode(pdbvt,root); + } + else + { + leaves.push_back(root); + } +} + +// +static void split( const tNodeArray& leaves, + tNodeArray& left, + tNodeArray& right, + const btVector3& org, + const btVector3& axis) +{ + left.resize(0); + right.resize(0); + for(int i=0,ni=leaves.size();ivolume.Center()-org)<0) + left.push_back(leaves[i]); + else + right.push_back(leaves[i]); + } +} + +// +static btDbvtVolume bounds( const tNodeArray& leaves) +{ +#if DBVT_MERGE_IMPL==DBVT_IMPL_SSE + ATTRIBUTE_ALIGNED16(char locals[sizeof(btDbvtVolume)]); + btDbvtVolume& volume=*(btDbvtVolume*)locals; + volume=leaves[0]->volume; +#else + btDbvtVolume volume=leaves[0]->volume; +#endif + for(int i=1,ni=leaves.size();ivolume,volume); + } + return(volume); +} + +// +static void bottomup( btDbvt* pdbvt, + tNodeArray& leaves) +{ + while(leaves.size()>1) + { + btScalar minsize=SIMD_INFINITY; + int minidx[2]={-1,-1}; + for(int i=0;ivolume,leaves[j]->volume)); + if(szvolume,n[1]->volume,0); + p->childs[0] = n[0]; + p->childs[1] = n[1]; + n[0]->parent = p; + n[1]->parent = p; + leaves[minidx[0]] = p; + leaves.swap(minidx[1],leaves.size()-1); + leaves.pop_back(); + } +} + +// +static btDbvtNode* topdown(btDbvt* pdbvt, + tNodeArray& leaves, + int bu_treshold) +{ + static const btVector3 axis[]={btVector3(1,0,0), + btVector3(0,1,0), + btVector3(0,0,1)}; + if(leaves.size()>1) + { + if(leaves.size()>bu_treshold) + { + const btDbvtVolume vol=bounds(leaves); + const btVector3 org=vol.Center(); + tNodeArray sets[2]; + int bestaxis=-1; + int bestmidp=leaves.size(); + int splitcount[3][2]={{0,0},{0,0},{0,0}}; + int i; + for( i=0;ivolume.Center()-org; + for(int j=0;j<3;++j) + { + ++splitcount[j][btDot(x,axis[j])>0?1:0]; + } + } + for( i=0;i<3;++i) + { + if((splitcount[i][0]>0)&&(splitcount[i][1]>0)) + { + const int midp=(int)btFabs(btScalar(splitcount[i][0]-splitcount[i][1])); + if(midp=0) + { + sets[0].reserve(splitcount[bestaxis][0]); + sets[1].reserve(splitcount[bestaxis][1]); + split(leaves,sets[0],sets[1],org,axis[bestaxis]); + } + else + { + sets[0].reserve(leaves.size()/2+1); + sets[1].reserve(leaves.size()/2); + for(int i=0,ni=leaves.size();ichilds[0]=topdown(pdbvt,sets[0],bu_treshold); + node->childs[1]=topdown(pdbvt,sets[1],bu_treshold); + node->childs[0]->parent=node; + node->childs[1]->parent=node; + return(node); + } + else + { + bottomup(pdbvt,leaves); + return(leaves[0]); + } + } + return(leaves[0]); +} + +// +static DBVT_INLINE btDbvtNode* sort(btDbvtNode* n,btDbvtNode*& r) +{ + btDbvtNode* p=n->parent; + btAssert(n->isinternal()); + if(p>n) + { + const int i=indexof(n); + const int j=1-i; + btDbvtNode* s=p->childs[j]; + btDbvtNode* q=p->parent; + btAssert(n==p->childs[i]); + if(q) q->childs[indexof(p)]=n; else r=n; + s->parent=n; + p->parent=n; + n->parent=q; + p->childs[0]=n->childs[0]; + p->childs[1]=n->childs[1]; + n->childs[0]->parent=p; + n->childs[1]->parent=p; + n->childs[i]=p; + n->childs[j]=s; + btSwap(p->volume,n->volume); + return(p); + } + return(n); +} + +#if 0 +static DBVT_INLINE btDbvtNode* walkup(btDbvtNode* n,int count) +{ + while(n&&(count--)) n=n->parent; + return(n); +} +#endif + +// +// Api +// + +// +btDbvt::btDbvt() +{ + m_root = 0; + m_free = 0; + m_lkhd = -1; + m_leaves = 0; + m_opath = 0; +} + +// +btDbvt::~btDbvt() +{ + clear(); +} + +// +void btDbvt::clear() +{ + if(m_root) + recursedeletenode(this,m_root); + btAlignedFree(m_free); + m_free=0; + m_lkhd = -1; + m_stkStack.clear(); + m_opath = 0; + +} + +// +void btDbvt::optimizeBottomUp() +{ + if(m_root) + { + tNodeArray leaves; + leaves.reserve(m_leaves); + fetchleaves(this,m_root,leaves); + bottomup(this,leaves); + m_root=leaves[0]; + } +} + +// +void btDbvt::optimizeTopDown(int bu_treshold) +{ + if(m_root) + { + tNodeArray leaves; + leaves.reserve(m_leaves); + fetchleaves(this,m_root,leaves); + m_root=topdown(this,leaves,bu_treshold); + } +} + +// +void btDbvt::optimizeIncremental(int passes) +{ + if(passes<0) passes=m_leaves; + if(m_root&&(passes>0)) + { + do { + btDbvtNode* node=m_root; + unsigned bit=0; + while(node->isinternal()) + { + node=sort(node,m_root)->childs[(m_opath>>bit)&1]; + bit=(bit+1)&(sizeof(unsigned)*8-1); + } + update(node); + ++m_opath; + } while(--passes); + } +} + +// +btDbvtNode* btDbvt::insert(const btDbvtVolume& volume,void* data) +{ + btDbvtNode* leaf=createnode(this,0,volume,data); + insertleaf(this,m_root,leaf); + ++m_leaves; + return(leaf); +} + +// +void btDbvt::update(btDbvtNode* leaf,int lookahead) +{ + btDbvtNode* root=removeleaf(this,leaf); + if(root) + { + if(lookahead>=0) + { + for(int i=0;(iparent;++i) + { + root=root->parent; + } + } else root=m_root; + } + insertleaf(this,root,leaf); +} + +// +void btDbvt::update(btDbvtNode* leaf,btDbvtVolume& volume) +{ + btDbvtNode* root=removeleaf(this,leaf); + if(root) + { + if(m_lkhd>=0) + { + for(int i=0;(iparent;++i) + { + root=root->parent; + } + } else root=m_root; + } + leaf->volume=volume; + insertleaf(this,root,leaf); +} + +// +bool btDbvt::update(btDbvtNode* leaf,btDbvtVolume& volume,const btVector3& velocity,btScalar margin) +{ + if(leaf->volume.Contain(volume)) return(false); + volume.Expand(btVector3(margin,margin,margin)); + volume.SignedExpand(velocity); + update(leaf,volume); + return(true); +} + +// +bool btDbvt::update(btDbvtNode* leaf,btDbvtVolume& volume,const btVector3& velocity) +{ + if(leaf->volume.Contain(volume)) return(false); + volume.SignedExpand(velocity); + update(leaf,volume); + return(true); +} + +// +bool btDbvt::update(btDbvtNode* leaf,btDbvtVolume& volume,btScalar margin) +{ + if(leaf->volume.Contain(volume)) return(false); + volume.Expand(btVector3(margin,margin,margin)); + update(leaf,volume); + return(true); +} + +// +void btDbvt::remove(btDbvtNode* leaf) +{ + removeleaf(this,leaf); + deletenode(this,leaf); + --m_leaves; +} + +// +void btDbvt::write(IWriter* iwriter) const +{ + btDbvtNodeEnumerator nodes; + nodes.nodes.reserve(m_leaves*2); + enumNodes(m_root,nodes); + iwriter->Prepare(m_root,nodes.nodes.size()); + for(int i=0;iparent) p=nodes.nodes.findLinearSearch(n->parent); + if(n->isinternal()) + { + const int c0=nodes.nodes.findLinearSearch(n->childs[0]); + const int c1=nodes.nodes.findLinearSearch(n->childs[1]); + iwriter->WriteNode(n,i,p,c0,c1); + } + else + { + iwriter->WriteLeaf(n,i,p); + } + } +} + +// +void btDbvt::clone(btDbvt& dest,IClone* iclone) const +{ + dest.clear(); + if(m_root!=0) + { + btAlignedObjectArray stack; + stack.reserve(m_leaves); + stack.push_back(sStkCLN(m_root,0)); + do { + const int i=stack.size()-1; + const sStkCLN e=stack[i]; + btDbvtNode* n=createnode(&dest,e.parent,e.node->volume,e.node->data); + stack.pop_back(); + if(e.parent!=0) + e.parent->childs[i&1]=n; + else + dest.m_root=n; + if(e.node->isinternal()) + { + stack.push_back(sStkCLN(e.node->childs[0],n)); + stack.push_back(sStkCLN(e.node->childs[1],n)); + } + else + { + iclone->CloneLeaf(n); + } + } while(stack.size()>0); + } +} + +// +int btDbvt::maxdepth(const btDbvtNode* node) +{ + int depth=0; + if(node) getmaxdepth(node,1,depth); + return(depth); +} + +// +int btDbvt::countLeaves(const btDbvtNode* node) +{ + if(node->isinternal()) + return(countLeaves(node->childs[0])+countLeaves(node->childs[1])); + else + return(1); +} + +// +void btDbvt::extractLeaves(const btDbvtNode* node,btAlignedObjectArray& leaves) +{ + if(node->isinternal()) + { + extractLeaves(node->childs[0],leaves); + extractLeaves(node->childs[1],leaves); + } + else + { + leaves.push_back(node); + } +} + +// +#if DBVT_ENABLE_BENCHMARK + +#include +#include +#include "LinearMath/btQuickProf.h" + +/* +q6600,2.4ghz + +/Ox /Ob2 /Oi /Ot /I "." /I "..\.." /I "..\..\src" /D "NDEBUG" /D "_LIB" /D "_WINDOWS" /D "_CRT_SECURE_NO_DEPRECATE" /D "_CRT_NONSTDC_NO_DEPRECATE" /D "WIN32" +/GF /FD /MT /GS- /Gy /arch:SSE2 /Zc:wchar_t- /Fp"..\..\out\release8\build\libbulletcollision\libbulletcollision.pch" +/Fo"..\..\out\release8\build\libbulletcollision\\" +/Fd"..\..\out\release8\build\libbulletcollision\bulletcollision.pdb" +/W3 /nologo /c /Wp64 /Zi /errorReport:prompt + +Benchmarking dbvt... +World scale: 100.000000 +Extents base: 1.000000 +Extents range: 4.000000 +Leaves: 8192 +sizeof(btDbvtVolume): 32 bytes +sizeof(btDbvtNode): 44 bytes +[1] btDbvtVolume intersections: 3499 ms (-1%) +[2] btDbvtVolume merges: 1934 ms (0%) +[3] btDbvt::collideTT: 5485 ms (-21%) +[4] btDbvt::collideTT self: 2814 ms (-20%) +[5] btDbvt::collideTT xform: 7379 ms (-1%) +[6] btDbvt::collideTT xform,self: 7270 ms (-2%) +[7] btDbvt::rayTest: 6314 ms (0%),(332143 r/s) +[8] insert/remove: 2093 ms (0%),(1001983 ir/s) +[9] updates (teleport): 1879 ms (-3%),(1116100 u/s) +[10] updates (jitter): 1244 ms (-4%),(1685813 u/s) +[11] optimize (incremental): 2514 ms (0%),(1668000 o/s) +[12] btDbvtVolume notequal: 3659 ms (0%) +[13] culling(OCL+fullsort): 2218 ms (0%),(461 t/s) +[14] culling(OCL+qsort): 3688 ms (5%),(2221 t/s) +[15] culling(KDOP+qsort): 1139 ms (-1%),(7192 t/s) +[16] insert/remove batch(256): 5092 ms (0%),(823704 bir/s) +[17] btDbvtVolume select: 3419 ms (0%) +*/ + +struct btDbvtBenchmark +{ + struct NilPolicy : btDbvt::ICollide + { + NilPolicy() : m_pcount(0),m_depth(-SIMD_INFINITY),m_checksort(true) {} + void Process(const btDbvtNode*,const btDbvtNode*) { ++m_pcount; } + void Process(const btDbvtNode*) { ++m_pcount; } + void Process(const btDbvtNode*,btScalar depth) + { + ++m_pcount; + if(m_checksort) + { if(depth>=m_depth) m_depth=depth; else printf("wrong depth: %f (should be >= %f)\r\n",depth,m_depth); } + } + int m_pcount; + btScalar m_depth; + bool m_checksort; + }; + struct P14 : btDbvt::ICollide + { + struct Node + { + const btDbvtNode* leaf; + btScalar depth; + }; + void Process(const btDbvtNode* leaf,btScalar depth) + { + Node n; + n.leaf = leaf; + n.depth = depth; + } + static int sortfnc(const Node& a,const Node& b) + { + if(a.depthb.depth) return(-1); + return(0); + } + btAlignedObjectArray m_nodes; + }; + struct P15 : btDbvt::ICollide + { + struct Node + { + const btDbvtNode* leaf; + btScalar depth; + }; + void Process(const btDbvtNode* leaf) + { + Node n; + n.leaf = leaf; + n.depth = dot(leaf->volume.Center(),m_axis); + } + static int sortfnc(const Node& a,const Node& b) + { + if(a.depthb.depth) return(-1); + return(0); + } + btAlignedObjectArray m_nodes; + btVector3 m_axis; + }; + static btScalar RandUnit() + { + return(rand()/(btScalar)RAND_MAX); + } + static btVector3 RandVector3() + { + return(btVector3(RandUnit(),RandUnit(),RandUnit())); + } + static btVector3 RandVector3(btScalar cs) + { + return(RandVector3()*cs-btVector3(cs,cs,cs)/2); + } + static btDbvtVolume RandVolume(btScalar cs,btScalar eb,btScalar es) + { + return(btDbvtVolume::FromCE(RandVector3(cs),btVector3(eb,eb,eb)+RandVector3()*es)); + } + static btTransform RandTransform(btScalar cs) + { + btTransform t; + t.setOrigin(RandVector3(cs)); + t.setRotation(btQuaternion(RandUnit()*SIMD_PI*2,RandUnit()*SIMD_PI*2,RandUnit()*SIMD_PI*2).normalized()); + return(t); + } + static void RandTree(btScalar cs,btScalar eb,btScalar es,int leaves,btDbvt& dbvt) + { + dbvt.clear(); + for(int i=0;i volumes; + btAlignedObjectArray results; + volumes.resize(cfgLeaves); + results.resize(cfgLeaves); + for(int i=0;i volumes; + btAlignedObjectArray results; + volumes.resize(cfgLeaves); + results.resize(cfgLeaves); + for(int i=0;i transforms; + btDbvtBenchmark::NilPolicy policy; + transforms.resize(cfgBenchmark5_Iterations); + for(int i=0;i transforms; + btDbvtBenchmark::NilPolicy policy; + transforms.resize(cfgBenchmark6_Iterations); + for(int i=0;i rayorg; + btAlignedObjectArray raydir; + btDbvtBenchmark::NilPolicy policy; + rayorg.resize(cfgBenchmark7_Iterations); + raydir.resize(cfgBenchmark7_Iterations); + for(int i=0;i leaves; + btDbvtBenchmark::RandTree(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale,cfgLeaves,dbvt); + dbvt.optimizeTopDown(); + dbvt.extractLeaves(dbvt.m_root,leaves); + printf("[9] updates (teleport): "); + wallclock.reset(); + for(int i=0;i(leaves[rand()%cfgLeaves]), + btDbvtBenchmark::RandVolume(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale)); + } + } + const int time=(int)wallclock.getTimeMilliseconds(); + const int up=cfgBenchmark9_Passes*cfgBenchmark9_Iterations; + printf("%u ms (%i%%),(%u u/s)\r\n",time,(time-cfgBenchmark9_Reference)*100/time,up*1000/time); + } + if(cfgBenchmark10_Enable) + {// Benchmark 10 + srand(380843); + btDbvt dbvt; + btAlignedObjectArray leaves; + btAlignedObjectArray vectors; + vectors.resize(cfgBenchmark10_Iterations); + for(int i=0;i(leaves[rand()%cfgLeaves]); + btDbvtVolume v=btDbvtVolume::FromMM(l->volume.Mins()+d,l->volume.Maxs()+d); + dbvt.update(l,v); + } + } + const int time=(int)wallclock.getTimeMilliseconds(); + const int up=cfgBenchmark10_Passes*cfgBenchmark10_Iterations; + printf("%u ms (%i%%),(%u u/s)\r\n",time,(time-cfgBenchmark10_Reference)*100/time,up*1000/time); + } + if(cfgBenchmark11_Enable) + {// Benchmark 11 + srand(380843); + btDbvt dbvt; + btDbvtBenchmark::RandTree(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale,cfgLeaves,dbvt); + dbvt.optimizeTopDown(); + printf("[11] optimize (incremental): "); + wallclock.reset(); + for(int i=0;i volumes; + btAlignedObjectArray results; + volumes.resize(cfgLeaves); + results.resize(cfgLeaves); + for(int i=0;i vectors; + btDbvtBenchmark::NilPolicy policy; + vectors.resize(cfgBenchmark13_Iterations); + for(int i=0;i vectors; + btDbvtBenchmark::P14 policy; + vectors.resize(cfgBenchmark14_Iterations); + for(int i=0;i vectors; + btDbvtBenchmark::P15 policy; + vectors.resize(cfgBenchmark15_Iterations); + for(int i=0;i batch; + btDbvtBenchmark::RandTree(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale,cfgLeaves,dbvt); + dbvt.optimizeTopDown(); + batch.reserve(cfgBenchmark16_BatchCount); + printf("[16] insert/remove batch(%u): ",cfgBenchmark16_BatchCount); + wallclock.reset(); + for(int i=0;i volumes; + btAlignedObjectArray results; + btAlignedObjectArray indices; + volumes.resize(cfgLeaves); + results.resize(cfgLeaves); + indices.resize(cfgLeaves); + for(int i=0;i= 1400) +#define DBVT_USE_TEMPLATE 1 +#else +#define DBVT_USE_TEMPLATE 0 +#endif +#else +#define DBVT_USE_TEMPLATE 0 +#endif + +// Use only intrinsics instead of inline asm +#define DBVT_USE_INTRINSIC_SSE 1 + +// Using memmov for collideOCL +#define DBVT_USE_MEMMOVE 1 + +// Enable benchmarking code +#define DBVT_ENABLE_BENCHMARK 0 + +// Inlining +#define DBVT_INLINE SIMD_FORCE_INLINE + +// Specific methods implementation + +//SSE gives errors on a MSVC 7.1 +#if defined (BT_USE_SSE) && defined (WIN32) +#define DBVT_SELECT_IMPL DBVT_IMPL_SSE +#define DBVT_MERGE_IMPL DBVT_IMPL_SSE +#define DBVT_INT0_IMPL DBVT_IMPL_SSE +#else +#define DBVT_SELECT_IMPL DBVT_IMPL_GENERIC +#define DBVT_MERGE_IMPL DBVT_IMPL_GENERIC +#define DBVT_INT0_IMPL DBVT_IMPL_GENERIC +#endif + +#if (DBVT_SELECT_IMPL==DBVT_IMPL_SSE)|| \ + (DBVT_MERGE_IMPL==DBVT_IMPL_SSE)|| \ + (DBVT_INT0_IMPL==DBVT_IMPL_SSE) +#include +#endif + +// +// Auto config and checks +// + +#if DBVT_USE_TEMPLATE +#define DBVT_VIRTUAL +#define DBVT_VIRTUAL_DTOR(a) +#define DBVT_PREFIX template +#define DBVT_IPOLICY T& policy +#define DBVT_CHECKTYPE static const ICollide& typechecker=*(T*)1;(void)typechecker; +#else +#define DBVT_VIRTUAL_DTOR(a) virtual ~a() {} +#define DBVT_VIRTUAL virtual +#define DBVT_PREFIX +#define DBVT_IPOLICY ICollide& policy +#define DBVT_CHECKTYPE +#endif + +#if DBVT_USE_MEMMOVE +#ifndef __CELLOS_LV2__ +#include +#endif +#include +#endif + +#ifndef DBVT_USE_TEMPLATE +#error "DBVT_USE_TEMPLATE undefined" +#endif + +#ifndef DBVT_USE_MEMMOVE +#error "DBVT_USE_MEMMOVE undefined" +#endif + +#ifndef DBVT_ENABLE_BENCHMARK +#error "DBVT_ENABLE_BENCHMARK undefined" +#endif + +#ifndef DBVT_SELECT_IMPL +#error "DBVT_SELECT_IMPL undefined" +#endif + +#ifndef DBVT_MERGE_IMPL +#error "DBVT_MERGE_IMPL undefined" +#endif + +#ifndef DBVT_INT0_IMPL +#error "DBVT_INT0_IMPL undefined" +#endif + +// +// Defaults volumes +// + +/* btDbvtAabbMm */ +struct btDbvtAabbMm +{ + DBVT_INLINE btVector3 Center() const { return((mi+mx)/2); } + DBVT_INLINE btVector3 Lengths() const { return(mx-mi); } + DBVT_INLINE btVector3 Extents() const { return((mx-mi)/2); } + DBVT_INLINE const btVector3& Mins() const { return(mi); } + DBVT_INLINE const btVector3& Maxs() const { return(mx); } + static inline btDbvtAabbMm FromCE(const btVector3& c,const btVector3& e); + static inline btDbvtAabbMm FromCR(const btVector3& c,btScalar r); + static inline btDbvtAabbMm FromMM(const btVector3& mi,const btVector3& mx); + static inline btDbvtAabbMm FromPoints(const btVector3* pts,int n); + static inline btDbvtAabbMm FromPoints(const btVector3** ppts,int n); + DBVT_INLINE void Expand(const btVector3& e); + DBVT_INLINE void SignedExpand(const btVector3& e); + DBVT_INLINE bool Contain(const btDbvtAabbMm& a) const; + DBVT_INLINE int Classify(const btVector3& n,btScalar o,int s) const; + DBVT_INLINE btScalar ProjectMinimum(const btVector3& v,unsigned signs) const; + DBVT_INLINE friend bool Intersect( const btDbvtAabbMm& a, + const btDbvtAabbMm& b); + + DBVT_INLINE friend bool Intersect( const btDbvtAabbMm& a, + const btVector3& b); + + DBVT_INLINE friend btScalar Proximity( const btDbvtAabbMm& a, + const btDbvtAabbMm& b); + DBVT_INLINE friend int Select( const btDbvtAabbMm& o, + const btDbvtAabbMm& a, + const btDbvtAabbMm& b); + DBVT_INLINE friend void Merge( const btDbvtAabbMm& a, + const btDbvtAabbMm& b, + btDbvtAabbMm& r); + DBVT_INLINE friend bool NotEqual( const btDbvtAabbMm& a, + const btDbvtAabbMm& b); +private: + DBVT_INLINE void AddSpan(const btVector3& d,btScalar& smi,btScalar& smx) const; +private: + btVector3 mi,mx; +}; + +// Types +typedef btDbvtAabbMm btDbvtVolume; + +/* btDbvtNode */ +struct btDbvtNode +{ + btDbvtVolume volume; + btDbvtNode* parent; + DBVT_INLINE bool isleaf() const { return(childs[1]==0); } + DBVT_INLINE bool isinternal() const { return(!isleaf()); } + union + { + btDbvtNode* childs[2]; + void* data; + int dataAsInt; + }; +}; + +///The btDbvt class implements a fast dynamic bounding volume tree based on axis aligned bounding boxes (aabb tree). +///This btDbvt is used for soft body collision detection and for the btDbvtBroadphase. It has a fast insert, remove and update of nodes. +///Unlike the btQuantizedBvh, nodes can be dynamically moved around, which allows for change in topology of the underlying data structure. +struct btDbvt +{ + /* Stack element */ + struct sStkNN + { + const btDbvtNode* a; + const btDbvtNode* b; + sStkNN() {} + sStkNN(const btDbvtNode* na,const btDbvtNode* nb) : a(na),b(nb) {} + }; + struct sStkNP + { + const btDbvtNode* node; + int mask; + sStkNP(const btDbvtNode* n,unsigned m) : node(n),mask(m) {} + }; + struct sStkNPS + { + const btDbvtNode* node; + int mask; + btScalar value; + sStkNPS() {} + sStkNPS(const btDbvtNode* n,unsigned m,btScalar v) : node(n),mask(m),value(v) {} + }; + struct sStkCLN + { + const btDbvtNode* node; + btDbvtNode* parent; + sStkCLN(const btDbvtNode* n,btDbvtNode* p) : node(n),parent(p) {} + }; + // Policies/Interfaces + + /* ICollide */ + struct ICollide + { + DBVT_VIRTUAL_DTOR(ICollide) + DBVT_VIRTUAL void Process(const btDbvtNode*,const btDbvtNode*) {} + DBVT_VIRTUAL void Process(const btDbvtNode*) {} + DBVT_VIRTUAL void Process(const btDbvtNode* n,btScalar) { Process(n); } + DBVT_VIRTUAL bool Descent(const btDbvtNode*) { return(true); } + DBVT_VIRTUAL bool AllLeaves(const btDbvtNode*) { return(true); } + }; + /* IWriter */ + struct IWriter + { + virtual ~IWriter() {} + virtual void Prepare(const btDbvtNode* root,int numnodes)=0; + virtual void WriteNode(const btDbvtNode*,int index,int parent,int child0,int child1)=0; + virtual void WriteLeaf(const btDbvtNode*,int index,int parent)=0; + }; + /* IClone */ + struct IClone + { + virtual ~IClone() {} + virtual void CloneLeaf(btDbvtNode*) {} + }; + + // Constants + enum { + SIMPLE_STACKSIZE = 64, + DOUBLE_STACKSIZE = SIMPLE_STACKSIZE*2 + }; + + // Fields + btDbvtNode* m_root; + btDbvtNode* m_free; + int m_lkhd; + int m_leaves; + unsigned m_opath; + + + btAlignedObjectArray m_stkStack; + + + // Methods + btDbvt(); + ~btDbvt(); + void clear(); + bool empty() const { return(0==m_root); } + void optimizeBottomUp(); + void optimizeTopDown(int bu_treshold=128); + void optimizeIncremental(int passes); + btDbvtNode* insert(const btDbvtVolume& box,void* data); + void update(btDbvtNode* leaf,int lookahead=-1); + void update(btDbvtNode* leaf,btDbvtVolume& volume); + bool update(btDbvtNode* leaf,btDbvtVolume& volume,const btVector3& velocity,btScalar margin); + bool update(btDbvtNode* leaf,btDbvtVolume& volume,const btVector3& velocity); + bool update(btDbvtNode* leaf,btDbvtVolume& volume,btScalar margin); + void remove(btDbvtNode* leaf); + void write(IWriter* iwriter) const; + void clone(btDbvt& dest,IClone* iclone=0) const; + static int maxdepth(const btDbvtNode* node); + static int countLeaves(const btDbvtNode* node); + static void extractLeaves(const btDbvtNode* node,btAlignedObjectArray& leaves); +#if DBVT_ENABLE_BENCHMARK + static void benchmark(); +#else + static void benchmark(){} +#endif + // DBVT_IPOLICY must support ICollide policy/interface + DBVT_PREFIX + static void enumNodes( const btDbvtNode* root, + DBVT_IPOLICY); + DBVT_PREFIX + static void enumLeaves( const btDbvtNode* root, + DBVT_IPOLICY); + DBVT_PREFIX + void collideTT( const btDbvtNode* root0, + const btDbvtNode* root1, + DBVT_IPOLICY); + + DBVT_PREFIX + void collideTTpersistentStack( const btDbvtNode* root0, + const btDbvtNode* root1, + DBVT_IPOLICY); +#if 0 + DBVT_PREFIX + void collideTT( const btDbvtNode* root0, + const btDbvtNode* root1, + const btTransform& xform, + DBVT_IPOLICY); + DBVT_PREFIX + void collideTT( const btDbvtNode* root0, + const btTransform& xform0, + const btDbvtNode* root1, + const btTransform& xform1, + DBVT_IPOLICY); +#endif + + DBVT_PREFIX + void collideTV( const btDbvtNode* root, + const btDbvtVolume& volume, + DBVT_IPOLICY); + ///rayTest is a re-entrant ray test, and can be called in parallel as long as the btAlignedAlloc is thread-safe (uses locking etc) + ///rayTest is slower than rayTestInternal, because it builds a local stack, using memory allocations, and it recomputes signs/rayDirectionInverses each time + DBVT_PREFIX + static void rayTest( const btDbvtNode* root, + const btVector3& rayFrom, + const btVector3& rayTo, + DBVT_IPOLICY); + ///rayTestInternal is faster than rayTest, because it uses a persistent stack (to reduce dynamic memory allocations to a minimum) and it uses precomputed signs/rayInverseDirections + ///rayTestInternal is used by btDbvtBroadphase to accelerate world ray casts + DBVT_PREFIX + void rayTestInternal( const btDbvtNode* root, + const btVector3& rayFrom, + const btVector3& rayTo, + const btVector3& rayDirectionInverse, + unsigned int signs[3], + btScalar lambda_max, + const btVector3& aabbMin, + const btVector3& aabbMax, + DBVT_IPOLICY) const; + + DBVT_PREFIX + static void collideKDOP(const btDbvtNode* root, + const btVector3* normals, + const btScalar* offsets, + int count, + DBVT_IPOLICY); + DBVT_PREFIX + static void collideOCL( const btDbvtNode* root, + const btVector3* normals, + const btScalar* offsets, + const btVector3& sortaxis, + int count, + DBVT_IPOLICY, + bool fullsort=true); + DBVT_PREFIX + static void collideTU( const btDbvtNode* root, + DBVT_IPOLICY); + // Helpers + static DBVT_INLINE int nearest(const int* i,const btDbvt::sStkNPS* a,btScalar v,int l,int h) + { + int m=0; + while(l>1; + if(a[i[m]].value>=v) l=m+1; else h=m; + } + return(h); + } + static DBVT_INLINE int allocate( btAlignedObjectArray& ifree, + btAlignedObjectArray& stock, + const sStkNPS& value) + { + int i; + if(ifree.size()>0) + { i=ifree[ifree.size()-1];ifree.pop_back();stock[i]=value; } + else + { i=stock.size();stock.push_back(value); } + return(i); + } + // +private: + btDbvt(const btDbvt&) {} +}; + +// +// Inline's +// + +// +inline btDbvtAabbMm btDbvtAabbMm::FromCE(const btVector3& c,const btVector3& e) +{ + btDbvtAabbMm box; + box.mi=c-e;box.mx=c+e; + return(box); +} + +// +inline btDbvtAabbMm btDbvtAabbMm::FromCR(const btVector3& c,btScalar r) +{ + return(FromCE(c,btVector3(r,r,r))); +} + +// +inline btDbvtAabbMm btDbvtAabbMm::FromMM(const btVector3& mi,const btVector3& mx) +{ + btDbvtAabbMm box; + box.mi=mi;box.mx=mx; + return(box); +} + +// +inline btDbvtAabbMm btDbvtAabbMm::FromPoints(const btVector3* pts,int n) +{ + btDbvtAabbMm box; + box.mi=box.mx=pts[0]; + for(int i=1;i0) mx.setX(mx.x()+e[0]); else mi.setX(mi.x()+e[0]); + if(e.y()>0) mx.setY(mx.y()+e[1]); else mi.setY(mi.y()+e[1]); + if(e.z()>0) mx.setZ(mx.z()+e[2]); else mi.setZ(mi.z()+e[2]); +} + +// +DBVT_INLINE bool btDbvtAabbMm::Contain(const btDbvtAabbMm& a) const +{ + return( (mi.x()<=a.mi.x())&& + (mi.y()<=a.mi.y())&& + (mi.z()<=a.mi.z())&& + (mx.x()>=a.mx.x())&& + (mx.y()>=a.mx.y())&& + (mx.z()>=a.mx.z())); +} + +// +DBVT_INLINE int btDbvtAabbMm::Classify(const btVector3& n,btScalar o,int s) const +{ + btVector3 pi,px; + switch(s) + { + case (0+0+0): px=btVector3(mi.x(),mi.y(),mi.z()); + pi=btVector3(mx.x(),mx.y(),mx.z());break; + case (1+0+0): px=btVector3(mx.x(),mi.y(),mi.z()); + pi=btVector3(mi.x(),mx.y(),mx.z());break; + case (0+2+0): px=btVector3(mi.x(),mx.y(),mi.z()); + pi=btVector3(mx.x(),mi.y(),mx.z());break; + case (1+2+0): px=btVector3(mx.x(),mx.y(),mi.z()); + pi=btVector3(mi.x(),mi.y(),mx.z());break; + case (0+0+4): px=btVector3(mi.x(),mi.y(),mx.z()); + pi=btVector3(mx.x(),mx.y(),mi.z());break; + case (1+0+4): px=btVector3(mx.x(),mi.y(),mx.z()); + pi=btVector3(mi.x(),mx.y(),mi.z());break; + case (0+2+4): px=btVector3(mi.x(),mx.y(),mx.z()); + pi=btVector3(mx.x(),mi.y(),mi.z());break; + case (1+2+4): px=btVector3(mx.x(),mx.y(),mx.z()); + pi=btVector3(mi.x(),mi.y(),mi.z());break; + } + if((btDot(n,px)+o)<0) return(-1); + if((btDot(n,pi)+o)>=0) return(+1); + return(0); +} + +// +DBVT_INLINE btScalar btDbvtAabbMm::ProjectMinimum(const btVector3& v,unsigned signs) const +{ + const btVector3* b[]={&mx,&mi}; + const btVector3 p( b[(signs>>0)&1]->x(), + b[(signs>>1)&1]->y(), + b[(signs>>2)&1]->z()); + return(btDot(p,v)); +} + +// +DBVT_INLINE void btDbvtAabbMm::AddSpan(const btVector3& d,btScalar& smi,btScalar& smx) const +{ + for(int i=0;i<3;++i) + { + if(d[i]<0) + { smi+=mx[i]*d[i];smx+=mi[i]*d[i]; } + else + { smi+=mi[i]*d[i];smx+=mx[i]*d[i]; } + } +} + +// +DBVT_INLINE bool Intersect( const btDbvtAabbMm& a, + const btDbvtAabbMm& b) +{ +#if DBVT_INT0_IMPL == DBVT_IMPL_SSE + const __m128 rt(_mm_or_ps( _mm_cmplt_ps(_mm_load_ps(b.mx),_mm_load_ps(a.mi)), + _mm_cmplt_ps(_mm_load_ps(a.mx),_mm_load_ps(b.mi)))); + const __int32* pu((const __int32*)&rt); + return((pu[0]|pu[1]|pu[2])==0); +#else + return( (a.mi.x()<=b.mx.x())&& + (a.mx.x()>=b.mi.x())&& + (a.mi.y()<=b.mx.y())&& + (a.mx.y()>=b.mi.y())&& + (a.mi.z()<=b.mx.z())&& + (a.mx.z()>=b.mi.z())); +#endif +} + + + +// +DBVT_INLINE bool Intersect( const btDbvtAabbMm& a, + const btVector3& b) +{ + return( (b.x()>=a.mi.x())&& + (b.y()>=a.mi.y())&& + (b.z()>=a.mi.z())&& + (b.x()<=a.mx.x())&& + (b.y()<=a.mx.y())&& + (b.z()<=a.mx.z())); +} + + + + + +////////////////////////////////////// + + +// +DBVT_INLINE btScalar Proximity( const btDbvtAabbMm& a, + const btDbvtAabbMm& b) +{ + const btVector3 d=(a.mi+a.mx)-(b.mi+b.mx); + return(btFabs(d.x())+btFabs(d.y())+btFabs(d.z())); +} + + + +// +DBVT_INLINE int Select( const btDbvtAabbMm& o, + const btDbvtAabbMm& a, + const btDbvtAabbMm& b) +{ +#if DBVT_SELECT_IMPL == DBVT_IMPL_SSE + static ATTRIBUTE_ALIGNED16(const unsigned __int32) mask[]={0x7fffffff,0x7fffffff,0x7fffffff,0x7fffffff}; + ///@todo: the intrinsic version is 11% slower +#if DBVT_USE_INTRINSIC_SSE + + union btSSEUnion ///NOTE: if we use more intrinsics, move btSSEUnion into the LinearMath directory + { + __m128 ssereg; + float floats[4]; + int ints[4]; + }; + + __m128 omi(_mm_load_ps(o.mi)); + omi=_mm_add_ps(omi,_mm_load_ps(o.mx)); + __m128 ami(_mm_load_ps(a.mi)); + ami=_mm_add_ps(ami,_mm_load_ps(a.mx)); + ami=_mm_sub_ps(ami,omi); + ami=_mm_and_ps(ami,_mm_load_ps((const float*)mask)); + __m128 bmi(_mm_load_ps(b.mi)); + bmi=_mm_add_ps(bmi,_mm_load_ps(b.mx)); + bmi=_mm_sub_ps(bmi,omi); + bmi=_mm_and_ps(bmi,_mm_load_ps((const float*)mask)); + __m128 t0(_mm_movehl_ps(ami,ami)); + ami=_mm_add_ps(ami,t0); + ami=_mm_add_ss(ami,_mm_shuffle_ps(ami,ami,1)); + __m128 t1(_mm_movehl_ps(bmi,bmi)); + bmi=_mm_add_ps(bmi,t1); + bmi=_mm_add_ss(bmi,_mm_shuffle_ps(bmi,bmi,1)); + + btSSEUnion tmp; + tmp.ssereg = _mm_cmple_ss(bmi,ami); + return tmp.ints[0]&1; + +#else + ATTRIBUTE_ALIGNED16(__int32 r[1]); + __asm + { + mov eax,o + mov ecx,a + mov edx,b + movaps xmm0,[eax] + movaps xmm5,mask + addps xmm0,[eax+16] + movaps xmm1,[ecx] + movaps xmm2,[edx] + addps xmm1,[ecx+16] + addps xmm2,[edx+16] + subps xmm1,xmm0 + subps xmm2,xmm0 + andps xmm1,xmm5 + andps xmm2,xmm5 + movhlps xmm3,xmm1 + movhlps xmm4,xmm2 + addps xmm1,xmm3 + addps xmm2,xmm4 + pshufd xmm3,xmm1,1 + pshufd xmm4,xmm2,1 + addss xmm1,xmm3 + addss xmm2,xmm4 + cmpless xmm2,xmm1 + movss r,xmm2 + } + return(r[0]&1); +#endif +#else + return(Proximity(o,a)b.mx[i]) r.mx[i]=a.mx[i]; else r.mx[i]=b.mx[i]; + } +#endif +} + +// +DBVT_INLINE bool NotEqual( const btDbvtAabbMm& a, + const btDbvtAabbMm& b) +{ + return( (a.mi.x()!=b.mi.x())|| + (a.mi.y()!=b.mi.y())|| + (a.mi.z()!=b.mi.z())|| + (a.mx.x()!=b.mx.x())|| + (a.mx.y()!=b.mx.y())|| + (a.mx.z()!=b.mx.z())); +} + +// +// Inline's +// + +// +DBVT_PREFIX +inline void btDbvt::enumNodes( const btDbvtNode* root, + DBVT_IPOLICY) +{ + DBVT_CHECKTYPE + policy.Process(root); + if(root->isinternal()) + { + enumNodes(root->childs[0],policy); + enumNodes(root->childs[1],policy); + } +} + +// +DBVT_PREFIX +inline void btDbvt::enumLeaves( const btDbvtNode* root, + DBVT_IPOLICY) +{ + DBVT_CHECKTYPE + if(root->isinternal()) + { + enumLeaves(root->childs[0],policy); + enumLeaves(root->childs[1],policy); + } + else + { + policy.Process(root); + } +} + +// +DBVT_PREFIX +inline void btDbvt::collideTT( const btDbvtNode* root0, + const btDbvtNode* root1, + DBVT_IPOLICY) +{ + DBVT_CHECKTYPE + if(root0&&root1) + { + int depth=1; + int treshold=DOUBLE_STACKSIZE-4; + btAlignedObjectArray stkStack; + stkStack.resize(DOUBLE_STACKSIZE); + stkStack[0]=sStkNN(root0,root1); + do { + sStkNN p=stkStack[--depth]; + if(depth>treshold) + { + stkStack.resize(stkStack.size()*2); + treshold=stkStack.size()-4; + } + if(p.a==p.b) + { + if(p.a->isinternal()) + { + stkStack[depth++]=sStkNN(p.a->childs[0],p.a->childs[0]); + stkStack[depth++]=sStkNN(p.a->childs[1],p.a->childs[1]); + stkStack[depth++]=sStkNN(p.a->childs[0],p.a->childs[1]); + } + } + else if(Intersect(p.a->volume,p.b->volume)) + { + if(p.a->isinternal()) + { + if(p.b->isinternal()) + { + stkStack[depth++]=sStkNN(p.a->childs[0],p.b->childs[0]); + stkStack[depth++]=sStkNN(p.a->childs[1],p.b->childs[0]); + stkStack[depth++]=sStkNN(p.a->childs[0],p.b->childs[1]); + stkStack[depth++]=sStkNN(p.a->childs[1],p.b->childs[1]); + } + else + { + stkStack[depth++]=sStkNN(p.a->childs[0],p.b); + stkStack[depth++]=sStkNN(p.a->childs[1],p.b); + } + } + else + { + if(p.b->isinternal()) + { + stkStack[depth++]=sStkNN(p.a,p.b->childs[0]); + stkStack[depth++]=sStkNN(p.a,p.b->childs[1]); + } + else + { + policy.Process(p.a,p.b); + } + } + } + } while(depth); + } +} + + + +DBVT_PREFIX +inline void btDbvt::collideTTpersistentStack( const btDbvtNode* root0, + const btDbvtNode* root1, + DBVT_IPOLICY) +{ + DBVT_CHECKTYPE + if(root0&&root1) + { + int depth=1; + int treshold=DOUBLE_STACKSIZE-4; + + m_stkStack.resize(DOUBLE_STACKSIZE); + m_stkStack[0]=sStkNN(root0,root1); + do { + sStkNN p=m_stkStack[--depth]; + if(depth>treshold) + { + m_stkStack.resize(m_stkStack.size()*2); + treshold=m_stkStack.size()-4; + } + if(p.a==p.b) + { + if(p.a->isinternal()) + { + m_stkStack[depth++]=sStkNN(p.a->childs[0],p.a->childs[0]); + m_stkStack[depth++]=sStkNN(p.a->childs[1],p.a->childs[1]); + m_stkStack[depth++]=sStkNN(p.a->childs[0],p.a->childs[1]); + } + } + else if(Intersect(p.a->volume,p.b->volume)) + { + if(p.a->isinternal()) + { + if(p.b->isinternal()) + { + m_stkStack[depth++]=sStkNN(p.a->childs[0],p.b->childs[0]); + m_stkStack[depth++]=sStkNN(p.a->childs[1],p.b->childs[0]); + m_stkStack[depth++]=sStkNN(p.a->childs[0],p.b->childs[1]); + m_stkStack[depth++]=sStkNN(p.a->childs[1],p.b->childs[1]); + } + else + { + m_stkStack[depth++]=sStkNN(p.a->childs[0],p.b); + m_stkStack[depth++]=sStkNN(p.a->childs[1],p.b); + } + } + else + { + if(p.b->isinternal()) + { + m_stkStack[depth++]=sStkNN(p.a,p.b->childs[0]); + m_stkStack[depth++]=sStkNN(p.a,p.b->childs[1]); + } + else + { + policy.Process(p.a,p.b); + } + } + } + } while(depth); + } +} + +#if 0 +// +DBVT_PREFIX +inline void btDbvt::collideTT( const btDbvtNode* root0, + const btDbvtNode* root1, + const btTransform& xform, + DBVT_IPOLICY) +{ + DBVT_CHECKTYPE + if(root0&&root1) + { + int depth=1; + int treshold=DOUBLE_STACKSIZE-4; + btAlignedObjectArray stkStack; + stkStack.resize(DOUBLE_STACKSIZE); + stkStack[0]=sStkNN(root0,root1); + do { + sStkNN p=stkStack[--depth]; + if(Intersect(p.a->volume,p.b->volume,xform)) + { + if(depth>treshold) + { + stkStack.resize(stkStack.size()*2); + treshold=stkStack.size()-4; + } + if(p.a->isinternal()) + { + if(p.b->isinternal()) + { + stkStack[depth++]=sStkNN(p.a->childs[0],p.b->childs[0]); + stkStack[depth++]=sStkNN(p.a->childs[1],p.b->childs[0]); + stkStack[depth++]=sStkNN(p.a->childs[0],p.b->childs[1]); + stkStack[depth++]=sStkNN(p.a->childs[1],p.b->childs[1]); + } + else + { + stkStack[depth++]=sStkNN(p.a->childs[0],p.b); + stkStack[depth++]=sStkNN(p.a->childs[1],p.b); + } + } + else + { + if(p.b->isinternal()) + { + stkStack[depth++]=sStkNN(p.a,p.b->childs[0]); + stkStack[depth++]=sStkNN(p.a,p.b->childs[1]); + } + else + { + policy.Process(p.a,p.b); + } + } + } + } while(depth); + } +} +// +DBVT_PREFIX +inline void btDbvt::collideTT( const btDbvtNode* root0, + const btTransform& xform0, + const btDbvtNode* root1, + const btTransform& xform1, + DBVT_IPOLICY) +{ + const btTransform xform=xform0.inverse()*xform1; + collideTT(root0,root1,xform,policy); +} +#endif + +// +DBVT_PREFIX +inline void btDbvt::collideTV( const btDbvtNode* root, + const btDbvtVolume& vol, + DBVT_IPOLICY) +{ + DBVT_CHECKTYPE + if(root) + { + ATTRIBUTE_ALIGNED16(btDbvtVolume) volume(vol); + btAlignedObjectArray stack; + stack.resize(0); + stack.reserve(SIMPLE_STACKSIZE); + stack.push_back(root); + do { + const btDbvtNode* n=stack[stack.size()-1]; + stack.pop_back(); + if(Intersect(n->volume,volume)) + { + if(n->isinternal()) + { + stack.push_back(n->childs[0]); + stack.push_back(n->childs[1]); + } + else + { + policy.Process(n); + } + } + } while(stack.size()>0); + } +} + +DBVT_PREFIX +inline void btDbvt::rayTestInternal( const btDbvtNode* root, + const btVector3& rayFrom, + const btVector3& rayTo, + const btVector3& rayDirectionInverse, + unsigned int signs[3], + btScalar lambda_max, + const btVector3& aabbMin, + const btVector3& aabbMax, + DBVT_IPOLICY) const +{ + DBVT_CHECKTYPE + if(root) + { + btVector3 resultNormal; + + int depth=1; + int treshold=DOUBLE_STACKSIZE-2; + btAlignedObjectArray stack; + stack.resize(DOUBLE_STACKSIZE); + stack[0]=root; + btVector3 bounds[2]; + do + { + const btDbvtNode* node=stack[--depth]; + bounds[0] = node->volume.Mins()+aabbMin; + bounds[1] = node->volume.Maxs()+aabbMax; + btScalar tmin=1.f,lambda_min=0.f; + unsigned int result1=false; + result1 = btRayAabb2(rayFrom,rayDirectionInverse,signs,bounds,tmin,lambda_min,lambda_max); + if(result1) + { + if(node->isinternal()) + { + if(depth>treshold) + { + stack.resize(stack.size()*2); + treshold=stack.size()-2; + } + stack[depth++]=node->childs[0]; + stack[depth++]=node->childs[1]; + } + else + { + policy.Process(node); + } + } + } while(depth); + } +} + +// +DBVT_PREFIX +inline void btDbvt::rayTest( const btDbvtNode* root, + const btVector3& rayFrom, + const btVector3& rayTo, + DBVT_IPOLICY) +{ + DBVT_CHECKTYPE + if(root) + { + btVector3 rayDir = (rayTo-rayFrom); + rayDir.normalize (); + + ///what about division by zero? --> just set rayDirection[i] to INF/BT_LARGE_FLOAT + btVector3 rayDirectionInverse; + rayDirectionInverse[0] = rayDir[0] == btScalar(0.0) ? btScalar(BT_LARGE_FLOAT) : btScalar(1.0) / rayDir[0]; + rayDirectionInverse[1] = rayDir[1] == btScalar(0.0) ? btScalar(BT_LARGE_FLOAT) : btScalar(1.0) / rayDir[1]; + rayDirectionInverse[2] = rayDir[2] == btScalar(0.0) ? btScalar(BT_LARGE_FLOAT) : btScalar(1.0) / rayDir[2]; + unsigned int signs[3] = { rayDirectionInverse[0] < 0.0, rayDirectionInverse[1] < 0.0, rayDirectionInverse[2] < 0.0}; + + btScalar lambda_max = rayDir.dot(rayTo-rayFrom); + + btVector3 resultNormal; + + btAlignedObjectArray stack; + + int depth=1; + int treshold=DOUBLE_STACKSIZE-2; + + stack.resize(DOUBLE_STACKSIZE); + stack[0]=root; + btVector3 bounds[2]; + do { + const btDbvtNode* node=stack[--depth]; + + bounds[0] = node->volume.Mins(); + bounds[1] = node->volume.Maxs(); + + btScalar tmin=1.f,lambda_min=0.f; + unsigned int result1 = btRayAabb2(rayFrom,rayDirectionInverse,signs,bounds,tmin,lambda_min,lambda_max); + +#ifdef COMPARE_BTRAY_AABB2 + btScalar param=1.f; + bool result2 = btRayAabb(rayFrom,rayTo,node->volume.Mins(),node->volume.Maxs(),param,resultNormal); + btAssert(result1 == result2); +#endif //TEST_BTRAY_AABB2 + + if(result1) + { + if(node->isinternal()) + { + if(depth>treshold) + { + stack.resize(stack.size()*2); + treshold=stack.size()-2; + } + stack[depth++]=node->childs[0]; + stack[depth++]=node->childs[1]; + } + else + { + policy.Process(node); + } + } + } while(depth); + + } +} + +// +DBVT_PREFIX +inline void btDbvt::collideKDOP(const btDbvtNode* root, + const btVector3* normals, + const btScalar* offsets, + int count, + DBVT_IPOLICY) +{ + DBVT_CHECKTYPE + if(root) + { + const int inside=(1< stack; + int signs[sizeof(unsigned)*8]; + btAssert(count=0)?1:0)+ + ((normals[i].y()>=0)?2:0)+ + ((normals[i].z()>=0)?4:0); + } + stack.reserve(SIMPLE_STACKSIZE); + stack.push_back(sStkNP(root,0)); + do { + sStkNP se=stack[stack.size()-1]; + bool out=false; + stack.pop_back(); + for(int i=0,j=1;(!out)&&(ivolume.Classify(normals[i],offsets[i],signs[i]); + switch(side) + { + case -1: out=true;break; + case +1: se.mask|=j;break; + } + } + } + if(!out) + { + if((se.mask!=inside)&&(se.node->isinternal())) + { + stack.push_back(sStkNP(se.node->childs[0],se.mask)); + stack.push_back(sStkNP(se.node->childs[1],se.mask)); + } + else + { + if(policy.AllLeaves(se.node)) enumLeaves(se.node,policy); + } + } + } while(stack.size()); + } +} + +// +DBVT_PREFIX +inline void btDbvt::collideOCL( const btDbvtNode* root, + const btVector3* normals, + const btScalar* offsets, + const btVector3& sortaxis, + int count, + DBVT_IPOLICY, + bool fsort) +{ + DBVT_CHECKTYPE + if(root) + { + const unsigned srtsgns=(sortaxis[0]>=0?1:0)+ + (sortaxis[1]>=0?2:0)+ + (sortaxis[2]>=0?4:0); + const int inside=(1< stock; + btAlignedObjectArray ifree; + btAlignedObjectArray stack; + int signs[sizeof(unsigned)*8]; + btAssert(count=0)?1:0)+ + ((normals[i].y()>=0)?2:0)+ + ((normals[i].z()>=0)?4:0); + } + stock.reserve(SIMPLE_STACKSIZE); + stack.reserve(SIMPLE_STACKSIZE); + ifree.reserve(SIMPLE_STACKSIZE); + stack.push_back(allocate(ifree,stock,sStkNPS(root,0,root->volume.ProjectMinimum(sortaxis,srtsgns)))); + do { + const int id=stack[stack.size()-1]; + sStkNPS se=stock[id]; + stack.pop_back();ifree.push_back(id); + if(se.mask!=inside) + { + bool out=false; + for(int i=0,j=1;(!out)&&(ivolume.Classify(normals[i],offsets[i],signs[i]); + switch(side) + { + case -1: out=true;break; + case +1: se.mask|=j;break; + } + } + } + if(out) continue; + } + if(policy.Descent(se.node)) + { + if(se.node->isinternal()) + { + const btDbvtNode* pns[]={ se.node->childs[0],se.node->childs[1]}; + sStkNPS nes[]={ sStkNPS(pns[0],se.mask,pns[0]->volume.ProjectMinimum(sortaxis,srtsgns)), + sStkNPS(pns[1],se.mask,pns[1]->volume.ProjectMinimum(sortaxis,srtsgns))}; + const int q=nes[0].value0)) + { + /* Insert 0 */ + j=nearest(&stack[0],&stock[0],nes[q].value,0,stack.size()); + stack.push_back(0); +#if DBVT_USE_MEMMOVE + memmove(&stack[j+1],&stack[j],sizeof(int)*(stack.size()-j-1)); +#else + for(int k=stack.size()-1;k>j;--k) stack[k]=stack[k-1]; +#endif + stack[j]=allocate(ifree,stock,nes[q]); + /* Insert 1 */ + j=nearest(&stack[0],&stock[0],nes[1-q].value,j,stack.size()); + stack.push_back(0); +#if DBVT_USE_MEMMOVE + memmove(&stack[j+1],&stack[j],sizeof(int)*(stack.size()-j-1)); +#else + for(int k=stack.size()-1;k>j;--k) stack[k]=stack[k-1]; +#endif + stack[j]=allocate(ifree,stock,nes[1-q]); + } + else + { + stack.push_back(allocate(ifree,stock,nes[q])); + stack.push_back(allocate(ifree,stock,nes[1-q])); + } + } + else + { + policy.Process(se.node,se.value); + } + } + } while(stack.size()); + } +} + +// +DBVT_PREFIX +inline void btDbvt::collideTU( const btDbvtNode* root, + DBVT_IPOLICY) +{ + DBVT_CHECKTYPE + if(root) + { + btAlignedObjectArray stack; + stack.reserve(SIMPLE_STACKSIZE); + stack.push_back(root); + do { + const btDbvtNode* n=stack[stack.size()-1]; + stack.pop_back(); + if(policy.Descent(n)) + { + if(n->isinternal()) + { stack.push_back(n->childs[0]);stack.push_back(n->childs[1]); } + else + { policy.Process(n); } + } + } while(stack.size()>0); + } +} + +// +// PP Cleanup +// + +#undef DBVT_USE_MEMMOVE +#undef DBVT_USE_TEMPLATE +#undef DBVT_VIRTUAL_DTOR +#undef DBVT_VIRTUAL +#undef DBVT_PREFIX +#undef DBVT_IPOLICY +#undef DBVT_CHECKTYPE +#undef DBVT_IMPL_GENERIC +#undef DBVT_IMPL_SSE +#undef DBVT_USE_INTRINSIC_SSE +#undef DBVT_SELECT_IMPL +#undef DBVT_MERGE_IMPL +#undef DBVT_INT0_IMPL + +#endif diff --git a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btDbvtBroadphase.cpp b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btDbvtBroadphase.cpp new file mode 100644 index 000000000..1a349a30b --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btDbvtBroadphase.cpp @@ -0,0 +1,728 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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. +*/ + +///btDbvtBroadphase implementation by Nathanael Presson + +#include "btDbvtBroadphase.h" + +// +// Profiling +// + +#if DBVT_BP_PROFILE||DBVT_BP_ENABLE_BENCHMARK +#include +#endif + +#if DBVT_BP_PROFILE +struct ProfileScope +{ + __forceinline ProfileScope(btClock& clock,unsigned long& value) : + m_clock(&clock),m_value(&value),m_base(clock.getTimeMicroseconds()) + { + } + __forceinline ~ProfileScope() + { + (*m_value)+=m_clock->getTimeMicroseconds()-m_base; + } + btClock* m_clock; + unsigned long* m_value; + unsigned long m_base; +}; +#define SPC(_value_) ProfileScope spc_scope(m_clock,_value_) +#else +#define SPC(_value_) +#endif + +// +// Helpers +// + +// +template +static inline void listappend(T* item,T*& list) +{ + item->links[0]=0; + item->links[1]=list; + if(list) list->links[0]=item; + list=item; +} + +// +template +static inline void listremove(T* item,T*& list) +{ + if(item->links[0]) item->links[0]->links[1]=item->links[1]; else list=item->links[1]; + if(item->links[1]) item->links[1]->links[0]=item->links[0]; +} + +// +template +static inline int listcount(T* root) +{ + int n=0; + while(root) { ++n;root=root->links[1]; } + return(n); +} + +// +template +static inline void clear(T& value) +{ + static const struct ZeroDummy : T {} zerodummy; + value=zerodummy; +} + +// +// Colliders +// + +/* Tree collider */ +struct btDbvtTreeCollider : btDbvt::ICollide +{ + btDbvtBroadphase* pbp; + btDbvtProxy* proxy; + btDbvtTreeCollider(btDbvtBroadphase* p) : pbp(p) {} + void Process(const btDbvtNode* na,const btDbvtNode* nb) + { + if(na!=nb) + { + btDbvtProxy* pa=(btDbvtProxy*)na->data; + btDbvtProxy* pb=(btDbvtProxy*)nb->data; +#if DBVT_BP_SORTPAIRS + if(pa->m_uniqueId>pb->m_uniqueId) + btSwap(pa,pb); +#endif + pbp->m_paircache->addOverlappingPair(pa,pb); + ++pbp->m_newpairs; + } + } + void Process(const btDbvtNode* n) + { + Process(n,proxy->leaf); + } +}; + +// +// btDbvtBroadphase +// + +// +btDbvtBroadphase::btDbvtBroadphase(btOverlappingPairCache* paircache) +{ + m_deferedcollide = false; + m_needcleanup = true; + m_releasepaircache = (paircache!=0)?false:true; + m_prediction = 0; + m_stageCurrent = 0; + m_fixedleft = 0; + m_fupdates = 1; + m_dupdates = 0; + m_cupdates = 10; + m_newpairs = 1; + m_updates_call = 0; + m_updates_done = 0; + m_updates_ratio = 0; + m_paircache = paircache? paircache : new(btAlignedAlloc(sizeof(btHashedOverlappingPairCache),16)) btHashedOverlappingPairCache(); + m_gid = 0; + m_pid = 0; + m_cid = 0; + for(int i=0;i<=STAGECOUNT;++i) + { + m_stageRoots[i]=0; + } +#if DBVT_BP_PROFILE + clear(m_profiling); +#endif +} + +// +btDbvtBroadphase::~btDbvtBroadphase() +{ + if(m_releasepaircache) + { + m_paircache->~btOverlappingPairCache(); + btAlignedFree(m_paircache); + } +} + +// +btBroadphaseProxy* btDbvtBroadphase::createProxy( const btVector3& aabbMin, + const btVector3& aabbMax, + int /*shapeType*/, + void* userPtr, + short int collisionFilterGroup, + short int collisionFilterMask, + btDispatcher* /*dispatcher*/, + void* /*multiSapProxy*/) +{ + btDbvtProxy* proxy=new(btAlignedAlloc(sizeof(btDbvtProxy),16)) btDbvtProxy( aabbMin,aabbMax,userPtr, + collisionFilterGroup, + collisionFilterMask); + + btDbvtAabbMm aabb = btDbvtVolume::FromMM(aabbMin,aabbMax); + + //bproxy->aabb = btDbvtVolume::FromMM(aabbMin,aabbMax); + proxy->stage = m_stageCurrent; + proxy->m_uniqueId = ++m_gid; + proxy->leaf = m_sets[0].insert(aabb,proxy); + listappend(proxy,m_stageRoots[m_stageCurrent]); + if(!m_deferedcollide) + { + btDbvtTreeCollider collider(this); + collider.proxy=proxy; + m_sets[0].collideTV(m_sets[0].m_root,aabb,collider); + m_sets[1].collideTV(m_sets[1].m_root,aabb,collider); + } + return(proxy); +} + +// +void btDbvtBroadphase::destroyProxy( btBroadphaseProxy* absproxy, + btDispatcher* dispatcher) +{ + btDbvtProxy* proxy=(btDbvtProxy*)absproxy; + if(proxy->stage==STAGECOUNT) + m_sets[1].remove(proxy->leaf); + else + m_sets[0].remove(proxy->leaf); + listremove(proxy,m_stageRoots[proxy->stage]); + m_paircache->removeOverlappingPairsContainingProxy(proxy,dispatcher); + btAlignedFree(proxy); + m_needcleanup=true; +} + +void btDbvtBroadphase::getAabb(btBroadphaseProxy* absproxy,btVector3& aabbMin, btVector3& aabbMax ) const +{ + btDbvtProxy* proxy=(btDbvtProxy*)absproxy; + aabbMin = proxy->m_aabbMin; + aabbMax = proxy->m_aabbMax; +} + +struct BroadphaseRayTester : btDbvt::ICollide +{ + btBroadphaseRayCallback& m_rayCallback; + BroadphaseRayTester(btBroadphaseRayCallback& orgCallback) + :m_rayCallback(orgCallback) + { + } + void Process(const btDbvtNode* leaf) + { + btDbvtProxy* proxy=(btDbvtProxy*)leaf->data; + m_rayCallback.process(proxy); + } +}; + +void btDbvtBroadphase::rayTest(const btVector3& rayFrom,const btVector3& rayTo, btBroadphaseRayCallback& rayCallback,const btVector3& aabbMin,const btVector3& aabbMax) +{ + BroadphaseRayTester callback(rayCallback); + + m_sets[0].rayTestInternal( m_sets[0].m_root, + rayFrom, + rayTo, + rayCallback.m_rayDirectionInverse, + rayCallback.m_signs, + rayCallback.m_lambda_max, + aabbMin, + aabbMax, + callback); + + m_sets[1].rayTestInternal( m_sets[1].m_root, + rayFrom, + rayTo, + rayCallback.m_rayDirectionInverse, + rayCallback.m_signs, + rayCallback.m_lambda_max, + aabbMin, + aabbMax, + callback); + +} + + +// +void btDbvtBroadphase::setAabb( btBroadphaseProxy* absproxy, + const btVector3& aabbMin, + const btVector3& aabbMax, + btDispatcher* /*dispatcher*/) +{ + btDbvtProxy* proxy=(btDbvtProxy*)absproxy; + ATTRIBUTE_ALIGNED16(btDbvtVolume) aabb=btDbvtVolume::FromMM(aabbMin,aabbMax); +#if DBVT_BP_PREVENTFALSEUPDATE + if(NotEqual(aabb,proxy->leaf->volume)) +#endif + { + bool docollide=false; + if(proxy->stage==STAGECOUNT) + {/* fixed -> dynamic set */ + m_sets[1].remove(proxy->leaf); + proxy->leaf=m_sets[0].insert(aabb,proxy); + docollide=true; + } + else + {/* dynamic set */ + ++m_updates_call; + if(Intersect(proxy->leaf->volume,aabb)) + {/* Moving */ + + const btVector3 delta=aabbMin-proxy->m_aabbMin; + btVector3 velocity(((proxy->m_aabbMax-proxy->m_aabbMin)/2)*m_prediction); + if(delta[0]<0) velocity[0]=-velocity[0]; + if(delta[1]<0) velocity[1]=-velocity[1]; + if(delta[2]<0) velocity[2]=-velocity[2]; + if ( +#ifdef DBVT_BP_MARGIN + m_sets[0].update(proxy->leaf,aabb,velocity,DBVT_BP_MARGIN) +#else + m_sets[0].update(proxy->leaf,aabb,velocity) +#endif + ) + { + ++m_updates_done; + docollide=true; + } + } + else + {/* Teleporting */ + m_sets[0].update(proxy->leaf,aabb); + ++m_updates_done; + docollide=true; + } + } + listremove(proxy,m_stageRoots[proxy->stage]); + proxy->m_aabbMin = aabbMin; + proxy->m_aabbMax = aabbMax; + proxy->stage = m_stageCurrent; + listappend(proxy,m_stageRoots[m_stageCurrent]); + if(docollide) + { + m_needcleanup=true; + if(!m_deferedcollide) + { + btDbvtTreeCollider collider(this); + m_sets[1].collideTTpersistentStack(m_sets[1].m_root,proxy->leaf,collider); + m_sets[0].collideTTpersistentStack(m_sets[0].m_root,proxy->leaf,collider); + } + } + } +} + +// +void btDbvtBroadphase::calculateOverlappingPairs(btDispatcher* dispatcher) +{ + collide(dispatcher); +#if DBVT_BP_PROFILE + if(0==(m_pid%DBVT_BP_PROFILING_RATE)) + { + printf("fixed(%u) dynamics(%u) pairs(%u)\r\n",m_sets[1].m_leaves,m_sets[0].m_leaves,m_paircache->getNumOverlappingPairs()); + unsigned int total=m_profiling.m_total; + if(total<=0) total=1; + printf("ddcollide: %u%% (%uus)\r\n",(50+m_profiling.m_ddcollide*100)/total,m_profiling.m_ddcollide/DBVT_BP_PROFILING_RATE); + printf("fdcollide: %u%% (%uus)\r\n",(50+m_profiling.m_fdcollide*100)/total,m_profiling.m_fdcollide/DBVT_BP_PROFILING_RATE); + printf("cleanup: %u%% (%uus)\r\n",(50+m_profiling.m_cleanup*100)/total,m_profiling.m_cleanup/DBVT_BP_PROFILING_RATE); + printf("total: %uus\r\n",total/DBVT_BP_PROFILING_RATE); + const unsigned long sum=m_profiling.m_ddcollide+ + m_profiling.m_fdcollide+ + m_profiling.m_cleanup; + printf("leaked: %u%% (%uus)\r\n",100-((50+sum*100)/total),(total-sum)/DBVT_BP_PROFILING_RATE); + printf("job counts: %u%%\r\n",(m_profiling.m_jobcount*100)/((m_sets[0].m_leaves+m_sets[1].m_leaves)*DBVT_BP_PROFILING_RATE)); + clear(m_profiling); + m_clock.reset(); + } +#endif + + performDeferredRemoval(dispatcher); + +} + +void btDbvtBroadphase::performDeferredRemoval(btDispatcher* dispatcher) +{ + + if (m_paircache->hasDeferredRemoval()) + { + + btBroadphasePairArray& overlappingPairArray = m_paircache->getOverlappingPairArray(); + + //perform a sort, to find duplicates and to sort 'invalid' pairs to the end + overlappingPairArray.quickSort(btBroadphasePairSortPredicate()); + + int invalidPair = 0; + + + int i; + + btBroadphasePair previousPair; + previousPair.m_pProxy0 = 0; + previousPair.m_pProxy1 = 0; + previousPair.m_algorithm = 0; + + + for (i=0;ileaf->volume,pb->leaf->volume); + + if (hasOverlap) + { + needsRemoval = false; + } else + { + needsRemoval = true; + } + } else + { + //remove duplicate + needsRemoval = true; + //should have no algorithm + btAssert(!pair.m_algorithm); + } + + if (needsRemoval) + { + m_paircache->cleanOverlappingPair(pair,dispatcher); + + pair.m_pProxy0 = 0; + pair.m_pProxy1 = 0; + invalidPair++; + } + + } + + //perform a sort, to sort 'invalid' pairs to the end + overlappingPairArray.quickSort(btBroadphasePairSortPredicate()); + overlappingPairArray.resize(overlappingPairArray.size() - invalidPair); + } +} + +// +void btDbvtBroadphase::collide(btDispatcher* dispatcher) +{ + /*printf("---------------------------------------------------------\n"); + printf("m_sets[0].m_leaves=%d\n",m_sets[0].m_leaves); + printf("m_sets[1].m_leaves=%d\n",m_sets[1].m_leaves); + printf("numPairs = %d\n",getOverlappingPairCache()->getNumOverlappingPairs()); + { + int i; + for (i=0;igetNumOverlappingPairs();i++) + { + printf("pair[%d]=(%d,%d),",i,getOverlappingPairCache()->getOverlappingPairArray()[i].m_pProxy0->getUid(), + getOverlappingPairCache()->getOverlappingPairArray()[i].m_pProxy1->getUid()); + } + printf("\n"); + } +*/ + + + + SPC(m_profiling.m_total); + /* optimize */ + m_sets[0].optimizeIncremental(1+(m_sets[0].m_leaves*m_dupdates)/100); + if(m_fixedleft) + { + const int count=1+(m_sets[1].m_leaves*m_fupdates)/100; + m_sets[1].optimizeIncremental(1+(m_sets[1].m_leaves*m_fupdates)/100); + m_fixedleft=btMax(0,m_fixedleft-count); + } + /* dynamic -> fixed set */ + m_stageCurrent=(m_stageCurrent+1)%STAGECOUNT; + btDbvtProxy* current=m_stageRoots[m_stageCurrent]; + if(current) + { + btDbvtTreeCollider collider(this); + do { + btDbvtProxy* next=current->links[1]; + listremove(current,m_stageRoots[current->stage]); + listappend(current,m_stageRoots[STAGECOUNT]); +#if DBVT_BP_ACCURATESLEEPING + m_paircache->removeOverlappingPairsContainingProxy(current,dispatcher); + collider.proxy=current; + btDbvt::collideTV(m_sets[0].m_root,current->aabb,collider); + btDbvt::collideTV(m_sets[1].m_root,current->aabb,collider); +#endif + m_sets[0].remove(current->leaf); + ATTRIBUTE_ALIGNED16(btDbvtVolume) curAabb=btDbvtVolume::FromMM(current->m_aabbMin,current->m_aabbMax); + current->leaf = m_sets[1].insert(curAabb,current); + current->stage = STAGECOUNT; + current = next; + } while(current); + m_fixedleft=m_sets[1].m_leaves; + m_needcleanup=true; + } + /* collide dynamics */ + { + btDbvtTreeCollider collider(this); + if(m_deferedcollide) + { + SPC(m_profiling.m_fdcollide); + m_sets[0].collideTTpersistentStack(m_sets[0].m_root,m_sets[1].m_root,collider); + } + if(m_deferedcollide) + { + SPC(m_profiling.m_ddcollide); + m_sets[0].collideTTpersistentStack(m_sets[0].m_root,m_sets[0].m_root,collider); + } + } + /* clean up */ + if(m_needcleanup) + { + SPC(m_profiling.m_cleanup); + btBroadphasePairArray& pairs=m_paircache->getOverlappingPairArray(); + if(pairs.size()>0) + { + + int ni=btMin(pairs.size(),btMax(m_newpairs,(pairs.size()*m_cupdates)/100)); + for(int i=0;ileaf->volume,pb->leaf->volume)) + { +#if DBVT_BP_SORTPAIRS + if(pa->m_uniqueId>pb->m_uniqueId) + btSwap(pa,pb); +#endif + m_paircache->removeOverlappingPair(pa,pb,dispatcher); + --ni;--i; + } + } + if(pairs.size()>0) m_cid=(m_cid+ni)%pairs.size(); else m_cid=0; + } + } + ++m_pid; + m_newpairs=1; + m_needcleanup=false; + if(m_updates_call>0) + { m_updates_ratio=m_updates_done/(btScalar)m_updates_call; } + else + { m_updates_ratio=0; } + m_updates_done/=2; + m_updates_call/=2; +} + +// +void btDbvtBroadphase::optimize() +{ + m_sets[0].optimizeTopDown(); + m_sets[1].optimizeTopDown(); +} + +// +btOverlappingPairCache* btDbvtBroadphase::getOverlappingPairCache() +{ + return(m_paircache); +} + +// +const btOverlappingPairCache* btDbvtBroadphase::getOverlappingPairCache() const +{ + return(m_paircache); +} + +// +void btDbvtBroadphase::getBroadphaseAabb(btVector3& aabbMin,btVector3& aabbMax) const +{ + + ATTRIBUTE_ALIGNED16(btDbvtVolume) bounds; + + if(!m_sets[0].empty()) + if(!m_sets[1].empty()) Merge( m_sets[0].m_root->volume, + m_sets[1].m_root->volume,bounds); + else + bounds=m_sets[0].m_root->volume; + else if(!m_sets[1].empty()) bounds=m_sets[1].m_root->volume; + else + bounds=btDbvtVolume::FromCR(btVector3(0,0,0),0); + aabbMin=bounds.Mins(); + aabbMax=bounds.Maxs(); +} + +void btDbvtBroadphase::resetPool(btDispatcher* dispatcher) +{ + + int totalObjects = m_sets[0].m_leaves + m_sets[1].m_leaves; + if (!totalObjects) + { + //reset internal dynamic tree data structures + m_sets[0].clear(); + m_sets[1].clear(); + + m_deferedcollide = false; + m_needcleanup = true; + m_stageCurrent = 0; + m_fixedleft = 0; + m_fupdates = 1; + m_dupdates = 0; + m_cupdates = 10; + m_newpairs = 1; + m_updates_call = 0; + m_updates_done = 0; + m_updates_ratio = 0; + + m_gid = 0; + m_pid = 0; + m_cid = 0; + for(int i=0;i<=STAGECOUNT;++i) + { + m_stageRoots[i]=0; + } + } +} + +// +void btDbvtBroadphase::printStats() +{} + +// +#if DBVT_BP_ENABLE_BENCHMARK + +struct btBroadphaseBenchmark +{ + struct Experiment + { + const char* name; + int object_count; + int update_count; + int spawn_count; + int iterations; + btScalar speed; + btScalar amplitude; + }; + struct Object + { + btVector3 center; + btVector3 extents; + btBroadphaseProxy* proxy; + btScalar time; + void update(btScalar speed,btScalar amplitude,btBroadphaseInterface* pbi) + { + time += speed; + center[0] = btCos(time*(btScalar)2.17)*amplitude+ + btSin(time)*amplitude/2; + center[1] = btCos(time*(btScalar)1.38)*amplitude+ + btSin(time)*amplitude; + center[2] = btSin(time*(btScalar)0.777)*amplitude; + pbi->setAabb(proxy,center-extents,center+extents,0); + } + }; + static int UnsignedRand(int range=RAND_MAX-1) { return(rand()%(range+1)); } + static btScalar UnitRand() { return(UnsignedRand(16384)/(btScalar)16384); } + static void OutputTime(const char* name,btClock& c,unsigned count=0) + { + const unsigned long us=c.getTimeMicroseconds(); + const unsigned long ms=(us+500)/1000; + const btScalar sec=us/(btScalar)(1000*1000); + if(count>0) + printf("%s : %u us (%u ms), %.2f/s\r\n",name,us,ms,count/sec); + else + printf("%s : %u us (%u ms)\r\n",name,us,ms); + } +}; + +void btDbvtBroadphase::benchmark(btBroadphaseInterface* pbi) +{ + static const btBroadphaseBenchmark::Experiment experiments[]= + { + {"1024o.10%",1024,10,0,8192,(btScalar)0.005,(btScalar)100}, + /*{"4096o.10%",4096,10,0,8192,(btScalar)0.005,(btScalar)100}, + {"8192o.10%",8192,10,0,8192,(btScalar)0.005,(btScalar)100},*/ + }; + static const int nexperiments=sizeof(experiments)/sizeof(experiments[0]); + btAlignedObjectArray objects; + btClock wallclock; + /* Begin */ + for(int iexp=0;iexpcenter[0]=btBroadphaseBenchmark::UnitRand()*50; + po->center[1]=btBroadphaseBenchmark::UnitRand()*50; + po->center[2]=btBroadphaseBenchmark::UnitRand()*50; + po->extents[0]=btBroadphaseBenchmark::UnitRand()*2+2; + po->extents[1]=btBroadphaseBenchmark::UnitRand()*2+2; + po->extents[2]=btBroadphaseBenchmark::UnitRand()*2+2; + po->time=btBroadphaseBenchmark::UnitRand()*2000; + po->proxy=pbi->createProxy(po->center-po->extents,po->center+po->extents,0,po,1,1,0,0); + objects.push_back(po); + } + btBroadphaseBenchmark::OutputTime("\tInitialization",wallclock); + /* First update */ + wallclock.reset(); + for(int i=0;iupdate(speed,amplitude,pbi); + } + btBroadphaseBenchmark::OutputTime("\tFirst update",wallclock); + /* Updates */ + wallclock.reset(); + for(int i=0;iupdate(speed,amplitude,pbi); + } + pbi->calculateOverlappingPairs(0); + } + btBroadphaseBenchmark::OutputTime("\tUpdate",wallclock,experiment.iterations); + /* Clean up */ + wallclock.reset(); + for(int i=0;idestroyProxy(objects[i]->proxy,0); + delete objects[i]; + } + objects.resize(0); + btBroadphaseBenchmark::OutputTime("\tRelease",wallclock); + } + +} +#else +void btDbvtBroadphase::benchmark(btBroadphaseInterface*) +{} +#endif + +#if DBVT_BP_PROFILE +#undef SPC +#endif + diff --git a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btDbvtBroadphase.h b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btDbvtBroadphase.h new file mode 100644 index 000000000..5615535c2 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btDbvtBroadphase.h @@ -0,0 +1,135 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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. +*/ + +///btDbvtBroadphase implementation by Nathanael Presson +#ifndef BT_DBVT_BROADPHASE_H +#define BT_DBVT_BROADPHASE_H + +#include "BulletCollision/BroadphaseCollision/btDbvt.h" +#include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h" + +// +// Compile time config +// + +#define DBVT_BP_PROFILE 0 +//#define DBVT_BP_SORTPAIRS 1 +#define DBVT_BP_PREVENTFALSEUPDATE 0 +#define DBVT_BP_ACCURATESLEEPING 0 +#define DBVT_BP_ENABLE_BENCHMARK 0 +#define DBVT_BP_MARGIN (btScalar)0.05 + +#if DBVT_BP_PROFILE +#define DBVT_BP_PROFILING_RATE 256 +#include "LinearMath/btQuickprof.h" +#endif + +// +// btDbvtProxy +// +struct btDbvtProxy : btBroadphaseProxy +{ + /* Fields */ + //btDbvtAabbMm aabb; + btDbvtNode* leaf; + btDbvtProxy* links[2]; + int stage; + /* ctor */ + btDbvtProxy(const btVector3& aabbMin,const btVector3& aabbMax,void* userPtr,short int collisionFilterGroup, short int collisionFilterMask) : + btBroadphaseProxy(aabbMin,aabbMax,userPtr,collisionFilterGroup,collisionFilterMask) + { + links[0]=links[1]=0; + } +}; + +typedef btAlignedObjectArray btDbvtProxyArray; + +///The btDbvtBroadphase implements a broadphase using two dynamic AABB bounding volume hierarchies/trees (see btDbvt). +///One tree is used for static/non-moving objects, and another tree is used for dynamic objects. Objects can move from one tree to the other. +///This is a very fast broadphase, especially for very dynamic worlds where many objects are moving. Its insert/add and remove of objects is generally faster than the sweep and prune broadphases btAxisSweep3 and bt32BitAxisSweep3. +struct btDbvtBroadphase : btBroadphaseInterface +{ + /* Config */ + enum { + DYNAMIC_SET = 0, /* Dynamic set index */ + FIXED_SET = 1, /* Fixed set index */ + STAGECOUNT = 2 /* Number of stages */ + }; + /* Fields */ + btDbvt m_sets[2]; // Dbvt sets + btDbvtProxy* m_stageRoots[STAGECOUNT+1]; // Stages list + btOverlappingPairCache* m_paircache; // Pair cache + btScalar m_prediction; // Velocity prediction + int m_stageCurrent; // Current stage + int m_fupdates; // % of fixed updates per frame + int m_dupdates; // % of dynamic updates per frame + int m_cupdates; // % of cleanup updates per frame + int m_newpairs; // Number of pairs created + int m_fixedleft; // Fixed optimization left + unsigned m_updates_call; // Number of updates call + unsigned m_updates_done; // Number of updates done + btScalar m_updates_ratio; // m_updates_done/m_updates_call + int m_pid; // Parse id + int m_cid; // Cleanup index + int m_gid; // Gen id + bool m_releasepaircache; // Release pair cache on delete + bool m_deferedcollide; // Defere dynamic/static collision to collide call + bool m_needcleanup; // Need to run cleanup? +#if DBVT_BP_PROFILE + btClock m_clock; + struct { + unsigned long m_total; + unsigned long m_ddcollide; + unsigned long m_fdcollide; + unsigned long m_cleanup; + unsigned long m_jobcount; + } m_profiling; +#endif + /* Methods */ + btDbvtBroadphase(btOverlappingPairCache* paircache=0); + ~btDbvtBroadphase(); + void collide(btDispatcher* dispatcher); + void optimize(); + /* btBroadphaseInterface Implementation */ + btBroadphaseProxy* createProxy(const btVector3& aabbMin,const btVector3& aabbMax,int shapeType,void* userPtr,short int collisionFilterGroup,short int collisionFilterMask,btDispatcher* dispatcher,void* multiSapProxy); + void destroyProxy(btBroadphaseProxy* proxy,btDispatcher* dispatcher); + void setAabb(btBroadphaseProxy* proxy,const btVector3& aabbMin,const btVector3& aabbMax,btDispatcher* dispatcher); + virtual void rayTest(const btVector3& rayFrom,const btVector3& rayTo, btBroadphaseRayCallback& rayCallback, const btVector3& aabbMin=btVector3(0,0,0), const btVector3& aabbMax = btVector3(0,0,0)); + + virtual void getAabb(btBroadphaseProxy* proxy,btVector3& aabbMin, btVector3& aabbMax ) const; + void calculateOverlappingPairs(btDispatcher* dispatcher); + btOverlappingPairCache* getOverlappingPairCache(); + const btOverlappingPairCache* getOverlappingPairCache() const; + void getBroadphaseAabb(btVector3& aabbMin,btVector3& aabbMax) const; + void printStats(); + static void benchmark(btBroadphaseInterface*); + + void setVelocityPrediction(btScalar prediction) + { + m_prediction = prediction; + } + btScalar getVelocityPrediction() const + { + return m_prediction; + } + + void performDeferredRemoval(btDispatcher* dispatcher); + + ///reset broadphase internal structures, to ensure determinism/reproducability + virtual void resetPool(btDispatcher* dispatcher); + +}; + +#endif diff --git a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btDispatcher.cpp b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btDispatcher.cpp new file mode 100644 index 000000000..20768225b --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btDispatcher.cpp @@ -0,0 +1,22 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btDispatcher.h" + +btDispatcher::~btDispatcher() +{ + +} + diff --git a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btDispatcher.h b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btDispatcher.h new file mode 100644 index 000000000..699c66b82 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btDispatcher.h @@ -0,0 +1,106 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 _DISPATCHER_H +#define _DISPATCHER_H + +#include "LinearMath/btScalar.h" + +class btCollisionAlgorithm; +struct btBroadphaseProxy; +class btRigidBody; +class btCollisionObject; +class btOverlappingPairCache; + + +class btPersistentManifold; +class btStackAlloc; + +struct btDispatcherInfo +{ + enum DispatchFunc + { + DISPATCH_DISCRETE = 1, + DISPATCH_CONTINUOUS + }; + btDispatcherInfo() + :m_timeStep(btScalar(0.)), + m_stepCount(0), + m_dispatchFunc(DISPATCH_DISCRETE), + m_timeOfImpact(btScalar(1.)), + m_useContinuous(false), + m_debugDraw(0), + m_enableSatConvex(false), + m_enableSPU(true), + m_useEpa(true), + m_allowedCcdPenetration(btScalar(0.04)), + m_useConvexConservativeDistanceUtil(false), + m_convexConservativeDistanceThreshold(0.0f), + m_stackAllocator(0) + { + + } + btScalar m_timeStep; + int m_stepCount; + int m_dispatchFunc; + mutable btScalar m_timeOfImpact; + bool m_useContinuous; + class btIDebugDraw* m_debugDraw; + bool m_enableSatConvex; + bool m_enableSPU; + bool m_useEpa; + btScalar m_allowedCcdPenetration; + bool m_useConvexConservativeDistanceUtil; + btScalar m_convexConservativeDistanceThreshold; + btStackAlloc* m_stackAllocator; +}; + +///The btDispatcher interface class can be used in combination with broadphase to dispatch calculations for overlapping pairs. +///For example for pairwise collision detection, calculating contact points stored in btPersistentManifold or user callbacks (game logic). +class btDispatcher +{ + + +public: + virtual ~btDispatcher() ; + + virtual btCollisionAlgorithm* findAlgorithm(btCollisionObject* body0,btCollisionObject* body1,btPersistentManifold* sharedManifold=0) = 0; + + virtual btPersistentManifold* getNewManifold(void* body0,void* body1)=0; + + virtual void releaseManifold(btPersistentManifold* manifold)=0; + + virtual void clearManifold(btPersistentManifold* manifold)=0; + + virtual bool needsCollision(btCollisionObject* body0,btCollisionObject* body1) = 0; + + virtual bool needsResponse(btCollisionObject* body0,btCollisionObject* body1)=0; + + virtual void dispatchAllCollisionPairs(btOverlappingPairCache* pairCache,const btDispatcherInfo& dispatchInfo,btDispatcher* dispatcher) =0; + + virtual int getNumManifolds() const = 0; + + virtual btPersistentManifold* getManifoldByIndexInternal(int index) = 0; + + virtual btPersistentManifold** getInternalManifoldPointer() = 0; + + virtual void* allocateCollisionAlgorithm(int size) = 0; + + virtual void freeCollisionAlgorithm(void* ptr) = 0; + +}; + + +#endif //_DISPATCHER_H diff --git a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btMultiSapBroadphase.cpp b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btMultiSapBroadphase.cpp new file mode 100644 index 000000000..6712f528e --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btMultiSapBroadphase.cpp @@ -0,0 +1,489 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btMultiSapBroadphase.h" + +#include "btSimpleBroadphase.h" +#include "LinearMath/btAabbUtil2.h" +#include "btQuantizedBvh.h" + +/// btSapBroadphaseArray m_sapBroadphases; + +/// btOverlappingPairCache* m_overlappingPairs; +extern int gOverlappingPairs; + +/* +class btMultiSapSortedOverlappingPairCache : public btSortedOverlappingPairCache +{ +public: + + virtual btBroadphasePair* addOverlappingPair(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1) + { + return btSortedOverlappingPairCache::addOverlappingPair((btBroadphaseProxy*)proxy0->m_multiSapParentProxy,(btBroadphaseProxy*)proxy1->m_multiSapParentProxy); + } +}; + +*/ + +btMultiSapBroadphase::btMultiSapBroadphase(int /*maxProxies*/,btOverlappingPairCache* pairCache) +:m_overlappingPairs(pairCache), +m_optimizedAabbTree(0), +m_ownsPairCache(false), +m_invalidPair(0) +{ + if (!m_overlappingPairs) + { + m_ownsPairCache = true; + void* mem = btAlignedAlloc(sizeof(btSortedOverlappingPairCache),16); + m_overlappingPairs = new (mem)btSortedOverlappingPairCache(); + } + + struct btMultiSapOverlapFilterCallback : public btOverlapFilterCallback + { + virtual ~btMultiSapOverlapFilterCallback() + {} + // return true when pairs need collision + virtual bool needBroadphaseCollision(btBroadphaseProxy* childProxy0,btBroadphaseProxy* childProxy1) const + { + btBroadphaseProxy* multiProxy0 = (btBroadphaseProxy*)childProxy0->m_multiSapParentProxy; + btBroadphaseProxy* multiProxy1 = (btBroadphaseProxy*)childProxy1->m_multiSapParentProxy; + + bool collides = (multiProxy0->m_collisionFilterGroup & multiProxy1->m_collisionFilterMask) != 0; + collides = collides && (multiProxy1->m_collisionFilterGroup & multiProxy0->m_collisionFilterMask); + + return collides; + } + }; + + void* mem = btAlignedAlloc(sizeof(btMultiSapOverlapFilterCallback),16); + m_filterCallback = new (mem)btMultiSapOverlapFilterCallback(); + + m_overlappingPairs->setOverlapFilterCallback(m_filterCallback); +// mem = btAlignedAlloc(sizeof(btSimpleBroadphase),16); +// m_simpleBroadphase = new (mem) btSimpleBroadphase(maxProxies,m_overlappingPairs); +} + +btMultiSapBroadphase::~btMultiSapBroadphase() +{ + if (m_ownsPairCache) + { + m_overlappingPairs->~btOverlappingPairCache(); + btAlignedFree(m_overlappingPairs); + } +} + + +void btMultiSapBroadphase::buildTree(const btVector3& bvhAabbMin,const btVector3& bvhAabbMax) +{ + m_optimizedAabbTree = new btQuantizedBvh(); + m_optimizedAabbTree->setQuantizationValues(bvhAabbMin,bvhAabbMax); + QuantizedNodeArray& nodes = m_optimizedAabbTree->getLeafNodeArray(); + for (int i=0;igetBroadphaseAabb(aabbMin,aabbMax); + m_optimizedAabbTree->quantize(&node.m_quantizedAabbMin[0],aabbMin,0); + m_optimizedAabbTree->quantize(&node.m_quantizedAabbMax[0],aabbMax,1); + int partId = 0; + node.m_escapeIndexOrTriangleIndex = (partId<<(31-MAX_NUM_PARTS_IN_BITS)) | i; + nodes.push_back(node); + } + m_optimizedAabbTree->buildInternal(); +} + +btBroadphaseProxy* btMultiSapBroadphase::createProxy( const btVector3& aabbMin, const btVector3& aabbMax,int shapeType,void* userPtr, short int collisionFilterGroup,short int collisionFilterMask, btDispatcher* dispatcher,void* /*ignoreMe*/) +{ + //void* ignoreMe -> we could think of recursive multi-sap, if someone is interested + + void* mem = btAlignedAlloc(sizeof(btMultiSapProxy),16); + btMultiSapProxy* proxy = new (mem)btMultiSapProxy(aabbMin, aabbMax,shapeType,userPtr, collisionFilterGroup,collisionFilterMask); + m_multiSapProxies.push_back(proxy); + + ///this should deal with inserting/removal into child broadphases + setAabb(proxy,aabbMin,aabbMax,dispatcher); + return proxy; +} + +void btMultiSapBroadphase::destroyProxy(btBroadphaseProxy* /*proxy*/,btDispatcher* /*dispatcher*/) +{ + ///not yet + btAssert(0); + +} + + +void btMultiSapBroadphase::addToChildBroadphase(btMultiSapProxy* parentMultiSapProxy, btBroadphaseProxy* childProxy, btBroadphaseInterface* childBroadphase) +{ + void* mem = btAlignedAlloc(sizeof(btBridgeProxy),16); + btBridgeProxy* bridgeProxyRef = new(mem) btBridgeProxy; + bridgeProxyRef->m_childProxy = childProxy; + bridgeProxyRef->m_childBroadphase = childBroadphase; + parentMultiSapProxy->m_bridgeProxies.push_back(bridgeProxyRef); +} + + +bool boxIsContainedWithinBox(const btVector3& amin,const btVector3& amax,const btVector3& bmin,const btVector3& bmax); +bool boxIsContainedWithinBox(const btVector3& amin,const btVector3& amax,const btVector3& bmin,const btVector3& bmax) +{ +return +amin.getX() >= bmin.getX() && amax.getX() <= bmax.getX() && +amin.getY() >= bmin.getY() && amax.getY() <= bmax.getY() && +amin.getZ() >= bmin.getZ() && amax.getZ() <= bmax.getZ(); +} + + + + + + +void btMultiSapBroadphase::getAabb(btBroadphaseProxy* proxy,btVector3& aabbMin, btVector3& aabbMax ) const +{ + btMultiSapProxy* multiProxy = static_cast(proxy); + aabbMin = multiProxy->m_aabbMin; + aabbMax = multiProxy->m_aabbMax; +} + +void btMultiSapBroadphase::rayTest(const btVector3& rayFrom,const btVector3& rayTo, btBroadphaseRayCallback& rayCallback, const btVector3& aabbMin,const btVector3& aabbMax) +{ + for (int i=0;i + +void btMultiSapBroadphase::setAabb(btBroadphaseProxy* proxy,const btVector3& aabbMin,const btVector3& aabbMax, btDispatcher* dispatcher) +{ + btMultiSapProxy* multiProxy = static_cast(proxy); + multiProxy->m_aabbMin = aabbMin; + multiProxy->m_aabbMax = aabbMax; + + +// bool fullyContained = false; +// bool alreadyInSimple = false; + + + + + struct MyNodeOverlapCallback : public btNodeOverlapCallback + { + btMultiSapBroadphase* m_multiSap; + btMultiSapProxy* m_multiProxy; + btDispatcher* m_dispatcher; + + MyNodeOverlapCallback(btMultiSapBroadphase* multiSap,btMultiSapProxy* multiProxy,btDispatcher* dispatcher) + :m_multiSap(multiSap), + m_multiProxy(multiProxy), + m_dispatcher(dispatcher) + { + + } + + virtual void processNode(int /*nodeSubPart*/, int broadphaseIndex) + { + btBroadphaseInterface* childBroadphase = m_multiSap->getBroadphaseArray()[broadphaseIndex]; + + int containingBroadphaseIndex = -1; + //already found? + for (int i=0;im_bridgeProxies.size();i++) + { + + if (m_multiProxy->m_bridgeProxies[i]->m_childBroadphase == childBroadphase) + { + containingBroadphaseIndex = i; + break; + } + } + if (containingBroadphaseIndex<0) + { + //add it + btBroadphaseProxy* childProxy = childBroadphase->createProxy(m_multiProxy->m_aabbMin,m_multiProxy->m_aabbMax,m_multiProxy->m_shapeType,m_multiProxy->m_clientObject,m_multiProxy->m_collisionFilterGroup,m_multiProxy->m_collisionFilterMask, m_dispatcher,m_multiProxy); + m_multiSap->addToChildBroadphase(m_multiProxy,childProxy,childBroadphase); + + } + } + }; + + MyNodeOverlapCallback myNodeCallback(this,multiProxy,dispatcher); + + + + + if (m_optimizedAabbTree) + m_optimizedAabbTree->reportAabbOverlappingNodex(&myNodeCallback,aabbMin,aabbMax); + + int i; + + for ( i=0;im_bridgeProxies.size();i++) + { + btVector3 worldAabbMin,worldAabbMax; + multiProxy->m_bridgeProxies[i]->m_childBroadphase->getBroadphaseAabb(worldAabbMin,worldAabbMax); + bool overlapsBroadphase = TestAabbAgainstAabb2(worldAabbMin,worldAabbMax,multiProxy->m_aabbMin,multiProxy->m_aabbMax); + if (!overlapsBroadphase) + { + //remove it now + btBridgeProxy* bridgeProxy = multiProxy->m_bridgeProxies[i]; + + btBroadphaseProxy* childProxy = bridgeProxy->m_childProxy; + bridgeProxy->m_childBroadphase->destroyProxy(childProxy,dispatcher); + + multiProxy->m_bridgeProxies.swap( i,multiProxy->m_bridgeProxies.size()-1); + multiProxy->m_bridgeProxies.pop_back(); + + } + } + + + /* + + if (1) + { + + //find broadphase that contain this multiProxy + int numChildBroadphases = getBroadphaseArray().size(); + for (int i=0;igetBroadphaseAabb(worldAabbMin,worldAabbMax); + bool overlapsBroadphase = TestAabbAgainstAabb2(worldAabbMin,worldAabbMax,multiProxy->m_aabbMin,multiProxy->m_aabbMax); + + // fullyContained = fullyContained || boxIsContainedWithinBox(worldAabbMin,worldAabbMax,multiProxy->m_aabbMin,multiProxy->m_aabbMax); + int containingBroadphaseIndex = -1; + + //if already contains this + + for (int i=0;im_bridgeProxies.size();i++) + { + if (multiProxy->m_bridgeProxies[i]->m_childBroadphase == childBroadphase) + { + containingBroadphaseIndex = i; + } + alreadyInSimple = alreadyInSimple || (multiProxy->m_bridgeProxies[i]->m_childBroadphase == m_simpleBroadphase); + } + + if (overlapsBroadphase) + { + if (containingBroadphaseIndex<0) + { + btBroadphaseProxy* childProxy = childBroadphase->createProxy(aabbMin,aabbMax,multiProxy->m_shapeType,multiProxy->m_clientObject,multiProxy->m_collisionFilterGroup,multiProxy->m_collisionFilterMask, dispatcher); + childProxy->m_multiSapParentProxy = multiProxy; + addToChildBroadphase(multiProxy,childProxy,childBroadphase); + } + } else + { + if (containingBroadphaseIndex>=0) + { + //remove + btBridgeProxy* bridgeProxy = multiProxy->m_bridgeProxies[containingBroadphaseIndex]; + + btBroadphaseProxy* childProxy = bridgeProxy->m_childProxy; + bridgeProxy->m_childBroadphase->destroyProxy(childProxy,dispatcher); + + multiProxy->m_bridgeProxies.swap( containingBroadphaseIndex,multiProxy->m_bridgeProxies.size()-1); + multiProxy->m_bridgeProxies.pop_back(); + } + } + } + + + ///If we are in no other child broadphase, stick the proxy in the global 'simple' broadphase (brute force) + ///hopefully we don't end up with many entries here (can assert/provide feedback on stats) + if (0)//!multiProxy->m_bridgeProxies.size()) + { + ///we don't pass the userPtr but our multisap proxy. We need to patch this, before processing an actual collision + ///this is needed to be able to calculate the aabb overlap + btBroadphaseProxy* childProxy = m_simpleBroadphase->createProxy(aabbMin,aabbMax,multiProxy->m_shapeType,multiProxy->m_clientObject,multiProxy->m_collisionFilterGroup,multiProxy->m_collisionFilterMask, dispatcher); + childProxy->m_multiSapParentProxy = multiProxy; + addToChildBroadphase(multiProxy,childProxy,m_simpleBroadphase); + } + } + + if (!multiProxy->m_bridgeProxies.size()) + { + ///we don't pass the userPtr but our multisap proxy. We need to patch this, before processing an actual collision + ///this is needed to be able to calculate the aabb overlap + btBroadphaseProxy* childProxy = m_simpleBroadphase->createProxy(aabbMin,aabbMax,multiProxy->m_shapeType,multiProxy->m_clientObject,multiProxy->m_collisionFilterGroup,multiProxy->m_collisionFilterMask, dispatcher); + childProxy->m_multiSapParentProxy = multiProxy; + addToChildBroadphase(multiProxy,childProxy,m_simpleBroadphase); + } +*/ + + + //update + for ( i=0;im_bridgeProxies.size();i++) + { + btBridgeProxy* bridgeProxyRef = multiProxy->m_bridgeProxies[i]; + bridgeProxyRef->m_childBroadphase->setAabb(bridgeProxyRef->m_childProxy,aabbMin,aabbMax,dispatcher); + } + +} +bool stopUpdating=false; + + + +class btMultiSapBroadphasePairSortPredicate +{ + public: + + bool operator() ( const btBroadphasePair& a1, const btBroadphasePair& b1 ) + { + btMultiSapBroadphase::btMultiSapProxy* aProxy0 = a1.m_pProxy0 ? (btMultiSapBroadphase::btMultiSapProxy*)a1.m_pProxy0->m_multiSapParentProxy : 0; + btMultiSapBroadphase::btMultiSapProxy* aProxy1 = a1.m_pProxy1 ? (btMultiSapBroadphase::btMultiSapProxy*)a1.m_pProxy1->m_multiSapParentProxy : 0; + btMultiSapBroadphase::btMultiSapProxy* bProxy0 = b1.m_pProxy0 ? (btMultiSapBroadphase::btMultiSapProxy*)b1.m_pProxy0->m_multiSapParentProxy : 0; + btMultiSapBroadphase::btMultiSapProxy* bProxy1 = b1.m_pProxy1 ? (btMultiSapBroadphase::btMultiSapProxy*)b1.m_pProxy1->m_multiSapParentProxy : 0; + + return aProxy0 > bProxy0 || + (aProxy0 == bProxy0 && aProxy1 > bProxy1) || + (aProxy0 == bProxy0 && aProxy1 == bProxy1 && a1.m_algorithm > b1.m_algorithm); + } +}; + + + ///calculateOverlappingPairs is optional: incremental algorithms (sweep and prune) might do it during the set aabb +void btMultiSapBroadphase::calculateOverlappingPairs(btDispatcher* dispatcher) +{ + +// m_simpleBroadphase->calculateOverlappingPairs(dispatcher); + + if (!stopUpdating && getOverlappingPairCache()->hasDeferredRemoval()) + { + + btBroadphasePairArray& overlappingPairArray = getOverlappingPairCache()->getOverlappingPairArray(); + + // quicksort(overlappingPairArray,0,overlappingPairArray.size()); + + overlappingPairArray.quickSort(btMultiSapBroadphasePairSortPredicate()); + + //perform a sort, to find duplicates and to sort 'invalid' pairs to the end + // overlappingPairArray.heapSort(btMultiSapBroadphasePairSortPredicate()); + + overlappingPairArray.resize(overlappingPairArray.size() - m_invalidPair); + m_invalidPair = 0; + + + int i; + + btBroadphasePair previousPair; + previousPair.m_pProxy0 = 0; + previousPair.m_pProxy1 = 0; + previousPair.m_algorithm = 0; + + + for (i=0;im_multiSapParentProxy : 0; + btMultiSapProxy* aProxy1 = pair.m_pProxy1 ? (btMultiSapProxy*)pair.m_pProxy1->m_multiSapParentProxy : 0; + btMultiSapProxy* bProxy0 = previousPair.m_pProxy0 ? (btMultiSapProxy*)previousPair.m_pProxy0->m_multiSapParentProxy : 0; + btMultiSapProxy* bProxy1 = previousPair.m_pProxy1 ? (btMultiSapProxy*)previousPair.m_pProxy1->m_multiSapParentProxy : 0; + + bool isDuplicate = (aProxy0 == bProxy0) && (aProxy1 == bProxy1); + + previousPair = pair; + + bool needsRemoval = false; + + if (!isDuplicate) + { + bool hasOverlap = testAabbOverlap(pair.m_pProxy0,pair.m_pProxy1); + + if (hasOverlap) + { + needsRemoval = false;//callback->processOverlap(pair); + } else + { + needsRemoval = true; + } + } else + { + //remove duplicate + needsRemoval = true; + //should have no algorithm + btAssert(!pair.m_algorithm); + } + + if (needsRemoval) + { + getOverlappingPairCache()->cleanOverlappingPair(pair,dispatcher); + + // m_overlappingPairArray.swap(i,m_overlappingPairArray.size()-1); + // m_overlappingPairArray.pop_back(); + pair.m_pProxy0 = 0; + pair.m_pProxy1 = 0; + m_invalidPair++; + gOverlappingPairs--; + } + + } + + ///if you don't like to skip the invalid pairs in the array, execute following code: + #define CLEAN_INVALID_PAIRS 1 + #ifdef CLEAN_INVALID_PAIRS + + //perform a sort, to sort 'invalid' pairs to the end + //overlappingPairArray.heapSort(btMultiSapBroadphasePairSortPredicate()); + overlappingPairArray.quickSort(btMultiSapBroadphasePairSortPredicate()); + + overlappingPairArray.resize(overlappingPairArray.size() - m_invalidPair); + m_invalidPair = 0; + #endif//CLEAN_INVALID_PAIRS + + //printf("overlappingPairArray.size()=%d\n",overlappingPairArray.size()); + } + + +} + + +bool btMultiSapBroadphase::testAabbOverlap(btBroadphaseProxy* childProxy0,btBroadphaseProxy* childProxy1) +{ + btMultiSapProxy* multiSapProxy0 = (btMultiSapProxy*)childProxy0->m_multiSapParentProxy; + btMultiSapProxy* multiSapProxy1 = (btMultiSapProxy*)childProxy1->m_multiSapParentProxy; + + return TestAabbAgainstAabb2(multiSapProxy0->m_aabbMin,multiSapProxy0->m_aabbMax, + multiSapProxy1->m_aabbMin,multiSapProxy1->m_aabbMax); + +} + + +void btMultiSapBroadphase::printStats() +{ +/* printf("---------------------------------\n"); + + printf("btMultiSapBroadphase.h\n"); + printf("numHandles = %d\n",m_multiSapProxies.size()); + //find broadphase that contain this multiProxy + int numChildBroadphases = getBroadphaseArray().size(); + for (int i=0;iprintStats(); + + } + */ + +} + +void btMultiSapBroadphase::resetPool(btDispatcher* dispatcher) +{ + // not yet +} diff --git a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btMultiSapBroadphase.h b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btMultiSapBroadphase.h new file mode 100644 index 000000000..7bcfe6b13 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btMultiSapBroadphase.h @@ -0,0 +1,151 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 BT_MULTI_SAP_BROADPHASE +#define BT_MULTI_SAP_BROADPHASE + +#include "btBroadphaseInterface.h" +#include "LinearMath/btAlignedObjectArray.h" +#include "btOverlappingPairCache.h" + + +class btBroadphaseInterface; +class btSimpleBroadphase; + + +typedef btAlignedObjectArray btSapBroadphaseArray; + +///The btMultiSapBroadphase is a research project, not recommended to use in production. Use btAxisSweep3 or btDbvtBroadphase instead. +///The btMultiSapBroadphase is a broadphase that contains multiple SAP broadphases. +///The user can add SAP broadphases that cover the world. A btBroadphaseProxy can be in multiple child broadphases at the same time. +///A btQuantizedBvh acceleration structures finds overlapping SAPs for each btBroadphaseProxy. +///See http://www.continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=328 +///and http://www.continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=1329 +class btMultiSapBroadphase :public btBroadphaseInterface +{ + btSapBroadphaseArray m_sapBroadphases; + + btSimpleBroadphase* m_simpleBroadphase; + + btOverlappingPairCache* m_overlappingPairs; + + class btQuantizedBvh* m_optimizedAabbTree; + + + bool m_ownsPairCache; + + btOverlapFilterCallback* m_filterCallback; + + int m_invalidPair; + + struct btBridgeProxy + { + btBroadphaseProxy* m_childProxy; + btBroadphaseInterface* m_childBroadphase; + }; + + +public: + + struct btMultiSapProxy : public btBroadphaseProxy + { + + ///array with all the entries that this proxy belongs to + btAlignedObjectArray m_bridgeProxies; + btVector3 m_aabbMin; + btVector3 m_aabbMax; + + int m_shapeType; + +/* void* m_userPtr; + short int m_collisionFilterGroup; + short int m_collisionFilterMask; +*/ + btMultiSapProxy(const btVector3& aabbMin, const btVector3& aabbMax,int shapeType,void* userPtr, short int collisionFilterGroup,short int collisionFilterMask) + :btBroadphaseProxy(aabbMin,aabbMax,userPtr,collisionFilterGroup,collisionFilterMask), + m_aabbMin(aabbMin), + m_aabbMax(aabbMax), + m_shapeType(shapeType) + { + m_multiSapParentProxy =this; + } + + + }; + +protected: + + + btAlignedObjectArray m_multiSapProxies; + +public: + + btMultiSapBroadphase(int maxProxies = 16384,btOverlappingPairCache* pairCache=0); + + + btSapBroadphaseArray& getBroadphaseArray() + { + return m_sapBroadphases; + } + + const btSapBroadphaseArray& getBroadphaseArray() const + { + return m_sapBroadphases; + } + + virtual ~btMultiSapBroadphase(); + + virtual btBroadphaseProxy* createProxy( const btVector3& aabbMin, const btVector3& aabbMax,int shapeType,void* userPtr, short int collisionFilterGroup,short int collisionFilterMask, btDispatcher* dispatcher,void* multiSapProxy); + virtual void destroyProxy(btBroadphaseProxy* proxy,btDispatcher* dispatcher); + virtual void setAabb(btBroadphaseProxy* proxy,const btVector3& aabbMin,const btVector3& aabbMax, btDispatcher* dispatcher); + virtual void getAabb(btBroadphaseProxy* proxy,btVector3& aabbMin, btVector3& aabbMax ) const; + + virtual void rayTest(const btVector3& rayFrom,const btVector3& rayTo, btBroadphaseRayCallback& rayCallback,const btVector3& aabbMin=btVector3(0,0,0),const btVector3& aabbMax=btVector3(0,0,0)); + + void addToChildBroadphase(btMultiSapProxy* parentMultiSapProxy, btBroadphaseProxy* childProxy, btBroadphaseInterface* childBroadphase); + + ///calculateOverlappingPairs is optional: incremental algorithms (sweep and prune) might do it during the set aabb + virtual void calculateOverlappingPairs(btDispatcher* dispatcher); + + bool testAabbOverlap(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1); + + virtual btOverlappingPairCache* getOverlappingPairCache() + { + return m_overlappingPairs; + } + virtual const btOverlappingPairCache* getOverlappingPairCache() const + { + return m_overlappingPairs; + } + + ///getAabb returns the axis aligned bounding box in the 'global' coordinate frame + ///will add some transform later + virtual void getBroadphaseAabb(btVector3& aabbMin,btVector3& aabbMax) const + { + aabbMin.setValue(-BT_LARGE_FLOAT,-BT_LARGE_FLOAT,-BT_LARGE_FLOAT); + aabbMax.setValue(BT_LARGE_FLOAT,BT_LARGE_FLOAT,BT_LARGE_FLOAT); + } + + void buildTree(const btVector3& bvhAabbMin,const btVector3& bvhAabbMax); + + virtual void printStats(); + + void quicksort (btBroadphasePairArray& a, int lo, int hi); + + ///reset broadphase internal structures, to ensure determinism/reproducability + virtual void resetPool(btDispatcher* dispatcher); + +}; + +#endif //BT_MULTI_SAP_BROADPHASE diff --git a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCache.cpp b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCache.cpp new file mode 100644 index 000000000..b209bcb9a --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCache.cpp @@ -0,0 +1,633 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btOverlappingPairCache.h" + +#include "btDispatcher.h" +#include "btCollisionAlgorithm.h" +#include "LinearMath/btAabbUtil2.h" + +#include + +int gOverlappingPairs = 0; + +int gRemovePairs =0; +int gAddedPairs =0; +int gFindPairs =0; + + + + +btHashedOverlappingPairCache::btHashedOverlappingPairCache(): + m_overlapFilterCallback(0), + m_blockedForChanges(false), + m_ghostPairCallback(0) +{ + int initialAllocatedSize= 2; + m_overlappingPairArray.reserve(initialAllocatedSize); + growTables(); +} + + + + +btHashedOverlappingPairCache::~btHashedOverlappingPairCache() +{ +} + + + +void btHashedOverlappingPairCache::cleanOverlappingPair(btBroadphasePair& pair,btDispatcher* dispatcher) +{ + if (pair.m_algorithm) + { + { + pair.m_algorithm->~btCollisionAlgorithm(); + dispatcher->freeCollisionAlgorithm(pair.m_algorithm); + pair.m_algorithm=0; + } + } +} + + + + +void btHashedOverlappingPairCache::cleanProxyFromPairs(btBroadphaseProxy* proxy,btDispatcher* dispatcher) +{ + + class CleanPairCallback : public btOverlapCallback + { + btBroadphaseProxy* m_cleanProxy; + btOverlappingPairCache* m_pairCache; + btDispatcher* m_dispatcher; + + public: + CleanPairCallback(btBroadphaseProxy* cleanProxy,btOverlappingPairCache* pairCache,btDispatcher* dispatcher) + :m_cleanProxy(cleanProxy), + m_pairCache(pairCache), + m_dispatcher(dispatcher) + { + } + virtual bool processOverlap(btBroadphasePair& pair) + { + if ((pair.m_pProxy0 == m_cleanProxy) || + (pair.m_pProxy1 == m_cleanProxy)) + { + m_pairCache->cleanOverlappingPair(pair,m_dispatcher); + } + return false; + } + + }; + + CleanPairCallback cleanPairs(proxy,this,dispatcher); + + processAllOverlappingPairs(&cleanPairs,dispatcher); + +} + + + + +void btHashedOverlappingPairCache::removeOverlappingPairsContainingProxy(btBroadphaseProxy* proxy,btDispatcher* dispatcher) +{ + + class RemovePairCallback : public btOverlapCallback + { + btBroadphaseProxy* m_obsoleteProxy; + + public: + RemovePairCallback(btBroadphaseProxy* obsoleteProxy) + :m_obsoleteProxy(obsoleteProxy) + { + } + virtual bool processOverlap(btBroadphasePair& pair) + { + return ((pair.m_pProxy0 == m_obsoleteProxy) || + (pair.m_pProxy1 == m_obsoleteProxy)); + } + + }; + + + RemovePairCallback removeCallback(proxy); + + processAllOverlappingPairs(&removeCallback,dispatcher); +} + + + + + +btBroadphasePair* btHashedOverlappingPairCache::findPair(btBroadphaseProxy* proxy0, btBroadphaseProxy* proxy1) +{ + gFindPairs++; + if(proxy0->m_uniqueId>proxy1->m_uniqueId) + btSwap(proxy0,proxy1); + int proxyId1 = proxy0->getUid(); + int proxyId2 = proxy1->getUid(); + + /*if (proxyId1 > proxyId2) + btSwap(proxyId1, proxyId2);*/ + + int hash = static_cast(getHash(static_cast(proxyId1), static_cast(proxyId2)) & (m_overlappingPairArray.capacity()-1)); + + if (hash >= m_hashTable.size()) + { + return NULL; + } + + int index = m_hashTable[hash]; + while (index != BT_NULL_PAIR && equalsPair(m_overlappingPairArray[index], proxyId1, proxyId2) == false) + { + index = m_next[index]; + } + + if (index == BT_NULL_PAIR) + { + return NULL; + } + + btAssert(index < m_overlappingPairArray.size()); + + return &m_overlappingPairArray[index]; +} + +//#include + +void btHashedOverlappingPairCache::growTables() +{ + + int newCapacity = m_overlappingPairArray.capacity(); + + if (m_hashTable.size() < newCapacity) + { + //grow hashtable and next table + int curHashtableSize = m_hashTable.size(); + + m_hashTable.resize(newCapacity); + m_next.resize(newCapacity); + + + int i; + + for (i= 0; i < newCapacity; ++i) + { + m_hashTable[i] = BT_NULL_PAIR; + } + for (i = 0; i < newCapacity; ++i) + { + m_next[i] = BT_NULL_PAIR; + } + + for(i=0;igetUid(); + int proxyId2 = pair.m_pProxy1->getUid(); + /*if (proxyId1 > proxyId2) + btSwap(proxyId1, proxyId2);*/ + int hashValue = static_cast(getHash(static_cast(proxyId1),static_cast(proxyId2)) & (m_overlappingPairArray.capacity()-1)); // New hash value with new mask + m_next[i] = m_hashTable[hashValue]; + m_hashTable[hashValue] = i; + } + + + } +} + +btBroadphasePair* btHashedOverlappingPairCache::internalAddPair(btBroadphaseProxy* proxy0, btBroadphaseProxy* proxy1) +{ + if(proxy0->m_uniqueId>proxy1->m_uniqueId) + btSwap(proxy0,proxy1); + int proxyId1 = proxy0->getUid(); + int proxyId2 = proxy1->getUid(); + + /*if (proxyId1 > proxyId2) + btSwap(proxyId1, proxyId2);*/ + + int hash = static_cast(getHash(static_cast(proxyId1),static_cast(proxyId2)) & (m_overlappingPairArray.capacity()-1)); // New hash value with new mask + + + btBroadphasePair* pair = internalFindPair(proxy0, proxy1, hash); + if (pair != NULL) + { + return pair; + } + /*for(int i=0;i%u\r\n",proxyId1,proxyId2); + internalFindPair(proxy0, proxy1, hash); + } + }*/ + int count = m_overlappingPairArray.size(); + int oldCapacity = m_overlappingPairArray.capacity(); + void* mem = &m_overlappingPairArray.expand(); + + //this is where we add an actual pair, so also call the 'ghost' + if (m_ghostPairCallback) + m_ghostPairCallback->addOverlappingPair(proxy0,proxy1); + + int newCapacity = m_overlappingPairArray.capacity(); + + if (oldCapacity < newCapacity) + { + growTables(); + //hash with new capacity + hash = static_cast(getHash(static_cast(proxyId1),static_cast(proxyId2)) & (m_overlappingPairArray.capacity()-1)); + } + + pair = new (mem) btBroadphasePair(*proxy0,*proxy1); +// pair->m_pProxy0 = proxy0; +// pair->m_pProxy1 = proxy1; + pair->m_algorithm = 0; + pair->m_internalTmpValue = 0; + + + m_next[count] = m_hashTable[hash]; + m_hashTable[hash] = count; + + return pair; +} + + + +void* btHashedOverlappingPairCache::removeOverlappingPair(btBroadphaseProxy* proxy0, btBroadphaseProxy* proxy1,btDispatcher* dispatcher) +{ + gRemovePairs++; + if(proxy0->m_uniqueId>proxy1->m_uniqueId) + btSwap(proxy0,proxy1); + int proxyId1 = proxy0->getUid(); + int proxyId2 = proxy1->getUid(); + + /*if (proxyId1 > proxyId2) + btSwap(proxyId1, proxyId2);*/ + + int hash = static_cast(getHash(static_cast(proxyId1),static_cast(proxyId2)) & (m_overlappingPairArray.capacity()-1)); + + btBroadphasePair* pair = internalFindPair(proxy0, proxy1, hash); + if (pair == NULL) + { + return 0; + } + + cleanOverlappingPair(*pair,dispatcher); + + void* userData = pair->m_internalInfo1; + + btAssert(pair->m_pProxy0->getUid() == proxyId1); + btAssert(pair->m_pProxy1->getUid() == proxyId2); + + int pairIndex = int(pair - &m_overlappingPairArray[0]); + btAssert(pairIndex < m_overlappingPairArray.size()); + + // Remove the pair from the hash table. + int index = m_hashTable[hash]; + btAssert(index != BT_NULL_PAIR); + + int previous = BT_NULL_PAIR; + while (index != pairIndex) + { + previous = index; + index = m_next[index]; + } + + if (previous != BT_NULL_PAIR) + { + btAssert(m_next[previous] == pairIndex); + m_next[previous] = m_next[pairIndex]; + } + else + { + m_hashTable[hash] = m_next[pairIndex]; + } + + // We now move the last pair into spot of the + // pair being removed. We need to fix the hash + // table indices to support the move. + + int lastPairIndex = m_overlappingPairArray.size() - 1; + + if (m_ghostPairCallback) + m_ghostPairCallback->removeOverlappingPair(proxy0, proxy1,dispatcher); + + // If the removed pair is the last pair, we are done. + if (lastPairIndex == pairIndex) + { + m_overlappingPairArray.pop_back(); + return userData; + } + + // Remove the last pair from the hash table. + const btBroadphasePair* last = &m_overlappingPairArray[lastPairIndex]; + /* missing swap here too, Nat. */ + int lastHash = static_cast(getHash(static_cast(last->m_pProxy0->getUid()), static_cast(last->m_pProxy1->getUid())) & (m_overlappingPairArray.capacity()-1)); + + index = m_hashTable[lastHash]; + btAssert(index != BT_NULL_PAIR); + + previous = BT_NULL_PAIR; + while (index != lastPairIndex) + { + previous = index; + index = m_next[index]; + } + + if (previous != BT_NULL_PAIR) + { + btAssert(m_next[previous] == lastPairIndex); + m_next[previous] = m_next[lastPairIndex]; + } + else + { + m_hashTable[lastHash] = m_next[lastPairIndex]; + } + + // Copy the last pair into the remove pair's spot. + m_overlappingPairArray[pairIndex] = m_overlappingPairArray[lastPairIndex]; + + // Insert the last pair into the hash table + m_next[pairIndex] = m_hashTable[lastHash]; + m_hashTable[lastHash] = pairIndex; + + m_overlappingPairArray.pop_back(); + + return userData; +} +//#include + +void btHashedOverlappingPairCache::processAllOverlappingPairs(btOverlapCallback* callback,btDispatcher* dispatcher) +{ + + int i; + +// printf("m_overlappingPairArray.size()=%d\n",m_overlappingPairArray.size()); + for (i=0;iprocessOverlap(*pair)) + { + removeOverlappingPair(pair->m_pProxy0,pair->m_pProxy1,dispatcher); + + gOverlappingPairs--; + } else + { + i++; + } + } +} + +void btHashedOverlappingPairCache::sortOverlappingPairs(btDispatcher* dispatcher) +{ + ///need to keep hashmap in sync with pair address, so rebuild all + btBroadphasePairArray tmpPairs; + int i; + for (i=0;iremoveOverlappingPair(proxy0, proxy1,dispatcher); + + m_overlappingPairArray.swap(findIndex,m_overlappingPairArray.capacity()-1); + m_overlappingPairArray.pop_back(); + return userData; + } + } + + return 0; +} + + + + + + + + +btBroadphasePair* btSortedOverlappingPairCache::addOverlappingPair(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1) +{ + //don't add overlap with own + btAssert(proxy0 != proxy1); + + if (!needsBroadphaseCollision(proxy0,proxy1)) + return 0; + + void* mem = &m_overlappingPairArray.expand(); + btBroadphasePair* pair = new (mem) btBroadphasePair(*proxy0,*proxy1); + + gOverlappingPairs++; + gAddedPairs++; + + if (m_ghostPairCallback) + m_ghostPairCallback->addOverlappingPair(proxy0, proxy1); + return pair; + +} + +///this findPair becomes really slow. Either sort the list to speedup the query, or +///use a different solution. It is mainly used for Removing overlapping pairs. Removal could be delayed. +///we could keep a linked list in each proxy, and store pair in one of the proxies (with lowest memory address) +///Also we can use a 2D bitmap, which can be useful for a future GPU implementation + btBroadphasePair* btSortedOverlappingPairCache::findPair(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1) +{ + if (!needsBroadphaseCollision(proxy0,proxy1)) + return 0; + + btBroadphasePair tmpPair(*proxy0,*proxy1); + int findIndex = m_overlappingPairArray.findLinearSearch(tmpPair); + + if (findIndex < m_overlappingPairArray.size()) + { + //btAssert(it != m_overlappingPairSet.end()); + btBroadphasePair* pair = &m_overlappingPairArray[findIndex]; + return pair; + } + return 0; +} + + + + + + + + + + +//#include + +void btSortedOverlappingPairCache::processAllOverlappingPairs(btOverlapCallback* callback,btDispatcher* dispatcher) +{ + + int i; + + for (i=0;iprocessOverlap(*pair)) + { + cleanOverlappingPair(*pair,dispatcher); + pair->m_pProxy0 = 0; + pair->m_pProxy1 = 0; + m_overlappingPairArray.swap(i,m_overlappingPairArray.size()-1); + m_overlappingPairArray.pop_back(); + gOverlappingPairs--; + } else + { + i++; + } + } +} + + + + +btSortedOverlappingPairCache::btSortedOverlappingPairCache(): + m_blockedForChanges(false), + m_hasDeferredRemoval(true), + m_overlapFilterCallback(0), + m_ghostPairCallback(0) +{ + int initialAllocatedSize= 2; + m_overlappingPairArray.reserve(initialAllocatedSize); +} + +btSortedOverlappingPairCache::~btSortedOverlappingPairCache() +{ +} + +void btSortedOverlappingPairCache::cleanOverlappingPair(btBroadphasePair& pair,btDispatcher* dispatcher) +{ + if (pair.m_algorithm) + { + { + pair.m_algorithm->~btCollisionAlgorithm(); + dispatcher->freeCollisionAlgorithm(pair.m_algorithm); + pair.m_algorithm=0; + gRemovePairs--; + } + } +} + + +void btSortedOverlappingPairCache::cleanProxyFromPairs(btBroadphaseProxy* proxy,btDispatcher* dispatcher) +{ + + class CleanPairCallback : public btOverlapCallback + { + btBroadphaseProxy* m_cleanProxy; + btOverlappingPairCache* m_pairCache; + btDispatcher* m_dispatcher; + + public: + CleanPairCallback(btBroadphaseProxy* cleanProxy,btOverlappingPairCache* pairCache,btDispatcher* dispatcher) + :m_cleanProxy(cleanProxy), + m_pairCache(pairCache), + m_dispatcher(dispatcher) + { + } + virtual bool processOverlap(btBroadphasePair& pair) + { + if ((pair.m_pProxy0 == m_cleanProxy) || + (pair.m_pProxy1 == m_cleanProxy)) + { + m_pairCache->cleanOverlappingPair(pair,m_dispatcher); + } + return false; + } + + }; + + CleanPairCallback cleanPairs(proxy,this,dispatcher); + + processAllOverlappingPairs(&cleanPairs,dispatcher); + +} + + +void btSortedOverlappingPairCache::removeOverlappingPairsContainingProxy(btBroadphaseProxy* proxy,btDispatcher* dispatcher) +{ + + class RemovePairCallback : public btOverlapCallback + { + btBroadphaseProxy* m_obsoleteProxy; + + public: + RemovePairCallback(btBroadphaseProxy* obsoleteProxy) + :m_obsoleteProxy(obsoleteProxy) + { + } + virtual bool processOverlap(btBroadphasePair& pair) + { + return ((pair.m_pProxy0 == m_obsoleteProxy) || + (pair.m_pProxy1 == m_obsoleteProxy)); + } + + }; + + RemovePairCallback removeCallback(proxy); + + processAllOverlappingPairs(&removeCallback,dispatcher); +} + +void btSortedOverlappingPairCache::sortOverlappingPairs(btDispatcher* dispatcher) +{ + //should already be sorted +} + diff --git a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCache.h b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCache.h new file mode 100644 index 000000000..eda45c47b --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCache.h @@ -0,0 +1,468 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 OVERLAPPING_PAIR_CACHE_H +#define OVERLAPPING_PAIR_CACHE_H + + +#include "btBroadphaseInterface.h" +#include "btBroadphaseProxy.h" +#include "btOverlappingPairCallback.h" + +#include "LinearMath/btAlignedObjectArray.h" +class btDispatcher; + +typedef btAlignedObjectArray btBroadphasePairArray; + +struct btOverlapCallback +{ + virtual ~btOverlapCallback() + {} + //return true for deletion of the pair + virtual bool processOverlap(btBroadphasePair& pair) = 0; + +}; + +struct btOverlapFilterCallback +{ + virtual ~btOverlapFilterCallback() + {} + // return true when pairs need collision + virtual bool needBroadphaseCollision(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1) const = 0; +}; + + + + + + + +extern int gRemovePairs; +extern int gAddedPairs; +extern int gFindPairs; + +const int BT_NULL_PAIR=0xffffffff; + +///The btOverlappingPairCache provides an interface for overlapping pair management (add, remove, storage), used by the btBroadphaseInterface broadphases. +///The btHashedOverlappingPairCache and btSortedOverlappingPairCache classes are two implementations. +class btOverlappingPairCache : public btOverlappingPairCallback +{ +public: + virtual ~btOverlappingPairCache() {} // this is needed so we can get to the derived class destructor + + virtual btBroadphasePair* getOverlappingPairArrayPtr() = 0; + + virtual const btBroadphasePair* getOverlappingPairArrayPtr() const = 0; + + virtual btBroadphasePairArray& getOverlappingPairArray() = 0; + + virtual void cleanOverlappingPair(btBroadphasePair& pair,btDispatcher* dispatcher) = 0; + + virtual int getNumOverlappingPairs() const = 0; + + virtual void cleanProxyFromPairs(btBroadphaseProxy* proxy,btDispatcher* dispatcher) = 0; + + virtual void setOverlapFilterCallback(btOverlapFilterCallback* callback) = 0; + + virtual void processAllOverlappingPairs(btOverlapCallback*,btDispatcher* dispatcher) = 0; + + virtual btBroadphasePair* findPair(btBroadphaseProxy* proxy0, btBroadphaseProxy* proxy1) = 0; + + virtual bool hasDeferredRemoval() = 0; + + virtual void setInternalGhostPairCallback(btOverlappingPairCallback* ghostPairCallback)=0; + + virtual void sortOverlappingPairs(btDispatcher* dispatcher) = 0; + + +}; + +/// Hash-space based Pair Cache, thanks to Erin Catto, Box2D, http://www.box2d.org, and Pierre Terdiman, Codercorner, http://codercorner.com +class btHashedOverlappingPairCache : public btOverlappingPairCache +{ + btBroadphasePairArray m_overlappingPairArray; + btOverlapFilterCallback* m_overlapFilterCallback; + bool m_blockedForChanges; + + +public: + btHashedOverlappingPairCache(); + virtual ~btHashedOverlappingPairCache(); + + + void removeOverlappingPairsContainingProxy(btBroadphaseProxy* proxy,btDispatcher* dispatcher); + + virtual void* removeOverlappingPair(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1,btDispatcher* dispatcher); + + SIMD_FORCE_INLINE bool needsBroadphaseCollision(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1) const + { + if (m_overlapFilterCallback) + return m_overlapFilterCallback->needBroadphaseCollision(proxy0,proxy1); + + bool collides = (proxy0->m_collisionFilterGroup & proxy1->m_collisionFilterMask) != 0; + collides = collides && (proxy1->m_collisionFilterGroup & proxy0->m_collisionFilterMask); + + return collides; + } + + // Add a pair and return the new pair. If the pair already exists, + // no new pair is created and the old one is returned. + virtual btBroadphasePair* addOverlappingPair(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1) + { + gAddedPairs++; + + if (!needsBroadphaseCollision(proxy0,proxy1)) + return 0; + + return internalAddPair(proxy0,proxy1); + } + + + + void cleanProxyFromPairs(btBroadphaseProxy* proxy,btDispatcher* dispatcher); + + + virtual void processAllOverlappingPairs(btOverlapCallback*,btDispatcher* dispatcher); + + virtual btBroadphasePair* getOverlappingPairArrayPtr() + { + return &m_overlappingPairArray[0]; + } + + const btBroadphasePair* getOverlappingPairArrayPtr() const + { + return &m_overlappingPairArray[0]; + } + + btBroadphasePairArray& getOverlappingPairArray() + { + return m_overlappingPairArray; + } + + const btBroadphasePairArray& getOverlappingPairArray() const + { + return m_overlappingPairArray; + } + + void cleanOverlappingPair(btBroadphasePair& pair,btDispatcher* dispatcher); + + + + btBroadphasePair* findPair(btBroadphaseProxy* proxy0, btBroadphaseProxy* proxy1); + + int GetCount() const { return m_overlappingPairArray.size(); } +// btBroadphasePair* GetPairs() { return m_pairs; } + + btOverlapFilterCallback* getOverlapFilterCallback() + { + return m_overlapFilterCallback; + } + + void setOverlapFilterCallback(btOverlapFilterCallback* callback) + { + m_overlapFilterCallback = callback; + } + + int getNumOverlappingPairs() const + { + return m_overlappingPairArray.size(); + } +private: + + btBroadphasePair* internalAddPair(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1); + + void growTables(); + + SIMD_FORCE_INLINE bool equalsPair(const btBroadphasePair& pair, int proxyId1, int proxyId2) + { + return pair.m_pProxy0->getUid() == proxyId1 && pair.m_pProxy1->getUid() == proxyId2; + } + + /* + // Thomas Wang's hash, see: http://www.concentric.net/~Ttwang/tech/inthash.htm + // This assumes proxyId1 and proxyId2 are 16-bit. + SIMD_FORCE_INLINE int getHash(int proxyId1, int proxyId2) + { + int key = (proxyId2 << 16) | proxyId1; + key = ~key + (key << 15); + key = key ^ (key >> 12); + key = key + (key << 2); + key = key ^ (key >> 4); + key = key * 2057; + key = key ^ (key >> 16); + return key; + } + */ + + + + SIMD_FORCE_INLINE unsigned int getHash(unsigned int proxyId1, unsigned int proxyId2) + { + int key = static_cast(((unsigned int)proxyId1) | (((unsigned int)proxyId2) <<16)); + // Thomas Wang's hash + + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += ~(key << 11); + key ^= (key >> 16); + return static_cast(key); + } + + + + + + SIMD_FORCE_INLINE btBroadphasePair* internalFindPair(btBroadphaseProxy* proxy0, btBroadphaseProxy* proxy1, int hash) + { + int proxyId1 = proxy0->getUid(); + int proxyId2 = proxy1->getUid(); + #if 0 // wrong, 'equalsPair' use unsorted uids, copy-past devil striked again. Nat. + if (proxyId1 > proxyId2) + btSwap(proxyId1, proxyId2); + #endif + + int index = m_hashTable[hash]; + + while( index != BT_NULL_PAIR && equalsPair(m_overlappingPairArray[index], proxyId1, proxyId2) == false) + { + index = m_next[index]; + } + + if ( index == BT_NULL_PAIR ) + { + return NULL; + } + + btAssert(index < m_overlappingPairArray.size()); + + return &m_overlappingPairArray[index]; + } + + virtual bool hasDeferredRemoval() + { + return false; + } + + virtual void setInternalGhostPairCallback(btOverlappingPairCallback* ghostPairCallback) + { + m_ghostPairCallback = ghostPairCallback; + } + + virtual void sortOverlappingPairs(btDispatcher* dispatcher); + + +protected: + + btAlignedObjectArray m_hashTable; + btAlignedObjectArray m_next; + btOverlappingPairCallback* m_ghostPairCallback; + +}; + + + + +///btSortedOverlappingPairCache maintains the objects with overlapping AABB +///Typically managed by the Broadphase, Axis3Sweep or btSimpleBroadphase +class btSortedOverlappingPairCache : public btOverlappingPairCache +{ + protected: + //avoid brute-force finding all the time + btBroadphasePairArray m_overlappingPairArray; + + //during the dispatch, check that user doesn't destroy/create proxy + bool m_blockedForChanges; + + ///by default, do the removal during the pair traversal + bool m_hasDeferredRemoval; + + //if set, use the callback instead of the built in filter in needBroadphaseCollision + btOverlapFilterCallback* m_overlapFilterCallback; + + btOverlappingPairCallback* m_ghostPairCallback; + + public: + + btSortedOverlappingPairCache(); + virtual ~btSortedOverlappingPairCache(); + + virtual void processAllOverlappingPairs(btOverlapCallback*,btDispatcher* dispatcher); + + void* removeOverlappingPair(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1,btDispatcher* dispatcher); + + void cleanOverlappingPair(btBroadphasePair& pair,btDispatcher* dispatcher); + + btBroadphasePair* addOverlappingPair(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1); + + btBroadphasePair* findPair(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1); + + + void cleanProxyFromPairs(btBroadphaseProxy* proxy,btDispatcher* dispatcher); + + void removeOverlappingPairsContainingProxy(btBroadphaseProxy* proxy,btDispatcher* dispatcher); + + + inline bool needsBroadphaseCollision(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1) const + { + if (m_overlapFilterCallback) + return m_overlapFilterCallback->needBroadphaseCollision(proxy0,proxy1); + + bool collides = (proxy0->m_collisionFilterGroup & proxy1->m_collisionFilterMask) != 0; + collides = collides && (proxy1->m_collisionFilterGroup & proxy0->m_collisionFilterMask); + + return collides; + } + + btBroadphasePairArray& getOverlappingPairArray() + { + return m_overlappingPairArray; + } + + const btBroadphasePairArray& getOverlappingPairArray() const + { + return m_overlappingPairArray; + } + + + + + btBroadphasePair* getOverlappingPairArrayPtr() + { + return &m_overlappingPairArray[0]; + } + + const btBroadphasePair* getOverlappingPairArrayPtr() const + { + return &m_overlappingPairArray[0]; + } + + int getNumOverlappingPairs() const + { + return m_overlappingPairArray.size(); + } + + btOverlapFilterCallback* getOverlapFilterCallback() + { + return m_overlapFilterCallback; + } + + void setOverlapFilterCallback(btOverlapFilterCallback* callback) + { + m_overlapFilterCallback = callback; + } + + virtual bool hasDeferredRemoval() + { + return m_hasDeferredRemoval; + } + + virtual void setInternalGhostPairCallback(btOverlappingPairCallback* ghostPairCallback) + { + m_ghostPairCallback = ghostPairCallback; + } + + virtual void sortOverlappingPairs(btDispatcher* dispatcher); + + +}; + + + +///btNullPairCache skips add/removal of overlapping pairs. Userful for benchmarking and unit testing. +class btNullPairCache : public btOverlappingPairCache +{ + + btBroadphasePairArray m_overlappingPairArray; + +public: + + virtual btBroadphasePair* getOverlappingPairArrayPtr() + { + return &m_overlappingPairArray[0]; + } + const btBroadphasePair* getOverlappingPairArrayPtr() const + { + return &m_overlappingPairArray[0]; + } + btBroadphasePairArray& getOverlappingPairArray() + { + return m_overlappingPairArray; + } + + virtual void cleanOverlappingPair(btBroadphasePair& /*pair*/,btDispatcher* /*dispatcher*/) + { + + } + + virtual int getNumOverlappingPairs() const + { + return 0; + } + + virtual void cleanProxyFromPairs(btBroadphaseProxy* /*proxy*/,btDispatcher* /*dispatcher*/) + { + + } + + virtual void setOverlapFilterCallback(btOverlapFilterCallback* /*callback*/) + { + } + + virtual void processAllOverlappingPairs(btOverlapCallback*,btDispatcher* /*dispatcher*/) + { + } + + virtual btBroadphasePair* findPair(btBroadphaseProxy* /*proxy0*/, btBroadphaseProxy* /*proxy1*/) + { + return 0; + } + + virtual bool hasDeferredRemoval() + { + return true; + } + + virtual void setInternalGhostPairCallback(btOverlappingPairCallback* /* ghostPairCallback */) + { + + } + + virtual btBroadphasePair* addOverlappingPair(btBroadphaseProxy* /*proxy0*/,btBroadphaseProxy* /*proxy1*/) + { + return 0; + } + + virtual void* removeOverlappingPair(btBroadphaseProxy* /*proxy0*/,btBroadphaseProxy* /*proxy1*/,btDispatcher* /*dispatcher*/) + { + return 0; + } + + virtual void removeOverlappingPairsContainingProxy(btBroadphaseProxy* /*proxy0*/,btDispatcher* /*dispatcher*/) + { + } + + virtual void sortOverlappingPairs(btDispatcher* dispatcher) + { + } + + +}; + + +#endif //OVERLAPPING_PAIR_CACHE_H + + diff --git a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCallback.h b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCallback.h new file mode 100644 index 000000000..9c7b6f813 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCallback.h @@ -0,0 +1,40 @@ + +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 OVERLAPPING_PAIR_CALLBACK_H +#define OVERLAPPING_PAIR_CALLBACK_H + +class btDispatcher; +struct btBroadphasePair; + +///The btOverlappingPairCallback class is an additional optional broadphase user callback for adding/removing overlapping pairs, similar interface to btOverlappingPairCache. +class btOverlappingPairCallback +{ +public: + virtual ~btOverlappingPairCallback() + { + + } + + virtual btBroadphasePair* addOverlappingPair(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1) = 0; + + virtual void* removeOverlappingPair(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1,btDispatcher* dispatcher) = 0; + + virtual void removeOverlappingPairsContainingProxy(btBroadphaseProxy* proxy0,btDispatcher* dispatcher) = 0; + +}; + +#endif //OVERLAPPING_PAIR_CALLBACK_H diff --git a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btQuantizedBvh.cpp b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btQuantizedBvh.cpp new file mode 100644 index 000000000..41ff80d15 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btQuantizedBvh.cpp @@ -0,0 +1,1148 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btQuantizedBvh.h" + +#include "LinearMath/btAabbUtil2.h" +#include "LinearMath/btIDebugDraw.h" + +#define RAYAABB2 + +btQuantizedBvh::btQuantizedBvh() : + m_bulletVersion(BT_BULLET_VERSION), + m_useQuantization(false), + //m_traversalMode(TRAVERSAL_STACKLESS_CACHE_FRIENDLY) + m_traversalMode(TRAVERSAL_STACKLESS) + //m_traversalMode(TRAVERSAL_RECURSIVE) + ,m_subtreeHeaderCount(0) //PCK: add this line +{ + m_bvhAabbMin.setValue(-SIMD_INFINITY,-SIMD_INFINITY,-SIMD_INFINITY); + m_bvhAabbMax.setValue(SIMD_INFINITY,SIMD_INFINITY,SIMD_INFINITY); +} + + + + + +void btQuantizedBvh::buildInternal() +{ + ///assumes that caller filled in the m_quantizedLeafNodes + m_useQuantization = true; + int numLeafNodes = 0; + + if (m_useQuantization) + { + //now we have an array of leafnodes in m_leafNodes + numLeafNodes = m_quantizedLeafNodes.size(); + + m_quantizedContiguousNodes.resize(2*numLeafNodes); + + } + + m_curNodeIndex = 0; + + buildTree(0,numLeafNodes); + + ///if the entire tree is small then subtree size, we need to create a header info for the tree + if(m_useQuantization && !m_SubtreeHeaders.size()) + { + btBvhSubtreeInfo& subtree = m_SubtreeHeaders.expand(); + subtree.setAabbFromQuantizeNode(m_quantizedContiguousNodes[0]); + subtree.m_rootNodeIndex = 0; + subtree.m_subtreeSize = m_quantizedContiguousNodes[0].isLeafNode() ? 1 : m_quantizedContiguousNodes[0].getEscapeIndex(); + } + + //PCK: update the copy of the size + m_subtreeHeaderCount = m_SubtreeHeaders.size(); + + //PCK: clear m_quantizedLeafNodes and m_leafNodes, they are temporary + m_quantizedLeafNodes.clear(); + m_leafNodes.clear(); +} + + + +///just for debugging, to visualize the individual patches/subtrees +#ifdef DEBUG_PATCH_COLORS +btVector3 color[4]= +{ + btVector3(255,0,0), + btVector3(0,255,0), + btVector3(0,0,255), + btVector3(0,255,255) +}; +#endif //DEBUG_PATCH_COLORS + + + +void btQuantizedBvh::setQuantizationValues(const btVector3& bvhAabbMin,const btVector3& bvhAabbMax,btScalar quantizationMargin) +{ + //enlarge the AABB to avoid division by zero when initializing the quantization values + btVector3 clampValue(quantizationMargin,quantizationMargin,quantizationMargin); + m_bvhAabbMin = bvhAabbMin - clampValue; + m_bvhAabbMax = bvhAabbMax + clampValue; + btVector3 aabbSize = m_bvhAabbMax - m_bvhAabbMin; + m_bvhQuantization = btVector3(btScalar(65533.0),btScalar(65533.0),btScalar(65533.0)) / aabbSize; + m_useQuantization = true; +} + + + + +btQuantizedBvh::~btQuantizedBvh() +{ +} + +#ifdef DEBUG_TREE_BUILDING +int gStackDepth = 0; +int gMaxStackDepth = 0; +#endif //DEBUG_TREE_BUILDING + +void btQuantizedBvh::buildTree (int startIndex,int endIndex) +{ +#ifdef DEBUG_TREE_BUILDING + gStackDepth++; + if (gStackDepth > gMaxStackDepth) + gMaxStackDepth = gStackDepth; +#endif //DEBUG_TREE_BUILDING + + + int splitAxis, splitIndex, i; + int numIndices =endIndex-startIndex; + int curIndex = m_curNodeIndex; + + btAssert(numIndices>0); + + if (numIndices==1) + { +#ifdef DEBUG_TREE_BUILDING + gStackDepth--; +#endif //DEBUG_TREE_BUILDING + + assignInternalNodeFromLeafNode(m_curNodeIndex,startIndex); + + m_curNodeIndex++; + return; + } + //calculate Best Splitting Axis and where to split it. Sort the incoming 'leafNodes' array within range 'startIndex/endIndex'. + + splitAxis = calcSplittingAxis(startIndex,endIndex); + + splitIndex = sortAndCalcSplittingIndex(startIndex,endIndex,splitAxis); + + int internalNodeIndex = m_curNodeIndex; + + //set the min aabb to 'inf' or a max value, and set the max aabb to a -inf/minimum value. + //the aabb will be expanded during buildTree/mergeInternalNodeAabb with actual node values + setInternalNodeAabbMin(m_curNodeIndex,m_bvhAabbMax);//can't use btVector3(SIMD_INFINITY,SIMD_INFINITY,SIMD_INFINITY)) because of quantization + setInternalNodeAabbMax(m_curNodeIndex,m_bvhAabbMin);//can't use btVector3(-SIMD_INFINITY,-SIMD_INFINITY,-SIMD_INFINITY)) because of quantization + + + for (i=startIndex;im_escapeIndex; + + int leftChildNodexIndex = m_curNodeIndex; + + //build left child tree + buildTree(startIndex,splitIndex); + + int rightChildNodexIndex = m_curNodeIndex; + //build right child tree + buildTree(splitIndex,endIndex); + +#ifdef DEBUG_TREE_BUILDING + gStackDepth--; +#endif //DEBUG_TREE_BUILDING + + int escapeIndex = m_curNodeIndex - curIndex; + + if (m_useQuantization) + { + //escapeIndex is the number of nodes of this subtree + const int sizeQuantizedNode =sizeof(btQuantizedBvhNode); + const int treeSizeInBytes = escapeIndex * sizeQuantizedNode; + if (treeSizeInBytes > MAX_SUBTREE_SIZE_IN_BYTES) + { + updateSubtreeHeaders(leftChildNodexIndex,rightChildNodexIndex); + } + } else + { + + } + + setInternalNodeEscapeIndex(internalNodeIndex,escapeIndex); + +} + +void btQuantizedBvh::updateSubtreeHeaders(int leftChildNodexIndex,int rightChildNodexIndex) +{ + btAssert(m_useQuantization); + + btQuantizedBvhNode& leftChildNode = m_quantizedContiguousNodes[leftChildNodexIndex]; + int leftSubTreeSize = leftChildNode.isLeafNode() ? 1 : leftChildNode.getEscapeIndex(); + int leftSubTreeSizeInBytes = leftSubTreeSize * static_cast(sizeof(btQuantizedBvhNode)); + + btQuantizedBvhNode& rightChildNode = m_quantizedContiguousNodes[rightChildNodexIndex]; + int rightSubTreeSize = rightChildNode.isLeafNode() ? 1 : rightChildNode.getEscapeIndex(); + int rightSubTreeSizeInBytes = rightSubTreeSize * static_cast(sizeof(btQuantizedBvhNode)); + + if(leftSubTreeSizeInBytes <= MAX_SUBTREE_SIZE_IN_BYTES) + { + btBvhSubtreeInfo& subtree = m_SubtreeHeaders.expand(); + subtree.setAabbFromQuantizeNode(leftChildNode); + subtree.m_rootNodeIndex = leftChildNodexIndex; + subtree.m_subtreeSize = leftSubTreeSize; + } + + if(rightSubTreeSizeInBytes <= MAX_SUBTREE_SIZE_IN_BYTES) + { + btBvhSubtreeInfo& subtree = m_SubtreeHeaders.expand(); + subtree.setAabbFromQuantizeNode(rightChildNode); + subtree.m_rootNodeIndex = rightChildNodexIndex; + subtree.m_subtreeSize = rightSubTreeSize; + } + + //PCK: update the copy of the size + m_subtreeHeaderCount = m_SubtreeHeaders.size(); +} + + +int btQuantizedBvh::sortAndCalcSplittingIndex(int startIndex,int endIndex,int splitAxis) +{ + int i; + int splitIndex =startIndex; + int numIndices = endIndex - startIndex; + btScalar splitValue; + + btVector3 means(btScalar(0.),btScalar(0.),btScalar(0.)); + for (i=startIndex;i splitValue) + { + //swap + swapLeafNodes(i,splitIndex); + splitIndex++; + } + } + + //if the splitIndex causes unbalanced trees, fix this by using the center in between startIndex and endIndex + //otherwise the tree-building might fail due to stack-overflows in certain cases. + //unbalanced1 is unsafe: it can cause stack overflows + //bool unbalanced1 = ((splitIndex==startIndex) || (splitIndex == (endIndex-1))); + + //unbalanced2 should work too: always use center (perfect balanced trees) + //bool unbalanced2 = true; + + //this should be safe too: + int rangeBalancedIndices = numIndices/3; + bool unbalanced = ((splitIndex<=(startIndex+rangeBalancedIndices)) || (splitIndex >=(endIndex-1-rangeBalancedIndices))); + + if (unbalanced) + { + splitIndex = startIndex+ (numIndices>>1); + } + + bool unbal = (splitIndex==startIndex) || (splitIndex == (endIndex)); + (void)unbal; + btAssert(!unbal); + + return splitIndex; +} + + +int btQuantizedBvh::calcSplittingAxis(int startIndex,int endIndex) +{ + int i; + + btVector3 means(btScalar(0.),btScalar(0.),btScalar(0.)); + btVector3 variance(btScalar(0.),btScalar(0.),btScalar(0.)); + int numIndices = endIndex-startIndex; + + for (i=startIndex;im_aabbMinOrg,rootNode->m_aabbMaxOrg); + isLeafNode = rootNode->m_escapeIndex == -1; + + //PCK: unsigned instead of bool + if (isLeafNode && (aabbOverlap != 0)) + { + nodeCallback->processNode(rootNode->m_subPart,rootNode->m_triangleIndex); + } + + //PCK: unsigned instead of bool + if ((aabbOverlap != 0) || isLeafNode) + { + rootNode++; + curIndex++; + } else + { + escapeIndex = rootNode->m_escapeIndex; + rootNode += escapeIndex; + curIndex += escapeIndex; + } + } + if (maxIterations < walkIterations) + maxIterations = walkIterations; + +} + +/* +///this was the original recursive traversal, before we optimized towards stackless traversal +void btQuantizedBvh::walkTree(btOptimizedBvhNode* rootNode,btNodeOverlapCallback* nodeCallback,const btVector3& aabbMin,const btVector3& aabbMax) const +{ + bool isLeafNode, aabbOverlap = TestAabbAgainstAabb2(aabbMin,aabbMax,rootNode->m_aabbMin,rootNode->m_aabbMax); + if (aabbOverlap) + { + isLeafNode = (!rootNode->m_leftChild && !rootNode->m_rightChild); + if (isLeafNode) + { + nodeCallback->processNode(rootNode); + } else + { + walkTree(rootNode->m_leftChild,nodeCallback,aabbMin,aabbMax); + walkTree(rootNode->m_rightChild,nodeCallback,aabbMin,aabbMax); + } + } + +} +*/ + +void btQuantizedBvh::walkRecursiveQuantizedTreeAgainstQueryAabb(const btQuantizedBvhNode* currentNode,btNodeOverlapCallback* nodeCallback,unsigned short int* quantizedQueryAabbMin,unsigned short int* quantizedQueryAabbMax) const +{ + btAssert(m_useQuantization); + + bool isLeafNode; + //PCK: unsigned instead of bool + unsigned aabbOverlap; + + //PCK: unsigned instead of bool + aabbOverlap = testQuantizedAabbAgainstQuantizedAabb(quantizedQueryAabbMin,quantizedQueryAabbMax,currentNode->m_quantizedAabbMin,currentNode->m_quantizedAabbMax); + isLeafNode = currentNode->isLeafNode(); + + //PCK: unsigned instead of bool + if (aabbOverlap != 0) + { + if (isLeafNode) + { + nodeCallback->processNode(currentNode->getPartId(),currentNode->getTriangleIndex()); + } else + { + //process left and right children + const btQuantizedBvhNode* leftChildNode = currentNode+1; + walkRecursiveQuantizedTreeAgainstQueryAabb(leftChildNode,nodeCallback,quantizedQueryAabbMin,quantizedQueryAabbMax); + + const btQuantizedBvhNode* rightChildNode = leftChildNode->isLeafNode() ? leftChildNode+1:leftChildNode+leftChildNode->getEscapeIndex(); + walkRecursiveQuantizedTreeAgainstQueryAabb(rightChildNode,nodeCallback,quantizedQueryAabbMin,quantizedQueryAabbMax); + } + } +} + + + +void btQuantizedBvh::walkStacklessTreeAgainstRay(btNodeOverlapCallback* nodeCallback, const btVector3& raySource, const btVector3& rayTarget, const btVector3& aabbMin, const btVector3& aabbMax, int startNodeIndex,int endNodeIndex) const +{ + btAssert(!m_useQuantization); + + const btOptimizedBvhNode* rootNode = &m_contiguousNodes[0]; + int escapeIndex, curIndex = 0; + int walkIterations = 0; + bool isLeafNode; + //PCK: unsigned instead of bool + unsigned aabbOverlap=0; + unsigned rayBoxOverlap=0; + btScalar lambda_max = 1.0; + + /* Quick pruning by quantized box */ + btVector3 rayAabbMin = raySource; + btVector3 rayAabbMax = raySource; + rayAabbMin.setMin(rayTarget); + rayAabbMax.setMax(rayTarget); + + /* Add box cast extents to bounding box */ + rayAabbMin += aabbMin; + rayAabbMax += aabbMax; + +#ifdef RAYAABB2 + btVector3 rayDir = (rayTarget-raySource); + rayDir.normalize (); + lambda_max = rayDir.dot(rayTarget-raySource); + ///what about division by zero? --> just set rayDirection[i] to 1.0 + btVector3 rayDirectionInverse; + rayDirectionInverse[0] = rayDir[0] == btScalar(0.0) ? btScalar(BT_LARGE_FLOAT) : btScalar(1.0) / rayDir[0]; + rayDirectionInverse[1] = rayDir[1] == btScalar(0.0) ? btScalar(BT_LARGE_FLOAT) : btScalar(1.0) / rayDir[1]; + rayDirectionInverse[2] = rayDir[2] == btScalar(0.0) ? btScalar(BT_LARGE_FLOAT) : btScalar(1.0) / rayDir[2]; + unsigned int sign[3] = { rayDirectionInverse[0] < 0.0, rayDirectionInverse[1] < 0.0, rayDirectionInverse[2] < 0.0}; +#endif + + btVector3 bounds[2]; + + while (curIndex < m_curNodeIndex) + { + btScalar param = 1.0; + //catch bugs in tree data + btAssert (walkIterations < m_curNodeIndex); + + walkIterations++; + + bounds[0] = rootNode->m_aabbMinOrg; + bounds[1] = rootNode->m_aabbMaxOrg; + /* Add box cast extents */ + bounds[0] += aabbMin; + bounds[1] += aabbMax; + + aabbOverlap = TestAabbAgainstAabb2(rayAabbMin,rayAabbMax,rootNode->m_aabbMinOrg,rootNode->m_aabbMaxOrg); + //perhaps profile if it is worth doing the aabbOverlap test first + +#ifdef RAYAABB2 + ///careful with this check: need to check division by zero (above) and fix the unQuantize method + ///thanks Joerg/hiker for the reproduction case! + ///http://www.bulletphysics.com/Bullet/phpBB3/viewtopic.php?f=9&t=1858 + rayBoxOverlap = aabbOverlap ? btRayAabb2 (raySource, rayDirectionInverse, sign, bounds, param, 0.0f, lambda_max) : false; + +#else + btVector3 normal; + rayBoxOverlap = btRayAabb(raySource, rayTarget,bounds[0],bounds[1],param, normal); +#endif + + isLeafNode = rootNode->m_escapeIndex == -1; + + //PCK: unsigned instead of bool + if (isLeafNode && (rayBoxOverlap != 0)) + { + nodeCallback->processNode(rootNode->m_subPart,rootNode->m_triangleIndex); + } + + //PCK: unsigned instead of bool + if ((rayBoxOverlap != 0) || isLeafNode) + { + rootNode++; + curIndex++; + } else + { + escapeIndex = rootNode->m_escapeIndex; + rootNode += escapeIndex; + curIndex += escapeIndex; + } + } + if (maxIterations < walkIterations) + maxIterations = walkIterations; + +} + + + +void btQuantizedBvh::walkStacklessQuantizedTreeAgainstRay(btNodeOverlapCallback* nodeCallback, const btVector3& raySource, const btVector3& rayTarget, const btVector3& aabbMin, const btVector3& aabbMax, int startNodeIndex,int endNodeIndex) const +{ + btAssert(m_useQuantization); + + int curIndex = startNodeIndex; + int walkIterations = 0; + int subTreeSize = endNodeIndex - startNodeIndex; + (void)subTreeSize; + + const btQuantizedBvhNode* rootNode = &m_quantizedContiguousNodes[startNodeIndex]; + int escapeIndex; + + bool isLeafNode; + //PCK: unsigned instead of bool + unsigned boxBoxOverlap = 0; + unsigned rayBoxOverlap = 0; + + btScalar lambda_max = 1.0; + +#ifdef RAYAABB2 + btVector3 rayDirection = (rayTarget-raySource); + rayDirection.normalize (); + lambda_max = rayDirection.dot(rayTarget-raySource); + ///what about division by zero? --> just set rayDirection[i] to 1.0 + rayDirection[0] = rayDirection[0] == btScalar(0.0) ? btScalar(BT_LARGE_FLOAT) : btScalar(1.0) / rayDirection[0]; + rayDirection[1] = rayDirection[1] == btScalar(0.0) ? btScalar(BT_LARGE_FLOAT) : btScalar(1.0) / rayDirection[1]; + rayDirection[2] = rayDirection[2] == btScalar(0.0) ? btScalar(BT_LARGE_FLOAT) : btScalar(1.0) / rayDirection[2]; + unsigned int sign[3] = { rayDirection[0] < 0.0, rayDirection[1] < 0.0, rayDirection[2] < 0.0}; +#endif + + /* Quick pruning by quantized box */ + btVector3 rayAabbMin = raySource; + btVector3 rayAabbMax = raySource; + rayAabbMin.setMin(rayTarget); + rayAabbMax.setMax(rayTarget); + + /* Add box cast extents to bounding box */ + rayAabbMin += aabbMin; + rayAabbMax += aabbMax; + + unsigned short int quantizedQueryAabbMin[3]; + unsigned short int quantizedQueryAabbMax[3]; + quantizeWithClamp(quantizedQueryAabbMin,rayAabbMin,0); + quantizeWithClamp(quantizedQueryAabbMax,rayAabbMax,1); + + while (curIndex < endNodeIndex) + { + +//#define VISUALLY_ANALYZE_BVH 1 +#ifdef VISUALLY_ANALYZE_BVH + //some code snippet to debugDraw aabb, to visually analyze bvh structure + static int drawPatch = 0; + //need some global access to a debugDrawer + extern btIDebugDraw* debugDrawerPtr; + if (curIndex==drawPatch) + { + btVector3 aabbMin,aabbMax; + aabbMin = unQuantize(rootNode->m_quantizedAabbMin); + aabbMax = unQuantize(rootNode->m_quantizedAabbMax); + btVector3 color(1,0,0); + debugDrawerPtr->drawAabb(aabbMin,aabbMax,color); + } +#endif//VISUALLY_ANALYZE_BVH + + //catch bugs in tree data + btAssert (walkIterations < subTreeSize); + + walkIterations++; + //PCK: unsigned instead of bool + // only interested if this is closer than any previous hit + btScalar param = 1.0; + rayBoxOverlap = 0; + boxBoxOverlap = testQuantizedAabbAgainstQuantizedAabb(quantizedQueryAabbMin,quantizedQueryAabbMax,rootNode->m_quantizedAabbMin,rootNode->m_quantizedAabbMax); + isLeafNode = rootNode->isLeafNode(); + if (boxBoxOverlap) + { + btVector3 bounds[2]; + bounds[0] = unQuantize(rootNode->m_quantizedAabbMin); + bounds[1] = unQuantize(rootNode->m_quantizedAabbMax); + /* Add box cast extents */ + bounds[0] += aabbMin; + bounds[1] += aabbMax; + btVector3 normal; +#if 0 + bool ra2 = btRayAabb2 (raySource, rayDirection, sign, bounds, param, 0.0, lambda_max); + bool ra = btRayAabb (raySource, rayTarget, bounds[0], bounds[1], param, normal); + if (ra2 != ra) + { + printf("functions don't match\n"); + } +#endif +#ifdef RAYAABB2 + ///careful with this check: need to check division by zero (above) and fix the unQuantize method + ///thanks Joerg/hiker for the reproduction case! + ///http://www.bulletphysics.com/Bullet/phpBB3/viewtopic.php?f=9&t=1858 + + //BT_PROFILE("btRayAabb2"); + rayBoxOverlap = btRayAabb2 (raySource, rayDirection, sign, bounds, param, 0.0f, lambda_max); + +#else + rayBoxOverlap = true;//btRayAabb(raySource, rayTarget, bounds[0], bounds[1], param, normal); +#endif + } + + if (isLeafNode && rayBoxOverlap) + { + nodeCallback->processNode(rootNode->getPartId(),rootNode->getTriangleIndex()); + } + + //PCK: unsigned instead of bool + if ((rayBoxOverlap != 0) || isLeafNode) + { + rootNode++; + curIndex++; + } else + { + escapeIndex = rootNode->getEscapeIndex(); + rootNode += escapeIndex; + curIndex += escapeIndex; + } + } + if (maxIterations < walkIterations) + maxIterations = walkIterations; + +} + +void btQuantizedBvh::walkStacklessQuantizedTree(btNodeOverlapCallback* nodeCallback,unsigned short int* quantizedQueryAabbMin,unsigned short int* quantizedQueryAabbMax,int startNodeIndex,int endNodeIndex) const +{ + btAssert(m_useQuantization); + + int curIndex = startNodeIndex; + int walkIterations = 0; + int subTreeSize = endNodeIndex - startNodeIndex; + (void)subTreeSize; + + const btQuantizedBvhNode* rootNode = &m_quantizedContiguousNodes[startNodeIndex]; + int escapeIndex; + + bool isLeafNode; + //PCK: unsigned instead of bool + unsigned aabbOverlap; + + while (curIndex < endNodeIndex) + { + +//#define VISUALLY_ANALYZE_BVH 1 +#ifdef VISUALLY_ANALYZE_BVH + //some code snippet to debugDraw aabb, to visually analyze bvh structure + static int drawPatch = 0; + //need some global access to a debugDrawer + extern btIDebugDraw* debugDrawerPtr; + if (curIndex==drawPatch) + { + btVector3 aabbMin,aabbMax; + aabbMin = unQuantize(rootNode->m_quantizedAabbMin); + aabbMax = unQuantize(rootNode->m_quantizedAabbMax); + btVector3 color(1,0,0); + debugDrawerPtr->drawAabb(aabbMin,aabbMax,color); + } +#endif//VISUALLY_ANALYZE_BVH + + //catch bugs in tree data + btAssert (walkIterations < subTreeSize); + + walkIterations++; + //PCK: unsigned instead of bool + aabbOverlap = testQuantizedAabbAgainstQuantizedAabb(quantizedQueryAabbMin,quantizedQueryAabbMax,rootNode->m_quantizedAabbMin,rootNode->m_quantizedAabbMax); + isLeafNode = rootNode->isLeafNode(); + + if (isLeafNode && aabbOverlap) + { + nodeCallback->processNode(rootNode->getPartId(),rootNode->getTriangleIndex()); + } + + //PCK: unsigned instead of bool + if ((aabbOverlap != 0) || isLeafNode) + { + rootNode++; + curIndex++; + } else + { + escapeIndex = rootNode->getEscapeIndex(); + rootNode += escapeIndex; + curIndex += escapeIndex; + } + } + if (maxIterations < walkIterations) + maxIterations = walkIterations; + +} + +//This traversal can be called from Playstation 3 SPU +void btQuantizedBvh::walkStacklessQuantizedTreeCacheFriendly(btNodeOverlapCallback* nodeCallback,unsigned short int* quantizedQueryAabbMin,unsigned short int* quantizedQueryAabbMax) const +{ + btAssert(m_useQuantization); + + int i; + + + for (i=0;im_SubtreeHeaders.size();i++) + { + const btBvhSubtreeInfo& subtree = m_SubtreeHeaders[i]; + + //PCK: unsigned instead of bool + unsigned overlap = testQuantizedAabbAgainstQuantizedAabb(quantizedQueryAabbMin,quantizedQueryAabbMax,subtree.m_quantizedAabbMin,subtree.m_quantizedAabbMax); + if (overlap != 0) + { + walkStacklessQuantizedTree(nodeCallback,quantizedQueryAabbMin,quantizedQueryAabbMax, + subtree.m_rootNodeIndex, + subtree.m_rootNodeIndex+subtree.m_subtreeSize); + } + } +} + + +void btQuantizedBvh::reportRayOverlappingNodex (btNodeOverlapCallback* nodeCallback, const btVector3& raySource, const btVector3& rayTarget) const +{ + reportBoxCastOverlappingNodex(nodeCallback,raySource,rayTarget,btVector3(0,0,0),btVector3(0,0,0)); +} + + +void btQuantizedBvh::reportBoxCastOverlappingNodex(btNodeOverlapCallback* nodeCallback, const btVector3& raySource, const btVector3& rayTarget, const btVector3& aabbMin,const btVector3& aabbMax) const +{ + //always use stackless + + if (m_useQuantization) + { + walkStacklessQuantizedTreeAgainstRay(nodeCallback, raySource, rayTarget, aabbMin, aabbMax, 0, m_curNodeIndex); + } + else + { + walkStacklessTreeAgainstRay(nodeCallback, raySource, rayTarget, aabbMin, aabbMax, 0, m_curNodeIndex); + } + /* + { + //recursive traversal + btVector3 qaabbMin = raySource; + btVector3 qaabbMax = raySource; + qaabbMin.setMin(rayTarget); + qaabbMax.setMax(rayTarget); + qaabbMin += aabbMin; + qaabbMax += aabbMax; + reportAabbOverlappingNodex(nodeCallback,qaabbMin,qaabbMax); + } + */ + +} + + +void btQuantizedBvh::swapLeafNodes(int i,int splitIndex) +{ + if (m_useQuantization) + { + btQuantizedBvhNode tmp = m_quantizedLeafNodes[i]; + m_quantizedLeafNodes[i] = m_quantizedLeafNodes[splitIndex]; + m_quantizedLeafNodes[splitIndex] = tmp; + } else + { + btOptimizedBvhNode tmp = m_leafNodes[i]; + m_leafNodes[i] = m_leafNodes[splitIndex]; + m_leafNodes[splitIndex] = tmp; + } +} + +void btQuantizedBvh::assignInternalNodeFromLeafNode(int internalNode,int leafNodeIndex) +{ + if (m_useQuantization) + { + m_quantizedContiguousNodes[internalNode] = m_quantizedLeafNodes[leafNodeIndex]; + } else + { + m_contiguousNodes[internalNode] = m_leafNodes[leafNodeIndex]; + } +} + +//PCK: include +#include + +#if 0 +//PCK: consts +static const unsigned BVH_ALIGNMENT = 16; +static const unsigned BVH_ALIGNMENT_MASK = BVH_ALIGNMENT-1; + +static const unsigned BVH_ALIGNMENT_BLOCKS = 2; +#endif + + +unsigned int btQuantizedBvh::getAlignmentSerializationPadding() +{ + // I changed this to 0 since the extra padding is not needed or used. + return 0;//BVH_ALIGNMENT_BLOCKS * BVH_ALIGNMENT; +} + +unsigned btQuantizedBvh::calculateSerializeBufferSize() +{ + unsigned baseSize = sizeof(btQuantizedBvh) + getAlignmentSerializationPadding(); + baseSize += sizeof(btBvhSubtreeInfo) * m_subtreeHeaderCount; + if (m_useQuantization) + { + return baseSize + m_curNodeIndex * sizeof(btQuantizedBvhNode); + } + return baseSize + m_curNodeIndex * sizeof(btOptimizedBvhNode); +} + +bool btQuantizedBvh::serialize(void *o_alignedDataBuffer, unsigned /*i_dataBufferSize */, bool i_swapEndian) +{ + btAssert(m_subtreeHeaderCount == m_SubtreeHeaders.size()); + m_subtreeHeaderCount = m_SubtreeHeaders.size(); + +/* if (i_dataBufferSize < calculateSerializeBufferSize() || o_alignedDataBuffer == NULL || (((unsigned)o_alignedDataBuffer & BVH_ALIGNMENT_MASK) != 0)) + { + ///check alignedment for buffer? + btAssert(0); + return false; + } +*/ + + btQuantizedBvh *targetBvh = (btQuantizedBvh *)o_alignedDataBuffer; + + // construct the class so the virtual function table, etc will be set up + // Also, m_leafNodes and m_quantizedLeafNodes will be initialized to default values by the constructor + new (targetBvh) btQuantizedBvh; + + if (i_swapEndian) + { + targetBvh->m_curNodeIndex = static_cast(btSwapEndian(m_curNodeIndex)); + + + btSwapVector3Endian(m_bvhAabbMin,targetBvh->m_bvhAabbMin); + btSwapVector3Endian(m_bvhAabbMax,targetBvh->m_bvhAabbMax); + btSwapVector3Endian(m_bvhQuantization,targetBvh->m_bvhQuantization); + + targetBvh->m_traversalMode = (btTraversalMode)btSwapEndian(m_traversalMode); + targetBvh->m_subtreeHeaderCount = static_cast(btSwapEndian(m_subtreeHeaderCount)); + } + else + { + targetBvh->m_curNodeIndex = m_curNodeIndex; + targetBvh->m_bvhAabbMin = m_bvhAabbMin; + targetBvh->m_bvhAabbMax = m_bvhAabbMax; + targetBvh->m_bvhQuantization = m_bvhQuantization; + targetBvh->m_traversalMode = m_traversalMode; + targetBvh->m_subtreeHeaderCount = m_subtreeHeaderCount; + } + + targetBvh->m_useQuantization = m_useQuantization; + + unsigned char *nodeData = (unsigned char *)targetBvh; + nodeData += sizeof(btQuantizedBvh); + + unsigned sizeToAdd = 0;//(BVH_ALIGNMENT-((unsigned)nodeData & BVH_ALIGNMENT_MASK))&BVH_ALIGNMENT_MASK; + nodeData += sizeToAdd; + + int nodeCount = m_curNodeIndex; + + if (m_useQuantization) + { + targetBvh->m_quantizedContiguousNodes.initializeFromBuffer(nodeData, nodeCount, nodeCount); + + if (i_swapEndian) + { + for (int nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++) + { + targetBvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[0] = btSwapEndian(m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[0]); + targetBvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[1] = btSwapEndian(m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[1]); + targetBvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[2] = btSwapEndian(m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[2]); + + targetBvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[0] = btSwapEndian(m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[0]); + targetBvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[1] = btSwapEndian(m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[1]); + targetBvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[2] = btSwapEndian(m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[2]); + + targetBvh->m_quantizedContiguousNodes[nodeIndex].m_escapeIndexOrTriangleIndex = static_cast(btSwapEndian(m_quantizedContiguousNodes[nodeIndex].m_escapeIndexOrTriangleIndex)); + } + } + else + { + for (int nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++) + { + + targetBvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[0] = m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[0]; + targetBvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[1] = m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[1]; + targetBvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[2] = m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[2]; + + targetBvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[0] = m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[0]; + targetBvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[1] = m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[1]; + targetBvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[2] = m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[2]; + + targetBvh->m_quantizedContiguousNodes[nodeIndex].m_escapeIndexOrTriangleIndex = m_quantizedContiguousNodes[nodeIndex].m_escapeIndexOrTriangleIndex; + + + } + } + nodeData += sizeof(btQuantizedBvhNode) * nodeCount; + + // this clears the pointer in the member variable it doesn't really do anything to the data + // it does call the destructor on the contained objects, but they are all classes with no destructor defined + // so the memory (which is not freed) is left alone + targetBvh->m_quantizedContiguousNodes.initializeFromBuffer(NULL, 0, 0); + } + else + { + targetBvh->m_contiguousNodes.initializeFromBuffer(nodeData, nodeCount, nodeCount); + + if (i_swapEndian) + { + for (int nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++) + { + btSwapVector3Endian(m_contiguousNodes[nodeIndex].m_aabbMinOrg, targetBvh->m_contiguousNodes[nodeIndex].m_aabbMinOrg); + btSwapVector3Endian(m_contiguousNodes[nodeIndex].m_aabbMaxOrg, targetBvh->m_contiguousNodes[nodeIndex].m_aabbMaxOrg); + + targetBvh->m_contiguousNodes[nodeIndex].m_escapeIndex = static_cast(btSwapEndian(m_contiguousNodes[nodeIndex].m_escapeIndex)); + targetBvh->m_contiguousNodes[nodeIndex].m_subPart = static_cast(btSwapEndian(m_contiguousNodes[nodeIndex].m_subPart)); + targetBvh->m_contiguousNodes[nodeIndex].m_triangleIndex = static_cast(btSwapEndian(m_contiguousNodes[nodeIndex].m_triangleIndex)); + } + } + else + { + for (int nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++) + { + targetBvh->m_contiguousNodes[nodeIndex].m_aabbMinOrg = m_contiguousNodes[nodeIndex].m_aabbMinOrg; + targetBvh->m_contiguousNodes[nodeIndex].m_aabbMaxOrg = m_contiguousNodes[nodeIndex].m_aabbMaxOrg; + + targetBvh->m_contiguousNodes[nodeIndex].m_escapeIndex = m_contiguousNodes[nodeIndex].m_escapeIndex; + targetBvh->m_contiguousNodes[nodeIndex].m_subPart = m_contiguousNodes[nodeIndex].m_subPart; + targetBvh->m_contiguousNodes[nodeIndex].m_triangleIndex = m_contiguousNodes[nodeIndex].m_triangleIndex; + } + } + nodeData += sizeof(btOptimizedBvhNode) * nodeCount; + + // this clears the pointer in the member variable it doesn't really do anything to the data + // it does call the destructor on the contained objects, but they are all classes with no destructor defined + // so the memory (which is not freed) is left alone + targetBvh->m_contiguousNodes.initializeFromBuffer(NULL, 0, 0); + } + + sizeToAdd = 0;//(BVH_ALIGNMENT-((unsigned)nodeData & BVH_ALIGNMENT_MASK))&BVH_ALIGNMENT_MASK; + nodeData += sizeToAdd; + + // Now serialize the subtree headers + targetBvh->m_SubtreeHeaders.initializeFromBuffer(nodeData, m_subtreeHeaderCount, m_subtreeHeaderCount); + if (i_swapEndian) + { + for (int i = 0; i < m_subtreeHeaderCount; i++) + { + targetBvh->m_SubtreeHeaders[i].m_quantizedAabbMin[0] = btSwapEndian(m_SubtreeHeaders[i].m_quantizedAabbMin[0]); + targetBvh->m_SubtreeHeaders[i].m_quantizedAabbMin[1] = btSwapEndian(m_SubtreeHeaders[i].m_quantizedAabbMin[1]); + targetBvh->m_SubtreeHeaders[i].m_quantizedAabbMin[2] = btSwapEndian(m_SubtreeHeaders[i].m_quantizedAabbMin[2]); + + targetBvh->m_SubtreeHeaders[i].m_quantizedAabbMax[0] = btSwapEndian(m_SubtreeHeaders[i].m_quantizedAabbMax[0]); + targetBvh->m_SubtreeHeaders[i].m_quantizedAabbMax[1] = btSwapEndian(m_SubtreeHeaders[i].m_quantizedAabbMax[1]); + targetBvh->m_SubtreeHeaders[i].m_quantizedAabbMax[2] = btSwapEndian(m_SubtreeHeaders[i].m_quantizedAabbMax[2]); + + targetBvh->m_SubtreeHeaders[i].m_rootNodeIndex = static_cast(btSwapEndian(m_SubtreeHeaders[i].m_rootNodeIndex)); + targetBvh->m_SubtreeHeaders[i].m_subtreeSize = static_cast(btSwapEndian(m_SubtreeHeaders[i].m_subtreeSize)); + } + } + else + { + for (int i = 0; i < m_subtreeHeaderCount; i++) + { + targetBvh->m_SubtreeHeaders[i].m_quantizedAabbMin[0] = (m_SubtreeHeaders[i].m_quantizedAabbMin[0]); + targetBvh->m_SubtreeHeaders[i].m_quantizedAabbMin[1] = (m_SubtreeHeaders[i].m_quantizedAabbMin[1]); + targetBvh->m_SubtreeHeaders[i].m_quantizedAabbMin[2] = (m_SubtreeHeaders[i].m_quantizedAabbMin[2]); + + targetBvh->m_SubtreeHeaders[i].m_quantizedAabbMax[0] = (m_SubtreeHeaders[i].m_quantizedAabbMax[0]); + targetBvh->m_SubtreeHeaders[i].m_quantizedAabbMax[1] = (m_SubtreeHeaders[i].m_quantizedAabbMax[1]); + targetBvh->m_SubtreeHeaders[i].m_quantizedAabbMax[2] = (m_SubtreeHeaders[i].m_quantizedAabbMax[2]); + + targetBvh->m_SubtreeHeaders[i].m_rootNodeIndex = (m_SubtreeHeaders[i].m_rootNodeIndex); + targetBvh->m_SubtreeHeaders[i].m_subtreeSize = (m_SubtreeHeaders[i].m_subtreeSize); + + // need to clear padding in destination buffer + targetBvh->m_SubtreeHeaders[i].m_padding[0] = 0; + targetBvh->m_SubtreeHeaders[i].m_padding[1] = 0; + targetBvh->m_SubtreeHeaders[i].m_padding[2] = 0; + } + } + nodeData += sizeof(btBvhSubtreeInfo) * m_subtreeHeaderCount; + + // this clears the pointer in the member variable it doesn't really do anything to the data + // it does call the destructor on the contained objects, but they are all classes with no destructor defined + // so the memory (which is not freed) is left alone + targetBvh->m_SubtreeHeaders.initializeFromBuffer(NULL, 0, 0); + + // this wipes the virtual function table pointer at the start of the buffer for the class + *((void**)o_alignedDataBuffer) = NULL; + + return true; +} + +btQuantizedBvh *btQuantizedBvh::deSerializeInPlace(void *i_alignedDataBuffer, unsigned int i_dataBufferSize, bool i_swapEndian) +{ + + if (i_alignedDataBuffer == NULL)// || (((unsigned)i_alignedDataBuffer & BVH_ALIGNMENT_MASK) != 0)) + { + return NULL; + } + btQuantizedBvh *bvh = (btQuantizedBvh *)i_alignedDataBuffer; + + if (i_swapEndian) + { + bvh->m_curNodeIndex = static_cast(btSwapEndian(bvh->m_curNodeIndex)); + + btUnSwapVector3Endian(bvh->m_bvhAabbMin); + btUnSwapVector3Endian(bvh->m_bvhAabbMax); + btUnSwapVector3Endian(bvh->m_bvhQuantization); + + bvh->m_traversalMode = (btTraversalMode)btSwapEndian(bvh->m_traversalMode); + bvh->m_subtreeHeaderCount = static_cast(btSwapEndian(bvh->m_subtreeHeaderCount)); + } + + unsigned int calculatedBufSize = bvh->calculateSerializeBufferSize(); + btAssert(calculatedBufSize <= i_dataBufferSize); + + if (calculatedBufSize > i_dataBufferSize) + { + return NULL; + } + + unsigned char *nodeData = (unsigned char *)bvh; + nodeData += sizeof(btQuantizedBvh); + + unsigned sizeToAdd = 0;//(BVH_ALIGNMENT-((unsigned)nodeData & BVH_ALIGNMENT_MASK))&BVH_ALIGNMENT_MASK; + nodeData += sizeToAdd; + + int nodeCount = bvh->m_curNodeIndex; + + // Must call placement new to fill in virtual function table, etc, but we don't want to overwrite most data, so call a special version of the constructor + // Also, m_leafNodes and m_quantizedLeafNodes will be initialized to default values by the constructor + new (bvh) btQuantizedBvh(*bvh, false); + + if (bvh->m_useQuantization) + { + bvh->m_quantizedContiguousNodes.initializeFromBuffer(nodeData, nodeCount, nodeCount); + + if (i_swapEndian) + { + for (int nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++) + { + bvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[0] = btSwapEndian(bvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[0]); + bvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[1] = btSwapEndian(bvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[1]); + bvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[2] = btSwapEndian(bvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[2]); + + bvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[0] = btSwapEndian(bvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[0]); + bvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[1] = btSwapEndian(bvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[1]); + bvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[2] = btSwapEndian(bvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[2]); + + bvh->m_quantizedContiguousNodes[nodeIndex].m_escapeIndexOrTriangleIndex = static_cast(btSwapEndian(bvh->m_quantizedContiguousNodes[nodeIndex].m_escapeIndexOrTriangleIndex)); + } + } + nodeData += sizeof(btQuantizedBvhNode) * nodeCount; + } + else + { + bvh->m_contiguousNodes.initializeFromBuffer(nodeData, nodeCount, nodeCount); + + if (i_swapEndian) + { + for (int nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++) + { + btUnSwapVector3Endian(bvh->m_contiguousNodes[nodeIndex].m_aabbMinOrg); + btUnSwapVector3Endian(bvh->m_contiguousNodes[nodeIndex].m_aabbMaxOrg); + + bvh->m_contiguousNodes[nodeIndex].m_escapeIndex = static_cast(btSwapEndian(bvh->m_contiguousNodes[nodeIndex].m_escapeIndex)); + bvh->m_contiguousNodes[nodeIndex].m_subPart = static_cast(btSwapEndian(bvh->m_contiguousNodes[nodeIndex].m_subPart)); + bvh->m_contiguousNodes[nodeIndex].m_triangleIndex = static_cast(btSwapEndian(bvh->m_contiguousNodes[nodeIndex].m_triangleIndex)); + } + } + nodeData += sizeof(btOptimizedBvhNode) * nodeCount; + } + + sizeToAdd = 0;//(BVH_ALIGNMENT-((unsigned)nodeData & BVH_ALIGNMENT_MASK))&BVH_ALIGNMENT_MASK; + nodeData += sizeToAdd; + + // Now serialize the subtree headers + bvh->m_SubtreeHeaders.initializeFromBuffer(nodeData, bvh->m_subtreeHeaderCount, bvh->m_subtreeHeaderCount); + if (i_swapEndian) + { + for (int i = 0; i < bvh->m_subtreeHeaderCount; i++) + { + bvh->m_SubtreeHeaders[i].m_quantizedAabbMin[0] = btSwapEndian(bvh->m_SubtreeHeaders[i].m_quantizedAabbMin[0]); + bvh->m_SubtreeHeaders[i].m_quantizedAabbMin[1] = btSwapEndian(bvh->m_SubtreeHeaders[i].m_quantizedAabbMin[1]); + bvh->m_SubtreeHeaders[i].m_quantizedAabbMin[2] = btSwapEndian(bvh->m_SubtreeHeaders[i].m_quantizedAabbMin[2]); + + bvh->m_SubtreeHeaders[i].m_quantizedAabbMax[0] = btSwapEndian(bvh->m_SubtreeHeaders[i].m_quantizedAabbMax[0]); + bvh->m_SubtreeHeaders[i].m_quantizedAabbMax[1] = btSwapEndian(bvh->m_SubtreeHeaders[i].m_quantizedAabbMax[1]); + bvh->m_SubtreeHeaders[i].m_quantizedAabbMax[2] = btSwapEndian(bvh->m_SubtreeHeaders[i].m_quantizedAabbMax[2]); + + bvh->m_SubtreeHeaders[i].m_rootNodeIndex = static_cast(btSwapEndian(bvh->m_SubtreeHeaders[i].m_rootNodeIndex)); + bvh->m_SubtreeHeaders[i].m_subtreeSize = static_cast(btSwapEndian(bvh->m_SubtreeHeaders[i].m_subtreeSize)); + } + } + + return bvh; +} + +// Constructor that prevents btVector3's default constructor from being called +btQuantizedBvh::btQuantizedBvh(btQuantizedBvh &self, bool /* ownsMemory */) : +m_bvhAabbMin(self.m_bvhAabbMin), +m_bvhAabbMax(self.m_bvhAabbMax), +m_bvhQuantization(self.m_bvhQuantization), +m_bulletVersion(BT_BULLET_VERSION) +{ + +} + + + + diff --git a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btQuantizedBvh.h b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btQuantizedBvh.h new file mode 100644 index 000000000..ced457b60 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btQuantizedBvh.h @@ -0,0 +1,473 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 QUANTIZED_BVH_H +#define QUANTIZED_BVH_H + +//#define DEBUG_CHECK_DEQUANTIZATION 1 +#ifdef DEBUG_CHECK_DEQUANTIZATION +#ifdef __SPU__ +#define printf spu_printf +#endif //__SPU__ + +#include +#include +#endif //DEBUG_CHECK_DEQUANTIZATION + +#include "LinearMath/btVector3.h" +#include "LinearMath/btAlignedAllocator.h" + + +//http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/vclrf__m128.asp + + +//Note: currently we have 16 bytes per quantized node +#define MAX_SUBTREE_SIZE_IN_BYTES 2048 + +// 10 gives the potential for 1024 parts, with at most 2^21 (2097152) (minus one +// actually) triangles each (since the sign bit is reserved +#define MAX_NUM_PARTS_IN_BITS 10 + +///btQuantizedBvhNode is a compressed aabb node, 16 bytes. +///Node can be used for leafnode or internal node. Leafnodes can point to 32-bit triangle index (non-negative range). +ATTRIBUTE_ALIGNED16 (struct) btQuantizedBvhNode +{ + BT_DECLARE_ALIGNED_ALLOCATOR(); + + //12 bytes + unsigned short int m_quantizedAabbMin[3]; + unsigned short int m_quantizedAabbMax[3]; + //4 bytes + int m_escapeIndexOrTriangleIndex; + + bool isLeafNode() const + { + //skipindex is negative (internal node), triangleindex >=0 (leafnode) + return (m_escapeIndexOrTriangleIndex >= 0); + } + int getEscapeIndex() const + { + btAssert(!isLeafNode()); + return -m_escapeIndexOrTriangleIndex; + } + int getTriangleIndex() const + { + btAssert(isLeafNode()); + // Get only the lower bits where the triangle index is stored + return (m_escapeIndexOrTriangleIndex&~((~0)<<(31-MAX_NUM_PARTS_IN_BITS))); + } + int getPartId() const + { + btAssert(isLeafNode()); + // Get only the highest bits where the part index is stored + return (m_escapeIndexOrTriangleIndex>>(31-MAX_NUM_PARTS_IN_BITS)); + } +} +; + +/// btOptimizedBvhNode contains both internal and leaf node information. +/// Total node size is 44 bytes / node. You can use the compressed version of 16 bytes. +ATTRIBUTE_ALIGNED16 (struct) btOptimizedBvhNode +{ + BT_DECLARE_ALIGNED_ALLOCATOR(); + + //32 bytes + btVector3 m_aabbMinOrg; + btVector3 m_aabbMaxOrg; + + //4 + int m_escapeIndex; + + //8 + //for child nodes + int m_subPart; + int m_triangleIndex; + int m_padding[5];//bad, due to alignment + + +}; + + +///btBvhSubtreeInfo provides info to gather a subtree of limited size +ATTRIBUTE_ALIGNED16(class) btBvhSubtreeInfo +{ +public: + BT_DECLARE_ALIGNED_ALLOCATOR(); + + //12 bytes + unsigned short int m_quantizedAabbMin[3]; + unsigned short int m_quantizedAabbMax[3]; + //4 bytes, points to the root of the subtree + int m_rootNodeIndex; + //4 bytes + int m_subtreeSize; + int m_padding[3]; + + btBvhSubtreeInfo() + { + //memset(&m_padding[0], 0, sizeof(m_padding)); + } + + + void setAabbFromQuantizeNode(const btQuantizedBvhNode& quantizedNode) + { + m_quantizedAabbMin[0] = quantizedNode.m_quantizedAabbMin[0]; + m_quantizedAabbMin[1] = quantizedNode.m_quantizedAabbMin[1]; + m_quantizedAabbMin[2] = quantizedNode.m_quantizedAabbMin[2]; + m_quantizedAabbMax[0] = quantizedNode.m_quantizedAabbMax[0]; + m_quantizedAabbMax[1] = quantizedNode.m_quantizedAabbMax[1]; + m_quantizedAabbMax[2] = quantizedNode.m_quantizedAabbMax[2]; + } +} +; + + +class btNodeOverlapCallback +{ +public: + virtual ~btNodeOverlapCallback() {}; + + virtual void processNode(int subPart, int triangleIndex) = 0; +}; + +#include "LinearMath/btAlignedAllocator.h" +#include "LinearMath/btAlignedObjectArray.h" + + + +///for code readability: +typedef btAlignedObjectArray NodeArray; +typedef btAlignedObjectArray QuantizedNodeArray; +typedef btAlignedObjectArray BvhSubtreeInfoArray; + + +///The btQuantizedBvh class stores an AABB tree that can be quickly traversed on CPU and Cell SPU. +///It is used by the btBvhTriangleMeshShape as midphase, and by the btMultiSapBroadphase. +///It is recommended to use quantization for better performance and lower memory requirements. +ATTRIBUTE_ALIGNED16(class) btQuantizedBvh +{ +public: + enum btTraversalMode + { + TRAVERSAL_STACKLESS = 0, + TRAVERSAL_STACKLESS_CACHE_FRIENDLY, + TRAVERSAL_RECURSIVE + }; + +protected: + + + btVector3 m_bvhAabbMin; + btVector3 m_bvhAabbMax; + btVector3 m_bvhQuantization; + + int m_bulletVersion; //for serialization versioning. It could also be used to detect endianess. + + int m_curNodeIndex; + //quantization data + bool m_useQuantization; + + + + NodeArray m_leafNodes; + NodeArray m_contiguousNodes; + QuantizedNodeArray m_quantizedLeafNodes; + QuantizedNodeArray m_quantizedContiguousNodes; + + btTraversalMode m_traversalMode; + BvhSubtreeInfoArray m_SubtreeHeaders; + + //This is only used for serialization so we don't have to add serialization directly to btAlignedObjectArray + int m_subtreeHeaderCount; + + + + + + ///two versions, one for quantized and normal nodes. This allows code-reuse while maintaining readability (no template/macro!) + ///this might be refactored into a virtual, it is usually not calculated at run-time + void setInternalNodeAabbMin(int nodeIndex, const btVector3& aabbMin) + { + if (m_useQuantization) + { + quantize(&m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[0] ,aabbMin,0); + } else + { + m_contiguousNodes[nodeIndex].m_aabbMinOrg = aabbMin; + + } + } + void setInternalNodeAabbMax(int nodeIndex,const btVector3& aabbMax) + { + if (m_useQuantization) + { + quantize(&m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[0],aabbMax,1); + } else + { + m_contiguousNodes[nodeIndex].m_aabbMaxOrg = aabbMax; + } + } + + btVector3 getAabbMin(int nodeIndex) const + { + if (m_useQuantization) + { + return unQuantize(&m_quantizedLeafNodes[nodeIndex].m_quantizedAabbMin[0]); + } + //non-quantized + return m_leafNodes[nodeIndex].m_aabbMinOrg; + + } + btVector3 getAabbMax(int nodeIndex) const + { + if (m_useQuantization) + { + return unQuantize(&m_quantizedLeafNodes[nodeIndex].m_quantizedAabbMax[0]); + } + //non-quantized + return m_leafNodes[nodeIndex].m_aabbMaxOrg; + + } + + + void setInternalNodeEscapeIndex(int nodeIndex, int escapeIndex) + { + if (m_useQuantization) + { + m_quantizedContiguousNodes[nodeIndex].m_escapeIndexOrTriangleIndex = -escapeIndex; + } + else + { + m_contiguousNodes[nodeIndex].m_escapeIndex = escapeIndex; + } + + } + + void mergeInternalNodeAabb(int nodeIndex,const btVector3& newAabbMin,const btVector3& newAabbMax) + { + if (m_useQuantization) + { + unsigned short int quantizedAabbMin[3]; + unsigned short int quantizedAabbMax[3]; + quantize(quantizedAabbMin,newAabbMin,0); + quantize(quantizedAabbMax,newAabbMax,1); + for (int i=0;i<3;i++) + { + if (m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[i] > quantizedAabbMin[i]) + m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[i] = quantizedAabbMin[i]; + + if (m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[i] < quantizedAabbMax[i]) + m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[i] = quantizedAabbMax[i]; + + } + } else + { + //non-quantized + m_contiguousNodes[nodeIndex].m_aabbMinOrg.setMin(newAabbMin); + m_contiguousNodes[nodeIndex].m_aabbMaxOrg.setMax(newAabbMax); + } + } + + void swapLeafNodes(int firstIndex,int secondIndex); + + void assignInternalNodeFromLeafNode(int internalNode,int leafNodeIndex); + +protected: + + + + void buildTree (int startIndex,int endIndex); + + int calcSplittingAxis(int startIndex,int endIndex); + + int sortAndCalcSplittingIndex(int startIndex,int endIndex,int splitAxis); + + void walkStacklessTree(btNodeOverlapCallback* nodeCallback,const btVector3& aabbMin,const btVector3& aabbMax) const; + + void walkStacklessQuantizedTreeAgainstRay(btNodeOverlapCallback* nodeCallback, const btVector3& raySource, const btVector3& rayTarget, const btVector3& aabbMin, const btVector3& aabbMax, int startNodeIndex,int endNodeIndex) const; + void walkStacklessQuantizedTree(btNodeOverlapCallback* nodeCallback,unsigned short int* quantizedQueryAabbMin,unsigned short int* quantizedQueryAabbMax,int startNodeIndex,int endNodeIndex) const; + void walkStacklessTreeAgainstRay(btNodeOverlapCallback* nodeCallback, const btVector3& raySource, const btVector3& rayTarget, const btVector3& aabbMin, const btVector3& aabbMax, int startNodeIndex,int endNodeIndex) const; + + ///tree traversal designed for small-memory processors like PS3 SPU + void walkStacklessQuantizedTreeCacheFriendly(btNodeOverlapCallback* nodeCallback,unsigned short int* quantizedQueryAabbMin,unsigned short int* quantizedQueryAabbMax) const; + + ///use the 16-byte stackless 'skipindex' node tree to do a recursive traversal + void walkRecursiveQuantizedTreeAgainstQueryAabb(const btQuantizedBvhNode* currentNode,btNodeOverlapCallback* nodeCallback,unsigned short int* quantizedQueryAabbMin,unsigned short int* quantizedQueryAabbMax) const; + + ///use the 16-byte stackless 'skipindex' node tree to do a recursive traversal + void walkRecursiveQuantizedTreeAgainstQuantizedTree(const btQuantizedBvhNode* treeNodeA,const btQuantizedBvhNode* treeNodeB,btNodeOverlapCallback* nodeCallback) const; + + + + + void updateSubtreeHeaders(int leftChildNodexIndex,int rightChildNodexIndex); + +public: + + BT_DECLARE_ALIGNED_ALLOCATOR(); + + btQuantizedBvh(); + + virtual ~btQuantizedBvh(); + + + ///***************************************** expert/internal use only ************************* + void setQuantizationValues(const btVector3& bvhAabbMin,const btVector3& bvhAabbMax,btScalar quantizationMargin=btScalar(1.0)); + QuantizedNodeArray& getLeafNodeArray() { return m_quantizedLeafNodes; } + ///buildInternal is expert use only: assumes that setQuantizationValues and LeafNodeArray are initialized + void buildInternal(); + ///***************************************** expert/internal use only ************************* + + void reportAabbOverlappingNodex(btNodeOverlapCallback* nodeCallback,const btVector3& aabbMin,const btVector3& aabbMax) const; + void reportRayOverlappingNodex (btNodeOverlapCallback* nodeCallback, const btVector3& raySource, const btVector3& rayTarget) const; + void reportBoxCastOverlappingNodex(btNodeOverlapCallback* nodeCallback, const btVector3& raySource, const btVector3& rayTarget, const btVector3& aabbMin,const btVector3& aabbMax) const; + + SIMD_FORCE_INLINE void quantize(unsigned short* out, const btVector3& point,int isMax) const + { + + btAssert(m_useQuantization); + + btAssert(point.getX() <= m_bvhAabbMax.getX()); + btAssert(point.getY() <= m_bvhAabbMax.getY()); + btAssert(point.getZ() <= m_bvhAabbMax.getZ()); + + btAssert(point.getX() >= m_bvhAabbMin.getX()); + btAssert(point.getY() >= m_bvhAabbMin.getY()); + btAssert(point.getZ() >= m_bvhAabbMin.getZ()); + + btVector3 v = (point - m_bvhAabbMin) * m_bvhQuantization; + ///Make sure rounding is done in a way that unQuantize(quantizeWithClamp(...)) is conservative + ///end-points always set the first bit, so that they are sorted properly (so that neighbouring AABBs overlap properly) + ///@todo: double-check this + if (isMax) + { + out[0] = (unsigned short) (((unsigned short)(v.getX()+btScalar(1.)) | 1)); + out[1] = (unsigned short) (((unsigned short)(v.getY()+btScalar(1.)) | 1)); + out[2] = (unsigned short) (((unsigned short)(v.getZ()+btScalar(1.)) | 1)); + } else + { + out[0] = (unsigned short) (((unsigned short)(v.getX()) & 0xfffe)); + out[1] = (unsigned short) (((unsigned short)(v.getY()) & 0xfffe)); + out[2] = (unsigned short) (((unsigned short)(v.getZ()) & 0xfffe)); + } + + +#ifdef DEBUG_CHECK_DEQUANTIZATION + btVector3 newPoint = unQuantize(out); + if (isMax) + { + if (newPoint.getX() < point.getX()) + { + printf("unconservative X, diffX = %f, oldX=%f,newX=%f\n",newPoint.getX()-point.getX(), newPoint.getX(),point.getX()); + } + if (newPoint.getY() < point.getY()) + { + printf("unconservative Y, diffY = %f, oldY=%f,newY=%f\n",newPoint.getY()-point.getY(), newPoint.getY(),point.getY()); + } + if (newPoint.getZ() < point.getZ()) + { + + printf("unconservative Z, diffZ = %f, oldZ=%f,newZ=%f\n",newPoint.getZ()-point.getZ(), newPoint.getZ(),point.getZ()); + } + } else + { + if (newPoint.getX() > point.getX()) + { + printf("unconservative X, diffX = %f, oldX=%f,newX=%f\n",newPoint.getX()-point.getX(), newPoint.getX(),point.getX()); + } + if (newPoint.getY() > point.getY()) + { + printf("unconservative Y, diffY = %f, oldY=%f,newY=%f\n",newPoint.getY()-point.getY(), newPoint.getY(),point.getY()); + } + if (newPoint.getZ() > point.getZ()) + { + printf("unconservative Z, diffZ = %f, oldZ=%f,newZ=%f\n",newPoint.getZ()-point.getZ(), newPoint.getZ(),point.getZ()); + } + } +#endif //DEBUG_CHECK_DEQUANTIZATION + + } + + + SIMD_FORCE_INLINE void quantizeWithClamp(unsigned short* out, const btVector3& point2,int isMax) const + { + + btAssert(m_useQuantization); + + btVector3 clampedPoint(point2); + clampedPoint.setMax(m_bvhAabbMin); + clampedPoint.setMin(m_bvhAabbMax); + + quantize(out,clampedPoint,isMax); + + } + + SIMD_FORCE_INLINE btVector3 unQuantize(const unsigned short* vecIn) const + { + btVector3 vecOut; + vecOut.setValue( + (btScalar)(vecIn[0]) / (m_bvhQuantization.getX()), + (btScalar)(vecIn[1]) / (m_bvhQuantization.getY()), + (btScalar)(vecIn[2]) / (m_bvhQuantization.getZ())); + vecOut += m_bvhAabbMin; + return vecOut; + } + + ///setTraversalMode let's you choose between stackless, recursive or stackless cache friendly tree traversal. Note this is only implemented for quantized trees. + void setTraversalMode(btTraversalMode traversalMode) + { + m_traversalMode = traversalMode; + } + + + SIMD_FORCE_INLINE QuantizedNodeArray& getQuantizedNodeArray() + { + return m_quantizedContiguousNodes; + } + + + SIMD_FORCE_INLINE BvhSubtreeInfoArray& getSubtreeInfoArray() + { + return m_SubtreeHeaders; + } + + + /////Calculate space needed to store BVH for serialization + unsigned calculateSerializeBufferSize(); + + /// Data buffer MUST be 16 byte aligned + virtual bool serialize(void *o_alignedDataBuffer, unsigned i_dataBufferSize, bool i_swapEndian); + + ///deSerializeInPlace loads and initializes a BVH from a buffer in memory 'in place' + static btQuantizedBvh *deSerializeInPlace(void *i_alignedDataBuffer, unsigned int i_dataBufferSize, bool i_swapEndian); + + static unsigned int getAlignmentSerializationPadding(); + + SIMD_FORCE_INLINE bool isQuantized() + { + return m_useQuantization; + } + +private: + // Special "copy" constructor that allows for in-place deserialization + // Prevents btVector3's default constructor from being called, but doesn't inialize much else + // ownsMemory should most likely be false if deserializing, and if you are not, don't call this (it also changes the function signature, which we need) + btQuantizedBvh(btQuantizedBvh &other, bool ownsMemory); + +} +; + + +#endif //QUANTIZED_BVH_H diff --git a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btSimpleBroadphase.cpp b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btSimpleBroadphase.cpp new file mode 100644 index 000000000..caed63db0 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btSimpleBroadphase.cpp @@ -0,0 +1,330 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btSimpleBroadphase.h" +#include "BulletCollision/BroadphaseCollision/btDispatcher.h" +#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" + +#include "LinearMath/btVector3.h" +#include "LinearMath/btTransform.h" +#include "LinearMath/btMatrix3x3.h" +#include + +extern int gOverlappingPairs; + +void btSimpleBroadphase::validate() +{ + for (int i=0;i~btOverlappingPairCache(); + btAlignedFree(m_pairCache); + } +} + + +btBroadphaseProxy* btSimpleBroadphase::createProxy( const btVector3& aabbMin, const btVector3& aabbMax,int shapeType,void* userPtr ,short int collisionFilterGroup,short int collisionFilterMask, btDispatcher* /*dispatcher*/,void* multiSapProxy) +{ + if (m_numHandles >= m_maxHandles) + { + btAssert(0); + return 0; //should never happen, but don't let the game crash ;-) + } + btAssert(aabbMin[0]<= aabbMax[0] && aabbMin[1]<= aabbMax[1] && aabbMin[2]<= aabbMax[2]); + + int newHandleIndex = allocHandle(); + btSimpleBroadphaseProxy* proxy = new (&m_pHandles[newHandleIndex])btSimpleBroadphaseProxy(aabbMin,aabbMax,shapeType,userPtr,collisionFilterGroup,collisionFilterMask,multiSapProxy); + + return proxy; +} + +class RemovingOverlapCallback : public btOverlapCallback +{ +protected: + virtual bool processOverlap(btBroadphasePair& pair) + { + (void)pair; + btAssert(0); + return false; + } +}; + +class RemovePairContainingProxy +{ + + btBroadphaseProxy* m_targetProxy; + public: + virtual ~RemovePairContainingProxy() + { + } +protected: + virtual bool processOverlap(btBroadphasePair& pair) + { + btSimpleBroadphaseProxy* proxy0 = static_cast(pair.m_pProxy0); + btSimpleBroadphaseProxy* proxy1 = static_cast(pair.m_pProxy1); + + return ((m_targetProxy == proxy0 || m_targetProxy == proxy1)); + }; +}; + +void btSimpleBroadphase::destroyProxy(btBroadphaseProxy* proxyOrg,btDispatcher* dispatcher) +{ + + btSimpleBroadphaseProxy* proxy0 = static_cast(proxyOrg); + freeHandle(proxy0); + + m_pairCache->removeOverlappingPairsContainingProxy(proxyOrg,dispatcher); + + //validate(); + +} + +void btSimpleBroadphase::getAabb(btBroadphaseProxy* proxy,btVector3& aabbMin, btVector3& aabbMax ) const +{ + const btSimpleBroadphaseProxy* sbp = getSimpleProxyFromProxy(proxy); + aabbMin = sbp->m_aabbMin; + aabbMax = sbp->m_aabbMax; +} + +void btSimpleBroadphase::setAabb(btBroadphaseProxy* proxy,const btVector3& aabbMin,const btVector3& aabbMax, btDispatcher* /*dispatcher*/) +{ + btSimpleBroadphaseProxy* sbp = getSimpleProxyFromProxy(proxy); + sbp->m_aabbMin = aabbMin; + sbp->m_aabbMax = aabbMax; +} + +void btSimpleBroadphase::rayTest(const btVector3& rayFrom,const btVector3& rayTo, btBroadphaseRayCallback& rayCallback, const btVector3& aabbMin,const btVector3& aabbMax) +{ + for (int i=0; i <= m_LastHandleIndex; i++) + { + btSimpleBroadphaseProxy* proxy = &m_pHandles[i]; + if(!proxy->m_clientObject) + { + continue; + } + rayCallback.process(proxy); + } +} + + + + + + + +bool btSimpleBroadphase::aabbOverlap(btSimpleBroadphaseProxy* proxy0,btSimpleBroadphaseProxy* proxy1) +{ + return proxy0->m_aabbMin[0] <= proxy1->m_aabbMax[0] && proxy1->m_aabbMin[0] <= proxy0->m_aabbMax[0] && + proxy0->m_aabbMin[1] <= proxy1->m_aabbMax[1] && proxy1->m_aabbMin[1] <= proxy0->m_aabbMax[1] && + proxy0->m_aabbMin[2] <= proxy1->m_aabbMax[2] && proxy1->m_aabbMin[2] <= proxy0->m_aabbMax[2]; + +} + + + +//then remove non-overlapping ones +class CheckOverlapCallback : public btOverlapCallback +{ +public: + virtual bool processOverlap(btBroadphasePair& pair) + { + return (!btSimpleBroadphase::aabbOverlap(static_cast(pair.m_pProxy0),static_cast(pair.m_pProxy1))); + } +}; + +void btSimpleBroadphase::calculateOverlappingPairs(btDispatcher* dispatcher) +{ + //first check for new overlapping pairs + int i,j; + if (m_numHandles >= 0) + { + int new_largest_index = -1; + for (i=0; i <= m_LastHandleIndex; i++) + { + btSimpleBroadphaseProxy* proxy0 = &m_pHandles[i]; + if(!proxy0->m_clientObject) + { + continue; + } + new_largest_index = i; + for (j=i+1; j <= m_LastHandleIndex; j++) + { + btSimpleBroadphaseProxy* proxy1 = &m_pHandles[j]; + btAssert(proxy0 != proxy1); + if(!proxy1->m_clientObject) + { + continue; + } + + btSimpleBroadphaseProxy* p0 = getSimpleProxyFromProxy(proxy0); + btSimpleBroadphaseProxy* p1 = getSimpleProxyFromProxy(proxy1); + + if (aabbOverlap(p0,p1)) + { + if ( !m_pairCache->findPair(proxy0,proxy1)) + { + m_pairCache->addOverlappingPair(proxy0,proxy1); + } + } else + { + if (!m_pairCache->hasDeferredRemoval()) + { + if ( m_pairCache->findPair(proxy0,proxy1)) + { + m_pairCache->removeOverlappingPair(proxy0,proxy1,dispatcher); + } + } + } + } + } + + m_LastHandleIndex = new_largest_index; + + if (m_ownsPairCache && m_pairCache->hasDeferredRemoval()) + { + + btBroadphasePairArray& overlappingPairArray = m_pairCache->getOverlappingPairArray(); + + //perform a sort, to find duplicates and to sort 'invalid' pairs to the end + overlappingPairArray.quickSort(btBroadphasePairSortPredicate()); + + overlappingPairArray.resize(overlappingPairArray.size() - m_invalidPair); + m_invalidPair = 0; + + + btBroadphasePair previousPair; + previousPair.m_pProxy0 = 0; + previousPair.m_pProxy1 = 0; + previousPair.m_algorithm = 0; + + + for (i=0;iprocessOverlap(pair); + } else + { + needsRemoval = true; + } + } else + { + //remove duplicate + needsRemoval = true; + //should have no algorithm + btAssert(!pair.m_algorithm); + } + + if (needsRemoval) + { + m_pairCache->cleanOverlappingPair(pair,dispatcher); + + // m_overlappingPairArray.swap(i,m_overlappingPairArray.size()-1); + // m_overlappingPairArray.pop_back(); + pair.m_pProxy0 = 0; + pair.m_pProxy1 = 0; + m_invalidPair++; + gOverlappingPairs--; + } + + } + + ///if you don't like to skip the invalid pairs in the array, execute following code: +#define CLEAN_INVALID_PAIRS 1 +#ifdef CLEAN_INVALID_PAIRS + + //perform a sort, to sort 'invalid' pairs to the end + overlappingPairArray.quickSort(btBroadphasePairSortPredicate()); + + overlappingPairArray.resize(overlappingPairArray.size() - m_invalidPair); + m_invalidPair = 0; +#endif//CLEAN_INVALID_PAIRS + + } + } +} + + +bool btSimpleBroadphase::testAabbOverlap(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1) +{ + btSimpleBroadphaseProxy* p0 = getSimpleProxyFromProxy(proxy0); + btSimpleBroadphaseProxy* p1 = getSimpleProxyFromProxy(proxy1); + return aabbOverlap(p0,p1); +} + +void btSimpleBroadphase::resetPool(btDispatcher* dispatcher) +{ + //not yet +} diff --git a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btSimpleBroadphase.h b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btSimpleBroadphase.h new file mode 100644 index 000000000..deffb0a7a --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btSimpleBroadphase.h @@ -0,0 +1,170 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 SIMPLE_BROADPHASE_H +#define SIMPLE_BROADPHASE_H + + +#include "btOverlappingPairCache.h" + + +struct btSimpleBroadphaseProxy : public btBroadphaseProxy +{ + int m_nextFree; + +// int m_handleId; + + + btSimpleBroadphaseProxy() {}; + + btSimpleBroadphaseProxy(const btVector3& minpt,const btVector3& maxpt,int shapeType,void* userPtr,short int collisionFilterGroup,short int collisionFilterMask,void* multiSapProxy) + :btBroadphaseProxy(minpt,maxpt,userPtr,collisionFilterGroup,collisionFilterMask,multiSapProxy) + { + (void)shapeType; + } + + + SIMD_FORCE_INLINE void SetNextFree(int next) {m_nextFree = next;} + SIMD_FORCE_INLINE int GetNextFree() const {return m_nextFree;} + + + + +}; + +///The SimpleBroadphase is just a unit-test for btAxisSweep3, bt32BitAxisSweep3, or btDbvtBroadphase, so use those classes instead. +///It is a brute force aabb culling broadphase based on O(n^2) aabb checks +class btSimpleBroadphase : public btBroadphaseInterface +{ + +protected: + + int m_numHandles; // number of active handles + int m_maxHandles; // max number of handles + int m_LastHandleIndex; + + btSimpleBroadphaseProxy* m_pHandles; // handles pool + + void* m_pHandlesRawPtr; + int m_firstFreeHandle; // free handles list + + int allocHandle() + { + btAssert(m_numHandles < m_maxHandles); + int freeHandle = m_firstFreeHandle; + m_firstFreeHandle = m_pHandles[freeHandle].GetNextFree(); + m_numHandles++; + if(freeHandle > m_LastHandleIndex) + { + m_LastHandleIndex = freeHandle; + } + return freeHandle; + } + + void freeHandle(btSimpleBroadphaseProxy* proxy) + { + int handle = int(proxy-m_pHandles); + btAssert(handle >= 0 && handle < m_maxHandles); + if(handle == m_LastHandleIndex) + { + m_LastHandleIndex--; + } + proxy->SetNextFree(m_firstFreeHandle); + m_firstFreeHandle = handle; + + proxy->m_clientObject = 0; + + m_numHandles--; + } + + btOverlappingPairCache* m_pairCache; + bool m_ownsPairCache; + + int m_invalidPair; + + + + inline btSimpleBroadphaseProxy* getSimpleProxyFromProxy(btBroadphaseProxy* proxy) + { + btSimpleBroadphaseProxy* proxy0 = static_cast(proxy); + return proxy0; + } + + inline const btSimpleBroadphaseProxy* getSimpleProxyFromProxy(btBroadphaseProxy* proxy) const + { + const btSimpleBroadphaseProxy* proxy0 = static_cast(proxy); + return proxy0; + } + + ///reset broadphase internal structures, to ensure determinism/reproducability + virtual void resetPool(btDispatcher* dispatcher); + + + void validate(); + +protected: + + + + +public: + btSimpleBroadphase(int maxProxies=16384,btOverlappingPairCache* overlappingPairCache=0); + virtual ~btSimpleBroadphase(); + + + static bool aabbOverlap(btSimpleBroadphaseProxy* proxy0,btSimpleBroadphaseProxy* proxy1); + + + virtual btBroadphaseProxy* createProxy( const btVector3& aabbMin, const btVector3& aabbMax,int shapeType,void* userPtr ,short int collisionFilterGroup,short int collisionFilterMask, btDispatcher* dispatcher,void* multiSapProxy); + + virtual void calculateOverlappingPairs(btDispatcher* dispatcher); + + virtual void destroyProxy(btBroadphaseProxy* proxy,btDispatcher* dispatcher); + virtual void setAabb(btBroadphaseProxy* proxy,const btVector3& aabbMin,const btVector3& aabbMax, btDispatcher* dispatcher); + virtual void getAabb(btBroadphaseProxy* proxy,btVector3& aabbMin, btVector3& aabbMax ) const; + + virtual void rayTest(const btVector3& rayFrom,const btVector3& rayTo, btBroadphaseRayCallback& rayCallback, const btVector3& aabbMin=btVector3(0,0,0),const btVector3& aabbMax=btVector3(0,0,0)); + + btOverlappingPairCache* getOverlappingPairCache() + { + return m_pairCache; + } + const btOverlappingPairCache* getOverlappingPairCache() const + { + return m_pairCache; + } + + bool testAabbOverlap(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1); + + + ///getAabb returns the axis aligned bounding box in the 'global' coordinate frame + ///will add some transform later + virtual void getBroadphaseAabb(btVector3& aabbMin,btVector3& aabbMax) const + { + aabbMin.setValue(-BT_LARGE_FLOAT,-BT_LARGE_FLOAT,-BT_LARGE_FLOAT); + aabbMax.setValue(BT_LARGE_FLOAT,BT_LARGE_FLOAT,BT_LARGE_FLOAT); + } + + virtual void printStats() + { +// printf("btSimpleBroadphase.h\n"); +// printf("numHandles = %d, maxHandles = %d\n",m_numHandles,m_maxHandles); + } +}; + + + +#endif //SIMPLE_BROADPHASE_H + diff --git a/Engine/lib/bullet/src/BulletCollision/CMakeLists.txt b/Engine/lib/bullet/src/BulletCollision/CMakeLists.txt new file mode 100644 index 000000000..0e2438b15 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CMakeLists.txt @@ -0,0 +1,267 @@ +INCLUDE_DIRECTORIES( ${BULLET_PHYSICS_SOURCE_DIR}/src } ) + +SET(BulletCollision_SRCS + BroadphaseCollision/btAxisSweep3.cpp + BroadphaseCollision/btBroadphaseProxy.cpp + BroadphaseCollision/btCollisionAlgorithm.cpp + BroadphaseCollision/btDbvt.cpp + BroadphaseCollision/btDbvtBroadphase.cpp + BroadphaseCollision/btDispatcher.cpp + BroadphaseCollision/btMultiSapBroadphase.cpp + BroadphaseCollision/btOverlappingPairCache.cpp + BroadphaseCollision/btQuantizedBvh.cpp + BroadphaseCollision/btSimpleBroadphase.cpp + CollisionDispatch/btActivatingCollisionAlgorithm.cpp + CollisionDispatch/btBoxBoxCollisionAlgorithm.cpp + CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp + CollisionDispatch/btBoxBoxDetector.cpp + CollisionDispatch/btCollisionDispatcher.cpp + CollisionDispatch/btCollisionObject.cpp + CollisionDispatch/btCollisionWorld.cpp + CollisionDispatch/btCompoundCollisionAlgorithm.cpp + CollisionDispatch/btConvexConcaveCollisionAlgorithm.cpp + CollisionDispatch/btConvexConvexAlgorithm.cpp + CollisionDispatch/btConvexPlaneCollisionAlgorithm.cpp + CollisionDispatch/btConvex2dConvex2dAlgorithm.cpp + CollisionDispatch/btDefaultCollisionConfiguration.cpp + CollisionDispatch/btEmptyCollisionAlgorithm.cpp + CollisionDispatch/btGhostObject.cpp + CollisionDispatch/btManifoldResult.cpp + CollisionDispatch/btSimulationIslandManager.cpp + CollisionDispatch/btSphereBoxCollisionAlgorithm.cpp + CollisionDispatch/btSphereSphereCollisionAlgorithm.cpp + CollisionDispatch/btSphereTriangleCollisionAlgorithm.cpp + CollisionDispatch/btUnionFind.cpp + CollisionDispatch/SphereTriangleDetector.cpp + CollisionShapes/btBoxShape.cpp + CollisionShapes/btBox2dShape.cpp + CollisionShapes/btBvhTriangleMeshShape.cpp + CollisionShapes/btCapsuleShape.cpp + CollisionShapes/btCollisionShape.cpp + CollisionShapes/btCompoundShape.cpp + CollisionShapes/btConcaveShape.cpp + CollisionShapes/btConeShape.cpp + CollisionShapes/btConvexHullShape.cpp + CollisionShapes/btConvexInternalShape.cpp + CollisionShapes/btConvexPointCloudShape.cpp + CollisionShapes/btConvexShape.cpp + CollisionShapes/btConvex2dShape.cpp + CollisionShapes/btConvexTriangleMeshShape.cpp + CollisionShapes/btCylinderShape.cpp + CollisionShapes/btEmptyShape.cpp + CollisionShapes/btHeightfieldTerrainShape.cpp + CollisionShapes/btMinkowskiSumShape.cpp + CollisionShapes/btMultimaterialTriangleMeshShape.cpp + CollisionShapes/btMultiSphereShape.cpp + CollisionShapes/btOptimizedBvh.cpp + CollisionShapes/btPolyhedralConvexShape.cpp + CollisionShapes/btScaledBvhTriangleMeshShape.cpp + CollisionShapes/btShapeHull.cpp + CollisionShapes/btSphereShape.cpp + CollisionShapes/btStaticPlaneShape.cpp + CollisionShapes/btStridingMeshInterface.cpp + CollisionShapes/btTetrahedronShape.cpp + CollisionShapes/btTriangleBuffer.cpp + CollisionShapes/btTriangleCallback.cpp + CollisionShapes/btTriangleIndexVertexArray.cpp + CollisionShapes/btTriangleIndexVertexMaterialArray.cpp + CollisionShapes/btTriangleMesh.cpp + CollisionShapes/btTriangleMeshShape.cpp + CollisionShapes/btUniformScalingShape.cpp + Gimpact/btContactProcessing.cpp + Gimpact/btGenericPoolAllocator.cpp + Gimpact/btGImpactBvh.cpp + Gimpact/btGImpactCollisionAlgorithm.cpp + Gimpact/btGImpactQuantizedBvh.cpp + Gimpact/btGImpactShape.cpp + Gimpact/btTriangleShapeEx.cpp + Gimpact/gim_box_set.cpp + Gimpact/gim_contact.cpp + Gimpact/gim_memory.cpp + Gimpact/gim_tri_collision.cpp + NarrowPhaseCollision/btContinuousConvexCollision.cpp + NarrowPhaseCollision/btConvexCast.cpp + NarrowPhaseCollision/btGjkConvexCast.cpp + NarrowPhaseCollision/btGjkEpa2.cpp + NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.cpp + NarrowPhaseCollision/btGjkPairDetector.cpp + NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.cpp + NarrowPhaseCollision/btPersistentManifold.cpp + NarrowPhaseCollision/btRaycastCallback.cpp + NarrowPhaseCollision/btSubSimplexConvexCast.cpp + NarrowPhaseCollision/btVoronoiSimplexSolver.cpp +) + +SET(Root_HDRS + ../btBulletCollisionCommon.h +) +SET(BroadphaseCollision_HDRS + BroadphaseCollision/btAxisSweep3.h + BroadphaseCollision/btBroadphaseInterface.h + BroadphaseCollision/btBroadphaseProxy.h + BroadphaseCollision/btCollisionAlgorithm.h + BroadphaseCollision/btDbvt.h + BroadphaseCollision/btDbvtBroadphase.h + BroadphaseCollision/btDispatcher.h + BroadphaseCollision/btMultiSapBroadphase.h + BroadphaseCollision/btOverlappingPairCache.h + BroadphaseCollision/btOverlappingPairCallback.h + BroadphaseCollision/btQuantizedBvh.h + BroadphaseCollision/btSimpleBroadphase.h +) +SET(CollisionDispatch_HDRS + CollisionDispatch/btActivatingCollisionAlgorithm.h + CollisionDispatch/btBoxBoxCollisionAlgorithm.h + CollisionDispatch/btBox2dBox2dCollisionAlgorithm.h + CollisionDispatch/btBoxBoxDetector.h + CollisionDispatch/btCollisionConfiguration.h + CollisionDispatch/btCollisionCreateFunc.h + CollisionDispatch/btCollisionDispatcher.h + CollisionDispatch/btCollisionObject.h + CollisionDispatch/btCollisionWorld.h + CollisionDispatch/btCompoundCollisionAlgorithm.h + CollisionDispatch/btConvexConcaveCollisionAlgorithm.h + CollisionDispatch/btConvexConvexAlgorithm.h + CollisionDispatch/btConvex2dConvex2dAlgorithm.h + CollisionDispatch/btConvexPlaneCollisionAlgorithm.h + CollisionDispatch/btDefaultCollisionConfiguration.h + CollisionDispatch/btEmptyCollisionAlgorithm.h + CollisionDispatch/btGhostObject.h + CollisionDispatch/btManifoldResult.h + CollisionDispatch/btSimulationIslandManager.h + CollisionDispatch/btSphereBoxCollisionAlgorithm.h + CollisionDispatch/btSphereSphereCollisionAlgorithm.h + CollisionDispatch/btSphereTriangleCollisionAlgorithm.h + CollisionDispatch/btUnionFind.h + CollisionDispatch/SphereTriangleDetector.h +) +SET(CollisionShapes_HDRS + CollisionShapes/btBoxShape.h + CollisionShapes/btBox2dShape.h + CollisionShapes/btBvhTriangleMeshShape.h + CollisionShapes/btCapsuleShape.h + CollisionShapes/btCollisionMargin.h + CollisionShapes/btCollisionShape.h + CollisionShapes/btCompoundShape.h + CollisionShapes/btConcaveShape.h + CollisionShapes/btConeShape.h + CollisionShapes/btConvexHullShape.h + CollisionShapes/btConvexInternalShape.h + CollisionShapes/btConvexPointCloudShape.h + CollisionShapes/btConvexShape.h + CollisionShapes/btConvex2dShape.h + CollisionShapes/btConvexTriangleMeshShape.h + CollisionShapes/btCylinderShape.h + CollisionShapes/btEmptyShape.h + CollisionShapes/btHeightfieldTerrainShape.h + CollisionShapes/btMaterial.h + CollisionShapes/btMinkowskiSumShape.h + CollisionShapes/btMultimaterialTriangleMeshShape.h + CollisionShapes/btMultiSphereShape.h + CollisionShapes/btOptimizedBvh.h + CollisionShapes/btPolyhedralConvexShape.h + CollisionShapes/btScaledBvhTriangleMeshShape.h + CollisionShapes/btShapeHull.h + CollisionShapes/btSphereShape.h + CollisionShapes/btStaticPlaneShape.h + CollisionShapes/btStridingMeshInterface.h + CollisionShapes/btTetrahedronShape.h + CollisionShapes/btTriangleBuffer.h + CollisionShapes/btTriangleCallback.h + CollisionShapes/btTriangleIndexVertexArray.h + CollisionShapes/btTriangleIndexVertexMaterialArray.h + CollisionShapes/btTriangleMesh.h + CollisionShapes/btTriangleMeshShape.h + CollisionShapes/btTriangleShape.h + CollisionShapes/btUniformScalingShape.h +) +SET(Gimpact_HDRS + Gimpact/btBoxCollision.h + Gimpact/btClipPolygon.h + Gimpact/btContactProcessing.h + Gimpact/btGenericPoolAllocator.h + Gimpact/btGeometryOperations.h + Gimpact/btGImpactBvh.h + Gimpact/btGImpactCollisionAlgorithm.h + Gimpact/btGImpactMassUtil.h + Gimpact/btGImpactQuantizedBvh.h + Gimpact/btGImpactShape.h + Gimpact/btQuantization.h + Gimpact/btTriangleShapeEx.h + Gimpact/gim_array.h + Gimpact/gim_basic_geometry_operations.h + Gimpact/gim_bitset.h + Gimpact/gim_box_collision.h + Gimpact/gim_box_set.h + Gimpact/gim_clip_polygon.h + Gimpact/gim_contact.h + Gimpact/gim_geom_types.h + Gimpact/gim_geometry.h + Gimpact/gim_hash_table.h + Gimpact/gim_linear_math.h + Gimpact/gim_math.h + Gimpact/gim_memory.h + Gimpact/gim_radixsort.h + Gimpact/gim_tri_collision.h +) +SET(NarrowPhaseCollision_HDRS + NarrowPhaseCollision/btContinuousConvexCollision.h + NarrowPhaseCollision/btConvexCast.h + NarrowPhaseCollision/btConvexPenetrationDepthSolver.h + NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h + NarrowPhaseCollision/btGjkConvexCast.h + NarrowPhaseCollision/btGjkEpa2.h + NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h + NarrowPhaseCollision/btGjkPairDetector.h + NarrowPhaseCollision/btManifoldPoint.h + NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.h + NarrowPhaseCollision/btPersistentManifold.h + NarrowPhaseCollision/btPointCollector.h + NarrowPhaseCollision/btRaycastCallback.h + NarrowPhaseCollision/btSimplexSolverInterface.h + NarrowPhaseCollision/btSubSimplexConvexCast.h + NarrowPhaseCollision/btVoronoiSimplexSolver.h +) + +SET(BulletCollision_HDRS + ${Root_HDRS} + ${BroadphaseCollision_HDRS} + ${CollisionDispatch_HDRS} + ${CollisionShapes_HDRS} + ${Gimpact_HDRS} + ${NarrowPhaseCollision_HDRS} +) + + +ADD_LIBRARY(BulletCollision ${BulletCollision_SRCS} ${BulletCollision_HDRS}) +SET_TARGET_PROPERTIES(BulletCollision PROPERTIES VERSION ${BULLET_VERSION}) +SET_TARGET_PROPERTIES(BulletCollision PROPERTIES SOVERSION ${BULLET_VERSION}) +IF (BUILD_SHARED_LIBS) + TARGET_LINK_LIBRARIES(BulletCollision LinearMath) +ENDIF (BUILD_SHARED_LIBS) + + + + +#INSTALL of other files requires CMake 2.6 +IF (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 2.5) + IF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) + INSTALL(TARGETS BulletCollision DESTINATION .) + ELSE (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) + INSTALL(TARGETS BulletCollision DESTINATION lib) + INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DESTINATION include FILES_MATCHING PATTERN "*.h") + ENDIF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) +ENDIF (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 2.5) + +IF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) + SET_TARGET_PROPERTIES(BulletCollision PROPERTIES FRAMEWORK true) + + SET_TARGET_PROPERTIES(BulletCollision PROPERTIES PUBLIC_HEADER "${Root_HDRS}") + # Have to list out sub-directories manually: + SET_PROPERTY(SOURCE ${BroadphaseCollision_HDRS} PROPERTY MACOSX_PACKAGE_LOCATION Headers/BroadphaseCollision) + SET_PROPERTY(SOURCE ${CollisionDispatch_HDRS} PROPERTY MACOSX_PACKAGE_LOCATION Headers/CollisionDispatch) + SET_PROPERTY(SOURCE ${CollisionShapes_HDRS} PROPERTY MACOSX_PACKAGE_LOCATION Headers/CollisionShapes) + SET_PROPERTY(SOURCE ${Gimpact_HDRS} PROPERTY MACOSX_PACKAGE_LOCATION Headers/Gimpact) + SET_PROPERTY(SOURCE ${NarrowPhaseCollision_HDRS} PROPERTY MACOSX_PACKAGE_LOCATION Headers/NarrowPhaseCollision) + +ENDIF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/SphereTriangleDetector.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/SphereTriangleDetector.cpp new file mode 100644 index 000000000..f76755fbb --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/SphereTriangleDetector.cpp @@ -0,0 +1,209 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "LinearMath/btScalar.h" +#include "SphereTriangleDetector.h" +#include "BulletCollision/CollisionShapes/btTriangleShape.h" +#include "BulletCollision/CollisionShapes/btSphereShape.h" + + +SphereTriangleDetector::SphereTriangleDetector(btSphereShape* sphere,btTriangleShape* triangle,btScalar contactBreakingThreshold) +:m_sphere(sphere), +m_triangle(triangle), +m_contactBreakingThreshold(contactBreakingThreshold) +{ + +} + +void SphereTriangleDetector::getClosestPoints(const ClosestPointInput& input,Result& output,class btIDebugDraw* debugDraw,bool swapResults) +{ + + (void)debugDraw; + const btTransform& transformA = input.m_transformA; + const btTransform& transformB = input.m_transformB; + + btVector3 point,normal; + btScalar timeOfImpact = btScalar(1.); + btScalar depth = btScalar(0.); +// output.m_distance = btScalar(BT_LARGE_FLOAT); + //move sphere into triangle space + btTransform sphereInTr = transformB.inverseTimes(transformA); + + if (collide(sphereInTr.getOrigin(),point,normal,depth,timeOfImpact,m_contactBreakingThreshold)) + { + if (swapResults) + { + btVector3 normalOnB = transformB.getBasis()*normal; + btVector3 normalOnA = -normalOnB; + btVector3 pointOnA = transformB*point+normalOnB*depth; + output.addContactPoint(normalOnA,pointOnA,depth); + } else + { + output.addContactPoint(transformB.getBasis()*normal,transformB*point,depth); + } + } + +} + +#define MAX_OVERLAP btScalar(0.) + + + +// See also geometrictools.com +// Basic idea: D = |p - (lo + t0*lv)| where t0 = lv . (p - lo) / lv . lv +btScalar SegmentSqrDistance(const btVector3& from, const btVector3& to,const btVector3 &p, btVector3 &nearest); + +btScalar SegmentSqrDistance(const btVector3& from, const btVector3& to,const btVector3 &p, btVector3 &nearest) { + btVector3 diff = p - from; + btVector3 v = to - from; + btScalar t = v.dot(diff); + + if (t > 0) { + btScalar dotVV = v.dot(v); + if (t < dotVV) { + t /= dotVV; + diff -= t*v; + } else { + t = 1; + diff -= v; + } + } else + t = 0; + + nearest = from + t*v; + return diff.dot(diff); +} + +bool SphereTriangleDetector::facecontains(const btVector3 &p,const btVector3* vertices,btVector3& normal) { + btVector3 lp(p); + btVector3 lnormal(normal); + + return pointInTriangle(vertices, lnormal, &lp); +} + +///combined discrete/continuous sphere-triangle +bool SphereTriangleDetector::collide(const btVector3& sphereCenter,btVector3 &point, btVector3& resultNormal, btScalar& depth, btScalar &timeOfImpact, btScalar contactBreakingThreshold) +{ + + const btVector3* vertices = &m_triangle->getVertexPtr(0); + const btVector3& c = sphereCenter; + btScalar r = m_sphere->getRadius(); + + btVector3 delta (0,0,0); + + btVector3 normal = (vertices[1]-vertices[0]).cross(vertices[2]-vertices[0]); + normal.normalize(); + btVector3 p1ToCentre = c - vertices[0]; + btScalar distanceFromPlane = p1ToCentre.dot(normal); + + if (distanceFromPlane < btScalar(0.)) + { + //triangle facing the other way + + distanceFromPlane *= btScalar(-1.); + normal *= btScalar(-1.); + } + + btScalar contactMargin = contactBreakingThreshold; + bool isInsideContactPlane = distanceFromPlane < r + contactMargin; + bool isInsideShellPlane = distanceFromPlane < r; + + btScalar deltaDotNormal = delta.dot(normal); + if (!isInsideShellPlane && deltaDotNormal >= btScalar(0.0)) + return false; + + // Check for contact / intersection + bool hasContact = false; + btVector3 contactPoint; + if (isInsideContactPlane) { + if (facecontains(c,vertices,normal)) { + // Inside the contact wedge - touches a point on the shell plane + hasContact = true; + contactPoint = c - normal*distanceFromPlane; + } else { + // Could be inside one of the contact capsules + btScalar contactCapsuleRadiusSqr = (r + contactMargin) * (r + contactMargin); + btVector3 nearestOnEdge; + for (int i = 0; i < m_triangle->getNumEdges(); i++) { + + btVector3 pa; + btVector3 pb; + + m_triangle->getEdge(i,pa,pb); + + btScalar distanceSqr = SegmentSqrDistance(pa,pb,c, nearestOnEdge); + if (distanceSqr < contactCapsuleRadiusSqr) { + // Yep, we're inside a capsule + hasContact = true; + contactPoint = nearestOnEdge; + } + + } + } + } + + if (hasContact) { + btVector3 contactToCentre = c - contactPoint; + btScalar distanceSqr = contactToCentre.length2(); + if (distanceSqr < (r - MAX_OVERLAP)*(r - MAX_OVERLAP)) { + btScalar distance = btSqrt(distanceSqr); + resultNormal = contactToCentre; + resultNormal.normalize(); + point = contactPoint; + depth = -(r-distance); + return true; + } + + if (delta.dot(contactToCentre) >= btScalar(0.0)) + return false; + + // Moving towards the contact point -> collision + point = contactPoint; + timeOfImpact = btScalar(0.0); + return true; + } + + return false; +} + + +bool SphereTriangleDetector::pointInTriangle(const btVector3 vertices[], const btVector3 &normal, btVector3 *p ) +{ + const btVector3* p1 = &vertices[0]; + const btVector3* p2 = &vertices[1]; + const btVector3* p3 = &vertices[2]; + + btVector3 edge1( *p2 - *p1 ); + btVector3 edge2( *p3 - *p2 ); + btVector3 edge3( *p1 - *p3 ); + + btVector3 p1_to_p( *p - *p1 ); + btVector3 p2_to_p( *p - *p2 ); + btVector3 p3_to_p( *p - *p3 ); + + btVector3 edge1_normal( edge1.cross(normal)); + btVector3 edge2_normal( edge2.cross(normal)); + btVector3 edge3_normal( edge3.cross(normal)); + + btScalar r1, r2, r3; + r1 = edge1_normal.dot( p1_to_p ); + r2 = edge2_normal.dot( p2_to_p ); + r3 = edge3_normal.dot( p3_to_p ); + if ( ( r1 > 0 && r2 > 0 && r3 > 0 ) || + ( r1 <= 0 && r2 <= 0 && r3 <= 0 ) ) + return true; + return false; + +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/SphereTriangleDetector.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/SphereTriangleDetector.h new file mode 100644 index 000000000..981bd54e7 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/SphereTriangleDetector.h @@ -0,0 +1,49 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 SPHERE_TRIANGLE_DETECTOR_H +#define SPHERE_TRIANGLE_DETECTOR_H + +#include "BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h" + + + +class btSphereShape; +class btTriangleShape; + + + +/// sphere-triangle to match the btDiscreteCollisionDetectorInterface +struct SphereTriangleDetector : public btDiscreteCollisionDetectorInterface +{ + virtual void getClosestPoints(const ClosestPointInput& input,Result& output,class btIDebugDraw* debugDraw,bool swapResults=false); + + SphereTriangleDetector(btSphereShape* sphere,btTriangleShape* triangle, btScalar contactBreakingThreshold); + + virtual ~SphereTriangleDetector() {}; + +private: + + bool collide(const btVector3& sphereCenter,btVector3 &point, btVector3& resultNormal, btScalar& depth, btScalar &timeOfImpact, btScalar contactBreakingThreshold); + bool pointInTriangle(const btVector3 vertices[], const btVector3 &normal, btVector3 *p ); + bool facecontains(const btVector3 &p,const btVector3* vertices,btVector3& normal); + + btSphereShape* m_sphere; + btTriangleShape* m_triangle; + btScalar m_contactBreakingThreshold; + +}; +#endif //SPHERE_TRIANGLE_DETECTOR_H + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btActivatingCollisionAlgorithm.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btActivatingCollisionAlgorithm.cpp new file mode 100644 index 000000000..7e5da6c58 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btActivatingCollisionAlgorithm.cpp @@ -0,0 +1,47 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2008 Erwin Coumans http://bulletphysics.com + +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 "btActivatingCollisionAlgorithm.h" +#include "btCollisionDispatcher.h" +#include "btCollisionObject.h" + +btActivatingCollisionAlgorithm::btActivatingCollisionAlgorithm (const btCollisionAlgorithmConstructionInfo& ci) +:btCollisionAlgorithm(ci) +//, +//m_colObj0(0), +//m_colObj1(0) +{ +} +btActivatingCollisionAlgorithm::btActivatingCollisionAlgorithm (const btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* colObj0,btCollisionObject* colObj1) +:btCollisionAlgorithm(ci) +//, +//m_colObj0(0), +//m_colObj1(0) +{ +// if (ci.m_dispatcher1->needsCollision(colObj0,colObj1)) +// { +// m_colObj0 = colObj0; +// m_colObj1 = colObj1; +// +// m_colObj0->activate(); +// m_colObj1->activate(); +// } +} + +btActivatingCollisionAlgorithm::~btActivatingCollisionAlgorithm() +{ +// m_colObj0->activate(); +// m_colObj1->activate(); +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btActivatingCollisionAlgorithm.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btActivatingCollisionAlgorithm.h new file mode 100644 index 000000000..25fe08894 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btActivatingCollisionAlgorithm.h @@ -0,0 +1,36 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2008 Erwin Coumans http://bulletphysics.com + +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 __BT_ACTIVATING_COLLISION_ALGORITHM_H +#define __BT_ACTIVATING_COLLISION_ALGORITHM_H + +#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" + +///This class is not enabled yet (work-in-progress) to more aggressively activate objects. +class btActivatingCollisionAlgorithm : public btCollisionAlgorithm +{ +// btCollisionObject* m_colObj0; +// btCollisionObject* m_colObj1; + +public: + + btActivatingCollisionAlgorithm (const btCollisionAlgorithmConstructionInfo& ci); + + btActivatingCollisionAlgorithm (const btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* colObj0,btCollisionObject* colObj1); + + virtual ~btActivatingCollisionAlgorithm(); + +}; +#endif //__BT_ACTIVATING_COLLISION_ALGORITHM_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp new file mode 100644 index 000000000..0dddedeca --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.cpp @@ -0,0 +1,437 @@ +/* +Bullet Continuous Collision Detection and Physics Library +* The b2CollidePolygons routines are Copyright (c) 2006-2007 Erin Catto http://www.gphysics.com + +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. +*/ + +///btBox2dBox2dCollisionAlgorithm, with modified b2CollidePolygons routines from the Box2D library. +///The modifications include: switching from b2Vec to btVector3, redefinition of b2Dot, b2Cross + +#include "btBox2dBox2dCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/CollisionShapes/btBoxShape.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "BulletCollision/CollisionDispatch/btBoxBoxDetector.h" +#include "BulletCollision/CollisionShapes/btBox2dShape.h" + +#define USE_PERSISTENT_CONTACTS 1 + +btBox2dBox2dCollisionAlgorithm::btBox2dBox2dCollisionAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* obj0,btCollisionObject* obj1) +: btActivatingCollisionAlgorithm(ci,obj0,obj1), +m_ownManifold(false), +m_manifoldPtr(mf) +{ + if (!m_manifoldPtr && m_dispatcher->needsCollision(obj0,obj1)) + { + m_manifoldPtr = m_dispatcher->getNewManifold(obj0,obj1); + m_ownManifold = true; + } +} + +btBox2dBox2dCollisionAlgorithm::~btBox2dBox2dCollisionAlgorithm() +{ + + if (m_ownManifold) + { + if (m_manifoldPtr) + m_dispatcher->releaseManifold(m_manifoldPtr); + } + +} + + +void b2CollidePolygons(btManifoldResult* manifold, const btBox2dShape* polyA, const btTransform& xfA, const btBox2dShape* polyB, const btTransform& xfB); + +//#include +void btBox2dBox2dCollisionAlgorithm::processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + if (!m_manifoldPtr) + return; + + btCollisionObject* col0 = body0; + btCollisionObject* col1 = body1; + btBox2dShape* box0 = (btBox2dShape*)col0->getCollisionShape(); + btBox2dShape* box1 = (btBox2dShape*)col1->getCollisionShape(); + + resultOut->setPersistentManifold(m_manifoldPtr); + + b2CollidePolygons(resultOut,box0,col0->getWorldTransform(),box1,col1->getWorldTransform()); + + // refreshContactPoints is only necessary when using persistent contact points. otherwise all points are newly added + if (m_ownManifold) + { + resultOut->refreshContactPoints(); + } + +} + +btScalar btBox2dBox2dCollisionAlgorithm::calculateTimeOfImpact(btCollisionObject* /*body0*/,btCollisionObject* /*body1*/,const btDispatcherInfo& /*dispatchInfo*/,btManifoldResult* /*resultOut*/) +{ + //not yet + return 1.f; +} + + +struct ClipVertex +{ + btVector3 v; + int id; + //b2ContactID id; + //b2ContactID id; +}; + +#define b2Dot(a,b) (a).dot(b) +#define b2Mul(a,b) (a)*(b) +#define b2MulT(a,b) (a).transpose()*(b) +#define b2Cross(a,b) (a).cross(b) +#define btCrossS(a,s) btVector3(s * a.getY(), -s * a.getX(),0.f) + +int b2_maxManifoldPoints =2; + +static int ClipSegmentToLine(ClipVertex vOut[2], ClipVertex vIn[2], + const btVector3& normal, btScalar offset) +{ + // Start with no output points + int numOut = 0; + + // Calculate the distance of end points to the line + btScalar distance0 = b2Dot(normal, vIn[0].v) - offset; + btScalar distance1 = b2Dot(normal, vIn[1].v) - offset; + + // If the points are behind the plane + if (distance0 <= 0.0f) vOut[numOut++] = vIn[0]; + if (distance1 <= 0.0f) vOut[numOut++] = vIn[1]; + + // If the points are on different sides of the plane + if (distance0 * distance1 < 0.0f) + { + // Find intersection point of edge and plane + btScalar interp = distance0 / (distance0 - distance1); + vOut[numOut].v = vIn[0].v + interp * (vIn[1].v - vIn[0].v); + if (distance0 > 0.0f) + { + vOut[numOut].id = vIn[0].id; + } + else + { + vOut[numOut].id = vIn[1].id; + } + ++numOut; + } + + return numOut; +} + +// Find the separation between poly1 and poly2 for a give edge normal on poly1. +static btScalar EdgeSeparation(const btBox2dShape* poly1, const btTransform& xf1, int edge1, + const btBox2dShape* poly2, const btTransform& xf2) +{ + int count1 = poly1->getVertexCount(); + const btVector3* vertices1 = poly1->getVertices(); + const btVector3* normals1 = poly1->getNormals(); + + int count2 = poly2->getVertexCount(); + const btVector3* vertices2 = poly2->getVertices(); + + btAssert(0 <= edge1 && edge1 < count1); + + // Convert normal from poly1's frame into poly2's frame. + btVector3 normal1World = b2Mul(xf1.getBasis(), normals1[edge1]); + btVector3 normal1 = b2MulT(xf2.getBasis(), normal1World); + + // Find support vertex on poly2 for -normal. + int index = 0; + btScalar minDot = BT_LARGE_FLOAT; + + for (int i = 0; i < count2; ++i) + { + btScalar dot = b2Dot(vertices2[i], normal1); + if (dot < minDot) + { + minDot = dot; + index = i; + } + } + + btVector3 v1 = b2Mul(xf1, vertices1[edge1]); + btVector3 v2 = b2Mul(xf2, vertices2[index]); + btScalar separation = b2Dot(v2 - v1, normal1World); + return separation; +} + +// Find the max separation between poly1 and poly2 using edge normals from poly1. +static btScalar FindMaxSeparation(int* edgeIndex, + const btBox2dShape* poly1, const btTransform& xf1, + const btBox2dShape* poly2, const btTransform& xf2) +{ + int count1 = poly1->getVertexCount(); + const btVector3* normals1 = poly1->getNormals(); + + // Vector pointing from the centroid of poly1 to the centroid of poly2. + btVector3 d = b2Mul(xf2, poly2->getCentroid()) - b2Mul(xf1, poly1->getCentroid()); + btVector3 dLocal1 = b2MulT(xf1.getBasis(), d); + + // Find edge normal on poly1 that has the largest projection onto d. + int edge = 0; + btScalar maxDot = -BT_LARGE_FLOAT; + for (int i = 0; i < count1; ++i) + { + btScalar dot = b2Dot(normals1[i], dLocal1); + if (dot > maxDot) + { + maxDot = dot; + edge = i; + } + } + + // Get the separation for the edge normal. + btScalar s = EdgeSeparation(poly1, xf1, edge, poly2, xf2); + if (s > 0.0f) + { + return s; + } + + // Check the separation for the previous edge normal. + int prevEdge = edge - 1 >= 0 ? edge - 1 : count1 - 1; + btScalar sPrev = EdgeSeparation(poly1, xf1, prevEdge, poly2, xf2); + if (sPrev > 0.0f) + { + return sPrev; + } + + // Check the separation for the next edge normal. + int nextEdge = edge + 1 < count1 ? edge + 1 : 0; + btScalar sNext = EdgeSeparation(poly1, xf1, nextEdge, poly2, xf2); + if (sNext > 0.0f) + { + return sNext; + } + + // Find the best edge and the search direction. + int bestEdge; + btScalar bestSeparation; + int increment; + if (sPrev > s && sPrev > sNext) + { + increment = -1; + bestEdge = prevEdge; + bestSeparation = sPrev; + } + else if (sNext > s) + { + increment = 1; + bestEdge = nextEdge; + bestSeparation = sNext; + } + else + { + *edgeIndex = edge; + return s; + } + + // Perform a local search for the best edge normal. + for ( ; ; ) + { + if (increment == -1) + edge = bestEdge - 1 >= 0 ? bestEdge - 1 : count1 - 1; + else + edge = bestEdge + 1 < count1 ? bestEdge + 1 : 0; + + s = EdgeSeparation(poly1, xf1, edge, poly2, xf2); + if (s > 0.0f) + { + return s; + } + + if (s > bestSeparation) + { + bestEdge = edge; + bestSeparation = s; + } + else + { + break; + } + } + + *edgeIndex = bestEdge; + return bestSeparation; +} + +static void FindIncidentEdge(ClipVertex c[2], + const btBox2dShape* poly1, const btTransform& xf1, int edge1, + const btBox2dShape* poly2, const btTransform& xf2) +{ + int count1 = poly1->getVertexCount(); + const btVector3* normals1 = poly1->getNormals(); + + int count2 = poly2->getVertexCount(); + const btVector3* vertices2 = poly2->getVertices(); + const btVector3* normals2 = poly2->getNormals(); + + btAssert(0 <= edge1 && edge1 < count1); + + // Get the normal of the reference edge in poly2's frame. + btVector3 normal1 = b2MulT(xf2.getBasis(), b2Mul(xf1.getBasis(), normals1[edge1])); + + // Find the incident edge on poly2. + int index = 0; + btScalar minDot = BT_LARGE_FLOAT; + for (int i = 0; i < count2; ++i) + { + btScalar dot = b2Dot(normal1, normals2[i]); + if (dot < minDot) + { + minDot = dot; + index = i; + } + } + + // Build the clip vertices for the incident edge. + int i1 = index; + int i2 = i1 + 1 < count2 ? i1 + 1 : 0; + + c[0].v = b2Mul(xf2, vertices2[i1]); +// c[0].id.features.referenceEdge = (unsigned char)edge1; +// c[0].id.features.incidentEdge = (unsigned char)i1; +// c[0].id.features.incidentVertex = 0; + + c[1].v = b2Mul(xf2, vertices2[i2]); +// c[1].id.features.referenceEdge = (unsigned char)edge1; +// c[1].id.features.incidentEdge = (unsigned char)i2; +// c[1].id.features.incidentVertex = 1; +} + +// Find edge normal of max separation on A - return if separating axis is found +// Find edge normal of max separation on B - return if separation axis is found +// Choose reference edge as min(minA, minB) +// Find incident edge +// Clip + +// The normal points from 1 to 2 +void b2CollidePolygons(btManifoldResult* manifold, + const btBox2dShape* polyA, const btTransform& xfA, + const btBox2dShape* polyB, const btTransform& xfB) +{ + + int edgeA = 0; + btScalar separationA = FindMaxSeparation(&edgeA, polyA, xfA, polyB, xfB); + if (separationA > 0.0f) + return; + + int edgeB = 0; + btScalar separationB = FindMaxSeparation(&edgeB, polyB, xfB, polyA, xfA); + if (separationB > 0.0f) + return; + + const btBox2dShape* poly1; // reference poly + const btBox2dShape* poly2; // incident poly + btTransform xf1, xf2; + int edge1; // reference edge + unsigned char flip; + const btScalar k_relativeTol = 0.98f; + const btScalar k_absoluteTol = 0.001f; + + // TODO_ERIN use "radius" of poly for absolute tolerance. + if (separationB > k_relativeTol * separationA + k_absoluteTol) + { + poly1 = polyB; + poly2 = polyA; + xf1 = xfB; + xf2 = xfA; + edge1 = edgeB; + flip = 1; + } + else + { + poly1 = polyA; + poly2 = polyB; + xf1 = xfA; + xf2 = xfB; + edge1 = edgeA; + flip = 0; + } + + ClipVertex incidentEdge[2]; + FindIncidentEdge(incidentEdge, poly1, xf1, edge1, poly2, xf2); + + int count1 = poly1->getVertexCount(); + const btVector3* vertices1 = poly1->getVertices(); + + btVector3 v11 = vertices1[edge1]; + btVector3 v12 = edge1 + 1 < count1 ? vertices1[edge1+1] : vertices1[0]; + + btVector3 dv = v12 - v11; + btVector3 sideNormal = b2Mul(xf1.getBasis(), v12 - v11); + sideNormal.normalize(); + btVector3 frontNormal = btCrossS(sideNormal, 1.0f); + + + v11 = b2Mul(xf1, v11); + v12 = b2Mul(xf1, v12); + + btScalar frontOffset = b2Dot(frontNormal, v11); + btScalar sideOffset1 = -b2Dot(sideNormal, v11); + btScalar sideOffset2 = b2Dot(sideNormal, v12); + + // Clip incident edge against extruded edge1 side edges. + ClipVertex clipPoints1[2]; + clipPoints1[0].v.setValue(0,0,0); + clipPoints1[1].v.setValue(0,0,0); + + ClipVertex clipPoints2[2]; + clipPoints2[0].v.setValue(0,0,0); + clipPoints2[1].v.setValue(0,0,0); + + + int np; + + // Clip to box side 1 + np = ClipSegmentToLine(clipPoints1, incidentEdge, -sideNormal, sideOffset1); + + if (np < 2) + return; + + // Clip to negative box side 1 + np = ClipSegmentToLine(clipPoints2, clipPoints1, sideNormal, sideOffset2); + + if (np < 2) + { + return; + } + + // Now clipPoints2 contains the clipped points. + btVector3 manifoldNormal = flip ? -frontNormal : frontNormal; + + int pointCount = 0; + for (int i = 0; i < b2_maxManifoldPoints; ++i) + { + btScalar separation = b2Dot(frontNormal, clipPoints2[i].v) - frontOffset; + + if (separation <= 0.0f) + { + + //b2ManifoldPoint* cp = manifold->points + pointCount; + //btScalar separation = separation; + //cp->localPoint1 = b2MulT(xfA, clipPoints2[i].v); + //cp->localPoint2 = b2MulT(xfB, clipPoints2[i].v); + + manifold->addContactPoint(-manifoldNormal,clipPoints2[i].v,separation); + +// cp->id = clipPoints2[i].id; +// cp->id.features.flip = flip; + ++pointCount; + } + } + +// manifold->pointCount = pointCount;} +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.h new file mode 100644 index 000000000..213421752 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.h @@ -0,0 +1,66 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 BOX_2D_BOX_2D__COLLISION_ALGORITHM_H +#define BOX_2D_BOX_2D__COLLISION_ALGORITHM_H + +#include "BulletCollision/CollisionDispatch/btActivatingCollisionAlgorithm.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "BulletCollision/BroadphaseCollision/btDispatcher.h" +#include "BulletCollision/CollisionDispatch/btCollisionCreateFunc.h" + +class btPersistentManifold; + +///box-box collision detection +class btBox2dBox2dCollisionAlgorithm : public btActivatingCollisionAlgorithm +{ + bool m_ownManifold; + btPersistentManifold* m_manifoldPtr; + +public: + btBox2dBox2dCollisionAlgorithm(const btCollisionAlgorithmConstructionInfo& ci) + : btActivatingCollisionAlgorithm(ci) {} + + virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + btBox2dBox2dCollisionAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* body0,btCollisionObject* body1); + + virtual ~btBox2dBox2dCollisionAlgorithm(); + + virtual void getAllContactManifolds(btManifoldArray& manifoldArray) + { + if (m_manifoldPtr && m_ownManifold) + { + manifoldArray.push_back(m_manifoldPtr); + } + } + + + struct CreateFunc :public btCollisionAlgorithmCreateFunc + { + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + int bbsize = sizeof(btBox2dBox2dCollisionAlgorithm); + void* ptr = ci.m_dispatcher1->allocateCollisionAlgorithm(bbsize); + return new(ptr) btBox2dBox2dCollisionAlgorithm(0,ci,body0,body1); + } + }; + +}; + +#endif //BOX_2D_BOX_2D__COLLISION_ALGORITHM_H + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btBoxBoxCollisionAlgorithm.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btBoxBoxCollisionAlgorithm.cpp new file mode 100644 index 000000000..496288534 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btBoxBoxCollisionAlgorithm.cpp @@ -0,0 +1,85 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btBoxBoxCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/CollisionShapes/btBoxShape.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "btBoxBoxDetector.h" + +#define USE_PERSISTENT_CONTACTS 1 + +btBoxBoxCollisionAlgorithm::btBoxBoxCollisionAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* obj0,btCollisionObject* obj1) +: btActivatingCollisionAlgorithm(ci,obj0,obj1), +m_ownManifold(false), +m_manifoldPtr(mf) +{ + if (!m_manifoldPtr && m_dispatcher->needsCollision(obj0,obj1)) + { + m_manifoldPtr = m_dispatcher->getNewManifold(obj0,obj1); + m_ownManifold = true; + } +} + +btBoxBoxCollisionAlgorithm::~btBoxBoxCollisionAlgorithm() +{ + if (m_ownManifold) + { + if (m_manifoldPtr) + m_dispatcher->releaseManifold(m_manifoldPtr); + } +} + +void btBoxBoxCollisionAlgorithm::processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + if (!m_manifoldPtr) + return; + + btCollisionObject* col0 = body0; + btCollisionObject* col1 = body1; + btBoxShape* box0 = (btBoxShape*)col0->getCollisionShape(); + btBoxShape* box1 = (btBoxShape*)col1->getCollisionShape(); + + + + /// report a contact. internally this will be kept persistent, and contact reduction is done + resultOut->setPersistentManifold(m_manifoldPtr); +#ifndef USE_PERSISTENT_CONTACTS + m_manifoldPtr->clearManifold(); +#endif //USE_PERSISTENT_CONTACTS + + btDiscreteCollisionDetectorInterface::ClosestPointInput input; + input.m_maximumDistanceSquared = BT_LARGE_FLOAT; + input.m_transformA = body0->getWorldTransform(); + input.m_transformB = body1->getWorldTransform(); + + btBoxBoxDetector detector(box0,box1); + detector.getClosestPoints(input,*resultOut,dispatchInfo.m_debugDraw); + +#ifdef USE_PERSISTENT_CONTACTS + // refreshContactPoints is only necessary when using persistent contact points. otherwise all points are newly added + if (m_ownManifold) + { + resultOut->refreshContactPoints(); + } +#endif //USE_PERSISTENT_CONTACTS + +} + +btScalar btBoxBoxCollisionAlgorithm::calculateTimeOfImpact(btCollisionObject* /*body0*/,btCollisionObject* /*body1*/,const btDispatcherInfo& /*dispatchInfo*/,btManifoldResult* /*resultOut*/) +{ + //not yet + return 1.f; +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btBoxBoxCollisionAlgorithm.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btBoxBoxCollisionAlgorithm.h new file mode 100644 index 000000000..e7d2cc25c --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btBoxBoxCollisionAlgorithm.h @@ -0,0 +1,66 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 BOX_BOX__COLLISION_ALGORITHM_H +#define BOX_BOX__COLLISION_ALGORITHM_H + +#include "btActivatingCollisionAlgorithm.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "BulletCollision/BroadphaseCollision/btDispatcher.h" +#include "BulletCollision/CollisionDispatch/btCollisionCreateFunc.h" + +class btPersistentManifold; + +///box-box collision detection +class btBoxBoxCollisionAlgorithm : public btActivatingCollisionAlgorithm +{ + bool m_ownManifold; + btPersistentManifold* m_manifoldPtr; + +public: + btBoxBoxCollisionAlgorithm(const btCollisionAlgorithmConstructionInfo& ci) + : btActivatingCollisionAlgorithm(ci) {} + + virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + btBoxBoxCollisionAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* body0,btCollisionObject* body1); + + virtual ~btBoxBoxCollisionAlgorithm(); + + virtual void getAllContactManifolds(btManifoldArray& manifoldArray) + { + if (m_manifoldPtr && m_ownManifold) + { + manifoldArray.push_back(m_manifoldPtr); + } + } + + + struct CreateFunc :public btCollisionAlgorithmCreateFunc + { + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + int bbsize = sizeof(btBoxBoxCollisionAlgorithm); + void* ptr = ci.m_dispatcher1->allocateCollisionAlgorithm(bbsize); + return new(ptr) btBoxBoxCollisionAlgorithm(0,ci,body0,body1); + } + }; + +}; + +#endif //BOX_BOX__COLLISION_ALGORITHM_H + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btBoxBoxDetector.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btBoxBoxDetector.cpp new file mode 100644 index 000000000..c802bea2b --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btBoxBoxDetector.cpp @@ -0,0 +1,689 @@ + +/* + * Box-Box collision detection re-distributed under the ZLib license with permission from Russell L. Smith + * Original version is from Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. + * All rights reserved. Email: russ@q12.org Web: www.q12.org + Bullet Continuous Collision Detection and Physics Library + Bullet is Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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. +*/ + +///ODE box-box collision detection is adapted to work with Bullet + +#include "btBoxBoxDetector.h" +#include "BulletCollision/CollisionShapes/btBoxShape.h" + +#include +#include + +btBoxBoxDetector::btBoxBoxDetector(btBoxShape* box1,btBoxShape* box2) +: m_box1(box1), +m_box2(box2) +{ + +} + + +// given two boxes (p1,R1,side1) and (p2,R2,side2), collide them together and +// generate contact points. this returns 0 if there is no contact otherwise +// it returns the number of contacts generated. +// `normal' returns the contact normal. +// `depth' returns the maximum penetration depth along that normal. +// `return_code' returns a number indicating the type of contact that was +// detected: +// 1,2,3 = box 2 intersects with a face of box 1 +// 4,5,6 = box 1 intersects with a face of box 2 +// 7..15 = edge-edge contact +// `maxc' is the maximum number of contacts allowed to be generated, i.e. +// the size of the `contact' array. +// `contact' and `skip' are the contact array information provided to the +// collision functions. this function only fills in the position and depth +// fields. +struct dContactGeom; +#define dDOTpq(a,b,p,q) ((a)[0]*(b)[0] + (a)[p]*(b)[q] + (a)[2*(p)]*(b)[2*(q)]) +#define dInfinity FLT_MAX + + +/*PURE_INLINE btScalar dDOT (const btScalar *a, const btScalar *b) { return dDOTpq(a,b,1,1); } +PURE_INLINE btScalar dDOT13 (const btScalar *a, const btScalar *b) { return dDOTpq(a,b,1,3); } +PURE_INLINE btScalar dDOT31 (const btScalar *a, const btScalar *b) { return dDOTpq(a,b,3,1); } +PURE_INLINE btScalar dDOT33 (const btScalar *a, const btScalar *b) { return dDOTpq(a,b,3,3); } +*/ +static btScalar dDOT (const btScalar *a, const btScalar *b) { return dDOTpq(a,b,1,1); } +static btScalar dDOT44 (const btScalar *a, const btScalar *b) { return dDOTpq(a,b,4,4); } +static btScalar dDOT41 (const btScalar *a, const btScalar *b) { return dDOTpq(a,b,4,1); } +static btScalar dDOT14 (const btScalar *a, const btScalar *b) { return dDOTpq(a,b,1,4); } +#define dMULTIPLYOP1_331(A,op,B,C) \ +{\ + (A)[0] op dDOT41((B),(C)); \ + (A)[1] op dDOT41((B+1),(C)); \ + (A)[2] op dDOT41((B+2),(C)); \ +} + +#define dMULTIPLYOP0_331(A,op,B,C) \ +{ \ + (A)[0] op dDOT((B),(C)); \ + (A)[1] op dDOT((B+4),(C)); \ + (A)[2] op dDOT((B+8),(C)); \ +} + +#define dMULTIPLY1_331(A,B,C) dMULTIPLYOP1_331(A,=,B,C) +#define dMULTIPLY0_331(A,B,C) dMULTIPLYOP0_331(A,=,B,C) + +typedef btScalar dMatrix3[4*3]; + +void dLineClosestApproach (const btVector3& pa, const btVector3& ua, + const btVector3& pb, const btVector3& ub, + btScalar *alpha, btScalar *beta); +void dLineClosestApproach (const btVector3& pa, const btVector3& ua, + const btVector3& pb, const btVector3& ub, + btScalar *alpha, btScalar *beta) +{ + btVector3 p; + p[0] = pb[0] - pa[0]; + p[1] = pb[1] - pa[1]; + p[2] = pb[2] - pa[2]; + btScalar uaub = dDOT(ua,ub); + btScalar q1 = dDOT(ua,p); + btScalar q2 = -dDOT(ub,p); + btScalar d = 1-uaub*uaub; + if (d <= btScalar(0.0001f)) { + // @@@ this needs to be made more robust + *alpha = 0; + *beta = 0; + } + else { + d = 1.f/d; + *alpha = (q1 + uaub*q2)*d; + *beta = (uaub*q1 + q2)*d; + } +} + + + +// find all the intersection points between the 2D rectangle with vertices +// at (+/-h[0],+/-h[1]) and the 2D quadrilateral with vertices (p[0],p[1]), +// (p[2],p[3]),(p[4],p[5]),(p[6],p[7]). +// +// the intersection points are returned as x,y pairs in the 'ret' array. +// the number of intersection points is returned by the function (this will +// be in the range 0 to 8). + +static int intersectRectQuad2 (btScalar h[2], btScalar p[8], btScalar ret[16]) +{ + // q (and r) contain nq (and nr) coordinate points for the current (and + // chopped) polygons + int nq=4,nr=0; + btScalar buffer[16]; + btScalar *q = p; + btScalar *r = ret; + for (int dir=0; dir <= 1; dir++) { + // direction notation: xy[0] = x axis, xy[1] = y axis + for (int sign=-1; sign <= 1; sign += 2) { + // chop q along the line xy[dir] = sign*h[dir] + btScalar *pq = q; + btScalar *pr = r; + nr = 0; + for (int i=nq; i > 0; i--) { + // go through all points in q and all lines between adjacent points + if (sign*pq[dir] < h[dir]) { + // this point is inside the chopping line + pr[0] = pq[0]; + pr[1] = pq[1]; + pr += 2; + nr++; + if (nr & 8) { + q = r; + goto done; + } + } + btScalar *nextq = (i > 1) ? pq+2 : q; + if ((sign*pq[dir] < h[dir]) ^ (sign*nextq[dir] < h[dir])) { + // this line crosses the chopping line + pr[1-dir] = pq[1-dir] + (nextq[1-dir]-pq[1-dir]) / + (nextq[dir]-pq[dir]) * (sign*h[dir]-pq[dir]); + pr[dir] = sign*h[dir]; + pr += 2; + nr++; + if (nr & 8) { + q = r; + goto done; + } + } + pq += 2; + } + q = r; + r = (q==ret) ? buffer : ret; + nq = nr; + } + } + done: + if (q != ret) memcpy (ret,q,nr*2*sizeof(btScalar)); + return nr; +} + + +#define M__PI 3.14159265f + +// given n points in the plane (array p, of size 2*n), generate m points that +// best represent the whole set. the definition of 'best' here is not +// predetermined - the idea is to select points that give good box-box +// collision detection behavior. the chosen point indexes are returned in the +// array iret (of size m). 'i0' is always the first entry in the array. +// n must be in the range [1..8]. m must be in the range [1..n]. i0 must be +// in the range [0..n-1]. + +void cullPoints2 (int n, btScalar p[], int m, int i0, int iret[]); +void cullPoints2 (int n, btScalar p[], int m, int i0, int iret[]) +{ + // compute the centroid of the polygon in cx,cy + int i,j; + btScalar a,cx,cy,q; + if (n==1) { + cx = p[0]; + cy = p[1]; + } + else if (n==2) { + cx = btScalar(0.5)*(p[0] + p[2]); + cy = btScalar(0.5)*(p[1] + p[3]); + } + else { + a = 0; + cx = 0; + cy = 0; + for (i=0; i<(n-1); i++) { + q = p[i*2]*p[i*2+3] - p[i*2+2]*p[i*2+1]; + a += q; + cx += q*(p[i*2]+p[i*2+2]); + cy += q*(p[i*2+1]+p[i*2+3]); + } + q = p[n*2-2]*p[1] - p[0]*p[n*2-1]; + if (btFabs(a+q) > SIMD_EPSILON) + { + a = 1.f/(btScalar(3.0)*(a+q)); + } else + { + a=BT_LARGE_FLOAT; + } + cx = a*(cx + q*(p[n*2-2]+p[0])); + cy = a*(cy + q*(p[n*2-1]+p[1])); + } + + // compute the angle of each point w.r.t. the centroid + btScalar A[8]; + for (i=0; i M__PI) a -= 2*M__PI; + btScalar maxdiff=1e9,diff; + + *iret = i0; // iret is not allowed to keep this value, but it sometimes does, when diff=#QNAN0 + + for (i=0; i M__PI) diff = 2*M__PI - diff; + if (diff < maxdiff) { + maxdiff = diff; + *iret = i; + } + } + } +#if defined(DEBUG) || defined (_DEBUG) + btAssert (*iret != i0); // ensure iret got set +#endif + avail[*iret] = 0; + iret++; + } +} + + + +int dBoxBox2 (const btVector3& p1, const dMatrix3 R1, + const btVector3& side1, const btVector3& p2, + const dMatrix3 R2, const btVector3& side2, + btVector3& normal, btScalar *depth, int *return_code, + int maxc, dContactGeom * /*contact*/, int /*skip*/,btDiscreteCollisionDetectorInterface::Result& output); +int dBoxBox2 (const btVector3& p1, const dMatrix3 R1, + const btVector3& side1, const btVector3& p2, + const dMatrix3 R2, const btVector3& side2, + btVector3& normal, btScalar *depth, int *return_code, + int maxc, dContactGeom * /*contact*/, int /*skip*/,btDiscreteCollisionDetectorInterface::Result& output) +{ + const btScalar fudge_factor = btScalar(1.05); + btVector3 p,pp,normalC(0.f,0.f,0.f); + const btScalar *normalR = 0; + btScalar A[3],B[3],R11,R12,R13,R21,R22,R23,R31,R32,R33, + Q11,Q12,Q13,Q21,Q22,Q23,Q31,Q32,Q33,s,s2,l; + int i,j,invert_normal,code; + + // get vector from centers of box 1 to box 2, relative to box 1 + p = p2 - p1; + dMULTIPLY1_331 (pp,R1,p); // get pp = p relative to body 1 + + // get side lengths / 2 + A[0] = side1[0]*btScalar(0.5); + A[1] = side1[1]*btScalar(0.5); + A[2] = side1[2]*btScalar(0.5); + B[0] = side2[0]*btScalar(0.5); + B[1] = side2[1]*btScalar(0.5); + B[2] = side2[2]*btScalar(0.5); + + // Rij is R1'*R2, i.e. the relative rotation between R1 and R2 + R11 = dDOT44(R1+0,R2+0); R12 = dDOT44(R1+0,R2+1); R13 = dDOT44(R1+0,R2+2); + R21 = dDOT44(R1+1,R2+0); R22 = dDOT44(R1+1,R2+1); R23 = dDOT44(R1+1,R2+2); + R31 = dDOT44(R1+2,R2+0); R32 = dDOT44(R1+2,R2+1); R33 = dDOT44(R1+2,R2+2); + + Q11 = btFabs(R11); Q12 = btFabs(R12); Q13 = btFabs(R13); + Q21 = btFabs(R21); Q22 = btFabs(R22); Q23 = btFabs(R23); + Q31 = btFabs(R31); Q32 = btFabs(R32); Q33 = btFabs(R33); + + // for all 15 possible separating axes: + // * see if the axis separates the boxes. if so, return 0. + // * find the depth of the penetration along the separating axis (s2) + // * if this is the largest depth so far, record it. + // the normal vector will be set to the separating axis with the smallest + // depth. note: normalR is set to point to a column of R1 or R2 if that is + // the smallest depth normal so far. otherwise normalR is 0 and normalC is + // set to a vector relative to body 1. invert_normal is 1 if the sign of + // the normal should be flipped. + +#define TST(expr1,expr2,norm,cc) \ + s2 = btFabs(expr1) - (expr2); \ + if (s2 > 0) return 0; \ + if (s2 > s) { \ + s = s2; \ + normalR = norm; \ + invert_normal = ((expr1) < 0); \ + code = (cc); \ + } + + s = -dInfinity; + invert_normal = 0; + code = 0; + + // separating axis = u1,u2,u3 + TST (pp[0],(A[0] + B[0]*Q11 + B[1]*Q12 + B[2]*Q13),R1+0,1); + TST (pp[1],(A[1] + B[0]*Q21 + B[1]*Q22 + B[2]*Q23),R1+1,2); + TST (pp[2],(A[2] + B[0]*Q31 + B[1]*Q32 + B[2]*Q33),R1+2,3); + + // separating axis = v1,v2,v3 + TST (dDOT41(R2+0,p),(A[0]*Q11 + A[1]*Q21 + A[2]*Q31 + B[0]),R2+0,4); + TST (dDOT41(R2+1,p),(A[0]*Q12 + A[1]*Q22 + A[2]*Q32 + B[1]),R2+1,5); + TST (dDOT41(R2+2,p),(A[0]*Q13 + A[1]*Q23 + A[2]*Q33 + B[2]),R2+2,6); + + // note: cross product axes need to be scaled when s is computed. + // normal (n1,n2,n3) is relative to box 1. +#undef TST +#define TST(expr1,expr2,n1,n2,n3,cc) \ + s2 = btFabs(expr1) - (expr2); \ + if (s2 > 0) return 0; \ + l = btSqrt((n1)*(n1) + (n2)*(n2) + (n3)*(n3)); \ + if (l > 0) { \ + s2 /= l; \ + if (s2*fudge_factor > s) { \ + s = s2; \ + normalR = 0; \ + normalC[0] = (n1)/l; normalC[1] = (n2)/l; normalC[2] = (n3)/l; \ + invert_normal = ((expr1) < 0); \ + code = (cc); \ + } \ + } + + // separating axis = u1 x (v1,v2,v3) + TST(pp[2]*R21-pp[1]*R31,(A[1]*Q31+A[2]*Q21+B[1]*Q13+B[2]*Q12),0,-R31,R21,7); + TST(pp[2]*R22-pp[1]*R32,(A[1]*Q32+A[2]*Q22+B[0]*Q13+B[2]*Q11),0,-R32,R22,8); + TST(pp[2]*R23-pp[1]*R33,(A[1]*Q33+A[2]*Q23+B[0]*Q12+B[1]*Q11),0,-R33,R23,9); + + // separating axis = u2 x (v1,v2,v3) + TST(pp[0]*R31-pp[2]*R11,(A[0]*Q31+A[2]*Q11+B[1]*Q23+B[2]*Q22),R31,0,-R11,10); + TST(pp[0]*R32-pp[2]*R12,(A[0]*Q32+A[2]*Q12+B[0]*Q23+B[2]*Q21),R32,0,-R12,11); + TST(pp[0]*R33-pp[2]*R13,(A[0]*Q33+A[2]*Q13+B[0]*Q22+B[1]*Q21),R33,0,-R13,12); + + // separating axis = u3 x (v1,v2,v3) + TST(pp[1]*R11-pp[0]*R21,(A[0]*Q21+A[1]*Q11+B[1]*Q33+B[2]*Q32),-R21,R11,0,13); + TST(pp[1]*R12-pp[0]*R22,(A[0]*Q22+A[1]*Q12+B[0]*Q33+B[2]*Q31),-R22,R12,0,14); + TST(pp[1]*R13-pp[0]*R23,(A[0]*Q23+A[1]*Q13+B[0]*Q32+B[1]*Q31),-R23,R13,0,15); + +#undef TST + + if (!code) return 0; + + // if we get to this point, the boxes interpenetrate. compute the normal + // in global coordinates. + if (normalR) { + normal[0] = normalR[0]; + normal[1] = normalR[4]; + normal[2] = normalR[8]; + } + else { + dMULTIPLY0_331 (normal,R1,normalC); + } + if (invert_normal) { + normal[0] = -normal[0]; + normal[1] = -normal[1]; + normal[2] = -normal[2]; + } + *depth = -s; + + // compute contact point(s) + + if (code > 6) { + // an edge from box 1 touches an edge from box 2. + // find a point pa on the intersecting edge of box 1 + btVector3 pa; + btScalar sign; + for (i=0; i<3; i++) pa[i] = p1[i]; + for (j=0; j<3; j++) { + sign = (dDOT14(normal,R1+j) > 0) ? btScalar(1.0) : btScalar(-1.0); + for (i=0; i<3; i++) pa[i] += sign * A[j] * R1[i*4+j]; + } + + // find a point pb on the intersecting edge of box 2 + btVector3 pb; + for (i=0; i<3; i++) pb[i] = p2[i]; + for (j=0; j<3; j++) { + sign = (dDOT14(normal,R2+j) > 0) ? btScalar(-1.0) : btScalar(1.0); + for (i=0; i<3; i++) pb[i] += sign * B[j] * R2[i*4+j]; + } + + btScalar alpha,beta; + btVector3 ua,ub; + for (i=0; i<3; i++) ua[i] = R1[((code)-7)/3 + i*4]; + for (i=0; i<3; i++) ub[i] = R2[((code)-7)%3 + i*4]; + + dLineClosestApproach (pa,ua,pb,ub,&alpha,&beta); + for (i=0; i<3; i++) pa[i] += ua[i]*alpha; + for (i=0; i<3; i++) pb[i] += ub[i]*beta; + + { + + //contact[0].pos[i] = btScalar(0.5)*(pa[i]+pb[i]); + //contact[0].depth = *depth; + btVector3 pointInWorld; + +#ifdef USE_CENTER_POINT + for (i=0; i<3; i++) + pointInWorld[i] = (pa[i]+pb[i])*btScalar(0.5); + output.addContactPoint(-normal,pointInWorld,-*depth); +#else + output.addContactPoint(-normal,pb,-*depth); +#endif // + *return_code = code; + } + return 1; + } + + // okay, we have a face-something intersection (because the separating + // axis is perpendicular to a face). define face 'a' to be the reference + // face (i.e. the normal vector is perpendicular to this) and face 'b' to be + // the incident face (the closest face of the other box). + + const btScalar *Ra,*Rb,*pa,*pb,*Sa,*Sb; + if (code <= 3) { + Ra = R1; + Rb = R2; + pa = p1; + pb = p2; + Sa = A; + Sb = B; + } + else { + Ra = R2; + Rb = R1; + pa = p2; + pb = p1; + Sa = B; + Sb = A; + } + + // nr = normal vector of reference face dotted with axes of incident box. + // anr = absolute values of nr. + btVector3 normal2,nr,anr; + if (code <= 3) { + normal2[0] = normal[0]; + normal2[1] = normal[1]; + normal2[2] = normal[2]; + } + else { + normal2[0] = -normal[0]; + normal2[1] = -normal[1]; + normal2[2] = -normal[2]; + } + dMULTIPLY1_331 (nr,Rb,normal2); + anr[0] = btFabs (nr[0]); + anr[1] = btFabs (nr[1]); + anr[2] = btFabs (nr[2]); + + // find the largest compontent of anr: this corresponds to the normal + // for the indident face. the other axis numbers of the indicent face + // are stored in a1,a2. + int lanr,a1,a2; + if (anr[1] > anr[0]) { + if (anr[1] > anr[2]) { + a1 = 0; + lanr = 1; + a2 = 2; + } + else { + a1 = 0; + a2 = 1; + lanr = 2; + } + } + else { + if (anr[0] > anr[2]) { + lanr = 0; + a1 = 1; + a2 = 2; + } + else { + a1 = 0; + a2 = 1; + lanr = 2; + } + } + + // compute center point of incident face, in reference-face coordinates + btVector3 center; + if (nr[lanr] < 0) { + for (i=0; i<3; i++) center[i] = pb[i] - pa[i] + Sb[lanr] * Rb[i*4+lanr]; + } + else { + for (i=0; i<3; i++) center[i] = pb[i] - pa[i] - Sb[lanr] * Rb[i*4+lanr]; + } + + // find the normal and non-normal axis numbers of the reference box + int codeN,code1,code2; + if (code <= 3) codeN = code-1; else codeN = code-4; + if (codeN==0) { + code1 = 1; + code2 = 2; + } + else if (codeN==1) { + code1 = 0; + code2 = 2; + } + else { + code1 = 0; + code2 = 1; + } + + // find the four corners of the incident face, in reference-face coordinates + btScalar quad[8]; // 2D coordinate of incident face (x,y pairs) + btScalar c1,c2,m11,m12,m21,m22; + c1 = dDOT14 (center,Ra+code1); + c2 = dDOT14 (center,Ra+code2); + // optimize this? - we have already computed this data above, but it is not + // stored in an easy-to-index format. for now it's quicker just to recompute + // the four dot products. + m11 = dDOT44 (Ra+code1,Rb+a1); + m12 = dDOT44 (Ra+code1,Rb+a2); + m21 = dDOT44 (Ra+code2,Rb+a1); + m22 = dDOT44 (Ra+code2,Rb+a2); + { + btScalar k1 = m11*Sb[a1]; + btScalar k2 = m21*Sb[a1]; + btScalar k3 = m12*Sb[a2]; + btScalar k4 = m22*Sb[a2]; + quad[0] = c1 - k1 - k3; + quad[1] = c2 - k2 - k4; + quad[2] = c1 - k1 + k3; + quad[3] = c2 - k2 + k4; + quad[4] = c1 + k1 + k3; + quad[5] = c2 + k2 + k4; + quad[6] = c1 + k1 - k3; + quad[7] = c2 + k2 - k4; + } + + // find the size of the reference face + btScalar rect[2]; + rect[0] = Sa[code1]; + rect[1] = Sa[code2]; + + // intersect the incident and reference faces + btScalar ret[16]; + int n = intersectRectQuad2 (rect,quad,ret); + if (n < 1) return 0; // this should never happen + + // convert the intersection points into reference-face coordinates, + // and compute the contact position and depth for each point. only keep + // those points that have a positive (penetrating) depth. delete points in + // the 'ret' array as necessary so that 'point' and 'ret' correspond. + btScalar point[3*8]; // penetrating contact points + btScalar dep[8]; // depths for those points + btScalar det1 = 1.f/(m11*m22 - m12*m21); + m11 *= det1; + m12 *= det1; + m21 *= det1; + m22 *= det1; + int cnum = 0; // number of penetrating contact points found + for (j=0; j < n; j++) { + btScalar k1 = m22*(ret[j*2]-c1) - m12*(ret[j*2+1]-c2); + btScalar k2 = -m21*(ret[j*2]-c1) + m11*(ret[j*2+1]-c2); + for (i=0; i<3; i++) point[cnum*3+i] = + center[i] + k1*Rb[i*4+a1] + k2*Rb[i*4+a2]; + dep[cnum] = Sa[codeN] - dDOT(normal2,point+cnum*3); + if (dep[cnum] >= 0) { + ret[cnum*2] = ret[j*2]; + ret[cnum*2+1] = ret[j*2+1]; + cnum++; + } + } + if (cnum < 1) return 0; // this should never happen + + // we can't generate more contacts than we actually have + if (maxc > cnum) maxc = cnum; + if (maxc < 1) maxc = 1; + + if (cnum <= maxc) { + // we have less contacts than we need, so we use them all + for (j=0; j < cnum; j++) { + + //AddContactPoint... + + //dContactGeom *con = CONTACT(contact,skip*j); + //for (i=0; i<3; i++) con->pos[i] = point[j*3+i] + pa[i]; + //con->depth = dep[j]; + + btVector3 pointInWorld; + for (i=0; i<3; i++) + pointInWorld[i] = point[j*3+i] + pa[i]; + output.addContactPoint(-normal,pointInWorld,-dep[j]); + + } + } + else { + // we have more contacts than are wanted, some of them must be culled. + // find the deepest point, it is always the first contact. + int i1 = 0; + btScalar maxdepth = dep[0]; + for (i=1; i maxdepth) { + maxdepth = dep[i]; + i1 = i; + } + } + + int iret[8]; + cullPoints2 (cnum,ret,maxc,i1,iret); + + for (j=0; j < maxc; j++) { +// dContactGeom *con = CONTACT(contact,skip*j); + // for (i=0; i<3; i++) con->pos[i] = point[iret[j]*3+i] + pa[i]; + // con->depth = dep[iret[j]]; + + btVector3 posInWorld; + for (i=0; i<3; i++) + posInWorld[i] = point[iret[j]*3+i] + pa[i]; + output.addContactPoint(-normal,posInWorld,-dep[iret[j]]); + } + cnum = maxc; + } + + *return_code = code; + return cnum; +} + +void btBoxBoxDetector::getClosestPoints(const ClosestPointInput& input,Result& output,class btIDebugDraw* /*debugDraw*/,bool /*swapResults*/) +{ + + const btTransform& transformA = input.m_transformA; + const btTransform& transformB = input.m_transformB; + + int skip = 0; + dContactGeom *contact = 0; + + dMatrix3 R1; + dMatrix3 R2; + + for (int j=0;j<3;j++) + { + R1[0+4*j] = transformA.getBasis()[j].x(); + R2[0+4*j] = transformB.getBasis()[j].x(); + + R1[1+4*j] = transformA.getBasis()[j].y(); + R2[1+4*j] = transformB.getBasis()[j].y(); + + + R1[2+4*j] = transformA.getBasis()[j].z(); + R2[2+4*j] = transformB.getBasis()[j].z(); + + } + + + + btVector3 normal; + btScalar depth; + int return_code; + int maxc = 4; + + + dBoxBox2 (transformA.getOrigin(), + R1, + 2.f*m_box1->getHalfExtentsWithMargin(), + transformB.getOrigin(), + R2, + 2.f*m_box2->getHalfExtentsWithMargin(), + normal, &depth, &return_code, + maxc, contact, skip, + output + ); + +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btBoxBoxDetector.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btBoxBoxDetector.h new file mode 100644 index 000000000..605294d47 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btBoxBoxDetector.h @@ -0,0 +1,44 @@ +/* + * Box-Box collision detection re-distributed under the ZLib license with permission from Russell L. Smith + * Original version is from Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. + * All rights reserved. Email: russ@q12.org Web: www.q12.org + +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 BOX_BOX_DETECTOR_H +#define BOX_BOX_DETECTOR_H + + +class btBoxShape; +#include "BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h" + + +/// btBoxBoxDetector wraps the ODE box-box collision detector +/// re-distributed under the Zlib license with permission from Russell L. Smith +struct btBoxBoxDetector : public btDiscreteCollisionDetectorInterface +{ + btBoxShape* m_box1; + btBoxShape* m_box2; + +public: + + btBoxBoxDetector(btBoxShape* box1,btBoxShape* box2); + + virtual ~btBoxBoxDetector() {}; + + virtual void getClosestPoints(const ClosestPointInput& input,Result& output,class btIDebugDraw* debugDraw,bool swapResults=false); + +}; + +#endif //BT_BOX_BOX_DETECTOR_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionConfiguration.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionConfiguration.h new file mode 100644 index 000000000..1db51a36d --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionConfiguration.h @@ -0,0 +1,47 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 BT_COLLISION_CONFIGURATION +#define BT_COLLISION_CONFIGURATION +struct btCollisionAlgorithmCreateFunc; + +class btStackAlloc; +class btPoolAllocator; + +///btCollisionConfiguration allows to configure Bullet collision detection +///stack allocator size, default collision algorithms and persistent manifold pool size +///@todo: describe the meaning +class btCollisionConfiguration +{ + +public: + + virtual ~btCollisionConfiguration() + { + } + + ///memory pools + virtual btPoolAllocator* getPersistentManifoldPool() = 0; + + virtual btPoolAllocator* getCollisionAlgorithmPool() = 0; + + virtual btStackAlloc* getStackAllocator() = 0; + + virtual btCollisionAlgorithmCreateFunc* getCollisionAlgorithmCreateFunc(int proxyType0,int proxyType1) =0; + +}; + +#endif //BT_COLLISION_CONFIGURATION + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionCreateFunc.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionCreateFunc.h new file mode 100644 index 000000000..a6da5f61a --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionCreateFunc.h @@ -0,0 +1,45 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 COLLISION_CREATE_FUNC +#define COLLISION_CREATE_FUNC + +#include "LinearMath/btAlignedObjectArray.h" +class btCollisionAlgorithm; +class btCollisionObject; + +struct btCollisionAlgorithmConstructionInfo; + +///Used by the btCollisionDispatcher to register and create instances for btCollisionAlgorithm +struct btCollisionAlgorithmCreateFunc +{ + bool m_swapped; + + btCollisionAlgorithmCreateFunc() + :m_swapped(false) + { + } + virtual ~btCollisionAlgorithmCreateFunc(){}; + + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& , btCollisionObject* body0,btCollisionObject* body1) + { + + (void)body0; + (void)body1; + return 0; + } +}; +#endif //COLLISION_CREATE_FUNC + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionDispatcher.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionDispatcher.cpp new file mode 100644 index 000000000..e6ff2130a --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionDispatcher.cpp @@ -0,0 +1,303 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btCollisionDispatcher.h" + + +#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" + +#include "BulletCollision/CollisionShapes/btCollisionShape.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h" +#include "LinearMath/btPoolAllocator.h" +#include "BulletCollision/CollisionDispatch/btCollisionConfiguration.h" + +int gNumManifold = 0; + +#ifdef BT_DEBUG +#include +#endif + + +btCollisionDispatcher::btCollisionDispatcher (btCollisionConfiguration* collisionConfiguration): + m_count(0), + m_useIslands(true), + m_staticWarningReported(false), + m_collisionConfiguration(collisionConfiguration) +{ + int i; + + setNearCallback(defaultNearCallback); + + m_collisionAlgorithmPoolAllocator = collisionConfiguration->getCollisionAlgorithmPool(); + + m_persistentManifoldPoolAllocator = collisionConfiguration->getPersistentManifoldPool(); + + for (i=0;igetCollisionAlgorithmCreateFunc(i,j); + btAssert(m_doubleDispatch[i][j]); + } + } + + +} + + +void btCollisionDispatcher::registerCollisionCreateFunc(int proxyType0, int proxyType1, btCollisionAlgorithmCreateFunc *createFunc) +{ + m_doubleDispatch[proxyType0][proxyType1] = createFunc; +} + +btCollisionDispatcher::~btCollisionDispatcher() +{ +} + +btPersistentManifold* btCollisionDispatcher::getNewManifold(void* b0,void* b1) +{ + gNumManifold++; + + //btAssert(gNumManifold < 65535); + + + btCollisionObject* body0 = (btCollisionObject*)b0; + btCollisionObject* body1 = (btCollisionObject*)b1; + + //test for Bullet 2.74: use a relative contact breaking threshold without clamping against 'gContactBreakingThreshold' + //btScalar contactBreakingThreshold = btMin(gContactBreakingThreshold,btMin(body0->getCollisionShape()->getContactBreakingThreshold(),body1->getCollisionShape()->getContactBreakingThreshold())); + btScalar contactBreakingThreshold = btMin(body0->getCollisionShape()->getContactBreakingThreshold(),body1->getCollisionShape()->getContactBreakingThreshold()); + + btScalar contactProcessingThreshold = btMin(body0->getContactProcessingThreshold(),body1->getContactProcessingThreshold()); + + void* mem = 0; + + if (m_persistentManifoldPoolAllocator->getFreeCount()) + { + mem = m_persistentManifoldPoolAllocator->allocate(sizeof(btPersistentManifold)); + } else + { + mem = btAlignedAlloc(sizeof(btPersistentManifold),16); + + } + btPersistentManifold* manifold = new(mem) btPersistentManifold (body0,body1,0,contactBreakingThreshold,contactProcessingThreshold); + manifold->m_index1a = m_manifoldsPtr.size(); + m_manifoldsPtr.push_back(manifold); + + return manifold; +} + +void btCollisionDispatcher::clearManifold(btPersistentManifold* manifold) +{ + manifold->clearManifold(); +} + + +void btCollisionDispatcher::releaseManifold(btPersistentManifold* manifold) +{ + + gNumManifold--; + + //printf("releaseManifold: gNumManifold %d\n",gNumManifold); + clearManifold(manifold); + + int findIndex = manifold->m_index1a; + btAssert(findIndex < m_manifoldsPtr.size()); + m_manifoldsPtr.swap(findIndex,m_manifoldsPtr.size()-1); + m_manifoldsPtr[findIndex]->m_index1a = findIndex; + m_manifoldsPtr.pop_back(); + + manifold->~btPersistentManifold(); + if (m_persistentManifoldPoolAllocator->validPtr(manifold)) + { + m_persistentManifoldPoolAllocator->freeMemory(manifold); + } else + { + btAlignedFree(manifold); + } + +} + + + +btCollisionAlgorithm* btCollisionDispatcher::findAlgorithm(btCollisionObject* body0,btCollisionObject* body1,btPersistentManifold* sharedManifold) +{ + + btCollisionAlgorithmConstructionInfo ci; + + ci.m_dispatcher1 = this; + ci.m_manifold = sharedManifold; + btCollisionAlgorithm* algo = m_doubleDispatch[body0->getCollisionShape()->getShapeType()][body1->getCollisionShape()->getShapeType()]->CreateCollisionAlgorithm(ci,body0,body1); + + return algo; +} + + + + +bool btCollisionDispatcher::needsResponse(btCollisionObject* body0,btCollisionObject* body1) +{ + //here you can do filtering + bool hasResponse = + (body0->hasContactResponse() && body1->hasContactResponse()); + //no response between two static/kinematic bodies: + hasResponse = hasResponse && + ((!body0->isStaticOrKinematicObject()) ||(! body1->isStaticOrKinematicObject())); + return hasResponse; +} + +bool btCollisionDispatcher::needsCollision(btCollisionObject* body0,btCollisionObject* body1) +{ + btAssert(body0); + btAssert(body1); + + bool needsCollision = true; + +#ifdef BT_DEBUG + if (!m_staticWarningReported) + { + //broadphase filtering already deals with this + if ((body0->isStaticObject() || body0->isKinematicObject()) && + (body1->isStaticObject() || body1->isKinematicObject())) + { + m_staticWarningReported = true; + printf("warning btCollisionDispatcher::needsCollision: static-static collision!\n"); + } + } +#endif //BT_DEBUG + + if ((!body0->isActive()) && (!body1->isActive())) + needsCollision = false; + else if (!body0->checkCollideWith(body1)) + needsCollision = false; + + return needsCollision ; + +} + + + +///interface for iterating all overlapping collision pairs, no matter how those pairs are stored (array, set, map etc) +///this is useful for the collision dispatcher. +class btCollisionPairCallback : public btOverlapCallback +{ + const btDispatcherInfo& m_dispatchInfo; + btCollisionDispatcher* m_dispatcher; + +public: + + btCollisionPairCallback(const btDispatcherInfo& dispatchInfo,btCollisionDispatcher* dispatcher) + :m_dispatchInfo(dispatchInfo), + m_dispatcher(dispatcher) + { + } + + /*btCollisionPairCallback& operator=(btCollisionPairCallback& other) + { + m_dispatchInfo = other.m_dispatchInfo; + m_dispatcher = other.m_dispatcher; + return *this; + } + */ + + + virtual ~btCollisionPairCallback() {} + + + virtual bool processOverlap(btBroadphasePair& pair) + { + (*m_dispatcher->getNearCallback())(pair,*m_dispatcher,m_dispatchInfo); + + return false; + } +}; + + + +void btCollisionDispatcher::dispatchAllCollisionPairs(btOverlappingPairCache* pairCache,const btDispatcherInfo& dispatchInfo,btDispatcher* dispatcher) +{ + //m_blockedForChanges = true; + + btCollisionPairCallback collisionCallback(dispatchInfo,this); + + pairCache->processAllOverlappingPairs(&collisionCallback,dispatcher); + + //m_blockedForChanges = false; + +} + + + + +//by default, Bullet will use this near callback +void btCollisionDispatcher::defaultNearCallback(btBroadphasePair& collisionPair, btCollisionDispatcher& dispatcher, const btDispatcherInfo& dispatchInfo) +{ + btCollisionObject* colObj0 = (btCollisionObject*)collisionPair.m_pProxy0->m_clientObject; + btCollisionObject* colObj1 = (btCollisionObject*)collisionPair.m_pProxy1->m_clientObject; + + if (dispatcher.needsCollision(colObj0,colObj1)) + { + //dispatcher will keep algorithms persistent in the collision pair + if (!collisionPair.m_algorithm) + { + collisionPair.m_algorithm = dispatcher.findAlgorithm(colObj0,colObj1); + } + + if (collisionPair.m_algorithm) + { + btManifoldResult contactPointResult(colObj0,colObj1); + + if (dispatchInfo.m_dispatchFunc == btDispatcherInfo::DISPATCH_DISCRETE) + { + //discrete collision detection query + collisionPair.m_algorithm->processCollision(colObj0,colObj1,dispatchInfo,&contactPointResult); + } else + { + //continuous collision detection query, time of impact (toi) + btScalar toi = collisionPair.m_algorithm->calculateTimeOfImpact(colObj0,colObj1,dispatchInfo,&contactPointResult); + if (dispatchInfo.m_timeOfImpact > toi) + dispatchInfo.m_timeOfImpact = toi; + + } + } + } + +} + + +void* btCollisionDispatcher::allocateCollisionAlgorithm(int size) +{ + if (m_collisionAlgorithmPoolAllocator->getFreeCount()) + { + return m_collisionAlgorithmPoolAllocator->allocate(size); + } + + //warn user for overflow? + return btAlignedAlloc(static_cast(size), 16); +} + +void btCollisionDispatcher::freeCollisionAlgorithm(void* ptr) +{ + if (m_collisionAlgorithmPoolAllocator->validPtr(ptr)) + { + m_collisionAlgorithmPoolAllocator->freeMemory(ptr); + } else + { + btAlignedFree(ptr); + } +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionDispatcher.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionDispatcher.h new file mode 100644 index 000000000..a9c9cd414 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionDispatcher.h @@ -0,0 +1,147 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 COLLISION__DISPATCHER_H +#define COLLISION__DISPATCHER_H + +#include "BulletCollision/BroadphaseCollision/btDispatcher.h" +#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" + +#include "BulletCollision/CollisionDispatch/btManifoldResult.h" + +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "LinearMath/btAlignedObjectArray.h" + +class btIDebugDraw; +class btOverlappingPairCache; +class btPoolAllocator; +class btCollisionConfiguration; + +#include "btCollisionCreateFunc.h" + +#define USE_DISPATCH_REGISTRY_ARRAY 1 + +class btCollisionDispatcher; +///user can override this nearcallback for collision filtering and more finegrained control over collision detection +typedef void (*btNearCallback)(btBroadphasePair& collisionPair, btCollisionDispatcher& dispatcher, const btDispatcherInfo& dispatchInfo); + + +///btCollisionDispatcher supports algorithms that handle ConvexConvex and ConvexConcave collision pairs. +///Time of Impact, Closest Points and Penetration Depth. +class btCollisionDispatcher : public btDispatcher +{ + int m_count; + + btAlignedObjectArray m_manifoldsPtr; + + bool m_useIslands; + + bool m_staticWarningReported; + + btManifoldResult m_defaultManifoldResult; + + btNearCallback m_nearCallback; + + btPoolAllocator* m_collisionAlgorithmPoolAllocator; + + btPoolAllocator* m_persistentManifoldPoolAllocator; + + btCollisionAlgorithmCreateFunc* m_doubleDispatch[MAX_BROADPHASE_COLLISION_TYPES][MAX_BROADPHASE_COLLISION_TYPES]; + + + btCollisionConfiguration* m_collisionConfiguration; + + +public: + + ///registerCollisionCreateFunc allows registration of custom/alternative collision create functions + void registerCollisionCreateFunc(int proxyType0,int proxyType1, btCollisionAlgorithmCreateFunc* createFunc); + + int getNumManifolds() const + { + return int( m_manifoldsPtr.size()); + } + + btPersistentManifold** getInternalManifoldPointer() + { + return &m_manifoldsPtr[0]; + } + + btPersistentManifold* getManifoldByIndexInternal(int index) + { + return m_manifoldsPtr[index]; + } + + const btPersistentManifold* getManifoldByIndexInternal(int index) const + { + return m_manifoldsPtr[index]; + } + + btCollisionDispatcher (btCollisionConfiguration* collisionConfiguration); + + virtual ~btCollisionDispatcher(); + + virtual btPersistentManifold* getNewManifold(void* b0,void* b1); + + virtual void releaseManifold(btPersistentManifold* manifold); + + + virtual void clearManifold(btPersistentManifold* manifold); + + + btCollisionAlgorithm* findAlgorithm(btCollisionObject* body0,btCollisionObject* body1,btPersistentManifold* sharedManifold = 0); + + virtual bool needsCollision(btCollisionObject* body0,btCollisionObject* body1); + + virtual bool needsResponse(btCollisionObject* body0,btCollisionObject* body1); + + virtual void dispatchAllCollisionPairs(btOverlappingPairCache* pairCache,const btDispatcherInfo& dispatchInfo,btDispatcher* dispatcher) ; + + void setNearCallback(btNearCallback nearCallback) + { + m_nearCallback = nearCallback; + } + + btNearCallback getNearCallback() const + { + return m_nearCallback; + } + + //by default, Bullet will use this near callback + static void defaultNearCallback(btBroadphasePair& collisionPair, btCollisionDispatcher& dispatcher, const btDispatcherInfo& dispatchInfo); + + virtual void* allocateCollisionAlgorithm(int size); + + virtual void freeCollisionAlgorithm(void* ptr); + + btCollisionConfiguration* getCollisionConfiguration() + { + return m_collisionConfiguration; + } + + const btCollisionConfiguration* getCollisionConfiguration() const + { + return m_collisionConfiguration; + } + + void setCollisionConfiguration(btCollisionConfiguration* config) + { + m_collisionConfiguration = config; + } + +}; + +#endif //COLLISION__DISPATCHER_H + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.cpp new file mode 100644 index 000000000..c04e70c53 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.cpp @@ -0,0 +1,68 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btCollisionObject.h" + +btCollisionObject::btCollisionObject() + : m_anisotropicFriction(1.f,1.f,1.f), + m_hasAnisotropicFriction(false), + m_contactProcessingThreshold(BT_LARGE_FLOAT), + m_broadphaseHandle(0), + m_collisionShape(0), + m_rootCollisionShape(0), + m_collisionFlags(btCollisionObject::CF_STATIC_OBJECT), + m_islandTag1(-1), + m_companionId(-1), + m_activationState1(1), + m_deactivationTime(btScalar(0.)), + m_friction(btScalar(0.5)), + m_restitution(btScalar(0.)), + m_userObjectPointer(0), + m_internalType(CO_COLLISION_OBJECT), + m_hitFraction(btScalar(1.)), + m_ccdSweptSphereRadius(btScalar(0.)), + m_ccdMotionThreshold(btScalar(0.)), + m_checkCollideWith(false) +{ + m_worldTransform.setIdentity(); +} + +btCollisionObject::~btCollisionObject() +{ +} + +void btCollisionObject::setActivationState(int newState) +{ + if ( (m_activationState1 != DISABLE_DEACTIVATION) && (m_activationState1 != DISABLE_SIMULATION)) + m_activationState1 = newState; +} + +void btCollisionObject::forceActivationState(int newState) +{ + m_activationState1 = newState; +} + +void btCollisionObject::activate(bool forceActivation) +{ + if (forceActivation || !(m_collisionFlags & (CF_STATIC_OBJECT|CF_KINEMATIC_OBJECT))) + { + setActivationState(ACTIVE_TAG); + m_deactivationTime = btScalar(0.); + } +} + + + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.h new file mode 100644 index 000000000..c4968c9b7 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.h @@ -0,0 +1,421 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 COLLISION_OBJECT_H +#define COLLISION_OBJECT_H + +#include "LinearMath/btTransform.h" + +//island management, m_activationState1 +#define ACTIVE_TAG 1 +#define ISLAND_SLEEPING 2 +#define WANTS_DEACTIVATION 3 +#define DISABLE_DEACTIVATION 4 +#define DISABLE_SIMULATION 5 + +struct btBroadphaseProxy; +class btCollisionShape; +#include "LinearMath/btMotionState.h" +#include "LinearMath/btAlignedAllocator.h" +#include "LinearMath/btAlignedObjectArray.h" + + +typedef btAlignedObjectArray btCollisionObjectArray; + + +/// btCollisionObject can be used to manage collision detection objects. +/// btCollisionObject maintains all information that is needed for a collision detection: Shape, Transform and AABB proxy. +/// They can be added to the btCollisionWorld. +ATTRIBUTE_ALIGNED16(class) btCollisionObject +{ + +protected: + + btTransform m_worldTransform; + + ///m_interpolationWorldTransform is used for CCD and interpolation + ///it can be either previous or future (predicted) transform + btTransform m_interpolationWorldTransform; + //those two are experimental: just added for bullet time effect, so you can still apply impulses (directly modifying velocities) + //without destroying the continuous interpolated motion (which uses this interpolation velocities) + btVector3 m_interpolationLinearVelocity; + btVector3 m_interpolationAngularVelocity; + + btVector3 m_anisotropicFriction; + bool m_hasAnisotropicFriction; + btScalar m_contactProcessingThreshold; + + btBroadphaseProxy* m_broadphaseHandle; + btCollisionShape* m_collisionShape; + + ///m_rootCollisionShape is temporarily used to store the original collision shape + ///The m_collisionShape might be temporarily replaced by a child collision shape during collision detection purposes + ///If it is NULL, the m_collisionShape is not temporarily replaced. + btCollisionShape* m_rootCollisionShape; + + int m_collisionFlags; + + int m_islandTag1; + int m_companionId; + + int m_activationState1; + btScalar m_deactivationTime; + + btScalar m_friction; + btScalar m_restitution; + + ///users can point to their objects, m_userPointer is not used by Bullet, see setUserPointer/getUserPointer + void* m_userObjectPointer; + + ///m_internalType is reserved to distinguish Bullet's btCollisionObject, btRigidBody, btSoftBody, btGhostObject etc. + ///do not assign your own m_internalType unless you write a new dynamics object class. + int m_internalType; + + ///time of impact calculation + btScalar m_hitFraction; + + ///Swept sphere radius (0.0 by default), see btConvexConvexAlgorithm:: + btScalar m_ccdSweptSphereRadius; + + /// Don't do continuous collision detection if the motion (in one step) is less then m_ccdMotionThreshold + btScalar m_ccdMotionThreshold; + + /// If some object should have elaborate collision filtering by sub-classes + bool m_checkCollideWith; + + char m_pad[7]; + + virtual bool checkCollideWithOverride(btCollisionObject* /* co */) + { + return true; + } + +public: + + BT_DECLARE_ALIGNED_ALLOCATOR(); + + enum CollisionFlags + { + CF_STATIC_OBJECT= 1, + CF_KINEMATIC_OBJECT= 2, + CF_NO_CONTACT_RESPONSE = 4, + CF_CUSTOM_MATERIAL_CALLBACK = 8,//this allows per-triangle material (friction/restitution) + CF_CHARACTER_OBJECT = 16 + }; + + enum CollisionObjectTypes + { + CO_COLLISION_OBJECT =1, + CO_RIGID_BODY, + ///CO_GHOST_OBJECT keeps track of all objects overlapping its AABB and that pass its collision filter + ///It is useful for collision sensors, explosion objects, character controller etc. + CO_GHOST_OBJECT, + CO_SOFT_BODY, + CO_HF_FLUID + }; + + SIMD_FORCE_INLINE bool mergesSimulationIslands() const + { + ///static objects, kinematic and object without contact response don't merge islands + return ((m_collisionFlags & (CF_STATIC_OBJECT | CF_KINEMATIC_OBJECT | CF_NO_CONTACT_RESPONSE) )==0); + } + + const btVector3& getAnisotropicFriction() const + { + return m_anisotropicFriction; + } + void setAnisotropicFriction(const btVector3& anisotropicFriction) + { + m_anisotropicFriction = anisotropicFriction; + m_hasAnisotropicFriction = (anisotropicFriction[0]!=1.f) || (anisotropicFriction[1]!=1.f) || (anisotropicFriction[2]!=1.f); + } + bool hasAnisotropicFriction() const + { + return m_hasAnisotropicFriction; + } + + ///the constraint solver can discard solving contacts, if the distance is above this threshold. 0 by default. + ///Note that using contacts with positive distance can improve stability. It increases, however, the chance of colliding with degerate contacts, such as 'interior' triangle edges + void setContactProcessingThreshold( btScalar contactProcessingThreshold) + { + m_contactProcessingThreshold = contactProcessingThreshold; + } + btScalar getContactProcessingThreshold() const + { + return m_contactProcessingThreshold; + } + + SIMD_FORCE_INLINE bool isStaticObject() const { + return (m_collisionFlags & CF_STATIC_OBJECT) != 0; + } + + SIMD_FORCE_INLINE bool isKinematicObject() const + { + return (m_collisionFlags & CF_KINEMATIC_OBJECT) != 0; + } + + SIMD_FORCE_INLINE bool isStaticOrKinematicObject() const + { + return (m_collisionFlags & (CF_KINEMATIC_OBJECT | CF_STATIC_OBJECT)) != 0 ; + } + + SIMD_FORCE_INLINE bool hasContactResponse() const { + return (m_collisionFlags & CF_NO_CONTACT_RESPONSE)==0; + } + + + btCollisionObject(); + + virtual ~btCollisionObject(); + + virtual void setCollisionShape(btCollisionShape* collisionShape) + { + m_collisionShape = collisionShape; + m_rootCollisionShape = collisionShape; + } + + SIMD_FORCE_INLINE const btCollisionShape* getCollisionShape() const + { + return m_collisionShape; + } + + SIMD_FORCE_INLINE btCollisionShape* getCollisionShape() + { + return m_collisionShape; + } + + SIMD_FORCE_INLINE const btCollisionShape* getRootCollisionShape() const + { + return m_rootCollisionShape; + } + + SIMD_FORCE_INLINE btCollisionShape* getRootCollisionShape() + { + return m_rootCollisionShape; + } + + ///Avoid using this internal API call + ///internalSetTemporaryCollisionShape is used to temporary replace the actual collision shape by a child collision shape. + void internalSetTemporaryCollisionShape(btCollisionShape* collisionShape) + { + m_collisionShape = collisionShape; + } + + SIMD_FORCE_INLINE int getActivationState() const { return m_activationState1;} + + void setActivationState(int newState); + + void setDeactivationTime(btScalar time) + { + m_deactivationTime = time; + } + btScalar getDeactivationTime() const + { + return m_deactivationTime; + } + + void forceActivationState(int newState); + + void activate(bool forceActivation = false); + + SIMD_FORCE_INLINE bool isActive() const + { + return ((getActivationState() != ISLAND_SLEEPING) && (getActivationState() != DISABLE_SIMULATION)); + } + + void setRestitution(btScalar rest) + { + m_restitution = rest; + } + btScalar getRestitution() const + { + return m_restitution; + } + void setFriction(btScalar frict) + { + m_friction = frict; + } + btScalar getFriction() const + { + return m_friction; + } + + ///reserved for Bullet internal usage + int getInternalType() const + { + return m_internalType; + } + + btTransform& getWorldTransform() + { + return m_worldTransform; + } + + const btTransform& getWorldTransform() const + { + return m_worldTransform; + } + + void setWorldTransform(const btTransform& worldTrans) + { + m_worldTransform = worldTrans; + } + + + SIMD_FORCE_INLINE btBroadphaseProxy* getBroadphaseHandle() + { + return m_broadphaseHandle; + } + + SIMD_FORCE_INLINE const btBroadphaseProxy* getBroadphaseHandle() const + { + return m_broadphaseHandle; + } + + void setBroadphaseHandle(btBroadphaseProxy* handle) + { + m_broadphaseHandle = handle; + } + + + const btTransform& getInterpolationWorldTransform() const + { + return m_interpolationWorldTransform; + } + + btTransform& getInterpolationWorldTransform() + { + return m_interpolationWorldTransform; + } + + void setInterpolationWorldTransform(const btTransform& trans) + { + m_interpolationWorldTransform = trans; + } + + void setInterpolationLinearVelocity(const btVector3& linvel) + { + m_interpolationLinearVelocity = linvel; + } + + void setInterpolationAngularVelocity(const btVector3& angvel) + { + m_interpolationAngularVelocity = angvel; + } + + const btVector3& getInterpolationLinearVelocity() const + { + return m_interpolationLinearVelocity; + } + + const btVector3& getInterpolationAngularVelocity() const + { + return m_interpolationAngularVelocity; + } + + SIMD_FORCE_INLINE int getIslandTag() const + { + return m_islandTag1; + } + + void setIslandTag(int tag) + { + m_islandTag1 = tag; + } + + SIMD_FORCE_INLINE int getCompanionId() const + { + return m_companionId; + } + + void setCompanionId(int id) + { + m_companionId = id; + } + + SIMD_FORCE_INLINE btScalar getHitFraction() const + { + return m_hitFraction; + } + + void setHitFraction(btScalar hitFraction) + { + m_hitFraction = hitFraction; + } + + + SIMD_FORCE_INLINE int getCollisionFlags() const + { + return m_collisionFlags; + } + + void setCollisionFlags(int flags) + { + m_collisionFlags = flags; + } + + ///Swept sphere radius (0.0 by default), see btConvexConvexAlgorithm:: + btScalar getCcdSweptSphereRadius() const + { + return m_ccdSweptSphereRadius; + } + + ///Swept sphere radius (0.0 by default), see btConvexConvexAlgorithm:: + void setCcdSweptSphereRadius(btScalar radius) + { + m_ccdSweptSphereRadius = radius; + } + + btScalar getCcdMotionThreshold() const + { + return m_ccdMotionThreshold; + } + + btScalar getCcdSquareMotionThreshold() const + { + return m_ccdMotionThreshold*m_ccdMotionThreshold; + } + + + + /// Don't do continuous collision detection if the motion (in one step) is less then m_ccdMotionThreshold + void setCcdMotionThreshold(btScalar ccdMotionThreshold) + { + m_ccdMotionThreshold = ccdMotionThreshold; + } + + ///users can point to their objects, userPointer is not used by Bullet + void* getUserPointer() const + { + return m_userObjectPointer; + } + + ///users can point to their objects, userPointer is not used by Bullet + void setUserPointer(void* userPointer) + { + m_userObjectPointer = userPointer; + } + + + inline bool checkCollideWith(btCollisionObject* co) + { + if (m_checkCollideWith) + return checkCollideWithOverride(co); + + return true; + } +}; + +#endif //COLLISION_OBJECT_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.cpp new file mode 100644 index 000000000..c5d5646ea --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.cpp @@ -0,0 +1,846 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btCollisionWorld.h" +#include "btCollisionDispatcher.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "BulletCollision/CollisionShapes/btCollisionShape.h" +#include "BulletCollision/CollisionShapes/btConvexShape.h" +#include "BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h" +#include "BulletCollision/CollisionShapes/btSphereShape.h" //for raycasting +#include "BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h" //for raycasting +#include "BulletCollision/NarrowPhaseCollision/btRaycastCallback.h" +#include "BulletCollision/CollisionShapes/btCompoundShape.h" +#include "BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.h" +#include "BulletCollision/NarrowPhaseCollision/btGjkConvexCast.h" +#include "BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.h" + +#include "BulletCollision/BroadphaseCollision/btBroadphaseInterface.h" +#include "LinearMath/btAabbUtil2.h" +#include "LinearMath/btQuickprof.h" +#include "LinearMath/btStackAlloc.h" + +//#define USE_BRUTEFORCE_RAYBROADPHASE 1 +//RECALCULATE_AABB is slower, but benefit is that you don't need to call 'stepSimulation' or 'updateAabbs' before using a rayTest +//#define RECALCULATE_AABB_RAYCAST 1 + +//When the user doesn't provide dispatcher or broadphase, create basic versions (and delete them in destructor) +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/BroadphaseCollision/btSimpleBroadphase.h" +#include "BulletCollision/CollisionDispatch/btCollisionConfiguration.h" + + +btCollisionWorld::btCollisionWorld(btDispatcher* dispatcher,btBroadphaseInterface* pairCache, btCollisionConfiguration* collisionConfiguration) +:m_dispatcher1(dispatcher), +m_broadphasePairCache(pairCache), +m_debugDrawer(0), +m_forceUpdateAllAabbs(true) +{ + m_stackAlloc = collisionConfiguration->getStackAllocator(); + m_dispatchInfo.m_stackAllocator = m_stackAlloc; +} + + +btCollisionWorld::~btCollisionWorld() +{ + + //clean up remaining objects + int i; + for (i=0;igetBroadphaseHandle(); + if (bp) + { + // + // only clear the cached algorithms + // + getBroadphase()->getOverlappingPairCache()->cleanProxyFromPairs(bp,m_dispatcher1); + getBroadphase()->destroyProxy(bp,m_dispatcher1); + collisionObject->setBroadphaseHandle(0); + } + } + + +} + + + + + + + + + + +void btCollisionWorld::addCollisionObject(btCollisionObject* collisionObject,short int collisionFilterGroup,short int collisionFilterMask) +{ + + btAssert(collisionObject); + + //check that the object isn't already added + btAssert( m_collisionObjects.findLinearSearch(collisionObject) == m_collisionObjects.size()); + + m_collisionObjects.push_back(collisionObject); + + //calculate new AABB + btTransform trans = collisionObject->getWorldTransform(); + + btVector3 minAabb; + btVector3 maxAabb; + collisionObject->getCollisionShape()->getAabb(trans,minAabb,maxAabb); + + int type = collisionObject->getCollisionShape()->getShapeType(); + collisionObject->setBroadphaseHandle( getBroadphase()->createProxy( + minAabb, + maxAabb, + type, + collisionObject, + collisionFilterGroup, + collisionFilterMask, + m_dispatcher1,0 + )) ; + + + + + +} + + + +void btCollisionWorld::updateSingleAabb(btCollisionObject* colObj) +{ + btVector3 minAabb,maxAabb; + colObj->getCollisionShape()->getAabb(colObj->getWorldTransform(), minAabb,maxAabb); + //need to increase the aabb for contact thresholds + btVector3 contactThreshold(gContactBreakingThreshold,gContactBreakingThreshold,gContactBreakingThreshold); + minAabb -= contactThreshold; + maxAabb += contactThreshold; + + btBroadphaseInterface* bp = (btBroadphaseInterface*)m_broadphasePairCache; + + //moving objects should be moderately sized, probably something wrong if not + if ( colObj->isStaticObject() || ((maxAabb-minAabb).length2() < btScalar(1e12))) + { + bp->setAabb(colObj->getBroadphaseHandle(),minAabb,maxAabb, m_dispatcher1); + } else + { + //something went wrong, investigate + //this assert is unwanted in 3D modelers (danger of loosing work) + colObj->setActivationState(DISABLE_SIMULATION); + + static bool reportMe = true; + if (reportMe && m_debugDrawer) + { + reportMe = false; + m_debugDrawer->reportErrorWarning("Overflow in AABB, object removed from simulation"); + m_debugDrawer->reportErrorWarning("If you can reproduce this, please email bugs@continuousphysics.com\n"); + m_debugDrawer->reportErrorWarning("Please include above information, your Platform, version of OS.\n"); + m_debugDrawer->reportErrorWarning("Thanks.\n"); + } + } +} + +void btCollisionWorld::updateAabbs() +{ + BT_PROFILE("updateAabbs"); + + btTransform predictedTrans; + for ( int i=0;iisActive()) + { + updateSingleAabb(colObj); + } + } +} + + + +void btCollisionWorld::performDiscreteCollisionDetection() +{ + BT_PROFILE("performDiscreteCollisionDetection"); + + btDispatcherInfo& dispatchInfo = getDispatchInfo(); + + updateAabbs(); + + { + BT_PROFILE("calculateOverlappingPairs"); + m_broadphasePairCache->calculateOverlappingPairs(m_dispatcher1); + } + + + btDispatcher* dispatcher = getDispatcher(); + { + BT_PROFILE("dispatchAllCollisionPairs"); + if (dispatcher) + dispatcher->dispatchAllCollisionPairs(m_broadphasePairCache->getOverlappingPairCache(),dispatchInfo,m_dispatcher1); + } + +} + + + +void btCollisionWorld::removeCollisionObject(btCollisionObject* collisionObject) +{ + + + //bool removeFromBroadphase = false; + + { + + btBroadphaseProxy* bp = collisionObject->getBroadphaseHandle(); + if (bp) + { + // + // only clear the cached algorithms + // + getBroadphase()->getOverlappingPairCache()->cleanProxyFromPairs(bp,m_dispatcher1); + getBroadphase()->destroyProxy(bp,m_dispatcher1); + collisionObject->setBroadphaseHandle(0); + } + } + + + //swapremove + m_collisionObjects.remove(collisionObject); + +} + + + +void btCollisionWorld::rayTestSingle(const btTransform& rayFromTrans,const btTransform& rayToTrans, + btCollisionObject* collisionObject, + const btCollisionShape* collisionShape, + const btTransform& colObjWorldTransform, + RayResultCallback& resultCallback) +{ + btSphereShape pointShape(btScalar(0.0)); + pointShape.setMargin(0.f); + const btConvexShape* castShape = &pointShape; + + if (collisionShape->isConvex()) + { +// BT_PROFILE("rayTestConvex"); + btConvexCast::CastResult castResult; + castResult.m_fraction = resultCallback.m_closestHitFraction; + + btConvexShape* convexShape = (btConvexShape*) collisionShape; + btVoronoiSimplexSolver simplexSolver; +#define USE_SUBSIMPLEX_CONVEX_CAST 1 +#ifdef USE_SUBSIMPLEX_CONVEX_CAST + btSubsimplexConvexCast convexCaster(castShape,convexShape,&simplexSolver); +#else + //btGjkConvexCast convexCaster(castShape,convexShape,&simplexSolver); + //btContinuousConvexCollision convexCaster(castShape,convexShape,&simplexSolver,0); +#endif //#USE_SUBSIMPLEX_CONVEX_CAST + + if (convexCaster.calcTimeOfImpact(rayFromTrans,rayToTrans,colObjWorldTransform,colObjWorldTransform,castResult)) + { + //add hit + if (castResult.m_normal.length2() > btScalar(0.0001)) + { + if (castResult.m_fraction < resultCallback.m_closestHitFraction) + { +#ifdef USE_SUBSIMPLEX_CONVEX_CAST + //rotate normal into worldspace + castResult.m_normal = rayFromTrans.getBasis() * castResult.m_normal; +#endif //USE_SUBSIMPLEX_CONVEX_CAST + + castResult.m_normal.normalize(); + btCollisionWorld::LocalRayResult localRayResult + ( + collisionObject, + 0, + castResult.m_normal, + castResult.m_fraction + ); + + bool normalInWorldSpace = true; + resultCallback.addSingleResult(localRayResult, normalInWorldSpace); + + } + } + } + } else { + if (collisionShape->isConcave()) + { +// BT_PROFILE("rayTestConcave"); + if (collisionShape->getShapeType()==TRIANGLE_MESH_SHAPE_PROXYTYPE) + { + ///optimized version for btBvhTriangleMeshShape + btBvhTriangleMeshShape* triangleMesh = (btBvhTriangleMeshShape*)collisionShape; + btTransform worldTocollisionObject = colObjWorldTransform.inverse(); + btVector3 rayFromLocal = worldTocollisionObject * rayFromTrans.getOrigin(); + btVector3 rayToLocal = worldTocollisionObject * rayToTrans.getOrigin(); + + //ConvexCast::CastResult + struct BridgeTriangleRaycastCallback : public btTriangleRaycastCallback + { + btCollisionWorld::RayResultCallback* m_resultCallback; + btCollisionObject* m_collisionObject; + btTriangleMeshShape* m_triangleMesh; + + btTransform m_colObjWorldTransform; + + BridgeTriangleRaycastCallback( const btVector3& from,const btVector3& to, + btCollisionWorld::RayResultCallback* resultCallback, btCollisionObject* collisionObject,btTriangleMeshShape* triangleMesh,const btTransform& colObjWorldTransform): + //@BP Mod + btTriangleRaycastCallback(from,to, resultCallback->m_flags), + m_resultCallback(resultCallback), + m_collisionObject(collisionObject), + m_triangleMesh(triangleMesh), + m_colObjWorldTransform(colObjWorldTransform) + { + } + + + virtual btScalar reportHit(const btVector3& hitNormalLocal, btScalar hitFraction, int partId, int triangleIndex ) + { + btCollisionWorld::LocalShapeInfo shapeInfo; + shapeInfo.m_shapePart = partId; + shapeInfo.m_triangleIndex = triangleIndex; + + btVector3 hitNormalWorld = m_colObjWorldTransform.getBasis() * hitNormalLocal; + + btCollisionWorld::LocalRayResult rayResult + (m_collisionObject, + &shapeInfo, + hitNormalWorld, + hitFraction); + + bool normalInWorldSpace = true; + return m_resultCallback->addSingleResult(rayResult,normalInWorldSpace); + } + + }; + + BridgeTriangleRaycastCallback rcb(rayFromLocal,rayToLocal,&resultCallback,collisionObject,triangleMesh,colObjWorldTransform); + rcb.m_hitFraction = resultCallback.m_closestHitFraction; + triangleMesh->performRaycast(&rcb,rayFromLocal,rayToLocal); + } else + { + //generic (slower) case + btConcaveShape* concaveShape = (btConcaveShape*)collisionShape; + + btTransform worldTocollisionObject = colObjWorldTransform.inverse(); + + btVector3 rayFromLocal = worldTocollisionObject * rayFromTrans.getOrigin(); + btVector3 rayToLocal = worldTocollisionObject * rayToTrans.getOrigin(); + + //ConvexCast::CastResult + + struct BridgeTriangleRaycastCallback : public btTriangleRaycastCallback + { + btCollisionWorld::RayResultCallback* m_resultCallback; + btCollisionObject* m_collisionObject; + btConcaveShape* m_triangleMesh; + + btTransform m_colObjWorldTransform; + + BridgeTriangleRaycastCallback( const btVector3& from,const btVector3& to, + btCollisionWorld::RayResultCallback* resultCallback, btCollisionObject* collisionObject,btConcaveShape* triangleMesh, const btTransform& colObjWorldTransform): + //@BP Mod + btTriangleRaycastCallback(from,to, resultCallback->m_flags), + m_resultCallback(resultCallback), + m_collisionObject(collisionObject), + m_triangleMesh(triangleMesh), + m_colObjWorldTransform(colObjWorldTransform) + { + } + + + virtual btScalar reportHit(const btVector3& hitNormalLocal, btScalar hitFraction, int partId, int triangleIndex ) + { + btCollisionWorld::LocalShapeInfo shapeInfo; + shapeInfo.m_shapePart = partId; + shapeInfo.m_triangleIndex = triangleIndex; + + btVector3 hitNormalWorld = m_colObjWorldTransform.getBasis() * hitNormalLocal; + + btCollisionWorld::LocalRayResult rayResult + (m_collisionObject, + &shapeInfo, + hitNormalWorld, + hitFraction); + + bool normalInWorldSpace = true; + return m_resultCallback->addSingleResult(rayResult,normalInWorldSpace); + } + + }; + + + BridgeTriangleRaycastCallback rcb(rayFromLocal,rayToLocal,&resultCallback,collisionObject,concaveShape, colObjWorldTransform); + rcb.m_hitFraction = resultCallback.m_closestHitFraction; + + btVector3 rayAabbMinLocal = rayFromLocal; + rayAabbMinLocal.setMin(rayToLocal); + btVector3 rayAabbMaxLocal = rayFromLocal; + rayAabbMaxLocal.setMax(rayToLocal); + + concaveShape->processAllTriangles(&rcb,rayAabbMinLocal,rayAabbMaxLocal); + } + } else { +// BT_PROFILE("rayTestCompound"); + ///@todo: use AABB tree or other BVH acceleration structure, see btDbvt + if (collisionShape->isCompound()) + { + const btCompoundShape* compoundShape = static_cast(collisionShape); + int i=0; + for (i=0;igetNumChildShapes();i++) + { + btTransform childTrans = compoundShape->getChildTransform(i); + const btCollisionShape* childCollisionShape = compoundShape->getChildShape(i); + btTransform childWorldTrans = colObjWorldTransform * childTrans; + // replace collision shape so that callback can determine the triangle + btCollisionShape* saveCollisionShape = collisionObject->getCollisionShape(); + collisionObject->internalSetTemporaryCollisionShape((btCollisionShape*)childCollisionShape); + rayTestSingle(rayFromTrans,rayToTrans, + collisionObject, + childCollisionShape, + childWorldTrans, + resultCallback); + // restore + collisionObject->internalSetTemporaryCollisionShape(saveCollisionShape); + } + } + } + } +} + +void btCollisionWorld::objectQuerySingle(const btConvexShape* castShape,const btTransform& convexFromTrans,const btTransform& convexToTrans, + btCollisionObject* collisionObject, + const btCollisionShape* collisionShape, + const btTransform& colObjWorldTransform, + ConvexResultCallback& resultCallback, btScalar allowedPenetration) +{ + if (collisionShape->isConvex()) + { + //BT_PROFILE("convexSweepConvex"); + btConvexCast::CastResult castResult; + castResult.m_allowedPenetration = allowedPenetration; + castResult.m_fraction = resultCallback.m_closestHitFraction;//btScalar(1.);//?? + + btConvexShape* convexShape = (btConvexShape*) collisionShape; + btVoronoiSimplexSolver simplexSolver; + btGjkEpaPenetrationDepthSolver gjkEpaPenetrationSolver; + + btContinuousConvexCollision convexCaster1(castShape,convexShape,&simplexSolver,&gjkEpaPenetrationSolver); + //btGjkConvexCast convexCaster2(castShape,convexShape,&simplexSolver); + //btSubsimplexConvexCast convexCaster3(castShape,convexShape,&simplexSolver); + + btConvexCast* castPtr = &convexCaster1; + + + + if (castPtr->calcTimeOfImpact(convexFromTrans,convexToTrans,colObjWorldTransform,colObjWorldTransform,castResult)) + { + //add hit + if (castResult.m_normal.length2() > btScalar(0.0001)) + { + if (castResult.m_fraction < resultCallback.m_closestHitFraction) + { + castResult.m_normal.normalize(); + btCollisionWorld::LocalConvexResult localConvexResult + ( + collisionObject, + 0, + castResult.m_normal, + castResult.m_hitPoint, + castResult.m_fraction + ); + + bool normalInWorldSpace = true; + resultCallback.addSingleResult(localConvexResult, normalInWorldSpace); + + } + } + } + } else { + if (collisionShape->isConcave()) + { + if (collisionShape->getShapeType()==TRIANGLE_MESH_SHAPE_PROXYTYPE) + { + //BT_PROFILE("convexSweepbtBvhTriangleMesh"); + btBvhTriangleMeshShape* triangleMesh = (btBvhTriangleMeshShape*)collisionShape; + btTransform worldTocollisionObject = colObjWorldTransform.inverse(); + btVector3 convexFromLocal = worldTocollisionObject * convexFromTrans.getOrigin(); + btVector3 convexToLocal = worldTocollisionObject * convexToTrans.getOrigin(); + // rotation of box in local mesh space = MeshRotation^-1 * ConvexToRotation + btTransform rotationXform = btTransform(worldTocollisionObject.getBasis() * convexToTrans.getBasis()); + + //ConvexCast::CastResult + struct BridgeTriangleConvexcastCallback : public btTriangleConvexcastCallback + { + btCollisionWorld::ConvexResultCallback* m_resultCallback; + btCollisionObject* m_collisionObject; + btTriangleMeshShape* m_triangleMesh; + + BridgeTriangleConvexcastCallback(const btConvexShape* castShape, const btTransform& from,const btTransform& to, + btCollisionWorld::ConvexResultCallback* resultCallback, btCollisionObject* collisionObject,btTriangleMeshShape* triangleMesh, const btTransform& triangleToWorld): + btTriangleConvexcastCallback(castShape, from,to, triangleToWorld, triangleMesh->getMargin()), + m_resultCallback(resultCallback), + m_collisionObject(collisionObject), + m_triangleMesh(triangleMesh) + { + } + + + virtual btScalar reportHit(const btVector3& hitNormalLocal, const btVector3& hitPointLocal, btScalar hitFraction, int partId, int triangleIndex ) + { + btCollisionWorld::LocalShapeInfo shapeInfo; + shapeInfo.m_shapePart = partId; + shapeInfo.m_triangleIndex = triangleIndex; + if (hitFraction <= m_resultCallback->m_closestHitFraction) + { + + btCollisionWorld::LocalConvexResult convexResult + (m_collisionObject, + &shapeInfo, + hitNormalLocal, + hitPointLocal, + hitFraction); + + bool normalInWorldSpace = true; + + + return m_resultCallback->addSingleResult(convexResult,normalInWorldSpace); + } + return hitFraction; + } + + }; + + BridgeTriangleConvexcastCallback tccb(castShape, convexFromTrans,convexToTrans,&resultCallback,collisionObject,triangleMesh, colObjWorldTransform); + tccb.m_hitFraction = resultCallback.m_closestHitFraction; + btVector3 boxMinLocal, boxMaxLocal; + castShape->getAabb(rotationXform, boxMinLocal, boxMaxLocal); + triangleMesh->performConvexcast(&tccb,convexFromLocal,convexToLocal,boxMinLocal, boxMaxLocal); + } else + { + //BT_PROFILE("convexSweepConcave"); + btConcaveShape* concaveShape = (btConcaveShape*)collisionShape; + btTransform worldTocollisionObject = colObjWorldTransform.inverse(); + btVector3 convexFromLocal = worldTocollisionObject * convexFromTrans.getOrigin(); + btVector3 convexToLocal = worldTocollisionObject * convexToTrans.getOrigin(); + // rotation of box in local mesh space = MeshRotation^-1 * ConvexToRotation + btTransform rotationXform = btTransform(worldTocollisionObject.getBasis() * convexToTrans.getBasis()); + + //ConvexCast::CastResult + struct BridgeTriangleConvexcastCallback : public btTriangleConvexcastCallback + { + btCollisionWorld::ConvexResultCallback* m_resultCallback; + btCollisionObject* m_collisionObject; + btConcaveShape* m_triangleMesh; + + BridgeTriangleConvexcastCallback(const btConvexShape* castShape, const btTransform& from,const btTransform& to, + btCollisionWorld::ConvexResultCallback* resultCallback, btCollisionObject* collisionObject,btConcaveShape* triangleMesh, const btTransform& triangleToWorld): + btTriangleConvexcastCallback(castShape, from,to, triangleToWorld, triangleMesh->getMargin()), + m_resultCallback(resultCallback), + m_collisionObject(collisionObject), + m_triangleMesh(triangleMesh) + { + } + + + virtual btScalar reportHit(const btVector3& hitNormalLocal, const btVector3& hitPointLocal, btScalar hitFraction, int partId, int triangleIndex ) + { + btCollisionWorld::LocalShapeInfo shapeInfo; + shapeInfo.m_shapePart = partId; + shapeInfo.m_triangleIndex = triangleIndex; + if (hitFraction <= m_resultCallback->m_closestHitFraction) + { + + btCollisionWorld::LocalConvexResult convexResult + (m_collisionObject, + &shapeInfo, + hitNormalLocal, + hitPointLocal, + hitFraction); + + bool normalInWorldSpace = false; + + return m_resultCallback->addSingleResult(convexResult,normalInWorldSpace); + } + return hitFraction; + } + + }; + + BridgeTriangleConvexcastCallback tccb(castShape, convexFromTrans,convexToTrans,&resultCallback,collisionObject,concaveShape, colObjWorldTransform); + tccb.m_hitFraction = resultCallback.m_closestHitFraction; + btVector3 boxMinLocal, boxMaxLocal; + castShape->getAabb(rotationXform, boxMinLocal, boxMaxLocal); + + btVector3 rayAabbMinLocal = convexFromLocal; + rayAabbMinLocal.setMin(convexToLocal); + btVector3 rayAabbMaxLocal = convexFromLocal; + rayAabbMaxLocal.setMax(convexToLocal); + rayAabbMinLocal += boxMinLocal; + rayAabbMaxLocal += boxMaxLocal; + concaveShape->processAllTriangles(&tccb,rayAabbMinLocal,rayAabbMaxLocal); + } + } else { + ///@todo : use AABB tree or other BVH acceleration structure! + if (collisionShape->isCompound()) + { + BT_PROFILE("convexSweepCompound"); + const btCompoundShape* compoundShape = static_cast(collisionShape); + int i=0; + for (i=0;igetNumChildShapes();i++) + { + btTransform childTrans = compoundShape->getChildTransform(i); + const btCollisionShape* childCollisionShape = compoundShape->getChildShape(i); + btTransform childWorldTrans = colObjWorldTransform * childTrans; + // replace collision shape so that callback can determine the triangle + btCollisionShape* saveCollisionShape = collisionObject->getCollisionShape(); + collisionObject->internalSetTemporaryCollisionShape((btCollisionShape*)childCollisionShape); + objectQuerySingle(castShape, convexFromTrans,convexToTrans, + collisionObject, + childCollisionShape, + childWorldTrans, + resultCallback, allowedPenetration); + // restore + collisionObject->internalSetTemporaryCollisionShape(saveCollisionShape); + } + } + } + } +} + + +struct btSingleRayCallback : public btBroadphaseRayCallback +{ + + btVector3 m_rayFromWorld; + btVector3 m_rayToWorld; + btTransform m_rayFromTrans; + btTransform m_rayToTrans; + btVector3 m_hitNormal; + + const btCollisionWorld* m_world; + btCollisionWorld::RayResultCallback& m_resultCallback; + + btSingleRayCallback(const btVector3& rayFromWorld,const btVector3& rayToWorld,const btCollisionWorld* world,btCollisionWorld::RayResultCallback& resultCallback) + :m_rayFromWorld(rayFromWorld), + m_rayToWorld(rayToWorld), + m_world(world), + m_resultCallback(resultCallback) + { + m_rayFromTrans.setIdentity(); + m_rayFromTrans.setOrigin(m_rayFromWorld); + m_rayToTrans.setIdentity(); + m_rayToTrans.setOrigin(m_rayToWorld); + + btVector3 rayDir = (rayToWorld-rayFromWorld); + + rayDir.normalize (); + ///what about division by zero? --> just set rayDirection[i] to INF/BT_LARGE_FLOAT + m_rayDirectionInverse[0] = rayDir[0] == btScalar(0.0) ? btScalar(BT_LARGE_FLOAT) : btScalar(1.0) / rayDir[0]; + m_rayDirectionInverse[1] = rayDir[1] == btScalar(0.0) ? btScalar(BT_LARGE_FLOAT) : btScalar(1.0) / rayDir[1]; + m_rayDirectionInverse[2] = rayDir[2] == btScalar(0.0) ? btScalar(BT_LARGE_FLOAT) : btScalar(1.0) / rayDir[2]; + m_signs[0] = m_rayDirectionInverse[0] < 0.0; + m_signs[1] = m_rayDirectionInverse[1] < 0.0; + m_signs[2] = m_rayDirectionInverse[2] < 0.0; + + m_lambda_max = rayDir.dot(m_rayToWorld-m_rayFromWorld); + + } + + + + virtual bool process(const btBroadphaseProxy* proxy) + { + ///terminate further ray tests, once the closestHitFraction reached zero + if (m_resultCallback.m_closestHitFraction == btScalar(0.f)) + return false; + + btCollisionObject* collisionObject = (btCollisionObject*)proxy->m_clientObject; + + //only perform raycast if filterMask matches + if(m_resultCallback.needsCollision(collisionObject->getBroadphaseHandle())) + { + //RigidcollisionObject* collisionObject = ctrl->GetRigidcollisionObject(); + //btVector3 collisionObjectAabbMin,collisionObjectAabbMax; +#if 0 +#ifdef RECALCULATE_AABB + btVector3 collisionObjectAabbMin,collisionObjectAabbMax; + collisionObject->getCollisionShape()->getAabb(collisionObject->getWorldTransform(),collisionObjectAabbMin,collisionObjectAabbMax); +#else + //getBroadphase()->getAabb(collisionObject->getBroadphaseHandle(),collisionObjectAabbMin,collisionObjectAabbMax); + const btVector3& collisionObjectAabbMin = collisionObject->getBroadphaseHandle()->m_aabbMin; + const btVector3& collisionObjectAabbMax = collisionObject->getBroadphaseHandle()->m_aabbMax; +#endif +#endif + //btScalar hitLambda = m_resultCallback.m_closestHitFraction; + //culling already done by broadphase + //if (btRayAabb(m_rayFromWorld,m_rayToWorld,collisionObjectAabbMin,collisionObjectAabbMax,hitLambda,m_hitNormal)) + { + m_world->rayTestSingle(m_rayFromTrans,m_rayToTrans, + collisionObject, + collisionObject->getCollisionShape(), + collisionObject->getWorldTransform(), + m_resultCallback); + } + } + return true; + } +}; + +void btCollisionWorld::rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, RayResultCallback& resultCallback) const +{ + //BT_PROFILE("rayTest"); + /// use the broadphase to accelerate the search for objects, based on their aabb + /// and for each object with ray-aabb overlap, perform an exact ray test + btSingleRayCallback rayCB(rayFromWorld,rayToWorld,this,resultCallback); + +#ifndef USE_BRUTEFORCE_RAYBROADPHASE + m_broadphasePairCache->rayTest(rayFromWorld,rayToWorld,rayCB); +#else + for (int i=0;igetNumCollisionObjects();i++) + { + rayCB.process(m_collisionObjects[i]->getBroadphaseHandle()); + } +#endif //USE_BRUTEFORCE_RAYBROADPHASE + +} + + +struct btSingleSweepCallback : public btBroadphaseRayCallback +{ + + btTransform m_convexFromTrans; + btTransform m_convexToTrans; + btVector3 m_hitNormal; + const btCollisionWorld* m_world; + btCollisionWorld::ConvexResultCallback& m_resultCallback; + btScalar m_allowedCcdPenetration; + const btConvexShape* m_castShape; + + + btSingleSweepCallback(const btConvexShape* castShape, const btTransform& convexFromTrans,const btTransform& convexToTrans,const btCollisionWorld* world,btCollisionWorld::ConvexResultCallback& resultCallback,btScalar allowedPenetration) + :m_convexFromTrans(convexFromTrans), + m_convexToTrans(convexToTrans), + m_world(world), + m_resultCallback(resultCallback), + m_allowedCcdPenetration(allowedPenetration), + m_castShape(castShape) + { + btVector3 unnormalizedRayDir = (m_convexToTrans.getOrigin()-m_convexFromTrans.getOrigin()); + btVector3 rayDir = unnormalizedRayDir.normalized(); + ///what about division by zero? --> just set rayDirection[i] to INF/BT_LARGE_FLOAT + m_rayDirectionInverse[0] = rayDir[0] == btScalar(0.0) ? btScalar(BT_LARGE_FLOAT) : btScalar(1.0) / rayDir[0]; + m_rayDirectionInverse[1] = rayDir[1] == btScalar(0.0) ? btScalar(BT_LARGE_FLOAT) : btScalar(1.0) / rayDir[1]; + m_rayDirectionInverse[2] = rayDir[2] == btScalar(0.0) ? btScalar(BT_LARGE_FLOAT) : btScalar(1.0) / rayDir[2]; + m_signs[0] = m_rayDirectionInverse[0] < 0.0; + m_signs[1] = m_rayDirectionInverse[1] < 0.0; + m_signs[2] = m_rayDirectionInverse[2] < 0.0; + + m_lambda_max = rayDir.dot(unnormalizedRayDir); + + } + + virtual bool process(const btBroadphaseProxy* proxy) + { + ///terminate further convex sweep tests, once the closestHitFraction reached zero + if (m_resultCallback.m_closestHitFraction == btScalar(0.f)) + return false; + + btCollisionObject* collisionObject = (btCollisionObject*)proxy->m_clientObject; + + //only perform raycast if filterMask matches + if(m_resultCallback.needsCollision(collisionObject->getBroadphaseHandle())) { + //RigidcollisionObject* collisionObject = ctrl->GetRigidcollisionObject(); + m_world->objectQuerySingle(m_castShape, m_convexFromTrans,m_convexToTrans, + collisionObject, + collisionObject->getCollisionShape(), + collisionObject->getWorldTransform(), + m_resultCallback, + m_allowedCcdPenetration); + } + + return true; + } +}; + + + +void btCollisionWorld::convexSweepTest(const btConvexShape* castShape, const btTransform& convexFromWorld, const btTransform& convexToWorld, ConvexResultCallback& resultCallback, btScalar allowedCcdPenetration) const +{ + + BT_PROFILE("convexSweepTest"); + /// use the broadphase to accelerate the search for objects, based on their aabb + /// and for each object with ray-aabb overlap, perform an exact ray test + /// unfortunately the implementation for rayTest and convexSweepTest duplicated, albeit practically identical + + + + btTransform convexFromTrans,convexToTrans; + convexFromTrans = convexFromWorld; + convexToTrans = convexToWorld; + btVector3 castShapeAabbMin, castShapeAabbMax; + /* Compute AABB that encompasses angular movement */ + { + btVector3 linVel, angVel; + btTransformUtil::calculateVelocity (convexFromTrans, convexToTrans, 1.0, linVel, angVel); + btVector3 zeroLinVel; + zeroLinVel.setValue(0,0,0); + btTransform R; + R.setIdentity (); + R.setRotation (convexFromTrans.getRotation()); + castShape->calculateTemporalAabb (R, zeroLinVel, angVel, 1.0, castShapeAabbMin, castShapeAabbMax); + } + +#ifndef USE_BRUTEFORCE_RAYBROADPHASE + + btSingleSweepCallback convexCB(castShape,convexFromWorld,convexToWorld,this,resultCallback,allowedCcdPenetration); + + m_broadphasePairCache->rayTest(convexFromTrans.getOrigin(),convexToTrans.getOrigin(),convexCB,castShapeAabbMin,castShapeAabbMax); + +#else + /// go over all objects, and if the ray intersects their aabb + cast shape aabb, + // do a ray-shape query using convexCaster (CCD) + int i; + for (i=0;igetBroadphaseHandle())) { + //RigidcollisionObject* collisionObject = ctrl->GetRigidcollisionObject(); + btVector3 collisionObjectAabbMin,collisionObjectAabbMax; + collisionObject->getCollisionShape()->getAabb(collisionObject->getWorldTransform(),collisionObjectAabbMin,collisionObjectAabbMax); + AabbExpand (collisionObjectAabbMin, collisionObjectAabbMax, castShapeAabbMin, castShapeAabbMax); + btScalar hitLambda = btScalar(1.); //could use resultCallback.m_closestHitFraction, but needs testing + btVector3 hitNormal; + if (btRayAabb(convexFromWorld.getOrigin(),convexToWorld.getOrigin(),collisionObjectAabbMin,collisionObjectAabbMax,hitLambda,hitNormal)) + { + objectQuerySingle(castShape, convexFromTrans,convexToTrans, + collisionObject, + collisionObject->getCollisionShape(), + collisionObject->getWorldTransform(), + resultCallback, + allowedCcdPenetration); + } + } + } +#endif //USE_BRUTEFORCE_RAYBROADPHASE +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.h new file mode 100644 index 000000000..983037e69 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.h @@ -0,0 +1,422 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://bulletphysics.com/Bullet/ + +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. +*/ + + +/** + * @mainpage Bullet Documentation + * + * @section intro_sec Introduction + * Bullet Collision Detection & Physics SDK + * + * Bullet is a Collision Detection and Rigid Body Dynamics Library. The Library is Open Source and free for commercial use, under the ZLib license ( http://opensource.org/licenses/zlib-license.php ). + * + * There is the Physics Forum for feedback and general Collision Detection and Physics discussions. + * Please visit http://www.bulletphysics.com + * + * @section install_sec Installation + * + * @subsection step1 Step 1: Download + * You can download the Bullet Physics Library from the Google Code repository: http://code.google.com/p/bullet/downloads/list + * @subsection step2 Step 2: Building + * Bullet comes with autogenerated Project Files for Microsoft Visual Studio 6, 7, 7.1 and 8. + * The main Workspace/Solution is located in Bullet/msvc/8/wksbullet.sln (replace 8 with your version). + * + * Under other platforms, like Linux or Mac OS-X, Bullet can be build using either using make, cmake, http://www.cmake.org , or jam, http://www.perforce.com/jam/jam.html . cmake can autogenerate Xcode, KDevelop, MSVC and other build systems. just run cmake . in the root of Bullet. + * So if you are not using MSVC or cmake, you can run ./autogen.sh ./configure to create both Makefile and Jamfile and then run make or jam. + * Jam is a build system that can build the library, demos and also autogenerate the MSVC Project Files. + * If you don't have jam installed, you can make jam from the included jam-2.5 sources, or download jam from ftp://ftp.perforce.com/jam + * + * @subsection step3 Step 3: Testing demos + * Try to run and experiment with BasicDemo executable as a starting point. + * Bullet can be used in several ways, as Full Rigid Body simulation, as Collision Detector Library or Low Level / Snippets like the GJK Closest Point calculation. + * The Dependencies can be seen in this documentation under Directories + * + * @subsection step4 Step 4: Integrating in your application, full Rigid Body and Soft Body simulation + * Check out BasicDemo how to create a btDynamicsWorld, btRigidBody and btCollisionShape, Stepping the simulation and synchronizing your graphics object transform. + * Check out SoftDemo how to use soft body dynamics, using btSoftRigidDynamicsWorld. + * @subsection step5 Step 5 : Integrate the Collision Detection Library (without Dynamics and other Extras) + * Bullet Collision Detection can also be used without the Dynamics/Extras. + * Check out btCollisionWorld and btCollisionObject, and the CollisionInterfaceDemo. + * @subsection step6 Step 6 : Use Snippets like the GJK Closest Point calculation. + * Bullet has been designed in a modular way keeping dependencies to a minimum. The ConvexHullDistance demo demonstrates direct use of btGjkPairDetector. + * + * @section copyright Copyright + * Copyright (C) 2005-2008 Erwin Coumans, some contributions Copyright Gino van den Bergen, Christer Ericson, Simon Hobbs, Ricardo Padrela, F Richter(res), Stephane Redon + * Special thanks to all visitors of the Bullet Physics forum, and in particular above contributors, John McCutchan, Nathanael Presson, Dave Eberle, Dirk Gregorius, Erin Catto, Dave Eberle, Adam Moravanszky, + * Pierre Terdiman, Kenny Erleben, Russell Smith, Oliver Strunk, Jan Paul van Waveren, Marten Svanfeldt. + * + */ + + + +#ifndef COLLISION_WORLD_H +#define COLLISION_WORLD_H + +class btStackAlloc; +class btCollisionShape; +class btConvexShape; +class btBroadphaseInterface; +#include "LinearMath/btVector3.h" +#include "LinearMath/btTransform.h" +#include "btCollisionObject.h" +#include "btCollisionDispatcher.h" +#include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h" +#include "LinearMath/btAlignedObjectArray.h" + +///CollisionWorld is interface and container for the collision detection +class btCollisionWorld +{ + + +protected: + + btAlignedObjectArray m_collisionObjects; + + btDispatcher* m_dispatcher1; + + btDispatcherInfo m_dispatchInfo; + + btStackAlloc* m_stackAlloc; + + btBroadphaseInterface* m_broadphasePairCache; + + btIDebugDraw* m_debugDrawer; + + ///m_forceUpdateAllAabbs can be set to false as an optimization to only update active object AABBs + ///it is true by default, because it is error-prone (setting the position of static objects wouldn't update their AABB) + bool m_forceUpdateAllAabbs; + +public: + + //this constructor doesn't own the dispatcher and paircache/broadphase + btCollisionWorld(btDispatcher* dispatcher,btBroadphaseInterface* broadphasePairCache, btCollisionConfiguration* collisionConfiguration); + + virtual ~btCollisionWorld(); + + void setBroadphase(btBroadphaseInterface* pairCache) + { + m_broadphasePairCache = pairCache; + } + + const btBroadphaseInterface* getBroadphase() const + { + return m_broadphasePairCache; + } + + btBroadphaseInterface* getBroadphase() + { + return m_broadphasePairCache; + } + + btOverlappingPairCache* getPairCache() + { + return m_broadphasePairCache->getOverlappingPairCache(); + } + + + btDispatcher* getDispatcher() + { + return m_dispatcher1; + } + + const btDispatcher* getDispatcher() const + { + return m_dispatcher1; + } + + void updateSingleAabb(btCollisionObject* colObj); + + virtual void updateAabbs(); + + virtual void setDebugDrawer(btIDebugDraw* debugDrawer) + { + m_debugDrawer = debugDrawer; + } + + virtual btIDebugDraw* getDebugDrawer() + { + return m_debugDrawer; + } + + + ///LocalShapeInfo gives extra information for complex shapes + ///Currently, only btTriangleMeshShape is available, so it just contains triangleIndex and subpart + struct LocalShapeInfo + { + int m_shapePart; + int m_triangleIndex; + + //const btCollisionShape* m_shapeTemp; + //const btTransform* m_shapeLocalTransform; + }; + + struct LocalRayResult + { + LocalRayResult(btCollisionObject* collisionObject, + LocalShapeInfo* localShapeInfo, + const btVector3& hitNormalLocal, + btScalar hitFraction) + :m_collisionObject(collisionObject), + m_localShapeInfo(localShapeInfo), + m_hitNormalLocal(hitNormalLocal), + m_hitFraction(hitFraction) + { + } + + btCollisionObject* m_collisionObject; + LocalShapeInfo* m_localShapeInfo; + btVector3 m_hitNormalLocal; + btScalar m_hitFraction; + + }; + + ///RayResultCallback is used to report new raycast results + struct RayResultCallback + { + btScalar m_closestHitFraction; + btCollisionObject* m_collisionObject; + short int m_collisionFilterGroup; + short int m_collisionFilterMask; + //@BP Mod - Custom flags, currently used to enable backface culling on tri-meshes, see btRaycastCallback + unsigned int m_flags; + + virtual ~RayResultCallback() + { + } + bool hasHit() const + { + return (m_collisionObject != 0); + } + + RayResultCallback() + :m_closestHitFraction(btScalar(1.)), + m_collisionObject(0), + m_collisionFilterGroup(btBroadphaseProxy::DefaultFilter), + m_collisionFilterMask(btBroadphaseProxy::AllFilter), + //@BP Mod + m_flags(0) + { + } + + virtual bool needsCollision(btBroadphaseProxy* proxy0) const + { + bool collides = (proxy0->m_collisionFilterGroup & m_collisionFilterMask) != 0; + collides = collides && (m_collisionFilterGroup & proxy0->m_collisionFilterMask); + return collides; + } + + + virtual btScalar addSingleResult(LocalRayResult& rayResult,bool normalInWorldSpace) = 0; + }; + + struct ClosestRayResultCallback : public RayResultCallback + { + ClosestRayResultCallback(const btVector3& rayFromWorld,const btVector3& rayToWorld) + :m_rayFromWorld(rayFromWorld), + m_rayToWorld(rayToWorld) + { + } + + btVector3 m_rayFromWorld;//used to calculate hitPointWorld from hitFraction + btVector3 m_rayToWorld; + + btVector3 m_hitNormalWorld; + btVector3 m_hitPointWorld; + + virtual btScalar addSingleResult(LocalRayResult& rayResult,bool normalInWorldSpace) + { + //caller already does the filter on the m_closestHitFraction + btAssert(rayResult.m_hitFraction <= m_closestHitFraction); + + m_closestHitFraction = rayResult.m_hitFraction; + m_collisionObject = rayResult.m_collisionObject; + if (normalInWorldSpace) + { + m_hitNormalWorld = rayResult.m_hitNormalLocal; + } else + { + ///need to transform normal into worldspace + m_hitNormalWorld = m_collisionObject->getWorldTransform().getBasis()*rayResult.m_hitNormalLocal; + } + m_hitPointWorld.setInterpolate3(m_rayFromWorld,m_rayToWorld,rayResult.m_hitFraction); + return rayResult.m_hitFraction; + } + }; + + + struct LocalConvexResult + { + LocalConvexResult(btCollisionObject* hitCollisionObject, + LocalShapeInfo* localShapeInfo, + const btVector3& hitNormalLocal, + const btVector3& hitPointLocal, + btScalar hitFraction + ) + :m_hitCollisionObject(hitCollisionObject), + m_localShapeInfo(localShapeInfo), + m_hitNormalLocal(hitNormalLocal), + m_hitPointLocal(hitPointLocal), + m_hitFraction(hitFraction) + { + } + + btCollisionObject* m_hitCollisionObject; + LocalShapeInfo* m_localShapeInfo; + btVector3 m_hitNormalLocal; + btVector3 m_hitPointLocal; + btScalar m_hitFraction; + }; + + ///RayResultCallback is used to report new raycast results + struct ConvexResultCallback + { + btScalar m_closestHitFraction; + short int m_collisionFilterGroup; + short int m_collisionFilterMask; + + ConvexResultCallback() + :m_closestHitFraction(btScalar(1.)), + m_collisionFilterGroup(btBroadphaseProxy::DefaultFilter), + m_collisionFilterMask(btBroadphaseProxy::AllFilter) + { + } + + virtual ~ConvexResultCallback() + { + } + + bool hasHit() const + { + return (m_closestHitFraction < btScalar(1.)); + } + + + + virtual bool needsCollision(btBroadphaseProxy* proxy0) const + { + bool collides = (proxy0->m_collisionFilterGroup & m_collisionFilterMask) != 0; + collides = collides && (m_collisionFilterGroup & proxy0->m_collisionFilterMask); + return collides; + } + + virtual btScalar addSingleResult(LocalConvexResult& convexResult,bool normalInWorldSpace) = 0; + }; + + struct ClosestConvexResultCallback : public ConvexResultCallback + { + ClosestConvexResultCallback(const btVector3& convexFromWorld,const btVector3& convexToWorld) + :m_convexFromWorld(convexFromWorld), + m_convexToWorld(convexToWorld), + m_hitCollisionObject(0) + { + } + + btVector3 m_convexFromWorld;//used to calculate hitPointWorld from hitFraction + btVector3 m_convexToWorld; + + btVector3 m_hitNormalWorld; + btVector3 m_hitPointWorld; + btCollisionObject* m_hitCollisionObject; + + virtual btScalar addSingleResult(LocalConvexResult& convexResult,bool normalInWorldSpace) + { +//caller already does the filter on the m_closestHitFraction + btAssert(convexResult.m_hitFraction <= m_closestHitFraction); + + m_closestHitFraction = convexResult.m_hitFraction; + m_hitCollisionObject = convexResult.m_hitCollisionObject; + if (normalInWorldSpace) + { + m_hitNormalWorld = convexResult.m_hitNormalLocal; + } else + { + ///need to transform normal into worldspace + m_hitNormalWorld = m_hitCollisionObject->getWorldTransform().getBasis()*convexResult.m_hitNormalLocal; + } + m_hitPointWorld = convexResult.m_hitPointLocal; + return convexResult.m_hitFraction; + } + }; + + int getNumCollisionObjects() const + { + return int(m_collisionObjects.size()); + } + + /// rayTest performs a raycast on all objects in the btCollisionWorld, and calls the resultCallback + /// This allows for several queries: first hit, all hits, any hit, dependent on the value returned by the callback. + void rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, RayResultCallback& resultCallback) const; + + // convexTest performs a swept convex cast on all objects in the btCollisionWorld, and calls the resultCallback + // This allows for several queries: first hit, all hits, any hit, dependent on the value return by the callback. + void convexSweepTest (const btConvexShape* castShape, const btTransform& from, const btTransform& to, ConvexResultCallback& resultCallback, btScalar allowedCcdPenetration = btScalar(0.)) const; + + + /// rayTestSingle performs a raycast call and calls the resultCallback. It is used internally by rayTest. + /// In a future implementation, we consider moving the ray test as a virtual method in btCollisionShape. + /// This allows more customization. + static void rayTestSingle(const btTransform& rayFromTrans,const btTransform& rayToTrans, + btCollisionObject* collisionObject, + const btCollisionShape* collisionShape, + const btTransform& colObjWorldTransform, + RayResultCallback& resultCallback); + + /// objectQuerySingle performs a collision detection query and calls the resultCallback. It is used internally by rayTest. + static void objectQuerySingle(const btConvexShape* castShape, const btTransform& rayFromTrans,const btTransform& rayToTrans, + btCollisionObject* collisionObject, + const btCollisionShape* collisionShape, + const btTransform& colObjWorldTransform, + ConvexResultCallback& resultCallback, btScalar allowedPenetration); + + virtual void addCollisionObject(btCollisionObject* collisionObject,short int collisionFilterGroup=btBroadphaseProxy::DefaultFilter,short int collisionFilterMask=btBroadphaseProxy::AllFilter); + + btCollisionObjectArray& getCollisionObjectArray() + { + return m_collisionObjects; + } + + const btCollisionObjectArray& getCollisionObjectArray() const + { + return m_collisionObjects; + } + + + virtual void removeCollisionObject(btCollisionObject* collisionObject); + + virtual void performDiscreteCollisionDetection(); + + btDispatcherInfo& getDispatchInfo() + { + return m_dispatchInfo; + } + + const btDispatcherInfo& getDispatchInfo() const + { + return m_dispatchInfo; + } + + bool getForceUpdateAllAabbs() const + { + return m_forceUpdateAllAabbs; + } + void setForceUpdateAllAabbs( bool forceUpdateAllAabbs) + { + m_forceUpdateAllAabbs = forceUpdateAllAabbs; + } + +}; + + +#endif //COLLISION_WORLD_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.cpp new file mode 100644 index 000000000..250c6badc --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.cpp @@ -0,0 +1,351 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "BulletCollision/CollisionShapes/btCompoundShape.h" +#include "BulletCollision/BroadphaseCollision/btDbvt.h" +#include "LinearMath/btIDebugDraw.h" +#include "LinearMath/btAabbUtil2.h" +#include "btManifoldResult.h" + +btCompoundCollisionAlgorithm::btCompoundCollisionAlgorithm( const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* body0,btCollisionObject* body1,bool isSwapped) +:btActivatingCollisionAlgorithm(ci,body0,body1), +m_isSwapped(isSwapped), +m_sharedManifold(ci.m_manifold) +{ + m_ownsManifold = false; + + btCollisionObject* colObj = m_isSwapped? body1 : body0; + btAssert (colObj->getCollisionShape()->isCompound()); + + btCompoundShape* compoundShape = static_cast(colObj->getCollisionShape()); + m_compoundShapeRevision = compoundShape->getUpdateRevision(); + + preallocateChildAlgorithms(body0,body1); +} + +void btCompoundCollisionAlgorithm::preallocateChildAlgorithms(btCollisionObject* body0,btCollisionObject* body1) +{ + btCollisionObject* colObj = m_isSwapped? body1 : body0; + btCollisionObject* otherObj = m_isSwapped? body0 : body1; + btAssert (colObj->getCollisionShape()->isCompound()); + + btCompoundShape* compoundShape = static_cast(colObj->getCollisionShape()); + + int numChildren = compoundShape->getNumChildShapes(); + int i; + + m_childCollisionAlgorithms.resize(numChildren); + for (i=0;igetDynamicAabbTree()) + { + m_childCollisionAlgorithms[i] = 0; + } else + { + btCollisionShape* tmpShape = colObj->getCollisionShape(); + btCollisionShape* childShape = compoundShape->getChildShape(i); + colObj->internalSetTemporaryCollisionShape( childShape ); + m_childCollisionAlgorithms[i] = m_dispatcher->findAlgorithm(colObj,otherObj,m_sharedManifold); + colObj->internalSetTemporaryCollisionShape( tmpShape ); + } + } +} + +void btCompoundCollisionAlgorithm::removeChildAlgorithms() +{ + int numChildren = m_childCollisionAlgorithms.size(); + int i; + for (i=0;i~btCollisionAlgorithm(); + m_dispatcher->freeCollisionAlgorithm(m_childCollisionAlgorithms[i]); + } + } +} + +btCompoundCollisionAlgorithm::~btCompoundCollisionAlgorithm() +{ + removeChildAlgorithms(); +} + + + + +struct btCompoundLeafCallback : btDbvt::ICollide +{ + +public: + + btCollisionObject* m_compoundColObj; + btCollisionObject* m_otherObj; + btDispatcher* m_dispatcher; + const btDispatcherInfo& m_dispatchInfo; + btManifoldResult* m_resultOut; + btCollisionAlgorithm** m_childCollisionAlgorithms; + btPersistentManifold* m_sharedManifold; + + + + + btCompoundLeafCallback (btCollisionObject* compoundObj,btCollisionObject* otherObj,btDispatcher* dispatcher,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut,btCollisionAlgorithm** childCollisionAlgorithms,btPersistentManifold* sharedManifold) + :m_compoundColObj(compoundObj),m_otherObj(otherObj),m_dispatcher(dispatcher),m_dispatchInfo(dispatchInfo),m_resultOut(resultOut), + m_childCollisionAlgorithms(childCollisionAlgorithms), + m_sharedManifold(sharedManifold) + { + + } + + + void ProcessChildShape(btCollisionShape* childShape,int index) + { + + btCompoundShape* compoundShape = static_cast(m_compoundColObj->getCollisionShape()); + + + //backup + btTransform orgTrans = m_compoundColObj->getWorldTransform(); + btTransform orgInterpolationTrans = m_compoundColObj->getInterpolationWorldTransform(); + const btTransform& childTrans = compoundShape->getChildTransform(index); + btTransform newChildWorldTrans = orgTrans*childTrans ; + + //perform an AABB check first + btVector3 aabbMin0,aabbMax0,aabbMin1,aabbMax1; + childShape->getAabb(newChildWorldTrans,aabbMin0,aabbMax0); + m_otherObj->getCollisionShape()->getAabb(m_otherObj->getWorldTransform(),aabbMin1,aabbMax1); + + if (TestAabbAgainstAabb2(aabbMin0,aabbMax0,aabbMin1,aabbMax1)) + { + + m_compoundColObj->setWorldTransform( newChildWorldTrans); + m_compoundColObj->setInterpolationWorldTransform(newChildWorldTrans); + + //the contactpoint is still projected back using the original inverted worldtrans + btCollisionShape* tmpShape = m_compoundColObj->getCollisionShape(); + m_compoundColObj->internalSetTemporaryCollisionShape( childShape ); + + if (!m_childCollisionAlgorithms[index]) + m_childCollisionAlgorithms[index] = m_dispatcher->findAlgorithm(m_compoundColObj,m_otherObj,m_sharedManifold); + + ///detect swapping case + if (m_resultOut->getBody0Internal() == m_compoundColObj) + { + m_resultOut->setShapeIdentifiersA(-1,index); + } else + { + m_resultOut->setShapeIdentifiersB(-1,index); + } + + m_childCollisionAlgorithms[index]->processCollision(m_compoundColObj,m_otherObj,m_dispatchInfo,m_resultOut); + if (m_dispatchInfo.m_debugDraw && (m_dispatchInfo.m_debugDraw->getDebugMode() & btIDebugDraw::DBG_DrawAabb)) + { + btVector3 worldAabbMin,worldAabbMax; + m_dispatchInfo.m_debugDraw->drawAabb(aabbMin0,aabbMax0,btVector3(1,1,1)); + m_dispatchInfo.m_debugDraw->drawAabb(aabbMin1,aabbMax1,btVector3(1,1,1)); + } + + //revert back transform + m_compoundColObj->internalSetTemporaryCollisionShape( tmpShape); + m_compoundColObj->setWorldTransform( orgTrans ); + m_compoundColObj->setInterpolationWorldTransform(orgInterpolationTrans); + } + } + void Process(const btDbvtNode* leaf) + { + int index = leaf->dataAsInt; + + btCompoundShape* compoundShape = static_cast(m_compoundColObj->getCollisionShape()); + btCollisionShape* childShape = compoundShape->getChildShape(index); + if (m_dispatchInfo.m_debugDraw && (m_dispatchInfo.m_debugDraw->getDebugMode() & btIDebugDraw::DBG_DrawAabb)) + { + btVector3 worldAabbMin,worldAabbMax; + btTransform orgTrans = m_compoundColObj->getWorldTransform(); + btTransformAabb(leaf->volume.Mins(),leaf->volume.Maxs(),0.,orgTrans,worldAabbMin,worldAabbMax); + m_dispatchInfo.m_debugDraw->drawAabb(worldAabbMin,worldAabbMax,btVector3(1,0,0)); + } + ProcessChildShape(childShape,index); + + } +}; + + + + + + +void btCompoundCollisionAlgorithm::processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + btCollisionObject* colObj = m_isSwapped? body1 : body0; + btCollisionObject* otherObj = m_isSwapped? body0 : body1; + + + + btAssert (colObj->getCollisionShape()->isCompound()); + btCompoundShape* compoundShape = static_cast(colObj->getCollisionShape()); + + ///btCompoundShape might have changed: + ////make sure the internal child collision algorithm caches are still valid + if (compoundShape->getUpdateRevision() != m_compoundShapeRevision) + { + ///clear and update all + removeChildAlgorithms(); + + preallocateChildAlgorithms(body0,body1); + } + + + btDbvt* tree = compoundShape->getDynamicAabbTree(); + //use a dynamic aabb tree to cull potential child-overlaps + btCompoundLeafCallback callback(colObj,otherObj,m_dispatcher,dispatchInfo,resultOut,&m_childCollisionAlgorithms[0],m_sharedManifold); + + ///we need to refresh all contact manifolds + ///note that we should actually recursively traverse all children, btCompoundShape can nested more then 1 level deep + ///so we should add a 'refreshManifolds' in the btCollisionAlgorithm + { + int i; + btManifoldArray manifoldArray; + for (i=0;igetAllContactManifolds(manifoldArray); + for (int m=0;mgetNumContacts()) + { + resultOut->setPersistentManifold(manifoldArray[m]); + resultOut->refreshContactPoints(); + resultOut->setPersistentManifold(0);//??necessary? + } + } + manifoldArray.clear(); + } + } + } + + if (tree) + { + + btVector3 localAabbMin,localAabbMax; + btTransform otherInCompoundSpace; + otherInCompoundSpace = colObj->getWorldTransform().inverse() * otherObj->getWorldTransform(); + otherObj->getCollisionShape()->getAabb(otherInCompoundSpace,localAabbMin,localAabbMax); + + const ATTRIBUTE_ALIGNED16(btDbvtVolume) bounds=btDbvtVolume::FromMM(localAabbMin,localAabbMax); + //process all children, that overlap with the given AABB bounds + tree->collideTV(tree->m_root,bounds,callback); + + } else + { + //iterate over all children, perform an AABB check inside ProcessChildShape + int numChildren = m_childCollisionAlgorithms.size(); + int i; + for (i=0;igetChildShape(i),i); + } + } + + { + //iterate over all children, perform an AABB check inside ProcessChildShape + int numChildren = m_childCollisionAlgorithms.size(); + int i; + btManifoldArray manifoldArray; + + for (i=0;igetChildShape(i); + //if not longer overlapping, remove the algorithm + btTransform orgTrans = colObj->getWorldTransform(); + btTransform orgInterpolationTrans = colObj->getInterpolationWorldTransform(); + const btTransform& childTrans = compoundShape->getChildTransform(i); + btTransform newChildWorldTrans = orgTrans*childTrans ; + + //perform an AABB check first + btVector3 aabbMin0,aabbMax0,aabbMin1,aabbMax1; + childShape->getAabb(newChildWorldTrans,aabbMin0,aabbMax0); + otherObj->getCollisionShape()->getAabb(otherObj->getWorldTransform(),aabbMin1,aabbMax1); + + if (!TestAabbAgainstAabb2(aabbMin0,aabbMax0,aabbMin1,aabbMax1)) + { + m_childCollisionAlgorithms[i]->~btCollisionAlgorithm(); + m_dispatcher->freeCollisionAlgorithm(m_childCollisionAlgorithms[i]); + m_childCollisionAlgorithms[i] = 0; + } + + } + + } + + + + } +} + +btScalar btCompoundCollisionAlgorithm::calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + + btCollisionObject* colObj = m_isSwapped? body1 : body0; + btCollisionObject* otherObj = m_isSwapped? body0 : body1; + + btAssert (colObj->getCollisionShape()->isCompound()); + + btCompoundShape* compoundShape = static_cast(colObj->getCollisionShape()); + + //We will use the OptimizedBVH, AABB tree to cull potential child-overlaps + //If both proxies are Compound, we will deal with that directly, by performing sequential/parallel tree traversals + //given Proxy0 and Proxy1, if both have a tree, Tree0 and Tree1, this means: + //determine overlapping nodes of Proxy1 using Proxy0 AABB against Tree1 + //then use each overlapping node AABB against Tree0 + //and vise versa. + + btScalar hitFraction = btScalar(1.); + + int numChildren = m_childCollisionAlgorithms.size(); + int i; + for (i=0;igetChildShape(i); + + //backup + btTransform orgTrans = colObj->getWorldTransform(); + + const btTransform& childTrans = compoundShape->getChildTransform(i); + //btTransform newChildWorldTrans = orgTrans*childTrans ; + colObj->setWorldTransform( orgTrans*childTrans ); + + btCollisionShape* tmpShape = colObj->getCollisionShape(); + colObj->internalSetTemporaryCollisionShape( childShape ); + btScalar frac = m_childCollisionAlgorithms[i]->calculateTimeOfImpact(colObj,otherObj,dispatchInfo,resultOut); + if (fracinternalSetTemporaryCollisionShape( tmpShape); + colObj->setWorldTransform( orgTrans); + } + return hitFraction; + +} + + + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.h new file mode 100644 index 000000000..255e0af66 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.h @@ -0,0 +1,86 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 COMPOUND_COLLISION_ALGORITHM_H +#define COMPOUND_COLLISION_ALGORITHM_H + +#include "btActivatingCollisionAlgorithm.h" +#include "BulletCollision/BroadphaseCollision/btDispatcher.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseInterface.h" + +#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" +class btDispatcher; +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "btCollisionCreateFunc.h" +#include "LinearMath/btAlignedObjectArray.h" +class btDispatcher; +class btCollisionObject; + +/// btCompoundCollisionAlgorithm supports collision between CompoundCollisionShapes and other collision shapes +class btCompoundCollisionAlgorithm : public btActivatingCollisionAlgorithm +{ + btAlignedObjectArray m_childCollisionAlgorithms; + bool m_isSwapped; + + class btPersistentManifold* m_sharedManifold; + bool m_ownsManifold; + + int m_compoundShapeRevision;//to keep track of changes, so that childAlgorithm array can be updated + + void removeChildAlgorithms(); + + void preallocateChildAlgorithms(btCollisionObject* body0,btCollisionObject* body1); + +public: + + btCompoundCollisionAlgorithm( const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* body0,btCollisionObject* body1,bool isSwapped); + + virtual ~btCompoundCollisionAlgorithm(); + + virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual void getAllContactManifolds(btManifoldArray& manifoldArray) + { + int i; + for (i=0;igetAllContactManifolds(manifoldArray); + } + } + + struct CreateFunc :public btCollisionAlgorithmCreateFunc + { + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + void* mem = ci.m_dispatcher1->allocateCollisionAlgorithm(sizeof(btCompoundCollisionAlgorithm)); + return new(mem) btCompoundCollisionAlgorithm(ci,body0,body1,false); + } + }; + + struct SwappedCreateFunc :public btCollisionAlgorithmCreateFunc + { + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + void* mem = ci.m_dispatcher1->allocateCollisionAlgorithm(sizeof(btCompoundCollisionAlgorithm)); + return new(mem) btCompoundCollisionAlgorithm(ci,body0,body1,true); + } + }; + +}; + +#endif //COMPOUND_COLLISION_ALGORITHM_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvex2dConvex2dAlgorithm.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvex2dConvex2dAlgorithm.cpp new file mode 100644 index 000000000..db7f884ac --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvex2dConvex2dAlgorithm.cpp @@ -0,0 +1,247 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btConvex2dConvex2dAlgorithm.h" + +//#include +#include "BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseInterface.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "BulletCollision/CollisionShapes/btConvexShape.h" +#include "BulletCollision/CollisionShapes/btCapsuleShape.h" + + +#include "BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/CollisionShapes/btBoxShape.h" +#include "BulletCollision/CollisionDispatch/btManifoldResult.h" + +#include "BulletCollision/NarrowPhaseCollision/btConvexPenetrationDepthSolver.h" +#include "BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.h" +#include "BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.h" +#include "BulletCollision/NarrowPhaseCollision/btGjkConvexCast.h" + + + +#include "BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.h" +#include "BulletCollision/CollisionShapes/btSphereShape.h" + +#include "BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.h" + +#include "BulletCollision/NarrowPhaseCollision/btGjkEpa2.h" +#include "BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h" + + +btConvex2dConvex2dAlgorithm::CreateFunc::CreateFunc(btSimplexSolverInterface* simplexSolver, btConvexPenetrationDepthSolver* pdSolver) +{ + m_numPerturbationIterations = 0; + m_minimumPointsPerturbationThreshold = 3; + m_simplexSolver = simplexSolver; + m_pdSolver = pdSolver; +} + +btConvex2dConvex2dAlgorithm::CreateFunc::~CreateFunc() +{ +} + +btConvex2dConvex2dAlgorithm::btConvex2dConvex2dAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* body0,btCollisionObject* body1,btSimplexSolverInterface* simplexSolver, btConvexPenetrationDepthSolver* pdSolver,int numPerturbationIterations, int minimumPointsPerturbationThreshold) +: btActivatingCollisionAlgorithm(ci,body0,body1), +m_simplexSolver(simplexSolver), +m_pdSolver(pdSolver), +m_ownManifold (false), +m_manifoldPtr(mf), +m_lowLevelOfDetail(false), + m_numPerturbationIterations(numPerturbationIterations), +m_minimumPointsPerturbationThreshold(minimumPointsPerturbationThreshold) +{ + (void)body0; + (void)body1; +} + + + + +btConvex2dConvex2dAlgorithm::~btConvex2dConvex2dAlgorithm() +{ + if (m_ownManifold) + { + if (m_manifoldPtr) + m_dispatcher->releaseManifold(m_manifoldPtr); + } +} + +void btConvex2dConvex2dAlgorithm ::setLowLevelOfDetail(bool useLowLevel) +{ + m_lowLevelOfDetail = useLowLevel; +} + + + +extern btScalar gContactBreakingThreshold; + + +// +// Convex-Convex collision algorithm +// +void btConvex2dConvex2dAlgorithm ::processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + + if (!m_manifoldPtr) + { + //swapped? + m_manifoldPtr = m_dispatcher->getNewManifold(body0,body1); + m_ownManifold = true; + } + resultOut->setPersistentManifold(m_manifoldPtr); + + //comment-out next line to test multi-contact generation + //resultOut->getPersistentManifold()->clearManifold(); + + + btConvexShape* min0 = static_cast(body0->getCollisionShape()); + btConvexShape* min1 = static_cast(body1->getCollisionShape()); + + btVector3 normalOnB; + btVector3 pointOnBWorld; + + { + + + btGjkPairDetector::ClosestPointInput input; + + btGjkPairDetector gjkPairDetector(min0,min1,m_simplexSolver,m_pdSolver); + //TODO: if (dispatchInfo.m_useContinuous) + gjkPairDetector.setMinkowskiA(min0); + gjkPairDetector.setMinkowskiB(min1); + + { + input.m_maximumDistanceSquared = min0->getMargin() + min1->getMargin() + m_manifoldPtr->getContactBreakingThreshold(); + input.m_maximumDistanceSquared*= input.m_maximumDistanceSquared; + } + + input.m_stackAlloc = dispatchInfo.m_stackAllocator; + input.m_transformA = body0->getWorldTransform(); + input.m_transformB = body1->getWorldTransform(); + + gjkPairDetector.getClosestPoints(input,*resultOut,dispatchInfo.m_debugDraw); + + btVector3 v0,v1; + btVector3 sepNormalWorldSpace; + + } + + if (m_ownManifold) + { + resultOut->refreshContactPoints(); + } + +} + + + + +btScalar btConvex2dConvex2dAlgorithm::calculateTimeOfImpact(btCollisionObject* col0,btCollisionObject* col1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + (void)resultOut; + (void)dispatchInfo; + ///Rather then checking ALL pairs, only calculate TOI when motion exceeds threshold + + ///Linear motion for one of objects needs to exceed m_ccdSquareMotionThreshold + ///col0->m_worldTransform, + btScalar resultFraction = btScalar(1.); + + + btScalar squareMot0 = (col0->getInterpolationWorldTransform().getOrigin() - col0->getWorldTransform().getOrigin()).length2(); + btScalar squareMot1 = (col1->getInterpolationWorldTransform().getOrigin() - col1->getWorldTransform().getOrigin()).length2(); + + if (squareMot0 < col0->getCcdSquareMotionThreshold() && + squareMot1 < col1->getCcdSquareMotionThreshold()) + return resultFraction; + + + //An adhoc way of testing the Continuous Collision Detection algorithms + //One object is approximated as a sphere, to simplify things + //Starting in penetration should report no time of impact + //For proper CCD, better accuracy and handling of 'allowed' penetration should be added + //also the mainloop of the physics should have a kind of toi queue (something like Brian Mirtich's application of Timewarp for Rigidbodies) + + + /// Convex0 against sphere for Convex1 + { + btConvexShape* convex0 = static_cast(col0->getCollisionShape()); + + btSphereShape sphere1(col1->getCcdSweptSphereRadius()); //todo: allow non-zero sphere sizes, for better approximation + btConvexCast::CastResult result; + btVoronoiSimplexSolver voronoiSimplex; + //SubsimplexConvexCast ccd0(&sphere,min0,&voronoiSimplex); + ///Simplification, one object is simplified as a sphere + btGjkConvexCast ccd1( convex0 ,&sphere1,&voronoiSimplex); + //ContinuousConvexCollision ccd(min0,min1,&voronoiSimplex,0); + if (ccd1.calcTimeOfImpact(col0->getWorldTransform(),col0->getInterpolationWorldTransform(), + col1->getWorldTransform(),col1->getInterpolationWorldTransform(),result)) + { + + //store result.m_fraction in both bodies + + if (col0->getHitFraction()> result.m_fraction) + col0->setHitFraction( result.m_fraction ); + + if (col1->getHitFraction() > result.m_fraction) + col1->setHitFraction( result.m_fraction); + + if (resultFraction > result.m_fraction) + resultFraction = result.m_fraction; + + } + + + + + } + + /// Sphere (for convex0) against Convex1 + { + btConvexShape* convex1 = static_cast(col1->getCollisionShape()); + + btSphereShape sphere0(col0->getCcdSweptSphereRadius()); //todo: allow non-zero sphere sizes, for better approximation + btConvexCast::CastResult result; + btVoronoiSimplexSolver voronoiSimplex; + //SubsimplexConvexCast ccd0(&sphere,min0,&voronoiSimplex); + ///Simplification, one object is simplified as a sphere + btGjkConvexCast ccd1(&sphere0,convex1,&voronoiSimplex); + //ContinuousConvexCollision ccd(min0,min1,&voronoiSimplex,0); + if (ccd1.calcTimeOfImpact(col0->getWorldTransform(),col0->getInterpolationWorldTransform(), + col1->getWorldTransform(),col1->getInterpolationWorldTransform(),result)) + { + + //store result.m_fraction in both bodies + + if (col0->getHitFraction() > result.m_fraction) + col0->setHitFraction( result.m_fraction); + + if (col1->getHitFraction() > result.m_fraction) + col1->setHitFraction( result.m_fraction); + + if (resultFraction > result.m_fraction) + resultFraction = result.m_fraction; + + } + } + + return resultFraction; + +} + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvex2dConvex2dAlgorithm.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvex2dConvex2dAlgorithm.h new file mode 100644 index 000000000..573840140 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvex2dConvex2dAlgorithm.h @@ -0,0 +1,95 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 CONVEX_2D_CONVEX_2D_ALGORITHM_H +#define CONVEX_2D_CONVEX_2D_ALGORITHM_H + +#include "BulletCollision/CollisionDispatch/btActivatingCollisionAlgorithm.h" +#include "BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h" +#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.h" +#include "BulletCollision/CollisionDispatch/btCollisionCreateFunc.h" +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "LinearMath/btTransformUtil.h" //for btConvexSeparatingDistanceUtil + +class btConvexPenetrationDepthSolver; + + +///The convex2dConvex2dAlgorithm collision algorithm support 2d collision detection for btConvex2dShape +///Currently it requires the btMinkowskiPenetrationDepthSolver, it has support for 2d penetration depth computation +class btConvex2dConvex2dAlgorithm : public btActivatingCollisionAlgorithm +{ + btSimplexSolverInterface* m_simplexSolver; + btConvexPenetrationDepthSolver* m_pdSolver; + + + bool m_ownManifold; + btPersistentManifold* m_manifoldPtr; + bool m_lowLevelOfDetail; + + int m_numPerturbationIterations; + int m_minimumPointsPerturbationThreshold; + +public: + + btConvex2dConvex2dAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* body0,btCollisionObject* body1, btSimplexSolverInterface* simplexSolver, btConvexPenetrationDepthSolver* pdSolver, int numPerturbationIterations, int minimumPointsPerturbationThreshold); + + + virtual ~btConvex2dConvex2dAlgorithm(); + + virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual void getAllContactManifolds(btManifoldArray& manifoldArray) + { + ///should we use m_ownManifold to avoid adding duplicates? + if (m_manifoldPtr && m_ownManifold) + manifoldArray.push_back(m_manifoldPtr); + } + + + void setLowLevelOfDetail(bool useLowLevel); + + + const btPersistentManifold* getManifold() + { + return m_manifoldPtr; + } + + struct CreateFunc :public btCollisionAlgorithmCreateFunc + { + + btConvexPenetrationDepthSolver* m_pdSolver; + btSimplexSolverInterface* m_simplexSolver; + int m_numPerturbationIterations; + int m_minimumPointsPerturbationThreshold; + + CreateFunc(btSimplexSolverInterface* simplexSolver, btConvexPenetrationDepthSolver* pdSolver); + + virtual ~CreateFunc(); + + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + void* mem = ci.m_dispatcher1->allocateCollisionAlgorithm(sizeof(btConvex2dConvex2dAlgorithm)); + return new(mem) btConvex2dConvex2dAlgorithm(ci.m_manifold,ci,body0,body1,m_simplexSolver,m_pdSolver,m_numPerturbationIterations,m_minimumPointsPerturbationThreshold); + } + }; + + +}; + +#endif //CONVEX_2D_CONVEX_2D_ALGORITHM_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.cpp new file mode 100644 index 000000000..6a556195b --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.cpp @@ -0,0 +1,317 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btConvexConcaveCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "BulletCollision/CollisionShapes/btMultiSphereShape.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "BulletCollision/CollisionShapes/btConcaveShape.h" +#include "BulletCollision/CollisionDispatch/btManifoldResult.h" +#include "BulletCollision/NarrowPhaseCollision/btRaycastCallback.h" +#include "BulletCollision/CollisionShapes/btTriangleShape.h" +#include "BulletCollision/CollisionShapes/btSphereShape.h" +#include "LinearMath/btIDebugDraw.h" +#include "BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.h" + +btConvexConcaveCollisionAlgorithm::btConvexConcaveCollisionAlgorithm( const btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1,bool isSwapped) +: btActivatingCollisionAlgorithm(ci,body0,body1), +m_isSwapped(isSwapped), +m_btConvexTriangleCallback(ci.m_dispatcher1,body0,body1,isSwapped) +{ +} + +btConvexConcaveCollisionAlgorithm::~btConvexConcaveCollisionAlgorithm() +{ +} + +void btConvexConcaveCollisionAlgorithm::getAllContactManifolds(btManifoldArray& manifoldArray) +{ + if (m_btConvexTriangleCallback.m_manifoldPtr) + { + manifoldArray.push_back(m_btConvexTriangleCallback.m_manifoldPtr); + } +} + + +btConvexTriangleCallback::btConvexTriangleCallback(btDispatcher* dispatcher,btCollisionObject* body0,btCollisionObject* body1,bool isSwapped): + m_dispatcher(dispatcher), + m_dispatchInfoPtr(0) +{ + m_convexBody = isSwapped? body1:body0; + m_triBody = isSwapped? body0:body1; + + // + // create the manifold from the dispatcher 'manifold pool' + // + m_manifoldPtr = m_dispatcher->getNewManifold(m_convexBody,m_triBody); + + clearCache(); +} + +btConvexTriangleCallback::~btConvexTriangleCallback() +{ + clearCache(); + m_dispatcher->releaseManifold( m_manifoldPtr ); + +} + + +void btConvexTriangleCallback::clearCache() +{ + m_dispatcher->clearManifold(m_manifoldPtr); +} + + + +void btConvexTriangleCallback::processTriangle(btVector3* triangle,int partId, int triangleIndex) +{ + + //just for debugging purposes + //printf("triangle %d",m_triangleCount++); + + + //aabb filter is already applied! + + btCollisionAlgorithmConstructionInfo ci; + ci.m_dispatcher1 = m_dispatcher; + + btCollisionObject* ob = static_cast(m_triBody); + + + + ///debug drawing of the overlapping triangles + if (m_dispatchInfoPtr && m_dispatchInfoPtr->m_debugDraw && (m_dispatchInfoPtr->m_debugDraw->getDebugMode() &btIDebugDraw::DBG_DrawWireframe )) + { + btVector3 color(255,255,0); + btTransform& tr = ob->getWorldTransform(); + m_dispatchInfoPtr->m_debugDraw->drawLine(tr(triangle[0]),tr(triangle[1]),color); + m_dispatchInfoPtr->m_debugDraw->drawLine(tr(triangle[1]),tr(triangle[2]),color); + m_dispatchInfoPtr->m_debugDraw->drawLine(tr(triangle[2]),tr(triangle[0]),color); + + //btVector3 center = triangle[0] + triangle[1]+triangle[2]; + //center *= btScalar(0.333333); + //m_dispatchInfoPtr->m_debugDraw->drawLine(tr(triangle[0]),tr(center),color); + //m_dispatchInfoPtr->m_debugDraw->drawLine(tr(triangle[1]),tr(center),color); + //m_dispatchInfoPtr->m_debugDraw->drawLine(tr(triangle[2]),tr(center),color); + + } + + + //btCollisionObject* colObj = static_cast(m_convexProxy->m_clientObject); + + if (m_convexBody->getCollisionShape()->isConvex()) + { + btTriangleShape tm(triangle[0],triangle[1],triangle[2]); + tm.setMargin(m_collisionMarginTriangle); + + btCollisionShape* tmpShape = ob->getCollisionShape(); + ob->internalSetTemporaryCollisionShape( &tm ); + + btCollisionAlgorithm* colAlgo = ci.m_dispatcher1->findAlgorithm(m_convexBody,m_triBody,m_manifoldPtr); + ///this should use the btDispatcher, so the actual registered algorithm is used + // btConvexConvexAlgorithm cvxcvxalgo(m_manifoldPtr,ci,m_convexBody,m_triBody); + + m_resultOut->setShapeIdentifiersB(partId,triangleIndex); + +// cvxcvxalgo.processCollision(m_convexBody,m_triBody,*m_dispatchInfoPtr,m_resultOut); + colAlgo->processCollision(m_convexBody,m_triBody,*m_dispatchInfoPtr,m_resultOut); + colAlgo->~btCollisionAlgorithm(); + ci.m_dispatcher1->freeCollisionAlgorithm(colAlgo); + ob->internalSetTemporaryCollisionShape( tmpShape); + } + + +} + + + +void btConvexTriangleCallback::setTimeStepAndCounters(btScalar collisionMarginTriangle,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + m_dispatchInfoPtr = &dispatchInfo; + m_collisionMarginTriangle = collisionMarginTriangle; + m_resultOut = resultOut; + + //recalc aabbs + btTransform convexInTriangleSpace; + convexInTriangleSpace = m_triBody->getWorldTransform().inverse() * m_convexBody->getWorldTransform(); + btCollisionShape* convexShape = static_cast(m_convexBody->getCollisionShape()); + //CollisionShape* triangleShape = static_cast(triBody->m_collisionShape); + convexShape->getAabb(convexInTriangleSpace,m_aabbMin,m_aabbMax); + btScalar extraMargin = collisionMarginTriangle; + btVector3 extra(extraMargin,extraMargin,extraMargin); + + m_aabbMax += extra; + m_aabbMin -= extra; + +} + +void btConvexConcaveCollisionAlgorithm::clearCache() +{ + m_btConvexTriangleCallback.clearCache(); + +} + +void btConvexConcaveCollisionAlgorithm::processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + + + btCollisionObject* convexBody = m_isSwapped ? body1 : body0; + btCollisionObject* triBody = m_isSwapped ? body0 : body1; + + if (triBody->getCollisionShape()->isConcave()) + { + + + btCollisionObject* triOb = triBody; + btConcaveShape* concaveShape = static_cast( triOb->getCollisionShape()); + + if (convexBody->getCollisionShape()->isConvex()) + { + btScalar collisionMarginTriangle = concaveShape->getMargin(); + + resultOut->setPersistentManifold(m_btConvexTriangleCallback.m_manifoldPtr); + m_btConvexTriangleCallback.setTimeStepAndCounters(collisionMarginTriangle,dispatchInfo,resultOut); + + //Disable persistency. previously, some older algorithm calculated all contacts in one go, so you can clear it here. + //m_dispatcher->clearManifold(m_btConvexTriangleCallback.m_manifoldPtr); + + m_btConvexTriangleCallback.m_manifoldPtr->setBodies(convexBody,triBody); + + concaveShape->processAllTriangles( &m_btConvexTriangleCallback,m_btConvexTriangleCallback.getAabbMin(),m_btConvexTriangleCallback.getAabbMax()); + + resultOut->refreshContactPoints(); + + } + + } + +} + + +btScalar btConvexConcaveCollisionAlgorithm::calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + (void)resultOut; + (void)dispatchInfo; + btCollisionObject* convexbody = m_isSwapped ? body1 : body0; + btCollisionObject* triBody = m_isSwapped ? body0 : body1; + + + //quick approximation using raycast, todo: hook up to the continuous collision detection (one of the btConvexCast) + + //only perform CCD above a certain threshold, this prevents blocking on the long run + //because object in a blocked ccd state (hitfraction<1) get their linear velocity halved each frame... + btScalar squareMot0 = (convexbody->getInterpolationWorldTransform().getOrigin() - convexbody->getWorldTransform().getOrigin()).length2(); + if (squareMot0 < convexbody->getCcdSquareMotionThreshold()) + { + return btScalar(1.); + } + + //const btVector3& from = convexbody->m_worldTransform.getOrigin(); + //btVector3 to = convexbody->m_interpolationWorldTransform.getOrigin(); + //todo: only do if the motion exceeds the 'radius' + + btTransform triInv = triBody->getWorldTransform().inverse(); + btTransform convexFromLocal = triInv * convexbody->getWorldTransform(); + btTransform convexToLocal = triInv * convexbody->getInterpolationWorldTransform(); + + struct LocalTriangleSphereCastCallback : public btTriangleCallback + { + btTransform m_ccdSphereFromTrans; + btTransform m_ccdSphereToTrans; + btTransform m_meshTransform; + + btScalar m_ccdSphereRadius; + btScalar m_hitFraction; + + + LocalTriangleSphereCastCallback(const btTransform& from,const btTransform& to,btScalar ccdSphereRadius,btScalar hitFraction) + :m_ccdSphereFromTrans(from), + m_ccdSphereToTrans(to), + m_ccdSphereRadius(ccdSphereRadius), + m_hitFraction(hitFraction) + { + } + + + virtual void processTriangle(btVector3* triangle, int partId, int triangleIndex) + { + (void)partId; + (void)triangleIndex; + //do a swept sphere for now + btTransform ident; + ident.setIdentity(); + btConvexCast::CastResult castResult; + castResult.m_fraction = m_hitFraction; + btSphereShape pointShape(m_ccdSphereRadius); + btTriangleShape triShape(triangle[0],triangle[1],triangle[2]); + btVoronoiSimplexSolver simplexSolver; + btSubsimplexConvexCast convexCaster(&pointShape,&triShape,&simplexSolver); + //GjkConvexCast convexCaster(&pointShape,convexShape,&simplexSolver); + //ContinuousConvexCollision convexCaster(&pointShape,convexShape,&simplexSolver,0); + //local space? + + if (convexCaster.calcTimeOfImpact(m_ccdSphereFromTrans,m_ccdSphereToTrans, + ident,ident,castResult)) + { + if (m_hitFraction > castResult.m_fraction) + m_hitFraction = castResult.m_fraction; + } + + } + + }; + + + + + + if (triBody->getCollisionShape()->isConcave()) + { + btVector3 rayAabbMin = convexFromLocal.getOrigin(); + rayAabbMin.setMin(convexToLocal.getOrigin()); + btVector3 rayAabbMax = convexFromLocal.getOrigin(); + rayAabbMax.setMax(convexToLocal.getOrigin()); + btScalar ccdRadius0 = convexbody->getCcdSweptSphereRadius(); + rayAabbMin -= btVector3(ccdRadius0,ccdRadius0,ccdRadius0); + rayAabbMax += btVector3(ccdRadius0,ccdRadius0,ccdRadius0); + + btScalar curHitFraction = btScalar(1.); //is this available? + LocalTriangleSphereCastCallback raycastCallback(convexFromLocal,convexToLocal, + convexbody->getCcdSweptSphereRadius(),curHitFraction); + + raycastCallback.m_hitFraction = convexbody->getHitFraction(); + + btCollisionObject* concavebody = triBody; + + btConcaveShape* triangleMesh = (btConcaveShape*) concavebody->getCollisionShape(); + + if (triangleMesh) + { + triangleMesh->processAllTriangles(&raycastCallback,rayAabbMin,rayAabbMax); + } + + + + if (raycastCallback.m_hitFraction < convexbody->getHitFraction()) + { + convexbody->setHitFraction( raycastCallback.m_hitFraction); + return raycastCallback.m_hitFraction; + } + } + + return btScalar(1.); + +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.h new file mode 100644 index 000000000..984a4c39e --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.h @@ -0,0 +1,116 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 CONVEX_CONCAVE_COLLISION_ALGORITHM_H +#define CONVEX_CONCAVE_COLLISION_ALGORITHM_H + +#include "btActivatingCollisionAlgorithm.h" +#include "BulletCollision/BroadphaseCollision/btDispatcher.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseInterface.h" +#include "BulletCollision/CollisionShapes/btTriangleCallback.h" +#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" +class btDispatcher; +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "btCollisionCreateFunc.h" + +///For each triangle in the concave mesh that overlaps with the AABB of a convex (m_convexProxy), processTriangle is called. +class btConvexTriangleCallback : public btTriangleCallback +{ + btCollisionObject* m_convexBody; + btCollisionObject* m_triBody; + + btVector3 m_aabbMin; + btVector3 m_aabbMax ; + + + btManifoldResult* m_resultOut; + btDispatcher* m_dispatcher; + const btDispatcherInfo* m_dispatchInfoPtr; + btScalar m_collisionMarginTriangle; + +public: +int m_triangleCount; + + btPersistentManifold* m_manifoldPtr; + + btConvexTriangleCallback(btDispatcher* dispatcher,btCollisionObject* body0,btCollisionObject* body1,bool isSwapped); + + void setTimeStepAndCounters(btScalar collisionMarginTriangle,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual ~btConvexTriangleCallback(); + + virtual void processTriangle(btVector3* triangle, int partId, int triangleIndex); + + void clearCache(); + + SIMD_FORCE_INLINE const btVector3& getAabbMin() const + { + return m_aabbMin; + } + SIMD_FORCE_INLINE const btVector3& getAabbMax() const + { + return m_aabbMax; + } + +}; + + + + +/// btConvexConcaveCollisionAlgorithm supports collision between convex shapes and (concave) trianges meshes. +class btConvexConcaveCollisionAlgorithm : public btActivatingCollisionAlgorithm +{ + + bool m_isSwapped; + + btConvexTriangleCallback m_btConvexTriangleCallback; + + + +public: + + btConvexConcaveCollisionAlgorithm( const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* body0,btCollisionObject* body1,bool isSwapped); + + virtual ~btConvexConcaveCollisionAlgorithm(); + + virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual void getAllContactManifolds(btManifoldArray& manifoldArray); + + void clearCache(); + + struct CreateFunc :public btCollisionAlgorithmCreateFunc + { + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + void* mem = ci.m_dispatcher1->allocateCollisionAlgorithm(sizeof(btConvexConcaveCollisionAlgorithm)); + return new(mem) btConvexConcaveCollisionAlgorithm(ci,body0,body1,false); + } + }; + + struct SwappedCreateFunc :public btCollisionAlgorithmCreateFunc + { + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + void* mem = ci.m_dispatcher1->allocateCollisionAlgorithm(sizeof(btConvexConcaveCollisionAlgorithm)); + return new(mem) btConvexConcaveCollisionAlgorithm(ci,body0,body1,true); + } + }; + +}; + +#endif //CONVEX_CONCAVE_COLLISION_ALGORITHM_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.cpp new file mode 100644 index 000000000..152506e58 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.cpp @@ -0,0 +1,565 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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. +*/ + +///Specialized capsule-capsule collision algorithm has been added for Bullet 2.75 release to increase ragdoll performance +///If you experience problems with capsule-capsule collision, try to define BT_DISABLE_CAPSULE_CAPSULE_COLLIDER and report it in the Bullet forums +///with reproduction case +//define BT_DISABLE_CAPSULE_CAPSULE_COLLIDER 1 + +#include "btConvexConvexAlgorithm.h" + +//#include +#include "BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseInterface.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "BulletCollision/CollisionShapes/btConvexShape.h" +#include "BulletCollision/CollisionShapes/btCapsuleShape.h" + + +#include "BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/CollisionShapes/btBoxShape.h" +#include "BulletCollision/CollisionDispatch/btManifoldResult.h" + +#include "BulletCollision/NarrowPhaseCollision/btConvexPenetrationDepthSolver.h" +#include "BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.h" +#include "BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.h" +#include "BulletCollision/NarrowPhaseCollision/btGjkConvexCast.h" + + + +#include "BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.h" +#include "BulletCollision/CollisionShapes/btSphereShape.h" + +#include "BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.h" + +#include "BulletCollision/NarrowPhaseCollision/btGjkEpa2.h" +#include "BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h" + + + +/////////// + + + +static SIMD_FORCE_INLINE void segmentsClosestPoints( + btVector3& ptsVector, + btVector3& offsetA, + btVector3& offsetB, + btScalar& tA, btScalar& tB, + const btVector3& translation, + const btVector3& dirA, btScalar hlenA, + const btVector3& dirB, btScalar hlenB ) +{ + // compute the parameters of the closest points on each line segment + + btScalar dirA_dot_dirB = btDot(dirA,dirB); + btScalar dirA_dot_trans = btDot(dirA,translation); + btScalar dirB_dot_trans = btDot(dirB,translation); + + btScalar denom = 1.0f - dirA_dot_dirB * dirA_dot_dirB; + + if ( denom == 0.0f ) { + tA = 0.0f; + } else { + tA = ( dirA_dot_trans - dirB_dot_trans * dirA_dot_dirB ) / denom; + if ( tA < -hlenA ) + tA = -hlenA; + else if ( tA > hlenA ) + tA = hlenA; + } + + tB = tA * dirA_dot_dirB - dirB_dot_trans; + + if ( tB < -hlenB ) { + tB = -hlenB; + tA = tB * dirA_dot_dirB + dirA_dot_trans; + + if ( tA < -hlenA ) + tA = -hlenA; + else if ( tA > hlenA ) + tA = hlenA; + } else if ( tB > hlenB ) { + tB = hlenB; + tA = tB * dirA_dot_dirB + dirA_dot_trans; + + if ( tA < -hlenA ) + tA = -hlenA; + else if ( tA > hlenA ) + tA = hlenA; + } + + // compute the closest points relative to segment centers. + + offsetA = dirA * tA; + offsetB = dirB * tB; + + ptsVector = translation - offsetA + offsetB; +} + + +static SIMD_FORCE_INLINE btScalar capsuleCapsuleDistance( + btVector3& normalOnB, + btVector3& pointOnB, + btScalar capsuleLengthA, + btScalar capsuleRadiusA, + btScalar capsuleLengthB, + btScalar capsuleRadiusB, + int capsuleAxisA, + int capsuleAxisB, + const btTransform& transformA, + const btTransform& transformB, + btScalar distanceThreshold ) +{ + btVector3 directionA = transformA.getBasis().getColumn(capsuleAxisA); + btVector3 translationA = transformA.getOrigin(); + btVector3 directionB = transformB.getBasis().getColumn(capsuleAxisB); + btVector3 translationB = transformB.getOrigin(); + + // translation between centers + + btVector3 translation = translationB - translationA; + + // compute the closest points of the capsule line segments + + btVector3 ptsVector; // the vector between the closest points + + btVector3 offsetA, offsetB; // offsets from segment centers to their closest points + btScalar tA, tB; // parameters on line segment + + segmentsClosestPoints( ptsVector, offsetA, offsetB, tA, tB, translation, + directionA, capsuleLengthA, directionB, capsuleLengthB ); + + btScalar distance = ptsVector.length() - capsuleRadiusA - capsuleRadiusB; + + if ( distance > distanceThreshold ) + return distance; + + btScalar lenSqr = ptsVector.length2(); + if (lenSqr<= (SIMD_EPSILON*SIMD_EPSILON)) + { + //degenerate case where 2 capsules are likely at the same location: take a vector tangential to 'directionA' + btVector3 q; + btPlaneSpace1(directionA,normalOnB,q); + } else + { + // compute the contact normal + normalOnB = ptsVector*-btRecipSqrt(lenSqr); + } + pointOnB = transformB.getOrigin()+offsetB + normalOnB * capsuleRadiusB; + + return distance; +} + + + + + + + +////////// + + + + + +btConvexConvexAlgorithm::CreateFunc::CreateFunc(btSimplexSolverInterface* simplexSolver, btConvexPenetrationDepthSolver* pdSolver) +{ + m_numPerturbationIterations = 0; + m_minimumPointsPerturbationThreshold = 3; + m_simplexSolver = simplexSolver; + m_pdSolver = pdSolver; +} + +btConvexConvexAlgorithm::CreateFunc::~CreateFunc() +{ +} + +btConvexConvexAlgorithm::btConvexConvexAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* body0,btCollisionObject* body1,btSimplexSolverInterface* simplexSolver, btConvexPenetrationDepthSolver* pdSolver,int numPerturbationIterations, int minimumPointsPerturbationThreshold) +: btActivatingCollisionAlgorithm(ci,body0,body1), +m_simplexSolver(simplexSolver), +m_pdSolver(pdSolver), +m_ownManifold (false), +m_manifoldPtr(mf), +m_lowLevelOfDetail(false), +#ifdef USE_SEPDISTANCE_UTIL2 +m_sepDistance((static_cast(body0->getCollisionShape()))->getAngularMotionDisc(), + (static_cast(body1->getCollisionShape()))->getAngularMotionDisc()), +#endif +m_numPerturbationIterations(numPerturbationIterations), +m_minimumPointsPerturbationThreshold(minimumPointsPerturbationThreshold) +{ + (void)body0; + (void)body1; +} + + + + +btConvexConvexAlgorithm::~btConvexConvexAlgorithm() +{ + if (m_ownManifold) + { + if (m_manifoldPtr) + m_dispatcher->releaseManifold(m_manifoldPtr); + } +} + +void btConvexConvexAlgorithm ::setLowLevelOfDetail(bool useLowLevel) +{ + m_lowLevelOfDetail = useLowLevel; +} + + +struct btPerturbedContactResult : public btManifoldResult +{ + btManifoldResult* m_originalManifoldResult; + btTransform m_transformA; + btTransform m_transformB; + btTransform m_unPerturbedTransform; + bool m_perturbA; + btIDebugDraw* m_debugDrawer; + + + btPerturbedContactResult(btManifoldResult* originalResult,const btTransform& transformA,const btTransform& transformB,const btTransform& unPerturbedTransform,bool perturbA,btIDebugDraw* debugDrawer) + :m_originalManifoldResult(originalResult), + m_transformA(transformA), + m_transformB(transformB), + m_perturbA(perturbA), + m_unPerturbedTransform(unPerturbedTransform), + m_debugDrawer(debugDrawer) + { + } + virtual ~ btPerturbedContactResult() + { + } + + virtual void addContactPoint(const btVector3& normalOnBInWorld,const btVector3& pointInWorld,btScalar orgDepth) + { + btVector3 endPt,startPt; + btScalar newDepth; + btVector3 newNormal; + + if (m_perturbA) + { + btVector3 endPtOrg = pointInWorld + normalOnBInWorld*orgDepth; + endPt = (m_unPerturbedTransform*m_transformA.inverse())(endPtOrg); + newDepth = (endPt - pointInWorld).dot(normalOnBInWorld); + startPt = endPt+normalOnBInWorld*newDepth; + } else + { + endPt = pointInWorld + normalOnBInWorld*orgDepth; + startPt = (m_unPerturbedTransform*m_transformB.inverse())(pointInWorld); + newDepth = (endPt - startPt).dot(normalOnBInWorld); + + } + +//#define DEBUG_CONTACTS 1 +#ifdef DEBUG_CONTACTS + m_debugDrawer->drawLine(startPt,endPt,btVector3(1,0,0)); + m_debugDrawer->drawSphere(startPt,0.05,btVector3(0,1,0)); + m_debugDrawer->drawSphere(endPt,0.05,btVector3(0,0,1)); +#endif //DEBUG_CONTACTS + + + m_originalManifoldResult->addContactPoint(normalOnBInWorld,startPt,newDepth); + } + +}; + +extern btScalar gContactBreakingThreshold; + + +// +// Convex-Convex collision algorithm +// +void btConvexConvexAlgorithm ::processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + + if (!m_manifoldPtr) + { + //swapped? + m_manifoldPtr = m_dispatcher->getNewManifold(body0,body1); + m_ownManifold = true; + } + resultOut->setPersistentManifold(m_manifoldPtr); + + //comment-out next line to test multi-contact generation + //resultOut->getPersistentManifold()->clearManifold(); + + + btConvexShape* min0 = static_cast(body0->getCollisionShape()); + btConvexShape* min1 = static_cast(body1->getCollisionShape()); + + btVector3 normalOnB; + btVector3 pointOnBWorld; +#ifndef BT_DISABLE_CAPSULE_CAPSULE_COLLIDER + if ((min0->getShapeType() == CAPSULE_SHAPE_PROXYTYPE) && (min1->getShapeType() == CAPSULE_SHAPE_PROXYTYPE)) + { + btCapsuleShape* capsuleA = (btCapsuleShape*) min0; + btCapsuleShape* capsuleB = (btCapsuleShape*) min1; + btVector3 localScalingA = capsuleA->getLocalScaling(); + btVector3 localScalingB = capsuleB->getLocalScaling(); + + btScalar threshold = m_manifoldPtr->getContactBreakingThreshold(); + + btScalar dist = capsuleCapsuleDistance(normalOnB, pointOnBWorld,capsuleA->getHalfHeight(),capsuleA->getRadius(), + capsuleB->getHalfHeight(),capsuleB->getRadius(),capsuleA->getUpAxis(),capsuleB->getUpAxis(), + body0->getWorldTransform(),body1->getWorldTransform(),threshold); + + if (dist=(SIMD_EPSILON*SIMD_EPSILON)); + resultOut->addContactPoint(normalOnB,pointOnBWorld,dist); + } + resultOut->refreshContactPoints(); + return; + } +#endif //BT_DISABLE_CAPSULE_CAPSULE_COLLIDER + + +#ifdef USE_SEPDISTANCE_UTIL2 + m_sepDistance.updateSeparatingDistance(body0->getWorldTransform(),body1->getWorldTransform()); + if (!dispatchInfo.m_useConvexConservativeDistanceUtil || m_sepDistance.getConservativeSeparatingDistance()<=0.f) +#endif //USE_SEPDISTANCE_UTIL2 + + { + + + btGjkPairDetector::ClosestPointInput input; + + btGjkPairDetector gjkPairDetector(min0,min1,m_simplexSolver,m_pdSolver); + //TODO: if (dispatchInfo.m_useContinuous) + gjkPairDetector.setMinkowskiA(min0); + gjkPairDetector.setMinkowskiB(min1); + +#ifdef USE_SEPDISTANCE_UTIL2 + if (dispatchInfo.m_useConvexConservativeDistanceUtil) + { + input.m_maximumDistanceSquared = BT_LARGE_FLOAT; + } else +#endif //USE_SEPDISTANCE_UTIL2 + { + input.m_maximumDistanceSquared = min0->getMargin() + min1->getMargin() + m_manifoldPtr->getContactBreakingThreshold(); + input.m_maximumDistanceSquared*= input.m_maximumDistanceSquared; + } + + input.m_stackAlloc = dispatchInfo.m_stackAllocator; + input.m_transformA = body0->getWorldTransform(); + input.m_transformB = body1->getWorldTransform(); + + gjkPairDetector.getClosestPoints(input,*resultOut,dispatchInfo.m_debugDraw); + + btVector3 v0,v1; + btVector3 sepNormalWorldSpace; + + +#ifdef USE_SEPDISTANCE_UTIL2 + btScalar sepDist = 0.f; + if (dispatchInfo.m_useConvexConservativeDistanceUtil) + { + sepDist = gjkPairDetector.getCachedSeparatingDistance(); + if (sepDist>SIMD_EPSILON) + { + sepDist += dispatchInfo.m_convexConservativeDistanceThreshold; + //now perturbe directions to get multiple contact points + sepNormalWorldSpace = gjkPairDetector.getCachedSeparatingAxis().normalized(); + btPlaneSpace1(sepNormalWorldSpace,v0,v1); + } + } +#endif //USE_SEPDISTANCE_UTIL2 + + //now perform 'm_numPerturbationIterations' collision queries with the perturbated collision objects + + //perform perturbation when more then 'm_minimumPointsPerturbationThreshold' points + if (resultOut->getPersistentManifold()->getNumContacts() < m_minimumPointsPerturbationThreshold) + { + + int i; + + bool perturbeA = true; + const btScalar angleLimit = 0.125f * SIMD_PI; + btScalar perturbeAngle; + btScalar radiusA = min0->getAngularMotionDisc(); + btScalar radiusB = min1->getAngularMotionDisc(); + if (radiusA < radiusB) + { + perturbeAngle = gContactBreakingThreshold /radiusA; + perturbeA = true; + } else + { + perturbeAngle = gContactBreakingThreshold / radiusB; + perturbeA = false; + } + if ( perturbeAngle > angleLimit ) + perturbeAngle = angleLimit; + + btTransform unPerturbedTransform; + if (perturbeA) + { + unPerturbedTransform = input.m_transformA; + } else + { + unPerturbedTransform = input.m_transformB; + } + + for ( i=0;igetWorldTransform().getBasis()); + input.m_transformB = body1->getWorldTransform(); +#ifdef DEBUG_CONTACTS + dispatchInfo.m_debugDraw->drawTransform(input.m_transformA,10.0); +#endif //DEBUG_CONTACTS + } else + { + input.m_transformA = body0->getWorldTransform(); + input.m_transformB.setBasis( btMatrix3x3(rotq.inverse()*perturbeRot*rotq)*body1->getWorldTransform().getBasis()); +#ifdef DEBUG_CONTACTS + dispatchInfo.m_debugDraw->drawTransform(input.m_transformB,10.0); +#endif + } + + btPerturbedContactResult perturbedResultOut(resultOut,input.m_transformA,input.m_transformB,unPerturbedTransform,perturbeA,dispatchInfo.m_debugDraw); + gjkPairDetector.getClosestPoints(input,perturbedResultOut,dispatchInfo.m_debugDraw); + + + } + } + + + +#ifdef USE_SEPDISTANCE_UTIL2 + if (dispatchInfo.m_useConvexConservativeDistanceUtil && (sepDist>SIMD_EPSILON)) + { + m_sepDistance.initSeparatingDistance(gjkPairDetector.getCachedSeparatingAxis(),sepDist,body0->getWorldTransform(),body1->getWorldTransform()); + } +#endif //USE_SEPDISTANCE_UTIL2 + + + } + + if (m_ownManifold) + { + resultOut->refreshContactPoints(); + } + +} + + + +bool disableCcd = false; +btScalar btConvexConvexAlgorithm::calculateTimeOfImpact(btCollisionObject* col0,btCollisionObject* col1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + (void)resultOut; + (void)dispatchInfo; + ///Rather then checking ALL pairs, only calculate TOI when motion exceeds threshold + + ///Linear motion for one of objects needs to exceed m_ccdSquareMotionThreshold + ///col0->m_worldTransform, + btScalar resultFraction = btScalar(1.); + + + btScalar squareMot0 = (col0->getInterpolationWorldTransform().getOrigin() - col0->getWorldTransform().getOrigin()).length2(); + btScalar squareMot1 = (col1->getInterpolationWorldTransform().getOrigin() - col1->getWorldTransform().getOrigin()).length2(); + + if (squareMot0 < col0->getCcdSquareMotionThreshold() && + squareMot1 < col1->getCcdSquareMotionThreshold()) + return resultFraction; + + if (disableCcd) + return btScalar(1.); + + + //An adhoc way of testing the Continuous Collision Detection algorithms + //One object is approximated as a sphere, to simplify things + //Starting in penetration should report no time of impact + //For proper CCD, better accuracy and handling of 'allowed' penetration should be added + //also the mainloop of the physics should have a kind of toi queue (something like Brian Mirtich's application of Timewarp for Rigidbodies) + + + /// Convex0 against sphere for Convex1 + { + btConvexShape* convex0 = static_cast(col0->getCollisionShape()); + + btSphereShape sphere1(col1->getCcdSweptSphereRadius()); //todo: allow non-zero sphere sizes, for better approximation + btConvexCast::CastResult result; + btVoronoiSimplexSolver voronoiSimplex; + //SubsimplexConvexCast ccd0(&sphere,min0,&voronoiSimplex); + ///Simplification, one object is simplified as a sphere + btGjkConvexCast ccd1( convex0 ,&sphere1,&voronoiSimplex); + //ContinuousConvexCollision ccd(min0,min1,&voronoiSimplex,0); + if (ccd1.calcTimeOfImpact(col0->getWorldTransform(),col0->getInterpolationWorldTransform(), + col1->getWorldTransform(),col1->getInterpolationWorldTransform(),result)) + { + + //store result.m_fraction in both bodies + + if (col0->getHitFraction()> result.m_fraction) + col0->setHitFraction( result.m_fraction ); + + if (col1->getHitFraction() > result.m_fraction) + col1->setHitFraction( result.m_fraction); + + if (resultFraction > result.m_fraction) + resultFraction = result.m_fraction; + + } + + + + + } + + /// Sphere (for convex0) against Convex1 + { + btConvexShape* convex1 = static_cast(col1->getCollisionShape()); + + btSphereShape sphere0(col0->getCcdSweptSphereRadius()); //todo: allow non-zero sphere sizes, for better approximation + btConvexCast::CastResult result; + btVoronoiSimplexSolver voronoiSimplex; + //SubsimplexConvexCast ccd0(&sphere,min0,&voronoiSimplex); + ///Simplification, one object is simplified as a sphere + btGjkConvexCast ccd1(&sphere0,convex1,&voronoiSimplex); + //ContinuousConvexCollision ccd(min0,min1,&voronoiSimplex,0); + if (ccd1.calcTimeOfImpact(col0->getWorldTransform(),col0->getInterpolationWorldTransform(), + col1->getWorldTransform(),col1->getInterpolationWorldTransform(),result)) + { + + //store result.m_fraction in both bodies + + if (col0->getHitFraction() > result.m_fraction) + col0->setHitFraction( result.m_fraction); + + if (col1->getHitFraction() > result.m_fraction) + col1->setHitFraction( result.m_fraction); + + if (resultFraction > result.m_fraction) + resultFraction = result.m_fraction; + + } + } + + return resultFraction; + +} + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.h new file mode 100644 index 000000000..4d10ffca7 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.h @@ -0,0 +1,109 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 CONVEX_CONVEX_ALGORITHM_H +#define CONVEX_CONVEX_ALGORITHM_H + +#include "btActivatingCollisionAlgorithm.h" +#include "BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h" +#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.h" +#include "btCollisionCreateFunc.h" +#include "btCollisionDispatcher.h" +#include "LinearMath/btTransformUtil.h" //for btConvexSeparatingDistanceUtil + +class btConvexPenetrationDepthSolver; + +///Enabling USE_SEPDISTANCE_UTIL2 requires 100% reliable distance computation. However, when using large size ratios GJK can be imprecise +///so the distance is not conservative. In that case, enabling this USE_SEPDISTANCE_UTIL2 would result in failing/missing collisions. +///Either improve GJK for large size ratios (testing a 100 units versus a 0.1 unit object) or only enable the util +///for certain pairs that have a small size ratio + +#define USE_SEPDISTANCE_UTIL2 1 + +///The convexConvexAlgorithm collision algorithm implements time of impact, convex closest points and penetration depth calculations between two convex objects. +///Multiple contact points are calculated by perturbing the orientation of the smallest object orthogonal to the separating normal. +///This idea was described by Gino van den Bergen in this forum topic http://www.bulletphysics.com/Bullet/phpBB3/viewtopic.php?f=4&t=288&p=888#p888 +class btConvexConvexAlgorithm : public btActivatingCollisionAlgorithm +{ +#ifdef USE_SEPDISTANCE_UTIL2 + btConvexSeparatingDistanceUtil m_sepDistance; +#endif + btSimplexSolverInterface* m_simplexSolver; + btConvexPenetrationDepthSolver* m_pdSolver; + + + bool m_ownManifold; + btPersistentManifold* m_manifoldPtr; + bool m_lowLevelOfDetail; + + int m_numPerturbationIterations; + int m_minimumPointsPerturbationThreshold; + + + ///cache separating vector to speedup collision detection + + +public: + + btConvexConvexAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* body0,btCollisionObject* body1, btSimplexSolverInterface* simplexSolver, btConvexPenetrationDepthSolver* pdSolver, int numPerturbationIterations, int minimumPointsPerturbationThreshold); + + + virtual ~btConvexConvexAlgorithm(); + + virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual void getAllContactManifolds(btManifoldArray& manifoldArray) + { + ///should we use m_ownManifold to avoid adding duplicates? + if (m_manifoldPtr && m_ownManifold) + manifoldArray.push_back(m_manifoldPtr); + } + + + void setLowLevelOfDetail(bool useLowLevel); + + + const btPersistentManifold* getManifold() + { + return m_manifoldPtr; + } + + struct CreateFunc :public btCollisionAlgorithmCreateFunc + { + + btConvexPenetrationDepthSolver* m_pdSolver; + btSimplexSolverInterface* m_simplexSolver; + int m_numPerturbationIterations; + int m_minimumPointsPerturbationThreshold; + + CreateFunc(btSimplexSolverInterface* simplexSolver, btConvexPenetrationDepthSolver* pdSolver); + + virtual ~CreateFunc(); + + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + void* mem = ci.m_dispatcher1->allocateCollisionAlgorithm(sizeof(btConvexConvexAlgorithm)); + return new(mem) btConvexConvexAlgorithm(ci.m_manifold,ci,body0,body1,m_simplexSolver,m_pdSolver,m_numPerturbationIterations,m_minimumPointsPerturbationThreshold); + } + }; + + +}; + +#endif //CONVEX_CONVEX_ALGORITHM_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvexPlaneCollisionAlgorithm.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvexPlaneCollisionAlgorithm.cpp new file mode 100644 index 000000000..dda85dc69 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvexPlaneCollisionAlgorithm.cpp @@ -0,0 +1,155 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btConvexPlaneCollisionAlgorithm.h" + +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "BulletCollision/CollisionShapes/btConvexShape.h" +#include "BulletCollision/CollisionShapes/btStaticPlaneShape.h" + +//#include + +btConvexPlaneCollisionAlgorithm::btConvexPlaneCollisionAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* col0,btCollisionObject* col1, bool isSwapped, int numPerturbationIterations,int minimumPointsPerturbationThreshold) +: btCollisionAlgorithm(ci), +m_ownManifold(false), +m_manifoldPtr(mf), +m_isSwapped(isSwapped), +m_numPerturbationIterations(numPerturbationIterations), +m_minimumPointsPerturbationThreshold(minimumPointsPerturbationThreshold) +{ + btCollisionObject* convexObj = m_isSwapped? col1 : col0; + btCollisionObject* planeObj = m_isSwapped? col0 : col1; + + if (!m_manifoldPtr && m_dispatcher->needsCollision(convexObj,planeObj)) + { + m_manifoldPtr = m_dispatcher->getNewManifold(convexObj,planeObj); + m_ownManifold = true; + } +} + + +btConvexPlaneCollisionAlgorithm::~btConvexPlaneCollisionAlgorithm() +{ + if (m_ownManifold) + { + if (m_manifoldPtr) + m_dispatcher->releaseManifold(m_manifoldPtr); + } +} + +void btConvexPlaneCollisionAlgorithm::collideSingleContact (const btQuaternion& perturbeRot, btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + btCollisionObject* convexObj = m_isSwapped? body1 : body0; + btCollisionObject* planeObj = m_isSwapped? body0: body1; + + btConvexShape* convexShape = (btConvexShape*) convexObj->getCollisionShape(); + btStaticPlaneShape* planeShape = (btStaticPlaneShape*) planeObj->getCollisionShape(); + + bool hasCollision = false; + const btVector3& planeNormal = planeShape->getPlaneNormal(); + const btScalar& planeConstant = planeShape->getPlaneConstant(); + + btTransform convexWorldTransform = convexObj->getWorldTransform(); + btTransform convexInPlaneTrans; + convexInPlaneTrans= planeObj->getWorldTransform().inverse() * convexWorldTransform; + //now perturbe the convex-world transform + convexWorldTransform.getBasis()*=btMatrix3x3(perturbeRot); + btTransform planeInConvex; + planeInConvex= convexWorldTransform.inverse() * planeObj->getWorldTransform(); + + btVector3 vtx = convexShape->localGetSupportingVertex(planeInConvex.getBasis()*-planeNormal); + + btVector3 vtxInPlane = convexInPlaneTrans(vtx); + btScalar distance = (planeNormal.dot(vtxInPlane) - planeConstant); + + btVector3 vtxInPlaneProjected = vtxInPlane - distance*planeNormal; + btVector3 vtxInPlaneWorld = planeObj->getWorldTransform() * vtxInPlaneProjected; + + hasCollision = distance < m_manifoldPtr->getContactBreakingThreshold(); + resultOut->setPersistentManifold(m_manifoldPtr); + if (hasCollision) + { + /// report a contact. internally this will be kept persistent, and contact reduction is done + btVector3 normalOnSurfaceB = planeObj->getWorldTransform().getBasis() * planeNormal; + btVector3 pOnB = vtxInPlaneWorld; + resultOut->addContactPoint(normalOnSurfaceB,pOnB,distance); + } +} + + +void btConvexPlaneCollisionAlgorithm::processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + (void)dispatchInfo; + if (!m_manifoldPtr) + return; + + btCollisionObject* convexObj = m_isSwapped? body1 : body0; + btCollisionObject* planeObj = m_isSwapped? body0: body1; + + btConvexShape* convexShape = (btConvexShape*) convexObj->getCollisionShape(); + btStaticPlaneShape* planeShape = (btStaticPlaneShape*) planeObj->getCollisionShape(); + + + const btVector3& planeNormal = planeShape->getPlaneNormal(); + //const btScalar& planeConstant = planeShape->getPlaneConstant(); + + //first perform a collision query with the non-perturbated collision objects + { + btQuaternion rotq(0,0,0,1); + collideSingleContact(rotq,body0,body1,dispatchInfo,resultOut); + } + + if (resultOut->getPersistentManifold()->getNumContacts()getAngularMotionDisc(); + perturbeAngle = gContactBreakingThreshold / radius; + if ( perturbeAngle > angleLimit ) + perturbeAngle = angleLimit; + + btQuaternion perturbeRot(v0,perturbeAngle); + for (int i=0;igetNumContacts()) + { + resultOut->refreshContactPoints(); + } + } +} + +btScalar btConvexPlaneCollisionAlgorithm::calculateTimeOfImpact(btCollisionObject* col0,btCollisionObject* col1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + (void)resultOut; + (void)dispatchInfo; + (void)col0; + (void)col1; + + //not yet + return btScalar(1.); +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvexPlaneCollisionAlgorithm.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvexPlaneCollisionAlgorithm.h new file mode 100644 index 000000000..f49ac45e7 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvexPlaneCollisionAlgorithm.h @@ -0,0 +1,84 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 CONVEX_PLANE_COLLISION_ALGORITHM_H +#define CONVEX_PLANE_COLLISION_ALGORITHM_H + +#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "BulletCollision/CollisionDispatch/btCollisionCreateFunc.h" +class btPersistentManifold; +#include "btCollisionDispatcher.h" + +#include "LinearMath/btVector3.h" + +/// btSphereBoxCollisionAlgorithm provides sphere-box collision detection. +/// Other features are frame-coherency (persistent data) and collision response. +class btConvexPlaneCollisionAlgorithm : public btCollisionAlgorithm +{ + bool m_ownManifold; + btPersistentManifold* m_manifoldPtr; + bool m_isSwapped; + int m_numPerturbationIterations; + int m_minimumPointsPerturbationThreshold; + +public: + + btConvexPlaneCollisionAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* col0,btCollisionObject* col1, bool isSwapped, int numPerturbationIterations,int minimumPointsPerturbationThreshold); + + virtual ~btConvexPlaneCollisionAlgorithm(); + + virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + void collideSingleContact (const btQuaternion& perturbeRot, btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual void getAllContactManifolds(btManifoldArray& manifoldArray) + { + if (m_manifoldPtr && m_ownManifold) + { + manifoldArray.push_back(m_manifoldPtr); + } + } + + struct CreateFunc :public btCollisionAlgorithmCreateFunc + { + int m_numPerturbationIterations; + int m_minimumPointsPerturbationThreshold; + + CreateFunc() + : m_numPerturbationIterations(1), + m_minimumPointsPerturbationThreshold(1) + { + } + + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + void* mem = ci.m_dispatcher1->allocateCollisionAlgorithm(sizeof(btConvexPlaneCollisionAlgorithm)); + if (!m_swapped) + { + return new(mem) btConvexPlaneCollisionAlgorithm(0,ci,body0,body1,false,m_numPerturbationIterations,m_minimumPointsPerturbationThreshold); + } else + { + return new(mem) btConvexPlaneCollisionAlgorithm(0,ci,body0,body1,true,m_numPerturbationIterations,m_minimumPointsPerturbationThreshold); + } + } + }; + +}; + +#endif //CONVEX_PLANE_COLLISION_ALGORITHM_H + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.cpp new file mode 100644 index 000000000..c27d8ce07 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.cpp @@ -0,0 +1,298 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btDefaultCollisionConfiguration.h" + +#include "BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btConvexPlaneCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btBoxBoxCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.h" +#ifdef USE_BUGGY_SPHERE_BOX_ALGORITHM +#include "BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.h" +#endif //USE_BUGGY_SPHERE_BOX_ALGORITHM +#include "BulletCollision/CollisionDispatch/btSphereTriangleCollisionAlgorithm.h" +#include "BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h" +#include "BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.h" +#include "BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.h" + + + +#include "LinearMath/btStackAlloc.h" +#include "LinearMath/btPoolAllocator.h" + + + + + +btDefaultCollisionConfiguration::btDefaultCollisionConfiguration(const btDefaultCollisionConstructionInfo& constructionInfo) +//btDefaultCollisionConfiguration::btDefaultCollisionConfiguration(btStackAlloc* stackAlloc,btPoolAllocator* persistentManifoldPool,btPoolAllocator* collisionAlgorithmPool) +{ + + void* mem = btAlignedAlloc(sizeof(btVoronoiSimplexSolver),16); + m_simplexSolver = new (mem)btVoronoiSimplexSolver(); + + if (constructionInfo.m_useEpaPenetrationAlgorithm) + { + mem = btAlignedAlloc(sizeof(btGjkEpaPenetrationDepthSolver),16); + m_pdSolver = new (mem)btGjkEpaPenetrationDepthSolver; + }else + { + mem = btAlignedAlloc(sizeof(btMinkowskiPenetrationDepthSolver),16); + m_pdSolver = new (mem)btMinkowskiPenetrationDepthSolver; + } + + //default CreationFunctions, filling the m_doubleDispatch table + mem = btAlignedAlloc(sizeof(btConvexConvexAlgorithm::CreateFunc),16); + m_convexConvexCreateFunc = new(mem) btConvexConvexAlgorithm::CreateFunc(m_simplexSolver,m_pdSolver); + mem = btAlignedAlloc(sizeof(btConvexConcaveCollisionAlgorithm::CreateFunc),16); + m_convexConcaveCreateFunc = new (mem)btConvexConcaveCollisionAlgorithm::CreateFunc; + mem = btAlignedAlloc(sizeof(btConvexConcaveCollisionAlgorithm::CreateFunc),16); + m_swappedConvexConcaveCreateFunc = new (mem)btConvexConcaveCollisionAlgorithm::SwappedCreateFunc; + mem = btAlignedAlloc(sizeof(btCompoundCollisionAlgorithm::CreateFunc),16); + m_compoundCreateFunc = new (mem)btCompoundCollisionAlgorithm::CreateFunc; + mem = btAlignedAlloc(sizeof(btCompoundCollisionAlgorithm::SwappedCreateFunc),16); + m_swappedCompoundCreateFunc = new (mem)btCompoundCollisionAlgorithm::SwappedCreateFunc; + mem = btAlignedAlloc(sizeof(btEmptyAlgorithm::CreateFunc),16); + m_emptyCreateFunc = new(mem) btEmptyAlgorithm::CreateFunc; + + mem = btAlignedAlloc(sizeof(btSphereSphereCollisionAlgorithm::CreateFunc),16); + m_sphereSphereCF = new(mem) btSphereSphereCollisionAlgorithm::CreateFunc; +#ifdef USE_BUGGY_SPHERE_BOX_ALGORITHM + mem = btAlignedAlloc(sizeof(btSphereBoxCollisionAlgorithm::CreateFunc),16); + m_sphereBoxCF = new(mem) btSphereBoxCollisionAlgorithm::CreateFunc; + mem = btAlignedAlloc(sizeof(btSphereBoxCollisionAlgorithm::CreateFunc),16); + m_boxSphereCF = new (mem)btSphereBoxCollisionAlgorithm::CreateFunc; + m_boxSphereCF->m_swapped = true; +#endif //USE_BUGGY_SPHERE_BOX_ALGORITHM + + mem = btAlignedAlloc(sizeof(btSphereTriangleCollisionAlgorithm::CreateFunc),16); + m_sphereTriangleCF = new (mem)btSphereTriangleCollisionAlgorithm::CreateFunc; + mem = btAlignedAlloc(sizeof(btSphereTriangleCollisionAlgorithm::CreateFunc),16); + m_triangleSphereCF = new (mem)btSphereTriangleCollisionAlgorithm::CreateFunc; + m_triangleSphereCF->m_swapped = true; + + mem = btAlignedAlloc(sizeof(btBoxBoxCollisionAlgorithm::CreateFunc),16); + m_boxBoxCF = new(mem)btBoxBoxCollisionAlgorithm::CreateFunc; + + //convex versus plane + mem = btAlignedAlloc (sizeof(btConvexPlaneCollisionAlgorithm::CreateFunc),16); + m_convexPlaneCF = new (mem) btConvexPlaneCollisionAlgorithm::CreateFunc; + mem = btAlignedAlloc (sizeof(btConvexPlaneCollisionAlgorithm::CreateFunc),16); + m_planeConvexCF = new (mem) btConvexPlaneCollisionAlgorithm::CreateFunc; + m_planeConvexCF->m_swapped = true; + + ///calculate maximum element size, big enough to fit any collision algorithm in the memory pool + int maxSize = sizeof(btConvexConvexAlgorithm); + int maxSize2 = sizeof(btConvexConcaveCollisionAlgorithm); + int maxSize3 = sizeof(btCompoundCollisionAlgorithm); + int sl = sizeof(btConvexSeparatingDistanceUtil); + sl = sizeof(btGjkPairDetector); + int collisionAlgorithmMaxElementSize = btMax(maxSize,constructionInfo.m_customCollisionAlgorithmMaxElementSize); + collisionAlgorithmMaxElementSize = btMax(collisionAlgorithmMaxElementSize,maxSize2); + collisionAlgorithmMaxElementSize = btMax(collisionAlgorithmMaxElementSize,maxSize3); + + if (constructionInfo.m_stackAlloc) + { + m_ownsStackAllocator = false; + this->m_stackAlloc = constructionInfo.m_stackAlloc; + } else + { + m_ownsStackAllocator = true; + void* mem = btAlignedAlloc(sizeof(btStackAlloc),16); + m_stackAlloc = new(mem)btStackAlloc(constructionInfo.m_defaultStackAllocatorSize); + } + + if (constructionInfo.m_persistentManifoldPool) + { + m_ownsPersistentManifoldPool = false; + m_persistentManifoldPool = constructionInfo.m_persistentManifoldPool; + } else + { + m_ownsPersistentManifoldPool = true; + void* mem = btAlignedAlloc(sizeof(btPoolAllocator),16); + m_persistentManifoldPool = new (mem) btPoolAllocator(sizeof(btPersistentManifold),constructionInfo.m_defaultMaxPersistentManifoldPoolSize); + } + + if (constructionInfo.m_collisionAlgorithmPool) + { + m_ownsCollisionAlgorithmPool = false; + m_collisionAlgorithmPool = constructionInfo.m_collisionAlgorithmPool; + } else + { + m_ownsCollisionAlgorithmPool = true; + void* mem = btAlignedAlloc(sizeof(btPoolAllocator),16); + m_collisionAlgorithmPool = new(mem) btPoolAllocator(collisionAlgorithmMaxElementSize,constructionInfo.m_defaultMaxCollisionAlgorithmPoolSize); + } + + +} + +btDefaultCollisionConfiguration::~btDefaultCollisionConfiguration() +{ + if (m_ownsStackAllocator) + { + m_stackAlloc->destroy(); + m_stackAlloc->~btStackAlloc(); + btAlignedFree(m_stackAlloc); + } + if (m_ownsCollisionAlgorithmPool) + { + m_collisionAlgorithmPool->~btPoolAllocator(); + btAlignedFree(m_collisionAlgorithmPool); + } + if (m_ownsPersistentManifoldPool) + { + m_persistentManifoldPool->~btPoolAllocator(); + btAlignedFree(m_persistentManifoldPool); + } + + m_convexConvexCreateFunc->~btCollisionAlgorithmCreateFunc(); + btAlignedFree( m_convexConvexCreateFunc); + + m_convexConcaveCreateFunc->~btCollisionAlgorithmCreateFunc(); + btAlignedFree( m_convexConcaveCreateFunc); + m_swappedConvexConcaveCreateFunc->~btCollisionAlgorithmCreateFunc(); + btAlignedFree( m_swappedConvexConcaveCreateFunc); + + m_compoundCreateFunc->~btCollisionAlgorithmCreateFunc(); + btAlignedFree( m_compoundCreateFunc); + + m_swappedCompoundCreateFunc->~btCollisionAlgorithmCreateFunc(); + btAlignedFree( m_swappedCompoundCreateFunc); + + m_emptyCreateFunc->~btCollisionAlgorithmCreateFunc(); + btAlignedFree( m_emptyCreateFunc); + + m_sphereSphereCF->~btCollisionAlgorithmCreateFunc(); + btAlignedFree( m_sphereSphereCF); + +#ifdef USE_BUGGY_SPHERE_BOX_ALGORITHM + m_sphereBoxCF->~btCollisionAlgorithmCreateFunc(); + btAlignedFree( m_sphereBoxCF); + m_boxSphereCF->~btCollisionAlgorithmCreateFunc(); + btAlignedFree( m_boxSphereCF); +#endif //USE_BUGGY_SPHERE_BOX_ALGORITHM + + m_sphereTriangleCF->~btCollisionAlgorithmCreateFunc(); + btAlignedFree( m_sphereTriangleCF); + m_triangleSphereCF->~btCollisionAlgorithmCreateFunc(); + btAlignedFree( m_triangleSphereCF); + m_boxBoxCF->~btCollisionAlgorithmCreateFunc(); + btAlignedFree( m_boxBoxCF); + + m_convexPlaneCF->~btCollisionAlgorithmCreateFunc(); + btAlignedFree( m_convexPlaneCF); + m_planeConvexCF->~btCollisionAlgorithmCreateFunc(); + btAlignedFree( m_planeConvexCF); + + m_simplexSolver->~btVoronoiSimplexSolver(); + btAlignedFree(m_simplexSolver); + + m_pdSolver->~btConvexPenetrationDepthSolver(); + + btAlignedFree(m_pdSolver); + + +} + + +btCollisionAlgorithmCreateFunc* btDefaultCollisionConfiguration::getCollisionAlgorithmCreateFunc(int proxyType0,int proxyType1) +{ + + + + if ((proxyType0 == SPHERE_SHAPE_PROXYTYPE) && (proxyType1==SPHERE_SHAPE_PROXYTYPE)) + { + return m_sphereSphereCF; + } +#ifdef USE_BUGGY_SPHERE_BOX_ALGORITHM + if ((proxyType0 == SPHERE_SHAPE_PROXYTYPE) && (proxyType1==BOX_SHAPE_PROXYTYPE)) + { + return m_sphereBoxCF; + } + + if ((proxyType0 == BOX_SHAPE_PROXYTYPE ) && (proxyType1==SPHERE_SHAPE_PROXYTYPE)) + { + return m_boxSphereCF; + } +#endif //USE_BUGGY_SPHERE_BOX_ALGORITHM + + + if ((proxyType0 == SPHERE_SHAPE_PROXYTYPE ) && (proxyType1==TRIANGLE_SHAPE_PROXYTYPE)) + { + return m_sphereTriangleCF; + } + + if ((proxyType0 == TRIANGLE_SHAPE_PROXYTYPE ) && (proxyType1==SPHERE_SHAPE_PROXYTYPE)) + { + return m_triangleSphereCF; + } + + if ((proxyType0 == BOX_SHAPE_PROXYTYPE) && (proxyType1 == BOX_SHAPE_PROXYTYPE)) + { + return m_boxBoxCF; + } + + if (btBroadphaseProxy::isConvex(proxyType0) && (proxyType1 == STATIC_PLANE_PROXYTYPE)) + { + return m_convexPlaneCF; + } + + if (btBroadphaseProxy::isConvex(proxyType1) && (proxyType0 == STATIC_PLANE_PROXYTYPE)) + { + return m_planeConvexCF; + } + + + + if (btBroadphaseProxy::isConvex(proxyType0) && btBroadphaseProxy::isConvex(proxyType1)) + { + return m_convexConvexCreateFunc; + } + + if (btBroadphaseProxy::isConvex(proxyType0) && btBroadphaseProxy::isConcave(proxyType1)) + { + return m_convexConcaveCreateFunc; + } + + if (btBroadphaseProxy::isConvex(proxyType1) && btBroadphaseProxy::isConcave(proxyType0)) + { + return m_swappedConvexConcaveCreateFunc; + } + + if (btBroadphaseProxy::isCompound(proxyType0)) + { + return m_compoundCreateFunc; + } else + { + if (btBroadphaseProxy::isCompound(proxyType1)) + { + return m_swappedCompoundCreateFunc; + } + } + + //failed to find an algorithm + return m_emptyCreateFunc; +} + +void btDefaultCollisionConfiguration::setConvexConvexMultipointIterations(int numPerturbationIterations, int minimumPointsPerturbationThreshold) +{ + btConvexConvexAlgorithm::CreateFunc* convexConvex = (btConvexConvexAlgorithm::CreateFunc*) m_convexConvexCreateFunc; + convexConvex->m_numPerturbationIterations = numPerturbationIterations; + convexConvex->m_minimumPointsPerturbationThreshold = minimumPointsPerturbationThreshold; +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h new file mode 100644 index 000000000..37748663a --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h @@ -0,0 +1,130 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 BT_DEFAULT_COLLISION_CONFIGURATION +#define BT_DEFAULT_COLLISION_CONFIGURATION + +#include "btCollisionConfiguration.h" +class btVoronoiSimplexSolver; +class btConvexPenetrationDepthSolver; + +struct btDefaultCollisionConstructionInfo +{ + btStackAlloc* m_stackAlloc; + btPoolAllocator* m_persistentManifoldPool; + btPoolAllocator* m_collisionAlgorithmPool; + int m_defaultMaxPersistentManifoldPoolSize; + int m_defaultMaxCollisionAlgorithmPoolSize; + int m_customCollisionAlgorithmMaxElementSize; + int m_defaultStackAllocatorSize; + int m_useEpaPenetrationAlgorithm; + + btDefaultCollisionConstructionInfo() + :m_stackAlloc(0), + m_persistentManifoldPool(0), + m_collisionAlgorithmPool(0), + m_defaultMaxPersistentManifoldPoolSize(4096), + m_defaultMaxCollisionAlgorithmPoolSize(4096), + m_customCollisionAlgorithmMaxElementSize(0), + m_defaultStackAllocatorSize(0), + m_useEpaPenetrationAlgorithm(true) + { + } +}; + + + +///btCollisionConfiguration allows to configure Bullet collision detection +///stack allocator, pool memory allocators +///@todo: describe the meaning +class btDefaultCollisionConfiguration : public btCollisionConfiguration +{ + +protected: + + int m_persistentManifoldPoolSize; + + btStackAlloc* m_stackAlloc; + bool m_ownsStackAllocator; + + btPoolAllocator* m_persistentManifoldPool; + bool m_ownsPersistentManifoldPool; + + + btPoolAllocator* m_collisionAlgorithmPool; + bool m_ownsCollisionAlgorithmPool; + + //default simplex/penetration depth solvers + btVoronoiSimplexSolver* m_simplexSolver; + btConvexPenetrationDepthSolver* m_pdSolver; + + //default CreationFunctions, filling the m_doubleDispatch table + btCollisionAlgorithmCreateFunc* m_convexConvexCreateFunc; + btCollisionAlgorithmCreateFunc* m_convexConcaveCreateFunc; + btCollisionAlgorithmCreateFunc* m_swappedConvexConcaveCreateFunc; + btCollisionAlgorithmCreateFunc* m_compoundCreateFunc; + btCollisionAlgorithmCreateFunc* m_swappedCompoundCreateFunc; + btCollisionAlgorithmCreateFunc* m_emptyCreateFunc; + btCollisionAlgorithmCreateFunc* m_sphereSphereCF; +#ifdef USE_BUGGY_SPHERE_BOX_ALGORITHM + btCollisionAlgorithmCreateFunc* m_sphereBoxCF; + btCollisionAlgorithmCreateFunc* m_boxSphereCF; +#endif //USE_BUGGY_SPHERE_BOX_ALGORITHM + + btCollisionAlgorithmCreateFunc* m_boxBoxCF; + btCollisionAlgorithmCreateFunc* m_sphereTriangleCF; + btCollisionAlgorithmCreateFunc* m_triangleSphereCF; + btCollisionAlgorithmCreateFunc* m_planeConvexCF; + btCollisionAlgorithmCreateFunc* m_convexPlaneCF; + +public: + + + btDefaultCollisionConfiguration(const btDefaultCollisionConstructionInfo& constructionInfo = btDefaultCollisionConstructionInfo()); + + virtual ~btDefaultCollisionConfiguration(); + + ///memory pools + virtual btPoolAllocator* getPersistentManifoldPool() + { + return m_persistentManifoldPool; + } + + virtual btPoolAllocator* getCollisionAlgorithmPool() + { + return m_collisionAlgorithmPool; + } + + virtual btStackAlloc* getStackAllocator() + { + return m_stackAlloc; + } + + + virtual btCollisionAlgorithmCreateFunc* getCollisionAlgorithmCreateFunc(int proxyType0,int proxyType1); + + ///Use this method to allow to generate multiple contact points between at once, between two objects using the generic convex-convex algorithm. + ///By default, this feature is disabled for best performance. + ///@param numPerturbationIterations controls the number of collision queries. Set it to zero to disable the feature. + ///@param minimumPointsPerturbationThreshold is the minimum number of points in the contact cache, above which the feature is disabled + ///3 is a good value for both params, if you want to enable the feature. This is because the default contact cache contains a maximum of 4 points, and one collision query at the unperturbed orientation is performed first. + ///See Bullet/Demos/CollisionDemo for an example how this feature gathers multiple points. + ///@todo we could add a per-object setting of those parameters, for level-of-detail collision detection. + void setConvexConvexMultipointIterations(int numPerturbationIterations=3, int minimumPointsPerturbationThreshold = 3); + +}; + +#endif //BT_DEFAULT_COLLISION_CONFIGURATION + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.cpp new file mode 100644 index 000000000..936054387 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.cpp @@ -0,0 +1,34 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btEmptyCollisionAlgorithm.h" + + + +btEmptyAlgorithm::btEmptyAlgorithm(const btCollisionAlgorithmConstructionInfo& ci) + : btCollisionAlgorithm(ci) +{ +} + +void btEmptyAlgorithm::processCollision (btCollisionObject* ,btCollisionObject* ,const btDispatcherInfo& ,btManifoldResult* ) +{ +} + +btScalar btEmptyAlgorithm::calculateTimeOfImpact(btCollisionObject* ,btCollisionObject* ,const btDispatcherInfo& ,btManifoldResult* ) +{ + return btScalar(1.); +} + + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.h new file mode 100644 index 000000000..e54721dec --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.h @@ -0,0 +1,54 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 EMPTY_ALGORITH +#define EMPTY_ALGORITH +#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" +#include "btCollisionCreateFunc.h" +#include "btCollisionDispatcher.h" + +#define ATTRIBUTE_ALIGNED(a) + +///EmptyAlgorithm is a stub for unsupported collision pairs. +///The dispatcher can dispatch a persistent btEmptyAlgorithm to avoid a search every frame. +class btEmptyAlgorithm : public btCollisionAlgorithm +{ + +public: + + btEmptyAlgorithm(const btCollisionAlgorithmConstructionInfo& ci); + + virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual void getAllContactManifolds(btManifoldArray& manifoldArray) + { + } + + struct CreateFunc :public btCollisionAlgorithmCreateFunc + { + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + (void)body0; + (void)body1; + void* mem = ci.m_dispatcher1->allocateCollisionAlgorithm(sizeof(btEmptyAlgorithm)); + return new(mem) btEmptyAlgorithm(ci); + } + }; + +} ATTRIBUTE_ALIGNED(16); + +#endif //EMPTY_ALGORITH diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btGhostObject.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btGhostObject.cpp new file mode 100644 index 000000000..86141fa68 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btGhostObject.cpp @@ -0,0 +1,171 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2008 Erwin Coumans http://bulletphysics.com + +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 "btGhostObject.h" +#include "btCollisionWorld.h" +#include "BulletCollision/CollisionShapes/btConvexShape.h" +#include "LinearMath/btAabbUtil2.h" + +btGhostObject::btGhostObject() +{ + m_internalType = CO_GHOST_OBJECT; +} + +btGhostObject::~btGhostObject() +{ + ///btGhostObject should have been removed from the world, so no overlapping objects + btAssert(!m_overlappingObjects.size()); +} + + +void btGhostObject::addOverlappingObjectInternal(btBroadphaseProxy* otherProxy,btBroadphaseProxy* thisProxy) +{ + btCollisionObject* otherObject = (btCollisionObject*)otherProxy->m_clientObject; + btAssert(otherObject); + ///if this linearSearch becomes too slow (too many overlapping objects) we should add a more appropriate data structure + int index = m_overlappingObjects.findLinearSearch(otherObject); + if (index==m_overlappingObjects.size()) + { + //not found + m_overlappingObjects.push_back(otherObject); + } +} + +void btGhostObject::removeOverlappingObjectInternal(btBroadphaseProxy* otherProxy,btDispatcher* dispatcher,btBroadphaseProxy* thisProxy) +{ + btCollisionObject* otherObject = (btCollisionObject*)otherProxy->m_clientObject; + btAssert(otherObject); + int index = m_overlappingObjects.findLinearSearch(otherObject); + if (index~btHashedOverlappingPairCache(); + btAlignedFree( m_hashPairCache ); +} + +void btPairCachingGhostObject::addOverlappingObjectInternal(btBroadphaseProxy* otherProxy,btBroadphaseProxy* thisProxy) +{ + btBroadphaseProxy*actualThisProxy = thisProxy ? thisProxy : getBroadphaseHandle(); + btAssert(actualThisProxy); + + btCollisionObject* otherObject = (btCollisionObject*)otherProxy->m_clientObject; + btAssert(otherObject); + int index = m_overlappingObjects.findLinearSearch(otherObject); + if (index==m_overlappingObjects.size()) + { + m_overlappingObjects.push_back(otherObject); + m_hashPairCache->addOverlappingPair(actualThisProxy,otherProxy); + } +} + +void btPairCachingGhostObject::removeOverlappingObjectInternal(btBroadphaseProxy* otherProxy,btDispatcher* dispatcher,btBroadphaseProxy* thisProxy1) +{ + btCollisionObject* otherObject = (btCollisionObject*)otherProxy->m_clientObject; + btBroadphaseProxy* actualThisProxy = thisProxy1 ? thisProxy1 : getBroadphaseHandle(); + btAssert(actualThisProxy); + + btAssert(otherObject); + int index = m_overlappingObjects.findLinearSearch(otherObject); + if (indexremoveOverlappingPair(actualThisProxy,otherProxy,dispatcher); + } +} + + +void btGhostObject::convexSweepTest(const btConvexShape* castShape, const btTransform& convexFromWorld, const btTransform& convexToWorld, btCollisionWorld::ConvexResultCallback& resultCallback, btScalar allowedCcdPenetration) const +{ + btTransform convexFromTrans,convexToTrans; + convexFromTrans = convexFromWorld; + convexToTrans = convexToWorld; + btVector3 castShapeAabbMin, castShapeAabbMax; + /* Compute AABB that encompasses angular movement */ + { + btVector3 linVel, angVel; + btTransformUtil::calculateVelocity (convexFromTrans, convexToTrans, 1.0, linVel, angVel); + btTransform R; + R.setIdentity (); + R.setRotation (convexFromTrans.getRotation()); + castShape->calculateTemporalAabb (R, linVel, angVel, 1.0, castShapeAabbMin, castShapeAabbMax); + } + + /// go over all objects, and if the ray intersects their aabb + cast shape aabb, + // do a ray-shape query using convexCaster (CCD) + int i; + for (i=0;igetBroadphaseHandle())) { + //RigidcollisionObject* collisionObject = ctrl->GetRigidcollisionObject(); + btVector3 collisionObjectAabbMin,collisionObjectAabbMax; + collisionObject->getCollisionShape()->getAabb(collisionObject->getWorldTransform(),collisionObjectAabbMin,collisionObjectAabbMax); + AabbExpand (collisionObjectAabbMin, collisionObjectAabbMax, castShapeAabbMin, castShapeAabbMax); + btScalar hitLambda = btScalar(1.); //could use resultCallback.m_closestHitFraction, but needs testing + btVector3 hitNormal; + if (btRayAabb(convexFromWorld.getOrigin(),convexToWorld.getOrigin(),collisionObjectAabbMin,collisionObjectAabbMax,hitLambda,hitNormal)) + { + btCollisionWorld::objectQuerySingle(castShape, convexFromTrans,convexToTrans, + collisionObject, + collisionObject->getCollisionShape(), + collisionObject->getWorldTransform(), + resultCallback, + allowedCcdPenetration); + } + } + } + +} + +void btGhostObject::rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, btCollisionWorld::RayResultCallback& resultCallback) const +{ + btTransform rayFromTrans; + rayFromTrans.setIdentity(); + rayFromTrans.setOrigin(rayFromWorld); + btTransform rayToTrans; + rayToTrans.setIdentity(); + rayToTrans.setOrigin(rayToWorld); + + + int i; + for (i=0;igetBroadphaseHandle())) + { + btCollisionWorld::rayTestSingle(rayFromTrans,rayToTrans, + collisionObject, + collisionObject->getCollisionShape(), + collisionObject->getWorldTransform(), + resultCallback); + } + } +} + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btGhostObject.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btGhostObject.h new file mode 100644 index 000000000..95b575024 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btGhostObject.h @@ -0,0 +1,174 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2008 Erwin Coumans http://bulletphysics.com + +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 BT_GHOST_OBJECT_H +#define BT_GHOST_OBJECT_H + + +#include "btCollisionObject.h" +#include "BulletCollision/BroadphaseCollision/btOverlappingPairCallback.h" +#include "LinearMath/btAlignedAllocator.h" +#include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h" +#include "btCollisionWorld.h" + +class btConvexShape; + +class btDispatcher; + +///The btGhostObject can keep track of all objects that are overlapping +///By default, this overlap is based on the AABB +///This is useful for creating a character controller, collision sensors/triggers, explosions etc. +///We plan on adding rayTest and other queries for the btGhostObject +ATTRIBUTE_ALIGNED16(class) btGhostObject : public btCollisionObject +{ +protected: + + btAlignedObjectArray m_overlappingObjects; + +public: + + btGhostObject(); + + virtual ~btGhostObject(); + + void convexSweepTest(const class btConvexShape* castShape, const btTransform& convexFromWorld, const btTransform& convexToWorld, btCollisionWorld::ConvexResultCallback& resultCallback, btScalar allowedCcdPenetration = 0.f) const; + + void rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, btCollisionWorld::RayResultCallback& resultCallback) const; + + ///this method is mainly for expert/internal use only. + virtual void addOverlappingObjectInternal(btBroadphaseProxy* otherProxy, btBroadphaseProxy* thisProxy=0); + ///this method is mainly for expert/internal use only. + virtual void removeOverlappingObjectInternal(btBroadphaseProxy* otherProxy,btDispatcher* dispatcher,btBroadphaseProxy* thisProxy=0); + + int getNumOverlappingObjects() const + { + return m_overlappingObjects.size(); + } + + btCollisionObject* getOverlappingObject(int index) + { + return m_overlappingObjects[index]; + } + + const btCollisionObject* getOverlappingObject(int index) const + { + return m_overlappingObjects[index]; + } + + btAlignedObjectArray& getOverlappingPairs() + { + return m_overlappingObjects; + } + + const btAlignedObjectArray getOverlappingPairs() const + { + return m_overlappingObjects; + } + + // + // internal cast + // + + static const btGhostObject* upcast(const btCollisionObject* colObj) + { + if (colObj->getInternalType()==CO_GHOST_OBJECT) + return (const btGhostObject*)colObj; + return 0; + } + static btGhostObject* upcast(btCollisionObject* colObj) + { + if (colObj->getInternalType()==CO_GHOST_OBJECT) + return (btGhostObject*)colObj; + return 0; + } + +}; + +class btPairCachingGhostObject : public btGhostObject +{ + btHashedOverlappingPairCache* m_hashPairCache; + +public: + + btPairCachingGhostObject(); + + virtual ~btPairCachingGhostObject(); + + ///this method is mainly for expert/internal use only. + virtual void addOverlappingObjectInternal(btBroadphaseProxy* otherProxy, btBroadphaseProxy* thisProxy=0); + + virtual void removeOverlappingObjectInternal(btBroadphaseProxy* otherProxy,btDispatcher* dispatcher,btBroadphaseProxy* thisProxy=0); + + btHashedOverlappingPairCache* getOverlappingPairCache() + { + return m_hashPairCache; + } + +}; + + + +///The btGhostPairCallback interfaces and forwards adding and removal of overlapping pairs from the btBroadphaseInterface to btGhostObject. +class btGhostPairCallback : public btOverlappingPairCallback +{ + +public: + btGhostPairCallback() + { + } + + virtual ~btGhostPairCallback() + { + + } + + virtual btBroadphasePair* addOverlappingPair(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1) + { + btCollisionObject* colObj0 = (btCollisionObject*) proxy0->m_clientObject; + btCollisionObject* colObj1 = (btCollisionObject*) proxy1->m_clientObject; + btGhostObject* ghost0 = btGhostObject::upcast(colObj0); + btGhostObject* ghost1 = btGhostObject::upcast(colObj1); + if (ghost0) + ghost0->addOverlappingObjectInternal(proxy1, proxy0); + if (ghost1) + ghost1->addOverlappingObjectInternal(proxy0, proxy1); + return 0; + } + + virtual void* removeOverlappingPair(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1,btDispatcher* dispatcher) + { + btCollisionObject* colObj0 = (btCollisionObject*) proxy0->m_clientObject; + btCollisionObject* colObj1 = (btCollisionObject*) proxy1->m_clientObject; + btGhostObject* ghost0 = btGhostObject::upcast(colObj0); + btGhostObject* ghost1 = btGhostObject::upcast(colObj1); + if (ghost0) + ghost0->removeOverlappingObjectInternal(proxy1,dispatcher,proxy0); + if (ghost1) + ghost1->removeOverlappingObjectInternal(proxy0,dispatcher,proxy1); + return 0; + } + + virtual void removeOverlappingPairsContainingProxy(btBroadphaseProxy* proxy0,btDispatcher* dispatcher) + { + btAssert(0); + //need to keep track of all ghost objects and call them here + //m_hashPairCache->removeOverlappingPairsContainingProxy(proxy0,dispatcher); + } + + + +}; + +#endif \ No newline at end of file diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btManifoldResult.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btManifoldResult.cpp new file mode 100644 index 000000000..e607bdbee --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btManifoldResult.cpp @@ -0,0 +1,134 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btManifoldResult.h" +#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" + + +///This is to allow MaterialCombiner/Custom Friction/Restitution values +ContactAddedCallback gContactAddedCallback=0; + +///User can override this material combiner by implementing gContactAddedCallback and setting body0->m_collisionFlags |= btCollisionObject::customMaterialCallback; +inline btScalar calculateCombinedFriction(const btCollisionObject* body0,const btCollisionObject* body1) +{ + btScalar friction = body0->getFriction() * body1->getFriction(); + + const btScalar MAX_FRICTION = btScalar(10.); + if (friction < -MAX_FRICTION) + friction = -MAX_FRICTION; + if (friction > MAX_FRICTION) + friction = MAX_FRICTION; + return friction; + +} + +inline btScalar calculateCombinedRestitution(const btCollisionObject* body0,const btCollisionObject* body1) +{ + return body0->getRestitution() * body1->getRestitution(); +} + + + +btManifoldResult::btManifoldResult(btCollisionObject* body0,btCollisionObject* body1) + :m_manifoldPtr(0), + m_body0(body0), + m_body1(body1) +#ifdef DEBUG_PART_INDEX + ,m_partId0(-1), + m_partId1(-1), + m_index0(-1), + m_index1(-1) +#endif //DEBUG_PART_INDEX +{ + m_rootTransA = body0->getWorldTransform(); + m_rootTransB = body1->getWorldTransform(); +} + + +void btManifoldResult::addContactPoint(const btVector3& normalOnBInWorld,const btVector3& pointInWorld,btScalar depth) +{ + btAssert(m_manifoldPtr); + //order in manifold needs to match + + if (depth > m_manifoldPtr->getContactBreakingThreshold()) + return; + + bool isSwapped = m_manifoldPtr->getBody0() != m_body0; + + btVector3 pointA = pointInWorld + normalOnBInWorld * depth; + + btVector3 localA; + btVector3 localB; + + if (isSwapped) + { + localA = m_rootTransB.invXform(pointA ); + localB = m_rootTransA.invXform(pointInWorld); + } else + { + localA = m_rootTransA.invXform(pointA ); + localB = m_rootTransB.invXform(pointInWorld); + } + + btManifoldPoint newPt(localA,localB,normalOnBInWorld,depth); + newPt.m_positionWorldOnA = pointA; + newPt.m_positionWorldOnB = pointInWorld; + + int insertIndex = m_manifoldPtr->getCacheEntry(newPt); + + newPt.m_combinedFriction = calculateCombinedFriction(m_body0,m_body1); + newPt.m_combinedRestitution = calculateCombinedRestitution(m_body0,m_body1); + + //BP mod, store contact triangles. + if (isSwapped) + { + newPt.m_partId0 = m_partId1; + newPt.m_partId1 = m_partId0; + newPt.m_index0 = m_index1; + newPt.m_index1 = m_index0; + } else + { + newPt.m_partId0 = m_partId0; + newPt.m_partId1 = m_partId1; + newPt.m_index0 = m_index0; + newPt.m_index1 = m_index1; + } + //printf("depth=%f\n",depth); + ///@todo, check this for any side effects + if (insertIndex >= 0) + { + //const btManifoldPoint& oldPoint = m_manifoldPtr->getContactPoint(insertIndex); + m_manifoldPtr->replaceContactPoint(newPt,insertIndex); + } else + { + insertIndex = m_manifoldPtr->addManifoldPoint(newPt); + } + + //User can override friction and/or restitution + if (gContactAddedCallback && + //and if either of the two bodies requires custom material + ((m_body0->getCollisionFlags() & btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK) || + (m_body1->getCollisionFlags() & btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK))) + { + //experimental feature info, for per-triangle material etc. + btCollisionObject* obj0 = isSwapped? m_body1 : m_body0; + btCollisionObject* obj1 = isSwapped? m_body0 : m_body1; + (*gContactAddedCallback)(m_manifoldPtr->getContactPoint(insertIndex),obj0,newPt.m_partId0,newPt.m_index0,obj1,newPt.m_partId1,newPt.m_index1); + } + +} + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btManifoldResult.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btManifoldResult.h new file mode 100644 index 000000000..978033997 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btManifoldResult.h @@ -0,0 +1,126 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 MANIFOLD_RESULT_H +#define MANIFOLD_RESULT_H + +class btCollisionObject; +#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" +class btManifoldPoint; + +#include "BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h" + +#include "LinearMath/btTransform.h" + +typedef bool (*ContactAddedCallback)(btManifoldPoint& cp, const btCollisionObject* colObj0,int partId0,int index0,const btCollisionObject* colObj1,int partId1,int index1); +extern ContactAddedCallback gContactAddedCallback; + +//#define DEBUG_PART_INDEX 1 + + +///btManifoldResult is a helper class to manage contact results. +class btManifoldResult : public btDiscreteCollisionDetectorInterface::Result +{ + btPersistentManifold* m_manifoldPtr; + + //we need this for compounds + btTransform m_rootTransA; + btTransform m_rootTransB; + + btCollisionObject* m_body0; + btCollisionObject* m_body1; + int m_partId0; + int m_partId1; + int m_index0; + int m_index1; + + +public: + + btManifoldResult() +#ifdef DEBUG_PART_INDEX + : + m_partId0(-1), + m_partId1(-1), + m_index0(-1), + m_index1(-1) +#endif //DEBUG_PART_INDEX + { + } + + btManifoldResult(btCollisionObject* body0,btCollisionObject* body1); + + virtual ~btManifoldResult() {}; + + void setPersistentManifold(btPersistentManifold* manifoldPtr) + { + m_manifoldPtr = manifoldPtr; + } + + const btPersistentManifold* getPersistentManifold() const + { + return m_manifoldPtr; + } + btPersistentManifold* getPersistentManifold() + { + return m_manifoldPtr; + } + + virtual void setShapeIdentifiersA(int partId0,int index0) + { + m_partId0=partId0; + m_index0=index0; + } + + virtual void setShapeIdentifiersB( int partId1,int index1) + { + m_partId1=partId1; + m_index1=index1; + } + + + virtual void addContactPoint(const btVector3& normalOnBInWorld,const btVector3& pointInWorld,btScalar depth); + + SIMD_FORCE_INLINE void refreshContactPoints() + { + btAssert(m_manifoldPtr); + if (!m_manifoldPtr->getNumContacts()) + return; + + bool isSwapped = m_manifoldPtr->getBody0() != m_body0; + + if (isSwapped) + { + m_manifoldPtr->refreshContactPoints(m_rootTransB,m_rootTransA); + } else + { + m_manifoldPtr->refreshContactPoints(m_rootTransA,m_rootTransB); + } + } + + const btCollisionObject* getBody0Internal() const + { + return m_body0; + } + + const btCollisionObject* getBody1Internal() const + { + return m_body1; + } + +}; + +#endif //MANIFOLD_RESULT_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btSimulationIslandManager.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btSimulationIslandManager.cpp new file mode 100644 index 000000000..0328d0f73 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btSimulationIslandManager.cpp @@ -0,0 +1,390 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "LinearMath/btScalar.h" +#include "btSimulationIslandManager.h" +#include "BulletCollision/BroadphaseCollision/btDispatcher.h" +#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "BulletCollision/CollisionDispatch/btCollisionWorld.h" + +//#include +#include "LinearMath/btQuickprof.h" + +btSimulationIslandManager::btSimulationIslandManager(): +m_splitIslands(true) +{ +} + +btSimulationIslandManager::~btSimulationIslandManager() +{ +} + + +void btSimulationIslandManager::initUnionFind(int n) +{ + m_unionFind.reset(n); +} + + +void btSimulationIslandManager::findUnions(btDispatcher* /* dispatcher */,btCollisionWorld* colWorld) +{ + + { + + for (int i=0;igetPairCache()->getNumOverlappingPairs();i++) + { + btBroadphasePair* pairPtr = colWorld->getPairCache()->getOverlappingPairArrayPtr(); + const btBroadphasePair& collisionPair = pairPtr[i]; + btCollisionObject* colObj0 = (btCollisionObject*)collisionPair.m_pProxy0->m_clientObject; + btCollisionObject* colObj1 = (btCollisionObject*)collisionPair.m_pProxy1->m_clientObject; + + if (((colObj0) && ((colObj0)->mergesSimulationIslands())) && + ((colObj1) && ((colObj1)->mergesSimulationIslands()))) + { + + m_unionFind.unite((colObj0)->getIslandTag(), + (colObj1)->getIslandTag()); + } + } + } +} + + +void btSimulationIslandManager::updateActivationState(btCollisionWorld* colWorld,btDispatcher* dispatcher) +{ + + initUnionFind( int (colWorld->getCollisionObjectArray().size())); + + // put the index into m_controllers into m_tag + { + + int index = 0; + int i; + for (i=0;igetCollisionObjectArray().size(); i++) + { + btCollisionObject* collisionObject= colWorld->getCollisionObjectArray()[i]; + collisionObject->setIslandTag(index); + collisionObject->setCompanionId(-1); + collisionObject->setHitFraction(btScalar(1.)); + index++; + + } + } + // do the union find + + findUnions(dispatcher,colWorld); + + + +} + + + + +void btSimulationIslandManager::storeIslandActivationState(btCollisionWorld* colWorld) +{ + // put the islandId ('find' value) into m_tag + { + + + int index = 0; + int i; + for (i=0;igetCollisionObjectArray().size();i++) + { + btCollisionObject* collisionObject= colWorld->getCollisionObjectArray()[i]; + if (!collisionObject->isStaticOrKinematicObject()) + { + collisionObject->setIslandTag( m_unionFind.find(index) ); + collisionObject->setCompanionId(-1); + } else + { + collisionObject->setIslandTag(-1); + collisionObject->setCompanionId(-2); + } + index++; + } + } +} + +inline int getIslandId(const btPersistentManifold* lhs) +{ + int islandId; + const btCollisionObject* rcolObj0 = static_cast(lhs->getBody0()); + const btCollisionObject* rcolObj1 = static_cast(lhs->getBody1()); + islandId= rcolObj0->getIslandTag()>=0?rcolObj0->getIslandTag():rcolObj1->getIslandTag(); + return islandId; + +} + + + +/// function object that routes calls to operator< +class btPersistentManifoldSortPredicate +{ + public: + + SIMD_FORCE_INLINE bool operator() ( const btPersistentManifold* lhs, const btPersistentManifold* rhs ) + { + return getIslandId(lhs) < getIslandId(rhs); + } +}; + + +void btSimulationIslandManager::buildIslands(btDispatcher* dispatcher,btCollisionWorld* collisionWorld) +{ + + BT_PROFILE("islandUnionFindAndQuickSort"); + + btCollisionObjectArray& collisionObjects = collisionWorld->getCollisionObjectArray(); + + m_islandmanifold.resize(0); + + //we are going to sort the unionfind array, and store the element id in the size + //afterwards, we clean unionfind, to make sure no-one uses it anymore + + getUnionFind().sortIslands(); + int numElem = getUnionFind().getNumElements(); + + int endIslandIndex=1; + int startIslandIndex; + + + //update the sleeping state for bodies, if all are sleeping + for ( startIslandIndex=0;startIslandIndexgetIslandTag() != islandId) && (colObj0->getIslandTag() != -1)) + { +// printf("error in island management\n"); + } + + btAssert((colObj0->getIslandTag() == islandId) || (colObj0->getIslandTag() == -1)); + if (colObj0->getIslandTag() == islandId) + { + if (colObj0->getActivationState()== ACTIVE_TAG) + { + allSleeping = false; + } + if (colObj0->getActivationState()== DISABLE_DEACTIVATION) + { + allSleeping = false; + } + } + } + + + if (allSleeping) + { + int idx; + for (idx=startIslandIndex;idxgetIslandTag() != islandId) && (colObj0->getIslandTag() != -1)) + { +// printf("error in island management\n"); + } + + btAssert((colObj0->getIslandTag() == islandId) || (colObj0->getIslandTag() == -1)); + + if (colObj0->getIslandTag() == islandId) + { + colObj0->setActivationState( ISLAND_SLEEPING ); + } + } + } else + { + + int idx; + for (idx=startIslandIndex;idxgetIslandTag() != islandId) && (colObj0->getIslandTag() != -1)) + { +// printf("error in island management\n"); + } + + btAssert((colObj0->getIslandTag() == islandId) || (colObj0->getIslandTag() == -1)); + + if (colObj0->getIslandTag() == islandId) + { + if ( colObj0->getActivationState() == ISLAND_SLEEPING) + { + colObj0->setActivationState( WANTS_DEACTIVATION); + colObj0->setDeactivationTime(0.f); + } + } + } + } + } + + + int i; + int maxNumManifolds = dispatcher->getNumManifolds(); + +//#define SPLIT_ISLANDS 1 +//#ifdef SPLIT_ISLANDS + + +//#endif //SPLIT_ISLANDS + + + for (i=0;igetManifoldByIndexInternal(i); + + btCollisionObject* colObj0 = static_cast(manifold->getBody0()); + btCollisionObject* colObj1 = static_cast(manifold->getBody1()); + + ///@todo: check sleeping conditions! + if (((colObj0) && colObj0->getActivationState() != ISLAND_SLEEPING) || + ((colObj1) && colObj1->getActivationState() != ISLAND_SLEEPING)) + { + + //kinematic objects don't merge islands, but wake up all connected objects + if (colObj0->isKinematicObject() && colObj0->getActivationState() != ISLAND_SLEEPING) + { + colObj1->activate(); + } + if (colObj1->isKinematicObject() && colObj1->getActivationState() != ISLAND_SLEEPING) + { + colObj0->activate(); + } + if(m_splitIslands) + { + //filtering for response + if (dispatcher->needsResponse(colObj0,colObj1)) + m_islandmanifold.push_back(manifold); + } + } + } +} + + + +///@todo: this is random access, it can be walked 'cache friendly'! +void btSimulationIslandManager::buildAndProcessIslands(btDispatcher* dispatcher,btCollisionWorld* collisionWorld, IslandCallback* callback) +{ + btCollisionObjectArray& collisionObjects = collisionWorld->getCollisionObjectArray(); + + buildIslands(dispatcher,collisionWorld); + + int endIslandIndex=1; + int startIslandIndex; + int numElem = getUnionFind().getNumElements(); + + BT_PROFILE("processIslands"); + + if(!m_splitIslands) + { + btPersistentManifold** manifold = dispatcher->getInternalManifoldPointer(); + int maxNumManifolds = dispatcher->getNumManifolds(); + callback->ProcessIsland(&collisionObjects[0],collisionObjects.size(),manifold,maxNumManifolds, -1); + } + else + { + // Sort manifolds, based on islands + // Sort the vector using predicate and std::sort + //std::sort(islandmanifold.begin(), islandmanifold.end(), btPersistentManifoldSortPredicate); + + int numManifolds = int (m_islandmanifold.size()); + + //we should do radix sort, it it much faster (O(n) instead of O (n log2(n)) + m_islandmanifold.quickSort(btPersistentManifoldSortPredicate()); + + //now process all active islands (sets of manifolds for now) + + int startManifoldIndex = 0; + int endManifoldIndex = 1; + + //int islandId; + + + + // printf("Start Islands\n"); + + //traverse the simulation islands, and call the solver, unless all objects are sleeping/deactivated + for ( startIslandIndex=0;startIslandIndexisActive()) + islandSleeping = true; + } + + + //find the accompanying contact manifold for this islandId + int numIslandManifolds = 0; + btPersistentManifold** startManifold = 0; + + if (startManifoldIndexProcessIsland(&m_islandBodies[0],m_islandBodies.size(),startManifold,numIslandManifolds, islandId); + // printf("Island callback of size:%d bodies, %d manifolds\n",islandBodies.size(),numIslandManifolds); + } + + if (numIslandManifolds) + { + startManifoldIndex = endManifoldIndex; + } + + m_islandBodies.resize(0); + } + } // else if(!splitIslands) + +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btSimulationIslandManager.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btSimulationIslandManager.h new file mode 100644 index 000000000..d059f5d6b --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btSimulationIslandManager.h @@ -0,0 +1,81 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 SIMULATION_ISLAND_MANAGER_H +#define SIMULATION_ISLAND_MANAGER_H + +#include "BulletCollision/CollisionDispatch/btUnionFind.h" +#include "btCollisionCreateFunc.h" +#include "LinearMath/btAlignedObjectArray.h" +#include "btCollisionObject.h" + +class btCollisionObject; +class btCollisionWorld; +class btDispatcher; +class btPersistentManifold; + + +///SimulationIslandManager creates and handles simulation islands, using btUnionFind +class btSimulationIslandManager +{ + btUnionFind m_unionFind; + + btAlignedObjectArray m_islandmanifold; + btAlignedObjectArray m_islandBodies; + + bool m_splitIslands; + +public: + btSimulationIslandManager(); + virtual ~btSimulationIslandManager(); + + + void initUnionFind(int n); + + + btUnionFind& getUnionFind() { return m_unionFind;} + + virtual void updateActivationState(btCollisionWorld* colWorld,btDispatcher* dispatcher); + virtual void storeIslandActivationState(btCollisionWorld* world); + + + void findUnions(btDispatcher* dispatcher,btCollisionWorld* colWorld); + + + + struct IslandCallback + { + virtual ~IslandCallback() {}; + + virtual void ProcessIsland(btCollisionObject** bodies,int numBodies,class btPersistentManifold** manifolds,int numManifolds, int islandId) = 0; + }; + + void buildAndProcessIslands(btDispatcher* dispatcher,btCollisionWorld* collisionWorld, IslandCallback* callback); + + void buildIslands(btDispatcher* dispatcher,btCollisionWorld* colWorld); + + bool getSplitIslands() + { + return m_splitIslands; + } + void setSplitIslands(bool doSplitIslands) + { + m_splitIslands = doSplitIslands; + } + +}; + +#endif //SIMULATION_ISLAND_MANAGER_H + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.cpp new file mode 100644 index 000000000..8df876928 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.cpp @@ -0,0 +1,260 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btSphereBoxCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/CollisionShapes/btSphereShape.h" +#include "BulletCollision/CollisionShapes/btBoxShape.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +//#include + +btSphereBoxCollisionAlgorithm::btSphereBoxCollisionAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* col0,btCollisionObject* col1, bool isSwapped) +: btActivatingCollisionAlgorithm(ci,col0,col1), +m_ownManifold(false), +m_manifoldPtr(mf), +m_isSwapped(isSwapped) +{ + btCollisionObject* sphereObj = m_isSwapped? col1 : col0; + btCollisionObject* boxObj = m_isSwapped? col0 : col1; + + if (!m_manifoldPtr && m_dispatcher->needsCollision(sphereObj,boxObj)) + { + m_manifoldPtr = m_dispatcher->getNewManifold(sphereObj,boxObj); + m_ownManifold = true; + } +} + + +btSphereBoxCollisionAlgorithm::~btSphereBoxCollisionAlgorithm() +{ + if (m_ownManifold) + { + if (m_manifoldPtr) + m_dispatcher->releaseManifold(m_manifoldPtr); + } +} + + + +void btSphereBoxCollisionAlgorithm::processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + (void)dispatchInfo; + (void)resultOut; + if (!m_manifoldPtr) + return; + + btCollisionObject* sphereObj = m_isSwapped? body1 : body0; + btCollisionObject* boxObj = m_isSwapped? body0 : body1; + + + btSphereShape* sphere0 = (btSphereShape*)sphereObj->getCollisionShape(); + + btVector3 normalOnSurfaceB; + btVector3 pOnBox,pOnSphere; + btVector3 sphereCenter = sphereObj->getWorldTransform().getOrigin(); + btScalar radius = sphere0->getRadius(); + + btScalar dist = getSphereDistance(boxObj,pOnBox,pOnSphere,sphereCenter,radius); + + resultOut->setPersistentManifold(m_manifoldPtr); + + if (dist < SIMD_EPSILON) + { + btVector3 normalOnSurfaceB = (pOnBox- pOnSphere).normalize(); + + /// report a contact. internally this will be kept persistent, and contact reduction is done + + resultOut->addContactPoint(normalOnSurfaceB,pOnBox,dist); + + } + + if (m_ownManifold) + { + if (m_manifoldPtr->getNumContacts()) + { + resultOut->refreshContactPoints(); + } + } + +} + +btScalar btSphereBoxCollisionAlgorithm::calculateTimeOfImpact(btCollisionObject* col0,btCollisionObject* col1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + (void)resultOut; + (void)dispatchInfo; + (void)col0; + (void)col1; + + //not yet + return btScalar(1.); +} + + +btScalar btSphereBoxCollisionAlgorithm::getSphereDistance(btCollisionObject* boxObj, btVector3& pointOnBox, btVector3& v3PointOnSphere, const btVector3& sphereCenter, btScalar fRadius ) +{ + + btScalar margins; + btVector3 bounds[2]; + btBoxShape* boxShape= (btBoxShape*)boxObj->getCollisionShape(); + + bounds[0] = -boxShape->getHalfExtentsWithoutMargin(); + bounds[1] = boxShape->getHalfExtentsWithoutMargin(); + + margins = boxShape->getMargin();//also add sphereShape margin? + + const btTransform& m44T = boxObj->getWorldTransform(); + + btVector3 boundsVec[2]; + btScalar fPenetration; + + boundsVec[0] = bounds[0]; + boundsVec[1] = bounds[1]; + + btVector3 marginsVec( margins, margins, margins ); + + // add margins + bounds[0] += marginsVec; + bounds[1] -= marginsVec; + + ///////////////////////////////////////////////// + + btVector3 tmp, prel, n[6], normal, v3P; + btScalar fSep = btScalar(10000000.0), fSepThis; + + n[0].setValue( btScalar(-1.0), btScalar(0.0), btScalar(0.0) ); + n[1].setValue( btScalar(0.0), btScalar(-1.0), btScalar(0.0) ); + n[2].setValue( btScalar(0.0), btScalar(0.0), btScalar(-1.0) ); + n[3].setValue( btScalar(1.0), btScalar(0.0), btScalar(0.0) ); + n[4].setValue( btScalar(0.0), btScalar(1.0), btScalar(0.0) ); + n[5].setValue( btScalar(0.0), btScalar(0.0), btScalar(1.0) ); + + // convert point in local space + prel = m44T.invXform( sphereCenter); + + bool bFound = false; + + v3P = prel; + + for (int i=0;i<6;i++) + { + int j = i<3? 0:1; + if ( (fSepThis = ((v3P-bounds[j]) .dot(n[i]))) > btScalar(0.0) ) + { + v3P = v3P - n[i]*fSepThis; + bFound = true; + } + } + + // + + if ( bFound ) + { + bounds[0] = boundsVec[0]; + bounds[1] = boundsVec[1]; + + normal = (prel - v3P).normalize(); + pointOnBox = v3P + normal*margins; + v3PointOnSphere = prel - normal*fRadius; + + if ( ((v3PointOnSphere - pointOnBox) .dot (normal)) > btScalar(0.0) ) + { + return btScalar(1.0); + } + + // transform back in world space + tmp = m44T( pointOnBox); + pointOnBox = tmp; + tmp = m44T( v3PointOnSphere); + v3PointOnSphere = tmp; + btScalar fSeps2 = (pointOnBox-v3PointOnSphere).length2(); + + //if this fails, fallback into deeper penetration case, below + if (fSeps2 > SIMD_EPSILON) + { + fSep = - btSqrt(fSeps2); + normal = (pointOnBox-v3PointOnSphere); + normal *= btScalar(1.)/fSep; + } + + return fSep; + } + + ////////////////////////////////////////////////// + // Deep penetration case + + fPenetration = getSpherePenetration( boxObj,pointOnBox, v3PointOnSphere, sphereCenter, fRadius,bounds[0],bounds[1] ); + + bounds[0] = boundsVec[0]; + bounds[1] = boundsVec[1]; + + if ( fPenetration <= btScalar(0.0) ) + return (fPenetration-margins); + else + return btScalar(1.0); +} + +btScalar btSphereBoxCollisionAlgorithm::getSpherePenetration( btCollisionObject* boxObj,btVector3& pointOnBox, btVector3& v3PointOnSphere, const btVector3& sphereCenter, btScalar fRadius, const btVector3& aabbMin, const btVector3& aabbMax) +{ + + btVector3 bounds[2]; + + bounds[0] = aabbMin; + bounds[1] = aabbMax; + + btVector3 p0, tmp, prel, n[6], normal; + btScalar fSep = btScalar(-10000000.0), fSepThis; + + // set p0 and normal to a default value to shup up GCC + p0.setValue(btScalar(0.), btScalar(0.), btScalar(0.)); + normal.setValue(btScalar(0.), btScalar(0.), btScalar(0.)); + + n[0].setValue( btScalar(-1.0), btScalar(0.0), btScalar(0.0) ); + n[1].setValue( btScalar(0.0), btScalar(-1.0), btScalar(0.0) ); + n[2].setValue( btScalar(0.0), btScalar(0.0), btScalar(-1.0) ); + n[3].setValue( btScalar(1.0), btScalar(0.0), btScalar(0.0) ); + n[4].setValue( btScalar(0.0), btScalar(1.0), btScalar(0.0) ); + n[5].setValue( btScalar(0.0), btScalar(0.0), btScalar(1.0) ); + + const btTransform& m44T = boxObj->getWorldTransform(); + + // convert point in local space + prel = m44T.invXform( sphereCenter); + + /////////// + + for (int i=0;i<6;i++) + { + int j = i<3 ? 0:1; + if ( (fSepThis = ((prel-bounds[j]) .dot( n[i]))-fRadius) > btScalar(0.0) ) return btScalar(1.0); + if ( fSepThis > fSep ) + { + p0 = bounds[j]; normal = (btVector3&)n[i]; + fSep = fSepThis; + } + } + + pointOnBox = prel - normal*(normal.dot((prel-p0))); + v3PointOnSphere = pointOnBox + normal*fSep; + + // transform back in world space + tmp = m44T( pointOnBox); + pointOnBox = tmp; + tmp = m44T( v3PointOnSphere); v3PointOnSphere = tmp; + normal = (pointOnBox-v3PointOnSphere).normalize(); + + return fSep; + +} + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.h new file mode 100644 index 000000000..47111d1c4 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.h @@ -0,0 +1,75 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 SPHERE_BOX_COLLISION_ALGORITHM_H +#define SPHERE_BOX_COLLISION_ALGORITHM_H + +#include "btActivatingCollisionAlgorithm.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "BulletCollision/CollisionDispatch/btCollisionCreateFunc.h" +class btPersistentManifold; +#include "btCollisionDispatcher.h" + +#include "LinearMath/btVector3.h" + +/// btSphereBoxCollisionAlgorithm provides sphere-box collision detection. +/// Other features are frame-coherency (persistent data) and collision response. +class btSphereBoxCollisionAlgorithm : public btActivatingCollisionAlgorithm +{ + bool m_ownManifold; + btPersistentManifold* m_manifoldPtr; + bool m_isSwapped; + +public: + + btSphereBoxCollisionAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* col0,btCollisionObject* col1, bool isSwapped); + + virtual ~btSphereBoxCollisionAlgorithm(); + + virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual void getAllContactManifolds(btManifoldArray& manifoldArray) + { + if (m_manifoldPtr && m_ownManifold) + { + manifoldArray.push_back(m_manifoldPtr); + } + } + + btScalar getSphereDistance( btCollisionObject* boxObj,btVector3& v3PointOnBox, btVector3& v3PointOnSphere, const btVector3& v3SphereCenter, btScalar fRadius ); + + btScalar getSpherePenetration( btCollisionObject* boxObj, btVector3& v3PointOnBox, btVector3& v3PointOnSphere, const btVector3& v3SphereCenter, btScalar fRadius, const btVector3& aabbMin, const btVector3& aabbMax); + + struct CreateFunc :public btCollisionAlgorithmCreateFunc + { + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + void* mem = ci.m_dispatcher1->allocateCollisionAlgorithm(sizeof(btSphereBoxCollisionAlgorithm)); + if (!m_swapped) + { + return new(mem) btSphereBoxCollisionAlgorithm(0,ci,body0,body1,false); + } else + { + return new(mem) btSphereBoxCollisionAlgorithm(0,ci,body0,body1,true); + } + } + }; + +}; + +#endif //SPHERE_BOX_COLLISION_ALGORITHM_H + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.cpp new file mode 100644 index 000000000..5c4e78fe5 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.cpp @@ -0,0 +1,105 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btSphereSphereCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/CollisionShapes/btSphereShape.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" + +btSphereSphereCollisionAlgorithm::btSphereSphereCollisionAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* col0,btCollisionObject* col1) +: btActivatingCollisionAlgorithm(ci,col0,col1), +m_ownManifold(false), +m_manifoldPtr(mf) +{ + if (!m_manifoldPtr) + { + m_manifoldPtr = m_dispatcher->getNewManifold(col0,col1); + m_ownManifold = true; + } +} + +btSphereSphereCollisionAlgorithm::~btSphereSphereCollisionAlgorithm() +{ + if (m_ownManifold) + { + if (m_manifoldPtr) + m_dispatcher->releaseManifold(m_manifoldPtr); + } +} + +void btSphereSphereCollisionAlgorithm::processCollision (btCollisionObject* col0,btCollisionObject* col1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + (void)dispatchInfo; + + if (!m_manifoldPtr) + return; + + resultOut->setPersistentManifold(m_manifoldPtr); + + btSphereShape* sphere0 = (btSphereShape*)col0->getCollisionShape(); + btSphereShape* sphere1 = (btSphereShape*)col1->getCollisionShape(); + + btVector3 diff = col0->getWorldTransform().getOrigin()- col1->getWorldTransform().getOrigin(); + btScalar len = diff.length(); + btScalar radius0 = sphere0->getRadius(); + btScalar radius1 = sphere1->getRadius(); + +#ifdef CLEAR_MANIFOLD + m_manifoldPtr->clearManifold(); //don't do this, it disables warmstarting +#endif + + ///iff distance positive, don't generate a new contact + if ( len > (radius0+radius1)) + { +#ifndef CLEAR_MANIFOLD + resultOut->refreshContactPoints(); +#endif //CLEAR_MANIFOLD + return; + } + ///distance (negative means penetration) + btScalar dist = len - (radius0+radius1); + + btVector3 normalOnSurfaceB(1,0,0); + if (len > SIMD_EPSILON) + { + normalOnSurfaceB = diff / len; + } + + ///point on A (worldspace) + ///btVector3 pos0 = col0->getWorldTransform().getOrigin() - radius0 * normalOnSurfaceB; + ///point on B (worldspace) + btVector3 pos1 = col1->getWorldTransform().getOrigin() + radius1* normalOnSurfaceB; + + /// report a contact. internally this will be kept persistent, and contact reduction is done + + + resultOut->addContactPoint(normalOnSurfaceB,pos1,dist); + +#ifndef CLEAR_MANIFOLD + resultOut->refreshContactPoints(); +#endif //CLEAR_MANIFOLD + +} + +btScalar btSphereSphereCollisionAlgorithm::calculateTimeOfImpact(btCollisionObject* col0,btCollisionObject* col1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + (void)col0; + (void)col1; + (void)dispatchInfo; + (void)resultOut; + + //not yet + return btScalar(1.); +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.h new file mode 100644 index 000000000..7d07512ca --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.h @@ -0,0 +1,66 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 SPHERE_SPHERE_COLLISION_ALGORITHM_H +#define SPHERE_SPHERE_COLLISION_ALGORITHM_H + +#include "btActivatingCollisionAlgorithm.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "BulletCollision/CollisionDispatch/btCollisionCreateFunc.h" +#include "btCollisionDispatcher.h" + +class btPersistentManifold; + +/// btSphereSphereCollisionAlgorithm provides sphere-sphere collision detection. +/// Other features are frame-coherency (persistent data) and collision response. +/// Also provides the most basic sample for custom/user btCollisionAlgorithm +class btSphereSphereCollisionAlgorithm : public btActivatingCollisionAlgorithm +{ + bool m_ownManifold; + btPersistentManifold* m_manifoldPtr; + +public: + btSphereSphereCollisionAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* body0,btCollisionObject* body1); + + btSphereSphereCollisionAlgorithm(const btCollisionAlgorithmConstructionInfo& ci) + : btActivatingCollisionAlgorithm(ci) {} + + virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual void getAllContactManifolds(btManifoldArray& manifoldArray) + { + if (m_manifoldPtr && m_ownManifold) + { + manifoldArray.push_back(m_manifoldPtr); + } + } + + virtual ~btSphereSphereCollisionAlgorithm(); + + struct CreateFunc :public btCollisionAlgorithmCreateFunc + { + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + void* mem = ci.m_dispatcher1->allocateCollisionAlgorithm(sizeof(btSphereSphereCollisionAlgorithm)); + return new(mem) btSphereSphereCollisionAlgorithm(0,ci,body0,body1); + } + }; + +}; + +#endif //SPHERE_SPHERE_COLLISION_ALGORITHM_H + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btSphereTriangleCollisionAlgorithm.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btSphereTriangleCollisionAlgorithm.cpp new file mode 100644 index 000000000..c327c3ff7 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btSphereTriangleCollisionAlgorithm.cpp @@ -0,0 +1,84 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btSphereTriangleCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/CollisionShapes/btSphereShape.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "SphereTriangleDetector.h" + + +btSphereTriangleCollisionAlgorithm::btSphereTriangleCollisionAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* col0,btCollisionObject* col1,bool swapped) +: btActivatingCollisionAlgorithm(ci,col0,col1), +m_ownManifold(false), +m_manifoldPtr(mf), +m_swapped(swapped) +{ + if (!m_manifoldPtr) + { + m_manifoldPtr = m_dispatcher->getNewManifold(col0,col1); + m_ownManifold = true; + } +} + +btSphereTriangleCollisionAlgorithm::~btSphereTriangleCollisionAlgorithm() +{ + if (m_ownManifold) + { + if (m_manifoldPtr) + m_dispatcher->releaseManifold(m_manifoldPtr); + } +} + +void btSphereTriangleCollisionAlgorithm::processCollision (btCollisionObject* col0,btCollisionObject* col1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + if (!m_manifoldPtr) + return; + + btCollisionObject* sphereObj = m_swapped? col1 : col0; + btCollisionObject* triObj = m_swapped? col0 : col1; + + btSphereShape* sphere = (btSphereShape*)sphereObj->getCollisionShape(); + btTriangleShape* triangle = (btTriangleShape*)triObj->getCollisionShape(); + + /// report a contact. internally this will be kept persistent, and contact reduction is done + resultOut->setPersistentManifold(m_manifoldPtr); + SphereTriangleDetector detector(sphere,triangle, m_manifoldPtr->getContactBreakingThreshold()); + + btDiscreteCollisionDetectorInterface::ClosestPointInput input; + input.m_maximumDistanceSquared = btScalar(BT_LARGE_FLOAT);///@todo: tighter bounds + input.m_transformA = sphereObj->getWorldTransform(); + input.m_transformB = triObj->getWorldTransform(); + + bool swapResults = m_swapped; + + detector.getClosestPoints(input,*resultOut,dispatchInfo.m_debugDraw,swapResults); + + if (m_ownManifold) + resultOut->refreshContactPoints(); + +} + +btScalar btSphereTriangleCollisionAlgorithm::calculateTimeOfImpact(btCollisionObject* col0,btCollisionObject* col1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + (void)resultOut; + (void)dispatchInfo; + (void)col0; + (void)col1; + + //not yet + return btScalar(1.); +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btSphereTriangleCollisionAlgorithm.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btSphereTriangleCollisionAlgorithm.h new file mode 100644 index 000000000..606c3635a --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btSphereTriangleCollisionAlgorithm.h @@ -0,0 +1,69 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 SPHERE_TRIANGLE_COLLISION_ALGORITHM_H +#define SPHERE_TRIANGLE_COLLISION_ALGORITHM_H + +#include "btActivatingCollisionAlgorithm.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "BulletCollision/CollisionDispatch/btCollisionCreateFunc.h" +class btPersistentManifold; +#include "btCollisionDispatcher.h" + +/// btSphereSphereCollisionAlgorithm provides sphere-sphere collision detection. +/// Other features are frame-coherency (persistent data) and collision response. +/// Also provides the most basic sample for custom/user btCollisionAlgorithm +class btSphereTriangleCollisionAlgorithm : public btActivatingCollisionAlgorithm +{ + bool m_ownManifold; + btPersistentManifold* m_manifoldPtr; + bool m_swapped; + +public: + btSphereTriangleCollisionAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* body0,btCollisionObject* body1,bool swapped); + + btSphereTriangleCollisionAlgorithm(const btCollisionAlgorithmConstructionInfo& ci) + : btActivatingCollisionAlgorithm(ci) {} + + virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual void getAllContactManifolds(btManifoldArray& manifoldArray) + { + if (m_manifoldPtr && m_ownManifold) + { + manifoldArray.push_back(m_manifoldPtr); + } + } + + virtual ~btSphereTriangleCollisionAlgorithm(); + + struct CreateFunc :public btCollisionAlgorithmCreateFunc + { + + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + + void* mem = ci.m_dispatcher1->allocateCollisionAlgorithm(sizeof(btSphereTriangleCollisionAlgorithm)); + + return new(mem) btSphereTriangleCollisionAlgorithm(ci.m_manifold,ci,body0,body1,m_swapped); + } + }; + +}; + +#endif //SPHERE_TRIANGLE_COLLISION_ALGORITHM_H + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btUnionFind.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btUnionFind.cpp new file mode 100644 index 000000000..c561df061 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btUnionFind.cpp @@ -0,0 +1,81 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btUnionFind.h" + + + +btUnionFind::~btUnionFind() +{ + Free(); + +} + +btUnionFind::btUnionFind() +{ + +} + +void btUnionFind::allocate(int N) +{ + m_elements.resize(N); +} +void btUnionFind::Free() +{ + m_elements.clear(); +} + + +void btUnionFind::reset(int N) +{ + allocate(N); + + for (int i = 0; i < N; i++) + { + m_elements[i].m_id = i; m_elements[i].m_sz = 1; + } +} + + +class btUnionFindElementSortPredicate +{ + public: + + bool operator() ( const btElement& lhs, const btElement& rhs ) + { + return lhs.m_id < rhs.m_id; + } +}; + +///this is a special operation, destroying the content of btUnionFind. +///it sorts the elements, based on island id, in order to make it easy to iterate over islands +void btUnionFind::sortIslands() +{ + + //first store the original body index, and islandId + int numElements = m_elements.size(); + + for (int i=0;i m_elements; + + public: + + btUnionFind(); + ~btUnionFind(); + + + //this is a special operation, destroying the content of btUnionFind. + //it sorts the elements, based on island id, in order to make it easy to iterate over islands + void sortIslands(); + + void reset(int N); + + SIMD_FORCE_INLINE int getNumElements() const + { + return int(m_elements.size()); + } + SIMD_FORCE_INLINE bool isRoot(int x) const + { + return (x == m_elements[x].m_id); + } + + btElement& getElement(int index) + { + return m_elements[index]; + } + const btElement& getElement(int index) const + { + return m_elements[index]; + } + + void allocate(int N); + void Free(); + + + + + int find(int p, int q) + { + return (find(p) == find(q)); + } + + void unite(int p, int q) + { + int i = find(p), j = find(q); + if (i == j) + return; + +#ifndef USE_PATH_COMPRESSION + //weighted quick union, this keeps the 'trees' balanced, and keeps performance of unite O( log(n) ) + if (m_elements[i].m_sz < m_elements[j].m_sz) + { + m_elements[i].m_id = j; m_elements[j].m_sz += m_elements[i].m_sz; + } + else + { + m_elements[j].m_id = i; m_elements[i].m_sz += m_elements[j].m_sz; + } +#else + m_elements[i].m_id = j; m_elements[j].m_sz += m_elements[i].m_sz; +#endif //USE_PATH_COMPRESSION + } + + int find(int x) + { + //btAssert(x < m_N); + //btAssert(x >= 0); + + while (x != m_elements[x].m_id) + { + //not really a reason not to use path compression, and it flattens the trees/improves find performance dramatically + + #ifdef USE_PATH_COMPRESSION + // + m_elements[x].m_id = m_elements[m_elements[x].m_id].m_id; + #endif // + x = m_elements[x].m_id; + //btAssert(x < m_N); + //btAssert(x >= 0); + + } + return x; + } + + + }; + + +#endif //UNION_FIND_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btBox2dShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btBox2dShape.cpp new file mode 100644 index 000000000..ecce028c2 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btBox2dShape.cpp @@ -0,0 +1,42 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btBox2dShape.h" + + +//{ + + +void btBox2dShape::getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const +{ + btTransformAabb(getHalfExtentsWithoutMargin(),getMargin(),t,aabbMin,aabbMax); +} + + +void btBox2dShape::calculateLocalInertia(btScalar mass,btVector3& inertia) const +{ + //btScalar margin = btScalar(0.); + btVector3 halfExtents = getHalfExtentsWithMargin(); + + btScalar lx=btScalar(2.)*(halfExtents.x()); + btScalar ly=btScalar(2.)*(halfExtents.y()); + btScalar lz=btScalar(2.)*(halfExtents.z()); + + inertia.setValue(mass/(btScalar(12.0)) * (ly*ly + lz*lz), + mass/(btScalar(12.0)) * (lx*lx + lz*lz), + mass/(btScalar(12.0)) * (lx*lx + ly*ly)); + +} + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btBox2dShape.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btBox2dShape.h new file mode 100644 index 000000000..fc032069c --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btBox2dShape.h @@ -0,0 +1,363 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 OBB_BOX_2D_SHAPE_H +#define OBB_BOX_2D_SHAPE_H + +#include "BulletCollision/CollisionShapes/btPolyhedralConvexShape.h" +#include "BulletCollision/CollisionShapes/btCollisionMargin.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "LinearMath/btVector3.h" +#include "LinearMath/btMinMax.h" + +///The btBox2dShape is a box primitive around the origin, its sides axis aligned with length specified by half extents, in local shape coordinates. When used as part of a btCollisionObject or btRigidBody it will be an oriented box in world space. +class btBox2dShape: public btPolyhedralConvexShape +{ + + //btVector3 m_boxHalfExtents1; //use m_implicitShapeDimensions instead + + btVector3 m_centroid; + btVector3 m_vertices[4]; + btVector3 m_normals[4]; + +public: + + btVector3 getHalfExtentsWithMargin() const + { + btVector3 halfExtents = getHalfExtentsWithoutMargin(); + btVector3 margin(getMargin(),getMargin(),getMargin()); + halfExtents += margin; + return halfExtents; + } + + const btVector3& getHalfExtentsWithoutMargin() const + { + return m_implicitShapeDimensions;//changed in Bullet 2.63: assume the scaling and margin are included + } + + + virtual btVector3 localGetSupportingVertex(const btVector3& vec) const + { + btVector3 halfExtents = getHalfExtentsWithoutMargin(); + btVector3 margin(getMargin(),getMargin(),getMargin()); + halfExtents += margin; + + return btVector3(btFsels(vec.x(), halfExtents.x(), -halfExtents.x()), + btFsels(vec.y(), halfExtents.y(), -halfExtents.y()), + btFsels(vec.z(), halfExtents.z(), -halfExtents.z())); + } + + SIMD_FORCE_INLINE btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec)const + { + const btVector3& halfExtents = getHalfExtentsWithoutMargin(); + + return btVector3(btFsels(vec.x(), halfExtents.x(), -halfExtents.x()), + btFsels(vec.y(), halfExtents.y(), -halfExtents.y()), + btFsels(vec.z(), halfExtents.z(), -halfExtents.z())); + } + + virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const + { + const btVector3& halfExtents = getHalfExtentsWithoutMargin(); + + for (int i=0;i>1)) - halfExtents.y() * ((i&2)>>1), + halfExtents.z() * (1-((i&4)>>2)) - halfExtents.z() * ((i&4)>>2)); + } + + + virtual void getPlaneEquation(btVector4& plane,int i) const + { + btVector3 halfExtents = getHalfExtentsWithoutMargin(); + + switch (i) + { + case 0: + plane.setValue(btScalar(1.),btScalar(0.),btScalar(0.),-halfExtents.x()); + break; + case 1: + plane.setValue(btScalar(-1.),btScalar(0.),btScalar(0.),-halfExtents.x()); + break; + case 2: + plane.setValue(btScalar(0.),btScalar(1.),btScalar(0.),-halfExtents.y()); + break; + case 3: + plane.setValue(btScalar(0.),btScalar(-1.),btScalar(0.),-halfExtents.y()); + break; + case 4: + plane.setValue(btScalar(0.),btScalar(0.),btScalar(1.),-halfExtents.z()); + break; + case 5: + plane.setValue(btScalar(0.),btScalar(0.),btScalar(-1.),-halfExtents.z()); + break; + default: + btAssert(0); + } + } + + + virtual void getEdge(int i,btVector3& pa,btVector3& pb) const + //virtual void getEdge(int i,Edge& edge) const + { + int edgeVert0 = 0; + int edgeVert1 = 0; + + switch (i) + { + case 0: + edgeVert0 = 0; + edgeVert1 = 1; + break; + case 1: + edgeVert0 = 0; + edgeVert1 = 2; + break; + case 2: + edgeVert0 = 1; + edgeVert1 = 3; + + break; + case 3: + edgeVert0 = 2; + edgeVert1 = 3; + break; + case 4: + edgeVert0 = 0; + edgeVert1 = 4; + break; + case 5: + edgeVert0 = 1; + edgeVert1 = 5; + + break; + case 6: + edgeVert0 = 2; + edgeVert1 = 6; + break; + case 7: + edgeVert0 = 3; + edgeVert1 = 7; + break; + case 8: + edgeVert0 = 4; + edgeVert1 = 5; + break; + case 9: + edgeVert0 = 4; + edgeVert1 = 6; + break; + case 10: + edgeVert0 = 5; + edgeVert1 = 7; + break; + case 11: + edgeVert0 = 6; + edgeVert1 = 7; + break; + default: + btAssert(0); + + } + + getVertex(edgeVert0,pa ); + getVertex(edgeVert1,pb ); + } + + + + + + virtual bool isInside(const btVector3& pt,btScalar tolerance) const + { + btVector3 halfExtents = getHalfExtentsWithoutMargin(); + + //btScalar minDist = 2*tolerance; + + bool result = (pt.x() <= (halfExtents.x()+tolerance)) && + (pt.x() >= (-halfExtents.x()-tolerance)) && + (pt.y() <= (halfExtents.y()+tolerance)) && + (pt.y() >= (-halfExtents.y()-tolerance)) && + (pt.z() <= (halfExtents.z()+tolerance)) && + (pt.z() >= (-halfExtents.z()-tolerance)); + + return result; + } + + + //debugging + virtual const char* getName()const + { + return "Box2d"; + } + + virtual int getNumPreferredPenetrationDirections() const + { + return 6; + } + + virtual void getPreferredPenetrationDirection(int index, btVector3& penetrationVector) const + { + switch (index) + { + case 0: + penetrationVector.setValue(btScalar(1.),btScalar(0.),btScalar(0.)); + break; + case 1: + penetrationVector.setValue(btScalar(-1.),btScalar(0.),btScalar(0.)); + break; + case 2: + penetrationVector.setValue(btScalar(0.),btScalar(1.),btScalar(0.)); + break; + case 3: + penetrationVector.setValue(btScalar(0.),btScalar(-1.),btScalar(0.)); + break; + case 4: + penetrationVector.setValue(btScalar(0.),btScalar(0.),btScalar(1.)); + break; + case 5: + penetrationVector.setValue(btScalar(0.),btScalar(0.),btScalar(-1.)); + break; + default: + btAssert(0); + } + } + +}; + +#endif //OBB_BOX_2D_SHAPE_H + + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btBoxShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btBoxShape.cpp new file mode 100644 index 000000000..c6644efbe --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btBoxShape.cpp @@ -0,0 +1,41 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 "btBoxShape.h" + + +//{ + + +void btBoxShape::getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const +{ + btTransformAabb(getHalfExtentsWithoutMargin(),getMargin(),t,aabbMin,aabbMax); +} + + +void btBoxShape::calculateLocalInertia(btScalar mass,btVector3& inertia) const +{ + //btScalar margin = btScalar(0.); + btVector3 halfExtents = getHalfExtentsWithMargin(); + + btScalar lx=btScalar(2.)*(halfExtents.x()); + btScalar ly=btScalar(2.)*(halfExtents.y()); + btScalar lz=btScalar(2.)*(halfExtents.z()); + + inertia.setValue(mass/(btScalar(12.0)) * (ly*ly + lz*lz), + mass/(btScalar(12.0)) * (lx*lx + lz*lz), + mass/(btScalar(12.0)) * (lx*lx + ly*ly)); + +} + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btBoxShape.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btBoxShape.h new file mode 100644 index 000000000..c71318dd3 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btBoxShape.h @@ -0,0 +1,317 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 OBB_BOX_MINKOWSKI_H +#define OBB_BOX_MINKOWSKI_H + +#include "btPolyhedralConvexShape.h" +#include "btCollisionMargin.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "LinearMath/btVector3.h" +#include "LinearMath/btMinMax.h" + +///The btBoxShape is a box primitive around the origin, its sides axis aligned with length specified by half extents, in local shape coordinates. When used as part of a btCollisionObject or btRigidBody it will be an oriented box in world space. +class btBoxShape: public btPolyhedralConvexShape +{ + + //btVector3 m_boxHalfExtents1; //use m_implicitShapeDimensions instead + + +public: + + btVector3 getHalfExtentsWithMargin() const + { + btVector3 halfExtents = getHalfExtentsWithoutMargin(); + btVector3 margin(getMargin(),getMargin(),getMargin()); + halfExtents += margin; + return halfExtents; + } + + const btVector3& getHalfExtentsWithoutMargin() const + { + return m_implicitShapeDimensions;//changed in Bullet 2.63: assume the scaling and margin are included + } + + + virtual btVector3 localGetSupportingVertex(const btVector3& vec) const + { + btVector3 halfExtents = getHalfExtentsWithoutMargin(); + btVector3 margin(getMargin(),getMargin(),getMargin()); + halfExtents += margin; + + return btVector3(btFsels(vec.x(), halfExtents.x(), -halfExtents.x()), + btFsels(vec.y(), halfExtents.y(), -halfExtents.y()), + btFsels(vec.z(), halfExtents.z(), -halfExtents.z())); + } + + SIMD_FORCE_INLINE btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec)const + { + const btVector3& halfExtents = getHalfExtentsWithoutMargin(); + + return btVector3(btFsels(vec.x(), halfExtents.x(), -halfExtents.x()), + btFsels(vec.y(), halfExtents.y(), -halfExtents.y()), + btFsels(vec.z(), halfExtents.z(), -halfExtents.z())); + } + + virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const + { + const btVector3& halfExtents = getHalfExtentsWithoutMargin(); + + for (int i=0;i>1)) - halfExtents.y() * ((i&2)>>1), + halfExtents.z() * (1-((i&4)>>2)) - halfExtents.z() * ((i&4)>>2)); + } + + + virtual void getPlaneEquation(btVector4& plane,int i) const + { + btVector3 halfExtents = getHalfExtentsWithoutMargin(); + + switch (i) + { + case 0: + plane.setValue(btScalar(1.),btScalar(0.),btScalar(0.),-halfExtents.x()); + break; + case 1: + plane.setValue(btScalar(-1.),btScalar(0.),btScalar(0.),-halfExtents.x()); + break; + case 2: + plane.setValue(btScalar(0.),btScalar(1.),btScalar(0.),-halfExtents.y()); + break; + case 3: + plane.setValue(btScalar(0.),btScalar(-1.),btScalar(0.),-halfExtents.y()); + break; + case 4: + plane.setValue(btScalar(0.),btScalar(0.),btScalar(1.),-halfExtents.z()); + break; + case 5: + plane.setValue(btScalar(0.),btScalar(0.),btScalar(-1.),-halfExtents.z()); + break; + default: + btAssert(0); + } + } + + + virtual void getEdge(int i,btVector3& pa,btVector3& pb) const + //virtual void getEdge(int i,Edge& edge) const + { + int edgeVert0 = 0; + int edgeVert1 = 0; + + switch (i) + { + case 0: + edgeVert0 = 0; + edgeVert1 = 1; + break; + case 1: + edgeVert0 = 0; + edgeVert1 = 2; + break; + case 2: + edgeVert0 = 1; + edgeVert1 = 3; + + break; + case 3: + edgeVert0 = 2; + edgeVert1 = 3; + break; + case 4: + edgeVert0 = 0; + edgeVert1 = 4; + break; + case 5: + edgeVert0 = 1; + edgeVert1 = 5; + + break; + case 6: + edgeVert0 = 2; + edgeVert1 = 6; + break; + case 7: + edgeVert0 = 3; + edgeVert1 = 7; + break; + case 8: + edgeVert0 = 4; + edgeVert1 = 5; + break; + case 9: + edgeVert0 = 4; + edgeVert1 = 6; + break; + case 10: + edgeVert0 = 5; + edgeVert1 = 7; + break; + case 11: + edgeVert0 = 6; + edgeVert1 = 7; + break; + default: + btAssert(0); + + } + + getVertex(edgeVert0,pa ); + getVertex(edgeVert1,pb ); + } + + + + + + virtual bool isInside(const btVector3& pt,btScalar tolerance) const + { + btVector3 halfExtents = getHalfExtentsWithoutMargin(); + + //btScalar minDist = 2*tolerance; + + bool result = (pt.x() <= (halfExtents.x()+tolerance)) && + (pt.x() >= (-halfExtents.x()-tolerance)) && + (pt.y() <= (halfExtents.y()+tolerance)) && + (pt.y() >= (-halfExtents.y()-tolerance)) && + (pt.z() <= (halfExtents.z()+tolerance)) && + (pt.z() >= (-halfExtents.z()-tolerance)); + + return result; + } + + + //debugging + virtual const char* getName()const + { + return "Box"; + } + + virtual int getNumPreferredPenetrationDirections() const + { + return 6; + } + + virtual void getPreferredPenetrationDirection(int index, btVector3& penetrationVector) const + { + switch (index) + { + case 0: + penetrationVector.setValue(btScalar(1.),btScalar(0.),btScalar(0.)); + break; + case 1: + penetrationVector.setValue(btScalar(-1.),btScalar(0.),btScalar(0.)); + break; + case 2: + penetrationVector.setValue(btScalar(0.),btScalar(1.),btScalar(0.)); + break; + case 3: + penetrationVector.setValue(btScalar(0.),btScalar(-1.),btScalar(0.)); + break; + case 4: + penetrationVector.setValue(btScalar(0.),btScalar(0.),btScalar(1.)); + break; + case 5: + penetrationVector.setValue(btScalar(0.),btScalar(0.),btScalar(-1.)); + break; + default: + btAssert(0); + } + } + +}; + +#endif //OBB_BOX_MINKOWSKI_H + + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btBvhTriangleMeshShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btBvhTriangleMeshShape.cpp new file mode 100644 index 000000000..b8e76d6f8 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btBvhTriangleMeshShape.cpp @@ -0,0 +1,374 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 DISABLE_BVH + +#include "BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h" +#include "BulletCollision/CollisionShapes/btOptimizedBvh.h" + +///Bvh Concave triangle mesh is a static-triangle mesh shape with Bounding Volume Hierarchy optimization. +///Uses an interface to access the triangles to allow for sharing graphics/physics triangles. +btBvhTriangleMeshShape::btBvhTriangleMeshShape(btStridingMeshInterface* meshInterface, bool useQuantizedAabbCompression, bool buildBvh) +:btTriangleMeshShape(meshInterface), +m_bvh(0), +m_useQuantizedAabbCompression(useQuantizedAabbCompression), +m_ownsBvh(false) +{ + m_shapeType = TRIANGLE_MESH_SHAPE_PROXYTYPE; + //construct bvh from meshInterface +#ifndef DISABLE_BVH + + btVector3 bvhAabbMin,bvhAabbMax; + if(meshInterface->hasPremadeAabb()) + { + meshInterface->getPremadeAabb(&bvhAabbMin, &bvhAabbMax); + } + else + { + meshInterface->calculateAabbBruteForce(bvhAabbMin,bvhAabbMax); + } + + if (buildBvh) + { + void* mem = btAlignedAlloc(sizeof(btOptimizedBvh),16); + m_bvh = new (mem) btOptimizedBvh(); + m_bvh->build(meshInterface,m_useQuantizedAabbCompression,bvhAabbMin,bvhAabbMax); + m_ownsBvh = true; + } + +#endif //DISABLE_BVH + +} + +btBvhTriangleMeshShape::btBvhTriangleMeshShape(btStridingMeshInterface* meshInterface, bool useQuantizedAabbCompression,const btVector3& bvhAabbMin,const btVector3& bvhAabbMax,bool buildBvh) +:btTriangleMeshShape(meshInterface), +m_bvh(0), +m_useQuantizedAabbCompression(useQuantizedAabbCompression), +m_ownsBvh(false) +{ + m_shapeType = TRIANGLE_MESH_SHAPE_PROXYTYPE; + //construct bvh from meshInterface +#ifndef DISABLE_BVH + + if (buildBvh) + { + void* mem = btAlignedAlloc(sizeof(btOptimizedBvh),16); + m_bvh = new (mem) btOptimizedBvh(); + + m_bvh->build(meshInterface,m_useQuantizedAabbCompression,bvhAabbMin,bvhAabbMax); + m_ownsBvh = true; + } + +#endif //DISABLE_BVH + +} + +void btBvhTriangleMeshShape::partialRefitTree(const btVector3& aabbMin,const btVector3& aabbMax) +{ + m_bvh->refitPartial( m_meshInterface,aabbMin,aabbMax ); + + m_localAabbMin.setMin(aabbMin); + m_localAabbMax.setMax(aabbMax); +} + + +void btBvhTriangleMeshShape::refitTree(const btVector3& aabbMin,const btVector3& aabbMax) +{ + m_bvh->refit( m_meshInterface, aabbMin,aabbMax ); + + recalcLocalAabb(); +} + +btBvhTriangleMeshShape::~btBvhTriangleMeshShape() +{ + if (m_ownsBvh) + { + m_bvh->~btOptimizedBvh(); + btAlignedFree(m_bvh); + } +} + +void btBvhTriangleMeshShape::performRaycast (btTriangleCallback* callback, const btVector3& raySource, const btVector3& rayTarget) +{ + struct MyNodeOverlapCallback : public btNodeOverlapCallback + { + btStridingMeshInterface* m_meshInterface; + btTriangleCallback* m_callback; + + MyNodeOverlapCallback(btTriangleCallback* callback,btStridingMeshInterface* meshInterface) + :m_meshInterface(meshInterface), + m_callback(callback) + { + } + + virtual void processNode(int nodeSubPart, int nodeTriangleIndex) + { + btVector3 m_triangle[3]; + const unsigned char *vertexbase; + int numverts; + PHY_ScalarType type; + int stride; + const unsigned char *indexbase; + int indexstride; + int numfaces; + PHY_ScalarType indicestype; + + m_meshInterface->getLockedReadOnlyVertexIndexBase( + &vertexbase, + numverts, + type, + stride, + &indexbase, + indexstride, + numfaces, + indicestype, + nodeSubPart); + + unsigned int* gfxbase = (unsigned int*)(indexbase+nodeTriangleIndex*indexstride); + btAssert(indicestype==PHY_INTEGER||indicestype==PHY_SHORT); + + const btVector3& meshScaling = m_meshInterface->getScaling(); + for (int j=2;j>=0;j--) + { + int graphicsindex = indicestype==PHY_SHORT?((unsigned short*)gfxbase)[j]:gfxbase[j]; + + if (type == PHY_FLOAT) + { + float* graphicsbase = (float*)(vertexbase+graphicsindex*stride); + + m_triangle[j] = btVector3(graphicsbase[0]*meshScaling.getX(),graphicsbase[1]*meshScaling.getY(),graphicsbase[2]*meshScaling.getZ()); + } + else + { + double* graphicsbase = (double*)(vertexbase+graphicsindex*stride); + + m_triangle[j] = btVector3(btScalar(graphicsbase[0])*meshScaling.getX(),btScalar(graphicsbase[1])*meshScaling.getY(),btScalar(graphicsbase[2])*meshScaling.getZ()); + } + } + + /* Perform ray vs. triangle collision here */ + m_callback->processTriangle(m_triangle,nodeSubPart,nodeTriangleIndex); + m_meshInterface->unLockReadOnlyVertexBase(nodeSubPart); + } + }; + + MyNodeOverlapCallback myNodeCallback(callback,m_meshInterface); + + m_bvh->reportRayOverlappingNodex(&myNodeCallback,raySource,rayTarget); +} + +void btBvhTriangleMeshShape::performConvexcast (btTriangleCallback* callback, const btVector3& raySource, const btVector3& rayTarget, const btVector3& aabbMin, const btVector3& aabbMax) +{ + struct MyNodeOverlapCallback : public btNodeOverlapCallback + { + btStridingMeshInterface* m_meshInterface; + btTriangleCallback* m_callback; + + MyNodeOverlapCallback(btTriangleCallback* callback,btStridingMeshInterface* meshInterface) + :m_meshInterface(meshInterface), + m_callback(callback) + { + } + + virtual void processNode(int nodeSubPart, int nodeTriangleIndex) + { + btVector3 m_triangle[3]; + const unsigned char *vertexbase; + int numverts; + PHY_ScalarType type; + int stride; + const unsigned char *indexbase; + int indexstride; + int numfaces; + PHY_ScalarType indicestype; + + m_meshInterface->getLockedReadOnlyVertexIndexBase( + &vertexbase, + numverts, + type, + stride, + &indexbase, + indexstride, + numfaces, + indicestype, + nodeSubPart); + + unsigned int* gfxbase = (unsigned int*)(indexbase+nodeTriangleIndex*indexstride); + btAssert(indicestype==PHY_INTEGER||indicestype==PHY_SHORT); + + const btVector3& meshScaling = m_meshInterface->getScaling(); + for (int j=2;j>=0;j--) + { + int graphicsindex = indicestype==PHY_SHORT?((unsigned short*)gfxbase)[j]:gfxbase[j]; + + if (type == PHY_FLOAT) + { + float* graphicsbase = (float*)(vertexbase+graphicsindex*stride); + + m_triangle[j] = btVector3(graphicsbase[0]*meshScaling.getX(),graphicsbase[1]*meshScaling.getY(),graphicsbase[2]*meshScaling.getZ()); + } + else + { + double* graphicsbase = (double*)(vertexbase+graphicsindex*stride); + + m_triangle[j] = btVector3(btScalar(graphicsbase[0])*meshScaling.getX(),btScalar(graphicsbase[1])*meshScaling.getY(),btScalar(graphicsbase[2])*meshScaling.getZ()); + } + } + + /* Perform ray vs. triangle collision here */ + m_callback->processTriangle(m_triangle,nodeSubPart,nodeTriangleIndex); + m_meshInterface->unLockReadOnlyVertexBase(nodeSubPart); + } + }; + + MyNodeOverlapCallback myNodeCallback(callback,m_meshInterface); + + m_bvh->reportBoxCastOverlappingNodex (&myNodeCallback, raySource, rayTarget, aabbMin, aabbMax); +} + +//perform bvh tree traversal and report overlapping triangles to 'callback' +void btBvhTriangleMeshShape::processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const +{ + +#ifdef DISABLE_BVH + //brute force traverse all triangles + btTriangleMeshShape::processAllTriangles(callback,aabbMin,aabbMax); +#else + + //first get all the nodes + + + struct MyNodeOverlapCallback : public btNodeOverlapCallback + { + btStridingMeshInterface* m_meshInterface; + btTriangleCallback* m_callback; + btVector3 m_triangle[3]; + + + MyNodeOverlapCallback(btTriangleCallback* callback,btStridingMeshInterface* meshInterface) + :m_meshInterface(meshInterface), + m_callback(callback) + { + } + + virtual void processNode(int nodeSubPart, int nodeTriangleIndex) + { + const unsigned char *vertexbase; + int numverts; + PHY_ScalarType type; + int stride; + const unsigned char *indexbase; + int indexstride; + int numfaces; + PHY_ScalarType indicestype; + + + m_meshInterface->getLockedReadOnlyVertexIndexBase( + &vertexbase, + numverts, + type, + stride, + &indexbase, + indexstride, + numfaces, + indicestype, + nodeSubPart); + + unsigned int* gfxbase = (unsigned int*)(indexbase+nodeTriangleIndex*indexstride); + btAssert(indicestype==PHY_INTEGER||indicestype==PHY_SHORT); + + const btVector3& meshScaling = m_meshInterface->getScaling(); + for (int j=2;j>=0;j--) + { + + int graphicsindex = indicestype==PHY_SHORT?((unsigned short*)gfxbase)[j]:gfxbase[j]; + + +#ifdef DEBUG_TRIANGLE_MESH + printf("%d ,",graphicsindex); +#endif //DEBUG_TRIANGLE_MESH + if (type == PHY_FLOAT) + { + float* graphicsbase = (float*)(vertexbase+graphicsindex*stride); + + m_triangle[j] = btVector3( + graphicsbase[0]*meshScaling.getX(), + graphicsbase[1]*meshScaling.getY(), + graphicsbase[2]*meshScaling.getZ()); + } + else + { + double* graphicsbase = (double*)(vertexbase+graphicsindex*stride); + + m_triangle[j] = btVector3( + btScalar(graphicsbase[0])*meshScaling.getX(), + btScalar(graphicsbase[1])*meshScaling.getY(), + btScalar(graphicsbase[2])*meshScaling.getZ()); + } +#ifdef DEBUG_TRIANGLE_MESH + printf("triangle vertices:%f,%f,%f\n",triangle[j].x(),triangle[j].y(),triangle[j].z()); +#endif //DEBUG_TRIANGLE_MESH + } + + m_callback->processTriangle(m_triangle,nodeSubPart,nodeTriangleIndex); + m_meshInterface->unLockReadOnlyVertexBase(nodeSubPart); + } + + }; + + MyNodeOverlapCallback myNodeCallback(callback,m_meshInterface); + + m_bvh->reportAabbOverlappingNodex(&myNodeCallback,aabbMin,aabbMax); + + +#endif//DISABLE_BVH + + +} + +void btBvhTriangleMeshShape::setLocalScaling(const btVector3& scaling) +{ + if ((getLocalScaling() -scaling).length2() > SIMD_EPSILON) + { + btTriangleMeshShape::setLocalScaling(scaling); + if (m_ownsBvh) + { + m_bvh->~btOptimizedBvh(); + btAlignedFree(m_bvh); + } + ///m_localAabbMin/m_localAabbMax is already re-calculated in btTriangleMeshShape. We could just scale aabb, but this needs some more work + void* mem = btAlignedAlloc(sizeof(btOptimizedBvh),16); + m_bvh = new(mem) btOptimizedBvh(); + //rebuild the bvh... + m_bvh->build(m_meshInterface,m_useQuantizedAabbCompression,m_localAabbMin,m_localAabbMax); + m_ownsBvh = true; + } +} + +void btBvhTriangleMeshShape::setOptimizedBvh(btOptimizedBvh* bvh, const btVector3& scaling) +{ + btAssert(!m_bvh); + btAssert(!m_ownsBvh); + + m_bvh = bvh; + m_ownsBvh = false; + // update the scaling without rebuilding the bvh + if ((getLocalScaling() -scaling).length2() > SIMD_EPSILON) + { + btTriangleMeshShape::setLocalScaling(scaling); + } +} + + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h new file mode 100644 index 000000000..8458a7ebc --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h @@ -0,0 +1,86 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 BVH_TRIANGLE_MESH_SHAPE_H +#define BVH_TRIANGLE_MESH_SHAPE_H + +#include "btTriangleMeshShape.h" +#include "btOptimizedBvh.h" +#include "LinearMath/btAlignedAllocator.h" + + +///The btBvhTriangleMeshShape is a static-triangle mesh shape with several optimizations, such as bounding volume hierarchy and cache friendly traversal for PlayStation 3 Cell SPU. It is recommended to enable useQuantizedAabbCompression for better memory usage. +///It takes a triangle mesh as input, for example a btTriangleMesh or btTriangleIndexVertexArray. The btBvhTriangleMeshShape class allows for triangle mesh deformations by a refit or partialRefit method. +///Instead of building the bounding volume hierarchy acceleration structure, it is also possible to serialize (save) and deserialize (load) the structure from disk. +///See Demos\ConcaveDemo\ConcavePhysicsDemo.cpp for an example. +ATTRIBUTE_ALIGNED16(class) btBvhTriangleMeshShape : public btTriangleMeshShape +{ + + btOptimizedBvh* m_bvh; + bool m_useQuantizedAabbCompression; + bool m_ownsBvh; + bool m_pad[11];////need padding due to alignment + +public: + + BT_DECLARE_ALIGNED_ALLOCATOR(); + + btBvhTriangleMeshShape() : btTriangleMeshShape(0),m_bvh(0),m_ownsBvh(false) {m_shapeType = TRIANGLE_MESH_SHAPE_PROXYTYPE;}; + btBvhTriangleMeshShape(btStridingMeshInterface* meshInterface, bool useQuantizedAabbCompression, bool buildBvh = true); + + ///optionally pass in a larger bvh aabb, used for quantization. This allows for deformations within this aabb + btBvhTriangleMeshShape(btStridingMeshInterface* meshInterface, bool useQuantizedAabbCompression,const btVector3& bvhAabbMin,const btVector3& bvhAabbMax, bool buildBvh = true); + + virtual ~btBvhTriangleMeshShape(); + + bool getOwnsBvh () const + { + return m_ownsBvh; + } + + + + void performRaycast (btTriangleCallback* callback, const btVector3& raySource, const btVector3& rayTarget); + void performConvexcast (btTriangleCallback* callback, const btVector3& boxSource, const btVector3& boxTarget, const btVector3& boxMin, const btVector3& boxMax); + + virtual void processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const; + + void refitTree(const btVector3& aabbMin,const btVector3& aabbMax); + + ///for a fast incremental refit of parts of the tree. Note: the entire AABB of the tree will become more conservative, it never shrinks + void partialRefitTree(const btVector3& aabbMin,const btVector3& aabbMax); + + //debugging + virtual const char* getName()const {return "BVHTRIANGLEMESH";} + + + virtual void setLocalScaling(const btVector3& scaling); + + btOptimizedBvh* getOptimizedBvh() + { + return m_bvh; + } + + + void setOptimizedBvh(btOptimizedBvh* bvh, const btVector3& localScaling=btVector3(1,1,1)); + + bool usesQuantizedAabbCompression() const + { + return m_useQuantizedAabbCompression; + } +} +; + +#endif //BVH_TRIANGLE_MESH_SHAPE_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCapsuleShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCapsuleShape.cpp new file mode 100644 index 000000000..2faa11d43 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCapsuleShape.cpp @@ -0,0 +1,171 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 "btCapsuleShape.h" + +#include "BulletCollision/CollisionShapes/btCollisionMargin.h" +#include "LinearMath/btQuaternion.h" + +btCapsuleShape::btCapsuleShape(btScalar radius, btScalar height) : btConvexInternalShape () +{ + m_shapeType = CAPSULE_SHAPE_PROXYTYPE; + m_upAxis = 1; + m_implicitShapeDimensions.setValue(radius,0.5f*height,radius); +} + + + btVector3 btCapsuleShape::localGetSupportingVertexWithoutMargin(const btVector3& vec0)const +{ + + btVector3 supVec(0,0,0); + + btScalar maxDot(btScalar(-BT_LARGE_FLOAT)); + + btVector3 vec = vec0; + btScalar lenSqr = vec.length2(); + if (lenSqr < btScalar(0.0001)) + { + vec.setValue(1,0,0); + } else + { + btScalar rlen = btScalar(1.) / btSqrt(lenSqr ); + vec *= rlen; + } + + btVector3 vtx; + btScalar newDot; + + btScalar radius = getRadius(); + + + { + btVector3 pos(0,0,0); + pos[getUpAxis()] = getHalfHeight(); + + vtx = pos +vec*m_localScaling*(radius) - vec * getMargin(); + newDot = vec.dot(vtx); + if (newDot > maxDot) + { + maxDot = newDot; + supVec = vtx; + } + } + { + btVector3 pos(0,0,0); + pos[getUpAxis()] = -getHalfHeight(); + + vtx = pos +vec*m_localScaling*(radius) - vec * getMargin(); + newDot = vec.dot(vtx); + if (newDot > maxDot) + { + maxDot = newDot; + supVec = vtx; + } + } + + return supVec; + +} + + void btCapsuleShape::batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const +{ + + + btScalar radius = getRadius(); + + for (int j=0;j maxDot) + { + maxDot = newDot; + supportVerticesOut[j] = vtx; + } + } + { + btVector3 pos(0,0,0); + pos[getUpAxis()] = -getHalfHeight(); + vtx = pos +vec*m_localScaling*(radius) - vec * getMargin(); + newDot = vec.dot(vtx); + if (newDot > maxDot) + { + maxDot = newDot; + supportVerticesOut[j] = vtx; + } + } + + } +} + + +void btCapsuleShape::calculateLocalInertia(btScalar mass,btVector3& inertia) const +{ + //as an approximation, take the inertia of the box that bounds the spheres + + btTransform ident; + ident.setIdentity(); + + + btScalar radius = getRadius(); + + btVector3 halfExtents(radius,radius,radius); + halfExtents[getUpAxis()]+=getHalfHeight(); + + btScalar margin = CONVEX_DISTANCE_MARGIN; + + btScalar lx=btScalar(2.)*(halfExtents[0]+margin); + btScalar ly=btScalar(2.)*(halfExtents[1]+margin); + btScalar lz=btScalar(2.)*(halfExtents[2]+margin); + const btScalar x2 = lx*lx; + const btScalar y2 = ly*ly; + const btScalar z2 = lz*lz; + const btScalar scaledmass = mass * btScalar(.08333333); + + inertia[0] = scaledmass * (y2+z2); + inertia[1] = scaledmass * (x2+z2); + inertia[2] = scaledmass * (x2+y2); + +} + +btCapsuleShapeX::btCapsuleShapeX(btScalar radius,btScalar height) +{ + m_upAxis = 0; + m_implicitShapeDimensions.setValue(0.5f*height, radius,radius); +} + + + + + + +btCapsuleShapeZ::btCapsuleShapeZ(btScalar radius,btScalar height) +{ + m_upAxis = 2; + m_implicitShapeDimensions.setValue(radius,radius,0.5f*height); +} + + + + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCapsuleShape.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCapsuleShape.h new file mode 100644 index 000000000..782efb235 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCapsuleShape.h @@ -0,0 +1,129 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 BT_CAPSULE_SHAPE_H +#define BT_CAPSULE_SHAPE_H + +#include "btConvexInternalShape.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" // for the types + + +///The btCapsuleShape represents a capsule around the Y axis, there is also the btCapsuleShapeX aligned around the X axis and btCapsuleShapeZ around the Z axis. +///The total height is height+2*radius, so the height is just the height between the center of each 'sphere' of the capsule caps. +///The btCapsuleShape is a convex hull of two spheres. The btMultiSphereShape is a more general collision shape that takes the convex hull of multiple sphere, so it can also represent a capsule when just using two spheres. +class btCapsuleShape : public btConvexInternalShape +{ +protected: + int m_upAxis; + +protected: + ///only used for btCapsuleShapeZ and btCapsuleShapeX subclasses. + btCapsuleShape() : btConvexInternalShape() {m_shapeType = CAPSULE_SHAPE_PROXYTYPE;}; + +public: + btCapsuleShape(btScalar radius,btScalar height); + + ///CollisionShape Interface + virtual void calculateLocalInertia(btScalar mass,btVector3& inertia) const; + + /// btConvexShape Interface + virtual btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec)const; + + virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const; + + virtual void getAabb (const btTransform& t, btVector3& aabbMin, btVector3& aabbMax) const + { + btVector3 halfExtents(getRadius(),getRadius(),getRadius()); + halfExtents[m_upAxis] = getRadius() + getHalfHeight(); + halfExtents += btVector3(getMargin(),getMargin(),getMargin()); + btMatrix3x3 abs_b = t.getBasis().absolute(); + btVector3 center = t.getOrigin(); + btVector3 extent = btVector3(abs_b[0].dot(halfExtents),abs_b[1].dot(halfExtents),abs_b[2].dot(halfExtents)); + + aabbMin = center - extent; + aabbMax = center + extent; + } + + virtual const char* getName()const + { + return "CapsuleShape"; + } + + int getUpAxis() const + { + return m_upAxis; + } + + btScalar getRadius() const + { + int radiusAxis = (m_upAxis+2)%3; + return m_implicitShapeDimensions[radiusAxis]; + } + + btScalar getHalfHeight() const + { + return m_implicitShapeDimensions[m_upAxis]; + } + + virtual void setLocalScaling(const btVector3& scaling) + { + btVector3 oldMargin(getMargin(),getMargin(),getMargin()); + btVector3 implicitShapeDimensionsWithMargin = m_implicitShapeDimensions+oldMargin; + btVector3 unScaledImplicitShapeDimensionsWithMargin = implicitShapeDimensionsWithMargin / m_localScaling; + + btConvexInternalShape::setLocalScaling(scaling); + + m_implicitShapeDimensions = (unScaledImplicitShapeDimensionsWithMargin * m_localScaling) - oldMargin; + + } +}; + +///btCapsuleShapeX represents a capsule around the Z axis +///the total height is height+2*radius, so the height is just the height between the center of each 'sphere' of the capsule caps. +class btCapsuleShapeX : public btCapsuleShape +{ +public: + + btCapsuleShapeX(btScalar radius,btScalar height); + + //debugging + virtual const char* getName()const + { + return "CapsuleX"; + } + + + +}; + +///btCapsuleShapeZ represents a capsule around the Z axis +///the total height is height+2*radius, so the height is just the height between the center of each 'sphere' of the capsule caps. +class btCapsuleShapeZ : public btCapsuleShape +{ +public: + btCapsuleShapeZ(btScalar radius,btScalar height); + + //debugging + virtual const char* getName()const + { + return "CapsuleZ"; + } + + +}; + + + +#endif //BT_CAPSULE_SHAPE_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCollisionMargin.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCollisionMargin.h new file mode 100644 index 000000000..18fd02604 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCollisionMargin.h @@ -0,0 +1,26 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 COLLISION_MARGIN_H +#define COLLISION_MARGIN_H + +//used by Gjk and some other algorithms + +#define CONVEX_DISTANCE_MARGIN btScalar(0.04)// btScalar(0.1)//;//btScalar(0.01) + + + +#endif //COLLISION_MARGIN_H + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCollisionShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCollisionShape.cpp new file mode 100644 index 000000000..b534998a1 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCollisionShape.cpp @@ -0,0 +1,98 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 "BulletCollision/CollisionShapes/btCollisionShape.h" + + +btScalar gContactThresholdFactor=btScalar(0.02); + + +/* + Make sure this dummy function never changes so that it + can be used by probes that are checking whether the + library is actually installed. +*/ +extern "C" +{ +void btBulletCollisionProbe (); + +void btBulletCollisionProbe () {} +} + + + +void btCollisionShape::getBoundingSphere(btVector3& center,btScalar& radius) const +{ + btTransform tr; + tr.setIdentity(); + btVector3 aabbMin,aabbMax; + + getAabb(tr,aabbMin,aabbMax); + + radius = (aabbMax-aabbMin).length()*btScalar(0.5); + center = (aabbMin+aabbMax)*btScalar(0.5); +} + + +btScalar btCollisionShape::getContactBreakingThreshold() const +{ + return getAngularMotionDisc() * gContactThresholdFactor; +} +btScalar btCollisionShape::getAngularMotionDisc() const +{ + ///@todo cache this value, to improve performance + btVector3 center; + btScalar disc; + getBoundingSphere(center,disc); + disc += (center).length(); + return disc; +} + +void btCollisionShape::calculateTemporalAabb(const btTransform& curTrans,const btVector3& linvel,const btVector3& angvel,btScalar timeStep, btVector3& temporalAabbMin,btVector3& temporalAabbMax) const +{ + //start with static aabb + getAabb(curTrans,temporalAabbMin,temporalAabbMax); + + btScalar temporalAabbMaxx = temporalAabbMax.getX(); + btScalar temporalAabbMaxy = temporalAabbMax.getY(); + btScalar temporalAabbMaxz = temporalAabbMax.getZ(); + btScalar temporalAabbMinx = temporalAabbMin.getX(); + btScalar temporalAabbMiny = temporalAabbMin.getY(); + btScalar temporalAabbMinz = temporalAabbMin.getZ(); + + // add linear motion + btVector3 linMotion = linvel*timeStep; + ///@todo: simd would have a vector max/min operation, instead of per-element access + if (linMotion.x() > btScalar(0.)) + temporalAabbMaxx += linMotion.x(); + else + temporalAabbMinx += linMotion.x(); + if (linMotion.y() > btScalar(0.)) + temporalAabbMaxy += linMotion.y(); + else + temporalAabbMiny += linMotion.y(); + if (linMotion.z() > btScalar(0.)) + temporalAabbMaxz += linMotion.z(); + else + temporalAabbMinz += linMotion.z(); + + //add conservative angular motion + btScalar angularMotion = angvel.length() * getAngularMotionDisc() * timeStep; + btVector3 angularMotion3d(angularMotion,angularMotion,angularMotion); + temporalAabbMin = btVector3(temporalAabbMinx,temporalAabbMiny,temporalAabbMinz); + temporalAabbMax = btVector3(temporalAabbMaxx,temporalAabbMaxy,temporalAabbMaxz); + + temporalAabbMin -= angularMotion3d; + temporalAabbMax += angularMotion3d; +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCollisionShape.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCollisionShape.h new file mode 100644 index 000000000..215758ef5 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCollisionShape.h @@ -0,0 +1,117 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 COLLISION_SHAPE_H +#define COLLISION_SHAPE_H + +#include "LinearMath/btTransform.h" +#include "LinearMath/btVector3.h" +#include "LinearMath/btMatrix3x3.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" //for the shape types + +///The btCollisionShape class provides an interface for collision shapes that can be shared among btCollisionObjects. +class btCollisionShape +{ +protected: + int m_shapeType; + void* m_userPointer; + +public: + + btCollisionShape() : m_shapeType (INVALID_SHAPE_PROXYTYPE), m_userPointer(0) + { + } + + virtual ~btCollisionShape() + { + } + + ///getAabb returns the axis aligned bounding box in the coordinate frame of the given transform t. + virtual void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const =0; + + virtual void getBoundingSphere(btVector3& center,btScalar& radius) const; + + ///getAngularMotionDisc returns the maximus radius needed for Conservative Advancement to handle time-of-impact with rotations. + virtual btScalar getAngularMotionDisc() const; + + virtual btScalar getContactBreakingThreshold() const; + + + ///calculateTemporalAabb calculates the enclosing aabb for the moving object over interval [0..timeStep) + ///result is conservative + void calculateTemporalAabb(const btTransform& curTrans,const btVector3& linvel,const btVector3& angvel,btScalar timeStep, btVector3& temporalAabbMin,btVector3& temporalAabbMax) const; + +#ifndef __SPU__ + + SIMD_FORCE_INLINE bool isPolyhedral() const + { + return btBroadphaseProxy::isPolyhedral(getShapeType()); + } + + SIMD_FORCE_INLINE bool isConvex2d() const + { + return btBroadphaseProxy::isConvex2d(getShapeType()); + } + + SIMD_FORCE_INLINE bool isConvex() const + { + return btBroadphaseProxy::isConvex(getShapeType()); + } + SIMD_FORCE_INLINE bool isConcave() const + { + return btBroadphaseProxy::isConcave(getShapeType()); + } + SIMD_FORCE_INLINE bool isCompound() const + { + return btBroadphaseProxy::isCompound(getShapeType()); + } + + ///isInfinite is used to catch simulation error (aabb check) + SIMD_FORCE_INLINE bool isInfinite() const + { + return btBroadphaseProxy::isInfinite(getShapeType()); + } + + + virtual void setLocalScaling(const btVector3& scaling) =0; + virtual const btVector3& getLocalScaling() const =0; + virtual void calculateLocalInertia(btScalar mass,btVector3& inertia) const = 0; + + +//debugging support + virtual const char* getName()const =0 ; +#endif //__SPU__ + + + int getShapeType() const { return m_shapeType; } + virtual void setMargin(btScalar margin) = 0; + virtual btScalar getMargin() const = 0; + + + ///optional user data pointer + void setUserPointer(void* userPtr) + { + m_userPointer = userPtr; + } + + void* getUserPointer() const + { + return m_userPointer; + } + +}; + +#endif //COLLISION_SHAPE_H + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCompoundShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCompoundShape.cpp new file mode 100644 index 000000000..d77193874 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCompoundShape.cpp @@ -0,0 +1,266 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 "btCompoundShape.h" +#include "btCollisionShape.h" +#include "BulletCollision/BroadphaseCollision/btDbvt.h" + +btCompoundShape::btCompoundShape(bool enableDynamicAabbTree) +: m_localAabbMin(btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT)), +m_localAabbMax(btScalar(-BT_LARGE_FLOAT),btScalar(-BT_LARGE_FLOAT),btScalar(-BT_LARGE_FLOAT)), +m_dynamicAabbTree(0), +m_updateRevision(1), +m_collisionMargin(btScalar(0.)), +m_localScaling(btScalar(1.),btScalar(1.),btScalar(1.)) +{ + m_shapeType = COMPOUND_SHAPE_PROXYTYPE; + + if (enableDynamicAabbTree) + { + void* mem = btAlignedAlloc(sizeof(btDbvt),16); + m_dynamicAabbTree = new(mem) btDbvt(); + btAssert(mem==m_dynamicAabbTree); + } +} + + +btCompoundShape::~btCompoundShape() +{ + if (m_dynamicAabbTree) + { + m_dynamicAabbTree->~btDbvt(); + btAlignedFree(m_dynamicAabbTree); + } +} + +void btCompoundShape::addChildShape(const btTransform& localTransform,btCollisionShape* shape) +{ + m_updateRevision++; + //m_childTransforms.push_back(localTransform); + //m_childShapes.push_back(shape); + btCompoundShapeChild child; + child.m_transform = localTransform; + child.m_childShape = shape; + child.m_childShapeType = shape->getShapeType(); + child.m_childMargin = shape->getMargin(); + + + //extend the local aabbMin/aabbMax + btVector3 localAabbMin,localAabbMax; + shape->getAabb(localTransform,localAabbMin,localAabbMax); + for (int i=0;i<3;i++) + { + if (m_localAabbMin[i] > localAabbMin[i]) + { + m_localAabbMin[i] = localAabbMin[i]; + } + if (m_localAabbMax[i] < localAabbMax[i]) + { + m_localAabbMax[i] = localAabbMax[i]; + } + + } + if (m_dynamicAabbTree) + { + const btDbvtVolume bounds=btDbvtVolume::FromMM(localAabbMin,localAabbMax); + int index = m_children.size(); + child.m_node = m_dynamicAabbTree->insert(bounds,(void*)index); + } + + m_children.push_back(child); + +} + +void btCompoundShape::updateChildTransform(int childIndex, const btTransform& newChildTransform) +{ + m_children[childIndex].m_transform = newChildTransform; + + if (m_dynamicAabbTree) + { + ///update the dynamic aabb tree + btVector3 localAabbMin,localAabbMax; + m_children[childIndex].m_childShape->getAabb(newChildTransform,localAabbMin,localAabbMax); + ATTRIBUTE_ALIGNED16(btDbvtVolume) bounds=btDbvtVolume::FromMM(localAabbMin,localAabbMax); + //int index = m_children.size()-1; + m_dynamicAabbTree->update(m_children[childIndex].m_node,bounds); + } + + recalculateLocalAabb(); +} + +void btCompoundShape::removeChildShapeByIndex(int childShapeIndex) +{ + m_updateRevision++; + btAssert(childShapeIndex >=0 && childShapeIndex < m_children.size()); + if (m_dynamicAabbTree) + { + m_dynamicAabbTree->remove(m_children[childShapeIndex].m_node); + } + m_children.swap(childShapeIndex,m_children.size()-1); + m_children.pop_back(); + +} + + + +void btCompoundShape::removeChildShape(btCollisionShape* shape) +{ + m_updateRevision++; + // Find the children containing the shape specified, and remove those children. + //note: there might be multiple children using the same shape! + for(int i = m_children.size()-1; i >= 0 ; i--) + { + if(m_children[i].m_childShape == shape) + { + removeChildShapeByIndex(i); + } + } + + + + recalculateLocalAabb(); +} + +void btCompoundShape::recalculateLocalAabb() +{ + // Recalculate the local aabb + // Brute force, it iterates over all the shapes left. + + m_localAabbMin = btVector3(btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT)); + m_localAabbMax = btVector3(btScalar(-BT_LARGE_FLOAT),btScalar(-BT_LARGE_FLOAT),btScalar(-BT_LARGE_FLOAT)); + + //extend the local aabbMin/aabbMax + for (int j = 0; j < m_children.size(); j++) + { + btVector3 localAabbMin,localAabbMax; + m_children[j].m_childShape->getAabb(m_children[j].m_transform, localAabbMin, localAabbMax); + for (int i=0;i<3;i++) + { + if (m_localAabbMin[i] > localAabbMin[i]) + m_localAabbMin[i] = localAabbMin[i]; + if (m_localAabbMax[i] < localAabbMax[i]) + m_localAabbMax[i] = localAabbMax[i]; + } + } +} + +///getAabb's default implementation is brute force, expected derived classes to implement a fast dedicated version +void btCompoundShape::getAabb(const btTransform& trans,btVector3& aabbMin,btVector3& aabbMax) const +{ + btVector3 localHalfExtents = btScalar(0.5)*(m_localAabbMax-m_localAabbMin); + btVector3 localCenter = btScalar(0.5)*(m_localAabbMax+m_localAabbMin); + + //avoid an illegal AABB when there are no children + if (!m_children.size()) + { + localHalfExtents.setValue(0,0,0); + localCenter.setValue(0,0,0); + } + localHalfExtents += btVector3(getMargin(),getMargin(),getMargin()); + + + btMatrix3x3 abs_b = trans.getBasis().absolute(); + + btVector3 center = trans(localCenter); + + btVector3 extent = btVector3(abs_b[0].dot(localHalfExtents), + abs_b[1].dot(localHalfExtents), + abs_b[2].dot(localHalfExtents)); + aabbMin = center-extent; + aabbMax = center+extent; + +} + +void btCompoundShape::calculateLocalInertia(btScalar mass,btVector3& inertia) const +{ + //approximation: take the inertia from the aabb for now + btTransform ident; + ident.setIdentity(); + btVector3 aabbMin,aabbMax; + getAabb(ident,aabbMin,aabbMax); + + btVector3 halfExtents = (aabbMax-aabbMin)*btScalar(0.5); + + btScalar lx=btScalar(2.)*(halfExtents.x()); + btScalar ly=btScalar(2.)*(halfExtents.y()); + btScalar lz=btScalar(2.)*(halfExtents.z()); + + inertia[0] = mass/(btScalar(12.0)) * (ly*ly + lz*lz); + inertia[1] = mass/(btScalar(12.0)) * (lx*lx + lz*lz); + inertia[2] = mass/(btScalar(12.0)) * (lx*lx + ly*ly); + +} + + + + +void btCompoundShape::calculatePrincipalAxisTransform(btScalar* masses, btTransform& principal, btVector3& inertia) const +{ + int n = m_children.size(); + + btScalar totalMass = 0; + btVector3 center(0, 0, 0); + int k; + + for (k = 0; k < n; k++) + { + center += m_children[k].m_transform.getOrigin() * masses[k]; + totalMass += masses[k]; + } + center /= totalMass; + principal.setOrigin(center); + + btMatrix3x3 tensor(0, 0, 0, 0, 0, 0, 0, 0, 0); + for ( k = 0; k < n; k++) + { + btVector3 i; + m_children[k].m_childShape->calculateLocalInertia(masses[k], i); + + const btTransform& t = m_children[k].m_transform; + btVector3 o = t.getOrigin() - center; + + //compute inertia tensor in coordinate system of compound shape + btMatrix3x3 j = t.getBasis().transpose(); + j[0] *= i[0]; + j[1] *= i[1]; + j[2] *= i[2]; + j = t.getBasis() * j; + + //add inertia tensor + tensor[0] += j[0]; + tensor[1] += j[1]; + tensor[2] += j[2]; + + //compute inertia tensor of pointmass at o + btScalar o2 = o.length2(); + j[0].setValue(o2, 0, 0); + j[1].setValue(0, o2, 0); + j[2].setValue(0, 0, o2); + j[0] += o * -o.x(); + j[1] += o * -o.y(); + j[2] += o * -o.z(); + + //add inertia tensor of pointmass + tensor[0] += masses[k] * j[0]; + tensor[1] += masses[k] * j[1]; + tensor[2] += masses[k] * j[2]; + } + + tensor.diagonalize(principal.getBasis(), btScalar(0.00001), 20); + inertia.setValue(tensor[0][0], tensor[1][1], tensor[2][2]); +} + + + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCompoundShape.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCompoundShape.h new file mode 100644 index 000000000..577ef9513 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCompoundShape.h @@ -0,0 +1,172 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 COMPOUND_SHAPE_H +#define COMPOUND_SHAPE_H + +#include "btCollisionShape.h" + +#include "LinearMath/btVector3.h" +#include "LinearMath/btTransform.h" +#include "LinearMath/btMatrix3x3.h" +#include "btCollisionMargin.h" +#include "LinearMath/btAlignedObjectArray.h" + +//class btOptimizedBvh; +struct btDbvt; + +ATTRIBUTE_ALIGNED16(struct) btCompoundShapeChild +{ + BT_DECLARE_ALIGNED_ALLOCATOR(); + + btTransform m_transform; + btCollisionShape* m_childShape; + int m_childShapeType; + btScalar m_childMargin; + struct btDbvtNode* m_node; +}; + +SIMD_FORCE_INLINE bool operator==(const btCompoundShapeChild& c1, const btCompoundShapeChild& c2) +{ + return ( c1.m_transform == c2.m_transform && + c1.m_childShape == c2.m_childShape && + c1.m_childShapeType == c2.m_childShapeType && + c1.m_childMargin == c2.m_childMargin ); +} + +/// The btCompoundShape allows to store multiple other btCollisionShapes +/// This allows for moving concave collision objects. This is more general then the static concave btBvhTriangleMeshShape. +/// It has an (optional) dynamic aabb tree to accelerate early rejection tests. +/// @todo: This aabb tree can also be use to speed up ray tests on btCompoundShape, see http://code.google.com/p/bullet/issues/detail?id=25 +/// Currently, removal of child shapes is only supported when disabling the aabb tree (pass 'false' in the constructor of btCompoundShape) +ATTRIBUTE_ALIGNED16(class) btCompoundShape : public btCollisionShape +{ + btAlignedObjectArray m_children; + btVector3 m_localAabbMin; + btVector3 m_localAabbMax; + + btDbvt* m_dynamicAabbTree; + + ///increment m_updateRevision when adding/removing/replacing child shapes, so that some caches can be updated + int m_updateRevision; + +public: + BT_DECLARE_ALIGNED_ALLOCATOR(); + + btCompoundShape(bool enableDynamicAabbTree = true); + + virtual ~btCompoundShape(); + + void addChildShape(const btTransform& localTransform,btCollisionShape* shape); + + /// Remove all children shapes that contain the specified shape + virtual void removeChildShape(btCollisionShape* shape); + + void removeChildShapeByIndex(int childShapeindex); + + + int getNumChildShapes() const + { + return int (m_children.size()); + } + + btCollisionShape* getChildShape(int index) + { + return m_children[index].m_childShape; + } + const btCollisionShape* getChildShape(int index) const + { + return m_children[index].m_childShape; + } + + btTransform& getChildTransform(int index) + { + return m_children[index].m_transform; + } + const btTransform& getChildTransform(int index) const + { + return m_children[index].m_transform; + } + + ///set a new transform for a child, and update internal data structures (local aabb and dynamic tree) + void updateChildTransform(int childIndex, const btTransform& newChildTransform); + + + btCompoundShapeChild* getChildList() + { + return &m_children[0]; + } + + ///getAabb's default implementation is brute force, expected derived classes to implement a fast dedicated version + virtual void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const; + + /** Re-calculate the local Aabb. Is called at the end of removeChildShapes. + Use this yourself if you modify the children or their transforms. */ + virtual void recalculateLocalAabb(); + + virtual void setLocalScaling(const btVector3& scaling) + { + m_localScaling = scaling; + } + virtual const btVector3& getLocalScaling() const + { + return m_localScaling; + } + + virtual void calculateLocalInertia(btScalar mass,btVector3& inertia) const; + + virtual void setMargin(btScalar margin) + { + m_collisionMargin = margin; + } + virtual btScalar getMargin() const + { + return m_collisionMargin; + } + virtual const char* getName()const + { + return "Compound"; + } + + //this is optional, but should make collision queries faster, by culling non-overlapping nodes + void createAabbTreeFromChildren(); + + btDbvt* getDynamicAabbTree() + { + return m_dynamicAabbTree; + } + + ///computes the exact moment of inertia and the transform from the coordinate system defined by the principal axes of the moment of inertia + ///and the center of mass to the current coordinate system. "masses" points to an array of masses of the children. The resulting transform + ///"principal" has to be applied inversely to all children transforms in order for the local coordinate system of the compound + ///shape to be centered at the center of mass and to coincide with the principal axes. This also necessitates a correction of the world transform + ///of the collision object by the principal transform. + void calculatePrincipalAxisTransform(btScalar* masses, btTransform& principal, btVector3& inertia) const; + + int getUpdateRevision() const + { + return m_updateRevision; + } + +private: + btScalar m_collisionMargin; +protected: + btVector3 m_localScaling; + +}; + + + +#endif //COMPOUND_SHAPE_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConcaveShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConcaveShape.cpp new file mode 100644 index 000000000..58ff84a5b --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConcaveShape.cpp @@ -0,0 +1,27 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 "btConcaveShape.h" + +btConcaveShape::btConcaveShape() : m_collisionMargin(btScalar(0.)) +{ + +} + +btConcaveShape::~btConcaveShape() +{ + +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConcaveShape.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConcaveShape.h new file mode 100644 index 000000000..2a370a47c --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConcaveShape.h @@ -0,0 +1,60 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 CONCAVE_SHAPE_H +#define CONCAVE_SHAPE_H + +#include "btCollisionShape.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" // for the types +#include "btTriangleCallback.h" + +/// PHY_ScalarType enumerates possible scalar types. +/// See the btStridingMeshInterface or btHeightfieldTerrainShape for its use +typedef enum PHY_ScalarType { + PHY_FLOAT, + PHY_DOUBLE, + PHY_INTEGER, + PHY_SHORT, + PHY_FIXEDPOINT88, + PHY_UCHAR +} PHY_ScalarType; + +///The btConcaveShape class provides an interface for non-moving (static) concave shapes. +///It has been implemented by the btStaticPlaneShape, btBvhTriangleMeshShape and btHeightfieldTerrainShape. +class btConcaveShape : public btCollisionShape +{ +protected: + btScalar m_collisionMargin; + +public: + btConcaveShape(); + + virtual ~btConcaveShape(); + + virtual void processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const = 0; + + virtual btScalar getMargin() const { + return m_collisionMargin; + } + virtual void setMargin(btScalar collisionMargin) + { + m_collisionMargin = collisionMargin; + } + + + +}; + +#endif //CONCAVE_SHAPE_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConeShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConeShape.cpp new file mode 100644 index 000000000..dfa3f941e --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConeShape.cpp @@ -0,0 +1,133 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 "btConeShape.h" + + + +btConeShape::btConeShape (btScalar radius,btScalar height): btConvexInternalShape (), +m_radius (radius), +m_height(height) +{ + m_shapeType = CONE_SHAPE_PROXYTYPE; + setConeUpIndex(1); + btVector3 halfExtents; + m_sinAngle = (m_radius / btSqrt(m_radius * m_radius + m_height * m_height)); +} + +btConeShapeZ::btConeShapeZ (btScalar radius,btScalar height): +btConeShape(radius,height) +{ + setConeUpIndex(2); +} + +btConeShapeX::btConeShapeX (btScalar radius,btScalar height): +btConeShape(radius,height) +{ + setConeUpIndex(0); +} + +///choose upAxis index +void btConeShape::setConeUpIndex(int upIndex) +{ + switch (upIndex) + { + case 0: + m_coneIndices[0] = 1; + m_coneIndices[1] = 0; + m_coneIndices[2] = 2; + break; + case 1: + m_coneIndices[0] = 0; + m_coneIndices[1] = 1; + m_coneIndices[2] = 2; + break; + case 2: + m_coneIndices[0] = 0; + m_coneIndices[1] = 2; + m_coneIndices[2] = 1; + break; + default: + btAssert(0); + }; +} + +btVector3 btConeShape::coneLocalSupport(const btVector3& v) const +{ + + btScalar halfHeight = m_height * btScalar(0.5); + + if (v[m_coneIndices[1]] > v.length() * m_sinAngle) + { + btVector3 tmp; + + tmp[m_coneIndices[0]] = btScalar(0.); + tmp[m_coneIndices[1]] = halfHeight; + tmp[m_coneIndices[2]] = btScalar(0.); + return tmp; + } + else { + btScalar s = btSqrt(v[m_coneIndices[0]] * v[m_coneIndices[0]] + v[m_coneIndices[2]] * v[m_coneIndices[2]]); + if (s > SIMD_EPSILON) { + btScalar d = m_radius / s; + btVector3 tmp; + tmp[m_coneIndices[0]] = v[m_coneIndices[0]] * d; + tmp[m_coneIndices[1]] = -halfHeight; + tmp[m_coneIndices[2]] = v[m_coneIndices[2]] * d; + return tmp; + } + else { + btVector3 tmp; + tmp[m_coneIndices[0]] = btScalar(0.); + tmp[m_coneIndices[1]] = -halfHeight; + tmp[m_coneIndices[2]] = btScalar(0.); + return tmp; + } + } + +} + +btVector3 btConeShape::localGetSupportingVertexWithoutMargin(const btVector3& vec) const +{ + return coneLocalSupport(vec); +} + +void btConeShape::batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const +{ + for (int i=0;ilocalGetSupportingVertexWithoutMargin(vec); +} + +void btConvex2dShape::batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const +{ + m_childConvexShape->batchedUnitVectorGetSupportingVertexWithoutMargin(vectors,supportVerticesOut,numVectors); +} + + +btVector3 btConvex2dShape::localGetSupportingVertex(const btVector3& vec)const +{ + return m_childConvexShape->localGetSupportingVertex(vec); +} + + +void btConvex2dShape::calculateLocalInertia(btScalar mass,btVector3& inertia) const +{ + ///this linear upscaling is not realistic, but we don't deal with large mass ratios... + m_childConvexShape->calculateLocalInertia(mass,inertia); +} + + + ///getAabb's default implementation is brute force, expected derived classes to implement a fast dedicated version +void btConvex2dShape::getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const +{ + m_childConvexShape->getAabb(t,aabbMin,aabbMax); +} + +void btConvex2dShape::getAabbSlow(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const +{ + m_childConvexShape->getAabbSlow(t,aabbMin,aabbMax); +} + +void btConvex2dShape::setLocalScaling(const btVector3& scaling) +{ + m_childConvexShape->setLocalScaling(scaling); +} + +const btVector3& btConvex2dShape::getLocalScaling() const +{ + return m_childConvexShape->getLocalScaling(); +} + +void btConvex2dShape::setMargin(btScalar margin) +{ + m_childConvexShape->setMargin(margin); +} +btScalar btConvex2dShape::getMargin() const +{ + return m_childConvexShape->getMargin(); +} + +int btConvex2dShape::getNumPreferredPenetrationDirections() const +{ + return m_childConvexShape->getNumPreferredPenetrationDirections(); +} + +void btConvex2dShape::getPreferredPenetrationDirection(int index, btVector3& penetrationVector) const +{ + m_childConvexShape->getPreferredPenetrationDirection(index,penetrationVector); +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvex2dShape.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvex2dShape.h new file mode 100644 index 000000000..58166c829 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvex2dShape.h @@ -0,0 +1,80 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 BT_CONVEX_2D_SHAPE_H +#define BT_CONVEX_2D_SHAPE_H + +#include "BulletCollision/CollisionShapes/btConvexShape.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" // for the types + +///The btConvex2dShape allows to use arbitrary convex shapes are 2d convex shapes, with the Z component assumed to be 0. +///For 2d boxes, the btBox2dShape is recommended. +class btConvex2dShape : public btConvexShape +{ + btConvexShape* m_childConvexShape; + + public: + + btConvex2dShape( btConvexShape* convexChildShape); + + virtual ~btConvex2dShape(); + + virtual btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec)const; + + virtual btVector3 localGetSupportingVertex(const btVector3& vec)const; + + virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const; + + virtual void calculateLocalInertia(btScalar mass,btVector3& inertia) const; + + btConvexShape* getChildShape() + { + return m_childConvexShape; + } + + const btConvexShape* getChildShape() const + { + return m_childConvexShape; + } + + virtual const char* getName()const + { + return "Convex2dShape"; + } + + + + /////////////////////////// + + + ///getAabb's default implementation is brute force, expected derived classes to implement a fast dedicated version + void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const; + + virtual void getAabbSlow(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const; + + virtual void setLocalScaling(const btVector3& scaling) ; + virtual const btVector3& getLocalScaling() const ; + + virtual void setMargin(btScalar margin); + virtual btScalar getMargin() const; + + virtual int getNumPreferredPenetrationDirections() const; + + virtual void getPreferredPenetrationDirection(int index, btVector3& penetrationVector) const; + + +}; + +#endif //BT_CONVEX_2D_SHAPE_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexHullShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexHullShape.cpp new file mode 100644 index 000000000..3008524e0 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexHullShape.cpp @@ -0,0 +1,188 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 "btConvexHullShape.h" +#include "BulletCollision/CollisionShapes/btCollisionMargin.h" + +#include "LinearMath/btQuaternion.h" + + +btConvexHullShape ::btConvexHullShape (const btScalar* points,int numPoints,int stride) : btPolyhedralConvexAabbCachingShape () +{ + m_shapeType = CONVEX_HULL_SHAPE_PROXYTYPE; + m_unscaledPoints.resize(numPoints); + + unsigned char* pointsAddress = (unsigned char*)points; + + for (int i=0;i maxDot) + { + maxDot = newDot; + supVec = vtx; + } + } + return supVec; +} + +void btConvexHullShape::batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const +{ + btScalar newDot; + //use 'w' component of supportVerticesOut? + { + for (int i=0;i supportVerticesOut[j][3]) + { + //WARNING: don't swap next lines, the w component would get overwritten! + supportVerticesOut[j] = vtx; + supportVerticesOut[j][3] = newDot; + } + } + } + + + +} + + + +btVector3 btConvexHullShape::localGetSupportingVertex(const btVector3& vec)const +{ + btVector3 supVertex = localGetSupportingVertexWithoutMargin(vec); + + if ( getMargin()!=btScalar(0.) ) + { + btVector3 vecnorm = vec; + if (vecnorm .length2() < (SIMD_EPSILON*SIMD_EPSILON)) + { + vecnorm.setValue(btScalar(-1.),btScalar(-1.),btScalar(-1.)); + } + vecnorm.normalize(); + supVertex+= getMargin() * vecnorm; + } + return supVertex; +} + + + + + + + + + +//currently just for debugging (drawing), perhaps future support for algebraic continuous collision detection +//Please note that you can debug-draw btConvexHullShape with the Raytracer Demo +int btConvexHullShape::getNumVertices() const +{ + return m_unscaledPoints.size(); +} + +int btConvexHullShape::getNumEdges() const +{ + return m_unscaledPoints.size(); +} + +void btConvexHullShape::getEdge(int i,btVector3& pa,btVector3& pb) const +{ + + int index0 = i%m_unscaledPoints.size(); + int index1 = (i+1)%m_unscaledPoints.size(); + pa = getScaledPoint(index0); + pb = getScaledPoint(index1); +} + +void btConvexHullShape::getVertex(int i,btVector3& vtx) const +{ + vtx = getScaledPoint(i); +} + +int btConvexHullShape::getNumPlanes() const +{ + return 0; +} + +void btConvexHullShape::getPlane(btVector3& ,btVector3& ,int ) const +{ + + btAssert(0); +} + +//not yet +bool btConvexHullShape::isInside(const btVector3& ,btScalar ) const +{ + btAssert(0); + return false; +} + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexHullShape.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexHullShape.h new file mode 100644 index 000000000..2b0494d19 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexHullShape.h @@ -0,0 +1,96 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 CONVEX_HULL_SHAPE_H +#define CONVEX_HULL_SHAPE_H + +#include "btPolyhedralConvexShape.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" // for the types +#include "LinearMath/btAlignedObjectArray.h" + + +///The btConvexHullShape implements an implicit convex hull of an array of vertices. +///Bullet provides a general and fast collision detector for convex shapes based on GJK and EPA using localGetSupportingVertex. +ATTRIBUTE_ALIGNED16(class) btConvexHullShape : public btPolyhedralConvexAabbCachingShape +{ + btAlignedObjectArray m_unscaledPoints; + +public: + BT_DECLARE_ALIGNED_ALLOCATOR(); + + + ///this constructor optionally takes in a pointer to points. Each point is assumed to be 3 consecutive btScalar (x,y,z), the striding defines the number of bytes between each point, in memory. + ///It is easier to not pass any points in the constructor, and just add one point at a time, using addPoint. + ///btConvexHullShape make an internal copy of the points. + btConvexHullShape(const btScalar* points=0,int numPoints=0, int stride=sizeof(btVector3)); + + void addPoint(const btVector3& point); + + + btVector3* getUnscaledPoints() + { + return &m_unscaledPoints[0]; + } + + const btVector3* getUnscaledPoints() const + { + return &m_unscaledPoints[0]; + } + + ///getPoints is obsolete, please use getUnscaledPoints + const btVector3* getPoints() const + { + return getUnscaledPoints(); + } + + + + + SIMD_FORCE_INLINE btVector3 getScaledPoint(int i) const + { + return m_unscaledPoints[i] * m_localScaling; + } + + SIMD_FORCE_INLINE int getNumPoints() const + { + return m_unscaledPoints.size(); + } + + virtual btVector3 localGetSupportingVertex(const btVector3& vec)const; + virtual btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec)const; + virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const; + + + + //debugging + virtual const char* getName()const {return "Convex";} + + + virtual int getNumVertices() const; + virtual int getNumEdges() const; + virtual void getEdge(int i,btVector3& pa,btVector3& pb) const; + virtual void getVertex(int i,btVector3& vtx) const; + virtual int getNumPlanes() const; + virtual void getPlane(btVector3& planeNormal,btVector3& planeSupport,int i ) const; + virtual bool isInside(const btVector3& pt,btScalar tolerance) const; + + ///in case we receive negative scaling + virtual void setLocalScaling(const btVector3& scaling); + +}; + + +#endif //CONVEX_HULL_SHAPE_H + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexInternalShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexInternalShape.cpp new file mode 100644 index 000000000..083d60b1b --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexInternalShape.cpp @@ -0,0 +1,151 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 "btConvexInternalShape.h" + + + +btConvexInternalShape::btConvexInternalShape() +: m_localScaling(btScalar(1.),btScalar(1.),btScalar(1.)), +m_collisionMargin(CONVEX_DISTANCE_MARGIN) +{ +} + + +void btConvexInternalShape::setLocalScaling(const btVector3& scaling) +{ + m_localScaling = scaling.absolute(); +} + + + +void btConvexInternalShape::getAabbSlow(const btTransform& trans,btVector3&minAabb,btVector3&maxAabb) const +{ +#ifndef __SPU__ + //use localGetSupportingVertexWithoutMargin? + btScalar margin = getMargin(); + for (int i=0;i<3;i++) + { + btVector3 vec(btScalar(0.),btScalar(0.),btScalar(0.)); + vec[i] = btScalar(1.); + + btVector3 sv = localGetSupportingVertex(vec*trans.getBasis()); + + btVector3 tmp = trans(sv); + maxAabb[i] = tmp[i]+margin; + vec[i] = btScalar(-1.); + tmp = trans(localGetSupportingVertex(vec*trans.getBasis())); + minAabb[i] = tmp[i]-margin; + } +#endif +} + + + +btVector3 btConvexInternalShape::localGetSupportingVertex(const btVector3& vec)const +{ +#ifndef __SPU__ + + btVector3 supVertex = localGetSupportingVertexWithoutMargin(vec); + + if ( getMargin()!=btScalar(0.) ) + { + btVector3 vecnorm = vec; + if (vecnorm .length2() < (SIMD_EPSILON*SIMD_EPSILON)) + { + vecnorm.setValue(btScalar(-1.),btScalar(-1.),btScalar(-1.)); + } + vecnorm.normalize(); + supVertex+= getMargin() * vecnorm; + } + return supVertex; + +#else + btAssert(0); + return btVector3(0,0,0); +#endif //__SPU__ + + } + + +btConvexInternalAabbCachingShape::btConvexInternalAabbCachingShape() + : btConvexInternalShape(), +m_localAabbMin(1,1,1), +m_localAabbMax(-1,-1,-1), +m_isLocalAabbValid(false) +{ +} + + +void btConvexInternalAabbCachingShape::getAabb(const btTransform& trans,btVector3& aabbMin,btVector3& aabbMax) const +{ + getNonvirtualAabb(trans,aabbMin,aabbMax,getMargin()); +} + +void btConvexInternalAabbCachingShape::setLocalScaling(const btVector3& scaling) +{ + btConvexInternalShape::setLocalScaling(scaling); + recalcLocalAabb(); +} + + +void btConvexInternalAabbCachingShape::recalcLocalAabb() +{ + m_isLocalAabbValid = true; + + #if 1 + static const btVector3 _directions[] = + { + btVector3( 1., 0., 0.), + btVector3( 0., 1., 0.), + btVector3( 0., 0., 1.), + btVector3( -1., 0., 0.), + btVector3( 0., -1., 0.), + btVector3( 0., 0., -1.) + }; + + btVector3 _supporting[] = + { + btVector3( 0., 0., 0.), + btVector3( 0., 0., 0.), + btVector3( 0., 0., 0.), + btVector3( 0., 0., 0.), + btVector3( 0., 0., 0.), + btVector3( 0., 0., 0.) + }; + + batchedUnitVectorGetSupportingVertexWithoutMargin(_directions, _supporting, 6); + + for ( int i = 0; i < 3; ++i ) + { + m_localAabbMax[i] = _supporting[i][i] + m_collisionMargin; + m_localAabbMin[i] = _supporting[i + 3][i] - m_collisionMargin; + } + + #else + + for (int i=0;i<3;i++) + { + btVector3 vec(btScalar(0.),btScalar(0.),btScalar(0.)); + vec[i] = btScalar(1.); + btVector3 tmp = localGetSupportingVertex(vec); + m_localAabbMax[i] = tmp[i]+m_collisionMargin; + vec[i] = btScalar(-1.); + tmp = localGetSupportingVertex(vec); + m_localAabbMin[i] = tmp[i]-m_collisionMargin; + } + #endif +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexInternalShape.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexInternalShape.h new file mode 100644 index 000000000..3865d8e4b --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexInternalShape.h @@ -0,0 +1,151 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 BT_CONVEX_INTERNAL_SHAPE_H +#define BT_CONVEX_INTERNAL_SHAPE_H + +#include "btConvexShape.h" +#include "LinearMath/btAabbUtil2.h" + +///The btConvexInternalShape is an internal base class, shared by most convex shape implementations. +class btConvexInternalShape : public btConvexShape +{ + + protected: + + //local scaling. collisionMargin is not scaled ! + btVector3 m_localScaling; + + btVector3 m_implicitShapeDimensions; + + btScalar m_collisionMargin; + + btScalar m_padding; + + btConvexInternalShape(); + +public: + + + + virtual ~btConvexInternalShape() + { + + } + + virtual btVector3 localGetSupportingVertex(const btVector3& vec)const; + + const btVector3& getImplicitShapeDimensions() const + { + return m_implicitShapeDimensions; + } + + ///getAabb's default implementation is brute force, expected derived classes to implement a fast dedicated version + void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const + { + getAabbSlow(t,aabbMin,aabbMax); + } + + + + virtual void getAabbSlow(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const; + + + virtual void setLocalScaling(const btVector3& scaling); + virtual const btVector3& getLocalScaling() const + { + return m_localScaling; + } + + const btVector3& getLocalScalingNV() const + { + return m_localScaling; + } + + virtual void setMargin(btScalar margin) + { + m_collisionMargin = margin; + } + virtual btScalar getMargin() const + { + return m_collisionMargin; + } + + btScalar getMarginNV() const + { + return m_collisionMargin; + } + + virtual int getNumPreferredPenetrationDirections() const + { + return 0; + } + + virtual void getPreferredPenetrationDirection(int index, btVector3& penetrationVector) const + { + (void)penetrationVector; + (void)index; + btAssert(0); + } + + + +}; + + +///btConvexInternalAabbCachingShape adds local aabb caching for convex shapes, to avoid expensive bounding box calculations +class btConvexInternalAabbCachingShape : public btConvexInternalShape +{ + btVector3 m_localAabbMin; + btVector3 m_localAabbMax; + bool m_isLocalAabbValid; + +protected: + + btConvexInternalAabbCachingShape(); + + void setCachedLocalAabb (const btVector3& aabbMin, const btVector3& aabbMax) + { + m_isLocalAabbValid = true; + m_localAabbMin = aabbMin; + m_localAabbMax = aabbMax; + } + + inline void getCachedLocalAabb (btVector3& aabbMin, btVector3& aabbMax) const + { + btAssert(m_isLocalAabbValid); + aabbMin = m_localAabbMin; + aabbMax = m_localAabbMax; + } + + inline void getNonvirtualAabb(const btTransform& trans,btVector3& aabbMin,btVector3& aabbMax, btScalar margin) const + { + + //lazy evaluation of local aabb + btAssert(m_isLocalAabbValid); + btTransformAabb(m_localAabbMin,m_localAabbMax,margin,trans,aabbMin,aabbMax); + } + +public: + + virtual void setLocalScaling(const btVector3& scaling); + + virtual void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const; + + void recalcLocalAabb(); + +}; + +#endif //BT_CONVEX_INTERNAL_SHAPE_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexPointCloudShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexPointCloudShape.cpp new file mode 100644 index 000000000..c1b155aef --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexPointCloudShape.cpp @@ -0,0 +1,157 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 "btConvexPointCloudShape.h" +#include "BulletCollision/CollisionShapes/btCollisionMargin.h" + +#include "LinearMath/btQuaternion.h" + +void btConvexPointCloudShape::setLocalScaling(const btVector3& scaling) +{ + m_localScaling = scaling; + recalcLocalAabb(); +} + +#ifndef __SPU__ +btVector3 btConvexPointCloudShape::localGetSupportingVertexWithoutMargin(const btVector3& vec0)const +{ + btVector3 supVec(btScalar(0.),btScalar(0.),btScalar(0.)); + btScalar newDot,maxDot = btScalar(-BT_LARGE_FLOAT); + + btVector3 vec = vec0; + btScalar lenSqr = vec.length2(); + if (lenSqr < btScalar(0.0001)) + { + vec.setValue(1,0,0); + } else + { + btScalar rlen = btScalar(1.) / btSqrt(lenSqr ); + vec *= rlen; + } + + + for (int i=0;i maxDot) + { + maxDot = newDot; + supVec = vtx; + } + } + return supVec; +} + +void btConvexPointCloudShape::batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const +{ + btScalar newDot; + //use 'w' component of supportVerticesOut? + { + for (int i=0;i supportVerticesOut[j][3]) + { + //WARNING: don't swap next lines, the w component would get overwritten! + supportVerticesOut[j] = vtx; + supportVerticesOut[j][3] = newDot; + } + } + } + + + +} + + + +btVector3 btConvexPointCloudShape::localGetSupportingVertex(const btVector3& vec)const +{ + btVector3 supVertex = localGetSupportingVertexWithoutMargin(vec); + + if ( getMargin()!=btScalar(0.) ) + { + btVector3 vecnorm = vec; + if (vecnorm .length2() < (SIMD_EPSILON*SIMD_EPSILON)) + { + vecnorm.setValue(btScalar(-1.),btScalar(-1.),btScalar(-1.)); + } + vecnorm.normalize(); + supVertex+= getMargin() * vecnorm; + } + return supVertex; +} + + +#endif + + + + + + +//currently just for debugging (drawing), perhaps future support for algebraic continuous collision detection +//Please note that you can debug-draw btConvexHullShape with the Raytracer Demo +int btConvexPointCloudShape::getNumVertices() const +{ + return m_numPoints; +} + +int btConvexPointCloudShape::getNumEdges() const +{ + return 0; +} + +void btConvexPointCloudShape::getEdge(int i,btVector3& pa,btVector3& pb) const +{ + btAssert (0); +} + +void btConvexPointCloudShape::getVertex(int i,btVector3& vtx) const +{ + vtx = m_unscaledPoints[i]*m_localScaling; +} + +int btConvexPointCloudShape::getNumPlanes() const +{ + return 0; +} + +void btConvexPointCloudShape::getPlane(btVector3& ,btVector3& ,int ) const +{ + + btAssert(0); +} + +//not yet +bool btConvexPointCloudShape::isInside(const btVector3& ,btScalar ) const +{ + btAssert(0); + return false; +} + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexPointCloudShape.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexPointCloudShape.h new file mode 100644 index 000000000..54b5afac3 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexPointCloudShape.h @@ -0,0 +1,105 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 BT_CONVEX_POINT_CLOUD_SHAPE_H +#define BT_CONVEX_POINT_CLOUD_SHAPE_H + +#include "btPolyhedralConvexShape.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" // for the types +#include "LinearMath/btAlignedObjectArray.h" + +///The btConvexPointCloudShape implements an implicit convex hull of an array of vertices. +ATTRIBUTE_ALIGNED16(class) btConvexPointCloudShape : public btPolyhedralConvexAabbCachingShape +{ + btVector3* m_unscaledPoints; + int m_numPoints; + +public: + BT_DECLARE_ALIGNED_ALLOCATOR(); + + btConvexPointCloudShape() + { + m_localScaling.setValue(1.f,1.f,1.f); + m_shapeType = CONVEX_POINT_CLOUD_SHAPE_PROXYTYPE; + m_unscaledPoints = 0; + m_numPoints = 0; + } + + btConvexPointCloudShape(btVector3* points,int numPoints, const btVector3& localScaling,bool computeAabb = true) + { + m_localScaling = localScaling; + m_shapeType = CONVEX_POINT_CLOUD_SHAPE_PROXYTYPE; + m_unscaledPoints = points; + m_numPoints = numPoints; + + if (computeAabb) + recalcLocalAabb(); + } + + void setPoints (btVector3* points, int numPoints, bool computeAabb = true,const btVector3& localScaling=btVector3(1.f,1.f,1.f)) + { + m_unscaledPoints = points; + m_numPoints = numPoints; + m_localScaling = localScaling; + + if (computeAabb) + recalcLocalAabb(); + } + + SIMD_FORCE_INLINE btVector3* getUnscaledPoints() + { + return m_unscaledPoints; + } + + SIMD_FORCE_INLINE const btVector3* getUnscaledPoints() const + { + return m_unscaledPoints; + } + + SIMD_FORCE_INLINE int getNumPoints() const + { + return m_numPoints; + } + + SIMD_FORCE_INLINE btVector3 getScaledPoint( int index) const + { + return m_unscaledPoints[index] * m_localScaling; + } + +#ifndef __SPU__ + virtual btVector3 localGetSupportingVertex(const btVector3& vec)const; + virtual btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec)const; + virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const; +#endif + + + //debugging + virtual const char* getName()const {return "ConvexPointCloud";} + + virtual int getNumVertices() const; + virtual int getNumEdges() const; + virtual void getEdge(int i,btVector3& pa,btVector3& pb) const; + virtual void getVertex(int i,btVector3& vtx) const; + virtual int getNumPlanes() const; + virtual void getPlane(btVector3& planeNormal,btVector3& planeSupport,int i ) const; + virtual bool isInside(const btVector3& pt,btScalar tolerance) const; + + ///in case we receive negative scaling + virtual void setLocalScaling(const btVector3& scaling); +}; + + +#endif //BT_CONVEX_POINT_CLOUD_SHAPE_H + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexShape.cpp new file mode 100644 index 000000000..f5f3aa58a --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexShape.cpp @@ -0,0 +1,429 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 "btConvexShape.h" +#include "btTriangleShape.h" +#include "btSphereShape.h" +#include "btCylinderShape.h" +#include "btCapsuleShape.h" +#include "btConvexHullShape.h" +#include "btConvexPointCloudShape.h" + +///not supported on IBM SDK, until we fix the alignment of btVector3 +#if defined (__CELLOS_LV2__) && defined (__SPU__) +#include +static inline vec_float4 vec_dot3( vec_float4 vec0, vec_float4 vec1 ) +{ + vec_float4 result; + result = spu_mul( vec0, vec1 ); + result = spu_madd( spu_rlqwbyte( vec0, 4 ), spu_rlqwbyte( vec1, 4 ), result ); + return spu_madd( spu_rlqwbyte( vec0, 8 ), spu_rlqwbyte( vec1, 8 ), result ); +} +#endif //__SPU__ + +btConvexShape::btConvexShape () +{ +} + +btConvexShape::~btConvexShape() +{ + +} + + + +static btVector3 convexHullSupport (const btVector3& localDirOrg, const btVector3* points, int numPoints, const btVector3& localScaling) +{ + + btVector3 vec = localDirOrg * localScaling; + +#if defined (__CELLOS_LV2__) && defined (__SPU__) + + btVector3 localDir = vec; + + vec_float4 v_distMax = {-FLT_MAX,0,0,0}; + vec_int4 v_idxMax = {-999,0,0,0}; + int v=0; + int numverts = numPoints; + + for(;v<(int)numverts-4;v+=4) { + vec_float4 p0 = vec_dot3(points[v ].get128(),localDir.get128()); + vec_float4 p1 = vec_dot3(points[v+1].get128(),localDir.get128()); + vec_float4 p2 = vec_dot3(points[v+2].get128(),localDir.get128()); + vec_float4 p3 = vec_dot3(points[v+3].get128(),localDir.get128()); + const vec_int4 i0 = {v ,0,0,0}; + const vec_int4 i1 = {v+1,0,0,0}; + const vec_int4 i2 = {v+2,0,0,0}; + const vec_int4 i3 = {v+3,0,0,0}; + vec_uint4 retGt01 = spu_cmpgt(p0,p1); + vec_float4 pmax01 = spu_sel(p1,p0,retGt01); + vec_int4 imax01 = spu_sel(i1,i0,retGt01); + vec_uint4 retGt23 = spu_cmpgt(p2,p3); + vec_float4 pmax23 = spu_sel(p3,p2,retGt23); + vec_int4 imax23 = spu_sel(i3,i2,retGt23); + vec_uint4 retGt0123 = spu_cmpgt(pmax01,pmax23); + vec_float4 pmax0123 = spu_sel(pmax23,pmax01,retGt0123); + vec_int4 imax0123 = spu_sel(imax23,imax01,retGt0123); + vec_uint4 retGtMax = spu_cmpgt(v_distMax,pmax0123); + v_distMax = spu_sel(pmax0123,v_distMax,retGtMax); + v_idxMax = spu_sel(imax0123,v_idxMax,retGtMax); + } + for(;v<(int)numverts;v++) { + vec_float4 p = vec_dot3(points[v].get128(),localDir.get128()); + const vec_int4 i = {v,0,0,0}; + vec_uint4 retGtMax = spu_cmpgt(v_distMax,p); + v_distMax = spu_sel(p,v_distMax,retGtMax); + v_idxMax = spu_sel(i,v_idxMax,retGtMax); + } + int ptIndex = spu_extract(v_idxMax,0); + const btVector3& supVec= points[ptIndex] * localScaling; + return supVec; +#else + + btScalar newDot,maxDot = btScalar(-BT_LARGE_FLOAT); + int ptIndex = -1; + + for (int i=0;i maxDot) + { + maxDot = newDot; + ptIndex = i; + } + } + btAssert(ptIndex >= 0); + btVector3 supVec = points[ptIndex] * localScaling; + return supVec; +#endif //__SPU__ +} + +btVector3 btConvexShape::localGetSupportVertexWithoutMarginNonVirtual (const btVector3& localDir) const +{ + switch (m_shapeType) + { + case SPHERE_SHAPE_PROXYTYPE: + { + return btVector3(0,0,0); + } + case BOX_SHAPE_PROXYTYPE: + { + btBoxShape* convexShape = (btBoxShape*)this; + const btVector3& halfExtents = convexShape->getImplicitShapeDimensions(); + + return btVector3(btFsels(localDir.x(), halfExtents.x(), -halfExtents.x()), + btFsels(localDir.y(), halfExtents.y(), -halfExtents.y()), + btFsels(localDir.z(), halfExtents.z(), -halfExtents.z())); + } + case TRIANGLE_SHAPE_PROXYTYPE: + { + btTriangleShape* triangleShape = (btTriangleShape*)this; + btVector3 dir(localDir.getX(),localDir.getY(),localDir.getZ()); + btVector3* vertices = &triangleShape->m_vertices1[0]; + btVector3 dots(dir.dot(vertices[0]), dir.dot(vertices[1]), dir.dot(vertices[2])); + btVector3 sup = vertices[dots.maxAxis()]; + return btVector3(sup.getX(),sup.getY(),sup.getZ()); + } + case CYLINDER_SHAPE_PROXYTYPE: + { + btCylinderShape* cylShape = (btCylinderShape*)this; + //mapping of halfextents/dimension onto radius/height depends on how cylinder local orientation is (upAxis) + + btVector3 halfExtents = cylShape->getImplicitShapeDimensions(); + btVector3 v(localDir.getX(),localDir.getY(),localDir.getZ()); + int cylinderUpAxis = cylShape->getUpAxis(); + int XX(1),YY(0),ZZ(2); + + switch (cylinderUpAxis) + { + case 0: + { + XX = 1; + YY = 0; + ZZ = 2; + } + break; + case 1: + { + XX = 0; + YY = 1; + ZZ = 2; + } + break; + case 2: + { + XX = 0; + YY = 2; + ZZ = 1; + + } + break; + default: + btAssert(0); + break; + }; + + btScalar radius = halfExtents[XX]; + btScalar halfHeight = halfExtents[cylinderUpAxis]; + + btVector3 tmp; + btScalar d ; + + btScalar s = btSqrt(v[XX] * v[XX] + v[ZZ] * v[ZZ]); + if (s != btScalar(0.0)) + { + d = radius / s; + tmp[XX] = v[XX] * d; + tmp[YY] = v[YY] < 0.0 ? -halfHeight : halfHeight; + tmp[ZZ] = v[ZZ] * d; + return btVector3(tmp.getX(),tmp.getY(),tmp.getZ()); + } else { + tmp[XX] = radius; + tmp[YY] = v[YY] < 0.0 ? -halfHeight : halfHeight; + tmp[ZZ] = btScalar(0.0); + return btVector3(tmp.getX(),tmp.getY(),tmp.getZ()); + } + } + case CAPSULE_SHAPE_PROXYTYPE: + { + btVector3 vec0(localDir.getX(),localDir.getY(),localDir.getZ()); + + btCapsuleShape* capsuleShape = (btCapsuleShape*)this; + btScalar halfHeight = capsuleShape->getHalfHeight(); + int capsuleUpAxis = capsuleShape->getUpAxis(); + + btScalar radius = capsuleShape->getRadius(); + btVector3 supVec(0,0,0); + + btScalar maxDot(btScalar(-BT_LARGE_FLOAT)); + + btVector3 vec = vec0; + btScalar lenSqr = vec.length2(); + if (lenSqr < btScalar(0.0001)) + { + vec.setValue(1,0,0); + } else + { + btScalar rlen = btScalar(1.) / btSqrt(lenSqr ); + vec *= rlen; + } + btVector3 vtx; + btScalar newDot; + { + btVector3 pos(0,0,0); + pos[capsuleUpAxis] = halfHeight; + + //vtx = pos +vec*(radius); + vtx = pos +vec*capsuleShape->getLocalScalingNV()*(radius) - vec * capsuleShape->getMarginNV(); + newDot = vec.dot(vtx); + + + if (newDot > maxDot) + { + maxDot = newDot; + supVec = vtx; + } + } + { + btVector3 pos(0,0,0); + pos[capsuleUpAxis] = -halfHeight; + + //vtx = pos +vec*(radius); + vtx = pos +vec*capsuleShape->getLocalScalingNV()*(radius) - vec * capsuleShape->getMarginNV(); + newDot = vec.dot(vtx); + if (newDot > maxDot) + { + maxDot = newDot; + supVec = vtx; + } + } + return btVector3(supVec.getX(),supVec.getY(),supVec.getZ()); + } + case CONVEX_POINT_CLOUD_SHAPE_PROXYTYPE: + { + btConvexPointCloudShape* convexPointCloudShape = (btConvexPointCloudShape*)this; + btVector3* points = convexPointCloudShape->getUnscaledPoints (); + int numPoints = convexPointCloudShape->getNumPoints (); + return convexHullSupport (localDir, points, numPoints,convexPointCloudShape->getLocalScalingNV()); + } + case CONVEX_HULL_SHAPE_PROXYTYPE: + { + btConvexHullShape* convexHullShape = (btConvexHullShape*)this; + btVector3* points = convexHullShape->getUnscaledPoints(); + int numPoints = convexHullShape->getNumPoints (); + return convexHullSupport (localDir, points, numPoints,convexHullShape->getLocalScalingNV()); + } + default: +#ifndef __SPU__ + return this->localGetSupportingVertexWithoutMargin (localDir); +#else + btAssert (0); +#endif + } + + // should never reach here + btAssert (0); + return btVector3 (btScalar(0.0f), btScalar(0.0f), btScalar(0.0f)); +} + +btVector3 btConvexShape::localGetSupportVertexNonVirtual (const btVector3& localDir) const +{ + btVector3 localDirNorm = localDir; + if (localDirNorm .length2() < (SIMD_EPSILON*SIMD_EPSILON)) + { + localDirNorm.setValue(btScalar(-1.),btScalar(-1.),btScalar(-1.)); + } + localDirNorm.normalize (); + + return localGetSupportVertexWithoutMarginNonVirtual(localDirNorm)+ getMarginNonVirtual() * localDirNorm; +} + +/* TODO: This should be bumped up to btCollisionShape () */ +btScalar btConvexShape::getMarginNonVirtual () const +{ + switch (m_shapeType) + { + case SPHERE_SHAPE_PROXYTYPE: + { + btSphereShape* sphereShape = (btSphereShape*)this; + return sphereShape->getRadius (); + } + case BOX_SHAPE_PROXYTYPE: + { + btBoxShape* convexShape = (btBoxShape*)this; + return convexShape->getMarginNV (); + } + case TRIANGLE_SHAPE_PROXYTYPE: + { + btTriangleShape* triangleShape = (btTriangleShape*)this; + return triangleShape->getMarginNV (); + } + case CYLINDER_SHAPE_PROXYTYPE: + { + btCylinderShape* cylShape = (btCylinderShape*)this; + return cylShape->getMarginNV(); + } + case CAPSULE_SHAPE_PROXYTYPE: + { + btCapsuleShape* capsuleShape = (btCapsuleShape*)this; + return capsuleShape->getMarginNV(); + } + case CONVEX_POINT_CLOUD_SHAPE_PROXYTYPE: + /* fall through */ + case CONVEX_HULL_SHAPE_PROXYTYPE: + { + btPolyhedralConvexShape* convexHullShape = (btPolyhedralConvexShape*)this; + return convexHullShape->getMarginNV(); + } + default: +#ifndef __SPU__ + return this->getMargin (); +#else + btAssert (0); +#endif + } + + // should never reach here + btAssert (0); + return btScalar(0.0f); +} +#ifndef __SPU__ +void btConvexShape::getAabbNonVirtual (const btTransform& t, btVector3& aabbMin, btVector3& aabbMax) const +{ + switch (m_shapeType) + { + case SPHERE_SHAPE_PROXYTYPE: + { + btSphereShape* sphereShape = (btSphereShape*)this; + btScalar radius = sphereShape->getImplicitShapeDimensions().getX();// * convexShape->getLocalScaling().getX(); + btScalar margin = radius + sphereShape->getMarginNonVirtual(); + const btVector3& center = t.getOrigin(); + btVector3 extent(margin,margin,margin); + aabbMin = center - extent; + aabbMax = center + extent; + } + break; + case CYLINDER_SHAPE_PROXYTYPE: + /* fall through */ + case BOX_SHAPE_PROXYTYPE: + { + btBoxShape* convexShape = (btBoxShape*)this; + btScalar margin=convexShape->getMarginNonVirtual(); + btVector3 halfExtents = convexShape->getImplicitShapeDimensions(); + halfExtents += btVector3(margin,margin,margin); + btMatrix3x3 abs_b = t.getBasis().absolute(); + btVector3 center = t.getOrigin(); + btVector3 extent = btVector3(abs_b[0].dot(halfExtents),abs_b[1].dot(halfExtents),abs_b[2].dot(halfExtents)); + + aabbMin = center - extent; + aabbMax = center + extent; + break; + } + case TRIANGLE_SHAPE_PROXYTYPE: + { + btTriangleShape* triangleShape = (btTriangleShape*)this; + btScalar margin = triangleShape->getMarginNonVirtual(); + for (int i=0;i<3;i++) + { + btVector3 vec(btScalar(0.),btScalar(0.),btScalar(0.)); + vec[i] = btScalar(1.); + + btVector3 sv = localGetSupportVertexWithoutMarginNonVirtual(vec*t.getBasis()); + + btVector3 tmp = t(sv); + aabbMax[i] = tmp[i]+margin; + vec[i] = btScalar(-1.); + tmp = t(localGetSupportVertexWithoutMarginNonVirtual(vec*t.getBasis())); + aabbMin[i] = tmp[i]-margin; + } + } + break; + case CAPSULE_SHAPE_PROXYTYPE: + { + btCapsuleShape* capsuleShape = (btCapsuleShape*)this; + btVector3 halfExtents(capsuleShape->getRadius(),capsuleShape->getRadius(),capsuleShape->getRadius()); + int m_upAxis = capsuleShape->getUpAxis(); + halfExtents[m_upAxis] = capsuleShape->getRadius() + capsuleShape->getHalfHeight(); + halfExtents += btVector3(capsuleShape->getMarginNonVirtual(),capsuleShape->getMarginNonVirtual(),capsuleShape->getMarginNonVirtual()); + btMatrix3x3 abs_b = t.getBasis().absolute(); + btVector3 center = t.getOrigin(); + btVector3 extent = btVector3(abs_b[0].dot(halfExtents),abs_b[1].dot(halfExtents),abs_b[2].dot(halfExtents)); + aabbMin = center - extent; + aabbMax = center + extent; + } + break; + case CONVEX_POINT_CLOUD_SHAPE_PROXYTYPE: + case CONVEX_HULL_SHAPE_PROXYTYPE: + { + btPolyhedralConvexAabbCachingShape* convexHullShape = (btPolyhedralConvexAabbCachingShape*)this; + btScalar margin = convexHullShape->getMarginNonVirtual(); + convexHullShape->getNonvirtualAabb (t, aabbMin, aabbMax, margin); + } + break; + default: +#ifndef __SPU__ + this->getAabb (t, aabbMin, aabbMax); +#else + btAssert (0); +#endif + break; + } + + // should never reach here + btAssert (0); +} + +#endif //__SPU__ diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexShape.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexShape.h new file mode 100644 index 000000000..9c158259c --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexShape.h @@ -0,0 +1,82 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 CONVEX_SHAPE_INTERFACE1 +#define CONVEX_SHAPE_INTERFACE1 + +#include "btCollisionShape.h" + +#include "LinearMath/btVector3.h" +#include "LinearMath/btTransform.h" +#include "LinearMath/btMatrix3x3.h" +#include "btCollisionMargin.h" +#include "LinearMath/btAlignedAllocator.h" + +#define MAX_PREFERRED_PENETRATION_DIRECTIONS 10 + +/// The btConvexShape is an abstract shape interface, implemented by all convex shapes such as btBoxShape, btConvexHullShape etc. +/// It describes general convex shapes using the localGetSupportingVertex interface, used by collision detectors such as btGjkPairDetector. +ATTRIBUTE_ALIGNED16(class) btConvexShape : public btCollisionShape +{ + + +public: + + BT_DECLARE_ALIGNED_ALLOCATOR(); + + btConvexShape (); + + virtual ~btConvexShape(); + + virtual btVector3 localGetSupportingVertex(const btVector3& vec)const = 0; + + //////// + #ifndef __SPU__ + virtual btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec) const=0; + #endif //#ifndef __SPU__ + + btVector3 localGetSupportVertexWithoutMarginNonVirtual (const btVector3& vec) const; + btVector3 localGetSupportVertexNonVirtual (const btVector3& vec) const; + btScalar getMarginNonVirtual () const; + void getAabbNonVirtual (const btTransform& t, btVector3& aabbMin, btVector3& aabbMax) const; + + + //notice that the vectors should be unit length + virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const= 0; + + ///getAabb's default implementation is brute force, expected derived classes to implement a fast dedicated version + void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const =0; + + virtual void getAabbSlow(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const =0; + + virtual void setLocalScaling(const btVector3& scaling) =0; + virtual const btVector3& getLocalScaling() const =0; + + virtual void setMargin(btScalar margin)=0; + + virtual btScalar getMargin() const=0; + + virtual int getNumPreferredPenetrationDirections() const=0; + + virtual void getPreferredPenetrationDirection(int index, btVector3& penetrationVector) const=0; + + + + +}; + + + +#endif //CONVEX_SHAPE_INTERFACE1 diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexTriangleMeshShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexTriangleMeshShape.cpp new file mode 100644 index 000000000..0f9ced554 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexTriangleMeshShape.cpp @@ -0,0 +1,315 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 "btConvexTriangleMeshShape.h" +#include "BulletCollision/CollisionShapes/btCollisionMargin.h" + +#include "LinearMath/btQuaternion.h" +#include "BulletCollision/CollisionShapes/btStridingMeshInterface.h" + + +btConvexTriangleMeshShape ::btConvexTriangleMeshShape (btStridingMeshInterface* meshInterface, bool calcAabb) +: btPolyhedralConvexAabbCachingShape(), m_stridingMesh(meshInterface) +{ + m_shapeType = CONVEX_TRIANGLEMESH_SHAPE_PROXYTYPE; + if ( calcAabb ) + recalcLocalAabb(); +} + + + + +///It's not nice to have all this virtual function overhead, so perhaps we can also gather the points once +///but then we are duplicating +class LocalSupportVertexCallback: public btInternalTriangleIndexCallback +{ + + btVector3 m_supportVertexLocal; +public: + + btScalar m_maxDot; + btVector3 m_supportVecLocal; + + LocalSupportVertexCallback(const btVector3& supportVecLocal) + : m_supportVertexLocal(btScalar(0.),btScalar(0.),btScalar(0.)), + m_maxDot(btScalar(-BT_LARGE_FLOAT)), + m_supportVecLocal(supportVecLocal) + { + } + + virtual void internalProcessTriangleIndex(btVector3* triangle,int partId,int triangleIndex) + { + (void)triangleIndex; + (void)partId; + + for (int i=0;i<3;i++) + { + btScalar dot = m_supportVecLocal.dot(triangle[i]); + if (dot > m_maxDot) + { + m_maxDot = dot; + m_supportVertexLocal = triangle[i]; + } + } + } + + btVector3 GetSupportVertexLocal() + { + return m_supportVertexLocal; + } + +}; + + + + + +btVector3 btConvexTriangleMeshShape::localGetSupportingVertexWithoutMargin(const btVector3& vec0)const +{ + btVector3 supVec(btScalar(0.),btScalar(0.),btScalar(0.)); + + btVector3 vec = vec0; + btScalar lenSqr = vec.length2(); + if (lenSqr < btScalar(0.0001)) + { + vec.setValue(1,0,0); + } else + { + btScalar rlen = btScalar(1.) / btSqrt(lenSqr ); + vec *= rlen; + } + + LocalSupportVertexCallback supportCallback(vec); + btVector3 aabbMax(btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT)); + m_stridingMesh->InternalProcessAllTriangles(&supportCallback,-aabbMax,aabbMax); + supVec = supportCallback.GetSupportVertexLocal(); + + return supVec; +} + +void btConvexTriangleMeshShape::batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const +{ + //use 'w' component of supportVerticesOut? + { + for (int i=0;iInternalProcessAllTriangles(&supportCallback,-aabbMax,aabbMax); + supportVerticesOut[j] = supportCallback.GetSupportVertexLocal(); + } + +} + + + +btVector3 btConvexTriangleMeshShape::localGetSupportingVertex(const btVector3& vec)const +{ + btVector3 supVertex = localGetSupportingVertexWithoutMargin(vec); + + if ( getMargin()!=btScalar(0.) ) + { + btVector3 vecnorm = vec; + if (vecnorm .length2() < (SIMD_EPSILON*SIMD_EPSILON)) + { + vecnorm.setValue(btScalar(-1.),btScalar(-1.),btScalar(-1.)); + } + vecnorm.normalize(); + supVertex+= getMargin() * vecnorm; + } + return supVertex; +} + + + + + + + + + +//currently just for debugging (drawing), perhaps future support for algebraic continuous collision detection +//Please note that you can debug-draw btConvexTriangleMeshShape with the Raytracer Demo +int btConvexTriangleMeshShape::getNumVertices() const +{ + //cache this? + return 0; + +} + +int btConvexTriangleMeshShape::getNumEdges() const +{ + return 0; +} + +void btConvexTriangleMeshShape::getEdge(int ,btVector3& ,btVector3& ) const +{ + btAssert(0); +} + +void btConvexTriangleMeshShape::getVertex(int ,btVector3& ) const +{ + btAssert(0); +} + +int btConvexTriangleMeshShape::getNumPlanes() const +{ + return 0; +} + +void btConvexTriangleMeshShape::getPlane(btVector3& ,btVector3& ,int ) const +{ + btAssert(0); +} + +//not yet +bool btConvexTriangleMeshShape::isInside(const btVector3& ,btScalar ) const +{ + btAssert(0); + return false; +} + + + +void btConvexTriangleMeshShape::setLocalScaling(const btVector3& scaling) +{ + m_stridingMesh->setScaling(scaling); + + recalcLocalAabb(); + +} + + +const btVector3& btConvexTriangleMeshShape::getLocalScaling() const +{ + return m_stridingMesh->getScaling(); +} + +void btConvexTriangleMeshShape::calculatePrincipalAxisTransform(btTransform& principal, btVector3& inertia, btScalar& volume) const +{ + class CenterCallback: public btInternalTriangleIndexCallback + { + bool first; + btVector3 ref; + btVector3 sum; + btScalar volume; + + public: + + CenterCallback() : first(true), ref(0, 0, 0), sum(0, 0, 0), volume(0) + { + } + + virtual void internalProcessTriangleIndex(btVector3* triangle, int partId, int triangleIndex) + { + (void) triangleIndex; + (void) partId; + if (first) + { + ref = triangle[0]; + first = false; + } + else + { + btScalar vol = btFabs((triangle[0] - ref).triple(triangle[1] - ref, triangle[2] - ref)); + sum += (btScalar(0.25) * vol) * ((triangle[0] + triangle[1] + triangle[2] + ref)); + volume += vol; + } + } + + btVector3 getCenter() + { + return (volume > 0) ? sum / volume : ref; + } + + btScalar getVolume() + { + return volume * btScalar(1. / 6); + } + + }; + + class InertiaCallback: public btInternalTriangleIndexCallback + { + btMatrix3x3 sum; + btVector3 center; + + public: + + InertiaCallback(btVector3& center) : sum(0, 0, 0, 0, 0, 0, 0, 0, 0), center(center) + { + } + + virtual void internalProcessTriangleIndex(btVector3* triangle, int partId, int triangleIndex) + { + (void) triangleIndex; + (void) partId; + btMatrix3x3 i; + btVector3 a = triangle[0] - center; + btVector3 b = triangle[1] - center; + btVector3 c = triangle[2] - center; + btScalar volNeg = -btFabs(a.triple(b, c)) * btScalar(1. / 6); + for (int j = 0; j < 3; j++) + { + for (int k = 0; k <= j; k++) + { + i[j][k] = i[k][j] = volNeg * (btScalar(0.1) * (a[j] * a[k] + b[j] * b[k] + c[j] * c[k]) + + btScalar(0.05) * (a[j] * b[k] + a[k] * b[j] + a[j] * c[k] + a[k] * c[j] + b[j] * c[k] + b[k] * c[j])); + } + } + btScalar i00 = -i[0][0]; + btScalar i11 = -i[1][1]; + btScalar i22 = -i[2][2]; + i[0][0] = i11 + i22; + i[1][1] = i22 + i00; + i[2][2] = i00 + i11; + sum[0] += i[0]; + sum[1] += i[1]; + sum[2] += i[2]; + } + + btMatrix3x3& getInertia() + { + return sum; + } + + }; + + CenterCallback centerCallback; + btVector3 aabbMax(btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT)); + m_stridingMesh->InternalProcessAllTriangles(¢erCallback, -aabbMax, aabbMax); + btVector3 center = centerCallback.getCenter(); + principal.setOrigin(center); + volume = centerCallback.getVolume(); + + InertiaCallback inertiaCallback(center); + m_stridingMesh->InternalProcessAllTriangles(&inertiaCallback, -aabbMax, aabbMax); + + btMatrix3x3& i = inertiaCallback.getInertia(); + i.diagonalize(principal.getBasis(), btScalar(0.00001), 20); + inertia.setValue(i[0][0], i[1][1], i[2][2]); + inertia /= volume; +} + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexTriangleMeshShape.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexTriangleMeshShape.h new file mode 100644 index 000000000..f5167e74b --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexTriangleMeshShape.h @@ -0,0 +1,75 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 CONVEX_TRIANGLEMESH_SHAPE_H +#define CONVEX_TRIANGLEMESH_SHAPE_H + + +#include "btPolyhedralConvexShape.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" // for the types + + +/// The btConvexTriangleMeshShape is a convex hull of a triangle mesh, but the performance is not as good as btConvexHullShape. +/// A small benefit of this class is that it uses the btStridingMeshInterface, so you can avoid the duplication of the triangle mesh data. Nevertheless, most users should use the much better performing btConvexHullShape instead. +class btConvexTriangleMeshShape : public btPolyhedralConvexAabbCachingShape +{ + + class btStridingMeshInterface* m_stridingMesh; + +public: + btConvexTriangleMeshShape(btStridingMeshInterface* meshInterface, bool calcAabb = true); + + class btStridingMeshInterface* getMeshInterface() + { + return m_stridingMesh; + } + const class btStridingMeshInterface* getMeshInterface() const + { + return m_stridingMesh; + } + + virtual btVector3 localGetSupportingVertex(const btVector3& vec)const; + virtual btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec)const; + virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const; + + //debugging + virtual const char* getName()const {return "ConvexTrimesh";} + + virtual int getNumVertices() const; + virtual int getNumEdges() const; + virtual void getEdge(int i,btVector3& pa,btVector3& pb) const; + virtual void getVertex(int i,btVector3& vtx) const; + virtual int getNumPlanes() const; + virtual void getPlane(btVector3& planeNormal,btVector3& planeSupport,int i ) const; + virtual bool isInside(const btVector3& pt,btScalar tolerance) const; + + + virtual void setLocalScaling(const btVector3& scaling); + virtual const btVector3& getLocalScaling() const; + + ///computes the exact moment of inertia and the transform from the coordinate system defined by the principal axes of the moment of inertia + ///and the center of mass to the current coordinate system. A mass of 1 is assumed, for other masses just multiply the computed "inertia" + ///by the mass. The resulting transform "principal" has to be applied inversely to the mesh in order for the local coordinate system of the + ///shape to be centered at the center of mass and to coincide with the principal axes. This also necessitates a correction of the world transform + ///of the collision object by the principal transform. This method also computes the volume of the convex mesh. + void calculatePrincipalAxisTransform(btTransform& principal, btVector3& inertia, btScalar& volume) const; + +}; + + + +#endif //CONVEX_TRIANGLEMESH_SHAPE_H + + + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCylinderShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCylinderShape.cpp new file mode 100644 index 000000000..268809304 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCylinderShape.cpp @@ -0,0 +1,222 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 "btCylinderShape.h" + +btCylinderShape::btCylinderShape (const btVector3& halfExtents) +:btConvexInternalShape(), +m_upAxis(1) +{ + btVector3 margin(getMargin(),getMargin(),getMargin()); + m_implicitShapeDimensions = (halfExtents * m_localScaling) - margin; + m_shapeType = CYLINDER_SHAPE_PROXYTYPE; +} + + +btCylinderShapeX::btCylinderShapeX (const btVector3& halfExtents) +:btCylinderShape(halfExtents) +{ + m_upAxis = 0; + +} + + +btCylinderShapeZ::btCylinderShapeZ (const btVector3& halfExtents) +:btCylinderShape(halfExtents) +{ + m_upAxis = 2; + +} + +void btCylinderShape::getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const +{ + btTransformAabb(getHalfExtentsWithoutMargin(),getMargin(),t,aabbMin,aabbMax); +} + +void btCylinderShape::calculateLocalInertia(btScalar mass,btVector3& inertia) const +{ + //approximation of box shape, todo: implement cylinder shape inertia before people notice ;-) + btVector3 halfExtents = getHalfExtentsWithMargin(); + + btScalar lx=btScalar(2.)*(halfExtents.x()); + btScalar ly=btScalar(2.)*(halfExtents.y()); + btScalar lz=btScalar(2.)*(halfExtents.z()); + + inertia.setValue(mass/(btScalar(12.0)) * (ly*ly + lz*lz), + mass/(btScalar(12.0)) * (lx*lx + lz*lz), + mass/(btScalar(12.0)) * (lx*lx + ly*ly)); + +} + + +SIMD_FORCE_INLINE btVector3 CylinderLocalSupportX(const btVector3& halfExtents,const btVector3& v) +{ +const int cylinderUpAxis = 0; +const int XX = 1; +const int YY = 0; +const int ZZ = 2; + + //mapping depends on how cylinder local orientation is + // extents of the cylinder is: X,Y is for radius, and Z for height + + + btScalar radius = halfExtents[XX]; + btScalar halfHeight = halfExtents[cylinderUpAxis]; + + + btVector3 tmp; + btScalar d ; + + btScalar s = btSqrt(v[XX] * v[XX] + v[ZZ] * v[ZZ]); + if (s != btScalar(0.0)) + { + d = radius / s; + tmp[XX] = v[XX] * d; + tmp[YY] = v[YY] < 0.0 ? -halfHeight : halfHeight; + tmp[ZZ] = v[ZZ] * d; + return tmp; + } + else + { + tmp[XX] = radius; + tmp[YY] = v[YY] < 0.0 ? -halfHeight : halfHeight; + tmp[ZZ] = btScalar(0.0); + return tmp; + } + + +} + + + + + + +inline btVector3 CylinderLocalSupportY(const btVector3& halfExtents,const btVector3& v) +{ + +const int cylinderUpAxis = 1; +const int XX = 0; +const int YY = 1; +const int ZZ = 2; + + + btScalar radius = halfExtents[XX]; + btScalar halfHeight = halfExtents[cylinderUpAxis]; + + + btVector3 tmp; + btScalar d ; + + btScalar s = btSqrt(v[XX] * v[XX] + v[ZZ] * v[ZZ]); + if (s != btScalar(0.0)) + { + d = radius / s; + tmp[XX] = v[XX] * d; + tmp[YY] = v[YY] < 0.0 ? -halfHeight : halfHeight; + tmp[ZZ] = v[ZZ] * d; + return tmp; + } + else + { + tmp[XX] = radius; + tmp[YY] = v[YY] < 0.0 ? -halfHeight : halfHeight; + tmp[ZZ] = btScalar(0.0); + return tmp; + } + +} + +inline btVector3 CylinderLocalSupportZ(const btVector3& halfExtents,const btVector3& v) +{ +const int cylinderUpAxis = 2; +const int XX = 0; +const int YY = 2; +const int ZZ = 1; + + //mapping depends on how cylinder local orientation is + // extents of the cylinder is: X,Y is for radius, and Z for height + + + btScalar radius = halfExtents[XX]; + btScalar halfHeight = halfExtents[cylinderUpAxis]; + + + btVector3 tmp; + btScalar d ; + + btScalar s = btSqrt(v[XX] * v[XX] + v[ZZ] * v[ZZ]); + if (s != btScalar(0.0)) + { + d = radius / s; + tmp[XX] = v[XX] * d; + tmp[YY] = v[YY] < 0.0 ? -halfHeight : halfHeight; + tmp[ZZ] = v[ZZ] * d; + return tmp; + } + else + { + tmp[XX] = radius; + tmp[YY] = v[YY] < 0.0 ? -halfHeight : halfHeight; + tmp[ZZ] = btScalar(0.0); + return tmp; + } + + +} + +btVector3 btCylinderShapeX::localGetSupportingVertexWithoutMargin(const btVector3& vec)const +{ + return CylinderLocalSupportX(getHalfExtentsWithoutMargin(),vec); +} + + +btVector3 btCylinderShapeZ::localGetSupportingVertexWithoutMargin(const btVector3& vec)const +{ + return CylinderLocalSupportZ(getHalfExtentsWithoutMargin(),vec); +} +btVector3 btCylinderShape::localGetSupportingVertexWithoutMargin(const btVector3& vec)const +{ + return CylinderLocalSupportY(getHalfExtentsWithoutMargin(),vec); +} + +void btCylinderShape::batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const +{ + for (int i=0;i 1 && "bad width"); + btAssert(heightStickLength > 1 && "bad length"); + btAssert(heightfieldData && "null heightfield data"); + // btAssert(heightScale) -- do we care? Trust caller here + btAssert(minHeight <= maxHeight && "bad min/max height"); + btAssert(upAxis >= 0 && upAxis < 3 && + "bad upAxis--should be in range [0,2]"); + btAssert(hdt != PHY_UCHAR || hdt != PHY_FLOAT || hdt != PHY_SHORT && + "Bad height data type enum"); + + // initialize member variables + m_shapeType = TERRAIN_SHAPE_PROXYTYPE; + m_heightStickWidth = heightStickWidth; + m_heightStickLength = heightStickLength; + m_minHeight = minHeight; + m_maxHeight = maxHeight; + m_width = (btScalar) (heightStickWidth - 1); + m_length = (btScalar) (heightStickLength - 1); + m_heightScale = heightScale; + m_heightfieldDataUnknown = heightfieldData; + m_heightDataType = hdt; + m_flipQuadEdges = flipQuadEdges; + m_useDiamondSubdivision = false; + m_upAxis = upAxis; + m_localScaling.setValue(btScalar(1.), btScalar(1.), btScalar(1.)); + + // determine min/max axis-aligned bounding box (aabb) values + switch (m_upAxis) + { + case 0: + { + m_localAabbMin.setValue(m_minHeight, 0, 0); + m_localAabbMax.setValue(m_maxHeight, m_width, m_length); + break; + } + case 1: + { + m_localAabbMin.setValue(0, m_minHeight, 0); + m_localAabbMax.setValue(m_width, m_maxHeight, m_length); + break; + }; + case 2: + { + m_localAabbMin.setValue(0, 0, m_minHeight); + m_localAabbMax.setValue(m_width, m_length, m_maxHeight); + break; + } + default: + { + //need to get valid m_upAxis + btAssert(0 && "Bad m_upAxis"); + } + } + + // remember origin (defined as exact middle of aabb) + m_localOrigin = btScalar(0.5) * (m_localAabbMin + m_localAabbMax); +} + + + +btHeightfieldTerrainShape::~btHeightfieldTerrainShape() +{ +} + + + +void btHeightfieldTerrainShape::getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const +{ + btVector3 halfExtents = (m_localAabbMax-m_localAabbMin)* m_localScaling * btScalar(0.5); + + btVector3 localOrigin(0, 0, 0); + localOrigin[m_upAxis] = (m_minHeight + m_maxHeight) * btScalar(0.5); + localOrigin *= m_localScaling; + + btMatrix3x3 abs_b = t.getBasis().absolute(); + btVector3 center = t.getOrigin(); + btVector3 extent = btVector3(abs_b[0].dot(halfExtents), + abs_b[1].dot(halfExtents), + abs_b[2].dot(halfExtents)); + extent += btVector3(getMargin(),getMargin(),getMargin()); + + aabbMin = center - extent; + aabbMax = center + extent; +} + + +/// This returns the "raw" (user's initial) height, not the actual height. +/// The actual height needs to be adjusted to be relative to the center +/// of the heightfield's AABB. +btScalar +btHeightfieldTerrainShape::getRawHeightFieldValue(int x,int y) const +{ + btScalar val = 0.f; + switch (m_heightDataType) + { + case PHY_FLOAT: + { + val = m_heightfieldDataFloat[(y*m_heightStickWidth)+x]; + break; + } + + case PHY_UCHAR: + { + unsigned char heightFieldValue = m_heightfieldDataUnsignedChar[(y*m_heightStickWidth)+x]; + val = heightFieldValue * m_heightScale; + break; + } + + case PHY_SHORT: + { + short hfValue = m_heightfieldDataShort[(y * m_heightStickWidth) + x]; + val = hfValue * m_heightScale; + break; + } + + default: + { + btAssert(!"Bad m_heightDataType"); + } + } + + return val; +} + + + + +/// this returns the vertex in bullet-local coordinates +void btHeightfieldTerrainShape::getVertex(int x,int y,btVector3& vertex) const +{ + btAssert(x>=0); + btAssert(y>=0); + btAssert(xstartX) + startX = quantizedAabbMin[1]; + if (quantizedAabbMax[1]startJ) + startJ = quantizedAabbMin[2]; + if (quantizedAabbMax[2]startX) + startX = quantizedAabbMin[0]; + if (quantizedAabbMax[0]startJ) + startJ = quantizedAabbMin[2]; + if (quantizedAabbMax[2]startX) + startX = quantizedAabbMin[0]; + if (quantizedAabbMax[0]startJ) + startJ = quantizedAabbMin[1]; + if (quantizedAabbMax[1]processTriangle(vertices,x,j); + //second triangle + getVertex(x,j,vertices[0]); + getVertex(x+1,j+1,vertices[1]); + getVertex(x,j+1,vertices[2]); + callback->processTriangle(vertices,x,j); + } else + { + //first triangle + getVertex(x,j,vertices[0]); + getVertex(x,j+1,vertices[1]); + getVertex(x+1,j,vertices[2]); + callback->processTriangle(vertices,x,j); + //second triangle + getVertex(x+1,j,vertices[0]); + getVertex(x,j+1,vertices[1]); + getVertex(x+1,j+1,vertices[2]); + callback->processTriangle(vertices,x,j); + } + } + } + + + +} + +void btHeightfieldTerrainShape::calculateLocalInertia(btScalar ,btVector3& inertia) const +{ + //moving concave objects not supported + + inertia.setValue(btScalar(0.),btScalar(0.),btScalar(0.)); +} + +void btHeightfieldTerrainShape::setLocalScaling(const btVector3& scaling) +{ + m_localScaling = scaling; +} +const btVector3& btHeightfieldTerrainShape::getLocalScaling() const +{ + return m_localScaling; +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h new file mode 100644 index 000000000..4f5d1e35b --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h @@ -0,0 +1,161 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 HEIGHTFIELD_TERRAIN_SHAPE_H +#define HEIGHTFIELD_TERRAIN_SHAPE_H + +#include "btConcaveShape.h" + +///btHeightfieldTerrainShape simulates a 2D heightfield terrain +/** + The caller is responsible for maintaining the heightfield array; this + class does not make a copy. + + The heightfield can be dynamic so long as the min/max height values + capture the extremes (heights must always be in that range). + + The local origin of the heightfield is assumed to be the exact + center (as determined by width and length and height, with each + axis multiplied by the localScaling). + + \b NOTE: be careful with coordinates. If you have a heightfield with a local + min height of -100m, and a max height of +500m, you may be tempted to place it + at the origin (0,0) and expect the heights in world coordinates to be + -100 to +500 meters. + Actually, the heights will be -300 to +300m, because bullet will re-center + the heightfield based on its AABB (which is determined by the min/max + heights). So keep in mind that once you create a btHeightfieldTerrainShape + object, the heights will be adjusted relative to the center of the AABB. This + is different to the behavior of many rendering engines, but is useful for + physics engines. + + Most (but not all) rendering and heightfield libraries assume upAxis = 1 + (that is, the y-axis is "up"). This class allows any of the 3 coordinates + to be "up". Make sure your choice of axis is consistent with your rendering + system. + + The heightfield heights are determined from the data type used for the + heightfieldData array. + + - PHY_UCHAR: height at a point is the uchar value at the + grid point, multipled by heightScale. uchar isn't recommended + because of its inability to deal with negative values, and + low resolution (8-bit). + + - PHY_SHORT: height at a point is the short int value at that grid + point, multipled by heightScale. + + - PHY_FLOAT: height at a point is the float value at that grid + point. heightScale is ignored when using the float heightfield + data type. + + Whatever the caller specifies as minHeight and maxHeight will be honored. + The class will not inspect the heightfield to discover the actual minimum + or maximum heights. These values are used to determine the heightfield's + axis-aligned bounding box, multiplied by localScaling. + + For usage and testing see the TerrainDemo. + */ +class btHeightfieldTerrainShape : public btConcaveShape +{ +protected: + btVector3 m_localAabbMin; + btVector3 m_localAabbMax; + btVector3 m_localOrigin; + + ///terrain data + int m_heightStickWidth; + int m_heightStickLength; + btScalar m_minHeight; + btScalar m_maxHeight; + btScalar m_width; + btScalar m_length; + btScalar m_heightScale; + union + { + unsigned char* m_heightfieldDataUnsignedChar; + short* m_heightfieldDataShort; + btScalar* m_heightfieldDataFloat; + void* m_heightfieldDataUnknown; + }; + + PHY_ScalarType m_heightDataType; + bool m_flipQuadEdges; + bool m_useDiamondSubdivision; + + int m_upAxis; + + btVector3 m_localScaling; + + virtual btScalar getRawHeightFieldValue(int x,int y) const; + void quantizeWithClamp(int* out, const btVector3& point,int isMax) const; + void getVertex(int x,int y,btVector3& vertex) const; + + + + /// protected initialization + /** + Handles the work of constructors so that public constructors can be + backwards-compatible without a lot of copy/paste. + */ + void initialize(int heightStickWidth, int heightStickLength, + void* heightfieldData, btScalar heightScale, + btScalar minHeight, btScalar maxHeight, int upAxis, + PHY_ScalarType heightDataType, bool flipQuadEdges); + +public: + /// preferred constructor + /** + This constructor supports a range of heightfield + data types, and allows for a non-zero minimum height value. + heightScale is needed for any integer-based heightfield data types. + */ + btHeightfieldTerrainShape(int heightStickWidth,int heightStickLength, + void* heightfieldData, btScalar heightScale, + btScalar minHeight, btScalar maxHeight, + int upAxis, PHY_ScalarType heightDataType, + bool flipQuadEdges); + + /// legacy constructor + /** + The legacy constructor assumes the heightfield has a minimum height + of zero. Only unsigned char or floats are supported. For legacy + compatibility reasons, heightScale is calculated as maxHeight / 65535 + (and is only used when useFloatData = false). + */ + btHeightfieldTerrainShape(int heightStickWidth,int heightStickLength,void* heightfieldData, btScalar maxHeight,int upAxis,bool useFloatData,bool flipQuadEdges); + + virtual ~btHeightfieldTerrainShape(); + + + void setUseDiamondSubdivision(bool useDiamondSubdivision=true) { m_useDiamondSubdivision = useDiamondSubdivision;} + + + virtual void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const; + + virtual void processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const; + + virtual void calculateLocalInertia(btScalar mass,btVector3& inertia) const; + + virtual void setLocalScaling(const btVector3& scaling); + + virtual const btVector3& getLocalScaling() const; + + //debugging + virtual const char* getName()const {return "HEIGHTFIELD";} + +}; + +#endif //HEIGHTFIELD_TERRAIN_SHAPE_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btMaterial.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btMaterial.h new file mode 100644 index 000000000..3f2c892a4 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btMaterial.h @@ -0,0 +1,34 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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. +*/ + +/// This file was created by Alex Silverman + +#ifndef MATERIAL_H +#define MATERIAL_H + +// Material class to be used by btMultimaterialTriangleMeshShape to store triangle properties +class btMaterial +{ + // public members so that materials can change due to world events +public: + btScalar m_friction; + btScalar m_restitution; + int pad[2]; + + btMaterial(){} + btMaterial(btScalar fric, btScalar rest) { m_friction = fric; m_restitution = rest; } +}; + +#endif // MATERIAL_H \ No newline at end of file diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btMinkowskiSumShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btMinkowskiSumShape.cpp new file mode 100644 index 000000000..06707e24e --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btMinkowskiSumShape.cpp @@ -0,0 +1,60 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 "btMinkowskiSumShape.h" + + +btMinkowskiSumShape::btMinkowskiSumShape(const btConvexShape* shapeA,const btConvexShape* shapeB) +: btConvexInternalShape (), +m_shapeA(shapeA), +m_shapeB(shapeB) +{ + m_shapeType = MINKOWSKI_DIFFERENCE_SHAPE_PROXYTYPE; + m_transA.setIdentity(); + m_transB.setIdentity(); +} + +btVector3 btMinkowskiSumShape::localGetSupportingVertexWithoutMargin(const btVector3& vec)const +{ + btVector3 supVertexA = m_transA(m_shapeA->localGetSupportingVertexWithoutMargin(vec*m_transA.getBasis())); + btVector3 supVertexB = m_transB(m_shapeB->localGetSupportingVertexWithoutMargin(-vec*m_transB.getBasis())); + return supVertexA - supVertexB; +} + +void btMinkowskiSumShape::batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const +{ + ///@todo: could make recursive use of batching. probably this shape is not used frequently. + for (int i=0;igetMargin() + m_shapeB->getMargin(); +} + + +void btMinkowskiSumShape::calculateLocalInertia(btScalar mass,btVector3& inertia) const +{ + (void)mass; + btAssert(0); + inertia.setValue(0,0,0); +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btMinkowskiSumShape.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btMinkowskiSumShape.h new file mode 100644 index 000000000..d6fd04021 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btMinkowskiSumShape.h @@ -0,0 +1,60 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 MINKOWSKI_SUM_SHAPE_H +#define MINKOWSKI_SUM_SHAPE_H + +#include "btConvexInternalShape.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" // for the types + +/// The btMinkowskiSumShape is only for advanced users. This shape represents implicit based minkowski sum of two convex implicit shapes. +class btMinkowskiSumShape : public btConvexInternalShape +{ + + btTransform m_transA; + btTransform m_transB; + const btConvexShape* m_shapeA; + const btConvexShape* m_shapeB; + +public: + + btMinkowskiSumShape(const btConvexShape* shapeA,const btConvexShape* shapeB); + + virtual btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec)const; + + virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const; + + + virtual void calculateLocalInertia(btScalar mass,btVector3& inertia) const; + + void setTransformA(const btTransform& transA) { m_transA = transA;} + void setTransformB(const btTransform& transB) { m_transB = transB;} + + const btTransform& getTransformA()const { return m_transA;} + const btTransform& GetTransformB()const { return m_transB;} + + + virtual btScalar getMargin() const; + + const btConvexShape* getShapeA() const { return m_shapeA;} + const btConvexShape* getShapeB() const { return m_shapeB;} + + virtual const char* getName()const + { + return "MinkowskiSum"; + } +}; + +#endif //MINKOWSKI_SUM_SHAPE_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btMultiSphereShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btMultiSphereShape.cpp new file mode 100644 index 000000000..f130d30fa --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btMultiSphereShape.cpp @@ -0,0 +1,140 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 "btMultiSphereShape.h" +#include "BulletCollision/CollisionShapes/btCollisionMargin.h" +#include "LinearMath/btQuaternion.h" + +btMultiSphereShape::btMultiSphereShape (const btVector3* positions,const btScalar* radi,int numSpheres) +:btConvexInternalAabbCachingShape () +{ + m_shapeType = MULTI_SPHERE_SHAPE_PROXYTYPE; + //btScalar startMargin = btScalar(BT_LARGE_FLOAT); + + m_localPositionArray.resize(numSpheres); + m_radiArray.resize(numSpheres); + for (int i=0;i maxDot) + { + maxDot = newDot; + supVec = vtx; + } + } + + return supVec; + +} + + void btMultiSphereShape::batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const +{ + + for (int j=0;j maxDot) + { + maxDot = newDot; + supportVerticesOut[j] = vtx; + } + } + } +} + + + + + + + + +void btMultiSphereShape::calculateLocalInertia(btScalar mass,btVector3& inertia) const +{ + //as an approximation, take the inertia of the box that bounds the spheres + + btVector3 localAabbMin,localAabbMax; + getCachedLocalAabb(localAabbMin,localAabbMax); + btVector3 halfExtents = (localAabbMax-localAabbMin)*btScalar(0.5); + + btScalar lx=btScalar(2.)*(halfExtents.x()); + btScalar ly=btScalar(2.)*(halfExtents.y()); + btScalar lz=btScalar(2.)*(halfExtents.z()); + + inertia.setValue(mass/(btScalar(12.0)) * (ly*ly + lz*lz), + mass/(btScalar(12.0)) * (lx*lx + lz*lz), + mass/(btScalar(12.0)) * (lx*lx + ly*ly)); + +} + + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btMultiSphereShape.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btMultiSphereShape.h new file mode 100644 index 000000000..f1c4cbf34 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btMultiSphereShape.h @@ -0,0 +1,68 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 MULTI_SPHERE_MINKOWSKI_H +#define MULTI_SPHERE_MINKOWSKI_H + +#include "btConvexInternalShape.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" // for the types +#include "LinearMath/btAlignedObjectArray.h" +#include "LinearMath/btAabbUtil2.h" + +///The btMultiSphereShape represents the convex hull of a collection of spheres. You can create special capsules or other smooth volumes. +///It is possible to animate the spheres for deformation, but call 'recalcLocalAabb' after changing any sphere position/radius +class btMultiSphereShape : public btConvexInternalAabbCachingShape +{ + + btAlignedObjectArray m_localPositionArray; + btAlignedObjectArray m_radiArray; + +public: + btMultiSphereShape (const btVector3* positions,const btScalar* radi,int numSpheres); + + ///CollisionShape Interface + virtual void calculateLocalInertia(btScalar mass,btVector3& inertia) const; + + /// btConvexShape Interface + virtual btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec)const; + + virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const; + + int getSphereCount() const + { + return m_localPositionArray.size(); + } + + const btVector3& getSpherePosition(int index) const + { + return m_localPositionArray[index]; + } + + btScalar getSphereRadius(int index) const + { + return m_radiArray[index]; + } + + + virtual const char* getName()const + { + return "MultiSphere"; + } + + +}; + + +#endif //MULTI_SPHERE_MINKOWSKI_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btMultimaterialTriangleMeshShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btMultimaterialTriangleMeshShape.cpp new file mode 100644 index 000000000..58799ac96 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btMultimaterialTriangleMeshShape.cpp @@ -0,0 +1,45 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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. +*/ + +/// This file was created by Alex Silverman + +#include "BulletCollision/CollisionShapes/btMultimaterialTriangleMeshShape.h" +#include "BulletCollision/CollisionShapes/btTriangleIndexVertexMaterialArray.h" +//#include "BulletCollision/CollisionShapes/btOptimizedBvh.h" + + +///Obtains the material for a specific triangle +const btMaterial * btMultimaterialTriangleMeshShape::getMaterialProperties(int partID, int triIndex) +{ + const unsigned char * materialBase = 0; + int numMaterials; + PHY_ScalarType materialType; + int materialStride; + const unsigned char * triangleMaterialBase = 0; + int numTriangles; + int triangleMaterialStride; + PHY_ScalarType triangleType; + + ((btTriangleIndexVertexMaterialArray*)m_meshInterface)->getLockedReadOnlyMaterialBase(&materialBase, numMaterials, materialType, materialStride, + &triangleMaterialBase, numTriangles, triangleMaterialStride, triangleType, partID); + + // return the pointer to the place with the friction for the triangle + // TODO: This depends on whether it's a moving mesh or not + // BUG IN GIMPACT + //return (btScalar*)(&materialBase[triangleMaterialBase[(triIndex-1) * triangleMaterialStride] * materialStride]); + int * matInd = (int *)(&(triangleMaterialBase[(triIndex * triangleMaterialStride)])); + btMaterial *matVal = (btMaterial *)(&(materialBase[*matInd * materialStride])); + return (matVal); +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btMultimaterialTriangleMeshShape.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btMultimaterialTriangleMeshShape.h new file mode 100644 index 000000000..231c58549 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btMultimaterialTriangleMeshShape.h @@ -0,0 +1,123 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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. +*/ + +/// This file was created by Alex Silverman + +#ifndef BVH_TRIANGLE_MATERIAL_MESH_SHAPE_H +#define BVH_TRIANGLE_MATERIAL_MESH_SHAPE_H + +#include "btBvhTriangleMeshShape.h" +#include "btMaterial.h" + +///The BvhTriangleMaterialMeshShape extends the btBvhTriangleMeshShape. Its main contribution is the interface into a material array, which allows per-triangle friction and restitution. +ATTRIBUTE_ALIGNED16(class) btMultimaterialTriangleMeshShape : public btBvhTriangleMeshShape +{ + btAlignedObjectArray m_materialList; + int ** m_triangleMaterials; + +public: + + BT_DECLARE_ALIGNED_ALLOCATOR(); + + btMultimaterialTriangleMeshShape(): btBvhTriangleMeshShape() {m_shapeType = MULTIMATERIAL_TRIANGLE_MESH_PROXYTYPE;} + btMultimaterialTriangleMeshShape(btStridingMeshInterface* meshInterface, bool useQuantizedAabbCompression, bool buildBvh = true): + btBvhTriangleMeshShape(meshInterface, useQuantizedAabbCompression, buildBvh) + { + m_shapeType = MULTIMATERIAL_TRIANGLE_MESH_PROXYTYPE; + + btVector3 m_triangle[3]; + const unsigned char *vertexbase; + int numverts; + PHY_ScalarType type; + int stride; + const unsigned char *indexbase; + int indexstride; + int numfaces; + PHY_ScalarType indicestype; + + //m_materialLookup = (int**)(btAlignedAlloc(sizeof(int*) * meshInterface->getNumSubParts(), 16)); + + for(int i = 0; i < meshInterface->getNumSubParts(); i++) + { + m_meshInterface->getLockedReadOnlyVertexIndexBase( + &vertexbase, + numverts, + type, + stride, + &indexbase, + indexstride, + numfaces, + indicestype, + i); + //m_materialLookup[i] = (int*)(btAlignedAlloc(sizeof(int) * numfaces, 16)); + } + } + + ///optionally pass in a larger bvh aabb, used for quantization. This allows for deformations within this aabb + btMultimaterialTriangleMeshShape(btStridingMeshInterface* meshInterface, bool useQuantizedAabbCompression,const btVector3& bvhAabbMin,const btVector3& bvhAabbMax, bool buildBvh = true): + btBvhTriangleMeshShape(meshInterface, useQuantizedAabbCompression, bvhAabbMin, bvhAabbMax, buildBvh) + { + m_shapeType = MULTIMATERIAL_TRIANGLE_MESH_PROXYTYPE; + + btVector3 m_triangle[3]; + const unsigned char *vertexbase; + int numverts; + PHY_ScalarType type; + int stride; + const unsigned char *indexbase; + int indexstride; + int numfaces; + PHY_ScalarType indicestype; + + //m_materialLookup = (int**)(btAlignedAlloc(sizeof(int*) * meshInterface->getNumSubParts(), 16)); + + for(int i = 0; i < meshInterface->getNumSubParts(); i++) + { + m_meshInterface->getLockedReadOnlyVertexIndexBase( + &vertexbase, + numverts, + type, + stride, + &indexbase, + indexstride, + numfaces, + indicestype, + i); + //m_materialLookup[i] = (int*)(btAlignedAlloc(sizeof(int) * numfaces * 2, 16)); + } + } + + virtual ~btMultimaterialTriangleMeshShape() + { +/* + for(int i = 0; i < m_meshInterface->getNumSubParts(); i++) + { + btAlignedFree(m_materialValues[i]); + m_materialLookup[i] = NULL; + } + btAlignedFree(m_materialValues); + m_materialLookup = NULL; +*/ + } + //debugging + virtual const char* getName()const {return "MULTIMATERIALTRIANGLEMESH";} + + ///Obtains the material for a specific triangle + const btMaterial * getMaterialProperties(int partID, int triIndex); + +} +; + +#endif //BVH_TRIANGLE_MATERIAL_MESH_SHAPE_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btOptimizedBvh.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btOptimizedBvh.cpp new file mode 100644 index 000000000..981b8a265 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btOptimizedBvh.cpp @@ -0,0 +1,391 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 "btOptimizedBvh.h" +#include "btStridingMeshInterface.h" +#include "LinearMath/btAabbUtil2.h" +#include "LinearMath/btIDebugDraw.h" + + +btOptimizedBvh::btOptimizedBvh() +{ +} + +btOptimizedBvh::~btOptimizedBvh() +{ +} + + +void btOptimizedBvh::build(btStridingMeshInterface* triangles, bool useQuantizedAabbCompression, const btVector3& bvhAabbMin, const btVector3& bvhAabbMax) +{ + m_useQuantization = useQuantizedAabbCompression; + + + // NodeArray triangleNodes; + + struct NodeTriangleCallback : public btInternalTriangleIndexCallback + { + + NodeArray& m_triangleNodes; + + NodeTriangleCallback& operator=(NodeTriangleCallback& other) + { + m_triangleNodes = other.m_triangleNodes; + return *this; + } + + NodeTriangleCallback(NodeArray& triangleNodes) + :m_triangleNodes(triangleNodes) + { + } + + virtual void internalProcessTriangleIndex(btVector3* triangle,int partId,int triangleIndex) + { + btOptimizedBvhNode node; + btVector3 aabbMin,aabbMax; + aabbMin.setValue(btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT)); + aabbMax.setValue(btScalar(-BT_LARGE_FLOAT),btScalar(-BT_LARGE_FLOAT),btScalar(-BT_LARGE_FLOAT)); + aabbMin.setMin(triangle[0]); + aabbMax.setMax(triangle[0]); + aabbMin.setMin(triangle[1]); + aabbMax.setMax(triangle[1]); + aabbMin.setMin(triangle[2]); + aabbMax.setMax(triangle[2]); + + //with quantization? + node.m_aabbMinOrg = aabbMin; + node.m_aabbMaxOrg = aabbMax; + + node.m_escapeIndex = -1; + + //for child nodes + node.m_subPart = partId; + node.m_triangleIndex = triangleIndex; + m_triangleNodes.push_back(node); + } + }; + struct QuantizedNodeTriangleCallback : public btInternalTriangleIndexCallback + { + QuantizedNodeArray& m_triangleNodes; + const btQuantizedBvh* m_optimizedTree; // for quantization + + QuantizedNodeTriangleCallback& operator=(QuantizedNodeTriangleCallback& other) + { + m_triangleNodes = other.m_triangleNodes; + m_optimizedTree = other.m_optimizedTree; + return *this; + } + + QuantizedNodeTriangleCallback(QuantizedNodeArray& triangleNodes,const btQuantizedBvh* tree) + :m_triangleNodes(triangleNodes),m_optimizedTree(tree) + { + } + + virtual void internalProcessTriangleIndex(btVector3* triangle,int partId,int triangleIndex) + { + // The partId and triangle index must fit in the same (positive) integer + btAssert(partId < (1<=0); + + btQuantizedBvhNode node; + btVector3 aabbMin,aabbMax; + aabbMin.setValue(btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT)); + aabbMax.setValue(btScalar(-BT_LARGE_FLOAT),btScalar(-BT_LARGE_FLOAT),btScalar(-BT_LARGE_FLOAT)); + aabbMin.setMin(triangle[0]); + aabbMax.setMax(triangle[0]); + aabbMin.setMin(triangle[1]); + aabbMax.setMax(triangle[1]); + aabbMin.setMin(triangle[2]); + aabbMax.setMax(triangle[2]); + + //PCK: add these checks for zero dimensions of aabb + const btScalar MIN_AABB_DIMENSION = btScalar(0.002); + const btScalar MIN_AABB_HALF_DIMENSION = btScalar(0.001); + if (aabbMax.x() - aabbMin.x() < MIN_AABB_DIMENSION) + { + aabbMax.setX(aabbMax.x() + MIN_AABB_HALF_DIMENSION); + aabbMin.setX(aabbMin.x() - MIN_AABB_HALF_DIMENSION); + } + if (aabbMax.y() - aabbMin.y() < MIN_AABB_DIMENSION) + { + aabbMax.setY(aabbMax.y() + MIN_AABB_HALF_DIMENSION); + aabbMin.setY(aabbMin.y() - MIN_AABB_HALF_DIMENSION); + } + if (aabbMax.z() - aabbMin.z() < MIN_AABB_DIMENSION) + { + aabbMax.setZ(aabbMax.z() + MIN_AABB_HALF_DIMENSION); + aabbMin.setZ(aabbMin.z() - MIN_AABB_HALF_DIMENSION); + } + + m_optimizedTree->quantize(&node.m_quantizedAabbMin[0],aabbMin,0); + m_optimizedTree->quantize(&node.m_quantizedAabbMax[0],aabbMax,1); + + node.m_escapeIndexOrTriangleIndex = (partId<<(31-MAX_NUM_PARTS_IN_BITS)) | triangleIndex; + + m_triangleNodes.push_back(node); + } + }; + + + + int numLeafNodes = 0; + + + if (m_useQuantization) + { + + //initialize quantization values + setQuantizationValues(bvhAabbMin,bvhAabbMax); + + QuantizedNodeTriangleCallback callback(m_quantizedLeafNodes,this); + + + triangles->InternalProcessAllTriangles(&callback,m_bvhAabbMin,m_bvhAabbMax); + + //now we have an array of leafnodes in m_leafNodes + numLeafNodes = m_quantizedLeafNodes.size(); + + + m_quantizedContiguousNodes.resize(2*numLeafNodes); + + + } else + { + NodeTriangleCallback callback(m_leafNodes); + + btVector3 aabbMin(btScalar(-BT_LARGE_FLOAT),btScalar(-BT_LARGE_FLOAT),btScalar(-BT_LARGE_FLOAT)); + btVector3 aabbMax(btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT)); + + triangles->InternalProcessAllTriangles(&callback,aabbMin,aabbMax); + + //now we have an array of leafnodes in m_leafNodes + numLeafNodes = m_leafNodes.size(); + + m_contiguousNodes.resize(2*numLeafNodes); + } + + m_curNodeIndex = 0; + + buildTree(0,numLeafNodes); + + ///if the entire tree is small then subtree size, we need to create a header info for the tree + if(m_useQuantization && !m_SubtreeHeaders.size()) + { + btBvhSubtreeInfo& subtree = m_SubtreeHeaders.expand(); + subtree.setAabbFromQuantizeNode(m_quantizedContiguousNodes[0]); + subtree.m_rootNodeIndex = 0; + subtree.m_subtreeSize = m_quantizedContiguousNodes[0].isLeafNode() ? 1 : m_quantizedContiguousNodes[0].getEscapeIndex(); + } + + //PCK: update the copy of the size + m_subtreeHeaderCount = m_SubtreeHeaders.size(); + + //PCK: clear m_quantizedLeafNodes and m_leafNodes, they are temporary + m_quantizedLeafNodes.clear(); + m_leafNodes.clear(); +} + + + + +void btOptimizedBvh::refit(btStridingMeshInterface* meshInterface,const btVector3& aabbMin,const btVector3& aabbMax) +{ + if (m_useQuantization) + { + + setQuantizationValues(aabbMin,aabbMax); + + updateBvhNodes(meshInterface,0,m_curNodeIndex,0); + + ///now update all subtree headers + + int i; + for (i=0;i m_bvhAabbMin.getX()); + btAssert(aabbMin.getY() > m_bvhAabbMin.getY()); + btAssert(aabbMin.getZ() > m_bvhAabbMin.getZ()); + + btAssert(aabbMax.getX() < m_bvhAabbMax.getX()); + btAssert(aabbMax.getY() < m_bvhAabbMax.getY()); + btAssert(aabbMax.getZ() < m_bvhAabbMax.getZ()); + + ///we should update all quantization values, using updateBvhNodes(meshInterface); + ///but we only update chunks that overlap the given aabb + + unsigned short quantizedQueryAabbMin[3]; + unsigned short quantizedQueryAabbMax[3]; + + quantize(&quantizedQueryAabbMin[0],aabbMin,0); + quantize(&quantizedQueryAabbMax[0],aabbMax,1); + + int i; + for (i=0;im_SubtreeHeaders.size();i++) + { + btBvhSubtreeInfo& subtree = m_SubtreeHeaders[i]; + + //PCK: unsigned instead of bool + unsigned overlap = testQuantizedAabbAgainstQuantizedAabb(quantizedQueryAabbMin,quantizedQueryAabbMax,subtree.m_quantizedAabbMin,subtree.m_quantizedAabbMax); + if (overlap != 0) + { + updateBvhNodes(meshInterface,subtree.m_rootNodeIndex,subtree.m_rootNodeIndex+subtree.m_subtreeSize,i); + + subtree.setAabbFromQuantizeNode(m_quantizedContiguousNodes[subtree.m_rootNodeIndex]); + } + } + +} + +void btOptimizedBvh::updateBvhNodes(btStridingMeshInterface* meshInterface,int firstNode,int endNode,int index) +{ + (void)index; + + btAssert(m_useQuantization); + + int curNodeSubPart=-1; + + //get access info to trianglemesh data + const unsigned char *vertexbase = 0; + int numverts = 0; + PHY_ScalarType type = PHY_INTEGER; + int stride = 0; + const unsigned char *indexbase = 0; + int indexstride = 0; + int numfaces = 0; + PHY_ScalarType indicestype = PHY_INTEGER; + + btVector3 triangleVerts[3]; + btVector3 aabbMin,aabbMax; + const btVector3& meshScaling = meshInterface->getScaling(); + + int i; + for (i=endNode-1;i>=firstNode;i--) + { + + + btQuantizedBvhNode& curNode = m_quantizedContiguousNodes[i]; + if (curNode.isLeafNode()) + { + //recalc aabb from triangle data + int nodeSubPart = curNode.getPartId(); + int nodeTriangleIndex = curNode.getTriangleIndex(); + if (nodeSubPart != curNodeSubPart) + { + if (curNodeSubPart >= 0) + meshInterface->unLockReadOnlyVertexBase(curNodeSubPart); + meshInterface->getLockedReadOnlyVertexIndexBase(&vertexbase,numverts, type,stride,&indexbase,indexstride,numfaces,indicestype,nodeSubPart); + + curNodeSubPart = nodeSubPart; + btAssert(indicestype==PHY_INTEGER||indicestype==PHY_SHORT); + } + //triangles->getLockedReadOnlyVertexIndexBase(vertexBase,numVerts, + + unsigned int* gfxbase = (unsigned int*)(indexbase+nodeTriangleIndex*indexstride); + + + for (int j=2;j>=0;j--) + { + + int graphicsindex = indicestype==PHY_SHORT?((unsigned short*)gfxbase)[j]:gfxbase[j]; + if (type == PHY_FLOAT) + { + float* graphicsbase = (float*)(vertexbase+graphicsindex*stride); + triangleVerts[j] = btVector3( + graphicsbase[0]*meshScaling.getX(), + graphicsbase[1]*meshScaling.getY(), + graphicsbase[2]*meshScaling.getZ()); + } + else + { + double* graphicsbase = (double*)(vertexbase+graphicsindex*stride); + triangleVerts[j] = btVector3( btScalar(graphicsbase[0]*meshScaling.getX()), btScalar(graphicsbase[1]*meshScaling.getY()), btScalar(graphicsbase[2]*meshScaling.getZ())); + } + } + + + + aabbMin.setValue(btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT)); + aabbMax.setValue(btScalar(-BT_LARGE_FLOAT),btScalar(-BT_LARGE_FLOAT),btScalar(-BT_LARGE_FLOAT)); + aabbMin.setMin(triangleVerts[0]); + aabbMax.setMax(triangleVerts[0]); + aabbMin.setMin(triangleVerts[1]); + aabbMax.setMax(triangleVerts[1]); + aabbMin.setMin(triangleVerts[2]); + aabbMax.setMax(triangleVerts[2]); + + quantize(&curNode.m_quantizedAabbMin[0],aabbMin,0); + quantize(&curNode.m_quantizedAabbMax[0],aabbMax,1); + + } else + { + //combine aabb from both children + + btQuantizedBvhNode* leftChildNode = &m_quantizedContiguousNodes[i+1]; + + btQuantizedBvhNode* rightChildNode = leftChildNode->isLeafNode() ? &m_quantizedContiguousNodes[i+2] : + &m_quantizedContiguousNodes[i+1+leftChildNode->getEscapeIndex()]; + + + { + for (int i=0;i<3;i++) + { + curNode.m_quantizedAabbMin[i] = leftChildNode->m_quantizedAabbMin[i]; + if (curNode.m_quantizedAabbMin[i]>rightChildNode->m_quantizedAabbMin[i]) + curNode.m_quantizedAabbMin[i]=rightChildNode->m_quantizedAabbMin[i]; + + curNode.m_quantizedAabbMax[i] = leftChildNode->m_quantizedAabbMax[i]; + if (curNode.m_quantizedAabbMax[i] < rightChildNode->m_quantizedAabbMax[i]) + curNode.m_quantizedAabbMax[i] = rightChildNode->m_quantizedAabbMax[i]; + } + } + } + + } + + if (curNodeSubPart >= 0) + meshInterface->unLockReadOnlyVertexBase(curNodeSubPart); + + +} + +///deSerializeInPlace loads and initializes a BVH from a buffer in memory 'in place' +btOptimizedBvh* btOptimizedBvh::deSerializeInPlace(void *i_alignedDataBuffer, unsigned int i_dataBufferSize, bool i_swapEndian) +{ + btQuantizedBvh* bvh = btQuantizedBvh::deSerializeInPlace(i_alignedDataBuffer,i_dataBufferSize,i_swapEndian); + + //we don't add additional data so just do a static upcast + return static_cast(bvh); +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btOptimizedBvh.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btOptimizedBvh.h new file mode 100644 index 000000000..8f3a29f7e --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btOptimizedBvh.h @@ -0,0 +1,65 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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. +*/ + +///Contains contributions from Disney Studio's + +#ifndef OPTIMIZED_BVH_H +#define OPTIMIZED_BVH_H + +#include "BulletCollision/BroadphaseCollision/btQuantizedBvh.h" + +class btStridingMeshInterface; + + +///The btOptimizedBvh extends the btQuantizedBvh to create AABB tree for triangle meshes, through the btStridingMeshInterface. +ATTRIBUTE_ALIGNED16(class) btOptimizedBvh : public btQuantizedBvh +{ + +public: + BT_DECLARE_ALIGNED_ALLOCATOR(); + +protected: + +public: + + btOptimizedBvh(); + + virtual ~btOptimizedBvh(); + + void build(btStridingMeshInterface* triangles,bool useQuantizedAabbCompression, const btVector3& bvhAabbMin, const btVector3& bvhAabbMax); + + void refit(btStridingMeshInterface* triangles,const btVector3& aabbMin,const btVector3& aabbMax); + + void refitPartial(btStridingMeshInterface* triangles,const btVector3& aabbMin, const btVector3& aabbMax); + + void updateBvhNodes(btStridingMeshInterface* meshInterface,int firstNode,int endNode,int index); + + /// Data buffer MUST be 16 byte aligned + virtual bool serialize(void *o_alignedDataBuffer, unsigned i_dataBufferSize, bool i_swapEndian) + { + return btQuantizedBvh::serialize(o_alignedDataBuffer,i_dataBufferSize,i_swapEndian); + + } + + ///deSerializeInPlace loads and initializes a BVH from a buffer in memory 'in place' + static btOptimizedBvh *deSerializeInPlace(void *i_alignedDataBuffer, unsigned int i_dataBufferSize, bool i_swapEndian); + + +}; + + +#endif //OPTIMIZED_BVH_H + + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp new file mode 100644 index 000000000..b1ecb3e43 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp @@ -0,0 +1,193 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 "BulletCollision/CollisionShapes/btPolyhedralConvexShape.h" + +btPolyhedralConvexShape::btPolyhedralConvexShape() :btConvexInternalShape() +{ + +} + + +btVector3 btPolyhedralConvexShape::localGetSupportingVertexWithoutMargin(const btVector3& vec0)const +{ + + + btVector3 supVec(0,0,0); +#ifndef __SPU__ + int i; + btScalar maxDot(btScalar(-BT_LARGE_FLOAT)); + + btVector3 vec = vec0; + btScalar lenSqr = vec.length2(); + if (lenSqr < btScalar(0.0001)) + { + vec.setValue(1,0,0); + } else + { + btScalar rlen = btScalar(1.) / btSqrt(lenSqr ); + vec *= rlen; + } + + btVector3 vtx; + btScalar newDot; + + for (i=0;i maxDot) + { + maxDot = newDot; + supVec = vtx; + } + } + + +#endif //__SPU__ + return supVec; +} + + + +void btPolyhedralConvexShape::batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const +{ +#ifndef __SPU__ + int i; + + btVector3 vtx; + btScalar newDot; + + for (i=0;i supportVerticesOut[j][3]) + { + //WARNING: don't swap next lines, the w component would get overwritten! + supportVerticesOut[j] = vtx; + supportVerticesOut[j][3] = newDot; + } + } + } +#endif //__SPU__ +} + + + +void btPolyhedralConvexShape::calculateLocalInertia(btScalar mass,btVector3& inertia) const +{ +#ifndef __SPU__ + //not yet, return box inertia + + btScalar margin = getMargin(); + + btTransform ident; + ident.setIdentity(); + btVector3 aabbMin,aabbMax; + getAabb(ident,aabbMin,aabbMax); + btVector3 halfExtents = (aabbMax-aabbMin)*btScalar(0.5); + + btScalar lx=btScalar(2.)*(halfExtents.x()+margin); + btScalar ly=btScalar(2.)*(halfExtents.y()+margin); + btScalar lz=btScalar(2.)*(halfExtents.z()+margin); + const btScalar x2 = lx*lx; + const btScalar y2 = ly*ly; + const btScalar z2 = lz*lz; + const btScalar scaledmass = mass * btScalar(0.08333333); + + inertia = scaledmass * (btVector3(y2+z2,x2+z2,x2+y2)); +#endif //__SPU__ +} + + + +void btPolyhedralConvexAabbCachingShape::setLocalScaling(const btVector3& scaling) +{ + btConvexInternalShape::setLocalScaling(scaling); + recalcLocalAabb(); +} + +btPolyhedralConvexAabbCachingShape::btPolyhedralConvexAabbCachingShape() +:btPolyhedralConvexShape(), +m_localAabbMin(1,1,1), +m_localAabbMax(-1,-1,-1), +m_isLocalAabbValid(false) +{ +} + +void btPolyhedralConvexAabbCachingShape::getAabb(const btTransform& trans,btVector3& aabbMin,btVector3& aabbMax) const +{ + getNonvirtualAabb(trans,aabbMin,aabbMax,getMargin()); +} + +void btPolyhedralConvexAabbCachingShape::recalcLocalAabb() +{ + m_isLocalAabbValid = true; + + #if 1 + static const btVector3 _directions[] = + { + btVector3( 1., 0., 0.), + btVector3( 0., 1., 0.), + btVector3( 0., 0., 1.), + btVector3( -1., 0., 0.), + btVector3( 0., -1., 0.), + btVector3( 0., 0., -1.) + }; + + btVector3 _supporting[] = + { + btVector3( 0., 0., 0.), + btVector3( 0., 0., 0.), + btVector3( 0., 0., 0.), + btVector3( 0., 0., 0.), + btVector3( 0., 0., 0.), + btVector3( 0., 0., 0.) + }; + + batchedUnitVectorGetSupportingVertexWithoutMargin(_directions, _supporting, 6); + + for ( int i = 0; i < 3; ++i ) + { + m_localAabbMax[i] = _supporting[i][i] + m_collisionMargin; + m_localAabbMin[i] = _supporting[i + 3][i] - m_collisionMargin; + } + + #else + + for (int i=0;i<3;i++) + { + btVector3 vec(btScalar(0.),btScalar(0.),btScalar(0.)); + vec[i] = btScalar(1.); + btVector3 tmp = localGetSupportingVertex(vec); + m_localAabbMax[i] = tmp[i]+m_collisionMargin; + vec[i] = btScalar(-1.); + tmp = localGetSupportingVertex(vec); + m_localAabbMin[i] = tmp[i]-m_collisionMargin; + } + #endif +} + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.h new file mode 100644 index 000000000..2c691b956 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.h @@ -0,0 +1,98 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 BU_SHAPE +#define BU_SHAPE + +#include "LinearMath/btMatrix3x3.h" +#include "btConvexInternalShape.h" + + +///The btPolyhedralConvexShape is an internal interface class for polyhedral convex shapes. +class btPolyhedralConvexShape : public btConvexInternalShape +{ + +protected: + +public: + + btPolyhedralConvexShape(); + + //brute force implementations + + virtual btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec)const; + virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const; + + virtual void calculateLocalInertia(btScalar mass,btVector3& inertia) const; + + + virtual int getNumVertices() const = 0 ; + virtual int getNumEdges() const = 0; + virtual void getEdge(int i,btVector3& pa,btVector3& pb) const = 0; + virtual void getVertex(int i,btVector3& vtx) const = 0; + virtual int getNumPlanes() const = 0; + virtual void getPlane(btVector3& planeNormal,btVector3& planeSupport,int i ) const = 0; +// virtual int getIndex(int i) const = 0 ; + + virtual bool isInside(const btVector3& pt,btScalar tolerance) const = 0; + +}; + + +///The btPolyhedralConvexAabbCachingShape adds aabb caching to the btPolyhedralConvexShape +class btPolyhedralConvexAabbCachingShape : public btPolyhedralConvexShape +{ + + btVector3 m_localAabbMin; + btVector3 m_localAabbMax; + bool m_isLocalAabbValid; + +protected: + + void setCachedLocalAabb (const btVector3& aabbMin, const btVector3& aabbMax) + { + m_isLocalAabbValid = true; + m_localAabbMin = aabbMin; + m_localAabbMax = aabbMax; + } + + inline void getCachedLocalAabb (btVector3& aabbMin, btVector3& aabbMax) const + { + btAssert(m_isLocalAabbValid); + aabbMin = m_localAabbMin; + aabbMax = m_localAabbMax; + } + +public: + + btPolyhedralConvexAabbCachingShape(); + + inline void getNonvirtualAabb(const btTransform& trans,btVector3& aabbMin,btVector3& aabbMax, btScalar margin) const + { + + //lazy evaluation of local aabb + btAssert(m_isLocalAabbValid); + btTransformAabb(m_localAabbMin,m_localAabbMax,margin,trans,aabbMin,aabbMax); + } + + virtual void setLocalScaling(const btVector3& scaling); + + virtual void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const; + + void recalcLocalAabb(); + +}; + +#endif //BU_SHAPE diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.cpp new file mode 100644 index 000000000..d964e1e48 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.cpp @@ -0,0 +1,121 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 "btScaledBvhTriangleMeshShape.h" + +btScaledBvhTriangleMeshShape::btScaledBvhTriangleMeshShape(btBvhTriangleMeshShape* childShape,const btVector3& localScaling) +:m_localScaling(localScaling),m_bvhTriMeshShape(childShape) +{ + m_shapeType = SCALED_TRIANGLE_MESH_SHAPE_PROXYTYPE; +} + +btScaledBvhTriangleMeshShape::~btScaledBvhTriangleMeshShape() +{ +} + + +class btScaledTriangleCallback : public btTriangleCallback +{ + btTriangleCallback* m_originalCallback; + + btVector3 m_localScaling; + +public: + + btScaledTriangleCallback(btTriangleCallback* originalCallback,const btVector3& localScaling) + :m_originalCallback(originalCallback), + m_localScaling(localScaling) + { + } + + virtual void processTriangle(btVector3* triangle, int partId, int triangleIndex) + { + btVector3 newTriangle[3]; + newTriangle[0] = triangle[0]*m_localScaling; + newTriangle[1] = triangle[1]*m_localScaling; + newTriangle[2] = triangle[2]*m_localScaling; + m_originalCallback->processTriangle(&newTriangle[0],partId,triangleIndex); + } +}; + +void btScaledBvhTriangleMeshShape::processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const +{ + btScaledTriangleCallback scaledCallback(callback,m_localScaling); + + btVector3 invLocalScaling(1.f/m_localScaling.getX(),1.f/m_localScaling.getY(),1.f/m_localScaling.getZ()); + btVector3 scaledAabbMin,scaledAabbMax; + + ///support negative scaling + scaledAabbMin[0] = m_localScaling.getX() >= 0. ? aabbMin[0] * invLocalScaling[0] : aabbMax[0] * invLocalScaling[0]; + scaledAabbMin[1] = m_localScaling.getY() >= 0. ? aabbMin[1] * invLocalScaling[1] : aabbMax[1] * invLocalScaling[1]; + scaledAabbMin[2] = m_localScaling.getZ() >= 0. ? aabbMin[2] * invLocalScaling[2] : aabbMax[2] * invLocalScaling[2]; + + scaledAabbMax[0] = m_localScaling.getX() <= 0. ? aabbMin[0] * invLocalScaling[0] : aabbMax[0] * invLocalScaling[0]; + scaledAabbMax[1] = m_localScaling.getY() <= 0. ? aabbMin[1] * invLocalScaling[1] : aabbMax[1] * invLocalScaling[1]; + scaledAabbMax[2] = m_localScaling.getZ() <= 0. ? aabbMin[2] * invLocalScaling[2] : aabbMax[2] * invLocalScaling[2]; + + + m_bvhTriMeshShape->processAllTriangles(&scaledCallback,scaledAabbMin,scaledAabbMax); +} + + +void btScaledBvhTriangleMeshShape::getAabb(const btTransform& trans,btVector3& aabbMin,btVector3& aabbMax) const +{ + btVector3 localAabbMin = m_bvhTriMeshShape->getLocalAabbMin(); + btVector3 localAabbMax = m_bvhTriMeshShape->getLocalAabbMax(); + + btVector3 tmpLocalAabbMin = localAabbMin * m_localScaling; + btVector3 tmpLocalAabbMax = localAabbMax * m_localScaling; + + localAabbMin[0] = (m_localScaling.getX() >= 0.) ? tmpLocalAabbMin[0] : tmpLocalAabbMax[0]; + localAabbMin[1] = (m_localScaling.getY() >= 0.) ? tmpLocalAabbMin[1] : tmpLocalAabbMax[1]; + localAabbMin[2] = (m_localScaling.getZ() >= 0.) ? tmpLocalAabbMin[2] : tmpLocalAabbMax[2]; + localAabbMax[0] = (m_localScaling.getX() <= 0.) ? tmpLocalAabbMin[0] : tmpLocalAabbMax[0]; + localAabbMax[1] = (m_localScaling.getY() <= 0.) ? tmpLocalAabbMin[1] : tmpLocalAabbMax[1]; + localAabbMax[2] = (m_localScaling.getZ() <= 0.) ? tmpLocalAabbMin[2] : tmpLocalAabbMax[2]; + + btVector3 localHalfExtents = btScalar(0.5)*(localAabbMax-localAabbMin); + btScalar margin = m_bvhTriMeshShape->getMargin(); + localHalfExtents += btVector3(margin,margin,margin); + btVector3 localCenter = btScalar(0.5)*(localAabbMax+localAabbMin); + + btMatrix3x3 abs_b = trans.getBasis().absolute(); + + btVector3 center = trans(localCenter); + + btVector3 extent = btVector3(abs_b[0].dot(localHalfExtents), + abs_b[1].dot(localHalfExtents), + abs_b[2].dot(localHalfExtents)); + aabbMin = center - extent; + aabbMax = center + extent; + +} + +void btScaledBvhTriangleMeshShape::setLocalScaling(const btVector3& scaling) +{ + m_localScaling = scaling; +} + +const btVector3& btScaledBvhTriangleMeshShape::getLocalScaling() const +{ + return m_localScaling; +} + +void btScaledBvhTriangleMeshShape::calculateLocalInertia(btScalar mass,btVector3& inertia) const +{ + ///don't make this a movable object! +// btAssert(0); +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.h new file mode 100644 index 000000000..d720b1b4f --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.h @@ -0,0 +1,62 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 SCALED_BVH_TRIANGLE_MESH_SHAPE_H +#define SCALED_BVH_TRIANGLE_MESH_SHAPE_H + +#include "BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h" + + +///The btScaledBvhTriangleMeshShape allows to instance a scaled version of an existing btBvhTriangleMeshShape. +///Note that each btBvhTriangleMeshShape still can have its own local scaling, independent from this btScaledBvhTriangleMeshShape 'localScaling' +ATTRIBUTE_ALIGNED16(class) btScaledBvhTriangleMeshShape : public btConcaveShape +{ + + + btVector3 m_localScaling; + + btBvhTriangleMeshShape* m_bvhTriMeshShape; + +public: + + + btScaledBvhTriangleMeshShape(btBvhTriangleMeshShape* childShape,const btVector3& localScaling); + + virtual ~btScaledBvhTriangleMeshShape(); + + + virtual void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const; + virtual void setLocalScaling(const btVector3& scaling); + virtual const btVector3& getLocalScaling() const; + virtual void calculateLocalInertia(btScalar mass,btVector3& inertia) const; + + virtual void processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const; + + btBvhTriangleMeshShape* getChildShape() + { + return m_bvhTriMeshShape; + } + + const btBvhTriangleMeshShape* getChildShape() const + { + return m_bvhTriMeshShape; + } + + //debugging + virtual const char* getName()const {return "SCALEDBVHTRIANGLEMESH";} + +}; + +#endif //BVH_TRIANGLE_MESH_SHAPE_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btShapeHull.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btShapeHull.cpp new file mode 100644 index 000000000..061054444 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btShapeHull.cpp @@ -0,0 +1,165 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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. +*/ + +//btShapeHull was implemented by John McCutchan. + + +#include "btShapeHull.h" +#include "LinearMath/btConvexHull.h" + +#define NUM_UNITSPHERE_POINTS 42 + +static btVector3 btUnitSpherePoints[NUM_UNITSPHERE_POINTS+MAX_PREFERRED_PENETRATION_DIRECTIONS*2] = +{ + btVector3(btScalar(0.000000) , btScalar(-0.000000),btScalar(-1.000000)), + btVector3(btScalar(0.723608) , btScalar(-0.525725),btScalar(-0.447219)), + btVector3(btScalar(-0.276388) , btScalar(-0.850649),btScalar(-0.447219)), + btVector3(btScalar(-0.894426) , btScalar(-0.000000),btScalar(-0.447216)), + btVector3(btScalar(-0.276388) , btScalar(0.850649),btScalar(-0.447220)), + btVector3(btScalar(0.723608) , btScalar(0.525725),btScalar(-0.447219)), + btVector3(btScalar(0.276388) , btScalar(-0.850649),btScalar(0.447220)), + btVector3(btScalar(-0.723608) , btScalar(-0.525725),btScalar(0.447219)), + btVector3(btScalar(-0.723608) , btScalar(0.525725),btScalar(0.447219)), + btVector3(btScalar(0.276388) , btScalar(0.850649),btScalar(0.447219)), + btVector3(btScalar(0.894426) , btScalar(0.000000),btScalar(0.447216)), + btVector3(btScalar(-0.000000) , btScalar(0.000000),btScalar(1.000000)), + btVector3(btScalar(0.425323) , btScalar(-0.309011),btScalar(-0.850654)), + btVector3(btScalar(-0.162456) , btScalar(-0.499995),btScalar(-0.850654)), + btVector3(btScalar(0.262869) , btScalar(-0.809012),btScalar(-0.525738)), + btVector3(btScalar(0.425323) , btScalar(0.309011),btScalar(-0.850654)), + btVector3(btScalar(0.850648) , btScalar(-0.000000),btScalar(-0.525736)), + btVector3(btScalar(-0.525730) , btScalar(-0.000000),btScalar(-0.850652)), + btVector3(btScalar(-0.688190) , btScalar(-0.499997),btScalar(-0.525736)), + btVector3(btScalar(-0.162456) , btScalar(0.499995),btScalar(-0.850654)), + btVector3(btScalar(-0.688190) , btScalar(0.499997),btScalar(-0.525736)), + btVector3(btScalar(0.262869) , btScalar(0.809012),btScalar(-0.525738)), + btVector3(btScalar(0.951058) , btScalar(0.309013),btScalar(0.000000)), + btVector3(btScalar(0.951058) , btScalar(-0.309013),btScalar(0.000000)), + btVector3(btScalar(0.587786) , btScalar(-0.809017),btScalar(0.000000)), + btVector3(btScalar(0.000000) , btScalar(-1.000000),btScalar(0.000000)), + btVector3(btScalar(-0.587786) , btScalar(-0.809017),btScalar(0.000000)), + btVector3(btScalar(-0.951058) , btScalar(-0.309013),btScalar(-0.000000)), + btVector3(btScalar(-0.951058) , btScalar(0.309013),btScalar(-0.000000)), + btVector3(btScalar(-0.587786) , btScalar(0.809017),btScalar(-0.000000)), + btVector3(btScalar(-0.000000) , btScalar(1.000000),btScalar(-0.000000)), + btVector3(btScalar(0.587786) , btScalar(0.809017),btScalar(-0.000000)), + btVector3(btScalar(0.688190) , btScalar(-0.499997),btScalar(0.525736)), + btVector3(btScalar(-0.262869) , btScalar(-0.809012),btScalar(0.525738)), + btVector3(btScalar(-0.850648) , btScalar(0.000000),btScalar(0.525736)), + btVector3(btScalar(-0.262869) , btScalar(0.809012),btScalar(0.525738)), + btVector3(btScalar(0.688190) , btScalar(0.499997),btScalar(0.525736)), + btVector3(btScalar(0.525730) , btScalar(0.000000),btScalar(0.850652)), + btVector3(btScalar(0.162456) , btScalar(-0.499995),btScalar(0.850654)), + btVector3(btScalar(-0.425323) , btScalar(-0.309011),btScalar(0.850654)), + btVector3(btScalar(-0.425323) , btScalar(0.309011),btScalar(0.850654)), + btVector3(btScalar(0.162456) , btScalar(0.499995),btScalar(0.850654)) +}; + +btShapeHull::btShapeHull (const btConvexShape* shape) +{ + m_shape = shape; + m_vertices.clear (); + m_indices.clear(); + m_numIndices = 0; +} + +btShapeHull::~btShapeHull () +{ + m_indices.clear(); + m_vertices.clear (); +} + +bool +btShapeHull::buildHull (btScalar /*margin*/) +{ + int numSampleDirections = NUM_UNITSPHERE_POINTS; + { + int numPDA = m_shape->getNumPreferredPenetrationDirections(); + if (numPDA) + { + for (int i=0;igetPreferredPenetrationDirection(i,norm); + btUnitSpherePoints[numSampleDirections] = norm; + numSampleDirections++; + } + } + } + + btVector3 supportPoints[NUM_UNITSPHERE_POINTS+MAX_PREFERRED_PENETRATION_DIRECTIONS*2]; + int i; + for (i = 0; i < numSampleDirections; i++) + { + supportPoints[i] = m_shape->localGetSupportingVertex(btUnitSpherePoints[i]); + } + + HullDesc hd; + hd.mFlags = QF_TRIANGLES; + hd.mVcount = static_cast(numSampleDirections); + +#ifdef BT_USE_DOUBLE_PRECISION + hd.mVertices = &supportPoints[0]; + hd.mVertexStride = sizeof(btVector3); +#else + hd.mVertices = &supportPoints[0]; + hd.mVertexStride = sizeof (btVector3); +#endif + + HullLibrary hl; + HullResult hr; + if (hl.CreateConvexHull (hd, hr) == QE_FAIL) + { + return false; + } + + m_vertices.resize (static_cast(hr.mNumOutputVertices)); + + + for (i = 0; i < static_cast(hr.mNumOutputVertices); i++) + { + m_vertices[i] = hr.m_OutputVertices[i]; + } + m_numIndices = hr.mNumIndices; + m_indices.resize(static_cast(m_numIndices)); + for (i = 0; i < static_cast(m_numIndices); i++) + { + m_indices[i] = hr.m_Indices[i]; + } + + // free temporary hull result that we just copied + hl.ReleaseResult (hr); + + return true; +} + +int +btShapeHull::numTriangles () const +{ + return static_cast(m_numIndices / 3); +} + +int +btShapeHull::numVertices () const +{ + return m_vertices.size (); +} + +int +btShapeHull::numIndices () const +{ + return static_cast(m_numIndices); +} + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btShapeHull.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btShapeHull.h new file mode 100644 index 000000000..708655d21 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btShapeHull.h @@ -0,0 +1,56 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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. +*/ + +///btShapeHull implemented by John McCutchan. + +#ifndef _SHAPE_HULL_H +#define _SHAPE_HULL_H + +#include "LinearMath/btAlignedObjectArray.h" +#include "BulletCollision/CollisionShapes/btConvexShape.h" + + +///The btShapeHull class takes a btConvexShape, builds a simplified convex hull using btConvexHull and provides triangle indices and vertices. +///It can be useful for to simplify a complex convex object and for visualization of a non-polyhedral convex object. +///It approximates the convex hull using the supporting vertex of 42 directions. +class btShapeHull +{ +public: + btShapeHull (const btConvexShape* shape); + ~btShapeHull (); + + bool buildHull (btScalar margin); + + int numTriangles () const; + int numVertices () const; + int numIndices () const; + + const btVector3* getVertexPointer() const + { + return &m_vertices[0]; + } + const unsigned int* getIndexPointer() const + { + return &m_indices[0]; + } + +protected: + btAlignedObjectArray m_vertices; + btAlignedObjectArray m_indices; + unsigned int m_numIndices; + const btConvexShape* m_shape; +}; + +#endif //_SHAPE_HULL_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btSphereShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btSphereShape.cpp new file mode 100644 index 000000000..b9a736c0f --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btSphereShape.cpp @@ -0,0 +1,71 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 "btSphereShape.h" +#include "BulletCollision/CollisionShapes/btCollisionMargin.h" + +#include "LinearMath/btQuaternion.h" + +btVector3 btSphereShape::localGetSupportingVertexWithoutMargin(const btVector3& vec)const +{ + (void)vec; + return btVector3(btScalar(0.),btScalar(0.),btScalar(0.)); +} + +void btSphereShape::batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const +{ + (void)vectors; + + for (int i=0;iprocessTriangle(triangle,0,0); + + triangle[0] = projectedCenter - tangentDir0*radius - tangentDir1*radius; + triangle[1] = projectedCenter - tangentDir0*radius + tangentDir1*radius; + triangle[2] = projectedCenter + tangentDir0*radius + tangentDir1*radius; + + callback->processTriangle(triangle,0,1); + +} + +void btStaticPlaneShape::calculateLocalInertia(btScalar mass,btVector3& inertia) const +{ + (void)mass; + + //moving concave objects not supported + + inertia.setValue(btScalar(0.),btScalar(0.),btScalar(0.)); +} + +void btStaticPlaneShape::setLocalScaling(const btVector3& scaling) +{ + m_localScaling = scaling; +} +const btVector3& btStaticPlaneShape::getLocalScaling() const +{ + return m_localScaling; +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btStaticPlaneShape.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btStaticPlaneShape.h new file mode 100644 index 000000000..2b5d4b338 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btStaticPlaneShape.h @@ -0,0 +1,64 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 STATIC_PLANE_SHAPE_H +#define STATIC_PLANE_SHAPE_H + +#include "btConcaveShape.h" + + +///The btStaticPlaneShape simulates an infinite non-moving (static) collision plane. +class btStaticPlaneShape : public btConcaveShape +{ +protected: + btVector3 m_localAabbMin; + btVector3 m_localAabbMax; + + btVector3 m_planeNormal; + btScalar m_planeConstant; + btVector3 m_localScaling; + +public: + btStaticPlaneShape(const btVector3& planeNormal,btScalar planeConstant); + + virtual ~btStaticPlaneShape(); + + + virtual void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const; + + virtual void processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const; + + virtual void calculateLocalInertia(btScalar mass,btVector3& inertia) const; + + virtual void setLocalScaling(const btVector3& scaling); + virtual const btVector3& getLocalScaling() const; + + const btVector3& getPlaneNormal() const + { + return m_planeNormal; + } + + const btScalar& getPlaneConstant() const + { + return m_planeConstant; + } + + //debugging + virtual const char* getName()const {return "STATICPLANE";} + + +}; + +#endif //STATIC_PLANE_SHAPE_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btStridingMeshInterface.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btStridingMeshInterface.cpp new file mode 100644 index 000000000..34389f7c5 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btStridingMeshInterface.cpp @@ -0,0 +1,181 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 "btStridingMeshInterface.h" + +btStridingMeshInterface::~btStridingMeshInterface() +{ + +} + + +void btStridingMeshInterface::InternalProcessAllTriangles(btInternalTriangleIndexCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const +{ + (void)aabbMin; + (void)aabbMax; + int numtotalphysicsverts = 0; + int part,graphicssubparts = getNumSubParts(); + const unsigned char * vertexbase; + const unsigned char * indexbase; + int indexstride; + PHY_ScalarType type; + PHY_ScalarType gfxindextype; + int stride,numverts,numtriangles; + int gfxindex; + btVector3 triangle[3]; + + btVector3 meshScaling = getScaling(); + + ///if the number of parts is big, the performance might drop due to the innerloop switch on indextype + for (part=0;partinternalProcessTriangleIndex(triangle,part,gfxindex); + } + break; + } + case PHY_SHORT: + { + for (gfxindex=0;gfxindexinternalProcessTriangleIndex(triangle,part,gfxindex); + } + break; + } + default: + btAssert((gfxindextype == PHY_INTEGER) || (gfxindextype == PHY_SHORT)); + } + break; + } + + case PHY_DOUBLE: + { + double* graphicsbase; + + switch (gfxindextype) + { + case PHY_INTEGER: + { + for (gfxindex=0;gfxindexinternalProcessTriangleIndex(triangle,part,gfxindex); + } + break; + } + case PHY_SHORT: + { + for (gfxindex=0;gfxindexinternalProcessTriangleIndex(triangle,part,gfxindex); + } + break; + } + default: + btAssert((gfxindextype == PHY_INTEGER) || (gfxindextype == PHY_SHORT)); + } + break; + } + default: + btAssert((type == PHY_FLOAT) || (type == PHY_DOUBLE)); + } + + unLockReadOnlyVertexBase(part); + } +} + +void btStridingMeshInterface::calculateAabbBruteForce(btVector3& aabbMin,btVector3& aabbMax) +{ + + struct AabbCalculationCallback : public btInternalTriangleIndexCallback + { + btVector3 m_aabbMin; + btVector3 m_aabbMax; + + AabbCalculationCallback() + { + m_aabbMin.setValue(btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT)); + m_aabbMax.setValue(btScalar(-BT_LARGE_FLOAT),btScalar(-BT_LARGE_FLOAT),btScalar(-BT_LARGE_FLOAT)); + } + + virtual void internalProcessTriangleIndex(btVector3* triangle,int partId,int triangleIndex) + { + (void)partId; + (void)triangleIndex; + + m_aabbMin.setMin(triangle[0]); + m_aabbMax.setMax(triangle[0]); + m_aabbMin.setMin(triangle[1]); + m_aabbMax.setMax(triangle[1]); + m_aabbMin.setMin(triangle[2]); + m_aabbMax.setMax(triangle[2]); + } + }; + + //first calculate the total aabb for all triangles + AabbCalculationCallback aabbCallback; + aabbMin.setValue(btScalar(-BT_LARGE_FLOAT),btScalar(-BT_LARGE_FLOAT),btScalar(-BT_LARGE_FLOAT)); + aabbMax.setValue(btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT)); + InternalProcessAllTriangles(&aabbCallback,aabbMin,aabbMax); + + aabbMin = aabbCallback.m_aabbMin; + aabbMax = aabbCallback.m_aabbMax; +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btStridingMeshInterface.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btStridingMeshInterface.h new file mode 100644 index 000000000..09641cf53 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btStridingMeshInterface.h @@ -0,0 +1,96 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 STRIDING_MESHINTERFACE_H +#define STRIDING_MESHINTERFACE_H + +#include "LinearMath/btVector3.h" +#include "btTriangleCallback.h" +#include "btConcaveShape.h" + + + +/// The btStridingMeshInterface is the interface class for high performance generic access to triangle meshes, used in combination with btBvhTriangleMeshShape and some other collision shapes. +/// Using index striding of 3*sizeof(integer) it can use triangle arrays, using index striding of 1*sizeof(integer) it can handle triangle strips. +/// It allows for sharing graphics and collision meshes. Also it provides locking/unlocking of graphics meshes that are in gpu memory. +class btStridingMeshInterface +{ + protected: + + btVector3 m_scaling; + + public: + btStridingMeshInterface() :m_scaling(btScalar(1.),btScalar(1.),btScalar(1.)) + { + + } + + virtual ~btStridingMeshInterface(); + + + + virtual void InternalProcessAllTriangles(btInternalTriangleIndexCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const; + + ///brute force method to calculate aabb + void calculateAabbBruteForce(btVector3& aabbMin,btVector3& aabbMax); + + /// get read and write access to a subpart of a triangle mesh + /// this subpart has a continuous array of vertices and indices + /// in this way the mesh can be handled as chunks of memory with striding + /// very similar to OpenGL vertexarray support + /// make a call to unLockVertexBase when the read and write access is finished + virtual void getLockedVertexIndexBase(unsigned char **vertexbase, int& numverts,PHY_ScalarType& type, int& stride,unsigned char **indexbase,int & indexstride,int& numfaces,PHY_ScalarType& indicestype,int subpart=0)=0; + + virtual void getLockedReadOnlyVertexIndexBase(const unsigned char **vertexbase, int& numverts,PHY_ScalarType& type, int& stride,const unsigned char **indexbase,int & indexstride,int& numfaces,PHY_ScalarType& indicestype,int subpart=0) const=0; + + /// unLockVertexBase finishes the access to a subpart of the triangle mesh + /// make a call to unLockVertexBase when the read and write access (using getLockedVertexIndexBase) is finished + virtual void unLockVertexBase(int subpart)=0; + + virtual void unLockReadOnlyVertexBase(int subpart) const=0; + + + /// getNumSubParts returns the number of seperate subparts + /// each subpart has a continuous array of vertices and indices + virtual int getNumSubParts() const=0; + + virtual void preallocateVertices(int numverts)=0; + virtual void preallocateIndices(int numindices)=0; + + virtual bool hasPremadeAabb() const { return false; } + virtual void setPremadeAabb(const btVector3& aabbMin, const btVector3& aabbMax ) const + { + (void) aabbMin; + (void) aabbMax; + } + virtual void getPremadeAabb(btVector3* aabbMin, btVector3* aabbMax ) const + { + (void) aabbMin; + (void) aabbMax; + } + + const btVector3& getScaling() const { + return m_scaling; + } + void setScaling(const btVector3& scaling) + { + m_scaling = scaling; + } + + + +}; + +#endif //STRIDING_MESHINTERFACE_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTetrahedronShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTetrahedronShape.cpp new file mode 100644 index 000000000..52f346bf7 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTetrahedronShape.cpp @@ -0,0 +1,218 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 "btTetrahedronShape.h" +#include "LinearMath/btMatrix3x3.h" + +btBU_Simplex1to4::btBU_Simplex1to4() : btPolyhedralConvexAabbCachingShape (), +m_numVertices(0) +{ + m_shapeType = TETRAHEDRAL_SHAPE_PROXYTYPE; +} + +btBU_Simplex1to4::btBU_Simplex1to4(const btVector3& pt0) : btPolyhedralConvexAabbCachingShape (), +m_numVertices(0) +{ + m_shapeType = TETRAHEDRAL_SHAPE_PROXYTYPE; + addVertex(pt0); +} + +btBU_Simplex1to4::btBU_Simplex1to4(const btVector3& pt0,const btVector3& pt1) : btPolyhedralConvexAabbCachingShape (), +m_numVertices(0) +{ + m_shapeType = TETRAHEDRAL_SHAPE_PROXYTYPE; + addVertex(pt0); + addVertex(pt1); +} + +btBU_Simplex1to4::btBU_Simplex1to4(const btVector3& pt0,const btVector3& pt1,const btVector3& pt2) : btPolyhedralConvexAabbCachingShape (), +m_numVertices(0) +{ + m_shapeType = TETRAHEDRAL_SHAPE_PROXYTYPE; + addVertex(pt0); + addVertex(pt1); + addVertex(pt2); +} + +btBU_Simplex1to4::btBU_Simplex1to4(const btVector3& pt0,const btVector3& pt1,const btVector3& pt2,const btVector3& pt3) : btPolyhedralConvexAabbCachingShape (), +m_numVertices(0) +{ + m_shapeType = TETRAHEDRAL_SHAPE_PROXYTYPE; + addVertex(pt0); + addVertex(pt1); + addVertex(pt2); + addVertex(pt3); +} + + +void btBU_Simplex1to4::getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const +{ +#if 1 + btPolyhedralConvexAabbCachingShape::getAabb(t,aabbMin,aabbMax); +#else + aabbMin.setValue(BT_LARGE_FLOAT,BT_LARGE_FLOAT,BT_LARGE_FLOAT); + aabbMax.setValue(-BT_LARGE_FLOAT,-BT_LARGE_FLOAT,-BT_LARGE_FLOAT); + + //just transform the vertices in worldspace, and take their AABB + for (int i=0;iprocessAllTriangles(&triBuf,aabbMin, aabbMax); +/// for (int i=0;i m_triangleBuffer; + +public: + + + virtual void processTriangle(btVector3* triangle, int partId, int triangleIndex); + + int getNumTriangles() const + { + return int(m_triangleBuffer.size()); + } + + const btTriangle& getTriangle(int index) const + { + return m_triangleBuffer[index]; + } + + void clearBuffer() + { + m_triangleBuffer.clear(); + } + +}; + + +#endif //BT_TRIANGLE_BUFFER_H + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleCallback.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleCallback.cpp new file mode 100644 index 000000000..f558bf6d2 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleCallback.cpp @@ -0,0 +1,28 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 "btTriangleCallback.h" + +btTriangleCallback::~btTriangleCallback() +{ + +} + + +btInternalTriangleIndexCallback::~btInternalTriangleIndexCallback() +{ + +} + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleCallback.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleCallback.h new file mode 100644 index 000000000..0499702b0 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleCallback.h @@ -0,0 +1,42 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 TRIANGLE_CALLBACK_H +#define TRIANGLE_CALLBACK_H + +#include "LinearMath/btVector3.h" + + +///The btTriangleCallback provides a callback for each overlapping triangle when calling processAllTriangles. +///This callback is called by processAllTriangles for all btConcaveShape derived class, such as btBvhTriangleMeshShape, btStaticPlaneShape and btHeightfieldTerrainShape. +class btTriangleCallback +{ +public: + + virtual ~btTriangleCallback(); + virtual void processTriangle(btVector3* triangle, int partId, int triangleIndex) = 0; +}; + +class btInternalTriangleIndexCallback +{ +public: + + virtual ~btInternalTriangleIndexCallback(); + virtual void internalProcessTriangleIndex(btVector3* triangle,int partId,int triangleIndex) = 0; +}; + + + +#endif //TRIANGLE_CALLBACK_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleIndexVertexArray.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleIndexVertexArray.cpp new file mode 100644 index 000000000..a665024cb --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleIndexVertexArray.cpp @@ -0,0 +1,95 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 "btTriangleIndexVertexArray.h" + +btTriangleIndexVertexArray::btTriangleIndexVertexArray(int numTriangles,int* triangleIndexBase,int triangleIndexStride,int numVertices,btScalar* vertexBase,int vertexStride) +: m_hasAabb(0) +{ + btIndexedMesh mesh; + + mesh.m_numTriangles = numTriangles; + mesh.m_triangleIndexBase = (const unsigned char *)triangleIndexBase; + mesh.m_triangleIndexStride = triangleIndexStride; + mesh.m_numVertices = numVertices; + mesh.m_vertexBase = (const unsigned char *)vertexBase; + mesh.m_vertexStride = vertexStride; + + addIndexedMesh(mesh); + +} + +btTriangleIndexVertexArray::~btTriangleIndexVertexArray() +{ + +} + +void btTriangleIndexVertexArray::getLockedVertexIndexBase(unsigned char **vertexbase, int& numverts,PHY_ScalarType& type, int& vertexStride,unsigned char **indexbase,int & indexstride,int& numfaces,PHY_ScalarType& indicestype,int subpart) +{ + btAssert(subpart< getNumSubParts() ); + + btIndexedMesh& mesh = m_indexedMeshes[subpart]; + + numverts = mesh.m_numVertices; + (*vertexbase) = (unsigned char *) mesh.m_vertexBase; + + type = mesh.m_vertexType; + + vertexStride = mesh.m_vertexStride; + + numfaces = mesh.m_numTriangles; + + (*indexbase) = (unsigned char *)mesh.m_triangleIndexBase; + indexstride = mesh.m_triangleIndexStride; + indicestype = mesh.m_indexType; +} + +void btTriangleIndexVertexArray::getLockedReadOnlyVertexIndexBase(const unsigned char **vertexbase, int& numverts,PHY_ScalarType& type, int& vertexStride,const unsigned char **indexbase,int & indexstride,int& numfaces,PHY_ScalarType& indicestype,int subpart) const +{ + const btIndexedMesh& mesh = m_indexedMeshes[subpart]; + + numverts = mesh.m_numVertices; + (*vertexbase) = (const unsigned char *)mesh.m_vertexBase; + + type = mesh.m_vertexType; + + vertexStride = mesh.m_vertexStride; + + numfaces = mesh.m_numTriangles; + (*indexbase) = (const unsigned char *)mesh.m_triangleIndexBase; + indexstride = mesh.m_triangleIndexStride; + indicestype = mesh.m_indexType; +} + +bool btTriangleIndexVertexArray::hasPremadeAabb() const +{ + return (m_hasAabb == 1); +} + + +void btTriangleIndexVertexArray::setPremadeAabb(const btVector3& aabbMin, const btVector3& aabbMax ) const +{ + m_aabbMin = aabbMin; + m_aabbMax = aabbMax; + m_hasAabb = 1; // this is intentionally an int see notes in header +} + +void btTriangleIndexVertexArray::getPremadeAabb(btVector3* aabbMin, btVector3* aabbMax ) const +{ + *aabbMin = m_aabbMin; + *aabbMax = m_aabbMax; +} + + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleIndexVertexArray.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleIndexVertexArray.h new file mode 100644 index 000000000..c64ea6e70 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleIndexVertexArray.h @@ -0,0 +1,131 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 BT_TRIANGLE_INDEX_VERTEX_ARRAY_H +#define BT_TRIANGLE_INDEX_VERTEX_ARRAY_H + +#include "btStridingMeshInterface.h" +#include "LinearMath/btAlignedObjectArray.h" +#include "LinearMath/btScalar.h" + + +///The btIndexedMesh indexes a single vertex and index array. Multiple btIndexedMesh objects can be passed into a btTriangleIndexVertexArray using addIndexedMesh. +///Instead of the number of indices, we pass the number of triangles. +ATTRIBUTE_ALIGNED16( struct) btIndexedMesh +{ + BT_DECLARE_ALIGNED_ALLOCATOR(); + + int m_numTriangles; + const unsigned char * m_triangleIndexBase; + int m_triangleIndexStride; + int m_numVertices; + const unsigned char * m_vertexBase; + int m_vertexStride; + + // The index type is set when adding an indexed mesh to the + // btTriangleIndexVertexArray, do not set it manually + PHY_ScalarType m_indexType; + + // The vertex type has a default type similar to Bullet's precision mode (float or double) + // but can be set manually if you for example run Bullet with double precision but have + // mesh data in single precision.. + PHY_ScalarType m_vertexType; + + + btIndexedMesh() + :m_indexType(PHY_INTEGER), +#ifdef BT_USE_DOUBLE_PRECISION + m_vertexType(PHY_DOUBLE) +#else // BT_USE_DOUBLE_PRECISION + m_vertexType(PHY_FLOAT) +#endif // BT_USE_DOUBLE_PRECISION + { + } +} +; + + +typedef btAlignedObjectArray IndexedMeshArray; + +///The btTriangleIndexVertexArray allows to access multiple triangle meshes, by indexing into existing triangle/index arrays. +///Additional meshes can be added using addIndexedMesh +///No duplcate is made of the vertex/index data, it only indexes into external vertex/index arrays. +///So keep those arrays around during the lifetime of this btTriangleIndexVertexArray. +ATTRIBUTE_ALIGNED16( class) btTriangleIndexVertexArray : public btStridingMeshInterface +{ +protected: + IndexedMeshArray m_indexedMeshes; + int m_pad[2]; + mutable int m_hasAabb; // using int instead of bool to maintain alignment + mutable btVector3 m_aabbMin; + mutable btVector3 m_aabbMax; + +public: + + BT_DECLARE_ALIGNED_ALLOCATOR(); + + btTriangleIndexVertexArray() : m_hasAabb(0) + { + } + + virtual ~btTriangleIndexVertexArray(); + + //just to be backwards compatible + btTriangleIndexVertexArray(int numTriangles,int* triangleIndexBase,int triangleIndexStride,int numVertices,btScalar* vertexBase,int vertexStride); + + void addIndexedMesh(const btIndexedMesh& mesh, PHY_ScalarType indexType = PHY_INTEGER) + { + m_indexedMeshes.push_back(mesh); + m_indexedMeshes[m_indexedMeshes.size()-1].m_indexType = indexType; + } + + + virtual void getLockedVertexIndexBase(unsigned char **vertexbase, int& numverts,PHY_ScalarType& type, int& vertexStride,unsigned char **indexbase,int & indexstride,int& numfaces,PHY_ScalarType& indicestype,int subpart=0); + + virtual void getLockedReadOnlyVertexIndexBase(const unsigned char **vertexbase, int& numverts,PHY_ScalarType& type, int& vertexStride,const unsigned char **indexbase,int & indexstride,int& numfaces,PHY_ScalarType& indicestype,int subpart=0) const; + + /// unLockVertexBase finishes the access to a subpart of the triangle mesh + /// make a call to unLockVertexBase when the read and write access (using getLockedVertexIndexBase) is finished + virtual void unLockVertexBase(int subpart) {(void)subpart;} + + virtual void unLockReadOnlyVertexBase(int subpart) const {(void)subpart;} + + /// getNumSubParts returns the number of seperate subparts + /// each subpart has a continuous array of vertices and indices + virtual int getNumSubParts() const { + return (int)m_indexedMeshes.size(); + } + + IndexedMeshArray& getIndexedMeshArray() + { + return m_indexedMeshes; + } + + const IndexedMeshArray& getIndexedMeshArray() const + { + return m_indexedMeshes; + } + + virtual void preallocateVertices(int numverts){(void) numverts;} + virtual void preallocateIndices(int numindices){(void) numindices;} + + virtual bool hasPremadeAabb() const; + virtual void setPremadeAabb(const btVector3& aabbMin, const btVector3& aabbMax ) const; + virtual void getPremadeAabb(btVector3* aabbMin, btVector3* aabbMax ) const; + +} +; + +#endif //BT_TRIANGLE_INDEX_VERTEX_ARRAY_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleIndexVertexMaterialArray.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleIndexVertexMaterialArray.cpp new file mode 100644 index 000000000..dc562941a --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleIndexVertexMaterialArray.cpp @@ -0,0 +1,86 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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. +*/ + +///This file was created by Alex Silverman + +#include "btTriangleIndexVertexMaterialArray.h" + +btTriangleIndexVertexMaterialArray::btTriangleIndexVertexMaterialArray(int numTriangles,int* triangleIndexBase,int triangleIndexStride, + int numVertices,btScalar* vertexBase,int vertexStride, + int numMaterials, unsigned char* materialBase, int materialStride, + int* triangleMaterialsBase, int materialIndexStride) : +btTriangleIndexVertexArray(numTriangles, triangleIndexBase, triangleIndexStride, numVertices, vertexBase, vertexStride) +{ + btMaterialProperties mat; + + mat.m_numMaterials = numMaterials; + mat.m_materialBase = materialBase; + mat.m_materialStride = materialStride; +#ifdef BT_USE_DOUBLE_PRECISION + mat.m_materialType = PHY_DOUBLE; +#else + mat.m_materialType = PHY_FLOAT; +#endif + + mat.m_numTriangles = numTriangles; + mat.m_triangleMaterialsBase = (unsigned char *)triangleMaterialsBase; + mat.m_triangleMaterialStride = materialIndexStride; + mat.m_triangleType = PHY_INTEGER; + + addMaterialProperties(mat); +} + + +void btTriangleIndexVertexMaterialArray::getLockedMaterialBase(unsigned char **materialBase, int& numMaterials, PHY_ScalarType& materialType, int& materialStride, + unsigned char ** triangleMaterialBase, int& numTriangles, int& triangleMaterialStride, PHY_ScalarType& triangleType, int subpart) +{ + btAssert(subpart< getNumSubParts() ); + + btMaterialProperties& mats = m_materials[subpart]; + + numMaterials = mats.m_numMaterials; + (*materialBase) = (unsigned char *) mats.m_materialBase; +#ifdef BT_USE_DOUBLE_PRECISION + materialType = PHY_DOUBLE; +#else + materialType = PHY_FLOAT; +#endif + materialStride = mats.m_materialStride; + + numTriangles = mats.m_numTriangles; + (*triangleMaterialBase) = (unsigned char *)mats.m_triangleMaterialsBase; + triangleMaterialStride = mats.m_triangleMaterialStride; + triangleType = mats.m_triangleType; +} + +void btTriangleIndexVertexMaterialArray::getLockedReadOnlyMaterialBase(const unsigned char **materialBase, int& numMaterials, PHY_ScalarType& materialType, int& materialStride, + const unsigned char ** triangleMaterialBase, int& numTriangles, int& triangleMaterialStride, PHY_ScalarType& triangleType, int subpart) +{ + btMaterialProperties& mats = m_materials[subpart]; + + numMaterials = mats.m_numMaterials; + (*materialBase) = (const unsigned char *) mats.m_materialBase; +#ifdef BT_USE_DOUBLE_PRECISION + materialType = PHY_DOUBLE; +#else + materialType = PHY_FLOAT; +#endif + materialStride = mats.m_materialStride; + + numTriangles = mats.m_numTriangles; + (*triangleMaterialBase) = (const unsigned char *)mats.m_triangleMaterialsBase; + triangleMaterialStride = mats.m_triangleMaterialStride; + triangleType = mats.m_triangleType; +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleIndexVertexMaterialArray.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleIndexVertexMaterialArray.h new file mode 100644 index 000000000..ba4f7b460 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleIndexVertexMaterialArray.h @@ -0,0 +1,84 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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. +*/ + +///This file was created by Alex Silverman + +#ifndef BT_MULTIMATERIAL_TRIANGLE_INDEX_VERTEX_ARRAY_H +#define BT_MULTIMATERIAL_TRIANGLE_INDEX_VERTEX_ARRAY_H + +#include "btTriangleIndexVertexArray.h" + + +ATTRIBUTE_ALIGNED16( struct) btMaterialProperties +{ + ///m_materialBase ==========> 2 btScalar values make up one material, friction then restitution + int m_numMaterials; + const unsigned char * m_materialBase; + int m_materialStride; + PHY_ScalarType m_materialType; + ///m_numTriangles <=========== This exists in the btIndexedMesh object for the same subpart, but since we're + /// padding the structure, it can be reproduced at no real cost + ///m_triangleMaterials =====> 1 integer value makes up one entry + /// eg: m_triangleMaterials[1] = 5; // This will set triangle 2 to use material 5 + int m_numTriangles; + const unsigned char * m_triangleMaterialsBase; + int m_triangleMaterialStride; + ///m_triangleType <========== Automatically set in addMaterialProperties + PHY_ScalarType m_triangleType; +}; + +typedef btAlignedObjectArray MaterialArray; + +///Teh btTriangleIndexVertexMaterialArray is built on TriangleIndexVertexArray +///The addition of a material array allows for the utilization of the partID and +///triangleIndex that are returned in the ContactAddedCallback. As with +///TriangleIndexVertexArray, no duplicate is made of the material data, so it +///is the users responsibility to maintain the array during the lifetime of the +///TriangleIndexVertexMaterialArray. +ATTRIBUTE_ALIGNED16(class) btTriangleIndexVertexMaterialArray : public btTriangleIndexVertexArray +{ +protected: + MaterialArray m_materials; + +public: + BT_DECLARE_ALIGNED_ALLOCATOR(); + + btTriangleIndexVertexMaterialArray() + { + } + + btTriangleIndexVertexMaterialArray(int numTriangles,int* triangleIndexBase,int triangleIndexStride, + int numVertices,btScalar* vertexBase,int vertexStride, + int numMaterials, unsigned char* materialBase, int materialStride, + int* triangleMaterialsBase, int materialIndexStride); + + virtual ~btTriangleIndexVertexMaterialArray() {} + + void addMaterialProperties(const btMaterialProperties& mat, PHY_ScalarType triangleType = PHY_INTEGER) + { + m_materials.push_back(mat); + m_materials[m_materials.size()-1].m_triangleType = triangleType; + } + + virtual void getLockedMaterialBase(unsigned char **materialBase, int& numMaterials, PHY_ScalarType& materialType, int& materialStride, + unsigned char ** triangleMaterialBase, int& numTriangles, int& triangleMaterialStride, PHY_ScalarType& triangleType ,int subpart = 0); + + virtual void getLockedReadOnlyMaterialBase(const unsigned char **materialBase, int& numMaterials, PHY_ScalarType& materialType, int& materialStride, + const unsigned char ** triangleMaterialBase, int& numTriangles, int& triangleMaterialStride, PHY_ScalarType& triangleType, int subpart = 0); + +} +; + +#endif //BT_MULTIMATERIAL_TRIANGLE_INDEX_VERTEX_ARRAY_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleMesh.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleMesh.cpp new file mode 100644 index 000000000..b29e0f71e --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleMesh.cpp @@ -0,0 +1,140 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 "btTriangleMesh.h" + + + +btTriangleMesh::btTriangleMesh (bool use32bitIndices,bool use4componentVertices) +:m_use32bitIndices(use32bitIndices), +m_use4componentVertices(use4componentVertices), +m_weldingThreshold(0.0) +{ + btIndexedMesh meshIndex; + meshIndex.m_numTriangles = 0; + meshIndex.m_numVertices = 0; + meshIndex.m_indexType = PHY_INTEGER; + meshIndex.m_triangleIndexBase = 0; + meshIndex.m_triangleIndexStride = 3*sizeof(int); + meshIndex.m_vertexBase = 0; + meshIndex.m_vertexStride = sizeof(btVector3); + m_indexedMeshes.push_back(meshIndex); + + if (m_use32bitIndices) + { + m_indexedMeshes[0].m_numTriangles = m_32bitIndices.size()/3; + m_indexedMeshes[0].m_triangleIndexBase = 0; + m_indexedMeshes[0].m_indexType = PHY_INTEGER; + m_indexedMeshes[0].m_triangleIndexStride = 3*sizeof(int); + } else + { + m_indexedMeshes[0].m_numTriangles = m_16bitIndices.size()/3; + m_indexedMeshes[0].m_triangleIndexBase = 0; + m_indexedMeshes[0].m_indexType = PHY_SHORT; + m_indexedMeshes[0].m_triangleIndexStride = 3*sizeof(short int); + } + + if (m_use4componentVertices) + { + m_indexedMeshes[0].m_numVertices = m_4componentVertices.size(); + m_indexedMeshes[0].m_vertexBase = 0; + m_indexedMeshes[0].m_vertexStride = sizeof(btVector3); + } else + { + m_indexedMeshes[0].m_numVertices = m_3componentVertices.size()/3; + m_indexedMeshes[0].m_vertexBase = 0; + m_indexedMeshes[0].m_vertexStride = 3*sizeof(btScalar); + } + + +} + +void btTriangleMesh::addIndex(int index) +{ + if (m_use32bitIndices) + { + m_32bitIndices.push_back(index); + m_indexedMeshes[0].m_triangleIndexBase = (unsigned char*) &m_32bitIndices[0]; + } else + { + m_16bitIndices.push_back(index); + m_indexedMeshes[0].m_triangleIndexBase = (unsigned char*) &m_16bitIndices[0]; + } +} + + +int btTriangleMesh::findOrAddVertex(const btVector3& vertex, bool removeDuplicateVertices) +{ + //return index of new/existing vertex + ///@todo: could use acceleration structure for this + if (m_use4componentVertices) + { + if (removeDuplicateVertices) + { + for (int i=0;i< m_4componentVertices.size();i++) + { + if ((m_4componentVertices[i]-vertex).length2() <= m_weldingThreshold) + { + return i; + } + } + } + m_indexedMeshes[0].m_numVertices++; + m_4componentVertices.push_back(vertex); + m_indexedMeshes[0].m_vertexBase = (unsigned char*)&m_4componentVertices[0]; + + return m_4componentVertices.size()-1; + + } else + { + + if (removeDuplicateVertices) + { + for (int i=0;i< m_3componentVertices.size();i+=3) + { + btVector3 vtx(m_3componentVertices[i],m_3componentVertices[i+1],m_3componentVertices[i+2]); + if ((vtx-vertex).length2() <= m_weldingThreshold) + { + return i/3; + } + } + } + m_3componentVertices.push_back((float)vertex.getX()); + m_3componentVertices.push_back((float)vertex.getY()); + m_3componentVertices.push_back((float)vertex.getZ()); + m_indexedMeshes[0].m_numVertices++; + m_indexedMeshes[0].m_vertexBase = (unsigned char*)&m_3componentVertices[0]; + return (m_3componentVertices.size()/3)-1; + } + +} + +void btTriangleMesh::addTriangle(const btVector3& vertex0,const btVector3& vertex1,const btVector3& vertex2,bool removeDuplicateVertices) +{ + m_indexedMeshes[0].m_numTriangles++; + addIndex(findOrAddVertex(vertex0,removeDuplicateVertices)); + addIndex(findOrAddVertex(vertex1,removeDuplicateVertices)); + addIndex(findOrAddVertex(vertex2,removeDuplicateVertices)); +} + +int btTriangleMesh::getNumTriangles() const +{ + if (m_use32bitIndices) + { + return m_32bitIndices.size() / 3; + } + return m_16bitIndices.size() / 3; +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleMesh.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleMesh.h new file mode 100644 index 000000000..d2624fe18 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleMesh.h @@ -0,0 +1,69 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 TRIANGLE_MESH_H +#define TRIANGLE_MESH_H + +#include "btTriangleIndexVertexArray.h" +#include "LinearMath/btVector3.h" +#include "LinearMath/btAlignedObjectArray.h" + +///The btTriangleMesh class is a convenience class derived from btTriangleIndexVertexArray, that provides storage for a concave triangle mesh. It can be used as data for the btBvhTriangleMeshShape. +///It allows either 32bit or 16bit indices, and 4 (x-y-z-w) or 3 (x-y-z) component vertices. +///If you want to share triangle/index data between graphics mesh and collision mesh (btBvhTriangleMeshShape), you can directly use btTriangleIndexVertexArray or derive your own class from btStridingMeshInterface. +///Performance of btTriangleMesh and btTriangleIndexVertexArray used in a btBvhTriangleMeshShape is the same. +class btTriangleMesh : public btTriangleIndexVertexArray +{ + btAlignedObjectArray m_4componentVertices; + btAlignedObjectArray m_3componentVertices; + + btAlignedObjectArray m_32bitIndices; + btAlignedObjectArray m_16bitIndices; + bool m_use32bitIndices; + bool m_use4componentVertices; + + + public: + btScalar m_weldingThreshold; + + btTriangleMesh (bool use32bitIndices=true,bool use4componentVertices=true); + + bool getUse32bitIndices() const + { + return m_use32bitIndices; + } + + bool getUse4componentVertices() const + { + return m_use4componentVertices; + } + ///By default addTriangle won't search for duplicate vertices, because the search is very slow for large triangle meshes. + ///In general it is better to directly use btTriangleIndexVertexArray instead. + void addTriangle(const btVector3& vertex0,const btVector3& vertex1,const btVector3& vertex2, bool removeDuplicateVertices=false); + + int getNumTriangles() const; + + virtual void preallocateVertices(int numverts){(void) numverts;} + virtual void preallocateIndices(int numindices){(void) numindices;} + + ///findOrAddVertex is an internal method, use addTriangle instead + int findOrAddVertex(const btVector3& vertex, bool removeDuplicateVertices); + ///addIndex is an internal method, use addTriangle instead + void addIndex(int index); + +}; + +#endif //TRIANGLE_MESH_H + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleMeshShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleMeshShape.cpp new file mode 100644 index 000000000..774fd4e76 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleMeshShape.cpp @@ -0,0 +1,209 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 "btTriangleMeshShape.h" +#include "LinearMath/btVector3.h" +#include "LinearMath/btQuaternion.h" +#include "btStridingMeshInterface.h" +#include "LinearMath/btAabbUtil2.h" +#include "BulletCollision/CollisionShapes/btCollisionMargin.h" + + +btTriangleMeshShape::btTriangleMeshShape(btStridingMeshInterface* meshInterface) +: btConcaveShape (), m_meshInterface(meshInterface) +{ + m_shapeType = TRIANGLE_MESH_SHAPE_PROXYTYPE; + if(meshInterface->hasPremadeAabb()) + { + meshInterface->getPremadeAabb(&m_localAabbMin, &m_localAabbMax); + } + else + { + recalcLocalAabb(); + } +} + + +btTriangleMeshShape::~btTriangleMeshShape() +{ + +} + + + + +void btTriangleMeshShape::getAabb(const btTransform& trans,btVector3& aabbMin,btVector3& aabbMax) const +{ + + btVector3 localHalfExtents = btScalar(0.5)*(m_localAabbMax-m_localAabbMin); + localHalfExtents += btVector3(getMargin(),getMargin(),getMargin()); + btVector3 localCenter = btScalar(0.5)*(m_localAabbMax+m_localAabbMin); + + btMatrix3x3 abs_b = trans.getBasis().absolute(); + + btVector3 center = trans(localCenter); + + btVector3 extent = btVector3(abs_b[0].dot(localHalfExtents), + abs_b[1].dot(localHalfExtents), + abs_b[2].dot(localHalfExtents)); + aabbMin = center - extent; + aabbMax = center + extent; + + +} + +void btTriangleMeshShape::recalcLocalAabb() +{ + for (int i=0;i<3;i++) + { + btVector3 vec(btScalar(0.),btScalar(0.),btScalar(0.)); + vec[i] = btScalar(1.); + btVector3 tmp = localGetSupportingVertex(vec); + m_localAabbMax[i] = tmp[i]+m_collisionMargin; + vec[i] = btScalar(-1.); + tmp = localGetSupportingVertex(vec); + m_localAabbMin[i] = tmp[i]-m_collisionMargin; + } +} + + + +class SupportVertexCallback : public btTriangleCallback +{ + + btVector3 m_supportVertexLocal; +public: + + btTransform m_worldTrans; + btScalar m_maxDot; + btVector3 m_supportVecLocal; + + SupportVertexCallback(const btVector3& supportVecWorld,const btTransform& trans) + : m_supportVertexLocal(btScalar(0.),btScalar(0.),btScalar(0.)), m_worldTrans(trans) ,m_maxDot(btScalar(-BT_LARGE_FLOAT)) + + { + m_supportVecLocal = supportVecWorld * m_worldTrans.getBasis(); + } + + virtual void processTriangle( btVector3* triangle,int partId, int triangleIndex) + { + (void)partId; + (void)triangleIndex; + for (int i=0;i<3;i++) + { + btScalar dot = m_supportVecLocal.dot(triangle[i]); + if (dot > m_maxDot) + { + m_maxDot = dot; + m_supportVertexLocal = triangle[i]; + } + } + } + + btVector3 GetSupportVertexWorldSpace() + { + return m_worldTrans(m_supportVertexLocal); + } + + btVector3 GetSupportVertexLocal() + { + return m_supportVertexLocal; + } + +}; + + +void btTriangleMeshShape::setLocalScaling(const btVector3& scaling) +{ + m_meshInterface->setScaling(scaling); + recalcLocalAabb(); +} + +const btVector3& btTriangleMeshShape::getLocalScaling() const +{ + return m_meshInterface->getScaling(); +} + + + + + + +//#define DEBUG_TRIANGLE_MESH + + + +void btTriangleMeshShape::processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const +{ + struct FilteredCallback : public btInternalTriangleIndexCallback + { + btTriangleCallback* m_callback; + btVector3 m_aabbMin; + btVector3 m_aabbMax; + + FilteredCallback(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) + :m_callback(callback), + m_aabbMin(aabbMin), + m_aabbMax(aabbMax) + { + } + + virtual void internalProcessTriangleIndex(btVector3* triangle,int partId,int triangleIndex) + { + if (TestTriangleAgainstAabb2(&triangle[0],m_aabbMin,m_aabbMax)) + { + //check aabb in triangle-space, before doing this + m_callback->processTriangle(triangle,partId,triangleIndex); + } + + } + + }; + + FilteredCallback filterCallback(callback,aabbMin,aabbMax); + + m_meshInterface->InternalProcessAllTriangles(&filterCallback,aabbMin,aabbMax); +} + + + + + +void btTriangleMeshShape::calculateLocalInertia(btScalar mass,btVector3& inertia) const +{ + (void)mass; + //moving concave objects not supported + btAssert(0); + inertia.setValue(btScalar(0.),btScalar(0.),btScalar(0.)); +} + + +btVector3 btTriangleMeshShape::localGetSupportingVertex(const btVector3& vec) const +{ + btVector3 supportVertex; + + btTransform ident; + ident.setIdentity(); + + SupportVertexCallback supportCallback(vec,ident); + + btVector3 aabbMax(btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT)); + + processAllTriangles(&supportCallback,-aabbMax,aabbMax); + + supportVertex = supportCallback.GetSupportVertexLocal(); + + return supportVertex; +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleMeshShape.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleMeshShape.h new file mode 100644 index 000000000..4bb14841f --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleMeshShape.h @@ -0,0 +1,85 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 TRIANGLE_MESH_SHAPE_H +#define TRIANGLE_MESH_SHAPE_H + +#include "btConcaveShape.h" +#include "btStridingMeshInterface.h" + + +///The btTriangleMeshShape is an internal concave triangle mesh interface. Don't use this class directly, use btBvhTriangleMeshShape instead. +class btTriangleMeshShape : public btConcaveShape +{ +protected: + btVector3 m_localAabbMin; + btVector3 m_localAabbMax; + btStridingMeshInterface* m_meshInterface; + + ///btTriangleMeshShape constructor has been disabled/protected, so that users will not mistakenly use this class. + ///Don't use btTriangleMeshShape but use btBvhTriangleMeshShape instead! + btTriangleMeshShape(btStridingMeshInterface* meshInterface); + +public: + + virtual ~btTriangleMeshShape(); + + virtual btVector3 localGetSupportingVertex(const btVector3& vec) const; + + virtual btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec)const + { + btAssert(0); + return localGetSupportingVertex(vec); + } + + void recalcLocalAabb(); + + virtual void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const; + + virtual void processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const; + + virtual void calculateLocalInertia(btScalar mass,btVector3& inertia) const; + + virtual void setLocalScaling(const btVector3& scaling); + virtual const btVector3& getLocalScaling() const; + + btStridingMeshInterface* getMeshInterface() + { + return m_meshInterface; + } + + const btStridingMeshInterface* getMeshInterface() const + { + return m_meshInterface; + } + + const btVector3& getLocalAabbMin() const + { + return m_localAabbMin; + } + const btVector3& getLocalAabbMax() const + { + return m_localAabbMax; + } + + + + //debugging + virtual const char* getName()const {return "TRIANGLEMESH";} + + +}; + +#endif //TRIANGLE_MESH_SHAPE_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleShape.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleShape.h new file mode 100644 index 000000000..847147cf6 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleShape.h @@ -0,0 +1,182 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 OBB_TRIANGLE_MINKOWSKI_H +#define OBB_TRIANGLE_MINKOWSKI_H + +#include "btConvexShape.h" +#include "btBoxShape.h" + +ATTRIBUTE_ALIGNED16(class) btTriangleShape : public btPolyhedralConvexShape +{ + + +public: + + btVector3 m_vertices1[3]; + + virtual int getNumVertices() const + { + return 3; + } + + btVector3& getVertexPtr(int index) + { + return m_vertices1[index]; + } + + const btVector3& getVertexPtr(int index) const + { + return m_vertices1[index]; + } + virtual void getVertex(int index,btVector3& vert) const + { + vert = m_vertices1[index]; + } + + virtual int getNumEdges() const + { + return 3; + } + + virtual void getEdge(int i,btVector3& pa,btVector3& pb) const + { + getVertex(i,pa); + getVertex((i+1)%3,pb); + } + + + virtual void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax)const + { +// btAssert(0); + getAabbSlow(t,aabbMin,aabbMax); + } + + btVector3 localGetSupportingVertexWithoutMargin(const btVector3& dir)const + { + btVector3 dots(dir.dot(m_vertices1[0]), dir.dot(m_vertices1[1]), dir.dot(m_vertices1[2])); + return m_vertices1[dots.maxAxis()]; + + } + + virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const + { + for (int i=0;i= -tolerance && dist <= tolerance) + { + //inside check on edge-planes + int i; + for (i=0;i<3;i++) + { + btVector3 pa,pb; + getEdge(i,pa,pb); + btVector3 edge = pb-pa; + btVector3 edgeNormal = edge.cross(normal); + edgeNormal.normalize(); + btScalar dist = pt.dot( edgeNormal); + btScalar edgeConst = pa.dot(edgeNormal); + dist -= edgeConst; + if (dist < -tolerance) + return false; + } + + return true; + } + + return false; + } + //debugging + virtual const char* getName()const + { + return "Triangle"; + } + + virtual int getNumPreferredPenetrationDirections() const + { + return 2; + } + + virtual void getPreferredPenetrationDirection(int index, btVector3& penetrationVector) const + { + calcNormal(penetrationVector); + if (index) + penetrationVector *= btScalar(-1.); + } + + +}; + +#endif //OBB_TRIANGLE_MINKOWSKI_H + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btUniformScalingShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btUniformScalingShape.cpp new file mode 100644 index 000000000..8e86f6bf2 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btUniformScalingShape.cpp @@ -0,0 +1,115 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 "btUniformScalingShape.h" + +btUniformScalingShape::btUniformScalingShape( btConvexShape* convexChildShape,btScalar uniformScalingFactor): +btConvexShape (), m_childConvexShape(convexChildShape), +m_uniformScalingFactor(uniformScalingFactor) +{ + m_shapeType = UNIFORM_SCALING_SHAPE_PROXYTYPE; +} + +btUniformScalingShape::~btUniformScalingShape() +{ +} + + +btVector3 btUniformScalingShape::localGetSupportingVertexWithoutMargin(const btVector3& vec)const +{ + btVector3 tmpVertex; + tmpVertex = m_childConvexShape->localGetSupportingVertexWithoutMargin(vec); + return tmpVertex*m_uniformScalingFactor; +} + +void btUniformScalingShape::batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const +{ + m_childConvexShape->batchedUnitVectorGetSupportingVertexWithoutMargin(vectors,supportVerticesOut,numVectors); + int i; + for (i=0;ilocalGetSupportingVertex(vec); + return tmpVertex*m_uniformScalingFactor; +} + + +void btUniformScalingShape::calculateLocalInertia(btScalar mass,btVector3& inertia) const +{ + + ///this linear upscaling is not realistic, but we don't deal with large mass ratios... + btVector3 tmpInertia; + m_childConvexShape->calculateLocalInertia(mass,tmpInertia); + inertia = tmpInertia * m_uniformScalingFactor; +} + + + ///getAabb's default implementation is brute force, expected derived classes to implement a fast dedicated version +void btUniformScalingShape::getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const +{ + m_childConvexShape->getAabb(t,aabbMin,aabbMax); + btVector3 aabbCenter = (aabbMax+aabbMin)*btScalar(0.5); + btVector3 scaledAabbHalfExtends = (aabbMax-aabbMin)*btScalar(0.5)*m_uniformScalingFactor; + + aabbMin = aabbCenter - scaledAabbHalfExtends; + aabbMax = aabbCenter + scaledAabbHalfExtends; + +} + +void btUniformScalingShape::getAabbSlow(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const +{ + m_childConvexShape->getAabbSlow(t,aabbMin,aabbMax); + btVector3 aabbCenter = (aabbMax+aabbMin)*btScalar(0.5); + btVector3 scaledAabbHalfExtends = (aabbMax-aabbMin)*btScalar(0.5)*m_uniformScalingFactor; + + aabbMin = aabbCenter - scaledAabbHalfExtends; + aabbMax = aabbCenter + scaledAabbHalfExtends; +} + +void btUniformScalingShape::setLocalScaling(const btVector3& scaling) +{ + m_childConvexShape->setLocalScaling(scaling); +} + +const btVector3& btUniformScalingShape::getLocalScaling() const +{ + return m_childConvexShape->getLocalScaling(); +} + +void btUniformScalingShape::setMargin(btScalar margin) +{ + m_childConvexShape->setMargin(margin); +} +btScalar btUniformScalingShape::getMargin() const +{ + return m_childConvexShape->getMargin() * m_uniformScalingFactor; +} + +int btUniformScalingShape::getNumPreferredPenetrationDirections() const +{ + return m_childConvexShape->getNumPreferredPenetrationDirections(); +} + +void btUniformScalingShape::getPreferredPenetrationDirection(int index, btVector3& penetrationVector) const +{ + m_childConvexShape->getPreferredPenetrationDirection(index,penetrationVector); +} diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btUniformScalingShape.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btUniformScalingShape.h new file mode 100644 index 000000000..cbf7e6fd3 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btUniformScalingShape.h @@ -0,0 +1,87 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 BT_UNIFORM_SCALING_SHAPE_H +#define BT_UNIFORM_SCALING_SHAPE_H + +#include "btConvexShape.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" // for the types + +///The btUniformScalingShape allows to re-use uniform scaled instances of btConvexShape in a memory efficient way. +///Istead of using btUniformScalingShape, it is better to use the non-uniform setLocalScaling method on convex shapes that implement it. +class btUniformScalingShape : public btConvexShape +{ + btConvexShape* m_childConvexShape; + + btScalar m_uniformScalingFactor; + + public: + + btUniformScalingShape( btConvexShape* convexChildShape, btScalar uniformScalingFactor); + + virtual ~btUniformScalingShape(); + + virtual btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec)const; + + virtual btVector3 localGetSupportingVertex(const btVector3& vec)const; + + virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const; + + virtual void calculateLocalInertia(btScalar mass,btVector3& inertia) const; + + btScalar getUniformScalingFactor() const + { + return m_uniformScalingFactor; + } + + btConvexShape* getChildShape() + { + return m_childConvexShape; + } + + const btConvexShape* getChildShape() const + { + return m_childConvexShape; + } + + virtual const char* getName()const + { + return "UniformScalingShape"; + } + + + + /////////////////////////// + + + ///getAabb's default implementation is brute force, expected derived classes to implement a fast dedicated version + void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const; + + virtual void getAabbSlow(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const; + + virtual void setLocalScaling(const btVector3& scaling) ; + virtual const btVector3& getLocalScaling() const ; + + virtual void setMargin(btScalar margin); + virtual btScalar getMargin() const; + + virtual int getNumPreferredPenetrationDirections() const; + + virtual void getPreferredPenetrationDirection(int index, btVector3& penetrationVector) const; + + +}; + +#endif //BT_UNIFORM_SCALING_SHAPE_H diff --git a/Engine/lib/bullet/src/BulletCollision/Doxyfile b/Engine/lib/bullet/src/BulletCollision/Doxyfile new file mode 100644 index 000000000..4ecb6acb6 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Doxyfile @@ -0,0 +1,746 @@ +# Doxyfile 1.2.4 + +# This file describes the settings to be used by doxygen for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. +PROJECT_NAME = "Bullet Continuous Collision Detection Library" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Dutch, French, Italian, Czech, Swedish, German, Finnish, Japanese, +# Korean, Hungarian, Norwegian, Spanish, Romanian, Russian, Croatian, +# Polish, Portuguese and Slovene. + +OUTPUT_LANGUAGE = English + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these class will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. It is allowed to use relative paths in the argument list. + +STRIP_FROM_PATH = + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a class diagram (in Html and LaTeX) for classes with base or +# super classes. Setting the tag to NO turns the diagrams off. + +CLASS_DIAGRAMS = YES + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower case letters. If set to YES upper case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# users are adviced to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explict @brief command for a brief description. + +JAVADOC_AUTOBRIEF = YES + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# reimplements. + +INHERIT_DOCS = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# The ENABLE_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = . + + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +FILE_PATTERNS = *.h *.cpp *.c + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. + +INPUT_FILTER = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse. + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side pannel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript and frames is required (for instance Netscape 4.0+ +# or Internet explorer 4.0+). + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimised for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using a WORD or other. +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assigments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. Warning: This feature +# is still experimental and very incomplete. + +GENERATE_XML = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_PREDEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = ../../generic/extern + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_PREDEF_ONLY tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +#--------------------------------------------------------------------------- +# Configuration::addtions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tagfiles. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the ENABLE_PREPROCESSING, INCLUDE_GRAPH, and HAVE_DOT tags are set to +# YES then doxygen will generate a graph for each documented file showing +# the direct and indirect include dependencies of the file with other +# documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, INCLUDED_BY_GRAPH, and HAVE_DOT tags are set to +# YES then doxygen will generate a graph for each documented header file showing +# the documented files that directly or indirectly include this file + +INCLUDED_BY_GRAPH = YES + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found on the path. + +DOT_PATH = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO + +# The CGI_NAME tag should be the name of the CGI script that +# starts the search engine (doxysearch) with the correct parameters. +# A script with this name will be generated by doxygen. + +CGI_NAME = search.cgi + +# The CGI_URL tag should be the absolute URL to the directory where the +# cgi binaries are located. See the documentation of your http daemon for +# details. + +CGI_URL = + +# The DOC_URL tag should be the absolute URL to the directory where the +# documentation is located. If left blank the absolute path to the +# documentation, with file:// prepended to it, will be used. + +DOC_URL = + +# The DOC_ABSPATH tag should be the absolute path to the directory where the +# documentation is located. If left blank the directory on the local machine +# will be used. + +DOC_ABSPATH = + +# The BIN_ABSPATH tag must point to the directory where the doxysearch binary +# is installed. + +BIN_ABSPATH = c:\program files\doxygen\bin + +# The EXT_DOC_PATHS tag can be used to specify one or more paths to +# documentation generated for other projects. This allows doxysearch to search +# the documentation for these projects as well. + +EXT_DOC_PATHS = diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/btBoxCollision.h b/Engine/lib/bullet/src/BulletCollision/Gimpact/btBoxCollision.h new file mode 100644 index 000000000..827a3c895 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/btBoxCollision.h @@ -0,0 +1,647 @@ +#ifndef BT_BOX_COLLISION_H_INCLUDED +#define BT_BOX_COLLISION_H_INCLUDED + +/*! \file gim_box_collision.h +\author Francisco Len Nßjera +*/ +/* +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2007 Francisco Leon Najera. C.C. 80087371. +email: projectileman@yahoo.com + + +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 "LinearMath/btTransform.h" + + +///Swap numbers +#define BT_SWAP_NUMBERS(a,b){ \ + a = a+b; \ + b = a-b; \ + a = a-b; \ +}\ + + +#define BT_MAX(a,b) (ab?b:a) + +#define BT_GREATER(x, y) btFabs(x) > (y) + +#define BT_MAX3(a,b,c) BT_MAX(a,BT_MAX(b,c)) +#define BT_MIN3(a,b,c) BT_MIN(a,BT_MIN(b,c)) + + + + + + +enum eBT_PLANE_INTERSECTION_TYPE +{ + BT_CONST_BACK_PLANE = 0, + BT_CONST_COLLIDE_PLANE, + BT_CONST_FRONT_PLANE +}; + +//SIMD_FORCE_INLINE bool test_cross_edge_box( +// const btVector3 & edge, +// const btVector3 & absolute_edge, +// const btVector3 & pointa, +// const btVector3 & pointb, const btVector3 & extend, +// int dir_index0, +// int dir_index1 +// int component_index0, +// int component_index1) +//{ +// // dir coords are -z and y +// +// const btScalar dir0 = -edge[dir_index0]; +// const btScalar dir1 = edge[dir_index1]; +// btScalar pmin = pointa[component_index0]*dir0 + pointa[component_index1]*dir1; +// btScalar pmax = pointb[component_index0]*dir0 + pointb[component_index1]*dir1; +// //find minmax +// if(pmin>pmax) +// { +// BT_SWAP_NUMBERS(pmin,pmax); +// } +// //find extends +// const btScalar rad = extend[component_index0] * absolute_edge[dir_index0] + +// extend[component_index1] * absolute_edge[dir_index1]; +// +// if(pmin>rad || -rad>pmax) return false; +// return true; +//} +// +//SIMD_FORCE_INLINE bool test_cross_edge_box_X_axis( +// const btVector3 & edge, +// const btVector3 & absolute_edge, +// const btVector3 & pointa, +// const btVector3 & pointb, btVector3 & extend) +//{ +// +// return test_cross_edge_box(edge,absolute_edge,pointa,pointb,extend,2,1,1,2); +//} +// +// +//SIMD_FORCE_INLINE bool test_cross_edge_box_Y_axis( +// const btVector3 & edge, +// const btVector3 & absolute_edge, +// const btVector3 & pointa, +// const btVector3 & pointb, btVector3 & extend) +//{ +// +// return test_cross_edge_box(edge,absolute_edge,pointa,pointb,extend,0,2,2,0); +//} +// +//SIMD_FORCE_INLINE bool test_cross_edge_box_Z_axis( +// const btVector3 & edge, +// const btVector3 & absolute_edge, +// const btVector3 & pointa, +// const btVector3 & pointb, btVector3 & extend) +//{ +// +// return test_cross_edge_box(edge,absolute_edge,pointa,pointb,extend,1,0,0,1); +//} + + +#define TEST_CROSS_EDGE_BOX_MCR(edge,absolute_edge,pointa,pointb,_extend,i_dir_0,i_dir_1,i_comp_0,i_comp_1)\ +{\ + const btScalar dir0 = -edge[i_dir_0];\ + const btScalar dir1 = edge[i_dir_1];\ + btScalar pmin = pointa[i_comp_0]*dir0 + pointa[i_comp_1]*dir1;\ + btScalar pmax = pointb[i_comp_0]*dir0 + pointb[i_comp_1]*dir1;\ + if(pmin>pmax)\ + {\ + BT_SWAP_NUMBERS(pmin,pmax); \ + }\ + const btScalar abs_dir0 = absolute_edge[i_dir_0];\ + const btScalar abs_dir1 = absolute_edge[i_dir_1];\ + const btScalar rad = _extend[i_comp_0] * abs_dir0 + _extend[i_comp_1] * abs_dir1;\ + if(pmin>rad || -rad>pmax) return false;\ +}\ + + +#define TEST_CROSS_EDGE_BOX_X_AXIS_MCR(edge,absolute_edge,pointa,pointb,_extend)\ +{\ + TEST_CROSS_EDGE_BOX_MCR(edge,absolute_edge,pointa,pointb,_extend,2,1,1,2);\ +}\ + +#define TEST_CROSS_EDGE_BOX_Y_AXIS_MCR(edge,absolute_edge,pointa,pointb,_extend)\ +{\ + TEST_CROSS_EDGE_BOX_MCR(edge,absolute_edge,pointa,pointb,_extend,0,2,2,0);\ +}\ + +#define TEST_CROSS_EDGE_BOX_Z_AXIS_MCR(edge,absolute_edge,pointa,pointb,_extend)\ +{\ + TEST_CROSS_EDGE_BOX_MCR(edge,absolute_edge,pointa,pointb,_extend,1,0,0,1);\ +}\ + + +//! Returns the dot product between a vec3f and the col of a matrix +SIMD_FORCE_INLINE btScalar bt_mat3_dot_col( +const btMatrix3x3 & mat, const btVector3 & vec3, int colindex) +{ + return vec3[0]*mat[0][colindex] + vec3[1]*mat[1][colindex] + vec3[2]*mat[2][colindex]; +} + + +//! Class for transforming a model1 to the space of model0 +ATTRIBUTE_ALIGNED16 (class) BT_BOX_BOX_TRANSFORM_CACHE +{ +public: + btVector3 m_T1to0;//!< Transforms translation of model1 to model 0 + btMatrix3x3 m_R1to0;//!< Transforms Rotation of model1 to model 0, equal to R0' * R1 + btMatrix3x3 m_AR;//!< Absolute value of m_R1to0 + + SIMD_FORCE_INLINE void calc_absolute_matrix() + { +// static const btVector3 vepsi(1e-6f,1e-6f,1e-6f); +// m_AR[0] = vepsi + m_R1to0[0].absolute(); +// m_AR[1] = vepsi + m_R1to0[1].absolute(); +// m_AR[2] = vepsi + m_R1to0[2].absolute(); + + int i,j; + + for(i=0;i<3;i++) + { + for(j=0;j<3;j++ ) + { + m_AR[i][j] = 1e-6f + btFabs(m_R1to0[i][j]); + } + } + + } + + BT_BOX_BOX_TRANSFORM_CACHE() + { + } + + + + //! Calc the transformation relative 1 to 0. Inverts matrics by transposing + SIMD_FORCE_INLINE void calc_from_homogenic(const btTransform & trans0,const btTransform & trans1) + { + + btTransform temp_trans = trans0.inverse(); + temp_trans = temp_trans * trans1; + + m_T1to0 = temp_trans.getOrigin(); + m_R1to0 = temp_trans.getBasis(); + + + calc_absolute_matrix(); + } + + //! Calcs the full invertion of the matrices. Useful for scaling matrices + SIMD_FORCE_INLINE void calc_from_full_invert(const btTransform & trans0,const btTransform & trans1) + { + m_R1to0 = trans0.getBasis().inverse(); + m_T1to0 = m_R1to0 * (-trans0.getOrigin()); + + m_T1to0 += m_R1to0*trans1.getOrigin(); + m_R1to0 *= trans1.getBasis(); + + calc_absolute_matrix(); + } + + SIMD_FORCE_INLINE btVector3 transform(const btVector3 & point) const + { + return btVector3(m_R1to0[0].dot(point) + m_T1to0.x(), + m_R1to0[1].dot(point) + m_T1to0.y(), + m_R1to0[2].dot(point) + m_T1to0.z()); + } +}; + + +#define BOX_PLANE_EPSILON 0.000001f + +//! Axis aligned box +ATTRIBUTE_ALIGNED16 (class) btAABB +{ +public: + btVector3 m_min; + btVector3 m_max; + + btAABB() + {} + + + btAABB(const btVector3 & V1, + const btVector3 & V2, + const btVector3 & V3) + { + m_min[0] = BT_MIN3(V1[0],V2[0],V3[0]); + m_min[1] = BT_MIN3(V1[1],V2[1],V3[1]); + m_min[2] = BT_MIN3(V1[2],V2[2],V3[2]); + + m_max[0] = BT_MAX3(V1[0],V2[0],V3[0]); + m_max[1] = BT_MAX3(V1[1],V2[1],V3[1]); + m_max[2] = BT_MAX3(V1[2],V2[2],V3[2]); + } + + btAABB(const btVector3 & V1, + const btVector3 & V2, + const btVector3 & V3, + btScalar margin) + { + m_min[0] = BT_MIN3(V1[0],V2[0],V3[0]); + m_min[1] = BT_MIN3(V1[1],V2[1],V3[1]); + m_min[2] = BT_MIN3(V1[2],V2[2],V3[2]); + + m_max[0] = BT_MAX3(V1[0],V2[0],V3[0]); + m_max[1] = BT_MAX3(V1[1],V2[1],V3[1]); + m_max[2] = BT_MAX3(V1[2],V2[2],V3[2]); + + m_min[0] -= margin; + m_min[1] -= margin; + m_min[2] -= margin; + m_max[0] += margin; + m_max[1] += margin; + m_max[2] += margin; + } + + btAABB(const btAABB &other): + m_min(other.m_min),m_max(other.m_max) + { + } + + btAABB(const btAABB &other,btScalar margin ): + m_min(other.m_min),m_max(other.m_max) + { + m_min[0] -= margin; + m_min[1] -= margin; + m_min[2] -= margin; + m_max[0] += margin; + m_max[1] += margin; + m_max[2] += margin; + } + + SIMD_FORCE_INLINE void invalidate() + { + m_min[0] = SIMD_INFINITY; + m_min[1] = SIMD_INFINITY; + m_min[2] = SIMD_INFINITY; + m_max[0] = -SIMD_INFINITY; + m_max[1] = -SIMD_INFINITY; + m_max[2] = -SIMD_INFINITY; + } + + SIMD_FORCE_INLINE void increment_margin(btScalar margin) + { + m_min[0] -= margin; + m_min[1] -= margin; + m_min[2] -= margin; + m_max[0] += margin; + m_max[1] += margin; + m_max[2] += margin; + } + + SIMD_FORCE_INLINE void copy_with_margin(const btAABB &other, btScalar margin) + { + m_min[0] = other.m_min[0] - margin; + m_min[1] = other.m_min[1] - margin; + m_min[2] = other.m_min[2] - margin; + + m_max[0] = other.m_max[0] + margin; + m_max[1] = other.m_max[1] + margin; + m_max[2] = other.m_max[2] + margin; + } + + template + SIMD_FORCE_INLINE void calc_from_triangle( + const CLASS_POINT & V1, + const CLASS_POINT & V2, + const CLASS_POINT & V3) + { + m_min[0] = BT_MIN3(V1[0],V2[0],V3[0]); + m_min[1] = BT_MIN3(V1[1],V2[1],V3[1]); + m_min[2] = BT_MIN3(V1[2],V2[2],V3[2]); + + m_max[0] = BT_MAX3(V1[0],V2[0],V3[0]); + m_max[1] = BT_MAX3(V1[1],V2[1],V3[1]); + m_max[2] = BT_MAX3(V1[2],V2[2],V3[2]); + } + + template + SIMD_FORCE_INLINE void calc_from_triangle_margin( + const CLASS_POINT & V1, + const CLASS_POINT & V2, + const CLASS_POINT & V3, btScalar margin) + { + m_min[0] = BT_MIN3(V1[0],V2[0],V3[0]); + m_min[1] = BT_MIN3(V1[1],V2[1],V3[1]); + m_min[2] = BT_MIN3(V1[2],V2[2],V3[2]); + + m_max[0] = BT_MAX3(V1[0],V2[0],V3[0]); + m_max[1] = BT_MAX3(V1[1],V2[1],V3[1]); + m_max[2] = BT_MAX3(V1[2],V2[2],V3[2]); + + m_min[0] -= margin; + m_min[1] -= margin; + m_min[2] -= margin; + m_max[0] += margin; + m_max[1] += margin; + m_max[2] += margin; + } + + //! Apply a transform to an AABB + SIMD_FORCE_INLINE void appy_transform(const btTransform & trans) + { + btVector3 center = (m_max+m_min)*0.5f; + btVector3 extends = m_max - center; + // Compute new center + center = trans(center); + + btVector3 textends(extends.dot(trans.getBasis().getRow(0).absolute()), + extends.dot(trans.getBasis().getRow(1).absolute()), + extends.dot(trans.getBasis().getRow(2).absolute())); + + m_min = center - textends; + m_max = center + textends; + } + + + //! Apply a transform to an AABB + SIMD_FORCE_INLINE void appy_transform_trans_cache(const BT_BOX_BOX_TRANSFORM_CACHE & trans) + { + btVector3 center = (m_max+m_min)*0.5f; + btVector3 extends = m_max - center; + // Compute new center + center = trans.transform(center); + + btVector3 textends(extends.dot(trans.m_R1to0.getRow(0).absolute()), + extends.dot(trans.m_R1to0.getRow(1).absolute()), + extends.dot(trans.m_R1to0.getRow(2).absolute())); + + m_min = center - textends; + m_max = center + textends; + } + + //! Merges a Box + SIMD_FORCE_INLINE void merge(const btAABB & box) + { + m_min[0] = BT_MIN(m_min[0],box.m_min[0]); + m_min[1] = BT_MIN(m_min[1],box.m_min[1]); + m_min[2] = BT_MIN(m_min[2],box.m_min[2]); + + m_max[0] = BT_MAX(m_max[0],box.m_max[0]); + m_max[1] = BT_MAX(m_max[1],box.m_max[1]); + m_max[2] = BT_MAX(m_max[2],box.m_max[2]); + } + + //! Merges a point + template + SIMD_FORCE_INLINE void merge_point(const CLASS_POINT & point) + { + m_min[0] = BT_MIN(m_min[0],point[0]); + m_min[1] = BT_MIN(m_min[1],point[1]); + m_min[2] = BT_MIN(m_min[2],point[2]); + + m_max[0] = BT_MAX(m_max[0],point[0]); + m_max[1] = BT_MAX(m_max[1],point[1]); + m_max[2] = BT_MAX(m_max[2],point[2]); + } + + //! Gets the extend and center + SIMD_FORCE_INLINE void get_center_extend(btVector3 & center,btVector3 & extend) const + { + center = (m_max+m_min)*0.5f; + extend = m_max - center; + } + + //! Finds the intersecting box between this box and the other. + SIMD_FORCE_INLINE void find_intersection(const btAABB & other, btAABB & intersection) const + { + intersection.m_min[0] = BT_MAX(other.m_min[0],m_min[0]); + intersection.m_min[1] = BT_MAX(other.m_min[1],m_min[1]); + intersection.m_min[2] = BT_MAX(other.m_min[2],m_min[2]); + + intersection.m_max[0] = BT_MIN(other.m_max[0],m_max[0]); + intersection.m_max[1] = BT_MIN(other.m_max[1],m_max[1]); + intersection.m_max[2] = BT_MIN(other.m_max[2],m_max[2]); + } + + + SIMD_FORCE_INLINE bool has_collision(const btAABB & other) const + { + if(m_min[0] > other.m_max[0] || + m_max[0] < other.m_min[0] || + m_min[1] > other.m_max[1] || + m_max[1] < other.m_min[1] || + m_min[2] > other.m_max[2] || + m_max[2] < other.m_min[2]) + { + return false; + } + return true; + } + + /*! \brief Finds the Ray intersection parameter. + \param aabb Aligned box + \param vorigin A vec3f with the origin of the ray + \param vdir A vec3f with the direction of the ray + */ + SIMD_FORCE_INLINE bool collide_ray(const btVector3 & vorigin,const btVector3 & vdir) const + { + btVector3 extents,center; + this->get_center_extend(center,extents);; + + btScalar Dx = vorigin[0] - center[0]; + if(BT_GREATER(Dx, extents[0]) && Dx*vdir[0]>=0.0f) return false; + btScalar Dy = vorigin[1] - center[1]; + if(BT_GREATER(Dy, extents[1]) && Dy*vdir[1]>=0.0f) return false; + btScalar Dz = vorigin[2] - center[2]; + if(BT_GREATER(Dz, extents[2]) && Dz*vdir[2]>=0.0f) return false; + + + btScalar f = vdir[1] * Dz - vdir[2] * Dy; + if(btFabs(f) > extents[1]*btFabs(vdir[2]) + extents[2]*btFabs(vdir[1])) return false; + f = vdir[2] * Dx - vdir[0] * Dz; + if(btFabs(f) > extents[0]*btFabs(vdir[2]) + extents[2]*btFabs(vdir[0]))return false; + f = vdir[0] * Dy - vdir[1] * Dx; + if(btFabs(f) > extents[0]*btFabs(vdir[1]) + extents[1]*btFabs(vdir[0]))return false; + return true; + } + + + SIMD_FORCE_INLINE void projection_interval(const btVector3 & direction, btScalar &vmin, btScalar &vmax) const + { + btVector3 center = (m_max+m_min)*0.5f; + btVector3 extend = m_max-center; + + btScalar _fOrigin = direction.dot(center); + btScalar _fMaximumExtent = extend.dot(direction.absolute()); + vmin = _fOrigin - _fMaximumExtent; + vmax = _fOrigin + _fMaximumExtent; + } + + SIMD_FORCE_INLINE eBT_PLANE_INTERSECTION_TYPE plane_classify(const btVector4 &plane) const + { + btScalar _fmin,_fmax; + this->projection_interval(plane,_fmin,_fmax); + + if(plane[3] > _fmax + BOX_PLANE_EPSILON) + { + return BT_CONST_BACK_PLANE; // 0 + } + + if(plane[3]+BOX_PLANE_EPSILON >=_fmin) + { + return BT_CONST_COLLIDE_PLANE; //1 + } + return BT_CONST_FRONT_PLANE;//2 + } + + SIMD_FORCE_INLINE bool overlapping_trans_conservative(const btAABB & box, btTransform & trans1_to_0) const + { + btAABB tbox = box; + tbox.appy_transform(trans1_to_0); + return has_collision(tbox); + } + + SIMD_FORCE_INLINE bool overlapping_trans_conservative2(const btAABB & box, + const BT_BOX_BOX_TRANSFORM_CACHE & trans1_to_0) const + { + btAABB tbox = box; + tbox.appy_transform_trans_cache(trans1_to_0); + return has_collision(tbox); + } + + //! transcache is the transformation cache from box to this AABB + SIMD_FORCE_INLINE bool overlapping_trans_cache( + const btAABB & box,const BT_BOX_BOX_TRANSFORM_CACHE & transcache, bool fulltest) const + { + + //Taken from OPCODE + btVector3 ea,eb;//extends + btVector3 ca,cb;//extends + get_center_extend(ca,ea); + box.get_center_extend(cb,eb); + + + btVector3 T; + btScalar t,t2; + int i; + + // Class I : A's basis vectors + for(i=0;i<3;i++) + { + T[i] = transcache.m_R1to0[i].dot(cb) + transcache.m_T1to0[i] - ca[i]; + t = transcache.m_AR[i].dot(eb) + ea[i]; + if(BT_GREATER(T[i], t)) return false; + } + // Class II : B's basis vectors + for(i=0;i<3;i++) + { + t = bt_mat3_dot_col(transcache.m_R1to0,T,i); + t2 = bt_mat3_dot_col(transcache.m_AR,ea,i) + eb[i]; + if(BT_GREATER(t,t2)) return false; + } + // Class III : 9 cross products + if(fulltest) + { + int j,m,n,o,p,q,r; + for(i=0;i<3;i++) + { + m = (i+1)%3; + n = (i+2)%3; + o = i==0?1:0; + p = i==2?1:2; + for(j=0;j<3;j++) + { + q = j==2?1:2; + r = j==0?1:0; + t = T[n]*transcache.m_R1to0[m][j] - T[m]*transcache.m_R1to0[n][j]; + t2 = ea[o]*transcache.m_AR[p][j] + ea[p]*transcache.m_AR[o][j] + + eb[r]*transcache.m_AR[i][q] + eb[q]*transcache.m_AR[i][r]; + if(BT_GREATER(t,t2)) return false; + } + } + } + return true; + } + + //! Simple test for planes. + SIMD_FORCE_INLINE bool collide_plane( + const btVector4 & plane) const + { + eBT_PLANE_INTERSECTION_TYPE classify = plane_classify(plane); + return (classify == BT_CONST_COLLIDE_PLANE); + } + + //! test for a triangle, with edges + SIMD_FORCE_INLINE bool collide_triangle_exact( + const btVector3 & p1, + const btVector3 & p2, + const btVector3 & p3, + const btVector4 & triangle_plane) const + { + if(!collide_plane(triangle_plane)) return false; + + btVector3 center,extends; + this->get_center_extend(center,extends); + + const btVector3 v1(p1 - center); + const btVector3 v2(p2 - center); + const btVector3 v3(p3 - center); + + //First axis + btVector3 diff(v2 - v1); + btVector3 abs_diff = diff.absolute(); + //Test With X axis + TEST_CROSS_EDGE_BOX_X_AXIS_MCR(diff,abs_diff,v1,v3,extends); + //Test With Y axis + TEST_CROSS_EDGE_BOX_Y_AXIS_MCR(diff,abs_diff,v1,v3,extends); + //Test With Z axis + TEST_CROSS_EDGE_BOX_Z_AXIS_MCR(diff,abs_diff,v1,v3,extends); + + + diff = v3 - v2; + abs_diff = diff.absolute(); + //Test With X axis + TEST_CROSS_EDGE_BOX_X_AXIS_MCR(diff,abs_diff,v2,v1,extends); + //Test With Y axis + TEST_CROSS_EDGE_BOX_Y_AXIS_MCR(diff,abs_diff,v2,v1,extends); + //Test With Z axis + TEST_CROSS_EDGE_BOX_Z_AXIS_MCR(diff,abs_diff,v2,v1,extends); + + diff = v1 - v3; + abs_diff = diff.absolute(); + //Test With X axis + TEST_CROSS_EDGE_BOX_X_AXIS_MCR(diff,abs_diff,v3,v2,extends); + //Test With Y axis + TEST_CROSS_EDGE_BOX_Y_AXIS_MCR(diff,abs_diff,v3,v2,extends); + //Test With Z axis + TEST_CROSS_EDGE_BOX_Z_AXIS_MCR(diff,abs_diff,v3,v2,extends); + + return true; + } +}; + + +//! Compairison of transformation objects +SIMD_FORCE_INLINE bool btCompareTransformsEqual(const btTransform & t1,const btTransform & t2) +{ + if(!(t1.getOrigin() == t2.getOrigin()) ) return false; + + if(!(t1.getBasis().getRow(0) == t2.getBasis().getRow(0)) ) return false; + if(!(t1.getBasis().getRow(1) == t2.getBasis().getRow(1)) ) return false; + if(!(t1.getBasis().getRow(2) == t2.getBasis().getRow(2)) ) return false; + return true; +} + + + +#endif // GIM_BOX_COLLISION_H_INCLUDED diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/btClipPolygon.h b/Engine/lib/bullet/src/BulletCollision/Gimpact/btClipPolygon.h new file mode 100644 index 000000000..5de391a75 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/btClipPolygon.h @@ -0,0 +1,182 @@ +#ifndef BT_CLIP_POLYGON_H_INCLUDED +#define BT_CLIP_POLYGON_H_INCLUDED + +/*! \file btClipPolygon.h +\author Francisco Len Nßjera +*/ +/* +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2007 Francisco Leon Najera. C.C. 80087371. +email: projectileman@yahoo.com + + +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 "LinearMath/btTransform.h" +#include "LinearMath/btGeometryUtil.h" + + +SIMD_FORCE_INLINE btScalar bt_distance_point_plane(const btVector4 & plane,const btVector3 &point) +{ + return point.dot(plane) - plane[3]; +} + +/*! Vector blending +Takes two vectors a, b, blends them together*/ +SIMD_FORCE_INLINE void bt_vec_blend(btVector3 &vr, const btVector3 &va,const btVector3 &vb, btScalar blend_factor) +{ + vr = (1-blend_factor)*va + blend_factor*vb; +} + +//! This function calcs the distance from a 3D plane +SIMD_FORCE_INLINE void bt_plane_clip_polygon_collect( + const btVector3 & point0, + const btVector3 & point1, + btScalar dist0, + btScalar dist1, + btVector3 * clipped, + int & clipped_count) +{ + bool _prevclassif = (dist0>SIMD_EPSILON); + bool _classif = (dist1>SIMD_EPSILON); + if(_classif!=_prevclassif) + { + btScalar blendfactor = -dist0/(dist1-dist0); + bt_vec_blend(clipped[clipped_count],point0,point1,blendfactor); + clipped_count++; + } + if(!_classif) + { + clipped[clipped_count] = point1; + clipped_count++; + } +} + + +//! Clips a polygon by a plane +/*! +*\return The count of the clipped counts +*/ +SIMD_FORCE_INLINE int bt_plane_clip_polygon( + const btVector4 & plane, + const btVector3 * polygon_points, + int polygon_point_count, + btVector3 * clipped) +{ + int clipped_count = 0; + + + //clip first point + btScalar firstdist = bt_distance_point_plane(plane,polygon_points[0]);; + if(!(firstdist>SIMD_EPSILON)) + { + clipped[clipped_count] = polygon_points[0]; + clipped_count++; + } + + btScalar olddist = firstdist; + for(int i=1;iSIMD_EPSILON)) + { + clipped[clipped_count] = point0; + clipped_count++; + } + + // point 1 + btScalar olddist = firstdist; + btScalar dist = bt_distance_point_plane(plane,point1); + + bt_plane_clip_polygon_collect( + point0,point1, + olddist, + dist, + clipped, + clipped_count); + + olddist = dist; + + + // point 2 + dist = bt_distance_point_plane(plane,point2); + + bt_plane_clip_polygon_collect( + point1,point2, + olddist, + dist, + clipped, + clipped_count); + olddist = dist; + + + + //RETURN TO FIRST point0 + bt_plane_clip_polygon_collect( + point2,point0, + olddist, + firstdist, + clipped, + clipped_count); + + return clipped_count; +} + + + + + +#endif // GIM_TRI_COLLISION_H_INCLUDED diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/btContactProcessing.cpp b/Engine/lib/bullet/src/BulletCollision/Gimpact/btContactProcessing.cpp new file mode 100644 index 000000000..c3b697bdd --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/btContactProcessing.cpp @@ -0,0 +1,181 @@ + +/* +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2007 Francisco Leon Najera. C.C. 80087371. +email: projectileman@yahoo.com + + +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 "btContactProcessing.h" + +#define MAX_COINCIDENT 8 + +struct CONTACT_KEY_TOKEN +{ + unsigned int m_key; + int m_value; + CONTACT_KEY_TOKEN() + { + } + + CONTACT_KEY_TOKEN(unsigned int key,int token) + { + m_key = key; + m_value = token; + } + + CONTACT_KEY_TOKEN(const CONTACT_KEY_TOKEN& rtoken) + { + m_key = rtoken.m_key; + m_value = rtoken.m_value; + } + + inline bool operator <(const CONTACT_KEY_TOKEN& other) const + { + return (m_key < other.m_key); + } + + inline bool operator >(const CONTACT_KEY_TOKEN& other) const + { + return (m_key > other.m_key); + } + +}; + +class CONTACT_KEY_TOKEN_COMP +{ + public: + + bool operator() ( const CONTACT_KEY_TOKEN& a, const CONTACT_KEY_TOKEN& b ) + { + return ( a < b ); + } +}; + + +void btContactArray::merge_contacts( + const btContactArray & contacts, bool normal_contact_average) +{ + clear(); + + int i; + if(contacts.size()==0) return; + + + if(contacts.size()==1) + { + push_back(contacts[0]); + return; + } + + btAlignedObjectArray keycontacts; + + keycontacts.reserve(contacts.size()); + + //fill key contacts + + for ( i = 0;im_depth - CONTACT_DIFF_EPSILON > scontact->m_depth)//) + { + *pcontact = *scontact; + coincident_count = 0; + } + else if(normal_contact_average) + { + if(btFabs(pcontact->m_depth - scontact->m_depth)m_normal; + coincident_count++; + } + } + } + } + else + {//add new contact + + if(normal_contact_average && coincident_count>0) + { + pcontact->interpolate_normals(coincident_normals,coincident_count); + coincident_count = 0; + } + + push_back(*scontact); + pcontact = &(*this)[this->size()-1]; + } + last_key = key; + } +} + +void btContactArray::merge_contacts_unique(const btContactArray & contacts) +{ + clear(); + + if(contacts.size()==0) return; + + if(contacts.size()==1) + { + push_back(contacts[0]); + return; + } + + GIM_CONTACT average_contact = contacts[0]; + + for (int i=1;i +{ +public: + btContactArray() + { + reserve(64); + } + + SIMD_FORCE_INLINE void push_contact( + const btVector3 &point,const btVector3 & normal, + btScalar depth, int feature1, int feature2) + { + push_back( GIM_CONTACT(point,normal,depth,feature1,feature2) ); + } + + SIMD_FORCE_INLINE void push_triangle_contacts( + const GIM_TRIANGLE_CONTACT & tricontact, + int feature1,int feature2) + { + for(int i = 0;i splitValue) + { + //swap + primitive_boxes.swap(i,splitIndex); + //swapLeafNodes(i,splitIndex); + splitIndex++; + } + } + + //if the splitIndex causes unbalanced trees, fix this by using the center in between startIndex and endIndex + //otherwise the tree-building might fail due to stack-overflows in certain cases. + //unbalanced1 is unsafe: it can cause stack overflows + //bool unbalanced1 = ((splitIndex==startIndex) || (splitIndex == (endIndex-1))); + + //unbalanced2 should work too: always use center (perfect balanced trees) + //bool unbalanced2 = true; + + //this should be safe too: + int rangeBalancedIndices = numIndices/3; + bool unbalanced = ((splitIndex<=(startIndex+rangeBalancedIndices)) || (splitIndex >=(endIndex-1-rangeBalancedIndices))); + + if (unbalanced) + { + splitIndex = startIndex+ (numIndices>>1); + } + + btAssert(!((splitIndex==startIndex) || (splitIndex == (endIndex)))); + + return splitIndex; + +} + + +void btBvhTree::_build_sub_tree(GIM_BVH_DATA_ARRAY & primitive_boxes, int startIndex, int endIndex) +{ + int curIndex = m_num_nodes; + m_num_nodes++; + + btAssert((endIndex-startIndex)>0); + + if ((endIndex-startIndex)==1) + { + //We have a leaf node + setNodeBound(curIndex,primitive_boxes[startIndex].m_bound); + m_node_array[curIndex].setDataIndex(primitive_boxes[startIndex].m_data); + + return; + } + //calculate Best Splitting Axis and where to split it. Sort the incoming 'leafNodes' array within range 'startIndex/endIndex'. + + //split axis + int splitIndex = _calc_splitting_axis(primitive_boxes,startIndex,endIndex); + + splitIndex = _sort_and_calc_splitting_index( + primitive_boxes,startIndex,endIndex, + splitIndex//split axis + ); + + + //calc this node bounding box + + btAABB node_bound; + node_bound.invalidate(); + + for (int i=startIndex;iget_primitive_box(getNodeData(nodecount),leafbox); + setNodeBound(nodecount,leafbox); + } + else + { + //const GIM_BVH_TREE_NODE * nodepointer = get_node_pointer(nodecount); + //get left bound + btAABB bound; + bound.invalidate(); + + btAABB temp_box; + + int child_node = getLeftNode(nodecount); + if(child_node) + { + getNodeBound(child_node,temp_box); + bound.merge(temp_box); + } + + child_node = getRightNode(nodecount); + if(child_node) + { + getNodeBound(child_node,temp_box); + bound.merge(temp_box); + } + + setNodeBound(nodecount,bound); + } + } +} + +//! this rebuild the entire set +void btGImpactBvh::buildSet() +{ + //obtain primitive boxes + GIM_BVH_DATA_ARRAY primitive_boxes; + primitive_boxes.resize(m_primitive_manager->get_primitive_count()); + + for (int i = 0;iget_primitive_box(i,primitive_boxes[i].m_bound); + primitive_boxes[i].m_data = i; + } + + m_box_tree.build_tree(primitive_boxes); +} + +//! returns the indices of the primitives in the m_primitive_manager +bool btGImpactBvh::boxQuery(const btAABB & box, btAlignedObjectArray & collided_results) const +{ + int curIndex = 0; + int numNodes = getNodeCount(); + + while (curIndex < numNodes) + { + btAABB bound; + getNodeBound(curIndex,bound); + + //catch bugs in tree data + + bool aabbOverlap = bound.has_collision(box); + bool isleafnode = isLeafNode(curIndex); + + if (isleafnode && aabbOverlap) + { + collided_results.push_back(getNodeData(curIndex)); + } + + if (aabbOverlap || isleafnode) + { + //next subnode + curIndex++; + } + else + { + //skip node + curIndex+= getEscapeNodeIndex(curIndex); + } + } + if(collided_results.size()>0) return true; + return false; +} + + + +//! returns the indices of the primitives in the m_primitive_manager +bool btGImpactBvh::rayQuery( + const btVector3 & ray_dir,const btVector3 & ray_origin , + btAlignedObjectArray & collided_results) const +{ + int curIndex = 0; + int numNodes = getNodeCount(); + + while (curIndex < numNodes) + { + btAABB bound; + getNodeBound(curIndex,bound); + + //catch bugs in tree data + + bool aabbOverlap = bound.collide_ray(ray_origin,ray_dir); + bool isleafnode = isLeafNode(curIndex); + + if (isleafnode && aabbOverlap) + { + collided_results.push_back(getNodeData( curIndex)); + } + + if (aabbOverlap || isleafnode) + { + //next subnode + curIndex++; + } + else + { + //skip node + curIndex+= getEscapeNodeIndex(curIndex); + } + } + if(collided_results.size()>0) return true; + return false; +} + + +SIMD_FORCE_INLINE bool _node_collision( + btGImpactBvh * boxset0, btGImpactBvh * boxset1, + const BT_BOX_BOX_TRANSFORM_CACHE & trans_cache_1to0, + int node0 ,int node1, bool complete_primitive_tests) +{ + btAABB box0; + boxset0->getNodeBound(node0,box0); + btAABB box1; + boxset1->getNodeBound(node1,box1); + + return box0.overlapping_trans_cache(box1,trans_cache_1to0,complete_primitive_tests ); +// box1.appy_transform_trans_cache(trans_cache_1to0); +// return box0.has_collision(box1); + +} + + +//stackless recursive collision routine +static void _find_collision_pairs_recursive( + btGImpactBvh * boxset0, btGImpactBvh * boxset1, + btPairSet * collision_pairs, + const BT_BOX_BOX_TRANSFORM_CACHE & trans_cache_1to0, + int node0, int node1, bool complete_primitive_tests) +{ + + + + if( _node_collision( + boxset0,boxset1,trans_cache_1to0, + node0,node1,complete_primitive_tests) ==false) return;//avoid colliding internal nodes + + if(boxset0->isLeafNode(node0)) + { + if(boxset1->isLeafNode(node1)) + { + // collision result + collision_pairs->push_pair( + boxset0->getNodeData(node0),boxset1->getNodeData(node1)); + return; + } + else + { + + //collide left recursive + + _find_collision_pairs_recursive( + boxset0,boxset1, + collision_pairs,trans_cache_1to0, + node0,boxset1->getLeftNode(node1),false); + + //collide right recursive + _find_collision_pairs_recursive( + boxset0,boxset1, + collision_pairs,trans_cache_1to0, + node0,boxset1->getRightNode(node1),false); + + + } + } + else + { + if(boxset1->isLeafNode(node1)) + { + + //collide left recursive + _find_collision_pairs_recursive( + boxset0,boxset1, + collision_pairs,trans_cache_1to0, + boxset0->getLeftNode(node0),node1,false); + + + //collide right recursive + + _find_collision_pairs_recursive( + boxset0,boxset1, + collision_pairs,trans_cache_1to0, + boxset0->getRightNode(node0),node1,false); + + + } + else + { + //collide left0 left1 + + + + _find_collision_pairs_recursive( + boxset0,boxset1, + collision_pairs,trans_cache_1to0, + boxset0->getLeftNode(node0),boxset1->getLeftNode(node1),false); + + //collide left0 right1 + + _find_collision_pairs_recursive( + boxset0,boxset1, + collision_pairs,trans_cache_1to0, + boxset0->getLeftNode(node0),boxset1->getRightNode(node1),false); + + + //collide right0 left1 + + _find_collision_pairs_recursive( + boxset0,boxset1, + collision_pairs,trans_cache_1to0, + boxset0->getRightNode(node0),boxset1->getLeftNode(node1),false); + + //collide right0 right1 + + _find_collision_pairs_recursive( + boxset0,boxset1, + collision_pairs,trans_cache_1to0, + boxset0->getRightNode(node0),boxset1->getRightNode(node1),false); + + }// else if node1 is not a leaf + }// else if node0 is not a leaf +} + + +void btGImpactBvh::find_collision(btGImpactBvh * boxset0, const btTransform & trans0, + btGImpactBvh * boxset1, const btTransform & trans1, + btPairSet & collision_pairs) +{ + + if(boxset0->getNodeCount()==0 || boxset1->getNodeCount()==0 ) return; + + BT_BOX_BOX_TRANSFORM_CACHE trans_cache_1to0; + + trans_cache_1to0.calc_from_homogenic(trans0,trans1); + +#ifdef TRI_COLLISION_PROFILING + bt_begin_gim02_tree_time(); +#endif //TRI_COLLISION_PROFILING + + _find_collision_pairs_recursive( + boxset0,boxset1, + &collision_pairs,trans_cache_1to0,0,0,true); +#ifdef TRI_COLLISION_PROFILING + bt_end_gim02_tree_time(); +#endif //TRI_COLLISION_PROFILING + +} + diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactBvh.h b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactBvh.h new file mode 100644 index 000000000..074de4a46 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactBvh.h @@ -0,0 +1,396 @@ +#ifndef GIM_BOX_SET_H_INCLUDED +#define GIM_BOX_SET_H_INCLUDED + +/*! \file gim_box_set.h +\author Francisco Len Nßjera +*/ +/* +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2007 Francisco Leon Najera. C.C. 80087371. +email: projectileman@yahoo.com + + +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 "LinearMath/btAlignedObjectArray.h" + +#include "btBoxCollision.h" +#include "btTriangleShapeEx.h" + + + + + +//! Overlapping pair +struct GIM_PAIR +{ + int m_index1; + int m_index2; + GIM_PAIR() + {} + + GIM_PAIR(const GIM_PAIR & p) + { + m_index1 = p.m_index1; + m_index2 = p.m_index2; + } + + GIM_PAIR(int index1, int index2) + { + m_index1 = index1; + m_index2 = index2; + } +}; + +//! A pairset array +class btPairSet: public btAlignedObjectArray +{ +public: + btPairSet() + { + reserve(32); + } + inline void push_pair(int index1,int index2) + { + push_back(GIM_PAIR(index1,index2)); + } + + inline void push_pair_inv(int index1,int index2) + { + push_back(GIM_PAIR(index2,index1)); + } +}; + + +///GIM_BVH_DATA is an internal GIMPACT collision structure to contain axis aligned bounding box +struct GIM_BVH_DATA +{ + btAABB m_bound; + int m_data; +}; + +//! Node Structure for trees +class GIM_BVH_TREE_NODE +{ +public: + btAABB m_bound; +protected: + int m_escapeIndexOrDataIndex; +public: + GIM_BVH_TREE_NODE() + { + m_escapeIndexOrDataIndex = 0; + } + + SIMD_FORCE_INLINE bool isLeafNode() const + { + //skipindex is negative (internal node), triangleindex >=0 (leafnode) + return (m_escapeIndexOrDataIndex>=0); + } + + SIMD_FORCE_INLINE int getEscapeIndex() const + { + //btAssert(m_escapeIndexOrDataIndex < 0); + return -m_escapeIndexOrDataIndex; + } + + SIMD_FORCE_INLINE void setEscapeIndex(int index) + { + m_escapeIndexOrDataIndex = -index; + } + + SIMD_FORCE_INLINE int getDataIndex() const + { + //btAssert(m_escapeIndexOrDataIndex >= 0); + + return m_escapeIndexOrDataIndex; + } + + SIMD_FORCE_INLINE void setDataIndex(int index) + { + m_escapeIndexOrDataIndex = index; + } + +}; + + +class GIM_BVH_DATA_ARRAY:public btAlignedObjectArray +{ +}; + + +class GIM_BVH_TREE_NODE_ARRAY:public btAlignedObjectArray +{ +}; + + + + +//! Basic Box tree structure +class btBvhTree +{ +protected: + int m_num_nodes; + GIM_BVH_TREE_NODE_ARRAY m_node_array; +protected: + int _sort_and_calc_splitting_index( + GIM_BVH_DATA_ARRAY & primitive_boxes, + int startIndex, int endIndex, int splitAxis); + + int _calc_splitting_axis(GIM_BVH_DATA_ARRAY & primitive_boxes, int startIndex, int endIndex); + + void _build_sub_tree(GIM_BVH_DATA_ARRAY & primitive_boxes, int startIndex, int endIndex); +public: + btBvhTree() + { + m_num_nodes = 0; + } + + //! prototype functions for box tree management + //!@{ + void build_tree(GIM_BVH_DATA_ARRAY & primitive_boxes); + + SIMD_FORCE_INLINE void clearNodes() + { + m_node_array.clear(); + m_num_nodes = 0; + } + + //! node count + SIMD_FORCE_INLINE int getNodeCount() const + { + return m_num_nodes; + } + + //! tells if the node is a leaf + SIMD_FORCE_INLINE bool isLeafNode(int nodeindex) const + { + return m_node_array[nodeindex].isLeafNode(); + } + + SIMD_FORCE_INLINE int getNodeData(int nodeindex) const + { + return m_node_array[nodeindex].getDataIndex(); + } + + SIMD_FORCE_INLINE void getNodeBound(int nodeindex, btAABB & bound) const + { + bound = m_node_array[nodeindex].m_bound; + } + + SIMD_FORCE_INLINE void setNodeBound(int nodeindex, const btAABB & bound) + { + m_node_array[nodeindex].m_bound = bound; + } + + SIMD_FORCE_INLINE int getLeftNode(int nodeindex) const + { + return nodeindex+1; + } + + SIMD_FORCE_INLINE int getRightNode(int nodeindex) const + { + if(m_node_array[nodeindex+1].isLeafNode()) return nodeindex+2; + return nodeindex+1 + m_node_array[nodeindex+1].getEscapeIndex(); + } + + SIMD_FORCE_INLINE int getEscapeNodeIndex(int nodeindex) const + { + return m_node_array[nodeindex].getEscapeIndex(); + } + + SIMD_FORCE_INLINE const GIM_BVH_TREE_NODE * get_node_pointer(int index = 0) const + { + return &m_node_array[index]; + } + + //!@} +}; + + +//! Prototype Base class for primitive classification +/*! +This class is a wrapper for primitive collections. +This tells relevant info for the Bounding Box set classes, which take care of space classification. +This class can manage Compound shapes and trimeshes, and if it is managing trimesh then the Hierarchy Bounding Box classes will take advantage of primitive Vs Box overlapping tests for getting optimal results and less Per Box compairisons. +*/ +class btPrimitiveManagerBase +{ +public: + + virtual ~btPrimitiveManagerBase() {} + + //! determines if this manager consist on only triangles, which special case will be optimized + virtual bool is_trimesh() const = 0; + virtual int get_primitive_count() const = 0; + virtual void get_primitive_box(int prim_index ,btAABB & primbox) const = 0; + //! retrieves only the points of the triangle, and the collision margin + virtual void get_primitive_triangle(int prim_index,btPrimitiveTriangle & triangle) const= 0; +}; + + +//! Structure for containing Boxes +/*! +This class offers an structure for managing a box tree of primitives. +Requires a Primitive prototype (like btPrimitiveManagerBase ) +*/ +class btGImpactBvh +{ +protected: + btBvhTree m_box_tree; + btPrimitiveManagerBase * m_primitive_manager; + +protected: + //stackless refit + void refit(); +public: + + //! this constructor doesn't build the tree. you must call buildSet + btGImpactBvh() + { + m_primitive_manager = NULL; + } + + //! this constructor doesn't build the tree. you must call buildSet + btGImpactBvh(btPrimitiveManagerBase * primitive_manager) + { + m_primitive_manager = primitive_manager; + } + + SIMD_FORCE_INLINE btAABB getGlobalBox() const + { + btAABB totalbox; + getNodeBound(0, totalbox); + return totalbox; + } + + SIMD_FORCE_INLINE void setPrimitiveManager(btPrimitiveManagerBase * primitive_manager) + { + m_primitive_manager = primitive_manager; + } + + SIMD_FORCE_INLINE btPrimitiveManagerBase * getPrimitiveManager() const + { + return m_primitive_manager; + } + + +//! node manager prototype functions +///@{ + + //! this attemps to refit the box set. + SIMD_FORCE_INLINE void update() + { + refit(); + } + + //! this rebuild the entire set + void buildSet(); + + //! returns the indices of the primitives in the m_primitive_manager + bool boxQuery(const btAABB & box, btAlignedObjectArray & collided_results) const; + + //! returns the indices of the primitives in the m_primitive_manager + SIMD_FORCE_INLINE bool boxQueryTrans(const btAABB & box, + const btTransform & transform, btAlignedObjectArray & collided_results) const + { + btAABB transbox=box; + transbox.appy_transform(transform); + return boxQuery(transbox,collided_results); + } + + //! returns the indices of the primitives in the m_primitive_manager + bool rayQuery( + const btVector3 & ray_dir,const btVector3 & ray_origin , + btAlignedObjectArray & collided_results) const; + + //! tells if this set has hierarcht + SIMD_FORCE_INLINE bool hasHierarchy() const + { + return true; + } + + //! tells if this set is a trimesh + SIMD_FORCE_INLINE bool isTrimesh() const + { + return m_primitive_manager->is_trimesh(); + } + + //! node count + SIMD_FORCE_INLINE int getNodeCount() const + { + return m_box_tree.getNodeCount(); + } + + //! tells if the node is a leaf + SIMD_FORCE_INLINE bool isLeafNode(int nodeindex) const + { + return m_box_tree.isLeafNode(nodeindex); + } + + SIMD_FORCE_INLINE int getNodeData(int nodeindex) const + { + return m_box_tree.getNodeData(nodeindex); + } + + SIMD_FORCE_INLINE void getNodeBound(int nodeindex, btAABB & bound) const + { + m_box_tree.getNodeBound(nodeindex, bound); + } + + SIMD_FORCE_INLINE void setNodeBound(int nodeindex, const btAABB & bound) + { + m_box_tree.setNodeBound(nodeindex, bound); + } + + + SIMD_FORCE_INLINE int getLeftNode(int nodeindex) const + { + return m_box_tree.getLeftNode(nodeindex); + } + + SIMD_FORCE_INLINE int getRightNode(int nodeindex) const + { + return m_box_tree.getRightNode(nodeindex); + } + + SIMD_FORCE_INLINE int getEscapeNodeIndex(int nodeindex) const + { + return m_box_tree.getEscapeNodeIndex(nodeindex); + } + + SIMD_FORCE_INLINE void getNodeTriangle(int nodeindex,btPrimitiveTriangle & triangle) const + { + m_primitive_manager->get_primitive_triangle(getNodeData(nodeindex),triangle); + } + + + SIMD_FORCE_INLINE const GIM_BVH_TREE_NODE * get_node_pointer(int index = 0) const + { + return m_box_tree.get_node_pointer(index); + } + + + static float getAverageTreeCollisionTime(); + + + static void find_collision(btGImpactBvh * boxset1, const btTransform & trans1, + btGImpactBvh * boxset2, const btTransform & trans2, + btPairSet & collision_pairs); +}; + + +#endif // GIM_BOXPRUNING_H_INCLUDED diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactCollisionAlgorithm.cpp b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactCollisionAlgorithm.cpp new file mode 100644 index 000000000..e6da8f909 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactCollisionAlgorithm.cpp @@ -0,0 +1,902 @@ +/* +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2007 Francisco Leon Najera. C.C. 80087371. +email: projectileman@yahoo.com + + +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. +*/ +/* +Author: Francisco Len Nßjera +Concave-Concave Collision + +*/ + +#include "BulletCollision/CollisionDispatch/btManifoldResult.h" +#include "LinearMath/btIDebugDraw.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "BulletCollision/CollisionShapes/btBoxShape.h" +#include "btGImpactCollisionAlgorithm.h" +#include "btContactProcessing.h" +#include "LinearMath/btQuickprof.h" + + +//! Class for accessing the plane equation +class btPlaneShape : public btStaticPlaneShape +{ +public: + + btPlaneShape(const btVector3& v, float f) + :btStaticPlaneShape(v,f) + { + } + + void get_plane_equation(btVector4 &equation) + { + equation[0] = m_planeNormal[0]; + equation[1] = m_planeNormal[1]; + equation[2] = m_planeNormal[2]; + equation[3] = m_planeConstant; + } + + + void get_plane_equation_transformed(const btTransform & trans,btVector4 &equation) + { + equation[0] = trans.getBasis().getRow(0).dot(m_planeNormal); + equation[1] = trans.getBasis().getRow(1).dot(m_planeNormal); + equation[2] = trans.getBasis().getRow(2).dot(m_planeNormal); + equation[3] = trans.getOrigin().dot(m_planeNormal) + m_planeConstant; + } +}; + + + +////////////////////////////////////////////////////////////////////////////////////////////// +#ifdef TRI_COLLISION_PROFILING + +btClock g_triangle_clock; + +float g_accum_triangle_collision_time = 0; +int g_count_triangle_collision = 0; + +void bt_begin_gim02_tri_time() +{ + g_triangle_clock.reset(); +} + +void bt_end_gim02_tri_time() +{ + g_accum_triangle_collision_time += g_triangle_clock.getTimeMicroseconds(); + g_count_triangle_collision++; +} +#endif //TRI_COLLISION_PROFILING +//! Retrieving shapes shapes +/*! +Declared here due of insuficent space on Pool allocators +*/ +//!@{ +class GIM_ShapeRetriever +{ +public: + btGImpactShapeInterface * m_gim_shape; + btTriangleShapeEx m_trishape; + btTetrahedronShapeEx m_tetrashape; + +public: + class ChildShapeRetriever + { + public: + GIM_ShapeRetriever * m_parent; + virtual btCollisionShape * getChildShape(int index) + { + return m_parent->m_gim_shape->getChildShape(index); + } + virtual ~ChildShapeRetriever() {} + }; + + class TriangleShapeRetriever:public ChildShapeRetriever + { + public: + + virtual btCollisionShape * getChildShape(int index) + { + m_parent->m_gim_shape->getBulletTriangle(index,m_parent->m_trishape); + return &m_parent->m_trishape; + } + virtual ~TriangleShapeRetriever() {} + }; + + class TetraShapeRetriever:public ChildShapeRetriever + { + public: + + virtual btCollisionShape * getChildShape(int index) + { + m_parent->m_gim_shape->getBulletTetrahedron(index,m_parent->m_tetrashape); + return &m_parent->m_tetrashape; + } + }; +public: + ChildShapeRetriever m_child_retriever; + TriangleShapeRetriever m_tri_retriever; + TetraShapeRetriever m_tetra_retriever; + ChildShapeRetriever * m_current_retriever; + + GIM_ShapeRetriever(btGImpactShapeInterface * gim_shape) + { + m_gim_shape = gim_shape; + //select retriever + if(m_gim_shape->needsRetrieveTriangles()) + { + m_current_retriever = &m_tri_retriever; + } + else if(m_gim_shape->needsRetrieveTetrahedrons()) + { + m_current_retriever = &m_tetra_retriever; + } + else + { + m_current_retriever = &m_child_retriever; + } + + m_current_retriever->m_parent = this; + } + + btCollisionShape * getChildShape(int index) + { + return m_current_retriever->getChildShape(index); + } + + +}; + + + +//!@} + + +#ifdef TRI_COLLISION_PROFILING + +//! Gets the average time in miliseconds of tree collisions +float btGImpactCollisionAlgorithm::getAverageTreeCollisionTime() +{ + return btGImpactBoxSet::getAverageTreeCollisionTime(); + +} + +//! Gets the average time in miliseconds of triangle collisions +float btGImpactCollisionAlgorithm::getAverageTriangleCollisionTime() +{ + if(g_count_triangle_collision == 0) return 0; + + float avgtime = g_accum_triangle_collision_time; + avgtime /= (float)g_count_triangle_collision; + + g_accum_triangle_collision_time = 0; + g_count_triangle_collision = 0; + + return avgtime; +} + +#endif //TRI_COLLISION_PROFILING + + + +btGImpactCollisionAlgorithm::btGImpactCollisionAlgorithm( const btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) +: btActivatingCollisionAlgorithm(ci,body0,body1) +{ + m_manifoldPtr = NULL; + m_convex_algorithm = NULL; +} + +btGImpactCollisionAlgorithm::~btGImpactCollisionAlgorithm() +{ + clearCache(); +} + + + + + +void btGImpactCollisionAlgorithm::addContactPoint(btCollisionObject * body0, + btCollisionObject * body1, + const btVector3 & point, + const btVector3 & normal, + btScalar distance) +{ + m_resultOut->setShapeIdentifiersA(m_part0,m_triface0); + m_resultOut->setShapeIdentifiersB(m_part1,m_triface1); + checkManifold(body0,body1); + m_resultOut->addContactPoint(normal,point,distance); +} + + +void btGImpactCollisionAlgorithm::shape_vs_shape_collision( + btCollisionObject * body0, + btCollisionObject * body1, + btCollisionShape * shape0, + btCollisionShape * shape1) +{ + + btCollisionShape* tmpShape0 = body0->getCollisionShape(); + btCollisionShape* tmpShape1 = body1->getCollisionShape(); + + body0->internalSetTemporaryCollisionShape(shape0); + body1->internalSetTemporaryCollisionShape(shape1); + + { + btCollisionAlgorithm* algor = newAlgorithm(body0,body1); + // post : checkManifold is called + + m_resultOut->setShapeIdentifiersA(m_part0,m_triface0); + m_resultOut->setShapeIdentifiersB(m_part1,m_triface1); + + algor->processCollision(body0,body1,*m_dispatchInfo,m_resultOut); + + algor->~btCollisionAlgorithm(); + m_dispatcher->freeCollisionAlgorithm(algor); + } + + body0->internalSetTemporaryCollisionShape(tmpShape0); + body1->internalSetTemporaryCollisionShape(tmpShape1); +} + +void btGImpactCollisionAlgorithm::convex_vs_convex_collision( + btCollisionObject * body0, + btCollisionObject * body1, + btCollisionShape * shape0, + btCollisionShape * shape1) +{ + + btCollisionShape* tmpShape0 = body0->getCollisionShape(); + btCollisionShape* tmpShape1 = body1->getCollisionShape(); + + body0->internalSetTemporaryCollisionShape(shape0); + body1->internalSetTemporaryCollisionShape(shape1); + + + m_resultOut->setShapeIdentifiersA(m_part0,m_triface0); + m_resultOut->setShapeIdentifiersB(m_part1,m_triface1); + + checkConvexAlgorithm(body0,body1); + m_convex_algorithm->processCollision(body0,body1,*m_dispatchInfo,m_resultOut); + + body0->internalSetTemporaryCollisionShape(tmpShape0); + body1->internalSetTemporaryCollisionShape(tmpShape1); + +} + + + + +void btGImpactCollisionAlgorithm::gimpact_vs_gimpact_find_pairs( + const btTransform & trans0, + const btTransform & trans1, + btGImpactShapeInterface * shape0, + btGImpactShapeInterface * shape1,btPairSet & pairset) +{ + if(shape0->hasBoxSet() && shape1->hasBoxSet()) + { + btGImpactBoxSet::find_collision(shape0->getBoxSet(),trans0,shape1->getBoxSet(),trans1,pairset); + } + else + { + btAABB boxshape0; + btAABB boxshape1; + int i = shape0->getNumChildShapes(); + + while(i--) + { + shape0->getChildAabb(i,trans0,boxshape0.m_min,boxshape0.m_max); + + int j = shape1->getNumChildShapes(); + while(j--) + { + shape1->getChildAabb(i,trans1,boxshape1.m_min,boxshape1.m_max); + + if(boxshape1.has_collision(boxshape0)) + { + pairset.push_pair(i,j); + } + } + } + } + + +} + + +void btGImpactCollisionAlgorithm::gimpact_vs_shape_find_pairs( + const btTransform & trans0, + const btTransform & trans1, + btGImpactShapeInterface * shape0, + btCollisionShape * shape1, + btAlignedObjectArray & collided_primitives) +{ + + btAABB boxshape; + + + if(shape0->hasBoxSet()) + { + btTransform trans1to0 = trans0.inverse(); + trans1to0 *= trans1; + + shape1->getAabb(trans1to0,boxshape.m_min,boxshape.m_max); + + shape0->getBoxSet()->boxQuery(boxshape, collided_primitives); + } + else + { + shape1->getAabb(trans1,boxshape.m_min,boxshape.m_max); + + btAABB boxshape0; + int i = shape0->getNumChildShapes(); + + while(i--) + { + shape0->getChildAabb(i,trans0,boxshape0.m_min,boxshape0.m_max); + + if(boxshape.has_collision(boxshape0)) + { + collided_primitives.push_back(i); + } + } + + } + +} + + +void btGImpactCollisionAlgorithm::collide_gjk_triangles(btCollisionObject * body0, + btCollisionObject * body1, + btGImpactMeshShapePart * shape0, + btGImpactMeshShapePart * shape1, + const int * pairs, int pair_count) +{ + btTriangleShapeEx tri0; + btTriangleShapeEx tri1; + + shape0->lockChildShapes(); + shape1->lockChildShapes(); + + const int * pair_pointer = pairs; + + while(pair_count--) + { + + m_triface0 = *(pair_pointer); + m_triface1 = *(pair_pointer+1); + pair_pointer+=2; + + + + shape0->getBulletTriangle(m_triface0,tri0); + shape1->getBulletTriangle(m_triface1,tri1); + + + //collide two convex shapes + if(tri0.overlap_test_conservative(tri1)) + { + convex_vs_convex_collision(body0,body1,&tri0,&tri1); + } + + } + + shape0->unlockChildShapes(); + shape1->unlockChildShapes(); +} + +void btGImpactCollisionAlgorithm::collide_sat_triangles(btCollisionObject * body0, + btCollisionObject * body1, + btGImpactMeshShapePart * shape0, + btGImpactMeshShapePart * shape1, + const int * pairs, int pair_count) +{ + btTransform orgtrans0 = body0->getWorldTransform(); + btTransform orgtrans1 = body1->getWorldTransform(); + + btPrimitiveTriangle ptri0; + btPrimitiveTriangle ptri1; + GIM_TRIANGLE_CONTACT contact_data; + + shape0->lockChildShapes(); + shape1->lockChildShapes(); + + const int * pair_pointer = pairs; + + while(pair_count--) + { + + m_triface0 = *(pair_pointer); + m_triface1 = *(pair_pointer+1); + pair_pointer+=2; + + + shape0->getPrimitiveTriangle(m_triface0,ptri0); + shape1->getPrimitiveTriangle(m_triface1,ptri1); + + #ifdef TRI_COLLISION_PROFILING + bt_begin_gim02_tri_time(); + #endif + + ptri0.applyTransform(orgtrans0); + ptri1.applyTransform(orgtrans1); + + + //build planes + ptri0.buildTriPlane(); + ptri1.buildTriPlane(); + // test conservative + + + + if(ptri0.overlap_test_conservative(ptri1)) + { + if(ptri0.find_triangle_collision_clip_method(ptri1,contact_data)) + { + + int j = contact_data.m_point_count; + while(j--) + { + + addContactPoint(body0, body1, + contact_data.m_points[j], + contact_data.m_separating_normal, + -contact_data.m_penetration_depth); + } + } + } + + #ifdef TRI_COLLISION_PROFILING + bt_end_gim02_tri_time(); + #endif + + } + + shape0->unlockChildShapes(); + shape1->unlockChildShapes(); + +} + + +void btGImpactCollisionAlgorithm::gimpact_vs_gimpact( + btCollisionObject * body0, + btCollisionObject * body1, + btGImpactShapeInterface * shape0, + btGImpactShapeInterface * shape1) +{ + + if(shape0->getGImpactShapeType()==CONST_GIMPACT_TRIMESH_SHAPE) + { + btGImpactMeshShape * meshshape0 = static_cast(shape0); + m_part0 = meshshape0->getMeshPartCount(); + + while(m_part0--) + { + gimpact_vs_gimpact(body0,body1,meshshape0->getMeshPart(m_part0),shape1); + } + + return; + } + + if(shape1->getGImpactShapeType()==CONST_GIMPACT_TRIMESH_SHAPE) + { + btGImpactMeshShape * meshshape1 = static_cast(shape1); + m_part1 = meshshape1->getMeshPartCount(); + + while(m_part1--) + { + + gimpact_vs_gimpact(body0,body1,shape0,meshshape1->getMeshPart(m_part1)); + + } + + return; + } + + + btTransform orgtrans0 = body0->getWorldTransform(); + btTransform orgtrans1 = body1->getWorldTransform(); + + btPairSet pairset; + + gimpact_vs_gimpact_find_pairs(orgtrans0,orgtrans1,shape0,shape1,pairset); + + if(pairset.size()== 0) return; + + if(shape0->getGImpactShapeType() == CONST_GIMPACT_TRIMESH_SHAPE_PART && + shape1->getGImpactShapeType() == CONST_GIMPACT_TRIMESH_SHAPE_PART) + { + btGImpactMeshShapePart * shapepart0 = static_cast(shape0); + btGImpactMeshShapePart * shapepart1 = static_cast(shape1); + //specialized function + #ifdef BULLET_TRIANGLE_COLLISION + collide_gjk_triangles(body0,body1,shapepart0,shapepart1,&pairset[0].m_index1,pairset.size()); + #else + collide_sat_triangles(body0,body1,shapepart0,shapepart1,&pairset[0].m_index1,pairset.size()); + #endif + + return; + } + + //general function + + shape0->lockChildShapes(); + shape1->lockChildShapes(); + + GIM_ShapeRetriever retriever0(shape0); + GIM_ShapeRetriever retriever1(shape1); + + bool child_has_transform0 = shape0->childrenHasTransform(); + bool child_has_transform1 = shape1->childrenHasTransform(); + + int i = pairset.size(); + while(i--) + { + GIM_PAIR * pair = &pairset[i]; + m_triface0 = pair->m_index1; + m_triface1 = pair->m_index2; + btCollisionShape * colshape0 = retriever0.getChildShape(m_triface0); + btCollisionShape * colshape1 = retriever1.getChildShape(m_triface1); + + if(child_has_transform0) + { + body0->setWorldTransform(orgtrans0*shape0->getChildTransform(m_triface0)); + } + + if(child_has_transform1) + { + body1->setWorldTransform(orgtrans1*shape1->getChildTransform(m_triface1)); + } + + //collide two convex shapes + convex_vs_convex_collision(body0,body1,colshape0,colshape1); + + + if(child_has_transform0) + { + body0->setWorldTransform(orgtrans0); + } + + if(child_has_transform1) + { + body1->setWorldTransform(orgtrans1); + } + + } + + shape0->unlockChildShapes(); + shape1->unlockChildShapes(); +} + +void btGImpactCollisionAlgorithm::gimpact_vs_shape(btCollisionObject * body0, + btCollisionObject * body1, + btGImpactShapeInterface * shape0, + btCollisionShape * shape1,bool swapped) +{ + if(shape0->getGImpactShapeType()==CONST_GIMPACT_TRIMESH_SHAPE) + { + btGImpactMeshShape * meshshape0 = static_cast(shape0); + int& part = swapped ? m_part1 : m_part0; + part = meshshape0->getMeshPartCount(); + + while(part--) + { + + gimpact_vs_shape(body0, + body1, + meshshape0->getMeshPart(part), + shape1,swapped); + + } + + return; + } + + #ifdef GIMPACT_VS_PLANE_COLLISION + if(shape0->getGImpactShapeType() == CONST_GIMPACT_TRIMESH_SHAPE_PART && + shape1->getShapeType() == STATIC_PLANE_PROXYTYPE) + { + btGImpactMeshShapePart * shapepart = static_cast(shape0); + btStaticPlaneShape * planeshape = static_cast(shape1); + gimpacttrimeshpart_vs_plane_collision(body0,body1,shapepart,planeshape,swapped); + return; + } + + #endif + + + + if(shape1->isCompound()) + { + btCompoundShape * compoundshape = static_cast(shape1); + gimpact_vs_compoundshape(body0,body1,shape0,compoundshape,swapped); + return; + } + else if(shape1->isConcave()) + { + btConcaveShape * concaveshape = static_cast(shape1); + gimpact_vs_concave(body0,body1,shape0,concaveshape,swapped); + return; + } + + + btTransform orgtrans0 = body0->getWorldTransform(); + + btTransform orgtrans1 = body1->getWorldTransform(); + + btAlignedObjectArray collided_results; + + gimpact_vs_shape_find_pairs(orgtrans0,orgtrans1,shape0,shape1,collided_results); + + if(collided_results.size() == 0) return; + + + shape0->lockChildShapes(); + + GIM_ShapeRetriever retriever0(shape0); + + + bool child_has_transform0 = shape0->childrenHasTransform(); + + + int i = collided_results.size(); + + while(i--) + { + int child_index = collided_results[i]; + if(swapped) + m_triface1 = child_index; + else + m_triface0 = child_index; + + btCollisionShape * colshape0 = retriever0.getChildShape(child_index); + + if(child_has_transform0) + { + body0->setWorldTransform(orgtrans0*shape0->getChildTransform(child_index)); + } + + //collide two shapes + if(swapped) + { + shape_vs_shape_collision(body1,body0,shape1,colshape0); + } + else + { + shape_vs_shape_collision(body0,body1,colshape0,shape1); + } + + //restore transforms + if(child_has_transform0) + { + body0->setWorldTransform(orgtrans0); + } + + } + + shape0->unlockChildShapes(); + +} + +void btGImpactCollisionAlgorithm::gimpact_vs_compoundshape(btCollisionObject * body0, + btCollisionObject * body1, + btGImpactShapeInterface * shape0, + btCompoundShape * shape1,bool swapped) +{ + btTransform orgtrans1 = body1->getWorldTransform(); + + int i = shape1->getNumChildShapes(); + while(i--) + { + + btCollisionShape * colshape1 = shape1->getChildShape(i); + btTransform childtrans1 = orgtrans1*shape1->getChildTransform(i); + + body1->setWorldTransform(childtrans1); + + //collide child shape + gimpact_vs_shape(body0, body1, + shape0,colshape1,swapped); + + + //restore transforms + body1->setWorldTransform(orgtrans1); + } +} + +void btGImpactCollisionAlgorithm::gimpacttrimeshpart_vs_plane_collision( + btCollisionObject * body0, + btCollisionObject * body1, + btGImpactMeshShapePart * shape0, + btStaticPlaneShape * shape1,bool swapped) +{ + + + btTransform orgtrans0 = body0->getWorldTransform(); + btTransform orgtrans1 = body1->getWorldTransform(); + + btPlaneShape * planeshape = static_cast(shape1); + btVector4 plane; + planeshape->get_plane_equation_transformed(orgtrans1,plane); + + //test box against plane + + btAABB tribox; + shape0->getAabb(orgtrans0,tribox.m_min,tribox.m_max); + tribox.increment_margin(planeshape->getMargin()); + + if( tribox.plane_classify(plane)!= BT_CONST_COLLIDE_PLANE) return; + + shape0->lockChildShapes(); + + btScalar margin = shape0->getMargin() + planeshape->getMargin(); + + btVector3 vertex; + int vi = shape0->getVertexCount(); + while(vi--) + { + shape0->getVertex(vi,vertex); + vertex = orgtrans0(vertex); + + btScalar distance = vertex.dot(plane) - plane[3] - margin; + + if(distance<0.0)//add contact + { + if(swapped) + { + addContactPoint(body1, body0, + vertex, + -plane, + distance); + } + else + { + addContactPoint(body0, body1, + vertex, + plane, + distance); + } + } + } + + shape0->unlockChildShapes(); +} + + + + +class btGImpactTriangleCallback: public btTriangleCallback +{ +public: + btGImpactCollisionAlgorithm * algorithm; + btCollisionObject * body0; + btCollisionObject * body1; + btGImpactShapeInterface * gimpactshape0; + bool swapped; + btScalar margin; + + virtual void processTriangle(btVector3* triangle, int partId, int triangleIndex) + { + btTriangleShapeEx tri1(triangle[0],triangle[1],triangle[2]); + tri1.setMargin(margin); + if(swapped) + { + algorithm->setPart0(partId); + algorithm->setFace0(triangleIndex); + } + else + { + algorithm->setPart1(partId); + algorithm->setFace1(triangleIndex); + } + algorithm->gimpact_vs_shape( + body0,body1,gimpactshape0,&tri1,swapped); + } +}; + + + + +void btGImpactCollisionAlgorithm::gimpact_vs_concave( + btCollisionObject * body0, + btCollisionObject * body1, + btGImpactShapeInterface * shape0, + btConcaveShape * shape1,bool swapped) +{ + //create the callback + btGImpactTriangleCallback tricallback; + tricallback.algorithm = this; + tricallback.body0 = body0; + tricallback.body1 = body1; + tricallback.gimpactshape0 = shape0; + tricallback.swapped = swapped; + tricallback.margin = shape1->getMargin(); + + //getting the trimesh AABB + btTransform gimpactInConcaveSpace; + + gimpactInConcaveSpace = body1->getWorldTransform().inverse() * body0->getWorldTransform(); + + btVector3 minAABB,maxAABB; + shape0->getAabb(gimpactInConcaveSpace,minAABB,maxAABB); + + shape1->processAllTriangles(&tricallback,minAABB,maxAABB); + +} + + + +void btGImpactCollisionAlgorithm::processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + clearCache(); + + m_resultOut = resultOut; + m_dispatchInfo = &dispatchInfo; + btGImpactShapeInterface * gimpactshape0; + btGImpactShapeInterface * gimpactshape1; + + if (body0->getCollisionShape()->getShapeType()==GIMPACT_SHAPE_PROXYTYPE) + { + gimpactshape0 = static_cast(body0->getCollisionShape()); + + if( body1->getCollisionShape()->getShapeType()==GIMPACT_SHAPE_PROXYTYPE ) + { + gimpactshape1 = static_cast(body1->getCollisionShape()); + + gimpact_vs_gimpact(body0,body1,gimpactshape0,gimpactshape1); + } + else + { + gimpact_vs_shape(body0,body1,gimpactshape0,body1->getCollisionShape(),false); + } + + } + else if (body1->getCollisionShape()->getShapeType()==GIMPACT_SHAPE_PROXYTYPE ) + { + gimpactshape1 = static_cast(body1->getCollisionShape()); + + gimpact_vs_shape(body1,body0,gimpactshape1,body0->getCollisionShape(),true); + } +} + + +btScalar btGImpactCollisionAlgorithm::calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + return 1.f; + +} + +///////////////////////////////////// REGISTERING ALGORITHM ////////////////////////////////////////////// + +btGImpactCollisionAlgorithm::CreateFunc g_gimpact_cf; + +//! Use this function for register the algorithm externally +void btGImpactCollisionAlgorithm::registerAlgorithm(btCollisionDispatcher * dispatcher) +{ + + int i; + + for ( i = 0;i < MAX_BROADPHASE_COLLISION_TYPES ;i++ ) + { + dispatcher->registerCollisionCreateFunc(GIMPACT_SHAPE_PROXYTYPE,i ,&g_gimpact_cf); + } + + for ( i = 0;i < MAX_BROADPHASE_COLLISION_TYPES ;i++ ) + { + dispatcher->registerCollisionCreateFunc(i,GIMPACT_SHAPE_PROXYTYPE ,&g_gimpact_cf); + } + +} diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactCollisionAlgorithm.h b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactCollisionAlgorithm.h new file mode 100644 index 000000000..453472aa0 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactCollisionAlgorithm.h @@ -0,0 +1,306 @@ +/*! \file btGImpactShape.h +\author Francisco Len Nßjera +*/ +/* +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2007 Francisco Leon Najera. C.C. 80087371. +email: projectileman@yahoo.com + + +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 BVH_CONCAVE_COLLISION_ALGORITHM_H +#define BVH_CONCAVE_COLLISION_ALGORITHM_H + +#include "BulletCollision/CollisionDispatch/btActivatingCollisionAlgorithm.h" +#include "BulletCollision/BroadphaseCollision/btDispatcher.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseInterface.h" +#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" +class btDispatcher; +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "BulletCollision/CollisionDispatch/btCollisionCreateFunc.h" +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" + +#include "LinearMath/btAlignedObjectArray.h" + +#include "btGImpactShape.h" +#include "BulletCollision/CollisionShapes/btStaticPlaneShape.h" +#include "BulletCollision/CollisionShapes/btCompoundShape.h" +#include "BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.h" +#include "LinearMath/btIDebugDraw.h" + + + +//! Collision Algorithm for GImpact Shapes +/*! +For register this algorithm in Bullet, proceed as following: + \code +btCollisionDispatcher * dispatcher = static_cast(m_dynamicsWorld ->getDispatcher()); +btGImpactCollisionAlgorithm::registerAlgorithm(dispatcher); + \endcode +*/ +class btGImpactCollisionAlgorithm : public btActivatingCollisionAlgorithm +{ +protected: + btCollisionAlgorithm * m_convex_algorithm; + btPersistentManifold * m_manifoldPtr; + btManifoldResult* m_resultOut; + const btDispatcherInfo * m_dispatchInfo; + int m_triface0; + int m_part0; + int m_triface1; + int m_part1; + + + //! Creates a new contact point + SIMD_FORCE_INLINE btPersistentManifold* newContactManifold(btCollisionObject* body0,btCollisionObject* body1) + { + m_manifoldPtr = m_dispatcher->getNewManifold(body0,body1); + return m_manifoldPtr; + } + + SIMD_FORCE_INLINE void destroyConvexAlgorithm() + { + if(m_convex_algorithm) + { + m_convex_algorithm->~btCollisionAlgorithm(); + m_dispatcher->freeCollisionAlgorithm( m_convex_algorithm); + m_convex_algorithm = NULL; + } + } + + SIMD_FORCE_INLINE void destroyContactManifolds() + { + if(m_manifoldPtr == NULL) return; + m_dispatcher->releaseManifold(m_manifoldPtr); + m_manifoldPtr = NULL; + } + + SIMD_FORCE_INLINE void clearCache() + { + destroyContactManifolds(); + destroyConvexAlgorithm(); + + m_triface0 = -1; + m_part0 = -1; + m_triface1 = -1; + m_part1 = -1; + } + + SIMD_FORCE_INLINE btPersistentManifold* getLastManifold() + { + return m_manifoldPtr; + } + + + // Call before process collision + SIMD_FORCE_INLINE void checkManifold(btCollisionObject* body0,btCollisionObject* body1) + { + if(getLastManifold() == 0) + { + newContactManifold(body0,body1); + } + + m_resultOut->setPersistentManifold(getLastManifold()); + } + + // Call before process collision + SIMD_FORCE_INLINE btCollisionAlgorithm * newAlgorithm(btCollisionObject* body0,btCollisionObject* body1) + { + checkManifold(body0,body1); + + btCollisionAlgorithm * convex_algorithm = m_dispatcher->findAlgorithm( + body0,body1,getLastManifold()); + return convex_algorithm ; + } + + // Call before process collision + SIMD_FORCE_INLINE void checkConvexAlgorithm(btCollisionObject* body0,btCollisionObject* body1) + { + if(m_convex_algorithm) return; + m_convex_algorithm = newAlgorithm(body0,body1); + } + + + + + void addContactPoint(btCollisionObject * body0, + btCollisionObject * body1, + const btVector3 & point, + const btVector3 & normal, + btScalar distance); + +//! Collision routines +//!@{ + + void collide_gjk_triangles(btCollisionObject * body0, + btCollisionObject * body1, + btGImpactMeshShapePart * shape0, + btGImpactMeshShapePart * shape1, + const int * pairs, int pair_count); + + void collide_sat_triangles(btCollisionObject * body0, + btCollisionObject * body1, + btGImpactMeshShapePart * shape0, + btGImpactMeshShapePart * shape1, + const int * pairs, int pair_count); + + + + + void shape_vs_shape_collision( + btCollisionObject * body0, + btCollisionObject * body1, + btCollisionShape * shape0, + btCollisionShape * shape1); + + void convex_vs_convex_collision(btCollisionObject * body0, + btCollisionObject * body1, + btCollisionShape * shape0, + btCollisionShape * shape1); + + + + void gimpact_vs_gimpact_find_pairs( + const btTransform & trans0, + const btTransform & trans1, + btGImpactShapeInterface * shape0, + btGImpactShapeInterface * shape1,btPairSet & pairset); + + void gimpact_vs_shape_find_pairs( + const btTransform & trans0, + const btTransform & trans1, + btGImpactShapeInterface * shape0, + btCollisionShape * shape1, + btAlignedObjectArray & collided_primitives); + + + void gimpacttrimeshpart_vs_plane_collision( + btCollisionObject * body0, + btCollisionObject * body1, + btGImpactMeshShapePart * shape0, + btStaticPlaneShape * shape1,bool swapped); + + +public: + + btGImpactCollisionAlgorithm( const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* body0,btCollisionObject* body1); + + virtual ~btGImpactCollisionAlgorithm(); + + virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual void getAllContactManifolds(btManifoldArray& manifoldArray) + { + if (m_manifoldPtr) + manifoldArray.push_back(m_manifoldPtr); + } + + + struct CreateFunc :public btCollisionAlgorithmCreateFunc + { + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + void* mem = ci.m_dispatcher1->allocateCollisionAlgorithm(sizeof(btGImpactCollisionAlgorithm)); + return new(mem) btGImpactCollisionAlgorithm(ci,body0,body1); + } + }; + + //! Use this function for register the algorithm externally + static void registerAlgorithm(btCollisionDispatcher * dispatcher); + + //! Gets the average time in miliseconds of tree collisions + static float getAverageTreeCollisionTime(); + + //! Gets the average time in miliseconds of triangle collisions + static float getAverageTriangleCollisionTime(); + + + //! Collides two gimpact shapes + /*! + \pre shape0 and shape1 couldn't be btGImpactMeshShape objects + */ + + + void gimpact_vs_gimpact(btCollisionObject * body0, + btCollisionObject * body1, + btGImpactShapeInterface * shape0, + btGImpactShapeInterface * shape1); + + void gimpact_vs_shape(btCollisionObject * body0, + btCollisionObject * body1, + btGImpactShapeInterface * shape0, + btCollisionShape * shape1,bool swapped); + + void gimpact_vs_compoundshape(btCollisionObject * body0, + btCollisionObject * body1, + btGImpactShapeInterface * shape0, + btCompoundShape * shape1,bool swapped); + + void gimpact_vs_concave( + btCollisionObject * body0, + btCollisionObject * body1, + btGImpactShapeInterface * shape0, + btConcaveShape * shape1,bool swapped); + + + + + /// Accessor/Mutator pairs for Part and triangleID + void setFace0(int value) + { + m_triface0 = value; + } + int getFace0() + { + return m_triface0; + } + void setFace1(int value) + { + m_triface1 = value; + } + int getFace1() + { + return m_triface1; + } + void setPart0(int value) + { + m_part0 = value; + } + int getPart0() + { + return m_part0; + } + void setPart1(int value) + { + m_part1 = value; + } + int getPart1() + { + return m_part1; + } + +}; + + +//algorithm details +//#define BULLET_TRIANGLE_COLLISION 1 +#define GIMPACT_VS_PLANE_COLLISION 1 + + + +#endif //BVH_CONCAVE_COLLISION_ALGORITHM_H diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactMassUtil.h b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactMassUtil.h new file mode 100644 index 000000000..0a10f3cdb --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactMassUtil.h @@ -0,0 +1,60 @@ +/*! \file btGImpactMassUtil.h +\author Francisco Len Nßjera +*/ +/* +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2007 Francisco Leon Najera. C.C. 80087371. +email: projectileman@yahoo.com + + +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 GIMPACT_MASS_UTIL_H +#define GIMPACT_MASS_UTIL_H + +#include "LinearMath/btTransform.h" + + + +SIMD_FORCE_INLINE btVector3 gim_inertia_add_transformed( + const btVector3 & source_inertia, const btVector3 & added_inertia, const btTransform & transform) +{ + btMatrix3x3 rotatedTensor = transform.getBasis().scaled(added_inertia) * transform.getBasis().transpose(); + + btScalar x2 = transform.getOrigin()[0]; + x2*= x2; + btScalar y2 = transform.getOrigin()[1]; + y2*= y2; + btScalar z2 = transform.getOrigin()[2]; + z2*= z2; + + btScalar ix = rotatedTensor[0][0]*(y2+z2); + btScalar iy = rotatedTensor[1][1]*(x2+z2); + btScalar iz = rotatedTensor[2][2]*(x2+y2); + + return btVector3(source_inertia[0]+ix,source_inertia[1]+iy,source_inertia[2] + iz); +} + +SIMD_FORCE_INLINE btVector3 gim_get_point_inertia(const btVector3 & point, btScalar mass) +{ + btScalar x2 = point[0]*point[0]; + btScalar y2 = point[1]*point[1]; + btScalar z2 = point[2]*point[2]; + return btVector3(mass*(y2+z2),mass*(x2+z2),mass*(x2+y2)); +} + + +#endif //GIMPACT_MESH_SHAPE_H diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactQuantizedBvh.cpp b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactQuantizedBvh.cpp new file mode 100644 index 000000000..ea1647a81 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactQuantizedBvh.cpp @@ -0,0 +1,528 @@ +/*! \file gim_box_set.h +\author Francisco Len Nßjera +*/ +/* +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2007 Francisco Leon Najera. C.C. 80087371. +email: projectileman@yahoo.com + + +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 "btGImpactQuantizedBvh.h" +#include "LinearMath/btQuickprof.h" + +#ifdef TRI_COLLISION_PROFILING +btClock g_q_tree_clock; + + +float g_q_accum_tree_collision_time = 0; +int g_q_count_traversing = 0; + + +void bt_begin_gim02_q_tree_time() +{ + g_q_tree_clock.reset(); +} + +void bt_end_gim02_q_tree_time() +{ + g_q_accum_tree_collision_time += g_q_tree_clock.getTimeMicroseconds(); + g_q_count_traversing++; +} + + +//! Gets the average time in miliseconds of tree collisions +float btGImpactQuantizedBvh::getAverageTreeCollisionTime() +{ + if(g_q_count_traversing == 0) return 0; + + float avgtime = g_q_accum_tree_collision_time; + avgtime /= (float)g_q_count_traversing; + + g_q_accum_tree_collision_time = 0; + g_q_count_traversing = 0; + return avgtime; + +// float avgtime = g_q_count_traversing; +// g_q_count_traversing = 0; +// return avgtime; + +} + +#endif //TRI_COLLISION_PROFILING + +/////////////////////// btQuantizedBvhTree ///////////////////////////////// + +void btQuantizedBvhTree::calc_quantization( + GIM_BVH_DATA_ARRAY & primitive_boxes, btScalar boundMargin) +{ + //calc globa box + btAABB global_bound; + global_bound.invalidate(); + + for (int i=0;i splitValue) + { + //swap + primitive_boxes.swap(i,splitIndex); + //swapLeafNodes(i,splitIndex); + splitIndex++; + } + } + + //if the splitIndex causes unbalanced trees, fix this by using the center in between startIndex and endIndex + //otherwise the tree-building might fail due to stack-overflows in certain cases. + //unbalanced1 is unsafe: it can cause stack overflows + //bool unbalanced1 = ((splitIndex==startIndex) || (splitIndex == (endIndex-1))); + + //unbalanced2 should work too: always use center (perfect balanced trees) + //bool unbalanced2 = true; + + //this should be safe too: + int rangeBalancedIndices = numIndices/3; + bool unbalanced = ((splitIndex<=(startIndex+rangeBalancedIndices)) || (splitIndex >=(endIndex-1-rangeBalancedIndices))); + + if (unbalanced) + { + splitIndex = startIndex+ (numIndices>>1); + } + + btAssert(!((splitIndex==startIndex) || (splitIndex == (endIndex)))); + + return splitIndex; + +} + + +void btQuantizedBvhTree::_build_sub_tree(GIM_BVH_DATA_ARRAY & primitive_boxes, int startIndex, int endIndex) +{ + int curIndex = m_num_nodes; + m_num_nodes++; + + btAssert((endIndex-startIndex)>0); + + if ((endIndex-startIndex)==1) + { + //We have a leaf node + setNodeBound(curIndex,primitive_boxes[startIndex].m_bound); + m_node_array[curIndex].setDataIndex(primitive_boxes[startIndex].m_data); + + return; + } + //calculate Best Splitting Axis and where to split it. Sort the incoming 'leafNodes' array within range 'startIndex/endIndex'. + + //split axis + int splitIndex = _calc_splitting_axis(primitive_boxes,startIndex,endIndex); + + splitIndex = _sort_and_calc_splitting_index( + primitive_boxes,startIndex,endIndex, + splitIndex//split axis + ); + + + //calc this node bounding box + + btAABB node_bound; + node_bound.invalidate(); + + for (int i=startIndex;iget_primitive_box(getNodeData(nodecount),leafbox); + setNodeBound(nodecount,leafbox); + } + else + { + //const GIM_BVH_TREE_NODE * nodepointer = get_node_pointer(nodecount); + //get left bound + btAABB bound; + bound.invalidate(); + + btAABB temp_box; + + int child_node = getLeftNode(nodecount); + if(child_node) + { + getNodeBound(child_node,temp_box); + bound.merge(temp_box); + } + + child_node = getRightNode(nodecount); + if(child_node) + { + getNodeBound(child_node,temp_box); + bound.merge(temp_box); + } + + setNodeBound(nodecount,bound); + } + } +} + +//! this rebuild the entire set +void btGImpactQuantizedBvh::buildSet() +{ + //obtain primitive boxes + GIM_BVH_DATA_ARRAY primitive_boxes; + primitive_boxes.resize(m_primitive_manager->get_primitive_count()); + + for (int i = 0;iget_primitive_box(i,primitive_boxes[i].m_bound); + primitive_boxes[i].m_data = i; + } + + m_box_tree.build_tree(primitive_boxes); +} + +//! returns the indices of the primitives in the m_primitive_manager +bool btGImpactQuantizedBvh::boxQuery(const btAABB & box, btAlignedObjectArray & collided_results) const +{ + int curIndex = 0; + int numNodes = getNodeCount(); + + //quantize box + + unsigned short quantizedMin[3]; + unsigned short quantizedMax[3]; + + m_box_tree.quantizePoint(quantizedMin,box.m_min); + m_box_tree.quantizePoint(quantizedMax,box.m_max); + + + while (curIndex < numNodes) + { + + //catch bugs in tree data + + bool aabbOverlap = m_box_tree.testQuantizedBoxOverlapp(curIndex, quantizedMin,quantizedMax); + bool isleafnode = isLeafNode(curIndex); + + if (isleafnode && aabbOverlap) + { + collided_results.push_back(getNodeData(curIndex)); + } + + if (aabbOverlap || isleafnode) + { + //next subnode + curIndex++; + } + else + { + //skip node + curIndex+= getEscapeNodeIndex(curIndex); + } + } + if(collided_results.size()>0) return true; + return false; +} + + + +//! returns the indices of the primitives in the m_primitive_manager +bool btGImpactQuantizedBvh::rayQuery( + const btVector3 & ray_dir,const btVector3 & ray_origin , + btAlignedObjectArray & collided_results) const +{ + int curIndex = 0; + int numNodes = getNodeCount(); + + while (curIndex < numNodes) + { + btAABB bound; + getNodeBound(curIndex,bound); + + //catch bugs in tree data + + bool aabbOverlap = bound.collide_ray(ray_origin,ray_dir); + bool isleafnode = isLeafNode(curIndex); + + if (isleafnode && aabbOverlap) + { + collided_results.push_back(getNodeData( curIndex)); + } + + if (aabbOverlap || isleafnode) + { + //next subnode + curIndex++; + } + else + { + //skip node + curIndex+= getEscapeNodeIndex(curIndex); + } + } + if(collided_results.size()>0) return true; + return false; +} + + +SIMD_FORCE_INLINE bool _quantized_node_collision( + btGImpactQuantizedBvh * boxset0, btGImpactQuantizedBvh * boxset1, + const BT_BOX_BOX_TRANSFORM_CACHE & trans_cache_1to0, + int node0 ,int node1, bool complete_primitive_tests) +{ + btAABB box0; + boxset0->getNodeBound(node0,box0); + btAABB box1; + boxset1->getNodeBound(node1,box1); + + return box0.overlapping_trans_cache(box1,trans_cache_1to0,complete_primitive_tests ); +// box1.appy_transform_trans_cache(trans_cache_1to0); +// return box0.has_collision(box1); + +} + + +//stackless recursive collision routine +static void _find_quantized_collision_pairs_recursive( + btGImpactQuantizedBvh * boxset0, btGImpactQuantizedBvh * boxset1, + btPairSet * collision_pairs, + const BT_BOX_BOX_TRANSFORM_CACHE & trans_cache_1to0, + int node0, int node1, bool complete_primitive_tests) +{ + + + + if( _quantized_node_collision( + boxset0,boxset1,trans_cache_1to0, + node0,node1,complete_primitive_tests) ==false) return;//avoid colliding internal nodes + + if(boxset0->isLeafNode(node0)) + { + if(boxset1->isLeafNode(node1)) + { + // collision result + collision_pairs->push_pair( + boxset0->getNodeData(node0),boxset1->getNodeData(node1)); + return; + } + else + { + + //collide left recursive + + _find_quantized_collision_pairs_recursive( + boxset0,boxset1, + collision_pairs,trans_cache_1to0, + node0,boxset1->getLeftNode(node1),false); + + //collide right recursive + _find_quantized_collision_pairs_recursive( + boxset0,boxset1, + collision_pairs,trans_cache_1to0, + node0,boxset1->getRightNode(node1),false); + + + } + } + else + { + if(boxset1->isLeafNode(node1)) + { + + //collide left recursive + _find_quantized_collision_pairs_recursive( + boxset0,boxset1, + collision_pairs,trans_cache_1to0, + boxset0->getLeftNode(node0),node1,false); + + + //collide right recursive + + _find_quantized_collision_pairs_recursive( + boxset0,boxset1, + collision_pairs,trans_cache_1to0, + boxset0->getRightNode(node0),node1,false); + + + } + else + { + //collide left0 left1 + + + + _find_quantized_collision_pairs_recursive( + boxset0,boxset1, + collision_pairs,trans_cache_1to0, + boxset0->getLeftNode(node0),boxset1->getLeftNode(node1),false); + + //collide left0 right1 + + _find_quantized_collision_pairs_recursive( + boxset0,boxset1, + collision_pairs,trans_cache_1to0, + boxset0->getLeftNode(node0),boxset1->getRightNode(node1),false); + + + //collide right0 left1 + + _find_quantized_collision_pairs_recursive( + boxset0,boxset1, + collision_pairs,trans_cache_1to0, + boxset0->getRightNode(node0),boxset1->getLeftNode(node1),false); + + //collide right0 right1 + + _find_quantized_collision_pairs_recursive( + boxset0,boxset1, + collision_pairs,trans_cache_1to0, + boxset0->getRightNode(node0),boxset1->getRightNode(node1),false); + + }// else if node1 is not a leaf + }// else if node0 is not a leaf +} + + +void btGImpactQuantizedBvh::find_collision(btGImpactQuantizedBvh * boxset0, const btTransform & trans0, + btGImpactQuantizedBvh * boxset1, const btTransform & trans1, + btPairSet & collision_pairs) +{ + + if(boxset0->getNodeCount()==0 || boxset1->getNodeCount()==0 ) return; + + BT_BOX_BOX_TRANSFORM_CACHE trans_cache_1to0; + + trans_cache_1to0.calc_from_homogenic(trans0,trans1); + +#ifdef TRI_COLLISION_PROFILING + bt_begin_gim02_q_tree_time(); +#endif //TRI_COLLISION_PROFILING + + _find_quantized_collision_pairs_recursive( + boxset0,boxset1, + &collision_pairs,trans_cache_1to0,0,0,true); +#ifdef TRI_COLLISION_PROFILING + bt_end_gim02_q_tree_time(); +#endif //TRI_COLLISION_PROFILING + +} + + diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactQuantizedBvh.h b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactQuantizedBvh.h new file mode 100644 index 000000000..e9cccac75 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactQuantizedBvh.h @@ -0,0 +1,372 @@ +#ifndef GIM_QUANTIZED_SET_H_INCLUDED +#define GIM_QUANTIZED_SET_H_INCLUDED + +/*! \file btGImpactQuantizedBvh.h +\author Francisco Len Nßjera +*/ +/* +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2007 Francisco Leon Najera. C.C. 80087371. +email: projectileman@yahoo.com + + +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 "btGImpactBvh.h" +#include "btQuantization.h" + + + + + +///btQuantizedBvhNode is a compressed aabb node, 16 bytes. +///Node can be used for leafnode or internal node. Leafnodes can point to 32-bit triangle index (non-negative range). +ATTRIBUTE_ALIGNED16 (struct) BT_QUANTIZED_BVH_NODE +{ + //12 bytes + unsigned short int m_quantizedAabbMin[3]; + unsigned short int m_quantizedAabbMax[3]; + //4 bytes + int m_escapeIndexOrDataIndex; + + BT_QUANTIZED_BVH_NODE() + { + m_escapeIndexOrDataIndex = 0; + } + + SIMD_FORCE_INLINE bool isLeafNode() const + { + //skipindex is negative (internal node), triangleindex >=0 (leafnode) + return (m_escapeIndexOrDataIndex>=0); + } + + SIMD_FORCE_INLINE int getEscapeIndex() const + { + //btAssert(m_escapeIndexOrDataIndex < 0); + return -m_escapeIndexOrDataIndex; + } + + SIMD_FORCE_INLINE void setEscapeIndex(int index) + { + m_escapeIndexOrDataIndex = -index; + } + + SIMD_FORCE_INLINE int getDataIndex() const + { + //btAssert(m_escapeIndexOrDataIndex >= 0); + + return m_escapeIndexOrDataIndex; + } + + SIMD_FORCE_INLINE void setDataIndex(int index) + { + m_escapeIndexOrDataIndex = index; + } + + SIMD_FORCE_INLINE bool testQuantizedBoxOverlapp( + unsigned short * quantizedMin,unsigned short * quantizedMax) const + { + if(m_quantizedAabbMin[0] > quantizedMax[0] || + m_quantizedAabbMax[0] < quantizedMin[0] || + m_quantizedAabbMin[1] > quantizedMax[1] || + m_quantizedAabbMax[1] < quantizedMin[1] || + m_quantizedAabbMin[2] > quantizedMax[2] || + m_quantizedAabbMax[2] < quantizedMin[2]) + { + return false; + } + return true; + } + +}; + + + +class GIM_QUANTIZED_BVH_NODE_ARRAY:public btAlignedObjectArray +{ +}; + + + + +//! Basic Box tree structure +class btQuantizedBvhTree +{ +protected: + int m_num_nodes; + GIM_QUANTIZED_BVH_NODE_ARRAY m_node_array; + btAABB m_global_bound; + btVector3 m_bvhQuantization; +protected: + void calc_quantization(GIM_BVH_DATA_ARRAY & primitive_boxes, btScalar boundMargin = btScalar(1.0) ); + + int _sort_and_calc_splitting_index( + GIM_BVH_DATA_ARRAY & primitive_boxes, + int startIndex, int endIndex, int splitAxis); + + int _calc_splitting_axis(GIM_BVH_DATA_ARRAY & primitive_boxes, int startIndex, int endIndex); + + void _build_sub_tree(GIM_BVH_DATA_ARRAY & primitive_boxes, int startIndex, int endIndex); +public: + btQuantizedBvhTree() + { + m_num_nodes = 0; + } + + //! prototype functions for box tree management + //!@{ + void build_tree(GIM_BVH_DATA_ARRAY & primitive_boxes); + + SIMD_FORCE_INLINE void quantizePoint( + unsigned short * quantizedpoint, const btVector3 & point) const + { + bt_quantize_clamp(quantizedpoint,point,m_global_bound.m_min,m_global_bound.m_max,m_bvhQuantization); + } + + + SIMD_FORCE_INLINE bool testQuantizedBoxOverlapp( + int node_index, + unsigned short * quantizedMin,unsigned short * quantizedMax) const + { + return m_node_array[node_index].testQuantizedBoxOverlapp(quantizedMin,quantizedMax); + } + + SIMD_FORCE_INLINE void clearNodes() + { + m_node_array.clear(); + m_num_nodes = 0; + } + + //! node count + SIMD_FORCE_INLINE int getNodeCount() const + { + return m_num_nodes; + } + + //! tells if the node is a leaf + SIMD_FORCE_INLINE bool isLeafNode(int nodeindex) const + { + return m_node_array[nodeindex].isLeafNode(); + } + + SIMD_FORCE_INLINE int getNodeData(int nodeindex) const + { + return m_node_array[nodeindex].getDataIndex(); + } + + SIMD_FORCE_INLINE void getNodeBound(int nodeindex, btAABB & bound) const + { + bound.m_min = bt_unquantize( + m_node_array[nodeindex].m_quantizedAabbMin, + m_global_bound.m_min,m_bvhQuantization); + + bound.m_max = bt_unquantize( + m_node_array[nodeindex].m_quantizedAabbMax, + m_global_bound.m_min,m_bvhQuantization); + } + + SIMD_FORCE_INLINE void setNodeBound(int nodeindex, const btAABB & bound) + { + bt_quantize_clamp( m_node_array[nodeindex].m_quantizedAabbMin, + bound.m_min, + m_global_bound.m_min, + m_global_bound.m_max, + m_bvhQuantization); + + bt_quantize_clamp( m_node_array[nodeindex].m_quantizedAabbMax, + bound.m_max, + m_global_bound.m_min, + m_global_bound.m_max, + m_bvhQuantization); + } + + SIMD_FORCE_INLINE int getLeftNode(int nodeindex) const + { + return nodeindex+1; + } + + SIMD_FORCE_INLINE int getRightNode(int nodeindex) const + { + if(m_node_array[nodeindex+1].isLeafNode()) return nodeindex+2; + return nodeindex+1 + m_node_array[nodeindex+1].getEscapeIndex(); + } + + SIMD_FORCE_INLINE int getEscapeNodeIndex(int nodeindex) const + { + return m_node_array[nodeindex].getEscapeIndex(); + } + + SIMD_FORCE_INLINE const BT_QUANTIZED_BVH_NODE * get_node_pointer(int index = 0) const + { + return &m_node_array[index]; + } + + //!@} +}; + + + +//! Structure for containing Boxes +/*! +This class offers an structure for managing a box tree of primitives. +Requires a Primitive prototype (like btPrimitiveManagerBase ) +*/ +class btGImpactQuantizedBvh +{ +protected: + btQuantizedBvhTree m_box_tree; + btPrimitiveManagerBase * m_primitive_manager; + +protected: + //stackless refit + void refit(); +public: + + //! this constructor doesn't build the tree. you must call buildSet + btGImpactQuantizedBvh() + { + m_primitive_manager = NULL; + } + + //! this constructor doesn't build the tree. you must call buildSet + btGImpactQuantizedBvh(btPrimitiveManagerBase * primitive_manager) + { + m_primitive_manager = primitive_manager; + } + + SIMD_FORCE_INLINE btAABB getGlobalBox() const + { + btAABB totalbox; + getNodeBound(0, totalbox); + return totalbox; + } + + SIMD_FORCE_INLINE void setPrimitiveManager(btPrimitiveManagerBase * primitive_manager) + { + m_primitive_manager = primitive_manager; + } + + SIMD_FORCE_INLINE btPrimitiveManagerBase * getPrimitiveManager() const + { + return m_primitive_manager; + } + + +//! node manager prototype functions +///@{ + + //! this attemps to refit the box set. + SIMD_FORCE_INLINE void update() + { + refit(); + } + + //! this rebuild the entire set + void buildSet(); + + //! returns the indices of the primitives in the m_primitive_manager + bool boxQuery(const btAABB & box, btAlignedObjectArray & collided_results) const; + + //! returns the indices of the primitives in the m_primitive_manager + SIMD_FORCE_INLINE bool boxQueryTrans(const btAABB & box, + const btTransform & transform, btAlignedObjectArray & collided_results) const + { + btAABB transbox=box; + transbox.appy_transform(transform); + return boxQuery(transbox,collided_results); + } + + //! returns the indices of the primitives in the m_primitive_manager + bool rayQuery( + const btVector3 & ray_dir,const btVector3 & ray_origin , + btAlignedObjectArray & collided_results) const; + + //! tells if this set has hierarcht + SIMD_FORCE_INLINE bool hasHierarchy() const + { + return true; + } + + //! tells if this set is a trimesh + SIMD_FORCE_INLINE bool isTrimesh() const + { + return m_primitive_manager->is_trimesh(); + } + + //! node count + SIMD_FORCE_INLINE int getNodeCount() const + { + return m_box_tree.getNodeCount(); + } + + //! tells if the node is a leaf + SIMD_FORCE_INLINE bool isLeafNode(int nodeindex) const + { + return m_box_tree.isLeafNode(nodeindex); + } + + SIMD_FORCE_INLINE int getNodeData(int nodeindex) const + { + return m_box_tree.getNodeData(nodeindex); + } + + SIMD_FORCE_INLINE void getNodeBound(int nodeindex, btAABB & bound) const + { + m_box_tree.getNodeBound(nodeindex, bound); + } + + SIMD_FORCE_INLINE void setNodeBound(int nodeindex, const btAABB & bound) + { + m_box_tree.setNodeBound(nodeindex, bound); + } + + + SIMD_FORCE_INLINE int getLeftNode(int nodeindex) const + { + return m_box_tree.getLeftNode(nodeindex); + } + + SIMD_FORCE_INLINE int getRightNode(int nodeindex) const + { + return m_box_tree.getRightNode(nodeindex); + } + + SIMD_FORCE_INLINE int getEscapeNodeIndex(int nodeindex) const + { + return m_box_tree.getEscapeNodeIndex(nodeindex); + } + + SIMD_FORCE_INLINE void getNodeTriangle(int nodeindex,btPrimitiveTriangle & triangle) const + { + m_primitive_manager->get_primitive_triangle(getNodeData(nodeindex),triangle); + } + + + SIMD_FORCE_INLINE const BT_QUANTIZED_BVH_NODE * get_node_pointer(int index = 0) const + { + return m_box_tree.get_node_pointer(index); + } + + + static float getAverageTreeCollisionTime(); + + + static void find_collision(btGImpactQuantizedBvh * boxset1, const btTransform & trans1, + btGImpactQuantizedBvh * boxset2, const btTransform & trans2, + btPairSet & collision_pairs); +}; + + +#endif // GIM_BOXPRUNING_H_INCLUDED diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactShape.cpp b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactShape.cpp new file mode 100644 index 000000000..da6a4dbfc --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactShape.cpp @@ -0,0 +1,183 @@ +/* +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2007 Francisco Leon Najera. C.C. 80087371. +email: projectileman@yahoo.com + + +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 "btGImpactShape.h" +#include "btGImpactMassUtil.h" + + +#define CALC_EXACT_INERTIA 1 + +void btGImpactCompoundShape::calculateLocalInertia(btScalar mass,btVector3& inertia) const +{ + lockChildShapes(); +#ifdef CALC_EXACT_INERTIA + inertia.setValue(0.f,0.f,0.f); + + int i = this->getNumChildShapes(); + btScalar shapemass = mass/btScalar(i); + + while(i--) + { + btVector3 temp_inertia; + m_childShapes[i]->calculateLocalInertia(shapemass,temp_inertia); + if(childrenHasTransform()) + { + inertia = gim_inertia_add_transformed( inertia,temp_inertia,m_childTransforms[i]); + } + else + { + inertia = gim_inertia_add_transformed( inertia,temp_inertia,btTransform::getIdentity()); + } + + } + +#else + + // Calc box inertia + + btScalar lx= m_localAABB.m_max[0] - m_localAABB.m_min[0]; + btScalar ly= m_localAABB.m_max[1] - m_localAABB.m_min[1]; + btScalar lz= m_localAABB.m_max[2] - m_localAABB.m_min[2]; + const btScalar x2 = lx*lx; + const btScalar y2 = ly*ly; + const btScalar z2 = lz*lz; + const btScalar scaledmass = mass * btScalar(0.08333333); + + inertia = scaledmass * (btVector3(y2+z2,x2+z2,x2+y2)); + +#endif + unlockChildShapes(); +} + + + +void btGImpactMeshShapePart::calculateLocalInertia(btScalar mass,btVector3& inertia) const +{ + lockChildShapes(); + + +#ifdef CALC_EXACT_INERTIA + inertia.setValue(0.f,0.f,0.f); + + int i = this->getVertexCount(); + btScalar pointmass = mass/btScalar(i); + + while(i--) + { + btVector3 pointintertia; + this->getVertex(i,pointintertia); + pointintertia = gim_get_point_inertia(pointintertia,pointmass); + inertia+=pointintertia; + } + +#else + + // Calc box inertia + + btScalar lx= m_localAABB.m_max[0] - m_localAABB.m_min[0]; + btScalar ly= m_localAABB.m_max[1] - m_localAABB.m_min[1]; + btScalar lz= m_localAABB.m_max[2] - m_localAABB.m_min[2]; + const btScalar x2 = lx*lx; + const btScalar y2 = ly*ly; + const btScalar z2 = lz*lz; + const btScalar scaledmass = mass * btScalar(0.08333333); + + inertia = scaledmass * (btVector3(y2+z2,x2+z2,x2+y2)); + +#endif + + unlockChildShapes(); +} + +void btGImpactMeshShape::calculateLocalInertia(btScalar mass,btVector3& inertia) const +{ + +#ifdef CALC_EXACT_INERTIA + inertia.setValue(0.f,0.f,0.f); + + int i = this->getMeshPartCount(); + btScalar partmass = mass/btScalar(i); + + while(i--) + { + btVector3 partinertia; + getMeshPart(i)->calculateLocalInertia(partmass,partinertia); + inertia+=partinertia; + } + +#else + + // Calc box inertia + + btScalar lx= m_localAABB.m_max[0] - m_localAABB.m_min[0]; + btScalar ly= m_localAABB.m_max[1] - m_localAABB.m_min[1]; + btScalar lz= m_localAABB.m_max[2] - m_localAABB.m_min[2]; + const btScalar x2 = lx*lx; + const btScalar y2 = ly*ly; + const btScalar z2 = lz*lz; + const btScalar scaledmass = mass * btScalar(0.08333333); + + inertia = scaledmass * (btVector3(y2+z2,x2+z2,x2+y2)); + +#endif +} + +void btGImpactMeshShape::rayTest(const btVector3& rayFrom, const btVector3& rayTo, btCollisionWorld::RayResultCallback& resultCallback) const +{ +} + + +void btGImpactMeshShapePart::processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const +{ + lockChildShapes(); + btAABB box; + box.m_min = aabbMin; + box.m_max = aabbMax; + + btAlignedObjectArray collided; + m_box_set.boxQuery(box,collided); + + if(collided.size()==0) + { + unlockChildShapes(); + return; + } + + int part = (int)getPart(); + btPrimitiveTriangle triangle; + int i = collided.size(); + while(i--) + { + this->getPrimitiveTriangle(collided[i],triangle); + callback->processTriangle(triangle.m_vertices,part,collided[i]); + } + unlockChildShapes(); + +} + +void btGImpactMeshShape::processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const +{ + int i = m_mesh_parts.size(); + while(i--) + { + m_mesh_parts[i]->processAllTriangles(callback,aabbMin,aabbMax); + } +} diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactShape.h b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactShape.h new file mode 100644 index 000000000..598dc6d5fd --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactShape.h @@ -0,0 +1,1124 @@ +/*! \file btGImpactShape.h +\author Francisco Len Nßjera +*/ +/* +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2007 Francisco Leon Najera. C.C. 80087371. +email: projectileman@yahoo.com + + +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 GIMPACT_SHAPE_H +#define GIMPACT_SHAPE_H + +#include "BulletCollision/CollisionShapes/btCollisionShape.h" +#include "BulletCollision/CollisionShapes/btTriangleShape.h" +#include "BulletCollision/CollisionShapes/btStridingMeshInterface.h" +#include "BulletCollision/CollisionShapes/btCollisionMargin.h" +#include "BulletCollision/CollisionDispatch/btCollisionWorld.h" +#include "BulletCollision/CollisionShapes/btConcaveShape.h" +#include "BulletCollision/CollisionShapes/btTetrahedronShape.h" +#include "LinearMath/btVector3.h" +#include "LinearMath/btTransform.h" +#include "LinearMath/btMatrix3x3.h" +#include "LinearMath/btAlignedObjectArray.h" + +#include "btGImpactQuantizedBvh.h" // box tree class + + +//! declare Quantized trees, (you can change to float based trees) +typedef btGImpactQuantizedBvh btGImpactBoxSet; + +enum eGIMPACT_SHAPE_TYPE +{ + CONST_GIMPACT_COMPOUND_SHAPE = 0, + CONST_GIMPACT_TRIMESH_SHAPE_PART, + CONST_GIMPACT_TRIMESH_SHAPE +}; + + +//! Helper class for tetrahedrons +class btTetrahedronShapeEx:public btBU_Simplex1to4 +{ +public: + btTetrahedronShapeEx() + { + m_numVertices = 4; + } + + + SIMD_FORCE_INLINE void setVertices( + const btVector3 & v0,const btVector3 & v1, + const btVector3 & v2,const btVector3 & v3) + { + m_vertices[0] = v0; + m_vertices[1] = v1; + m_vertices[2] = v2; + m_vertices[3] = v3; + recalcLocalAabb(); + } +}; + + +//! Base class for gimpact shapes +class btGImpactShapeInterface : public btConcaveShape +{ +protected: + btAABB m_localAABB; + bool m_needs_update; + btVector3 localScaling; + btGImpactBoxSet m_box_set;// optionally boxset + + //! use this function for perfofm refit in bounding boxes + //! use this function for perfofm refit in bounding boxes + virtual void calcLocalAABB() + { + lockChildShapes(); + if(m_box_set.getNodeCount() == 0) + { + m_box_set.buildSet(); + } + else + { + m_box_set.update(); + } + unlockChildShapes(); + + m_localAABB = m_box_set.getGlobalBox(); + } + + +public: + btGImpactShapeInterface() + { + m_shapeType=GIMPACT_SHAPE_PROXYTYPE; + m_localAABB.invalidate(); + m_needs_update = true; + localScaling.setValue(1.f,1.f,1.f); + } + + + //! performs refit operation + /*! + Updates the entire Box set of this shape. + \pre postUpdate() must be called for attemps to calculating the box set, else this function + will does nothing. + \post if m_needs_update == true, then it calls calcLocalAABB(); + */ + SIMD_FORCE_INLINE void updateBound() + { + if(!m_needs_update) return; + calcLocalAABB(); + m_needs_update = false; + } + + //! If the Bounding box is not updated, then this class attemps to calculate it. + /*! + \post Calls updateBound() for update the box set. + */ + void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const + { + btAABB transformedbox = m_localAABB; + transformedbox.appy_transform(t); + aabbMin = transformedbox.m_min; + aabbMax = transformedbox.m_max; + } + + //! Tells to this object that is needed to refit the box set + virtual void postUpdate() + { + m_needs_update = true; + } + + //! Obtains the local box, which is the global calculated box of the total of subshapes + SIMD_FORCE_INLINE const btAABB & getLocalBox() + { + return m_localAABB; + } + + + virtual int getShapeType() const + { + return GIMPACT_SHAPE_PROXYTYPE; + } + + /*! + \post You must call updateBound() for update the box set. + */ + virtual void setLocalScaling(const btVector3& scaling) + { + localScaling = scaling; + postUpdate(); + } + + virtual const btVector3& getLocalScaling() const + { + return localScaling; + } + + + virtual void setMargin(btScalar margin) + { + m_collisionMargin = margin; + int i = getNumChildShapes(); + while(i--) + { + btCollisionShape* child = getChildShape(i); + child->setMargin(margin); + } + + m_needs_update = true; + } + + + //! Subshape member functions + //!@{ + + //! Base method for determinig which kind of GIMPACT shape we get + virtual eGIMPACT_SHAPE_TYPE getGImpactShapeType() = 0; + + //! gets boxset + SIMD_FORCE_INLINE btGImpactBoxSet * getBoxSet() + { + return &m_box_set; + } + + //! Determines if this class has a hierarchy structure for sorting its primitives + SIMD_FORCE_INLINE bool hasBoxSet() const + { + if(m_box_set.getNodeCount() == 0) return false; + return true; + } + + //! Obtains the primitive manager + virtual const btPrimitiveManagerBase * getPrimitiveManager() const = 0; + + + //! Gets the number of children + virtual int getNumChildShapes() const = 0; + + //! if true, then its children must get transforms. + virtual bool childrenHasTransform() const = 0; + + //! Determines if this shape has triangles + virtual bool needsRetrieveTriangles() const = 0; + + //! Determines if this shape has tetrahedrons + virtual bool needsRetrieveTetrahedrons() const = 0; + + virtual void getBulletTriangle(int prim_index,btTriangleShapeEx & triangle) const = 0; + + virtual void getBulletTetrahedron(int prim_index,btTetrahedronShapeEx & tetrahedron) const = 0; + + + + //! call when reading child shapes + virtual void lockChildShapes() const + { + } + + virtual void unlockChildShapes() const + { + } + + //! if this trimesh + SIMD_FORCE_INLINE void getPrimitiveTriangle(int index,btPrimitiveTriangle & triangle) const + { + getPrimitiveManager()->get_primitive_triangle(index,triangle); + } + + + //! Retrieves the bound from a child + /*! + */ + virtual void getChildAabb(int child_index,const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const + { + btAABB child_aabb; + getPrimitiveManager()->get_primitive_box(child_index,child_aabb); + child_aabb.appy_transform(t); + aabbMin = child_aabb.m_min; + aabbMax = child_aabb.m_max; + } + + //! Gets the children + virtual btCollisionShape* getChildShape(int index) = 0; + + + //! Gets the child + virtual const btCollisionShape* getChildShape(int index) const = 0; + + //! Gets the children transform + virtual btTransform getChildTransform(int index) const = 0; + + //! Sets the children transform + /*! + \post You must call updateBound() for update the box set. + */ + virtual void setChildTransform(int index, const btTransform & transform) = 0; + + //!@} + + + //! virtual method for ray collision + virtual void rayTest(const btVector3& rayFrom, const btVector3& rayTo, btCollisionWorld::RayResultCallback& resultCallback) const + { + } + + //! Function for retrieve triangles. + /*! + It gives the triangles in local space + */ + virtual void processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const + { + } + + //!@} + +}; + + +//! btGImpactCompoundShape allows to handle multiple btCollisionShape objects at once +/*! +This class only can manage Convex subshapes +*/ +class btGImpactCompoundShape : public btGImpactShapeInterface +{ +public: + //! compound primitive manager + class CompoundPrimitiveManager:public btPrimitiveManagerBase + { + public: + virtual ~CompoundPrimitiveManager() {} + btGImpactCompoundShape * m_compoundShape; + + + CompoundPrimitiveManager(const CompoundPrimitiveManager& compound) + { + m_compoundShape = compound.m_compoundShape; + } + + CompoundPrimitiveManager(btGImpactCompoundShape * compoundShape) + { + m_compoundShape = compoundShape; + } + + CompoundPrimitiveManager() + { + m_compoundShape = NULL; + } + + virtual bool is_trimesh() const + { + return false; + } + + virtual int get_primitive_count() const + { + return (int )m_compoundShape->getNumChildShapes(); + } + + virtual void get_primitive_box(int prim_index ,btAABB & primbox) const + { + btTransform prim_trans; + if(m_compoundShape->childrenHasTransform()) + { + prim_trans = m_compoundShape->getChildTransform(prim_index); + } + else + { + prim_trans.setIdentity(); + } + const btCollisionShape* shape = m_compoundShape->getChildShape(prim_index); + shape->getAabb(prim_trans,primbox.m_min,primbox.m_max); + } + + virtual void get_primitive_triangle(int prim_index,btPrimitiveTriangle & triangle) const + { + btAssert(0); + } + + }; + + + +protected: + CompoundPrimitiveManager m_primitive_manager; + btAlignedObjectArray m_childTransforms; + btAlignedObjectArray m_childShapes; + + +public: + + btGImpactCompoundShape(bool children_has_transform = true) + { + m_primitive_manager.m_compoundShape = this; + m_box_set.setPrimitiveManager(&m_primitive_manager); + } + + virtual ~btGImpactCompoundShape() + { + } + + + //! if true, then its children must get transforms. + virtual bool childrenHasTransform() const + { + if(m_childTransforms.size()==0) return false; + return true; + } + + + //! Obtains the primitive manager + virtual const btPrimitiveManagerBase * getPrimitiveManager() const + { + return &m_primitive_manager; + } + + //! Obtains the compopund primitive manager + SIMD_FORCE_INLINE CompoundPrimitiveManager * getCompoundPrimitiveManager() + { + return &m_primitive_manager; + } + + //! Gets the number of children + virtual int getNumChildShapes() const + { + return m_childShapes.size(); + } + + + //! Use this method for adding children. Only Convex shapes are allowed. + void addChildShape(const btTransform& localTransform,btCollisionShape* shape) + { + btAssert(shape->isConvex()); + m_childTransforms.push_back(localTransform); + m_childShapes.push_back(shape); + } + + //! Use this method for adding children. Only Convex shapes are allowed. + void addChildShape(btCollisionShape* shape) + { + btAssert(shape->isConvex()); + m_childShapes.push_back(shape); + } + + //! Gets the children + virtual btCollisionShape* getChildShape(int index) + { + return m_childShapes[index]; + } + + //! Gets the children + virtual const btCollisionShape* getChildShape(int index) const + { + return m_childShapes[index]; + } + + //! Retrieves the bound from a child + /*! + */ + virtual void getChildAabb(int child_index,const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const + { + + if(childrenHasTransform()) + { + m_childShapes[child_index]->getAabb(t*m_childTransforms[child_index],aabbMin,aabbMax); + } + else + { + m_childShapes[child_index]->getAabb(t,aabbMin,aabbMax); + } + } + + + //! Gets the children transform + virtual btTransform getChildTransform(int index) const + { + btAssert(m_childTransforms.size() == m_childShapes.size()); + return m_childTransforms[index]; + } + + //! Sets the children transform + /*! + \post You must call updateBound() for update the box set. + */ + virtual void setChildTransform(int index, const btTransform & transform) + { + btAssert(m_childTransforms.size() == m_childShapes.size()); + m_childTransforms[index] = transform; + postUpdate(); + } + + //! Determines if this shape has triangles + virtual bool needsRetrieveTriangles() const + { + return false; + } + + //! Determines if this shape has tetrahedrons + virtual bool needsRetrieveTetrahedrons() const + { + return false; + } + + + virtual void getBulletTriangle(int prim_index,btTriangleShapeEx & triangle) const + { + btAssert(0); + } + + virtual void getBulletTetrahedron(int prim_index,btTetrahedronShapeEx & tetrahedron) const + { + btAssert(0); + } + + + //! Calculates the exact inertia tensor for this shape + virtual void calculateLocalInertia(btScalar mass,btVector3& inertia) const; + + virtual const char* getName()const + { + return "GImpactCompound"; + } + + virtual eGIMPACT_SHAPE_TYPE getGImpactShapeType() + { + return CONST_GIMPACT_COMPOUND_SHAPE; + } + +}; + + + +//! This class manages a sub part of a mesh supplied by the btStridingMeshInterface interface. +/*! +- Simply create this shape by passing the btStridingMeshInterface to the constructor btGImpactMeshShapePart, then you must call updateBound() after creating the mesh +- When making operations with this shape, you must call lock before accessing to the trimesh primitives, and then call unlock +- You can handle deformable meshes with this shape, by calling postUpdate() every time when changing the mesh vertices. + +*/ +class btGImpactMeshShapePart : public btGImpactShapeInterface +{ +public: + //! Trimesh primitive manager + /*! + Manages the info from btStridingMeshInterface object and controls the Lock/Unlock mechanism + */ + class TrimeshPrimitiveManager:public btPrimitiveManagerBase + { + public: + btScalar m_margin; + btStridingMeshInterface * m_meshInterface; + btVector3 m_scale; + int m_part; + int m_lock_count; + const unsigned char *vertexbase; + int numverts; + PHY_ScalarType type; + int stride; + const unsigned char *indexbase; + int indexstride; + int numfaces; + PHY_ScalarType indicestype; + + TrimeshPrimitiveManager() + { + m_meshInterface = NULL; + m_part = 0; + m_margin = 0.01f; + m_scale = btVector3(1.f,1.f,1.f); + m_lock_count = 0; + vertexbase = 0; + numverts = 0; + stride = 0; + indexbase = 0; + indexstride = 0; + numfaces = 0; + } + + TrimeshPrimitiveManager(const TrimeshPrimitiveManager & manager) + { + m_meshInterface = manager.m_meshInterface; + m_part = manager.m_part; + m_margin = manager.m_margin; + m_scale = manager.m_scale; + m_lock_count = 0; + vertexbase = 0; + numverts = 0; + stride = 0; + indexbase = 0; + indexstride = 0; + numfaces = 0; + + } + + TrimeshPrimitiveManager( + btStridingMeshInterface * meshInterface, int part) + { + m_meshInterface = meshInterface; + m_part = part; + m_scale = m_meshInterface->getScaling(); + m_margin = 0.1f; + m_lock_count = 0; + vertexbase = 0; + numverts = 0; + stride = 0; + indexbase = 0; + indexstride = 0; + numfaces = 0; + + } + + virtual ~TrimeshPrimitiveManager() {} + + void lock() + { + if(m_lock_count>0) + { + m_lock_count++; + return; + } + m_meshInterface->getLockedReadOnlyVertexIndexBase( + &vertexbase,numverts, + type, stride,&indexbase, indexstride, numfaces,indicestype,m_part); + + m_lock_count = 1; + } + + void unlock() + { + if(m_lock_count == 0) return; + if(m_lock_count>1) + { + --m_lock_count; + return; + } + m_meshInterface->unLockReadOnlyVertexBase(m_part); + vertexbase = NULL; + m_lock_count = 0; + } + + virtual bool is_trimesh() const + { + return true; + } + + virtual int get_primitive_count() const + { + return (int )numfaces; + } + + SIMD_FORCE_INLINE int get_vertex_count() const + { + return (int )numverts; + } + + SIMD_FORCE_INLINE void get_indices(int face_index,int &i0,int &i1,int &i2) const + { + if(indicestype == PHY_SHORT) + { + short * s_indices = (short *)(indexbase + face_index*indexstride); + i0 = s_indices[0]; + i1 = s_indices[1]; + i2 = s_indices[2]; + } + else + { + int * i_indices = (int *)(indexbase + face_index*indexstride); + i0 = i_indices[0]; + i1 = i_indices[1]; + i2 = i_indices[2]; + } + } + + SIMD_FORCE_INLINE void get_vertex(int vertex_index, btVector3 & vertex) const + { + if(type == PHY_DOUBLE) + { + double * dvertices = (double *)(vertexbase + vertex_index*stride); + vertex[0] = btScalar(dvertices[0]*m_scale[0]); + vertex[1] = btScalar(dvertices[1]*m_scale[1]); + vertex[2] = btScalar(dvertices[2]*m_scale[2]); + } + else + { + float * svertices = (float *)(vertexbase + vertex_index*stride); + vertex[0] = svertices[0]*m_scale[0]; + vertex[1] = svertices[1]*m_scale[1]; + vertex[2] = svertices[2]*m_scale[2]; + } + } + + virtual void get_primitive_box(int prim_index ,btAABB & primbox) const + { + btPrimitiveTriangle triangle; + get_primitive_triangle(prim_index,triangle); + primbox.calc_from_triangle_margin( + triangle.m_vertices[0], + triangle.m_vertices[1],triangle.m_vertices[2],triangle.m_margin); + } + + virtual void get_primitive_triangle(int prim_index,btPrimitiveTriangle & triangle) const + { + int indices[3]; + get_indices(prim_index,indices[0],indices[1],indices[2]); + get_vertex(indices[0],triangle.m_vertices[0]); + get_vertex(indices[1],triangle.m_vertices[1]); + get_vertex(indices[2],triangle.m_vertices[2]); + triangle.m_margin = m_margin; + } + + SIMD_FORCE_INLINE void get_bullet_triangle(int prim_index,btTriangleShapeEx & triangle) const + { + int indices[3]; + get_indices(prim_index,indices[0],indices[1],indices[2]); + get_vertex(indices[0],triangle.m_vertices1[0]); + get_vertex(indices[1],triangle.m_vertices1[1]); + get_vertex(indices[2],triangle.m_vertices1[2]); + triangle.setMargin(m_margin); + } + + }; + + +protected: + TrimeshPrimitiveManager m_primitive_manager; +public: + + btGImpactMeshShapePart() + { + m_box_set.setPrimitiveManager(&m_primitive_manager); + } + + + btGImpactMeshShapePart(btStridingMeshInterface * meshInterface, int part) + { + m_primitive_manager.m_meshInterface = meshInterface; + m_primitive_manager.m_part = part; + m_box_set.setPrimitiveManager(&m_primitive_manager); + } + + virtual ~btGImpactMeshShapePart() + { + } + + //! if true, then its children must get transforms. + virtual bool childrenHasTransform() const + { + return false; + } + + + //! call when reading child shapes + virtual void lockChildShapes() const + { + void * dummy = (void*)(m_box_set.getPrimitiveManager()); + TrimeshPrimitiveManager * dummymanager = static_cast(dummy); + dummymanager->lock(); + } + + virtual void unlockChildShapes() const + { + void * dummy = (void*)(m_box_set.getPrimitiveManager()); + TrimeshPrimitiveManager * dummymanager = static_cast(dummy); + dummymanager->unlock(); + } + + //! Gets the number of children + virtual int getNumChildShapes() const + { + return m_primitive_manager.get_primitive_count(); + } + + + //! Gets the children + virtual btCollisionShape* getChildShape(int index) + { + btAssert(0); + return NULL; + } + + + + //! Gets the child + virtual const btCollisionShape* getChildShape(int index) const + { + btAssert(0); + return NULL; + } + + //! Gets the children transform + virtual btTransform getChildTransform(int index) const + { + btAssert(0); + return btTransform(); + } + + //! Sets the children transform + /*! + \post You must call updateBound() for update the box set. + */ + virtual void setChildTransform(int index, const btTransform & transform) + { + btAssert(0); + } + + + //! Obtains the primitive manager + virtual const btPrimitiveManagerBase * getPrimitiveManager() const + { + return &m_primitive_manager; + } + + SIMD_FORCE_INLINE TrimeshPrimitiveManager * getTrimeshPrimitiveManager() + { + return &m_primitive_manager; + } + + + + + + virtual void calculateLocalInertia(btScalar mass,btVector3& inertia) const; + + + + + virtual const char* getName()const + { + return "GImpactMeshShapePart"; + } + + virtual eGIMPACT_SHAPE_TYPE getGImpactShapeType() + { + return CONST_GIMPACT_TRIMESH_SHAPE_PART; + } + + //! Determines if this shape has triangles + virtual bool needsRetrieveTriangles() const + { + return true; + } + + //! Determines if this shape has tetrahedrons + virtual bool needsRetrieveTetrahedrons() const + { + return false; + } + + virtual void getBulletTriangle(int prim_index,btTriangleShapeEx & triangle) const + { + m_primitive_manager.get_bullet_triangle(prim_index,triangle); + } + + virtual void getBulletTetrahedron(int prim_index,btTetrahedronShapeEx & tetrahedron) const + { + btAssert(0); + } + + + + SIMD_FORCE_INLINE int getVertexCount() const + { + return m_primitive_manager.get_vertex_count(); + } + + SIMD_FORCE_INLINE void getVertex(int vertex_index, btVector3 & vertex) const + { + m_primitive_manager.get_vertex(vertex_index,vertex); + } + + SIMD_FORCE_INLINE void setMargin(btScalar margin) + { + m_primitive_manager.m_margin = margin; + postUpdate(); + } + + SIMD_FORCE_INLINE btScalar getMargin() const + { + return m_primitive_manager.m_margin; + } + + virtual void setLocalScaling(const btVector3& scaling) + { + m_primitive_manager.m_scale = scaling; + postUpdate(); + } + + virtual const btVector3& getLocalScaling() const + { + return m_primitive_manager.m_scale; + } + + SIMD_FORCE_INLINE int getPart() const + { + return (int)m_primitive_manager.m_part; + } + + virtual void processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const; +}; + + +//! This class manages a mesh supplied by the btStridingMeshInterface interface. +/*! +Set of btGImpactMeshShapePart parts +- Simply create this shape by passing the btStridingMeshInterface to the constructor btGImpactMeshShape, then you must call updateBound() after creating the mesh + +- You can handle deformable meshes with this shape, by calling postUpdate() every time when changing the mesh vertices. + +*/ +class btGImpactMeshShape : public btGImpactShapeInterface +{ + btStridingMeshInterface* m_meshInterface; + +protected: + btAlignedObjectArray m_mesh_parts; + void buildMeshParts(btStridingMeshInterface * meshInterface) + { + for (int i=0;igetNumSubParts() ;++i ) + { + btGImpactMeshShapePart * newpart = new btGImpactMeshShapePart(meshInterface,i); + m_mesh_parts.push_back(newpart); + } + } + + //! use this function for perfofm refit in bounding boxes + virtual void calcLocalAABB() + { + m_localAABB.invalidate(); + int i = m_mesh_parts.size(); + while(i--) + { + m_mesh_parts[i]->updateBound(); + m_localAABB.merge(m_mesh_parts[i]->getLocalBox()); + } + } + +public: + btGImpactMeshShape(btStridingMeshInterface * meshInterface) + { + m_meshInterface = meshInterface; + buildMeshParts(meshInterface); + } + + virtual ~btGImpactMeshShape() + { + int i = m_mesh_parts.size(); + while(i--) + { + btGImpactMeshShapePart * part = m_mesh_parts[i]; + delete part; + } + m_mesh_parts.clear(); + } + + + btStridingMeshInterface* getMeshInterface() + { + return m_meshInterface; + } + + const btStridingMeshInterface* getMeshInterface() const + { + return m_meshInterface; + } + + int getMeshPartCount() const + { + return m_mesh_parts.size(); + } + + btGImpactMeshShapePart * getMeshPart(int index) + { + return m_mesh_parts[index]; + } + + + + const btGImpactMeshShapePart * getMeshPart(int index) const + { + return m_mesh_parts[index]; + } + + + virtual void setLocalScaling(const btVector3& scaling) + { + localScaling = scaling; + + int i = m_mesh_parts.size(); + while(i--) + { + btGImpactMeshShapePart * part = m_mesh_parts[i]; + part->setLocalScaling(scaling); + } + + m_needs_update = true; + } + + virtual void setMargin(btScalar margin) + { + m_collisionMargin = margin; + + int i = m_mesh_parts.size(); + while(i--) + { + btGImpactMeshShapePart * part = m_mesh_parts[i]; + part->setMargin(margin); + } + + m_needs_update = true; + } + + //! Tells to this object that is needed to refit all the meshes + virtual void postUpdate() + { + int i = m_mesh_parts.size(); + while(i--) + { + btGImpactMeshShapePart * part = m_mesh_parts[i]; + part->postUpdate(); + } + + m_needs_update = true; + } + + virtual void calculateLocalInertia(btScalar mass,btVector3& inertia) const; + + + //! Obtains the primitive manager + virtual const btPrimitiveManagerBase * getPrimitiveManager() const + { + btAssert(0); + return NULL; + } + + + //! Gets the number of children + virtual int getNumChildShapes() const + { + btAssert(0); + return 0; + } + + + //! if true, then its children must get transforms. + virtual bool childrenHasTransform() const + { + btAssert(0); + return false; + } + + //! Determines if this shape has triangles + virtual bool needsRetrieveTriangles() const + { + btAssert(0); + return false; + } + + //! Determines if this shape has tetrahedrons + virtual bool needsRetrieveTetrahedrons() const + { + btAssert(0); + return false; + } + + virtual void getBulletTriangle(int prim_index,btTriangleShapeEx & triangle) const + { + btAssert(0); + } + + virtual void getBulletTetrahedron(int prim_index,btTetrahedronShapeEx & tetrahedron) const + { + btAssert(0); + } + + //! call when reading child shapes + virtual void lockChildShapes() const + { + btAssert(0); + } + + virtual void unlockChildShapes() const + { + btAssert(0); + } + + + + + //! Retrieves the bound from a child + /*! + */ + virtual void getChildAabb(int child_index,const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const + { + btAssert(0); + } + + //! Gets the children + virtual btCollisionShape* getChildShape(int index) + { + btAssert(0); + return NULL; + } + + + //! Gets the child + virtual const btCollisionShape* getChildShape(int index) const + { + btAssert(0); + return NULL; + } + + //! Gets the children transform + virtual btTransform getChildTransform(int index) const + { + btAssert(0); + return btTransform(); + } + + //! Sets the children transform + /*! + \post You must call updateBound() for update the box set. + */ + virtual void setChildTransform(int index, const btTransform & transform) + { + btAssert(0); + } + + + virtual eGIMPACT_SHAPE_TYPE getGImpactShapeType() + { + return CONST_GIMPACT_TRIMESH_SHAPE; + } + + + virtual const char* getName()const + { + return "GImpactMesh"; + } + + virtual void rayTest(const btVector3& rayFrom, const btVector3& rayTo, btCollisionWorld::RayResultCallback& resultCallback) const; + + //! Function for retrieve triangles. + /*! + It gives the triangles in local space + */ + virtual void processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const; +}; + + +#endif //GIMPACT_MESH_SHAPE_H diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/btGenericPoolAllocator.cpp b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGenericPoolAllocator.cpp new file mode 100644 index 000000000..956fa0430 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGenericPoolAllocator.cpp @@ -0,0 +1,283 @@ +/*! \file btGenericPoolAllocator.cpp +\author Francisco Len Nßjera. email projectileman@yahoo.com + +General purpose allocator class +*/ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btGenericPoolAllocator.h" + + + +/// *************** btGenericMemoryPool ******************/////////// + +size_t btGenericMemoryPool::allocate_from_free_nodes(size_t num_elements) +{ + size_t ptr = BT_UINT_MAX; + + if(m_free_nodes_count == 0) return BT_UINT_MAX; + // find an avaliable free node with the correct size + size_t revindex = m_free_nodes_count; + + while(revindex-- && ptr == BT_UINT_MAX) + { + if(m_allocated_sizes[m_free_nodes[revindex]]>=num_elements) + { + ptr = revindex; + } + } + if(ptr == BT_UINT_MAX) return BT_UINT_MAX; // not found + + + revindex = ptr; + ptr = m_free_nodes[revindex]; + // post: ptr contains the node index, and revindex the index in m_free_nodes + + size_t finalsize = m_allocated_sizes[ptr]; + finalsize -= num_elements; + + m_allocated_sizes[ptr] = num_elements; + + // post: finalsize>=0, m_allocated_sizes[ptr] has the requested size + + if(finalsize>0) // preserve free node, there are some free memory + { + m_free_nodes[revindex] = ptr + num_elements; + m_allocated_sizes[ptr + num_elements] = finalsize; + } + else // delete free node + { + // swap with end + m_free_nodes[revindex] = m_free_nodes[m_free_nodes_count-1]; + m_free_nodes_count--; + } + + return ptr; +} + +size_t btGenericMemoryPool::allocate_from_pool(size_t num_elements) +{ + if(m_allocated_count+num_elements>m_max_element_count) return BT_UINT_MAX; + + size_t ptr = m_allocated_count; + + m_allocated_sizes[m_allocated_count] = num_elements; + m_allocated_count+=num_elements; + + return ptr; +} + + +void btGenericMemoryPool::init_pool(size_t element_size, size_t element_count) +{ + m_allocated_count = 0; + m_free_nodes_count = 0; + + m_element_size = element_size; + m_max_element_count = element_count; + + + + + m_pool = (unsigned char *) btAlignedAlloc(m_element_size*m_max_element_count,16); + m_free_nodes = (size_t *) btAlignedAlloc(sizeof(size_t)*m_max_element_count,16); + m_allocated_sizes = (size_t *) btAlignedAlloc(sizeof(size_t)*m_max_element_count,16); + + for (size_t i = 0;i< m_max_element_count;i++ ) + { + m_allocated_sizes[i] = 0; + } +} + +void btGenericMemoryPool::end_pool() +{ + btAlignedFree(m_pool); + btAlignedFree(m_free_nodes); + btAlignedFree(m_allocated_sizes); + m_allocated_count = 0; + m_free_nodes_count = 0; +} + + +//! Allocates memory in pool +/*! +\param size_bytes size in bytes of the buffer +*/ +void * btGenericMemoryPool::allocate(size_t size_bytes) +{ + + size_t module = size_bytes%m_element_size; + size_t element_count = size_bytes/m_element_size; + if(module>0) element_count++; + + size_t alloc_pos = allocate_from_free_nodes(element_count); + // a free node is found + if(alloc_pos != BT_UINT_MAX) + { + return get_element_data(alloc_pos); + } + // allocate directly on pool + alloc_pos = allocate_from_pool(element_count); + + if(alloc_pos == BT_UINT_MAX) return NULL; // not space + return get_element_data(alloc_pos); +} + +bool btGenericMemoryPool::freeMemory(void * pointer) +{ + unsigned char * pointer_pos = (unsigned char *)pointer; + unsigned char * pool_pos = (unsigned char *)m_pool; + // calc offset + if(pointer_pos=get_pool_capacity()) return false;// far away + + // find free position + m_free_nodes[m_free_nodes_count] = offset/m_element_size; + m_free_nodes_count++; + return true; +} + + +/// *******************! btGenericPoolAllocator *******************!/// + + +btGenericPoolAllocator::~btGenericPoolAllocator() +{ + // destroy pools + size_t i; + for (i=0;iend_pool(); + btAlignedFree(m_pools[i]); + } +} + + +// creates a pool +btGenericMemoryPool * btGenericPoolAllocator::push_new_pool() +{ + if(m_pool_count >= BT_DEFAULT_MAX_POOLS) return NULL; + + btGenericMemoryPool * newptr = (btGenericMemoryPool *)btAlignedAlloc(sizeof(btGenericMemoryPool),16); + + m_pools[m_pool_count] = newptr; + + m_pools[m_pool_count]->init_pool(m_pool_element_size,m_pool_element_count); + + m_pool_count++; + return newptr; +} + +void * btGenericPoolAllocator::failback_alloc(size_t size_bytes) +{ + + btGenericMemoryPool * pool = NULL; + + + if(size_bytes<=get_pool_capacity()) + { + pool = push_new_pool(); + } + + if(pool==NULL) // failback + { + return btAlignedAlloc(size_bytes,16); + } + + return pool->allocate(size_bytes); +} + +bool btGenericPoolAllocator::failback_free(void * pointer) +{ + btAlignedFree(pointer); + return true; +} + + +//! Allocates memory in pool +/*! +\param size_bytes size in bytes of the buffer +*/ +void * btGenericPoolAllocator::allocate(size_t size_bytes) +{ + void * ptr = NULL; + + size_t i = 0; + while(iallocate(size_bytes); + ++i; + } + + if(ptr) return ptr; + + return failback_alloc(size_bytes); +} + +bool btGenericPoolAllocator::freeMemory(void * pointer) +{ + bool result = false; + + size_t i = 0; + while(ifreeMemory(pointer); + ++i; + } + + if(result) return true; + + return failback_free(pointer); +} + +/// ************** STANDARD ALLOCATOR ***************************/// + + +#define BT_DEFAULT_POOL_SIZE 32768 +#define BT_DEFAULT_POOL_ELEMENT_SIZE 8 + +// main allocator +class GIM_STANDARD_ALLOCATOR: public btGenericPoolAllocator +{ +public: + GIM_STANDARD_ALLOCATOR():btGenericPoolAllocator(BT_DEFAULT_POOL_ELEMENT_SIZE,BT_DEFAULT_POOL_SIZE) + { + } +}; + +// global allocator +GIM_STANDARD_ALLOCATOR g_main_allocator; + + +void * btPoolAlloc(size_t size) +{ + return g_main_allocator.allocate(size); +} + +void * btPoolRealloc(void *ptr, size_t oldsize, size_t newsize) +{ + void * newptr = btPoolAlloc(newsize); + size_t copysize = oldsize +#include +#include +#include "LinearMath/btAlignedAllocator.h" + +#define BT_UINT_MAX UINT_MAX +#define BT_DEFAULT_MAX_POOLS 16 + + +//! Generic Pool class +class btGenericMemoryPool +{ +public: + unsigned char * m_pool; //[m_element_size*m_max_element_count]; + size_t * m_free_nodes; //[m_max_element_count];//! free nodes + size_t * m_allocated_sizes;//[m_max_element_count];//! Number of elements allocated per node + size_t m_allocated_count; + size_t m_free_nodes_count; +protected: + size_t m_element_size; + size_t m_max_element_count; + + size_t allocate_from_free_nodes(size_t num_elements); + size_t allocate_from_pool(size_t num_elements); + +public: + + void init_pool(size_t element_size, size_t element_count); + + void end_pool(); + + + btGenericMemoryPool(size_t element_size, size_t element_count) + { + init_pool(element_size, element_count); + } + + ~btGenericMemoryPool() + { + end_pool(); + } + + + inline size_t get_pool_capacity() + { + return m_element_size*m_max_element_count; + } + + inline size_t gem_element_size() + { + return m_element_size; + } + + inline size_t get_max_element_count() + { + return m_max_element_count; + } + + inline size_t get_allocated_count() + { + return m_allocated_count; + } + + inline size_t get_free_positions_count() + { + return m_free_nodes_count; + } + + inline void * get_element_data(size_t element_index) + { + return &m_pool[element_index*m_element_size]; + } + + //! Allocates memory in pool + /*! + \param size_bytes size in bytes of the buffer + */ + void * allocate(size_t size_bytes); + + bool freeMemory(void * pointer); +}; + + + + +//! Generic Allocator with pools +/*! +General purpose Allocator which can create Memory Pools dynamiacally as needed. +*/ +class btGenericPoolAllocator +{ +protected: + size_t m_pool_element_size; + size_t m_pool_element_count; +public: + btGenericMemoryPool * m_pools[BT_DEFAULT_MAX_POOLS]; + size_t m_pool_count; + + + inline size_t get_pool_capacity() + { + return m_pool_element_size*m_pool_element_count; + } + + +protected: + // creates a pool + btGenericMemoryPool * push_new_pool(); + + void * failback_alloc(size_t size_bytes); + + bool failback_free(void * pointer); +public: + + btGenericPoolAllocator(size_t pool_element_size, size_t pool_element_count) + { + m_pool_count = 0; + m_pool_element_size = pool_element_size; + m_pool_element_count = pool_element_count; + } + + virtual ~btGenericPoolAllocator(); + + //! Allocates memory in pool + /*! + \param size_bytes size in bytes of the buffer + */ + void * allocate(size_t size_bytes); + + bool freeMemory(void * pointer); +}; + + + +void * btPoolAlloc(size_t size); +void * btPoolRealloc(void *ptr, size_t oldsize, size_t newsize); +void btPoolFree(void *ptr); + + +#endif diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/btGeometryOperations.h b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGeometryOperations.h new file mode 100644 index 000000000..bc5a416dd --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGeometryOperations.h @@ -0,0 +1,212 @@ +#ifndef BT_BASIC_GEOMETRY_OPERATIONS_H_INCLUDED +#define BT_BASIC_GEOMETRY_OPERATIONS_H_INCLUDED + +/*! \file btGeometryOperations.h +*\author Francisco Len Nßjera + +*/ +/* +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2007 Francisco Leon Najera. C.C. 80087371. +email: projectileman@yahoo.com + + +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 "btBoxCollision.h" + + + + + +#define PLANEDIREPSILON 0.0000001f +#define PARALELENORMALS 0.000001f + + +#define BT_CLAMP(number,minval,maxval) (numbermaxval?maxval:number)) + +/// Calc a plane from a triangle edge an a normal. plane is a vec4f +SIMD_FORCE_INLINE void bt_edge_plane(const btVector3 & e1,const btVector3 & e2, const btVector3 & normal,btVector4 & plane) +{ + btVector3 planenormal = (e2-e1).cross(normal); + planenormal.normalize(); + plane.setValue(planenormal[0],planenormal[1],planenormal[2],e2.dot(planenormal)); +} + + + +//***************** SEGMENT and LINE FUNCTIONS **********************************/// + +/*! Finds the closest point(cp) to (v) on a segment (e1,e2) + */ +SIMD_FORCE_INLINE void bt_closest_point_on_segment( + btVector3 & cp, const btVector3 & v, + const btVector3 &e1,const btVector3 &e2) +{ + btVector3 n = e2-e1; + cp = v - e1; + btScalar _scalar = cp.dot(n)/n.dot(n); + if(_scalar <0.0f) + { + cp = e1; + } + else if(_scalar >1.0f) + { + cp = e2; + } + else + { + cp = _scalar*n + e1; + } +} + + +//! line plane collision +/*! +*\return + -0 if the ray never intersects + -1 if the ray collides in front + -2 if the ray collides in back +*/ + +SIMD_FORCE_INLINE int bt_line_plane_collision( + const btVector4 & plane, + const btVector3 & vDir, + const btVector3 & vPoint, + btVector3 & pout, + btScalar &tparam, + btScalar tmin, btScalar tmax) +{ + + btScalar _dotdir = vDir.dot(plane); + + if(btFabs(_dotdir)tmax) + { + returnvalue = 0; + tparam = tmax; + } + pout = tparam*vDir + vPoint; + return returnvalue; +} + + +//! Find closest points on segments +SIMD_FORCE_INLINE void bt_segment_collision( + const btVector3 & vA1, + const btVector3 & vA2, + const btVector3 & vB1, + const btVector3 & vB2, + btVector3 & vPointA, + btVector3 & vPointB) +{ + btVector3 AD = vA2 - vA1; + btVector3 BD = vB2 - vB1; + btVector3 N = AD.cross(BD); + btScalar tp = N.length2(); + + btVector4 _M;//plane + + if(tp_M[1]) + { + invert_b_order = true; + BT_SWAP_NUMBERS(_M[0],_M[1]); + } + _M[2] = vA1.dot(AD); + _M[3] = vA2.dot(AD); + //mid points + N[0] = (_M[0]+_M[1])*0.5f; + N[1] = (_M[2]+_M[3])*0.5f; + + if(N[0]=0.0f) + { + if (_dist>m_penetration_depth) + { + m_penetration_depth = _dist; + point_indices[0] = _k; + m_point_count=1; + } + else if ((_dist+SIMD_EPSILON)>=m_penetration_depth) + { + point_indices[m_point_count] = _k; + m_point_count++; + } + } + } + + for ( _k=0;_k0.0f&&dis1>0.0f&&dis2>0.0f) return false; + + // classify points on this triangle + dis0 = bt_distance_point_plane(other.m_plane,m_vertices[0]) - total_margin; + + dis1 = bt_distance_point_plane(other.m_plane,m_vertices[1]) - total_margin; + + dis2 = bt_distance_point_plane(other.m_plane,m_vertices[2]) - total_margin; + + if (dis0>0.0f&&dis1>0.0f&&dis2>0.0f) return false; + + return true; +} + +int btPrimitiveTriangle::clip_triangle(btPrimitiveTriangle & other, btVector3 * clipped_points ) +{ + // edge 0 + + btVector3 temp_points[MAX_TRI_CLIPPING]; + + + btVector4 edgeplane; + + get_edge_plane(0,edgeplane); + + + int clipped_count = bt_plane_clip_triangle( + edgeplane,other.m_vertices[0],other.m_vertices[1],other.m_vertices[2],temp_points); + + if (clipped_count == 0) return 0; + + btVector3 temp_points1[MAX_TRI_CLIPPING]; + + + // edge 1 + get_edge_plane(1,edgeplane); + + + clipped_count = bt_plane_clip_polygon(edgeplane,temp_points,clipped_count,temp_points1); + + if (clipped_count == 0) return 0; + + // edge 2 + get_edge_plane(2,edgeplane); + + clipped_count = bt_plane_clip_polygon( + edgeplane,temp_points1,clipped_count,clipped_points); + + return clipped_count; +} + +bool btPrimitiveTriangle::find_triangle_collision_clip_method(btPrimitiveTriangle & other, GIM_TRIANGLE_CONTACT & contacts) +{ + btScalar margin = m_margin + other.m_margin; + + btVector3 clipped_points[MAX_TRI_CLIPPING]; + int clipped_count; + //create planes + // plane v vs U points + + GIM_TRIANGLE_CONTACT contacts1; + + contacts1.m_separating_normal = m_plane; + + + clipped_count = clip_triangle(other,clipped_points); + + if (clipped_count == 0 ) + { + return false;//Reject + } + + //find most deep interval face1 + contacts1.merge_points(contacts1.m_separating_normal,margin,clipped_points,clipped_count); + if (contacts1.m_point_count == 0) return false; // too far + //Normal pointing to this triangle + contacts1.m_separating_normal *= -1.f; + + + //Clip tri1 by tri2 edges + GIM_TRIANGLE_CONTACT contacts2; + contacts2.m_separating_normal = other.m_plane; + + clipped_count = other.clip_triangle(*this,clipped_points); + + if (clipped_count == 0 ) + { + return false;//Reject + } + + //find most deep interval face1 + contacts2.merge_points(contacts2.m_separating_normal,margin,clipped_points,clipped_count); + if (contacts2.m_point_count == 0) return false; // too far + + + + + ////check most dir for contacts + if (contacts2.m_penetration_depth0.0f&&dis1>0.0f&&dis2>0.0f) return false; + + // classify points on this triangle + dis0 = bt_distance_point_plane(plane1,m_vertices1[0]) - total_margin; + + dis1 = bt_distance_point_plane(plane1,m_vertices1[1]) - total_margin; + + dis2 = bt_distance_point_plane(plane1,m_vertices1[2]) - total_margin; + + if (dis0>0.0f&&dis1>0.0f&&dis2>0.0f) return false; + + return true; +} + + diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/btTriangleShapeEx.h b/Engine/lib/bullet/src/BulletCollision/Gimpact/btTriangleShapeEx.h new file mode 100644 index 000000000..bbd6b630c --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/btTriangleShapeEx.h @@ -0,0 +1,180 @@ +/*! \file btGImpactShape.h +\author Francisco Len Nßjera +*/ +/* +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2007 Francisco Leon Najera. C.C. 80087371. +email: projectileman@yahoo.com + + +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 TRIANGLE_SHAPE_EX_H +#define TRIANGLE_SHAPE_EX_H + +#include "BulletCollision/CollisionShapes/btCollisionShape.h" +#include "BulletCollision/CollisionShapes/btTriangleShape.h" +#include "btBoxCollision.h" +#include "btClipPolygon.h" +#include "btGeometryOperations.h" + + +#define MAX_TRI_CLIPPING 16 + +//! Structure for collision +struct GIM_TRIANGLE_CONTACT +{ + btScalar m_penetration_depth; + int m_point_count; + btVector4 m_separating_normal; + btVector3 m_points[MAX_TRI_CLIPPING]; + + SIMD_FORCE_INLINE void copy_from(const GIM_TRIANGLE_CONTACT& other) + { + m_penetration_depth = other.m_penetration_depth; + m_separating_normal = other.m_separating_normal; + m_point_count = other.m_point_count; + int i = m_point_count; + while(i--) + { + m_points[i] = other.m_points[i]; + } + } + + GIM_TRIANGLE_CONTACT() + { + } + + GIM_TRIANGLE_CONTACT(const GIM_TRIANGLE_CONTACT& other) + { + copy_from(other); + } + + //! classify points that are closer + void merge_points(const btVector4 & plane, + btScalar margin, const btVector3 * points, int point_count); + +}; + + + +class btPrimitiveTriangle +{ +public: + btVector3 m_vertices[3]; + btVector4 m_plane; + btScalar m_margin; + btScalar m_dummy; + btPrimitiveTriangle():m_margin(0.01f) + { + + } + + + SIMD_FORCE_INLINE void buildTriPlane() + { + btVector3 normal = (m_vertices[1]-m_vertices[0]).cross(m_vertices[2]-m_vertices[0]); + normal.normalize(); + m_plane.setValue(normal[0],normal[1],normal[2],m_vertices[0].dot(normal)); + } + + //! Test if triangles could collide + bool overlap_test_conservative(const btPrimitiveTriangle& other); + + //! Calcs the plane which is paralele to the edge and perpendicular to the triangle plane + /*! + \pre this triangle must have its plane calculated. + */ + SIMD_FORCE_INLINE void get_edge_plane(int edge_index, btVector4 &plane) const + { + const btVector3 & e0 = m_vertices[edge_index]; + const btVector3 & e1 = m_vertices[(edge_index+1)%3]; + bt_edge_plane(e0,e1,m_plane,plane); + } + + void applyTransform(const btTransform& t) + { + m_vertices[0] = t(m_vertices[0]); + m_vertices[1] = t(m_vertices[1]); + m_vertices[2] = t(m_vertices[2]); + } + + //! Clips the triangle against this + /*! + \pre clipped_points must have MAX_TRI_CLIPPING size, and this triangle must have its plane calculated. + \return the number of clipped points + */ + int clip_triangle(btPrimitiveTriangle & other, btVector3 * clipped_points ); + + //! Find collision using the clipping method + /*! + \pre this triangle and other must have their triangles calculated + */ + bool find_triangle_collision_clip_method(btPrimitiveTriangle & other, GIM_TRIANGLE_CONTACT & contacts); +}; + + + +//! Helper class for colliding Bullet Triangle Shapes +/*! +This class implements a better getAabb method than the previous btTriangleShape class +*/ +class btTriangleShapeEx: public btTriangleShape +{ +public: + + btTriangleShapeEx():btTriangleShape(btVector3(0,0,0),btVector3(0,0,0),btVector3(0,0,0)) + { + } + + btTriangleShapeEx(const btVector3& p0,const btVector3& p1,const btVector3& p2): btTriangleShape(p0,p1,p2) + { + } + + btTriangleShapeEx(const btTriangleShapeEx & other): btTriangleShape(other.m_vertices1[0],other.m_vertices1[1],other.m_vertices1[2]) + { + } + + virtual void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax)const + { + btVector3 tv0 = t(m_vertices1[0]); + btVector3 tv1 = t(m_vertices1[1]); + btVector3 tv2 = t(m_vertices1[2]); + + btAABB trianglebox(tv0,tv1,tv2,m_collisionMargin); + aabbMin = trianglebox.m_min; + aabbMax = trianglebox.m_max; + } + + void applyTransform(const btTransform& t) + { + m_vertices1[0] = t(m_vertices1[0]); + m_vertices1[1] = t(m_vertices1[1]); + m_vertices1[2] = t(m_vertices1[2]); + } + + SIMD_FORCE_INLINE void buildTriPlane(btVector4 & plane) const + { + btVector3 normal = (m_vertices1[1]-m_vertices1[0]).cross(m_vertices1[2]-m_vertices1[0]); + normal.normalize(); + plane.setValue(normal[0],normal[1],normal[2],m_vertices1[0].dot(normal)); + } + + bool overlap_test_conservative(const btTriangleShapeEx& other); +}; + + +#endif //TRIANGLE_MESH_SHAPE_H diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_array.h b/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_array.h new file mode 100644 index 000000000..c8161d252 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_array.h @@ -0,0 +1,326 @@ +#ifndef GIM_ARRAY_H_INCLUDED +#define GIM_ARRAY_H_INCLUDED +/*! \file gim_array.h +\author Francisco Len Nßjera +*/ +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon Najera. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + (3) The zlib/libpng license that is included with this library in + the file GIMPACT-LICENSE-ZLIB.TXT. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files + GIMPACT-LICENSE-LGPL.TXT, GIMPACT-LICENSE-ZLIB.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + +#include "gim_memory.h" + + +#define GIM_ARRAY_GROW_INCREMENT 2 +#define GIM_ARRAY_GROW_FACTOR 2 + +//! Very simple array container with fast access and simd memory +template +class gim_array +{ +public: +//! properties +//!@{ + T *m_data; + GUINT m_size; + GUINT m_allocated_size; +//!@} +//! protected operations +//!@{ + + inline void destroyData() + { + m_allocated_size = 0; + if(m_data==NULL) return; + gim_free(m_data); + m_data = NULL; + } + + inline bool resizeData(GUINT newsize) + { + if(newsize==0) + { + destroyData(); + return true; + } + + if(m_size>0) + { + m_data = (T*)gim_realloc(m_data,m_size*sizeof(T),newsize*sizeof(T)); + } + else + { + m_data = (T*)gim_alloc(newsize*sizeof(T)); + } + m_allocated_size = newsize; + return true; + } + + inline bool growingCheck() + { + if(m_allocated_size<=m_size) + { + GUINT requestsize = m_size; + m_size = m_allocated_size; + if(resizeData((requestsize+GIM_ARRAY_GROW_INCREMENT)*GIM_ARRAY_GROW_FACTOR)==false) return false; + } + return true; + } + +//!@} +//! public operations +//!@{ + inline bool reserve(GUINT size) + { + if(m_allocated_size>=size) return false; + return resizeData(size); + } + + inline void clear_range(GUINT start_range) + { + while(m_size>start_range) + { + m_data[--m_size].~T(); + } + } + + inline void clear() + { + if(m_size==0)return; + clear_range(0); + } + + inline void clear_memory() + { + clear(); + destroyData(); + } + + gim_array() + { + m_data = 0; + m_size = 0; + m_allocated_size = 0; + } + + gim_array(GUINT reservesize) + { + m_data = 0; + m_size = 0; + + m_allocated_size = 0; + reserve(reservesize); + } + + ~gim_array() + { + clear_memory(); + } + + inline GUINT size() const + { + return m_size; + } + + inline GUINT max_size() const + { + return m_allocated_size; + } + + inline T & operator[](size_t i) + { + return m_data[i]; + } + inline const T & operator[](size_t i) const + { + return m_data[i]; + } + + inline T * pointer(){ return m_data;} + inline const T * pointer() const + { return m_data;} + + + inline T * get_pointer_at(GUINT i) + { + return m_data + i; + } + + inline const T * get_pointer_at(GUINT i) const + { + return m_data + i; + } + + inline T & at(GUINT i) + { + return m_data[i]; + } + + inline const T & at(GUINT i) const + { + return m_data[i]; + } + + inline T & front() + { + return *m_data; + } + + inline const T & front() const + { + return *m_data; + } + + inline T & back() + { + return m_data[m_size-1]; + } + + inline const T & back() const + { + return m_data[m_size-1]; + } + + + inline void swap(GUINT i, GUINT j) + { + gim_swap_elements(m_data,i,j); + } + + inline void push_back(const T & obj) + { + this->growingCheck(); + m_data[m_size] = obj; + m_size++; + } + + //!Simply increase the m_size, doesn't call the new element constructor + inline void push_back_mem() + { + this->growingCheck(); + m_size++; + } + + inline void push_back_memcpy(const T & obj) + { + this->growingCheck(); + irr_simd_memcpy(&m_data[m_size],&obj,sizeof(T)); + m_size++; + } + + inline void pop_back() + { + m_size--; + m_data[m_size].~T(); + } + + //!Simply decrease the m_size, doesn't call the deleted element destructor + inline void pop_back_mem() + { + m_size--; + } + + //! fast erase + inline void erase(GUINT index) + { + if(indexgrowingCheck(); + for(GUINT i = m_size;i>index;i--) + { + gim_simd_memcpy(m_data+i,m_data+i-1,sizeof(T)); + } + m_size++; + } + + inline void insert(const T & obj,GUINT index) + { + insert_mem(index); + m_data[index] = obj; + } + + inline void resize(GUINT size, bool call_constructor = true) + { + + if(size>m_size) + { + reserve(size); + if(call_constructor) + { + T obj; + while(m_size +SIMD_FORCE_INLINE bool POINT_IN_HULL( + const CLASS_POINT& point,const CLASS_PLANE * planes,GUINT plane_count) +{ + GREAL _dis; + for (GUINT _i = 0;_i< plane_count;++_i) + { + _dis = DISTANCE_PLANE_POINT(planes[_i],point); + if(_dis>0.0f) return false; + } + return true; +} + +template +SIMD_FORCE_INLINE void PLANE_CLIP_SEGMENT( + const CLASS_POINT& s1, + const CLASS_POINT &s2,const CLASS_PLANE &plane,CLASS_POINT &clipped) +{ + GREAL _dis1,_dis2; + _dis1 = DISTANCE_PLANE_POINT(plane,s1); + VEC_DIFF(clipped,s2,s1); + _dis2 = VEC_DOT(clipped,plane); + VEC_SCALE(clipped,-_dis1/_dis2,clipped); + VEC_SUM(clipped,clipped,s1); +} + +enum ePLANE_INTERSECTION_TYPE +{ + G_BACK_PLANE = 0, + G_COLLIDE_PLANE, + G_FRONT_PLANE +}; + +enum eLINE_PLANE_INTERSECTION_TYPE +{ + G_FRONT_PLANE_S1 = 0, + G_FRONT_PLANE_S2, + G_BACK_PLANE_S1, + G_BACK_PLANE_S2, + G_COLLIDE_PLANE_S1, + G_COLLIDE_PLANE_S2 +}; + +//! Confirms if the plane intersect the edge or nor +/*! +intersection type must have the following values +
    +
  • 0 : Segment in front of plane, s1 closest +
  • 1 : Segment in front of plane, s2 closest +
  • 2 : Segment in back of plane, s1 closest +
  • 3 : Segment in back of plane, s2 closest +
  • 4 : Segment collides plane, s1 in back +
  • 5 : Segment collides plane, s2 in back +
+*/ + +template +SIMD_FORCE_INLINE eLINE_PLANE_INTERSECTION_TYPE PLANE_CLIP_SEGMENT2( + const CLASS_POINT& s1, + const CLASS_POINT &s2, + const CLASS_PLANE &plane,CLASS_POINT &clipped) +{ + GREAL _dis1 = DISTANCE_PLANE_POINT(plane,s1); + GREAL _dis2 = DISTANCE_PLANE_POINT(plane,s2); + if(_dis1 >-G_EPSILON && _dis2 >-G_EPSILON) + { + if(_dis1<_dis2) return G_FRONT_PLANE_S1; + return G_FRONT_PLANE_S2; + } + else if(_dis1 _dis2) return G_BACK_PLANE_S1; + return G_BACK_PLANE_S2; + } + + VEC_DIFF(clipped,s2,s1); + _dis2 = VEC_DOT(clipped,plane); + VEC_SCALE(clipped,-_dis1/_dis2,clipped); + VEC_SUM(clipped,clipped,s1); + if(_dis1<_dis2) return G_COLLIDE_PLANE_S1; + return G_COLLIDE_PLANE_S2; +} + +//! Confirms if the plane intersect the edge or not +/*! +clipped1 and clipped2 are the vertices behind the plane. +clipped1 is the closest + +intersection_type must have the following values +
    +
  • 0 : Segment in front of plane, s1 closest +
  • 1 : Segment in front of plane, s2 closest +
  • 2 : Segment in back of plane, s1 closest +
  • 3 : Segment in back of plane, s2 closest +
  • 4 : Segment collides plane, s1 in back +
  • 5 : Segment collides plane, s2 in back +
+*/ +template +SIMD_FORCE_INLINE eLINE_PLANE_INTERSECTION_TYPE PLANE_CLIP_SEGMENT_CLOSEST( + const CLASS_POINT& s1, + const CLASS_POINT &s2, + const CLASS_PLANE &plane, + CLASS_POINT &clipped1,CLASS_POINT &clipped2) +{ + eLINE_PLANE_INTERSECTION_TYPE intersection_type = PLANE_CLIP_SEGMENT2(s1,s2,plane,clipped1); + switch(intersection_type) + { + case G_FRONT_PLANE_S1: + VEC_COPY(clipped1,s1); + VEC_COPY(clipped2,s2); + break; + case G_FRONT_PLANE_S2: + VEC_COPY(clipped1,s2); + VEC_COPY(clipped2,s1); + break; + case G_BACK_PLANE_S1: + VEC_COPY(clipped1,s1); + VEC_COPY(clipped2,s2); + break; + case G_BACK_PLANE_S2: + VEC_COPY(clipped1,s2); + VEC_COPY(clipped2,s1); + break; + case G_COLLIDE_PLANE_S1: + VEC_COPY(clipped2,s1); + break; + case G_COLLIDE_PLANE_S2: + VEC_COPY(clipped2,s2); + break; + } + return intersection_type; +} + + +//! Finds the 2 smallest cartesian coordinates of a plane normal +#define PLANE_MINOR_AXES(plane, i0, i1) VEC_MINOR_AXES(plane, i0, i1) + +//! Ray plane collision in one way +/*! +Intersects plane in one way only. The ray must face the plane (normals must be in opossite directions).
+It uses the PLANEDIREPSILON constant. +*/ +template +SIMD_FORCE_INLINE bool RAY_PLANE_COLLISION( + const CLASS_PLANE & plane, + const CLASS_POINT & vDir, + const CLASS_POINT & vPoint, + CLASS_POINT & pout,T &tparam) +{ + GREAL _dis,_dotdir; + _dotdir = VEC_DOT(plane,vDir); + if(_dotdir +SIMD_FORCE_INLINE GUINT LINE_PLANE_COLLISION( + const CLASS_PLANE & plane, + const CLASS_POINT & vDir, + const CLASS_POINT & vPoint, + CLASS_POINT & pout, + T &tparam, + T tmin, T tmax) +{ + GREAL _dis,_dotdir; + _dotdir = VEC_DOT(plane,vDir); + if(btFabs(_dotdir)tmax) + { + returnvalue = 0; + tparam = tmax; + } + + VEC_SCALE(pout,tparam,vDir); + VEC_SUM(pout,vPoint,pout); + return returnvalue; +} + +/*! \brief Returns the Ray on which 2 planes intersect if they do. + Written by Rodrigo Hernandez on ODE convex collision + + \param p1 Plane 1 + \param p2 Plane 2 + \param p Contains the origin of the ray upon returning if planes intersect + \param d Contains the direction of the ray upon returning if planes intersect + \return true if the planes intersect, 0 if paralell. + +*/ +template +SIMD_FORCE_INLINE bool INTERSECT_PLANES( + const CLASS_PLANE &p1, + const CLASS_PLANE &p2, + CLASS_POINT &p, + CLASS_POINT &d) +{ + VEC_CROSS(d,p1,p2); + GREAL denom = VEC_DOT(d, d); + if(GIM_IS_ZERO(denom)) return false; + vec3f _n; + _n[0]=p1[3]*p2[0] - p2[3]*p1[0]; + _n[1]=p1[3]*p2[1] - p2[3]*p1[1]; + _n[2]=p1[3]*p2[2] - p2[3]*p1[2]; + VEC_CROSS(p,_n,d); + p[0]/=denom; + p[1]/=denom; + p[2]/=denom; + return true; +} + +//***************** SEGMENT and LINE FUNCTIONS **********************************/// + +/*! Finds the closest point(cp) to (v) on a segment (e1,e2) + */ +template +SIMD_FORCE_INLINE void CLOSEST_POINT_ON_SEGMENT( + CLASS_POINT & cp, const CLASS_POINT & v, + const CLASS_POINT &e1,const CLASS_POINT &e2) +{ + vec3f _n; + VEC_DIFF(_n,e2,e1); + VEC_DIFF(cp,v,e1); + GREAL _scalar = VEC_DOT(cp, _n); + _scalar/= VEC_DOT(_n, _n); + if(_scalar <0.0f) + { + VEC_COPY(cp,e1); + } + else if(_scalar >1.0f) + { + VEC_COPY(cp,e2); + } + else + { + VEC_SCALE(cp,_scalar,_n); + VEC_SUM(cp,cp,e1); + } +} + + +/*! \brief Finds the line params where these lines intersect. + +\param dir1 Direction of line 1 +\param point1 Point of line 1 +\param dir2 Direction of line 2 +\param point2 Point of line 2 +\param t1 Result Parameter for line 1 +\param t2 Result Parameter for line 2 +\param dointersect 0 if the lines won't intersect, else 1 + +*/ +template +SIMD_FORCE_INLINE bool LINE_INTERSECTION_PARAMS( + const CLASS_POINT & dir1, + CLASS_POINT & point1, + const CLASS_POINT & dir2, + CLASS_POINT & point2, + T& t1,T& t2) +{ + GREAL det; + GREAL e1e1 = VEC_DOT(dir1,dir1); + GREAL e1e2 = VEC_DOT(dir1,dir2); + GREAL e2e2 = VEC_DOT(dir2,dir2); + vec3f p1p2; + VEC_DIFF(p1p2,point1,point2); + GREAL p1p2e1 = VEC_DOT(p1p2,dir1); + GREAL p1p2e2 = VEC_DOT(p1p2,dir2); + det = e1e2*e1e2 - e1e1*e2e2; + if(GIM_IS_ZERO(det)) return false; + t1 = (e1e2*p1p2e2 - e2e2*p1p2e1)/det; + t2 = (e1e1*p1p2e2 - e1e2*p1p2e1)/det; + return true; +} + +//! Find closest points on segments +template +SIMD_FORCE_INLINE void SEGMENT_COLLISION( + const CLASS_POINT & vA1, + const CLASS_POINT & vA2, + const CLASS_POINT & vB1, + const CLASS_POINT & vB2, + CLASS_POINT & vPointA, + CLASS_POINT & vPointB) +{ + CLASS_POINT _AD,_BD,_N; + vec4f _M;//plane + VEC_DIFF(_AD,vA2,vA1); + VEC_DIFF(_BD,vB2,vB1); + VEC_CROSS(_N,_AD,_BD); + GREAL _tp = VEC_DOT(_N,_N); + if(_tp_M[1]) + { + invert_b_order = true; + GIM_SWAP_NUMBERS(_M[0],_M[1]); + } + _M[2] = VEC_DOT(vA1,_AD); + _M[3] = VEC_DOT(vA2,_AD); + //mid points + _N[0] = (_M[0]+_M[1])*0.5f; + _N[1] = (_M[2]+_M[3])*0.5f; + + if(_N[0]<_N[1]) + { + if(_M[1]<_M[2]) + { + vPointB = invert_b_order?vB1:vB2; + vPointA = vA1; + } + else if(_M[1]<_M[3]) + { + vPointB = invert_b_order?vB1:vB2; + CLOSEST_POINT_ON_SEGMENT(vPointA,vPointB,vA1,vA2); + } + else + { + vPointA = vA2; + CLOSEST_POINT_ON_SEGMENT(vPointB,vPointA,vB1,vB2); + } + } + else + { + if(_M[3]<_M[0]) + { + vPointB = invert_b_order?vB2:vB1; + vPointA = vA2; + } + else if(_M[3]<_M[1]) + { + vPointA = vA2; + CLOSEST_POINT_ON_SEGMENT(vPointB,vPointA,vB1,vB2); + } + else + { + vPointB = invert_b_order?vB1:vB2; + CLOSEST_POINT_ON_SEGMENT(vPointA,vPointB,vA1,vA2); + } + } + return; + } + + + VEC_CROSS(_M,_N,_BD); + _M[3] = VEC_DOT(_M,vB1); + + LINE_PLANE_COLLISION(_M,_AD,vA1,vPointA,_tp,btScalar(0), btScalar(1)); + /*Closest point on segment*/ + VEC_DIFF(vPointB,vPointA,vB1); + _tp = VEC_DOT(vPointB, _BD); + _tp/= VEC_DOT(_BD, _BD); + _tp = GIM_CLAMP(_tp,0.0f,1.0f); + VEC_SCALE(vPointB,_tp,_BD); + VEC_SUM(vPointB,vPointB,vB1); +} + + + + +//! Line box intersection in one dimension +/*! + +*\param pos Position of the ray +*\param dir Projection of the Direction of the ray +*\param bmin Minimum bound of the box +*\param bmax Maximum bound of the box +*\param tfirst the minimum projection. Assign to 0 at first. +*\param tlast the maximum projection. Assign to INFINITY at first. +*\return true if there is an intersection. +*/ +template +SIMD_FORCE_INLINE bool BOX_AXIS_INTERSECT(T pos, T dir,T bmin, T bmax, T & tfirst, T & tlast) +{ + if(GIM_IS_ZERO(dir)) + { + return !(pos < bmin || pos > bmax); + } + GREAL a0 = (bmin - pos) / dir; + GREAL a1 = (bmax - pos) / dir; + if(a0 > a1) GIM_SWAP_NUMBERS(a0, a1); + tfirst = GIM_MAX(a0, tfirst); + tlast = GIM_MIN(a1, tlast); + if (tlast < tfirst) return false; + return true; +} + + +//! Sorts 3 componets +template +SIMD_FORCE_INLINE void SORT_3_INDICES( + const T * values, + GUINT * order_indices) +{ + //get minimum + order_indices[0] = values[0] < values[1] ? (values[0] < values[2] ? 0 : 2) : (values[1] < values[2] ? 1 : 2); + + //get second and third + GUINT i0 = (order_indices[0] + 1)%3; + GUINT i1 = (i0 + 1)%3; + + if(values[i0] < values[i1]) + { + order_indices[1] = i0; + order_indices[2] = i1; + } + else + { + order_indices[1] = i1; + order_indices[2] = i0; + } +} + + + + + +#endif // GIM_VECTOR_H_INCLUDED diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_bitset.h b/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_bitset.h new file mode 100644 index 000000000..322004a8d --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_bitset.h @@ -0,0 +1,123 @@ +#ifndef GIM_BITSET_H_INCLUDED +#define GIM_BITSET_H_INCLUDED +/*! \file gim_bitset.h +\author Francisco Len Nßjera +*/ +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon Najera. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + (3) The zlib/libpng license that is included with this library in + the file GIMPACT-LICENSE-ZLIB.TXT. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files + GIMPACT-LICENSE-LGPL.TXT, GIMPACT-LICENSE-ZLIB.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + +#include "gim_array.h" + + +#define GUINT_BIT_COUNT 32 +#define GUINT_EXPONENT 5 + +class gim_bitset +{ +public: + gim_array m_container; + + gim_bitset() + { + + } + + gim_bitset(GUINT bits_count) + { + resize(bits_count); + } + + ~gim_bitset() + { + } + + inline bool resize(GUINT newsize) + { + GUINT oldsize = m_container.size(); + m_container.resize(newsize/GUINT_BIT_COUNT + 1,false); + while(oldsize=size()) + { + resize(bit_index); + } + m_container[bit_index >> GUINT_EXPONENT] |= (1 << (bit_index & (GUINT_BIT_COUNT-1))); + } + + ///Return 0 or 1 + inline char get(GUINT bit_index) + { + if(bit_index>=size()) + { + return 0; + } + char value = m_container[bit_index >> GUINT_EXPONENT] & + (1 << (bit_index & (GUINT_BIT_COUNT-1))); + return value; + } + + inline void clear(GUINT bit_index) + { + m_container[bit_index >> GUINT_EXPONENT] &= ~(1 << (bit_index & (GUINT_BIT_COUNT-1))); + } +}; + + + + + +#endif // GIM_CONTAINERS_H_INCLUDED diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_box_collision.h b/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_box_collision.h new file mode 100644 index 000000000..0add5e4b9 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_box_collision.h @@ -0,0 +1,590 @@ +#ifndef GIM_BOX_COLLISION_H_INCLUDED +#define GIM_BOX_COLLISION_H_INCLUDED + +/*! \file gim_box_collision.h +\author Francisco Len Nßjera +*/ +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon Najera. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + (3) The zlib/libpng license that is included with this library in + the file GIMPACT-LICENSE-ZLIB.TXT. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files + GIMPACT-LICENSE-LGPL.TXT, GIMPACT-LICENSE-ZLIB.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ +#include "gim_basic_geometry_operations.h" +#include "LinearMath/btTransform.h" + + + +//SIMD_FORCE_INLINE bool test_cross_edge_box( +// const btVector3 & edge, +// const btVector3 & absolute_edge, +// const btVector3 & pointa, +// const btVector3 & pointb, const btVector3 & extend, +// int dir_index0, +// int dir_index1 +// int component_index0, +// int component_index1) +//{ +// // dir coords are -z and y +// +// const btScalar dir0 = -edge[dir_index0]; +// const btScalar dir1 = edge[dir_index1]; +// btScalar pmin = pointa[component_index0]*dir0 + pointa[component_index1]*dir1; +// btScalar pmax = pointb[component_index0]*dir0 + pointb[component_index1]*dir1; +// //find minmax +// if(pmin>pmax) +// { +// GIM_SWAP_NUMBERS(pmin,pmax); +// } +// //find extends +// const btScalar rad = extend[component_index0] * absolute_edge[dir_index0] + +// extend[component_index1] * absolute_edge[dir_index1]; +// +// if(pmin>rad || -rad>pmax) return false; +// return true; +//} +// +//SIMD_FORCE_INLINE bool test_cross_edge_box_X_axis( +// const btVector3 & edge, +// const btVector3 & absolute_edge, +// const btVector3 & pointa, +// const btVector3 & pointb, btVector3 & extend) +//{ +// +// return test_cross_edge_box(edge,absolute_edge,pointa,pointb,extend,2,1,1,2); +//} +// +// +//SIMD_FORCE_INLINE bool test_cross_edge_box_Y_axis( +// const btVector3 & edge, +// const btVector3 & absolute_edge, +// const btVector3 & pointa, +// const btVector3 & pointb, btVector3 & extend) +//{ +// +// return test_cross_edge_box(edge,absolute_edge,pointa,pointb,extend,0,2,2,0); +//} +// +//SIMD_FORCE_INLINE bool test_cross_edge_box_Z_axis( +// const btVector3 & edge, +// const btVector3 & absolute_edge, +// const btVector3 & pointa, +// const btVector3 & pointb, btVector3 & extend) +//{ +// +// return test_cross_edge_box(edge,absolute_edge,pointa,pointb,extend,1,0,0,1); +//} + +#define TEST_CROSS_EDGE_BOX_MCR(edge,absolute_edge,pointa,pointb,_extend,i_dir_0,i_dir_1,i_comp_0,i_comp_1)\ +{\ + const btScalar dir0 = -edge[i_dir_0];\ + const btScalar dir1 = edge[i_dir_1];\ + btScalar pmin = pointa[i_comp_0]*dir0 + pointa[i_comp_1]*dir1;\ + btScalar pmax = pointb[i_comp_0]*dir0 + pointb[i_comp_1]*dir1;\ + if(pmin>pmax)\ + {\ + GIM_SWAP_NUMBERS(pmin,pmax); \ + }\ + const btScalar abs_dir0 = absolute_edge[i_dir_0];\ + const btScalar abs_dir1 = absolute_edge[i_dir_1];\ + const btScalar rad = _extend[i_comp_0] * abs_dir0 + _extend[i_comp_1] * abs_dir1;\ + if(pmin>rad || -rad>pmax) return false;\ +}\ + + +#define TEST_CROSS_EDGE_BOX_X_AXIS_MCR(edge,absolute_edge,pointa,pointb,_extend)\ +{\ + TEST_CROSS_EDGE_BOX_MCR(edge,absolute_edge,pointa,pointb,_extend,2,1,1,2);\ +}\ + +#define TEST_CROSS_EDGE_BOX_Y_AXIS_MCR(edge,absolute_edge,pointa,pointb,_extend)\ +{\ + TEST_CROSS_EDGE_BOX_MCR(edge,absolute_edge,pointa,pointb,_extend,0,2,2,0);\ +}\ + +#define TEST_CROSS_EDGE_BOX_Z_AXIS_MCR(edge,absolute_edge,pointa,pointb,_extend)\ +{\ + TEST_CROSS_EDGE_BOX_MCR(edge,absolute_edge,pointa,pointb,_extend,1,0,0,1);\ +}\ + + + +//! Class for transforming a model1 to the space of model0 +class GIM_BOX_BOX_TRANSFORM_CACHE +{ +public: + btVector3 m_T1to0;//!< Transforms translation of model1 to model 0 + btMatrix3x3 m_R1to0;//!< Transforms Rotation of model1 to model 0, equal to R0' * R1 + btMatrix3x3 m_AR;//!< Absolute value of m_R1to0 + + SIMD_FORCE_INLINE void calc_absolute_matrix() + { + static const btVector3 vepsi(1e-6f,1e-6f,1e-6f); + m_AR[0] = vepsi + m_R1to0[0].absolute(); + m_AR[1] = vepsi + m_R1to0[1].absolute(); + m_AR[2] = vepsi + m_R1to0[2].absolute(); + } + + GIM_BOX_BOX_TRANSFORM_CACHE() + { + } + + + GIM_BOX_BOX_TRANSFORM_CACHE(mat4f trans1_to_0) + { + COPY_MATRIX_3X3(m_R1to0,trans1_to_0) + MAT_GET_TRANSLATION(trans1_to_0,m_T1to0) + calc_absolute_matrix(); + } + + //! Calc the transformation relative 1 to 0. Inverts matrics by transposing + SIMD_FORCE_INLINE void calc_from_homogenic(const btTransform & trans0,const btTransform & trans1) + { + + m_R1to0 = trans0.getBasis().transpose(); + m_T1to0 = m_R1to0 * (-trans0.getOrigin()); + + m_T1to0 += m_R1to0*trans1.getOrigin(); + m_R1to0 *= trans1.getBasis(); + + calc_absolute_matrix(); + } + + //! Calcs the full invertion of the matrices. Useful for scaling matrices + SIMD_FORCE_INLINE void calc_from_full_invert(const btTransform & trans0,const btTransform & trans1) + { + m_R1to0 = trans0.getBasis().inverse(); + m_T1to0 = m_R1to0 * (-trans0.getOrigin()); + + m_T1to0 += m_R1to0*trans1.getOrigin(); + m_R1to0 *= trans1.getBasis(); + + calc_absolute_matrix(); + } + + SIMD_FORCE_INLINE btVector3 transform(const btVector3 & point) + { + return btVector3(m_R1to0[0].dot(point) + m_T1to0.x(), + m_R1to0[1].dot(point) + m_T1to0.y(), + m_R1to0[2].dot(point) + m_T1to0.z()); + } +}; + + +#define BOX_PLANE_EPSILON 0.000001f + +//! Axis aligned box +class GIM_AABB +{ +public: + btVector3 m_min; + btVector3 m_max; + + GIM_AABB() + {} + + + GIM_AABB(const btVector3 & V1, + const btVector3 & V2, + const btVector3 & V3) + { + m_min[0] = GIM_MIN3(V1[0],V2[0],V3[0]); + m_min[1] = GIM_MIN3(V1[1],V2[1],V3[1]); + m_min[2] = GIM_MIN3(V1[2],V2[2],V3[2]); + + m_max[0] = GIM_MAX3(V1[0],V2[0],V3[0]); + m_max[1] = GIM_MAX3(V1[1],V2[1],V3[1]); + m_max[2] = GIM_MAX3(V1[2],V2[2],V3[2]); + } + + GIM_AABB(const btVector3 & V1, + const btVector3 & V2, + const btVector3 & V3, + GREAL margin) + { + m_min[0] = GIM_MIN3(V1[0],V2[0],V3[0]); + m_min[1] = GIM_MIN3(V1[1],V2[1],V3[1]); + m_min[2] = GIM_MIN3(V1[2],V2[2],V3[2]); + + m_max[0] = GIM_MAX3(V1[0],V2[0],V3[0]); + m_max[1] = GIM_MAX3(V1[1],V2[1],V3[1]); + m_max[2] = GIM_MAX3(V1[2],V2[2],V3[2]); + + m_min[0] -= margin; + m_min[1] -= margin; + m_min[2] -= margin; + m_max[0] += margin; + m_max[1] += margin; + m_max[2] += margin; + } + + GIM_AABB(const GIM_AABB &other): + m_min(other.m_min),m_max(other.m_max) + { + } + + GIM_AABB(const GIM_AABB &other,btScalar margin ): + m_min(other.m_min),m_max(other.m_max) + { + m_min[0] -= margin; + m_min[1] -= margin; + m_min[2] -= margin; + m_max[0] += margin; + m_max[1] += margin; + m_max[2] += margin; + } + + SIMD_FORCE_INLINE void invalidate() + { + m_min[0] = G_REAL_INFINITY; + m_min[1] = G_REAL_INFINITY; + m_min[2] = G_REAL_INFINITY; + m_max[0] = -G_REAL_INFINITY; + m_max[1] = -G_REAL_INFINITY; + m_max[2] = -G_REAL_INFINITY; + } + + SIMD_FORCE_INLINE void increment_margin(btScalar margin) + { + m_min[0] -= margin; + m_min[1] -= margin; + m_min[2] -= margin; + m_max[0] += margin; + m_max[1] += margin; + m_max[2] += margin; + } + + SIMD_FORCE_INLINE void copy_with_margin(const GIM_AABB &other, btScalar margin) + { + m_min[0] = other.m_min[0] - margin; + m_min[1] = other.m_min[1] - margin; + m_min[2] = other.m_min[2] - margin; + + m_max[0] = other.m_max[0] + margin; + m_max[1] = other.m_max[1] + margin; + m_max[2] = other.m_max[2] + margin; + } + + template + SIMD_FORCE_INLINE void calc_from_triangle( + const CLASS_POINT & V1, + const CLASS_POINT & V2, + const CLASS_POINT & V3) + { + m_min[0] = GIM_MIN3(V1[0],V2[0],V3[0]); + m_min[1] = GIM_MIN3(V1[1],V2[1],V3[1]); + m_min[2] = GIM_MIN3(V1[2],V2[2],V3[2]); + + m_max[0] = GIM_MAX3(V1[0],V2[0],V3[0]); + m_max[1] = GIM_MAX3(V1[1],V2[1],V3[1]); + m_max[2] = GIM_MAX3(V1[2],V2[2],V3[2]); + } + + template + SIMD_FORCE_INLINE void calc_from_triangle_margin( + const CLASS_POINT & V1, + const CLASS_POINT & V2, + const CLASS_POINT & V3, btScalar margin) + { + m_min[0] = GIM_MIN3(V1[0],V2[0],V3[0]); + m_min[1] = GIM_MIN3(V1[1],V2[1],V3[1]); + m_min[2] = GIM_MIN3(V1[2],V2[2],V3[2]); + + m_max[0] = GIM_MAX3(V1[0],V2[0],V3[0]); + m_max[1] = GIM_MAX3(V1[1],V2[1],V3[1]); + m_max[2] = GIM_MAX3(V1[2],V2[2],V3[2]); + + m_min[0] -= margin; + m_min[1] -= margin; + m_min[2] -= margin; + m_max[0] += margin; + m_max[1] += margin; + m_max[2] += margin; + } + + //! Apply a transform to an AABB + SIMD_FORCE_INLINE void appy_transform(const btTransform & trans) + { + btVector3 center = (m_max+m_min)*0.5f; + btVector3 extends = m_max - center; + // Compute new center + center = trans(center); + + btVector3 textends(extends.dot(trans.getBasis().getRow(0).absolute()), + extends.dot(trans.getBasis().getRow(1).absolute()), + extends.dot(trans.getBasis().getRow(2).absolute())); + + m_min = center - textends; + m_max = center + textends; + } + + //! Merges a Box + SIMD_FORCE_INLINE void merge(const GIM_AABB & box) + { + m_min[0] = GIM_MIN(m_min[0],box.m_min[0]); + m_min[1] = GIM_MIN(m_min[1],box.m_min[1]); + m_min[2] = GIM_MIN(m_min[2],box.m_min[2]); + + m_max[0] = GIM_MAX(m_max[0],box.m_max[0]); + m_max[1] = GIM_MAX(m_max[1],box.m_max[1]); + m_max[2] = GIM_MAX(m_max[2],box.m_max[2]); + } + + //! Merges a point + template + SIMD_FORCE_INLINE void merge_point(const CLASS_POINT & point) + { + m_min[0] = GIM_MIN(m_min[0],point[0]); + m_min[1] = GIM_MIN(m_min[1],point[1]); + m_min[2] = GIM_MIN(m_min[2],point[2]); + + m_max[0] = GIM_MAX(m_max[0],point[0]); + m_max[1] = GIM_MAX(m_max[1],point[1]); + m_max[2] = GIM_MAX(m_max[2],point[2]); + } + + //! Gets the extend and center + SIMD_FORCE_INLINE void get_center_extend(btVector3 & center,btVector3 & extend) const + { + center = (m_max+m_min)*0.5f; + extend = m_max - center; + } + + //! Finds the intersecting box between this box and the other. + SIMD_FORCE_INLINE void find_intersection(const GIM_AABB & other, GIM_AABB & intersection) const + { + intersection.m_min[0] = GIM_MAX(other.m_min[0],m_min[0]); + intersection.m_min[1] = GIM_MAX(other.m_min[1],m_min[1]); + intersection.m_min[2] = GIM_MAX(other.m_min[2],m_min[2]); + + intersection.m_max[0] = GIM_MIN(other.m_max[0],m_max[0]); + intersection.m_max[1] = GIM_MIN(other.m_max[1],m_max[1]); + intersection.m_max[2] = GIM_MIN(other.m_max[2],m_max[2]); + } + + + SIMD_FORCE_INLINE bool has_collision(const GIM_AABB & other) const + { + if(m_min[0] > other.m_max[0] || + m_max[0] < other.m_min[0] || + m_min[1] > other.m_max[1] || + m_max[1] < other.m_min[1] || + m_min[2] > other.m_max[2] || + m_max[2] < other.m_min[2]) + { + return false; + } + return true; + } + + /*! \brief Finds the Ray intersection parameter. + \param aabb Aligned box + \param vorigin A vec3f with the origin of the ray + \param vdir A vec3f with the direction of the ray + */ + SIMD_FORCE_INLINE bool collide_ray(const btVector3 & vorigin,const btVector3 & vdir) + { + btVector3 extents,center; + this->get_center_extend(center,extents);; + + btScalar Dx = vorigin[0] - center[0]; + if(GIM_GREATER(Dx, extents[0]) && Dx*vdir[0]>=0.0f) return false; + btScalar Dy = vorigin[1] - center[1]; + if(GIM_GREATER(Dy, extents[1]) && Dy*vdir[1]>=0.0f) return false; + btScalar Dz = vorigin[2] - center[2]; + if(GIM_GREATER(Dz, extents[2]) && Dz*vdir[2]>=0.0f) return false; + + + btScalar f = vdir[1] * Dz - vdir[2] * Dy; + if(btFabs(f) > extents[1]*btFabs(vdir[2]) + extents[2]*btFabs(vdir[1])) return false; + f = vdir[2] * Dx - vdir[0] * Dz; + if(btFabs(f) > extents[0]*btFabs(vdir[2]) + extents[2]*btFabs(vdir[0]))return false; + f = vdir[0] * Dy - vdir[1] * Dx; + if(btFabs(f) > extents[0]*btFabs(vdir[1]) + extents[1]*btFabs(vdir[0]))return false; + return true; + } + + + SIMD_FORCE_INLINE void projection_interval(const btVector3 & direction, btScalar &vmin, btScalar &vmax) const + { + btVector3 center = (m_max+m_min)*0.5f; + btVector3 extend = m_max-center; + + btScalar _fOrigin = direction.dot(center); + btScalar _fMaximumExtent = extend.dot(direction.absolute()); + vmin = _fOrigin - _fMaximumExtent; + vmax = _fOrigin + _fMaximumExtent; + } + + SIMD_FORCE_INLINE ePLANE_INTERSECTION_TYPE plane_classify(const btVector4 &plane) const + { + btScalar _fmin,_fmax; + this->projection_interval(plane,_fmin,_fmax); + + if(plane[3] > _fmax + BOX_PLANE_EPSILON) + { + return G_BACK_PLANE; // 0 + } + + if(plane[3]+BOX_PLANE_EPSILON >=_fmin) + { + return G_COLLIDE_PLANE; //1 + } + return G_FRONT_PLANE;//2 + } + + SIMD_FORCE_INLINE bool overlapping_trans_conservative(const GIM_AABB & box, btTransform & trans1_to_0) + { + GIM_AABB tbox = box; + tbox.appy_transform(trans1_to_0); + return has_collision(tbox); + } + + //! transcache is the transformation cache from box to this AABB + SIMD_FORCE_INLINE bool overlapping_trans_cache( + const GIM_AABB & box,const GIM_BOX_BOX_TRANSFORM_CACHE & transcache, bool fulltest) + { + + //Taken from OPCODE + btVector3 ea,eb;//extends + btVector3 ca,cb;//extends + get_center_extend(ca,ea); + box.get_center_extend(cb,eb); + + + btVector3 T; + btScalar t,t2; + int i; + + // Class I : A's basis vectors + for(i=0;i<3;i++) + { + T[i] = transcache.m_R1to0[i].dot(cb) + transcache.m_T1to0[i] - ca[i]; + t = transcache.m_AR[i].dot(eb) + ea[i]; + if(GIM_GREATER(T[i], t)) return false; + } + // Class II : B's basis vectors + for(i=0;i<3;i++) + { + t = MAT_DOT_COL(transcache.m_R1to0,T,i); + t2 = MAT_DOT_COL(transcache.m_AR,ea,i) + eb[i]; + if(GIM_GREATER(t,t2)) return false; + } + // Class III : 9 cross products + if(fulltest) + { + int j,m,n,o,p,q,r; + for(i=0;i<3;i++) + { + m = (i+1)%3; + n = (i+2)%3; + o = i==0?1:0; + p = i==2?1:2; + for(j=0;j<3;j++) + { + q = j==2?1:2; + r = j==0?1:0; + t = T[n]*transcache.m_R1to0[m][j] - T[m]*transcache.m_R1to0[n][j]; + t2 = ea[o]*transcache.m_AR[p][j] + ea[p]*transcache.m_AR[o][j] + + eb[r]*transcache.m_AR[i][q] + eb[q]*transcache.m_AR[i][r]; + if(GIM_GREATER(t,t2)) return false; + } + } + } + return true; + } + + //! Simple test for planes. + SIMD_FORCE_INLINE bool collide_plane( + const btVector4 & plane) + { + ePLANE_INTERSECTION_TYPE classify = plane_classify(plane); + return (classify == G_COLLIDE_PLANE); + } + + //! test for a triangle, with edges + SIMD_FORCE_INLINE bool collide_triangle_exact( + const btVector3 & p1, + const btVector3 & p2, + const btVector3 & p3, + const btVector4 & triangle_plane) + { + if(!collide_plane(triangle_plane)) return false; + + btVector3 center,extends; + this->get_center_extend(center,extends); + + const btVector3 v1(p1 - center); + const btVector3 v2(p2 - center); + const btVector3 v3(p3 - center); + + //First axis + btVector3 diff(v2 - v1); + btVector3 abs_diff = diff.absolute(); + //Test With X axis + TEST_CROSS_EDGE_BOX_X_AXIS_MCR(diff,abs_diff,v1,v3,extends); + //Test With Y axis + TEST_CROSS_EDGE_BOX_Y_AXIS_MCR(diff,abs_diff,v1,v3,extends); + //Test With Z axis + TEST_CROSS_EDGE_BOX_Z_AXIS_MCR(diff,abs_diff,v1,v3,extends); + + + diff = v3 - v2; + abs_diff = diff.absolute(); + //Test With X axis + TEST_CROSS_EDGE_BOX_X_AXIS_MCR(diff,abs_diff,v2,v1,extends); + //Test With Y axis + TEST_CROSS_EDGE_BOX_Y_AXIS_MCR(diff,abs_diff,v2,v1,extends); + //Test With Z axis + TEST_CROSS_EDGE_BOX_Z_AXIS_MCR(diff,abs_diff,v2,v1,extends); + + diff = v1 - v3; + abs_diff = diff.absolute(); + //Test With X axis + TEST_CROSS_EDGE_BOX_X_AXIS_MCR(diff,abs_diff,v3,v2,extends); + //Test With Y axis + TEST_CROSS_EDGE_BOX_Y_AXIS_MCR(diff,abs_diff,v3,v2,extends); + //Test With Z axis + TEST_CROSS_EDGE_BOX_Z_AXIS_MCR(diff,abs_diff,v3,v2,extends); + + return true; + } +}; + + +//! Compairison of transformation objects +SIMD_FORCE_INLINE bool btCompareTransformsEqual(const btTransform & t1,const btTransform & t2) +{ + if(!(t1.getOrigin() == t2.getOrigin()) ) return false; + + if(!(t1.getBasis().getRow(0) == t2.getBasis().getRow(0)) ) return false; + if(!(t1.getBasis().getRow(1) == t2.getBasis().getRow(1)) ) return false; + if(!(t1.getBasis().getRow(2) == t2.getBasis().getRow(2)) ) return false; + return true; +} + + + +#endif // GIM_BOX_COLLISION_H_INCLUDED diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_box_set.cpp b/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_box_set.cpp new file mode 100644 index 000000000..0c3d7ba8d --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_box_set.cpp @@ -0,0 +1,182 @@ + +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon Najera. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + (3) The zlib/libpng license that is included with this library in + the file GIMPACT-LICENSE-ZLIB.TXT. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files + GIMPACT-LICENSE-LGPL.TXT, GIMPACT-LICENSE-ZLIB.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + + +#include "gim_box_set.h" + + +GUINT GIM_BOX_TREE::_calc_splitting_axis( + gim_array & primitive_boxes, GUINT startIndex, GUINT endIndex) +{ + GUINT i; + + btVector3 means(btScalar(0.),btScalar(0.),btScalar(0.)); + btVector3 variance(btScalar(0.),btScalar(0.),btScalar(0.)); + GUINT numIndices = endIndex-startIndex; + + for (i=startIndex;i & primitive_boxes, GUINT startIndex, + GUINT endIndex, GUINT splitAxis) +{ + GUINT i; + GUINT splitIndex =startIndex; + GUINT numIndices = endIndex - startIndex; + + // average of centers + btScalar splitValue = 0.0f; + for (i=startIndex;i splitValue) + { + //swap + primitive_boxes.swap(i,splitIndex); + splitIndex++; + } + } + + //if the splitIndex causes unbalanced trees, fix this by using the center in between startIndex and endIndex + //otherwise the tree-building might fail due to stack-overflows in certain cases. + //unbalanced1 is unsafe: it can cause stack overflows + //bool unbalanced1 = ((splitIndex==startIndex) || (splitIndex == (endIndex-1))); + + //unbalanced2 should work too: always use center (perfect balanced trees) + //bool unbalanced2 = true; + + //this should be safe too: + GUINT rangeBalancedIndices = numIndices/3; + bool unbalanced = ((splitIndex<=(startIndex+rangeBalancedIndices)) || (splitIndex >=(endIndex-1-rangeBalancedIndices))); + + if (unbalanced) + { + splitIndex = startIndex+ (numIndices>>1); + } + + btAssert(!((splitIndex==startIndex) || (splitIndex == (endIndex)))); + + return splitIndex; +} + + +void GIM_BOX_TREE::_build_sub_tree(gim_array & primitive_boxes, GUINT startIndex, GUINT endIndex) +{ + GUINT current_index = m_num_nodes++; + + btAssert((endIndex-startIndex)>0); + + if((endIndex-startIndex) == 1) //we got a leaf + { + m_node_array[current_index].m_left = 0; + m_node_array[current_index].m_right = 0; + m_node_array[current_index].m_escapeIndex = 0; + + m_node_array[current_index].m_bound = primitive_boxes[startIndex].m_bound; + m_node_array[current_index].m_data = primitive_boxes[startIndex].m_data; + return; + } + + //configure inner node + + GUINT splitIndex; + + //calc this node bounding box + m_node_array[current_index].m_bound.invalidate(); + for (splitIndex=startIndex;splitIndex & primitive_boxes) +{ + // initialize node count to 0 + m_num_nodes = 0; + // allocate nodes + m_node_array.resize(primitive_boxes.size()*2); + + _build_sub_tree(primitive_boxes, 0, primitive_boxes.size()); +} + + diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_box_set.h b/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_box_set.h new file mode 100644 index 000000000..1058a0872 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_box_set.h @@ -0,0 +1,671 @@ +#ifndef GIM_BOX_SET_H_INCLUDED +#define GIM_BOX_SET_H_INCLUDED + +/*! \file gim_box_set.h +\author Francisco Len Nßjera +*/ +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon Najera. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + (3) The zlib/libpng license that is included with this library in + the file GIMPACT-LICENSE-ZLIB.TXT. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files + GIMPACT-LICENSE-LGPL.TXT, GIMPACT-LICENSE-ZLIB.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + + +#include "gim_array.h" +#include "gim_radixsort.h" +#include "gim_box_collision.h" +#include "gim_tri_collision.h" + + + +//! Overlapping pair +struct GIM_PAIR +{ + GUINT m_index1; + GUINT m_index2; + GIM_PAIR() + {} + + GIM_PAIR(const GIM_PAIR & p) + { + m_index1 = p.m_index1; + m_index2 = p.m_index2; + } + + GIM_PAIR(GUINT index1, GUINT index2) + { + m_index1 = index1; + m_index2 = index2; + } +}; + +//! A pairset array +class gim_pair_set: public gim_array +{ +public: + gim_pair_set():gim_array(32) + { + } + inline void push_pair(GUINT index1,GUINT index2) + { + push_back(GIM_PAIR(index1,index2)); + } + + inline void push_pair_inv(GUINT index1,GUINT index2) + { + push_back(GIM_PAIR(index2,index1)); + } +}; + + +//! Prototype Base class for primitive classification +/*! +This class is a wrapper for primitive collections. +This tells relevant info for the Bounding Box set classes, which take care of space classification. +This class can manage Compound shapes and trimeshes, and if it is managing trimesh then the Hierarchy Bounding Box classes will take advantage of primitive Vs Box overlapping tests for getting optimal results and less Per Box compairisons. +*/ +class GIM_PRIMITIVE_MANAGER_PROTOTYPE +{ +public: + + //! determines if this manager consist on only triangles, which special case will be optimized + virtual bool is_trimesh() = 0; + virtual GUINT get_primitive_count() = 0; + virtual void get_primitive_box(GUINT prim_index ,GIM_AABB & primbox) = 0; + virtual void get_primitive_triangle(GUINT prim_index,GIM_TRIANGLE & triangle) = 0; +}; + + +struct GIM_AABB_DATA +{ + GIM_AABB m_bound; + GUINT m_data; +}; + +//! Node Structure for trees +struct GIM_BOX_TREE_NODE +{ + GIM_AABB m_bound; + GUINT m_left;//!< Left subtree + GUINT m_right;//!< Right subtree + GUINT m_escapeIndex;//!< Scape index for traversing + GUINT m_data;//!< primitive index if apply + + GIM_BOX_TREE_NODE() + { + m_left = 0; + m_right = 0; + m_escapeIndex = 0; + m_data = 0; + } + + SIMD_FORCE_INLINE bool is_leaf_node() const + { + return (!m_left && !m_right); + } +}; + +//! Basic Box tree structure +class GIM_BOX_TREE +{ +protected: + GUINT m_num_nodes; + gim_array m_node_array; +protected: + GUINT _sort_and_calc_splitting_index( + gim_array & primitive_boxes, + GUINT startIndex, GUINT endIndex, GUINT splitAxis); + + GUINT _calc_splitting_axis(gim_array & primitive_boxes, GUINT startIndex, GUINT endIndex); + + void _build_sub_tree(gim_array & primitive_boxes, GUINT startIndex, GUINT endIndex); +public: + GIM_BOX_TREE() + { + m_num_nodes = 0; + } + + //! prototype functions for box tree management + //!@{ + void build_tree(gim_array & primitive_boxes); + + SIMD_FORCE_INLINE void clearNodes() + { + m_node_array.clear(); + m_num_nodes = 0; + } + + //! node count + SIMD_FORCE_INLINE GUINT getNodeCount() const + { + return m_num_nodes; + } + + //! tells if the node is a leaf + SIMD_FORCE_INLINE bool isLeafNode(GUINT nodeindex) const + { + return m_node_array[nodeindex].is_leaf_node(); + } + + SIMD_FORCE_INLINE GUINT getNodeData(GUINT nodeindex) const + { + return m_node_array[nodeindex].m_data; + } + + SIMD_FORCE_INLINE void getNodeBound(GUINT nodeindex, GIM_AABB & bound) const + { + bound = m_node_array[nodeindex].m_bound; + } + + SIMD_FORCE_INLINE void setNodeBound(GUINT nodeindex, const GIM_AABB & bound) + { + m_node_array[nodeindex].m_bound = bound; + } + + SIMD_FORCE_INLINE GUINT getLeftNodeIndex(GUINT nodeindex) const + { + return m_node_array[nodeindex].m_left; + } + + SIMD_FORCE_INLINE GUINT getRightNodeIndex(GUINT nodeindex) const + { + return m_node_array[nodeindex].m_right; + } + + SIMD_FORCE_INLINE GUINT getScapeNodeIndex(GUINT nodeindex) const + { + return m_node_array[nodeindex].m_escapeIndex; + } + + //!@} +}; + + +//! Generic Box Tree Template +/*! +This class offers an structure for managing a box tree of primitives. +Requires a Primitive prototype (like GIM_PRIMITIVE_MANAGER_PROTOTYPE ) and +a Box tree structure ( like GIM_BOX_TREE). +*/ +template +class GIM_BOX_TREE_TEMPLATE_SET +{ +protected: + _GIM_PRIMITIVE_MANAGER_PROTOTYPE m_primitive_manager; + _GIM_BOX_TREE_PROTOTYPE m_box_tree; +protected: + //stackless refit + SIMD_FORCE_INLINE void refit() + { + GUINT nodecount = getNodeCount(); + while(nodecount--) + { + if(isLeafNode(nodecount)) + { + GIM_AABB leafbox; + m_primitive_manager.get_primitive_box(getNodeData(nodecount),leafbox); + setNodeBound(nodecount,leafbox); + } + else + { + //get left bound + GUINT childindex = getLeftNodeIndex(nodecount); + GIM_AABB bound; + getNodeBound(childindex,bound); + //get right bound + childindex = getRightNodeIndex(nodecount); + GIM_AABB bound2; + getNodeBound(childindex,bound2); + bound.merge(bound2); + + setNodeBound(nodecount,bound); + } + } + } +public: + + GIM_BOX_TREE_TEMPLATE_SET() + { + } + + SIMD_FORCE_INLINE GIM_AABB getGlobalBox() const + { + GIM_AABB totalbox; + getNodeBound(0, totalbox); + return totalbox; + } + + SIMD_FORCE_INLINE void setPrimitiveManager(const _GIM_PRIMITIVE_MANAGER_PROTOTYPE & primitive_manager) + { + m_primitive_manager = primitive_manager; + } + + const _GIM_PRIMITIVE_MANAGER_PROTOTYPE & getPrimitiveManager() const + { + return m_primitive_manager; + } + + _GIM_PRIMITIVE_MANAGER_PROTOTYPE & getPrimitiveManager() + { + return m_primitive_manager; + } + +//! node manager prototype functions +///@{ + + //! this attemps to refit the box set. + SIMD_FORCE_INLINE void update() + { + refit(); + } + + //! this rebuild the entire set + SIMD_FORCE_INLINE void buildSet() + { + //obtain primitive boxes + gim_array primitive_boxes; + primitive_boxes.resize(m_primitive_manager.get_primitive_count(),false); + + for (GUINT i = 0;i & collided_results) const + { + GUINT curIndex = 0; + GUINT numNodes = getNodeCount(); + + while (curIndex < numNodes) + { + GIM_AABB bound; + getNodeBound(curIndex,bound); + + //catch bugs in tree data + + bool aabbOverlap = bound.has_collision(box); + bool isleafnode = isLeafNode(curIndex); + + if (isleafnode && aabbOverlap) + { + collided_results.push_back(getNodeData(curIndex)); + } + + if (aabbOverlap || isleafnode) + { + //next subnode + curIndex++; + } + else + { + //skip node + curIndex+= getScapeNodeIndex(curIndex); + } + } + if(collided_results.size()>0) return true; + return false; + } + + //! returns the indices of the primitives in the m_primitive_manager + SIMD_FORCE_INLINE bool boxQueryTrans(const GIM_AABB & box, + const btTransform & transform, gim_array & collided_results) const + { + GIM_AABB transbox=box; + transbox.appy_transform(transform); + return boxQuery(transbox,collided_results); + } + + //! returns the indices of the primitives in the m_primitive_manager + SIMD_FORCE_INLINE bool rayQuery( + const btVector3 & ray_dir,const btVector3 & ray_origin , + gim_array & collided_results) const + { + GUINT curIndex = 0; + GUINT numNodes = getNodeCount(); + + while (curIndex < numNodes) + { + GIM_AABB bound; + getNodeBound(curIndex,bound); + + //catch bugs in tree data + + bool aabbOverlap = bound.collide_ray(ray_origin,ray_dir); + bool isleafnode = isLeafNode(curIndex); + + if (isleafnode && aabbOverlap) + { + collided_results.push_back(getNodeData( curIndex)); + } + + if (aabbOverlap || isleafnode) + { + //next subnode + curIndex++; + } + else + { + //skip node + curIndex+= getScapeNodeIndex(curIndex); + } + } + if(collided_results.size()>0) return true; + return false; + } + + //! tells if this set has hierarcht + SIMD_FORCE_INLINE bool hasHierarchy() const + { + return true; + } + + //! tells if this set is a trimesh + SIMD_FORCE_INLINE bool isTrimesh() const + { + return m_primitive_manager.is_trimesh(); + } + + //! node count + SIMD_FORCE_INLINE GUINT getNodeCount() const + { + return m_box_tree.getNodeCount(); + } + + //! tells if the node is a leaf + SIMD_FORCE_INLINE bool isLeafNode(GUINT nodeindex) const + { + return m_box_tree.isLeafNode(nodeindex); + } + + SIMD_FORCE_INLINE GUINT getNodeData(GUINT nodeindex) const + { + return m_box_tree.getNodeData(nodeindex); + } + + SIMD_FORCE_INLINE void getNodeBound(GUINT nodeindex, GIM_AABB & bound) const + { + m_box_tree.getNodeBound(nodeindex, bound); + } + + SIMD_FORCE_INLINE void setNodeBound(GUINT nodeindex, const GIM_AABB & bound) + { + m_box_tree.setNodeBound(nodeindex, bound); + } + + SIMD_FORCE_INLINE GUINT getLeftNodeIndex(GUINT nodeindex) const + { + return m_box_tree.getLeftNodeIndex(nodeindex); + } + + SIMD_FORCE_INLINE GUINT getRightNodeIndex(GUINT nodeindex) const + { + return m_box_tree.getRightNodeIndex(nodeindex); + } + + SIMD_FORCE_INLINE GUINT getScapeNodeIndex(GUINT nodeindex) const + { + return m_box_tree.getScapeNodeIndex(nodeindex); + } + + SIMD_FORCE_INLINE void getNodeTriangle(GUINT nodeindex,GIM_TRIANGLE & triangle) const + { + m_primitive_manager.get_primitive_triangle(getNodeData(nodeindex),triangle); + } + +}; + +//! Class for Box Tree Sets +/*! +this has the GIM_BOX_TREE implementation for bounding boxes. +*/ +template +class GIM_BOX_TREE_SET: public GIM_BOX_TREE_TEMPLATE_SET< _GIM_PRIMITIVE_MANAGER_PROTOTYPE, GIM_BOX_TREE> +{ +public: + +}; + + + + + +/// GIM_BOX_SET collision methods +template +class GIM_TREE_TREE_COLLIDER +{ +public: + gim_pair_set * m_collision_pairs; + BOX_SET_CLASS0 * m_boxset0; + BOX_SET_CLASS1 * m_boxset1; + GUINT current_node0; + GUINT current_node1; + bool node0_is_leaf; + bool node1_is_leaf; + bool t0_is_trimesh; + bool t1_is_trimesh; + bool node0_has_triangle; + bool node1_has_triangle; + GIM_AABB m_box0; + GIM_AABB m_box1; + GIM_BOX_BOX_TRANSFORM_CACHE trans_cache_1to0; + btTransform trans_cache_0to1; + GIM_TRIANGLE m_tri0; + btVector4 m_tri0_plane; + GIM_TRIANGLE m_tri1; + btVector4 m_tri1_plane; + + +public: + GIM_TREE_TREE_COLLIDER() + { + current_node0 = G_UINT_INFINITY; + current_node1 = G_UINT_INFINITY; + } +protected: + SIMD_FORCE_INLINE void retrieve_node0_triangle(GUINT node0) + { + if(node0_has_triangle) return; + m_boxset0->getNodeTriangle(node0,m_tri0); + //transform triangle + m_tri0.m_vertices[0] = trans_cache_0to1(m_tri0.m_vertices[0]); + m_tri0.m_vertices[1] = trans_cache_0to1(m_tri0.m_vertices[1]); + m_tri0.m_vertices[2] = trans_cache_0to1(m_tri0.m_vertices[2]); + m_tri0.get_plane(m_tri0_plane); + + node0_has_triangle = true; + } + + SIMD_FORCE_INLINE void retrieve_node1_triangle(GUINT node1) + { + if(node1_has_triangle) return; + m_boxset1->getNodeTriangle(node1,m_tri1); + //transform triangle + m_tri1.m_vertices[0] = trans_cache_1to0.transform(m_tri1.m_vertices[0]); + m_tri1.m_vertices[1] = trans_cache_1to0.transform(m_tri1.m_vertices[1]); + m_tri1.m_vertices[2] = trans_cache_1to0.transform(m_tri1.m_vertices[2]); + m_tri1.get_plane(m_tri1_plane); + + node1_has_triangle = true; + } + + SIMD_FORCE_INLINE void retrieve_node0_info(GUINT node0) + { + if(node0 == current_node0) return; + m_boxset0->getNodeBound(node0,m_box0); + node0_is_leaf = m_boxset0->isLeafNode(node0); + node0_has_triangle = false; + current_node0 = node0; + } + + SIMD_FORCE_INLINE void retrieve_node1_info(GUINT node1) + { + if(node1 == current_node1) return; + m_boxset1->getNodeBound(node1,m_box1); + node1_is_leaf = m_boxset1->isLeafNode(node1); + node1_has_triangle = false; + current_node1 = node1; + } + + SIMD_FORCE_INLINE bool node_collision(GUINT node0 ,GUINT node1) + { + retrieve_node0_info(node0); + retrieve_node1_info(node1); + bool result = m_box0.overlapping_trans_cache(m_box1,trans_cache_1to0,true); + if(!result) return false; + + if(t0_is_trimesh && node0_is_leaf) + { + //perform primitive vs box collision + retrieve_node0_triangle(node0); + //do triangle vs box collision + m_box1.increment_margin(m_tri0.m_margin); + + result = m_box1.collide_triangle_exact( + m_tri0.m_vertices[0],m_tri0.m_vertices[1],m_tri0.m_vertices[2],m_tri0_plane); + + m_box1.increment_margin(-m_tri0.m_margin); + + if(!result) return false; + return true; + } + else if(t1_is_trimesh && node1_is_leaf) + { + //perform primitive vs box collision + retrieve_node1_triangle(node1); + //do triangle vs box collision + m_box0.increment_margin(m_tri1.m_margin); + + result = m_box0.collide_triangle_exact( + m_tri1.m_vertices[0],m_tri1.m_vertices[1],m_tri1.m_vertices[2],m_tri1_plane); + + m_box0.increment_margin(-m_tri1.m_margin); + + if(!result) return false; + return true; + } + return true; + } + + //stackless collision routine + void find_collision_pairs() + { + gim_pair_set stack_collisions; + stack_collisions.reserve(32); + + //add the first pair + stack_collisions.push_pair(0,0); + + + while(stack_collisions.size()) + { + //retrieve the last pair and pop + GUINT node0 = stack_collisions.back().m_index1; + GUINT node1 = stack_collisions.back().m_index2; + stack_collisions.pop_back(); + if(node_collision(node0,node1)) // a collision is found + { + if(node0_is_leaf) + { + if(node1_is_leaf) + { + m_collision_pairs->push_pair(m_boxset0->getNodeData(node0),m_boxset1->getNodeData(node1)); + } + else + { + //collide left + stack_collisions.push_pair(node0,m_boxset1->getLeftNodeIndex(node1)); + + //collide right + stack_collisions.push_pair(node0,m_boxset1->getRightNodeIndex(node1)); + } + } + else + { + if(node1_is_leaf) + { + //collide left + stack_collisions.push_pair(m_boxset0->getLeftNodeIndex(node0),node1); + //collide right + stack_collisions.push_pair(m_boxset0->getRightNodeIndex(node0),node1); + } + else + { + GUINT left0 = m_boxset0->getLeftNodeIndex(node0); + GUINT right0 = m_boxset0->getRightNodeIndex(node0); + GUINT left1 = m_boxset1->getLeftNodeIndex(node1); + GUINT right1 = m_boxset1->getRightNodeIndex(node1); + //collide left + stack_collisions.push_pair(left0,left1); + //collide right + stack_collisions.push_pair(left0,right1); + //collide left + stack_collisions.push_pair(right0,left1); + //collide right + stack_collisions.push_pair(right0,right1); + + }// else if node1 is not a leaf + }// else if node0 is not a leaf + + }// if(node_collision(node0,node1)) + }//while(stack_collisions.size()) + } +public: + void find_collision(BOX_SET_CLASS0 * boxset1, const btTransform & trans1, + BOX_SET_CLASS1 * boxset2, const btTransform & trans2, + gim_pair_set & collision_pairs, bool complete_primitive_tests = true) + { + m_collision_pairs = &collision_pairs; + m_boxset0 = boxset1; + m_boxset1 = boxset2; + + trans_cache_1to0.calc_from_homogenic(trans1,trans2); + + trans_cache_0to1 = trans2.inverse(); + trans_cache_0to1 *= trans1; + + + if(complete_primitive_tests) + { + t0_is_trimesh = boxset1->getPrimitiveManager().is_trimesh(); + t1_is_trimesh = boxset2->getPrimitiveManager().is_trimesh(); + } + else + { + t0_is_trimesh = false; + t1_is_trimesh = false; + } + + find_collision_pairs(); + } +}; + + +#endif // GIM_BOXPRUNING_H_INCLUDED diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_clip_polygon.h b/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_clip_polygon.h new file mode 100644 index 000000000..a91fd3aa4 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_clip_polygon.h @@ -0,0 +1,210 @@ +#ifndef GIM_CLIP_POLYGON_H_INCLUDED +#define GIM_CLIP_POLYGON_H_INCLUDED + +/*! \file gim_tri_collision.h +\author Francisco Len Nßjera +*/ +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon Najera. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + (3) The zlib/libpng license that is included with this library in + the file GIMPACT-LICENSE-ZLIB.TXT. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files + GIMPACT-LICENSE-LGPL.TXT, GIMPACT-LICENSE-ZLIB.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + + +//! This function calcs the distance from a 3D plane +class DISTANCE_PLANE_3D_FUNC +{ +public: + template + inline GREAL operator()(const CLASS_PLANE & plane, const CLASS_POINT & point) + { + return DISTANCE_PLANE_POINT(plane, point); + } +}; + + + +template +SIMD_FORCE_INLINE void PLANE_CLIP_POLYGON_COLLECT( + const CLASS_POINT & point0, + const CLASS_POINT & point1, + GREAL dist0, + GREAL dist1, + CLASS_POINT * clipped, + GUINT & clipped_count) +{ + GUINT _prevclassif = (dist0>G_EPSILON); + GUINT _classif = (dist1>G_EPSILON); + if(_classif!=_prevclassif) + { + GREAL blendfactor = -dist0/(dist1-dist0); + VEC_BLEND(clipped[clipped_count],point0,point1,blendfactor); + clipped_count++; + } + if(!_classif) + { + VEC_COPY(clipped[clipped_count],point1); + clipped_count++; + } +} + + +//! Clips a polygon by a plane +/*! +*\return The count of the clipped counts +*/ +template +SIMD_FORCE_INLINE GUINT PLANE_CLIP_POLYGON_GENERIC( + const CLASS_PLANE & plane, + const CLASS_POINT * polygon_points, + GUINT polygon_point_count, + CLASS_POINT * clipped,DISTANCE_PLANE_FUNC distance_func) +{ + GUINT clipped_count = 0; + + + //clip first point + GREAL firstdist = distance_func(plane,polygon_points[0]);; + if(!(firstdist>G_EPSILON)) + { + VEC_COPY(clipped[clipped_count],polygon_points[0]); + clipped_count++; + } + + GREAL olddist = firstdist; + for(GUINT _i=1;_i +SIMD_FORCE_INLINE GUINT PLANE_CLIP_TRIANGLE_GENERIC( + const CLASS_PLANE & plane, + const CLASS_POINT & point0, + const CLASS_POINT & point1, + const CLASS_POINT & point2, + CLASS_POINT * clipped,DISTANCE_PLANE_FUNC distance_func) +{ + GUINT clipped_count = 0; + + //clip first point + GREAL firstdist = distance_func(plane,point0);; + if(!(firstdist>G_EPSILON)) + { + VEC_COPY(clipped[clipped_count],point0); + clipped_count++; + } + + // point 1 + GREAL olddist = firstdist; + GREAL dist = distance_func(plane,point1); + + PLANE_CLIP_POLYGON_COLLECT( + point0,point1, + olddist, + dist, + clipped, + clipped_count); + + olddist = dist; + + + // point 2 + dist = distance_func(plane,point2); + + PLANE_CLIP_POLYGON_COLLECT( + point1,point2, + olddist, + dist, + clipped, + clipped_count); + olddist = dist; + + + + //RETURN TO FIRST point + PLANE_CLIP_POLYGON_COLLECT( + point2,point0, + olddist, + firstdist, + clipped, + clipped_count); + + return clipped_count; +} + + +template +SIMD_FORCE_INLINE GUINT PLANE_CLIP_POLYGON3D( + const CLASS_PLANE & plane, + const CLASS_POINT * polygon_points, + GUINT polygon_point_count, + CLASS_POINT * clipped) +{ + return PLANE_CLIP_POLYGON_GENERIC(plane,polygon_points,polygon_point_count,clipped,DISTANCE_PLANE_3D_FUNC()); +} + + +template +SIMD_FORCE_INLINE GUINT PLANE_CLIP_TRIANGLE3D( + const CLASS_PLANE & plane, + const CLASS_POINT & point0, + const CLASS_POINT & point1, + const CLASS_POINT & point2, + CLASS_POINT * clipped) +{ + return PLANE_CLIP_TRIANGLE_GENERIC(plane,point0,point1,point2,clipped,DISTANCE_PLANE_3D_FUNC()); +} + + + +#endif // GIM_TRI_COLLISION_H_INCLUDED diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_contact.cpp b/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_contact.cpp new file mode 100644 index 000000000..20e41de08 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_contact.cpp @@ -0,0 +1,146 @@ + +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon Najera. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + (3) The zlib/libpng license that is included with this library in + the file GIMPACT-LICENSE-ZLIB.TXT. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files + GIMPACT-LICENSE-LGPL.TXT, GIMPACT-LICENSE-ZLIB.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + +#include "gim_contact.h" + +#define MAX_COINCIDENT 8 + +void gim_contact_array::merge_contacts( + const gim_contact_array & contacts, bool normal_contact_average) +{ + clear(); + + if(contacts.size()==1) + { + push_back(contacts.back()); + return; + } + + gim_array keycontacts(contacts.size()); + keycontacts.resize(contacts.size(),false); + + //fill key contacts + + GUINT i; + + for (i = 0;im_depth - CONTACT_DIFF_EPSILON > scontact->m_depth)//) + { + *pcontact = *scontact; + coincident_count = 0; + } + else if(normal_contact_average) + { + if(btFabs(pcontact->m_depth - scontact->m_depth)m_normal; + coincident_count++; + } + } + } + } + else + {//add new contact + + if(normal_contact_average && coincident_count>0) + { + pcontact->interpolate_normals(coincident_normals,coincident_count); + coincident_count = 0; + } + + push_back(*scontact); + pcontact = &back(); + } + last_key = key; + } +} + +void gim_contact_array::merge_contacts_unique(const gim_contact_array & contacts) +{ + clear(); + + if(contacts.size()==1) + { + push_back(contacts.back()); + return; + } + + GIM_CONTACT average_contact = contacts.back(); + + for (GUINT i=1;i +{ +public: + gim_contact_array():gim_array(64) + { + } + + SIMD_FORCE_INLINE void push_contact(const btVector3 &point,const btVector3 & normal, + GREAL depth, GUINT feature1, GUINT feature2) + { + push_back_mem(); + GIM_CONTACT & newele = back(); + newele.m_point = point; + newele.m_normal = normal; + newele.m_depth = depth; + newele.m_feature1 = feature1; + newele.m_feature2 = feature2; + } + + SIMD_FORCE_INLINE void push_triangle_contacts( + const GIM_TRIANGLE_CONTACT_DATA & tricontact, + GUINT feature1,GUINT feature2) + { + for(GUINT i = 0;i +struct GIM_HASH_TABLE_NODE +{ + GUINT m_key; + T m_data; + GIM_HASH_TABLE_NODE() + { + } + + GIM_HASH_TABLE_NODE(const GIM_HASH_TABLE_NODE & value) + { + m_key = value.m_key; + m_data = value.m_data; + } + + GIM_HASH_TABLE_NODE(GUINT key, const T & data) + { + m_key = key; + m_data = data; + } + + bool operator <(const GIM_HASH_TABLE_NODE & other) const + { + ///inverse order, further objects are first + if(m_key < other.m_key) return true; + return false; + } + + bool operator >(const GIM_HASH_TABLE_NODE & other) const + { + ///inverse order, further objects are first + if(m_key > other.m_key) return true; + return false; + } + + bool operator ==(const GIM_HASH_TABLE_NODE & other) const + { + ///inverse order, further objects are first + if(m_key == other.m_key) return true; + return false; + } +}; + +///Macro for getting the key +class GIM_HASH_NODE_GET_KEY +{ +public: + template + inline GUINT operator()( const T& a) + { + return a.m_key; + } +}; + + + +///Macro for comparing the key and the element +class GIM_HASH_NODE_CMP_KEY_MACRO +{ +public: + template + inline int operator() ( const T& a, GUINT key) + { + return ((int)(a.m_key - key)); + } +}; + +///Macro for comparing Hash nodes +class GIM_HASH_NODE_CMP_MACRO +{ +public: + template + inline int operator() ( const T& a, const T& b ) + { + return ((int)(a.m_key - b.m_key)); + } +}; + + + + + +//! Sorting for hash table +/*! +switch automatically between quicksort and radixsort +*/ +template +void gim_sort_hash_node_array(T * array, GUINT array_count) +{ + if(array_count + +
    +
  • if node_size = 0, then this container becomes a simple sorted array allocator. reserve_size is used for reserve memory in m_nodes. +When the array size reaches the size equivalent to 'min_hash_table_size', then it becomes a hash table by calling check_for_switching_to_hashtable. +
  • If node_size != 0, then this container becomes a hash table for ever +
+ +*/ +template +class gim_hash_table +{ +protected: + typedef GIM_HASH_TABLE_NODE _node_type; + + //!The nodes + //array< _node_type, SuperAllocator<_node_type> > m_nodes; + gim_array< _node_type > m_nodes; + //SuperBufferedArray< _node_type > m_nodes; + bool m_sorted; + + ///Hash table data management. The hash table has the indices to the corresponding m_nodes array + GUINT * m_hash_table;//!< + GUINT m_table_size;//!< + GUINT m_node_size;//!< + GUINT m_min_hash_table_size; + + + + //! Returns the cell index + inline GUINT _find_cell(GUINT hashkey) + { + _node_type * nodesptr = m_nodes.pointer(); + GUINT start_index = (hashkey%m_table_size)*m_node_size; + GUINT end_index = start_index + m_node_size; + + while(start_index= m_nodes.size()) return false; + if(m_nodes[index].m_key != GIM_INVALID_HASH) + { + //Search for the avaliable cell in buffer + GUINT cell_index = _find_cell(m_nodes[index].m_key); + + btAssert(cell_index!=GIM_INVALID_HASH); + btAssert(m_hash_table[cell_index]==index); + + m_hash_table[cell_index] = GIM_INVALID_HASH; + } + + return this->_erase_unsorted(index); + } + + //! erase by key in hash table + inline bool _erase_hash_table(GUINT hashkey) + { + if(hashkey == GIM_INVALID_HASH) return false; + + //Search for the avaliable cell in buffer + GUINT cell_index = _find_cell(hashkey); + if(cell_index ==GIM_INVALID_HASH) return false; + + GUINT index = m_hash_table[cell_index]; + m_hash_table[cell_index] = GIM_INVALID_HASH; + + return this->_erase_unsorted(index); + } + + + + //! insert an element in hash table + /*! + If the element exists, this won't insert the element + \return the index in the array of the existing element,or GIM_INVALID_HASH if the element has been inserted + If so, the element has been inserted at the last position of the array. + */ + inline GUINT _insert_hash_table(GUINT hashkey, const T & value) + { + if(hashkey==GIM_INVALID_HASH) + { + //Insert anyway + _insert_unsorted(hashkey,value); + return GIM_INVALID_HASH; + } + + GUINT cell_index = _assign_hash_table_cell(hashkey); + + GUINT value_key = m_hash_table[cell_index]; + + if(value_key!= GIM_INVALID_HASH) return value_key;// Not overrited + + m_hash_table[cell_index] = m_nodes.size(); + + _insert_unsorted(hashkey,value); + return GIM_INVALID_HASH; + } + + //! insert an element in hash table. + /*! + If the element exists, this replaces the element. + \return the index in the array of the existing element,or GIM_INVALID_HASH if the element has been inserted + If so, the element has been inserted at the last position of the array. + */ + inline GUINT _insert_hash_table_replace(GUINT hashkey, const T & value) + { + if(hashkey==GIM_INVALID_HASH) + { + //Insert anyway + _insert_unsorted(hashkey,value); + return GIM_INVALID_HASH; + } + + GUINT cell_index = _assign_hash_table_cell(hashkey); + + GUINT value_key = m_hash_table[cell_index]; + + if(value_key!= GIM_INVALID_HASH) + {//replaces the existing + m_nodes[value_key] = _node_type(hashkey,value); + return value_key;// index of the replaced element + } + + m_hash_table[cell_index] = m_nodes.size(); + + _insert_unsorted(hashkey,value); + return GIM_INVALID_HASH; + + } + + + ///Sorted array data management. The hash table has the indices to the corresponding m_nodes array + inline bool _erase_sorted(GUINT index) + { + if(index>=(GUINT)m_nodes.size()) return false; + m_nodes.erase_sorted(index); + if(m_nodes.size()<2) m_sorted = false; + return true; + } + + //! faster, but unsorted + inline bool _erase_unsorted(GUINT index) + { + if(index>=m_nodes.size()) return false; + + GUINT lastindex = m_nodes.size()-1; + if(indexcheck_for_switching_to_hashtable(); + } + + //! Insert an element in an ordered array + inline GUINT _insert_sorted(GUINT hashkey, const T & value) + { + if(hashkey==GIM_INVALID_HASH || size()==0) + { + m_nodes.push_back(_node_type(hashkey,value)); + return GIM_INVALID_HASH; + } + //Insert at last position + //Sort element + + + GUINT result_ind=0; + GUINT last_index = m_nodes.size()-1; + _node_type * ptr = m_nodes.pointer(); + + bool found = gim_binary_search_ex( + ptr,0,last_index,result_ind,hashkey,GIM_HASH_NODE_CMP_KEY_MACRO()); + + + //Insert before found index + if(found) + { + return result_ind; + } + else + { + _insert_in_pos(hashkey, value, result_ind); + } + return GIM_INVALID_HASH; + } + + inline GUINT _insert_sorted_replace(GUINT hashkey, const T & value) + { + if(hashkey==GIM_INVALID_HASH || size()==0) + { + m_nodes.push_back(_node_type(hashkey,value)); + return GIM_INVALID_HASH; + } + //Insert at last position + //Sort element + GUINT result_ind; + GUINT last_index = m_nodes.size()-1; + _node_type * ptr = m_nodes.pointer(); + + bool found = gim_binary_search_ex( + ptr,0,last_index,result_ind,hashkey,GIM_HASH_NODE_CMP_KEY_MACRO()); + + //Insert before found index + if(found) + { + m_nodes[result_ind] = _node_type(hashkey,value); + } + else + { + _insert_in_pos(hashkey, value, result_ind); + } + return result_ind; + } + + //! Fast insertion in m_nodes array + inline GUINT _insert_unsorted(GUINT hashkey, const T & value) + { + m_nodes.push_back(_node_type(hashkey,value)); + m_sorted = false; + return GIM_INVALID_HASH; + } + + + +public: + + /*! +
  • if node_size = 0, then this container becomes a simple sorted array allocator. reserve_size is used for reserve memory in m_nodes. + When the array size reaches the size equivalent to 'min_hash_table_size', then it becomes a hash table by calling check_for_switching_to_hashtable. +
  • If node_size != 0, then this container becomes a hash table for ever + + */ + gim_hash_table(GUINT reserve_size = GIM_DEFAULT_HASH_TABLE_SIZE, + GUINT node_size = GIM_DEFAULT_HASH_TABLE_NODE_SIZE, + GUINT min_hash_table_size = GIM_INVALID_HASH) + { + m_hash_table = NULL; + m_table_size = 0; + m_sorted = false; + m_node_size = node_size; + m_min_hash_table_size = min_hash_table_size; + + if(m_node_size!=0) + { + if(reserve_size!=0) + { + m_nodes.reserve(reserve_size); + _reserve_table_memory(reserve_size); + _invalidate_keys(); + } + else + { + m_nodes.reserve(GIM_DEFAULT_HASH_TABLE_SIZE); + _reserve_table_memory(GIM_DEFAULT_HASH_TABLE_SIZE); + _invalidate_keys(); + } + } + else if(reserve_size!=0) + { + m_nodes.reserve(reserve_size); + } + + } + + ~gim_hash_table() + { + _destroy(); + } + + inline bool is_hash_table() + { + if(m_hash_table) return true; + return false; + } + + inline bool is_sorted() + { + if(size()<2) return true; + return m_sorted; + } + + bool sort() + { + if(is_sorted()) return true; + if(m_nodes.size()<2) return false; + + + _node_type * ptr = m_nodes.pointer(); + GUINT siz = m_nodes.size(); + gim_sort_hash_node_array(ptr,siz); + m_sorted=true; + + + + if(m_hash_table) + { + _rehash(); + } + return true; + } + + bool switch_to_hashtable() + { + if(m_hash_table) return false; + if(m_node_size==0) m_node_size = GIM_DEFAULT_HASH_TABLE_NODE_SIZE; + if(m_nodes.size()m_hash_table) return true; + + if(!(m_nodes.size()< m_min_hash_table_size)) + { + if(m_node_size == 0) + { + m_node_size = GIM_DEFAULT_HASH_TABLE_NODE_SIZE; + } + + _resize_table(m_nodes.size()+1); + return true; + } + return false; + } + + inline void set_sorted(bool value) + { + m_sorted = value; + } + + //! Retrieves the amount of keys. + inline GUINT size() const + { + return m_nodes.size(); + } + + //! Retrieves the hash key. + inline GUINT get_key(GUINT index) const + { + return m_nodes[index].m_key; + } + + //! Retrieves the value by index + /*! + */ + inline T * get_value_by_index(GUINT index) + { + return &m_nodes[index].m_data; + } + + inline const T& operator[](GUINT index) const + { + return m_nodes[index].m_data; + } + + inline T& operator[](GUINT index) + { + return m_nodes[index].m_data; + } + + //! Finds the index of the element with the key + /*! + \return the index in the array of the existing element,or GIM_INVALID_HASH if the element has been inserted + If so, the element has been inserted at the last position of the array. + */ + inline GUINT find(GUINT hashkey) + { + if(m_hash_table) + { + GUINT cell_index = _find_cell(hashkey); + if(cell_index==GIM_INVALID_HASH) return GIM_INVALID_HASH; + return m_hash_table[cell_index]; + } + GUINT last_index = m_nodes.size(); + if(last_index<2) + { + if(last_index==0) return GIM_INVALID_HASH; + if(m_nodes[0].m_key == hashkey) return 0; + return GIM_INVALID_HASH; + } + else if(m_sorted) + { + //Binary search + GUINT result_ind = 0; + last_index--; + _node_type * ptr = m_nodes.pointer(); + + bool found = gim_binary_search_ex(ptr,0,last_index,result_ind,hashkey,GIM_HASH_NODE_CMP_KEY_MACRO()); + + + if(found) return result_ind; + } + return GIM_INVALID_HASH; + } + + //! Retrieves the value associated with the index + /*! + \return the found element, or null + */ + inline T * get_value(GUINT hashkey) + { + GUINT index = find(hashkey); + if(index == GIM_INVALID_HASH) return NULL; + return &m_nodes[index].m_data; + } + + + /*! + */ + inline bool erase_by_index(GUINT index) + { + if(index > m_nodes.size()) return false; + + if(m_hash_table == NULL) + { + if(is_sorted()) + { + return this->_erase_sorted(index); + } + else + { + return this->_erase_unsorted(index); + } + } + else + { + return this->_erase_by_index_hash_table(index); + } + return false; + } + + + + inline bool erase_by_index_unsorted(GUINT index) + { + if(index > m_nodes.size()) return false; + + if(m_hash_table == NULL) + { + return this->_erase_unsorted(index); + } + else + { + return this->_erase_by_index_hash_table(index); + } + return false; + } + + + + /*! + + */ + inline bool erase_by_key(GUINT hashkey) + { + if(size()==0) return false; + + if(m_hash_table) + { + return this->_erase_hash_table(hashkey); + } + //Binary search + + if(is_sorted()==false) return false; + + GUINT result_ind = find(hashkey); + if(result_ind!= GIM_INVALID_HASH) + { + return this->_erase_sorted(result_ind); + } + return false; + } + + void clear() + { + m_nodes.clear(); + + if(m_hash_table==NULL) return; + GUINT datasize = m_table_size*m_node_size; + //Initialize the hashkeys. + GUINT i; + for(i=0;i_insert_hash_table(hashkey,element); + } + if(this->is_sorted()) + { + return this->_insert_sorted(hashkey,element); + } + return this->_insert_unsorted(hashkey,element); + } + + //! Insert an element into the hash, and could overrite an existing object with the same hash. + /*! + \return If GIM_INVALID_HASH, the object has been inserted succesfully. Else it returns the position + of the replaced element. + */ + inline GUINT insert_override(GUINT hashkey, const T & element) + { + if(m_hash_table) + { + return this->_insert_hash_table_replace(hashkey,element); + } + if(this->is_sorted()) + { + return this->_insert_sorted_replace(hashkey,element); + } + this->_insert_unsorted(hashkey,element); + return m_nodes.size(); + } + + + + //! Insert an element into the hash,But if this container is a sorted array, this inserts it unsorted + /*! + */ + inline GUINT insert_unsorted(GUINT hashkey,const T & element) + { + if(m_hash_table) + { + return this->_insert_hash_table(hashkey,element); + } + return this->_insert_unsorted(hashkey,element); + } + + +}; + + + +#endif // GIM_CONTAINERS_H_INCLUDED diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_linear_math.h b/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_linear_math.h new file mode 100644 index 000000000..0247d4e61 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_linear_math.h @@ -0,0 +1,1573 @@ +#ifndef GIM_LINEAR_H_INCLUDED +#define GIM_LINEAR_H_INCLUDED + +/*! \file gim_linear_math.h +*\author Francisco Len Nßjera +Type Independant Vector and matrix operations. +*/ +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon Najera. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + (3) The zlib/libpng license that is included with this library in + the file GIMPACT-LICENSE-ZLIB.TXT. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files + GIMPACT-LICENSE-LGPL.TXT, GIMPACT-LICENSE-ZLIB.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + + +#include "gim_math.h" +#include "gim_geom_types.h" + + + + +//! Zero out a 2D vector +#define VEC_ZERO_2(a) \ +{ \ + (a)[0] = (a)[1] = 0.0f; \ +}\ + + +//! Zero out a 3D vector +#define VEC_ZERO(a) \ +{ \ + (a)[0] = (a)[1] = (a)[2] = 0.0f; \ +}\ + + +/// Zero out a 4D vector +#define VEC_ZERO_4(a) \ +{ \ + (a)[0] = (a)[1] = (a)[2] = (a)[3] = 0.0f; \ +}\ + + +/// Vector copy +#define VEC_COPY_2(b,a) \ +{ \ + (b)[0] = (a)[0]; \ + (b)[1] = (a)[1]; \ +}\ + + +/// Copy 3D vector +#define VEC_COPY(b,a) \ +{ \ + (b)[0] = (a)[0]; \ + (b)[1] = (a)[1]; \ + (b)[2] = (a)[2]; \ +}\ + + +/// Copy 4D vector +#define VEC_COPY_4(b,a) \ +{ \ + (b)[0] = (a)[0]; \ + (b)[1] = (a)[1]; \ + (b)[2] = (a)[2]; \ + (b)[3] = (a)[3]; \ +}\ + +/// VECTOR SWAP +#define VEC_SWAP(b,a) \ +{ \ + GIM_SWAP_NUMBERS((b)[0],(a)[0]);\ + GIM_SWAP_NUMBERS((b)[1],(a)[1]);\ + GIM_SWAP_NUMBERS((b)[2],(a)[2]);\ +}\ + +/// Vector difference +#define VEC_DIFF_2(v21,v2,v1) \ +{ \ + (v21)[0] = (v2)[0] - (v1)[0]; \ + (v21)[1] = (v2)[1] - (v1)[1]; \ +}\ + + +/// Vector difference +#define VEC_DIFF(v21,v2,v1) \ +{ \ + (v21)[0] = (v2)[0] - (v1)[0]; \ + (v21)[1] = (v2)[1] - (v1)[1]; \ + (v21)[2] = (v2)[2] - (v1)[2]; \ +}\ + + +/// Vector difference +#define VEC_DIFF_4(v21,v2,v1) \ +{ \ + (v21)[0] = (v2)[0] - (v1)[0]; \ + (v21)[1] = (v2)[1] - (v1)[1]; \ + (v21)[2] = (v2)[2] - (v1)[2]; \ + (v21)[3] = (v2)[3] - (v1)[3]; \ +}\ + + +/// Vector sum +#define VEC_SUM_2(v21,v2,v1) \ +{ \ + (v21)[0] = (v2)[0] + (v1)[0]; \ + (v21)[1] = (v2)[1] + (v1)[1]; \ +}\ + + +/// Vector sum +#define VEC_SUM(v21,v2,v1) \ +{ \ + (v21)[0] = (v2)[0] + (v1)[0]; \ + (v21)[1] = (v2)[1] + (v1)[1]; \ + (v21)[2] = (v2)[2] + (v1)[2]; \ +}\ + + +/// Vector sum +#define VEC_SUM_4(v21,v2,v1) \ +{ \ + (v21)[0] = (v2)[0] + (v1)[0]; \ + (v21)[1] = (v2)[1] + (v1)[1]; \ + (v21)[2] = (v2)[2] + (v1)[2]; \ + (v21)[3] = (v2)[3] + (v1)[3]; \ +}\ + + +/// scalar times vector +#define VEC_SCALE_2(c,a,b) \ +{ \ + (c)[0] = (a)*(b)[0]; \ + (c)[1] = (a)*(b)[1]; \ +}\ + + +/// scalar times vector +#define VEC_SCALE(c,a,b) \ +{ \ + (c)[0] = (a)*(b)[0]; \ + (c)[1] = (a)*(b)[1]; \ + (c)[2] = (a)*(b)[2]; \ +}\ + + +/// scalar times vector +#define VEC_SCALE_4(c,a,b) \ +{ \ + (c)[0] = (a)*(b)[0]; \ + (c)[1] = (a)*(b)[1]; \ + (c)[2] = (a)*(b)[2]; \ + (c)[3] = (a)*(b)[3]; \ +}\ + + +/// accumulate scaled vector +#define VEC_ACCUM_2(c,a,b) \ +{ \ + (c)[0] += (a)*(b)[0]; \ + (c)[1] += (a)*(b)[1]; \ +}\ + + +/// accumulate scaled vector +#define VEC_ACCUM(c,a,b) \ +{ \ + (c)[0] += (a)*(b)[0]; \ + (c)[1] += (a)*(b)[1]; \ + (c)[2] += (a)*(b)[2]; \ +}\ + + +/// accumulate scaled vector +#define VEC_ACCUM_4(c,a,b) \ +{ \ + (c)[0] += (a)*(b)[0]; \ + (c)[1] += (a)*(b)[1]; \ + (c)[2] += (a)*(b)[2]; \ + (c)[3] += (a)*(b)[3]; \ +}\ + + +/// Vector dot product +#define VEC_DOT_2(a,b) ((a)[0]*(b)[0] + (a)[1]*(b)[1]) + + +/// Vector dot product +#define VEC_DOT(a,b) ((a)[0]*(b)[0] + (a)[1]*(b)[1] + (a)[2]*(b)[2]) + +/// Vector dot product +#define VEC_DOT_4(a,b) ((a)[0]*(b)[0] + (a)[1]*(b)[1] + (a)[2]*(b)[2] + (a)[3]*(b)[3]) + +/// vector impact parameter (squared) +#define VEC_IMPACT_SQ(bsq,direction,position) {\ + GREAL _llel_ = VEC_DOT(direction, position);\ + bsq = VEC_DOT(position, position) - _llel_*_llel_;\ +}\ + + +/// vector impact parameter +#define VEC_IMPACT(bsq,direction,position) {\ + VEC_IMPACT_SQ(bsq,direction,position); \ + GIM_SQRT(bsq,bsq); \ +}\ + +/// Vector length +#define VEC_LENGTH_2(a,l)\ +{\ + GREAL _pp = VEC_DOT_2(a,a);\ + GIM_SQRT(_pp,l);\ +}\ + + +/// Vector length +#define VEC_LENGTH(a,l)\ +{\ + GREAL _pp = VEC_DOT(a,a);\ + GIM_SQRT(_pp,l);\ +}\ + + +/// Vector length +#define VEC_LENGTH_4(a,l)\ +{\ + GREAL _pp = VEC_DOT_4(a,a);\ + GIM_SQRT(_pp,l);\ +}\ + +/// Vector inv length +#define VEC_INV_LENGTH_2(a,l)\ +{\ + GREAL _pp = VEC_DOT_2(a,a);\ + GIM_INV_SQRT(_pp,l);\ +}\ + + +/// Vector inv length +#define VEC_INV_LENGTH(a,l)\ +{\ + GREAL _pp = VEC_DOT(a,a);\ + GIM_INV_SQRT(_pp,l);\ +}\ + + +/// Vector inv length +#define VEC_INV_LENGTH_4(a,l)\ +{\ + GREAL _pp = VEC_DOT_4(a,a);\ + GIM_INV_SQRT(_pp,l);\ +}\ + + + +/// distance between two points +#define VEC_DISTANCE(_len,_va,_vb) {\ + vec3f _tmp_; \ + VEC_DIFF(_tmp_, _vb, _va); \ + VEC_LENGTH(_tmp_,_len); \ +}\ + + +/// Vector length +#define VEC_CONJUGATE_LENGTH(a,l)\ +{\ + GREAL _pp = 1.0 - a[0]*a[0] - a[1]*a[1] - a[2]*a[2];\ + GIM_SQRT(_pp,l);\ +}\ + + +/// Vector length +#define VEC_NORMALIZE(a) { \ + GREAL len;\ + VEC_INV_LENGTH(a,len); \ + if(lenA[1]?(A[0]>A[2]?0:2):(A[1]>A[2]?1:2);\ +}\ + +//! Finds the 2 smallest cartesian coordinates from a vector +#define VEC_MINOR_AXES(vec, i0, i1)\ +{\ + VEC_MAYOR_COORD(vec,i0);\ + i0 = (i0+1)%3;\ + i1 = (i0+1)%3;\ +}\ + + + + +#define VEC_EQUAL(v1,v2) (v1[0]==v2[0]&&v1[1]==v2[1]&&v1[2]==v2[2]) + +#define VEC_NEAR_EQUAL(v1,v2) (GIM_NEAR_EQUAL(v1[0],v2[0])&&GIM_NEAR_EQUAL(v1[1],v2[1])&&GIM_NEAR_EQUAL(v1[2],v2[2])) + + +/// Vector cross +#define X_AXIS_CROSS_VEC(dst,src)\ +{ \ + dst[0] = 0.0f; \ + dst[1] = -src[2]; \ + dst[2] = src[1]; \ +}\ + +#define Y_AXIS_CROSS_VEC(dst,src)\ +{ \ + dst[0] = src[2]; \ + dst[1] = 0.0f; \ + dst[2] = -src[0]; \ +}\ + +#define Z_AXIS_CROSS_VEC(dst,src)\ +{ \ + dst[0] = -src[1]; \ + dst[1] = src[0]; \ + dst[2] = 0.0f; \ +}\ + + + + + + +/// initialize matrix +#define IDENTIFY_MATRIX_3X3(m) \ +{ \ + m[0][0] = 1.0; \ + m[0][1] = 0.0; \ + m[0][2] = 0.0; \ + \ + m[1][0] = 0.0; \ + m[1][1] = 1.0; \ + m[1][2] = 0.0; \ + \ + m[2][0] = 0.0; \ + m[2][1] = 0.0; \ + m[2][2] = 1.0; \ +}\ + +/*! initialize matrix */ +#define IDENTIFY_MATRIX_4X4(m) \ +{ \ + m[0][0] = 1.0; \ + m[0][1] = 0.0; \ + m[0][2] = 0.0; \ + m[0][3] = 0.0; \ + \ + m[1][0] = 0.0; \ + m[1][1] = 1.0; \ + m[1][2] = 0.0; \ + m[1][3] = 0.0; \ + \ + m[2][0] = 0.0; \ + m[2][1] = 0.0; \ + m[2][2] = 1.0; \ + m[2][3] = 0.0; \ + \ + m[3][0] = 0.0; \ + m[3][1] = 0.0; \ + m[3][2] = 0.0; \ + m[3][3] = 1.0; \ +}\ + +/*! initialize matrix */ +#define ZERO_MATRIX_4X4(m) \ +{ \ + m[0][0] = 0.0; \ + m[0][1] = 0.0; \ + m[0][2] = 0.0; \ + m[0][3] = 0.0; \ + \ + m[1][0] = 0.0; \ + m[1][1] = 0.0; \ + m[1][2] = 0.0; \ + m[1][3] = 0.0; \ + \ + m[2][0] = 0.0; \ + m[2][1] = 0.0; \ + m[2][2] = 0.0; \ + m[2][3] = 0.0; \ + \ + m[3][0] = 0.0; \ + m[3][1] = 0.0; \ + m[3][2] = 0.0; \ + m[3][3] = 0.0; \ +}\ + +/*! matrix rotation X */ +#define ROTX_CS(m,cosine,sine) \ +{ \ + /* rotation about the x-axis */ \ + \ + m[0][0] = 1.0; \ + m[0][1] = 0.0; \ + m[0][2] = 0.0; \ + m[0][3] = 0.0; \ + \ + m[1][0] = 0.0; \ + m[1][1] = (cosine); \ + m[1][2] = (sine); \ + m[1][3] = 0.0; \ + \ + m[2][0] = 0.0; \ + m[2][1] = -(sine); \ + m[2][2] = (cosine); \ + m[2][3] = 0.0; \ + \ + m[3][0] = 0.0; \ + m[3][1] = 0.0; \ + m[3][2] = 0.0; \ + m[3][3] = 1.0; \ +}\ + +/*! matrix rotation Y */ +#define ROTY_CS(m,cosine,sine) \ +{ \ + /* rotation about the y-axis */ \ + \ + m[0][0] = (cosine); \ + m[0][1] = 0.0; \ + m[0][2] = -(sine); \ + m[0][3] = 0.0; \ + \ + m[1][0] = 0.0; \ + m[1][1] = 1.0; \ + m[1][2] = 0.0; \ + m[1][3] = 0.0; \ + \ + m[2][0] = (sine); \ + m[2][1] = 0.0; \ + m[2][2] = (cosine); \ + m[2][3] = 0.0; \ + \ + m[3][0] = 0.0; \ + m[3][1] = 0.0; \ + m[3][2] = 0.0; \ + m[3][3] = 1.0; \ +}\ + +/*! matrix rotation Z */ +#define ROTZ_CS(m,cosine,sine) \ +{ \ + /* rotation about the z-axis */ \ + \ + m[0][0] = (cosine); \ + m[0][1] = (sine); \ + m[0][2] = 0.0; \ + m[0][3] = 0.0; \ + \ + m[1][0] = -(sine); \ + m[1][1] = (cosine); \ + m[1][2] = 0.0; \ + m[1][3] = 0.0; \ + \ + m[2][0] = 0.0; \ + m[2][1] = 0.0; \ + m[2][2] = 1.0; \ + m[2][3] = 0.0; \ + \ + m[3][0] = 0.0; \ + m[3][1] = 0.0; \ + m[3][2] = 0.0; \ + m[3][3] = 1.0; \ +}\ + +/*! matrix copy */ +#define COPY_MATRIX_2X2(b,a) \ +{ \ + b[0][0] = a[0][0]; \ + b[0][1] = a[0][1]; \ + \ + b[1][0] = a[1][0]; \ + b[1][1] = a[1][1]; \ + \ +}\ + + +/*! matrix copy */ +#define COPY_MATRIX_2X3(b,a) \ +{ \ + b[0][0] = a[0][0]; \ + b[0][1] = a[0][1]; \ + b[0][2] = a[0][2]; \ + \ + b[1][0] = a[1][0]; \ + b[1][1] = a[1][1]; \ + b[1][2] = a[1][2]; \ +}\ + + +/*! matrix copy */ +#define COPY_MATRIX_3X3(b,a) \ +{ \ + b[0][0] = a[0][0]; \ + b[0][1] = a[0][1]; \ + b[0][2] = a[0][2]; \ + \ + b[1][0] = a[1][0]; \ + b[1][1] = a[1][1]; \ + b[1][2] = a[1][2]; \ + \ + b[2][0] = a[2][0]; \ + b[2][1] = a[2][1]; \ + b[2][2] = a[2][2]; \ +}\ + + +/*! matrix copy */ +#define COPY_MATRIX_4X4(b,a) \ +{ \ + b[0][0] = a[0][0]; \ + b[0][1] = a[0][1]; \ + b[0][2] = a[0][2]; \ + b[0][3] = a[0][3]; \ + \ + b[1][0] = a[1][0]; \ + b[1][1] = a[1][1]; \ + b[1][2] = a[1][2]; \ + b[1][3] = a[1][3]; \ + \ + b[2][0] = a[2][0]; \ + b[2][1] = a[2][1]; \ + b[2][2] = a[2][2]; \ + b[2][3] = a[2][3]; \ + \ + b[3][0] = a[3][0]; \ + b[3][1] = a[3][1]; \ + b[3][2] = a[3][2]; \ + b[3][3] = a[3][3]; \ +}\ + + +/*! matrix transpose */ +#define TRANSPOSE_MATRIX_2X2(b,a) \ +{ \ + b[0][0] = a[0][0]; \ + b[0][1] = a[1][0]; \ + \ + b[1][0] = a[0][1]; \ + b[1][1] = a[1][1]; \ +}\ + + +/*! matrix transpose */ +#define TRANSPOSE_MATRIX_3X3(b,a) \ +{ \ + b[0][0] = a[0][0]; \ + b[0][1] = a[1][0]; \ + b[0][2] = a[2][0]; \ + \ + b[1][0] = a[0][1]; \ + b[1][1] = a[1][1]; \ + b[1][2] = a[2][1]; \ + \ + b[2][0] = a[0][2]; \ + b[2][1] = a[1][2]; \ + b[2][2] = a[2][2]; \ +}\ + + +/*! matrix transpose */ +#define TRANSPOSE_MATRIX_4X4(b,a) \ +{ \ + b[0][0] = a[0][0]; \ + b[0][1] = a[1][0]; \ + b[0][2] = a[2][0]; \ + b[0][3] = a[3][0]; \ + \ + b[1][0] = a[0][1]; \ + b[1][1] = a[1][1]; \ + b[1][2] = a[2][1]; \ + b[1][3] = a[3][1]; \ + \ + b[2][0] = a[0][2]; \ + b[2][1] = a[1][2]; \ + b[2][2] = a[2][2]; \ + b[2][3] = a[3][2]; \ + \ + b[3][0] = a[0][3]; \ + b[3][1] = a[1][3]; \ + b[3][2] = a[2][3]; \ + b[3][3] = a[3][3]; \ +}\ + + +/*! multiply matrix by scalar */ +#define SCALE_MATRIX_2X2(b,s,a) \ +{ \ + b[0][0] = (s) * a[0][0]; \ + b[0][1] = (s) * a[0][1]; \ + \ + b[1][0] = (s) * a[1][0]; \ + b[1][1] = (s) * a[1][1]; \ +}\ + + +/*! multiply matrix by scalar */ +#define SCALE_MATRIX_3X3(b,s,a) \ +{ \ + b[0][0] = (s) * a[0][0]; \ + b[0][1] = (s) * a[0][1]; \ + b[0][2] = (s) * a[0][2]; \ + \ + b[1][0] = (s) * a[1][0]; \ + b[1][1] = (s) * a[1][1]; \ + b[1][2] = (s) * a[1][2]; \ + \ + b[2][0] = (s) * a[2][0]; \ + b[2][1] = (s) * a[2][1]; \ + b[2][2] = (s) * a[2][2]; \ +}\ + + +/*! multiply matrix by scalar */ +#define SCALE_MATRIX_4X4(b,s,a) \ +{ \ + b[0][0] = (s) * a[0][0]; \ + b[0][1] = (s) * a[0][1]; \ + b[0][2] = (s) * a[0][2]; \ + b[0][3] = (s) * a[0][3]; \ + \ + b[1][0] = (s) * a[1][0]; \ + b[1][1] = (s) * a[1][1]; \ + b[1][2] = (s) * a[1][2]; \ + b[1][3] = (s) * a[1][3]; \ + \ + b[2][0] = (s) * a[2][0]; \ + b[2][1] = (s) * a[2][1]; \ + b[2][2] = (s) * a[2][2]; \ + b[2][3] = (s) * a[2][3]; \ + \ + b[3][0] = s * a[3][0]; \ + b[3][1] = s * a[3][1]; \ + b[3][2] = s * a[3][2]; \ + b[3][3] = s * a[3][3]; \ +}\ + + +/*! multiply matrix by scalar */ +#define SCALE_VEC_MATRIX_2X2(b,svec,a) \ +{ \ + b[0][0] = svec[0] * a[0][0]; \ + b[1][0] = svec[0] * a[1][0]; \ + \ + b[0][1] = svec[1] * a[0][1]; \ + b[1][1] = svec[1] * a[1][1]; \ +}\ + + +/*! multiply matrix by scalar. Each columns is scaled by each scalar vector component */ +#define SCALE_VEC_MATRIX_3X3(b,svec,a) \ +{ \ + b[0][0] = svec[0] * a[0][0]; \ + b[1][0] = svec[0] * a[1][0]; \ + b[2][0] = svec[0] * a[2][0]; \ + \ + b[0][1] = svec[1] * a[0][1]; \ + b[1][1] = svec[1] * a[1][1]; \ + b[2][1] = svec[1] * a[2][1]; \ + \ + b[0][2] = svec[2] * a[0][2]; \ + b[1][2] = svec[2] * a[1][2]; \ + b[2][2] = svec[2] * a[2][2]; \ +}\ + + +/*! multiply matrix by scalar */ +#define SCALE_VEC_MATRIX_4X4(b,svec,a) \ +{ \ + b[0][0] = svec[0] * a[0][0]; \ + b[1][0] = svec[0] * a[1][0]; \ + b[2][0] = svec[0] * a[2][0]; \ + b[3][0] = svec[0] * a[3][0]; \ + \ + b[0][1] = svec[1] * a[0][1]; \ + b[1][1] = svec[1] * a[1][1]; \ + b[2][1] = svec[1] * a[2][1]; \ + b[3][1] = svec[1] * a[3][1]; \ + \ + b[0][2] = svec[2] * a[0][2]; \ + b[1][2] = svec[2] * a[1][2]; \ + b[2][2] = svec[2] * a[2][2]; \ + b[3][2] = svec[2] * a[3][2]; \ + \ + b[0][3] = svec[3] * a[0][3]; \ + b[1][3] = svec[3] * a[1][3]; \ + b[2][3] = svec[3] * a[2][3]; \ + b[3][3] = svec[3] * a[3][3]; \ +}\ + + +/*! multiply matrix by scalar */ +#define ACCUM_SCALE_MATRIX_2X2(b,s,a) \ +{ \ + b[0][0] += (s) * a[0][0]; \ + b[0][1] += (s) * a[0][1]; \ + \ + b[1][0] += (s) * a[1][0]; \ + b[1][1] += (s) * a[1][1]; \ +}\ + + +/*! multiply matrix by scalar */ +#define ACCUM_SCALE_MATRIX_3X3(b,s,a) \ +{ \ + b[0][0] += (s) * a[0][0]; \ + b[0][1] += (s) * a[0][1]; \ + b[0][2] += (s) * a[0][2]; \ + \ + b[1][0] += (s) * a[1][0]; \ + b[1][1] += (s) * a[1][1]; \ + b[1][2] += (s) * a[1][2]; \ + \ + b[2][0] += (s) * a[2][0]; \ + b[2][1] += (s) * a[2][1]; \ + b[2][2] += (s) * a[2][2]; \ +}\ + + +/*! multiply matrix by scalar */ +#define ACCUM_SCALE_MATRIX_4X4(b,s,a) \ +{ \ + b[0][0] += (s) * a[0][0]; \ + b[0][1] += (s) * a[0][1]; \ + b[0][2] += (s) * a[0][2]; \ + b[0][3] += (s) * a[0][3]; \ + \ + b[1][0] += (s) * a[1][0]; \ + b[1][1] += (s) * a[1][1]; \ + b[1][2] += (s) * a[1][2]; \ + b[1][3] += (s) * a[1][3]; \ + \ + b[2][0] += (s) * a[2][0]; \ + b[2][1] += (s) * a[2][1]; \ + b[2][2] += (s) * a[2][2]; \ + b[2][3] += (s) * a[2][3]; \ + \ + b[3][0] += (s) * a[3][0]; \ + b[3][1] += (s) * a[3][1]; \ + b[3][2] += (s) * a[3][2]; \ + b[3][3] += (s) * a[3][3]; \ +}\ + +/*! matrix product */ +/*! c[x][y] = a[x][0]*b[0][y]+a[x][1]*b[1][y]+a[x][2]*b[2][y]+a[x][3]*b[3][y];*/ +#define MATRIX_PRODUCT_2X2(c,a,b) \ +{ \ + c[0][0] = a[0][0]*b[0][0]+a[0][1]*b[1][0]; \ + c[0][1] = a[0][0]*b[0][1]+a[0][1]*b[1][1]; \ + \ + c[1][0] = a[1][0]*b[0][0]+a[1][1]*b[1][0]; \ + c[1][1] = a[1][0]*b[0][1]+a[1][1]*b[1][1]; \ + \ +}\ + +/*! matrix product */ +/*! c[x][y] = a[x][0]*b[0][y]+a[x][1]*b[1][y]+a[x][2]*b[2][y]+a[x][3]*b[3][y];*/ +#define MATRIX_PRODUCT_3X3(c,a,b) \ +{ \ + c[0][0] = a[0][0]*b[0][0]+a[0][1]*b[1][0]+a[0][2]*b[2][0]; \ + c[0][1] = a[0][0]*b[0][1]+a[0][1]*b[1][1]+a[0][2]*b[2][1]; \ + c[0][2] = a[0][0]*b[0][2]+a[0][1]*b[1][2]+a[0][2]*b[2][2]; \ + \ + c[1][0] = a[1][0]*b[0][0]+a[1][1]*b[1][0]+a[1][2]*b[2][0]; \ + c[1][1] = a[1][0]*b[0][1]+a[1][1]*b[1][1]+a[1][2]*b[2][1]; \ + c[1][2] = a[1][0]*b[0][2]+a[1][1]*b[1][2]+a[1][2]*b[2][2]; \ + \ + c[2][0] = a[2][0]*b[0][0]+a[2][1]*b[1][0]+a[2][2]*b[2][0]; \ + c[2][1] = a[2][0]*b[0][1]+a[2][1]*b[1][1]+a[2][2]*b[2][1]; \ + c[2][2] = a[2][0]*b[0][2]+a[2][1]*b[1][2]+a[2][2]*b[2][2]; \ +}\ + + +/*! matrix product */ +/*! c[x][y] = a[x][0]*b[0][y]+a[x][1]*b[1][y]+a[x][2]*b[2][y]+a[x][3]*b[3][y];*/ +#define MATRIX_PRODUCT_4X4(c,a,b) \ +{ \ + c[0][0] = a[0][0]*b[0][0]+a[0][1]*b[1][0]+a[0][2]*b[2][0]+a[0][3]*b[3][0];\ + c[0][1] = a[0][0]*b[0][1]+a[0][1]*b[1][1]+a[0][2]*b[2][1]+a[0][3]*b[3][1];\ + c[0][2] = a[0][0]*b[0][2]+a[0][1]*b[1][2]+a[0][2]*b[2][2]+a[0][3]*b[3][2];\ + c[0][3] = a[0][0]*b[0][3]+a[0][1]*b[1][3]+a[0][2]*b[2][3]+a[0][3]*b[3][3];\ + \ + c[1][0] = a[1][0]*b[0][0]+a[1][1]*b[1][0]+a[1][2]*b[2][0]+a[1][3]*b[3][0];\ + c[1][1] = a[1][0]*b[0][1]+a[1][1]*b[1][1]+a[1][2]*b[2][1]+a[1][3]*b[3][1];\ + c[1][2] = a[1][0]*b[0][2]+a[1][1]*b[1][2]+a[1][2]*b[2][2]+a[1][3]*b[3][2];\ + c[1][3] = a[1][0]*b[0][3]+a[1][1]*b[1][3]+a[1][2]*b[2][3]+a[1][3]*b[3][3];\ + \ + c[2][0] = a[2][0]*b[0][0]+a[2][1]*b[1][0]+a[2][2]*b[2][0]+a[2][3]*b[3][0];\ + c[2][1] = a[2][0]*b[0][1]+a[2][1]*b[1][1]+a[2][2]*b[2][1]+a[2][3]*b[3][1];\ + c[2][2] = a[2][0]*b[0][2]+a[2][1]*b[1][2]+a[2][2]*b[2][2]+a[2][3]*b[3][2];\ + c[2][3] = a[2][0]*b[0][3]+a[2][1]*b[1][3]+a[2][2]*b[2][3]+a[2][3]*b[3][3];\ + \ + c[3][0] = a[3][0]*b[0][0]+a[3][1]*b[1][0]+a[3][2]*b[2][0]+a[3][3]*b[3][0];\ + c[3][1] = a[3][0]*b[0][1]+a[3][1]*b[1][1]+a[3][2]*b[2][1]+a[3][3]*b[3][1];\ + c[3][2] = a[3][0]*b[0][2]+a[3][1]*b[1][2]+a[3][2]*b[2][2]+a[3][3]*b[3][2];\ + c[3][3] = a[3][0]*b[0][3]+a[3][1]*b[1][3]+a[3][2]*b[2][3]+a[3][3]*b[3][3];\ +}\ + + +/*! matrix times vector */ +#define MAT_DOT_VEC_2X2(p,m,v) \ +{ \ + p[0] = m[0][0]*v[0] + m[0][1]*v[1]; \ + p[1] = m[1][0]*v[0] + m[1][1]*v[1]; \ +}\ + + +/*! matrix times vector */ +#define MAT_DOT_VEC_3X3(p,m,v) \ +{ \ + p[0] = m[0][0]*v[0] + m[0][1]*v[1] + m[0][2]*v[2]; \ + p[1] = m[1][0]*v[0] + m[1][1]*v[1] + m[1][2]*v[2]; \ + p[2] = m[2][0]*v[0] + m[2][1]*v[1] + m[2][2]*v[2]; \ +}\ + + +/*! matrix times vector +v is a vec4f +*/ +#define MAT_DOT_VEC_4X4(p,m,v) \ +{ \ + p[0] = m[0][0]*v[0] + m[0][1]*v[1] + m[0][2]*v[2] + m[0][3]*v[3]; \ + p[1] = m[1][0]*v[0] + m[1][1]*v[1] + m[1][2]*v[2] + m[1][3]*v[3]; \ + p[2] = m[2][0]*v[0] + m[2][1]*v[1] + m[2][2]*v[2] + m[2][3]*v[3]; \ + p[3] = m[3][0]*v[0] + m[3][1]*v[1] + m[3][2]*v[2] + m[3][3]*v[3]; \ +}\ + +/*! matrix times vector +v is a vec3f +and m is a mat4f
    +Last column is added as the position +*/ +#define MAT_DOT_VEC_3X4(p,m,v) \ +{ \ + p[0] = m[0][0]*v[0] + m[0][1]*v[1] + m[0][2]*v[2] + m[0][3]; \ + p[1] = m[1][0]*v[0] + m[1][1]*v[1] + m[1][2]*v[2] + m[1][3]; \ + p[2] = m[2][0]*v[0] + m[2][1]*v[1] + m[2][2]*v[2] + m[2][3]; \ +}\ + + +/*! vector transpose times matrix */ +/*! p[j] = v[0]*m[0][j] + v[1]*m[1][j] + v[2]*m[2][j]; */ +#define VEC_DOT_MAT_3X3(p,v,m) \ +{ \ + p[0] = v[0]*m[0][0] + v[1]*m[1][0] + v[2]*m[2][0]; \ + p[1] = v[0]*m[0][1] + v[1]*m[1][1] + v[2]*m[2][1]; \ + p[2] = v[0]*m[0][2] + v[1]*m[1][2] + v[2]*m[2][2]; \ +}\ + + +/*! affine matrix times vector */ +/** The matrix is assumed to be an affine matrix, with last two + * entries representing a translation */ +#define MAT_DOT_VEC_2X3(p,m,v) \ +{ \ + p[0] = m[0][0]*v[0] + m[0][1]*v[1] + m[0][2]; \ + p[1] = m[1][0]*v[0] + m[1][1]*v[1] + m[1][2]; \ +}\ + +//! Transform a plane +#define MAT_TRANSFORM_PLANE_4X4(pout,m,plane)\ +{ \ + pout[0] = m[0][0]*plane[0] + m[0][1]*plane[1] + m[0][2]*plane[2];\ + pout[1] = m[1][0]*plane[0] + m[1][1]*plane[1] + m[1][2]*plane[2];\ + pout[2] = m[2][0]*plane[0] + m[2][1]*plane[1] + m[2][2]*plane[2];\ + pout[3] = m[0][3]*pout[0] + m[1][3]*pout[1] + m[2][3]*pout[2] + plane[3];\ +}\ + + + +/** inverse transpose of matrix times vector + * + * This macro computes inverse transpose of matrix m, + * and multiplies vector v into it, to yeild vector p + * + * DANGER !!! Do Not use this on normal vectors!!! + * It will leave normals the wrong length !!! + * See macro below for use on normals. + */ +#define INV_TRANSP_MAT_DOT_VEC_2X2(p,m,v) \ +{ \ + GREAL det; \ + \ + det = m[0][0]*m[1][1] - m[0][1]*m[1][0]; \ + p[0] = m[1][1]*v[0] - m[1][0]*v[1]; \ + p[1] = - m[0][1]*v[0] + m[0][0]*v[1]; \ + \ + /* if matrix not singular, and not orthonormal, then renormalize */ \ + if ((det!=1.0f) && (det != 0.0f)) { \ + det = 1.0f / det; \ + p[0] *= det; \ + p[1] *= det; \ + } \ +}\ + + +/** transform normal vector by inverse transpose of matrix + * and then renormalize the vector + * + * This macro computes inverse transpose of matrix m, + * and multiplies vector v into it, to yeild vector p + * Vector p is then normalized. + */ +#define NORM_XFORM_2X2(p,m,v) \ +{ \ + GREAL len; \ + \ + /* do nothing if off-diagonals are zero and diagonals are \ + * equal */ \ + if ((m[0][1] != 0.0) || (m[1][0] != 0.0) || (m[0][0] != m[1][1])) { \ + p[0] = m[1][1]*v[0] - m[1][0]*v[1]; \ + p[1] = - m[0][1]*v[0] + m[0][0]*v[1]; \ + \ + len = p[0]*p[0] + p[1]*p[1]; \ + GIM_INV_SQRT(len,len); \ + p[0] *= len; \ + p[1] *= len; \ + } else { \ + VEC_COPY_2 (p, v); \ + } \ +}\ + + +/** outer product of vector times vector transpose + * + * The outer product of vector v and vector transpose t yeilds + * dyadic matrix m. + */ +#define OUTER_PRODUCT_2X2(m,v,t) \ +{ \ + m[0][0] = v[0] * t[0]; \ + m[0][1] = v[0] * t[1]; \ + \ + m[1][0] = v[1] * t[0]; \ + m[1][1] = v[1] * t[1]; \ +}\ + + +/** outer product of vector times vector transpose + * + * The outer product of vector v and vector transpose t yeilds + * dyadic matrix m. + */ +#define OUTER_PRODUCT_3X3(m,v,t) \ +{ \ + m[0][0] = v[0] * t[0]; \ + m[0][1] = v[0] * t[1]; \ + m[0][2] = v[0] * t[2]; \ + \ + m[1][0] = v[1] * t[0]; \ + m[1][1] = v[1] * t[1]; \ + m[1][2] = v[1] * t[2]; \ + \ + m[2][0] = v[2] * t[0]; \ + m[2][1] = v[2] * t[1]; \ + m[2][2] = v[2] * t[2]; \ +}\ + + +/** outer product of vector times vector transpose + * + * The outer product of vector v and vector transpose t yeilds + * dyadic matrix m. + */ +#define OUTER_PRODUCT_4X4(m,v,t) \ +{ \ + m[0][0] = v[0] * t[0]; \ + m[0][1] = v[0] * t[1]; \ + m[0][2] = v[0] * t[2]; \ + m[0][3] = v[0] * t[3]; \ + \ + m[1][0] = v[1] * t[0]; \ + m[1][1] = v[1] * t[1]; \ + m[1][2] = v[1] * t[2]; \ + m[1][3] = v[1] * t[3]; \ + \ + m[2][0] = v[2] * t[0]; \ + m[2][1] = v[2] * t[1]; \ + m[2][2] = v[2] * t[2]; \ + m[2][3] = v[2] * t[3]; \ + \ + m[3][0] = v[3] * t[0]; \ + m[3][1] = v[3] * t[1]; \ + m[3][2] = v[3] * t[2]; \ + m[3][3] = v[3] * t[3]; \ +}\ + + +/** outer product of vector times vector transpose + * + * The outer product of vector v and vector transpose t yeilds + * dyadic matrix m. + */ +#define ACCUM_OUTER_PRODUCT_2X2(m,v,t) \ +{ \ + m[0][0] += v[0] * t[0]; \ + m[0][1] += v[0] * t[1]; \ + \ + m[1][0] += v[1] * t[0]; \ + m[1][1] += v[1] * t[1]; \ +}\ + + +/** outer product of vector times vector transpose + * + * The outer product of vector v and vector transpose t yeilds + * dyadic matrix m. + */ +#define ACCUM_OUTER_PRODUCT_3X3(m,v,t) \ +{ \ + m[0][0] += v[0] * t[0]; \ + m[0][1] += v[0] * t[1]; \ + m[0][2] += v[0] * t[2]; \ + \ + m[1][0] += v[1] * t[0]; \ + m[1][1] += v[1] * t[1]; \ + m[1][2] += v[1] * t[2]; \ + \ + m[2][0] += v[2] * t[0]; \ + m[2][1] += v[2] * t[1]; \ + m[2][2] += v[2] * t[2]; \ +}\ + + +/** outer product of vector times vector transpose + * + * The outer product of vector v and vector transpose t yeilds + * dyadic matrix m. + */ +#define ACCUM_OUTER_PRODUCT_4X4(m,v,t) \ +{ \ + m[0][0] += v[0] * t[0]; \ + m[0][1] += v[0] * t[1]; \ + m[0][2] += v[0] * t[2]; \ + m[0][3] += v[0] * t[3]; \ + \ + m[1][0] += v[1] * t[0]; \ + m[1][1] += v[1] * t[1]; \ + m[1][2] += v[1] * t[2]; \ + m[1][3] += v[1] * t[3]; \ + \ + m[2][0] += v[2] * t[0]; \ + m[2][1] += v[2] * t[1]; \ + m[2][2] += v[2] * t[2]; \ + m[2][3] += v[2] * t[3]; \ + \ + m[3][0] += v[3] * t[0]; \ + m[3][1] += v[3] * t[1]; \ + m[3][2] += v[3] * t[2]; \ + m[3][3] += v[3] * t[3]; \ +}\ + + +/** determinant of matrix + * + * Computes determinant of matrix m, returning d + */ +#define DETERMINANT_2X2(d,m) \ +{ \ + d = m[0][0] * m[1][1] - m[0][1] * m[1][0]; \ +}\ + + +/** determinant of matrix + * + * Computes determinant of matrix m, returning d + */ +#define DETERMINANT_3X3(d,m) \ +{ \ + d = m[0][0] * (m[1][1]*m[2][2] - m[1][2] * m[2][1]); \ + d -= m[0][1] * (m[1][0]*m[2][2] - m[1][2] * m[2][0]); \ + d += m[0][2] * (m[1][0]*m[2][1] - m[1][1] * m[2][0]); \ +}\ + + +/** i,j,th cofactor of a 4x4 matrix + * + */ +#define COFACTOR_4X4_IJ(fac,m,i,j) \ +{ \ + GUINT __ii[4], __jj[4], __k; \ + \ + for (__k=0; __k +*/ +#define INV_MAT_DOT_VEC_3X3(p,m,v) \ +{ \ + p[0] = MAT_DOT_COL(m,v,0); \ + p[1] = MAT_DOT_COL(m,v,1); \ + p[2] = MAT_DOT_COL(m,v,2); \ +}\ + + + +#endif // GIM_VECTOR_H_INCLUDED diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_math.h b/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_math.h new file mode 100644 index 000000000..8b9e6806e --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_math.h @@ -0,0 +1,157 @@ +#ifndef GIM_MATH_H_INCLUDED +#define GIM_MATH_H_INCLUDED +/*! \file gim_math.h +\author Francisco Len Nßjera +*/ +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon Najera. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + (3) The zlib/libpng license that is included with this library in + the file GIMPACT-LICENSE-ZLIB.TXT. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files + GIMPACT-LICENSE-LGPL.TXT, GIMPACT-LICENSE-ZLIB.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + +#include "LinearMath/btScalar.h" + + + +#define GREAL btScalar +#define GREAL2 double +#define GINT int +#define GUINT unsigned int +#define GSHORT short +#define GUSHORT unsigned short +#define GINT64 long long +#define GUINT64 unsigned long long + + + +#define G_PI 3.14159265358979f +#define G_HALF_PI 1.5707963f +//267948966 +#define G_TWO_PI 6.28318530f +//71795864 +#define G_ROOT3 1.73205f +#define G_ROOT2 1.41421f +#define G_UINT_INFINITY 0xffffffff //!< A very very high value +#define G_REAL_INFINITY FLT_MAX +#define G_SIGN_BITMASK 0x80000000 +#define G_EPSILON SIMD_EPSILON + + + +enum GIM_SCALAR_TYPES +{ + G_STYPE_REAL =0, + G_STYPE_REAL2, + G_STYPE_SHORT, + G_STYPE_USHORT, + G_STYPE_INT, + G_STYPE_UINT, + G_STYPE_INT64, + G_STYPE_UINT64 +}; + + + +#define G_DEGTORAD(X) ((X)*3.1415926f/180.0f) +#define G_RADTODEG(X) ((X)*180.0f/3.1415926f) + +//! Integer representation of a floating-point value. +#define GIM_IR(x) ((GUINT&)(x)) + +//! Signed integer representation of a floating-point value. +#define GIM_SIR(x) ((GINT&)(x)) + +//! Absolute integer representation of a floating-point value +#define GIM_AIR(x) (GIM_IR(x)&0x7fffffff) + +//! Floating-point representation of an integer value. +#define GIM_FR(x) ((GREAL&)(x)) + +#define GIM_MAX(a,b) (ab?b:a) + +#define GIM_MAX3(a,b,c) GIM_MAX(a,GIM_MAX(b,c)) +#define GIM_MIN3(a,b,c) GIM_MIN(a,GIM_MIN(b,c)) + +#define GIM_IS_ZERO(value) (value < G_EPSILON && value > -G_EPSILON) + +#define GIM_IS_NEGATIVE(value) (value <= -G_EPSILON) + +#define GIM_IS_POSISITVE(value) (value >= G_EPSILON) + +#define GIM_NEAR_EQUAL(v1,v2) GIM_IS_ZERO((v1-v2)) + +///returns a clamped number +#define GIM_CLAMP(number,minval,maxval) (numbermaxval?maxval:number)) + +#define GIM_GREATER(x, y) btFabs(x) > (y) + +///Swap numbers +#define GIM_SWAP_NUMBERS(a,b){ \ + a = a+b; \ + b = a-b; \ + a = a-b; \ +}\ + +#define GIM_INV_SQRT(va,isva)\ +{\ + if(va<=0.0000001f)\ + {\ + isva = G_REAL_INFINITY;\ + }\ + else\ + {\ + GREAL _x = va * 0.5f;\ + GUINT _y = 0x5f3759df - ( GIM_IR(va) >> 1);\ + isva = GIM_FR(_y);\ + isva = isva * ( 1.5f - ( _x * isva * isva ) );\ + }\ +}\ + +#define GIM_SQRT(va,sva)\ +{\ + GIM_INV_SQRT(va,sva);\ + sva = 1.0f/sva;\ +}\ + +//! Computes 1.0f / sqrtf(x). Comes from Quake3. See http://www.magic-software.com/3DGEDInvSqrt.html +inline GREAL gim_inv_sqrt(GREAL f) +{ + GREAL r; + GIM_INV_SQRT(f,r); + return r; +} + +inline GREAL gim_sqrt(GREAL f) +{ + GREAL r; + GIM_SQRT(f,r); + return r; +} + + + +#endif // GIM_MATH_H_INCLUDED diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_memory.cpp b/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_memory.cpp new file mode 100644 index 000000000..1636eb786 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_memory.cpp @@ -0,0 +1,135 @@ +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon Najera. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + (3) The zlib/libpng license that is included with this library in + the file GIMPACT-LICENSE-ZLIB.TXT. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files + GIMPACT-LICENSE-LGPL.TXT, GIMPACT-LICENSE-ZLIB.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + + +#include "gim_memory.h" +#include "stdlib.h" + +#ifdef GIM_SIMD_MEMORY +#include "LinearMath/btAlignedAllocator.h" +#endif + +static gim_alloc_function *g_allocfn = 0; +static gim_alloca_function *g_allocafn = 0; +static gim_realloc_function *g_reallocfn = 0; +static gim_free_function *g_freefn = 0; + +void gim_set_alloc_handler (gim_alloc_function *fn) +{ + g_allocfn = fn; +} + +void gim_set_alloca_handler (gim_alloca_function *fn) +{ + g_allocafn = fn; +} + +void gim_set_realloc_handler (gim_realloc_function *fn) +{ + g_reallocfn = fn; +} + +void gim_set_free_handler (gim_free_function *fn) +{ + g_freefn = fn; +} + +gim_alloc_function *gim_get_alloc_handler() +{ + return g_allocfn; +} + +gim_alloca_function *gim_get_alloca_handler() +{ + return g_allocafn; +} + + +gim_realloc_function *gim_get_realloc_handler () +{ + return g_reallocfn; +} + + +gim_free_function *gim_get_free_handler () +{ + return g_freefn; +} + + +void * gim_alloc(size_t size) +{ + void * ptr; + if (g_allocfn) + { + ptr = g_allocfn(size); + } + else + { +#ifdef GIM_SIMD_MEMORY + ptr = btAlignedAlloc(size,16); +#else + ptr = malloc(size); +#endif + } + return ptr; +} + +void * gim_alloca(size_t size) +{ + if (g_allocafn) return g_allocafn(size); else return gim_alloc(size); +} + + +void * gim_realloc(void *ptr, size_t oldsize, size_t newsize) +{ + void * newptr = gim_alloc(newsize); + size_t copysize = oldsize + +#ifdef PREFETCH +#include // for prefetch +#define pfval 64 +#define pfval2 128 +//! Prefetch 64 +#define pf(_x,_i) _mm_prefetch((void *)(_x + _i + pfval), 0) +//! Prefetch 128 +#define pf2(_x,_i) _mm_prefetch((void *)(_x + _i + pfval2), 0) +#else +//! Prefetch 64 +#define pf(_x,_i) +//! Prefetch 128 +#define pf2(_x,_i) +#endif + + +///Functions for manip packed arrays of numbers +#define GIM_COPY_ARRAYS(dest_array,source_array,element_count)\ +{\ + for (GUINT _i_=0;_i_=SIMD_T_SIZE) + { + *(ui_dst_ptr++) = *(ui_src_ptr++); + copysize-=SIMD_T_SIZE; + } + if(copysize==0) return; +*/ + + char * c_src_ptr = (char *)src; + char * c_dst_ptr = (char *)dst; + while(copysize>0) + { + *(c_dst_ptr++) = *(c_src_ptr++); + copysize--; + } + return; +#else + memcpy(dst,src,copysize); +#endif +} + + + +template +inline void gim_swap_elements(T* _array,size_t _i,size_t _j) +{ + T _e_tmp_ = _array[_i]; + _array[_i] = _array[_j]; + _array[_j] = _e_tmp_; +} + + +template +inline void gim_swap_elements_memcpy(T* _array,size_t _i,size_t _j) +{ + char _e_tmp_[sizeof(T)]; + gim_simd_memcpy(_e_tmp_,&_array[_i],sizeof(T)); + gim_simd_memcpy(&_array[_i],&_array[_j],sizeof(T)); + gim_simd_memcpy(&_array[_j],_e_tmp_,sizeof(T)); +} + +template +inline void gim_swap_elements_ptr(char * _array,size_t _i,size_t _j) +{ + char _e_tmp_[SIZE]; + _i*=SIZE; + _j*=SIZE; + gim_simd_memcpy(_e_tmp_,_array+_i,SIZE); + gim_simd_memcpy(_array+_i,_array+_j,SIZE); + gim_simd_memcpy(_array+_j,_e_tmp_,SIZE); +} + +#endif // GIM_MEMORY_H_INCLUDED diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_radixsort.h b/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_radixsort.h new file mode 100644 index 000000000..f7dadbbca --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_radixsort.h @@ -0,0 +1,406 @@ +#ifndef GIM_RADIXSORT_H_INCLUDED +#define GIM_RADIXSORT_H_INCLUDED +/*! \file gim_radixsort.h +\author Francisco Len Nßjera. +Based on the work of Michael Herf : "fast floating-point radix sort" +Avaliable on http://www.stereopsis.com/radix.html +*/ +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon Najera. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + (3) The zlib/libpng license that is included with this library in + the file GIMPACT-LICENSE-ZLIB.TXT. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files + GIMPACT-LICENSE-LGPL.TXT, GIMPACT-LICENSE-ZLIB.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + +#include "gim_memory.h" + +///Macros for sorting. +//! Prototype for comparators +class less_comparator +{ + public: + + template + inline int operator() ( const T& a, const Z& b ) + { + return ( ab?1:0)); + } +}; + +//! Prototype for comparators +class integer_comparator +{ + public: + + template + inline int operator() ( const T& a, const T& b ) + { + return (int)(a-b); + } +}; + +//!Prototype for getting the integer representation of an object +class uint_key_func +{ +public: + template + inline GUINT operator()( const T& a) + { + return (GUINT)a; + } +}; + + +//!Prototype for copying elements +class copy_elements_func +{ +public: + template + inline void operator()(T& a,T& b) + { + a = b; + } +}; + +//!Prototype for copying elements +class memcopy_elements_func +{ +public: + template + inline void operator()(T& a,T& b) + { + gim_simd_memcpy(&a,&b,sizeof(T)); + } +}; + + +//! @{ +struct GIM_RSORT_TOKEN +{ + GUINT m_key; + GUINT m_value; + GIM_RSORT_TOKEN() + { + } + GIM_RSORT_TOKEN(const GIM_RSORT_TOKEN& rtoken) + { + m_key = rtoken.m_key; + m_value = rtoken.m_value; + } + + inline bool operator <(const GIM_RSORT_TOKEN& other) const + { + return (m_key < other.m_key); + } + + inline bool operator >(const GIM_RSORT_TOKEN& other) const + { + return (m_key > other.m_key); + } +}; + +//! Prototype for comparators +class GIM_RSORT_TOKEN_COMPARATOR +{ + public: + + inline int operator()( const GIM_RSORT_TOKEN& a, const GIM_RSORT_TOKEN& b ) + { + return (int)((a.m_key) - (b.m_key)); + } +}; + + + +#define kHist 2048 +// ---- utils for accessing 11-bit quantities +#define D11_0(x) (x & 0x7FF) +#define D11_1(x) (x >> 11 & 0x7FF) +#define D11_2(x) (x >> 22 ) + + + +///Radix sort for unsigned integer keys +inline void gim_radix_sort_rtokens( + GIM_RSORT_TOKEN * array, + GIM_RSORT_TOKEN * sorted, GUINT element_count) +{ + GUINT i; + GUINT b0[kHist * 3]; + GUINT *b1 = b0 + kHist; + GUINT *b2 = b1 + kHist; + for (i = 0; i < kHist * 3; ++i) + { + b0[i] = 0; + } + GUINT fi; + GUINT pos; + for (i = 0; i < element_count; ++i) + { + fi = array[i].m_key; + b0[D11_0(fi)] ++; + b1[D11_1(fi)] ++; + b2[D11_2(fi)] ++; + } + { + GUINT sum0 = 0, sum1 = 0, sum2 = 0; + GUINT tsum; + for (i = 0; i < kHist; ++i) + { + tsum = b0[i] + sum0; + b0[i] = sum0 - 1; + sum0 = tsum; + tsum = b1[i] + sum1; + b1[i] = sum1 - 1; + sum1 = tsum; + tsum = b2[i] + sum2; + b2[i] = sum2 - 1; + sum2 = tsum; + } + } + for (i = 0; i < element_count; ++i) + { + fi = array[i].m_key; + pos = D11_0(fi); + pos = ++b0[pos]; + sorted[pos].m_key = array[i].m_key; + sorted[pos].m_value = array[i].m_value; + } + for (i = 0; i < element_count; ++i) + { + fi = sorted[i].m_key; + pos = D11_1(fi); + pos = ++b1[pos]; + array[pos].m_key = sorted[i].m_key; + array[pos].m_value = sorted[i].m_value; + } + for (i = 0; i < element_count; ++i) + { + fi = array[i].m_key; + pos = D11_2(fi); + pos = ++b2[pos]; + sorted[pos].m_key = array[i].m_key; + sorted[pos].m_value = array[i].m_value; + } +} + + + + +/// Get the sorted tokens from an array. For generic use. Tokens are IRR_RSORT_TOKEN +/*! +*\param array Array of elements to sort +*\param sorted_tokens Tokens of sorted elements +*\param element_count element count +*\param uintkey_macro Functor which retrieves the integer representation of an array element +*/ +template +void gim_radix_sort_array_tokens( + T* array , + GIM_RSORT_TOKEN * sorted_tokens, + GUINT element_count,GETKEY_CLASS uintkey_macro) +{ + GIM_RSORT_TOKEN * _unsorted = (GIM_RSORT_TOKEN *) gim_alloc(sizeof(GIM_RSORT_TOKEN)*element_count); + for (GUINT _i=0;_i +void gim_radix_sort( + T * array, GUINT element_count, + GETKEY_CLASS get_uintkey_macro, COPY_CLASS copy_elements_macro) +{ + GIM_RSORT_TOKEN * _sorted = (GIM_RSORT_TOKEN *) gim_alloc(sizeof(GIM_RSORT_TOKEN)*element_count); + gim_radix_sort_array_tokens(array,_sorted,element_count,get_uintkey_macro); + T * _original_array = (T *) gim_alloc(sizeof(T)*element_count); + gim_simd_memcpy(_original_array,array,sizeof(T)*element_count); + for (GUINT _i=0;_i +bool gim_binary_search_ex( + const T* _array, GUINT _start_i, + GUINT _end_i,GUINT & _result_index, + const KEYCLASS & _search_key, + COMP_CLASS _comp_macro) +{ + GUINT _k; + int _comp_result; + GUINT _i = _start_i; + GUINT _j = _end_i+1; + while (_i < _j) + { + _k = (_j+_i-1)/2; + _comp_result = _comp_macro(_array[_k], _search_key); + if (_comp_result == 0) + { + _result_index = _k; + return true; + } + else if (_comp_result < 0) + { + _i = _k+1; + } + else + { + _j = _k; + } + } + _result_index = _i; + return false; +} + + + +//! Failsafe Iterative binary search,Template version +/*! +If the element is not found, it returns the nearest upper element position, may be the further position after the last element. +\param _array +\param _start_i the beginning of the array +\param _end_i the ending index of the array +\param _search_key Value to find +\param _result_index the index of the found element, or if not found then it will get the index of the closest bigger value +\return true if found, else false +*/ +template +bool gim_binary_search( + const T*_array,GUINT _start_i, + GUINT _end_i,const T & _search_key, + GUINT & _result_index) +{ + GUINT _i = _start_i; + GUINT _j = _end_i+1; + GUINT _k; + while(_i < _j) + { + _k = (_j+_i-1)/2; + if(_array[_k]==_search_key) + { + _result_index = _k; + return true; + } + else if (_array[_k]<_search_key) + { + _i = _k+1; + } + else + { + _j = _k; + } + } + _result_index = _i; + return false; +} + + + +///heap sort from http://www.csse.monash.edu.au/~lloyd/tildeAlgDS/Sort/Heap/ +template +void gim_down_heap(T *pArr, GUINT k, GUINT n,COMP_CLASS CompareFunc) +{ + /* PRE: a[k+1..N] is a heap */ + /* POST: a[k..N] is a heap */ + + T temp = pArr[k - 1]; + /* k has child(s) */ + while (k <= n/2) + { + int child = 2*k; + + if ((child < (int)n) && CompareFunc(pArr[child - 1] , pArr[child])<0) + { + child++; + } + /* pick larger child */ + if (CompareFunc(temp , pArr[child - 1])<0) + { + /* move child up */ + pArr[k - 1] = pArr[child - 1]; + k = child; + } + else + { + break; + } + } + pArr[k - 1] = temp; +} /*downHeap*/ + + +template +void gim_heap_sort(T *pArr, GUINT element_count, COMP_CLASS CompareFunc) +{ + /* sort a[0..N-1], N.B. 0 to N-1 */ + GUINT k; + GUINT n = element_count; + for (k = n/2; k > 0; k--) + { + gim_down_heap(pArr, k, n, CompareFunc); + } + + /* a[1..N] is now a heap */ + while ( n>=2 ) + { + gim_swap_elements(pArr,0,n-1); /* largest of a[0..n-1] */ + --n; + /* restore a[1..i-1] heap */ + gim_down_heap(pArr, 1, n, CompareFunc); + } +} + + + + +#endif // GIM_RADIXSORT_H_INCLUDED diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_tri_collision.cpp b/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_tri_collision.cpp new file mode 100644 index 000000000..74d734146 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/gim_tri_collision.cpp @@ -0,0 +1,640 @@ + +/*! \file gim_tri_collision.h +\author Francisco Len Nßjera +*/ +/* +----------------------------------------------------------------------------- +This source file is part of GIMPACT Library. + +For the latest info, see http://gimpact.sourceforge.net/ + +Copyright (c) 2006 Francisco Leon Najera. C.C. 80087371. +email: projectileman@yahoo.com + + This library is free software; you can redistribute it and/or + modify it under the terms of EITHER: + (1) The GNU Lesser General Public License as published by the Free + Software Foundation; either version 2.1 of the License, or (at + your option) any later version. The text of the GNU Lesser + General Public License is included with this library in the + file GIMPACT-LICENSE-LGPL.TXT. + (2) The BSD-style license that is included with this library in + the file GIMPACT-LICENSE-BSD.TXT. + (3) The zlib/libpng license that is included with this library in + the file GIMPACT-LICENSE-ZLIB.TXT. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files + GIMPACT-LICENSE-LGPL.TXT, GIMPACT-LICENSE-ZLIB.TXT and GIMPACT-LICENSE-BSD.TXT for more details. + +----------------------------------------------------------------------------- +*/ + +#include "gim_tri_collision.h" + + +#define TRI_LOCAL_EPSILON 0.000001f +#define MIN_EDGE_EDGE_DIS 0.00001f + + +class GIM_TRIANGLE_CALCULATION_CACHE +{ +public: + GREAL margin; + btVector3 tu_vertices[3]; + btVector3 tv_vertices[3]; + btVector4 tu_plane; + btVector4 tv_plane; + btVector3 closest_point_u; + btVector3 closest_point_v; + btVector3 edge_edge_dir; + btVector3 distances; + GREAL du[4]; + GREAL du0du1; + GREAL du0du2; + GREAL dv[4]; + GREAL dv0dv1; + GREAL dv0dv2; + btVector3 temp_points[MAX_TRI_CLIPPING]; + btVector3 temp_points1[MAX_TRI_CLIPPING]; + btVector3 contact_points[MAX_TRI_CLIPPING]; + + + + //! if returns false, the faces are paralele + SIMD_FORCE_INLINE bool compute_intervals( + const GREAL &D0, + const GREAL &D1, + const GREAL &D2, + const GREAL &D0D1, + const GREAL &D0D2, + GREAL & scale_edge0, + GREAL & scale_edge1, + GUINT &edge_index0, + GUINT &edge_index1) + { + if(D0D1>0.0f) + { + /* here we know that D0D2<=0.0 */ + /* that is D0, D1 are on the same side, D2 on the other or on the plane */ + scale_edge0 = -D2/(D0-D2); + scale_edge1 = -D1/(D2-D1); + edge_index0 = 2;edge_index1 = 1; + } + else if(D0D2>0.0f) + { + /* here we know that d0d1<=0.0 */ + scale_edge0 = -D0/(D1-D0); + scale_edge1 = -D1/(D2-D1); + edge_index0 = 0;edge_index1 = 1; + } + else if(D1*D2>0.0f || D0!=0.0f) + { + /* here we know that d0d1<=0.0 or that D0!=0.0 */ + scale_edge0 = -D0/(D1-D0); + scale_edge1 = -D2/(D0-D2); + edge_index0 = 0 ;edge_index1 = 2; + } + else + { + return false; + } + return true; + } + + + //! clip triangle + /*! + */ + SIMD_FORCE_INLINE GUINT clip_triangle( + const btVector4 & tri_plane, + const btVector3 * tripoints, + const btVector3 * srcpoints, + btVector3 * clip_points) + { + // edge 0 + + btVector4 edgeplane; + + EDGE_PLANE(tripoints[0],tripoints[1],tri_plane,edgeplane); + + GUINT clipped_count = PLANE_CLIP_TRIANGLE3D( + edgeplane,srcpoints[0],srcpoints[1],srcpoints[2],temp_points); + + if(clipped_count == 0) return 0; + + // edge 1 + + EDGE_PLANE(tripoints[1],tripoints[2],tri_plane,edgeplane); + + clipped_count = PLANE_CLIP_POLYGON3D( + edgeplane,temp_points,clipped_count,temp_points1); + + if(clipped_count == 0) return 0; + + // edge 2 + + EDGE_PLANE(tripoints[2],tripoints[0],tri_plane,edgeplane); + + clipped_count = PLANE_CLIP_POLYGON3D( + edgeplane,temp_points1,clipped_count,clip_points); + + return clipped_count; + + + /*GUINT i0 = (tri_plane.closestAxis()+1)%3; + GUINT i1 = (i0+1)%3; + // edge 0 + btVector3 temp_points[MAX_TRI_CLIPPING]; + btVector3 temp_points1[MAX_TRI_CLIPPING]; + + GUINT clipped_count= PLANE_CLIP_TRIANGLE_GENERIC( + 0,srcpoints[0],srcpoints[1],srcpoints[2],temp_points, + DISTANCE_EDGE(tripoints[0],tripoints[1],i0,i1)); + + + if(clipped_count == 0) return 0; + + // edge 1 + clipped_count = PLANE_CLIP_POLYGON_GENERIC( + 0,temp_points,clipped_count,temp_points1, + DISTANCE_EDGE(tripoints[1],tripoints[2],i0,i1)); + + if(clipped_count == 0) return 0; + + // edge 2 + clipped_count = PLANE_CLIP_POLYGON_GENERIC( + 0,temp_points1,clipped_count,clipped_points, + DISTANCE_EDGE(tripoints[2],tripoints[0],i0,i1)); + + return clipped_count;*/ + } + + SIMD_FORCE_INLINE void sort_isect( + GREAL & isect0,GREAL & isect1,GUINT &e0,GUINT &e1,btVector3 & vec0,btVector3 & vec1) + { + if(isect1=isect_v[1]) // face U casts face V + { + return 1; + } + else if(isect_v[0]<=isect_u[0]) // face V casts face U + { + return 2; + } + // closest points + closest_point_u = up_e1; + closest_point_v = vp_e0; + // calc edges and separation + + if(isect_u[1]+ MIN_EDGE_EDGE_DIS=isect_u[1]) // face V casts face U + { + return 2; + } + else if(isect_u[0]<=isect_v[0]) // face U casts face V + { + return 1; + } + // closest points + closest_point_u = up_e0; + closest_point_v = vp_e1; + // calc edges and separation + + if(isect_v[1]+MIN_EDGE_EDGE_DIS0.0f && du0du2>0.0f) // same sign on all of them + not equal 0 ? + { + if(du[0]<0) //we need test behind the triangle plane + { + distances[0] = GIM_MAX3(du[0],du[1],du[2]); + distances[0] = -distances[0]; + if(distances[0]>margin) return false; //never intersect + + //reorder triangle v + VEC_SWAP(tv_vertices[0],tv_vertices[1]); + VEC_SCALE_4(tv_plane,-1.0f,tv_plane); + } + else + { + distances[0] = GIM_MIN3(du[0],du[1],du[2]); + if(distances[0]>margin) return false; //never intersect + } + } + else + { + //Look if we need to invert the triangle + distances[0] = (du[0]+du[1]+du[2])/3.0f; //centroid + + if(distances[0]<0.0f) + { + //reorder triangle v + VEC_SWAP(tv_vertices[0],tv_vertices[1]); + VEC_SCALE_4(tv_plane,-1.0f,tv_plane); + + distances[0] = GIM_MAX3(du[0],du[1],du[2]); + distances[0] = -distances[0]; + } + else + { + distances[0] = GIM_MIN3(du[0],du[1],du[2]); + } + } + + + // plane U vs V points + + TRIANGLE_PLANE(tu_vertices[0],tu_vertices[1],tu_vertices[2],tu_plane); + + dv[0] = DISTANCE_PLANE_POINT(tu_plane,tv_vertices[0]); + dv[1] = DISTANCE_PLANE_POINT(tu_plane,tv_vertices[1]); + dv[2] = DISTANCE_PLANE_POINT(tu_plane,tv_vertices[2]); + + dv0dv1 = dv[0] * dv[1]; + dv0dv2 = dv[0] * dv[2]; + + + if(dv0dv1>0.0f && dv0dv2>0.0f) // same sign on all of them + not equal 0 ? + { + if(dv[0]<0) //we need test behind the triangle plane + { + distances[1] = GIM_MAX3(dv[0],dv[1],dv[2]); + distances[1] = -distances[1]; + if(distances[1]>margin) return false; //never intersect + + //reorder triangle u + VEC_SWAP(tu_vertices[0],tu_vertices[1]); + VEC_SCALE_4(tu_plane,-1.0f,tu_plane); + } + else + { + distances[1] = GIM_MIN3(dv[0],dv[1],dv[2]); + if(distances[1]>margin) return false; //never intersect + } + } + else + { + //Look if we need to invert the triangle + distances[1] = (dv[0]+dv[1]+dv[2])/3.0f; //centroid + + if(distances[1]<0.0f) + { + //reorder triangle v + VEC_SWAP(tu_vertices[0],tu_vertices[1]); + VEC_SCALE_4(tu_plane,-1.0f,tu_plane); + + distances[1] = GIM_MAX3(dv[0],dv[1],dv[2]); + distances[1] = -distances[1]; + } + else + { + distances[1] = GIM_MIN3(dv[0],dv[1],dv[2]); + } + } + + GUINT bl; + /* bl = cross_line_intersection_test(); + if(bl==3) + { + //take edge direction too + bl = distances.maxAxis(); + } + else + {*/ + bl = 0; + if(distances[0]margin) return false; + + contacts.m_penetration_depth = -distances[2] + margin; + contacts.m_points[0] = closest_point_v; + contacts.m_point_count = 1; + VEC_COPY(contacts.m_separating_normal,edge_edge_dir); + + return true; + } + + //clip face against other + + + GUINT point_count; + //TODO + if(bl == 0) //clip U points against V + { + point_count = clip_triangle(tv_plane,tv_vertices,tu_vertices,contact_points); + if(point_count == 0) return false; + contacts.merge_points(tv_plane,margin,contact_points,point_count); + } + else //clip V points against U + { + point_count = clip_triangle(tu_plane,tu_vertices,tv_vertices,contact_points); + if(point_count == 0) return false; + contacts.merge_points(tu_plane,margin,contact_points,point_count); + contacts.m_separating_normal *= -1.f; + } + if(contacts.m_point_count == 0) return false; + return true; + } + +}; + + +/*class GIM_TRIANGLE_CALCULATION_CACHE +{ +public: + GREAL margin; + GUINT clipped_count; + btVector3 tu_vertices[3]; + btVector3 tv_vertices[3]; + btVector3 temp_points[MAX_TRI_CLIPPING]; + btVector3 temp_points1[MAX_TRI_CLIPPING]; + btVector3 clipped_points[MAX_TRI_CLIPPING]; + GIM_TRIANGLE_CONTACT_DATA contacts1; + GIM_TRIANGLE_CONTACT_DATA contacts2; + + + //! clip triangle + GUINT clip_triangle( + const btVector4 & tri_plane, + const btVector3 * tripoints, + const btVector3 * srcpoints, + btVector3 * clipped_points) + { + // edge 0 + + btVector4 edgeplane; + + EDGE_PLANE(tripoints[0],tripoints[1],tri_plane,edgeplane); + + GUINT clipped_count = PLANE_CLIP_TRIANGLE3D( + edgeplane,srcpoints[0],srcpoints[1],srcpoints[2],temp_points); + + if(clipped_count == 0) return 0; + + // edge 1 + + EDGE_PLANE(tripoints[1],tripoints[2],tri_plane,edgeplane); + + clipped_count = PLANE_CLIP_POLYGON3D( + edgeplane,temp_points,clipped_count,temp_points1); + + if(clipped_count == 0) return 0; + + // edge 2 + + EDGE_PLANE(tripoints[2],tripoints[0],tri_plane,edgeplane); + + clipped_count = PLANE_CLIP_POLYGON3D( + edgeplane,temp_points1,clipped_count,clipped_points); + + return clipped_count; + } + + + + + //! collides only on one side + bool triangle_collision( + const btVector3 & u0, + const btVector3 & u1, + const btVector3 & u2, + GREAL margin_u, + const btVector3 & v0, + const btVector3 & v1, + const btVector3 & v2, + GREAL margin_v, + GIM_TRIANGLE_CONTACT_DATA & contacts) + { + + margin = margin_u + margin_v; + + + tu_vertices[0] = u0; + tu_vertices[1] = u1; + tu_vertices[2] = u2; + + tv_vertices[0] = v0; + tv_vertices[1] = v1; + tv_vertices[2] = v2; + + //create planes + // plane v vs U points + + + TRIANGLE_PLANE(tv_vertices[0],tv_vertices[1],tv_vertices[2],contacts1.m_separating_normal); + + clipped_count = clip_triangle( + contacts1.m_separating_normal,tv_vertices,tu_vertices,clipped_points); + + if(clipped_count == 0 ) + { + return false;//Reject + } + + //find most deep interval face1 + contacts1.merge_points(contacts1.m_separating_normal,margin,clipped_points,clipped_count); + if(contacts1.m_point_count == 0) return false; // too far + + //Normal pointing to triangle1 + //contacts1.m_separating_normal *= -1.f; + + //Clip tri1 by tri2 edges + + TRIANGLE_PLANE(tu_vertices[0],tu_vertices[1],tu_vertices[2],contacts2.m_separating_normal); + + clipped_count = clip_triangle( + contacts2.m_separating_normal,tu_vertices,tv_vertices,clipped_points); + + if(clipped_count == 0 ) + { + return false;//Reject + } + + //find most deep interval face1 + contacts2.merge_points(contacts2.m_separating_normal,margin,clipped_points,clipped_count); + if(contacts2.m_point_count == 0) return false; // too far + + contacts2.m_separating_normal *= -1.f; + + ////check most dir for contacts + if(contacts2.m_penetration_depth + SIMD_FORCE_INLINE void mergepoints_generic(const CLASS_PLANE & plane, + GREAL margin, const btVector3 * points, GUINT point_count, DISTANCE_FUNC distance_func) + { + m_point_count = 0; + m_penetration_depth= -1000.0f; + + GUINT point_indices[MAX_TRI_CLIPPING]; + + GUINT _k; + + for(_k=0;_k=0.0f) + { + if(_dist>m_penetration_depth) + { + m_penetration_depth = _dist; + point_indices[0] = _k; + m_point_count=1; + } + else if((_dist+G_EPSILON)>=m_penetration_depth) + { + point_indices[m_point_count] = _k; + m_point_count++; + } + } + } + + for( _k=0;_k u*axe1[i1] + ((vecproj[i2] - u*axe1[i2])/axe2[i2])*axe2[i1] = vecproj[i1] + + --> u*axe1[i1] + vecproj[i2]*axe2[i1]/axe2[i2] - u*axe1[i2]*axe2[i1]/axe2[i2] = vecproj[i1] + + --> u*(axe1[i1] - axe1[i2]*axe2[i1]/axe2[i2]) = vecproj[i1] - vecproj[i2]*axe2[i1]/axe2[i2] + + --> u*((axe1[i1]*axe2[i2] - axe1[i2]*axe2[i1])/axe2[i2]) = (vecproj[i1]*axe2[i2] - vecproj[i2]*axe2[i1])/axe2[i2] + + --> u*(axe1[i1]*axe2[i2] - axe1[i2]*axe2[i1]) = vecproj[i1]*axe2[i2] - vecproj[i2]*axe2[i1] + + --> u = (vecproj[i1]*axe2[i2] - vecproj[i2]*axe2[i1]) /(axe1[i1]*axe2[i2] - axe1[i2]*axe2[i1]) + +if 0.0<= u+v <=1.0 then they are inside of triangle + + \return false if the point is outside of triangle.This function doesn't take the margin + */ + SIMD_FORCE_INLINE bool get_uv_parameters( + const btVector3 & point, + const btVector3 & tri_plane, + GREAL & u, GREAL & v) const + { + btVector3 _axe1 = m_vertices[1]-m_vertices[0]; + btVector3 _axe2 = m_vertices[2]-m_vertices[0]; + btVector3 _vecproj = point - m_vertices[0]; + GUINT _i1 = (tri_plane.closestAxis()+1)%3; + GUINT _i2 = (_i1+1)%3; + if(btFabs(_axe2[_i2])G_EPSILON) + { + return false; + } + } + return true; + } + + //! is point in triangle beam? + /*! + Test if point is in triangle, with m_margin tolerance + */ + SIMD_FORCE_INLINE bool is_point_inside(const btVector3 & point, const btVector3 & tri_normal) const + { + //Test with edge 0 + btVector4 edge_plane; + this->get_edge_plane(0,tri_normal,edge_plane); + GREAL dist = DISTANCE_PLANE_POINT(edge_plane,point); + if(dist-m_margin>0.0f) return false; // outside plane + + this->get_edge_plane(1,tri_normal,edge_plane); + dist = DISTANCE_PLANE_POINT(edge_plane,point); + if(dist-m_margin>0.0f) return false; // outside plane + + this->get_edge_plane(2,tri_normal,edge_plane); + dist = DISTANCE_PLANE_POINT(edge_plane,point); + if(dist-m_margin>0.0f) return false; // outside plane + return true; + } + + + //! Bidireccional ray collision + SIMD_FORCE_INLINE bool ray_collision( + const btVector3 & vPoint, + const btVector3 & vDir, btVector3 & pout, btVector3 & triangle_normal, + GREAL & tparam, GREAL tmax = G_REAL_INFINITY) + { + btVector4 faceplane; + { + btVector3 dif1 = m_vertices[1] - m_vertices[0]; + btVector3 dif2 = m_vertices[2] - m_vertices[0]; + VEC_CROSS(faceplane,dif1,dif2); + faceplane[3] = m_vertices[0].dot(faceplane); + } + + GUINT res = LINE_PLANE_COLLISION(faceplane,vDir,vPoint,pout,tparam, btScalar(0), tmax); + if(res == 0) return false; + if(! is_point_inside(pout,faceplane)) return false; + + if(res==2) //invert normal + { + triangle_normal.setValue(-faceplane[0],-faceplane[1],-faceplane[2]); + } + else + { + triangle_normal.setValue(faceplane[0],faceplane[1],faceplane[2]); + } + + VEC_NORMALIZE(triangle_normal); + + return true; + } + + + //! one direccion ray collision + SIMD_FORCE_INLINE bool ray_collision_front_side( + const btVector3 & vPoint, + const btVector3 & vDir, btVector3 & pout, btVector3 & triangle_normal, + GREAL & tparam, GREAL tmax = G_REAL_INFINITY) + { + btVector4 faceplane; + { + btVector3 dif1 = m_vertices[1] - m_vertices[0]; + btVector3 dif2 = m_vertices[2] - m_vertices[0]; + VEC_CROSS(faceplane,dif1,dif2); + faceplane[3] = m_vertices[0].dot(faceplane); + } + + GUINT res = LINE_PLANE_COLLISION(faceplane,vDir,vPoint,pout,tparam, btScalar(0), tmax); + if(res != 1) return false; + + if(!is_point_inside(pout,faceplane)) return false; + + triangle_normal.setValue(faceplane[0],faceplane[1],faceplane[2]); + + VEC_NORMALIZE(triangle_normal); + + return true; + } + +}; + + + + +#endif // GIM_TRI_COLLISION_H_INCLUDED diff --git a/Engine/lib/bullet/src/BulletCollision/Jamfile b/Engine/lib/bullet/src/BulletCollision/Jamfile new file mode 100644 index 000000000..127e83f80 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Jamfile @@ -0,0 +1,14 @@ + +SubDir TOP src BulletCollision ; + + +Description bulletcollision : "Bullet Collision Detection" ; +Library bulletcollision : + [ Wildcard Gimpact : *.h *.cpp ] + [ Wildcard BroadphaseCollision : *.h *.cpp ] + [ Wildcard CollisionDispatch : *.h *.cpp ] + [ Wildcard CollisionShapes : *.h *.cpp ] + [ Wildcard NarrowPhaseCollision : *.h *.cpp ] +; +LibDepends bulletcollision : bulletmath ; + diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.cpp b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.cpp new file mode 100644 index 000000000..9ee83e7d5 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.cpp @@ -0,0 +1,236 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btContinuousConvexCollision.h" +#include "BulletCollision/CollisionShapes/btConvexShape.h" +#include "BulletCollision/NarrowPhaseCollision/btSimplexSolverInterface.h" +#include "LinearMath/btTransformUtil.h" +#include "BulletCollision/CollisionShapes/btSphereShape.h" + +#include "btGjkPairDetector.h" +#include "btPointCollector.h" + + + +btContinuousConvexCollision::btContinuousConvexCollision ( const btConvexShape* convexA,const btConvexShape* convexB,btSimplexSolverInterface* simplexSolver, btConvexPenetrationDepthSolver* penetrationDepthSolver) +:m_simplexSolver(simplexSolver), +m_penetrationDepthSolver(penetrationDepthSolver), +m_convexA(convexA),m_convexB(convexB) +{ +} + +/// This maximum should not be necessary. It allows for untested/degenerate cases in production code. +/// You don't want your game ever to lock-up. +#define MAX_ITERATIONS 64 + +bool btContinuousConvexCollision::calcTimeOfImpact( + const btTransform& fromA, + const btTransform& toA, + const btTransform& fromB, + const btTransform& toB, + CastResult& result) +{ + + m_simplexSolver->reset(); + + /// compute linear and angular velocity for this interval, to interpolate + btVector3 linVelA,angVelA,linVelB,angVelB; + btTransformUtil::calculateVelocity(fromA,toA,btScalar(1.),linVelA,angVelA); + btTransformUtil::calculateVelocity(fromB,toB,btScalar(1.),linVelB,angVelB); + + + btScalar boundingRadiusA = m_convexA->getAngularMotionDisc(); + btScalar boundingRadiusB = m_convexB->getAngularMotionDisc(); + + btScalar maxAngularProjectedVelocity = angVelA.length() * boundingRadiusA + angVelB.length() * boundingRadiusB; + btVector3 relLinVel = (linVelB-linVelA); + + btScalar relLinVelocLength = (linVelB-linVelA).length(); + + if ((relLinVelocLength+maxAngularProjectedVelocity) == 0.f) + return false; + + + btScalar radius = btScalar(0.001); + + btScalar lambda = btScalar(0.); + btVector3 v(1,0,0); + + int maxIter = MAX_ITERATIONS; + + btVector3 n; + n.setValue(btScalar(0.),btScalar(0.),btScalar(0.)); + bool hasResult = false; + btVector3 c; + + btScalar lastLambda = lambda; + //btScalar epsilon = btScalar(0.001); + + int numIter = 0; + //first solution, using GJK + + + btTransform identityTrans; + identityTrans.setIdentity(); + + btSphereShape raySphere(btScalar(0.0)); + raySphere.setMargin(btScalar(0.)); + + +// result.drawCoordSystem(sphereTr); + + btPointCollector pointCollector1; + + { + + btGjkPairDetector gjk(m_convexA,m_convexB,m_convexA->getShapeType(),m_convexB->getShapeType(),m_convexA->getMargin(),m_convexB->getMargin(),m_simplexSolver,m_penetrationDepthSolver); + btGjkPairDetector::ClosestPointInput input; + + //we don't use margins during CCD + // gjk.setIgnoreMargin(true); + + input.m_transformA = fromA; + input.m_transformB = fromB; + gjk.getClosestPoints(input,pointCollector1,0); + + hasResult = pointCollector1.m_hasResult; + c = pointCollector1.m_pointInWorld; + } + + if (hasResult) + { + btScalar dist; + dist = pointCollector1.m_distance; + n = pointCollector1.m_normalOnBInWorld; + + btScalar projectedLinearVelocity = relLinVel.dot(n); + + //not close enough + while (dist > radius) + { + if (result.m_debugDrawer) + { + result.m_debugDrawer->drawSphere(c,0.2f,btVector3(1,1,1)); + } + numIter++; + if (numIter > maxIter) + { + return false; //todo: report a failure + } + btScalar dLambda = btScalar(0.); + + projectedLinearVelocity = relLinVel.dot(n); + + //calculate safe moving fraction from distance / (linear+rotational velocity) + + //btScalar clippedDist = GEN_min(angularConservativeRadius,dist); + //btScalar clippedDist = dist; + + //don't report time of impact for motion away from the contact normal (or causes minor penetration) + if ((projectedLinearVelocity+ maxAngularProjectedVelocity)<=SIMD_EPSILON) + return false; + + dLambda = dist / (projectedLinearVelocity+ maxAngularProjectedVelocity); + + + + lambda = lambda + dLambda; + + if (lambda > btScalar(1.)) + return false; + + if (lambda < btScalar(0.)) + return false; + + + //todo: next check with relative epsilon + if (lambda <= lastLambda) + { + return false; + //n.setValue(0,0,0); + break; + } + lastLambda = lambda; + + + + //interpolate to next lambda + btTransform interpolatedTransA,interpolatedTransB,relativeTrans; + + btTransformUtil::integrateTransform(fromA,linVelA,angVelA,lambda,interpolatedTransA); + btTransformUtil::integrateTransform(fromB,linVelB,angVelB,lambda,interpolatedTransB); + relativeTrans = interpolatedTransB.inverseTimes(interpolatedTransA); + + if (result.m_debugDrawer) + { + result.m_debugDrawer->drawSphere(interpolatedTransA.getOrigin(),0.2f,btVector3(1,0,0)); + } + + result.DebugDraw( lambda ); + + btPointCollector pointCollector; + btGjkPairDetector gjk(m_convexA,m_convexB,m_simplexSolver,m_penetrationDepthSolver); + btGjkPairDetector::ClosestPointInput input; + input.m_transformA = interpolatedTransA; + input.m_transformB = interpolatedTransB; + gjk.getClosestPoints(input,pointCollector,0); + if (pointCollector.m_hasResult) + { + if (pointCollector.m_distance < btScalar(0.)) + { + //degenerate ?! + result.m_fraction = lastLambda; + n = pointCollector.m_normalOnBInWorld; + result.m_normal=n;//.setValue(1,1,1);// = n; + result.m_hitPoint = pointCollector.m_pointInWorld; + return true; + } + c = pointCollector.m_pointInWorld; + n = pointCollector.m_normalOnBInWorld; + dist = pointCollector.m_distance; + } else + { + //?? + return false; + } + + + } + + if ((projectedLinearVelocity+ maxAngularProjectedVelocity)<=result.m_allowedPenetration)//SIMD_EPSILON) + return false; + + result.m_fraction = lambda; + result.m_normal = n; + result.m_hitPoint = c; + return true; + } + + return false; + +/* +//todo: + //if movement away from normal, discard result + btVector3 move = transBLocalTo.getOrigin() - transBLocalFrom.getOrigin(); + if (result.m_fraction < btScalar(1.)) + { + if (move.dot(result.m_normal) <= btScalar(0.)) + { + } + } +*/ + +} diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.h b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.h new file mode 100644 index 000000000..28c2b4d61 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.h @@ -0,0 +1,52 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 CONTINUOUS_COLLISION_CONVEX_CAST_H +#define CONTINUOUS_COLLISION_CONVEX_CAST_H + +#include "btConvexCast.h" +#include "btSimplexSolverInterface.h" +class btConvexPenetrationDepthSolver; +class btConvexShape; + +/// btContinuousConvexCollision implements angular and linear time of impact for convex objects. +/// Based on Brian Mirtich's Conservative Advancement idea (PhD thesis). +/// Algorithm operates in worldspace, in order to keep inbetween motion globally consistent. +/// It uses GJK at the moment. Future improvement would use minkowski sum / supporting vertex, merging innerloops +class btContinuousConvexCollision : public btConvexCast +{ + btSimplexSolverInterface* m_simplexSolver; + btConvexPenetrationDepthSolver* m_penetrationDepthSolver; + const btConvexShape* m_convexA; + const btConvexShape* m_convexB; + + +public: + + btContinuousConvexCollision (const btConvexShape* shapeA,const btConvexShape* shapeB ,btSimplexSolverInterface* simplexSolver,btConvexPenetrationDepthSolver* penetrationDepthSolver); + + virtual bool calcTimeOfImpact( + const btTransform& fromA, + const btTransform& toA, + const btTransform& fromB, + const btTransform& toB, + CastResult& result); + + +}; + +#endif //CONTINUOUS_COLLISION_CONVEX_CAST_H + diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btConvexCast.cpp b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btConvexCast.cpp new file mode 100644 index 000000000..d2a1310b2 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btConvexCast.cpp @@ -0,0 +1,20 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btConvexCast.h" + +btConvexCast::~btConvexCast() +{ +} diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btConvexCast.h b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btConvexCast.h new file mode 100644 index 000000000..b0bce341e --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btConvexCast.h @@ -0,0 +1,73 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 CONVEX_CAST_H +#define CONVEX_CAST_H + +#include "LinearMath/btTransform.h" +#include "LinearMath/btVector3.h" +#include "LinearMath/btScalar.h" +class btMinkowskiSumShape; +#include "LinearMath/btIDebugDraw.h" + +/// btConvexCast is an interface for Casting +class btConvexCast +{ +public: + + + virtual ~btConvexCast(); + + ///RayResult stores the closest result + /// alternatively, add a callback method to decide about closest/all results + struct CastResult + { + //virtual bool addRayResult(const btVector3& normal,btScalar fraction) = 0; + + virtual void DebugDraw(btScalar fraction) {(void)fraction;} + virtual void drawCoordSystem(const btTransform& trans) {(void)trans;} + + CastResult() + :m_fraction(btScalar(BT_LARGE_FLOAT)), + m_debugDrawer(0), + m_allowedPenetration(btScalar(0)) + { + } + + + virtual ~CastResult() {}; + + btTransform m_hitTransformA; + btTransform m_hitTransformB; + btVector3 m_normal; + btVector3 m_hitPoint; + btScalar m_fraction; //input and output + btIDebugDraw* m_debugDrawer; + btScalar m_allowedPenetration; + + }; + + + /// cast a convex against another convex object + virtual bool calcTimeOfImpact( + const btTransform& fromA, + const btTransform& toA, + const btTransform& fromB, + const btTransform& toB, + CastResult& result) = 0; +}; + +#endif //CONVEX_CAST_H diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btConvexPenetrationDepthSolver.h b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btConvexPenetrationDepthSolver.h new file mode 100644 index 000000000..7e3fde8e2 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btConvexPenetrationDepthSolver.h @@ -0,0 +1,42 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 __CONVEX_PENETRATION_DEPTH_H +#define __CONVEX_PENETRATION_DEPTH_H + +class btStackAlloc; +class btVector3; +#include "btSimplexSolverInterface.h" +class btConvexShape; +class btTransform; + +///ConvexPenetrationDepthSolver provides an interface for penetration depth calculation. +class btConvexPenetrationDepthSolver +{ +public: + + virtual ~btConvexPenetrationDepthSolver() {}; + virtual bool calcPenDepth( btSimplexSolverInterface& simplexSolver, + const btConvexShape* convexA,const btConvexShape* convexB, + const btTransform& transA,const btTransform& transB, + btVector3& v, btVector3& pa, btVector3& pb, + class btIDebugDraw* debugDraw,btStackAlloc* stackAlloc + ) = 0; + + +}; +#endif //CONVEX_PENETRATION_DEPTH_H + diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h new file mode 100644 index 000000000..bc711ad49 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h @@ -0,0 +1,89 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 DISCRETE_COLLISION_DETECTOR1_INTERFACE_H +#define DISCRETE_COLLISION_DETECTOR1_INTERFACE_H +#include "LinearMath/btTransform.h" +#include "LinearMath/btVector3.h" +class btStackAlloc; + +/// This interface is made to be used by an iterative approach to do TimeOfImpact calculations +/// This interface allows to query for closest points and penetration depth between two (convex) objects +/// the closest point is on the second object (B), and the normal points from the surface on B towards A. +/// distance is between closest points on B and closest point on A. So you can calculate closest point on A +/// by taking closestPointInA = closestPointInB + m_distance * m_normalOnSurfaceB +struct btDiscreteCollisionDetectorInterface +{ + + struct Result + { + + virtual ~Result(){} + + ///setShapeIdentifiersA/B provides experimental support for per-triangle material / custom material combiner + virtual void setShapeIdentifiersA(int partId0,int index0)=0; + virtual void setShapeIdentifiersB(int partId1,int index1)=0; + virtual void addContactPoint(const btVector3& normalOnBInWorld,const btVector3& pointInWorld,btScalar depth)=0; + }; + + struct ClosestPointInput + { + ClosestPointInput() + :m_maximumDistanceSquared(btScalar(BT_LARGE_FLOAT)), + m_stackAlloc(0) + { + } + + btTransform m_transformA; + btTransform m_transformB; + btScalar m_maximumDistanceSquared; + btStackAlloc* m_stackAlloc; + }; + + virtual ~btDiscreteCollisionDetectorInterface() {}; + + // + // give either closest points (distance > 0) or penetration (distance) + // the normal always points from B towards A + // + virtual void getClosestPoints(const ClosestPointInput& input,Result& output,class btIDebugDraw* debugDraw,bool swapResults=false) = 0; + +}; + +struct btStorageResult : public btDiscreteCollisionDetectorInterface::Result +{ + btVector3 m_normalOnSurfaceB; + btVector3 m_closestPointInB; + btScalar m_distance; //negative means penetration ! + + btStorageResult() : m_distance(btScalar(BT_LARGE_FLOAT)) + { + + } + virtual ~btStorageResult() {}; + + virtual void addContactPoint(const btVector3& normalOnBInWorld,const btVector3& pointInWorld,btScalar depth) + { + if (depth < m_distance) + { + m_normalOnSurfaceB = normalOnBInWorld; + m_closestPointInB = pointInWorld; + m_distance = depth; + } + } +}; + +#endif //DISCRETE_COLLISION_DETECTOR_INTERFACE1_H diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkConvexCast.cpp b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkConvexCast.cpp new file mode 100644 index 000000000..bef697a0a --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkConvexCast.cpp @@ -0,0 +1,176 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btGjkConvexCast.h" +#include "BulletCollision/CollisionShapes/btSphereShape.h" +#include "btGjkPairDetector.h" +#include "btPointCollector.h" +#include "LinearMath/btTransformUtil.h" + +#ifdef BT_USE_DOUBLE_PRECISION +#define MAX_ITERATIONS 64 +#else +#define MAX_ITERATIONS 32 +#endif + +btGjkConvexCast::btGjkConvexCast(const btConvexShape* convexA,const btConvexShape* convexB,btSimplexSolverInterface* simplexSolver) +:m_simplexSolver(simplexSolver), +m_convexA(convexA), +m_convexB(convexB) +{ +} + +bool btGjkConvexCast::calcTimeOfImpact( + const btTransform& fromA, + const btTransform& toA, + const btTransform& fromB, + const btTransform& toB, + CastResult& result) +{ + + + m_simplexSolver->reset(); + + /// compute linear velocity for this interval, to interpolate + //assume no rotation/angular velocity, assert here? + btVector3 linVelA,linVelB; + linVelA = toA.getOrigin()-fromA.getOrigin(); + linVelB = toB.getOrigin()-fromB.getOrigin(); + + btScalar radius = btScalar(0.001); + btScalar lambda = btScalar(0.); + btVector3 v(1,0,0); + + int maxIter = MAX_ITERATIONS; + + btVector3 n; + n.setValue(btScalar(0.),btScalar(0.),btScalar(0.)); + bool hasResult = false; + btVector3 c; + btVector3 r = (linVelA-linVelB); + + btScalar lastLambda = lambda; + //btScalar epsilon = btScalar(0.001); + + int numIter = 0; + //first solution, using GJK + + + btTransform identityTrans; + identityTrans.setIdentity(); + + +// result.drawCoordSystem(sphereTr); + + btPointCollector pointCollector; + + + btGjkPairDetector gjk(m_convexA,m_convexB,m_simplexSolver,0);//m_penetrationDepthSolver); + btGjkPairDetector::ClosestPointInput input; + + //we don't use margins during CCD + // gjk.setIgnoreMargin(true); + + input.m_transformA = fromA; + input.m_transformB = fromB; + gjk.getClosestPoints(input,pointCollector,0); + + hasResult = pointCollector.m_hasResult; + c = pointCollector.m_pointInWorld; + + if (hasResult) + { + btScalar dist; + dist = pointCollector.m_distance; + n = pointCollector.m_normalOnBInWorld; + + + + //not close enough + while (dist > radius) + { + numIter++; + if (numIter > maxIter) + { + return false; //todo: report a failure + } + btScalar dLambda = btScalar(0.); + + btScalar projectedLinearVelocity = r.dot(n); + + dLambda = dist / (projectedLinearVelocity); + + lambda = lambda - dLambda; + + if (lambda > btScalar(1.)) + return false; + + if (lambda < btScalar(0.)) + return false; + + //todo: next check with relative epsilon + if (lambda <= lastLambda) + { + return false; + //n.setValue(0,0,0); + break; + } + lastLambda = lambda; + + //interpolate to next lambda + result.DebugDraw( lambda ); + input.m_transformA.getOrigin().setInterpolate3(fromA.getOrigin(),toA.getOrigin(),lambda); + input.m_transformB.getOrigin().setInterpolate3(fromB.getOrigin(),toB.getOrigin(),lambda); + + gjk.getClosestPoints(input,pointCollector,0); + if (pointCollector.m_hasResult) + { + if (pointCollector.m_distance < btScalar(0.)) + { + result.m_fraction = lastLambda; + n = pointCollector.m_normalOnBInWorld; + result.m_normal=n; + result.m_hitPoint = pointCollector.m_pointInWorld; + return true; + } + c = pointCollector.m_pointInWorld; + n = pointCollector.m_normalOnBInWorld; + dist = pointCollector.m_distance; + } else + { + //?? + return false; + } + + } + + //is n normalized? + //don't report time of impact for motion away from the contact normal (or causes minor penetration) + if (n.dot(r)>=-result.m_allowedPenetration) + return false; + + result.m_fraction = lambda; + result.m_normal = n; + result.m_hitPoint = c; + return true; + } + + return false; + + +} + diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkConvexCast.h b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkConvexCast.h new file mode 100644 index 000000000..a977c9e83 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkConvexCast.h @@ -0,0 +1,50 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 GJK_CONVEX_CAST_H +#define GJK_CONVEX_CAST_H + +#include "BulletCollision/CollisionShapes/btCollisionMargin.h" + +#include "LinearMath/btVector3.h" +#include "btConvexCast.h" +class btConvexShape; +class btMinkowskiSumShape; +#include "btSimplexSolverInterface.h" + +///GjkConvexCast performs a raycast on a convex object using support mapping. +class btGjkConvexCast : public btConvexCast +{ + btSimplexSolverInterface* m_simplexSolver; + const btConvexShape* m_convexA; + const btConvexShape* m_convexB; + +public: + + btGjkConvexCast(const btConvexShape* convexA,const btConvexShape* convexB,btSimplexSolverInterface* simplexSolver); + + /// cast a convex against another convex object + virtual bool calcTimeOfImpact( + const btTransform& fromA, + const btTransform& toA, + const btTransform& fromB, + const btTransform& toB, + CastResult& result); + +}; + +#endif //GJK_CONVEX_CAST_H diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpa2.cpp b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpa2.cpp new file mode 100644 index 000000000..f74261d4b --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpa2.cpp @@ -0,0 +1,989 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2008 Erwin Coumans http://continuousphysics.com/Bullet/ + +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. +*/ + +/* +GJK-EPA collision solver by Nathanael Presson, 2008 +*/ +#include "BulletCollision/CollisionShapes/btConvexInternalShape.h" +#include "BulletCollision/CollisionShapes/btSphereShape.h" +#include "btGjkEpa2.h" + +#if defined(DEBUG) || defined (_DEBUG) +#include //for debug printf +#ifdef __SPU__ +#include +#define printf spu_printf +#endif //__SPU__ +#endif + +namespace gjkepa2_impl +{ + + // Config + + /* GJK */ +#define GJK_MAX_ITERATIONS 128 +#define GJK_ACCURARY ((btScalar)0.0001) +#define GJK_MIN_DISTANCE ((btScalar)0.0001) +#define GJK_DUPLICATED_EPS ((btScalar)0.0001) +#define GJK_SIMPLEX2_EPS ((btScalar)0.0) +#define GJK_SIMPLEX3_EPS ((btScalar)0.0) +#define GJK_SIMPLEX4_EPS ((btScalar)0.0) + + /* EPA */ +#define EPA_MAX_VERTICES 64 +#define EPA_MAX_FACES (EPA_MAX_VERTICES*2) +#define EPA_MAX_ITERATIONS 255 +#define EPA_ACCURACY ((btScalar)0.0001) +#define EPA_FALLBACK (10*EPA_ACCURACY) +#define EPA_PLANE_EPS ((btScalar)0.00001) +#define EPA_INSIDE_EPS ((btScalar)0.01) + + + // Shorthands + typedef unsigned int U; + typedef unsigned char U1; + + // MinkowskiDiff + struct MinkowskiDiff + { + const btConvexShape* m_shapes[2]; + btMatrix3x3 m_toshape1; + btTransform m_toshape0; +#ifdef __SPU__ + bool m_enableMargin; +#else + btVector3 (btConvexShape::*Ls)(const btVector3&) const; +#endif//__SPU__ + + + MinkowskiDiff() + { + + } +#ifdef __SPU__ + void EnableMargin(bool enable) + { + m_enableMargin = enable; + } + inline btVector3 Support0(const btVector3& d) const + { + if (m_enableMargin) + { + return m_shapes[0]->localGetSupportVertexNonVirtual(d); + } else + { + return m_shapes[0]->localGetSupportVertexWithoutMarginNonVirtual(d); + } + } + inline btVector3 Support1(const btVector3& d) const + { + if (m_enableMargin) + { + return m_toshape0*(m_shapes[1]->localGetSupportVertexNonVirtual(m_toshape1*d)); + } else + { + return m_toshape0*(m_shapes[1]->localGetSupportVertexWithoutMarginNonVirtual(m_toshape1*d)); + } + } +#else + void EnableMargin(bool enable) + { + if(enable) + Ls=&btConvexShape::localGetSupportVertexNonVirtual; + else + Ls=&btConvexShape::localGetSupportVertexWithoutMarginNonVirtual; + } + inline btVector3 Support0(const btVector3& d) const + { + return(((m_shapes[0])->*(Ls))(d)); + } + inline btVector3 Support1(const btVector3& d) const + { + return(m_toshape0*((m_shapes[1])->*(Ls))(m_toshape1*d)); + } +#endif //__SPU__ + + inline btVector3 Support(const btVector3& d) const + { + return(Support0(d)-Support1(-d)); + } + btVector3 Support(const btVector3& d,U index) const + { + if(index) + return(Support1(d)); + else + return(Support0(d)); + } + }; + + typedef MinkowskiDiff tShape; + + + // GJK + struct GJK + { + /* Types */ + struct sSV + { + btVector3 d,w; + }; + struct sSimplex + { + sSV* c[4]; + btScalar p[4]; + U rank; + }; + struct eStatus { enum _ { + Valid, + Inside, + Failed };}; + /* Fields */ + tShape m_shape; + btVector3 m_ray; + btScalar m_distance; + sSimplex m_simplices[2]; + sSV m_store[4]; + sSV* m_free[4]; + U m_nfree; + U m_current; + sSimplex* m_simplex; + eStatus::_ m_status; + /* Methods */ + GJK() + { + Initialize(); + } + void Initialize() + { + m_ray = btVector3(0,0,0); + m_nfree = 0; + m_status = eStatus::Failed; + m_current = 0; + m_distance = 0; + } + eStatus::_ Evaluate(const tShape& shapearg,const btVector3& guess) + { + U iterations=0; + btScalar sqdist=0; + btScalar alpha=0; + btVector3 lastw[4]; + U clastw=0; + /* Initialize solver */ + m_free[0] = &m_store[0]; + m_free[1] = &m_store[1]; + m_free[2] = &m_store[2]; + m_free[3] = &m_store[3]; + m_nfree = 4; + m_current = 0; + m_status = eStatus::Valid; + m_shape = shapearg; + m_distance = 0; + /* Initialize simplex */ + m_simplices[0].rank = 0; + m_ray = guess; + const btScalar sqrl= m_ray.length2(); + appendvertice(m_simplices[0],sqrl>0?-m_ray:btVector3(1,0,0)); + m_simplices[0].p[0] = 1; + m_ray = m_simplices[0].c[0]->w; + sqdist = sqrl; + lastw[0] = + lastw[1] = + lastw[2] = + lastw[3] = m_ray; + /* Loop */ + do { + const U next=1-m_current; + sSimplex& cs=m_simplices[m_current]; + sSimplex& ns=m_simplices[next]; + /* Check zero */ + const btScalar rl=m_ray.length(); + if(rlw; + bool found=false; + for(U i=0;i<4;++i) + { + if((w-lastw[i]).length2()w, + cs.c[1]->w, + weights,mask);break; + case 3: sqdist=projectorigin( cs.c[0]->w, + cs.c[1]->w, + cs.c[2]->w, + weights,mask);break; + case 4: sqdist=projectorigin( cs.c[0]->w, + cs.c[1]->w, + cs.c[2]->w, + cs.c[3]->w, + weights,mask);break; + } + if(sqdist>=0) + {/* Valid */ + ns.rank = 0; + m_ray = btVector3(0,0,0); + m_current = next; + for(U i=0,ni=cs.rank;iw*weights[i]; + } + else + { + m_free[m_nfree++] = cs.c[i]; + } + } + if(mask==15) m_status=eStatus::Inside; + } + else + {/* Return old simplex */ + removevertice(m_simplices[m_current]); + break; + } + m_status=((++iterations)rank) + { + case 1: + { + for(U i=0;i<3;++i) + { + btVector3 axis=btVector3(0,0,0); + axis[i]=1; + appendvertice(*m_simplex, axis); + if(EncloseOrigin()) return(true); + removevertice(*m_simplex); + appendvertice(*m_simplex,-axis); + if(EncloseOrigin()) return(true); + removevertice(*m_simplex); + } + } + break; + case 2: + { + const btVector3 d=m_simplex->c[1]->w-m_simplex->c[0]->w; + for(U i=0;i<3;++i) + { + btVector3 axis=btVector3(0,0,0); + axis[i]=1; + const btVector3 p=btCross(d,axis); + if(p.length2()>0) + { + appendvertice(*m_simplex, p); + if(EncloseOrigin()) return(true); + removevertice(*m_simplex); + appendvertice(*m_simplex,-p); + if(EncloseOrigin()) return(true); + removevertice(*m_simplex); + } + } + } + break; + case 3: + { + const btVector3 n=btCross(m_simplex->c[1]->w-m_simplex->c[0]->w, + m_simplex->c[2]->w-m_simplex->c[0]->w); + if(n.length2()>0) + { + appendvertice(*m_simplex,n); + if(EncloseOrigin()) return(true); + removevertice(*m_simplex); + appendvertice(*m_simplex,-n); + if(EncloseOrigin()) return(true); + removevertice(*m_simplex); + } + } + break; + case 4: + { + if(btFabs(det( m_simplex->c[0]->w-m_simplex->c[3]->w, + m_simplex->c[1]->w-m_simplex->c[3]->w, + m_simplex->c[2]->w-m_simplex->c[3]->w))>0) + return(true); + } + break; + } + return(false); + } + /* Internals */ + void getsupport(const btVector3& d,sSV& sv) const + { + sv.d = d/d.length(); + sv.w = m_shape.Support(sv.d); + } + void removevertice(sSimplex& simplex) + { + m_free[m_nfree++]=simplex.c[--simplex.rank]; + } + void appendvertice(sSimplex& simplex,const btVector3& v) + { + simplex.p[simplex.rank]=0; + simplex.c[simplex.rank]=m_free[--m_nfree]; + getsupport(v,*simplex.c[simplex.rank++]); + } + static btScalar det(const btVector3& a,const btVector3& b,const btVector3& c) + { + return( a.y()*b.z()*c.x()+a.z()*b.x()*c.y()- + a.x()*b.z()*c.y()-a.y()*b.x()*c.z()+ + a.x()*b.y()*c.z()-a.z()*b.y()*c.x()); + } + static btScalar projectorigin( const btVector3& a, + const btVector3& b, + btScalar* w,U& m) + { + const btVector3 d=b-a; + const btScalar l=d.length2(); + if(l>GJK_SIMPLEX2_EPS) + { + const btScalar t(l>0?-btDot(a,d)/l:0); + if(t>=1) { w[0]=0;w[1]=1;m=2;return(b.length2()); } + else if(t<=0) { w[0]=1;w[1]=0;m=1;return(a.length2()); } + else { w[0]=1-(w[1]=t);m=3;return((a+d*t).length2()); } + } + return(-1); + } + static btScalar projectorigin( const btVector3& a, + const btVector3& b, + const btVector3& c, + btScalar* w,U& m) + { + static const U imd3[]={1,2,0}; + const btVector3* vt[]={&a,&b,&c}; + const btVector3 dl[]={a-b,b-c,c-a}; + const btVector3 n=btCross(dl[0],dl[1]); + const btScalar l=n.length2(); + if(l>GJK_SIMPLEX3_EPS) + { + btScalar mindist=-1; + btScalar subw[2]={0.f,0.f}; + U subm(0); + for(U i=0;i<3;++i) + { + if(btDot(*vt[i],btCross(dl[i],n))>0) + { + const U j=imd3[i]; + const btScalar subd(projectorigin(*vt[i],*vt[j],subw,subm)); + if((mindist<0)||(subd(((subm&1)?1<GJK_SIMPLEX4_EPS)) + { + btScalar mindist=-1; + btScalar subw[3]={0.f,0.f,0.f}; + U subm(0); + for(U i=0;i<3;++i) + { + const U j=imd3[i]; + const btScalar s=vl*btDot(d,btCross(dl[i],dl[j])); + if(s>0) + { + const btScalar subd=projectorigin(*vt[i],*vt[j],d,subw,subm); + if((mindist<0)||(subd((subm&1?1<e[ea]=(U1)eb;fa->f[ea]=fb; + fb->e[eb]=(U1)ea;fb->f[eb]=fa; + } + static inline void append(sList& list,sFace* face) + { + face->l[0] = 0; + face->l[1] = list.root; + if(list.root) list.root->l[0]=face; + list.root = face; + ++list.count; + } + static inline void remove(sList& list,sFace* face) + { + if(face->l[1]) face->l[1]->l[0]=face->l[0]; + if(face->l[0]) face->l[0]->l[1]=face->l[1]; + if(face==list.root) list.root=face->l[1]; + --list.count; + } + + + void Initialize() + { + m_status = eStatus::Failed; + m_normal = btVector3(0,0,0); + m_depth = 0; + m_nextsv = 0; + for(U i=0;i1)&&gjk.EncloseOrigin()) + { + + /* Clean up */ + while(m_hull.root) + { + sFace* f = m_hull.root; + remove(m_hull,f); + append(m_stock,f); + } + m_status = eStatus::Valid; + m_nextsv = 0; + /* Orient simplex */ + if(gjk.det( simplex.c[0]->w-simplex.c[3]->w, + simplex.c[1]->w-simplex.c[3]->w, + simplex.c[2]->w-simplex.c[3]->w)<0) + { + btSwap(simplex.c[0],simplex.c[1]); + btSwap(simplex.p[0],simplex.p[1]); + } + /* Build initial hull */ + sFace* tetra[]={newface(simplex.c[0],simplex.c[1],simplex.c[2],true), + newface(simplex.c[1],simplex.c[0],simplex.c[3],true), + newface(simplex.c[2],simplex.c[1],simplex.c[3],true), + newface(simplex.c[0],simplex.c[2],simplex.c[3],true)}; + if(m_hull.count==4) + { + sFace* best=findbest(); + sFace outer=*best; + U pass=0; + U iterations=0; + bind(tetra[0],0,tetra[1],0); + bind(tetra[0],1,tetra[2],0); + bind(tetra[0],2,tetra[3],0); + bind(tetra[1],1,tetra[3],2); + bind(tetra[1],2,tetra[2],1); + bind(tetra[2],2,tetra[3],1); + m_status=eStatus::Valid; + for(;iterationspass = (U1)(++pass); + gjk.getsupport(best->n,*w); + const btScalar wdist=btDot(best->n,w->w)-best->d; + if(wdist>EPA_ACCURACY) + { + for(U j=0;(j<3)&&valid;++j) + { + valid&=expand( pass,w, + best->f[j],best->e[j], + horizon); + } + if(valid&&(horizon.nf>=3)) + { + bind(horizon.cf,1,horizon.ff,2); + remove(m_hull,best); + append(m_stock,best); + best=findbest(); + if(best->p>=outer.p) outer=*best; + } else { m_status=eStatus::InvalidHull;break; } + } else { m_status=eStatus::AccuraryReached;break; } + } else { m_status=eStatus::OutOfVertices;break; } + } + const btVector3 projection=outer.n*outer.d; + m_normal = outer.n; + m_depth = outer.d; + m_result.rank = 3; + m_result.c[0] = outer.c[0]; + m_result.c[1] = outer.c[1]; + m_result.c[2] = outer.c[2]; + m_result.p[0] = btCross( outer.c[1]->w-projection, + outer.c[2]->w-projection).length(); + m_result.p[1] = btCross( outer.c[2]->w-projection, + outer.c[0]->w-projection).length(); + m_result.p[2] = btCross( outer.c[0]->w-projection, + outer.c[1]->w-projection).length(); + const btScalar sum=m_result.p[0]+m_result.p[1]+m_result.p[2]; + m_result.p[0] /= sum; + m_result.p[1] /= sum; + m_result.p[2] /= sum; + return(m_status); + } + } + /* Fallback */ + m_status = eStatus::FallBack; + m_normal = -guess; + const btScalar nl=m_normal.length(); + if(nl>0) + m_normal = m_normal/nl; + else + m_normal = btVector3(1,0,0); + m_depth = 0; + m_result.rank=1; + m_result.c[0]=simplex.c[0]; + m_result.p[0]=1; + return(m_status); + } + sFace* newface(sSV* a,sSV* b,sSV* c,bool forced) + { + if(m_stock.root) + { + sFace* face=m_stock.root; + remove(m_stock,face); + append(m_hull,face); + face->pass = 0; + face->c[0] = a; + face->c[1] = b; + face->c[2] = c; + face->n = btCross(b->w-a->w,c->w-a->w); + const btScalar l=face->n.length(); + const bool v=l>EPA_ACCURACY; + face->p = btMin(btMin( + btDot(a->w,btCross(face->n,a->w-b->w)), + btDot(b->w,btCross(face->n,b->w-c->w))), + btDot(c->w,btCross(face->n,c->w-a->w))) / + (v?l:1); + face->p = face->p>=-EPA_INSIDE_EPS?0:face->p; + if(v) + { + face->d = btDot(a->w,face->n)/l; + face->n /= l; + if(forced||(face->d>=-EPA_PLANE_EPS)) + { + return(face); + } else m_status=eStatus::NonConvex; + } else m_status=eStatus::Degenerated; + remove(m_hull,face); + append(m_stock,face); + return(0); + } + m_status=m_stock.root?eStatus::OutOfVertices:eStatus::OutOfFaces; + return(0); + } + sFace* findbest() + { + sFace* minf=m_hull.root; + btScalar mind=minf->d*minf->d; + btScalar maxp=minf->p; + for(sFace* f=minf->l[1];f;f=f->l[1]) + { + const btScalar sqd=f->d*f->d; + if((f->p>=maxp)&&(sqdp; + } + } + return(minf); + } + bool expand(U pass,sSV* w,sFace* f,U e,sHorizon& horizon) + { + static const U i1m3[]={1,2,0}; + static const U i2m3[]={2,0,1}; + if(f->pass!=pass) + { + const U e1=i1m3[e]; + if((btDot(f->n,w->w)-f->d)<-EPA_PLANE_EPS) + { + sFace* nf=newface(f->c[e1],f->c[e],w,false); + if(nf) + { + bind(nf,0,f,e); + if(horizon.cf) bind(horizon.cf,1,nf,2); else horizon.ff=nf; + horizon.cf=nf; + ++horizon.nf; + return(true); + } + } + else + { + const U e2=i2m3[e]; + f->pass = (U1)pass; + if( expand(pass,w,f->f[e1],f->e[e1],horizon)&& + expand(pass,w,f->f[e2],f->e[e2],horizon)) + { + remove(m_hull,f); + append(m_stock,f); + return(true); + } + } + } + return(false); + } + + }; + + // + static void Initialize( const btConvexShape* shape0,const btTransform& wtrs0, + const btConvexShape* shape1,const btTransform& wtrs1, + btGjkEpaSolver2::sResults& results, + tShape& shape, + bool withmargins) + { + /* Results */ + results.witnesses[0] = + results.witnesses[1] = btVector3(0,0,0); + results.status = btGjkEpaSolver2::sResults::Separated; + /* Shape */ + shape.m_shapes[0] = shape0; + shape.m_shapes[1] = shape1; + shape.m_toshape1 = wtrs1.getBasis().transposeTimes(wtrs0.getBasis()); + shape.m_toshape0 = wtrs0.inverseTimes(wtrs1); + shape.EnableMargin(withmargins); + } + +} + +// +// Api +// + +using namespace gjkepa2_impl; + +// +int btGjkEpaSolver2::StackSizeRequirement() +{ + return(sizeof(GJK)+sizeof(EPA)); +} + +// +bool btGjkEpaSolver2::Distance( const btConvexShape* shape0, + const btTransform& wtrs0, + const btConvexShape* shape1, + const btTransform& wtrs1, + const btVector3& guess, + sResults& results) +{ + tShape shape; + Initialize(shape0,wtrs0,shape1,wtrs1,results,shape,false); + GJK gjk; + GJK::eStatus::_ gjk_status=gjk.Evaluate(shape,guess); + if(gjk_status==GJK::eStatus::Valid) + { + btVector3 w0=btVector3(0,0,0); + btVector3 w1=btVector3(0,0,0); + for(U i=0;irank;++i) + { + const btScalar p=gjk.m_simplex->p[i]; + w0+=shape.Support( gjk.m_simplex->c[i]->d,0)*p; + w1+=shape.Support(-gjk.m_simplex->c[i]->d,1)*p; + } + results.witnesses[0] = wtrs0*w0; + results.witnesses[1] = wtrs0*w1; + results.normal = w0-w1; + results.distance = results.normal.length(); + results.normal /= results.distance>GJK_MIN_DISTANCE?results.distance:1; + return(true); + } + else + { + results.status = gjk_status==GJK::eStatus::Inside? + sResults::Penetrating : + sResults::GJK_Failed ; + return(false); + } +} + +// +bool btGjkEpaSolver2::Penetration( const btConvexShape* shape0, + const btTransform& wtrs0, + const btConvexShape* shape1, + const btTransform& wtrs1, + const btVector3& guess, + sResults& results, + bool usemargins) +{ + tShape shape; + Initialize(shape0,wtrs0,shape1,wtrs1,results,shape,usemargins); + GJK gjk; + GJK::eStatus::_ gjk_status=gjk.Evaluate(shape,-guess); + switch(gjk_status) + { + case GJK::eStatus::Inside: + { + EPA epa; + EPA::eStatus::_ epa_status=epa.Evaluate(gjk,-guess); + if(epa_status!=EPA::eStatus::Failed) + { + btVector3 w0=btVector3(0,0,0); + for(U i=0;id,0)*epa.m_result.p[i]; + } + results.status = sResults::Penetrating; + results.witnesses[0] = wtrs0*w0; + results.witnesses[1] = wtrs0*(w0-epa.m_normal*epa.m_depth); + results.normal = -epa.m_normal; + results.distance = -epa.m_depth; + return(true); + } else results.status=sResults::EPA_Failed; + } + break; + case GJK::eStatus::Failed: + results.status=sResults::GJK_Failed; + break; + default: + { + } + } + return(false); +} + +#ifndef __SPU__ +// +btScalar btGjkEpaSolver2::SignedDistance(const btVector3& position, + btScalar margin, + const btConvexShape* shape0, + const btTransform& wtrs0, + sResults& results) +{ + tShape shape; + btSphereShape shape1(margin); + btTransform wtrs1(btQuaternion(0,0,0,1),position); + Initialize(shape0,wtrs0,&shape1,wtrs1,results,shape,false); + GJK gjk; + GJK::eStatus::_ gjk_status=gjk.Evaluate(shape,btVector3(1,1,1)); + if(gjk_status==GJK::eStatus::Valid) + { + btVector3 w0=btVector3(0,0,0); + btVector3 w1=btVector3(0,0,0); + for(U i=0;irank;++i) + { + const btScalar p=gjk.m_simplex->p[i]; + w0+=shape.Support( gjk.m_simplex->c[i]->d,0)*p; + w1+=shape.Support(-gjk.m_simplex->c[i]->d,1)*p; + } + results.witnesses[0] = wtrs0*w0; + results.witnesses[1] = wtrs0*w1; + const btVector3 delta= results.witnesses[1]- + results.witnesses[0]; + const btScalar margin= shape0->getMarginNonVirtual()+ + shape1.getMarginNonVirtual(); + const btScalar length= delta.length(); + results.normal = delta/length; + results.witnesses[0] += results.normal*margin; + return(length-margin); + } + else + { + if(gjk_status==GJK::eStatus::Inside) + { + if(Penetration(shape0,wtrs0,&shape1,wtrs1,gjk.m_ray,results)) + { + const btVector3 delta= results.witnesses[0]- + results.witnesses[1]; + const btScalar length= delta.length(); + if (length >= SIMD_EPSILON) + results.normal = delta/length; + return(-length); + } + } + } + return(SIMD_INFINITY); +} + +// +bool btGjkEpaSolver2::SignedDistance(const btConvexShape* shape0, + const btTransform& wtrs0, + const btConvexShape* shape1, + const btTransform& wtrs1, + const btVector3& guess, + sResults& results) +{ + if(!Distance(shape0,wtrs0,shape1,wtrs1,guess,results)) + return(Penetration(shape0,wtrs0,shape1,wtrs1,guess,results,false)); + else + return(true); +} +#endif //__SPU__ + +/* Symbols cleanup */ + +#undef GJK_MAX_ITERATIONS +#undef GJK_ACCURARY +#undef GJK_MIN_DISTANCE +#undef GJK_DUPLICATED_EPS +#undef GJK_SIMPLEX2_EPS +#undef GJK_SIMPLEX3_EPS +#undef GJK_SIMPLEX4_EPS + +#undef EPA_MAX_VERTICES +#undef EPA_MAX_FACES +#undef EPA_MAX_ITERATIONS +#undef EPA_ACCURACY +#undef EPA_FALLBACK +#undef EPA_PLANE_EPS +#undef EPA_INSIDE_EPS diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpa2.h b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpa2.h new file mode 100644 index 000000000..2296527d7 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpa2.h @@ -0,0 +1,73 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2008 Erwin Coumans http://continuousphysics.com/Bullet/ + +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. +*/ + +/* +GJK-EPA collision solver by Nathanael Presson, 2008 +*/ +#ifndef _68DA1F85_90B7_4bb0_A705_83B4040A75C6_ +#define _68DA1F85_90B7_4bb0_A705_83B4040A75C6_ +#include "BulletCollision/CollisionShapes/btConvexShape.h" + +///btGjkEpaSolver contributed under zlib by Nathanael Presson +struct btGjkEpaSolver2 +{ +struct sResults + { + enum eStatus + { + Separated, /* Shapes doesnt penetrate */ + Penetrating, /* Shapes are penetrating */ + GJK_Failed, /* GJK phase fail, no big issue, shapes are probably just 'touching' */ + EPA_Failed /* EPA phase fail, bigger problem, need to save parameters, and debug */ + } status; + btVector3 witnesses[2]; + btVector3 normal; + btScalar distance; + }; + +static int StackSizeRequirement(); + +static bool Distance( const btConvexShape* shape0,const btTransform& wtrs0, + const btConvexShape* shape1,const btTransform& wtrs1, + const btVector3& guess, + sResults& results); + +static bool Penetration(const btConvexShape* shape0,const btTransform& wtrs0, + const btConvexShape* shape1,const btTransform& wtrs1, + const btVector3& guess, + sResults& results, + bool usemargins=true); +#ifndef __SPU__ +static btScalar SignedDistance( const btVector3& position, + btScalar margin, + const btConvexShape* shape, + const btTransform& wtrs, + sResults& results); + +static bool SignedDistance( const btConvexShape* shape0,const btTransform& wtrs0, + const btConvexShape* shape1,const btTransform& wtrs1, + const btVector3& guess, + sResults& results); +#endif //__SPU__ + +}; + +#endif diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.cpp b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.cpp new file mode 100644 index 000000000..c6dc3f3a6 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.cpp @@ -0,0 +1,66 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +EPA Copyright (c) Ricardo Padrela 2006 + +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 "BulletCollision/CollisionShapes/btConvexShape.h" +#include "btGjkEpaPenetrationDepthSolver.h" + + +#include "BulletCollision/NarrowPhaseCollision/btGjkEpa2.h" + +bool btGjkEpaPenetrationDepthSolver::calcPenDepth( btSimplexSolverInterface& simplexSolver, + const btConvexShape* pConvexA, const btConvexShape* pConvexB, + const btTransform& transformA, const btTransform& transformB, + btVector3& v, btVector3& wWitnessOnA, btVector3& wWitnessOnB, + class btIDebugDraw* debugDraw, btStackAlloc* stackAlloc ) +{ + + (void)debugDraw; + (void)v; + (void)simplexSolver; + +// const btScalar radialmargin(btScalar(0.)); + + btVector3 guessVector(transformA.getOrigin()-transformB.getOrigin()); + btGjkEpaSolver2::sResults results; + + + if(btGjkEpaSolver2::Penetration(pConvexA,transformA, + pConvexB,transformB, + guessVector,results)) + + { + // debugDraw->drawLine(results.witnesses[1],results.witnesses[1]+results.normal,btVector3(255,0,0)); + //resultOut->addContactPoint(results.normal,results.witnesses[1],-results.depth); + wWitnessOnA = results.witnesses[0]; + wWitnessOnB = results.witnesses[1]; + v = results.normal; + return true; + } else + { + if(btGjkEpaSolver2::Distance(pConvexA,transformA,pConvexB,transformB,guessVector,results)) + { + wWitnessOnA = results.witnesses[0]; + wWitnessOnB = results.witnesses[1]; + v = results.normal; + return false; + } + } + + return false; +} + + diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h new file mode 100644 index 000000000..a49689a15 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h @@ -0,0 +1,43 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +EPA Copyright (c) Ricardo Padrela 2006 + +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 BT_GJP_EPA_PENETRATION_DEPTH_H +#define BT_GJP_EPA_PENETRATION_DEPTH_H + +#include "btConvexPenetrationDepthSolver.h" + +///EpaPenetrationDepthSolver uses the Expanding Polytope Algorithm to +///calculate the penetration depth between two convex shapes. +class btGjkEpaPenetrationDepthSolver : public btConvexPenetrationDepthSolver +{ + public : + + btGjkEpaPenetrationDepthSolver() + { + } + + bool calcPenDepth( btSimplexSolverInterface& simplexSolver, + const btConvexShape* pConvexA, const btConvexShape* pConvexB, + const btTransform& transformA, const btTransform& transformB, + btVector3& v, btVector3& wWitnessOnA, btVector3& wWitnessOnB, + class btIDebugDraw* debugDraw,btStackAlloc* stackAlloc ); + + private : + +}; + +#endif // BT_GJP_EPA_PENETRATION_DEPTH_H + diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.cpp b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.cpp new file mode 100644 index 000000000..1a5619573 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.cpp @@ -0,0 +1,456 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btGjkPairDetector.h" +#include "BulletCollision/CollisionShapes/btConvexShape.h" +#include "BulletCollision/NarrowPhaseCollision/btSimplexSolverInterface.h" +#include "BulletCollision/NarrowPhaseCollision/btConvexPenetrationDepthSolver.h" + + + +#if defined(DEBUG) || defined (_DEBUG) +//#define TEST_NON_VIRTUAL 1 +#include //for debug printf +#ifdef __SPU__ +#include +#define printf spu_printf +//#define DEBUG_SPU_COLLISION_DETECTION 1 +#endif //__SPU__ +#endif + +//must be above the machine epsilon +#define REL_ERROR2 btScalar(1.0e-6) + +//temp globals, to improve GJK/EPA/penetration calculations +int gNumDeepPenetrationChecks = 0; +int gNumGjkChecks = 0; + + +btGjkPairDetector::btGjkPairDetector(const btConvexShape* objectA,const btConvexShape* objectB,btSimplexSolverInterface* simplexSolver,btConvexPenetrationDepthSolver* penetrationDepthSolver) +:m_cachedSeparatingAxis(btScalar(0.),btScalar(1.),btScalar(0.)), +m_penetrationDepthSolver(penetrationDepthSolver), +m_simplexSolver(simplexSolver), +m_minkowskiA(objectA), +m_minkowskiB(objectB), +m_shapeTypeA(objectA->getShapeType()), +m_shapeTypeB(objectB->getShapeType()), +m_marginA(objectA->getMargin()), +m_marginB(objectB->getMargin()), +m_ignoreMargin(false), +m_lastUsedMethod(-1), +m_catchDegeneracies(1) +{ +} +btGjkPairDetector::btGjkPairDetector(const btConvexShape* objectA,const btConvexShape* objectB,int shapeTypeA,int shapeTypeB,btScalar marginA, btScalar marginB, btSimplexSolverInterface* simplexSolver,btConvexPenetrationDepthSolver* penetrationDepthSolver) +:m_cachedSeparatingAxis(btScalar(0.),btScalar(1.),btScalar(0.)), +m_penetrationDepthSolver(penetrationDepthSolver), +m_simplexSolver(simplexSolver), +m_minkowskiA(objectA), +m_minkowskiB(objectB), +m_shapeTypeA(shapeTypeA), +m_shapeTypeB(shapeTypeB), +m_marginA(marginA), +m_marginB(marginB), +m_ignoreMargin(false), +m_lastUsedMethod(-1), +m_catchDegeneracies(1) +{ +} + +void btGjkPairDetector::getClosestPoints(const ClosestPointInput& input,Result& output,class btIDebugDraw* debugDraw,bool swapResults) +{ + (void)swapResults; + + getClosestPointsNonVirtual(input,output,debugDraw); +} + +#ifdef __SPU__ +void btGjkPairDetector::getClosestPointsNonVirtual(const ClosestPointInput& input,Result& output,class btIDebugDraw* debugDraw) +#else +void btGjkPairDetector::getClosestPointsNonVirtual(const ClosestPointInput& input,Result& output,class btIDebugDraw* debugDraw) +#endif +{ + m_cachedSeparatingDistance = 0.f; + + btScalar distance=btScalar(0.); + btVector3 normalInB(btScalar(0.),btScalar(0.),btScalar(0.)); + btVector3 pointOnA,pointOnB; + btTransform localTransA = input.m_transformA; + btTransform localTransB = input.m_transformB; + btVector3 positionOffset = (localTransA.getOrigin() + localTransB.getOrigin()) * btScalar(0.5); + localTransA.getOrigin() -= positionOffset; + localTransB.getOrigin() -= positionOffset; + + bool check2d = m_minkowskiA->isConvex2d() && m_minkowskiB->isConvex2d(); + + btScalar marginA = m_marginA; + btScalar marginB = m_marginB; + + gNumGjkChecks++; + +#ifdef DEBUG_SPU_COLLISION_DETECTION + spu_printf("inside gjk\n"); +#endif + //for CCD we don't use margins + if (m_ignoreMargin) + { + marginA = btScalar(0.); + marginB = btScalar(0.); +#ifdef DEBUG_SPU_COLLISION_DETECTION + spu_printf("ignoring margin\n"); +#endif + } + + m_curIter = 0; + int gGjkMaxIter = 1000;//this is to catch invalid input, perhaps check for #NaN? + m_cachedSeparatingAxis.setValue(0,1,0); + + bool isValid = false; + bool checkSimplex = false; + bool checkPenetration = true; + m_degenerateSimplex = 0; + + m_lastUsedMethod = -1; + + { + btScalar squaredDistance = BT_LARGE_FLOAT; + btScalar delta = btScalar(0.); + + btScalar margin = marginA + marginB; + + + + m_simplexSolver->reset(); + + for ( ; ; ) + //while (true) + { + + btVector3 seperatingAxisInA = (-m_cachedSeparatingAxis)* input.m_transformA.getBasis(); + btVector3 seperatingAxisInB = m_cachedSeparatingAxis* input.m_transformB.getBasis(); + +#if 1 + + btVector3 pInA = m_minkowskiA->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInA); + btVector3 qInB = m_minkowskiB->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInB); + +// btVector3 pInA = localGetSupportingVertexWithoutMargin(m_shapeTypeA, m_minkowskiA, seperatingAxisInA,input.m_convexVertexData[0]);//, &featureIndexA); +// btVector3 qInB = localGetSupportingVertexWithoutMargin(m_shapeTypeB, m_minkowskiB, seperatingAxisInB,input.m_convexVertexData[1]);//, &featureIndexB); + +#else +#ifdef __SPU__ + btVector3 pInA = m_minkowskiA->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInA); + btVector3 qInB = m_minkowskiB->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInB); +#else + btVector3 pInA = m_minkowskiA->localGetSupportingVertexWithoutMargin(seperatingAxisInA); + btVector3 qInB = m_minkowskiB->localGetSupportingVertexWithoutMargin(seperatingAxisInB); +#ifdef TEST_NON_VIRTUAL + btVector3 pInAv = m_minkowskiA->localGetSupportingVertexWithoutMargin(seperatingAxisInA); + btVector3 qInBv = m_minkowskiB->localGetSupportingVertexWithoutMargin(seperatingAxisInB); + btAssert((pInAv-pInA).length() < 0.0001); + btAssert((qInBv-qInB).length() < 0.0001); +#endif // +#endif //__SPU__ +#endif + + + btVector3 pWorld = localTransA(pInA); + btVector3 qWorld = localTransB(qInB); + +#ifdef DEBUG_SPU_COLLISION_DETECTION + spu_printf("got local supporting vertices\n"); +#endif + + if (check2d) + { + pWorld[2] = 0.f; + qWorld[2] = 0.f; + } + + btVector3 w = pWorld - qWorld; + delta = m_cachedSeparatingAxis.dot(w); + + // potential exit, they don't overlap + if ((delta > btScalar(0.0)) && (delta * delta > squaredDistance * input.m_maximumDistanceSquared)) + { + m_degenerateSimplex = 10; + checkSimplex=true; + //checkPenetration = false; + break; + } + + //exit 0: the new point is already in the simplex, or we didn't come any closer + if (m_simplexSolver->inSimplex(w)) + { + m_degenerateSimplex = 1; + checkSimplex = true; + break; + } + // are we getting any closer ? + btScalar f0 = squaredDistance - delta; + btScalar f1 = squaredDistance * REL_ERROR2; + + if (f0 <= f1) + { + if (f0 <= btScalar(0.)) + { + m_degenerateSimplex = 2; + } else + { + m_degenerateSimplex = 11; + } + checkSimplex = true; + break; + } + +#ifdef DEBUG_SPU_COLLISION_DETECTION + spu_printf("addVertex 1\n"); +#endif + //add current vertex to simplex + m_simplexSolver->addVertex(w, pWorld, qWorld); +#ifdef DEBUG_SPU_COLLISION_DETECTION + spu_printf("addVertex 2\n"); +#endif + btVector3 newCachedSeparatingAxis; + + //calculate the closest point to the origin (update vector v) + if (!m_simplexSolver->closest(newCachedSeparatingAxis)) + { + m_degenerateSimplex = 3; + checkSimplex = true; + break; + } + + if(newCachedSeparatingAxis.length2()previousSquaredDistance) + { + m_degenerateSimplex = 7; + squaredDistance = previousSquaredDistance; + checkSimplex = false; + break; + } +#endif // + + m_cachedSeparatingAxis = newCachedSeparatingAxis; + + //redundant m_simplexSolver->compute_points(pointOnA, pointOnB); + + //are we getting any closer ? + if (previousSquaredDistance - squaredDistance <= SIMD_EPSILON * previousSquaredDistance) + { + m_simplexSolver->backup_closest(m_cachedSeparatingAxis); + checkSimplex = true; + m_degenerateSimplex = 12; + + break; + } + + //degeneracy, this is typically due to invalid/uninitialized worldtransforms for a btCollisionObject + if (m_curIter++ > gGjkMaxIter) + { + #if defined(DEBUG) || defined (_DEBUG) || defined (DEBUG_SPU_COLLISION_DETECTION) + + printf("btGjkPairDetector maxIter exceeded:%i\n",m_curIter); + printf("sepAxis=(%f,%f,%f), squaredDistance = %f, shapeTypeA=%i,shapeTypeB=%i\n", + m_cachedSeparatingAxis.getX(), + m_cachedSeparatingAxis.getY(), + m_cachedSeparatingAxis.getZ(), + squaredDistance, + m_minkowskiA->getShapeType(), + m_minkowskiB->getShapeType()); + + #endif + break; + + } + + + bool check = (!m_simplexSolver->fullSimplex()); + //bool check = (!m_simplexSolver->fullSimplex() && squaredDistance > SIMD_EPSILON * m_simplexSolver->maxVertex()); + + if (!check) + { + //do we need this backup_closest here ? + m_simplexSolver->backup_closest(m_cachedSeparatingAxis); + m_degenerateSimplex = 13; + break; + } + } + + if (checkSimplex) + { + m_simplexSolver->compute_points(pointOnA, pointOnB); + normalInB = pointOnA-pointOnB; + btScalar lenSqr =m_cachedSeparatingAxis.length2(); + + //valid normal + if (lenSqr < 0.0001) + { + m_degenerateSimplex = 5; + } + if (lenSqr > SIMD_EPSILON*SIMD_EPSILON) + { + btScalar rlen = btScalar(1.) / btSqrt(lenSqr ); + normalInB *= rlen; //normalize + btScalar s = btSqrt(squaredDistance); + + btAssert(s > btScalar(0.0)); + pointOnA -= m_cachedSeparatingAxis * (marginA / s); + pointOnB += m_cachedSeparatingAxis * (marginB / s); + distance = ((btScalar(1.)/rlen) - margin); + isValid = true; + + m_lastUsedMethod = 1; + } else + { + m_lastUsedMethod = 2; + } + } + + bool catchDegeneratePenetrationCase = + (m_catchDegeneracies && m_penetrationDepthSolver && m_degenerateSimplex && ((distance+margin) < 0.01)); + + //if (checkPenetration && !isValid) + if (checkPenetration && (!isValid || catchDegeneratePenetrationCase )) + { + //penetration case + + //if there is no way to handle penetrations, bail out + if (m_penetrationDepthSolver) + { + // Penetration depth case. + btVector3 tmpPointOnA,tmpPointOnB; + + gNumDeepPenetrationChecks++; + m_cachedSeparatingAxis.setZero(); + + bool isValid2 = m_penetrationDepthSolver->calcPenDepth( + *m_simplexSolver, + m_minkowskiA,m_minkowskiB, + localTransA,localTransB, + m_cachedSeparatingAxis, tmpPointOnA, tmpPointOnB, + debugDraw,input.m_stackAlloc + ); + + + if (isValid2) + { + btVector3 tmpNormalInB = tmpPointOnB-tmpPointOnA; + btScalar lenSqr = tmpNormalInB.length2(); + if (lenSqr <= (SIMD_EPSILON*SIMD_EPSILON)) + { + tmpNormalInB = m_cachedSeparatingAxis; + lenSqr = m_cachedSeparatingAxis.length2(); + } + + if (lenSqr > (SIMD_EPSILON*SIMD_EPSILON)) + { + tmpNormalInB /= btSqrt(lenSqr); + btScalar distance2 = -(tmpPointOnA-tmpPointOnB).length(); + //only replace valid penetrations when the result is deeper (check) + if (!isValid || (distance2 < distance)) + { + distance = distance2; + pointOnA = tmpPointOnA; + pointOnB = tmpPointOnB; + normalInB = tmpNormalInB; + isValid = true; + m_lastUsedMethod = 3; + } else + { + m_lastUsedMethod = 8; + } + } else + { + m_lastUsedMethod = 9; + } + } else + + { + ///this is another degenerate case, where the initial GJK calculation reports a degenerate case + ///EPA reports no penetration, and the second GJK (using the supporting vector without margin) + ///reports a valid positive distance. Use the results of the second GJK instead of failing. + ///thanks to Jacob.Langford for the reproduction case + ///http://code.google.com/p/bullet/issues/detail?id=250 + + + if (m_cachedSeparatingAxis.length2() > btScalar(0.)) + { + btScalar distance2 = (tmpPointOnA-tmpPointOnB).length()-margin; + //only replace valid distances when the distance is less + if (!isValid || (distance2 < distance)) + { + distance = distance2; + pointOnA = tmpPointOnA; + pointOnB = tmpPointOnB; + pointOnA -= m_cachedSeparatingAxis * marginA ; + pointOnB += m_cachedSeparatingAxis * marginB ; + normalInB = m_cachedSeparatingAxis; + normalInB.normalize(); + isValid = true; + m_lastUsedMethod = 6; + } else + { + m_lastUsedMethod = 5; + } + } + } + + } + + } + } + + + + if (isValid && ((distance < 0) || (distance*distance < input.m_maximumDistanceSquared))) + { +#if 0 +///some debugging +// if (check2d) + { + printf("n = %2.3f,%2.3f,%2.3f. ",normalInB[0],normalInB[1],normalInB[2]); + printf("distance = %2.3f exit=%d deg=%d\n",distance,m_lastUsedMethod,m_degenerateSimplex); + } +#endif + + m_cachedSeparatingAxis = normalInB; + m_cachedSeparatingDistance = distance; + + output.addContactPoint( + normalInB, + pointOnB+positionOffset, + distance); + + } + + +} + + + + + diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h new file mode 100644 index 000000000..cc6287c86 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h @@ -0,0 +1,103 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 GJK_PAIR_DETECTOR_H +#define GJK_PAIR_DETECTOR_H + +#include "btDiscreteCollisionDetectorInterface.h" +#include "BulletCollision/CollisionShapes/btCollisionMargin.h" + +class btConvexShape; +#include "btSimplexSolverInterface.h" +class btConvexPenetrationDepthSolver; + +/// btGjkPairDetector uses GJK to implement the btDiscreteCollisionDetectorInterface +class btGjkPairDetector : public btDiscreteCollisionDetectorInterface +{ + + + btVector3 m_cachedSeparatingAxis; + btConvexPenetrationDepthSolver* m_penetrationDepthSolver; + btSimplexSolverInterface* m_simplexSolver; + const btConvexShape* m_minkowskiA; + const btConvexShape* m_minkowskiB; + int m_shapeTypeA; + int m_shapeTypeB; + btScalar m_marginA; + btScalar m_marginB; + + bool m_ignoreMargin; + btScalar m_cachedSeparatingDistance; + + +public: + + //some debugging to fix degeneracy problems + int m_lastUsedMethod; + int m_curIter; + int m_degenerateSimplex; + int m_catchDegeneracies; + + + btGjkPairDetector(const btConvexShape* objectA,const btConvexShape* objectB,btSimplexSolverInterface* simplexSolver,btConvexPenetrationDepthSolver* penetrationDepthSolver); + btGjkPairDetector(const btConvexShape* objectA,const btConvexShape* objectB,int shapeTypeA,int shapeTypeB,btScalar marginA, btScalar marginB, btSimplexSolverInterface* simplexSolver,btConvexPenetrationDepthSolver* penetrationDepthSolver); + virtual ~btGjkPairDetector() {}; + + virtual void getClosestPoints(const ClosestPointInput& input,Result& output,class btIDebugDraw* debugDraw,bool swapResults=false); + + void getClosestPointsNonVirtual(const ClosestPointInput& input,Result& output,class btIDebugDraw* debugDraw); + + + void setMinkowskiA(btConvexShape* minkA) + { + m_minkowskiA = minkA; + } + + void setMinkowskiB(btConvexShape* minkB) + { + m_minkowskiB = minkB; + } + void setCachedSeperatingAxis(const btVector3& seperatingAxis) + { + m_cachedSeparatingAxis = seperatingAxis; + } + + const btVector3& getCachedSeparatingAxis() const + { + return m_cachedSeparatingAxis; + } + btScalar getCachedSeparatingDistance() const + { + return m_cachedSeparatingDistance; + } + + void setPenetrationDepthSolver(btConvexPenetrationDepthSolver* penetrationDepthSolver) + { + m_penetrationDepthSolver = penetrationDepthSolver; + } + + ///don't use setIgnoreMargin, it's for Bullet's internal use + void setIgnoreMargin(bool ignoreMargin) + { + m_ignoreMargin = ignoreMargin; + } + + +}; + +#endif //GJK_PAIR_DETECTOR_H diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btManifoldPoint.h b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btManifoldPoint.h new file mode 100644 index 000000000..c7c981298 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btManifoldPoint.h @@ -0,0 +1,125 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 MANIFOLD_CONTACT_POINT_H +#define MANIFOLD_CONTACT_POINT_H + +#include "LinearMath/btVector3.h" +#include "LinearMath/btTransformUtil.h" + + + + + +/// ManifoldContactPoint collects and maintains persistent contactpoints. +/// used to improve stability and performance of rigidbody dynamics response. +class btManifoldPoint + { + public: + btManifoldPoint() + :m_userPersistentData(0), + m_appliedImpulse(0.f), + m_lateralFrictionInitialized(false), + m_appliedImpulseLateral1(0.f), + m_appliedImpulseLateral2(0.f), + m_lifeTime(0) + { + } + + btManifoldPoint( const btVector3 &pointA, const btVector3 &pointB, + const btVector3 &normal, + btScalar distance ) : + m_localPointA( pointA ), + m_localPointB( pointB ), + m_normalWorldOnB( normal ), + m_distance1( distance ), + m_combinedFriction(btScalar(0.)), + m_combinedRestitution(btScalar(0.)), + m_userPersistentData(0), + m_appliedImpulse(0.f), + m_lateralFrictionInitialized(false), + m_appliedImpulseLateral1(0.f), + m_appliedImpulseLateral2(0.f), + m_lifeTime(0) + { + + + } + + + + btVector3 m_localPointA; + btVector3 m_localPointB; + btVector3 m_positionWorldOnB; + ///m_positionWorldOnA is redundant information, see getPositionWorldOnA(), but for clarity + btVector3 m_positionWorldOnA; + btVector3 m_normalWorldOnB; + + btScalar m_distance1; + btScalar m_combinedFriction; + btScalar m_combinedRestitution; + + //BP mod, store contact triangles. + int m_partId0; + int m_partId1; + int m_index0; + int m_index1; + + mutable void* m_userPersistentData; + btScalar m_appliedImpulse; + + bool m_lateralFrictionInitialized; + btScalar m_appliedImpulseLateral1; + btScalar m_appliedImpulseLateral2; + int m_lifeTime;//lifetime of the contactpoint in frames + + btVector3 m_lateralFrictionDir1; + btVector3 m_lateralFrictionDir2; + + btScalar getDistance() const + { + return m_distance1; + } + int getLifeTime() const + { + return m_lifeTime; + } + + const btVector3& getPositionWorldOnA() const { + return m_positionWorldOnA; +// return m_positionWorldOnB + m_normalWorldOnB * m_distance1; + } + + const btVector3& getPositionWorldOnB() const + { + return m_positionWorldOnB; + } + + void setDistance(btScalar dist) + { + m_distance1 = dist; + } + + ///this returns the most recent applied impulse, to satisfy contact constraints by the constraint solver + btScalar getAppliedImpulse() const + { + return m_appliedImpulse; + } + + + + }; + +#endif //MANIFOLD_CONTACT_POINT_H diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.cpp b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.cpp new file mode 100644 index 000000000..8b8238251 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.cpp @@ -0,0 +1,357 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btMinkowskiPenetrationDepthSolver.h" +#include "BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.h" +#include "BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.h" +#include "BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h" +#include "BulletCollision/CollisionShapes/btConvexShape.h" + +#define NUM_UNITSPHERE_POINTS 42 +static btVector3 sPenetrationDirections[NUM_UNITSPHERE_POINTS+MAX_PREFERRED_PENETRATION_DIRECTIONS*2] = +{ +btVector3(btScalar(0.000000) , btScalar(-0.000000),btScalar(-1.000000)), +btVector3(btScalar(0.723608) , btScalar(-0.525725),btScalar(-0.447219)), +btVector3(btScalar(-0.276388) , btScalar(-0.850649),btScalar(-0.447219)), +btVector3(btScalar(-0.894426) , btScalar(-0.000000),btScalar(-0.447216)), +btVector3(btScalar(-0.276388) , btScalar(0.850649),btScalar(-0.447220)), +btVector3(btScalar(0.723608) , btScalar(0.525725),btScalar(-0.447219)), +btVector3(btScalar(0.276388) , btScalar(-0.850649),btScalar(0.447220)), +btVector3(btScalar(-0.723608) , btScalar(-0.525725),btScalar(0.447219)), +btVector3(btScalar(-0.723608) , btScalar(0.525725),btScalar(0.447219)), +btVector3(btScalar(0.276388) , btScalar(0.850649),btScalar(0.447219)), +btVector3(btScalar(0.894426) , btScalar(0.000000),btScalar(0.447216)), +btVector3(btScalar(-0.000000) , btScalar(0.000000),btScalar(1.000000)), +btVector3(btScalar(0.425323) , btScalar(-0.309011),btScalar(-0.850654)), +btVector3(btScalar(-0.162456) , btScalar(-0.499995),btScalar(-0.850654)), +btVector3(btScalar(0.262869) , btScalar(-0.809012),btScalar(-0.525738)), +btVector3(btScalar(0.425323) , btScalar(0.309011),btScalar(-0.850654)), +btVector3(btScalar(0.850648) , btScalar(-0.000000),btScalar(-0.525736)), +btVector3(btScalar(-0.525730) , btScalar(-0.000000),btScalar(-0.850652)), +btVector3(btScalar(-0.688190) , btScalar(-0.499997),btScalar(-0.525736)), +btVector3(btScalar(-0.162456) , btScalar(0.499995),btScalar(-0.850654)), +btVector3(btScalar(-0.688190) , btScalar(0.499997),btScalar(-0.525736)), +btVector3(btScalar(0.262869) , btScalar(0.809012),btScalar(-0.525738)), +btVector3(btScalar(0.951058) , btScalar(0.309013),btScalar(0.000000)), +btVector3(btScalar(0.951058) , btScalar(-0.309013),btScalar(0.000000)), +btVector3(btScalar(0.587786) , btScalar(-0.809017),btScalar(0.000000)), +btVector3(btScalar(0.000000) , btScalar(-1.000000),btScalar(0.000000)), +btVector3(btScalar(-0.587786) , btScalar(-0.809017),btScalar(0.000000)), +btVector3(btScalar(-0.951058) , btScalar(-0.309013),btScalar(-0.000000)), +btVector3(btScalar(-0.951058) , btScalar(0.309013),btScalar(-0.000000)), +btVector3(btScalar(-0.587786) , btScalar(0.809017),btScalar(-0.000000)), +btVector3(btScalar(-0.000000) , btScalar(1.000000),btScalar(-0.000000)), +btVector3(btScalar(0.587786) , btScalar(0.809017),btScalar(-0.000000)), +btVector3(btScalar(0.688190) , btScalar(-0.499997),btScalar(0.525736)), +btVector3(btScalar(-0.262869) , btScalar(-0.809012),btScalar(0.525738)), +btVector3(btScalar(-0.850648) , btScalar(0.000000),btScalar(0.525736)), +btVector3(btScalar(-0.262869) , btScalar(0.809012),btScalar(0.525738)), +btVector3(btScalar(0.688190) , btScalar(0.499997),btScalar(0.525736)), +btVector3(btScalar(0.525730) , btScalar(0.000000),btScalar(0.850652)), +btVector3(btScalar(0.162456) , btScalar(-0.499995),btScalar(0.850654)), +btVector3(btScalar(-0.425323) , btScalar(-0.309011),btScalar(0.850654)), +btVector3(btScalar(-0.425323) , btScalar(0.309011),btScalar(0.850654)), +btVector3(btScalar(0.162456) , btScalar(0.499995),btScalar(0.850654)) +}; + + +bool btMinkowskiPenetrationDepthSolver::calcPenDepth(btSimplexSolverInterface& simplexSolver, + const btConvexShape* convexA,const btConvexShape* convexB, + const btTransform& transA,const btTransform& transB, + btVector3& v, btVector3& pa, btVector3& pb, + class btIDebugDraw* debugDraw,btStackAlloc* stackAlloc + ) +{ + + (void)stackAlloc; + (void)v; + + bool check2d= convexA->isConvex2d() && convexB->isConvex2d(); + + struct btIntermediateResult : public btDiscreteCollisionDetectorInterface::Result + { + + btIntermediateResult():m_hasResult(false) + { + } + + btVector3 m_normalOnBInWorld; + btVector3 m_pointInWorld; + btScalar m_depth; + bool m_hasResult; + + virtual void setShapeIdentifiersA(int partId0,int index0) + { + (void)partId0; + (void)index0; + } + virtual void setShapeIdentifiersB(int partId1,int index1) + { + (void)partId1; + (void)index1; + } + void addContactPoint(const btVector3& normalOnBInWorld,const btVector3& pointInWorld,btScalar depth) + { + m_normalOnBInWorld = normalOnBInWorld; + m_pointInWorld = pointInWorld; + m_depth = depth; + m_hasResult = true; + } + }; + + //just take fixed number of orientation, and sample the penetration depth in that direction + btScalar minProj = btScalar(BT_LARGE_FLOAT); + btVector3 minNorm(btScalar(0.), btScalar(0.), btScalar(0.)); + btVector3 minA,minB; + btVector3 seperatingAxisInA,seperatingAxisInB; + btVector3 pInA,qInB,pWorld,qWorld,w; + +#ifndef __SPU__ +#define USE_BATCHED_SUPPORT 1 +#endif +#ifdef USE_BATCHED_SUPPORT + + btVector3 supportVerticesABatch[NUM_UNITSPHERE_POINTS+MAX_PREFERRED_PENETRATION_DIRECTIONS*2]; + btVector3 supportVerticesBBatch[NUM_UNITSPHERE_POINTS+MAX_PREFERRED_PENETRATION_DIRECTIONS*2]; + btVector3 seperatingAxisInABatch[NUM_UNITSPHERE_POINTS+MAX_PREFERRED_PENETRATION_DIRECTIONS*2]; + btVector3 seperatingAxisInBBatch[NUM_UNITSPHERE_POINTS+MAX_PREFERRED_PENETRATION_DIRECTIONS*2]; + int i; + + int numSampleDirections = NUM_UNITSPHERE_POINTS; + + for (i=0;igetNumPreferredPenetrationDirections(); + if (numPDA) + { + for (int i=0;igetPreferredPenetrationDirection(i,norm); + norm = transA.getBasis() * norm; + sPenetrationDirections[numSampleDirections] = norm; + seperatingAxisInABatch[numSampleDirections] = (-norm) * transA.getBasis(); + seperatingAxisInBBatch[numSampleDirections] = norm * transB.getBasis(); + numSampleDirections++; + } + } + } + + { + int numPDB = convexB->getNumPreferredPenetrationDirections(); + if (numPDB) + { + for (int i=0;igetPreferredPenetrationDirection(i,norm); + norm = transB.getBasis() * norm; + sPenetrationDirections[numSampleDirections] = norm; + seperatingAxisInABatch[numSampleDirections] = (-norm) * transA.getBasis(); + seperatingAxisInBBatch[numSampleDirections] = norm * transB.getBasis(); + numSampleDirections++; + } + } + } + + + + + convexA->batchedUnitVectorGetSupportingVertexWithoutMargin(seperatingAxisInABatch,supportVerticesABatch,numSampleDirections); + convexB->batchedUnitVectorGetSupportingVertexWithoutMargin(seperatingAxisInBBatch,supportVerticesBBatch,numSampleDirections); + + for (i=0;i0.01) + { + + seperatingAxisInA = seperatingAxisInABatch[i]; + seperatingAxisInB = seperatingAxisInBBatch[i]; + + pInA = supportVerticesABatch[i]; + qInB = supportVerticesBBatch[i]; + + pWorld = transA(pInA); + qWorld = transB(qInB); + if (check2d) + { + pWorld[2] = 0.f; + qWorld[2] = 0.f; + } + + w = qWorld - pWorld; + btScalar delta = norm.dot(w); + //find smallest delta + if (delta < minProj) + { + minProj = delta; + minNorm = norm; + minA = pWorld; + minB = qWorld; + } + } + } +#else + + int numSampleDirections = NUM_UNITSPHERE_POINTS; + +#ifndef __SPU__ + { + int numPDA = convexA->getNumPreferredPenetrationDirections(); + if (numPDA) + { + for (int i=0;igetPreferredPenetrationDirection(i,norm); + norm = transA.getBasis() * norm; + sPenetrationDirections[numSampleDirections] = norm; + numSampleDirections++; + } + } + } + + { + int numPDB = convexB->getNumPreferredPenetrationDirections(); + if (numPDB) + { + for (int i=0;igetPreferredPenetrationDirection(i,norm); + norm = transB.getBasis() * norm; + sPenetrationDirections[numSampleDirections] = norm; + numSampleDirections++; + } + } + } +#endif // __SPU__ + + for (int i=0;ilocalGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInA); + qInB = convexB->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInB); + pWorld = transA(pInA); + qWorld = transB(qInB); + w = qWorld - pWorld; + btScalar delta = norm.dot(w); + //find smallest delta + if (delta < minProj) + { + minProj = delta; + minNorm = norm; + minA = pWorld; + minB = qWorld; + } + } +#endif //USE_BATCHED_SUPPORT + + //add the margins + + minA += minNorm*convexA->getMarginNonVirtual(); + minB -= minNorm*convexB->getMarginNonVirtual(); + //no penetration + if (minProj < btScalar(0.)) + return false; + + btScalar extraSeparation = 0.5f;///scale dependent + minProj += extraSeparation+(convexA->getMarginNonVirtual() + convexB->getMarginNonVirtual()); + + + + + +//#define DEBUG_DRAW 1 +#ifdef DEBUG_DRAW + if (debugDraw) + { + btVector3 color(0,1,0); + debugDraw->drawLine(minA,minB,color); + color = btVector3 (1,1,1); + btVector3 vec = minB-minA; + btScalar prj2 = minNorm.dot(vec); + debugDraw->drawLine(minA,minA+(minNorm*minProj),color); + + } +#endif //DEBUG_DRAW + + + + btGjkPairDetector gjkdet(convexA,convexB,&simplexSolver,0); + + btScalar offsetDist = minProj; + btVector3 offset = minNorm * offsetDist; + + + + btGjkPairDetector::ClosestPointInput input; + + btVector3 newOrg = transA.getOrigin() + offset; + + btTransform displacedTrans = transA; + displacedTrans.setOrigin(newOrg); + + input.m_transformA = displacedTrans; + input.m_transformB = transB; + input.m_maximumDistanceSquared = btScalar(BT_LARGE_FLOAT);//minProj; + + btIntermediateResult res; + gjkdet.setCachedSeperatingAxis(-minNorm); + gjkdet.getClosestPoints(input,res,debugDraw); + + btScalar correctedMinNorm = minProj - res.m_depth; + + + //the penetration depth is over-estimated, relax it + btScalar penetration_relaxation= btScalar(1.); + minNorm*=penetration_relaxation; + + + if (res.m_hasResult) + { + + pa = res.m_pointInWorld - minNorm * correctedMinNorm; + pb = res.m_pointInWorld; + v = minNorm; + +#ifdef DEBUG_DRAW + if (debugDraw) + { + btVector3 color(1,0,0); + debugDraw->drawLine(pa,pb,color); + } +#endif//DEBUG_DRAW + + + } + return res.m_hasResult; +} + + + diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.h b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.h new file mode 100644 index 000000000..23cbd57ac --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.h @@ -0,0 +1,36 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 MINKOWSKI_PENETRATION_DEPTH_SOLVER_H +#define MINKOWSKI_PENETRATION_DEPTH_SOLVER_H + +#include "btConvexPenetrationDepthSolver.h" + +///MinkowskiPenetrationDepthSolver implements bruteforce penetration depth estimation. +///Implementation is based on sampling the depth using support mapping, and using GJK step to get the witness points. +class btMinkowskiPenetrationDepthSolver : public btConvexPenetrationDepthSolver +{ +public: + + virtual bool calcPenDepth( btSimplexSolverInterface& simplexSolver, + const btConvexShape* convexA,const btConvexShape* convexB, + const btTransform& transA,const btTransform& transB, + btVector3& v, btVector3& pa, btVector3& pb, + class btIDebugDraw* debugDraw,btStackAlloc* stackAlloc + ); +}; + +#endif //MINKOWSKI_PENETRATION_DEPTH_SOLVER_H + diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btPersistentManifold.cpp b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btPersistentManifold.cpp new file mode 100644 index 000000000..924a8af87 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btPersistentManifold.cpp @@ -0,0 +1,260 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btPersistentManifold.h" +#include "LinearMath/btTransform.h" + + +btScalar gContactBreakingThreshold = btScalar(0.02); +ContactDestroyedCallback gContactDestroyedCallback = 0; +ContactProcessedCallback gContactProcessedCallback = 0; + + + +btPersistentManifold::btPersistentManifold() +:btTypedObject(BT_PERSISTENT_MANIFOLD_TYPE), +m_body0(0), +m_body1(0), +m_cachedPoints (0), +m_index1a(0) +{ +} + + + + +#ifdef DEBUG_PERSISTENCY +#include +void btPersistentManifold::DebugPersistency() +{ + int i; + printf("DebugPersistency : numPoints %d\n",m_cachedPoints); + for (i=0;i1) + printf("error in clearUserCache\n"); + } + } + btAssert(occurance<=0); +#endif //DEBUG_PERSISTENCY + + if (pt.m_userPersistentData && gContactDestroyedCallback) + { + (*gContactDestroyedCallback)(pt.m_userPersistentData); + pt.m_userPersistentData = 0; + } + +#ifdef DEBUG_PERSISTENCY + DebugPersistency(); +#endif + } + + +} + + +int btPersistentManifold::sortCachedPoints(const btManifoldPoint& pt) +{ + + //calculate 4 possible cases areas, and take biggest area + //also need to keep 'deepest' + + int maxPenetrationIndex = -1; +#define KEEP_DEEPEST_POINT 1 +#ifdef KEEP_DEEPEST_POINT + btScalar maxPenetration = pt.getDistance(); + for (int i=0;i<4;i++) + { + if (m_pointCache[i].getDistance() < maxPenetration) + { + maxPenetrationIndex = i; + maxPenetration = m_pointCache[i].getDistance(); + } + } +#endif //KEEP_DEEPEST_POINT + + btScalar res0(btScalar(0.)),res1(btScalar(0.)),res2(btScalar(0.)),res3(btScalar(0.)); + if (maxPenetrationIndex != 0) + { + btVector3 a0 = pt.m_localPointA-m_pointCache[1].m_localPointA; + btVector3 b0 = m_pointCache[3].m_localPointA-m_pointCache[2].m_localPointA; + btVector3 cross = a0.cross(b0); + res0 = cross.length2(); + } + if (maxPenetrationIndex != 1) + { + btVector3 a1 = pt.m_localPointA-m_pointCache[0].m_localPointA; + btVector3 b1 = m_pointCache[3].m_localPointA-m_pointCache[2].m_localPointA; + btVector3 cross = a1.cross(b1); + res1 = cross.length2(); + } + + if (maxPenetrationIndex != 2) + { + btVector3 a2 = pt.m_localPointA-m_pointCache[0].m_localPointA; + btVector3 b2 = m_pointCache[3].m_localPointA-m_pointCache[1].m_localPointA; + btVector3 cross = a2.cross(b2); + res2 = cross.length2(); + } + + if (maxPenetrationIndex != 3) + { + btVector3 a3 = pt.m_localPointA-m_pointCache[0].m_localPointA; + btVector3 b3 = m_pointCache[2].m_localPointA-m_pointCache[1].m_localPointA; + btVector3 cross = a3.cross(b3); + res3 = cross.length2(); + } + + btVector4 maxvec(res0,res1,res2,res3); + int biggestarea = maxvec.closestAxis4(); + return biggestarea; +} + + +int btPersistentManifold::getCacheEntry(const btManifoldPoint& newPoint) const +{ + btScalar shortestDist = getContactBreakingThreshold() * getContactBreakingThreshold(); + int size = getNumContacts(); + int nearestPoint = -1; + for( int i = 0; i < size; i++ ) + { + const btManifoldPoint &mp = m_pointCache[i]; + + btVector3 diffA = mp.m_localPointA- newPoint.m_localPointA; + const btScalar distToManiPoint = diffA.dot(diffA); + if( distToManiPoint < shortestDist ) + { + shortestDist = distToManiPoint; + nearestPoint = i; + } + } + return nearestPoint; +} + +int btPersistentManifold::addManifoldPoint(const btManifoldPoint& newPoint) +{ + btAssert(validContactDistance(newPoint)); + + int insertIndex = getNumContacts(); + if (insertIndex == MANIFOLD_CACHE_SIZE) + { +#if MANIFOLD_CACHE_SIZE >= 4 + //sort cache so best points come first, based on area + insertIndex = sortCachedPoints(newPoint); +#else + insertIndex = 0; +#endif + clearUserCache(m_pointCache[insertIndex]); + + } else + { + m_cachedPoints++; + + + } + if (insertIndex<0) + insertIndex=0; + + btAssert(m_pointCache[insertIndex].m_userPersistentData==0); + m_pointCache[insertIndex] = newPoint; + return insertIndex; +} + +btScalar btPersistentManifold::getContactBreakingThreshold() const +{ + return m_contactBreakingThreshold; +} + + + +void btPersistentManifold::refreshContactPoints(const btTransform& trA,const btTransform& trB) +{ + int i; +#ifdef DEBUG_PERSISTENCY + printf("refreshContactPoints posA = (%f,%f,%f) posB = (%f,%f,%f)\n", + trA.getOrigin().getX(), + trA.getOrigin().getY(), + trA.getOrigin().getZ(), + trB.getOrigin().getX(), + trB.getOrigin().getY(), + trB.getOrigin().getZ()); +#endif //DEBUG_PERSISTENCY + /// first refresh worldspace positions and distance + for (i=getNumContacts()-1;i>=0;i--) + { + btManifoldPoint &manifoldPoint = m_pointCache[i]; + manifoldPoint.m_positionWorldOnA = trA( manifoldPoint.m_localPointA ); + manifoldPoint.m_positionWorldOnB = trB( manifoldPoint.m_localPointB ); + manifoldPoint.m_distance1 = (manifoldPoint.m_positionWorldOnA - manifoldPoint.m_positionWorldOnB).dot(manifoldPoint.m_normalWorldOnB); + manifoldPoint.m_lifeTime++; + } + + /// then + btScalar distance2d; + btVector3 projectedDifference,projectedPoint; + for (i=getNumContacts()-1;i>=0;i--) + { + + btManifoldPoint &manifoldPoint = m_pointCache[i]; + //contact becomes invalid when signed distance exceeds margin (projected on contactnormal direction) + if (!validContactDistance(manifoldPoint)) + { + removeContactPoint(i); + } else + { + //contact also becomes invalid when relative movement orthogonal to normal exceeds margin + projectedPoint = manifoldPoint.m_positionWorldOnA - manifoldPoint.m_normalWorldOnB * manifoldPoint.m_distance1; + projectedDifference = manifoldPoint.m_positionWorldOnB - projectedPoint; + distance2d = projectedDifference.dot(projectedDifference); + if (distance2d > getContactBreakingThreshold()*getContactBreakingThreshold() ) + { + removeContactPoint(i); + } else + { + //contact point processed callback + if (gContactProcessedCallback) + (*gContactProcessedCallback)(manifoldPoint,m_body0,m_body1); + } + } + } +#ifdef DEBUG_PERSISTENCY + DebugPersistency(); +#endif // +} + + + + + diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btPersistentManifold.h b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btPersistentManifold.h new file mode 100644 index 000000000..7a30f92c5 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btPersistentManifold.h @@ -0,0 +1,207 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 PERSISTENT_MANIFOLD_H +#define PERSISTENT_MANIFOLD_H + + +#include "LinearMath/btVector3.h" +#include "LinearMath/btTransform.h" +#include "btManifoldPoint.h" +#include "LinearMath/btAlignedAllocator.h" + +struct btCollisionResult; + +///maximum contact breaking and merging threshold +extern btScalar gContactBreakingThreshold; + +typedef bool (*ContactDestroyedCallback)(void* userPersistentData); +typedef bool (*ContactProcessedCallback)(btManifoldPoint& cp,void* body0,void* body1); +extern ContactDestroyedCallback gContactDestroyedCallback; + + +enum btContactManifoldTypes +{ + BT_PERSISTENT_MANIFOLD_TYPE = 1, + MAX_CONTACT_MANIFOLD_TYPE +}; + +#define MANIFOLD_CACHE_SIZE 4 + +///btPersistentManifold is a contact point cache, it stays persistent as long as objects are overlapping in the broadphase. +///Those contact points are created by the collision narrow phase. +///The cache can be empty, or hold 1,2,3 or 4 points. Some collision algorithms (GJK) might only add one point at a time. +///updates/refreshes old contact points, and throw them away if necessary (distance becomes too large) +///reduces the cache to 4 points, when more then 4 points are added, using following rules: +///the contact point with deepest penetration is always kept, and it tries to maximuze the area covered by the points +///note that some pairs of objects might have more then one contact manifold. +ATTRIBUTE_ALIGNED16( class) btPersistentManifold : public btTypedObject +{ + + btManifoldPoint m_pointCache[MANIFOLD_CACHE_SIZE]; + + /// this two body pointers can point to the physics rigidbody class. + /// void* will allow any rigidbody class + void* m_body0; + void* m_body1; + int m_cachedPoints; + + btScalar m_contactBreakingThreshold; + btScalar m_contactProcessingThreshold; + + + /// sort cached points so most isolated points come first + int sortCachedPoints(const btManifoldPoint& pt); + + int findContactPoint(const btManifoldPoint* unUsed, int numUnused,const btManifoldPoint& pt); + +public: + + BT_DECLARE_ALIGNED_ALLOCATOR(); + + int m_index1a; + + btPersistentManifold(); + + btPersistentManifold(void* body0,void* body1,int , btScalar contactBreakingThreshold,btScalar contactProcessingThreshold) + : btTypedObject(BT_PERSISTENT_MANIFOLD_TYPE), + m_body0(body0),m_body1(body1),m_cachedPoints(0), + m_contactBreakingThreshold(contactBreakingThreshold), + m_contactProcessingThreshold(contactProcessingThreshold) + { + } + + SIMD_FORCE_INLINE void* getBody0() { return m_body0;} + SIMD_FORCE_INLINE void* getBody1() { return m_body1;} + + SIMD_FORCE_INLINE const void* getBody0() const { return m_body0;} + SIMD_FORCE_INLINE const void* getBody1() const { return m_body1;} + + void setBodies(void* body0,void* body1) + { + m_body0 = body0; + m_body1 = body1; + } + + void clearUserCache(btManifoldPoint& pt); + +#ifdef DEBUG_PERSISTENCY + void DebugPersistency(); +#endif // + + SIMD_FORCE_INLINE int getNumContacts() const { return m_cachedPoints;} + + SIMD_FORCE_INLINE const btManifoldPoint& getContactPoint(int index) const + { + btAssert(index < m_cachedPoints); + return m_pointCache[index]; + } + + SIMD_FORCE_INLINE btManifoldPoint& getContactPoint(int index) + { + btAssert(index < m_cachedPoints); + return m_pointCache[index]; + } + + ///@todo: get this margin from the current physics / collision environment + btScalar getContactBreakingThreshold() const; + + btScalar getContactProcessingThreshold() const + { + return m_contactProcessingThreshold; + } + + int getCacheEntry(const btManifoldPoint& newPoint) const; + + int addManifoldPoint( const btManifoldPoint& newPoint); + + void removeContactPoint (int index) + { + clearUserCache(m_pointCache[index]); + + int lastUsedIndex = getNumContacts() - 1; +// m_pointCache[index] = m_pointCache[lastUsedIndex]; + if(index != lastUsedIndex) + { + m_pointCache[index] = m_pointCache[lastUsedIndex]; + //get rid of duplicated userPersistentData pointer + m_pointCache[lastUsedIndex].m_userPersistentData = 0; + m_pointCache[lastUsedIndex].m_appliedImpulse = 0.f; + m_pointCache[lastUsedIndex].m_lateralFrictionInitialized = false; + m_pointCache[lastUsedIndex].m_appliedImpulseLateral1 = 0.f; + m_pointCache[lastUsedIndex].m_appliedImpulseLateral2 = 0.f; + m_pointCache[lastUsedIndex].m_lifeTime = 0; + } + + btAssert(m_pointCache[lastUsedIndex].m_userPersistentData==0); + m_cachedPoints--; + } + void replaceContactPoint(const btManifoldPoint& newPoint,int insertIndex) + { + btAssert(validContactDistance(newPoint)); + +#define MAINTAIN_PERSISTENCY 1 +#ifdef MAINTAIN_PERSISTENCY + int lifeTime = m_pointCache[insertIndex].getLifeTime(); + btScalar appliedImpulse = m_pointCache[insertIndex].m_appliedImpulse; + btScalar appliedLateralImpulse1 = m_pointCache[insertIndex].m_appliedImpulseLateral1; + btScalar appliedLateralImpulse2 = m_pointCache[insertIndex].m_appliedImpulseLateral2; + + btAssert(lifeTime>=0); + void* cache = m_pointCache[insertIndex].m_userPersistentData; + + m_pointCache[insertIndex] = newPoint; + + m_pointCache[insertIndex].m_userPersistentData = cache; + m_pointCache[insertIndex].m_appliedImpulse = appliedImpulse; + m_pointCache[insertIndex].m_appliedImpulseLateral1 = appliedLateralImpulse1; + m_pointCache[insertIndex].m_appliedImpulseLateral2 = appliedLateralImpulse2; + + m_pointCache[insertIndex].m_lifeTime = lifeTime; +#else + clearUserCache(m_pointCache[insertIndex]); + m_pointCache[insertIndex] = newPoint; + +#endif + } + + bool validContactDistance(const btManifoldPoint& pt) const + { + return pt.m_distance1 <= getContactBreakingThreshold(); + } + /// calculated new worldspace coordinates and depth, and reject points that exceed the collision margin + void refreshContactPoints( const btTransform& trA,const btTransform& trB); + + + SIMD_FORCE_INLINE void clearManifold() + { + int i; + for (i=0;i + +#include "BulletCollision/CollisionShapes/btConvexShape.h" +#include "BulletCollision/CollisionShapes/btTriangleShape.h" +#include "BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.h" +#include "BulletCollision/NarrowPhaseCollision/btGjkConvexCast.h" +#include "BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.h" +#include "BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h" +#include "btRaycastCallback.h" + +btTriangleRaycastCallback::btTriangleRaycastCallback(const btVector3& from,const btVector3& to, unsigned int flags) + : + m_from(from), + m_to(to), + //@BP Mod + m_flags(flags), + m_hitFraction(btScalar(1.)) +{ + +} + + + +void btTriangleRaycastCallback::processTriangle(btVector3* triangle,int partId, int triangleIndex) +{ + const btVector3 &vert0=triangle[0]; + const btVector3 &vert1=triangle[1]; + const btVector3 &vert2=triangle[2]; + + btVector3 v10; v10 = vert1 - vert0 ; + btVector3 v20; v20 = vert2 - vert0 ; + + btVector3 triangleNormal; triangleNormal = v10.cross( v20 ); + + const btScalar dist = vert0.dot(triangleNormal); + btScalar dist_a = triangleNormal.dot(m_from) ; + dist_a-= dist; + btScalar dist_b = triangleNormal.dot(m_to); + dist_b -= dist; + + if ( dist_a * dist_b >= btScalar(0.0) ) + { + return ; // same sign + } + //@BP Mod - Backface filtering + if (((m_flags & kF_FilterBackfaces) != 0) && (dist_a > btScalar(0.0))) + { + // Backface, skip check + return; + } + + const btScalar proj_length=dist_a-dist_b; + const btScalar distance = (dist_a)/(proj_length); + // Now we have the intersection point on the plane, we'll see if it's inside the triangle + // Add an epsilon as a tolerance for the raycast, + // in case the ray hits exacly on the edge of the triangle. + // It must be scaled for the triangle size. + + if(distance < m_hitFraction) + { + + + btScalar edge_tolerance =triangleNormal.length2(); + edge_tolerance *= btScalar(-0.0001); + btVector3 point; point.setInterpolate3( m_from, m_to, distance); + { + btVector3 v0p; v0p = vert0 - point; + btVector3 v1p; v1p = vert1 - point; + btVector3 cp0; cp0 = v0p.cross( v1p ); + + if ( (btScalar)(cp0.dot(triangleNormal)) >=edge_tolerance) + { + + + btVector3 v2p; v2p = vert2 - point; + btVector3 cp1; + cp1 = v1p.cross( v2p); + if ( (btScalar)(cp1.dot(triangleNormal)) >=edge_tolerance) + { + btVector3 cp2; + cp2 = v2p.cross(v0p); + + if ( (btScalar)(cp2.dot(triangleNormal)) >=edge_tolerance) + { + //@BP Mod + // Triangle normal isn't normalized + triangleNormal.normalize(); + + //@BP Mod - Allow for unflipped normal when raycasting against backfaces + if (((m_flags & kF_KeepUnflippedNormal) != 0) || (dist_a <= btScalar(0.0))) + { + m_hitFraction = reportHit(-triangleNormal,distance,partId,triangleIndex); + } + else + { + m_hitFraction = reportHit(triangleNormal,distance,partId,triangleIndex); + } + } + } + } + } + } +} + + +btTriangleConvexcastCallback::btTriangleConvexcastCallback (const btConvexShape* convexShape, const btTransform& convexShapeFrom, const btTransform& convexShapeTo, const btTransform& triangleToWorld, const btScalar triangleCollisionMargin) +{ + m_convexShape = convexShape; + m_convexShapeFrom = convexShapeFrom; + m_convexShapeTo = convexShapeTo; + m_triangleToWorld = triangleToWorld; + m_hitFraction = 1.0; + m_triangleCollisionMargin = triangleCollisionMargin; +} + +void +btTriangleConvexcastCallback::processTriangle (btVector3* triangle, int partId, int triangleIndex) +{ + btTriangleShape triangleShape (triangle[0], triangle[1], triangle[2]); + triangleShape.setMargin(m_triangleCollisionMargin); + + btVoronoiSimplexSolver simplexSolver; + btGjkEpaPenetrationDepthSolver gjkEpaPenetrationSolver; + +//#define USE_SUBSIMPLEX_CONVEX_CAST 1 +//if you reenable USE_SUBSIMPLEX_CONVEX_CAST see commented out code below +#ifdef USE_SUBSIMPLEX_CONVEX_CAST + btSubsimplexConvexCast convexCaster(m_convexShape, &triangleShape, &simplexSolver); +#else + //btGjkConvexCast convexCaster(m_convexShape,&triangleShape,&simplexSolver); + btContinuousConvexCollision convexCaster(m_convexShape,&triangleShape,&simplexSolver,&gjkEpaPenetrationSolver); +#endif //#USE_SUBSIMPLEX_CONVEX_CAST + + btConvexCast::CastResult castResult; + castResult.m_fraction = btScalar(1.); + if (convexCaster.calcTimeOfImpact(m_convexShapeFrom,m_convexShapeTo,m_triangleToWorld, m_triangleToWorld, castResult)) + { + //add hit + if (castResult.m_normal.length2() > btScalar(0.0001)) + { + if (castResult.m_fraction < m_hitFraction) + { +/* btContinuousConvexCast's normal is already in world space */ +/* +#ifdef USE_SUBSIMPLEX_CONVEX_CAST + //rotate normal into worldspace + castResult.m_normal = m_convexShapeFrom.getBasis() * castResult.m_normal; +#endif //USE_SUBSIMPLEX_CONVEX_CAST +*/ + castResult.m_normal.normalize(); + + reportHit (castResult.m_normal, + castResult.m_hitPoint, + castResult.m_fraction, + partId, + triangleIndex); + } + } + } +} diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.h b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.h new file mode 100644 index 000000000..3a1ab388c --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.h @@ -0,0 +1,71 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 RAYCAST_TRI_CALLBACK_H +#define RAYCAST_TRI_CALLBACK_H + +#include "BulletCollision/CollisionShapes/btTriangleCallback.h" +#include "LinearMath/btTransform.h" +struct btBroadphaseProxy; +class btConvexShape; + +class btTriangleRaycastCallback: public btTriangleCallback +{ +public: + + //input + btVector3 m_from; + btVector3 m_to; + + //@BP Mod - allow backface filtering and unflipped normals + enum EFlags + { + kF_None = 0, + kF_FilterBackfaces = 1 << 0, + kF_KeepUnflippedNormal = 1 << 1, // Prevents returned face normal getting flipped when a ray hits a back-facing triangle + + kF_Terminator = 0xFFFFFFFF + }; + unsigned int m_flags; + + btScalar m_hitFraction; + + btTriangleRaycastCallback(const btVector3& from,const btVector3& to, unsigned int flags=0); + + virtual void processTriangle(btVector3* triangle, int partId, int triangleIndex); + + virtual btScalar reportHit(const btVector3& hitNormalLocal, btScalar hitFraction, int partId, int triangleIndex ) = 0; + +}; + +class btTriangleConvexcastCallback : public btTriangleCallback +{ +public: + const btConvexShape* m_convexShape; + btTransform m_convexShapeFrom; + btTransform m_convexShapeTo; + btTransform m_triangleToWorld; + btScalar m_hitFraction; + btScalar m_triangleCollisionMargin; + + btTriangleConvexcastCallback (const btConvexShape* convexShape, const btTransform& convexShapeFrom, const btTransform& convexShapeTo, const btTransform& triangleToWorld, const btScalar triangleCollisionMargin); + + virtual void processTriangle (btVector3* triangle, int partId, int triangleIndex); + + virtual btScalar reportHit (const btVector3& hitNormalLocal, const btVector3& hitPointLocal, btScalar hitFraction, int partId, int triangleIndex) = 0; +}; + +#endif //RAYCAST_TRI_CALLBACK_H + diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btSimplexSolverInterface.h b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btSimplexSolverInterface.h new file mode 100644 index 000000000..823b4e715 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btSimplexSolverInterface.h @@ -0,0 +1,63 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 SIMPLEX_SOLVER_INTERFACE_H +#define SIMPLEX_SOLVER_INTERFACE_H + +#include "LinearMath/btVector3.h" + +#define NO_VIRTUAL_INTERFACE 1 +#ifdef NO_VIRTUAL_INTERFACE +#include "btVoronoiSimplexSolver.h" +#define btSimplexSolverInterface btVoronoiSimplexSolver +#else + +/// btSimplexSolverInterface can incrementally calculate distance between origin and up to 4 vertices +/// Used by GJK or Linear Casting. Can be implemented by the Johnson-algorithm or alternative approaches based on +/// voronoi regions or barycentric coordinates +class btSimplexSolverInterface +{ + public: + virtual ~btSimplexSolverInterface() {}; + + virtual void reset() = 0; + + virtual void addVertex(const btVector3& w, const btVector3& p, const btVector3& q) = 0; + + virtual bool closest(btVector3& v) = 0; + + virtual btScalar maxVertex() = 0; + + virtual bool fullSimplex() const = 0; + + virtual int getSimplex(btVector3 *pBuf, btVector3 *qBuf, btVector3 *yBuf) const = 0; + + virtual bool inSimplex(const btVector3& w) = 0; + + virtual void backup_closest(btVector3& v) = 0; + + virtual bool emptySimplex() const = 0; + + virtual void compute_points(btVector3& p1, btVector3& p2) = 0; + + virtual int numVertices() const =0; + + +}; +#endif +#endif //SIMPLEX_SOLVER_INTERFACE_H + diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.cpp b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.cpp new file mode 100644 index 000000000..4c709a8c3 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.cpp @@ -0,0 +1,157 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btSubSimplexConvexCast.h" +#include "BulletCollision/CollisionShapes/btConvexShape.h" + +#include "BulletCollision/CollisionShapes/btMinkowskiSumShape.h" +#include "BulletCollision/NarrowPhaseCollision/btSimplexSolverInterface.h" +#include "btPointCollector.h" +#include "LinearMath/btTransformUtil.h" + +btSubsimplexConvexCast::btSubsimplexConvexCast (const btConvexShape* convexA,const btConvexShape* convexB,btSimplexSolverInterface* simplexSolver) +:m_simplexSolver(simplexSolver), +m_convexA(convexA),m_convexB(convexB) +{ +} + +///Typically the conservative advancement reaches solution in a few iterations, clip it to 32 for degenerate cases. +///See discussion about this here http://continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=565 +#ifdef BT_USE_DOUBLE_PRECISION +#define MAX_ITERATIONS 64 +#else +#define MAX_ITERATIONS 32 +#endif +bool btSubsimplexConvexCast::calcTimeOfImpact( + const btTransform& fromA, + const btTransform& toA, + const btTransform& fromB, + const btTransform& toB, + CastResult& result) +{ + + m_simplexSolver->reset(); + + btVector3 linVelA,linVelB; + linVelA = toA.getOrigin()-fromA.getOrigin(); + linVelB = toB.getOrigin()-fromB.getOrigin(); + + btScalar lambda = btScalar(0.); + + btTransform interpolatedTransA = fromA; + btTransform interpolatedTransB = fromB; + + ///take relative motion + btVector3 r = (linVelA-linVelB); + btVector3 v; + + btVector3 supVertexA = fromA(m_convexA->localGetSupportingVertex(-r*fromA.getBasis())); + btVector3 supVertexB = fromB(m_convexB->localGetSupportingVertex(r*fromB.getBasis())); + v = supVertexA-supVertexB; + int maxIter = MAX_ITERATIONS; + + btVector3 n; + n.setValue(btScalar(0.),btScalar(0.),btScalar(0.)); + bool hasResult = false; + btVector3 c; + + btScalar lastLambda = lambda; + + + btScalar dist2 = v.length2(); +#ifdef BT_USE_DOUBLE_PRECISION + btScalar epsilon = btScalar(0.0001); +#else + btScalar epsilon = btScalar(0.0001); +#endif //BT_USE_DOUBLE_PRECISION + btVector3 w,p; + btScalar VdotR; + + while ( (dist2 > epsilon) && maxIter--) + { + supVertexA = interpolatedTransA(m_convexA->localGetSupportingVertex(-v*interpolatedTransA.getBasis())); + supVertexB = interpolatedTransB(m_convexB->localGetSupportingVertex(v*interpolatedTransB.getBasis())); + w = supVertexA-supVertexB; + + btScalar VdotW = v.dot(w); + + if (lambda > btScalar(1.0)) + { + return false; + } + + if ( VdotW > btScalar(0.)) + { + VdotR = v.dot(r); + + if (VdotR >= -(SIMD_EPSILON*SIMD_EPSILON)) + return false; + else + { + lambda = lambda - VdotW / VdotR; + //interpolate to next lambda + // x = s + lambda * r; + interpolatedTransA.getOrigin().setInterpolate3(fromA.getOrigin(),toA.getOrigin(),lambda); + interpolatedTransB.getOrigin().setInterpolate3(fromB.getOrigin(),toB.getOrigin(),lambda); + //m_simplexSolver->reset(); + //check next line + w = supVertexA-supVertexB; + lastLambda = lambda; + n = v; + hasResult = true; + } + } + m_simplexSolver->addVertex( w, supVertexA , supVertexB); + if (m_simplexSolver->closest(v)) + { + dist2 = v.length2(); + hasResult = true; + //todo: check this normal for validity + //n=v; + //printf("V=%f , %f, %f\n",v[0],v[1],v[2]); + //printf("DIST2=%f\n",dist2); + //printf("numverts = %i\n",m_simplexSolver->numVertices()); + } else + { + dist2 = btScalar(0.); + } + } + + //int numiter = MAX_ITERATIONS - maxIter; +// printf("number of iterations: %d", numiter); + + //don't report a time of impact when moving 'away' from the hitnormal + + + result.m_fraction = lambda; + if (n.length2() >= (SIMD_EPSILON*SIMD_EPSILON)) + result.m_normal = n.normalized(); + else + result.m_normal = btVector3(btScalar(0.0), btScalar(0.0), btScalar(0.0)); + + //don't report time of impact for motion away from the contact normal (or causes minor penetration) + if (result.m_normal.dot(r)>=-result.m_allowedPenetration) + return false; + + btVector3 hitA,hitB; + m_simplexSolver->compute_points(hitA,hitB); + result.m_hitPoint=hitB; + return true; +} + + + + diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.h b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.h new file mode 100644 index 000000000..05662db5d --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.h @@ -0,0 +1,50 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 SUBSIMPLEX_CONVEX_CAST_H +#define SUBSIMPLEX_CONVEX_CAST_H + +#include "btConvexCast.h" +#include "btSimplexSolverInterface.h" +class btConvexShape; + +/// btSubsimplexConvexCast implements Gino van den Bergens' paper +///"Ray Casting against bteral Convex Objects with Application to Continuous Collision Detection" +/// GJK based Ray Cast, optimized version +/// Objects should not start in overlap, otherwise results are not defined. +class btSubsimplexConvexCast : public btConvexCast +{ + btSimplexSolverInterface* m_simplexSolver; + const btConvexShape* m_convexA; + const btConvexShape* m_convexB; + +public: + + btSubsimplexConvexCast (const btConvexShape* shapeA,const btConvexShape* shapeB,btSimplexSolverInterface* simplexSolver); + + //virtual ~btSubsimplexConvexCast(); + ///SimsimplexConvexCast calculateTimeOfImpact calculates the time of impact+normal for the linear cast (sweep) between two moving objects. + ///Precondition is that objects should not penetration/overlap at the start from the interval. Overlap can be tested using btGjkPairDetector. + virtual bool calcTimeOfImpact( + const btTransform& fromA, + const btTransform& toA, + const btTransform& fromB, + const btTransform& toB, + CastResult& result); + +}; + +#endif //SUBSIMPLEX_CONVEX_CAST_H diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.cpp b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.cpp new file mode 100644 index 000000000..b07173330 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.cpp @@ -0,0 +1,605 @@ + +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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. + + Elsevier CDROM license agreements grants nonexclusive license to use the software + for any purpose, commercial or non-commercial as long as the following credit is included + identifying the original source of the software: + + Parts of the source are "from the book Real-Time Collision Detection by + Christer Ericson, published by Morgan Kaufmann Publishers, + (c) 2005 Elsevier Inc." + +*/ + + +#include "btVoronoiSimplexSolver.h" + +#define VERTA 0 +#define VERTB 1 +#define VERTC 2 +#define VERTD 3 + +#define CATCH_DEGENERATE_TETRAHEDRON 1 +void btVoronoiSimplexSolver::removeVertex(int index) +{ + + btAssert(m_numVertices>0); + m_numVertices--; + m_simplexVectorW[index] = m_simplexVectorW[m_numVertices]; + m_simplexPointsP[index] = m_simplexPointsP[m_numVertices]; + m_simplexPointsQ[index] = m_simplexPointsQ[m_numVertices]; +} + +void btVoronoiSimplexSolver::reduceVertices (const btUsageBitfield& usedVerts) +{ + if ((numVertices() >= 4) && (!usedVerts.usedVertexD)) + removeVertex(3); + + if ((numVertices() >= 3) && (!usedVerts.usedVertexC)) + removeVertex(2); + + if ((numVertices() >= 2) && (!usedVerts.usedVertexB)) + removeVertex(1); + + if ((numVertices() >= 1) && (!usedVerts.usedVertexA)) + removeVertex(0); + +} + + + + + +//clear the simplex, remove all the vertices +void btVoronoiSimplexSolver::reset() +{ + m_cachedValidClosest = false; + m_numVertices = 0; + m_needsUpdate = true; + m_lastW = btVector3(btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT)); + m_cachedBC.reset(); +} + + + + //add a vertex +void btVoronoiSimplexSolver::addVertex(const btVector3& w, const btVector3& p, const btVector3& q) +{ + m_lastW = w; + m_needsUpdate = true; + + m_simplexVectorW[m_numVertices] = w; + m_simplexPointsP[m_numVertices] = p; + m_simplexPointsQ[m_numVertices] = q; + + m_numVertices++; +} + +bool btVoronoiSimplexSolver::updateClosestVectorAndPoints() +{ + + if (m_needsUpdate) + { + m_cachedBC.reset(); + + m_needsUpdate = false; + + switch (numVertices()) + { + case 0: + m_cachedValidClosest = false; + break; + case 1: + { + m_cachedP1 = m_simplexPointsP[0]; + m_cachedP2 = m_simplexPointsQ[0]; + m_cachedV = m_cachedP1-m_cachedP2; //== m_simplexVectorW[0] + m_cachedBC.reset(); + m_cachedBC.setBarycentricCoordinates(btScalar(1.),btScalar(0.),btScalar(0.),btScalar(0.)); + m_cachedValidClosest = m_cachedBC.isValid(); + break; + }; + case 2: + { + //closest point origin from line segment + const btVector3& from = m_simplexVectorW[0]; + const btVector3& to = m_simplexVectorW[1]; + btVector3 nearest; + + btVector3 p (btScalar(0.),btScalar(0.),btScalar(0.)); + btVector3 diff = p - from; + btVector3 v = to - from; + btScalar t = v.dot(diff); + + if (t > 0) { + btScalar dotVV = v.dot(v); + if (t < dotVV) { + t /= dotVV; + diff -= t*v; + m_cachedBC.m_usedVertices.usedVertexA = true; + m_cachedBC.m_usedVertices.usedVertexB = true; + } else { + t = 1; + diff -= v; + //reduce to 1 point + m_cachedBC.m_usedVertices.usedVertexB = true; + } + } else + { + t = 0; + //reduce to 1 point + m_cachedBC.m_usedVertices.usedVertexA = true; + } + m_cachedBC.setBarycentricCoordinates(1-t,t); + nearest = from + t*v; + + m_cachedP1 = m_simplexPointsP[0] + t * (m_simplexPointsP[1] - m_simplexPointsP[0]); + m_cachedP2 = m_simplexPointsQ[0] + t * (m_simplexPointsQ[1] - m_simplexPointsQ[0]); + m_cachedV = m_cachedP1 - m_cachedP2; + + reduceVertices(m_cachedBC.m_usedVertices); + + m_cachedValidClosest = m_cachedBC.isValid(); + break; + } + case 3: + { + //closest point origin from triangle + btVector3 p (btScalar(0.),btScalar(0.),btScalar(0.)); + + const btVector3& a = m_simplexVectorW[0]; + const btVector3& b = m_simplexVectorW[1]; + const btVector3& c = m_simplexVectorW[2]; + + closestPtPointTriangle(p,a,b,c,m_cachedBC); + m_cachedP1 = m_simplexPointsP[0] * m_cachedBC.m_barycentricCoords[0] + + m_simplexPointsP[1] * m_cachedBC.m_barycentricCoords[1] + + m_simplexPointsP[2] * m_cachedBC.m_barycentricCoords[2]; + + m_cachedP2 = m_simplexPointsQ[0] * m_cachedBC.m_barycentricCoords[0] + + m_simplexPointsQ[1] * m_cachedBC.m_barycentricCoords[1] + + m_simplexPointsQ[2] * m_cachedBC.m_barycentricCoords[2]; + + m_cachedV = m_cachedP1-m_cachedP2; + + reduceVertices (m_cachedBC.m_usedVertices); + m_cachedValidClosest = m_cachedBC.isValid(); + + break; + } + case 4: + { + + + btVector3 p (btScalar(0.),btScalar(0.),btScalar(0.)); + + const btVector3& a = m_simplexVectorW[0]; + const btVector3& b = m_simplexVectorW[1]; + const btVector3& c = m_simplexVectorW[2]; + const btVector3& d = m_simplexVectorW[3]; + + bool hasSeperation = closestPtPointTetrahedron(p,a,b,c,d,m_cachedBC); + + if (hasSeperation) + { + + m_cachedP1 = m_simplexPointsP[0] * m_cachedBC.m_barycentricCoords[0] + + m_simplexPointsP[1] * m_cachedBC.m_barycentricCoords[1] + + m_simplexPointsP[2] * m_cachedBC.m_barycentricCoords[2] + + m_simplexPointsP[3] * m_cachedBC.m_barycentricCoords[3]; + + m_cachedP2 = m_simplexPointsQ[0] * m_cachedBC.m_barycentricCoords[0] + + m_simplexPointsQ[1] * m_cachedBC.m_barycentricCoords[1] + + m_simplexPointsQ[2] * m_cachedBC.m_barycentricCoords[2] + + m_simplexPointsQ[3] * m_cachedBC.m_barycentricCoords[3]; + + m_cachedV = m_cachedP1-m_cachedP2; + reduceVertices (m_cachedBC.m_usedVertices); + } else + { +// printf("sub distance got penetration\n"); + + if (m_cachedBC.m_degenerate) + { + m_cachedValidClosest = false; + } else + { + m_cachedValidClosest = true; + //degenerate case == false, penetration = true + zero + m_cachedV.setValue(btScalar(0.),btScalar(0.),btScalar(0.)); + } + break; + } + + m_cachedValidClosest = m_cachedBC.isValid(); + + //closest point origin from tetrahedron + break; + } + default: + { + m_cachedValidClosest = false; + } + }; + } + + return m_cachedValidClosest; + +} + +//return/calculate the closest vertex +bool btVoronoiSimplexSolver::closest(btVector3& v) +{ + bool succes = updateClosestVectorAndPoints(); + v = m_cachedV; + return succes; +} + + + +btScalar btVoronoiSimplexSolver::maxVertex() +{ + int i, numverts = numVertices(); + btScalar maxV = btScalar(0.); + for (i=0;i= btScalar(0.0) && d4 <= d3) + { + result.m_closestPointOnSimplex = b; + result.m_usedVertices.usedVertexB = true; + result.setBarycentricCoordinates(0,1,0); + + return true; // b; // barycentric coordinates (0,1,0) + } + // Check if P in edge region of AB, if so return projection of P onto AB + btScalar vc = d1*d4 - d3*d2; + if (vc <= btScalar(0.0) && d1 >= btScalar(0.0) && d3 <= btScalar(0.0)) { + btScalar v = d1 / (d1 - d3); + result.m_closestPointOnSimplex = a + v * ab; + result.m_usedVertices.usedVertexA = true; + result.m_usedVertices.usedVertexB = true; + result.setBarycentricCoordinates(1-v,v,0); + return true; + //return a + v * ab; // barycentric coordinates (1-v,v,0) + } + + // Check if P in vertex region outside C + btVector3 cp = p - c; + btScalar d5 = ab.dot(cp); + btScalar d6 = ac.dot(cp); + if (d6 >= btScalar(0.0) && d5 <= d6) + { + result.m_closestPointOnSimplex = c; + result.m_usedVertices.usedVertexC = true; + result.setBarycentricCoordinates(0,0,1); + return true;//c; // barycentric coordinates (0,0,1) + } + + // Check if P in edge region of AC, if so return projection of P onto AC + btScalar vb = d5*d2 - d1*d6; + if (vb <= btScalar(0.0) && d2 >= btScalar(0.0) && d6 <= btScalar(0.0)) { + btScalar w = d2 / (d2 - d6); + result.m_closestPointOnSimplex = a + w * ac; + result.m_usedVertices.usedVertexA = true; + result.m_usedVertices.usedVertexC = true; + result.setBarycentricCoordinates(1-w,0,w); + return true; + //return a + w * ac; // barycentric coordinates (1-w,0,w) + } + + // Check if P in edge region of BC, if so return projection of P onto BC + btScalar va = d3*d6 - d5*d4; + if (va <= btScalar(0.0) && (d4 - d3) >= btScalar(0.0) && (d5 - d6) >= btScalar(0.0)) { + btScalar w = (d4 - d3) / ((d4 - d3) + (d5 - d6)); + + result.m_closestPointOnSimplex = b + w * (c - b); + result.m_usedVertices.usedVertexB = true; + result.m_usedVertices.usedVertexC = true; + result.setBarycentricCoordinates(0,1-w,w); + return true; + // return b + w * (c - b); // barycentric coordinates (0,1-w,w) + } + + // P inside face region. Compute Q through its barycentric coordinates (u,v,w) + btScalar denom = btScalar(1.0) / (va + vb + vc); + btScalar v = vb * denom; + btScalar w = vc * denom; + + result.m_closestPointOnSimplex = a + ab * v + ac * w; + result.m_usedVertices.usedVertexA = true; + result.m_usedVertices.usedVertexB = true; + result.m_usedVertices.usedVertexC = true; + result.setBarycentricCoordinates(1-v-w,v,w); + + return true; +// return a + ab * v + ac * w; // = u*a + v*b + w*c, u = va * denom = btScalar(1.0) - v - w + +} + + + + + +/// Test if point p and d lie on opposite sides of plane through abc +int btVoronoiSimplexSolver::pointOutsideOfPlane(const btVector3& p, const btVector3& a, const btVector3& b, const btVector3& c, const btVector3& d) +{ + btVector3 normal = (b-a).cross(c-a); + + btScalar signp = (p - a).dot(normal); // [AP AB AC] + btScalar signd = (d - a).dot( normal); // [AD AB AC] + +#ifdef CATCH_DEGENERATE_TETRAHEDRON +#ifdef BT_USE_DOUBLE_PRECISION +if (signd * signd < (btScalar(1e-8) * btScalar(1e-8))) + { + return -1; + } +#else + if (signd * signd < (btScalar(1e-4) * btScalar(1e-4))) + { +// printf("affine dependent/degenerate\n");// + return -1; + } +#endif + +#endif + // Points on opposite sides if expression signs are opposite + return signp * signd < btScalar(0.); +} + + +bool btVoronoiSimplexSolver::closestPtPointTetrahedron(const btVector3& p, const btVector3& a, const btVector3& b, const btVector3& c, const btVector3& d, btSubSimplexClosestResult& finalResult) +{ + btSubSimplexClosestResult tempResult; + + // Start out assuming point inside all halfspaces, so closest to itself + finalResult.m_closestPointOnSimplex = p; + finalResult.m_usedVertices.reset(); + finalResult.m_usedVertices.usedVertexA = true; + finalResult.m_usedVertices.usedVertexB = true; + finalResult.m_usedVertices.usedVertexC = true; + finalResult.m_usedVertices.usedVertexD = true; + + int pointOutsideABC = pointOutsideOfPlane(p, a, b, c, d); + int pointOutsideACD = pointOutsideOfPlane(p, a, c, d, b); + int pointOutsideADB = pointOutsideOfPlane(p, a, d, b, c); + int pointOutsideBDC = pointOutsideOfPlane(p, b, d, c, a); + + if (pointOutsideABC < 0 || pointOutsideACD < 0 || pointOutsideADB < 0 || pointOutsideBDC < 0) + { + finalResult.m_degenerate = true; + return false; + } + + if (!pointOutsideABC && !pointOutsideACD && !pointOutsideADB && !pointOutsideBDC) + { + return false; + } + + + btScalar bestSqDist = FLT_MAX; + // If point outside face abc then compute closest point on abc + if (pointOutsideABC) + { + closestPtPointTriangle(p, a, b, c,tempResult); + btVector3 q = tempResult.m_closestPointOnSimplex; + + btScalar sqDist = (q - p).dot( q - p); + // Update best closest point if (squared) distance is less than current best + if (sqDist < bestSqDist) { + bestSqDist = sqDist; + finalResult.m_closestPointOnSimplex = q; + //convert result bitmask! + finalResult.m_usedVertices.reset(); + finalResult.m_usedVertices.usedVertexA = tempResult.m_usedVertices.usedVertexA; + finalResult.m_usedVertices.usedVertexB = tempResult.m_usedVertices.usedVertexB; + finalResult.m_usedVertices.usedVertexC = tempResult.m_usedVertices.usedVertexC; + finalResult.setBarycentricCoordinates( + tempResult.m_barycentricCoords[VERTA], + tempResult.m_barycentricCoords[VERTB], + tempResult.m_barycentricCoords[VERTC], + 0 + ); + + } + } + + + // Repeat test for face acd + if (pointOutsideACD) + { + closestPtPointTriangle(p, a, c, d,tempResult); + btVector3 q = tempResult.m_closestPointOnSimplex; + //convert result bitmask! + + btScalar sqDist = (q - p).dot( q - p); + if (sqDist < bestSqDist) + { + bestSqDist = sqDist; + finalResult.m_closestPointOnSimplex = q; + finalResult.m_usedVertices.reset(); + finalResult.m_usedVertices.usedVertexA = tempResult.m_usedVertices.usedVertexA; + + finalResult.m_usedVertices.usedVertexC = tempResult.m_usedVertices.usedVertexB; + finalResult.m_usedVertices.usedVertexD = tempResult.m_usedVertices.usedVertexC; + finalResult.setBarycentricCoordinates( + tempResult.m_barycentricCoords[VERTA], + 0, + tempResult.m_barycentricCoords[VERTB], + tempResult.m_barycentricCoords[VERTC] + ); + + } + } + // Repeat test for face adb + + + if (pointOutsideADB) + { + closestPtPointTriangle(p, a, d, b,tempResult); + btVector3 q = tempResult.m_closestPointOnSimplex; + //convert result bitmask! + + btScalar sqDist = (q - p).dot( q - p); + if (sqDist < bestSqDist) + { + bestSqDist = sqDist; + finalResult.m_closestPointOnSimplex = q; + finalResult.m_usedVertices.reset(); + finalResult.m_usedVertices.usedVertexA = tempResult.m_usedVertices.usedVertexA; + finalResult.m_usedVertices.usedVertexB = tempResult.m_usedVertices.usedVertexC; + + finalResult.m_usedVertices.usedVertexD = tempResult.m_usedVertices.usedVertexB; + finalResult.setBarycentricCoordinates( + tempResult.m_barycentricCoords[VERTA], + tempResult.m_barycentricCoords[VERTC], + 0, + tempResult.m_barycentricCoords[VERTB] + ); + + } + } + // Repeat test for face bdc + + + if (pointOutsideBDC) + { + closestPtPointTriangle(p, b, d, c,tempResult); + btVector3 q = tempResult.m_closestPointOnSimplex; + //convert result bitmask! + btScalar sqDist = (q - p).dot( q - p); + if (sqDist < bestSqDist) + { + bestSqDist = sqDist; + finalResult.m_closestPointOnSimplex = q; + finalResult.m_usedVertices.reset(); + // + finalResult.m_usedVertices.usedVertexB = tempResult.m_usedVertices.usedVertexA; + finalResult.m_usedVertices.usedVertexC = tempResult.m_usedVertices.usedVertexC; + finalResult.m_usedVertices.usedVertexD = tempResult.m_usedVertices.usedVertexB; + + finalResult.setBarycentricCoordinates( + 0, + tempResult.m_barycentricCoords[VERTA], + tempResult.m_barycentricCoords[VERTC], + tempResult.m_barycentricCoords[VERTB] + ); + + } + } + + //help! we ended up full ! + + if (finalResult.m_usedVertices.usedVertexA && + finalResult.m_usedVertices.usedVertexB && + finalResult.m_usedVertices.usedVertexC && + finalResult.m_usedVertices.usedVertexD) + { + return true; + } + + return true; +} + diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.h b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.h new file mode 100644 index 000000000..d3162d9fb --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.h @@ -0,0 +1,157 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 btVoronoiSimplexSolver_H +#define btVoronoiSimplexSolver_H + +#include "btSimplexSolverInterface.h" + + + +#define VORONOI_SIMPLEX_MAX_VERTS 5 + +struct btUsageBitfield{ + btUsageBitfield() + { + reset(); + } + + void reset() + { + usedVertexA = false; + usedVertexB = false; + usedVertexC = false; + usedVertexD = false; + } + unsigned short usedVertexA : 1; + unsigned short usedVertexB : 1; + unsigned short usedVertexC : 1; + unsigned short usedVertexD : 1; + unsigned short unused1 : 1; + unsigned short unused2 : 1; + unsigned short unused3 : 1; + unsigned short unused4 : 1; +}; + + +struct btSubSimplexClosestResult +{ + btVector3 m_closestPointOnSimplex; + //MASK for m_usedVertices + //stores the simplex vertex-usage, using the MASK, + // if m_usedVertices & MASK then the related vertex is used + btUsageBitfield m_usedVertices; + btScalar m_barycentricCoords[4]; + bool m_degenerate; + + void reset() + { + m_degenerate = false; + setBarycentricCoordinates(); + m_usedVertices.reset(); + } + bool isValid() + { + bool valid = (m_barycentricCoords[0] >= btScalar(0.)) && + (m_barycentricCoords[1] >= btScalar(0.)) && + (m_barycentricCoords[2] >= btScalar(0.)) && + (m_barycentricCoords[3] >= btScalar(0.)); + + + return valid; + } + void setBarycentricCoordinates(btScalar a=btScalar(0.),btScalar b=btScalar(0.),btScalar c=btScalar(0.),btScalar d=btScalar(0.)) + { + m_barycentricCoords[0] = a; + m_barycentricCoords[1] = b; + m_barycentricCoords[2] = c; + m_barycentricCoords[3] = d; + } + +}; + +/// btVoronoiSimplexSolver is an implementation of the closest point distance algorithm from a 1-4 points simplex to the origin. +/// Can be used with GJK, as an alternative to Johnson distance algorithm. +#ifdef NO_VIRTUAL_INTERFACE +class btVoronoiSimplexSolver +#else +class btVoronoiSimplexSolver : public btSimplexSolverInterface +#endif +{ +public: + + int m_numVertices; + + btVector3 m_simplexVectorW[VORONOI_SIMPLEX_MAX_VERTS]; + btVector3 m_simplexPointsP[VORONOI_SIMPLEX_MAX_VERTS]; + btVector3 m_simplexPointsQ[VORONOI_SIMPLEX_MAX_VERTS]; + + + + btVector3 m_cachedP1; + btVector3 m_cachedP2; + btVector3 m_cachedV; + btVector3 m_lastW; + bool m_cachedValidClosest; + + btSubSimplexClosestResult m_cachedBC; + + bool m_needsUpdate; + + void removeVertex(int index); + void reduceVertices (const btUsageBitfield& usedVerts); + bool updateClosestVectorAndPoints(); + + bool closestPtPointTetrahedron(const btVector3& p, const btVector3& a, const btVector3& b, const btVector3& c, const btVector3& d, btSubSimplexClosestResult& finalResult); + int pointOutsideOfPlane(const btVector3& p, const btVector3& a, const btVector3& b, const btVector3& c, const btVector3& d); + bool closestPtPointTriangle(const btVector3& p, const btVector3& a, const btVector3& b, const btVector3& c,btSubSimplexClosestResult& result); + +public: + + void reset(); + + void addVertex(const btVector3& w, const btVector3& p, const btVector3& q); + + + bool closest(btVector3& v); + + btScalar maxVertex(); + + bool fullSimplex() const + { + return (m_numVertices == 4); + } + + int getSimplex(btVector3 *pBuf, btVector3 *qBuf, btVector3 *yBuf) const; + + bool inSimplex(const btVector3& w); + + void backup_closest(btVector3& v) ; + + bool emptySimplex() const ; + + void compute_points(btVector3& p1, btVector3& p2) ; + + int numVertices() const + { + return m_numVertices; + } + + +}; + +#endif //VoronoiSimplexSolver diff --git a/Engine/lib/bullet/src/BulletCollision/ibmsdk/Makefile b/Engine/lib/bullet/src/BulletCollision/ibmsdk/Makefile new file mode 100644 index 000000000..8a7878326 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/ibmsdk/Makefile @@ -0,0 +1,112 @@ +#### Source code Dirs +VPATH = \ +../BroadphaseCollision \ +../CollisionDispatch \ +../NarrowPhaseCollision \ +../CollisionShapes + +ROOT = ../../.. + +#### Library +LIBRARY_ppu = bulletcollision.a + +#### Compiler flags +CPPFLAGS = \ +-DUSE_LIBSPE2 \ +-I../BroadphaseCollision \ +-I../CollisionDispath \ +-I../NarrowPhaseCollision \ +-I../CollisionShapes \ +-I$(ROOT)/src/ \ +-I$(SDKINC) + +#### Optimization level flags +#CC_OPT_LEVEL = $(CC_OPT_LEVEL_DEBUG) +CC_OPT_LEVEL = -O3 + +##### Objects to be archived in lib + +OBJS = \ +btAxisSweep3.o \ +btQuantizedBvh.o \ +btBroadphaseProxy.o \ +btCollisionAlgorithm.o \ +btDispatcher.o \ +btDbvtBroadphase.o \ +btDbvt.o \ +btOverlappingPairCache.o \ +btSimpleBroadphase.o \ +btContinuousConvexCollision.o \ +btConvexCast.o \ +btGjkConvexCast.o \ +btGjkEpa2.o \ +btGjkEpaPenetrationDepthSolver.o \ +btGjkPairDetector.o \ +btDefaultCollisionConfiguration.o \ +btMinkowskiPenetrationDepthSolver.o \ +btPersistentManifold.o \ +btRaycastCallback.o \ +btSubSimplexConvexCast.o \ +btVoronoiSimplexSolver.o \ +btCollisionDispatcher.o \ +btCollisionObject.o \ +btCollisionWorld.o \ +btCompoundCollisionAlgorithm.o \ +btBoxBoxCollisionAlgorithm.o \ +btBoxBoxDetector.o \ +btConvexPlaneCollisionAlgorithm.o \ +btConvexConcaveCollisionAlgorithm.o \ +btConvexConvexAlgorithm.o \ +btDefaultCollisionConfiguration.o \ +btEmptyCollisionAlgorithm.o \ +btManifoldResult.o \ +btSimulationIslandManager.o \ +btSphereBoxCollisionAlgorithm.o \ +btSphereSphereCollisionAlgorithm.o \ +btSphereTriangleCollisionAlgorithm.o \ +btActivatingCollisionAlgorithm.o \ +btUnionFind.o \ +SphereTriangleDetector.o \ +btBoxShape.o \ +btBvhTriangleMeshShape.o \ +btCapsuleShape.o \ +btCollisionShape.o \ +btCompoundShape.o \ +btConcaveShape.o \ +btConeShape.o \ +btConvexHullShape.o \ +btConvexShape.o \ +btConvexInternalShape.o \ +btConvexTriangleMeshShape.o \ +btCylinderShape.o \ +btEmptyShape.o \ +btHeightfieldTerrainShape.o \ +btMinkowskiSumShape.o \ +btMultiSphereShape.o \ +btOptimizedBvh.o \ +btPolyhedralConvexShape.o \ +btSphereShape.o \ +btStaticPlaneShape.o \ +btStridingMeshInterface.o \ +btTetrahedronShape.o \ +btTriangleBuffer.o \ +btTriangleCallback.o \ +btTriangleIndexVertexArray.o \ +btTriangleMesh.o \ +btTriangleMeshShape.o \ +btUniformScalingShape.o + +#### Install directories +INSTALL_DIR = $(ROOT)/lib/ibmsdk +INSTALL_FILES = $(LIBRARY_ppu) + +IBM_CELLSDK_VERSION := $(shell if [ -d /opt/cell ]; then echo "3.0"; fi) + +ifeq ("$(IBM_CELLSDK_VERSION)","3.0") + CELL_TOP ?= /opt/cell/sdk + include $(CELL_TOP)/buildutils/make.footer +else + CELL_TOP ?= /opt/ibm/cell-sdk/prototype + include $(CELL_TOP)/make.footer +endif + diff --git a/Engine/lib/bullet/src/BulletDynamics/CMakeLists.txt b/Engine/lib/bullet/src/BulletDynamics/CMakeLists.txt new file mode 100644 index 000000000..e4fd766a3 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/CMakeLists.txt @@ -0,0 +1,107 @@ +INCLUDE_DIRECTORIES( ${BULLET_PHYSICS_SOURCE_DIR}/src } ) + + + +SET(BulletDynamics_SRCS + Character/btKinematicCharacterController.cpp + ConstraintSolver/btConeTwistConstraint.cpp + ConstraintSolver/btContactConstraint.cpp + ConstraintSolver/btGeneric6DofConstraint.cpp + ConstraintSolver/btGeneric6DofSpringConstraint.cpp + ConstraintSolver/btHinge2Constraint.cpp + ConstraintSolver/btHingeConstraint.cpp + ConstraintSolver/btPoint2PointConstraint.cpp + ConstraintSolver/btSequentialImpulseConstraintSolver.cpp + ConstraintSolver/btSliderConstraint.cpp + ConstraintSolver/btSolve2LinearConstraint.cpp + ConstraintSolver/btTypedConstraint.cpp + ConstraintSolver/btUniversalConstraint.cpp + Dynamics/btContinuousDynamicsWorld.cpp + Dynamics/btDiscreteDynamicsWorld.cpp + Dynamics/btRigidBody.cpp + Dynamics/btSimpleDynamicsWorld.cpp + Dynamics/Bullet-C-API.cpp + Vehicle/btRaycastVehicle.cpp + Vehicle/btWheelInfo.cpp +) + +SET(Root_HDRS + ../btBulletDynamicsCommon.h + ../btBulletCollisionCommon.h +) +SET(ConstraintSolver_HDRS + ConstraintSolver/btConeTwistConstraint.h + ConstraintSolver/btConstraintSolver.h + ConstraintSolver/btContactConstraint.h + ConstraintSolver/btContactSolverInfo.h + ConstraintSolver/btGeneric6DofConstraint.h + ConstraintSolver/btGeneric6DofSpringConstraint.h + ConstraintSolver/btHinge2Constraint.h + ConstraintSolver/btHingeConstraint.h + ConstraintSolver/btJacobianEntry.h + ConstraintSolver/btPoint2PointConstraint.h + ConstraintSolver/btSequentialImpulseConstraintSolver.h + ConstraintSolver/btSliderConstraint.h + ConstraintSolver/btSolve2LinearConstraint.h + ConstraintSolver/btSolverBody.h + ConstraintSolver/btSolverConstraint.h + ConstraintSolver/btTypedConstraint.h + ConstraintSolver/btUniversalConstraint.h +) +SET(Dynamics_HDRS + Dynamics/btActionInterface.h + Dynamics/btContinuousDynamicsWorld.h + Dynamics/btDiscreteDynamicsWorld.h + Dynamics/btDynamicsWorld.h + Dynamics/btSimpleDynamicsWorld.h + Dynamics/btRigidBody.h +) +SET(Vehicle_HDRS + Vehicle/btRaycastVehicle.h + Vehicle/btVehicleRaycaster.h + Vehicle/btWheelInfo.h +) + +SET(Character_HDRS + Character/btCharacterControllerInterface.h + Character/btKinematicCharacterController.h +) + + + +SET(BulletDynamics_HDRS + ${Root_HDRS} + ${ConstraintSolver_HDRS} + ${Dynamics_HDRS} + ${Vehicle_HDRS} + ${Character_HDRS} +) + + +ADD_LIBRARY(BulletDynamics ${BulletDynamics_SRCS} ${BulletDynamics_HDRS}) +SET_TARGET_PROPERTIES(BulletDynamics PROPERTIES VERSION ${BULLET_VERSION}) +SET_TARGET_PROPERTIES(BulletDynamics PROPERTIES SOVERSION ${BULLET_VERSION}) +IF (BUILD_SHARED_LIBS) + TARGET_LINK_LIBRARIES(BulletDynamics BulletCollision LinearMath) +ENDIF (BUILD_SHARED_LIBS) + +IF (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 2.5) + IF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) + INSTALL(TARGETS BulletDynamics DESTINATION .) + ELSE (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) + INSTALL(TARGETS BulletDynamics DESTINATION lib) + INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DESTINATION include FILES_MATCHING PATTERN "*.h") + ENDIF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) +ENDIF (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 2.5) + +IF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) + SET_TARGET_PROPERTIES(BulletDynamics PROPERTIES FRAMEWORK true) + + SET_TARGET_PROPERTIES(BulletDynamics PROPERTIES PUBLIC_HEADER "${Root_HDRS}") + # Have to list out sub-directories manually: + SET_PROPERTY(SOURCE ${ConstraintSolver_HDRS} PROPERTY MACOSX_PACKAGE_LOCATION Headers/ConstraintSolver) + SET_PROPERTY(SOURCE ${Dynamics_HDRS} PROPERTY MACOSX_PACKAGE_LOCATION Headers/Dynamics) + SET_PROPERTY(SOURCE ${Vehicle_HDRS} PROPERTY MACOSX_PACKAGE_LOCATION Headers/Vehicle) + SET_PROPERTY(SOURCE ${Character_HDRS} PROPERTY MACOSX_PACKAGE_LOCATION Headers/Character) + +ENDIF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) diff --git a/Engine/lib/bullet/src/BulletDynamics/Character/btCharacterControllerInterface.h b/Engine/lib/bullet/src/BulletDynamics/Character/btCharacterControllerInterface.h new file mode 100644 index 000000000..19373daa2 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Character/btCharacterControllerInterface.h @@ -0,0 +1,45 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2008 Erwin Coumans http://bulletphysics.com + +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 CHARACTER_CONTROLLER_INTERFACE_H +#define CHARACTER_CONTROLLER_INTERFACE_H + +#include "LinearMath/btVector3.h" +#include "BulletDynamics/Dynamics/btActionInterface.h" + +class btCollisionShape; +class btRigidBody; +class btCollisionWorld; + +class btCharacterControllerInterface : public btActionInterface +{ +public: + btCharacterControllerInterface () {}; + virtual ~btCharacterControllerInterface () {}; + + virtual void setWalkDirection(const btVector3& walkDirection) = 0; + virtual void setVelocityForTimeInterval(const btVector3& velocity, btScalar timeInterval) = 0; + virtual void reset () = 0; + virtual void warp (const btVector3& origin) = 0; + + virtual void preStep ( btCollisionWorld* collisionWorld) = 0; + virtual void playerStep (btCollisionWorld* collisionWorld, btScalar dt) = 0; + virtual bool canJump () const = 0; + virtual void jump () = 0; + + virtual bool onGround () const = 0; +}; + +#endif diff --git a/Engine/lib/bullet/src/BulletDynamics/Character/btKinematicCharacterController.cpp b/Engine/lib/bullet/src/BulletDynamics/Character/btKinematicCharacterController.cpp new file mode 100644 index 000000000..4a2d6089e --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Character/btKinematicCharacterController.cpp @@ -0,0 +1,547 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2008 Erwin Coumans http://bulletphysics.com + +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 "LinearMath/btIDebugDraw.h" +#include "BulletCollision/CollisionDispatch/btGhostObject.h" +#include "BulletCollision/CollisionShapes/btMultiSphereShape.h" +#include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h" +#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btCollisionWorld.h" +#include "LinearMath/btDefaultMotionState.h" +#include "btKinematicCharacterController.h" + +static btVector3 upAxisDirection[3] = { btVector3(1.0f, 0.0f, 0.0f), btVector3(0.0f, 1.0f, 0.0f), btVector3(0.0f, 0.0f, 1.0f) }; + + +// static helper method +static btVector3 +getNormalizedVector(const btVector3& v) +{ + btVector3 n = v.normalized(); + if (n.length() < SIMD_EPSILON) { + n.setValue(0, 0, 0); + } + return n; +} + + +///@todo Interact with dynamic objects, +///Ride kinematicly animated platforms properly +///More realistic (or maybe just a config option) falling +/// -> Should integrate falling velocity manually and use that in stepDown() +///Support jumping +///Support ducking +class btKinematicClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback +{ +public: + btKinematicClosestNotMeRayResultCallback (btCollisionObject* me) : btCollisionWorld::ClosestRayResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)) + { + m_me = me; + } + + virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace) + { + if (rayResult.m_collisionObject == m_me) + return 1.0; + + return ClosestRayResultCallback::addSingleResult (rayResult, normalInWorldSpace); + } +protected: + btCollisionObject* m_me; +}; + +class btKinematicClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback +{ +public: + btKinematicClosestNotMeConvexResultCallback (btCollisionObject* me) : btCollisionWorld::ClosestConvexResultCallback(btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0)) + { + m_me = me; + } + + virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace) + { + if (convexResult.m_hitCollisionObject == m_me) + return 1.0; + + return ClosestConvexResultCallback::addSingleResult (convexResult, normalInWorldSpace); + } +protected: + btCollisionObject* m_me; +}; + +/* + * Returns the reflection direction of a ray going 'direction' hitting a surface with normal 'normal' + * + * from: http://www-cs-students.stanford.edu/~adityagp/final/node3.html + */ +btVector3 btKinematicCharacterController::computeReflectionDirection (const btVector3& direction, const btVector3& normal) +{ + return direction - (btScalar(2.0) * direction.dot(normal)) * normal; +} + +/* + * Returns the portion of 'direction' that is parallel to 'normal' + */ +btVector3 btKinematicCharacterController::parallelComponent (const btVector3& direction, const btVector3& normal) +{ + btScalar magnitude = direction.dot(normal); + return normal * magnitude; +} + +/* + * Returns the portion of 'direction' that is perpindicular to 'normal' + */ +btVector3 btKinematicCharacterController::perpindicularComponent (const btVector3& direction, const btVector3& normal) +{ + return direction - parallelComponent(direction, normal); +} + +btKinematicCharacterController::btKinematicCharacterController (btPairCachingGhostObject* ghostObject,btConvexShape* convexShape,btScalar stepHeight, int upAxis) +{ + m_upAxis = upAxis; + m_addedMargin = 0.02f; + m_walkDirection.setValue(0,0,0); + m_useGhostObjectSweepTest = true; + m_ghostObject = ghostObject; + m_stepHeight = stepHeight; + m_turnAngle = btScalar(0.0); + m_convexShape=convexShape; + m_useWalkDirection = true; // use walk direction by default, legacy behavior + m_velocityTimeInterval = 0.0; +} + +btKinematicCharacterController::~btKinematicCharacterController () +{ +} + +btPairCachingGhostObject* btKinematicCharacterController::getGhostObject() +{ + return m_ghostObject; +} + +bool btKinematicCharacterController::recoverFromPenetration ( btCollisionWorld* collisionWorld) +{ + + bool penetration = false; + + collisionWorld->getDispatcher()->dispatchAllCollisionPairs(m_ghostObject->getOverlappingPairCache(), collisionWorld->getDispatchInfo(), collisionWorld->getDispatcher()); + + m_currentPosition = m_ghostObject->getWorldTransform().getOrigin(); + + btScalar maxPen = btScalar(0.0); + for (int i = 0; i < m_ghostObject->getOverlappingPairCache()->getNumOverlappingPairs(); i++) + { + m_manifoldArray.resize(0); + + btBroadphasePair* collisionPair = &m_ghostObject->getOverlappingPairCache()->getOverlappingPairArray()[i]; + + if (collisionPair->m_algorithm) + collisionPair->m_algorithm->getAllContactManifolds(m_manifoldArray); + + + for (int j=0;jgetBody0() == m_ghostObject ? btScalar(-1.0) : btScalar(1.0); + for (int p=0;pgetNumContacts();p++) + { + const btManifoldPoint&pt = manifold->getContactPoint(p); + + if (pt.getDistance() < 0.0) + { + if (pt.getDistance() < maxPen) + { + maxPen = pt.getDistance(); + m_touchingNormal = pt.m_normalWorldOnB * directionSign;//?? + + } + m_currentPosition += pt.m_normalWorldOnB * directionSign * pt.getDistance() * btScalar(0.2); + penetration = true; + } else { + //printf("touching %f\n", pt.getDistance()); + } + } + + //manifold->clearManifold(); + } + } + btTransform newTrans = m_ghostObject->getWorldTransform(); + newTrans.setOrigin(m_currentPosition); + m_ghostObject->setWorldTransform(newTrans); +// printf("m_touchingNormal = %f,%f,%f\n",m_touchingNormal[0],m_touchingNormal[1],m_touchingNormal[2]); + return penetration; +} + +void btKinematicCharacterController::stepUp ( btCollisionWorld* world) +{ + // phase 1: up + btTransform start, end; + m_targetPosition = m_currentPosition + upAxisDirection[m_upAxis] * m_stepHeight; + + start.setIdentity (); + end.setIdentity (); + + /* FIXME: Handle penetration properly */ + start.setOrigin (m_currentPosition + upAxisDirection[m_upAxis] * btScalar(0.1f)); + end.setOrigin (m_targetPosition); + + btKinematicClosestNotMeConvexResultCallback callback (m_ghostObject); + callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; + callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; + + if (m_useGhostObjectSweepTest) + { + m_ghostObject->convexSweepTest (m_convexShape, start, end, callback, world->getDispatchInfo().m_allowedCcdPenetration); + } + else + { + world->convexSweepTest (m_convexShape, start, end, callback); + } + + if (callback.hasHit()) + { + // we moved up only a fraction of the step height + m_currentStepOffset = m_stepHeight * callback.m_closestHitFraction; + m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction); + } else { + m_currentStepOffset = m_stepHeight; + m_currentPosition = m_targetPosition; + } +} + +void btKinematicCharacterController::updateTargetPositionBasedOnCollision (const btVector3& hitNormal, btScalar tangentMag, btScalar normalMag) +{ + btVector3 movementDirection = m_targetPosition - m_currentPosition; + btScalar movementLength = movementDirection.length(); + if (movementLength>SIMD_EPSILON) + { + movementDirection.normalize(); + + btVector3 reflectDir = computeReflectionDirection (movementDirection, hitNormal); + reflectDir.normalize(); + + btVector3 parallelDir, perpindicularDir; + + parallelDir = parallelComponent (reflectDir, hitNormal); + perpindicularDir = perpindicularComponent (reflectDir, hitNormal); + + m_targetPosition = m_currentPosition; + if (0)//tangentMag != 0.0) + { + btVector3 parComponent = parallelDir * btScalar (tangentMag*movementLength); +// printf("parComponent=%f,%f,%f\n",parComponent[0],parComponent[1],parComponent[2]); + m_targetPosition += parComponent; + } + + if (normalMag != 0.0) + { + btVector3 perpComponent = perpindicularDir * btScalar (normalMag*movementLength); +// printf("perpComponent=%f,%f,%f\n",perpComponent[0],perpComponent[1],perpComponent[2]); + m_targetPosition += perpComponent; + } + } else + { +// printf("movementLength don't normalize a zero vector\n"); + } +} + +void btKinematicCharacterController::stepForwardAndStrafe ( btCollisionWorld* collisionWorld, const btVector3& walkMove) +{ + // printf("m_normalizedDirection=%f,%f,%f\n", + // m_normalizedDirection[0],m_normalizedDirection[1],m_normalizedDirection[2]); + // phase 2: forward and strafe + btTransform start, end; + m_targetPosition = m_currentPosition + walkMove; + start.setIdentity (); + end.setIdentity (); + + btScalar fraction = 1.0; + btScalar distance2 = (m_currentPosition-m_targetPosition).length2(); +// printf("distance2=%f\n",distance2); + + if (m_touchingContact) + { + if (m_normalizedDirection.dot(m_touchingNormal) > btScalar(0.0)) + updateTargetPositionBasedOnCollision (m_touchingNormal); + } + + int maxIter = 10; + + while (fraction > btScalar(0.01) && maxIter-- > 0) + { + start.setOrigin (m_currentPosition); + end.setOrigin (m_targetPosition); + + btKinematicClosestNotMeConvexResultCallback callback (m_ghostObject); + callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; + callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; + + + btScalar margin = m_convexShape->getMargin(); + m_convexShape->setMargin(margin + m_addedMargin); + + + if (m_useGhostObjectSweepTest) + { + m_ghostObject->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); + } else + { + collisionWorld->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); + } + + m_convexShape->setMargin(margin); + + + fraction -= callback.m_closestHitFraction; + + if (callback.hasHit()) + { + // we moved only a fraction + btScalar hitDistance = (callback.m_hitPointWorld - m_currentPosition).length(); + if (hitDistance<0.f) + { +// printf("neg dist?\n"); + } + + /* If the distance is farther than the collision margin, move */ + if (hitDistance > m_addedMargin) + { +// printf("callback.m_closestHitFraction=%f\n",callback.m_closestHitFraction); + m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction); + } + + updateTargetPositionBasedOnCollision (callback.m_hitNormalWorld); + btVector3 currentDir = m_targetPosition - m_currentPosition; + distance2 = currentDir.length2(); + if (distance2 > SIMD_EPSILON) + { + currentDir.normalize(); + /* See Quake2: "If velocity is against original velocity, stop ead to avoid tiny oscilations in sloping corners." */ + if (currentDir.dot(m_normalizedDirection) <= btScalar(0.0)) + { + break; + } + } else + { +// printf("currentDir: don't normalize a zero vector\n"); + break; + } + } else { + // we moved whole way + m_currentPosition = m_targetPosition; + } + + // if (callback.m_closestHitFraction == 0.f) + // break; + + } +} + +void btKinematicCharacterController::stepDown ( btCollisionWorld* collisionWorld, btScalar dt) +{ + btTransform start, end; + + // phase 3: down + btVector3 step_drop = upAxisDirection[m_upAxis] * m_currentStepOffset; + btVector3 gravity_drop = upAxisDirection[m_upAxis] * m_stepHeight; + m_targetPosition -= (step_drop + gravity_drop); + + start.setIdentity (); + end.setIdentity (); + + start.setOrigin (m_currentPosition); + end.setOrigin (m_targetPosition); + + btKinematicClosestNotMeConvexResultCallback callback (m_ghostObject); + callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; + callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; + + if (m_useGhostObjectSweepTest) + { + m_ghostObject->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); + } else + { + collisionWorld->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); + } + + if (callback.hasHit()) + { + // we dropped a fraction of the height -> hit floor + m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction); + } else { + // we dropped the full height + + m_currentPosition = m_targetPosition; + } +} + + + +void btKinematicCharacterController::setWalkDirection +( +const btVector3& walkDirection +) +{ + m_useWalkDirection = true; + m_walkDirection = walkDirection; + m_normalizedDirection = getNormalizedVector(m_walkDirection); +} + + + +void btKinematicCharacterController::setVelocityForTimeInterval +( +const btVector3& velocity, +btScalar timeInterval +) +{ +// printf("setVelocity!\n"); +// printf(" interval: %f\n", timeInterval); +// printf(" velocity: (%f, %f, %f)\n", +// velocity.x(), velocity.y(), velocity.z()); + + m_useWalkDirection = false; + m_walkDirection = velocity; + m_normalizedDirection = getNormalizedVector(m_walkDirection); + m_velocityTimeInterval = timeInterval; +} + + + +void btKinematicCharacterController::reset () +{ +} + +void btKinematicCharacterController::warp (const btVector3& origin) +{ + btTransform xform; + xform.setIdentity(); + xform.setOrigin (origin); + m_ghostObject->setWorldTransform (xform); +} + + +void btKinematicCharacterController::preStep ( btCollisionWorld* collisionWorld) +{ + + int numPenetrationLoops = 0; + m_touchingContact = false; + while (recoverFromPenetration (collisionWorld)) + { + numPenetrationLoops++; + m_touchingContact = true; + if (numPenetrationLoops > 4) + { +// printf("character could not recover from penetration = %d\n", numPenetrationLoops); + break; + } + } + + m_currentPosition = m_ghostObject->getWorldTransform().getOrigin(); + m_targetPosition = m_currentPosition; +// printf("m_targetPosition=%f,%f,%f\n",m_targetPosition[0],m_targetPosition[1],m_targetPosition[2]); + + +} + +void btKinematicCharacterController::playerStep ( btCollisionWorld* collisionWorld, btScalar dt) +{ +// printf("playerStep(): "); +// printf(" dt = %f", dt); + + // quick check... + if (!m_useWalkDirection && m_velocityTimeInterval <= 0.0) { +// printf("\n"); + return; // no motion + } + + btTransform xform; + xform = m_ghostObject->getWorldTransform (); + +// printf("walkDirection(%f,%f,%f)\n",walkDirection[0],walkDirection[1],walkDirection[2]); +// printf("walkSpeed=%f\n",walkSpeed); + + stepUp (collisionWorld); + if (m_useWalkDirection) { + stepForwardAndStrafe (collisionWorld, m_walkDirection); + } else { + //printf(" time: %f", m_velocityTimeInterval); + // still have some time left for moving! + btScalar dtMoving = + (dt < m_velocityTimeInterval) ? dt : m_velocityTimeInterval; + m_velocityTimeInterval -= dt; + + // how far will we move while we are moving? + btVector3 move = m_walkDirection * dtMoving; + + // printf(" dtMoving: %f", dtMoving); + + // okay, step + stepForwardAndStrafe(collisionWorld, move); + } + stepDown (collisionWorld, dt); + + // printf("\n"); + + xform.setOrigin (m_currentPosition); + m_ghostObject->setWorldTransform (xform); +} + +void btKinematicCharacterController::setFallSpeed (btScalar fallSpeed) +{ + m_fallSpeed = fallSpeed; +} + +void btKinematicCharacterController::setJumpSpeed (btScalar jumpSpeed) +{ + m_jumpSpeed = jumpSpeed; +} + +void btKinematicCharacterController::setMaxJumpHeight (btScalar maxJumpHeight) +{ + m_maxJumpHeight = maxJumpHeight; +} + +bool btKinematicCharacterController::canJump () const +{ + return onGround(); +} + +void btKinematicCharacterController::jump () +{ + if (!canJump()) + return; + +#if 0 + currently no jumping. + btTransform xform; + m_rigidBody->getMotionState()->getWorldTransform (xform); + btVector3 up = xform.getBasis()[1]; + up.normalize (); + btScalar magnitude = (btScalar(1.0)/m_rigidBody->getInvMass()) * btScalar(8.0); + m_rigidBody->applyCentralImpulse (up * magnitude); +#endif +} + +bool btKinematicCharacterController::onGround () const +{ + return true; +} + + +void btKinematicCharacterController::debugDraw(btIDebugDraw* debugDrawer) +{ +} diff --git a/Engine/lib/bullet/src/BulletDynamics/Character/btKinematicCharacterController.h b/Engine/lib/bullet/src/BulletDynamics/Character/btKinematicCharacterController.h new file mode 100644 index 000000000..4fc56c056 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Character/btKinematicCharacterController.h @@ -0,0 +1,142 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2008 Erwin Coumans http://bulletphysics.com + +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 KINEMATIC_CHARACTER_CONTROLLER_H +#define KINEMATIC_CHARACTER_CONTROLLER_H + +#include "LinearMath/btVector3.h" + +#include "btCharacterControllerInterface.h" + +#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" + + +class btCollisionShape; +class btRigidBody; +class btCollisionWorld; +class btCollisionDispatcher; +class btPairCachingGhostObject; + +///btKinematicCharacterController is an object that supports a sliding motion in a world. +///It uses a ghost object and convex sweep test to test for upcoming collisions. This is combined with discrete collision detection to recover from penetrations. +///Interaction between btKinematicCharacterController and dynamic rigid bodies needs to be explicity implemented by the user. +class btKinematicCharacterController : public btCharacterControllerInterface +{ +protected: + btScalar m_halfHeight; + + btPairCachingGhostObject* m_ghostObject; + btConvexShape* m_convexShape;//is also in m_ghostObject, but it needs to be convex, so we store it here to avoid upcast + + btScalar m_fallSpeed; + btScalar m_jumpSpeed; + btScalar m_maxJumpHeight; + + btScalar m_turnAngle; + + btScalar m_stepHeight; + + btScalar m_addedMargin;//@todo: remove this and fix the code + + ///this is the desired walk direction, set by the user + btVector3 m_walkDirection; + btVector3 m_normalizedDirection; + + //some internal variables + btVector3 m_currentPosition; + btScalar m_currentStepOffset; + btVector3 m_targetPosition; + + ///keep track of the contact manifolds + btManifoldArray m_manifoldArray; + + bool m_touchingContact; + btVector3 m_touchingNormal; + + bool m_useGhostObjectSweepTest; + bool m_useWalkDirection; + float m_velocityTimeInterval; + int m_upAxis; + + btVector3 computeReflectionDirection (const btVector3& direction, const btVector3& normal); + btVector3 parallelComponent (const btVector3& direction, const btVector3& normal); + btVector3 perpindicularComponent (const btVector3& direction, const btVector3& normal); + + bool recoverFromPenetration ( btCollisionWorld* collisionWorld); + void stepUp (btCollisionWorld* collisionWorld); + void updateTargetPositionBasedOnCollision (const btVector3& hit_normal, btScalar tangentMag = btScalar(0.0), btScalar normalMag = btScalar(1.0)); + void stepForwardAndStrafe (btCollisionWorld* collisionWorld, const btVector3& walkMove); + void stepDown (btCollisionWorld* collisionWorld, btScalar dt); +public: + btKinematicCharacterController (btPairCachingGhostObject* ghostObject,btConvexShape* convexShape,btScalar stepHeight, int upAxis = 1); + ~btKinematicCharacterController (); + + + ///btActionInterface interface + virtual void updateAction( btCollisionWorld* collisionWorld,btScalar deltaTime) + { + preStep ( collisionWorld); + playerStep (collisionWorld, deltaTime); + } + + ///btActionInterface interface + void debugDraw(btIDebugDraw* debugDrawer); + + void setUpAxis (int axis) + { + if (axis < 0) + axis = 0; + if (axis > 2) + axis = 2; + m_upAxis = axis; + } + + /// This should probably be called setPositionIncrementPerSimulatorStep. + /// This is neither a direction nor a velocity, but the amount to + /// increment the position each simulation iteration, regardless + /// of dt. + /// This call will reset any velocity set by setVelocityForTimeInterval(). + virtual void setWalkDirection(const btVector3& walkDirection); + + /// Caller provides a velocity with which the character should move for + /// the given time period. After the time period, velocity is reset + /// to zero. + /// This call will reset any walk direction set by setWalkDirection(). + /// Negative time intervals will result in no motion. + virtual void setVelocityForTimeInterval(const btVector3& velocity, + btScalar timeInterval); + + void reset (); + void warp (const btVector3& origin); + + void preStep ( btCollisionWorld* collisionWorld); + void playerStep ( btCollisionWorld* collisionWorld, btScalar dt); + + void setFallSpeed (btScalar fallSpeed); + void setJumpSpeed (btScalar jumpSpeed); + void setMaxJumpHeight (btScalar maxJumpHeight); + bool canJump () const; + void jump (); + + btPairCachingGhostObject* getGhostObject(); + void setUseGhostSweepTest(bool useGhostObjectSweepTest) + { + m_useGhostObjectSweepTest = useGhostObjectSweepTest; + } + + bool onGround () const; +}; + +#endif // KINEMATIC_CHARACTER_CONTROLLER_H diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btConeTwistConstraint.cpp b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btConeTwistConstraint.cpp new file mode 100644 index 000000000..03a851219 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btConeTwistConstraint.cpp @@ -0,0 +1,1026 @@ +/* +Bullet Continuous Collision Detection and Physics Library +btConeTwistConstraint is Copyright (c) 2007 Starbreeze Studios + +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. + +Written by: Marcus Hennix +*/ + + +#include "btConeTwistConstraint.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include "LinearMath/btTransformUtil.h" +#include "LinearMath/btMinMax.h" +#include + + + +//#define CONETWIST_USE_OBSOLETE_SOLVER true +#define CONETWIST_USE_OBSOLETE_SOLVER false +#define CONETWIST_DEF_FIX_THRESH btScalar(.05f) + + +SIMD_FORCE_INLINE btScalar computeAngularImpulseDenominator(const btVector3& axis, const btMatrix3x3& invInertiaWorld) +{ + btVector3 vec = axis * invInertiaWorld; + return axis.dot(vec); +} + + +btConeTwistConstraint::btConeTwistConstraint() +:btTypedConstraint(CONETWIST_CONSTRAINT_TYPE), +m_useSolveConstraintObsolete(CONETWIST_USE_OBSOLETE_SOLVER) +{ +} + + +btConeTwistConstraint::btConeTwistConstraint(btRigidBody& rbA,btRigidBody& rbB, + const btTransform& rbAFrame,const btTransform& rbBFrame) + :btTypedConstraint(CONETWIST_CONSTRAINT_TYPE, rbA,rbB),m_rbAFrame(rbAFrame),m_rbBFrame(rbBFrame), + m_angularOnly(false), + m_useSolveConstraintObsolete(CONETWIST_USE_OBSOLETE_SOLVER) +{ + init(); +} + +btConeTwistConstraint::btConeTwistConstraint(btRigidBody& rbA,const btTransform& rbAFrame) + :btTypedConstraint(CONETWIST_CONSTRAINT_TYPE,rbA),m_rbAFrame(rbAFrame), + m_angularOnly(false), + m_useSolveConstraintObsolete(CONETWIST_USE_OBSOLETE_SOLVER) +{ + m_rbBFrame = m_rbAFrame; + init(); +} + + +void btConeTwistConstraint::init() +{ + m_angularOnly = false; + m_solveTwistLimit = false; + m_solveSwingLimit = false; + m_bMotorEnabled = false; + m_maxMotorImpulse = btScalar(-1); + + setLimit(btScalar(BT_LARGE_FLOAT), btScalar(BT_LARGE_FLOAT), btScalar(BT_LARGE_FLOAT)); + m_damping = btScalar(0.01); + m_fixThresh = CONETWIST_DEF_FIX_THRESH; +} + + +void btConeTwistConstraint::getInfo1 (btConstraintInfo1* info) +{ + if (m_useSolveConstraintObsolete) + { + info->m_numConstraintRows = 0; + info->nub = 0; + } + else + { + info->m_numConstraintRows = 3; + info->nub = 3; + calcAngleInfo2(m_rbA.getCenterOfMassTransform(),m_rbB.getCenterOfMassTransform(),m_rbA.getInvInertiaTensorWorld(),m_rbB.getInvInertiaTensorWorld()); + if(m_solveSwingLimit) + { + info->m_numConstraintRows++; + info->nub--; + if((m_swingSpan1 < m_fixThresh) && (m_swingSpan2 < m_fixThresh)) + { + info->m_numConstraintRows++; + info->nub--; + } + } + if(m_solveTwistLimit) + { + info->m_numConstraintRows++; + info->nub--; + } + } +} + +void btConeTwistConstraint::getInfo1NonVirtual (btConstraintInfo1* info) +{ + //always reserve 6 rows: object transform is not available on SPU + info->m_numConstraintRows = 6; + info->nub = 0; + +} + + +void btConeTwistConstraint::getInfo2 (btConstraintInfo2* info) +{ + getInfo2NonVirtual(info,m_rbA.getCenterOfMassTransform(),m_rbB.getCenterOfMassTransform(),m_rbA.getInvInertiaTensorWorld(),m_rbB.getInvInertiaTensorWorld()); +} + +void btConeTwistConstraint::getInfo2NonVirtual (btConstraintInfo2* info,const btTransform& transA,const btTransform& transB,const btMatrix3x3& invInertiaWorldA,const btMatrix3x3& invInertiaWorldB) +{ + calcAngleInfo2(transA,transB,invInertiaWorldA,invInertiaWorldB); + + btAssert(!m_useSolveConstraintObsolete); + // set jacobian + info->m_J1linearAxis[0] = 1; + info->m_J1linearAxis[info->rowskip+1] = 1; + info->m_J1linearAxis[2*info->rowskip+2] = 1; + btVector3 a1 = transA.getBasis() * m_rbAFrame.getOrigin(); + { + btVector3* angular0 = (btVector3*)(info->m_J1angularAxis); + btVector3* angular1 = (btVector3*)(info->m_J1angularAxis+info->rowskip); + btVector3* angular2 = (btVector3*)(info->m_J1angularAxis+2*info->rowskip); + btVector3 a1neg = -a1; + a1neg.getSkewSymmetricMatrix(angular0,angular1,angular2); + } + btVector3 a2 = transB.getBasis() * m_rbBFrame.getOrigin(); + { + btVector3* angular0 = (btVector3*)(info->m_J2angularAxis); + btVector3* angular1 = (btVector3*)(info->m_J2angularAxis+info->rowskip); + btVector3* angular2 = (btVector3*)(info->m_J2angularAxis+2*info->rowskip); + a2.getSkewSymmetricMatrix(angular0,angular1,angular2); + } + // set right hand side + btScalar k = info->fps * info->erp; + int j; + for (j=0; j<3; j++) + { + info->m_constraintError[j*info->rowskip] = k * (a2[j] + transB.getOrigin()[j] - a1[j] - transA.getOrigin()[j]); + info->m_lowerLimit[j*info->rowskip] = -SIMD_INFINITY; + info->m_upperLimit[j*info->rowskip] = SIMD_INFINITY; + } + int row = 3; + int srow = row * info->rowskip; + btVector3 ax1; + // angular limits + if(m_solveSwingLimit) + { + btScalar *J1 = info->m_J1angularAxis; + btScalar *J2 = info->m_J2angularAxis; + if((m_swingSpan1 < m_fixThresh) && (m_swingSpan2 < m_fixThresh)) + { + btTransform trA = transA*m_rbAFrame; + btVector3 p = trA.getBasis().getColumn(1); + btVector3 q = trA.getBasis().getColumn(2); + int srow1 = srow + info->rowskip; + J1[srow+0] = p[0]; + J1[srow+1] = p[1]; + J1[srow+2] = p[2]; + J1[srow1+0] = q[0]; + J1[srow1+1] = q[1]; + J1[srow1+2] = q[2]; + J2[srow+0] = -p[0]; + J2[srow+1] = -p[1]; + J2[srow+2] = -p[2]; + J2[srow1+0] = -q[0]; + J2[srow1+1] = -q[1]; + J2[srow1+2] = -q[2]; + btScalar fact = info->fps * m_relaxationFactor; + info->m_constraintError[srow] = fact * m_swingAxis.dot(p); + info->m_constraintError[srow1] = fact * m_swingAxis.dot(q); + info->m_lowerLimit[srow] = -SIMD_INFINITY; + info->m_upperLimit[srow] = SIMD_INFINITY; + info->m_lowerLimit[srow1] = -SIMD_INFINITY; + info->m_upperLimit[srow1] = SIMD_INFINITY; + srow = srow1 + info->rowskip; + } + else + { + ax1 = m_swingAxis * m_relaxationFactor * m_relaxationFactor; + J1[srow+0] = ax1[0]; + J1[srow+1] = ax1[1]; + J1[srow+2] = ax1[2]; + J2[srow+0] = -ax1[0]; + J2[srow+1] = -ax1[1]; + J2[srow+2] = -ax1[2]; + btScalar k = info->fps * m_biasFactor; + + info->m_constraintError[srow] = k * m_swingCorrection; + info->cfm[srow] = 0.0f; + // m_swingCorrection is always positive or 0 + info->m_lowerLimit[srow] = 0; + info->m_upperLimit[srow] = SIMD_INFINITY; + srow += info->rowskip; + } + } + if(m_solveTwistLimit) + { + ax1 = m_twistAxis * m_relaxationFactor * m_relaxationFactor; + btScalar *J1 = info->m_J1angularAxis; + btScalar *J2 = info->m_J2angularAxis; + J1[srow+0] = ax1[0]; + J1[srow+1] = ax1[1]; + J1[srow+2] = ax1[2]; + J2[srow+0] = -ax1[0]; + J2[srow+1] = -ax1[1]; + J2[srow+2] = -ax1[2]; + btScalar k = info->fps * m_biasFactor; + info->m_constraintError[srow] = k * m_twistCorrection; + info->cfm[srow] = 0.0f; + if(m_twistSpan > 0.0f) + { + + if(m_twistCorrection > 0.0f) + { + info->m_lowerLimit[srow] = 0; + info->m_upperLimit[srow] = SIMD_INFINITY; + } + else + { + info->m_lowerLimit[srow] = -SIMD_INFINITY; + info->m_upperLimit[srow] = 0; + } + } + else + { + info->m_lowerLimit[srow] = -SIMD_INFINITY; + info->m_upperLimit[srow] = SIMD_INFINITY; + } + srow += info->rowskip; + } +} + + + +void btConeTwistConstraint::buildJacobian() +{ + if (m_useSolveConstraintObsolete) + { + m_appliedImpulse = btScalar(0.); + m_accTwistLimitImpulse = btScalar(0.); + m_accSwingLimitImpulse = btScalar(0.); + m_accMotorImpulse = btVector3(0.,0.,0.); + + if (!m_angularOnly) + { + btVector3 pivotAInW = m_rbA.getCenterOfMassTransform()*m_rbAFrame.getOrigin(); + btVector3 pivotBInW = m_rbB.getCenterOfMassTransform()*m_rbBFrame.getOrigin(); + btVector3 relPos = pivotBInW - pivotAInW; + + btVector3 normal[3]; + if (relPos.length2() > SIMD_EPSILON) + { + normal[0] = relPos.normalized(); + } + else + { + normal[0].setValue(btScalar(1.0),0,0); + } + + btPlaneSpace1(normal[0], normal[1], normal[2]); + + for (int i=0;i<3;i++) + { + new (&m_jac[i]) btJacobianEntry( + m_rbA.getCenterOfMassTransform().getBasis().transpose(), + m_rbB.getCenterOfMassTransform().getBasis().transpose(), + pivotAInW - m_rbA.getCenterOfMassPosition(), + pivotBInW - m_rbB.getCenterOfMassPosition(), + normal[i], + m_rbA.getInvInertiaDiagLocal(), + m_rbA.getInvMass(), + m_rbB.getInvInertiaDiagLocal(), + m_rbB.getInvMass()); + } + } + + calcAngleInfo2(m_rbA.getCenterOfMassTransform(),m_rbB.getCenterOfMassTransform(),m_rbA.getInvInertiaTensorWorld(),m_rbB.getInvInertiaTensorWorld()); + } +} + + + +void btConeTwistConstraint::solveConstraintObsolete(btSolverBody& bodyA,btSolverBody& bodyB,btScalar timeStep) +{ + #ifndef __SPU__ + if (m_useSolveConstraintObsolete) + { + btVector3 pivotAInW = m_rbA.getCenterOfMassTransform()*m_rbAFrame.getOrigin(); + btVector3 pivotBInW = m_rbB.getCenterOfMassTransform()*m_rbBFrame.getOrigin(); + + btScalar tau = btScalar(0.3); + + //linear part + if (!m_angularOnly) + { + btVector3 rel_pos1 = pivotAInW - m_rbA.getCenterOfMassPosition(); + btVector3 rel_pos2 = pivotBInW - m_rbB.getCenterOfMassPosition(); + + btVector3 vel1; + bodyA.getVelocityInLocalPointObsolete(rel_pos1,vel1); + btVector3 vel2; + bodyB.getVelocityInLocalPointObsolete(rel_pos2,vel2); + btVector3 vel = vel1 - vel2; + + for (int i=0;i<3;i++) + { + const btVector3& normal = m_jac[i].m_linearJointAxis; + btScalar jacDiagABInv = btScalar(1.) / m_jac[i].getDiagonal(); + + btScalar rel_vel; + rel_vel = normal.dot(vel); + //positional error (zeroth order error) + btScalar depth = -(pivotAInW - pivotBInW).dot(normal); //this is the error projected on the normal + btScalar impulse = depth*tau/timeStep * jacDiagABInv - rel_vel * jacDiagABInv; + m_appliedImpulse += impulse; + + btVector3 ftorqueAxis1 = rel_pos1.cross(normal); + btVector3 ftorqueAxis2 = rel_pos2.cross(normal); + bodyA.applyImpulse(normal*m_rbA.getInvMass(), m_rbA.getInvInertiaTensorWorld()*ftorqueAxis1,impulse); + bodyB.applyImpulse(normal*m_rbB.getInvMass(), m_rbB.getInvInertiaTensorWorld()*ftorqueAxis2,-impulse); + + } + } + + // apply motor + if (m_bMotorEnabled) + { + // compute current and predicted transforms + btTransform trACur = m_rbA.getCenterOfMassTransform(); + btTransform trBCur = m_rbB.getCenterOfMassTransform(); + btVector3 omegaA; bodyA.getAngularVelocity(omegaA); + btVector3 omegaB; bodyB.getAngularVelocity(omegaB); + btTransform trAPred; trAPred.setIdentity(); + btVector3 zerovec(0,0,0); + btTransformUtil::integrateTransform( + trACur, zerovec, omegaA, timeStep, trAPred); + btTransform trBPred; trBPred.setIdentity(); + btTransformUtil::integrateTransform( + trBCur, zerovec, omegaB, timeStep, trBPred); + + // compute desired transforms in world + btTransform trPose(m_qTarget); + btTransform trABDes = m_rbBFrame * trPose * m_rbAFrame.inverse(); + btTransform trADes = trBPred * trABDes; + btTransform trBDes = trAPred * trABDes.inverse(); + + // compute desired omegas in world + btVector3 omegaADes, omegaBDes; + + btTransformUtil::calculateVelocity(trACur, trADes, timeStep, zerovec, omegaADes); + btTransformUtil::calculateVelocity(trBCur, trBDes, timeStep, zerovec, omegaBDes); + + // compute delta omegas + btVector3 dOmegaA = omegaADes - omegaA; + btVector3 dOmegaB = omegaBDes - omegaB; + + // compute weighted avg axis of dOmega (weighting based on inertias) + btVector3 axisA, axisB; + btScalar kAxisAInv = 0, kAxisBInv = 0; + + if (dOmegaA.length2() > SIMD_EPSILON) + { + axisA = dOmegaA.normalized(); + kAxisAInv = getRigidBodyA().computeAngularImpulseDenominator(axisA); + } + + if (dOmegaB.length2() > SIMD_EPSILON) + { + axisB = dOmegaB.normalized(); + kAxisBInv = getRigidBodyB().computeAngularImpulseDenominator(axisB); + } + + btVector3 avgAxis = kAxisAInv * axisA + kAxisBInv * axisB; + + static bool bDoTorque = true; + if (bDoTorque && avgAxis.length2() > SIMD_EPSILON) + { + avgAxis.normalize(); + kAxisAInv = getRigidBodyA().computeAngularImpulseDenominator(avgAxis); + kAxisBInv = getRigidBodyB().computeAngularImpulseDenominator(avgAxis); + btScalar kInvCombined = kAxisAInv + kAxisBInv; + + btVector3 impulse = (kAxisAInv * dOmegaA - kAxisBInv * dOmegaB) / + (kInvCombined * kInvCombined); + + if (m_maxMotorImpulse >= 0) + { + btScalar fMaxImpulse = m_maxMotorImpulse; + if (m_bNormalizedMotorStrength) + fMaxImpulse = fMaxImpulse/kAxisAInv; + + btVector3 newUnclampedAccImpulse = m_accMotorImpulse + impulse; + btScalar newUnclampedMag = newUnclampedAccImpulse.length(); + if (newUnclampedMag > fMaxImpulse) + { + newUnclampedAccImpulse.normalize(); + newUnclampedAccImpulse *= fMaxImpulse; + impulse = newUnclampedAccImpulse - m_accMotorImpulse; + } + m_accMotorImpulse += impulse; + } + + btScalar impulseMag = impulse.length(); + btVector3 impulseAxis = impulse / impulseMag; + + bodyA.applyImpulse(btVector3(0,0,0), m_rbA.getInvInertiaTensorWorld()*impulseAxis, impulseMag); + bodyB.applyImpulse(btVector3(0,0,0), m_rbB.getInvInertiaTensorWorld()*impulseAxis, -impulseMag); + + } + } + else if (m_damping > SIMD_EPSILON) // no motor: do a little damping + { + btVector3 angVelA; bodyA.getAngularVelocity(angVelA); + btVector3 angVelB; bodyB.getAngularVelocity(angVelB); + btVector3 relVel = angVelB - angVelA; + if (relVel.length2() > SIMD_EPSILON) + { + btVector3 relVelAxis = relVel.normalized(); + btScalar m_kDamping = btScalar(1.) / + (getRigidBodyA().computeAngularImpulseDenominator(relVelAxis) + + getRigidBodyB().computeAngularImpulseDenominator(relVelAxis)); + btVector3 impulse = m_damping * m_kDamping * relVel; + + btScalar impulseMag = impulse.length(); + btVector3 impulseAxis = impulse / impulseMag; + bodyA.applyImpulse(btVector3(0,0,0), m_rbA.getInvInertiaTensorWorld()*impulseAxis, impulseMag); + bodyB.applyImpulse(btVector3(0,0,0), m_rbB.getInvInertiaTensorWorld()*impulseAxis, -impulseMag); + } + } + + // joint limits + { + ///solve angular part + btVector3 angVelA; + bodyA.getAngularVelocity(angVelA); + btVector3 angVelB; + bodyB.getAngularVelocity(angVelB); + + // solve swing limit + if (m_solveSwingLimit) + { + btScalar amplitude = m_swingLimitRatio * m_swingCorrection*m_biasFactor/timeStep; + btScalar relSwingVel = (angVelB - angVelA).dot(m_swingAxis); + if (relSwingVel > 0) + amplitude += m_swingLimitRatio * relSwingVel * m_relaxationFactor; + btScalar impulseMag = amplitude * m_kSwing; + + // Clamp the accumulated impulse + btScalar temp = m_accSwingLimitImpulse; + m_accSwingLimitImpulse = btMax(m_accSwingLimitImpulse + impulseMag, btScalar(0.0) ); + impulseMag = m_accSwingLimitImpulse - temp; + + btVector3 impulse = m_swingAxis * impulseMag; + + // don't let cone response affect twist + // (this can happen since body A's twist doesn't match body B's AND we use an elliptical cone limit) + { + btVector3 impulseTwistCouple = impulse.dot(m_twistAxisA) * m_twistAxisA; + btVector3 impulseNoTwistCouple = impulse - impulseTwistCouple; + impulse = impulseNoTwistCouple; + } + + impulseMag = impulse.length(); + btVector3 noTwistSwingAxis = impulse / impulseMag; + + bodyA.applyImpulse(btVector3(0,0,0), m_rbA.getInvInertiaTensorWorld()*noTwistSwingAxis, impulseMag); + bodyB.applyImpulse(btVector3(0,0,0), m_rbB.getInvInertiaTensorWorld()*noTwistSwingAxis, -impulseMag); + } + + + // solve twist limit + if (m_solveTwistLimit) + { + btScalar amplitude = m_twistLimitRatio * m_twistCorrection*m_biasFactor/timeStep; + btScalar relTwistVel = (angVelB - angVelA).dot( m_twistAxis ); + if (relTwistVel > 0) // only damp when moving towards limit (m_twistAxis flipping is important) + amplitude += m_twistLimitRatio * relTwistVel * m_relaxationFactor; + btScalar impulseMag = amplitude * m_kTwist; + + // Clamp the accumulated impulse + btScalar temp = m_accTwistLimitImpulse; + m_accTwistLimitImpulse = btMax(m_accTwistLimitImpulse + impulseMag, btScalar(0.0) ); + impulseMag = m_accTwistLimitImpulse - temp; + + btVector3 impulse = m_twistAxis * impulseMag; + + bodyA.applyImpulse(btVector3(0,0,0), m_rbA.getInvInertiaTensorWorld()*m_twistAxis,impulseMag); + bodyB.applyImpulse(btVector3(0,0,0), m_rbB.getInvInertiaTensorWorld()*m_twistAxis,-impulseMag); + } + } + } +#else +btAssert(0); +#endif //__SPU__ +} + + + + +void btConeTwistConstraint::updateRHS(btScalar timeStep) +{ + (void)timeStep; + +} + + +#ifndef __SPU__ +void btConeTwistConstraint::calcAngleInfo() +{ + m_swingCorrection = btScalar(0.); + m_twistLimitSign = btScalar(0.); + m_solveTwistLimit = false; + m_solveSwingLimit = false; + + btVector3 b1Axis1,b1Axis2,b1Axis3; + btVector3 b2Axis1,b2Axis2; + + b1Axis1 = getRigidBodyA().getCenterOfMassTransform().getBasis() * this->m_rbAFrame.getBasis().getColumn(0); + b2Axis1 = getRigidBodyB().getCenterOfMassTransform().getBasis() * this->m_rbBFrame.getBasis().getColumn(0); + + btScalar swing1=btScalar(0.),swing2 = btScalar(0.); + + btScalar swx=btScalar(0.),swy = btScalar(0.); + btScalar thresh = btScalar(10.); + btScalar fact; + + // Get Frame into world space + if (m_swingSpan1 >= btScalar(0.05f)) + { + b1Axis2 = getRigidBodyA().getCenterOfMassTransform().getBasis() * this->m_rbAFrame.getBasis().getColumn(1); + swx = b2Axis1.dot(b1Axis1); + swy = b2Axis1.dot(b1Axis2); + swing1 = btAtan2Fast(swy, swx); + fact = (swy*swy + swx*swx) * thresh * thresh; + fact = fact / (fact + btScalar(1.0)); + swing1 *= fact; + } + + if (m_swingSpan2 >= btScalar(0.05f)) + { + b1Axis3 = getRigidBodyA().getCenterOfMassTransform().getBasis() * this->m_rbAFrame.getBasis().getColumn(2); + swx = b2Axis1.dot(b1Axis1); + swy = b2Axis1.dot(b1Axis3); + swing2 = btAtan2Fast(swy, swx); + fact = (swy*swy + swx*swx) * thresh * thresh; + fact = fact / (fact + btScalar(1.0)); + swing2 *= fact; + } + + btScalar RMaxAngle1Sq = 1.0f / (m_swingSpan1*m_swingSpan1); + btScalar RMaxAngle2Sq = 1.0f / (m_swingSpan2*m_swingSpan2); + btScalar EllipseAngle = btFabs(swing1*swing1)* RMaxAngle1Sq + btFabs(swing2*swing2) * RMaxAngle2Sq; + + if (EllipseAngle > 1.0f) + { + m_swingCorrection = EllipseAngle-1.0f; + m_solveSwingLimit = true; + // Calculate necessary axis & factors + m_swingAxis = b2Axis1.cross(b1Axis2* b2Axis1.dot(b1Axis2) + b1Axis3* b2Axis1.dot(b1Axis3)); + m_swingAxis.normalize(); + btScalar swingAxisSign = (b2Axis1.dot(b1Axis1) >= 0.0f) ? 1.0f : -1.0f; + m_swingAxis *= swingAxisSign; + } + + // Twist limits + if (m_twistSpan >= btScalar(0.)) + { + btVector3 b2Axis2 = getRigidBodyB().getCenterOfMassTransform().getBasis() * this->m_rbBFrame.getBasis().getColumn(1); + btQuaternion rotationArc = shortestArcQuat(b2Axis1,b1Axis1); + btVector3 TwistRef = quatRotate(rotationArc,b2Axis2); + btScalar twist = btAtan2Fast( TwistRef.dot(b1Axis3), TwistRef.dot(b1Axis2) ); + m_twistAngle = twist; + +// btScalar lockedFreeFactor = (m_twistSpan > btScalar(0.05f)) ? m_limitSoftness : btScalar(0.); + btScalar lockedFreeFactor = (m_twistSpan > btScalar(0.05f)) ? btScalar(1.0f) : btScalar(0.); + if (twist <= -m_twistSpan*lockedFreeFactor) + { + m_twistCorrection = -(twist + m_twistSpan); + m_solveTwistLimit = true; + m_twistAxis = (b2Axis1 + b1Axis1) * 0.5f; + m_twistAxis.normalize(); + m_twistAxis *= -1.0f; + } + else if (twist > m_twistSpan*lockedFreeFactor) + { + m_twistCorrection = (twist - m_twistSpan); + m_solveTwistLimit = true; + m_twistAxis = (b2Axis1 + b1Axis1) * 0.5f; + m_twistAxis.normalize(); + } + } +} +#endif //__SPU__ + +static btVector3 vTwist(1,0,0); // twist axis in constraint's space + + + +void btConeTwistConstraint::calcAngleInfo2(const btTransform& transA, const btTransform& transB, const btMatrix3x3& invInertiaWorldA,const btMatrix3x3& invInertiaWorldB) +{ + m_swingCorrection = btScalar(0.); + m_twistLimitSign = btScalar(0.); + m_solveTwistLimit = false; + m_solveSwingLimit = false; + // compute rotation of A wrt B (in constraint space) + if (m_bMotorEnabled && (!m_useSolveConstraintObsolete)) + { // it is assumed that setMotorTarget() was alredy called + // and motor target m_qTarget is within constraint limits + // TODO : split rotation to pure swing and pure twist + // compute desired transforms in world + btTransform trPose(m_qTarget); + btTransform trA = transA * m_rbAFrame; + btTransform trB = transB * m_rbBFrame; + btTransform trDeltaAB = trB * trPose * trA.inverse(); + btQuaternion qDeltaAB = trDeltaAB.getRotation(); + btVector3 swingAxis = btVector3(qDeltaAB.x(), qDeltaAB.y(), qDeltaAB.z()); + m_swingAxis = swingAxis; + m_swingAxis.normalize(); + m_swingCorrection = qDeltaAB.getAngle(); + if(!btFuzzyZero(m_swingCorrection)) + { + m_solveSwingLimit = true; + } + return; + } + + + { + // compute rotation of A wrt B (in constraint space) + btQuaternion qA = transA.getRotation() * m_rbAFrame.getRotation(); + btQuaternion qB = transB.getRotation() * m_rbBFrame.getRotation(); + btQuaternion qAB = qB.inverse() * qA; + // split rotation into cone and twist + // (all this is done from B's perspective. Maybe I should be averaging axes...) + btVector3 vConeNoTwist = quatRotate(qAB, vTwist); vConeNoTwist.normalize(); + btQuaternion qABCone = shortestArcQuat(vTwist, vConeNoTwist); qABCone.normalize(); + btQuaternion qABTwist = qABCone.inverse() * qAB; qABTwist.normalize(); + + if (m_swingSpan1 >= m_fixThresh && m_swingSpan2 >= m_fixThresh) + { + btScalar swingAngle, swingLimit = 0; btVector3 swingAxis; + computeConeLimitInfo(qABCone, swingAngle, swingAxis, swingLimit); + + if (swingAngle > swingLimit * m_limitSoftness) + { + m_solveSwingLimit = true; + + // compute limit ratio: 0->1, where + // 0 == beginning of soft limit + // 1 == hard/real limit + m_swingLimitRatio = 1.f; + if (swingAngle < swingLimit && m_limitSoftness < 1.f - SIMD_EPSILON) + { + m_swingLimitRatio = (swingAngle - swingLimit * m_limitSoftness)/ + (swingLimit - swingLimit * m_limitSoftness); + } + + // swing correction tries to get back to soft limit + m_swingCorrection = swingAngle - (swingLimit * m_limitSoftness); + + // adjustment of swing axis (based on ellipse normal) + adjustSwingAxisToUseEllipseNormal(swingAxis); + + // Calculate necessary axis & factors + m_swingAxis = quatRotate(qB, -swingAxis); + + m_twistAxisA.setValue(0,0,0); + + m_kSwing = btScalar(1.) / + (computeAngularImpulseDenominator(m_swingAxis,invInertiaWorldA) + + computeAngularImpulseDenominator(m_swingAxis,invInertiaWorldB)); + } + } + else + { + // you haven't set any limits; + // or you're trying to set at least one of the swing limits too small. (if so, do you really want a conetwist constraint?) + // anyway, we have either hinge or fixed joint + btVector3 ivA = transA.getBasis() * m_rbAFrame.getBasis().getColumn(0); + btVector3 jvA = transA.getBasis() * m_rbAFrame.getBasis().getColumn(1); + btVector3 kvA = transA.getBasis() * m_rbAFrame.getBasis().getColumn(2); + btVector3 ivB = transB.getBasis() * m_rbBFrame.getBasis().getColumn(0); + btVector3 target; + btScalar x = ivB.dot(ivA); + btScalar y = ivB.dot(jvA); + btScalar z = ivB.dot(kvA); + if((m_swingSpan1 < m_fixThresh) && (m_swingSpan2 < m_fixThresh)) + { // fixed. We'll need to add one more row to constraint + if((!btFuzzyZero(y)) || (!(btFuzzyZero(z)))) + { + m_solveSwingLimit = true; + m_swingAxis = -ivB.cross(ivA); + } + } + else + { + if(m_swingSpan1 < m_fixThresh) + { // hinge around Y axis + if(!(btFuzzyZero(y))) + { + m_solveSwingLimit = true; + if(m_swingSpan2 >= m_fixThresh) + { + y = btScalar(0.f); + btScalar span2 = btAtan2(z, x); + if(span2 > m_swingSpan2) + { + x = btCos(m_swingSpan2); + z = btSin(m_swingSpan2); + } + else if(span2 < -m_swingSpan2) + { + x = btCos(m_swingSpan2); + z = -btSin(m_swingSpan2); + } + } + } + } + else + { // hinge around Z axis + if(!btFuzzyZero(z)) + { + m_solveSwingLimit = true; + if(m_swingSpan1 >= m_fixThresh) + { + z = btScalar(0.f); + btScalar span1 = btAtan2(y, x); + if(span1 > m_swingSpan1) + { + x = btCos(m_swingSpan1); + y = btSin(m_swingSpan1); + } + else if(span1 < -m_swingSpan1) + { + x = btCos(m_swingSpan1); + y = -btSin(m_swingSpan1); + } + } + } + } + target[0] = x * ivA[0] + y * jvA[0] + z * kvA[0]; + target[1] = x * ivA[1] + y * jvA[1] + z * kvA[1]; + target[2] = x * ivA[2] + y * jvA[2] + z * kvA[2]; + target.normalize(); + m_swingAxis = -ivB.cross(target); + m_swingCorrection = m_swingAxis.length(); + m_swingAxis.normalize(); + } + } + + if (m_twistSpan >= btScalar(0.f)) + { + btVector3 twistAxis; + computeTwistLimitInfo(qABTwist, m_twistAngle, twistAxis); + + if (m_twistAngle > m_twistSpan*m_limitSoftness) + { + m_solveTwistLimit = true; + + m_twistLimitRatio = 1.f; + if (m_twistAngle < m_twistSpan && m_limitSoftness < 1.f - SIMD_EPSILON) + { + m_twistLimitRatio = (m_twistAngle - m_twistSpan * m_limitSoftness)/ + (m_twistSpan - m_twistSpan * m_limitSoftness); + } + + // twist correction tries to get back to soft limit + m_twistCorrection = m_twistAngle - (m_twistSpan * m_limitSoftness); + + m_twistAxis = quatRotate(qB, -twistAxis); + + m_kTwist = btScalar(1.) / + (computeAngularImpulseDenominator(m_twistAxis,invInertiaWorldA) + + computeAngularImpulseDenominator(m_twistAxis,invInertiaWorldB)); + } + + if (m_solveSwingLimit) + m_twistAxisA = quatRotate(qA, -twistAxis); + } + else + { + m_twistAngle = btScalar(0.f); + } + } +} + + + +// given a cone rotation in constraint space, (pre: twist must already be removed) +// this method computes its corresponding swing angle and axis. +// more interestingly, it computes the cone/swing limit (angle) for this cone "pose". +void btConeTwistConstraint::computeConeLimitInfo(const btQuaternion& qCone, + btScalar& swingAngle, // out + btVector3& vSwingAxis, // out + btScalar& swingLimit) // out +{ + swingAngle = qCone.getAngle(); + if (swingAngle > SIMD_EPSILON) + { + vSwingAxis = btVector3(qCone.x(), qCone.y(), qCone.z()); + vSwingAxis.normalize(); + if (fabs(vSwingAxis.x()) > SIMD_EPSILON) + { + // non-zero twist?! this should never happen. + int wtf = 0; wtf = wtf; + } + + // Compute limit for given swing. tricky: + // Given a swing axis, we're looking for the intersection with the bounding cone ellipse. + // (Since we're dealing with angles, this ellipse is embedded on the surface of a sphere.) + + // For starters, compute the direction from center to surface of ellipse. + // This is just the perpendicular (ie. rotate 2D vector by PI/2) of the swing axis. + // (vSwingAxis is the cone rotation (in z,y); change vars and rotate to (x,y) coords.) + btScalar xEllipse = vSwingAxis.y(); + btScalar yEllipse = -vSwingAxis.z(); + + // Now, we use the slope of the vector (using x/yEllipse) and find the length + // of the line that intersects the ellipse: + // x^2 y^2 + // --- + --- = 1, where a and b are semi-major axes 2 and 1 respectively (ie. the limits) + // a^2 b^2 + // Do the math and it should be clear. + + swingLimit = m_swingSpan1; // if xEllipse == 0, we have a pure vSwingAxis.z rotation: just use swingspan1 + if (fabs(xEllipse) > SIMD_EPSILON) + { + btScalar surfaceSlope2 = (yEllipse*yEllipse)/(xEllipse*xEllipse); + btScalar norm = 1 / (m_swingSpan2 * m_swingSpan2); + norm += surfaceSlope2 / (m_swingSpan1 * m_swingSpan1); + btScalar swingLimit2 = (1 + surfaceSlope2) / norm; + swingLimit = sqrt(swingLimit2); + } + + // test! + /*swingLimit = m_swingSpan2; + if (fabs(vSwingAxis.z()) > SIMD_EPSILON) + { + btScalar mag_2 = m_swingSpan1*m_swingSpan1 + m_swingSpan2*m_swingSpan2; + btScalar sinphi = m_swingSpan2 / sqrt(mag_2); + btScalar phi = asin(sinphi); + btScalar theta = atan2(fabs(vSwingAxis.y()),fabs(vSwingAxis.z())); + btScalar alpha = 3.14159f - theta - phi; + btScalar sinalpha = sin(alpha); + swingLimit = m_swingSpan1 * sinphi/sinalpha; + }*/ + } + else if (swingAngle < 0) + { + // this should never happen! + int wtf = 0; wtf = wtf; + } +} + +btVector3 btConeTwistConstraint::GetPointForAngle(btScalar fAngleInRadians, btScalar fLength) const +{ + // compute x/y in ellipse using cone angle (0 -> 2*PI along surface of cone) + btScalar xEllipse = btCos(fAngleInRadians); + btScalar yEllipse = btSin(fAngleInRadians); + + // Use the slope of the vector (using x/yEllipse) and find the length + // of the line that intersects the ellipse: + // x^2 y^2 + // --- + --- = 1, where a and b are semi-major axes 2 and 1 respectively (ie. the limits) + // a^2 b^2 + // Do the math and it should be clear. + + float swingLimit = m_swingSpan1; // if xEllipse == 0, just use axis b (1) + if (fabs(xEllipse) > SIMD_EPSILON) + { + btScalar surfaceSlope2 = (yEllipse*yEllipse)/(xEllipse*xEllipse); + btScalar norm = 1 / (m_swingSpan2 * m_swingSpan2); + norm += surfaceSlope2 / (m_swingSpan1 * m_swingSpan1); + btScalar swingLimit2 = (1 + surfaceSlope2) / norm; + swingLimit = sqrt(swingLimit2); + } + + // convert into point in constraint space: + // note: twist is x-axis, swing 1 and 2 are along the z and y axes respectively + btVector3 vSwingAxis(0, xEllipse, -yEllipse); + btQuaternion qSwing(vSwingAxis, swingLimit); + btVector3 vPointInConstraintSpace(fLength,0,0); + return quatRotate(qSwing, vPointInConstraintSpace); +} + +// given a twist rotation in constraint space, (pre: cone must already be removed) +// this method computes its corresponding angle and axis. +void btConeTwistConstraint::computeTwistLimitInfo(const btQuaternion& qTwist, + btScalar& twistAngle, // out + btVector3& vTwistAxis) // out +{ + btQuaternion qMinTwist = qTwist; + twistAngle = qTwist.getAngle(); + + if (twistAngle > SIMD_PI) // long way around. flip quat and recalculate. + { + qMinTwist = operator-(qTwist); + twistAngle = qMinTwist.getAngle(); + } + if (twistAngle < 0) + { + // this should never happen + int wtf = 0; wtf = wtf; + } + + vTwistAxis = btVector3(qMinTwist.x(), qMinTwist.y(), qMinTwist.z()); + if (twistAngle > SIMD_EPSILON) + vTwistAxis.normalize(); +} + + +void btConeTwistConstraint::adjustSwingAxisToUseEllipseNormal(btVector3& vSwingAxis) const +{ + // the swing axis is computed as the "twist-free" cone rotation, + // but the cone limit is not circular, but elliptical (if swingspan1 != swingspan2). + // so, if we're outside the limits, the closest way back inside the cone isn't + // along the vector back to the center. better (and more stable) to use the ellipse normal. + + // convert swing axis to direction from center to surface of ellipse + // (ie. rotate 2D vector by PI/2) + btScalar y = -vSwingAxis.z(); + btScalar z = vSwingAxis.y(); + + // do the math... + if (fabs(z) > SIMD_EPSILON) // avoid division by 0. and we don't need an update if z == 0. + { + // compute gradient/normal of ellipse surface at current "point" + btScalar grad = y/z; + grad *= m_swingSpan2 / m_swingSpan1; + + // adjust y/z to represent normal at point (instead of vector to point) + if (y > 0) + y = fabs(grad * z); + else + y = -fabs(grad * z); + + // convert ellipse direction back to swing axis + vSwingAxis.setZ(-y); + vSwingAxis.setY( z); + vSwingAxis.normalize(); + } +} + + + +void btConeTwistConstraint::setMotorTarget(const btQuaternion &q) +{ + btTransform trACur = m_rbA.getCenterOfMassTransform(); + btTransform trBCur = m_rbB.getCenterOfMassTransform(); + btTransform trABCur = trBCur.inverse() * trACur; + btQuaternion qABCur = trABCur.getRotation(); + btTransform trConstraintCur = (trBCur * m_rbBFrame).inverse() * (trACur * m_rbAFrame); + btQuaternion qConstraintCur = trConstraintCur.getRotation(); + + btQuaternion qConstraint = m_rbBFrame.getRotation().inverse() * q * m_rbAFrame.getRotation(); + setMotorTargetInConstraintSpace(qConstraint); +} + + +void btConeTwistConstraint::setMotorTargetInConstraintSpace(const btQuaternion &q) +{ + m_qTarget = q; + + // clamp motor target to within limits + { + btScalar softness = 1.f;//m_limitSoftness; + + // split into twist and cone + btVector3 vTwisted = quatRotate(m_qTarget, vTwist); + btQuaternion qTargetCone = shortestArcQuat(vTwist, vTwisted); qTargetCone.normalize(); + btQuaternion qTargetTwist = qTargetCone.inverse() * m_qTarget; qTargetTwist.normalize(); + + // clamp cone + if (m_swingSpan1 >= btScalar(0.05f) && m_swingSpan2 >= btScalar(0.05f)) + { + btScalar swingAngle, swingLimit; btVector3 swingAxis; + computeConeLimitInfo(qTargetCone, swingAngle, swingAxis, swingLimit); + + if (fabs(swingAngle) > SIMD_EPSILON) + { + if (swingAngle > swingLimit*softness) + swingAngle = swingLimit*softness; + else if (swingAngle < -swingLimit*softness) + swingAngle = -swingLimit*softness; + qTargetCone = btQuaternion(swingAxis, swingAngle); + } + } + + // clamp twist + if (m_twistSpan >= btScalar(0.05f)) + { + btScalar twistAngle; btVector3 twistAxis; + computeTwistLimitInfo(qTargetTwist, twistAngle, twistAxis); + + if (fabs(twistAngle) > SIMD_EPSILON) + { + // eddy todo: limitSoftness used here??? + if (twistAngle > m_twistSpan*softness) + twistAngle = m_twistSpan*softness; + else if (twistAngle < -m_twistSpan*softness) + twistAngle = -m_twistSpan*softness; + qTargetTwist = btQuaternion(twistAxis, twistAngle); + } + } + + m_qTarget = qTargetCone * qTargetTwist; + } +} + + + + diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btConeTwistConstraint.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btConeTwistConstraint.h new file mode 100644 index 000000000..db1e061db --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btConeTwistConstraint.h @@ -0,0 +1,259 @@ +/* +Bullet Continuous Collision Detection and Physics Library +btConeTwistConstraint is Copyright (c) 2007 Starbreeze Studios + +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. + +Written by: Marcus Hennix +*/ + + + +/* +Overview: + +btConeTwistConstraint can be used to simulate ragdoll joints (upper arm, leg etc). +It is a fixed translation, 3 degree-of-freedom (DOF) rotational "joint". +It divides the 3 rotational DOFs into swing (movement within a cone) and twist. +Swing is divided into swing1 and swing2 which can have different limits, giving an elliptical shape. +(Note: the cone's base isn't flat, so this ellipse is "embedded" on the surface of a sphere.) + +In the contraint's frame of reference: +twist is along the x-axis, +and swing 1 and 2 are along the z and y axes respectively. +*/ + + + +#ifndef CONETWISTCONSTRAINT_H +#define CONETWISTCONSTRAINT_H + +#include "LinearMath/btVector3.h" +#include "btJacobianEntry.h" +#include "btTypedConstraint.h" + +class btRigidBody; + + +///btConeTwistConstraint can be used to simulate ragdoll joints (upper arm, leg etc) +class btConeTwistConstraint : public btTypedConstraint +{ +#ifdef IN_PARALLELL_SOLVER +public: +#endif + btJacobianEntry m_jac[3]; //3 orthogonal linear constraints + + btTransform m_rbAFrame; + btTransform m_rbBFrame; + + btScalar m_limitSoftness; + btScalar m_biasFactor; + btScalar m_relaxationFactor; + + btScalar m_damping; + + btScalar m_swingSpan1; + btScalar m_swingSpan2; + btScalar m_twistSpan; + + btScalar m_fixThresh; + + btVector3 m_swingAxis; + btVector3 m_twistAxis; + + btScalar m_kSwing; + btScalar m_kTwist; + + btScalar m_twistLimitSign; + btScalar m_swingCorrection; + btScalar m_twistCorrection; + + btScalar m_twistAngle; + + btScalar m_accSwingLimitImpulse; + btScalar m_accTwistLimitImpulse; + + bool m_angularOnly; + bool m_solveTwistLimit; + bool m_solveSwingLimit; + + bool m_useSolveConstraintObsolete; + + // not yet used... + btScalar m_swingLimitRatio; + btScalar m_twistLimitRatio; + btVector3 m_twistAxisA; + + // motor + bool m_bMotorEnabled; + bool m_bNormalizedMotorStrength; + btQuaternion m_qTarget; + btScalar m_maxMotorImpulse; + btVector3 m_accMotorImpulse; + +public: + + btConeTwistConstraint(btRigidBody& rbA,btRigidBody& rbB,const btTransform& rbAFrame, const btTransform& rbBFrame); + + btConeTwistConstraint(btRigidBody& rbA,const btTransform& rbAFrame); + + btConeTwistConstraint(); + + virtual void buildJacobian(); + + virtual void getInfo1 (btConstraintInfo1* info); + + void getInfo1NonVirtual(btConstraintInfo1* info); + + virtual void getInfo2 (btConstraintInfo2* info); + + void getInfo2NonVirtual(btConstraintInfo2* info,const btTransform& transA,const btTransform& transB,const btMatrix3x3& invInertiaWorldA,const btMatrix3x3& invInertiaWorldB); + + virtual void solveConstraintObsolete(btSolverBody& bodyA,btSolverBody& bodyB,btScalar timeStep); + + void updateRHS(btScalar timeStep); + + const btRigidBody& getRigidBodyA() const + { + return m_rbA; + } + const btRigidBody& getRigidBodyB() const + { + return m_rbB; + } + + void setAngularOnly(bool angularOnly) + { + m_angularOnly = angularOnly; + } + + void setLimit(int limitIndex,btScalar limitValue) + { + switch (limitIndex) + { + case 3: + { + m_twistSpan = limitValue; + break; + } + case 4: + { + m_swingSpan2 = limitValue; + break; + } + case 5: + { + m_swingSpan1 = limitValue; + break; + } + default: + { + } + }; + } + + // setLimit(), a few notes: + // _softness: + // 0->1, recommend ~0.8->1. + // describes % of limits where movement is free. + // beyond this softness %, the limit is gradually enforced until the "hard" (1.0) limit is reached. + // _biasFactor: + // 0->1?, recommend 0.3 +/-0.3 or so. + // strength with which constraint resists zeroth order (angular, not angular velocity) limit violation. + // __relaxationFactor: + // 0->1, recommend to stay near 1. + // the lower the value, the less the constraint will fight velocities which violate the angular limits. + void setLimit(btScalar _swingSpan1,btScalar _swingSpan2,btScalar _twistSpan, btScalar _softness = 1.f, btScalar _biasFactor = 0.3f, btScalar _relaxationFactor = 1.0f) + { + m_swingSpan1 = _swingSpan1; + m_swingSpan2 = _swingSpan2; + m_twistSpan = _twistSpan; + + m_limitSoftness = _softness; + m_biasFactor = _biasFactor; + m_relaxationFactor = _relaxationFactor; + } + + const btTransform& getAFrame() { return m_rbAFrame; }; + const btTransform& getBFrame() { return m_rbBFrame; }; + + inline int getSolveTwistLimit() + { + return m_solveTwistLimit; + } + + inline int getSolveSwingLimit() + { + return m_solveTwistLimit; + } + + inline btScalar getTwistLimitSign() + { + return m_twistLimitSign; + } + + void calcAngleInfo(); + void calcAngleInfo2(const btTransform& transA, const btTransform& transB,const btMatrix3x3& invInertiaWorldA,const btMatrix3x3& invInertiaWorldB); + + inline btScalar getSwingSpan1() + { + return m_swingSpan1; + } + inline btScalar getSwingSpan2() + { + return m_swingSpan2; + } + inline btScalar getTwistSpan() + { + return m_twistSpan; + } + inline btScalar getTwistAngle() + { + return m_twistAngle; + } + bool isPastSwingLimit() { return m_solveSwingLimit; } + + + void setDamping(btScalar damping) { m_damping = damping; } + + void enableMotor(bool b) { m_bMotorEnabled = b; } + void setMaxMotorImpulse(btScalar maxMotorImpulse) { m_maxMotorImpulse = maxMotorImpulse; m_bNormalizedMotorStrength = false; } + void setMaxMotorImpulseNormalized(btScalar maxMotorImpulse) { m_maxMotorImpulse = maxMotorImpulse; m_bNormalizedMotorStrength = true; } + + btScalar getFixThresh() { return m_fixThresh; } + void setFixThresh(btScalar fixThresh) { m_fixThresh = fixThresh; } + + // setMotorTarget: + // q: the desired rotation of bodyA wrt bodyB. + // note: if q violates the joint limits, the internal target is clamped to avoid conflicting impulses (very bad for stability) + // note: don't forget to enableMotor() + void setMotorTarget(const btQuaternion &q); + + // same as above, but q is the desired rotation of frameA wrt frameB in constraint space + void setMotorTargetInConstraintSpace(const btQuaternion &q); + + btVector3 GetPointForAngle(btScalar fAngleInRadians, btScalar fLength) const; + + + +protected: + void init(); + + void computeConeLimitInfo(const btQuaternion& qCone, // in + btScalar& swingAngle, btVector3& vSwingAxis, btScalar& swingLimit); // all outs + + void computeTwistLimitInfo(const btQuaternion& qTwist, // in + btScalar& twistAngle, btVector3& vTwistAxis); // all outs + + void adjustSwingAxisToUseEllipseNormal(btVector3& vSwingAxis) const; +}; + +#endif //CONETWISTCONSTRAINT_H diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btConstraintSolver.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btConstraintSolver.h new file mode 100644 index 000000000..7a8e9c195 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btConstraintSolver.h @@ -0,0 +1,52 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 CONSTRAINT_SOLVER_H +#define CONSTRAINT_SOLVER_H + +#include "LinearMath/btScalar.h" + +class btPersistentManifold; +class btRigidBody; +class btCollisionObject; +class btTypedConstraint; +struct btContactSolverInfo; +struct btBroadphaseProxy; +class btIDebugDraw; +class btStackAlloc; +class btDispatcher; +/// btConstraintSolver provides solver interface +class btConstraintSolver +{ + +public: + + virtual ~btConstraintSolver() {} + + virtual void prepareSolve (int /* numBodies */, int /* numManifolds */) {;} + + ///solve a group of constraints + virtual btScalar solveGroup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifold,int numManifolds,btTypedConstraint** constraints,int numConstraints, const btContactSolverInfo& info,class btIDebugDraw* debugDrawer, btStackAlloc* stackAlloc,btDispatcher* dispatcher) = 0; + + virtual void allSolved (const btContactSolverInfo& /* info */,class btIDebugDraw* /* debugDrawer */, btStackAlloc* /* stackAlloc */) {;} + + ///clear internal cached data and reset random seed + virtual void reset() = 0; +}; + + + + +#endif //CONSTRAINT_SOLVER_H diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btContactConstraint.cpp b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btContactConstraint.cpp new file mode 100644 index 000000000..72116c6ba --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btContactConstraint.cpp @@ -0,0 +1,141 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btContactConstraint.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include "LinearMath/btVector3.h" +#include "btJacobianEntry.h" +#include "btContactSolverInfo.h" +#include "LinearMath/btMinMax.h" +#include "BulletCollision/NarrowPhaseCollision/btManifoldPoint.h" + +btContactConstraint::btContactConstraint() +:btTypedConstraint(CONTACT_CONSTRAINT_TYPE) +{ +} + +btContactConstraint::btContactConstraint(btPersistentManifold* contactManifold,btRigidBody& rbA,btRigidBody& rbB) +:btTypedConstraint(CONTACT_CONSTRAINT_TYPE,rbA,rbB), + m_contactManifold(*contactManifold) +{ + +} + +btContactConstraint::~btContactConstraint() +{ + +} + +void btContactConstraint::setContactManifold(btPersistentManifold* contactManifold) +{ + m_contactManifold = *contactManifold; +} + +void btContactConstraint::getInfo1 (btConstraintInfo1* info) +{ + +} + +void btContactConstraint::getInfo2 (btConstraintInfo2* info) +{ + +} + +void btContactConstraint::buildJacobian() +{ + +} + +void btContactConstraint::solveConstraintObsolete(btSolverBody& bodyA,btSolverBody& bodyB,btScalar timeStep) +{ + +} + + + + +#include "btContactConstraint.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include "LinearMath/btVector3.h" +#include "btJacobianEntry.h" +#include "btContactSolverInfo.h" +#include "LinearMath/btMinMax.h" +#include "BulletCollision/NarrowPhaseCollision/btManifoldPoint.h" + +#define ASSERT2 btAssert + +#define USE_INTERNAL_APPLY_IMPULSE 1 + + +//bilateral constraint between two dynamic objects +void resolveSingleBilateral(btRigidBody& body1, const btVector3& pos1, + btRigidBody& body2, const btVector3& pos2, + btScalar distance, const btVector3& normal,btScalar& impulse ,btScalar timeStep) +{ + (void)timeStep; + (void)distance; + + + btScalar normalLenSqr = normal.length2(); + ASSERT2(btFabs(normalLenSqr) < btScalar(1.1)); + if (normalLenSqr > btScalar(1.1)) + { + impulse = btScalar(0.); + return; + } + btVector3 rel_pos1 = pos1 - body1.getCenterOfMassPosition(); + btVector3 rel_pos2 = pos2 - body2.getCenterOfMassPosition(); + //this jacobian entry could be re-used for all iterations + + btVector3 vel1 = body1.getVelocityInLocalPoint(rel_pos1); + btVector3 vel2 = body2.getVelocityInLocalPoint(rel_pos2); + btVector3 vel = vel1 - vel2; + + + btJacobianEntry jac(body1.getCenterOfMassTransform().getBasis().transpose(), + body2.getCenterOfMassTransform().getBasis().transpose(), + rel_pos1,rel_pos2,normal,body1.getInvInertiaDiagLocal(),body1.getInvMass(), + body2.getInvInertiaDiagLocal(),body2.getInvMass()); + + btScalar jacDiagAB = jac.getDiagonal(); + btScalar jacDiagABInv = btScalar(1.) / jacDiagAB; + + btScalar rel_vel = jac.getRelativeVelocity( + body1.getLinearVelocity(), + body1.getCenterOfMassTransform().getBasis().transpose() * body1.getAngularVelocity(), + body2.getLinearVelocity(), + body2.getCenterOfMassTransform().getBasis().transpose() * body2.getAngularVelocity()); + btScalar a; + a=jacDiagABInv; + + + rel_vel = normal.dot(vel); + + //todo: move this into proper structure + btScalar contactDamping = btScalar(0.2); + +#ifdef ONLY_USE_LINEAR_MASS + btScalar massTerm = btScalar(1.) / (body1.getInvMass() + body2.getInvMass()); + impulse = - contactDamping * rel_vel * massTerm; +#else + btScalar velocityImpulse = -contactDamping * rel_vel * jacDiagABInv; + impulse = velocityImpulse; +#endif +} + + + + diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btContactConstraint.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btContactConstraint.h new file mode 100644 index 000000000..481b89e54 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btContactConstraint.h @@ -0,0 +1,71 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 CONTACT_CONSTRAINT_H +#define CONTACT_CONSTRAINT_H + +#include "LinearMath/btVector3.h" +#include "btJacobianEntry.h" +#include "btTypedConstraint.h" +#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" + +///btContactConstraint can be automatically created to solve contact constraints using the unified btTypedConstraint interface +ATTRIBUTE_ALIGNED16(class) btContactConstraint : public btTypedConstraint +{ +protected: + + btPersistentManifold m_contactManifold; + +public: + + btContactConstraint(); + + btContactConstraint(btPersistentManifold* contactManifold,btRigidBody& rbA,btRigidBody& rbB); + + void setContactManifold(btPersistentManifold* contactManifold); + + btPersistentManifold* getContactManifold() + { + return &m_contactManifold; + } + + const btPersistentManifold* getContactManifold() const + { + return &m_contactManifold; + } + + virtual ~btContactConstraint(); + + virtual void getInfo1 (btConstraintInfo1* info); + + virtual void getInfo2 (btConstraintInfo2* info); + + ///obsolete methods + virtual void buildJacobian(); + + ///obsolete methods + virtual void solveConstraintObsolete(btSolverBody& bodyA,btSolverBody& bodyB,btScalar timeStep); + +}; + + +///resolveSingleBilateral is an obsolete methods used for vehicle friction between two dynamic objects +void resolveSingleBilateral(btRigidBody& body1, const btVector3& pos1, + btRigidBody& body2, const btVector3& pos2, + btScalar distance, const btVector3& normal,btScalar& impulse ,btScalar timeStep); + + + +#endif //CONTACT_CONSTRAINT_H diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btContactSolverInfo.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btContactSolverInfo.h new file mode 100644 index 000000000..3293c8de9 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btContactSolverInfo.h @@ -0,0 +1,85 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 CONTACT_SOLVER_INFO +#define CONTACT_SOLVER_INFO + +enum btSolverMode +{ + SOLVER_RANDMIZE_ORDER = 1, + SOLVER_FRICTION_SEPARATE = 2, + SOLVER_USE_WARMSTARTING = 4, + SOLVER_USE_FRICTION_WARMSTARTING = 8, + SOLVER_USE_2_FRICTION_DIRECTIONS = 16, + SOLVER_ENABLE_FRICTION_DIRECTION_CACHING = 32, + SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION = 64, + SOLVER_CACHE_FRIENDLY = 128, + SOLVER_SIMD = 256, //enabled for Windows, the solver innerloop is branchless SIMD, 40% faster than FPU/scalar version + SOLVER_CUDA = 512 //will be open sourced during Game Developers Conference 2009. Much faster. +}; + +struct btContactSolverInfoData +{ + + + btScalar m_tau; + btScalar m_damping; + btScalar m_friction; + btScalar m_timeStep; + btScalar m_restitution; + int m_numIterations; + btScalar m_maxErrorReduction; + btScalar m_sor; + btScalar m_erp;//used as Baumgarte factor + btScalar m_erp2;//used in Split Impulse + btScalar m_globalCfm;//constraint force mixing + int m_splitImpulse; + btScalar m_splitImpulsePenetrationThreshold; + btScalar m_linearSlop; + btScalar m_warmstartingFactor; + + int m_solverMode; + int m_restingContactRestitutionThreshold; + + +}; + +struct btContactSolverInfo : public btContactSolverInfoData +{ + + + + inline btContactSolverInfo() + { + m_tau = btScalar(0.6); + m_damping = btScalar(1.0); + m_friction = btScalar(0.3); + m_restitution = btScalar(0.); + m_maxErrorReduction = btScalar(20.); + m_numIterations = 10; + m_erp = btScalar(0.2); + m_erp2 = btScalar(0.1); + m_globalCfm = btScalar(0.); + m_sor = btScalar(1.); + m_splitImpulse = false; + m_splitImpulsePenetrationThreshold = -0.02f; + m_linearSlop = btScalar(0.0); + m_warmstartingFactor=btScalar(0.85); + m_solverMode = SOLVER_USE_WARMSTARTING | SOLVER_SIMD;// | SOLVER_RANDMIZE_ORDER; + m_restingContactRestitutionThreshold = 2;//resting contact lifetime threshold to disable restitution + } +}; + +#endif //CONTACT_SOLVER_INFO diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.cpp b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.cpp new file mode 100644 index 000000000..b24f35615 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.cpp @@ -0,0 +1,893 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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. +*/ +/* +2007-09-09 +Refactored by Francisco Le?n +email: projectileman@yahoo.com +http://gimpact.sf.net +*/ + +#include "btGeneric6DofConstraint.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include "LinearMath/btTransformUtil.h" +#include "LinearMath/btTransformUtil.h" +#include + + + +#define D6_USE_OBSOLETE_METHOD false + + +btGeneric6DofConstraint::btGeneric6DofConstraint() +:btTypedConstraint(D6_CONSTRAINT_TYPE), +m_useLinearReferenceFrameA(true), +m_useSolveConstraintObsolete(D6_USE_OBSOLETE_METHOD) +{ +} + + + +btGeneric6DofConstraint::btGeneric6DofConstraint(btRigidBody& rbA, btRigidBody& rbB, const btTransform& frameInA, const btTransform& frameInB, bool useLinearReferenceFrameA) +: btTypedConstraint(D6_CONSTRAINT_TYPE, rbA, rbB) +, m_frameInA(frameInA) +, m_frameInB(frameInB), +m_useLinearReferenceFrameA(useLinearReferenceFrameA), +m_useSolveConstraintObsolete(D6_USE_OBSOLETE_METHOD) +{ + +} + + + +#define GENERIC_D6_DISABLE_WARMSTARTING 1 + + + +btScalar btGetMatrixElem(const btMatrix3x3& mat, int index); +btScalar btGetMatrixElem(const btMatrix3x3& mat, int index) +{ + int i = index%3; + int j = index/3; + return mat[i][j]; +} + + + +///MatrixToEulerXYZ from http://www.geometrictools.com/LibFoundation/Mathematics/Wm4Matrix3.inl.html +bool matrixToEulerXYZ(const btMatrix3x3& mat,btVector3& xyz); +bool matrixToEulerXYZ(const btMatrix3x3& mat,btVector3& xyz) +{ + // // rot = cy*cz -cy*sz sy + // // cz*sx*sy+cx*sz cx*cz-sx*sy*sz -cy*sx + // // -cx*cz*sy+sx*sz cz*sx+cx*sy*sz cx*cy + // + + btScalar fi = btGetMatrixElem(mat,2); + if (fi < btScalar(1.0f)) + { + if (fi > btScalar(-1.0f)) + { + xyz[0] = btAtan2(-btGetMatrixElem(mat,5),btGetMatrixElem(mat,8)); + xyz[1] = btAsin(btGetMatrixElem(mat,2)); + xyz[2] = btAtan2(-btGetMatrixElem(mat,1),btGetMatrixElem(mat,0)); + return true; + } + else + { + // WARNING. Not unique. XA - ZA = -atan2(r10,r11) + xyz[0] = -btAtan2(btGetMatrixElem(mat,3),btGetMatrixElem(mat,4)); + xyz[1] = -SIMD_HALF_PI; + xyz[2] = btScalar(0.0); + return false; + } + } + else + { + // WARNING. Not unique. XAngle + ZAngle = atan2(r10,r11) + xyz[0] = btAtan2(btGetMatrixElem(mat,3),btGetMatrixElem(mat,4)); + xyz[1] = SIMD_HALF_PI; + xyz[2] = 0.0; + } + return false; +} + +//////////////////////////// btRotationalLimitMotor //////////////////////////////////// + +int btRotationalLimitMotor::testLimitValue(btScalar test_value) +{ + if(m_loLimit>m_hiLimit) + { + m_currentLimit = 0;//Free from violation + return 0; + } + if (test_value < m_loLimit) + { + m_currentLimit = 1;//low limit violation + m_currentLimitError = test_value - m_loLimit; + return 1; + } + else if (test_value> m_hiLimit) + { + m_currentLimit = 2;//High limit violation + m_currentLimitError = test_value - m_hiLimit; + return 2; + }; + + m_currentLimit = 0;//Free from violation + return 0; + +} + + + +btScalar btRotationalLimitMotor::solveAngularLimits( + btScalar timeStep,btVector3& axis,btScalar jacDiagABInv, + btRigidBody * body0, btSolverBody& bodyA, btRigidBody * body1, btSolverBody& bodyB) +{ + if (needApplyTorques()==false) return 0.0f; + + btScalar target_velocity = m_targetVelocity; + btScalar maxMotorForce = m_maxMotorForce; + + //current error correction + if (m_currentLimit!=0) + { + target_velocity = -m_ERP*m_currentLimitError/(timeStep); + maxMotorForce = m_maxLimitForce; + } + + maxMotorForce *= timeStep; + + // current velocity difference + + btVector3 angVelA; + bodyA.getAngularVelocity(angVelA); + btVector3 angVelB; + bodyB.getAngularVelocity(angVelB); + + btVector3 vel_diff; + vel_diff = angVelA-angVelB; + + + + btScalar rel_vel = axis.dot(vel_diff); + + // correction velocity + btScalar motor_relvel = m_limitSoftness*(target_velocity - m_damping*rel_vel); + + + if ( motor_relvel < SIMD_EPSILON && motor_relvel > -SIMD_EPSILON ) + { + return 0.0f;//no need for applying force + } + + + // correction impulse + btScalar unclippedMotorImpulse = (1+m_bounce)*motor_relvel*jacDiagABInv; + + // clip correction impulse + btScalar clippedMotorImpulse; + + ///@todo: should clip against accumulated impulse + if (unclippedMotorImpulse>0.0f) + { + clippedMotorImpulse = unclippedMotorImpulse > maxMotorForce? maxMotorForce: unclippedMotorImpulse; + } + else + { + clippedMotorImpulse = unclippedMotorImpulse < -maxMotorForce ? -maxMotorForce: unclippedMotorImpulse; + } + + + // sort with accumulated impulses + btScalar lo = btScalar(-BT_LARGE_FLOAT); + btScalar hi = btScalar(BT_LARGE_FLOAT); + + btScalar oldaccumImpulse = m_accumulatedImpulse; + btScalar sum = oldaccumImpulse + clippedMotorImpulse; + m_accumulatedImpulse = sum > hi ? btScalar(0.) : sum < lo ? btScalar(0.) : sum; + + clippedMotorImpulse = m_accumulatedImpulse - oldaccumImpulse; + + btVector3 motorImp = clippedMotorImpulse * axis; + + //body0->applyTorqueImpulse(motorImp); + //body1->applyTorqueImpulse(-motorImp); + + bodyA.applyImpulse(btVector3(0,0,0), body0->getInvInertiaTensorWorld()*axis,clippedMotorImpulse); + bodyB.applyImpulse(btVector3(0,0,0), body1->getInvInertiaTensorWorld()*axis,-clippedMotorImpulse); + + + return clippedMotorImpulse; + + +} + +//////////////////////////// End btRotationalLimitMotor //////////////////////////////////// + + + + +//////////////////////////// btTranslationalLimitMotor //////////////////////////////////// + + +int btTranslationalLimitMotor::testLimitValue(int limitIndex, btScalar test_value) +{ + btScalar loLimit = m_lowerLimit[limitIndex]; + btScalar hiLimit = m_upperLimit[limitIndex]; + if(loLimit > hiLimit) + { + m_currentLimit[limitIndex] = 0;//Free from violation + m_currentLimitError[limitIndex] = btScalar(0.f); + return 0; + } + + if (test_value < loLimit) + { + m_currentLimit[limitIndex] = 2;//low limit violation + m_currentLimitError[limitIndex] = test_value - loLimit; + return 2; + } + else if (test_value> hiLimit) + { + m_currentLimit[limitIndex] = 1;//High limit violation + m_currentLimitError[limitIndex] = test_value - hiLimit; + return 1; + }; + + m_currentLimit[limitIndex] = 0;//Free from violation + m_currentLimitError[limitIndex] = btScalar(0.f); + return 0; +} + + + +btScalar btTranslationalLimitMotor::solveLinearAxis( + btScalar timeStep, + btScalar jacDiagABInv, + btRigidBody& body1,btSolverBody& bodyA,const btVector3 &pointInA, + btRigidBody& body2,btSolverBody& bodyB,const btVector3 &pointInB, + int limit_index, + const btVector3 & axis_normal_on_a, + const btVector3 & anchorPos) +{ + + ///find relative velocity + // btVector3 rel_pos1 = pointInA - body1.getCenterOfMassPosition(); + // btVector3 rel_pos2 = pointInB - body2.getCenterOfMassPosition(); + btVector3 rel_pos1 = anchorPos - body1.getCenterOfMassPosition(); + btVector3 rel_pos2 = anchorPos - body2.getCenterOfMassPosition(); + + btVector3 vel1; + bodyA.getVelocityInLocalPointObsolete(rel_pos1,vel1); + btVector3 vel2; + bodyB.getVelocityInLocalPointObsolete(rel_pos2,vel2); + btVector3 vel = vel1 - vel2; + + btScalar rel_vel = axis_normal_on_a.dot(vel); + + + + /// apply displacement correction + + //positional error (zeroth order error) + btScalar depth = -(pointInA - pointInB).dot(axis_normal_on_a); + btScalar lo = btScalar(-BT_LARGE_FLOAT); + btScalar hi = btScalar(BT_LARGE_FLOAT); + + btScalar minLimit = m_lowerLimit[limit_index]; + btScalar maxLimit = m_upperLimit[limit_index]; + + //handle the limits + if (minLimit < maxLimit) + { + { + if (depth > maxLimit) + { + depth -= maxLimit; + lo = btScalar(0.); + + } + else + { + if (depth < minLimit) + { + depth -= minLimit; + hi = btScalar(0.); + } + else + { + return 0.0f; + } + } + } + } + + btScalar normalImpulse= m_limitSoftness*(m_restitution*depth/timeStep - m_damping*rel_vel) * jacDiagABInv; + + + + + btScalar oldNormalImpulse = m_accumulatedImpulse[limit_index]; + btScalar sum = oldNormalImpulse + normalImpulse; + m_accumulatedImpulse[limit_index] = sum > hi ? btScalar(0.) : sum < lo ? btScalar(0.) : sum; + normalImpulse = m_accumulatedImpulse[limit_index] - oldNormalImpulse; + + btVector3 impulse_vector = axis_normal_on_a * normalImpulse; + //body1.applyImpulse( impulse_vector, rel_pos1); + //body2.applyImpulse(-impulse_vector, rel_pos2); + + btVector3 ftorqueAxis1 = rel_pos1.cross(axis_normal_on_a); + btVector3 ftorqueAxis2 = rel_pos2.cross(axis_normal_on_a); + bodyA.applyImpulse(axis_normal_on_a*body1.getInvMass(), body1.getInvInertiaTensorWorld()*ftorqueAxis1,normalImpulse); + bodyB.applyImpulse(axis_normal_on_a*body2.getInvMass(), body2.getInvInertiaTensorWorld()*ftorqueAxis2,-normalImpulse); + + + + + return normalImpulse; +} + +//////////////////////////// btTranslationalLimitMotor //////////////////////////////////// + +void btGeneric6DofConstraint::calculateAngleInfo() +{ + btMatrix3x3 relative_frame = m_calculatedTransformA.getBasis().inverse()*m_calculatedTransformB.getBasis(); + matrixToEulerXYZ(relative_frame,m_calculatedAxisAngleDiff); + // in euler angle mode we do not actually constrain the angular velocity + // along the axes axis[0] and axis[2] (although we do use axis[1]) : + // + // to get constrain w2-w1 along ...not + // ------ --------------------- ------ + // d(angle[0])/dt = 0 ax[1] x ax[2] ax[0] + // d(angle[1])/dt = 0 ax[1] + // d(angle[2])/dt = 0 ax[0] x ax[1] ax[2] + // + // constraining w2-w1 along an axis 'a' means that a'*(w2-w1)=0. + // to prove the result for angle[0], write the expression for angle[0] from + // GetInfo1 then take the derivative. to prove this for angle[2] it is + // easier to take the euler rate expression for d(angle[2])/dt with respect + // to the components of w and set that to 0. + btVector3 axis0 = m_calculatedTransformB.getBasis().getColumn(0); + btVector3 axis2 = m_calculatedTransformA.getBasis().getColumn(2); + + m_calculatedAxis[1] = axis2.cross(axis0); + m_calculatedAxis[0] = m_calculatedAxis[1].cross(axis2); + m_calculatedAxis[2] = axis0.cross(m_calculatedAxis[1]); + + m_calculatedAxis[0].normalize(); + m_calculatedAxis[1].normalize(); + m_calculatedAxis[2].normalize(); + +} + +void btGeneric6DofConstraint::calculateTransforms() +{ + calculateTransforms(m_rbA.getCenterOfMassTransform(),m_rbB.getCenterOfMassTransform()); +} + +void btGeneric6DofConstraint::calculateTransforms(const btTransform& transA,const btTransform& transB) +{ + m_calculatedTransformA = transA * m_frameInA; + m_calculatedTransformB = transB * m_frameInB; + calculateLinearInfo(); + calculateAngleInfo(); +} + + + +void btGeneric6DofConstraint::buildLinearJacobian( + btJacobianEntry & jacLinear,const btVector3 & normalWorld, + const btVector3 & pivotAInW,const btVector3 & pivotBInW) +{ + new (&jacLinear) btJacobianEntry( + m_rbA.getCenterOfMassTransform().getBasis().transpose(), + m_rbB.getCenterOfMassTransform().getBasis().transpose(), + pivotAInW - m_rbA.getCenterOfMassPosition(), + pivotBInW - m_rbB.getCenterOfMassPosition(), + normalWorld, + m_rbA.getInvInertiaDiagLocal(), + m_rbA.getInvMass(), + m_rbB.getInvInertiaDiagLocal(), + m_rbB.getInvMass()); +} + + + +void btGeneric6DofConstraint::buildAngularJacobian( + btJacobianEntry & jacAngular,const btVector3 & jointAxisW) +{ + new (&jacAngular) btJacobianEntry(jointAxisW, + m_rbA.getCenterOfMassTransform().getBasis().transpose(), + m_rbB.getCenterOfMassTransform().getBasis().transpose(), + m_rbA.getInvInertiaDiagLocal(), + m_rbB.getInvInertiaDiagLocal()); + +} + + + +bool btGeneric6DofConstraint::testAngularLimitMotor(int axis_index) +{ + btScalar angle = m_calculatedAxisAngleDiff[axis_index]; + angle = btAdjustAngleToLimits(angle, m_angularLimits[axis_index].m_loLimit, m_angularLimits[axis_index].m_hiLimit); + m_angularLimits[axis_index].m_currentPosition = angle; + //test limits + m_angularLimits[axis_index].testLimitValue(angle); + return m_angularLimits[axis_index].needApplyTorques(); +} + + + +void btGeneric6DofConstraint::buildJacobian() +{ +#ifndef __SPU__ + if (m_useSolveConstraintObsolete) + { + + // Clear accumulated impulses for the next simulation step + m_linearLimits.m_accumulatedImpulse.setValue(btScalar(0.), btScalar(0.), btScalar(0.)); + int i; + for(i = 0; i < 3; i++) + { + m_angularLimits[i].m_accumulatedImpulse = btScalar(0.); + } + //calculates transform + calculateTransforms(m_rbA.getCenterOfMassTransform(),m_rbB.getCenterOfMassTransform()); + + // const btVector3& pivotAInW = m_calculatedTransformA.getOrigin(); + // const btVector3& pivotBInW = m_calculatedTransformB.getOrigin(); + calcAnchorPos(); + btVector3 pivotAInW = m_AnchorPos; + btVector3 pivotBInW = m_AnchorPos; + + // not used here + // btVector3 rel_pos1 = pivotAInW - m_rbA.getCenterOfMassPosition(); + // btVector3 rel_pos2 = pivotBInW - m_rbB.getCenterOfMassPosition(); + + btVector3 normalWorld; + //linear part + for (i=0;i<3;i++) + { + if (m_linearLimits.isLimited(i)) + { + if (m_useLinearReferenceFrameA) + normalWorld = m_calculatedTransformA.getBasis().getColumn(i); + else + normalWorld = m_calculatedTransformB.getBasis().getColumn(i); + + buildLinearJacobian( + m_jacLinear[i],normalWorld , + pivotAInW,pivotBInW); + + } + } + + // angular part + for (i=0;i<3;i++) + { + //calculates error angle + if (testAngularLimitMotor(i)) + { + normalWorld = this->getAxis(i); + // Create angular atom + buildAngularJacobian(m_jacAng[i],normalWorld); + } + } + + } +#endif //__SPU__ + +} + + +void btGeneric6DofConstraint::getInfo1 (btConstraintInfo1* info) +{ + if (m_useSolveConstraintObsolete) + { + info->m_numConstraintRows = 0; + info->nub = 0; + } else + { + //prepare constraint + calculateTransforms(m_rbA.getCenterOfMassTransform(),m_rbB.getCenterOfMassTransform()); + info->m_numConstraintRows = 0; + info->nub = 6; + int i; + //test linear limits + for(i = 0; i < 3; i++) + { + if(m_linearLimits.needApplyForce(i)) + { + info->m_numConstraintRows++; + info->nub--; + } + } + //test angular limits + for (i=0;i<3 ;i++ ) + { + if(testAngularLimitMotor(i)) + { + info->m_numConstraintRows++; + info->nub--; + } + } + } +} + +void btGeneric6DofConstraint::getInfo1NonVirtual (btConstraintInfo1* info) +{ + if (m_useSolveConstraintObsolete) + { + info->m_numConstraintRows = 0; + info->nub = 0; + } else + { + //pre-allocate all 6 + info->m_numConstraintRows = 6; + info->nub = 0; + } +} + + +void btGeneric6DofConstraint::getInfo2 (btConstraintInfo2* info) +{ + getInfo2NonVirtual(info,m_rbA.getCenterOfMassTransform(),m_rbB.getCenterOfMassTransform(), m_rbA.getLinearVelocity(),m_rbB.getLinearVelocity(),m_rbA.getAngularVelocity(), m_rbB.getAngularVelocity()); +} + +void btGeneric6DofConstraint::getInfo2NonVirtual (btConstraintInfo2* info, const btTransform& transA,const btTransform& transB,const btVector3& linVelA,const btVector3& linVelB,const btVector3& angVelA,const btVector3& angVelB) +{ + btAssert(!m_useSolveConstraintObsolete); + + //prepare constraint + calculateTransforms(transA,transB); + + int i; + //test linear limits + for(i = 0; i < 3; i++) + { + if(m_linearLimits.needApplyForce(i)) + { + + } + } + //test angular limits + for (i=0;i<3 ;i++ ) + { + if(testAngularLimitMotor(i)) + { + + } + } + + int row = setLinearLimits(info,transA,transB,linVelA,linVelB,angVelA,angVelB); + setAngularLimits(info, row,transA,transB,linVelA,linVelB,angVelA,angVelB); +} + + + +int btGeneric6DofConstraint::setLinearLimits(btConstraintInfo2* info,const btTransform& transA,const btTransform& transB,const btVector3& linVelA,const btVector3& linVelB,const btVector3& angVelA,const btVector3& angVelB) +{ + int row = 0; + //solve linear limits + btRotationalLimitMotor limot; + for (int i=0;i<3 ;i++ ) + { + if(m_linearLimits.needApplyForce(i)) + { // re-use rotational motor code + limot.m_bounce = btScalar(0.f); + limot.m_currentLimit = m_linearLimits.m_currentLimit[i]; + limot.m_currentPosition = m_linearLimits.m_currentLinearDiff[i]; + limot.m_currentLimitError = m_linearLimits.m_currentLimitError[i]; + limot.m_damping = m_linearLimits.m_damping; + limot.m_enableMotor = m_linearLimits.m_enableMotor[i]; + limot.m_ERP = m_linearLimits.m_restitution; + limot.m_hiLimit = m_linearLimits.m_upperLimit[i]; + limot.m_limitSoftness = m_linearLimits.m_limitSoftness; + limot.m_loLimit = m_linearLimits.m_lowerLimit[i]; + limot.m_maxLimitForce = btScalar(0.f); + limot.m_maxMotorForce = m_linearLimits.m_maxMotorForce[i]; + limot.m_targetVelocity = m_linearLimits.m_targetVelocity[i]; + btVector3 axis = m_calculatedTransformA.getBasis().getColumn(i); + row += get_limit_motor_info2(&limot, + transA,transB,linVelA,linVelB,angVelA,angVelB + , info, row, axis, 0); + } + } + return row; +} + + + +int btGeneric6DofConstraint::setAngularLimits(btConstraintInfo2 *info, int row_offset, const btTransform& transA,const btTransform& transB,const btVector3& linVelA,const btVector3& linVelB,const btVector3& angVelA,const btVector3& angVelB) +{ + btGeneric6DofConstraint * d6constraint = this; + int row = row_offset; + //solve angular limits + for (int i=0;i<3 ;i++ ) + { + if(d6constraint->getRotationalLimitMotor(i)->needApplyTorques()) + { + btVector3 axis = d6constraint->getAxis(i); + row += get_limit_motor_info2( + d6constraint->getRotationalLimitMotor(i), + transA,transB,linVelA,linVelB,angVelA,angVelB, + info,row,axis,1); + } + } + + return row; +} + + + +void btGeneric6DofConstraint::solveConstraintObsolete(btSolverBody& bodyA,btSolverBody& bodyB,btScalar timeStep) +{ + if (m_useSolveConstraintObsolete) + { + + + m_timeStep = timeStep; + + //calculateTransforms(); + + int i; + + // linear + + btVector3 pointInA = m_calculatedTransformA.getOrigin(); + btVector3 pointInB = m_calculatedTransformB.getOrigin(); + + btScalar jacDiagABInv; + btVector3 linear_axis; + for (i=0;i<3;i++) + { + if (m_linearLimits.isLimited(i)) + { + jacDiagABInv = btScalar(1.) / m_jacLinear[i].getDiagonal(); + + if (m_useLinearReferenceFrameA) + linear_axis = m_calculatedTransformA.getBasis().getColumn(i); + else + linear_axis = m_calculatedTransformB.getBasis().getColumn(i); + + m_linearLimits.solveLinearAxis( + m_timeStep, + jacDiagABInv, + m_rbA,bodyA,pointInA, + m_rbB,bodyB,pointInB, + i,linear_axis, m_AnchorPos); + + } + } + + // angular + btVector3 angular_axis; + btScalar angularJacDiagABInv; + for (i=0;i<3;i++) + { + if (m_angularLimits[i].needApplyTorques()) + { + + // get axis + angular_axis = getAxis(i); + + angularJacDiagABInv = btScalar(1.) / m_jacAng[i].getDiagonal(); + + m_angularLimits[i].solveAngularLimits(m_timeStep,angular_axis,angularJacDiagABInv, &m_rbA,bodyA,&m_rbB,bodyB); + } + } + } +} + + + +void btGeneric6DofConstraint::updateRHS(btScalar timeStep) +{ + (void)timeStep; + +} + + + +btVector3 btGeneric6DofConstraint::getAxis(int axis_index) const +{ + return m_calculatedAxis[axis_index]; +} + + +btScalar btGeneric6DofConstraint::getRelativePivotPosition(int axisIndex) const +{ + return m_calculatedLinearDiff[axisIndex]; +} + + +btScalar btGeneric6DofConstraint::getAngle(int axisIndex) const +{ + return m_calculatedAxisAngleDiff[axisIndex]; +} + + + +void btGeneric6DofConstraint::calcAnchorPos(void) +{ + btScalar imA = m_rbA.getInvMass(); + btScalar imB = m_rbB.getInvMass(); + btScalar weight; + if(imB == btScalar(0.0)) + { + weight = btScalar(1.0); + } + else + { + weight = imA / (imA + imB); + } + const btVector3& pA = m_calculatedTransformA.getOrigin(); + const btVector3& pB = m_calculatedTransformB.getOrigin(); + m_AnchorPos = pA * weight + pB * (btScalar(1.0) - weight); + return; +} + + + +void btGeneric6DofConstraint::calculateLinearInfo() +{ + m_calculatedLinearDiff = m_calculatedTransformB.getOrigin() - m_calculatedTransformA.getOrigin(); + m_calculatedLinearDiff = m_calculatedTransformA.getBasis().inverse() * m_calculatedLinearDiff; + for(int i = 0; i < 3; i++) + { + m_linearLimits.m_currentLinearDiff[i] = m_calculatedLinearDiff[i]; + m_linearLimits.testLimitValue(i, m_calculatedLinearDiff[i]); + } +} + + + +int btGeneric6DofConstraint::get_limit_motor_info2( + btRotationalLimitMotor * limot, + const btTransform& transA,const btTransform& transB,const btVector3& linVelA,const btVector3& linVelB,const btVector3& angVelA,const btVector3& angVelB, + btConstraintInfo2 *info, int row, btVector3& ax1, int rotational) +{ + int srow = row * info->rowskip; + int powered = limot->m_enableMotor; + int limit = limot->m_currentLimit; + if (powered || limit) + { // if the joint is powered, or has joint limits, add in the extra row + btScalar *J1 = rotational ? info->m_J1angularAxis : info->m_J1linearAxis; + btScalar *J2 = rotational ? info->m_J2angularAxis : 0; + J1[srow+0] = ax1[0]; + J1[srow+1] = ax1[1]; + J1[srow+2] = ax1[2]; + if(rotational) + { + J2[srow+0] = -ax1[0]; + J2[srow+1] = -ax1[1]; + J2[srow+2] = -ax1[2]; + } + if((!rotational)) + { + btVector3 ltd; // Linear Torque Decoupling vector + btVector3 c = m_calculatedTransformB.getOrigin() - transA.getOrigin(); + ltd = c.cross(ax1); + info->m_J1angularAxis[srow+0] = ltd[0]; + info->m_J1angularAxis[srow+1] = ltd[1]; + info->m_J1angularAxis[srow+2] = ltd[2]; + + c = m_calculatedTransformB.getOrigin() - transB.getOrigin(); + ltd = -c.cross(ax1); + info->m_J2angularAxis[srow+0] = ltd[0]; + info->m_J2angularAxis[srow+1] = ltd[1]; + info->m_J2angularAxis[srow+2] = ltd[2]; + } + // if we're limited low and high simultaneously, the joint motor is + // ineffective + if (limit && (limot->m_loLimit == limot->m_hiLimit)) powered = 0; + info->m_constraintError[srow] = btScalar(0.f); + if (powered) + { + info->cfm[srow] = 0.0f; + if(!limit) + { + btScalar tag_vel = rotational ? limot->m_targetVelocity : -limot->m_targetVelocity; + + btScalar mot_fact = getMotorFactor( limot->m_currentPosition, + limot->m_loLimit, + limot->m_hiLimit, + tag_vel, + info->fps * info->erp); + info->m_constraintError[srow] += mot_fact * limot->m_targetVelocity; + info->m_lowerLimit[srow] = -limot->m_maxMotorForce; + info->m_upperLimit[srow] = limot->m_maxMotorForce; + } + } + if(limit) + { + btScalar k = info->fps * limot->m_ERP; + if(!rotational) + { + info->m_constraintError[srow] += k * limot->m_currentLimitError; + } + else + { + info->m_constraintError[srow] += -k * limot->m_currentLimitError; + } + info->cfm[srow] = 0.0f; + if (limot->m_loLimit == limot->m_hiLimit) + { // limited low and high simultaneously + info->m_lowerLimit[srow] = -SIMD_INFINITY; + info->m_upperLimit[srow] = SIMD_INFINITY; + } + else + { + if (limit == 1) + { + info->m_lowerLimit[srow] = 0; + info->m_upperLimit[srow] = SIMD_INFINITY; + } + else + { + info->m_lowerLimit[srow] = -SIMD_INFINITY; + info->m_upperLimit[srow] = 0; + } + // deal with bounce + if (limot->m_bounce > 0) + { + // calculate joint velocity + btScalar vel; + if (rotational) + { + vel = angVelA.dot(ax1); +//make sure that if no body -> angVelB == zero vec +// if (body1) + vel -= angVelB.dot(ax1); + } + else + { + vel = linVelA.dot(ax1); +//make sure that if no body -> angVelB == zero vec +// if (body1) + vel -= linVelB.dot(ax1); + } + // only apply bounce if the velocity is incoming, and if the + // resulting c[] exceeds what we already have. + if (limit == 1) + { + if (vel < 0) + { + btScalar newc = -limot->m_bounce* vel; + if (newc > info->m_constraintError[srow]) + info->m_constraintError[srow] = newc; + } + } + else + { + if (vel > 0) + { + btScalar newc = -limot->m_bounce * vel; + if (newc < info->m_constraintError[srow]) + info->m_constraintError[srow] = newc; + } + } + } + } + } + return 1; + } + else return 0; +} + + + + diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.h new file mode 100644 index 000000000..3d1936da3 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.h @@ -0,0 +1,492 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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. +*/ + +/// 2009 March: btGeneric6DofConstraint refactored by Roman Ponomarev +/// Added support for generic constraint solver through getInfo1/getInfo2 methods + +/* +2007-09-09 +btGeneric6DofConstraint Refactored by Francisco Le?n +email: projectileman@yahoo.com +http://gimpact.sf.net +*/ + + +#ifndef GENERIC_6DOF_CONSTRAINT_H +#define GENERIC_6DOF_CONSTRAINT_H + +#include "LinearMath/btVector3.h" +#include "btJacobianEntry.h" +#include "btTypedConstraint.h" + +class btRigidBody; + + + + +//! Rotation Limit structure for generic joints +class btRotationalLimitMotor +{ +public: + //! limit_parameters + //!@{ + btScalar m_loLimit;//!< joint limit + btScalar m_hiLimit;//!< joint limit + btScalar m_targetVelocity;//!< target motor velocity + btScalar m_maxMotorForce;//!< max force on motor + btScalar m_maxLimitForce;//!< max force on limit + btScalar m_damping;//!< Damping. + btScalar m_limitSoftness;//! Relaxation factor + btScalar m_ERP;//!< Error tolerance factor when joint is at limit + btScalar m_bounce;//!< restitution factor + bool m_enableMotor; + + //!@} + + //! temp_variables + //!@{ + btScalar m_currentLimitError;//! How much is violated this limit + btScalar m_currentPosition; //! current value of angle + int m_currentLimit;//!< 0=free, 1=at lo limit, 2=at hi limit + btScalar m_accumulatedImpulse; + //!@} + + btRotationalLimitMotor() + { + m_accumulatedImpulse = 0.f; + m_targetVelocity = 0; + m_maxMotorForce = 0.1f; + m_maxLimitForce = 300.0f; + m_loLimit = 1.0f; + m_hiLimit = -1.0f; + m_ERP = 0.5f; + m_bounce = 0.0f; + m_damping = 1.0f; + m_limitSoftness = 0.5f; + m_currentLimit = 0; + m_currentLimitError = 0; + m_enableMotor = false; + } + + btRotationalLimitMotor(const btRotationalLimitMotor & limot) + { + m_targetVelocity = limot.m_targetVelocity; + m_maxMotorForce = limot.m_maxMotorForce; + m_limitSoftness = limot.m_limitSoftness; + m_loLimit = limot.m_loLimit; + m_hiLimit = limot.m_hiLimit; + m_ERP = limot.m_ERP; + m_bounce = limot.m_bounce; + m_currentLimit = limot.m_currentLimit; + m_currentLimitError = limot.m_currentLimitError; + m_enableMotor = limot.m_enableMotor; + } + + + + //! Is limited + bool isLimited() + { + if(m_loLimit > m_hiLimit) return false; + return true; + } + + //! Need apply correction + bool needApplyTorques() + { + if(m_currentLimit == 0 && m_enableMotor == false) return false; + return true; + } + + //! calculates error + /*! + calculates m_currentLimit and m_currentLimitError. + */ + int testLimitValue(btScalar test_value); + + //! apply the correction impulses for two bodies + btScalar solveAngularLimits(btScalar timeStep,btVector3& axis, btScalar jacDiagABInv,btRigidBody * body0, btSolverBody& bodyA,btRigidBody * body1,btSolverBody& bodyB); + +}; + + + +class btTranslationalLimitMotor +{ +public: + btVector3 m_lowerLimit;//!< the constraint lower limits + btVector3 m_upperLimit;//!< the constraint upper limits + btVector3 m_accumulatedImpulse; + //! Linear_Limit_parameters + //!@{ + btScalar m_limitSoftness;//!< Softness for linear limit + btScalar m_damping;//!< Damping for linear limit + btScalar m_restitution;//! Bounce parameter for linear limit + //!@} + bool m_enableMotor[3]; + btVector3 m_targetVelocity;//!< target motor velocity + btVector3 m_maxMotorForce;//!< max force on motor + btVector3 m_currentLimitError;//! How much is violated this limit + btVector3 m_currentLinearDiff;//! Current relative offset of constraint frames + int m_currentLimit[3];//!< 0=free, 1=at lower limit, 2=at upper limit + + btTranslationalLimitMotor() + { + m_lowerLimit.setValue(0.f,0.f,0.f); + m_upperLimit.setValue(0.f,0.f,0.f); + m_accumulatedImpulse.setValue(0.f,0.f,0.f); + + m_limitSoftness = 0.7f; + m_damping = btScalar(1.0f); + m_restitution = btScalar(0.5f); + for(int i=0; i < 3; i++) + { + m_enableMotor[i] = false; + m_targetVelocity[i] = btScalar(0.f); + m_maxMotorForce[i] = btScalar(0.f); + } + } + + btTranslationalLimitMotor(const btTranslationalLimitMotor & other ) + { + m_lowerLimit = other.m_lowerLimit; + m_upperLimit = other.m_upperLimit; + m_accumulatedImpulse = other.m_accumulatedImpulse; + + m_limitSoftness = other.m_limitSoftness ; + m_damping = other.m_damping; + m_restitution = other.m_restitution; + for(int i=0; i < 3; i++) + { + m_enableMotor[i] = other.m_enableMotor[i]; + m_targetVelocity[i] = other.m_targetVelocity[i]; + m_maxMotorForce[i] = other.m_maxMotorForce[i]; + } + } + + //! Test limit + /*! + - free means upper < lower, + - locked means upper == lower + - limited means upper > lower + - limitIndex: first 3 are linear, next 3 are angular + */ + inline bool isLimited(int limitIndex) + { + return (m_upperLimit[limitIndex] >= m_lowerLimit[limitIndex]); + } + inline bool needApplyForce(int limitIndex) + { + if(m_currentLimit[limitIndex] == 0 && m_enableMotor[limitIndex] == false) return false; + return true; + } + int testLimitValue(int limitIndex, btScalar test_value); + + + btScalar solveLinearAxis( + btScalar timeStep, + btScalar jacDiagABInv, + btRigidBody& body1,btSolverBody& bodyA,const btVector3 &pointInA, + btRigidBody& body2,btSolverBody& bodyB,const btVector3 &pointInB, + int limit_index, + const btVector3 & axis_normal_on_a, + const btVector3 & anchorPos); + + +}; + +/// btGeneric6DofConstraint between two rigidbodies each with a pivotpoint that descibes the axis location in local space +/*! +btGeneric6DofConstraint can leave any of the 6 degree of freedom 'free' or 'locked'. +currently this limit supports rotational motors
    +
      +
    • For Linear limits, use btGeneric6DofConstraint.setLinearUpperLimit, btGeneric6DofConstraint.setLinearLowerLimit. You can set the parameters with the btTranslationalLimitMotor structure accsesible through the btGeneric6DofConstraint.getTranslationalLimitMotor method. +At this moment translational motors are not supported. May be in the future.
    • + +
    • For Angular limits, use the btRotationalLimitMotor structure for configuring the limit. +This is accessible through btGeneric6DofConstraint.getLimitMotor method, +This brings support for limit parameters and motors.
    • + +
    • Angulars limits have these possible ranges: + +AXIS + + + + + + + + + + + + +
      MIN ANGLEMAX ANGLEX-PIPIY-PI/2PI/2Z-PI/2PI/2
      +
    • +
    + +*/ +class btGeneric6DofConstraint : public btTypedConstraint +{ +protected: + + //! relative_frames + //!@{ + btTransform m_frameInA;//!< the constraint space w.r.t body A + btTransform m_frameInB;//!< the constraint space w.r.t body B + //!@} + + //! Jacobians + //!@{ + btJacobianEntry m_jacLinear[3];//!< 3 orthogonal linear constraints + btJacobianEntry m_jacAng[3];//!< 3 orthogonal angular constraints + //!@} + + //! Linear_Limit_parameters + //!@{ + btTranslationalLimitMotor m_linearLimits; + //!@} + + + //! hinge_parameters + //!@{ + btRotationalLimitMotor m_angularLimits[3]; + //!@} + + +protected: + //! temporal variables + //!@{ + btScalar m_timeStep; + btTransform m_calculatedTransformA; + btTransform m_calculatedTransformB; + btVector3 m_calculatedAxisAngleDiff; + btVector3 m_calculatedAxis[3]; + btVector3 m_calculatedLinearDiff; + + btVector3 m_AnchorPos; // point betwen pivots of bodies A and B to solve linear axes + + bool m_useLinearReferenceFrameA; + + //!@} + + btGeneric6DofConstraint& operator=(btGeneric6DofConstraint& other) + { + btAssert(0); + (void) other; + return *this; + } + + + int setAngularLimits(btConstraintInfo2 *info, int row_offset,const btTransform& transA,const btTransform& transB,const btVector3& linVelA,const btVector3& linVelB,const btVector3& angVelA,const btVector3& angVelB); + + int setLinearLimits(btConstraintInfo2 *info,const btTransform& transA,const btTransform& transB,const btVector3& linVelA,const btVector3& linVelB,const btVector3& angVelA,const btVector3& angVelB); + + void buildLinearJacobian( + btJacobianEntry & jacLinear,const btVector3 & normalWorld, + const btVector3 & pivotAInW,const btVector3 & pivotBInW); + + void buildAngularJacobian(btJacobianEntry & jacAngular,const btVector3 & jointAxisW); + + // tests linear limits + void calculateLinearInfo(); + + //! calcs the euler angles between the two bodies. + void calculateAngleInfo(); + + + +public: + + ///for backwards compatibility during the transition to 'getInfo/getInfo2' + bool m_useSolveConstraintObsolete; + + btGeneric6DofConstraint(btRigidBody& rbA, btRigidBody& rbB, const btTransform& frameInA, const btTransform& frameInB ,bool useLinearReferenceFrameA); + + btGeneric6DofConstraint(); + + //! Calcs global transform of the offsets + /*! + Calcs the global transform for the joint offset for body A an B, and also calcs the agle differences between the bodies. + \sa btGeneric6DofConstraint.getCalculatedTransformA , btGeneric6DofConstraint.getCalculatedTransformB, btGeneric6DofConstraint.calculateAngleInfo + */ + void calculateTransforms(const btTransform& transA,const btTransform& transB); + + void calculateTransforms(); + + //! Gets the global transform of the offset for body A + /*! + \sa btGeneric6DofConstraint.getFrameOffsetA, btGeneric6DofConstraint.getFrameOffsetB, btGeneric6DofConstraint.calculateAngleInfo. + */ + const btTransform & getCalculatedTransformA() const + { + return m_calculatedTransformA; + } + + //! Gets the global transform of the offset for body B + /*! + \sa btGeneric6DofConstraint.getFrameOffsetA, btGeneric6DofConstraint.getFrameOffsetB, btGeneric6DofConstraint.calculateAngleInfo. + */ + const btTransform & getCalculatedTransformB() const + { + return m_calculatedTransformB; + } + + const btTransform & getFrameOffsetA() const + { + return m_frameInA; + } + + const btTransform & getFrameOffsetB() const + { + return m_frameInB; + } + + + btTransform & getFrameOffsetA() + { + return m_frameInA; + } + + btTransform & getFrameOffsetB() + { + return m_frameInB; + } + + + //! performs Jacobian calculation, and also calculates angle differences and axis + virtual void buildJacobian(); + + virtual void getInfo1 (btConstraintInfo1* info); + + void getInfo1NonVirtual (btConstraintInfo1* info); + + virtual void getInfo2 (btConstraintInfo2* info); + + void getInfo2NonVirtual (btConstraintInfo2* info,const btTransform& transA,const btTransform& transB,const btVector3& linVelA,const btVector3& linVelB,const btVector3& angVelA,const btVector3& angVelB); + + + virtual void solveConstraintObsolete(btSolverBody& bodyA,btSolverBody& bodyB,btScalar timeStep); + + void updateRHS(btScalar timeStep); + + //! Get the rotation axis in global coordinates + /*! + \pre btGeneric6DofConstraint.buildJacobian must be called previously. + */ + btVector3 getAxis(int axis_index) const; + + //! Get the relative Euler angle + /*! + \pre btGeneric6DofConstraint::calculateTransforms() must be called previously. + */ + btScalar getAngle(int axis_index) const; + + //! Get the relative position of the constraint pivot + /*! + \pre btGeneric6DofConstraint::calculateTransforms() must be called previously. + */ + btScalar getRelativePivotPosition(int axis_index) const; + + + //! Test angular limit. + /*! + Calculates angular correction and returns true if limit needs to be corrected. + \pre btGeneric6DofConstraint::calculateTransforms() must be called previously. + */ + bool testAngularLimitMotor(int axis_index); + + void setLinearLowerLimit(const btVector3& linearLower) + { + m_linearLimits.m_lowerLimit = linearLower; + } + + void setLinearUpperLimit(const btVector3& linearUpper) + { + m_linearLimits.m_upperLimit = linearUpper; + } + + void setAngularLowerLimit(const btVector3& angularLower) + { + for(int i = 0; i < 3; i++) + m_angularLimits[i].m_loLimit = btNormalizeAngle(angularLower[i]); + } + + void setAngularUpperLimit(const btVector3& angularUpper) + { + for(int i = 0; i < 3; i++) + m_angularLimits[i].m_hiLimit = btNormalizeAngle(angularUpper[i]); + } + + //! Retrieves the angular limit informacion + btRotationalLimitMotor * getRotationalLimitMotor(int index) + { + return &m_angularLimits[index]; + } + + //! Retrieves the limit informacion + btTranslationalLimitMotor * getTranslationalLimitMotor() + { + return &m_linearLimits; + } + + //first 3 are linear, next 3 are angular + void setLimit(int axis, btScalar lo, btScalar hi) + { + if(axis<3) + { + m_linearLimits.m_lowerLimit[axis] = lo; + m_linearLimits.m_upperLimit[axis] = hi; + } + else + { + lo = btNormalizeAngle(lo); + hi = btNormalizeAngle(hi); + m_angularLimits[axis-3].m_loLimit = lo; + m_angularLimits[axis-3].m_hiLimit = hi; + } + } + + //! Test limit + /*! + - free means upper < lower, + - locked means upper == lower + - limited means upper > lower + - limitIndex: first 3 are linear, next 3 are angular + */ + bool isLimited(int limitIndex) + { + if(limitIndex<3) + { + return m_linearLimits.isLimited(limitIndex); + + } + return m_angularLimits[limitIndex-3].isLimited(); + } + + virtual void calcAnchorPos(void); // overridable + + int get_limit_motor_info2( btRotationalLimitMotor * limot, + const btTransform& transA,const btTransform& transB,const btVector3& linVelA,const btVector3& linVelB,const btVector3& angVelA,const btVector3& angVelB, + btConstraintInfo2 *info, int row, btVector3& ax1, int rotational); + + +}; + + +#endif //GENERIC_6DOF_CONSTRAINT_H diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.cpp b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.cpp new file mode 100644 index 000000000..5dbf4692e --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.cpp @@ -0,0 +1,144 @@ +/* +Bullet Continuous Collision Detection and Physics Library, http://bulletphysics.org +Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + +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 "btGeneric6DofSpringConstraint.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include "LinearMath/btTransformUtil.h" + + +btGeneric6DofSpringConstraint::btGeneric6DofSpringConstraint(btRigidBody& rbA, btRigidBody& rbB, const btTransform& frameInA, const btTransform& frameInB ,bool useLinearReferenceFrameA) + : btGeneric6DofConstraint(rbA, rbB, frameInA, frameInB, useLinearReferenceFrameA) +{ + for(int i = 0; i < 6; i++) + { + m_springEnabled[i] = false; + m_equilibriumPoint[i] = btScalar(0.f); + m_springStiffness[i] = btScalar(0.f); + m_springDamping[i] = btScalar(1.f); + } +} + + +void btGeneric6DofSpringConstraint::enableSpring(int index, bool onOff) +{ + btAssert((index >= 0) && (index < 6)); + m_springEnabled[index] = onOff; + if(index < 3) + { + m_linearLimits.m_enableMotor[index] = onOff; + } + else + { + m_angularLimits[index - 3].m_enableMotor = onOff; + } +} + + + +void btGeneric6DofSpringConstraint::setStiffness(int index, btScalar stiffness) +{ + btAssert((index >= 0) && (index < 6)); + m_springStiffness[index] = stiffness; +} + + +void btGeneric6DofSpringConstraint::setDamping(int index, btScalar damping) +{ + btAssert((index >= 0) && (index < 6)); + m_springDamping[index] = damping; +} + + +void btGeneric6DofSpringConstraint::setEquilibriumPoint() +{ + calculateTransforms(); + for(int i = 0; i < 3; i++) + { + m_equilibriumPoint[i] = m_calculatedLinearDiff[i]; + } + for(int i = 0; i < 3; i++) + { + m_equilibriumPoint[i + 3] = m_calculatedAxisAngleDiff[i]; + } +} + + + +void btGeneric6DofSpringConstraint::setEquilibriumPoint(int index) +{ + btAssert((index >= 0) && (index < 6)); + calculateTransforms(); + if(index < 3) + { + m_equilibriumPoint[index] = m_calculatedLinearDiff[index]; + } + else + { + m_equilibriumPoint[index + 3] = m_calculatedAxisAngleDiff[index]; + } +} + + + +void btGeneric6DofSpringConstraint::internalUpdateSprings(btConstraintInfo2* info) +{ + // it is assumed that calculateTransforms() have been called before this call + int i; + btVector3 relVel = m_rbB.getLinearVelocity() - m_rbA.getLinearVelocity(); + for(i = 0; i < 3; i++) + { + if(m_springEnabled[i]) + { + // get current position of constraint + btScalar currPos = m_calculatedLinearDiff[i]; + // calculate difference + btScalar delta = currPos - m_equilibriumPoint[i]; + // spring force is (delta * m_stiffness) according to Hooke's Law + btScalar force = delta * m_springStiffness[i]; + btScalar velFactor = info->fps * m_springDamping[i] / btScalar(info->m_numIterations); + m_linearLimits.m_targetVelocity[i] = velFactor * force; + m_linearLimits.m_maxMotorForce[i] = btFabs(force) / info->fps; + } + } + for(i = 0; i < 3; i++) + { + if(m_springEnabled[i + 3]) + { + // get current position of constraint + btScalar currPos = m_calculatedAxisAngleDiff[i]; + // calculate difference + btScalar delta = currPos - m_equilibriumPoint[i+3]; + // spring force is (-delta * m_stiffness) according to Hooke's Law + btScalar force = -delta * m_springStiffness[i+3]; + btScalar velFactor = info->fps * m_springDamping[i+3] / btScalar(info->m_numIterations); + m_angularLimits[i].m_targetVelocity = velFactor * force; + m_angularLimits[i].m_maxMotorForce = btFabs(force) / info->fps; + } + } +} + + +void btGeneric6DofSpringConstraint::getInfo2(btConstraintInfo2* info) +{ + // this will be called by constraint solver at the constraint setup stage + // set current motor parameters + internalUpdateSprings(info); + // do the rest of job for constraint setup + btGeneric6DofConstraint::getInfo2(info); +} + + + + diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.h new file mode 100644 index 000000000..e0c1fc9ae --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.h @@ -0,0 +1,54 @@ +/* +Bullet Continuous Collision Detection and Physics Library, http://bulletphysics.org +Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + +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 GENERIC_6DOF_SPRING_CONSTRAINT_H +#define GENERIC_6DOF_SPRING_CONSTRAINT_H + + +#include "LinearMath/btVector3.h" +#include "btTypedConstraint.h" +#include "btGeneric6DofConstraint.h" + + +/// Generic 6 DOF constraint that allows to set spring motors to any translational and rotational DOF + +/// DOF index used in enableSpring() and setStiffness() means: +/// 0 : translation X +/// 1 : translation Y +/// 2 : translation Z +/// 3 : rotation X (3rd Euler rotational around new position of X axis, range [-PI+epsilon, PI-epsilon] ) +/// 4 : rotation Y (2nd Euler rotational around new position of Y axis, range [-PI/2+epsilon, PI/2-epsilon] ) +/// 5 : rotation Z (1st Euler rotational around Z axis, range [-PI+epsilon, PI-epsilon] ) + +class btGeneric6DofSpringConstraint : public btGeneric6DofConstraint +{ +protected: + bool m_springEnabled[6]; + btScalar m_equilibriumPoint[6]; + btScalar m_springStiffness[6]; + btScalar m_springDamping[6]; // between 0 and 1 (1 == no damping) + void internalUpdateSprings(btConstraintInfo2* info); +public: + btGeneric6DofSpringConstraint(btRigidBody& rbA, btRigidBody& rbB, const btTransform& frameInA, const btTransform& frameInB ,bool useLinearReferenceFrameA); + void enableSpring(int index, bool onOff); + void setStiffness(int index, btScalar stiffness); + void setDamping(int index, btScalar damping); + void setEquilibriumPoint(); // set the current constraint position/orientation as an equilibrium point for all DOF + void setEquilibriumPoint(int index); // set the current constraint position/orientation as an equilibrium point for given DOF + virtual void getInfo2 (btConstraintInfo2* info); +}; + +#endif // GENERIC_6DOF_SPRING_CONSTRAINT_H + diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btHinge2Constraint.cpp b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btHinge2Constraint.cpp new file mode 100644 index 000000000..29123d526 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btHinge2Constraint.cpp @@ -0,0 +1,66 @@ +/* +Bullet Continuous Collision Detection and Physics Library, http://bulletphysics.org +Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + +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 "btHinge2Constraint.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include "LinearMath/btTransformUtil.h" + + + +// constructor +// anchor, axis1 and axis2 are in world coordinate system +// axis1 must be orthogonal to axis2 +btHinge2Constraint::btHinge2Constraint(btRigidBody& rbA, btRigidBody& rbB, btVector3& anchor, btVector3& axis1, btVector3& axis2) +: btGeneric6DofSpringConstraint(rbA, rbB, btTransform::getIdentity(), btTransform::getIdentity(), true), + m_anchor(anchor), + m_axis1(axis1), + m_axis2(axis2) +{ + // build frame basis + // 6DOF constraint uses Euler angles and to define limits + // it is assumed that rotational order is : + // Z - first, allowed limits are (-PI,PI); + // new position of Y - second (allowed limits are (-PI/2 + epsilon, PI/2 - epsilon), where epsilon is a small positive number + // used to prevent constraint from instability on poles; + // new position of X, allowed limits are (-PI,PI); + // So to simulate ODE Universal joint we should use parent axis as Z, child axis as Y and limit all other DOFs + // Build the frame in world coordinate system first + btVector3 zAxis = axis1.normalize(); + btVector3 xAxis = axis2.normalize(); + btVector3 yAxis = zAxis.cross(xAxis); // we want right coordinate system + btTransform frameInW; + frameInW.setIdentity(); + frameInW.getBasis().setValue( xAxis[0], yAxis[0], zAxis[0], + xAxis[1], yAxis[1], zAxis[1], + xAxis[2], yAxis[2], zAxis[2]); + frameInW.setOrigin(anchor); + // now get constraint frame in local coordinate systems + m_frameInA = rbA.getCenterOfMassTransform().inverse() * frameInW; + m_frameInB = rbB.getCenterOfMassTransform().inverse() * frameInW; + // sei limits + setLinearLowerLimit(btVector3(0.f, 0.f, -1.f)); + setLinearUpperLimit(btVector3(0.f, 0.f, 1.f)); + // like front wheels of a car + setAngularLowerLimit(btVector3(1.f, 0.f, -SIMD_HALF_PI * 0.5f)); + setAngularUpperLimit(btVector3(-1.f, 0.f, SIMD_HALF_PI * 0.5f)); + // enable suspension + enableSpring(2, true); + setStiffness(2, SIMD_PI * SIMD_PI * 4.f); // period 1 sec for 1 kilogramm weel :-) + setDamping(2, 0.01f); + setEquilibriumPoint(); +} + diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btHinge2Constraint.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btHinge2Constraint.h new file mode 100644 index 000000000..15fd4a014 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btHinge2Constraint.h @@ -0,0 +1,58 @@ +/* +Bullet Continuous Collision Detection and Physics Library, http://bulletphysics.org +Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + +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 HINGE2_CONSTRAINT_H +#define HINGE2_CONSTRAINT_H + + + +#include "LinearMath/btVector3.h" +#include "btTypedConstraint.h" +#include "btGeneric6DofSpringConstraint.h" + + + +// Constraint similar to ODE Hinge2 Joint +// has 3 degrees of frredom: +// 2 rotational degrees of freedom, similar to Euler rotations around Z (axis 1) and X (axis 2) +// 1 translational (along axis Z) with suspension spring + +class btHinge2Constraint : public btGeneric6DofSpringConstraint +{ +protected: + btVector3 m_anchor; + btVector3 m_axis1; + btVector3 m_axis2; +public: + // constructor + // anchor, axis1 and axis2 are in world coordinate system + // axis1 must be orthogonal to axis2 + btHinge2Constraint(btRigidBody& rbA, btRigidBody& rbB, btVector3& anchor, btVector3& axis1, btVector3& axis2); + // access + const btVector3& getAnchor() { return m_calculatedTransformA.getOrigin(); } + const btVector3& getAnchor2() { return m_calculatedTransformB.getOrigin(); } + const btVector3& getAxis1() { return m_axis1; } + const btVector3& getAxis2() { return m_axis2; } + btScalar getAngle1() { return getAngle(2); } + btScalar getAngle2() { return getAngle(0); } + // limits + void setUpperLimit(btScalar ang1max) { setAngularUpperLimit(btVector3(-1.f, 0.f, ang1max)); } + void setLowerLimit(btScalar ang1min) { setAngularLowerLimit(btVector3( 1.f, 0.f, ang1min)); } +}; + + + +#endif // HINGE2_CONSTRAINT_H + diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btHingeConstraint.cpp b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btHingeConstraint.cpp new file mode 100644 index 000000000..c637231c4 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btHingeConstraint.cpp @@ -0,0 +1,813 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btHingeConstraint.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include "LinearMath/btTransformUtil.h" +#include "LinearMath/btMinMax.h" +#include +#include "btSolverBody.h" + + + +#define HINGE_USE_OBSOLETE_SOLVER false + + +#ifndef __SPU__ + +btHingeConstraint::btHingeConstraint() +: btTypedConstraint (HINGE_CONSTRAINT_TYPE), +m_enableAngularMotor(false), +m_useSolveConstraintObsolete(HINGE_USE_OBSOLETE_SOLVER), +m_useReferenceFrameA(false) +{ + m_referenceSign = m_useReferenceFrameA ? btScalar(-1.f) : btScalar(1.f); +} + + + +btHingeConstraint::btHingeConstraint(btRigidBody& rbA,btRigidBody& rbB, const btVector3& pivotInA,const btVector3& pivotInB, + btVector3& axisInA,btVector3& axisInB, bool useReferenceFrameA) + :btTypedConstraint(HINGE_CONSTRAINT_TYPE, rbA,rbB), + m_angularOnly(false), + m_enableAngularMotor(false), + m_useSolveConstraintObsolete(HINGE_USE_OBSOLETE_SOLVER), + m_useReferenceFrameA(useReferenceFrameA) +{ + m_rbAFrame.getOrigin() = pivotInA; + + // since no frame is given, assume this to be zero angle and just pick rb transform axis + btVector3 rbAxisA1 = rbA.getCenterOfMassTransform().getBasis().getColumn(0); + + btVector3 rbAxisA2; + btScalar projection = axisInA.dot(rbAxisA1); + if (projection >= 1.0f - SIMD_EPSILON) { + rbAxisA1 = -rbA.getCenterOfMassTransform().getBasis().getColumn(2); + rbAxisA2 = rbA.getCenterOfMassTransform().getBasis().getColumn(1); + } else if (projection <= -1.0f + SIMD_EPSILON) { + rbAxisA1 = rbA.getCenterOfMassTransform().getBasis().getColumn(2); + rbAxisA2 = rbA.getCenterOfMassTransform().getBasis().getColumn(1); + } else { + rbAxisA2 = axisInA.cross(rbAxisA1); + rbAxisA1 = rbAxisA2.cross(axisInA); + } + + m_rbAFrame.getBasis().setValue( rbAxisA1.getX(),rbAxisA2.getX(),axisInA.getX(), + rbAxisA1.getY(),rbAxisA2.getY(),axisInA.getY(), + rbAxisA1.getZ(),rbAxisA2.getZ(),axisInA.getZ() ); + + btQuaternion rotationArc = shortestArcQuat(axisInA,axisInB); + btVector3 rbAxisB1 = quatRotate(rotationArc,rbAxisA1); + btVector3 rbAxisB2 = axisInB.cross(rbAxisB1); + + m_rbBFrame.getOrigin() = pivotInB; + m_rbBFrame.getBasis().setValue( rbAxisB1.getX(),rbAxisB2.getX(),axisInB.getX(), + rbAxisB1.getY(),rbAxisB2.getY(),axisInB.getY(), + rbAxisB1.getZ(),rbAxisB2.getZ(),axisInB.getZ() ); + + //start with free + m_lowerLimit = btScalar(1.0f); + m_upperLimit = btScalar(-1.0f); + m_biasFactor = 0.3f; + m_relaxationFactor = 1.0f; + m_limitSoftness = 0.9f; + m_solveLimit = false; + m_referenceSign = m_useReferenceFrameA ? btScalar(-1.f) : btScalar(1.f); +} + + + +btHingeConstraint::btHingeConstraint(btRigidBody& rbA,const btVector3& pivotInA,btVector3& axisInA, bool useReferenceFrameA) +:btTypedConstraint(HINGE_CONSTRAINT_TYPE, rbA), m_angularOnly(false), m_enableAngularMotor(false), +m_useSolveConstraintObsolete(HINGE_USE_OBSOLETE_SOLVER), +m_useReferenceFrameA(useReferenceFrameA) +{ + + // since no frame is given, assume this to be zero angle and just pick rb transform axis + // fixed axis in worldspace + btVector3 rbAxisA1, rbAxisA2; + btPlaneSpace1(axisInA, rbAxisA1, rbAxisA2); + + m_rbAFrame.getOrigin() = pivotInA; + m_rbAFrame.getBasis().setValue( rbAxisA1.getX(),rbAxisA2.getX(),axisInA.getX(), + rbAxisA1.getY(),rbAxisA2.getY(),axisInA.getY(), + rbAxisA1.getZ(),rbAxisA2.getZ(),axisInA.getZ() ); + + btVector3 axisInB = rbA.getCenterOfMassTransform().getBasis() * axisInA; + + btQuaternion rotationArc = shortestArcQuat(axisInA,axisInB); + btVector3 rbAxisB1 = quatRotate(rotationArc,rbAxisA1); + btVector3 rbAxisB2 = axisInB.cross(rbAxisB1); + + + m_rbBFrame.getOrigin() = rbA.getCenterOfMassTransform()(pivotInA); + m_rbBFrame.getBasis().setValue( rbAxisB1.getX(),rbAxisB2.getX(),axisInB.getX(), + rbAxisB1.getY(),rbAxisB2.getY(),axisInB.getY(), + rbAxisB1.getZ(),rbAxisB2.getZ(),axisInB.getZ() ); + + //start with free + m_lowerLimit = btScalar(1.0f); + m_upperLimit = btScalar(-1.0f); + m_biasFactor = 0.3f; + m_relaxationFactor = 1.0f; + m_limitSoftness = 0.9f; + m_solveLimit = false; + m_referenceSign = m_useReferenceFrameA ? btScalar(-1.f) : btScalar(1.f); +} + + + +btHingeConstraint::btHingeConstraint(btRigidBody& rbA,btRigidBody& rbB, + const btTransform& rbAFrame, const btTransform& rbBFrame, bool useReferenceFrameA) +:btTypedConstraint(HINGE_CONSTRAINT_TYPE, rbA,rbB),m_rbAFrame(rbAFrame),m_rbBFrame(rbBFrame), +m_angularOnly(false), +m_enableAngularMotor(false), +m_useSolveConstraintObsolete(HINGE_USE_OBSOLETE_SOLVER), +m_useReferenceFrameA(useReferenceFrameA) +{ + //start with free + m_lowerLimit = btScalar(1.0f); + m_upperLimit = btScalar(-1.0f); + m_biasFactor = 0.3f; + m_relaxationFactor = 1.0f; + m_limitSoftness = 0.9f; + m_solveLimit = false; + m_referenceSign = m_useReferenceFrameA ? btScalar(-1.f) : btScalar(1.f); +} + + + +btHingeConstraint::btHingeConstraint(btRigidBody& rbA, const btTransform& rbAFrame, bool useReferenceFrameA) +:btTypedConstraint(HINGE_CONSTRAINT_TYPE, rbA),m_rbAFrame(rbAFrame),m_rbBFrame(rbAFrame), +m_angularOnly(false), +m_enableAngularMotor(false), +m_useSolveConstraintObsolete(HINGE_USE_OBSOLETE_SOLVER), +m_useReferenceFrameA(useReferenceFrameA) +{ + ///not providing rigidbody B means implicitly using worldspace for body B + + m_rbBFrame.getOrigin() = m_rbA.getCenterOfMassTransform()(m_rbAFrame.getOrigin()); + + //start with free + m_lowerLimit = btScalar(1.0f); + m_upperLimit = btScalar(-1.0f); + m_biasFactor = 0.3f; + m_relaxationFactor = 1.0f; + m_limitSoftness = 0.9f; + m_solveLimit = false; + m_referenceSign = m_useReferenceFrameA ? btScalar(-1.f) : btScalar(1.f); +} + + + +void btHingeConstraint::buildJacobian() +{ + if (m_useSolveConstraintObsolete) + { + m_appliedImpulse = btScalar(0.); + m_accMotorImpulse = btScalar(0.); + + if (!m_angularOnly) + { + btVector3 pivotAInW = m_rbA.getCenterOfMassTransform()*m_rbAFrame.getOrigin(); + btVector3 pivotBInW = m_rbB.getCenterOfMassTransform()*m_rbBFrame.getOrigin(); + btVector3 relPos = pivotBInW - pivotAInW; + + btVector3 normal[3]; + if (relPos.length2() > SIMD_EPSILON) + { + normal[0] = relPos.normalized(); + } + else + { + normal[0].setValue(btScalar(1.0),0,0); + } + + btPlaneSpace1(normal[0], normal[1], normal[2]); + + for (int i=0;i<3;i++) + { + new (&m_jac[i]) btJacobianEntry( + m_rbA.getCenterOfMassTransform().getBasis().transpose(), + m_rbB.getCenterOfMassTransform().getBasis().transpose(), + pivotAInW - m_rbA.getCenterOfMassPosition(), + pivotBInW - m_rbB.getCenterOfMassPosition(), + normal[i], + m_rbA.getInvInertiaDiagLocal(), + m_rbA.getInvMass(), + m_rbB.getInvInertiaDiagLocal(), + m_rbB.getInvMass()); + } + } + + //calculate two perpendicular jointAxis, orthogonal to hingeAxis + //these two jointAxis require equal angular velocities for both bodies + + //this is unused for now, it's a todo + btVector3 jointAxis0local; + btVector3 jointAxis1local; + + btPlaneSpace1(m_rbAFrame.getBasis().getColumn(2),jointAxis0local,jointAxis1local); + + getRigidBodyA().getCenterOfMassTransform().getBasis() * m_rbAFrame.getBasis().getColumn(2); + btVector3 jointAxis0 = getRigidBodyA().getCenterOfMassTransform().getBasis() * jointAxis0local; + btVector3 jointAxis1 = getRigidBodyA().getCenterOfMassTransform().getBasis() * jointAxis1local; + btVector3 hingeAxisWorld = getRigidBodyA().getCenterOfMassTransform().getBasis() * m_rbAFrame.getBasis().getColumn(2); + + new (&m_jacAng[0]) btJacobianEntry(jointAxis0, + m_rbA.getCenterOfMassTransform().getBasis().transpose(), + m_rbB.getCenterOfMassTransform().getBasis().transpose(), + m_rbA.getInvInertiaDiagLocal(), + m_rbB.getInvInertiaDiagLocal()); + + new (&m_jacAng[1]) btJacobianEntry(jointAxis1, + m_rbA.getCenterOfMassTransform().getBasis().transpose(), + m_rbB.getCenterOfMassTransform().getBasis().transpose(), + m_rbA.getInvInertiaDiagLocal(), + m_rbB.getInvInertiaDiagLocal()); + + new (&m_jacAng[2]) btJacobianEntry(hingeAxisWorld, + m_rbA.getCenterOfMassTransform().getBasis().transpose(), + m_rbB.getCenterOfMassTransform().getBasis().transpose(), + m_rbA.getInvInertiaDiagLocal(), + m_rbB.getInvInertiaDiagLocal()); + + // clear accumulator + m_accLimitImpulse = btScalar(0.); + + // test angular limit + testLimit(m_rbA.getCenterOfMassTransform(),m_rbB.getCenterOfMassTransform()); + + //Compute K = J*W*J' for hinge axis + btVector3 axisA = getRigidBodyA().getCenterOfMassTransform().getBasis() * m_rbAFrame.getBasis().getColumn(2); + m_kHinge = 1.0f / (getRigidBodyA().computeAngularImpulseDenominator(axisA) + + getRigidBodyB().computeAngularImpulseDenominator(axisA)); + + } +} + +void btHingeConstraint::solveConstraintObsolete(btSolverBody& bodyA,btSolverBody& bodyB,btScalar timeStep) +{ + + ///for backwards compatibility during the transition to 'getInfo/getInfo2' + if (m_useSolveConstraintObsolete) + { + + btVector3 pivotAInW = m_rbA.getCenterOfMassTransform()*m_rbAFrame.getOrigin(); + btVector3 pivotBInW = m_rbB.getCenterOfMassTransform()*m_rbBFrame.getOrigin(); + + btScalar tau = btScalar(0.3); + + //linear part + if (!m_angularOnly) + { + btVector3 rel_pos1 = pivotAInW - m_rbA.getCenterOfMassPosition(); + btVector3 rel_pos2 = pivotBInW - m_rbB.getCenterOfMassPosition(); + + btVector3 vel1,vel2; + bodyA.getVelocityInLocalPointObsolete(rel_pos1,vel1); + bodyB.getVelocityInLocalPointObsolete(rel_pos2,vel2); + btVector3 vel = vel1 - vel2; + + for (int i=0;i<3;i++) + { + const btVector3& normal = m_jac[i].m_linearJointAxis; + btScalar jacDiagABInv = btScalar(1.) / m_jac[i].getDiagonal(); + + btScalar rel_vel; + rel_vel = normal.dot(vel); + //positional error (zeroth order error) + btScalar depth = -(pivotAInW - pivotBInW).dot(normal); //this is the error projected on the normal + btScalar impulse = depth*tau/timeStep * jacDiagABInv - rel_vel * jacDiagABInv; + m_appliedImpulse += impulse; + btVector3 impulse_vector = normal * impulse; + btVector3 ftorqueAxis1 = rel_pos1.cross(normal); + btVector3 ftorqueAxis2 = rel_pos2.cross(normal); + bodyA.applyImpulse(normal*m_rbA.getInvMass(), m_rbA.getInvInertiaTensorWorld()*ftorqueAxis1,impulse); + bodyB.applyImpulse(normal*m_rbB.getInvMass(), m_rbB.getInvInertiaTensorWorld()*ftorqueAxis2,-impulse); + } + } + + + { + ///solve angular part + + // get axes in world space + btVector3 axisA = getRigidBodyA().getCenterOfMassTransform().getBasis() * m_rbAFrame.getBasis().getColumn(2); + btVector3 axisB = getRigidBodyB().getCenterOfMassTransform().getBasis() * m_rbBFrame.getBasis().getColumn(2); + + btVector3 angVelA; + bodyA.getAngularVelocity(angVelA); + btVector3 angVelB; + bodyB.getAngularVelocity(angVelB); + + btVector3 angVelAroundHingeAxisA = axisA * axisA.dot(angVelA); + btVector3 angVelAroundHingeAxisB = axisB * axisB.dot(angVelB); + + btVector3 angAorthog = angVelA - angVelAroundHingeAxisA; + btVector3 angBorthog = angVelB - angVelAroundHingeAxisB; + btVector3 velrelOrthog = angAorthog-angBorthog; + { + + + //solve orthogonal angular velocity correction + //btScalar relaxation = btScalar(1.); + btScalar len = velrelOrthog.length(); + if (len > btScalar(0.00001)) + { + btVector3 normal = velrelOrthog.normalized(); + btScalar denom = getRigidBodyA().computeAngularImpulseDenominator(normal) + + getRigidBodyB().computeAngularImpulseDenominator(normal); + // scale for mass and relaxation + //velrelOrthog *= (btScalar(1.)/denom) * m_relaxationFactor; + + bodyA.applyImpulse(btVector3(0,0,0), m_rbA.getInvInertiaTensorWorld()*velrelOrthog,-(btScalar(1.)/denom)); + bodyB.applyImpulse(btVector3(0,0,0), m_rbB.getInvInertiaTensorWorld()*velrelOrthog,(btScalar(1.)/denom)); + + } + + //solve angular positional correction + btVector3 angularError = axisA.cross(axisB) *(btScalar(1.)/timeStep); + btScalar len2 = angularError.length(); + if (len2>btScalar(0.00001)) + { + btVector3 normal2 = angularError.normalized(); + btScalar denom2 = getRigidBodyA().computeAngularImpulseDenominator(normal2) + + getRigidBodyB().computeAngularImpulseDenominator(normal2); + //angularError *= (btScalar(1.)/denom2) * relaxation; + + bodyA.applyImpulse(btVector3(0,0,0), m_rbA.getInvInertiaTensorWorld()*angularError,(btScalar(1.)/denom2)); + bodyB.applyImpulse(btVector3(0,0,0), m_rbB.getInvInertiaTensorWorld()*angularError,-(btScalar(1.)/denom2)); + + } + + + + + + // solve limit + if (m_solveLimit) + { + btScalar amplitude = ( (angVelB - angVelA).dot( axisA )*m_relaxationFactor + m_correction* (btScalar(1.)/timeStep)*m_biasFactor ) * m_limitSign; + + btScalar impulseMag = amplitude * m_kHinge; + + // Clamp the accumulated impulse + btScalar temp = m_accLimitImpulse; + m_accLimitImpulse = btMax(m_accLimitImpulse + impulseMag, btScalar(0) ); + impulseMag = m_accLimitImpulse - temp; + + + + bodyA.applyImpulse(btVector3(0,0,0), m_rbA.getInvInertiaTensorWorld()*axisA,impulseMag * m_limitSign); + bodyB.applyImpulse(btVector3(0,0,0), m_rbB.getInvInertiaTensorWorld()*axisA,-(impulseMag * m_limitSign)); + + } + } + + //apply motor + if (m_enableAngularMotor) + { + //todo: add limits too + btVector3 angularLimit(0,0,0); + + btVector3 velrel = angVelAroundHingeAxisA - angVelAroundHingeAxisB; + btScalar projRelVel = velrel.dot(axisA); + + btScalar desiredMotorVel = m_motorTargetVelocity; + btScalar motor_relvel = desiredMotorVel - projRelVel; + + btScalar unclippedMotorImpulse = m_kHinge * motor_relvel;; + + // accumulated impulse clipping: + btScalar fMaxImpulse = m_maxMotorImpulse; + btScalar newAccImpulse = m_accMotorImpulse + unclippedMotorImpulse; + btScalar clippedMotorImpulse = unclippedMotorImpulse; + if (newAccImpulse > fMaxImpulse) + { + newAccImpulse = fMaxImpulse; + clippedMotorImpulse = newAccImpulse - m_accMotorImpulse; + } + else if (newAccImpulse < -fMaxImpulse) + { + newAccImpulse = -fMaxImpulse; + clippedMotorImpulse = newAccImpulse - m_accMotorImpulse; + } + m_accMotorImpulse += clippedMotorImpulse; + + bodyA.applyImpulse(btVector3(0,0,0), m_rbA.getInvInertiaTensorWorld()*axisA,clippedMotorImpulse); + bodyB.applyImpulse(btVector3(0,0,0), m_rbB.getInvInertiaTensorWorld()*axisA,-clippedMotorImpulse); + + } + } + } + +} + + +#endif //__SPU__ + + +void btHingeConstraint::getInfo1(btConstraintInfo1* info) +{ + if (m_useSolveConstraintObsolete) + { + info->m_numConstraintRows = 0; + info->nub = 0; + } + else + { + info->m_numConstraintRows = 5; // Fixed 3 linear + 2 angular + info->nub = 1; + //always add the row, to avoid computation (data is not available yet) + //prepare constraint + testLimit(m_rbA.getCenterOfMassTransform(),m_rbB.getCenterOfMassTransform()); + if(getSolveLimit() || getEnableAngularMotor()) + { + info->m_numConstraintRows++; // limit 3rd anguar as well + info->nub--; + } + + } +} + +void btHingeConstraint::getInfo1NonVirtual(btConstraintInfo1* info) +{ + if (m_useSolveConstraintObsolete) + { + info->m_numConstraintRows = 0; + info->nub = 0; + } + else + { + //always add the 'limit' row, to avoid computation (data is not available yet) + info->m_numConstraintRows = 6; // Fixed 3 linear + 2 angular + info->nub = 0; + } +} + +void btHingeConstraint::getInfo2 (btConstraintInfo2* info) +{ + getInfo2Internal(info, m_rbA.getCenterOfMassTransform(),m_rbB.getCenterOfMassTransform(),m_rbA.getAngularVelocity(),m_rbB.getAngularVelocity()); +} + + +void btHingeConstraint::getInfo2NonVirtual (btConstraintInfo2* info,const btTransform& transA,const btTransform& transB,const btVector3& angVelA,const btVector3& angVelB) +{ + ///the regular (virtual) implementation getInfo2 already performs 'testLimit' during getInfo1, so we need to do it now + testLimit(transA,transB); + + getInfo2Internal(info,transA,transB,angVelA,angVelB); +} + + +void btHingeConstraint::getInfo2Internal(btConstraintInfo2* info, const btTransform& transA,const btTransform& transB,const btVector3& angVelA,const btVector3& angVelB) +{ + + btAssert(!m_useSolveConstraintObsolete); + int i, skip = info->rowskip; + // transforms in world space + btTransform trA = transA*m_rbAFrame; + btTransform trB = transB*m_rbBFrame; + // pivot point + btVector3 pivotAInW = trA.getOrigin(); + btVector3 pivotBInW = trB.getOrigin(); +#if 0 + if (0) + { + for (i=0;i<6;i++) + { + info->m_J1linearAxis[i*skip]=0; + info->m_J1linearAxis[i*skip+1]=0; + info->m_J1linearAxis[i*skip+2]=0; + + info->m_J1angularAxis[i*skip]=0; + info->m_J1angularAxis[i*skip+1]=0; + info->m_J1angularAxis[i*skip+2]=0; + + info->m_J2angularAxis[i*skip]=0; + info->m_J2angularAxis[i*skip+1]=0; + info->m_J2angularAxis[i*skip+2]=0; + + info->m_constraintError[i*skip]=0.f; + } + } +#endif //#if 0 + // linear (all fixed) + info->m_J1linearAxis[0] = 1; + info->m_J1linearAxis[skip + 1] = 1; + info->m_J1linearAxis[2 * skip + 2] = 1; + + + + + + btVector3 a1 = pivotAInW - transA.getOrigin(); + { + btVector3* angular0 = (btVector3*)(info->m_J1angularAxis); + btVector3* angular1 = (btVector3*)(info->m_J1angularAxis + skip); + btVector3* angular2 = (btVector3*)(info->m_J1angularAxis + 2 * skip); + btVector3 a1neg = -a1; + a1neg.getSkewSymmetricMatrix(angular0,angular1,angular2); + } + btVector3 a2 = pivotBInW - transB.getOrigin(); + { + btVector3* angular0 = (btVector3*)(info->m_J2angularAxis); + btVector3* angular1 = (btVector3*)(info->m_J2angularAxis + skip); + btVector3* angular2 = (btVector3*)(info->m_J2angularAxis + 2 * skip); + a2.getSkewSymmetricMatrix(angular0,angular1,angular2); + } + // linear RHS + btScalar k = info->fps * info->erp; + for(i = 0; i < 3; i++) + { + info->m_constraintError[i * skip] = k * (pivotBInW[i] - pivotAInW[i]); + } + // make rotations around X and Y equal + // the hinge axis should be the only unconstrained + // rotational axis, the angular velocity of the two bodies perpendicular to + // the hinge axis should be equal. thus the constraint equations are + // p*w1 - p*w2 = 0 + // q*w1 - q*w2 = 0 + // where p and q are unit vectors normal to the hinge axis, and w1 and w2 + // are the angular velocity vectors of the two bodies. + // get hinge axis (Z) + btVector3 ax1 = trA.getBasis().getColumn(2); + // get 2 orthos to hinge axis (X, Y) + btVector3 p = trA.getBasis().getColumn(0); + btVector3 q = trA.getBasis().getColumn(1); + // set the two hinge angular rows + int s3 = 3 * info->rowskip; + int s4 = 4 * info->rowskip; + + info->m_J1angularAxis[s3 + 0] = p[0]; + info->m_J1angularAxis[s3 + 1] = p[1]; + info->m_J1angularAxis[s3 + 2] = p[2]; + info->m_J1angularAxis[s4 + 0] = q[0]; + info->m_J1angularAxis[s4 + 1] = q[1]; + info->m_J1angularAxis[s4 + 2] = q[2]; + + info->m_J2angularAxis[s3 + 0] = -p[0]; + info->m_J2angularAxis[s3 + 1] = -p[1]; + info->m_J2angularAxis[s3 + 2] = -p[2]; + info->m_J2angularAxis[s4 + 0] = -q[0]; + info->m_J2angularAxis[s4 + 1] = -q[1]; + info->m_J2angularAxis[s4 + 2] = -q[2]; + // compute the right hand side of the constraint equation. set relative + // body velocities along p and q to bring the hinge back into alignment. + // if ax1,ax2 are the unit length hinge axes as computed from body1 and + // body2, we need to rotate both bodies along the axis u = (ax1 x ax2). + // if `theta' is the angle between ax1 and ax2, we need an angular velocity + // along u to cover angle erp*theta in one step : + // |angular_velocity| = angle/time = erp*theta / stepsize + // = (erp*fps) * theta + // angular_velocity = |angular_velocity| * (ax1 x ax2) / |ax1 x ax2| + // = (erp*fps) * theta * (ax1 x ax2) / sin(theta) + // ...as ax1 and ax2 are unit length. if theta is smallish, + // theta ~= sin(theta), so + // angular_velocity = (erp*fps) * (ax1 x ax2) + // ax1 x ax2 is in the plane space of ax1, so we project the angular + // velocity to p and q to find the right hand side. + btVector3 ax2 = trB.getBasis().getColumn(2); + btVector3 u = ax1.cross(ax2); + info->m_constraintError[s3] = k * u.dot(p); + info->m_constraintError[s4] = k * u.dot(q); + // check angular limits + int nrow = 4; // last filled row + int srow; + btScalar limit_err = btScalar(0.0); + int limit = 0; + if(getSolveLimit()) + { + limit_err = m_correction * m_referenceSign; + limit = (limit_err > btScalar(0.0)) ? 1 : 2; + } + // if the hinge has joint limits or motor, add in the extra row + int powered = 0; + if(getEnableAngularMotor()) + { + powered = 1; + } + if(limit || powered) + { + nrow++; + srow = nrow * info->rowskip; + info->m_J1angularAxis[srow+0] = ax1[0]; + info->m_J1angularAxis[srow+1] = ax1[1]; + info->m_J1angularAxis[srow+2] = ax1[2]; + + info->m_J2angularAxis[srow+0] = -ax1[0]; + info->m_J2angularAxis[srow+1] = -ax1[1]; + info->m_J2angularAxis[srow+2] = -ax1[2]; + + btScalar lostop = getLowerLimit(); + btScalar histop = getUpperLimit(); + if(limit && (lostop == histop)) + { // the joint motor is ineffective + powered = 0; + } + info->m_constraintError[srow] = btScalar(0.0f); + if(powered) + { + info->cfm[srow] = btScalar(0.0); + btScalar mot_fact = getMotorFactor(m_hingeAngle, lostop, histop, m_motorTargetVelocity, info->fps * info->erp); + info->m_constraintError[srow] += mot_fact * m_motorTargetVelocity * m_referenceSign; + info->m_lowerLimit[srow] = - m_maxMotorImpulse; + info->m_upperLimit[srow] = m_maxMotorImpulse; + } + if(limit) + { + k = info->fps * info->erp; + info->m_constraintError[srow] += k * limit_err; + info->cfm[srow] = btScalar(0.0); + if(lostop == histop) + { + // limited low and high simultaneously + info->m_lowerLimit[srow] = -SIMD_INFINITY; + info->m_upperLimit[srow] = SIMD_INFINITY; + } + else if(limit == 1) + { // low limit + info->m_lowerLimit[srow] = 0; + info->m_upperLimit[srow] = SIMD_INFINITY; + } + else + { // high limit + info->m_lowerLimit[srow] = -SIMD_INFINITY; + info->m_upperLimit[srow] = 0; + } + // bounce (we'll use slider parameter abs(1.0 - m_dampingLimAng) for that) + btScalar bounce = m_relaxationFactor; + if(bounce > btScalar(0.0)) + { + btScalar vel = angVelA.dot(ax1); + vel -= angVelB.dot(ax1); + // only apply bounce if the velocity is incoming, and if the + // resulting c[] exceeds what we already have. + if(limit == 1) + { // low limit + if(vel < 0) + { + btScalar newc = -bounce * vel; + if(newc > info->m_constraintError[srow]) + { + info->m_constraintError[srow] = newc; + } + } + } + else + { // high limit - all those computations are reversed + if(vel > 0) + { + btScalar newc = -bounce * vel; + if(newc < info->m_constraintError[srow]) + { + info->m_constraintError[srow] = newc; + } + } + } + } + info->m_constraintError[srow] *= m_biasFactor; + } // if(limit) + } // if angular limit or powered +} + + + + + + +void btHingeConstraint::updateRHS(btScalar timeStep) +{ + (void)timeStep; + +} + + +btScalar btHingeConstraint::getHingeAngle() +{ + return getHingeAngle(m_rbA.getCenterOfMassTransform(),m_rbB.getCenterOfMassTransform()); +} + +btScalar btHingeConstraint::getHingeAngle(const btTransform& transA,const btTransform& transB) +{ + const btVector3 refAxis0 = transA.getBasis() * m_rbAFrame.getBasis().getColumn(0); + const btVector3 refAxis1 = transA.getBasis() * m_rbAFrame.getBasis().getColumn(1); + const btVector3 swingAxis = transB.getBasis() * m_rbBFrame.getBasis().getColumn(1); + btScalar angle = btAtan2Fast(swingAxis.dot(refAxis0), swingAxis.dot(refAxis1)); + return m_referenceSign * angle; +} + + +#if 0 +void btHingeConstraint::testLimit() +{ + // Compute limit information + m_hingeAngle = getHingeAngle(); + m_correction = btScalar(0.); + m_limitSign = btScalar(0.); + m_solveLimit = false; + if (m_lowerLimit <= m_upperLimit) + { + if (m_hingeAngle <= m_lowerLimit) + { + m_correction = (m_lowerLimit - m_hingeAngle); + m_limitSign = 1.0f; + m_solveLimit = true; + } + else if (m_hingeAngle >= m_upperLimit) + { + m_correction = m_upperLimit - m_hingeAngle; + m_limitSign = -1.0f; + m_solveLimit = true; + } + } + return; +} +#else + + +void btHingeConstraint::testLimit(const btTransform& transA,const btTransform& transB) +{ + // Compute limit information + m_hingeAngle = getHingeAngle(transA,transB); + m_correction = btScalar(0.); + m_limitSign = btScalar(0.); + m_solveLimit = false; + if (m_lowerLimit <= m_upperLimit) + { + m_hingeAngle = btAdjustAngleToLimits(m_hingeAngle, m_lowerLimit, m_upperLimit); + if (m_hingeAngle <= m_lowerLimit) + { + m_correction = (m_lowerLimit - m_hingeAngle); + m_limitSign = 1.0f; + m_solveLimit = true; + } + else if (m_hingeAngle >= m_upperLimit) + { + m_correction = m_upperLimit - m_hingeAngle; + m_limitSign = -1.0f; + m_solveLimit = true; + } + } + return; +} +#endif + +static btVector3 vHinge(0, 0, btScalar(1)); + +void btHingeConstraint::setMotorTarget(const btQuaternion& qAinB, btScalar dt) +{ + // convert target from body to constraint space + btQuaternion qConstraint = m_rbBFrame.getRotation().inverse() * qAinB * m_rbAFrame.getRotation(); + qConstraint.normalize(); + + // extract "pure" hinge component + btVector3 vNoHinge = quatRotate(qConstraint, vHinge); vNoHinge.normalize(); + btQuaternion qNoHinge = shortestArcQuat(vHinge, vNoHinge); + btQuaternion qHinge = qNoHinge.inverse() * qConstraint; + qHinge.normalize(); + + // compute angular target, clamped to limits + btScalar targetAngle = qHinge.getAngle(); + if (targetAngle > SIMD_PI) // long way around. flip quat and recalculate. + { + qHinge = operator-(qHinge); + targetAngle = qHinge.getAngle(); + } + if (qHinge.getZ() < 0) + targetAngle = -targetAngle; + + setMotorTarget(targetAngle, dt); +} + +void btHingeConstraint::setMotorTarget(btScalar targetAngle, btScalar dt) +{ + if (m_lowerLimit < m_upperLimit) + { + if (targetAngle < m_lowerLimit) + targetAngle = m_lowerLimit; + else if (targetAngle > m_upperLimit) + targetAngle = m_upperLimit; + } + + // compute angular velocity + btScalar curAngle = getHingeAngle(m_rbA.getCenterOfMassTransform(),m_rbB.getCenterOfMassTransform()); + btScalar dAngle = targetAngle - curAngle; + m_motorTargetVelocity = dAngle / dt; +} + + diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btHingeConstraint.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btHingeConstraint.h new file mode 100644 index 000000000..270f3f85f --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btHingeConstraint.h @@ -0,0 +1,223 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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. +*/ + +/* Hinge Constraint by Dirk Gregorius. Limits added by Marcus Hennix at Starbreeze Studios */ + +#ifndef HINGECONSTRAINT_H +#define HINGECONSTRAINT_H + +#include "LinearMath/btVector3.h" +#include "btJacobianEntry.h" +#include "btTypedConstraint.h" + +class btRigidBody; + +/// hinge constraint between two rigidbodies each with a pivotpoint that descibes the axis location in local space +/// axis defines the orientation of the hinge axis +ATTRIBUTE_ALIGNED16(class) btHingeConstraint : public btTypedConstraint +{ +#ifdef IN_PARALLELL_SOLVER +public: +#endif + btJacobianEntry m_jac[3]; //3 orthogonal linear constraints + btJacobianEntry m_jacAng[3]; //2 orthogonal angular constraints+ 1 for limit/motor + + btTransform m_rbAFrame; // constraint axii. Assumes z is hinge axis. + btTransform m_rbBFrame; + + btScalar m_motorTargetVelocity; + btScalar m_maxMotorImpulse; + + btScalar m_limitSoftness; + btScalar m_biasFactor; + btScalar m_relaxationFactor; + + btScalar m_lowerLimit; + btScalar m_upperLimit; + + btScalar m_kHinge; + + btScalar m_limitSign; + btScalar m_correction; + + btScalar m_accLimitImpulse; + btScalar m_hingeAngle; + btScalar m_referenceSign; + + bool m_angularOnly; + bool m_enableAngularMotor; + bool m_solveLimit; + bool m_useSolveConstraintObsolete; + bool m_useReferenceFrameA; + + btScalar m_accMotorImpulse; + + +public: + + btHingeConstraint(btRigidBody& rbA,btRigidBody& rbB, const btVector3& pivotInA,const btVector3& pivotInB, btVector3& axisInA,btVector3& axisInB, bool useReferenceFrameA = false); + + btHingeConstraint(btRigidBody& rbA,const btVector3& pivotInA,btVector3& axisInA, bool useReferenceFrameA = false); + + btHingeConstraint(btRigidBody& rbA,btRigidBody& rbB, const btTransform& rbAFrame, const btTransform& rbBFrame, bool useReferenceFrameA = false); + + btHingeConstraint(btRigidBody& rbA,const btTransform& rbAFrame, bool useReferenceFrameA = false); + + btHingeConstraint(); + + virtual void buildJacobian(); + + virtual void getInfo1 (btConstraintInfo1* info); + + void getInfo1NonVirtual(btConstraintInfo1* info); + + virtual void getInfo2 (btConstraintInfo2* info); + + void getInfo2NonVirtual(btConstraintInfo2* info,const btTransform& transA,const btTransform& transB,const btVector3& angVelA,const btVector3& angVelB); + + void getInfo2Internal(btConstraintInfo2* info,const btTransform& transA,const btTransform& transB,const btVector3& angVelA,const btVector3& angVelB); + + virtual void solveConstraintObsolete(btSolverBody& bodyA,btSolverBody& bodyB,btScalar timeStep); + + void updateRHS(btScalar timeStep); + + const btRigidBody& getRigidBodyA() const + { + return m_rbA; + } + const btRigidBody& getRigidBodyB() const + { + return m_rbB; + } + + btRigidBody& getRigidBodyA() + { + return m_rbA; + } + + btRigidBody& getRigidBodyB() + { + return m_rbB; + } + + void setAngularOnly(bool angularOnly) + { + m_angularOnly = angularOnly; + } + + void enableAngularMotor(bool enableMotor,btScalar targetVelocity,btScalar maxMotorImpulse) + { + m_enableAngularMotor = enableMotor; + m_motorTargetVelocity = targetVelocity; + m_maxMotorImpulse = maxMotorImpulse; + } + + // extra motor API, including ability to set a target rotation (as opposed to angular velocity) + // note: setMotorTarget sets angular velocity under the hood, so you must call it every tick to + // maintain a given angular target. + void enableMotor(bool enableMotor) { m_enableAngularMotor = enableMotor; } + void setMaxMotorImpulse(btScalar maxMotorImpulse) { m_maxMotorImpulse = maxMotorImpulse; } + void setMotorTarget(const btQuaternion& qAinB, btScalar dt); // qAinB is rotation of body A wrt body B. + void setMotorTarget(btScalar targetAngle, btScalar dt); + + + void setLimit(btScalar low,btScalar high,btScalar _softness = 0.9f, btScalar _biasFactor = 0.3f, btScalar _relaxationFactor = 1.0f) + { + m_lowerLimit = btNormalizeAngle(low); + m_upperLimit = btNormalizeAngle(high); + + m_limitSoftness = _softness; + m_biasFactor = _biasFactor; + m_relaxationFactor = _relaxationFactor; + + } + + void setAxis(btVector3& axisInA) + { + btVector3 rbAxisA1, rbAxisA2; + btPlaneSpace1(axisInA, rbAxisA1, rbAxisA2); + btVector3 pivotInA = m_rbAFrame.getOrigin(); +// m_rbAFrame.getOrigin() = pivotInA; + m_rbAFrame.getBasis().setValue( rbAxisA1.getX(),rbAxisA2.getX(),axisInA.getX(), + rbAxisA1.getY(),rbAxisA2.getY(),axisInA.getY(), + rbAxisA1.getZ(),rbAxisA2.getZ(),axisInA.getZ() ); + + btVector3 axisInB = m_rbA.getCenterOfMassTransform().getBasis() * axisInA; + + btQuaternion rotationArc = shortestArcQuat(axisInA,axisInB); + btVector3 rbAxisB1 = quatRotate(rotationArc,rbAxisA1); + btVector3 rbAxisB2 = axisInB.cross(rbAxisB1); + + + m_rbBFrame.getOrigin() = m_rbA.getCenterOfMassTransform()(pivotInA); + m_rbBFrame.getBasis().setValue( rbAxisB1.getX(),rbAxisB2.getX(),axisInB.getX(), + rbAxisB1.getY(),rbAxisB2.getY(),axisInB.getY(), + rbAxisB1.getZ(),rbAxisB2.getZ(),axisInB.getZ() ); + } + + btScalar getLowerLimit() const + { + return m_lowerLimit; + } + + btScalar getUpperLimit() const + { + return m_upperLimit; + } + + + btScalar getHingeAngle(); + + btScalar getHingeAngle(const btTransform& transA,const btTransform& transB); + + void testLimit(const btTransform& transA,const btTransform& transB); + + + const btTransform& getAFrame() const { return m_rbAFrame; }; + const btTransform& getBFrame() const { return m_rbBFrame; }; + + btTransform& getAFrame() { return m_rbAFrame; }; + btTransform& getBFrame() { return m_rbBFrame; }; + + inline int getSolveLimit() + { + return m_solveLimit; + } + + inline btScalar getLimitSign() + { + return m_limitSign; + } + + inline bool getAngularOnly() + { + return m_angularOnly; + } + inline bool getEnableAngularMotor() + { + return m_enableAngularMotor; + } + inline btScalar getMotorTargetVelosity() + { + return m_motorTargetVelocity; + } + inline btScalar getMaxMotorImpulse() + { + return m_maxMotorImpulse; + } + +}; + +#endif //HINGECONSTRAINT_H diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btJacobianEntry.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btJacobianEntry.h new file mode 100644 index 000000000..22a8af66b --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btJacobianEntry.h @@ -0,0 +1,156 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 JACOBIAN_ENTRY_H +#define JACOBIAN_ENTRY_H + +#include "LinearMath/btVector3.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" + + +//notes: +// Another memory optimization would be to store m_1MinvJt in the remaining 3 w components +// which makes the btJacobianEntry memory layout 16 bytes +// if you only are interested in angular part, just feed massInvA and massInvB zero + +/// Jacobian entry is an abstraction that allows to describe constraints +/// it can be used in combination with a constraint solver +/// Can be used to relate the effect of an impulse to the constraint error +ATTRIBUTE_ALIGNED16(class) btJacobianEntry +{ +public: + btJacobianEntry() {}; + //constraint between two different rigidbodies + btJacobianEntry( + const btMatrix3x3& world2A, + const btMatrix3x3& world2B, + const btVector3& rel_pos1,const btVector3& rel_pos2, + const btVector3& jointAxis, + const btVector3& inertiaInvA, + const btScalar massInvA, + const btVector3& inertiaInvB, + const btScalar massInvB) + :m_linearJointAxis(jointAxis) + { + m_aJ = world2A*(rel_pos1.cross(m_linearJointAxis)); + m_bJ = world2B*(rel_pos2.cross(-m_linearJointAxis)); + m_0MinvJt = inertiaInvA * m_aJ; + m_1MinvJt = inertiaInvB * m_bJ; + m_Adiag = massInvA + m_0MinvJt.dot(m_aJ) + massInvB + m_1MinvJt.dot(m_bJ); + + btAssert(m_Adiag > btScalar(0.0)); + } + + //angular constraint between two different rigidbodies + btJacobianEntry(const btVector3& jointAxis, + const btMatrix3x3& world2A, + const btMatrix3x3& world2B, + const btVector3& inertiaInvA, + const btVector3& inertiaInvB) + :m_linearJointAxis(btVector3(btScalar(0.),btScalar(0.),btScalar(0.))) + { + m_aJ= world2A*jointAxis; + m_bJ = world2B*-jointAxis; + m_0MinvJt = inertiaInvA * m_aJ; + m_1MinvJt = inertiaInvB * m_bJ; + m_Adiag = m_0MinvJt.dot(m_aJ) + m_1MinvJt.dot(m_bJ); + + btAssert(m_Adiag > btScalar(0.0)); + } + + //angular constraint between two different rigidbodies + btJacobianEntry(const btVector3& axisInA, + const btVector3& axisInB, + const btVector3& inertiaInvA, + const btVector3& inertiaInvB) + : m_linearJointAxis(btVector3(btScalar(0.),btScalar(0.),btScalar(0.))) + , m_aJ(axisInA) + , m_bJ(-axisInB) + { + m_0MinvJt = inertiaInvA * m_aJ; + m_1MinvJt = inertiaInvB * m_bJ; + m_Adiag = m_0MinvJt.dot(m_aJ) + m_1MinvJt.dot(m_bJ); + + btAssert(m_Adiag > btScalar(0.0)); + } + + //constraint on one rigidbody + btJacobianEntry( + const btMatrix3x3& world2A, + const btVector3& rel_pos1,const btVector3& rel_pos2, + const btVector3& jointAxis, + const btVector3& inertiaInvA, + const btScalar massInvA) + :m_linearJointAxis(jointAxis) + { + m_aJ= world2A*(rel_pos1.cross(jointAxis)); + m_bJ = world2A*(rel_pos2.cross(-jointAxis)); + m_0MinvJt = inertiaInvA * m_aJ; + m_1MinvJt = btVector3(btScalar(0.),btScalar(0.),btScalar(0.)); + m_Adiag = massInvA + m_0MinvJt.dot(m_aJ); + + btAssert(m_Adiag > btScalar(0.0)); + } + + btScalar getDiagonal() const { return m_Adiag; } + + // for two constraints on the same rigidbody (for example vehicle friction) + btScalar getNonDiagonal(const btJacobianEntry& jacB, const btScalar massInvA) const + { + const btJacobianEntry& jacA = *this; + btScalar lin = massInvA * jacA.m_linearJointAxis.dot(jacB.m_linearJointAxis); + btScalar ang = jacA.m_0MinvJt.dot(jacB.m_aJ); + return lin + ang; + } + + + + // for two constraints on sharing two same rigidbodies (for example two contact points between two rigidbodies) + btScalar getNonDiagonal(const btJacobianEntry& jacB,const btScalar massInvA,const btScalar massInvB) const + { + const btJacobianEntry& jacA = *this; + btVector3 lin = jacA.m_linearJointAxis * jacB.m_linearJointAxis; + btVector3 ang0 = jacA.m_0MinvJt * jacB.m_aJ; + btVector3 ang1 = jacA.m_1MinvJt * jacB.m_bJ; + btVector3 lin0 = massInvA * lin ; + btVector3 lin1 = massInvB * lin; + btVector3 sum = ang0+ang1+lin0+lin1; + return sum[0]+sum[1]+sum[2]; + } + + btScalar getRelativeVelocity(const btVector3& linvelA,const btVector3& angvelA,const btVector3& linvelB,const btVector3& angvelB) + { + btVector3 linrel = linvelA - linvelB; + btVector3 angvela = angvelA * m_aJ; + btVector3 angvelb = angvelB * m_bJ; + linrel *= m_linearJointAxis; + angvela += angvelb; + angvela += linrel; + btScalar rel_vel2 = angvela[0]+angvela[1]+angvela[2]; + return rel_vel2 + SIMD_EPSILON; + } +//private: + + btVector3 m_linearJointAxis; + btVector3 m_aJ; + btVector3 m_bJ; + btVector3 m_0MinvJt; + btVector3 m_1MinvJt; + //Optimization: can be stored in the w/last component of one of the vectors + btScalar m_Adiag; + +}; + +#endif //JACOBIAN_ENTRY_H diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btPoint2PointConstraint.cpp b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btPoint2PointConstraint.cpp new file mode 100644 index 000000000..0c58b907d --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btPoint2PointConstraint.cpp @@ -0,0 +1,242 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btPoint2PointConstraint.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include + + + +btPoint2PointConstraint::btPoint2PointConstraint() +:btTypedConstraint(POINT2POINT_CONSTRAINT_TYPE), +m_useSolveConstraintObsolete(false) +{ +} + +btPoint2PointConstraint::btPoint2PointConstraint(btRigidBody& rbA,btRigidBody& rbB, const btVector3& pivotInA,const btVector3& pivotInB) +:btTypedConstraint(POINT2POINT_CONSTRAINT_TYPE,rbA,rbB),m_pivotInA(pivotInA),m_pivotInB(pivotInB), +m_useSolveConstraintObsolete(false) +{ + +} + + +btPoint2PointConstraint::btPoint2PointConstraint(btRigidBody& rbA,const btVector3& pivotInA) +:btTypedConstraint(POINT2POINT_CONSTRAINT_TYPE,rbA),m_pivotInA(pivotInA),m_pivotInB(rbA.getCenterOfMassTransform()(pivotInA)), +m_useSolveConstraintObsolete(false) +{ + +} + +void btPoint2PointConstraint::buildJacobian() +{ + + ///we need it for both methods + { + m_appliedImpulse = btScalar(0.); + + btVector3 normal(0,0,0); + + for (int i=0;i<3;i++) + { + normal[i] = 1; + new (&m_jac[i]) btJacobianEntry( + m_rbA.getCenterOfMassTransform().getBasis().transpose(), + m_rbB.getCenterOfMassTransform().getBasis().transpose(), + m_rbA.getCenterOfMassTransform()*m_pivotInA - m_rbA.getCenterOfMassPosition(), + m_rbB.getCenterOfMassTransform()*m_pivotInB - m_rbB.getCenterOfMassPosition(), + normal, + m_rbA.getInvInertiaDiagLocal(), + m_rbA.getInvMass(), + m_rbB.getInvInertiaDiagLocal(), + m_rbB.getInvMass()); + normal[i] = 0; + } + } + + +} + +void btPoint2PointConstraint::getInfo1 (btConstraintInfo1* info) +{ + getInfo1NonVirtual(info); +} + +void btPoint2PointConstraint::getInfo1NonVirtual (btConstraintInfo1* info) +{ + if (m_useSolveConstraintObsolete) + { + info->m_numConstraintRows = 0; + info->nub = 0; + } else + { + info->m_numConstraintRows = 3; + info->nub = 3; + } +} + + + + +void btPoint2PointConstraint::getInfo2 (btConstraintInfo2* info) +{ + getInfo2NonVirtual(info, m_rbA.getCenterOfMassTransform(),m_rbB.getCenterOfMassTransform()); +} + +void btPoint2PointConstraint::getInfo2NonVirtual (btConstraintInfo2* info, const btTransform& body0_trans, const btTransform& body1_trans) +{ + btAssert(!m_useSolveConstraintObsolete); + + //retrieve matrices + + // anchor points in global coordinates with respect to body PORs. + + // set jacobian + info->m_J1linearAxis[0] = 1; + info->m_J1linearAxis[info->rowskip+1] = 1; + info->m_J1linearAxis[2*info->rowskip+2] = 1; + + btVector3 a1 = body0_trans.getBasis()*getPivotInA(); + { + btVector3* angular0 = (btVector3*)(info->m_J1angularAxis); + btVector3* angular1 = (btVector3*)(info->m_J1angularAxis+info->rowskip); + btVector3* angular2 = (btVector3*)(info->m_J1angularAxis+2*info->rowskip); + btVector3 a1neg = -a1; + a1neg.getSkewSymmetricMatrix(angular0,angular1,angular2); + } + + /*info->m_J2linearAxis[0] = -1; + info->m_J2linearAxis[s+1] = -1; + info->m_J2linearAxis[2*s+2] = -1; + */ + + btVector3 a2 = body1_trans.getBasis()*getPivotInB(); + + { + btVector3 a2n = -a2; + btVector3* angular0 = (btVector3*)(info->m_J2angularAxis); + btVector3* angular1 = (btVector3*)(info->m_J2angularAxis+info->rowskip); + btVector3* angular2 = (btVector3*)(info->m_J2angularAxis+2*info->rowskip); + a2.getSkewSymmetricMatrix(angular0,angular1,angular2); + } + + + + // set right hand side + btScalar k = info->fps * info->erp; + int j; + + for (j=0; j<3; j++) + { + info->m_constraintError[j*info->rowskip] = k * (a2[j] + body1_trans.getOrigin()[j] - a1[j] - body0_trans.getOrigin()[j]); + //printf("info->m_constraintError[%d]=%f\n",j,info->m_constraintError[j]); + } + + btScalar impulseClamp = m_setting.m_impulseClamp;// + for (j=0; j<3; j++) + { + if (m_setting.m_impulseClamp > 0) + { + info->m_lowerLimit[j*info->rowskip] = -impulseClamp; + info->m_upperLimit[j*info->rowskip] = impulseClamp; + } + } + +} + + +void btPoint2PointConstraint::solveConstraintObsolete(btSolverBody& bodyA,btSolverBody& bodyB,btScalar timeStep) +{ + + if (m_useSolveConstraintObsolete) + { + btVector3 pivotAInW = m_rbA.getCenterOfMassTransform()*m_pivotInA; + btVector3 pivotBInW = m_rbB.getCenterOfMassTransform()*m_pivotInB; + + + btVector3 normal(0,0,0); + + + // btVector3 angvelA = m_rbA.getCenterOfMassTransform().getBasis().transpose() * m_rbA.getAngularVelocity(); + // btVector3 angvelB = m_rbB.getCenterOfMassTransform().getBasis().transpose() * m_rbB.getAngularVelocity(); + + for (int i=0;i<3;i++) + { + normal[i] = 1; + btScalar jacDiagABInv = btScalar(1.) / m_jac[i].getDiagonal(); + + btVector3 rel_pos1 = pivotAInW - m_rbA.getCenterOfMassPosition(); + btVector3 rel_pos2 = pivotBInW - m_rbB.getCenterOfMassPosition(); + //this jacobian entry could be re-used for all iterations + + btVector3 vel1,vel2; + bodyA.getVelocityInLocalPointObsolete(rel_pos1,vel1); + bodyB.getVelocityInLocalPointObsolete(rel_pos2,vel2); + btVector3 vel = vel1 - vel2; + + btScalar rel_vel; + rel_vel = normal.dot(vel); + + /* + //velocity error (first order error) + btScalar rel_vel = m_jac[i].getRelativeVelocity(m_rbA.getLinearVelocity(),angvelA, + m_rbB.getLinearVelocity(),angvelB); + */ + + //positional error (zeroth order error) + btScalar depth = -(pivotAInW - pivotBInW).dot(normal); //this is the error projected on the normal + + btScalar deltaImpulse = depth*m_setting.m_tau/timeStep * jacDiagABInv - m_setting.m_damping * rel_vel * jacDiagABInv; + + btScalar impulseClamp = m_setting.m_impulseClamp; + + const btScalar sum = btScalar(m_appliedImpulse) + deltaImpulse; + if (sum < -impulseClamp) + { + deltaImpulse = -impulseClamp-m_appliedImpulse; + m_appliedImpulse = -impulseClamp; + } + else if (sum > impulseClamp) + { + deltaImpulse = impulseClamp-m_appliedImpulse; + m_appliedImpulse = impulseClamp; + } + else + { + m_appliedImpulse = sum; + } + + + btVector3 impulse_vector = normal * deltaImpulse; + + btVector3 ftorqueAxis1 = rel_pos1.cross(normal); + btVector3 ftorqueAxis2 = rel_pos2.cross(normal); + bodyA.applyImpulse(normal*m_rbA.getInvMass(), m_rbA.getInvInertiaTensorWorld()*ftorqueAxis1,deltaImpulse); + bodyB.applyImpulse(normal*m_rbB.getInvMass(), m_rbB.getInvInertiaTensorWorld()*ftorqueAxis2,-deltaImpulse); + + + normal[i] = 0; + } + } + +} + +void btPoint2PointConstraint::updateRHS(btScalar timeStep) +{ + (void)timeStep; + +} + diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btPoint2PointConstraint.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btPoint2PointConstraint.h new file mode 100644 index 000000000..dcc194068 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btPoint2PointConstraint.h @@ -0,0 +1,101 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 POINT2POINTCONSTRAINT_H +#define POINT2POINTCONSTRAINT_H + +#include "LinearMath/btVector3.h" +#include "btJacobianEntry.h" +#include "btTypedConstraint.h" + +class btRigidBody; + +struct btConstraintSetting +{ + btConstraintSetting() : + m_tau(btScalar(0.3)), + m_damping(btScalar(1.)), + m_impulseClamp(btScalar(0.)) + { + } + btScalar m_tau; + btScalar m_damping; + btScalar m_impulseClamp; +}; + +/// point to point constraint between two rigidbodies each with a pivotpoint that descibes the 'ballsocket' location in local space +ATTRIBUTE_ALIGNED16(class) btPoint2PointConstraint : public btTypedConstraint +{ +#ifdef IN_PARALLELL_SOLVER +public: +#endif + btJacobianEntry m_jac[3]; //3 orthogonal linear constraints + + btVector3 m_pivotInA; + btVector3 m_pivotInB; + + + +public: + + ///for backwards compatibility during the transition to 'getInfo/getInfo2' + bool m_useSolveConstraintObsolete; + + btConstraintSetting m_setting; + + btPoint2PointConstraint(btRigidBody& rbA,btRigidBody& rbB, const btVector3& pivotInA,const btVector3& pivotInB); + + btPoint2PointConstraint(btRigidBody& rbA,const btVector3& pivotInA); + + btPoint2PointConstraint(); + + virtual void buildJacobian(); + + virtual void getInfo1 (btConstraintInfo1* info); + + void getInfo1NonVirtual (btConstraintInfo1* info); + + virtual void getInfo2 (btConstraintInfo2* info); + + void getInfo2NonVirtual (btConstraintInfo2* info, const btTransform& body0_trans, const btTransform& body1_trans); + + virtual void solveConstraintObsolete(btSolverBody& bodyA,btSolverBody& bodyB,btScalar timeStep); + + void updateRHS(btScalar timeStep); + + void setPivotA(const btVector3& pivotA) + { + m_pivotInA = pivotA; + } + + void setPivotB(const btVector3& pivotB) + { + m_pivotInB = pivotB; + } + + const btVector3& getPivotInA() const + { + return m_pivotInA; + } + + const btVector3& getPivotInB() const + { + return m_pivotInB; + } + + +}; + +#endif //POINT2POINTCONSTRAINT_H diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp new file mode 100644 index 000000000..560981d52 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp @@ -0,0 +1,1147 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 COMPUTE_IMPULSE_DENOM 1 +//It is not necessary (redundant) to refresh contact manifolds, this refresh has been moved to the collision algorithms. + +#include "btSequentialImpulseConstraintSolver.h" +#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include "btContactConstraint.h" +#include "btSolve2LinearConstraint.h" +#include "btContactSolverInfo.h" +#include "LinearMath/btIDebugDraw.h" +#include "btJacobianEntry.h" +#include "LinearMath/btMinMax.h" +#include "BulletDynamics/ConstraintSolver/btTypedConstraint.h" +#include +#include "LinearMath/btStackAlloc.h" +#include "LinearMath/btQuickprof.h" +#include "btSolverBody.h" +#include "btSolverConstraint.h" +#include "LinearMath/btAlignedObjectArray.h" +#include //for memset + +int gNumSplitImpulseRecoveries = 0; + +btSequentialImpulseConstraintSolver::btSequentialImpulseConstraintSolver() +:m_btSeed2(0) +{ + +} + +btSequentialImpulseConstraintSolver::~btSequentialImpulseConstraintSolver() +{ +} + +#ifdef USE_SIMD +#include +#define vec_splat(x, e) _mm_shuffle_ps(x, x, _MM_SHUFFLE(e,e,e,e)) +static inline __m128 _vmathVfDot3( __m128 vec0, __m128 vec1 ) +{ + __m128 result = _mm_mul_ps( vec0, vec1); + return _mm_add_ps( vec_splat( result, 0 ), _mm_add_ps( vec_splat( result, 1 ), vec_splat( result, 2 ) ) ); +} +#endif//USE_SIMD + +// Project Gauss Seidel or the equivalent Sequential Impulse +void btSequentialImpulseConstraintSolver::resolveSingleConstraintRowGenericSIMD(btSolverBody& body1,btSolverBody& body2,const btSolverConstraint& c) +{ +#ifdef USE_SIMD + __m128 cpAppliedImp = _mm_set1_ps(c.m_appliedImpulse); + __m128 lowerLimit1 = _mm_set1_ps(c.m_lowerLimit); + __m128 upperLimit1 = _mm_set1_ps(c.m_upperLimit); + __m128 deltaImpulse = _mm_sub_ps(_mm_set1_ps(c.m_rhs), _mm_mul_ps(_mm_set1_ps(c.m_appliedImpulse),_mm_set1_ps(c.m_cfm))); + __m128 deltaVel1Dotn = _mm_add_ps(_vmathVfDot3(c.m_contactNormal.mVec128,body1.m_deltaLinearVelocity.mVec128), _vmathVfDot3(c.m_relpos1CrossNormal.mVec128,body1.m_deltaAngularVelocity.mVec128)); + __m128 deltaVel2Dotn = _mm_sub_ps(_vmathVfDot3(c.m_relpos2CrossNormal.mVec128,body2.m_deltaAngularVelocity.mVec128),_vmathVfDot3((c.m_contactNormal).mVec128,body2.m_deltaLinearVelocity.mVec128)); + deltaImpulse = _mm_sub_ps(deltaImpulse,_mm_mul_ps(deltaVel1Dotn,_mm_set1_ps(c.m_jacDiagABInv))); + deltaImpulse = _mm_sub_ps(deltaImpulse,_mm_mul_ps(deltaVel2Dotn,_mm_set1_ps(c.m_jacDiagABInv))); + btSimdScalar sum = _mm_add_ps(cpAppliedImp,deltaImpulse); + btSimdScalar resultLowerLess,resultUpperLess; + resultLowerLess = _mm_cmplt_ps(sum,lowerLimit1); + resultUpperLess = _mm_cmplt_ps(sum,upperLimit1); + __m128 lowMinApplied = _mm_sub_ps(lowerLimit1,cpAppliedImp); + deltaImpulse = _mm_or_ps( _mm_and_ps(resultLowerLess, lowMinApplied), _mm_andnot_ps(resultLowerLess, deltaImpulse) ); + c.m_appliedImpulse = _mm_or_ps( _mm_and_ps(resultLowerLess, lowerLimit1), _mm_andnot_ps(resultLowerLess, sum) ); + __m128 upperMinApplied = _mm_sub_ps(upperLimit1,cpAppliedImp); + deltaImpulse = _mm_or_ps( _mm_and_ps(resultUpperLess, deltaImpulse), _mm_andnot_ps(resultUpperLess, upperMinApplied) ); + c.m_appliedImpulse = _mm_or_ps( _mm_and_ps(resultUpperLess, c.m_appliedImpulse), _mm_andnot_ps(resultUpperLess, upperLimit1) ); + __m128 linearComponentA = _mm_mul_ps(c.m_contactNormal.mVec128,body1.m_invMass.mVec128); + __m128 linearComponentB = _mm_mul_ps((c.m_contactNormal).mVec128,body2.m_invMass.mVec128); + __m128 impulseMagnitude = deltaImpulse; + body1.m_deltaLinearVelocity.mVec128 = _mm_add_ps(body1.m_deltaLinearVelocity.mVec128,_mm_mul_ps(linearComponentA,impulseMagnitude)); + body1.m_deltaAngularVelocity.mVec128 = _mm_add_ps(body1.m_deltaAngularVelocity.mVec128 ,_mm_mul_ps(c.m_angularComponentA.mVec128,impulseMagnitude)); + body2.m_deltaLinearVelocity.mVec128 = _mm_sub_ps(body2.m_deltaLinearVelocity.mVec128,_mm_mul_ps(linearComponentB,impulseMagnitude)); + body2.m_deltaAngularVelocity.mVec128 = _mm_add_ps(body2.m_deltaAngularVelocity.mVec128 ,_mm_mul_ps(c.m_angularComponentB.mVec128,impulseMagnitude)); +#else + resolveSingleConstraintRowGeneric(body1,body2,c); +#endif +} + +// Project Gauss Seidel or the equivalent Sequential Impulse + void btSequentialImpulseConstraintSolver::resolveSingleConstraintRowGeneric(btSolverBody& body1,btSolverBody& body2,const btSolverConstraint& c) +{ + btScalar deltaImpulse = c.m_rhs-btScalar(c.m_appliedImpulse)*c.m_cfm; + const btScalar deltaVel1Dotn = c.m_contactNormal.dot(body1.m_deltaLinearVelocity) + c.m_relpos1CrossNormal.dot(body1.m_deltaAngularVelocity); + const btScalar deltaVel2Dotn = -c.m_contactNormal.dot(body2.m_deltaLinearVelocity) + c.m_relpos2CrossNormal.dot(body2.m_deltaAngularVelocity); + +// const btScalar delta_rel_vel = deltaVel1Dotn-deltaVel2Dotn; + deltaImpulse -= deltaVel1Dotn*c.m_jacDiagABInv; + deltaImpulse -= deltaVel2Dotn*c.m_jacDiagABInv; + + const btScalar sum = btScalar(c.m_appliedImpulse) + deltaImpulse; + if (sum < c.m_lowerLimit) + { + deltaImpulse = c.m_lowerLimit-c.m_appliedImpulse; + c.m_appliedImpulse = c.m_lowerLimit; + } + else if (sum > c.m_upperLimit) + { + deltaImpulse = c.m_upperLimit-c.m_appliedImpulse; + c.m_appliedImpulse = c.m_upperLimit; + } + else + { + c.m_appliedImpulse = sum; + } + body1.applyImpulse(c.m_contactNormal*body1.m_invMass,c.m_angularComponentA,deltaImpulse); + body2.applyImpulse(-c.m_contactNormal*body2.m_invMass,c.m_angularComponentB,deltaImpulse); +} + + void btSequentialImpulseConstraintSolver::resolveSingleConstraintRowLowerLimitSIMD(btSolverBody& body1,btSolverBody& body2,const btSolverConstraint& c) +{ +#ifdef USE_SIMD + __m128 cpAppliedImp = _mm_set1_ps(c.m_appliedImpulse); + __m128 lowerLimit1 = _mm_set1_ps(c.m_lowerLimit); + __m128 upperLimit1 = _mm_set1_ps(c.m_upperLimit); + __m128 deltaImpulse = _mm_sub_ps(_mm_set1_ps(c.m_rhs), _mm_mul_ps(_mm_set1_ps(c.m_appliedImpulse),_mm_set1_ps(c.m_cfm))); + __m128 deltaVel1Dotn = _mm_add_ps(_vmathVfDot3(c.m_contactNormal.mVec128,body1.m_deltaLinearVelocity.mVec128), _vmathVfDot3(c.m_relpos1CrossNormal.mVec128,body1.m_deltaAngularVelocity.mVec128)); + __m128 deltaVel2Dotn = _mm_sub_ps(_vmathVfDot3(c.m_relpos2CrossNormal.mVec128,body2.m_deltaAngularVelocity.mVec128),_vmathVfDot3((c.m_contactNormal).mVec128,body2.m_deltaLinearVelocity.mVec128)); + deltaImpulse = _mm_sub_ps(deltaImpulse,_mm_mul_ps(deltaVel1Dotn,_mm_set1_ps(c.m_jacDiagABInv))); + deltaImpulse = _mm_sub_ps(deltaImpulse,_mm_mul_ps(deltaVel2Dotn,_mm_set1_ps(c.m_jacDiagABInv))); + btSimdScalar sum = _mm_add_ps(cpAppliedImp,deltaImpulse); + btSimdScalar resultLowerLess,resultUpperLess; + resultLowerLess = _mm_cmplt_ps(sum,lowerLimit1); + resultUpperLess = _mm_cmplt_ps(sum,upperLimit1); + __m128 lowMinApplied = _mm_sub_ps(lowerLimit1,cpAppliedImp); + deltaImpulse = _mm_or_ps( _mm_and_ps(resultLowerLess, lowMinApplied), _mm_andnot_ps(resultLowerLess, deltaImpulse) ); + c.m_appliedImpulse = _mm_or_ps( _mm_and_ps(resultLowerLess, lowerLimit1), _mm_andnot_ps(resultLowerLess, sum) ); + __m128 linearComponentA = _mm_mul_ps(c.m_contactNormal.mVec128,body1.m_invMass.mVec128); + __m128 linearComponentB = _mm_mul_ps((c.m_contactNormal).mVec128,body2.m_invMass.mVec128); + __m128 impulseMagnitude = deltaImpulse; + body1.m_deltaLinearVelocity.mVec128 = _mm_add_ps(body1.m_deltaLinearVelocity.mVec128,_mm_mul_ps(linearComponentA,impulseMagnitude)); + body1.m_deltaAngularVelocity.mVec128 = _mm_add_ps(body1.m_deltaAngularVelocity.mVec128 ,_mm_mul_ps(c.m_angularComponentA.mVec128,impulseMagnitude)); + body2.m_deltaLinearVelocity.mVec128 = _mm_sub_ps(body2.m_deltaLinearVelocity.mVec128,_mm_mul_ps(linearComponentB,impulseMagnitude)); + body2.m_deltaAngularVelocity.mVec128 = _mm_add_ps(body2.m_deltaAngularVelocity.mVec128 ,_mm_mul_ps(c.m_angularComponentB.mVec128,impulseMagnitude)); +#else + resolveSingleConstraintRowLowerLimit(body1,body2,c); +#endif +} + +// Project Gauss Seidel or the equivalent Sequential Impulse + void btSequentialImpulseConstraintSolver::resolveSingleConstraintRowLowerLimit(btSolverBody& body1,btSolverBody& body2,const btSolverConstraint& c) +{ + btScalar deltaImpulse = c.m_rhs-btScalar(c.m_appliedImpulse)*c.m_cfm; + const btScalar deltaVel1Dotn = c.m_contactNormal.dot(body1.m_deltaLinearVelocity) + c.m_relpos1CrossNormal.dot(body1.m_deltaAngularVelocity); + const btScalar deltaVel2Dotn = -c.m_contactNormal.dot(body2.m_deltaLinearVelocity) + c.m_relpos2CrossNormal.dot(body2.m_deltaAngularVelocity); + + deltaImpulse -= deltaVel1Dotn*c.m_jacDiagABInv; + deltaImpulse -= deltaVel2Dotn*c.m_jacDiagABInv; + const btScalar sum = btScalar(c.m_appliedImpulse) + deltaImpulse; + if (sum < c.m_lowerLimit) + { + deltaImpulse = c.m_lowerLimit-c.m_appliedImpulse; + c.m_appliedImpulse = c.m_lowerLimit; + } + else + { + c.m_appliedImpulse = sum; + } + body1.applyImpulse(c.m_contactNormal*body1.m_invMass,c.m_angularComponentA,deltaImpulse); + body2.applyImpulse(-c.m_contactNormal*body2.m_invMass,c.m_angularComponentB,deltaImpulse); +} + + +void btSequentialImpulseConstraintSolver::resolveSplitPenetrationImpulseCacheFriendly( + btSolverBody& body1, + btSolverBody& body2, + const btSolverConstraint& c) +{ + if (c.m_rhsPenetration) + { + gNumSplitImpulseRecoveries++; + btScalar deltaImpulse = c.m_rhsPenetration-btScalar(c.m_appliedPushImpulse)*c.m_cfm; + const btScalar deltaVel1Dotn = c.m_contactNormal.dot(body1.m_pushVelocity) + c.m_relpos1CrossNormal.dot(body1.m_turnVelocity); + const btScalar deltaVel2Dotn = -c.m_contactNormal.dot(body2.m_pushVelocity) + c.m_relpos2CrossNormal.dot(body2.m_turnVelocity); + + deltaImpulse -= deltaVel1Dotn*c.m_jacDiagABInv; + deltaImpulse -= deltaVel2Dotn*c.m_jacDiagABInv; + const btScalar sum = btScalar(c.m_appliedPushImpulse) + deltaImpulse; + if (sum < c.m_lowerLimit) + { + deltaImpulse = c.m_lowerLimit-c.m_appliedPushImpulse; + c.m_appliedPushImpulse = c.m_lowerLimit; + } + else + { + c.m_appliedPushImpulse = sum; + } + body1.internalApplyPushImpulse(c.m_contactNormal*body1.m_invMass,c.m_angularComponentA,deltaImpulse); + body2.internalApplyPushImpulse(-c.m_contactNormal*body2.m_invMass,c.m_angularComponentB,deltaImpulse); + } +} + + void btSequentialImpulseConstraintSolver::resolveSplitPenetrationSIMD(btSolverBody& body1,btSolverBody& body2,const btSolverConstraint& c) +{ +#ifdef USE_SIMD + if (!c.m_rhsPenetration) + return; + + gNumSplitImpulseRecoveries++; + + __m128 cpAppliedImp = _mm_set1_ps(c.m_appliedPushImpulse); + __m128 lowerLimit1 = _mm_set1_ps(c.m_lowerLimit); + __m128 upperLimit1 = _mm_set1_ps(c.m_upperLimit); + __m128 deltaImpulse = _mm_sub_ps(_mm_set1_ps(c.m_rhsPenetration), _mm_mul_ps(_mm_set1_ps(c.m_appliedPushImpulse),_mm_set1_ps(c.m_cfm))); + __m128 deltaVel1Dotn = _mm_add_ps(_vmathVfDot3(c.m_contactNormal.mVec128,body1.m_pushVelocity.mVec128), _vmathVfDot3(c.m_relpos1CrossNormal.mVec128,body1.m_turnVelocity.mVec128)); + __m128 deltaVel2Dotn = _mm_sub_ps(_vmathVfDot3(c.m_relpos2CrossNormal.mVec128,body2.m_turnVelocity.mVec128),_vmathVfDot3((c.m_contactNormal).mVec128,body2.m_pushVelocity.mVec128)); + deltaImpulse = _mm_sub_ps(deltaImpulse,_mm_mul_ps(deltaVel1Dotn,_mm_set1_ps(c.m_jacDiagABInv))); + deltaImpulse = _mm_sub_ps(deltaImpulse,_mm_mul_ps(deltaVel2Dotn,_mm_set1_ps(c.m_jacDiagABInv))); + btSimdScalar sum = _mm_add_ps(cpAppliedImp,deltaImpulse); + btSimdScalar resultLowerLess,resultUpperLess; + resultLowerLess = _mm_cmplt_ps(sum,lowerLimit1); + resultUpperLess = _mm_cmplt_ps(sum,upperLimit1); + __m128 lowMinApplied = _mm_sub_ps(lowerLimit1,cpAppliedImp); + deltaImpulse = _mm_or_ps( _mm_and_ps(resultLowerLess, lowMinApplied), _mm_andnot_ps(resultLowerLess, deltaImpulse) ); + c.m_appliedImpulse = _mm_or_ps( _mm_and_ps(resultLowerLess, lowerLimit1), _mm_andnot_ps(resultLowerLess, sum) ); + __m128 linearComponentA = _mm_mul_ps(c.m_contactNormal.mVec128,body1.m_invMass.mVec128); + __m128 linearComponentB = _mm_mul_ps((c.m_contactNormal).mVec128,body2.m_invMass.mVec128); + __m128 impulseMagnitude = deltaImpulse; + body1.m_pushVelocity.mVec128 = _mm_add_ps(body1.m_pushVelocity.mVec128,_mm_mul_ps(linearComponentA,impulseMagnitude)); + body1.m_turnVelocity.mVec128 = _mm_add_ps(body1.m_turnVelocity.mVec128 ,_mm_mul_ps(c.m_angularComponentA.mVec128,impulseMagnitude)); + body2.m_pushVelocity.mVec128 = _mm_sub_ps(body2.m_pushVelocity.mVec128,_mm_mul_ps(linearComponentB,impulseMagnitude)); + body2.m_turnVelocity.mVec128 = _mm_add_ps(body2.m_turnVelocity.mVec128 ,_mm_mul_ps(c.m_angularComponentB.mVec128,impulseMagnitude)); +#else + resolveSplitPenetrationImpulseCacheFriendly(body1,body2,c); +#endif +} + + + +unsigned long btSequentialImpulseConstraintSolver::btRand2() +{ + m_btSeed2 = (1664525L*m_btSeed2 + 1013904223L) & 0xffffffff; + return m_btSeed2; +} + + + +//See ODE: adam's all-int straightforward(?) dRandInt (0..n-1) +int btSequentialImpulseConstraintSolver::btRandInt2 (int n) +{ + // seems good; xor-fold and modulus + const unsigned long un = static_cast(n); + unsigned long r = btRand2(); + + // note: probably more aggressive than it needs to be -- might be + // able to get away without one or two of the innermost branches. + if (un <= 0x00010000UL) { + r ^= (r >> 16); + if (un <= 0x00000100UL) { + r ^= (r >> 8); + if (un <= 0x00000010UL) { + r ^= (r >> 4); + if (un <= 0x00000004UL) { + r ^= (r >> 2); + if (un <= 0x00000002UL) { + r ^= (r >> 1); + } + } + } + } + } + + return (int) (r % un); +} + + + +void btSequentialImpulseConstraintSolver::initSolverBody(btSolverBody* solverBody, btCollisionObject* collisionObject) +{ + btRigidBody* rb = collisionObject? btRigidBody::upcast(collisionObject) : 0; + + solverBody->m_deltaLinearVelocity.setValue(0.f,0.f,0.f); + solverBody->m_deltaAngularVelocity.setValue(0.f,0.f,0.f); + solverBody->m_pushVelocity.setValue(0.f,0.f,0.f); + solverBody->m_turnVelocity.setValue(0.f,0.f,0.f); + + if (rb) + { + solverBody->m_invMass = btVector3(rb->getInvMass(),rb->getInvMass(),rb->getInvMass())*rb->getLinearFactor(); + solverBody->m_originalBody = rb; + solverBody->m_angularFactor = rb->getAngularFactor(); + } else + { + solverBody->m_invMass.setValue(0,0,0); + solverBody->m_originalBody = 0; + solverBody->m_angularFactor.setValue(1,1,1); + } +} + + + + + +btScalar btSequentialImpulseConstraintSolver::restitutionCurve(btScalar rel_vel, btScalar restitution) +{ + btScalar rest = restitution * -rel_vel; + return rest; +} + + + +void applyAnisotropicFriction(btCollisionObject* colObj,btVector3& frictionDirection); +void applyAnisotropicFriction(btCollisionObject* colObj,btVector3& frictionDirection) +{ + if (colObj && colObj->hasAnisotropicFriction()) + { + // transform to local coordinates + btVector3 loc_lateral = frictionDirection * colObj->getWorldTransform().getBasis(); + const btVector3& friction_scaling = colObj->getAnisotropicFriction(); + //apply anisotropic friction + loc_lateral *= friction_scaling; + // ... and transform it back to global coordinates + frictionDirection = colObj->getWorldTransform().getBasis() * loc_lateral; + } +} + + + +btSolverConstraint& btSequentialImpulseConstraintSolver::addFrictionConstraint(const btVector3& normalAxis,int solverBodyIdA,int solverBodyIdB,int frictionIndex,btManifoldPoint& cp,const btVector3& rel_pos1,const btVector3& rel_pos2,btCollisionObject* colObj0,btCollisionObject* colObj1, btScalar relaxation) +{ + + + btRigidBody* body0=btRigidBody::upcast(colObj0); + btRigidBody* body1=btRigidBody::upcast(colObj1); + + btSolverConstraint& solverConstraint = m_tmpSolverContactFrictionConstraintPool.expand(); + memset(&solverConstraint,0xff,sizeof(btSolverConstraint)); + solverConstraint.m_contactNormal = normalAxis; + + solverConstraint.m_solverBodyIdA = solverBodyIdA; + solverConstraint.m_solverBodyIdB = solverBodyIdB; + solverConstraint.m_frictionIndex = frictionIndex; + + solverConstraint.m_friction = cp.m_combinedFriction; + solverConstraint.m_originalContactPoint = 0; + + solverConstraint.m_appliedImpulse = 0.f; + solverConstraint.m_appliedPushImpulse = 0.f; + + { + btVector3 ftorqueAxis1 = rel_pos1.cross(solverConstraint.m_contactNormal); + solverConstraint.m_relpos1CrossNormal = ftorqueAxis1; + solverConstraint.m_angularComponentA = body0 ? body0->getInvInertiaTensorWorld()*ftorqueAxis1*body0->getAngularFactor() : btVector3(0,0,0); + } + { + btVector3 ftorqueAxis1 = rel_pos2.cross(-solverConstraint.m_contactNormal); + solverConstraint.m_relpos2CrossNormal = ftorqueAxis1; + solverConstraint.m_angularComponentB = body1 ? body1->getInvInertiaTensorWorld()*ftorqueAxis1*body1->getAngularFactor() : btVector3(0,0,0); + } + +#ifdef COMPUTE_IMPULSE_DENOM + btScalar denom0 = rb0->computeImpulseDenominator(pos1,solverConstraint.m_contactNormal); + btScalar denom1 = rb1->computeImpulseDenominator(pos2,solverConstraint.m_contactNormal); +#else + btVector3 vec; + btScalar denom0 = 0.f; + btScalar denom1 = 0.f; + if (body0) + { + vec = ( solverConstraint.m_angularComponentA).cross(rel_pos1); + denom0 = body0->getInvMass() + normalAxis.dot(vec); + } + if (body1) + { + vec = ( -solverConstraint.m_angularComponentB).cross(rel_pos2); + denom1 = body1->getInvMass() + normalAxis.dot(vec); + } + + +#endif //COMPUTE_IMPULSE_DENOM + btScalar denom = relaxation/(denom0+denom1); + solverConstraint.m_jacDiagABInv = denom; + +#ifdef _USE_JACOBIAN + solverConstraint.m_jac = btJacobianEntry ( + rel_pos1,rel_pos2,solverConstraint.m_contactNormal, + body0->getInvInertiaDiagLocal(), + body0->getInvMass(), + body1->getInvInertiaDiagLocal(), + body1->getInvMass()); +#endif //_USE_JACOBIAN + + + { + btScalar rel_vel; + btScalar vel1Dotn = solverConstraint.m_contactNormal.dot(body0?body0->getLinearVelocity():btVector3(0,0,0)) + + solverConstraint.m_relpos1CrossNormal.dot(body0?body0->getAngularVelocity():btVector3(0,0,0)); + btScalar vel2Dotn = -solverConstraint.m_contactNormal.dot(body1?body1->getLinearVelocity():btVector3(0,0,0)) + + solverConstraint.m_relpos2CrossNormal.dot(body1?body1->getAngularVelocity():btVector3(0,0,0)); + + rel_vel = vel1Dotn+vel2Dotn; + +// btScalar positionalError = 0.f; + + btSimdScalar velocityError = - rel_vel; + btSimdScalar velocityImpulse = velocityError * btSimdScalar(solverConstraint.m_jacDiagABInv); + solverConstraint.m_rhs = velocityImpulse; + solverConstraint.m_cfm = 0.f; + solverConstraint.m_lowerLimit = 0; + solverConstraint.m_upperLimit = 1e10f; + } + + return solverConstraint; +} + +int btSequentialImpulseConstraintSolver::getOrInitSolverBody(btCollisionObject& body) +{ + int solverBodyIdA = -1; + + if (body.getCompanionId() >= 0) + { + //body has already been converted + solverBodyIdA = body.getCompanionId(); + } else + { + btRigidBody* rb = btRigidBody::upcast(&body); + if (rb && rb->getInvMass()) + { + solverBodyIdA = m_tmpSolverBodyPool.size(); + btSolverBody& solverBody = m_tmpSolverBodyPool.expand(); + initSolverBody(&solverBody,&body); + body.setCompanionId(solverBodyIdA); + } else + { + return 0;//assume first one is a fixed solver body + } + } + return solverBodyIdA; +} +#include + + + +void btSequentialImpulseConstraintSolver::convertContact(btPersistentManifold* manifold,const btContactSolverInfo& infoGlobal) +{ + btCollisionObject* colObj0=0,*colObj1=0; + + colObj0 = (btCollisionObject*)manifold->getBody0(); + colObj1 = (btCollisionObject*)manifold->getBody1(); + + int solverBodyIdA=-1; + int solverBodyIdB=-1; + + if (manifold->getNumContacts()) + { + solverBodyIdA = getOrInitSolverBody(*colObj0); + solverBodyIdB = getOrInitSolverBody(*colObj1); + } + + ///avoid collision response between two static objects + if (!solverBodyIdA && !solverBodyIdB) + return; + + btVector3 rel_pos1; + btVector3 rel_pos2; + btScalar relaxation; + + for (int j=0;jgetNumContacts();j++) + { + + btManifoldPoint& cp = manifold->getContactPoint(j); + + if (cp.getDistance() <= manifold->getContactProcessingThreshold()) + { + + const btVector3& pos1 = cp.getPositionWorldOnA(); + const btVector3& pos2 = cp.getPositionWorldOnB(); + + rel_pos1 = pos1 - colObj0->getWorldTransform().getOrigin(); + rel_pos2 = pos2 - colObj1->getWorldTransform().getOrigin(); + + + relaxation = 1.f; + btScalar rel_vel; + btVector3 vel; + + int frictionIndex = m_tmpSolverContactConstraintPool.size(); + + { + btSolverConstraint& solverConstraint = m_tmpSolverContactConstraintPool.expand(); + btRigidBody* rb0 = btRigidBody::upcast(colObj0); + btRigidBody* rb1 = btRigidBody::upcast(colObj1); + + solverConstraint.m_solverBodyIdA = solverBodyIdA; + solverConstraint.m_solverBodyIdB = solverBodyIdB; + + solverConstraint.m_originalContactPoint = &cp; + + btVector3 torqueAxis0 = rel_pos1.cross(cp.m_normalWorldOnB); + solverConstraint.m_angularComponentA = rb0 ? rb0->getInvInertiaTensorWorld()*torqueAxis0*rb0->getAngularFactor() : btVector3(0,0,0); + btVector3 torqueAxis1 = rel_pos2.cross(cp.m_normalWorldOnB); + solverConstraint.m_angularComponentB = rb1 ? rb1->getInvInertiaTensorWorld()*-torqueAxis1*rb1->getAngularFactor() : btVector3(0,0,0); + { +#ifdef COMPUTE_IMPULSE_DENOM + btScalar denom0 = rb0->computeImpulseDenominator(pos1,cp.m_normalWorldOnB); + btScalar denom1 = rb1->computeImpulseDenominator(pos2,cp.m_normalWorldOnB); +#else + btVector3 vec; + btScalar denom0 = 0.f; + btScalar denom1 = 0.f; + if (rb0) + { + vec = ( solverConstraint.m_angularComponentA).cross(rel_pos1); + denom0 = rb0->getInvMass() + cp.m_normalWorldOnB.dot(vec); + } + if (rb1) + { + vec = ( -solverConstraint.m_angularComponentB).cross(rel_pos2); + denom1 = rb1->getInvMass() + cp.m_normalWorldOnB.dot(vec); + } +#endif //COMPUTE_IMPULSE_DENOM + + btScalar denom = relaxation/(denom0+denom1); + solverConstraint.m_jacDiagABInv = denom; + } + + solverConstraint.m_contactNormal = cp.m_normalWorldOnB; + solverConstraint.m_relpos1CrossNormal = rel_pos1.cross(cp.m_normalWorldOnB); + solverConstraint.m_relpos2CrossNormal = rel_pos2.cross(-cp.m_normalWorldOnB); + + + btVector3 vel1 = rb0 ? rb0->getVelocityInLocalPoint(rel_pos1) : btVector3(0,0,0); + btVector3 vel2 = rb1 ? rb1->getVelocityInLocalPoint(rel_pos2) : btVector3(0,0,0); + + vel = vel1 - vel2; + + rel_vel = cp.m_normalWorldOnB.dot(vel); + + btScalar penetration = cp.getDistance()+infoGlobal.m_linearSlop; + + + solverConstraint.m_friction = cp.m_combinedFriction; + + btScalar restitution = 0.f; + + if (cp.m_lifeTime>infoGlobal.m_restingContactRestitutionThreshold) + { + restitution = 0.f; + } else + { + restitution = restitutionCurve(rel_vel, cp.m_combinedRestitution); + if (restitution <= btScalar(0.)) + { + restitution = 0.f; + }; + } + + + ///warm starting (or zero if disabled) + if (infoGlobal.m_solverMode & SOLVER_USE_WARMSTARTING) + { + solverConstraint.m_appliedImpulse = cp.m_appliedImpulse * infoGlobal.m_warmstartingFactor; + if (rb0) + m_tmpSolverBodyPool[solverConstraint.m_solverBodyIdA].applyImpulse(solverConstraint.m_contactNormal*rb0->getInvMass()*rb0->getLinearFactor(),solverConstraint.m_angularComponentA,solverConstraint.m_appliedImpulse); + if (rb1) + m_tmpSolverBodyPool[solverConstraint.m_solverBodyIdB].applyImpulse(solverConstraint.m_contactNormal*rb1->getInvMass()*rb1->getLinearFactor(),-solverConstraint.m_angularComponentB,-solverConstraint.m_appliedImpulse); + } else + { + solverConstraint.m_appliedImpulse = 0.f; + } + + solverConstraint.m_appliedPushImpulse = 0.f; + + { + btScalar rel_vel; + btScalar vel1Dotn = solverConstraint.m_contactNormal.dot(rb0?rb0->getLinearVelocity():btVector3(0,0,0)) + + solverConstraint.m_relpos1CrossNormal.dot(rb0?rb0->getAngularVelocity():btVector3(0,0,0)); + btScalar vel2Dotn = -solverConstraint.m_contactNormal.dot(rb1?rb1->getLinearVelocity():btVector3(0,0,0)) + + solverConstraint.m_relpos2CrossNormal.dot(rb1?rb1->getAngularVelocity():btVector3(0,0,0)); + + rel_vel = vel1Dotn+vel2Dotn; + + btScalar positionalError = 0.f; + positionalError = -penetration * infoGlobal.m_erp/infoGlobal.m_timeStep; + btScalar velocityError = restitution - rel_vel;// * damping; + btScalar penetrationImpulse = positionalError*solverConstraint.m_jacDiagABInv; + btScalar velocityImpulse = velocityError *solverConstraint.m_jacDiagABInv; + if (!infoGlobal.m_splitImpulse || (penetration > infoGlobal.m_splitImpulsePenetrationThreshold)) + { + //combine position and velocity into rhs + solverConstraint.m_rhs = penetrationImpulse+velocityImpulse; + solverConstraint.m_rhsPenetration = 0.f; + } else + { + //split position and velocity into rhs and m_rhsPenetration + solverConstraint.m_rhs = velocityImpulse; + solverConstraint.m_rhsPenetration = penetrationImpulse; + } + solverConstraint.m_cfm = 0.f; + solverConstraint.m_lowerLimit = 0; + solverConstraint.m_upperLimit = 1e10f; + } + + + /////setup the friction constraints + + + + if (1) + { + solverConstraint.m_frictionIndex = m_tmpSolverContactFrictionConstraintPool.size(); + if (!(infoGlobal.m_solverMode & SOLVER_ENABLE_FRICTION_DIRECTION_CACHING) || !cp.m_lateralFrictionInitialized) + { + cp.m_lateralFrictionDir1 = vel - cp.m_normalWorldOnB * rel_vel; + btScalar lat_rel_vel = cp.m_lateralFrictionDir1.length2(); + if (!(infoGlobal.m_solverMode & SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION) && lat_rel_vel > SIMD_EPSILON) + { + cp.m_lateralFrictionDir1 /= btSqrt(lat_rel_vel); + if((infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS)) + { + cp.m_lateralFrictionDir2 = cp.m_lateralFrictionDir1.cross(cp.m_normalWorldOnB); + cp.m_lateralFrictionDir2.normalize();//?? + applyAnisotropicFriction(colObj0,cp.m_lateralFrictionDir2); + applyAnisotropicFriction(colObj1,cp.m_lateralFrictionDir2); + addFrictionConstraint(cp.m_lateralFrictionDir2,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); + } + + applyAnisotropicFriction(colObj0,cp.m_lateralFrictionDir1); + applyAnisotropicFriction(colObj1,cp.m_lateralFrictionDir1); + addFrictionConstraint(cp.m_lateralFrictionDir1,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); + cp.m_lateralFrictionInitialized = true; + } else + { + //re-calculate friction direction every frame, todo: check if this is really needed + btPlaneSpace1(cp.m_normalWorldOnB,cp.m_lateralFrictionDir1,cp.m_lateralFrictionDir2); + if ((infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS)) + { + applyAnisotropicFriction(colObj0,cp.m_lateralFrictionDir2); + applyAnisotropicFriction(colObj1,cp.m_lateralFrictionDir2); + addFrictionConstraint(cp.m_lateralFrictionDir2,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); + } + + applyAnisotropicFriction(colObj0,cp.m_lateralFrictionDir1); + applyAnisotropicFriction(colObj1,cp.m_lateralFrictionDir1); + addFrictionConstraint(cp.m_lateralFrictionDir1,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); + + cp.m_lateralFrictionInitialized = true; + } + + } else + { + addFrictionConstraint(cp.m_lateralFrictionDir1,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); + if ((infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS)) + addFrictionConstraint(cp.m_lateralFrictionDir2,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); + } + + if (infoGlobal.m_solverMode & SOLVER_USE_FRICTION_WARMSTARTING) + { + { + btSolverConstraint& frictionConstraint1 = m_tmpSolverContactFrictionConstraintPool[solverConstraint.m_frictionIndex]; + if (infoGlobal.m_solverMode & SOLVER_USE_WARMSTARTING) + { + frictionConstraint1.m_appliedImpulse = cp.m_appliedImpulseLateral1 * infoGlobal.m_warmstartingFactor; + if (rb0) + m_tmpSolverBodyPool[solverConstraint.m_solverBodyIdA].applyImpulse(frictionConstraint1.m_contactNormal*rb0->getInvMass()*rb0->getLinearFactor(),frictionConstraint1.m_angularComponentA,frictionConstraint1.m_appliedImpulse); + if (rb1) + m_tmpSolverBodyPool[solverConstraint.m_solverBodyIdB].applyImpulse(frictionConstraint1.m_contactNormal*rb1->getInvMass()*rb1->getLinearFactor(),-frictionConstraint1.m_angularComponentB,-frictionConstraint1.m_appliedImpulse); + } else + { + frictionConstraint1.m_appliedImpulse = 0.f; + } + } + + if ((infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS)) + { + btSolverConstraint& frictionConstraint2 = m_tmpSolverContactFrictionConstraintPool[solverConstraint.m_frictionIndex+1]; + if (infoGlobal.m_solverMode & SOLVER_USE_WARMSTARTING) + { + frictionConstraint2.m_appliedImpulse = cp.m_appliedImpulseLateral2 * infoGlobal.m_warmstartingFactor; + if (rb0) + m_tmpSolverBodyPool[solverConstraint.m_solverBodyIdA].applyImpulse(frictionConstraint2.m_contactNormal*rb0->getInvMass(),frictionConstraint2.m_angularComponentA,frictionConstraint2.m_appliedImpulse); + if (rb1) + m_tmpSolverBodyPool[solverConstraint.m_solverBodyIdB].applyImpulse(frictionConstraint2.m_contactNormal*rb1->getInvMass(),-frictionConstraint2.m_angularComponentB,-frictionConstraint2.m_appliedImpulse); + } else + { + frictionConstraint2.m_appliedImpulse = 0.f; + } + } + } else + { + btSolverConstraint& frictionConstraint1 = m_tmpSolverContactFrictionConstraintPool[solverConstraint.m_frictionIndex]; + frictionConstraint1.m_appliedImpulse = 0.f; + if ((infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS)) + { + btSolverConstraint& frictionConstraint2 = m_tmpSolverContactFrictionConstraintPool[solverConstraint.m_frictionIndex+1]; + frictionConstraint2.m_appliedImpulse = 0.f; + } + } + } + } + + + } + } +} + + +btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlySetup(btCollisionObject** /*bodies */,int /*numBodies */,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer,btStackAlloc* stackAlloc) +{ + BT_PROFILE("solveGroupCacheFriendlySetup"); + (void)stackAlloc; + (void)debugDrawer; + + + if (!(numConstraints + numManifolds)) + { + // printf("empty\n"); + return 0.f; + } + + if (1) + { + int j; + for (j=0;jbuildJacobian(); + } + } + + btSolverBody& fixedBody = m_tmpSolverBodyPool.expand(); + initSolverBody(&fixedBody,0); + + //btRigidBody* rb0=0,*rb1=0; + + //if (1) + { + { + + int totalNumRows = 0; + int i; + + m_tmpConstraintSizesPool.resize(numConstraints); + //calculate the total number of contraint rows + for (i=0;igetInfo1(&info1); + totalNumRows += info1.m_numConstraintRows; + } + m_tmpSolverNonContactConstraintPool.resize(totalNumRows); + + + ///setup the btSolverConstraints + int currentRow = 0; + + for (i=0;igetRigidBodyA(); + btRigidBody& rbB = constraint->getRigidBodyB(); + + int solverBodyIdA = getOrInitSolverBody(rbA); + int solverBodyIdB = getOrInitSolverBody(rbB); + + btSolverBody* bodyAPtr = &m_tmpSolverBodyPool[solverBodyIdA]; + btSolverBody* bodyBPtr = &m_tmpSolverBodyPool[solverBodyIdB]; + + int j; + for ( j=0;jm_deltaLinearVelocity.setValue(0.f,0.f,0.f); + bodyAPtr->m_deltaAngularVelocity.setValue(0.f,0.f,0.f); + bodyBPtr->m_deltaLinearVelocity.setValue(0.f,0.f,0.f); + bodyBPtr->m_deltaAngularVelocity.setValue(0.f,0.f,0.f); + + + + btTypedConstraint::btConstraintInfo2 info2; + info2.fps = 1.f/infoGlobal.m_timeStep; + info2.erp = infoGlobal.m_erp; + info2.m_J1linearAxis = currentConstraintRow->m_contactNormal; + info2.m_J1angularAxis = currentConstraintRow->m_relpos1CrossNormal; + info2.m_J2linearAxis = 0; + info2.m_J2angularAxis = currentConstraintRow->m_relpos2CrossNormal; + info2.rowskip = sizeof(btSolverConstraint)/sizeof(btScalar);//check this + ///the size of btSolverConstraint needs be a multiple of btScalar + btAssert(info2.rowskip*sizeof(btScalar)== sizeof(btSolverConstraint)); + info2.m_constraintError = ¤tConstraintRow->m_rhs; + info2.cfm = ¤tConstraintRow->m_cfm; + info2.m_lowerLimit = ¤tConstraintRow->m_lowerLimit; + info2.m_upperLimit = ¤tConstraintRow->m_upperLimit; + info2.m_numIterations = infoGlobal.m_numIterations; + constraints[i]->getInfo2(&info2); + + ///finalize the constraint setup + for ( j=0;jgetRigidBodyA().getInvInertiaTensorWorld()*ftorqueAxis1*constraint->getRigidBodyA().getAngularFactor(); + } + { + const btVector3& ftorqueAxis2 = solverConstraint.m_relpos2CrossNormal; + solverConstraint.m_angularComponentB = constraint->getRigidBodyB().getInvInertiaTensorWorld()*ftorqueAxis2*constraint->getRigidBodyB().getAngularFactor(); + } + + { + btVector3 iMJlA = solverConstraint.m_contactNormal*rbA.getInvMass(); + btVector3 iMJaA = rbA.getInvInertiaTensorWorld()*solverConstraint.m_relpos1CrossNormal; + btVector3 iMJlB = solverConstraint.m_contactNormal*rbB.getInvMass();//sign of normal? + btVector3 iMJaB = rbB.getInvInertiaTensorWorld()*solverConstraint.m_relpos2CrossNormal; + + btScalar sum = iMJlA.dot(solverConstraint.m_contactNormal); + sum += iMJaA.dot(solverConstraint.m_relpos1CrossNormal); + sum += iMJlB.dot(solverConstraint.m_contactNormal); + sum += iMJaB.dot(solverConstraint.m_relpos2CrossNormal); + + solverConstraint.m_jacDiagABInv = btScalar(1.)/sum; + } + + + ///fix rhs + ///todo: add force/torque accelerators + { + btScalar rel_vel; + btScalar vel1Dotn = solverConstraint.m_contactNormal.dot(rbA.getLinearVelocity()) + solverConstraint.m_relpos1CrossNormal.dot(rbA.getAngularVelocity()); + btScalar vel2Dotn = -solverConstraint.m_contactNormal.dot(rbB.getLinearVelocity()) + solverConstraint.m_relpos2CrossNormal.dot(rbB.getAngularVelocity()); + + rel_vel = vel1Dotn+vel2Dotn; + + btScalar restitution = 0.f; + btScalar positionalError = solverConstraint.m_rhs;//already filled in by getConstraintInfo2 + btScalar velocityError = restitution - rel_vel;// * damping; + btScalar penetrationImpulse = positionalError*solverConstraint.m_jacDiagABInv; + btScalar velocityImpulse = velocityError *solverConstraint.m_jacDiagABInv; + solverConstraint.m_rhs = penetrationImpulse+velocityImpulse; + solverConstraint.m_appliedImpulse = 0.f; + + } + } + } + currentRow+=m_tmpConstraintSizesPool[i].m_numConstraintRows; + } + } + + { + int i; + btPersistentManifold* manifold = 0; +// btCollisionObject* colObj0=0,*colObj1=0; + + + for (i=0;igetRigidBodyA()); + int bodyBid = getOrInitSolverBody(constraints[j]->getRigidBodyB()); + btSolverBody& bodyA = m_tmpSolverBodyPool[bodyAid]; + btSolverBody& bodyB = m_tmpSolverBodyPool[bodyBid]; + constraints[j]->solveConstraintObsolete(bodyA,bodyB,infoGlobal.m_timeStep); + } + + ///solve all contact constraints using SIMD, if available + int numPoolConstraints = m_tmpSolverContactConstraintPool.size(); + for (j=0;jbtScalar(0)) + { + solveManifold.m_lowerLimit = -(solveManifold.m_friction*totalImpulse); + solveManifold.m_upperLimit = solveManifold.m_friction*totalImpulse; + + resolveSingleConstraintRowGenericSIMD(m_tmpSolverBodyPool[solveManifold.m_solverBodyIdA], m_tmpSolverBodyPool[solveManifold.m_solverBodyIdB],solveManifold); + } + } + } else + { + + ///solve all joint constraints + for (j=0;jgetRigidBodyA()); + int bodyBid = getOrInitSolverBody(constraints[j]->getRigidBodyB()); + btSolverBody& bodyA = m_tmpSolverBodyPool[bodyAid]; + btSolverBody& bodyB = m_tmpSolverBodyPool[bodyBid]; + + constraints[j]->solveConstraintObsolete(bodyA,bodyB,infoGlobal.m_timeStep); + } + + ///solve all contact constraints + int numPoolConstraints = m_tmpSolverContactConstraintPool.size(); + for (j=0;jbtScalar(0)) + { + solveManifold.m_lowerLimit = -(solveManifold.m_friction*totalImpulse); + solveManifold.m_upperLimit = solveManifold.m_friction*totalImpulse; + + resolveSingleConstraintRowGeneric(m_tmpSolverBodyPool[solveManifold.m_solverBodyIdA], m_tmpSolverBodyPool[solveManifold.m_solverBodyIdB],solveManifold); + } + } + } + + } + + if (infoGlobal.m_splitImpulse) + { + if (infoGlobal.m_solverMode & SOLVER_SIMD) + { + for ( iteration = 0;iterationm_appliedImpulse = solveManifold.m_appliedImpulse; + if (infoGlobal.m_solverMode & SOLVER_USE_FRICTION_WARMSTARTING) + { + pt->m_appliedImpulseLateral1 = m_tmpSolverContactFrictionConstraintPool[solveManifold.m_frictionIndex].m_appliedImpulse; + pt->m_appliedImpulseLateral2 = m_tmpSolverContactFrictionConstraintPool[solveManifold.m_frictionIndex+1].m_appliedImpulse; + } + + //do a callback here? + } + + if (infoGlobal.m_splitImpulse) + { + for ( i=0;i m_tmpSolverBodyPool; + btConstraintArray m_tmpSolverContactConstraintPool; + btConstraintArray m_tmpSolverNonContactConstraintPool; + btConstraintArray m_tmpSolverContactFrictionConstraintPool; + btAlignedObjectArray m_orderTmpConstraintPool; + btAlignedObjectArray m_orderFrictionConstraintPool; + btAlignedObjectArray m_tmpConstraintSizesPool; + + btSolverConstraint& addFrictionConstraint(const btVector3& normalAxis,int solverBodyIdA,int solverBodyIdB,int frictionIndex,btManifoldPoint& cp,const btVector3& rel_pos1,const btVector3& rel_pos2,btCollisionObject* colObj0,btCollisionObject* colObj1, btScalar relaxation); + + ///m_btSeed2 is used for re-arranging the constraint rows. improves convergence/quality of friction + unsigned long m_btSeed2; + + void initSolverBody(btSolverBody* solverBody, btCollisionObject* collisionObject); + btScalar restitutionCurve(btScalar rel_vel, btScalar restitution); + + void convertContact(btPersistentManifold* manifold,const btContactSolverInfo& infoGlobal); + + + void resolveSplitPenetrationSIMD( + btSolverBody& body1, + btSolverBody& body2, + const btSolverConstraint& contactConstraint); + + void resolveSplitPenetrationImpulseCacheFriendly( + btSolverBody& body1, + btSolverBody& body2, + const btSolverConstraint& contactConstraint); + + //internal method + int getOrInitSolverBody(btCollisionObject& body); + + void resolveSingleConstraintRowGeneric(btSolverBody& body1,btSolverBody& body2,const btSolverConstraint& contactConstraint); + + void resolveSingleConstraintRowGenericSIMD(btSolverBody& body1,btSolverBody& body2,const btSolverConstraint& contactConstraint); + + void resolveSingleConstraintRowLowerLimit(btSolverBody& body1,btSolverBody& body2,const btSolverConstraint& contactConstraint); + + void resolveSingleConstraintRowLowerLimitSIMD(btSolverBody& body1,btSolverBody& body2,const btSolverConstraint& contactConstraint); + +public: + + + btSequentialImpulseConstraintSolver(); + virtual ~btSequentialImpulseConstraintSolver(); + + virtual btScalar solveGroup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifold,int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& info, btIDebugDraw* debugDrawer, btStackAlloc* stackAlloc,btDispatcher* dispatcher); + + btScalar solveGroupCacheFriendlySetup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer,btStackAlloc* stackAlloc); + btScalar solveGroupCacheFriendlyIterations(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer,btStackAlloc* stackAlloc); + + ///clear internal cached data and reset random seed + virtual void reset(); + + unsigned long btRand2(); + + int btRandInt2 (int n); + + void setRandSeed(unsigned long seed) + { + m_btSeed2 = seed; + } + unsigned long getRandSeed() const + { + return m_btSeed2; + } + +}; + +#ifndef BT_PREFER_SIMD +typedef btSequentialImpulseConstraintSolver btSequentialImpulseConstraintSolverPrefered; +#endif + + +#endif //SEQUENTIAL_IMPULSE_CONSTRAINT_SOLVER_H + diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSliderConstraint.cpp b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSliderConstraint.cpp new file mode 100644 index 000000000..aa305d7c1 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSliderConstraint.cpp @@ -0,0 +1,851 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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. +*/ + +/* +Added by Roman Ponomarev (rponom@gmail.com) +April 04, 2008 +*/ + + + +#include "btSliderConstraint.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include "LinearMath/btTransformUtil.h" +#include + + + +void btSliderConstraint::initParams() +{ + m_lowerLinLimit = btScalar(1.0); + m_upperLinLimit = btScalar(-1.0); + m_lowerAngLimit = btScalar(0.); + m_upperAngLimit = btScalar(0.); + m_softnessDirLin = SLIDER_CONSTRAINT_DEF_SOFTNESS; + m_restitutionDirLin = SLIDER_CONSTRAINT_DEF_RESTITUTION; + m_dampingDirLin = btScalar(0.); + m_softnessDirAng = SLIDER_CONSTRAINT_DEF_SOFTNESS; + m_restitutionDirAng = SLIDER_CONSTRAINT_DEF_RESTITUTION; + m_dampingDirAng = btScalar(0.); + m_softnessOrthoLin = SLIDER_CONSTRAINT_DEF_SOFTNESS; + m_restitutionOrthoLin = SLIDER_CONSTRAINT_DEF_RESTITUTION; + m_dampingOrthoLin = SLIDER_CONSTRAINT_DEF_DAMPING; + m_softnessOrthoAng = SLIDER_CONSTRAINT_DEF_SOFTNESS; + m_restitutionOrthoAng = SLIDER_CONSTRAINT_DEF_RESTITUTION; + m_dampingOrthoAng = SLIDER_CONSTRAINT_DEF_DAMPING; + m_softnessLimLin = SLIDER_CONSTRAINT_DEF_SOFTNESS; + m_restitutionLimLin = SLIDER_CONSTRAINT_DEF_RESTITUTION; + m_dampingLimLin = SLIDER_CONSTRAINT_DEF_DAMPING; + m_softnessLimAng = SLIDER_CONSTRAINT_DEF_SOFTNESS; + m_restitutionLimAng = SLIDER_CONSTRAINT_DEF_RESTITUTION; + m_dampingLimAng = SLIDER_CONSTRAINT_DEF_DAMPING; + + m_poweredLinMotor = false; + m_targetLinMotorVelocity = btScalar(0.); + m_maxLinMotorForce = btScalar(0.); + m_accumulatedLinMotorImpulse = btScalar(0.0); + + m_poweredAngMotor = false; + m_targetAngMotorVelocity = btScalar(0.); + m_maxAngMotorForce = btScalar(0.); + m_accumulatedAngMotorImpulse = btScalar(0.0); + +} + + + +btSliderConstraint::btSliderConstraint() + :btTypedConstraint(SLIDER_CONSTRAINT_TYPE), + m_useSolveConstraintObsolete(false), + m_useLinearReferenceFrameA(true) +{ + initParams(); +} + + + +btSliderConstraint::btSliderConstraint(btRigidBody& rbA, btRigidBody& rbB, const btTransform& frameInA, const btTransform& frameInB, bool useLinearReferenceFrameA) + : btTypedConstraint(SLIDER_CONSTRAINT_TYPE, rbA, rbB), + m_useSolveConstraintObsolete(false), + m_frameInA(frameInA), + m_frameInB(frameInB), + m_useLinearReferenceFrameA(useLinearReferenceFrameA) +{ + initParams(); +} + + +static btRigidBody s_fixed(0, 0, 0); +btSliderConstraint::btSliderConstraint(btRigidBody& rbB, const btTransform& frameInB, bool useLinearReferenceFrameB) + : btTypedConstraint(SLIDER_CONSTRAINT_TYPE, s_fixed, rbB), + m_useSolveConstraintObsolete(false), + m_frameInB(frameInB), + m_useLinearReferenceFrameA(useLinearReferenceFrameB) +{ + ///not providing rigidbody B means implicitly using worldspace for body B +// m_frameInA.getOrigin() = m_rbA.getCenterOfMassTransform()(m_frameInA.getOrigin()); + + initParams(); +} + + + +void btSliderConstraint::buildJacobian() +{ + if (!m_useSolveConstraintObsolete) + { + return; + } + if(m_useLinearReferenceFrameA) + { + buildJacobianInt(m_rbA, m_rbB, m_frameInA, m_frameInB); + } + else + { + buildJacobianInt(m_rbB, m_rbA, m_frameInB, m_frameInA); + } +} + + + +void btSliderConstraint::buildJacobianInt(btRigidBody& rbA, btRigidBody& rbB, const btTransform& frameInA, const btTransform& frameInB) +{ +#ifndef __SPU__ + //calculate transforms + m_calculatedTransformA = rbA.getCenterOfMassTransform() * frameInA; + m_calculatedTransformB = rbB.getCenterOfMassTransform() * frameInB; + m_realPivotAInW = m_calculatedTransformA.getOrigin(); + m_realPivotBInW = m_calculatedTransformB.getOrigin(); + m_sliderAxis = m_calculatedTransformA.getBasis().getColumn(0); // along X + m_delta = m_realPivotBInW - m_realPivotAInW; + m_projPivotInW = m_realPivotAInW + m_sliderAxis.dot(m_delta) * m_sliderAxis; + m_relPosA = m_projPivotInW - rbA.getCenterOfMassPosition(); + m_relPosB = m_realPivotBInW - rbB.getCenterOfMassPosition(); + btVector3 normalWorld; + int i; + //linear part + for(i = 0; i < 3; i++) + { + normalWorld = m_calculatedTransformA.getBasis().getColumn(i); + new (&m_jacLin[i]) btJacobianEntry( + rbA.getCenterOfMassTransform().getBasis().transpose(), + rbB.getCenterOfMassTransform().getBasis().transpose(), + m_relPosA, + m_relPosB, + normalWorld, + rbA.getInvInertiaDiagLocal(), + rbA.getInvMass(), + rbB.getInvInertiaDiagLocal(), + rbB.getInvMass() + ); + m_jacLinDiagABInv[i] = btScalar(1.) / m_jacLin[i].getDiagonal(); + m_depth[i] = m_delta.dot(normalWorld); + } + testLinLimits(); + // angular part + for(i = 0; i < 3; i++) + { + normalWorld = m_calculatedTransformA.getBasis().getColumn(i); + new (&m_jacAng[i]) btJacobianEntry( + normalWorld, + rbA.getCenterOfMassTransform().getBasis().transpose(), + rbB.getCenterOfMassTransform().getBasis().transpose(), + rbA.getInvInertiaDiagLocal(), + rbB.getInvInertiaDiagLocal() + ); + } + testAngLimits(); + btVector3 axisA = m_calculatedTransformA.getBasis().getColumn(0); + m_kAngle = btScalar(1.0 )/ (rbA.computeAngularImpulseDenominator(axisA) + rbB.computeAngularImpulseDenominator(axisA)); + // clear accumulator for motors + m_accumulatedLinMotorImpulse = btScalar(0.0); + m_accumulatedAngMotorImpulse = btScalar(0.0); +#endif //__SPU__ +} + + +void btSliderConstraint::getInfo1(btConstraintInfo1* info) +{ + if (m_useSolveConstraintObsolete) + { + info->m_numConstraintRows = 0; + info->nub = 0; + } + else + { + info->m_numConstraintRows = 4; // Fixed 2 linear + 2 angular + info->nub = 2; + //prepare constraint + calculateTransforms(m_rbA.getCenterOfMassTransform(),m_rbB.getCenterOfMassTransform()); + testLinLimits(); + if(getSolveLinLimit() || getPoweredLinMotor()) + { + info->m_numConstraintRows++; // limit 3rd linear as well + info->nub--; + } + testAngLimits(); + if(getSolveAngLimit() || getPoweredAngMotor()) + { + info->m_numConstraintRows++; // limit 3rd angular as well + info->nub--; + } + } +} + +void btSliderConstraint::getInfo1NonVirtual(btConstraintInfo1* info) +{ + + info->m_numConstraintRows = 6; // Fixed 2 linear + 2 angular + 1 limit (even if not used) + info->nub = 0; +} + +void btSliderConstraint::getInfo2(btConstraintInfo2* info) +{ + getInfo2NonVirtual(info,m_rbA.getCenterOfMassTransform(),m_rbB.getCenterOfMassTransform(), m_rbA.getLinearVelocity(),m_rbB.getLinearVelocity(), m_rbA.getInvMass(),m_rbB.getInvMass()); +} + +void btSliderConstraint::getInfo2NonVirtual(btConstraintInfo2* info, const btTransform& transA,const btTransform& transB, const btVector3& linVelA,const btVector3& linVelB, btScalar rbAinvMass,btScalar rbBinvMass ) +{ + //prepare constraint + calculateTransforms(transA,transB); + testLinLimits(); + testAngLimits(); + + const btTransform& trA = getCalculatedTransformA(); + const btTransform& trB = getCalculatedTransformB(); + + btAssert(!m_useSolveConstraintObsolete); + int i, s = info->rowskip; + + btScalar signFact = m_useLinearReferenceFrameA ? btScalar(1.0f) : btScalar(-1.0f); + // make rotations around Y and Z equal + // the slider axis should be the only unconstrained + // rotational axis, the angular velocity of the two bodies perpendicular to + // the slider axis should be equal. thus the constraint equations are + // p*w1 - p*w2 = 0 + // q*w1 - q*w2 = 0 + // where p and q are unit vectors normal to the slider axis, and w1 and w2 + // are the angular velocity vectors of the two bodies. + // get slider axis (X) + btVector3 ax1 = trA.getBasis().getColumn(0); + // get 2 orthos to slider axis (Y, Z) + btVector3 p = trA.getBasis().getColumn(1); + btVector3 q = trA.getBasis().getColumn(2); + // set the two slider rows + info->m_J1angularAxis[0] = p[0]; + info->m_J1angularAxis[1] = p[1]; + info->m_J1angularAxis[2] = p[2]; + info->m_J1angularAxis[s+0] = q[0]; + info->m_J1angularAxis[s+1] = q[1]; + info->m_J1angularAxis[s+2] = q[2]; + + info->m_J2angularAxis[0] = -p[0]; + info->m_J2angularAxis[1] = -p[1]; + info->m_J2angularAxis[2] = -p[2]; + info->m_J2angularAxis[s+0] = -q[0]; + info->m_J2angularAxis[s+1] = -q[1]; + info->m_J2angularAxis[s+2] = -q[2]; + // compute the right hand side of the constraint equation. set relative + // body velocities along p and q to bring the slider back into alignment. + // if ax1,ax2 are the unit length slider axes as computed from body1 and + // body2, we need to rotate both bodies along the axis u = (ax1 x ax2). + // if "theta" is the angle between ax1 and ax2, we need an angular velocity + // along u to cover angle erp*theta in one step : + // |angular_velocity| = angle/time = erp*theta / stepsize + // = (erp*fps) * theta + // angular_velocity = |angular_velocity| * (ax1 x ax2) / |ax1 x ax2| + // = (erp*fps) * theta * (ax1 x ax2) / sin(theta) + // ...as ax1 and ax2 are unit length. if theta is smallish, + // theta ~= sin(theta), so + // angular_velocity = (erp*fps) * (ax1 x ax2) + // ax1 x ax2 is in the plane space of ax1, so we project the angular + // velocity to p and q to find the right hand side. + btScalar k = info->fps * info->erp * getSoftnessOrthoAng(); + btVector3 ax2 = trB.getBasis().getColumn(0); + btVector3 u = ax1.cross(ax2); + info->m_constraintError[0] = k * u.dot(p); + info->m_constraintError[s] = k * u.dot(q); + // pull out pos and R for both bodies. also get the connection + // vector c = pos2-pos1. + // next two rows. we want: vel2 = vel1 + w1 x c ... but this would + // result in three equations, so we project along the planespace vectors + // so that sliding along the slider axis is disregarded. for symmetry we + // also consider rotation around center of mass of two bodies (factA and factB). + btTransform bodyA_trans = transA; + btTransform bodyB_trans = transB; + int s2 = 2 * s, s3 = 3 * s; + btVector3 c; + btScalar miA = rbAinvMass; + btScalar miB = rbBinvMass; + btScalar miS = miA + miB; + btScalar factA, factB; + if(miS > btScalar(0.f)) + { + factA = miB / miS; + } + else + { + factA = btScalar(0.5f); + } + if(factA > 0.99f) factA = 0.99f; + if(factA < 0.01f) factA = 0.01f; + factB = btScalar(1.0f) - factA; + c = bodyB_trans.getOrigin() - bodyA_trans.getOrigin(); + btVector3 tmp = c.cross(p); + for (i=0; i<3; i++) info->m_J1angularAxis[s2+i] = factA*tmp[i]; + for (i=0; i<3; i++) info->m_J2angularAxis[s2+i] = factB*tmp[i]; + tmp = c.cross(q); + for (i=0; i<3; i++) info->m_J1angularAxis[s3+i] = factA*tmp[i]; + for (i=0; i<3; i++) info->m_J2angularAxis[s3+i] = factB*tmp[i]; + + for (i=0; i<3; i++) info->m_J1linearAxis[s2+i] = p[i]; + for (i=0; i<3; i++) info->m_J1linearAxis[s3+i] = q[i]; + // compute two elements of right hand side. we want to align the offset + // point (in body 2's frame) with the center of body 1. + btVector3 ofs; // offset point in global coordinates + ofs = trB.getOrigin() - trA.getOrigin(); + k = info->fps * info->erp * getSoftnessOrthoLin(); + info->m_constraintError[s2] = k * p.dot(ofs); + info->m_constraintError[s3] = k * q.dot(ofs); + int nrow = 3; // last filled row + int srow; + // check linear limits linear + btScalar limit_err = btScalar(0.0); + int limit = 0; + if(getSolveLinLimit()) + { + limit_err = getLinDepth() * signFact; + limit = (limit_err > btScalar(0.0)) ? 2 : 1; + } + int powered = 0; + if(getPoweredLinMotor()) + { + powered = 1; + } + // if the slider has joint limits or motor, add in the extra row + if (limit || powered) + { + nrow++; + srow = nrow * info->rowskip; + info->m_J1linearAxis[srow+0] = ax1[0]; + info->m_J1linearAxis[srow+1] = ax1[1]; + info->m_J1linearAxis[srow+2] = ax1[2]; + // linear torque decoupling step: + // + // we have to be careful that the linear constraint forces (+/- ax1) applied to the two bodies + // do not create a torque couple. in other words, the points that the + // constraint force is applied at must lie along the same ax1 axis. + // a torque couple will result in limited slider-jointed free + // bodies from gaining angular momentum. + // the solution used here is to apply the constraint forces at the center of mass of the two bodies + btVector3 ltd; // Linear Torque Decoupling vector (a torque) +// c = btScalar(0.5) * c; + ltd = c.cross(ax1); + info->m_J1angularAxis[srow+0] = factA*ltd[0]; + info->m_J1angularAxis[srow+1] = factA*ltd[1]; + info->m_J1angularAxis[srow+2] = factA*ltd[2]; + info->m_J2angularAxis[srow+0] = factB*ltd[0]; + info->m_J2angularAxis[srow+1] = factB*ltd[1]; + info->m_J2angularAxis[srow+2] = factB*ltd[2]; + // right-hand part + btScalar lostop = getLowerLinLimit(); + btScalar histop = getUpperLinLimit(); + if(limit && (lostop == histop)) + { // the joint motor is ineffective + powered = 0; + } + info->m_constraintError[srow] = 0.; + info->m_lowerLimit[srow] = 0.; + info->m_upperLimit[srow] = 0.; + if(powered) + { + info->cfm[nrow] = btScalar(0.0); + btScalar tag_vel = getTargetLinMotorVelocity(); + btScalar mot_fact = getMotorFactor(m_linPos, m_lowerLinLimit, m_upperLinLimit, tag_vel, info->fps * info->erp); +// info->m_constraintError[srow] += mot_fact * getTargetLinMotorVelocity(); + info->m_constraintError[srow] -= signFact * mot_fact * getTargetLinMotorVelocity(); + info->m_lowerLimit[srow] += -getMaxLinMotorForce() * info->fps; + info->m_upperLimit[srow] += getMaxLinMotorForce() * info->fps; + } + if(limit) + { + k = info->fps * info->erp; + info->m_constraintError[srow] += k * limit_err; + info->cfm[srow] = btScalar(0.0); // stop_cfm; + if(lostop == histop) + { // limited low and high simultaneously + info->m_lowerLimit[srow] = -SIMD_INFINITY; + info->m_upperLimit[srow] = SIMD_INFINITY; + } + else if(limit == 1) + { // low limit + info->m_lowerLimit[srow] = -SIMD_INFINITY; + info->m_upperLimit[srow] = 0; + } + else + { // high limit + info->m_lowerLimit[srow] = 0; + info->m_upperLimit[srow] = SIMD_INFINITY; + } + // bounce (we'll use slider parameter abs(1.0 - m_dampingLimLin) for that) + btScalar bounce = btFabs(btScalar(1.0) - getDampingLimLin()); + if(bounce > btScalar(0.0)) + { + btScalar vel = linVelA.dot(ax1); + vel -= linVelB.dot(ax1); + vel *= signFact; + // only apply bounce if the velocity is incoming, and if the + // resulting c[] exceeds what we already have. + if(limit == 1) + { // low limit + if(vel < 0) + { + btScalar newc = -bounce * vel; + if (newc > info->m_constraintError[srow]) + { + info->m_constraintError[srow] = newc; + } + } + } + else + { // high limit - all those computations are reversed + if(vel > 0) + { + btScalar newc = -bounce * vel; + if(newc < info->m_constraintError[srow]) + { + info->m_constraintError[srow] = newc; + } + } + } + } + info->m_constraintError[srow] *= getSoftnessLimLin(); + } // if(limit) + } // if linear limit + // check angular limits + limit_err = btScalar(0.0); + limit = 0; + if(getSolveAngLimit()) + { + limit_err = getAngDepth(); + limit = (limit_err > btScalar(0.0)) ? 1 : 2; + } + // if the slider has joint limits, add in the extra row + powered = 0; + if(getPoweredAngMotor()) + { + powered = 1; + } + if(limit || powered) + { + nrow++; + srow = nrow * info->rowskip; + info->m_J1angularAxis[srow+0] = ax1[0]; + info->m_J1angularAxis[srow+1] = ax1[1]; + info->m_J1angularAxis[srow+2] = ax1[2]; + + info->m_J2angularAxis[srow+0] = -ax1[0]; + info->m_J2angularAxis[srow+1] = -ax1[1]; + info->m_J2angularAxis[srow+2] = -ax1[2]; + + btScalar lostop = getLowerAngLimit(); + btScalar histop = getUpperAngLimit(); + if(limit && (lostop == histop)) + { // the joint motor is ineffective + powered = 0; + } + if(powered) + { + info->cfm[srow] = btScalar(0.0); + btScalar mot_fact = getMotorFactor(m_angPos, m_lowerAngLimit, m_upperAngLimit, getTargetAngMotorVelocity(), info->fps * info->erp); + info->m_constraintError[srow] = mot_fact * getTargetAngMotorVelocity(); + info->m_lowerLimit[srow] = -getMaxAngMotorForce() * info->fps; + info->m_upperLimit[srow] = getMaxAngMotorForce() * info->fps; + } + if(limit) + { + k = info->fps * info->erp; + info->m_constraintError[srow] += k * limit_err; + info->cfm[srow] = btScalar(0.0); // stop_cfm; + if(lostop == histop) + { + // limited low and high simultaneously + info->m_lowerLimit[srow] = -SIMD_INFINITY; + info->m_upperLimit[srow] = SIMD_INFINITY; + } + else if(limit == 1) + { // low limit + info->m_lowerLimit[srow] = 0; + info->m_upperLimit[srow] = SIMD_INFINITY; + } + else + { // high limit + info->m_lowerLimit[srow] = -SIMD_INFINITY; + info->m_upperLimit[srow] = 0; + } + // bounce (we'll use slider parameter abs(1.0 - m_dampingLimAng) for that) + btScalar bounce = btFabs(btScalar(1.0) - getDampingLimAng()); + if(bounce > btScalar(0.0)) + { + btScalar vel = m_rbA.getAngularVelocity().dot(ax1); + vel -= m_rbB.getAngularVelocity().dot(ax1); + // only apply bounce if the velocity is incoming, and if the + // resulting c[] exceeds what we already have. + if(limit == 1) + { // low limit + if(vel < 0) + { + btScalar newc = -bounce * vel; + if(newc > info->m_constraintError[srow]) + { + info->m_constraintError[srow] = newc; + } + } + } + else + { // high limit - all those computations are reversed + if(vel > 0) + { + btScalar newc = -bounce * vel; + if(newc < info->m_constraintError[srow]) + { + info->m_constraintError[srow] = newc; + } + } + } + } + info->m_constraintError[srow] *= getSoftnessLimAng(); + } // if(limit) + } // if angular limit or powered +} + + + +void btSliderConstraint::solveConstraintObsolete(btSolverBody& bodyA,btSolverBody& bodyB,btScalar timeStep) +{ + if (m_useSolveConstraintObsolete) + { + m_timeStep = timeStep; + if(m_useLinearReferenceFrameA) + { + solveConstraintInt(m_rbA,bodyA, m_rbB,bodyB); + } + else + { + solveConstraintInt(m_rbB,bodyB, m_rbA,bodyA); + } + } +} + + + +void btSliderConstraint::solveConstraintInt(btRigidBody& rbA, btSolverBody& bodyA,btRigidBody& rbB, btSolverBody& bodyB) +{ +#ifndef __SPU__ + int i; + // linear + btVector3 velA; + bodyA.getVelocityInLocalPointObsolete(m_relPosA,velA); + btVector3 velB; + bodyB.getVelocityInLocalPointObsolete(m_relPosB,velB); + btVector3 vel = velA - velB; + for(i = 0; i < 3; i++) + { + const btVector3& normal = m_jacLin[i].m_linearJointAxis; + btScalar rel_vel = normal.dot(vel); + // calculate positional error + btScalar depth = m_depth[i]; + // get parameters + btScalar softness = (i) ? m_softnessOrthoLin : (m_solveLinLim ? m_softnessLimLin : m_softnessDirLin); + btScalar restitution = (i) ? m_restitutionOrthoLin : (m_solveLinLim ? m_restitutionLimLin : m_restitutionDirLin); + btScalar damping = (i) ? m_dampingOrthoLin : (m_solveLinLim ? m_dampingLimLin : m_dampingDirLin); + // calcutate and apply impulse + btScalar normalImpulse = softness * (restitution * depth / m_timeStep - damping * rel_vel) * m_jacLinDiagABInv[i]; + btVector3 impulse_vector = normal * normalImpulse; + + //rbA.applyImpulse( impulse_vector, m_relPosA); + //rbB.applyImpulse(-impulse_vector, m_relPosB); + { + btVector3 ftorqueAxis1 = m_relPosA.cross(normal); + btVector3 ftorqueAxis2 = m_relPosB.cross(normal); + bodyA.applyImpulse(normal*rbA.getInvMass(), rbA.getInvInertiaTensorWorld()*ftorqueAxis1,normalImpulse); + bodyB.applyImpulse(normal*rbB.getInvMass(), rbB.getInvInertiaTensorWorld()*ftorqueAxis2,-normalImpulse); + } + + + + if(m_poweredLinMotor && (!i)) + { // apply linear motor + if(m_accumulatedLinMotorImpulse < m_maxLinMotorForce) + { + btScalar desiredMotorVel = m_targetLinMotorVelocity; + btScalar motor_relvel = desiredMotorVel + rel_vel; + normalImpulse = -motor_relvel * m_jacLinDiagABInv[i]; + // clamp accumulated impulse + btScalar new_acc = m_accumulatedLinMotorImpulse + btFabs(normalImpulse); + if(new_acc > m_maxLinMotorForce) + { + new_acc = m_maxLinMotorForce; + } + btScalar del = new_acc - m_accumulatedLinMotorImpulse; + if(normalImpulse < btScalar(0.0)) + { + normalImpulse = -del; + } + else + { + normalImpulse = del; + } + m_accumulatedLinMotorImpulse = new_acc; + // apply clamped impulse + impulse_vector = normal * normalImpulse; + //rbA.applyImpulse( impulse_vector, m_relPosA); + //rbB.applyImpulse(-impulse_vector, m_relPosB); + + { + btVector3 ftorqueAxis1 = m_relPosA.cross(normal); + btVector3 ftorqueAxis2 = m_relPosB.cross(normal); + bodyA.applyImpulse(normal*rbA.getInvMass(), rbA.getInvInertiaTensorWorld()*ftorqueAxis1,normalImpulse); + bodyB.applyImpulse(normal*rbB.getInvMass(), rbB.getInvInertiaTensorWorld()*ftorqueAxis2,-normalImpulse); + } + + + + } + } + } + // angular + // get axes in world space + btVector3 axisA = m_calculatedTransformA.getBasis().getColumn(0); + btVector3 axisB = m_calculatedTransformB.getBasis().getColumn(0); + + btVector3 angVelA; + bodyA.getAngularVelocity(angVelA); + btVector3 angVelB; + bodyB.getAngularVelocity(angVelB); + + btVector3 angVelAroundAxisA = axisA * axisA.dot(angVelA); + btVector3 angVelAroundAxisB = axisB * axisB.dot(angVelB); + + btVector3 angAorthog = angVelA - angVelAroundAxisA; + btVector3 angBorthog = angVelB - angVelAroundAxisB; + btVector3 velrelOrthog = angAorthog-angBorthog; + //solve orthogonal angular velocity correction + btScalar len = velrelOrthog.length(); + btScalar orthorImpulseMag = 0.f; + + if (len > btScalar(0.00001)) + { + btVector3 normal = velrelOrthog.normalized(); + btScalar denom = rbA.computeAngularImpulseDenominator(normal) + rbB.computeAngularImpulseDenominator(normal); + //velrelOrthog *= (btScalar(1.)/denom) * m_dampingOrthoAng * m_softnessOrthoAng; + orthorImpulseMag = (btScalar(1.)/denom) * m_dampingOrthoAng * m_softnessOrthoAng; + } + //solve angular positional correction + btVector3 angularError = axisA.cross(axisB) *(btScalar(1.)/m_timeStep); + btVector3 angularAxis = angularError; + btScalar angularImpulseMag = 0; + + btScalar len2 = angularError.length(); + if (len2>btScalar(0.00001)) + { + btVector3 normal2 = angularError.normalized(); + btScalar denom2 = rbA.computeAngularImpulseDenominator(normal2) + rbB.computeAngularImpulseDenominator(normal2); + angularImpulseMag = (btScalar(1.)/denom2) * m_restitutionOrthoAng * m_softnessOrthoAng; + angularError *= angularImpulseMag; + } + // apply impulse + //rbA.applyTorqueImpulse(-velrelOrthog+angularError); + //rbB.applyTorqueImpulse(velrelOrthog-angularError); + + bodyA.applyImpulse(btVector3(0,0,0), rbA.getInvInertiaTensorWorld()*velrelOrthog,-orthorImpulseMag); + bodyB.applyImpulse(btVector3(0,0,0), rbB.getInvInertiaTensorWorld()*velrelOrthog,orthorImpulseMag); + bodyA.applyImpulse(btVector3(0,0,0), rbA.getInvInertiaTensorWorld()*angularAxis,angularImpulseMag); + bodyB.applyImpulse(btVector3(0,0,0), rbB.getInvInertiaTensorWorld()*angularAxis,-angularImpulseMag); + + + btScalar impulseMag; + //solve angular limits + if(m_solveAngLim) + { + impulseMag = (angVelB - angVelA).dot(axisA) * m_dampingLimAng + m_angDepth * m_restitutionLimAng / m_timeStep; + impulseMag *= m_kAngle * m_softnessLimAng; + } + else + { + impulseMag = (angVelB - angVelA).dot(axisA) * m_dampingDirAng + m_angDepth * m_restitutionDirAng / m_timeStep; + impulseMag *= m_kAngle * m_softnessDirAng; + } + btVector3 impulse = axisA * impulseMag; + //rbA.applyTorqueImpulse(impulse); + //rbB.applyTorqueImpulse(-impulse); + + bodyA.applyImpulse(btVector3(0,0,0), rbA.getInvInertiaTensorWorld()*axisA,impulseMag); + bodyB.applyImpulse(btVector3(0,0,0), rbB.getInvInertiaTensorWorld()*axisA,-impulseMag); + + + + //apply angular motor + if(m_poweredAngMotor) + { + if(m_accumulatedAngMotorImpulse < m_maxAngMotorForce) + { + btVector3 velrel = angVelAroundAxisA - angVelAroundAxisB; + btScalar projRelVel = velrel.dot(axisA); + + btScalar desiredMotorVel = m_targetAngMotorVelocity; + btScalar motor_relvel = desiredMotorVel - projRelVel; + + btScalar angImpulse = m_kAngle * motor_relvel; + // clamp accumulated impulse + btScalar new_acc = m_accumulatedAngMotorImpulse + btFabs(angImpulse); + if(new_acc > m_maxAngMotorForce) + { + new_acc = m_maxAngMotorForce; + } + btScalar del = new_acc - m_accumulatedAngMotorImpulse; + if(angImpulse < btScalar(0.0)) + { + angImpulse = -del; + } + else + { + angImpulse = del; + } + m_accumulatedAngMotorImpulse = new_acc; + // apply clamped impulse + btVector3 motorImp = angImpulse * axisA; + //rbA.applyTorqueImpulse(motorImp); + //rbB.applyTorqueImpulse(-motorImp); + + bodyA.applyImpulse(btVector3(0,0,0), rbA.getInvInertiaTensorWorld()*axisA,angImpulse); + bodyB.applyImpulse(btVector3(0,0,0), rbB.getInvInertiaTensorWorld()*axisA,-angImpulse); + } + } +#endif //__SPU__ +} + + + + + +void btSliderConstraint::calculateTransforms(const btTransform& transA,const btTransform& transB) +{ + if(m_useLinearReferenceFrameA || (!m_useSolveConstraintObsolete)) + { + m_calculatedTransformA = transA * m_frameInA; + m_calculatedTransformB = transB * m_frameInB; + } + else + { + m_calculatedTransformA = transB * m_frameInB; + m_calculatedTransformB = transA * m_frameInA; + } + m_realPivotAInW = m_calculatedTransformA.getOrigin(); + m_realPivotBInW = m_calculatedTransformB.getOrigin(); + m_sliderAxis = m_calculatedTransformA.getBasis().getColumn(0); // along X + if(m_useLinearReferenceFrameA || m_useSolveConstraintObsolete) + { + m_delta = m_realPivotBInW - m_realPivotAInW; + } + else + { + m_delta = m_realPivotAInW - m_realPivotBInW; + } + m_projPivotInW = m_realPivotAInW + m_sliderAxis.dot(m_delta) * m_sliderAxis; + btVector3 normalWorld; + int i; + //linear part + for(i = 0; i < 3; i++) + { + normalWorld = m_calculatedTransformA.getBasis().getColumn(i); + m_depth[i] = m_delta.dot(normalWorld); + } +} + + + +void btSliderConstraint::testLinLimits(void) +{ + m_solveLinLim = false; + m_linPos = m_depth[0]; + if(m_lowerLinLimit <= m_upperLinLimit) + { + if(m_depth[0] > m_upperLinLimit) + { + m_depth[0] -= m_upperLinLimit; + m_solveLinLim = true; + } + else if(m_depth[0] < m_lowerLinLimit) + { + m_depth[0] -= m_lowerLinLimit; + m_solveLinLim = true; + } + else + { + m_depth[0] = btScalar(0.); + } + } + else + { + m_depth[0] = btScalar(0.); + } +} + + + +void btSliderConstraint::testAngLimits(void) +{ + m_angDepth = btScalar(0.); + m_solveAngLim = false; + if(m_lowerAngLimit <= m_upperAngLimit) + { + const btVector3 axisA0 = m_calculatedTransformA.getBasis().getColumn(1); + const btVector3 axisA1 = m_calculatedTransformA.getBasis().getColumn(2); + const btVector3 axisB0 = m_calculatedTransformB.getBasis().getColumn(1); + btScalar rot = btAtan2Fast(axisB0.dot(axisA1), axisB0.dot(axisA0)); + rot = btAdjustAngleToLimits(rot, m_lowerAngLimit, m_upperAngLimit); + m_angPos = rot; + if(rot < m_lowerAngLimit) + { + m_angDepth = rot - m_lowerAngLimit; + m_solveAngLim = true; + } + else if(rot > m_upperAngLimit) + { + m_angDepth = rot - m_upperAngLimit; + m_solveAngLim = true; + } + } +} + + + +btVector3 btSliderConstraint::getAncorInA(void) +{ + btVector3 ancorInA; + ancorInA = m_realPivotAInW + (m_lowerLinLimit + m_upperLinLimit) * btScalar(0.5) * m_sliderAxis; + ancorInA = m_rbA.getCenterOfMassTransform().inverse() * ancorInA; + return ancorInA; +} + + + +btVector3 btSliderConstraint::getAncorInB(void) +{ + btVector3 ancorInB; + ancorInB = m_frameInB.getOrigin(); + return ancorInB; +} diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSliderConstraint.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSliderConstraint.h new file mode 100644 index 000000000..57b0ed062 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSliderConstraint.h @@ -0,0 +1,234 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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. +*/ + +/* +Added by Roman Ponomarev (rponom@gmail.com) +April 04, 2008 + +TODO: + - add clamping od accumulated impulse to improve stability + - add conversion for ODE constraint solver +*/ + +#ifndef SLIDER_CONSTRAINT_H +#define SLIDER_CONSTRAINT_H + + + +#include "LinearMath/btVector3.h" +#include "btJacobianEntry.h" +#include "btTypedConstraint.h" + + + +class btRigidBody; + + + +#define SLIDER_CONSTRAINT_DEF_SOFTNESS (btScalar(1.0)) +#define SLIDER_CONSTRAINT_DEF_DAMPING (btScalar(1.0)) +#define SLIDER_CONSTRAINT_DEF_RESTITUTION (btScalar(0.7)) + + + +class btSliderConstraint : public btTypedConstraint +{ +protected: + ///for backwards compatibility during the transition to 'getInfo/getInfo2' + bool m_useSolveConstraintObsolete; + btTransform m_frameInA; + btTransform m_frameInB; + // use frameA fo define limits, if true + bool m_useLinearReferenceFrameA; + // linear limits + btScalar m_lowerLinLimit; + btScalar m_upperLinLimit; + // angular limits + btScalar m_lowerAngLimit; + btScalar m_upperAngLimit; + // softness, restitution and damping for different cases + // DirLin - moving inside linear limits + // LimLin - hitting linear limit + // DirAng - moving inside angular limits + // LimAng - hitting angular limit + // OrthoLin, OrthoAng - against constraint axis + btScalar m_softnessDirLin; + btScalar m_restitutionDirLin; + btScalar m_dampingDirLin; + btScalar m_softnessDirAng; + btScalar m_restitutionDirAng; + btScalar m_dampingDirAng; + btScalar m_softnessLimLin; + btScalar m_restitutionLimLin; + btScalar m_dampingLimLin; + btScalar m_softnessLimAng; + btScalar m_restitutionLimAng; + btScalar m_dampingLimAng; + btScalar m_softnessOrthoLin; + btScalar m_restitutionOrthoLin; + btScalar m_dampingOrthoLin; + btScalar m_softnessOrthoAng; + btScalar m_restitutionOrthoAng; + btScalar m_dampingOrthoAng; + + // for interlal use + bool m_solveLinLim; + bool m_solveAngLim; + + btJacobianEntry m_jacLin[3]; + btScalar m_jacLinDiagABInv[3]; + + btJacobianEntry m_jacAng[3]; + + btScalar m_timeStep; + btTransform m_calculatedTransformA; + btTransform m_calculatedTransformB; + + btVector3 m_sliderAxis; + btVector3 m_realPivotAInW; + btVector3 m_realPivotBInW; + btVector3 m_projPivotInW; + btVector3 m_delta; + btVector3 m_depth; + btVector3 m_relPosA; + btVector3 m_relPosB; + + btScalar m_linPos; + btScalar m_angPos; + + btScalar m_angDepth; + btScalar m_kAngle; + + bool m_poweredLinMotor; + btScalar m_targetLinMotorVelocity; + btScalar m_maxLinMotorForce; + btScalar m_accumulatedLinMotorImpulse; + + bool m_poweredAngMotor; + btScalar m_targetAngMotorVelocity; + btScalar m_maxAngMotorForce; + btScalar m_accumulatedAngMotorImpulse; + + //------------------------ + void initParams(); +public: + // constructors + btSliderConstraint(btRigidBody& rbA, btRigidBody& rbB, const btTransform& frameInA, const btTransform& frameInB ,bool useLinearReferenceFrameA); + btSliderConstraint(btRigidBody& rbB, const btTransform& frameInB, bool useLinearReferenceFrameB); + btSliderConstraint(); + // overrides + virtual void buildJacobian(); + virtual void getInfo1 (btConstraintInfo1* info); + + void getInfo1NonVirtual(btConstraintInfo1* info); + + virtual void getInfo2 (btConstraintInfo2* info); + + void getInfo2NonVirtual(btConstraintInfo2* info, const btTransform& transA, const btTransform& transB,const btVector3& linVelA,const btVector3& linVelB, btScalar rbAinvMass,btScalar rbBinvMass); + + virtual void solveConstraintObsolete(btSolverBody& bodyA,btSolverBody& bodyB,btScalar timeStep); + + + // access + const btRigidBody& getRigidBodyA() const { return m_rbA; } + const btRigidBody& getRigidBodyB() const { return m_rbB; } + const btTransform & getCalculatedTransformA() const { return m_calculatedTransformA; } + const btTransform & getCalculatedTransformB() const { return m_calculatedTransformB; } + const btTransform & getFrameOffsetA() const { return m_frameInA; } + const btTransform & getFrameOffsetB() const { return m_frameInB; } + btTransform & getFrameOffsetA() { return m_frameInA; } + btTransform & getFrameOffsetB() { return m_frameInB; } + btScalar getLowerLinLimit() { return m_lowerLinLimit; } + void setLowerLinLimit(btScalar lowerLimit) { m_lowerLinLimit = lowerLimit; } + btScalar getUpperLinLimit() { return m_upperLinLimit; } + void setUpperLinLimit(btScalar upperLimit) { m_upperLinLimit = upperLimit; } + btScalar getLowerAngLimit() { return m_lowerAngLimit; } + void setLowerAngLimit(btScalar lowerLimit) { m_lowerAngLimit = btNormalizeAngle(lowerLimit); } + btScalar getUpperAngLimit() { return m_upperAngLimit; } + void setUpperAngLimit(btScalar upperLimit) { m_upperAngLimit = btNormalizeAngle(upperLimit); } + bool getUseLinearReferenceFrameA() { return m_useLinearReferenceFrameA; } + btScalar getSoftnessDirLin() { return m_softnessDirLin; } + btScalar getRestitutionDirLin() { return m_restitutionDirLin; } + btScalar getDampingDirLin() { return m_dampingDirLin ; } + btScalar getSoftnessDirAng() { return m_softnessDirAng; } + btScalar getRestitutionDirAng() { return m_restitutionDirAng; } + btScalar getDampingDirAng() { return m_dampingDirAng; } + btScalar getSoftnessLimLin() { return m_softnessLimLin; } + btScalar getRestitutionLimLin() { return m_restitutionLimLin; } + btScalar getDampingLimLin() { return m_dampingLimLin; } + btScalar getSoftnessLimAng() { return m_softnessLimAng; } + btScalar getRestitutionLimAng() { return m_restitutionLimAng; } + btScalar getDampingLimAng() { return m_dampingLimAng; } + btScalar getSoftnessOrthoLin() { return m_softnessOrthoLin; } + btScalar getRestitutionOrthoLin() { return m_restitutionOrthoLin; } + btScalar getDampingOrthoLin() { return m_dampingOrthoLin; } + btScalar getSoftnessOrthoAng() { return m_softnessOrthoAng; } + btScalar getRestitutionOrthoAng() { return m_restitutionOrthoAng; } + btScalar getDampingOrthoAng() { return m_dampingOrthoAng; } + void setSoftnessDirLin(btScalar softnessDirLin) { m_softnessDirLin = softnessDirLin; } + void setRestitutionDirLin(btScalar restitutionDirLin) { m_restitutionDirLin = restitutionDirLin; } + void setDampingDirLin(btScalar dampingDirLin) { m_dampingDirLin = dampingDirLin; } + void setSoftnessDirAng(btScalar softnessDirAng) { m_softnessDirAng = softnessDirAng; } + void setRestitutionDirAng(btScalar restitutionDirAng) { m_restitutionDirAng = restitutionDirAng; } + void setDampingDirAng(btScalar dampingDirAng) { m_dampingDirAng = dampingDirAng; } + void setSoftnessLimLin(btScalar softnessLimLin) { m_softnessLimLin = softnessLimLin; } + void setRestitutionLimLin(btScalar restitutionLimLin) { m_restitutionLimLin = restitutionLimLin; } + void setDampingLimLin(btScalar dampingLimLin) { m_dampingLimLin = dampingLimLin; } + void setSoftnessLimAng(btScalar softnessLimAng) { m_softnessLimAng = softnessLimAng; } + void setRestitutionLimAng(btScalar restitutionLimAng) { m_restitutionLimAng = restitutionLimAng; } + void setDampingLimAng(btScalar dampingLimAng) { m_dampingLimAng = dampingLimAng; } + void setSoftnessOrthoLin(btScalar softnessOrthoLin) { m_softnessOrthoLin = softnessOrthoLin; } + void setRestitutionOrthoLin(btScalar restitutionOrthoLin) { m_restitutionOrthoLin = restitutionOrthoLin; } + void setDampingOrthoLin(btScalar dampingOrthoLin) { m_dampingOrthoLin = dampingOrthoLin; } + void setSoftnessOrthoAng(btScalar softnessOrthoAng) { m_softnessOrthoAng = softnessOrthoAng; } + void setRestitutionOrthoAng(btScalar restitutionOrthoAng) { m_restitutionOrthoAng = restitutionOrthoAng; } + void setDampingOrthoAng(btScalar dampingOrthoAng) { m_dampingOrthoAng = dampingOrthoAng; } + void setPoweredLinMotor(bool onOff) { m_poweredLinMotor = onOff; } + bool getPoweredLinMotor() { return m_poweredLinMotor; } + void setTargetLinMotorVelocity(btScalar targetLinMotorVelocity) { m_targetLinMotorVelocity = targetLinMotorVelocity; } + btScalar getTargetLinMotorVelocity() { return m_targetLinMotorVelocity; } + void setMaxLinMotorForce(btScalar maxLinMotorForce) { m_maxLinMotorForce = maxLinMotorForce; } + btScalar getMaxLinMotorForce() { return m_maxLinMotorForce; } + void setPoweredAngMotor(bool onOff) { m_poweredAngMotor = onOff; } + bool getPoweredAngMotor() { return m_poweredAngMotor; } + void setTargetAngMotorVelocity(btScalar targetAngMotorVelocity) { m_targetAngMotorVelocity = targetAngMotorVelocity; } + btScalar getTargetAngMotorVelocity() { return m_targetAngMotorVelocity; } + void setMaxAngMotorForce(btScalar maxAngMotorForce) { m_maxAngMotorForce = maxAngMotorForce; } + btScalar getMaxAngMotorForce() { return m_maxAngMotorForce; } + btScalar getLinearPos() { return m_linPos; } + + + // access for ODE solver + bool getSolveLinLimit() { return m_solveLinLim; } + btScalar getLinDepth() { return m_depth[0]; } + bool getSolveAngLimit() { return m_solveAngLim; } + btScalar getAngDepth() { return m_angDepth; } + // internal + void buildJacobianInt(btRigidBody& rbA, btRigidBody& rbB, const btTransform& frameInA, const btTransform& frameInB); + void solveConstraintInt(btRigidBody& rbA, btSolverBody& bodyA,btRigidBody& rbB, btSolverBody& bodyB); + // shared code used by ODE solver + void calculateTransforms(const btTransform& transA,const btTransform& transB); + void testLinLimits(); + void testLinLimits2(btConstraintInfo2* info); + void testAngLimits(); + // access for PE Solver + btVector3 getAncorInA(); + btVector3 getAncorInB(); +}; + + + +#endif //SLIDER_CONSTRAINT_H + diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.cpp b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.cpp new file mode 100644 index 000000000..0c7dbd668 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.cpp @@ -0,0 +1,255 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btSolve2LinearConstraint.h" + +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include "LinearMath/btVector3.h" +#include "btJacobianEntry.h" + + +void btSolve2LinearConstraint::resolveUnilateralPairConstraint( + btRigidBody* body1, + btRigidBody* body2, + + const btMatrix3x3& world2A, + const btMatrix3x3& world2B, + + const btVector3& invInertiaADiag, + const btScalar invMassA, + const btVector3& linvelA,const btVector3& angvelA, + const btVector3& rel_posA1, + const btVector3& invInertiaBDiag, + const btScalar invMassB, + const btVector3& linvelB,const btVector3& angvelB, + const btVector3& rel_posA2, + + btScalar depthA, const btVector3& normalA, + const btVector3& rel_posB1,const btVector3& rel_posB2, + btScalar depthB, const btVector3& normalB, + btScalar& imp0,btScalar& imp1) +{ + (void)linvelA; + (void)linvelB; + (void)angvelB; + (void)angvelA; + + + + imp0 = btScalar(0.); + imp1 = btScalar(0.); + + btScalar len = btFabs(normalA.length()) - btScalar(1.); + if (btFabs(len) >= SIMD_EPSILON) + return; + + btAssert(len < SIMD_EPSILON); + + + //this jacobian entry could be re-used for all iterations + btJacobianEntry jacA(world2A,world2B,rel_posA1,rel_posA2,normalA,invInertiaADiag,invMassA, + invInertiaBDiag,invMassB); + btJacobianEntry jacB(world2A,world2B,rel_posB1,rel_posB2,normalB,invInertiaADiag,invMassA, + invInertiaBDiag,invMassB); + + //const btScalar vel0 = jacA.getRelativeVelocity(linvelA,angvelA,linvelB,angvelB); + //const btScalar vel1 = jacB.getRelativeVelocity(linvelA,angvelA,linvelB,angvelB); + + const btScalar vel0 = normalA.dot(body1->getVelocityInLocalPoint(rel_posA1)-body2->getVelocityInLocalPoint(rel_posA1)); + const btScalar vel1 = normalB.dot(body1->getVelocityInLocalPoint(rel_posB1)-body2->getVelocityInLocalPoint(rel_posB1)); + +// btScalar penetrationImpulse = (depth*contactTau*timeCorrection) * massTerm;//jacDiagABInv + btScalar massTerm = btScalar(1.) / (invMassA + invMassB); + + + // calculate rhs (or error) terms + const btScalar dv0 = depthA * m_tau * massTerm - vel0 * m_damping; + const btScalar dv1 = depthB * m_tau * massTerm - vel1 * m_damping; + + + // dC/dv * dv = -C + + // jacobian * impulse = -error + // + + //impulse = jacobianInverse * -error + + // inverting 2x2 symmetric system (offdiagonal are equal!) + // + + + btScalar nonDiag = jacA.getNonDiagonal(jacB,invMassA,invMassB); + btScalar invDet = btScalar(1.0) / (jacA.getDiagonal() * jacB.getDiagonal() - nonDiag * nonDiag ); + + //imp0 = dv0 * jacA.getDiagonal() * invDet + dv1 * -nonDiag * invDet; + //imp1 = dv1 * jacB.getDiagonal() * invDet + dv0 * - nonDiag * invDet; + + imp0 = dv0 * jacA.getDiagonal() * invDet + dv1 * -nonDiag * invDet; + imp1 = dv1 * jacB.getDiagonal() * invDet + dv0 * - nonDiag * invDet; + + //[a b] [d -c] + //[c d] inverse = (1 / determinant) * [-b a] where determinant is (ad - bc) + + //[jA nD] * [imp0] = [dv0] + //[nD jB] [imp1] [dv1] + +} + + + +void btSolve2LinearConstraint::resolveBilateralPairConstraint( + btRigidBody* body1, + btRigidBody* body2, + const btMatrix3x3& world2A, + const btMatrix3x3& world2B, + + const btVector3& invInertiaADiag, + const btScalar invMassA, + const btVector3& linvelA,const btVector3& angvelA, + const btVector3& rel_posA1, + const btVector3& invInertiaBDiag, + const btScalar invMassB, + const btVector3& linvelB,const btVector3& angvelB, + const btVector3& rel_posA2, + + btScalar depthA, const btVector3& normalA, + const btVector3& rel_posB1,const btVector3& rel_posB2, + btScalar depthB, const btVector3& normalB, + btScalar& imp0,btScalar& imp1) +{ + + (void)linvelA; + (void)linvelB; + (void)angvelA; + (void)angvelB; + + + + imp0 = btScalar(0.); + imp1 = btScalar(0.); + + btScalar len = btFabs(normalA.length()) - btScalar(1.); + if (btFabs(len) >= SIMD_EPSILON) + return; + + btAssert(len < SIMD_EPSILON); + + + //this jacobian entry could be re-used for all iterations + btJacobianEntry jacA(world2A,world2B,rel_posA1,rel_posA2,normalA,invInertiaADiag,invMassA, + invInertiaBDiag,invMassB); + btJacobianEntry jacB(world2A,world2B,rel_posB1,rel_posB2,normalB,invInertiaADiag,invMassA, + invInertiaBDiag,invMassB); + + //const btScalar vel0 = jacA.getRelativeVelocity(linvelA,angvelA,linvelB,angvelB); + //const btScalar vel1 = jacB.getRelativeVelocity(linvelA,angvelA,linvelB,angvelB); + + const btScalar vel0 = normalA.dot(body1->getVelocityInLocalPoint(rel_posA1)-body2->getVelocityInLocalPoint(rel_posA1)); + const btScalar vel1 = normalB.dot(body1->getVelocityInLocalPoint(rel_posB1)-body2->getVelocityInLocalPoint(rel_posB1)); + + // calculate rhs (or error) terms + const btScalar dv0 = depthA * m_tau - vel0 * m_damping; + const btScalar dv1 = depthB * m_tau - vel1 * m_damping; + + // dC/dv * dv = -C + + // jacobian * impulse = -error + // + + //impulse = jacobianInverse * -error + + // inverting 2x2 symmetric system (offdiagonal are equal!) + // + + + btScalar nonDiag = jacA.getNonDiagonal(jacB,invMassA,invMassB); + btScalar invDet = btScalar(1.0) / (jacA.getDiagonal() * jacB.getDiagonal() - nonDiag * nonDiag ); + + //imp0 = dv0 * jacA.getDiagonal() * invDet + dv1 * -nonDiag * invDet; + //imp1 = dv1 * jacB.getDiagonal() * invDet + dv0 * - nonDiag * invDet; + + imp0 = dv0 * jacA.getDiagonal() * invDet + dv1 * -nonDiag * invDet; + imp1 = dv1 * jacB.getDiagonal() * invDet + dv0 * - nonDiag * invDet; + + //[a b] [d -c] + //[c d] inverse = (1 / determinant) * [-b a] where determinant is (ad - bc) + + //[jA nD] * [imp0] = [dv0] + //[nD jB] [imp1] [dv1] + + if ( imp0 > btScalar(0.0)) + { + if ( imp1 > btScalar(0.0) ) + { + //both positive + } + else + { + imp1 = btScalar(0.); + + // now imp0>0 imp1<0 + imp0 = dv0 / jacA.getDiagonal(); + if ( imp0 > btScalar(0.0) ) + { + } else + { + imp0 = btScalar(0.); + } + } + } + else + { + imp0 = btScalar(0.); + + imp1 = dv1 / jacB.getDiagonal(); + if ( imp1 <= btScalar(0.0) ) + { + imp1 = btScalar(0.); + // now imp0>0 imp1<0 + imp0 = dv0 / jacA.getDiagonal(); + if ( imp0 > btScalar(0.0) ) + { + } else + { + imp0 = btScalar(0.); + } + } else + { + } + } +} + + +/* +void btSolve2LinearConstraint::resolveAngularConstraint( const btMatrix3x3& invInertiaAWS, + const btScalar invMassA, + const btVector3& linvelA,const btVector3& angvelA, + const btVector3& rel_posA1, + const btMatrix3x3& invInertiaBWS, + const btScalar invMassB, + const btVector3& linvelB,const btVector3& angvelB, + const btVector3& rel_posA2, + + btScalar depthA, const btVector3& normalA, + const btVector3& rel_posB1,const btVector3& rel_posB2, + btScalar depthB, const btVector3& normalB, + btScalar& imp0,btScalar& imp1) +{ + +} +*/ + diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.h new file mode 100644 index 000000000..057d3fac8 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.h @@ -0,0 +1,107 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 SOLVE_2LINEAR_CONSTRAINT_H +#define SOLVE_2LINEAR_CONSTRAINT_H + +#include "LinearMath/btMatrix3x3.h" +#include "LinearMath/btVector3.h" + + +class btRigidBody; + + + +/// constraint class used for lateral tyre friction. +class btSolve2LinearConstraint +{ + btScalar m_tau; + btScalar m_damping; + +public: + + btSolve2LinearConstraint(btScalar tau,btScalar damping) + { + m_tau = tau; + m_damping = damping; + } + // + // solve unilateral constraint (equality, direct method) + // + void resolveUnilateralPairConstraint( + btRigidBody* body0, + btRigidBody* body1, + + const btMatrix3x3& world2A, + const btMatrix3x3& world2B, + + const btVector3& invInertiaADiag, + const btScalar invMassA, + const btVector3& linvelA,const btVector3& angvelA, + const btVector3& rel_posA1, + const btVector3& invInertiaBDiag, + const btScalar invMassB, + const btVector3& linvelB,const btVector3& angvelB, + const btVector3& rel_posA2, + + btScalar depthA, const btVector3& normalA, + const btVector3& rel_posB1,const btVector3& rel_posB2, + btScalar depthB, const btVector3& normalB, + btScalar& imp0,btScalar& imp1); + + + // + // solving 2x2 lcp problem (inequality, direct solution ) + // + void resolveBilateralPairConstraint( + btRigidBody* body0, + btRigidBody* body1, + const btMatrix3x3& world2A, + const btMatrix3x3& world2B, + + const btVector3& invInertiaADiag, + const btScalar invMassA, + const btVector3& linvelA,const btVector3& angvelA, + const btVector3& rel_posA1, + const btVector3& invInertiaBDiag, + const btScalar invMassB, + const btVector3& linvelB,const btVector3& angvelB, + const btVector3& rel_posA2, + + btScalar depthA, const btVector3& normalA, + const btVector3& rel_posB1,const btVector3& rel_posB2, + btScalar depthB, const btVector3& normalB, + btScalar& imp0,btScalar& imp1); + +/* + void resolveAngularConstraint( const btMatrix3x3& invInertiaAWS, + const btScalar invMassA, + const btVector3& linvelA,const btVector3& angvelA, + const btVector3& rel_posA1, + const btMatrix3x3& invInertiaBWS, + const btScalar invMassB, + const btVector3& linvelB,const btVector3& angvelB, + const btVector3& rel_posA2, + + btScalar depthA, const btVector3& normalA, + const btVector3& rel_posB1,const btVector3& rel_posB2, + btScalar depthB, const btVector3& normalB, + btScalar& imp0,btScalar& imp1); + +*/ + +}; + +#endif //SOLVE_2LINEAR_CONSTRAINT_H diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSolverBody.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSolverBody.h new file mode 100644 index 000000000..57a8fd31d --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSolverBody.h @@ -0,0 +1,191 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 BT_SOLVER_BODY_H +#define BT_SOLVER_BODY_H + +class btRigidBody; +#include "LinearMath/btVector3.h" +#include "LinearMath/btMatrix3x3.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include "LinearMath/btAlignedAllocator.h" +#include "LinearMath/btTransformUtil.h" + +///Until we get other contributions, only use SIMD on Windows, when using Visual Studio 2008 or later, and not double precision +#ifdef BT_USE_SSE +#define USE_SIMD 1 +#endif // + + +#ifdef USE_SIMD + +struct btSimdScalar +{ + SIMD_FORCE_INLINE btSimdScalar() + { + + } + + SIMD_FORCE_INLINE btSimdScalar(float fl) + :m_vec128 (_mm_set1_ps(fl)) + { + } + + SIMD_FORCE_INLINE btSimdScalar(__m128 v128) + :m_vec128(v128) + { + } + union + { + __m128 m_vec128; + float m_floats[4]; + int m_ints[4]; + btScalar m_unusedPadding; + }; + SIMD_FORCE_INLINE __m128 get128() + { + return m_vec128; + } + + SIMD_FORCE_INLINE const __m128 get128() const + { + return m_vec128; + } + + SIMD_FORCE_INLINE void set128(__m128 v128) + { + m_vec128 = v128; + } + + SIMD_FORCE_INLINE operator __m128() + { + return m_vec128; + } + SIMD_FORCE_INLINE operator const __m128() const + { + return m_vec128; + } + + SIMD_FORCE_INLINE operator float() const + { + return m_floats[0]; + } + +}; + +///@brief Return the elementwise product of two btSimdScalar +SIMD_FORCE_INLINE btSimdScalar +operator*(const btSimdScalar& v1, const btSimdScalar& v2) +{ + return btSimdScalar(_mm_mul_ps(v1.get128(),v2.get128())); +} + +///@brief Return the elementwise product of two btSimdScalar +SIMD_FORCE_INLINE btSimdScalar +operator+(const btSimdScalar& v1, const btSimdScalar& v2) +{ + return btSimdScalar(_mm_add_ps(v1.get128(),v2.get128())); +} + + +#else +#define btSimdScalar btScalar +#endif + +///The btSolverBody is an internal datastructure for the constraint solver. Only necessary data is packed to increase cache coherence/performance. +ATTRIBUTE_ALIGNED16 (struct) btSolverBody +{ + BT_DECLARE_ALIGNED_ALLOCATOR(); + btVector3 m_deltaLinearVelocity; + btVector3 m_deltaAngularVelocity; + btVector3 m_angularFactor; + btVector3 m_invMass; + btScalar m_friction; + btRigidBody* m_originalBody; + btVector3 m_pushVelocity; + btVector3 m_turnVelocity; + + + SIMD_FORCE_INLINE void getVelocityInLocalPointObsolete(const btVector3& rel_pos, btVector3& velocity ) const + { + if (m_originalBody) + velocity = m_originalBody->getLinearVelocity()+m_deltaLinearVelocity + (m_originalBody->getAngularVelocity()+m_deltaAngularVelocity).cross(rel_pos); + else + velocity.setValue(0,0,0); + } + + SIMD_FORCE_INLINE void getAngularVelocity(btVector3& angVel) const + { + if (m_originalBody) + angVel = m_originalBody->getAngularVelocity()+m_deltaAngularVelocity; + else + angVel.setValue(0,0,0); + } + + + //Optimization for the iterative solver: avoid calculating constant terms involving inertia, normal, relative position + SIMD_FORCE_INLINE void applyImpulse(const btVector3& linearComponent, const btVector3& angularComponent,const btScalar impulseMagnitude) + { + //if (m_invMass) + { + m_deltaLinearVelocity += linearComponent*impulseMagnitude; + m_deltaAngularVelocity += angularComponent*(impulseMagnitude*m_angularFactor); + } + } + + SIMD_FORCE_INLINE void internalApplyPushImpulse(const btVector3& linearComponent, const btVector3& angularComponent,btScalar impulseMagnitude) + { + if (m_originalBody) + { + m_pushVelocity += linearComponent*impulseMagnitude; + m_turnVelocity += angularComponent*(impulseMagnitude*m_angularFactor); + } + } + + void writebackVelocity() + { + if (m_originalBody) + { + m_originalBody->setLinearVelocity(m_originalBody->getLinearVelocity()+ m_deltaLinearVelocity); + m_originalBody->setAngularVelocity(m_originalBody->getAngularVelocity()+m_deltaAngularVelocity); + + //m_originalBody->setCompanionId(-1); + } + } + + + void writebackVelocity(btScalar timeStep) + { + if (m_originalBody) + { + m_originalBody->setLinearVelocity(m_originalBody->getLinearVelocity()+ m_deltaLinearVelocity); + m_originalBody->setAngularVelocity(m_originalBody->getAngularVelocity()+m_deltaAngularVelocity); + + //correct the position/orientation based on push/turn recovery + btTransform newTransform; + btTransformUtil::integrateTransform(m_originalBody->getWorldTransform(),m_pushVelocity,m_turnVelocity,timeStep,newTransform); + m_originalBody->setWorldTransform(newTransform); + + //m_originalBody->setCompanionId(-1); + } + } + + + +}; + +#endif //BT_SOLVER_BODY_H + + diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSolverConstraint.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSolverConstraint.h new file mode 100644 index 000000000..eb1aae1ff --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSolverConstraint.h @@ -0,0 +1,96 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 BT_SOLVER_CONSTRAINT_H +#define BT_SOLVER_CONSTRAINT_H + +class btRigidBody; +#include "LinearMath/btVector3.h" +#include "LinearMath/btMatrix3x3.h" +#include "btJacobianEntry.h" + +//#define NO_FRICTION_TANGENTIALS 1 +#include "btSolverBody.h" + + +///1D constraint along a normal axis between bodyA and bodyB. It can be combined to solve contact and friction constraints. +ATTRIBUTE_ALIGNED16 (struct) btSolverConstraint +{ + BT_DECLARE_ALIGNED_ALLOCATOR(); + + btVector3 m_relpos1CrossNormal; + btVector3 m_contactNormal; + + btVector3 m_relpos2CrossNormal; + //btVector3 m_contactNormal2;//usually m_contactNormal2 == -m_contactNormal + + btVector3 m_angularComponentA; + btVector3 m_angularComponentB; + + mutable btSimdScalar m_appliedPushImpulse; + mutable btSimdScalar m_appliedImpulse; + + + btScalar m_friction; + btScalar m_jacDiagABInv; + union + { + int m_numConsecutiveRowsPerKernel; + btScalar m_unusedPadding0; + }; + + union + { + int m_frictionIndex; + btScalar m_unusedPadding1; + }; + union + { + int m_solverBodyIdA; + btScalar m_unusedPadding2; + }; + union + { + int m_solverBodyIdB; + btScalar m_unusedPadding3; + }; + + union + { + void* m_originalContactPoint; + btScalar m_unusedPadding4; + }; + + btScalar m_rhs; + btScalar m_cfm; + btScalar m_lowerLimit; + btScalar m_upperLimit; + + btScalar m_rhsPenetration; + + enum btSolverConstraintType + { + BT_SOLVER_CONTACT_1D = 0, + BT_SOLVER_FRICTION_1D + }; +}; + +typedef btAlignedObjectArray btConstraintArray; + + +#endif //BT_SOLVER_CONSTRAINT_H + + + diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btTypedConstraint.cpp b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btTypedConstraint.cpp new file mode 100644 index 000000000..235be5871 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btTypedConstraint.cpp @@ -0,0 +1,116 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btTypedConstraint.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" + +static btRigidBody s_fixed(0, 0,0); + +#define DEFAULT_DEBUGDRAW_SIZE btScalar(0.3f) + +btTypedConstraint::btTypedConstraint(btTypedConstraintType type) +:btTypedObject(type), +m_userConstraintType(-1), +m_userConstraintId(-1), +m_needsFeedback(false), +m_rbA(s_fixed), +m_rbB(s_fixed), +m_appliedImpulse(btScalar(0.)), +m_dbgDrawSize(DEFAULT_DEBUGDRAW_SIZE) +{ + s_fixed.setMassProps(btScalar(0.),btVector3(btScalar(0.),btScalar(0.),btScalar(0.))); +} +btTypedConstraint::btTypedConstraint(btTypedConstraintType type, btRigidBody& rbA) +:btTypedObject(type), +m_userConstraintType(-1), +m_userConstraintId(-1), +m_needsFeedback(false), +m_rbA(rbA), +m_rbB(s_fixed), +m_appliedImpulse(btScalar(0.)), +m_dbgDrawSize(DEFAULT_DEBUGDRAW_SIZE) +{ + s_fixed.setMassProps(btScalar(0.),btVector3(btScalar(0.),btScalar(0.),btScalar(0.))); +} + + +btTypedConstraint::btTypedConstraint(btTypedConstraintType type, btRigidBody& rbA,btRigidBody& rbB) +:btTypedObject(type), +m_userConstraintType(-1), +m_userConstraintId(-1), +m_needsFeedback(false), +m_rbA(rbA), +m_rbB(rbB), +m_appliedImpulse(btScalar(0.)), +m_dbgDrawSize(DEFAULT_DEBUGDRAW_SIZE) +{ + s_fixed.setMassProps(btScalar(0.),btVector3(btScalar(0.),btScalar(0.),btScalar(0.))); + +} + + + + +btScalar btTypedConstraint::getMotorFactor(btScalar pos, btScalar lowLim, btScalar uppLim, btScalar vel, btScalar timeFact) +{ + if(lowLim > uppLim) + { + return btScalar(1.0f); + } + else if(lowLim == uppLim) + { + return btScalar(0.0f); + } + btScalar lim_fact = btScalar(1.0f); + btScalar delta_max = vel / timeFact; + if(delta_max < btScalar(0.0f)) + { + if((pos >= lowLim) && (pos < (lowLim - delta_max))) + { + lim_fact = (lowLim - pos) / delta_max; + } + else if(pos < lowLim) + { + lim_fact = btScalar(0.0f); + } + else + { + lim_fact = btScalar(1.0f); + } + } + else if(delta_max > btScalar(0.0f)) + { + if((pos <= uppLim) && (pos > (uppLim - delta_max))) + { + lim_fact = (uppLim - pos) / delta_max; + } + else if(pos > uppLim) + { + lim_fact = btScalar(0.0f); + } + else + { + lim_fact = btScalar(1.0f); + } + } + else + { + lim_fact = btScalar(0.0f); + } + return lim_fact; +} + + diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btTypedConstraint.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btTypedConstraint.h new file mode 100644 index 000000000..430e16285 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btTypedConstraint.h @@ -0,0 +1,271 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 TYPED_CONSTRAINT_H +#define TYPED_CONSTRAINT_H + +class btRigidBody; +#include "LinearMath/btScalar.h" +#include "btSolverConstraint.h" +#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" +struct btSolverBody; + +enum btTypedConstraintType +{ + POINT2POINT_CONSTRAINT_TYPE=MAX_CONTACT_MANIFOLD_TYPE+1, + HINGE_CONSTRAINT_TYPE, + CONETWIST_CONSTRAINT_TYPE, + D6_CONSTRAINT_TYPE, + SLIDER_CONSTRAINT_TYPE, + CONTACT_CONSTRAINT_TYPE +}; + +///TypedConstraint is the baseclass for Bullet constraints and vehicles +class btTypedConstraint : public btTypedObject +{ + int m_userConstraintType; + int m_userConstraintId; + bool m_needsFeedback; + + btTypedConstraint& operator=(btTypedConstraint& other) + { + btAssert(0); + (void) other; + return *this; + } + +protected: + btRigidBody& m_rbA; + btRigidBody& m_rbB; + btScalar m_appliedImpulse; + btScalar m_dbgDrawSize; + + btVector3 m_appliedLinearImpulse; + btVector3 m_appliedAngularImpulseA; + btVector3 m_appliedAngularImpulseB; + +public: + + btTypedConstraint(btTypedConstraintType type); + virtual ~btTypedConstraint() {}; + btTypedConstraint(btTypedConstraintType type, btRigidBody& rbA); + btTypedConstraint(btTypedConstraintType type, btRigidBody& rbA,btRigidBody& rbB); + + struct btConstraintInfo1 { + int m_numConstraintRows,nub; + }; + + struct btConstraintInfo2 { + // integrator parameters: frames per second (1/stepsize), default error + // reduction parameter (0..1). + btScalar fps,erp; + + // for the first and second body, pointers to two (linear and angular) + // n*3 jacobian sub matrices, stored by rows. these matrices will have + // been initialized to 0 on entry. if the second body is zero then the + // J2xx pointers may be 0. + btScalar *m_J1linearAxis,*m_J1angularAxis,*m_J2linearAxis,*m_J2angularAxis; + + // elements to jump from one row to the next in J's + int rowskip; + + // right hand sides of the equation J*v = c + cfm * lambda. cfm is the + // "constraint force mixing" vector. c is set to zero on entry, cfm is + // set to a constant value (typically very small or zero) value on entry. + btScalar *m_constraintError,*cfm; + + // lo and hi limits for variables (set to -/+ infinity on entry). + btScalar *m_lowerLimit,*m_upperLimit; + + // findex vector for variables. see the LCP solver interface for a + // description of what this does. this is set to -1 on entry. + // note that the returned indexes are relative to the first index of + // the constraint. + int *findex; + // number of solver iterations + int m_numIterations; + }; + + ///internal method used by the constraint solver, don't use them directly + virtual void buildJacobian() = 0; + + ///internal method used by the constraint solver, don't use them directly + virtual void setupSolverConstraint(btConstraintArray& ca, int solverBodyA,int solverBodyB, btScalar timeStep) + { + } + + ///internal method used by the constraint solver, don't use them directly + virtual void getInfo1 (btConstraintInfo1* info)=0; + + ///internal method used by the constraint solver, don't use them directly + virtual void getInfo2 (btConstraintInfo2* info)=0; + + ///internal method used by the constraint solver, don't use them directly + void internalSetAppliedImpulse(btScalar appliedImpulse) + { + m_appliedImpulse = appliedImpulse; + } + + ///internal method used by the constraint solver, don't use them directly + virtual void solveConstraintObsolete(btSolverBody& bodyA,btSolverBody& bodyB,btScalar timeStep) = 0; + + ///internal method used by the constraint solver, don't use them directly + btScalar getMotorFactor(btScalar pos, btScalar lowLim, btScalar uppLim, btScalar vel, btScalar timeFact); + + const btRigidBody& getRigidBodyA() const + { + return m_rbA; + } + const btRigidBody& getRigidBodyB() const + { + return m_rbB; + } + + btRigidBody& getRigidBodyA() + { + return m_rbA; + } + btRigidBody& getRigidBodyB() + { + return m_rbB; + } + + int getUserConstraintType() const + { + return m_userConstraintType ; + } + + void setUserConstraintType(int userConstraintType) + { + m_userConstraintType = userConstraintType; + }; + + void setUserConstraintId(int uid) + { + m_userConstraintId = uid; + } + + int getUserConstraintId() const + { + return m_userConstraintId; + } + + int getUid() const + { + return m_userConstraintId; + } + + bool needsFeedback() const + { + return m_needsFeedback; + } + + ///enableFeedback will allow to read the applied linear and angular impulse + ///use getAppliedImpulse, getAppliedLinearImpulse and getAppliedAngularImpulse to read feedback information + void enableFeedback(bool needsFeedback) + { + m_needsFeedback = needsFeedback; + } + + ///getAppliedImpulse is an estimated total applied impulse. + ///This feedback could be used to determine breaking constraints or playing sounds. + btScalar getAppliedImpulse() const + { + btAssert(m_needsFeedback); + return m_appliedImpulse; + } + + const btVector3& getAppliedLinearImpulse() const + { + btAssert(m_needsFeedback); + return m_appliedLinearImpulse; + } + + btVector3& getAppliedLinearImpulse() + { + btAssert(m_needsFeedback); + return m_appliedLinearImpulse; + } + + const btVector3& getAppliedAngularImpulseA() const + { + btAssert(m_needsFeedback); + return m_appliedAngularImpulseA; + } + + btVector3& getAppliedAngularImpulseA() + { + btAssert(m_needsFeedback); + return m_appliedAngularImpulseA; + } + + const btVector3& getAppliedAngularImpulseB() const + { + btAssert(m_needsFeedback); + return m_appliedAngularImpulseB; + } + + btVector3& getAppliedAngularImpulseB() + { + btAssert(m_needsFeedback); + return m_appliedAngularImpulseB; + } + + + + btTypedConstraintType getConstraintType () const + { + return btTypedConstraintType(m_objectType); + } + + void setDbgDrawSize(btScalar dbgDrawSize) + { + m_dbgDrawSize = dbgDrawSize; + } + btScalar getDbgDrawSize() + { + return m_dbgDrawSize; + } + +}; + +// returns angle in range [-SIMD_2_PI, SIMD_2_PI], closest to one of the limits +// all arguments should be normalized angles (i.e. in range [-SIMD_PI, SIMD_PI]) +SIMD_FORCE_INLINE btScalar btAdjustAngleToLimits(btScalar angleInRadians, btScalar angleLowerLimitInRadians, btScalar angleUpperLimitInRadians) +{ + if(angleLowerLimitInRadians >= angleUpperLimitInRadians) + { + return angleInRadians; + } + else if(angleInRadians < angleLowerLimitInRadians) + { + btScalar diffLo = btNormalizeAngle(angleLowerLimitInRadians - angleInRadians); // this is positive + btScalar diffHi = btFabs(btNormalizeAngle(angleUpperLimitInRadians - angleInRadians)); + return (diffLo < diffHi) ? angleInRadians : (angleInRadians + SIMD_2_PI); + } + else if(angleInRadians > angleUpperLimitInRadians) + { + btScalar diffHi = btNormalizeAngle(angleInRadians - angleUpperLimitInRadians); // this is positive + btScalar diffLo = btFabs(btNormalizeAngle(angleInRadians - angleLowerLimitInRadians)); + return (diffLo < diffHi) ? (angleInRadians - SIMD_2_PI) : angleInRadians; + } + else + { + return angleInRadians; + } +} + + +#endif //TYPED_CONSTRAINT_H diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btUniversalConstraint.cpp b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btUniversalConstraint.cpp new file mode 100644 index 000000000..3a4c2afa6 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btUniversalConstraint.cpp @@ -0,0 +1,63 @@ +/* +Bullet Continuous Collision Detection and Physics Library, http://bulletphysics.org +Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + +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 "btUniversalConstraint.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include "LinearMath/btTransformUtil.h" + + + +#define UNIV_EPS btScalar(0.01f) + + +// constructor +// anchor, axis1 and axis2 are in world coordinate system +// axis1 must be orthogonal to axis2 +btUniversalConstraint::btUniversalConstraint(btRigidBody& rbA, btRigidBody& rbB, btVector3& anchor, btVector3& axis1, btVector3& axis2) +: btGeneric6DofConstraint(rbA, rbB, btTransform::getIdentity(), btTransform::getIdentity(), true), + m_anchor(anchor), + m_axis1(axis1), + m_axis2(axis2) +{ + // build frame basis + // 6DOF constraint uses Euler angles and to define limits + // it is assumed that rotational order is : + // Z - first, allowed limits are (-PI,PI); + // new position of Y - second (allowed limits are (-PI/2 + epsilon, PI/2 - epsilon), where epsilon is a small positive number + // used to prevent constraint from instability on poles; + // new position of X, allowed limits are (-PI,PI); + // So to simulate ODE Universal joint we should use parent axis as Z, child axis as Y and limit all other DOFs + // Build the frame in world coordinate system first + btVector3 zAxis = axis1.normalize(); + btVector3 yAxis = axis2.normalize(); + btVector3 xAxis = yAxis.cross(zAxis); // we want right coordinate system + btTransform frameInW; + frameInW.setIdentity(); + frameInW.getBasis().setValue( xAxis[0], yAxis[0], zAxis[0], + xAxis[1], yAxis[1], zAxis[1], + xAxis[2], yAxis[2], zAxis[2]); + frameInW.setOrigin(anchor); + // now get constraint frame in local coordinate systems + m_frameInA = rbA.getCenterOfMassTransform().inverse() * frameInW; + m_frameInB = rbB.getCenterOfMassTransform().inverse() * frameInW; + // sei limits + setLinearLowerLimit(btVector3(0., 0., 0.)); + setLinearUpperLimit(btVector3(0., 0., 0.)); + setAngularLowerLimit(btVector3(0.f, -SIMD_HALF_PI + UNIV_EPS, -SIMD_PI + UNIV_EPS)); + setAngularUpperLimit(btVector3(0.f, SIMD_HALF_PI - UNIV_EPS, SIMD_PI - UNIV_EPS)); +} + diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btUniversalConstraint.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btUniversalConstraint.h new file mode 100644 index 000000000..4e64a7d7e --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btUniversalConstraint.h @@ -0,0 +1,60 @@ +/* +Bullet Continuous Collision Detection and Physics Library, http://bulletphysics.org +Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + +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 UNIVERSAL_CONSTRAINT_H +#define UNIVERSAL_CONSTRAINT_H + + + +#include "LinearMath/btVector3.h" +#include "btTypedConstraint.h" +#include "btGeneric6DofConstraint.h" + + + +/// Constraint similar to ODE Universal Joint +/// has 2 rotatioonal degrees of freedom, similar to Euler rotations around Z (axis 1) +/// and Y (axis 2) +/// Description from ODE manual : +/// "Given axis 1 on body 1, and axis 2 on body 2 that is perpendicular to axis 1, it keeps them perpendicular. +/// In other words, rotation of the two bodies about the direction perpendicular to the two axes will be equal." + +class btUniversalConstraint : public btGeneric6DofConstraint +{ +protected: + btVector3 m_anchor; + btVector3 m_axis1; + btVector3 m_axis2; +public: + // constructor + // anchor, axis1 and axis2 are in world coordinate system + // axis1 must be orthogonal to axis2 + btUniversalConstraint(btRigidBody& rbA, btRigidBody& rbB, btVector3& anchor, btVector3& axis1, btVector3& axis2); + // access + const btVector3& getAnchor() { return m_calculatedTransformA.getOrigin(); } + const btVector3& getAnchor2() { return m_calculatedTransformB.getOrigin(); } + const btVector3& getAxis1() { return m_axis1; } + const btVector3& getAxis2() { return m_axis2; } + btScalar getAngle1() { return getAngle(2); } + btScalar getAngle2() { return getAngle(1); } + // limits + void setUpperLimit(btScalar ang1max, btScalar ang2max) { setAngularUpperLimit(btVector3(0.f, ang1max, ang2max)); } + void setLowerLimit(btScalar ang1min, btScalar ang2min) { setAngularLowerLimit(btVector3(0.f, ang1min, ang2min)); } +}; + + + +#endif // UNIVERSAL_CONSTRAINT_H + diff --git a/Engine/lib/bullet/src/BulletDynamics/Dynamics/Bullet-C-API.cpp b/Engine/lib/bullet/src/BulletDynamics/Dynamics/Bullet-C-API.cpp new file mode 100644 index 000000000..32b63f195 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Dynamics/Bullet-C-API.cpp @@ -0,0 +1,408 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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. +*/ + +/* + Draft high-level generic physics C-API. For low-level access, use the physics SDK native API's. + Work in progress, functionality will be added on demand. + + If possible, use the richer Bullet C++ API, by including +*/ + +#include "Bullet-C-Api.h" +#include "btBulletDynamicsCommon.h" +#include "LinearMath/btAlignedAllocator.h" + + + +#include "LinearMath/btVector3.h" +#include "LinearMath/btScalar.h" +#include "LinearMath/btMatrix3x3.h" +#include "LinearMath/btTransform.h" +#include "BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.h" +#include "BulletCollision/CollisionShapes/btTriangleShape.h" + +#include "BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h" +#include "BulletCollision/NarrowPhaseCollision/btPointCollector.h" +#include "BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.h" +#include "BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.h" +#include "BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h" +#include "BulletCollision/NarrowPhaseCollision/btGjkEpa2.h" +#include "BulletCollision/CollisionShapes/btMinkowskiSumShape.h" +#include "BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h" +#include "BulletCollision/NarrowPhaseCollision/btSimplexSolverInterface.h" +#include "BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.h" +#include "LinearMath/btStackAlloc.h" + +/* + Create and Delete a Physics SDK +*/ + +struct btPhysicsSdk +{ + +// btDispatcher* m_dispatcher; +// btOverlappingPairCache* m_pairCache; +// btConstraintSolver* m_constraintSolver + + btVector3 m_worldAabbMin; + btVector3 m_worldAabbMax; + + + //todo: version, hardware/optimization settings etc? + btPhysicsSdk() + :m_worldAabbMin(-1000,-1000,-1000), + m_worldAabbMax(1000,1000,1000) + { + + } + + +}; + +plPhysicsSdkHandle plNewBulletSdk() +{ + void* mem = btAlignedAlloc(sizeof(btPhysicsSdk),16); + return (plPhysicsSdkHandle)new (mem)btPhysicsSdk; +} + +void plDeletePhysicsSdk(plPhysicsSdkHandle physicsSdk) +{ + btPhysicsSdk* phys = reinterpret_cast(physicsSdk); + btAlignedFree(phys); +} + + +/* Dynamics World */ +plDynamicsWorldHandle plCreateDynamicsWorld(plPhysicsSdkHandle physicsSdkHandle) +{ + btPhysicsSdk* physicsSdk = reinterpret_cast(physicsSdkHandle); + void* mem = btAlignedAlloc(sizeof(btDefaultCollisionConfiguration),16); + btDefaultCollisionConfiguration* collisionConfiguration = new (mem)btDefaultCollisionConfiguration(); + mem = btAlignedAlloc(sizeof(btCollisionDispatcher),16); + btDispatcher* dispatcher = new (mem)btCollisionDispatcher(collisionConfiguration); + mem = btAlignedAlloc(sizeof(btAxisSweep3),16); + btBroadphaseInterface* pairCache = new (mem)btAxisSweep3(physicsSdk->m_worldAabbMin,physicsSdk->m_worldAabbMax); + mem = btAlignedAlloc(sizeof(btSequentialImpulseConstraintSolver),16); + btConstraintSolver* constraintSolver = new(mem) btSequentialImpulseConstraintSolver(); + + mem = btAlignedAlloc(sizeof(btDiscreteDynamicsWorld),16); + return (plDynamicsWorldHandle) new (mem)btDiscreteDynamicsWorld(dispatcher,pairCache,constraintSolver,collisionConfiguration); +} +void plDeleteDynamicsWorld(plDynamicsWorldHandle world) +{ + //todo: also clean up the other allocations, axisSweep, pairCache,dispatcher,constraintSolver,collisionConfiguration + btDynamicsWorld* dynamicsWorld = reinterpret_cast< btDynamicsWorld* >(world); + btAlignedFree(dynamicsWorld); +} + +void plStepSimulation(plDynamicsWorldHandle world, plReal timeStep) +{ + btDynamicsWorld* dynamicsWorld = reinterpret_cast< btDynamicsWorld* >(world); + btAssert(dynamicsWorld); + dynamicsWorld->stepSimulation(timeStep); +} + +void plAddRigidBody(plDynamicsWorldHandle world, plRigidBodyHandle object) +{ + btDynamicsWorld* dynamicsWorld = reinterpret_cast< btDynamicsWorld* >(world); + btAssert(dynamicsWorld); + btRigidBody* body = reinterpret_cast< btRigidBody* >(object); + btAssert(body); + + dynamicsWorld->addRigidBody(body); +} + +void plRemoveRigidBody(plDynamicsWorldHandle world, plRigidBodyHandle object) +{ + btDynamicsWorld* dynamicsWorld = reinterpret_cast< btDynamicsWorld* >(world); + btAssert(dynamicsWorld); + btRigidBody* body = reinterpret_cast< btRigidBody* >(object); + btAssert(body); + + dynamicsWorld->removeRigidBody(body); +} + +/* Rigid Body */ + +plRigidBodyHandle plCreateRigidBody( void* user_data, float mass, plCollisionShapeHandle cshape ) +{ + btTransform trans; + trans.setIdentity(); + btVector3 localInertia(0,0,0); + btCollisionShape* shape = reinterpret_cast( cshape); + btAssert(shape); + if (mass) + { + shape->calculateLocalInertia(mass,localInertia); + } + void* mem = btAlignedAlloc(sizeof(btRigidBody),16); + btRigidBody::btRigidBodyConstructionInfo rbci(mass, 0,shape,localInertia); + btRigidBody* body = new (mem)btRigidBody(rbci); + body->setWorldTransform(trans); + body->setUserPointer(user_data); + return (plRigidBodyHandle) body; +} + +void plDeleteRigidBody(plRigidBodyHandle cbody) +{ + btRigidBody* body = reinterpret_cast< btRigidBody* >(cbody); + btAssert(body); + btAlignedFree( body); +} + + +/* Collision Shape definition */ + +plCollisionShapeHandle plNewSphereShape(plReal radius) +{ + void* mem = btAlignedAlloc(sizeof(btSphereShape),16); + return (plCollisionShapeHandle) new (mem)btSphereShape(radius); + +} + +plCollisionShapeHandle plNewBoxShape(plReal x, plReal y, plReal z) +{ + void* mem = btAlignedAlloc(sizeof(btBoxShape),16); + return (plCollisionShapeHandle) new (mem)btBoxShape(btVector3(x,y,z)); +} + +plCollisionShapeHandle plNewCapsuleShape(plReal radius, plReal height) +{ + //capsule is convex hull of 2 spheres, so use btMultiSphereShape + + const int numSpheres = 2; + btVector3 positions[numSpheres] = {btVector3(0,height,0),btVector3(0,-height,0)}; + btScalar radi[numSpheres] = {radius,radius}; + void* mem = btAlignedAlloc(sizeof(btMultiSphereShape),16); + return (plCollisionShapeHandle) new (mem)btMultiSphereShape(positions,radi,numSpheres); +} +plCollisionShapeHandle plNewConeShape(plReal radius, plReal height) +{ + void* mem = btAlignedAlloc(sizeof(btConeShape),16); + return (plCollisionShapeHandle) new (mem)btConeShape(radius,height); +} + +plCollisionShapeHandle plNewCylinderShape(plReal radius, plReal height) +{ + void* mem = btAlignedAlloc(sizeof(btCylinderShape),16); + return (plCollisionShapeHandle) new (mem)btCylinderShape(btVector3(radius,height,radius)); +} + +/* Convex Meshes */ +plCollisionShapeHandle plNewConvexHullShape() +{ + void* mem = btAlignedAlloc(sizeof(btConvexHullShape),16); + return (plCollisionShapeHandle) new (mem)btConvexHullShape(); +} + + +/* Concave static triangle meshes */ +plMeshInterfaceHandle plNewMeshInterface() +{ + return 0; +} + +plCollisionShapeHandle plNewCompoundShape() +{ + void* mem = btAlignedAlloc(sizeof(btCompoundShape),16); + return (plCollisionShapeHandle) new (mem)btCompoundShape(); +} + +void plAddChildShape(plCollisionShapeHandle compoundShapeHandle,plCollisionShapeHandle childShapeHandle, plVector3 childPos,plQuaternion childOrn) +{ + btCollisionShape* colShape = reinterpret_cast(compoundShapeHandle); + btAssert(colShape->getShapeType() == COMPOUND_SHAPE_PROXYTYPE); + btCompoundShape* compoundShape = reinterpret_cast(colShape); + btCollisionShape* childShape = reinterpret_cast(childShapeHandle); + btTransform localTrans; + localTrans.setIdentity(); + localTrans.setOrigin(btVector3(childPos[0],childPos[1],childPos[2])); + localTrans.setRotation(btQuaternion(childOrn[0],childOrn[1],childOrn[2],childOrn[3])); + compoundShape->addChildShape(localTrans,childShape); +} + +void plSetEuler(plReal yaw,plReal pitch,plReal roll, plQuaternion orient) +{ + btQuaternion orn; + orn.setEuler(yaw,pitch,roll); + orient[0] = orn.getX(); + orient[1] = orn.getY(); + orient[2] = orn.getZ(); + orient[3] = orn.getW(); + +} + + +// extern void plAddTriangle(plMeshInterfaceHandle meshHandle, plVector3 v0,plVector3 v1,plVector3 v2); +// extern plCollisionShapeHandle plNewStaticTriangleMeshShape(plMeshInterfaceHandle); + + +void plAddVertex(plCollisionShapeHandle cshape, plReal x,plReal y,plReal z) +{ + btCollisionShape* colShape = reinterpret_cast( cshape); + (void)colShape; + btAssert(colShape->getShapeType()==CONVEX_HULL_SHAPE_PROXYTYPE); + btConvexHullShape* convexHullShape = reinterpret_cast( cshape); + convexHullShape->addPoint(btVector3(x,y,z)); + +} + +void plDeleteShape(plCollisionShapeHandle cshape) +{ + btCollisionShape* shape = reinterpret_cast( cshape); + btAssert(shape); + btAlignedFree(shape); +} +void plSetScaling(plCollisionShapeHandle cshape, plVector3 cscaling) +{ + btCollisionShape* shape = reinterpret_cast( cshape); + btAssert(shape); + btVector3 scaling(cscaling[0],cscaling[1],cscaling[2]); + shape->setLocalScaling(scaling); +} + + + +void plSetPosition(plRigidBodyHandle object, const plVector3 position) +{ + btRigidBody* body = reinterpret_cast< btRigidBody* >(object); + btAssert(body); + btVector3 pos(position[0],position[1],position[2]); + btTransform worldTrans = body->getWorldTransform(); + worldTrans.setOrigin(pos); + body->setWorldTransform(worldTrans); +} + +void plSetOrientation(plRigidBodyHandle object, const plQuaternion orientation) +{ + btRigidBody* body = reinterpret_cast< btRigidBody* >(object); + btAssert(body); + btQuaternion orn(orientation[0],orientation[1],orientation[2],orientation[3]); + btTransform worldTrans = body->getWorldTransform(); + worldTrans.setRotation(orn); + body->setWorldTransform(worldTrans); +} + +void plSetOpenGLMatrix(plRigidBodyHandle object, plReal* matrix) +{ + btRigidBody* body = reinterpret_cast< btRigidBody* >(object); + btAssert(body); + btTransform& worldTrans = body->getWorldTransform(); + worldTrans.setFromOpenGLMatrix(matrix); +} + +void plGetOpenGLMatrix(plRigidBodyHandle object, plReal* matrix) +{ + btRigidBody* body = reinterpret_cast< btRigidBody* >(object); + btAssert(body); + body->getWorldTransform().getOpenGLMatrix(matrix); + +} + +void plGetPosition(plRigidBodyHandle object,plVector3 position) +{ + btRigidBody* body = reinterpret_cast< btRigidBody* >(object); + btAssert(body); + const btVector3& pos = body->getWorldTransform().getOrigin(); + position[0] = pos.getX(); + position[1] = pos.getY(); + position[2] = pos.getZ(); +} + +void plGetOrientation(plRigidBodyHandle object,plQuaternion orientation) +{ + btRigidBody* body = reinterpret_cast< btRigidBody* >(object); + btAssert(body); + const btQuaternion& orn = body->getWorldTransform().getRotation(); + orientation[0] = orn.getX(); + orientation[1] = orn.getY(); + orientation[2] = orn.getZ(); + orientation[3] = orn.getW(); +} + + + +//plRigidBodyHandle plRayCast(plDynamicsWorldHandle world, const plVector3 rayStart, const plVector3 rayEnd, plVector3 hitpoint, plVector3 normal); + +// extern plRigidBodyHandle plObjectCast(plDynamicsWorldHandle world, const plVector3 rayStart, const plVector3 rayEnd, plVector3 hitpoint, plVector3 normal); + +double plNearestPoints(float p1[3], float p2[3], float p3[3], float q1[3], float q2[3], float q3[3], float *pa, float *pb, float normal[3]) +{ + btVector3 vp(p1[0], p1[1], p1[2]); + btTriangleShape trishapeA(vp, + btVector3(p2[0], p2[1], p2[2]), + btVector3(p3[0], p3[1], p3[2])); + trishapeA.setMargin(0.000001f); + btVector3 vq(q1[0], q1[1], q1[2]); + btTriangleShape trishapeB(vq, + btVector3(q2[0], q2[1], q2[2]), + btVector3(q3[0], q3[1], q3[2])); + trishapeB.setMargin(0.000001f); + + // btVoronoiSimplexSolver sGjkSimplexSolver; + // btGjkEpaPenetrationDepthSolver penSolverPtr; + + static btSimplexSolverInterface sGjkSimplexSolver; + sGjkSimplexSolver.reset(); + + static btGjkEpaPenetrationDepthSolver Solver0; + static btMinkowskiPenetrationDepthSolver Solver1; + + btConvexPenetrationDepthSolver* Solver = NULL; + + Solver = &Solver1; + + btGjkPairDetector convexConvex(&trishapeA ,&trishapeB,&sGjkSimplexSolver,Solver); + + convexConvex.m_catchDegeneracies = 1; + + // btGjkPairDetector convexConvex(&trishapeA ,&trishapeB,&sGjkSimplexSolver,0); + + btPointCollector gjkOutput; + btGjkPairDetector::ClosestPointInput input; + + btStackAlloc gStackAlloc(1024*1024*2); + + input.m_stackAlloc = &gStackAlloc; + + btTransform tr; + tr.setIdentity(); + + input.m_transformA = tr; + input.m_transformB = tr; + + convexConvex.getClosestPoints(input, gjkOutput, 0); + + + if (gjkOutput.m_hasResult) + { + + pb[0] = pa[0] = gjkOutput.m_pointInWorld[0]; + pb[1] = pa[1] = gjkOutput.m_pointInWorld[1]; + pb[2] = pa[2] = gjkOutput.m_pointInWorld[2]; + + pb[0]+= gjkOutput.m_normalOnBInWorld[0] * gjkOutput.m_distance; + pb[1]+= gjkOutput.m_normalOnBInWorld[1] * gjkOutput.m_distance; + pb[2]+= gjkOutput.m_normalOnBInWorld[2] * gjkOutput.m_distance; + + normal[0] = gjkOutput.m_normalOnBInWorld[0]; + normal[1] = gjkOutput.m_normalOnBInWorld[1]; + normal[2] = gjkOutput.m_normalOnBInWorld[2]; + + return gjkOutput.m_distance; + } + return -1.0f; +} + diff --git a/Engine/lib/bullet/src/BulletDynamics/Dynamics/btActionInterface.h b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btActionInterface.h new file mode 100644 index 000000000..0832c9b24 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btActionInterface.h @@ -0,0 +1,40 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 _BT_ACTION_INTERFACE_H +#define _BT_ACTION_INTERFACE_H + +class btIDebugDraw; +class btCollisionWorld; + +#include "LinearMath/btScalar.h" + +///Basic interface to allow actions such as vehicles and characters to be updated inside a btDynamicsWorld +class btActionInterface +{ + public: + + virtual ~btActionInterface() + { + } + + virtual void updateAction( btCollisionWorld* collisionWorld, btScalar deltaTimeStep)=0; + + virtual void debugDraw(btIDebugDraw* debugDrawer) = 0; + +}; + +#endif //_BT_ACTION_INTERFACE_H + diff --git a/Engine/lib/bullet/src/BulletDynamics/Dynamics/btContinuousDynamicsWorld.cpp b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btContinuousDynamicsWorld.cpp new file mode 100644 index 000000000..23501c443 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btContinuousDynamicsWorld.cpp @@ -0,0 +1,196 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btContinuousDynamicsWorld.h" +#include "LinearMath/btQuickprof.h" + +//collision detection +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/BroadphaseCollision/btSimpleBroadphase.h" +#include "BulletCollision/CollisionShapes/btCollisionShape.h" +#include "BulletCollision/CollisionDispatch/btSimulationIslandManager.h" + +//rigidbody & constraints +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include "BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h" +#include "BulletDynamics/ConstraintSolver/btContactSolverInfo.h" +#include "BulletDynamics/ConstraintSolver/btTypedConstraint.h" + + + +#include + +btContinuousDynamicsWorld::btContinuousDynamicsWorld(btDispatcher* dispatcher,btBroadphaseInterface* pairCache,btConstraintSolver* constraintSolver,btCollisionConfiguration* collisionConfiguration) +:btDiscreteDynamicsWorld(dispatcher,pairCache,constraintSolver,collisionConfiguration) +{ +} + +btContinuousDynamicsWorld::~btContinuousDynamicsWorld() +{ +} + + +void btContinuousDynamicsWorld::internalSingleStepSimulation( btScalar timeStep) +{ + + startProfiling(timeStep); + + if(0 != m_internalPreTickCallback) { + (*m_internalPreTickCallback)(this, timeStep); + } + + + ///update aabbs information + updateAabbs(); + //static int frame=0; +// printf("frame %d\n",frame++); + + ///apply gravity, predict motion + predictUnconstraintMotion(timeStep); + + btDispatcherInfo& dispatchInfo = getDispatchInfo(); + + dispatchInfo.m_timeStep = timeStep; + dispatchInfo.m_stepCount = 0; + dispatchInfo.m_debugDraw = getDebugDrawer(); + + ///perform collision detection + performDiscreteCollisionDetection(); + + calculateSimulationIslands(); + + + getSolverInfo().m_timeStep = timeStep; + + + + ///solve contact and other joint constraints + solveConstraints(getSolverInfo()); + + ///CallbackTriggers(); + calculateTimeOfImpacts(timeStep); + + btScalar toi = dispatchInfo.m_timeOfImpact; +// if (toi < 1.f) +// printf("toi = %f\n",toi); + if (toi < 0.f) + printf("toi = %f\n",toi); + + + ///integrate transforms + integrateTransforms(timeStep * toi); + + ///update vehicle simulation + updateActions(timeStep); + + updateActivationState( timeStep ); + + if(0 != m_internalTickCallback) { + (*m_internalTickCallback)(this, timeStep); + } +} + +void btContinuousDynamicsWorld::calculateTimeOfImpacts(btScalar timeStep) +{ + ///these should be 'temporal' aabbs! + updateTemporalAabbs(timeStep); + + ///'toi' is the global smallest time of impact. However, we just calculate the time of impact for each object individually. + ///so we handle the case moving versus static properly, and we cheat for moving versus moving + btScalar toi = 1.f; + + + btDispatcherInfo& dispatchInfo = getDispatchInfo(); + dispatchInfo.m_timeStep = timeStep; + dispatchInfo.m_timeOfImpact = 1.f; + dispatchInfo.m_stepCount = 0; + dispatchInfo.m_dispatchFunc = btDispatcherInfo::DISPATCH_CONTINUOUS; + + ///calculate time of impact for overlapping pairs + + + btDispatcher* dispatcher = getDispatcher(); + if (dispatcher) + dispatcher->dispatchAllCollisionPairs(m_broadphasePairCache->getOverlappingPairCache(),dispatchInfo,m_dispatcher1); + + toi = dispatchInfo.m_timeOfImpact; + + dispatchInfo.m_dispatchFunc = btDispatcherInfo::DISPATCH_DISCRETE; + +} + +void btContinuousDynamicsWorld::updateTemporalAabbs(btScalar timeStep) +{ + + btVector3 temporalAabbMin,temporalAabbMax; + + for ( int i=0;igetCollisionShape()->getAabb(m_collisionObjects[i]->getWorldTransform(),temporalAabbMin,temporalAabbMax); + const btVector3& linvel = body->getLinearVelocity(); + + //make the AABB temporal + btScalar temporalAabbMaxx = temporalAabbMax.getX(); + btScalar temporalAabbMaxy = temporalAabbMax.getY(); + btScalar temporalAabbMaxz = temporalAabbMax.getZ(); + btScalar temporalAabbMinx = temporalAabbMin.getX(); + btScalar temporalAabbMiny = temporalAabbMin.getY(); + btScalar temporalAabbMinz = temporalAabbMin.getZ(); + + // add linear motion + btVector3 linMotion = linvel*timeStep; + + if (linMotion.x() > 0.f) + temporalAabbMaxx += linMotion.x(); + else + temporalAabbMinx += linMotion.x(); + if (linMotion.y() > 0.f) + temporalAabbMaxy += linMotion.y(); + else + temporalAabbMiny += linMotion.y(); + if (linMotion.z() > 0.f) + temporalAabbMaxz += linMotion.z(); + else + temporalAabbMinz += linMotion.z(); + + //add conservative angular motion + btScalar angularMotion(0);// = angvel.length() * GetAngularMotionDisc() * timeStep; + btVector3 angularMotion3d(angularMotion,angularMotion,angularMotion); + temporalAabbMin = btVector3(temporalAabbMinx,temporalAabbMiny,temporalAabbMinz); + temporalAabbMax = btVector3(temporalAabbMaxx,temporalAabbMaxy,temporalAabbMaxz); + + temporalAabbMin -= angularMotion3d; + temporalAabbMax += angularMotion3d; + + m_broadphasePairCache->setAabb(body->getBroadphaseHandle(),temporalAabbMin,temporalAabbMax,m_dispatcher1); + } + } + + //update aabb (of all moved objects) + + m_broadphasePairCache->calculateOverlappingPairs(m_dispatcher1); + + + +} + + + diff --git a/Engine/lib/bullet/src/BulletDynamics/Dynamics/btContinuousDynamicsWorld.h b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btContinuousDynamicsWorld.h new file mode 100644 index 000000000..61c8dea03 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btContinuousDynamicsWorld.h @@ -0,0 +1,46 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 BT_CONTINUOUS_DYNAMICS_WORLD_H +#define BT_CONTINUOUS_DYNAMICS_WORLD_H + +#include "btDiscreteDynamicsWorld.h" + +///btContinuousDynamicsWorld adds optional (per object) continuous collision detection for fast moving objects to the btDiscreteDynamicsWorld. +///This copes with fast moving objects that otherwise would tunnel/miss collisions. +///Under construction, don't use yet! Please use btDiscreteDynamicsWorld instead. +class btContinuousDynamicsWorld : public btDiscreteDynamicsWorld +{ + + void updateTemporalAabbs(btScalar timeStep); + + public: + + btContinuousDynamicsWorld(btDispatcher* dispatcher,btBroadphaseInterface* pairCache,btConstraintSolver* constraintSolver,btCollisionConfiguration* collisionConfiguration); + virtual ~btContinuousDynamicsWorld(); + + ///time stepping with calculation of time of impact for selected fast moving objects + virtual void internalSingleStepSimulation( btScalar timeStep); + + virtual void calculateTimeOfImpacts(btScalar timeStep); + + virtual btDynamicsWorldType getWorldType() const + { + return BT_CONTINUOUS_DYNAMICS_WORLD; + } + +}; + +#endif //BT_CONTINUOUS_DYNAMICS_WORLD_H diff --git a/Engine/lib/bullet/src/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.cpp b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.cpp new file mode 100644 index 000000000..e97300c52 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.cpp @@ -0,0 +1,1421 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 "btDiscreteDynamicsWorld.h" + +//collision detection +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/BroadphaseCollision/btSimpleBroadphase.h" +#include "BulletCollision/CollisionShapes/btCollisionShape.h" +#include "BulletCollision/CollisionDispatch/btSimulationIslandManager.h" +#include "LinearMath/btTransformUtil.h" +#include "LinearMath/btQuickprof.h" + +//rigidbody & constraints +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include "BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h" +#include "BulletDynamics/ConstraintSolver/btContactSolverInfo.h" +#include "BulletDynamics/ConstraintSolver/btTypedConstraint.h" +#include "BulletDynamics/ConstraintSolver/btPoint2PointConstraint.h" +#include "BulletDynamics/ConstraintSolver/btHingeConstraint.h" +#include "BulletDynamics/ConstraintSolver/btConeTwistConstraint.h" +#include "BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.h" +#include "BulletDynamics/ConstraintSolver/btSliderConstraint.h" + +//for debug rendering +#include "BulletCollision/CollisionShapes/btBoxShape.h" +#include "BulletCollision/CollisionShapes/btCapsuleShape.h" +#include "BulletCollision/CollisionShapes/btCompoundShape.h" +#include "BulletCollision/CollisionShapes/btConeShape.h" +#include "BulletCollision/CollisionShapes/btConvexTriangleMeshShape.h" +#include "BulletCollision/CollisionShapes/btCylinderShape.h" +#include "BulletCollision/CollisionShapes/btMultiSphereShape.h" +#include "BulletCollision/CollisionShapes/btPolyhedralConvexShape.h" +#include "BulletCollision/CollisionShapes/btSphereShape.h" +#include "BulletCollision/CollisionShapes/btTriangleCallback.h" +#include "BulletCollision/CollisionShapes/btTriangleMeshShape.h" +#include "BulletCollision/CollisionShapes/btStaticPlaneShape.h" +#include "LinearMath/btIDebugDraw.h" + + +#include "BulletDynamics/Dynamics/btActionInterface.h" +#include "LinearMath/btQuickprof.h" +#include "LinearMath/btMotionState.h" + + + + + +btDiscreteDynamicsWorld::btDiscreteDynamicsWorld(btDispatcher* dispatcher,btBroadphaseInterface* pairCache,btConstraintSolver* constraintSolver, btCollisionConfiguration* collisionConfiguration) +:btDynamicsWorld(dispatcher,pairCache,collisionConfiguration), +m_constraintSolver(constraintSolver), +m_gravity(0,-10,0), +m_localTime(btScalar(1.)/btScalar(60.)), +m_synchronizeAllMotionStates(false), +m_profileTimings(0) +{ + if (!m_constraintSolver) + { + void* mem = btAlignedAlloc(sizeof(btSequentialImpulseConstraintSolver),16); + m_constraintSolver = new (mem) btSequentialImpulseConstraintSolver; + m_ownsConstraintSolver = true; + } else + { + m_ownsConstraintSolver = false; + } + + { + void* mem = btAlignedAlloc(sizeof(btSimulationIslandManager),16); + m_islandManager = new (mem) btSimulationIslandManager(); + } + + m_ownsIslandManager = true; +} + + +btDiscreteDynamicsWorld::~btDiscreteDynamicsWorld() +{ + //only delete it when we created it + if (m_ownsIslandManager) + { + m_islandManager->~btSimulationIslandManager(); + btAlignedFree( m_islandManager); + } + if (m_ownsConstraintSolver) + { + + m_constraintSolver->~btConstraintSolver(); + btAlignedFree(m_constraintSolver); + } +} + +void btDiscreteDynamicsWorld::saveKinematicState(btScalar timeStep) +{ +///would like to iterate over m_nonStaticRigidBodies, but unfortunately old API allows +///to switch status _after_ adding kinematic objects to the world +///fix it for Bullet 3.x release + for (int i=0;igetActivationState() != ISLAND_SLEEPING) + { + if (body->isKinematicObject()) + { + //to calculate velocities next frame + body->saveKinematicState(timeStep); + } + } + } + +} + +void btDiscreteDynamicsWorld::debugDrawWorld() +{ + BT_PROFILE("debugDrawWorld"); + + if (getDebugDrawer() && getDebugDrawer()->getDebugMode() & btIDebugDraw::DBG_DrawContactPoints) + { + int numManifolds = getDispatcher()->getNumManifolds(); + btVector3 color(0,0,0); + for (int i=0;igetManifoldByIndexInternal(i); + //btCollisionObject* obA = static_cast(contactManifold->getBody0()); + //btCollisionObject* obB = static_cast(contactManifold->getBody1()); + + int numContacts = contactManifold->getNumContacts(); + for (int j=0;jgetContactPoint(j); + getDebugDrawer()->drawContactPoint(cp.m_positionWorldOnB,cp.m_normalWorldOnB,cp.getDistance(),cp.getLifeTime(),color); + } + } + } + bool drawConstraints = false; + if (getDebugDrawer()) + { + int mode = getDebugDrawer()->getDebugMode(); + if(mode & (btIDebugDraw::DBG_DrawConstraints | btIDebugDraw::DBG_DrawConstraintLimits)) + { + drawConstraints = true; + } + } + if(drawConstraints) + { + for(int i = getNumConstraints()-1; i>=0 ;i--) + { + btTypedConstraint* constraint = getConstraint(i); + debugDrawConstraint(constraint); + } + } + + + + if (getDebugDrawer() && getDebugDrawer()->getDebugMode() & (btIDebugDraw::DBG_DrawWireframe | btIDebugDraw::DBG_DrawAabb)) + { + int i; + + for ( i=0;igetDebugMode() & btIDebugDraw::DBG_DrawWireframe) + { + btVector3 color(btScalar(255.),btScalar(255.),btScalar(255.)); + switch(colObj->getActivationState()) + { + case ACTIVE_TAG: + color = btVector3(btScalar(255.),btScalar(255.),btScalar(255.)); break; + case ISLAND_SLEEPING: + color = btVector3(btScalar(0.),btScalar(255.),btScalar(0.));break; + case WANTS_DEACTIVATION: + color = btVector3(btScalar(0.),btScalar(255.),btScalar(255.));break; + case DISABLE_DEACTIVATION: + color = btVector3(btScalar(255.),btScalar(0.),btScalar(0.));break; + case DISABLE_SIMULATION: + color = btVector3(btScalar(255.),btScalar(255.),btScalar(0.));break; + default: + { + color = btVector3(btScalar(255.),btScalar(0.),btScalar(0.)); + } + }; + + debugDrawObject(colObj->getWorldTransform(),colObj->getCollisionShape(),color); + } + if (m_debugDrawer && (m_debugDrawer->getDebugMode() & btIDebugDraw::DBG_DrawAabb)) + { + btVector3 minAabb,maxAabb; + btVector3 colorvec(1,0,0); + colObj->getCollisionShape()->getAabb(colObj->getWorldTransform(), minAabb,maxAabb); + m_debugDrawer->drawAabb(minAabb,maxAabb,colorvec); + } + + } + + if (getDebugDrawer() && getDebugDrawer()->getDebugMode()) + { + for (i=0;idebugDraw(m_debugDrawer); + } + } + } +} + +void btDiscreteDynamicsWorld::clearForces() +{ + ///@todo: iterate over awake simulation islands! + for ( int i=0;iclearForces(); + } +} + +///apply gravity, call this once per timestep +void btDiscreteDynamicsWorld::applyGravity() +{ + ///@todo: iterate over awake simulation islands! + for ( int i=0;iisActive()) + { + body->applyGravity(); + } + } +} + + +void btDiscreteDynamicsWorld::synchronizeSingleMotionState(btRigidBody* body) +{ + btAssert(body); + + if (body->getMotionState() && !body->isStaticOrKinematicObject()) + { + //we need to call the update at least once, even for sleeping objects + //otherwise the 'graphics' transform never updates properly + ///@todo: add 'dirty' flag + //if (body->getActivationState() != ISLAND_SLEEPING) + { + btTransform interpolatedTransform; + btTransformUtil::integrateTransform(body->getInterpolationWorldTransform(), + body->getInterpolationLinearVelocity(),body->getInterpolationAngularVelocity(),m_localTime*body->getHitFraction(),interpolatedTransform); + body->getMotionState()->setWorldTransform(interpolatedTransform); + } + } +} + + +void btDiscreteDynamicsWorld::synchronizeMotionStates() +{ + BT_PROFILE("synchronizeMotionStates"); + if (m_synchronizeAllMotionStates) + { + //iterate over all collision objects + for ( int i=0;iisActive()) + synchronizeSingleMotionState(body); + } + } +} + + +int btDiscreteDynamicsWorld::stepSimulation( btScalar timeStep,int maxSubSteps, btScalar fixedTimeStep) +{ + startProfiling(timeStep); + + BT_PROFILE("stepSimulation"); + + int numSimulationSubSteps = 0; + + if (maxSubSteps) + { + //fixed timestep with interpolation + m_localTime += timeStep; + if (m_localTime >= fixedTimeStep) + { + numSimulationSubSteps = int( m_localTime / fixedTimeStep); + m_localTime -= numSimulationSubSteps * fixedTimeStep; + } + } else + { + //variable timestep + fixedTimeStep = timeStep; + m_localTime = timeStep; + if (btFuzzyZero(timeStep)) + { + numSimulationSubSteps = 0; + maxSubSteps = 0; + } else + { + numSimulationSubSteps = 1; + maxSubSteps = 1; + } + } + + //process some debugging flags + if (getDebugDrawer()) + { + btIDebugDraw* debugDrawer = getDebugDrawer (); + gDisableDeactivation = (debugDrawer->getDebugMode() & btIDebugDraw::DBG_NoDeactivation) != 0; + } + if (numSimulationSubSteps) + { + + saveKinematicState(fixedTimeStep); + + applyGravity(); + + //clamp the number of substeps, to prevent simulation grinding spiralling down to a halt + int clampedSimulationSteps = (numSimulationSubSteps > maxSubSteps)? maxSubSteps : numSimulationSubSteps; + + for (int i=0;iisActive()) + { + body->setGravity(gravity); + } + } +} + +btVector3 btDiscreteDynamicsWorld::getGravity () const +{ + return m_gravity; +} + +void btDiscreteDynamicsWorld::addCollisionObject(btCollisionObject* collisionObject,short int collisionFilterGroup,short int collisionFilterMask) +{ + btCollisionWorld::addCollisionObject(collisionObject,collisionFilterGroup,collisionFilterMask); +} + +void btDiscreteDynamicsWorld::removeCollisionObject(btCollisionObject* collisionObject) +{ + btRigidBody* body = btRigidBody::upcast(collisionObject); + if (body) + removeRigidBody(body); + else + btCollisionWorld::removeCollisionObject(collisionObject); +} + +void btDiscreteDynamicsWorld::removeRigidBody(btRigidBody* body) +{ + m_nonStaticRigidBodies.remove(body); + btCollisionWorld::removeCollisionObject(body); +} + + +void btDiscreteDynamicsWorld::addRigidBody(btRigidBody* body) +{ + if (!body->isStaticOrKinematicObject()) + { + body->setGravity(m_gravity); + } + + if (body->getCollisionShape()) + { + if (!body->isStaticObject()) + { + m_nonStaticRigidBodies.push_back(body); + } else + { + body->setActivationState(ISLAND_SLEEPING); + } + + bool isDynamic = !(body->isStaticObject() || body->isKinematicObject()); + short collisionFilterGroup = isDynamic? short(btBroadphaseProxy::DefaultFilter) : short(btBroadphaseProxy::StaticFilter); + short collisionFilterMask = isDynamic? short(btBroadphaseProxy::AllFilter) : short(btBroadphaseProxy::AllFilter ^ btBroadphaseProxy::StaticFilter); + + addCollisionObject(body,collisionFilterGroup,collisionFilterMask); + } +} + +void btDiscreteDynamicsWorld::addRigidBody(btRigidBody* body, short group, short mask) +{ + if (!body->isStaticOrKinematicObject()) + { + body->setGravity(m_gravity); + } + + if (body->getCollisionShape()) + { + if (!body->isStaticObject()) + { + m_nonStaticRigidBodies.push_back(body); + } + else + { + body->setActivationState(ISLAND_SLEEPING); + } + addCollisionObject(body,group,mask); + } +} + + +void btDiscreteDynamicsWorld::updateActions(btScalar timeStep) +{ + BT_PROFILE("updateActions"); + + for ( int i=0;iupdateAction( this, timeStep); + } +} + + +void btDiscreteDynamicsWorld::updateActivationState(btScalar timeStep) +{ + BT_PROFILE("updateActivationState"); + + for ( int i=0;iupdateDeactivation(timeStep); + + if (body->wantsSleeping()) + { + if (body->isStaticOrKinematicObject()) + { + body->setActivationState(ISLAND_SLEEPING); + } else + { + if (body->getActivationState() == ACTIVE_TAG) + body->setActivationState( WANTS_DEACTIVATION ); + if (body->getActivationState() == ISLAND_SLEEPING) + { + body->setAngularVelocity(btVector3(0,0,0)); + body->setLinearVelocity(btVector3(0,0,0)); + } + + } + } else + { + if (body->getActivationState() != DISABLE_DEACTIVATION) + body->setActivationState( ACTIVE_TAG ); + } + } + } +} + +void btDiscreteDynamicsWorld::addConstraint(btTypedConstraint* constraint,bool disableCollisionsBetweenLinkedBodies) +{ + m_constraints.push_back(constraint); + if (disableCollisionsBetweenLinkedBodies) + { + constraint->getRigidBodyA().addConstraintRef(constraint); + constraint->getRigidBodyB().addConstraintRef(constraint); + } +} + +void btDiscreteDynamicsWorld::removeConstraint(btTypedConstraint* constraint) +{ + m_constraints.remove(constraint); + constraint->getRigidBodyA().removeConstraintRef(constraint); + constraint->getRigidBodyB().removeConstraintRef(constraint); +} + +void btDiscreteDynamicsWorld::addAction(btActionInterface* action) +{ + m_actions.push_back(action); +} + +void btDiscreteDynamicsWorld::removeAction(btActionInterface* action) +{ + m_actions.remove(action); +} + + +void btDiscreteDynamicsWorld::addVehicle(btActionInterface* vehicle) +{ + addAction(vehicle); +} + +void btDiscreteDynamicsWorld::removeVehicle(btActionInterface* vehicle) +{ + removeAction(vehicle); +} + +void btDiscreteDynamicsWorld::addCharacter(btActionInterface* character) +{ + addAction(character); +} + +void btDiscreteDynamicsWorld::removeCharacter(btActionInterface* character) +{ + removeAction(character); +} + + +SIMD_FORCE_INLINE int btGetConstraintIslandId(const btTypedConstraint* lhs) +{ + int islandId; + + const btCollisionObject& rcolObj0 = lhs->getRigidBodyA(); + const btCollisionObject& rcolObj1 = lhs->getRigidBodyB(); + islandId= rcolObj0.getIslandTag()>=0?rcolObj0.getIslandTag():rcolObj1.getIslandTag(); + return islandId; + +} + + +class btSortConstraintOnIslandPredicate +{ + public: + + bool operator() ( const btTypedConstraint* lhs, const btTypedConstraint* rhs ) + { + int rIslandId0,lIslandId0; + rIslandId0 = btGetConstraintIslandId(rhs); + lIslandId0 = btGetConstraintIslandId(lhs); + return lIslandId0 < rIslandId0; + } +}; + + + + +void btDiscreteDynamicsWorld::solveConstraints(btContactSolverInfo& solverInfo) +{ + BT_PROFILE("solveConstraints"); + + struct InplaceSolverIslandCallback : public btSimulationIslandManager::IslandCallback + { + + btContactSolverInfo& m_solverInfo; + btConstraintSolver* m_solver; + btTypedConstraint** m_sortedConstraints; + int m_numConstraints; + btIDebugDraw* m_debugDrawer; + btStackAlloc* m_stackAlloc; + btDispatcher* m_dispatcher; + + InplaceSolverIslandCallback( + btContactSolverInfo& solverInfo, + btConstraintSolver* solver, + btTypedConstraint** sortedConstraints, + int numConstraints, + btIDebugDraw* debugDrawer, + btStackAlloc* stackAlloc, + btDispatcher* dispatcher) + :m_solverInfo(solverInfo), + m_solver(solver), + m_sortedConstraints(sortedConstraints), + m_numConstraints(numConstraints), + m_debugDrawer(debugDrawer), + m_stackAlloc(stackAlloc), + m_dispatcher(dispatcher) + { + + } + + InplaceSolverIslandCallback& operator=(InplaceSolverIslandCallback& other) + { + btAssert(0); + (void)other; + return *this; + } + virtual void ProcessIsland(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifolds,int numManifolds, int islandId) + { + if (islandId<0) + { + if (numManifolds + m_numConstraints) + { + ///we don't split islands, so all constraints/contact manifolds/bodies are passed into the solver regardless the island id + m_solver->solveGroup( bodies,numBodies,manifolds, numManifolds,&m_sortedConstraints[0],m_numConstraints,m_solverInfo,m_debugDrawer,m_stackAlloc,m_dispatcher); + } + } else + { + //also add all non-contact constraints/joints for this island + btTypedConstraint** startConstraint = 0; + int numCurConstraints = 0; + int i; + + //find the first constraint for this island + for (i=0;isolveGroup( bodies,numBodies,manifolds, numManifolds,startConstraint,numCurConstraints,m_solverInfo,m_debugDrawer,m_stackAlloc,m_dispatcher); + } + + } + } + + }; + + //sorted version of all btTypedConstraint, based on islandId + btAlignedObjectArray sortedConstraints; + sortedConstraints.resize( m_constraints.size()); + int i; + for (i=0;iprepareSolve(getCollisionWorld()->getNumCollisionObjects(), getCollisionWorld()->getDispatcher()->getNumManifolds()); + + /// solve all the constraints for this island + m_islandManager->buildAndProcessIslands(getCollisionWorld()->getDispatcher(),getCollisionWorld(),&solverCallback); + + m_constraintSolver->allSolved(solverInfo, m_debugDrawer, m_stackAlloc); +} + + + + +void btDiscreteDynamicsWorld::calculateSimulationIslands() +{ + BT_PROFILE("calculateSimulationIslands"); + + getSimulationIslandManager()->updateActivationState(getCollisionWorld(),getCollisionWorld()->getDispatcher()); + + { + int i; + int numConstraints = int(m_constraints.size()); + for (i=0;i< numConstraints ; i++ ) + { + btTypedConstraint* constraint = m_constraints[i]; + + const btRigidBody* colObj0 = &constraint->getRigidBodyA(); + const btRigidBody* colObj1 = &constraint->getRigidBodyB(); + + if (((colObj0) && (!(colObj0)->isStaticOrKinematicObject())) && + ((colObj1) && (!(colObj1)->isStaticOrKinematicObject()))) + { + if (colObj0->isActive() || colObj1->isActive()) + { + + getSimulationIslandManager()->getUnionFind().unite((colObj0)->getIslandTag(), + (colObj1)->getIslandTag()); + } + } + } + } + + //Store the island id in each body + getSimulationIslandManager()->storeIslandActivationState(getCollisionWorld()); + + +} + + +#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" + +class btClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback +{ + btCollisionObject* m_me; + btScalar m_allowedPenetration; + btOverlappingPairCache* m_pairCache; + btDispatcher* m_dispatcher; + + +public: + btClosestNotMeConvexResultCallback (btCollisionObject* me,const btVector3& fromA,const btVector3& toA,btOverlappingPairCache* pairCache,btDispatcher* dispatcher) : + btCollisionWorld::ClosestConvexResultCallback(fromA,toA), + m_allowedPenetration(0.0f), + m_me(me), + m_pairCache(pairCache), + m_dispatcher(dispatcher) + { + } + + virtual btScalar addSingleResult(btCollisionWorld::LocalConvexResult& convexResult,bool normalInWorldSpace) + { + if (convexResult.m_hitCollisionObject == m_me) + return 1.0f; + + //ignore result if there is no contact response + if(!convexResult.m_hitCollisionObject->hasContactResponse()) + return 1.0f; + + btVector3 linVelA,linVelB; + linVelA = m_convexToWorld-m_convexFromWorld; + linVelB = btVector3(0,0,0);//toB.getOrigin()-fromB.getOrigin(); + + btVector3 relativeVelocity = (linVelA-linVelB); + //don't report time of impact for motion away from the contact normal (or causes minor penetration) + if (convexResult.m_hitNormalLocal.dot(relativeVelocity)>=-m_allowedPenetration) + return 1.f; + + return ClosestConvexResultCallback::addSingleResult (convexResult, normalInWorldSpace); + } + + virtual bool needsCollision(btBroadphaseProxy* proxy0) const + { + //don't collide with itself + if (proxy0->m_clientObject == m_me) + return false; + + ///don't do CCD when the collision filters are not matching + if (!ClosestConvexResultCallback::needsCollision(proxy0)) + return false; + + btCollisionObject* otherObj = (btCollisionObject*) proxy0->m_clientObject; + + //call needsResponse, see http://code.google.com/p/bullet/issues/detail?id=179 + if (m_dispatcher->needsResponse(m_me,otherObj)) + { + ///don't do CCD when there are already contact points (touching contact/penetration) + btAlignedObjectArray manifoldArray; + btBroadphasePair* collisionPair = m_pairCache->findPair(m_me->getBroadphaseHandle(),proxy0); + if (collisionPair) + { + if (collisionPair->m_algorithm) + { + manifoldArray.resize(0); + collisionPair->m_algorithm->getAllContactManifolds(manifoldArray); + for (int j=0;jgetNumContacts()>0) + return false; + } + } + } + } + return true; + } + + +}; + +///internal debugging variable. this value shouldn't be too high +int gNumClampedCcdMotions=0; + +//#include "stdio.h" +void btDiscreteDynamicsWorld::integrateTransforms(btScalar timeStep) +{ + BT_PROFILE("integrateTransforms"); + btTransform predictedTrans; + for ( int i=0;isetHitFraction(1.f); + + if (body->isActive() && (!body->isStaticOrKinematicObject())) + { + body->predictIntegratedTransform(timeStep, predictedTrans); + btScalar squareMotion = (predictedTrans.getOrigin()-body->getWorldTransform().getOrigin()).length2(); + + if (body->getCcdSquareMotionThreshold() && body->getCcdSquareMotionThreshold() < squareMotion) + { + BT_PROFILE("CCD motion clamping"); + if (body->getCollisionShape()->isConvex()) + { + gNumClampedCcdMotions++; + + btClosestNotMeConvexResultCallback sweepResults(body,body->getWorldTransform().getOrigin(),predictedTrans.getOrigin(),getBroadphase()->getOverlappingPairCache(),getDispatcher()); + //btConvexShape* convexShape = static_cast(body->getCollisionShape()); + btSphereShape tmpSphere(body->getCcdSweptSphereRadius());//btConvexShape* convexShape = static_cast(body->getCollisionShape()); + + sweepResults.m_collisionFilterGroup = body->getBroadphaseProxy()->m_collisionFilterGroup; + sweepResults.m_collisionFilterMask = body->getBroadphaseProxy()->m_collisionFilterMask; + + convexSweepTest(&tmpSphere,body->getWorldTransform(),predictedTrans,sweepResults); + if (sweepResults.hasHit() && (sweepResults.m_closestHitFraction < 1.f)) + { + body->setHitFraction(sweepResults.m_closestHitFraction); + body->predictIntegratedTransform(timeStep*body->getHitFraction(), predictedTrans); + body->setHitFraction(0.f); +// printf("clamped integration to hit fraction = %f\n",fraction); + } + } + } + + body->proceedToTransform( predictedTrans); + } + } +} + + + + + +void btDiscreteDynamicsWorld::predictUnconstraintMotion(btScalar timeStep) +{ + BT_PROFILE("predictUnconstraintMotion"); + for ( int i=0;iisStaticOrKinematicObject()) + { + body->integrateVelocities( timeStep); + //damping + body->applyDamping(timeStep); + + body->predictIntegratedTransform(timeStep,body->getInterpolationWorldTransform()); + } + } +} + + +void btDiscreteDynamicsWorld::startProfiling(btScalar timeStep) +{ + (void)timeStep; + +#ifndef BT_NO_PROFILE + CProfileManager::Reset(); +#endif //BT_NO_PROFILE + +} + + + + + + +class DebugDrawcallback : public btTriangleCallback, public btInternalTriangleIndexCallback +{ + btIDebugDraw* m_debugDrawer; + btVector3 m_color; + btTransform m_worldTrans; + +public: + + DebugDrawcallback(btIDebugDraw* debugDrawer,const btTransform& worldTrans,const btVector3& color) : + m_debugDrawer(debugDrawer), + m_color(color), + m_worldTrans(worldTrans) + { + } + + virtual void internalProcessTriangleIndex(btVector3* triangle,int partId,int triangleIndex) + { + processTriangle(triangle,partId,triangleIndex); + } + + virtual void processTriangle(btVector3* triangle,int partId, int triangleIndex) + { + (void)partId; + (void)triangleIndex; + + btVector3 wv0,wv1,wv2; + wv0 = m_worldTrans*triangle[0]; + wv1 = m_worldTrans*triangle[1]; + wv2 = m_worldTrans*triangle[2]; + m_debugDrawer->drawLine(wv0,wv1,m_color); + m_debugDrawer->drawLine(wv1,wv2,m_color); + m_debugDrawer->drawLine(wv2,wv0,m_color); + } +}; + +void btDiscreteDynamicsWorld::debugDrawSphere(btScalar radius, const btTransform& transform, const btVector3& color) +{ + btVector3 start = transform.getOrigin(); + + const btVector3 xoffs = transform.getBasis() * btVector3(radius,0,0); + const btVector3 yoffs = transform.getBasis() * btVector3(0,radius,0); + const btVector3 zoffs = transform.getBasis() * btVector3(0,0,radius); + + // XY + getDebugDrawer()->drawLine(start-xoffs, start+yoffs, color); + getDebugDrawer()->drawLine(start+yoffs, start+xoffs, color); + getDebugDrawer()->drawLine(start+xoffs, start-yoffs, color); + getDebugDrawer()->drawLine(start-yoffs, start-xoffs, color); + + // XZ + getDebugDrawer()->drawLine(start-xoffs, start+zoffs, color); + getDebugDrawer()->drawLine(start+zoffs, start+xoffs, color); + getDebugDrawer()->drawLine(start+xoffs, start-zoffs, color); + getDebugDrawer()->drawLine(start-zoffs, start-xoffs, color); + + // YZ + getDebugDrawer()->drawLine(start-yoffs, start+zoffs, color); + getDebugDrawer()->drawLine(start+zoffs, start+yoffs, color); + getDebugDrawer()->drawLine(start+yoffs, start-zoffs, color); + getDebugDrawer()->drawLine(start-zoffs, start-yoffs, color); +} + +void btDiscreteDynamicsWorld::debugDrawObject(const btTransform& worldTransform, const btCollisionShape* shape, const btVector3& color) +{ + // Draw a small simplex at the center of the object + { + btVector3 start = worldTransform.getOrigin(); + getDebugDrawer()->drawLine(start, start+worldTransform.getBasis() * btVector3(1,0,0), btVector3(1,0,0)); + getDebugDrawer()->drawLine(start, start+worldTransform.getBasis() * btVector3(0,1,0), btVector3(0,1,0)); + getDebugDrawer()->drawLine(start, start+worldTransform.getBasis() * btVector3(0,0,1), btVector3(0,0,1)); + } + + if (shape->getShapeType() == COMPOUND_SHAPE_PROXYTYPE) + { + const btCompoundShape* compoundShape = static_cast(shape); + for (int i=compoundShape->getNumChildShapes()-1;i>=0;i--) + { + btTransform childTrans = compoundShape->getChildTransform(i); + const btCollisionShape* colShape = compoundShape->getChildShape(i); + debugDrawObject(worldTransform*childTrans,colShape,color); + } + + } else + { + switch (shape->getShapeType()) + { + + case SPHERE_SHAPE_PROXYTYPE: + { + const btSphereShape* sphereShape = static_cast(shape); + btScalar radius = sphereShape->getMargin();//radius doesn't include the margin, so draw with margin + + debugDrawSphere(radius, worldTransform, color); + break; + } + case MULTI_SPHERE_SHAPE_PROXYTYPE: + { + const btMultiSphereShape* multiSphereShape = static_cast(shape); + + btTransform childTransform; + childTransform.setIdentity(); + + for (int i = multiSphereShape->getSphereCount()-1; i>=0;i--) + { + childTransform.setOrigin(multiSphereShape->getSpherePosition(i)); + debugDrawSphere(multiSphereShape->getSphereRadius(i), worldTransform*childTransform, color); + } + + break; + } + case CAPSULE_SHAPE_PROXYTYPE: + { + const btCapsuleShape* capsuleShape = static_cast(shape); + + btScalar radius = capsuleShape->getRadius(); + btScalar halfHeight = capsuleShape->getHalfHeight(); + + int upAxis = capsuleShape->getUpAxis(); + + + btVector3 capStart(0.f,0.f,0.f); + capStart[upAxis] = -halfHeight; + + btVector3 capEnd(0.f,0.f,0.f); + capEnd[upAxis] = halfHeight; + + // Draw the ends + { + + btTransform childTransform = worldTransform; + childTransform.getOrigin() = worldTransform * capStart; + debugDrawSphere(radius, childTransform, color); + } + + { + btTransform childTransform = worldTransform; + childTransform.getOrigin() = worldTransform * capEnd; + debugDrawSphere(radius, childTransform, color); + } + + // Draw some additional lines + btVector3 start = worldTransform.getOrigin(); + + + capStart[(upAxis+1)%3] = radius; + capEnd[(upAxis+1)%3] = radius; + getDebugDrawer()->drawLine(start+worldTransform.getBasis() * capStart,start+worldTransform.getBasis() * capEnd, color); + capStart[(upAxis+1)%3] = -radius; + capEnd[(upAxis+1)%3] = -radius; + getDebugDrawer()->drawLine(start+worldTransform.getBasis() * capStart,start+worldTransform.getBasis() * capEnd, color); + + capStart[(upAxis+1)%3] = 0.f; + capEnd[(upAxis+1)%3] = 0.f; + + capStart[(upAxis+2)%3] = radius; + capEnd[(upAxis+2)%3] = radius; + getDebugDrawer()->drawLine(start+worldTransform.getBasis() * capStart,start+worldTransform.getBasis() * capEnd, color); + capStart[(upAxis+2)%3] = -radius; + capEnd[(upAxis+2)%3] = -radius; + getDebugDrawer()->drawLine(start+worldTransform.getBasis() * capStart,start+worldTransform.getBasis() * capEnd, color); + + + break; + } + case CONE_SHAPE_PROXYTYPE: + { + const btConeShape* coneShape = static_cast(shape); + btScalar radius = coneShape->getRadius();//+coneShape->getMargin(); + btScalar height = coneShape->getHeight();//+coneShape->getMargin(); + btVector3 start = worldTransform.getOrigin(); + + int upAxis= coneShape->getConeUpIndex(); + + + btVector3 offsetHeight(0,0,0); + offsetHeight[upAxis] = height * btScalar(0.5); + btVector3 offsetRadius(0,0,0); + offsetRadius[(upAxis+1)%3] = radius; + btVector3 offset2Radius(0,0,0); + offset2Radius[(upAxis+2)%3] = radius; + + getDebugDrawer()->drawLine(start+worldTransform.getBasis() * (offsetHeight),start+worldTransform.getBasis() * (-offsetHeight+offsetRadius),color); + getDebugDrawer()->drawLine(start+worldTransform.getBasis() * (offsetHeight),start+worldTransform.getBasis() * (-offsetHeight-offsetRadius),color); + getDebugDrawer()->drawLine(start+worldTransform.getBasis() * (offsetHeight),start+worldTransform.getBasis() * (-offsetHeight+offset2Radius),color); + getDebugDrawer()->drawLine(start+worldTransform.getBasis() * (offsetHeight),start+worldTransform.getBasis() * (-offsetHeight-offset2Radius),color); + + + + break; + + } + case CYLINDER_SHAPE_PROXYTYPE: + { + const btCylinderShape* cylinder = static_cast(shape); + int upAxis = cylinder->getUpAxis(); + btScalar radius = cylinder->getRadius(); + btScalar halfHeight = cylinder->getHalfExtentsWithMargin()[upAxis]; + btVector3 start = worldTransform.getOrigin(); + btVector3 offsetHeight(0,0,0); + offsetHeight[upAxis] = halfHeight; + btVector3 offsetRadius(0,0,0); + offsetRadius[(upAxis+1)%3] = radius; + getDebugDrawer()->drawLine(start+worldTransform.getBasis() * (offsetHeight+offsetRadius),start+worldTransform.getBasis() * (-offsetHeight+offsetRadius),color); + getDebugDrawer()->drawLine(start+worldTransform.getBasis() * (offsetHeight-offsetRadius),start+worldTransform.getBasis() * (-offsetHeight-offsetRadius),color); + break; + } + + case STATIC_PLANE_PROXYTYPE: + { + const btStaticPlaneShape* staticPlaneShape = static_cast(shape); + btScalar planeConst = staticPlaneShape->getPlaneConstant(); + const btVector3& planeNormal = staticPlaneShape->getPlaneNormal(); + btVector3 planeOrigin = planeNormal * planeConst; + btVector3 vec0,vec1; + btPlaneSpace1(planeNormal,vec0,vec1); + btScalar vecLen = 100.f; + btVector3 pt0 = planeOrigin + vec0*vecLen; + btVector3 pt1 = planeOrigin - vec0*vecLen; + btVector3 pt2 = planeOrigin + vec1*vecLen; + btVector3 pt3 = planeOrigin - vec1*vecLen; + getDebugDrawer()->drawLine(worldTransform*pt0,worldTransform*pt1,color); + getDebugDrawer()->drawLine(worldTransform*pt2,worldTransform*pt3,color); + break; + + } + default: + { + + if (shape->isConcave()) + { + btConcaveShape* concaveMesh = (btConcaveShape*) shape; + + ///@todo pass camera, for some culling? no -> we are not a graphics lib + btVector3 aabbMax(btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT)); + btVector3 aabbMin(btScalar(-BT_LARGE_FLOAT),btScalar(-BT_LARGE_FLOAT),btScalar(-BT_LARGE_FLOAT)); + + DebugDrawcallback drawCallback(getDebugDrawer(),worldTransform,color); + concaveMesh->processAllTriangles(&drawCallback,aabbMin,aabbMax); + + } + + if (shape->getShapeType() == CONVEX_TRIANGLEMESH_SHAPE_PROXYTYPE) + { + btConvexTriangleMeshShape* convexMesh = (btConvexTriangleMeshShape*) shape; + //todo: pass camera for some culling + btVector3 aabbMax(btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT),btScalar(BT_LARGE_FLOAT)); + btVector3 aabbMin(btScalar(-BT_LARGE_FLOAT),btScalar(-BT_LARGE_FLOAT),btScalar(-BT_LARGE_FLOAT)); + //DebugDrawcallback drawCallback; + DebugDrawcallback drawCallback(getDebugDrawer(),worldTransform,color); + convexMesh->getMeshInterface()->InternalProcessAllTriangles(&drawCallback,aabbMin,aabbMax); + } + + + /// for polyhedral shapes + if (shape->isPolyhedral()) + { + btPolyhedralConvexShape* polyshape = (btPolyhedralConvexShape*) shape; + + int i; + for (i=0;igetNumEdges();i++) + { + btVector3 a,b; + polyshape->getEdge(i,a,b); + btVector3 wa = worldTransform * a; + btVector3 wb = worldTransform * b; + getDebugDrawer()->drawLine(wa,wb,color); + + } + + + } + } + } + } +} + + +void btDiscreteDynamicsWorld::debugDrawConstraint(btTypedConstraint* constraint) +{ + bool drawFrames = (getDebugDrawer()->getDebugMode() & btIDebugDraw::DBG_DrawConstraints) != 0; + bool drawLimits = (getDebugDrawer()->getDebugMode() & btIDebugDraw::DBG_DrawConstraintLimits) != 0; + btScalar dbgDrawSize = constraint->getDbgDrawSize(); + if(dbgDrawSize <= btScalar(0.f)) + { + return; + } + + switch(constraint->getConstraintType()) + { + case POINT2POINT_CONSTRAINT_TYPE: + { + btPoint2PointConstraint* p2pC = (btPoint2PointConstraint*)constraint; + btTransform tr; + tr.setIdentity(); + btVector3 pivot = p2pC->getPivotInA(); + pivot = p2pC->getRigidBodyA().getCenterOfMassTransform() * pivot; + tr.setOrigin(pivot); + getDebugDrawer()->drawTransform(tr, dbgDrawSize); + // that ideally should draw the same frame + pivot = p2pC->getPivotInB(); + pivot = p2pC->getRigidBodyB().getCenterOfMassTransform() * pivot; + tr.setOrigin(pivot); + if(drawFrames) getDebugDrawer()->drawTransform(tr, dbgDrawSize); + } + break; + case HINGE_CONSTRAINT_TYPE: + { + btHingeConstraint* pHinge = (btHingeConstraint*)constraint; + btTransform tr = pHinge->getRigidBodyA().getCenterOfMassTransform() * pHinge->getAFrame(); + if(drawFrames) getDebugDrawer()->drawTransform(tr, dbgDrawSize); + tr = pHinge->getRigidBodyB().getCenterOfMassTransform() * pHinge->getBFrame(); + if(drawFrames) getDebugDrawer()->drawTransform(tr, dbgDrawSize); + btScalar minAng = pHinge->getLowerLimit(); + btScalar maxAng = pHinge->getUpperLimit(); + if(minAng == maxAng) + { + break; + } + bool drawSect = true; + if(minAng > maxAng) + { + minAng = btScalar(0.f); + maxAng = SIMD_2_PI; + drawSect = false; + } + if(drawLimits) + { + btVector3& center = tr.getOrigin(); + btVector3 normal = tr.getBasis().getColumn(2); + btVector3 axis = tr.getBasis().getColumn(0); + getDebugDrawer()->drawArc(center, normal, axis, dbgDrawSize, dbgDrawSize, minAng, maxAng, btVector3(0,0,0), drawSect); + } + } + break; + case CONETWIST_CONSTRAINT_TYPE: + { + btConeTwistConstraint* pCT = (btConeTwistConstraint*)constraint; + btTransform tr = pCT->getRigidBodyA().getCenterOfMassTransform() * pCT->getAFrame(); + if(drawFrames) getDebugDrawer()->drawTransform(tr, dbgDrawSize); + tr = pCT->getRigidBodyB().getCenterOfMassTransform() * pCT->getBFrame(); + if(drawFrames) getDebugDrawer()->drawTransform(tr, dbgDrawSize); + if(drawLimits) + { + //const btScalar length = btScalar(5); + const btScalar length = dbgDrawSize; + static int nSegments = 8*4; + btScalar fAngleInRadians = btScalar(2.*3.1415926) * (btScalar)(nSegments-1)/btScalar(nSegments); + btVector3 pPrev = pCT->GetPointForAngle(fAngleInRadians, length); + pPrev = tr * pPrev; + for (int i=0; iGetPointForAngle(fAngleInRadians, length); + pCur = tr * pCur; + getDebugDrawer()->drawLine(pPrev, pCur, btVector3(0,0,0)); + + if (i%(nSegments/8) == 0) + getDebugDrawer()->drawLine(tr.getOrigin(), pCur, btVector3(0,0,0)); + + pPrev = pCur; + } + btScalar tws = pCT->getTwistSpan(); + btScalar twa = pCT->getTwistAngle(); + bool useFrameB = (pCT->getRigidBodyB().getInvMass() > btScalar(0.f)); + if(useFrameB) + { + tr = pCT->getRigidBodyB().getCenterOfMassTransform() * pCT->getBFrame(); + } + else + { + tr = pCT->getRigidBodyA().getCenterOfMassTransform() * pCT->getAFrame(); + } + btVector3 pivot = tr.getOrigin(); + btVector3 normal = tr.getBasis().getColumn(0); + btVector3 axis1 = tr.getBasis().getColumn(1); + getDebugDrawer()->drawArc(pivot, normal, axis1, dbgDrawSize, dbgDrawSize, -twa-tws, -twa+tws, btVector3(0,0,0), true); + + } + } + break; + case D6_CONSTRAINT_TYPE: + { + btGeneric6DofConstraint* p6DOF = (btGeneric6DofConstraint*)constraint; + btTransform tr = p6DOF->getCalculatedTransformA(); + if(drawFrames) getDebugDrawer()->drawTransform(tr, dbgDrawSize); + tr = p6DOF->getCalculatedTransformB(); + if(drawFrames) getDebugDrawer()->drawTransform(tr, dbgDrawSize); + if(drawLimits) + { + tr = p6DOF->getCalculatedTransformA(); + const btVector3& center = p6DOF->getCalculatedTransformB().getOrigin(); + btVector3 up = tr.getBasis().getColumn(2); + btVector3 axis = tr.getBasis().getColumn(0); + btScalar minTh = p6DOF->getRotationalLimitMotor(1)->m_loLimit; + btScalar maxTh = p6DOF->getRotationalLimitMotor(1)->m_hiLimit; + btScalar minPs = p6DOF->getRotationalLimitMotor(2)->m_loLimit; + btScalar maxPs = p6DOF->getRotationalLimitMotor(2)->m_hiLimit; + getDebugDrawer()->drawSpherePatch(center, up, axis, dbgDrawSize * btScalar(.9f), minTh, maxTh, minPs, maxPs, btVector3(0,0,0)); + axis = tr.getBasis().getColumn(1); + btScalar ay = p6DOF->getAngle(1); + btScalar az = p6DOF->getAngle(2); + btScalar cy = btCos(ay); + btScalar sy = btSin(ay); + btScalar cz = btCos(az); + btScalar sz = btSin(az); + btVector3 ref; + ref[0] = cy*cz*axis[0] + cy*sz*axis[1] - sy*axis[2]; + ref[1] = -sz*axis[0] + cz*axis[1]; + ref[2] = cz*sy*axis[0] + sz*sy*axis[1] + cy*axis[2]; + tr = p6DOF->getCalculatedTransformB(); + btVector3 normal = -tr.getBasis().getColumn(0); + btScalar minFi = p6DOF->getRotationalLimitMotor(0)->m_loLimit; + btScalar maxFi = p6DOF->getRotationalLimitMotor(0)->m_hiLimit; + if(minFi > maxFi) + { + getDebugDrawer()->drawArc(center, normal, ref, dbgDrawSize, dbgDrawSize, -SIMD_PI, SIMD_PI, btVector3(0,0,0), false); + } + else if(minFi < maxFi) + { + getDebugDrawer()->drawArc(center, normal, ref, dbgDrawSize, dbgDrawSize, minFi, maxFi, btVector3(0,0,0), true); + } + tr = p6DOF->getCalculatedTransformA(); + btVector3 bbMin = p6DOF->getTranslationalLimitMotor()->m_lowerLimit; + btVector3 bbMax = p6DOF->getTranslationalLimitMotor()->m_upperLimit; + getDebugDrawer()->drawBox(bbMin, bbMax, tr, btVector3(0,0,0)); + } + } + break; + case SLIDER_CONSTRAINT_TYPE: + { + btSliderConstraint* pSlider = (btSliderConstraint*)constraint; + btTransform tr = pSlider->getCalculatedTransformA(); + if(drawFrames) getDebugDrawer()->drawTransform(tr, dbgDrawSize); + tr = pSlider->getCalculatedTransformB(); + if(drawFrames) getDebugDrawer()->drawTransform(tr, dbgDrawSize); + if(drawLimits) + { + btTransform tr = pSlider->getCalculatedTransformA(); + btVector3 li_min = tr * btVector3(pSlider->getLowerLinLimit(), 0.f, 0.f); + btVector3 li_max = tr * btVector3(pSlider->getUpperLinLimit(), 0.f, 0.f); + getDebugDrawer()->drawLine(li_min, li_max, btVector3(0, 0, 0)); + btVector3 normal = tr.getBasis().getColumn(0); + btVector3 axis = tr.getBasis().getColumn(1); + btScalar a_min = pSlider->getLowerAngLimit(); + btScalar a_max = pSlider->getUpperAngLimit(); + const btVector3& center = pSlider->getCalculatedTransformB().getOrigin(); + getDebugDrawer()->drawArc(center, normal, axis, dbgDrawSize, dbgDrawSize, a_min, a_max, btVector3(0,0,0), true); + } + } + break; + default : + break; + } + return; +} + + + + + +void btDiscreteDynamicsWorld::setConstraintSolver(btConstraintSolver* solver) +{ + if (m_ownsConstraintSolver) + { + btAlignedFree( m_constraintSolver); + } + m_ownsConstraintSolver = false; + m_constraintSolver = solver; +} + +btConstraintSolver* btDiscreteDynamicsWorld::getConstraintSolver() +{ + return m_constraintSolver; +} + + +int btDiscreteDynamicsWorld::getNumConstraints() const +{ + return int(m_constraints.size()); +} +btTypedConstraint* btDiscreteDynamicsWorld::getConstraint(int index) +{ + return m_constraints[index]; +} +const btTypedConstraint* btDiscreteDynamicsWorld::getConstraint(int index) const +{ + return m_constraints[index]; +} + + diff --git a/Engine/lib/bullet/src/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h new file mode 100644 index 000000000..23284a9b7 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h @@ -0,0 +1,197 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 BT_DISCRETE_DYNAMICS_WORLD_H +#define BT_DISCRETE_DYNAMICS_WORLD_H + +#include "btDynamicsWorld.h" + +class btDispatcher; +class btOverlappingPairCache; +class btConstraintSolver; +class btSimulationIslandManager; +class btTypedConstraint; +class btActionInterface; + +class btIDebugDraw; +#include "LinearMath/btAlignedObjectArray.h" + + +///btDiscreteDynamicsWorld provides discrete rigid body simulation +///those classes replace the obsolete CcdPhysicsEnvironment/CcdPhysicsController +class btDiscreteDynamicsWorld : public btDynamicsWorld +{ +protected: + + btConstraintSolver* m_constraintSolver; + + btSimulationIslandManager* m_islandManager; + + btAlignedObjectArray m_constraints; + + btAlignedObjectArray m_nonStaticRigidBodies; + + btVector3 m_gravity; + + //for variable timesteps + btScalar m_localTime; + //for variable timesteps + + bool m_ownsIslandManager; + bool m_ownsConstraintSolver; + bool m_synchronizeAllMotionStates; + + btAlignedObjectArray m_actions; + + int m_profileTimings; + + virtual void predictUnconstraintMotion(btScalar timeStep); + + virtual void integrateTransforms(btScalar timeStep); + + virtual void calculateSimulationIslands(); + + virtual void solveConstraints(btContactSolverInfo& solverInfo); + + void updateActivationState(btScalar timeStep); + + void updateActions(btScalar timeStep); + + void startProfiling(btScalar timeStep); + + virtual void internalSingleStepSimulation( btScalar timeStep); + + + virtual void saveKinematicState(btScalar timeStep); + + void debugDrawSphere(btScalar radius, const btTransform& transform, const btVector3& color); + + +public: + + + ///this btDiscreteDynamicsWorld constructor gets created objects from the user, and will not delete those + btDiscreteDynamicsWorld(btDispatcher* dispatcher,btBroadphaseInterface* pairCache,btConstraintSolver* constraintSolver,btCollisionConfiguration* collisionConfiguration); + + virtual ~btDiscreteDynamicsWorld(); + + ///if maxSubSteps > 0, it will interpolate motion between fixedTimeStep's + virtual int stepSimulation( btScalar timeStep,int maxSubSteps=1, btScalar fixedTimeStep=btScalar(1.)/btScalar(60.)); + + + virtual void synchronizeMotionStates(); + + ///this can be useful to synchronize a single rigid body -> graphics object + void synchronizeSingleMotionState(btRigidBody* body); + + virtual void addConstraint(btTypedConstraint* constraint, bool disableCollisionsBetweenLinkedBodies=false); + + virtual void removeConstraint(btTypedConstraint* constraint); + + virtual void addAction(btActionInterface*); + + virtual void removeAction(btActionInterface*); + + btSimulationIslandManager* getSimulationIslandManager() + { + return m_islandManager; + } + + const btSimulationIslandManager* getSimulationIslandManager() const + { + return m_islandManager; + } + + btCollisionWorld* getCollisionWorld() + { + return this; + } + + virtual void setGravity(const btVector3& gravity); + + virtual btVector3 getGravity () const; + + virtual void addCollisionObject(btCollisionObject* collisionObject,short int collisionFilterGroup=btBroadphaseProxy::StaticFilter,short int collisionFilterMask=btBroadphaseProxy::AllFilter ^ btBroadphaseProxy::StaticFilter); + + virtual void addRigidBody(btRigidBody* body); + + virtual void addRigidBody(btRigidBody* body, short group, short mask); + + virtual void removeRigidBody(btRigidBody* body); + + ///removeCollisionObject will first check if it is a rigid body, if so call removeRigidBody otherwise call btCollisionWorld::removeCollisionObject + virtual void removeCollisionObject(btCollisionObject* collisionObject); + + void debugDrawObject(const btTransform& worldTransform, const btCollisionShape* shape, const btVector3& color); + + void debugDrawConstraint(btTypedConstraint* constraint); + + virtual void debugDrawWorld(); + + virtual void setConstraintSolver(btConstraintSolver* solver); + + virtual btConstraintSolver* getConstraintSolver(); + + virtual int getNumConstraints() const; + + virtual btTypedConstraint* getConstraint(int index) ; + + virtual const btTypedConstraint* getConstraint(int index) const; + + + virtual btDynamicsWorldType getWorldType() const + { + return BT_DISCRETE_DYNAMICS_WORLD; + } + + ///the forces on each rigidbody is accumulating together with gravity. clear this after each timestep. + virtual void clearForces(); + + ///apply gravity, call this once per timestep + virtual void applyGravity(); + + virtual void setNumTasks(int numTasks) + { + (void) numTasks; + } + + ///obsolete, use updateActions instead + virtual void updateVehicles(btScalar timeStep) + { + updateActions(timeStep); + } + + ///obsolete, use addAction instead + virtual void addVehicle(btActionInterface* vehicle); + ///obsolete, use removeAction instead + virtual void removeVehicle(btActionInterface* vehicle); + ///obsolete, use addAction instead + virtual void addCharacter(btActionInterface* character); + ///obsolete, use removeAction instead + virtual void removeCharacter(btActionInterface* character); + + void setSynchronizeAllMotionStates(bool synchronizeAll) + { + m_synchronizeAllMotionStates = synchronizeAll; + } + bool getSynchronizeAllMotionStates() const + { + return m_synchronizeAllMotionStates; + } + +}; + +#endif //BT_DISCRETE_DYNAMICS_WORLD_H diff --git a/Engine/lib/bullet/src/BulletDynamics/Dynamics/btDynamicsWorld.h b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btDynamicsWorld.h new file mode 100644 index 000000000..a7b85afbe --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btDynamicsWorld.h @@ -0,0 +1,148 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 BT_DYNAMICS_WORLD_H +#define BT_DYNAMICS_WORLD_H + +#include "BulletCollision/CollisionDispatch/btCollisionWorld.h" +#include "BulletDynamics/ConstraintSolver/btContactSolverInfo.h" + +class btTypedConstraint; +class btActionInterface; +class btConstraintSolver; +class btDynamicsWorld; + + +/// Type for the callback for each tick +typedef void (*btInternalTickCallback)(btDynamicsWorld *world, btScalar timeStep); + +enum btDynamicsWorldType +{ + BT_SIMPLE_DYNAMICS_WORLD=1, + BT_DISCRETE_DYNAMICS_WORLD=2, + BT_CONTINUOUS_DYNAMICS_WORLD=3 +}; + +///The btDynamicsWorld is the interface class for several dynamics implementation, basic, discrete, parallel, and continuous etc. +class btDynamicsWorld : public btCollisionWorld +{ + +protected: + btInternalTickCallback m_internalTickCallback; + btInternalTickCallback m_internalPreTickCallback; + void* m_worldUserInfo; + + btContactSolverInfo m_solverInfo; + +public: + + + btDynamicsWorld(btDispatcher* dispatcher,btBroadphaseInterface* broadphase,btCollisionConfiguration* collisionConfiguration) + :btCollisionWorld(dispatcher,broadphase,collisionConfiguration), m_internalTickCallback(0),m_internalPreTickCallback(0), m_worldUserInfo(0) + { + } + + virtual ~btDynamicsWorld() + { + } + + ///stepSimulation proceeds the simulation over 'timeStep', units in preferably in seconds. + ///By default, Bullet will subdivide the timestep in constant substeps of each 'fixedTimeStep'. + ///in order to keep the simulation real-time, the maximum number of substeps can be clamped to 'maxSubSteps'. + ///You can disable subdividing the timestep/substepping by passing maxSubSteps=0 as second argument to stepSimulation, but in that case you have to keep the timeStep constant. + virtual int stepSimulation( btScalar timeStep,int maxSubSteps=1, btScalar fixedTimeStep=btScalar(1.)/btScalar(60.))=0; + + virtual void debugDrawWorld() = 0; + + virtual void addConstraint(btTypedConstraint* constraint, bool disableCollisionsBetweenLinkedBodies=false) + { + (void)constraint; (void)disableCollisionsBetweenLinkedBodies; + } + + virtual void removeConstraint(btTypedConstraint* constraint) {(void)constraint;} + + virtual void addAction(btActionInterface* action) = 0; + + virtual void removeAction(btActionInterface* action) = 0; + + //once a rigidbody is added to the dynamics world, it will get this gravity assigned + //existing rigidbodies in the world get gravity assigned too, during this method + virtual void setGravity(const btVector3& gravity) = 0; + virtual btVector3 getGravity () const = 0; + + virtual void synchronizeMotionStates() = 0; + + virtual void addRigidBody(btRigidBody* body) = 0; + + virtual void removeRigidBody(btRigidBody* body) = 0; + + virtual void setConstraintSolver(btConstraintSolver* solver) = 0; + + virtual btConstraintSolver* getConstraintSolver() = 0; + + virtual int getNumConstraints() const { return 0; } + + virtual btTypedConstraint* getConstraint(int index) { (void)index; return 0; } + + virtual const btTypedConstraint* getConstraint(int index) const { (void)index; return 0; } + + virtual btDynamicsWorldType getWorldType() const=0; + + virtual void clearForces() = 0; + + /// Set the callback for when an internal tick (simulation substep) happens, optional user info + void setInternalTickCallback(btInternalTickCallback cb, void* worldUserInfo=0,bool isPreTick=false) + { + if (isPreTick) + { + m_internalPreTickCallback = cb; + } else + { + m_internalTickCallback = cb; + } + m_worldUserInfo = worldUserInfo; + } + + void setWorldUserInfo(void* worldUserInfo) + { + m_worldUserInfo = worldUserInfo; + } + + void* getWorldUserInfo() const + { + return m_worldUserInfo; + } + + btContactSolverInfo& getSolverInfo() + { + return m_solverInfo; + } + + + ///obsolete, use addAction instead. + virtual void addVehicle(btActionInterface* vehicle) {(void)vehicle;} + ///obsolete, use removeAction instead + virtual void removeVehicle(btActionInterface* vehicle) {(void)vehicle;} + ///obsolete, use addAction instead. + virtual void addCharacter(btActionInterface* character) {(void)character;} + ///obsolete, use removeAction instead + virtual void removeCharacter(btActionInterface* character) {(void)character;} + + +}; + +#endif //BT_DYNAMICS_WORLD_H + + diff --git a/Engine/lib/bullet/src/BulletDynamics/Dynamics/btRigidBody.cpp b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btRigidBody.cpp new file mode 100644 index 000000000..a4d8e1d77 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btRigidBody.cpp @@ -0,0 +1,317 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btRigidBody.h" +#include "BulletCollision/CollisionShapes/btConvexShape.h" +#include "LinearMath/btMinMax.h" +#include "LinearMath/btTransformUtil.h" +#include "LinearMath/btMotionState.h" +#include "BulletDynamics/ConstraintSolver/btTypedConstraint.h" + +//'temporarily' global variables +btScalar gDeactivationTime = btScalar(2.); +bool gDisableDeactivation = false; +static int uniqueId = 0; + + +btRigidBody::btRigidBody(const btRigidBody::btRigidBodyConstructionInfo& constructionInfo) +{ + setupRigidBody(constructionInfo); +} + +btRigidBody::btRigidBody(btScalar mass, btMotionState *motionState, btCollisionShape *collisionShape, const btVector3 &localInertia) +{ + btRigidBodyConstructionInfo cinfo(mass,motionState,collisionShape,localInertia); + setupRigidBody(cinfo); +} + +void btRigidBody::setupRigidBody(const btRigidBody::btRigidBodyConstructionInfo& constructionInfo) +{ + + m_internalType=CO_RIGID_BODY; + + m_linearVelocity.setValue(btScalar(0.0), btScalar(0.0), btScalar(0.0)); + m_angularVelocity.setValue(btScalar(0.),btScalar(0.),btScalar(0.)); + m_angularFactor.setValue(1,1,1); + m_linearFactor.setValue(1,1,1); + m_gravity.setValue(btScalar(0.0), btScalar(0.0), btScalar(0.0)); + m_gravity_acceleration.setValue(btScalar(0.0), btScalar(0.0), btScalar(0.0)); + m_totalForce.setValue(btScalar(0.0), btScalar(0.0), btScalar(0.0)); + m_totalTorque.setValue(btScalar(0.0), btScalar(0.0), btScalar(0.0)), + m_linearDamping = btScalar(0.); + m_angularDamping = btScalar(0.5); + m_linearSleepingThreshold = constructionInfo.m_linearSleepingThreshold; + m_angularSleepingThreshold = constructionInfo.m_angularSleepingThreshold; + m_optionalMotionState = constructionInfo.m_motionState; + m_contactSolverType = 0; + m_frictionSolverType = 0; + m_additionalDamping = constructionInfo.m_additionalDamping; + m_additionalDampingFactor = constructionInfo.m_additionalDampingFactor; + m_additionalLinearDampingThresholdSqr = constructionInfo.m_additionalLinearDampingThresholdSqr; + m_additionalAngularDampingThresholdSqr = constructionInfo.m_additionalAngularDampingThresholdSqr; + m_additionalAngularDampingFactor = constructionInfo.m_additionalAngularDampingFactor; + + if (m_optionalMotionState) + { + m_optionalMotionState->getWorldTransform(m_worldTransform); + } else + { + m_worldTransform = constructionInfo.m_startWorldTransform; + } + + m_interpolationWorldTransform = m_worldTransform; + m_interpolationLinearVelocity.setValue(0,0,0); + m_interpolationAngularVelocity.setValue(0,0,0); + + //moved to btCollisionObject + m_friction = constructionInfo.m_friction; + m_restitution = constructionInfo.m_restitution; + + setCollisionShape( constructionInfo.m_collisionShape ); + m_debugBodyId = uniqueId++; + + setMassProps(constructionInfo.m_mass, constructionInfo.m_localInertia); + setDamping(constructionInfo.m_linearDamping, constructionInfo.m_angularDamping); + updateInertiaTensor(); + +} + + +void btRigidBody::predictIntegratedTransform(btScalar timeStep,btTransform& predictedTransform) +{ + btTransformUtil::integrateTransform(m_worldTransform,m_linearVelocity,m_angularVelocity,timeStep,predictedTransform); +} + +void btRigidBody::saveKinematicState(btScalar timeStep) +{ + //todo: clamp to some (user definable) safe minimum timestep, to limit maximum angular/linear velocities + if (timeStep != btScalar(0.)) + { + //if we use motionstate to synchronize world transforms, get the new kinematic/animated world transform + if (getMotionState()) + getMotionState()->getWorldTransform(m_worldTransform); + btVector3 linVel,angVel; + + btTransformUtil::calculateVelocity(m_interpolationWorldTransform,m_worldTransform,timeStep,m_linearVelocity,m_angularVelocity); + m_interpolationLinearVelocity = m_linearVelocity; + m_interpolationAngularVelocity = m_angularVelocity; + m_interpolationWorldTransform = m_worldTransform; + //printf("angular = %f %f %f\n",m_angularVelocity.getX(),m_angularVelocity.getY(),m_angularVelocity.getZ()); + } +} + +void btRigidBody::getAabb(btVector3& aabbMin,btVector3& aabbMax) const +{ + getCollisionShape()->getAabb(m_worldTransform,aabbMin,aabbMax); +} + + + + +void btRigidBody::setGravity(const btVector3& acceleration) +{ + if (m_inverseMass != btScalar(0.0)) + { + m_gravity = acceleration * (btScalar(1.0) / m_inverseMass); + } + m_gravity_acceleration = acceleration; +} + + + + + + +void btRigidBody::setDamping(btScalar lin_damping, btScalar ang_damping) +{ + m_linearDamping = GEN_clamped(lin_damping, (btScalar)btScalar(0.0), (btScalar)btScalar(1.0)); + m_angularDamping = GEN_clamped(ang_damping, (btScalar)btScalar(0.0), (btScalar)btScalar(1.0)); +} + + + + +///applyDamping damps the velocity, using the given m_linearDamping and m_angularDamping +void btRigidBody::applyDamping(btScalar timeStep) +{ + //On new damping: see discussion/issue report here: http://code.google.com/p/bullet/issues/detail?id=74 + //todo: do some performance comparisons (but other parts of the engine are probably bottleneck anyway + +//#define USE_OLD_DAMPING_METHOD 1 +#ifdef USE_OLD_DAMPING_METHOD + m_linearVelocity *= GEN_clamped((btScalar(1.) - timeStep * m_linearDamping), (btScalar)btScalar(0.0), (btScalar)btScalar(1.0)); + m_angularVelocity *= GEN_clamped((btScalar(1.) - timeStep * m_angularDamping), (btScalar)btScalar(0.0), (btScalar)btScalar(1.0)); +#else + m_linearVelocity *= btPow(btScalar(1)-m_linearDamping, timeStep); + m_angularVelocity *= btPow(btScalar(1)-m_angularDamping, timeStep); +#endif + + if (m_additionalDamping) + { + //Additional damping can help avoiding lowpass jitter motion, help stability for ragdolls etc. + //Such damping is undesirable, so once the overall simulation quality of the rigid body dynamics system has improved, this should become obsolete + if ((m_angularVelocity.length2() < m_additionalAngularDampingThresholdSqr) && + (m_linearVelocity.length2() < m_additionalLinearDampingThresholdSqr)) + { + m_angularVelocity *= m_additionalDampingFactor; + m_linearVelocity *= m_additionalDampingFactor; + } + + + btScalar speed = m_linearVelocity.length(); + if (speed < m_linearDamping) + { + btScalar dampVel = btScalar(0.005); + if (speed > dampVel) + { + btVector3 dir = m_linearVelocity.normalized(); + m_linearVelocity -= dir * dampVel; + } else + { + m_linearVelocity.setValue(btScalar(0.),btScalar(0.),btScalar(0.)); + } + } + + btScalar angSpeed = m_angularVelocity.length(); + if (angSpeed < m_angularDamping) + { + btScalar angDampVel = btScalar(0.005); + if (angSpeed > angDampVel) + { + btVector3 dir = m_angularVelocity.normalized(); + m_angularVelocity -= dir * angDampVel; + } else + { + m_angularVelocity.setValue(btScalar(0.),btScalar(0.),btScalar(0.)); + } + } + } +} + + +void btRigidBody::applyGravity() +{ + if (isStaticOrKinematicObject()) + return; + + applyCentralForce(m_gravity); + +} + +void btRigidBody::proceedToTransform(const btTransform& newTrans) +{ + setCenterOfMassTransform( newTrans ); +} + + +void btRigidBody::setMassProps(btScalar mass, const btVector3& inertia) +{ + if (mass == btScalar(0.)) + { + m_collisionFlags |= btCollisionObject::CF_STATIC_OBJECT; + m_inverseMass = btScalar(0.); + } else + { + m_collisionFlags &= (~btCollisionObject::CF_STATIC_OBJECT); + m_inverseMass = btScalar(1.0) / mass; + } + + m_invInertiaLocal.setValue(inertia.x() != btScalar(0.0) ? btScalar(1.0) / inertia.x(): btScalar(0.0), + inertia.y() != btScalar(0.0) ? btScalar(1.0) / inertia.y(): btScalar(0.0), + inertia.z() != btScalar(0.0) ? btScalar(1.0) / inertia.z(): btScalar(0.0)); + +} + + + +void btRigidBody::updateInertiaTensor() +{ + m_invInertiaTensorWorld = m_worldTransform.getBasis().scaled(m_invInertiaLocal) * m_worldTransform.getBasis().transpose(); +} + + +void btRigidBody::integrateVelocities(btScalar step) +{ + if (isStaticOrKinematicObject()) + return; + + m_linearVelocity += m_totalForce * (m_inverseMass * step); + m_angularVelocity += m_invInertiaTensorWorld * m_totalTorque * step; + +#define MAX_ANGVEL SIMD_HALF_PI + /// clamp angular velocity. collision calculations will fail on higher angular velocities + btScalar angvel = m_angularVelocity.length(); + if (angvel*step > MAX_ANGVEL) + { + m_angularVelocity *= (MAX_ANGVEL/step) /angvel; + } + +} + +btQuaternion btRigidBody::getOrientation() const +{ + btQuaternion orn; + m_worldTransform.getBasis().getRotation(orn); + return orn; +} + + +void btRigidBody::setCenterOfMassTransform(const btTransform& xform) +{ + + if (isStaticOrKinematicObject()) + { + m_interpolationWorldTransform = m_worldTransform; + } else + { + m_interpolationWorldTransform = xform; + } + m_interpolationLinearVelocity = getLinearVelocity(); + m_interpolationAngularVelocity = getAngularVelocity(); + m_worldTransform = xform; + updateInertiaTensor(); +} + + +bool btRigidBody::checkCollideWithOverride(btCollisionObject* co) +{ + btRigidBody* otherRb = btRigidBody::upcast(co); + if (!otherRb) + return true; + + for (int i = 0; i < m_constraintRefs.size(); ++i) + { + btTypedConstraint* c = m_constraintRefs[i]; + if (&c->getRigidBodyA() == otherRb || &c->getRigidBodyB() == otherRb) + return false; + } + + return true; +} + +void btRigidBody::addConstraintRef(btTypedConstraint* c) +{ + int index = m_constraintRefs.findLinearSearch(c); + if (index == m_constraintRefs.size()) + m_constraintRefs.push_back(c); + + m_checkCollideWith = true; +} + +void btRigidBody::removeConstraintRef(btTypedConstraint* c) +{ + m_constraintRefs.remove(c); + m_checkCollideWith = m_constraintRefs.size() > 0; +} diff --git a/Engine/lib/bullet/src/BulletDynamics/Dynamics/btRigidBody.h b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btRigidBody.h new file mode 100644 index 000000000..da1fcb786 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btRigidBody.h @@ -0,0 +1,503 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 RIGIDBODY_H +#define RIGIDBODY_H + +#include "LinearMath/btAlignedObjectArray.h" +#include "LinearMath/btTransform.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" + +class btCollisionShape; +class btMotionState; +class btTypedConstraint; + + +extern btScalar gDeactivationTime; +extern bool gDisableDeactivation; + + +///The btRigidBody is the main class for rigid body objects. It is derived from btCollisionObject, so it keeps a pointer to a btCollisionShape. +///It is recommended for performance and memory use to share btCollisionShape objects whenever possible. +///There are 3 types of rigid bodies: +///- A) Dynamic rigid bodies, with positive mass. Motion is controlled by rigid body dynamics. +///- B) Fixed objects with zero mass. They are not moving (basically collision objects) +///- C) Kinematic objects, which are objects without mass, but the user can move them. There is on-way interaction, and Bullet calculates a velocity based on the timestep and previous and current world transform. +///Bullet automatically deactivates dynamic rigid bodies, when the velocity is below a threshold for a given time. +///Deactivated (sleeping) rigid bodies don't take any processing time, except a minor broadphase collision detection impact (to allow active objects to activate/wake up sleeping objects) +class btRigidBody : public btCollisionObject +{ + + btMatrix3x3 m_invInertiaTensorWorld; + btVector3 m_linearVelocity; + btVector3 m_angularVelocity; + btScalar m_inverseMass; + btVector3 m_angularFactor; + btVector3 m_linearFactor; + + btVector3 m_gravity; + btVector3 m_gravity_acceleration; + btVector3 m_invInertiaLocal; + btVector3 m_totalForce; + btVector3 m_totalTorque; + + btScalar m_linearDamping; + btScalar m_angularDamping; + + bool m_additionalDamping; + btScalar m_additionalDampingFactor; + btScalar m_additionalLinearDampingThresholdSqr; + btScalar m_additionalAngularDampingThresholdSqr; + btScalar m_additionalAngularDampingFactor; + + + btScalar m_linearSleepingThreshold; + btScalar m_angularSleepingThreshold; + + //m_optionalMotionState allows to automatic synchronize the world transform for active objects + btMotionState* m_optionalMotionState; + + //keep track of typed constraints referencing this rigid body + btAlignedObjectArray m_constraintRefs; + +public: + + + ///The btRigidBodyConstructionInfo structure provides information to create a rigid body. Setting mass to zero creates a fixed (non-dynamic) rigid body. + ///For dynamic objects, you can use the collision shape to approximate the local inertia tensor, otherwise use the zero vector (default argument) + ///You can use the motion state to synchronize the world transform between physics and graphics objects. + ///And if the motion state is provided, the rigid body will initialize its initial world transform from the motion state, + ///m_startWorldTransform is only used when you don't provide a motion state. + struct btRigidBodyConstructionInfo + { + btScalar m_mass; + + ///When a motionState is provided, the rigid body will initialize its world transform from the motion state + ///In this case, m_startWorldTransform is ignored. + btMotionState* m_motionState; + btTransform m_startWorldTransform; + + btCollisionShape* m_collisionShape; + btVector3 m_localInertia; + btScalar m_linearDamping; + btScalar m_angularDamping; + + ///best simulation results when friction is non-zero + btScalar m_friction; + ///best simulation results using zero restitution. + btScalar m_restitution; + + btScalar m_linearSleepingThreshold; + btScalar m_angularSleepingThreshold; + + //Additional damping can help avoiding lowpass jitter motion, help stability for ragdolls etc. + //Such damping is undesirable, so once the overall simulation quality of the rigid body dynamics system has improved, this should become obsolete + bool m_additionalDamping; + btScalar m_additionalDampingFactor; + btScalar m_additionalLinearDampingThresholdSqr; + btScalar m_additionalAngularDampingThresholdSqr; + btScalar m_additionalAngularDampingFactor; + + + btRigidBodyConstructionInfo( btScalar mass, btMotionState* motionState, btCollisionShape* collisionShape, const btVector3& localInertia=btVector3(0,0,0)): + m_mass(mass), + m_motionState(motionState), + m_collisionShape(collisionShape), + m_localInertia(localInertia), + m_linearDamping(btScalar(0.)), + m_angularDamping(btScalar(0.)), + m_friction(btScalar(0.5)), + m_restitution(btScalar(0.)), + m_linearSleepingThreshold(btScalar(0.8)), + m_angularSleepingThreshold(btScalar(1.f)), + m_additionalDamping(false), + m_additionalDampingFactor(btScalar(0.005)), + m_additionalLinearDampingThresholdSqr(btScalar(0.01)), + m_additionalAngularDampingThresholdSqr(btScalar(0.01)), + m_additionalAngularDampingFactor(btScalar(0.01)) + { + m_startWorldTransform.setIdentity(); + } + }; + + ///btRigidBody constructor using construction info + btRigidBody( const btRigidBodyConstructionInfo& constructionInfo); + + ///btRigidBody constructor for backwards compatibility. + ///To specify friction (etc) during rigid body construction, please use the other constructor (using btRigidBodyConstructionInfo) + btRigidBody( btScalar mass, btMotionState* motionState, btCollisionShape* collisionShape, const btVector3& localInertia=btVector3(0,0,0)); + + + virtual ~btRigidBody() + { + //No constraints should point to this rigidbody + //Remove constraints from the dynamics world before you delete the related rigidbodies. + btAssert(m_constraintRefs.size()==0); + } + +protected: + + ///setupRigidBody is only used internally by the constructor + void setupRigidBody(const btRigidBodyConstructionInfo& constructionInfo); + +public: + + void proceedToTransform(const btTransform& newTrans); + + ///to keep collision detection and dynamics separate we don't store a rigidbody pointer + ///but a rigidbody is derived from btCollisionObject, so we can safely perform an upcast + static const btRigidBody* upcast(const btCollisionObject* colObj) + { + if (colObj->getInternalType()==btCollisionObject::CO_RIGID_BODY) + return (const btRigidBody*)colObj; + return 0; + } + static btRigidBody* upcast(btCollisionObject* colObj) + { + if (colObj->getInternalType()==btCollisionObject::CO_RIGID_BODY) + return (btRigidBody*)colObj; + return 0; + } + + /// continuous collision detection needs prediction + void predictIntegratedTransform(btScalar step, btTransform& predictedTransform) ; + + void saveKinematicState(btScalar step); + + void applyGravity(); + + void setGravity(const btVector3& acceleration); + + const btVector3& getGravity() const + { + return m_gravity_acceleration; + } + + void setDamping(btScalar lin_damping, btScalar ang_damping); + + btScalar getLinearDamping() const + { + return m_linearDamping; + } + + btScalar getAngularDamping() const + { + return m_angularDamping; + } + + btScalar getLinearSleepingThreshold() const + { + return m_linearSleepingThreshold; + } + + btScalar getAngularSleepingThreshold() const + { + return m_angularSleepingThreshold; + } + + void applyDamping(btScalar timeStep); + + SIMD_FORCE_INLINE const btCollisionShape* getCollisionShape() const { + return m_collisionShape; + } + + SIMD_FORCE_INLINE btCollisionShape* getCollisionShape() { + return m_collisionShape; + } + + void setMassProps(btScalar mass, const btVector3& inertia); + + const btVector3& getLinearFactor() const + { + return m_linearFactor; + } + void setLinearFactor(const btVector3& linearFactor) + { + m_linearFactor = linearFactor; + } + btScalar getInvMass() const { return m_inverseMass; } + const btMatrix3x3& getInvInertiaTensorWorld() const { + return m_invInertiaTensorWorld; + } + + void integrateVelocities(btScalar step); + + void setCenterOfMassTransform(const btTransform& xform); + + void applyCentralForce(const btVector3& force) + { + m_totalForce += force*m_linearFactor; + } + + const btVector3& getTotalForce() + { + return m_totalForce; + }; + + const btVector3& getTotalTorque() + { + return m_totalTorque; + }; + + const btVector3& getInvInertiaDiagLocal() const + { + return m_invInertiaLocal; + }; + + void setInvInertiaDiagLocal(const btVector3& diagInvInertia) + { + m_invInertiaLocal = diagInvInertia; + } + + void setSleepingThresholds(btScalar linear,btScalar angular) + { + m_linearSleepingThreshold = linear; + m_angularSleepingThreshold = angular; + } + + void applyTorque(const btVector3& torque) + { + m_totalTorque += torque*m_angularFactor; + } + + void applyForce(const btVector3& force, const btVector3& rel_pos) + { + applyCentralForce(force); + applyTorque(rel_pos.cross(force*m_linearFactor)); + } + + void applyCentralImpulse(const btVector3& impulse) + { + m_linearVelocity += impulse *m_linearFactor * m_inverseMass; + } + + void applyTorqueImpulse(const btVector3& torque) + { + m_angularVelocity += m_invInertiaTensorWorld * torque * m_angularFactor; + } + + void applyImpulse(const btVector3& impulse, const btVector3& rel_pos) + { + if (m_inverseMass != btScalar(0.)) + { + applyCentralImpulse(impulse); + if (m_angularFactor) + { + applyTorqueImpulse(rel_pos.cross(impulse*m_linearFactor)); + } + } + } + + //Optimization for the iterative solver: avoid calculating constant terms involving inertia, normal, relative position + SIMD_FORCE_INLINE void internalApplyImpulse(const btVector3& linearComponent, const btVector3& angularComponent,btScalar impulseMagnitude) + { + if (m_inverseMass != btScalar(0.)) + { + m_linearVelocity += linearComponent*m_linearFactor*impulseMagnitude; + if (m_angularFactor) + { + m_angularVelocity += angularComponent*m_angularFactor*impulseMagnitude; + } + } + } + + void clearForces() + { + m_totalForce.setValue(btScalar(0.0), btScalar(0.0), btScalar(0.0)); + m_totalTorque.setValue(btScalar(0.0), btScalar(0.0), btScalar(0.0)); + } + + void updateInertiaTensor(); + + const btVector3& getCenterOfMassPosition() const { + return m_worldTransform.getOrigin(); + } + btQuaternion getOrientation() const; + + const btTransform& getCenterOfMassTransform() const { + return m_worldTransform; + } + const btVector3& getLinearVelocity() const { + return m_linearVelocity; + } + const btVector3& getAngularVelocity() const { + return m_angularVelocity; + } + + + inline void setLinearVelocity(const btVector3& lin_vel) + { + m_linearVelocity = lin_vel; + } + + inline void setAngularVelocity(const btVector3& ang_vel) + { + m_angularVelocity = ang_vel; + } + + btVector3 getVelocityInLocalPoint(const btVector3& rel_pos) const + { + //we also calculate lin/ang velocity for kinematic objects + return m_linearVelocity + m_angularVelocity.cross(rel_pos); + + //for kinematic objects, we could also use use: + // return (m_worldTransform(rel_pos) - m_interpolationWorldTransform(rel_pos)) / m_kinematicTimeStep; + } + + void translate(const btVector3& v) + { + m_worldTransform.getOrigin() += v; + } + + + void getAabb(btVector3& aabbMin,btVector3& aabbMax) const; + + + + + + SIMD_FORCE_INLINE btScalar computeImpulseDenominator(const btVector3& pos, const btVector3& normal) const + { + btVector3 r0 = pos - getCenterOfMassPosition(); + + btVector3 c0 = (r0).cross(normal); + + btVector3 vec = (c0 * getInvInertiaTensorWorld()).cross(r0); + + return m_inverseMass + normal.dot(vec); + + } + + SIMD_FORCE_INLINE btScalar computeAngularImpulseDenominator(const btVector3& axis) const + { + btVector3 vec = axis * getInvInertiaTensorWorld(); + return axis.dot(vec); + } + + SIMD_FORCE_INLINE void updateDeactivation(btScalar timeStep) + { + if ( (getActivationState() == ISLAND_SLEEPING) || (getActivationState() == DISABLE_DEACTIVATION)) + return; + + if ((getLinearVelocity().length2() < m_linearSleepingThreshold*m_linearSleepingThreshold) && + (getAngularVelocity().length2() < m_angularSleepingThreshold*m_angularSleepingThreshold)) + { + m_deactivationTime += timeStep; + } else + { + m_deactivationTime=btScalar(0.); + setActivationState(0); + } + + } + + SIMD_FORCE_INLINE bool wantsSleeping() + { + + if (getActivationState() == DISABLE_DEACTIVATION) + return false; + + //disable deactivation + if (gDisableDeactivation || (gDeactivationTime == btScalar(0.))) + return false; + + if ( (getActivationState() == ISLAND_SLEEPING) || (getActivationState() == WANTS_DEACTIVATION)) + return true; + + if (m_deactivationTime> gDeactivationTime) + { + return true; + } + return false; + } + + + + const btBroadphaseProxy* getBroadphaseProxy() const + { + return m_broadphaseHandle; + } + btBroadphaseProxy* getBroadphaseProxy() + { + return m_broadphaseHandle; + } + void setNewBroadphaseProxy(btBroadphaseProxy* broadphaseProxy) + { + m_broadphaseHandle = broadphaseProxy; + } + + //btMotionState allows to automatic synchronize the world transform for active objects + btMotionState* getMotionState() + { + return m_optionalMotionState; + } + const btMotionState* getMotionState() const + { + return m_optionalMotionState; + } + void setMotionState(btMotionState* motionState) + { + m_optionalMotionState = motionState; + if (m_optionalMotionState) + motionState->getWorldTransform(m_worldTransform); + } + + //for experimental overriding of friction/contact solver func + int m_contactSolverType; + int m_frictionSolverType; + + void setAngularFactor(const btVector3& angFac) + { + m_angularFactor = angFac; + } + + void setAngularFactor(btScalar angFac) + { + m_angularFactor.setValue(angFac,angFac,angFac); + } + const btVector3& getAngularFactor() const + { + return m_angularFactor; + } + + //is this rigidbody added to a btCollisionWorld/btDynamicsWorld/btBroadphase? + bool isInWorld() const + { + return (getBroadphaseProxy() != 0); + } + + virtual bool checkCollideWithOverride(btCollisionObject* co); + + void addConstraintRef(btTypedConstraint* c); + void removeConstraintRef(btTypedConstraint* c); + + btTypedConstraint* getConstraintRef(int index) + { + return m_constraintRefs[index]; + } + + int getNumConstraintRefs() + { + return m_constraintRefs.size(); + } + + int m_debugBodyId; +}; + + + +#endif + diff --git a/Engine/lib/bullet/src/BulletDynamics/Dynamics/btSimpleDynamicsWorld.cpp b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btSimpleDynamicsWorld.cpp new file mode 100644 index 000000000..ae449f292 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btSimpleDynamicsWorld.cpp @@ -0,0 +1,253 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btSimpleDynamicsWorld.h" +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/BroadphaseCollision/btSimpleBroadphase.h" +#include "BulletCollision/CollisionShapes/btCollisionShape.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include "BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h" +#include "BulletDynamics/ConstraintSolver/btContactSolverInfo.h" + + +/* + Make sure this dummy function never changes so that it + can be used by probes that are checking whether the + library is actually installed. +*/ +extern "C" +{ + void btBulletDynamicsProbe (); + void btBulletDynamicsProbe () {} +} + + + + +btSimpleDynamicsWorld::btSimpleDynamicsWorld(btDispatcher* dispatcher,btBroadphaseInterface* pairCache,btConstraintSolver* constraintSolver,btCollisionConfiguration* collisionConfiguration) +:btDynamicsWorld(dispatcher,pairCache,collisionConfiguration), +m_constraintSolver(constraintSolver), +m_ownsConstraintSolver(false), +m_gravity(0,0,-10) +{ + +} + + +btSimpleDynamicsWorld::~btSimpleDynamicsWorld() +{ + if (m_ownsConstraintSolver) + btAlignedFree( m_constraintSolver); +} + +int btSimpleDynamicsWorld::stepSimulation( btScalar timeStep,int maxSubSteps, btScalar fixedTimeStep) +{ + (void)fixedTimeStep; + (void)maxSubSteps; + + + ///apply gravity, predict motion + predictUnconstraintMotion(timeStep); + + btDispatcherInfo& dispatchInfo = getDispatchInfo(); + dispatchInfo.m_timeStep = timeStep; + dispatchInfo.m_stepCount = 0; + dispatchInfo.m_debugDraw = getDebugDrawer(); + + ///perform collision detection + performDiscreteCollisionDetection(); + + ///solve contact constraints + int numManifolds = m_dispatcher1->getNumManifolds(); + if (numManifolds) + { + btPersistentManifold** manifoldPtr = ((btCollisionDispatcher*)m_dispatcher1)->getInternalManifoldPointer(); + + btContactSolverInfo infoGlobal; + infoGlobal.m_timeStep = timeStep; + m_constraintSolver->prepareSolve(0,numManifolds); + m_constraintSolver->solveGroup(0,0,manifoldPtr, numManifolds,0,0,infoGlobal,m_debugDrawer, m_stackAlloc,m_dispatcher1); + m_constraintSolver->allSolved(infoGlobal,m_debugDrawer, m_stackAlloc); + } + + ///integrate transforms + integrateTransforms(timeStep); + + updateAabbs(); + + synchronizeMotionStates(); + + clearForces(); + + return 1; + +} + +void btSimpleDynamicsWorld::clearForces() +{ + ///@todo: iterate over awake simulation islands! + for ( int i=0;iclearForces(); + } + } +} + + +void btSimpleDynamicsWorld::setGravity(const btVector3& gravity) +{ + m_gravity = gravity; + for ( int i=0;isetGravity(gravity); + } + } +} + +btVector3 btSimpleDynamicsWorld::getGravity () const +{ + return m_gravity; +} + +void btSimpleDynamicsWorld::removeRigidBody(btRigidBody* body) +{ + btCollisionWorld::removeCollisionObject(body); +} + +void btSimpleDynamicsWorld::removeCollisionObject(btCollisionObject* collisionObject) +{ + btRigidBody* body = btRigidBody::upcast(collisionObject); + if (body) + removeRigidBody(body); + else + btCollisionWorld::removeCollisionObject(collisionObject); +} + + +void btSimpleDynamicsWorld::addRigidBody(btRigidBody* body) +{ + body->setGravity(m_gravity); + + if (body->getCollisionShape()) + { + addCollisionObject(body); + } +} + +void btSimpleDynamicsWorld::updateAabbs() +{ + btTransform predictedTrans; + for ( int i=0;iisActive() && (!body->isStaticObject())) + { + btVector3 minAabb,maxAabb; + colObj->getCollisionShape()->getAabb(colObj->getWorldTransform(), minAabb,maxAabb); + btBroadphaseInterface* bp = getBroadphase(); + bp->setAabb(body->getBroadphaseHandle(),minAabb,maxAabb, m_dispatcher1); + } + } + } +} + +void btSimpleDynamicsWorld::integrateTransforms(btScalar timeStep) +{ + btTransform predictedTrans; + for ( int i=0;iisActive() && (!body->isStaticObject())) + { + body->predictIntegratedTransform(timeStep, predictedTrans); + body->proceedToTransform( predictedTrans); + } + } + } +} + + + +void btSimpleDynamicsWorld::predictUnconstraintMotion(btScalar timeStep) +{ + for ( int i=0;iisStaticObject()) + { + if (body->isActive()) + { + body->applyGravity(); + body->integrateVelocities( timeStep); + body->applyDamping(timeStep); + body->predictIntegratedTransform(timeStep,body->getInterpolationWorldTransform()); + } + } + } + } +} + + +void btSimpleDynamicsWorld::synchronizeMotionStates() +{ + ///@todo: iterate over awake simulation islands! + for ( int i=0;igetMotionState()) + { + if (body->getActivationState() != ISLAND_SLEEPING) + { + body->getMotionState()->setWorldTransform(body->getWorldTransform()); + } + } + } + +} + + +void btSimpleDynamicsWorld::setConstraintSolver(btConstraintSolver* solver) +{ + if (m_ownsConstraintSolver) + { + btAlignedFree(m_constraintSolver); + } + m_ownsConstraintSolver = false; + m_constraintSolver = solver; +} + +btConstraintSolver* btSimpleDynamicsWorld::getConstraintSolver() +{ + return m_constraintSolver; +} diff --git a/Engine/lib/bullet/src/BulletDynamics/Dynamics/btSimpleDynamicsWorld.h b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btSimpleDynamicsWorld.h new file mode 100644 index 000000000..ad1f54134 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btSimpleDynamicsWorld.h @@ -0,0 +1,81 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 BT_SIMPLE_DYNAMICS_WORLD_H +#define BT_SIMPLE_DYNAMICS_WORLD_H + +#include "btDynamicsWorld.h" + +class btDispatcher; +class btOverlappingPairCache; +class btConstraintSolver; + +///The btSimpleDynamicsWorld serves as unit-test and to verify more complicated and optimized dynamics worlds. +///Please use btDiscreteDynamicsWorld instead (or btContinuousDynamicsWorld once it is finished). +class btSimpleDynamicsWorld : public btDynamicsWorld +{ +protected: + + btConstraintSolver* m_constraintSolver; + + bool m_ownsConstraintSolver; + + void predictUnconstraintMotion(btScalar timeStep); + + void integrateTransforms(btScalar timeStep); + + btVector3 m_gravity; + +public: + + + + ///this btSimpleDynamicsWorld constructor creates dispatcher, broadphase pairCache and constraintSolver + btSimpleDynamicsWorld(btDispatcher* dispatcher,btBroadphaseInterface* pairCache,btConstraintSolver* constraintSolver,btCollisionConfiguration* collisionConfiguration); + + virtual ~btSimpleDynamicsWorld(); + + ///maxSubSteps/fixedTimeStep for interpolation is currently ignored for btSimpleDynamicsWorld, use btDiscreteDynamicsWorld instead + virtual int stepSimulation( btScalar timeStep,int maxSubSteps=1, btScalar fixedTimeStep=btScalar(1.)/btScalar(60.)); + + virtual void setGravity(const btVector3& gravity); + + virtual btVector3 getGravity () const; + + virtual void addRigidBody(btRigidBody* body); + + virtual void removeRigidBody(btRigidBody* body); + + ///removeCollisionObject will first check if it is a rigid body, if so call removeRigidBody otherwise call btCollisionWorld::removeCollisionObject + virtual void removeCollisionObject(btCollisionObject* collisionObject); + + virtual void updateAabbs(); + + virtual void synchronizeMotionStates(); + + virtual void setConstraintSolver(btConstraintSolver* solver); + + virtual btConstraintSolver* getConstraintSolver(); + + virtual btDynamicsWorldType getWorldType() const + { + return BT_SIMPLE_DYNAMICS_WORLD; + } + + virtual void clearForces(); + +}; + +#endif //BT_SIMPLE_DYNAMICS_WORLD_H diff --git a/Engine/lib/bullet/src/BulletDynamics/Jamfile b/Engine/lib/bullet/src/BulletDynamics/Jamfile new file mode 100644 index 000000000..b4c52b230 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Jamfile @@ -0,0 +1,13 @@ + +SubDir TOP src BulletDynamics ; + +Description bulletdynamics : "Bullet Rigidbody Dynamics" ; +Library bulletdynamics : + [ Wildcard ConstraintSolver : *.h *.cpp ] + [ Wildcard Dynamics : *.h *.cpp ] + [ Wildcard Vehicle : *.h *.cpp ] + [ Wildcard Character : *.h *.cpp ] +; + +LibDepends bulletdynamics : bulletcollision ; + diff --git a/Engine/lib/bullet/src/BulletDynamics/Vehicle/btRaycastVehicle.cpp b/Engine/lib/bullet/src/BulletDynamics/Vehicle/btRaycastVehicle.cpp new file mode 100644 index 000000000..031fcb5b4 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Vehicle/btRaycastVehicle.cpp @@ -0,0 +1,758 @@ +/* + * Copyright (c) 2005 Erwin Coumans http://continuousphysics.com/Bullet/ + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies. + * Erwin Coumans makes no representations about the suitability + * of this software for any purpose. + * It is provided "as is" without express or implied warranty. +*/ + +#include "LinearMath/btVector3.h" +#include "btRaycastVehicle.h" + +#include "BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.h" +#include "BulletDynamics/ConstraintSolver/btJacobianEntry.h" +#include "LinearMath/btQuaternion.h" +#include "BulletDynamics/Dynamics/btDynamicsWorld.h" +#include "btVehicleRaycaster.h" +#include "btWheelInfo.h" +#include "LinearMath/btMinMax.h" +#include "LinearMath/btIDebugDraw.h" +#include "BulletDynamics/ConstraintSolver/btContactConstraint.h" + +static btRigidBody s_fixedObject( 0,0,0); + +btRaycastVehicle::btRaycastVehicle(const btVehicleTuning& tuning,btRigidBody* chassis, btVehicleRaycaster* raycaster ) +:m_vehicleRaycaster(raycaster), +m_pitchControl(btScalar(0.)) +{ + m_chassisBody = chassis; + m_indexRightAxis = 0; + m_indexUpAxis = 2; + m_indexForwardAxis = 1; + defaultInit(tuning); +} + + +void btRaycastVehicle::defaultInit(const btVehicleTuning& tuning) +{ + (void)tuning; + m_currentVehicleSpeedKmHour = btScalar(0.); + m_steeringValue = btScalar(0.); + +} + + + +btRaycastVehicle::~btRaycastVehicle() +{ +} + + +// +// basically most of the code is general for 2 or 4 wheel vehicles, but some of it needs to be reviewed +// +btWheelInfo& btRaycastVehicle::addWheel( const btVector3& connectionPointCS, const btVector3& wheelDirectionCS0,const btVector3& wheelAxleCS, btScalar suspensionRestLength, btScalar wheelRadius,const btVehicleTuning& tuning, bool isFrontWheel) +{ + + btWheelInfoConstructionInfo ci; + + ci.m_chassisConnectionCS = connectionPointCS; + ci.m_wheelDirectionCS = wheelDirectionCS0; + ci.m_wheelAxleCS = wheelAxleCS; + ci.m_suspensionRestLength = suspensionRestLength; + ci.m_wheelRadius = wheelRadius; + ci.m_suspensionStiffness = tuning.m_suspensionStiffness; + ci.m_wheelsDampingCompression = tuning.m_suspensionCompression; + ci.m_wheelsDampingRelaxation = tuning.m_suspensionDamping; + ci.m_frictionSlip = tuning.m_frictionSlip; + ci.m_bIsFrontWheel = isFrontWheel; + ci.m_maxSuspensionTravelCm = tuning.m_maxSuspensionTravelCm; + + m_wheelInfo.push_back( btWheelInfo(ci)); + + btWheelInfo& wheel = m_wheelInfo[getNumWheels()-1]; + + updateWheelTransformsWS( wheel , false ); + updateWheelTransform(getNumWheels()-1,false); + return wheel; +} + + + + +const btTransform& btRaycastVehicle::getWheelTransformWS( int wheelIndex ) const +{ + btAssert(wheelIndex < getNumWheels()); + const btWheelInfo& wheel = m_wheelInfo[wheelIndex]; + return wheel.m_worldTransform; + +} + +void btRaycastVehicle::updateWheelTransform( int wheelIndex , bool interpolatedTransform) +{ + + btWheelInfo& wheel = m_wheelInfo[ wheelIndex ]; + updateWheelTransformsWS(wheel,interpolatedTransform); + btVector3 up = -wheel.m_raycastInfo.m_wheelDirectionWS; + const btVector3& right = wheel.m_raycastInfo.m_wheelAxleWS; + btVector3 fwd = up.cross(right); + fwd = fwd.normalize(); +// up = right.cross(fwd); +// up.normalize(); + + //rotate around steering over de wheelAxleWS + btScalar steering = wheel.m_steering; + + btQuaternion steeringOrn(up,steering);//wheel.m_steering); + btMatrix3x3 steeringMat(steeringOrn); + + btQuaternion rotatingOrn(right,-wheel.m_rotation); + btMatrix3x3 rotatingMat(rotatingOrn); + + btMatrix3x3 basis2( + right[0],fwd[0],up[0], + right[1],fwd[1],up[1], + right[2],fwd[2],up[2] + ); + + wheel.m_worldTransform.setBasis(steeringMat * rotatingMat * basis2); + wheel.m_worldTransform.setOrigin( + wheel.m_raycastInfo.m_hardPointWS + wheel.m_raycastInfo.m_wheelDirectionWS * wheel.m_raycastInfo.m_suspensionLength + ); +} + +void btRaycastVehicle::resetSuspension() +{ + + int i; + for (i=0;igetMotionState())) + { + getRigidBody()->getMotionState()->getWorldTransform(chassisTrans); + } + + wheel.m_raycastInfo.m_hardPointWS = chassisTrans( wheel.m_chassisConnectionPointCS ); + wheel.m_raycastInfo.m_wheelDirectionWS = chassisTrans.getBasis() * wheel.m_wheelDirectionCS ; + wheel.m_raycastInfo.m_wheelAxleWS = chassisTrans.getBasis() * wheel.m_wheelAxleCS; +} + +btScalar btRaycastVehicle::rayCast(btWheelInfo& wheel) +{ + updateWheelTransformsWS( wheel,false); + + + btScalar depth = -1; + + btScalar raylen = wheel.getSuspensionRestLength()+wheel.m_wheelsRadius; + + btVector3 rayvector = wheel.m_raycastInfo.m_wheelDirectionWS * (raylen); + const btVector3& source = wheel.m_raycastInfo.m_hardPointWS; + wheel.m_raycastInfo.m_contactPointWS = source + rayvector; + const btVector3& target = wheel.m_raycastInfo.m_contactPointWS; + + btScalar param = btScalar(0.); + + btVehicleRaycaster::btVehicleRaycasterResult rayResults; + + btAssert(m_vehicleRaycaster); + + void* object = m_vehicleRaycaster->castRay(source,target,rayResults); + + wheel.m_raycastInfo.m_groundObject = 0; + + if (object) + { + param = rayResults.m_distFraction; + depth = raylen * rayResults.m_distFraction; + wheel.m_raycastInfo.m_contactNormalWS = rayResults.m_hitNormalInWorld; + wheel.m_raycastInfo.m_isInContact = true; + + wheel.m_raycastInfo.m_groundObject = &s_fixedObject;///@todo for driving on dynamic/movable objects!; + //wheel.m_raycastInfo.m_groundObject = object; + + + btScalar hitDistance = param*raylen; + wheel.m_raycastInfo.m_suspensionLength = hitDistance - wheel.m_wheelsRadius; + //clamp on max suspension travel + + btScalar minSuspensionLength = wheel.getSuspensionRestLength() - wheel.m_maxSuspensionTravelCm*btScalar(0.01); + btScalar maxSuspensionLength = wheel.getSuspensionRestLength()+ wheel.m_maxSuspensionTravelCm*btScalar(0.01); + if (wheel.m_raycastInfo.m_suspensionLength < minSuspensionLength) + { + wheel.m_raycastInfo.m_suspensionLength = minSuspensionLength; + } + if (wheel.m_raycastInfo.m_suspensionLength > maxSuspensionLength) + { + wheel.m_raycastInfo.m_suspensionLength = maxSuspensionLength; + } + + wheel.m_raycastInfo.m_contactPointWS = rayResults.m_hitPointInWorld; + + btScalar denominator= wheel.m_raycastInfo.m_contactNormalWS.dot( wheel.m_raycastInfo.m_wheelDirectionWS ); + + btVector3 chassis_velocity_at_contactPoint; + btVector3 relpos = wheel.m_raycastInfo.m_contactPointWS-getRigidBody()->getCenterOfMassPosition(); + + chassis_velocity_at_contactPoint = getRigidBody()->getVelocityInLocalPoint(relpos); + + btScalar projVel = wheel.m_raycastInfo.m_contactNormalWS.dot( chassis_velocity_at_contactPoint ); + + if ( denominator >= btScalar(-0.1)) + { + wheel.m_suspensionRelativeVelocity = btScalar(0.0); + wheel.m_clippedInvContactDotSuspension = btScalar(1.0) / btScalar(0.1); + } + else + { + btScalar inv = btScalar(-1.) / denominator; + wheel.m_suspensionRelativeVelocity = projVel * inv; + wheel.m_clippedInvContactDotSuspension = inv; + } + + } else + { + //put wheel info as in rest position + wheel.m_raycastInfo.m_suspensionLength = wheel.getSuspensionRestLength(); + wheel.m_suspensionRelativeVelocity = btScalar(0.0); + wheel.m_raycastInfo.m_contactNormalWS = - wheel.m_raycastInfo.m_wheelDirectionWS; + wheel.m_clippedInvContactDotSuspension = btScalar(1.0); + } + + return depth; +} + + +const btTransform& btRaycastVehicle::getChassisWorldTransform() const +{ + /*if (getRigidBody()->getMotionState()) + { + btTransform chassisWorldTrans; + getRigidBody()->getMotionState()->getWorldTransform(chassisWorldTrans); + return chassisWorldTrans; + } + */ + + + return getRigidBody()->getCenterOfMassTransform(); +} + + +void btRaycastVehicle::updateVehicle( btScalar step ) +{ + { + for (int i=0;igetLinearVelocity().length(); + + const btTransform& chassisTrans = getChassisWorldTransform(); + + btVector3 forwardW ( + chassisTrans.getBasis()[0][m_indexForwardAxis], + chassisTrans.getBasis()[1][m_indexForwardAxis], + chassisTrans.getBasis()[2][m_indexForwardAxis]); + + if (forwardW.dot(getRigidBody()->getLinearVelocity()) < btScalar(0.)) + { + m_currentVehicleSpeedKmHour *= btScalar(-1.); + } + + // + // simulate suspension + // + + int i=0; + for (i=0;i gMaxSuspensionForce) + { + suspensionForce = gMaxSuspensionForce; + } + btVector3 impulse = wheel.m_raycastInfo.m_contactNormalWS * suspensionForce * step; + btVector3 relpos = wheel.m_raycastInfo.m_contactPointWS - getRigidBody()->getCenterOfMassPosition(); + + getRigidBody()->applyImpulse(impulse, relpos); + + } + + + + updateFriction( step); + + + for (i=0;igetCenterOfMassPosition(); + btVector3 vel = getRigidBody()->getVelocityInLocalPoint( relpos ); + + if (wheel.m_raycastInfo.m_isInContact) + { + const btTransform& chassisWorldTransform = getChassisWorldTransform(); + + btVector3 fwd ( + chassisWorldTransform.getBasis()[0][m_indexForwardAxis], + chassisWorldTransform.getBasis()[1][m_indexForwardAxis], + chassisWorldTransform.getBasis()[2][m_indexForwardAxis]); + + btScalar proj = fwd.dot(wheel.m_raycastInfo.m_contactNormalWS); + fwd -= wheel.m_raycastInfo.m_contactNormalWS * proj; + + btScalar proj2 = fwd.dot(vel); + + wheel.m_deltaRotation = (proj2 * step) / (wheel.m_wheelsRadius); + wheel.m_rotation += wheel.m_deltaRotation; + + } else + { + wheel.m_rotation += wheel.m_deltaRotation; + } + + wheel.m_deltaRotation *= btScalar(0.99);//damping of rotation when not in contact + + } + + + +} + + +void btRaycastVehicle::setSteeringValue(btScalar steering,int wheel) +{ + btAssert(wheel>=0 && wheel < getNumWheels()); + + btWheelInfo& wheelInfo = getWheelInfo(wheel); + wheelInfo.m_steering = steering; +} + + + +btScalar btRaycastVehicle::getSteeringValue(int wheel) const +{ + return getWheelInfo(wheel).m_steering; +} + + +void btRaycastVehicle::applyEngineForce(btScalar force, int wheel) +{ + btAssert(wheel>=0 && wheel < getNumWheels()); + btWheelInfo& wheelInfo = getWheelInfo(wheel); + wheelInfo.m_engineForce = force; +} + + +const btWheelInfo& btRaycastVehicle::getWheelInfo(int index) const +{ + btAssert((index >= 0) && (index < getNumWheels())); + + return m_wheelInfo[index]; +} + +btWheelInfo& btRaycastVehicle::getWheelInfo(int index) +{ + btAssert((index >= 0) && (index < getNumWheels())); + + return m_wheelInfo[index]; +} + +void btRaycastVehicle::setBrake(btScalar brake,int wheelIndex) +{ + btAssert((wheelIndex >= 0) && (wheelIndex < getNumWheels())); + getWheelInfo(wheelIndex).m_brake = brake; +} + + +void btRaycastVehicle::updateSuspension(btScalar deltaTime) +{ + (void)deltaTime; + + btScalar chassisMass = btScalar(1.) / m_chassisBody->getInvMass(); + + for (int w_it=0; w_itcomputeImpulseDenominator(frictionPosWorld,frictionDirectionWorld); + btScalar denom1 = body1->computeImpulseDenominator(frictionPosWorld,frictionDirectionWorld); + btScalar relaxation = 1.f; + m_jacDiagABInv = relaxation/(denom0+denom1); + } + + + +}; + +btScalar calcRollingFriction(btWheelContactPoint& contactPoint); +btScalar calcRollingFriction(btWheelContactPoint& contactPoint) +{ + + btScalar j1=0.f; + + const btVector3& contactPosWorld = contactPoint.m_frictionPositionWorld; + + btVector3 rel_pos1 = contactPosWorld - contactPoint.m_body0->getCenterOfMassPosition(); + btVector3 rel_pos2 = contactPosWorld - contactPoint.m_body1->getCenterOfMassPosition(); + + btScalar maxImpulse = contactPoint.m_maxImpulse; + + btVector3 vel1 = contactPoint.m_body0->getVelocityInLocalPoint(rel_pos1); + btVector3 vel2 = contactPoint.m_body1->getVelocityInLocalPoint(rel_pos2); + btVector3 vel = vel1 - vel2; + + btScalar vrel = contactPoint.m_frictionDirectionWorld.dot(vel); + + // calculate j that moves us to zero relative velocity + j1 = -vrel * contactPoint.m_jacDiagABInv; + btSetMin(j1, maxImpulse); + btSetMax(j1, -maxImpulse); + + return j1; +} + + + + +btScalar sideFrictionStiffness2 = btScalar(1.0); +void btRaycastVehicle::updateFriction(btScalar timeStep) +{ + + //calculate the impulse, so that the wheels don't move sidewards + int numWheel = getNumWheels(); + if (!numWheel) + return; + + m_forwardWS.resize(numWheel); + m_axle.resize(numWheel); + m_forwardImpulse.resize(numWheel); + m_sideImpulse.resize(numWheel); + + int numWheelsOnGround = 0; + + + //collapse all those loops into one! + for (int i=0;i maximpSquared) + { + sliding = true; + + btScalar factor = maximp / btSqrt(impulseSquared); + + m_wheelInfo[wheel].m_skidInfo *= factor; + } + } + + } + } + + + + + if (sliding) + { + for (int wheel = 0;wheel < getNumWheels(); wheel++) + { + if (m_sideImpulse[wheel] != btScalar(0.)) + { + if (m_wheelInfo[wheel].m_skidInfo< btScalar(1.)) + { + m_forwardImpulse[wheel] *= m_wheelInfo[wheel].m_skidInfo; + m_sideImpulse[wheel] *= m_wheelInfo[wheel].m_skidInfo; + } + } + } + } + + // apply the impulses + { + for (int wheel = 0;wheelgetCenterOfMassPosition(); + + if (m_forwardImpulse[wheel] != btScalar(0.)) + { + m_chassisBody->applyImpulse(m_forwardWS[wheel]*(m_forwardImpulse[wheel]),rel_pos); + } + if (m_sideImpulse[wheel] != btScalar(0.)) + { + class btRigidBody* groundObject = (class btRigidBody*) m_wheelInfo[wheel].m_raycastInfo.m_groundObject; + + btVector3 rel_pos2 = wheelInfo.m_raycastInfo.m_contactPointWS - + groundObject->getCenterOfMassPosition(); + + + btVector3 sideImp = m_axle[wheel] * m_sideImpulse[wheel]; + + rel_pos[m_indexUpAxis] *= wheelInfo.m_rollInfluence; + m_chassisBody->applyImpulse(sideImp,rel_pos); + + //apply friction impulse on the ground + groundObject->applyImpulse(-sideImp,rel_pos2); + } + } + } + + +} + + + +void btRaycastVehicle::debugDraw(btIDebugDraw* debugDrawer) +{ + + for (int v=0;vgetNumWheels();v++) + { + btVector3 wheelColor(0,255,255); + if (getWheelInfo(v).m_raycastInfo.m_isInContact) + { + wheelColor.setValue(0,0,255); + } else + { + wheelColor.setValue(255,0,255); + } + + btVector3 wheelPosWS = getWheelInfo(v).m_worldTransform.getOrigin(); + + btVector3 axle = btVector3( + getWheelInfo(v).m_worldTransform.getBasis()[0][getRightAxis()], + getWheelInfo(v).m_worldTransform.getBasis()[1][getRightAxis()], + getWheelInfo(v).m_worldTransform.getBasis()[2][getRightAxis()]); + + //debug wheels (cylinders) + debugDrawer->drawLine(wheelPosWS,wheelPosWS+axle,wheelColor); + debugDrawer->drawLine(wheelPosWS,getWheelInfo(v).m_raycastInfo.m_contactPointWS,wheelColor); + + } +} + + +void* btDefaultVehicleRaycaster::castRay(const btVector3& from,const btVector3& to, btVehicleRaycasterResult& result) +{ +// RayResultCallback& resultCallback; + + btCollisionWorld::ClosestRayResultCallback rayCallback(from,to); + + m_dynamicsWorld->rayTest(from, to, rayCallback); + + if (rayCallback.hasHit()) + { + + btRigidBody* body = btRigidBody::upcast(rayCallback.m_collisionObject); + if (body && body->hasContactResponse()) + { + result.m_hitPointInWorld = rayCallback.m_hitPointWorld; + result.m_hitNormalInWorld = rayCallback.m_hitNormalWorld; + result.m_hitNormalInWorld.normalize(); + result.m_distFraction = rayCallback.m_closestHitFraction; + return body; + } + } + return 0; +} + diff --git a/Engine/lib/bullet/src/BulletDynamics/Vehicle/btRaycastVehicle.h b/Engine/lib/bullet/src/BulletDynamics/Vehicle/btRaycastVehicle.h new file mode 100644 index 000000000..58eef98d2 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Vehicle/btRaycastVehicle.h @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2005 Erwin Coumans http://continuousphysics.com/Bullet/ + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies. + * Erwin Coumans makes no representations about the suitability + * of this software for any purpose. + * It is provided "as is" without express or implied warranty. +*/ +#ifndef RAYCASTVEHICLE_H +#define RAYCASTVEHICLE_H + +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include "BulletDynamics/ConstraintSolver/btTypedConstraint.h" +#include "btVehicleRaycaster.h" +class btDynamicsWorld; +#include "LinearMath/btAlignedObjectArray.h" +#include "btWheelInfo.h" +#include "BulletDynamics/Dynamics/btActionInterface.h" + +class btVehicleTuning; + +///rayCast vehicle, very special constraint that turn a rigidbody into a vehicle. +class btRaycastVehicle : public btActionInterface +{ + + btAlignedObjectArray m_forwardWS; + btAlignedObjectArray m_axle; + btAlignedObjectArray m_forwardImpulse; + btAlignedObjectArray m_sideImpulse; + +public: + class btVehicleTuning + { + public: + + btVehicleTuning() + :m_suspensionStiffness(btScalar(5.88)), + m_suspensionCompression(btScalar(0.83)), + m_suspensionDamping(btScalar(0.88)), + m_maxSuspensionTravelCm(btScalar(500.)), + m_frictionSlip(btScalar(10.5)) + { + } + btScalar m_suspensionStiffness; + btScalar m_suspensionCompression; + btScalar m_suspensionDamping; + btScalar m_maxSuspensionTravelCm; + btScalar m_frictionSlip; + + }; +private: + + btScalar m_tau; + btScalar m_damping; + btVehicleRaycaster* m_vehicleRaycaster; + btScalar m_pitchControl; + btScalar m_steeringValue; + btScalar m_currentVehicleSpeedKmHour; + + btRigidBody* m_chassisBody; + + int m_indexRightAxis; + int m_indexUpAxis; + int m_indexForwardAxis; + + void defaultInit(const btVehicleTuning& tuning); + +public: + + //constructor to create a car from an existing rigidbody + btRaycastVehicle(const btVehicleTuning& tuning,btRigidBody* chassis, btVehicleRaycaster* raycaster ); + + virtual ~btRaycastVehicle() ; + + + ///btActionInterface interface + virtual void updateAction( btCollisionWorld* collisionWorld, btScalar step) + { + updateVehicle(step); + } + + + ///btActionInterface interface + void debugDraw(btIDebugDraw* debugDrawer); + + const btTransform& getChassisWorldTransform() const; + + btScalar rayCast(btWheelInfo& wheel); + + virtual void updateVehicle(btScalar step); + + + void resetSuspension(); + + btScalar getSteeringValue(int wheel) const; + + void setSteeringValue(btScalar steering,int wheel); + + + void applyEngineForce(btScalar force, int wheel); + + const btTransform& getWheelTransformWS( int wheelIndex ) const; + + void updateWheelTransform( int wheelIndex, bool interpolatedTransform = true ); + + void setRaycastWheelInfo( int wheelIndex , bool isInContact, const btVector3& hitPoint, const btVector3& hitNormal,btScalar depth); + + btWheelInfo& addWheel( const btVector3& connectionPointCS0, const btVector3& wheelDirectionCS0,const btVector3& wheelAxleCS,btScalar suspensionRestLength,btScalar wheelRadius,const btVehicleTuning& tuning, bool isFrontWheel); + + inline int getNumWheels() const { + return int (m_wheelInfo.size()); + } + + btAlignedObjectArray m_wheelInfo; + + + const btWheelInfo& getWheelInfo(int index) const; + + btWheelInfo& getWheelInfo(int index); + + void updateWheelTransformsWS(btWheelInfo& wheel , bool interpolatedTransform = true); + + + void setBrake(btScalar brake,int wheelIndex); + + void setPitchControl(btScalar pitch) + { + m_pitchControl = pitch; + } + + void updateSuspension(btScalar deltaTime); + + virtual void updateFriction(btScalar timeStep); + + + + inline btRigidBody* getRigidBody() + { + return m_chassisBody; + } + + const btRigidBody* getRigidBody() const + { + return m_chassisBody; + } + + inline int getRightAxis() const + { + return m_indexRightAxis; + } + inline int getUpAxis() const + { + return m_indexUpAxis; + } + + inline int getForwardAxis() const + { + return m_indexForwardAxis; + } + + + ///Worldspace forward vector + btVector3 getForwardVector() const + { + const btTransform& chassisTrans = getChassisWorldTransform(); + + btVector3 forwardW ( + chassisTrans.getBasis()[0][m_indexForwardAxis], + chassisTrans.getBasis()[1][m_indexForwardAxis], + chassisTrans.getBasis()[2][m_indexForwardAxis]); + + return forwardW; + } + + ///Velocity of vehicle (positive if velocity vector has same direction as foward vector) + btScalar getCurrentSpeedKmHour() const + { + return m_currentVehicleSpeedKmHour; + } + + virtual void setCoordinateSystem(int rightIndex,int upIndex,int forwardIndex) + { + m_indexRightAxis = rightIndex; + m_indexUpAxis = upIndex; + m_indexForwardAxis = forwardIndex; + } + + + +}; + +class btDefaultVehicleRaycaster : public btVehicleRaycaster +{ + btDynamicsWorld* m_dynamicsWorld; +public: + btDefaultVehicleRaycaster(btDynamicsWorld* world) + :m_dynamicsWorld(world) + { + } + + virtual void* castRay(const btVector3& from,const btVector3& to, btVehicleRaycasterResult& result); + +}; + + +#endif //RAYCASTVEHICLE_H + diff --git a/Engine/lib/bullet/src/BulletDynamics/Vehicle/btVehicleRaycaster.h b/Engine/lib/bullet/src/BulletDynamics/Vehicle/btVehicleRaycaster.h new file mode 100644 index 000000000..5112ce6d4 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Vehicle/btVehicleRaycaster.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2005 Erwin Coumans http://continuousphysics.com/Bullet/ + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies. + * Erwin Coumans makes no representations about the suitability + * of this software for any purpose. + * It is provided "as is" without express or implied warranty. +*/ +#ifndef VEHICLE_RAYCASTER_H +#define VEHICLE_RAYCASTER_H + +#include "LinearMath/btVector3.h" + +/// btVehicleRaycaster is provides interface for between vehicle simulation and raycasting +struct btVehicleRaycaster +{ +virtual ~btVehicleRaycaster() +{ +} + struct btVehicleRaycasterResult + { + btVehicleRaycasterResult() :m_distFraction(btScalar(-1.)){}; + btVector3 m_hitPointInWorld; + btVector3 m_hitNormalInWorld; + btScalar m_distFraction; + }; + + virtual void* castRay(const btVector3& from,const btVector3& to, btVehicleRaycasterResult& result) = 0; + +}; + +#endif //VEHICLE_RAYCASTER_H + diff --git a/Engine/lib/bullet/src/BulletDynamics/Vehicle/btWheelInfo.cpp b/Engine/lib/bullet/src/BulletDynamics/Vehicle/btWheelInfo.cpp new file mode 100644 index 000000000..ef93c16ff --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Vehicle/btWheelInfo.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2005 Erwin Coumans http://continuousphysics.com/Bullet/ + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies. + * Erwin Coumans makes no representations about the suitability + * of this software for any purpose. + * It is provided "as is" without express or implied warranty. +*/ +#include "btWheelInfo.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" // for pointvelocity + + +btScalar btWheelInfo::getSuspensionRestLength() const +{ + + return m_suspensionRestLength1; + +} + +void btWheelInfo::updateWheel(const btRigidBody& chassis,RaycastInfo& raycastInfo) +{ + (void)raycastInfo; + + + if (m_raycastInfo.m_isInContact) + + { + btScalar project= m_raycastInfo.m_contactNormalWS.dot( m_raycastInfo.m_wheelDirectionWS ); + btVector3 chassis_velocity_at_contactPoint; + btVector3 relpos = m_raycastInfo.m_contactPointWS - chassis.getCenterOfMassPosition(); + chassis_velocity_at_contactPoint = chassis.getVelocityInLocalPoint( relpos ); + btScalar projVel = m_raycastInfo.m_contactNormalWS.dot( chassis_velocity_at_contactPoint ); + if ( project >= btScalar(-0.1)) + { + m_suspensionRelativeVelocity = btScalar(0.0); + m_clippedInvContactDotSuspension = btScalar(1.0) / btScalar(0.1); + } + else + { + btScalar inv = btScalar(-1.) / project; + m_suspensionRelativeVelocity = projVel * inv; + m_clippedInvContactDotSuspension = inv; + } + + } + + else // Not in contact : position wheel in a nice (rest length) position + { + m_raycastInfo.m_suspensionLength = this->getSuspensionRestLength(); + m_suspensionRelativeVelocity = btScalar(0.0); + m_raycastInfo.m_contactNormalWS = -m_raycastInfo.m_wheelDirectionWS; + m_clippedInvContactDotSuspension = btScalar(1.0); + } +} diff --git a/Engine/lib/bullet/src/BulletDynamics/Vehicle/btWheelInfo.h b/Engine/lib/bullet/src/BulletDynamics/Vehicle/btWheelInfo.h new file mode 100644 index 000000000..ac2729f4f --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Vehicle/btWheelInfo.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2005 Erwin Coumans http://continuousphysics.com/Bullet/ + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies. + * Erwin Coumans makes no representations about the suitability + * of this software for any purpose. + * It is provided "as is" without express or implied warranty. +*/ +#ifndef WHEEL_INFO_H +#define WHEEL_INFO_H + +#include "LinearMath/btVector3.h" +#include "LinearMath/btTransform.h" + +class btRigidBody; + +struct btWheelInfoConstructionInfo +{ + btVector3 m_chassisConnectionCS; + btVector3 m_wheelDirectionCS; + btVector3 m_wheelAxleCS; + btScalar m_suspensionRestLength; + btScalar m_maxSuspensionTravelCm; + btScalar m_wheelRadius; + + btScalar m_suspensionStiffness; + btScalar m_wheelsDampingCompression; + btScalar m_wheelsDampingRelaxation; + btScalar m_frictionSlip; + bool m_bIsFrontWheel; + +}; + +/// btWheelInfo contains information per wheel about friction and suspension. +struct btWheelInfo +{ + struct RaycastInfo + { + //set by raycaster + btVector3 m_contactNormalWS;//contactnormal + btVector3 m_contactPointWS;//raycast hitpoint + btScalar m_suspensionLength; + btVector3 m_hardPointWS;//raycast starting point + btVector3 m_wheelDirectionWS; //direction in worldspace + btVector3 m_wheelAxleWS; // axle in worldspace + bool m_isInContact; + void* m_groundObject; //could be general void* ptr + }; + + RaycastInfo m_raycastInfo; + + btTransform m_worldTransform; + + btVector3 m_chassisConnectionPointCS; //const + btVector3 m_wheelDirectionCS;//const + btVector3 m_wheelAxleCS; // const or modified by steering + btScalar m_suspensionRestLength1;//const + btScalar m_maxSuspensionTravelCm; + btScalar getSuspensionRestLength() const; + btScalar m_wheelsRadius;//const + btScalar m_suspensionStiffness;//const + btScalar m_wheelsDampingCompression;//const + btScalar m_wheelsDampingRelaxation;//const + btScalar m_frictionSlip; + btScalar m_steering; + btScalar m_rotation; + btScalar m_deltaRotation; + btScalar m_rollInfluence; + + btScalar m_engineForce; + + btScalar m_brake; + + bool m_bIsFrontWheel; + + void* m_clientInfo;//can be used to store pointer to sync transforms... + + btWheelInfo(btWheelInfoConstructionInfo& ci) + + { + + m_suspensionRestLength1 = ci.m_suspensionRestLength; + m_maxSuspensionTravelCm = ci.m_maxSuspensionTravelCm; + + m_wheelsRadius = ci.m_wheelRadius; + m_suspensionStiffness = ci.m_suspensionStiffness; + m_wheelsDampingCompression = ci.m_wheelsDampingCompression; + m_wheelsDampingRelaxation = ci.m_wheelsDampingRelaxation; + m_chassisConnectionPointCS = ci.m_chassisConnectionCS; + m_wheelDirectionCS = ci.m_wheelDirectionCS; + m_wheelAxleCS = ci.m_wheelAxleCS; + m_frictionSlip = ci.m_frictionSlip; + m_steering = btScalar(0.); + m_engineForce = btScalar(0.); + m_rotation = btScalar(0.); + m_deltaRotation = btScalar(0.); + m_brake = btScalar(0.); + m_rollInfluence = btScalar(0.1); + m_bIsFrontWheel = ci.m_bIsFrontWheel; + + } + + void updateWheel(const btRigidBody& chassis,RaycastInfo& raycastInfo); + + btScalar m_clippedInvContactDotSuspension; + btScalar m_suspensionRelativeVelocity; + //calculated by suspension + btScalar m_wheelsSuspensionForce; + btScalar m_skidInfo; + +}; + +#endif //WHEEL_INFO_H + diff --git a/Engine/lib/bullet/src/BulletDynamics/ibmsdk/Makefile b/Engine/lib/bullet/src/BulletDynamics/ibmsdk/Makefile new file mode 100644 index 000000000..57505d835 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ibmsdk/Makefile @@ -0,0 +1,53 @@ +#### Source code Dirs +VPATH = \ +../ConstraintSolver \ +../Dynamics \ +../Vehicle + +ROOT = ../../.. + +#### Library +LIBRARY_ppu = bulletdynamics.a + +#### Compiler flags +CPPFLAGS = \ +-DUSE_LIBSPE2 \ +-I../ConstraintSolver \ +-I../Dynamics \ +-I../Vehicle \ +-I$(ROOT)/src \ +-I$(SDKINC) + +#### Optimization level flags +#CC_OPT_LEVEL = $(CC_OPT_LEVEL_DEBUG) +CC_OPT_LEVEL = -O3 + +##### Objects to be archived in lib + +OBJS = \ +btContactConstraint.o \ +btGeneric6DofConstraint.o \ +btHingeConstraint.o \ +btPoint2PointConstraint.o \ +btSequentialImpulseConstraintSolver.o \ +btSolve2LinearConstraint.o \ +btTypedConstraint.o \ +btDiscreteDynamicsWorld.o \ +btRigidBody.o \ +btSimpleDynamicsWorld.o \ +btRaycastVehicle.o \ +btWheelInfo.o +#### Install directories +INSTALL_DIR = $(ROOT)/lib/ibmsdk +INSTALL_FILES = $(LIBRARY_ppu) + +IBM_CELLSDK_VERSION := $(shell if [ -d /opt/cell ]; then echo "3.0"; fi) + +ifeq ("$(IBM_CELLSDK_VERSION)","3.0") + CELL_TOP ?= /opt/cell/sdk + include $(CELL_TOP)/buildutils/make.footer +else + CELL_TOP ?= /opt/ibm/cell-sdk/prototype + include $(CELL_TOP)/make.footer +endif + diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/CMakeLists.txt b/Engine/lib/bullet/src/BulletMultiThreaded/CMakeLists.txt new file mode 100644 index 000000000..31fbd0411 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/CMakeLists.txt @@ -0,0 +1,71 @@ +INCLUDE_DIRECTORIES( + ${BULLET_PHYSICS_SOURCE_DIR}/src + ${BULLET_PHYSICS_SOURCE_DIR}/src/BulletMultiThreaded/vectormath/scalar/cpp +) + +ADD_LIBRARY(BulletMultiThreaded + PlatformDefinitions.h + SpuFakeDma.cpp + SpuFakeDma.h + SpuSync.h + SpuDoubleBuffer.h + SpuLibspe2Support.cpp + SpuLibspe2Support.h + btThreadSupportInterface.cpp + btThreadSupportInterface.h + + Win32ThreadSupport.cpp + Win32ThreadSupport.h + PosixThreadSupport.cpp + PosixThreadSupport.h + SequentialThreadSupport.cpp + SequentialThreadSupport.h + SpuSampleTaskProcess.h + SpuSampleTaskProcess.cpp + + SpuCollisionObjectWrapper.cpp + SpuCollisionObjectWrapper.h + SpuCollisionTaskProcess.h + SpuCollisionTaskProcess.cpp + SpuGatheringCollisionDispatcher.h + SpuGatheringCollisionDispatcher.cpp + SpuContactManifoldCollisionAlgorithm.cpp + SpuContactManifoldCollisionAlgorithm.h + SpuNarrowPhaseCollisionTask/Box.h + SpuNarrowPhaseCollisionTask/boxBoxDistance.cpp + SpuNarrowPhaseCollisionTask/boxBoxDistance.h + SpuNarrowPhaseCollisionTask/SpuContactResult.cpp + SpuNarrowPhaseCollisionTask/SpuContactResult.h + SpuNarrowPhaseCollisionTask/SpuMinkowskiPenetrationDepthSolver.cpp + SpuNarrowPhaseCollisionTask/SpuMinkowskiPenetrationDepthSolver.h + SpuNarrowPhaseCollisionTask/SpuConvexPenetrationDepthSolver.h + SpuNarrowPhaseCollisionTask/SpuPreferredPenetrationDirections.h + SpuNarrowPhaseCollisionTask/SpuGatheringCollisionTask.cpp + SpuNarrowPhaseCollisionTask/SpuGatheringCollisionTask.h + SpuNarrowPhaseCollisionTask/SpuCollisionShapes.cpp + SpuNarrowPhaseCollisionTask/SpuCollisionShapes.h + + +#Some GPU related stuff, mainly CUDA and perhaps OpenCL + btGpu3DGridBroadphase.cpp + btGpu3DGridBroadphase.h + btGpu3DGridBroadphaseSharedCode.h + btGpu3DGridBroadphaseSharedDefs.h + btGpu3DGridBroadphaseSharedTypes.h + btGpuDefines.h + btGpuUtilsSharedCode.h + btGpuUtilsSharedDefs.h + +#MiniCL provides a small subset of OpenCL + MiniCLTaskScheduler.cpp + MiniCLTaskScheduler.h + MiniCLTask/MiniCLTask.cpp + MiniCLTask/MiniCLTask.h + ../MiniCL/cl.h + ../MiniCL/cl_gl.h + ../MiniCL/cl_platform.h +) + +IF (BUILD_SHARED_LIBS) + TARGET_LINK_LIBRARIES(BulletMultiThreaded BulletCollision) +ENDIF (BUILD_SHARED_LIBS) diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/Jamfile b/Engine/lib/bullet/src/BulletMultiThreaded/Jamfile new file mode 100644 index 000000000..9f0c8d732 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/Jamfile @@ -0,0 +1,14 @@ +SubDir TOP src BulletMultiThreaded ; + +#IncludeDir src/BulletMultiThreaded ; + +Library bulletmultithreaded : [ Wildcard . : *.h *.cpp ] [ Wildcard MiniCLTask : *.h *.cpp ] [ Wildcard SpuNarrowPhaseCollisionTask : *.h *.cpp ] : noinstall ; +CFlags bulletmultithreaded : [ FIncludes $(TOP)/src/BulletMultiThreaded ] [ FIncludes $(TOP)/src/BulletMultiThreaded/vectormath/scalar/cpp ] ; +LibDepends bulletmultithreaded : ; + + MsvcIncDirs bulletmultithreaded : + "../../src/BulletMultiThreaded" + "../../src/BulletMultiThreaded/vectormath/scalar/cpp" + ; + +InstallHeader [ Wildcard *.h ] : bulletmultithreaded ; diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/Makefile.original b/Engine/lib/bullet/src/BulletMultiThreaded/Makefile.original new file mode 100644 index 000000000..d0a8318b6 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/Makefile.original @@ -0,0 +1,187 @@ +__ARCH_BITS__ := 32 + +# define macros +NARROWPHASEDIR=./SpuNarrowPhaseCollisionTask +SPU_TASKFILE=$(NARROWPHASEDIR)/SpuGatheringCollisionTask + +IBM_CELLSDK_VERSION := $(shell if [ -d /opt/cell ]; then echo "3.0"; fi) + +ifeq ("$(IBM_CELLSDK_VERSION)","3.0") + CELL_TOP ?= /opt/cell/sdk + CELL_SYSROOT := /opt/cell/sysroot +else + CELL_TOP ?= /opt/ibm/cell-sdk/prototype + CELL_SYSROOT := $(CELL_TOP)/sysroot +endif + + +USE_CCACHE=ccache +RM=rm -f +OUTDIR=./out +DEBUGFLAG=-DNDEBUG +LIBOUTDIR=../../lib/ibmsdk +COLLISIONDIR=../../src/BulletCollision +MATHDIR=../../src/LinearMath +ARCHITECTUREFLAG=-m$(__ARCH_BITS__) +ifeq "$(__ARCH_BITS__)" "64" + SPU_DEFFLAGS= -DUSE_LIBSPE2 -D__SPU__ -DUSE_ADDR64 +else + SPU_DEFFLAGS= -DUSE_LIBSPE2 -D__SPU__ +endif + +SPU_DEFFLAGS+=-DUSE_PE_BOX_BOX + +SPU_GCC=$(USE_CCACHE) /usr/bin/spu-gcc +SPU_INCLUDEDIR= -Ivectormath/scalar/cpp -I. -I$(CELL_SYSROOT)/usr/spu/include -I../../src -I$(NARROWPHASEDIR) +#SPU_CFLAGS= $(DEBUGFLAG) -W -Wall -Winline -Os -c -include spu_intrinsics.h -include stdbool.h +SPU_CFLAGS= $(DEBUGFLAG) -W -Wall -Winline -O3 -mbranch-hints -fomit-frame-pointer -ftree-vectorize -finline-functions -ftree-vect-loop-version -ftree-loop-optimize -ffast-math -fno-rtti -fno-exceptions -c -include spu_intrinsics.h -include stdbool.h + +SPU_LFLAGS= -Wl,-N +SPU_LIBRARIES=-lstdc++ +SPU_EMBED=/usr/bin/ppu-embedspu +SPU_AR=/usr/bin/ar +SYMBOLNAME=spu_program + +ifeq "$(__ARCH_BITS__)" "64" + PPU_DEFFLAGS= -DUSE_LIBSPE2 -DUSE_ADDR64 + PPU_GCC=$(USE_CCACHE) /usr/bin/ppu-gcc +else + PPU_DEFFLAGS= -DUSE_LIBSPE2 + PPU_GCC=$(USE_CCACHE) /usr/bin/ppu32-gcc +endif + +PPU_CFLAGS= $(ARCHITECTUREFLAG) $(DEBUGFLAG) -W -Wall -Winline -O3 -c -mabi=altivec -maltivec -include altivec.h -include stdbool.h +PPU_INCLUDEDIR= -I. -I$(CELL_SYSROOT)/usr/include -I../../src -I$(NARROWPHASEDIR) +PPU_LFLAGS= $(ARCHITECTUREFLAG) -Wl,-m,elf$(__ARCH_BITS__)ppc +PPU_LIBRARIES= -lstdc++ -lsupc++ -lgcc -lgcov -lspe2 -lpthread -L../../lib/ibmsdk -lbulletcollision -lbulletdynamics -lbulletmath -L$(CELL_SYSROOT)/usr/lib$(__ARCH_BITS__) -R$(CELL_SYSROOT)/usr/lib +PPU_AR=/usr/bin/ar + +MakeOut : +# rm -f -R $(OUTDIR) ; mkdir $(OUTDIR) + @echo "usage: make spu, make ppu, make all, or make clean" +# SPU +SpuTaskFile : MakeOut + $(SPU_GCC) $(SPU_DEFFLAGS) $(SPU_CFLAGS) $(SPU_INCLUDEDIR) -o $(OUTDIR)/SpuTaskFile.o $(SPU_TASKFILE).cpp + +boxBoxDistance : MakeOut + $(SPU_GCC) $(SPU_DEFFLAGS) $(SPU_CFLAGS) $(SPU_INCLUDEDIR) -o $(OUTDIR)/$@.o $(NARROWPHASEDIR)/$@.cpp + +SpuFakeDma : MakeOut + $(SPU_GCC) $(SPU_DEFFLAGS) $(SPU_CFLAGS) $(SPU_INCLUDEDIR) -o $(OUTDIR)/$@.o $@.cpp + +SpuContactManifoldCollisionAlgorithm_spu : MakeOut + $(SPU_GCC) $(SPU_DEFFLAGS) $(SPU_CFLAGS) $(SPU_INCLUDEDIR) -o $(OUTDIR)/$@.o SpuContactManifoldCollisionAlgorithm.cpp + +SpuCollisionShapes : MakeOut + $(SPU_GCC) $(SPU_DEFFLAGS) $(SPU_CFLAGS) $(SPU_INCLUDEDIR) -o $(OUTDIR)/$@.o $(NARROWPHASEDIR)/$@.cpp + +SpuContactResult : MakeOut + $(SPU_GCC) $(SPU_DEFFLAGS) $(SPU_CFLAGS) $(SPU_INCLUDEDIR) -o $(OUTDIR)/$@.o $(NARROWPHASEDIR)/$@.cpp + +#SpuGatheringCollisionTask : MakeOut +# $(SPU_GCC) $(SPU_DEFFLAGS) $(SPU_CFLAGS) $(SPU_INCLUDEDIR) -o $(OUTDIR)/$@.o $(NARROWPHASEDIR)/$@.cpp + +SpuGjkPairDetector: MakeOut + $(SPU_GCC) $(SPU_DEFFLAGS) $(SPU_CFLAGS) $(SPU_INCLUDEDIR) -o $(OUTDIR)/$@.o $(NARROWPHASEDIR)/$@.cpp + +SpuMinkowskiPenetrationDepthSolver : MakeOut + $(SPU_GCC) $(SPU_DEFFLAGS) $(SPU_CFLAGS) $(SPU_INCLUDEDIR) -o $(OUTDIR)/$@.o $(NARROWPHASEDIR)/$@.cpp + +SpuVoronoiSimplexSolver : MakeOut + $(SPU_GCC) $(SPU_DEFFLAGS) $(SPU_CFLAGS) $(SPU_INCLUDEDIR) -o $(OUTDIR)/$@.o $(NARROWPHASEDIR)/$@.cpp + +#SpuLibspe2Support_spu : MakeOut +# $(SPU_GCC) $(SPU_DEFFLAGS) $(SPU_CFLAGS) $(SPU_INCLUDEDIR) -o $(OUTDIR)/$@.o SpuLibspe2Support.cpp + +## SPU-Bullet +btPersistentManifold : MakeOut + $(SPU_GCC) $(SPU_DEFFLAGS) $(SPU_CFLAGS) $(SPU_INCLUDEDIR) -o $(OUTDIR)/$@.o $(COLLISIONDIR)/NarrowPhaseCollision/$@.cpp + +btOptimizedBvh : MakeOut + $(SPU_GCC) $(SPU_DEFFLAGS) $(SPU_CFLAGS) $(SPU_INCLUDEDIR) -o $(OUTDIR)/$@.o $(COLLISIONDIR)/CollisionShapes/$@.cpp + +btCollisionObject : MakeOut + $(SPU_GCC) $(SPU_DEFFLAGS) $(SPU_CFLAGS) $(SPU_INCLUDEDIR) -o $(OUTDIR)/$@.o $(COLLISIONDIR)/CollisionDispatch/$@.cpp + +btTriangleCallback : MakeOut + $(SPU_GCC) $(SPU_DEFFLAGS) $(SPU_CFLAGS) $(SPU_INCLUDEDIR) -o $(OUTDIR)/$@.o $(COLLISIONDIR)/CollisionShapes/$@.cpp + +btTriangleIndexVertexArray : MakeOut + $(SPU_GCC) $(SPU_DEFFLAGS) $(SPU_CFLAGS) $(SPU_INCLUDEDIR) -o $(OUTDIR)/$@.o $(COLLISIONDIR)/CollisionShapes/$@.cpp + +btStridingMeshInterface : MakeOut + $(SPU_GCC) $(SPU_DEFFLAGS) $(SPU_CFLAGS) $(SPU_INCLUDEDIR) -o $(OUTDIR)/$@.o $(COLLISIONDIR)/CollisionShapes/$@.cpp + +btAlignedAllocator : MakeOut + $(SPU_GCC) $(SPU_DEFFLAGS) $(SPU_CFLAGS) $(SPU_INCLUDEDIR) -o $(OUTDIR)/$@.o $(MATHDIR)/$@.cpp + + +# PPU +SpuGatheringCollisionDispatcher : MakeOut + $(PPU_GCC) $(PPU_DEFFLAGS) $(PPU_CFLAGS) $(PPU_INCLUDEDIR) -o $(OUTDIR)/$@.o $@.cpp + +SequentialThreadSupport: MakeOut + $(PPU_GCC) $(PPU_DEFFLAGS) $(PPU_CFLAGS) $(PPU_INCLUDEDIR) -o $(OUTDIR)/$@.o $@.cpp + +SpuLibspe2Support: MakeOut + $(PPU_GCC) $(PPU_DEFFLAGS) $(PPU_CFLAGS) $(PPU_INCLUDEDIR) -o $(OUTDIR)/$@.o $@.cpp + +btThreadSupportInterface: MakeOut + $(PPU_GCC) $(PPU_DEFFLAGS) $(PPU_CFLAGS) $(PPU_INCLUDEDIR) -o $(OUTDIR)/$@.o $@.cpp + +SpuCollisionTaskProcess : MakeOut + $(PPU_GCC) $(PPU_DEFFLAGS) $(PPU_CFLAGS) $(PPU_INCLUDEDIR) -o $(OUTDIR)/$@.o $@.cpp + +SpuContactManifoldCollisionAlgorithm : MakeOut + $(PPU_GCC) $(PPU_DEFFLAGS) $(PPU_CFLAGS) $(PPU_INCLUDEDIR) -o $(OUTDIR)/$@.o $@.cpp + +SpuSampleTaskProcess : MakeOut + $(PPU_GCC) $(PPU_DEFFLAGS) $(PPU_CFLAGS) $(PPU_INCLUDEDIR) -o $(OUTDIR)/$@.o $@.cpp + + + +spu : boxBoxDistance SpuFakeDma SpuContactManifoldCollisionAlgorithm_spu SpuContactResult SpuTaskFile \ + SpuGjkPairDetector SpuMinkowskiPenetrationDepthSolver SpuVoronoiSimplexSolver SpuCollisionShapes \ + btPersistentManifold btOptimizedBvh btCollisionObject btTriangleCallback btTriangleIndexVertexArray \ + btStridingMeshInterface btAlignedAllocator + $(SPU_GCC) -o $(OUTDIR)/spuCollision.elf \ + $(OUTDIR)/SpuTaskFile.o \ + $(OUTDIR)/SpuFakeDma.o \ + $(OUTDIR)/boxBoxDistance.o \ + $(OUTDIR)/SpuContactManifoldCollisionAlgorithm_spu.o \ + $(OUTDIR)/SpuContactResult.o \ + $(OUTDIR)/SpuCollisionShapes.o \ + $(OUTDIR)/SpuGjkPairDetector.o \ + $(OUTDIR)/SpuMinkowskiPenetrationDepthSolver.o \ + $(OUTDIR)/SpuVoronoiSimplexSolver.o \ + $(OUTDIR)/btPersistentManifold.o \ + $(OUTDIR)/btTriangleCallback.o \ + $(OUTDIR)/btTriangleIndexVertexArray.o \ + $(OUTDIR)/btStridingMeshInterface.o \ + $(OUTDIR)/btAlignedAllocator.o \ + $(SPU_LFLAGS) $(SPU_LIBRARIES) + +spu-embed : spu + $(SPU_EMBED) $(ARCHITECTUREFLAG) $(SYMBOLNAME) $(OUTDIR)/spuCollision.elf $(OUTDIR)/$@.o + $(SPU_AR) -qcs $(LIBOUTDIR)/libspu.a $(OUTDIR)/$@.o + + + +ppu : SpuGatheringCollisionDispatcher SpuCollisionTaskProcess btThreadSupportInterface \ + SpuLibspe2Support SpuContactManifoldCollisionAlgorithm SpuSampleTaskProcess + $(PPU_AR) -qcs $(LIBOUTDIR)/bulletmultithreaded.a \ + $(OUTDIR)/SpuCollisionTaskProcess.o \ + $(OUTDIR)/SpuSampleTaskProcess.o \ + $(OUTDIR)/SpuGatheringCollisionDispatcher.o \ + $(OUTDIR)/SpuLibspe2Support.o \ + $(OUTDIR)/btThreadSupportInterface.o \ + $(OUTDIR)/SpuContactManifoldCollisionAlgorithm.o + +all : spu-embed ppu + +clean: + $(RM) $(OUTDIR)/* ; $(RM) $(LIBOUTDIR)/libspu.a ; $(RM) $(LIBOUTDIR)/bulletmultithreaded.a + + + + diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/MiniCLTask/MiniCLTask.cpp b/Engine/lib/bullet/src/BulletMultiThreaded/MiniCLTask/MiniCLTask.cpp new file mode 100644 index 000000000..b9680eaa2 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/MiniCLTask/MiniCLTask.cpp @@ -0,0 +1,116 @@ +/* +Bullet Continuous Collision Detection and Physics Library, Copyright (c) 2007 Erwin Coumans + +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 "MiniCLTask.h" +#include "../PlatformDefinitions.h" +#include "../SpuFakeDma.h" +#include "LinearMath/btMinMax.h" +#include "BulletMultiThreaded/MiniCLTask/MiniCLTask.h" + +#ifdef __SPU__ +#include +#else +#include +#define spu_printf printf +#endif + +#define __kernel +#define __global +#define get_global_id(a) guid + +struct MiniCLTask_LocalStoreMemory +{ + +}; + + +/////////////////////////////////////////////////// +// OpenCL Kernel Function for element by element vector addition +__kernel void VectorAdd(__global const float8* a, __global const float8* b, __global float8* c, int guid) +{ + // get oct-float index into global data array + int iGID = get_global_id(0); + + // read inputs into registers + float8 f8InA = a[iGID]; + float8 f8InB = b[iGID]; + float8 f8Out = (float8)0.0f; + + // add the vector elements + f8Out.s0 = f8InA.s0 + f8InB.s0; + f8Out.s1 = f8InA.s1 + f8InB.s1; + f8Out.s2 = f8InA.s2 + f8InB.s2; + f8Out.s3 = f8InA.s3 + f8InB.s3; + f8Out.s4 = f8InA.s4 + f8InB.s4; + f8Out.s5 = f8InA.s5 + f8InB.s5; + f8Out.s6 = f8InA.s6 + f8InB.s6; + f8Out.s7 = f8InA.s7 + f8InB.s7; + + // write back out to GMEM + c[get_global_id(0)] = f8Out; +} +/////////////////////////////////////////////////// + + +//-- MAIN METHOD +void processMiniCLTask(void* userPtr, void* lsMemory) +{ + // BT_PROFILE("processSampleTask"); + + MiniCLTask_LocalStoreMemory* localMemory = (MiniCLTask_LocalStoreMemory*)lsMemory; + + MiniCLTaskDesc* taskDescPtr = (MiniCLTaskDesc*)userPtr; + MiniCLTaskDesc& taskDesc = *taskDescPtr; + + printf("Compute Unit[%d] executed kernel %d work items [%d..%d)\n",taskDesc.m_taskId,taskDesc.m_kernelProgramId,taskDesc.m_firstWorkUnit,taskDesc.m_lastWorkUnit); + + + switch (taskDesc.m_kernelProgramId) + { + case CMD_MINICL_ADDVECTOR: + { + for (unsigned int i=taskDesc.m_firstWorkUnit;i + +#ifdef __SPU__ + + + +void SampleThreadFunc(void* userPtr,void* lsMemory) +{ + //do nothing + printf("hello world\n"); +} + + +void* SamplelsMemoryFunc() +{ + //don't create local store memory, just return 0 + return 0; +} + + +#else + + +#include "btThreadSupportInterface.h" + +//# include "SPUAssert.h" +#include + + + +extern "C" { + extern char SPU_SAMPLE_ELF_SYMBOL[]; +} + + + + + +MiniCLTaskScheduler::MiniCLTaskScheduler(btThreadSupportInterface* threadInterface, int maxNumOutstandingTasks) +:m_threadInterface(threadInterface), +m_maxNumOutstandingTasks(maxNumOutstandingTasks) +{ + + m_taskBusy.resize(m_maxNumOutstandingTasks); + m_spuSampleTaskDesc.resize(m_maxNumOutstandingTasks); + + for (int i = 0; i < m_maxNumOutstandingTasks; i++) + { + m_taskBusy[i] = false; + } + m_numBusyTasks = 0; + m_currentTask = 0; + + m_initialized = false; + + m_threadInterface->startSPU(); + + +} + +MiniCLTaskScheduler::~MiniCLTaskScheduler() +{ + m_threadInterface->stopSPU(); + +} + + + +void MiniCLTaskScheduler::initialize() +{ +#ifdef DEBUG_SPU_TASK_SCHEDULING + printf("MiniCLTaskScheduler::initialize()\n"); +#endif //DEBUG_SPU_TASK_SCHEDULING + + for (int i = 0; i < m_maxNumOutstandingTasks; i++) + { + m_taskBusy[i] = false; + } + m_numBusyTasks = 0; + m_currentTask = 0; + m_initialized = true; + +} + + +void MiniCLTaskScheduler::issueTask(int firstWorkUnit, int lastWorkUnit,int kernelProgramId,char* argData,int* argSizes) +{ + +#ifdef DEBUG_SPU_TASK_SCHEDULING + printf("MiniCLTaskScheduler::issueTask (m_currentTask= %d\)n", m_currentTask); +#endif //DEBUG_SPU_TASK_SCHEDULING + + m_taskBusy[m_currentTask] = true; + m_numBusyTasks++; + + MiniCLTaskDesc& taskDesc = m_spuSampleTaskDesc[m_currentTask]; + { + // send task description in event message + taskDesc.m_firstWorkUnit = firstWorkUnit; + taskDesc.m_lastWorkUnit = lastWorkUnit; + taskDesc.m_kernelProgramId = kernelProgramId; + //some bookkeeping to recognize finished tasks + taskDesc.m_taskId = m_currentTask; + + for (int i=0;isendRequest(1, (ppu_address_t) &taskDesc, m_currentTask); + + // if all tasks busy, wait for spu event to clear the task. + + if (m_numBusyTasks >= m_maxNumOutstandingTasks) + { + unsigned int taskId; + unsigned int outputSize; + + for (int i=0;iwaitForResponse(&taskId, &outputSize); + + //printf("PPU: after issue, received event: %u %d\n", taskId, outputSize); + + postProcess(taskId, outputSize); + + m_taskBusy[taskId] = false; + + m_numBusyTasks--; + } + + // find new task buffer + for (int i = 0; i < m_maxNumOutstandingTasks; i++) + { + if (!m_taskBusy[i]) + { + m_currentTask = i; + break; + } + } +} + + +///Optional PPU-size post processing for each task +void MiniCLTaskScheduler::postProcess(int taskId, int outputSize) +{ + +} + + +void MiniCLTaskScheduler::flush() +{ +#ifdef DEBUG_SPU_TASK_SCHEDULING + printf("\nSpuCollisionTaskProcess::flush()\n"); +#endif //DEBUG_SPU_TASK_SCHEDULING + + + // all tasks are issued, wait for all tasks to be complete + while(m_numBusyTasks > 0) + { +// Consolidating SPU code + unsigned int taskId; + unsigned int outputSize; + + for (int i=0;iwaitForResponse(&taskId, &outputSize); + } + + //printf("PPU: flushing, received event: %u %d\n", taskId, outputSize); + + postProcess(taskId, outputSize); + + m_taskBusy[taskId] = false; + + m_numBusyTasks--; + } + + +} + +#endif + + +#endif //USE_SAMPLE_PROCESS diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/MiniCLTaskScheduler.h b/Engine/lib/bullet/src/BulletMultiThreaded/MiniCLTaskScheduler.h new file mode 100644 index 000000000..580b509b8 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/MiniCLTaskScheduler.h @@ -0,0 +1,181 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.com + +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 MINICL_TASK_SCHEDULER_H +#define MINICL_TASK_SCHEDULER_H + +#include + + +#include "PlatformDefinitions.h" + +#include + +#include "LinearMath/btAlignedObjectArray.h" + + +#include "MiniCLTask/MiniCLTask.h" + + +//just add your commands here, try to keep them globally unique for debugging purposes +#define CMD_SAMPLE_TASK_COMMAND 10 + + + +/// MiniCLTaskScheduler handles SPU processing of collision pairs. +/// When PPU issues a task, it will look for completed task buffers +/// PPU will do postprocessing, dependent on workunit output (not likely) +class MiniCLTaskScheduler +{ + // track task buffers that are being used, and total busy tasks + btAlignedObjectArray m_taskBusy; + btAlignedObjectArray m_spuSampleTaskDesc; + + int m_numBusyTasks; + + // the current task and the current entry to insert a new work unit + int m_currentTask; + + bool m_initialized; + + void postProcess(int taskId, int outputSize); + + class btThreadSupportInterface* m_threadInterface; + + int m_maxNumOutstandingTasks; + + + +public: + MiniCLTaskScheduler(btThreadSupportInterface* threadInterface, int maxNumOutstandingTasks); + + ~MiniCLTaskScheduler(); + + ///call initialize in the beginning of the frame, before addCollisionPairToTask + void initialize(); + + void issueTask(int firstWorkUnit, int lastWorkUnit,int kernelProgramId,char* argData,int* argSizes); + + ///call flush to submit potential outstanding work to SPUs and wait for all involved SPUs to be finished + void flush(); + + class btThreadSupportInterface* getThreadSupportInterface() + { + return m_threadInterface; + } + + int findProgramCommandIdByName(const char* programName) const + { + return CMD_MINICL_ADDVECTOR;//hardcoded temp value, todo: implement multi-program support + } + + int getMaxNumOutstandingTasks() const + { + return m_maxNumOutstandingTasks; + } +}; + + +struct MiniCLKernel +{ + MiniCLTaskScheduler* m_scheduler; + + int m_kernelProgramCommandId; + + char m_argData[MINI_CL_MAX_ARG][MINICL_MAX_ARGLENGTH]; + int m_argSizes[MINI_CL_MAX_ARG]; +}; + + +#if defined(USE_LIBSPE2) && defined(__SPU__) +////////////////////MAIN///////////////////////////// +#include "../SpuLibspe2Support.h" +#include +#include +#include + +void * SamplelsMemoryFunc(); +void SampleThreadFunc(void* userPtr,void* lsMemory); + +//#define DEBUG_LIBSPE2_MAINLOOP + +int main(unsigned long long speid, addr64 argp, addr64 envp) +{ + printf("SPU is up \n"); + + ATTRIBUTE_ALIGNED128(btSpuStatus status); + ATTRIBUTE_ALIGNED16( SpuSampleTaskDesc taskDesc ) ; + unsigned int received_message = Spu_Mailbox_Event_Nothing; + bool shutdown = false; + + cellDmaGet(&status, argp.ull, sizeof(btSpuStatus), DMA_TAG(3), 0, 0); + cellDmaWaitTagStatusAll(DMA_MASK(3)); + + status.m_status = Spu_Status_Free; + status.m_lsMemory.p = SamplelsMemoryFunc(); + + cellDmaLargePut(&status, argp.ull, sizeof(btSpuStatus), DMA_TAG(3), 0, 0); + cellDmaWaitTagStatusAll(DMA_MASK(3)); + + + while (!shutdown) + { + received_message = spu_read_in_mbox(); + + + + switch(received_message) + { + case Spu_Mailbox_Event_Shutdown: + shutdown = true; + break; + case Spu_Mailbox_Event_Task: + // refresh the status +#ifdef DEBUG_LIBSPE2_MAINLOOP + printf("SPU recieved Task \n"); +#endif //DEBUG_LIBSPE2_MAINLOOP + cellDmaGet(&status, argp.ull, sizeof(btSpuStatus), DMA_TAG(3), 0, 0); + cellDmaWaitTagStatusAll(DMA_MASK(3)); + + btAssert(status.m_status==Spu_Status_Occupied); + + cellDmaGet(&taskDesc, status.m_taskDesc.p, sizeof(SpuSampleTaskDesc), DMA_TAG(3), 0, 0); + cellDmaWaitTagStatusAll(DMA_MASK(3)); + + SampleThreadFunc((void*)&taskDesc, reinterpret_cast (taskDesc.m_mainMemoryPtr) ); + break; + case Spu_Mailbox_Event_Nothing: + default: + break; + } + + // set to status free and wait for next task + status.m_status = Spu_Status_Free; + cellDmaLargePut(&status, argp.ull, sizeof(btSpuStatus), DMA_TAG(3), 0, 0); + cellDmaWaitTagStatusAll(DMA_MASK(3)); + + + } + return 0; +} +////////////////////////////////////////////////////// +#endif + + + +#endif // MINICL_TASK_SCHEDULER_H + diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/PlatformDefinitions.h b/Engine/lib/bullet/src/BulletMultiThreaded/PlatformDefinitions.h new file mode 100644 index 000000000..bda2d3b06 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/PlatformDefinitions.h @@ -0,0 +1,82 @@ +#ifndef TYPE_DEFINITIONS_H +#define TYPE_DEFINITIONS_H + +///This file provides some platform/compiler checks for common definitions + +#ifdef WIN32 + +typedef union +{ + unsigned int u; + void *p; +} addr64; + +#define USE_WIN32_THREADING 1 + + #if defined(__MINGW32__) || defined(__CYGWIN__) || (defined (_MSC_VER) && _MSC_VER < 1300) + #else + #endif //__MINGW32__ + + typedef unsigned char uint8_t; +#ifndef __PHYSICS_COMMON_H__ + typedef unsigned long int uint64_t; + typedef unsigned int uint32_t; +#endif //__PHYSICS_COMMON_H__ + typedef unsigned short uint16_t; + + #include + #define memalign(alignment, size) malloc(size); + +#include //memcpy + + + + #include + #define spu_printf printf + +#else + #include + #include + #include //for memcpy + +#if defined (__CELLOS_LV2__) + // Playstation 3 Cell SDK +#include + +#else + // posix system + +#define USE_PTHREADS (1) + +#ifdef USE_LIBSPE2 +#include +#define spu_printf printf +#define DWORD unsigned int + + typedef union + { + unsigned long long ull; + unsigned int ui[2]; + void *p; + } addr64; + + +#else + +#include +#define spu_printf printf + +#endif // USE_LIBSPE2 + +#endif //__CELLOS_LV2__ + +#endif + + +/* Included here because we need uint*_t typedefs */ +#include "PpuAddressSpace.h" + +#endif //TYPE_DEFINITIONS_H + + + diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/PosixThreadSupport.cpp b/Engine/lib/bullet/src/BulletMultiThreaded/PosixThreadSupport.cpp new file mode 100644 index 000000000..241780940 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/PosixThreadSupport.cpp @@ -0,0 +1,249 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.com + +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 +#include "PosixThreadSupport.h" +#ifdef USE_PTHREADS +#include +#include + +#include "SpuCollisionTaskProcess.h" +#include "SpuNarrowPhaseCollisionTask/SpuGatheringCollisionTask.h" + +#define checkPThreadFunction(returnValue) \ + if(0 != returnValue) { \ + printf("PThread problem at line %i in file %s: %i %d\n", __LINE__, __FILE__, returnValue, errno); \ + } + +// The number of threads should be equal to the number of available cores +// Todo: each worker should be linked to a single core, using SetThreadIdealProcessor. + +// PosixThreadSupport helps to initialize/shutdown libspe2, start/stop SPU tasks and communication +// Setup and initialize SPU/CELL/Libspe2 +PosixThreadSupport::PosixThreadSupport(ThreadConstructionInfo& threadConstructionInfo) +{ + startThreads(threadConstructionInfo); +} + +// cleanup/shutdown Libspe2 +PosixThreadSupport::~PosixThreadSupport() +{ + stopSPU(); +} + +#if (defined (__APPLE__)) +#define NAMED_SEMAPHORES +#endif + +// this semaphore will signal, if and how many threads are finished with their work +static sem_t* mainSemaphore; + +static sem_t* createSem(const char* baseName) +{ + static int semCount = 0; +#ifdef NAMED_SEMAPHORES + /// Named semaphore begin + char name[32]; + snprintf(name, 32, "/%s-%d-%4.4d", baseName, getpid(), semCount++); + sem_t* tempSem = sem_open(name, O_CREAT, 0600, 0); + if (tempSem != reinterpret_cast(SEM_FAILED)) + { + //printf("Created \"%s\" Semaphore %x\n", name, tempSem); + } + else + { + //printf("Error creating Semaphore %d\n", errno); + exit(-1); + } + /// Named semaphore end +#else + sem_t* tempSem = new sem_t; + checkPThreadFunction(sem_init(tempSem, 0, 0)); +#endif + return tempSem; +} + +static void destroySem(sem_t* semaphore) +{ +#ifdef NAMED_SEMAPHORES + checkPThreadFunction(sem_close(semaphore)); +#else + checkPThreadFunction(sem_destroy(semaphore)); + delete semaphore; +#endif +} + +static void *threadFunction(void *argument) +{ + + PosixThreadSupport::btSpuStatus* status = (PosixThreadSupport::btSpuStatus*)argument; + + + while (1) + { + checkPThreadFunction(sem_wait(status->startSemaphore)); + + void* userPtr = status->m_userPtr; + + if (userPtr) + { + btAssert(status->m_status); + status->m_userThreadFunc(userPtr,status->m_lsMemory); + status->m_status = 2; + checkPThreadFunction(sem_post(mainSemaphore)); + status->threadUsed++; + } else { + //exit Thread + status->m_status = 3; + checkPThreadFunction(sem_post(mainSemaphore)); + printf("Thread with taskId %i exiting\n",status->m_taskId); + break; + } + + } + + printf("Thread TERMINATED\n"); + return 0; + +} + +///send messages to SPUs +void PosixThreadSupport::sendRequest(uint32_t uiCommand, ppu_address_t uiArgument0, uint32_t taskId) +{ + /// gMidphaseSPU.sendRequest(CMD_GATHER_AND_PROCESS_PAIRLIST, (uint32_t) &taskDesc); + + ///we should spawn an SPU task here, and in 'waitForResponse' it should wait for response of the (one of) the first tasks that finished + + + + switch (uiCommand) + { + case CMD_GATHER_AND_PROCESS_PAIRLIST: + { + btSpuStatus& spuStatus = m_activeSpuStatus[taskId]; + btAssert(taskId >= 0); + btAssert(taskId < m_activeSpuStatus.size()); + + spuStatus.m_commandId = uiCommand; + spuStatus.m_status = 1; + spuStatus.m_userPtr = (void*)uiArgument0; + + // fire event to start new task + checkPThreadFunction(sem_post(spuStatus.startSemaphore)); + break; + } + default: + { + ///not implemented + btAssert(0); + } + + }; + + +} + + +///check for messages from SPUs +void PosixThreadSupport::waitForResponse(unsigned int *puiArgument0, unsigned int *puiArgument1) +{ + ///We should wait for (one of) the first tasks to finish (or other SPU messages), and report its response + + ///A possible response can be 'yes, SPU handled it', or 'no, please do a PPU fallback' + + + btAssert(m_activeSpuStatus.size()); + + // wait for any of the threads to finish + checkPThreadFunction(sem_wait(mainSemaphore)); + + // get at least one thread which has finished + size_t last = -1; + + for(size_t t=0; t < m_activeSpuStatus.size(); ++t) { + if(2 == m_activeSpuStatus[t].m_status) { + last = t; + break; + } + } + + btSpuStatus& spuStatus = m_activeSpuStatus[last]; + + btAssert(spuStatus.m_status > 1); + spuStatus.m_status = 0; + + // need to find an active spu + btAssert(last >= 0); + + *puiArgument0 = spuStatus.m_taskId; + *puiArgument1 = spuStatus.m_status; +} + + + +void PosixThreadSupport::startThreads(ThreadConstructionInfo& threadConstructionInfo) +{ + printf("%s creating %i threads.\n", __FUNCTION__, threadConstructionInfo.m_numThreads); + m_activeSpuStatus.resize(threadConstructionInfo.m_numThreads); + + mainSemaphore = createSem("main"); + + for (int i=0;i < threadConstructionInfo.m_numThreads;i++) + { + printf("starting thread %d\n",i); + + btSpuStatus& spuStatus = m_activeSpuStatus[i]; + + spuStatus.startSemaphore = createSem("threadLocal"); + + checkPThreadFunction(pthread_create(&spuStatus.thread, NULL, &threadFunction, (void*)&spuStatus)); + + spuStatus.m_userPtr=0; + + spuStatus.m_taskId = i; + spuStatus.m_commandId = 0; + spuStatus.m_status = 0; + spuStatus.m_lsMemory = threadConstructionInfo.m_lsMemoryFunc(); + spuStatus.m_userThreadFunc = threadConstructionInfo.m_userThreadFunc; + spuStatus.threadUsed = 0; + + printf("started thread %d \n",i); + + } + +} + +void PosixThreadSupport::startSPU() +{ +} + + +///tell the task scheduler we are done with the SPU tasks +void PosixThreadSupport::stopSPU() +{ + for(size_t t=0; t < m_activeSpuStatus.size(); ++t) { + btSpuStatus& spuStatus = m_activeSpuStatus[t]; + printf("%s: Thread %i used: %ld\n", __FUNCTION__, t, spuStatus.threadUsed); + + destroySem(spuStatus.startSemaphore); + checkPThreadFunction(pthread_cancel(spuStatus.thread)); + } + destroySem(mainSemaphore); + + m_activeSpuStatus.clear(); +} + +#endif // USE_PTHREADS + diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/PosixThreadSupport.h b/Engine/lib/bullet/src/BulletMultiThreaded/PosixThreadSupport.h new file mode 100644 index 000000000..7cc49115b --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/PosixThreadSupport.h @@ -0,0 +1,124 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.com + +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 "LinearMath/btScalar.h" +#include "PlatformDefinitions.h" + +#ifdef USE_PTHREADS //platform specific defines are defined in PlatformDefinitions.h +#include +#include + +#ifndef POSIX_THREAD_SUPPORT_H +#define POSIX_THREAD_SUPPORT_H + +#include "LinearMath/btAlignedObjectArray.h" + +#include "btThreadSupportInterface.h" + + +typedef void (*PosixThreadFunc)(void* userPtr,void* lsMemory); +typedef void* (*PosixlsMemorySetupFunc)(); + +// PosixThreadSupport helps to initialize/shutdown libspe2, start/stop SPU tasks and communication +class PosixThreadSupport : public btThreadSupportInterface +{ +public: + typedef enum sStatus { + STATUS_BUSY, + STATUS_READY, + STATUS_FINISHED + } Status; + + // placeholder, until libspe2 support is there + struct btSpuStatus + { + uint32_t m_taskId; + uint32_t m_commandId; + uint32_t m_status; + + PosixThreadFunc m_userThreadFunc; + void* m_userPtr; //for taskDesc etc + void* m_lsMemory; //initialized using PosixLocalStoreMemorySetupFunc + + pthread_t thread; + sem_t* startSemaphore; + + unsigned long threadUsed; + }; +private: + + btAlignedObjectArray m_activeSpuStatus; +public: + ///Setup and initialize SPU/CELL/Libspe2 + + + + struct ThreadConstructionInfo + { + ThreadConstructionInfo(char* uniqueName, + PosixThreadFunc userThreadFunc, + PosixlsMemorySetupFunc lsMemoryFunc, + int numThreads=1, + int threadStackSize=65535 + ) + :m_uniqueName(uniqueName), + m_userThreadFunc(userThreadFunc), + m_lsMemoryFunc(lsMemoryFunc), + m_numThreads(numThreads), + m_threadStackSize(threadStackSize) + { + + } + + char* m_uniqueName; + PosixThreadFunc m_userThreadFunc; + PosixlsMemorySetupFunc m_lsMemoryFunc; + int m_numThreads; + int m_threadStackSize; + + }; + + PosixThreadSupport(ThreadConstructionInfo& threadConstructionInfo); + +///cleanup/shutdown Libspe2 + virtual ~PosixThreadSupport(); + + void startThreads(ThreadConstructionInfo& threadInfo); + + +///send messages to SPUs + virtual void sendRequest(uint32_t uiCommand, ppu_address_t uiArgument0, uint32_t uiArgument1); + +///check for messages from SPUs + virtual void waitForResponse(unsigned int *puiArgument0, unsigned int *puiArgument1); + +///start the spus (can be called at the beginning of each frame, to make sure that the right SPU program is loaded) + virtual void startSPU(); + +///tell the task scheduler we are done with the SPU tasks + virtual void stopSPU(); + + virtual void setNumTasks(int numTasks) {} + + virtual int getNumTasks() const + { + return m_activeSpuStatus.size(); + } +}; + +#endif // POSIX_THREAD_SUPPORT_H + +#endif // USE_PTHREADS diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/PpuAddressSpace.h b/Engine/lib/bullet/src/BulletMultiThreaded/PpuAddressSpace.h new file mode 100644 index 000000000..93c83648c --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/PpuAddressSpace.h @@ -0,0 +1,18 @@ +#ifndef __PPU_ADDRESS_SPACE_H +#define __PPU_ADDRESS_SPACE_H + + +#ifdef WIN32 +//stop those casting warnings until we have a better solution for ppu_address_t / void* / uint64 conversions +#pragma warning (disable: 4311) +#pragma warning (disable: 4312) +#endif //WIN32 + +#ifdef USE_ADDR64 +typedef uint64_t ppu_address_t; +#else +typedef uint32_t ppu_address_t; +#endif + +#endif + diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SequentialThreadSupport.cpp b/Engine/lib/bullet/src/BulletMultiThreaded/SequentialThreadSupport.cpp new file mode 100644 index 000000000..4e9c822bb --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SequentialThreadSupport.cpp @@ -0,0 +1,93 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.com + +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 "SequentialThreadSupport.h" + + +#include "SpuCollisionTaskProcess.h" +#include "SpuNarrowPhaseCollisionTask/SpuGatheringCollisionTask.h" + +SequentialThreadSupport::SequentialThreadSupport(SequentialThreadConstructionInfo& threadConstructionInfo) +{ + startThreads(threadConstructionInfo); +} + +///cleanup/shutdown Libspe2 +SequentialThreadSupport::~SequentialThreadSupport() +{ + stopSPU(); +} + +#include + +///send messages to SPUs +void SequentialThreadSupport::sendRequest(uint32_t uiCommand, ppu_address_t uiArgument0, uint32_t taskId) +{ + switch (uiCommand) + { + case CMD_GATHER_AND_PROCESS_PAIRLIST: + { + btSpuStatus& spuStatus = m_activeSpuStatus[0]; + spuStatus.m_userPtr=(void*)uiArgument0; + spuStatus.m_userThreadFunc(spuStatus.m_userPtr,spuStatus.m_lsMemory); + } + break; + default: + { + ///not implemented + btAssert(0 && "Not implemented"); + } + + }; + + +} + +///check for messages from SPUs +void SequentialThreadSupport::waitForResponse(unsigned int *puiArgument0, unsigned int *puiArgument1) +{ + btAssert(m_activeSpuStatus.size()); + btSpuStatus& spuStatus = m_activeSpuStatus[0]; + *puiArgument0 = spuStatus.m_taskId; + *puiArgument1 = spuStatus.m_status; +} + +void SequentialThreadSupport::startThreads(SequentialThreadConstructionInfo& threadConstructionInfo) +{ + m_activeSpuStatus.resize(1); + printf("STS: Not starting any threads\n"); + btSpuStatus& spuStatus = m_activeSpuStatus[0]; + spuStatus.m_userPtr = 0; + spuStatus.m_taskId = 0; + spuStatus.m_commandId = 0; + spuStatus.m_status = 0; + spuStatus.m_lsMemory = threadConstructionInfo.m_lsMemoryFunc(); + spuStatus.m_userThreadFunc = threadConstructionInfo.m_userThreadFunc; + printf("STS: Created local store at %p for task %s\n", spuStatus.m_lsMemory, threadConstructionInfo.m_uniqueName); +} + +void SequentialThreadSupport::startSPU() +{ +} + +void SequentialThreadSupport::stopSPU() +{ + m_activeSpuStatus.clear(); +} + +void SequentialThreadSupport::setNumTasks(int numTasks) +{ + printf("SequentialThreadSupport::setNumTasks(%d) is not implemented and has no effect\n",numTasks); +} diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SequentialThreadSupport.h b/Engine/lib/bullet/src/BulletMultiThreaded/SequentialThreadSupport.h new file mode 100644 index 000000000..4256ebd2a --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SequentialThreadSupport.h @@ -0,0 +1,92 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.com + +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 "LinearMath/btScalar.h" +#include "PlatformDefinitions.h" + + +#ifndef SEQUENTIAL_THREAD_SUPPORT_H +#define SEQUENTIAL_THREAD_SUPPORT_H + +#include "LinearMath/btAlignedObjectArray.h" + +#include "btThreadSupportInterface.h" + +typedef void (*SequentialThreadFunc)(void* userPtr,void* lsMemory); +typedef void* (*SequentiallsMemorySetupFunc)(); + + + +///The SequentialThreadSupport is a portable non-parallel implementation of the btThreadSupportInterface +///This is useful for debugging and porting SPU Tasks to other platforms. +class SequentialThreadSupport : public btThreadSupportInterface +{ +public: + struct btSpuStatus + { + uint32_t m_taskId; + uint32_t m_commandId; + uint32_t m_status; + + SequentialThreadFunc m_userThreadFunc; + + void* m_userPtr; //for taskDesc etc + void* m_lsMemory; //initialized using SequentiallsMemorySetupFunc + }; +private: + btAlignedObjectArray m_activeSpuStatus; + btAlignedObjectArray m_completeHandles; +public: + struct SequentialThreadConstructionInfo + { + SequentialThreadConstructionInfo (char* uniqueName, + SequentialThreadFunc userThreadFunc, + SequentiallsMemorySetupFunc lsMemoryFunc + ) + :m_uniqueName(uniqueName), + m_userThreadFunc(userThreadFunc), + m_lsMemoryFunc(lsMemoryFunc) + { + + } + + char* m_uniqueName; + SequentialThreadFunc m_userThreadFunc; + SequentiallsMemorySetupFunc m_lsMemoryFunc; + }; + + SequentialThreadSupport(SequentialThreadConstructionInfo& threadConstructionInfo); + virtual ~SequentialThreadSupport(); + void startThreads(SequentialThreadConstructionInfo& threadInfo); +///send messages to SPUs + virtual void sendRequest(uint32_t uiCommand, ppu_address_t uiArgument0, uint32_t uiArgument1); +///check for messages from SPUs + virtual void waitForResponse(unsigned int *puiArgument0, unsigned int *puiArgument1); +///start the spus (can be called at the beginning of each frame, to make sure that the right SPU program is loaded) + virtual void startSPU(); +///tell the task scheduler we are done with the SPU tasks + virtual void stopSPU(); + + virtual void setNumTasks(int numTasks); + + virtual int getNumTasks() const + { + return 1; + } + +}; + +#endif //SEQUENTIAL_THREAD_SUPPORT_H + diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuCollisionObjectWrapper.cpp b/Engine/lib/bullet/src/BulletMultiThreaded/SpuCollisionObjectWrapper.cpp new file mode 100644 index 000000000..182aa2694 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuCollisionObjectWrapper.cpp @@ -0,0 +1,48 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.com + +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 "SpuCollisionObjectWrapper.h" +#include "BulletCollision/CollisionShapes/btCollisionShape.h" + +SpuCollisionObjectWrapper::SpuCollisionObjectWrapper () +{ +} + +#ifndef __SPU__ +SpuCollisionObjectWrapper::SpuCollisionObjectWrapper (const btCollisionObject* collisionObject) +{ + m_shapeType = collisionObject->getCollisionShape()->getShapeType (); + m_collisionObjectPtr = (ppu_address_t)collisionObject; + m_margin = collisionObject->getCollisionShape()->getMargin (); +} +#endif + +int +SpuCollisionObjectWrapper::getShapeType () const +{ + return m_shapeType; +} + +float +SpuCollisionObjectWrapper::getCollisionMargin () const +{ + return m_margin; +} + +ppu_address_t +SpuCollisionObjectWrapper::getCollisionObjectPtr () const +{ + return m_collisionObjectPtr; +} diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuCollisionObjectWrapper.h b/Engine/lib/bullet/src/BulletMultiThreaded/SpuCollisionObjectWrapper.h new file mode 100644 index 000000000..36ea49209 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuCollisionObjectWrapper.h @@ -0,0 +1,40 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.com + +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 SPU_COLLISION_OBJECT_WRAPPER_H +#define SPU_COLLISION_OBJECT_WRAPPER_H + +#include "PlatformDefinitions.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" + +ATTRIBUTE_ALIGNED16(class) SpuCollisionObjectWrapper +{ +protected: + int m_shapeType; + float m_margin; + ppu_address_t m_collisionObjectPtr; + +public: + SpuCollisionObjectWrapper (); + + SpuCollisionObjectWrapper (const btCollisionObject* collisionObject); + + int getShapeType () const; + float getCollisionMargin () const; + ppu_address_t getCollisionObjectPtr () const; +}; + + +#endif //SPU_COLLISION_OBJECT_WRAPPER_H diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuCollisionTaskProcess.cpp b/Engine/lib/bullet/src/BulletMultiThreaded/SpuCollisionTaskProcess.cpp new file mode 100644 index 000000000..dab42cc71 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuCollisionTaskProcess.cpp @@ -0,0 +1,318 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.com + +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 DEBUG_SPU_TASK_SCHEDULING 1 + + +//class OptimizedBvhNode; + +#include "SpuCollisionTaskProcess.h" + + + + +void SpuCollisionTaskProcess::setNumTasks(int maxNumTasks) +{ + if (m_maxNumOutstandingTasks != maxNumTasks) + { + m_maxNumOutstandingTasks = maxNumTasks; + m_taskBusy.resize(m_maxNumOutstandingTasks); + m_spuGatherTaskDesc.resize(m_maxNumOutstandingTasks); + + for (int i = 0; i < m_taskBusy.size(); i++) + { + m_taskBusy[i] = false; + } + + ///re-allocate task memory buffers + if (m_workUnitTaskBuffers != 0) + { + btAlignedFree(m_workUnitTaskBuffers); + } + + m_workUnitTaskBuffers = (unsigned char *)btAlignedAlloc(MIDPHASE_WORKUNIT_TASK_SIZE*m_maxNumOutstandingTasks, 128); + m_workUnitTaskBuffers = (unsigned char *)btAlignedAlloc(MIDPHASE_WORKUNIT_TASK_SIZE*6, 128); + } + +} + + + +SpuCollisionTaskProcess::SpuCollisionTaskProcess(class btThreadSupportInterface* threadInterface, unsigned int maxNumOutstandingTasks) +:m_threadInterface(threadInterface), +m_maxNumOutstandingTasks(0) +{ + m_workUnitTaskBuffers = (unsigned char *)0; + setNumTasks(maxNumOutstandingTasks); + m_numBusyTasks = 0; + m_currentTask = 0; + m_currentPage = 0; + m_currentPageEntry = 0; + +#ifdef DEBUG_SpuCollisionTaskProcess + m_initialized = false; +#endif + + m_threadInterface->startSPU(); + + //printf("sizeof vec_float4: %d\n", sizeof(vec_float4)); + printf("sizeof SpuGatherAndProcessWorkUnitInput: %d\n", sizeof(SpuGatherAndProcessWorkUnitInput)); + +} + +SpuCollisionTaskProcess::~SpuCollisionTaskProcess() +{ + + if (m_workUnitTaskBuffers != 0) + { + btAlignedFree(m_workUnitTaskBuffers); + m_workUnitTaskBuffers = 0; + } + + + + m_threadInterface->stopSPU(); + +} + + + +void SpuCollisionTaskProcess::initialize2(bool useEpa) +{ + +#ifdef DEBUG_SPU_TASK_SCHEDULING + printf("SpuCollisionTaskProcess::initialize()\n"); +#endif //DEBUG_SPU_TASK_SCHEDULING + + for (int i = 0; i < int (m_maxNumOutstandingTasks); i++) + { + m_taskBusy[i] = false; + } + m_numBusyTasks = 0; + m_currentTask = 0; + m_currentPage = 0; + m_currentPageEntry = 0; + m_useEpa = useEpa; + +#ifdef DEBUG_SpuCollisionTaskProcess + m_initialized = true; + btAssert(MIDPHASE_NUM_WORKUNITS_PER_TASK*sizeof(SpuGatherAndProcessWorkUnitInput) <= MIDPHASE_WORKUNIT_TASK_SIZE); +#endif +} + + +void SpuCollisionTaskProcess::issueTask2() +{ + +#ifdef DEBUG_SPU_TASK_SCHEDULING + printf("SpuCollisionTaskProcess::issueTask (m_currentTask= %d\n)", m_currentTask); +#endif //DEBUG_SPU_TASK_SCHEDULING + + m_taskBusy[m_currentTask] = true; + m_numBusyTasks++; + + + SpuGatherAndProcessPairsTaskDesc& taskDesc = m_spuGatherTaskDesc[m_currentTask]; + taskDesc.m_useEpa = m_useEpa; + + { + // send task description in event message + // no error checking here... + // but, currently, event queue can be no larger than NUM_WORKUNIT_TASKS. + + taskDesc.m_inPairPtr = reinterpret_cast(MIDPHASE_TASK_PTR(m_currentTask)); + + taskDesc.taskId = m_currentTask; + taskDesc.numPages = m_currentPage+1; + taskDesc.numOnLastPage = m_currentPageEntry; + } + + + + m_threadInterface->sendRequest(CMD_GATHER_AND_PROCESS_PAIRLIST, (ppu_address_t) &taskDesc,m_currentTask); + + // if all tasks busy, wait for spu event to clear the task. + + + if (m_numBusyTasks >= m_maxNumOutstandingTasks) + { + unsigned int taskId; + unsigned int outputSize; + + + for (int i=0;i=0); + + + m_threadInterface->waitForResponse(&taskId, &outputSize); + +// printf("issueTask taskId %d completed, numBusy=%d\n",taskId,m_numBusyTasks); + + //printf("PPU: after issue, received event: %u %d\n", taskId, outputSize); + + //postProcess(taskId, outputSize); + + m_taskBusy[taskId] = false; + + m_numBusyTasks--; + } + +} + +void SpuCollisionTaskProcess::addWorkToTask(void* pairArrayPtr,int startIndex,int endIndex) +{ +#ifdef DEBUG_SPU_TASK_SCHEDULING + printf("#"); +#endif //DEBUG_SPU_TASK_SCHEDULING + +#ifdef DEBUG_SpuCollisionTaskProcess + btAssert(m_initialized); + btAssert(m_workUnitTaskBuffers); + +#endif + + bool batch = true; + + if (batch) + { + if (m_currentPageEntry == MIDPHASE_NUM_WORKUNITS_PER_PAGE) + { + if (m_currentPage == MIDPHASE_NUM_WORKUNIT_PAGES-1) + { + // task buffer is full, issue current task. + // if all task buffers busy, this waits until SPU is done. + issueTask2(); + + // find new task buffer + for (unsigned int i = 0; i < m_maxNumOutstandingTasks; i++) + { + if (!m_taskBusy[i]) + { + m_currentTask = i; + //init the task data + + break; + } + } + + m_currentPage = 0; + } + else + { + m_currentPage++; + } + + m_currentPageEntry = 0; + } + } + + { + + + + SpuGatherAndProcessWorkUnitInput &wuInput = + *(reinterpret_cast + (MIDPHASE_ENTRY_PTR(m_currentTask, m_currentPage, m_currentPageEntry))); + + wuInput.m_pairArrayPtr = reinterpret_cast(pairArrayPtr); + wuInput.m_startIndex = startIndex; + wuInput.m_endIndex = endIndex; + + + + m_currentPageEntry++; + + if (!batch) + { + issueTask2(); + + // find new task buffer + for (unsigned int i = 0; i < m_maxNumOutstandingTasks; i++) + { + if (!m_taskBusy[i]) + { + m_currentTask = i; + //init the task data + + break; + } + } + + m_currentPage = 0; + m_currentPageEntry =0; + } + } +} + + +void +SpuCollisionTaskProcess::flush2() +{ +#ifdef DEBUG_SPU_TASK_SCHEDULING + printf("\nSpuCollisionTaskProcess::flush()\n"); +#endif //DEBUG_SPU_TASK_SCHEDULING + + // if there's a partially filled task buffer, submit that task + if (m_currentPage > 0 || m_currentPageEntry > 0) + { + issueTask2(); + } + + + // all tasks are issued, wait for all tasks to be complete + while(m_numBusyTasks > 0) + { + // Consolidating SPU code + unsigned int taskId=-1; + unsigned int outputSize; + + for (int i=0;i=0); + + + { + + // SPURS support. + m_threadInterface->waitForResponse(&taskId, &outputSize); + } +// printf("flush2 taskId %d completed, numBusy =%d \n",taskId,m_numBusyTasks); + //printf("PPU: flushing, received event: %u %d\n", taskId, outputSize); + + //postProcess(taskId, outputSize); + + m_taskBusy[taskId] = false; + + m_numBusyTasks--; + } + + +} diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuCollisionTaskProcess.h b/Engine/lib/bullet/src/BulletMultiThreaded/SpuCollisionTaskProcess.h new file mode 100644 index 000000000..a9ffde1a5 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuCollisionTaskProcess.h @@ -0,0 +1,163 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.com + +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 SPU_COLLISION_TASK_PROCESS_H +#define SPU_COLLISION_TASK_PROCESS_H + +#include + +#include + +#include "PlatformDefinitions.h" +#include "LinearMath/btAlignedObjectArray.h" +#include "SpuNarrowPhaseCollisionTask/SpuGatheringCollisionTask.h" // for definitions processCollisionTask and createCollisionLocalStoreMemory + +#include "btThreadSupportInterface.h" + + +//#include "SPUAssert.h" +#include + + +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "BulletCollision/CollisionShapes/btCollisionShape.h" +#include "BulletCollision/CollisionShapes/btConvexShape.h" + +#include + +#include + + +#define DEBUG_SpuCollisionTaskProcess 1 + + +#define CMD_GATHER_AND_PROCESS_PAIRLIST 1 + +class btCollisionObject; +class btPersistentManifold; +class btDispatcher; + + +/////Task Description for SPU collision detection +//struct SpuGatherAndProcessPairsTaskDesc +//{ +// uint64_t inPtr;//m_pairArrayPtr; +// //mutex variable +// uint32_t m_someMutexVariableInMainMemory; +// +// uint64_t m_dispatcher; +// +// uint32_t numOnLastPage; +// +// uint16_t numPages; +// uint16_t taskId; +// +// struct CollisionTask_LocalStoreMemory* m_lsMemory; +//} +// +//#if defined(__CELLOS_LV2__) || defined(USE_LIBSPE2) +//__attribute__ ((aligned (16))) +//#endif +//; + + +///MidphaseWorkUnitInput stores individual primitive versus mesh collision detection input, to be processed by the SPU. +ATTRIBUTE_ALIGNED16(struct) SpuGatherAndProcessWorkUnitInput +{ + uint64_t m_pairArrayPtr; + int m_startIndex; + int m_endIndex; +}; + + + + +/// SpuCollisionTaskProcess handles SPU processing of collision pairs. +/// Maintains a set of task buffers. +/// When the task is full, the task is issued for SPUs to process. Contact output goes into btPersistentManifold +/// associated with each task. +/// When PPU issues a task, it will look for completed task buffers +/// PPU will do postprocessing, dependent on workunit output (not likely) +class SpuCollisionTaskProcess +{ + + unsigned char *m_workUnitTaskBuffers; + + + // track task buffers that are being used, and total busy tasks + btAlignedObjectArray m_taskBusy; + btAlignedObjectArray m_spuGatherTaskDesc; + + class btThreadSupportInterface* m_threadInterface; + + unsigned int m_maxNumOutstandingTasks; + + unsigned int m_numBusyTasks; + + // the current task and the current entry to insert a new work unit + unsigned int m_currentTask; + unsigned int m_currentPage; + unsigned int m_currentPageEntry; + + bool m_useEpa; + +#ifdef DEBUG_SpuCollisionTaskProcess + bool m_initialized; +#endif + void issueTask2(); + //void postProcess(unsigned int taskId, int outputSize); + +public: + SpuCollisionTaskProcess(btThreadSupportInterface* threadInterface, unsigned int maxNumOutstandingTasks); + + ~SpuCollisionTaskProcess(); + + ///call initialize in the beginning of the frame, before addCollisionPairToTask + void initialize2(bool useEpa = false); + + ///batch up additional work to a current task for SPU processing. When batch is full, it issues the task. + void addWorkToTask(void* pairArrayPtr,int startIndex,int endIndex); + + ///call flush to submit potential outstanding work to SPUs and wait for all involved SPUs to be finished + void flush2(); + + /// set the maximum number of SPU tasks allocated + void setNumTasks(int maxNumTasks); + + int getNumTasks() const + { + return m_maxNumOutstandingTasks; + } +}; + + + +#define MIDPHASE_TASK_PTR(task) (&m_workUnitTaskBuffers[0] + MIDPHASE_WORKUNIT_TASK_SIZE*task) +#define MIDPHASE_ENTRY_PTR(task,page,entry) (MIDPHASE_TASK_PTR(task) + MIDPHASE_WORKUNIT_PAGE_SIZE*page + sizeof(SpuGatherAndProcessWorkUnitInput)*entry) +#define MIDPHASE_OUTPUT_PTR(task) (&m_contactOutputBuffers[0] + MIDPHASE_MAX_CONTACT_BUFFER_SIZE*task) +#define MIDPHASE_TREENODES_PTR(task) (&m_complexShapeBuffers[0] + MIDPHASE_COMPLEX_SHAPE_BUFFER_SIZE*task) + + +#define MIDPHASE_WORKUNIT_PAGE_SIZE (16) +//#define MIDPHASE_WORKUNIT_PAGE_SIZE (128) + +#define MIDPHASE_NUM_WORKUNIT_PAGES 1 +#define MIDPHASE_WORKUNIT_TASK_SIZE (MIDPHASE_WORKUNIT_PAGE_SIZE*MIDPHASE_NUM_WORKUNIT_PAGES) +#define MIDPHASE_NUM_WORKUNITS_PER_PAGE (MIDPHASE_WORKUNIT_PAGE_SIZE / sizeof(SpuGatherAndProcessWorkUnitInput)) +#define MIDPHASE_NUM_WORKUNITS_PER_TASK (MIDPHASE_NUM_WORKUNITS_PER_PAGE*MIDPHASE_NUM_WORKUNIT_PAGES) + + +#endif // SPU_COLLISION_TASK_PROCESS_H + diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuContactManifoldCollisionAlgorithm.cpp b/Engine/lib/bullet/src/BulletMultiThreaded/SpuContactManifoldCollisionAlgorithm.cpp new file mode 100644 index 000000000..286b63191 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuContactManifoldCollisionAlgorithm.cpp @@ -0,0 +1,69 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.com + +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 "SpuContactManifoldCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "BulletCollision/CollisionShapes/btCollisionShape.h" +#include "BulletCollision/CollisionShapes/btPolyhedralConvexShape.h" + + + + +void SpuContactManifoldCollisionAlgorithm::processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + btAssert(0); +} + +btScalar SpuContactManifoldCollisionAlgorithm::calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + btAssert(0); + return 1.f; +} + +#ifndef __SPU__ +SpuContactManifoldCollisionAlgorithm::SpuContactManifoldCollisionAlgorithm(const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* body0,btCollisionObject* body1) +:btCollisionAlgorithm(ci) +#ifdef USE_SEPDISTANCE_UTIL +,m_sepDistance(body0->getCollisionShape()->getAngularMotionDisc(),body1->getCollisionShape()->getAngularMotionDisc()) +#endif //USE_SEPDISTANCE_UTIL +{ + m_manifoldPtr = m_dispatcher->getNewManifold(body0,body1); + m_shapeType0 = body0->getCollisionShape()->getShapeType(); + m_shapeType1 = body1->getCollisionShape()->getShapeType(); + m_collisionMargin0 = body0->getCollisionShape()->getMargin(); + m_collisionMargin1 = body1->getCollisionShape()->getMargin(); + m_collisionObject0 = body0; + m_collisionObject1 = body1; + + if (body0->getCollisionShape()->isPolyhedral()) + { + btPolyhedralConvexShape* convex0 = (btPolyhedralConvexShape*)body0->getCollisionShape(); + m_shapeDimensions0 = convex0->getImplicitShapeDimensions(); + } + if (body1->getCollisionShape()->isPolyhedral()) + { + btPolyhedralConvexShape* convex1 = (btPolyhedralConvexShape*)body1->getCollisionShape(); + m_shapeDimensions1 = convex1->getImplicitShapeDimensions(); + } +} +#endif //__SPU__ + + +SpuContactManifoldCollisionAlgorithm::~SpuContactManifoldCollisionAlgorithm() +{ + if (m_manifoldPtr) + m_dispatcher->releaseManifold(m_manifoldPtr); +} diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuContactManifoldCollisionAlgorithm.h b/Engine/lib/bullet/src/BulletMultiThreaded/SpuContactManifoldCollisionAlgorithm.h new file mode 100644 index 000000000..151cb2c79 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuContactManifoldCollisionAlgorithm.h @@ -0,0 +1,120 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.com + +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 SPU_CONTACTMANIFOLD_COLLISION_ALGORITHM_H +#define SPU_CONTACTMANIFOLD_COLLISION_ALGORITHM_H + +#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "BulletCollision/CollisionDispatch/btCollisionCreateFunc.h" +#include "BulletCollision/BroadphaseCollision/btDispatcher.h" +#include "LinearMath/btTransformUtil.h" + +class btPersistentManifold; + +//#define USE_SEPDISTANCE_UTIL 1 + +/// SpuContactManifoldCollisionAlgorithm provides contact manifold and should be processed on SPU. +ATTRIBUTE_ALIGNED16(class) SpuContactManifoldCollisionAlgorithm : public btCollisionAlgorithm +{ + btVector3 m_shapeDimensions0; + btVector3 m_shapeDimensions1; + btPersistentManifold* m_manifoldPtr; + int m_shapeType0; + int m_shapeType1; + float m_collisionMargin0; + float m_collisionMargin1; + + btCollisionObject* m_collisionObject0; + btCollisionObject* m_collisionObject1; + + + + +public: + + virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + + SpuContactManifoldCollisionAlgorithm(const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* body0,btCollisionObject* body1); +#ifdef USE_SEPDISTANCE_UTIL + btConvexSeparatingDistanceUtil m_sepDistance; +#endif //USE_SEPDISTANCE_UTIL + + virtual ~SpuContactManifoldCollisionAlgorithm(); + + virtual void getAllContactManifolds(btManifoldArray& manifoldArray) + { + if (m_manifoldPtr) + manifoldArray.push_back(m_manifoldPtr); + } + + btPersistentManifold* getContactManifoldPtr() + { + return m_manifoldPtr; + } + + btCollisionObject* getCollisionObject0() + { + return m_collisionObject0; + } + + btCollisionObject* getCollisionObject1() + { + return m_collisionObject1; + } + + int getShapeType0() const + { + return m_shapeType0; + } + + int getShapeType1() const + { + return m_shapeType1; + } + float getCollisionMargin0() const + { + return m_collisionMargin0; + } + float getCollisionMargin1() const + { + return m_collisionMargin1; + } + + const btVector3& getShapeDimensions0() const + { + return m_shapeDimensions0; + } + + const btVector3& getShapeDimensions1() const + { + return m_shapeDimensions1; + } + + struct CreateFunc :public btCollisionAlgorithmCreateFunc + { + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + void* mem = ci.m_dispatcher1->allocateCollisionAlgorithm(sizeof(SpuContactManifoldCollisionAlgorithm)); + return new(mem) SpuContactManifoldCollisionAlgorithm(ci,body0,body1); + } + }; + +}; + +#endif //SPU_CONTACTMANIFOLD_COLLISION_ALGORITHM_H diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuDoubleBuffer.h b/Engine/lib/bullet/src/BulletMultiThreaded/SpuDoubleBuffer.h new file mode 100644 index 000000000..801c86080 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuDoubleBuffer.h @@ -0,0 +1,110 @@ +#ifndef DOUBLE_BUFFER_H +#define DOUBLE_BUFFER_H + +#include "SpuFakeDma.h" +#include + + +///DoubleBuffer +template +class DoubleBuffer +{ +#if defined(__SPU__) || defined(USE_LIBSPE2) + ATTRIBUTE_ALIGNED128( T m_buffer0[size] ) ; + ATTRIBUTE_ALIGNED128( T m_buffer1[size] ) ; +#else + T m_buffer0[size]; + T m_buffer1[size]; +#endif + + T *m_frontBuffer; + T *m_backBuffer; + + unsigned int m_dmaTag; + bool m_dmaPending; +public: + bool isPending() const { return m_dmaPending;} + DoubleBuffer(); + + void init (); + + // dma get and put commands + void backBufferDmaGet(uint64_t ea, unsigned int numBytes, unsigned int tag); + void backBufferDmaPut(uint64_t ea, unsigned int numBytes, unsigned int tag); + + // gets pointer to a buffer + T *getFront(); + T *getBack(); + + // if back buffer dma was started, wait for it to complete + // then move back to front and vice versa + T *swapBuffers(); +}; + +template +DoubleBuffer::DoubleBuffer() +{ + init (); +} + +template +void DoubleBuffer::init() +{ + this->m_dmaPending = false; + this->m_frontBuffer = &this->m_buffer0[0]; + this->m_backBuffer = &this->m_buffer1[0]; +} + +template +void +DoubleBuffer::backBufferDmaGet(uint64_t ea, unsigned int numBytes, unsigned int tag) +{ + m_dmaPending = true; + m_dmaTag = tag; + if (numBytes) + { + m_backBuffer = (T*)cellDmaLargeGetReadOnly(m_backBuffer, ea, numBytes, tag, 0, 0); + } +} + +template +void +DoubleBuffer::backBufferDmaPut(uint64_t ea, unsigned int numBytes, unsigned int tag) +{ + m_dmaPending = true; + m_dmaTag = tag; + cellDmaLargePut(m_backBuffer, ea, numBytes, tag, 0, 0); +} + +template +T * +DoubleBuffer::getFront() +{ + return m_frontBuffer; +} + +template +T * +DoubleBuffer::getBack() +{ + return m_backBuffer; +} + +template +T * +DoubleBuffer::swapBuffers() +{ + if (m_dmaPending) + { + cellDmaWaitTagStatusAll(1< //for btAssert +//Disabling memcpy sometimes helps debugging DMA + +#define USE_MEMCPY 1 +#ifdef USE_MEMCPY + +#endif + + +void* cellDmaLargeGetReadOnly(void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid) +{ + +#if defined (__SPU__) || defined (USE_LIBSPE2) + cellDmaLargeGet(ls,ea,size,tag,tid,rid); + return ls; +#else + return (void*)(uint32_t)ea; +#endif +} + +void* cellDmaSmallGetReadOnly(void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid) +{ +#if defined (__SPU__) || defined (USE_LIBSPE2) + mfc_get(ls,ea,size,tag,0,0); + return ls; +#else + return (void*)(uint32_t)ea; +#endif +} + + + + +void* cellDmaGetReadOnly(void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid) +{ +#if defined (__SPU__) || defined (USE_LIBSPE2) + cellDmaGet(ls,ea,size,tag,tid,rid); + return ls; +#else + return (void*)(uint32_t)ea; +#endif +} + + +///this unalignedDma should not be frequently used, only for small data. It handles alignment and performs check on size (<16 bytes) +int stallingUnalignedDmaSmallGet(void *ls, uint64_t ea, uint32_t size) +{ + + btAssert(size<32); + + ATTRIBUTE_ALIGNED16(char tmpBuffer[32]); + + + char* localStore = (char*)ls; + uint32_t i; + + + ///make sure last 4 bits are the same, for cellDmaSmallGet + uint32_t last4BitsOffset = ea & 0x0f; + char* tmpTarget = tmpBuffer + last4BitsOffset; + +#if defined (__SPU__) || defined (USE_LIBSPE2) + + int remainingSize = size; + +//#define FORCE_cellDmaUnalignedGet 1 +#ifdef FORCE_cellDmaUnalignedGet + cellDmaUnalignedGet(tmpTarget,ea,size,DMA_TAG(1),0,0); +#else + char* remainingTmpTarget = tmpTarget; + uint64_t remainingEa = ea; + + while (remainingSize) + { + switch (remainingSize) + { + case 1: + case 2: + case 4: + case 8: + case 16: + { + mfc_get(remainingTmpTarget,remainingEa,remainingSize,DMA_TAG(1),0,0); + remainingSize=0; + break; + } + default: + { + //spu_printf("unaligned DMA with non-natural size:%d\n",remainingSize); + int actualSize = 0; + + if (remainingSize > 16) + actualSize = 16; + else + if (remainingSize >8) + actualSize=8; + else + if (remainingSize >4) + actualSize=4; + else + if (remainingSize >2) + actualSize=2; + mfc_get(remainingTmpTarget,remainingEa,actualSize,DMA_TAG(1),0,0); + remainingSize-=actualSize; + remainingTmpTarget+=actualSize; + remainingEa += actualSize; + } + } + } +#endif//FORCE_cellDmaUnalignedGet + +#else + char* mainMem = (char*)ea; + //copy into final destination +#ifdef USE_MEMCPY + + memcpy(tmpTarget,mainMem,size); +#else + for ( i=0;i +#include + +#define DMA_TAG(xfer) (xfer + 1) +#define DMA_MASK(xfer) (1 << DMA_TAG(xfer)) + +#else // !USE_LIBSPE2 + +#define DMA_TAG(xfer) (xfer + 1) +#define DMA_MASK(xfer) (1 << DMA_TAG(xfer)) + +#include + +#define DEBUG_DMA +#ifdef DEBUG_DMA +#define dUASSERT(a,b) if (!(a)) { printf(b);} +#define uintsize ppu_address_t + +#define cellDmaLargeGet(ls, ea, size, tag, tid, rid) if ( (((uintsize)ls%16) != ((uintsize)ea%16)) || ((((uintsize)ea%16) || ((uintsize)ls%16)) && (( ((uintsize)ls%16) != ((uintsize)size%16) ) || ( ((uintsize)ea%16) != ((uintsize)size%16) ) ) ) || ( ((uintsize)size%16) && ((uintsize)size!=1) && ((uintsize)size!=2) && ((uintsize)size!=4) && ((uintsize)size!=8) ) || (size >= 16384) || !(uintsize)ls || !(uintsize)ea) { \ + dUASSERT( (((uintsize)ea % 16) == 0) || (size < 16), "XDR Address not aligned: "); \ + dUASSERT( (((uintsize)ls % 16) == 0) || (size < 16), "LS Address not aligned: "); \ + dUASSERT( ((((uintsize)ls % size) == 0) && (((uintsize)ea % size) == 0)) || (size > 16), "Not naturally aligned: "); \ + dUASSERT((size == 1) || (size == 2) || (size == 4) || (size == 8) || ((size % 16) == 0), "size not a multiple of 16byte: "); \ + dUASSERT(size < 16384, "size too big: "); \ + dUASSERT( ((uintsize)ea%16)==((uintsize)ls%16), "wrong Quadword alignment of LS and EA: "); \ + dUASSERT(ea != 0, "Nullpointer EA: "); dUASSERT(ls != 0, "Nullpointer LS: ");\ + printf("GET %s:%d from: 0x%x, to: 0x%x - %d bytes\n", __FILE__, __LINE__, (unsigned int)ea,(unsigned int)ls,(unsigned int)size);\ + } \ + mfc_get(ls, ea, size, tag, tid, rid) +#define cellDmaGet(ls, ea, size, tag, tid, rid) if ( (((uintsize)ls%16) != ((uintsize)ea%16)) || ((((uintsize)ea%16) || ((uintsize)ls%16)) && (( ((uintsize)ls%16) != ((uintsize)size%16) ) || ( ((uintsize)ea%16) != ((uintsize)size%16) ) ) ) || ( ((uintsize)size%16) && ((uintsize)size!=1) && ((uintsize)size!=2) && ((uintsize)size!=4) && ((uintsize)size!=8) ) || (size >= 16384) || !(uintsize)ls || !(uintsize)ea) { \ + dUASSERT( (((uintsize)ea % 16) == 0) || (size < 16), "XDR Address not aligned: "); \ + dUASSERT( (((uintsize)ls % 16) == 0) || (size < 16), "LS Address not aligned: "); \ + dUASSERT( ((((uintsize)ls % size) == 0) && (((uintsize)ea % size) == 0)) || (size > 16), "Not naturally aligned: "); \ + dUASSERT((size == 1) || (size == 2) || (size == 4) || (size == 8) || ((size % 16) == 0), "size not a multiple of 16byte: "); \ + dUASSERT(size < 16384, "size too big: "); \ + dUASSERT( ((uintsize)ea%16)==((uintsize)ls%16), "wrong Quadword alignment of LS and EA: "); \ + dUASSERT(ea != 0, "Nullpointer EA: "); dUASSERT(ls != 0, "Nullpointer LS: ");\ + printf("GET %s:%d from: 0x%x, to: 0x%x - %d bytes\n", __FILE__, __LINE__, (unsigned int)ea,(unsigned int)ls,(unsigned int)size);\ + } \ + mfc_get(ls, ea, size, tag, tid, rid) +#define cellDmaLargePut(ls, ea, size, tag, tid, rid) if ( (((uintsize)ls%16) != ((uintsize)ea%16)) || ((((uintsize)ea%16) || ((uintsize)ls%16)) && (( ((uintsize)ls%16) != ((uintsize)size%16) ) || ( ((uintsize)ea%16) != ((uintsize)size%16) ) ) ) || ( ((uintsize)size%16) && ((uintsize)size!=1) && ((uintsize)size!=2) && ((uintsize)size!=4) && ((uintsize)size!=8) ) || (size >= 16384) || !(uintsize)ls || !(uintsize)ea) { \ + dUASSERT( (((uintsize)ea % 16) == 0) || (size < 16), "XDR Address not aligned: "); \ + dUASSERT( (((uintsize)ls % 16) == 0) || (size < 16), "LS Address not aligned: "); \ + dUASSERT( ((((uintsize)ls % size) == 0) && (((uintsize)ea % size) == 0)) || (size > 16), "Not naturally aligned: "); \ + dUASSERT((size == 1) || (size == 2) || (size == 4) || (size == 8) || ((size % 16) == 0), "size not a multiple of 16byte: "); \ + dUASSERT(size < 16384, "size too big: "); \ + dUASSERT( ((uintsize)ea%16)==((uintsize)ls%16), "wrong Quadword alignment of LS and EA: "); \ + dUASSERT(ea != 0, "Nullpointer EA: "); dUASSERT(ls != 0, "Nullpointer LS: ");\ + printf("PUT %s:%d from: 0x%x, to: 0x%x - %d bytes\n", __FILE__, __LINE__, (unsigned int)ls,(unsigned int)ea,(unsigned int)size); \ + } \ + mfc_put(ls, ea, size, tag, tid, rid) +#define cellDmaSmallGet(ls, ea, size, tag, tid, rid) if ( (((uintsize)ls%16) != ((uintsize)ea%16)) || ((((uintsize)ea%16) || ((uintsize)ls%16)) && (( ((uintsize)ls%16) != ((uintsize)size%16) ) || ( ((uintsize)ea%16) != ((uintsize)size%16) ) ) ) || ( ((uintsize)size%16) && ((uintsize)size!=1) && ((uintsize)size!=2) && ((uintsize)size!=4) && ((uintsize)size!=8) ) || (size >= 16384) || !(uintsize)ls || !(uintsize)ea) { \ + dUASSERT( (((uintsize)ea % 16) == 0) || (size < 16), "XDR Address not aligned: "); \ + dUASSERT( (((uintsize)ls % 16) == 0) || (size < 16), "LS Address not aligned: "); \ + dUASSERT( ((((uintsize)ls % size) == 0) && (((uintsize)ea % size) == 0)) || (size > 16), "Not naturally aligned: "); \ + dUASSERT((size == 1) || (size == 2) || (size == 4) || (size == 8) || ((size % 16) == 0), "size not a multiple of 16byte: "); \ + dUASSERT(size < 16384, "size too big: "); \ + dUASSERT( ((uintsize)ea%16)==((uintsize)ls%16), "wrong Quadword alignment of LS and EA: "); \ + dUASSERT(ea != 0, "Nullpointer EA: "); dUASSERT(ls != 0, "Nullpointer LS: ");\ + printf("GET %s:%d from: 0x%x, to: 0x%x - %d bytes\n", __FILE__, __LINE__, (unsigned int)ea,(unsigned int)ls,(unsigned int)size);\ + } \ + mfc_get(ls, ea, size, tag, tid, rid) +#define cellDmaWaitTagStatusAll(ignore) mfc_write_tag_mask(ignore) ; mfc_read_tag_status_all() + +#else +#define cellDmaLargeGet(ls, ea, size, tag, tid, rid) mfc_get(ls, ea, size, tag, tid, rid) +#define cellDmaGet(ls, ea, size, tag, tid, rid) mfc_get(ls, ea, size, tag, tid, rid) +#define cellDmaLargePut(ls, ea, size, tag, tid, rid) mfc_put(ls, ea, size, tag, tid, rid) +#define cellDmaSmallGet(ls, ea, size, tag, tid, rid) mfc_get(ls, ea, size, tag, tid, rid) +#define cellDmaWaitTagStatusAll(ignore) mfc_write_tag_mask(ignore) ; mfc_read_tag_status_all() +#endif // DEBUG_DMA + + + + + + + + +#endif // USE_LIBSPE2 +#else // !__SPU__ +//Simulate DMA using memcpy or direct access on non-CELL platforms that don't have DMAs and SPUs (Win32, Mac, Linux etc) +//Potential to add networked simulation using this interface + +#define DMA_TAG(a) (a) +#define DMA_MASK(a) (a) + + /// cellDmaLargeGet Win32 replacements for Cell DMA to allow simulating most of the SPU code (just memcpy) + int cellDmaLargeGet(void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid); + int cellDmaGet(void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid); + /// cellDmaLargePut Win32 replacements for Cell DMA to allow simulating most of the SPU code (just memcpy) + int cellDmaLargePut(const void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid); + /// cellDmaWaitTagStatusAll Win32 replacements for Cell DMA to allow simulating most of the SPU code (just memcpy) + void cellDmaWaitTagStatusAll(int ignore); + + +#endif //__CELLOS_LV2__ + +///stallingUnalignedDmaSmallGet internally uses DMA_TAG(1) +int stallingUnalignedDmaSmallGet(void *ls, uint64_t ea, uint32_t size); + + +void* cellDmaLargeGetReadOnly(void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid); +void* cellDmaGetReadOnly(void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid); +void* cellDmaSmallGetReadOnly(void *ls, uint64_t ea, uint32_t size, uint32_t tag, uint32_t tid, uint32_t rid); + + +#endif //FAKE_DMA_H diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuGatheringCollisionDispatcher.cpp b/Engine/lib/bullet/src/BulletMultiThreaded/SpuGatheringCollisionDispatcher.cpp new file mode 100644 index 000000000..f81f75190 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuGatheringCollisionDispatcher.cpp @@ -0,0 +1,238 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.com + +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 "SpuGatheringCollisionDispatcher.h" +#include "SpuCollisionTaskProcess.h" + + +#include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h" +#include "BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.h" +#include "SpuContactManifoldCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "BulletCollision/CollisionShapes/btCollisionShape.h" +#include "LinearMath/btQuickprof.h" + + + + +SpuGatheringCollisionDispatcher::SpuGatheringCollisionDispatcher(class btThreadSupportInterface* threadInterface, unsigned int maxNumOutstandingTasks,btCollisionConfiguration* collisionConfiguration) +:btCollisionDispatcher(collisionConfiguration), +m_spuCollisionTaskProcess(0), +m_threadInterface(threadInterface), +m_maxNumOutstandingTasks(maxNumOutstandingTasks) +{ + +} + + +bool SpuGatheringCollisionDispatcher::supportsDispatchPairOnSpu(int proxyType0,int proxyType1) +{ + bool supported0 = ( + (proxyType0 == BOX_SHAPE_PROXYTYPE) || + (proxyType0 == TRIANGLE_SHAPE_PROXYTYPE) || + (proxyType0 == SPHERE_SHAPE_PROXYTYPE) || + (proxyType0 == CAPSULE_SHAPE_PROXYTYPE) || + (proxyType0 == CYLINDER_SHAPE_PROXYTYPE) || +// (proxyType0 == CONE_SHAPE_PROXYTYPE) || + (proxyType0 == TRIANGLE_MESH_SHAPE_PROXYTYPE) || + (proxyType0 == CONVEX_HULL_SHAPE_PROXYTYPE)|| + (proxyType0 == COMPOUND_SHAPE_PROXYTYPE) + ); + + bool supported1 = ( + (proxyType1 == BOX_SHAPE_PROXYTYPE) || + (proxyType1 == TRIANGLE_SHAPE_PROXYTYPE) || + (proxyType1 == SPHERE_SHAPE_PROXYTYPE) || + (proxyType1 == CAPSULE_SHAPE_PROXYTYPE) || + (proxyType1 == CYLINDER_SHAPE_PROXYTYPE) || +// (proxyType1 == CONE_SHAPE_PROXYTYPE) || + (proxyType1 == TRIANGLE_MESH_SHAPE_PROXYTYPE) || + (proxyType1 == CONVEX_HULL_SHAPE_PROXYTYPE) || + (proxyType1 == COMPOUND_SHAPE_PROXYTYPE) + ); + + return supported0 && supported1; +} + + + +SpuGatheringCollisionDispatcher::~SpuGatheringCollisionDispatcher() +{ + if (m_spuCollisionTaskProcess) + delete m_spuCollisionTaskProcess; + +} + +#include "stdio.h" + + + +///interface for iterating all overlapping collision pairs, no matter how those pairs are stored (array, set, map etc) +///this is useful for the collision dispatcher. +class btSpuCollisionPairCallback : public btOverlapCallback +{ + const btDispatcherInfo& m_dispatchInfo; + SpuGatheringCollisionDispatcher* m_dispatcher; + +public: + + btSpuCollisionPairCallback(const btDispatcherInfo& dispatchInfo, SpuGatheringCollisionDispatcher* dispatcher) + :m_dispatchInfo(dispatchInfo), + m_dispatcher(dispatcher) + { + } + + virtual bool processOverlap(btBroadphasePair& collisionPair) + { + + + //PPU version + //(*m_dispatcher->getNearCallback())(collisionPair,*m_dispatcher,m_dispatchInfo); + + //only support discrete collision detection for now, we could fallback on PPU/unoptimized version for TOI/CCD + btAssert(m_dispatchInfo.m_dispatchFunc == btDispatcherInfo::DISPATCH_DISCRETE); + + //by default, Bullet will use this near callback + { + ///userInfo is used to determine if the SPU has to handle this case or not (skip PPU tasks) + if (!collisionPair.m_internalTmpValue) + { + collisionPair.m_internalTmpValue = 1; + } + if (!collisionPair.m_algorithm) + { + btCollisionObject* colObj0 = (btCollisionObject*)collisionPair.m_pProxy0->m_clientObject; + btCollisionObject* colObj1 = (btCollisionObject*)collisionPair.m_pProxy1->m_clientObject; + + btCollisionAlgorithmConstructionInfo ci; + ci.m_dispatcher1 = m_dispatcher; + ci.m_manifold = 0; + + if (m_dispatcher->needsCollision(colObj0,colObj1)) + { + int proxyType0 = colObj0->getCollisionShape()->getShapeType(); + int proxyType1 = colObj1->getCollisionShape()->getShapeType(); + if (m_dispatcher->supportsDispatchPairOnSpu(proxyType0,proxyType1)) + { + int so = sizeof(SpuContactManifoldCollisionAlgorithm); +#ifdef ALLOCATE_SEPARATELY + void* mem = btAlignedAlloc(so,16);//m_dispatcher->allocateCollisionAlgorithm(so); +#else + void* mem = m_dispatcher->allocateCollisionAlgorithm(so); +#endif + collisionPair.m_algorithm = new(mem) SpuContactManifoldCollisionAlgorithm(ci,colObj0,colObj1); + collisionPair.m_internalTmpValue = 2; + } else + { + collisionPair.m_algorithm = m_dispatcher->findAlgorithm(colObj0,colObj1); + collisionPair.m_internalTmpValue = 3; + } + } + } + } + return false; + } +}; + +void SpuGatheringCollisionDispatcher::dispatchAllCollisionPairs(btOverlappingPairCache* pairCache,const btDispatcherInfo& dispatchInfo, btDispatcher* dispatcher) +{ + + if (dispatchInfo.m_enableSPU) + { + m_maxNumOutstandingTasks = m_threadInterface->getNumTasks(); + + { + BT_PROFILE("processAllOverlappingPairs"); + + if (!m_spuCollisionTaskProcess) + m_spuCollisionTaskProcess = new SpuCollisionTaskProcess(m_threadInterface,m_maxNumOutstandingTasks); + + m_spuCollisionTaskProcess->setNumTasks(m_maxNumOutstandingTasks); + // printf("m_maxNumOutstandingTasks =%d\n",m_maxNumOutstandingTasks); + + m_spuCollisionTaskProcess->initialize2(dispatchInfo.m_useEpa); + + + ///modified version of btCollisionDispatcher::dispatchAllCollisionPairs: + { + btSpuCollisionPairCallback collisionCallback(dispatchInfo,this); + + pairCache->processAllOverlappingPairs(&collisionCallback,dispatcher); + } + } + + //send one big batch + int numTotalPairs = pairCache->getNumOverlappingPairs(); + btBroadphasePair* pairPtr = pairCache->getOverlappingPairArrayPtr(); + int i; + { + BT_PROFILE("addWorkToTask"); + for (i=0;iaddWorkToTask(pairPtr,i,endIndex); + i = endIndex; + } + } + + { + BT_PROFILE("PPU fallback"); + //handle PPU fallback pairs + for (i=0;im_clientObject; + btCollisionObject* colObj1 = (btCollisionObject*)collisionPair.m_pProxy1->m_clientObject; + + if (dispatcher->needsCollision(colObj0,colObj1)) + { + btManifoldResult contactPointResult(colObj0,colObj1); + + if (dispatchInfo.m_dispatchFunc == btDispatcherInfo::DISPATCH_DISCRETE) + { + //discrete collision detection query + collisionPair.m_algorithm->processCollision(colObj0,colObj1,dispatchInfo,&contactPointResult); + } else + { + //continuous collision detection query, time of impact (toi) + btScalar toi = collisionPair.m_algorithm->calculateTimeOfImpact(colObj0,colObj1,dispatchInfo,&contactPointResult); + if (dispatchInfo.m_timeOfImpact > toi) + dispatchInfo.m_timeOfImpact = toi; + + } + } + } + } + } + } + { + BT_PROFILE("flush2"); + //make sure all SPU work is done + m_spuCollisionTaskProcess->flush2(); + } + + } else + { + ///PPU fallback + ///!Need to make sure to clear all 'algorithms' when switching between SPU and PPU + btCollisionDispatcher::dispatchAllCollisionPairs(pairCache,dispatchInfo,dispatcher); + } +} diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuGatheringCollisionDispatcher.h b/Engine/lib/bullet/src/BulletMultiThreaded/SpuGatheringCollisionDispatcher.h new file mode 100644 index 000000000..120751a1e --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuGatheringCollisionDispatcher.h @@ -0,0 +1,69 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.com + +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 SPU_GATHERING_COLLISION__DISPATCHER_H +#define SPU_GATHERING_COLLISION__DISPATCHER_H + +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" + + +///Tuning value to optimized SPU utilization +///Too small value means Task overhead is large compared to computation (too fine granularity) +///Too big value might render some SPUs are idle, while a few other SPUs are doing all work. +//#define SPU_BATCHSIZE_BROADPHASE_PAIRS 8 +//#define SPU_BATCHSIZE_BROADPHASE_PAIRS 16 +#define SPU_BATCHSIZE_BROADPHASE_PAIRS 64 +//#define SPU_BATCHSIZE_BROADPHASE_PAIRS 128 +//#define SPU_BATCHSIZE_BROADPHASE_PAIRS 256 +//#define SPU_BATCHSIZE_BROADPHASE_PAIRS 1024 + + + +class SpuCollisionTaskProcess; + +///SpuGatheringCollisionDispatcher can use SPU to gather and calculate collision detection +///Time of Impact, Closest Points and Penetration Depth. +class SpuGatheringCollisionDispatcher : public btCollisionDispatcher +{ + + SpuCollisionTaskProcess* m_spuCollisionTaskProcess; + +protected: + + class btThreadSupportInterface* m_threadInterface; + + unsigned int m_maxNumOutstandingTasks; + + +public: + + //can be used by SPU collision algorithms + SpuCollisionTaskProcess* getSpuCollisionTaskProcess() + { + return m_spuCollisionTaskProcess; + } + + SpuGatheringCollisionDispatcher (class btThreadSupportInterface* threadInterface, unsigned int maxNumOutstandingTasks,btCollisionConfiguration* collisionConfiguration); + + virtual ~SpuGatheringCollisionDispatcher(); + + bool supportsDispatchPairOnSpu(int proxyType0,int proxyType1); + + virtual void dispatchAllCollisionPairs(btOverlappingPairCache* pairCache,const btDispatcherInfo& dispatchInfo,btDispatcher* dispatcher) ; + +}; + + + +#endif //SPU_GATHERING_COLLISION__DISPATCHER_H diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuLibspe2Support.cpp b/Engine/lib/bullet/src/BulletMultiThreaded/SpuLibspe2Support.cpp new file mode 100644 index 000000000..a312450ed --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuLibspe2Support.cpp @@ -0,0 +1,257 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.com + +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. +*/ + +#ifdef USE_LIBSPE2 + +#include "SpuLibspe2Support.h" + + + + +//SpuLibspe2Support helps to initialize/shutdown libspe2, start/stop SPU tasks and communication +///Setup and initialize SPU/CELL/Libspe2 +SpuLibspe2Support::SpuLibspe2Support(spe_program_handle_t *speprog, int numThreads) +{ + this->program = speprog; + this->numThreads = ((numThreads <= spe_cpu_info_get(SPE_COUNT_PHYSICAL_SPES, -1)) ? numThreads : spe_cpu_info_get(SPE_COUNT_PHYSICAL_SPES, -1)); +} + +///cleanup/shutdown Libspe2 +SpuLibspe2Support::~SpuLibspe2Support() +{ + + stopSPU(); +} + + + +///send messages to SPUs +void SpuLibspe2Support::sendRequest(uint32_t uiCommand, uint32_t uiArgument0, uint32_t uiArgument1) +{ + spe_context_ptr_t context; + + switch (uiCommand) + { + case CMD_SAMPLE_TASK_COMMAND: + { + //get taskdescription + SpuSampleTaskDesc* taskDesc = (SpuSampleTaskDesc*) uiArgument0; + + btAssert(taskDesc->m_taskIdm_taskId]; + + //set data for spuStatus + spuStatus.m_commandId = uiCommand; + spuStatus.m_status = Spu_Status_Occupied; //set SPU as "occupied" + spuStatus.m_taskDesc.p = taskDesc; + + //get context + context = data[taskDesc->m_taskId].context; + + + taskDesc->m_mainMemoryPtr = reinterpret_cast (spuStatus.m_lsMemory.p); + + + break; + } + case CMD_GATHER_AND_PROCESS_PAIRLIST: + { + //get taskdescription + SpuGatherAndProcessPairsTaskDesc* taskDesc = (SpuGatherAndProcessPairsTaskDesc*) uiArgument0; + + btAssert(taskDesc->taskIdtaskId]; + + //set data for spuStatus + spuStatus.m_commandId = uiCommand; + spuStatus.m_status = Spu_Status_Occupied; //set SPU as "occupied" + spuStatus.m_taskDesc.p = taskDesc; + + //get context + context = data[taskDesc->taskId].context; + + + taskDesc->m_lsMemory = (CollisionTask_LocalStoreMemory*)spuStatus.m_lsMemory.p; + + break; + } + default: + { + ///not implemented + btAssert(0); + } + + }; + + + //write taskdescription in mailbox + unsigned int event = Spu_Mailbox_Event_Task; + spe_in_mbox_write(context, &event, 1, SPE_MBOX_ANY_NONBLOCKING); + +} + +///check for messages from SPUs +void SpuLibspe2Support::waitForResponse(unsigned int *puiArgument0, unsigned int *puiArgument1) +{ + ///We should wait for (one of) the first tasks to finish (or other SPU messages), and report its response + + ///A possible response can be 'yes, SPU handled it', or 'no, please do a PPU fallback' + + btAssert(m_activeSpuStatus.size()); + + + int last = -1; + + //find an active spu/thread + while(last < 0) + { + for (int i=0;i=0); + + + + *puiArgument0 = spuStatus.m_taskId; + *puiArgument1 = spuStatus.m_status; + + +} + + +void SpuLibspe2Support::startSPU() +{ + this->internal_startSPU(); +} + + + +///start the spus group (can be called at the beginning of each frame, to make sure that the right SPU program is loaded) +void SpuLibspe2Support::internal_startSPU() +{ + m_activeSpuStatus.resize(numThreads); + + + for (int i=0; i < numThreads; i++) + { + + if(data[i].context == NULL) + { + + /* Create context */ + if ((data[i].context = spe_context_create(0, NULL)) == NULL) + { + perror ("Failed creating context"); + exit(1); + } + + /* Load program into context */ + if(spe_program_load(data[i].context, this->program)) + { + perror ("Failed loading program"); + exit(1); + } + + m_activeSpuStatus[i].m_status = Spu_Status_Startup; + m_activeSpuStatus[i].m_taskId = i; + m_activeSpuStatus[i].m_commandId = 0; + m_activeSpuStatus[i].m_lsMemory.p = NULL; + + + data[i].entry = SPE_DEFAULT_ENTRY; + data[i].flags = 0; + data[i].argp.p = &m_activeSpuStatus[i]; + data[i].envp.p = NULL; + + /* Create thread for each SPE context */ + if (pthread_create(&data[i].pthread, NULL, &ppu_pthread_function, &(data[i]) )) + { + perror ("Failed creating thread"); + exit(1); + } + /* + else + { + printf("started thread %d\n",i); + }*/ + } + } + + + for (int i=0; i < numThreads; i++) + { + if(data[i].context != NULL) + { + while( m_activeSpuStatus[i].m_status == Spu_Status_Startup) + { + // wait for spu to set up + sched_yield(); + } + printf("Spu %d is ready\n", i); + } + } +} + +///tell the task scheduler we are done with the SPU tasks +void SpuLibspe2Support::stopSPU() +{ + // wait for all threads to finish + int i; + for ( i = 0; i < this->numThreads; i++ ) + { + + unsigned int event = Spu_Mailbox_Event_Shutdown; + spe_context_ptr_t context = data[i].context; + spe_in_mbox_write(context, &event, 1, SPE_MBOX_ALL_BLOCKING); + pthread_join (data[i].pthread, NULL); + + } + // close SPE program + spe_image_close(program); + // destroy SPE contexts + for ( i = 0; i < this->numThreads; i++ ) + { + if(data[i].context != NULL) + { + spe_context_destroy (data[i].context); + } + } + + m_activeSpuStatus.clear(); + +} + + + +#endif //USE_LIBSPE2 + diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuLibspe2Support.h b/Engine/lib/bullet/src/BulletMultiThreaded/SpuLibspe2Support.h new file mode 100644 index 000000000..a6d6baca4 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuLibspe2Support.h @@ -0,0 +1,180 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.com + +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 SPU_LIBSPE2_SUPPORT_H +#define SPU_LIBSPE2_SUPPORT_H + +#include //for uint32_t etc. + +#ifdef USE_LIBSPE2 + +#include +#include +//#include "SpuNarrowPhaseCollisionTask/SpuGatheringCollisionTask.h" +#include "PlatformDefinitions.h" + + +//extern struct SpuGatherAndProcessPairsTaskDesc; + +enum +{ + Spu_Mailbox_Event_Nothing = 0, + Spu_Mailbox_Event_Task = 1, + Spu_Mailbox_Event_Shutdown = 2, + + Spu_Mailbox_Event_ForceDword = 0xFFFFFFFF + +}; + +enum +{ + Spu_Status_Free = 0, + Spu_Status_Occupied = 1, + Spu_Status_Startup = 2, + + Spu_Status_ForceDword = 0xFFFFFFFF + +}; + + +struct btSpuStatus +{ + uint32_t m_taskId; + uint32_t m_commandId; + uint32_t m_status; + + addr64 m_taskDesc; + addr64 m_lsMemory; + +} +__attribute__ ((aligned (128))) +; + + + +#ifndef __SPU__ + +#include "LinearMath/btAlignedObjectArray.h" +#include "SpuCollisionTaskProcess.h" +#include "SpuSampleTaskProcess.h" +#include "btThreadSupportInterface.h" +#include +#include +#include + +#define MAX_SPUS 4 + +typedef struct ppu_pthread_data +{ + spe_context_ptr_t context; + pthread_t pthread; + unsigned int entry; + unsigned int flags; + addr64 argp; + addr64 envp; + spe_stop_info_t stopinfo; +} ppu_pthread_data_t; + + +static void *ppu_pthread_function(void *arg) +{ + ppu_pthread_data_t * datap = (ppu_pthread_data_t *)arg; + /* + int rc; + do + {*/ + spe_context_run(datap->context, &datap->entry, datap->flags, datap->argp.p, datap->envp.p, &datap->stopinfo); + if (datap->stopinfo.stop_reason == SPE_EXIT) + { + if (datap->stopinfo.result.spe_exit_code != 0) + { + perror("FAILED: SPE returned a non-zero exit status: \n"); + exit(1); + } + } + else + { + perror("FAILED: SPE abnormally terminated\n"); + exit(1); + } + + + //} while (rc > 0); // loop until exit or error, and while any stop & signal + pthread_exit(NULL); +} + + + + + + +///SpuLibspe2Support helps to initialize/shutdown libspe2, start/stop SPU tasks and communication +class SpuLibspe2Support : public btThreadSupportInterface +{ + + btAlignedObjectArray m_activeSpuStatus; + +public: + //Setup and initialize SPU/CELL/Libspe2 + SpuLibspe2Support(spe_program_handle_t *speprog,int numThreads); + + // SPE program handle ptr. + spe_program_handle_t *program; + + // SPE program data + ppu_pthread_data_t data[MAX_SPUS]; + + //cleanup/shutdown Libspe2 + ~SpuLibspe2Support(); + + ///send messages to SPUs + void sendRequest(uint32_t uiCommand, uint32_t uiArgument0, uint32_t uiArgument1=0); + + //check for messages from SPUs + void waitForResponse(unsigned int *puiArgument0, unsigned int *puiArgument1); + + //start the spus (can be called at the beginning of each frame, to make sure that the right SPU program is loaded) + virtual void startSPU(); + + //tell the task scheduler we are done with the SPU tasks + virtual void stopSPU(); + + virtual void setNumTasks(int numTasks) + { + //changing the number of tasks after initialization is not implemented (yet) + } + +private: + + ///start the spus (can be called at the beginning of each frame, to make sure that the right SPU program is loaded) + void internal_startSPU(); + + + + + int numThreads; + +}; + +#endif // NOT __SPU__ + +#endif //USE_LIBSPE2 + +#endif //SPU_LIBSPE2_SUPPORT_H + + + + diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/Box.h b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/Box.h new file mode 100644 index 000000000..c5b68743c --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/Box.h @@ -0,0 +1,167 @@ +/* + Copyright (C) 2006, 2008 Sony Computer Entertainment Inc. + All rights reserved. + +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 __BOX_H__ +#define __BOX_H__ + + +#ifndef PE_REF +#define PE_REF(a) a& +#endif + +#include + +//#include "BulletMultiThreaded/vectormath/scalar/cpp/vectormath_aos.h" +#include + + +using namespace Vectormath::Aos; + +enum FeatureType { F, E, V }; + +//---------------------------------------------------------------------------- +// Box +//---------------------------------------------------------------------------- +///The Box is an internal class used by the boxBoxDistance calculation. +class Box +{ +public: + Vector3 half; + + inline Box() + {} + inline Box(PE_REF(Vector3) half_); + inline Box(float hx, float hy, float hz); + + inline void Set(PE_REF(Vector3) half_); + inline void Set(float hx, float hy, float hz); + + inline Vector3 GetAABB(const Matrix3& rotation) const; +}; + +inline +Box::Box(PE_REF(Vector3) half_) +{ + Set(half_); +} + +inline +Box::Box(float hx, float hy, float hz) +{ + Set(hx, hy, hz); +} + +inline +void +Box::Set(PE_REF(Vector3) half_) +{ + half = half_; +} + +inline +void +Box::Set(float hx, float hy, float hz) +{ + half = Vector3(hx, hy, hz); +} + +inline +Vector3 +Box::GetAABB(const Matrix3& rotation) const +{ + return absPerElem(rotation) * half; +} + +//------------------------------------------------------------------------------------------------- +// BoxPoint +//------------------------------------------------------------------------------------------------- + +///The BoxPoint class is an internally used class to contain feature information for boxBoxDistance calculation. +class BoxPoint +{ +public: + BoxPoint() : localPoint(0.0f) {} + + Point3 localPoint; + FeatureType featureType; + int featureIdx; + + inline void setVertexFeature(int plusX, int plusY, int plusZ); + inline void setEdgeFeature(int dim0, int plus0, int dim1, int plus1); + inline void setFaceFeature(int dim, int plus); + + inline void getVertexFeature(int & plusX, int & plusY, int & plusZ) const; + inline void getEdgeFeature(int & dim0, int & plus0, int & dim1, int & plus1) const; + inline void getFaceFeature(int & dim, int & plus) const; +}; + +inline +void +BoxPoint::setVertexFeature(int plusX, int plusY, int plusZ) +{ + featureType = V; + featureIdx = plusX << 2 | plusY << 1 | plusZ; +} + +inline +void +BoxPoint::setEdgeFeature(int dim0, int plus0, int dim1, int plus1) +{ + featureType = E; + + if (dim0 > dim1) { + featureIdx = plus1 << 5 | dim1 << 3 | plus0 << 2 | dim0; + } else { + featureIdx = plus0 << 5 | dim0 << 3 | plus1 << 2 | dim1; + } +} + +inline +void +BoxPoint::setFaceFeature(int dim, int plus) +{ + featureType = F; + featureIdx = plus << 2 | dim; +} + +inline +void +BoxPoint::getVertexFeature(int & plusX, int & plusY, int & plusZ) const +{ + plusX = featureIdx >> 2; + plusY = featureIdx >> 1 & 1; + plusZ = featureIdx & 1; +} + +inline +void +BoxPoint::getEdgeFeature(int & dim0, int & plus0, int & dim1, int & plus1) const +{ + plus0 = featureIdx >> 5; + dim0 = featureIdx >> 3 & 3; + plus1 = featureIdx >> 2 & 1; + dim1 = featureIdx & 3; +} + +inline +void +BoxPoint::getFaceFeature(int & dim, int & plus) const +{ + plus = featureIdx >> 2; + dim = featureIdx & 3; +} + +#endif /* __BOX_H__ */ diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuCollisionShapes.cpp b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuCollisionShapes.cpp new file mode 100644 index 000000000..b3c8f3d2f --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuCollisionShapes.cpp @@ -0,0 +1,295 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "SpuCollisionShapes.h" + +///not supported on IBM SDK, until we fix the alignment of btVector3 +#if defined (__CELLOS_LV2__) && defined (__SPU__) +#include +static inline vec_float4 vec_dot3( vec_float4 vec0, vec_float4 vec1 ) +{ + vec_float4 result; + result = spu_mul( vec0, vec1 ); + result = spu_madd( spu_rlqwbyte( vec0, 4 ), spu_rlqwbyte( vec1, 4 ), result ); + return spu_madd( spu_rlqwbyte( vec0, 8 ), spu_rlqwbyte( vec1, 8 ), result ); +} +#endif //__SPU__ + + +void computeAabb (btVector3& aabbMin, btVector3& aabbMax, btConvexInternalShape* convexShape, ppu_address_t convexShapePtr, int shapeType, const btTransform& xform) +{ + //calculate the aabb, given the types... + switch (shapeType) + { + case CYLINDER_SHAPE_PROXYTYPE: + /* fall through */ + case BOX_SHAPE_PROXYTYPE: + { + btScalar margin=convexShape->getMarginNV(); + btVector3 halfExtents = convexShape->getImplicitShapeDimensions(); + halfExtents += btVector3(margin,margin,margin); + const btTransform& t = xform; + btMatrix3x3 abs_b = t.getBasis().absolute(); + btVector3 center = t.getOrigin(); + btVector3 extent = btVector3(abs_b[0].dot(halfExtents),abs_b[1].dot(halfExtents),abs_b[2].dot(halfExtents)); + + aabbMin = center - extent; + aabbMax = center + extent; + break; + } + case CAPSULE_SHAPE_PROXYTYPE: + { + btScalar margin=convexShape->getMarginNV(); + btVector3 halfExtents = convexShape->getImplicitShapeDimensions(); + //add the radius to y-axis to get full height + btScalar radius = halfExtents[0]; + halfExtents[1] += radius; + halfExtents += btVector3(margin,margin,margin); +#if 0 + int capsuleUpAxis = convexShape->getUpAxis(); + btScalar halfHeight = convexShape->getHalfHeight(); + btScalar radius = convexShape->getRadius(); + halfExtents[capsuleUpAxis] = radius + halfHeight; +#endif + const btTransform& t = xform; + btMatrix3x3 abs_b = t.getBasis().absolute(); + btVector3 center = t.getOrigin(); + btVector3 extent = btVector3(abs_b[0].dot(halfExtents),abs_b[1].dot(halfExtents),abs_b[2].dot(halfExtents)); + + aabbMin = center - extent; + aabbMax = center + extent; + break; + } + case SPHERE_SHAPE_PROXYTYPE: + { + btScalar radius = convexShape->getImplicitShapeDimensions().getX();// * convexShape->getLocalScaling().getX(); + btScalar margin = radius + convexShape->getMarginNV(); + const btTransform& t = xform; + const btVector3& center = t.getOrigin(); + btVector3 extent(margin,margin,margin); + aabbMin = center - extent; + aabbMax = center + extent; + break; + } + case CONVEX_HULL_SHAPE_PROXYTYPE: + { + ATTRIBUTE_ALIGNED16(char convexHullShape0[sizeof(btConvexHullShape)]); + cellDmaGet(&convexHullShape0, convexShapePtr , sizeof(btConvexHullShape), DMA_TAG(1), 0, 0); + cellDmaWaitTagStatusAll(DMA_MASK(1)); + btConvexHullShape* localPtr = (btConvexHullShape*)&convexHullShape0; + const btTransform& t = xform; + btScalar margin = convexShape->getMarginNV(); + localPtr->getNonvirtualAabb(t,aabbMin,aabbMax,margin); + //spu_printf("SPU convex aabbMin=%f,%f,%f=\n",aabbMin.getX(),aabbMin.getY(),aabbMin.getZ()); + //spu_printf("SPU convex aabbMax=%f,%f,%f=\n",aabbMax.getX(),aabbMax.getY(),aabbMax.getZ()); + break; + } + default: + { + // spu_printf("SPU: unsupported shapetype %d in AABB calculation\n"); + } + }; +} + +void dmaBvhShapeData (bvhMeshShape_LocalStoreMemory* bvhMeshShape, btBvhTriangleMeshShape* triMeshShape) +{ + register int dmaSize; + register ppu_address_t dmaPpuAddress2; + + dmaSize = sizeof(btTriangleIndexVertexArray); + dmaPpuAddress2 = reinterpret_cast(triMeshShape->getMeshInterface()); + // spu_printf("trimeshShape->getMeshInterface() == %llx\n",dmaPpuAddress2); +#ifdef __SPU__ + cellDmaGet(&bvhMeshShape->gTriangleMeshInterfaceStorage, dmaPpuAddress2 , dmaSize, DMA_TAG(1), 0, 0); + bvhMeshShape->gTriangleMeshInterfacePtr = &bvhMeshShape->gTriangleMeshInterfaceStorage; +#else + bvhMeshShape->gTriangleMeshInterfacePtr = (btTriangleIndexVertexArray*)cellDmaGetReadOnly(&bvhMeshShape->gTriangleMeshInterfaceStorage, dmaPpuAddress2 , dmaSize, DMA_TAG(1), 0, 0); +#endif + + //cellDmaWaitTagStatusAll(DMA_MASK(1)); + + ///now DMA over the BVH + + dmaSize = sizeof(btOptimizedBvh); + dmaPpuAddress2 = reinterpret_cast(triMeshShape->getOptimizedBvh()); + //spu_printf("trimeshShape->getOptimizedBvh() == %llx\n",dmaPpuAddress2); + cellDmaGet(&bvhMeshShape->gOptimizedBvh, dmaPpuAddress2 , dmaSize, DMA_TAG(2), 0, 0); + //cellDmaWaitTagStatusAll(DMA_MASK(2)); + cellDmaWaitTagStatusAll(DMA_MASK(1) | DMA_MASK(2)); +} + +void dmaBvhIndexedMesh (btIndexedMesh* IndexMesh, IndexedMeshArray& indexArray, int index, uint32_t dmaTag) +{ + cellDmaGet(IndexMesh, (ppu_address_t)&indexArray[index] , sizeof(btIndexedMesh), DMA_TAG(dmaTag), 0, 0); + +} + +void dmaBvhSubTreeHeaders (btBvhSubtreeInfo* subTreeHeaders, ppu_address_t subTreePtr, int batchSize, uint32_t dmaTag) +{ + cellDmaGet(subTreeHeaders, subTreePtr, batchSize * sizeof(btBvhSubtreeInfo), DMA_TAG(dmaTag), 0, 0); +} + +void dmaBvhSubTreeNodes (btQuantizedBvhNode* nodes, const btBvhSubtreeInfo& subtree, QuantizedNodeArray& nodeArray, int dmaTag) +{ + cellDmaGet(nodes, reinterpret_cast(&nodeArray[subtree.m_rootNodeIndex]) , subtree.m_subtreeSize* sizeof(btQuantizedBvhNode), DMA_TAG(2), 0, 0); +} + +///getShapeTypeSize could easily be optimized, but it is not likely a bottleneck +int getShapeTypeSize(int shapeType) +{ + + + switch (shapeType) + { + case CYLINDER_SHAPE_PROXYTYPE: + { + int shapeSize = sizeof(btCylinderShape); + btAssert(shapeSize < MAX_SHAPE_SIZE); + return shapeSize; + } + case BOX_SHAPE_PROXYTYPE: + { + int shapeSize = sizeof(btBoxShape); + btAssert(shapeSize < MAX_SHAPE_SIZE); + return shapeSize; + } + case SPHERE_SHAPE_PROXYTYPE: + { + int shapeSize = sizeof(btSphereShape); + btAssert(shapeSize < MAX_SHAPE_SIZE); + return shapeSize; + } + case TRIANGLE_MESH_SHAPE_PROXYTYPE: + { + int shapeSize = sizeof(btBvhTriangleMeshShape); + btAssert(shapeSize < MAX_SHAPE_SIZE); + return shapeSize; + } + case CAPSULE_SHAPE_PROXYTYPE: + { + int shapeSize = sizeof(btCapsuleShape); + btAssert(shapeSize < MAX_SHAPE_SIZE); + return shapeSize; + } + + case CONVEX_HULL_SHAPE_PROXYTYPE: + { + int shapeSize = sizeof(btConvexHullShape); + btAssert(shapeSize < MAX_SHAPE_SIZE); + return shapeSize; + } + + case COMPOUND_SHAPE_PROXYTYPE: + { + int shapeSize = sizeof(btCompoundShape); + btAssert(shapeSize < MAX_SHAPE_SIZE); + return shapeSize; + } + + default: + btAssert(0); + //unsupported shapetype, please add here + return 0; + } +} + +void dmaConvexVertexData (SpuConvexPolyhedronVertexData* convexVertexData, btConvexHullShape* convexShapeSPU) +{ + convexVertexData->gNumConvexPoints = convexShapeSPU->getNumPoints(); + if (convexVertexData->gNumConvexPoints>MAX_NUM_SPU_CONVEX_POINTS) + { + btAssert(0); + // spu_printf("SPU: Error: MAX_NUM_SPU_CONVEX_POINTS(%d) exceeded: %d\n",MAX_NUM_SPU_CONVEX_POINTS,convexVertexData->gNumConvexPoints); + return; + } + + register int dmaSize = convexVertexData->gNumConvexPoints*sizeof(btVector3); + ppu_address_t pointsPPU = (ppu_address_t) convexShapeSPU->getUnscaledPoints(); + cellDmaGet(&convexVertexData->g_convexPointBuffer[0], pointsPPU , dmaSize, DMA_TAG(2), 0, 0); +} + +void dmaCollisionShape (void* collisionShapeLocation, ppu_address_t collisionShapePtr, uint32_t dmaTag, int shapeType) +{ + register int dmaSize = getShapeTypeSize(shapeType); + cellDmaGet(collisionShapeLocation, collisionShapePtr , dmaSize, DMA_TAG(dmaTag), 0, 0); + //cellDmaWaitTagStatusAll(DMA_MASK(dmaTag)); +} + +void dmaCompoundShapeInfo (CompoundShape_LocalStoreMemory* compoundShapeLocation, btCompoundShape* spuCompoundShape, uint32_t dmaTag) +{ + register int dmaSize; + register ppu_address_t dmaPpuAddress2; + int childShapeCount = spuCompoundShape->getNumChildShapes(); + dmaSize = childShapeCount * sizeof(btCompoundShapeChild); + dmaPpuAddress2 = (ppu_address_t)spuCompoundShape->getChildList(); + cellDmaGet(&compoundShapeLocation->gSubshapes[0], dmaPpuAddress2, dmaSize, DMA_TAG(dmaTag), 0, 0); +} + +void dmaCompoundSubShapes (CompoundShape_LocalStoreMemory* compoundShapeLocation, btCompoundShape* spuCompoundShape, uint32_t dmaTag) +{ + int childShapeCount = spuCompoundShape->getNumChildShapes(); + int i; + // DMA all the subshapes + for ( i = 0; i < childShapeCount; ++i) + { + btCompoundShapeChild& childShape = compoundShapeLocation->gSubshapes[i]; + dmaCollisionShape (&compoundShapeLocation->gSubshapeShape[i],(ppu_address_t)childShape.m_childShape, dmaTag, childShape.m_childShapeType); + } +} + + +void spuWalkStacklessQuantizedTree(btNodeOverlapCallback* nodeCallback,unsigned short int* quantizedQueryAabbMin,unsigned short int* quantizedQueryAabbMax,const btQuantizedBvhNode* rootNode,int startNodeIndex,int endNodeIndex) +{ + + int curIndex = startNodeIndex; + int walkIterations = 0; +#ifdef BT_DEBUG + int subTreeSize = endNodeIndex - startNodeIndex; +#endif + + int escapeIndex; + + unsigned int aabbOverlap, isLeafNode; + + while (curIndex < endNodeIndex) + { + //catch bugs in tree data + btAssert (walkIterations < subTreeSize); + + walkIterations++; + aabbOverlap = spuTestQuantizedAabbAgainstQuantizedAabb(quantizedQueryAabbMin,quantizedQueryAabbMax,rootNode->m_quantizedAabbMin,rootNode->m_quantizedAabbMax); + isLeafNode = rootNode->isLeafNode(); + + if (isLeafNode && aabbOverlap) + { + //printf("overlap with node %d\n",rootNode->getTriangleIndex()); + nodeCallback->processNode(0,rootNode->getTriangleIndex()); + // spu_printf("SPU: overlap detected with triangleIndex:%d\n",rootNode->getTriangleIndex()); + } + + if (aabbOverlap || isLeafNode) + { + rootNode++; + curIndex++; + } else + { + escapeIndex = rootNode->getEscapeIndex(); + rootNode += escapeIndex; + curIndex += escapeIndex; + } + } + +} diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuCollisionShapes.h b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuCollisionShapes.h new file mode 100644 index 000000000..bc2159260 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuCollisionShapes.h @@ -0,0 +1,125 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 __SPU_COLLISION_SHAPES_H +#define __SPU_COLLISION_SHAPES_H + +#include "../SpuDoubleBuffer.h" + +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "BulletCollision/CollisionShapes/btConvexInternalShape.h" +#include "BulletCollision/CollisionShapes/btCylinderShape.h" + +#include "BulletCollision/CollisionShapes/btOptimizedBvh.h" +#include "BulletCollision/CollisionShapes/btTriangleIndexVertexArray.h" +#include "BulletCollision/CollisionShapes/btSphereShape.h" + +#include "BulletCollision/CollisionShapes/btCapsuleShape.h" + +#include "BulletCollision/CollisionShapes/btConvexShape.h" +#include "BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h" +#include "BulletCollision/CollisionShapes/btConvexHullShape.h" +#include "BulletCollision/CollisionShapes/btCompoundShape.h" + +#define MAX_NUM_SPU_CONVEX_POINTS 128 + +ATTRIBUTE_ALIGNED16(struct) SpuConvexPolyhedronVertexData +{ + void* gSpuConvexShapePtr; + btVector3* gConvexPoints; + int gNumConvexPoints; + int unused; + ATTRIBUTE_ALIGNED16(btVector3 g_convexPointBuffer[MAX_NUM_SPU_CONVEX_POINTS]); +}; + +#define MAX_SHAPE_SIZE 256 + +ATTRIBUTE_ALIGNED16(struct) CollisionShape_LocalStoreMemory +{ + ATTRIBUTE_ALIGNED16(char collisionShape[MAX_SHAPE_SIZE]); +}; + +ATTRIBUTE_ALIGNED16(struct) CompoundShape_LocalStoreMemory +{ + // Compound data +#define MAX_SPU_COMPOUND_SUBSHAPES 16 + ATTRIBUTE_ALIGNED16(btCompoundShapeChild gSubshapes[MAX_SPU_COMPOUND_SUBSHAPES]); + ATTRIBUTE_ALIGNED16(char gSubshapeShape[MAX_SPU_COMPOUND_SUBSHAPES][MAX_SHAPE_SIZE]); +}; + +ATTRIBUTE_ALIGNED16(struct) bvhMeshShape_LocalStoreMemory +{ + //ATTRIBUTE_ALIGNED16(btOptimizedBvh gOptimizedBvh); + ATTRIBUTE_ALIGNED16(char gOptimizedBvh[sizeof(btOptimizedBvh)+16]); + btOptimizedBvh* getOptimizedBvh() + { + return (btOptimizedBvh*) gOptimizedBvh; + } + + ATTRIBUTE_ALIGNED16(btTriangleIndexVertexArray gTriangleMeshInterfaceStorage); + btTriangleIndexVertexArray* gTriangleMeshInterfacePtr; + ///only a single mesh part for now, we can add support for multiple parts, but quantized trees don't support this at the moment + ATTRIBUTE_ALIGNED16(btIndexedMesh gIndexMesh); + #define MAX_SPU_SUBTREE_HEADERS 32 + //1024 + ATTRIBUTE_ALIGNED16(btBvhSubtreeInfo gSubtreeHeaders[MAX_SPU_SUBTREE_HEADERS]); + ATTRIBUTE_ALIGNED16(btQuantizedBvhNode gSubtreeNodes[MAX_SUBTREE_SIZE_IN_BYTES/sizeof(btQuantizedBvhNode)]); +}; + + +void computeAabb (btVector3& aabbMin, btVector3& aabbMax, btConvexInternalShape* convexShape, ppu_address_t convexShapePtr, int shapeType, const btTransform& xform); +void dmaBvhShapeData (bvhMeshShape_LocalStoreMemory* bvhMeshShape, btBvhTriangleMeshShape* triMeshShape); +void dmaBvhIndexedMesh (btIndexedMesh* IndexMesh, IndexedMeshArray& indexArray, int index, uint32_t dmaTag); +void dmaBvhSubTreeHeaders (btBvhSubtreeInfo* subTreeHeaders, ppu_address_t subTreePtr, int batchSize, uint32_t dmaTag); +void dmaBvhSubTreeNodes (btQuantizedBvhNode* nodes, const btBvhSubtreeInfo& subtree, QuantizedNodeArray& nodeArray, int dmaTag); + +int getShapeTypeSize(int shapeType); +void dmaConvexVertexData (SpuConvexPolyhedronVertexData* convexVertexData, btConvexHullShape* convexShapeSPU); +void dmaCollisionShape (void* collisionShapeLocation, ppu_address_t collisionShapePtr, uint32_t dmaTag, int shapeType); +void dmaCompoundShapeInfo (CompoundShape_LocalStoreMemory* compoundShapeLocation, btCompoundShape* spuCompoundShape, uint32_t dmaTag); +void dmaCompoundSubShapes (CompoundShape_LocalStoreMemory* compoundShapeLocation, btCompoundShape* spuCompoundShape, uint32_t dmaTag); + + +#define USE_BRANCHFREE_TEST 1 +#ifdef USE_BRANCHFREE_TEST +SIMD_FORCE_INLINE unsigned int spuTestQuantizedAabbAgainstQuantizedAabb(unsigned short int* aabbMin1,unsigned short int* aabbMax1,const unsigned short int* aabbMin2,const unsigned short int* aabbMax2) +{ +#if defined(__CELLOS_LV2__) && defined (__SPU__) + vec_ushort8 vecMin = {aabbMin1[0],aabbMin2[0],aabbMin1[2],aabbMin2[2],aabbMin1[1],aabbMin2[1],0,0}; + vec_ushort8 vecMax = {aabbMax2[0],aabbMax1[0],aabbMax2[2],aabbMax1[2],aabbMax2[1],aabbMax1[1],0,0}; + vec_ushort8 isGt = spu_cmpgt(vecMin,vecMax); + return spu_extract(spu_gather(isGt),0)==0; + +#else + return btSelect((unsigned)((aabbMin1[0] <= aabbMax2[0]) & (aabbMax1[0] >= aabbMin2[0]) + & (aabbMin1[2] <= aabbMax2[2]) & (aabbMax1[2] >= aabbMin2[2]) + & (aabbMin1[1] <= aabbMax2[1]) & (aabbMax1[1] >= aabbMin2[1])), + 1, 0); +#endif +} +#else + +SIMD_FORCE_INLINE unsigned int spuTestQuantizedAabbAgainstQuantizedAabb(const unsigned short int* aabbMin1,const unsigned short int* aabbMax1,const unsigned short int* aabbMin2,const unsigned short int* aabbMax2) +{ + unsigned int overlap = 1; + overlap = (aabbMin1[0] > aabbMax2[0] || aabbMax1[0] < aabbMin2[0]) ? 0 : overlap; + overlap = (aabbMin1[2] > aabbMax2[2] || aabbMax1[2] < aabbMin2[2]) ? 0 : overlap; + overlap = (aabbMin1[1] > aabbMax2[1] || aabbMax1[1] < aabbMin2[1]) ? 0 : overlap; + return overlap; +} +#endif + +void spuWalkStacklessQuantizedTree(btNodeOverlapCallback* nodeCallback,unsigned short int* quantizedQueryAabbMin,unsigned short int* quantizedQueryAabbMax,const btQuantizedBvhNode* rootNode,int startNodeIndex,int endNodeIndex); + +#endif diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuContactResult.cpp b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuContactResult.cpp new file mode 100644 index 000000000..5ddd1b85f --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuContactResult.cpp @@ -0,0 +1,236 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "SpuContactResult.h" + +//#define DEBUG_SPU_COLLISION_DETECTION 1 + + +SpuContactResult::SpuContactResult() +{ + m_manifoldAddress = 0; + m_spuManifold = NULL; + m_RequiresWriteBack = false; +} + + SpuContactResult::~SpuContactResult() +{ + g_manifoldDmaExport.swapBuffers(); +} + + ///User can override this material combiner by implementing gContactAddedCallback and setting body0->m_collisionFlags |= btCollisionObject::customMaterialCallback; +inline btScalar calculateCombinedFriction(btScalar friction0,btScalar friction1) +{ + btScalar friction = friction0*friction1; + + const btScalar MAX_FRICTION = btScalar(10.); + + if (friction < -MAX_FRICTION) + friction = -MAX_FRICTION; + if (friction > MAX_FRICTION) + friction = MAX_FRICTION; + return friction; + +} + +inline btScalar calculateCombinedRestitution(btScalar restitution0,btScalar restitution1) +{ + return restitution0*restitution1; +} + + + + void SpuContactResult::setContactInfo(btPersistentManifold* spuManifold, ppu_address_t manifoldAddress,const btTransform& worldTrans0,const btTransform& worldTrans1, btScalar restitution0,btScalar restitution1, btScalar friction0,btScalar friction1, bool isSwapped) + { + //spu_printf("SpuContactResult::setContactInfo ManifoldAddress: %lu\n", manifoldAddress); + m_rootWorldTransform0 = worldTrans0; + m_rootWorldTransform1 = worldTrans1; + m_manifoldAddress = manifoldAddress; + m_spuManifold = spuManifold; + + m_combinedFriction = calculateCombinedFriction(friction0,friction1); + m_combinedRestitution = calculateCombinedRestitution(restitution0,restitution1); + m_isSwapped = isSwapped; + } + + void SpuContactResult::setShapeIdentifiersA(int partId0,int index0) + { + + } + + void SpuContactResult::setShapeIdentifiersB(int partId1,int index1) + { + + } + + + + ///return true if it requires a dma transfer back +bool ManifoldResultAddContactPoint(const btVector3& normalOnBInWorld, + const btVector3& pointInWorld, + float depth, + btPersistentManifold* manifoldPtr, + btTransform& transA, + btTransform& transB, + btScalar combinedFriction, + btScalar combinedRestitution, + bool isSwapped) +{ + +// float contactTreshold = manifoldPtr->getContactBreakingThreshold(); + + //spu_printf("SPU: add contactpoint, depth:%f, contactTreshold %f, manifoldPtr %llx\n",depth,contactTreshold,manifoldPtr); + +#ifdef DEBUG_SPU_COLLISION_DETECTION + spu_printf("SPU: contactTreshold %f\n",contactTreshold); +#endif //DEBUG_SPU_COLLISION_DETECTION + if (depth > manifoldPtr->getContactBreakingThreshold()) + return false; + + //provide inverses or just calculate? + btTransform transAInv = transA.inverse();//m_body0->m_cachedInvertedWorldTransform; + btTransform transBInv= transB.inverse();//m_body1->m_cachedInvertedWorldTransform; + + btVector3 pointA; + btVector3 localA; + btVector3 localB; + btVector3 normal; + + if (isSwapped) + { + normal = normalOnBInWorld * -1; + pointA = pointInWorld + normal * depth; + localA = transAInv(pointA ); + localB = transBInv(pointInWorld); + /*localA = transBInv(pointA ); + localB = transAInv(pointInWorld);*/ + } + else + { + normal = normalOnBInWorld; + pointA = pointInWorld + normal * depth; + localA = transAInv(pointA ); + localB = transBInv(pointInWorld); + } + + btManifoldPoint newPt(localA,localB,normal,depth); + + int insertIndex = manifoldPtr->getCacheEntry(newPt); + if (insertIndex >= 0) + { +// manifoldPtr->replaceContactPoint(newPt,insertIndex); +// return true; + +#ifdef DEBUG_SPU_COLLISION_DETECTION + spu_printf("SPU: same contact detected, nothing done\n"); +#endif //DEBUG_SPU_COLLISION_DETECTION + // This is not needed, just use the old info! saves a DMA transfer as well + } else + { + + newPt.m_combinedFriction = combinedFriction; + newPt.m_combinedRestitution = combinedRestitution; + + /* + ///@todo: SPU callbacks, either immediate (local on the SPU), or deferred + //User can override friction and/or restitution + if (gContactAddedCallback && + //and if either of the two bodies requires custom material + ((m_body0->m_collisionFlags & btCollisionObject::customMaterialCallback) || + (m_body1->m_collisionFlags & btCollisionObject::customMaterialCallback))) + { + //experimental feature info, for per-triangle material etc. + (*gContactAddedCallback)(newPt,m_body0,m_partId0,m_index0,m_body1,m_partId1,m_index1); + } + */ + manifoldPtr->addManifoldPoint(newPt); + return true; + + } + return false; + +} + + +void SpuContactResult::writeDoubleBufferedManifold(btPersistentManifold* lsManifold, btPersistentManifold* mmManifold) +{ + ///only write back the contact information on SPU. Other platforms avoid copying, and use the data in-place + ///see SpuFakeDma.cpp 'cellDmaLargeGetReadOnly' +#if defined (__SPU__) || defined (USE_LIBSPE2) + memcpy(g_manifoldDmaExport.getFront(),lsManifold,sizeof(btPersistentManifold)); + + g_manifoldDmaExport.swapBuffers(); + ppu_address_t mmAddr = (ppu_address_t)mmManifold; + g_manifoldDmaExport.backBufferDmaPut(mmAddr, sizeof(btPersistentManifold), DMA_TAG(9)); + // Should there be any kind of wait here? What if somebody tries to use this tag again? What if we call this function again really soon? + //no, the swapBuffers does the wait +#endif +} + +void SpuContactResult::addContactPoint(const btVector3& normalOnBInWorld,const btVector3& pointInWorld,btScalar depth) +{ + //spu_printf("*** SpuContactResult::addContactPoint: depth = %f\n",depth); + +#ifdef DEBUG_SPU_COLLISION_DETECTION + // int sman = sizeof(rage::phManifold); +// spu_printf("sizeof_manifold = %i\n",sman); +#endif //DEBUG_SPU_COLLISION_DETECTION + + btPersistentManifold* localManifold = m_spuManifold; + + btVector3 normalB(normalOnBInWorld.getX(),normalOnBInWorld.getY(),normalOnBInWorld.getZ()); + btVector3 pointWrld(pointInWorld.getX(),pointInWorld.getY(),pointInWorld.getZ()); + + //process the contact point + const bool retVal = ManifoldResultAddContactPoint(normalB, + pointWrld, + depth, + localManifold, + m_rootWorldTransform0, + m_rootWorldTransform1, + m_combinedFriction, + m_combinedRestitution, + m_isSwapped); + m_RequiresWriteBack = m_RequiresWriteBack || retVal; +} + +void SpuContactResult::flush() +{ + + if (m_spuManifold && m_spuManifold->getNumContacts()) + { + m_spuManifold->refreshContactPoints(m_rootWorldTransform0,m_rootWorldTransform1); + m_RequiresWriteBack = true; + } + + + if (m_RequiresWriteBack) + { +#ifdef DEBUG_SPU_COLLISION_DETECTION + spu_printf("SPU: Start SpuContactResult::flush (Put) DMA\n"); + spu_printf("Num contacts:%d\n", m_spuManifold->getNumContacts()); + spu_printf("Manifold address: %llu\n", m_manifoldAddress); +#endif //DEBUG_SPU_COLLISION_DETECTION + // spu_printf("writeDoubleBufferedManifold\n"); + writeDoubleBufferedManifold(m_spuManifold, (btPersistentManifold*)m_manifoldAddress); +#ifdef DEBUG_SPU_COLLISION_DETECTION + spu_printf("SPU: Finished (Put) DMA\n"); +#endif //DEBUG_SPU_COLLISION_DETECTION + } + m_spuManifold = NULL; + m_RequiresWriteBack = false; +} + + diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuContactResult.h b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuContactResult.h new file mode 100644 index 000000000..e7be71ab1 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuContactResult.h @@ -0,0 +1,106 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 SPU_CONTACT_RESULT2_H +#define SPU_CONTACT_RESULT2_H + + +#ifndef WIN32 +#include +#endif + + + +#include "../SpuDoubleBuffer.h" + + +#include "LinearMath/btTransform.h" + + +#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" +#include "BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h" + +class btCollisionShape; + + +struct SpuCollisionPairInput +{ + ppu_address_t m_collisionShapes[2]; + btCollisionShape* m_spuCollisionShapes[2]; + + ppu_address_t m_persistentManifoldPtr; + btVector3 m_primitiveDimensions0; + btVector3 m_primitiveDimensions1; + int m_shapeType0; + int m_shapeType1; + float m_collisionMargin0; + float m_collisionMargin1; + + btTransform m_worldTransform0; + btTransform m_worldTransform1; + + bool m_isSwapped; + bool m_useEpa; +}; + + +struct SpuClosestPointInput : public btDiscreteCollisionDetectorInterface::ClosestPointInput +{ + struct SpuConvexPolyhedronVertexData* m_convexVertexData[2]; +}; + +///SpuContactResult exports the contact points using double-buffered DMA transfers, only when needed +///So when an existing contact point is duplicated, no transfer/refresh is performed. +class SpuContactResult : public btDiscreteCollisionDetectorInterface::Result +{ + btTransform m_rootWorldTransform0; + btTransform m_rootWorldTransform1; + ppu_address_t m_manifoldAddress; + + btPersistentManifold* m_spuManifold; + bool m_RequiresWriteBack; + btScalar m_combinedFriction; + btScalar m_combinedRestitution; + + bool m_isSwapped; + + DoubleBuffer g_manifoldDmaExport; + + public: + SpuContactResult(); + virtual ~SpuContactResult(); + + btPersistentManifold* GetSpuManifold() const + { + return m_spuManifold; + } + + virtual void setShapeIdentifiersA(int partId0,int index0); + virtual void setShapeIdentifiersB(int partId1,int index1); + + void setContactInfo(btPersistentManifold* spuManifold, ppu_address_t manifoldAddress,const btTransform& worldTrans0,const btTransform& worldTrans1, btScalar restitution0,btScalar restitution1, btScalar friction0,btScalar friction01, bool isSwapped); + + + void writeDoubleBufferedManifold(btPersistentManifold* lsManifold, btPersistentManifold* mmManifold); + + virtual void addContactPoint(const btVector3& normalOnBInWorld,const btVector3& pointInWorld,btScalar depth); + + void flush(); +}; + + + +#endif //SPU_CONTACT_RESULT2_H + diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuConvexPenetrationDepthSolver.h b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuConvexPenetrationDepthSolver.h new file mode 100644 index 000000000..5e4cc2881 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuConvexPenetrationDepthSolver.h @@ -0,0 +1,51 @@ + +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 SPU_CONVEX_PENETRATION_DEPTH_H +#define SPU_CONVEX_PENETRATION_DEPTH_H + + + +class btStackAlloc; +class btIDebugDraw; +#include "BulletCollision/NarrowphaseCollision/btConvexPenetrationDepthSolver.h" + +#include + + +///ConvexPenetrationDepthSolver provides an interface for penetration depth calculation. +class SpuConvexPenetrationDepthSolver : public btConvexPenetrationDepthSolver +{ +public: + + virtual ~SpuConvexPenetrationDepthSolver() {}; + virtual bool calcPenDepth( SpuVoronoiSimplexSolver& simplexSolver, + void* convexA,void* convexB,int shapeTypeA, int shapeTypeB, float marginA, float marginB, + btTransform& transA,const btTransform& transB, + btVector3& v, btVector3& pa, btVector3& pb, + class btIDebugDraw* debugDraw,btStackAlloc* stackAlloc, + struct SpuConvexPolyhedronVertexData* convexVertexDataA, + struct SpuConvexPolyhedronVertexData* convexVertexDataB + ) const = 0; + + +}; + + + +#endif //SPU_CONVEX_PENETRATION_DEPTH_H + diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuGatheringCollisionTask.cpp b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuGatheringCollisionTask.cpp new file mode 100644 index 000000000..754fd5d90 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuGatheringCollisionTask.cpp @@ -0,0 +1,1211 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "SpuGatheringCollisionTask.h" + +//#define DEBUG_SPU_COLLISION_DETECTION 1 +#include "../SpuDoubleBuffer.h" + +#include "../SpuCollisionTaskProcess.h" +#include "../SpuGatheringCollisionDispatcher.h" //for SPU_BATCHSIZE_BROADPHASE_PAIRS + +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "../SpuContactManifoldCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "SpuContactResult.h" +#include "BulletCollision/CollisionShapes/btOptimizedBvh.h" +#include "BulletCollision/CollisionShapes/btTriangleIndexVertexArray.h" +#include "BulletCollision/CollisionShapes/btSphereShape.h" +#include "BulletCollision/CollisionShapes/btConvexPointCloudShape.h" + +#include "BulletCollision/CollisionShapes/btCapsuleShape.h" + +#include "BulletCollision/CollisionShapes/btConvexShape.h" +#include "BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h" +#include "BulletCollision/CollisionShapes/btConvexHullShape.h" +#include "BulletCollision/CollisionShapes/btCompoundShape.h" + +#include "SpuMinkowskiPenetrationDepthSolver.h" +//#include "SpuEpaPenetrationDepthSolver.h" +#include "BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h" + + +#include "boxBoxDistance.h" +#include "BulletMultiThreaded/vectormath2bullet.h" +#include "SpuCollisionShapes.h" //definition of SpuConvexPolyhedronVertexData +#include "BulletCollision/CollisionDispatch/btBoxBoxDetector.h" +#include "BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h" +#include "BulletCollision/CollisionShapes/btTriangleShape.h" + +#ifdef __SPU__ +///Software caching from the IBM Cell SDK, it reduces 25% SPU time for our test cases +#ifndef USE_LIBSPE2 +#define USE_SOFTWARE_CACHE 1 +#endif +#endif //__SPU__ + +int gSkippedCol = 0; +int gProcessedCol = 0; + +//////////////////////////////////////////////// +/// software caching +#if USE_SOFTWARE_CACHE +#include +#include +#include +#include +#define SPE_CACHE_NWAY 4 +//#define SPE_CACHE_NSETS 32, 16 +#define SPE_CACHE_NSETS 8 +//#define SPE_CACHELINE_SIZE 512 +#define SPE_CACHELINE_SIZE 128 +#define SPE_CACHE_SET_TAGID(set) 15 +///make sure that spe_cache.h is below those defines! +#include "../Extras/software_cache/cache/include/spe_cache.h" + + +int g_CacheMisses=0; +int g_CacheHits=0; + +#if 0 // Added to allow cache misses and hits to be tracked, change this to 1 to restore unmodified version +#define spe_cache_read(ea) _spe_cache_lookup_xfer_wait_(ea, 0, 1) +#else +#define spe_cache_read(ea) \ +({ \ + int set, idx, line, byte; \ + _spe_cache_nway_lookup_(ea, set, idx); \ + \ + if (btUnlikely(idx < 0)) { \ + ++g_CacheMisses; \ + idx = _spe_cache_miss_(ea, set, -1); \ + spu_writech(22, SPE_CACHE_SET_TAGMASK(set)); \ + spu_mfcstat(MFC_TAG_UPDATE_ALL); \ + } \ + else \ + { \ + ++g_CacheHits; \ + } \ + line = _spe_cacheline_num_(set, idx); \ + byte = _spe_cacheline_byte_offset_(ea); \ + (void *) &spe_cache_mem[line + byte]; \ +}) + +#endif + +#endif // USE_SOFTWARE_CACHE + +bool gUseEpa = false; + +#ifdef USE_SN_TUNER +#include +#endif //USE_SN_TUNER + +#if defined (__SPU__) && !defined (USE_LIBSPE2) +#include +#elif defined (USE_LIBSPE2) +#define spu_printf(a) +#else +#define IGNORE_ALIGNMENT 1 +#include +#include +#define spu_printf printf + +#endif + +//int gNumConvexPoints0=0; + +///Make sure no destructors are called on this memory +struct CollisionTask_LocalStoreMemory +{ + ///This CollisionTask_LocalStoreMemory is mainly used for the SPU version, using explicit DMA + ///Other platforms can use other memory programming models. + + ATTRIBUTE_ALIGNED16(btBroadphasePair gBroadphasePairsBuffer[SPU_BATCHSIZE_BROADPHASE_PAIRS]); + DoubleBuffer g_workUnitTaskBuffers; + ATTRIBUTE_ALIGNED16(char gSpuContactManifoldAlgoBuffer [sizeof(SpuContactManifoldCollisionAlgorithm)+16]); + ATTRIBUTE_ALIGNED16(char gColObj0Buffer [sizeof(btCollisionObject)+16]); + ATTRIBUTE_ALIGNED16(char gColObj1Buffer [sizeof(btCollisionObject)+16]); + ///we reserve 32bit integer indices, even though they might be 16bit + ATTRIBUTE_ALIGNED16(int spuIndices[16]); + btPersistentManifold gPersistentManifoldBuffer; + CollisionShape_LocalStoreMemory gCollisionShapes[2]; + bvhMeshShape_LocalStoreMemory bvhShapeData; + SpuConvexPolyhedronVertexData convexVertexData[2]; + CompoundShape_LocalStoreMemory compoundShapeData[2]; + + ///The following pointers might either point into this local store memory, or to the original/other memory locations. + ///See SpuFakeDma for implementation of cellDmaSmallGetReadOnly. + btCollisionObject* m_lsColObj0Ptr; + btCollisionObject* m_lsColObj1Ptr; + btBroadphasePair* m_pairsPointer; + btPersistentManifold* m_lsManifoldPtr; + SpuContactManifoldCollisionAlgorithm* m_lsCollisionAlgorithmPtr; + + bool needsDmaPutContactManifoldAlgo; + + btCollisionObject* getColObj0() + { + return m_lsColObj0Ptr; + } + btCollisionObject* getColObj1() + { + return m_lsColObj1Ptr; + } + + + btBroadphasePair* getBroadphasePairPtr() + { + return m_pairsPointer; + } + + SpuContactManifoldCollisionAlgorithm* getlocalCollisionAlgorithm() + { + return m_lsCollisionAlgorithmPtr; + } + + btPersistentManifold* getContactManifoldPtr() + { + return m_lsManifoldPtr; + } +}; + + +#if defined(__CELLOS_LV2__) || defined(USE_LIBSPE2) + +ATTRIBUTE_ALIGNED16(CollisionTask_LocalStoreMemory gLocalStoreMemory); + +void* createCollisionLocalStoreMemory() +{ + return &gLocalStoreMemory; +} +#else +void* createCollisionLocalStoreMemory() +{ + return new CollisionTask_LocalStoreMemory; +} + +#endif + +void ProcessSpuConvexConvexCollision(SpuCollisionPairInput* wuInput, CollisionTask_LocalStoreMemory* lsMemPtr, SpuContactResult& spuContacts); + + +SIMD_FORCE_INLINE void small_cache_read(void* buffer, ppu_address_t ea, size_t size) +{ +#if USE_SOFTWARE_CACHE + // Check for alignment requirements. We need to make sure the entire request fits within one cache line, + // so the first and last bytes should fall on the same cache line + btAssert((ea & ~SPE_CACHELINE_MASK) == ((ea + size - 1) & ~SPE_CACHELINE_MASK)); + + void* ls = spe_cache_read(ea); + memcpy(buffer, ls, size); +#else + stallingUnalignedDmaSmallGet(buffer,ea,size); +#endif +} + +SIMD_FORCE_INLINE void small_cache_read_triple( void* ls0, ppu_address_t ea0, + void* ls1, ppu_address_t ea1, + void* ls2, ppu_address_t ea2, + size_t size) +{ + btAssert(size<16); + ATTRIBUTE_ALIGNED16(char tmpBuffer0[32]); + ATTRIBUTE_ALIGNED16(char tmpBuffer1[32]); + ATTRIBUTE_ALIGNED16(char tmpBuffer2[32]); + + uint32_t i; + + + ///make sure last 4 bits are the same, for cellDmaSmallGet + char* localStore0 = (char*)ls0; + uint32_t last4BitsOffset = ea0 & 0x0f; + char* tmpTarget0 = tmpBuffer0 + last4BitsOffset; +#ifdef __SPU__ + cellDmaSmallGet(tmpTarget0,ea0,size,DMA_TAG(1),0,0); +#else + tmpTarget0 = (char*)cellDmaSmallGetReadOnly(tmpTarget0,ea0,size,DMA_TAG(1),0,0); +#endif + + + char* localStore1 = (char*)ls1; + last4BitsOffset = ea1 & 0x0f; + char* tmpTarget1 = tmpBuffer1 + last4BitsOffset; +#ifdef __SPU__ + cellDmaSmallGet(tmpTarget1,ea1,size,DMA_TAG(1),0,0); +#else + tmpTarget1 = (char*)cellDmaSmallGetReadOnly(tmpTarget1,ea1,size,DMA_TAG(1),0,0); +#endif + + char* localStore2 = (char*)ls2; + last4BitsOffset = ea2 & 0x0f; + char* tmpTarget2 = tmpBuffer2 + last4BitsOffset; +#ifdef __SPU__ + cellDmaSmallGet(tmpTarget2,ea2,size,DMA_TAG(1),0,0); +#else + tmpTarget2 = (char*)cellDmaSmallGetReadOnly(tmpTarget2,ea2,size,DMA_TAG(1),0,0); +#endif + + + cellDmaWaitTagStatusAll( DMA_MASK(1) ); + + //this is slowish, perhaps memcpy on SPU is smarter? + for (i=0; btLikely( ibvhShapeData.gIndexMesh.m_indexType == PHY_SHORT) + { + unsigned short int* indexBasePtr = (unsigned short int*)(m_lsMemPtr->bvhShapeData.gIndexMesh.m_triangleIndexBase+triangleIndex*m_lsMemPtr->bvhShapeData.gIndexMesh.m_triangleIndexStride); + ATTRIBUTE_ALIGNED16(unsigned short int tmpIndices[3]); + + small_cache_read_triple(&tmpIndices[0],(ppu_address_t)&indexBasePtr[0], + &tmpIndices[1],(ppu_address_t)&indexBasePtr[1], + &tmpIndices[2],(ppu_address_t)&indexBasePtr[2], + sizeof(unsigned short int)); + + m_lsMemPtr->spuIndices[0] = int(tmpIndices[0]); + m_lsMemPtr->spuIndices[1] = int(tmpIndices[1]); + m_lsMemPtr->spuIndices[2] = int(tmpIndices[2]); + } else + { + unsigned int* indexBasePtr = (unsigned int*)(m_lsMemPtr->bvhShapeData.gIndexMesh.m_triangleIndexBase+triangleIndex*m_lsMemPtr->bvhShapeData.gIndexMesh.m_triangleIndexStride); + + small_cache_read_triple(&m_lsMemPtr->spuIndices[0],(ppu_address_t)&indexBasePtr[0], + &m_lsMemPtr->spuIndices[1],(ppu_address_t)&indexBasePtr[1], + &m_lsMemPtr->spuIndices[2],(ppu_address_t)&indexBasePtr[2], + sizeof(int)); + } + + // spu_printf("SPU index0=%d ,",spuIndices[0]); + // spu_printf("SPU index1=%d ,",spuIndices[1]); + // spu_printf("SPU index2=%d ,",spuIndices[2]); + // spu_printf("SPU: indexBasePtr=%llx\n",indexBasePtr); + + const btVector3& meshScaling = m_lsMemPtr->bvhShapeData.gTriangleMeshInterfacePtr->getScaling(); + for (int j=2;btLikely( j>=0 );j--) + { + int graphicsindex = m_lsMemPtr->spuIndices[j]; + + // spu_printf("SPU index=%d ,",graphicsindex); + btScalar* graphicsbasePtr = (btScalar*)(m_lsMemPtr->bvhShapeData.gIndexMesh.m_vertexBase+graphicsindex*m_lsMemPtr->bvhShapeData.gIndexMesh.m_vertexStride); + // spu_printf("SPU graphicsbasePtr=%llx\n",graphicsbasePtr); + + + ///handle un-aligned vertices... + + //another DMA for each vertex + small_cache_read_triple(&spuUnscaledVertex[0],(ppu_address_t)&graphicsbasePtr[0], + &spuUnscaledVertex[1],(ppu_address_t)&graphicsbasePtr[1], + &spuUnscaledVertex[2],(ppu_address_t)&graphicsbasePtr[2], + sizeof(btScalar)); + + m_tmpTriangleShape.getVertexPtr(j).setValue(spuUnscaledVertex[0]*meshScaling.getX(), + spuUnscaledVertex[1]*meshScaling.getY(), + spuUnscaledVertex[2]*meshScaling.getZ()); + + // spu_printf("SPU:triangle vertices:%f,%f,%f\n",spuTriangleVertices[j].x(),spuTriangleVertices[j].y(),spuTriangleVertices[j].z()); + } + + + SpuCollisionPairInput triangleConcaveInput(*m_wuInput); +// triangleConcaveInput.m_spuCollisionShapes[1] = &spuTriangleVertices[0]; + triangleConcaveInput.m_spuCollisionShapes[1] = &m_tmpTriangleShape; + triangleConcaveInput.m_shapeType1 = TRIANGLE_SHAPE_PROXYTYPE; + + m_spuContacts.setShapeIdentifiersB(subPart,triangleIndex); + + // m_spuContacts.flush(); + + ProcessSpuConvexConvexCollision(&triangleConcaveInput, m_lsMemPtr,m_spuContacts); + ///this flush should be automatic + // m_spuContacts.flush(); + } + +}; + + +//////////////////////// +/// Convex versus Concave triangle mesh collision detection (handles concave triangle mesh versus sphere, box, cylinder, triangle, cone, convex polyhedron etc) +/////////////////// +void ProcessConvexConcaveSpuCollision(SpuCollisionPairInput* wuInput, CollisionTask_LocalStoreMemory* lsMemPtr, SpuContactResult& spuContacts) +{ + //order: first collision shape is convex, second concave. m_isSwapped is true, if the original order was opposite + + btBvhTriangleMeshShape* trimeshShape = (btBvhTriangleMeshShape*)wuInput->m_spuCollisionShapes[1]; + //need the mesh interface, for access to triangle vertices + dmaBvhShapeData (&lsMemPtr->bvhShapeData, trimeshShape); + + btVector3 aabbMin(-1,-400,-1); + btVector3 aabbMax(1,400,1); + + + //recalc aabbs + btTransform convexInTriangleSpace; + convexInTriangleSpace = wuInput->m_worldTransform1.inverse() * wuInput->m_worldTransform0; + btConvexInternalShape* convexShape = (btConvexInternalShape*)wuInput->m_spuCollisionShapes[0]; + + computeAabb (aabbMin, aabbMax, convexShape, wuInput->m_collisionShapes[0], wuInput->m_shapeType0, convexInTriangleSpace); + + + //CollisionShape* triangleShape = static_cast(triBody->m_collisionShape); + //convexShape->getAabb(convexInTriangleSpace,m_aabbMin,m_aabbMax); + + // btScalar extraMargin = collisionMarginTriangle; + // btVector3 extra(extraMargin,extraMargin,extraMargin); + // aabbMax += extra; + // aabbMin -= extra; + + ///quantize query AABB + unsigned short int quantizedQueryAabbMin[3]; + unsigned short int quantizedQueryAabbMax[3]; + lsMemPtr->bvhShapeData.getOptimizedBvh()->quantizeWithClamp(quantizedQueryAabbMin,aabbMin,0); + lsMemPtr->bvhShapeData.getOptimizedBvh()->quantizeWithClamp(quantizedQueryAabbMax,aabbMax,1); + + QuantizedNodeArray& nodeArray = lsMemPtr->bvhShapeData.getOptimizedBvh()->getQuantizedNodeArray(); + //spu_printf("SPU: numNodes = %d\n",nodeArray.size()); + + BvhSubtreeInfoArray& subTrees = lsMemPtr->bvhShapeData.getOptimizedBvh()->getSubtreeInfoArray(); + + + spuNodeCallback nodeCallback(wuInput,lsMemPtr,spuContacts); + IndexedMeshArray& indexArray = lsMemPtr->bvhShapeData.gTriangleMeshInterfacePtr->getIndexedMeshArray(); + //spu_printf("SPU:indexArray.size() = %d\n",indexArray.size()); + + // spu_printf("SPU: numSubTrees = %d\n",subTrees.size()); + //not likely to happen + if (subTrees.size() && indexArray.size() == 1) + { + ///DMA in the index info + dmaBvhIndexedMesh (&lsMemPtr->bvhShapeData.gIndexMesh, indexArray, 0 /* index into indexArray */, 1 /* dmaTag */); + cellDmaWaitTagStatusAll(DMA_MASK(1)); + + //display the headers + int numBatch = subTrees.size(); + for (int i=0;ibvhShapeData.gSubtreeHeaders[0], (ppu_address_t)(&subTrees[i]), nextBatch, 1); + cellDmaWaitTagStatusAll(DMA_MASK(1)); + + + // spu_printf("nextBatch = %d\n",nextBatch); + + for (int j=0;jbvhShapeData.gSubtreeHeaders[j]; + + unsigned int overlap = spuTestQuantizedAabbAgainstQuantizedAabb(quantizedQueryAabbMin,quantizedQueryAabbMax,subtree.m_quantizedAabbMin,subtree.m_quantizedAabbMax); + if (overlap) + { + btAssert(subtree.m_subtreeSize); + + //dma the actual nodes of this subtree + dmaBvhSubTreeNodes (&lsMemPtr->bvhShapeData.gSubtreeNodes[0], subtree, nodeArray, 2); + cellDmaWaitTagStatusAll(DMA_MASK(2)); + + /* Walk this subtree */ + spuWalkStacklessQuantizedTree(&nodeCallback,quantizedQueryAabbMin,quantizedQueryAabbMax, + &lsMemPtr->bvhShapeData.gSubtreeNodes[0], + 0, + subtree.m_subtreeSize); + } + // spu_printf("subtreeSize = %d\n",gSubtreeHeaders[j].m_subtreeSize); + } + + // unsigned short int m_quantizedAabbMin[3]; + // unsigned short int m_quantizedAabbMax[3]; + // int m_rootNodeIndex; + // int m_subtreeSize; + i+=nextBatch; + } + + //pre-fetch first tree, then loop and double buffer + } + +} + + +int stats[11]={0,0,0,0,0,0,0,0,0,0,0}; +int degenerateStats[11]={0,0,0,0,0,0,0,0,0,0,0}; + + +//////////////////////// +/// Convex versus Convex collision detection (handles collision between sphere, box, cylinder, triangle, cone, convex polyhedron etc) +/////////////////// +void ProcessSpuConvexConvexCollision(SpuCollisionPairInput* wuInput, CollisionTask_LocalStoreMemory* lsMemPtr, SpuContactResult& spuContacts) +{ + register int dmaSize; + register ppu_address_t dmaPpuAddress2; + +#ifdef DEBUG_SPU_COLLISION_DETECTION + //spu_printf("SPU: ProcessSpuConvexConvexCollision\n"); +#endif //DEBUG_SPU_COLLISION_DETECTION + //CollisionShape* shape0 = (CollisionShape*)wuInput->m_collisionShapes[0]; + //CollisionShape* shape1 = (CollisionShape*)wuInput->m_collisionShapes[1]; + btPersistentManifold* manifold = (btPersistentManifold*)wuInput->m_persistentManifoldPtr; + + bool genericGjk = true; + + if (genericGjk) + { + //try generic GJK + + + + //SpuConvexPenetrationDepthSolver* penetrationSolver=0; + btVoronoiSimplexSolver simplexSolver; + btGjkEpaPenetrationDepthSolver epaPenetrationSolver2; + + btConvexPenetrationDepthSolver* penetrationSolver = &epaPenetrationSolver2; + + //SpuMinkowskiPenetrationDepthSolver minkowskiPenetrationSolver; +#ifdef ENABLE_EPA + if (gUseEpa) + { + penetrationSolver = &epaPenetrationSolver2; + } else +#endif + { + //penetrationSolver = &minkowskiPenetrationSolver; + } + + + ///DMA in the vertices for convex shapes + ATTRIBUTE_ALIGNED16(char convexHullShape0[sizeof(btConvexHullShape)]); + ATTRIBUTE_ALIGNED16(char convexHullShape1[sizeof(btConvexHullShape)]); + + if ( btLikely( wuInput->m_shapeType0== CONVEX_HULL_SHAPE_PROXYTYPE ) ) + { + // spu_printf("SPU: DMA btConvexHullShape\n"); + + dmaSize = sizeof(btConvexHullShape); + dmaPpuAddress2 = wuInput->m_collisionShapes[0]; + + cellDmaGet(&convexHullShape0, dmaPpuAddress2 , dmaSize, DMA_TAG(1), 0, 0); + //cellDmaWaitTagStatusAll(DMA_MASK(1)); + } + + if ( btLikely( wuInput->m_shapeType1 == CONVEX_HULL_SHAPE_PROXYTYPE ) ) + { + // spu_printf("SPU: DMA btConvexHullShape\n"); + dmaSize = sizeof(btConvexHullShape); + dmaPpuAddress2 = wuInput->m_collisionShapes[1]; + cellDmaGet(&convexHullShape1, dmaPpuAddress2 , dmaSize, DMA_TAG(1), 0, 0); + //cellDmaWaitTagStatusAll(DMA_MASK(1)); + } + + if ( btLikely( wuInput->m_shapeType0 == CONVEX_HULL_SHAPE_PROXYTYPE ) ) + { + cellDmaWaitTagStatusAll(DMA_MASK(1)); + dmaConvexVertexData (&lsMemPtr->convexVertexData[0], (btConvexHullShape*)&convexHullShape0); + lsMemPtr->convexVertexData[0].gSpuConvexShapePtr = wuInput->m_spuCollisionShapes[0]; + } + + + if ( btLikely( wuInput->m_shapeType1 == CONVEX_HULL_SHAPE_PROXYTYPE ) ) + { + cellDmaWaitTagStatusAll(DMA_MASK(1)); + dmaConvexVertexData (&lsMemPtr->convexVertexData[1], (btConvexHullShape*)&convexHullShape1); + lsMemPtr->convexVertexData[1].gSpuConvexShapePtr = wuInput->m_spuCollisionShapes[1]; + } + + + btConvexPointCloudShape cpc0,cpc1; + + if ( btLikely( wuInput->m_shapeType0 == CONVEX_HULL_SHAPE_PROXYTYPE ) ) + { + cellDmaWaitTagStatusAll(DMA_MASK(2)); + lsMemPtr->convexVertexData[0].gConvexPoints = &lsMemPtr->convexVertexData[0].g_convexPointBuffer[0]; + btConvexHullShape* ch = (btConvexHullShape*)wuInput->m_spuCollisionShapes[0]; + const btVector3& localScaling = ch->getLocalScalingNV(); + cpc0.setPoints(lsMemPtr->convexVertexData[0].gConvexPoints,lsMemPtr->convexVertexData[0].gNumConvexPoints,false,localScaling); + wuInput->m_spuCollisionShapes[0] = &cpc0; + } + + if ( btLikely( wuInput->m_shapeType1 == CONVEX_HULL_SHAPE_PROXYTYPE ) ) + { + cellDmaWaitTagStatusAll(DMA_MASK(2)); + lsMemPtr->convexVertexData[1].gConvexPoints = &lsMemPtr->convexVertexData[1].g_convexPointBuffer[0]; + btConvexHullShape* ch = (btConvexHullShape*)wuInput->m_spuCollisionShapes[1]; + const btVector3& localScaling = ch->getLocalScalingNV(); + cpc1.setPoints(lsMemPtr->convexVertexData[1].gConvexPoints,lsMemPtr->convexVertexData[1].gNumConvexPoints,false,localScaling); + wuInput->m_spuCollisionShapes[1] = &cpc1; + + } + + + const btConvexShape* shape0Ptr = (const btConvexShape*)wuInput->m_spuCollisionShapes[0]; + const btConvexShape* shape1Ptr = (const btConvexShape*)wuInput->m_spuCollisionShapes[1]; + int shapeType0 = wuInput->m_shapeType0; + int shapeType1 = wuInput->m_shapeType1; + float marginA = wuInput->m_collisionMargin0; + float marginB = wuInput->m_collisionMargin1; + + SpuClosestPointInput cpInput; + cpInput.m_convexVertexData[0] = &lsMemPtr->convexVertexData[0]; + cpInput.m_convexVertexData[1] = &lsMemPtr->convexVertexData[1]; + cpInput.m_transformA = wuInput->m_worldTransform0; + cpInput.m_transformB = wuInput->m_worldTransform1; + float sumMargin = (marginA+marginB+lsMemPtr->getContactManifoldPtr()->getContactBreakingThreshold()); + cpInput.m_maximumDistanceSquared = sumMargin * sumMargin; + + ppu_address_t manifoldAddress = (ppu_address_t)manifold; + + btPersistentManifold* spuManifold=lsMemPtr->getContactManifoldPtr(); + //spuContacts.setContactInfo(spuManifold,manifoldAddress,wuInput->m_worldTransform0,wuInput->m_worldTransform1,wuInput->m_isSwapped); + spuContacts.setContactInfo(spuManifold,manifoldAddress,lsMemPtr->getColObj0()->getWorldTransform(), + lsMemPtr->getColObj1()->getWorldTransform(), + lsMemPtr->getColObj0()->getRestitution(),lsMemPtr->getColObj1()->getRestitution(), + lsMemPtr->getColObj0()->getFriction(),lsMemPtr->getColObj1()->getFriction(), + wuInput->m_isSwapped); + + { + btGjkPairDetector gjk(shape0Ptr,shape1Ptr,shapeType0,shapeType1,marginA,marginB,&simplexSolver,penetrationSolver);//&vsSolver,penetrationSolver); + gjk.getClosestPoints(cpInput,spuContacts,0);//,debugDraw); + + stats[gjk.m_lastUsedMethod]++; + degenerateStats[gjk.m_degenerateSimplex]++; + +#ifdef USE_SEPDISTANCE_UTIL + btScalar sepDist = gjk.getCachedSeparatingDistance()+spuManifold->getContactBreakingThreshold(); + lsMemPtr->getlocalCollisionAlgorithm()->m_sepDistance.initSeparatingDistance(gjk.getCachedSeparatingAxis(),sepDist,wuInput->m_worldTransform0,wuInput->m_worldTransform1); + lsMemPtr->needsDmaPutContactManifoldAlgo = true; +#endif //USE_SEPDISTANCE_UTIL + + } + + } + + +} + + +template void DoSwap(T& a, T& b) +{ + char tmp[sizeof(T)]; + memcpy(tmp, &a, sizeof(T)); + memcpy(&a, &b, sizeof(T)); + memcpy(&b, tmp, sizeof(T)); +} + +SIMD_FORCE_INLINE void dmaAndSetupCollisionObjects(SpuCollisionPairInput& collisionPairInput, CollisionTask_LocalStoreMemory& lsMem) +{ + register int dmaSize; + register ppu_address_t dmaPpuAddress2; + + dmaSize = sizeof(btCollisionObject);//btTransform); + dmaPpuAddress2 = /*collisionPairInput.m_isSwapped ? (ppu_address_t)lsMem.gProxyPtr1->m_clientObject :*/ (ppu_address_t)lsMem.getlocalCollisionAlgorithm()->getCollisionObject0(); + lsMem.m_lsColObj0Ptr = (btCollisionObject*)cellDmaGetReadOnly(&lsMem.gColObj0Buffer, dmaPpuAddress2 , dmaSize, DMA_TAG(1), 0, 0); + + dmaSize = sizeof(btCollisionObject);//btTransform); + dmaPpuAddress2 = /*collisionPairInput.m_isSwapped ? (ppu_address_t)lsMem.gProxyPtr0->m_clientObject :*/ (ppu_address_t)lsMem.getlocalCollisionAlgorithm()->getCollisionObject1(); + lsMem.m_lsColObj1Ptr = (btCollisionObject*)cellDmaGetReadOnly(&lsMem.gColObj1Buffer, dmaPpuAddress2 , dmaSize, DMA_TAG(2), 0, 0); + + cellDmaWaitTagStatusAll(DMA_MASK(1) | DMA_MASK(2)); + + btCollisionObject* ob0 = lsMem.getColObj0(); + btCollisionObject* ob1 = lsMem.getColObj1(); + + collisionPairInput.m_worldTransform0 = ob0->getWorldTransform(); + collisionPairInput.m_worldTransform1 = ob1->getWorldTransform(); +} + + + +void handleCollisionPair(SpuCollisionPairInput& collisionPairInput, CollisionTask_LocalStoreMemory& lsMem, + SpuContactResult &spuContacts, + ppu_address_t collisionShape0Ptr, void* collisionShape0Loc, + ppu_address_t collisionShape1Ptr, void* collisionShape1Loc, bool dmaShapes = true) +{ + + if (btBroadphaseProxy::isConvex(collisionPairInput.m_shapeType0) + && btBroadphaseProxy::isConvex(collisionPairInput.m_shapeType1)) + { + if (dmaShapes) + { + dmaCollisionShape (collisionShape0Loc, collisionShape0Ptr, 1, collisionPairInput.m_shapeType0); + dmaCollisionShape (collisionShape1Loc, collisionShape1Ptr, 2, collisionPairInput.m_shapeType1); + cellDmaWaitTagStatusAll(DMA_MASK(1) | DMA_MASK(2)); + } + + btConvexInternalShape* spuConvexShape0 = (btConvexInternalShape*)collisionShape0Loc; + btConvexInternalShape* spuConvexShape1 = (btConvexInternalShape*)collisionShape1Loc; + + btVector3 dim0 = spuConvexShape0->getImplicitShapeDimensions(); + btVector3 dim1 = spuConvexShape1->getImplicitShapeDimensions(); + + collisionPairInput.m_primitiveDimensions0 = dim0; + collisionPairInput.m_primitiveDimensions1 = dim1; + collisionPairInput.m_collisionShapes[0] = collisionShape0Ptr; + collisionPairInput.m_collisionShapes[1] = collisionShape1Ptr; + collisionPairInput.m_spuCollisionShapes[0] = spuConvexShape0; + collisionPairInput.m_spuCollisionShapes[1] = spuConvexShape1; + ProcessSpuConvexConvexCollision(&collisionPairInput,&lsMem,spuContacts); + } + else if (btBroadphaseProxy::isCompound(collisionPairInput.m_shapeType0) && + btBroadphaseProxy::isCompound(collisionPairInput.m_shapeType1)) + { + //snPause(); + + dmaCollisionShape (collisionShape0Loc, collisionShape0Ptr, 1, collisionPairInput.m_shapeType0); + dmaCollisionShape (collisionShape1Loc, collisionShape1Ptr, 2, collisionPairInput.m_shapeType1); + cellDmaWaitTagStatusAll(DMA_MASK(1) | DMA_MASK(2)); + + // Both are compounds, do N^2 CD for now + ///@todo: add some AABB-based pruning (probably not -> slower) + + btCompoundShape* spuCompoundShape0 = (btCompoundShape*)collisionShape0Loc; + btCompoundShape* spuCompoundShape1 = (btCompoundShape*)collisionShape1Loc; + + dmaCompoundShapeInfo (&lsMem.compoundShapeData[0], spuCompoundShape0, 1); + dmaCompoundShapeInfo (&lsMem.compoundShapeData[1], spuCompoundShape1, 2); + cellDmaWaitTagStatusAll(DMA_MASK(1) | DMA_MASK(2)); + + + dmaCompoundSubShapes (&lsMem.compoundShapeData[0], spuCompoundShape0, 1); + cellDmaWaitTagStatusAll(DMA_MASK(1)); + dmaCompoundSubShapes (&lsMem.compoundShapeData[1], spuCompoundShape1, 1); + cellDmaWaitTagStatusAll(DMA_MASK(1)); + + int childShapeCount0 = spuCompoundShape0->getNumChildShapes(); + int childShapeCount1 = spuCompoundShape1->getNumChildShapes(); + + // Start the N^2 + for (int i = 0; i < childShapeCount0; ++i) + { + btCompoundShapeChild& childShape0 = lsMem.compoundShapeData[0].gSubshapes[i]; + + for (int j = 0; j < childShapeCount1; ++j) + { + btCompoundShapeChild& childShape1 = lsMem.compoundShapeData[1].gSubshapes[j]; + + /* Create a new collision pair input struct using the two child shapes */ + SpuCollisionPairInput cinput (collisionPairInput); + + cinput.m_worldTransform0 = collisionPairInput.m_worldTransform0 * childShape0.m_transform; + cinput.m_shapeType0 = childShape0.m_childShapeType; + cinput.m_collisionMargin0 = childShape0.m_childMargin; + + cinput.m_worldTransform1 = collisionPairInput.m_worldTransform1 * childShape1.m_transform; + cinput.m_shapeType1 = childShape1.m_childShapeType; + cinput.m_collisionMargin1 = childShape1.m_childMargin; + /* Recursively call handleCollisionPair () with new collision pair input */ + handleCollisionPair(cinput, lsMem, spuContacts, + (ppu_address_t)childShape0.m_childShape, lsMem.compoundShapeData[0].gSubshapeShape[i], + (ppu_address_t)childShape1.m_childShape, lsMem.compoundShapeData[1].gSubshapeShape[j], false); // bug fix: changed index to j. + } + } + } + else if (btBroadphaseProxy::isCompound(collisionPairInput.m_shapeType0) ) + { + //snPause(); + + dmaCollisionShape (collisionShape0Loc, collisionShape0Ptr, 1, collisionPairInput.m_shapeType0); + dmaCollisionShape (collisionShape1Loc, collisionShape1Ptr, 2, collisionPairInput.m_shapeType1); + cellDmaWaitTagStatusAll(DMA_MASK(1) | DMA_MASK(2)); + + // object 0 compound, object 1 non-compound + btCompoundShape* spuCompoundShape = (btCompoundShape*)collisionShape0Loc; + dmaCompoundShapeInfo (&lsMem.compoundShapeData[0], spuCompoundShape, 1); + cellDmaWaitTagStatusAll(DMA_MASK(1)); + + int childShapeCount = spuCompoundShape->getNumChildShapes(); + + for (int i = 0; i < childShapeCount; ++i) + { + btCompoundShapeChild& childShape = lsMem.compoundShapeData[0].gSubshapes[i]; + + // Dma the child shape + dmaCollisionShape (&lsMem.compoundShapeData[0].gSubshapeShape[i], (ppu_address_t)childShape.m_childShape, 1, childShape.m_childShapeType); + cellDmaWaitTagStatusAll(DMA_MASK(1)); + + SpuCollisionPairInput cinput (collisionPairInput); + cinput.m_worldTransform0 = collisionPairInput.m_worldTransform0 * childShape.m_transform; + cinput.m_shapeType0 = childShape.m_childShapeType; + cinput.m_collisionMargin0 = childShape.m_childMargin; + + handleCollisionPair(cinput, lsMem, spuContacts, + (ppu_address_t)childShape.m_childShape, lsMem.compoundShapeData[0].gSubshapeShape[i], + collisionShape1Ptr, collisionShape1Loc, false); + } + } + else if (btBroadphaseProxy::isCompound(collisionPairInput.m_shapeType1) ) + { + //snPause(); + + dmaCollisionShape (collisionShape0Loc, collisionShape0Ptr, 1, collisionPairInput.m_shapeType0); + dmaCollisionShape (collisionShape1Loc, collisionShape1Ptr, 2, collisionPairInput.m_shapeType1); + cellDmaWaitTagStatusAll(DMA_MASK(1) | DMA_MASK(2)); + // object 0 non-compound, object 1 compound + btCompoundShape* spuCompoundShape = (btCompoundShape*)collisionShape1Loc; + dmaCompoundShapeInfo (&lsMem.compoundShapeData[0], spuCompoundShape, 1); + cellDmaWaitTagStatusAll(DMA_MASK(1)); + + int childShapeCount = spuCompoundShape->getNumChildShapes(); + + for (int i = 0; i < childShapeCount; ++i) + { + btCompoundShapeChild& childShape = lsMem.compoundShapeData[0].gSubshapes[i]; + // Dma the child shape + dmaCollisionShape (&lsMem.compoundShapeData[0].gSubshapeShape[i], (ppu_address_t)childShape.m_childShape, 1, childShape.m_childShapeType); + cellDmaWaitTagStatusAll(DMA_MASK(1)); + + SpuCollisionPairInput cinput (collisionPairInput); + cinput.m_worldTransform1 = collisionPairInput.m_worldTransform1 * childShape.m_transform; + cinput.m_shapeType1 = childShape.m_childShapeType; + cinput.m_collisionMargin1 = childShape.m_childMargin; + handleCollisionPair(cinput, lsMem, spuContacts, + collisionShape0Ptr, collisionShape0Loc, + (ppu_address_t)childShape.m_childShape, lsMem.compoundShapeData[0].gSubshapeShape[i], false); + } + + } + else + { + //a non-convex shape is involved + bool handleConvexConcave = false; + + //snPause(); + + if (btBroadphaseProxy::isConcave(collisionPairInput.m_shapeType0) && + btBroadphaseProxy::isConvex(collisionPairInput.m_shapeType1)) + { + // Swap stuff + DoSwap(collisionShape0Ptr, collisionShape1Ptr); + DoSwap(collisionShape0Loc, collisionShape1Loc); + DoSwap(collisionPairInput.m_shapeType0, collisionPairInput.m_shapeType1); + DoSwap(collisionPairInput.m_worldTransform0, collisionPairInput.m_worldTransform1); + DoSwap(collisionPairInput.m_collisionMargin0, collisionPairInput.m_collisionMargin1); + + collisionPairInput.m_isSwapped = true; + } + + if (btBroadphaseProxy::isConvex(collisionPairInput.m_shapeType0)&& + btBroadphaseProxy::isConcave(collisionPairInput.m_shapeType1)) + { + handleConvexConcave = true; + } + if (handleConvexConcave) + { + if (dmaShapes) + { + dmaCollisionShape (collisionShape0Loc, collisionShape0Ptr, 1, collisionPairInput.m_shapeType0); + dmaCollisionShape (collisionShape1Loc, collisionShape1Ptr, 2, collisionPairInput.m_shapeType1); + cellDmaWaitTagStatusAll(DMA_MASK(1) | DMA_MASK(2)); + } + + btConvexInternalShape* spuConvexShape0 = (btConvexInternalShape*)collisionShape0Loc; + btBvhTriangleMeshShape* trimeshShape = (btBvhTriangleMeshShape*)collisionShape1Loc; + + btVector3 dim0 = spuConvexShape0->getImplicitShapeDimensions(); + collisionPairInput.m_primitiveDimensions0 = dim0; + collisionPairInput.m_collisionShapes[0] = collisionShape0Ptr; + collisionPairInput.m_collisionShapes[1] = collisionShape1Ptr; + collisionPairInput.m_spuCollisionShapes[0] = spuConvexShape0; + collisionPairInput.m_spuCollisionShapes[1] = trimeshShape; + + ProcessConvexConcaveSpuCollision(&collisionPairInput,&lsMem,spuContacts); + } + + } + + spuContacts.flush(); + +} + + +void processCollisionTask(void* userPtr, void* lsMemPtr) +{ + + SpuGatherAndProcessPairsTaskDesc* taskDescPtr = (SpuGatherAndProcessPairsTaskDesc*)userPtr; + SpuGatherAndProcessPairsTaskDesc& taskDesc = *taskDescPtr; + CollisionTask_LocalStoreMemory* colMemPtr = (CollisionTask_LocalStoreMemory*)lsMemPtr; + CollisionTask_LocalStoreMemory& lsMem = *(colMemPtr); + + gUseEpa = taskDesc.m_useEpa; + + // spu_printf("taskDescPtr=%llx\n",taskDescPtr); + + SpuContactResult spuContacts; + + //////////////////// + + ppu_address_t dmaInPtr = taskDesc.m_inPairPtr; + unsigned int numPages = taskDesc.numPages; + unsigned int numOnLastPage = taskDesc.numOnLastPage; + + // prefetch first set of inputs and wait + lsMem.g_workUnitTaskBuffers.init(); + + unsigned int nextNumOnPage = (numPages > 1)? MIDPHASE_NUM_WORKUNITS_PER_PAGE : numOnLastPage; + lsMem.g_workUnitTaskBuffers.backBufferDmaGet(dmaInPtr, nextNumOnPage*sizeof(SpuGatherAndProcessWorkUnitInput), DMA_TAG(3)); + dmaInPtr += MIDPHASE_WORKUNIT_PAGE_SIZE; + + + register unsigned char *inputPtr; + register unsigned int numOnPage; + register unsigned int j; + SpuGatherAndProcessWorkUnitInput* wuInputs; + register int dmaSize; + register ppu_address_t dmaPpuAddress; + register ppu_address_t dmaPpuAddress2; + + int numPairs; + register int p; + SpuCollisionPairInput collisionPairInput; + + for (unsigned int i = 0; btLikely(i < numPages); i++) + { + + // wait for back buffer dma and swap buffers + inputPtr = lsMem.g_workUnitTaskBuffers.swapBuffers(); + + // number on current page is number prefetched last iteration + numOnPage = nextNumOnPage; + + + // prefetch next set of inputs +#if MIDPHASE_NUM_WORKUNIT_PAGES > 2 + if ( btLikely( i < numPages-1 ) ) +#else + if ( btUnlikely( i < numPages-1 ) ) +#endif + { + nextNumOnPage = (i == numPages-2)? numOnLastPage : MIDPHASE_NUM_WORKUNITS_PER_PAGE; + lsMem.g_workUnitTaskBuffers.backBufferDmaGet(dmaInPtr, nextNumOnPage*sizeof(SpuGatherAndProcessWorkUnitInput), DMA_TAG(3)); + dmaInPtr += MIDPHASE_WORKUNIT_PAGE_SIZE; + } + + wuInputs = reinterpret_cast(inputPtr); + + + for (j = 0; btLikely( j < numOnPage ); j++) + { +#ifdef DEBUG_SPU_COLLISION_DETECTION + // printMidphaseInput(&wuInputs[j]); +#endif //DEBUG_SPU_COLLISION_DETECTION + + + numPairs = wuInputs[j].m_endIndex - wuInputs[j].m_startIndex; + + if ( btLikely( numPairs ) ) + { + dmaSize = numPairs*sizeof(btBroadphasePair); + dmaPpuAddress = wuInputs[j].m_pairArrayPtr+wuInputs[j].m_startIndex * sizeof(btBroadphasePair); + lsMem.m_pairsPointer = (btBroadphasePair*)cellDmaGetReadOnly(&lsMem.gBroadphasePairsBuffer, dmaPpuAddress , dmaSize, DMA_TAG(1), 0, 0); + cellDmaWaitTagStatusAll(DMA_MASK(1)); + + + for (p=0;pm_userInfo = %d\n",pair.m_userInfo); + spu_printf("pair->m_algorithm = %d\n",pair.m_algorithm); + spu_printf("pair->m_pProxy0 = %d\n",pair.m_pProxy0); + spu_printf("pair->m_pProxy1 = %d\n",pair.m_pProxy1); +#endif //DEBUG_SPU_COLLISION_DETECTION + + if (pair.m_internalTmpValue == 2 && pair.m_algorithm && pair.m_pProxy0 && pair.m_pProxy1) + { + dmaSize = sizeof(SpuContactManifoldCollisionAlgorithm); + dmaPpuAddress2 = (ppu_address_t)pair.m_algorithm; + lsMem.m_lsCollisionAlgorithmPtr = (SpuContactManifoldCollisionAlgorithm*)cellDmaGetReadOnly(&lsMem.gSpuContactManifoldAlgoBuffer, dmaPpuAddress2 , dmaSize, DMA_TAG(1), 0, 0); + + cellDmaWaitTagStatusAll(DMA_MASK(1)); + + lsMem.needsDmaPutContactManifoldAlgo = false; + + collisionPairInput.m_persistentManifoldPtr = (ppu_address_t) lsMem.getlocalCollisionAlgorithm()->getContactManifoldPtr(); + collisionPairInput.m_isSwapped = false; + + if (1) + { + + ///can wait on the combined DMA_MASK, or dma on the same tag + + +#ifdef DEBUG_SPU_COLLISION_DETECTION + // spu_printf("SPU collisionPairInput->m_shapeType0 = %d\n",collisionPairInput->m_shapeType0); + // spu_printf("SPU collisionPairInput->m_shapeType1 = %d\n",collisionPairInput->m_shapeType1); +#endif //DEBUG_SPU_COLLISION_DETECTION + + + dmaSize = sizeof(btPersistentManifold); + + dmaPpuAddress2 = collisionPairInput.m_persistentManifoldPtr; + lsMem.m_lsManifoldPtr = (btPersistentManifold*)cellDmaGetReadOnly(&lsMem.gPersistentManifoldBuffer, dmaPpuAddress2 , dmaSize, DMA_TAG(1), 0, 0); + + collisionPairInput.m_shapeType0 = lsMem.getlocalCollisionAlgorithm()->getShapeType0(); + collisionPairInput.m_shapeType1 = lsMem.getlocalCollisionAlgorithm()->getShapeType1(); + collisionPairInput.m_collisionMargin0 = lsMem.getlocalCollisionAlgorithm()->getCollisionMargin0(); + collisionPairInput.m_collisionMargin1 = lsMem.getlocalCollisionAlgorithm()->getCollisionMargin1(); + + + + //??cellDmaWaitTagStatusAll(DMA_MASK(1)); + + + if (1) + { + //snPause(); + + // Get the collision objects + dmaAndSetupCollisionObjects(collisionPairInput, lsMem); + + if (lsMem.getColObj0()->isActive() || lsMem.getColObj1()->isActive()) + { + + lsMem.needsDmaPutContactManifoldAlgo = true; +#ifdef USE_SEPDISTANCE_UTIL + lsMem.getlocalCollisionAlgorithm()->m_sepDistance.updateSeparatingDistance(collisionPairInput.m_worldTransform0,collisionPairInput.m_worldTransform1); +#endif //USE_SEPDISTANCE_UTIL + +#define USE_DEDICATED_BOX_BOX 1 +#ifdef USE_DEDICATED_BOX_BOX + bool boxbox = ((lsMem.getlocalCollisionAlgorithm()->getShapeType0()==BOX_SHAPE_PROXYTYPE)&& + (lsMem.getlocalCollisionAlgorithm()->getShapeType1()==BOX_SHAPE_PROXYTYPE)); + if (boxbox) + { + //spu_printf("boxbox dist = %f\n",distance); + btPersistentManifold* spuManifold=lsMem.getContactManifoldPtr(); + btPersistentManifold* manifold = (btPersistentManifold*)collisionPairInput.m_persistentManifoldPtr; + ppu_address_t manifoldAddress = (ppu_address_t)manifold; + + spuContacts.setContactInfo(spuManifold,manifoldAddress,lsMem.getColObj0()->getWorldTransform(), + lsMem.getColObj1()->getWorldTransform(), + lsMem.getColObj0()->getRestitution(),lsMem.getColObj1()->getRestitution(), + lsMem.getColObj0()->getFriction(),lsMem.getColObj1()->getFriction(), + collisionPairInput.m_isSwapped); + + + float distance=0.f; + btVector3 normalInB; + + + if (//!gUseEpa && +#ifdef USE_SEPDISTANCE_UTIL + lsMem.getlocalCollisionAlgorithm()->m_sepDistance.getConservativeSeparatingDistance()<=0.f +#else + 1 +#endif + ) + { +//#define USE_PE_BOX_BOX 1 +#ifdef USE_PE_BOX_BOX + { + + //getCollisionMargin0 + btScalar margin0 = lsMem.getlocalCollisionAlgorithm()->getCollisionMargin0(); + btScalar margin1 = lsMem.getlocalCollisionAlgorithm()->getCollisionMargin1(); + btVector3 shapeDim0 = lsMem.getlocalCollisionAlgorithm()->getShapeDimensions0()+btVector3(margin0,margin0,margin0); + btVector3 shapeDim1 = lsMem.getlocalCollisionAlgorithm()->getShapeDimensions1()+btVector3(margin1,margin1,margin1); + + Box boxA(shapeDim0.getX(),shapeDim0.getY(),shapeDim0.getZ()); + Vector3 vmPos0 = getVmVector3(collisionPairInput.m_worldTransform0.getOrigin()); + Vector3 vmPos1 = getVmVector3(collisionPairInput.m_worldTransform1.getOrigin()); + Matrix3 vmMatrix0 = getVmMatrix3(collisionPairInput.m_worldTransform0.getBasis()); + Matrix3 vmMatrix1 = getVmMatrix3(collisionPairInput.m_worldTransform1.getBasis()); + + Transform3 transformA(vmMatrix0,vmPos0); + Box boxB(shapeDim1.getX(),shapeDim1.getY(),shapeDim1.getZ()); + Transform3 transformB(vmMatrix1,vmPos1); + BoxPoint resultClosestBoxPointA; + BoxPoint resultClosestBoxPointB; + Vector3 resultNormal; +#ifdef USE_SEPDISTANCE_UTIL + float distanceThreshold = FLT_MAX +#else + float distanceThreshold = 0.f; +#endif + + + distance = boxBoxDistance(resultNormal,resultClosestBoxPointA,resultClosestBoxPointB, boxA, transformA, boxB,transformB,distanceThreshold); + + normalInB = -getBtVector3(resultNormal); + + if(distance < spuManifold->getContactBreakingThreshold()) + { + btVector3 pointOnB = collisionPairInput.m_worldTransform1(getBtVector3(resultClosestBoxPointB.localPoint)); + + spuContacts.addContactPoint( + normalInB, + pointOnB, + distance); + } + } +#else + { + + btScalar margin0 = lsMem.getlocalCollisionAlgorithm()->getCollisionMargin0(); + btScalar margin1 = lsMem.getlocalCollisionAlgorithm()->getCollisionMargin1(); + btVector3 shapeDim0 = lsMem.getlocalCollisionAlgorithm()->getShapeDimensions0()+btVector3(margin0,margin0,margin0); + btVector3 shapeDim1 = lsMem.getlocalCollisionAlgorithm()->getShapeDimensions1()+btVector3(margin1,margin1,margin1); + + + btBoxShape box0(shapeDim0); + btBoxShape box1(shapeDim1); + + struct SpuBridgeContactCollector : public btDiscreteCollisionDetectorInterface::Result + { + SpuContactResult& m_spuContacts; + + virtual void setShapeIdentifiersA(int partId0,int index0) + { + m_spuContacts.setShapeIdentifiersA(partId0,index0); + } + virtual void setShapeIdentifiersB(int partId1,int index1) + { + m_spuContacts.setShapeIdentifiersB(partId1,index1); + } + virtual void addContactPoint(const btVector3& normalOnBInWorld,const btVector3& pointInWorld,btScalar depth) + { + m_spuContacts.addContactPoint(normalOnBInWorld,pointInWorld,depth); + } + + SpuBridgeContactCollector(SpuContactResult& spuContacts) + :m_spuContacts(spuContacts) + { + + } + }; + + SpuBridgeContactCollector bridgeOutput(spuContacts); + + btDiscreteCollisionDetectorInterface::ClosestPointInput input; + input.m_maximumDistanceSquared = BT_LARGE_FLOAT; + input.m_transformA = collisionPairInput.m_worldTransform0; + input.m_transformB = collisionPairInput.m_worldTransform1; + + btBoxBoxDetector detector(&box0,&box1); + + detector.getClosestPoints(input,bridgeOutput,0); + + } +#endif //USE_PE_BOX_BOX + + lsMem.needsDmaPutContactManifoldAlgo = true; +#ifdef USE_SEPDISTANCE_UTIL + btScalar sepDist2 = distance+spuManifold->getContactBreakingThreshold(); + lsMem.getlocalCollisionAlgorithm()->m_sepDistance.initSeparatingDistance(normalInB,sepDist2,collisionPairInput.m_worldTransform0,collisionPairInput.m_worldTransform1); +#endif //USE_SEPDISTANCE_UTIL + gProcessedCol++; + } else + { + gSkippedCol++; + } + + spuContacts.flush(); + + + } else +#endif //USE_DEDICATED_BOX_BOX + { + if ( +#ifdef USE_SEPDISTANCE_UTIL + lsMem.getlocalCollisionAlgorithm()->m_sepDistance.getConservativeSeparatingDistance()<=0.f +#else + 1 +#endif //USE_SEPDISTANCE_UTIL + ) + { + handleCollisionPair(collisionPairInput, lsMem, spuContacts, (ppu_address_t)lsMem.getColObj0()->getCollisionShape(), &lsMem.gCollisionShapes[0].collisionShape, (ppu_address_t)lsMem.getColObj1()->getCollisionShape(), &lsMem.gCollisionShapes[1].collisionShape); + } else + { + //spu_printf("boxbox dist = %f\n",distance); + btPersistentManifold* spuManifold=lsMem.getContactManifoldPtr(); + btPersistentManifold* manifold = (btPersistentManifold*)collisionPairInput.m_persistentManifoldPtr; + ppu_address_t manifoldAddress = (ppu_address_t)manifold; + + spuContacts.setContactInfo(spuManifold,manifoldAddress,lsMem.getColObj0()->getWorldTransform(), + lsMem.getColObj1()->getWorldTransform(), + lsMem.getColObj0()->getRestitution(),lsMem.getColObj1()->getRestitution(), + lsMem.getColObj0()->getFriction(),lsMem.getColObj1()->getFriction(), + collisionPairInput.m_isSwapped); + + spuContacts.flush(); + } + } + + } + + } + } + +#ifdef USE_SEPDISTANCE_UTIL +#if defined (__SPU__) || defined (USE_LIBSPE2) + if (lsMem.needsDmaPutContactManifoldAlgo) + { + dmaSize = sizeof(SpuContactManifoldCollisionAlgorithm); + dmaPpuAddress2 = (ppu_address_t)pair.m_algorithm; + cellDmaLargePut(&lsMem.gSpuContactManifoldAlgoBuffer, dmaPpuAddress2 , dmaSize, DMA_TAG(1), 0, 0); + cellDmaWaitTagStatusAll(DMA_MASK(1)); + } +#endif +#endif //#ifdef USE_SEPDISTANCE_UTIL + + } + } + } + } //end for (j = 0; j < numOnPage; j++) + + }// for + + + + return; +} diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuGatheringCollisionTask.h b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuGatheringCollisionTask.h new file mode 100644 index 000000000..bbaa555ee --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuGatheringCollisionTask.h @@ -0,0 +1,140 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 SPU_GATHERING_COLLISION_TASK_H +#define SPU_GATHERING_COLLISION_TASK_H + +#include "../PlatformDefinitions.h" +//#define DEBUG_SPU_COLLISION_DETECTION 1 + + +///Task Description for SPU collision detection +struct SpuGatherAndProcessPairsTaskDesc +{ + ppu_address_t m_inPairPtr;//m_pairArrayPtr; + //mutex variable + uint32_t m_someMutexVariableInMainMemory; + + ppu_address_t m_dispatcher; + + uint32_t numOnLastPage; + + uint16_t numPages; + uint16_t taskId; + bool m_useEpa; + + struct CollisionTask_LocalStoreMemory* m_lsMemory; +} + +#if defined(__CELLOS_LV2__) || defined(USE_LIBSPE2) +__attribute__ ((aligned (128))) +#endif +; + + +void processCollisionTask(void* userPtr, void* lsMemory); + +void* createCollisionLocalStoreMemory(); + + +#if defined(USE_LIBSPE2) && defined(__SPU__) +#include "../SpuLibspe2Support.h" +#include +#include +#include + +//#define DEBUG_LIBSPE2_SPU_TASK + + + +int main(unsigned long long speid, addr64 argp, addr64 envp) +{ + printf("SPU: hello \n"); + + ATTRIBUTE_ALIGNED128(btSpuStatus status); + ATTRIBUTE_ALIGNED16( SpuGatherAndProcessPairsTaskDesc taskDesc ) ; + unsigned int received_message = Spu_Mailbox_Event_Nothing; + bool shutdown = false; + + cellDmaGet(&status, argp.ull, sizeof(btSpuStatus), DMA_TAG(3), 0, 0); + cellDmaWaitTagStatusAll(DMA_MASK(3)); + + status.m_status = Spu_Status_Free; + status.m_lsMemory.p = createCollisionLocalStoreMemory(); + + cellDmaLargePut(&status, argp.ull, sizeof(btSpuStatus), DMA_TAG(3), 0, 0); + cellDmaWaitTagStatusAll(DMA_MASK(3)); + + + while ( btLikely( !shutdown ) ) + { + + received_message = spu_read_in_mbox(); + + if( btLikely( received_message == Spu_Mailbox_Event_Task )) + { +#ifdef DEBUG_LIBSPE2_SPU_TASK + printf("SPU: received Spu_Mailbox_Event_Task\n"); +#endif //DEBUG_LIBSPE2_SPU_TASK + + // refresh the status + cellDmaGet(&status, argp.ull, sizeof(btSpuStatus), DMA_TAG(3), 0, 0); + cellDmaWaitTagStatusAll(DMA_MASK(3)); + + btAssert(status.m_status==Spu_Status_Occupied); + + cellDmaGet(&taskDesc, status.m_taskDesc.p, sizeof(SpuGatherAndProcessPairsTaskDesc), DMA_TAG(3), 0, 0); + cellDmaWaitTagStatusAll(DMA_MASK(3)); +#ifdef DEBUG_LIBSPE2_SPU_TASK + printf("SPU:processCollisionTask\n"); +#endif //DEBUG_LIBSPE2_SPU_TASK + processCollisionTask((void*)&taskDesc, taskDesc.m_lsMemory); + +#ifdef DEBUG_LIBSPE2_SPU_TASK + printf("SPU:finished processCollisionTask\n"); +#endif //DEBUG_LIBSPE2_SPU_TASK + } + else + { +#ifdef DEBUG_LIBSPE2_SPU_TASK + printf("SPU: received ShutDown\n"); +#endif //DEBUG_LIBSPE2_SPU_TASK + if( btLikely( received_message == Spu_Mailbox_Event_Shutdown ) ) + { + shutdown = true; + } + else + { + //printf("SPU - Sth. recieved\n"); + } + } + + // set to status free and wait for next task + status.m_status = Spu_Status_Free; + cellDmaLargePut(&status, argp.ull, sizeof(btSpuStatus), DMA_TAG(3), 0, 0); + cellDmaWaitTagStatusAll(DMA_MASK(3)); + + + } + + printf("SPU: shutdown\n"); + return 0; +} +#endif // USE_LIBSPE2 + + +#endif //SPU_GATHERING_COLLISION_TASK_H + + diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuLocalSupport.h b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuLocalSupport.h new file mode 100644 index 000000000..8b89de03f --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuLocalSupport.h @@ -0,0 +1,19 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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. +*/ + + + + + diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuMinkowskiPenetrationDepthSolver.cpp b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuMinkowskiPenetrationDepthSolver.cpp new file mode 100644 index 000000000..9f7e64dd1 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuMinkowskiPenetrationDepthSolver.cpp @@ -0,0 +1,348 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "SpuMinkowskiPenetrationDepthSolver.h" +#include "SpuContactResult.h" +#include "SpuPreferredPenetrationDirections.h" +#include "BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.h" +#include "BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h" +#include "SpuCollisionShapes.h" + +#define NUM_UNITSPHERE_POINTS 42 +static btVector3 sPenetrationDirections[NUM_UNITSPHERE_POINTS+MAX_PREFERRED_PENETRATION_DIRECTIONS*2] = +{ +btVector3(btScalar(0.000000) , btScalar(-0.000000),btScalar(-1.000000)), +btVector3(btScalar(0.723608) , btScalar(-0.525725),btScalar(-0.447219)), +btVector3(btScalar(-0.276388) , btScalar(-0.850649),btScalar(-0.447219)), +btVector3(btScalar(-0.894426) , btScalar(-0.000000),btScalar(-0.447216)), +btVector3(btScalar(-0.276388) , btScalar(0.850649),btScalar(-0.447220)), +btVector3(btScalar(0.723608) , btScalar(0.525725),btScalar(-0.447219)), +btVector3(btScalar(0.276388) , btScalar(-0.850649),btScalar(0.447220)), +btVector3(btScalar(-0.723608) , btScalar(-0.525725),btScalar(0.447219)), +btVector3(btScalar(-0.723608) , btScalar(0.525725),btScalar(0.447219)), +btVector3(btScalar(0.276388) , btScalar(0.850649),btScalar(0.447219)), +btVector3(btScalar(0.894426) , btScalar(0.000000),btScalar(0.447216)), +btVector3(btScalar(-0.000000) , btScalar(0.000000),btScalar(1.000000)), +btVector3(btScalar(0.425323) , btScalar(-0.309011),btScalar(-0.850654)), +btVector3(btScalar(-0.162456) , btScalar(-0.499995),btScalar(-0.850654)), +btVector3(btScalar(0.262869) , btScalar(-0.809012),btScalar(-0.525738)), +btVector3(btScalar(0.425323) , btScalar(0.309011),btScalar(-0.850654)), +btVector3(btScalar(0.850648) , btScalar(-0.000000),btScalar(-0.525736)), +btVector3(btScalar(-0.525730) , btScalar(-0.000000),btScalar(-0.850652)), +btVector3(btScalar(-0.688190) , btScalar(-0.499997),btScalar(-0.525736)), +btVector3(btScalar(-0.162456) , btScalar(0.499995),btScalar(-0.850654)), +btVector3(btScalar(-0.688190) , btScalar(0.499997),btScalar(-0.525736)), +btVector3(btScalar(0.262869) , btScalar(0.809012),btScalar(-0.525738)), +btVector3(btScalar(0.951058) , btScalar(0.309013),btScalar(0.000000)), +btVector3(btScalar(0.951058) , btScalar(-0.309013),btScalar(0.000000)), +btVector3(btScalar(0.587786) , btScalar(-0.809017),btScalar(0.000000)), +btVector3(btScalar(0.000000) , btScalar(-1.000000),btScalar(0.000000)), +btVector3(btScalar(-0.587786) , btScalar(-0.809017),btScalar(0.000000)), +btVector3(btScalar(-0.951058) , btScalar(-0.309013),btScalar(-0.000000)), +btVector3(btScalar(-0.951058) , btScalar(0.309013),btScalar(-0.000000)), +btVector3(btScalar(-0.587786) , btScalar(0.809017),btScalar(-0.000000)), +btVector3(btScalar(-0.000000) , btScalar(1.000000),btScalar(-0.000000)), +btVector3(btScalar(0.587786) , btScalar(0.809017),btScalar(-0.000000)), +btVector3(btScalar(0.688190) , btScalar(-0.499997),btScalar(0.525736)), +btVector3(btScalar(-0.262869) , btScalar(-0.809012),btScalar(0.525738)), +btVector3(btScalar(-0.850648) , btScalar(0.000000),btScalar(0.525736)), +btVector3(btScalar(-0.262869) , btScalar(0.809012),btScalar(0.525738)), +btVector3(btScalar(0.688190) , btScalar(0.499997),btScalar(0.525736)), +btVector3(btScalar(0.525730) , btScalar(0.000000),btScalar(0.850652)), +btVector3(btScalar(0.162456) , btScalar(-0.499995),btScalar(0.850654)), +btVector3(btScalar(-0.425323) , btScalar(-0.309011),btScalar(0.850654)), +btVector3(btScalar(-0.425323) , btScalar(0.309011),btScalar(0.850654)), +btVector3(btScalar(0.162456) , btScalar(0.499995),btScalar(0.850654)) +}; + + +bool SpuMinkowskiPenetrationDepthSolver::calcPenDepth( btSimplexSolverInterface& simplexSolver, + const btConvexShape* convexA,const btConvexShape* convexB, + const btTransform& transA,const btTransform& transB, + btVector3& v, btVector3& pa, btVector3& pb, + class btIDebugDraw* debugDraw,btStackAlloc* stackAlloc) +{ +#if 0 + (void)stackAlloc; + (void)v; + + + struct btIntermediateResult : public SpuContactResult + { + + btIntermediateResult():m_hasResult(false) + { + } + + btVector3 m_normalOnBInWorld; + btVector3 m_pointInWorld; + btScalar m_depth; + bool m_hasResult; + + virtual void setShapeIdentifiersA(int partId0,int index0) + { + (void)partId0; + (void)index0; + } + + virtual void setShapeIdentifiersB(int partId1,int index1) + { + (void)partId1; + (void)index1; + } + void addContactPoint(const btVector3& normalOnBInWorld,const btVector3& pointInWorld,btScalar depth) + { + m_normalOnBInWorld = normalOnBInWorld; + m_pointInWorld = pointInWorld; + m_depth = depth; + m_hasResult = true; + } + }; + + //just take fixed number of orientation, and sample the penetration depth in that direction + btScalar minProj = btScalar(BT_LARGE_FLOAT); + btVector3 minNorm(0.f,0.f,0.f); + btVector3 minVertex; + btVector3 minA,minB; + btVector3 seperatingAxisInA,seperatingAxisInB; + btVector3 pInA,qInB,pWorld,qWorld,w; + +//#define USE_BATCHED_SUPPORT 1 +#ifdef USE_BATCHED_SUPPORT + + btVector3 supportVerticesABatch[NUM_UNITSPHERE_POINTS+MAX_PREFERRED_PENETRATION_DIRECTIONS*2]; + btVector3 supportVerticesBBatch[NUM_UNITSPHERE_POINTS+MAX_PREFERRED_PENETRATION_DIRECTIONS*2]; + btVector3 seperatingAxisInABatch[NUM_UNITSPHERE_POINTS+MAX_PREFERRED_PENETRATION_DIRECTIONS*2]; + btVector3 seperatingAxisInBBatch[NUM_UNITSPHERE_POINTS+MAX_PREFERRED_PENETRATION_DIRECTIONS*2]; + int i; + + int numSampleDirections = NUM_UNITSPHERE_POINTS; + + for (i=0;igetNumPreferredPenetrationDirections(); + if (numPDA) + { + for (int i=0;igetPreferredPenetrationDirection(i,norm); + norm = transA.getBasis() * norm; + sPenetrationDirections[numSampleDirections] = norm; + seperatingAxisInABatch[numSampleDirections] = (-norm) * transA.getBasis(); + seperatingAxisInBBatch[numSampleDirections] = norm * transB.getBasis(); + numSampleDirections++; + } + } + } + + { + int numPDB = convexB->getNumPreferredPenetrationDirections(); + if (numPDB) + { + for (int i=0;igetPreferredPenetrationDirection(i,norm); + norm = transB.getBasis() * norm; + sPenetrationDirections[numSampleDirections] = norm; + seperatingAxisInABatch[numSampleDirections] = (-norm) * transA.getBasis(); + seperatingAxisInBBatch[numSampleDirections] = norm * transB.getBasis(); + numSampleDirections++; + } + } + } + + + + convexA->batchedUnitVectorGetSupportingVertexWithoutMargin(seperatingAxisInABatch,supportVerticesABatch,numSampleDirections); + convexB->batchedUnitVectorGetSupportingVertexWithoutMargin(seperatingAxisInBBatch,supportVerticesBBatch,numSampleDirections); + + for (i=0;ilocalGetSupportVertexWithoutMarginNonVirtual( seperatingAxisInA);//, NULL); + qInB = convexB->localGetSupportVertexWithoutMarginNonVirtual(seperatingAxisInB);//, NULL); + + // pInA = convexA->localGetSupportingVertexWithoutMargin(seperatingAxisInA); + // qInB = convexB->localGetSupportingVertexWithoutMargin(seperatingAxisInB); + + pWorld = transA(pInA); + qWorld = transB(qInB); + w = qWorld - pWorld; + btScalar delta = norm.dot(w); + //find smallest delta + if (delta < minProj) + { + minProj = delta; + minNorm = norm; + minA = pWorld; + minB = qWorld; + } + } +#endif //USE_BATCHED_SUPPORT + + //add the margins + + minA += minNorm*marginA; + minB -= minNorm*marginB; + //no penetration + if (minProj < btScalar(0.)) + return false; + + minProj += (marginA + marginB) + btScalar(1.00); + + + + + +//#define DEBUG_DRAW 1 +#ifdef DEBUG_DRAW + if (debugDraw) + { + btVector3 color(0,1,0); + debugDraw->drawLine(minA,minB,color); + color = btVector3 (1,1,1); + btVector3 vec = minB-minA; + btScalar prj2 = minNorm.dot(vec); + debugDraw->drawLine(minA,minA+(minNorm*minProj),color); + + } +#endif //DEBUG_DRAW + + + btGjkPairDetector gjkdet(convexA,convexB,&simplexSolver,0); + + btScalar offsetDist = minProj; + btVector3 offset = minNorm * offsetDist; + + + SpuClosestPointInput input; + input.m_convexVertexData[0] = convexVertexDataA; + input.m_convexVertexData[1] = convexVertexDataB; + btVector3 newOrg = transA.getOrigin() + offset; + + btTransform displacedTrans = transA; + displacedTrans.setOrigin(newOrg); + + input.m_transformA = displacedTrans; + input.m_transformB = transB; + input.m_maximumDistanceSquared = btScalar(BT_LARGE_FLOAT);//minProj; + + btIntermediateResult res; + gjkdet.getClosestPoints(input,res,0); + + btScalar correctedMinNorm = minProj - res.m_depth; + + + //the penetration depth is over-estimated, relax it + btScalar penetration_relaxation= btScalar(1.); + minNorm*=penetration_relaxation; + + if (res.m_hasResult) + { + + pa = res.m_pointInWorld - minNorm * correctedMinNorm; + pb = res.m_pointInWorld; + +#ifdef DEBUG_DRAW + if (debugDraw) + { + btVector3 color(1,0,0); + debugDraw->drawLine(pa,pb,color); + } +#endif//DEBUG_DRAW + + + } else { + // could not seperate shapes + //btAssert (false); + } + return res.m_hasResult; +#endif + return false; +} + + + diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuMinkowskiPenetrationDepthSolver.h b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuMinkowskiPenetrationDepthSolver.h new file mode 100644 index 000000000..18ad223ed --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuMinkowskiPenetrationDepthSolver.h @@ -0,0 +1,48 @@ + +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 MINKOWSKI_PENETRATION_DEPTH_SOLVER_H +#define MINKOWSKI_PENETRATION_DEPTH_SOLVER_H + + +#include "BulletCollision/NarrowPhaseCollision/btConvexPenetrationDepthSolver.h" + +class btStackAlloc; +class btIDebugDraw; +class btVoronoiSimplexSolver; +class btConvexShape; + +///MinkowskiPenetrationDepthSolver implements bruteforce penetration depth estimation. +///Implementation is based on sampling the depth using support mapping, and using GJK step to get the witness points. +class SpuMinkowskiPenetrationDepthSolver : public btConvexPenetrationDepthSolver +{ +public: + SpuMinkowskiPenetrationDepthSolver() {} + virtual ~SpuMinkowskiPenetrationDepthSolver() {}; + + virtual bool calcPenDepth( btSimplexSolverInterface& simplexSolver, + const btConvexShape* convexA,const btConvexShape* convexB, + const btTransform& transA,const btTransform& transB, + btVector3& v, btVector3& pa, btVector3& pb, + class btIDebugDraw* debugDraw,btStackAlloc* stackAlloc + ); + + +}; + + +#endif //MINKOWSKI_PENETRATION_DEPTH_SOLVER_H + diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuPreferredPenetrationDirections.h b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuPreferredPenetrationDirections.h new file mode 100644 index 000000000..774a0cb2e --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuPreferredPenetrationDirections.h @@ -0,0 +1,70 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2007 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 _SPU_PREFERRED_PENETRATION_DIRECTIONS_H +#define _SPU_PREFERRED_PENETRATION_DIRECTIONS_H + + +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" + +int spuGetNumPreferredPenetrationDirections(int shapeType, void* shape) +{ + switch (shapeType) + { + case TRIANGLE_SHAPE_PROXYTYPE: + { + return 2; + //spu_printf("2\n"); + break; + } + default: + { +#if __ASSERT + spu_printf("spuGetNumPreferredPenetrationDirections() - Unsupported bound type: %d.\n", shapeType); +#endif // __ASSERT + } + } + + return 0; +} + +void spuGetPreferredPenetrationDirection(int shapeType, void* shape, int index, btVector3& penetrationVector) +{ + + + switch (shapeType) + { + case TRIANGLE_SHAPE_PROXYTYPE: + { + btVector3* vertices = (btVector3*)shape; + ///calcNormal + penetrationVector = (vertices[1]-vertices[0]).cross(vertices[2]-vertices[0]); + penetrationVector.normalize(); + if (index) + penetrationVector *= btScalar(-1.); + break; + } + default: + { + +#if __ASSERT + spu_printf("spuGetNumPreferredPenetrationDirections() - Unsupported bound type: %d.\n", shapeType); +#endif // __ASSERT + } + } + +} + +#endif //_SPU_PREFERRED_PENETRATION_DIRECTIONS_H diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/boxBoxDistance.cpp b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/boxBoxDistance.cpp new file mode 100644 index 000000000..30642a392 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/boxBoxDistance.cpp @@ -0,0 +1,1155 @@ +/* + Copyright (C) 2006, 2008 Sony Computer Entertainment Inc. + All rights reserved. + +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 "Box.h" + +static inline float sqr( float a ) +{ + return (a * a); +} + +enum BoxSepAxisType +{ + A_AXIS, B_AXIS, CROSS_AXIS +}; + +//------------------------------------------------------------------------------------------------- +// voronoiTol: bevels Voronoi planes slightly which helps when features are parallel. +//------------------------------------------------------------------------------------------------- + +static const float voronoiTol = -1.0e-5f; + +//------------------------------------------------------------------------------------------------- +// separating axis tests: gaps along each axis are computed, and the axis with the maximum +// gap is stored. cross product axes are normalized. +//------------------------------------------------------------------------------------------------- + +#define AaxisTest( dim, letter, first ) \ +{ \ + if ( first ) \ + { \ + maxGap = gap = gapsA.get##letter(); \ + if ( gap > distanceThreshold ) return gap; \ + axisType = A_AXIS; \ + faceDimA = dim; \ + axisA = identity.getCol##dim(); \ + } \ + else \ + { \ + gap = gapsA.get##letter(); \ + if ( gap > distanceThreshold ) return gap; \ + else if ( gap > maxGap ) \ + { \ + maxGap = gap; \ + axisType = A_AXIS; \ + faceDimA = dim; \ + axisA = identity.getCol##dim(); \ + } \ + } \ +} + + +#define BaxisTest( dim, letter ) \ +{ \ + gap = gapsB.get##letter(); \ + if ( gap > distanceThreshold ) return gap; \ + else if ( gap > maxGap ) \ + { \ + maxGap = gap; \ + axisType = B_AXIS; \ + faceDimB = dim; \ + axisB = identity.getCol##dim(); \ + } \ +} + +#define CrossAxisTest( dima, dimb, letterb ) \ +{ \ + const float lsqr_tolerance = 1.0e-30f; \ + float lsqr; \ + \ + lsqr = lsqrs.getCol##dima().get##letterb(); \ + \ + if ( lsqr > lsqr_tolerance ) \ + { \ + float l_recip = 1.0f / sqrtf( lsqr ); \ + gap = float(gapsAxB.getCol##dima().get##letterb()) * l_recip; \ + \ + if ( gap > distanceThreshold ) \ + { \ + return gap; \ + } \ + \ + if ( gap > maxGap ) \ + { \ + maxGap = gap; \ + axisType = CROSS_AXIS; \ + edgeDimA = dima; \ + edgeDimB = dimb; \ + axisA = cross(identity.getCol##dima(),matrixAB.getCol##dimb()) * l_recip; \ + } \ + } \ +} + +//------------------------------------------------------------------------------------------------- +// tests whether a vertex of box B and a face of box A are the closest features +//------------------------------------------------------------------------------------------------- + +inline +float +VertexBFaceATest( + bool & inVoronoi, + float & t0, + float & t1, + const Vector3 & hA, + PE_REF(Vector3) faceOffsetAB, + PE_REF(Vector3) faceOffsetBA, + const Matrix3 & matrixAB, + const Matrix3 & matrixBA, + PE_REF(Vector3) signsB, + PE_REF(Vector3) scalesB ) +{ + // compute a corner of box B in A's coordinate system + + Vector3 corner = + Vector3( faceOffsetAB + matrixAB.getCol0() * scalesB.getX() + matrixAB.getCol1() * scalesB.getY() ); + + // compute the parameters of the point on A, closest to this corner + + t0 = corner[0]; + t1 = corner[1]; + + if ( t0 > hA[0] ) + t0 = hA[0]; + else if ( t0 < -hA[0] ) + t0 = -hA[0]; + if ( t1 > hA[1] ) + t1 = hA[1]; + else if ( t1 < -hA[1] ) + t1 = -hA[1]; + + // do the Voronoi test: already know the point on B is in the Voronoi region of the + // point on A, check the reverse. + + Vector3 facePointB = + Vector3( mulPerElem( faceOffsetBA + matrixBA.getCol0() * t0 + matrixBA.getCol1() * t1 - scalesB, signsB ) ); + + inVoronoi = ( ( facePointB[0] >= voronoiTol * facePointB[2] ) && + ( facePointB[1] >= voronoiTol * facePointB[0] ) && + ( facePointB[2] >= voronoiTol * facePointB[1] ) ); + + return (sqr( corner[0] - t0 ) + sqr( corner[1] - t1 ) + sqr( corner[2] )); +} + +#define VertexBFaceA_SetNewMin() \ +{ \ + minDistSqr = distSqr; \ + localPointA.setX(t0); \ + localPointA.setY(t1); \ + localPointB.setX( scalesB.getX() ); \ + localPointB.setY( scalesB.getY() ); \ + featureA = F; \ + featureB = V; \ +} + +void +VertexBFaceATests( + bool & done, + float & minDistSqr, + Point3 & localPointA, + Point3 & localPointB, + FeatureType & featureA, + FeatureType & featureB, + const Vector3 & hA, + PE_REF(Vector3) faceOffsetAB, + PE_REF(Vector3) faceOffsetBA, + const Matrix3 & matrixAB, + const Matrix3 & matrixBA, + PE_REF(Vector3) signsB, + PE_REF(Vector3) scalesB, + bool first ) +{ + + float t0, t1; + float distSqr; + + distSqr = VertexBFaceATest( done, t0, t1, hA, faceOffsetAB, faceOffsetBA, + matrixAB, matrixBA, signsB, scalesB ); + + if ( first ) { + VertexBFaceA_SetNewMin(); + } else { + if ( distSqr < minDistSqr ) { + VertexBFaceA_SetNewMin(); + } + } + + if ( done ) + return; + + signsB.setX( -signsB.getX() ); + scalesB.setX( -scalesB.getX() ); + + distSqr = VertexBFaceATest( done, t0, t1, hA, faceOffsetAB, faceOffsetBA, + matrixAB, matrixBA, signsB, scalesB ); + + if ( distSqr < minDistSqr ) { + VertexBFaceA_SetNewMin(); + } + + if ( done ) + return; + + signsB.setY( -signsB.getY() ); + scalesB.setY( -scalesB.getY() ); + + distSqr = VertexBFaceATest( done, t0, t1, hA, faceOffsetAB, faceOffsetBA, + matrixAB, matrixBA, signsB, scalesB ); + + if ( distSqr < minDistSqr ) { + VertexBFaceA_SetNewMin(); + } + + if ( done ) + return; + + signsB.setX( -signsB.getX() ); + scalesB.setX( -scalesB.getX() ); + + distSqr = VertexBFaceATest( done, t0, t1, hA, faceOffsetAB, faceOffsetBA, + matrixAB, matrixBA, signsB, scalesB ); + + if ( distSqr < minDistSqr ) { + VertexBFaceA_SetNewMin(); + } +} + +//------------------------------------------------------------------------------------------------- +// VertexAFaceBTest: tests whether a vertex of box A and a face of box B are the closest features +//------------------------------------------------------------------------------------------------- + +inline +float +VertexAFaceBTest( + bool & inVoronoi, + float & t0, + float & t1, + const Vector3 & hB, + PE_REF(Vector3) faceOffsetAB, + PE_REF(Vector3) faceOffsetBA, + const Matrix3 & matrixAB, + const Matrix3 & matrixBA, + PE_REF(Vector3) signsA, + PE_REF(Vector3) scalesA ) +{ + Vector3 corner = + Vector3( faceOffsetBA + matrixBA.getCol0() * scalesA.getX() + matrixBA.getCol1() * scalesA.getY() ); + + t0 = corner[0]; + t1 = corner[1]; + + if ( t0 > hB[0] ) + t0 = hB[0]; + else if ( t0 < -hB[0] ) + t0 = -hB[0]; + if ( t1 > hB[1] ) + t1 = hB[1]; + else if ( t1 < -hB[1] ) + t1 = -hB[1]; + + Vector3 facePointA = + Vector3( mulPerElem( faceOffsetAB + matrixAB.getCol0() * t0 + matrixAB.getCol1() * t1 - scalesA, signsA ) ); + + inVoronoi = ( ( facePointA[0] >= voronoiTol * facePointA[2] ) && + ( facePointA[1] >= voronoiTol * facePointA[0] ) && + ( facePointA[2] >= voronoiTol * facePointA[1] ) ); + + return (sqr( corner[0] - t0 ) + sqr( corner[1] - t1 ) + sqr( corner[2] )); +} + +#define VertexAFaceB_SetNewMin() \ +{ \ + minDistSqr = distSqr; \ + localPointB.setX(t0); \ + localPointB.setY(t1); \ + localPointA.setX( scalesA.getX() ); \ + localPointA.setY( scalesA.getY() ); \ + featureA = V; \ + featureB = F; \ +} + +void +VertexAFaceBTests( + bool & done, + float & minDistSqr, + Point3 & localPointA, + Point3 & localPointB, + FeatureType & featureA, + FeatureType & featureB, + const Vector3 & hB, + PE_REF(Vector3) faceOffsetAB, + PE_REF(Vector3) faceOffsetBA, + const Matrix3 & matrixAB, + const Matrix3 & matrixBA, + PE_REF(Vector3) signsA, + PE_REF(Vector3) scalesA, + bool first ) +{ + float t0, t1; + float distSqr; + + distSqr = VertexAFaceBTest( done, t0, t1, hB, faceOffsetAB, faceOffsetBA, + matrixAB, matrixBA, signsA, scalesA ); + + if ( first ) { + VertexAFaceB_SetNewMin(); + } else { + if ( distSqr < minDistSqr ) { + VertexAFaceB_SetNewMin(); + } + } + + if ( done ) + return; + + signsA.setX( -signsA.getX() ); + scalesA.setX( -scalesA.getX() ); + + distSqr = VertexAFaceBTest( done, t0, t1, hB, faceOffsetAB, faceOffsetBA, + matrixAB, matrixBA, signsA, scalesA ); + + if ( distSqr < minDistSqr ) { + VertexAFaceB_SetNewMin(); + } + + if ( done ) + return; + + signsA.setY( -signsA.getY() ); + scalesA.setY( -scalesA.getY() ); + + distSqr = VertexAFaceBTest( done, t0, t1, hB, faceOffsetAB, faceOffsetBA, + matrixAB, matrixBA, signsA, scalesA ); + + if ( distSqr < minDistSqr ) { + VertexAFaceB_SetNewMin(); + } + + if ( done ) + return; + + signsA.setX( -signsA.getX() ); + scalesA.setX( -scalesA.getX() ); + + distSqr = VertexAFaceBTest( done, t0, t1, hB, faceOffsetAB, faceOffsetBA, + matrixAB, matrixBA, signsA, scalesA ); + + if ( distSqr < minDistSqr ) { + VertexAFaceB_SetNewMin(); + } +} + +//------------------------------------------------------------------------------------------------- +// EdgeEdgeTest: +// +// tests whether a pair of edges are the closest features +// +// note on the shorthand: +// 'a' & 'b' refer to the edges. +// 'c' is the dimension of the axis that points from the face center to the edge Center +// 'd' is the dimension of the edge Direction +// the dimension of the face normal is 2 +//------------------------------------------------------------------------------------------------- + +#define EdgeEdgeTest( ac, ac_letter, ad, ad_letter, bc, bc_letter, bd, bd_letter ) \ +{ \ + Vector3 edgeOffsetAB; \ + Vector3 edgeOffsetBA; \ + \ + edgeOffsetAB = faceOffsetAB + matrixAB.getCol##bc() * scalesB.get##bc_letter(); \ + edgeOffsetAB.set##ac_letter( edgeOffsetAB.get##ac_letter() - scalesA.get##ac_letter() ); \ + \ + edgeOffsetBA = faceOffsetBA + matrixBA.getCol##ac() * scalesA.get##ac_letter(); \ + edgeOffsetBA.set##bc_letter( edgeOffsetBA.get##bc_letter() - scalesB.get##bc_letter() ); \ + \ + float dirDot = matrixAB.getCol##bd().get##ad_letter(); \ + float denom = 1.0f - dirDot*dirDot; \ + float edgeOffsetAB_ad = edgeOffsetAB.get##ad_letter(); \ + float edgeOffsetBA_bd = edgeOffsetBA.get##bd_letter(); \ + \ + if ( denom == 0.0f ) \ + { \ + tA = 0.0f; \ + } \ + else \ + { \ + tA = ( edgeOffsetAB_ad + edgeOffsetBA_bd * dirDot ) / denom; \ + } \ + \ + if ( tA < -hA[ad] ) tA = -hA[ad]; \ + else if ( tA > hA[ad] ) tA = hA[ad]; \ + \ + tB = tA * dirDot + edgeOffsetBA_bd; \ + \ + if ( tB < -hB[bd] ) \ + { \ + tB = -hB[bd]; \ + tA = tB * dirDot + edgeOffsetAB_ad; \ + \ + if ( tA < -hA[ad] ) tA = -hA[ad]; \ + else if ( tA > hA[ad] ) tA = hA[ad]; \ + } \ + else if ( tB > hB[bd] ) \ + { \ + tB = hB[bd]; \ + tA = tB * dirDot + edgeOffsetAB_ad; \ + \ + if ( tA < -hA[ad] ) tA = -hA[ad]; \ + else if ( tA > hA[ad] ) tA = hA[ad]; \ + } \ + \ + Vector3 edgeOffAB = Vector3( mulPerElem( edgeOffsetAB + matrixAB.getCol##bd() * tB, signsA ) );\ + Vector3 edgeOffBA = Vector3( mulPerElem( edgeOffsetBA + matrixBA.getCol##ad() * tA, signsB ) );\ + \ + inVoronoi = ( edgeOffAB[ac] >= voronoiTol * edgeOffAB[2] ) && \ + ( edgeOffAB[2] >= voronoiTol * edgeOffAB[ac] ) && \ + ( edgeOffBA[bc] >= voronoiTol * edgeOffBA[2] ) && \ + ( edgeOffBA[2] >= voronoiTol * edgeOffBA[bc] ); \ + \ + edgeOffAB[ad] -= tA; \ + edgeOffBA[bd] -= tB; \ + \ + return dot(edgeOffAB,edgeOffAB); \ +} + +float +EdgeEdgeTest_0101( + bool & inVoronoi, + float & tA, + float & tB, + const Vector3 & hA, + const Vector3 & hB, + PE_REF(Vector3) faceOffsetAB, + PE_REF(Vector3) faceOffsetBA, + const Matrix3 & matrixAB, + const Matrix3 & matrixBA, + PE_REF(Vector3) signsA, + PE_REF(Vector3) signsB, + PE_REF(Vector3) scalesA, + PE_REF(Vector3) scalesB ) +{ + EdgeEdgeTest( 0, X, 1, Y, 0, X, 1, Y ); +} + +float +EdgeEdgeTest_0110( + bool & inVoronoi, + float & tA, + float & tB, + const Vector3 & hA, + const Vector3 & hB, + PE_REF(Vector3) faceOffsetAB, + PE_REF(Vector3) faceOffsetBA, + const Matrix3 & matrixAB, + const Matrix3 & matrixBA, + PE_REF(Vector3) signsA, + PE_REF(Vector3) signsB, + PE_REF(Vector3) scalesA, + PE_REF(Vector3) scalesB ) +{ + EdgeEdgeTest( 0, X, 1, Y, 1, Y, 0, X ); +} + +float +EdgeEdgeTest_1001( + bool & inVoronoi, + float & tA, + float & tB, + const Vector3 & hA, + const Vector3 & hB, + PE_REF(Vector3) faceOffsetAB, + PE_REF(Vector3) faceOffsetBA, + const Matrix3 & matrixAB, + const Matrix3 & matrixBA, + PE_REF(Vector3) signsA, + PE_REF(Vector3) signsB, + PE_REF(Vector3) scalesA, + PE_REF(Vector3) scalesB ) +{ + EdgeEdgeTest( 1, Y, 0, X, 0, X, 1, Y ); +} + +float +EdgeEdgeTest_1010( + bool & inVoronoi, + float & tA, + float & tB, + const Vector3 & hA, + const Vector3 & hB, + PE_REF(Vector3) faceOffsetAB, + PE_REF(Vector3) faceOffsetBA, + const Matrix3 & matrixAB, + const Matrix3 & matrixBA, + PE_REF(Vector3) signsA, + PE_REF(Vector3) signsB, + PE_REF(Vector3) scalesA, + PE_REF(Vector3) scalesB ) +{ + EdgeEdgeTest( 1, Y, 0, X, 1, Y, 0, X ); +} + +#define EdgeEdge_SetNewMin( ac_letter, ad_letter, bc_letter, bd_letter ) \ +{ \ + minDistSqr = distSqr; \ + localPointA.set##ac_letter(scalesA.get##ac_letter()); \ + localPointA.set##ad_letter(tA); \ + localPointB.set##bc_letter(scalesB.get##bc_letter()); \ + localPointB.set##bd_letter(tB); \ + otherFaceDimA = testOtherFaceDimA; \ + otherFaceDimB = testOtherFaceDimB; \ + featureA = E; \ + featureB = E; \ +} + +void +EdgeEdgeTests( + bool & done, + float & minDistSqr, + Point3 & localPointA, + Point3 & localPointB, + int & otherFaceDimA, + int & otherFaceDimB, + FeatureType & featureA, + FeatureType & featureB, + const Vector3 & hA, + const Vector3 & hB, + PE_REF(Vector3) faceOffsetAB, + PE_REF(Vector3) faceOffsetBA, + const Matrix3 & matrixAB, + const Matrix3 & matrixBA, + PE_REF(Vector3) signsA, + PE_REF(Vector3) signsB, + PE_REF(Vector3) scalesA, + PE_REF(Vector3) scalesB, + bool first ) +{ + + float distSqr; + float tA, tB; + + int testOtherFaceDimA, testOtherFaceDimB; + + testOtherFaceDimA = 0; + testOtherFaceDimB = 0; + + distSqr = EdgeEdgeTest_0101( done, tA, tB, hA, hB, faceOffsetAB, faceOffsetBA, + matrixAB, matrixBA, signsA, signsB, scalesA, scalesB ); + + if ( first ) { + EdgeEdge_SetNewMin( X, Y, X, Y ); + } else { + if ( distSqr < minDistSqr ) { + EdgeEdge_SetNewMin( X, Y, X, Y ); + } + } + + if ( done ) + return; + + signsA.setX( -signsA.getX() ); + scalesA.setX( -scalesA.getX() ); + + distSqr = EdgeEdgeTest_0101( done, tA, tB, hA, hB, faceOffsetAB, faceOffsetBA, + matrixAB, matrixBA, signsA, signsB, scalesA, scalesB ); + + if ( distSqr < minDistSqr ) { + EdgeEdge_SetNewMin( X, Y, X, Y ); + } + + if ( done ) + return; + + signsB.setX( -signsB.getX() ); + scalesB.setX( -scalesB.getX() ); + + distSqr = EdgeEdgeTest_0101( done, tA, tB, hA, hB, faceOffsetAB, faceOffsetBA, + matrixAB, matrixBA, signsA, signsB, scalesA, scalesB ); + + if ( distSqr < minDistSqr ) { + EdgeEdge_SetNewMin( X, Y, X, Y ); + } + + if ( done ) + return; + + signsA.setX( -signsA.getX() ); + scalesA.setX( -scalesA.getX() ); + + distSqr = EdgeEdgeTest_0101( done, tA, tB, hA, hB, faceOffsetAB, faceOffsetBA, + matrixAB, matrixBA, signsA, signsB, scalesA, scalesB ); + + if ( distSqr < minDistSqr ) { + EdgeEdge_SetNewMin( X, Y, X, Y ); + } + + if ( done ) + return; + + testOtherFaceDimA = 1; + testOtherFaceDimB = 0; + signsB.setX( -signsB.getX() ); + scalesB.setX( -scalesB.getX() ); + + distSqr = EdgeEdgeTest_1001( done, tA, tB, hA, hB, faceOffsetAB, faceOffsetBA, + matrixAB, matrixBA, signsA, signsB, scalesA, scalesB ); + + if ( distSqr < minDistSqr ) { + EdgeEdge_SetNewMin( Y, X, X, Y ); + } + + if ( done ) + return; + + signsA.setY( -signsA.getY() ); + scalesA.setY( -scalesA.getY() ); + + distSqr = EdgeEdgeTest_1001( done, tA, tB, hA, hB, faceOffsetAB, faceOffsetBA, + matrixAB, matrixBA, signsA, signsB, scalesA, scalesB ); + + if ( distSqr < minDistSqr ) { + EdgeEdge_SetNewMin( Y, X, X, Y ); + } + + if ( done ) + return; + + signsB.setX( -signsB.getX() ); + scalesB.setX( -scalesB.getX() ); + + distSqr = EdgeEdgeTest_1001( done, tA, tB, hA, hB, faceOffsetAB, faceOffsetBA, + matrixAB, matrixBA, signsA, signsB, scalesA, scalesB ); + + if ( distSqr < minDistSqr ) { + EdgeEdge_SetNewMin( Y, X, X, Y ); + } + + if ( done ) + return; + + signsA.setY( -signsA.getY() ); + scalesA.setY( -scalesA.getY() ); + + distSqr = EdgeEdgeTest_1001( done, tA, tB, hA, hB, faceOffsetAB, faceOffsetBA, + matrixAB, matrixBA, signsA, signsB, scalesA, scalesB ); + + if ( distSqr < minDistSqr ) { + EdgeEdge_SetNewMin( Y, X, X, Y ); + } + + if ( done ) + return; + + testOtherFaceDimA = 0; + testOtherFaceDimB = 1; + signsB.setX( -signsB.getX() ); + scalesB.setX( -scalesB.getX() ); + + distSqr = EdgeEdgeTest_0110( done, tA, tB, hA, hB, faceOffsetAB, faceOffsetBA, + matrixAB, matrixBA, signsA, signsB, scalesA, scalesB ); + + if ( distSqr < minDistSqr ) { + EdgeEdge_SetNewMin( X, Y, Y, X ); + } + + if ( done ) + return; + + signsA.setX( -signsA.getX() ); + scalesA.setX( -scalesA.getX() ); + + distSqr = EdgeEdgeTest_0110( done, tA, tB, hA, hB, faceOffsetAB, faceOffsetBA, + matrixAB, matrixBA, signsA, signsB, scalesA, scalesB ); + + if ( distSqr < minDistSqr ) { + EdgeEdge_SetNewMin( X, Y, Y, X ); + } + + if ( done ) + return; + + signsB.setY( -signsB.getY() ); + scalesB.setY( -scalesB.getY() ); + + distSqr = EdgeEdgeTest_0110( done, tA, tB, hA, hB, faceOffsetAB, faceOffsetBA, + matrixAB, matrixBA, signsA, signsB, scalesA, scalesB ); + + if ( distSqr < minDistSqr ) { + EdgeEdge_SetNewMin( X, Y, Y, X ); + } + + if ( done ) + return; + + signsA.setX( -signsA.getX() ); + scalesA.setX( -scalesA.getX() ); + + distSqr = EdgeEdgeTest_0110( done, tA, tB, hA, hB, faceOffsetAB, faceOffsetBA, + matrixAB, matrixBA, signsA, signsB, scalesA, scalesB ); + + if ( distSqr < minDistSqr ) { + EdgeEdge_SetNewMin( X, Y, Y, X ); + } + + if ( done ) + return; + + testOtherFaceDimA = 1; + testOtherFaceDimB = 1; + signsB.setY( -signsB.getY() ); + scalesB.setY( -scalesB.getY() ); + + distSqr = EdgeEdgeTest_1010( done, tA, tB, hA, hB, faceOffsetAB, faceOffsetBA, + matrixAB, matrixBA, signsA, signsB, scalesA, scalesB ); + + if ( distSqr < minDistSqr ) { + EdgeEdge_SetNewMin( Y, X, Y, X ); + } + + if ( done ) + return; + + signsA.setY( -signsA.getY() ); + scalesA.setY( -scalesA.getY() ); + + distSqr = EdgeEdgeTest_1010( done, tA, tB, hA, hB, faceOffsetAB, faceOffsetBA, + matrixAB, matrixBA, signsA, signsB, scalesA, scalesB ); + + if ( distSqr < minDistSqr ) { + EdgeEdge_SetNewMin( Y, X, Y, X ); + } + + if ( done ) + return; + + signsB.setY( -signsB.getY() ); + scalesB.setY( -scalesB.getY() ); + + distSqr = EdgeEdgeTest_1010( done, tA, tB, hA, hB, faceOffsetAB, faceOffsetBA, + matrixAB, matrixBA, signsA, signsB, scalesA, scalesB ); + + if ( distSqr < minDistSqr ) { + EdgeEdge_SetNewMin( Y, X, Y, X ); + } + + if ( done ) + return; + + signsA.setY( -signsA.getY() ); + scalesA.setY( -scalesA.getY() ); + + distSqr = EdgeEdgeTest_1010( done, tA, tB, hA, hB, faceOffsetAB, faceOffsetBA, + matrixAB, matrixBA, signsA, signsB, scalesA, scalesB ); + + if ( distSqr < minDistSqr ) { + EdgeEdge_SetNewMin( Y, X, Y, X ); + } +} + +float +boxBoxDistance( + Vector3& normal, + BoxPoint& boxPointA, + BoxPoint& boxPointB, + PE_REF(Box) boxA, const Transform3& transformA, + PE_REF(Box) boxB, const Transform3& transformB, + float distanceThreshold ) +{ + Matrix3 identity; + identity = Matrix3::identity(); + Vector3 ident[3]; + ident[0] = identity.getCol0(); + ident[1] = identity.getCol1(); + ident[2] = identity.getCol2(); + + // get relative transformations + + Transform3 transformAB, transformBA; + Matrix3 matrixAB, matrixBA; + Vector3 offsetAB, offsetBA; + + transformAB = orthoInverse(transformA) * transformB; + transformBA = orthoInverse(transformAB); + + matrixAB = transformAB.getUpper3x3(); + offsetAB = transformAB.getTranslation(); + matrixBA = transformBA.getUpper3x3(); + offsetBA = transformBA.getTranslation(); + + Matrix3 absMatrixAB = absPerElem(matrixAB); + Matrix3 absMatrixBA = absPerElem(matrixBA); + + // find separating axis with largest gap between projections + + BoxSepAxisType axisType; + Vector3 axisA(0.0f), axisB(0.0f); + float gap, maxGap; + int faceDimA = 0, faceDimB = 0, edgeDimA = 0, edgeDimB = 0; + + // face axes + + Vector3 gapsA = absPerElem(offsetAB) - boxA.half - absMatrixAB * boxB.half; + + AaxisTest(0,X,true); + AaxisTest(1,Y,false); + AaxisTest(2,Z,false); + + Vector3 gapsB = absPerElem(offsetBA) - boxB.half - absMatrixBA * boxA.half; + + BaxisTest(0,X); + BaxisTest(1,Y); + BaxisTest(2,Z); + + // cross product axes + + // ŠOĎ‚Ş‚O‚̂Ƃ«‚̑΍ô + absMatrixAB += Matrix3(1.0e-5f); + absMatrixBA += Matrix3(1.0e-5f); + + Matrix3 lsqrs, projOffset, projAhalf, projBhalf; + + lsqrs.setCol0( mulPerElem( matrixBA.getCol2(), matrixBA.getCol2() ) + + mulPerElem( matrixBA.getCol1(), matrixBA.getCol1() ) ); + lsqrs.setCol1( mulPerElem( matrixBA.getCol2(), matrixBA.getCol2() ) + + mulPerElem( matrixBA.getCol0(), matrixBA.getCol0() ) ); + lsqrs.setCol2( mulPerElem( matrixBA.getCol1(), matrixBA.getCol1() ) + + mulPerElem( matrixBA.getCol0(), matrixBA.getCol0() ) ); + + projOffset.setCol0(matrixBA.getCol1() * offsetAB.getZ() - matrixBA.getCol2() * offsetAB.getY()); + projOffset.setCol1(matrixBA.getCol2() * offsetAB.getX() - matrixBA.getCol0() * offsetAB.getZ()); + projOffset.setCol2(matrixBA.getCol0() * offsetAB.getY() - matrixBA.getCol1() * offsetAB.getX()); + + projAhalf.setCol0(absMatrixBA.getCol1() * boxA.half.getZ() + absMatrixBA.getCol2() * boxA.half.getY()); + projAhalf.setCol1(absMatrixBA.getCol2() * boxA.half.getX() + absMatrixBA.getCol0() * boxA.half.getZ()); + projAhalf.setCol2(absMatrixBA.getCol0() * boxA.half.getY() + absMatrixBA.getCol1() * boxA.half.getX()); + + projBhalf.setCol0(absMatrixAB.getCol1() * boxB.half.getZ() + absMatrixAB.getCol2() * boxB.half.getY()); + projBhalf.setCol1(absMatrixAB.getCol2() * boxB.half.getX() + absMatrixAB.getCol0() * boxB.half.getZ()); + projBhalf.setCol2(absMatrixAB.getCol0() * boxB.half.getY() + absMatrixAB.getCol1() * boxB.half.getX()); + + Matrix3 gapsAxB = absPerElem(projOffset) - projAhalf - transpose(projBhalf); + + CrossAxisTest(0,0,X); + CrossAxisTest(0,1,Y); + CrossAxisTest(0,2,Z); + CrossAxisTest(1,0,X); + CrossAxisTest(1,1,Y); + CrossAxisTest(1,2,Z); + CrossAxisTest(2,0,X); + CrossAxisTest(2,1,Y); + CrossAxisTest(2,2,Z); + + // need to pick the face on each box whose normal best matches the separating axis. + // will transform vectors to be in the coordinate system of this face to simplify things later. + // for this, a permutation matrix can be used, which the next section computes. + + int dimA[3], dimB[3]; + + if ( axisType == A_AXIS ) { + if ( dot(axisA,offsetAB) < 0.0f ) + axisA = -axisA; + axisB = matrixBA * -axisA; + + Vector3 absAxisB = Vector3(absPerElem(axisB)); + + if ( ( absAxisB[0] > absAxisB[1] ) && ( absAxisB[0] > absAxisB[2] ) ) + faceDimB = 0; + else if ( absAxisB[1] > absAxisB[2] ) + faceDimB = 1; + else + faceDimB = 2; + } else if ( axisType == B_AXIS ) { + if ( dot(axisB,offsetBA) < 0.0f ) + axisB = -axisB; + axisA = matrixAB * -axisB; + + Vector3 absAxisA = Vector3(absPerElem(axisA)); + + if ( ( absAxisA[0] > absAxisA[1] ) && ( absAxisA[0] > absAxisA[2] ) ) + faceDimA = 0; + else if ( absAxisA[1] > absAxisA[2] ) + faceDimA = 1; + else + faceDimA = 2; + } + + if ( axisType == CROSS_AXIS ) { + if ( dot(axisA,offsetAB) < 0.0f ) + axisA = -axisA; + axisB = matrixBA * -axisA; + + Vector3 absAxisA = Vector3(absPerElem(axisA)); + Vector3 absAxisB = Vector3(absPerElem(axisB)); + + dimA[1] = edgeDimA; + dimB[1] = edgeDimB; + + if ( edgeDimA == 0 ) { + if ( absAxisA[1] > absAxisA[2] ) { + dimA[0] = 2; + dimA[2] = 1; + } else { + dimA[0] = 1; + dimA[2] = 2; + } + } else if ( edgeDimA == 1 ) { + if ( absAxisA[2] > absAxisA[0] ) { + dimA[0] = 0; + dimA[2] = 2; + } else { + dimA[0] = 2; + dimA[2] = 0; + } + } else { + if ( absAxisA[0] > absAxisA[1] ) { + dimA[0] = 1; + dimA[2] = 0; + } else { + dimA[0] = 0; + dimA[2] = 1; + } + } + + if ( edgeDimB == 0 ) { + if ( absAxisB[1] > absAxisB[2] ) { + dimB[0] = 2; + dimB[2] = 1; + } else { + dimB[0] = 1; + dimB[2] = 2; + } + } else if ( edgeDimB == 1 ) { + if ( absAxisB[2] > absAxisB[0] ) { + dimB[0] = 0; + dimB[2] = 2; + } else { + dimB[0] = 2; + dimB[2] = 0; + } + } else { + if ( absAxisB[0] > absAxisB[1] ) { + dimB[0] = 1; + dimB[2] = 0; + } else { + dimB[0] = 0; + dimB[2] = 1; + } + } + } else { + dimA[2] = faceDimA; + dimA[0] = (faceDimA+1)%3; + dimA[1] = (faceDimA+2)%3; + dimB[2] = faceDimB; + dimB[0] = (faceDimB+1)%3; + dimB[1] = (faceDimB+2)%3; + } + + Matrix3 aperm_col, bperm_col; + + aperm_col.setCol0(ident[dimA[0]]); + aperm_col.setCol1(ident[dimA[1]]); + aperm_col.setCol2(ident[dimA[2]]); + + bperm_col.setCol0(ident[dimB[0]]); + bperm_col.setCol1(ident[dimB[1]]); + bperm_col.setCol2(ident[dimB[2]]); + + Matrix3 aperm_row, bperm_row; + + aperm_row = transpose(aperm_col); + bperm_row = transpose(bperm_col); + + // permute all box parameters to be in the face coordinate systems + + Matrix3 matrixAB_perm = aperm_row * matrixAB * bperm_col; + Matrix3 matrixBA_perm = transpose(matrixAB_perm); + + Vector3 offsetAB_perm, offsetBA_perm; + + offsetAB_perm = aperm_row * offsetAB; + offsetBA_perm = bperm_row * offsetBA; + + Vector3 halfA_perm, halfB_perm; + + halfA_perm = aperm_row * boxA.half; + halfB_perm = bperm_row * boxB.half; + + // compute the vector between the centers of each face, in each face's coordinate frame + + Vector3 signsA_perm, signsB_perm, scalesA_perm, scalesB_perm, faceOffsetAB_perm, faceOffsetBA_perm; + + signsA_perm = copySignPerElem(Vector3(1.0f),aperm_row * axisA); + signsB_perm = copySignPerElem(Vector3(1.0f),bperm_row * axisB); + scalesA_perm = mulPerElem( signsA_perm, halfA_perm ); + scalesB_perm = mulPerElem( signsB_perm, halfB_perm ); + + faceOffsetAB_perm = offsetAB_perm + matrixAB_perm.getCol2() * scalesB_perm.getZ(); + faceOffsetAB_perm.setZ( faceOffsetAB_perm.getZ() - scalesA_perm.getZ() ); + + faceOffsetBA_perm = offsetBA_perm + matrixBA_perm.getCol2() * scalesA_perm.getZ(); + faceOffsetBA_perm.setZ( faceOffsetBA_perm.getZ() - scalesB_perm.getZ() ); + + if ( maxGap < 0.0f ) { + // if boxes overlap, this will separate the faces for finding points of penetration. + + faceOffsetAB_perm -= aperm_row * axisA * maxGap * 1.01f; + faceOffsetBA_perm -= bperm_row * axisB * maxGap * 1.01f; + } + + // for each vertex/face or edge/edge pair of the two faces, find the closest points. + // + // these points each have an associated box feature (vertex, edge, or face). if each + // point is in the external Voronoi region of the other's feature, they are the + // closest points of the boxes, and the algorithm can exit. + // + // the feature pairs are arranged so that in the general case, the first test will + // succeed. degenerate cases (parallel faces) may require up to all tests in the + // worst case. + // + // if for some reason no case passes the Voronoi test, the features with the minimum + // distance are returned. + + Point3 localPointA_perm, localPointB_perm; + float minDistSqr; + bool done; + + Vector3 hA_perm( halfA_perm ), hB_perm( halfB_perm ); + + localPointA_perm.setZ( scalesA_perm.getZ() ); + localPointB_perm.setZ( scalesB_perm.getZ() ); + scalesA_perm.setZ(0.0f); + scalesB_perm.setZ(0.0f); + + int otherFaceDimA, otherFaceDimB; + FeatureType featureA, featureB; + + if ( axisType == CROSS_AXIS ) { + EdgeEdgeTests( done, minDistSqr, localPointA_perm, localPointB_perm, + otherFaceDimA, otherFaceDimB, featureA, featureB, + hA_perm, hB_perm, faceOffsetAB_perm, faceOffsetBA_perm, + matrixAB_perm, matrixBA_perm, signsA_perm, signsB_perm, + scalesA_perm, scalesB_perm, true ); + + if ( !done ) { + VertexBFaceATests( done, minDistSqr, localPointA_perm, localPointB_perm, + featureA, featureB, + hA_perm, faceOffsetAB_perm, faceOffsetBA_perm, + matrixAB_perm, matrixBA_perm, signsB_perm, scalesB_perm, false ); + + if ( !done ) { + VertexAFaceBTests( done, minDistSqr, localPointA_perm, localPointB_perm, + featureA, featureB, + hB_perm, faceOffsetAB_perm, faceOffsetBA_perm, + matrixAB_perm, matrixBA_perm, signsA_perm, scalesA_perm, false ); + } + } + } else if ( axisType == B_AXIS ) { + VertexAFaceBTests( done, minDistSqr, localPointA_perm, localPointB_perm, + featureA, featureB, + hB_perm, faceOffsetAB_perm, faceOffsetBA_perm, + matrixAB_perm, matrixBA_perm, signsA_perm, scalesA_perm, true ); + + if ( !done ) { + VertexBFaceATests( done, minDistSqr, localPointA_perm, localPointB_perm, + featureA, featureB, + hA_perm, faceOffsetAB_perm, faceOffsetBA_perm, + matrixAB_perm, matrixBA_perm, signsB_perm, scalesB_perm, false ); + + if ( !done ) { + EdgeEdgeTests( done, minDistSqr, localPointA_perm, localPointB_perm, + otherFaceDimA, otherFaceDimB, featureA, featureB, + hA_perm, hB_perm, faceOffsetAB_perm, faceOffsetBA_perm, + matrixAB_perm, matrixBA_perm, signsA_perm, signsB_perm, + scalesA_perm, scalesB_perm, false ); + } + } + } else { + VertexBFaceATests( done, minDistSqr, localPointA_perm, localPointB_perm, + featureA, featureB, + hA_perm, faceOffsetAB_perm, faceOffsetBA_perm, + matrixAB_perm, matrixBA_perm, signsB_perm, scalesB_perm, true ); + + if ( !done ) { + VertexAFaceBTests( done, minDistSqr, localPointA_perm, localPointB_perm, + featureA, featureB, + hB_perm, faceOffsetAB_perm, faceOffsetBA_perm, + matrixAB_perm, matrixBA_perm, signsA_perm, scalesA_perm, false ); + + if ( !done ) { + EdgeEdgeTests( done, minDistSqr, localPointA_perm, localPointB_perm, + otherFaceDimA, otherFaceDimB, featureA, featureB, + hA_perm, hB_perm, faceOffsetAB_perm, faceOffsetBA_perm, + matrixAB_perm, matrixBA_perm, signsA_perm, signsB_perm, + scalesA_perm, scalesB_perm, false ); + } + } + } + + // convert local points from face-local to box-local coordinate system + + boxPointA.localPoint = Point3( aperm_col * Vector3( localPointA_perm ) ); + boxPointB.localPoint = Point3( bperm_col * Vector3( localPointB_perm ) ); + + // find which features of the boxes are involved. + // the only feature pairs which occur in this function are VF, FV, and EE, even though the + // closest points might actually lie on sub-features, as in a VF contact might be used for + // what's actually a VV contact. this means some feature pairs could possibly seem distinct + // from others, although their contact positions are the same. don't know yet whether this + // matters. + + int sA[3], sB[3]; + + sA[0] = boxPointA.localPoint.getX() > 0.0f; + sA[1] = boxPointA.localPoint.getY() > 0.0f; + sA[2] = boxPointA.localPoint.getZ() > 0.0f; + + sB[0] = boxPointB.localPoint.getX() > 0.0f; + sB[1] = boxPointB.localPoint.getY() > 0.0f; + sB[2] = boxPointB.localPoint.getZ() > 0.0f; + + if ( featureA == F ) { + boxPointA.setFaceFeature( dimA[2], sA[dimA[2]] ); + } else if ( featureA == E ) { + boxPointA.setEdgeFeature( dimA[2], sA[dimA[2]], dimA[otherFaceDimA], sA[dimA[otherFaceDimA]] ); + } else { + boxPointA.setVertexFeature( sA[0], sA[1], sA[2] ); + } + + if ( featureB == F ) { + boxPointB.setFaceFeature( dimB[2], sB[dimB[2]] ); + } else if ( featureB == E ) { + boxPointB.setEdgeFeature( dimB[2], sB[dimB[2]], dimB[otherFaceDimB], sB[dimB[otherFaceDimB]] ); + } else { + boxPointB.setVertexFeature( sB[0], sB[1], sB[2] ); + } + + normal = transformA * axisA; + + if ( maxGap < 0.0f ) { + return (maxGap); + } else { + return (sqrtf( minDistSqr )); + } +} diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/boxBoxDistance.h b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/boxBoxDistance.h new file mode 100644 index 000000000..c58e257c0 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/boxBoxDistance.h @@ -0,0 +1,66 @@ +/* + Copyright (C) 2006, 2008 Sony Computer Entertainment Inc. + All rights reserved. + +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 __BOXBOXDISTANCE_H__ +#define __BOXBOXDISTANCE_H__ + + +#include "Box.h" + +using namespace Vectormath::Aos; + +//--------------------------------------------------------------------------- +// boxBoxDistance: +// +// description: +// this computes info that can be used for the collision response of two boxes. when the boxes +// do not overlap, the points are set to the closest points of the boxes, and a positive +// distance between them is returned. if the boxes do overlap, a negative distance is returned +// and the points are set to two points that would touch after the boxes are translated apart. +// the contact normal gives the direction to repel or separate the boxes when they touch or +// overlap (it's being approximated here as one of the 15 "separating axis" directions). +// +// returns: +// positive or negative distance between two boxes. +// +// args: +// Vector3& normal: set to a unit contact normal pointing from box A to box B. +// +// BoxPoint& boxPointA, BoxPoint& boxPointB: +// set to a closest point or point of penetration on each box. +// +// Box boxA, Box boxB: +// boxes, represented as 3 half-widths +// +// const Transform3& transformA, const Transform3& transformB: +// box transformations, in world coordinates +// +// float distanceThreshold: +// the algorithm will exit early if it finds that the boxes are more distant than this +// threshold, and not compute a contact normal or points. if this distance returned +// exceeds the threshold, all the other output data may not have been computed. by +// default, this is set to MAX_FLOAT so it will have no effect. +// +//--------------------------------------------------------------------------- + +float +boxBoxDistance(Vector3& normal, BoxPoint& boxPointA, BoxPoint& boxPointB, + PE_REF(Box) boxA, const Transform3 & transformA, PE_REF(Box) boxB, + const Transform3 & transformB, + float distanceThreshold = FLT_MAX ); + +#endif /* __BOXBOXDISTANCE_H__ */ diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/readme.txt b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/readme.txt new file mode 100644 index 000000000..5b4a90705 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/readme.txt @@ -0,0 +1 @@ +Empty placeholder for future Libspe2 SPU task diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuSampleTask/SpuSampleTask.cpp b/Engine/lib/bullet/src/BulletMultiThreaded/SpuSampleTask/SpuSampleTask.cpp new file mode 100644 index 000000000..fe6195557 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuSampleTask/SpuSampleTask.cpp @@ -0,0 +1,214 @@ +/* +Bullet Continuous Collision Detection and Physics Library, Copyright (c) 2007 Erwin Coumans + +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 "SpuSampleTask.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include "../PlatformDefinitions.h" +#include "../SpuFakeDma.h" +#include "LinearMath/btMinMax.h" + +#ifdef __SPU__ +#include +#else +#include +#define spu_printf printf +#endif + +#define MAX_NUM_BODIES 8192 + +struct SampleTask_LocalStoreMemory +{ + ATTRIBUTE_ALIGNED16(char gLocalRigidBody [sizeof(btRigidBody)+16]); + ATTRIBUTE_ALIGNED16(void* gPointerArray[MAX_NUM_BODIES]); + +}; + + + + +//-- MAIN METHOD +void processSampleTask(void* userPtr, void* lsMemory) +{ + // BT_PROFILE("processSampleTask"); + + SampleTask_LocalStoreMemory* localMemory = (SampleTask_LocalStoreMemory*)lsMemory; + + SpuSampleTaskDesc* taskDescPtr = (SpuSampleTaskDesc*)userPtr; + SpuSampleTaskDesc& taskDesc = *taskDescPtr; + + switch (taskDesc.m_sampleCommand) + { + case CMD_SAMPLE_INTEGRATE_BODIES: + { + btTransform predictedTrans; + btCollisionObject** eaPtr = (btCollisionObject**)taskDesc.m_mainMemoryPtr; + + int batchSize = taskDesc.m_sampleValue; + if (batchSize>MAX_NUM_BODIES) + { + spu_printf("SPU Error: exceed number of bodies, see MAX_NUM_BODIES in SpuSampleTask.cpp\n"); + break; + } + int dmaArraySize = batchSize*sizeof(void*); + + uint64_t ppuArrayAddress = reinterpret_cast(eaPtr); + + // spu_printf("array location is at %llx, batchSize = %d, DMA size = %d\n",ppuArrayAddress,batchSize,dmaArraySize); + + if (dmaArraySize>=16) + { + cellDmaLargeGet((void*)&localMemory->gPointerArray[0], ppuArrayAddress , dmaArraySize, DMA_TAG(1), 0, 0); + cellDmaWaitTagStatusAll(DMA_MASK(1)); + } else + { + stallingUnalignedDmaSmallGet((void*)&localMemory->gPointerArray[0], ppuArrayAddress , dmaArraySize); + } + + + for ( int i=0;igLocalRigidBody[0]; + void* shortAdd = localMemory->gPointerArray[i]; + uint64_t ppuRigidBodyAddress = reinterpret_cast(shortAdd); + + // spu_printf("cellDmaGet at CMD_SAMPLE_INTEGRATE_BODIES from %llx to %llx\n",ppuRigidBodyAddress,localPtr); + + int dmaBodySize = sizeof(btRigidBody); + + cellDmaGet((void*)localPtr, ppuRigidBodyAddress , dmaBodySize, DMA_TAG(1), 0, 0); + cellDmaWaitTagStatusAll(DMA_MASK(1)); + + + float timeStep = 1.f/60.f; + + btRigidBody* body = (btRigidBody*) localPtr;//btRigidBody::upcast(colObj); + if (body) + { + if (body->isActive() && (!body->isStaticOrKinematicObject())) + { + body->predictIntegratedTransform(timeStep, predictedTrans); + body->proceedToTransform( predictedTrans); + void* ptr = (void*)localPtr; + // spu_printf("cellDmaLargePut from %llx to LS %llx\n",ptr,ppuRigidBodyAddress); + + cellDmaLargePut(ptr, ppuRigidBodyAddress , dmaBodySize, DMA_TAG(1), 0, 0); + cellDmaWaitTagStatusAll(DMA_MASK(1)); + + } + } + + } + break; + } + + + case CMD_SAMPLE_PREDICT_MOTION_BODIES: + { + btTransform predictedTrans; + btCollisionObject** eaPtr = (btCollisionObject**)taskDesc.m_mainMemoryPtr; + + int batchSize = taskDesc.m_sampleValue; + int dmaArraySize = batchSize*sizeof(void*); + + if (batchSize>MAX_NUM_BODIES) + { + spu_printf("SPU Error: exceed number of bodies, see MAX_NUM_BODIES in SpuSampleTask.cpp\n"); + break; + } + + uint64_t ppuArrayAddress = reinterpret_cast(eaPtr); + + // spu_printf("array location is at %llx, batchSize = %d, DMA size = %d\n",ppuArrayAddress,batchSize,dmaArraySize); + + if (dmaArraySize>=16) + { + cellDmaLargeGet((void*)&localMemory->gPointerArray[0], ppuArrayAddress , dmaArraySize, DMA_TAG(1), 0, 0); + cellDmaWaitTagStatusAll(DMA_MASK(1)); + } else + { + stallingUnalignedDmaSmallGet((void*)&localMemory->gPointerArray[0], ppuArrayAddress , dmaArraySize); + } + + + for ( int i=0;igLocalRigidBody[0]; + void* shortAdd = localMemory->gPointerArray[i]; + uint64_t ppuRigidBodyAddress = reinterpret_cast(shortAdd); + + // spu_printf("cellDmaGet at CMD_SAMPLE_INTEGRATE_BODIES from %llx to %llx\n",ppuRigidBodyAddress,localPtr); + + int dmaBodySize = sizeof(btRigidBody); + + cellDmaGet((void*)localPtr, ppuRigidBodyAddress , dmaBodySize, DMA_TAG(1), 0, 0); + cellDmaWaitTagStatusAll(DMA_MASK(1)); + + + float timeStep = 1.f/60.f; + + btRigidBody* body = (btRigidBody*) localPtr;//btRigidBody::upcast(colObj); + if (body) + { + if (!body->isStaticOrKinematicObject()) + { + if (body->isActive()) + { + body->integrateVelocities( timeStep); + //damping + body->applyDamping(timeStep); + + body->predictIntegratedTransform(timeStep,body->getInterpolationWorldTransform()); + + void* ptr = (void*)localPtr; + cellDmaLargePut(ptr, ppuRigidBodyAddress , dmaBodySize, DMA_TAG(1), 0, 0); + cellDmaWaitTagStatusAll(DMA_MASK(1)); + } + } + } + + } + break; + } + + + + default: + { + + } + }; +} + + +#if defined(__CELLOS_LV2__) || defined (LIBSPE2) + +ATTRIBUTE_ALIGNED16(SampleTask_LocalStoreMemory gLocalStoreMemory); + +void* createSampleLocalStoreMemory() +{ + return &gLocalStoreMemory; +} +#else +void* createSampleLocalStoreMemory() +{ + return new SampleTask_LocalStoreMemory; +}; + +#endif diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuSampleTask/SpuSampleTask.h b/Engine/lib/bullet/src/BulletMultiThreaded/SpuSampleTask/SpuSampleTask.h new file mode 100644 index 000000000..c8ebdfd62 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuSampleTask/SpuSampleTask.h @@ -0,0 +1,54 @@ +/* +Bullet Continuous Collision Detection and Physics Library, Copyright (c) 2007 Erwin Coumans + +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 SPU_SAMPLE_TASK_H +#define SPU_SAMPLE_TASK_H + +#include "../PlatformDefinitions.h" +#include "LinearMath/btScalar.h" +#include "LinearMath/btVector3.h" +#include "LinearMath/btMatrix3x3.h" + +#include "LinearMath/btAlignedAllocator.h" + + +enum +{ + CMD_SAMPLE_INTEGRATE_BODIES = 1, + CMD_SAMPLE_PREDICT_MOTION_BODIES +}; + + + +ATTRIBUTE_ALIGNED16(struct) SpuSampleTaskDesc +{ + BT_DECLARE_ALIGNED_ALLOCATOR(); + + uint32_t m_sampleCommand; + uint32_t m_taskId; + + uint64_t m_mainMemoryPtr; + int m_sampleValue; + + +}; + + +void processSampleTask(void* userPtr, void* lsMemory); +void* createSampleLocalStoreMemory(); + + +#endif //SPU_SAMPLE_TASK_H + diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuSampleTask/readme.txt b/Engine/lib/bullet/src/BulletMultiThreaded/SpuSampleTask/readme.txt new file mode 100644 index 000000000..5b4a90705 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuSampleTask/readme.txt @@ -0,0 +1 @@ +Empty placeholder for future Libspe2 SPU task diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuSampleTaskProcess.cpp b/Engine/lib/bullet/src/BulletMultiThreaded/SpuSampleTaskProcess.cpp new file mode 100644 index 000000000..11cb9e7c3 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuSampleTaskProcess.cpp @@ -0,0 +1,222 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.com + +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 __CELLOS_LV2__ 1 + +#define USE_SAMPLE_PROCESS 1 +#ifdef USE_SAMPLE_PROCESS + + +#include "SpuSampleTaskProcess.h" +#include + +#ifdef __SPU__ + + + +void SampleThreadFunc(void* userPtr,void* lsMemory) +{ + //do nothing + printf("hello world\n"); +} + + +void* SamplelsMemoryFunc() +{ + //don't create local store memory, just return 0 + return 0; +} + + +#else + + +#include "btThreadSupportInterface.h" + +//# include "SPUAssert.h" +#include + + + +extern "C" { + extern char SPU_SAMPLE_ELF_SYMBOL[]; +} + + + + + +SpuSampleTaskProcess::SpuSampleTaskProcess(btThreadSupportInterface* threadInterface, int maxNumOutstandingTasks) +:m_threadInterface(threadInterface), +m_maxNumOutstandingTasks(maxNumOutstandingTasks) +{ + + m_taskBusy.resize(m_maxNumOutstandingTasks); + m_spuSampleTaskDesc.resize(m_maxNumOutstandingTasks); + + for (int i = 0; i < m_maxNumOutstandingTasks; i++) + { + m_taskBusy[i] = false; + } + m_numBusyTasks = 0; + m_currentTask = 0; + + m_initialized = false; + + m_threadInterface->startSPU(); + + +} + +SpuSampleTaskProcess::~SpuSampleTaskProcess() +{ + m_threadInterface->stopSPU(); + +} + + + +void SpuSampleTaskProcess::initialize() +{ +#ifdef DEBUG_SPU_TASK_SCHEDULING + printf("SpuSampleTaskProcess::initialize()\n"); +#endif //DEBUG_SPU_TASK_SCHEDULING + + for (int i = 0; i < m_maxNumOutstandingTasks; i++) + { + m_taskBusy[i] = false; + } + m_numBusyTasks = 0; + m_currentTask = 0; + m_initialized = true; + +} + + +void SpuSampleTaskProcess::issueTask(void* sampleMainMemPtr,int sampleValue,int sampleCommand) +{ + +#ifdef DEBUG_SPU_TASK_SCHEDULING + printf("SpuSampleTaskProcess::issueTask (m_currentTask= %d\)n", m_currentTask); +#endif //DEBUG_SPU_TASK_SCHEDULING + + m_taskBusy[m_currentTask] = true; + m_numBusyTasks++; + + SpuSampleTaskDesc& taskDesc = m_spuSampleTaskDesc[m_currentTask]; + { + // send task description in event message + // no error checking here... + // but, currently, event queue can be no larger than NUM_WORKUNIT_TASKS. + + taskDesc.m_mainMemoryPtr = reinterpret_cast(sampleMainMemPtr); + taskDesc.m_sampleValue = sampleValue; + taskDesc.m_sampleCommand = sampleCommand; + + //some bookkeeping to recognize finished tasks + taskDesc.m_taskId = m_currentTask; + } + + + m_threadInterface->sendRequest(1, (ppu_address_t) &taskDesc, m_currentTask); + + // if all tasks busy, wait for spu event to clear the task. + + if (m_numBusyTasks >= m_maxNumOutstandingTasks) + { + unsigned int taskId; + unsigned int outputSize; + + for (int i=0;iwaitForResponse(&taskId, &outputSize); + + //printf("PPU: after issue, received event: %u %d\n", taskId, outputSize); + + postProcess(taskId, outputSize); + + m_taskBusy[taskId] = false; + + m_numBusyTasks--; + } + + // find new task buffer + for (int i = 0; i < m_maxNumOutstandingTasks; i++) + { + if (!m_taskBusy[i]) + { + m_currentTask = i; + break; + } + } +} + + +///Optional PPU-size post processing for each task +void SpuSampleTaskProcess::postProcess(int taskId, int outputSize) +{ + +} + + +void SpuSampleTaskProcess::flush() +{ +#ifdef DEBUG_SPU_TASK_SCHEDULING + printf("\nSpuCollisionTaskProcess::flush()\n"); +#endif //DEBUG_SPU_TASK_SCHEDULING + + + // all tasks are issued, wait for all tasks to be complete + while(m_numBusyTasks > 0) + { +// Consolidating SPU code + unsigned int taskId; + unsigned int outputSize; + + for (int i=0;iwaitForResponse(&taskId, &outputSize); + } + + //printf("PPU: flushing, received event: %u %d\n", taskId, outputSize); + + postProcess(taskId, outputSize); + + m_taskBusy[taskId] = false; + + m_numBusyTasks--; + } + + +} + +#endif + + +#endif //USE_SAMPLE_PROCESS diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuSampleTaskProcess.h b/Engine/lib/bullet/src/BulletMultiThreaded/SpuSampleTaskProcess.h new file mode 100644 index 000000000..d733a9a85 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuSampleTaskProcess.h @@ -0,0 +1,153 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.com + +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 SPU_SAMPLE_TASK_PROCESS_H +#define SPU_SAMPLE_TASK_PROCESS_H + +#include + + +#include "PlatformDefinitions.h" + +#include + +#include "LinearMath/btAlignedObjectArray.h" + + +#include "SpuSampleTask/SpuSampleTask.h" + + +//just add your commands here, try to keep them globally unique for debugging purposes +#define CMD_SAMPLE_TASK_COMMAND 10 + + + +/// SpuSampleTaskProcess handles SPU processing of collision pairs. +/// When PPU issues a task, it will look for completed task buffers +/// PPU will do postprocessing, dependent on workunit output (not likely) +class SpuSampleTaskProcess +{ + // track task buffers that are being used, and total busy tasks + btAlignedObjectArray m_taskBusy; + btAlignedObjectArraym_spuSampleTaskDesc; + + int m_numBusyTasks; + + // the current task and the current entry to insert a new work unit + int m_currentTask; + + bool m_initialized; + + void postProcess(int taskId, int outputSize); + + class btThreadSupportInterface* m_threadInterface; + + int m_maxNumOutstandingTasks; + + + +public: + SpuSampleTaskProcess(btThreadSupportInterface* threadInterface, int maxNumOutstandingTasks); + + ~SpuSampleTaskProcess(); + + ///call initialize in the beginning of the frame, before addCollisionPairToTask + void initialize(); + + void issueTask(void* sampleMainMemPtr,int sampleValue,int sampleCommand); + + ///call flush to submit potential outstanding work to SPUs and wait for all involved SPUs to be finished + void flush(); +}; + + +#if defined(USE_LIBSPE2) && defined(__SPU__) +////////////////////MAIN///////////////////////////// +#include "../SpuLibspe2Support.h" +#include +#include +#include + +void * SamplelsMemoryFunc(); +void SampleThreadFunc(void* userPtr,void* lsMemory); + +//#define DEBUG_LIBSPE2_MAINLOOP + +int main(unsigned long long speid, addr64 argp, addr64 envp) +{ + printf("SPU is up \n"); + + ATTRIBUTE_ALIGNED128(btSpuStatus status); + ATTRIBUTE_ALIGNED16( SpuSampleTaskDesc taskDesc ) ; + unsigned int received_message = Spu_Mailbox_Event_Nothing; + bool shutdown = false; + + cellDmaGet(&status, argp.ull, sizeof(btSpuStatus), DMA_TAG(3), 0, 0); + cellDmaWaitTagStatusAll(DMA_MASK(3)); + + status.m_status = Spu_Status_Free; + status.m_lsMemory.p = SamplelsMemoryFunc(); + + cellDmaLargePut(&status, argp.ull, sizeof(btSpuStatus), DMA_TAG(3), 0, 0); + cellDmaWaitTagStatusAll(DMA_MASK(3)); + + + while (!shutdown) + { + received_message = spu_read_in_mbox(); + + + + switch(received_message) + { + case Spu_Mailbox_Event_Shutdown: + shutdown = true; + break; + case Spu_Mailbox_Event_Task: + // refresh the status +#ifdef DEBUG_LIBSPE2_MAINLOOP + printf("SPU recieved Task \n"); +#endif //DEBUG_LIBSPE2_MAINLOOP + cellDmaGet(&status, argp.ull, sizeof(btSpuStatus), DMA_TAG(3), 0, 0); + cellDmaWaitTagStatusAll(DMA_MASK(3)); + + btAssert(status.m_status==Spu_Status_Occupied); + + cellDmaGet(&taskDesc, status.m_taskDesc.p, sizeof(SpuSampleTaskDesc), DMA_TAG(3), 0, 0); + cellDmaWaitTagStatusAll(DMA_MASK(3)); + + SampleThreadFunc((void*)&taskDesc, reinterpret_cast (taskDesc.m_mainMemoryPtr) ); + break; + case Spu_Mailbox_Event_Nothing: + default: + break; + } + + // set to status free and wait for next task + status.m_status = Spu_Status_Free; + cellDmaLargePut(&status, argp.ull, sizeof(btSpuStatus), DMA_TAG(3), 0, 0); + cellDmaWaitTagStatusAll(DMA_MASK(3)); + + + } + return 0; +} +////////////////////////////////////////////////////// +#endif + + + +#endif // SPU_SAMPLE_TASK_PROCESS_H + diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuSync.h b/Engine/lib/bullet/src/BulletMultiThreaded/SpuSync.h new file mode 100644 index 000000000..b90d0fcbf --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuSync.h @@ -0,0 +1,148 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2007 Starbreeze Studios + +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. + +Written by: Marten Svanfeldt +*/ + +#ifndef SPU_SYNC_H +#define SPU_SYNC_H + + +#include "PlatformDefinitions.h" + + +#if defined(WIN32) + +#define WIN32_LEAN_AND_MEAN +#ifdef _XBOX +#include +#else +#include +#endif + +///The btSpinlock is a structure to allow multi-platform synchronization. This allows to port the SPU tasks to other platforms. +class btSpinlock +{ +public: + //typedef volatile LONG SpinVariable; + typedef CRITICAL_SECTION SpinVariable; + + btSpinlock (SpinVariable* var) + : spinVariable (var) + {} + + void Init () + { + //*spinVariable = 0; + InitializeCriticalSection(spinVariable); + } + + void Lock () + { + EnterCriticalSection(spinVariable); + } + + void Unlock () + { + LeaveCriticalSection(spinVariable); + } + +private: + SpinVariable* spinVariable; +}; + + +#elif defined (__CELLOS_LV2__) + +//#include +#include + +///The btSpinlock is a structure to allow multi-platform synchronization. This allows to port the SPU tasks to other platforms. +class btSpinlock +{ +public: + typedef CellSyncMutex SpinVariable; + + btSpinlock (SpinVariable* var) + : spinVariable (var) + {} + + void Init () + { +#ifndef __SPU__ + //*spinVariable = 1; + cellSyncMutexInitialize(spinVariable); +#endif + } + + + + void Lock () + { +#ifdef __SPU__ + // lock semaphore + /*while (cellAtomicTestAndDecr32(atomic_buf, (uint64_t)spinVariable) == 0) + { + + };*/ + cellSyncMutexLock((uint64_t)spinVariable); +#endif + } + + void Unlock () + { +#ifdef __SPU__ + //cellAtomicIncr32(atomic_buf, (uint64_t)spinVariable); + cellSyncMutexUnlock((uint64_t)spinVariable); +#endif + } + + +private: + SpinVariable* spinVariable; + ATTRIBUTE_ALIGNED128(uint32_t atomic_buf[32]); +}; + +#else +//create a dummy implementation (without any locking) useful for serial processing +class btSpinlock +{ +public: + typedef int SpinVariable; + + btSpinlock (SpinVariable* var) + : spinVariable (var) + {} + + void Init () + { + } + + void Lock () + { + } + + void Unlock () + { + } + +private: + SpinVariable* spinVariable; +}; + + +#endif + + +#endif diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/Win32ThreadSupport.cpp b/Engine/lib/bullet/src/BulletMultiThreaded/Win32ThreadSupport.cpp new file mode 100644 index 000000000..ae3e68c0b --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/Win32ThreadSupport.cpp @@ -0,0 +1,262 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.com + +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 "Win32ThreadSupport.h" + +#ifdef USE_WIN32_THREADING + +#include + +#include "SpuCollisionTaskProcess.h" + +#include "SpuNarrowPhaseCollisionTask/SpuGatheringCollisionTask.h" + + + +///The number of threads should be equal to the number of available cores +///@todo: each worker should be linked to a single core, using SetThreadIdealProcessor. + +///Win32ThreadSupport helps to initialize/shutdown libspe2, start/stop SPU tasks and communication +///Setup and initialize SPU/CELL/Libspe2 +Win32ThreadSupport::Win32ThreadSupport(const Win32ThreadConstructionInfo & threadConstructionInfo) +{ + m_maxNumTasks = threadConstructionInfo.m_numThreads; + startThreads(threadConstructionInfo); +} + +///cleanup/shutdown Libspe2 +Win32ThreadSupport::~Win32ThreadSupport() +{ + stopSPU(); +} + + + + +#include + +DWORD WINAPI Thread_no_1( LPVOID lpParam ) +{ + + Win32ThreadSupport::btSpuStatus* status = (Win32ThreadSupport::btSpuStatus*)lpParam; + + + while (1) + { + WaitForSingleObject(status->m_eventStartHandle,INFINITE); + + void* userPtr = status->m_userPtr; + + if (userPtr) + { + btAssert(status->m_status); + status->m_userThreadFunc(userPtr,status->m_lsMemory); + status->m_status = 2; + SetEvent(status->m_eventCompletetHandle); + } else + { + //exit Thread + status->m_status = 3; + SetEvent(status->m_eventCompletetHandle); + printf("Thread with taskId %i with handle %p exiting\n",status->m_taskId, status->m_threadHandle); + break; + } + + } + + printf("Thread TERMINATED\n"); + return 0; + +} + +///send messages to SPUs +void Win32ThreadSupport::sendRequest(uint32_t uiCommand, ppu_address_t uiArgument0, uint32_t taskId) +{ + /// gMidphaseSPU.sendRequest(CMD_GATHER_AND_PROCESS_PAIRLIST, (ppu_address_t) &taskDesc); + + ///we should spawn an SPU task here, and in 'waitForResponse' it should wait for response of the (one of) the first tasks that finished + + + + switch (uiCommand) + { + case CMD_GATHER_AND_PROCESS_PAIRLIST: + { + + +//#define SINGLE_THREADED 1 +#ifdef SINGLE_THREADED + + btSpuStatus& spuStatus = m_activeSpuStatus[0]; + spuStatus.m_userPtr=(void*)uiArgument0; + spuStatus.m_userThreadFunc(spuStatus.m_userPtr,spuStatus.m_lsMemory); + HANDLE handle =0; +#else + + + btSpuStatus& spuStatus = m_activeSpuStatus[taskId]; + btAssert(taskId>=0); + btAssert(int(taskId) 1); + spuStatus.m_status = 0; + + ///need to find an active spu + btAssert(last>=0); + +#else + last=0; + btSpuStatus& spuStatus = m_activeSpuStatus[last]; +#endif //SINGLE_THREADED + + + + *puiArgument0 = spuStatus.m_taskId; + *puiArgument1 = spuStatus.m_status; + + +} + + + +void Win32ThreadSupport::startThreads(const Win32ThreadConstructionInfo& threadConstructionInfo) +{ + + m_activeSpuStatus.resize(threadConstructionInfo.m_numThreads); + m_completeHandles.resize(threadConstructionInfo.m_numThreads); + + m_maxNumTasks = threadConstructionInfo.m_numThreads; + + for (int i=0;i0) + { + WaitForSingleObject(spuStatus.m_eventCompletetHandle, INFINITE); + } + + + spuStatus.m_userPtr = 0; + SetEvent(spuStatus.m_eventStartHandle); + WaitForSingleObject(spuStatus.m_eventCompletetHandle, INFINITE); + + CloseHandle(spuStatus.m_eventCompletetHandle); + CloseHandle(spuStatus.m_eventStartHandle); + CloseHandle(spuStatus.m_threadHandle); + } + + m_activeSpuStatus.clear(); + m_completeHandles.clear(); + +} + +#endif //USE_WIN32_THREADING diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/Win32ThreadSupport.h b/Engine/lib/bullet/src/BulletMultiThreaded/Win32ThreadSupport.h new file mode 100644 index 000000000..c61ad901c --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/Win32ThreadSupport.h @@ -0,0 +1,132 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.com + +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 "LinearMath/btScalar.h" +#include "PlatformDefinitions.h" + +#ifdef USE_WIN32_THREADING //platform specific defines are defined in PlatformDefinitions.h + +#ifndef WIN32_THREAD_SUPPORT_H +#define WIN32_THREAD_SUPPORT_H + +#include "LinearMath/btAlignedObjectArray.h" + +#include "btThreadSupportInterface.h" + + +typedef void (*Win32ThreadFunc)(void* userPtr,void* lsMemory); +typedef void* (*Win32lsMemorySetupFunc)(); + + + + + + +///Win32ThreadSupport helps to initialize/shutdown libspe2, start/stop SPU tasks and communication +class Win32ThreadSupport : public btThreadSupportInterface +{ +public: + ///placeholder, until libspe2 support is there + struct btSpuStatus + { + uint32_t m_taskId; + uint32_t m_commandId; + uint32_t m_status; + + Win32ThreadFunc m_userThreadFunc; + void* m_userPtr; //for taskDesc etc + void* m_lsMemory; //initialized using Win32LocalStoreMemorySetupFunc + + void* m_threadHandle; //this one is calling 'Win32ThreadFunc' + + void* m_eventStartHandle; + char m_eventStartHandleName[32]; + + void* m_eventCompletetHandle; + char m_eventCompletetHandleName[32]; + + + }; +private: + + btAlignedObjectArray m_activeSpuStatus; + btAlignedObjectArray m_completeHandles; + + int m_maxNumTasks; +public: + ///Setup and initialize SPU/CELL/Libspe2 + + struct Win32ThreadConstructionInfo + { + Win32ThreadConstructionInfo(char* uniqueName, + Win32ThreadFunc userThreadFunc, + Win32lsMemorySetupFunc lsMemoryFunc, + int numThreads=1, + int threadStackSize=65535 + ) + :m_uniqueName(uniqueName), + m_userThreadFunc(userThreadFunc), + m_lsMemoryFunc(lsMemoryFunc), + m_numThreads(numThreads), + m_threadStackSize(threadStackSize) + { + + } + + char* m_uniqueName; + Win32ThreadFunc m_userThreadFunc; + Win32lsMemorySetupFunc m_lsMemoryFunc; + int m_numThreads; + int m_threadStackSize; + + }; + + + + Win32ThreadSupport(const Win32ThreadConstructionInfo& threadConstructionInfo); + +///cleanup/shutdown Libspe2 + virtual ~Win32ThreadSupport(); + + void startThreads(const Win32ThreadConstructionInfo& threadInfo); + + +///send messages to SPUs + virtual void sendRequest(uint32_t uiCommand, ppu_address_t uiArgument0, uint32_t uiArgument1); + +///check for messages from SPUs + virtual void waitForResponse(unsigned int *puiArgument0, unsigned int *puiArgument1); + +///start the spus (can be called at the beginning of each frame, to make sure that the right SPU program is loaded) + virtual void startSPU(); + +///tell the task scheduler we are done with the SPU tasks + virtual void stopSPU(); + + virtual void setNumTasks(int numTasks) + { + m_maxNumTasks = numTasks; + } + + virtual int getNumTasks() const + { + return m_maxNumTasks; + } + +}; + +#endif //WIN32_THREAD_SUPPORT_H + +#endif //USE_WIN32_THREADING diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/btGpu3DGridBroadphase.cpp b/Engine/lib/bullet/src/BulletMultiThreaded/btGpu3DGridBroadphase.cpp new file mode 100644 index 000000000..84a5e59f0 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/btGpu3DGridBroadphase.cpp @@ -0,0 +1,590 @@ +/* +Bullet Continuous Collision Detection and Physics Library, http://bulletphysics.org +Copyright (C) 2006, 2009 Sony Computer Entertainment Inc. + +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. +*/ + +///The 3 following lines include the CPU implementation of the kernels, keep them in this order. +#include "BulletMultiThreaded/btGpuDefines.h" +#include "BulletMultiThreaded/btGpuUtilsSharedDefs.h" +#include "BulletMultiThreaded/btGpuUtilsSharedCode.h" + + + +#include "LinearMath/btAlignedAllocator.h" +#include "LinearMath/btQuickprof.h" +#include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h" + + + +#include "btGpuDefines.h" +#include "btGpuUtilsSharedDefs.h" + +#include "btGpu3DGridBroadphaseSharedDefs.h" + +#include "btGpu3DGridBroadphase.h" +#include //for memset + + +#include + + + +static bt3DGridBroadphaseParams s3DGridBroadphaseParams; + + + +btGpu3DGridBroadphase::btGpu3DGridBroadphase( const btVector3& worldAabbMin,const btVector3& worldAabbMax, + int gridSizeX, int gridSizeY, int gridSizeZ, + int maxSmallProxies, int maxLargeProxies, int maxPairsPerBody, + int maxBodiesPerCell, + btScalar cellFactorAABB) : + btSimpleBroadphase(maxSmallProxies, +// new (btAlignedAlloc(sizeof(btSortedOverlappingPairCache),16)) btSortedOverlappingPairCache), + new (btAlignedAlloc(sizeof(btHashedOverlappingPairCache),16)) btHashedOverlappingPairCache), + m_bInitialized(false), + m_numBodies(0) +{ + _initialize(worldAabbMin, worldAabbMax, gridSizeX, gridSizeY, gridSizeZ, + maxSmallProxies, maxLargeProxies, maxPairsPerBody, + maxBodiesPerCell, cellFactorAABB); +} + + + +btGpu3DGridBroadphase::btGpu3DGridBroadphase( btOverlappingPairCache* overlappingPairCache, + const btVector3& worldAabbMin,const btVector3& worldAabbMax, + int gridSizeX, int gridSizeY, int gridSizeZ, + int maxSmallProxies, int maxLargeProxies, int maxPairsPerBody, + int maxBodiesPerCell, + btScalar cellFactorAABB) : + btSimpleBroadphase(maxSmallProxies, overlappingPairCache), + m_bInitialized(false), + m_numBodies(0) +{ + _initialize(worldAabbMin, worldAabbMax, gridSizeX, gridSizeY, gridSizeZ, + maxSmallProxies, maxLargeProxies, maxPairsPerBody, + maxBodiesPerCell, cellFactorAABB); +} + + + +btGpu3DGridBroadphase::~btGpu3DGridBroadphase() +{ + //btSimpleBroadphase will free memory of btSortedOverlappingPairCache, because m_ownsPairCache + assert(m_bInitialized); + _finalize(); +} + + + +void btGpu3DGridBroadphase::_initialize( const btVector3& worldAabbMin,const btVector3& worldAabbMax, + int gridSizeX, int gridSizeY, int gridSizeZ, + int maxSmallProxies, int maxLargeProxies, int maxPairsPerBody, + int maxBodiesPerCell, + btScalar cellFactorAABB) +{ + // set various paramerers + m_ownsPairCache = true; + m_params.m_gridSizeX = gridSizeX; + m_params.m_gridSizeY = gridSizeY; + m_params.m_gridSizeZ = gridSizeZ; + m_params.m_numCells = m_params.m_gridSizeX * m_params.m_gridSizeY * m_params.m_gridSizeZ; + btVector3 w_org = worldAabbMin; + m_params.m_worldOriginX = w_org.getX(); + m_params.m_worldOriginY = w_org.getY(); + m_params.m_worldOriginZ = w_org.getZ(); + btVector3 w_size = worldAabbMax - worldAabbMin; + m_params.m_cellSizeX = w_size.getX() / m_params.m_gridSizeX; + m_params.m_cellSizeY = w_size.getY() / m_params.m_gridSizeY; + m_params.m_cellSizeZ = w_size.getZ() / m_params.m_gridSizeZ; + m_maxRadius = btMin(btMin(m_params.m_cellSizeX, m_params.m_cellSizeY), m_params.m_cellSizeZ); + m_maxRadius *= btScalar(0.5f); + m_params.m_numBodies = m_numBodies; + m_params.m_maxBodiesPerCell = maxBodiesPerCell; + + m_numLargeHandles = 0; + m_maxLargeHandles = maxLargeProxies; + + m_maxPairsPerBody = maxPairsPerBody; + + m_cellFactorAABB = cellFactorAABB; + + m_LastLargeHandleIndex = -1; + + assert(!m_bInitialized); + // allocate host storage + m_hBodiesHash = new unsigned int[m_maxHandles * 2]; + memset(m_hBodiesHash, 0x00, m_maxHandles*2*sizeof(unsigned int)); + + m_hCellStart = new unsigned int[m_params.m_numCells]; + memset(m_hCellStart, 0x00, m_params.m_numCells * sizeof(unsigned int)); + + m_hPairBuffStartCurr = new unsigned int[m_maxHandles * 2 + 2]; + // --------------- for now, init with m_maxPairsPerBody for each body + m_hPairBuffStartCurr[0] = 0; + m_hPairBuffStartCurr[1] = 0; + for(int i = 1; i <= m_maxHandles; i++) + { + m_hPairBuffStartCurr[i * 2] = m_hPairBuffStartCurr[(i-1) * 2] + m_maxPairsPerBody; + m_hPairBuffStartCurr[i * 2 + 1] = 0; + } + //---------------- + unsigned int numAABB = m_maxHandles + m_maxLargeHandles; + m_hAABB = new bt3DGrid3F1U[numAABB * 2]; // AABB Min & Max + + m_hPairBuff = new unsigned int[m_maxHandles * m_maxPairsPerBody]; + memset(m_hPairBuff, 0x00, m_maxHandles * m_maxPairsPerBody * sizeof(unsigned int)); // needed? + + m_hPairScan = new unsigned int[m_maxHandles + 1]; + + m_hPairOut = new unsigned int[m_maxHandles * m_maxPairsPerBody]; + +// large proxies + + // allocate handles buffer and put all handles on free list + m_pLargeHandlesRawPtr = btAlignedAlloc(sizeof(btSimpleBroadphaseProxy) * m_maxLargeHandles, 16); + m_pLargeHandles = new(m_pLargeHandlesRawPtr) btSimpleBroadphaseProxy[m_maxLargeHandles]; + m_firstFreeLargeHandle = 0; + { + for (int i = m_firstFreeLargeHandle; i < m_maxLargeHandles; i++) + { + m_pLargeHandles[i].SetNextFree(i + 1); + m_pLargeHandles[i].m_uniqueId = m_maxHandles+2+i; + } + m_pLargeHandles[m_maxLargeHandles - 1].SetNextFree(0); + } + +// debug data + m_numPairsAdded = 0; + m_numOverflows = 0; + + m_bInitialized = true; +} + + + +void btGpu3DGridBroadphase::_finalize() +{ + assert(m_bInitialized); + delete [] m_hBodiesHash; + delete [] m_hCellStart; + delete [] m_hPairBuffStartCurr; + delete [] m_hAABB; + delete [] m_hPairBuff; + delete [] m_hPairScan; + delete [] m_hPairOut; + btAlignedFree(m_pLargeHandlesRawPtr); + m_bInitialized = false; +} + + + +void btGpu3DGridBroadphase::calculateOverlappingPairs(btDispatcher* dispatcher) +{ + if(m_numHandles <= 0) + { + BT_PROFILE("addLarge2LargePairsToCache"); + addLarge2LargePairsToCache(dispatcher); + return; + } + // update constants + setParameters(&m_params); + // prepare AABB array + prepareAABB(); + // calculate hash + calcHashAABB(); + // sort bodies based on hash + sortHash(); + // find start of each cell + findCellStart(); + // findOverlappingPairs (small/small) + findOverlappingPairs(); + // findOverlappingPairs (small/large) + findPairsLarge(); + // add pairs to CPU cache + computePairCacheChanges(); + scanOverlappingPairBuff(); + squeezeOverlappingPairBuff(); + addPairsToCache(dispatcher); + // find and add large/large pairs to CPU cache + addLarge2LargePairsToCache(dispatcher); + return; +} + + + +void btGpu3DGridBroadphase::addPairsToCache(btDispatcher* dispatcher) +{ + m_numPairsAdded = 0; + m_numPairsRemoved = 0; + for(int i = 0; i < m_numHandles; i++) + { + unsigned int num = m_hPairScan[i+1] - m_hPairScan[i]; + if(!num) + { + continue; + } + unsigned int* pInp = m_hPairOut + m_hPairScan[i]; + unsigned int index0 = m_hAABB[i * 2].uw; + btSimpleBroadphaseProxy* proxy0 = &m_pHandles[index0]; + for(unsigned int j = 0; j < num; j++) + { + unsigned int indx1_s = pInp[j]; + unsigned int index1 = indx1_s & (~BT_3DGRID_PAIR_ANY_FLG); + btSimpleBroadphaseProxy* proxy1; + if(index1 < (unsigned int)m_maxHandles) + { + proxy1 = &m_pHandles[index1]; + } + else + { + index1 -= m_maxHandles; + btAssert((index1 >= 0) && (index1 < (unsigned int)m_maxLargeHandles)); + proxy1 = &m_pLargeHandles[index1]; + } + if(indx1_s & BT_3DGRID_PAIR_NEW_FLG) + { + m_pairCache->addOverlappingPair(proxy0,proxy1); + m_numPairsAdded++; + } + else + { + m_pairCache->removeOverlappingPair(proxy0,proxy1,dispatcher); + m_numPairsRemoved++; + } + } + } +} + + + +btBroadphaseProxy* btGpu3DGridBroadphase::createProxy( const btVector3& aabbMin, const btVector3& aabbMax,int shapeType,void* userPtr ,short int collisionFilterGroup,short int collisionFilterMask, btDispatcher* dispatcher,void* multiSapProxy) +{ + btBroadphaseProxy* proxy; + bool bIsLarge = isLargeProxy(aabbMin, aabbMax); + if(bIsLarge) + { + if (m_numLargeHandles >= m_maxLargeHandles) + { + ///you have to increase the cell size, so 'large' proxies become 'small' proxies (fitting a cell) + btAssert(0); + return 0; //should never happen, but don't let the game crash ;-) + } + btAssert((aabbMin[0]<= aabbMax[0]) && (aabbMin[1]<= aabbMax[1]) && (aabbMin[2]<= aabbMax[2])); + int newHandleIndex = allocLargeHandle(); + proxy = new (&m_pLargeHandles[newHandleIndex])btSimpleBroadphaseProxy(aabbMin,aabbMax,shapeType,userPtr,collisionFilterGroup,collisionFilterMask,multiSapProxy); + } + else + { + proxy = btSimpleBroadphase::createProxy(aabbMin, aabbMax, shapeType, userPtr, collisionFilterGroup, collisionFilterMask, dispatcher, multiSapProxy); + } + return proxy; +} + + + +void btGpu3DGridBroadphase::destroyProxy(btBroadphaseProxy* proxy, btDispatcher* dispatcher) +{ + bool bIsLarge = isLargeProxy(proxy); + if(bIsLarge) + { + + btSimpleBroadphaseProxy* proxy0 = static_cast(proxy); + freeLargeHandle(proxy0); + m_pairCache->removeOverlappingPairsContainingProxy(proxy,dispatcher); + } + else + { + btSimpleBroadphase::destroyProxy(proxy, dispatcher); + } + return; +} + + + +void btGpu3DGridBroadphase::resetPool(btDispatcher* dispatcher) +{ + m_hPairBuffStartCurr[0] = 0; + m_hPairBuffStartCurr[1] = 0; + for(int i = 1; i <= m_maxHandles; i++) + { + m_hPairBuffStartCurr[i * 2] = m_hPairBuffStartCurr[(i-1) * 2] + m_maxPairsPerBody; + m_hPairBuffStartCurr[i * 2 + 1] = 0; + } +} + + + +bool btGpu3DGridBroadphase::isLargeProxy(const btVector3& aabbMin, const btVector3& aabbMax) +{ + btVector3 diag = aabbMax - aabbMin; + + ///use the bounding sphere radius of this bounding box, to include rotation + btScalar radius = diag.length() * btScalar(0.5f); + radius *= m_cellFactorAABB; // user-defined factor + + return (radius > m_maxRadius); +} + + + +bool btGpu3DGridBroadphase::isLargeProxy(btBroadphaseProxy* proxy) +{ + return (proxy->getUid() >= (m_maxHandles+2)); +} + + + +void btGpu3DGridBroadphase::addLarge2LargePairsToCache(btDispatcher* dispatcher) +{ + int i,j; + if (m_numLargeHandles <= 0) + { + return; + } + int new_largest_index = -1; + for(i = 0; i <= m_LastLargeHandleIndex; i++) + { + btSimpleBroadphaseProxy* proxy0 = &m_pLargeHandles[i]; + if(!proxy0->m_clientObject) + { + continue; + } + new_largest_index = i; + for(j = i + 1; j <= m_LastLargeHandleIndex; j++) + { + btSimpleBroadphaseProxy* proxy1 = &m_pLargeHandles[j]; + if(!proxy1->m_clientObject) + { + continue; + } + btAssert(proxy0 != proxy1); + btSimpleBroadphaseProxy* p0 = getSimpleProxyFromProxy(proxy0); + btSimpleBroadphaseProxy* p1 = getSimpleProxyFromProxy(proxy1); + if(aabbOverlap(p0,p1)) + { + if (!m_pairCache->findPair(proxy0,proxy1)) + { + m_pairCache->addOverlappingPair(proxy0,proxy1); + } + } + else + { + if(m_pairCache->findPair(proxy0,proxy1)) + { + m_pairCache->removeOverlappingPair(proxy0,proxy1,dispatcher); + } + } + } + } + m_LastLargeHandleIndex = new_largest_index; + return; +} + + + +void btGpu3DGridBroadphase::rayTest(const btVector3& rayFrom,const btVector3& rayTo, btBroadphaseRayCallback& rayCallback) +{ + btSimpleBroadphase::rayTest(rayFrom, rayTo, rayCallback); + for (int i=0; i <= m_LastLargeHandleIndex; i++) + { + btSimpleBroadphaseProxy* proxy = &m_pLargeHandles[i]; + if(!proxy->m_clientObject) + { + continue; + } + rayCallback.process(proxy); + } +} + + + +// +// overrides for CPU version +// + + + +void btGpu3DGridBroadphase::prepareAABB() +{ + BT_PROFILE("prepareAABB"); + bt3DGrid3F1U* pBB = m_hAABB; + int i; + int new_largest_index = -1; + unsigned int num_small = 0; + for(i = 0; i <= m_LastHandleIndex; i++) + { + btSimpleBroadphaseProxy* proxy0 = &m_pHandles[i]; + if(!proxy0->m_clientObject) + { + continue; + } + new_largest_index = i; + pBB->fx = proxy0->m_aabbMin.getX(); + pBB->fy = proxy0->m_aabbMin.getY(); + pBB->fz = proxy0->m_aabbMin.getZ(); + pBB->uw = i; + pBB++; + pBB->fx = proxy0->m_aabbMax.getX(); + pBB->fy = proxy0->m_aabbMax.getY(); + pBB->fz = proxy0->m_aabbMax.getZ(); + pBB->uw = num_small; + pBB++; + num_small++; + } + m_LastHandleIndex = new_largest_index; + new_largest_index = -1; + unsigned int num_large = 0; + for(i = 0; i <= m_LastLargeHandleIndex; i++) + { + btSimpleBroadphaseProxy* proxy0 = &m_pLargeHandles[i]; + if(!proxy0->m_clientObject) + { + continue; + } + new_largest_index = i; + pBB->fx = proxy0->m_aabbMin.getX(); + pBB->fy = proxy0->m_aabbMin.getY(); + pBB->fz = proxy0->m_aabbMin.getZ(); + pBB->uw = i + m_maxHandles; + pBB++; + pBB->fx = proxy0->m_aabbMax.getX(); + pBB->fy = proxy0->m_aabbMax.getY(); + pBB->fz = proxy0->m_aabbMax.getZ(); + pBB->uw = num_large + m_maxHandles; + pBB++; + num_large++; + } + m_LastLargeHandleIndex = new_largest_index; + // paranoid checks + btAssert(num_small == m_numHandles); + btAssert(num_large == m_numLargeHandles); + return; +} + + + +void btGpu3DGridBroadphase::setParameters(bt3DGridBroadphaseParams* hostParams) +{ + s3DGridBroadphaseParams = *hostParams; + return; +} + + + +void btGpu3DGridBroadphase::calcHashAABB() +{ + BT_PROFILE("bt3DGrid_calcHashAABB"); + btGpu_calcHashAABB(m_hAABB, m_hBodiesHash, m_numHandles); + return; +} + + + +void btGpu3DGridBroadphase::sortHash() +{ + class bt3DGridHashKey + { + public: + unsigned int hash; + unsigned int index; + void quickSort(bt3DGridHashKey* pData, int lo, int hi) + { + int i=lo, j=hi; + bt3DGridHashKey x = pData[(lo+hi)/2]; + do + { + while(pData[i].hash > x.hash) i++; + while(x.hash > pData[j].hash) j--; + if(i <= j) + { + bt3DGridHashKey t = pData[i]; + pData[i] = pData[j]; + pData[j] = t; + i++; j--; + } + } while(i <= j); + if(lo < j) pData->quickSort(pData, lo, j); + if(i < hi) pData->quickSort(pData, i, hi); + } + }; + BT_PROFILE("bt3DGrid_sortHash"); + bt3DGridHashKey* pHash = (bt3DGridHashKey*)m_hBodiesHash; + pHash->quickSort(pHash, 0, m_numHandles - 1); + return; +} + + + +void btGpu3DGridBroadphase::findCellStart() +{ + BT_PROFILE("bt3DGrid_findCellStart"); + btGpu_findCellStart(m_hBodiesHash, m_hCellStart, m_numHandles, m_params.m_numCells); + return; +} + + + +void btGpu3DGridBroadphase::findOverlappingPairs() +{ + BT_PROFILE("bt3DGrid_findOverlappingPairs"); + btGpu_findOverlappingPairs(m_hAABB, m_hBodiesHash, m_hCellStart, m_hPairBuff, m_hPairBuffStartCurr, m_numHandles); + return; +} + + + +void btGpu3DGridBroadphase::findPairsLarge() +{ + BT_PROFILE("bt3DGrid_findPairsLarge"); + btGpu_findPairsLarge(m_hAABB, m_hBodiesHash, m_hCellStart, m_hPairBuff, m_hPairBuffStartCurr, m_numHandles, m_numLargeHandles); + return; +} + + + +void btGpu3DGridBroadphase::computePairCacheChanges() +{ + BT_PROFILE("bt3DGrid_computePairCacheChanges"); + btGpu_computePairCacheChanges(m_hPairBuff, m_hPairBuffStartCurr, m_hPairScan, m_hAABB, m_numHandles); + return; +} + + + +void btGpu3DGridBroadphase::scanOverlappingPairBuff() +{ + BT_PROFILE("bt3DGrid_scanOverlappingPairBuff"); + m_hPairScan[0] = 0; + for(int i = 1; i <= m_numHandles; i++) + { + unsigned int delta = m_hPairScan[i]; + m_hPairScan[i] = m_hPairScan[i-1] + delta; + } + return; +} + + + +void btGpu3DGridBroadphase::squeezeOverlappingPairBuff() +{ + BT_PROFILE("bt3DGrid_squeezeOverlappingPairBuff"); + btGpu_squeezeOverlappingPairBuff(m_hPairBuff, m_hPairBuffStartCurr, m_hPairScan, m_hPairOut, m_hAABB, m_numHandles); + return; +} + + + +#include "btGpu3DGridBroadphaseSharedCode.h" + + diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/btGpu3DGridBroadphase.h b/Engine/lib/bullet/src/BulletMultiThreaded/btGpu3DGridBroadphase.h new file mode 100644 index 000000000..1d49a0557 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/btGpu3DGridBroadphase.h @@ -0,0 +1,138 @@ +/* +Bullet Continuous Collision Detection and Physics Library, http://bulletphysics.org +Copyright (C) 2006, 2009 Sony Computer Entertainment Inc. + +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 BTGPU3DGRIDBROADPHASE_H +#define BTGPU3DGRIDBROADPHASE_H + +//---------------------------------------------------------------------------------------- + +#include "BulletCollision/BroadphaseCollision/btSimpleBroadphase.h" + +#include "btGpu3DGridBroadphaseSharedTypes.h" + +//---------------------------------------------------------------------------------------- + +///The btGpu3DGridBroadphase uses GPU-style code compiled for CPU to compute overlapping pairs + +class btGpu3DGridBroadphase : public btSimpleBroadphase +{ +protected: + bool m_bInitialized; + unsigned int m_numBodies; + unsigned int m_numCells; + unsigned int m_maxPairsPerBody; + btScalar m_cellFactorAABB; + unsigned int m_maxBodiesPerCell; + bt3DGridBroadphaseParams m_params; + btScalar m_maxRadius; + // CPU data + unsigned int* m_hBodiesHash; + unsigned int* m_hCellStart; + unsigned int* m_hPairBuffStartCurr; + bt3DGrid3F1U* m_hAABB; + unsigned int* m_hPairBuff; + unsigned int* m_hPairScan; + unsigned int* m_hPairOut; +// large proxies + int m_numLargeHandles; + int m_maxLargeHandles; + int m_LastLargeHandleIndex; + btSimpleBroadphaseProxy* m_pLargeHandles; + void* m_pLargeHandlesRawPtr; + int m_firstFreeLargeHandle; + int allocLargeHandle() + { + btAssert(m_numLargeHandles < m_maxLargeHandles); + int freeLargeHandle = m_firstFreeLargeHandle; + m_firstFreeLargeHandle = m_pLargeHandles[freeLargeHandle].GetNextFree(); + m_numLargeHandles++; + if(freeLargeHandle > m_LastLargeHandleIndex) + { + m_LastLargeHandleIndex = freeLargeHandle; + } + return freeLargeHandle; + } + void freeLargeHandle(btSimpleBroadphaseProxy* proxy) + { + int handle = int(proxy - m_pLargeHandles); + btAssert((handle >= 0) && (handle < m_maxHandles)); + if(handle == m_LastLargeHandleIndex) + { + m_LastLargeHandleIndex--; + } + proxy->SetNextFree(m_firstFreeLargeHandle); + m_firstFreeLargeHandle = handle; + proxy->m_clientObject = 0; + m_numLargeHandles--; + } + bool isLargeProxy(const btVector3& aabbMin, const btVector3& aabbMax); + bool isLargeProxy(btBroadphaseProxy* proxy); +// debug + unsigned int m_numPairsAdded; + unsigned int m_numPairsRemoved; + unsigned int m_numOverflows; +// +public: + btGpu3DGridBroadphase(const btVector3& worldAabbMin,const btVector3& worldAabbMax, + int gridSizeX, int gridSizeY, int gridSizeZ, + int maxSmallProxies, int maxLargeProxies, int maxPairsPerBody, + int maxBodiesPerCell = 8, + btScalar cellFactorAABB = btScalar(1.0f)); + btGpu3DGridBroadphase( btOverlappingPairCache* overlappingPairCache, + const btVector3& worldAabbMin,const btVector3& worldAabbMax, + int gridSizeX, int gridSizeY, int gridSizeZ, + int maxSmallProxies, int maxLargeProxies, int maxPairsPerBody, + int maxBodiesPerCell = 8, + btScalar cellFactorAABB = btScalar(1.0f)); + virtual ~btGpu3DGridBroadphase(); + virtual void calculateOverlappingPairs(btDispatcher* dispatcher); + + virtual btBroadphaseProxy* createProxy(const btVector3& aabbMin, const btVector3& aabbMax,int shapeType,void* userPtr ,short int collisionFilterGroup,short int collisionFilterMask, btDispatcher* dispatcher,void* multiSapProxy); + virtual void destroyProxy(btBroadphaseProxy* proxy,btDispatcher* dispatcher); + virtual void rayTest(const btVector3& rayFrom,const btVector3& rayTo, btBroadphaseRayCallback& rayCallback); + virtual void resetPool(btDispatcher* dispatcher); + +protected: + void _initialize( const btVector3& worldAabbMin,const btVector3& worldAabbMax, + int gridSizeX, int gridSizeY, int gridSizeZ, + int maxSmallProxies, int maxLargeProxies, int maxPairsPerBody, + int maxBodiesPerCell = 8, + btScalar cellFactorAABB = btScalar(1.0f)); + void _finalize(); + void addPairsToCache(btDispatcher* dispatcher); + void addLarge2LargePairsToCache(btDispatcher* dispatcher); + +// overrides for CPU version + virtual void setParameters(bt3DGridBroadphaseParams* hostParams); + virtual void prepareAABB(); + virtual void calcHashAABB(); + virtual void sortHash(); + virtual void findCellStart(); + virtual void findOverlappingPairs(); + virtual void findPairsLarge(); + virtual void computePairCacheChanges(); + virtual void scanOverlappingPairBuff(); + virtual void squeezeOverlappingPairBuff(); +}; + +//---------------------------------------------------------------------------------------- + +#endif //BTGPU3DGRIDBROADPHASE_H + +//---------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------- diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/btGpu3DGridBroadphaseSharedCode.h b/Engine/lib/bullet/src/BulletMultiThreaded/btGpu3DGridBroadphaseSharedCode.h new file mode 100644 index 000000000..e0afb87bb --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/btGpu3DGridBroadphaseSharedCode.h @@ -0,0 +1,430 @@ +/* +Bullet Continuous Collision Detection and Physics Library, http://bulletphysics.org +Copyright (C) 2006, 2009 Sony Computer Entertainment Inc. + +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. +*/ + +//---------------------------------------------------------------------------------------- + +//---------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------- +// K E R N E L F U N C T I O N S +//---------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------- + +// calculate position in uniform grid +BT_GPU___device__ int3 bt3DGrid_calcGridPos(float4 p) +{ + int3 gridPos; + gridPos.x = (int)floor((p.x - BT_GPU_params.m_worldOriginX) / BT_GPU_params.m_cellSizeX); + gridPos.y = (int)floor((p.y - BT_GPU_params.m_worldOriginY) / BT_GPU_params.m_cellSizeY); + gridPos.z = (int)floor((p.z - BT_GPU_params.m_worldOriginZ) / BT_GPU_params.m_cellSizeZ); + return gridPos; +} // bt3DGrid_calcGridPos() + +//---------------------------------------------------------------------------------------- + +// calculate address in grid from position (clamping to edges) +BT_GPU___device__ uint bt3DGrid_calcGridHash(int3 gridPos) +{ + gridPos.x = BT_GPU_max(0, BT_GPU_min(gridPos.x, (int)BT_GPU_params.m_gridSizeX - 1)); + gridPos.y = BT_GPU_max(0, BT_GPU_min(gridPos.y, (int)BT_GPU_params.m_gridSizeY - 1)); + gridPos.z = BT_GPU_max(0, BT_GPU_min(gridPos.z, (int)BT_GPU_params.m_gridSizeZ - 1)); + return BT_GPU___mul24(BT_GPU___mul24(gridPos.z, BT_GPU_params.m_gridSizeY), BT_GPU_params.m_gridSizeX) + BT_GPU___mul24(gridPos.y, BT_GPU_params.m_gridSizeX) + gridPos.x; +} // bt3DGrid_calcGridHash() + +//---------------------------------------------------------------------------------------- + +// calculate grid hash value for each body using its AABB +BT_GPU___global__ void calcHashAABBD(bt3DGrid3F1U* pAABB, uint2* pHash, uint numBodies) +{ + int index = BT_GPU___mul24(BT_GPU_blockIdx.x, BT_GPU_blockDim.x) + BT_GPU_threadIdx.x; + if(index >= (int)numBodies) + { + return; + } + bt3DGrid3F1U bbMin = pAABB[index*2]; + bt3DGrid3F1U bbMax = pAABB[index*2 + 1]; + float4 pos; + pos.x = (bbMin.fx + bbMax.fx) * 0.5f; + pos.y = (bbMin.fy + bbMax.fy) * 0.5f; + pos.z = (bbMin.fz + bbMax.fz) * 0.5f; + // get address in grid + int3 gridPos = bt3DGrid_calcGridPos(pos); + uint gridHash = bt3DGrid_calcGridHash(gridPos); + // store grid hash and body index + pHash[index] = BT_GPU_make_uint2(gridHash, index); +} // calcHashAABBD() + +//---------------------------------------------------------------------------------------- + +BT_GPU___global__ void findCellStartD(uint2* pHash, uint* cellStart, uint numBodies) +{ + int index = BT_GPU___mul24(BT_GPU_blockIdx.x, BT_GPU_blockDim.x) + BT_GPU_threadIdx.x; + if(index >= (int)numBodies) + { + return; + } + uint2 sortedData = pHash[index]; + // Load hash data into shared memory so that we can look + // at neighboring body's hash value without loading + // two hash values per thread + BT_GPU___shared__ uint sharedHash[257]; + sharedHash[BT_GPU_threadIdx.x+1] = sortedData.x; + if((index > 0) && (BT_GPU_threadIdx.x == 0)) + { + // first thread in block must load neighbor body hash + volatile uint2 prevData = pHash[index-1]; + sharedHash[0] = prevData.x; + } + BT_GPU___syncthreads(); + if((index == 0) || (sortedData.x != sharedHash[BT_GPU_threadIdx.x])) + { + cellStart[sortedData.x] = index; + } +} // findCellStartD() + +//---------------------------------------------------------------------------------------- + +BT_GPU___device__ uint cudaTestAABBOverlap(bt3DGrid3F1U min0, bt3DGrid3F1U max0, bt3DGrid3F1U min1, bt3DGrid3F1U max1) +{ + return (min0.fx <= max1.fx)&& (min1.fx <= max0.fx) && + (min0.fy <= max1.fy)&& (min1.fy <= max0.fy) && + (min0.fz <= max1.fz)&& (min1.fz <= max0.fz); +} // cudaTestAABBOverlap() + +//---------------------------------------------------------------------------------------- + +BT_GPU___device__ void findPairsInCell( int3 gridPos, + uint index, + uint2* pHash, + uint* pCellStart, + bt3DGrid3F1U* pAABB, + uint* pPairBuff, + uint2* pPairBuffStartCurr, + uint numBodies) +{ + if ( (gridPos.x < 0) || (gridPos.x > (int)BT_GPU_params.m_gridSizeX - 1) + || (gridPos.y < 0) || (gridPos.y > (int)BT_GPU_params.m_gridSizeY - 1) + || (gridPos.z < 0) || (gridPos.z > (int)BT_GPU_params.m_gridSizeZ - 1)) + { + return; + } + uint gridHash = bt3DGrid_calcGridHash(gridPos); + // get start of bucket for this cell + uint bucketStart = pCellStart[gridHash]; + if (bucketStart == 0xffffffff) + { + return; // cell empty + } + // iterate over bodies in this cell + uint2 sortedData = pHash[index]; + uint unsorted_indx = sortedData.y; + bt3DGrid3F1U min0 = BT_GPU_FETCH(pAABB, unsorted_indx*2); + bt3DGrid3F1U max0 = BT_GPU_FETCH(pAABB, unsorted_indx*2 + 1); + uint handleIndex = min0.uw; + uint2 start_curr = pPairBuffStartCurr[handleIndex]; + uint start = start_curr.x; + uint curr = start_curr.y; + uint2 start_curr_next = pPairBuffStartCurr[handleIndex+1]; + uint curr_max = start_curr_next.x - start - 1; + uint bucketEnd = bucketStart + BT_GPU_params.m_maxBodiesPerCell; + bucketEnd = (bucketEnd > numBodies) ? numBodies : bucketEnd; + for(uint index2 = bucketStart; index2 < bucketEnd; index2++) + { + uint2 cellData = pHash[index2]; + if (cellData.x != gridHash) + { + break; // no longer in same bucket + } + uint unsorted_indx2 = cellData.y; + if (unsorted_indx2 < unsorted_indx) // check not colliding with self + { + bt3DGrid3F1U min1 = BT_GPU_FETCH(pAABB, unsorted_indx2*2); + bt3DGrid3F1U max1 = BT_GPU_FETCH(pAABB, unsorted_indx2*2 + 1); + if(cudaTestAABBOverlap(min0, max0, min1, max1)) + { + uint handleIndex2 = min1.uw; + uint k; + for(k = 0; k < curr; k++) + { + uint old_pair = pPairBuff[start+k] & (~BT_3DGRID_PAIR_ANY_FLG); + if(old_pair == handleIndex2) + { + pPairBuff[start+k] |= BT_3DGRID_PAIR_FOUND_FLG; + break; + } + } + if(k == curr) + { + if(curr >= curr_max) + { // not a good solution, but let's avoid crash + break; + } + pPairBuff[start+curr] = handleIndex2 | BT_3DGRID_PAIR_NEW_FLG; + curr++; + } + } + } + } + pPairBuffStartCurr[handleIndex] = BT_GPU_make_uint2(start, curr); + return; +} // findPairsInCell() + +//---------------------------------------------------------------------------------------- + +BT_GPU___global__ void findOverlappingPairsD( bt3DGrid3F1U* pAABB, uint2* pHash, uint* pCellStart, + uint* pPairBuff, uint2* pPairBuffStartCurr, uint numBodies) +{ + int index = BT_GPU___mul24(BT_GPU_blockIdx.x, BT_GPU_blockDim.x) + BT_GPU_threadIdx.x; + if(index >= (int)numBodies) + { + return; + } + uint2 sortedData = pHash[index]; + uint unsorted_indx = sortedData.y; + bt3DGrid3F1U bbMin = BT_GPU_FETCH(pAABB, unsorted_indx*2); + bt3DGrid3F1U bbMax = BT_GPU_FETCH(pAABB, unsorted_indx*2 + 1); + float4 pos; + pos.x = (bbMin.fx + bbMax.fx) * 0.5f; + pos.y = (bbMin.fy + bbMax.fy) * 0.5f; + pos.z = (bbMin.fz + bbMax.fz) * 0.5f; + // get address in grid + int3 gridPos = bt3DGrid_calcGridPos(pos); + // examine only neighbouring cells + for(int z=-1; z<=1; z++) { + for(int y=-1; y<=1; y++) { + for(int x=-1; x<=1; x++) { + findPairsInCell(gridPos + BT_GPU_make_int3(x, y, z), index, pHash, pCellStart, pAABB, pPairBuff, pPairBuffStartCurr, numBodies); + } + } + } +} // findOverlappingPairsD() + +//---------------------------------------------------------------------------------------- + +BT_GPU___global__ void findPairsLargeD( bt3DGrid3F1U* pAABB, uint2* pHash, uint* pCellStart, uint* pPairBuff, + uint2* pPairBuffStartCurr, uint numBodies, uint numLarge) +{ + int index = BT_GPU___mul24(BT_GPU_blockIdx.x, BT_GPU_blockDim.x) + BT_GPU_threadIdx.x; + if(index >= (int)numBodies) + { + return; + } + uint2 sortedData = pHash[index]; + uint unsorted_indx = sortedData.y; + bt3DGrid3F1U min0 = BT_GPU_FETCH(pAABB, unsorted_indx*2); + bt3DGrid3F1U max0 = BT_GPU_FETCH(pAABB, unsorted_indx*2 + 1); + uint handleIndex = min0.uw; + uint2 start_curr = pPairBuffStartCurr[handleIndex]; + uint start = start_curr.x; + uint curr = start_curr.y; + uint2 start_curr_next = pPairBuffStartCurr[handleIndex+1]; + uint curr_max = start_curr_next.x - start - 1; + for(uint i = 0; i < numLarge; i++) + { + uint indx2 = numBodies + i; + bt3DGrid3F1U min1 = BT_GPU_FETCH(pAABB, indx2*2); + bt3DGrid3F1U max1 = BT_GPU_FETCH(pAABB, indx2*2 + 1); + if(cudaTestAABBOverlap(min0, max0, min1, max1)) + { + uint k; + uint handleIndex2 = min1.uw; + for(k = 0; k < curr; k++) + { + uint old_pair = pPairBuff[start+k] & (~BT_3DGRID_PAIR_ANY_FLG); + if(old_pair == handleIndex2) + { + pPairBuff[start+k] |= BT_3DGRID_PAIR_FOUND_FLG; + break; + } + } + if(k == curr) + { + pPairBuff[start+curr] = handleIndex2 | BT_3DGRID_PAIR_NEW_FLG; + if(curr >= curr_max) + { // not a good solution, but let's avoid crash + break; + } + curr++; + } + } + } + pPairBuffStartCurr[handleIndex] = BT_GPU_make_uint2(start, curr); + return; +} // findPairsLargeD() + +//---------------------------------------------------------------------------------------- + +BT_GPU___global__ void computePairCacheChangesD(uint* pPairBuff, uint2* pPairBuffStartCurr, + uint* pPairScan, bt3DGrid3F1U* pAABB, uint numBodies) +{ + int index = BT_GPU___mul24(BT_GPU_blockIdx.x, BT_GPU_blockDim.x) + BT_GPU_threadIdx.x; + if(index >= (int)numBodies) + { + return; + } + bt3DGrid3F1U bbMin = pAABB[index * 2]; + uint handleIndex = bbMin.uw; + uint2 start_curr = pPairBuffStartCurr[handleIndex]; + uint start = start_curr.x; + uint curr = start_curr.y; + uint *pInp = pPairBuff + start; + uint num_changes = 0; + for(uint k = 0; k < curr; k++, pInp++) + { + if(!((*pInp) & BT_3DGRID_PAIR_FOUND_FLG)) + { + num_changes++; + } + } + pPairScan[index+1] = num_changes; +} // computePairCacheChangesD() + +//---------------------------------------------------------------------------------------- + +BT_GPU___global__ void squeezeOverlappingPairBuffD(uint* pPairBuff, uint2* pPairBuffStartCurr, uint* pPairScan, + uint* pPairOut, bt3DGrid3F1U* pAABB, uint numBodies) +{ + int index = BT_GPU___mul24(BT_GPU_blockIdx.x, BT_GPU_blockDim.x) + BT_GPU_threadIdx.x; + if(index >= (int)numBodies) + { + return; + } + bt3DGrid3F1U bbMin = pAABB[index * 2]; + uint handleIndex = bbMin.uw; + uint2 start_curr = pPairBuffStartCurr[handleIndex]; + uint start = start_curr.x; + uint curr = start_curr.y; + uint* pInp = pPairBuff + start; + uint* pOut = pPairOut + pPairScan[index]; + uint* pOut2 = pInp; + uint num = 0; + for(uint k = 0; k < curr; k++, pInp++) + { + if(!((*pInp) & BT_3DGRID_PAIR_FOUND_FLG)) + { + *pOut = *pInp; + pOut++; + } + if((*pInp) & BT_3DGRID_PAIR_ANY_FLG) + { + *pOut2 = (*pInp) & (~BT_3DGRID_PAIR_ANY_FLG); + pOut2++; + num++; + } + } + pPairBuffStartCurr[handleIndex] = BT_GPU_make_uint2(start, num); +} // squeezeOverlappingPairBuffD() + + +//---------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------- +// E N D O F K E R N E L F U N C T I O N S +//---------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------- +//---------------------------------------------------------------------------------------- + +extern "C" +{ + +//---------------------------------------------------------------------------------------- + +void BT_GPU_PREF(calcHashAABB)(bt3DGrid3F1U* pAABB, unsigned int* hash, unsigned int numBodies) +{ + int numThreads, numBlocks; + BT_GPU_PREF(computeGridSize)(numBodies, 256, numBlocks, numThreads); + // execute the kernel + BT_GPU_EXECKERNEL(numBlocks, numThreads, calcHashAABBD, (pAABB, (uint2*)hash, numBodies)); + // check if kernel invocation generated an error + BT_GPU_CHECK_ERROR("calcHashAABBD kernel execution failed"); +} // calcHashAABB() + +//---------------------------------------------------------------------------------------- + +void BT_GPU_PREF(findCellStart(unsigned int* hash, unsigned int* cellStart, unsigned int numBodies, unsigned int numCells)) +{ + int numThreads, numBlocks; + BT_GPU_PREF(computeGridSize)(numBodies, 256, numBlocks, numThreads); + BT_GPU_SAFE_CALL(BT_GPU_Memset(cellStart, 0xffffffff, numCells*sizeof(uint))); + BT_GPU_EXECKERNEL(numBlocks, numThreads, findCellStartD, ((uint2*)hash, (uint*)cellStart, numBodies)); + BT_GPU_CHECK_ERROR("Kernel execution failed: findCellStartD"); +} // findCellStart() + +//---------------------------------------------------------------------------------------- + +void BT_GPU_PREF(findOverlappingPairs(bt3DGrid3F1U* pAABB, unsigned int* pHash, unsigned int* pCellStart, unsigned int* pPairBuff, unsigned int* pPairBuffStartCurr, unsigned int numBodies)) +{ +#if B_CUDA_USE_TEX + BT_GPU_SAFE_CALL(cudaBindTexture(0, pAABBTex, pAABB, numBodies * 2 * sizeof(bt3DGrid3F1U))); +#endif + int numThreads, numBlocks; + BT_GPU_PREF(computeGridSize)(numBodies, 64, numBlocks, numThreads); + BT_GPU_EXECKERNEL(numBlocks, numThreads, findOverlappingPairsD, (pAABB,(uint2*)pHash,(uint*)pCellStart,(uint*)pPairBuff,(uint2*)pPairBuffStartCurr,numBodies)); + BT_GPU_CHECK_ERROR("Kernel execution failed: bt_CudaFindOverlappingPairsD"); +#if B_CUDA_USE_TEX + BT_GPU_SAFE_CALL(cudaUnbindTexture(pAABBTex)); +#endif +} // findOverlappingPairs() + +//---------------------------------------------------------------------------------------- + +void BT_GPU_PREF(findPairsLarge(bt3DGrid3F1U* pAABB, unsigned int* pHash, unsigned int* pCellStart, unsigned int* pPairBuff, unsigned int* pPairBuffStartCurr, unsigned int numBodies, unsigned int numLarge)) +{ +#if B_CUDA_USE_TEX + BT_GPU_SAFE_CALL(cudaBindTexture(0, pAABBTex, pAABB, (numBodies+numLarge) * 2 * sizeof(bt3DGrid3F1U))); +#endif + int numThreads, numBlocks; + BT_GPU_PREF(computeGridSize)(numBodies, 64, numBlocks, numThreads); + BT_GPU_EXECKERNEL(numBlocks, numThreads, findPairsLargeD, (pAABB,(uint2*)pHash,(uint*)pCellStart,(uint*)pPairBuff,(uint2*)pPairBuffStartCurr,numBodies,numLarge)); + BT_GPU_CHECK_ERROR("Kernel execution failed: btCuda_findPairsLargeD"); +#if B_CUDA_USE_TEX + BT_GPU_SAFE_CALL(cudaUnbindTexture(pAABBTex)); +#endif +} // findPairsLarge() + +//---------------------------------------------------------------------------------------- + +void BT_GPU_PREF(computePairCacheChanges(unsigned int* pPairBuff, unsigned int* pPairBuffStartCurr, unsigned int* pPairScan, bt3DGrid3F1U* pAABB, unsigned int numBodies)) +{ + int numThreads, numBlocks; + BT_GPU_PREF(computeGridSize)(numBodies, 256, numBlocks, numThreads); + BT_GPU_EXECKERNEL(numBlocks, numThreads, computePairCacheChangesD, ((uint*)pPairBuff,(uint2*)pPairBuffStartCurr,(uint*)pPairScan,pAABB,numBodies)); + BT_GPU_CHECK_ERROR("Kernel execution failed: btCudaComputePairCacheChangesD"); +} // computePairCacheChanges() + +//---------------------------------------------------------------------------------------- + +void BT_GPU_PREF(squeezeOverlappingPairBuff(unsigned int* pPairBuff, unsigned int* pPairBuffStartCurr, unsigned int* pPairScan, unsigned int* pPairOut, bt3DGrid3F1U* pAABB, unsigned int numBodies)) +{ + int numThreads, numBlocks; + BT_GPU_PREF(computeGridSize)(numBodies, 256, numBlocks, numThreads); + BT_GPU_EXECKERNEL(numBlocks, numThreads, squeezeOverlappingPairBuffD, ((uint*)pPairBuff,(uint2*)pPairBuffStartCurr,(uint*)pPairScan,(uint*)pPairOut,pAABB,numBodies)); + BT_GPU_CHECK_ERROR("Kernel execution failed: btCudaSqueezeOverlappingPairBuffD"); +} // btCuda_squeezeOverlappingPairBuff() + +//------------------------------------------------------------------------------------------------ + +} // extern "C" + +//------------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------------ diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/btGpu3DGridBroadphaseSharedDefs.h b/Engine/lib/bullet/src/BulletMultiThreaded/btGpu3DGridBroadphaseSharedDefs.h new file mode 100644 index 000000000..db1b6206e --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/btGpu3DGridBroadphaseSharedDefs.h @@ -0,0 +1,60 @@ +/* +Bullet Continuous Collision Detection and Physics Library, http://bulletphysics.org +Copyright (C) 2006, 2009 Sony Computer Entertainment Inc. + +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. +*/ + +//---------------------------------------------------------------------------------------- + +// Shared definitions for GPU-based 3D Grid collision detection broadphase + +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// Keep this file free from Bullet headers +// it is included into both CUDA and CPU code +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +//---------------------------------------------------------------------------------------- + +#ifndef BTGPU3DGRIDBROADPHASESHAREDDEFS_H +#define BTGPU3DGRIDBROADPHASESHAREDDEFS_H + +//---------------------------------------------------------------------------------------- + +#include "btGpu3DGridBroadphaseSharedTypes.h" + +//---------------------------------------------------------------------------------------- + +extern "C" +{ + +//---------------------------------------------------------------------------------------- + +void BT_GPU_PREF(calcHashAABB)(bt3DGrid3F1U* pAABB, unsigned int* hash, unsigned int numBodies); + +void BT_GPU_PREF(findCellStart)(unsigned int* hash, unsigned int* cellStart, unsigned int numBodies, unsigned int numCells); + +void BT_GPU_PREF(findOverlappingPairs)(bt3DGrid3F1U* pAABB, unsigned int* pHash, unsigned int* pCellStart, unsigned int* pPairBuff, unsigned int* pPairBuffStartCurr, unsigned int numBodies); + +void BT_GPU_PREF(findPairsLarge)(bt3DGrid3F1U* pAABB, unsigned int* pHash, unsigned int* pCellStart, unsigned int* pPairBuff, unsigned int* pPairBuffStartCurr, unsigned int numBodies, unsigned int numLarge); + +void BT_GPU_PREF(computePairCacheChanges)(unsigned int* pPairBuff, unsigned int* pPairBuffStartCurr, unsigned int* pPairScan, bt3DGrid3F1U* pAABB, unsigned int numBodies); + +void BT_GPU_PREF(squeezeOverlappingPairBuff)(unsigned int* pPairBuff, unsigned int* pPairBuffStartCurr, unsigned int* pPairScan, unsigned int* pPairOut, bt3DGrid3F1U* pAABB, unsigned int numBodies); + + +//---------------------------------------------------------------------------------------- + +} // extern "C" + +//---------------------------------------------------------------------------------------- + +#endif // BTGPU3DGRIDBROADPHASESHAREDDEFS_H \ No newline at end of file diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/btGpu3DGridBroadphaseSharedTypes.h b/Engine/lib/bullet/src/BulletMultiThreaded/btGpu3DGridBroadphaseSharedTypes.h new file mode 100644 index 000000000..e1988841e --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/btGpu3DGridBroadphaseSharedTypes.h @@ -0,0 +1,66 @@ +/* +Bullet Continuous Collision Detection and Physics Library, http://bulletphysics.org +Copyright (C) 2006, 2009 Sony Computer Entertainment Inc. + +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. +*/ + +//---------------------------------------------------------------------------------------- + +// Shared definitions for GPU-based 3D Grid collision detection broadphase + +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// Keep this file free from Bullet headers +// it is included into both CUDA and CPU code +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +//---------------------------------------------------------------------------------------- + +#ifndef BTGPU3DGRIDBROADPHASESHAREDTYPES_H +#define BTGPU3DGRIDBROADPHASESHAREDTYPES_H + +//---------------------------------------------------------------------------------------- + +#define BT_3DGRID_PAIR_FOUND_FLG (0x40000000) +#define BT_3DGRID_PAIR_NEW_FLG (0x20000000) +#define BT_3DGRID_PAIR_ANY_FLG (BT_3DGRID_PAIR_FOUND_FLG | BT_3DGRID_PAIR_NEW_FLG) + +//---------------------------------------------------------------------------------------- + +struct bt3DGridBroadphaseParams +{ + unsigned int m_gridSizeX; + unsigned int m_gridSizeY; + unsigned int m_gridSizeZ; + unsigned int m_numCells; + float m_worldOriginX; + float m_worldOriginY; + float m_worldOriginZ; + float m_cellSizeX; + float m_cellSizeY; + float m_cellSizeZ; + unsigned int m_numBodies; + unsigned int m_maxBodiesPerCell; +}; + +//---------------------------------------------------------------------------------------- + +struct bt3DGrid3F1U +{ + float fx; + float fy; + float fz; + unsigned int uw; +}; + +//---------------------------------------------------------------------------------------- + +#endif // BTGPU3DGRIDBROADPHASESHAREDTYPES_H \ No newline at end of file diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/btGpuDefines.h b/Engine/lib/bullet/src/BulletMultiThreaded/btGpuDefines.h new file mode 100644 index 000000000..3b5eac028 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/btGpuDefines.h @@ -0,0 +1,211 @@ +/* +Bullet Continuous Collision Detection and Physics Library, http://bulletphysics.org +Copyright (C) 2006, 2009 Sony Computer Entertainment Inc. + +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. +*/ + + + +// definitions for "GPU on CPU" code + + +#ifndef BT_GPU_DEFINES_H +#define BT_GPU_DEFINES_H + +typedef unsigned int uint; + +struct int2 +{ + int x, y; +}; + +struct uint2 +{ + unsigned int x, y; +}; + +struct int3 +{ + int x, y, z; +}; + +struct uint3 +{ + unsigned int x, y, z; +}; + +struct float4 +{ + float x, y, z, w; +}; + +struct float3 +{ + float x, y, z; +}; + + +#define BT_GPU___device__ inline +#define BT_GPU___devdata__ +#define BT_GPU___constant__ +#define BT_GPU_max(a, b) ((a) > (b) ? (a) : (b)) +#define BT_GPU_min(a, b) ((a) < (b) ? (a) : (b)) +#define BT_GPU_params s3DGridBroadphaseParams +#define BT_GPU___mul24(a, b) ((a)*(b)) +#define BT_GPU___global__ inline +#define BT_GPU___shared__ static +#define BT_GPU___syncthreads() +#define CUDART_PI_F SIMD_PI + +static inline uint2 bt3dGrid_make_uint2(unsigned int x, unsigned int y) +{ + uint2 t; t.x = x; t.y = y; return t; +} +#define BT_GPU_make_uint2(x, y) bt3dGrid_make_uint2(x, y) + +static inline int3 bt3dGrid_make_int3(int x, int y, int z) +{ + int3 t; t.x = x; t.y = y; t.z = z; return t; +} +#define BT_GPU_make_int3(x, y, z) bt3dGrid_make_int3(x, y, z) + +static inline float3 bt3dGrid_make_float3(float x, float y, float z) +{ + float3 t; t.x = x; t.y = y; t.z = z; return t; +} +#define BT_GPU_make_float3(x, y, z) bt3dGrid_make_float3(x, y, z) + +static inline float3 bt3dGrid_make_float34(float4 f) +{ + float3 t; t.x = f.x; t.y = f.y; t.z = f.z; return t; +} +#define BT_GPU_make_float34(f) bt3dGrid_make_float34(f) + +static inline float3 bt3dGrid_make_float31(float f) +{ + float3 t; t.x = t.y = t.z = f; return t; +} +#define BT_GPU_make_float31(x) bt3dGrid_make_float31(x) + +static inline float4 bt3dGrid_make_float42(float3 v, float f) +{ + float4 t; t.x = v.x; t.y = v.y; t.z = v.z; t.w = f; return t; +} +#define BT_GPU_make_float42(a, b) bt3dGrid_make_float42(a, b) + +static inline float4 bt3dGrid_make_float44(float a, float b, float c, float d) +{ + float4 t; t.x = a; t.y = b; t.z = c; t.w = d; return t; +} +#define BT_GPU_make_float44(a, b, c, d) bt3dGrid_make_float44(a, b, c, d) + +inline int3 operator+(int3 a, int3 b) +{ + return bt3dGrid_make_int3(a.x + b.x, a.y + b.y, a.z + b.z); +} + +inline float4 operator+(const float4& a, const float4& b) +{ + float4 r; r.x = a.x+b.x; r.y = a.y+b.y; r.z = a.z+b.z; r.w = a.w+b.w; return r; +} +inline float4 operator*(const float4& a, float fact) +{ + float4 r; r.x = a.x*fact; r.y = a.y*fact; r.z = a.z*fact; r.w = a.w*fact; return r; +} +inline float4 operator*(float fact, float4& a) +{ + return (a * fact); +} +inline float4& operator*=(float4& a, float fact) +{ + a = fact * a; + return a; +} +inline float4& operator+=(float4& a, const float4& b) +{ + a = a + b; + return a; +} + +inline float3 operator+(const float3& a, const float3& b) +{ + float3 r; r.x = a.x+b.x; r.y = a.y+b.y; r.z = a.z+b.z; return r; +} +inline float3 operator-(const float3& a, const float3& b) +{ + float3 r; r.x = a.x-b.x; r.y = a.y-b.y; r.z = a.z-b.z; return r; +} +static inline float bt3dGrid_dot(float3& a, float3& b) +{ + return a.x*b.x+a.y*b.y+a.z*b.z; +} +#define BT_GPU_dot(a,b) bt3dGrid_dot(a,b) + +static inline float bt3dGrid_dot4(float4& a, float4& b) +{ + return a.x*b.x+a.y*b.y+a.z*b.z+a.w*b.w; +} +#define BT_GPU_dot4(a,b) bt3dGrid_dot4(a,b) + +static inline float3 bt3dGrid_cross(const float3& a, const float3& b) +{ + float3 r; r.x = a.y*b.z-a.z*b.y; r.y = -a.x*b.z+a.z*b.x; r.z = a.x*b.y-a.y*b.x; return r; +} +#define BT_GPU_cross(a,b) bt3dGrid_cross(a,b) + + +inline float3 operator*(const float3& a, float fact) +{ + float3 r; r.x = a.x*fact; r.y = a.y*fact; r.z = a.z*fact; return r; +} + + +inline float3& operator+=(float3& a, const float3& b) +{ + a = a + b; + return a; +} +inline float3& operator-=(float3& a, const float3& b) +{ + a = a - b; + return a; +} +inline float3& operator*=(float3& a, float fact) +{ + a = a * fact; + return a; +} +inline float3 operator-(const float3& v) +{ + float3 r; r.x = -v.x; r.y = -v.y; r.z = -v.z; return r; +} + + +#define BT_GPU_FETCH(a, b) a[b] +#define BT_GPU_FETCH4(a, b) a[b] +#define BT_GPU_PREF(func) btGpu_##func +#define BT_GPU_SAFE_CALL(func) func +#define BT_GPU_Memset memset +#define BT_GPU_MemcpyToSymbol(a, b, c) memcpy(a, b, c) +#define BT_GPU_BindTexture(a, b, c, d) +#define BT_GPU_UnbindTexture(a) + +static uint2 s_blockIdx, s_blockDim, s_threadIdx; +#define BT_GPU_blockIdx s_blockIdx +#define BT_GPU_blockDim s_blockDim +#define BT_GPU_threadIdx s_threadIdx +#define BT_GPU_EXECKERNEL(numb, numt, kfunc, args) {s_blockDim.x=numt;for(int nb=0;nb //for uint32_t etc. +#include "PlatformDefinitions.h" +#include "PpuAddressSpace.h" + +class btThreadSupportInterface +{ +public: + + virtual ~btThreadSupportInterface(); + +///send messages to SPUs + virtual void sendRequest(uint32_t uiCommand, ppu_address_t uiArgument0, uint32_t uiArgument1) =0; + +///check for messages from SPUs + virtual void waitForResponse(unsigned int *puiArgument0, unsigned int *puiArgument1) =0; + +///start the spus (can be called at the beginning of each frame, to make sure that the right SPU program is loaded) + virtual void startSPU() =0; + +///tell the task scheduler we are done with the SPU tasks + virtual void stopSPU()=0; + + ///tell the task scheduler to use no more than numTasks tasks + virtual void setNumTasks(int numTasks)=0; + + virtual int getNumTasks() const = 0; + +}; + +#endif //THREAD_SUPPORT_INTERFACE_H + diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/vectormath/scalar/cpp/mat_aos.h b/Engine/lib/bullet/src/BulletMultiThreaded/vectormath/scalar/cpp/mat_aos.h new file mode 100644 index 000000000..5d5d012d9 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/vectormath/scalar/cpp/mat_aos.h @@ -0,0 +1,1643 @@ +/* + Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Sony Computer Entertainment Inc nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _VECTORMATH_MAT_AOS_CPP_H +#define _VECTORMATH_MAT_AOS_CPP_H + +namespace Vectormath { +namespace Aos { + +//----------------------------------------------------------------------------- +// Constants + +#define _VECTORMATH_PI_OVER_2 1.570796327f + +//----------------------------------------------------------------------------- +// Definitions + +inline Matrix3::Matrix3( const Matrix3 & mat ) +{ + mCol0 = mat.mCol0; + mCol1 = mat.mCol1; + mCol2 = mat.mCol2; +} + +inline Matrix3::Matrix3( float scalar ) +{ + mCol0 = Vector3( scalar ); + mCol1 = Vector3( scalar ); + mCol2 = Vector3( scalar ); +} + +inline Matrix3::Matrix3( const Quat & unitQuat ) +{ + float qx, qy, qz, qw, qx2, qy2, qz2, qxqx2, qyqy2, qzqz2, qxqy2, qyqz2, qzqw2, qxqz2, qyqw2, qxqw2; + qx = unitQuat.getX(); + qy = unitQuat.getY(); + qz = unitQuat.getZ(); + qw = unitQuat.getW(); + qx2 = ( qx + qx ); + qy2 = ( qy + qy ); + qz2 = ( qz + qz ); + qxqx2 = ( qx * qx2 ); + qxqy2 = ( qx * qy2 ); + qxqz2 = ( qx * qz2 ); + qxqw2 = ( qw * qx2 ); + qyqy2 = ( qy * qy2 ); + qyqz2 = ( qy * qz2 ); + qyqw2 = ( qw * qy2 ); + qzqz2 = ( qz * qz2 ); + qzqw2 = ( qw * qz2 ); + mCol0 = Vector3( ( ( 1.0f - qyqy2 ) - qzqz2 ), ( qxqy2 + qzqw2 ), ( qxqz2 - qyqw2 ) ); + mCol1 = Vector3( ( qxqy2 - qzqw2 ), ( ( 1.0f - qxqx2 ) - qzqz2 ), ( qyqz2 + qxqw2 ) ); + mCol2 = Vector3( ( qxqz2 + qyqw2 ), ( qyqz2 - qxqw2 ), ( ( 1.0f - qxqx2 ) - qyqy2 ) ); +} + +inline Matrix3::Matrix3( const Vector3 & _col0, const Vector3 & _col1, const Vector3 & _col2 ) +{ + mCol0 = _col0; + mCol1 = _col1; + mCol2 = _col2; +} + +inline Matrix3 & Matrix3::setCol0( const Vector3 & _col0 ) +{ + mCol0 = _col0; + return *this; +} + +inline Matrix3 & Matrix3::setCol1( const Vector3 & _col1 ) +{ + mCol1 = _col1; + return *this; +} + +inline Matrix3 & Matrix3::setCol2( const Vector3 & _col2 ) +{ + mCol2 = _col2; + return *this; +} + +inline Matrix3 & Matrix3::setCol( int col, const Vector3 & vec ) +{ + *(&mCol0 + col) = vec; + return *this; +} + +inline Matrix3 & Matrix3::setRow( int row, const Vector3 & vec ) +{ + mCol0.setElem( row, vec.getElem( 0 ) ); + mCol1.setElem( row, vec.getElem( 1 ) ); + mCol2.setElem( row, vec.getElem( 2 ) ); + return *this; +} + +inline Matrix3 & Matrix3::setElem( int col, int row, float val ) +{ + Vector3 tmpV3_0; + tmpV3_0 = this->getCol( col ); + tmpV3_0.setElem( row, val ); + this->setCol( col, tmpV3_0 ); + return *this; +} + +inline float Matrix3::getElem( int col, int row ) const +{ + return this->getCol( col ).getElem( row ); +} + +inline const Vector3 Matrix3::getCol0( ) const +{ + return mCol0; +} + +inline const Vector3 Matrix3::getCol1( ) const +{ + return mCol1; +} + +inline const Vector3 Matrix3::getCol2( ) const +{ + return mCol2; +} + +inline const Vector3 Matrix3::getCol( int col ) const +{ + return *(&mCol0 + col); +} + +inline const Vector3 Matrix3::getRow( int row ) const +{ + return Vector3( mCol0.getElem( row ), mCol1.getElem( row ), mCol2.getElem( row ) ); +} + +inline Vector3 & Matrix3::operator []( int col ) +{ + return *(&mCol0 + col); +} + +inline const Vector3 Matrix3::operator []( int col ) const +{ + return *(&mCol0 + col); +} + +inline Matrix3 & Matrix3::operator =( const Matrix3 & mat ) +{ + mCol0 = mat.mCol0; + mCol1 = mat.mCol1; + mCol2 = mat.mCol2; + return *this; +} + +inline const Matrix3 transpose( const Matrix3 & mat ) +{ + return Matrix3( + Vector3( mat.getCol0().getX(), mat.getCol1().getX(), mat.getCol2().getX() ), + Vector3( mat.getCol0().getY(), mat.getCol1().getY(), mat.getCol2().getY() ), + Vector3( mat.getCol0().getZ(), mat.getCol1().getZ(), mat.getCol2().getZ() ) + ); +} + +inline const Matrix3 inverse( const Matrix3 & mat ) +{ + Vector3 tmp0, tmp1, tmp2; + float detinv; + tmp0 = cross( mat.getCol1(), mat.getCol2() ); + tmp1 = cross( mat.getCol2(), mat.getCol0() ); + tmp2 = cross( mat.getCol0(), mat.getCol1() ); + detinv = ( 1.0f / dot( mat.getCol2(), tmp2 ) ); + return Matrix3( + Vector3( ( tmp0.getX() * detinv ), ( tmp1.getX() * detinv ), ( tmp2.getX() * detinv ) ), + Vector3( ( tmp0.getY() * detinv ), ( tmp1.getY() * detinv ), ( tmp2.getY() * detinv ) ), + Vector3( ( tmp0.getZ() * detinv ), ( tmp1.getZ() * detinv ), ( tmp2.getZ() * detinv ) ) + ); +} + +inline float determinant( const Matrix3 & mat ) +{ + return dot( mat.getCol2(), cross( mat.getCol0(), mat.getCol1() ) ); +} + +inline const Matrix3 Matrix3::operator +( const Matrix3 & mat ) const +{ + return Matrix3( + ( mCol0 + mat.mCol0 ), + ( mCol1 + mat.mCol1 ), + ( mCol2 + mat.mCol2 ) + ); +} + +inline const Matrix3 Matrix3::operator -( const Matrix3 & mat ) const +{ + return Matrix3( + ( mCol0 - mat.mCol0 ), + ( mCol1 - mat.mCol1 ), + ( mCol2 - mat.mCol2 ) + ); +} + +inline Matrix3 & Matrix3::operator +=( const Matrix3 & mat ) +{ + *this = *this + mat; + return *this; +} + +inline Matrix3 & Matrix3::operator -=( const Matrix3 & mat ) +{ + *this = *this - mat; + return *this; +} + +inline const Matrix3 Matrix3::operator -( ) const +{ + return Matrix3( + ( -mCol0 ), + ( -mCol1 ), + ( -mCol2 ) + ); +} + +inline const Matrix3 absPerElem( const Matrix3 & mat ) +{ + return Matrix3( + absPerElem( mat.getCol0() ), + absPerElem( mat.getCol1() ), + absPerElem( mat.getCol2() ) + ); +} + +inline const Matrix3 Matrix3::operator *( float scalar ) const +{ + return Matrix3( + ( mCol0 * scalar ), + ( mCol1 * scalar ), + ( mCol2 * scalar ) + ); +} + +inline Matrix3 & Matrix3::operator *=( float scalar ) +{ + *this = *this * scalar; + return *this; +} + +inline const Matrix3 operator *( float scalar, const Matrix3 & mat ) +{ + return mat * scalar; +} + +inline const Vector3 Matrix3::operator *( const Vector3 & vec ) const +{ + return Vector3( + ( ( ( mCol0.getX() * vec.getX() ) + ( mCol1.getX() * vec.getY() ) ) + ( mCol2.getX() * vec.getZ() ) ), + ( ( ( mCol0.getY() * vec.getX() ) + ( mCol1.getY() * vec.getY() ) ) + ( mCol2.getY() * vec.getZ() ) ), + ( ( ( mCol0.getZ() * vec.getX() ) + ( mCol1.getZ() * vec.getY() ) ) + ( mCol2.getZ() * vec.getZ() ) ) + ); +} + +inline const Matrix3 Matrix3::operator *( const Matrix3 & mat ) const +{ + return Matrix3( + ( *this * mat.mCol0 ), + ( *this * mat.mCol1 ), + ( *this * mat.mCol2 ) + ); +} + +inline Matrix3 & Matrix3::operator *=( const Matrix3 & mat ) +{ + *this = *this * mat; + return *this; +} + +inline const Matrix3 mulPerElem( const Matrix3 & mat0, const Matrix3 & mat1 ) +{ + return Matrix3( + mulPerElem( mat0.getCol0(), mat1.getCol0() ), + mulPerElem( mat0.getCol1(), mat1.getCol1() ), + mulPerElem( mat0.getCol2(), mat1.getCol2() ) + ); +} + +inline const Matrix3 Matrix3::identity( ) +{ + return Matrix3( + Vector3::xAxis( ), + Vector3::yAxis( ), + Vector3::zAxis( ) + ); +} + +inline const Matrix3 Matrix3::rotationX( float radians ) +{ + float s, c; + s = sinf( radians ); + c = cosf( radians ); + return Matrix3( + Vector3::xAxis( ), + Vector3( 0.0f, c, s ), + Vector3( 0.0f, -s, c ) + ); +} + +inline const Matrix3 Matrix3::rotationY( float radians ) +{ + float s, c; + s = sinf( radians ); + c = cosf( radians ); + return Matrix3( + Vector3( c, 0.0f, -s ), + Vector3::yAxis( ), + Vector3( s, 0.0f, c ) + ); +} + +inline const Matrix3 Matrix3::rotationZ( float radians ) +{ + float s, c; + s = sinf( radians ); + c = cosf( radians ); + return Matrix3( + Vector3( c, s, 0.0f ), + Vector3( -s, c, 0.0f ), + Vector3::zAxis( ) + ); +} + +inline const Matrix3 Matrix3::rotationZYX( const Vector3 & radiansXYZ ) +{ + float sX, cX, sY, cY, sZ, cZ, tmp0, tmp1; + sX = sinf( radiansXYZ.getX() ); + cX = cosf( radiansXYZ.getX() ); + sY = sinf( radiansXYZ.getY() ); + cY = cosf( radiansXYZ.getY() ); + sZ = sinf( radiansXYZ.getZ() ); + cZ = cosf( radiansXYZ.getZ() ); + tmp0 = ( cZ * sY ); + tmp1 = ( sZ * sY ); + return Matrix3( + Vector3( ( cZ * cY ), ( sZ * cY ), -sY ), + Vector3( ( ( tmp0 * sX ) - ( sZ * cX ) ), ( ( tmp1 * sX ) + ( cZ * cX ) ), ( cY * sX ) ), + Vector3( ( ( tmp0 * cX ) + ( sZ * sX ) ), ( ( tmp1 * cX ) - ( cZ * sX ) ), ( cY * cX ) ) + ); +} + +inline const Matrix3 Matrix3::rotation( float radians, const Vector3 & unitVec ) +{ + float x, y, z, s, c, oneMinusC, xy, yz, zx; + s = sinf( radians ); + c = cosf( radians ); + x = unitVec.getX(); + y = unitVec.getY(); + z = unitVec.getZ(); + xy = ( x * y ); + yz = ( y * z ); + zx = ( z * x ); + oneMinusC = ( 1.0f - c ); + return Matrix3( + Vector3( ( ( ( x * x ) * oneMinusC ) + c ), ( ( xy * oneMinusC ) + ( z * s ) ), ( ( zx * oneMinusC ) - ( y * s ) ) ), + Vector3( ( ( xy * oneMinusC ) - ( z * s ) ), ( ( ( y * y ) * oneMinusC ) + c ), ( ( yz * oneMinusC ) + ( x * s ) ) ), + Vector3( ( ( zx * oneMinusC ) + ( y * s ) ), ( ( yz * oneMinusC ) - ( x * s ) ), ( ( ( z * z ) * oneMinusC ) + c ) ) + ); +} + +inline const Matrix3 Matrix3::rotation( const Quat & unitQuat ) +{ + return Matrix3( unitQuat ); +} + +inline const Matrix3 Matrix3::scale( const Vector3 & scaleVec ) +{ + return Matrix3( + Vector3( scaleVec.getX(), 0.0f, 0.0f ), + Vector3( 0.0f, scaleVec.getY(), 0.0f ), + Vector3( 0.0f, 0.0f, scaleVec.getZ() ) + ); +} + +inline const Matrix3 appendScale( const Matrix3 & mat, const Vector3 & scaleVec ) +{ + return Matrix3( + ( mat.getCol0() * scaleVec.getX( ) ), + ( mat.getCol1() * scaleVec.getY( ) ), + ( mat.getCol2() * scaleVec.getZ( ) ) + ); +} + +inline const Matrix3 prependScale( const Vector3 & scaleVec, const Matrix3 & mat ) +{ + return Matrix3( + mulPerElem( mat.getCol0(), scaleVec ), + mulPerElem( mat.getCol1(), scaleVec ), + mulPerElem( mat.getCol2(), scaleVec ) + ); +} + +inline const Matrix3 select( const Matrix3 & mat0, const Matrix3 & mat1, bool select1 ) +{ + return Matrix3( + select( mat0.getCol0(), mat1.getCol0(), select1 ), + select( mat0.getCol1(), mat1.getCol1(), select1 ), + select( mat0.getCol2(), mat1.getCol2(), select1 ) + ); +} + +#ifdef _VECTORMATH_DEBUG + +inline void print( const Matrix3 & mat ) +{ + print( mat.getRow( 0 ) ); + print( mat.getRow( 1 ) ); + print( mat.getRow( 2 ) ); +} + +inline void print( const Matrix3 & mat, const char * name ) +{ + printf("%s:\n", name); + print( mat ); +} + +#endif + +inline Matrix4::Matrix4( const Matrix4 & mat ) +{ + mCol0 = mat.mCol0; + mCol1 = mat.mCol1; + mCol2 = mat.mCol2; + mCol3 = mat.mCol3; +} + +inline Matrix4::Matrix4( float scalar ) +{ + mCol0 = Vector4( scalar ); + mCol1 = Vector4( scalar ); + mCol2 = Vector4( scalar ); + mCol3 = Vector4( scalar ); +} + +inline Matrix4::Matrix4( const Transform3 & mat ) +{ + mCol0 = Vector4( mat.getCol0(), 0.0f ); + mCol1 = Vector4( mat.getCol1(), 0.0f ); + mCol2 = Vector4( mat.getCol2(), 0.0f ); + mCol3 = Vector4( mat.getCol3(), 1.0f ); +} + +inline Matrix4::Matrix4( const Vector4 & _col0, const Vector4 & _col1, const Vector4 & _col2, const Vector4 & _col3 ) +{ + mCol0 = _col0; + mCol1 = _col1; + mCol2 = _col2; + mCol3 = _col3; +} + +inline Matrix4::Matrix4( const Matrix3 & mat, const Vector3 & translateVec ) +{ + mCol0 = Vector4( mat.getCol0(), 0.0f ); + mCol1 = Vector4( mat.getCol1(), 0.0f ); + mCol2 = Vector4( mat.getCol2(), 0.0f ); + mCol3 = Vector4( translateVec, 1.0f ); +} + +inline Matrix4::Matrix4( const Quat & unitQuat, const Vector3 & translateVec ) +{ + Matrix3 mat; + mat = Matrix3( unitQuat ); + mCol0 = Vector4( mat.getCol0(), 0.0f ); + mCol1 = Vector4( mat.getCol1(), 0.0f ); + mCol2 = Vector4( mat.getCol2(), 0.0f ); + mCol3 = Vector4( translateVec, 1.0f ); +} + +inline Matrix4 & Matrix4::setCol0( const Vector4 & _col0 ) +{ + mCol0 = _col0; + return *this; +} + +inline Matrix4 & Matrix4::setCol1( const Vector4 & _col1 ) +{ + mCol1 = _col1; + return *this; +} + +inline Matrix4 & Matrix4::setCol2( const Vector4 & _col2 ) +{ + mCol2 = _col2; + return *this; +} + +inline Matrix4 & Matrix4::setCol3( const Vector4 & _col3 ) +{ + mCol3 = _col3; + return *this; +} + +inline Matrix4 & Matrix4::setCol( int col, const Vector4 & vec ) +{ + *(&mCol0 + col) = vec; + return *this; +} + +inline Matrix4 & Matrix4::setRow( int row, const Vector4 & vec ) +{ + mCol0.setElem( row, vec.getElem( 0 ) ); + mCol1.setElem( row, vec.getElem( 1 ) ); + mCol2.setElem( row, vec.getElem( 2 ) ); + mCol3.setElem( row, vec.getElem( 3 ) ); + return *this; +} + +inline Matrix4 & Matrix4::setElem( int col, int row, float val ) +{ + Vector4 tmpV3_0; + tmpV3_0 = this->getCol( col ); + tmpV3_0.setElem( row, val ); + this->setCol( col, tmpV3_0 ); + return *this; +} + +inline float Matrix4::getElem( int col, int row ) const +{ + return this->getCol( col ).getElem( row ); +} + +inline const Vector4 Matrix4::getCol0( ) const +{ + return mCol0; +} + +inline const Vector4 Matrix4::getCol1( ) const +{ + return mCol1; +} + +inline const Vector4 Matrix4::getCol2( ) const +{ + return mCol2; +} + +inline const Vector4 Matrix4::getCol3( ) const +{ + return mCol3; +} + +inline const Vector4 Matrix4::getCol( int col ) const +{ + return *(&mCol0 + col); +} + +inline const Vector4 Matrix4::getRow( int row ) const +{ + return Vector4( mCol0.getElem( row ), mCol1.getElem( row ), mCol2.getElem( row ), mCol3.getElem( row ) ); +} + +inline Vector4 & Matrix4::operator []( int col ) +{ + return *(&mCol0 + col); +} + +inline const Vector4 Matrix4::operator []( int col ) const +{ + return *(&mCol0 + col); +} + +inline Matrix4 & Matrix4::operator =( const Matrix4 & mat ) +{ + mCol0 = mat.mCol0; + mCol1 = mat.mCol1; + mCol2 = mat.mCol2; + mCol3 = mat.mCol3; + return *this; +} + +inline const Matrix4 transpose( const Matrix4 & mat ) +{ + return Matrix4( + Vector4( mat.getCol0().getX(), mat.getCol1().getX(), mat.getCol2().getX(), mat.getCol3().getX() ), + Vector4( mat.getCol0().getY(), mat.getCol1().getY(), mat.getCol2().getY(), mat.getCol3().getY() ), + Vector4( mat.getCol0().getZ(), mat.getCol1().getZ(), mat.getCol2().getZ(), mat.getCol3().getZ() ), + Vector4( mat.getCol0().getW(), mat.getCol1().getW(), mat.getCol2().getW(), mat.getCol3().getW() ) + ); +} + +inline const Matrix4 inverse( const Matrix4 & mat ) +{ + Vector4 res0, res1, res2, res3; + float mA, mB, mC, mD, mE, mF, mG, mH, mI, mJ, mK, mL, mM, mN, mO, mP, tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, detInv; + mA = mat.getCol0().getX(); + mB = mat.getCol0().getY(); + mC = mat.getCol0().getZ(); + mD = mat.getCol0().getW(); + mE = mat.getCol1().getX(); + mF = mat.getCol1().getY(); + mG = mat.getCol1().getZ(); + mH = mat.getCol1().getW(); + mI = mat.getCol2().getX(); + mJ = mat.getCol2().getY(); + mK = mat.getCol2().getZ(); + mL = mat.getCol2().getW(); + mM = mat.getCol3().getX(); + mN = mat.getCol3().getY(); + mO = mat.getCol3().getZ(); + mP = mat.getCol3().getW(); + tmp0 = ( ( mK * mD ) - ( mC * mL ) ); + tmp1 = ( ( mO * mH ) - ( mG * mP ) ); + tmp2 = ( ( mB * mK ) - ( mJ * mC ) ); + tmp3 = ( ( mF * mO ) - ( mN * mG ) ); + tmp4 = ( ( mJ * mD ) - ( mB * mL ) ); + tmp5 = ( ( mN * mH ) - ( mF * mP ) ); + res0.setX( ( ( ( mJ * tmp1 ) - ( mL * tmp3 ) ) - ( mK * tmp5 ) ) ); + res0.setY( ( ( ( mN * tmp0 ) - ( mP * tmp2 ) ) - ( mO * tmp4 ) ) ); + res0.setZ( ( ( ( mD * tmp3 ) + ( mC * tmp5 ) ) - ( mB * tmp1 ) ) ); + res0.setW( ( ( ( mH * tmp2 ) + ( mG * tmp4 ) ) - ( mF * tmp0 ) ) ); + detInv = ( 1.0f / ( ( ( ( mA * res0.getX() ) + ( mE * res0.getY() ) ) + ( mI * res0.getZ() ) ) + ( mM * res0.getW() ) ) ); + res1.setX( ( mI * tmp1 ) ); + res1.setY( ( mM * tmp0 ) ); + res1.setZ( ( mA * tmp1 ) ); + res1.setW( ( mE * tmp0 ) ); + res3.setX( ( mI * tmp3 ) ); + res3.setY( ( mM * tmp2 ) ); + res3.setZ( ( mA * tmp3 ) ); + res3.setW( ( mE * tmp2 ) ); + res2.setX( ( mI * tmp5 ) ); + res2.setY( ( mM * tmp4 ) ); + res2.setZ( ( mA * tmp5 ) ); + res2.setW( ( mE * tmp4 ) ); + tmp0 = ( ( mI * mB ) - ( mA * mJ ) ); + tmp1 = ( ( mM * mF ) - ( mE * mN ) ); + tmp2 = ( ( mI * mD ) - ( mA * mL ) ); + tmp3 = ( ( mM * mH ) - ( mE * mP ) ); + tmp4 = ( ( mI * mC ) - ( mA * mK ) ); + tmp5 = ( ( mM * mG ) - ( mE * mO ) ); + res2.setX( ( ( ( mL * tmp1 ) - ( mJ * tmp3 ) ) + res2.getX() ) ); + res2.setY( ( ( ( mP * tmp0 ) - ( mN * tmp2 ) ) + res2.getY() ) ); + res2.setZ( ( ( ( mB * tmp3 ) - ( mD * tmp1 ) ) - res2.getZ() ) ); + res2.setW( ( ( ( mF * tmp2 ) - ( mH * tmp0 ) ) - res2.getW() ) ); + res3.setX( ( ( ( mJ * tmp5 ) - ( mK * tmp1 ) ) + res3.getX() ) ); + res3.setY( ( ( ( mN * tmp4 ) - ( mO * tmp0 ) ) + res3.getY() ) ); + res3.setZ( ( ( ( mC * tmp1 ) - ( mB * tmp5 ) ) - res3.getZ() ) ); + res3.setW( ( ( ( mG * tmp0 ) - ( mF * tmp4 ) ) - res3.getW() ) ); + res1.setX( ( ( ( mK * tmp3 ) - ( mL * tmp5 ) ) - res1.getX() ) ); + res1.setY( ( ( ( mO * tmp2 ) - ( mP * tmp4 ) ) - res1.getY() ) ); + res1.setZ( ( ( ( mD * tmp5 ) - ( mC * tmp3 ) ) + res1.getZ() ) ); + res1.setW( ( ( ( mH * tmp4 ) - ( mG * tmp2 ) ) + res1.getW() ) ); + return Matrix4( + ( res0 * detInv ), + ( res1 * detInv ), + ( res2 * detInv ), + ( res3 * detInv ) + ); +} + +inline const Matrix4 affineInverse( const Matrix4 & mat ) +{ + Transform3 affineMat; + affineMat.setCol0( mat.getCol0().getXYZ( ) ); + affineMat.setCol1( mat.getCol1().getXYZ( ) ); + affineMat.setCol2( mat.getCol2().getXYZ( ) ); + affineMat.setCol3( mat.getCol3().getXYZ( ) ); + return Matrix4( inverse( affineMat ) ); +} + +inline const Matrix4 orthoInverse( const Matrix4 & mat ) +{ + Transform3 affineMat; + affineMat.setCol0( mat.getCol0().getXYZ( ) ); + affineMat.setCol1( mat.getCol1().getXYZ( ) ); + affineMat.setCol2( mat.getCol2().getXYZ( ) ); + affineMat.setCol3( mat.getCol3().getXYZ( ) ); + return Matrix4( orthoInverse( affineMat ) ); +} + +inline float determinant( const Matrix4 & mat ) +{ + float dx, dy, dz, dw, mA, mB, mC, mD, mE, mF, mG, mH, mI, mJ, mK, mL, mM, mN, mO, mP, tmp0, tmp1, tmp2, tmp3, tmp4, tmp5; + mA = mat.getCol0().getX(); + mB = mat.getCol0().getY(); + mC = mat.getCol0().getZ(); + mD = mat.getCol0().getW(); + mE = mat.getCol1().getX(); + mF = mat.getCol1().getY(); + mG = mat.getCol1().getZ(); + mH = mat.getCol1().getW(); + mI = mat.getCol2().getX(); + mJ = mat.getCol2().getY(); + mK = mat.getCol2().getZ(); + mL = mat.getCol2().getW(); + mM = mat.getCol3().getX(); + mN = mat.getCol3().getY(); + mO = mat.getCol3().getZ(); + mP = mat.getCol3().getW(); + tmp0 = ( ( mK * mD ) - ( mC * mL ) ); + tmp1 = ( ( mO * mH ) - ( mG * mP ) ); + tmp2 = ( ( mB * mK ) - ( mJ * mC ) ); + tmp3 = ( ( mF * mO ) - ( mN * mG ) ); + tmp4 = ( ( mJ * mD ) - ( mB * mL ) ); + tmp5 = ( ( mN * mH ) - ( mF * mP ) ); + dx = ( ( ( mJ * tmp1 ) - ( mL * tmp3 ) ) - ( mK * tmp5 ) ); + dy = ( ( ( mN * tmp0 ) - ( mP * tmp2 ) ) - ( mO * tmp4 ) ); + dz = ( ( ( mD * tmp3 ) + ( mC * tmp5 ) ) - ( mB * tmp1 ) ); + dw = ( ( ( mH * tmp2 ) + ( mG * tmp4 ) ) - ( mF * tmp0 ) ); + return ( ( ( ( mA * dx ) + ( mE * dy ) ) + ( mI * dz ) ) + ( mM * dw ) ); +} + +inline const Matrix4 Matrix4::operator +( const Matrix4 & mat ) const +{ + return Matrix4( + ( mCol0 + mat.mCol0 ), + ( mCol1 + mat.mCol1 ), + ( mCol2 + mat.mCol2 ), + ( mCol3 + mat.mCol3 ) + ); +} + +inline const Matrix4 Matrix4::operator -( const Matrix4 & mat ) const +{ + return Matrix4( + ( mCol0 - mat.mCol0 ), + ( mCol1 - mat.mCol1 ), + ( mCol2 - mat.mCol2 ), + ( mCol3 - mat.mCol3 ) + ); +} + +inline Matrix4 & Matrix4::operator +=( const Matrix4 & mat ) +{ + *this = *this + mat; + return *this; +} + +inline Matrix4 & Matrix4::operator -=( const Matrix4 & mat ) +{ + *this = *this - mat; + return *this; +} + +inline const Matrix4 Matrix4::operator -( ) const +{ + return Matrix4( + ( -mCol0 ), + ( -mCol1 ), + ( -mCol2 ), + ( -mCol3 ) + ); +} + +inline const Matrix4 absPerElem( const Matrix4 & mat ) +{ + return Matrix4( + absPerElem( mat.getCol0() ), + absPerElem( mat.getCol1() ), + absPerElem( mat.getCol2() ), + absPerElem( mat.getCol3() ) + ); +} + +inline const Matrix4 Matrix4::operator *( float scalar ) const +{ + return Matrix4( + ( mCol0 * scalar ), + ( mCol1 * scalar ), + ( mCol2 * scalar ), + ( mCol3 * scalar ) + ); +} + +inline Matrix4 & Matrix4::operator *=( float scalar ) +{ + *this = *this * scalar; + return *this; +} + +inline const Matrix4 operator *( float scalar, const Matrix4 & mat ) +{ + return mat * scalar; +} + +inline const Vector4 Matrix4::operator *( const Vector4 & vec ) const +{ + return Vector4( + ( ( ( ( mCol0.getX() * vec.getX() ) + ( mCol1.getX() * vec.getY() ) ) + ( mCol2.getX() * vec.getZ() ) ) + ( mCol3.getX() * vec.getW() ) ), + ( ( ( ( mCol0.getY() * vec.getX() ) + ( mCol1.getY() * vec.getY() ) ) + ( mCol2.getY() * vec.getZ() ) ) + ( mCol3.getY() * vec.getW() ) ), + ( ( ( ( mCol0.getZ() * vec.getX() ) + ( mCol1.getZ() * vec.getY() ) ) + ( mCol2.getZ() * vec.getZ() ) ) + ( mCol3.getZ() * vec.getW() ) ), + ( ( ( ( mCol0.getW() * vec.getX() ) + ( mCol1.getW() * vec.getY() ) ) + ( mCol2.getW() * vec.getZ() ) ) + ( mCol3.getW() * vec.getW() ) ) + ); +} + +inline const Vector4 Matrix4::operator *( const Vector3 & vec ) const +{ + return Vector4( + ( ( ( mCol0.getX() * vec.getX() ) + ( mCol1.getX() * vec.getY() ) ) + ( mCol2.getX() * vec.getZ() ) ), + ( ( ( mCol0.getY() * vec.getX() ) + ( mCol1.getY() * vec.getY() ) ) + ( mCol2.getY() * vec.getZ() ) ), + ( ( ( mCol0.getZ() * vec.getX() ) + ( mCol1.getZ() * vec.getY() ) ) + ( mCol2.getZ() * vec.getZ() ) ), + ( ( ( mCol0.getW() * vec.getX() ) + ( mCol1.getW() * vec.getY() ) ) + ( mCol2.getW() * vec.getZ() ) ) + ); +} + +inline const Vector4 Matrix4::operator *( const Point3 & pnt ) const +{ + return Vector4( + ( ( ( ( mCol0.getX() * pnt.getX() ) + ( mCol1.getX() * pnt.getY() ) ) + ( mCol2.getX() * pnt.getZ() ) ) + mCol3.getX() ), + ( ( ( ( mCol0.getY() * pnt.getX() ) + ( mCol1.getY() * pnt.getY() ) ) + ( mCol2.getY() * pnt.getZ() ) ) + mCol3.getY() ), + ( ( ( ( mCol0.getZ() * pnt.getX() ) + ( mCol1.getZ() * pnt.getY() ) ) + ( mCol2.getZ() * pnt.getZ() ) ) + mCol3.getZ() ), + ( ( ( ( mCol0.getW() * pnt.getX() ) + ( mCol1.getW() * pnt.getY() ) ) + ( mCol2.getW() * pnt.getZ() ) ) + mCol3.getW() ) + ); +} + +inline const Matrix4 Matrix4::operator *( const Matrix4 & mat ) const +{ + return Matrix4( + ( *this * mat.mCol0 ), + ( *this * mat.mCol1 ), + ( *this * mat.mCol2 ), + ( *this * mat.mCol3 ) + ); +} + +inline Matrix4 & Matrix4::operator *=( const Matrix4 & mat ) +{ + *this = *this * mat; + return *this; +} + +inline const Matrix4 Matrix4::operator *( const Transform3 & tfrm ) const +{ + return Matrix4( + ( *this * tfrm.getCol0() ), + ( *this * tfrm.getCol1() ), + ( *this * tfrm.getCol2() ), + ( *this * Point3( tfrm.getCol3() ) ) + ); +} + +inline Matrix4 & Matrix4::operator *=( const Transform3 & tfrm ) +{ + *this = *this * tfrm; + return *this; +} + +inline const Matrix4 mulPerElem( const Matrix4 & mat0, const Matrix4 & mat1 ) +{ + return Matrix4( + mulPerElem( mat0.getCol0(), mat1.getCol0() ), + mulPerElem( mat0.getCol1(), mat1.getCol1() ), + mulPerElem( mat0.getCol2(), mat1.getCol2() ), + mulPerElem( mat0.getCol3(), mat1.getCol3() ) + ); +} + +inline const Matrix4 Matrix4::identity( ) +{ + return Matrix4( + Vector4::xAxis( ), + Vector4::yAxis( ), + Vector4::zAxis( ), + Vector4::wAxis( ) + ); +} + +inline Matrix4 & Matrix4::setUpper3x3( const Matrix3 & mat3 ) +{ + mCol0.setXYZ( mat3.getCol0() ); + mCol1.setXYZ( mat3.getCol1() ); + mCol2.setXYZ( mat3.getCol2() ); + return *this; +} + +inline const Matrix3 Matrix4::getUpper3x3( ) const +{ + return Matrix3( + mCol0.getXYZ( ), + mCol1.getXYZ( ), + mCol2.getXYZ( ) + ); +} + +inline Matrix4 & Matrix4::setTranslation( const Vector3 & translateVec ) +{ + mCol3.setXYZ( translateVec ); + return *this; +} + +inline const Vector3 Matrix4::getTranslation( ) const +{ + return mCol3.getXYZ( ); +} + +inline const Matrix4 Matrix4::rotationX( float radians ) +{ + float s, c; + s = sinf( radians ); + c = cosf( radians ); + return Matrix4( + Vector4::xAxis( ), + Vector4( 0.0f, c, s, 0.0f ), + Vector4( 0.0f, -s, c, 0.0f ), + Vector4::wAxis( ) + ); +} + +inline const Matrix4 Matrix4::rotationY( float radians ) +{ + float s, c; + s = sinf( radians ); + c = cosf( radians ); + return Matrix4( + Vector4( c, 0.0f, -s, 0.0f ), + Vector4::yAxis( ), + Vector4( s, 0.0f, c, 0.0f ), + Vector4::wAxis( ) + ); +} + +inline const Matrix4 Matrix4::rotationZ( float radians ) +{ + float s, c; + s = sinf( radians ); + c = cosf( radians ); + return Matrix4( + Vector4( c, s, 0.0f, 0.0f ), + Vector4( -s, c, 0.0f, 0.0f ), + Vector4::zAxis( ), + Vector4::wAxis( ) + ); +} + +inline const Matrix4 Matrix4::rotationZYX( const Vector3 & radiansXYZ ) +{ + float sX, cX, sY, cY, sZ, cZ, tmp0, tmp1; + sX = sinf( radiansXYZ.getX() ); + cX = cosf( radiansXYZ.getX() ); + sY = sinf( radiansXYZ.getY() ); + cY = cosf( radiansXYZ.getY() ); + sZ = sinf( radiansXYZ.getZ() ); + cZ = cosf( radiansXYZ.getZ() ); + tmp0 = ( cZ * sY ); + tmp1 = ( sZ * sY ); + return Matrix4( + Vector4( ( cZ * cY ), ( sZ * cY ), -sY, 0.0f ), + Vector4( ( ( tmp0 * sX ) - ( sZ * cX ) ), ( ( tmp1 * sX ) + ( cZ * cX ) ), ( cY * sX ), 0.0f ), + Vector4( ( ( tmp0 * cX ) + ( sZ * sX ) ), ( ( tmp1 * cX ) - ( cZ * sX ) ), ( cY * cX ), 0.0f ), + Vector4::wAxis( ) + ); +} + +inline const Matrix4 Matrix4::rotation( float radians, const Vector3 & unitVec ) +{ + float x, y, z, s, c, oneMinusC, xy, yz, zx; + s = sinf( radians ); + c = cosf( radians ); + x = unitVec.getX(); + y = unitVec.getY(); + z = unitVec.getZ(); + xy = ( x * y ); + yz = ( y * z ); + zx = ( z * x ); + oneMinusC = ( 1.0f - c ); + return Matrix4( + Vector4( ( ( ( x * x ) * oneMinusC ) + c ), ( ( xy * oneMinusC ) + ( z * s ) ), ( ( zx * oneMinusC ) - ( y * s ) ), 0.0f ), + Vector4( ( ( xy * oneMinusC ) - ( z * s ) ), ( ( ( y * y ) * oneMinusC ) + c ), ( ( yz * oneMinusC ) + ( x * s ) ), 0.0f ), + Vector4( ( ( zx * oneMinusC ) + ( y * s ) ), ( ( yz * oneMinusC ) - ( x * s ) ), ( ( ( z * z ) * oneMinusC ) + c ), 0.0f ), + Vector4::wAxis( ) + ); +} + +inline const Matrix4 Matrix4::rotation( const Quat & unitQuat ) +{ + return Matrix4( Transform3::rotation( unitQuat ) ); +} + +inline const Matrix4 Matrix4::scale( const Vector3 & scaleVec ) +{ + return Matrix4( + Vector4( scaleVec.getX(), 0.0f, 0.0f, 0.0f ), + Vector4( 0.0f, scaleVec.getY(), 0.0f, 0.0f ), + Vector4( 0.0f, 0.0f, scaleVec.getZ(), 0.0f ), + Vector4::wAxis( ) + ); +} + +inline const Matrix4 appendScale( const Matrix4 & mat, const Vector3 & scaleVec ) +{ + return Matrix4( + ( mat.getCol0() * scaleVec.getX( ) ), + ( mat.getCol1() * scaleVec.getY( ) ), + ( mat.getCol2() * scaleVec.getZ( ) ), + mat.getCol3() + ); +} + +inline const Matrix4 prependScale( const Vector3 & scaleVec, const Matrix4 & mat ) +{ + Vector4 scale4; + scale4 = Vector4( scaleVec, 1.0f ); + return Matrix4( + mulPerElem( mat.getCol0(), scale4 ), + mulPerElem( mat.getCol1(), scale4 ), + mulPerElem( mat.getCol2(), scale4 ), + mulPerElem( mat.getCol3(), scale4 ) + ); +} + +inline const Matrix4 Matrix4::translation( const Vector3 & translateVec ) +{ + return Matrix4( + Vector4::xAxis( ), + Vector4::yAxis( ), + Vector4::zAxis( ), + Vector4( translateVec, 1.0f ) + ); +} + +inline const Matrix4 Matrix4::lookAt( const Point3 & eyePos, const Point3 & lookAtPos, const Vector3 & upVec ) +{ + Matrix4 m4EyeFrame; + Vector3 v3X, v3Y, v3Z; + v3Y = normalize( upVec ); + v3Z = normalize( ( eyePos - lookAtPos ) ); + v3X = normalize( cross( v3Y, v3Z ) ); + v3Y = cross( v3Z, v3X ); + m4EyeFrame = Matrix4( Vector4( v3X ), Vector4( v3Y ), Vector4( v3Z ), Vector4( eyePos ) ); + return orthoInverse( m4EyeFrame ); +} + +inline const Matrix4 Matrix4::perspective( float fovyRadians, float aspect, float zNear, float zFar ) +{ + float f, rangeInv; + f = tanf( ( (float)( _VECTORMATH_PI_OVER_2 ) - ( 0.5f * fovyRadians ) ) ); + rangeInv = ( 1.0f / ( zNear - zFar ) ); + return Matrix4( + Vector4( ( f / aspect ), 0.0f, 0.0f, 0.0f ), + Vector4( 0.0f, f, 0.0f, 0.0f ), + Vector4( 0.0f, 0.0f, ( ( zNear + zFar ) * rangeInv ), -1.0f ), + Vector4( 0.0f, 0.0f, ( ( ( zNear * zFar ) * rangeInv ) * 2.0f ), 0.0f ) + ); +} + +inline const Matrix4 Matrix4::frustum( float left, float right, float bottom, float top, float zNear, float zFar ) +{ + float sum_rl, sum_tb, sum_nf, inv_rl, inv_tb, inv_nf, n2; + sum_rl = ( right + left ); + sum_tb = ( top + bottom ); + sum_nf = ( zNear + zFar ); + inv_rl = ( 1.0f / ( right - left ) ); + inv_tb = ( 1.0f / ( top - bottom ) ); + inv_nf = ( 1.0f / ( zNear - zFar ) ); + n2 = ( zNear + zNear ); + return Matrix4( + Vector4( ( n2 * inv_rl ), 0.0f, 0.0f, 0.0f ), + Vector4( 0.0f, ( n2 * inv_tb ), 0.0f, 0.0f ), + Vector4( ( sum_rl * inv_rl ), ( sum_tb * inv_tb ), ( sum_nf * inv_nf ), -1.0f ), + Vector4( 0.0f, 0.0f, ( ( n2 * inv_nf ) * zFar ), 0.0f ) + ); +} + +inline const Matrix4 Matrix4::orthographic( float left, float right, float bottom, float top, float zNear, float zFar ) +{ + float sum_rl, sum_tb, sum_nf, inv_rl, inv_tb, inv_nf; + sum_rl = ( right + left ); + sum_tb = ( top + bottom ); + sum_nf = ( zNear + zFar ); + inv_rl = ( 1.0f / ( right - left ) ); + inv_tb = ( 1.0f / ( top - bottom ) ); + inv_nf = ( 1.0f / ( zNear - zFar ) ); + return Matrix4( + Vector4( ( inv_rl + inv_rl ), 0.0f, 0.0f, 0.0f ), + Vector4( 0.0f, ( inv_tb + inv_tb ), 0.0f, 0.0f ), + Vector4( 0.0f, 0.0f, ( inv_nf + inv_nf ), 0.0f ), + Vector4( ( -sum_rl * inv_rl ), ( -sum_tb * inv_tb ), ( sum_nf * inv_nf ), 1.0f ) + ); +} + +inline const Matrix4 select( const Matrix4 & mat0, const Matrix4 & mat1, bool select1 ) +{ + return Matrix4( + select( mat0.getCol0(), mat1.getCol0(), select1 ), + select( mat0.getCol1(), mat1.getCol1(), select1 ), + select( mat0.getCol2(), mat1.getCol2(), select1 ), + select( mat0.getCol3(), mat1.getCol3(), select1 ) + ); +} + +#ifdef _VECTORMATH_DEBUG + +inline void print( const Matrix4 & mat ) +{ + print( mat.getRow( 0 ) ); + print( mat.getRow( 1 ) ); + print( mat.getRow( 2 ) ); + print( mat.getRow( 3 ) ); +} + +inline void print( const Matrix4 & mat, const char * name ) +{ + printf("%s:\n", name); + print( mat ); +} + +#endif + +inline Transform3::Transform3( const Transform3 & tfrm ) +{ + mCol0 = tfrm.mCol0; + mCol1 = tfrm.mCol1; + mCol2 = tfrm.mCol2; + mCol3 = tfrm.mCol3; +} + +inline Transform3::Transform3( float scalar ) +{ + mCol0 = Vector3( scalar ); + mCol1 = Vector3( scalar ); + mCol2 = Vector3( scalar ); + mCol3 = Vector3( scalar ); +} + +inline Transform3::Transform3( const Vector3 & _col0, const Vector3 & _col1, const Vector3 & _col2, const Vector3 & _col3 ) +{ + mCol0 = _col0; + mCol1 = _col1; + mCol2 = _col2; + mCol3 = _col3; +} + +inline Transform3::Transform3( const Matrix3 & tfrm, const Vector3 & translateVec ) +{ + this->setUpper3x3( tfrm ); + this->setTranslation( translateVec ); +} + +inline Transform3::Transform3( const Quat & unitQuat, const Vector3 & translateVec ) +{ + this->setUpper3x3( Matrix3( unitQuat ) ); + this->setTranslation( translateVec ); +} + +inline Transform3 & Transform3::setCol0( const Vector3 & _col0 ) +{ + mCol0 = _col0; + return *this; +} + +inline Transform3 & Transform3::setCol1( const Vector3 & _col1 ) +{ + mCol1 = _col1; + return *this; +} + +inline Transform3 & Transform3::setCol2( const Vector3 & _col2 ) +{ + mCol2 = _col2; + return *this; +} + +inline Transform3 & Transform3::setCol3( const Vector3 & _col3 ) +{ + mCol3 = _col3; + return *this; +} + +inline Transform3 & Transform3::setCol( int col, const Vector3 & vec ) +{ + *(&mCol0 + col) = vec; + return *this; +} + +inline Transform3 & Transform3::setRow( int row, const Vector4 & vec ) +{ + mCol0.setElem( row, vec.getElem( 0 ) ); + mCol1.setElem( row, vec.getElem( 1 ) ); + mCol2.setElem( row, vec.getElem( 2 ) ); + mCol3.setElem( row, vec.getElem( 3 ) ); + return *this; +} + +inline Transform3 & Transform3::setElem( int col, int row, float val ) +{ + Vector3 tmpV3_0; + tmpV3_0 = this->getCol( col ); + tmpV3_0.setElem( row, val ); + this->setCol( col, tmpV3_0 ); + return *this; +} + +inline float Transform3::getElem( int col, int row ) const +{ + return this->getCol( col ).getElem( row ); +} + +inline const Vector3 Transform3::getCol0( ) const +{ + return mCol0; +} + +inline const Vector3 Transform3::getCol1( ) const +{ + return mCol1; +} + +inline const Vector3 Transform3::getCol2( ) const +{ + return mCol2; +} + +inline const Vector3 Transform3::getCol3( ) const +{ + return mCol3; +} + +inline const Vector3 Transform3::getCol( int col ) const +{ + return *(&mCol0 + col); +} + +inline const Vector4 Transform3::getRow( int row ) const +{ + return Vector4( mCol0.getElem( row ), mCol1.getElem( row ), mCol2.getElem( row ), mCol3.getElem( row ) ); +} + +inline Vector3 & Transform3::operator []( int col ) +{ + return *(&mCol0 + col); +} + +inline const Vector3 Transform3::operator []( int col ) const +{ + return *(&mCol0 + col); +} + +inline Transform3 & Transform3::operator =( const Transform3 & tfrm ) +{ + mCol0 = tfrm.mCol0; + mCol1 = tfrm.mCol1; + mCol2 = tfrm.mCol2; + mCol3 = tfrm.mCol3; + return *this; +} + +inline const Transform3 inverse( const Transform3 & tfrm ) +{ + Vector3 tmp0, tmp1, tmp2, inv0, inv1, inv2; + float detinv; + tmp0 = cross( tfrm.getCol1(), tfrm.getCol2() ); + tmp1 = cross( tfrm.getCol2(), tfrm.getCol0() ); + tmp2 = cross( tfrm.getCol0(), tfrm.getCol1() ); + detinv = ( 1.0f / dot( tfrm.getCol2(), tmp2 ) ); + inv0 = Vector3( ( tmp0.getX() * detinv ), ( tmp1.getX() * detinv ), ( tmp2.getX() * detinv ) ); + inv1 = Vector3( ( tmp0.getY() * detinv ), ( tmp1.getY() * detinv ), ( tmp2.getY() * detinv ) ); + inv2 = Vector3( ( tmp0.getZ() * detinv ), ( tmp1.getZ() * detinv ), ( tmp2.getZ() * detinv ) ); + return Transform3( + inv0, + inv1, + inv2, + Vector3( ( -( ( inv0 * tfrm.getCol3().getX() ) + ( ( inv1 * tfrm.getCol3().getY() ) + ( inv2 * tfrm.getCol3().getZ() ) ) ) ) ) + ); +} + +inline const Transform3 orthoInverse( const Transform3 & tfrm ) +{ + Vector3 inv0, inv1, inv2; + inv0 = Vector3( tfrm.getCol0().getX(), tfrm.getCol1().getX(), tfrm.getCol2().getX() ); + inv1 = Vector3( tfrm.getCol0().getY(), tfrm.getCol1().getY(), tfrm.getCol2().getY() ); + inv2 = Vector3( tfrm.getCol0().getZ(), tfrm.getCol1().getZ(), tfrm.getCol2().getZ() ); + return Transform3( + inv0, + inv1, + inv2, + Vector3( ( -( ( inv0 * tfrm.getCol3().getX() ) + ( ( inv1 * tfrm.getCol3().getY() ) + ( inv2 * tfrm.getCol3().getZ() ) ) ) ) ) + ); +} + +inline const Transform3 absPerElem( const Transform3 & tfrm ) +{ + return Transform3( + absPerElem( tfrm.getCol0() ), + absPerElem( tfrm.getCol1() ), + absPerElem( tfrm.getCol2() ), + absPerElem( tfrm.getCol3() ) + ); +} + +inline const Vector3 Transform3::operator *( const Vector3 & vec ) const +{ + return Vector3( + ( ( ( mCol0.getX() * vec.getX() ) + ( mCol1.getX() * vec.getY() ) ) + ( mCol2.getX() * vec.getZ() ) ), + ( ( ( mCol0.getY() * vec.getX() ) + ( mCol1.getY() * vec.getY() ) ) + ( mCol2.getY() * vec.getZ() ) ), + ( ( ( mCol0.getZ() * vec.getX() ) + ( mCol1.getZ() * vec.getY() ) ) + ( mCol2.getZ() * vec.getZ() ) ) + ); +} + +inline const Point3 Transform3::operator *( const Point3 & pnt ) const +{ + return Point3( + ( ( ( ( mCol0.getX() * pnt.getX() ) + ( mCol1.getX() * pnt.getY() ) ) + ( mCol2.getX() * pnt.getZ() ) ) + mCol3.getX() ), + ( ( ( ( mCol0.getY() * pnt.getX() ) + ( mCol1.getY() * pnt.getY() ) ) + ( mCol2.getY() * pnt.getZ() ) ) + mCol3.getY() ), + ( ( ( ( mCol0.getZ() * pnt.getX() ) + ( mCol1.getZ() * pnt.getY() ) ) + ( mCol2.getZ() * pnt.getZ() ) ) + mCol3.getZ() ) + ); +} + +inline const Transform3 Transform3::operator *( const Transform3 & tfrm ) const +{ + return Transform3( + ( *this * tfrm.mCol0 ), + ( *this * tfrm.mCol1 ), + ( *this * tfrm.mCol2 ), + Vector3( ( *this * Point3( tfrm.mCol3 ) ) ) + ); +} + +inline Transform3 & Transform3::operator *=( const Transform3 & tfrm ) +{ + *this = *this * tfrm; + return *this; +} + +inline const Transform3 mulPerElem( const Transform3 & tfrm0, const Transform3 & tfrm1 ) +{ + return Transform3( + mulPerElem( tfrm0.getCol0(), tfrm1.getCol0() ), + mulPerElem( tfrm0.getCol1(), tfrm1.getCol1() ), + mulPerElem( tfrm0.getCol2(), tfrm1.getCol2() ), + mulPerElem( tfrm0.getCol3(), tfrm1.getCol3() ) + ); +} + +inline const Transform3 Transform3::identity( ) +{ + return Transform3( + Vector3::xAxis( ), + Vector3::yAxis( ), + Vector3::zAxis( ), + Vector3( 0.0f ) + ); +} + +inline Transform3 & Transform3::setUpper3x3( const Matrix3 & tfrm ) +{ + mCol0 = tfrm.getCol0(); + mCol1 = tfrm.getCol1(); + mCol2 = tfrm.getCol2(); + return *this; +} + +inline const Matrix3 Transform3::getUpper3x3( ) const +{ + return Matrix3( mCol0, mCol1, mCol2 ); +} + +inline Transform3 & Transform3::setTranslation( const Vector3 & translateVec ) +{ + mCol3 = translateVec; + return *this; +} + +inline const Vector3 Transform3::getTranslation( ) const +{ + return mCol3; +} + +inline const Transform3 Transform3::rotationX( float radians ) +{ + float s, c; + s = sinf( radians ); + c = cosf( radians ); + return Transform3( + Vector3::xAxis( ), + Vector3( 0.0f, c, s ), + Vector3( 0.0f, -s, c ), + Vector3( 0.0f ) + ); +} + +inline const Transform3 Transform3::rotationY( float radians ) +{ + float s, c; + s = sinf( radians ); + c = cosf( radians ); + return Transform3( + Vector3( c, 0.0f, -s ), + Vector3::yAxis( ), + Vector3( s, 0.0f, c ), + Vector3( 0.0f ) + ); +} + +inline const Transform3 Transform3::rotationZ( float radians ) +{ + float s, c; + s = sinf( radians ); + c = cosf( radians ); + return Transform3( + Vector3( c, s, 0.0f ), + Vector3( -s, c, 0.0f ), + Vector3::zAxis( ), + Vector3( 0.0f ) + ); +} + +inline const Transform3 Transform3::rotationZYX( const Vector3 & radiansXYZ ) +{ + float sX, cX, sY, cY, sZ, cZ, tmp0, tmp1; + sX = sinf( radiansXYZ.getX() ); + cX = cosf( radiansXYZ.getX() ); + sY = sinf( radiansXYZ.getY() ); + cY = cosf( radiansXYZ.getY() ); + sZ = sinf( radiansXYZ.getZ() ); + cZ = cosf( radiansXYZ.getZ() ); + tmp0 = ( cZ * sY ); + tmp1 = ( sZ * sY ); + return Transform3( + Vector3( ( cZ * cY ), ( sZ * cY ), -sY ), + Vector3( ( ( tmp0 * sX ) - ( sZ * cX ) ), ( ( tmp1 * sX ) + ( cZ * cX ) ), ( cY * sX ) ), + Vector3( ( ( tmp0 * cX ) + ( sZ * sX ) ), ( ( tmp1 * cX ) - ( cZ * sX ) ), ( cY * cX ) ), + Vector3( 0.0f ) + ); +} + +inline const Transform3 Transform3::rotation( float radians, const Vector3 & unitVec ) +{ + return Transform3( Matrix3::rotation( radians, unitVec ), Vector3( 0.0f ) ); +} + +inline const Transform3 Transform3::rotation( const Quat & unitQuat ) +{ + return Transform3( Matrix3( unitQuat ), Vector3( 0.0f ) ); +} + +inline const Transform3 Transform3::scale( const Vector3 & scaleVec ) +{ + return Transform3( + Vector3( scaleVec.getX(), 0.0f, 0.0f ), + Vector3( 0.0f, scaleVec.getY(), 0.0f ), + Vector3( 0.0f, 0.0f, scaleVec.getZ() ), + Vector3( 0.0f ) + ); +} + +inline const Transform3 appendScale( const Transform3 & tfrm, const Vector3 & scaleVec ) +{ + return Transform3( + ( tfrm.getCol0() * scaleVec.getX( ) ), + ( tfrm.getCol1() * scaleVec.getY( ) ), + ( tfrm.getCol2() * scaleVec.getZ( ) ), + tfrm.getCol3() + ); +} + +inline const Transform3 prependScale( const Vector3 & scaleVec, const Transform3 & tfrm ) +{ + return Transform3( + mulPerElem( tfrm.getCol0(), scaleVec ), + mulPerElem( tfrm.getCol1(), scaleVec ), + mulPerElem( tfrm.getCol2(), scaleVec ), + mulPerElem( tfrm.getCol3(), scaleVec ) + ); +} + +inline const Transform3 Transform3::translation( const Vector3 & translateVec ) +{ + return Transform3( + Vector3::xAxis( ), + Vector3::yAxis( ), + Vector3::zAxis( ), + translateVec + ); +} + +inline const Transform3 select( const Transform3 & tfrm0, const Transform3 & tfrm1, bool select1 ) +{ + return Transform3( + select( tfrm0.getCol0(), tfrm1.getCol0(), select1 ), + select( tfrm0.getCol1(), tfrm1.getCol1(), select1 ), + select( tfrm0.getCol2(), tfrm1.getCol2(), select1 ), + select( tfrm0.getCol3(), tfrm1.getCol3(), select1 ) + ); +} + +#ifdef _VECTORMATH_DEBUG + +inline void print( const Transform3 & tfrm ) +{ + print( tfrm.getRow( 0 ) ); + print( tfrm.getRow( 1 ) ); + print( tfrm.getRow( 2 ) ); +} + +inline void print( const Transform3 & tfrm, const char * name ) +{ + printf("%s:\n", name); + print( tfrm ); +} + +#endif + +inline Quat::Quat( const Matrix3 & tfrm ) +{ + float trace, radicand, scale, xx, yx, zx, xy, yy, zy, xz, yz, zz, tmpx, tmpy, tmpz, tmpw, qx, qy, qz, qw; + int negTrace, ZgtX, ZgtY, YgtX; + int largestXorY, largestYorZ, largestZorX; + + xx = tfrm.getCol0().getX(); + yx = tfrm.getCol0().getY(); + zx = tfrm.getCol0().getZ(); + xy = tfrm.getCol1().getX(); + yy = tfrm.getCol1().getY(); + zy = tfrm.getCol1().getZ(); + xz = tfrm.getCol2().getX(); + yz = tfrm.getCol2().getY(); + zz = tfrm.getCol2().getZ(); + + trace = ( ( xx + yy ) + zz ); + + negTrace = ( trace < 0.0f ); + ZgtX = zz > xx; + ZgtY = zz > yy; + YgtX = yy > xx; + largestXorY = ( !ZgtX || !ZgtY ) && negTrace; + largestYorZ = ( YgtX || ZgtX ) && negTrace; + largestZorX = ( ZgtY || !YgtX ) && negTrace; + + if ( largestXorY ) + { + zz = -zz; + xy = -xy; + } + if ( largestYorZ ) + { + xx = -xx; + yz = -yz; + } + if ( largestZorX ) + { + yy = -yy; + zx = -zx; + } + + radicand = ( ( ( xx + yy ) + zz ) + 1.0f ); + scale = ( 0.5f * ( 1.0f / sqrtf( radicand ) ) ); + + tmpx = ( ( zy - yz ) * scale ); + tmpy = ( ( xz - zx ) * scale ); + tmpz = ( ( yx - xy ) * scale ); + tmpw = ( radicand * scale ); + qx = tmpx; + qy = tmpy; + qz = tmpz; + qw = tmpw; + + if ( largestXorY ) + { + qx = tmpw; + qy = tmpz; + qz = tmpy; + qw = tmpx; + } + if ( largestYorZ ) + { + tmpx = qx; + tmpz = qz; + qx = qy; + qy = tmpx; + qz = qw; + qw = tmpz; + } + + mX = qx; + mY = qy; + mZ = qz; + mW = qw; +} + +inline const Matrix3 outer( const Vector3 & tfrm0, const Vector3 & tfrm1 ) +{ + return Matrix3( + ( tfrm0 * tfrm1.getX( ) ), + ( tfrm0 * tfrm1.getY( ) ), + ( tfrm0 * tfrm1.getZ( ) ) + ); +} + +inline const Matrix4 outer( const Vector4 & tfrm0, const Vector4 & tfrm1 ) +{ + return Matrix4( + ( tfrm0 * tfrm1.getX( ) ), + ( tfrm0 * tfrm1.getY( ) ), + ( tfrm0 * tfrm1.getZ( ) ), + ( tfrm0 * tfrm1.getW( ) ) + ); +} + +inline const Vector3 rowMul( const Vector3 & vec, const Matrix3 & mat ) +{ + return Vector3( + ( ( ( vec.getX() * mat.getCol0().getX() ) + ( vec.getY() * mat.getCol0().getY() ) ) + ( vec.getZ() * mat.getCol0().getZ() ) ), + ( ( ( vec.getX() * mat.getCol1().getX() ) + ( vec.getY() * mat.getCol1().getY() ) ) + ( vec.getZ() * mat.getCol1().getZ() ) ), + ( ( ( vec.getX() * mat.getCol2().getX() ) + ( vec.getY() * mat.getCol2().getY() ) ) + ( vec.getZ() * mat.getCol2().getZ() ) ) + ); +} + +inline const Matrix3 crossMatrix( const Vector3 & vec ) +{ + return Matrix3( + Vector3( 0.0f, vec.getZ(), -vec.getY() ), + Vector3( -vec.getZ(), 0.0f, vec.getX() ), + Vector3( vec.getY(), -vec.getX(), 0.0f ) + ); +} + +inline const Matrix3 crossMatrixMul( const Vector3 & vec, const Matrix3 & mat ) +{ + return Matrix3( cross( vec, mat.getCol0() ), cross( vec, mat.getCol1() ), cross( vec, mat.getCol2() ) ); +} + +} // namespace Aos +} // namespace Vectormath + +#endif diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/vectormath/scalar/cpp/quat_aos.h b/Engine/lib/bullet/src/BulletMultiThreaded/vectormath/scalar/cpp/quat_aos.h new file mode 100644 index 000000000..7f1e8822b --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/vectormath/scalar/cpp/quat_aos.h @@ -0,0 +1,432 @@ +/* + Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Sony Computer Entertainment Inc nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _VECTORMATH_QUAT_AOS_CPP_H +#define _VECTORMATH_QUAT_AOS_CPP_H +//----------------------------------------------------------------------------- +// Definitions + +#ifndef _VECTORMATH_INTERNAL_FUNCTIONS +#define _VECTORMATH_INTERNAL_FUNCTIONS + +#endif + +namespace Vectormath { +namespace Aos { + +inline Quat::Quat( const Quat & quat ) +{ + mX = quat.mX; + mY = quat.mY; + mZ = quat.mZ; + mW = quat.mW; +} + +inline Quat::Quat( float _x, float _y, float _z, float _w ) +{ + mX = _x; + mY = _y; + mZ = _z; + mW = _w; +} + +inline Quat::Quat( const Vector3 & xyz, float _w ) +{ + this->setXYZ( xyz ); + this->setW( _w ); +} + +inline Quat::Quat( const Vector4 & vec ) +{ + mX = vec.getX(); + mY = vec.getY(); + mZ = vec.getZ(); + mW = vec.getW(); +} + +inline Quat::Quat( float scalar ) +{ + mX = scalar; + mY = scalar; + mZ = scalar; + mW = scalar; +} + +inline const Quat Quat::identity( ) +{ + return Quat( 0.0f, 0.0f, 0.0f, 1.0f ); +} + +inline const Quat lerp( float t, const Quat & quat0, const Quat & quat1 ) +{ + return ( quat0 + ( ( quat1 - quat0 ) * t ) ); +} + +inline const Quat slerp( float t, const Quat & unitQuat0, const Quat & unitQuat1 ) +{ + Quat start; + float recipSinAngle, scale0, scale1, cosAngle, angle; + cosAngle = dot( unitQuat0, unitQuat1 ); + if ( cosAngle < 0.0f ) { + cosAngle = -cosAngle; + start = ( -unitQuat0 ); + } else { + start = unitQuat0; + } + if ( cosAngle < _VECTORMATH_SLERP_TOL ) { + angle = acosf( cosAngle ); + recipSinAngle = ( 1.0f / sinf( angle ) ); + scale0 = ( sinf( ( ( 1.0f - t ) * angle ) ) * recipSinAngle ); + scale1 = ( sinf( ( t * angle ) ) * recipSinAngle ); + } else { + scale0 = ( 1.0f - t ); + scale1 = t; + } + return ( ( start * scale0 ) + ( unitQuat1 * scale1 ) ); +} + +inline const Quat squad( float t, const Quat & unitQuat0, const Quat & unitQuat1, const Quat & unitQuat2, const Quat & unitQuat3 ) +{ + Quat tmp0, tmp1; + tmp0 = slerp( t, unitQuat0, unitQuat3 ); + tmp1 = slerp( t, unitQuat1, unitQuat2 ); + return slerp( ( ( 2.0f * t ) * ( 1.0f - t ) ), tmp0, tmp1 ); +} + +inline Quat & Quat::operator =( const Quat & quat ) +{ + mX = quat.mX; + mY = quat.mY; + mZ = quat.mZ; + mW = quat.mW; + return *this; +} + +inline Quat & Quat::setXYZ( const Vector3 & vec ) +{ + mX = vec.getX(); + mY = vec.getY(); + mZ = vec.getZ(); + return *this; +} + +inline const Vector3 Quat::getXYZ( ) const +{ + return Vector3( mX, mY, mZ ); +} + +inline Quat & Quat::setX( float _x ) +{ + mX = _x; + return *this; +} + +inline float Quat::getX( ) const +{ + return mX; +} + +inline Quat & Quat::setY( float _y ) +{ + mY = _y; + return *this; +} + +inline float Quat::getY( ) const +{ + return mY; +} + +inline Quat & Quat::setZ( float _z ) +{ + mZ = _z; + return *this; +} + +inline float Quat::getZ( ) const +{ + return mZ; +} + +inline Quat & Quat::setW( float _w ) +{ + mW = _w; + return *this; +} + +inline float Quat::getW( ) const +{ + return mW; +} + +inline Quat & Quat::setElem( int idx, float value ) +{ + *(&mX + idx) = value; + return *this; +} + +inline float Quat::getElem( int idx ) const +{ + return *(&mX + idx); +} + +inline float & Quat::operator []( int idx ) +{ + return *(&mX + idx); +} + +inline float Quat::operator []( int idx ) const +{ + return *(&mX + idx); +} + +inline const Quat Quat::operator +( const Quat & quat ) const +{ + return Quat( + ( mX + quat.mX ), + ( mY + quat.mY ), + ( mZ + quat.mZ ), + ( mW + quat.mW ) + ); +} + +inline const Quat Quat::operator -( const Quat & quat ) const +{ + return Quat( + ( mX - quat.mX ), + ( mY - quat.mY ), + ( mZ - quat.mZ ), + ( mW - quat.mW ) + ); +} + +inline const Quat Quat::operator *( float scalar ) const +{ + return Quat( + ( mX * scalar ), + ( mY * scalar ), + ( mZ * scalar ), + ( mW * scalar ) + ); +} + +inline Quat & Quat::operator +=( const Quat & quat ) +{ + *this = *this + quat; + return *this; +} + +inline Quat & Quat::operator -=( const Quat & quat ) +{ + *this = *this - quat; + return *this; +} + +inline Quat & Quat::operator *=( float scalar ) +{ + *this = *this * scalar; + return *this; +} + +inline const Quat Quat::operator /( float scalar ) const +{ + return Quat( + ( mX / scalar ), + ( mY / scalar ), + ( mZ / scalar ), + ( mW / scalar ) + ); +} + +inline Quat & Quat::operator /=( float scalar ) +{ + *this = *this / scalar; + return *this; +} + +inline const Quat Quat::operator -( ) const +{ + return Quat( + -mX, + -mY, + -mZ, + -mW + ); +} + +inline const Quat operator *( float scalar, const Quat & quat ) +{ + return quat * scalar; +} + +inline float dot( const Quat & quat0, const Quat & quat1 ) +{ + float result; + result = ( quat0.getX() * quat1.getX() ); + result = ( result + ( quat0.getY() * quat1.getY() ) ); + result = ( result + ( quat0.getZ() * quat1.getZ() ) ); + result = ( result + ( quat0.getW() * quat1.getW() ) ); + return result; +} + +inline float norm( const Quat & quat ) +{ + float result; + result = ( quat.getX() * quat.getX() ); + result = ( result + ( quat.getY() * quat.getY() ) ); + result = ( result + ( quat.getZ() * quat.getZ() ) ); + result = ( result + ( quat.getW() * quat.getW() ) ); + return result; +} + +inline float length( const Quat & quat ) +{ + return sqrtf( norm( quat ) ); +} + +inline const Quat normalize( const Quat & quat ) +{ + float lenSqr, lenInv; + lenSqr = norm( quat ); + lenInv = ( 1.0f / sqrtf( lenSqr ) ); + return Quat( + ( quat.getX() * lenInv ), + ( quat.getY() * lenInv ), + ( quat.getZ() * lenInv ), + ( quat.getW() * lenInv ) + ); +} + +inline const Quat Quat::rotation( const Vector3 & unitVec0, const Vector3 & unitVec1 ) +{ + float cosHalfAngleX2, recipCosHalfAngleX2; + cosHalfAngleX2 = sqrtf( ( 2.0f * ( 1.0f + dot( unitVec0, unitVec1 ) ) ) ); + recipCosHalfAngleX2 = ( 1.0f / cosHalfAngleX2 ); + return Quat( ( cross( unitVec0, unitVec1 ) * recipCosHalfAngleX2 ), ( cosHalfAngleX2 * 0.5f ) ); +} + +inline const Quat Quat::rotation( float radians, const Vector3 & unitVec ) +{ + float s, c, angle; + angle = ( radians * 0.5f ); + s = sinf( angle ); + c = cosf( angle ); + return Quat( ( unitVec * s ), c ); +} + +inline const Quat Quat::rotationX( float radians ) +{ + float s, c, angle; + angle = ( radians * 0.5f ); + s = sinf( angle ); + c = cosf( angle ); + return Quat( s, 0.0f, 0.0f, c ); +} + +inline const Quat Quat::rotationY( float radians ) +{ + float s, c, angle; + angle = ( radians * 0.5f ); + s = sinf( angle ); + c = cosf( angle ); + return Quat( 0.0f, s, 0.0f, c ); +} + +inline const Quat Quat::rotationZ( float radians ) +{ + float s, c, angle; + angle = ( radians * 0.5f ); + s = sinf( angle ); + c = cosf( angle ); + return Quat( 0.0f, 0.0f, s, c ); +} + +inline const Quat Quat::operator *( const Quat & quat ) const +{ + return Quat( + ( ( ( ( mW * quat.mX ) + ( mX * quat.mW ) ) + ( mY * quat.mZ ) ) - ( mZ * quat.mY ) ), + ( ( ( ( mW * quat.mY ) + ( mY * quat.mW ) ) + ( mZ * quat.mX ) ) - ( mX * quat.mZ ) ), + ( ( ( ( mW * quat.mZ ) + ( mZ * quat.mW ) ) + ( mX * quat.mY ) ) - ( mY * quat.mX ) ), + ( ( ( ( mW * quat.mW ) - ( mX * quat.mX ) ) - ( mY * quat.mY ) ) - ( mZ * quat.mZ ) ) + ); +} + +inline Quat & Quat::operator *=( const Quat & quat ) +{ + *this = *this * quat; + return *this; +} + +inline const Vector3 rotate( const Quat & quat, const Vector3 & vec ) +{ + float tmpX, tmpY, tmpZ, tmpW; + tmpX = ( ( ( quat.getW() * vec.getX() ) + ( quat.getY() * vec.getZ() ) ) - ( quat.getZ() * vec.getY() ) ); + tmpY = ( ( ( quat.getW() * vec.getY() ) + ( quat.getZ() * vec.getX() ) ) - ( quat.getX() * vec.getZ() ) ); + tmpZ = ( ( ( quat.getW() * vec.getZ() ) + ( quat.getX() * vec.getY() ) ) - ( quat.getY() * vec.getX() ) ); + tmpW = ( ( ( quat.getX() * vec.getX() ) + ( quat.getY() * vec.getY() ) ) + ( quat.getZ() * vec.getZ() ) ); + return Vector3( + ( ( ( ( tmpW * quat.getX() ) + ( tmpX * quat.getW() ) ) - ( tmpY * quat.getZ() ) ) + ( tmpZ * quat.getY() ) ), + ( ( ( ( tmpW * quat.getY() ) + ( tmpY * quat.getW() ) ) - ( tmpZ * quat.getX() ) ) + ( tmpX * quat.getZ() ) ), + ( ( ( ( tmpW * quat.getZ() ) + ( tmpZ * quat.getW() ) ) - ( tmpX * quat.getY() ) ) + ( tmpY * quat.getX() ) ) + ); +} + +inline const Quat conj( const Quat & quat ) +{ + return Quat( -quat.getX(), -quat.getY(), -quat.getZ(), quat.getW() ); +} + +inline const Quat select( const Quat & quat0, const Quat & quat1, bool select1 ) +{ + return Quat( + ( select1 )? quat1.getX() : quat0.getX(), + ( select1 )? quat1.getY() : quat0.getY(), + ( select1 )? quat1.getZ() : quat0.getZ(), + ( select1 )? quat1.getW() : quat0.getW() + ); +} + +#ifdef _VECTORMATH_DEBUG + +inline void print( const Quat & quat ) +{ + printf( "( %f %f %f %f )\n", quat.getX(), quat.getY(), quat.getZ(), quat.getW() ); +} + +inline void print( const Quat & quat, const char * name ) +{ + printf( "%s: ( %f %f %f %f )\n", name, quat.getX(), quat.getY(), quat.getZ(), quat.getW() ); +} + +#endif + +} // namespace Aos +} // namespace Vectormath + +#endif diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/vectormath/scalar/cpp/vec_aos.h b/Engine/lib/bullet/src/BulletMultiThreaded/vectormath/scalar/cpp/vec_aos.h new file mode 100644 index 000000000..a1a75333a --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/vectormath/scalar/cpp/vec_aos.h @@ -0,0 +1,1173 @@ +/* + Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Sony Computer Entertainment Inc nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _VECTORMATH_VEC_AOS_CPP_H +#define _VECTORMATH_VEC_AOS_CPP_H +//----------------------------------------------------------------------------- +// Constants + +#define _VECTORMATH_SLERP_TOL 0.999f + +//----------------------------------------------------------------------------- +// Definitions + +#ifndef _VECTORMATH_INTERNAL_FUNCTIONS +#define _VECTORMATH_INTERNAL_FUNCTIONS + +#endif + +namespace Vectormath { +namespace Aos { + +inline Vector3::Vector3( const Vector3 & vec ) +{ + mX = vec.mX; + mY = vec.mY; + mZ = vec.mZ; +} + +inline Vector3::Vector3( float _x, float _y, float _z ) +{ + mX = _x; + mY = _y; + mZ = _z; +} + +inline Vector3::Vector3( const Point3 & pnt ) +{ + mX = pnt.getX(); + mY = pnt.getY(); + mZ = pnt.getZ(); +} + +inline Vector3::Vector3( float scalar ) +{ + mX = scalar; + mY = scalar; + mZ = scalar; +} + +inline const Vector3 Vector3::xAxis( ) +{ + return Vector3( 1.0f, 0.0f, 0.0f ); +} + +inline const Vector3 Vector3::yAxis( ) +{ + return Vector3( 0.0f, 1.0f, 0.0f ); +} + +inline const Vector3 Vector3::zAxis( ) +{ + return Vector3( 0.0f, 0.0f, 1.0f ); +} + +inline const Vector3 lerp( float t, const Vector3 & vec0, const Vector3 & vec1 ) +{ + return ( vec0 + ( ( vec1 - vec0 ) * t ) ); +} + +inline const Vector3 slerp( float t, const Vector3 & unitVec0, const Vector3 & unitVec1 ) +{ + float recipSinAngle, scale0, scale1, cosAngle, angle; + cosAngle = dot( unitVec0, unitVec1 ); + if ( cosAngle < _VECTORMATH_SLERP_TOL ) { + angle = acosf( cosAngle ); + recipSinAngle = ( 1.0f / sinf( angle ) ); + scale0 = ( sinf( ( ( 1.0f - t ) * angle ) ) * recipSinAngle ); + scale1 = ( sinf( ( t * angle ) ) * recipSinAngle ); + } else { + scale0 = ( 1.0f - t ); + scale1 = t; + } + return ( ( unitVec0 * scale0 ) + ( unitVec1 * scale1 ) ); +} + +inline Vector3 & Vector3::operator =( const Vector3 & vec ) +{ + mX = vec.mX; + mY = vec.mY; + mZ = vec.mZ; + return *this; +} + +inline Vector3 & Vector3::setX( float _x ) +{ + mX = _x; + return *this; +} + +inline float Vector3::getX( ) const +{ + return mX; +} + +inline Vector3 & Vector3::setY( float _y ) +{ + mY = _y; + return *this; +} + +inline float Vector3::getY( ) const +{ + return mY; +} + +inline Vector3 & Vector3::setZ( float _z ) +{ + mZ = _z; + return *this; +} + +inline float Vector3::getZ( ) const +{ + return mZ; +} + +inline Vector3 & Vector3::setElem( int idx, float value ) +{ + *(&mX + idx) = value; + return *this; +} + +inline float Vector3::getElem( int idx ) const +{ + return *(&mX + idx); +} + +inline float & Vector3::operator []( int idx ) +{ + return *(&mX + idx); +} + +inline float Vector3::operator []( int idx ) const +{ + return *(&mX + idx); +} + +inline const Vector3 Vector3::operator +( const Vector3 & vec ) const +{ + return Vector3( + ( mX + vec.mX ), + ( mY + vec.mY ), + ( mZ + vec.mZ ) + ); +} + +inline const Vector3 Vector3::operator -( const Vector3 & vec ) const +{ + return Vector3( + ( mX - vec.mX ), + ( mY - vec.mY ), + ( mZ - vec.mZ ) + ); +} + +inline const Point3 Vector3::operator +( const Point3 & pnt ) const +{ + return Point3( + ( mX + pnt.getX() ), + ( mY + pnt.getY() ), + ( mZ + pnt.getZ() ) + ); +} + +inline const Vector3 Vector3::operator *( float scalar ) const +{ + return Vector3( + ( mX * scalar ), + ( mY * scalar ), + ( mZ * scalar ) + ); +} + +inline Vector3 & Vector3::operator +=( const Vector3 & vec ) +{ + *this = *this + vec; + return *this; +} + +inline Vector3 & Vector3::operator -=( const Vector3 & vec ) +{ + *this = *this - vec; + return *this; +} + +inline Vector3 & Vector3::operator *=( float scalar ) +{ + *this = *this * scalar; + return *this; +} + +inline const Vector3 Vector3::operator /( float scalar ) const +{ + return Vector3( + ( mX / scalar ), + ( mY / scalar ), + ( mZ / scalar ) + ); +} + +inline Vector3 & Vector3::operator /=( float scalar ) +{ + *this = *this / scalar; + return *this; +} + +inline const Vector3 Vector3::operator -( ) const +{ + return Vector3( + -mX, + -mY, + -mZ + ); +} + +inline const Vector3 operator *( float scalar, const Vector3 & vec ) +{ + return vec * scalar; +} + +inline const Vector3 mulPerElem( const Vector3 & vec0, const Vector3 & vec1 ) +{ + return Vector3( + ( vec0.getX() * vec1.getX() ), + ( vec0.getY() * vec1.getY() ), + ( vec0.getZ() * vec1.getZ() ) + ); +} + +inline const Vector3 divPerElem( const Vector3 & vec0, const Vector3 & vec1 ) +{ + return Vector3( + ( vec0.getX() / vec1.getX() ), + ( vec0.getY() / vec1.getY() ), + ( vec0.getZ() / vec1.getZ() ) + ); +} + +inline const Vector3 recipPerElem( const Vector3 & vec ) +{ + return Vector3( + ( 1.0f / vec.getX() ), + ( 1.0f / vec.getY() ), + ( 1.0f / vec.getZ() ) + ); +} + +inline const Vector3 sqrtPerElem( const Vector3 & vec ) +{ + return Vector3( + sqrtf( vec.getX() ), + sqrtf( vec.getY() ), + sqrtf( vec.getZ() ) + ); +} + +inline const Vector3 rsqrtPerElem( const Vector3 & vec ) +{ + return Vector3( + ( 1.0f / sqrtf( vec.getX() ) ), + ( 1.0f / sqrtf( vec.getY() ) ), + ( 1.0f / sqrtf( vec.getZ() ) ) + ); +} + +inline const Vector3 absPerElem( const Vector3 & vec ) +{ + return Vector3( + fabsf( vec.getX() ), + fabsf( vec.getY() ), + fabsf( vec.getZ() ) + ); +} + +inline const Vector3 copySignPerElem( const Vector3 & vec0, const Vector3 & vec1 ) +{ + return Vector3( + ( vec1.getX() < 0.0f )? -fabsf( vec0.getX() ) : fabsf( vec0.getX() ), + ( vec1.getY() < 0.0f )? -fabsf( vec0.getY() ) : fabsf( vec0.getY() ), + ( vec1.getZ() < 0.0f )? -fabsf( vec0.getZ() ) : fabsf( vec0.getZ() ) + ); +} + +inline const Vector3 maxPerElem( const Vector3 & vec0, const Vector3 & vec1 ) +{ + return Vector3( + (vec0.getX() > vec1.getX())? vec0.getX() : vec1.getX(), + (vec0.getY() > vec1.getY())? vec0.getY() : vec1.getY(), + (vec0.getZ() > vec1.getZ())? vec0.getZ() : vec1.getZ() + ); +} + +inline float maxElem( const Vector3 & vec ) +{ + float result; + result = (vec.getX() > vec.getY())? vec.getX() : vec.getY(); + result = (vec.getZ() > result)? vec.getZ() : result; + return result; +} + +inline const Vector3 minPerElem( const Vector3 & vec0, const Vector3 & vec1 ) +{ + return Vector3( + (vec0.getX() < vec1.getX())? vec0.getX() : vec1.getX(), + (vec0.getY() < vec1.getY())? vec0.getY() : vec1.getY(), + (vec0.getZ() < vec1.getZ())? vec0.getZ() : vec1.getZ() + ); +} + +inline float minElem( const Vector3 & vec ) +{ + float result; + result = (vec.getX() < vec.getY())? vec.getX() : vec.getY(); + result = (vec.getZ() < result)? vec.getZ() : result; + return result; +} + +inline float sum( const Vector3 & vec ) +{ + float result; + result = ( vec.getX() + vec.getY() ); + result = ( result + vec.getZ() ); + return result; +} + +inline float dot( const Vector3 & vec0, const Vector3 & vec1 ) +{ + float result; + result = ( vec0.getX() * vec1.getX() ); + result = ( result + ( vec0.getY() * vec1.getY() ) ); + result = ( result + ( vec0.getZ() * vec1.getZ() ) ); + return result; +} + +inline float lengthSqr( const Vector3 & vec ) +{ + float result; + result = ( vec.getX() * vec.getX() ); + result = ( result + ( vec.getY() * vec.getY() ) ); + result = ( result + ( vec.getZ() * vec.getZ() ) ); + return result; +} + +inline float length( const Vector3 & vec ) +{ + return sqrtf( lengthSqr( vec ) ); +} + +inline const Vector3 normalize( const Vector3 & vec ) +{ + float lenSqr, lenInv; + lenSqr = lengthSqr( vec ); + lenInv = ( 1.0f / sqrtf( lenSqr ) ); + return Vector3( + ( vec.getX() * lenInv ), + ( vec.getY() * lenInv ), + ( vec.getZ() * lenInv ) + ); +} + +inline const Vector3 cross( const Vector3 & vec0, const Vector3 & vec1 ) +{ + return Vector3( + ( ( vec0.getY() * vec1.getZ() ) - ( vec0.getZ() * vec1.getY() ) ), + ( ( vec0.getZ() * vec1.getX() ) - ( vec0.getX() * vec1.getZ() ) ), + ( ( vec0.getX() * vec1.getY() ) - ( vec0.getY() * vec1.getX() ) ) + ); +} + +inline const Vector3 select( const Vector3 & vec0, const Vector3 & vec1, bool select1 ) +{ + return Vector3( + ( select1 )? vec1.getX() : vec0.getX(), + ( select1 )? vec1.getY() : vec0.getY(), + ( select1 )? vec1.getZ() : vec0.getZ() + ); +} + +#ifdef _VECTORMATH_DEBUG + +inline void print( const Vector3 & vec ) +{ + printf( "( %f %f %f )\n", vec.getX(), vec.getY(), vec.getZ() ); +} + +inline void print( const Vector3 & vec, const char * name ) +{ + printf( "%s: ( %f %f %f )\n", name, vec.getX(), vec.getY(), vec.getZ() ); +} + +#endif + +inline Vector4::Vector4( const Vector4 & vec ) +{ + mX = vec.mX; + mY = vec.mY; + mZ = vec.mZ; + mW = vec.mW; +} + +inline Vector4::Vector4( float _x, float _y, float _z, float _w ) +{ + mX = _x; + mY = _y; + mZ = _z; + mW = _w; +} + +inline Vector4::Vector4( const Vector3 & xyz, float _w ) +{ + this->setXYZ( xyz ); + this->setW( _w ); +} + +inline Vector4::Vector4( const Vector3 & vec ) +{ + mX = vec.getX(); + mY = vec.getY(); + mZ = vec.getZ(); + mW = 0.0f; +} + +inline Vector4::Vector4( const Point3 & pnt ) +{ + mX = pnt.getX(); + mY = pnt.getY(); + mZ = pnt.getZ(); + mW = 1.0f; +} + +inline Vector4::Vector4( const Quat & quat ) +{ + mX = quat.getX(); + mY = quat.getY(); + mZ = quat.getZ(); + mW = quat.getW(); +} + +inline Vector4::Vector4( float scalar ) +{ + mX = scalar; + mY = scalar; + mZ = scalar; + mW = scalar; +} + +inline const Vector4 Vector4::xAxis( ) +{ + return Vector4( 1.0f, 0.0f, 0.0f, 0.0f ); +} + +inline const Vector4 Vector4::yAxis( ) +{ + return Vector4( 0.0f, 1.0f, 0.0f, 0.0f ); +} + +inline const Vector4 Vector4::zAxis( ) +{ + return Vector4( 0.0f, 0.0f, 1.0f, 0.0f ); +} + +inline const Vector4 Vector4::wAxis( ) +{ + return Vector4( 0.0f, 0.0f, 0.0f, 1.0f ); +} + +inline const Vector4 lerp( float t, const Vector4 & vec0, const Vector4 & vec1 ) +{ + return ( vec0 + ( ( vec1 - vec0 ) * t ) ); +} + +inline const Vector4 slerp( float t, const Vector4 & unitVec0, const Vector4 & unitVec1 ) +{ + float recipSinAngle, scale0, scale1, cosAngle, angle; + cosAngle = dot( unitVec0, unitVec1 ); + if ( cosAngle < _VECTORMATH_SLERP_TOL ) { + angle = acosf( cosAngle ); + recipSinAngle = ( 1.0f / sinf( angle ) ); + scale0 = ( sinf( ( ( 1.0f - t ) * angle ) ) * recipSinAngle ); + scale1 = ( sinf( ( t * angle ) ) * recipSinAngle ); + } else { + scale0 = ( 1.0f - t ); + scale1 = t; + } + return ( ( unitVec0 * scale0 ) + ( unitVec1 * scale1 ) ); +} + +inline Vector4 & Vector4::operator =( const Vector4 & vec ) +{ + mX = vec.mX; + mY = vec.mY; + mZ = vec.mZ; + mW = vec.mW; + return *this; +} + +inline Vector4 & Vector4::setXYZ( const Vector3 & vec ) +{ + mX = vec.getX(); + mY = vec.getY(); + mZ = vec.getZ(); + return *this; +} + +inline const Vector3 Vector4::getXYZ( ) const +{ + return Vector3( mX, mY, mZ ); +} + +inline Vector4 & Vector4::setX( float _x ) +{ + mX = _x; + return *this; +} + +inline float Vector4::getX( ) const +{ + return mX; +} + +inline Vector4 & Vector4::setY( float _y ) +{ + mY = _y; + return *this; +} + +inline float Vector4::getY( ) const +{ + return mY; +} + +inline Vector4 & Vector4::setZ( float _z ) +{ + mZ = _z; + return *this; +} + +inline float Vector4::getZ( ) const +{ + return mZ; +} + +inline Vector4 & Vector4::setW( float _w ) +{ + mW = _w; + return *this; +} + +inline float Vector4::getW( ) const +{ + return mW; +} + +inline Vector4 & Vector4::setElem( int idx, float value ) +{ + *(&mX + idx) = value; + return *this; +} + +inline float Vector4::getElem( int idx ) const +{ + return *(&mX + idx); +} + +inline float & Vector4::operator []( int idx ) +{ + return *(&mX + idx); +} + +inline float Vector4::operator []( int idx ) const +{ + return *(&mX + idx); +} + +inline const Vector4 Vector4::operator +( const Vector4 & vec ) const +{ + return Vector4( + ( mX + vec.mX ), + ( mY + vec.mY ), + ( mZ + vec.mZ ), + ( mW + vec.mW ) + ); +} + +inline const Vector4 Vector4::operator -( const Vector4 & vec ) const +{ + return Vector4( + ( mX - vec.mX ), + ( mY - vec.mY ), + ( mZ - vec.mZ ), + ( mW - vec.mW ) + ); +} + +inline const Vector4 Vector4::operator *( float scalar ) const +{ + return Vector4( + ( mX * scalar ), + ( mY * scalar ), + ( mZ * scalar ), + ( mW * scalar ) + ); +} + +inline Vector4 & Vector4::operator +=( const Vector4 & vec ) +{ + *this = *this + vec; + return *this; +} + +inline Vector4 & Vector4::operator -=( const Vector4 & vec ) +{ + *this = *this - vec; + return *this; +} + +inline Vector4 & Vector4::operator *=( float scalar ) +{ + *this = *this * scalar; + return *this; +} + +inline const Vector4 Vector4::operator /( float scalar ) const +{ + return Vector4( + ( mX / scalar ), + ( mY / scalar ), + ( mZ / scalar ), + ( mW / scalar ) + ); +} + +inline Vector4 & Vector4::operator /=( float scalar ) +{ + *this = *this / scalar; + return *this; +} + +inline const Vector4 Vector4::operator -( ) const +{ + return Vector4( + -mX, + -mY, + -mZ, + -mW + ); +} + +inline const Vector4 operator *( float scalar, const Vector4 & vec ) +{ + return vec * scalar; +} + +inline const Vector4 mulPerElem( const Vector4 & vec0, const Vector4 & vec1 ) +{ + return Vector4( + ( vec0.getX() * vec1.getX() ), + ( vec0.getY() * vec1.getY() ), + ( vec0.getZ() * vec1.getZ() ), + ( vec0.getW() * vec1.getW() ) + ); +} + +inline const Vector4 divPerElem( const Vector4 & vec0, const Vector4 & vec1 ) +{ + return Vector4( + ( vec0.getX() / vec1.getX() ), + ( vec0.getY() / vec1.getY() ), + ( vec0.getZ() / vec1.getZ() ), + ( vec0.getW() / vec1.getW() ) + ); +} + +inline const Vector4 recipPerElem( const Vector4 & vec ) +{ + return Vector4( + ( 1.0f / vec.getX() ), + ( 1.0f / vec.getY() ), + ( 1.0f / vec.getZ() ), + ( 1.0f / vec.getW() ) + ); +} + +inline const Vector4 sqrtPerElem( const Vector4 & vec ) +{ + return Vector4( + sqrtf( vec.getX() ), + sqrtf( vec.getY() ), + sqrtf( vec.getZ() ), + sqrtf( vec.getW() ) + ); +} + +inline const Vector4 rsqrtPerElem( const Vector4 & vec ) +{ + return Vector4( + ( 1.0f / sqrtf( vec.getX() ) ), + ( 1.0f / sqrtf( vec.getY() ) ), + ( 1.0f / sqrtf( vec.getZ() ) ), + ( 1.0f / sqrtf( vec.getW() ) ) + ); +} + +inline const Vector4 absPerElem( const Vector4 & vec ) +{ + return Vector4( + fabsf( vec.getX() ), + fabsf( vec.getY() ), + fabsf( vec.getZ() ), + fabsf( vec.getW() ) + ); +} + +inline const Vector4 copySignPerElem( const Vector4 & vec0, const Vector4 & vec1 ) +{ + return Vector4( + ( vec1.getX() < 0.0f )? -fabsf( vec0.getX() ) : fabsf( vec0.getX() ), + ( vec1.getY() < 0.0f )? -fabsf( vec0.getY() ) : fabsf( vec0.getY() ), + ( vec1.getZ() < 0.0f )? -fabsf( vec0.getZ() ) : fabsf( vec0.getZ() ), + ( vec1.getW() < 0.0f )? -fabsf( vec0.getW() ) : fabsf( vec0.getW() ) + ); +} + +inline const Vector4 maxPerElem( const Vector4 & vec0, const Vector4 & vec1 ) +{ + return Vector4( + (vec0.getX() > vec1.getX())? vec0.getX() : vec1.getX(), + (vec0.getY() > vec1.getY())? vec0.getY() : vec1.getY(), + (vec0.getZ() > vec1.getZ())? vec0.getZ() : vec1.getZ(), + (vec0.getW() > vec1.getW())? vec0.getW() : vec1.getW() + ); +} + +inline float maxElem( const Vector4 & vec ) +{ + float result; + result = (vec.getX() > vec.getY())? vec.getX() : vec.getY(); + result = (vec.getZ() > result)? vec.getZ() : result; + result = (vec.getW() > result)? vec.getW() : result; + return result; +} + +inline const Vector4 minPerElem( const Vector4 & vec0, const Vector4 & vec1 ) +{ + return Vector4( + (vec0.getX() < vec1.getX())? vec0.getX() : vec1.getX(), + (vec0.getY() < vec1.getY())? vec0.getY() : vec1.getY(), + (vec0.getZ() < vec1.getZ())? vec0.getZ() : vec1.getZ(), + (vec0.getW() < vec1.getW())? vec0.getW() : vec1.getW() + ); +} + +inline float minElem( const Vector4 & vec ) +{ + float result; + result = (vec.getX() < vec.getY())? vec.getX() : vec.getY(); + result = (vec.getZ() < result)? vec.getZ() : result; + result = (vec.getW() < result)? vec.getW() : result; + return result; +} + +inline float sum( const Vector4 & vec ) +{ + float result; + result = ( vec.getX() + vec.getY() ); + result = ( result + vec.getZ() ); + result = ( result + vec.getW() ); + return result; +} + +inline float dot( const Vector4 & vec0, const Vector4 & vec1 ) +{ + float result; + result = ( vec0.getX() * vec1.getX() ); + result = ( result + ( vec0.getY() * vec1.getY() ) ); + result = ( result + ( vec0.getZ() * vec1.getZ() ) ); + result = ( result + ( vec0.getW() * vec1.getW() ) ); + return result; +} + +inline float lengthSqr( const Vector4 & vec ) +{ + float result; + result = ( vec.getX() * vec.getX() ); + result = ( result + ( vec.getY() * vec.getY() ) ); + result = ( result + ( vec.getZ() * vec.getZ() ) ); + result = ( result + ( vec.getW() * vec.getW() ) ); + return result; +} + +inline float length( const Vector4 & vec ) +{ + return sqrtf( lengthSqr( vec ) ); +} + +inline const Vector4 normalize( const Vector4 & vec ) +{ + float lenSqr, lenInv; + lenSqr = lengthSqr( vec ); + lenInv = ( 1.0f / sqrtf( lenSqr ) ); + return Vector4( + ( vec.getX() * lenInv ), + ( vec.getY() * lenInv ), + ( vec.getZ() * lenInv ), + ( vec.getW() * lenInv ) + ); +} + +inline const Vector4 select( const Vector4 & vec0, const Vector4 & vec1, bool select1 ) +{ + return Vector4( + ( select1 )? vec1.getX() : vec0.getX(), + ( select1 )? vec1.getY() : vec0.getY(), + ( select1 )? vec1.getZ() : vec0.getZ(), + ( select1 )? vec1.getW() : vec0.getW() + ); +} + +#ifdef _VECTORMATH_DEBUG + +inline void print( const Vector4 & vec ) +{ + printf( "( %f %f %f %f )\n", vec.getX(), vec.getY(), vec.getZ(), vec.getW() ); +} + +inline void print( const Vector4 & vec, const char * name ) +{ + printf( "%s: ( %f %f %f %f )\n", name, vec.getX(), vec.getY(), vec.getZ(), vec.getW() ); +} + +#endif + +inline Point3::Point3( const Point3 & pnt ) +{ + mX = pnt.mX; + mY = pnt.mY; + mZ = pnt.mZ; +} + +inline Point3::Point3( float _x, float _y, float _z ) +{ + mX = _x; + mY = _y; + mZ = _z; +} + +inline Point3::Point3( const Vector3 & vec ) +{ + mX = vec.getX(); + mY = vec.getY(); + mZ = vec.getZ(); +} + +inline Point3::Point3( float scalar ) +{ + mX = scalar; + mY = scalar; + mZ = scalar; +} + +inline const Point3 lerp( float t, const Point3 & pnt0, const Point3 & pnt1 ) +{ + return ( pnt0 + ( ( pnt1 - pnt0 ) * t ) ); +} + +inline Point3 & Point3::operator =( const Point3 & pnt ) +{ + mX = pnt.mX; + mY = pnt.mY; + mZ = pnt.mZ; + return *this; +} + +inline Point3 & Point3::setX( float _x ) +{ + mX = _x; + return *this; +} + +inline float Point3::getX( ) const +{ + return mX; +} + +inline Point3 & Point3::setY( float _y ) +{ + mY = _y; + return *this; +} + +inline float Point3::getY( ) const +{ + return mY; +} + +inline Point3 & Point3::setZ( float _z ) +{ + mZ = _z; + return *this; +} + +inline float Point3::getZ( ) const +{ + return mZ; +} + +inline Point3 & Point3::setElem( int idx, float value ) +{ + *(&mX + idx) = value; + return *this; +} + +inline float Point3::getElem( int idx ) const +{ + return *(&mX + idx); +} + +inline float & Point3::operator []( int idx ) +{ + return *(&mX + idx); +} + +inline float Point3::operator []( int idx ) const +{ + return *(&mX + idx); +} + +inline const Vector3 Point3::operator -( const Point3 & pnt ) const +{ + return Vector3( + ( mX - pnt.mX ), + ( mY - pnt.mY ), + ( mZ - pnt.mZ ) + ); +} + +inline const Point3 Point3::operator +( const Vector3 & vec ) const +{ + return Point3( + ( mX + vec.getX() ), + ( mY + vec.getY() ), + ( mZ + vec.getZ() ) + ); +} + +inline const Point3 Point3::operator -( const Vector3 & vec ) const +{ + return Point3( + ( mX - vec.getX() ), + ( mY - vec.getY() ), + ( mZ - vec.getZ() ) + ); +} + +inline Point3 & Point3::operator +=( const Vector3 & vec ) +{ + *this = *this + vec; + return *this; +} + +inline Point3 & Point3::operator -=( const Vector3 & vec ) +{ + *this = *this - vec; + return *this; +} + +inline const Point3 mulPerElem( const Point3 & pnt0, const Point3 & pnt1 ) +{ + return Point3( + ( pnt0.getX() * pnt1.getX() ), + ( pnt0.getY() * pnt1.getY() ), + ( pnt0.getZ() * pnt1.getZ() ) + ); +} + +inline const Point3 divPerElem( const Point3 & pnt0, const Point3 & pnt1 ) +{ + return Point3( + ( pnt0.getX() / pnt1.getX() ), + ( pnt0.getY() / pnt1.getY() ), + ( pnt0.getZ() / pnt1.getZ() ) + ); +} + +inline const Point3 recipPerElem( const Point3 & pnt ) +{ + return Point3( + ( 1.0f / pnt.getX() ), + ( 1.0f / pnt.getY() ), + ( 1.0f / pnt.getZ() ) + ); +} + +inline const Point3 sqrtPerElem( const Point3 & pnt ) +{ + return Point3( + sqrtf( pnt.getX() ), + sqrtf( pnt.getY() ), + sqrtf( pnt.getZ() ) + ); +} + +inline const Point3 rsqrtPerElem( const Point3 & pnt ) +{ + return Point3( + ( 1.0f / sqrtf( pnt.getX() ) ), + ( 1.0f / sqrtf( pnt.getY() ) ), + ( 1.0f / sqrtf( pnt.getZ() ) ) + ); +} + +inline const Point3 absPerElem( const Point3 & pnt ) +{ + return Point3( + fabsf( pnt.getX() ), + fabsf( pnt.getY() ), + fabsf( pnt.getZ() ) + ); +} + +inline const Point3 copySignPerElem( const Point3 & pnt0, const Point3 & pnt1 ) +{ + return Point3( + ( pnt1.getX() < 0.0f )? -fabsf( pnt0.getX() ) : fabsf( pnt0.getX() ), + ( pnt1.getY() < 0.0f )? -fabsf( pnt0.getY() ) : fabsf( pnt0.getY() ), + ( pnt1.getZ() < 0.0f )? -fabsf( pnt0.getZ() ) : fabsf( pnt0.getZ() ) + ); +} + +inline const Point3 maxPerElem( const Point3 & pnt0, const Point3 & pnt1 ) +{ + return Point3( + (pnt0.getX() > pnt1.getX())? pnt0.getX() : pnt1.getX(), + (pnt0.getY() > pnt1.getY())? pnt0.getY() : pnt1.getY(), + (pnt0.getZ() > pnt1.getZ())? pnt0.getZ() : pnt1.getZ() + ); +} + +inline float maxElem( const Point3 & pnt ) +{ + float result; + result = (pnt.getX() > pnt.getY())? pnt.getX() : pnt.getY(); + result = (pnt.getZ() > result)? pnt.getZ() : result; + return result; +} + +inline const Point3 minPerElem( const Point3 & pnt0, const Point3 & pnt1 ) +{ + return Point3( + (pnt0.getX() < pnt1.getX())? pnt0.getX() : pnt1.getX(), + (pnt0.getY() < pnt1.getY())? pnt0.getY() : pnt1.getY(), + (pnt0.getZ() < pnt1.getZ())? pnt0.getZ() : pnt1.getZ() + ); +} + +inline float minElem( const Point3 & pnt ) +{ + float result; + result = (pnt.getX() < pnt.getY())? pnt.getX() : pnt.getY(); + result = (pnt.getZ() < result)? pnt.getZ() : result; + return result; +} + +inline float sum( const Point3 & pnt ) +{ + float result; + result = ( pnt.getX() + pnt.getY() ); + result = ( result + pnt.getZ() ); + return result; +} + +inline const Point3 scale( const Point3 & pnt, float scaleVal ) +{ + return mulPerElem( pnt, Point3( scaleVal ) ); +} + +inline const Point3 scale( const Point3 & pnt, const Vector3 & scaleVec ) +{ + return mulPerElem( pnt, Point3( scaleVec ) ); +} + +inline float projection( const Point3 & pnt, const Vector3 & unitVec ) +{ + float result; + result = ( pnt.getX() * unitVec.getX() ); + result = ( result + ( pnt.getY() * unitVec.getY() ) ); + result = ( result + ( pnt.getZ() * unitVec.getZ() ) ); + return result; +} + +inline float distSqrFromOrigin( const Point3 & pnt ) +{ + return lengthSqr( Vector3( pnt ) ); +} + +inline float distFromOrigin( const Point3 & pnt ) +{ + return length( Vector3( pnt ) ); +} + +inline float distSqr( const Point3 & pnt0, const Point3 & pnt1 ) +{ + return lengthSqr( ( pnt1 - pnt0 ) ); +} + +inline float dist( const Point3 & pnt0, const Point3 & pnt1 ) +{ + return length( ( pnt1 - pnt0 ) ); +} + +inline const Point3 select( const Point3 & pnt0, const Point3 & pnt1, bool select1 ) +{ + return Point3( + ( select1 )? pnt1.getX() : pnt0.getX(), + ( select1 )? pnt1.getY() : pnt0.getY(), + ( select1 )? pnt1.getZ() : pnt0.getZ() + ); +} + +#ifdef _VECTORMATH_DEBUG + +inline void print( const Point3 & pnt ) +{ + printf( "( %f %f %f )\n", pnt.getX(), pnt.getY(), pnt.getZ() ); +} + +inline void print( const Point3 & pnt, const char * name ) +{ + printf( "%s: ( %f %f %f )\n", name, pnt.getX(), pnt.getY(), pnt.getZ() ); +} + +#endif + +} // namespace Aos +} // namespace Vectormath + +#endif diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/vectormath/scalar/cpp/vectormath_aos.h b/Engine/lib/bullet/src/BulletMultiThreaded/vectormath/scalar/cpp/vectormath_aos.h new file mode 100644 index 000000000..7913c11ea --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/vectormath/scalar/cpp/vectormath_aos.h @@ -0,0 +1,1809 @@ +/* + Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Sony Computer Entertainment Inc nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _VECTORMATH_AOS_CPP_SCALAR_H +#define _VECTORMATH_AOS_CPP_SCALAR_H + +#include + +#ifdef _VECTORMATH_DEBUG +#include +#endif + +namespace Vectormath { + +namespace Aos { + +//----------------------------------------------------------------------------- +// Forward Declarations +// + +class Vector3; +class Vector4; +class Point3; +class Quat; +class Matrix3; +class Matrix4; +class Transform3; + +// A 3-D vector in array-of-structures format +// +class Vector3 +{ + float mX; + float mY; + float mZ; +#ifndef __GNUC__ + float d; +#endif + +public: + // Default constructor; does no initialization + // + inline Vector3( ) { }; + + // Copy a 3-D vector + // + inline Vector3( const Vector3 & vec ); + + // Construct a 3-D vector from x, y, and z elements + // + inline Vector3( float x, float y, float z ); + + // Copy elements from a 3-D point into a 3-D vector + // + explicit inline Vector3( const Point3 & pnt ); + + // Set all elements of a 3-D vector to the same scalar value + // + explicit inline Vector3( float scalar ); + + // Assign one 3-D vector to another + // + inline Vector3 & operator =( const Vector3 & vec ); + + // Set the x element of a 3-D vector + // + inline Vector3 & setX( float x ); + + // Set the y element of a 3-D vector + // + inline Vector3 & setY( float y ); + + // Set the z element of a 3-D vector + // + inline Vector3 & setZ( float z ); + + // Get the x element of a 3-D vector + // + inline float getX( ) const; + + // Get the y element of a 3-D vector + // + inline float getY( ) const; + + // Get the z element of a 3-D vector + // + inline float getZ( ) const; + + // Set an x, y, or z element of a 3-D vector by index + // + inline Vector3 & setElem( int idx, float value ); + + // Get an x, y, or z element of a 3-D vector by index + // + inline float getElem( int idx ) const; + + // Subscripting operator to set or get an element + // + inline float & operator []( int idx ); + + // Subscripting operator to get an element + // + inline float operator []( int idx ) const; + + // Add two 3-D vectors + // + inline const Vector3 operator +( const Vector3 & vec ) const; + + // Subtract a 3-D vector from another 3-D vector + // + inline const Vector3 operator -( const Vector3 & vec ) const; + + // Add a 3-D vector to a 3-D point + // + inline const Point3 operator +( const Point3 & pnt ) const; + + // Multiply a 3-D vector by a scalar + // + inline const Vector3 operator *( float scalar ) const; + + // Divide a 3-D vector by a scalar + // + inline const Vector3 operator /( float scalar ) const; + + // Perform compound assignment and addition with a 3-D vector + // + inline Vector3 & operator +=( const Vector3 & vec ); + + // Perform compound assignment and subtraction by a 3-D vector + // + inline Vector3 & operator -=( const Vector3 & vec ); + + // Perform compound assignment and multiplication by a scalar + // + inline Vector3 & operator *=( float scalar ); + + // Perform compound assignment and division by a scalar + // + inline Vector3 & operator /=( float scalar ); + + // Negate all elements of a 3-D vector + // + inline const Vector3 operator -( ) const; + + // Construct x axis + // + static inline const Vector3 xAxis( ); + + // Construct y axis + // + static inline const Vector3 yAxis( ); + + // Construct z axis + // + static inline const Vector3 zAxis( ); + +} +#ifdef __GNUC__ +__attribute__ ((aligned(16))) +#endif +; + +// Multiply a 3-D vector by a scalar +// +inline const Vector3 operator *( float scalar, const Vector3 & vec ); + +// Multiply two 3-D vectors per element +// +inline const Vector3 mulPerElem( const Vector3 & vec0, const Vector3 & vec1 ); + +// Divide two 3-D vectors per element +// NOTE: +// Floating-point behavior matches standard library function divf4. +// +inline const Vector3 divPerElem( const Vector3 & vec0, const Vector3 & vec1 ); + +// Compute the reciprocal of a 3-D vector per element +// NOTE: +// Floating-point behavior matches standard library function recipf4. +// +inline const Vector3 recipPerElem( const Vector3 & vec ); + +// Compute the square root of a 3-D vector per element +// NOTE: +// Floating-point behavior matches standard library function sqrtf4. +// +inline const Vector3 sqrtPerElem( const Vector3 & vec ); + +// Compute the reciprocal square root of a 3-D vector per element +// NOTE: +// Floating-point behavior matches standard library function rsqrtf4. +// +inline const Vector3 rsqrtPerElem( const Vector3 & vec ); + +// Compute the absolute value of a 3-D vector per element +// +inline const Vector3 absPerElem( const Vector3 & vec ); + +// Copy sign from one 3-D vector to another, per element +// +inline const Vector3 copySignPerElem( const Vector3 & vec0, const Vector3 & vec1 ); + +// Maximum of two 3-D vectors per element +// +inline const Vector3 maxPerElem( const Vector3 & vec0, const Vector3 & vec1 ); + +// Minimum of two 3-D vectors per element +// +inline const Vector3 minPerElem( const Vector3 & vec0, const Vector3 & vec1 ); + +// Maximum element of a 3-D vector +// +inline float maxElem( const Vector3 & vec ); + +// Minimum element of a 3-D vector +// +inline float minElem( const Vector3 & vec ); + +// Compute the sum of all elements of a 3-D vector +// +inline float sum( const Vector3 & vec ); + +// Compute the dot product of two 3-D vectors +// +inline float dot( const Vector3 & vec0, const Vector3 & vec1 ); + +// Compute the square of the length of a 3-D vector +// +inline float lengthSqr( const Vector3 & vec ); + +// Compute the length of a 3-D vector +// +inline float length( const Vector3 & vec ); + +// Normalize a 3-D vector +// NOTE: +// The result is unpredictable when all elements of vec are at or near zero. +// +inline const Vector3 normalize( const Vector3 & vec ); + +// Compute cross product of two 3-D vectors +// +inline const Vector3 cross( const Vector3 & vec0, const Vector3 & vec1 ); + +// Outer product of two 3-D vectors +// +inline const Matrix3 outer( const Vector3 & vec0, const Vector3 & vec1 ); + +// Pre-multiply a row vector by a 3x3 matrix +// +inline const Vector3 rowMul( const Vector3 & vec, const Matrix3 & mat ); + +// Cross-product matrix of a 3-D vector +// +inline const Matrix3 crossMatrix( const Vector3 & vec ); + +// Create cross-product matrix and multiply +// NOTE: +// Faster than separately creating a cross-product matrix and multiplying. +// +inline const Matrix3 crossMatrixMul( const Vector3 & vec, const Matrix3 & mat ); + +// Linear interpolation between two 3-D vectors +// NOTE: +// Does not clamp t between 0 and 1. +// +inline const Vector3 lerp( float t, const Vector3 & vec0, const Vector3 & vec1 ); + +// Spherical linear interpolation between two 3-D vectors +// NOTE: +// The result is unpredictable if the vectors point in opposite directions. +// Does not clamp t between 0 and 1. +// +inline const Vector3 slerp( float t, const Vector3 & unitVec0, const Vector3 & unitVec1 ); + +// Conditionally select between two 3-D vectors +// +inline const Vector3 select( const Vector3 & vec0, const Vector3 & vec1, bool select1 ); + +#ifdef _VECTORMATH_DEBUG + +// Print a 3-D vector +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( const Vector3 & vec ); + +// Print a 3-D vector and an associated string identifier +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( const Vector3 & vec, const char * name ); + +#endif + +// A 4-D vector in array-of-structures format +// +class Vector4 +{ + float mX; + float mY; + float mZ; + float mW; + +public: + // Default constructor; does no initialization + // + inline Vector4( ) { }; + + // Copy a 4-D vector + // + inline Vector4( const Vector4 & vec ); + + // Construct a 4-D vector from x, y, z, and w elements + // + inline Vector4( float x, float y, float z, float w ); + + // Construct a 4-D vector from a 3-D vector and a scalar + // + inline Vector4( const Vector3 & xyz, float w ); + + // Copy x, y, and z from a 3-D vector into a 4-D vector, and set w to 0 + // + explicit inline Vector4( const Vector3 & vec ); + + // Copy x, y, and z from a 3-D point into a 4-D vector, and set w to 1 + // + explicit inline Vector4( const Point3 & pnt ); + + // Copy elements from a quaternion into a 4-D vector + // + explicit inline Vector4( const Quat & quat ); + + // Set all elements of a 4-D vector to the same scalar value + // + explicit inline Vector4( float scalar ); + + // Assign one 4-D vector to another + // + inline Vector4 & operator =( const Vector4 & vec ); + + // Set the x, y, and z elements of a 4-D vector + // NOTE: + // This function does not change the w element. + // + inline Vector4 & setXYZ( const Vector3 & vec ); + + // Get the x, y, and z elements of a 4-D vector + // + inline const Vector3 getXYZ( ) const; + + // Set the x element of a 4-D vector + // + inline Vector4 & setX( float x ); + + // Set the y element of a 4-D vector + // + inline Vector4 & setY( float y ); + + // Set the z element of a 4-D vector + // + inline Vector4 & setZ( float z ); + + // Set the w element of a 4-D vector + // + inline Vector4 & setW( float w ); + + // Get the x element of a 4-D vector + // + inline float getX( ) const; + + // Get the y element of a 4-D vector + // + inline float getY( ) const; + + // Get the z element of a 4-D vector + // + inline float getZ( ) const; + + // Get the w element of a 4-D vector + // + inline float getW( ) const; + + // Set an x, y, z, or w element of a 4-D vector by index + // + inline Vector4 & setElem( int idx, float value ); + + // Get an x, y, z, or w element of a 4-D vector by index + // + inline float getElem( int idx ) const; + + // Subscripting operator to set or get an element + // + inline float & operator []( int idx ); + + // Subscripting operator to get an element + // + inline float operator []( int idx ) const; + + // Add two 4-D vectors + // + inline const Vector4 operator +( const Vector4 & vec ) const; + + // Subtract a 4-D vector from another 4-D vector + // + inline const Vector4 operator -( const Vector4 & vec ) const; + + // Multiply a 4-D vector by a scalar + // + inline const Vector4 operator *( float scalar ) const; + + // Divide a 4-D vector by a scalar + // + inline const Vector4 operator /( float scalar ) const; + + // Perform compound assignment and addition with a 4-D vector + // + inline Vector4 & operator +=( const Vector4 & vec ); + + // Perform compound assignment and subtraction by a 4-D vector + // + inline Vector4 & operator -=( const Vector4 & vec ); + + // Perform compound assignment and multiplication by a scalar + // + inline Vector4 & operator *=( float scalar ); + + // Perform compound assignment and division by a scalar + // + inline Vector4 & operator /=( float scalar ); + + // Negate all elements of a 4-D vector + // + inline const Vector4 operator -( ) const; + + // Construct x axis + // + static inline const Vector4 xAxis( ); + + // Construct y axis + // + static inline const Vector4 yAxis( ); + + // Construct z axis + // + static inline const Vector4 zAxis( ); + + // Construct w axis + // + static inline const Vector4 wAxis( ); + +} +#ifdef __GNUC__ +__attribute__ ((aligned(16))) +#endif +; + +// Multiply a 4-D vector by a scalar +// +inline const Vector4 operator *( float scalar, const Vector4 & vec ); + +// Multiply two 4-D vectors per element +// +inline const Vector4 mulPerElem( const Vector4 & vec0, const Vector4 & vec1 ); + +// Divide two 4-D vectors per element +// NOTE: +// Floating-point behavior matches standard library function divf4. +// +inline const Vector4 divPerElem( const Vector4 & vec0, const Vector4 & vec1 ); + +// Compute the reciprocal of a 4-D vector per element +// NOTE: +// Floating-point behavior matches standard library function recipf4. +// +inline const Vector4 recipPerElem( const Vector4 & vec ); + +// Compute the square root of a 4-D vector per element +// NOTE: +// Floating-point behavior matches standard library function sqrtf4. +// +inline const Vector4 sqrtPerElem( const Vector4 & vec ); + +// Compute the reciprocal square root of a 4-D vector per element +// NOTE: +// Floating-point behavior matches standard library function rsqrtf4. +// +inline const Vector4 rsqrtPerElem( const Vector4 & vec ); + +// Compute the absolute value of a 4-D vector per element +// +inline const Vector4 absPerElem( const Vector4 & vec ); + +// Copy sign from one 4-D vector to another, per element +// +inline const Vector4 copySignPerElem( const Vector4 & vec0, const Vector4 & vec1 ); + +// Maximum of two 4-D vectors per element +// +inline const Vector4 maxPerElem( const Vector4 & vec0, const Vector4 & vec1 ); + +// Minimum of two 4-D vectors per element +// +inline const Vector4 minPerElem( const Vector4 & vec0, const Vector4 & vec1 ); + +// Maximum element of a 4-D vector +// +inline float maxElem( const Vector4 & vec ); + +// Minimum element of a 4-D vector +// +inline float minElem( const Vector4 & vec ); + +// Compute the sum of all elements of a 4-D vector +// +inline float sum( const Vector4 & vec ); + +// Compute the dot product of two 4-D vectors +// +inline float dot( const Vector4 & vec0, const Vector4 & vec1 ); + +// Compute the square of the length of a 4-D vector +// +inline float lengthSqr( const Vector4 & vec ); + +// Compute the length of a 4-D vector +// +inline float length( const Vector4 & vec ); + +// Normalize a 4-D vector +// NOTE: +// The result is unpredictable when all elements of vec are at or near zero. +// +inline const Vector4 normalize( const Vector4 & vec ); + +// Outer product of two 4-D vectors +// +inline const Matrix4 outer( const Vector4 & vec0, const Vector4 & vec1 ); + +// Linear interpolation between two 4-D vectors +// NOTE: +// Does not clamp t between 0 and 1. +// +inline const Vector4 lerp( float t, const Vector4 & vec0, const Vector4 & vec1 ); + +// Spherical linear interpolation between two 4-D vectors +// NOTE: +// The result is unpredictable if the vectors point in opposite directions. +// Does not clamp t between 0 and 1. +// +inline const Vector4 slerp( float t, const Vector4 & unitVec0, const Vector4 & unitVec1 ); + +// Conditionally select between two 4-D vectors +// +inline const Vector4 select( const Vector4 & vec0, const Vector4 & vec1, bool select1 ); + +#ifdef _VECTORMATH_DEBUG + +// Print a 4-D vector +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( const Vector4 & vec ); + +// Print a 4-D vector and an associated string identifier +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( const Vector4 & vec, const char * name ); + +#endif + +// A 3-D point in array-of-structures format +// +class Point3 +{ + float mX; + float mY; + float mZ; +#ifndef __GNUC__ + float d; +#endif + +public: + // Default constructor; does no initialization + // + inline Point3( ) { }; + + // Copy a 3-D point + // + inline Point3( const Point3 & pnt ); + + // Construct a 3-D point from x, y, and z elements + // + inline Point3( float x, float y, float z ); + + // Copy elements from a 3-D vector into a 3-D point + // + explicit inline Point3( const Vector3 & vec ); + + // Set all elements of a 3-D point to the same scalar value + // + explicit inline Point3( float scalar ); + + // Assign one 3-D point to another + // + inline Point3 & operator =( const Point3 & pnt ); + + // Set the x element of a 3-D point + // + inline Point3 & setX( float x ); + + // Set the y element of a 3-D point + // + inline Point3 & setY( float y ); + + // Set the z element of a 3-D point + // + inline Point3 & setZ( float z ); + + // Get the x element of a 3-D point + // + inline float getX( ) const; + + // Get the y element of a 3-D point + // + inline float getY( ) const; + + // Get the z element of a 3-D point + // + inline float getZ( ) const; + + // Set an x, y, or z element of a 3-D point by index + // + inline Point3 & setElem( int idx, float value ); + + // Get an x, y, or z element of a 3-D point by index + // + inline float getElem( int idx ) const; + + // Subscripting operator to set or get an element + // + inline float & operator []( int idx ); + + // Subscripting operator to get an element + // + inline float operator []( int idx ) const; + + // Subtract a 3-D point from another 3-D point + // + inline const Vector3 operator -( const Point3 & pnt ) const; + + // Add a 3-D point to a 3-D vector + // + inline const Point3 operator +( const Vector3 & vec ) const; + + // Subtract a 3-D vector from a 3-D point + // + inline const Point3 operator -( const Vector3 & vec ) const; + + // Perform compound assignment and addition with a 3-D vector + // + inline Point3 & operator +=( const Vector3 & vec ); + + // Perform compound assignment and subtraction by a 3-D vector + // + inline Point3 & operator -=( const Vector3 & vec ); + +} +#ifdef __GNUC__ +__attribute__ ((aligned(16))) +#endif +; + +// Multiply two 3-D points per element +// +inline const Point3 mulPerElem( const Point3 & pnt0, const Point3 & pnt1 ); + +// Divide two 3-D points per element +// NOTE: +// Floating-point behavior matches standard library function divf4. +// +inline const Point3 divPerElem( const Point3 & pnt0, const Point3 & pnt1 ); + +// Compute the reciprocal of a 3-D point per element +// NOTE: +// Floating-point behavior matches standard library function recipf4. +// +inline const Point3 recipPerElem( const Point3 & pnt ); + +// Compute the square root of a 3-D point per element +// NOTE: +// Floating-point behavior matches standard library function sqrtf4. +// +inline const Point3 sqrtPerElem( const Point3 & pnt ); + +// Compute the reciprocal square root of a 3-D point per element +// NOTE: +// Floating-point behavior matches standard library function rsqrtf4. +// +inline const Point3 rsqrtPerElem( const Point3 & pnt ); + +// Compute the absolute value of a 3-D point per element +// +inline const Point3 absPerElem( const Point3 & pnt ); + +// Copy sign from one 3-D point to another, per element +// +inline const Point3 copySignPerElem( const Point3 & pnt0, const Point3 & pnt1 ); + +// Maximum of two 3-D points per element +// +inline const Point3 maxPerElem( const Point3 & pnt0, const Point3 & pnt1 ); + +// Minimum of two 3-D points per element +// +inline const Point3 minPerElem( const Point3 & pnt0, const Point3 & pnt1 ); + +// Maximum element of a 3-D point +// +inline float maxElem( const Point3 & pnt ); + +// Minimum element of a 3-D point +// +inline float minElem( const Point3 & pnt ); + +// Compute the sum of all elements of a 3-D point +// +inline float sum( const Point3 & pnt ); + +// Apply uniform scale to a 3-D point +// +inline const Point3 scale( const Point3 & pnt, float scaleVal ); + +// Apply non-uniform scale to a 3-D point +// +inline const Point3 scale( const Point3 & pnt, const Vector3 & scaleVec ); + +// Scalar projection of a 3-D point on a unit-length 3-D vector +// +inline float projection( const Point3 & pnt, const Vector3 & unitVec ); + +// Compute the square of the distance of a 3-D point from the coordinate-system origin +// +inline float distSqrFromOrigin( const Point3 & pnt ); + +// Compute the distance of a 3-D point from the coordinate-system origin +// +inline float distFromOrigin( const Point3 & pnt ); + +// Compute the square of the distance between two 3-D points +// +inline float distSqr( const Point3 & pnt0, const Point3 & pnt1 ); + +// Compute the distance between two 3-D points +// +inline float dist( const Point3 & pnt0, const Point3 & pnt1 ); + +// Linear interpolation between two 3-D points +// NOTE: +// Does not clamp t between 0 and 1. +// +inline const Point3 lerp( float t, const Point3 & pnt0, const Point3 & pnt1 ); + +// Conditionally select between two 3-D points +// +inline const Point3 select( const Point3 & pnt0, const Point3 & pnt1, bool select1 ); + +#ifdef _VECTORMATH_DEBUG + +// Print a 3-D point +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( const Point3 & pnt ); + +// Print a 3-D point and an associated string identifier +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( const Point3 & pnt, const char * name ); + +#endif + +// A quaternion in array-of-structures format +// +class Quat +{ + float mX; + float mY; + float mZ; + float mW; + +public: + // Default constructor; does no initialization + // + inline Quat( ) { }; + + // Copy a quaternion + // + inline Quat( const Quat & quat ); + + // Construct a quaternion from x, y, z, and w elements + // + inline Quat( float x, float y, float z, float w ); + + // Construct a quaternion from a 3-D vector and a scalar + // + inline Quat( const Vector3 & xyz, float w ); + + // Copy elements from a 4-D vector into a quaternion + // + explicit inline Quat( const Vector4 & vec ); + + // Convert a rotation matrix to a unit-length quaternion + // + explicit inline Quat( const Matrix3 & rotMat ); + + // Set all elements of a quaternion to the same scalar value + // + explicit inline Quat( float scalar ); + + // Assign one quaternion to another + // + inline Quat & operator =( const Quat & quat ); + + // Set the x, y, and z elements of a quaternion + // NOTE: + // This function does not change the w element. + // + inline Quat & setXYZ( const Vector3 & vec ); + + // Get the x, y, and z elements of a quaternion + // + inline const Vector3 getXYZ( ) const; + + // Set the x element of a quaternion + // + inline Quat & setX( float x ); + + // Set the y element of a quaternion + // + inline Quat & setY( float y ); + + // Set the z element of a quaternion + // + inline Quat & setZ( float z ); + + // Set the w element of a quaternion + // + inline Quat & setW( float w ); + + // Get the x element of a quaternion + // + inline float getX( ) const; + + // Get the y element of a quaternion + // + inline float getY( ) const; + + // Get the z element of a quaternion + // + inline float getZ( ) const; + + // Get the w element of a quaternion + // + inline float getW( ) const; + + // Set an x, y, z, or w element of a quaternion by index + // + inline Quat & setElem( int idx, float value ); + + // Get an x, y, z, or w element of a quaternion by index + // + inline float getElem( int idx ) const; + + // Subscripting operator to set or get an element + // + inline float & operator []( int idx ); + + // Subscripting operator to get an element + // + inline float operator []( int idx ) const; + + // Add two quaternions + // + inline const Quat operator +( const Quat & quat ) const; + + // Subtract a quaternion from another quaternion + // + inline const Quat operator -( const Quat & quat ) const; + + // Multiply two quaternions + // + inline const Quat operator *( const Quat & quat ) const; + + // Multiply a quaternion by a scalar + // + inline const Quat operator *( float scalar ) const; + + // Divide a quaternion by a scalar + // + inline const Quat operator /( float scalar ) const; + + // Perform compound assignment and addition with a quaternion + // + inline Quat & operator +=( const Quat & quat ); + + // Perform compound assignment and subtraction by a quaternion + // + inline Quat & operator -=( const Quat & quat ); + + // Perform compound assignment and multiplication by a quaternion + // + inline Quat & operator *=( const Quat & quat ); + + // Perform compound assignment and multiplication by a scalar + // + inline Quat & operator *=( float scalar ); + + // Perform compound assignment and division by a scalar + // + inline Quat & operator /=( float scalar ); + + // Negate all elements of a quaternion + // + inline const Quat operator -( ) const; + + // Construct an identity quaternion + // + static inline const Quat identity( ); + + // Construct a quaternion to rotate between two unit-length 3-D vectors + // NOTE: + // The result is unpredictable if unitVec0 and unitVec1 point in opposite directions. + // + static inline const Quat rotation( const Vector3 & unitVec0, const Vector3 & unitVec1 ); + + // Construct a quaternion to rotate around a unit-length 3-D vector + // + static inline const Quat rotation( float radians, const Vector3 & unitVec ); + + // Construct a quaternion to rotate around the x axis + // + static inline const Quat rotationX( float radians ); + + // Construct a quaternion to rotate around the y axis + // + static inline const Quat rotationY( float radians ); + + // Construct a quaternion to rotate around the z axis + // + static inline const Quat rotationZ( float radians ); + +} +#ifdef __GNUC__ +__attribute__ ((aligned(16))) +#endif +; + +// Multiply a quaternion by a scalar +// +inline const Quat operator *( float scalar, const Quat & quat ); + +// Compute the conjugate of a quaternion +// +inline const Quat conj( const Quat & quat ); + +// Use a unit-length quaternion to rotate a 3-D vector +// +inline const Vector3 rotate( const Quat & unitQuat, const Vector3 & vec ); + +// Compute the dot product of two quaternions +// +inline float dot( const Quat & quat0, const Quat & quat1 ); + +// Compute the norm of a quaternion +// +inline float norm( const Quat & quat ); + +// Compute the length of a quaternion +// +inline float length( const Quat & quat ); + +// Normalize a quaternion +// NOTE: +// The result is unpredictable when all elements of quat are at or near zero. +// +inline const Quat normalize( const Quat & quat ); + +// Linear interpolation between two quaternions +// NOTE: +// Does not clamp t between 0 and 1. +// +inline const Quat lerp( float t, const Quat & quat0, const Quat & quat1 ); + +// Spherical linear interpolation between two quaternions +// NOTE: +// Interpolates along the shortest path between orientations. +// Does not clamp t between 0 and 1. +// +inline const Quat slerp( float t, const Quat & unitQuat0, const Quat & unitQuat1 ); + +// Spherical quadrangle interpolation +// +inline const Quat squad( float t, const Quat & unitQuat0, const Quat & unitQuat1, const Quat & unitQuat2, const Quat & unitQuat3 ); + +// Conditionally select between two quaternions +// +inline const Quat select( const Quat & quat0, const Quat & quat1, bool select1 ); + +#ifdef _VECTORMATH_DEBUG + +// Print a quaternion +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( const Quat & quat ); + +// Print a quaternion and an associated string identifier +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( const Quat & quat, const char * name ); + +#endif + +// A 3x3 matrix in array-of-structures format +// +class Matrix3 +{ + Vector3 mCol0; + Vector3 mCol1; + Vector3 mCol2; + +public: + // Default constructor; does no initialization + // + inline Matrix3( ) { }; + + // Copy a 3x3 matrix + // + inline Matrix3( const Matrix3 & mat ); + + // Construct a 3x3 matrix containing the specified columns + // + inline Matrix3( const Vector3 & col0, const Vector3 & col1, const Vector3 & col2 ); + + // Construct a 3x3 rotation matrix from a unit-length quaternion + // + explicit inline Matrix3( const Quat & unitQuat ); + + // Set all elements of a 3x3 matrix to the same scalar value + // + explicit inline Matrix3( float scalar ); + + // Assign one 3x3 matrix to another + // + inline Matrix3 & operator =( const Matrix3 & mat ); + + // Set column 0 of a 3x3 matrix + // + inline Matrix3 & setCol0( const Vector3 & col0 ); + + // Set column 1 of a 3x3 matrix + // + inline Matrix3 & setCol1( const Vector3 & col1 ); + + // Set column 2 of a 3x3 matrix + // + inline Matrix3 & setCol2( const Vector3 & col2 ); + + // Get column 0 of a 3x3 matrix + // + inline const Vector3 getCol0( ) const; + + // Get column 1 of a 3x3 matrix + // + inline const Vector3 getCol1( ) const; + + // Get column 2 of a 3x3 matrix + // + inline const Vector3 getCol2( ) const; + + // Set the column of a 3x3 matrix referred to by the specified index + // + inline Matrix3 & setCol( int col, const Vector3 & vec ); + + // Set the row of a 3x3 matrix referred to by the specified index + // + inline Matrix3 & setRow( int row, const Vector3 & vec ); + + // Get the column of a 3x3 matrix referred to by the specified index + // + inline const Vector3 getCol( int col ) const; + + // Get the row of a 3x3 matrix referred to by the specified index + // + inline const Vector3 getRow( int row ) const; + + // Subscripting operator to set or get a column + // + inline Vector3 & operator []( int col ); + + // Subscripting operator to get a column + // + inline const Vector3 operator []( int col ) const; + + // Set the element of a 3x3 matrix referred to by column and row indices + // + inline Matrix3 & setElem( int col, int row, float val ); + + // Get the element of a 3x3 matrix referred to by column and row indices + // + inline float getElem( int col, int row ) const; + + // Add two 3x3 matrices + // + inline const Matrix3 operator +( const Matrix3 & mat ) const; + + // Subtract a 3x3 matrix from another 3x3 matrix + // + inline const Matrix3 operator -( const Matrix3 & mat ) const; + + // Negate all elements of a 3x3 matrix + // + inline const Matrix3 operator -( ) const; + + // Multiply a 3x3 matrix by a scalar + // + inline const Matrix3 operator *( float scalar ) const; + + // Multiply a 3x3 matrix by a 3-D vector + // + inline const Vector3 operator *( const Vector3 & vec ) const; + + // Multiply two 3x3 matrices + // + inline const Matrix3 operator *( const Matrix3 & mat ) const; + + // Perform compound assignment and addition with a 3x3 matrix + // + inline Matrix3 & operator +=( const Matrix3 & mat ); + + // Perform compound assignment and subtraction by a 3x3 matrix + // + inline Matrix3 & operator -=( const Matrix3 & mat ); + + // Perform compound assignment and multiplication by a scalar + // + inline Matrix3 & operator *=( float scalar ); + + // Perform compound assignment and multiplication by a 3x3 matrix + // + inline Matrix3 & operator *=( const Matrix3 & mat ); + + // Construct an identity 3x3 matrix + // + static inline const Matrix3 identity( ); + + // Construct a 3x3 matrix to rotate around the x axis + // + static inline const Matrix3 rotationX( float radians ); + + // Construct a 3x3 matrix to rotate around the y axis + // + static inline const Matrix3 rotationY( float radians ); + + // Construct a 3x3 matrix to rotate around the z axis + // + static inline const Matrix3 rotationZ( float radians ); + + // Construct a 3x3 matrix to rotate around the x, y, and z axes + // + static inline const Matrix3 rotationZYX( const Vector3 & radiansXYZ ); + + // Construct a 3x3 matrix to rotate around a unit-length 3-D vector + // + static inline const Matrix3 rotation( float radians, const Vector3 & unitVec ); + + // Construct a rotation matrix from a unit-length quaternion + // + static inline const Matrix3 rotation( const Quat & unitQuat ); + + // Construct a 3x3 matrix to perform scaling + // + static inline const Matrix3 scale( const Vector3 & scaleVec ); + +}; +// Multiply a 3x3 matrix by a scalar +// +inline const Matrix3 operator *( float scalar, const Matrix3 & mat ); + +// Append (post-multiply) a scale transformation to a 3x3 matrix +// NOTE: +// Faster than creating and multiplying a scale transformation matrix. +// +inline const Matrix3 appendScale( const Matrix3 & mat, const Vector3 & scaleVec ); + +// Prepend (pre-multiply) a scale transformation to a 3x3 matrix +// NOTE: +// Faster than creating and multiplying a scale transformation matrix. +// +inline const Matrix3 prependScale( const Vector3 & scaleVec, const Matrix3 & mat ); + +// Multiply two 3x3 matrices per element +// +inline const Matrix3 mulPerElem( const Matrix3 & mat0, const Matrix3 & mat1 ); + +// Compute the absolute value of a 3x3 matrix per element +// +inline const Matrix3 absPerElem( const Matrix3 & mat ); + +// Transpose of a 3x3 matrix +// +inline const Matrix3 transpose( const Matrix3 & mat ); + +// Compute the inverse of a 3x3 matrix +// NOTE: +// Result is unpredictable when the determinant of mat is equal to or near 0. +// +inline const Matrix3 inverse( const Matrix3 & mat ); + +// Determinant of a 3x3 matrix +// +inline float determinant( const Matrix3 & mat ); + +// Conditionally select between two 3x3 matrices +// +inline const Matrix3 select( const Matrix3 & mat0, const Matrix3 & mat1, bool select1 ); + +#ifdef _VECTORMATH_DEBUG + +// Print a 3x3 matrix +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( const Matrix3 & mat ); + +// Print a 3x3 matrix and an associated string identifier +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( const Matrix3 & mat, const char * name ); + +#endif + +// A 4x4 matrix in array-of-structures format +// +class Matrix4 +{ + Vector4 mCol0; + Vector4 mCol1; + Vector4 mCol2; + Vector4 mCol3; + +public: + // Default constructor; does no initialization + // + inline Matrix4( ) { }; + + // Copy a 4x4 matrix + // + inline Matrix4( const Matrix4 & mat ); + + // Construct a 4x4 matrix containing the specified columns + // + inline Matrix4( const Vector4 & col0, const Vector4 & col1, const Vector4 & col2, const Vector4 & col3 ); + + // Construct a 4x4 matrix from a 3x4 transformation matrix + // + explicit inline Matrix4( const Transform3 & mat ); + + // Construct a 4x4 matrix from a 3x3 matrix and a 3-D vector + // + inline Matrix4( const Matrix3 & mat, const Vector3 & translateVec ); + + // Construct a 4x4 matrix from a unit-length quaternion and a 3-D vector + // + inline Matrix4( const Quat & unitQuat, const Vector3 & translateVec ); + + // Set all elements of a 4x4 matrix to the same scalar value + // + explicit inline Matrix4( float scalar ); + + // Assign one 4x4 matrix to another + // + inline Matrix4 & operator =( const Matrix4 & mat ); + + // Set the upper-left 3x3 submatrix + // NOTE: + // This function does not change the bottom row elements. + // + inline Matrix4 & setUpper3x3( const Matrix3 & mat3 ); + + // Get the upper-left 3x3 submatrix of a 4x4 matrix + // + inline const Matrix3 getUpper3x3( ) const; + + // Set translation component + // NOTE: + // This function does not change the bottom row elements. + // + inline Matrix4 & setTranslation( const Vector3 & translateVec ); + + // Get the translation component of a 4x4 matrix + // + inline const Vector3 getTranslation( ) const; + + // Set column 0 of a 4x4 matrix + // + inline Matrix4 & setCol0( const Vector4 & col0 ); + + // Set column 1 of a 4x4 matrix + // + inline Matrix4 & setCol1( const Vector4 & col1 ); + + // Set column 2 of a 4x4 matrix + // + inline Matrix4 & setCol2( const Vector4 & col2 ); + + // Set column 3 of a 4x4 matrix + // + inline Matrix4 & setCol3( const Vector4 & col3 ); + + // Get column 0 of a 4x4 matrix + // + inline const Vector4 getCol0( ) const; + + // Get column 1 of a 4x4 matrix + // + inline const Vector4 getCol1( ) const; + + // Get column 2 of a 4x4 matrix + // + inline const Vector4 getCol2( ) const; + + // Get column 3 of a 4x4 matrix + // + inline const Vector4 getCol3( ) const; + + // Set the column of a 4x4 matrix referred to by the specified index + // + inline Matrix4 & setCol( int col, const Vector4 & vec ); + + // Set the row of a 4x4 matrix referred to by the specified index + // + inline Matrix4 & setRow( int row, const Vector4 & vec ); + + // Get the column of a 4x4 matrix referred to by the specified index + // + inline const Vector4 getCol( int col ) const; + + // Get the row of a 4x4 matrix referred to by the specified index + // + inline const Vector4 getRow( int row ) const; + + // Subscripting operator to set or get a column + // + inline Vector4 & operator []( int col ); + + // Subscripting operator to get a column + // + inline const Vector4 operator []( int col ) const; + + // Set the element of a 4x4 matrix referred to by column and row indices + // + inline Matrix4 & setElem( int col, int row, float val ); + + // Get the element of a 4x4 matrix referred to by column and row indices + // + inline float getElem( int col, int row ) const; + + // Add two 4x4 matrices + // + inline const Matrix4 operator +( const Matrix4 & mat ) const; + + // Subtract a 4x4 matrix from another 4x4 matrix + // + inline const Matrix4 operator -( const Matrix4 & mat ) const; + + // Negate all elements of a 4x4 matrix + // + inline const Matrix4 operator -( ) const; + + // Multiply a 4x4 matrix by a scalar + // + inline const Matrix4 operator *( float scalar ) const; + + // Multiply a 4x4 matrix by a 4-D vector + // + inline const Vector4 operator *( const Vector4 & vec ) const; + + // Multiply a 4x4 matrix by a 3-D vector + // + inline const Vector4 operator *( const Vector3 & vec ) const; + + // Multiply a 4x4 matrix by a 3-D point + // + inline const Vector4 operator *( const Point3 & pnt ) const; + + // Multiply two 4x4 matrices + // + inline const Matrix4 operator *( const Matrix4 & mat ) const; + + // Multiply a 4x4 matrix by a 3x4 transformation matrix + // + inline const Matrix4 operator *( const Transform3 & tfrm ) const; + + // Perform compound assignment and addition with a 4x4 matrix + // + inline Matrix4 & operator +=( const Matrix4 & mat ); + + // Perform compound assignment and subtraction by a 4x4 matrix + // + inline Matrix4 & operator -=( const Matrix4 & mat ); + + // Perform compound assignment and multiplication by a scalar + // + inline Matrix4 & operator *=( float scalar ); + + // Perform compound assignment and multiplication by a 4x4 matrix + // + inline Matrix4 & operator *=( const Matrix4 & mat ); + + // Perform compound assignment and multiplication by a 3x4 transformation matrix + // + inline Matrix4 & operator *=( const Transform3 & tfrm ); + + // Construct an identity 4x4 matrix + // + static inline const Matrix4 identity( ); + + // Construct a 4x4 matrix to rotate around the x axis + // + static inline const Matrix4 rotationX( float radians ); + + // Construct a 4x4 matrix to rotate around the y axis + // + static inline const Matrix4 rotationY( float radians ); + + // Construct a 4x4 matrix to rotate around the z axis + // + static inline const Matrix4 rotationZ( float radians ); + + // Construct a 4x4 matrix to rotate around the x, y, and z axes + // + static inline const Matrix4 rotationZYX( const Vector3 & radiansXYZ ); + + // Construct a 4x4 matrix to rotate around a unit-length 3-D vector + // + static inline const Matrix4 rotation( float radians, const Vector3 & unitVec ); + + // Construct a rotation matrix from a unit-length quaternion + // + static inline const Matrix4 rotation( const Quat & unitQuat ); + + // Construct a 4x4 matrix to perform scaling + // + static inline const Matrix4 scale( const Vector3 & scaleVec ); + + // Construct a 4x4 matrix to perform translation + // + static inline const Matrix4 translation( const Vector3 & translateVec ); + + // Construct viewing matrix based on eye position, position looked at, and up direction + // + static inline const Matrix4 lookAt( const Point3 & eyePos, const Point3 & lookAtPos, const Vector3 & upVec ); + + // Construct a perspective projection matrix + // + static inline const Matrix4 perspective( float fovyRadians, float aspect, float zNear, float zFar ); + + // Construct a perspective projection matrix based on frustum + // + static inline const Matrix4 frustum( float left, float right, float bottom, float top, float zNear, float zFar ); + + // Construct an orthographic projection matrix + // + static inline const Matrix4 orthographic( float left, float right, float bottom, float top, float zNear, float zFar ); + +}; +// Multiply a 4x4 matrix by a scalar +// +inline const Matrix4 operator *( float scalar, const Matrix4 & mat ); + +// Append (post-multiply) a scale transformation to a 4x4 matrix +// NOTE: +// Faster than creating and multiplying a scale transformation matrix. +// +inline const Matrix4 appendScale( const Matrix4 & mat, const Vector3 & scaleVec ); + +// Prepend (pre-multiply) a scale transformation to a 4x4 matrix +// NOTE: +// Faster than creating and multiplying a scale transformation matrix. +// +inline const Matrix4 prependScale( const Vector3 & scaleVec, const Matrix4 & mat ); + +// Multiply two 4x4 matrices per element +// +inline const Matrix4 mulPerElem( const Matrix4 & mat0, const Matrix4 & mat1 ); + +// Compute the absolute value of a 4x4 matrix per element +// +inline const Matrix4 absPerElem( const Matrix4 & mat ); + +// Transpose of a 4x4 matrix +// +inline const Matrix4 transpose( const Matrix4 & mat ); + +// Compute the inverse of a 4x4 matrix +// NOTE: +// Result is unpredictable when the determinant of mat is equal to or near 0. +// +inline const Matrix4 inverse( const Matrix4 & mat ); + +// Compute the inverse of a 4x4 matrix, which is expected to be an affine matrix +// NOTE: +// This can be used to achieve better performance than a general inverse when the specified 4x4 matrix meets the given restrictions. The result is unpredictable when the determinant of mat is equal to or near 0. +// +inline const Matrix4 affineInverse( const Matrix4 & mat ); + +// Compute the inverse of a 4x4 matrix, which is expected to be an affine matrix with an orthogonal upper-left 3x3 submatrix +// NOTE: +// This can be used to achieve better performance than a general inverse when the specified 4x4 matrix meets the given restrictions. +// +inline const Matrix4 orthoInverse( const Matrix4 & mat ); + +// Determinant of a 4x4 matrix +// +inline float determinant( const Matrix4 & mat ); + +// Conditionally select between two 4x4 matrices +// +inline const Matrix4 select( const Matrix4 & mat0, const Matrix4 & mat1, bool select1 ); + +#ifdef _VECTORMATH_DEBUG + +// Print a 4x4 matrix +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( const Matrix4 & mat ); + +// Print a 4x4 matrix and an associated string identifier +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( const Matrix4 & mat, const char * name ); + +#endif + +// A 3x4 transformation matrix in array-of-structures format +// +class Transform3 +{ + Vector3 mCol0; + Vector3 mCol1; + Vector3 mCol2; + Vector3 mCol3; + +public: + // Default constructor; does no initialization + // + inline Transform3( ) { }; + + // Copy a 3x4 transformation matrix + // + inline Transform3( const Transform3 & tfrm ); + + // Construct a 3x4 transformation matrix containing the specified columns + // + inline Transform3( const Vector3 & col0, const Vector3 & col1, const Vector3 & col2, const Vector3 & col3 ); + + // Construct a 3x4 transformation matrix from a 3x3 matrix and a 3-D vector + // + inline Transform3( const Matrix3 & tfrm, const Vector3 & translateVec ); + + // Construct a 3x4 transformation matrix from a unit-length quaternion and a 3-D vector + // + inline Transform3( const Quat & unitQuat, const Vector3 & translateVec ); + + // Set all elements of a 3x4 transformation matrix to the same scalar value + // + explicit inline Transform3( float scalar ); + + // Assign one 3x4 transformation matrix to another + // + inline Transform3 & operator =( const Transform3 & tfrm ); + + // Set the upper-left 3x3 submatrix + // + inline Transform3 & setUpper3x3( const Matrix3 & mat3 ); + + // Get the upper-left 3x3 submatrix of a 3x4 transformation matrix + // + inline const Matrix3 getUpper3x3( ) const; + + // Set translation component + // + inline Transform3 & setTranslation( const Vector3 & translateVec ); + + // Get the translation component of a 3x4 transformation matrix + // + inline const Vector3 getTranslation( ) const; + + // Set column 0 of a 3x4 transformation matrix + // + inline Transform3 & setCol0( const Vector3 & col0 ); + + // Set column 1 of a 3x4 transformation matrix + // + inline Transform3 & setCol1( const Vector3 & col1 ); + + // Set column 2 of a 3x4 transformation matrix + // + inline Transform3 & setCol2( const Vector3 & col2 ); + + // Set column 3 of a 3x4 transformation matrix + // + inline Transform3 & setCol3( const Vector3 & col3 ); + + // Get column 0 of a 3x4 transformation matrix + // + inline const Vector3 getCol0( ) const; + + // Get column 1 of a 3x4 transformation matrix + // + inline const Vector3 getCol1( ) const; + + // Get column 2 of a 3x4 transformation matrix + // + inline const Vector3 getCol2( ) const; + + // Get column 3 of a 3x4 transformation matrix + // + inline const Vector3 getCol3( ) const; + + // Set the column of a 3x4 transformation matrix referred to by the specified index + // + inline Transform3 & setCol( int col, const Vector3 & vec ); + + // Set the row of a 3x4 transformation matrix referred to by the specified index + // + inline Transform3 & setRow( int row, const Vector4 & vec ); + + // Get the column of a 3x4 transformation matrix referred to by the specified index + // + inline const Vector3 getCol( int col ) const; + + // Get the row of a 3x4 transformation matrix referred to by the specified index + // + inline const Vector4 getRow( int row ) const; + + // Subscripting operator to set or get a column + // + inline Vector3 & operator []( int col ); + + // Subscripting operator to get a column + // + inline const Vector3 operator []( int col ) const; + + // Set the element of a 3x4 transformation matrix referred to by column and row indices + // + inline Transform3 & setElem( int col, int row, float val ); + + // Get the element of a 3x4 transformation matrix referred to by column and row indices + // + inline float getElem( int col, int row ) const; + + // Multiply a 3x4 transformation matrix by a 3-D vector + // + inline const Vector3 operator *( const Vector3 & vec ) const; + + // Multiply a 3x4 transformation matrix by a 3-D point + // + inline const Point3 operator *( const Point3 & pnt ) const; + + // Multiply two 3x4 transformation matrices + // + inline const Transform3 operator *( const Transform3 & tfrm ) const; + + // Perform compound assignment and multiplication by a 3x4 transformation matrix + // + inline Transform3 & operator *=( const Transform3 & tfrm ); + + // Construct an identity 3x4 transformation matrix + // + static inline const Transform3 identity( ); + + // Construct a 3x4 transformation matrix to rotate around the x axis + // + static inline const Transform3 rotationX( float radians ); + + // Construct a 3x4 transformation matrix to rotate around the y axis + // + static inline const Transform3 rotationY( float radians ); + + // Construct a 3x4 transformation matrix to rotate around the z axis + // + static inline const Transform3 rotationZ( float radians ); + + // Construct a 3x4 transformation matrix to rotate around the x, y, and z axes + // + static inline const Transform3 rotationZYX( const Vector3 & radiansXYZ ); + + // Construct a 3x4 transformation matrix to rotate around a unit-length 3-D vector + // + static inline const Transform3 rotation( float radians, const Vector3 & unitVec ); + + // Construct a rotation matrix from a unit-length quaternion + // + static inline const Transform3 rotation( const Quat & unitQuat ); + + // Construct a 3x4 transformation matrix to perform scaling + // + static inline const Transform3 scale( const Vector3 & scaleVec ); + + // Construct a 3x4 transformation matrix to perform translation + // + static inline const Transform3 translation( const Vector3 & translateVec ); + +}; +// Append (post-multiply) a scale transformation to a 3x4 transformation matrix +// NOTE: +// Faster than creating and multiplying a scale transformation matrix. +// +inline const Transform3 appendScale( const Transform3 & tfrm, const Vector3 & scaleVec ); + +// Prepend (pre-multiply) a scale transformation to a 3x4 transformation matrix +// NOTE: +// Faster than creating and multiplying a scale transformation matrix. +// +inline const Transform3 prependScale( const Vector3 & scaleVec, const Transform3 & tfrm ); + +// Multiply two 3x4 transformation matrices per element +// +inline const Transform3 mulPerElem( const Transform3 & tfrm0, const Transform3 & tfrm1 ); + +// Compute the absolute value of a 3x4 transformation matrix per element +// +inline const Transform3 absPerElem( const Transform3 & tfrm ); + +// Inverse of a 3x4 transformation matrix +// NOTE: +// Result is unpredictable when the determinant of the left 3x3 submatrix is equal to or near 0. +// +inline const Transform3 inverse( const Transform3 & tfrm ); + +// Compute the inverse of a 3x4 transformation matrix, expected to have an orthogonal upper-left 3x3 submatrix +// NOTE: +// This can be used to achieve better performance than a general inverse when the specified 3x4 transformation matrix meets the given restrictions. +// +inline const Transform3 orthoInverse( const Transform3 & tfrm ); + +// Conditionally select between two 3x4 transformation matrices +// +inline const Transform3 select( const Transform3 & tfrm0, const Transform3 & tfrm1, bool select1 ); + +#ifdef _VECTORMATH_DEBUG + +// Print a 3x4 transformation matrix +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( const Transform3 & tfrm ); + +// Print a 3x4 transformation matrix and an associated string identifier +// NOTE: +// Function is only defined when _VECTORMATH_DEBUG is defined. +// +inline void print( const Transform3 & tfrm, const char * name ); + +#endif + +} // namespace Aos +} // namespace Vectormath + +#include "vec_aos.h" +#include "quat_aos.h" +#include "mat_aos.h" + +#endif diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/vectormath2bullet.h b/Engine/lib/bullet/src/BulletMultiThreaded/vectormath2bullet.h new file mode 100644 index 000000000..efc7a3c92 --- /dev/null +++ b/Engine/lib/bullet/src/BulletMultiThreaded/vectormath2bullet.h @@ -0,0 +1,75 @@ +/* + Copyright (C) 2006, 2007 Sony Computer Entertainment Inc. + All rights reserved. + + Redistribution and use in source and binary forms, + with or without modification, are permitted provided that the + following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Sony Computer Entertainment Inc nor the names + of its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef AOS_VECTORMATH_BULLET_CONVERT_H +#define AOS_VECTORMATH_BULLET_CONVERT_H + +#include +//#include "BulletMultiThreaded/vectormath/scalar/cpp/vectormath_aos.h" + +#include "LinearMath/btVector3.h" +#include "LinearMath/btQuaternion.h" +#include "LinearMath/btMatrix3x3.h" + +inline Vectormath::Aos::Vector3 getVmVector3(const btVector3& bulletVec) +{ + return Vectormath::Aos::Vector3(bulletVec.getX(),bulletVec.getY(),bulletVec.getZ()); +} + +inline btVector3 getBtVector3(const Vectormath::Aos::Vector3& vmVec) +{ + return btVector3(vmVec.getX(),vmVec.getY(),vmVec.getZ()); +} +inline btVector3 getBtVector3(const Vectormath::Aos::Point3& vmVec) +{ + return btVector3(vmVec.getX(),vmVec.getY(),vmVec.getZ()); +} + +inline Vectormath::Aos::Quat getVmQuat(const btQuaternion& bulletQuat) +{ + Vectormath::Aos::Quat vmQuat(bulletQuat.getX(),bulletQuat.getY(),bulletQuat.getZ(),bulletQuat.getW()); + return vmQuat; +} + +inline btQuaternion getBtQuat(const Vectormath::Aos::Quat& vmQuat) +{ + return btQuaternion (vmQuat.getX(),vmQuat.getY(),vmQuat.getZ(),vmQuat.getW()); +} + +inline Vectormath::Aos::Matrix3 getVmMatrix3(const btMatrix3x3& btMat) +{ + Vectormath::Aos::Matrix3 mat( + getVmVector3(btMat.getColumn(0)), + getVmVector3(btMat.getColumn(1)), + getVmVector3(btMat.getColumn(2))); + return mat; +} + + +#endif //AOS_VECTORMATH_BULLET_CONVERT_H diff --git a/Engine/lib/bullet/src/BulletSoftBody/CMakeLists.txt b/Engine/lib/bullet/src/BulletSoftBody/CMakeLists.txt new file mode 100644 index 000000000..70f0fb446 --- /dev/null +++ b/Engine/lib/bullet/src/BulletSoftBody/CMakeLists.txt @@ -0,0 +1,48 @@ + +INCLUDE_DIRECTORIES( +${BULLET_PHYSICS_SOURCE_DIR}/src } +) + +SET(BulletSoftBody_SRCS + btSoftBody.cpp + btSoftBodyConcaveCollisionAlgorithm.cpp + btSoftBodyHelpers.cpp + btSoftBodyRigidBodyCollisionConfiguration.cpp + btSoftRigidCollisionAlgorithm.cpp + btSoftRigidDynamicsWorld.cpp + btSoftSoftCollisionAlgorithm.cpp +) + +SET(BulletSoftBody_HDRS + btSoftBody.h + btSoftBodyConcaveCollisionAlgorithm.h + btSoftBodyHelpers.h + btSoftBodyRigidBodyCollisionConfiguration.h + btSoftRigidCollisionAlgorithm.h + btSoftRigidDynamicsWorld.h + btSoftSoftCollisionAlgorithm.h + btSparseSDF.h +) + + + +ADD_LIBRARY(BulletSoftBody ${BulletSoftBody_SRCS} ${BulletSoftBody_HDRS}) +SET_TARGET_PROPERTIES(BulletSoftBody PROPERTIES VERSION ${BULLET_VERSION}) +SET_TARGET_PROPERTIES(BulletSoftBody PROPERTIES SOVERSION ${BULLET_VERSION}) +IF (BUILD_SHARED_LIBS) + TARGET_LINK_LIBRARIES(BulletSoftBody BulletDynamics) +ENDIF (BUILD_SHARED_LIBS) + +IF (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 2.5) + IF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) + INSTALL(TARGETS BulletSoftBody DESTINATION .) + ELSE (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) + INSTALL(TARGETS BulletSoftBody DESTINATION lib) + INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DESTINATION include FILES_MATCHING PATTERN "*.h") + ENDIF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) +ENDIF (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 2.5) + +IF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) + SET_TARGET_PROPERTIES(BulletSoftBody PROPERTIES FRAMEWORK true) + SET_TARGET_PROPERTIES(BulletSoftBody PROPERTIES PUBLIC_HEADER "${BulletSoftBody_HDRS}") +ENDIF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) diff --git a/Engine/lib/bullet/src/BulletSoftBody/Jamfile b/Engine/lib/bullet/src/BulletSoftBody/Jamfile new file mode 100644 index 000000000..bb6061928 --- /dev/null +++ b/Engine/lib/bullet/src/BulletSoftBody/Jamfile @@ -0,0 +1,9 @@ + +SubDir TOP src BulletSoftBody ; + +Description bulletsoftbody : "Bullet Softbody Dynamics" ; +Library bulletsoftbody : + [ Wildcard *.h *.cpp ] +; + +LibDepends bulletsoftbody : bulletdynamics bulletcollision ; diff --git a/Engine/lib/bullet/src/BulletSoftBody/btSoftBody.cpp b/Engine/lib/bullet/src/BulletSoftBody/btSoftBody.cpp new file mode 100644 index 000000000..431546f17 --- /dev/null +++ b/Engine/lib/bullet/src/BulletSoftBody/btSoftBody.cpp @@ -0,0 +1,2917 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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. +*/ +///btSoftBody implementation by Nathanael Presson + +#include "btSoftBodyInternals.h" + +// +btSoftBody::btSoftBody(btSoftBodyWorldInfo* worldInfo,int node_count, const btVector3* x, const btScalar* m) +:m_worldInfo(worldInfo) +{ + /* Init */ + m_internalType = CO_SOFT_BODY; + m_cfg.aeromodel = eAeroModel::V_Point; + m_cfg.kVCF = 1; + m_cfg.kDG = 0; + m_cfg.kLF = 0; + m_cfg.kDP = 0; + m_cfg.kPR = 0; + m_cfg.kVC = 0; + m_cfg.kDF = (btScalar)0.2; + m_cfg.kMT = 0; + m_cfg.kCHR = (btScalar)1.0; + m_cfg.kKHR = (btScalar)0.1; + m_cfg.kSHR = (btScalar)1.0; + m_cfg.kAHR = (btScalar)0.7; + m_cfg.kSRHR_CL = (btScalar)0.1; + m_cfg.kSKHR_CL = (btScalar)1; + m_cfg.kSSHR_CL = (btScalar)0.5; + m_cfg.kSR_SPLT_CL = (btScalar)0.5; + m_cfg.kSK_SPLT_CL = (btScalar)0.5; + m_cfg.kSS_SPLT_CL = (btScalar)0.5; + m_cfg.maxvolume = (btScalar)1; + m_cfg.timescale = 1; + m_cfg.viterations = 0; + m_cfg.piterations = 1; + m_cfg.diterations = 0; + m_cfg.citerations = 4; + m_cfg.collisions = fCollision::Default; + m_pose.m_bvolume = false; + m_pose.m_bframe = false; + m_pose.m_volume = 0; + m_pose.m_com = btVector3(0,0,0); + m_pose.m_rot.setIdentity(); + m_pose.m_scl.setIdentity(); + m_tag = 0; + m_timeacc = 0; + m_bUpdateRtCst = true; + m_bounds[0] = btVector3(0,0,0); + m_bounds[1] = btVector3(0,0,0); + m_worldTransform.setIdentity(); + setSolver(eSolverPresets::Positions); + /* Default material */ + Material* pm=appendMaterial(); + pm->m_kLST = 1; + pm->m_kAST = 1; + pm->m_kVST = 1; + pm->m_flags = fMaterial::Default; + /* Collision shape */ + ///for now, create a collision shape internally + m_collisionShape = new btSoftBodyCollisionShape(this); + m_collisionShape->setMargin(0.25); + /* Nodes */ + const btScalar margin=getCollisionShape()->getMargin(); + m_nodes.resize(node_count); + for(int i=0,ni=node_count;i0?1/n.m_im:0; + n.m_leaf = m_ndbvt.insert(btDbvtVolume::FromCR(n.m_x,margin),&n); + n.m_material= pm; + } + updateBounds(); + + m_initialWorldTransform.setIdentity(); +} + +// +btSoftBody::~btSoftBody() +{ + //for now, delete the internal shape + delete m_collisionShape; + int i; + + releaseClusters(); + for(i=0;i0) + *pm=*m_materials[0]; + else + ZeroInitialize(*pm); + m_materials.push_back(pm); + return(pm); +} + +// +void btSoftBody::appendNote( const char* text, + const btVector3& o, + const btVector4& c, + Node* n0, + Node* n1, + Node* n2, + Node* n3) +{ + Note n; + ZeroInitialize(n); + n.m_rank = 0; + n.m_text = text; + n.m_offset = o; + n.m_coords[0] = c.x(); + n.m_coords[1] = c.y(); + n.m_coords[2] = c.z(); + n.m_coords[3] = c.w(); + n.m_nodes[0] = n0;n.m_rank+=n0?1:0; + n.m_nodes[1] = n1;n.m_rank+=n1?1:0; + n.m_nodes[2] = n2;n.m_rank+=n2?1:0; + n.m_nodes[3] = n3;n.m_rank+=n3?1:0; + m_notes.push_back(n); +} + +// +void btSoftBody::appendNote( const char* text, + const btVector3& o, + Node* feature) +{ + appendNote(text,o,btVector4(1,0,0,0),feature); +} + +// +void btSoftBody::appendNote( const char* text, + const btVector3& o, + Link* feature) +{ + static const btScalar w=1/(btScalar)2; + appendNote(text,o,btVector4(w,w,0,0), feature->m_n[0], + feature->m_n[1]); +} + +// +void btSoftBody::appendNote( const char* text, + const btVector3& o, + Face* feature) +{ + static const btScalar w=1/(btScalar)3; + appendNote(text,o,btVector4(w,w,w,0), feature->m_n[0], + feature->m_n[1], + feature->m_n[2]); +} + +// +void btSoftBody::appendNode( const btVector3& x,btScalar m) +{ + if(m_nodes.capacity()==m_nodes.size()) + { + pointersToIndices(); + m_nodes.reserve(m_nodes.size()*2+1); + indicesToPointers(); + } + const btScalar margin=getCollisionShape()->getMargin(); + m_nodes.push_back(Node()); + Node& n=m_nodes[m_nodes.size()-1]; + ZeroInitialize(n); + n.m_x = x; + n.m_q = n.m_x; + n.m_im = m>0?1/m:0; + n.m_material = m_materials[0]; + n.m_leaf = m_ndbvt.insert(btDbvtVolume::FromCR(n.m_x,margin),&n); +} + +// +void btSoftBody::appendLink(int model,Material* mat) +{ + Link l; + if(model>=0) + l=m_links[model]; + else + { ZeroInitialize(l);l.m_material=mat?mat:m_materials[0]; } + m_links.push_back(l); +} + +// +void btSoftBody::appendLink( int node0, + int node1, + Material* mat, + bool bcheckexist) +{ + appendLink(&m_nodes[node0],&m_nodes[node1],mat,bcheckexist); +} + +// +void btSoftBody::appendLink( Node* node0, + Node* node1, + Material* mat, + bool bcheckexist) +{ + if((!bcheckexist)||(!checkLink(node0,node1))) + { + appendLink(-1,mat); + Link& l=m_links[m_links.size()-1]; + l.m_n[0] = node0; + l.m_n[1] = node1; + l.m_rl = (l.m_n[0]->m_x-l.m_n[1]->m_x).length(); + m_bUpdateRtCst=true; + } +} + +// +void btSoftBody::appendFace(int model,Material* mat) +{ + Face f; + if(model>=0) + { f=m_faces[model]; } + else + { ZeroInitialize(f);f.m_material=mat?mat:m_materials[0]; } + m_faces.push_back(f); +} + +// +void btSoftBody::appendFace(int node0,int node1,int node2,Material* mat) +{ + if (node0==node1) + return; + if (node1==node2) + return; + if (node2==node0) + return; + + appendFace(-1,mat); + Face& f=m_faces[m_faces.size()-1]; + btAssert(node0!=node1); + btAssert(node1!=node2); + btAssert(node2!=node0); + f.m_n[0] = &m_nodes[node0]; + f.m_n[1] = &m_nodes[node1]; + f.m_n[2] = &m_nodes[node2]; + f.m_ra = AreaOf( f.m_n[0]->m_x, + f.m_n[1]->m_x, + f.m_n[2]->m_x); + m_bUpdateRtCst=true; +} + +// +void btSoftBody::appendTetra(int model,Material* mat) +{ +Tetra t; +if(model>=0) + t=m_tetras[model]; + else + { ZeroInitialize(t);t.m_material=mat?mat:m_materials[0]; } +m_tetras.push_back(t); +} + +// +void btSoftBody::appendTetra(int node0, + int node1, + int node2, + int node3, + Material* mat) +{ + appendTetra(-1,mat); + Tetra& t=m_tetras[m_tetras.size()-1]; + t.m_n[0] = &m_nodes[node0]; + t.m_n[1] = &m_nodes[node1]; + t.m_n[2] = &m_nodes[node2]; + t.m_n[3] = &m_nodes[node3]; + t.m_rv = VolumeOf(t.m_n[0]->m_x,t.m_n[1]->m_x,t.m_n[2]->m_x,t.m_n[3]->m_x); + m_bUpdateRtCst=true; +} + +// +void btSoftBody::appendAnchor(int node,btRigidBody* body, bool disableCollisionBetweenLinkedBodies) +{ + if (disableCollisionBetweenLinkedBodies) + { + if (m_collisionDisabledObjects.findLinearSearch(body)==m_collisionDisabledObjects.size()) + { + m_collisionDisabledObjects.push_back(body); + } + } + + Anchor a; + a.m_node = &m_nodes[node]; + a.m_body = body; + a.m_local = body->getInterpolationWorldTransform().inverse()*a.m_node->m_x; + a.m_node->m_battach = 1; + m_anchors.push_back(a); +} + +// +void btSoftBody::appendLinearJoint(const LJoint::Specs& specs,Cluster* body0,Body body1) +{ + LJoint* pj = new(btAlignedAlloc(sizeof(LJoint),16)) LJoint(); + pj->m_bodies[0] = body0; + pj->m_bodies[1] = body1; + pj->m_refs[0] = pj->m_bodies[0].xform().inverse()*specs.position; + pj->m_refs[1] = pj->m_bodies[1].xform().inverse()*specs.position; + pj->m_cfm = specs.cfm; + pj->m_erp = specs.erp; + pj->m_split = specs.split; + m_joints.push_back(pj); +} + +// +void btSoftBody::appendLinearJoint(const LJoint::Specs& specs,Body body) +{ + appendLinearJoint(specs,m_clusters[0],body); +} + +// +void btSoftBody::appendLinearJoint(const LJoint::Specs& specs,btSoftBody* body) +{ + appendLinearJoint(specs,m_clusters[0],body->m_clusters[0]); +} + +// +void btSoftBody::appendAngularJoint(const AJoint::Specs& specs,Cluster* body0,Body body1) +{ + AJoint* pj = new(btAlignedAlloc(sizeof(AJoint),16)) AJoint(); + pj->m_bodies[0] = body0; + pj->m_bodies[1] = body1; + pj->m_refs[0] = pj->m_bodies[0].xform().inverse().getBasis()*specs.axis; + pj->m_refs[1] = pj->m_bodies[1].xform().inverse().getBasis()*specs.axis; + pj->m_cfm = specs.cfm; + pj->m_erp = specs.erp; + pj->m_split = specs.split; + pj->m_icontrol = specs.icontrol; + m_joints.push_back(pj); +} + +// +void btSoftBody::appendAngularJoint(const AJoint::Specs& specs,Body body) +{ + appendAngularJoint(specs,m_clusters[0],body); +} + +// +void btSoftBody::appendAngularJoint(const AJoint::Specs& specs,btSoftBody* body) +{ + appendAngularJoint(specs,m_clusters[0],body->m_clusters[0]); +} + +// +void btSoftBody::addForce(const btVector3& force) +{ + for(int i=0,ni=m_nodes.size();i0) + { + n.m_f += force; + } +} + +// +void btSoftBody::addVelocity(const btVector3& velocity) +{ + for(int i=0,ni=m_nodes.size();i0) + { + n.m_v = velocity; + } + } +} + + +// +void btSoftBody::addVelocity(const btVector3& velocity,int node) +{ + Node& n=m_nodes[node]; + if(n.m_im>0) + { + n.m_v += velocity; + } +} + +// +void btSoftBody::setMass(int node,btScalar mass) +{ + m_nodes[node].m_im=mass>0?1/mass:0; + m_bUpdateRtCst=true; +} + +// +btScalar btSoftBody::getMass(int node) const +{ + return(m_nodes[node].m_im>0?1/m_nodes[node].m_im:0); +} + +// +btScalar btSoftBody::getTotalMass() const +{ + btScalar mass=0; + for(int i=0;im_x, + f.m_n[1]->m_x, + f.m_n[2]->m_x); + for(int j=0;j<3;++j) + { + f.m_n[j]->m_im+=twicearea; + } + } + for( i=0;i ranks; +ranks.resize(m_nodes.size(),0); +for(int i=0;im_im+=btFabs(t.m_rv); + ranks[int(t.m_n[j]-&m_nodes[0])]+=1; + } + } +for(int i=0;i0) + { + m_nodes[i].m_im=ranks[i]/m_nodes[i].m_im; + } + } +setTotalMass(mass,false); +} + +// +void btSoftBody::setVolumeDensity(btScalar density) +{ +btScalar volume=0; +for(int i=0;igetMargin(); + ATTRIBUTE_ALIGNED16(btDbvtVolume) vol; + + for(int i=0,ni=m_nodes.size();igetMargin(); + ATTRIBUTE_ALIGNED16(btDbvtVolume) vol; + + for(int i=0,ni=m_nodes.size();i0 ? + 1/(m_nodes[i].m_im*tmass) : + kmass/tmass; + } + /* Pos */ + const btVector3 com=evaluateCom(); + m_pose.m_pos.resize(m_nodes.size()); + for( i=0,ni=m_nodes.size();i0) + { + int i,ni; + + const btVector3 org=m_nodes[0].m_x; + for(i=0,ni=m_faces.size();im_x-org,btCross(f.m_n[1]->m_x-org,f.m_n[2]->m_x-org)); + } + vol/=(btScalar)6; + } + return(vol); +} + +// +int btSoftBody::clusterCount() const +{ + return(m_clusters.size()); +} + +// +btVector3 btSoftBody::clusterCom(const Cluster* cluster) +{ + btVector3 com(0,0,0); + for(int i=0,ni=cluster->m_nodes.size();im_nodes[i]->m_x*cluster->m_masses[i]; + } + return(com*cluster->m_imass); +} + +// +btVector3 btSoftBody::clusterCom(int cluster) const +{ + return(clusterCom(m_clusters[cluster])); +} + +// +btVector3 btSoftBody::clusterVelocity(const Cluster* cluster,const btVector3& rpos) +{ + return(cluster->m_lv+btCross(cluster->m_av,rpos)); +} + +// +void btSoftBody::clusterVImpulse(Cluster* cluster,const btVector3& rpos,const btVector3& impulse) +{ + const btVector3 li=cluster->m_imass*impulse; + const btVector3 ai=cluster->m_invwi*btCross(rpos,impulse); + cluster->m_vimpulses[0]+=li;cluster->m_lv+=li; + cluster->m_vimpulses[1]+=ai;cluster->m_av+=ai; + cluster->m_nvimpulses++; +} + +// +void btSoftBody::clusterDImpulse(Cluster* cluster,const btVector3& rpos,const btVector3& impulse) +{ + const btVector3 li=cluster->m_imass*impulse; + const btVector3 ai=cluster->m_invwi*btCross(rpos,impulse); + cluster->m_dimpulses[0]+=li; + cluster->m_dimpulses[1]+=ai; + cluster->m_ndimpulses++; +} + +// +void btSoftBody::clusterImpulse(Cluster* cluster,const btVector3& rpos,const Impulse& impulse) +{ + if(impulse.m_asVelocity) clusterVImpulse(cluster,rpos,impulse.m_velocity); + if(impulse.m_asDrift) clusterDImpulse(cluster,rpos,impulse.m_drift); +} + +// +void btSoftBody::clusterVAImpulse(Cluster* cluster,const btVector3& impulse) +{ + const btVector3 ai=cluster->m_invwi*impulse; + cluster->m_vimpulses[1]+=ai;cluster->m_av+=ai; + cluster->m_nvimpulses++; +} + +// +void btSoftBody::clusterDAImpulse(Cluster* cluster,const btVector3& impulse) +{ + const btVector3 ai=cluster->m_invwi*impulse; + cluster->m_dimpulses[1]+=ai; + cluster->m_ndimpulses++; +} + +// +void btSoftBody::clusterAImpulse(Cluster* cluster,const Impulse& impulse) +{ + if(impulse.m_asVelocity) clusterVAImpulse(cluster,impulse.m_velocity); + if(impulse.m_asDrift) clusterDAImpulse(cluster,impulse.m_drift); +} + +// +void btSoftBody::clusterDCImpulse(Cluster* cluster,const btVector3& impulse) +{ + cluster->m_dimpulses[0]+=impulse*cluster->m_imass; + cluster->m_ndimpulses++; +} + +struct NodeLinks +{ + btAlignedObjectArray m_links; +}; + + + +// +int btSoftBody::generateBendingConstraints(int distance,Material* mat) +{ + int i,j; + + if(distance>1) + { + /* Build graph */ + const int n=m_nodes.size(); + const unsigned inf=(~(unsigned)0)>>1; + unsigned* adj=new unsigned[n*n]; + + +#define IDX(_x_,_y_) ((_y_)*n+(_x_)) + for(j=0;j nodeLinks; + + + /* Build node links */ + nodeLinks.resize(m_nodes.size()); + + for( i=0;isum) + { + adj[IDX(i,j)]=adj[IDX(j,i)]=sum; + } + } + + } + } + } + } else + { + ///generic Floyd's algorithm + for(int k=0;ksum) + { + adj[IDX(i,j)]=adj[IDX(j,i)]=sum; + } + } + } + } + } + + + /* Build links */ + int nlinks=0; + for(j=0;jm_leaf) m_cdbvt.remove(c->m_leaf); + c->~Cluster(); + btAlignedFree(c); + m_clusters.remove(c); +} + +// +void btSoftBody::releaseClusters() +{ + while(m_clusters.size()>0) releaseCluster(0); +} + +// +int btSoftBody::generateClusters(int k,int maxiterations) +{ + int i; + releaseClusters(); + m_clusters.resize(btMin(k,m_nodes.size())); + for(i=0;im_collide= true; + } + k=m_clusters.size(); + if(k>0) + { + /* Initialize */ + btAlignedObjectArray centers; + btVector3 cog(0,0,0); + int i; + for(i=0;im_nodes.push_back(&m_nodes[i]); + } + cog/=(btScalar)m_nodes.size(); + centers.resize(k,cog); + /* Iterate */ + const btScalar slope=16; + bool changed; + int iterations=0; + do { + const btScalar w=2-btMin(1,iterations/slope); + changed=false; + iterations++; + int i; + + for(i=0;im_nodes.size();++j) + { + c+=m_clusters[i]->m_nodes[j]->m_x; + } + if(m_clusters[i]->m_nodes.size()) + { + c /= (btScalar)m_clusters[i]->m_nodes.size(); + c = centers[i]+(c-centers[i])*w; + changed |= ((c-centers[i]).length2()>SIMD_EPSILON); + centers[i] = c; + m_clusters[i]->m_nodes.resize(0); + } + } + for(i=0;im_nodes.push_back(&m_nodes[i]); + } + } while(changed&&(iterations cids; + cids.resize(m_nodes.size(),-1); + for(i=0;im_nodes.size();++j) + { + cids[int(m_clusters[i]->m_nodes[j]-&m_nodes[0])]=i; + } + } + for(i=0;im_nodes.findLinearSearch(&m_nodes[kid])==m_clusters[cid]->m_nodes.size()) + { + m_clusters[cid]->m_nodes.push_back(&m_nodes[kid]); + } + } + } + } + } + /* Master */ + if(m_clusters.size()>1) + { + Cluster* pmaster=new(btAlignedAlloc(sizeof(Cluster),16)) Cluster(); + pmaster->m_collide = false; + pmaster->m_nodes.reserve(m_nodes.size()); + for(int i=0;im_nodes.push_back(&m_nodes[i]); + m_clusters.push_back(pmaster); + btSwap(m_clusters[0],m_clusters[m_clusters.size()-1]); + } + /* Terminate */ + for(i=0;im_nodes.size()==0) + { + releaseCluster(i--); + } + } + } else + { + //create a cluster for each tetrahedron (if tetrahedra exist) or each face + if (m_tetras.size()) + { + m_clusters.resize(m_tetras.size()); + for(i=0;im_collide= true; + } + for (i=0;im_nodes.push_back(m_tetras[i].m_n[j]); + } + } + + } else + { + m_clusters.resize(m_faces.size()); + for(i=0;im_collide= true; + } + + for(i=0;im_nodes.push_back(m_faces[i].m_n[j]); + } + } + } + } + + if (m_clusters.size()) + { + initializeClusters(); + updateClusters(); + + + //for self-collision + m_clusterConnectivity.resize(m_clusters.size()*m_clusters.size()); + { + for (int c0=0;c0m_clusterIndex=c0; + for (int c1=0;c1m_nodes.size();i++) + { + for (int j=0;jm_nodes.size();j++) + { + if (cla->m_nodes[i] == clb->m_nodes[j]) + { + connected=true; + break; + } + } + } + m_clusterConnectivity[c0+c1*m_clusters.size()]=connected; + } + } + } + } + + return(m_clusters.size()); +} + +// +void btSoftBody::refine(ImplicitFn* ifn,btScalar accurary,bool cut) +{ + const Node* nbase = &m_nodes[0]; + int ncount = m_nodes.size(); + btSymMatrix edges(ncount,-2); + int newnodes=0; + int i,j,k,ni; + + /* Filter out */ + for(i=0;iEval(l.m_n[0]->m_x),ifn->Eval(l.m_n[1]->m_x))) + { + btSwap(m_links[i],m_links[m_links.size()-1]); + m_links.pop_back();--i; + } + } + } + /* Fill edges */ + for(i=0;i0) + { + const btVector3 x=Lerp(a.m_x,b.m_x,t); + const btVector3 v=Lerp(a.m_v,b.m_v,t); + btScalar m=0; + if(a.m_im>0) + { + if(b.m_im>0) + { + const btScalar ma=1/a.m_im; + const btScalar mb=1/b.m_im; + const btScalar mc=Lerp(ma,mb,t); + const btScalar f=(ma+mb)/(ma+mb+mc); + a.m_im=1/(ma*f); + b.m_im=1/(mb*f); + m=mc*f; + } + else + { a.m_im/=0.5;m=1/a.m_im; } + } + else + { + if(b.m_im>0) + { b.m_im/=0.5;m=1/b.m_im; } + else + m=0; + } + appendNode(x,m); + edges(i,j)=m_nodes.size()-1; + m_nodes[edges(i,j)].m_v=v; + ++newnodes; + } + } + } + } + nbase=&m_nodes[0]; + /* Refine links */ + for(i=0,ni=m_links.size();i0) + { + appendLink(i); + Link* pft[]={ &m_links[i], + &m_links[m_links.size()-1]}; + pft[0]->m_n[0]=&m_nodes[idx[0]]; + pft[0]->m_n[1]=&m_nodes[ni]; + pft[1]->m_n[0]=&m_nodes[ni]; + pft[1]->m_n[1]=&m_nodes[idx[1]]; + } + } + } + /* Refine faces */ + for(i=0;i0) + { + appendFace(i); + const int l=(k+1)%3; + Face* pft[]={ &m_faces[i], + &m_faces[m_faces.size()-1]}; + pft[0]->m_n[0]=&m_nodes[idx[l]]; + pft[0]->m_n[1]=&m_nodes[idx[j]]; + pft[0]->m_n[2]=&m_nodes[ni]; + pft[1]->m_n[0]=&m_nodes[ni]; + pft[1]->m_n[1]=&m_nodes[idx[k]]; + pft[1]->m_n[2]=&m_nodes[idx[l]]; + appendLink(ni,idx[l],pft[0]->m_material); + --i;break; + } + } + } + } + /* Cut */ + if(cut) + { + btAlignedObjectArray cnodes; + const int pcount=ncount; + int i; + ncount=m_nodes.size(); + cnodes.resize(ncount,0); + /* Nodes */ + for(i=0;i=pcount)||(btFabs(ifn->Eval(x))0) { m*=0.5;m_nodes[i].m_im/=0.5; } + appendNode(x,m); + cnodes[i]=m_nodes.size()-1; + m_nodes[cnodes[i]].m_v=v; + } + } + nbase=&m_nodes[0]; + /* Links */ + for(i=0,ni=m_links.size();iEval(m_nodes[id[0]].m_x)Eval(m_nodes[id[1]].m_x)Eval(n[0]->m_x)Eval(n[1]->m_x)Eval(n[2]->m_x) ranks; + btAlignedObjectArray todelete; + ranks.resize(nnodes,0); + for(i=0,ni=m_links.size();i=0;--i) + { + if(!ranks[i]) todelete.push_back(i); + } + if(todelete.size()) + { + btAlignedObjectArray& map=ranks; + for(int i=0;im_v=v; + pn[1]->m_v=v; + for(i=0,ni=m_links.size();im_n[1]=pn[mtch]; + pft[1]->m_n[0]=pn[1-mtch]; + done=true; + } + } + for(i=0,ni=m_faces.size();im_n[l]=pn[mtch]; + pft[1]->m_n[k]=pn[1-mtch]; + appendLink(pn[0],pft[0]->m_n[(l+1)%3],pft[0]->m_material,true); + appendLink(pn[1],pft[0]->m_n[(l+1)%3],pft[0]->m_material,true); + } + } + } + if(!done) + { + m_ndbvt.remove(pn[0]->m_leaf); + m_ndbvt.remove(pn[1]->m_leaf); + m_nodes.pop_back(); + m_nodes.pop_back(); + } + return(done); +} + +// +bool btSoftBody::rayTest(const btVector3& rayFrom, + const btVector3& rayTo, + sRayCast& results) +{ + if(m_faces.size()&&m_fdbvt.empty()) + initializeFaceTree(); + + results.body = this; + results.fraction = 1.f; + results.feature = eFeature::None; + results.index = -1; + + return(rayTest(rayFrom,rayTo,results.fraction,results.feature,results.index,false)!=0); +} + +// +void btSoftBody::setSolver(eSolverPresets::_ preset) +{ + m_cfg.m_vsequence.clear(); + m_cfg.m_psequence.clear(); + m_cfg.m_dsequence.clear(); + switch(preset) + { + case eSolverPresets::Positions: + m_cfg.m_psequence.push_back(ePSolver::Anchors); + m_cfg.m_psequence.push_back(ePSolver::RContacts); + m_cfg.m_psequence.push_back(ePSolver::SContacts); + m_cfg.m_psequence.push_back(ePSolver::Linear); + break; + case eSolverPresets::Velocities: + m_cfg.m_vsequence.push_back(eVSolver::Linear); + + m_cfg.m_psequence.push_back(ePSolver::Anchors); + m_cfg.m_psequence.push_back(ePSolver::RContacts); + m_cfg.m_psequence.push_back(ePSolver::SContacts); + + m_cfg.m_dsequence.push_back(ePSolver::Linear); + break; + } +} + +// +void btSoftBody::predictMotion(btScalar dt) +{ + int i,ni; + + /* Update */ + if(m_bUpdateRtCst) + { + m_bUpdateRtCst=false; + updateConstants(); + m_fdbvt.clear(); + if(m_cfg.collisions&fCollision::VF_SS) + { + initializeFaceTree(); + } + } + + /* Prepare */ + m_sst.sdt = dt*m_cfg.timescale; + m_sst.isdt = 1/m_sst.sdt; + m_sst.velmrg = m_sst.sdt*3; + m_sst.radmrg = getCollisionShape()->getMargin(); + m_sst.updmrg = m_sst.radmrg*(btScalar)0.25; + /* Forces */ + addVelocity(m_worldInfo->m_gravity*m_sst.sdt); + applyForces(); + /* Integrate */ + for(i=0,ni=m_nodes.size();im_v+ + f.m_n[1]->m_v+ + f.m_n[2]->m_v)/3; + vol = VolumeOf(f,m_sst.radmrg); + m_fdbvt.update( f.m_leaf, + vol, + v*m_sst.velmrg, + m_sst.updmrg); + } + } + /* Pose */ + updatePose(); + /* Match */ + if(m_pose.m_bframe&&(m_cfg.kMT>0)) + { + const btMatrix3x3 posetrs=m_pose.m_rot; + for(int i=0,ni=m_nodes.size();i0) + { + const btVector3 x=posetrs*m_pose.m_pos[i]+m_pose.m_com; + n.m_x=Lerp(n.m_x,x,m_cfg.kMT); + } + } + } + /* Clear contacts */ + m_rcontacts.resize(0); + m_scontacts.resize(0); + /* Optimize dbvt's */ + m_ndbvt.optimizeIncremental(1); + m_fdbvt.optimizeIncremental(1); + m_cdbvt.optimizeIncremental(1); +} + +// +void btSoftBody::solveConstraints() +{ + /* Apply clusters */ + applyClusters(false); + /* Prepare links */ + + int i,ni; + + for(i=0,ni=m_links.size();im_q-l.m_n[0]->m_q; + l.m_c2 = 1/(l.m_c3.length2()*l.m_c0); + } + /* Prepare anchors */ + for(i=0,ni=m_anchors.size();igetWorldTransform().getBasis()*a.m_local; + a.m_c0 = ImpulseMatrix( m_sst.sdt, + a.m_node->m_im, + a.m_body->getInvMass(), + a.m_body->getInvInertiaTensorWorld(), + ra); + a.m_c1 = ra; + a.m_c2 = m_sst.sdt*a.m_node->m_im; + a.m_body->activate(); + } + /* Solve velocities */ + if(m_cfg.viterations>0) + { + /* Solve */ + for(int isolve=0;isolve0) + { + for(int isolve=0;isolve0) + { + const btScalar vcf=m_cfg.kVCF*m_sst.isdt; + for(i=0,ni=m_nodes.size();i& bodies) +{ + const int nb=bodies.size(); + int iterations=0; + int i; + + for(i=0;im_cfg.citerations); + } + for(i=0;iprepareClusters(iterations); + } + for(i=0;isolveClusters(sor); + } + } + for(i=0;icleanupClusters(); + } +} + +// +void btSoftBody::integrateMotion() +{ + /* Update */ + updateNormals(); +} + +// +btSoftBody::RayFromToCaster::RayFromToCaster(const btVector3& rayFrom,const btVector3& rayTo,btScalar mxt) +{ + m_rayFrom = rayFrom; + m_rayNormalizedDirection = (rayTo-rayFrom); + m_rayTo = rayTo; + m_mint = mxt; + m_face = 0; + m_tests = 0; +} + +// +void btSoftBody::RayFromToCaster::Process(const btDbvtNode* leaf) +{ + btSoftBody::Face& f=*(btSoftBody::Face*)leaf->data; + const btScalar t=rayFromToTriangle( m_rayFrom,m_rayTo,m_rayNormalizedDirection, + f.m_n[0]->m_x, + f.m_n[1]->m_x, + f.m_n[2]->m_x, + m_mint); + if((t>0)&&(tteps)&&(tceps) && + (btDot(n,btCross(b-hit,c-hit))>ceps) && + (btDot(n,btCross(c-hit,a-hit))>ceps)) + { + return(t); + } + } + } + return(-1); +} + +// +void btSoftBody::pointersToIndices() +{ +#define PTR2IDX(_p_,_b_) reinterpret_cast((_p_)-(_b_)) + btSoftBody::Node* base=&m_nodes[0]; + int i,ni; + + for(i=0,ni=m_nodes.size();idata=*(void**)&i; + } + } + for(i=0,ni=m_links.size();idata=*(void**)&i; + } + } + for(i=0,ni=m_anchors.size();idata=&m_nodes[i]; + } + } + for(i=0,ni=m_links.size();idata=&m_faces[i]; + } + } + for(i=0,ni=m_anchors.size();im_x, + f.m_n[1]->m_x, + f.m_n[2]->m_x, + mint); + if(t>0) + { + ++cnt; + if(!bcountonly) + { + feature=btSoftBody::eFeature::Face; + index=i; + mint=t; + } + } + } + } + else + {/* Use dbvt */ + RayFromToCaster collider(rayFrom,rayTo,mint); + + btDbvt::rayTest(m_fdbvt.m_root,rayFrom,rayTo,collider); + if(collider.m_face) + { + mint=collider.m_mint; + feature=btSoftBody::eFeature::Face; + index=(int)(collider.m_face-&m_faces[0]); + cnt=1; + } + } + return(cnt); +} + +// +void btSoftBody::initializeFaceTree() +{ + m_fdbvt.clear(); + for(int i=0;igetCollisionShape(); + btRigidBody* tmpRigid = btRigidBody::upcast(colObj); + const btTransform& wtr=tmpRigid? tmpRigid->getInterpolationWorldTransform() : colObj->getWorldTransform(); + btScalar dst=m_worldInfo->m_sparsesdf.Evaluate( wtr.invXform(x), + shp, + nrm, + margin); + if(dst<0) + { + cti.m_colObj = colObj; + cti.m_normal = wtr.getBasis()*nrm; + cti.m_offset = -btDot( cti.m_normal, + x-cti.m_normal*dst); + return(true); + } + return(false); +} + +// +void btSoftBody::updateNormals() +{ + const btVector3 zv(0,0,0); + int i,ni; + + for(i=0,ni=m_nodes.size();im_x-f.m_n[0]->m_x, + f.m_n[2]->m_x-f.m_n[0]->m_x); + f.m_normal=n.normalized(); + f.m_n[0]->m_n+=n; + f.m_n[1]->m_n+=n; + f.m_n[2]->m_n+=n; + } + for(i=0,ni=m_nodes.size();iSIMD_EPSILON) + m_nodes[i].m_n /= len; + } +} + +// +void btSoftBody::updateBounds() +{ + if(m_ndbvt.m_root) + { + const btVector3& mins=m_ndbvt.m_root->volume.Mins(); + const btVector3& maxs=m_ndbvt.m_root->volume.Maxs(); + const btScalar csm=getCollisionShape()->getMargin(); + const btVector3 mrg=btVector3( csm, + csm, + csm)*1; // ??? to investigate... + m_bounds[0]=mins-mrg; + m_bounds[1]=maxs+mrg; + if(0!=getBroadphaseHandle()) + { + m_worldInfo->m_broadphase->setAabb( getBroadphaseHandle(), + m_bounds[0], + m_bounds[1], + m_worldInfo->m_dispatcher); + } + } + else + { + m_bounds[0]= + m_bounds[1]=btVector3(0,0,0); + } +} + + +// +void btSoftBody::updatePose() +{ + if(m_pose.m_bframe) + { + btSoftBody::Pose& pose=m_pose; + const btVector3 com=evaluateCom(); + /* Com */ + pose.m_com = com; + /* Rotation */ + btMatrix3x3 Apq; + const btScalar eps=SIMD_EPSILON; + Apq[0]=Apq[1]=Apq[2]=btVector3(0,0,0); + Apq[0].setX(eps);Apq[1].setY(eps*2);Apq[2].setZ(eps*3); + for(int i=0,ni=m_nodes.size();i1) + { + const btScalar idet=Clamp( 1/pose.m_scl.determinant(), + 1,m_cfg.maxvolume); + pose.m_scl=Mul(pose.m_scl,idet); + } + + } +} + +// +void btSoftBody::updateConstants() +{ + int i,ni; + + /* Links */ + for(i=0,ni=m_links.size();im_x-l.m_n[1]->m_x).length(); + l.m_c0 = (l.m_n[0]->m_im+l.m_n[1]->m_im)/m.m_kLST; + l.m_c1 = l.m_rl*l.m_rl; + } + /* Faces */ + for(i=0,ni=m_faces.size();im_x,f.m_n[1]->m_x,f.m_n[2]->m_x); + } + /* Area's */ + btAlignedObjectArray counts; + counts.resize(m_nodes.size(),0); + for(i=0,ni=m_nodes.size();im_area+=btFabs(f.m_ra); + } + } + for(i=0,ni=m_nodes.size();i0) + m_nodes[i].m_area/=(btScalar)counts[i]; + else + m_nodes[i].m_area=0; + } +} + +// +void btSoftBody::initializeClusters() +{ + int i; + + for( i=0;im_im==0) + { + c.m_containsAnchor = true; + c.m_masses[j] = BT_LARGE_FLOAT; + } else + { + c.m_masses[j] = btScalar(1.)/c.m_nodes[j]->m_im; + } + c.m_imass += c.m_masses[j]; + } + c.m_imass = btScalar(1.)/c.m_imass; + c.m_com = btSoftBody::clusterCom(&c); + c.m_lv = btVector3(0,0,0); + c.m_av = btVector3(0,0,0); + c.m_leaf = 0; + /* Inertia */ + btMatrix3x3& ii=c.m_locii; + ii[0]=ii[1]=ii[2]=btVector3(0,0,0); + { + int i,ni; + + for(i=0,ni=c.m_nodes.size();im_x-c.m_com; + const btVector3 q=k*k; + const btScalar m=c.m_masses[i]; + ii[0][0] += m*(q[1]+q[2]); + ii[1][1] += m*(q[0]+q[2]); + ii[2][2] += m*(q[0]+q[1]); + ii[0][1] -= m*k[0]*k[1]; + ii[0][2] -= m*k[0]*k[2]; + ii[1][2] -= m*k[1]*k[2]; + } + } + ii[1][0]=ii[0][1]; + ii[2][0]=ii[0][2]; + ii[2][1]=ii[1][2]; + + ii = ii.inverse(); + + /* Frame */ + c.m_framexform.setIdentity(); + c.m_framexform.setOrigin(c.m_com); + c.m_framerefs.resize(c.m_nodes.size()); + { + int i; + for(i=0;im_x-c.m_com; + } + } + } +} + +// +void btSoftBody::updateClusters() +{ + BT_PROFILE("UpdateClusters"); + int i; + + for(i=0;im_x-c.m_com; + const btVector3& b=c.m_framerefs[i]; + m[0]+=a[0]*b;m[1]+=a[1]*b;m[2]+=a[2]*b; + } + PolarDecompose(m,r,s); + c.m_framexform.setOrigin(c.m_com); + c.m_framexform.setBasis(r); + /* Inertia */ +#if 1/* Constant */ + c.m_invwi=c.m_framexform.getBasis()*c.m_locii*c.m_framexform.getBasis().transpose(); +#else +#if 0/* Sphere */ + const btScalar rk=(2*c.m_extents.length2())/(5*c.m_imass); + const btVector3 inertia(rk,rk,rk); + const btVector3 iin(btFabs(inertia[0])>SIMD_EPSILON?1/inertia[0]:0, + btFabs(inertia[1])>SIMD_EPSILON?1/inertia[1]:0, + btFabs(inertia[2])>SIMD_EPSILON?1/inertia[2]:0); + + c.m_invwi=c.m_xform.getBasis().scaled(iin)*c.m_xform.getBasis().transpose(); +#else/* Actual */ + c.m_invwi[0]=c.m_invwi[1]=c.m_invwi[2]=btVector3(0,0,0); + for(int i=0;im_x-c.m_com; + const btVector3 q=k*k; + const btScalar m=1/c.m_nodes[i]->m_im; + c.m_invwi[0][0] += m*(q[1]+q[2]); + c.m_invwi[1][1] += m*(q[0]+q[2]); + c.m_invwi[2][2] += m*(q[0]+q[1]); + c.m_invwi[0][1] -= m*k[0]*k[1]; + c.m_invwi[0][2] -= m*k[0]*k[2]; + c.m_invwi[1][2] -= m*k[1]*k[2]; + } + c.m_invwi[1][0]=c.m_invwi[0][1]; + c.m_invwi[2][0]=c.m_invwi[0][2]; + c.m_invwi[2][1]=c.m_invwi[1][2]; + c.m_invwi=c.m_invwi.inverse(); +#endif +#endif + /* Velocities */ + c.m_lv=btVector3(0,0,0); + c.m_av=btVector3(0,0,0); + { + int i; + + for(i=0;im_v*c.m_masses[i]; + c.m_lv += v; + c.m_av += btCross(c.m_nodes[i]->m_x-c.m_com,v); + } + } + c.m_lv=c.m_imass*c.m_lv*(1-c.m_ldamping); + c.m_av=c.m_invwi*c.m_av*(1-c.m_adamping); + c.m_vimpulses[0] = + c.m_vimpulses[1] = btVector3(0,0,0); + c.m_dimpulses[0] = + c.m_dimpulses[1] = btVector3(0,0,0); + c.m_nvimpulses = 0; + c.m_ndimpulses = 0; + /* Matching */ + if(c.m_matching>0) + { + for(int j=0;jm_x; + btVector3 mx=mi; + for(int j=1;jm_x); + mx.setMax(c.m_nodes[j]->m_x); + } + ATTRIBUTE_ALIGNED16(btDbvtVolume) bounds=btDbvtVolume::FromMM(mi,mx); + if(c.m_leaf) + m_cdbvt.update(c.m_leaf,bounds,c.m_lv*m_sst.sdt*3,m_sst.radmrg); + else + c.m_leaf=m_cdbvt.insert(bounds,&c); + } + } + } + + +} + + + + +// +void btSoftBody::cleanupClusters() +{ + for(int i=0;iTerminate(m_sst.sdt); + if(m_joints[i]->m_delete) + { + btAlignedFree(m_joints[i]); + m_joints.remove(m_joints[i--]); + } + } +} + +// +void btSoftBody::prepareClusters(int iterations) +{ + for(int i=0;iPrepare(m_sst.sdt,iterations); + } +} + + +// +void btSoftBody::solveClusters(btScalar sor) +{ + for(int i=0,ni=m_joints.size();iSolve(m_sst.sdt,sor); + } +} + +// +void btSoftBody::applyClusters(bool drift) +{ + BT_PROFILE("ApplyClusters"); + const btScalar f0=m_sst.sdt; + //const btScalar f1=f0/2; + btAlignedObjectArray deltas; + btAlignedObjectArray weights; + deltas.resize(m_nodes.size(),btVector3(0,0,0)); + weights.resize(m_nodes.size(),0); + int i; + + if(drift) + { + for(i=0;im_x; + const btScalar q=c.m_masses[j]; + deltas[idx] += (v+btCross(w,x-c.m_com))*q; + weights[idx] += q; + } + } + } + for(i=0;i0) m_nodes[i].m_x+=deltas[i]/weights[i]; + } +} + +// +void btSoftBody::dampClusters() +{ + int i; + + for(i=0;i0) + { + for(int j=0;j0) + { + const btVector3 vx=c.m_lv+btCross(c.m_av,c.m_nodes[j]->m_q-c.m_com); + if(vx.length2()<=n.m_v.length2()) + { + n.m_v += c.m_ndamping*(vx-n.m_v); + } + } + } + } + } +} + +// +void btSoftBody::Joint::Prepare(btScalar dt,int) +{ + m_bodies[0].activate(); + m_bodies[1].activate(); +} + +// +void btSoftBody::LJoint::Prepare(btScalar dt,int iterations) +{ + static const btScalar maxdrift=4; + Joint::Prepare(dt,iterations); + m_rpos[0] = m_bodies[0].xform()*m_refs[0]; + m_rpos[1] = m_bodies[1].xform()*m_refs[1]; + m_drift = Clamp(m_rpos[0]-m_rpos[1],maxdrift)*m_erp/dt; + m_rpos[0] -= m_bodies[0].xform().getOrigin(); + m_rpos[1] -= m_bodies[1].xform().getOrigin(); + m_massmatrix = ImpulseMatrix( m_bodies[0].invMass(),m_bodies[0].invWorldInertia(),m_rpos[0], + m_bodies[1].invMass(),m_bodies[1].invWorldInertia(),m_rpos[1]); + if(m_split>0) + { + m_sdrift = m_massmatrix*(m_drift*m_split); + m_drift *= 1-m_split; + } + m_drift /=(btScalar)iterations; +} + +// +void btSoftBody::LJoint::Solve(btScalar dt,btScalar sor) +{ + const btVector3 va=m_bodies[0].velocity(m_rpos[0]); + const btVector3 vb=m_bodies[1].velocity(m_rpos[1]); + const btVector3 vr=va-vb; + btSoftBody::Impulse impulse; + impulse.m_asVelocity = 1; + impulse.m_velocity = m_massmatrix*(m_drift+vr*m_cfm)*sor; + m_bodies[0].applyImpulse(-impulse,m_rpos[0]); + m_bodies[1].applyImpulse( impulse,m_rpos[1]); +} + +// +void btSoftBody::LJoint::Terminate(btScalar dt) +{ + if(m_split>0) + { + m_bodies[0].applyDImpulse(-m_sdrift,m_rpos[0]); + m_bodies[1].applyDImpulse( m_sdrift,m_rpos[1]); + } +} + +// +void btSoftBody::AJoint::Prepare(btScalar dt,int iterations) +{ + static const btScalar maxdrift=SIMD_PI/16; + m_icontrol->Prepare(this); + Joint::Prepare(dt,iterations); + m_axis[0] = m_bodies[0].xform().getBasis()*m_refs[0]; + m_axis[1] = m_bodies[1].xform().getBasis()*m_refs[1]; + m_drift = NormalizeAny(btCross(m_axis[1],m_axis[0])); + m_drift *= btMin(maxdrift,btAcos(Clamp(btDot(m_axis[0],m_axis[1]),-1,+1))); + m_drift *= m_erp/dt; + m_massmatrix= AngularImpulseMatrix(m_bodies[0].invWorldInertia(),m_bodies[1].invWorldInertia()); + if(m_split>0) + { + m_sdrift = m_massmatrix*(m_drift*m_split); + m_drift *= 1-m_split; + } + m_drift /=(btScalar)iterations; +} + +// +void btSoftBody::AJoint::Solve(btScalar dt,btScalar sor) +{ + const btVector3 va=m_bodies[0].angularVelocity(); + const btVector3 vb=m_bodies[1].angularVelocity(); + const btVector3 vr=va-vb; + const btScalar sp=btDot(vr,m_axis[0]); + const btVector3 vc=vr-m_axis[0]*m_icontrol->Speed(this,sp); + btSoftBody::Impulse impulse; + impulse.m_asVelocity = 1; + impulse.m_velocity = m_massmatrix*(m_drift+vc*m_cfm)*sor; + m_bodies[0].applyAImpulse(-impulse); + m_bodies[1].applyAImpulse( impulse); +} + +// +void btSoftBody::AJoint::Terminate(btScalar dt) +{ + if(m_split>0) + { + m_bodies[0].applyDAImpulse(-m_sdrift); + m_bodies[1].applyDAImpulse( m_sdrift); + } +} + +// +void btSoftBody::CJoint::Prepare(btScalar dt,int iterations) +{ + Joint::Prepare(dt,iterations); + const bool dodrift=(m_life==0); + m_delete=(++m_life)>m_maxlife; + if(dodrift) + { + m_drift=m_drift*m_erp/dt; + if(m_split>0) + { + m_sdrift = m_massmatrix*(m_drift*m_split); + m_drift *= 1-m_split; + } + m_drift/=(btScalar)iterations; + } + else + { + m_drift=m_sdrift=btVector3(0,0,0); + } +} + +// +void btSoftBody::CJoint::Solve(btScalar dt,btScalar sor) +{ + const btVector3 va=m_bodies[0].velocity(m_rpos[0]); + const btVector3 vb=m_bodies[1].velocity(m_rpos[1]); + const btVector3 vrel=va-vb; + const btScalar rvac=btDot(vrel,m_normal); + btSoftBody::Impulse impulse; + impulse.m_asVelocity = 1; + impulse.m_velocity = m_drift; + if(rvac<0) + { + const btVector3 iv=m_normal*rvac; + const btVector3 fv=vrel-iv; + impulse.m_velocity += iv+fv*m_friction; + } + impulse.m_velocity=m_massmatrix*impulse.m_velocity*sor; + + if (m_bodies[0].m_soft==m_bodies[1].m_soft) + { + if ((impulse.m_velocity.getX() ==impulse.m_velocity.getX())&&(impulse.m_velocity.getY() ==impulse.m_velocity.getY())&& + (impulse.m_velocity.getZ() ==impulse.m_velocity.getZ())) + { + if (impulse.m_asVelocity) + { + if (impulse.m_velocity.length() m_maxSelfCollisionImpulse) + { + + } else + { + m_bodies[0].applyImpulse(-impulse*m_bodies[0].m_soft->m_selfCollisionImpulseFactor,m_rpos[0]); + m_bodies[1].applyImpulse( impulse*m_bodies[0].m_soft->m_selfCollisionImpulseFactor,m_rpos[1]); + } + } + } + } else + { + m_bodies[0].applyImpulse(-impulse,m_rpos[0]); + m_bodies[1].applyImpulse( impulse,m_rpos[1]); + } +} + +// +void btSoftBody::CJoint::Terminate(btScalar dt) +{ + if(m_split>0) + { + m_bodies[0].applyDImpulse(-m_sdrift,m_rpos[0]); + m_bodies[1].applyDImpulse( m_sdrift,m_rpos[1]); + } +} + +// +void btSoftBody::applyForces() +{ + + BT_PROFILE("SoftBody applyForces"); + const btScalar dt=m_sst.sdt; + const btScalar kLF=m_cfg.kLF; + const btScalar kDG=m_cfg.kDG; + const btScalar kPR=m_cfg.kPR; + const btScalar kVC=m_cfg.kVC; + const bool as_lift=kLF>0; + const bool as_drag=kDG>0; + const bool as_pressure=kPR!=0; + const bool as_volume=kVC>0; + const bool as_aero= as_lift || + as_drag ; + const bool as_vaero= as_aero && + (m_cfg.aeromodel=btSoftBody::eAeroModel::F_TwoSided); + const bool use_medium= as_aero; + const bool use_volume= as_pressure || + as_volume ; + btScalar volume=0; + btScalar ivolumetp=0; + btScalar dvolumetv=0; + btSoftBody::sMedium medium; + if(use_volume) + { + volume = getVolume(); + ivolumetp = 1/btFabs(volume)*kPR; + dvolumetv = (m_pose.m_volume-volume)*kVC; + } + /* Per vertex forces */ + int i,ni; + + for(i=0,ni=m_nodes.size();i0) + { + if(use_medium) + { + EvaluateMedium(m_worldInfo,n.m_x,medium); + /* Aerodynamics */ + if(as_vaero) + { + const btVector3 rel_v=n.m_v-medium.m_velocity; + const btScalar rel_v2=rel_v.length2(); + if(rel_v2>SIMD_EPSILON) + { + btVector3 nrm=n.m_n; + /* Setup normal */ + switch(m_cfg.aeromodel) + { + case btSoftBody::eAeroModel::V_Point: + nrm=NormalizeAny(rel_v);break; + case btSoftBody::eAeroModel::V_TwoSided: + nrm*=(btScalar)(btDot(nrm,rel_v)<0?-1:+1);break; + } + const btScalar dvn=btDot(rel_v,nrm); + /* Compute forces */ + if(dvn>0) + { + btVector3 force(0,0,0); + const btScalar c0 = n.m_area*dvn*rel_v2/2; + const btScalar c1 = c0*medium.m_density; + force += nrm*(-c1*kLF); + force += rel_v.normalized()*(-c1*kDG); + ApplyClampedForce(n,force,dt); + } + } + } + } + /* Pressure */ + if(as_pressure) + { + n.m_f += n.m_n*(n.m_area*ivolumetp); + } + /* Volume */ + if(as_volume) + { + n.m_f += n.m_n*(n.m_area*dvolumetv); + } + } + } + /* Per face forces */ + for(i=0,ni=m_faces.size();im_v+f.m_n[1]->m_v+f.m_n[2]->m_v)/3; + const btVector3 x=(f.m_n[0]->m_x+f.m_n[1]->m_x+f.m_n[2]->m_x)/3; + EvaluateMedium(m_worldInfo,x,medium); + const btVector3 rel_v=v-medium.m_velocity; + const btScalar rel_v2=rel_v.length2(); + if(rel_v2>SIMD_EPSILON) + { + btVector3 nrm=f.m_normal; + /* Setup normal */ + switch(m_cfg.aeromodel) + { + case btSoftBody::eAeroModel::F_TwoSided: + nrm*=(btScalar)(btDot(nrm,rel_v)<0?-1:+1);break; + } + const btScalar dvn=btDot(rel_v,nrm); + /* Compute forces */ + if(dvn>0) + { + btVector3 force(0,0,0); + const btScalar c0 = f.m_ra*dvn*rel_v2; + const btScalar c1 = c0*medium.m_density; + force += nrm*(-c1*kLF); + force += rel_v.normalized()*(-c1*kDG); + force /= 3; + for(int j=0;j<3;++j) ApplyClampedForce(*f.m_n[j],force,dt); + } + } + } + } +} + +// +void btSoftBody::PSolve_Anchors(btSoftBody* psb,btScalar kst,btScalar ti) +{ + const btScalar kAHR=psb->m_cfg.kAHR*kst; + const btScalar dt=psb->m_sst.sdt; + for(int i=0,ni=psb->m_anchors.size();im_anchors[i]; + const btTransform& t=a.m_body->getInterpolationWorldTransform(); + Node& n=*a.m_node; + const btVector3 wa=t*a.m_local; + const btVector3 va=a.m_body->getVelocityInLocalPoint(a.m_c1)*dt; + const btVector3 vb=n.m_x-n.m_q; + const btVector3 vr=(va-vb)+(wa-n.m_x)*kAHR; + const btVector3 impulse=a.m_c0*vr; + n.m_x+=impulse*a.m_c2; + a.m_body->applyImpulse(-impulse,a.m_c1); + } +} + +// +void btSoftBody::PSolve_RContacts(btSoftBody* psb,btScalar kst,btScalar ti) +{ + const btScalar dt=psb->m_sst.sdt; + const btScalar mrg=psb->getCollisionShape()->getMargin(); + for(int i=0,ni=psb->m_rcontacts.size();im_rcontacts[i]; + const sCti& cti=c.m_cti; + btRigidBody* tmpRigid = btRigidBody::upcast(cti.m_colObj); + + const btVector3 va=tmpRigid ? tmpRigid->getVelocityInLocalPoint(c.m_c1)*dt : btVector3(0,0,0); + const btVector3 vb=c.m_node->m_x-c.m_node->m_q; + const btVector3 vr=vb-va; + const btScalar dn=btDot(vr,cti.m_normal); + if(dn<=SIMD_EPSILON) + { + const btScalar dp=btMin(btDot(c.m_node->m_x,cti.m_normal)+cti.m_offset,mrg); + const btVector3 fv=vr-cti.m_normal*dn; + const btVector3 impulse=c.m_c0*((vr-fv*c.m_c3+cti.m_normal*(dp*c.m_c4))*kst); + c.m_node->m_x-=impulse*c.m_c2; + if (tmpRigid) + tmpRigid->applyImpulse(impulse,c.m_c1); + } + } +} + +// +void btSoftBody::PSolve_SContacts(btSoftBody* psb,btScalar,btScalar ti) +{ + for(int i=0,ni=psb->m_scontacts.size();im_scontacts[i]; + const btVector3& nr=c.m_normal; + Node& n=*c.m_node; + Face& f=*c.m_face; + const btVector3 p=BaryEval( f.m_n[0]->m_x, + f.m_n[1]->m_x, + f.m_n[2]->m_x, + c.m_weights); + const btVector3 q=BaryEval( f.m_n[0]->m_q, + f.m_n[1]->m_q, + f.m_n[2]->m_q, + c.m_weights); + const btVector3 vr=(n.m_x-n.m_q)-(p-q); + btVector3 corr(0,0,0); + btScalar dot = btDot(vr,nr); + if(dot<0) + { + const btScalar j=c.m_margin-(btDot(nr,n.m_x)-btDot(nr,p)); + corr+=c.m_normal*j; + } + corr -= ProjectOnPlane(vr,nr)*c.m_friction; + n.m_x += corr*c.m_cfm[0]; + f.m_n[0]->m_x -= corr*(c.m_cfm[1]*c.m_weights.x()); + f.m_n[1]->m_x -= corr*(c.m_cfm[1]*c.m_weights.y()); + f.m_n[2]->m_x -= corr*(c.m_cfm[1]*c.m_weights.z()); + } +} + +// +void btSoftBody::PSolve_Links(btSoftBody* psb,btScalar kst,btScalar ti) +{ + for(int i=0,ni=psb->m_links.size();im_links[i]; + if(l.m_c0>0) + { + Node& a=*l.m_n[0]; + Node& b=*l.m_n[1]; + const btVector3 del=b.m_x-a.m_x; + const btScalar len=del.length2(); + const btScalar k=((l.m_c1-len)/(l.m_c0*(l.m_c1+len)))*kst; + //const btScalar t=k*a.m_im; + a.m_x-=del*(k*a.m_im); + b.m_x+=del*(k*b.m_im); + } + } +} + +// +void btSoftBody::VSolve_Links(btSoftBody* psb,btScalar kst) +{ + for(int i=0,ni=psb->m_links.size();im_links[i]; + Node** n=l.m_n; + const btScalar j=-btDot(l.m_c3,n[0]->m_v-n[1]->m_v)*l.m_c2*kst; + n[0]->m_v+= l.m_c3*(j*n[0]->m_im); + n[1]->m_v-= l.m_c3*(j*n[1]->m_im); + } +} + +// +btSoftBody::psolver_t btSoftBody::getSolver(ePSolver::_ solver) +{ + switch(solver) + { + case ePSolver::Anchors: + return(&btSoftBody::PSolve_Anchors); + case ePSolver::Linear: + return(&btSoftBody::PSolve_Links); + case ePSolver::RContacts: + return(&btSoftBody::PSolve_RContacts); + case ePSolver::SContacts: + return(&btSoftBody::PSolve_SContacts); + } + return(0); +} + +// +btSoftBody::vsolver_t btSoftBody::getSolver(eVSolver::_ solver) +{ + switch(solver) + { + case eVSolver::Linear: return(&btSoftBody::VSolve_Links); + } + return(0); +} + +// +void btSoftBody::defaultCollisionHandler(btCollisionObject* pco) +{ + switch(m_cfg.collisions&fCollision::RVSmask) + { + case fCollision::SDF_RS: + { + btSoftColliders::CollideSDF_RS docollide; + btRigidBody* prb1=btRigidBody::upcast(pco); + btTransform wtr=prb1 ? prb1->getInterpolationWorldTransform() : pco->getWorldTransform(); + + const btTransform ctr=pco->getWorldTransform(); + const btScalar timemargin=(wtr.getOrigin()-ctr.getOrigin()).length(); + const btScalar basemargin=getCollisionShape()->getMargin(); + btVector3 mins; + btVector3 maxs; + ATTRIBUTE_ALIGNED16(btDbvtVolume) volume; + pco->getCollisionShape()->getAabb( pco->getInterpolationWorldTransform(), + mins, + maxs); + volume=btDbvtVolume::FromMM(mins,maxs); + volume.Expand(btVector3(basemargin,basemargin,basemargin)); + docollide.psb = this; + docollide.m_colObj1 = pco; + docollide.m_rigidBody = prb1; + + docollide.dynmargin = basemargin+timemargin; + docollide.stamargin = basemargin; + m_ndbvt.collideTV(m_ndbvt.m_root,volume,docollide); + } + break; + case fCollision::CL_RS: + { + btSoftColliders::CollideCL_RS collider; + collider.Process(this,pco); + } + break; + } +} + +// +void btSoftBody::defaultCollisionHandler(btSoftBody* psb) +{ + const int cf=m_cfg.collisions&psb->m_cfg.collisions; + switch(cf&fCollision::SVSmask) + { + case fCollision::CL_SS: + { + + //support self-collision if CL_SELF flag set + if (this!=psb || psb->m_cfg.collisions&fCollision::CL_SELF) + { + btSoftColliders::CollideCL_SS docollide; + docollide.Process(this,psb); + } + + } + break; + case fCollision::VF_SS: + { + //only self-collision for Cluster, not Vertex-Face yet + if (this!=psb) + { + btSoftColliders::CollideVF_SS docollide; + /* common */ + docollide.mrg= getCollisionShape()->getMargin()+ + psb->getCollisionShape()->getMargin(); + /* psb0 nodes vs psb1 faces */ + docollide.psb[0]=this; + docollide.psb[1]=psb; + docollide.psb[0]->m_ndbvt.collideTT( docollide.psb[0]->m_ndbvt.m_root, + docollide.psb[1]->m_fdbvt.m_root, + docollide); + /* psb1 nodes vs psb0 faces */ + docollide.psb[0]=psb; + docollide.psb[1]=this; + docollide.psb[0]->m_ndbvt.collideTT( docollide.psb[0]->m_ndbvt.m_root, + docollide.psb[1]->m_fdbvt.m_root, + docollide); + } + } + break; + default: + { + + } + } +} diff --git a/Engine/lib/bullet/src/BulletSoftBody/btSoftBody.h b/Engine/lib/bullet/src/BulletSoftBody/btSoftBody.h new file mode 100644 index 000000000..d69e835f1 --- /dev/null +++ b/Engine/lib/bullet/src/BulletSoftBody/btSoftBody.h @@ -0,0 +1,891 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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. +*/ +///btSoftBody implementation by Nathanael Presson + +#ifndef _BT_SOFT_BODY_H +#define _BT_SOFT_BODY_H + +#include "LinearMath/btAlignedObjectArray.h" +#include "LinearMath/btTransform.h" +#include "LinearMath/btIDebugDraw.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" + +#include "BulletCollision/CollisionShapes/btConcaveShape.h" +#include "BulletCollision/CollisionDispatch/btCollisionCreateFunc.h" +#include "btSparseSDF.h" +#include "BulletCollision/BroadphaseCollision/btDbvt.h" + +class btBroadphaseInterface; +class btDispatcher; + +/* btSoftBodyWorldInfo */ +struct btSoftBodyWorldInfo +{ + btScalar air_density; + btScalar water_density; + btScalar water_offset; + btVector3 water_normal; + btBroadphaseInterface* m_broadphase; + btDispatcher* m_dispatcher; + btVector3 m_gravity; + btSparseSdf<3> m_sparsesdf; +}; + + +///The btSoftBody is an class to simulate cloth and volumetric soft bodies. +///There is two-way interaction between btSoftBody and btRigidBody/btCollisionObject. +class btSoftBody : public btCollisionObject +{ +public: + btAlignedObjectArray m_collisionDisabledObjects; + + // + // Enumerations + // + + ///eAeroModel + struct eAeroModel { enum _ { + V_Point, ///Vertex normals are oriented toward velocity + V_TwoSided, ///Vertex normals are fliped to match velocity + V_OneSided, ///Vertex normals are taken as it is + F_TwoSided, ///Face normals are fliped to match velocity + F_OneSided, ///Face normals are taken as it is + END + };}; + + ///eVSolver : velocities solvers + struct eVSolver { enum _ { + Linear, ///Linear solver + END + };}; + + ///ePSolver : positions solvers + struct ePSolver { enum _ { + Linear, ///Linear solver + Anchors, ///Anchor solver + RContacts, ///Rigid contacts solver + SContacts, ///Soft contacts solver + END + };}; + + ///eSolverPresets + struct eSolverPresets { enum _ { + Positions, + Velocities, + Default = Positions, + END + };}; + + ///eFeature + struct eFeature { enum _ { + None, + Node, + Link, + Face, + END + };}; + + typedef btAlignedObjectArray tVSolverArray; + typedef btAlignedObjectArray tPSolverArray; + + // + // Flags + // + + ///fCollision + struct fCollision { enum _ { + RVSmask = 0x000f, ///Rigid versus soft mask + SDF_RS = 0x0001, ///SDF based rigid vs soft + CL_RS = 0x0002, ///Cluster vs convex rigid vs soft + + SVSmask = 0x0030, ///Rigid versus soft mask + VF_SS = 0x0010, ///Vertex vs face soft vs soft handling + CL_SS = 0x0020, ///Cluster vs cluster soft vs soft handling + CL_SELF = 0x0040, ///Cluster soft body self collision + /* presets */ + Default = SDF_RS, + END + };}; + + ///fMaterial + struct fMaterial { enum _ { + DebugDraw = 0x0001, /// Enable debug draw + /* presets */ + Default = DebugDraw, + END + };}; + + // + // API Types + // + + /* sRayCast */ + struct sRayCast + { + btSoftBody* body; /// soft body + eFeature::_ feature; /// feature type + int index; /// feature index + btScalar fraction; /// time of impact fraction (rayorg+(rayto-rayfrom)*fraction) + }; + + /* ImplicitFn */ + struct ImplicitFn + { + virtual btScalar Eval(const btVector3& x)=0; + }; + + // + // Internal types + // + + typedef btAlignedObjectArray tScalarArray; + typedef btAlignedObjectArray tVector3Array; + + /* sCti is Softbody contact info */ + struct sCti + { + btCollisionObject* m_colObj; /* Rigid body */ + btVector3 m_normal; /* Outward normal */ + btScalar m_offset; /* Offset from origin */ + }; + + /* sMedium */ + struct sMedium + { + btVector3 m_velocity; /* Velocity */ + btScalar m_pressure; /* Pressure */ + btScalar m_density; /* Density */ + }; + + /* Base type */ + struct Element + { + void* m_tag; // User data + Element() : m_tag(0) {} + }; + /* Material */ + struct Material : Element + { + btScalar m_kLST; // Linear stiffness coefficient [0,1] + btScalar m_kAST; // Area/Angular stiffness coefficient [0,1] + btScalar m_kVST; // Volume stiffness coefficient [0,1] + int m_flags; // Flags + }; + + /* Feature */ + struct Feature : Element + { + Material* m_material; // Material + }; + /* Node */ + struct Node : Feature + { + btVector3 m_x; // Position + btVector3 m_q; // Previous step position + btVector3 m_v; // Velocity + btVector3 m_f; // Force accumulator + btVector3 m_n; // Normal + btScalar m_im; // 1/mass + btScalar m_area; // Area + btDbvtNode* m_leaf; // Leaf data + int m_battach:1; // Attached + }; + /* Link */ + struct Link : Feature + { + Node* m_n[2]; // Node pointers + btScalar m_rl; // Rest length + int m_bbending:1; // Bending link + btScalar m_c0; // (ima+imb)*kLST + btScalar m_c1; // rl^2 + btScalar m_c2; // |gradient|^2/c0 + btVector3 m_c3; // gradient + }; + /* Face */ + struct Face : Feature + { + Node* m_n[3]; // Node pointers + btVector3 m_normal; // Normal + btScalar m_ra; // Rest area + btDbvtNode* m_leaf; // Leaf data + }; + /* Tetra */ + struct Tetra : Feature + { + Node* m_n[4]; // Node pointers + btScalar m_rv; // Rest volume + btDbvtNode* m_leaf; // Leaf data + btVector3 m_c0[4]; // gradients + btScalar m_c1; // (4*kVST)/(im0+im1+im2+im3) + btScalar m_c2; // m_c1/sum(|g0..3|^2) + }; + /* RContact */ + struct RContact + { + sCti m_cti; // Contact infos + Node* m_node; // Owner node + btMatrix3x3 m_c0; // Impulse matrix + btVector3 m_c1; // Relative anchor + btScalar m_c2; // ima*dt + btScalar m_c3; // Friction + btScalar m_c4; // Hardness + }; + /* SContact */ + struct SContact + { + Node* m_node; // Node + Face* m_face; // Face + btVector3 m_weights; // Weigths + btVector3 m_normal; // Normal + btScalar m_margin; // Margin + btScalar m_friction; // Friction + btScalar m_cfm[2]; // Constraint force mixing + }; + /* Anchor */ + struct Anchor + { + Node* m_node; // Node pointer + btVector3 m_local; // Anchor position in body space + btRigidBody* m_body; // Body + btMatrix3x3 m_c0; // Impulse matrix + btVector3 m_c1; // Relative anchor + btScalar m_c2; // ima*dt + }; + /* Note */ + struct Note : Element + { + const char* m_text; // Text + btVector3 m_offset; // Offset + int m_rank; // Rank + Node* m_nodes[4]; // Nodes + btScalar m_coords[4]; // Coordinates + }; + /* Pose */ + struct Pose + { + bool m_bvolume; // Is valid + bool m_bframe; // Is frame + btScalar m_volume; // Rest volume + tVector3Array m_pos; // Reference positions + tScalarArray m_wgh; // Weights + btVector3 m_com; // COM + btMatrix3x3 m_rot; // Rotation + btMatrix3x3 m_scl; // Scale + btMatrix3x3 m_aqq; // Base scaling + }; + /* Cluster */ + struct Cluster + { + btAlignedObjectArray m_nodes; + tScalarArray m_masses; + tVector3Array m_framerefs; + btTransform m_framexform; + btScalar m_idmass; + btScalar m_imass; + btMatrix3x3 m_locii; + btMatrix3x3 m_invwi; + btVector3 m_com; + btVector3 m_vimpulses[2]; + btVector3 m_dimpulses[2]; + int m_nvimpulses; + int m_ndimpulses; + btVector3 m_lv; + btVector3 m_av; + btDbvtNode* m_leaf; + btScalar m_ndamping; /* Node damping */ + btScalar m_ldamping; /* Linear damping */ + btScalar m_adamping; /* Angular damping */ + btScalar m_matching; + btScalar m_maxSelfCollisionImpulse; + btScalar m_selfCollisionImpulseFactor; + bool m_containsAnchor; + bool m_collide; + int m_clusterIndex; + Cluster() : m_leaf(0),m_ndamping(0),m_ldamping(0),m_adamping(0),m_matching(0) + ,m_maxSelfCollisionImpulse(100.f), + m_selfCollisionImpulseFactor(0.01f), + m_containsAnchor(false) + {} + }; + /* Impulse */ + struct Impulse + { + btVector3 m_velocity; + btVector3 m_drift; + int m_asVelocity:1; + int m_asDrift:1; + Impulse() : m_velocity(0,0,0),m_drift(0,0,0),m_asVelocity(0),m_asDrift(0) {} + Impulse operator -() const + { + Impulse i=*this; + i.m_velocity=-i.m_velocity; + i.m_drift=-i.m_drift; + return(i); + } + Impulse operator*(btScalar x) const + { + Impulse i=*this; + i.m_velocity*=x; + i.m_drift*=x; + return(i); + } + }; + /* Body */ + struct Body + { + Cluster* m_soft; + btRigidBody* m_rigid; + btCollisionObject* m_collisionObject; + + Body() : m_soft(0),m_rigid(0),m_collisionObject(0) {} + Body(Cluster* p) : m_soft(p),m_rigid(0),m_collisionObject(0) {} + Body(btCollisionObject* colObj) : m_soft(0),m_collisionObject(colObj) + { + m_rigid = btRigidBody::upcast(m_collisionObject); + } + + void activate() const + { + if(m_rigid) m_rigid->activate(); + } + const btMatrix3x3& invWorldInertia() const + { + static const btMatrix3x3 iwi(0,0,0,0,0,0,0,0,0); + if(m_rigid) return(m_rigid->getInvInertiaTensorWorld()); + if(m_soft) return(m_soft->m_invwi); + return(iwi); + } + btScalar invMass() const + { + if(m_rigid) return(m_rigid->getInvMass()); + if(m_soft) return(m_soft->m_imass); + return(0); + } + const btTransform& xform() const + { + static const btTransform identity=btTransform::getIdentity(); + if(m_collisionObject) return(m_collisionObject->getInterpolationWorldTransform()); + if(m_soft) return(m_soft->m_framexform); + return(identity); + } + btVector3 linearVelocity() const + { + if(m_rigid) return(m_rigid->getLinearVelocity()); + if(m_soft) return(m_soft->m_lv); + return(btVector3(0,0,0)); + } + btVector3 angularVelocity(const btVector3& rpos) const + { + if(m_rigid) return(btCross(m_rigid->getAngularVelocity(),rpos)); + if(m_soft) return(btCross(m_soft->m_av,rpos)); + return(btVector3(0,0,0)); + } + btVector3 angularVelocity() const + { + if(m_rigid) return(m_rigid->getAngularVelocity()); + if(m_soft) return(m_soft->m_av); + return(btVector3(0,0,0)); + } + btVector3 velocity(const btVector3& rpos) const + { + return(linearVelocity()+angularVelocity(rpos)); + } + void applyVImpulse(const btVector3& impulse,const btVector3& rpos) const + { + if(m_rigid) m_rigid->applyImpulse(impulse,rpos); + if(m_soft) btSoftBody::clusterVImpulse(m_soft,rpos,impulse); + } + void applyDImpulse(const btVector3& impulse,const btVector3& rpos) const + { + if(m_rigid) m_rigid->applyImpulse(impulse,rpos); + if(m_soft) btSoftBody::clusterDImpulse(m_soft,rpos,impulse); + } + void applyImpulse(const Impulse& impulse,const btVector3& rpos) const + { + if(impulse.m_asVelocity) + { +// printf("impulse.m_velocity = %f,%f,%f\n",impulse.m_velocity.getX(),impulse.m_velocity.getY(),impulse.m_velocity.getZ()); + applyVImpulse(impulse.m_velocity,rpos); + } + if(impulse.m_asDrift) + { +// printf("impulse.m_drift = %f,%f,%f\n",impulse.m_drift.getX(),impulse.m_drift.getY(),impulse.m_drift.getZ()); + applyDImpulse(impulse.m_drift,rpos); + } + } + void applyVAImpulse(const btVector3& impulse) const + { + if(m_rigid) m_rigid->applyTorqueImpulse(impulse); + if(m_soft) btSoftBody::clusterVAImpulse(m_soft,impulse); + } + void applyDAImpulse(const btVector3& impulse) const + { + if(m_rigid) m_rigid->applyTorqueImpulse(impulse); + if(m_soft) btSoftBody::clusterDAImpulse(m_soft,impulse); + } + void applyAImpulse(const Impulse& impulse) const + { + if(impulse.m_asVelocity) applyVAImpulse(impulse.m_velocity); + if(impulse.m_asDrift) applyDAImpulse(impulse.m_drift); + } + void applyDCImpulse(const btVector3& impulse) const + { + if(m_rigid) m_rigid->applyCentralImpulse(impulse); + if(m_soft) btSoftBody::clusterDCImpulse(m_soft,impulse); + } + }; + /* Joint */ + struct Joint + { + struct eType { enum _ { + Linear, + Angular, + Contact + };}; + struct Specs + { + Specs() : erp(1),cfm(1),split(1) {} + btScalar erp; + btScalar cfm; + btScalar split; + }; + Body m_bodies[2]; + btVector3 m_refs[2]; + btScalar m_cfm; + btScalar m_erp; + btScalar m_split; + btVector3 m_drift; + btVector3 m_sdrift; + btMatrix3x3 m_massmatrix; + bool m_delete; + virtual ~Joint() {} + Joint() : m_delete(false) {} + virtual void Prepare(btScalar dt,int iterations); + virtual void Solve(btScalar dt,btScalar sor)=0; + virtual void Terminate(btScalar dt)=0; + virtual eType::_ Type() const=0; + }; + /* LJoint */ + struct LJoint : Joint + { + struct Specs : Joint::Specs + { + btVector3 position; + }; + btVector3 m_rpos[2]; + void Prepare(btScalar dt,int iterations); + void Solve(btScalar dt,btScalar sor); + void Terminate(btScalar dt); + eType::_ Type() const { return(eType::Linear); } + }; + /* AJoint */ + struct AJoint : Joint + { + struct IControl + { + virtual void Prepare(AJoint*) {} + virtual btScalar Speed(AJoint*,btScalar current) { return(current); } + static IControl* Default() { static IControl def;return(&def); } + }; + struct Specs : Joint::Specs + { + Specs() : icontrol(IControl::Default()) {} + btVector3 axis; + IControl* icontrol; + }; + btVector3 m_axis[2]; + IControl* m_icontrol; + void Prepare(btScalar dt,int iterations); + void Solve(btScalar dt,btScalar sor); + void Terminate(btScalar dt); + eType::_ Type() const { return(eType::Angular); } + }; + /* CJoint */ + struct CJoint : Joint + { + int m_life; + int m_maxlife; + btVector3 m_rpos[2]; + btVector3 m_normal; + btScalar m_friction; + void Prepare(btScalar dt,int iterations); + void Solve(btScalar dt,btScalar sor); + void Terminate(btScalar dt); + eType::_ Type() const { return(eType::Contact); } + }; + /* Config */ + struct Config + { + eAeroModel::_ aeromodel; // Aerodynamic model (default: V_Point) + btScalar kVCF; // Velocities correction factor (Baumgarte) + btScalar kDP; // Damping coefficient [0,1] + btScalar kDG; // Drag coefficient [0,+inf] + btScalar kLF; // Lift coefficient [0,+inf] + btScalar kPR; // Pressure coefficient [-inf,+inf] + btScalar kVC; // Volume conversation coefficient [0,+inf] + btScalar kDF; // Dynamic friction coefficient [0,1] + btScalar kMT; // Pose matching coefficient [0,1] + btScalar kCHR; // Rigid contacts hardness [0,1] + btScalar kKHR; // Kinetic contacts hardness [0,1] + btScalar kSHR; // Soft contacts hardness [0,1] + btScalar kAHR; // Anchors hardness [0,1] + btScalar kSRHR_CL; // Soft vs rigid hardness [0,1] (cluster only) + btScalar kSKHR_CL; // Soft vs kinetic hardness [0,1] (cluster only) + btScalar kSSHR_CL; // Soft vs soft hardness [0,1] (cluster only) + btScalar kSR_SPLT_CL; // Soft vs rigid impulse split [0,1] (cluster only) + btScalar kSK_SPLT_CL; // Soft vs rigid impulse split [0,1] (cluster only) + btScalar kSS_SPLT_CL; // Soft vs rigid impulse split [0,1] (cluster only) + btScalar maxvolume; // Maximum volume ratio for pose + btScalar timescale; // Time scale + int viterations; // Velocities solver iterations + int piterations; // Positions solver iterations + int diterations; // Drift solver iterations + int citerations; // Cluster solver iterations + int collisions; // Collisions flags + tVSolverArray m_vsequence; // Velocity solvers sequence + tPSolverArray m_psequence; // Position solvers sequence + tPSolverArray m_dsequence; // Drift solvers sequence + }; + /* SolverState */ + struct SolverState + { + btScalar sdt; // dt*timescale + btScalar isdt; // 1/sdt + btScalar velmrg; // velocity margin + btScalar radmrg; // radial margin + btScalar updmrg; // Update margin + }; + /// RayFromToCaster takes a ray from, ray to (instead of direction!) + struct RayFromToCaster : btDbvt::ICollide + { + btVector3 m_rayFrom; + btVector3 m_rayTo; + btVector3 m_rayNormalizedDirection; + btScalar m_mint; + Face* m_face; + int m_tests; + RayFromToCaster(const btVector3& rayFrom,const btVector3& rayTo,btScalar mxt); + void Process(const btDbvtNode* leaf); + + static inline btScalar rayFromToTriangle(const btVector3& rayFrom, + const btVector3& rayTo, + const btVector3& rayNormalizedDirection, + const btVector3& a, + const btVector3& b, + const btVector3& c, + btScalar maxt=SIMD_INFINITY); + }; + + // + // Typedef's + // + + typedef void (*psolver_t)(btSoftBody*,btScalar,btScalar); + typedef void (*vsolver_t)(btSoftBody*,btScalar); + typedef btAlignedObjectArray tClusterArray; + typedef btAlignedObjectArray tNoteArray; + typedef btAlignedObjectArray tNodeArray; + typedef btAlignedObjectArray tLeafArray; + typedef btAlignedObjectArray tLinkArray; + typedef btAlignedObjectArray tFaceArray; + typedef btAlignedObjectArray tTetraArray; + typedef btAlignedObjectArray tAnchorArray; + typedef btAlignedObjectArray tRContactArray; + typedef btAlignedObjectArray tSContactArray; + typedef btAlignedObjectArray tMaterialArray; + typedef btAlignedObjectArray tJointArray; + typedef btAlignedObjectArray tSoftBodyArray; + + // + // Fields + // + + Config m_cfg; // Configuration + SolverState m_sst; // Solver state + Pose m_pose; // Pose + void* m_tag; // User data + btSoftBodyWorldInfo* m_worldInfo; // World info + tNoteArray m_notes; // Notes + tNodeArray m_nodes; // Nodes + tLinkArray m_links; // Links + tFaceArray m_faces; // Faces + tTetraArray m_tetras; // Tetras + tAnchorArray m_anchors; // Anchors + tRContactArray m_rcontacts; // Rigid contacts + tSContactArray m_scontacts; // Soft contacts + tJointArray m_joints; // Joints + tMaterialArray m_materials; // Materials + btScalar m_timeacc; // Time accumulator + btVector3 m_bounds[2]; // Spatial bounds + bool m_bUpdateRtCst; // Update runtime constants + btDbvt m_ndbvt; // Nodes tree + btDbvt m_fdbvt; // Faces tree + btDbvt m_cdbvt; // Clusters tree + tClusterArray m_clusters; // Clusters + + btAlignedObjectArraym_clusterConnectivity;//cluster connectivity, for self-collision + + btTransform m_initialWorldTransform; + + // + // Api + // + + /* ctor */ + btSoftBody( btSoftBodyWorldInfo* worldInfo,int node_count, + const btVector3* x, + const btScalar* m); + /* dtor */ + virtual ~btSoftBody(); + /* Check for existing link */ + + btAlignedObjectArray m_userIndexMapping; + + btSoftBodyWorldInfo* getWorldInfo() + { + return m_worldInfo; + } + + ///@todo: avoid internal softbody shape hack and move collision code to collision library + virtual void setCollisionShape(btCollisionShape* collisionShape) + { + + } + + bool checkLink( int node0, + int node1) const; + bool checkLink( const Node* node0, + const Node* node1) const; + /* Check for existring face */ + bool checkFace( int node0, + int node1, + int node2) const; + /* Append material */ + Material* appendMaterial(); + /* Append note */ + void appendNote( const char* text, + const btVector3& o, + const btVector4& c=btVector4(1,0,0,0), + Node* n0=0, + Node* n1=0, + Node* n2=0, + Node* n3=0); + void appendNote( const char* text, + const btVector3& o, + Node* feature); + void appendNote( const char* text, + const btVector3& o, + Link* feature); + void appendNote( const char* text, + const btVector3& o, + Face* feature); + /* Append node */ + void appendNode( const btVector3& x,btScalar m); + /* Append link */ + void appendLink(int model=-1,Material* mat=0); + void appendLink( int node0, + int node1, + Material* mat=0, + bool bcheckexist=false); + void appendLink( Node* node0, + Node* node1, + Material* mat=0, + bool bcheckexist=false); + /* Append face */ + void appendFace(int model=-1,Material* mat=0); + void appendFace( int node0, + int node1, + int node2, + Material* mat=0); + void appendTetra(int model,Material* mat); + // + void appendTetra(int node0, + int node1, + int node2, + int node3, + Material* mat=0); + + + /* Append anchor */ + void appendAnchor( int node, + btRigidBody* body, bool disableCollisionBetweenLinkedBodies=false); + /* Append linear joint */ + void appendLinearJoint(const LJoint::Specs& specs,Cluster* body0,Body body1); + void appendLinearJoint(const LJoint::Specs& specs,Body body=Body()); + void appendLinearJoint(const LJoint::Specs& specs,btSoftBody* body); + /* Append linear joint */ + void appendAngularJoint(const AJoint::Specs& specs,Cluster* body0,Body body1); + void appendAngularJoint(const AJoint::Specs& specs,Body body=Body()); + void appendAngularJoint(const AJoint::Specs& specs,btSoftBody* body); + /* Add force (or gravity) to the entire body */ + void addForce( const btVector3& force); + /* Add force (or gravity) to a node of the body */ + void addForce( const btVector3& force, + int node); + /* Add velocity to the entire body */ + void addVelocity( const btVector3& velocity); + + /* Set velocity for the entire body */ + void setVelocity( const btVector3& velocity); + + /* Add velocity to a node of the body */ + void addVelocity( const btVector3& velocity, + int node); + /* Set mass */ + void setMass( int node, + btScalar mass); + /* Get mass */ + btScalar getMass( int node) const; + /* Get total mass */ + btScalar getTotalMass() const; + /* Set total mass (weighted by previous masses) */ + void setTotalMass( btScalar mass, + bool fromfaces=false); + /* Set total density */ + void setTotalDensity(btScalar density); + /* Set volume mass (using tetrahedrons) */ + void setVolumeMass( btScalar mass); + /* Set volume density (using tetrahedrons) */ + void setVolumeDensity( btScalar density); + /* Transform */ + void transform( const btTransform& trs); + /* Translate */ + void translate( const btVector3& trs); + /* Rotate */ + void rotate( const btQuaternion& rot); + /* Scale */ + void scale( const btVector3& scl); + /* Set current state as pose */ + void setPose( bool bvolume, + bool bframe); + /* Return the volume */ + btScalar getVolume() const; + /* Cluster count */ + int clusterCount() const; + /* Cluster center of mass */ + static btVector3 clusterCom(const Cluster* cluster); + btVector3 clusterCom(int cluster) const; + /* Cluster velocity at rpos */ + static btVector3 clusterVelocity(const Cluster* cluster,const btVector3& rpos); + /* Cluster impulse */ + static void clusterVImpulse(Cluster* cluster,const btVector3& rpos,const btVector3& impulse); + static void clusterDImpulse(Cluster* cluster,const btVector3& rpos,const btVector3& impulse); + static void clusterImpulse(Cluster* cluster,const btVector3& rpos,const Impulse& impulse); + static void clusterVAImpulse(Cluster* cluster,const btVector3& impulse); + static void clusterDAImpulse(Cluster* cluster,const btVector3& impulse); + static void clusterAImpulse(Cluster* cluster,const Impulse& impulse); + static void clusterDCImpulse(Cluster* cluster,const btVector3& impulse); + /* Generate bending constraints based on distance in the adjency graph */ + int generateBendingConstraints( int distance, + Material* mat=0); + /* Randomize constraints to reduce solver bias */ + void randomizeConstraints(); + /* Release clusters */ + void releaseCluster(int index); + void releaseClusters(); + /* Generate clusters (K-mean) */ + ///generateClusters with k=0 will create a convex cluster for each tetrahedron or triangle + ///otherwise an approximation will be used (better performance) + int generateClusters(int k,int maxiterations=8192); + /* Refine */ + void refine(ImplicitFn* ifn,btScalar accurary,bool cut); + /* CutLink */ + bool cutLink(int node0,int node1,btScalar position); + bool cutLink(const Node* node0,const Node* node1,btScalar position); + + ///Ray casting using rayFrom and rayTo in worldspace, (not direction!) + bool rayTest(const btVector3& rayFrom, + const btVector3& rayTo, + sRayCast& results); + /* Solver presets */ + void setSolver(eSolverPresets::_ preset); + /* predictMotion */ + void predictMotion(btScalar dt); + /* solveConstraints */ + void solveConstraints(); + /* staticSolve */ + void staticSolve(int iterations); + /* solveCommonConstraints */ + static void solveCommonConstraints(btSoftBody** bodies,int count,int iterations); + /* solveClusters */ + static void solveClusters(const btAlignedObjectArray& bodies); + /* integrateMotion */ + void integrateMotion(); + /* defaultCollisionHandlers */ + void defaultCollisionHandler(btCollisionObject* pco); + void defaultCollisionHandler(btSoftBody* psb); + + // + // Cast + // + + static const btSoftBody* upcast(const btCollisionObject* colObj) + { + if (colObj->getInternalType()==CO_SOFT_BODY) + return (const btSoftBody*)colObj; + return 0; + } + static btSoftBody* upcast(btCollisionObject* colObj) + { + if (colObj->getInternalType()==CO_SOFT_BODY) + return (btSoftBody*)colObj; + return 0; + } + + // + // ::btCollisionObject + // + + virtual void getAabb(btVector3& aabbMin,btVector3& aabbMax) const + { + aabbMin = m_bounds[0]; + aabbMax = m_bounds[1]; + } + // + // Private + // + void pointersToIndices(); + void indicesToPointers(const int* map=0); + + int rayTest(const btVector3& rayFrom,const btVector3& rayTo, + btScalar& mint,eFeature::_& feature,int& index,bool bcountonly) const; + void initializeFaceTree(); + btVector3 evaluateCom() const; + bool checkContact(btCollisionObject* colObj,const btVector3& x,btScalar margin,btSoftBody::sCti& cti) const; + void updateNormals(); + void updateBounds(); + void updatePose(); + void updateConstants(); + void initializeClusters(); + void updateClusters(); + void cleanupClusters(); + void prepareClusters(int iterations); + void solveClusters(btScalar sor); + void applyClusters(bool drift); + void dampClusters(); + void applyForces(); + static void PSolve_Anchors(btSoftBody* psb,btScalar kst,btScalar ti); + static void PSolve_RContacts(btSoftBody* psb,btScalar kst,btScalar ti); + static void PSolve_SContacts(btSoftBody* psb,btScalar,btScalar ti); + static void PSolve_Links(btSoftBody* psb,btScalar kst,btScalar ti); + static void VSolve_Links(btSoftBody* psb,btScalar kst); + static psolver_t getSolver(ePSolver::_ solver); + static vsolver_t getSolver(eVSolver::_ solver); + +}; + + + +#endif //_BT_SOFT_BODY_H diff --git a/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyConcaveCollisionAlgorithm.cpp b/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyConcaveCollisionAlgorithm.cpp new file mode 100644 index 000000000..02e8186be --- /dev/null +++ b/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyConcaveCollisionAlgorithm.cpp @@ -0,0 +1,368 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btSoftBodyConcaveCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "BulletCollision/CollisionShapes/btMultiSphereShape.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "BulletCollision/CollisionShapes/btConcaveShape.h" +#include "BulletCollision/CollisionDispatch/btManifoldResult.h" +#include "BulletCollision/NarrowPhaseCollision/btRaycastCallback.h" +#include "BulletCollision/CollisionShapes/btTriangleShape.h" +#include "BulletCollision/CollisionShapes/btSphereShape.h" +#include "BulletCollision/CollisionShapes/btTetrahedronShape.h" +#include "BulletCollision/CollisionShapes/btConvexHullShape.h" + + + +#include "LinearMath/btIDebugDraw.h" +#include "BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.h" +#include "BulletSoftBody/btSoftBody.h" + +#define BT_SOFTBODY_TRIANGLE_EXTRUSION btScalar(0.06)//make this configurable + +btSoftBodyConcaveCollisionAlgorithm::btSoftBodyConcaveCollisionAlgorithm( const btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1,bool isSwapped) +: btCollisionAlgorithm(ci), +m_isSwapped(isSwapped), +m_btSoftBodyTriangleCallback(ci.m_dispatcher1,body0,body1,isSwapped) +{ +} + + + +btSoftBodyConcaveCollisionAlgorithm::~btSoftBodyConcaveCollisionAlgorithm() +{ +} + + + +btSoftBodyTriangleCallback::btSoftBodyTriangleCallback(btDispatcher* dispatcher,btCollisionObject* body0,btCollisionObject* body1,bool isSwapped): +m_dispatcher(dispatcher), +m_dispatchInfoPtr(0) +{ + m_softBody = (btSoftBody*) (isSwapped? body1:body0); + m_triBody = isSwapped? body0:body1; + + // + // create the manifold from the dispatcher 'manifold pool' + // + // m_manifoldPtr = m_dispatcher->getNewManifold(m_convexBody,m_triBody); + + clearCache(); +} + +btSoftBodyTriangleCallback::~btSoftBodyTriangleCallback() +{ + clearCache(); + // m_dispatcher->releaseManifold( m_manifoldPtr ); + +} + + +void btSoftBodyTriangleCallback::clearCache() +{ + for (int i=0;im_childShape); + m_softBody->getWorldInfo()->m_sparsesdf.RemoveReferences(tmp->m_childShape);//necessary? + delete tmp->m_childShape; + } + m_shapeCache.clear(); +} + + +void btSoftBodyTriangleCallback::processTriangle(btVector3* triangle,int partId, int triangleIndex) +{ + //just for debugging purposes + //printf("triangle %d",m_triangleCount++); + btCollisionObject* ob = static_cast(m_triBody); + btCollisionAlgorithmConstructionInfo ci; + ci.m_dispatcher1 = m_dispatcher; + + ///debug drawing of the overlapping triangles + if (m_dispatchInfoPtr && m_dispatchInfoPtr->m_debugDraw && m_dispatchInfoPtr->m_debugDraw->getDebugMode() &btIDebugDraw::DBG_DrawWireframe) + { + btVector3 color(255,255,0); + btTransform& tr = ob->getWorldTransform(); + m_dispatchInfoPtr->m_debugDraw->drawLine(tr(triangle[0]),tr(triangle[1]),color); + m_dispatchInfoPtr->m_debugDraw->drawLine(tr(triangle[1]),tr(triangle[2]),color); + m_dispatchInfoPtr->m_debugDraw->drawLine(tr(triangle[2]),tr(triangle[0]),color); + } + + btTriIndex triIndex(partId,triangleIndex,0); + btHashKey triKey(triIndex.getUid()); + + + btTriIndex* shapeIndex = m_shapeCache[triKey]; + if (shapeIndex) + { + btCollisionShape* tm = shapeIndex->m_childShape; + btAssert(tm); + + //copy over user pointers to temporary shape + tm->setUserPointer(ob->getRootCollisionShape()->getUserPointer()); + + btCollisionShape* tmpShape = ob->getCollisionShape(); + ob->internalSetTemporaryCollisionShape( tm ); + + + btCollisionAlgorithm* colAlgo = ci.m_dispatcher1->findAlgorithm(m_softBody,m_triBody,0);//m_manifoldPtr); + + colAlgo->processCollision(m_softBody,m_triBody,*m_dispatchInfoPtr,m_resultOut); + colAlgo->~btCollisionAlgorithm(); + ci.m_dispatcher1->freeCollisionAlgorithm(colAlgo); + ob->internalSetTemporaryCollisionShape( tmpShape); + return; + } + + //aabb filter is already applied! + + //btCollisionObject* colObj = static_cast(m_convexProxy->m_clientObject); + + // if (m_softBody->getCollisionShape()->getShapeType()== + { + // btVector3 other; + btVector3 normal = (triangle[1]-triangle[0]).cross(triangle[2]-triangle[0]); + normal.normalize(); + normal*= BT_SOFTBODY_TRIANGLE_EXTRUSION; + // other=(triangle[0]+triangle[1]+triangle[2])*0.333333f; + // other+=normal*22.f; + btVector3 pts[6] = {triangle[0]+normal, + triangle[1]+normal, + triangle[2]+normal, + triangle[0]-normal, + triangle[1]-normal, + triangle[2]-normal}; + + btConvexHullShape* tm = new btConvexHullShape(&pts[0].getX(),6); + + + // btBU_Simplex1to4 tm(triangle[0],triangle[1],triangle[2],other); + + //btTriangleShape tm(triangle[0],triangle[1],triangle[2]); + // tm.setMargin(m_collisionMarginTriangle); + + //copy over user pointers to temporary shape + tm->setUserPointer(ob->getRootCollisionShape()->getUserPointer()); + + btCollisionShape* tmpShape = ob->getCollisionShape(); + ob->internalSetTemporaryCollisionShape( tm ); + + + btCollisionAlgorithm* colAlgo = ci.m_dispatcher1->findAlgorithm(m_softBody,m_triBody,0);//m_manifoldPtr); + ///this should use the btDispatcher, so the actual registered algorithm is used + // btConvexConvexAlgorithm cvxcvxalgo(m_manifoldPtr,ci,m_convexBody,m_triBody); + + //m_resultOut->setShapeIdentifiersB(partId,triangleIndex); + // cvxcvxalgo.processCollision(m_convexBody,m_triBody,*m_dispatchInfoPtr,m_resultOut); + colAlgo->processCollision(m_softBody,m_triBody,*m_dispatchInfoPtr,m_resultOut); + colAlgo->~btCollisionAlgorithm(); + ci.m_dispatcher1->freeCollisionAlgorithm(colAlgo); + + + ob->internalSetTemporaryCollisionShape( tmpShape ); + triIndex.m_childShape = tm; + m_shapeCache.insert(triKey,triIndex); + + } + + + +} + + + +void btSoftBodyTriangleCallback::setTimeStepAndCounters(btScalar collisionMarginTriangle,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + m_dispatchInfoPtr = &dispatchInfo; + m_collisionMarginTriangle = collisionMarginTriangle+btScalar(BT_SOFTBODY_TRIANGLE_EXTRUSION); + m_resultOut = resultOut; + + + btVector3 aabbWorldSpaceMin,aabbWorldSpaceMax; + m_softBody->getAabb(aabbWorldSpaceMin,aabbWorldSpaceMax); + btVector3 halfExtents = (aabbWorldSpaceMax-aabbWorldSpaceMin)*btScalar(0.5); + btVector3 softBodyCenter = (aabbWorldSpaceMax+aabbWorldSpaceMin)*btScalar(0.5); + + btTransform softTransform; + softTransform.setIdentity(); + softTransform.setOrigin(softBodyCenter); + + btTransform convexInTriangleSpace; + convexInTriangleSpace = m_triBody->getWorldTransform().inverse() * softTransform; + btTransformAabb(halfExtents,m_collisionMarginTriangle,convexInTriangleSpace,m_aabbMin,m_aabbMax); +} + +void btSoftBodyConcaveCollisionAlgorithm::clearCache() +{ + m_btSoftBodyTriangleCallback.clearCache(); + +} + +void btSoftBodyConcaveCollisionAlgorithm::processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + + + //btCollisionObject* convexBody = m_isSwapped ? body1 : body0; + btCollisionObject* triBody = m_isSwapped ? body0 : body1; + + if (triBody->getCollisionShape()->isConcave()) + { + + + btCollisionObject* triOb = triBody; + btConcaveShape* concaveShape = static_cast( triOb->getCollisionShape()); + + // if (convexBody->getCollisionShape()->isConvex()) + { + btScalar collisionMarginTriangle = concaveShape->getMargin(); + + // resultOut->setPersistentManifold(m_btSoftBodyTriangleCallback.m_manifoldPtr); + m_btSoftBodyTriangleCallback.setTimeStepAndCounters(collisionMarginTriangle,dispatchInfo,resultOut); + + //Disable persistency. previously, some older algorithm calculated all contacts in one go, so you can clear it here. + //m_dispatcher->clearManifold(m_btSoftBodyTriangleCallback.m_manifoldPtr); + + // m_btSoftBodyTriangleCallback.m_manifoldPtr->setBodies(convexBody,triBody); + + + concaveShape->processAllTriangles( &m_btSoftBodyTriangleCallback,m_btSoftBodyTriangleCallback.getAabbMin(),m_btSoftBodyTriangleCallback.getAabbMax()); + + // resultOut->refreshContactPoints(); + + } + + } + +} + + +btScalar btSoftBodyConcaveCollisionAlgorithm::calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + (void)resultOut; + (void)dispatchInfo; + btCollisionObject* convexbody = m_isSwapped ? body1 : body0; + btCollisionObject* triBody = m_isSwapped ? body0 : body1; + + + //quick approximation using raycast, todo: hook up to the continuous collision detection (one of the btConvexCast) + + //only perform CCD above a certain threshold, this prevents blocking on the long run + //because object in a blocked ccd state (hitfraction<1) get their linear velocity halved each frame... + btScalar squareMot0 = (convexbody->getInterpolationWorldTransform().getOrigin() - convexbody->getWorldTransform().getOrigin()).length2(); + if (squareMot0 < convexbody->getCcdSquareMotionThreshold()) + { + return btScalar(1.); + } + + //const btVector3& from = convexbody->m_worldTransform.getOrigin(); + //btVector3 to = convexbody->m_interpolationWorldTransform.getOrigin(); + //todo: only do if the motion exceeds the 'radius' + + btTransform triInv = triBody->getWorldTransform().inverse(); + btTransform convexFromLocal = triInv * convexbody->getWorldTransform(); + btTransform convexToLocal = triInv * convexbody->getInterpolationWorldTransform(); + + struct LocalTriangleSphereCastCallback : public btTriangleCallback + { + btTransform m_ccdSphereFromTrans; + btTransform m_ccdSphereToTrans; + btTransform m_meshTransform; + + btScalar m_ccdSphereRadius; + btScalar m_hitFraction; + + + LocalTriangleSphereCastCallback(const btTransform& from,const btTransform& to,btScalar ccdSphereRadius,btScalar hitFraction) + :m_ccdSphereFromTrans(from), + m_ccdSphereToTrans(to), + m_ccdSphereRadius(ccdSphereRadius), + m_hitFraction(hitFraction) + { + } + + + virtual void processTriangle(btVector3* triangle, int partId, int triangleIndex) + { + (void)partId; + (void)triangleIndex; + //do a swept sphere for now + btTransform ident; + ident.setIdentity(); + btConvexCast::CastResult castResult; + castResult.m_fraction = m_hitFraction; + btSphereShape pointShape(m_ccdSphereRadius); + btTriangleShape triShape(triangle[0],triangle[1],triangle[2]); + btVoronoiSimplexSolver simplexSolver; + btSubsimplexConvexCast convexCaster(&pointShape,&triShape,&simplexSolver); + //GjkConvexCast convexCaster(&pointShape,convexShape,&simplexSolver); + //ContinuousConvexCollision convexCaster(&pointShape,convexShape,&simplexSolver,0); + //local space? + + if (convexCaster.calcTimeOfImpact(m_ccdSphereFromTrans,m_ccdSphereToTrans, + ident,ident,castResult)) + { + if (m_hitFraction > castResult.m_fraction) + m_hitFraction = castResult.m_fraction; + } + + } + + }; + + + + + + if (triBody->getCollisionShape()->isConcave()) + { + btVector3 rayAabbMin = convexFromLocal.getOrigin(); + rayAabbMin.setMin(convexToLocal.getOrigin()); + btVector3 rayAabbMax = convexFromLocal.getOrigin(); + rayAabbMax.setMax(convexToLocal.getOrigin()); + btScalar ccdRadius0 = convexbody->getCcdSweptSphereRadius(); + rayAabbMin -= btVector3(ccdRadius0,ccdRadius0,ccdRadius0); + rayAabbMax += btVector3(ccdRadius0,ccdRadius0,ccdRadius0); + + btScalar curHitFraction = btScalar(1.); //is this available? + LocalTriangleSphereCastCallback raycastCallback(convexFromLocal,convexToLocal, + convexbody->getCcdSweptSphereRadius(),curHitFraction); + + raycastCallback.m_hitFraction = convexbody->getHitFraction(); + + btCollisionObject* concavebody = triBody; + + btConcaveShape* triangleMesh = (btConcaveShape*) concavebody->getCollisionShape(); + + if (triangleMesh) + { + triangleMesh->processAllTriangles(&raycastCallback,rayAabbMin,rayAabbMax); + } + + + + if (raycastCallback.m_hitFraction < convexbody->getHitFraction()) + { + convexbody->setHitFraction( raycastCallback.m_hitFraction); + return raycastCallback.m_hitFraction; + } + } + + return btScalar(1.); + +} diff --git a/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyConcaveCollisionAlgorithm.h b/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyConcaveCollisionAlgorithm.h new file mode 100644 index 000000000..a6ea33717 --- /dev/null +++ b/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyConcaveCollisionAlgorithm.h @@ -0,0 +1,153 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 SOFT_BODY_CONCAVE_COLLISION_ALGORITHM_H +#define SOFT_BODY_CONCAVE_COLLISION_ALGORITHM_H + +#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" +#include "BulletCollision/BroadphaseCollision/btDispatcher.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseInterface.h" +#include "BulletCollision/CollisionShapes/btTriangleCallback.h" +#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" +class btDispatcher; +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "BulletCollision/CollisionDispatch/btCollisionCreateFunc.h" +class btSoftBody; +class btCollisionShape; + +#include "LinearMath/btHashMap.h" + +#include "BulletCollision/BroadphaseCollision/btQuantizedBvh.h" //for definition of MAX_NUM_PARTS_IN_BITS + +struct btTriIndex +{ + int m_PartIdTriangleIndex; + class btCollisionShape* m_childShape; + + btTriIndex(int partId,int triangleIndex,btCollisionShape* shape) + { + m_PartIdTriangleIndex = (partId<<(31-MAX_NUM_PARTS_IN_BITS)) | triangleIndex; + m_childShape = shape; + } + + int getTriangleIndex() const + { + // Get only the lower bits where the triangle index is stored + return (m_PartIdTriangleIndex&~((~0)<<(31-MAX_NUM_PARTS_IN_BITS))); + } + int getPartId() const + { + // Get only the highest bits where the part index is stored + return (m_PartIdTriangleIndex>>(31-MAX_NUM_PARTS_IN_BITS)); + } + int getUid() const + { + return m_PartIdTriangleIndex; + } +}; + + +///For each triangle in the concave mesh that overlaps with the AABB of a soft body (m_softBody), processTriangle is called. +class btSoftBodyTriangleCallback : public btTriangleCallback +{ + btSoftBody* m_softBody; + btCollisionObject* m_triBody; + + btVector3 m_aabbMin; + btVector3 m_aabbMax ; + + btManifoldResult* m_resultOut; + + btDispatcher* m_dispatcher; + const btDispatcherInfo* m_dispatchInfoPtr; + btScalar m_collisionMarginTriangle; + + btHashMap,btTriIndex> m_shapeCache; + +public: + int m_triangleCount; + + // btPersistentManifold* m_manifoldPtr; + + btSoftBodyTriangleCallback(btDispatcher* dispatcher,btCollisionObject* body0,btCollisionObject* body1,bool isSwapped); + + void setTimeStepAndCounters(btScalar collisionMarginTriangle,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual ~btSoftBodyTriangleCallback(); + + virtual void processTriangle(btVector3* triangle, int partId, int triangleIndex); + + void clearCache(); + + SIMD_FORCE_INLINE const btVector3& getAabbMin() const + { + return m_aabbMin; + } + SIMD_FORCE_INLINE const btVector3& getAabbMax() const + { + return m_aabbMax; + } + +}; + + + + +/// btSoftBodyConcaveCollisionAlgorithm supports collision between soft body shapes and (concave) trianges meshes. +class btSoftBodyConcaveCollisionAlgorithm : public btCollisionAlgorithm +{ + + bool m_isSwapped; + + btSoftBodyTriangleCallback m_btSoftBodyTriangleCallback; + +public: + + btSoftBodyConcaveCollisionAlgorithm( const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* body0,btCollisionObject* body1,bool isSwapped); + + virtual ~btSoftBodyConcaveCollisionAlgorithm(); + + virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual void getAllContactManifolds(btManifoldArray& manifoldArray) + { + //we don't add any manifolds + } + + void clearCache(); + + struct CreateFunc :public btCollisionAlgorithmCreateFunc + { + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + void* mem = ci.m_dispatcher1->allocateCollisionAlgorithm(sizeof(btSoftBodyConcaveCollisionAlgorithm)); + return new(mem) btSoftBodyConcaveCollisionAlgorithm(ci,body0,body1,false); + } + }; + + struct SwappedCreateFunc :public btCollisionAlgorithmCreateFunc + { + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + void* mem = ci.m_dispatcher1->allocateCollisionAlgorithm(sizeof(btSoftBodyConcaveCollisionAlgorithm)); + return new(mem) btSoftBodyConcaveCollisionAlgorithm(ci,body0,body1,true); + } + }; + +}; + +#endif //SOFT_BODY_CONCAVE_COLLISION_ALGORITHM_H diff --git a/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyHelpers.cpp b/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyHelpers.cpp new file mode 100644 index 000000000..448d3c81f --- /dev/null +++ b/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyHelpers.cpp @@ -0,0 +1,1006 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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. +*/ +///btSoftBodyHelpers.cpp by Nathanael Presson + +#include "btSoftBodyInternals.h" +#include +#include +#include "btSoftBodyHelpers.h" +#include "LinearMath/btConvexHull.h" + +// +static void drawVertex( btIDebugDraw* idraw, + const btVector3& x,btScalar s,const btVector3& c) +{ + idraw->drawLine(x-btVector3(s,0,0),x+btVector3(s,0,0),c); + idraw->drawLine(x-btVector3(0,s,0),x+btVector3(0,s,0),c); + idraw->drawLine(x-btVector3(0,0,s),x+btVector3(0,0,s),c); +} + +// +static void drawBox( btIDebugDraw* idraw, + const btVector3& mins, + const btVector3& maxs, + const btVector3& color) +{ + const btVector3 c[]={ btVector3(mins.x(),mins.y(),mins.z()), + btVector3(maxs.x(),mins.y(),mins.z()), + btVector3(maxs.x(),maxs.y(),mins.z()), + btVector3(mins.x(),maxs.y(),mins.z()), + btVector3(mins.x(),mins.y(),maxs.z()), + btVector3(maxs.x(),mins.y(),maxs.z()), + btVector3(maxs.x(),maxs.y(),maxs.z()), + btVector3(mins.x(),maxs.y(),maxs.z())}; + idraw->drawLine(c[0],c[1],color);idraw->drawLine(c[1],c[2],color); + idraw->drawLine(c[2],c[3],color);idraw->drawLine(c[3],c[0],color); + idraw->drawLine(c[4],c[5],color);idraw->drawLine(c[5],c[6],color); + idraw->drawLine(c[6],c[7],color);idraw->drawLine(c[7],c[4],color); + idraw->drawLine(c[0],c[4],color);idraw->drawLine(c[1],c[5],color); + idraw->drawLine(c[2],c[6],color);idraw->drawLine(c[3],c[7],color); +} + +// +static void drawTree( btIDebugDraw* idraw, + const btDbvtNode* node, + int depth, + const btVector3& ncolor, + const btVector3& lcolor, + int mindepth, + int maxdepth) +{ + if(node) + { + if(node->isinternal()&&((depthchilds[0],depth+1,ncolor,lcolor,mindepth,maxdepth); + drawTree(idraw,node->childs[1],depth+1,ncolor,lcolor,mindepth,maxdepth); + } + if(depth>=mindepth) + { + const btScalar scl=(btScalar)(node->isinternal()?1:1); + const btVector3 mi=node->volume.Center()-node->volume.Extents()*scl; + const btVector3 mx=node->volume.Center()+node->volume.Extents()*scl; + drawBox(idraw,mi,mx,node->isleaf()?lcolor:ncolor); + } + } +} + +// +template +static inline T sum(const btAlignedObjectArray& items) +{ + T v; + if(items.size()) + { + v=items[0]; + for(int i=1,ni=items.size();i +static inline void add(btAlignedObjectArray& items,const Q& value) +{ + for(int i=0,ni=items.size();i +static inline void mul(btAlignedObjectArray& items,const Q& value) +{ + for(int i=0,ni=items.size();i +static inline T average(const btAlignedObjectArray& items) +{ + const btScalar n=(btScalar)(items.size()>0?items.size():1); + return(sum(items)/n); +} + +// +static inline btScalar tetravolume(const btVector3& x0, + const btVector3& x1, + const btVector3& x2, + const btVector3& x3) +{ + const btVector3 a=x1-x0; + const btVector3 b=x2-x0; + const btVector3 c=x3-x0; + return(btDot(a,btCross(b,c))); +} + +// +#if 0 +static btVector3 stresscolor(btScalar stress) +{ + static const btVector3 spectrum[]= { btVector3(1,0,1), + btVector3(0,0,1), + btVector3(0,1,1), + btVector3(0,1,0), + btVector3(1,1,0), + btVector3(1,0,0), + btVector3(1,0,0)}; + static const int ncolors=sizeof(spectrum)/sizeof(spectrum[0])-1; + static const btScalar one=1; + stress=btMax(0,btMin(1,stress))*ncolors; + const int sel=(int)stress; + const btScalar frc=stress-sel; + return(spectrum[sel]+(spectrum[sel+1]-spectrum[sel])*frc); +} +#endif + +// +void btSoftBodyHelpers::Draw( btSoftBody* psb, + btIDebugDraw* idraw, + int drawflags) +{ + const btScalar scl=(btScalar)0.1; + const btScalar nscl=scl*5; + const btVector3 lcolor=btVector3(0,0,0); + const btVector3 ncolor=btVector3(1,1,1); + const btVector3 ccolor=btVector3(1,0,0); + int i,j,nj; + + /* Nodes */ + if(0!=(drawflags&fDrawFlags::Nodes)) + { + for(i=0;im_nodes.size();++i) + { + const btSoftBody::Node& n=psb->m_nodes[i]; + if(0==(n.m_material->m_flags&btSoftBody::fMaterial::DebugDraw)) continue; + idraw->drawLine(n.m_x-btVector3(scl,0,0),n.m_x+btVector3(scl,0,0),btVector3(1,0,0)); + idraw->drawLine(n.m_x-btVector3(0,scl,0),n.m_x+btVector3(0,scl,0),btVector3(0,1,0)); + idraw->drawLine(n.m_x-btVector3(0,0,scl),n.m_x+btVector3(0,0,scl),btVector3(0,0,1)); + } + } + /* Links */ + if(0!=(drawflags&fDrawFlags::Links)) + { + for(i=0;im_links.size();++i) + { + const btSoftBody::Link& l=psb->m_links[i]; + if(0==(l.m_material->m_flags&btSoftBody::fMaterial::DebugDraw)) continue; + idraw->drawLine(l.m_n[0]->m_x,l.m_n[1]->m_x,lcolor); + } + } + /* Normals */ + if(0!=(drawflags&fDrawFlags::Normals)) + { + for(i=0;im_nodes.size();++i) + { + const btSoftBody::Node& n=psb->m_nodes[i]; + if(0==(n.m_material->m_flags&btSoftBody::fMaterial::DebugDraw)) continue; + const btVector3 d=n.m_n*nscl; + idraw->drawLine(n.m_x,n.m_x+d,ncolor); + idraw->drawLine(n.m_x,n.m_x-d,ncolor*0.5); + } + } + /* Contacts */ + if(0!=(drawflags&fDrawFlags::Contacts)) + { + static const btVector3 axis[]={btVector3(1,0,0), + btVector3(0,1,0), + btVector3(0,0,1)}; + for(i=0;im_rcontacts.size();++i) + { + const btSoftBody::RContact& c=psb->m_rcontacts[i]; + const btVector3 o= c.m_node->m_x-c.m_cti.m_normal* + (btDot(c.m_node->m_x,c.m_cti.m_normal)+c.m_cti.m_offset); + const btVector3 x=btCross(c.m_cti.m_normal,axis[c.m_cti.m_normal.minAxis()]).normalized(); + const btVector3 y=btCross(x,c.m_cti.m_normal).normalized(); + idraw->drawLine(o-x*nscl,o+x*nscl,ccolor); + idraw->drawLine(o-y*nscl,o+y*nscl,ccolor); + idraw->drawLine(o,o+c.m_cti.m_normal*nscl*3,btVector3(1,1,0)); + } + } + /* Anchors */ + if(0!=(drawflags&fDrawFlags::Anchors)) + { + for(i=0;im_anchors.size();++i) + { + const btSoftBody::Anchor& a=psb->m_anchors[i]; + const btVector3 q=a.m_body->getWorldTransform()*a.m_local; + drawVertex(idraw,a.m_node->m_x,0.25,btVector3(1,0,0)); + drawVertex(idraw,q,0.25,btVector3(0,1,0)); + idraw->drawLine(a.m_node->m_x,q,btVector3(1,1,1)); + } + for(i=0;im_nodes.size();++i) + { + const btSoftBody::Node& n=psb->m_nodes[i]; + if(0==(n.m_material->m_flags&btSoftBody::fMaterial::DebugDraw)) continue; + if(n.m_im<=0) + { + drawVertex(idraw,n.m_x,0.25,btVector3(1,0,0)); + } + } + } + /* Faces */ + if(0!=(drawflags&fDrawFlags::Faces)) + { + const btScalar scl=(btScalar)0.8; + const btScalar alp=(btScalar)1; + const btVector3 col(0,(btScalar)0.7,0); + for(i=0;im_faces.size();++i) + { + const btSoftBody::Face& f=psb->m_faces[i]; + if(0==(f.m_material->m_flags&btSoftBody::fMaterial::DebugDraw)) continue; + const btVector3 x[]={f.m_n[0]->m_x,f.m_n[1]->m_x,f.m_n[2]->m_x}; + const btVector3 c=(x[0]+x[1]+x[2])/3; + idraw->drawTriangle((x[0]-c)*scl+c, + (x[1]-c)*scl+c, + (x[2]-c)*scl+c, + col,alp); + } + } + /* Clusters */ + if(0!=(drawflags&fDrawFlags::Clusters)) + { + srand(1806); + for(i=0;im_clusters.size();++i) + { + if(psb->m_clusters[i]->m_collide) + { + btVector3 color( rand()/(btScalar)RAND_MAX, + rand()/(btScalar)RAND_MAX, + rand()/(btScalar)RAND_MAX); + color=color.normalized()*0.75; + btAlignedObjectArray vertices; + vertices.resize(psb->m_clusters[i]->m_nodes.size()); + for(j=0,nj=vertices.size();jm_clusters[i]->m_nodes[j]->m_x; + } + HullDesc hdsc(QF_TRIANGLES,vertices.size(),&vertices[0]); + HullResult hres; + HullLibrary hlib; + hdsc.mMaxVertices=vertices.size(); + hlib.CreateConvexHull(hdsc,hres); + const btVector3 center=average(hres.m_OutputVertices); + add(hres.m_OutputVertices,-center); + mul(hres.m_OutputVertices,(btScalar)1); + add(hres.m_OutputVertices,center); + for(j=0;j<(int)hres.mNumFaces;++j) + { + const int idx[]={hres.m_Indices[j*3+0],hres.m_Indices[j*3+1],hres.m_Indices[j*3+2]}; + idraw->drawTriangle(hres.m_OutputVertices[idx[0]], + hres.m_OutputVertices[idx[1]], + hres.m_OutputVertices[idx[2]], + color,1); + } + hlib.ReleaseResult(hres); + } + /* Velocities */ +#if 0 + for(int j=0;jm_clusters[i].m_nodes.size();++j) + { + const btSoftBody::Cluster& c=psb->m_clusters[i]; + const btVector3 r=c.m_nodes[j]->m_x-c.m_com; + const btVector3 v=c.m_lv+btCross(c.m_av,r); + idraw->drawLine(c.m_nodes[j]->m_x,c.m_nodes[j]->m_x+v,btVector3(1,0,0)); + } +#endif + /* Frame */ + btSoftBody::Cluster& c=*psb->m_clusters[i]; + idraw->drawLine(c.m_com,c.m_framexform*btVector3(10,0,0),btVector3(1,0,0)); + idraw->drawLine(c.m_com,c.m_framexform*btVector3(0,10,0),btVector3(0,1,0)); + idraw->drawLine(c.m_com,c.m_framexform*btVector3(0,0,10),btVector3(0,0,1)); + } + } + + /* Tetras */ + if(0!=(drawflags&fDrawFlags::Tetras)) + { + const btScalar scl=(btScalar)0.8; + const btScalar alp=(btScalar)1; + const btVector3 col((btScalar)0.7,(btScalar)0.7,(btScalar)0.7); + for(int i=0;im_tetras.size();++i) + { + const btSoftBody::Tetra& t=psb->m_tetras[i]; + if(0==(t.m_material->m_flags&btSoftBody::fMaterial::DebugDraw)) continue; + const btVector3 x[]={t.m_n[0]->m_x,t.m_n[1]->m_x,t.m_n[2]->m_x,t.m_n[3]->m_x}; + const btVector3 c=(x[0]+x[1]+x[2]+x[3])/4; + idraw->drawTriangle((x[0]-c)*scl+c,(x[1]-c)*scl+c,(x[2]-c)*scl+c,col,alp); + idraw->drawTriangle((x[0]-c)*scl+c,(x[1]-c)*scl+c,(x[3]-c)*scl+c,col,alp); + idraw->drawTriangle((x[1]-c)*scl+c,(x[2]-c)*scl+c,(x[3]-c)*scl+c,col,alp); + idraw->drawTriangle((x[2]-c)*scl+c,(x[0]-c)*scl+c,(x[3]-c)*scl+c,col,alp); + } + } + + /* Notes */ + if(0!=(drawflags&fDrawFlags::Notes)) + { + for(i=0;im_notes.size();++i) + { + const btSoftBody::Note& n=psb->m_notes[i]; + btVector3 p=n.m_offset; + for(int j=0;jm_x*n.m_coords[j]; + } + idraw->draw3dText(p,n.m_text); + } + } + /* Node tree */ + if(0!=(drawflags&fDrawFlags::NodeTree)) DrawNodeTree(psb,idraw); + /* Face tree */ + if(0!=(drawflags&fDrawFlags::FaceTree)) DrawFaceTree(psb,idraw); + /* Cluster tree */ + if(0!=(drawflags&fDrawFlags::ClusterTree)) DrawClusterTree(psb,idraw); + /* Joints */ + if(0!=(drawflags&fDrawFlags::Joints)) + { + for(i=0;im_joints.size();++i) + { + const btSoftBody::Joint* pj=psb->m_joints[i]; + switch(pj->Type()) + { + case btSoftBody::Joint::eType::Linear: + { + const btSoftBody::LJoint* pjl=(const btSoftBody::LJoint*)pj; + const btVector3 a0=pj->m_bodies[0].xform()*pjl->m_refs[0]; + const btVector3 a1=pj->m_bodies[1].xform()*pjl->m_refs[1]; + idraw->drawLine(pj->m_bodies[0].xform().getOrigin(),a0,btVector3(1,1,0)); + idraw->drawLine(pj->m_bodies[1].xform().getOrigin(),a1,btVector3(0,1,1)); + drawVertex(idraw,a0,0.25,btVector3(1,1,0)); + drawVertex(idraw,a1,0.25,btVector3(0,1,1)); + } + break; + case btSoftBody::Joint::eType::Angular: + { + //const btSoftBody::AJoint* pja=(const btSoftBody::AJoint*)pj; + const btVector3 o0=pj->m_bodies[0].xform().getOrigin(); + const btVector3 o1=pj->m_bodies[1].xform().getOrigin(); + const btVector3 a0=pj->m_bodies[0].xform().getBasis()*pj->m_refs[0]; + const btVector3 a1=pj->m_bodies[1].xform().getBasis()*pj->m_refs[1]; + idraw->drawLine(o0,o0+a0*10,btVector3(1,1,0)); + idraw->drawLine(o0,o0+a1*10,btVector3(1,1,0)); + idraw->drawLine(o1,o1+a0*10,btVector3(0,1,1)); + idraw->drawLine(o1,o1+a1*10,btVector3(0,1,1)); + } + } + } + } +} + +// +void btSoftBodyHelpers::DrawInfos( btSoftBody* psb, + btIDebugDraw* idraw, + bool masses, + bool areas, + bool /*stress*/) +{ + for(int i=0;im_nodes.size();++i) + { + const btSoftBody::Node& n=psb->m_nodes[i]; + char text[2048]={0}; + char buff[1024]; + if(masses) + { + sprintf(buff," M(%.2f)",1/n.m_im); + strcat(text,buff); + } + if(areas) + { + sprintf(buff," A(%.2f)",n.m_area); + strcat(text,buff); + } + if(text[0]) idraw->draw3dText(n.m_x,text); + } +} + +// +void btSoftBodyHelpers::DrawNodeTree( btSoftBody* psb, + btIDebugDraw* idraw, + int mindepth, + int maxdepth) +{ + drawTree(idraw,psb->m_ndbvt.m_root,0,btVector3(1,0,1),btVector3(1,1,1),mindepth,maxdepth); +} + +// +void btSoftBodyHelpers::DrawFaceTree( btSoftBody* psb, + btIDebugDraw* idraw, + int mindepth, + int maxdepth) +{ + drawTree(idraw,psb->m_fdbvt.m_root,0,btVector3(0,1,0),btVector3(1,0,0),mindepth,maxdepth); +} + +// +void btSoftBodyHelpers::DrawClusterTree( btSoftBody* psb, + btIDebugDraw* idraw, + int mindepth, + int maxdepth) +{ + drawTree(idraw,psb->m_cdbvt.m_root,0,btVector3(0,1,1),btVector3(1,0,0),mindepth,maxdepth); +} + +// +void btSoftBodyHelpers::DrawFrame( btSoftBody* psb, + btIDebugDraw* idraw) +{ + if(psb->m_pose.m_bframe) + { + static const btScalar ascl=10; + static const btScalar nscl=(btScalar)0.1; + const btVector3 com=psb->m_pose.m_com; + const btMatrix3x3 trs=psb->m_pose.m_rot*psb->m_pose.m_scl; + const btVector3 Xaxis=(trs*btVector3(1,0,0)).normalized(); + const btVector3 Yaxis=(trs*btVector3(0,1,0)).normalized(); + const btVector3 Zaxis=(trs*btVector3(0,0,1)).normalized(); + idraw->drawLine(com,com+Xaxis*ascl,btVector3(1,0,0)); + idraw->drawLine(com,com+Yaxis*ascl,btVector3(0,1,0)); + idraw->drawLine(com,com+Zaxis*ascl,btVector3(0,0,1)); + for(int i=0;im_pose.m_pos.size();++i) + { + const btVector3 x=com+trs*psb->m_pose.m_pos[i]; + drawVertex(idraw,x,nscl,btVector3(1,0,1)); + } + } +} + +// +btSoftBody* btSoftBodyHelpers::CreateRope( btSoftBodyWorldInfo& worldInfo, const btVector3& from, + const btVector3& to, + int res, + int fixeds) +{ + /* Create nodes */ + const int r=res+2; + btVector3* x=new btVector3[r]; + btScalar* m=new btScalar[r]; + int i; + + for(i=0;isetMass(0,0); + if(fixeds&2) psb->setMass(r-1,0); + delete[] x; + delete[] m; + /* Create links */ + for(i=1;iappendLink(i-1,i); + } + /* Finished */ + return(psb); +} + +// +btSoftBody* btSoftBodyHelpers::CreatePatch(btSoftBodyWorldInfo& worldInfo,const btVector3& corner00, + const btVector3& corner10, + const btVector3& corner01, + const btVector3& corner11, + int resx, + int resy, + int fixeds, + bool gendiags) +{ +#define IDX(_x_,_y_) ((_y_)*rx+(_x_)) + /* Create nodes */ + if((resx<2)||(resy<2)) return(0); + const int rx=resx; + const int ry=resy; + const int tot=rx*ry; + btVector3* x=new btVector3[tot]; + btScalar* m=new btScalar[tot]; + int iy; + + for(iy=0;iysetMass(IDX(0,0),0); + if(fixeds&2) psb->setMass(IDX(rx-1,0),0); + if(fixeds&4) psb->setMass(IDX(0,ry-1),0); + if(fixeds&8) psb->setMass(IDX(rx-1,ry-1),0); + delete[] x; + delete[] m; + /* Create links and faces */ + for(iy=0;iyappendLink(idx,IDX(ix+1,iy)); + if(mdy) psb->appendLink(idx,IDX(ix,iy+1)); + if(mdx&&mdy) + { + if((ix+iy)&1) + { + psb->appendFace(IDX(ix,iy),IDX(ix+1,iy),IDX(ix+1,iy+1)); + psb->appendFace(IDX(ix,iy),IDX(ix+1,iy+1),IDX(ix,iy+1)); + if(gendiags) + { + psb->appendLink(IDX(ix,iy),IDX(ix+1,iy+1)); + } + } + else + { + psb->appendFace(IDX(ix,iy+1),IDX(ix,iy),IDX(ix+1,iy)); + psb->appendFace(IDX(ix,iy+1),IDX(ix+1,iy),IDX(ix+1,iy+1)); + if(gendiags) + { + psb->appendLink(IDX(ix+1,iy),IDX(ix,iy+1)); + } + } + } + } + } + /* Finished */ +#undef IDX + return(psb); +} + +// +btSoftBody* btSoftBodyHelpers::CreatePatchUV(btSoftBodyWorldInfo& worldInfo, + const btVector3& corner00, + const btVector3& corner10, + const btVector3& corner01, + const btVector3& corner11, + int resx, + int resy, + int fixeds, + bool gendiags, + float* tex_coords) +{ + + /* + * + * corners: + * + * [0][0] corner00 ------- corner01 [resx][0] + * | | + * | | + * [0][resy] corner10 -------- corner11 [resx][resy] + * + * + * + * + * + * + * "fixedgs" map: + * + * corner00 --> +1 + * corner01 --> +2 + * corner10 --> +4 + * corner11 --> +8 + * upper middle --> +16 + * left middle --> +32 + * right middle --> +64 + * lower middle --> +128 + * center --> +256 + * + * + * tex_coords size (resx-1)*(resy-1)*12 + * + * + * + * SINGLE QUAD INTERNALS + * + * 1) btSoftBody's nodes and links, + * diagonal link is optional ("gendiags") + * + * + * node00 ------ node01 + * | . + * | . + * | . + * | . + * | . + * node10 node11 + * + * + * + * 2) Faces: + * two triangles, + * UV Coordinates (hier example for single quad) + * + * (0,1) (0,1) (1,1) + * 1 |\ 3 \-----| 2 + * | \ \ | + * | \ \ | + * | \ \ | + * | \ \ | + * 2 |-----\ 3 \| 1 + * (0,0) (1,0) (1,0) + * + * + * + * + * + * + */ + +#define IDX(_x_,_y_) ((_y_)*rx+(_x_)) + /* Create nodes */ + if((resx<2)||(resy<2)) return(0); + const int rx=resx; + const int ry=resy; + const int tot=rx*ry; + btVector3* x=new btVector3[tot]; + btScalar* m=new btScalar[tot]; + + int iy; + + for(iy=0;iysetMass(IDX(0,0),0); + if(fixeds&2) psb->setMass(IDX(rx-1,0),0); + if(fixeds&4) psb->setMass(IDX(0,ry-1),0); + if(fixeds&8) psb->setMass(IDX(rx-1,ry-1),0); + if(fixeds&16) psb->setMass(IDX((rx-1)/2,0),0); + if(fixeds&32) psb->setMass(IDX(0,(ry-1)/2),0); + if(fixeds&64) psb->setMass(IDX(rx-1,(ry-1)/2),0); + if(fixeds&128) psb->setMass(IDX((rx-1)/2,ry-1),0); + if(fixeds&256) psb->setMass(IDX((rx-1)/2,(ry-1)/2),0); + delete[] x; + delete[] m; + + + int z = 0; + /* Create links and faces */ + for(iy=0;iyappendLink(node00,node01); + if(mdy) psb->appendLink(node00,node10); + if(mdx&&mdy) + { + psb->appendFace(node00,node10,node11); + if (tex_coords) { + tex_coords[z+0]=CalculateUV(resx,resy,ix,iy,0); + tex_coords[z+1]=CalculateUV(resx,resy,ix,iy,1); + tex_coords[z+2]=CalculateUV(resx,resy,ix,iy,0); + tex_coords[z+3]=CalculateUV(resx,resy,ix,iy,2); + tex_coords[z+4]=CalculateUV(resx,resy,ix,iy,3); + tex_coords[z+5]=CalculateUV(resx,resy,ix,iy,2); + } + psb->appendFace(node11,node01,node00); + if (tex_coords) { + tex_coords[z+6 ]=CalculateUV(resx,resy,ix,iy,3); + tex_coords[z+7 ]=CalculateUV(resx,resy,ix,iy,2); + tex_coords[z+8 ]=CalculateUV(resx,resy,ix,iy,3); + tex_coords[z+9 ]=CalculateUV(resx,resy,ix,iy,1); + tex_coords[z+10]=CalculateUV(resx,resy,ix,iy,0); + tex_coords[z+11]=CalculateUV(resx,resy,ix,iy,1); + } + if (gendiags) psb->appendLink(node00,node11); + z += 12; + } + } + } + /* Finished */ +#undef IDX + return(psb); +} + +float btSoftBodyHelpers::CalculateUV(int resx,int resy,int ix,int iy,int id) +{ + + /* + * + * + * node00 --- node01 + * | | + * node10 --- node11 + * + * + * ID map: + * + * node00 s --> 0 + * node00 t --> 1 + * + * node01 s --> 3 + * node01 t --> 1 + * + * node10 s --> 0 + * node10 t --> 2 + * + * node11 s --> 3 + * node11 t --> 2 + * + * + */ + + float tc=0.0f; + if (id == 0) { + tc = (1.0f/((resx-1))*ix); + } + else if (id==1) { + tc = (1.0f/((resy-1))*(resy-1-iy)); + } + else if (id==2) { + tc = (1.0f/((resy-1))*(resy-1-iy-1)); + } + else if (id==3) { + tc = (1.0f/((resx-1))*(ix+1)); + } + return tc; +} +// +btSoftBody* btSoftBodyHelpers::CreateEllipsoid(btSoftBodyWorldInfo& worldInfo,const btVector3& center, + const btVector3& radius, + int res) +{ + struct Hammersley + { + static void Generate(btVector3* x,int n) + { + for(int i=0;i>=1) if(j&1) t+=p; + btScalar w=2*t-1; + btScalar a=(SIMD_PI+2*i*SIMD_PI)/n; + btScalar s=btSqrt(1-w*w); + *x++=btVector3(s*btCos(a),s*btSin(a),w); + } + } + }; + btAlignedObjectArray vtx; + vtx.resize(3+res); + Hammersley::Generate(&vtx[0],vtx.size()); + for(int i=0;i chks; + btAlignedObjectArray vtx; + chks.resize(maxidx*maxidx,false); + vtx.resize(maxidx); + for(i=0,j=0,ni=maxidx*3;iappendLink(idx[j],idx[k]); + } + } +#undef IDX + psb->appendFace(idx[0],idx[1],idx[2]); + } + psb->randomizeConstraints(); + return(psb); +} + +// +btSoftBody* btSoftBodyHelpers::CreateFromConvexHull(btSoftBodyWorldInfo& worldInfo, const btVector3* vertices, + int nvertices) +{ + HullDesc hdsc(QF_TRIANGLES,nvertices,vertices); + HullResult hres; + HullLibrary hlib;/*??*/ + hdsc.mMaxVertices=nvertices; + hlib.CreateConvexHull(hdsc,hres); + btSoftBody* psb=new btSoftBody(&worldInfo,(int)hres.mNumOutputVertices, + &hres.m_OutputVertices[0],0); + for(int i=0;i<(int)hres.mNumFaces;++i) + { + const int idx[]={ hres.m_Indices[i*3+0], + hres.m_Indices[i*3+1], + hres.m_Indices[i*3+2]}; + if(idx[0]appendLink( idx[0],idx[1]); + if(idx[1]appendLink( idx[1],idx[2]); + if(idx[2]appendLink( idx[2],idx[0]); + psb->appendFace(idx[0],idx[1],idx[2]); + } + hlib.ReleaseResult(hres); + psb->randomizeConstraints(); + return(psb); +} + + + + +static int nextLine(const char* buffer) +{ + int numBytesRead=0; + + while (*buffer != '\n') + { + buffer++; + numBytesRead++; + } + + + if (buffer[0]==0x0a) + { + buffer++; + numBytesRead++; + } + return numBytesRead; +} + +/* Create from TetGen .ele, .face, .node data */ +btSoftBody* btSoftBodyHelpers::CreateFromTetGenData(btSoftBodyWorldInfo& worldInfo, + const char* ele, + const char* face, + const char* node, + bool bfacelinks, + bool btetralinks, + bool bfacesfromtetras) +{ +btAlignedObjectArray pos; +int nnode=0; +int ndims=0; +int nattrb=0; +int hasbounds=0; +int result = sscanf(node,"%d %d %d %d",&nnode,&ndims,&nattrb,&hasbounds); +result = sscanf(node,"%d %d %d %d",&nnode,&ndims,&nattrb,&hasbounds); +node += nextLine(node); + +pos.resize(nnode); +for(int i=0;i>index; +// sn>>x;sn>>y;sn>>z; + node += nextLine(node); + + //for(int j=0;j>a; + + //if(hasbounds) + // sn>>bound; + + pos[index].setX(btScalar(x)); + pos[index].setY(btScalar(y)); + pos[index].setZ(btScalar(z)); + } +btSoftBody* psb=new btSoftBody(&worldInfo,nnode,&pos[0],0); +#if 0 +if(face&&face[0]) + { + int nface=0; + sf>>nface;sf>>hasbounds; + for(int i=0;i>index; + sf>>ni[0];sf>>ni[1];sf>>ni[2]; + sf>>bound; + psb->appendFace(ni[0],ni[1],ni[2]); + if(btetralinks) + { + psb->appendLink(ni[0],ni[1],0,true); + psb->appendLink(ni[1],ni[2],0,true); + psb->appendLink(ni[2],ni[0],0,true); + } + } + } +#endif + +if(ele&&ele[0]) + { + int ntetra=0; + int ncorner=0; + int neattrb=0; + sscanf(ele,"%d %d %d",&ntetra,&ncorner,&neattrb); + ele += nextLine(ele); + + //se>>ntetra;se>>ncorner;se>>neattrb; + for(int i=0;i>index; + //se>>ni[0];se>>ni[1];se>>ni[2];se>>ni[3]; + sscanf(ele,"%d %d %d %d %d",&index,&ni[0],&ni[1],&ni[2],&ni[3]); + ele+=nextLine(ele); + //for(int j=0;j>a; + psb->appendTetra(ni[0],ni[1],ni[2],ni[3]); + if(btetralinks) + { + psb->appendLink(ni[0],ni[1],0,true); + psb->appendLink(ni[1],ni[2],0,true); + psb->appendLink(ni[2],ni[0],0,true); + psb->appendLink(ni[0],ni[3],0,true); + psb->appendLink(ni[1],ni[3],0,true); + psb->appendLink(ni[2],ni[3],0,true); + } + } + } +printf("Nodes: %u\r\n",psb->m_nodes.size()); +printf("Links: %u\r\n",psb->m_links.size()); +printf("Faces: %u\r\n",psb->m_faces.size()); +printf("Tetras: %u\r\n",psb->m_tetras.size()); +return(psb); +} + diff --git a/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyHelpers.h b/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyHelpers.h new file mode 100644 index 000000000..54f81f354 --- /dev/null +++ b/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyHelpers.h @@ -0,0 +1,141 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2008 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 SOFT_BODY_HELPERS_H +#define SOFT_BODY_HELPERS_H + +#include "btSoftBody.h" + +// +// Helpers +// + +/* fDrawFlags */ +struct fDrawFlags { enum _ { + Nodes = 0x0001, + Links = 0x0002, + Faces = 0x0004, + Tetras = 0x0008, + Normals = 0x0010, + Contacts = 0x0020, + Anchors = 0x0040, + Notes = 0x0080, + Clusters = 0x0100, + NodeTree = 0x0200, + FaceTree = 0x0400, + ClusterTree = 0x0800, + Joints = 0x1000, + /* presets */ + Std = Links+Faces+Tetras+Anchors+Notes+Joints, + StdTetra = Std-Faces+Tetras +};}; + +struct btSoftBodyHelpers +{ + /* Draw body */ + static void Draw( btSoftBody* psb, + btIDebugDraw* idraw, + int drawflags=fDrawFlags::Std); + /* Draw body infos */ + static void DrawInfos( btSoftBody* psb, + btIDebugDraw* idraw, + bool masses, + bool areas, + bool stress); + /* Draw node tree */ + static void DrawNodeTree( btSoftBody* psb, + btIDebugDraw* idraw, + int mindepth=0, + int maxdepth=-1); + /* Draw face tree */ + static void DrawFaceTree( btSoftBody* psb, + btIDebugDraw* idraw, + int mindepth=0, + int maxdepth=-1); + /* Draw cluster tree */ + static void DrawClusterTree(btSoftBody* psb, + btIDebugDraw* idraw, + int mindepth=0, + int maxdepth=-1); + /* Draw rigid frame */ + static void DrawFrame( btSoftBody* psb, + btIDebugDraw* idraw); + /* Create a rope */ + static btSoftBody* CreateRope( btSoftBodyWorldInfo& worldInfo, + const btVector3& from, + const btVector3& to, + int res, + int fixeds); + /* Create a patch */ + static btSoftBody* CreatePatch(btSoftBodyWorldInfo& worldInfo, + const btVector3& corner00, + const btVector3& corner10, + const btVector3& corner01, + const btVector3& corner11, + int resx, + int resy, + int fixeds, + bool gendiags); + /* Create a patch with UV Texture Coordinates */ + static btSoftBody* CreatePatchUV(btSoftBodyWorldInfo& worldInfo, + const btVector3& corner00, + const btVector3& corner10, + const btVector3& corner01, + const btVector3& corner11, + int resx, + int resy, + int fixeds, + bool gendiags, + float* tex_coords=0); + static float CalculateUV(int resx,int resy,int ix,int iy,int id); + /* Create an ellipsoid */ + static btSoftBody* CreateEllipsoid(btSoftBodyWorldInfo& worldInfo, + const btVector3& center, + const btVector3& radius, + int res); + /* Create from trimesh */ + static btSoftBody* CreateFromTriMesh( btSoftBodyWorldInfo& worldInfo, + const btScalar* vertices, + const int* triangles, + int ntriangles); + /* Create from convex-hull */ + static btSoftBody* CreateFromConvexHull( btSoftBodyWorldInfo& worldInfo, + const btVector3* vertices, + int nvertices); + + + /* Export TetGen compatible .smesh file */ + static void ExportAsSMeshFile( btSoftBody* psb, + const char* filename); + /* Create from TetGen .ele, .face, .node files */ + static btSoftBody* CreateFromTetGenFile( btSoftBodyWorldInfo& worldInfo, + const char* ele, + const char* face, + const char* node, + bool bfacelinks, + bool btetralinks, + bool bfacesfromtetras); + /* Create from TetGen .ele, .face, .node data */ + static btSoftBody* CreateFromTetGenData( btSoftBodyWorldInfo& worldInfo, + const char* ele, + const char* face, + const char* node, + bool bfacelinks, + bool btetralinks, + bool bfacesfromtetras); + +}; + +#endif //SOFT_BODY_HELPERS_H diff --git a/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyInternals.h b/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyInternals.h new file mode 100644 index 000000000..2cb7744cb --- /dev/null +++ b/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyInternals.h @@ -0,0 +1,931 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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. +*/ +///btSoftBody implementation by Nathanael Presson + +#ifndef _BT_SOFT_BODY_INTERNALS_H +#define _BT_SOFT_BODY_INTERNALS_H + +#include "btSoftBody.h" + + +#include "LinearMath/btQuickprof.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseInterface.h" +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/CollisionShapes/btConvexInternalShape.h" +#include "BulletCollision/NarrowPhaseCollision/btGjkEpa2.h" + +// +// btSymMatrix +// +template +struct btSymMatrix +{ + btSymMatrix() : dim(0) {} + btSymMatrix(int n,const T& init=T()) { resize(n,init); } + void resize(int n,const T& init=T()) { dim=n;store.resize((n*(n+1))/2,init); } + int index(int c,int r) const { if(c>r) btSwap(c,r);btAssert(r store; + int dim; +}; + +// +// btSoftBodyCollisionShape +// +class btSoftBodyCollisionShape : public btConcaveShape +{ +public: + btSoftBody* m_body; + + btSoftBodyCollisionShape(btSoftBody* backptr) + { + m_shapeType = SOFTBODY_SHAPE_PROXYTYPE; + m_body=backptr; + } + + virtual ~btSoftBodyCollisionShape() + { + + } + + void processAllTriangles(btTriangleCallback* /*callback*/,const btVector3& /*aabbMin*/,const btVector3& /*aabbMax*/) const + { + //not yet + btAssert(0); + } + + ///getAabb returns the axis aligned bounding box in the coordinate frame of the given transform t. + virtual void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const + { + /* t should be identity, but better be safe than...fast? */ + const btVector3 mins=m_body->m_bounds[0]; + const btVector3 maxs=m_body->m_bounds[1]; + const btVector3 crns[]={t*btVector3(mins.x(),mins.y(),mins.z()), + t*btVector3(maxs.x(),mins.y(),mins.z()), + t*btVector3(maxs.x(),maxs.y(),mins.z()), + t*btVector3(mins.x(),maxs.y(),mins.z()), + t*btVector3(mins.x(),mins.y(),maxs.z()), + t*btVector3(maxs.x(),mins.y(),maxs.z()), + t*btVector3(maxs.x(),maxs.y(),maxs.z()), + t*btVector3(mins.x(),maxs.y(),maxs.z())}; + aabbMin=aabbMax=crns[0]; + for(int i=1;i<8;++i) + { + aabbMin.setMin(crns[i]); + aabbMax.setMax(crns[i]); + } + } + + + virtual void setLocalScaling(const btVector3& /*scaling*/) + { + ///na + } + virtual const btVector3& getLocalScaling() const + { + static const btVector3 dummy(1,1,1); + return dummy; + } + virtual void calculateLocalInertia(btScalar /*mass*/,btVector3& /*inertia*/) const + { + ///not yet + btAssert(0); + } + virtual const char* getName()const + { + return "SoftBody"; + } + +}; + +// +// btSoftClusterCollisionShape +// +class btSoftClusterCollisionShape : public btConvexInternalShape +{ +public: + const btSoftBody::Cluster* m_cluster; + + btSoftClusterCollisionShape (const btSoftBody::Cluster* cluster) : m_cluster(cluster) { setMargin(0); } + + + virtual btVector3 localGetSupportingVertex(const btVector3& vec) const + { + btSoftBody::Node* const * n=&m_cluster->m_nodes[0]; + btScalar d=btDot(vec,n[0]->m_x); + int j=0; + for(int i=1,ni=m_cluster->m_nodes.size();im_x); + if(k>d) { d=k;j=i; } + } + return(n[j]->m_x); + } + virtual btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec)const + { + return(localGetSupportingVertex(vec)); + } + //notice that the vectors should be unit length + virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const + {} + + + virtual void calculateLocalInertia(btScalar mass,btVector3& inertia) const + {} + + virtual void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const + {} + + virtual int getShapeType() const { return SOFTBODY_SHAPE_PROXYTYPE; } + + //debugging + virtual const char* getName()const {return "SOFTCLUSTER";} + + virtual void setMargin(btScalar margin) + { + btConvexInternalShape::setMargin(margin); + } + virtual btScalar getMargin() const + { + return getMargin(); + } +}; + +// +// Inline's +// + +// +template +static inline void ZeroInitialize(T& value) +{ + static const T zerodummy; + value=zerodummy; +} +// +template +static inline bool CompLess(const T& a,const T& b) +{ return(a +static inline bool CompGreater(const T& a,const T& b) +{ return(a>b); } +// +template +static inline T Lerp(const T& a,const T& b,btScalar t) +{ return(a+(b-a)*t); } +// +template +static inline T InvLerp(const T& a,const T& b,btScalar t) +{ return((b+a*t-b*t)/(a*b)); } +// +static inline btMatrix3x3 Lerp( const btMatrix3x3& a, + const btMatrix3x3& b, + btScalar t) +{ + btMatrix3x3 r; + r[0]=Lerp(a[0],b[0],t); + r[1]=Lerp(a[1],b[1],t); + r[2]=Lerp(a[2],b[2],t); + return(r); +} +// +static inline btVector3 Clamp(const btVector3& v,btScalar maxlength) +{ + const btScalar sql=v.length2(); + if(sql>(maxlength*maxlength)) + return((v*maxlength)/btSqrt(sql)); + else + return(v); +} +// +template +static inline T Clamp(const T& x,const T& l,const T& h) +{ return(xh?h:x); } +// +template +static inline T Sq(const T& x) +{ return(x*x); } +// +template +static inline T Cube(const T& x) +{ return(x*x*x); } +// +template +static inline T Sign(const T& x) +{ return((T)(x<0?-1:+1)); } +// +template +static inline bool SameSign(const T& x,const T& y) +{ return((x*y)>0); } +// +static inline btScalar ClusterMetric(const btVector3& x,const btVector3& y) +{ + const btVector3 d=x-y; + return(btFabs(d[0])+btFabs(d[1])+btFabs(d[2])); +} +// +static inline btMatrix3x3 ScaleAlongAxis(const btVector3& a,btScalar s) +{ + const btScalar xx=a.x()*a.x(); + const btScalar yy=a.y()*a.y(); + const btScalar zz=a.z()*a.z(); + const btScalar xy=a.x()*a.y(); + const btScalar yz=a.y()*a.z(); + const btScalar zx=a.z()*a.x(); + btMatrix3x3 m; + m[0]=btVector3(1-xx+xx*s,xy*s-xy,zx*s-zx); + m[1]=btVector3(xy*s-xy,1-yy+yy*s,yz*s-yz); + m[2]=btVector3(zx*s-zx,yz*s-yz,1-zz+zz*s); + return(m); +} +// +static inline btMatrix3x3 Cross(const btVector3& v) +{ + btMatrix3x3 m; + m[0]=btVector3(0,-v.z(),+v.y()); + m[1]=btVector3(+v.z(),0,-v.x()); + m[2]=btVector3(-v.y(),+v.x(),0); + return(m); +} +// +static inline btMatrix3x3 Diagonal(btScalar x) +{ + btMatrix3x3 m; + m[0]=btVector3(x,0,0); + m[1]=btVector3(0,x,0); + m[2]=btVector3(0,0,x); + return(m); +} +// +static inline btMatrix3x3 Add(const btMatrix3x3& a, + const btMatrix3x3& b) +{ + btMatrix3x3 r; + for(int i=0;i<3;++i) r[i]=a[i]+b[i]; + return(r); +} +// +static inline btMatrix3x3 Sub(const btMatrix3x3& a, + const btMatrix3x3& b) +{ + btMatrix3x3 r; + for(int i=0;i<3;++i) r[i]=a[i]-b[i]; + return(r); +} +// +static inline btMatrix3x3 Mul(const btMatrix3x3& a, + btScalar b) +{ + btMatrix3x3 r; + for(int i=0;i<3;++i) r[i]=a[i]*b; + return(r); +} +// +static inline void Orthogonalize(btMatrix3x3& m) +{ + m[2]=btCross(m[0],m[1]).normalized(); + m[1]=btCross(m[2],m[0]).normalized(); + m[0]=btCross(m[1],m[2]).normalized(); +} +// +static inline btMatrix3x3 MassMatrix(btScalar im,const btMatrix3x3& iwi,const btVector3& r) +{ + const btMatrix3x3 cr=Cross(r); + return(Sub(Diagonal(im),cr*iwi*cr)); +} + +// +static inline btMatrix3x3 ImpulseMatrix( btScalar dt, + btScalar ima, + btScalar imb, + const btMatrix3x3& iwi, + const btVector3& r) +{ + return(Diagonal(1/dt)*Add(Diagonal(ima),MassMatrix(imb,iwi,r)).inverse()); +} + +// +static inline btMatrix3x3 ImpulseMatrix( btScalar ima,const btMatrix3x3& iia,const btVector3& ra, + btScalar imb,const btMatrix3x3& iib,const btVector3& rb) +{ + return(Add(MassMatrix(ima,iia,ra),MassMatrix(imb,iib,rb)).inverse()); +} + +// +static inline btMatrix3x3 AngularImpulseMatrix( const btMatrix3x3& iia, + const btMatrix3x3& iib) +{ + return(Add(iia,iib).inverse()); +} + +// +static inline btVector3 ProjectOnAxis( const btVector3& v, + const btVector3& a) +{ + return(a*btDot(v,a)); +} +// +static inline btVector3 ProjectOnPlane( const btVector3& v, + const btVector3& a) +{ + return(v-ProjectOnAxis(v,a)); +} + +// +static inline void ProjectOrigin( const btVector3& a, + const btVector3& b, + btVector3& prj, + btScalar& sqd) +{ + const btVector3 d=b-a; + const btScalar m2=d.length2(); + if(m2>SIMD_EPSILON) + { + const btScalar t=Clamp(-btDot(a,d)/m2,0,1); + const btVector3 p=a+d*t; + const btScalar l2=p.length2(); + if(l2SIMD_EPSILON) + { + const btVector3 n=q/btSqrt(m2); + const btScalar k=btDot(a,n); + const btScalar k2=k*k; + if(k20)&& + (btDot(btCross(b-p,c-p),q)>0)&& + (btDot(btCross(c-p,a-p),q)>0)) + { + prj=p; + sqd=k2; + } + else + { + ProjectOrigin(a,b,prj,sqd); + ProjectOrigin(b,c,prj,sqd); + ProjectOrigin(c,a,prj,sqd); + } + } + } +} + +// +template +static inline T BaryEval( const T& a, + const T& b, + const T& c, + const btVector3& coord) +{ + return(a*coord.x()+b*coord.y()+c*coord.z()); +} +// +static inline btVector3 BaryCoord( const btVector3& a, + const btVector3& b, + const btVector3& c, + const btVector3& p) +{ + const btScalar w[]={ btCross(a-p,b-p).length(), + btCross(b-p,c-p).length(), + btCross(c-p,a-p).length()}; + const btScalar isum=1/(w[0]+w[1]+w[2]); + return(btVector3(w[1]*isum,w[2]*isum,w[0]*isum)); +} + +// +static btScalar ImplicitSolve( btSoftBody::ImplicitFn* fn, + const btVector3& a, + const btVector3& b, + const btScalar accuracy, + const int maxiterations=256) +{ + btScalar span[2]={0,1}; + btScalar values[2]={fn->Eval(a),fn->Eval(b)}; + if(values[0]>values[1]) + { + btSwap(span[0],span[1]); + btSwap(values[0],values[1]); + } + if(values[0]>-accuracy) return(-1); + if(values[1]<+accuracy) return(-1); + for(int i=0;iEval(Lerp(a,b,t)); + if((t<=0)||(t>=1)) break; + if(btFabs(v)SIMD_EPSILON) + return(v/l); + else + return(btVector3(0,0,0)); +} + +// +static inline btDbvtVolume VolumeOf( const btSoftBody::Face& f, + btScalar margin) +{ + const btVector3* pts[]={ &f.m_n[0]->m_x, + &f.m_n[1]->m_x, + &f.m_n[2]->m_x}; + btDbvtVolume vol=btDbvtVolume::FromPoints(pts,3); + vol.Expand(btVector3(margin,margin,margin)); + return(vol); +} + +// +static inline btVector3 CenterOf( const btSoftBody::Face& f) +{ + return((f.m_n[0]->m_x+f.m_n[1]->m_x+f.m_n[2]->m_x)/3); +} + +// +static inline btScalar AreaOf( const btVector3& x0, + const btVector3& x1, + const btVector3& x2) +{ + const btVector3 a=x1-x0; + const btVector3 b=x2-x0; + const btVector3 cr=btCross(a,b); + const btScalar area=cr.length(); + return(area); +} + +// +static inline btScalar VolumeOf( const btVector3& x0, + const btVector3& x1, + const btVector3& x2, + const btVector3& x3) +{ + const btVector3 a=x1-x0; + const btVector3 b=x2-x0; + const btVector3 c=x3-x0; + return(btDot(a,btCross(b,c))); +} + +// +static void EvaluateMedium( const btSoftBodyWorldInfo* wfi, + const btVector3& x, + btSoftBody::sMedium& medium) +{ + medium.m_velocity = btVector3(0,0,0); + medium.m_pressure = 0; + medium.m_density = wfi->air_density; + if(wfi->water_density>0) + { + const btScalar depth=-(btDot(x,wfi->water_normal)+wfi->water_offset); + if(depth>0) + { + medium.m_density = wfi->water_density; + medium.m_pressure = depth*wfi->water_density*wfi->m_gravity.length(); + } + } +} + +// +static inline void ApplyClampedForce( btSoftBody::Node& n, + const btVector3& f, + btScalar dt) +{ + const btScalar dtim=dt*n.m_im; + if((f*dtim).length2()>n.m_v.length2()) + {/* Clamp */ + n.m_f-=ProjectOnAxis(n.m_v,f.normalized())/dtim; + } + else + {/* Apply */ + n.m_f+=f; + } +} + +// +static inline int MatchEdge( const btSoftBody::Node* a, + const btSoftBody::Node* b, + const btSoftBody::Node* ma, + const btSoftBody::Node* mb) +{ + if((a==ma)&&(b==mb)) return(0); + if((a==mb)&&(b==ma)) return(1); + return(-1); +} + +// +// btEigen : Extract eigen system, +// straitforward implementation of http://math.fullerton.edu/mathews/n2003/JacobiMethodMod.html +// outputs are NOT sorted. +// +struct btEigen +{ + static int system(btMatrix3x3& a,btMatrix3x3* vectors,btVector3* values=0) + { + static const int maxiterations=16; + static const btScalar accuracy=(btScalar)0.0001; + btMatrix3x3& v=*vectors; + int iterations=0; + vectors->setIdentity(); + do { + int p=0,q=1; + if(btFabs(a[p][q])accuracy) + { + const btScalar w=(a[q][q]-a[p][p])/(2*a[p][q]); + const btScalar z=btFabs(w); + const btScalar t=w/(z*(btSqrt(1+w*w)+z)); + if(t==t)/* [WARNING] let hope that one does not get thrown aways by some compilers... */ + { + const btScalar c=1/btSqrt(t*t+1); + const btScalar s=c*t; + mulPQ(a,c,s,p,q); + mulTPQ(a,c,s,p,q); + mulPQ(v,c,s,p,q); + } else break; + } else break; + } while((++iterations)accuracy) det=ndet; else break; + } + /* Final orthogonalization */ + Orthogonalize(q); + /* Compute 'S' */ + s=q.transpose()*m; + } + else + { + q.setIdentity(); + s.setIdentity(); + } + return(i); +} + +// +// btSoftColliders +// +struct btSoftColliders +{ + // + // ClusterBase + // + struct ClusterBase : btDbvt::ICollide + { + btScalar erp; + btScalar idt; + btScalar m_margin; + btScalar friction; + btScalar threshold; + ClusterBase() + { + erp =(btScalar)1; + idt =0; + m_margin =0; + friction =0; + threshold =(btScalar)0; + } + bool SolveContact( const btGjkEpaSolver2::sResults& res, + btSoftBody::Body ba,btSoftBody::Body bb, + btSoftBody::CJoint& joint) + { + if(res.distancedata; + btSoftClusterCollisionShape cshape(cluster); + + const btConvexShape* rshape=(const btConvexShape*)m_colObj->getCollisionShape(); + + ///don't collide an anchored cluster with a static/kinematic object + if(m_colObj->isStaticOrKinematicObject() && cluster->m_containsAnchor) + return; + + btGjkEpaSolver2::sResults res; + if(btGjkEpaSolver2::SignedDistance( &cshape,btTransform::getIdentity(), + rshape,m_colObj->getInterpolationWorldTransform(), + btVector3(1,0,0),res)) + { + btSoftBody::CJoint joint; + if(SolveContact(res,cluster,m_colObj,joint))//prb,joint)) + { + btSoftBody::CJoint* pj=new(btAlignedAlloc(sizeof(btSoftBody::CJoint),16)) btSoftBody::CJoint(); + *pj=joint;psb->m_joints.push_back(pj); + if(m_colObj->isStaticOrKinematicObject()) + { + pj->m_erp *= psb->m_cfg.kSKHR_CL; + pj->m_split *= psb->m_cfg.kSK_SPLT_CL; + } + else + { + pj->m_erp *= psb->m_cfg.kSRHR_CL; + pj->m_split *= psb->m_cfg.kSR_SPLT_CL; + } + } + } + } + void Process(btSoftBody* ps,btCollisionObject* colOb) + { + psb = ps; + m_colObj = colOb; + idt = ps->m_sst.isdt; + m_margin = m_colObj->getCollisionShape()->getMargin()+psb->getCollisionShape()->getMargin(); + ///Bullet rigid body uses multiply instead of minimum to determine combined friction. Some customization would be useful. + friction = btMin(psb->m_cfg.kDF,m_colObj->getFriction()); + btVector3 mins; + btVector3 maxs; + + ATTRIBUTE_ALIGNED16(btDbvtVolume) volume; + colOb->getCollisionShape()->getAabb(colOb->getInterpolationWorldTransform(),mins,maxs); + volume=btDbvtVolume::FromMM(mins,maxs); + volume.Expand(btVector3(1,1,1)*m_margin); + ps->m_cdbvt.collideTV(ps->m_cdbvt.m_root,volume,*this); + } + }; + // + // CollideCL_SS + // + struct CollideCL_SS : ClusterBase + { + btSoftBody* bodies[2]; + void Process(const btDbvtNode* la,const btDbvtNode* lb) + { + btSoftBody::Cluster* cla=(btSoftBody::Cluster*)la->data; + btSoftBody::Cluster* clb=(btSoftBody::Cluster*)lb->data; + + + bool connected=false; + if ((bodies[0]==bodies[1])&&(bodies[0]->m_clusterConnectivity.size())) + { + connected = bodies[0]->m_clusterConnectivity[cla->m_clusterIndex+bodies[0]->m_clusters.size()*clb->m_clusterIndex]; + } + + if (!connected) + { + btSoftClusterCollisionShape csa(cla); + btSoftClusterCollisionShape csb(clb); + btGjkEpaSolver2::sResults res; + if(btGjkEpaSolver2::SignedDistance( &csa,btTransform::getIdentity(), + &csb,btTransform::getIdentity(), + cla->m_com-clb->m_com,res)) + { + btSoftBody::CJoint joint; + if(SolveContact(res,cla,clb,joint)) + { + btSoftBody::CJoint* pj=new(btAlignedAlloc(sizeof(btSoftBody::CJoint),16)) btSoftBody::CJoint(); + *pj=joint;bodies[0]->m_joints.push_back(pj); + pj->m_erp *= btMax(bodies[0]->m_cfg.kSSHR_CL,bodies[1]->m_cfg.kSSHR_CL); + pj->m_split *= (bodies[0]->m_cfg.kSS_SPLT_CL+bodies[1]->m_cfg.kSS_SPLT_CL)/2; + } + } + } else + { + static int count=0; + count++; + //printf("count=%d\n",count); + + } + } + void Process(btSoftBody* psa,btSoftBody* psb) + { + idt = psa->m_sst.isdt; + //m_margin = (psa->getCollisionShape()->getMargin()+psb->getCollisionShape()->getMargin())/2; + m_margin = (psa->getCollisionShape()->getMargin()+psb->getCollisionShape()->getMargin()); + friction = btMin(psa->m_cfg.kDF,psb->m_cfg.kDF); + bodies[0] = psa; + bodies[1] = psb; + psa->m_cdbvt.collideTT(psa->m_cdbvt.m_root,psb->m_cdbvt.m_root,*this); + } + }; + // + // CollideSDF_RS + // + struct CollideSDF_RS : btDbvt::ICollide + { + void Process(const btDbvtNode* leaf) + { + btSoftBody::Node* node=(btSoftBody::Node*)leaf->data; + DoNode(*node); + } + void DoNode(btSoftBody::Node& n) const + { + const btScalar m=n.m_im>0?dynmargin:stamargin; + btSoftBody::RContact c; + if( (!n.m_battach)&& + psb->checkContact(m_colObj1,n.m_x,m,c.m_cti)) + { + const btScalar ima=n.m_im; + const btScalar imb= m_rigidBody? m_rigidBody->getInvMass() : 0.f; + const btScalar ms=ima+imb; + if(ms>0) + { + const btTransform& wtr=m_rigidBody?m_rigidBody->getInterpolationWorldTransform() : m_colObj1->getWorldTransform(); + static const btMatrix3x3 iwiStatic(0,0,0,0,0,0,0,0,0); + const btMatrix3x3& iwi=m_rigidBody?m_rigidBody->getInvInertiaTensorWorld() : iwiStatic; + const btVector3 ra=n.m_x-wtr.getOrigin(); + const btVector3 va=m_rigidBody ? m_rigidBody->getVelocityInLocalPoint(ra)*psb->m_sst.sdt : btVector3(0,0,0); + const btVector3 vb=n.m_x-n.m_q; + const btVector3 vr=vb-va; + const btScalar dn=btDot(vr,c.m_cti.m_normal); + const btVector3 fv=vr-c.m_cti.m_normal*dn; + const btScalar fc=psb->m_cfg.kDF*m_colObj1->getFriction(); + c.m_node = &n; + c.m_c0 = ImpulseMatrix(psb->m_sst.sdt,ima,imb,iwi,ra); + c.m_c1 = ra; + c.m_c2 = ima*psb->m_sst.sdt; + c.m_c3 = fv.length2()<(btFabs(dn)*fc)?0:1-fc; + c.m_c4 = m_colObj1->isStaticOrKinematicObject()?psb->m_cfg.kKHR:psb->m_cfg.kCHR; + psb->m_rcontacts.push_back(c); + if (m_rigidBody) + m_rigidBody->activate(); + } + } + } + btSoftBody* psb; + btCollisionObject* m_colObj1; + btRigidBody* m_rigidBody; + btScalar dynmargin; + btScalar stamargin; + }; + // + // CollideVF_SS + // + struct CollideVF_SS : btDbvt::ICollide + { + void Process(const btDbvtNode* lnode, + const btDbvtNode* lface) + { + btSoftBody::Node* node=(btSoftBody::Node*)lnode->data; + btSoftBody::Face* face=(btSoftBody::Face*)lface->data; + btVector3 o=node->m_x; + btVector3 p; + btScalar d=SIMD_INFINITY; + ProjectOrigin( face->m_n[0]->m_x-o, + face->m_n[1]->m_x-o, + face->m_n[2]->m_x-o, + p,d); + const btScalar m=mrg+(o-node->m_q).length()*2; + if(d<(m*m)) + { + const btSoftBody::Node* n[]={face->m_n[0],face->m_n[1],face->m_n[2]}; + const btVector3 w=BaryCoord(n[0]->m_x,n[1]->m_x,n[2]->m_x,p+o); + const btScalar ma=node->m_im; + btScalar mb=BaryEval(n[0]->m_im,n[1]->m_im,n[2]->m_im,w); + if( (n[0]->m_im<=0)|| + (n[1]->m_im<=0)|| + (n[2]->m_im<=0)) + { + mb=0; + } + const btScalar ms=ma+mb; + if(ms>0) + { + btSoftBody::SContact c; + c.m_normal = p/-btSqrt(d); + c.m_margin = m; + c.m_node = node; + c.m_face = face; + c.m_weights = w; + c.m_friction = btMax(psb[0]->m_cfg.kDF,psb[1]->m_cfg.kDF); + c.m_cfm[0] = ma/ms*psb[0]->m_cfg.kSHR; + c.m_cfm[1] = mb/ms*psb[1]->m_cfg.kSHR; + psb[0]->m_scontacts.push_back(c); + } + } + } + btSoftBody* psb[2]; + btScalar mrg; + }; +}; + +#endif //_BT_SOFT_BODY_INTERNALS_H diff --git a/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.cpp b/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.cpp new file mode 100644 index 000000000..f5a67f6d8 --- /dev/null +++ b/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.cpp @@ -0,0 +1,134 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btSoftBodyRigidBodyCollisionConfiguration.h" +#include "btSoftRigidCollisionAlgorithm.h" +#include "btSoftBodyConcaveCollisionAlgorithm.h" +#include "btSoftSoftCollisionAlgorithm.h" + +#include "LinearMath/btPoolAllocator.h" + +#define ENABLE_SOFTBODY_CONCAVE_COLLISIONS 1 + +btSoftBodyRigidBodyCollisionConfiguration::btSoftBodyRigidBodyCollisionConfiguration(const btDefaultCollisionConstructionInfo& constructionInfo) +:btDefaultCollisionConfiguration(constructionInfo) +{ + void* mem; + + mem = btAlignedAlloc(sizeof(btSoftSoftCollisionAlgorithm::CreateFunc),16); + m_softSoftCreateFunc = new(mem) btSoftSoftCollisionAlgorithm::CreateFunc; + + mem = btAlignedAlloc(sizeof(btSoftRigidCollisionAlgorithm::CreateFunc),16); + m_softRigidConvexCreateFunc = new(mem) btSoftRigidCollisionAlgorithm::CreateFunc; + + mem = btAlignedAlloc(sizeof(btSoftRigidCollisionAlgorithm::CreateFunc),16); + m_swappedSoftRigidConvexCreateFunc = new(mem) btSoftRigidCollisionAlgorithm::CreateFunc; + m_swappedSoftRigidConvexCreateFunc->m_swapped=true; + +#ifdef ENABLE_SOFTBODY_CONCAVE_COLLISIONS + mem = btAlignedAlloc(sizeof(btSoftBodyConcaveCollisionAlgorithm::CreateFunc),16); + m_softRigidConcaveCreateFunc = new(mem) btSoftBodyConcaveCollisionAlgorithm::CreateFunc; + + mem = btAlignedAlloc(sizeof(btSoftBodyConcaveCollisionAlgorithm::CreateFunc),16); + m_swappedSoftRigidConcaveCreateFunc = new(mem) btSoftBodyConcaveCollisionAlgorithm::SwappedCreateFunc; + m_swappedSoftRigidConcaveCreateFunc->m_swapped=true; +#endif + + //replace pool by a new one, with potential larger size + + if (m_ownsCollisionAlgorithmPool && m_collisionAlgorithmPool) + { + int curElemSize = m_collisionAlgorithmPool->getElementSize(); + ///calculate maximum element size, big enough to fit any collision algorithm in the memory pool + + + int maxSize0 = sizeof(btSoftSoftCollisionAlgorithm); + int maxSize1 = sizeof(btSoftRigidCollisionAlgorithm); + int maxSize2 = sizeof(btSoftBodyConcaveCollisionAlgorithm); + + int collisionAlgorithmMaxElementSize = btMax(maxSize0,maxSize1); + collisionAlgorithmMaxElementSize = btMax(collisionAlgorithmMaxElementSize,maxSize2); + + if (collisionAlgorithmMaxElementSize > curElemSize) + { + m_collisionAlgorithmPool->~btPoolAllocator(); + btAlignedFree(m_collisionAlgorithmPool); + void* mem = btAlignedAlloc(sizeof(btPoolAllocator),16); + m_collisionAlgorithmPool = new(mem) btPoolAllocator(collisionAlgorithmMaxElementSize,constructionInfo.m_defaultMaxCollisionAlgorithmPoolSize); + } + } + +} + +btSoftBodyRigidBodyCollisionConfiguration::~btSoftBodyRigidBodyCollisionConfiguration() +{ + m_softSoftCreateFunc->~btCollisionAlgorithmCreateFunc(); + btAlignedFree( m_softSoftCreateFunc); + + m_softRigidConvexCreateFunc->~btCollisionAlgorithmCreateFunc(); + btAlignedFree( m_softRigidConvexCreateFunc); + + m_swappedSoftRigidConvexCreateFunc->~btCollisionAlgorithmCreateFunc(); + btAlignedFree( m_swappedSoftRigidConvexCreateFunc); + +#ifdef ENABLE_SOFTBODY_CONCAVE_COLLISIONS + m_softRigidConcaveCreateFunc->~btCollisionAlgorithmCreateFunc(); + btAlignedFree( m_softRigidConcaveCreateFunc); + + m_swappedSoftRigidConcaveCreateFunc->~btCollisionAlgorithmCreateFunc(); + btAlignedFree( m_swappedSoftRigidConcaveCreateFunc); +#endif +} + +///creation of soft-soft and soft-rigid, and otherwise fallback to base class implementation +btCollisionAlgorithmCreateFunc* btSoftBodyRigidBodyCollisionConfiguration::getCollisionAlgorithmCreateFunc(int proxyType0,int proxyType1) +{ + + ///try to handle the softbody interactions first + + if ((proxyType0 == SOFTBODY_SHAPE_PROXYTYPE ) && (proxyType1==SOFTBODY_SHAPE_PROXYTYPE)) + { + return m_softSoftCreateFunc; + } + + ///softbody versus convex + if (proxyType0 == SOFTBODY_SHAPE_PROXYTYPE && btBroadphaseProxy::isConvex(proxyType1)) + { + return m_softRigidConvexCreateFunc; + } + + ///convex versus soft body + if (btBroadphaseProxy::isConvex(proxyType0) && proxyType1 == SOFTBODY_SHAPE_PROXYTYPE ) + { + return m_swappedSoftRigidConvexCreateFunc; + } + +#ifdef ENABLE_SOFTBODY_CONCAVE_COLLISIONS + ///softbody versus convex + if (proxyType0 == SOFTBODY_SHAPE_PROXYTYPE && btBroadphaseProxy::isConcave(proxyType1)) + { + return m_softRigidConcaveCreateFunc; + } + + ///convex versus soft body + if (btBroadphaseProxy::isConcave(proxyType0) && proxyType1 == SOFTBODY_SHAPE_PROXYTYPE ) + { + return m_swappedSoftRigidConcaveCreateFunc; + } +#endif + + ///fallback to the regular rigid collision shape + return btDefaultCollisionConfiguration::getCollisionAlgorithmCreateFunc(proxyType0,proxyType1); +} diff --git a/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.h b/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.h new file mode 100644 index 000000000..21addcfe2 --- /dev/null +++ b/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.h @@ -0,0 +1,48 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 BT_SOFTBODY_RIGIDBODY_COLLISION_CONFIGURATION +#define BT_SOFTBODY_RIGIDBODY_COLLISION_CONFIGURATION + +#include "BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h" + +class btVoronoiSimplexSolver; +class btGjkEpaPenetrationDepthSolver; + + +///btSoftBodyRigidBodyCollisionConfiguration add softbody interaction on top of btDefaultCollisionConfiguration +class btSoftBodyRigidBodyCollisionConfiguration : public btDefaultCollisionConfiguration +{ + + //default CreationFunctions, filling the m_doubleDispatch table + btCollisionAlgorithmCreateFunc* m_softSoftCreateFunc; + btCollisionAlgorithmCreateFunc* m_softRigidConvexCreateFunc; + btCollisionAlgorithmCreateFunc* m_swappedSoftRigidConvexCreateFunc; + btCollisionAlgorithmCreateFunc* m_softRigidConcaveCreateFunc; + btCollisionAlgorithmCreateFunc* m_swappedSoftRigidConcaveCreateFunc; + +public: + + btSoftBodyRigidBodyCollisionConfiguration(const btDefaultCollisionConstructionInfo& constructionInfo = btDefaultCollisionConstructionInfo()); + + virtual ~btSoftBodyRigidBodyCollisionConfiguration(); + + ///creation of soft-soft and soft-rigid, and otherwise fallback to base class implementation + virtual btCollisionAlgorithmCreateFunc* getCollisionAlgorithmCreateFunc(int proxyType0,int proxyType1); + +}; + +#endif //BT_SOFTBODY_RIGIDBODY_COLLISION_CONFIGURATION + diff --git a/Engine/lib/bullet/src/BulletSoftBody/btSoftRigidCollisionAlgorithm.cpp b/Engine/lib/bullet/src/BulletSoftBody/btSoftRigidCollisionAlgorithm.cpp new file mode 100644 index 000000000..11ad9e7da --- /dev/null +++ b/Engine/lib/bullet/src/BulletSoftBody/btSoftRigidCollisionAlgorithm.cpp @@ -0,0 +1,82 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btSoftRigidCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/CollisionShapes/btSphereShape.h" +#include "BulletCollision/CollisionShapes/btBoxShape.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "btSoftBody.h" +///TODO: include all the shapes that the softbody can collide with +///alternatively, implement special case collision algorithms (just like for rigid collision shapes) + +//#include + +btSoftRigidCollisionAlgorithm::btSoftRigidCollisionAlgorithm(btPersistentManifold* /*mf*/,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* /*col0*/,btCollisionObject* /*col1*/, bool isSwapped) +: btCollisionAlgorithm(ci), +//m_ownManifold(false), +//m_manifoldPtr(mf), +m_isSwapped(isSwapped) +{ +} + + +btSoftRigidCollisionAlgorithm::~btSoftRigidCollisionAlgorithm() +{ + + //m_softBody->m_overlappingRigidBodies.remove(m_rigidCollisionObject); + + /*if (m_ownManifold) + { + if (m_manifoldPtr) + m_dispatcher->releaseManifold(m_manifoldPtr); + } + */ + +} + + +#include + +void btSoftRigidCollisionAlgorithm::processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + (void)dispatchInfo; + (void)resultOut; + //printf("btSoftRigidCollisionAlgorithm\n"); + + btSoftBody* softBody = m_isSwapped? (btSoftBody*)body1 : (btSoftBody*)body0; + btCollisionObject* rigidCollisionObject = m_isSwapped? body0 : body1; + + if (softBody->m_collisionDisabledObjects.findLinearSearch(rigidCollisionObject)==softBody->m_collisionDisabledObjects.size()) + { + softBody->defaultCollisionHandler(rigidCollisionObject); + } + + +} + +btScalar btSoftRigidCollisionAlgorithm::calculateTimeOfImpact(btCollisionObject* col0,btCollisionObject* col1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + (void)resultOut; + (void)dispatchInfo; + (void)col0; + (void)col1; + + //not yet + return btScalar(1.); +} + + + diff --git a/Engine/lib/bullet/src/BulletSoftBody/btSoftRigidCollisionAlgorithm.h b/Engine/lib/bullet/src/BulletSoftBody/btSoftRigidCollisionAlgorithm.h new file mode 100644 index 000000000..adc3844e3 --- /dev/null +++ b/Engine/lib/bullet/src/BulletSoftBody/btSoftRigidCollisionAlgorithm.h @@ -0,0 +1,75 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 SOFT_RIGID_COLLISION_ALGORITHM_H +#define SOFT_RIGID_COLLISION_ALGORITHM_H + +#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "BulletCollision/CollisionDispatch/btCollisionCreateFunc.h" +class btPersistentManifold; +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" + +#include "LinearMath/btVector3.h" +class btSoftBody; + +/// btSoftRigidCollisionAlgorithm provides collision detection between btSoftBody and btRigidBody +class btSoftRigidCollisionAlgorithm : public btCollisionAlgorithm +{ + // bool m_ownManifold; + // btPersistentManifold* m_manifoldPtr; + + btSoftBody* m_softBody; + btCollisionObject* m_rigidCollisionObject; + + ///for rigid versus soft (instead of soft versus rigid), we use this swapped boolean + bool m_isSwapped; + +public: + + btSoftRigidCollisionAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* col0,btCollisionObject* col1, bool isSwapped); + + virtual ~btSoftRigidCollisionAlgorithm(); + + virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual void getAllContactManifolds(btManifoldArray& manifoldArray) + { + //we don't add any manifolds + } + + + struct CreateFunc :public btCollisionAlgorithmCreateFunc + { + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + void* mem = ci.m_dispatcher1->allocateCollisionAlgorithm(sizeof(btSoftRigidCollisionAlgorithm)); + if (!m_swapped) + { + return new(mem) btSoftRigidCollisionAlgorithm(0,ci,body0,body1,false); + } else + { + return new(mem) btSoftRigidCollisionAlgorithm(0,ci,body0,body1,true); + } + } + }; + +}; + +#endif //SOFT_RIGID_COLLISION_ALGORITHM_H + + diff --git a/Engine/lib/bullet/src/BulletSoftBody/btSoftRigidDynamicsWorld.cpp b/Engine/lib/bullet/src/BulletSoftBody/btSoftRigidDynamicsWorld.cpp new file mode 100644 index 000000000..8f3656829 --- /dev/null +++ b/Engine/lib/bullet/src/BulletSoftBody/btSoftRigidDynamicsWorld.cpp @@ -0,0 +1,151 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btSoftRigidDynamicsWorld.h" +#include "LinearMath/btQuickprof.h" + +//softbody & helpers +#include "btSoftBody.h" +#include "btSoftBodyHelpers.h" + + + + + +btSoftRigidDynamicsWorld::btSoftRigidDynamicsWorld(btDispatcher* dispatcher,btBroadphaseInterface* pairCache,btConstraintSolver* constraintSolver,btCollisionConfiguration* collisionConfiguration) +:btDiscreteDynamicsWorld(dispatcher,pairCache,constraintSolver,collisionConfiguration) +{ + m_drawFlags = fDrawFlags::Std; + m_drawNodeTree = true; + m_drawFaceTree = false; + m_drawClusterTree = false; + m_sbi.m_broadphase = pairCache; + m_sbi.m_dispatcher = dispatcher; + m_sbi.m_sparsesdf.Initialize(); + m_sbi.m_sparsesdf.Reset(); + +} + +btSoftRigidDynamicsWorld::~btSoftRigidDynamicsWorld() +{ + +} + +void btSoftRigidDynamicsWorld::predictUnconstraintMotion(btScalar timeStep) +{ + btDiscreteDynamicsWorld::predictUnconstraintMotion( timeStep); + + for ( int i=0;ipredictMotion(timeStep); + } +} + +void btSoftRigidDynamicsWorld::internalSingleStepSimulation( btScalar timeStep) +{ + btDiscreteDynamicsWorld::internalSingleStepSimulation( timeStep ); + + ///solve soft bodies constraints + solveSoftBodiesConstraints(); + + //self collisions + for ( int i=0;idefaultCollisionHandler(psb); + } + + ///update soft bodies + updateSoftBodies(); + +} + +void btSoftRigidDynamicsWorld::updateSoftBodies() +{ + BT_PROFILE("updateSoftBodies"); + + for ( int i=0;iintegrateMotion(); + } +} + +void btSoftRigidDynamicsWorld::solveSoftBodiesConstraints() +{ + BT_PROFILE("solveSoftConstraints"); + + if(m_softBodies.size()) + { + btSoftBody::solveClusters(m_softBodies); + } + + for(int i=0;isolveConstraints(); + } +} + +void btSoftRigidDynamicsWorld::addSoftBody(btSoftBody* body,short int collisionFilterGroup,short int collisionFilterMask) +{ + m_softBodies.push_back(body); + + btCollisionWorld::addCollisionObject(body, + collisionFilterGroup, + collisionFilterMask); + +} + +void btSoftRigidDynamicsWorld::removeSoftBody(btSoftBody* body) +{ + m_softBodies.remove(body); + + btCollisionWorld::removeCollisionObject(body); +} + +void btSoftRigidDynamicsWorld::removeCollisionObject(btCollisionObject* collisionObject) +{ + btSoftBody* body = btSoftBody::upcast(collisionObject); + if (body) + removeSoftBody(body); + else + btDiscreteDynamicsWorld::removeCollisionObject(collisionObject); +} + +void btSoftRigidDynamicsWorld::debugDrawWorld() +{ + btDiscreteDynamicsWorld::debugDrawWorld(); + + if (getDebugDrawer()) + { + int i; + for ( i=0;im_softBodies.size();i++) + { + btSoftBody* psb=(btSoftBody*)this->m_softBodies[i]; + btSoftBodyHelpers::DrawFrame(psb,m_debugDrawer); + btSoftBodyHelpers::Draw(psb,m_debugDrawer,m_drawFlags); + if (m_debugDrawer && (m_debugDrawer->getDebugMode() & btIDebugDraw::DBG_DrawAabb)) + { + if(m_drawNodeTree) btSoftBodyHelpers::DrawNodeTree(psb,m_debugDrawer); + if(m_drawFaceTree) btSoftBodyHelpers::DrawFaceTree(psb,m_debugDrawer); + if(m_drawClusterTree) btSoftBodyHelpers::DrawClusterTree(psb,m_debugDrawer); + } + } + } +} diff --git a/Engine/lib/bullet/src/BulletSoftBody/btSoftRigidDynamicsWorld.h b/Engine/lib/bullet/src/BulletSoftBody/btSoftRigidDynamicsWorld.h new file mode 100644 index 000000000..6b7ed5dea --- /dev/null +++ b/Engine/lib/bullet/src/BulletSoftBody/btSoftRigidDynamicsWorld.h @@ -0,0 +1,85 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 BT_SOFT_RIGID_DYNAMICS_WORLD_H +#define BT_SOFT_RIGID_DYNAMICS_WORLD_H + +#include "BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h" +#include "btSoftBody.h" + +typedef btAlignedObjectArray btSoftBodyArray; + +class btSoftRigidDynamicsWorld : public btDiscreteDynamicsWorld +{ + + btSoftBodyArray m_softBodies; + int m_drawFlags; + bool m_drawNodeTree; + bool m_drawFaceTree; + bool m_drawClusterTree; + btSoftBodyWorldInfo m_sbi; + +protected: + + virtual void predictUnconstraintMotion(btScalar timeStep); + + virtual void internalSingleStepSimulation( btScalar timeStep); + + void updateSoftBodies(); + + void solveSoftBodiesConstraints(); + + +public: + + btSoftRigidDynamicsWorld(btDispatcher* dispatcher,btBroadphaseInterface* pairCache,btConstraintSolver* constraintSolver,btCollisionConfiguration* collisionConfiguration); + + virtual ~btSoftRigidDynamicsWorld(); + + virtual void debugDrawWorld(); + + void addSoftBody(btSoftBody* body,short int collisionFilterGroup=btBroadphaseProxy::DefaultFilter,short int collisionFilterMask=btBroadphaseProxy::AllFilter); + + void removeSoftBody(btSoftBody* body); + + ///removeCollisionObject will first check if it is a rigid body, if so call removeRigidBody otherwise call btDiscreteDynamicsWorld::removeCollisionObject + virtual void removeCollisionObject(btCollisionObject* collisionObject); + + int getDrawFlags() const { return(m_drawFlags); } + void setDrawFlags(int f) { m_drawFlags=f; } + + btSoftBodyWorldInfo& getWorldInfo() + { + return m_sbi; + } + const btSoftBodyWorldInfo& getWorldInfo() const + { + return m_sbi; + } + + + btSoftBodyArray& getSoftBodyArray() + { + return m_softBodies; + } + + const btSoftBodyArray& getSoftBodyArray() const + { + return m_softBodies; + } + +}; + +#endif //BT_SOFT_RIGID_DYNAMICS_WORLD_H diff --git a/Engine/lib/bullet/src/BulletSoftBody/btSoftSoftCollisionAlgorithm.cpp b/Engine/lib/bullet/src/BulletSoftBody/btSoftSoftCollisionAlgorithm.cpp new file mode 100644 index 000000000..85a727944 --- /dev/null +++ b/Engine/lib/bullet/src/BulletSoftBody/btSoftSoftCollisionAlgorithm.cpp @@ -0,0 +1,46 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btSoftSoftCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/CollisionShapes/btBoxShape.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "btSoftBody.h" + +#define USE_PERSISTENT_CONTACTS 1 + +btSoftSoftCollisionAlgorithm::btSoftSoftCollisionAlgorithm(btPersistentManifold* /*mf*/,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* /*obj0*/,btCollisionObject* /*obj1*/) +: btCollisionAlgorithm(ci) +//m_ownManifold(false), +//m_manifoldPtr(mf) +{ +} + +btSoftSoftCollisionAlgorithm::~btSoftSoftCollisionAlgorithm() +{ +} + +void btSoftSoftCollisionAlgorithm::processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& /*dispatchInfo*/,btManifoldResult* /*resultOut*/) +{ + btSoftBody* soft0 = (btSoftBody*)body0; + btSoftBody* soft1 = (btSoftBody*)body1; + soft0->defaultCollisionHandler(soft1); +} + +btScalar btSoftSoftCollisionAlgorithm::calculateTimeOfImpact(btCollisionObject* /*body0*/,btCollisionObject* /*body1*/,const btDispatcherInfo& /*dispatchInfo*/,btManifoldResult* /*resultOut*/) +{ + //not yet + return 1.f; +} diff --git a/Engine/lib/bullet/src/BulletSoftBody/btSoftSoftCollisionAlgorithm.h b/Engine/lib/bullet/src/BulletSoftBody/btSoftSoftCollisionAlgorithm.h new file mode 100644 index 000000000..1b34e0af6 --- /dev/null +++ b/Engine/lib/bullet/src/BulletSoftBody/btSoftSoftCollisionAlgorithm.h @@ -0,0 +1,69 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 SOFT_SOFT_COLLISION_ALGORITHM_H +#define SOFT_SOFT_COLLISION_ALGORITHM_H + +#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "BulletCollision/BroadphaseCollision/btDispatcher.h" +#include "BulletCollision/CollisionDispatch/btCollisionCreateFunc.h" + +class btPersistentManifold; +class btSoftBody; + +///collision detection between two btSoftBody shapes +class btSoftSoftCollisionAlgorithm : public btCollisionAlgorithm +{ + bool m_ownManifold; + btPersistentManifold* m_manifoldPtr; + + btSoftBody* m_softBody0; + btSoftBody* m_softBody1; + + +public: + btSoftSoftCollisionAlgorithm(const btCollisionAlgorithmConstructionInfo& ci) + : btCollisionAlgorithm(ci) {} + + virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual void getAllContactManifolds(btManifoldArray& manifoldArray) + { + if (m_manifoldPtr && m_ownManifold) + manifoldArray.push_back(m_manifoldPtr); + } + + btSoftSoftCollisionAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* body0,btCollisionObject* body1); + + virtual ~btSoftSoftCollisionAlgorithm(); + + struct CreateFunc :public btCollisionAlgorithmCreateFunc + { + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1) + { + int bbsize = sizeof(btSoftSoftCollisionAlgorithm); + void* ptr = ci.m_dispatcher1->allocateCollisionAlgorithm(bbsize); + return new(ptr) btSoftSoftCollisionAlgorithm(0,ci,body0,body1); + } + }; + +}; + +#endif //SOFT_SOFT_COLLISION_ALGORITHM_H + + diff --git a/Engine/lib/bullet/src/BulletSoftBody/btSparseSDF.h b/Engine/lib/bullet/src/BulletSoftBody/btSparseSDF.h new file mode 100644 index 000000000..cc4266732 --- /dev/null +++ b/Engine/lib/bullet/src/BulletSoftBody/btSparseSDF.h @@ -0,0 +1,306 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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. +*/ +///btSparseSdf implementation by Nathanael Presson + +#ifndef _14F9D17F_EAE8_4aba_B41C_292DB2AA70F3_ +#define _14F9D17F_EAE8_4aba_B41C_292DB2AA70F3_ + +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "BulletCollision/NarrowPhaseCollision/btGjkEpa2.h" + +// Modified Paul Hsieh hash +template +unsigned int HsiehHash(const void* pdata) +{ + const unsigned short* data=(const unsigned short*)pdata; + unsigned hash=DWORDLEN<<2,tmp; + for(int i=0;i>11; + } + hash^=hash<<3;hash+=hash>>5; + hash^=hash<<4;hash+=hash>>17; + hash^=hash<<25;hash+=hash>>6; + return(hash); +} + +template +struct btSparseSdf +{ + // + // Inner types + // + struct IntFrac + { + int b; + int i; + btScalar f; + }; + struct Cell + { + btScalar d[CELLSIZE+1][CELLSIZE+1][CELLSIZE+1]; + int c[3]; + int puid; + unsigned hash; + btCollisionShape* pclient; + Cell* next; + }; + // + // Fields + // + + btAlignedObjectArray cells; + btScalar voxelsz; + int puid; + int ncells; + int nprobes; + int nqueries; + + // + // Methods + // + + // + void Initialize(int hashsize=2383) + { + cells.resize(hashsize,0); + Reset(); + } + // + void Reset() + { + for(int i=0,ni=cells.size();inext; + delete pc; + pc=pn; + } + } + voxelsz =0.25; + puid =0; + ncells =0; + nprobes =1; + nqueries =1; + } + // + void GarbageCollect(int lifetime=256) + { + const int life=puid-lifetime; + for(int i=0;inext; + if(pc->puidnext=pn; else root=pn; + delete pc;pc=pp;--ncells; + } + pp=pc;pc=pn; + } + } + //printf("GC[%d]: %d cells, PpQ: %f\r\n",puid,ncells,nprobes/(btScalar)nqueries); + nqueries=1; + nprobes=1; + ++puid; ///@todo: Reset puid's when int range limit is reached */ + /* else setup a priority list... */ + } + // + int RemoveReferences(btCollisionShape* pcs) + { + int refcount=0; + for(int i=0;inext; + if(pc->pclient==pcs) + { + if(pp) pp->next=pn; else root=pn; + delete pc;pc=pp;++refcount; + } + pp=pc;pc=pn; + } + } + return(refcount); + } + // + btScalar Evaluate( const btVector3& x, + btCollisionShape* shape, + btVector3& normal, + btScalar margin) + { + /* Lookup cell */ + const btVector3 scx=x/voxelsz; + const IntFrac ix=Decompose(scx.x()); + const IntFrac iy=Decompose(scx.y()); + const IntFrac iz=Decompose(scx.z()); + const unsigned h=Hash(ix.b,iy.b,iz.b,shape); + Cell*& root=cells[static_cast(h%cells.size())]; + Cell* c=root; + ++nqueries; + while(c) + { + ++nprobes; + if( (c->hash==h) && + (c->c[0]==ix.b) && + (c->c[1]==iy.b) && + (c->c[2]==iz.b) && + (c->pclient==shape)) + { break; } + else + { c=c->next; } + } + if(!c) + { + ++nprobes; + ++ncells; + c=new Cell(); + c->next=root;root=c; + c->pclient=shape; + c->hash=h; + c->c[0]=ix.b;c->c[1]=iy.b;c->c[2]=iz.b; + BuildCell(*c); + } + c->puid=puid; + /* Extract infos */ + const int o[]={ ix.i,iy.i,iz.i}; + const btScalar d[]={ c->d[o[0]+0][o[1]+0][o[2]+0], + c->d[o[0]+1][o[1]+0][o[2]+0], + c->d[o[0]+1][o[1]+1][o[2]+0], + c->d[o[0]+0][o[1]+1][o[2]+0], + c->d[o[0]+0][o[1]+0][o[2]+1], + c->d[o[0]+1][o[1]+0][o[2]+1], + c->d[o[0]+1][o[1]+1][o[2]+1], + c->d[o[0]+0][o[1]+1][o[2]+1]}; + /* Normal */ +#if 1 + const btScalar gx[]={ d[1]-d[0],d[2]-d[3], + d[5]-d[4],d[6]-d[7]}; + const btScalar gy[]={ d[3]-d[0],d[2]-d[1], + d[7]-d[4],d[6]-d[5]}; + const btScalar gz[]={ d[4]-d[0],d[5]-d[1], + d[7]-d[3],d[6]-d[2]}; + normal.setX(Lerp( Lerp(gx[0],gx[1],iy.f), + Lerp(gx[2],gx[3],iy.f),iz.f)); + normal.setY(Lerp( Lerp(gy[0],gy[1],ix.f), + Lerp(gy[2],gy[3],ix.f),iz.f)); + normal.setZ(Lerp( Lerp(gz[0],gz[1],ix.f), + Lerp(gz[2],gz[3],ix.f),iy.f)); + normal = normal.normalized(); +#else + normal = btVector3(d[1]-d[0],d[3]-d[0],d[4]-d[0]).normalized(); +#endif + /* Distance */ + const btScalar d0=Lerp(Lerp(d[0],d[1],ix.f), + Lerp(d[3],d[2],ix.f),iy.f); + const btScalar d1=Lerp(Lerp(d[4],d[5],ix.f), + Lerp(d[7],d[6],ix.f),iy.f); + return(Lerp(d0,d1,iz.f)-margin); + } + // + void BuildCell(Cell& c) + { + const btVector3 org=btVector3( (btScalar)c.c[0], + (btScalar)c.c[1], + (btScalar)c.c[2]) * + CELLSIZE*voxelsz; + for(int k=0;k<=CELLSIZE;++k) + { + const btScalar z=voxelsz*k+org.z(); + for(int j=0;j<=CELLSIZE;++j) + { + const btScalar y=voxelsz*j+org.y(); + for(int i=0;i<=CELLSIZE;++i) + { + const btScalar x=voxelsz*i+org.x(); + c.d[i][j][k]=DistanceToShape( btVector3(x,y,z), + c.pclient); + } + } + } + } + // + static inline btScalar DistanceToShape(const btVector3& x, + btCollisionShape* shape) + { + btTransform unit; + unit.setIdentity(); + if(shape->isConvex()) + { + btGjkEpaSolver2::sResults res; + btConvexShape* csh=static_cast(shape); + return(btGjkEpaSolver2::SignedDistance(x,0,csh,unit,res)); + } + return(0); + } + // + static inline IntFrac Decompose(btScalar x) + { + /* That one need a lot of improvements... */ + /* Remove test, faster floor... */ + IntFrac r; + x/=CELLSIZE; + const int o=x<0?(int)(-x+1):0; + x+=o;r.b=(int)x; + const btScalar k=(x-r.b)*CELLSIZE; + r.i=(int)k;r.f=k-r.i;r.b-=o; + return(r); + } + // + static inline btScalar Lerp(btScalar a,btScalar b,btScalar t) + { + return(a+(b-a)*t); + } + + + + // + static inline unsigned int Hash(int x,int y,int z,btCollisionShape* shape) + { + struct btS + { + int x,y,z; + void* p; + }; + + btS myset; + + myset.x=x;myset.y=y;myset.z=z;myset.p=shape; + const void* ptr = &myset; + + unsigned int result = HsiehHash (ptr); + + + return result; + } +}; + + +#endif diff --git a/Engine/lib/bullet/src/CMakeLists.txt b/Engine/lib/bullet/src/CMakeLists.txt new file mode 100644 index 000000000..d901bb341 --- /dev/null +++ b/Engine/lib/bullet/src/CMakeLists.txt @@ -0,0 +1,15 @@ +if (CMAKE_SIZEOF_VOID_P MATCHES "8") +SUBDIRS( BulletSoftBody BulletCollision BulletDynamics LinearMath ) +else (CMAKE_SIZEOF_VOID_P MATCHES "8") +SUBDIRS( BulletMultiThreaded BulletSoftBody BulletCollision BulletDynamics LinearMath ) +endif (CMAKE_SIZEOF_VOID_P MATCHES "8") + +#INSTALL of other files requires CMake 2.6 +IF (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 2.5) + IF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) + # Don't actually need to install any common files, the frameworks include everything + ELSE (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) + INSTALL(FILES btBulletCollisionCommon.h btBulletDynamicsCommon.h Bullet-C-Api.h DESTINATION include) + ENDIF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) +ENDIF (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 2.5) + diff --git a/Engine/lib/bullet/src/Jamfile b/Engine/lib/bullet/src/Jamfile new file mode 100644 index 000000000..5689366b2 --- /dev/null +++ b/Engine/lib/bullet/src/Jamfile @@ -0,0 +1,7 @@ +SubDir TOP src ; +SubInclude TOP src BulletMultiThreaded ; +SubInclude TOP src BulletSoftBody ; +SubInclude TOP src BulletCollision ; +SubInclude TOP src BulletDynamics ; +SubInclude TOP src LinearMath ; +Recurse InstallHeader : .h ; diff --git a/Engine/lib/bullet/src/LinearMath/CMakeLists.txt b/Engine/lib/bullet/src/LinearMath/CMakeLists.txt new file mode 100644 index 000000000..e93add4dd --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/CMakeLists.txt @@ -0,0 +1,55 @@ + +INCLUDE_DIRECTORIES( + ${BULLET_PHYSICS_SOURCE_DIR}/src } +) + +SET(LinearMath_SRCS + btAlignedAllocator.cpp + btConvexHull.cpp + btGeometryUtil.cpp + btQuickprof.cpp +) + +SET(LinearMath_HDRS + btAabbUtil2.h + btAlignedAllocator.h + btAlignedObjectArray.h + btConvexHull.h + btDefaultMotionState.h + btGeometryUtil.h + btHashMap.h + btIDebugDraw.h + btList.h + btMatrix3x3.h + btMinMax.h + btMotionState.h + btPoolAllocator.h + btQuadWord.h + btQuaternion.h + btQuickprof.h + btRandom.h + btScalar.h + btStackAlloc.h + btTransform.h + btTransformUtil.h + btVector3.h +) + +ADD_LIBRARY(LinearMath ${LinearMath_SRCS} ${LinearMath_HDRS}) +SET_TARGET_PROPERTIES(LinearMath PROPERTIES VERSION ${BULLET_VERSION}) +SET_TARGET_PROPERTIES(LinearMath PROPERTIES SOVERSION ${BULLET_VERSION}) + +#FILES_MATCHING requires CMake 2.6 +IF (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 2.5) + IF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) + INSTALL(TARGETS LinearMath DESTINATION .) + ELSE (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) + INSTALL(TARGETS LinearMath DESTINATION lib) + INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DESTINATION include FILES_MATCHING PATTERN "*.h") + ENDIF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) +ENDIF (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 2.5) + +IF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) + SET_TARGET_PROPERTIES(LinearMath PROPERTIES FRAMEWORK true) + SET_TARGET_PROPERTIES(LinearMath PROPERTIES PUBLIC_HEADER "${LinearMath_HDRS}") +ENDIF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) diff --git a/Engine/lib/bullet/src/LinearMath/Jamfile b/Engine/lib/bullet/src/LinearMath/Jamfile new file mode 100644 index 000000000..469784104 --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/Jamfile @@ -0,0 +1,10 @@ + +SubDir TOP src LinearMath ; + +Description bulletmath : "Bullet Math Library" ; + +Library bulletmath : +[ Wildcard *.h *.cpp ] + ; + +#InstallHeader [ Wildcard *.h ] ; diff --git a/Engine/lib/bullet/src/LinearMath/btAabbUtil2.h b/Engine/lib/bullet/src/LinearMath/btAabbUtil2.h new file mode 100644 index 000000000..532ce1bf6 --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/btAabbUtil2.h @@ -0,0 +1,236 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / Erwin Coumans http://continuousphysics.com/Bullet/ + +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 AABB_UTIL2 +#define AABB_UTIL2 + +#include "btTransform.h" +#include "btVector3.h" +#include "btMinMax.h" + + + +SIMD_FORCE_INLINE void AabbExpand (btVector3& aabbMin, + btVector3& aabbMax, + const btVector3& expansionMin, + const btVector3& expansionMax) +{ + aabbMin = aabbMin + expansionMin; + aabbMax = aabbMax + expansionMax; +} + +/// conservative test for overlap between two aabbs +SIMD_FORCE_INLINE bool TestPointAgainstAabb2(const btVector3 &aabbMin1, const btVector3 &aabbMax1, + const btVector3 &point) +{ + bool overlap = true; + overlap = (aabbMin1.getX() > point.getX() || aabbMax1.getX() < point.getX()) ? false : overlap; + overlap = (aabbMin1.getZ() > point.getZ() || aabbMax1.getZ() < point.getZ()) ? false : overlap; + overlap = (aabbMin1.getY() > point.getY() || aabbMax1.getY() < point.getY()) ? false : overlap; + return overlap; +} + + +/// conservative test for overlap between two aabbs +SIMD_FORCE_INLINE bool TestAabbAgainstAabb2(const btVector3 &aabbMin1, const btVector3 &aabbMax1, + const btVector3 &aabbMin2, const btVector3 &aabbMax2) +{ + bool overlap = true; + overlap = (aabbMin1.getX() > aabbMax2.getX() || aabbMax1.getX() < aabbMin2.getX()) ? false : overlap; + overlap = (aabbMin1.getZ() > aabbMax2.getZ() || aabbMax1.getZ() < aabbMin2.getZ()) ? false : overlap; + overlap = (aabbMin1.getY() > aabbMax2.getY() || aabbMax1.getY() < aabbMin2.getY()) ? false : overlap; + return overlap; +} + +/// conservative test for overlap between triangle and aabb +SIMD_FORCE_INLINE bool TestTriangleAgainstAabb2(const btVector3 *vertices, + const btVector3 &aabbMin, const btVector3 &aabbMax) +{ + const btVector3 &p1 = vertices[0]; + const btVector3 &p2 = vertices[1]; + const btVector3 &p3 = vertices[2]; + + if (btMin(btMin(p1[0], p2[0]), p3[0]) > aabbMax[0]) return false; + if (btMax(btMax(p1[0], p2[0]), p3[0]) < aabbMin[0]) return false; + + if (btMin(btMin(p1[2], p2[2]), p3[2]) > aabbMax[2]) return false; + if (btMax(btMax(p1[2], p2[2]), p3[2]) < aabbMin[2]) return false; + + if (btMin(btMin(p1[1], p2[1]), p3[1]) > aabbMax[1]) return false; + if (btMax(btMax(p1[1], p2[1]), p3[1]) < aabbMin[1]) return false; + return true; +} + + +SIMD_FORCE_INLINE int btOutcode(const btVector3& p,const btVector3& halfExtent) +{ + return (p.getX() < -halfExtent.getX() ? 0x01 : 0x0) | + (p.getX() > halfExtent.getX() ? 0x08 : 0x0) | + (p.getY() < -halfExtent.getY() ? 0x02 : 0x0) | + (p.getY() > halfExtent.getY() ? 0x10 : 0x0) | + (p.getZ() < -halfExtent.getZ() ? 0x4 : 0x0) | + (p.getZ() > halfExtent.getZ() ? 0x20 : 0x0); +} + + + +SIMD_FORCE_INLINE bool btRayAabb2(const btVector3& rayFrom, + const btVector3& rayInvDirection, + const unsigned int raySign[3], + const btVector3 bounds[2], + btScalar& tmin, + btScalar lambda_min, + btScalar lambda_max) +{ + btScalar tmax, tymin, tymax, tzmin, tzmax; + tmin = (bounds[raySign[0]].getX() - rayFrom.getX()) * rayInvDirection.getX(); + tmax = (bounds[1-raySign[0]].getX() - rayFrom.getX()) * rayInvDirection.getX(); + tymin = (bounds[raySign[1]].getY() - rayFrom.getY()) * rayInvDirection.getY(); + tymax = (bounds[1-raySign[1]].getY() - rayFrom.getY()) * rayInvDirection.getY(); + + if ( (tmin > tymax) || (tymin > tmax) ) + return false; + + if (tymin > tmin) + tmin = tymin; + + if (tymax < tmax) + tmax = tymax; + + tzmin = (bounds[raySign[2]].getZ() - rayFrom.getZ()) * rayInvDirection.getZ(); + tzmax = (bounds[1-raySign[2]].getZ() - rayFrom.getZ()) * rayInvDirection.getZ(); + + if ( (tmin > tzmax) || (tzmin > tmax) ) + return false; + if (tzmin > tmin) + tmin = tzmin; + if (tzmax < tmax) + tmax = tzmax; + return ( (tmin < lambda_max) && (tmax > lambda_min) ); +} + +SIMD_FORCE_INLINE bool btRayAabb(const btVector3& rayFrom, + const btVector3& rayTo, + const btVector3& aabbMin, + const btVector3& aabbMax, + btScalar& param, btVector3& normal) +{ + btVector3 aabbHalfExtent = (aabbMax-aabbMin)* btScalar(0.5); + btVector3 aabbCenter = (aabbMax+aabbMin)* btScalar(0.5); + btVector3 source = rayFrom - aabbCenter; + btVector3 target = rayTo - aabbCenter; + int sourceOutcode = btOutcode(source,aabbHalfExtent); + int targetOutcode = btOutcode(target,aabbHalfExtent); + if ((sourceOutcode & targetOutcode) == 0x0) + { + btScalar lambda_enter = btScalar(0.0); + btScalar lambda_exit = param; + btVector3 r = target - source; + int i; + btScalar normSign = 1; + btVector3 hitNormal(0,0,0); + int bit=1; + + for (int j=0;j<2;j++) + { + for (i = 0; i != 3; ++i) + { + if (sourceOutcode & bit) + { + btScalar lambda = (-source[i] - aabbHalfExtent[i]*normSign) / r[i]; + if (lambda_enter <= lambda) + { + lambda_enter = lambda; + hitNormal.setValue(0,0,0); + hitNormal[i] = normSign; + } + } + else if (targetOutcode & bit) + { + btScalar lambda = (-source[i] - aabbHalfExtent[i]*normSign) / r[i]; + btSetMin(lambda_exit, lambda); + } + bit<<=1; + } + normSign = btScalar(-1.); + } + if (lambda_enter <= lambda_exit) + { + param = lambda_enter; + normal = hitNormal; + return true; + } + } + return false; +} + + + +SIMD_FORCE_INLINE void btTransformAabb(const btVector3& halfExtents, btScalar margin,const btTransform& t,btVector3& aabbMinOut,btVector3& aabbMaxOut) +{ + btVector3 halfExtentsWithMargin = halfExtents+btVector3(margin,margin,margin); + btMatrix3x3 abs_b = t.getBasis().absolute(); + btVector3 center = t.getOrigin(); + btVector3 extent = btVector3(abs_b[0].dot(halfExtentsWithMargin), + abs_b[1].dot(halfExtentsWithMargin), + abs_b[2].dot(halfExtentsWithMargin)); + aabbMinOut = center - extent; + aabbMaxOut = center + extent; +} + + +SIMD_FORCE_INLINE void btTransformAabb(const btVector3& localAabbMin,const btVector3& localAabbMax, btScalar margin,const btTransform& trans,btVector3& aabbMinOut,btVector3& aabbMaxOut) +{ + btAssert(localAabbMin.getX() <= localAabbMax.getX()); + btAssert(localAabbMin.getY() <= localAabbMax.getY()); + btAssert(localAabbMin.getZ() <= localAabbMax.getZ()); + btVector3 localHalfExtents = btScalar(0.5)*(localAabbMax-localAabbMin); + localHalfExtents+=btVector3(margin,margin,margin); + + btVector3 localCenter = btScalar(0.5)*(localAabbMax+localAabbMin); + btMatrix3x3 abs_b = trans.getBasis().absolute(); + btVector3 center = trans(localCenter); + btVector3 extent = btVector3(abs_b[0].dot(localHalfExtents), + abs_b[1].dot(localHalfExtents), + abs_b[2].dot(localHalfExtents)); + aabbMinOut = center-extent; + aabbMaxOut = center+extent; +} + +#define USE_BANCHLESS 1 +#ifdef USE_BANCHLESS + //This block replaces the block below and uses no branches, and replaces the 8 bit return with a 32 bit return for improved performance (~3x on XBox 360) + SIMD_FORCE_INLINE unsigned testQuantizedAabbAgainstQuantizedAabb(const unsigned short int* aabbMin1,const unsigned short int* aabbMax1,const unsigned short int* aabbMin2,const unsigned short int* aabbMax2) + { + return static_cast(btSelect((unsigned)((aabbMin1[0] <= aabbMax2[0]) & (aabbMax1[0] >= aabbMin2[0]) + & (aabbMin1[2] <= aabbMax2[2]) & (aabbMax1[2] >= aabbMin2[2]) + & (aabbMin1[1] <= aabbMax2[1]) & (aabbMax1[1] >= aabbMin2[1])), + 1, 0)); + } +#else + SIMD_FORCE_INLINE bool testQuantizedAabbAgainstQuantizedAabb(const unsigned short int* aabbMin1,const unsigned short int* aabbMax1,const unsigned short int* aabbMin2,const unsigned short int* aabbMax2) + { + bool overlap = true; + overlap = (aabbMin1[0] > aabbMax2[0] || aabbMax1[0] < aabbMin2[0]) ? false : overlap; + overlap = (aabbMin1[2] > aabbMax2[2] || aabbMax1[2] < aabbMin2[2]) ? false : overlap; + overlap = (aabbMin1[1] > aabbMax2[1] || aabbMax1[1] < aabbMin2[1]) ? false : overlap; + return overlap; + } +#endif //USE_BANCHLESS + +#endif + + diff --git a/Engine/lib/bullet/src/LinearMath/btAlignedAllocator.cpp b/Engine/lib/bullet/src/LinearMath/btAlignedAllocator.cpp new file mode 100644 index 000000000..a3d790f8a --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/btAlignedAllocator.cpp @@ -0,0 +1,205 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btAlignedAllocator.h" + +int gNumAlignedAllocs = 0; +int gNumAlignedFree = 0; +int gTotalBytesAlignedAllocs = 0;//detect memory leaks + +static void *btAllocDefault(size_t size) +{ + return malloc(size); +} + +static void btFreeDefault(void *ptr) +{ + free(ptr); +} + +static btAllocFunc *sAllocFunc = btAllocDefault; +static btFreeFunc *sFreeFunc = btFreeDefault; + + + +#if defined (BT_HAS_ALIGNED_ALLOCATOR) +#include +static void *btAlignedAllocDefault(size_t size, int alignment) +{ + return _aligned_malloc(size, (size_t)alignment); +} + +static void btAlignedFreeDefault(void *ptr) +{ + _aligned_free(ptr); +} +#elif defined(__CELLOS_LV2__) +#include + +static inline void *btAlignedAllocDefault(size_t size, int alignment) +{ + return memalign(alignment, size); +} + +static inline void btAlignedFreeDefault(void *ptr) +{ + free(ptr); +} +#else +static inline void *btAlignedAllocDefault(size_t size, int alignment) +{ + void *ret; + char *real; + unsigned long offset; + + real = (char *)sAllocFunc(size + sizeof(void *) + (alignment-1)); + if (real) { + offset = (alignment - (unsigned long)(real + sizeof(void *))) & (alignment-1); + ret = (void *)((real + sizeof(void *)) + offset); + *((void **)(ret)-1) = (void *)(real); + } else { + ret = (void *)(real); + } + return (ret); +} + +static inline void btAlignedFreeDefault(void *ptr) +{ + void* real; + + if (ptr) { + real = *((void **)(ptr)-1); + sFreeFunc(real); + } +} +#endif + + +static btAlignedAllocFunc *sAlignedAllocFunc = btAlignedAllocDefault; +static btAlignedFreeFunc *sAlignedFreeFunc = btAlignedFreeDefault; + +void btAlignedAllocSetCustomAligned(btAlignedAllocFunc *allocFunc, btAlignedFreeFunc *freeFunc) +{ + sAlignedAllocFunc = allocFunc ? allocFunc : btAlignedAllocDefault; + sAlignedFreeFunc = freeFunc ? freeFunc : btAlignedFreeDefault; +} + +void btAlignedAllocSetCustom(btAllocFunc *allocFunc, btFreeFunc *freeFunc) +{ + sAllocFunc = allocFunc ? allocFunc : btAllocDefault; + sFreeFunc = freeFunc ? freeFunc : btFreeDefault; +} + +#ifdef BT_DEBUG_MEMORY_ALLOCATIONS +//this generic allocator provides the total allocated number of bytes +#include + +void* btAlignedAllocInternal (size_t size, int alignment,int line,char* filename) +{ + void *ret; + char *real; + unsigned long offset; + + gTotalBytesAlignedAllocs += size; + gNumAlignedAllocs++; + + + real = (char *)sAllocFunc(size + 2*sizeof(void *) + (alignment-1)); + if (real) { + offset = (alignment - (unsigned long)(real + 2*sizeof(void *))) & +(alignment-1); + ret = (void *)((real + 2*sizeof(void *)) + offset); + *((void **)(ret)-1) = (void *)(real); + *((int*)(ret)-2) = size; + + } else { + ret = (void *)(real);//?? + } + + printf("allocation#%d at address %x, from %s,line %d, size %d\n",gNumAlignedAllocs,real, filename,line,size); + + int* ptr = (int*)ret; + *ptr = 12; + return (ret); +} + +void btAlignedFreeInternal (void* ptr,int line,char* filename) +{ + + void* real; + gNumAlignedFree++; + + if (ptr) { + real = *((void **)(ptr)-1); + int size = *((int*)(ptr)-2); + gTotalBytesAlignedAllocs -= size; + + printf("free #%d at address %x, from %s,line %d, size %d\n",gNumAlignedFree,real, filename,line,size); + + sFreeFunc(real); + } else + { + printf("NULL ptr\n"); + } +} + +#else //BT_DEBUG_MEMORY_ALLOCATIONS + +void* btAlignedAllocInternal (size_t size, int alignment) +{ + gNumAlignedAllocs++; + void* ptr; +#if defined (BT_HAS_ALIGNED_ALLOCATOR) || defined(__CELLOS_LV2__) + ptr = sAlignedAllocFunc(size, alignment); +#else + char *real; + unsigned long offset; + + real = (char *)sAllocFunc(size + sizeof(void *) + (alignment-1)); + if (real) { + offset = (alignment - (unsigned long)(real + sizeof(void *))) & (alignment-1); + ptr = (void *)((real + sizeof(void *)) + offset); + *((void **)(ptr)-1) = (void *)(real); + } else { + ptr = (void *)(real); + } +#endif // defined (BT_HAS_ALIGNED_ALLOCATOR) || defined(__CELLOS_LV2__) +// printf("btAlignedAllocInternal %d, %x\n",size,ptr); + return ptr; +} + +void btAlignedFreeInternal (void* ptr) +{ + if (!ptr) + { + return; + } + + gNumAlignedFree++; +// printf("btAlignedFreeInternal %x\n",ptr); +#if defined (BT_HAS_ALIGNED_ALLOCATOR) || defined(__CELLOS_LV2__) + sAlignedFreeFunc(ptr); +#else + void* real; + + if (ptr) { + real = *((void **)(ptr)-1); + sFreeFunc(real); + } +#endif // defined (BT_HAS_ALIGNED_ALLOCATOR) || defined(__CELLOS_LV2__) +} + +#endif //BT_DEBUG_MEMORY_ALLOCATIONS + diff --git a/Engine/lib/bullet/src/LinearMath/btAlignedAllocator.h b/Engine/lib/bullet/src/LinearMath/btAlignedAllocator.h new file mode 100644 index 000000000..f168f3c66 --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/btAlignedAllocator.h @@ -0,0 +1,107 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 BT_ALIGNED_ALLOCATOR +#define BT_ALIGNED_ALLOCATOR + +///we probably replace this with our own aligned memory allocator +///so we replace _aligned_malloc and _aligned_free with our own +///that is better portable and more predictable + +#include "btScalar.h" +//#define BT_DEBUG_MEMORY_ALLOCATIONS 1 +#ifdef BT_DEBUG_MEMORY_ALLOCATIONS + +#define btAlignedAlloc(a,b) \ + btAlignedAllocInternal(a,b,__LINE__,__FILE__) + +#define btAlignedFree(ptr) \ + btAlignedFreeInternal(ptr,__LINE__,__FILE__) + +void* btAlignedAllocInternal (size_t size, int alignment,int line,char* filename); + +void btAlignedFreeInternal (void* ptr,int line,char* filename); + +#else + void* btAlignedAllocInternal (size_t size, int alignment); + void btAlignedFreeInternal (void* ptr); + + #define btAlignedAlloc(size,alignment) btAlignedAllocInternal(size,alignment) + #define btAlignedFree(ptr) btAlignedFreeInternal(ptr) + +#endif +typedef int size_type; + +typedef void *(btAlignedAllocFunc)(size_t size, int alignment); +typedef void (btAlignedFreeFunc)(void *memblock); +typedef void *(btAllocFunc)(size_t size); +typedef void (btFreeFunc)(void *memblock); + +///The developer can let all Bullet memory allocations go through a custom memory allocator, using btAlignedAllocSetCustom +void btAlignedAllocSetCustom(btAllocFunc *allocFunc, btFreeFunc *freeFunc); +///If the developer has already an custom aligned allocator, then btAlignedAllocSetCustomAligned can be used. The default aligned allocator pre-allocates extra memory using the non-aligned allocator, and instruments it. +void btAlignedAllocSetCustomAligned(btAlignedAllocFunc *allocFunc, btAlignedFreeFunc *freeFunc); + + +///The btAlignedAllocator is a portable class for aligned memory allocations. +///Default implementations for unaligned and aligned allocations can be overridden by a custom allocator using btAlignedAllocSetCustom and btAlignedAllocSetCustomAligned. +template < typename T , unsigned Alignment > +class btAlignedAllocator { + + typedef btAlignedAllocator< T , Alignment > self_type; + +public: + + //just going down a list: + btAlignedAllocator() {} + /* + btAlignedAllocator( const self_type & ) {} + */ + + template < typename Other > + btAlignedAllocator( const btAlignedAllocator< Other , Alignment > & ) {} + + typedef const T* const_pointer; + typedef const T& const_reference; + typedef T* pointer; + typedef T& reference; + typedef T value_type; + + pointer address ( reference ref ) const { return &ref; } + const_pointer address ( const_reference ref ) const { return &ref; } + pointer allocate ( size_type n , const_pointer * hint = 0 ) { + (void)hint; + return reinterpret_cast< pointer >(btAlignedAlloc( sizeof(value_type) * n , Alignment )); + } + void construct ( pointer ptr , const value_type & value ) { new (ptr) value_type( value ); } + void deallocate( pointer ptr ) { + btAlignedFree( reinterpret_cast< void * >( ptr ) ); + } + void destroy ( pointer ptr ) { ptr->~value_type(); } + + + template < typename O > struct rebind { + typedef btAlignedAllocator< O , Alignment > other; + }; + template < typename O > + self_type & operator=( const btAlignedAllocator< O , Alignment > & ) { return *this; } + + friend bool operator==( const self_type & , const self_type & ) { return true; } +}; + + + +#endif //BT_ALIGNED_ALLOCATOR + diff --git a/Engine/lib/bullet/src/LinearMath/btAlignedObjectArray.h b/Engine/lib/bullet/src/LinearMath/btAlignedObjectArray.h new file mode 100644 index 000000000..7c013d375 --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/btAlignedObjectArray.h @@ -0,0 +1,442 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 BT_OBJECT_ARRAY__ +#define BT_OBJECT_ARRAY__ + +#include "btScalar.h" // has definitions like SIMD_FORCE_INLINE +#include "btAlignedAllocator.h" + +///If the platform doesn't support placement new, you can disable BT_USE_PLACEMENT_NEW +///then the btAlignedObjectArray doesn't support objects with virtual methods, and non-trivial constructors/destructors +///You can enable BT_USE_MEMCPY, then swapping elements in the array will use memcpy instead of operator= +///see discussion here: http://continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=1231 and +///http://www.continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=1240 + +#define BT_USE_PLACEMENT_NEW 1 +//#define BT_USE_MEMCPY 1 //disable, because it is cumbersome to find out for each platform where memcpy is defined. It can be in or or otherwise... + +#ifdef BT_USE_MEMCPY +#include +#include +#endif //BT_USE_MEMCPY + +#ifdef BT_USE_PLACEMENT_NEW +#include //for placement new +#endif //BT_USE_PLACEMENT_NEW + + +///The btAlignedObjectArray template class uses a subset of the stl::vector interface for its methods +///It is developed to replace stl::vector to avoid portability issues, including STL alignment issues to add SIMD/SSE data +template +//template +class btAlignedObjectArray +{ + btAlignedAllocator m_allocator; + + int m_size; + int m_capacity; + T* m_data; + //PCK: added this line + bool m_ownsMemory; + + protected: + SIMD_FORCE_INLINE int allocSize(int size) + { + return (size ? size*2 : 1); + } + SIMD_FORCE_INLINE void copy(int start,int end, T* dest) const + { + int i; + for (i=start;i size()) + { + reserve(newsize); + } +#ifdef BT_USE_PLACEMENT_NEW + for (int i=curSize;i + void quickSortInternal(L CompareFunc,int lo, int hi) + { + // lo is the lower index, hi is the upper index + // of the region of array a that is to be sorted + int i=lo, j=hi; + T x=m_data[(lo+hi)/2]; + + // partition + do + { + while (CompareFunc(m_data[i],x)) + i++; + while (CompareFunc(x,m_data[j])) + j--; + if (i<=j) + { + swap(i,j); + i++; j--; + } + } while (i<=j); + + // recursion + if (lo + void quickSort(L CompareFunc) + { + //don't sort 0 or 1 elements + if (size()>1) + { + quickSortInternal(CompareFunc,0,size()-1); + } + } + + + ///heap sort from http://www.csse.monash.edu.au/~lloyd/tildeAlgDS/Sort/Heap/ + template + void downHeap(T *pArr, int k, int n,L CompareFunc) + { + /* PRE: a[k+1..N] is a heap */ + /* POST: a[k..N] is a heap */ + + T temp = pArr[k - 1]; + /* k has child(s) */ + while (k <= n/2) + { + int child = 2*k; + + if ((child < n) && CompareFunc(pArr[child - 1] , pArr[child])) + { + child++; + } + /* pick larger child */ + if (CompareFunc(temp , pArr[child - 1])) + { + /* move child up */ + pArr[k - 1] = pArr[child - 1]; + k = child; + } + else + { + break; + } + } + pArr[k - 1] = temp; + } /*downHeap*/ + + void swap(int index0,int index1) + { +#ifdef BT_USE_MEMCPY + char temp[sizeof(T)]; + memcpy(temp,&m_data[index0],sizeof(T)); + memcpy(&m_data[index0],&m_data[index1],sizeof(T)); + memcpy(&m_data[index1],temp,sizeof(T)); +#else + T temp = m_data[index0]; + m_data[index0] = m_data[index1]; + m_data[index1] = temp; +#endif //BT_USE_PLACEMENT_NEW + + } + + template + void heapSort(L CompareFunc) + { + /* sort a[0..N-1], N.B. 0 to N-1 */ + int k; + int n = m_size; + for (k = n/2; k > 0; k--) + { + downHeap(m_data, k, n, CompareFunc); + } + + /* a[1..N] is now a heap */ + while ( n>=1 ) + { + swap(0,n-1); /* largest of a[0..n-1] */ + + + n = n - 1; + /* restore a[1..i-1] heap */ + downHeap(m_data, 1, n, CompareFunc); + } + } + + ///non-recursive binary search, assumes sorted array + int findBinarySearch(const T& key) const + { + int first = 0; + int last = size(); + + //assume sorted array + while (first <= last) { + int mid = (first + last) / 2; // compute mid point. + if (key > m_data[mid]) + first = mid + 1; // repeat search in top half. + else if (key < m_data[mid]) + last = mid - 1; // repeat search in bottom half. + else + return mid; // found it. return position ///// + } + return size(); // failed to find key + } + + + int findLinearSearch(const T& key) const + { + int index=size(); + int i; + + for (i=0;i + +#include "btConvexHull.h" +#include "LinearMath/btAlignedObjectArray.h" +#include "LinearMath/btMinMax.h" +#include "LinearMath/btVector3.h" + + + +template +void Swap(T &a,T &b) +{ + T tmp = a; + a=b; + b=tmp; +} + + +//---------------------------------- + +class int3 +{ +public: + int x,y,z; + int3(){}; + int3(int _x,int _y, int _z){x=_x;y=_y;z=_z;} + const int& operator[](int i) const {return (&x)[i];} + int& operator[](int i) {return (&x)[i];} +}; + + +//------- btPlane ---------- + + +inline btPlane PlaneFlip(const btPlane &plane){return btPlane(-plane.normal,-plane.dist);} +inline int operator==( const btPlane &a, const btPlane &b ) { return (a.normal==b.normal && a.dist==b.dist); } +inline int coplanar( const btPlane &a, const btPlane &b ) { return (a==b || a==PlaneFlip(b)); } + + +//--------- Utility Functions ------ + +btVector3 PlaneLineIntersection(const btPlane &plane, const btVector3 &p0, const btVector3 &p1); +btVector3 PlaneProject(const btPlane &plane, const btVector3 &point); + +btVector3 ThreePlaneIntersection(const btPlane &p0,const btPlane &p1, const btPlane &p2); +btVector3 ThreePlaneIntersection(const btPlane &p0,const btPlane &p1, const btPlane &p2) +{ + btVector3 N1 = p0.normal; + btVector3 N2 = p1.normal; + btVector3 N3 = p2.normal; + + btVector3 n2n3; n2n3 = N2.cross(N3); + btVector3 n3n1; n3n1 = N3.cross(N1); + btVector3 n1n2; n1n2 = N1.cross(N2); + + btScalar quotient = (N1.dot(n2n3)); + + btAssert(btFabs(quotient) > btScalar(0.000001)); + + quotient = btScalar(-1.) / quotient; + n2n3 *= p0.dist; + n3n1 *= p1.dist; + n1n2 *= p2.dist; + btVector3 potentialVertex = n2n3; + potentialVertex += n3n1; + potentialVertex += n1n2; + potentialVertex *= quotient; + + btVector3 result(potentialVertex.getX(),potentialVertex.getY(),potentialVertex.getZ()); + return result; + +} + +btScalar DistanceBetweenLines(const btVector3 &ustart, const btVector3 &udir, const btVector3 &vstart, const btVector3 &vdir, btVector3 *upoint=NULL, btVector3 *vpoint=NULL); +btVector3 TriNormal(const btVector3 &v0, const btVector3 &v1, const btVector3 &v2); +btVector3 NormalOf(const btVector3 *vert, const int n); + + +btVector3 PlaneLineIntersection(const btPlane &plane, const btVector3 &p0, const btVector3 &p1) +{ + // returns the point where the line p0-p1 intersects the plane n&d + static btVector3 dif; + dif = p1-p0; + btScalar dn= btDot(plane.normal,dif); + btScalar t = -(plane.dist+btDot(plane.normal,p0) )/dn; + return p0 + (dif*t); +} + +btVector3 PlaneProject(const btPlane &plane, const btVector3 &point) +{ + return point - plane.normal * (btDot(point,plane.normal)+plane.dist); +} + +btVector3 TriNormal(const btVector3 &v0, const btVector3 &v1, const btVector3 &v2) +{ + // return the normal of the triangle + // inscribed by v0, v1, and v2 + btVector3 cp=btCross(v1-v0,v2-v1); + btScalar m=cp.length(); + if(m==0) return btVector3(1,0,0); + return cp*(btScalar(1.0)/m); +} + + +btScalar DistanceBetweenLines(const btVector3 &ustart, const btVector3 &udir, const btVector3 &vstart, const btVector3 &vdir, btVector3 *upoint, btVector3 *vpoint) +{ + static btVector3 cp; + cp = btCross(udir,vdir).normalized(); + + btScalar distu = -btDot(cp,ustart); + btScalar distv = -btDot(cp,vstart); + btScalar dist = (btScalar)fabs(distu-distv); + if(upoint) + { + btPlane plane; + plane.normal = btCross(vdir,cp).normalized(); + plane.dist = -btDot(plane.normal,vstart); + *upoint = PlaneLineIntersection(plane,ustart,ustart+udir); + } + if(vpoint) + { + btPlane plane; + plane.normal = btCross(udir,cp).normalized(); + plane.dist = -btDot(plane.normal,ustart); + *vpoint = PlaneLineIntersection(plane,vstart,vstart+vdir); + } + return dist; +} + + + + + + + +#define COPLANAR (0) +#define UNDER (1) +#define OVER (2) +#define SPLIT (OVER|UNDER) +#define PAPERWIDTH (btScalar(0.001)) + +btScalar planetestepsilon = PAPERWIDTH; + + + +typedef ConvexH::HalfEdge HalfEdge; + +ConvexH::ConvexH(int vertices_size,int edges_size,int facets_size) +{ + vertices.resize(vertices_size); + edges.resize(edges_size); + facets.resize(facets_size); +} + + +int PlaneTest(const btPlane &p, const btVector3 &v); +int PlaneTest(const btPlane &p, const btVector3 &v) { + btScalar a = btDot(v,p.normal)+p.dist; + int flag = (a>planetestepsilon)?OVER:((a<-planetestepsilon)?UNDER:COPLANAR); + return flag; +} + +int SplitTest(ConvexH &convex,const btPlane &plane); +int SplitTest(ConvexH &convex,const btPlane &plane) { + int flag=0; + for(int i=0;i +int maxdirfiltered(const T *p,int count,const T &dir,btAlignedObjectArray &allow) +{ + btAssert(count); + int m=-1; + for(int i=0;ibtDot(p[m],dir)) + m=i; + } + btAssert(m!=-1); + return m; +} + +btVector3 orth(const btVector3 &v); +btVector3 orth(const btVector3 &v) +{ + btVector3 a=btCross(v,btVector3(0,0,1)); + btVector3 b=btCross(v,btVector3(0,1,0)); + if (a.length() > b.length()) + { + return a.normalized(); + } else { + return b.normalized(); + } +} + + +template +int maxdirsterid(const T *p,int count,const T &dir,btAlignedObjectArray &allow) +{ + int m=-1; + while(m==-1) + { + m = maxdirfiltered(p,count,dir,allow); + if(allow[m]==3) return m; + T u = orth(dir); + T v = btCross(u,dir); + int ma=-1; + for(btScalar x = btScalar(0.0) ; x<= btScalar(360.0) ; x+= btScalar(45.0)) + { + btScalar s = btSin(SIMD_RADS_PER_DEG*(x)); + btScalar c = btCos(SIMD_RADS_PER_DEG*(x)); + int mb = maxdirfiltered(p,count,dir+(u*s+v*c)*btScalar(0.025),allow); + if(ma==m && mb==m) + { + allow[m]=3; + return m; + } + if(ma!=-1 && ma!=mb) // Yuck - this is really ugly + { + int mc = ma; + for(btScalar xx = x-btScalar(40.0) ; xx <= x ; xx+= btScalar(5.0)) + { + btScalar s = btSin(SIMD_RADS_PER_DEG*(xx)); + btScalar c = btCos(SIMD_RADS_PER_DEG*(xx)); + int md = maxdirfiltered(p,count,dir+(u*s+v*c)*btScalar(0.025),allow); + if(mc==m && md==m) + { + allow[m]=3; + return m; + } + mc=md; + } + } + ma=mb; + } + allow[m]=0; + m=-1; + } + btAssert(0); + return m; +} + + + + +int operator ==(const int3 &a,const int3 &b); +int operator ==(const int3 &a,const int3 &b) +{ + for(int i=0;i<3;i++) + { + if(a[i]!=b[i]) return 0; + } + return 1; +} + + +int above(btVector3* vertices,const int3& t, const btVector3 &p, btScalar epsilon); +int above(btVector3* vertices,const int3& t, const btVector3 &p, btScalar epsilon) +{ + btVector3 n=TriNormal(vertices[t[0]],vertices[t[1]],vertices[t[2]]); + return (btDot(n,p-vertices[t[0]]) > epsilon); // EPSILON??? +} +int hasedge(const int3 &t, int a,int b); +int hasedge(const int3 &t, int a,int b) +{ + for(int i=0;i<3;i++) + { + int i1= (i+1)%3; + if(t[i]==a && t[i1]==b) return 1; + } + return 0; +} +int hasvert(const int3 &t, int v); +int hasvert(const int3 &t, int v) +{ + return (t[0]==v || t[1]==v || t[2]==v) ; +} +int shareedge(const int3 &a,const int3 &b); +int shareedge(const int3 &a,const int3 &b) +{ + int i; + for(i=0;i<3;i++) + { + int i1= (i+1)%3; + if(hasedge(a,b[i1],b[i])) return 1; + } + return 0; +} + +class btHullTriangle; + + + +class btHullTriangle : public int3 +{ +public: + int3 n; + int id; + int vmax; + btScalar rise; + btHullTriangle(int a,int b,int c):int3(a,b,c),n(-1,-1,-1) + { + vmax=-1; + rise = btScalar(0.0); + } + ~btHullTriangle() + { + } + int &neib(int a,int b); +}; + + +int &btHullTriangle::neib(int a,int b) +{ + static int er=-1; + int i; + for(i=0;i<3;i++) + { + int i1=(i+1)%3; + int i2=(i+2)%3; + if((*this)[i]==a && (*this)[i1]==b) return n[i2]; + if((*this)[i]==b && (*this)[i1]==a) return n[i2]; + } + btAssert(0); + return er; +} +void HullLibrary::b2bfix(btHullTriangle* s,btHullTriangle*t) +{ + int i; + for(i=0;i<3;i++) + { + int i1=(i+1)%3; + int i2=(i+2)%3; + int a = (*s)[i1]; + int b = (*s)[i2]; + btAssert(m_tris[s->neib(a,b)]->neib(b,a) == s->id); + btAssert(m_tris[t->neib(a,b)]->neib(b,a) == t->id); + m_tris[s->neib(a,b)]->neib(b,a) = t->neib(b,a); + m_tris[t->neib(b,a)]->neib(a,b) = s->neib(a,b); + } +} + +void HullLibrary::removeb2b(btHullTriangle* s,btHullTriangle*t) +{ + b2bfix(s,t); + deAllocateTriangle(s); + + deAllocateTriangle(t); +} + +void HullLibrary::checkit(btHullTriangle *t) +{ + (void)t; + + int i; + btAssert(m_tris[t->id]==t); + for(i=0;i<3;i++) + { + int i1=(i+1)%3; + int i2=(i+2)%3; + int a = (*t)[i1]; + int b = (*t)[i2]; + + // release compile fix + (void)i1; + (void)i2; + (void)a; + (void)b; + + btAssert(a!=b); + btAssert( m_tris[t->n[i]]->neib(b,a) == t->id); + } +} + +btHullTriangle* HullLibrary::allocateTriangle(int a,int b,int c) +{ + void* mem = btAlignedAlloc(sizeof(btHullTriangle),16); + btHullTriangle* tr = new (mem)btHullTriangle(a,b,c); + tr->id = m_tris.size(); + m_tris.push_back(tr); + + return tr; +} + +void HullLibrary::deAllocateTriangle(btHullTriangle* tri) +{ + btAssert(m_tris[tri->id]==tri); + m_tris[tri->id]=NULL; + tri->~btHullTriangle(); + btAlignedFree(tri); +} + + +void HullLibrary::extrude(btHullTriangle *t0,int v) +{ + int3 t= *t0; + int n = m_tris.size(); + btHullTriangle* ta = allocateTriangle(v,t[1],t[2]); + ta->n = int3(t0->n[0],n+1,n+2); + m_tris[t0->n[0]]->neib(t[1],t[2]) = n+0; + btHullTriangle* tb = allocateTriangle(v,t[2],t[0]); + tb->n = int3(t0->n[1],n+2,n+0); + m_tris[t0->n[1]]->neib(t[2],t[0]) = n+1; + btHullTriangle* tc = allocateTriangle(v,t[0],t[1]); + tc->n = int3(t0->n[2],n+0,n+1); + m_tris[t0->n[2]]->neib(t[0],t[1]) = n+2; + checkit(ta); + checkit(tb); + checkit(tc); + if(hasvert(*m_tris[ta->n[0]],v)) removeb2b(ta,m_tris[ta->n[0]]); + if(hasvert(*m_tris[tb->n[0]],v)) removeb2b(tb,m_tris[tb->n[0]]); + if(hasvert(*m_tris[tc->n[0]],v)) removeb2b(tc,m_tris[tc->n[0]]); + deAllocateTriangle(t0); + +} + +btHullTriangle* HullLibrary::extrudable(btScalar epsilon) +{ + int i; + btHullTriangle *t=NULL; + for(i=0;iriserise)) + { + t = m_tris[i]; + } + } + return (t->rise >epsilon)?t:NULL ; +} + + + + +int4 HullLibrary::FindSimplex(btVector3 *verts,int verts_count,btAlignedObjectArray &allow) +{ + btVector3 basis[3]; + basis[0] = btVector3( btScalar(0.01), btScalar(0.02), btScalar(1.0) ); + int p0 = maxdirsterid(verts,verts_count, basis[0],allow); + int p1 = maxdirsterid(verts,verts_count,-basis[0],allow); + basis[0] = verts[p0]-verts[p1]; + if(p0==p1 || basis[0]==btVector3(0,0,0)) + return int4(-1,-1,-1,-1); + basis[1] = btCross(btVector3( btScalar(1),btScalar(0.02), btScalar(0)),basis[0]); + basis[2] = btCross(btVector3(btScalar(-0.02), btScalar(1), btScalar(0)),basis[0]); + if (basis[1].length() > basis[2].length()) + { + basis[1].normalize(); + } else { + basis[1] = basis[2]; + basis[1].normalize (); + } + int p2 = maxdirsterid(verts,verts_count,basis[1],allow); + if(p2 == p0 || p2 == p1) + { + p2 = maxdirsterid(verts,verts_count,-basis[1],allow); + } + if(p2 == p0 || p2 == p1) + return int4(-1,-1,-1,-1); + basis[1] = verts[p2] - verts[p0]; + basis[2] = btCross(basis[1],basis[0]).normalized(); + int p3 = maxdirsterid(verts,verts_count,basis[2],allow); + if(p3==p0||p3==p1||p3==p2) p3 = maxdirsterid(verts,verts_count,-basis[2],allow); + if(p3==p0||p3==p1||p3==p2) + return int4(-1,-1,-1,-1); + btAssert(!(p0==p1||p0==p2||p0==p3||p1==p2||p1==p3||p2==p3)); + if(btDot(verts[p3]-verts[p0],btCross(verts[p1]-verts[p0],verts[p2]-verts[p0])) <0) {Swap(p2,p3);} + return int4(p0,p1,p2,p3); +} + +int HullLibrary::calchullgen(btVector3 *verts,int verts_count, int vlimit) +{ + if(verts_count <4) return 0; + if(vlimit==0) vlimit=1000000000; + int j; + btVector3 bmin(*verts),bmax(*verts); + btAlignedObjectArray isextreme; + isextreme.reserve(verts_count); + btAlignedObjectArray allow; + allow.reserve(verts_count); + + for(j=0;jn=int3(2,3,1); + btHullTriangle *t1 = allocateTriangle(p[3],p[2],p[0]); t1->n=int3(3,2,0); + btHullTriangle *t2 = allocateTriangle(p[0],p[1],p[3]); t2->n=int3(0,1,3); + btHullTriangle *t3 = allocateTriangle(p[1],p[0],p[2]); t3->n=int3(1,0,2); + isextreme[p[0]]=isextreme[p[1]]=isextreme[p[2]]=isextreme[p[3]]=1; + checkit(t0);checkit(t1);checkit(t2);checkit(t3); + + for(j=0;jvmax<0); + btVector3 n=TriNormal(verts[(*t)[0]],verts[(*t)[1]],verts[(*t)[2]]); + t->vmax = maxdirsterid(verts,verts_count,n,allow); + t->rise = btDot(n,verts[t->vmax]-verts[(*t)[0]]); + } + btHullTriangle *te; + vlimit-=4; + while(vlimit >0 && ((te=extrudable(epsilon)) != 0)) + { + int3 ti=*te; + int v=te->vmax; + btAssert(v != -1); + btAssert(!isextreme[v]); // wtf we've already done this vertex + isextreme[v]=1; + //if(v==p0 || v==p1 || v==p2 || v==p3) continue; // done these already + j=m_tris.size(); + while(j--) { + if(!m_tris[j]) continue; + int3 t=*m_tris[j]; + if(above(verts,t,verts[v],btScalar(0.01)*epsilon)) + { + extrude(m_tris[j],v); + } + } + // now check for those degenerate cases where we have a flipped triangle or a really skinny triangle + j=m_tris.size(); + while(j--) + { + if(!m_tris[j]) continue; + if(!hasvert(*m_tris[j],v)) break; + int3 nt=*m_tris[j]; + if(above(verts,nt,center,btScalar(0.01)*epsilon) || btCross(verts[nt[1]]-verts[nt[0]],verts[nt[2]]-verts[nt[1]]).length()< epsilon*epsilon*btScalar(0.1) ) + { + btHullTriangle *nb = m_tris[m_tris[j]->n[0]]; + btAssert(nb);btAssert(!hasvert(*nb,v));btAssert(nb->idvmax>=0) break; + btVector3 n=TriNormal(verts[(*t)[0]],verts[(*t)[1]],verts[(*t)[2]]); + t->vmax = maxdirsterid(verts,verts_count,n,allow); + if(isextreme[t->vmax]) + { + t->vmax=-1; // already done that vertex - algorithm needs to be able to terminate. + } + else + { + t->rise = btDot(n,verts[t->vmax]-verts[(*t)[0]]); + } + } + vlimit --; + } + return 1; +} + +int HullLibrary::calchull(btVector3 *verts,int verts_count, TUIntArray& tris_out, int &tris_count,int vlimit) +{ + int rc=calchullgen(verts,verts_count, vlimit) ; + if(!rc) return 0; + btAlignedObjectArray ts; + int i; + + for(i=0;i(ts[i]); + } + m_tris.resize(0); + + return 1; +} + + + + + +bool HullLibrary::ComputeHull(unsigned int vcount,const btVector3 *vertices,PHullResult &result,unsigned int vlimit) +{ + + int tris_count; + int ret = calchull( (btVector3 *) vertices, (int) vcount, result.m_Indices, tris_count, static_cast(vlimit) ); + if(!ret) return false; + result.mIndexCount = (unsigned int) (tris_count*3); + result.mFaceCount = (unsigned int) tris_count; + result.mVertices = (btVector3*) vertices; + result.mVcount = (unsigned int) vcount; + return true; + +} + + +void ReleaseHull(PHullResult &result); +void ReleaseHull(PHullResult &result) +{ + if ( result.m_Indices.size() ) + { + result.m_Indices.clear(); + } + + result.mVcount = 0; + result.mIndexCount = 0; + result.mVertices = 0; +} + + +//********************************************************************* +//********************************************************************* +//******** HullLib header +//********************************************************************* +//********************************************************************* + +//********************************************************************* +//********************************************************************* +//******** HullLib implementation +//********************************************************************* +//********************************************************************* + +HullError HullLibrary::CreateConvexHull(const HullDesc &desc, // describes the input request + HullResult &result) // contains the resulst +{ + HullError ret = QE_FAIL; + + + PHullResult hr; + + unsigned int vcount = desc.mVcount; + if ( vcount < 8 ) vcount = 8; + + btAlignedObjectArray vertexSource; + vertexSource.resize(static_cast(vcount)); + + btVector3 scale; + + unsigned int ovcount; + + bool ok = CleanupVertices(desc.mVcount,desc.mVertices, desc.mVertexStride, ovcount, &vertexSource[0], desc.mNormalEpsilon, scale ); // normalize point cloud, remove duplicates! + + if ( ok ) + { + + +// if ( 1 ) // scale vertices back to their original size. + { + for (unsigned int i=0; i(i)]; + v[0]*=scale[0]; + v[1]*=scale[1]; + v[2]*=scale[2]; + } + } + + ok = ComputeHull(ovcount,&vertexSource[0],hr,desc.mMaxVertices); + + if ( ok ) + { + + // re-index triangle mesh so it refers to only used vertices, rebuild a new vertex table. + btAlignedObjectArray vertexScratch; + vertexScratch.resize(static_cast(hr.mVcount)); + + BringOutYourDead(hr.mVertices,hr.mVcount, &vertexScratch[0], ovcount, &hr.m_Indices[0], hr.mIndexCount ); + + ret = QE_OK; + + if ( desc.HasHullFlag(QF_TRIANGLES) ) // if he wants the results as triangle! + { + result.mPolygons = false; + result.mNumOutputVertices = ovcount; + result.m_OutputVertices.resize(static_cast(ovcount)); + result.mNumFaces = hr.mFaceCount; + result.mNumIndices = hr.mIndexCount; + + result.m_Indices.resize(static_cast(hr.mIndexCount)); + + memcpy(&result.m_OutputVertices[0], &vertexScratch[0], sizeof(btVector3)*ovcount ); + + if ( desc.HasHullFlag(QF_REVERSE_ORDER) ) + { + + const unsigned int *source = &hr.m_Indices[0]; + unsigned int *dest = &result.m_Indices[0]; + + for (unsigned int i=0; i(ovcount)); + result.mNumFaces = hr.mFaceCount; + result.mNumIndices = hr.mIndexCount+hr.mFaceCount; + result.m_Indices.resize(static_cast(result.mNumIndices)); + memcpy(&result.m_OutputVertices[0], &vertexScratch[0], sizeof(btVector3)*ovcount ); + +// if ( 1 ) + { + const unsigned int *source = &hr.m_Indices[0]; + unsigned int *dest = &result.m_Indices[0]; + for (unsigned int i=0; i bmax[j] ) bmax[j] = p[j]; + } + } + } + + btScalar dx = bmax[0] - bmin[0]; + btScalar dy = bmax[1] - bmin[1]; + btScalar dz = bmax[2] - bmin[2]; + + btVector3 center; + + center[0] = dx*btScalar(0.5) + bmin[0]; + center[1] = dy*btScalar(0.5) + bmin[1]; + center[2] = dz*btScalar(0.5) + bmin[2]; + + if ( dx < EPSILON || dy < EPSILON || dz < EPSILON || svcount < 3 ) + { + + btScalar len = FLT_MAX; + + if ( dx > EPSILON && dx < len ) len = dx; + if ( dy > EPSILON && dy < len ) len = dy; + if ( dz > EPSILON && dz < len ) len = dz; + + if ( len == FLT_MAX ) + { + dx = dy = dz = btScalar(0.01); // one centimeter + } + else + { + if ( dx < EPSILON ) dx = len * btScalar(0.05); // 1/5th the shortest non-zero edge. + if ( dy < EPSILON ) dy = len * btScalar(0.05); + if ( dz < EPSILON ) dz = len * btScalar(0.05); + } + + btScalar x1 = center[0] - dx; + btScalar x2 = center[0] + dx; + + btScalar y1 = center[1] - dy; + btScalar y2 = center[1] + dy; + + btScalar z1 = center[2] - dz; + btScalar z2 = center[2] + dz; + + addPoint(vcount,vertices,x1,y1,z1); + addPoint(vcount,vertices,x2,y1,z1); + addPoint(vcount,vertices,x2,y2,z1); + addPoint(vcount,vertices,x1,y2,z1); + addPoint(vcount,vertices,x1,y1,z2); + addPoint(vcount,vertices,x2,y1,z2); + addPoint(vcount,vertices,x2,y2,z2); + addPoint(vcount,vertices,x1,y2,z2); + + return true; // return cube + + + } + else + { + if ( scale ) + { + scale[0] = dx; + scale[1] = dy; + scale[2] = dz; + + recip[0] = 1 / dx; + recip[1] = 1 / dy; + recip[2] = 1 / dz; + + center[0]*=recip[0]; + center[1]*=recip[1]; + center[2]*=recip[2]; + + } + + } + + + + vtx = (const char *) svertices; + + for (unsigned int i=0; igetX(); + btScalar py = p->getY(); + btScalar pz = p->getZ(); + + if ( scale ) + { + px = px*recip[0]; // normalize + py = py*recip[1]; // normalize + pz = pz*recip[2]; // normalize + } + +// if ( 1 ) + { + unsigned int j; + + for (j=0; j dist2 ) + { + v[0] = px; + v[1] = py; + v[2] = pz; + + } + + break; + } + } + + if ( j == vcount ) + { + btVector3& dest = vertices[vcount]; + dest[0] = px; + dest[1] = py; + dest[2] = pz; + vcount++; + } + m_vertexIndexMapping.push_back(j); + } + } + + // ok..now make sure we didn't prune so many vertices it is now invalid. +// if ( 1 ) + { + btScalar bmin[3] = { FLT_MAX, FLT_MAX, FLT_MAX }; + btScalar bmax[3] = { -FLT_MAX, -FLT_MAX, -FLT_MAX }; + + for (unsigned int i=0; i bmax[j] ) bmax[j] = p[j]; + } + } + + btScalar dx = bmax[0] - bmin[0]; + btScalar dy = bmax[1] - bmin[1]; + btScalar dz = bmax[2] - bmin[2]; + + if ( dx < EPSILON || dy < EPSILON || dz < EPSILON || vcount < 3) + { + btScalar cx = dx*btScalar(0.5) + bmin[0]; + btScalar cy = dy*btScalar(0.5) + bmin[1]; + btScalar cz = dz*btScalar(0.5) + bmin[2]; + + btScalar len = FLT_MAX; + + if ( dx >= EPSILON && dx < len ) len = dx; + if ( dy >= EPSILON && dy < len ) len = dy; + if ( dz >= EPSILON && dz < len ) len = dz; + + if ( len == FLT_MAX ) + { + dx = dy = dz = btScalar(0.01); // one centimeter + } + else + { + if ( dx < EPSILON ) dx = len * btScalar(0.05); // 1/5th the shortest non-zero edge. + if ( dy < EPSILON ) dy = len * btScalar(0.05); + if ( dz < EPSILON ) dz = len * btScalar(0.05); + } + + btScalar x1 = cx - dx; + btScalar x2 = cx + dx; + + btScalar y1 = cy - dy; + btScalar y2 = cy + dy; + + btScalar z1 = cz - dz; + btScalar z2 = cz + dz; + + vcount = 0; // add box + + addPoint(vcount,vertices,x1,y1,z1); + addPoint(vcount,vertices,x2,y1,z1); + addPoint(vcount,vertices,x2,y2,z1); + addPoint(vcount,vertices,x1,y2,z1); + addPoint(vcount,vertices,x1,y1,z2); + addPoint(vcount,vertices,x2,y1,z2); + addPoint(vcount,vertices,x2,y2,z2); + addPoint(vcount,vertices,x1,y2,z2); + + return true; + } + } + + return true; +} + +void HullLibrary::BringOutYourDead(const btVector3* verts,unsigned int vcount, btVector3* overts,unsigned int &ocount,unsigned int *indices,unsigned indexcount) +{ + btAlignedObjectArraytmpIndices; + tmpIndices.resize(m_vertexIndexMapping.size()); + int i; + + for (i=0;i(vcount)); + memset(&usedIndices[0],0,sizeof(unsigned int)*vcount); + + ocount = 0; + + for (i=0; i= 0 && v < vcount ); + + if ( usedIndices[static_cast(v)] ) // if already remapped + { + indices[i] = usedIndices[static_cast(v)]-1; // index to new array + } + else + { + + indices[i] = ocount; // new index mapping + + overts[ocount][0] = verts[v][0]; // copy old vert to new vert array + overts[ocount][1] = verts[v][1]; + overts[ocount][2] = verts[v][2]; + + for (int k=0;k=0 && ocount <= vcount ); + + usedIndices[static_cast(v)] = ocount; // assign new index remapping + + + } + } + + +} diff --git a/Engine/lib/bullet/src/LinearMath/btConvexHull.h b/Engine/lib/bullet/src/LinearMath/btConvexHull.h new file mode 100644 index 000000000..92560bddb --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/btConvexHull.h @@ -0,0 +1,241 @@ + +/* +Stan Melax Convex Hull Computation +Copyright (c) 2008 Stan Melax http://www.melax.com/ + +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. +*/ + +///includes modifications/improvements by John Ratcliff, see BringOutYourDead below. + +#ifndef CD_HULL_H +#define CD_HULL_H + +#include "LinearMath/btVector3.h" +#include "LinearMath/btAlignedObjectArray.h" + +typedef btAlignedObjectArray TUIntArray; + +class HullResult +{ +public: + HullResult(void) + { + mPolygons = true; + mNumOutputVertices = 0; + mNumFaces = 0; + mNumIndices = 0; + } + bool mPolygons; // true if indices represents polygons, false indices are triangles + unsigned int mNumOutputVertices; // number of vertices in the output hull + btAlignedObjectArray m_OutputVertices; // array of vertices + unsigned int mNumFaces; // the number of faces produced + unsigned int mNumIndices; // the total number of indices + btAlignedObjectArray m_Indices; // pointer to indices. + +// If triangles, then indices are array indexes into the vertex list. +// If polygons, indices are in the form (number of points in face) (p1, p2, p3, ..) etc.. +}; + +enum HullFlag +{ + QF_TRIANGLES = (1<<0), // report results as triangles, not polygons. + QF_REVERSE_ORDER = (1<<1), // reverse order of the triangle indices. + QF_DEFAULT = QF_TRIANGLES +}; + + +class HullDesc +{ +public: + HullDesc(void) + { + mFlags = QF_DEFAULT; + mVcount = 0; + mVertices = 0; + mVertexStride = sizeof(btVector3); + mNormalEpsilon = 0.001f; + mMaxVertices = 4096; // maximum number of points to be considered for a convex hull. + mMaxFaces = 4096; + }; + + HullDesc(HullFlag flag, + unsigned int vcount, + const btVector3 *vertices, + unsigned int stride = sizeof(btVector3)) + { + mFlags = flag; + mVcount = vcount; + mVertices = vertices; + mVertexStride = stride; + mNormalEpsilon = btScalar(0.001); + mMaxVertices = 4096; + } + + bool HasHullFlag(HullFlag flag) const + { + if ( mFlags & flag ) return true; + return false; + } + + void SetHullFlag(HullFlag flag) + { + mFlags|=flag; + } + + void ClearHullFlag(HullFlag flag) + { + mFlags&=~flag; + } + + unsigned int mFlags; // flags to use when generating the convex hull. + unsigned int mVcount; // number of vertices in the input point cloud + const btVector3 *mVertices; // the array of vertices. + unsigned int mVertexStride; // the stride of each vertex, in bytes. + btScalar mNormalEpsilon; // the epsilon for removing duplicates. This is a normalized value, if normalized bit is on. + unsigned int mMaxVertices; // maximum number of vertices to be considered for the hull! + unsigned int mMaxFaces; +}; + +enum HullError +{ + QE_OK, // success! + QE_FAIL // failed. +}; + +class btPlane +{ + public: + btVector3 normal; + btScalar dist; // distance below origin - the D from plane equasion Ax+By+Cz+D=0 + btPlane(const btVector3 &n,btScalar d):normal(n),dist(d){} + btPlane():normal(),dist(0){} + +}; + + + +class ConvexH +{ + public: + class HalfEdge + { + public: + short ea; // the other half of the edge (index into edges list) + unsigned char v; // the vertex at the start of this edge (index into vertices list) + unsigned char p; // the facet on which this edge lies (index into facets list) + HalfEdge(){} + HalfEdge(short _ea,unsigned char _v, unsigned char _p):ea(_ea),v(_v),p(_p){} + }; + ConvexH() + { + } + ~ConvexH() + { + } + btAlignedObjectArray vertices; + btAlignedObjectArray edges; + btAlignedObjectArray facets; + ConvexH(int vertices_size,int edges_size,int facets_size); +}; + + +class int4 +{ +public: + int x,y,z,w; + int4(){}; + int4(int _x,int _y, int _z,int _w){x=_x;y=_y;z=_z;w=_w;} + const int& operator[](int i) const {return (&x)[i];} + int& operator[](int i) {return (&x)[i];} +}; + +class PHullResult +{ +public: + + PHullResult(void) + { + mVcount = 0; + mIndexCount = 0; + mFaceCount = 0; + mVertices = 0; + } + + unsigned int mVcount; + unsigned int mIndexCount; + unsigned int mFaceCount; + btVector3* mVertices; + TUIntArray m_Indices; +}; + + + +///The HullLibrary class can create a convex hull from a collection of vertices, using the ComputeHull method. +///The btShapeHull class uses this HullLibrary to create a approximate convex mesh given a general (non-polyhedral) convex shape. +class HullLibrary +{ + + btAlignedObjectArray m_tris; + +public: + + btAlignedObjectArray m_vertexIndexMapping; + + + HullError CreateConvexHull(const HullDesc& desc, // describes the input request + HullResult& result); // contains the resulst + HullError ReleaseResult(HullResult &result); // release memory allocated for this result, we are done with it. + +private: + + bool ComputeHull(unsigned int vcount,const btVector3 *vertices,PHullResult &result,unsigned int vlimit); + + class btHullTriangle* allocateTriangle(int a,int b,int c); + void deAllocateTriangle(btHullTriangle*); + void b2bfix(btHullTriangle* s,btHullTriangle*t); + + void removeb2b(btHullTriangle* s,btHullTriangle*t); + + void checkit(btHullTriangle *t); + + btHullTriangle* extrudable(btScalar epsilon); + + int calchull(btVector3 *verts,int verts_count, TUIntArray& tris_out, int &tris_count,int vlimit); + + int calchullgen(btVector3 *verts,int verts_count, int vlimit); + + int4 FindSimplex(btVector3 *verts,int verts_count,btAlignedObjectArray &allow); + + class ConvexH* ConvexHCrop(ConvexH& convex,const btPlane& slice); + + void extrude(class btHullTriangle* t0,int v); + + ConvexH* test_cube(); + + //BringOutYourDead (John Ratcliff): When you create a convex hull you hand it a large input set of vertices forming a 'point cloud'. + //After the hull is generated it give you back a set of polygon faces which index the *original* point cloud. + //The thing is, often times, there are many 'dead vertices' in the point cloud that are on longer referenced by the hull. + //The routine 'BringOutYourDead' find only the referenced vertices, copies them to an new buffer, and re-indexes the hull so that it is a minimal representation. + void BringOutYourDead(const btVector3* verts,unsigned int vcount, btVector3* overts,unsigned int &ocount,unsigned int* indices,unsigned indexcount); + + bool CleanupVertices(unsigned int svcount, + const btVector3* svertices, + unsigned int stride, + unsigned int &vcount, // output number of vertices + btVector3* vertices, // location to store the results. + btScalar normalepsilon, + btVector3& scale); +}; + + +#endif + diff --git a/Engine/lib/bullet/src/LinearMath/btDefaultMotionState.h b/Engine/lib/bullet/src/LinearMath/btDefaultMotionState.h new file mode 100644 index 000000000..d758f77ed --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/btDefaultMotionState.h @@ -0,0 +1,38 @@ +#ifndef DEFAULT_MOTION_STATE_H +#define DEFAULT_MOTION_STATE_H + +///The btDefaultMotionState provides a common implementation to synchronize world transforms with offsets. +struct btDefaultMotionState : public btMotionState +{ + btTransform m_graphicsWorldTrans; + btTransform m_centerOfMassOffset; + btTransform m_startWorldTrans; + void* m_userPointer; + + btDefaultMotionState(const btTransform& startTrans = btTransform::getIdentity(),const btTransform& centerOfMassOffset = btTransform::getIdentity()) + : m_graphicsWorldTrans(startTrans), + m_centerOfMassOffset(centerOfMassOffset), + m_startWorldTrans(startTrans), + m_userPointer(0) + + { + } + + ///synchronizes world transform from user to physics + virtual void getWorldTransform(btTransform& centerOfMassWorldTrans ) const + { + centerOfMassWorldTrans = m_centerOfMassOffset.inverse() * m_graphicsWorldTrans ; + } + + ///synchronizes world transform from physics to user + ///Bullet only calls the update of worldtransform for active objects + virtual void setWorldTransform(const btTransform& centerOfMassWorldTrans) + { + m_graphicsWorldTrans = centerOfMassWorldTrans * m_centerOfMassOffset ; + } + + + +}; + +#endif //DEFAULT_MOTION_STATE_H diff --git a/Engine/lib/bullet/src/LinearMath/btGeometryUtil.cpp b/Engine/lib/bullet/src/LinearMath/btGeometryUtil.cpp new file mode 100644 index 000000000..5ac230f71 --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/btGeometryUtil.cpp @@ -0,0 +1,185 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / Erwin Coumans http://continuousphysics.com/Bullet/ + +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 "btGeometryUtil.h" + + +/* + Make sure this dummy function never changes so that it + can be used by probes that are checking whether the + library is actually installed. +*/ +extern "C" +{ + void btBulletMathProbe (); + + void btBulletMathProbe () {} +} + + +bool btGeometryUtil::isPointInsidePlanes(const btAlignedObjectArray& planeEquations, const btVector3& point, btScalar margin) +{ + int numbrushes = planeEquations.size(); + for (int i=0;ibtScalar(0.)) + { + return false; + } + } + return true; + +} + + +bool btGeometryUtil::areVerticesBehindPlane(const btVector3& planeNormal, const btAlignedObjectArray& vertices, btScalar margin) +{ + int numvertices = vertices.size(); + for (int i=0;ibtScalar(0.)) + { + return false; + } + } + return true; +} + +bool notExist(const btVector3& planeEquation,const btAlignedObjectArray& planeEquations); + +bool notExist(const btVector3& planeEquation,const btAlignedObjectArray& planeEquations) +{ + int numbrushes = planeEquations.size(); + for (int i=0;i btScalar(0.999)) + { + return false; + } + } + return true; +} + +void btGeometryUtil::getPlaneEquationsFromVertices(btAlignedObjectArray& vertices, btAlignedObjectArray& planeEquationsOut ) +{ + const int numvertices = vertices.size(); + // brute force: + for (int i=0;i btScalar(0.0001)) + { + planeEquation.normalize(); + if (notExist(planeEquation,planeEquationsOut)) + { + planeEquation[3] = -planeEquation.dot(N1); + + //check if inside, and replace supportingVertexOut if needed + if (areVerticesBehindPlane(planeEquation,vertices,btScalar(0.01))) + { + planeEquationsOut.push_back(planeEquation); + } + } + } + normalSign = btScalar(-1.); + } + + } + } + } + +} + +void btGeometryUtil::getVerticesFromPlaneEquations(const btAlignedObjectArray& planeEquations , btAlignedObjectArray& verticesOut ) +{ + const int numbrushes = planeEquations.size(); + // brute force: + for (int i=0;i btScalar(0.0001) ) && + ( n3n1.length2() > btScalar(0.0001) ) && + ( n1n2.length2() > btScalar(0.0001) ) ) + { + //point P out of 3 plane equations: + + // d1 ( N2 * N3 ) + d2 ( N3 * N1 ) + d3 ( N1 * N2 ) + //P = ------------------------------------------------------------------------- + // N1 . ( N2 * N3 ) + + + btScalar quotient = (N1.dot(n2n3)); + if (btFabs(quotient) > btScalar(0.000001)) + { + quotient = btScalar(-1.) / quotient; + n2n3 *= N1[3]; + n3n1 *= N2[3]; + n1n2 *= N3[3]; + btVector3 potentialVertex = n2n3; + potentialVertex += n3n1; + potentialVertex += n1n2; + potentialVertex *= quotient; + + //check if inside, and replace supportingVertexOut if needed + if (isPointInsidePlanes(planeEquations,potentialVertex,btScalar(0.01))) + { + verticesOut.push_back(potentialVertex); + } + } + } + } + } + } +} + diff --git a/Engine/lib/bullet/src/LinearMath/btGeometryUtil.h b/Engine/lib/bullet/src/LinearMath/btGeometryUtil.h new file mode 100644 index 000000000..a4b13b456 --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/btGeometryUtil.h @@ -0,0 +1,42 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / Erwin Coumans http://continuousphysics.com/Bullet/ + +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 BT_GEOMETRY_UTIL_H +#define BT_GEOMETRY_UTIL_H + +#include "btVector3.h" +#include "btAlignedObjectArray.h" + +///The btGeometryUtil helper class provides a few methods to convert between plane equations and vertices. +class btGeometryUtil +{ + public: + + + static void getPlaneEquationsFromVertices(btAlignedObjectArray& vertices, btAlignedObjectArray& planeEquationsOut ); + + static void getVerticesFromPlaneEquations(const btAlignedObjectArray& planeEquations , btAlignedObjectArray& verticesOut ); + + static bool isInside(const btAlignedObjectArray& vertices, const btVector3& planeNormal, btScalar margin); + + static bool isPointInsidePlanes(const btAlignedObjectArray& planeEquations, const btVector3& point, btScalar margin); + + static bool areVerticesBehindPlane(const btVector3& planeNormal, const btAlignedObjectArray& vertices, btScalar margin); + +}; + + +#endif //BT_GEOMETRY_UTIL_H + diff --git a/Engine/lib/bullet/src/LinearMath/btHashMap.h b/Engine/lib/bullet/src/LinearMath/btHashMap.h new file mode 100644 index 000000000..fbe07d5be --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/btHashMap.h @@ -0,0 +1,370 @@ +#ifndef BT_HASH_MAP_H +#define BT_HASH_MAP_H + +#include "btAlignedObjectArray.h" + +///very basic hashable string implementation, compatible with btHashMap +struct btHashString +{ + const char* m_string; + unsigned int m_hash; + + SIMD_FORCE_INLINE unsigned int getHash()const + { + return m_hash; + } + + btHashString(const char* name) + :m_string(name) + { + /* magic numbers from http://www.isthe.com/chongo/tech/comp/fnv/ */ + static const unsigned int InitialFNV = 2166136261; + static const unsigned int FNVMultiple = 16777619; + + /* Fowler / Noll / Vo (FNV) Hash */ + unsigned int hash = InitialFNV; + + for(int i = 0; m_string[i]; i++) + { + hash = hash ^ (m_string[i]); /* xor the low 8 bits */ + hash = hash * FNVMultiple; /* multiply by the magic number */ + } + m_hash = hash; + } + + int portableStringCompare(const char* src, const char* dst) const + { + int ret = 0 ; + + while( ! (ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst) + ++src, ++dst; + + if ( ret < 0 ) + ret = -1 ; + else if ( ret > 0 ) + ret = 1 ; + + return( ret ); + } + + const bool equals(const btHashString& other) const + { + return (m_string == other.m_string) || + (0==portableStringCompare(m_string,other.m_string)); + + } + +}; + +const int BT_HASH_NULL=0xffffffff; + +template +class btHashKey +{ + int m_uid; +public: + + btHashKey(int uid) + :m_uid(uid) + { + } + + int getUid1() const + { + return m_uid; + } + + bool equals(const btHashKey& other) const + { + return getUid1() == other.getUid1(); + } + //to our success + SIMD_FORCE_INLINE unsigned int getHash()const + { + int key = m_uid; + // Thomas Wang's hash + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += ~(key << 11); + key ^= (key >> 16); + return key; + } + + +}; + + +template +class btHashKeyPtr +{ + int m_uid; +public: + + btHashKeyPtr(int uid) + :m_uid(uid) + { + } + + int getUid1() const + { + return m_uid; + } + + bool equals(const btHashKeyPtr& other) const + { + return getUid1() == other.getUid1(); + } + + //to our success + SIMD_FORCE_INLINE unsigned int getHash()const + { + int key = m_uid; + // Thomas Wang's hash + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += ~(key << 11); + key ^= (key >> 16); + return key; + } + + +}; + +///The btHashMap template class implements a generic and lightweight hashmap. +///A basic sample of how to use btHashMap is located in Demos\BasicDemo\main.cpp +template +class btHashMap +{ + + btAlignedObjectArray m_hashTable; + btAlignedObjectArray m_next; + + btAlignedObjectArray m_valueArray; + btAlignedObjectArray m_keyArray; + + void growTables(const Key& key) + { + int newCapacity = m_valueArray.capacity(); + + if (m_hashTable.size() < newCapacity) + { + //grow hashtable and next table + int curHashtableSize = m_hashTable.size(); + + m_hashTable.resize(newCapacity); + m_next.resize(newCapacity); + + int i; + + for (i= 0; i < newCapacity; ++i) + { + m_hashTable[i] = BT_HASH_NULL; + } + for (i = 0; i < newCapacity; ++i) + { + m_next[i] = BT_HASH_NULL; + } + + for(i=0;i= (unsigned int)m_hashTable.size()) + { + return BT_HASH_NULL; + } + + int index = m_hashTable[hash]; + while ((index != BT_HASH_NULL) && key.equals(m_keyArray[index]) == false) + { + index = m_next[index]; + } + return index; + } + + void clear() + { + m_hashTable.clear(); + m_next.clear(); + m_valueArray.clear(); + m_keyArray.clear(); + } + +}; + +#endif //BT_HASH_MAP_H diff --git a/Engine/lib/bullet/src/LinearMath/btIDebugDraw.h b/Engine/lib/bullet/src/LinearMath/btIDebugDraw.h new file mode 100644 index 000000000..a0cbf1dfc --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/btIDebugDraw.h @@ -0,0 +1,287 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2009 Erwin Coumans http://bulletphysics.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 IDEBUG_DRAW__H +#define IDEBUG_DRAW__H + +#include "btVector3.h" +#include "btTransform.h" + + +///The btIDebugDraw interface class allows hooking up a debug renderer to visually debug simulations. +///Typical use case: create a debug drawer object, and assign it to a btCollisionWorld or btDynamicsWorld using setDebugDrawer and call debugDrawWorld. +///A class that implements the btIDebugDraw interface has to implement the drawLine method at a minimum. +class btIDebugDraw +{ + public: + + enum DebugDrawModes + { + DBG_NoDebug=0, + DBG_DrawWireframe = 1, + DBG_DrawAabb=2, + DBG_DrawFeaturesText=4, + DBG_DrawContactPoints=8, + DBG_NoDeactivation=16, + DBG_NoHelpText = 32, + DBG_DrawText=64, + DBG_ProfileTimings = 128, + DBG_EnableSatComparison = 256, + DBG_DisableBulletLCP = 512, + DBG_EnableCCD = 1024, + DBG_DrawConstraints = (1 << 11), + DBG_DrawConstraintLimits = (1 << 12), + DBG_FastWireframe = (1<<13), + DBG_MAX_DEBUG_DRAW_MODE + }; + + virtual ~btIDebugDraw() {}; + + virtual void drawLine(const btVector3& from,const btVector3& to, const btVector3& fromColor, const btVector3& toColor) + { + drawLine (from, to, fromColor); + } + + virtual void drawBox (const btVector3& boxMin, const btVector3& boxMax, const btVector3& color, btScalar alpha) + { + } + + virtual void drawSphere (const btVector3& p, btScalar radius, const btVector3& color) + { + } + + virtual void drawLine(const btVector3& from,const btVector3& to,const btVector3& color)=0; + + virtual void drawTriangle(const btVector3& v0,const btVector3& v1,const btVector3& v2,const btVector3& /*n0*/,const btVector3& /*n1*/,const btVector3& /*n2*/,const btVector3& color, btScalar alpha) + { + drawTriangle(v0,v1,v2,color,alpha); + } + virtual void drawTriangle(const btVector3& v0,const btVector3& v1,const btVector3& v2,const btVector3& color, btScalar /*alpha*/) + { + drawLine(v0,v1,color); + drawLine(v1,v2,color); + drawLine(v2,v0,color); + } + + virtual void drawContactPoint(const btVector3& PointOnB,const btVector3& normalOnB,btScalar distance,int lifeTime,const btVector3& color)=0; + + virtual void reportErrorWarning(const char* warningString) = 0; + + virtual void draw3dText(const btVector3& location,const char* textString) = 0; + + virtual void setDebugMode(int debugMode) =0; + + virtual int getDebugMode() const = 0; + + inline void drawAabb(const btVector3& from,const btVector3& to,const btVector3& color) + { + + btVector3 halfExtents = (to-from)* 0.5f; + btVector3 center = (to+from) *0.5f; + int i,j; + + btVector3 edgecoord(1.f,1.f,1.f),pa,pb; + for (i=0;i<4;i++) + { + for (j=0;j<3;j++) + { + pa = btVector3(edgecoord[0]*halfExtents[0], edgecoord[1]*halfExtents[1], + edgecoord[2]*halfExtents[2]); + pa+=center; + + int othercoord = j%3; + edgecoord[othercoord]*=-1.f; + pb = btVector3(edgecoord[0]*halfExtents[0], edgecoord[1]*halfExtents[1], + edgecoord[2]*halfExtents[2]); + pb+=center; + + drawLine(pa,pb,color); + } + edgecoord = btVector3(-1.f,-1.f,-1.f); + if (i<3) + edgecoord[i]*=-1.f; + } + } + void drawTransform(const btTransform& transform, btScalar orthoLen) + { + btVector3 start = transform.getOrigin(); + drawLine(start, start+transform.getBasis() * btVector3(orthoLen, 0, 0), btVector3(0.7f,0,0)); + drawLine(start, start+transform.getBasis() * btVector3(0, orthoLen, 0), btVector3(0,0.7f,0)); + drawLine(start, start+transform.getBasis() * btVector3(0, 0, orthoLen), btVector3(0,0,0.7f)); + } + + void drawArc(const btVector3& center, const btVector3& normal, const btVector3& axis, btScalar radiusA, btScalar radiusB, btScalar minAngle, btScalar maxAngle, + const btVector3& color, bool drawSect, btScalar stepDegrees = btScalar(10.f)) + { + const btVector3& vx = axis; + btVector3 vy = normal.cross(axis); + btScalar step = stepDegrees * SIMD_RADS_PER_DEG; + int nSteps = (int)((maxAngle - minAngle) / step); + if(!nSteps) nSteps = 1; + btVector3 prev = center + radiusA * vx * btCos(minAngle) + radiusB * vy * btSin(minAngle); + if(drawSect) + { + drawLine(center, prev, color); + } + for(int i = 1; i <= nSteps; i++) + { + btScalar angle = minAngle + (maxAngle - minAngle) * btScalar(i) / btScalar(nSteps); + btVector3 next = center + radiusA * vx * btCos(angle) + radiusB * vy * btSin(angle); + drawLine(prev, next, color); + prev = next; + } + if(drawSect) + { + drawLine(center, prev, color); + } + } + void drawSpherePatch(const btVector3& center, const btVector3& up, const btVector3& axis, btScalar radius, + btScalar minTh, btScalar maxTh, btScalar minPs, btScalar maxPs, const btVector3& color, btScalar stepDegrees = btScalar(10.f)) + { + btVector3 vA[74]; + btVector3 vB[74]; + btVector3 *pvA = vA, *pvB = vB, *pT; + btVector3 npole = center + up * radius; + btVector3 spole = center - up * radius; + btVector3 arcStart; + btScalar step = stepDegrees * SIMD_RADS_PER_DEG; + const btVector3& kv = up; + const btVector3& iv = axis; + btVector3 jv = kv.cross(iv); + bool drawN = false; + bool drawS = false; + if(minTh <= -SIMD_HALF_PI) + { + minTh = -SIMD_HALF_PI + step; + drawN = true; + } + if(maxTh >= SIMD_HALF_PI) + { + maxTh = SIMD_HALF_PI - step; + drawS = true; + } + if(minTh > maxTh) + { + minTh = -SIMD_HALF_PI + step; + maxTh = SIMD_HALF_PI - step; + drawN = drawS = true; + } + int n_hor = (int)((maxTh - minTh) / step) + 1; + if(n_hor < 2) n_hor = 2; + btScalar step_h = (maxTh - minTh) / btScalar(n_hor - 1); + bool isClosed = false; + if(minPs > maxPs) + { + minPs = -SIMD_PI + step; + maxPs = SIMD_PI; + isClosed = true; + } + else if((maxPs - minPs) >= SIMD_PI * btScalar(2.f)) + { + isClosed = true; + } + else + { + isClosed = false; + } + int n_vert = (int)((maxPs - minPs) / step) + 1; + if(n_vert < 2) n_vert = 2; + btScalar step_v = (maxPs - minPs) / btScalar(n_vert - 1); + for(int i = 0; i < n_hor; i++) + { + btScalar th = minTh + btScalar(i) * step_h; + btScalar sth = radius * btSin(th); + btScalar cth = radius * btCos(th); + for(int j = 0; j < n_vert; j++) + { + btScalar psi = minPs + btScalar(j) * step_v; + btScalar sps = btSin(psi); + btScalar cps = btCos(psi); + pvB[j] = center + cth * cps * iv + cth * sps * jv + sth * kv; + if(i) + { + drawLine(pvA[j], pvB[j], color); + } + else if(drawS) + { + drawLine(spole, pvB[j], color); + } + if(j) + { + drawLine(pvB[j-1], pvB[j], color); + } + else + { + arcStart = pvB[j]; + } + if((i == (n_hor - 1)) && drawN) + { + drawLine(npole, pvB[j], color); + } + if(isClosed) + { + if(j == (n_vert-1)) + { + drawLine(arcStart, pvB[j], color); + } + } + else + { + if(((!i) || (i == (n_hor-1))) && ((!j) || (j == (n_vert-1)))) + { + drawLine(center, pvB[j], color); + } + } + } + pT = pvA; pvA = pvB; pvB = pT; + } + } + + void drawBox(const btVector3& bbMin, const btVector3& bbMax, const btVector3& color) + { + drawLine(btVector3(bbMin[0], bbMin[1], bbMin[2]), btVector3(bbMax[0], bbMin[1], bbMin[2]), color); + drawLine(btVector3(bbMax[0], bbMin[1], bbMin[2]), btVector3(bbMax[0], bbMax[1], bbMin[2]), color); + drawLine(btVector3(bbMax[0], bbMax[1], bbMin[2]), btVector3(bbMin[0], bbMax[1], bbMin[2]), color); + drawLine(btVector3(bbMin[0], bbMax[1], bbMin[2]), btVector3(bbMin[0], bbMin[1], bbMin[2]), color); + drawLine(btVector3(bbMin[0], bbMin[1], bbMin[2]), btVector3(bbMin[0], bbMin[1], bbMax[2]), color); + drawLine(btVector3(bbMax[0], bbMin[1], bbMin[2]), btVector3(bbMax[0], bbMin[1], bbMax[2]), color); + drawLine(btVector3(bbMax[0], bbMax[1], bbMin[2]), btVector3(bbMax[0], bbMax[1], bbMax[2]), color); + drawLine(btVector3(bbMin[0], bbMax[1], bbMin[2]), btVector3(bbMin[0], bbMax[1], bbMax[2]), color); + drawLine(btVector3(bbMin[0], bbMin[1], bbMax[2]), btVector3(bbMax[0], bbMin[1], bbMax[2]), color); + drawLine(btVector3(bbMax[0], bbMin[1], bbMax[2]), btVector3(bbMax[0], bbMax[1], bbMax[2]), color); + drawLine(btVector3(bbMax[0], bbMax[1], bbMax[2]), btVector3(bbMin[0], bbMax[1], bbMax[2]), color); + drawLine(btVector3(bbMin[0], bbMax[1], bbMax[2]), btVector3(bbMin[0], bbMin[1], bbMax[2]), color); + } + void drawBox(const btVector3& bbMin, const btVector3& bbMax, const btTransform& trans, const btVector3& color) + { + drawLine(trans * btVector3(bbMin[0], bbMin[1], bbMin[2]), trans * btVector3(bbMax[0], bbMin[1], bbMin[2]), color); + drawLine(trans * btVector3(bbMax[0], bbMin[1], bbMin[2]), trans * btVector3(bbMax[0], bbMax[1], bbMin[2]), color); + drawLine(trans * btVector3(bbMax[0], bbMax[1], bbMin[2]), trans * btVector3(bbMin[0], bbMax[1], bbMin[2]), color); + drawLine(trans * btVector3(bbMin[0], bbMax[1], bbMin[2]), trans * btVector3(bbMin[0], bbMin[1], bbMin[2]), color); + drawLine(trans * btVector3(bbMin[0], bbMin[1], bbMin[2]), trans * btVector3(bbMin[0], bbMin[1], bbMax[2]), color); + drawLine(trans * btVector3(bbMax[0], bbMin[1], bbMin[2]), trans * btVector3(bbMax[0], bbMin[1], bbMax[2]), color); + drawLine(trans * btVector3(bbMax[0], bbMax[1], bbMin[2]), trans * btVector3(bbMax[0], bbMax[1], bbMax[2]), color); + drawLine(trans * btVector3(bbMin[0], bbMax[1], bbMin[2]), trans * btVector3(bbMin[0], bbMax[1], bbMax[2]), color); + drawLine(trans * btVector3(bbMin[0], bbMin[1], bbMax[2]), trans * btVector3(bbMax[0], bbMin[1], bbMax[2]), color); + drawLine(trans * btVector3(bbMax[0], bbMin[1], bbMax[2]), trans * btVector3(bbMax[0], bbMax[1], bbMax[2]), color); + drawLine(trans * btVector3(bbMax[0], bbMax[1], bbMax[2]), trans * btVector3(bbMin[0], bbMax[1], bbMax[2]), color); + drawLine(trans * btVector3(bbMin[0], bbMax[1], bbMax[2]), trans * btVector3(bbMin[0], bbMin[1], bbMax[2]), color); + } +}; + + +#endif //IDEBUG_DRAW__H + diff --git a/Engine/lib/bullet/src/LinearMath/btList.h b/Engine/lib/bullet/src/LinearMath/btList.h new file mode 100644 index 000000000..c87b47faf --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/btList.h @@ -0,0 +1,73 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / Erwin Coumans http://continuousphysics.com/Bullet/ + +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 GEN_LIST_H +#define GEN_LIST_H + +class btGEN_Link { +public: + btGEN_Link() : m_next(0), m_prev(0) {} + btGEN_Link(btGEN_Link *next, btGEN_Link *prev) : m_next(next), m_prev(prev) {} + + btGEN_Link *getNext() const { return m_next; } + btGEN_Link *getPrev() const { return m_prev; } + + bool isHead() const { return m_prev == 0; } + bool isTail() const { return m_next == 0; } + + void insertBefore(btGEN_Link *link) { + m_next = link; + m_prev = link->m_prev; + m_next->m_prev = this; + m_prev->m_next = this; + } + + void insertAfter(btGEN_Link *link) { + m_next = link->m_next; + m_prev = link; + m_next->m_prev = this; + m_prev->m_next = this; + } + + void remove() { + m_next->m_prev = m_prev; + m_prev->m_next = m_next; + } + +private: + btGEN_Link *m_next; + btGEN_Link *m_prev; +}; + +class btGEN_List { +public: + btGEN_List() : m_head(&m_tail, 0), m_tail(0, &m_head) {} + + btGEN_Link *getHead() const { return m_head.getNext(); } + btGEN_Link *getTail() const { return m_tail.getPrev(); } + + void addHead(btGEN_Link *link) { link->insertAfter(&m_head); } + void addTail(btGEN_Link *link) { link->insertBefore(&m_tail); } + +private: + btGEN_Link m_head; + btGEN_Link m_tail; +}; + +#endif + + + diff --git a/Engine/lib/bullet/src/LinearMath/btMatrix3x3.h b/Engine/lib/bullet/src/LinearMath/btMatrix3x3.h new file mode 100644 index 000000000..ea26bbbba --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/btMatrix3x3.h @@ -0,0 +1,618 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / Erwin Coumans http://continuousphysics.com/Bullet/ + +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 btMatrix3x3_H +#define btMatrix3x3_H + +#include "btScalar.h" + +#include "btVector3.h" +#include "btQuaternion.h" + + + +/**@brief The btMatrix3x3 class implements a 3x3 rotation matrix, to perform linear algebra in combination with btQuaternion, btTransform and btVector3. + * Make sure to only include a pure orthogonal matrix without scaling. */ +class btMatrix3x3 { + public: + /** @brief No initializaion constructor */ + btMatrix3x3 () {} + +// explicit btMatrix3x3(const btScalar *m) { setFromOpenGLSubMatrix(m); } + + /**@brief Constructor from Quaternion */ + explicit btMatrix3x3(const btQuaternion& q) { setRotation(q); } + /* + template + Matrix3x3(const btScalar& yaw, const btScalar& pitch, const btScalar& roll) + { + setEulerYPR(yaw, pitch, roll); + } + */ + /** @brief Constructor with row major formatting */ + btMatrix3x3(const btScalar& xx, const btScalar& xy, const btScalar& xz, + const btScalar& yx, const btScalar& yy, const btScalar& yz, + const btScalar& zx, const btScalar& zy, const btScalar& zz) + { + setValue(xx, xy, xz, + yx, yy, yz, + zx, zy, zz); + } + /** @brief Copy constructor */ + SIMD_FORCE_INLINE btMatrix3x3 (const btMatrix3x3& other) + { + m_el[0] = other.m_el[0]; + m_el[1] = other.m_el[1]; + m_el[2] = other.m_el[2]; + } + /** @brief Assignment Operator */ + SIMD_FORCE_INLINE btMatrix3x3& operator=(const btMatrix3x3& other) + { + m_el[0] = other.m_el[0]; + m_el[1] = other.m_el[1]; + m_el[2] = other.m_el[2]; + return *this; + } + + /** @brief Get a column of the matrix as a vector + * @param i Column number 0 indexed */ + SIMD_FORCE_INLINE btVector3 getColumn(int i) const + { + return btVector3(m_el[0][i],m_el[1][i],m_el[2][i]); + } + + + /** @brief Get a row of the matrix as a vector + * @param i Row number 0 indexed */ + SIMD_FORCE_INLINE const btVector3& getRow(int i) const + { + btFullAssert(0 <= i && i < 3); + return m_el[i]; + } + + /** @brief Get a mutable reference to a row of the matrix as a vector + * @param i Row number 0 indexed */ + SIMD_FORCE_INLINE btVector3& operator[](int i) + { + btFullAssert(0 <= i && i < 3); + return m_el[i]; + } + + /** @brief Get a const reference to a row of the matrix as a vector + * @param i Row number 0 indexed */ + SIMD_FORCE_INLINE const btVector3& operator[](int i) const + { + btFullAssert(0 <= i && i < 3); + return m_el[i]; + } + + /** @brief Multiply by the target matrix on the right + * @param m Rotation matrix to be applied + * Equivilant to this = this * m */ + btMatrix3x3& operator*=(const btMatrix3x3& m); + + /** @brief Set from a carray of btScalars + * @param m A pointer to the beginning of an array of 9 btScalars */ + void setFromOpenGLSubMatrix(const btScalar *m) + { + m_el[0].setValue(m[0],m[4],m[8]); + m_el[1].setValue(m[1],m[5],m[9]); + m_el[2].setValue(m[2],m[6],m[10]); + + } + /** @brief Set the values of the matrix explicitly (row major) + * @param xx Top left + * @param xy Top Middle + * @param xz Top Right + * @param yx Middle Left + * @param yy Middle Middle + * @param yz Middle Right + * @param zx Bottom Left + * @param zy Bottom Middle + * @param zz Bottom Right*/ + void setValue(const btScalar& xx, const btScalar& xy, const btScalar& xz, + const btScalar& yx, const btScalar& yy, const btScalar& yz, + const btScalar& zx, const btScalar& zy, const btScalar& zz) + { + m_el[0].setValue(xx,xy,xz); + m_el[1].setValue(yx,yy,yz); + m_el[2].setValue(zx,zy,zz); + } + + /** @brief Set the matrix from a quaternion + * @param q The Quaternion to match */ + void setRotation(const btQuaternion& q) + { + btScalar d = q.length2(); + btFullAssert(d != btScalar(0.0)); + btScalar s = btScalar(2.0) / d; + btScalar xs = q.x() * s, ys = q.y() * s, zs = q.z() * s; + btScalar wx = q.w() * xs, wy = q.w() * ys, wz = q.w() * zs; + btScalar xx = q.x() * xs, xy = q.x() * ys, xz = q.x() * zs; + btScalar yy = q.y() * ys, yz = q.y() * zs, zz = q.z() * zs; + setValue(btScalar(1.0) - (yy + zz), xy - wz, xz + wy, + xy + wz, btScalar(1.0) - (xx + zz), yz - wx, + xz - wy, yz + wx, btScalar(1.0) - (xx + yy)); + } + + + /** @brief Set the matrix from euler angles using YPR around YXZ respectively + * @param yaw Yaw about Y axis + * @param pitch Pitch about X axis + * @param roll Roll about Z axis + */ + void setEulerYPR(const btScalar& yaw, const btScalar& pitch, const btScalar& roll) + { + setEulerZYX(roll, pitch, yaw); + } + + /** @brief Set the matrix from euler angles YPR around ZYX axes + * @param eulerX Roll about X axis + * @param eulerY Pitch around Y axis + * @param eulerZ Yaw aboud Z axis + * + * These angles are used to produce a rotation matrix. The euler + * angles are applied in ZYX order. I.e a vector is first rotated + * about X then Y and then Z + **/ + void setEulerZYX(btScalar eulerX,btScalar eulerY,btScalar eulerZ) { + ///@todo proposed to reverse this since it's labeled zyx but takes arguments xyz and it will match all other parts of the code + btScalar ci ( btCos(eulerX)); + btScalar cj ( btCos(eulerY)); + btScalar ch ( btCos(eulerZ)); + btScalar si ( btSin(eulerX)); + btScalar sj ( btSin(eulerY)); + btScalar sh ( btSin(eulerZ)); + btScalar cc = ci * ch; + btScalar cs = ci * sh; + btScalar sc = si * ch; + btScalar ss = si * sh; + + setValue(cj * ch, sj * sc - cs, sj * cc + ss, + cj * sh, sj * ss + cc, sj * cs - sc, + -sj, cj * si, cj * ci); + } + + /**@brief Set the matrix to the identity */ + void setIdentity() + { + setValue(btScalar(1.0), btScalar(0.0), btScalar(0.0), + btScalar(0.0), btScalar(1.0), btScalar(0.0), + btScalar(0.0), btScalar(0.0), btScalar(1.0)); + } + + static const btMatrix3x3& getIdentity() + { + static const btMatrix3x3 identityMatrix(btScalar(1.0), btScalar(0.0), btScalar(0.0), + btScalar(0.0), btScalar(1.0), btScalar(0.0), + btScalar(0.0), btScalar(0.0), btScalar(1.0)); + return identityMatrix; + } + + /**@brief Fill the values of the matrix into a 9 element array + * @param m The array to be filled */ + void getOpenGLSubMatrix(btScalar *m) const + { + m[0] = btScalar(m_el[0].x()); + m[1] = btScalar(m_el[1].x()); + m[2] = btScalar(m_el[2].x()); + m[3] = btScalar(0.0); + m[4] = btScalar(m_el[0].y()); + m[5] = btScalar(m_el[1].y()); + m[6] = btScalar(m_el[2].y()); + m[7] = btScalar(0.0); + m[8] = btScalar(m_el[0].z()); + m[9] = btScalar(m_el[1].z()); + m[10] = btScalar(m_el[2].z()); + m[11] = btScalar(0.0); + } + + /**@brief Get the matrix represented as a quaternion + * @param q The quaternion which will be set */ + void getRotation(btQuaternion& q) const + { + btScalar trace = m_el[0].x() + m_el[1].y() + m_el[2].z(); + btScalar temp[4]; + + if (trace > btScalar(0.0)) + { + btScalar s = btSqrt(trace + btScalar(1.0)); + temp[3]=(s * btScalar(0.5)); + s = btScalar(0.5) / s; + + temp[0]=((m_el[2].y() - m_el[1].z()) * s); + temp[1]=((m_el[0].z() - m_el[2].x()) * s); + temp[2]=((m_el[1].x() - m_el[0].y()) * s); + } + else + { + int i = m_el[0].x() < m_el[1].y() ? + (m_el[1].y() < m_el[2].z() ? 2 : 1) : + (m_el[0].x() < m_el[2].z() ? 2 : 0); + int j = (i + 1) % 3; + int k = (i + 2) % 3; + + btScalar s = btSqrt(m_el[i][i] - m_el[j][j] - m_el[k][k] + btScalar(1.0)); + temp[i] = s * btScalar(0.5); + s = btScalar(0.5) / s; + + temp[3] = (m_el[k][j] - m_el[j][k]) * s; + temp[j] = (m_el[j][i] + m_el[i][j]) * s; + temp[k] = (m_el[k][i] + m_el[i][k]) * s; + } + q.setValue(temp[0],temp[1],temp[2],temp[3]); + } + + /**@brief Get the matrix represented as euler angles around YXZ, roundtrip with setEulerYPR + * @param yaw Yaw around Y axis + * @param pitch Pitch around X axis + * @param roll around Z axis */ + void getEulerYPR(btScalar& yaw, btScalar& pitch, btScalar& roll) const + { + + // first use the normal calculus + yaw = btScalar(btAtan2(m_el[1].x(), m_el[0].x())); + pitch = btScalar(btAsin(-m_el[2].x())); + roll = btScalar(btAtan2(m_el[2].y(), m_el[2].z())); + + // on pitch = +/-HalfPI + if (btFabs(pitch)==SIMD_HALF_PI) + { + if (yaw>0) + yaw-=SIMD_PI; + else + yaw+=SIMD_PI; + + if (roll>0) + roll-=SIMD_PI; + else + roll+=SIMD_PI; + } + }; + + + /**@brief Get the matrix represented as euler angles around ZYX + * @param yaw Yaw around X axis + * @param pitch Pitch around Y axis + * @param roll around X axis + * @param solution_number Which solution of two possible solutions ( 1 or 2) are possible values*/ + void getEulerZYX(btScalar& yaw, btScalar& pitch, btScalar& roll, unsigned int solution_number = 1) const + { + struct Euler{btScalar yaw, pitch, roll;}; + Euler euler_out; + Euler euler_out2; //second solution + //get the pointer to the raw data + + // Check that pitch is not at a singularity + if (btFabs(m_el[2].x()) >= 1) + { + euler_out.yaw = 0; + euler_out2.yaw = 0; + + // From difference of angles formula + btScalar delta = btAtan2(m_el[0].x(),m_el[0].z()); + if (m_el[2].x() > 0) //gimbal locked up + { + euler_out.pitch = SIMD_PI / btScalar(2.0); + euler_out2.pitch = SIMD_PI / btScalar(2.0); + euler_out.roll = euler_out.pitch + delta; + euler_out2.roll = euler_out.pitch + delta; + } + else // gimbal locked down + { + euler_out.pitch = -SIMD_PI / btScalar(2.0); + euler_out2.pitch = -SIMD_PI / btScalar(2.0); + euler_out.roll = -euler_out.pitch + delta; + euler_out2.roll = -euler_out.pitch + delta; + } + } + else + { + euler_out.pitch = - btAsin(m_el[2].x()); + euler_out2.pitch = SIMD_PI - euler_out.pitch; + + euler_out.roll = btAtan2(m_el[2].y()/btCos(euler_out.pitch), + m_el[2].z()/btCos(euler_out.pitch)); + euler_out2.roll = btAtan2(m_el[2].y()/btCos(euler_out2.pitch), + m_el[2].z()/btCos(euler_out2.pitch)); + + euler_out.yaw = btAtan2(m_el[1].x()/btCos(euler_out.pitch), + m_el[0].x()/btCos(euler_out.pitch)); + euler_out2.yaw = btAtan2(m_el[1].x()/btCos(euler_out2.pitch), + m_el[0].x()/btCos(euler_out2.pitch)); + } + + if (solution_number == 1) + { + yaw = euler_out.yaw; + pitch = euler_out.pitch; + roll = euler_out.roll; + } + else + { + yaw = euler_out2.yaw; + pitch = euler_out2.pitch; + roll = euler_out2.roll; + } + } + + /**@brief Create a scaled copy of the matrix + * @param s Scaling vector The elements of the vector will scale each column */ + + btMatrix3x3 scaled(const btVector3& s) const + { + return btMatrix3x3(m_el[0].x() * s.x(), m_el[0].y() * s.y(), m_el[0].z() * s.z(), + m_el[1].x() * s.x(), m_el[1].y() * s.y(), m_el[1].z() * s.z(), + m_el[2].x() * s.x(), m_el[2].y() * s.y(), m_el[2].z() * s.z()); + } + + /**@brief Return the determinant of the matrix */ + btScalar determinant() const; + /**@brief Return the adjoint of the matrix */ + btMatrix3x3 adjoint() const; + /**@brief Return the matrix with all values non negative */ + btMatrix3x3 absolute() const; + /**@brief Return the transpose of the matrix */ + btMatrix3x3 transpose() const; + /**@brief Return the inverse of the matrix */ + btMatrix3x3 inverse() const; + + btMatrix3x3 transposeTimes(const btMatrix3x3& m) const; + btMatrix3x3 timesTranspose(const btMatrix3x3& m) const; + + SIMD_FORCE_INLINE btScalar tdotx(const btVector3& v) const + { + return m_el[0].x() * v.x() + m_el[1].x() * v.y() + m_el[2].x() * v.z(); + } + SIMD_FORCE_INLINE btScalar tdoty(const btVector3& v) const + { + return m_el[0].y() * v.x() + m_el[1].y() * v.y() + m_el[2].y() * v.z(); + } + SIMD_FORCE_INLINE btScalar tdotz(const btVector3& v) const + { + return m_el[0].z() * v.x() + m_el[1].z() * v.y() + m_el[2].z() * v.z(); + } + + + /**@brief diagonalizes this matrix by the Jacobi method. + * @param rot stores the rotation from the coordinate system in which the matrix is diagonal to the original + * coordinate system, i.e., old_this = rot * new_this * rot^T. + * @param threshold See iteration + * @param iteration The iteration stops when all off-diagonal elements are less than the threshold multiplied + * by the sum of the absolute values of the diagonal, or when maxSteps have been executed. + * + * Note that this matrix is assumed to be symmetric. + */ + void diagonalize(btMatrix3x3& rot, btScalar threshold, int maxSteps) + { + rot.setIdentity(); + for (int step = maxSteps; step > 0; step--) + { + // find off-diagonal element [p][q] with largest magnitude + int p = 0; + int q = 1; + int r = 2; + btScalar max = btFabs(m_el[0][1]); + btScalar v = btFabs(m_el[0][2]); + if (v > max) + { + q = 2; + r = 1; + max = v; + } + v = btFabs(m_el[1][2]); + if (v > max) + { + p = 1; + q = 2; + r = 0; + max = v; + } + + btScalar t = threshold * (btFabs(m_el[0][0]) + btFabs(m_el[1][1]) + btFabs(m_el[2][2])); + if (max <= t) + { + if (max <= SIMD_EPSILON * t) + { + return; + } + step = 1; + } + + // compute Jacobi rotation J which leads to a zero for element [p][q] + btScalar mpq = m_el[p][q]; + btScalar theta = (m_el[q][q] - m_el[p][p]) / (2 * mpq); + btScalar theta2 = theta * theta; + btScalar cos; + btScalar sin; + if (theta2 * theta2 < btScalar(10 / SIMD_EPSILON)) + { + t = (theta >= 0) ? 1 / (theta + btSqrt(1 + theta2)) + : 1 / (theta - btSqrt(1 + theta2)); + cos = 1 / btSqrt(1 + t * t); + sin = cos * t; + } + else + { + // approximation for large theta-value, i.e., a nearly diagonal matrix + t = 1 / (theta * (2 + btScalar(0.5) / theta2)); + cos = 1 - btScalar(0.5) * t * t; + sin = cos * t; + } + + // apply rotation to matrix (this = J^T * this * J) + m_el[p][q] = m_el[q][p] = 0; + m_el[p][p] -= t * mpq; + m_el[q][q] += t * mpq; + btScalar mrp = m_el[r][p]; + btScalar mrq = m_el[r][q]; + m_el[r][p] = m_el[p][r] = cos * mrp - sin * mrq; + m_el[r][q] = m_el[q][r] = cos * mrq + sin * mrp; + + // apply rotation to rot (rot = rot * J) + for (int i = 0; i < 3; i++) + { + btVector3& row = rot[i]; + mrp = row[p]; + mrq = row[q]; + row[p] = cos * mrp - sin * mrq; + row[q] = cos * mrq + sin * mrp; + } + } + } + + + + protected: + /**@brief Calculate the matrix cofactor + * @param r1 The first row to use for calculating the cofactor + * @param c1 The first column to use for calculating the cofactor + * @param r1 The second row to use for calculating the cofactor + * @param c1 The second column to use for calculating the cofactor + * See http://en.wikipedia.org/wiki/Cofactor_(linear_algebra) for more details + */ + btScalar cofac(int r1, int c1, int r2, int c2) const + { + return m_el[r1][c1] * m_el[r2][c2] - m_el[r1][c2] * m_el[r2][c1]; + } + ///Data storage for the matrix, each vector is a row of the matrix + btVector3 m_el[3]; + }; + + SIMD_FORCE_INLINE btMatrix3x3& + btMatrix3x3::operator*=(const btMatrix3x3& m) + { + setValue(m.tdotx(m_el[0]), m.tdoty(m_el[0]), m.tdotz(m_el[0]), + m.tdotx(m_el[1]), m.tdoty(m_el[1]), m.tdotz(m_el[1]), + m.tdotx(m_el[2]), m.tdoty(m_el[2]), m.tdotz(m_el[2])); + return *this; + } + + SIMD_FORCE_INLINE btScalar + btMatrix3x3::determinant() const + { + return btTriple((*this)[0], (*this)[1], (*this)[2]); + } + + + SIMD_FORCE_INLINE btMatrix3x3 + btMatrix3x3::absolute() const + { + return btMatrix3x3( + btFabs(m_el[0].x()), btFabs(m_el[0].y()), btFabs(m_el[0].z()), + btFabs(m_el[1].x()), btFabs(m_el[1].y()), btFabs(m_el[1].z()), + btFabs(m_el[2].x()), btFabs(m_el[2].y()), btFabs(m_el[2].z())); + } + + SIMD_FORCE_INLINE btMatrix3x3 + btMatrix3x3::transpose() const + { + return btMatrix3x3(m_el[0].x(), m_el[1].x(), m_el[2].x(), + m_el[0].y(), m_el[1].y(), m_el[2].y(), + m_el[0].z(), m_el[1].z(), m_el[2].z()); + } + + SIMD_FORCE_INLINE btMatrix3x3 + btMatrix3x3::adjoint() const + { + return btMatrix3x3(cofac(1, 1, 2, 2), cofac(0, 2, 2, 1), cofac(0, 1, 1, 2), + cofac(1, 2, 2, 0), cofac(0, 0, 2, 2), cofac(0, 2, 1, 0), + cofac(1, 0, 2, 1), cofac(0, 1, 2, 0), cofac(0, 0, 1, 1)); + } + + SIMD_FORCE_INLINE btMatrix3x3 + btMatrix3x3::inverse() const + { + btVector3 co(cofac(1, 1, 2, 2), cofac(1, 2, 2, 0), cofac(1, 0, 2, 1)); + btScalar det = (*this)[0].dot(co); + btFullAssert(det != btScalar(0.0)); + btScalar s = btScalar(1.0) / det; + return btMatrix3x3(co.x() * s, cofac(0, 2, 2, 1) * s, cofac(0, 1, 1, 2) * s, + co.y() * s, cofac(0, 0, 2, 2) * s, cofac(0, 2, 1, 0) * s, + co.z() * s, cofac(0, 1, 2, 0) * s, cofac(0, 0, 1, 1) * s); + } + + SIMD_FORCE_INLINE btMatrix3x3 + btMatrix3x3::transposeTimes(const btMatrix3x3& m) const + { + return btMatrix3x3( + m_el[0].x() * m[0].x() + m_el[1].x() * m[1].x() + m_el[2].x() * m[2].x(), + m_el[0].x() * m[0].y() + m_el[1].x() * m[1].y() + m_el[2].x() * m[2].y(), + m_el[0].x() * m[0].z() + m_el[1].x() * m[1].z() + m_el[2].x() * m[2].z(), + m_el[0].y() * m[0].x() + m_el[1].y() * m[1].x() + m_el[2].y() * m[2].x(), + m_el[0].y() * m[0].y() + m_el[1].y() * m[1].y() + m_el[2].y() * m[2].y(), + m_el[0].y() * m[0].z() + m_el[1].y() * m[1].z() + m_el[2].y() * m[2].z(), + m_el[0].z() * m[0].x() + m_el[1].z() * m[1].x() + m_el[2].z() * m[2].x(), + m_el[0].z() * m[0].y() + m_el[1].z() * m[1].y() + m_el[2].z() * m[2].y(), + m_el[0].z() * m[0].z() + m_el[1].z() * m[1].z() + m_el[2].z() * m[2].z()); + } + + SIMD_FORCE_INLINE btMatrix3x3 + btMatrix3x3::timesTranspose(const btMatrix3x3& m) const + { + return btMatrix3x3( + m_el[0].dot(m[0]), m_el[0].dot(m[1]), m_el[0].dot(m[2]), + m_el[1].dot(m[0]), m_el[1].dot(m[1]), m_el[1].dot(m[2]), + m_el[2].dot(m[0]), m_el[2].dot(m[1]), m_el[2].dot(m[2])); + + } + + SIMD_FORCE_INLINE btVector3 + operator*(const btMatrix3x3& m, const btVector3& v) + { + return btVector3(m[0].dot(v), m[1].dot(v), m[2].dot(v)); + } + + + SIMD_FORCE_INLINE btVector3 + operator*(const btVector3& v, const btMatrix3x3& m) + { + return btVector3(m.tdotx(v), m.tdoty(v), m.tdotz(v)); + } + + SIMD_FORCE_INLINE btMatrix3x3 + operator*(const btMatrix3x3& m1, const btMatrix3x3& m2) + { + return btMatrix3x3( + m2.tdotx( m1[0]), m2.tdoty( m1[0]), m2.tdotz( m1[0]), + m2.tdotx( m1[1]), m2.tdoty( m1[1]), m2.tdotz( m1[1]), + m2.tdotx( m1[2]), m2.tdoty( m1[2]), m2.tdotz( m1[2])); + } + +/* + SIMD_FORCE_INLINE btMatrix3x3 btMultTransposeLeft(const btMatrix3x3& m1, const btMatrix3x3& m2) { + return btMatrix3x3( + m1[0][0] * m2[0][0] + m1[1][0] * m2[1][0] + m1[2][0] * m2[2][0], + m1[0][0] * m2[0][1] + m1[1][0] * m2[1][1] + m1[2][0] * m2[2][1], + m1[0][0] * m2[0][2] + m1[1][0] * m2[1][2] + m1[2][0] * m2[2][2], + m1[0][1] * m2[0][0] + m1[1][1] * m2[1][0] + m1[2][1] * m2[2][0], + m1[0][1] * m2[0][1] + m1[1][1] * m2[1][1] + m1[2][1] * m2[2][1], + m1[0][1] * m2[0][2] + m1[1][1] * m2[1][2] + m1[2][1] * m2[2][2], + m1[0][2] * m2[0][0] + m1[1][2] * m2[1][0] + m1[2][2] * m2[2][0], + m1[0][2] * m2[0][1] + m1[1][2] * m2[1][1] + m1[2][2] * m2[2][1], + m1[0][2] * m2[0][2] + m1[1][2] * m2[1][2] + m1[2][2] * m2[2][2]); +} +*/ + +/**@brief Equality operator between two matrices + * It will test all elements are equal. */ +SIMD_FORCE_INLINE bool operator==(const btMatrix3x3& m1, const btMatrix3x3& m2) +{ + return ( m1[0][0] == m2[0][0] && m1[1][0] == m2[1][0] && m1[2][0] == m2[2][0] && + m1[0][1] == m2[0][1] && m1[1][1] == m2[1][1] && m1[2][1] == m2[2][1] && + m1[0][2] == m2[0][2] && m1[1][2] == m2[1][2] && m1[2][2] == m2[2][2] ); +} + +#endif diff --git a/Engine/lib/bullet/src/LinearMath/btMinMax.h b/Engine/lib/bullet/src/LinearMath/btMinMax.h new file mode 100644 index 000000000..5e27d62a4 --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/btMinMax.h @@ -0,0 +1,69 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / Erwin Coumans http://continuousphysics.com/Bullet/ + +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 GEN_MINMAX_H +#define GEN_MINMAX_H + +template +SIMD_FORCE_INLINE const T& btMin(const T& a, const T& b) +{ + return a < b ? a : b ; +} + +template +SIMD_FORCE_INLINE const T& btMax(const T& a, const T& b) +{ + return a > b ? a : b; +} + +template +SIMD_FORCE_INLINE const T& GEN_clamped(const T& a, const T& lb, const T& ub) +{ + return a < lb ? lb : (ub < a ? ub : a); +} + +template +SIMD_FORCE_INLINE void btSetMin(T& a, const T& b) +{ + if (b < a) + { + a = b; + } +} + +template +SIMD_FORCE_INLINE void btSetMax(T& a, const T& b) +{ + if (a < b) + { + a = b; + } +} + +template +SIMD_FORCE_INLINE void GEN_clamp(T& a, const T& lb, const T& ub) +{ + if (a < lb) + { + a = lb; + } + else if (ub < a) + { + a = ub; + } +} + +#endif diff --git a/Engine/lib/bullet/src/LinearMath/btMotionState.h b/Engine/lib/bullet/src/LinearMath/btMotionState.h new file mode 100644 index 000000000..943181409 --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/btMotionState.h @@ -0,0 +1,40 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 BT_MOTIONSTATE_H +#define BT_MOTIONSTATE_H + +#include "btTransform.h" + +///The btMotionState interface class allows the dynamics world to synchronize and interpolate the updated world transforms with graphics +///For optimizations, potentially only moving objects get synchronized (using setWorldPosition/setWorldOrientation) +class btMotionState +{ + public: + + virtual ~btMotionState() + { + + } + + virtual void getWorldTransform(btTransform& worldTrans ) const =0; + + //Bullet only calls the update of worldtransform for active objects + virtual void setWorldTransform(const btTransform& worldTrans)=0; + + +}; + +#endif //BT_MOTIONSTATE_H diff --git a/Engine/lib/bullet/src/LinearMath/btPoolAllocator.h b/Engine/lib/bullet/src/LinearMath/btPoolAllocator.h new file mode 100644 index 000000000..39d2559c7 --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/btPoolAllocator.h @@ -0,0 +1,102 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / Erwin Coumans http://continuousphysics.com/Bullet/ + +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 _BT_POOL_ALLOCATOR_H +#define _BT_POOL_ALLOCATOR_H + +#include "btScalar.h" +#include "btAlignedAllocator.h" + +///The btPoolAllocator class allows to efficiently allocate a large pool of objects, instead of dynamically allocating them separately. +class btPoolAllocator +{ + int m_elemSize; + int m_maxElements; + int m_freeCount; + void* m_firstFree; + unsigned char* m_pool; + +public: + + btPoolAllocator(int elemSize, int maxElements) + :m_elemSize(elemSize), + m_maxElements(maxElements) + { + m_pool = (unsigned char*) btAlignedAlloc( static_cast(m_elemSize*m_maxElements),16); + + unsigned char* p = m_pool; + m_firstFree = p; + m_freeCount = m_maxElements; + int count = m_maxElements; + while (--count) { + *(void**)p = (p + m_elemSize); + p += m_elemSize; + } + *(void**)p = 0; + } + + ~btPoolAllocator() + { + btAlignedFree( m_pool); + } + + int getFreeCount() const + { + return m_freeCount; + } + + void* allocate(int size) + { + // release mode fix + (void)size; + btAssert(!size || size<=m_elemSize); + btAssert(m_freeCount>0); + void* result = m_firstFree; + m_firstFree = *(void**)m_firstFree; + --m_freeCount; + return result; + } + + bool validPtr(void* ptr) + { + if (ptr) { + if (((unsigned char*)ptr >= m_pool && (unsigned char*)ptr < m_pool + m_maxElements * m_elemSize)) + { + return true; + } + } + return false; + } + + void freeMemory(void* ptr) + { + if (ptr) { + btAssert((unsigned char*)ptr >= m_pool && (unsigned char*)ptr < m_pool + m_maxElements * m_elemSize); + + *(void**)ptr = m_firstFree; + m_firstFree = ptr; + ++m_freeCount; + } + } + + int getElementSize() const + { + return m_elemSize; + } + + +}; + +#endif //_BT_POOL_ALLOCATOR_H diff --git a/Engine/lib/bullet/src/LinearMath/btQuadWord.h b/Engine/lib/bullet/src/LinearMath/btQuadWord.h new file mode 100644 index 000000000..c657afd2b --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/btQuadWord.h @@ -0,0 +1,180 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / Erwin Coumans http://continuousphysics.com/Bullet/ + +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 SIMD_QUADWORD_H +#define SIMD_QUADWORD_H + +#include "btScalar.h" +#include "btMinMax.h" + + +#if defined (__CELLOS_LV2) && defined (__SPU__) +#include +#endif + +/**@brief The btQuadWord class is base class for btVector3 and btQuaternion. + * Some issues under PS3 Linux with IBM 2.1 SDK, gcc compiler prevent from using aligned quadword. + */ +#ifndef USE_LIBSPE2 +ATTRIBUTE_ALIGNED16(class) btQuadWord +#else +class btQuadWord +#endif +{ +protected: + +#if defined (__SPU__) && defined (__CELLOS_LV2__) + union { + vec_float4 mVec128; + btScalar m_floats[4]; + }; +public: + vec_float4 get128() const + { + return mVec128; + } +protected: +#else //__CELLOS_LV2__ __SPU__ + btScalar m_floats[4]; +#endif //__CELLOS_LV2__ __SPU__ + + public: + + + /**@brief Return the x value */ + SIMD_FORCE_INLINE const btScalar& getX() const { return m_floats[0]; } + /**@brief Return the y value */ + SIMD_FORCE_INLINE const btScalar& getY() const { return m_floats[1]; } + /**@brief Return the z value */ + SIMD_FORCE_INLINE const btScalar& getZ() const { return m_floats[2]; } + /**@brief Set the x value */ + SIMD_FORCE_INLINE void setX(btScalar x) { m_floats[0] = x;}; + /**@brief Set the y value */ + SIMD_FORCE_INLINE void setY(btScalar y) { m_floats[1] = y;}; + /**@brief Set the z value */ + SIMD_FORCE_INLINE void setZ(btScalar z) { m_floats[2] = z;}; + /**@brief Set the w value */ + SIMD_FORCE_INLINE void setW(btScalar w) { m_floats[3] = w;}; + /**@brief Return the x value */ + SIMD_FORCE_INLINE const btScalar& x() const { return m_floats[0]; } + /**@brief Return the y value */ + SIMD_FORCE_INLINE const btScalar& y() const { return m_floats[1]; } + /**@brief Return the z value */ + SIMD_FORCE_INLINE const btScalar& z() const { return m_floats[2]; } + /**@brief Return the w value */ + SIMD_FORCE_INLINE const btScalar& w() const { return m_floats[3]; } + + //SIMD_FORCE_INLINE btScalar& operator[](int i) { return (&m_floats[0])[i]; } + //SIMD_FORCE_INLINE const btScalar& operator[](int i) const { return (&m_floats[0])[i]; } + ///operator btScalar*() replaces operator[], using implicit conversion. We added operator != and operator == to avoid pointer comparisons. + SIMD_FORCE_INLINE operator btScalar *() { return &m_floats[0]; } + SIMD_FORCE_INLINE operator const btScalar *() const { return &m_floats[0]; } + + SIMD_FORCE_INLINE bool operator==(const btQuadWord& other) const + { + return ((m_floats[3]==other.m_floats[3]) && (m_floats[2]==other.m_floats[2]) && (m_floats[1]==other.m_floats[1]) && (m_floats[0]==other.m_floats[0])); + } + + SIMD_FORCE_INLINE bool operator!=(const btQuadWord& other) const + { + return !(*this == other); + } + + /**@brief Set x,y,z and zero w + * @param x Value of x + * @param y Value of y + * @param z Value of z + */ + SIMD_FORCE_INLINE void setValue(const btScalar& x, const btScalar& y, const btScalar& z) + { + m_floats[0]=x; + m_floats[1]=y; + m_floats[2]=z; + m_floats[3] = 0.f; + } + +/* void getValue(btScalar *m) const + { + m[0] = m_floats[0]; + m[1] = m_floats[1]; + m[2] = m_floats[2]; + } +*/ +/**@brief Set the values + * @param x Value of x + * @param y Value of y + * @param z Value of z + * @param w Value of w + */ + SIMD_FORCE_INLINE void setValue(const btScalar& x, const btScalar& y, const btScalar& z,const btScalar& w) + { + m_floats[0]=x; + m_floats[1]=y; + m_floats[2]=z; + m_floats[3]=w; + } + /**@brief No initialization constructor */ + SIMD_FORCE_INLINE btQuadWord() + // :m_floats[0](btScalar(0.)),m_floats[1](btScalar(0.)),m_floats[2](btScalar(0.)),m_floats[3](btScalar(0.)) + { + } + + /**@brief Three argument constructor (zeros w) + * @param x Value of x + * @param y Value of y + * @param z Value of z + */ + SIMD_FORCE_INLINE btQuadWord(const btScalar& x, const btScalar& y, const btScalar& z) + { + m_floats[0] = x, m_floats[1] = y, m_floats[2] = z, m_floats[3] = 0.0f; + } + +/**@brief Initializing constructor + * @param x Value of x + * @param y Value of y + * @param z Value of z + * @param w Value of w + */ + SIMD_FORCE_INLINE btQuadWord(const btScalar& x, const btScalar& y, const btScalar& z,const btScalar& w) + { + m_floats[0] = x, m_floats[1] = y, m_floats[2] = z, m_floats[3] = w; + } + + /**@brief Set each element to the max of the current values and the values of another btQuadWord + * @param other The other btQuadWord to compare with + */ + SIMD_FORCE_INLINE void setMax(const btQuadWord& other) + { + btSetMax(m_floats[0], other.m_floats[0]); + btSetMax(m_floats[1], other.m_floats[1]); + btSetMax(m_floats[2], other.m_floats[2]); + btSetMax(m_floats[3], other.m_floats[3]); + } + /**@brief Set each element to the min of the current values and the values of another btQuadWord + * @param other The other btQuadWord to compare with + */ + SIMD_FORCE_INLINE void setMin(const btQuadWord& other) + { + btSetMin(m_floats[0], other.m_floats[0]); + btSetMin(m_floats[1], other.m_floats[1]); + btSetMin(m_floats[2], other.m_floats[2]); + btSetMin(m_floats[3], other.m_floats[3]); + } + + + +}; + +#endif //SIMD_QUADWORD_H diff --git a/Engine/lib/bullet/src/LinearMath/btQuaternion.h b/Engine/lib/bullet/src/LinearMath/btQuaternion.h new file mode 100644 index 000000000..f530c71df --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/btQuaternion.h @@ -0,0 +1,426 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / Erwin Coumans http://continuousphysics.com/Bullet/ + +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 SIMD__QUATERNION_H_ +#define SIMD__QUATERNION_H_ + + +#include "btVector3.h" +#include "btQuadWord.h" + +/**@brief The btQuaternion implements quaternion to perform linear algebra rotations in combination with btMatrix3x3, btVector3 and btTransform. */ +class btQuaternion : public btQuadWord { +public: + /**@brief No initialization constructor */ + btQuaternion() {} + + // template + // explicit Quaternion(const btScalar *v) : Tuple4(v) {} + /**@brief Constructor from scalars */ + btQuaternion(const btScalar& x, const btScalar& y, const btScalar& z, const btScalar& w) + : btQuadWord(x, y, z, w) + {} + /**@brief Axis angle Constructor + * @param axis The axis which the rotation is around + * @param angle The magnitude of the rotation around the angle (Radians) */ + btQuaternion(const btVector3& axis, const btScalar& angle) + { + setRotation(axis, angle); + } + /**@brief Constructor from Euler angles + * @param yaw Angle around Y unless BT_EULER_DEFAULT_ZYX defined then Z + * @param pitch Angle around X unless BT_EULER_DEFAULT_ZYX defined then Y + * @param roll Angle around Z unless BT_EULER_DEFAULT_ZYX defined then X */ + btQuaternion(const btScalar& yaw, const btScalar& pitch, const btScalar& roll) + { +#ifndef BT_EULER_DEFAULT_ZYX + setEuler(yaw, pitch, roll); +#else + setEulerZYX(yaw, pitch, roll); +#endif + } + /**@brief Set the rotation using axis angle notation + * @param axis The axis around which to rotate + * @param angle The magnitude of the rotation in Radians */ + void setRotation(const btVector3& axis, const btScalar& angle) + { + btScalar d = axis.length(); + btAssert(d != btScalar(0.0)); + btScalar s = btSin(angle * btScalar(0.5)) / d; + setValue(axis.x() * s, axis.y() * s, axis.z() * s, + btCos(angle * btScalar(0.5))); + } + /**@brief Set the quaternion using Euler angles + * @param yaw Angle around Y + * @param pitch Angle around X + * @param roll Angle around Z */ + void setEuler(const btScalar& yaw, const btScalar& pitch, const btScalar& roll) + { + btScalar halfYaw = btScalar(yaw) * btScalar(0.5); + btScalar halfPitch = btScalar(pitch) * btScalar(0.5); + btScalar halfRoll = btScalar(roll) * btScalar(0.5); + btScalar cosYaw = btCos(halfYaw); + btScalar sinYaw = btSin(halfYaw); + btScalar cosPitch = btCos(halfPitch); + btScalar sinPitch = btSin(halfPitch); + btScalar cosRoll = btCos(halfRoll); + btScalar sinRoll = btSin(halfRoll); + setValue(cosRoll * sinPitch * cosYaw + sinRoll * cosPitch * sinYaw, + cosRoll * cosPitch * sinYaw - sinRoll * sinPitch * cosYaw, + sinRoll * cosPitch * cosYaw - cosRoll * sinPitch * sinYaw, + cosRoll * cosPitch * cosYaw + sinRoll * sinPitch * sinYaw); + } + /**@brief Set the quaternion using euler angles + * @param yaw Angle around Z + * @param pitch Angle around Y + * @param roll Angle around X */ + void setEulerZYX(const btScalar& yaw, const btScalar& pitch, const btScalar& roll) + { + btScalar halfYaw = btScalar(yaw) * btScalar(0.5); + btScalar halfPitch = btScalar(pitch) * btScalar(0.5); + btScalar halfRoll = btScalar(roll) * btScalar(0.5); + btScalar cosYaw = btCos(halfYaw); + btScalar sinYaw = btSin(halfYaw); + btScalar cosPitch = btCos(halfPitch); + btScalar sinPitch = btSin(halfPitch); + btScalar cosRoll = btCos(halfRoll); + btScalar sinRoll = btSin(halfRoll); + setValue(sinRoll * cosPitch * cosYaw - cosRoll * sinPitch * sinYaw, //x + cosRoll * sinPitch * cosYaw + sinRoll * cosPitch * sinYaw, //y + cosRoll * cosPitch * sinYaw - sinRoll * sinPitch * cosYaw, //z + cosRoll * cosPitch * cosYaw + sinRoll * sinPitch * sinYaw); //formerly yzx + } + /**@brief Add two quaternions + * @param q The quaternion to add to this one */ + SIMD_FORCE_INLINE btQuaternion& operator+=(const btQuaternion& q) + { + m_floats[0] += q.x(); m_floats[1] += q.y(); m_floats[2] += q.z(); m_floats[3] += q.m_floats[3]; + return *this; + } + + /**@brief Subtract out a quaternion + * @param q The quaternion to subtract from this one */ + btQuaternion& operator-=(const btQuaternion& q) + { + m_floats[0] -= q.x(); m_floats[1] -= q.y(); m_floats[2] -= q.z(); m_floats[3] -= q.m_floats[3]; + return *this; + } + + /**@brief Scale this quaternion + * @param s The scalar to scale by */ + btQuaternion& operator*=(const btScalar& s) + { + m_floats[0] *= s; m_floats[1] *= s; m_floats[2] *= s; m_floats[3] *= s; + return *this; + } + + /**@brief Multiply this quaternion by q on the right + * @param q The other quaternion + * Equivilant to this = this * q */ + btQuaternion& operator*=(const btQuaternion& q) + { + setValue(m_floats[3] * q.x() + m_floats[0] * q.m_floats[3] + m_floats[1] * q.z() - m_floats[2] * q.y(), + m_floats[3] * q.y() + m_floats[1] * q.m_floats[3] + m_floats[2] * q.x() - m_floats[0] * q.z(), + m_floats[3] * q.z() + m_floats[2] * q.m_floats[3] + m_floats[0] * q.y() - m_floats[1] * q.x(), + m_floats[3] * q.m_floats[3] - m_floats[0] * q.x() - m_floats[1] * q.y() - m_floats[2] * q.z()); + return *this; + } + /**@brief Return the dot product between this quaternion and another + * @param q The other quaternion */ + btScalar dot(const btQuaternion& q) const + { + return m_floats[0] * q.x() + m_floats[1] * q.y() + m_floats[2] * q.z() + m_floats[3] * q.m_floats[3]; + } + + /**@brief Return the length squared of the quaternion */ + btScalar length2() const + { + return dot(*this); + } + + /**@brief Return the length of the quaternion */ + btScalar length() const + { + return btSqrt(length2()); + } + + /**@brief Normalize the quaternion + * Such that x^2 + y^2 + z^2 +w^2 = 1 */ + btQuaternion& normalize() + { + return *this /= length(); + } + + /**@brief Return a scaled version of this quaternion + * @param s The scale factor */ + SIMD_FORCE_INLINE btQuaternion + operator*(const btScalar& s) const + { + return btQuaternion(x() * s, y() * s, z() * s, m_floats[3] * s); + } + + + /**@brief Return an inversely scaled versionof this quaternion + * @param s The inverse scale factor */ + btQuaternion operator/(const btScalar& s) const + { + btAssert(s != btScalar(0.0)); + return *this * (btScalar(1.0) / s); + } + + /**@brief Inversely scale this quaternion + * @param s The scale factor */ + btQuaternion& operator/=(const btScalar& s) + { + btAssert(s != btScalar(0.0)); + return *this *= btScalar(1.0) / s; + } + + /**@brief Return a normalized version of this quaternion */ + btQuaternion normalized() const + { + return *this / length(); + } + /**@brief Return the angle between this quaternion and the other + * @param q The other quaternion */ + btScalar angle(const btQuaternion& q) const + { + btScalar s = btSqrt(length2() * q.length2()); + btAssert(s != btScalar(0.0)); + return btAcos(dot(q) / s); + } + /**@brief Return the angle of rotation represented by this quaternion */ + btScalar getAngle() const + { + btScalar s = btScalar(2.) * btAcos(m_floats[3]); + return s; + } + + /**@brief Return the axis of the rotation represented by this quaternion */ + btVector3 getAxis() const + { + btScalar s_squared = btScalar(1.) - btPow(m_floats[3], btScalar(2.)); + if (s_squared < btScalar(10.) * SIMD_EPSILON) //Check for divide by zero + return btVector3(1.0, 0.0, 0.0); // Arbitrary + btScalar s = btSqrt(s_squared); + return btVector3(m_floats[0] / s, m_floats[1] / s, m_floats[2] / s); + } + + /**@brief Return the inverse of this quaternion */ + btQuaternion inverse() const + { + return btQuaternion(-m_floats[0], -m_floats[1], -m_floats[2], m_floats[3]); + } + + /**@brief Return the sum of this quaternion and the other + * @param q2 The other quaternion */ + SIMD_FORCE_INLINE btQuaternion + operator+(const btQuaternion& q2) const + { + const btQuaternion& q1 = *this; + return btQuaternion(q1.x() + q2.x(), q1.y() + q2.y(), q1.z() + q2.z(), q1.m_floats[3] + q2.m_floats[3]); + } + + /**@brief Return the difference between this quaternion and the other + * @param q2 The other quaternion */ + SIMD_FORCE_INLINE btQuaternion + operator-(const btQuaternion& q2) const + { + const btQuaternion& q1 = *this; + return btQuaternion(q1.x() - q2.x(), q1.y() - q2.y(), q1.z() - q2.z(), q1.m_floats[3] - q2.m_floats[3]); + } + + /**@brief Return the negative of this quaternion + * This simply negates each element */ + SIMD_FORCE_INLINE btQuaternion operator-() const + { + const btQuaternion& q2 = *this; + return btQuaternion( - q2.x(), - q2.y(), - q2.z(), - q2.m_floats[3]); + } + /**@todo document this and it's use */ + SIMD_FORCE_INLINE btQuaternion farthest( const btQuaternion& qd) const + { + btQuaternion diff,sum; + diff = *this - qd; + sum = *this + qd; + if( diff.dot(diff) > sum.dot(sum) ) + return qd; + return (-qd); + } + + /**@todo document this and it's use */ + SIMD_FORCE_INLINE btQuaternion nearest( const btQuaternion& qd) const + { + btQuaternion diff,sum; + diff = *this - qd; + sum = *this + qd; + if( diff.dot(diff) < sum.dot(sum) ) + return qd; + return (-qd); + } + + + /**@brief Return the quaternion which is the result of Spherical Linear Interpolation between this and the other quaternion + * @param q The other quaternion to interpolate with + * @param t The ratio between this and q to interpolate. If t = 0 the result is this, if t=1 the result is q. + * Slerp interpolates assuming constant velocity. */ + btQuaternion slerp(const btQuaternion& q, const btScalar& t) const + { + btScalar theta = angle(q); + if (theta != btScalar(0.0)) + { + btScalar d = btScalar(1.0) / btSin(theta); + btScalar s0 = btSin((btScalar(1.0) - t) * theta); + btScalar s1 = btSin(t * theta); + return btQuaternion((m_floats[0] * s0 + q.x() * s1) * d, + (m_floats[1] * s0 + q.y() * s1) * d, + (m_floats[2] * s0 + q.z() * s1) * d, + (m_floats[3] * s0 + q.m_floats[3] * s1) * d); + } + else + { + return *this; + } + } + + static const btQuaternion& getIdentity() + { + static const btQuaternion identityQuat(btScalar(0.),btScalar(0.),btScalar(0.),btScalar(1.)); + return identityQuat; + } + + SIMD_FORCE_INLINE const btScalar& getW() const { return m_floats[3]; } + + +}; + + +/**@brief Return the negative of a quaternion */ +SIMD_FORCE_INLINE btQuaternion +operator-(const btQuaternion& q) +{ + return btQuaternion(-q.x(), -q.y(), -q.z(), -q.w()); +} + + + +/**@brief Return the product of two quaternions */ +SIMD_FORCE_INLINE btQuaternion +operator*(const btQuaternion& q1, const btQuaternion& q2) { + return btQuaternion(q1.w() * q2.x() + q1.x() * q2.w() + q1.y() * q2.z() - q1.z() * q2.y(), + q1.w() * q2.y() + q1.y() * q2.w() + q1.z() * q2.x() - q1.x() * q2.z(), + q1.w() * q2.z() + q1.z() * q2.w() + q1.x() * q2.y() - q1.y() * q2.x(), + q1.w() * q2.w() - q1.x() * q2.x() - q1.y() * q2.y() - q1.z() * q2.z()); +} + +SIMD_FORCE_INLINE btQuaternion +operator*(const btQuaternion& q, const btVector3& w) +{ + return btQuaternion( q.w() * w.x() + q.y() * w.z() - q.z() * w.y(), + q.w() * w.y() + q.z() * w.x() - q.x() * w.z(), + q.w() * w.z() + q.x() * w.y() - q.y() * w.x(), + -q.x() * w.x() - q.y() * w.y() - q.z() * w.z()); +} + +SIMD_FORCE_INLINE btQuaternion +operator*(const btVector3& w, const btQuaternion& q) +{ + return btQuaternion( w.x() * q.w() + w.y() * q.z() - w.z() * q.y(), + w.y() * q.w() + w.z() * q.x() - w.x() * q.z(), + w.z() * q.w() + w.x() * q.y() - w.y() * q.x(), + -w.x() * q.x() - w.y() * q.y() - w.z() * q.z()); +} + +/**@brief Calculate the dot product between two quaternions */ +SIMD_FORCE_INLINE btScalar +dot(const btQuaternion& q1, const btQuaternion& q2) +{ + return q1.dot(q2); +} + + +/**@brief Return the length of a quaternion */ +SIMD_FORCE_INLINE btScalar +length(const btQuaternion& q) +{ + return q.length(); +} + +/**@brief Return the angle between two quaternions*/ +SIMD_FORCE_INLINE btScalar +angle(const btQuaternion& q1, const btQuaternion& q2) +{ + return q1.angle(q2); +} + +/**@brief Return the inverse of a quaternion*/ +SIMD_FORCE_INLINE btQuaternion +inverse(const btQuaternion& q) +{ + return q.inverse(); +} + +/**@brief Return the result of spherical linear interpolation betwen two quaternions + * @param q1 The first quaternion + * @param q2 The second quaternion + * @param t The ration between q1 and q2. t = 0 return q1, t=1 returns q2 + * Slerp assumes constant velocity between positions. */ +SIMD_FORCE_INLINE btQuaternion +slerp(const btQuaternion& q1, const btQuaternion& q2, const btScalar& t) +{ + return q1.slerp(q2, t); +} + +SIMD_FORCE_INLINE btVector3 +quatRotate(const btQuaternion& rotation, const btVector3& v) +{ + btQuaternion q = rotation * v; + q *= rotation.inverse(); + return btVector3(q.getX(),q.getY(),q.getZ()); +} + +SIMD_FORCE_INLINE btQuaternion +shortestArcQuat(const btVector3& v0, const btVector3& v1) // Game Programming Gems 2.10. make sure v0,v1 are normalized +{ + btVector3 c = v0.cross(v1); + btScalar d = v0.dot(v1); + + if (d < -1.0 + SIMD_EPSILON) + { + btVector3 n,unused; + btPlaneSpace1(v0,n,unused); + return btQuaternion(n.x(),n.y(),n.z(),0.0f); // just pick any vector that is orthogonal to v0 + } + + btScalar s = btSqrt((1.0f + d) * 2.0f); + btScalar rs = 1.0f / s; + + return btQuaternion(c.getX()*rs,c.getY()*rs,c.getZ()*rs,s * 0.5f); +} + +SIMD_FORCE_INLINE btQuaternion +shortestArcQuatNormalize2(btVector3& v0,btVector3& v1) +{ + v0.normalize(); + v1.normalize(); + return shortestArcQuat(v0,v1); +} + +#endif + + + + diff --git a/Engine/lib/bullet/src/LinearMath/btQuickprof.cpp b/Engine/lib/bullet/src/LinearMath/btQuickprof.cpp new file mode 100644 index 000000000..fa45d02b3 --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/btQuickprof.cpp @@ -0,0 +1,346 @@ +/* + +/*************************************************************************************************** +** +** profile.cpp +** +** Real-Time Hierarchical Profiling for Game Programming Gems 3 +** +** by Greg Hjelstrom & Byon Garrabrant +** +***************************************************************************************************/ + +// Credits: The Clock class was inspired by the Timer classes in +// Ogre (www.ogre3d.org). + +#include "LinearMath/btQuickprof.h" + + +#ifdef USE_BT_CLOCK + +static btClock gProfileClock; + +inline void Profile_Get_Ticks(unsigned long int * ticks) +{ + *ticks = gProfileClock.getTimeMicroseconds(); +} + +inline float Profile_Get_Tick_Rate(void) +{ +// return 1000000.f; + return 1000.f; + +} + + + +/*************************************************************************************************** +** +** CProfileNode +** +***************************************************************************************************/ + +/*********************************************************************************************** + * INPUT: * + * name - pointer to a static string which is the name of this profile node * + * parent - parent pointer * + * * + * WARNINGS: * + * The name is assumed to be a static pointer, only the pointer is stored and compared for * + * efficiency reasons. * + *=============================================================================================*/ +CProfileNode::CProfileNode( const char * name, CProfileNode * parent ) : + Name( name ), + TotalCalls( 0 ), + TotalTime( 0 ), + StartTime( 0 ), + RecursionCounter( 0 ), + Parent( parent ), + Child( NULL ), + Sibling( NULL ) +{ + Reset(); +} + + +void CProfileNode::CleanupMemory() +{ + delete ( Child); + Child = NULL; + delete ( Sibling); + Sibling = NULL; +} + +CProfileNode::~CProfileNode( void ) +{ + delete ( Child); + delete ( Sibling); +} + + +/*********************************************************************************************** + * INPUT: * + * name - static string pointer to the name of the node we are searching for * + * * + * WARNINGS: * + * All profile names are assumed to be static strings so this function uses pointer compares * + * to find the named node. * + *=============================================================================================*/ +CProfileNode * CProfileNode::Get_Sub_Node( const char * name ) +{ + // Try to find this sub node + CProfileNode * child = Child; + while ( child ) { + if ( child->Name == name ) { + return child; + } + child = child->Sibling; + } + + // We didn't find it, so add it + + CProfileNode * node = new CProfileNode( name, this ); + node->Sibling = Child; + Child = node; + return node; +} + + +void CProfileNode::Reset( void ) +{ + TotalCalls = 0; + TotalTime = 0.0f; + + + if ( Child ) { + Child->Reset(); + } + if ( Sibling ) { + Sibling->Reset(); + } +} + + +void CProfileNode::Call( void ) +{ + TotalCalls++; + if (RecursionCounter++ == 0) { + Profile_Get_Ticks(&StartTime); + } +} + + +bool CProfileNode::Return( void ) +{ + if ( --RecursionCounter == 0 && TotalCalls != 0 ) { + unsigned long int time; + Profile_Get_Ticks(&time); + time-=StartTime; + TotalTime += (float)time / Profile_Get_Tick_Rate(); + } + return ( RecursionCounter == 0 ); +} + + +/*************************************************************************************************** +** +** CProfileIterator +** +***************************************************************************************************/ +CProfileIterator::CProfileIterator( CProfileNode * start ) +{ + CurrentParent = start; + CurrentChild = CurrentParent->Get_Child(); +} + + +void CProfileIterator::First(void) +{ + CurrentChild = CurrentParent->Get_Child(); +} + + +void CProfileIterator::Next(void) +{ + CurrentChild = CurrentChild->Get_Sibling(); +} + + +bool CProfileIterator::Is_Done(void) +{ + return CurrentChild == NULL; +} + + +void CProfileIterator::Enter_Child( int index ) +{ + CurrentChild = CurrentParent->Get_Child(); + while ( (CurrentChild != NULL) && (index != 0) ) { + index--; + CurrentChild = CurrentChild->Get_Sibling(); + } + + if ( CurrentChild != NULL ) { + CurrentParent = CurrentChild; + CurrentChild = CurrentParent->Get_Child(); + } +} + + +void CProfileIterator::Enter_Parent( void ) +{ + if ( CurrentParent->Get_Parent() != NULL ) { + CurrentParent = CurrentParent->Get_Parent(); + } + CurrentChild = CurrentParent->Get_Child(); +} + + +/*************************************************************************************************** +** +** CProfileManager +** +***************************************************************************************************/ + +CProfileNode CProfileManager::Root( "Root", NULL ); +CProfileNode * CProfileManager::CurrentNode = &CProfileManager::Root; +int CProfileManager::FrameCounter = 0; +unsigned long int CProfileManager::ResetTime = 0; + + +/*********************************************************************************************** + * CProfileManager::Start_Profile -- Begin a named profile * + * * + * Steps one level deeper into the tree, if a child already exists with the specified name * + * then it accumulates the profiling; otherwise a new child node is added to the profile tree. * + * * + * INPUT: * + * name - name of this profiling record * + * * + * WARNINGS: * + * The string used is assumed to be a static string; pointer compares are used throughout * + * the profiling code for efficiency. * + *=============================================================================================*/ +void CProfileManager::Start_Profile( const char * name ) +{ + if (name != CurrentNode->Get_Name()) { + CurrentNode = CurrentNode->Get_Sub_Node( name ); + } + + CurrentNode->Call(); +} + + +/*********************************************************************************************** + * CProfileManager::Stop_Profile -- Stop timing and record the results. * + *=============================================================================================*/ +void CProfileManager::Stop_Profile( void ) +{ + // Return will indicate whether we should back up to our parent (we may + // be profiling a recursive function) + if (CurrentNode->Return()) { + CurrentNode = CurrentNode->Get_Parent(); + } +} + + +/*********************************************************************************************** + * CProfileManager::Reset -- Reset the contents of the profiling system * + * * + * This resets everything except for the tree structure. All of the timing data is reset. * + *=============================================================================================*/ +void CProfileManager::Reset( void ) +{ + gProfileClock.reset(); + Root.Reset(); + Root.Call(); + FrameCounter = 0; + Profile_Get_Ticks(&ResetTime); +} + + +/*********************************************************************************************** + * CProfileManager::Increment_Frame_Counter -- Increment the frame counter * + *=============================================================================================*/ +void CProfileManager::Increment_Frame_Counter( void ) +{ + FrameCounter++; +} + + +/*********************************************************************************************** + * CProfileManager::Get_Time_Since_Reset -- returns the elapsed time since last reset * + *=============================================================================================*/ +float CProfileManager::Get_Time_Since_Reset( void ) +{ + unsigned long int time; + Profile_Get_Ticks(&time); + time -= ResetTime; + return (float)time / Profile_Get_Tick_Rate(); +} + +#include + +void CProfileManager::dumpRecursive(CProfileIterator* profileIterator, int spacing) +{ + profileIterator->First(); + if (profileIterator->Is_Done()) + return; + + float accumulated_time=0,parent_time = profileIterator->Is_Root() ? CProfileManager::Get_Time_Since_Reset() : profileIterator->Get_Current_Parent_Total_Time(); + int i; + int frames_since_reset = CProfileManager::Get_Frame_Count_Since_Reset(); + for (i=0;iGet_Current_Parent_Name(), parent_time ); + float totalTime = 0.f; + + + int numChildren = 0; + + for (i = 0; !profileIterator->Is_Done(); i++,profileIterator->Next()) + { + numChildren++; + float current_total_time = profileIterator->Get_Current_Total_Time(); + accumulated_time += current_total_time; + float fraction = parent_time > SIMD_EPSILON ? (current_total_time / parent_time) * 100 : 0.f; + { + int i; for (i=0;iGet_Current_Name(), fraction,(current_total_time / (double)frames_since_reset),profileIterator->Get_Current_Total_Calls()); + totalTime += current_total_time; + //recurse into children + } + + if (parent_time < accumulated_time) + { + printf("what's wrong\n"); + } + for (i=0;i SIMD_EPSILON ? ((parent_time - accumulated_time) / parent_time) * 100 : 0.f, parent_time - accumulated_time); + + for (i=0;iEnter_Child(i); + dumpRecursive(profileIterator,spacing+3); + profileIterator->Enter_Parent(); + } +} + + + +void CProfileManager::dumpAll() +{ + CProfileIterator* profileIterator = 0; + profileIterator = CProfileManager::Get_Iterator(); + + dumpRecursive(profileIterator,0); + + CProfileManager::Release_Iterator(profileIterator); +} + + + +#endif //USE_BT_CLOCK + diff --git a/Engine/lib/bullet/src/LinearMath/btQuickprof.h b/Engine/lib/bullet/src/LinearMath/btQuickprof.h new file mode 100644 index 000000000..f8d47c368 --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/btQuickprof.h @@ -0,0 +1,370 @@ + +/*************************************************************************************************** +** +** Real-Time Hierarchical Profiling for Game Programming Gems 3 +** +** by Greg Hjelstrom & Byon Garrabrant +** +***************************************************************************************************/ + +// Credits: The Clock class was inspired by the Timer classes in +// Ogre (www.ogre3d.org). + + + +#ifndef QUICK_PROF_H +#define QUICK_PROF_H + +//To disable built-in profiling, please comment out next line +//#define BT_NO_PROFILE 1 +#ifndef BT_NO_PROFILE + +#include "btScalar.h" +#include "LinearMath/btAlignedAllocator.h" +#include + + + + +//if you don't need btClock, you can comment next line +#define USE_BT_CLOCK 1 + +#ifdef USE_BT_CLOCK +#ifdef __CELLOS_LV2__ +#include +#include +#include +#endif + +#if defined (SUNOS) || defined (__SUNOS__) +#include +#endif + +#if defined(WIN32) || defined(_WIN32) + +#define USE_WINDOWS_TIMERS +#define WIN32_LEAN_AND_MEAN +#define NOWINRES +#define NOMCX +#define NOIME +#ifdef _XBOX +#include +#else +#include +#endif +#include + +#else +#include +#endif + +#define mymin(a,b) (a > b ? a : b) + +///The btClock is a portable basic clock that measures accurate time in seconds, use for profiling. +class btClock +{ +public: + btClock() + { +#ifdef USE_WINDOWS_TIMERS + QueryPerformanceFrequency(&mClockFrequency); +#endif + reset(); + } + + ~btClock() + { + } + + /// Resets the initial reference time. + void reset() + { +#ifdef USE_WINDOWS_TIMERS + QueryPerformanceCounter(&mStartTime); + mStartTick = GetTickCount(); + mPrevElapsedTime = 0; +#else +#ifdef __CELLOS_LV2__ + + typedef uint64_t ClockSize; + ClockSize newTime; + //__asm __volatile__( "mftb %0" : "=r" (newTime) : : "memory"); + SYS_TIMEBASE_GET( newTime ); + mStartTime = newTime; +#else + gettimeofday(&mStartTime, 0); +#endif + +#endif + } + + /// Returns the time in ms since the last call to reset or since + /// the btClock was created. + unsigned long int getTimeMilliseconds() + { +#ifdef USE_WINDOWS_TIMERS + LARGE_INTEGER currentTime; + QueryPerformanceCounter(¤tTime); + LONGLONG elapsedTime = currentTime.QuadPart - + mStartTime.QuadPart; + + // Compute the number of millisecond ticks elapsed. + unsigned long msecTicks = (unsigned long)(1000 * elapsedTime / + mClockFrequency.QuadPart); + + // Check for unexpected leaps in the Win32 performance counter. + // (This is caused by unexpected data across the PCI to ISA + // bridge, aka south bridge. See Microsoft KB274323.) + unsigned long elapsedTicks = GetTickCount() - mStartTick; + signed long msecOff = (signed long)(msecTicks - elapsedTicks); + if (msecOff < -100 || msecOff > 100) + { + // Adjust the starting time forwards. + LONGLONG msecAdjustment = mymin(msecOff * + mClockFrequency.QuadPart / 1000, elapsedTime - + mPrevElapsedTime); + mStartTime.QuadPart += msecAdjustment; + elapsedTime -= msecAdjustment; + + // Recompute the number of millisecond ticks elapsed. + msecTicks = (unsigned long)(1000 * elapsedTime / + mClockFrequency.QuadPart); + } + + // Store the current elapsed time for adjustments next time. + mPrevElapsedTime = elapsedTime; + + return msecTicks; +#else + +#ifdef __CELLOS_LV2__ + uint64_t freq=sys_time_get_timebase_frequency(); + double dFreq=((double) freq) / 1000.0; + typedef uint64_t ClockSize; + ClockSize newTime; + SYS_TIMEBASE_GET( newTime ); + //__asm __volatile__( "mftb %0" : "=r" (newTime) : : "memory"); + + return (unsigned long int)((double(newTime-mStartTime)) / dFreq); +#else + + struct timeval currentTime; + gettimeofday(¤tTime, 0); + return (currentTime.tv_sec - mStartTime.tv_sec) * 1000 + + (currentTime.tv_usec - mStartTime.tv_usec) / 1000; +#endif //__CELLOS_LV2__ +#endif + } + + /// Returns the time in us since the last call to reset or since + /// the Clock was created. + unsigned long int getTimeMicroseconds() + { +#ifdef USE_WINDOWS_TIMERS + LARGE_INTEGER currentTime; + QueryPerformanceCounter(¤tTime); + LONGLONG elapsedTime = currentTime.QuadPart - + mStartTime.QuadPart; + + // Compute the number of millisecond ticks elapsed. + unsigned long msecTicks = (unsigned long)(1000 * elapsedTime / + mClockFrequency.QuadPart); + + // Check for unexpected leaps in the Win32 performance counter. + // (This is caused by unexpected data across the PCI to ISA + // bridge, aka south bridge. See Microsoft KB274323.) + unsigned long elapsedTicks = GetTickCount() - mStartTick; + signed long msecOff = (signed long)(msecTicks - elapsedTicks); + if (msecOff < -100 || msecOff > 100) + { + // Adjust the starting time forwards. + LONGLONG msecAdjustment = mymin(msecOff * + mClockFrequency.QuadPart / 1000, elapsedTime - + mPrevElapsedTime); + mStartTime.QuadPart += msecAdjustment; + elapsedTime -= msecAdjustment; + } + + // Store the current elapsed time for adjustments next time. + mPrevElapsedTime = elapsedTime; + + // Convert to microseconds. + unsigned long usecTicks = (unsigned long)(1000000 * elapsedTime / + mClockFrequency.QuadPart); + + return usecTicks; +#else + +#ifdef __CELLOS_LV2__ + uint64_t freq=sys_time_get_timebase_frequency(); + double dFreq=((double) freq)/ 1000000.0; + typedef uint64_t ClockSize; + ClockSize newTime; + //__asm __volatile__( "mftb %0" : "=r" (newTime) : : "memory"); + SYS_TIMEBASE_GET( newTime ); + + return (unsigned long int)((double(newTime-mStartTime)) / dFreq); +#else + + struct timeval currentTime; + gettimeofday(¤tTime, 0); + return (currentTime.tv_sec - mStartTime.tv_sec) * 1000000 + + (currentTime.tv_usec - mStartTime.tv_usec); +#endif//__CELLOS_LV2__ +#endif + } + +private: +#ifdef USE_WINDOWS_TIMERS + LARGE_INTEGER mClockFrequency; + DWORD mStartTick; + LONGLONG mPrevElapsedTime; + LARGE_INTEGER mStartTime; +#else +#ifdef __CELLOS_LV2__ + uint64_t mStartTime; +#else + struct timeval mStartTime; +#endif +#endif //__CELLOS_LV2__ + +}; + +#endif //USE_BT_CLOCK + + + + +///A node in the Profile Hierarchy Tree +class CProfileNode { + +public: + CProfileNode( const char * name, CProfileNode * parent ); + ~CProfileNode( void ); + + CProfileNode * Get_Sub_Node( const char * name ); + + CProfileNode * Get_Parent( void ) { return Parent; } + CProfileNode * Get_Sibling( void ) { return Sibling; } + CProfileNode * Get_Child( void ) { return Child; } + + void CleanupMemory(); + void Reset( void ); + void Call( void ); + bool Return( void ); + + const char * Get_Name( void ) { return Name; } + int Get_Total_Calls( void ) { return TotalCalls; } + float Get_Total_Time( void ) { return TotalTime; } + +protected: + + const char * Name; + int TotalCalls; + float TotalTime; + unsigned long int StartTime; + int RecursionCounter; + + CProfileNode * Parent; + CProfileNode * Child; + CProfileNode * Sibling; +}; + +///An iterator to navigate through the tree +class CProfileIterator +{ +public: + // Access all the children of the current parent + void First(void); + void Next(void); + bool Is_Done(void); + bool Is_Root(void) { return (CurrentParent->Get_Parent() == 0); } + + void Enter_Child( int index ); // Make the given child the new parent + void Enter_Largest_Child( void ); // Make the largest child the new parent + void Enter_Parent( void ); // Make the current parent's parent the new parent + + // Access the current child + const char * Get_Current_Name( void ) { return CurrentChild->Get_Name(); } + int Get_Current_Total_Calls( void ) { return CurrentChild->Get_Total_Calls(); } + float Get_Current_Total_Time( void ) { return CurrentChild->Get_Total_Time(); } + + // Access the current parent + const char * Get_Current_Parent_Name( void ) { return CurrentParent->Get_Name(); } + int Get_Current_Parent_Total_Calls( void ) { return CurrentParent->Get_Total_Calls(); } + float Get_Current_Parent_Total_Time( void ) { return CurrentParent->Get_Total_Time(); } + +protected: + + CProfileNode * CurrentParent; + CProfileNode * CurrentChild; + + CProfileIterator( CProfileNode * start ); + friend class CProfileManager; +}; + + +///The Manager for the Profile system +class CProfileManager { +public: + static void Start_Profile( const char * name ); + static void Stop_Profile( void ); + + static void CleanupMemory(void) + { + Root.CleanupMemory(); + } + + static void Reset( void ); + static void Increment_Frame_Counter( void ); + static int Get_Frame_Count_Since_Reset( void ) { return FrameCounter; } + static float Get_Time_Since_Reset( void ); + + static CProfileIterator * Get_Iterator( void ) + { + + return new CProfileIterator( &Root ); + } + static void Release_Iterator( CProfileIterator * iterator ) { delete ( iterator); } + + static void dumpRecursive(CProfileIterator* profileIterator, int spacing); + + static void dumpAll(); + +private: + static CProfileNode Root; + static CProfileNode * CurrentNode; + static int FrameCounter; + static unsigned long int ResetTime; +}; + + +///ProfileSampleClass is a simple way to profile a function's scope +///Use the BT_PROFILE macro at the start of scope to time +class CProfileSample { +public: + CProfileSample( const char * name ) + { + CProfileManager::Start_Profile( name ); + } + + ~CProfileSample( void ) + { + CProfileManager::Stop_Profile(); + } +}; + + +#define BT_PROFILE( name ) CProfileSample __profile( name ) + +#else + +#define BT_PROFILE( name ) + +#endif //#ifndef BT_NO_PROFILE + + + +#endif //QUICK_PROF_H + + diff --git a/Engine/lib/bullet/src/LinearMath/btRandom.h b/Engine/lib/bullet/src/LinearMath/btRandom.h new file mode 100644 index 000000000..fdf65e01c --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/btRandom.h @@ -0,0 +1,42 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / Erwin Coumans http://continuousphysics.com/Bullet/ + +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 GEN_RANDOM_H +#define GEN_RANDOM_H + +#ifdef MT19937 + +#include +#include + +#define GEN_RAND_MAX UINT_MAX + +SIMD_FORCE_INLINE void GEN_srand(unsigned int seed) { init_genrand(seed); } +SIMD_FORCE_INLINE unsigned int GEN_rand() { return genrand_int32(); } + +#else + +#include + +#define GEN_RAND_MAX RAND_MAX + +SIMD_FORCE_INLINE void GEN_srand(unsigned int seed) { srand(seed); } +SIMD_FORCE_INLINE unsigned int GEN_rand() { return rand(); } + +#endif + +#endif + diff --git a/Engine/lib/bullet/src/LinearMath/btScalar.h b/Engine/lib/bullet/src/LinearMath/btScalar.h new file mode 100644 index 000000000..67dce56e9 --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/btScalar.h @@ -0,0 +1,495 @@ +/* +Copyright (c) 2003-2009 Erwin Coumans http://bullet.googlecode.com + +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 SIMD___SCALAR_H +#define SIMD___SCALAR_H + +#include +#include //size_t for MSVC 6.0 +#include +#include +#include + +/* SVN $Revision$ on $Date$ from http://bullet.googlecode.com*/ +#define BT_BULLET_VERSION 275 + +inline int btGetVersion() +{ + return BT_BULLET_VERSION; +} + +#if defined(DEBUG) || defined (_DEBUG) +#define BT_DEBUG +#endif + + +#ifdef WIN32 + + #if defined(__MINGW32__) || defined(__CYGWIN__) || (defined (_MSC_VER) && _MSC_VER < 1300) + + #define SIMD_FORCE_INLINE inline + #define ATTRIBUTE_ALIGNED16(a) a + #define ATTRIBUTE_ALIGNED128(a) a + #else + //#define BT_HAS_ALIGNED_ALLOCATOR + #pragma warning(disable : 4324) // disable padding warning +// #pragma warning(disable:4530) // Disable the exception disable but used in MSCV Stl warning. +// #pragma warning(disable:4996) //Turn off warnings about deprecated C routines +// #pragma warning(disable:4786) // Disable the "debug name too long" warning + + #define SIMD_FORCE_INLINE __forceinline + #define ATTRIBUTE_ALIGNED16(a) __declspec(align(16)) a + #define ATTRIBUTE_ALIGNED128(a) __declspec (align(128)) a + #ifdef _XBOX + #define BT_USE_VMX128 + + #include + #define BT_HAVE_NATIVE_FSEL + #define btFsel(a,b,c) __fsel((a),(b),(c)) + #else + +#if (defined (WIN32) && (_MSC_VER) && _MSC_VER >= 1400) && (!defined (BT_USE_DOUBLE_PRECISION)) + #define BT_USE_SSE + #include +#endif + + #endif//_XBOX + + #endif //__MINGW32__ + + #include +#ifdef BT_DEBUG + #define btAssert assert +#else + #define btAssert(x) +#endif + //btFullAssert is optional, slows down a lot + #define btFullAssert(x) + + #define btLikely(_c) _c + #define btUnlikely(_c) _c + +#else + +#if defined (__CELLOS_LV2__) + #define SIMD_FORCE_INLINE inline + #define ATTRIBUTE_ALIGNED16(a) a __attribute__ ((aligned (16))) + #define ATTRIBUTE_ALIGNED128(a) a __attribute__ ((aligned (128))) + #ifndef assert + #include + #endif +#ifdef BT_DEBUG + #define btAssert assert +#else + #define btAssert(x) +#endif + //btFullAssert is optional, slows down a lot + #define btFullAssert(x) + + #define btLikely(_c) _c + #define btUnlikely(_c) _c + +#else + +#ifdef USE_LIBSPE2 + + #define SIMD_FORCE_INLINE __inline + #define ATTRIBUTE_ALIGNED16(a) a __attribute__ ((aligned (16))) + #define ATTRIBUTE_ALIGNED128(a) a __attribute__ ((aligned (128))) + #ifndef assert + #include + #endif +#ifdef BT_DEBUG + #define btAssert assert +#else + #define btAssert(x) +#endif + //btFullAssert is optional, slows down a lot + #define btFullAssert(x) + + + #define btLikely(_c) __builtin_expect((_c), 1) + #define btUnlikely(_c) __builtin_expect((_c), 0) + + +#else + //non-windows systems + +#if (defined (__APPLE__) && defined (__i386__) && (!defined (BT_USE_DOUBLE_PRECISION))) + #define BT_USE_SSE + #include + + #define SIMD_FORCE_INLINE inline +///@todo: check out alignment methods for other platforms/compilers + #define ATTRIBUTE_ALIGNED16(a) a __attribute__ ((aligned (16))) + #define ATTRIBUTE_ALIGNED128(a) a __attribute__ ((aligned (128))) + #ifndef assert + #include + #endif + + #if defined(DEBUG) || defined (_DEBUG) + #define btAssert assert + #else + #define btAssert(x) + #endif + + //btFullAssert is optional, slows down a lot + #define btFullAssert(x) + #define btLikely(_c) _c + #define btUnlikely(_c) _c + +#else + + #define SIMD_FORCE_INLINE inline + ///@todo: check out alignment methods for other platforms/compilers + ///#define ATTRIBUTE_ALIGNED16(a) a __attribute__ ((aligned (16))) + ///#define ATTRIBUTE_ALIGNED128(a) a __attribute__ ((aligned (128))) + #define ATTRIBUTE_ALIGNED16(a) a + #define ATTRIBUTE_ALIGNED128(a) a + #ifndef assert + #include + #endif + +#if defined(DEBUG) || defined (_DEBUG) + #define btAssert assert +#else + #define btAssert(x) +#endif + + //btFullAssert is optional, slows down a lot + #define btFullAssert(x) + #define btLikely(_c) _c + #define btUnlikely(_c) _c +#endif //__APPLE__ + +#endif // LIBSPE2 + +#endif //__CELLOS_LV2__ +#endif + + +///The btScalar type abstracts floating point numbers, to easily switch between double and single floating point precision. +#if defined(BT_USE_DOUBLE_PRECISION) +typedef double btScalar; +//this number could be bigger in double precision +#define BT_LARGE_FLOAT 1e30 +#else +typedef float btScalar; +//keep BT_LARGE_FLOAT*BT_LARGE_FLOAT < FLT_MAX +#define BT_LARGE_FLOAT 1e18f +#endif + + + +#define BT_DECLARE_ALIGNED_ALLOCATOR() \ + SIMD_FORCE_INLINE void* operator new(size_t sizeInBytes) { return btAlignedAlloc(sizeInBytes,16); } \ + SIMD_FORCE_INLINE void operator delete(void* ptr) { btAlignedFree(ptr); } \ + SIMD_FORCE_INLINE void* operator new(size_t, void* ptr) { return ptr; } \ + SIMD_FORCE_INLINE void operator delete(void*, void*) { } \ + SIMD_FORCE_INLINE void* operator new[](size_t sizeInBytes) { return btAlignedAlloc(sizeInBytes,16); } \ + SIMD_FORCE_INLINE void operator delete[](void* ptr) { btAlignedFree(ptr); } \ + SIMD_FORCE_INLINE void* operator new[](size_t, void* ptr) { return ptr; } \ + SIMD_FORCE_INLINE void operator delete[](void*, void*) { } \ + + + +#if defined(BT_USE_DOUBLE_PRECISION) || defined(BT_FORCE_DOUBLE_FUNCTIONS) + +SIMD_FORCE_INLINE btScalar btSqrt(btScalar x) { return sqrt(x); } +SIMD_FORCE_INLINE btScalar btFabs(btScalar x) { return fabs(x); } +SIMD_FORCE_INLINE btScalar btCos(btScalar x) { return cos(x); } +SIMD_FORCE_INLINE btScalar btSin(btScalar x) { return sin(x); } +SIMD_FORCE_INLINE btScalar btTan(btScalar x) { return tan(x); } +SIMD_FORCE_INLINE btScalar btAcos(btScalar x) { return acos(x); } +SIMD_FORCE_INLINE btScalar btAsin(btScalar x) { return asin(x); } +SIMD_FORCE_INLINE btScalar btAtan(btScalar x) { return atan(x); } +SIMD_FORCE_INLINE btScalar btAtan2(btScalar x, btScalar y) { return atan2(x, y); } +SIMD_FORCE_INLINE btScalar btExp(btScalar x) { return exp(x); } +SIMD_FORCE_INLINE btScalar btLog(btScalar x) { return log(x); } +SIMD_FORCE_INLINE btScalar btPow(btScalar x,btScalar y) { return pow(x,y); } +SIMD_FORCE_INLINE btScalar btFmod(btScalar x,btScalar y) { return fmod(x,y); } + +#else + +SIMD_FORCE_INLINE btScalar btSqrt(btScalar y) +{ +#ifdef USE_APPROXIMATION + double x, z, tempf; + unsigned long *tfptr = ((unsigned long *)&tempf) + 1; + + tempf = y; + *tfptr = (0xbfcdd90a - *tfptr)>>1; /* estimate of 1/sqrt(y) */ + x = tempf; + z = y*btScalar(0.5); /* hoist out the “/2” */ + x = (btScalar(1.5)*x)-(x*x)*(x*z); /* iteration formula */ + x = (btScalar(1.5)*x)-(x*x)*(x*z); + x = (btScalar(1.5)*x)-(x*x)*(x*z); + x = (btScalar(1.5)*x)-(x*x)*(x*z); + x = (btScalar(1.5)*x)-(x*x)*(x*z); + return x*y; +#else + return sqrtf(y); +#endif +} +SIMD_FORCE_INLINE btScalar btFabs(btScalar x) { return fabsf(x); } +SIMD_FORCE_INLINE btScalar btCos(btScalar x) { return cosf(x); } +SIMD_FORCE_INLINE btScalar btSin(btScalar x) { return sinf(x); } +SIMD_FORCE_INLINE btScalar btTan(btScalar x) { return tanf(x); } +SIMD_FORCE_INLINE btScalar btAcos(btScalar x) { + btAssert(x <= btScalar(1.)); + return acosf(x); +} +SIMD_FORCE_INLINE btScalar btAsin(btScalar x) { return asinf(x); } +SIMD_FORCE_INLINE btScalar btAtan(btScalar x) { return atanf(x); } +SIMD_FORCE_INLINE btScalar btAtan2(btScalar x, btScalar y) { return atan2f(x, y); } +SIMD_FORCE_INLINE btScalar btExp(btScalar x) { return expf(x); } +SIMD_FORCE_INLINE btScalar btLog(btScalar x) { return logf(x); } +SIMD_FORCE_INLINE btScalar btPow(btScalar x,btScalar y) { return powf(x,y); } +SIMD_FORCE_INLINE btScalar btFmod(btScalar x,btScalar y) { return fmodf(x,y); } + +#endif + +#define SIMD_2_PI btScalar(6.283185307179586232) +#define SIMD_PI (SIMD_2_PI * btScalar(0.5)) +#define SIMD_HALF_PI (SIMD_2_PI * btScalar(0.25)) +#define SIMD_RADS_PER_DEG (SIMD_2_PI / btScalar(360.0)) +#define SIMD_DEGS_PER_RAD (btScalar(360.0) / SIMD_2_PI) +#define SIMDSQRT12 btScalar(0.7071067811865475244008443621048490) + +#define btRecipSqrt(x) ((btScalar)(btScalar(1.0)/btSqrt(btScalar(x)))) /* reciprocal square root */ + + +#ifdef BT_USE_DOUBLE_PRECISION +#define SIMD_EPSILON DBL_EPSILON +#define SIMD_INFINITY DBL_MAX +#else +#define SIMD_EPSILON FLT_EPSILON +#define SIMD_INFINITY FLT_MAX +#endif + +SIMD_FORCE_INLINE btScalar btAtan2Fast(btScalar y, btScalar x) +{ + btScalar coeff_1 = SIMD_PI / 4.0f; + btScalar coeff_2 = 3.0f * coeff_1; + btScalar abs_y = btFabs(y); + btScalar angle; + if (x >= 0.0f) { + btScalar r = (x - abs_y) / (x + abs_y); + angle = coeff_1 - coeff_1 * r; + } else { + btScalar r = (x + abs_y) / (abs_y - x); + angle = coeff_2 - coeff_1 * r; + } + return (y < 0.0f) ? -angle : angle; +} + +SIMD_FORCE_INLINE bool btFuzzyZero(btScalar x) { return btFabs(x) < SIMD_EPSILON; } + +SIMD_FORCE_INLINE bool btEqual(btScalar a, btScalar eps) { + return (((a) <= eps) && !((a) < -eps)); +} +SIMD_FORCE_INLINE bool btGreaterEqual (btScalar a, btScalar eps) { + return (!((a) <= eps)); +} + + +SIMD_FORCE_INLINE int btIsNegative(btScalar x) { + return x < btScalar(0.0) ? 1 : 0; +} + +SIMD_FORCE_INLINE btScalar btRadians(btScalar x) { return x * SIMD_RADS_PER_DEG; } +SIMD_FORCE_INLINE btScalar btDegrees(btScalar x) { return x * SIMD_DEGS_PER_RAD; } + +#define BT_DECLARE_HANDLE(name) typedef struct name##__ { int unused; } *name + +#ifndef btFsel +SIMD_FORCE_INLINE btScalar btFsel(btScalar a, btScalar b, btScalar c) +{ + return a >= 0 ? b : c; +} +#endif +#define btFsels(a,b,c) (btScalar)btFsel(a,b,c) + + +SIMD_FORCE_INLINE bool btMachineIsLittleEndian() +{ + long int i = 1; + const char *p = (const char *) &i; + if (p[0] == 1) // Lowest address contains the least significant byte + return true; + else + return false; +} + + + +///btSelect avoids branches, which makes performance much better for consoles like Playstation 3 and XBox 360 +///Thanks Phil Knight. See also http://www.cellperformance.com/articles/2006/04/more_techniques_for_eliminatin_1.html +SIMD_FORCE_INLINE unsigned btSelect(unsigned condition, unsigned valueIfConditionNonZero, unsigned valueIfConditionZero) +{ + // Set testNz to 0xFFFFFFFF if condition is nonzero, 0x00000000 if condition is zero + // Rely on positive value or'ed with its negative having sign bit on + // and zero value or'ed with its negative (which is still zero) having sign bit off + // Use arithmetic shift right, shifting the sign bit through all 32 bits + unsigned testNz = (unsigned)(((int)condition | -(int)condition) >> 31); + unsigned testEqz = ~testNz; + return ((valueIfConditionNonZero & testNz) | (valueIfConditionZero & testEqz)); +} +SIMD_FORCE_INLINE int btSelect(unsigned condition, int valueIfConditionNonZero, int valueIfConditionZero) +{ + unsigned testNz = (unsigned)(((int)condition | -(int)condition) >> 31); + unsigned testEqz = ~testNz; + return static_cast((valueIfConditionNonZero & testNz) | (valueIfConditionZero & testEqz)); +} +SIMD_FORCE_INLINE float btSelect(unsigned condition, float valueIfConditionNonZero, float valueIfConditionZero) +{ +#ifdef BT_HAVE_NATIVE_FSEL + return (float)btFsel((btScalar)condition - btScalar(1.0f), valueIfConditionNonZero, valueIfConditionZero); +#else + return (condition != 0) ? valueIfConditionNonZero : valueIfConditionZero; +#endif +} + +template SIMD_FORCE_INLINE void btSwap(T& a, T& b) +{ + T tmp = a; + a = b; + b = tmp; +} + + +//PCK: endian swapping functions +SIMD_FORCE_INLINE unsigned btSwapEndian(unsigned val) +{ + return (((val & 0xff000000) >> 24) | ((val & 0x00ff0000) >> 8) | ((val & 0x0000ff00) << 8) | ((val & 0x000000ff) << 24)); +} + +SIMD_FORCE_INLINE unsigned short btSwapEndian(unsigned short val) +{ + return static_cast(((val & 0xff00) >> 8) | ((val & 0x00ff) << 8)); +} + +SIMD_FORCE_INLINE unsigned btSwapEndian(int val) +{ + return btSwapEndian((unsigned)val); +} + +SIMD_FORCE_INLINE unsigned short btSwapEndian(short val) +{ + return btSwapEndian((unsigned short) val); +} + +///btSwapFloat uses using char pointers to swap the endianness +////btSwapFloat/btSwapDouble will NOT return a float, because the machine might 'correct' invalid floating point values +///Not all values of sign/exponent/mantissa are valid floating point numbers according to IEEE 754. +///When a floating point unit is faced with an invalid value, it may actually change the value, or worse, throw an exception. +///In most systems, running user mode code, you wouldn't get an exception, but instead the hardware/os/runtime will 'fix' the number for you. +///so instead of returning a float/double, we return integer/long long integer +SIMD_FORCE_INLINE unsigned int btSwapEndianFloat(float d) +{ + unsigned int a = 0; + unsigned char *dst = (unsigned char *)&a; + unsigned char *src = (unsigned char *)&d; + + dst[0] = src[3]; + dst[1] = src[2]; + dst[2] = src[1]; + dst[3] = src[0]; + return a; +} + +// unswap using char pointers +SIMD_FORCE_INLINE float btUnswapEndianFloat(unsigned int a) +{ + float d = 0.0f; + unsigned char *src = (unsigned char *)&a; + unsigned char *dst = (unsigned char *)&d; + + dst[0] = src[3]; + dst[1] = src[2]; + dst[2] = src[1]; + dst[3] = src[0]; + + return d; +} + + +// swap using char pointers +SIMD_FORCE_INLINE void btSwapEndianDouble(double d, unsigned char* dst) +{ + unsigned char *src = (unsigned char *)&d; + + dst[0] = src[7]; + dst[1] = src[6]; + dst[2] = src[5]; + dst[3] = src[4]; + dst[4] = src[3]; + dst[5] = src[2]; + dst[6] = src[1]; + dst[7] = src[0]; + +} + +// unswap using char pointers +SIMD_FORCE_INLINE double btUnswapEndianDouble(const unsigned char *src) +{ + double d = 0.0; + unsigned char *dst = (unsigned char *)&d; + + dst[0] = src[7]; + dst[1] = src[6]; + dst[2] = src[5]; + dst[3] = src[4]; + dst[4] = src[3]; + dst[5] = src[2]; + dst[6] = src[1]; + dst[7] = src[0]; + + return d; +} + +// returns normalized value in range [-SIMD_PI, SIMD_PI] +SIMD_FORCE_INLINE btScalar btNormalizeAngle(btScalar angleInRadians) +{ + angleInRadians = btFmod(angleInRadians, SIMD_2_PI); + if(angleInRadians < -SIMD_PI) + { + return angleInRadians + SIMD_2_PI; + } + else if(angleInRadians > SIMD_PI) + { + return angleInRadians - SIMD_2_PI; + } + else + { + return angleInRadians; + } +} + +///rudimentary class to provide type info +struct btTypedObject +{ + btTypedObject(int objectType) + :m_objectType(objectType) + { + } + int m_objectType; + inline int getObjectType() const + { + return m_objectType; + } +}; +#endif //SIMD___SCALAR_H diff --git a/Engine/lib/bullet/src/LinearMath/btStackAlloc.h b/Engine/lib/bullet/src/LinearMath/btStackAlloc.h new file mode 100644 index 000000000..397b08487 --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/btStackAlloc.h @@ -0,0 +1,116 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / Erwin Coumans http://continuousphysics.com/Bullet/ + +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. +*/ + +/* +StackAlloc extracted from GJK-EPA collision solver by Nathanael Presson +Nov.2006 +*/ + +#ifndef BT_STACK_ALLOC +#define BT_STACK_ALLOC + +#include "btScalar.h" //for btAssert +#include "btAlignedAllocator.h" + +///The btBlock class is an internal structure for the btStackAlloc memory allocator. +struct btBlock +{ + btBlock* previous; + unsigned char* address; +}; + +///The StackAlloc class provides some fast stack-based memory allocator (LIFO last-in first-out) +class btStackAlloc +{ +public: + + btStackAlloc(unsigned int size) { ctor();create(size); } + ~btStackAlloc() { destroy(); } + + inline void create(unsigned int size) + { + destroy(); + data = (unsigned char*) btAlignedAlloc(size,16); + totalsize = size; + } + inline void destroy() + { + btAssert(usedsize==0); + //Raise(L"StackAlloc is still in use"); + + if(usedsize==0) + { + if(!ischild && data) + btAlignedFree(data); + + data = 0; + usedsize = 0; + } + + } + + int getAvailableMemory() const + { + return static_cast(totalsize - usedsize); + } + + unsigned char* allocate(unsigned int size) + { + const unsigned int nus(usedsize+size); + if(nusprevious = current; + pb->address = data+usedsize; + current = pb; + return(pb); + } + SIMD_FORCE_INLINE void endBlock(btBlock* block) + { + btAssert(block==current); + //Raise(L"Unmatched blocks"); + if(block==current) + { + current = block->previous; + usedsize = (unsigned int)((block->address-data)-sizeof(btBlock)); + } + } + +private: + void ctor() + { + data = 0; + totalsize = 0; + usedsize = 0; + current = 0; + ischild = false; + } + unsigned char* data; + unsigned int totalsize; + unsigned int usedsize; + btBlock* current; + bool ischild; +}; + +#endif //BT_STACK_ALLOC diff --git a/Engine/lib/bullet/src/LinearMath/btTransform.h b/Engine/lib/bullet/src/LinearMath/btTransform.h new file mode 100644 index 000000000..c4fe33eec --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/btTransform.h @@ -0,0 +1,243 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / Erwin Coumans http://continuousphysics.com/Bullet/ + +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 btTransform_H +#define btTransform_H + +#include "btVector3.h" +#include "btMatrix3x3.h" + + +/**@brief The btTransform class supports rigid transforms with only translation and rotation and no scaling/shear. + *It can be used in combination with btVector3, btQuaternion and btMatrix3x3 linear algebra classes. */ +class btTransform { + + +public: + + /**@brief No initialization constructor */ + btTransform() {} + /**@brief Constructor from btQuaternion (optional btVector3 ) + * @param q Rotation from quaternion + * @param c Translation from Vector (default 0,0,0) */ + explicit SIMD_FORCE_INLINE btTransform(const btQuaternion& q, + const btVector3& c = btVector3(btScalar(0), btScalar(0), btScalar(0))) + : m_basis(q), + m_origin(c) + {} + + /**@brief Constructor from btMatrix3x3 (optional btVector3) + * @param b Rotation from Matrix + * @param c Translation from Vector default (0,0,0)*/ + explicit SIMD_FORCE_INLINE btTransform(const btMatrix3x3& b, + const btVector3& c = btVector3(btScalar(0), btScalar(0), btScalar(0))) + : m_basis(b), + m_origin(c) + {} + /**@brief Copy constructor */ + SIMD_FORCE_INLINE btTransform (const btTransform& other) + : m_basis(other.m_basis), + m_origin(other.m_origin) + { + } + /**@brief Assignment Operator */ + SIMD_FORCE_INLINE btTransform& operator=(const btTransform& other) + { + m_basis = other.m_basis; + m_origin = other.m_origin; + return *this; + } + + + /**@brief Set the current transform as the value of the product of two transforms + * @param t1 Transform 1 + * @param t2 Transform 2 + * This = Transform1 * Transform2 */ + SIMD_FORCE_INLINE void mult(const btTransform& t1, const btTransform& t2) { + m_basis = t1.m_basis * t2.m_basis; + m_origin = t1(t2.m_origin); + } + +/* void multInverseLeft(const btTransform& t1, const btTransform& t2) { + btVector3 v = t2.m_origin - t1.m_origin; + m_basis = btMultTransposeLeft(t1.m_basis, t2.m_basis); + m_origin = v * t1.m_basis; + } + */ + +/**@brief Return the transform of the vector */ + SIMD_FORCE_INLINE btVector3 operator()(const btVector3& x) const + { + return btVector3(m_basis[0].dot(x) + m_origin.x(), + m_basis[1].dot(x) + m_origin.y(), + m_basis[2].dot(x) + m_origin.z()); + } + + /**@brief Return the transform of the vector */ + SIMD_FORCE_INLINE btVector3 operator*(const btVector3& x) const + { + return (*this)(x); + } + + /**@brief Return the transform of the btQuaternion */ + SIMD_FORCE_INLINE btQuaternion operator*(const btQuaternion& q) const + { + return getRotation() * q; + } + + /**@brief Return the basis matrix for the rotation */ + SIMD_FORCE_INLINE btMatrix3x3& getBasis() { return m_basis; } + /**@brief Return the basis matrix for the rotation */ + SIMD_FORCE_INLINE const btMatrix3x3& getBasis() const { return m_basis; } + + /**@brief Return the origin vector translation */ + SIMD_FORCE_INLINE btVector3& getOrigin() { return m_origin; } + /**@brief Return the origin vector translation */ + SIMD_FORCE_INLINE const btVector3& getOrigin() const { return m_origin; } + + /**@brief Return a quaternion representing the rotation */ + btQuaternion getRotation() const { + btQuaternion q; + m_basis.getRotation(q); + return q; + } + + + /**@brief Set from an array + * @param m A pointer to a 15 element array (12 rotation(row major padded on the right by 1), and 3 translation */ + void setFromOpenGLMatrix(const btScalar *m) + { + m_basis.setFromOpenGLSubMatrix(m); + m_origin.setValue(m[12],m[13],m[14]); + } + + /**@brief Fill an array representation + * @param m A pointer to a 15 element array (12 rotation(row major padded on the right by 1), and 3 translation */ + void getOpenGLMatrix(btScalar *m) const + { + m_basis.getOpenGLSubMatrix(m); + m[12] = m_origin.x(); + m[13] = m_origin.y(); + m[14] = m_origin.z(); + m[15] = btScalar(1.0); + } + + /**@brief Set the translational element + * @param origin The vector to set the translation to */ + SIMD_FORCE_INLINE void setOrigin(const btVector3& origin) + { + m_origin = origin; + } + + SIMD_FORCE_INLINE btVector3 invXform(const btVector3& inVec) const; + + + /**@brief Set the rotational element by btMatrix3x3 */ + SIMD_FORCE_INLINE void setBasis(const btMatrix3x3& basis) + { + m_basis = basis; + } + + /**@brief Set the rotational element by btQuaternion */ + SIMD_FORCE_INLINE void setRotation(const btQuaternion& q) + { + m_basis.setRotation(q); + } + + + /**@brief Set this transformation to the identity */ + void setIdentity() + { + m_basis.setIdentity(); + m_origin.setValue(btScalar(0.0), btScalar(0.0), btScalar(0.0)); + } + + /**@brief Multiply this Transform by another(this = this * another) + * @param t The other transform */ + btTransform& operator*=(const btTransform& t) + { + m_origin += m_basis * t.m_origin; + m_basis *= t.m_basis; + return *this; + } + + /**@brief Return the inverse of this transform */ + btTransform inverse() const + { + btMatrix3x3 inv = m_basis.transpose(); + return btTransform(inv, inv * -m_origin); + } + + /**@brief Return the inverse of this transform times the other transform + * @param t The other transform + * return this.inverse() * the other */ + btTransform inverseTimes(const btTransform& t) const; + + /**@brief Return the product of this transform and the other */ + btTransform operator*(const btTransform& t) const; + + /**@brief Return an identity transform */ + static const btTransform& getIdentity() + { + static const btTransform identityTransform(btMatrix3x3::getIdentity()); + return identityTransform; + } + +private: + ///Storage for the rotation + btMatrix3x3 m_basis; + ///Storage for the translation + btVector3 m_origin; +}; + + +SIMD_FORCE_INLINE btVector3 +btTransform::invXform(const btVector3& inVec) const +{ + btVector3 v = inVec - m_origin; + return (m_basis.transpose() * v); +} + +SIMD_FORCE_INLINE btTransform +btTransform::inverseTimes(const btTransform& t) const +{ + btVector3 v = t.getOrigin() - m_origin; + return btTransform(m_basis.transposeTimes(t.m_basis), + v * m_basis); +} + +SIMD_FORCE_INLINE btTransform +btTransform::operator*(const btTransform& t) const +{ + return btTransform(m_basis * t.m_basis, + (*this)(t.m_origin)); +} + +/**@brief Test if two transforms have all elements equal */ +SIMD_FORCE_INLINE bool operator==(const btTransform& t1, const btTransform& t2) +{ + return ( t1.getBasis() == t2.getBasis() && + t1.getOrigin() == t2.getOrigin() ); +} + + +#endif + + + + + + diff --git a/Engine/lib/bullet/src/LinearMath/btTransformUtil.h b/Engine/lib/bullet/src/LinearMath/btTransformUtil.h new file mode 100644 index 000000000..1603d3e05 --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/btTransformUtil.h @@ -0,0 +1,230 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / Erwin Coumans http://continuousphysics.com/Bullet/ + +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 SIMD_TRANSFORM_UTIL_H +#define SIMD_TRANSFORM_UTIL_H + +#include "btTransform.h" +#define ANGULAR_MOTION_THRESHOLD btScalar(0.5)*SIMD_HALF_PI + + + + +SIMD_FORCE_INLINE btVector3 btAabbSupport(const btVector3& halfExtents,const btVector3& supportDir) +{ + return btVector3(supportDir.x() < btScalar(0.0) ? -halfExtents.x() : halfExtents.x(), + supportDir.y() < btScalar(0.0) ? -halfExtents.y() : halfExtents.y(), + supportDir.z() < btScalar(0.0) ? -halfExtents.z() : halfExtents.z()); +} + + + + + + +/// Utils related to temporal transforms +class btTransformUtil +{ + +public: + + static void integrateTransform(const btTransform& curTrans,const btVector3& linvel,const btVector3& angvel,btScalar timeStep,btTransform& predictedTransform) + { + predictedTransform.setOrigin(curTrans.getOrigin() + linvel * timeStep); +// #define QUATERNION_DERIVATIVE + #ifdef QUATERNION_DERIVATIVE + btQuaternion predictedOrn = curTrans.getRotation(); + predictedOrn += (angvel * predictedOrn) * (timeStep * btScalar(0.5)); + predictedOrn.normalize(); + #else + //Exponential map + //google for "Practical Parameterization of Rotations Using the Exponential Map", F. Sebastian Grassia + + btVector3 axis; + btScalar fAngle = angvel.length(); + //limit the angular motion + if (fAngle*timeStep > ANGULAR_MOTION_THRESHOLD) + { + fAngle = ANGULAR_MOTION_THRESHOLD / timeStep; + } + + if ( fAngle < btScalar(0.001) ) + { + // use Taylor's expansions of sync function + axis = angvel*( btScalar(0.5)*timeStep-(timeStep*timeStep*timeStep)*(btScalar(0.020833333333))*fAngle*fAngle ); + } + else + { + // sync(fAngle) = sin(c*fAngle)/t + axis = angvel*( btSin(btScalar(0.5)*fAngle*timeStep)/fAngle ); + } + btQuaternion dorn (axis.x(),axis.y(),axis.z(),btCos( fAngle*timeStep*btScalar(0.5) )); + btQuaternion orn0 = curTrans.getRotation(); + + btQuaternion predictedOrn = dorn * orn0; + predictedOrn.normalize(); + #endif + predictedTransform.setRotation(predictedOrn); + } + + static void calculateVelocityQuaternion(const btVector3& pos0,const btVector3& pos1,const btQuaternion& orn0,const btQuaternion& orn1,btScalar timeStep,btVector3& linVel,btVector3& angVel) + { + linVel = (pos1 - pos0) / timeStep; + btVector3 axis; + btScalar angle; + if (orn0 != orn1) + { + calculateDiffAxisAngleQuaternion(orn0,orn1,axis,angle); + angVel = axis * angle / timeStep; + } else + { + angVel.setValue(0,0,0); + } + } + + static void calculateDiffAxisAngleQuaternion(const btQuaternion& orn0,const btQuaternion& orn1a,btVector3& axis,btScalar& angle) + { + btQuaternion orn1 = orn0.nearest(orn1a); + btQuaternion dorn = orn1 * orn0.inverse(); + ///floating point inaccuracy can lead to w component > 1..., which breaks + dorn.normalize(); + angle = dorn.getAngle(); + axis = btVector3(dorn.x(),dorn.y(),dorn.z()); + axis[3] = btScalar(0.); + //check for axis length + btScalar len = axis.length2(); + if (len < SIMD_EPSILON*SIMD_EPSILON) + axis = btVector3(btScalar(1.),btScalar(0.),btScalar(0.)); + else + axis /= btSqrt(len); + } + + static void calculateVelocity(const btTransform& transform0,const btTransform& transform1,btScalar timeStep,btVector3& linVel,btVector3& angVel) + { + linVel = (transform1.getOrigin() - transform0.getOrigin()) / timeStep; + btVector3 axis; + btScalar angle; + calculateDiffAxisAngle(transform0,transform1,axis,angle); + angVel = axis * angle / timeStep; + } + + static void calculateDiffAxisAngle(const btTransform& transform0,const btTransform& transform1,btVector3& axis,btScalar& angle) + { + btMatrix3x3 dmat = transform1.getBasis() * transform0.getBasis().inverse(); + btQuaternion dorn; + dmat.getRotation(dorn); + + ///floating point inaccuracy can lead to w component > 1..., which breaks + dorn.normalize(); + + angle = dorn.getAngle(); + axis = btVector3(dorn.x(),dorn.y(),dorn.z()); + axis[3] = btScalar(0.); + //check for axis length + btScalar len = axis.length2(); + if (len < SIMD_EPSILON*SIMD_EPSILON) + axis = btVector3(btScalar(1.),btScalar(0.),btScalar(0.)); + else + axis /= btSqrt(len); + } + +}; + + +///The btConvexSeparatingDistanceUtil can help speed up convex collision detection +///by conservatively updating a cached separating distance/vector instead of re-calculating the closest distance +class btConvexSeparatingDistanceUtil +{ + btQuaternion m_ornA; + btQuaternion m_ornB; + btVector3 m_posA; + btVector3 m_posB; + + btVector3 m_separatingNormal; + + btScalar m_boundingRadiusA; + btScalar m_boundingRadiusB; + btScalar m_separatingDistance; + +public: + + btConvexSeparatingDistanceUtil(btScalar boundingRadiusA,btScalar boundingRadiusB) + :m_boundingRadiusA(boundingRadiusA), + m_boundingRadiusB(boundingRadiusB), + m_separatingDistance(0.f) + { + } + + btScalar getConservativeSeparatingDistance() + { + return m_separatingDistance; + } + + void updateSeparatingDistance(const btTransform& transA,const btTransform& transB) + { + const btVector3& toPosA = transA.getOrigin(); + const btVector3& toPosB = transB.getOrigin(); + btQuaternion toOrnA = transA.getRotation(); + btQuaternion toOrnB = transB.getRotation(); + + if (m_separatingDistance>0.f) + { + + + btVector3 linVelA,angVelA,linVelB,angVelB; + btTransformUtil::calculateVelocityQuaternion(m_posA,toPosA,m_ornA,toOrnA,btScalar(1.),linVelA,angVelA); + btTransformUtil::calculateVelocityQuaternion(m_posB,toPosB,m_ornB,toOrnB,btScalar(1.),linVelB,angVelB); + btScalar maxAngularProjectedVelocity = angVelA.length() * m_boundingRadiusA + angVelB.length() * m_boundingRadiusB; + btVector3 relLinVel = (linVelB-linVelA); + btScalar relLinVelocLength = (linVelB-linVelA).dot(m_separatingNormal); + if (relLinVelocLength<0.f) + { + relLinVelocLength = 0.f; + } + + btScalar projectedMotion = maxAngularProjectedVelocity +relLinVelocLength; + m_separatingDistance -= projectedMotion; + } + + m_posA = toPosA; + m_posB = toPosB; + m_ornA = toOrnA; + m_ornB = toOrnB; + } + + void initSeparatingDistance(const btVector3& separatingVector,btScalar separatingDistance,const btTransform& transA,const btTransform& transB) + { + m_separatingDistance = separatingDistance; + + if (m_separatingDistance>0.f) + { + m_separatingNormal = separatingVector; + + const btVector3& toPosA = transA.getOrigin(); + const btVector3& toPosB = transB.getOrigin(); + btQuaternion toOrnA = transA.getRotation(); + btQuaternion toOrnB = transB.getRotation(); + m_posA = toPosA; + m_posB = toPosB; + m_ornA = toOrnA; + m_ornB = toOrnB; + } + } + +}; + + +#endif //SIMD_TRANSFORM_UTIL_H + diff --git a/Engine/lib/bullet/src/LinearMath/btVector3.h b/Engine/lib/bullet/src/LinearMath/btVector3.h new file mode 100644 index 000000000..4a670f4fb --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/btVector3.h @@ -0,0 +1,660 @@ +/* +Copyright (c) 2003-2006 Gino van den Bergen / Erwin Coumans http://continuousphysics.com/Bullet/ + +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 SIMD__VECTOR3_H +#define SIMD__VECTOR3_H + + +#include "btScalar.h" +#include "btScalar.h" +#include "btMinMax.h" +/**@brief btVector3 can be used to represent 3D points and vectors. + * It has an un-used w component to suit 16-byte alignment when btVector3 is stored in containers. This extra component can be used by derived classes (Quaternion?) or by user + * Ideally, this class should be replaced by a platform optimized SIMD version that keeps the data in registers + */ + +ATTRIBUTE_ALIGNED16(class) btVector3 +{ +public: + +#if defined (__SPU__) && defined (__CELLOS_LV2__) + btScalar m_floats[4]; +public: + SIMD_FORCE_INLINE const vec_float4& get128() const + { + return *((const vec_float4*)&m_floats[0]); + } +public: +#else //__CELLOS_LV2__ __SPU__ +#ifdef BT_USE_SSE // WIN32 + union { + __m128 mVec128; + btScalar m_floats[4]; + }; + SIMD_FORCE_INLINE __m128 get128() const + { + return mVec128; + } + SIMD_FORCE_INLINE void set128(__m128 v128) + { + mVec128 = v128; + } +#else + btScalar m_floats[4]; +#endif +#endif //__CELLOS_LV2__ __SPU__ + + public: + + /**@brief No initialization constructor */ + SIMD_FORCE_INLINE btVector3() {} + + + + /**@brief Constructor from scalars + * @param x X value + * @param y Y value + * @param z Z value + */ + SIMD_FORCE_INLINE btVector3(const btScalar& x, const btScalar& y, const btScalar& z) + { + m_floats[0] = x; + m_floats[1] = y; + m_floats[2] = z; + m_floats[3] = btScalar(0.); + } + + +/**@brief Add a vector to this one + * @param The vector to add to this one */ + SIMD_FORCE_INLINE btVector3& operator+=(const btVector3& v) + { + + m_floats[0] += v.m_floats[0]; m_floats[1] += v.m_floats[1];m_floats[2] += v.m_floats[2]; + return *this; + } + + + /**@brief Subtract a vector from this one + * @param The vector to subtract */ + SIMD_FORCE_INLINE btVector3& operator-=(const btVector3& v) + { + m_floats[0] -= v.m_floats[0]; m_floats[1] -= v.m_floats[1];m_floats[2] -= v.m_floats[2]; + return *this; + } + /**@brief Scale the vector + * @param s Scale factor */ + SIMD_FORCE_INLINE btVector3& operator*=(const btScalar& s) + { + m_floats[0] *= s; m_floats[1] *= s;m_floats[2] *= s; + return *this; + } + + /**@brief Inversely scale the vector + * @param s Scale factor to divide by */ + SIMD_FORCE_INLINE btVector3& operator/=(const btScalar& s) + { + btFullAssert(s != btScalar(0.0)); + return *this *= btScalar(1.0) / s; + } + + /**@brief Return the dot product + * @param v The other vector in the dot product */ + SIMD_FORCE_INLINE btScalar dot(const btVector3& v) const + { + return m_floats[0] * v.m_floats[0] + m_floats[1] * v.m_floats[1] +m_floats[2] * v.m_floats[2]; + } + + /**@brief Return the length of the vector squared */ + SIMD_FORCE_INLINE btScalar length2() const + { + return dot(*this); + } + + /**@brief Return the length of the vector */ + SIMD_FORCE_INLINE btScalar length() const + { + return btSqrt(length2()); + } + + /**@brief Return the distance squared between the ends of this and another vector + * This is symantically treating the vector like a point */ + SIMD_FORCE_INLINE btScalar distance2(const btVector3& v) const; + + /**@brief Return the distance between the ends of this and another vector + * This is symantically treating the vector like a point */ + SIMD_FORCE_INLINE btScalar distance(const btVector3& v) const; + + /**@brief Normalize this vector + * x^2 + y^2 + z^2 = 1 */ + SIMD_FORCE_INLINE btVector3& normalize() + { + return *this /= length(); + } + + /**@brief Return a normalized version of this vector */ + SIMD_FORCE_INLINE btVector3 normalized() const; + + /**@brief Rotate this vector + * @param wAxis The axis to rotate about + * @param angle The angle to rotate by */ + SIMD_FORCE_INLINE btVector3 rotate( const btVector3& wAxis, const btScalar angle ); + + /**@brief Return the angle between this and another vector + * @param v The other vector */ + SIMD_FORCE_INLINE btScalar angle(const btVector3& v) const + { + btScalar s = btSqrt(length2() * v.length2()); + btFullAssert(s != btScalar(0.0)); + return btAcos(dot(v) / s); + } + /**@brief Return a vector will the absolute values of each element */ + SIMD_FORCE_INLINE btVector3 absolute() const + { + return btVector3( + btFabs(m_floats[0]), + btFabs(m_floats[1]), + btFabs(m_floats[2])); + } + /**@brief Return the cross product between this and another vector + * @param v The other vector */ + SIMD_FORCE_INLINE btVector3 cross(const btVector3& v) const + { + return btVector3( + m_floats[1] * v.m_floats[2] -m_floats[2] * v.m_floats[1], + m_floats[2] * v.m_floats[0] - m_floats[0] * v.m_floats[2], + m_floats[0] * v.m_floats[1] - m_floats[1] * v.m_floats[0]); + } + + SIMD_FORCE_INLINE btScalar triple(const btVector3& v1, const btVector3& v2) const + { + return m_floats[0] * (v1.m_floats[1] * v2.m_floats[2] - v1.m_floats[2] * v2.m_floats[1]) + + m_floats[1] * (v1.m_floats[2] * v2.m_floats[0] - v1.m_floats[0] * v2.m_floats[2]) + + m_floats[2] * (v1.m_floats[0] * v2.m_floats[1] - v1.m_floats[1] * v2.m_floats[0]); + } + + /**@brief Return the axis with the smallest value + * Note return values are 0,1,2 for x, y, or z */ + SIMD_FORCE_INLINE int minAxis() const + { + return m_floats[0] < m_floats[1] ? (m_floats[0] return this, t=1 => return other) */ + SIMD_FORCE_INLINE btVector3 lerp(const btVector3& v, const btScalar& t) const + { + return btVector3(m_floats[0] + (v.m_floats[0] - m_floats[0]) * t, + m_floats[1] + (v.m_floats[1] - m_floats[1]) * t, + m_floats[2] + (v.m_floats[2] -m_floats[2]) * t); + } + + /**@brief Elementwise multiply this vector by the other + * @param v The other vector */ + SIMD_FORCE_INLINE btVector3& operator*=(const btVector3& v) + { + m_floats[0] *= v.m_floats[0]; m_floats[1] *= v.m_floats[1];m_floats[2] *= v.m_floats[2]; + return *this; + } + + /**@brief Return the x value */ + SIMD_FORCE_INLINE const btScalar& getX() const { return m_floats[0]; } + /**@brief Return the y value */ + SIMD_FORCE_INLINE const btScalar& getY() const { return m_floats[1]; } + /**@brief Return the z value */ + SIMD_FORCE_INLINE const btScalar& getZ() const { return m_floats[2]; } + /**@brief Set the x value */ + SIMD_FORCE_INLINE void setX(btScalar x) { m_floats[0] = x;}; + /**@brief Set the y value */ + SIMD_FORCE_INLINE void setY(btScalar y) { m_floats[1] = y;}; + /**@brief Set the z value */ + SIMD_FORCE_INLINE void setZ(btScalar z) {m_floats[2] = z;}; + /**@brief Set the w value */ + SIMD_FORCE_INLINE void setW(btScalar w) { m_floats[3] = w;}; + /**@brief Return the x value */ + SIMD_FORCE_INLINE const btScalar& x() const { return m_floats[0]; } + /**@brief Return the y value */ + SIMD_FORCE_INLINE const btScalar& y() const { return m_floats[1]; } + /**@brief Return the z value */ + SIMD_FORCE_INLINE const btScalar& z() const { return m_floats[2]; } + /**@brief Return the w value */ + SIMD_FORCE_INLINE const btScalar& w() const { return m_floats[3]; } + + //SIMD_FORCE_INLINE btScalar& operator[](int i) { return (&m_floats[0])[i]; } + //SIMD_FORCE_INLINE const btScalar& operator[](int i) const { return (&m_floats[0])[i]; } + ///operator btScalar*() replaces operator[], using implicit conversion. We added operator != and operator == to avoid pointer comparisons. + SIMD_FORCE_INLINE operator btScalar *() { return &m_floats[0]; } + SIMD_FORCE_INLINE operator const btScalar *() const { return &m_floats[0]; } + + SIMD_FORCE_INLINE bool operator==(const btVector3& other) const + { + return ((m_floats[3]==other.m_floats[3]) && (m_floats[2]==other.m_floats[2]) && (m_floats[1]==other.m_floats[1]) && (m_floats[0]==other.m_floats[0])); + } + + SIMD_FORCE_INLINE bool operator!=(const btVector3& other) const + { + return !(*this == other); + } + + /**@brief Set each element to the max of the current values and the values of another btVector3 + * @param other The other btVector3 to compare with + */ + SIMD_FORCE_INLINE void setMax(const btVector3& other) + { + btSetMax(m_floats[0], other.m_floats[0]); + btSetMax(m_floats[1], other.m_floats[1]); + btSetMax(m_floats[2], other.m_floats[2]); + btSetMax(m_floats[3], other.w()); + } + /**@brief Set each element to the min of the current values and the values of another btVector3 + * @param other The other btVector3 to compare with + */ + SIMD_FORCE_INLINE void setMin(const btVector3& other) + { + btSetMin(m_floats[0], other.m_floats[0]); + btSetMin(m_floats[1], other.m_floats[1]); + btSetMin(m_floats[2], other.m_floats[2]); + btSetMin(m_floats[3], other.w()); + } + + SIMD_FORCE_INLINE void setValue(const btScalar& x, const btScalar& y, const btScalar& z) + { + m_floats[0]=x; + m_floats[1]=y; + m_floats[2]=z; + m_floats[3] = btScalar(0.); + } + + void getSkewSymmetricMatrix(btVector3* v0,btVector3* v1,btVector3* v2) const + { + v0->setValue(0. ,-z() ,y()); + v1->setValue(z() ,0. ,-x()); + v2->setValue(-y() ,x() ,0.); + } + + void setZero() + { + setValue(btScalar(0.),btScalar(0.),btScalar(0.)); + } + +}; + +/**@brief Return the sum of two vectors (Point symantics)*/ +SIMD_FORCE_INLINE btVector3 +operator+(const btVector3& v1, const btVector3& v2) +{ + return btVector3(v1.m_floats[0] + v2.m_floats[0], v1.m_floats[1] + v2.m_floats[1], v1.m_floats[2] + v2.m_floats[2]); +} + +/**@brief Return the elementwise product of two vectors */ +SIMD_FORCE_INLINE btVector3 +operator*(const btVector3& v1, const btVector3& v2) +{ + return btVector3(v1.m_floats[0] * v2.m_floats[0], v1.m_floats[1] * v2.m_floats[1], v1.m_floats[2] * v2.m_floats[2]); +} + +/**@brief Return the difference between two vectors */ +SIMD_FORCE_INLINE btVector3 +operator-(const btVector3& v1, const btVector3& v2) +{ + return btVector3(v1.m_floats[0] - v2.m_floats[0], v1.m_floats[1] - v2.m_floats[1], v1.m_floats[2] - v2.m_floats[2]); +} +/**@brief Return the negative of the vector */ +SIMD_FORCE_INLINE btVector3 +operator-(const btVector3& v) +{ + return btVector3(-v.m_floats[0], -v.m_floats[1], -v.m_floats[2]); +} + +/**@brief Return the vector scaled by s */ +SIMD_FORCE_INLINE btVector3 +operator*(const btVector3& v, const btScalar& s) +{ + return btVector3(v.m_floats[0] * s, v.m_floats[1] * s, v.m_floats[2] * s); +} + +/**@brief Return the vector scaled by s */ +SIMD_FORCE_INLINE btVector3 +operator*(const btScalar& s, const btVector3& v) +{ + return v * s; +} + +/**@brief Return the vector inversely scaled by s */ +SIMD_FORCE_INLINE btVector3 +operator/(const btVector3& v, const btScalar& s) +{ + btFullAssert(s != btScalar(0.0)); + return v * (btScalar(1.0) / s); +} + +/**@brief Return the vector inversely scaled by s */ +SIMD_FORCE_INLINE btVector3 +operator/(const btVector3& v1, const btVector3& v2) +{ + return btVector3(v1.m_floats[0] / v2.m_floats[0],v1.m_floats[1] / v2.m_floats[1],v1.m_floats[2] / v2.m_floats[2]); +} + +/**@brief Return the dot product between two vectors */ +SIMD_FORCE_INLINE btScalar +btDot(const btVector3& v1, const btVector3& v2) +{ + return v1.dot(v2); +} + + +/**@brief Return the distance squared between two vectors */ +SIMD_FORCE_INLINE btScalar +btDistance2(const btVector3& v1, const btVector3& v2) +{ + return v1.distance2(v2); +} + + +/**@brief Return the distance between two vectors */ +SIMD_FORCE_INLINE btScalar +btDistance(const btVector3& v1, const btVector3& v2) +{ + return v1.distance(v2); +} + +/**@brief Return the angle between two vectors */ +SIMD_FORCE_INLINE btScalar +btAngle(const btVector3& v1, const btVector3& v2) +{ + return v1.angle(v2); +} + +/**@brief Return the cross product of two vectors */ +SIMD_FORCE_INLINE btVector3 +btCross(const btVector3& v1, const btVector3& v2) +{ + return v1.cross(v2); +} + +SIMD_FORCE_INLINE btScalar +btTriple(const btVector3& v1, const btVector3& v2, const btVector3& v3) +{ + return v1.triple(v2, v3); +} + +/**@brief Return the linear interpolation between two vectors + * @param v1 One vector + * @param v2 The other vector + * @param t The ration of this to v (t = 0 => return v1, t=1 => return v2) */ +SIMD_FORCE_INLINE btVector3 +lerp(const btVector3& v1, const btVector3& v2, const btScalar& t) +{ + return v1.lerp(v2, t); +} + + + +SIMD_FORCE_INLINE btScalar btVector3::distance2(const btVector3& v) const +{ + return (v - *this).length2(); +} + +SIMD_FORCE_INLINE btScalar btVector3::distance(const btVector3& v) const +{ + return (v - *this).length(); +} + +SIMD_FORCE_INLINE btVector3 btVector3::normalized() const +{ + return *this / length(); +} + +SIMD_FORCE_INLINE btVector3 btVector3::rotate( const btVector3& wAxis, const btScalar angle ) +{ + // wAxis must be a unit lenght vector + + btVector3 o = wAxis * wAxis.dot( *this ); + btVector3 x = *this - o; + btVector3 y; + + y = wAxis.cross( *this ); + + return ( o + x * btCos( angle ) + y * btSin( angle ) ); +} + +class btVector4 : public btVector3 +{ +public: + + SIMD_FORCE_INLINE btVector4() {} + + + SIMD_FORCE_INLINE btVector4(const btScalar& x, const btScalar& y, const btScalar& z,const btScalar& w) + : btVector3(x,y,z) + { + m_floats[3] = w; + } + + + SIMD_FORCE_INLINE btVector4 absolute4() const + { + return btVector4( + btFabs(m_floats[0]), + btFabs(m_floats[1]), + btFabs(m_floats[2]), + btFabs(m_floats[3])); + } + + + + btScalar getW() const { return m_floats[3];} + + + SIMD_FORCE_INLINE int maxAxis4() const + { + int maxIndex = -1; + btScalar maxVal = btScalar(-BT_LARGE_FLOAT); + if (m_floats[0] > maxVal) + { + maxIndex = 0; + maxVal = m_floats[0]; + } + if (m_floats[1] > maxVal) + { + maxIndex = 1; + maxVal = m_floats[1]; + } + if (m_floats[2] > maxVal) + { + maxIndex = 2; + maxVal =m_floats[2]; + } + if (m_floats[3] > maxVal) + { + maxIndex = 3; + maxVal = m_floats[3]; + } + + + + + return maxIndex; + + } + + + SIMD_FORCE_INLINE int minAxis4() const + { + int minIndex = -1; + btScalar minVal = btScalar(BT_LARGE_FLOAT); + if (m_floats[0] < minVal) + { + minIndex = 0; + minVal = m_floats[0]; + } + if (m_floats[1] < minVal) + { + minIndex = 1; + minVal = m_floats[1]; + } + if (m_floats[2] < minVal) + { + minIndex = 2; + minVal =m_floats[2]; + } + if (m_floats[3] < minVal) + { + minIndex = 3; + minVal = m_floats[3]; + } + + return minIndex; + + } + + + SIMD_FORCE_INLINE int closestAxis4() const + { + return absolute4().maxAxis4(); + } + + + + + /**@brief Set x,y,z and zero w + * @param x Value of x + * @param y Value of y + * @param z Value of z + */ + + +/* void getValue(btScalar *m) const + { + m[0] = m_floats[0]; + m[1] = m_floats[1]; + m[2] =m_floats[2]; + } +*/ +/**@brief Set the values + * @param x Value of x + * @param y Value of y + * @param z Value of z + * @param w Value of w + */ + SIMD_FORCE_INLINE void setValue(const btScalar& x, const btScalar& y, const btScalar& z,const btScalar& w) + { + m_floats[0]=x; + m_floats[1]=y; + m_floats[2]=z; + m_floats[3]=w; + } + + + + +}; + + +///btSwapVector3Endian swaps vector endianness, useful for network and cross-platform serialization +SIMD_FORCE_INLINE void btSwapScalarEndian(const btScalar& sourceVal, btScalar& destVal) +{ + #ifdef BT_USE_DOUBLE_PRECISION + unsigned char* dest = (unsigned char*) &destVal; + unsigned char* src = (unsigned char*) &sourceVal; + dest[0] = src[7]; + dest[1] = src[6]; + dest[2] = src[5]; + dest[3] = src[4]; + dest[4] = src[3]; + dest[5] = src[2]; + dest[6] = src[1]; + dest[7] = src[0]; +#else + unsigned char* dest = (unsigned char*) &destVal; + unsigned char* src = (unsigned char*) &sourceVal; + dest[0] = src[3]; + dest[1] = src[2]; + dest[2] = src[1]; + dest[3] = src[0]; +#endif //BT_USE_DOUBLE_PRECISION +} +///btSwapVector3Endian swaps vector endianness, useful for network and cross-platform serialization +SIMD_FORCE_INLINE void btSwapVector3Endian(const btVector3& sourceVec, btVector3& destVec) +{ + for (int i=0;i<4;i++) + { + btSwapScalarEndian(sourceVec[i],destVec[i]); + } + +} + +///btUnSwapVector3Endian swaps vector endianness, useful for network and cross-platform serialization +SIMD_FORCE_INLINE void btUnSwapVector3Endian(btVector3& vector) +{ + + btVector3 swappedVec; + for (int i=0;i<4;i++) + { + btSwapScalarEndian(vector[i],swappedVec[i]); + } + vector = swappedVec; +} + +SIMD_FORCE_INLINE void btPlaneSpace1 (const btVector3& n, btVector3& p, btVector3& q) +{ + if (btFabs(n.z()) > SIMDSQRT12) { + // choose p in y-z plane + btScalar a = n[1]*n[1] + n[2]*n[2]; + btScalar k = btRecipSqrt (a); + p.setValue(0,-n[2]*k,n[1]*k); + // set q = n x p + q.setValue(a*k,-n[0]*p[2],n[0]*p[1]); + } + else { + // choose p in x-y plane + btScalar a = n.x()*n.x() + n.y()*n.y(); + btScalar k = btRecipSqrt (a); + p.setValue(-n.y()*k,n.x()*k,0); + // set q = n x p + q.setValue(-n.z()*p.y(),n.z()*p.x(),a*k); + } +} + +#endif //SIMD__VECTOR3_H diff --git a/Engine/lib/bullet/src/LinearMath/ibmsdk/Makefile b/Engine/lib/bullet/src/LinearMath/ibmsdk/Makefile new file mode 100644 index 000000000..04148730f --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/ibmsdk/Makefile @@ -0,0 +1,39 @@ +#### Source code Dirs +VPATH = ../ + +ROOT = ../../.. + +#### Library +LIBRARY_ppu = bulletmath.a + +#### Compiler flags +CPPFLAGS = \ +-DUSE_LIBSPE2 \ +-I$(ROOT)/src \ +-I$(SDKINC) + +#### Optimization level flags +#CC_OPT_LEVEL = $(CC_OPT_LEVEL_DEBUG) +CC_OPT_LEVEL = -O3 + +##### Objects to be archived in lib + +OBJS = \ +btAlignedAllocator.o \ +btGeometryUtil.o \ +btQuickprof.o + +#### Install directories +INSTALL_DIR = $(ROOT)/lib/ibmsdk +INSTALL_FILES = $(LIBRARY_ppu) + +IBM_CELLSDK_VERSION := $(shell if [ -d /opt/cell ]; then echo "3.0"; fi) + +ifeq ("$(IBM_CELLSDK_VERSION)","3.0") + CELL_TOP ?= /opt/cell/sdk + include $(CELL_TOP)/buildutils/make.footer +else + CELL_TOP ?= /opt/ibm/cell-sdk/prototype + include $(CELL_TOP)/make.footer +endif + diff --git a/Engine/lib/bullet/src/Makefile.am b/Engine/lib/bullet/src/Makefile.am new file mode 100644 index 000000000..6c246d176 --- /dev/null +++ b/Engine/lib/bullet/src/Makefile.am @@ -0,0 +1,531 @@ +bullet_includedir = $(includedir)/bullet +nobase_bullet_include_HEADERS = \ + btBulletDynamicsCommon.h \ + Bullet-C-Api.h \ + btBulletCollisionCommon.h + +if CONDITIONAL_BUILD_MULTITHREADED +nobase_bullet_include_HEADERS += \ + BulletMultiThreaded/PosixThreadSupport.h \ + BulletMultiThreaded/vectormath/scalar/cpp/mat_aos.h \ + BulletMultiThreaded/vectormath/scalar/cpp/vec_aos.h \ + BulletMultiThreaded/vectormath/scalar/cpp/quat_aos.h \ + BulletMultiThreaded/vectormath/scalar/cpp/vectormath_aos.h \ + BulletMultiThreaded/PpuAddressSpace.h \ + BulletMultiThreaded/SpuCollisionTaskProcess.h \ + BulletMultiThreaded/PlatformDefinitions.h \ + BulletMultiThreaded/vectormath2bullet.h \ + BulletMultiThreaded/SpuGatheringCollisionDispatcher.h \ + BulletMultiThreaded/SpuCollisionObjectWrapper.h \ + BulletMultiThreaded/SpuSampleTaskProcess.h \ + BulletMultiThreaded/SpuNarrowPhaseCollisionTask/boxBoxDistance.h \ + BulletMultiThreaded/SpuNarrowPhaseCollisionTask/Box.h \ + BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuMinkowskiPenetrationDepthSolver.h \ + BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuLocalSupport.h \ + BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuCollisionShapes.h \ + BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuGatheringCollisionTask.h \ + BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuConvexPenetrationDepthSolver.h \ + BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuContactResult.h \ + BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuPreferredPenetrationDirections.h \ + BulletMultiThreaded/SpuSync.h \ + BulletMultiThreaded/btThreadSupportInterface.h \ + BulletMultiThreaded/SpuLibspe2Support.h \ + BulletMultiThreaded/SpuSampleTask/SpuSampleTask.h \ + BulletMultiThreaded/SpuFakeDma.h \ + BulletMultiThreaded/SpuContactManifoldCollisionAlgorithm.h \ + BulletMultiThreaded/SpuDoubleBuffer.h \ + BulletMultiThreaded/Win32ThreadSupport.h \ + BulletMultiThreaded/SequentialThreadSupport.h + +lib_LTLIBRARIES = libbulletmath.la libbulletcollision.la libbulletdynamics.la libbulletsoftbody.la libbulletmultithreaded.la + +libbulletmultithreaded_la_CXXFLAGS = ${CXXFLAGS} -I./BulletMultiThreaded/vectormath/scalar/cpp +libbulletmultithreaded_la_SOURCES =\ + BulletMultiThreaded/SpuCollisionObjectWrapper.cpp \ + BulletMultiThreaded/SpuSampleTask/SpuSampleTask.cpp \ + BulletMultiThreaded/SpuLibspe2Support.cpp \ + BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuContactResult.cpp \ + BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuGatheringCollisionTask.cpp \ + BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuMinkowskiPenetrationDepthSolver.cpp \ + BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuCollisionShapes.cpp \ + BulletMultiThreaded/btThreadSupportInterface.cpp \ + BulletMultiThreaded/SequentialThreadSupport.cpp \ + BulletMultiThreaded/SpuGatheringCollisionDispatcher.cpp \ + BulletMultiThreaded/Win32ThreadSupport.cpp \ + BulletMultiThreaded/SpuFakeDma.cpp \ + BulletMultiThreaded/PosixThreadSupport.cpp \ + BulletMultiThreaded/SpuCollisionTaskProcess.cpp \ + BulletMultiThreaded/SpuContactManifoldCollisionAlgorithm.cpp \ + BulletMultiThreaded/SpuSampleTaskProcess.cpp \ + BulletMultiThreaded/SpuSampleTask/SpuSampleTask.h \ + BulletMultiThreaded/PpuAddressSpace.h \ + BulletMultiThreaded/SpuSampleTaskProcess.h \ + BulletMultiThreaded/SequentialThreadSupport.h \ + BulletMultiThreaded/PlatformDefinitions.h \ + BulletMultiThreaded/Win32ThreadSupport.h \ + BulletMultiThreaded/SpuContactManifoldCollisionAlgorithm.h \ + BulletMultiThreaded/btThreadSupportInterface.h \ + BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuGatheringCollisionTask.h \ + BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuConvexPenetrationDepthSolver.h \ + BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuPreferredPenetrationDirections.h \ + BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuCollisionShapes.h \ + BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuLocalSupport.h \ + BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuMinkowskiPenetrationDepthSolver.h \ + BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuContactResult.h \ + BulletMultiThreaded/SpuGatheringCollisionDispatcher.h \ + BulletMultiThreaded/SpuFakeDma.h \ + BulletMultiThreaded/SpuSync.h \ + BulletMultiThreaded/SpuCollisionObjectWrapper.h \ + BulletMultiThreaded/SpuDoubleBuffer.h \ + BulletMultiThreaded/SpuCollisionTaskProcess.h \ + BulletMultiThreaded/PosixThreadSupport.h \ + BulletMultiThreaded/SpuLibspe2Support.h \ + BulletMultiThreaded/SpuNarrowPhaseCollisionTask/boxBoxDistance.cpp \ + BulletMultiThreaded/SpuNarrowPhaseCollisionTask/boxBoxDistance.h \ + BulletMultiThreaded/SpuNarrowPhaseCollisionTask/Box.h + +else +lib_LTLIBRARIES = libbulletmath.la libbulletcollision.la libbulletdynamics.la libbulletsoftbody.la +endif + + +libbulletmath_la_SOURCES = \ + LinearMath/btQuickprof.cpp \ + LinearMath/btGeometryUtil.cpp \ + LinearMath/btAlignedAllocator.cpp \ + LinearMath/btConvexHull.cpp \ + LinearMath/btHashMap.h \ + LinearMath/btConvexHull.h \ + LinearMath/btAabbUtil2.h \ + LinearMath/btGeometryUtil.h \ + LinearMath/btQuadWord.h \ + LinearMath/btPoolAllocator.h \ + LinearMath/btScalar.h \ + LinearMath/btMinMax.h \ + LinearMath/btVector3.h \ + LinearMath/btList.h \ + LinearMath/btStackAlloc.h \ + LinearMath/btMatrix3x3.h \ + LinearMath/btMotionState.h \ + LinearMath/btAlignedAllocator.h \ + LinearMath/btQuaternion.h \ + LinearMath/btAlignedObjectArray.h \ + LinearMath/btQuickprof.h \ + LinearMath/btTransformUtil.h \ + LinearMath/btTransform.h \ + LinearMath/btDefaultMotionState.h \ + LinearMath/btIDebugDraw.h \ + LinearMath/btRandom.h + + +libbulletcollision_la_SOURCES = \ + BulletCollision/NarrowPhaseCollision/btRaycastCallback.cpp \ + BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.cpp \ + BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.cpp \ + BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.cpp \ + BulletCollision/NarrowPhaseCollision/btGjkConvexCast.cpp \ + BulletCollision/NarrowPhaseCollision/btPersistentManifold.cpp \ + BulletCollision/NarrowPhaseCollision/btConvexCast.cpp \ + BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.cpp \ + BulletCollision/NarrowPhaseCollision/btGjkPairDetector.cpp \ + BulletCollision/NarrowPhaseCollision/btGjkEpa2.cpp \ + BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.cpp \ + BulletCollision/CollisionDispatch/btActivatingCollisionAlgorithm.cpp \ + BulletCollision/CollisionDispatch/btCollisionObject.cpp \ + BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.cpp \ + BulletCollision/CollisionDispatch/btGhostObject.cpp \ + BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.cpp \ + BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.cpp \ + BulletCollision/CollisionDispatch/btCollisionDispatcher.cpp \ + BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.cpp \ + BulletCollision/CollisionDispatch/btSimulationIslandManager.cpp \ + BulletCollision/CollisionDispatch/btBoxBoxDetector.cpp \ + BulletCollision/CollisionDispatch/btConvexPlaneCollisionAlgorithm.cpp \ + BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.cpp \ + BulletCollision/CollisionDispatch/btBoxBoxCollisionAlgorithm.cpp \ + BulletCollision/CollisionDispatch/SphereTriangleDetector.cpp \ + BulletCollision/CollisionDispatch/btManifoldResult.cpp \ + BulletCollision/CollisionDispatch/btCollisionWorld.cpp \ + BulletCollision/CollisionDispatch/btSphereTriangleCollisionAlgorithm.cpp \ + BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.cpp \ + BulletCollision/CollisionDispatch/btUnionFind.cpp \ + BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.cpp \ + BulletCollision/CollisionShapes/btTetrahedronShape.cpp \ + BulletCollision/CollisionShapes/btShapeHull.cpp \ + BulletCollision/CollisionShapes/btMinkowskiSumShape.cpp \ + BulletCollision/CollisionShapes/btCompoundShape.cpp \ + BulletCollision/CollisionShapes/btConeShape.cpp \ + BulletCollision/CollisionShapes/btMultiSphereShape.cpp \ + BulletCollision/CollisionShapes/btUniformScalingShape.cpp \ + BulletCollision/CollisionShapes/btSphereShape.cpp \ + BulletCollision/CollisionShapes/btTriangleIndexVertexArray.cpp \ + BulletCollision/CollisionShapes/btBvhTriangleMeshShape.cpp \ + BulletCollision/CollisionShapes/btTriangleMeshShape.cpp \ + BulletCollision/CollisionShapes/btTriangleBuffer.cpp \ + BulletCollision/CollisionShapes/btStaticPlaneShape.cpp \ + BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp \ + BulletCollision/CollisionShapes/btEmptyShape.cpp \ + BulletCollision/CollisionShapes/btCollisionShape.cpp \ + BulletCollision/CollisionShapes/btConvexShape.cpp \ + BulletCollision/CollisionShapes/btConvexInternalShape.cpp \ + BulletCollision/CollisionShapes/btConvexHullShape.cpp \ + BulletCollision/CollisionShapes/btTriangleCallback.cpp \ + BulletCollision/CollisionShapes/btCapsuleShape.cpp \ + BulletCollision/CollisionShapes/btConvexTriangleMeshShape.cpp \ + BulletCollision/CollisionShapes/btConcaveShape.cpp \ + BulletCollision/CollisionShapes/btConvexPointCloudShape.cpp \ + BulletCollision/CollisionShapes/btBoxShape.cpp \ + BulletCollision/CollisionShapes/btOptimizedBvh.cpp \ + BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp \ + BulletCollision/CollisionShapes/btMultimaterialTriangleMeshShape.cpp \ + BulletCollision/CollisionShapes/btCylinderShape.cpp \ + BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.cpp \ + BulletCollision/CollisionShapes/btStridingMeshInterface.cpp \ + BulletCollision/CollisionShapes/btTriangleIndexVertexMaterialArray.cpp \ + BulletCollision/CollisionShapes/btTriangleMesh.cpp \ + BulletCollision/BroadphaseCollision/btAxisSweep3.cpp \ + BulletCollision/BroadphaseCollision/btOverlappingPairCache.cpp \ + BulletCollision/BroadphaseCollision/btDbvtBroadphase.cpp \ + BulletCollision/BroadphaseCollision/btMultiSapBroadphase.cpp \ + BulletCollision/BroadphaseCollision/btDispatcher.cpp \ + BulletCollision/BroadphaseCollision/btBroadphaseProxy.cpp \ + BulletCollision/BroadphaseCollision/btQuantizedBvh.cpp \ + BulletCollision/BroadphaseCollision/btCollisionAlgorithm.cpp \ + BulletCollision/BroadphaseCollision/btDbvt.cpp \ + BulletCollision/BroadphaseCollision/btSimpleBroadphase.cpp \ + BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h \ + BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.h \ + BulletCollision/NarrowPhaseCollision/btConvexCast.h \ + BulletCollision/NarrowPhaseCollision/btGjkEpa2.h \ + BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h \ + BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.h \ + BulletCollision/NarrowPhaseCollision/btPointCollector.h \ + BulletCollision/NarrowPhaseCollision/btConvexPenetrationDepthSolver.h \ + BulletCollision/NarrowPhaseCollision/btSimplexSolverInterface.h \ + BulletCollision/NarrowPhaseCollision/btRaycastCallback.h \ + BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.h \ + BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.h \ + BulletCollision/NarrowPhaseCollision/btPersistentManifold.h \ + BulletCollision/NarrowPhaseCollision/btGjkConvexCast.h \ + BulletCollision/NarrowPhaseCollision/btManifoldPoint.h \ + BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h \ + BulletCollision/CollisionDispatch/btCollisionObject.h \ + BulletCollision/CollisionDispatch/btGhostObject.h \ + BulletCollision/CollisionDispatch/btSphereTriangleCollisionAlgorithm.h \ + BulletCollision/CollisionDispatch/btBoxBoxCollisionAlgorithm.h \ + BulletCollision/CollisionDispatch/btConvexPlaneCollisionAlgorithm.h \ + BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.h \ + BulletCollision/CollisionDispatch/btCollisionCreateFunc.h \ + BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.h \ + BulletCollision/CollisionDispatch/btBoxBoxDetector.h \ + BulletCollision/CollisionDispatch/btCollisionDispatcher.h \ + BulletCollision/CollisionDispatch/SphereTriangleDetector.h \ + BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.h \ + BulletCollision/CollisionDispatch/btUnionFind.h \ + BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.h \ + BulletCollision/CollisionDispatch/btSimulationIslandManager.h \ + BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h \ + BulletCollision/CollisionDispatch/btCollisionWorld.h \ + BulletCollision/CollisionDispatch/btManifoldResult.h \ + BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.h \ + BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.h \ + BulletCollision/CollisionDispatch/btCollisionConfiguration.h \ + BulletCollision/CollisionShapes/btConvexShape.h \ + BulletCollision/CollisionShapes/btTriangleCallback.h \ + BulletCollision/CollisionShapes/btPolyhedralConvexShape.h \ + BulletCollision/CollisionShapes/btMultimaterialTriangleMeshShape.h \ + BulletCollision/CollisionShapes/btCompoundShape.h \ + BulletCollision/CollisionShapes/btBoxShape.h \ + BulletCollision/CollisionShapes/btMultiSphereShape.h \ + BulletCollision/CollisionShapes/btCollisionMargin.h \ + BulletCollision/CollisionShapes/btConcaveShape.h \ + BulletCollision/CollisionShapes/btConvexTriangleMeshShape.h \ + BulletCollision/CollisionShapes/btEmptyShape.h \ + BulletCollision/CollisionShapes/btUniformScalingShape.h \ + BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.h \ + BulletCollision/CollisionShapes/btMaterial.h \ + BulletCollision/CollisionShapes/btTriangleIndexVertexArray.h \ + BulletCollision/CollisionShapes/btSphereShape.h \ + BulletCollision/CollisionShapes/btConvexPointCloudShape.h \ + BulletCollision/CollisionShapes/btCapsuleShape.h \ + BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h \ + BulletCollision/CollisionShapes/btCollisionShape.h \ + BulletCollision/CollisionShapes/btStaticPlaneShape.h \ + BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h \ + BulletCollision/CollisionShapes/btTriangleMeshShape.h \ + BulletCollision/CollisionShapes/btStridingMeshInterface.h \ + BulletCollision/CollisionShapes/btTriangleMesh.h \ + BulletCollision/CollisionShapes/btTriangleBuffer.h \ + BulletCollision/CollisionShapes/btShapeHull.h \ + BulletCollision/CollisionShapes/btMinkowskiSumShape.h \ + BulletCollision/CollisionShapes/btOptimizedBvh.h \ + BulletCollision/CollisionShapes/btTriangleShape.h \ + BulletCollision/CollisionShapes/btTriangleIndexVertexMaterialArray.h \ + BulletCollision/CollisionShapes/btCylinderShape.h \ + BulletCollision/CollisionShapes/btTetrahedronShape.h \ + BulletCollision/CollisionShapes/btConvexInternalShape.h \ + BulletCollision/CollisionShapes/btConeShape.h \ + BulletCollision/CollisionShapes/btConvexHullShape.h \ + BulletCollision/BroadphaseCollision/btAxisSweep3.h \ + BulletCollision/BroadphaseCollision/btDbvtBroadphase.h \ + BulletCollision/BroadphaseCollision/btSimpleBroadphase.h \ + BulletCollision/BroadphaseCollision/btMultiSapBroadphase.h \ + BulletCollision/BroadphaseCollision/btDbvt.h \ + BulletCollision/BroadphaseCollision/btOverlappingPairCallback.h \ + BulletCollision/BroadphaseCollision/btDispatcher.h \ + BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h \ + BulletCollision/BroadphaseCollision/btBroadphaseProxy.h \ + BulletCollision/BroadphaseCollision/btOverlappingPairCache.h \ + BulletCollision/BroadphaseCollision/btBroadphaseInterface.h \ + BulletCollision/BroadphaseCollision/btQuantizedBvh.h \ + BulletCollision/Gimpact/btGImpactBvh.cpp\ + BulletCollision/Gimpact/btGImpactQuantizedBvh.cpp\ + BulletCollision/Gimpact/btTriangleShapeEx.cpp\ + BulletCollision/Gimpact/btGImpactCollisionAlgorithm.cpp\ + BulletCollision/Gimpact/btGImpactShape.cpp\ + BulletCollision/Gimpact/gim_box_set.cpp\ + BulletCollision/Gimpact/gim_contact.cpp\ + BulletCollision/Gimpact/gim_memory.cpp\ + BulletCollision/Gimpact/gim_tri_collision.cpp + +libbulletdynamics_la_SOURCES = \ + BulletDynamics/Dynamics/btContinuousDynamicsWorld.cpp \ + BulletDynamics/Dynamics/btRigidBody.cpp \ + BulletDynamics/Dynamics/btSimpleDynamicsWorld.cpp \ + BulletDynamics/Dynamics/Bullet-C-API.cpp \ + BulletDynamics/Dynamics/btDiscreteDynamicsWorld.cpp \ + BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.cpp \ + BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.cpp \ + BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.cpp \ + BulletDynamics/ConstraintSolver/btPoint2PointConstraint.cpp \ + BulletDynamics/ConstraintSolver/btTypedConstraint.cpp \ + BulletDynamics/ConstraintSolver/btContactConstraint.cpp \ + BulletDynamics/ConstraintSolver/btSliderConstraint.cpp \ + BulletDynamics/ConstraintSolver/btConeTwistConstraint.cpp \ + BulletDynamics/ConstraintSolver/btHingeConstraint.cpp \ + BulletDynamics/ConstraintSolver/btHinge2Constraint.cpp \ + BulletDynamics/ConstraintSolver/btUniversalConstraint.cpp \ + BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp \ + BulletDynamics/Vehicle/btWheelInfo.cpp \ + BulletDynamics/Vehicle/btRaycastVehicle.cpp \ + BulletDynamics/Character/btKinematicCharacterController.cpp \ + BulletDynamics/Character/btKinematicCharacterController.h \ + BulletDynamics/Character/btCharacterControllerInterface.h \ + BulletDynamics/Dynamics/btContinuousDynamicsWorld.h \ + BulletDynamics/Dynamics/btSimpleDynamicsWorld.h \ + BulletDynamics/Dynamics/btRigidBody.h \ + BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h \ + BulletDynamics/Dynamics/btDynamicsWorld.h \ + BulletDynamics/ConstraintSolver/btSolverBody.h \ + BulletDynamics/ConstraintSolver/btConstraintSolver.h \ + BulletDynamics/ConstraintSolver/btConeTwistConstraint.h \ + BulletDynamics/ConstraintSolver/btTypedConstraint.h \ + BulletDynamics/ConstraintSolver/btContactSolverInfo.h \ + BulletDynamics/ConstraintSolver/btContactConstraint.h \ + BulletDynamics/ConstraintSolver/btPoint2PointConstraint.h \ + BulletDynamics/ConstraintSolver/btJacobianEntry.h \ + BulletDynamics/ConstraintSolver/btSolverConstraint.h \ + BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h \ + BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.h \ + BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.h \ + BulletDynamics/ConstraintSolver/btSliderConstraint.h \ + BulletDynamics/ConstraintSolver/btHingeConstraint.h \ + BulletDynamics/ConstraintSolver/btHinge2Constraint.h \ + BulletDynamics/ConstraintSolver/btUniversalConstraint.h \ + BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.h \ + BulletDynamics/Vehicle/btVehicleRaycaster.h \ + BulletDynamics/Vehicle/btRaycastVehicle.h \ + BulletDynamics/Vehicle/btWheelInfo.h + +libbulletsoftbody_la_SOURCES = \ + BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.cpp \ + BulletSoftBody/btSoftBody.cpp \ + BulletSoftBody/btSoftRigidCollisionAlgorithm.cpp \ + BulletSoftBody/btSoftBodyConcaveCollisionAlgorithm.cpp \ + BulletSoftBody/btSoftRigidDynamicsWorld.cpp \ + BulletSoftBody/btSoftBodyHelpers.cpp \ + BulletSoftBody/btSoftSoftCollisionAlgorithm.cpp \ + BulletSoftBody/btSparseSDF.h \ + BulletSoftBody/btSoftRigidCollisionAlgorithm.h \ + BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.h \ + BulletSoftBody/btSoftBody.h \ + BulletSoftBody/btSoftSoftCollisionAlgorithm.h \ + BulletSoftBody/btSoftBodyInternals.h \ + BulletSoftBody/btSoftBodyConcaveCollisionAlgorithm.h \ + BulletSoftBody/btSoftRigidDynamicsWorld.h \ + BulletSoftBody/btSoftBodyHelpers.h + + + +nobase_bullet_include_HEADERS += \ + BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.h \ + BulletSoftBody/btSoftBodyInternals.h \ + BulletSoftBody/btSoftBodyConcaveCollisionAlgorithm.h \ + BulletSoftBody/btSoftSoftCollisionAlgorithm.h \ + BulletSoftBody/btSoftBody.h \ + BulletSoftBody/btSoftBodyHelpers.h \ + BulletSoftBody/btSparseSDF.h \ + BulletSoftBody/btSoftRigidCollisionAlgorithm.h \ + BulletSoftBody/btSoftRigidDynamicsWorld.h \ + BulletDynamics/Vehicle/btRaycastVehicle.h \ + BulletDynamics/Vehicle/btWheelInfo.h \ + BulletDynamics/Vehicle/btVehicleRaycaster.h \ + BulletDynamics/Dynamics/btContinuousDynamicsWorld.h \ + BulletDynamics/Dynamics/btRigidBody.h \ + BulletDynamics/Dynamics/btDynamicsWorld.h \ + BulletDynamics/Dynamics/btSimpleDynamicsWorld.h \ + BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h \ + BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h \ + BulletDynamics/ConstraintSolver/btSolverConstraint.h \ + BulletDynamics/ConstraintSolver/btPoint2PointConstraint.h \ + BulletDynamics/ConstraintSolver/btTypedConstraint.h \ + BulletDynamics/ConstraintSolver/btSliderConstraint.h \ + BulletDynamics/ConstraintSolver/btConstraintSolver.h \ + BulletDynamics/ConstraintSolver/btContactConstraint.h \ + BulletDynamics/ConstraintSolver/btContactSolverInfo.h \ + BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.h \ + BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.h \ + BulletDynamics/ConstraintSolver/btJacobianEntry.h \ + BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.h \ + BulletDynamics/ConstraintSolver/btConeTwistConstraint.h \ + BulletDynamics/ConstraintSolver/btHingeConstraint.h \ + BulletDynamics/ConstraintSolver/btHinge2Constraint.h \ + BulletDynamics/ConstraintSolver/btUniversalConstraint.h \ + BulletDynamics/ConstraintSolver/btSolverBody.h \ + BulletDynamics/Character/btCharacterControllerInterface.h \ + BulletDynamics/Character/btKinematicCharacterController.h \ + BulletCollision/CollisionShapes/btShapeHull.h \ + BulletCollision/CollisionShapes/btConcaveShape.h \ + BulletCollision/CollisionShapes/btCollisionMargin.h \ + BulletCollision/CollisionShapes/btCompoundShape.h \ + BulletCollision/CollisionShapes/btConvexHullShape.h \ + BulletCollision/CollisionShapes/btCylinderShape.h \ + BulletCollision/CollisionShapes/btTriangleMesh.h \ + BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h \ + BulletCollision/CollisionShapes/btUniformScalingShape.h \ + BulletCollision/CollisionShapes/btConvexPointCloudShape.h \ + BulletCollision/CollisionShapes/btTetrahedronShape.h \ + BulletCollision/CollisionShapes/btCapsuleShape.h \ + BulletCollision/CollisionShapes/btSphereShape.h \ + BulletCollision/CollisionShapes/btMultiSphereShape.h \ + BulletCollision/CollisionShapes/btConvexInternalShape.h \ + BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.h \ + BulletCollision/CollisionShapes/btStridingMeshInterface.h \ + BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h \ + BulletCollision/CollisionShapes/btEmptyShape.h \ + BulletCollision/CollisionShapes/btOptimizedBvh.h \ + BulletCollision/CollisionShapes/btConvexTriangleMeshShape.h \ + BulletCollision/CollisionShapes/btTriangleCallback.h \ + BulletCollision/CollisionShapes/btTriangleIndexVertexMaterialArray.h \ + BulletCollision/CollisionShapes/btTriangleIndexVertexArray.h \ + BulletCollision/CollisionShapes/btTriangleBuffer.h \ + BulletCollision/CollisionShapes/btConvexShape.h \ + BulletCollision/CollisionShapes/btStaticPlaneShape.h \ + BulletCollision/CollisionShapes/btConeShape.h \ + BulletCollision/CollisionShapes/btCollisionShape.h \ + BulletCollision/CollisionShapes/btTriangleShape.h \ + BulletCollision/CollisionShapes/btBoxShape.h \ + BulletCollision/CollisionShapes/btMinkowskiSumShape.h \ + BulletCollision/CollisionShapes/btTriangleMeshShape.h \ + BulletCollision/CollisionShapes/btMaterial.h \ + BulletCollision/CollisionShapes/btMultimaterialTriangleMeshShape.h \ + BulletCollision/CollisionShapes/btPolyhedralConvexShape.h \ + BulletCollision/NarrowPhaseCollision/btConvexCast.h \ + BulletCollision/NarrowPhaseCollision/btGjkEpa2.h \ + BulletCollision/NarrowPhaseCollision/btSimplexSolverInterface.h \ + BulletCollision/NarrowPhaseCollision/btContinuousConvexCollision.h \ + BulletCollision/NarrowPhaseCollision/btConvexPenetrationDepthSolver.h \ + BulletCollision/NarrowPhaseCollision/btGjkConvexCast.h \ + BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h \ + BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.h \ + BulletCollision/NarrowPhaseCollision/btPersistentManifold.h \ + BulletCollision/NarrowPhaseCollision/btManifoldPoint.h \ + BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h \ + BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h \ + BulletCollision/NarrowPhaseCollision/btRaycastCallback.h \ + BulletCollision/NarrowPhaseCollision/btSubSimplexConvexCast.h \ + BulletCollision/NarrowPhaseCollision/btPointCollector.h \ + BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.h \ + BulletCollision/BroadphaseCollision/btDbvt.h \ + BulletCollision/BroadphaseCollision/btDispatcher.h \ + BulletCollision/BroadphaseCollision/btDbvtBroadphase.h \ + BulletCollision/BroadphaseCollision/btSimpleBroadphase.h \ + BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h \ + BulletCollision/BroadphaseCollision/btOverlappingPairCallback.h \ + BulletCollision/BroadphaseCollision/btMultiSapBroadphase.h \ + BulletCollision/BroadphaseCollision/btQuantizedBvh.h \ + BulletCollision/BroadphaseCollision/btAxisSweep3.h \ + BulletCollision/BroadphaseCollision/btBroadphaseInterface.h \ + BulletCollision/BroadphaseCollision/btOverlappingPairCache.h \ + BulletCollision/BroadphaseCollision/btBroadphaseProxy.h \ + BulletCollision/CollisionDispatch/btUnionFind.h \ + BulletCollision/CollisionDispatch/btCollisionConfiguration.h \ + BulletCollision/CollisionDispatch/btCollisionDispatcher.h \ + BulletCollision/CollisionDispatch/SphereTriangleDetector.h \ + BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.h \ + BulletCollision/CollisionDispatch/btCollisionWorld.h \ + BulletCollision/CollisionDispatch/btCollisionCreateFunc.h \ + BulletCollision/CollisionDispatch/btSphereTriangleCollisionAlgorithm.h \ + BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.h \ + BulletCollision/CollisionDispatch/btCollisionObject.h \ + BulletCollision/CollisionDispatch/btConvexPlaneCollisionAlgorithm.h \ + BulletCollision/CollisionDispatch/btBoxBoxCollisionAlgorithm.h \ + BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h \ + BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.h \ + BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.h \ + BulletCollision/CollisionDispatch/btGhostObject.h \ + BulletCollision/CollisionDispatch/btSimulationIslandManager.h \ + BulletCollision/CollisionDispatch/btActivatingCollisionAlgorithm.h \ + BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.h \ + BulletCollision/CollisionDispatch/btBoxBoxDetector.h \ + BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.h \ + BulletCollision/CollisionDispatch/btManifoldResult.h \ + BulletCollision/Gimpact/gim_memory.h \ + BulletCollision/Gimpact/gim_clip_polygon.h \ + BulletCollision/Gimpact/gim_bitset.h \ + BulletCollision/Gimpact/gim_linear_math.h \ + BulletCollision/Gimpact/btGeometryOperations.h \ + BulletCollision/Gimpact/btGImpactCollisionAlgorithm.h \ + BulletCollision/Gimpact/btGImpactBvh.h \ + BulletCollision/Gimpact/gim_box_set.h \ + BulletCollision/Gimpact/gim_array.h \ + BulletCollision/Gimpact/btGImpactShape.h \ + BulletCollision/Gimpact/btTriangleShapeEx.h \ + BulletCollision/Gimpact/btClipPolygon.h \ + BulletCollision/Gimpact/gim_box_collision.h \ + BulletCollision/Gimpact/gim_tri_collision.h \ + BulletCollision/Gimpact/gim_geometry.h \ + BulletCollision/Gimpact/gim_math.h \ + BulletCollision/Gimpact/btQuantization.h \ + BulletCollision/Gimpact/btGImpactQuantizedBvh.h \ + BulletCollision/Gimpact/gim_geom_types.h \ + BulletCollision/Gimpact/gim_basic_geometry_operations.h \ + BulletCollision/Gimpact/gim_contact.h \ + BulletCollision/Gimpact/gim_hash_table.h \ + BulletCollision/Gimpact/gim_radixsort.h \ + BulletCollision/Gimpact/btGImpactMassUtil.h \ + BulletCollision/Gimpact/btGenericPoolAllocator.h \ + BulletCollision/Gimpact/btBoxCollision.h \ + BulletCollision/Gimpact/btContactProcessing.h \ + LinearMath/btGeometryUtil.h \ + LinearMath/btConvexHull.h \ + LinearMath/btList.h \ + LinearMath/btMatrix3x3.h \ + LinearMath/btVector3.h \ + LinearMath/btPoolAllocator.h \ + LinearMath/btScalar.h \ + LinearMath/btDefaultMotionState.h \ + LinearMath/btTransform.h \ + LinearMath/btQuadWord.h \ + LinearMath/btAabbUtil2.h \ + LinearMath/btTransformUtil.h \ + LinearMath/btRandom.h \ + LinearMath/btQuaternion.h \ + LinearMath/btMinMax.h \ + LinearMath/btMotionState.h \ + LinearMath/btIDebugDraw.h \ + LinearMath/btAlignedAllocator.h \ + LinearMath/btStackAlloc.h \ + LinearMath/btAlignedObjectArray.h \ + LinearMath/btHashMap.h \ + LinearMath/btQuickprof.h diff --git a/Engine/lib/bullet/src/MiniCL/cl.h b/Engine/lib/bullet/src/MiniCL/cl.h new file mode 100644 index 000000000..b0cda4237 --- /dev/null +++ b/Engine/lib/bullet/src/MiniCL/cl.h @@ -0,0 +1,865 @@ +/******************************************************************************* + * Copyright (c) 2008-2009 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and/or associated documentation files (the + * "Materials"), to deal in the Materials without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Materials, and to + * permit persons to whom the Materials are furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + ******************************************************************************/ + +#ifndef __OPENCL_CL_H +#define __OPENCL_CL_H + +#ifdef __APPLE__ +#include +#else +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************************/ + +typedef struct _cl_platform_id * cl_platform_id; +typedef struct _cl_device_id * cl_device_id; +typedef struct _cl_context * cl_context; +typedef struct _cl_command_queue * cl_command_queue; +typedef struct _cl_mem * cl_mem; +typedef struct _cl_program * cl_program; +typedef struct _cl_kernel * cl_kernel; +typedef struct _cl_event * cl_event; +typedef struct _cl_sampler * cl_sampler; + +typedef cl_uint cl_bool; /* WARNING! Unlike cl_ types in cl_platform.h, cl_bool is not guaranteed to be the same size as the bool in kernels. */ +typedef cl_ulong cl_bitfield; +typedef cl_bitfield cl_device_type; +typedef cl_uint cl_platform_info; +typedef cl_uint cl_device_info; +typedef cl_bitfield cl_device_address_info; +typedef cl_bitfield cl_device_fp_config; +typedef cl_uint cl_device_mem_cache_type; +typedef cl_uint cl_device_local_mem_type; +typedef cl_bitfield cl_device_exec_capabilities; +typedef cl_bitfield cl_command_queue_properties; + +typedef intptr_t cl_context_properties; +typedef cl_uint cl_context_info; +typedef cl_uint cl_command_queue_info; +typedef cl_uint cl_channel_order; +typedef cl_uint cl_channel_type; +typedef cl_bitfield cl_mem_flags; +typedef cl_uint cl_mem_object_type; +typedef cl_uint cl_mem_info; +typedef cl_uint cl_image_info; +typedef cl_uint cl_addressing_mode; +typedef cl_uint cl_filter_mode; +typedef cl_uint cl_sampler_info; +typedef cl_bitfield cl_map_flags; +typedef cl_uint cl_program_info; +typedef cl_uint cl_program_build_info; +typedef cl_int cl_build_status; +typedef cl_uint cl_kernel_info; +typedef cl_uint cl_kernel_work_group_info; +typedef cl_uint cl_event_info; +typedef cl_uint cl_command_type; +typedef cl_uint cl_profiling_info; + +typedef struct _cl_image_format { + cl_channel_order image_channel_order; + cl_channel_type image_channel_data_type; +} cl_image_format; + +/******************************************************************************/ + +// Error Codes +#define CL_SUCCESS 0 +#define CL_DEVICE_NOT_FOUND -1 +#define CL_DEVICE_NOT_AVAILABLE -2 +#define CL_DEVICE_COMPILER_NOT_AVAILABLE -3 +#define CL_MEM_OBJECT_ALLOCATION_FAILURE -4 +#define CL_OUT_OF_RESOURCES -5 +#define CL_OUT_OF_HOST_MEMORY -6 +#define CL_PROFILING_INFO_NOT_AVAILABLE -7 +#define CL_MEM_COPY_OVERLAP -8 +#define CL_IMAGE_FORMAT_MISMATCH -9 +#define CL_IMAGE_FORMAT_NOT_SUPPORTED -10 +#define CL_BUILD_PROGRAM_FAILURE -11 +#define CL_MAP_FAILURE -12 + +#define CL_INVALID_VALUE -30 +#define CL_INVALID_DEVICE_TYPE -31 +#define CL_INVALID_PLATFORM -32 +#define CL_INVALID_DEVICE -33 +#define CL_INVALID_CONTEXT -34 +#define CL_INVALID_QUEUE_PROPERTIES -35 +#define CL_INVALID_COMMAND_QUEUE -36 +#define CL_INVALID_HOST_PTR -37 +#define CL_INVALID_MEM_OBJECT -38 +#define CL_INVALID_IMAGE_FORMAT_DESCRIPTOR -39 +#define CL_INVALID_IMAGE_SIZE -40 +#define CL_INVALID_SAMPLER -41 +#define CL_INVALID_BINARY -42 +#define CL_INVALID_BUILD_OPTIONS -43 +#define CL_INVALID_PROGRAM -44 +#define CL_INVALID_PROGRAM_EXECUTABLE -45 +#define CL_INVALID_KERNEL_NAME -46 +#define CL_INVALID_KERNEL_DEFINITION -47 +#define CL_INVALID_KERNEL -48 +#define CL_INVALID_ARG_INDEX -49 +#define CL_INVALID_ARG_VALUE -50 +#define CL_INVALID_ARG_SIZE -51 +#define CL_INVALID_KERNEL_ARGS -52 +#define CL_INVALID_WORK_DIMENSION -53 +#define CL_INVALID_WORK_GROUP_SIZE -54 +#define CL_INVALID_WORK_ITEM_SIZE -55 +#define CL_INVALID_GLOBAL_OFFSET -56 +#define CL_INVALID_EVENT_WAIT_LIST -57 +#define CL_INVALID_EVENT -58 +#define CL_INVALID_OPERATION -59 +#define CL_INVALID_GL_OBJECT -60 +#define CL_INVALID_BUFFER_SIZE -61 +#define CL_INVALID_MIP_LEVEL -62 + +// OpenCL Version +#define CL_VERSION_1_0 1 + +// cl_bool +#define CL_FALSE 0 +#define CL_TRUE 1 + +// cl_platform_info +#define CL_PLATFORM_PROFILE 0x0900 +#define CL_PLATFORM_VERSION 0x0901 +#define CL_PLATFORM_NAME 0x0902 +#define CL_PLATFORM_VENDOR 0x0903 +#define CL_PLATFORM_EXTENSIONS 0x0904 + +// cl_device_type - bitfield +#define CL_DEVICE_TYPE_DEFAULT (1 << 0) +#define CL_DEVICE_TYPE_CPU (1 << 1) +#define CL_DEVICE_TYPE_GPU (1 << 2) +#define CL_DEVICE_TYPE_ACCELERATOR (1 << 3) +#define CL_DEVICE_TYPE_ALL 0xFFFFFFFF + +// cl_device_info +#define CL_DEVICE_TYPE 0x1000 +#define CL_DEVICE_VENDOR_ID 0x1001 +#define CL_DEVICE_MAX_COMPUTE_UNITS 0x1002 +#define CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS 0x1003 +#define CL_DEVICE_MAX_WORK_GROUP_SIZE 0x1004 +#define CL_DEVICE_MAX_WORK_ITEM_SIZES 0x1005 +#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR 0x1006 +#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT 0x1007 +#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT 0x1008 +#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG 0x1009 +#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT 0x100A +#define CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE 0x100B +#define CL_DEVICE_MAX_CLOCK_FREQUENCY 0x100C +#define CL_DEVICE_ADDRESS_BITS 0x100D +#define CL_DEVICE_MAX_READ_IMAGE_ARGS 0x100E +#define CL_DEVICE_MAX_WRITE_IMAGE_ARGS 0x100F +#define CL_DEVICE_MAX_MEM_ALLOC_SIZE 0x1010 +#define CL_DEVICE_IMAGE2D_MAX_WIDTH 0x1011 +#define CL_DEVICE_IMAGE2D_MAX_HEIGHT 0x1012 +#define CL_DEVICE_IMAGE3D_MAX_WIDTH 0x1013 +#define CL_DEVICE_IMAGE3D_MAX_HEIGHT 0x1014 +#define CL_DEVICE_IMAGE3D_MAX_DEPTH 0x1015 +#define CL_DEVICE_IMAGE_SUPPORT 0x1016 +#define CL_DEVICE_MAX_PARAMETER_SIZE 0x1017 +#define CL_DEVICE_MAX_SAMPLERS 0x1018 +#define CL_DEVICE_MEM_BASE_ADDR_ALIGN 0x1019 +#define CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE 0x101A +#define CL_DEVICE_SINGLE_FP_CONFIG 0x101B +#define CL_DEVICE_GLOBAL_MEM_CACHE_TYPE 0x101C +#define CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE 0x101D +#define CL_DEVICE_GLOBAL_MEM_CACHE_SIZE 0x101E +#define CL_DEVICE_GLOBAL_MEM_SIZE 0x101F +#define CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE 0x1020 +#define CL_DEVICE_MAX_CONSTANT_ARGS 0x1021 +#define CL_DEVICE_LOCAL_MEM_TYPE 0x1022 +#define CL_DEVICE_LOCAL_MEM_SIZE 0x1023 +#define CL_DEVICE_ERROR_CORRECTION_SUPPORT 0x1024 +#define CL_DEVICE_PROFILING_TIMER_RESOLUTION 0x1025 +#define CL_DEVICE_ENDIAN_LITTLE 0x1026 +#define CL_DEVICE_AVAILABLE 0x1027 +#define CL_DEVICE_COMPILER_AVAILABLE 0x1028 +#define CL_DEVICE_EXECUTION_CAPABILITIES 0x1029 +#define CL_DEVICE_QUEUE_PROPERTIES 0x102A +#define CL_DEVICE_NAME 0x102B +#define CL_DEVICE_VENDOR 0x102C +#define CL_DRIVER_VERSION 0x102D +#define CL_DEVICE_PROFILE 0x102E +#define CL_DEVICE_VERSION 0x102F +#define CL_DEVICE_EXTENSIONS 0x1030 +#define CL_DEVICE_PLATFORM 0x1031 + +// cl_device_address_info - bitfield +#define CL_DEVICE_ADDRESS_32_BITS (1 << 0) +#define CL_DEVICE_ADDRESS_64_BITS (1 << 1) + +// cl_device_fp_config - bitfield +#define CL_FP_DENORM (1 << 0) +#define CL_FP_INF_NAN (1 << 1) +#define CL_FP_ROUND_TO_NEAREST (1 << 2) +#define CL_FP_ROUND_TO_ZERO (1 << 3) +#define CL_FP_ROUND_TO_INF (1 << 4) +#define CL_FP_FMA (1 << 5) + +// cl_device_mem_cache_type +#define CL_NONE 0x0 +#define CL_READ_ONLY_CACHE 0x1 +#define CL_READ_WRITE_CACHE 0x2 + +// cl_device_local_mem_type +#define CL_LOCAL 0x1 +#define CL_GLOBAL 0x2 + +// cl_device_exec_capabilities - bitfield +#define CL_EXEC_KERNEL (1 << 0) +#define CL_EXEC_NATIVE_KERNEL (1 << 1) + +// cl_command_queue_properties - bitfield +#define CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE (1 << 0) +#define CL_QUEUE_PROFILING_ENABLE (1 << 1) + +// cl_context_info +#define CL_CONTEXT_REFERENCE_COUNT 0x1080 +#define CL_CONTEXT_NUM_DEVICES 0x1081 +#define CL_CONTEXT_DEVICES 0x1082 +#define CL_CONTEXT_PROPERTIES 0x1083 +#define CL_CONTEXT_PLATFORM 0x1084 + +// cl_command_queue_info +#define CL_QUEUE_CONTEXT 0x1090 +#define CL_QUEUE_DEVICE 0x1091 +#define CL_QUEUE_REFERENCE_COUNT 0x1092 +#define CL_QUEUE_PROPERTIES 0x1093 + +// cl_mem_flags - bitfield +#define CL_MEM_READ_WRITE (1 << 0) +#define CL_MEM_WRITE_ONLY (1 << 1) +#define CL_MEM_READ_ONLY (1 << 2) +#define CL_MEM_USE_HOST_PTR (1 << 3) +#define CL_MEM_ALLOC_HOST_PTR (1 << 4) +#define CL_MEM_COPY_HOST_PTR (1 << 5) + +// cl_channel_order +#define CL_R 0x10B0 +#define CL_A 0x10B1 +#define CL_RG 0x10B2 +#define CL_RA 0x10B3 +#define CL_RGB 0x10B4 +#define CL_RGBA 0x10B5 +#define CL_BGRA 0x10B6 +#define CL_ARGB 0x10B7 +#define CL_INTENSITY 0x10B8 +#define CL_LUMINANCE 0x10B9 + +// cl_channel_type +#define CL_SNORM_INT8 0x10D0 +#define CL_SNORM_INT16 0x10D1 +#define CL_UNORM_INT8 0x10D2 +#define CL_UNORM_INT16 0x10D3 +#define CL_UNORM_SHORT_565 0x10D4 +#define CL_UNORM_SHORT_555 0x10D5 +#define CL_UNORM_INT_101010 0x10D6 +#define CL_SIGNED_INT8 0x10D7 +#define CL_SIGNED_INT16 0x10D8 +#define CL_SIGNED_INT32 0x10D9 +#define CL_UNSIGNED_INT8 0x10DA +#define CL_UNSIGNED_INT16 0x10DB +#define CL_UNSIGNED_INT32 0x10DC +#define CL_HALF_FLOAT 0x10DD +#define CL_FLOAT 0x10DE + +// cl_mem_object_type +#define CL_MEM_OBJECT_BUFFER 0x10F0 +#define CL_MEM_OBJECT_IMAGE2D 0x10F1 +#define CL_MEM_OBJECT_IMAGE3D 0x10F2 + +// cl_mem_info +#define CL_MEM_TYPE 0x1100 +#define CL_MEM_FLAGS 0x1101 +#define CL_MEM_SIZE 0x1102 +#define CL_MEM_HOST_PTR 0x1103 +#define CL_MEM_MAP_COUNT 0x1104 +#define CL_MEM_REFERENCE_COUNT 0x1105 +#define CL_MEM_CONTEXT 0x1106 + +// cl_image_info +#define CL_IMAGE_FORMAT 0x1110 +#define CL_IMAGE_ELEMENT_SIZE 0x1111 +#define CL_IMAGE_ROW_PITCH 0x1112 +#define CL_IMAGE_SLICE_PITCH 0x1113 +#define CL_IMAGE_WIDTH 0x1114 +#define CL_IMAGE_HEIGHT 0x1115 +#define CL_IMAGE_DEPTH 0x1116 + +// cl_addressing_mode +#define CL_ADDRESS_NONE 0x1130 +#define CL_ADDRESS_CLAMP_TO_EDGE 0x1131 +#define CL_ADDRESS_CLAMP 0x1132 +#define CL_ADDRESS_REPEAT 0x1133 + +// cl_filter_mode +#define CL_FILTER_NEAREST 0x1140 +#define CL_FILTER_LINEAR 0x1141 + +// cl_sampler_info +#define CL_SAMPLER_REFERENCE_COUNT 0x1150 +#define CL_SAMPLER_CONTEXT 0x1151 +#define CL_SAMPLER_NORMALIZED_COORDS 0x1152 +#define CL_SAMPLER_ADDRESSING_MODE 0x1153 +#define CL_SAMPLER_FILTER_MODE 0x1154 + +// cl_map_flags - bitfield +#define CL_MAP_READ (1 << 0) +#define CL_MAP_WRITE (1 << 1) + +// cl_program_info +#define CL_PROGRAM_REFERENCE_COUNT 0x1160 +#define CL_PROGRAM_CONTEXT 0x1161 +#define CL_PROGRAM_NUM_DEVICES 0x1162 +#define CL_PROGRAM_DEVICES 0x1163 +#define CL_PROGRAM_SOURCE 0x1164 +#define CL_PROGRAM_BINARY_SIZES 0x1165 +#define CL_PROGRAM_BINARIES 0x1166 + +// cl_program_build_info +#define CL_PROGRAM_BUILD_STATUS 0x1181 +#define CL_PROGRAM_BUILD_OPTIONS 0x1182 +#define CL_PROGRAM_BUILD_LOG 0x1183 + +// cl_build_status +#define CL_BUILD_SUCCESS 0 +#define CL_BUILD_NONE -1 +#define CL_BUILD_ERROR -2 +#define CL_BUILD_IN_PROGRESS -3 + +// cl_kernel_info +#define CL_KERNEL_FUNCTION_NAME 0x1190 +#define CL_KERNEL_NUM_ARGS 0x1191 +#define CL_KERNEL_REFERENCE_COUNT 0x1192 +#define CL_KERNEL_CONTEXT 0x1193 +#define CL_KERNEL_PROGRAM 0x1194 + +// cl_kernel_work_group_info +#define CL_KERNEL_WORK_GROUP_SIZE 0x11B0 +#define CL_KERNEL_COMPILE_WORK_GROUP_SIZE 0x11B1 +#define CL_KERNEL_LOCAL_MEM_SIZE 0x11B2 + +// cl_event_info +#define CL_EVENT_COMMAND_QUEUE 0x11D0 +#define CL_EVENT_COMMAND_TYPE 0x11D1 +#define CL_EVENT_REFERENCE_COUNT 0x11D2 +#define CL_EVENT_COMMAND_EXECUTION_STATUS 0x11D3 + +// cl_command_type +#define CL_COMMAND_NDRANGE_KERNEL 0x11F0 +#define CL_COMMAND_TASK 0x11F1 +#define CL_COMMAND_NATIVE_KERNEL 0x11F2 +#define CL_COMMAND_READ_BUFFER 0x11F3 +#define CL_COMMAND_WRITE_BUFFER 0x11F4 +#define CL_COMMAND_COPY_BUFFER 0x11F5 +#define CL_COMMAND_READ_IMAGE 0x11F6 +#define CL_COMMAND_WRITE_IMAGE 0x11F7 +#define CL_COMMAND_COPY_IMAGE 0x11F8 +#define CL_COMMAND_COPY_IMAGE_TO_BUFFER 0x11F9 +#define CL_COMMAND_COPY_BUFFER_TO_IMAGE 0x11FA +#define CL_COMMAND_MAP_BUFFER 0x11FB +#define CL_COMMAND_MAP_IMAGE 0x11FC +#define CL_COMMAND_UNMAP_MEM_OBJECT 0x11FD +#define CL_COMMAND_MARKER 0x11FE +#define CL_COMMAND_WAIT_FOR_EVENTS 0x11FF +#define CL_COMMAND_BARRIER 0x1200 +#define CL_COMMAND_ACQUIRE_GL_OBJECTS 0x1201 +#define CL_COMMAND_RELEASE_GL_OBJECTS 0x1202 + +// command execution status +#define CL_COMPLETE 0x0 +#define CL_RUNNING 0x1 +#define CL_SUBMITTED 0x2 +#define CL_QUEUED 0x3 + +// cl_profiling_info +#define CL_PROFILING_COMMAND_QUEUED 0x1280 +#define CL_PROFILING_COMMAND_SUBMIT 0x1281 +#define CL_PROFILING_COMMAND_START 0x1282 +#define CL_PROFILING_COMMAND_END 0x1283 + +/********************************************************************************************************/ + +// Platform API +extern CL_API_ENTRY cl_int CL_API_CALL +clGetPlatformIDs(cl_uint /* num_entries */, + cl_platform_id * /* platforms */, + cl_uint * /* num_platforms */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetPlatformInfo(cl_platform_id /* platform */, + cl_platform_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +// Device APIs +extern CL_API_ENTRY cl_int CL_API_CALL +clGetDeviceIDs(cl_platform_id /* platform */, + cl_device_type /* device_type */, + cl_uint /* num_entries */, + cl_device_id * /* devices */, + cl_uint * /* num_devices */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetDeviceInfo(cl_device_id /* device */, + cl_device_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +// Context APIs +extern CL_API_ENTRY cl_context CL_API_CALL +clCreateContext(cl_context_properties * /* properties */, + cl_uint /* num_devices */, + const cl_device_id * /* devices */, + void (*pfn_notify)(const char *, const void *, size_t, void *) /* pfn_notify */, + void * /* user_data */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_context CL_API_CALL +clCreateContextFromType(cl_context_properties * /* properties */, + cl_device_type /* device_type */, + void (*pfn_notify)(const char *, const void *, size_t, void *) /* pfn_notify */, + void * /* user_data */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clRetainContext(cl_context /* context */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clReleaseContext(cl_context /* context */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetContextInfo(cl_context /* context */, + cl_context_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +// Command Queue APIs +extern CL_API_ENTRY cl_command_queue CL_API_CALL +clCreateCommandQueue(cl_context /* context */, + cl_device_id /* device */, + cl_command_queue_properties /* properties */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clRetainCommandQueue(cl_command_queue /* command_queue */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clReleaseCommandQueue(cl_command_queue /* command_queue */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetCommandQueueInfo(cl_command_queue /* command_queue */, + cl_command_queue_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clSetCommandQueueProperty(cl_command_queue /* command_queue */, + cl_command_queue_properties /* properties */, + cl_bool /* enable */, + cl_command_queue_properties * /* old_properties */) CL_API_SUFFIX__VERSION_1_0; + +// Memory Object APIs +extern CL_API_ENTRY cl_mem CL_API_CALL +clCreateBuffer(cl_context /* context */, + cl_mem_flags /* flags */, + size_t /* size */, + void * /* host_ptr */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_mem CL_API_CALL +clCreateImage2D(cl_context /* context */, + cl_mem_flags /* flags */, + const cl_image_format * /* image_format */, + size_t /* image_width */, + size_t /* image_height */, + size_t /* image_row_pitch */, + void * /* host_ptr */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_mem CL_API_CALL +clCreateImage3D(cl_context /* context */, + cl_mem_flags /* flags */, + const cl_image_format * /* image_format */, + size_t /* image_width */, + size_t /* image_height */, + size_t /* image_depth */, + size_t /* image_row_pitch */, + size_t /* image_slice_pitch */, + void * /* host_ptr */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clRetainMemObject(cl_mem /* memobj */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clReleaseMemObject(cl_mem /* memobj */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetSupportedImageFormats(cl_context /* context */, + cl_mem_flags /* flags */, + cl_mem_object_type /* image_type */, + cl_uint /* num_entries */, + cl_image_format * /* image_formats */, + cl_uint * /* num_image_formats */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetMemObjectInfo(cl_mem /* memobj */, + cl_mem_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetImageInfo(cl_mem /* image */, + cl_image_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +// Sampler APIs +extern CL_API_ENTRY cl_sampler CL_API_CALL +clCreateSampler(cl_context /* context */, + cl_bool /* normalized_coords */, + cl_addressing_mode /* addressing_mode */, + cl_filter_mode /* filter_mode */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clRetainSampler(cl_sampler /* sampler */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clReleaseSampler(cl_sampler /* sampler */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetSamplerInfo(cl_sampler /* sampler */, + cl_sampler_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +// Program Object APIs +extern CL_API_ENTRY cl_program CL_API_CALL +clCreateProgramWithSource(cl_context /* context */, + cl_uint /* count */, + const char ** /* strings */, + const size_t * /* lengths */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_program CL_API_CALL +clCreateProgramWithBinary(cl_context /* context */, + cl_uint /* num_devices */, + const cl_device_id * /* device_list */, + const size_t * /* lengths */, + const unsigned char ** /* binaries */, + cl_int * /* binary_status */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clRetainProgram(cl_program /* program */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clReleaseProgram(cl_program /* program */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clBuildProgram(cl_program /* program */, + cl_uint /* num_devices */, + const cl_device_id * /* device_list */, + const char * /* options */, + void (*pfn_notify)(cl_program /* program */, void * /* user_data */), + void * /* user_data */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clUnloadCompiler(void) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetProgramInfo(cl_program /* program */, + cl_program_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetProgramBuildInfo(cl_program /* program */, + cl_device_id /* device */, + cl_program_build_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +// Kernel Object APIs +extern CL_API_ENTRY cl_kernel CL_API_CALL +clCreateKernel(cl_program /* program */, + const char * /* kernel_name */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clCreateKernelsInProgram(cl_program /* program */, + cl_uint /* num_kernels */, + cl_kernel * /* kernels */, + cl_uint * /* num_kernels_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clRetainKernel(cl_kernel /* kernel */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clReleaseKernel(cl_kernel /* kernel */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clSetKernelArg(cl_kernel /* kernel */, + cl_uint /* arg_index */, + size_t /* arg_size */, + const void * /* arg_value */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetKernelInfo(cl_kernel /* kernel */, + cl_kernel_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetKernelWorkGroupInfo(cl_kernel /* kernel */, + cl_device_id /* device */, + cl_kernel_work_group_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +// Event Object APIs +extern CL_API_ENTRY cl_int CL_API_CALL +clWaitForEvents(cl_uint /* num_events */, + const cl_event * /* event_list */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetEventInfo(cl_event /* event */, + cl_event_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clRetainEvent(cl_event /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clReleaseEvent(cl_event /* event */) CL_API_SUFFIX__VERSION_1_0; + +// Profiling APIs +extern CL_API_ENTRY cl_int CL_API_CALL +clGetEventProfilingInfo(cl_event /* event */, + cl_profiling_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +// Flush and Finish APIs +extern CL_API_ENTRY cl_int CL_API_CALL +clFlush(cl_command_queue /* command_queue */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clFinish(cl_command_queue /* command_queue */) CL_API_SUFFIX__VERSION_1_0; + +// Enqueued Commands APIs +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueReadBuffer(cl_command_queue /* command_queue */, + cl_mem /* buffer */, + cl_bool /* blocking_read */, + size_t /* offset */, + size_t /* cb */, + void * /* ptr */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueWriteBuffer(cl_command_queue /* command_queue */, + cl_mem /* buffer */, + cl_bool /* blocking_write */, + size_t /* offset */, + size_t /* cb */, + const void * /* ptr */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueCopyBuffer(cl_command_queue /* command_queue */, + cl_mem /* src_buffer */, + cl_mem /* dst_buffer */, + size_t /* src_offset */, + size_t /* dst_offset */, + size_t /* cb */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueReadImage(cl_command_queue /* command_queue */, + cl_mem /* image */, + cl_bool /* blocking_read */, + const size_t * /* origin[3] */, + const size_t * /* region[3] */, + size_t /* row_pitch */, + size_t /* slice_pitch */, + void * /* ptr */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueWriteImage(cl_command_queue /* command_queue */, + cl_mem /* image */, + cl_bool /* blocking_write */, + const size_t * /* origin[3] */, + const size_t * /* region[3] */, + size_t /* input_row_pitch */, + size_t /* input_slice_pitch */, + const void * /* ptr */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueCopyImage(cl_command_queue /* command_queue */, + cl_mem /* src_image */, + cl_mem /* dst_image */, + const size_t * /* src_origin[3] */, + const size_t * /* dst_origin[3] */, + const size_t * /* region[3] */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueCopyImageToBuffer(cl_command_queue /* command_queue */, + cl_mem /* src_image */, + cl_mem /* dst_buffer */, + const size_t * /* src_origin[3] */, + const size_t * /* region[3] */, + size_t /* dst_offset */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueCopyBufferToImage(cl_command_queue /* command_queue */, + cl_mem /* src_buffer */, + cl_mem /* dst_image */, + size_t /* src_offset */, + const size_t * /* dst_origin[3] */, + const size_t * /* region[3] */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY void * CL_API_CALL +clEnqueueMapBuffer(cl_command_queue /* command_queue */, + cl_mem /* buffer */, + cl_bool /* blocking_map */, + cl_map_flags /* map_flags */, + size_t /* offset */, + size_t /* cb */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY void * CL_API_CALL +clEnqueueMapImage(cl_command_queue /* command_queue */, + cl_mem /* image */, + cl_bool /* blocking_map */, + cl_map_flags /* map_flags */, + const size_t * /* origin[3] */, + const size_t * /* region[3] */, + size_t * /* image_row_pitch */, + size_t * /* image_slice_pitch */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueUnmapMemObject(cl_command_queue /* command_queue */, + cl_mem /* memobj */, + void * /* mapped_ptr */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueNDRangeKernel(cl_command_queue /* command_queue */, + cl_kernel /* kernel */, + cl_uint /* work_dim */, + const size_t * /* global_work_offset */, + const size_t * /* global_work_size */, + const size_t * /* local_work_size */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueTask(cl_command_queue /* command_queue */, + cl_kernel /* kernel */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueNativeKernel(cl_command_queue /* command_queue */, + void (*user_func)(void *), + void * /* args */, + size_t /* cb_args */, + cl_uint /* num_mem_objects */, + const cl_mem * /* mem_list */, + const void ** /* args_mem_loc */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueMarker(cl_command_queue /* command_queue */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueWaitForEvents(cl_command_queue /* command_queue */, + cl_uint /* num_events */, + const cl_event * /* event_list */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueBarrier(cl_command_queue /* command_queue */) CL_API_SUFFIX__VERSION_1_0; + +#ifdef __cplusplus +} +#endif + +#endif // __OPENCL_CL_H + diff --git a/Engine/lib/bullet/src/MiniCL/cl_gl.h b/Engine/lib/bullet/src/MiniCL/cl_gl.h new file mode 100644 index 000000000..71bdaaa6e --- /dev/null +++ b/Engine/lib/bullet/src/MiniCL/cl_gl.h @@ -0,0 +1,113 @@ +/********************************************************************************** + * Copyright (c) 2008-2009 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and/or associated documentation files (the + * "Materials"), to deal in the Materials without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Materials, and to + * permit persons to whom the Materials are furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + **********************************************************************************/ + +#ifndef __OPENCL_CL_GL_H +#define __OPENCL_CL_GL_H + +#ifdef __APPLE__ +#include +#else +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// NOTE: Make sure that appropriate GL header file is included separately + +typedef cl_uint cl_gl_object_type; +typedef cl_uint cl_gl_texture_info; +typedef cl_uint cl_gl_platform_info; + +// cl_gl_object_type +#define CL_GL_OBJECT_BUFFER 0x2000 +#define CL_GL_OBJECT_TEXTURE2D 0x2001 +#define CL_GL_OBJECT_TEXTURE3D 0x2002 +#define CL_GL_OBJECT_RENDERBUFFER 0x2003 + +// cl_gl_texture_info +#define CL_GL_TEXTURE_TARGET 0x2004 +#define CL_GL_MIPMAP_LEVEL 0x2005 + +extern CL_API_ENTRY cl_mem CL_API_CALL +clCreateFromGLBuffer(cl_context /* context */, + cl_mem_flags /* flags */, + GLuint /* bufobj */, + int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_mem CL_API_CALL +clCreateFromGLTexture2D(cl_context /* context */, + cl_mem_flags /* flags */, + GLenum /* target */, + GLint /* miplevel */, + GLuint /* texture */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_mem CL_API_CALL +clCreateFromGLTexture3D(cl_context /* context */, + cl_mem_flags /* flags */, + GLenum /* target */, + GLint /* miplevel */, + GLuint /* texture */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_mem CL_API_CALL +clCreateFromGLRenderbuffer(cl_context /* context */, + cl_mem_flags /* flags */, + GLuint /* renderbuffer */, + cl_int * /* errcode_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetGLObjectInfo(cl_mem /* memobj */, + cl_gl_object_type * /* gl_object_type */, + GLuint * /* gl_object_name */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clGetGLTextureInfo(cl_mem /* memobj */, + cl_gl_texture_info /* param_name */, + size_t /* param_value_size */, + void * /* param_value */, + size_t * /* param_value_size_ret */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueAcquireGLObjects(cl_command_queue /* command_queue */, + cl_uint /* num_objects */, + const cl_mem * /* mem_objects */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +extern CL_API_ENTRY cl_int CL_API_CALL +clEnqueueReleaseGLObjects(cl_command_queue /* command_queue */, + cl_uint /* num_objects */, + const cl_mem * /* mem_objects */, + cl_uint /* num_events_in_wait_list */, + const cl_event * /* event_wait_list */, + cl_event * /* event */) CL_API_SUFFIX__VERSION_1_0; + +#ifdef __cplusplus +} +#endif + +#endif // __OPENCL_CL_GL_H diff --git a/Engine/lib/bullet/src/MiniCL/cl_platform.h b/Engine/lib/bullet/src/MiniCL/cl_platform.h new file mode 100644 index 000000000..522512996 --- /dev/null +++ b/Engine/lib/bullet/src/MiniCL/cl_platform.h @@ -0,0 +1,244 @@ +/********************************************************************************** + * Copyright (c) 2008-2009 The Khronos Group Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and/or associated documentation files (the + * "Materials"), to deal in the Materials without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Materials, and to + * permit persons to whom the Materials are furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. + **********************************************************************************/ + +#ifndef __CL_PLATFORM_H +#define __CL_PLATFORM_H + +#ifdef __APPLE__ + /* Contains #defines for AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER below */ + #include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define CL_API_ENTRY +#define CL_API_CALL +#ifdef __APPLE__ +#define CL_API_SUFFIX__VERSION_1_0 // AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER +#define CL_EXTENSION_WEAK_LINK __attribute__((weak_import)) +#else +#define CL_API_SUFFIX__VERSION_1_0 +#define CL_EXTENSION_WEAK_LINK +#endif + +#ifdef WIN32 +typedef signed __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef signed __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef signed __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + +typedef int8_t cl_char; +typedef uint8_t cl_uchar; +typedef int16_t cl_short ; +typedef uint16_t cl_ushort ; +typedef int32_t cl_int ; +typedef uint32_t cl_uint ; +typedef int64_t cl_long ; +typedef uint64_t cl_ulong ; + +typedef uint16_t cl_half ; +typedef float cl_float ; +typedef double cl_double ; + + +typedef int8_t cl_char2[2] ; +typedef int8_t cl_char4[4] ; +typedef int8_t cl_char8[8] ; +typedef int8_t cl_char16[16] ; +typedef uint8_t cl_uchar2[2] ; +typedef uint8_t cl_uchar4[4] ; +typedef uint8_t cl_uchar8[8] ; +typedef uint8_t cl_uchar16[16] ; + +typedef int16_t cl_short2[2] ; +typedef int16_t cl_short4[4] ; +typedef int16_t cl_short8[8] ; +typedef int16_t cl_short16[16] ; +typedef uint16_t cl_ushort2[2] ; +typedef uint16_t cl_ushort4[4] ; +typedef uint16_t cl_ushort8[8] ; +typedef uint16_t cl_ushort16[16] ; + +typedef int32_t cl_int2[2] ; +typedef int32_t cl_int4[4] ; +typedef int32_t cl_int8[8] ; +typedef int32_t cl_int16[16] ; +typedef uint32_t cl_uint2[2] ; +typedef uint32_t cl_uint4[4] ; +typedef uint32_t cl_uint8[8] ; +typedef uint32_t cl_uint16[16] ; + +typedef int64_t cl_long2[2] ; +typedef int64_t cl_long4[4] ; +typedef int64_t cl_long8[8] ; +typedef int64_t cl_long16[16] ; +typedef uint64_t cl_ulong2[2] ; +typedef uint64_t cl_ulong4[4] ; +typedef uint64_t cl_ulong8[8] ; +typedef uint64_t cl_ulong16[16] ; + +typedef float cl_float2[2] ; +typedef float cl_float4[4] ; +typedef float cl_float8[8] ; +typedef float cl_float16[16] ; + +typedef double cl_double2[2] ; +typedef double cl_double4[4] ; +typedef double cl_double8[8] ; +typedef double cl_double16[16] ; + + +#else +#include + +/* scalar types */ +typedef int8_t cl_char; +typedef uint8_t cl_uchar; +typedef int16_t cl_short __attribute__((aligned(2))); +typedef uint16_t cl_ushort __attribute__((aligned(2))); +typedef int32_t cl_int __attribute__((aligned(4))); +typedef uint32_t cl_uint __attribute__((aligned(4))); +typedef int64_t cl_long __attribute__((aligned(8))); +typedef uint64_t cl_ulong __attribute__((aligned(8))); + +typedef uint16_t cl_half __attribute__((aligned(2))); +typedef float cl_float __attribute__((aligned(4))); +typedef double cl_double __attribute__((aligned(8))); + + +/* + * Vector types + * + * Note: OpenCL requires that all types be naturally aligned. + * This means that vector types must be naturally aligned. + * For example, a vector of four floats must be aligned to + * a 16 byte boundary (calculated as 4 * the natural 4-byte + * alignment of the float). The alignment qualifiers here + * will only function properly if your compiler supports them + * and if you don't actively work to defeat them. For example, + * in order for a cl_float4 to be 16 byte aligned in a struct, + * the start of the struct must itself be 16-byte aligned. + * + * Maintaining proper alignment is the user's responsibility. + */ +typedef int8_t cl_char2[2] __attribute__((aligned(2))); +typedef int8_t cl_char4[4] __attribute__((aligned(4))); +typedef int8_t cl_char8[8] __attribute__((aligned(8))); +typedef int8_t cl_char16[16] __attribute__((aligned(16))); +typedef uint8_t cl_uchar2[2] __attribute__((aligned(2))); +typedef uint8_t cl_uchar4[4] __attribute__((aligned(4))); +typedef uint8_t cl_uchar8[8] __attribute__((aligned(8))); +typedef uint8_t cl_uchar16[16] __attribute__((aligned(16))); + +typedef int16_t cl_short2[2] __attribute__((aligned(4))); +typedef int16_t cl_short4[4] __attribute__((aligned(8))); +typedef int16_t cl_short8[8] __attribute__((aligned(16))); +typedef int16_t cl_short16[16] __attribute__((aligned(32))); +typedef uint16_t cl_ushort2[2] __attribute__((aligned(4))); +typedef uint16_t cl_ushort4[4] __attribute__((aligned(8))); +typedef uint16_t cl_ushort8[8] __attribute__((aligned(16))); +typedef uint16_t cl_ushort16[16] __attribute__((aligned(32))); + +typedef int32_t cl_int2[2] __attribute__((aligned(8))); +typedef int32_t cl_int4[4] __attribute__((aligned(16))); +typedef int32_t cl_int8[8] __attribute__((aligned(32))); +typedef int32_t cl_int16[16] __attribute__((aligned(64))); +typedef uint32_t cl_uint2[2] __attribute__((aligned(8))); +typedef uint32_t cl_uint4[4] __attribute__((aligned(16))); +typedef uint32_t cl_uint8[8] __attribute__((aligned(32))); +typedef uint32_t cl_uint16[16] __attribute__((aligned(64))); + +typedef int64_t cl_long2[2] __attribute__((aligned(16))); +typedef int64_t cl_long4[4] __attribute__((aligned(32))); +typedef int64_t cl_long8[8] __attribute__((aligned(64))); +typedef int64_t cl_long16[16] __attribute__((aligned(128))); +typedef uint64_t cl_ulong2[2] __attribute__((aligned(16))); +typedef uint64_t cl_ulong4[4] __attribute__((aligned(32))); +typedef uint64_t cl_ulong8[8] __attribute__((aligned(64))); +typedef uint64_t cl_ulong16[16] __attribute__((aligned(128))); + +typedef float cl_float2[2] __attribute__((aligned(8))); +typedef float cl_float4[4] __attribute__((aligned(16))); +typedef float cl_float8[8] __attribute__((aligned(32))); +typedef float cl_float16[16] __attribute__((aligned(64))); + +typedef double cl_double2[2] __attribute__((aligned(16))); +typedef double cl_double4[4] __attribute__((aligned(32))); +typedef double cl_double8[8] __attribute__((aligned(64))); +typedef double cl_double16[16] __attribute__((aligned(128))); +#endif + +#include + +/* and a few goodies to go with them */ +#define CL_CHAR_BIT 8 +#define CL_SCHAR_MAX 127 +#define CL_SCHAR_MIN (-127-1) +#define CL_CHAR_MAX CL_SCHAR_MAX +#define CL_CHAR_MIN CL_SCHAR_MIN +#define CL_UCHAR_MAX 255 +#define CL_SHRT_MAX 32767 +#define CL_SHRT_MIN (-32767-1) +#define CL_USHRT_MAX 65535 +#define CL_INT_MAX 2147483647 +#define CL_INT_MIN (-2147483647-1) +#define CL_UINT_MAX 0xffffffffU +#define CL_LONG_MAX ((cl_long) 0x7FFFFFFFFFFFFFFFLL) +#define CL_LONG_MIN ((cl_long) -0x7FFFFFFFFFFFFFFFLL - 1LL) +#define CL_ULONG_MAX ((cl_ulong) 0xFFFFFFFFFFFFFFFFULL) + +#define CL_FLT_DIG 6 +#define CL_FLT_MANT_DIG 24 +#define CL_FLT_MAX_10_EXP +38 +#define CL_FLT_MAX_EXP +128 +#define CL_FLT_MIN_10_EXP -37 +#define CL_FLT_MIN_EXP -125 +#define CL_FLT_RADIX 2 +#define CL_FLT_MAX 0x1.fffffep127f +#define CL_FLT_MIN 0x1.0p-126f +#define CL_FLT_EPSILON 0x1.0p-23f + +#define CL_DBL_DIG 15 +#define CL_DBL_MANT_DIG 53 +#define CL_DBL_MAX_10_EXP +308 +#define CL_DBL_MAX_EXP +1024 +#define CL_DBL_MIN_10_EXP -307 +#define CL_DBL_MIN_EXP -1021 +#define CL_DBL_RADIX 2 +#define CL_DBL_MAX 0x1.fffffffffffffp1023 +#define CL_DBL_MIN 0x1.0p-1022 +#define CL_DBL_EPSILON 0x1.0p-52 + +/* There are no vector types for half */ + +#ifdef __cplusplus +} +#endif + +#endif // __CL_PLATFORM_H diff --git a/Engine/lib/bullet/src/btBulletCollisionCommon.h b/Engine/lib/bullet/src/btBulletCollisionCommon.h new file mode 100644 index 000000000..4b14f6d00 --- /dev/null +++ b/Engine/lib/bullet/src/btBulletCollisionCommon.h @@ -0,0 +1,66 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 BULLET_COLLISION_COMMON_H +#define BULLET_COLLISION_COMMON_H + +///Common headerfile includes for Bullet Collision Detection + +///Bullet's btCollisionWorld and btCollisionObject definitions +#include "BulletCollision/CollisionDispatch/btCollisionWorld.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" + +///Collision Shapes +#include "BulletCollision/CollisionShapes/btBoxShape.h" +#include "BulletCollision/CollisionShapes/btSphereShape.h" +#include "BulletCollision/CollisionShapes/btCapsuleShape.h" +#include "BulletCollision/CollisionShapes/btCylinderShape.h" +#include "BulletCollision/CollisionShapes/btConeShape.h" +#include "BulletCollision/CollisionShapes/btStaticPlaneShape.h" +#include "BulletCollision/CollisionShapes/btConvexHullShape.h" +#include "BulletCollision/CollisionShapes/btTriangleMesh.h" +#include "BulletCollision/CollisionShapes/btConvexTriangleMeshShape.h" +#include "BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h" +#include "BulletCollision/CollisionShapes/btTriangleMeshShape.h" +#include "BulletCollision/CollisionShapes/btTriangleIndexVertexArray.h" +#include "BulletCollision/CollisionShapes/btCompoundShape.h" +#include "BulletCollision/CollisionShapes/btTetrahedronShape.h" +#include "BulletCollision/CollisionShapes/btEmptyShape.h" +#include "BulletCollision/CollisionShapes/btMultiSphereShape.h" +#include "BulletCollision/CollisionShapes/btUniformScalingShape.h" + +///Narrowphase Collision Detector +#include "BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.h" + +//btSphereBoxCollisionAlgorithm is broken, use gjk for now +//#include "BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h" + +///Dispatching and generation of collision pairs (broadphase) +#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h" +#include "BulletCollision/BroadphaseCollision/btSimpleBroadphase.h" +#include "BulletCollision/BroadphaseCollision/btAxisSweep3.h" +#include "BulletCollision/BroadphaseCollision/btMultiSapBroadphase.h" +#include "BulletCollision/BroadphaseCollision/btDbvtBroadphase.h" + +///Math library & Utils +#include "LinearMath/btQuaternion.h" +#include "LinearMath/btTransform.h" +#include "LinearMath/btDefaultMotionState.h" +#include "LinearMath/btQuickprof.h" +#include "LinearMath/btIDebugDraw.h" + +#endif //BULLET_COLLISION_COMMON_H + diff --git a/Engine/lib/bullet/src/btBulletDynamicsCommon.h b/Engine/lib/bullet/src/btBulletDynamicsCommon.h new file mode 100644 index 000000000..db8b37989 --- /dev/null +++ b/Engine/lib/bullet/src/btBulletDynamicsCommon.h @@ -0,0 +1,49 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +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 BULLET_DYNAMICS_COMMON_H +#define BULLET_DYNAMICS_COMMON_H + +///Common headerfile includes for Bullet Dynamics, including Collision Detection +#include "btBulletCollisionCommon.h" + +#include "BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h" +#include "BulletDynamics/Dynamics/btContinuousDynamicsWorld.h" + +#include "BulletDynamics/Dynamics/btSimpleDynamicsWorld.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" + +#include "BulletDynamics/ConstraintSolver/btPoint2PointConstraint.h" +#include "BulletDynamics/ConstraintSolver/btHingeConstraint.h" +#include "BulletDynamics/ConstraintSolver/btConeTwistConstraint.h" +#include "BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.h" +#include "BulletDynamics/ConstraintSolver/btSliderConstraint.h" +#include "BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.h" +#include "BulletDynamics/ConstraintSolver/btUniversalConstraint.h" +#include "BulletDynamics/ConstraintSolver/btHinge2Constraint.h" + +#include "BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h" + + +///Vehicle simulation, with wheel contact simulated by raycasts +#include "BulletDynamics/Vehicle/btRaycastVehicle.h" + + + + + + +#endif //BULLET_DYNAMICS_COMMON_H + diff --git a/Engine/lib/bullet/src/ibmsdk/Makefile b/Engine/lib/bullet/src/ibmsdk/Makefile new file mode 100644 index 000000000..1a0453eef --- /dev/null +++ b/Engine/lib/bullet/src/ibmsdk/Makefile @@ -0,0 +1,16 @@ +#### Visit Bullet library ibmsdk dirs and build code + +DIRS := \ +../BulletCollision/ibmsdk \ +../BulletDynamics/ibmsdk \ +../LinearMath/ibmsdk + +IBM_CELLSDK_VERSION := $(shell if [ -d /opt/cell ]; then echo "3.0"; fi) + +ifeq ("$(IBM_CELLSDK_VERSION)","3.0") + CELL_TOP ?= /opt/cell/sdk + include $(CELL_TOP)/buildutils/make.footer +else + CELL_TOP ?= /opt/ibm/cell-sdk/prototype + include $(CELL_TOP)/make.footer +endif diff --git a/Engine/lib/collada/changes.txt b/Engine/lib/collada/changes.txt new file mode 100644 index 000000000..851874537 --- /dev/null +++ b/Engine/lib/collada/changes.txt @@ -0,0 +1,2 @@ +Need to include to use std::auto_ptr with gcc-4.3 - Andrew Galante, GG 8/2/2009: include/dae.h + diff --git a/Engine/lib/collada/include/1.4/dom/domAccessor.h b/Engine/lib/collada/include/1.4/dom/domAccessor.h new file mode 100644 index 000000000..77cc4d752 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domAccessor.h @@ -0,0 +1,159 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domAccessor_h__ +#define __domAccessor_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * The accessor element declares an access pattern to one of the array elements: + * float_array, int_array, Name_array, bool_array, and IDREF_array. The accessor + * element describes access to arrays that are organized in either an interleaved + * or non-interleaved manner, depending on the offset and stride attributes. + */ +class domAccessor : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ACCESSOR; } + static daeInt ID() { return 609; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The count attribute indicates the number of times the array is accessed. + * Required attribute. + */ + domUint attrCount; +/** + * The offset attribute indicates the index of the first value to be read + * from the array. The default value is 0. Optional attribute. + */ + domUint attrOffset; +/** + * The source attribute indicates the location of the array to access using + * a URL expression. Required attribute. + */ + xsAnyURI attrSource; +/** + * The stride attribute indicates number of values to be considered a unit + * during each access to the array. The default value is 1, indicating that + * a single value is accessed. Optional attribute. + */ + domUint attrStride; + +protected: // Element +/** + * The accessor element may have any number of param elements. @see domParam + */ + domParam_Array elemParam_array; + +public: //Accessors and Mutators + /** + * Gets the count attribute. + * @return Returns a domUint of the count attribute. + */ + domUint getCount() const { return attrCount; } + /** + * Sets the count attribute. + * @param atCount The new value for the count attribute. + */ + void setCount( domUint atCount ) { attrCount = atCount; _validAttributeArray[0] = true; } + + /** + * Gets the offset attribute. + * @return Returns a domUint of the offset attribute. + */ + domUint getOffset() const { return attrOffset; } + /** + * Sets the offset attribute. + * @param atOffset The new value for the offset attribute. + */ + void setOffset( domUint atOffset ) { attrOffset = atOffset; _validAttributeArray[1] = true; } + + /** + * Gets the source attribute. + * @return Returns a xsAnyURI reference of the source attribute. + */ + xsAnyURI &getSource() { return attrSource; } + /** + * Gets the source attribute. + * @return Returns a constant xsAnyURI reference of the source attribute. + */ + const xsAnyURI &getSource() const { return attrSource; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( const xsAnyURI &atSource ) { attrSource = atSource; _validAttributeArray[2] = true; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( xsString atSource ) { attrSource = atSource; _validAttributeArray[2] = true; } + + /** + * Gets the stride attribute. + * @return Returns a domUint of the stride attribute. + */ + domUint getStride() const { return attrStride; } + /** + * Sets the stride attribute. + * @param atStride The new value for the stride attribute. + */ + void setStride( domUint atStride ) { attrStride = atStride; _validAttributeArray[3] = true; } + + /** + * Gets the param element array. + * @return Returns a reference to the array of param elements. + */ + domParam_Array &getParam_array() { return elemParam_array; } + /** + * Gets the param element array. + * @return Returns a constant reference to the array of param elements. + */ + const domParam_Array &getParam_array() const { return elemParam_array; } +protected: + /** + * Constructor + */ + domAccessor(DAE& dae) : daeElement(dae), attrCount(), attrOffset(), attrSource(dae, *this), attrStride(), elemParam_array() {} + /** + * Destructor + */ + virtual ~domAccessor() {} + /** + * Overloaded assignment operator + */ + virtual domAccessor &operator=( const domAccessor &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domAnimation.h b/Engine/lib/collada/include/1.4/dom/domAnimation.h new file mode 100644 index 000000000..fc1d233d0 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domAnimation.h @@ -0,0 +1,217 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domAnimation_h__ +#define __domAnimation_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +class DAE; + +/** + * The animation element categorizes the declaration of animation information. + * The animation hierarchy contains elements that describe the animation’s + * key-frame data and sampler functions, ordered in such a way to group together + * animations that should be executed together. + */ +class domAnimation : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ANIMATION; } + static daeInt ID() { return 651; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The animation element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * The animation element may contain any number of source elements. @see + * domSource + */ + domSource_Array elemSource_array; +/** + * The animation element may contain any number of sampler elements. @see + * domSampler + */ + domSampler_Array elemSampler_array; +/** + * The animation element may contain any number of channel elements. @see + * domChannel + */ + domChannel_Array elemChannel_array; +/** + * The animation may be hierarchical and may contain any number of other + * animation elements. @see domAnimation + */ + domAnimation_Array elemAnimation_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the source element array. + * @return Returns a reference to the array of source elements. + */ + domSource_Array &getSource_array() { return elemSource_array; } + /** + * Gets the source element array. + * @return Returns a constant reference to the array of source elements. + */ + const domSource_Array &getSource_array() const { return elemSource_array; } + /** + * Gets the sampler element array. + * @return Returns a reference to the array of sampler elements. + */ + domSampler_Array &getSampler_array() { return elemSampler_array; } + /** + * Gets the sampler element array. + * @return Returns a constant reference to the array of sampler elements. + */ + const domSampler_Array &getSampler_array() const { return elemSampler_array; } + /** + * Gets the channel element array. + * @return Returns a reference to the array of channel elements. + */ + domChannel_Array &getChannel_array() { return elemChannel_array; } + /** + * Gets the channel element array. + * @return Returns a constant reference to the array of channel elements. + */ + const domChannel_Array &getChannel_array() const { return elemChannel_array; } + /** + * Gets the animation element array. + * @return Returns a reference to the array of animation elements. + */ + domAnimation_Array &getAnimation_array() { return elemAnimation_array; } + /** + * Gets the animation element array. + * @return Returns a constant reference to the array of animation elements. + */ + const domAnimation_Array &getAnimation_array() const { return elemAnimation_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domAnimation(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemSource_array(), elemSampler_array(), elemChannel_array(), elemAnimation_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domAnimation() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domAnimation &operator=( const domAnimation &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domAnimation_clip.h b/Engine/lib/collada/include/1.4/dom/domAnimation_clip.h new file mode 100644 index 000000000..451bc756d --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domAnimation_clip.h @@ -0,0 +1,179 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domAnimation_clip_h__ +#define __domAnimation_clip_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * The animation_clip element defines a section of the animation curves to + * be used together as an animation clip. + */ +class domAnimation_clip : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ANIMATION_CLIP; } + static daeInt ID() { return 652; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; +/** + * The start attribute is the time in seconds of the beginning of the clip. + * This time is the same as that used in the key-frame data and is used to + * determine which set of key-frames will be included in the clip. The start + * time does not specify when the clip will be played. If the time falls + * between two keyframes of a referenced animation, an interpolated value + * should be used. The default value is 0.0. Optional attribute. + */ + xsDouble attrStart; +/** + * The end attribute is the time in seconds of the end of the clip. This + * is used in the same way as the start time. If end is not specified, the + * value is taken to be the end time of the longest animation. Optional + * attribute. + */ + xsDouble attrEnd; + +protected: // Elements +/** + * The animation_clip element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * The animation_clip must instance at least one animation element. @see domInstance_animation + */ + domInstanceWithExtra_Array elemInstance_animation_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the start attribute. + * @return Returns a xsDouble of the start attribute. + */ + xsDouble getStart() const { return attrStart; } + /** + * Sets the start attribute. + * @param atStart The new value for the start attribute. + */ + void setStart( xsDouble atStart ) { attrStart = atStart; _validAttributeArray[2] = true; } + + /** + * Gets the end attribute. + * @return Returns a xsDouble of the end attribute. + */ + xsDouble getEnd() const { return attrEnd; } + /** + * Sets the end attribute. + * @param atEnd The new value for the end attribute. + */ + void setEnd( xsDouble atEnd ) { attrEnd = atEnd; _validAttributeArray[3] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the instance_animation element array. + * @return Returns a reference to the array of instance_animation elements. + */ + domInstanceWithExtra_Array &getInstance_animation_array() { return elemInstance_animation_array; } + /** + * Gets the instance_animation element array. + * @return Returns a constant reference to the array of instance_animation elements. + */ + const domInstanceWithExtra_Array &getInstance_animation_array() const { return elemInstance_animation_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domAnimation_clip(DAE& dae) : daeElement(dae), attrId(), attrName(), attrStart(), attrEnd(), elemAsset(), elemInstance_animation_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domAnimation_clip() {} + /** + * Overloaded assignment operator + */ + virtual domAnimation_clip &operator=( const domAnimation_clip &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domAsset.h b/Engine/lib/collada/include/1.4/dom/domAsset.h new file mode 100644 index 000000000..c93fab3ff --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domAsset.h @@ -0,0 +1,1106 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domAsset_h__ +#define __domAsset_h__ + +#include +#include +#include + +class DAE; + +/** + * The asset element defines asset management information regarding its parent + * element. + */ +class domAsset : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ASSET; } + static daeInt ID() { return 664; } + virtual daeInt typeID() const { return ID(); } +public: + class domContributor; + + typedef daeSmartRef domContributorRef; + typedef daeTArray domContributor_Array; + +/** + * The contributor element defines authoring information for asset management + */ + class domContributor : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CONTRIBUTOR; } + static daeInt ID() { return 665; } + virtual daeInt typeID() const { return ID(); } + public: + class domAuthor; + + typedef daeSmartRef domAuthorRef; + typedef daeTArray domAuthor_Array; + +/** + * The author element contains a string with the author's name. There may + * be only one author element. + */ + class domAuthor : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::AUTHOR; } + static daeInt ID() { return 666; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsString value of the text data of this element. + */ + xsString _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsString of the value. + */ + xsString getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsString val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domAuthor(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domAuthor() {} + /** + * Overloaded assignment operator + */ + virtual domAuthor &operator=( const domAuthor &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domAuthoring_tool; + + typedef daeSmartRef domAuthoring_toolRef; + typedef daeTArray domAuthoring_tool_Array; + +/** + * The authoring_tool element contains a string with the authoring tool's + * name. There may be only one authoring_tool element. + */ + class domAuthoring_tool : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::AUTHORING_TOOL; } + static daeInt ID() { return 667; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsString value of the text data of this element. + */ + xsString _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsString of the value. + */ + xsString getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsString val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domAuthoring_tool(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domAuthoring_tool() {} + /** + * Overloaded assignment operator + */ + virtual domAuthoring_tool &operator=( const domAuthoring_tool &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domComments; + + typedef daeSmartRef domCommentsRef; + typedef daeTArray domComments_Array; + +/** + * The comments element contains a string with comments from this contributor. + * There may be only one comments element. + */ + class domComments : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::COMMENTS; } + static daeInt ID() { return 668; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsString value of the text data of this element. + */ + xsString _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsString of the value. + */ + xsString getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsString val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domComments(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domComments() {} + /** + * Overloaded assignment operator + */ + virtual domComments &operator=( const domComments &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domCopyright; + + typedef daeSmartRef domCopyrightRef; + typedef daeTArray domCopyright_Array; + +/** + * The copyright element contains a string with copyright information. There + * may be only one copyright element. + */ + class domCopyright : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::COPYRIGHT; } + static daeInt ID() { return 669; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsString value of the text data of this element. + */ + xsString _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsString of the value. + */ + xsString getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsString val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domCopyright(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domCopyright() {} + /** + * Overloaded assignment operator + */ + virtual domCopyright &operator=( const domCopyright &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domSource_data; + + typedef daeSmartRef domSource_dataRef; + typedef daeTArray domSource_data_Array; + +/** + * The source_data element contains a URI reference to the source data used + * for this asset. There may be only one source_data element. + */ + class domSource_data : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SOURCE_DATA; } + static daeInt ID() { return 670; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsAnyURI value of the text data of this element. + */ + xsAnyURI _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsAnyURI of the value. + */ + xsAnyURI &getValue() { return _value; } + /** + * Gets the value of this element. + * @return Returns a constant xsAnyURI of the value. + */ + const xsAnyURI &getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( const xsAnyURI &val ) { _value = val; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsString val ) { _value = val; } + + protected: + /** + * Constructor + */ + domSource_data(DAE& dae) : daeElement(dae), _value(dae, *this) {} + /** + * Destructor + */ + virtual ~domSource_data() {} + /** + * Overloaded assignment operator + */ + virtual domSource_data &operator=( const domSource_data &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements +/** + * The author element contains a string with the author's name. There may + * be only one author element. @see domAuthor + */ + domAuthorRef elemAuthor; +/** + * The authoring_tool element contains a string with the authoring tool's + * name. There may be only one authoring_tool element. @see domAuthoring_tool + */ + domAuthoring_toolRef elemAuthoring_tool; +/** + * The comments element contains a string with comments from this contributor. + * There may be only one comments element. @see domComments + */ + domCommentsRef elemComments; +/** + * The copyright element contains a string with copyright information. There + * may be only one copyright element. @see domCopyright + */ + domCopyrightRef elemCopyright; +/** + * The source_data element contains a URI reference to the source data used + * for this asset. There may be only one source_data element. @see domSource_data + */ + domSource_dataRef elemSource_data; + + public: //Accessors and Mutators + /** + * Gets the author element. + * @return a daeSmartRef to the author element. + */ + const domAuthorRef getAuthor() const { return elemAuthor; } + /** + * Gets the authoring_tool element. + * @return a daeSmartRef to the authoring_tool element. + */ + const domAuthoring_toolRef getAuthoring_tool() const { return elemAuthoring_tool; } + /** + * Gets the comments element. + * @return a daeSmartRef to the comments element. + */ + const domCommentsRef getComments() const { return elemComments; } + /** + * Gets the copyright element. + * @return a daeSmartRef to the copyright element. + */ + const domCopyrightRef getCopyright() const { return elemCopyright; } + /** + * Gets the source_data element. + * @return a daeSmartRef to the source_data element. + */ + const domSource_dataRef getSource_data() const { return elemSource_data; } + protected: + /** + * Constructor + */ + domContributor(DAE& dae) : daeElement(dae), elemAuthor(), elemAuthoring_tool(), elemComments(), elemCopyright(), elemSource_data() {} + /** + * Destructor + */ + virtual ~domContributor() {} + /** + * Overloaded assignment operator + */ + virtual domContributor &operator=( const domContributor &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domCreated; + + typedef daeSmartRef domCreatedRef; + typedef daeTArray domCreated_Array; + +/** + * The created element contains the date and time that the parent element + * was created and is represented in an ISO 8601 format. The created element + * may appear zero or one time. + */ + class domCreated : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CREATED; } + static daeInt ID() { return 671; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsDateTime value of the text data of this element. + */ + xsDateTime _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsDateTime of the value. + */ + xsDateTime getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsDateTime val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domCreated(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domCreated() {} + /** + * Overloaded assignment operator + */ + virtual domCreated &operator=( const domCreated &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domKeywords; + + typedef daeSmartRef domKeywordsRef; + typedef daeTArray domKeywords_Array; + +/** + * The keywords element contains a list of words used as search criteria for + * the parent element. The keywords element may appear zero or more times. + */ + class domKeywords : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::KEYWORDS; } + static daeInt ID() { return 672; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsString value of the text data of this element. + */ + xsString _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsString of the value. + */ + xsString getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsString val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domKeywords(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domKeywords() {} + /** + * Overloaded assignment operator + */ + virtual domKeywords &operator=( const domKeywords &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domModified; + + typedef daeSmartRef domModifiedRef; + typedef daeTArray domModified_Array; + +/** + * The modified element contains the date and time that the parent element + * was last modified and represented in an ISO 8601 format. The modified + * element may appear zero or one time. + */ + class domModified : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MODIFIED; } + static daeInt ID() { return 673; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsDateTime value of the text data of this element. + */ + xsDateTime _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsDateTime of the value. + */ + xsDateTime getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsDateTime val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domModified(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domModified() {} + /** + * Overloaded assignment operator + */ + virtual domModified &operator=( const domModified &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domRevision; + + typedef daeSmartRef domRevisionRef; + typedef daeTArray domRevision_Array; + +/** + * The revision element contains the revision information for the parent element. + * The revision element may appear zero or one time. + */ + class domRevision : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::REVISION; } + static daeInt ID() { return 674; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsString value of the text data of this element. + */ + xsString _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsString of the value. + */ + xsString getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsString val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domRevision(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domRevision() {} + /** + * Overloaded assignment operator + */ + virtual domRevision &operator=( const domRevision &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domSubject; + + typedef daeSmartRef domSubjectRef; + typedef daeTArray domSubject_Array; + +/** + * The subject element contains a description of the topical subject of the + * parent element. The subject element may appear zero or one time. + */ + class domSubject : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SUBJECT; } + static daeInt ID() { return 675; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsString value of the text data of this element. + */ + xsString _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsString of the value. + */ + xsString getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsString val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domSubject(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domSubject() {} + /** + * Overloaded assignment operator + */ + virtual domSubject &operator=( const domSubject &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domTitle; + + typedef daeSmartRef domTitleRef; + typedef daeTArray domTitle_Array; + +/** + * The title element contains the title information for the parent element. + * The title element may appear zero or one time. + */ + class domTitle : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TITLE; } + static daeInt ID() { return 676; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsString value of the text data of this element. + */ + xsString _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsString of the value. + */ + xsString getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsString val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domTitle(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domTitle() {} + /** + * Overloaded assignment operator + */ + virtual domTitle &operator=( const domTitle &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domUnit; + + typedef daeSmartRef domUnitRef; + typedef daeTArray domUnit_Array; + +/** + * The unit element contains descriptive information about unit of measure. + * It has attributes for the name of the unit and the measurement with respect + * to the meter. The unit element may appear zero or one time. + */ + class domUnit : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::UNIT; } + static daeInt ID() { return 677; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes +/** + * The meter attribute specifies the measurement with respect to the meter. + * The default value for the meter attribute is “1.0”. + */ + domFloat attrMeter; +/** + * The name attribute specifies the name of the unit. The default value for + * the name attribute is “meter”. + */ + xsNMTOKEN attrName; + + + public: //Accessors and Mutators + /** + * Gets the meter attribute. + * @return Returns a domFloat of the meter attribute. + */ + domFloat getMeter() const { return attrMeter; } + /** + * Sets the meter attribute. + * @param atMeter The new value for the meter attribute. + */ + void setMeter( domFloat atMeter ) { attrMeter = atMeter; _validAttributeArray[0] = true; } + + /** + * Gets the name attribute. + * @return Returns a xsNMTOKEN of the name attribute. + */ + xsNMTOKEN getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNMTOKEN atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domUnit(DAE& dae) : daeElement(dae), attrMeter(), attrName() {} + /** + * Destructor + */ + virtual ~domUnit() {} + /** + * Overloaded assignment operator + */ + virtual domUnit &operator=( const domUnit &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domUp_axis; + + typedef daeSmartRef domUp_axisRef; + typedef daeTArray domUp_axis_Array; + +/** + * The up_axis element contains descriptive information about coordinate system + * of the geometric data. All coordinates are right-handed by definition. + * This element specifies which axis is considered up. The default is the + * Y-axis. The up_axis element may appear zero or one time. + */ + class domUp_axis : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::UP_AXIS; } + static daeInt ID() { return 678; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domUpAxisType value of the text data of this element. + */ + domUpAxisType _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domUpAxisType of the value. + */ + domUpAxisType getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domUpAxisType val ) { _value = val; } + + protected: + /** + * Constructor + */ + domUp_axis(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domUp_axis() {} + /** + * Overloaded assignment operator + */ + virtual domUp_axis &operator=( const domUp_axis &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements +/** + * The contributor element defines authoring information for asset management + * @see domContributor + */ + domContributor_Array elemContributor_array; +/** + * The created element contains the date and time that the parent element + * was created and is represented in an ISO 8601 format. The created element + * may appear zero or one time. @see domCreated + */ + domCreatedRef elemCreated; +/** + * The keywords element contains a list of words used as search criteria for + * the parent element. The keywords element may appear zero or more times. + * @see domKeywords + */ + domKeywordsRef elemKeywords; +/** + * The modified element contains the date and time that the parent element + * was last modified and represented in an ISO 8601 format. The modified + * element may appear zero or one time. @see domModified + */ + domModifiedRef elemModified; +/** + * The revision element contains the revision information for the parent element. + * The revision element may appear zero or one time. @see domRevision + */ + domRevisionRef elemRevision; +/** + * The subject element contains a description of the topical subject of the + * parent element. The subject element may appear zero or one time. @see + * domSubject + */ + domSubjectRef elemSubject; +/** + * The title element contains the title information for the parent element. + * The title element may appear zero or one time. @see domTitle + */ + domTitleRef elemTitle; +/** + * The unit element contains descriptive information about unit of measure. + * It has attributes for the name of the unit and the measurement with respect + * to the meter. The unit element may appear zero or one time. @see domUnit + */ + domUnitRef elemUnit; +/** + * The up_axis element contains descriptive information about coordinate system + * of the geometric data. All coordinates are right-handed by definition. + * This element specifies which axis is considered up. The default is the + * Y-axis. The up_axis element may appear zero or one time. @see domUp_axis + */ + domUp_axisRef elemUp_axis; + +public: //Accessors and Mutators + /** + * Gets the contributor element array. + * @return Returns a reference to the array of contributor elements. + */ + domContributor_Array &getContributor_array() { return elemContributor_array; } + /** + * Gets the contributor element array. + * @return Returns a constant reference to the array of contributor elements. + */ + const domContributor_Array &getContributor_array() const { return elemContributor_array; } + /** + * Gets the created element. + * @return a daeSmartRef to the created element. + */ + const domCreatedRef getCreated() const { return elemCreated; } + /** + * Gets the keywords element. + * @return a daeSmartRef to the keywords element. + */ + const domKeywordsRef getKeywords() const { return elemKeywords; } + /** + * Gets the modified element. + * @return a daeSmartRef to the modified element. + */ + const domModifiedRef getModified() const { return elemModified; } + /** + * Gets the revision element. + * @return a daeSmartRef to the revision element. + */ + const domRevisionRef getRevision() const { return elemRevision; } + /** + * Gets the subject element. + * @return a daeSmartRef to the subject element. + */ + const domSubjectRef getSubject() const { return elemSubject; } + /** + * Gets the title element. + * @return a daeSmartRef to the title element. + */ + const domTitleRef getTitle() const { return elemTitle; } + /** + * Gets the unit element. + * @return a daeSmartRef to the unit element. + */ + const domUnitRef getUnit() const { return elemUnit; } + /** + * Gets the up_axis element. + * @return a daeSmartRef to the up_axis element. + */ + const domUp_axisRef getUp_axis() const { return elemUp_axis; } +protected: + /** + * Constructor + */ + domAsset(DAE& dae) : daeElement(dae), elemContributor_array(), elemCreated(), elemKeywords(), elemModified(), elemRevision(), elemSubject(), elemTitle(), elemUnit(), elemUp_axis() {} + /** + * Destructor + */ + virtual ~domAsset() {} + /** + * Overloaded assignment operator + */ + virtual domAsset &operator=( const domAsset &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domBind_material.h b/Engine/lib/collada/include/1.4/dom/domBind_material.h new file mode 100644 index 000000000..1cc21b0be --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domBind_material.h @@ -0,0 +1,190 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domBind_material_h__ +#define __domBind_material_h__ + +#include +#include +#include + +#include +#include +#include +#include +class DAE; + +/** + * Bind a specific material to a piece of geometry, binding varying and uniform + * parameters at the same time. + */ +class domBind_material : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BIND_MATERIAL; } + static daeInt ID() { return 686; } + virtual daeInt typeID() const { return ID(); } +public: + class domTechnique_common; + + typedef daeSmartRef domTechnique_commonRef; + typedef daeTArray domTechnique_common_Array; + +/** + * The technique_common element specifies the bind_material information for + * the common profile which all COLLADA implementations need to support. + */ + class domTechnique_common : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TECHNIQUE_COMMON; } + static daeInt ID() { return 687; } + virtual daeInt typeID() const { return ID(); } + + protected: // Element +/** + * The instance_material element specifies the information needed to bind + * a geometry to a material. This element must appear at least once. @see + * domInstance_material + */ + domInstance_material_Array elemInstance_material_array; + + public: //Accessors and Mutators + /** + * Gets the instance_material element array. + * @return Returns a reference to the array of instance_material elements. + */ + domInstance_material_Array &getInstance_material_array() { return elemInstance_material_array; } + /** + * Gets the instance_material element array. + * @return Returns a constant reference to the array of instance_material elements. + */ + const domInstance_material_Array &getInstance_material_array() const { return elemInstance_material_array; } + protected: + /** + * Constructor + */ + domTechnique_common(DAE& dae) : daeElement(dae), elemInstance_material_array() {} + /** + * Destructor + */ + virtual ~domTechnique_common() {} + /** + * Overloaded assignment operator + */ + virtual domTechnique_common &operator=( const domTechnique_common &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements +/** + * The bind_material element may contain any number of param elements. @see + * domParam + */ + domParam_Array elemParam_array; +/** + * The technique_common element specifies the bind_material information for + * the common profile which all COLLADA implementations need to support. + * @see domTechnique_common + */ + domTechnique_commonRef elemTechnique_common; +/** + * This element may contain any number of non-common profile techniques. + * @see domTechnique + */ + domTechnique_Array elemTechnique_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the param element array. + * @return Returns a reference to the array of param elements. + */ + domParam_Array &getParam_array() { return elemParam_array; } + /** + * Gets the param element array. + * @return Returns a constant reference to the array of param elements. + */ + const domParam_Array &getParam_array() const { return elemParam_array; } + /** + * Gets the technique_common element. + * @return a daeSmartRef to the technique_common element. + */ + const domTechnique_commonRef getTechnique_common() const { return elemTechnique_common; } + /** + * Gets the technique element array. + * @return Returns a reference to the array of technique elements. + */ + domTechnique_Array &getTechnique_array() { return elemTechnique_array; } + /** + * Gets the technique element array. + * @return Returns a constant reference to the array of technique elements. + */ + const domTechnique_Array &getTechnique_array() const { return elemTechnique_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domBind_material(DAE& dae) : daeElement(dae), elemParam_array(), elemTechnique_common(), elemTechnique_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domBind_material() {} + /** + * Overloaded assignment operator + */ + virtual domBind_material &operator=( const domBind_material &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domBool_array.h b/Engine/lib/collada/include/1.4/dom/domBool_array.h new file mode 100644 index 000000000..5ca93f611 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domBool_array.h @@ -0,0 +1,137 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domBool_array_h__ +#define __domBool_array_h__ + +#include +#include +#include + +class DAE; + +/** + * The bool_array element declares the storage for a homogenous array of boolean + * values. + */ +class domBool_array : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL_ARRAY; } + static daeInt ID() { return 606; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; +/** + * The count attribute indicates the number of values in the array. Required + * attribute. + */ + domUint attrCount; + +protected: // Value + /** + * The domListOfBools value of the text data of this element. + */ + domListOfBools _value; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the count attribute. + * @return Returns a domUint of the count attribute. + */ + domUint getCount() const { return attrCount; } + /** + * Sets the count attribute. + * @param atCount The new value for the count attribute. + */ + void setCount( domUint atCount ) { attrCount = atCount; _validAttributeArray[2] = true; } + + /** + * Gets the _value array. + * @return Returns a domListOfBools reference of the _value array. + */ + domListOfBools &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domListOfBools reference of the _value array. + */ + const domListOfBools &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domListOfBools &val ) { _value = val; } + +protected: + /** + * Constructor + */ + domBool_array(DAE& dae) : daeElement(dae), attrId(), attrName(), attrCount(), _value() {} + /** + * Destructor + */ + virtual ~domBool_array() {} + /** + * Overloaded assignment operator + */ + virtual domBool_array &operator=( const domBool_array &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domBox.h b/Engine/lib/collada/include/1.4/dom/domBox.h new file mode 100644 index 000000000..59248b088 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domBox.h @@ -0,0 +1,157 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domBox_h__ +#define __domBox_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * An axis-aligned, centered box primitive. + */ +class domBox : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOX; } + static daeInt ID() { return 767; } + virtual daeInt typeID() const { return ID(); } +public: + class domHalf_extents; + + typedef daeSmartRef domHalf_extentsRef; + typedef daeTArray domHalf_extents_Array; + +/** + * 3 float values that represent the extents of the box + */ + class domHalf_extents : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HALF_EXTENTS; } + static daeInt ID() { return 768; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFloat3 value of the text data of this element. + */ + domFloat3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domFloat3 reference of the _value array. + */ + domFloat3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domFloat3 reference of the _value array. + */ + const domFloat3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domFloat3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHalf_extents(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domHalf_extents() {} + /** + * Overloaded assignment operator + */ + virtual domHalf_extents &operator=( const domHalf_extents &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements +/** + * 3 float values that represent the extents of the box @see domHalf_extents + */ + domHalf_extentsRef elemHalf_extents; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the half_extents element. + * @return a daeSmartRef to the half_extents element. + */ + const domHalf_extentsRef getHalf_extents() const { return elemHalf_extents; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domBox(DAE& dae) : daeElement(dae), elemHalf_extents(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domBox() {} + /** + * Overloaded assignment operator + */ + virtual domBox &operator=( const domBox &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domCOLLADA.h b/Engine/lib/collada/include/1.4/dom/domCOLLADA.h new file mode 100644 index 000000000..6cc9aa537 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domCOLLADA.h @@ -0,0 +1,527 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domCOLLADA_h__ +#define __domCOLLADA_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +class DAE; + +/** + * The COLLADA element declares the root of the document that comprises some + * of the content in the COLLADA schema. + */ +class domCOLLADA : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::COLLADA; } + static daeInt ID() { return 602; } + virtual daeInt typeID() const { return ID(); } +public: + class domScene; + + typedef daeSmartRef domSceneRef; + typedef daeTArray domScene_Array; + +/** + * The scene embodies the entire set of information that can be visualized + * from the contents of a COLLADA resource. The scene element declares the + * base of the scene hierarchy or scene graph. The scene contains elements + * that comprise much of the visual and transformational information content + * as created by the authoring tools. + */ + class domScene : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SCENE; } + static daeInt ID() { return 603; } + virtual daeInt typeID() const { return ID(); } + + protected: // Elements +/** + * The instance_physics_scene element declares the instantiation of a COLLADA + * physics_scene resource. The instance_physics_scene element may appear any + * number of times. @see domInstance_physics_scene + */ + domInstanceWithExtra_Array elemInstance_physics_scene_array; +/** + * The instance_visual_scene element declares the instantiation of a COLLADA + * visual_scene resource. The instance_visual_scene element may only appear + * once. @see domInstance_visual_scene + */ + domInstanceWithExtraRef elemInstance_visual_scene; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + + public: //Accessors and Mutators + /** + * Gets the instance_physics_scene element array. + * @return Returns a reference to the array of instance_physics_scene elements. + */ + domInstanceWithExtra_Array &getInstance_physics_scene_array() { return elemInstance_physics_scene_array; } + /** + * Gets the instance_physics_scene element array. + * @return Returns a constant reference to the array of instance_physics_scene elements. + */ + const domInstanceWithExtra_Array &getInstance_physics_scene_array() const { return elemInstance_physics_scene_array; } + /** + * Gets the instance_visual_scene element. + * @return a daeSmartRef to the instance_visual_scene element. + */ + const domInstanceWithExtraRef getInstance_visual_scene() const { return elemInstance_visual_scene; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + protected: + /** + * Constructor + */ + domScene(DAE& dae) : daeElement(dae), elemInstance_physics_scene_array(), elemInstance_visual_scene(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domScene() {} + /** + * Overloaded assignment operator + */ + virtual domScene &operator=( const domScene &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attributes + /** + * This element may specify its own xmlns. + */ + xsAnyURI attrXmlns; +/** + * The version attribute is the COLLADA schema revision with which the instance + * document conforms. Required Attribute. + */ + domVersionType attrVersion; +/** + * The xml:base attribute allows you to define the base URI for this COLLADA + * document. See http://www.w3.org/TR/xmlbase/ for more information. + */ + xsAnyURI attrXml_base; + +protected: // Elements +/** + * The COLLADA element must contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * The COLLADA element may contain any number of library_animations elements. + * @see domLibrary_animations + */ + domLibrary_animations_Array elemLibrary_animations_array; +/** + * The COLLADA element may contain any number of library_animation_clips + * elements. @see domLibrary_animation_clips + */ + domLibrary_animation_clips_Array elemLibrary_animation_clips_array; +/** + * The COLLADA element may contain any number of library_cameras elements. + * @see domLibrary_cameras + */ + domLibrary_cameras_Array elemLibrary_cameras_array; +/** + * The COLLADA element may contain any number of library_controllerss elements. + * @see domLibrary_controllers + */ + domLibrary_controllers_Array elemLibrary_controllers_array; +/** + * The COLLADA element may contain any number of library_geometriess elements. + * @see domLibrary_geometries + */ + domLibrary_geometries_Array elemLibrary_geometries_array; +/** + * The COLLADA element may contain any number of library_effects elements. + * @see domLibrary_effects + */ + domLibrary_effects_Array elemLibrary_effects_array; +/** + * The COLLADA element may contain any number of library_force_fields elements. + * @see domLibrary_force_fields + */ + domLibrary_force_fields_Array elemLibrary_force_fields_array; +/** + * The COLLADA element may contain any number of library_images elements. + * @see domLibrary_images + */ + domLibrary_images_Array elemLibrary_images_array; +/** + * The COLLADA element may contain any number of library_lights elements. + * @see domLibrary_lights + */ + domLibrary_lights_Array elemLibrary_lights_array; +/** + * The COLLADA element may contain any number of library_materials elements. + * @see domLibrary_materials + */ + domLibrary_materials_Array elemLibrary_materials_array; +/** + * The COLLADA element may contain any number of library_nodes elements. + * @see domLibrary_nodes + */ + domLibrary_nodes_Array elemLibrary_nodes_array; +/** + * The COLLADA element may contain any number of library_materials elements. + * @see domLibrary_physics_materials + */ + domLibrary_physics_materials_Array elemLibrary_physics_materials_array; +/** + * The COLLADA element may contain any number of library_physics_models elements. + * @see domLibrary_physics_models + */ + domLibrary_physics_models_Array elemLibrary_physics_models_array; +/** + * The COLLADA element may contain any number of library_physics_scenes elements. + * @see domLibrary_physics_scenes + */ + domLibrary_physics_scenes_Array elemLibrary_physics_scenes_array; +/** + * The COLLADA element may contain any number of library_visual_scenes elements. + * @see domLibrary_visual_scenes + */ + domLibrary_visual_scenes_Array elemLibrary_visual_scenes_array; +/** + * The scene embodies the entire set of information that can be visualized + * from the contents of a COLLADA resource. The scene element declares the + * base of the scene hierarchy or scene graph. The scene contains elements + * that comprise much of the visual and transformational information content + * as created by the authoring tools. @see domScene + */ + domSceneRef elemScene; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the xmlns attribute. + * @return Returns a xsAnyURI reference of the xmlns attribute. + */ + xsAnyURI &getXmlns() { return attrXmlns; } + /** + * Gets the xmlns attribute. + * @return Returns a constant xsAnyURI reference of the xmlns attribute. + */ + const xsAnyURI &getXmlns() const { return attrXmlns; } + /** + * Sets the xmlns attribute. + * @param xmlns The new value for the xmlns attribute. + */ + void setXmlns( const xsAnyURI &xmlns ) { attrXmlns = xmlns; + _validAttributeArray[0] = true; } + + /** + * Gets the version attribute. + * @return Returns a domVersionType of the version attribute. + */ + domVersionType getVersion() const { return attrVersion; } + /** + * Sets the version attribute. + * @param atVersion The new value for the version attribute. + */ + void setVersion( domVersionType atVersion ) { attrVersion = atVersion; _validAttributeArray[1] = true; } + + /** + * Gets the xml_base attribute. + * @return Returns a xsAnyURI reference of the xml_base attribute. + */ + xsAnyURI &getXml_base() { return attrXml_base; } + /** + * Gets the xml_base attribute. + * @return Returns a constant xsAnyURI reference of the xml_base attribute. + */ + const xsAnyURI &getXml_base() const { return attrXml_base; } + /** + * Sets the xml_base attribute. + * @param atXml_base The new value for the xml_base attribute. + */ + void setXml_base( const xsAnyURI &atXml_base ) { attrXml_base = atXml_base; _validAttributeArray[2] = true; } + /** + * Sets the xml_base attribute. + * @param atXml_base The new value for the xml_base attribute. + */ + void setXml_base( xsString atXml_base ) { attrXml_base = atXml_base; _validAttributeArray[2] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the library_animations element array. + * @return Returns a reference to the array of library_animations elements. + */ + domLibrary_animations_Array &getLibrary_animations_array() { return elemLibrary_animations_array; } + /** + * Gets the library_animations element array. + * @return Returns a constant reference to the array of library_animations elements. + */ + const domLibrary_animations_Array &getLibrary_animations_array() const { return elemLibrary_animations_array; } + /** + * Gets the library_animation_clips element array. + * @return Returns a reference to the array of library_animation_clips elements. + */ + domLibrary_animation_clips_Array &getLibrary_animation_clips_array() { return elemLibrary_animation_clips_array; } + /** + * Gets the library_animation_clips element array. + * @return Returns a constant reference to the array of library_animation_clips elements. + */ + const domLibrary_animation_clips_Array &getLibrary_animation_clips_array() const { return elemLibrary_animation_clips_array; } + /** + * Gets the library_cameras element array. + * @return Returns a reference to the array of library_cameras elements. + */ + domLibrary_cameras_Array &getLibrary_cameras_array() { return elemLibrary_cameras_array; } + /** + * Gets the library_cameras element array. + * @return Returns a constant reference to the array of library_cameras elements. + */ + const domLibrary_cameras_Array &getLibrary_cameras_array() const { return elemLibrary_cameras_array; } + /** + * Gets the library_controllers element array. + * @return Returns a reference to the array of library_controllers elements. + */ + domLibrary_controllers_Array &getLibrary_controllers_array() { return elemLibrary_controllers_array; } + /** + * Gets the library_controllers element array. + * @return Returns a constant reference to the array of library_controllers elements. + */ + const domLibrary_controllers_Array &getLibrary_controllers_array() const { return elemLibrary_controllers_array; } + /** + * Gets the library_geometries element array. + * @return Returns a reference to the array of library_geometries elements. + */ + domLibrary_geometries_Array &getLibrary_geometries_array() { return elemLibrary_geometries_array; } + /** + * Gets the library_geometries element array. + * @return Returns a constant reference to the array of library_geometries elements. + */ + const domLibrary_geometries_Array &getLibrary_geometries_array() const { return elemLibrary_geometries_array; } + /** + * Gets the library_effects element array. + * @return Returns a reference to the array of library_effects elements. + */ + domLibrary_effects_Array &getLibrary_effects_array() { return elemLibrary_effects_array; } + /** + * Gets the library_effects element array. + * @return Returns a constant reference to the array of library_effects elements. + */ + const domLibrary_effects_Array &getLibrary_effects_array() const { return elemLibrary_effects_array; } + /** + * Gets the library_force_fields element array. + * @return Returns a reference to the array of library_force_fields elements. + */ + domLibrary_force_fields_Array &getLibrary_force_fields_array() { return elemLibrary_force_fields_array; } + /** + * Gets the library_force_fields element array. + * @return Returns a constant reference to the array of library_force_fields elements. + */ + const domLibrary_force_fields_Array &getLibrary_force_fields_array() const { return elemLibrary_force_fields_array; } + /** + * Gets the library_images element array. + * @return Returns a reference to the array of library_images elements. + */ + domLibrary_images_Array &getLibrary_images_array() { return elemLibrary_images_array; } + /** + * Gets the library_images element array. + * @return Returns a constant reference to the array of library_images elements. + */ + const domLibrary_images_Array &getLibrary_images_array() const { return elemLibrary_images_array; } + /** + * Gets the library_lights element array. + * @return Returns a reference to the array of library_lights elements. + */ + domLibrary_lights_Array &getLibrary_lights_array() { return elemLibrary_lights_array; } + /** + * Gets the library_lights element array. + * @return Returns a constant reference to the array of library_lights elements. + */ + const domLibrary_lights_Array &getLibrary_lights_array() const { return elemLibrary_lights_array; } + /** + * Gets the library_materials element array. + * @return Returns a reference to the array of library_materials elements. + */ + domLibrary_materials_Array &getLibrary_materials_array() { return elemLibrary_materials_array; } + /** + * Gets the library_materials element array. + * @return Returns a constant reference to the array of library_materials elements. + */ + const domLibrary_materials_Array &getLibrary_materials_array() const { return elemLibrary_materials_array; } + /** + * Gets the library_nodes element array. + * @return Returns a reference to the array of library_nodes elements. + */ + domLibrary_nodes_Array &getLibrary_nodes_array() { return elemLibrary_nodes_array; } + /** + * Gets the library_nodes element array. + * @return Returns a constant reference to the array of library_nodes elements. + */ + const domLibrary_nodes_Array &getLibrary_nodes_array() const { return elemLibrary_nodes_array; } + /** + * Gets the library_physics_materials element array. + * @return Returns a reference to the array of library_physics_materials elements. + */ + domLibrary_physics_materials_Array &getLibrary_physics_materials_array() { return elemLibrary_physics_materials_array; } + /** + * Gets the library_physics_materials element array. + * @return Returns a constant reference to the array of library_physics_materials elements. + */ + const domLibrary_physics_materials_Array &getLibrary_physics_materials_array() const { return elemLibrary_physics_materials_array; } + /** + * Gets the library_physics_models element array. + * @return Returns a reference to the array of library_physics_models elements. + */ + domLibrary_physics_models_Array &getLibrary_physics_models_array() { return elemLibrary_physics_models_array; } + /** + * Gets the library_physics_models element array. + * @return Returns a constant reference to the array of library_physics_models elements. + */ + const domLibrary_physics_models_Array &getLibrary_physics_models_array() const { return elemLibrary_physics_models_array; } + /** + * Gets the library_physics_scenes element array. + * @return Returns a reference to the array of library_physics_scenes elements. + */ + domLibrary_physics_scenes_Array &getLibrary_physics_scenes_array() { return elemLibrary_physics_scenes_array; } + /** + * Gets the library_physics_scenes element array. + * @return Returns a constant reference to the array of library_physics_scenes elements. + */ + const domLibrary_physics_scenes_Array &getLibrary_physics_scenes_array() const { return elemLibrary_physics_scenes_array; } + /** + * Gets the library_visual_scenes element array. + * @return Returns a reference to the array of library_visual_scenes elements. + */ + domLibrary_visual_scenes_Array &getLibrary_visual_scenes_array() { return elemLibrary_visual_scenes_array; } + /** + * Gets the library_visual_scenes element array. + * @return Returns a constant reference to the array of library_visual_scenes elements. + */ + const domLibrary_visual_scenes_Array &getLibrary_visual_scenes_array() const { return elemLibrary_visual_scenes_array; } + /** + * Gets the scene element. + * @return a daeSmartRef to the scene element. + */ + const domSceneRef getScene() const { return elemScene; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domCOLLADA(DAE& dae) : daeElement(dae), attrXmlns(dae, *this), attrVersion(), attrXml_base(dae, *this), elemAsset(), elemLibrary_animations_array(), elemLibrary_animation_clips_array(), elemLibrary_cameras_array(), elemLibrary_controllers_array(), elemLibrary_geometries_array(), elemLibrary_effects_array(), elemLibrary_force_fields_array(), elemLibrary_images_array(), elemLibrary_lights_array(), elemLibrary_materials_array(), elemLibrary_nodes_array(), elemLibrary_physics_materials_array(), elemLibrary_physics_models_array(), elemLibrary_physics_scenes_array(), elemLibrary_visual_scenes_array(), elemScene(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domCOLLADA() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domCOLLADA &operator=( const domCOLLADA &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domCamera.h b/Engine/lib/collada/include/1.4/dom/domCamera.h new file mode 100644 index 000000000..9474158d5 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domCamera.h @@ -0,0 +1,658 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domCamera_h__ +#define __domCamera_h__ + +#include +#include +#include + +#include +#include +#include +#include +class DAE; + +/** + * The camera element declares a view into the scene hierarchy or scene graph. + * The camera contains elements that describe the camera’s optics and imager. + */ +class domCamera : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CAMERA; } + static daeInt ID() { return 645; } + virtual daeInt typeID() const { return ID(); } +public: + class domOptics; + + typedef daeSmartRef domOpticsRef; + typedef daeTArray domOptics_Array; + +/** + * Optics represents the apparatus on a camera that projects the image onto + * the image sensor. + */ + class domOptics : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::OPTICS; } + static daeInt ID() { return 646; } + virtual daeInt typeID() const { return ID(); } + public: + class domTechnique_common; + + typedef daeSmartRef domTechnique_commonRef; + typedef daeTArray domTechnique_common_Array; + +/** + * The technique_common element specifies the optics information for the common + * profile which all COLLADA implementations need to support. + */ + class domTechnique_common : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TECHNIQUE_COMMON; } + static daeInt ID() { return 647; } + virtual daeInt typeID() const { return ID(); } + public: + class domOrthographic; + + typedef daeSmartRef domOrthographicRef; + typedef daeTArray domOrthographic_Array; + +/** + * The orthographic element describes the field of view of an orthographic + * camera. + */ + class domOrthographic : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ORTHOGRAPHIC; } + static daeInt ID() { return 648; } + virtual daeInt typeID() const { return ID(); } + + protected: // Elements +/** + * The xmag element contains a floating point number describing the horizontal + * magnification of the view. @see domXmag + */ + domTargetableFloatRef elemXmag; +/** + * The ymag element contains a floating point number describing the vertical + * magnification of the view. It can also have a sid. @see domYmag + */ + domTargetableFloatRef elemYmag; +/** + * The aspect_ratio element contains a floating point number describing the + * aspect ratio of the field of view. If the aspect_ratio element is not + * present the aspect ratio is to be calculated from the xmag or ymag elements + * and the current viewport. @see domAspect_ratio + */ + domTargetableFloatRef elemAspect_ratio; +/** + * The znear element contains a floating point number that describes the distance + * to the near clipping plane. The znear element must occur exactly once. + * @see domZnear + */ + domTargetableFloatRef elemZnear; +/** + * The zfar element contains a floating point number that describes the distance + * to the far clipping plane. The zfar element must occur exactly once. @see + * domZfar + */ + domTargetableFloatRef elemZfar; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the xmag element. + * @return a daeSmartRef to the xmag element. + */ + const domTargetableFloatRef getXmag() const { return elemXmag; } + /** + * Gets the ymag element. + * @return a daeSmartRef to the ymag element. + */ + const domTargetableFloatRef getYmag() const { return elemYmag; } + /** + * Gets the aspect_ratio element. + * @return a daeSmartRef to the aspect_ratio element. + */ + const domTargetableFloatRef getAspect_ratio() const { return elemAspect_ratio; } + /** + * Gets the znear element. + * @return a daeSmartRef to the znear element. + */ + const domTargetableFloatRef getZnear() const { return elemZnear; } + /** + * Gets the zfar element. + * @return a daeSmartRef to the zfar element. + */ + const domTargetableFloatRef getZfar() const { return elemZfar; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domOrthographic(DAE& dae) : daeElement(dae), elemXmag(), elemYmag(), elemAspect_ratio(), elemZnear(), elemZfar() {} + /** + * Destructor + */ + virtual ~domOrthographic() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domOrthographic &operator=( const domOrthographic &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPerspective; + + typedef daeSmartRef domPerspectiveRef; + typedef daeTArray domPerspective_Array; + +/** + * The perspective element describes the optics of a perspective camera. + */ + class domPerspective : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PERSPECTIVE; } + static daeInt ID() { return 649; } + virtual daeInt typeID() const { return ID(); } + + protected: // Elements +/** + * The xfov element contains a floating point number describing the horizontal + * field of view in degrees. @see domXfov + */ + domTargetableFloatRef elemXfov; +/** + * The yfov element contains a floating point number describing the verticle + * field of view in degrees. @see domYfov + */ + domTargetableFloatRef elemYfov; +/** + * The aspect_ratio element contains a floating point number describing the + * aspect ratio of the field of view. If the aspect_ratio element is not + * present the aspect ratio is to be calculated from the xfov or yfov elements + * and the current viewport. @see domAspect_ratio + */ + domTargetableFloatRef elemAspect_ratio; +/** + * The znear element contains a floating point number that describes the distance + * to the near clipping plane. The znear element must occur exactly once. + * @see domZnear + */ + domTargetableFloatRef elemZnear; +/** + * The zfar element contains a floating point number that describes the distance + * to the far clipping plane. The zfar element must occur exactly once. @see + * domZfar + */ + domTargetableFloatRef elemZfar; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the xfov element. + * @return a daeSmartRef to the xfov element. + */ + const domTargetableFloatRef getXfov() const { return elemXfov; } + /** + * Gets the yfov element. + * @return a daeSmartRef to the yfov element. + */ + const domTargetableFloatRef getYfov() const { return elemYfov; } + /** + * Gets the aspect_ratio element. + * @return a daeSmartRef to the aspect_ratio element. + */ + const domTargetableFloatRef getAspect_ratio() const { return elemAspect_ratio; } + /** + * Gets the znear element. + * @return a daeSmartRef to the znear element. + */ + const domTargetableFloatRef getZnear() const { return elemZnear; } + /** + * Gets the zfar element. + * @return a daeSmartRef to the zfar element. + */ + const domTargetableFloatRef getZfar() const { return elemZfar; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domPerspective(DAE& dae) : daeElement(dae), elemXfov(), elemYfov(), elemAspect_ratio(), elemZnear(), elemZfar() {} + /** + * Destructor + */ + virtual ~domPerspective() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domPerspective &operator=( const domPerspective &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements +/** + * The orthographic element describes the field of view of an orthographic + * camera. @see domOrthographic + */ + domOrthographicRef elemOrthographic; +/** + * The perspective element describes the optics of a perspective camera. @see + * domPerspective + */ + domPerspectiveRef elemPerspective; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the orthographic element. + * @return a daeSmartRef to the orthographic element. + */ + const domOrthographicRef getOrthographic() const { return elemOrthographic; } + /** + * Gets the perspective element. + * @return a daeSmartRef to the perspective element. + */ + const domPerspectiveRef getPerspective() const { return elemPerspective; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domTechnique_common(DAE& dae) : daeElement(dae), elemOrthographic(), elemPerspective() {} + /** + * Destructor + */ + virtual ~domTechnique_common() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domTechnique_common &operator=( const domTechnique_common &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements +/** + * The technique_common element specifies the optics information for the common + * profile which all COLLADA implementations need to support. @see domTechnique_common + */ + domTechnique_commonRef elemTechnique_common; +/** + * This element may contain any number of non-common profile techniques. + * @see domTechnique + */ + domTechnique_Array elemTechnique_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + + public: //Accessors and Mutators + /** + * Gets the technique_common element. + * @return a daeSmartRef to the technique_common element. + */ + const domTechnique_commonRef getTechnique_common() const { return elemTechnique_common; } + /** + * Gets the technique element array. + * @return Returns a reference to the array of technique elements. + */ + domTechnique_Array &getTechnique_array() { return elemTechnique_array; } + /** + * Gets the technique element array. + * @return Returns a constant reference to the array of technique elements. + */ + const domTechnique_Array &getTechnique_array() const { return elemTechnique_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + protected: + /** + * Constructor + */ + domOptics(DAE& dae) : daeElement(dae), elemTechnique_common(), elemTechnique_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domOptics() {} + /** + * Overloaded assignment operator + */ + virtual domOptics &operator=( const domOptics &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domImager; + + typedef daeSmartRef domImagerRef; + typedef daeTArray domImager_Array; + +/** + * Imagers represent the image sensor of a camera (for example film or CCD). + */ + class domImager : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::IMAGER; } + static daeInt ID() { return 650; } + virtual daeInt typeID() const { return ID(); } + + protected: // Elements +/** + * This element may contain any number of non-common profile techniques. + * There is no common technique for imager. @see domTechnique + */ + domTechnique_Array elemTechnique_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + + public: //Accessors and Mutators + /** + * Gets the technique element array. + * @return Returns a reference to the array of technique elements. + */ + domTechnique_Array &getTechnique_array() { return elemTechnique_array; } + /** + * Gets the technique element array. + * @return Returns a constant reference to the array of technique elements. + */ + const domTechnique_Array &getTechnique_array() const { return elemTechnique_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + protected: + /** + * Constructor + */ + domImager(DAE& dae) : daeElement(dae), elemTechnique_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domImager() {} + /** + * Overloaded assignment operator + */ + virtual domImager &operator=( const domImager &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The camera element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * Optics represents the apparatus on a camera that projects the image onto + * the image sensor. @see domOptics + */ + domOpticsRef elemOptics; +/** + * Imagers represent the image sensor of a camera (for example film or CCD). + * @see domImager + */ + domImagerRef elemImager; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the optics element. + * @return a daeSmartRef to the optics element. + */ + const domOpticsRef getOptics() const { return elemOptics; } + /** + * Gets the imager element. + * @return a daeSmartRef to the imager element. + */ + const domImagerRef getImager() const { return elemImager; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domCamera(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemOptics(), elemImager(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domCamera() {} + /** + * Overloaded assignment operator + */ + virtual domCamera &operator=( const domCamera &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domCapsule.h b/Engine/lib/collada/include/1.4/dom/domCapsule.h new file mode 100644 index 000000000..87ac3f8c0 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domCapsule.h @@ -0,0 +1,230 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domCapsule_h__ +#define __domCapsule_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * A capsule primitive that is centered on and aligned with the local Y axis. + */ +class domCapsule : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CAPSULE; } + static daeInt ID() { return 782; } + virtual daeInt typeID() const { return ID(); } +public: + class domHeight; + + typedef daeSmartRef domHeightRef; + typedef daeTArray domHeight_Array; + +/** + * A float value that represents the length of the line segment connecting + * the centers of the capping hemispheres. + */ + class domHeight : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HEIGHT; } + static daeInt ID() { return 783; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFloat value of the text data of this element. + */ + domFloat _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFloat of the value. + */ + domFloat getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFloat val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHeight(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domHeight() {} + /** + * Overloaded assignment operator + */ + virtual domHeight &operator=( const domHeight &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domRadius; + + typedef daeSmartRef domRadiusRef; + typedef daeTArray domRadius_Array; + +/** + * Two float values that represent the radii of the capsule (it may be elliptical) + */ + class domRadius : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::RADIUS; } + static daeInt ID() { return 784; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFloat2 value of the text data of this element. + */ + domFloat2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domFloat2 reference of the _value array. + */ + domFloat2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domFloat2 reference of the _value array. + */ + const domFloat2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domFloat2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domRadius(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domRadius() {} + /** + * Overloaded assignment operator + */ + virtual domRadius &operator=( const domRadius &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements +/** + * A float value that represents the length of the line segment connecting + * the centers of the capping hemispheres. @see domHeight + */ + domHeightRef elemHeight; +/** + * Two float values that represent the radii of the capsule (it may be elliptical) + * @see domRadius + */ + domRadiusRef elemRadius; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the height element. + * @return a daeSmartRef to the height element. + */ + const domHeightRef getHeight() const { return elemHeight; } + /** + * Gets the radius element. + * @return a daeSmartRef to the radius element. + */ + const domRadiusRef getRadius() const { return elemRadius; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domCapsule(DAE& dae) : daeElement(dae), elemHeight(), elemRadius(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domCapsule() {} + /** + * Overloaded assignment operator + */ + virtual domCapsule &operator=( const domCapsule &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domCg_connect_param.h b/Engine/lib/collada/include/1.4/dom/domCg_connect_param.h new file mode 100644 index 000000000..315eca391 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domCg_connect_param.h @@ -0,0 +1,110 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domCg_connect_param_h__ +#define __domCg_connect_param_h__ + +#include +#include +#include + +class DAE; + +/** + * Creates a symbolic connection between two previously defined parameters. + */ +class domCg_connect_param_complexType +{ +protected: // Attribute + domCg_identifier attrRef; + + +public: //Accessors and Mutators + /** + * Gets the ref attribute. + * @return Returns a domCg_identifier of the ref attribute. + */ + domCg_identifier getRef() const { return attrRef; } + /** + * Sets the ref attribute. + * @param atRef The new value for the ref attribute. + */ + void setRef( domCg_identifier atRef ) { attrRef = atRef; } + +protected: + /** + * Constructor + */ + domCg_connect_param_complexType(DAE& dae, daeElement* elt) : attrRef() {} + /** + * Destructor + */ + virtual ~domCg_connect_param_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domCg_connect_param_complexType &operator=( const domCg_connect_param_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domCg_connect_param_complexType. + */ +class domCg_connect_param : public daeElement, public domCg_connect_param_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CG_CONNECT_PARAM; } + static daeInt ID() { return 133; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the ref attribute. + * @return Returns a domCg_identifier of the ref attribute. + */ + domCg_identifier getRef() const { return attrRef; } + /** + * Sets the ref attribute. + * @param atRef The new value for the ref attribute. + */ + void setRef( domCg_identifier atRef ) { attrRef = atRef; _validAttributeArray[0] = true; } + +protected: + /** + * Constructor + */ + domCg_connect_param(DAE& dae) : daeElement(dae), domCg_connect_param_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domCg_connect_param() {} + /** + * Overloaded assignment operator + */ + virtual domCg_connect_param &operator=( const domCg_connect_param &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domCg_newarray_type.h b/Engine/lib/collada/include/1.4/dom/domCg_newarray_type.h new file mode 100644 index 000000000..450c4e9a1 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domCg_newarray_type.h @@ -0,0 +1,194 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domCg_newarray_type_h__ +#define __domCg_newarray_type_h__ + +#include +#include +#include + +#include +#include +#include +#include +class DAE; + +/** + * Creates a parameter of a one-dimensional array type. + */ +class domCg_newarray_type_complexType +{ +protected: // Attribute +/** + * The length attribute specifies the length of the array. + */ + xsPositiveInteger attrLength; + +protected: // Elements + domCg_param_type_Array elemCg_param_type_array; +/** + * Nested array elements allow you to create multidemensional arrays. @see + * domArray + */ + domCg_newarray_type_Array elemArray_array; +/** + * The usertype element allows you to create arrays of usertypes. @see domUsertype + */ + domCg_setuser_type_Array elemUsertype_array; + domCg_connect_param_Array elemConnect_param_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the length attribute. + * @return Returns a xsPositiveInteger of the length attribute. + */ + xsPositiveInteger getLength() const { return attrLength; } + /** + * Sets the length attribute. + * @param atLength The new value for the length attribute. + */ + void setLength( xsPositiveInteger atLength ) { attrLength = atLength; } + + /** + * Gets the cg_param_type element array. + * @return Returns a reference to the array of cg_param_type elements. + */ + domCg_param_type_Array &getCg_param_type_array() { return elemCg_param_type_array; } + /** + * Gets the cg_param_type element array. + * @return Returns a constant reference to the array of cg_param_type elements. + */ + const domCg_param_type_Array &getCg_param_type_array() const { return elemCg_param_type_array; } + /** + * Gets the array element array. + * @return Returns a reference to the array of array elements. + */ + domCg_newarray_type_Array &getArray_array() { return elemArray_array; } + /** + * Gets the array element array. + * @return Returns a constant reference to the array of array elements. + */ + const domCg_newarray_type_Array &getArray_array() const { return elemArray_array; } + /** + * Gets the usertype element array. + * @return Returns a reference to the array of usertype elements. + */ + domCg_setuser_type_Array &getUsertype_array() { return elemUsertype_array; } + /** + * Gets the usertype element array. + * @return Returns a constant reference to the array of usertype elements. + */ + const domCg_setuser_type_Array &getUsertype_array() const { return elemUsertype_array; } + /** + * Gets the connect_param element array. + * @return Returns a reference to the array of connect_param elements. + */ + domCg_connect_param_Array &getConnect_param_array() { return elemConnect_param_array; } + /** + * Gets the connect_param element array. + * @return Returns a constant reference to the array of connect_param elements. + */ + const domCg_connect_param_Array &getConnect_param_array() const { return elemConnect_param_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domCg_newarray_type_complexType(DAE& dae, daeElement* elt) : attrLength(), elemCg_param_type_array(), elemArray_array(), elemUsertype_array(), elemConnect_param_array() {} + /** + * Destructor + */ + virtual ~domCg_newarray_type_complexType() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domCg_newarray_type_complexType &operator=( const domCg_newarray_type_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domCg_newarray_type_complexType. + */ +class domCg_newarray_type : public daeElement, public domCg_newarray_type_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CG_NEWARRAY_TYPE; } + static daeInt ID() { return 134; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the length attribute. + * @return Returns a xsPositiveInteger of the length attribute. + */ + xsPositiveInteger getLength() const { return attrLength; } + /** + * Sets the length attribute. + * @param atLength The new value for the length attribute. + */ + void setLength( xsPositiveInteger atLength ) { attrLength = atLength; _validAttributeArray[0] = true; } + +protected: + /** + * Constructor + */ + domCg_newarray_type(DAE& dae) : daeElement(dae), domCg_newarray_type_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domCg_newarray_type() {} + /** + * Overloaded assignment operator + */ + virtual domCg_newarray_type &operator=( const domCg_newarray_type &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domCg_newparam.h b/Engine/lib/collada/include/1.4/dom/domCg_newparam.h new file mode 100644 index 000000000..90d2337f9 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domCg_newparam.h @@ -0,0 +1,318 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domCg_newparam_h__ +#define __domCg_newparam_h__ + +#include +#include +#include + +#include +#include +#include +#include +class DAE; + +/** + * Create a new, named param object in the CG Runtime, assign it a type, an + * initial value, and additional attributes at declaration time. + */ +class domCg_newparam_complexType +{ +public: + class domSemantic; + + typedef daeSmartRef domSemanticRef; + typedef daeTArray domSemantic_Array; + +/** + * The semantic element allows you to specify a semantic for this new param. + */ + class domSemantic : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SEMANTIC; } + static daeInt ID() { return 140; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsNCName value of the text data of this element. + */ + xsNCName _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsNCName of the value. + */ + xsNCName getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNCName val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domSemantic(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domSemantic() {} + /** + * Overloaded assignment operator + */ + virtual domSemantic &operator=( const domSemantic &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domModifier; + + typedef daeSmartRef domModifierRef; + typedef daeTArray domModifier_Array; + +/** + * The modifier element allows you to specify a modifier for this new param. + */ + class domModifier : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MODIFIER; } + static daeInt ID() { return 141; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_modifier_enum_common value of the text data of this element. + */ + domFx_modifier_enum_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_modifier_enum_common of the value. + */ + domFx_modifier_enum_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_modifier_enum_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domModifier(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domModifier() {} + /** + * Overloaded assignment operator + */ + virtual domModifier &operator=( const domModifier &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attribute + domCg_identifier attrSid; + +protected: // Elements +/** + * The annotate element allows you to specify an annotation for this new param. + * @see domAnnotate + */ + domFx_annotate_common_Array elemAnnotate_array; +/** + * The semantic element allows you to specify a semantic for this new param. + * @see domSemantic + */ + domSemanticRef elemSemantic; +/** + * The modifier element allows you to specify a modifier for this new param. + * @see domModifier + */ + domModifierRef elemModifier; + domCg_param_typeRef elemCg_param_type; + domCg_setuser_typeRef elemUsertype; + domCg_newarray_typeRef elemArray; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a domCg_identifier of the sid attribute. + */ + domCg_identifier getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( domCg_identifier atSid ) { attrSid = atSid; } + + /** + * Gets the annotate element array. + * @return Returns a reference to the array of annotate elements. + */ + domFx_annotate_common_Array &getAnnotate_array() { return elemAnnotate_array; } + /** + * Gets the annotate element array. + * @return Returns a constant reference to the array of annotate elements. + */ + const domFx_annotate_common_Array &getAnnotate_array() const { return elemAnnotate_array; } + /** + * Gets the semantic element. + * @return a daeSmartRef to the semantic element. + */ + const domSemanticRef getSemantic() const { return elemSemantic; } + /** + * Gets the modifier element. + * @return a daeSmartRef to the modifier element. + */ + const domModifierRef getModifier() const { return elemModifier; } + /** + * Gets the cg_param_type element. + * @return a daeSmartRef to the cg_param_type element. + */ + const domCg_param_typeRef getCg_param_type() const { return elemCg_param_type; } + /** + * Gets the usertype element. + * @return a daeSmartRef to the usertype element. + */ + const domCg_setuser_typeRef getUsertype() const { return elemUsertype; } + /** + * Gets the array element. + * @return a daeSmartRef to the array element. + */ + const domCg_newarray_typeRef getArray() const { return elemArray; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domCg_newparam_complexType(DAE& dae, daeElement* elt) : attrSid(), elemAnnotate_array(), elemSemantic(), elemModifier(), elemCg_param_type(), elemUsertype(), elemArray() {} + /** + * Destructor + */ + virtual ~domCg_newparam_complexType() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domCg_newparam_complexType &operator=( const domCg_newparam_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domCg_newparam_complexType. + */ +class domCg_newparam : public daeElement, public domCg_newparam_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CG_NEWPARAM; } + static daeInt ID() { return 142; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a domCg_identifier of the sid attribute. + */ + domCg_identifier getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( domCg_identifier atSid ) { attrSid = atSid; _validAttributeArray[0] = true; } + +protected: + /** + * Constructor + */ + domCg_newparam(DAE& dae) : daeElement(dae), domCg_newparam_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domCg_newparam() {} + /** + * Overloaded assignment operator + */ + virtual domCg_newparam &operator=( const domCg_newparam &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domCg_param_type.h b/Engine/lib/collada/include/1.4/dom/domCg_param_type.h new file mode 100644 index 000000000..3687c7ce5 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domCg_param_type.h @@ -0,0 +1,7464 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domCg_param_type_h__ +#define __domCg_param_type_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +class DAE; + +/** + * A group that specifies the allowable types for CG profile parameters. + */ +class domCg_param_type : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CG_PARAM_TYPE; } + static daeInt ID() { return 380; } + virtual daeInt typeID() const { return ID(); } +public: + class domBool; + + typedef daeSmartRef domBoolRef; + typedef daeTArray domBool_Array; + + class domBool : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL; } + static daeInt ID() { return 381; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_bool value of the text data of this element. + */ + domCg_bool _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domCg_bool of the value. + */ + domCg_bool getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domCg_bool val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool() {} + /** + * Overloaded assignment operator + */ + virtual domBool &operator=( const domBool &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool1; + + typedef daeSmartRef domBool1Ref; + typedef daeTArray domBool1_Array; + + class domBool1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL1; } + static daeInt ID() { return 382; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_bool1 value of the text data of this element. + */ + domCg_bool1 _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domCg_bool1 of the value. + */ + domCg_bool1 getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domCg_bool1 val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool1() {} + /** + * Overloaded assignment operator + */ + virtual domBool1 &operator=( const domBool1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool2; + + typedef daeSmartRef domBool2Ref; + typedef daeTArray domBool2_Array; + + class domBool2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL2; } + static daeInt ID() { return 383; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_bool2 value of the text data of this element. + */ + domCg_bool2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_bool2 reference of the _value array. + */ + domCg_bool2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_bool2 reference of the _value array. + */ + const domCg_bool2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_bool2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool2() {} + /** + * Overloaded assignment operator + */ + virtual domBool2 &operator=( const domBool2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool3; + + typedef daeSmartRef domBool3Ref; + typedef daeTArray domBool3_Array; + + class domBool3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL3; } + static daeInt ID() { return 384; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_bool3 value of the text data of this element. + */ + domCg_bool3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_bool3 reference of the _value array. + */ + domCg_bool3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_bool3 reference of the _value array. + */ + const domCg_bool3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_bool3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool3() {} + /** + * Overloaded assignment operator + */ + virtual domBool3 &operator=( const domBool3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool4; + + typedef daeSmartRef domBool4Ref; + typedef daeTArray domBool4_Array; + + class domBool4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL4; } + static daeInt ID() { return 385; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_bool4 value of the text data of this element. + */ + domCg_bool4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_bool4 reference of the _value array. + */ + domCg_bool4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_bool4 reference of the _value array. + */ + const domCg_bool4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_bool4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool4() {} + /** + * Overloaded assignment operator + */ + virtual domBool4 &operator=( const domBool4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool1x1; + + typedef daeSmartRef domBool1x1Ref; + typedef daeTArray domBool1x1_Array; + + class domBool1x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL1X1; } + static daeInt ID() { return 386; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_bool1x1 value of the text data of this element. + */ + domCg_bool1x1 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_bool1x1 reference of the _value array. + */ + domCg_bool1x1 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_bool1x1 reference of the _value array. + */ + const domCg_bool1x1 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_bool1x1 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool1x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool1x1() {} + /** + * Overloaded assignment operator + */ + virtual domBool1x1 &operator=( const domBool1x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool1x2; + + typedef daeSmartRef domBool1x2Ref; + typedef daeTArray domBool1x2_Array; + + class domBool1x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL1X2; } + static daeInt ID() { return 387; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_bool1x2 value of the text data of this element. + */ + domCg_bool1x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_bool1x2 reference of the _value array. + */ + domCg_bool1x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_bool1x2 reference of the _value array. + */ + const domCg_bool1x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_bool1x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool1x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool1x2() {} + /** + * Overloaded assignment operator + */ + virtual domBool1x2 &operator=( const domBool1x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool1x3; + + typedef daeSmartRef domBool1x3Ref; + typedef daeTArray domBool1x3_Array; + + class domBool1x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL1X3; } + static daeInt ID() { return 388; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_bool1x3 value of the text data of this element. + */ + domCg_bool1x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_bool1x3 reference of the _value array. + */ + domCg_bool1x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_bool1x3 reference of the _value array. + */ + const domCg_bool1x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_bool1x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool1x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool1x3() {} + /** + * Overloaded assignment operator + */ + virtual domBool1x3 &operator=( const domBool1x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool1x4; + + typedef daeSmartRef domBool1x4Ref; + typedef daeTArray domBool1x4_Array; + + class domBool1x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL1X4; } + static daeInt ID() { return 389; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_bool1x4 value of the text data of this element. + */ + domCg_bool1x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_bool1x4 reference of the _value array. + */ + domCg_bool1x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_bool1x4 reference of the _value array. + */ + const domCg_bool1x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_bool1x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool1x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool1x4() {} + /** + * Overloaded assignment operator + */ + virtual domBool1x4 &operator=( const domBool1x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool2x1; + + typedef daeSmartRef domBool2x1Ref; + typedef daeTArray domBool2x1_Array; + + class domBool2x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL2X1; } + static daeInt ID() { return 390; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_bool2x1 value of the text data of this element. + */ + domCg_bool2x1 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_bool2x1 reference of the _value array. + */ + domCg_bool2x1 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_bool2x1 reference of the _value array. + */ + const domCg_bool2x1 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_bool2x1 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool2x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool2x1() {} + /** + * Overloaded assignment operator + */ + virtual domBool2x1 &operator=( const domBool2x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool2x2; + + typedef daeSmartRef domBool2x2Ref; + typedef daeTArray domBool2x2_Array; + + class domBool2x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL2X2; } + static daeInt ID() { return 391; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_bool2x2 value of the text data of this element. + */ + domCg_bool2x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_bool2x2 reference of the _value array. + */ + domCg_bool2x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_bool2x2 reference of the _value array. + */ + const domCg_bool2x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_bool2x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool2x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool2x2() {} + /** + * Overloaded assignment operator + */ + virtual domBool2x2 &operator=( const domBool2x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool2x3; + + typedef daeSmartRef domBool2x3Ref; + typedef daeTArray domBool2x3_Array; + + class domBool2x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL2X3; } + static daeInt ID() { return 392; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_bool2x3 value of the text data of this element. + */ + domCg_bool2x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_bool2x3 reference of the _value array. + */ + domCg_bool2x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_bool2x3 reference of the _value array. + */ + const domCg_bool2x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_bool2x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool2x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool2x3() {} + /** + * Overloaded assignment operator + */ + virtual domBool2x3 &operator=( const domBool2x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool2x4; + + typedef daeSmartRef domBool2x4Ref; + typedef daeTArray domBool2x4_Array; + + class domBool2x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL2X4; } + static daeInt ID() { return 393; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_bool2x4 value of the text data of this element. + */ + domCg_bool2x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_bool2x4 reference of the _value array. + */ + domCg_bool2x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_bool2x4 reference of the _value array. + */ + const domCg_bool2x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_bool2x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool2x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool2x4() {} + /** + * Overloaded assignment operator + */ + virtual domBool2x4 &operator=( const domBool2x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool3x1; + + typedef daeSmartRef domBool3x1Ref; + typedef daeTArray domBool3x1_Array; + + class domBool3x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL3X1; } + static daeInt ID() { return 394; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_bool3x1 value of the text data of this element. + */ + domCg_bool3x1 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_bool3x1 reference of the _value array. + */ + domCg_bool3x1 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_bool3x1 reference of the _value array. + */ + const domCg_bool3x1 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_bool3x1 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool3x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool3x1() {} + /** + * Overloaded assignment operator + */ + virtual domBool3x1 &operator=( const domBool3x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool3x2; + + typedef daeSmartRef domBool3x2Ref; + typedef daeTArray domBool3x2_Array; + + class domBool3x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL3X2; } + static daeInt ID() { return 395; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_bool3x2 value of the text data of this element. + */ + domCg_bool3x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_bool3x2 reference of the _value array. + */ + domCg_bool3x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_bool3x2 reference of the _value array. + */ + const domCg_bool3x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_bool3x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool3x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool3x2() {} + /** + * Overloaded assignment operator + */ + virtual domBool3x2 &operator=( const domBool3x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool3x3; + + typedef daeSmartRef domBool3x3Ref; + typedef daeTArray domBool3x3_Array; + + class domBool3x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL3X3; } + static daeInt ID() { return 396; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_bool3x3 value of the text data of this element. + */ + domCg_bool3x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_bool3x3 reference of the _value array. + */ + domCg_bool3x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_bool3x3 reference of the _value array. + */ + const domCg_bool3x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_bool3x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool3x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool3x3() {} + /** + * Overloaded assignment operator + */ + virtual domBool3x3 &operator=( const domBool3x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool3x4; + + typedef daeSmartRef domBool3x4Ref; + typedef daeTArray domBool3x4_Array; + + class domBool3x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL3X4; } + static daeInt ID() { return 397; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_bool3x4 value of the text data of this element. + */ + domCg_bool3x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_bool3x4 reference of the _value array. + */ + domCg_bool3x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_bool3x4 reference of the _value array. + */ + const domCg_bool3x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_bool3x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool3x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool3x4() {} + /** + * Overloaded assignment operator + */ + virtual domBool3x4 &operator=( const domBool3x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool4x1; + + typedef daeSmartRef domBool4x1Ref; + typedef daeTArray domBool4x1_Array; + + class domBool4x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL4X1; } + static daeInt ID() { return 398; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_bool4x1 value of the text data of this element. + */ + domCg_bool4x1 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_bool4x1 reference of the _value array. + */ + domCg_bool4x1 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_bool4x1 reference of the _value array. + */ + const domCg_bool4x1 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_bool4x1 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool4x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool4x1() {} + /** + * Overloaded assignment operator + */ + virtual domBool4x1 &operator=( const domBool4x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool4x2; + + typedef daeSmartRef domBool4x2Ref; + typedef daeTArray domBool4x2_Array; + + class domBool4x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL4X2; } + static daeInt ID() { return 399; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_bool4x2 value of the text data of this element. + */ + domCg_bool4x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_bool4x2 reference of the _value array. + */ + domCg_bool4x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_bool4x2 reference of the _value array. + */ + const domCg_bool4x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_bool4x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool4x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool4x2() {} + /** + * Overloaded assignment operator + */ + virtual domBool4x2 &operator=( const domBool4x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool4x3; + + typedef daeSmartRef domBool4x3Ref; + typedef daeTArray domBool4x3_Array; + + class domBool4x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL4X3; } + static daeInt ID() { return 400; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_bool4x3 value of the text data of this element. + */ + domCg_bool4x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_bool4x3 reference of the _value array. + */ + domCg_bool4x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_bool4x3 reference of the _value array. + */ + const domCg_bool4x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_bool4x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool4x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool4x3() {} + /** + * Overloaded assignment operator + */ + virtual domBool4x3 &operator=( const domBool4x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool4x4; + + typedef daeSmartRef domBool4x4Ref; + typedef daeTArray domBool4x4_Array; + + class domBool4x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL4X4; } + static daeInt ID() { return 401; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_bool4x4 value of the text data of this element. + */ + domCg_bool4x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_bool4x4 reference of the _value array. + */ + domCg_bool4x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_bool4x4 reference of the _value array. + */ + const domCg_bool4x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_bool4x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool4x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool4x4() {} + /** + * Overloaded assignment operator + */ + virtual domBool4x4 &operator=( const domBool4x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat; + + typedef daeSmartRef domFloatRef; + typedef daeTArray domFloat_Array; + + class domFloat : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT; } + static daeInt ID() { return 402; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_float value of the text data of this element. + */ + domCg_float _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domCg_float of the value. + */ + domCg_float getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domCg_float val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat() {} + /** + * Overloaded assignment operator + */ + virtual domFloat &operator=( const domFloat &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat1; + + typedef daeSmartRef domFloat1Ref; + typedef daeTArray domFloat1_Array; + + class domFloat1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT1; } + static daeInt ID() { return 403; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_float1 value of the text data of this element. + */ + domCg_float1 _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domCg_float1 of the value. + */ + domCg_float1 getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domCg_float1 val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat1() {} + /** + * Overloaded assignment operator + */ + virtual domFloat1 &operator=( const domFloat1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat2; + + typedef daeSmartRef domFloat2Ref; + typedef daeTArray domFloat2_Array; + + class domFloat2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT2; } + static daeInt ID() { return 404; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_float2 value of the text data of this element. + */ + domCg_float2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_float2 reference of the _value array. + */ + domCg_float2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_float2 reference of the _value array. + */ + const domCg_float2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_float2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat2() {} + /** + * Overloaded assignment operator + */ + virtual domFloat2 &operator=( const domFloat2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat3; + + typedef daeSmartRef domFloat3Ref; + typedef daeTArray domFloat3_Array; + + class domFloat3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT3; } + static daeInt ID() { return 405; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_float3 value of the text data of this element. + */ + domCg_float3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_float3 reference of the _value array. + */ + domCg_float3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_float3 reference of the _value array. + */ + const domCg_float3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_float3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat3() {} + /** + * Overloaded assignment operator + */ + virtual domFloat3 &operator=( const domFloat3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat4; + + typedef daeSmartRef domFloat4Ref; + typedef daeTArray domFloat4_Array; + + class domFloat4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT4; } + static daeInt ID() { return 406; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_float4 value of the text data of this element. + */ + domCg_float4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_float4 reference of the _value array. + */ + domCg_float4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_float4 reference of the _value array. + */ + const domCg_float4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_float4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat4() {} + /** + * Overloaded assignment operator + */ + virtual domFloat4 &operator=( const domFloat4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat1x1; + + typedef daeSmartRef domFloat1x1Ref; + typedef daeTArray domFloat1x1_Array; + + class domFloat1x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT1X1; } + static daeInt ID() { return 407; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_float1x1 value of the text data of this element. + */ + domCg_float1x1 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_float1x1 reference of the _value array. + */ + domCg_float1x1 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_float1x1 reference of the _value array. + */ + const domCg_float1x1 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_float1x1 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat1x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat1x1() {} + /** + * Overloaded assignment operator + */ + virtual domFloat1x1 &operator=( const domFloat1x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat1x2; + + typedef daeSmartRef domFloat1x2Ref; + typedef daeTArray domFloat1x2_Array; + + class domFloat1x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT1X2; } + static daeInt ID() { return 408; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_float1x2 value of the text data of this element. + */ + domCg_float1x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_float1x2 reference of the _value array. + */ + domCg_float1x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_float1x2 reference of the _value array. + */ + const domCg_float1x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_float1x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat1x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat1x2() {} + /** + * Overloaded assignment operator + */ + virtual domFloat1x2 &operator=( const domFloat1x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat1x3; + + typedef daeSmartRef domFloat1x3Ref; + typedef daeTArray domFloat1x3_Array; + + class domFloat1x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT1X3; } + static daeInt ID() { return 409; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_float1x3 value of the text data of this element. + */ + domCg_float1x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_float1x3 reference of the _value array. + */ + domCg_float1x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_float1x3 reference of the _value array. + */ + const domCg_float1x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_float1x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat1x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat1x3() {} + /** + * Overloaded assignment operator + */ + virtual domFloat1x3 &operator=( const domFloat1x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat1x4; + + typedef daeSmartRef domFloat1x4Ref; + typedef daeTArray domFloat1x4_Array; + + class domFloat1x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT1X4; } + static daeInt ID() { return 410; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_float1x4 value of the text data of this element. + */ + domCg_float1x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_float1x4 reference of the _value array. + */ + domCg_float1x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_float1x4 reference of the _value array. + */ + const domCg_float1x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_float1x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat1x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat1x4() {} + /** + * Overloaded assignment operator + */ + virtual domFloat1x4 &operator=( const domFloat1x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat2x1; + + typedef daeSmartRef domFloat2x1Ref; + typedef daeTArray domFloat2x1_Array; + + class domFloat2x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT2X1; } + static daeInt ID() { return 411; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_float2x1 value of the text data of this element. + */ + domCg_float2x1 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_float2x1 reference of the _value array. + */ + domCg_float2x1 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_float2x1 reference of the _value array. + */ + const domCg_float2x1 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_float2x1 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat2x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat2x1() {} + /** + * Overloaded assignment operator + */ + virtual domFloat2x1 &operator=( const domFloat2x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat2x2; + + typedef daeSmartRef domFloat2x2Ref; + typedef daeTArray domFloat2x2_Array; + + class domFloat2x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT2X2; } + static daeInt ID() { return 412; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_float2x2 value of the text data of this element. + */ + domCg_float2x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_float2x2 reference of the _value array. + */ + domCg_float2x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_float2x2 reference of the _value array. + */ + const domCg_float2x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_float2x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat2x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat2x2() {} + /** + * Overloaded assignment operator + */ + virtual domFloat2x2 &operator=( const domFloat2x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat2x3; + + typedef daeSmartRef domFloat2x3Ref; + typedef daeTArray domFloat2x3_Array; + + class domFloat2x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT2X3; } + static daeInt ID() { return 413; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_float2x3 value of the text data of this element. + */ + domCg_float2x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_float2x3 reference of the _value array. + */ + domCg_float2x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_float2x3 reference of the _value array. + */ + const domCg_float2x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_float2x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat2x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat2x3() {} + /** + * Overloaded assignment operator + */ + virtual domFloat2x3 &operator=( const domFloat2x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat2x4; + + typedef daeSmartRef domFloat2x4Ref; + typedef daeTArray domFloat2x4_Array; + + class domFloat2x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT2X4; } + static daeInt ID() { return 414; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_float2x4 value of the text data of this element. + */ + domCg_float2x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_float2x4 reference of the _value array. + */ + domCg_float2x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_float2x4 reference of the _value array. + */ + const domCg_float2x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_float2x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat2x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat2x4() {} + /** + * Overloaded assignment operator + */ + virtual domFloat2x4 &operator=( const domFloat2x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat3x1; + + typedef daeSmartRef domFloat3x1Ref; + typedef daeTArray domFloat3x1_Array; + + class domFloat3x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT3X1; } + static daeInt ID() { return 415; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_float3x1 value of the text data of this element. + */ + domCg_float3x1 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_float3x1 reference of the _value array. + */ + domCg_float3x1 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_float3x1 reference of the _value array. + */ + const domCg_float3x1 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_float3x1 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat3x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat3x1() {} + /** + * Overloaded assignment operator + */ + virtual domFloat3x1 &operator=( const domFloat3x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat3x2; + + typedef daeSmartRef domFloat3x2Ref; + typedef daeTArray domFloat3x2_Array; + + class domFloat3x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT3X2; } + static daeInt ID() { return 416; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_float3x2 value of the text data of this element. + */ + domCg_float3x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_float3x2 reference of the _value array. + */ + domCg_float3x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_float3x2 reference of the _value array. + */ + const domCg_float3x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_float3x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat3x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat3x2() {} + /** + * Overloaded assignment operator + */ + virtual domFloat3x2 &operator=( const domFloat3x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat3x3; + + typedef daeSmartRef domFloat3x3Ref; + typedef daeTArray domFloat3x3_Array; + + class domFloat3x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT3X3; } + static daeInt ID() { return 417; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_float3x3 value of the text data of this element. + */ + domCg_float3x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_float3x3 reference of the _value array. + */ + domCg_float3x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_float3x3 reference of the _value array. + */ + const domCg_float3x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_float3x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat3x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat3x3() {} + /** + * Overloaded assignment operator + */ + virtual domFloat3x3 &operator=( const domFloat3x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat3x4; + + typedef daeSmartRef domFloat3x4Ref; + typedef daeTArray domFloat3x4_Array; + + class domFloat3x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT3X4; } + static daeInt ID() { return 418; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_float3x4 value of the text data of this element. + */ + domCg_float3x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_float3x4 reference of the _value array. + */ + domCg_float3x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_float3x4 reference of the _value array. + */ + const domCg_float3x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_float3x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat3x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat3x4() {} + /** + * Overloaded assignment operator + */ + virtual domFloat3x4 &operator=( const domFloat3x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat4x1; + + typedef daeSmartRef domFloat4x1Ref; + typedef daeTArray domFloat4x1_Array; + + class domFloat4x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT4X1; } + static daeInt ID() { return 419; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_float4x1 value of the text data of this element. + */ + domCg_float4x1 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_float4x1 reference of the _value array. + */ + domCg_float4x1 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_float4x1 reference of the _value array. + */ + const domCg_float4x1 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_float4x1 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat4x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat4x1() {} + /** + * Overloaded assignment operator + */ + virtual domFloat4x1 &operator=( const domFloat4x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat4x2; + + typedef daeSmartRef domFloat4x2Ref; + typedef daeTArray domFloat4x2_Array; + + class domFloat4x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT4X2; } + static daeInt ID() { return 420; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_float4x2 value of the text data of this element. + */ + domCg_float4x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_float4x2 reference of the _value array. + */ + domCg_float4x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_float4x2 reference of the _value array. + */ + const domCg_float4x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_float4x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat4x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat4x2() {} + /** + * Overloaded assignment operator + */ + virtual domFloat4x2 &operator=( const domFloat4x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat4x3; + + typedef daeSmartRef domFloat4x3Ref; + typedef daeTArray domFloat4x3_Array; + + class domFloat4x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT4X3; } + static daeInt ID() { return 421; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_float4x3 value of the text data of this element. + */ + domCg_float4x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_float4x3 reference of the _value array. + */ + domCg_float4x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_float4x3 reference of the _value array. + */ + const domCg_float4x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_float4x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat4x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat4x3() {} + /** + * Overloaded assignment operator + */ + virtual domFloat4x3 &operator=( const domFloat4x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat4x4; + + typedef daeSmartRef domFloat4x4Ref; + typedef daeTArray domFloat4x4_Array; + + class domFloat4x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT4X4; } + static daeInt ID() { return 422; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_float4x4 value of the text data of this element. + */ + domCg_float4x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_float4x4 reference of the _value array. + */ + domCg_float4x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_float4x4 reference of the _value array. + */ + const domCg_float4x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_float4x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat4x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat4x4() {} + /** + * Overloaded assignment operator + */ + virtual domFloat4x4 &operator=( const domFloat4x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt; + + typedef daeSmartRef domIntRef; + typedef daeTArray domInt_Array; + + class domInt : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT; } + static daeInt ID() { return 423; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_int value of the text data of this element. + */ + domCg_int _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domCg_int of the value. + */ + domCg_int getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domCg_int val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt() {} + /** + * Overloaded assignment operator + */ + virtual domInt &operator=( const domInt &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt1; + + typedef daeSmartRef domInt1Ref; + typedef daeTArray domInt1_Array; + + class domInt1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT1; } + static daeInt ID() { return 424; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_int1 value of the text data of this element. + */ + domCg_int1 _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domCg_int1 of the value. + */ + domCg_int1 getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domCg_int1 val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt1() {} + /** + * Overloaded assignment operator + */ + virtual domInt1 &operator=( const domInt1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt2; + + typedef daeSmartRef domInt2Ref; + typedef daeTArray domInt2_Array; + + class domInt2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT2; } + static daeInt ID() { return 425; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_int2 value of the text data of this element. + */ + domCg_int2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_int2 reference of the _value array. + */ + domCg_int2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_int2 reference of the _value array. + */ + const domCg_int2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_int2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt2() {} + /** + * Overloaded assignment operator + */ + virtual domInt2 &operator=( const domInt2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt3; + + typedef daeSmartRef domInt3Ref; + typedef daeTArray domInt3_Array; + + class domInt3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT3; } + static daeInt ID() { return 426; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_int3 value of the text data of this element. + */ + domCg_int3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_int3 reference of the _value array. + */ + domCg_int3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_int3 reference of the _value array. + */ + const domCg_int3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_int3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt3() {} + /** + * Overloaded assignment operator + */ + virtual domInt3 &operator=( const domInt3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt4; + + typedef daeSmartRef domInt4Ref; + typedef daeTArray domInt4_Array; + + class domInt4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT4; } + static daeInt ID() { return 427; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_int4 value of the text data of this element. + */ + domCg_int4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_int4 reference of the _value array. + */ + domCg_int4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_int4 reference of the _value array. + */ + const domCg_int4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_int4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt4() {} + /** + * Overloaded assignment operator + */ + virtual domInt4 &operator=( const domInt4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt1x1; + + typedef daeSmartRef domInt1x1Ref; + typedef daeTArray domInt1x1_Array; + + class domInt1x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT1X1; } + static daeInt ID() { return 428; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_int1x1 value of the text data of this element. + */ + domCg_int1x1 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_int1x1 reference of the _value array. + */ + domCg_int1x1 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_int1x1 reference of the _value array. + */ + const domCg_int1x1 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_int1x1 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt1x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt1x1() {} + /** + * Overloaded assignment operator + */ + virtual domInt1x1 &operator=( const domInt1x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt1x2; + + typedef daeSmartRef domInt1x2Ref; + typedef daeTArray domInt1x2_Array; + + class domInt1x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT1X2; } + static daeInt ID() { return 429; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_int1x2 value of the text data of this element. + */ + domCg_int1x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_int1x2 reference of the _value array. + */ + domCg_int1x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_int1x2 reference of the _value array. + */ + const domCg_int1x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_int1x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt1x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt1x2() {} + /** + * Overloaded assignment operator + */ + virtual domInt1x2 &operator=( const domInt1x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt1x3; + + typedef daeSmartRef domInt1x3Ref; + typedef daeTArray domInt1x3_Array; + + class domInt1x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT1X3; } + static daeInt ID() { return 430; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_int1x3 value of the text data of this element. + */ + domCg_int1x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_int1x3 reference of the _value array. + */ + domCg_int1x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_int1x3 reference of the _value array. + */ + const domCg_int1x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_int1x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt1x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt1x3() {} + /** + * Overloaded assignment operator + */ + virtual domInt1x3 &operator=( const domInt1x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt1x4; + + typedef daeSmartRef domInt1x4Ref; + typedef daeTArray domInt1x4_Array; + + class domInt1x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT1X4; } + static daeInt ID() { return 431; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_int1x4 value of the text data of this element. + */ + domCg_int1x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_int1x4 reference of the _value array. + */ + domCg_int1x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_int1x4 reference of the _value array. + */ + const domCg_int1x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_int1x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt1x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt1x4() {} + /** + * Overloaded assignment operator + */ + virtual domInt1x4 &operator=( const domInt1x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt2x1; + + typedef daeSmartRef domInt2x1Ref; + typedef daeTArray domInt2x1_Array; + + class domInt2x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT2X1; } + static daeInt ID() { return 432; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_int2x1 value of the text data of this element. + */ + domCg_int2x1 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_int2x1 reference of the _value array. + */ + domCg_int2x1 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_int2x1 reference of the _value array. + */ + const domCg_int2x1 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_int2x1 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt2x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt2x1() {} + /** + * Overloaded assignment operator + */ + virtual domInt2x1 &operator=( const domInt2x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt2x2; + + typedef daeSmartRef domInt2x2Ref; + typedef daeTArray domInt2x2_Array; + + class domInt2x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT2X2; } + static daeInt ID() { return 433; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_int2x2 value of the text data of this element. + */ + domCg_int2x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_int2x2 reference of the _value array. + */ + domCg_int2x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_int2x2 reference of the _value array. + */ + const domCg_int2x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_int2x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt2x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt2x2() {} + /** + * Overloaded assignment operator + */ + virtual domInt2x2 &operator=( const domInt2x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt2x3; + + typedef daeSmartRef domInt2x3Ref; + typedef daeTArray domInt2x3_Array; + + class domInt2x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT2X3; } + static daeInt ID() { return 434; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_int2x3 value of the text data of this element. + */ + domCg_int2x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_int2x3 reference of the _value array. + */ + domCg_int2x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_int2x3 reference of the _value array. + */ + const domCg_int2x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_int2x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt2x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt2x3() {} + /** + * Overloaded assignment operator + */ + virtual domInt2x3 &operator=( const domInt2x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt2x4; + + typedef daeSmartRef domInt2x4Ref; + typedef daeTArray domInt2x4_Array; + + class domInt2x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT2X4; } + static daeInt ID() { return 435; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_int2x4 value of the text data of this element. + */ + domCg_int2x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_int2x4 reference of the _value array. + */ + domCg_int2x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_int2x4 reference of the _value array. + */ + const domCg_int2x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_int2x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt2x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt2x4() {} + /** + * Overloaded assignment operator + */ + virtual domInt2x4 &operator=( const domInt2x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt3x1; + + typedef daeSmartRef domInt3x1Ref; + typedef daeTArray domInt3x1_Array; + + class domInt3x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT3X1; } + static daeInt ID() { return 436; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_int3x1 value of the text data of this element. + */ + domCg_int3x1 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_int3x1 reference of the _value array. + */ + domCg_int3x1 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_int3x1 reference of the _value array. + */ + const domCg_int3x1 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_int3x1 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt3x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt3x1() {} + /** + * Overloaded assignment operator + */ + virtual domInt3x1 &operator=( const domInt3x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt3x2; + + typedef daeSmartRef domInt3x2Ref; + typedef daeTArray domInt3x2_Array; + + class domInt3x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT3X2; } + static daeInt ID() { return 437; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_int3x2 value of the text data of this element. + */ + domCg_int3x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_int3x2 reference of the _value array. + */ + domCg_int3x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_int3x2 reference of the _value array. + */ + const domCg_int3x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_int3x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt3x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt3x2() {} + /** + * Overloaded assignment operator + */ + virtual domInt3x2 &operator=( const domInt3x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt3x3; + + typedef daeSmartRef domInt3x3Ref; + typedef daeTArray domInt3x3_Array; + + class domInt3x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT3X3; } + static daeInt ID() { return 438; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_int3x3 value of the text data of this element. + */ + domCg_int3x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_int3x3 reference of the _value array. + */ + domCg_int3x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_int3x3 reference of the _value array. + */ + const domCg_int3x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_int3x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt3x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt3x3() {} + /** + * Overloaded assignment operator + */ + virtual domInt3x3 &operator=( const domInt3x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt3x4; + + typedef daeSmartRef domInt3x4Ref; + typedef daeTArray domInt3x4_Array; + + class domInt3x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT3X4; } + static daeInt ID() { return 439; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_int3x4 value of the text data of this element. + */ + domCg_int3x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_int3x4 reference of the _value array. + */ + domCg_int3x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_int3x4 reference of the _value array. + */ + const domCg_int3x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_int3x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt3x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt3x4() {} + /** + * Overloaded assignment operator + */ + virtual domInt3x4 &operator=( const domInt3x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt4x1; + + typedef daeSmartRef domInt4x1Ref; + typedef daeTArray domInt4x1_Array; + + class domInt4x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT4X1; } + static daeInt ID() { return 440; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_int4x1 value of the text data of this element. + */ + domCg_int4x1 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_int4x1 reference of the _value array. + */ + domCg_int4x1 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_int4x1 reference of the _value array. + */ + const domCg_int4x1 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_int4x1 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt4x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt4x1() {} + /** + * Overloaded assignment operator + */ + virtual domInt4x1 &operator=( const domInt4x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt4x2; + + typedef daeSmartRef domInt4x2Ref; + typedef daeTArray domInt4x2_Array; + + class domInt4x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT4X2; } + static daeInt ID() { return 441; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_int4x2 value of the text data of this element. + */ + domCg_int4x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_int4x2 reference of the _value array. + */ + domCg_int4x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_int4x2 reference of the _value array. + */ + const domCg_int4x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_int4x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt4x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt4x2() {} + /** + * Overloaded assignment operator + */ + virtual domInt4x2 &operator=( const domInt4x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt4x3; + + typedef daeSmartRef domInt4x3Ref; + typedef daeTArray domInt4x3_Array; + + class domInt4x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT4X3; } + static daeInt ID() { return 442; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_int4x3 value of the text data of this element. + */ + domCg_int4x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_int4x3 reference of the _value array. + */ + domCg_int4x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_int4x3 reference of the _value array. + */ + const domCg_int4x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_int4x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt4x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt4x3() {} + /** + * Overloaded assignment operator + */ + virtual domInt4x3 &operator=( const domInt4x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt4x4; + + typedef daeSmartRef domInt4x4Ref; + typedef daeTArray domInt4x4_Array; + + class domInt4x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT4X4; } + static daeInt ID() { return 443; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_int4x4 value of the text data of this element. + */ + domCg_int4x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_int4x4 reference of the _value array. + */ + domCg_int4x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_int4x4 reference of the _value array. + */ + const domCg_int4x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_int4x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt4x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt4x4() {} + /** + * Overloaded assignment operator + */ + virtual domInt4x4 &operator=( const domInt4x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domHalf; + + typedef daeSmartRef domHalfRef; + typedef daeTArray domHalf_Array; + + class domHalf : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HALF; } + static daeInt ID() { return 444; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_half value of the text data of this element. + */ + domCg_half _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domCg_half of the value. + */ + domCg_half getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domCg_half val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHalf(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domHalf() {} + /** + * Overloaded assignment operator + */ + virtual domHalf &operator=( const domHalf &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domHalf1; + + typedef daeSmartRef domHalf1Ref; + typedef daeTArray domHalf1_Array; + + class domHalf1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HALF1; } + static daeInt ID() { return 445; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_half1 value of the text data of this element. + */ + domCg_half1 _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domCg_half1 of the value. + */ + domCg_half1 getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domCg_half1 val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHalf1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domHalf1() {} + /** + * Overloaded assignment operator + */ + virtual domHalf1 &operator=( const domHalf1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domHalf2; + + typedef daeSmartRef domHalf2Ref; + typedef daeTArray domHalf2_Array; + + class domHalf2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HALF2; } + static daeInt ID() { return 446; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_half2 value of the text data of this element. + */ + domCg_half2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_half2 reference of the _value array. + */ + domCg_half2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_half2 reference of the _value array. + */ + const domCg_half2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_half2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHalf2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domHalf2() {} + /** + * Overloaded assignment operator + */ + virtual domHalf2 &operator=( const domHalf2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domHalf3; + + typedef daeSmartRef domHalf3Ref; + typedef daeTArray domHalf3_Array; + + class domHalf3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HALF3; } + static daeInt ID() { return 447; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_half3 value of the text data of this element. + */ + domCg_half3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_half3 reference of the _value array. + */ + domCg_half3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_half3 reference of the _value array. + */ + const domCg_half3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_half3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHalf3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domHalf3() {} + /** + * Overloaded assignment operator + */ + virtual domHalf3 &operator=( const domHalf3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domHalf4; + + typedef daeSmartRef domHalf4Ref; + typedef daeTArray domHalf4_Array; + + class domHalf4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HALF4; } + static daeInt ID() { return 448; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_half4 value of the text data of this element. + */ + domCg_half4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_half4 reference of the _value array. + */ + domCg_half4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_half4 reference of the _value array. + */ + const domCg_half4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_half4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHalf4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domHalf4() {} + /** + * Overloaded assignment operator + */ + virtual domHalf4 &operator=( const domHalf4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domHalf1x1; + + typedef daeSmartRef domHalf1x1Ref; + typedef daeTArray domHalf1x1_Array; + + class domHalf1x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HALF1X1; } + static daeInt ID() { return 449; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_half1x1 value of the text data of this element. + */ + domCg_half1x1 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_half1x1 reference of the _value array. + */ + domCg_half1x1 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_half1x1 reference of the _value array. + */ + const domCg_half1x1 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_half1x1 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHalf1x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domHalf1x1() {} + /** + * Overloaded assignment operator + */ + virtual domHalf1x1 &operator=( const domHalf1x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domHalf1x2; + + typedef daeSmartRef domHalf1x2Ref; + typedef daeTArray domHalf1x2_Array; + + class domHalf1x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HALF1X2; } + static daeInt ID() { return 450; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_half1x2 value of the text data of this element. + */ + domCg_half1x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_half1x2 reference of the _value array. + */ + domCg_half1x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_half1x2 reference of the _value array. + */ + const domCg_half1x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_half1x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHalf1x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domHalf1x2() {} + /** + * Overloaded assignment operator + */ + virtual domHalf1x2 &operator=( const domHalf1x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domHalf1x3; + + typedef daeSmartRef domHalf1x3Ref; + typedef daeTArray domHalf1x3_Array; + + class domHalf1x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HALF1X3; } + static daeInt ID() { return 451; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_half1x3 value of the text data of this element. + */ + domCg_half1x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_half1x3 reference of the _value array. + */ + domCg_half1x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_half1x3 reference of the _value array. + */ + const domCg_half1x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_half1x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHalf1x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domHalf1x3() {} + /** + * Overloaded assignment operator + */ + virtual domHalf1x3 &operator=( const domHalf1x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domHalf1x4; + + typedef daeSmartRef domHalf1x4Ref; + typedef daeTArray domHalf1x4_Array; + + class domHalf1x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HALF1X4; } + static daeInt ID() { return 452; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_half1x4 value of the text data of this element. + */ + domCg_half1x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_half1x4 reference of the _value array. + */ + domCg_half1x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_half1x4 reference of the _value array. + */ + const domCg_half1x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_half1x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHalf1x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domHalf1x4() {} + /** + * Overloaded assignment operator + */ + virtual domHalf1x4 &operator=( const domHalf1x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domHalf2x1; + + typedef daeSmartRef domHalf2x1Ref; + typedef daeTArray domHalf2x1_Array; + + class domHalf2x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HALF2X1; } + static daeInt ID() { return 453; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_half2x1 value of the text data of this element. + */ + domCg_half2x1 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_half2x1 reference of the _value array. + */ + domCg_half2x1 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_half2x1 reference of the _value array. + */ + const domCg_half2x1 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_half2x1 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHalf2x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domHalf2x1() {} + /** + * Overloaded assignment operator + */ + virtual domHalf2x1 &operator=( const domHalf2x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domHalf2x2; + + typedef daeSmartRef domHalf2x2Ref; + typedef daeTArray domHalf2x2_Array; + + class domHalf2x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HALF2X2; } + static daeInt ID() { return 454; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_half2x2 value of the text data of this element. + */ + domCg_half2x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_half2x2 reference of the _value array. + */ + domCg_half2x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_half2x2 reference of the _value array. + */ + const domCg_half2x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_half2x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHalf2x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domHalf2x2() {} + /** + * Overloaded assignment operator + */ + virtual domHalf2x2 &operator=( const domHalf2x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domHalf2x3; + + typedef daeSmartRef domHalf2x3Ref; + typedef daeTArray domHalf2x3_Array; + + class domHalf2x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HALF2X3; } + static daeInt ID() { return 455; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_half2x3 value of the text data of this element. + */ + domCg_half2x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_half2x3 reference of the _value array. + */ + domCg_half2x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_half2x3 reference of the _value array. + */ + const domCg_half2x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_half2x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHalf2x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domHalf2x3() {} + /** + * Overloaded assignment operator + */ + virtual domHalf2x3 &operator=( const domHalf2x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domHalf2x4; + + typedef daeSmartRef domHalf2x4Ref; + typedef daeTArray domHalf2x4_Array; + + class domHalf2x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HALF2X4; } + static daeInt ID() { return 456; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_half2x4 value of the text data of this element. + */ + domCg_half2x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_half2x4 reference of the _value array. + */ + domCg_half2x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_half2x4 reference of the _value array. + */ + const domCg_half2x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_half2x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHalf2x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domHalf2x4() {} + /** + * Overloaded assignment operator + */ + virtual domHalf2x4 &operator=( const domHalf2x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domHalf3x1; + + typedef daeSmartRef domHalf3x1Ref; + typedef daeTArray domHalf3x1_Array; + + class domHalf3x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HALF3X1; } + static daeInt ID() { return 457; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_half3x1 value of the text data of this element. + */ + domCg_half3x1 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_half3x1 reference of the _value array. + */ + domCg_half3x1 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_half3x1 reference of the _value array. + */ + const domCg_half3x1 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_half3x1 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHalf3x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domHalf3x1() {} + /** + * Overloaded assignment operator + */ + virtual domHalf3x1 &operator=( const domHalf3x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domHalf3x2; + + typedef daeSmartRef domHalf3x2Ref; + typedef daeTArray domHalf3x2_Array; + + class domHalf3x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HALF3X2; } + static daeInt ID() { return 458; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_half3x2 value of the text data of this element. + */ + domCg_half3x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_half3x2 reference of the _value array. + */ + domCg_half3x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_half3x2 reference of the _value array. + */ + const domCg_half3x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_half3x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHalf3x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domHalf3x2() {} + /** + * Overloaded assignment operator + */ + virtual domHalf3x2 &operator=( const domHalf3x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domHalf3x3; + + typedef daeSmartRef domHalf3x3Ref; + typedef daeTArray domHalf3x3_Array; + + class domHalf3x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HALF3X3; } + static daeInt ID() { return 459; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_half3x3 value of the text data of this element. + */ + domCg_half3x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_half3x3 reference of the _value array. + */ + domCg_half3x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_half3x3 reference of the _value array. + */ + const domCg_half3x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_half3x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHalf3x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domHalf3x3() {} + /** + * Overloaded assignment operator + */ + virtual domHalf3x3 &operator=( const domHalf3x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domHalf3x4; + + typedef daeSmartRef domHalf3x4Ref; + typedef daeTArray domHalf3x4_Array; + + class domHalf3x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HALF3X4; } + static daeInt ID() { return 460; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_half3x4 value of the text data of this element. + */ + domCg_half3x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_half3x4 reference of the _value array. + */ + domCg_half3x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_half3x4 reference of the _value array. + */ + const domCg_half3x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_half3x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHalf3x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domHalf3x4() {} + /** + * Overloaded assignment operator + */ + virtual domHalf3x4 &operator=( const domHalf3x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domHalf4x1; + + typedef daeSmartRef domHalf4x1Ref; + typedef daeTArray domHalf4x1_Array; + + class domHalf4x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HALF4X1; } + static daeInt ID() { return 461; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_half4x1 value of the text data of this element. + */ + domCg_half4x1 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_half4x1 reference of the _value array. + */ + domCg_half4x1 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_half4x1 reference of the _value array. + */ + const domCg_half4x1 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_half4x1 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHalf4x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domHalf4x1() {} + /** + * Overloaded assignment operator + */ + virtual domHalf4x1 &operator=( const domHalf4x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domHalf4x2; + + typedef daeSmartRef domHalf4x2Ref; + typedef daeTArray domHalf4x2_Array; + + class domHalf4x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HALF4X2; } + static daeInt ID() { return 462; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_half4x2 value of the text data of this element. + */ + domCg_half4x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_half4x2 reference of the _value array. + */ + domCg_half4x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_half4x2 reference of the _value array. + */ + const domCg_half4x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_half4x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHalf4x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domHalf4x2() {} + /** + * Overloaded assignment operator + */ + virtual domHalf4x2 &operator=( const domHalf4x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domHalf4x3; + + typedef daeSmartRef domHalf4x3Ref; + typedef daeTArray domHalf4x3_Array; + + class domHalf4x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HALF4X3; } + static daeInt ID() { return 463; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_half4x3 value of the text data of this element. + */ + domCg_half4x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_half4x3 reference of the _value array. + */ + domCg_half4x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_half4x3 reference of the _value array. + */ + const domCg_half4x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_half4x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHalf4x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domHalf4x3() {} + /** + * Overloaded assignment operator + */ + virtual domHalf4x3 &operator=( const domHalf4x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domHalf4x4; + + typedef daeSmartRef domHalf4x4Ref; + typedef daeTArray domHalf4x4_Array; + + class domHalf4x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HALF4X4; } + static daeInt ID() { return 464; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_half4x4 value of the text data of this element. + */ + domCg_half4x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_half4x4 reference of the _value array. + */ + domCg_half4x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_half4x4 reference of the _value array. + */ + const domCg_half4x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_half4x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHalf4x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domHalf4x4() {} + /** + * Overloaded assignment operator + */ + virtual domHalf4x4 &operator=( const domHalf4x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFixed; + + typedef daeSmartRef domFixedRef; + typedef daeTArray domFixed_Array; + + class domFixed : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FIXED; } + static daeInt ID() { return 465; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_fixed value of the text data of this element. + */ + domCg_fixed _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domCg_fixed of the value. + */ + domCg_fixed getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domCg_fixed val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFixed(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFixed() {} + /** + * Overloaded assignment operator + */ + virtual domFixed &operator=( const domFixed &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFixed1; + + typedef daeSmartRef domFixed1Ref; + typedef daeTArray domFixed1_Array; + + class domFixed1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FIXED1; } + static daeInt ID() { return 466; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_fixed1 value of the text data of this element. + */ + domCg_fixed1 _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domCg_fixed1 of the value. + */ + domCg_fixed1 getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domCg_fixed1 val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFixed1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFixed1() {} + /** + * Overloaded assignment operator + */ + virtual domFixed1 &operator=( const domFixed1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFixed2; + + typedef daeSmartRef domFixed2Ref; + typedef daeTArray domFixed2_Array; + + class domFixed2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FIXED2; } + static daeInt ID() { return 467; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_fixed2 value of the text data of this element. + */ + domCg_fixed2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_fixed2 reference of the _value array. + */ + domCg_fixed2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_fixed2 reference of the _value array. + */ + const domCg_fixed2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_fixed2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFixed2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFixed2() {} + /** + * Overloaded assignment operator + */ + virtual domFixed2 &operator=( const domFixed2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFixed3; + + typedef daeSmartRef domFixed3Ref; + typedef daeTArray domFixed3_Array; + + class domFixed3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FIXED3; } + static daeInt ID() { return 468; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_fixed3 value of the text data of this element. + */ + domCg_fixed3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_fixed3 reference of the _value array. + */ + domCg_fixed3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_fixed3 reference of the _value array. + */ + const domCg_fixed3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_fixed3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFixed3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFixed3() {} + /** + * Overloaded assignment operator + */ + virtual domFixed3 &operator=( const domFixed3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFixed4; + + typedef daeSmartRef domFixed4Ref; + typedef daeTArray domFixed4_Array; + + class domFixed4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FIXED4; } + static daeInt ID() { return 469; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_fixed4 value of the text data of this element. + */ + domCg_fixed4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_fixed4 reference of the _value array. + */ + domCg_fixed4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_fixed4 reference of the _value array. + */ + const domCg_fixed4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_fixed4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFixed4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFixed4() {} + /** + * Overloaded assignment operator + */ + virtual domFixed4 &operator=( const domFixed4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFixed1x1; + + typedef daeSmartRef domFixed1x1Ref; + typedef daeTArray domFixed1x1_Array; + + class domFixed1x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FIXED1X1; } + static daeInt ID() { return 470; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_fixed1x1 value of the text data of this element. + */ + domCg_fixed1x1 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_fixed1x1 reference of the _value array. + */ + domCg_fixed1x1 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_fixed1x1 reference of the _value array. + */ + const domCg_fixed1x1 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_fixed1x1 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFixed1x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFixed1x1() {} + /** + * Overloaded assignment operator + */ + virtual domFixed1x1 &operator=( const domFixed1x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFixed1x2; + + typedef daeSmartRef domFixed1x2Ref; + typedef daeTArray domFixed1x2_Array; + + class domFixed1x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FIXED1X2; } + static daeInt ID() { return 471; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_fixed1x2 value of the text data of this element. + */ + domCg_fixed1x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_fixed1x2 reference of the _value array. + */ + domCg_fixed1x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_fixed1x2 reference of the _value array. + */ + const domCg_fixed1x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_fixed1x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFixed1x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFixed1x2() {} + /** + * Overloaded assignment operator + */ + virtual domFixed1x2 &operator=( const domFixed1x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFixed1x3; + + typedef daeSmartRef domFixed1x3Ref; + typedef daeTArray domFixed1x3_Array; + + class domFixed1x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FIXED1X3; } + static daeInt ID() { return 472; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_fixed1x3 value of the text data of this element. + */ + domCg_fixed1x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_fixed1x3 reference of the _value array. + */ + domCg_fixed1x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_fixed1x3 reference of the _value array. + */ + const domCg_fixed1x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_fixed1x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFixed1x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFixed1x3() {} + /** + * Overloaded assignment operator + */ + virtual domFixed1x3 &operator=( const domFixed1x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFixed1x4; + + typedef daeSmartRef domFixed1x4Ref; + typedef daeTArray domFixed1x4_Array; + + class domFixed1x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FIXED1X4; } + static daeInt ID() { return 473; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_fixed1x4 value of the text data of this element. + */ + domCg_fixed1x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_fixed1x4 reference of the _value array. + */ + domCg_fixed1x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_fixed1x4 reference of the _value array. + */ + const domCg_fixed1x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_fixed1x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFixed1x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFixed1x4() {} + /** + * Overloaded assignment operator + */ + virtual domFixed1x4 &operator=( const domFixed1x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFixed2x1; + + typedef daeSmartRef domFixed2x1Ref; + typedef daeTArray domFixed2x1_Array; + + class domFixed2x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FIXED2X1; } + static daeInt ID() { return 474; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_fixed2x1 value of the text data of this element. + */ + domCg_fixed2x1 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_fixed2x1 reference of the _value array. + */ + domCg_fixed2x1 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_fixed2x1 reference of the _value array. + */ + const domCg_fixed2x1 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_fixed2x1 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFixed2x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFixed2x1() {} + /** + * Overloaded assignment operator + */ + virtual domFixed2x1 &operator=( const domFixed2x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFixed2x2; + + typedef daeSmartRef domFixed2x2Ref; + typedef daeTArray domFixed2x2_Array; + + class domFixed2x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FIXED2X2; } + static daeInt ID() { return 475; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_fixed2x2 value of the text data of this element. + */ + domCg_fixed2x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_fixed2x2 reference of the _value array. + */ + domCg_fixed2x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_fixed2x2 reference of the _value array. + */ + const domCg_fixed2x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_fixed2x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFixed2x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFixed2x2() {} + /** + * Overloaded assignment operator + */ + virtual domFixed2x2 &operator=( const domFixed2x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFixed2x3; + + typedef daeSmartRef domFixed2x3Ref; + typedef daeTArray domFixed2x3_Array; + + class domFixed2x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FIXED2X3; } + static daeInt ID() { return 476; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_fixed2x3 value of the text data of this element. + */ + domCg_fixed2x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_fixed2x3 reference of the _value array. + */ + domCg_fixed2x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_fixed2x3 reference of the _value array. + */ + const domCg_fixed2x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_fixed2x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFixed2x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFixed2x3() {} + /** + * Overloaded assignment operator + */ + virtual domFixed2x3 &operator=( const domFixed2x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFixed2x4; + + typedef daeSmartRef domFixed2x4Ref; + typedef daeTArray domFixed2x4_Array; + + class domFixed2x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FIXED2X4; } + static daeInt ID() { return 477; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_fixed2x4 value of the text data of this element. + */ + domCg_fixed2x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_fixed2x4 reference of the _value array. + */ + domCg_fixed2x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_fixed2x4 reference of the _value array. + */ + const domCg_fixed2x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_fixed2x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFixed2x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFixed2x4() {} + /** + * Overloaded assignment operator + */ + virtual domFixed2x4 &operator=( const domFixed2x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFixed3x1; + + typedef daeSmartRef domFixed3x1Ref; + typedef daeTArray domFixed3x1_Array; + + class domFixed3x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FIXED3X1; } + static daeInt ID() { return 478; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_fixed3x1 value of the text data of this element. + */ + domCg_fixed3x1 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_fixed3x1 reference of the _value array. + */ + domCg_fixed3x1 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_fixed3x1 reference of the _value array. + */ + const domCg_fixed3x1 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_fixed3x1 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFixed3x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFixed3x1() {} + /** + * Overloaded assignment operator + */ + virtual domFixed3x1 &operator=( const domFixed3x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFixed3x2; + + typedef daeSmartRef domFixed3x2Ref; + typedef daeTArray domFixed3x2_Array; + + class domFixed3x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FIXED3X2; } + static daeInt ID() { return 479; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_fixed3x2 value of the text data of this element. + */ + domCg_fixed3x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_fixed3x2 reference of the _value array. + */ + domCg_fixed3x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_fixed3x2 reference of the _value array. + */ + const domCg_fixed3x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_fixed3x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFixed3x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFixed3x2() {} + /** + * Overloaded assignment operator + */ + virtual domFixed3x2 &operator=( const domFixed3x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFixed3x3; + + typedef daeSmartRef domFixed3x3Ref; + typedef daeTArray domFixed3x3_Array; + + class domFixed3x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FIXED3X3; } + static daeInt ID() { return 480; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_fixed3x3 value of the text data of this element. + */ + domCg_fixed3x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_fixed3x3 reference of the _value array. + */ + domCg_fixed3x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_fixed3x3 reference of the _value array. + */ + const domCg_fixed3x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_fixed3x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFixed3x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFixed3x3() {} + /** + * Overloaded assignment operator + */ + virtual domFixed3x3 &operator=( const domFixed3x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFixed3x4; + + typedef daeSmartRef domFixed3x4Ref; + typedef daeTArray domFixed3x4_Array; + + class domFixed3x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FIXED3X4; } + static daeInt ID() { return 481; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_fixed3x4 value of the text data of this element. + */ + domCg_fixed3x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_fixed3x4 reference of the _value array. + */ + domCg_fixed3x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_fixed3x4 reference of the _value array. + */ + const domCg_fixed3x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_fixed3x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFixed3x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFixed3x4() {} + /** + * Overloaded assignment operator + */ + virtual domFixed3x4 &operator=( const domFixed3x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFixed4x1; + + typedef daeSmartRef domFixed4x1Ref; + typedef daeTArray domFixed4x1_Array; + + class domFixed4x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FIXED4X1; } + static daeInt ID() { return 482; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_fixed4x1 value of the text data of this element. + */ + domCg_fixed4x1 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_fixed4x1 reference of the _value array. + */ + domCg_fixed4x1 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_fixed4x1 reference of the _value array. + */ + const domCg_fixed4x1 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_fixed4x1 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFixed4x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFixed4x1() {} + /** + * Overloaded assignment operator + */ + virtual domFixed4x1 &operator=( const domFixed4x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFixed4x2; + + typedef daeSmartRef domFixed4x2Ref; + typedef daeTArray domFixed4x2_Array; + + class domFixed4x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FIXED4X2; } + static daeInt ID() { return 483; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_fixed4x2 value of the text data of this element. + */ + domCg_fixed4x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_fixed4x2 reference of the _value array. + */ + domCg_fixed4x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_fixed4x2 reference of the _value array. + */ + const domCg_fixed4x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_fixed4x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFixed4x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFixed4x2() {} + /** + * Overloaded assignment operator + */ + virtual domFixed4x2 &operator=( const domFixed4x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFixed4x3; + + typedef daeSmartRef domFixed4x3Ref; + typedef daeTArray domFixed4x3_Array; + + class domFixed4x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FIXED4X3; } + static daeInt ID() { return 484; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_fixed4x3 value of the text data of this element. + */ + domCg_fixed4x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_fixed4x3 reference of the _value array. + */ + domCg_fixed4x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_fixed4x3 reference of the _value array. + */ + const domCg_fixed4x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_fixed4x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFixed4x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFixed4x3() {} + /** + * Overloaded assignment operator + */ + virtual domFixed4x3 &operator=( const domFixed4x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFixed4x4; + + typedef daeSmartRef domFixed4x4Ref; + typedef daeTArray domFixed4x4_Array; + + class domFixed4x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FIXED4X4; } + static daeInt ID() { return 485; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domCg_fixed4x4 value of the text data of this element. + */ + domCg_fixed4x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domCg_fixed4x4 reference of the _value array. + */ + domCg_fixed4x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domCg_fixed4x4 reference of the _value array. + */ + const domCg_fixed4x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domCg_fixed4x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFixed4x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFixed4x4() {} + /** + * Overloaded assignment operator + */ + virtual domFixed4x4 &operator=( const domFixed4x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domString; + + typedef daeSmartRef domStringRef; + typedef daeTArray domString_Array; + + class domString : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::STRING; } + static daeInt ID() { return 486; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::xsString value of the text data of this element. + */ + ::xsString _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a ::xsString of the value. + */ + ::xsString getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( ::xsString val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domString(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domString() {} + /** + * Overloaded assignment operator + */ + virtual domString &operator=( const domString &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domEnum; + + typedef daeSmartRef domEnumRef; + typedef daeTArray domEnum_Array; + + class domEnum : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ENUM; } + static daeInt ID() { return 487; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domGl_enumeration value of the text data of this element. + */ + domGl_enumeration _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domGl_enumeration of the value. + */ + domGl_enumeration getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domGl_enumeration val ) { _value = val; } + + protected: + /** + * Constructor + */ + domEnum(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domEnum() {} + /** + * Overloaded assignment operator + */ + virtual domEnum &operator=( const domEnum &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements + domBoolRef elemBool; + domBool1Ref elemBool1; + domBool2Ref elemBool2; + domBool3Ref elemBool3; + domBool4Ref elemBool4; + domBool1x1Ref elemBool1x1; + domBool1x2Ref elemBool1x2; + domBool1x3Ref elemBool1x3; + domBool1x4Ref elemBool1x4; + domBool2x1Ref elemBool2x1; + domBool2x2Ref elemBool2x2; + domBool2x3Ref elemBool2x3; + domBool2x4Ref elemBool2x4; + domBool3x1Ref elemBool3x1; + domBool3x2Ref elemBool3x2; + domBool3x3Ref elemBool3x3; + domBool3x4Ref elemBool3x4; + domBool4x1Ref elemBool4x1; + domBool4x2Ref elemBool4x2; + domBool4x3Ref elemBool4x3; + domBool4x4Ref elemBool4x4; + domFloatRef elemFloat; + domFloat1Ref elemFloat1; + domFloat2Ref elemFloat2; + domFloat3Ref elemFloat3; + domFloat4Ref elemFloat4; + domFloat1x1Ref elemFloat1x1; + domFloat1x2Ref elemFloat1x2; + domFloat1x3Ref elemFloat1x3; + domFloat1x4Ref elemFloat1x4; + domFloat2x1Ref elemFloat2x1; + domFloat2x2Ref elemFloat2x2; + domFloat2x3Ref elemFloat2x3; + domFloat2x4Ref elemFloat2x4; + domFloat3x1Ref elemFloat3x1; + domFloat3x2Ref elemFloat3x2; + domFloat3x3Ref elemFloat3x3; + domFloat3x4Ref elemFloat3x4; + domFloat4x1Ref elemFloat4x1; + domFloat4x2Ref elemFloat4x2; + domFloat4x3Ref elemFloat4x3; + domFloat4x4Ref elemFloat4x4; + domIntRef elemInt; + domInt1Ref elemInt1; + domInt2Ref elemInt2; + domInt3Ref elemInt3; + domInt4Ref elemInt4; + domInt1x1Ref elemInt1x1; + domInt1x2Ref elemInt1x2; + domInt1x3Ref elemInt1x3; + domInt1x4Ref elemInt1x4; + domInt2x1Ref elemInt2x1; + domInt2x2Ref elemInt2x2; + domInt2x3Ref elemInt2x3; + domInt2x4Ref elemInt2x4; + domInt3x1Ref elemInt3x1; + domInt3x2Ref elemInt3x2; + domInt3x3Ref elemInt3x3; + domInt3x4Ref elemInt3x4; + domInt4x1Ref elemInt4x1; + domInt4x2Ref elemInt4x2; + domInt4x3Ref elemInt4x3; + domInt4x4Ref elemInt4x4; + domHalfRef elemHalf; + domHalf1Ref elemHalf1; + domHalf2Ref elemHalf2; + domHalf3Ref elemHalf3; + domHalf4Ref elemHalf4; + domHalf1x1Ref elemHalf1x1; + domHalf1x2Ref elemHalf1x2; + domHalf1x3Ref elemHalf1x3; + domHalf1x4Ref elemHalf1x4; + domHalf2x1Ref elemHalf2x1; + domHalf2x2Ref elemHalf2x2; + domHalf2x3Ref elemHalf2x3; + domHalf2x4Ref elemHalf2x4; + domHalf3x1Ref elemHalf3x1; + domHalf3x2Ref elemHalf3x2; + domHalf3x3Ref elemHalf3x3; + domHalf3x4Ref elemHalf3x4; + domHalf4x1Ref elemHalf4x1; + domHalf4x2Ref elemHalf4x2; + domHalf4x3Ref elemHalf4x3; + domHalf4x4Ref elemHalf4x4; + domFixedRef elemFixed; + domFixed1Ref elemFixed1; + domFixed2Ref elemFixed2; + domFixed3Ref elemFixed3; + domFixed4Ref elemFixed4; + domFixed1x1Ref elemFixed1x1; + domFixed1x2Ref elemFixed1x2; + domFixed1x3Ref elemFixed1x3; + domFixed1x4Ref elemFixed1x4; + domFixed2x1Ref elemFixed2x1; + domFixed2x2Ref elemFixed2x2; + domFixed2x3Ref elemFixed2x3; + domFixed2x4Ref elemFixed2x4; + domFixed3x1Ref elemFixed3x1; + domFixed3x2Ref elemFixed3x2; + domFixed3x3Ref elemFixed3x3; + domFixed3x4Ref elemFixed3x4; + domFixed4x1Ref elemFixed4x1; + domFixed4x2Ref elemFixed4x2; + domFixed4x3Ref elemFixed4x3; + domFixed4x4Ref elemFixed4x4; + domCg_surface_typeRef elemSurface; + domCg_sampler1DRef elemSampler1D; + domCg_sampler2DRef elemSampler2D; + domCg_sampler3DRef elemSampler3D; + domCg_samplerRECTRef elemSamplerRECT; + domCg_samplerCUBERef elemSamplerCUBE; + domCg_samplerDEPTHRef elemSamplerDEPTH; + domStringRef elemString; + domEnumRef elemEnum; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the bool element. + * @return a daeSmartRef to the bool element. + */ + const domBoolRef getBool() const { return elemBool; } + /** + * Gets the bool1 element. + * @return a daeSmartRef to the bool1 element. + */ + const domBool1Ref getBool1() const { return elemBool1; } + /** + * Gets the bool2 element. + * @return a daeSmartRef to the bool2 element. + */ + const domBool2Ref getBool2() const { return elemBool2; } + /** + * Gets the bool3 element. + * @return a daeSmartRef to the bool3 element. + */ + const domBool3Ref getBool3() const { return elemBool3; } + /** + * Gets the bool4 element. + * @return a daeSmartRef to the bool4 element. + */ + const domBool4Ref getBool4() const { return elemBool4; } + /** + * Gets the bool1x1 element. + * @return a daeSmartRef to the bool1x1 element. + */ + const domBool1x1Ref getBool1x1() const { return elemBool1x1; } + /** + * Gets the bool1x2 element. + * @return a daeSmartRef to the bool1x2 element. + */ + const domBool1x2Ref getBool1x2() const { return elemBool1x2; } + /** + * Gets the bool1x3 element. + * @return a daeSmartRef to the bool1x3 element. + */ + const domBool1x3Ref getBool1x3() const { return elemBool1x3; } + /** + * Gets the bool1x4 element. + * @return a daeSmartRef to the bool1x4 element. + */ + const domBool1x4Ref getBool1x4() const { return elemBool1x4; } + /** + * Gets the bool2x1 element. + * @return a daeSmartRef to the bool2x1 element. + */ + const domBool2x1Ref getBool2x1() const { return elemBool2x1; } + /** + * Gets the bool2x2 element. + * @return a daeSmartRef to the bool2x2 element. + */ + const domBool2x2Ref getBool2x2() const { return elemBool2x2; } + /** + * Gets the bool2x3 element. + * @return a daeSmartRef to the bool2x3 element. + */ + const domBool2x3Ref getBool2x3() const { return elemBool2x3; } + /** + * Gets the bool2x4 element. + * @return a daeSmartRef to the bool2x4 element. + */ + const domBool2x4Ref getBool2x4() const { return elemBool2x4; } + /** + * Gets the bool3x1 element. + * @return a daeSmartRef to the bool3x1 element. + */ + const domBool3x1Ref getBool3x1() const { return elemBool3x1; } + /** + * Gets the bool3x2 element. + * @return a daeSmartRef to the bool3x2 element. + */ + const domBool3x2Ref getBool3x2() const { return elemBool3x2; } + /** + * Gets the bool3x3 element. + * @return a daeSmartRef to the bool3x3 element. + */ + const domBool3x3Ref getBool3x3() const { return elemBool3x3; } + /** + * Gets the bool3x4 element. + * @return a daeSmartRef to the bool3x4 element. + */ + const domBool3x4Ref getBool3x4() const { return elemBool3x4; } + /** + * Gets the bool4x1 element. + * @return a daeSmartRef to the bool4x1 element. + */ + const domBool4x1Ref getBool4x1() const { return elemBool4x1; } + /** + * Gets the bool4x2 element. + * @return a daeSmartRef to the bool4x2 element. + */ + const domBool4x2Ref getBool4x2() const { return elemBool4x2; } + /** + * Gets the bool4x3 element. + * @return a daeSmartRef to the bool4x3 element. + */ + const domBool4x3Ref getBool4x3() const { return elemBool4x3; } + /** + * Gets the bool4x4 element. + * @return a daeSmartRef to the bool4x4 element. + */ + const domBool4x4Ref getBool4x4() const { return elemBool4x4; } + /** + * Gets the float element. + * @return a daeSmartRef to the float element. + */ + const domFloatRef getFloat() const { return elemFloat; } + /** + * Gets the float1 element. + * @return a daeSmartRef to the float1 element. + */ + const domFloat1Ref getFloat1() const { return elemFloat1; } + /** + * Gets the float2 element. + * @return a daeSmartRef to the float2 element. + */ + const domFloat2Ref getFloat2() const { return elemFloat2; } + /** + * Gets the float3 element. + * @return a daeSmartRef to the float3 element. + */ + const domFloat3Ref getFloat3() const { return elemFloat3; } + /** + * Gets the float4 element. + * @return a daeSmartRef to the float4 element. + */ + const domFloat4Ref getFloat4() const { return elemFloat4; } + /** + * Gets the float1x1 element. + * @return a daeSmartRef to the float1x1 element. + */ + const domFloat1x1Ref getFloat1x1() const { return elemFloat1x1; } + /** + * Gets the float1x2 element. + * @return a daeSmartRef to the float1x2 element. + */ + const domFloat1x2Ref getFloat1x2() const { return elemFloat1x2; } + /** + * Gets the float1x3 element. + * @return a daeSmartRef to the float1x3 element. + */ + const domFloat1x3Ref getFloat1x3() const { return elemFloat1x3; } + /** + * Gets the float1x4 element. + * @return a daeSmartRef to the float1x4 element. + */ + const domFloat1x4Ref getFloat1x4() const { return elemFloat1x4; } + /** + * Gets the float2x1 element. + * @return a daeSmartRef to the float2x1 element. + */ + const domFloat2x1Ref getFloat2x1() const { return elemFloat2x1; } + /** + * Gets the float2x2 element. + * @return a daeSmartRef to the float2x2 element. + */ + const domFloat2x2Ref getFloat2x2() const { return elemFloat2x2; } + /** + * Gets the float2x3 element. + * @return a daeSmartRef to the float2x3 element. + */ + const domFloat2x3Ref getFloat2x3() const { return elemFloat2x3; } + /** + * Gets the float2x4 element. + * @return a daeSmartRef to the float2x4 element. + */ + const domFloat2x4Ref getFloat2x4() const { return elemFloat2x4; } + /** + * Gets the float3x1 element. + * @return a daeSmartRef to the float3x1 element. + */ + const domFloat3x1Ref getFloat3x1() const { return elemFloat3x1; } + /** + * Gets the float3x2 element. + * @return a daeSmartRef to the float3x2 element. + */ + const domFloat3x2Ref getFloat3x2() const { return elemFloat3x2; } + /** + * Gets the float3x3 element. + * @return a daeSmartRef to the float3x3 element. + */ + const domFloat3x3Ref getFloat3x3() const { return elemFloat3x3; } + /** + * Gets the float3x4 element. + * @return a daeSmartRef to the float3x4 element. + */ + const domFloat3x4Ref getFloat3x4() const { return elemFloat3x4; } + /** + * Gets the float4x1 element. + * @return a daeSmartRef to the float4x1 element. + */ + const domFloat4x1Ref getFloat4x1() const { return elemFloat4x1; } + /** + * Gets the float4x2 element. + * @return a daeSmartRef to the float4x2 element. + */ + const domFloat4x2Ref getFloat4x2() const { return elemFloat4x2; } + /** + * Gets the float4x3 element. + * @return a daeSmartRef to the float4x3 element. + */ + const domFloat4x3Ref getFloat4x3() const { return elemFloat4x3; } + /** + * Gets the float4x4 element. + * @return a daeSmartRef to the float4x4 element. + */ + const domFloat4x4Ref getFloat4x4() const { return elemFloat4x4; } + /** + * Gets the int element. + * @return a daeSmartRef to the int element. + */ + const domIntRef getInt() const { return elemInt; } + /** + * Gets the int1 element. + * @return a daeSmartRef to the int1 element. + */ + const domInt1Ref getInt1() const { return elemInt1; } + /** + * Gets the int2 element. + * @return a daeSmartRef to the int2 element. + */ + const domInt2Ref getInt2() const { return elemInt2; } + /** + * Gets the int3 element. + * @return a daeSmartRef to the int3 element. + */ + const domInt3Ref getInt3() const { return elemInt3; } + /** + * Gets the int4 element. + * @return a daeSmartRef to the int4 element. + */ + const domInt4Ref getInt4() const { return elemInt4; } + /** + * Gets the int1x1 element. + * @return a daeSmartRef to the int1x1 element. + */ + const domInt1x1Ref getInt1x1() const { return elemInt1x1; } + /** + * Gets the int1x2 element. + * @return a daeSmartRef to the int1x2 element. + */ + const domInt1x2Ref getInt1x2() const { return elemInt1x2; } + /** + * Gets the int1x3 element. + * @return a daeSmartRef to the int1x3 element. + */ + const domInt1x3Ref getInt1x3() const { return elemInt1x3; } + /** + * Gets the int1x4 element. + * @return a daeSmartRef to the int1x4 element. + */ + const domInt1x4Ref getInt1x4() const { return elemInt1x4; } + /** + * Gets the int2x1 element. + * @return a daeSmartRef to the int2x1 element. + */ + const domInt2x1Ref getInt2x1() const { return elemInt2x1; } + /** + * Gets the int2x2 element. + * @return a daeSmartRef to the int2x2 element. + */ + const domInt2x2Ref getInt2x2() const { return elemInt2x2; } + /** + * Gets the int2x3 element. + * @return a daeSmartRef to the int2x3 element. + */ + const domInt2x3Ref getInt2x3() const { return elemInt2x3; } + /** + * Gets the int2x4 element. + * @return a daeSmartRef to the int2x4 element. + */ + const domInt2x4Ref getInt2x4() const { return elemInt2x4; } + /** + * Gets the int3x1 element. + * @return a daeSmartRef to the int3x1 element. + */ + const domInt3x1Ref getInt3x1() const { return elemInt3x1; } + /** + * Gets the int3x2 element. + * @return a daeSmartRef to the int3x2 element. + */ + const domInt3x2Ref getInt3x2() const { return elemInt3x2; } + /** + * Gets the int3x3 element. + * @return a daeSmartRef to the int3x3 element. + */ + const domInt3x3Ref getInt3x3() const { return elemInt3x3; } + /** + * Gets the int3x4 element. + * @return a daeSmartRef to the int3x4 element. + */ + const domInt3x4Ref getInt3x4() const { return elemInt3x4; } + /** + * Gets the int4x1 element. + * @return a daeSmartRef to the int4x1 element. + */ + const domInt4x1Ref getInt4x1() const { return elemInt4x1; } + /** + * Gets the int4x2 element. + * @return a daeSmartRef to the int4x2 element. + */ + const domInt4x2Ref getInt4x2() const { return elemInt4x2; } + /** + * Gets the int4x3 element. + * @return a daeSmartRef to the int4x3 element. + */ + const domInt4x3Ref getInt4x3() const { return elemInt4x3; } + /** + * Gets the int4x4 element. + * @return a daeSmartRef to the int4x4 element. + */ + const domInt4x4Ref getInt4x4() const { return elemInt4x4; } + /** + * Gets the half element. + * @return a daeSmartRef to the half element. + */ + const domHalfRef getHalf() const { return elemHalf; } + /** + * Gets the half1 element. + * @return a daeSmartRef to the half1 element. + */ + const domHalf1Ref getHalf1() const { return elemHalf1; } + /** + * Gets the half2 element. + * @return a daeSmartRef to the half2 element. + */ + const domHalf2Ref getHalf2() const { return elemHalf2; } + /** + * Gets the half3 element. + * @return a daeSmartRef to the half3 element. + */ + const domHalf3Ref getHalf3() const { return elemHalf3; } + /** + * Gets the half4 element. + * @return a daeSmartRef to the half4 element. + */ + const domHalf4Ref getHalf4() const { return elemHalf4; } + /** + * Gets the half1x1 element. + * @return a daeSmartRef to the half1x1 element. + */ + const domHalf1x1Ref getHalf1x1() const { return elemHalf1x1; } + /** + * Gets the half1x2 element. + * @return a daeSmartRef to the half1x2 element. + */ + const domHalf1x2Ref getHalf1x2() const { return elemHalf1x2; } + /** + * Gets the half1x3 element. + * @return a daeSmartRef to the half1x3 element. + */ + const domHalf1x3Ref getHalf1x3() const { return elemHalf1x3; } + /** + * Gets the half1x4 element. + * @return a daeSmartRef to the half1x4 element. + */ + const domHalf1x4Ref getHalf1x4() const { return elemHalf1x4; } + /** + * Gets the half2x1 element. + * @return a daeSmartRef to the half2x1 element. + */ + const domHalf2x1Ref getHalf2x1() const { return elemHalf2x1; } + /** + * Gets the half2x2 element. + * @return a daeSmartRef to the half2x2 element. + */ + const domHalf2x2Ref getHalf2x2() const { return elemHalf2x2; } + /** + * Gets the half2x3 element. + * @return a daeSmartRef to the half2x3 element. + */ + const domHalf2x3Ref getHalf2x3() const { return elemHalf2x3; } + /** + * Gets the half2x4 element. + * @return a daeSmartRef to the half2x4 element. + */ + const domHalf2x4Ref getHalf2x4() const { return elemHalf2x4; } + /** + * Gets the half3x1 element. + * @return a daeSmartRef to the half3x1 element. + */ + const domHalf3x1Ref getHalf3x1() const { return elemHalf3x1; } + /** + * Gets the half3x2 element. + * @return a daeSmartRef to the half3x2 element. + */ + const domHalf3x2Ref getHalf3x2() const { return elemHalf3x2; } + /** + * Gets the half3x3 element. + * @return a daeSmartRef to the half3x3 element. + */ + const domHalf3x3Ref getHalf3x3() const { return elemHalf3x3; } + /** + * Gets the half3x4 element. + * @return a daeSmartRef to the half3x4 element. + */ + const domHalf3x4Ref getHalf3x4() const { return elemHalf3x4; } + /** + * Gets the half4x1 element. + * @return a daeSmartRef to the half4x1 element. + */ + const domHalf4x1Ref getHalf4x1() const { return elemHalf4x1; } + /** + * Gets the half4x2 element. + * @return a daeSmartRef to the half4x2 element. + */ + const domHalf4x2Ref getHalf4x2() const { return elemHalf4x2; } + /** + * Gets the half4x3 element. + * @return a daeSmartRef to the half4x3 element. + */ + const domHalf4x3Ref getHalf4x3() const { return elemHalf4x3; } + /** + * Gets the half4x4 element. + * @return a daeSmartRef to the half4x4 element. + */ + const domHalf4x4Ref getHalf4x4() const { return elemHalf4x4; } + /** + * Gets the fixed element. + * @return a daeSmartRef to the fixed element. + */ + const domFixedRef getFixed() const { return elemFixed; } + /** + * Gets the fixed1 element. + * @return a daeSmartRef to the fixed1 element. + */ + const domFixed1Ref getFixed1() const { return elemFixed1; } + /** + * Gets the fixed2 element. + * @return a daeSmartRef to the fixed2 element. + */ + const domFixed2Ref getFixed2() const { return elemFixed2; } + /** + * Gets the fixed3 element. + * @return a daeSmartRef to the fixed3 element. + */ + const domFixed3Ref getFixed3() const { return elemFixed3; } + /** + * Gets the fixed4 element. + * @return a daeSmartRef to the fixed4 element. + */ + const domFixed4Ref getFixed4() const { return elemFixed4; } + /** + * Gets the fixed1x1 element. + * @return a daeSmartRef to the fixed1x1 element. + */ + const domFixed1x1Ref getFixed1x1() const { return elemFixed1x1; } + /** + * Gets the fixed1x2 element. + * @return a daeSmartRef to the fixed1x2 element. + */ + const domFixed1x2Ref getFixed1x2() const { return elemFixed1x2; } + /** + * Gets the fixed1x3 element. + * @return a daeSmartRef to the fixed1x3 element. + */ + const domFixed1x3Ref getFixed1x3() const { return elemFixed1x3; } + /** + * Gets the fixed1x4 element. + * @return a daeSmartRef to the fixed1x4 element. + */ + const domFixed1x4Ref getFixed1x4() const { return elemFixed1x4; } + /** + * Gets the fixed2x1 element. + * @return a daeSmartRef to the fixed2x1 element. + */ + const domFixed2x1Ref getFixed2x1() const { return elemFixed2x1; } + /** + * Gets the fixed2x2 element. + * @return a daeSmartRef to the fixed2x2 element. + */ + const domFixed2x2Ref getFixed2x2() const { return elemFixed2x2; } + /** + * Gets the fixed2x3 element. + * @return a daeSmartRef to the fixed2x3 element. + */ + const domFixed2x3Ref getFixed2x3() const { return elemFixed2x3; } + /** + * Gets the fixed2x4 element. + * @return a daeSmartRef to the fixed2x4 element. + */ + const domFixed2x4Ref getFixed2x4() const { return elemFixed2x4; } + /** + * Gets the fixed3x1 element. + * @return a daeSmartRef to the fixed3x1 element. + */ + const domFixed3x1Ref getFixed3x1() const { return elemFixed3x1; } + /** + * Gets the fixed3x2 element. + * @return a daeSmartRef to the fixed3x2 element. + */ + const domFixed3x2Ref getFixed3x2() const { return elemFixed3x2; } + /** + * Gets the fixed3x3 element. + * @return a daeSmartRef to the fixed3x3 element. + */ + const domFixed3x3Ref getFixed3x3() const { return elemFixed3x3; } + /** + * Gets the fixed3x4 element. + * @return a daeSmartRef to the fixed3x4 element. + */ + const domFixed3x4Ref getFixed3x4() const { return elemFixed3x4; } + /** + * Gets the fixed4x1 element. + * @return a daeSmartRef to the fixed4x1 element. + */ + const domFixed4x1Ref getFixed4x1() const { return elemFixed4x1; } + /** + * Gets the fixed4x2 element. + * @return a daeSmartRef to the fixed4x2 element. + */ + const domFixed4x2Ref getFixed4x2() const { return elemFixed4x2; } + /** + * Gets the fixed4x3 element. + * @return a daeSmartRef to the fixed4x3 element. + */ + const domFixed4x3Ref getFixed4x3() const { return elemFixed4x3; } + /** + * Gets the fixed4x4 element. + * @return a daeSmartRef to the fixed4x4 element. + */ + const domFixed4x4Ref getFixed4x4() const { return elemFixed4x4; } + /** + * Gets the surface element. + * @return a daeSmartRef to the surface element. + */ + const domCg_surface_typeRef getSurface() const { return elemSurface; } + /** + * Gets the sampler1D element. + * @return a daeSmartRef to the sampler1D element. + */ + const domCg_sampler1DRef getSampler1D() const { return elemSampler1D; } + /** + * Gets the sampler2D element. + * @return a daeSmartRef to the sampler2D element. + */ + const domCg_sampler2DRef getSampler2D() const { return elemSampler2D; } + /** + * Gets the sampler3D element. + * @return a daeSmartRef to the sampler3D element. + */ + const domCg_sampler3DRef getSampler3D() const { return elemSampler3D; } + /** + * Gets the samplerRECT element. + * @return a daeSmartRef to the samplerRECT element. + */ + const domCg_samplerRECTRef getSamplerRECT() const { return elemSamplerRECT; } + /** + * Gets the samplerCUBE element. + * @return a daeSmartRef to the samplerCUBE element. + */ + const domCg_samplerCUBERef getSamplerCUBE() const { return elemSamplerCUBE; } + /** + * Gets the samplerDEPTH element. + * @return a daeSmartRef to the samplerDEPTH element. + */ + const domCg_samplerDEPTHRef getSamplerDEPTH() const { return elemSamplerDEPTH; } + /** + * Gets the string element. + * @return a daeSmartRef to the string element. + */ + const domStringRef getString() const { return elemString; } + /** + * Gets the enum element. + * @return a daeSmartRef to the enum element. + */ + const domEnumRef getEnum() const { return elemEnum; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domCg_param_type(DAE& dae) : daeElement(dae), elemBool(), elemBool1(), elemBool2(), elemBool3(), elemBool4(), elemBool1x1(), elemBool1x2(), elemBool1x3(), elemBool1x4(), elemBool2x1(), elemBool2x2(), elemBool2x3(), elemBool2x4(), elemBool3x1(), elemBool3x2(), elemBool3x3(), elemBool3x4(), elemBool4x1(), elemBool4x2(), elemBool4x3(), elemBool4x4(), elemFloat(), elemFloat1(), elemFloat2(), elemFloat3(), elemFloat4(), elemFloat1x1(), elemFloat1x2(), elemFloat1x3(), elemFloat1x4(), elemFloat2x1(), elemFloat2x2(), elemFloat2x3(), elemFloat2x4(), elemFloat3x1(), elemFloat3x2(), elemFloat3x3(), elemFloat3x4(), elemFloat4x1(), elemFloat4x2(), elemFloat4x3(), elemFloat4x4(), elemInt(), elemInt1(), elemInt2(), elemInt3(), elemInt4(), elemInt1x1(), elemInt1x2(), elemInt1x3(), elemInt1x4(), elemInt2x1(), elemInt2x2(), elemInt2x3(), elemInt2x4(), elemInt3x1(), elemInt3x2(), elemInt3x3(), elemInt3x4(), elemInt4x1(), elemInt4x2(), elemInt4x3(), elemInt4x4(), elemHalf(), elemHalf1(), elemHalf2(), elemHalf3(), elemHalf4(), elemHalf1x1(), elemHalf1x2(), elemHalf1x3(), elemHalf1x4(), elemHalf2x1(), elemHalf2x2(), elemHalf2x3(), elemHalf2x4(), elemHalf3x1(), elemHalf3x2(), elemHalf3x3(), elemHalf3x4(), elemHalf4x1(), elemHalf4x2(), elemHalf4x3(), elemHalf4x4(), elemFixed(), elemFixed1(), elemFixed2(), elemFixed3(), elemFixed4(), elemFixed1x1(), elemFixed1x2(), elemFixed1x3(), elemFixed1x4(), elemFixed2x1(), elemFixed2x2(), elemFixed2x3(), elemFixed2x4(), elemFixed3x1(), elemFixed3x2(), elemFixed3x3(), elemFixed3x4(), elemFixed4x1(), elemFixed4x2(), elemFixed4x3(), elemFixed4x4(), elemSurface(), elemSampler1D(), elemSampler2D(), elemSampler3D(), elemSamplerRECT(), elemSamplerCUBE(), elemSamplerDEPTH(), elemString(), elemEnum() {} + /** + * Destructor + */ + virtual ~domCg_param_type() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domCg_param_type &operator=( const domCg_param_type &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domCg_sampler1D.h b/Engine/lib/collada/include/1.4/dom/domCg_sampler1D.h new file mode 100644 index 000000000..8df40d5ae --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domCg_sampler1D.h @@ -0,0 +1,80 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domCg_sampler1D_h__ +#define __domCg_sampler1D_h__ + +#include +#include +#include + +#include +class DAE; + +class domCg_sampler1D_complexType : public domFx_sampler1D_common_complexType +{ + +protected: + /** + * Constructor + */ + domCg_sampler1D_complexType(DAE& dae, daeElement* elt) : domFx_sampler1D_common_complexType(dae, elt) {} + /** + * Destructor + */ + virtual ~domCg_sampler1D_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domCg_sampler1D_complexType &operator=( const domCg_sampler1D_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domCg_sampler1D_complexType. + */ +class domCg_sampler1D : public daeElement, public domCg_sampler1D_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CG_SAMPLER1D; } + static daeInt ID() { return 127; } + virtual daeInt typeID() const { return ID(); } +protected: + /** + * Constructor + */ + domCg_sampler1D(DAE& dae) : daeElement(dae), domCg_sampler1D_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domCg_sampler1D() {} + /** + * Overloaded assignment operator + */ + virtual domCg_sampler1D &operator=( const domCg_sampler1D &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domCg_sampler2D.h b/Engine/lib/collada/include/1.4/dom/domCg_sampler2D.h new file mode 100644 index 000000000..b895c67f5 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domCg_sampler2D.h @@ -0,0 +1,80 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domCg_sampler2D_h__ +#define __domCg_sampler2D_h__ + +#include +#include +#include + +#include +class DAE; + +class domCg_sampler2D_complexType : public domFx_sampler2D_common_complexType +{ + +protected: + /** + * Constructor + */ + domCg_sampler2D_complexType(DAE& dae, daeElement* elt) : domFx_sampler2D_common_complexType(dae, elt) {} + /** + * Destructor + */ + virtual ~domCg_sampler2D_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domCg_sampler2D_complexType &operator=( const domCg_sampler2D_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domCg_sampler2D_complexType. + */ +class domCg_sampler2D : public daeElement, public domCg_sampler2D_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CG_SAMPLER2D; } + static daeInt ID() { return 128; } + virtual daeInt typeID() const { return ID(); } +protected: + /** + * Constructor + */ + domCg_sampler2D(DAE& dae) : daeElement(dae), domCg_sampler2D_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domCg_sampler2D() {} + /** + * Overloaded assignment operator + */ + virtual domCg_sampler2D &operator=( const domCg_sampler2D &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domCg_sampler3D.h b/Engine/lib/collada/include/1.4/dom/domCg_sampler3D.h new file mode 100644 index 000000000..f09a32ff2 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domCg_sampler3D.h @@ -0,0 +1,80 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domCg_sampler3D_h__ +#define __domCg_sampler3D_h__ + +#include +#include +#include + +#include +class DAE; + +class domCg_sampler3D_complexType : public domFx_sampler3D_common_complexType +{ + +protected: + /** + * Constructor + */ + domCg_sampler3D_complexType(DAE& dae, daeElement* elt) : domFx_sampler3D_common_complexType(dae, elt) {} + /** + * Destructor + */ + virtual ~domCg_sampler3D_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domCg_sampler3D_complexType &operator=( const domCg_sampler3D_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domCg_sampler3D_complexType. + */ +class domCg_sampler3D : public daeElement, public domCg_sampler3D_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CG_SAMPLER3D; } + static daeInt ID() { return 129; } + virtual daeInt typeID() const { return ID(); } +protected: + /** + * Constructor + */ + domCg_sampler3D(DAE& dae) : daeElement(dae), domCg_sampler3D_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domCg_sampler3D() {} + /** + * Overloaded assignment operator + */ + virtual domCg_sampler3D &operator=( const domCg_sampler3D &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domCg_samplerCUBE.h b/Engine/lib/collada/include/1.4/dom/domCg_samplerCUBE.h new file mode 100644 index 000000000..73aaccde6 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domCg_samplerCUBE.h @@ -0,0 +1,80 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domCg_samplerCUBE_h__ +#define __domCg_samplerCUBE_h__ + +#include +#include +#include + +#include +class DAE; + +class domCg_samplerCUBE_complexType : public domFx_samplerCUBE_common_complexType +{ + +protected: + /** + * Constructor + */ + domCg_samplerCUBE_complexType(DAE& dae, daeElement* elt) : domFx_samplerCUBE_common_complexType(dae, elt) {} + /** + * Destructor + */ + virtual ~domCg_samplerCUBE_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domCg_samplerCUBE_complexType &operator=( const domCg_samplerCUBE_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domCg_samplerCUBE_complexType. + */ +class domCg_samplerCUBE : public daeElement, public domCg_samplerCUBE_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CG_SAMPLERCUBE; } + static daeInt ID() { return 130; } + virtual daeInt typeID() const { return ID(); } +protected: + /** + * Constructor + */ + domCg_samplerCUBE(DAE& dae) : daeElement(dae), domCg_samplerCUBE_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domCg_samplerCUBE() {} + /** + * Overloaded assignment operator + */ + virtual domCg_samplerCUBE &operator=( const domCg_samplerCUBE &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domCg_samplerDEPTH.h b/Engine/lib/collada/include/1.4/dom/domCg_samplerDEPTH.h new file mode 100644 index 000000000..79cd9c641 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domCg_samplerDEPTH.h @@ -0,0 +1,80 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domCg_samplerDEPTH_h__ +#define __domCg_samplerDEPTH_h__ + +#include +#include +#include + +#include +class DAE; + +class domCg_samplerDEPTH_complexType : public domFx_samplerDEPTH_common_complexType +{ + +protected: + /** + * Constructor + */ + domCg_samplerDEPTH_complexType(DAE& dae, daeElement* elt) : domFx_samplerDEPTH_common_complexType(dae, elt) {} + /** + * Destructor + */ + virtual ~domCg_samplerDEPTH_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domCg_samplerDEPTH_complexType &operator=( const domCg_samplerDEPTH_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domCg_samplerDEPTH_complexType. + */ +class domCg_samplerDEPTH : public daeElement, public domCg_samplerDEPTH_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CG_SAMPLERDEPTH; } + static daeInt ID() { return 132; } + virtual daeInt typeID() const { return ID(); } +protected: + /** + * Constructor + */ + domCg_samplerDEPTH(DAE& dae) : daeElement(dae), domCg_samplerDEPTH_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domCg_samplerDEPTH() {} + /** + * Overloaded assignment operator + */ + virtual domCg_samplerDEPTH &operator=( const domCg_samplerDEPTH &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domCg_samplerRECT.h b/Engine/lib/collada/include/1.4/dom/domCg_samplerRECT.h new file mode 100644 index 000000000..04c9b480f --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domCg_samplerRECT.h @@ -0,0 +1,80 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domCg_samplerRECT_h__ +#define __domCg_samplerRECT_h__ + +#include +#include +#include + +#include +class DAE; + +class domCg_samplerRECT_complexType : public domFx_samplerRECT_common_complexType +{ + +protected: + /** + * Constructor + */ + domCg_samplerRECT_complexType(DAE& dae, daeElement* elt) : domFx_samplerRECT_common_complexType(dae, elt) {} + /** + * Destructor + */ + virtual ~domCg_samplerRECT_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domCg_samplerRECT_complexType &operator=( const domCg_samplerRECT_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domCg_samplerRECT_complexType. + */ +class domCg_samplerRECT : public daeElement, public domCg_samplerRECT_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CG_SAMPLERRECT; } + static daeInt ID() { return 131; } + virtual daeInt typeID() const { return ID(); } +protected: + /** + * Constructor + */ + domCg_samplerRECT(DAE& dae) : daeElement(dae), domCg_samplerRECT_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domCg_samplerRECT() {} + /** + * Overloaded assignment operator + */ + virtual domCg_samplerRECT &operator=( const domCg_samplerRECT &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domCg_setarray_type.h b/Engine/lib/collada/include/1.4/dom/domCg_setarray_type.h new file mode 100644 index 000000000..c69901188 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domCg_setarray_type.h @@ -0,0 +1,182 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domCg_setarray_type_h__ +#define __domCg_setarray_type_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * Creates a parameter of a one-dimensional array type. + */ +class domCg_setarray_type_complexType +{ +protected: // Attribute +/** + * The length attribute specifies the length of the array. + */ + xsPositiveInteger attrLength; + +protected: // Elements + domCg_param_type_Array elemCg_param_type_array; +/** + * Nested array elements allow you to create multidemensional arrays. @see + * domArray + */ + domCg_setarray_type_Array elemArray_array; +/** + * The usertype element allows you to create arrays of usertypes. @see domUsertype + */ + domCg_setuser_type_Array elemUsertype_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the length attribute. + * @return Returns a xsPositiveInteger of the length attribute. + */ + xsPositiveInteger getLength() const { return attrLength; } + /** + * Sets the length attribute. + * @param atLength The new value for the length attribute. + */ + void setLength( xsPositiveInteger atLength ) { attrLength = atLength; } + + /** + * Gets the cg_param_type element array. + * @return Returns a reference to the array of cg_param_type elements. + */ + domCg_param_type_Array &getCg_param_type_array() { return elemCg_param_type_array; } + /** + * Gets the cg_param_type element array. + * @return Returns a constant reference to the array of cg_param_type elements. + */ + const domCg_param_type_Array &getCg_param_type_array() const { return elemCg_param_type_array; } + /** + * Gets the array element array. + * @return Returns a reference to the array of array elements. + */ + domCg_setarray_type_Array &getArray_array() { return elemArray_array; } + /** + * Gets the array element array. + * @return Returns a constant reference to the array of array elements. + */ + const domCg_setarray_type_Array &getArray_array() const { return elemArray_array; } + /** + * Gets the usertype element array. + * @return Returns a reference to the array of usertype elements. + */ + domCg_setuser_type_Array &getUsertype_array() { return elemUsertype_array; } + /** + * Gets the usertype element array. + * @return Returns a constant reference to the array of usertype elements. + */ + const domCg_setuser_type_Array &getUsertype_array() const { return elemUsertype_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domCg_setarray_type_complexType(DAE& dae, daeElement* elt) : attrLength(), elemCg_param_type_array(), elemArray_array(), elemUsertype_array() {} + /** + * Destructor + */ + virtual ~domCg_setarray_type_complexType() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domCg_setarray_type_complexType &operator=( const domCg_setarray_type_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domCg_setarray_type_complexType. + */ +class domCg_setarray_type : public daeElement, public domCg_setarray_type_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CG_SETARRAY_TYPE; } + static daeInt ID() { return 135; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the length attribute. + * @return Returns a xsPositiveInteger of the length attribute. + */ + xsPositiveInteger getLength() const { return attrLength; } + /** + * Sets the length attribute. + * @param atLength The new value for the length attribute. + */ + void setLength( xsPositiveInteger atLength ) { attrLength = atLength; _validAttributeArray[0] = true; } + +protected: + /** + * Constructor + */ + domCg_setarray_type(DAE& dae) : daeElement(dae), domCg_setarray_type_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domCg_setarray_type() {} + /** + * Overloaded assignment operator + */ + virtual domCg_setarray_type &operator=( const domCg_setarray_type &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domCg_setparam.h b/Engine/lib/collada/include/1.4/dom/domCg_setparam.h new file mode 100644 index 000000000..47830828d --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domCg_setparam.h @@ -0,0 +1,187 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domCg_setparam_h__ +#define __domCg_setparam_h__ + +#include +#include +#include + +#include +#include +#include +#include +class DAE; + +/** + * Assigns a new value to a previously defined parameter. + */ +class domCg_setparam_complexType +{ +protected: // Attributes + domCg_identifier attrRef; + xsNCName attrProgram; + +protected: // Elements + domCg_param_typeRef elemCg_param_type; + domCg_setuser_typeRef elemUsertype; + domCg_setarray_typeRef elemArray; + domCg_connect_paramRef elemConnect_param; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the ref attribute. + * @return Returns a domCg_identifier of the ref attribute. + */ + domCg_identifier getRef() const { return attrRef; } + /** + * Sets the ref attribute. + * @param atRef The new value for the ref attribute. + */ + void setRef( domCg_identifier atRef ) { attrRef = atRef; } + + /** + * Gets the program attribute. + * @return Returns a xsNCName of the program attribute. + */ + xsNCName getProgram() const { return attrProgram; } + /** + * Sets the program attribute. + * @param atProgram The new value for the program attribute. + */ + void setProgram( xsNCName atProgram ) { *(daeStringRef*)&attrProgram = atProgram;} + + /** + * Gets the cg_param_type element. + * @return a daeSmartRef to the cg_param_type element. + */ + const domCg_param_typeRef getCg_param_type() const { return elemCg_param_type; } + /** + * Gets the usertype element. + * @return a daeSmartRef to the usertype element. + */ + const domCg_setuser_typeRef getUsertype() const { return elemUsertype; } + /** + * Gets the array element. + * @return a daeSmartRef to the array element. + */ + const domCg_setarray_typeRef getArray() const { return elemArray; } + /** + * Gets the connect_param element. + * @return a daeSmartRef to the connect_param element. + */ + const domCg_connect_paramRef getConnect_param() const { return elemConnect_param; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domCg_setparam_complexType(DAE& dae, daeElement* elt) : attrRef(), attrProgram(), elemCg_param_type(), elemUsertype(), elemArray(), elemConnect_param() {} + /** + * Destructor + */ + virtual ~domCg_setparam_complexType() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domCg_setparam_complexType &operator=( const domCg_setparam_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domCg_setparam_complexType. + */ +class domCg_setparam : public daeElement, public domCg_setparam_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CG_SETPARAM; } + static daeInt ID() { return 144; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the ref attribute. + * @return Returns a domCg_identifier of the ref attribute. + */ + domCg_identifier getRef() const { return attrRef; } + /** + * Sets the ref attribute. + * @param atRef The new value for the ref attribute. + */ + void setRef( domCg_identifier atRef ) { attrRef = atRef; _validAttributeArray[0] = true; } + + /** + * Gets the program attribute. + * @return Returns a xsNCName of the program attribute. + */ + xsNCName getProgram() const { return attrProgram; } + /** + * Sets the program attribute. + * @param atProgram The new value for the program attribute. + */ + void setProgram( xsNCName atProgram ) { *(daeStringRef*)&attrProgram = atProgram; _validAttributeArray[1] = true; } + +protected: + /** + * Constructor + */ + domCg_setparam(DAE& dae) : daeElement(dae), domCg_setparam_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domCg_setparam() {} + /** + * Overloaded assignment operator + */ + virtual domCg_setparam &operator=( const domCg_setparam &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domCg_setparam_simple.h b/Engine/lib/collada/include/1.4/dom/domCg_setparam_simple.h new file mode 100644 index 000000000..bb692d17f --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domCg_setparam_simple.h @@ -0,0 +1,127 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domCg_setparam_simple_h__ +#define __domCg_setparam_simple_h__ + +#include +#include +#include + +#include +#include +class DAE; + +class domCg_setparam_simple_complexType +{ +protected: // Attribute + domCg_identifier attrRef; + +protected: // Elements + domFx_annotate_common_Array elemAnnotate_array; + domCg_param_typeRef elemCg_param_type; + +public: //Accessors and Mutators + /** + * Gets the ref attribute. + * @return Returns a domCg_identifier of the ref attribute. + */ + domCg_identifier getRef() const { return attrRef; } + /** + * Sets the ref attribute. + * @param atRef The new value for the ref attribute. + */ + void setRef( domCg_identifier atRef ) { attrRef = atRef; } + + /** + * Gets the annotate element array. + * @return Returns a reference to the array of annotate elements. + */ + domFx_annotate_common_Array &getAnnotate_array() { return elemAnnotate_array; } + /** + * Gets the annotate element array. + * @return Returns a constant reference to the array of annotate elements. + */ + const domFx_annotate_common_Array &getAnnotate_array() const { return elemAnnotate_array; } + /** + * Gets the cg_param_type element. + * @return a daeSmartRef to the cg_param_type element. + */ + const domCg_param_typeRef getCg_param_type() const { return elemCg_param_type; } +protected: + /** + * Constructor + */ + domCg_setparam_simple_complexType(DAE& dae, daeElement* elt) : attrRef(), elemAnnotate_array(), elemCg_param_type() {} + /** + * Destructor + */ + virtual ~domCg_setparam_simple_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domCg_setparam_simple_complexType &operator=( const domCg_setparam_simple_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domCg_setparam_simple_complexType. + */ +class domCg_setparam_simple : public daeElement, public domCg_setparam_simple_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CG_SETPARAM_SIMPLE; } + static daeInt ID() { return 143; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the ref attribute. + * @return Returns a domCg_identifier of the ref attribute. + */ + domCg_identifier getRef() const { return attrRef; } + /** + * Sets the ref attribute. + * @param atRef The new value for the ref attribute. + */ + void setRef( domCg_identifier atRef ) { attrRef = atRef; _validAttributeArray[0] = true; } + +protected: + /** + * Constructor + */ + domCg_setparam_simple(DAE& dae) : daeElement(dae), domCg_setparam_simple_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domCg_setparam_simple() {} + /** + * Overloaded assignment operator + */ + virtual domCg_setparam_simple &operator=( const domCg_setparam_simple &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domCg_setuser_type.h b/Engine/lib/collada/include/1.4/dom/domCg_setuser_type.h new file mode 100644 index 000000000..dd711acaf --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domCg_setuser_type.h @@ -0,0 +1,226 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domCg_setuser_type_h__ +#define __domCg_setuser_type_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include +class DAE; + +/** + * Creates an instance of a structured class. + */ +class domCg_setuser_type_complexType +{ +protected: // Attributes + domCg_identifier attrName; +/** + * Reference a code or include element which defines the usertype + */ + xsNCName attrSource; + +protected: // Elements + domCg_param_type_Array elemCg_param_type_array; + domCg_setarray_type_Array elemArray_array; + domCg_setuser_type_Array elemUsertype_array; + domCg_connect_param_Array elemConnect_param_array; +/** + * Use a series of these to set the members by name. The ref attribute will + * be relative to the usertype you are in right now. @see domSetparam + */ + domCg_setparam_Array elemSetparam_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the name attribute. + * @return Returns a domCg_identifier of the name attribute. + */ + domCg_identifier getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( domCg_identifier atName ) { attrName = atName; } + + /** + * Gets the source attribute. + * @return Returns a xsNCName of the source attribute. + */ + xsNCName getSource() const { return attrSource; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( xsNCName atSource ) { *(daeStringRef*)&attrSource = atSource;} + + /** + * Gets the cg_param_type element array. + * @return Returns a reference to the array of cg_param_type elements. + */ + domCg_param_type_Array &getCg_param_type_array() { return elemCg_param_type_array; } + /** + * Gets the cg_param_type element array. + * @return Returns a constant reference to the array of cg_param_type elements. + */ + const domCg_param_type_Array &getCg_param_type_array() const { return elemCg_param_type_array; } + /** + * Gets the array element array. + * @return Returns a reference to the array of array elements. + */ + domCg_setarray_type_Array &getArray_array() { return elemArray_array; } + /** + * Gets the array element array. + * @return Returns a constant reference to the array of array elements. + */ + const domCg_setarray_type_Array &getArray_array() const { return elemArray_array; } + /** + * Gets the usertype element array. + * @return Returns a reference to the array of usertype elements. + */ + domCg_setuser_type_Array &getUsertype_array() { return elemUsertype_array; } + /** + * Gets the usertype element array. + * @return Returns a constant reference to the array of usertype elements. + */ + const domCg_setuser_type_Array &getUsertype_array() const { return elemUsertype_array; } + /** + * Gets the connect_param element array. + * @return Returns a reference to the array of connect_param elements. + */ + domCg_connect_param_Array &getConnect_param_array() { return elemConnect_param_array; } + /** + * Gets the connect_param element array. + * @return Returns a constant reference to the array of connect_param elements. + */ + const domCg_connect_param_Array &getConnect_param_array() const { return elemConnect_param_array; } + /** + * Gets the setparam element array. + * @return Returns a reference to the array of setparam elements. + */ + domCg_setparam_Array &getSetparam_array() { return elemSetparam_array; } + /** + * Gets the setparam element array. + * @return Returns a constant reference to the array of setparam elements. + */ + const domCg_setparam_Array &getSetparam_array() const { return elemSetparam_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domCg_setuser_type_complexType(DAE& dae, daeElement* elt) : attrName(), attrSource(), elemCg_param_type_array(), elemArray_array(), elemUsertype_array(), elemConnect_param_array(), elemSetparam_array() {} + /** + * Destructor + */ + virtual ~domCg_setuser_type_complexType() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domCg_setuser_type_complexType &operator=( const domCg_setuser_type_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domCg_setuser_type_complexType. + */ +class domCg_setuser_type : public daeElement, public domCg_setuser_type_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CG_SETUSER_TYPE; } + static daeInt ID() { return 136; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the name attribute. + * @return Returns a domCg_identifier of the name attribute. + */ + domCg_identifier getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( domCg_identifier atName ) { attrName = atName; _validAttributeArray[0] = true; } + + /** + * Gets the source attribute. + * @return Returns a xsNCName of the source attribute. + */ + xsNCName getSource() const { return attrSource; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( xsNCName atSource ) { *(daeStringRef*)&attrSource = atSource; _validAttributeArray[1] = true; } + +protected: + /** + * Constructor + */ + domCg_setuser_type(DAE& dae) : daeElement(dae), domCg_setuser_type_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domCg_setuser_type() {} + /** + * Overloaded assignment operator + */ + virtual domCg_setuser_type &operator=( const domCg_setuser_type &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domCg_surface_type.h b/Engine/lib/collada/include/1.4/dom/domCg_surface_type.h new file mode 100644 index 000000000..fc2052439 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domCg_surface_type.h @@ -0,0 +1,317 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domCg_surface_type_h__ +#define __domCg_surface_type_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include +class DAE; + +/** + * Declares a resource that can be used both as the source for texture samples + * and as the target of a rendering pass. + */ +class domCg_surface_type_complexType : public domFx_surface_common_complexType +{ +public: + class domGenerator; + + typedef daeSmartRef domGeneratorRef; + typedef daeTArray domGenerator_Array; + +/** + * A procedural surface generator for the cg profile. + */ + class domGenerator : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GENERATOR; } + static daeInt ID() { return 137; } + virtual daeInt typeID() const { return ID(); } + public: + class domName; + + typedef daeSmartRef domNameRef; + typedef daeTArray domName_Array; + +/** + * The entry symbol for the shader function. + */ + class domName : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::NAME; } + static daeInt ID() { return 138; } + virtual daeInt typeID() const { return ID(); } + protected: // Attribute + xsNCName attrSource; + + protected: // Value + /** + * The xsNCName value of the text data of this element. + */ + xsNCName _value; + + public: //Accessors and Mutators + /** + * Gets the source attribute. + * @return Returns a xsNCName of the source attribute. + */ + xsNCName getSource() const { return attrSource; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( xsNCName atSource ) { *(daeStringRef*)&attrSource = atSource; _validAttributeArray[0] = true; } + + /** + * Gets the value of this element. + * @return Returns a xsNCName of the value. + */ + xsNCName getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNCName val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domName(DAE& dae) : daeElement(dae), attrSource(), _value() {} + /** + * Destructor + */ + virtual ~domName() {} + /** + * Overloaded assignment operator + */ + virtual domName &operator=( const domName &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements +/** + * The annotate element allows you to specify an annotation for this generator. + * @see domAnnotate + */ + domFx_annotate_common_Array elemAnnotate_array; +/** + * The code element allows you to embed cg sourcecode for the surface generator. + * @see domCode + */ + domFx_code_profile_Array elemCode_array; +/** + * The include element imports cg source code or precompiled binary shaders + * into the FX Runtime by referencing an external resource. @see domInclude + */ + domFx_include_common_Array elemInclude_array; +/** + * The entry symbol for the shader function. @see domName + */ + domNameRef elemName; +/** + * Assigns a new value to a previously defined parameter. @see domSetparam + */ + domCg_setparam_simple_Array elemSetparam_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the annotate element array. + * @return Returns a reference to the array of annotate elements. + */ + domFx_annotate_common_Array &getAnnotate_array() { return elemAnnotate_array; } + /** + * Gets the annotate element array. + * @return Returns a constant reference to the array of annotate elements. + */ + const domFx_annotate_common_Array &getAnnotate_array() const { return elemAnnotate_array; } + /** + * Gets the code element array. + * @return Returns a reference to the array of code elements. + */ + domFx_code_profile_Array &getCode_array() { return elemCode_array; } + /** + * Gets the code element array. + * @return Returns a constant reference to the array of code elements. + */ + const domFx_code_profile_Array &getCode_array() const { return elemCode_array; } + /** + * Gets the include element array. + * @return Returns a reference to the array of include elements. + */ + domFx_include_common_Array &getInclude_array() { return elemInclude_array; } + /** + * Gets the include element array. + * @return Returns a constant reference to the array of include elements. + */ + const domFx_include_common_Array &getInclude_array() const { return elemInclude_array; } + /** + * Gets the name element. + * @return a daeSmartRef to the name element. + */ + const domNameRef getName() const { return elemName; } + /** + * Gets the setparam element array. + * @return Returns a reference to the array of setparam elements. + */ + domCg_setparam_simple_Array &getSetparam_array() { return elemSetparam_array; } + /** + * Gets the setparam element array. + * @return Returns a constant reference to the array of setparam elements. + */ + const domCg_setparam_simple_Array &getSetparam_array() const { return elemSetparam_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domGenerator(DAE& dae) : daeElement(dae), elemAnnotate_array(), elemCode_array(), elemInclude_array(), elemName(), elemSetparam_array() {} + /** + * Destructor + */ + virtual ~domGenerator() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domGenerator &operator=( const domGenerator &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Element +/** + * A procedural surface generator for the cg profile. @see domGenerator + */ + domGeneratorRef elemGenerator; + +public: //Accessors and Mutators + /** + * Gets the generator element. + * @return a daeSmartRef to the generator element. + */ + const domGeneratorRef getGenerator() const { return elemGenerator; } +protected: + /** + * Constructor + */ + domCg_surface_type_complexType(DAE& dae, daeElement* elt) : domFx_surface_common_complexType(dae, elt), elemGenerator() {} + /** + * Destructor + */ + virtual ~domCg_surface_type_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domCg_surface_type_complexType &operator=( const domCg_surface_type_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domCg_surface_type_complexType. + */ +class domCg_surface_type : public daeElement, public domCg_surface_type_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CG_SURFACE_TYPE; } + static daeInt ID() { return 139; } + virtual daeInt typeID() const { return ID(); } +protected: + /** + * Constructor + */ + domCg_surface_type(DAE& dae) : daeElement(dae), domCg_surface_type_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domCg_surface_type() {} + /** + * Overloaded assignment operator + */ + virtual domCg_surface_type &operator=( const domCg_surface_type &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domChannel.h b/Engine/lib/collada/include/1.4/dom/domChannel.h new file mode 100644 index 000000000..3edda0ec7 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domChannel.h @@ -0,0 +1,109 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domChannel_h__ +#define __domChannel_h__ + +#include +#include +#include + +class DAE; + +/** + * The channel element declares an output channel of an animation. + */ +class domChannel : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CHANNEL; } + static daeInt ID() { return 653; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The source attribute indicates the location of the sampler using a URL + * expression. The sampler must be declared within the same document. Required + * attribute. + */ + domURIFragmentType attrSource; +/** + * The target attribute indicates the location of the element bound to the + * output of the sampler. This text string is a path-name following a simple + * syntax described in Address Syntax. Required attribute. + */ + xsToken attrTarget; + + +public: //Accessors and Mutators + /** + * Gets the source attribute. + * @return Returns a domURIFragmentType reference of the source attribute. + */ + domURIFragmentType &getSource() { return attrSource; } + /** + * Gets the source attribute. + * @return Returns a constant domURIFragmentType reference of the source attribute. + */ + const domURIFragmentType &getSource() const { return attrSource; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( const domURIFragmentType &atSource ) { attrSource = atSource; _validAttributeArray[0] = true; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( xsString atSource ) { attrSource = atSource; _validAttributeArray[0] = true; } + + /** + * Gets the target attribute. + * @return Returns a xsToken of the target attribute. + */ + xsToken getTarget() const { return attrTarget; } + /** + * Sets the target attribute. + * @param atTarget The new value for the target attribute. + */ + void setTarget( xsToken atTarget ) { *(daeStringRef*)&attrTarget = atTarget; _validAttributeArray[1] = true; } + +protected: + /** + * Constructor + */ + domChannel(DAE& dae) : daeElement(dae), attrSource(dae, *this), attrTarget() {} + /** + * Destructor + */ + virtual ~domChannel() {} + /** + * Overloaded assignment operator + */ + virtual domChannel &operator=( const domChannel &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domCommon_color_or_texture_type.h b/Engine/lib/collada/include/1.4/dom/domCommon_color_or_texture_type.h new file mode 100644 index 000000000..2958e58c3 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domCommon_color_or_texture_type.h @@ -0,0 +1,333 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domCommon_color_or_texture_type_h__ +#define __domCommon_color_or_texture_type_h__ + +#include +#include +#include + +#include +class DAE; + +class domCommon_color_or_texture_type_complexType +{ +public: + class domColor; + + typedef daeSmartRef domColorRef; + typedef daeTArray domColor_Array; + + class domColor : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::COLOR; } + static daeInt ID() { return 116; } + virtual daeInt typeID() const { return ID(); } + protected: // Attribute + xsNCName attrSid; + + protected: // Value + /** + * The domFx_color_common value of the text data of this element. + */ + domFx_color_common _value; + + public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + + /** + * Gets the _value array. + * @return Returns a domFx_color_common reference of the _value array. + */ + domFx_color_common &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domFx_color_common reference of the _value array. + */ + const domFx_color_common &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domFx_color_common &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domColor(DAE& dae) : daeElement(dae), attrSid(), _value() {} + /** + * Destructor + */ + virtual ~domColor() {} + /** + * Overloaded assignment operator + */ + virtual domColor &operator=( const domColor &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domParam; + + typedef daeSmartRef domParamRef; + typedef daeTArray domParam_Array; + + class domParam : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PARAM; } + static daeInt ID() { return 117; } + virtual daeInt typeID() const { return ID(); } + protected: // Attribute + xsNCName attrRef; + + + public: //Accessors and Mutators + /** + * Gets the ref attribute. + * @return Returns a xsNCName of the ref attribute. + */ + xsNCName getRef() const { return attrRef; } + /** + * Sets the ref attribute. + * @param atRef The new value for the ref attribute. + */ + void setRef( xsNCName atRef ) { *(daeStringRef*)&attrRef = atRef; _validAttributeArray[0] = true; } + + protected: + /** + * Constructor + */ + domParam(DAE& dae) : daeElement(dae), attrRef() {} + /** + * Destructor + */ + virtual ~domParam() {} + /** + * Overloaded assignment operator + */ + virtual domParam &operator=( const domParam &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domTexture; + + typedef daeSmartRef domTextureRef; + typedef daeTArray domTexture_Array; + + class domTexture : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TEXTURE; } + static daeInt ID() { return 118; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + xsNCName attrTexture; + xsNCName attrTexcoord; + + protected: // Element + domExtraRef elemExtra; + + public: //Accessors and Mutators + /** + * Gets the texture attribute. + * @return Returns a xsNCName of the texture attribute. + */ + xsNCName getTexture() const { return attrTexture; } + /** + * Sets the texture attribute. + * @param atTexture The new value for the texture attribute. + */ + void setTexture( xsNCName atTexture ) { *(daeStringRef*)&attrTexture = atTexture; _validAttributeArray[0] = true; } + + /** + * Gets the texcoord attribute. + * @return Returns a xsNCName of the texcoord attribute. + */ + xsNCName getTexcoord() const { return attrTexcoord; } + /** + * Sets the texcoord attribute. + * @param atTexcoord The new value for the texcoord attribute. + */ + void setTexcoord( xsNCName atTexcoord ) { *(daeStringRef*)&attrTexcoord = atTexcoord; _validAttributeArray[1] = true; } + + /** + * Gets the extra element. + * @return a daeSmartRef to the extra element. + */ + const domExtraRef getExtra() const { return elemExtra; } + protected: + /** + * Constructor + */ + domTexture(DAE& dae) : daeElement(dae), attrTexture(), attrTexcoord(), elemExtra() {} + /** + * Destructor + */ + virtual ~domTexture() {} + /** + * Overloaded assignment operator + */ + virtual domTexture &operator=( const domTexture &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements + domColorRef elemColor; + domParamRef elemParam; + domTextureRef elemTexture; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the color element. + * @return a daeSmartRef to the color element. + */ + const domColorRef getColor() const { return elemColor; } + /** + * Gets the param element. + * @return a daeSmartRef to the param element. + */ + const domParamRef getParam() const { return elemParam; } + /** + * Gets the texture element. + * @return a daeSmartRef to the texture element. + */ + const domTextureRef getTexture() const { return elemTexture; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domCommon_color_or_texture_type_complexType(DAE& dae, daeElement* elt) : elemColor(), elemParam(), elemTexture() {} + /** + * Destructor + */ + virtual ~domCommon_color_or_texture_type_complexType() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domCommon_color_or_texture_type_complexType &operator=( const domCommon_color_or_texture_type_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domCommon_color_or_texture_type_complexType. + */ +class domCommon_color_or_texture_type : public daeElement, public domCommon_color_or_texture_type_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::COMMON_COLOR_OR_TEXTURE_TYPE; } + static daeInt ID() { return 119; } + virtual daeInt typeID() const { return ID(); } +protected: + /** + * Constructor + */ + domCommon_color_or_texture_type(DAE& dae) : daeElement(dae), domCommon_color_or_texture_type_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domCommon_color_or_texture_type() {} + /** + * Overloaded assignment operator + */ + virtual domCommon_color_or_texture_type &operator=( const domCommon_color_or_texture_type &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domCommon_float_or_param_type.h b/Engine/lib/collada/include/1.4/dom/domCommon_float_or_param_type.h new file mode 100644 index 000000000..339aa66cf --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domCommon_float_or_param_type.h @@ -0,0 +1,247 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domCommon_float_or_param_type_h__ +#define __domCommon_float_or_param_type_h__ + +#include +#include +#include + +class DAE; + +class domCommon_float_or_param_type_complexType +{ +public: + class domFloat; + + typedef daeSmartRef domFloatRef; + typedef daeTArray domFloat_Array; + + class domFloat : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT; } + static daeInt ID() { return 113; } + virtual daeInt typeID() const { return ID(); } + protected: // Attribute + xsNCName attrSid; + + protected: // Value + /** + * The ::domFloat value of the text data of this element. + */ + ::domFloat _value; + + public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + + /** + * Gets the value of this element. + * @return a ::domFloat of the value. + */ + ::domFloat getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( ::domFloat val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat(DAE& dae) : daeElement(dae), attrSid(), _value() {} + /** + * Destructor + */ + virtual ~domFloat() {} + /** + * Overloaded assignment operator + */ + virtual domFloat &operator=( const domFloat &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domParam; + + typedef daeSmartRef domParamRef; + typedef daeTArray domParam_Array; + + class domParam : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PARAM; } + static daeInt ID() { return 114; } + virtual daeInt typeID() const { return ID(); } + protected: // Attribute + xsNCName attrRef; + + + public: //Accessors and Mutators + /** + * Gets the ref attribute. + * @return Returns a xsNCName of the ref attribute. + */ + xsNCName getRef() const { return attrRef; } + /** + * Sets the ref attribute. + * @param atRef The new value for the ref attribute. + */ + void setRef( xsNCName atRef ) { *(daeStringRef*)&attrRef = atRef; _validAttributeArray[0] = true; } + + protected: + /** + * Constructor + */ + domParam(DAE& dae) : daeElement(dae), attrRef() {} + /** + * Destructor + */ + virtual ~domParam() {} + /** + * Overloaded assignment operator + */ + virtual domParam &operator=( const domParam &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements + domFloatRef elemFloat; + domParamRef elemParam; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the float element. + * @return a daeSmartRef to the float element. + */ + const domFloatRef getFloat() const { return elemFloat; } + /** + * Gets the param element. + * @return a daeSmartRef to the param element. + */ + const domParamRef getParam() const { return elemParam; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domCommon_float_or_param_type_complexType(DAE& dae, daeElement* elt) : elemFloat(), elemParam() {} + /** + * Destructor + */ + virtual ~domCommon_float_or_param_type_complexType() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domCommon_float_or_param_type_complexType &operator=( const domCommon_float_or_param_type_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domCommon_float_or_param_type_complexType. + */ +class domCommon_float_or_param_type : public daeElement, public domCommon_float_or_param_type_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::COMMON_FLOAT_OR_PARAM_TYPE; } + static daeInt ID() { return 115; } + virtual daeInt typeID() const { return ID(); } +protected: + /** + * Constructor + */ + domCommon_float_or_param_type(DAE& dae) : daeElement(dae), domCommon_float_or_param_type_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domCommon_float_or_param_type() {} + /** + * Overloaded assignment operator + */ + virtual domCommon_float_or_param_type &operator=( const domCommon_float_or_param_type &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domCommon_newparam_type.h b/Engine/lib/collada/include/1.4/dom/domCommon_newparam_type.h new file mode 100644 index 000000000..187b68bef --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domCommon_newparam_type.h @@ -0,0 +1,489 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domCommon_newparam_type_h__ +#define __domCommon_newparam_type_h__ + +#include +#include +#include + +#include +#include +class DAE; + +class domCommon_newparam_type_complexType +{ +public: + class domSemantic; + + typedef daeSmartRef domSemanticRef; + typedef daeTArray domSemantic_Array; + + class domSemantic : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SEMANTIC; } + static daeInt ID() { return 121; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsNCName value of the text data of this element. + */ + xsNCName _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsNCName of the value. + */ + xsNCName getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNCName val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domSemantic(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domSemantic() {} + /** + * Overloaded assignment operator + */ + virtual domSemantic &operator=( const domSemantic &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat; + + typedef daeSmartRef domFloatRef; + typedef daeTArray domFloat_Array; + + class domFloat : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT; } + static daeInt ID() { return 122; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat value of the text data of this element. + */ + ::domFloat _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a ::domFloat of the value. + */ + ::domFloat getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( ::domFloat val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat() {} + /** + * Overloaded assignment operator + */ + virtual domFloat &operator=( const domFloat &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat2; + + typedef daeSmartRef domFloat2Ref; + typedef daeTArray domFloat2_Array; + + class domFloat2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT2; } + static daeInt ID() { return 123; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat2 value of the text data of this element. + */ + ::domFloat2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat2 reference of the _value array. + */ + ::domFloat2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat2 reference of the _value array. + */ + const ::domFloat2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat2() {} + /** + * Overloaded assignment operator + */ + virtual domFloat2 &operator=( const domFloat2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat3; + + typedef daeSmartRef domFloat3Ref; + typedef daeTArray domFloat3_Array; + + class domFloat3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT3; } + static daeInt ID() { return 124; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat3 value of the text data of this element. + */ + ::domFloat3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat3 reference of the _value array. + */ + ::domFloat3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat3 reference of the _value array. + */ + const ::domFloat3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat3() {} + /** + * Overloaded assignment operator + */ + virtual domFloat3 &operator=( const domFloat3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat4; + + typedef daeSmartRef domFloat4Ref; + typedef daeTArray domFloat4_Array; + + class domFloat4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT4; } + static daeInt ID() { return 125; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat4 value of the text data of this element. + */ + ::domFloat4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat4 reference of the _value array. + */ + ::domFloat4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat4 reference of the _value array. + */ + const ::domFloat4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat4() {} + /** + * Overloaded assignment operator + */ + virtual domFloat4 &operator=( const domFloat4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attribute +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; + +protected: // Elements + domSemanticRef elemSemantic; + domFloatRef elemFloat; + domFloat2Ref elemFloat2; + domFloat3Ref elemFloat3; + domFloat4Ref elemFloat4; + domFx_surface_commonRef elemSurface; + domFx_sampler2D_commonRef elemSampler2D; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid;} + + /** + * Gets the semantic element. + * @return a daeSmartRef to the semantic element. + */ + const domSemanticRef getSemantic() const { return elemSemantic; } + /** + * Gets the float element. + * @return a daeSmartRef to the float element. + */ + const domFloatRef getFloat() const { return elemFloat; } + /** + * Gets the float2 element. + * @return a daeSmartRef to the float2 element. + */ + const domFloat2Ref getFloat2() const { return elemFloat2; } + /** + * Gets the float3 element. + * @return a daeSmartRef to the float3 element. + */ + const domFloat3Ref getFloat3() const { return elemFloat3; } + /** + * Gets the float4 element. + * @return a daeSmartRef to the float4 element. + */ + const domFloat4Ref getFloat4() const { return elemFloat4; } + /** + * Gets the surface element. + * @return a daeSmartRef to the surface element. + */ + const domFx_surface_commonRef getSurface() const { return elemSurface; } + /** + * Gets the sampler2D element. + * @return a daeSmartRef to the sampler2D element. + */ + const domFx_sampler2D_commonRef getSampler2D() const { return elemSampler2D; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domCommon_newparam_type_complexType(DAE& dae, daeElement* elt) : attrSid(), elemSemantic(), elemFloat(), elemFloat2(), elemFloat3(), elemFloat4(), elemSurface(), elemSampler2D() {} + /** + * Destructor + */ + virtual ~domCommon_newparam_type_complexType() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domCommon_newparam_type_complexType &operator=( const domCommon_newparam_type_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domCommon_newparam_type_complexType. + */ +class domCommon_newparam_type : public daeElement, public domCommon_newparam_type_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::COMMON_NEWPARAM_TYPE; } + static daeInt ID() { return 126; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + +protected: + /** + * Constructor + */ + domCommon_newparam_type(DAE& dae) : daeElement(dae), domCommon_newparam_type_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domCommon_newparam_type() {} + /** + * Overloaded assignment operator + */ + virtual domCommon_newparam_type &operator=( const domCommon_newparam_type &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domCommon_transparent_type.h b/Engine/lib/collada/include/1.4/dom/domCommon_transparent_type.h new file mode 100644 index 000000000..7a82629e9 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domCommon_transparent_type.h @@ -0,0 +1,108 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domCommon_transparent_type_h__ +#define __domCommon_transparent_type_h__ + +#include +#include +#include + +#include +class DAE; + +class domCommon_transparent_type_complexType : public domCommon_color_or_texture_type_complexType +{ +protected: // Attribute + domFx_opaque_enum attrOpaque; + + +public: //Accessors and Mutators + /** + * Gets the opaque attribute. + * @return Returns a domFx_opaque_enum of the opaque attribute. + */ + domFx_opaque_enum getOpaque() const { return attrOpaque; } + /** + * Sets the opaque attribute. + * @param atOpaque The new value for the opaque attribute. + */ + void setOpaque( domFx_opaque_enum atOpaque ) { attrOpaque = atOpaque; } + +protected: + /** + * Constructor + */ + domCommon_transparent_type_complexType(DAE& dae, daeElement* elt) : domCommon_color_or_texture_type_complexType(dae, elt), attrOpaque() {} + /** + * Destructor + */ + virtual ~domCommon_transparent_type_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domCommon_transparent_type_complexType &operator=( const domCommon_transparent_type_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domCommon_transparent_type_complexType. + */ +class domCommon_transparent_type : public daeElement, public domCommon_transparent_type_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::COMMON_TRANSPARENT_TYPE; } + static daeInt ID() { return 120; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the opaque attribute. + * @return Returns a domFx_opaque_enum of the opaque attribute. + */ + domFx_opaque_enum getOpaque() const { return attrOpaque; } + /** + * Sets the opaque attribute. + * @param atOpaque The new value for the opaque attribute. + */ + void setOpaque( domFx_opaque_enum atOpaque ) { attrOpaque = atOpaque; _validAttributeArray[0] = true; } + +protected: + /** + * Constructor + */ + domCommon_transparent_type(DAE& dae) : daeElement(dae), domCommon_transparent_type_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domCommon_transparent_type() {} + /** + * Overloaded assignment operator + */ + virtual domCommon_transparent_type &operator=( const domCommon_transparent_type &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domConstants.h b/Engine/lib/collada/include/1.4/dom/domConstants.h new file mode 100644 index 000000000..cec91560f --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domConstants.h @@ -0,0 +1,1093 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DOM_CONSTANTS_H__ +#define __DOM_CONSTANTS_H__ + +#include + +extern DLLSPEC daeString COLLADA_VERSION; +extern DLLSPEC daeString COLLADA_NAMESPACE; +extern DLLSPEC daeString COMMON_PROFILE_INPUT_BINORMAL; +extern DLLSPEC daeString COMMON_PROFILE_INPUT_COLOR; +extern DLLSPEC daeString COMMON_PROFILE_INPUT_CONTINUITY; +extern DLLSPEC daeString COMMON_PROFILE_INPUT_IMAGE; +extern DLLSPEC daeString COMMON_PROFILE_INPUT_IN_TANGENT; +extern DLLSPEC daeString COMMON_PROFILE_INPUT_INPUT; +extern DLLSPEC daeString COMMON_PROFILE_INPUT_INTERPOLATION; +extern DLLSPEC daeString COMMON_PROFILE_INPUT_INV_BIND_MATRIX; +extern DLLSPEC daeString COMMON_PROFILE_INPUT_JOINT; +extern DLLSPEC daeString COMMON_PROFILE_INPUT_LINEAR_STEPS; +extern DLLSPEC daeString COMMON_PROFILE_INPUT_MORPH_TARGET; +extern DLLSPEC daeString COMMON_PROFILE_INPUT_MORPH_WEIGHT; +extern DLLSPEC daeString COMMON_PROFILE_INPUT_NORMAL; +extern DLLSPEC daeString COMMON_PROFILE_INPUT_OUTPUT; +extern DLLSPEC daeString COMMON_PROFILE_INPUT_OUT_TANGENT; +extern DLLSPEC daeString COMMON_PROFILE_INPUT_POSITION; +extern DLLSPEC daeString COMMON_PROFILE_INPUT_TANGENT; +extern DLLSPEC daeString COMMON_PROFILE_INPUT_TEXBINORMAL; +extern DLLSPEC daeString COMMON_PROFILE_INPUT_TEXCOORD; +extern DLLSPEC daeString COMMON_PROFILE_INPUT_TEXTANGENT; +extern DLLSPEC daeString COMMON_PROFILE_INPUT_UV; +extern DLLSPEC daeString COMMON_PROFILE_INPUT_VERTEX; +extern DLLSPEC daeString COMMON_PROFILE_INPUT_WEIGHT; + +extern DLLSPEC daeString COMMON_PROFILE_PARAM_A; +extern DLLSPEC daeString COMMON_PROFILE_PARAM_ANGLE; +extern DLLSPEC daeString COMMON_PROFILE_PARAM_B; +extern DLLSPEC daeString COMMON_PROFILE_PARAM_DOUBLE_SIDED; +extern DLLSPEC daeString COMMON_PROFILE_PARAM_G; +extern DLLSPEC daeString COMMON_PROFILE_PARAM_P; +extern DLLSPEC daeString COMMON_PROFILE_PARAM_Q; +extern DLLSPEC daeString COMMON_PROFILE_PARAM_R; +extern DLLSPEC daeString COMMON_PROFILE_PARAM_S; +extern DLLSPEC daeString COMMON_PROFILE_PARAM_T; +extern DLLSPEC daeString COMMON_PROFILE_PARAM_TIME; +extern DLLSPEC daeString COMMON_PROFILE_PARAM_U; +extern DLLSPEC daeString COMMON_PROFILE_PARAM_V; +extern DLLSPEC daeString COMMON_PROFILE_PARAM_W; +extern DLLSPEC daeString COMMON_PROFILE_PARAM_X; +extern DLLSPEC daeString COMMON_PROFILE_PARAM_Y; +extern DLLSPEC daeString COMMON_PROFILE_PARAM_Z; + + +extern DLLSPEC daeString COLLADA_TYPE_INPUTGLOBAL; +extern DLLSPEC daeString COLLADA_TYPE_INPUTLOCAL; +extern DLLSPEC daeString COLLADA_TYPE_INPUTLOCALOFFSET; +extern DLLSPEC daeString COLLADA_TYPE_INSTANCEWITHEXTRA; +extern DLLSPEC daeString COLLADA_TYPE_TARGETABLEFLOAT; +extern DLLSPEC daeString COLLADA_TYPE_TARGETABLEFLOAT3; +extern DLLSPEC daeString COLLADA_TYPE_FX_SURFACE_FORMAT_HINT_COMMON; +extern DLLSPEC daeString COLLADA_TYPE_CHANNELS; +extern DLLSPEC daeString COLLADA_TYPE_RANGE; +extern DLLSPEC daeString COLLADA_TYPE_PRECISION; +extern DLLSPEC daeString COLLADA_TYPE_OPTION; +extern DLLSPEC daeString COLLADA_TYPE_FX_SURFACE_INIT_PLANAR_COMMON; +extern DLLSPEC daeString COLLADA_TYPE_ALL; +extern DLLSPEC daeString COLLADA_TYPE_FX_SURFACE_INIT_VOLUME_COMMON; +extern DLLSPEC daeString COLLADA_TYPE_PRIMARY; +extern DLLSPEC daeString COLLADA_TYPE_FX_SURFACE_INIT_CUBE_COMMON; +extern DLLSPEC daeString COLLADA_TYPE_ORDER; +extern DLLSPEC daeString COLLADA_TYPE_FACE; +extern DLLSPEC daeString COLLADA_TYPE_FX_SURFACE_INIT_FROM_COMMON; +extern DLLSPEC daeString COLLADA_TYPE_FX_SURFACE_COMMON; +extern DLLSPEC daeString COLLADA_TYPE_FORMAT; +extern DLLSPEC daeString COLLADA_TYPE_SIZE; +extern DLLSPEC daeString COLLADA_TYPE_VIEWPORT_RATIO; +extern DLLSPEC daeString COLLADA_TYPE_MIP_LEVELS; +extern DLLSPEC daeString COLLADA_TYPE_MIPMAP_GENERATE; +extern DLLSPEC daeString COLLADA_TYPE_FX_SAMPLER1D_COMMON; +extern DLLSPEC daeString COLLADA_TYPE_SOURCE; +extern DLLSPEC daeString COLLADA_TYPE_WRAP_S; +extern DLLSPEC daeString COLLADA_TYPE_MINFILTER; +extern DLLSPEC daeString COLLADA_TYPE_MAGFILTER; +extern DLLSPEC daeString COLLADA_TYPE_MIPFILTER; +extern DLLSPEC daeString COLLADA_TYPE_BORDER_COLOR; +extern DLLSPEC daeString COLLADA_TYPE_MIPMAP_MAXLEVEL; +extern DLLSPEC daeString COLLADA_TYPE_MIPMAP_BIAS; +extern DLLSPEC daeString COLLADA_TYPE_FX_SAMPLER2D_COMMON; +extern DLLSPEC daeString COLLADA_TYPE_WRAP_T; +extern DLLSPEC daeString COLLADA_TYPE_FX_SAMPLER3D_COMMON; +extern DLLSPEC daeString COLLADA_TYPE_WRAP_P; +extern DLLSPEC daeString COLLADA_TYPE_FX_SAMPLERCUBE_COMMON; +extern DLLSPEC daeString COLLADA_TYPE_FX_SAMPLERRECT_COMMON; +extern DLLSPEC daeString COLLADA_TYPE_FX_SAMPLERDEPTH_COMMON; +extern DLLSPEC daeString COLLADA_TYPE_FX_COLORTARGET_COMMON; +extern DLLSPEC daeString COLLADA_TYPE_FX_DEPTHTARGET_COMMON; +extern DLLSPEC daeString COLLADA_TYPE_FX_STENCILTARGET_COMMON; +extern DLLSPEC daeString COLLADA_TYPE_FX_CLEARCOLOR_COMMON; +extern DLLSPEC daeString COLLADA_TYPE_FX_CLEARDEPTH_COMMON; +extern DLLSPEC daeString COLLADA_TYPE_FX_CLEARSTENCIL_COMMON; +extern DLLSPEC daeString COLLADA_TYPE_FX_ANNOTATE_COMMON; +extern DLLSPEC daeString COLLADA_TYPE_FX_INCLUDE_COMMON; +extern DLLSPEC daeString COLLADA_TYPE_FX_NEWPARAM_COMMON; +extern DLLSPEC daeString COLLADA_TYPE_SEMANTIC; +extern DLLSPEC daeString COLLADA_TYPE_MODIFIER; +extern DLLSPEC daeString COLLADA_TYPE_FX_CODE_PROFILE; +extern DLLSPEC daeString COLLADA_TYPE_GL_SAMPLER1D; +extern DLLSPEC daeString COLLADA_TYPE_GL_SAMPLER2D; +extern DLLSPEC daeString COLLADA_TYPE_GL_SAMPLER3D; +extern DLLSPEC daeString COLLADA_TYPE_GL_SAMPLERCUBE; +extern DLLSPEC daeString COLLADA_TYPE_GL_SAMPLERRECT; +extern DLLSPEC daeString COLLADA_TYPE_GL_SAMPLERDEPTH; +extern DLLSPEC daeString COLLADA_TYPE_GLSL_NEWARRAY_TYPE; +extern DLLSPEC daeString COLLADA_TYPE_GLSL_SETARRAY_TYPE; +extern DLLSPEC daeString COLLADA_TYPE_GLSL_SURFACE_TYPE; +extern DLLSPEC daeString COLLADA_TYPE_GENERATOR; +extern DLLSPEC daeString COLLADA_TYPE_NAME; +extern DLLSPEC daeString COLLADA_TYPE_GLSL_NEWPARAM; +extern DLLSPEC daeString COLLADA_TYPE_GLSL_SETPARAM_SIMPLE; +extern DLLSPEC daeString COLLADA_TYPE_GLSL_SETPARAM; +extern DLLSPEC daeString COLLADA_TYPE_COMMON_FLOAT_OR_PARAM_TYPE; +extern DLLSPEC daeString COLLADA_TYPE_FLOAT; +extern DLLSPEC daeString COLLADA_TYPE_PARAM; +extern DLLSPEC daeString COLLADA_TYPE_COMMON_COLOR_OR_TEXTURE_TYPE; +extern DLLSPEC daeString COLLADA_TYPE_COLOR; +extern DLLSPEC daeString COLLADA_TYPE_TEXTURE; +extern DLLSPEC daeString COLLADA_TYPE_COMMON_TRANSPARENT_TYPE; +extern DLLSPEC daeString COLLADA_TYPE_COMMON_NEWPARAM_TYPE; +extern DLLSPEC daeString COLLADA_TYPE_FLOAT2; +extern DLLSPEC daeString COLLADA_TYPE_FLOAT3; +extern DLLSPEC daeString COLLADA_TYPE_FLOAT4; +extern DLLSPEC daeString COLLADA_TYPE_CG_SAMPLER1D; +extern DLLSPEC daeString COLLADA_TYPE_CG_SAMPLER2D; +extern DLLSPEC daeString COLLADA_TYPE_CG_SAMPLER3D; +extern DLLSPEC daeString COLLADA_TYPE_CG_SAMPLERCUBE; +extern DLLSPEC daeString COLLADA_TYPE_CG_SAMPLERRECT; +extern DLLSPEC daeString COLLADA_TYPE_CG_SAMPLERDEPTH; +extern DLLSPEC daeString COLLADA_TYPE_CG_CONNECT_PARAM; +extern DLLSPEC daeString COLLADA_TYPE_CG_NEWARRAY_TYPE; +extern DLLSPEC daeString COLLADA_TYPE_CG_SETARRAY_TYPE; +extern DLLSPEC daeString COLLADA_TYPE_CG_SETUSER_TYPE; +extern DLLSPEC daeString COLLADA_TYPE_CG_SURFACE_TYPE; +extern DLLSPEC daeString COLLADA_TYPE_CG_NEWPARAM; +extern DLLSPEC daeString COLLADA_TYPE_CG_SETPARAM_SIMPLE; +extern DLLSPEC daeString COLLADA_TYPE_CG_SETPARAM; +extern DLLSPEC daeString COLLADA_TYPE_GLES_TEXTURE_CONSTANT_TYPE; +extern DLLSPEC daeString COLLADA_TYPE_GLES_TEXENV_COMMAND_TYPE; +extern DLLSPEC daeString COLLADA_TYPE_GLES_TEXCOMBINER_ARGUMENTRGB_TYPE; +extern DLLSPEC daeString COLLADA_TYPE_GLES_TEXCOMBINER_ARGUMENTALPHA_TYPE; +extern DLLSPEC daeString COLLADA_TYPE_GLES_TEXCOMBINER_COMMANDRGB_TYPE; +extern DLLSPEC daeString COLLADA_TYPE_GLES_TEXCOMBINER_COMMANDALPHA_TYPE; +extern DLLSPEC daeString COLLADA_TYPE_GLES_TEXCOMBINER_COMMAND_TYPE; +extern DLLSPEC daeString COLLADA_TYPE_GLES_TEXTURE_PIPELINE; +extern DLLSPEC daeString COLLADA_TYPE_GLES_TEXTURE_UNIT; +extern DLLSPEC daeString COLLADA_TYPE_SURFACE; +extern DLLSPEC daeString COLLADA_TYPE_SAMPLER_STATE; +extern DLLSPEC daeString COLLADA_TYPE_TEXCOORD; +extern DLLSPEC daeString COLLADA_TYPE_GLES_SAMPLER_STATE; +extern DLLSPEC daeString COLLADA_TYPE_GLES_NEWPARAM; +extern DLLSPEC daeString COLLADA_TYPE_FX_SURFACE_INIT_COMMON; +extern DLLSPEC daeString COLLADA_TYPE_INIT_AS_NULL; +extern DLLSPEC daeString COLLADA_TYPE_INIT_AS_TARGET; +extern DLLSPEC daeString COLLADA_TYPE_FX_ANNOTATE_TYPE_COMMON; +extern DLLSPEC daeString COLLADA_TYPE_BOOL; +extern DLLSPEC daeString COLLADA_TYPE_BOOL2; +extern DLLSPEC daeString COLLADA_TYPE_BOOL3; +extern DLLSPEC daeString COLLADA_TYPE_BOOL4; +extern DLLSPEC daeString COLLADA_TYPE_INT; +extern DLLSPEC daeString COLLADA_TYPE_INT2; +extern DLLSPEC daeString COLLADA_TYPE_INT3; +extern DLLSPEC daeString COLLADA_TYPE_INT4; +extern DLLSPEC daeString COLLADA_TYPE_FLOAT2X2; +extern DLLSPEC daeString COLLADA_TYPE_FLOAT3X3; +extern DLLSPEC daeString COLLADA_TYPE_FLOAT4X4; +extern DLLSPEC daeString COLLADA_TYPE_STRING; +extern DLLSPEC daeString COLLADA_TYPE_FX_BASIC_TYPE_COMMON; +extern DLLSPEC daeString COLLADA_TYPE_FLOAT1X1; +extern DLLSPEC daeString COLLADA_TYPE_FLOAT1X2; +extern DLLSPEC daeString COLLADA_TYPE_FLOAT1X3; +extern DLLSPEC daeString COLLADA_TYPE_FLOAT1X4; +extern DLLSPEC daeString COLLADA_TYPE_FLOAT2X1; +extern DLLSPEC daeString COLLADA_TYPE_FLOAT2X3; +extern DLLSPEC daeString COLLADA_TYPE_FLOAT2X4; +extern DLLSPEC daeString COLLADA_TYPE_FLOAT3X1; +extern DLLSPEC daeString COLLADA_TYPE_FLOAT3X2; +extern DLLSPEC daeString COLLADA_TYPE_FLOAT3X4; +extern DLLSPEC daeString COLLADA_TYPE_FLOAT4X1; +extern DLLSPEC daeString COLLADA_TYPE_FLOAT4X2; +extern DLLSPEC daeString COLLADA_TYPE_FLOAT4X3; +extern DLLSPEC daeString COLLADA_TYPE_ENUM; +extern DLLSPEC daeString COLLADA_TYPE_GL_PIPELINE_SETTINGS; +extern DLLSPEC daeString COLLADA_TYPE_ALPHA_FUNC; +extern DLLSPEC daeString COLLADA_TYPE_FUNC; +extern DLLSPEC daeString COLLADA_TYPE_VALUE; +extern DLLSPEC daeString COLLADA_TYPE_BLEND_FUNC; +extern DLLSPEC daeString COLLADA_TYPE_SRC; +extern DLLSPEC daeString COLLADA_TYPE_DEST; +extern DLLSPEC daeString COLLADA_TYPE_BLEND_FUNC_SEPARATE; +extern DLLSPEC daeString COLLADA_TYPE_SRC_RGB; +extern DLLSPEC daeString COLLADA_TYPE_DEST_RGB; +extern DLLSPEC daeString COLLADA_TYPE_SRC_ALPHA; +extern DLLSPEC daeString COLLADA_TYPE_DEST_ALPHA; +extern DLLSPEC daeString COLLADA_TYPE_BLEND_EQUATION; +extern DLLSPEC daeString COLLADA_TYPE_BLEND_EQUATION_SEPARATE; +extern DLLSPEC daeString COLLADA_TYPE_RGB; +extern DLLSPEC daeString COLLADA_TYPE_ALPHA; +extern DLLSPEC daeString COLLADA_TYPE_COLOR_MATERIAL; +extern DLLSPEC daeString COLLADA_TYPE_MODE; +extern DLLSPEC daeString COLLADA_TYPE_CULL_FACE; +extern DLLSPEC daeString COLLADA_TYPE_DEPTH_FUNC; +extern DLLSPEC daeString COLLADA_TYPE_FOG_MODE; +extern DLLSPEC daeString COLLADA_TYPE_FOG_COORD_SRC; +extern DLLSPEC daeString COLLADA_TYPE_FRONT_FACE; +extern DLLSPEC daeString COLLADA_TYPE_LIGHT_MODEL_COLOR_CONTROL; +extern DLLSPEC daeString COLLADA_TYPE_LOGIC_OP; +extern DLLSPEC daeString COLLADA_TYPE_POLYGON_MODE; +extern DLLSPEC daeString COLLADA_TYPE_SHADE_MODEL; +extern DLLSPEC daeString COLLADA_TYPE_STENCIL_FUNC; +extern DLLSPEC daeString COLLADA_TYPE_REF; +extern DLLSPEC daeString COLLADA_TYPE_MASK; +extern DLLSPEC daeString COLLADA_TYPE_STENCIL_OP; +extern DLLSPEC daeString COLLADA_TYPE_FAIL; +extern DLLSPEC daeString COLLADA_TYPE_ZFAIL; +extern DLLSPEC daeString COLLADA_TYPE_ZPASS; +extern DLLSPEC daeString COLLADA_TYPE_STENCIL_FUNC_SEPARATE; +extern DLLSPEC daeString COLLADA_TYPE_FRONT; +extern DLLSPEC daeString COLLADA_TYPE_BACK; +extern DLLSPEC daeString COLLADA_TYPE_STENCIL_OP_SEPARATE; +extern DLLSPEC daeString COLLADA_TYPE_STENCIL_MASK_SEPARATE; +extern DLLSPEC daeString COLLADA_TYPE_LIGHT_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_LIGHT_AMBIENT; +extern DLLSPEC daeString COLLADA_TYPE_LIGHT_DIFFUSE; +extern DLLSPEC daeString COLLADA_TYPE_LIGHT_SPECULAR; +extern DLLSPEC daeString COLLADA_TYPE_LIGHT_POSITION; +extern DLLSPEC daeString COLLADA_TYPE_LIGHT_CONSTANT_ATTENUATION; +extern DLLSPEC daeString COLLADA_TYPE_LIGHT_LINEAR_ATTENUATION; +extern DLLSPEC daeString COLLADA_TYPE_LIGHT_QUADRATIC_ATTENUATION; +extern DLLSPEC daeString COLLADA_TYPE_LIGHT_SPOT_CUTOFF; +extern DLLSPEC daeString COLLADA_TYPE_LIGHT_SPOT_DIRECTION; +extern DLLSPEC daeString COLLADA_TYPE_LIGHT_SPOT_EXPONENT; +extern DLLSPEC daeString COLLADA_TYPE_TEXTURE1D; +extern DLLSPEC daeString COLLADA_TYPE_TEXTURE2D; +extern DLLSPEC daeString COLLADA_TYPE_TEXTURE3D; +extern DLLSPEC daeString COLLADA_TYPE_TEXTURECUBE; +extern DLLSPEC daeString COLLADA_TYPE_TEXTURERECT; +extern DLLSPEC daeString COLLADA_TYPE_TEXTUREDEPTH; +extern DLLSPEC daeString COLLADA_TYPE_TEXTURE1D_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_TEXTURE2D_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_TEXTURE3D_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_TEXTURECUBE_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_TEXTURERECT_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_TEXTUREDEPTH_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_TEXTURE_ENV_COLOR; +extern DLLSPEC daeString COLLADA_TYPE_TEXTURE_ENV_MODE; +extern DLLSPEC daeString COLLADA_TYPE_CLIP_PLANE; +extern DLLSPEC daeString COLLADA_TYPE_CLIP_PLANE_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_BLEND_COLOR; +extern DLLSPEC daeString COLLADA_TYPE_CLEAR_COLOR; +extern DLLSPEC daeString COLLADA_TYPE_CLEAR_STENCIL; +extern DLLSPEC daeString COLLADA_TYPE_CLEAR_DEPTH; +extern DLLSPEC daeString COLLADA_TYPE_COLOR_MASK; +extern DLLSPEC daeString COLLADA_TYPE_DEPTH_BOUNDS; +extern DLLSPEC daeString COLLADA_TYPE_DEPTH_MASK; +extern DLLSPEC daeString COLLADA_TYPE_DEPTH_RANGE; +extern DLLSPEC daeString COLLADA_TYPE_FOG_DENSITY; +extern DLLSPEC daeString COLLADA_TYPE_FOG_START; +extern DLLSPEC daeString COLLADA_TYPE_FOG_END; +extern DLLSPEC daeString COLLADA_TYPE_FOG_COLOR; +extern DLLSPEC daeString COLLADA_TYPE_LIGHT_MODEL_AMBIENT; +extern DLLSPEC daeString COLLADA_TYPE_LIGHTING_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_LINE_STIPPLE; +extern DLLSPEC daeString COLLADA_TYPE_LINE_WIDTH; +extern DLLSPEC daeString COLLADA_TYPE_MATERIAL_AMBIENT; +extern DLLSPEC daeString COLLADA_TYPE_MATERIAL_DIFFUSE; +extern DLLSPEC daeString COLLADA_TYPE_MATERIAL_EMISSION; +extern DLLSPEC daeString COLLADA_TYPE_MATERIAL_SHININESS; +extern DLLSPEC daeString COLLADA_TYPE_MATERIAL_SPECULAR; +extern DLLSPEC daeString COLLADA_TYPE_MODEL_VIEW_MATRIX; +extern DLLSPEC daeString COLLADA_TYPE_POINT_DISTANCE_ATTENUATION; +extern DLLSPEC daeString COLLADA_TYPE_POINT_FADE_THRESHOLD_SIZE; +extern DLLSPEC daeString COLLADA_TYPE_POINT_SIZE; +extern DLLSPEC daeString COLLADA_TYPE_POINT_SIZE_MIN; +extern DLLSPEC daeString COLLADA_TYPE_POINT_SIZE_MAX; +extern DLLSPEC daeString COLLADA_TYPE_POLYGON_OFFSET; +extern DLLSPEC daeString COLLADA_TYPE_PROJECTION_MATRIX; +extern DLLSPEC daeString COLLADA_TYPE_SCISSOR; +extern DLLSPEC daeString COLLADA_TYPE_STENCIL_MASK; +extern DLLSPEC daeString COLLADA_TYPE_ALPHA_TEST_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_AUTO_NORMAL_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_BLEND_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_COLOR_LOGIC_OP_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_COLOR_MATERIAL_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_CULL_FACE_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_DEPTH_BOUNDS_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_DEPTH_CLAMP_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_DEPTH_TEST_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_DITHER_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_FOG_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_LIGHT_MODEL_LOCAL_VIEWER_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_LIGHT_MODEL_TWO_SIDE_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_LINE_SMOOTH_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_LINE_STIPPLE_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_LOGIC_OP_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_MULTISAMPLE_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_NORMALIZE_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_POINT_SMOOTH_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_POLYGON_OFFSET_FILL_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_POLYGON_OFFSET_LINE_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_POLYGON_OFFSET_POINT_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_POLYGON_SMOOTH_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_POLYGON_STIPPLE_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_RESCALE_NORMAL_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_SAMPLE_ALPHA_TO_COVERAGE_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_SAMPLE_ALPHA_TO_ONE_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_SAMPLE_COVERAGE_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_SCISSOR_TEST_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_STENCIL_TEST_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_GLSL_PARAM_TYPE; +extern DLLSPEC daeString COLLADA_TYPE_CG_PARAM_TYPE; +extern DLLSPEC daeString COLLADA_TYPE_BOOL1; +extern DLLSPEC daeString COLLADA_TYPE_BOOL1X1; +extern DLLSPEC daeString COLLADA_TYPE_BOOL1X2; +extern DLLSPEC daeString COLLADA_TYPE_BOOL1X3; +extern DLLSPEC daeString COLLADA_TYPE_BOOL1X4; +extern DLLSPEC daeString COLLADA_TYPE_BOOL2X1; +extern DLLSPEC daeString COLLADA_TYPE_BOOL2X2; +extern DLLSPEC daeString COLLADA_TYPE_BOOL2X3; +extern DLLSPEC daeString COLLADA_TYPE_BOOL2X4; +extern DLLSPEC daeString COLLADA_TYPE_BOOL3X1; +extern DLLSPEC daeString COLLADA_TYPE_BOOL3X2; +extern DLLSPEC daeString COLLADA_TYPE_BOOL3X3; +extern DLLSPEC daeString COLLADA_TYPE_BOOL3X4; +extern DLLSPEC daeString COLLADA_TYPE_BOOL4X1; +extern DLLSPEC daeString COLLADA_TYPE_BOOL4X2; +extern DLLSPEC daeString COLLADA_TYPE_BOOL4X3; +extern DLLSPEC daeString COLLADA_TYPE_BOOL4X4; +extern DLLSPEC daeString COLLADA_TYPE_FLOAT1; +extern DLLSPEC daeString COLLADA_TYPE_INT1; +extern DLLSPEC daeString COLLADA_TYPE_INT1X1; +extern DLLSPEC daeString COLLADA_TYPE_INT1X2; +extern DLLSPEC daeString COLLADA_TYPE_INT1X3; +extern DLLSPEC daeString COLLADA_TYPE_INT1X4; +extern DLLSPEC daeString COLLADA_TYPE_INT2X1; +extern DLLSPEC daeString COLLADA_TYPE_INT2X2; +extern DLLSPEC daeString COLLADA_TYPE_INT2X3; +extern DLLSPEC daeString COLLADA_TYPE_INT2X4; +extern DLLSPEC daeString COLLADA_TYPE_INT3X1; +extern DLLSPEC daeString COLLADA_TYPE_INT3X2; +extern DLLSPEC daeString COLLADA_TYPE_INT3X3; +extern DLLSPEC daeString COLLADA_TYPE_INT3X4; +extern DLLSPEC daeString COLLADA_TYPE_INT4X1; +extern DLLSPEC daeString COLLADA_TYPE_INT4X2; +extern DLLSPEC daeString COLLADA_TYPE_INT4X3; +extern DLLSPEC daeString COLLADA_TYPE_INT4X4; +extern DLLSPEC daeString COLLADA_TYPE_HALF; +extern DLLSPEC daeString COLLADA_TYPE_HALF1; +extern DLLSPEC daeString COLLADA_TYPE_HALF2; +extern DLLSPEC daeString COLLADA_TYPE_HALF3; +extern DLLSPEC daeString COLLADA_TYPE_HALF4; +extern DLLSPEC daeString COLLADA_TYPE_HALF1X1; +extern DLLSPEC daeString COLLADA_TYPE_HALF1X2; +extern DLLSPEC daeString COLLADA_TYPE_HALF1X3; +extern DLLSPEC daeString COLLADA_TYPE_HALF1X4; +extern DLLSPEC daeString COLLADA_TYPE_HALF2X1; +extern DLLSPEC daeString COLLADA_TYPE_HALF2X2; +extern DLLSPEC daeString COLLADA_TYPE_HALF2X3; +extern DLLSPEC daeString COLLADA_TYPE_HALF2X4; +extern DLLSPEC daeString COLLADA_TYPE_HALF3X1; +extern DLLSPEC daeString COLLADA_TYPE_HALF3X2; +extern DLLSPEC daeString COLLADA_TYPE_HALF3X3; +extern DLLSPEC daeString COLLADA_TYPE_HALF3X4; +extern DLLSPEC daeString COLLADA_TYPE_HALF4X1; +extern DLLSPEC daeString COLLADA_TYPE_HALF4X2; +extern DLLSPEC daeString COLLADA_TYPE_HALF4X3; +extern DLLSPEC daeString COLLADA_TYPE_HALF4X4; +extern DLLSPEC daeString COLLADA_TYPE_FIXED; +extern DLLSPEC daeString COLLADA_TYPE_FIXED1; +extern DLLSPEC daeString COLLADA_TYPE_FIXED2; +extern DLLSPEC daeString COLLADA_TYPE_FIXED3; +extern DLLSPEC daeString COLLADA_TYPE_FIXED4; +extern DLLSPEC daeString COLLADA_TYPE_FIXED1X1; +extern DLLSPEC daeString COLLADA_TYPE_FIXED1X2; +extern DLLSPEC daeString COLLADA_TYPE_FIXED1X3; +extern DLLSPEC daeString COLLADA_TYPE_FIXED1X4; +extern DLLSPEC daeString COLLADA_TYPE_FIXED2X1; +extern DLLSPEC daeString COLLADA_TYPE_FIXED2X2; +extern DLLSPEC daeString COLLADA_TYPE_FIXED2X3; +extern DLLSPEC daeString COLLADA_TYPE_FIXED2X4; +extern DLLSPEC daeString COLLADA_TYPE_FIXED3X1; +extern DLLSPEC daeString COLLADA_TYPE_FIXED3X2; +extern DLLSPEC daeString COLLADA_TYPE_FIXED3X3; +extern DLLSPEC daeString COLLADA_TYPE_FIXED3X4; +extern DLLSPEC daeString COLLADA_TYPE_FIXED4X1; +extern DLLSPEC daeString COLLADA_TYPE_FIXED4X2; +extern DLLSPEC daeString COLLADA_TYPE_FIXED4X3; +extern DLLSPEC daeString COLLADA_TYPE_FIXED4X4; +extern DLLSPEC daeString COLLADA_TYPE_GLES_PIPELINE_SETTINGS; +extern DLLSPEC daeString COLLADA_TYPE_TEXTURE_PIPELINE; +extern DLLSPEC daeString COLLADA_TYPE_LIGHT_LINEAR_ATTENUTATION; +extern DLLSPEC daeString COLLADA_TYPE_TEXTURE_PIPELINE_ENABLE; +extern DLLSPEC daeString COLLADA_TYPE_GLES_BASIC_TYPE_COMMON; +extern DLLSPEC daeString COLLADA_TYPE_COLLADA; +extern DLLSPEC daeString COLLADA_TYPE_SCENE; +extern DLLSPEC daeString COLLADA_TYPE_IDREF_ARRAY; +extern DLLSPEC daeString COLLADA_TYPE_NAME_ARRAY; +extern DLLSPEC daeString COLLADA_TYPE_BOOL_ARRAY; +extern DLLSPEC daeString COLLADA_TYPE_FLOAT_ARRAY; +extern DLLSPEC daeString COLLADA_TYPE_INT_ARRAY; +extern DLLSPEC daeString COLLADA_TYPE_ACCESSOR; +extern DLLSPEC daeString COLLADA_TYPE_TECHNIQUE_COMMON; +extern DLLSPEC daeString COLLADA_TYPE_GEOMETRY; +extern DLLSPEC daeString COLLADA_TYPE_MESH; +extern DLLSPEC daeString COLLADA_TYPE_SPLINE; +extern DLLSPEC daeString COLLADA_TYPE_CONTROL_VERTICES; +extern DLLSPEC daeString COLLADA_TYPE_P; +extern DLLSPEC daeString COLLADA_TYPE_LINES; +extern DLLSPEC daeString COLLADA_TYPE_LINESTRIPS; +extern DLLSPEC daeString COLLADA_TYPE_POLYGONS; +extern DLLSPEC daeString COLLADA_TYPE_PH; +extern DLLSPEC daeString COLLADA_TYPE_H; +extern DLLSPEC daeString COLLADA_TYPE_POLYLIST; +extern DLLSPEC daeString COLLADA_TYPE_VCOUNT; +extern DLLSPEC daeString COLLADA_TYPE_TRIANGLES; +extern DLLSPEC daeString COLLADA_TYPE_TRIFANS; +extern DLLSPEC daeString COLLADA_TYPE_TRISTRIPS; +extern DLLSPEC daeString COLLADA_TYPE_VERTICES; +extern DLLSPEC daeString COLLADA_TYPE_LOOKAT; +extern DLLSPEC daeString COLLADA_TYPE_MATRIX; +extern DLLSPEC daeString COLLADA_TYPE_ROTATE; +extern DLLSPEC daeString COLLADA_TYPE_SCALE; +extern DLLSPEC daeString COLLADA_TYPE_SKEW; +extern DLLSPEC daeString COLLADA_TYPE_TRANSLATE; +extern DLLSPEC daeString COLLADA_TYPE_IMAGE; +extern DLLSPEC daeString COLLADA_TYPE_DATA; +extern DLLSPEC daeString COLLADA_TYPE_INIT_FROM; +extern DLLSPEC daeString COLLADA_TYPE_LIGHT; +extern DLLSPEC daeString COLLADA_TYPE_AMBIENT; +extern DLLSPEC daeString COLLADA_TYPE_DIRECTIONAL; +extern DLLSPEC daeString COLLADA_TYPE_POINT; +extern DLLSPEC daeString COLLADA_TYPE_SPOT; +extern DLLSPEC daeString COLLADA_TYPE_MATERIAL; +extern DLLSPEC daeString COLLADA_TYPE_CAMERA; +extern DLLSPEC daeString COLLADA_TYPE_OPTICS; +extern DLLSPEC daeString COLLADA_TYPE_ORTHOGRAPHIC; +extern DLLSPEC daeString COLLADA_TYPE_PERSPECTIVE; +extern DLLSPEC daeString COLLADA_TYPE_IMAGER; +extern DLLSPEC daeString COLLADA_TYPE_ANIMATION; +extern DLLSPEC daeString COLLADA_TYPE_ANIMATION_CLIP; +extern DLLSPEC daeString COLLADA_TYPE_CHANNEL; +extern DLLSPEC daeString COLLADA_TYPE_SAMPLER; +extern DLLSPEC daeString COLLADA_TYPE_CONTROLLER; +extern DLLSPEC daeString COLLADA_TYPE_SKIN; +extern DLLSPEC daeString COLLADA_TYPE_BIND_SHAPE_MATRIX; +extern DLLSPEC daeString COLLADA_TYPE_JOINTS; +extern DLLSPEC daeString COLLADA_TYPE_VERTEX_WEIGHTS; +extern DLLSPEC daeString COLLADA_TYPE_V; +extern DLLSPEC daeString COLLADA_TYPE_MORPH; +extern DLLSPEC daeString COLLADA_TYPE_TARGETS; +extern DLLSPEC daeString COLLADA_TYPE_ASSET; +extern DLLSPEC daeString COLLADA_TYPE_CONTRIBUTOR; +extern DLLSPEC daeString COLLADA_TYPE_AUTHOR; +extern DLLSPEC daeString COLLADA_TYPE_AUTHORING_TOOL; +extern DLLSPEC daeString COLLADA_TYPE_COMMENTS; +extern DLLSPEC daeString COLLADA_TYPE_COPYRIGHT; +extern DLLSPEC daeString COLLADA_TYPE_SOURCE_DATA; +extern DLLSPEC daeString COLLADA_TYPE_CREATED; +extern DLLSPEC daeString COLLADA_TYPE_KEYWORDS; +extern DLLSPEC daeString COLLADA_TYPE_MODIFIED; +extern DLLSPEC daeString COLLADA_TYPE_REVISION; +extern DLLSPEC daeString COLLADA_TYPE_SUBJECT; +extern DLLSPEC daeString COLLADA_TYPE_TITLE; +extern DLLSPEC daeString COLLADA_TYPE_UNIT; +extern DLLSPEC daeString COLLADA_TYPE_UP_AXIS; +extern DLLSPEC daeString COLLADA_TYPE_EXTRA; +extern DLLSPEC daeString COLLADA_TYPE_TECHNIQUE; +extern DLLSPEC daeString COLLADA_TYPE_NODE; +extern DLLSPEC daeString COLLADA_TYPE_VISUAL_SCENE; +extern DLLSPEC daeString COLLADA_TYPE_EVALUATE_SCENE; +extern DLLSPEC daeString COLLADA_TYPE_RENDER; +extern DLLSPEC daeString COLLADA_TYPE_LAYER; +extern DLLSPEC daeString COLLADA_TYPE_BIND_MATERIAL; +extern DLLSPEC daeString COLLADA_TYPE_INSTANCE_CAMERA; +extern DLLSPEC daeString COLLADA_TYPE_INSTANCE_CONTROLLER; +extern DLLSPEC daeString COLLADA_TYPE_SKELETON; +extern DLLSPEC daeString COLLADA_TYPE_INSTANCE_EFFECT; +extern DLLSPEC daeString COLLADA_TYPE_TECHNIQUE_HINT; +extern DLLSPEC daeString COLLADA_TYPE_SETPARAM; +extern DLLSPEC daeString COLLADA_TYPE_INSTANCE_FORCE_FIELD; +extern DLLSPEC daeString COLLADA_TYPE_INSTANCE_GEOMETRY; +extern DLLSPEC daeString COLLADA_TYPE_INSTANCE_LIGHT; +extern DLLSPEC daeString COLLADA_TYPE_INSTANCE_MATERIAL; +extern DLLSPEC daeString COLLADA_TYPE_BIND; +extern DLLSPEC daeString COLLADA_TYPE_BIND_VERTEX_INPUT; +extern DLLSPEC daeString COLLADA_TYPE_INSTANCE_NODE; +extern DLLSPEC daeString COLLADA_TYPE_INSTANCE_PHYSICS_MATERIAL; +extern DLLSPEC daeString COLLADA_TYPE_INSTANCE_PHYSICS_MODEL; +extern DLLSPEC daeString COLLADA_TYPE_INSTANCE_RIGID_BODY; +extern DLLSPEC daeString COLLADA_TYPE_ANGULAR_VELOCITY; +extern DLLSPEC daeString COLLADA_TYPE_VELOCITY; +extern DLLSPEC daeString COLLADA_TYPE_DYNAMIC; +extern DLLSPEC daeString COLLADA_TYPE_MASS_FRAME; +extern DLLSPEC daeString COLLADA_TYPE_SHAPE; +extern DLLSPEC daeString COLLADA_TYPE_HOLLOW; +extern DLLSPEC daeString COLLADA_TYPE_INSTANCE_RIGID_CONSTRAINT; +extern DLLSPEC daeString COLLADA_TYPE_LIBRARY_ANIMATIONS; +extern DLLSPEC daeString COLLADA_TYPE_LIBRARY_ANIMATION_CLIPS; +extern DLLSPEC daeString COLLADA_TYPE_LIBRARY_CAMERAS; +extern DLLSPEC daeString COLLADA_TYPE_LIBRARY_CONTROLLERS; +extern DLLSPEC daeString COLLADA_TYPE_LIBRARY_GEOMETRIES; +extern DLLSPEC daeString COLLADA_TYPE_LIBRARY_EFFECTS; +extern DLLSPEC daeString COLLADA_TYPE_LIBRARY_FORCE_FIELDS; +extern DLLSPEC daeString COLLADA_TYPE_LIBRARY_IMAGES; +extern DLLSPEC daeString COLLADA_TYPE_LIBRARY_LIGHTS; +extern DLLSPEC daeString COLLADA_TYPE_LIBRARY_MATERIALS; +extern DLLSPEC daeString COLLADA_TYPE_LIBRARY_NODES; +extern DLLSPEC daeString COLLADA_TYPE_LIBRARY_PHYSICS_MATERIALS; +extern DLLSPEC daeString COLLADA_TYPE_LIBRARY_PHYSICS_MODELS; +extern DLLSPEC daeString COLLADA_TYPE_LIBRARY_PHYSICS_SCENES; +extern DLLSPEC daeString COLLADA_TYPE_LIBRARY_VISUAL_SCENES; +extern DLLSPEC daeString COLLADA_TYPE_FX_PROFILE_ABSTRACT; +extern DLLSPEC daeString COLLADA_TYPE_EFFECT; +extern DLLSPEC daeString COLLADA_TYPE_GL_HOOK_ABSTRACT; +extern DLLSPEC daeString COLLADA_TYPE_PROFILE_GLSL; +extern DLLSPEC daeString COLLADA_TYPE_PASS; +extern DLLSPEC daeString COLLADA_TYPE_DRAW; +extern DLLSPEC daeString COLLADA_TYPE_SHADER; +extern DLLSPEC daeString COLLADA_TYPE_COMPILER_TARGET; +extern DLLSPEC daeString COLLADA_TYPE_COMPILER_OPTIONS; +extern DLLSPEC daeString COLLADA_TYPE_PROFILE_COMMON; +extern DLLSPEC daeString COLLADA_TYPE_CONSTANT; +extern DLLSPEC daeString COLLADA_TYPE_LAMBERT; +extern DLLSPEC daeString COLLADA_TYPE_PHONG; +extern DLLSPEC daeString COLLADA_TYPE_BLINN; +extern DLLSPEC daeString COLLADA_TYPE_PROFILE_CG; +extern DLLSPEC daeString COLLADA_TYPE_PROFILE_GLES; +extern DLLSPEC daeString COLLADA_TYPE_COLOR_TARGET; +extern DLLSPEC daeString COLLADA_TYPE_DEPTH_TARGET; +extern DLLSPEC daeString COLLADA_TYPE_STENCIL_TARGET; +extern DLLSPEC daeString COLLADA_TYPE_COLOR_CLEAR; +extern DLLSPEC daeString COLLADA_TYPE_DEPTH_CLEAR; +extern DLLSPEC daeString COLLADA_TYPE_STENCIL_CLEAR; +extern DLLSPEC daeString COLLADA_TYPE_BOX; +extern DLLSPEC daeString COLLADA_TYPE_HALF_EXTENTS; +extern DLLSPEC daeString COLLADA_TYPE_PLANE; +extern DLLSPEC daeString COLLADA_TYPE_EQUATION; +extern DLLSPEC daeString COLLADA_TYPE_SPHERE; +extern DLLSPEC daeString COLLADA_TYPE_RADIUS; +extern DLLSPEC daeString COLLADA_TYPE_ELLIPSOID; +extern DLLSPEC daeString COLLADA_TYPE_CYLINDER; +extern DLLSPEC daeString COLLADA_TYPE_HEIGHT; +extern DLLSPEC daeString COLLADA_TYPE_TAPERED_CYLINDER; +extern DLLSPEC daeString COLLADA_TYPE_RADIUS1; +extern DLLSPEC daeString COLLADA_TYPE_RADIUS2; +extern DLLSPEC daeString COLLADA_TYPE_CAPSULE; +extern DLLSPEC daeString COLLADA_TYPE_TAPERED_CAPSULE; +extern DLLSPEC daeString COLLADA_TYPE_CONVEX_MESH; +extern DLLSPEC daeString COLLADA_TYPE_FORCE_FIELD; +extern DLLSPEC daeString COLLADA_TYPE_PHYSICS_MATERIAL; +extern DLLSPEC daeString COLLADA_TYPE_PHYSICS_SCENE; +extern DLLSPEC daeString COLLADA_TYPE_RIGID_BODY; +extern DLLSPEC daeString COLLADA_TYPE_RIGID_CONSTRAINT; +extern DLLSPEC daeString COLLADA_TYPE_REF_ATTACHMENT; +extern DLLSPEC daeString COLLADA_TYPE_ATTACHMENT; +extern DLLSPEC daeString COLLADA_TYPE_ENABLED; +extern DLLSPEC daeString COLLADA_TYPE_INTERPENETRATE; +extern DLLSPEC daeString COLLADA_TYPE_LIMITS; +extern DLLSPEC daeString COLLADA_TYPE_SWING_CONE_AND_TWIST; +extern DLLSPEC daeString COLLADA_TYPE_LINEAR; +extern DLLSPEC daeString COLLADA_TYPE_SPRING; +extern DLLSPEC daeString COLLADA_TYPE_ANGULAR; +extern DLLSPEC daeString COLLADA_TYPE_PHYSICS_MODEL; + +extern DLLSPEC daeString COLLADA_ELEMENT_COLLADA; +extern DLLSPEC daeString COLLADA_ELEMENT_EXTRA; +extern DLLSPEC daeString COLLADA_ELEMENT_CHANNELS; +extern DLLSPEC daeString COLLADA_ELEMENT_RANGE; +extern DLLSPEC daeString COLLADA_ELEMENT_PRECISION; +extern DLLSPEC daeString COLLADA_ELEMENT_OPTION; +extern DLLSPEC daeString COLLADA_ELEMENT_ALL; +extern DLLSPEC daeString COLLADA_ELEMENT_PRIMARY; +extern DLLSPEC daeString COLLADA_ELEMENT_FACE; +extern DLLSPEC daeString COLLADA_ELEMENT_ORDER; +extern DLLSPEC daeString COLLADA_ELEMENT_FX_SURFACE_INIT_COMMON; +extern DLLSPEC daeString COLLADA_ELEMENT_FORMAT; +extern DLLSPEC daeString COLLADA_ELEMENT_FORMAT_HINT; +extern DLLSPEC daeString COLLADA_ELEMENT_SIZE; +extern DLLSPEC daeString COLLADA_ELEMENT_VIEWPORT_RATIO; +extern DLLSPEC daeString COLLADA_ELEMENT_MIP_LEVELS; +extern DLLSPEC daeString COLLADA_ELEMENT_MIPMAP_GENERATE; +extern DLLSPEC daeString COLLADA_ELEMENT_SOURCE; +extern DLLSPEC daeString COLLADA_ELEMENT_WRAP_S; +extern DLLSPEC daeString COLLADA_ELEMENT_MINFILTER; +extern DLLSPEC daeString COLLADA_ELEMENT_MAGFILTER; +extern DLLSPEC daeString COLLADA_ELEMENT_MIPFILTER; +extern DLLSPEC daeString COLLADA_ELEMENT_BORDER_COLOR; +extern DLLSPEC daeString COLLADA_ELEMENT_MIPMAP_MAXLEVEL; +extern DLLSPEC daeString COLLADA_ELEMENT_MIPMAP_BIAS; +extern DLLSPEC daeString COLLADA_ELEMENT_WRAP_T; +extern DLLSPEC daeString COLLADA_ELEMENT_WRAP_P; +extern DLLSPEC daeString COLLADA_ELEMENT_FX_ANNOTATE_TYPE_COMMON; +extern DLLSPEC daeString COLLADA_ELEMENT_ANNOTATE; +extern DLLSPEC daeString COLLADA_ELEMENT_SEMANTIC; +extern DLLSPEC daeString COLLADA_ELEMENT_MODIFIER; +extern DLLSPEC daeString COLLADA_ELEMENT_FX_BASIC_TYPE_COMMON; +extern DLLSPEC daeString COLLADA_ELEMENT_GLSL_PARAM_TYPE; +extern DLLSPEC daeString COLLADA_ELEMENT_ARRAY; +extern DLLSPEC daeString COLLADA_ELEMENT_GENERATOR; +extern DLLSPEC daeString COLLADA_ELEMENT_CODE; +extern DLLSPEC daeString COLLADA_ELEMENT_INCLUDE; +extern DLLSPEC daeString COLLADA_ELEMENT_NAME; +extern DLLSPEC daeString COLLADA_ELEMENT_SETPARAM; +extern DLLSPEC daeString COLLADA_ELEMENT_FLOAT; +extern DLLSPEC daeString COLLADA_ELEMENT_PARAM; +extern DLLSPEC daeString COLLADA_ELEMENT_COLOR; +extern DLLSPEC daeString COLLADA_ELEMENT_TEXTURE; +extern DLLSPEC daeString COLLADA_ELEMENT_FLOAT2; +extern DLLSPEC daeString COLLADA_ELEMENT_FLOAT3; +extern DLLSPEC daeString COLLADA_ELEMENT_FLOAT4; +extern DLLSPEC daeString COLLADA_ELEMENT_SURFACE; +extern DLLSPEC daeString COLLADA_ELEMENT_SAMPLER2D; +extern DLLSPEC daeString COLLADA_ELEMENT_CG_PARAM_TYPE; +extern DLLSPEC daeString COLLADA_ELEMENT_USERTYPE; +extern DLLSPEC daeString COLLADA_ELEMENT_CONNECT_PARAM; +extern DLLSPEC daeString COLLADA_ELEMENT_CONSTANT; +extern DLLSPEC daeString COLLADA_ELEMENT_ARGUMENT; +extern DLLSPEC daeString COLLADA_ELEMENT_RGB; +extern DLLSPEC daeString COLLADA_ELEMENT_ALPHA; +extern DLLSPEC daeString COLLADA_ELEMENT_TEXCOMBINER; +extern DLLSPEC daeString COLLADA_ELEMENT_TEXENV; +extern DLLSPEC daeString COLLADA_ELEMENT_SAMPLER_STATE; +extern DLLSPEC daeString COLLADA_ELEMENT_TEXCOORD; +extern DLLSPEC daeString COLLADA_ELEMENT_GLES_BASIC_TYPE_COMMON; +extern DLLSPEC daeString COLLADA_ELEMENT_INIT_AS_NULL; +extern DLLSPEC daeString COLLADA_ELEMENT_INIT_AS_TARGET; +extern DLLSPEC daeString COLLADA_ELEMENT_INIT_CUBE; +extern DLLSPEC daeString COLLADA_ELEMENT_INIT_VOLUME; +extern DLLSPEC daeString COLLADA_ELEMENT_INIT_PLANAR; +extern DLLSPEC daeString COLLADA_ELEMENT_INIT_FROM; +extern DLLSPEC daeString COLLADA_ELEMENT_BOOL; +extern DLLSPEC daeString COLLADA_ELEMENT_BOOL2; +extern DLLSPEC daeString COLLADA_ELEMENT_BOOL3; +extern DLLSPEC daeString COLLADA_ELEMENT_BOOL4; +extern DLLSPEC daeString COLLADA_ELEMENT_INT; +extern DLLSPEC daeString COLLADA_ELEMENT_INT2; +extern DLLSPEC daeString COLLADA_ELEMENT_INT3; +extern DLLSPEC daeString COLLADA_ELEMENT_INT4; +extern DLLSPEC daeString COLLADA_ELEMENT_FLOAT2X2; +extern DLLSPEC daeString COLLADA_ELEMENT_FLOAT3X3; +extern DLLSPEC daeString COLLADA_ELEMENT_FLOAT4X4; +extern DLLSPEC daeString COLLADA_ELEMENT_STRING; +extern DLLSPEC daeString COLLADA_ELEMENT_FLOAT1X1; +extern DLLSPEC daeString COLLADA_ELEMENT_FLOAT1X2; +extern DLLSPEC daeString COLLADA_ELEMENT_FLOAT1X3; +extern DLLSPEC daeString COLLADA_ELEMENT_FLOAT1X4; +extern DLLSPEC daeString COLLADA_ELEMENT_FLOAT2X1; +extern DLLSPEC daeString COLLADA_ELEMENT_FLOAT2X3; +extern DLLSPEC daeString COLLADA_ELEMENT_FLOAT2X4; +extern DLLSPEC daeString COLLADA_ELEMENT_FLOAT3X1; +extern DLLSPEC daeString COLLADA_ELEMENT_FLOAT3X2; +extern DLLSPEC daeString COLLADA_ELEMENT_FLOAT3X4; +extern DLLSPEC daeString COLLADA_ELEMENT_FLOAT4X1; +extern DLLSPEC daeString COLLADA_ELEMENT_FLOAT4X2; +extern DLLSPEC daeString COLLADA_ELEMENT_FLOAT4X3; +extern DLLSPEC daeString COLLADA_ELEMENT_SAMPLER1D; +extern DLLSPEC daeString COLLADA_ELEMENT_SAMPLER3D; +extern DLLSPEC daeString COLLADA_ELEMENT_SAMPLERCUBE; +extern DLLSPEC daeString COLLADA_ELEMENT_SAMPLERRECT; +extern DLLSPEC daeString COLLADA_ELEMENT_SAMPLERDEPTH; +extern DLLSPEC daeString COLLADA_ELEMENT_ENUM; +extern DLLSPEC daeString COLLADA_ELEMENT_ALPHA_FUNC; +extern DLLSPEC daeString COLLADA_ELEMENT_BLEND_FUNC; +extern DLLSPEC daeString COLLADA_ELEMENT_BLEND_FUNC_SEPARATE; +extern DLLSPEC daeString COLLADA_ELEMENT_BLEND_EQUATION; +extern DLLSPEC daeString COLLADA_ELEMENT_BLEND_EQUATION_SEPARATE; +extern DLLSPEC daeString COLLADA_ELEMENT_COLOR_MATERIAL; +extern DLLSPEC daeString COLLADA_ELEMENT_CULL_FACE; +extern DLLSPEC daeString COLLADA_ELEMENT_DEPTH_FUNC; +extern DLLSPEC daeString COLLADA_ELEMENT_FOG_MODE; +extern DLLSPEC daeString COLLADA_ELEMENT_FOG_COORD_SRC; +extern DLLSPEC daeString COLLADA_ELEMENT_FRONT_FACE; +extern DLLSPEC daeString COLLADA_ELEMENT_LIGHT_MODEL_COLOR_CONTROL; +extern DLLSPEC daeString COLLADA_ELEMENT_LOGIC_OP; +extern DLLSPEC daeString COLLADA_ELEMENT_POLYGON_MODE; +extern DLLSPEC daeString COLLADA_ELEMENT_SHADE_MODEL; +extern DLLSPEC daeString COLLADA_ELEMENT_STENCIL_FUNC; +extern DLLSPEC daeString COLLADA_ELEMENT_STENCIL_OP; +extern DLLSPEC daeString COLLADA_ELEMENT_STENCIL_FUNC_SEPARATE; +extern DLLSPEC daeString COLLADA_ELEMENT_STENCIL_OP_SEPARATE; +extern DLLSPEC daeString COLLADA_ELEMENT_STENCIL_MASK_SEPARATE; +extern DLLSPEC daeString COLLADA_ELEMENT_LIGHT_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_LIGHT_AMBIENT; +extern DLLSPEC daeString COLLADA_ELEMENT_LIGHT_DIFFUSE; +extern DLLSPEC daeString COLLADA_ELEMENT_LIGHT_SPECULAR; +extern DLLSPEC daeString COLLADA_ELEMENT_LIGHT_POSITION; +extern DLLSPEC daeString COLLADA_ELEMENT_LIGHT_CONSTANT_ATTENUATION; +extern DLLSPEC daeString COLLADA_ELEMENT_LIGHT_LINEAR_ATTENUATION; +extern DLLSPEC daeString COLLADA_ELEMENT_LIGHT_QUADRATIC_ATTENUATION; +extern DLLSPEC daeString COLLADA_ELEMENT_LIGHT_SPOT_CUTOFF; +extern DLLSPEC daeString COLLADA_ELEMENT_LIGHT_SPOT_DIRECTION; +extern DLLSPEC daeString COLLADA_ELEMENT_LIGHT_SPOT_EXPONENT; +extern DLLSPEC daeString COLLADA_ELEMENT_TEXTURE1D; +extern DLLSPEC daeString COLLADA_ELEMENT_TEXTURE2D; +extern DLLSPEC daeString COLLADA_ELEMENT_TEXTURE3D; +extern DLLSPEC daeString COLLADA_ELEMENT_TEXTURECUBE; +extern DLLSPEC daeString COLLADA_ELEMENT_TEXTURERECT; +extern DLLSPEC daeString COLLADA_ELEMENT_TEXTUREDEPTH; +extern DLLSPEC daeString COLLADA_ELEMENT_TEXTURE1D_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_TEXTURE2D_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_TEXTURE3D_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_TEXTURECUBE_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_TEXTURERECT_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_TEXTUREDEPTH_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_TEXTURE_ENV_COLOR; +extern DLLSPEC daeString COLLADA_ELEMENT_TEXTURE_ENV_MODE; +extern DLLSPEC daeString COLLADA_ELEMENT_CLIP_PLANE; +extern DLLSPEC daeString COLLADA_ELEMENT_CLIP_PLANE_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_BLEND_COLOR; +extern DLLSPEC daeString COLLADA_ELEMENT_CLEAR_COLOR; +extern DLLSPEC daeString COLLADA_ELEMENT_CLEAR_STENCIL; +extern DLLSPEC daeString COLLADA_ELEMENT_CLEAR_DEPTH; +extern DLLSPEC daeString COLLADA_ELEMENT_COLOR_MASK; +extern DLLSPEC daeString COLLADA_ELEMENT_DEPTH_BOUNDS; +extern DLLSPEC daeString COLLADA_ELEMENT_DEPTH_MASK; +extern DLLSPEC daeString COLLADA_ELEMENT_DEPTH_RANGE; +extern DLLSPEC daeString COLLADA_ELEMENT_FOG_DENSITY; +extern DLLSPEC daeString COLLADA_ELEMENT_FOG_START; +extern DLLSPEC daeString COLLADA_ELEMENT_FOG_END; +extern DLLSPEC daeString COLLADA_ELEMENT_FOG_COLOR; +extern DLLSPEC daeString COLLADA_ELEMENT_LIGHT_MODEL_AMBIENT; +extern DLLSPEC daeString COLLADA_ELEMENT_LIGHTING_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_LINE_STIPPLE; +extern DLLSPEC daeString COLLADA_ELEMENT_LINE_WIDTH; +extern DLLSPEC daeString COLLADA_ELEMENT_MATERIAL_AMBIENT; +extern DLLSPEC daeString COLLADA_ELEMENT_MATERIAL_DIFFUSE; +extern DLLSPEC daeString COLLADA_ELEMENT_MATERIAL_EMISSION; +extern DLLSPEC daeString COLLADA_ELEMENT_MATERIAL_SHININESS; +extern DLLSPEC daeString COLLADA_ELEMENT_MATERIAL_SPECULAR; +extern DLLSPEC daeString COLLADA_ELEMENT_MODEL_VIEW_MATRIX; +extern DLLSPEC daeString COLLADA_ELEMENT_POINT_DISTANCE_ATTENUATION; +extern DLLSPEC daeString COLLADA_ELEMENT_POINT_FADE_THRESHOLD_SIZE; +extern DLLSPEC daeString COLLADA_ELEMENT_POINT_SIZE; +extern DLLSPEC daeString COLLADA_ELEMENT_POINT_SIZE_MIN; +extern DLLSPEC daeString COLLADA_ELEMENT_POINT_SIZE_MAX; +extern DLLSPEC daeString COLLADA_ELEMENT_POLYGON_OFFSET; +extern DLLSPEC daeString COLLADA_ELEMENT_PROJECTION_MATRIX; +extern DLLSPEC daeString COLLADA_ELEMENT_SCISSOR; +extern DLLSPEC daeString COLLADA_ELEMENT_STENCIL_MASK; +extern DLLSPEC daeString COLLADA_ELEMENT_ALPHA_TEST_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_AUTO_NORMAL_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_BLEND_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_COLOR_LOGIC_OP_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_COLOR_MATERIAL_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_CULL_FACE_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_DEPTH_BOUNDS_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_DEPTH_CLAMP_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_DEPTH_TEST_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_DITHER_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_FOG_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_LIGHT_MODEL_LOCAL_VIEWER_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_LIGHT_MODEL_TWO_SIDE_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_LINE_SMOOTH_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_LINE_STIPPLE_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_LOGIC_OP_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_MULTISAMPLE_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_NORMALIZE_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_POINT_SMOOTH_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_POLYGON_OFFSET_FILL_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_POLYGON_OFFSET_LINE_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_POLYGON_OFFSET_POINT_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_POLYGON_SMOOTH_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_POLYGON_STIPPLE_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_RESCALE_NORMAL_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_SAMPLE_ALPHA_TO_COVERAGE_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_SAMPLE_ALPHA_TO_ONE_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_SAMPLE_COVERAGE_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_SCISSOR_TEST_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_STENCIL_TEST_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_GL_HOOK_ABSTRACT; +extern DLLSPEC daeString COLLADA_ELEMENT_FUNC; +extern DLLSPEC daeString COLLADA_ELEMENT_VALUE; +extern DLLSPEC daeString COLLADA_ELEMENT_SRC; +extern DLLSPEC daeString COLLADA_ELEMENT_DEST; +extern DLLSPEC daeString COLLADA_ELEMENT_SRC_RGB; +extern DLLSPEC daeString COLLADA_ELEMENT_DEST_RGB; +extern DLLSPEC daeString COLLADA_ELEMENT_SRC_ALPHA; +extern DLLSPEC daeString COLLADA_ELEMENT_DEST_ALPHA; +extern DLLSPEC daeString COLLADA_ELEMENT_rgb; +extern DLLSPEC daeString COLLADA_ELEMENT_MODE; +extern DLLSPEC daeString COLLADA_ELEMENT_REF; +extern DLLSPEC daeString COLLADA_ELEMENT_MASK; +extern DLLSPEC daeString COLLADA_ELEMENT_FAIL; +extern DLLSPEC daeString COLLADA_ELEMENT_ZFAIL; +extern DLLSPEC daeString COLLADA_ELEMENT_ZPASS; +extern DLLSPEC daeString COLLADA_ELEMENT_FRONT; +extern DLLSPEC daeString COLLADA_ELEMENT_BACK; +extern DLLSPEC daeString COLLADA_ELEMENT_BOOL1; +extern DLLSPEC daeString COLLADA_ELEMENT_BOOL1X1; +extern DLLSPEC daeString COLLADA_ELEMENT_BOOL1X2; +extern DLLSPEC daeString COLLADA_ELEMENT_BOOL1X3; +extern DLLSPEC daeString COLLADA_ELEMENT_BOOL1X4; +extern DLLSPEC daeString COLLADA_ELEMENT_BOOL2X1; +extern DLLSPEC daeString COLLADA_ELEMENT_BOOL2X2; +extern DLLSPEC daeString COLLADA_ELEMENT_BOOL2X3; +extern DLLSPEC daeString COLLADA_ELEMENT_BOOL2X4; +extern DLLSPEC daeString COLLADA_ELEMENT_BOOL3X1; +extern DLLSPEC daeString COLLADA_ELEMENT_BOOL3X2; +extern DLLSPEC daeString COLLADA_ELEMENT_BOOL3X3; +extern DLLSPEC daeString COLLADA_ELEMENT_BOOL3X4; +extern DLLSPEC daeString COLLADA_ELEMENT_BOOL4X1; +extern DLLSPEC daeString COLLADA_ELEMENT_BOOL4X2; +extern DLLSPEC daeString COLLADA_ELEMENT_BOOL4X3; +extern DLLSPEC daeString COLLADA_ELEMENT_BOOL4X4; +extern DLLSPEC daeString COLLADA_ELEMENT_FLOAT1; +extern DLLSPEC daeString COLLADA_ELEMENT_INT1; +extern DLLSPEC daeString COLLADA_ELEMENT_INT1X1; +extern DLLSPEC daeString COLLADA_ELEMENT_INT1X2; +extern DLLSPEC daeString COLLADA_ELEMENT_INT1X3; +extern DLLSPEC daeString COLLADA_ELEMENT_INT1X4; +extern DLLSPEC daeString COLLADA_ELEMENT_INT2X1; +extern DLLSPEC daeString COLLADA_ELEMENT_INT2X2; +extern DLLSPEC daeString COLLADA_ELEMENT_INT2X3; +extern DLLSPEC daeString COLLADA_ELEMENT_INT2X4; +extern DLLSPEC daeString COLLADA_ELEMENT_INT3X1; +extern DLLSPEC daeString COLLADA_ELEMENT_INT3X2; +extern DLLSPEC daeString COLLADA_ELEMENT_INT3X3; +extern DLLSPEC daeString COLLADA_ELEMENT_INT3X4; +extern DLLSPEC daeString COLLADA_ELEMENT_INT4X1; +extern DLLSPEC daeString COLLADA_ELEMENT_INT4X2; +extern DLLSPEC daeString COLLADA_ELEMENT_INT4X3; +extern DLLSPEC daeString COLLADA_ELEMENT_INT4X4; +extern DLLSPEC daeString COLLADA_ELEMENT_HALF; +extern DLLSPEC daeString COLLADA_ELEMENT_HALF1; +extern DLLSPEC daeString COLLADA_ELEMENT_HALF2; +extern DLLSPEC daeString COLLADA_ELEMENT_HALF3; +extern DLLSPEC daeString COLLADA_ELEMENT_HALF4; +extern DLLSPEC daeString COLLADA_ELEMENT_HALF1X1; +extern DLLSPEC daeString COLLADA_ELEMENT_HALF1X2; +extern DLLSPEC daeString COLLADA_ELEMENT_HALF1X3; +extern DLLSPEC daeString COLLADA_ELEMENT_HALF1X4; +extern DLLSPEC daeString COLLADA_ELEMENT_HALF2X1; +extern DLLSPEC daeString COLLADA_ELEMENT_HALF2X2; +extern DLLSPEC daeString COLLADA_ELEMENT_HALF2X3; +extern DLLSPEC daeString COLLADA_ELEMENT_HALF2X4; +extern DLLSPEC daeString COLLADA_ELEMENT_HALF3X1; +extern DLLSPEC daeString COLLADA_ELEMENT_HALF3X2; +extern DLLSPEC daeString COLLADA_ELEMENT_HALF3X3; +extern DLLSPEC daeString COLLADA_ELEMENT_HALF3X4; +extern DLLSPEC daeString COLLADA_ELEMENT_HALF4X1; +extern DLLSPEC daeString COLLADA_ELEMENT_HALF4X2; +extern DLLSPEC daeString COLLADA_ELEMENT_HALF4X3; +extern DLLSPEC daeString COLLADA_ELEMENT_HALF4X4; +extern DLLSPEC daeString COLLADA_ELEMENT_FIXED; +extern DLLSPEC daeString COLLADA_ELEMENT_FIXED1; +extern DLLSPEC daeString COLLADA_ELEMENT_FIXED2; +extern DLLSPEC daeString COLLADA_ELEMENT_FIXED3; +extern DLLSPEC daeString COLLADA_ELEMENT_FIXED4; +extern DLLSPEC daeString COLLADA_ELEMENT_FIXED1X1; +extern DLLSPEC daeString COLLADA_ELEMENT_FIXED1X2; +extern DLLSPEC daeString COLLADA_ELEMENT_FIXED1X3; +extern DLLSPEC daeString COLLADA_ELEMENT_FIXED1X4; +extern DLLSPEC daeString COLLADA_ELEMENT_FIXED2X1; +extern DLLSPEC daeString COLLADA_ELEMENT_FIXED2X2; +extern DLLSPEC daeString COLLADA_ELEMENT_FIXED2X3; +extern DLLSPEC daeString COLLADA_ELEMENT_FIXED2X4; +extern DLLSPEC daeString COLLADA_ELEMENT_FIXED3X1; +extern DLLSPEC daeString COLLADA_ELEMENT_FIXED3X2; +extern DLLSPEC daeString COLLADA_ELEMENT_FIXED3X3; +extern DLLSPEC daeString COLLADA_ELEMENT_FIXED3X4; +extern DLLSPEC daeString COLLADA_ELEMENT_FIXED4X1; +extern DLLSPEC daeString COLLADA_ELEMENT_FIXED4X2; +extern DLLSPEC daeString COLLADA_ELEMENT_FIXED4X3; +extern DLLSPEC daeString COLLADA_ELEMENT_FIXED4X4; +extern DLLSPEC daeString COLLADA_ELEMENT_TEXTURE_PIPELINE; +extern DLLSPEC daeString COLLADA_ELEMENT_LIGHT_LINEAR_ATTENUTATION; +extern DLLSPEC daeString COLLADA_ELEMENT_TEXTURE_PIPELINE_ENABLE; +extern DLLSPEC daeString COLLADA_ELEMENT_TEXTURE_UNIT; +extern DLLSPEC daeString COLLADA_ELEMENT_ASSET; +extern DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_ANIMATIONS; +extern DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_ANIMATION_CLIPS; +extern DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_CAMERAS; +extern DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_CONTROLLERS; +extern DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_GEOMETRIES; +extern DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_EFFECTS; +extern DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_FORCE_FIELDS; +extern DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_IMAGES; +extern DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_LIGHTS; +extern DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_MATERIALS; +extern DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_NODES; +extern DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_PHYSICS_MATERIALS; +extern DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_PHYSICS_MODELS; +extern DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_PHYSICS_SCENES; +extern DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_VISUAL_SCENES; +extern DLLSPEC daeString COLLADA_ELEMENT_SCENE; +extern DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_PHYSICS_SCENE; +extern DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_VISUAL_SCENE; +extern DLLSPEC daeString COLLADA_ELEMENT_IDREF_ARRAY; +extern DLLSPEC daeString COLLADA_ELEMENT_NAME_ARRAY; +extern DLLSPEC daeString COLLADA_ELEMENT_BOOL_ARRAY; +extern DLLSPEC daeString COLLADA_ELEMENT_FLOAT_ARRAY; +extern DLLSPEC daeString COLLADA_ELEMENT_INT_ARRAY; +extern DLLSPEC daeString COLLADA_ELEMENT_TECHNIQUE_COMMON; +extern DLLSPEC daeString COLLADA_ELEMENT_TECHNIQUE; +extern DLLSPEC daeString COLLADA_ELEMENT_ACCESSOR; +extern DLLSPEC daeString COLLADA_ELEMENT_CONVEX_MESH; +extern DLLSPEC daeString COLLADA_ELEMENT_MESH; +extern DLLSPEC daeString COLLADA_ELEMENT_SPLINE; +extern DLLSPEC daeString COLLADA_ELEMENT_VERTICES; +extern DLLSPEC daeString COLLADA_ELEMENT_LINES; +extern DLLSPEC daeString COLLADA_ELEMENT_LINESTRIPS; +extern DLLSPEC daeString COLLADA_ELEMENT_POLYGONS; +extern DLLSPEC daeString COLLADA_ELEMENT_POLYLIST; +extern DLLSPEC daeString COLLADA_ELEMENT_TRIANGLES; +extern DLLSPEC daeString COLLADA_ELEMENT_TRIFANS; +extern DLLSPEC daeString COLLADA_ELEMENT_TRISTRIPS; +extern DLLSPEC daeString COLLADA_ELEMENT_CONTROL_VERTICES; +extern DLLSPEC daeString COLLADA_ELEMENT_INPUT; +extern DLLSPEC daeString COLLADA_ELEMENT_P; +extern DLLSPEC daeString COLLADA_ELEMENT_PH; +extern DLLSPEC daeString COLLADA_ELEMENT_H; +extern DLLSPEC daeString COLLADA_ELEMENT_VCOUNT; +extern DLLSPEC daeString COLLADA_ELEMENT_DATA; +extern DLLSPEC daeString COLLADA_ELEMENT_AMBIENT; +extern DLLSPEC daeString COLLADA_ELEMENT_DIRECTIONAL; +extern DLLSPEC daeString COLLADA_ELEMENT_POINT; +extern DLLSPEC daeString COLLADA_ELEMENT_SPOT; +extern DLLSPEC daeString COLLADA_ELEMENT_CONSTANT_ATTENUATION; +extern DLLSPEC daeString COLLADA_ELEMENT_LINEAR_ATTENUATION; +extern DLLSPEC daeString COLLADA_ELEMENT_QUADRATIC_ATTENUATION; +extern DLLSPEC daeString COLLADA_ELEMENT_FALLOFF_ANGLE; +extern DLLSPEC daeString COLLADA_ELEMENT_FALLOFF_EXPONENT; +extern DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_EFFECT; +extern DLLSPEC daeString COLLADA_ELEMENT_OPTICS; +extern DLLSPEC daeString COLLADA_ELEMENT_IMAGER; +extern DLLSPEC daeString COLLADA_ELEMENT_ORTHOGRAPHIC; +extern DLLSPEC daeString COLLADA_ELEMENT_PERSPECTIVE; +extern DLLSPEC daeString COLLADA_ELEMENT_XMAG; +extern DLLSPEC daeString COLLADA_ELEMENT_YMAG; +extern DLLSPEC daeString COLLADA_ELEMENT_ASPECT_RATIO; +extern DLLSPEC daeString COLLADA_ELEMENT_ZNEAR; +extern DLLSPEC daeString COLLADA_ELEMENT_ZFAR; +extern DLLSPEC daeString COLLADA_ELEMENT_XFOV; +extern DLLSPEC daeString COLLADA_ELEMENT_YFOV; +extern DLLSPEC daeString COLLADA_ELEMENT_SAMPLER; +extern DLLSPEC daeString COLLADA_ELEMENT_CHANNEL; +extern DLLSPEC daeString COLLADA_ELEMENT_ANIMATION; +extern DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_ANIMATION; +extern DLLSPEC daeString COLLADA_ELEMENT_SKIN; +extern DLLSPEC daeString COLLADA_ELEMENT_MORPH; +extern DLLSPEC daeString COLLADA_ELEMENT_BIND_SHAPE_MATRIX; +extern DLLSPEC daeString COLLADA_ELEMENT_JOINTS; +extern DLLSPEC daeString COLLADA_ELEMENT_VERTEX_WEIGHTS; +extern DLLSPEC daeString COLLADA_ELEMENT_V; +extern DLLSPEC daeString COLLADA_ELEMENT_TARGETS; +extern DLLSPEC daeString COLLADA_ELEMENT_CONTRIBUTOR; +extern DLLSPEC daeString COLLADA_ELEMENT_CREATED; +extern DLLSPEC daeString COLLADA_ELEMENT_KEYWORDS; +extern DLLSPEC daeString COLLADA_ELEMENT_MODIFIED; +extern DLLSPEC daeString COLLADA_ELEMENT_REVISION; +extern DLLSPEC daeString COLLADA_ELEMENT_SUBJECT; +extern DLLSPEC daeString COLLADA_ELEMENT_TITLE; +extern DLLSPEC daeString COLLADA_ELEMENT_UNIT; +extern DLLSPEC daeString COLLADA_ELEMENT_UP_AXIS; +extern DLLSPEC daeString COLLADA_ELEMENT_AUTHOR; +extern DLLSPEC daeString COLLADA_ELEMENT_AUTHORING_TOOL; +extern DLLSPEC daeString COLLADA_ELEMENT_COMMENTS; +extern DLLSPEC daeString COLLADA_ELEMENT_COPYRIGHT; +extern DLLSPEC daeString COLLADA_ELEMENT_SOURCE_DATA; +extern DLLSPEC daeString COLLADA_ELEMENT_LOOKAT; +extern DLLSPEC daeString COLLADA_ELEMENT_MATRIX; +extern DLLSPEC daeString COLLADA_ELEMENT_ROTATE; +extern DLLSPEC daeString COLLADA_ELEMENT_SCALE; +extern DLLSPEC daeString COLLADA_ELEMENT_SKEW; +extern DLLSPEC daeString COLLADA_ELEMENT_TRANSLATE; +extern DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_CAMERA; +extern DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_CONTROLLER; +extern DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_GEOMETRY; +extern DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_LIGHT; +extern DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_NODE; +extern DLLSPEC daeString COLLADA_ELEMENT_NODE; +extern DLLSPEC daeString COLLADA_ELEMENT_EVALUATE_SCENE; +extern DLLSPEC daeString COLLADA_ELEMENT_RENDER; +extern DLLSPEC daeString COLLADA_ELEMENT_LAYER; +extern DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_MATERIAL; +extern DLLSPEC daeString COLLADA_ELEMENT_SKELETON; +extern DLLSPEC daeString COLLADA_ELEMENT_BIND_MATERIAL; +extern DLLSPEC daeString COLLADA_ELEMENT_TECHNIQUE_HINT; +extern DLLSPEC daeString COLLADA_ELEMENT_BIND; +extern DLLSPEC daeString COLLADA_ELEMENT_BIND_VERTEX_INPUT; +extern DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_FORCE_FIELD; +extern DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_RIGID_BODY; +extern DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_RIGID_CONSTRAINT; +extern DLLSPEC daeString COLLADA_ELEMENT_ANGULAR_VELOCITY; +extern DLLSPEC daeString COLLADA_ELEMENT_VELOCITY; +extern DLLSPEC daeString COLLADA_ELEMENT_DYNAMIC; +extern DLLSPEC daeString COLLADA_ELEMENT_MASS; +extern DLLSPEC daeString COLLADA_ELEMENT_MASS_FRAME; +extern DLLSPEC daeString COLLADA_ELEMENT_INERTIA; +extern DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_PHYSICS_MATERIAL; +extern DLLSPEC daeString COLLADA_ELEMENT_PHYSICS_MATERIAL; +extern DLLSPEC daeString COLLADA_ELEMENT_SHAPE; +extern DLLSPEC daeString COLLADA_ELEMENT_HOLLOW; +extern DLLSPEC daeString COLLADA_ELEMENT_DENSITY; +extern DLLSPEC daeString COLLADA_ELEMENT_PLANE; +extern DLLSPEC daeString COLLADA_ELEMENT_BOX; +extern DLLSPEC daeString COLLADA_ELEMENT_SPHERE; +extern DLLSPEC daeString COLLADA_ELEMENT_CYLINDER; +extern DLLSPEC daeString COLLADA_ELEMENT_TAPERED_CYLINDER; +extern DLLSPEC daeString COLLADA_ELEMENT_CAPSULE; +extern DLLSPEC daeString COLLADA_ELEMENT_TAPERED_CAPSULE; +extern DLLSPEC daeString COLLADA_ELEMENT_ANIMATION_CLIP; +extern DLLSPEC daeString COLLADA_ELEMENT_CAMERA; +extern DLLSPEC daeString COLLADA_ELEMENT_CONTROLLER; +extern DLLSPEC daeString COLLADA_ELEMENT_GEOMETRY; +extern DLLSPEC daeString COLLADA_ELEMENT_EFFECT; +extern DLLSPEC daeString COLLADA_ELEMENT_FORCE_FIELD; +extern DLLSPEC daeString COLLADA_ELEMENT_IMAGE; +extern DLLSPEC daeString COLLADA_ELEMENT_LIGHT; +extern DLLSPEC daeString COLLADA_ELEMENT_MATERIAL; +extern DLLSPEC daeString COLLADA_ELEMENT_PHYSICS_MODEL; +extern DLLSPEC daeString COLLADA_ELEMENT_PHYSICS_SCENE; +extern DLLSPEC daeString COLLADA_ELEMENT_VISUAL_SCENE; +extern DLLSPEC daeString COLLADA_ELEMENT_NEWPARAM; +extern DLLSPEC daeString COLLADA_ELEMENT_FX_PROFILE_ABSTRACT; +extern DLLSPEC daeString COLLADA_ELEMENT_PROFILE_GLSL; +extern DLLSPEC daeString COLLADA_ELEMENT_PASS; +extern DLLSPEC daeString COLLADA_ELEMENT_COLOR_TARGET; +extern DLLSPEC daeString COLLADA_ELEMENT_DEPTH_TARGET; +extern DLLSPEC daeString COLLADA_ELEMENT_STENCIL_TARGET; +extern DLLSPEC daeString COLLADA_ELEMENT_COLOR_CLEAR; +extern DLLSPEC daeString COLLADA_ELEMENT_DEPTH_CLEAR; +extern DLLSPEC daeString COLLADA_ELEMENT_STENCIL_CLEAR; +extern DLLSPEC daeString COLLADA_ELEMENT_DRAW; +extern DLLSPEC daeString COLLADA_ELEMENT_GL_PIPELINE_SETTINGS; +extern DLLSPEC daeString COLLADA_ELEMENT_SHADER; +extern DLLSPEC daeString COLLADA_ELEMENT_COMPILER_TARGET; +extern DLLSPEC daeString COLLADA_ELEMENT_COMPILER_OPTIONS; +extern DLLSPEC daeString COLLADA_ELEMENT_PROFILE_COMMON; +extern DLLSPEC daeString COLLADA_ELEMENT_LAMBERT; +extern DLLSPEC daeString COLLADA_ELEMENT_PHONG; +extern DLLSPEC daeString COLLADA_ELEMENT_BLINN; +extern DLLSPEC daeString COLLADA_ELEMENT_EMISSION; +extern DLLSPEC daeString COLLADA_ELEMENT_REFLECTIVE; +extern DLLSPEC daeString COLLADA_ELEMENT_REFLECTIVITY; +extern DLLSPEC daeString COLLADA_ELEMENT_TRANSPARENT; +extern DLLSPEC daeString COLLADA_ELEMENT_TRANSPARENCY; +extern DLLSPEC daeString COLLADA_ELEMENT_INDEX_OF_REFRACTION; +extern DLLSPEC daeString COLLADA_ELEMENT_DIFFUSE; +extern DLLSPEC daeString COLLADA_ELEMENT_SPECULAR; +extern DLLSPEC daeString COLLADA_ELEMENT_SHININESS; +extern DLLSPEC daeString COLLADA_ELEMENT_PROFILE_CG; +extern DLLSPEC daeString COLLADA_ELEMENT_PROFILE_GLES; +extern DLLSPEC daeString COLLADA_ELEMENT_GLES_PIPELINE_SETTINGS; +extern DLLSPEC daeString COLLADA_ELEMENT_HALF_EXTENTS; +extern DLLSPEC daeString COLLADA_ELEMENT_EQUATION; +extern DLLSPEC daeString COLLADA_ELEMENT_RADIUS; +extern DLLSPEC daeString COLLADA_ELEMENT_HEIGHT; +extern DLLSPEC daeString COLLADA_ELEMENT_RADIUS1; +extern DLLSPEC daeString COLLADA_ELEMENT_RADIUS2; +extern DLLSPEC daeString COLLADA_ELEMENT_DYNAMIC_FRICTION; +extern DLLSPEC daeString COLLADA_ELEMENT_RESTITUTION; +extern DLLSPEC daeString COLLADA_ELEMENT_STATIC_FRICTION; +extern DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_PHYSICS_MODEL; +extern DLLSPEC daeString COLLADA_ELEMENT_GRAVITY; +extern DLLSPEC daeString COLLADA_ELEMENT_TIME_STEP; +extern DLLSPEC daeString COLLADA_ELEMENT_REF_ATTACHMENT; +extern DLLSPEC daeString COLLADA_ELEMENT_ATTACHMENT; +extern DLLSPEC daeString COLLADA_ELEMENT_ENABLED; +extern DLLSPEC daeString COLLADA_ELEMENT_INTERPENETRATE; +extern DLLSPEC daeString COLLADA_ELEMENT_LIMITS; +extern DLLSPEC daeString COLLADA_ELEMENT_SPRING; +extern DLLSPEC daeString COLLADA_ELEMENT_SWING_CONE_AND_TWIST; +extern DLLSPEC daeString COLLADA_ELEMENT_LINEAR; +extern DLLSPEC daeString COLLADA_ELEMENT_MIN; +extern DLLSPEC daeString COLLADA_ELEMENT_MAX; +extern DLLSPEC daeString COLLADA_ELEMENT_ANGULAR; +extern DLLSPEC daeString COLLADA_ELEMENT_STIFFNESS; +extern DLLSPEC daeString COLLADA_ELEMENT_DAMPING; +extern DLLSPEC daeString COLLADA_ELEMENT_TARGET_VALUE; +extern DLLSPEC daeString COLLADA_ELEMENT_RIGID_BODY; +extern DLLSPEC daeString COLLADA_ELEMENT_RIGID_CONSTRAINT; + +#endif //__DOM_CONSTANTS_H__ + diff --git a/Engine/lib/collada/include/1.4/dom/domController.h b/Engine/lib/collada/include/1.4/dom/domController.h new file mode 100644 index 000000000..54c7bd509 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domController.h @@ -0,0 +1,174 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domController_h__ +#define __domController_h__ + +#include +#include +#include + +#include +#include +#include +#include +class DAE; + +/** + * The controller element categorizes the declaration of generic control information. + * A controller is a device or mechanism that manages and directs the operations + * of another object. + */ +class domController : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CONTROLLER; } + static daeInt ID() { return 655; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The controller element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * The controller element may contain either a skin element or a morph element. + * @see domSkin + */ + domSkinRef elemSkin; +/** + * The controller element may contain either a skin element or a morph element. + * @see domMorph + */ + domMorphRef elemMorph; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the skin element. + * @return a daeSmartRef to the skin element. + */ + const domSkinRef getSkin() const { return elemSkin; } + /** + * Gets the morph element. + * @return a daeSmartRef to the morph element. + */ + const domMorphRef getMorph() const { return elemMorph; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domController(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemSkin(), elemMorph(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domController() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domController &operator=( const domController &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domConvex_mesh.h b/Engine/lib/collada/include/1.4/dom/domConvex_mesh.h new file mode 100644 index 000000000..7695ea78c --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domConvex_mesh.h @@ -0,0 +1,239 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domConvex_mesh_h__ +#define __domConvex_mesh_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +class DAE; + +/** + * The definition of the convex_mesh element is identical to the mesh element + * with the exception that instead of a complete description (source, vertices, + * polygons etc.), it may simply point to another geometry to derive its + * shape. The latter case means that the convex hull of that geometry should + * be computed and is indicated by the optional “convex_hull_of” attribute. + */ +class domConvex_mesh : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CONVEX_MESH; } + static daeInt ID() { return 789; } + virtual daeInt typeID() const { return ID(); } +protected: // Attribute +/** + * The convex_hull_of attribute is a URI string of geometry to compute the + * convex hull of. Optional attribute. + */ + xsAnyURI attrConvex_hull_of; + +protected: // Elements + domSource_Array elemSource_array; + domVerticesRef elemVertices; + domLines_Array elemLines_array; + domLinestrips_Array elemLinestrips_array; + domPolygons_Array elemPolygons_array; + domPolylist_Array elemPolylist_array; + domTriangles_Array elemTriangles_array; + domTrifans_Array elemTrifans_array; + domTristrips_Array elemTristrips_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the convex_hull_of attribute. + * @return Returns a xsAnyURI reference of the convex_hull_of attribute. + */ + xsAnyURI &getConvex_hull_of() { return attrConvex_hull_of; } + /** + * Gets the convex_hull_of attribute. + * @return Returns a constant xsAnyURI reference of the convex_hull_of attribute. + */ + const xsAnyURI &getConvex_hull_of() const { return attrConvex_hull_of; } + /** + * Sets the convex_hull_of attribute. + * @param atConvex_hull_of The new value for the convex_hull_of attribute. + */ + void setConvex_hull_of( const xsAnyURI &atConvex_hull_of ) { attrConvex_hull_of = atConvex_hull_of; _validAttributeArray[0] = true; } + /** + * Sets the convex_hull_of attribute. + * @param atConvex_hull_of The new value for the convex_hull_of attribute. + */ + void setConvex_hull_of( xsString atConvex_hull_of ) { attrConvex_hull_of = atConvex_hull_of; _validAttributeArray[0] = true; } + + /** + * Gets the source element array. + * @return Returns a reference to the array of source elements. + */ + domSource_Array &getSource_array() { return elemSource_array; } + /** + * Gets the source element array. + * @return Returns a constant reference to the array of source elements. + */ + const domSource_Array &getSource_array() const { return elemSource_array; } + /** + * Gets the vertices element. + * @return a daeSmartRef to the vertices element. + */ + const domVerticesRef getVertices() const { return elemVertices; } + /** + * Gets the lines element array. + * @return Returns a reference to the array of lines elements. + */ + domLines_Array &getLines_array() { return elemLines_array; } + /** + * Gets the lines element array. + * @return Returns a constant reference to the array of lines elements. + */ + const domLines_Array &getLines_array() const { return elemLines_array; } + /** + * Gets the linestrips element array. + * @return Returns a reference to the array of linestrips elements. + */ + domLinestrips_Array &getLinestrips_array() { return elemLinestrips_array; } + /** + * Gets the linestrips element array. + * @return Returns a constant reference to the array of linestrips elements. + */ + const domLinestrips_Array &getLinestrips_array() const { return elemLinestrips_array; } + /** + * Gets the polygons element array. + * @return Returns a reference to the array of polygons elements. + */ + domPolygons_Array &getPolygons_array() { return elemPolygons_array; } + /** + * Gets the polygons element array. + * @return Returns a constant reference to the array of polygons elements. + */ + const domPolygons_Array &getPolygons_array() const { return elemPolygons_array; } + /** + * Gets the polylist element array. + * @return Returns a reference to the array of polylist elements. + */ + domPolylist_Array &getPolylist_array() { return elemPolylist_array; } + /** + * Gets the polylist element array. + * @return Returns a constant reference to the array of polylist elements. + */ + const domPolylist_Array &getPolylist_array() const { return elemPolylist_array; } + /** + * Gets the triangles element array. + * @return Returns a reference to the array of triangles elements. + */ + domTriangles_Array &getTriangles_array() { return elemTriangles_array; } + /** + * Gets the triangles element array. + * @return Returns a constant reference to the array of triangles elements. + */ + const domTriangles_Array &getTriangles_array() const { return elemTriangles_array; } + /** + * Gets the trifans element array. + * @return Returns a reference to the array of trifans elements. + */ + domTrifans_Array &getTrifans_array() { return elemTrifans_array; } + /** + * Gets the trifans element array. + * @return Returns a constant reference to the array of trifans elements. + */ + const domTrifans_Array &getTrifans_array() const { return elemTrifans_array; } + /** + * Gets the tristrips element array. + * @return Returns a reference to the array of tristrips elements. + */ + domTristrips_Array &getTristrips_array() { return elemTristrips_array; } + /** + * Gets the tristrips element array. + * @return Returns a constant reference to the array of tristrips elements. + */ + const domTristrips_Array &getTristrips_array() const { return elemTristrips_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domConvex_mesh(DAE& dae) : daeElement(dae), attrConvex_hull_of(dae, *this), elemSource_array(), elemVertices(), elemLines_array(), elemLinestrips_array(), elemPolygons_array(), elemPolylist_array(), elemTriangles_array(), elemTrifans_array(), elemTristrips_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domConvex_mesh() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domConvex_mesh &operator=( const domConvex_mesh &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domCylinder.h b/Engine/lib/collada/include/1.4/dom/domCylinder.h new file mode 100644 index 000000000..b642febc3 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domCylinder.h @@ -0,0 +1,229 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domCylinder_h__ +#define __domCylinder_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * A cylinder primitive that is centered on, and aligned with. the local Y + * axis. + */ +class domCylinder : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CYLINDER; } + static daeInt ID() { return 775; } + virtual daeInt typeID() const { return ID(); } +public: + class domHeight; + + typedef daeSmartRef domHeightRef; + typedef daeTArray domHeight_Array; + +/** + * A float value that represents the length of the cylinder along the Y axis. + */ + class domHeight : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HEIGHT; } + static daeInt ID() { return 776; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFloat value of the text data of this element. + */ + domFloat _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFloat of the value. + */ + domFloat getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFloat val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHeight(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domHeight() {} + /** + * Overloaded assignment operator + */ + virtual domHeight &operator=( const domHeight &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domRadius; + + typedef daeSmartRef domRadiusRef; + typedef daeTArray domRadius_Array; + +/** + * float2 values that represent the radii of the cylinder. + */ + class domRadius : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::RADIUS; } + static daeInt ID() { return 777; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFloat2 value of the text data of this element. + */ + domFloat2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domFloat2 reference of the _value array. + */ + domFloat2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domFloat2 reference of the _value array. + */ + const domFloat2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domFloat2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domRadius(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domRadius() {} + /** + * Overloaded assignment operator + */ + virtual domRadius &operator=( const domRadius &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements +/** + * A float value that represents the length of the cylinder along the Y axis. + * @see domHeight + */ + domHeightRef elemHeight; +/** + * float2 values that represent the radii of the cylinder. @see domRadius + */ + domRadiusRef elemRadius; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the height element. + * @return a daeSmartRef to the height element. + */ + const domHeightRef getHeight() const { return elemHeight; } + /** + * Gets the radius element. + * @return a daeSmartRef to the radius element. + */ + const domRadiusRef getRadius() const { return elemRadius; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domCylinder(DAE& dae) : daeElement(dae), elemHeight(), elemRadius(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domCylinder() {} + /** + * Overloaded assignment operator + */ + virtual domCylinder &operator=( const domCylinder &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domEffect.h b/Engine/lib/collada/include/1.4/dom/domEffect.h new file mode 100644 index 000000000..d3a90e0ff --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domEffect.h @@ -0,0 +1,209 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domEffect_h__ +#define __domEffect_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +class DAE; + +/** + * A self contained description of a shader effect. + */ +class domEffect : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::EFFECT; } + static daeInt ID() { return 728; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The effect element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * The annotate element allows you to specify an annotation on this effect. + * @see domAnnotate + */ + domFx_annotate_common_Array elemAnnotate_array; +/** + * The image element allows you to create image resources which can be shared + * by multipe profiles. @see domImage + */ + domImage_Array elemImage_array; +/** + * The newparam element allows you to create new effect parameters which can + * be shared by multipe profiles. @see domNewparam + */ + domFx_newparam_common_Array elemNewparam_array; +/** + * This is the substituion group hook which allows you to swap in other COLLADA + * FX profiles. @see domFx_profile_abstract + */ + domFx_profile_abstract_Array elemFx_profile_abstract_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the annotate element array. + * @return Returns a reference to the array of annotate elements. + */ + domFx_annotate_common_Array &getAnnotate_array() { return elemAnnotate_array; } + /** + * Gets the annotate element array. + * @return Returns a constant reference to the array of annotate elements. + */ + const domFx_annotate_common_Array &getAnnotate_array() const { return elemAnnotate_array; } + /** + * Gets the image element array. + * @return Returns a reference to the array of image elements. + */ + domImage_Array &getImage_array() { return elemImage_array; } + /** + * Gets the image element array. + * @return Returns a constant reference to the array of image elements. + */ + const domImage_Array &getImage_array() const { return elemImage_array; } + /** + * Gets the newparam element array. + * @return Returns a reference to the array of newparam elements. + */ + domFx_newparam_common_Array &getNewparam_array() { return elemNewparam_array; } + /** + * Gets the newparam element array. + * @return Returns a constant reference to the array of newparam elements. + */ + const domFx_newparam_common_Array &getNewparam_array() const { return elemNewparam_array; } + /** + * Gets the fx_profile_abstract element array. + * @return Returns a reference to the array of fx_profile_abstract elements. + */ + domFx_profile_abstract_Array &getFx_profile_abstract_array() { return elemFx_profile_abstract_array; } + /** + * Gets the fx_profile_abstract element array. + * @return Returns a constant reference to the array of fx_profile_abstract elements. + */ + const domFx_profile_abstract_Array &getFx_profile_abstract_array() const { return elemFx_profile_abstract_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domEffect(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemAnnotate_array(), elemImage_array(), elemNewparam_array(), elemFx_profile_abstract_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domEffect() {} + /** + * Overloaded assignment operator + */ + virtual domEffect &operator=( const domEffect &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domElements.h b/Engine/lib/collada/include/1.4/dom/domElements.h new file mode 100644 index 000000000..121a94be8 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domElements.h @@ -0,0 +1,871 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DOM_ELEMENTS_H__ +#define __DOM_ELEMENTS_H__ + +#include + +class domInputGlobal; + +typedef daeSmartRef domInputGlobalRef; +typedef daeTArray domInputGlobal_Array; + +class domInputLocal; + +typedef daeSmartRef domInputLocalRef; +typedef daeTArray domInputLocal_Array; + +class domInputLocalOffset; + +typedef daeSmartRef domInputLocalOffsetRef; +typedef daeTArray domInputLocalOffset_Array; + +class domInstanceWithExtra; + +typedef daeSmartRef domInstanceWithExtraRef; +typedef daeTArray domInstanceWithExtra_Array; + +class domTargetableFloat; + +typedef daeSmartRef domTargetableFloatRef; +typedef daeTArray domTargetableFloat_Array; + +class domTargetableFloat3; + +typedef daeSmartRef domTargetableFloat3Ref; +typedef daeTArray domTargetableFloat3_Array; + +class domFx_surface_format_hint_common; + +typedef daeSmartRef domFx_surface_format_hint_commonRef; +typedef daeTArray domFx_surface_format_hint_common_Array; + +class domFx_surface_init_planar_common; + +typedef daeSmartRef domFx_surface_init_planar_commonRef; +typedef daeTArray domFx_surface_init_planar_common_Array; + +class domFx_surface_init_volume_common; + +typedef daeSmartRef domFx_surface_init_volume_commonRef; +typedef daeTArray domFx_surface_init_volume_common_Array; + +class domFx_surface_init_cube_common; + +typedef daeSmartRef domFx_surface_init_cube_commonRef; +typedef daeTArray domFx_surface_init_cube_common_Array; + +class domFx_surface_init_from_common; + +typedef daeSmartRef domFx_surface_init_from_commonRef; +typedef daeTArray domFx_surface_init_from_common_Array; + +class domFx_surface_common; + +typedef daeSmartRef domFx_surface_commonRef; +typedef daeTArray domFx_surface_common_Array; + +class domFx_sampler1D_common; + +typedef daeSmartRef domFx_sampler1D_commonRef; +typedef daeTArray domFx_sampler1D_common_Array; + +class domFx_sampler2D_common; + +typedef daeSmartRef domFx_sampler2D_commonRef; +typedef daeTArray domFx_sampler2D_common_Array; + +class domFx_sampler3D_common; + +typedef daeSmartRef domFx_sampler3D_commonRef; +typedef daeTArray domFx_sampler3D_common_Array; + +class domFx_samplerCUBE_common; + +typedef daeSmartRef domFx_samplerCUBE_commonRef; +typedef daeTArray domFx_samplerCUBE_common_Array; + +class domFx_samplerRECT_common; + +typedef daeSmartRef domFx_samplerRECT_commonRef; +typedef daeTArray domFx_samplerRECT_common_Array; + +class domFx_samplerDEPTH_common; + +typedef daeSmartRef domFx_samplerDEPTH_commonRef; +typedef daeTArray domFx_samplerDEPTH_common_Array; + +class domFx_colortarget_common; + +typedef daeSmartRef domFx_colortarget_commonRef; +typedef daeTArray domFx_colortarget_common_Array; + +class domFx_depthtarget_common; + +typedef daeSmartRef domFx_depthtarget_commonRef; +typedef daeTArray domFx_depthtarget_common_Array; + +class domFx_stenciltarget_common; + +typedef daeSmartRef domFx_stenciltarget_commonRef; +typedef daeTArray domFx_stenciltarget_common_Array; + +class domFx_clearcolor_common; + +typedef daeSmartRef domFx_clearcolor_commonRef; +typedef daeTArray domFx_clearcolor_common_Array; + +class domFx_cleardepth_common; + +typedef daeSmartRef domFx_cleardepth_commonRef; +typedef daeTArray domFx_cleardepth_common_Array; + +class domFx_clearstencil_common; + +typedef daeSmartRef domFx_clearstencil_commonRef; +typedef daeTArray domFx_clearstencil_common_Array; + +class domFx_annotate_common; + +typedef daeSmartRef domFx_annotate_commonRef; +typedef daeTArray domFx_annotate_common_Array; + +class domFx_include_common; + +typedef daeSmartRef domFx_include_commonRef; +typedef daeTArray domFx_include_common_Array; + +class domFx_newparam_common; + +typedef daeSmartRef domFx_newparam_commonRef; +typedef daeTArray domFx_newparam_common_Array; + +class domFx_code_profile; + +typedef daeSmartRef domFx_code_profileRef; +typedef daeTArray domFx_code_profile_Array; + +class domGl_sampler1D; + +typedef daeSmartRef domGl_sampler1DRef; +typedef daeTArray domGl_sampler1D_Array; + +class domGl_sampler2D; + +typedef daeSmartRef domGl_sampler2DRef; +typedef daeTArray domGl_sampler2D_Array; + +class domGl_sampler3D; + +typedef daeSmartRef domGl_sampler3DRef; +typedef daeTArray domGl_sampler3D_Array; + +class domGl_samplerCUBE; + +typedef daeSmartRef domGl_samplerCUBERef; +typedef daeTArray domGl_samplerCUBE_Array; + +class domGl_samplerRECT; + +typedef daeSmartRef domGl_samplerRECTRef; +typedef daeTArray domGl_samplerRECT_Array; + +class domGl_samplerDEPTH; + +typedef daeSmartRef domGl_samplerDEPTHRef; +typedef daeTArray domGl_samplerDEPTH_Array; + +class domGlsl_newarray_type; + +typedef daeSmartRef domGlsl_newarray_typeRef; +typedef daeTArray domGlsl_newarray_type_Array; + +class domGlsl_setarray_type; + +typedef daeSmartRef domGlsl_setarray_typeRef; +typedef daeTArray domGlsl_setarray_type_Array; + +class domGlsl_surface_type; + +typedef daeSmartRef domGlsl_surface_typeRef; +typedef daeTArray domGlsl_surface_type_Array; + +class domGlsl_newparam; + +typedef daeSmartRef domGlsl_newparamRef; +typedef daeTArray domGlsl_newparam_Array; + +class domGlsl_setparam_simple; + +typedef daeSmartRef domGlsl_setparam_simpleRef; +typedef daeTArray domGlsl_setparam_simple_Array; + +class domGlsl_setparam; + +typedef daeSmartRef domGlsl_setparamRef; +typedef daeTArray domGlsl_setparam_Array; + +class domCommon_float_or_param_type; + +typedef daeSmartRef domCommon_float_or_param_typeRef; +typedef daeTArray domCommon_float_or_param_type_Array; + +class domCommon_color_or_texture_type; + +typedef daeSmartRef domCommon_color_or_texture_typeRef; +typedef daeTArray domCommon_color_or_texture_type_Array; + +class domCommon_transparent_type; + +typedef daeSmartRef domCommon_transparent_typeRef; +typedef daeTArray domCommon_transparent_type_Array; + +class domCommon_newparam_type; + +typedef daeSmartRef domCommon_newparam_typeRef; +typedef daeTArray domCommon_newparam_type_Array; + +class domCg_sampler1D; + +typedef daeSmartRef domCg_sampler1DRef; +typedef daeTArray domCg_sampler1D_Array; + +class domCg_sampler2D; + +typedef daeSmartRef domCg_sampler2DRef; +typedef daeTArray domCg_sampler2D_Array; + +class domCg_sampler3D; + +typedef daeSmartRef domCg_sampler3DRef; +typedef daeTArray domCg_sampler3D_Array; + +class domCg_samplerCUBE; + +typedef daeSmartRef domCg_samplerCUBERef; +typedef daeTArray domCg_samplerCUBE_Array; + +class domCg_samplerRECT; + +typedef daeSmartRef domCg_samplerRECTRef; +typedef daeTArray domCg_samplerRECT_Array; + +class domCg_samplerDEPTH; + +typedef daeSmartRef domCg_samplerDEPTHRef; +typedef daeTArray domCg_samplerDEPTH_Array; + +class domCg_connect_param; + +typedef daeSmartRef domCg_connect_paramRef; +typedef daeTArray domCg_connect_param_Array; + +class domCg_newarray_type; + +typedef daeSmartRef domCg_newarray_typeRef; +typedef daeTArray domCg_newarray_type_Array; + +class domCg_setarray_type; + +typedef daeSmartRef domCg_setarray_typeRef; +typedef daeTArray domCg_setarray_type_Array; + +class domCg_setuser_type; + +typedef daeSmartRef domCg_setuser_typeRef; +typedef daeTArray domCg_setuser_type_Array; + +class domCg_surface_type; + +typedef daeSmartRef domCg_surface_typeRef; +typedef daeTArray domCg_surface_type_Array; + +class domCg_newparam; + +typedef daeSmartRef domCg_newparamRef; +typedef daeTArray domCg_newparam_Array; + +class domCg_setparam_simple; + +typedef daeSmartRef domCg_setparam_simpleRef; +typedef daeTArray domCg_setparam_simple_Array; + +class domCg_setparam; + +typedef daeSmartRef domCg_setparamRef; +typedef daeTArray domCg_setparam_Array; + +class domGles_texture_constant_type; + +typedef daeSmartRef domGles_texture_constant_typeRef; +typedef daeTArray domGles_texture_constant_type_Array; + +class domGles_texenv_command_type; + +typedef daeSmartRef domGles_texenv_command_typeRef; +typedef daeTArray domGles_texenv_command_type_Array; + +class domGles_texcombiner_argumentRGB_type; + +typedef daeSmartRef domGles_texcombiner_argumentRGB_typeRef; +typedef daeTArray domGles_texcombiner_argumentRGB_type_Array; + +class domGles_texcombiner_argumentAlpha_type; + +typedef daeSmartRef domGles_texcombiner_argumentAlpha_typeRef; +typedef daeTArray domGles_texcombiner_argumentAlpha_type_Array; + +class domGles_texcombiner_commandRGB_type; + +typedef daeSmartRef domGles_texcombiner_commandRGB_typeRef; +typedef daeTArray domGles_texcombiner_commandRGB_type_Array; + +class domGles_texcombiner_commandAlpha_type; + +typedef daeSmartRef domGles_texcombiner_commandAlpha_typeRef; +typedef daeTArray domGles_texcombiner_commandAlpha_type_Array; + +class domGles_texcombiner_command_type; + +typedef daeSmartRef domGles_texcombiner_command_typeRef; +typedef daeTArray domGles_texcombiner_command_type_Array; + +class domGles_texture_pipeline; + +typedef daeSmartRef domGles_texture_pipelineRef; +typedef daeTArray domGles_texture_pipeline_Array; + +class domGles_texture_unit; + +typedef daeSmartRef domGles_texture_unitRef; +typedef daeTArray domGles_texture_unit_Array; + +class domGles_sampler_state; + +typedef daeSmartRef domGles_sampler_stateRef; +typedef daeTArray domGles_sampler_state_Array; + +class domGles_newparam; + +typedef daeSmartRef domGles_newparamRef; +typedef daeTArray domGles_newparam_Array; + +class domFx_surface_init_common; + +typedef daeSmartRef domFx_surface_init_commonRef; +typedef daeTArray domFx_surface_init_common_Array; + +class domFx_annotate_type_common; + +typedef daeSmartRef domFx_annotate_type_commonRef; +typedef daeTArray domFx_annotate_type_common_Array; + +class domFx_basic_type_common; + +typedef daeSmartRef domFx_basic_type_commonRef; +typedef daeTArray domFx_basic_type_common_Array; + +class domGl_pipeline_settings; + +typedef daeSmartRef domGl_pipeline_settingsRef; +typedef daeTArray domGl_pipeline_settings_Array; + +class domGlsl_param_type; + +typedef daeSmartRef domGlsl_param_typeRef; +typedef daeTArray domGlsl_param_type_Array; + +class domCg_param_type; + +typedef daeSmartRef domCg_param_typeRef; +typedef daeTArray domCg_param_type_Array; + +class domGles_pipeline_settings; + +typedef daeSmartRef domGles_pipeline_settingsRef; +typedef daeTArray domGles_pipeline_settings_Array; + +class domGles_basic_type_common; + +typedef daeSmartRef domGles_basic_type_commonRef; +typedef daeTArray domGles_basic_type_common_Array; + +class domCOLLADA; + +typedef daeSmartRef domCOLLADARef; +typedef daeTArray domCOLLADA_Array; + +class domIDREF_array; + +typedef daeSmartRef domIDREF_arrayRef; +typedef daeTArray domIDREF_array_Array; + +class domName_array; + +typedef daeSmartRef domName_arrayRef; +typedef daeTArray domName_array_Array; + +class domBool_array; + +typedef daeSmartRef domBool_arrayRef; +typedef daeTArray domBool_array_Array; + +class domFloat_array; + +typedef daeSmartRef domFloat_arrayRef; +typedef daeTArray domFloat_array_Array; + +class domInt_array; + +typedef daeSmartRef domInt_arrayRef; +typedef daeTArray domInt_array_Array; + +class domAccessor; + +typedef daeSmartRef domAccessorRef; +typedef daeTArray domAccessor_Array; + +class domParam; + +typedef daeSmartRef domParamRef; +typedef daeTArray domParam_Array; + +class domSource; + +typedef daeSmartRef domSourceRef; +typedef daeTArray domSource_Array; + +class domGeometry; + +typedef daeSmartRef domGeometryRef; +typedef daeTArray domGeometry_Array; + +class domMesh; + +typedef daeSmartRef domMeshRef; +typedef daeTArray domMesh_Array; + +class domSpline; + +typedef daeSmartRef domSplineRef; +typedef daeTArray domSpline_Array; + +class domP; + +typedef daeSmartRef domPRef; +typedef daeTArray domP_Array; + +class domLines; + +typedef daeSmartRef domLinesRef; +typedef daeTArray domLines_Array; + +class domLinestrips; + +typedef daeSmartRef domLinestripsRef; +typedef daeTArray domLinestrips_Array; + +class domPolygons; + +typedef daeSmartRef domPolygonsRef; +typedef daeTArray domPolygons_Array; + +class domPolylist; + +typedef daeSmartRef domPolylistRef; +typedef daeTArray domPolylist_Array; + +class domTriangles; + +typedef daeSmartRef domTrianglesRef; +typedef daeTArray domTriangles_Array; + +class domTrifans; + +typedef daeSmartRef domTrifansRef; +typedef daeTArray domTrifans_Array; + +class domTristrips; + +typedef daeSmartRef domTristripsRef; +typedef daeTArray domTristrips_Array; + +class domVertices; + +typedef daeSmartRef domVerticesRef; +typedef daeTArray domVertices_Array; + +class domLookat; + +typedef daeSmartRef domLookatRef; +typedef daeTArray domLookat_Array; + +class domMatrix; + +typedef daeSmartRef domMatrixRef; +typedef daeTArray domMatrix_Array; + +class domRotate; + +typedef daeSmartRef domRotateRef; +typedef daeTArray domRotate_Array; + +class domScale; + +typedef daeSmartRef domScaleRef; +typedef daeTArray domScale_Array; + +class domSkew; + +typedef daeSmartRef domSkewRef; +typedef daeTArray domSkew_Array; + +class domTranslate; + +typedef daeSmartRef domTranslateRef; +typedef daeTArray domTranslate_Array; + +class domImage; + +typedef daeSmartRef domImageRef; +typedef daeTArray domImage_Array; + +class domLight; + +typedef daeSmartRef domLightRef; +typedef daeTArray domLight_Array; + +class domMaterial; + +typedef daeSmartRef domMaterialRef; +typedef daeTArray domMaterial_Array; + +class domCamera; + +typedef daeSmartRef domCameraRef; +typedef daeTArray domCamera_Array; + +class domAnimation; + +typedef daeSmartRef domAnimationRef; +typedef daeTArray domAnimation_Array; + +class domAnimation_clip; + +typedef daeSmartRef domAnimation_clipRef; +typedef daeTArray domAnimation_clip_Array; + +class domChannel; + +typedef daeSmartRef domChannelRef; +typedef daeTArray domChannel_Array; + +class domSampler; + +typedef daeSmartRef domSamplerRef; +typedef daeTArray domSampler_Array; + +class domController; + +typedef daeSmartRef domControllerRef; +typedef daeTArray domController_Array; + +class domSkin; + +typedef daeSmartRef domSkinRef; +typedef daeTArray domSkin_Array; + +class domMorph; + +typedef daeSmartRef domMorphRef; +typedef daeTArray domMorph_Array; + +class domAsset; + +typedef daeSmartRef domAssetRef; +typedef daeTArray domAsset_Array; + +class domExtra; + +typedef daeSmartRef domExtraRef; +typedef daeTArray domExtra_Array; + +class domTechnique; + +typedef daeSmartRef domTechniqueRef; +typedef daeTArray domTechnique_Array; + +class domNode; + +typedef daeSmartRef domNodeRef; +typedef daeTArray domNode_Array; + +class domVisual_scene; + +typedef daeSmartRef domVisual_sceneRef; +typedef daeTArray domVisual_scene_Array; + +class domBind_material; + +typedef daeSmartRef domBind_materialRef; +typedef daeTArray domBind_material_Array; + +class domInstance_camera; + +typedef daeSmartRef domInstance_cameraRef; +typedef daeTArray domInstance_camera_Array; + +class domInstance_controller; + +typedef daeSmartRef domInstance_controllerRef; +typedef daeTArray domInstance_controller_Array; + +class domInstance_effect; + +typedef daeSmartRef domInstance_effectRef; +typedef daeTArray domInstance_effect_Array; + +class domInstance_force_field; + +typedef daeSmartRef domInstance_force_fieldRef; +typedef daeTArray domInstance_force_field_Array; + +class domInstance_geometry; + +typedef daeSmartRef domInstance_geometryRef; +typedef daeTArray domInstance_geometry_Array; + +class domInstance_light; + +typedef daeSmartRef domInstance_lightRef; +typedef daeTArray domInstance_light_Array; + +class domInstance_material; + +typedef daeSmartRef domInstance_materialRef; +typedef daeTArray domInstance_material_Array; + +class domInstance_node; + +typedef daeSmartRef domInstance_nodeRef; +typedef daeTArray domInstance_node_Array; + +class domInstance_physics_material; + +typedef daeSmartRef domInstance_physics_materialRef; +typedef daeTArray domInstance_physics_material_Array; + +class domInstance_physics_model; + +typedef daeSmartRef domInstance_physics_modelRef; +typedef daeTArray domInstance_physics_model_Array; + +class domInstance_rigid_body; + +typedef daeSmartRef domInstance_rigid_bodyRef; +typedef daeTArray domInstance_rigid_body_Array; + +class domInstance_rigid_constraint; + +typedef daeSmartRef domInstance_rigid_constraintRef; +typedef daeTArray domInstance_rigid_constraint_Array; + +class domLibrary_animations; + +typedef daeSmartRef domLibrary_animationsRef; +typedef daeTArray domLibrary_animations_Array; + +class domLibrary_animation_clips; + +typedef daeSmartRef domLibrary_animation_clipsRef; +typedef daeTArray domLibrary_animation_clips_Array; + +class domLibrary_cameras; + +typedef daeSmartRef domLibrary_camerasRef; +typedef daeTArray domLibrary_cameras_Array; + +class domLibrary_controllers; + +typedef daeSmartRef domLibrary_controllersRef; +typedef daeTArray domLibrary_controllers_Array; + +class domLibrary_geometries; + +typedef daeSmartRef domLibrary_geometriesRef; +typedef daeTArray domLibrary_geometries_Array; + +class domLibrary_effects; + +typedef daeSmartRef domLibrary_effectsRef; +typedef daeTArray domLibrary_effects_Array; + +class domLibrary_force_fields; + +typedef daeSmartRef domLibrary_force_fieldsRef; +typedef daeTArray domLibrary_force_fields_Array; + +class domLibrary_images; + +typedef daeSmartRef domLibrary_imagesRef; +typedef daeTArray domLibrary_images_Array; + +class domLibrary_lights; + +typedef daeSmartRef domLibrary_lightsRef; +typedef daeTArray domLibrary_lights_Array; + +class domLibrary_materials; + +typedef daeSmartRef domLibrary_materialsRef; +typedef daeTArray domLibrary_materials_Array; + +class domLibrary_nodes; + +typedef daeSmartRef domLibrary_nodesRef; +typedef daeTArray domLibrary_nodes_Array; + +class domLibrary_physics_materials; + +typedef daeSmartRef domLibrary_physics_materialsRef; +typedef daeTArray domLibrary_physics_materials_Array; + +class domLibrary_physics_models; + +typedef daeSmartRef domLibrary_physics_modelsRef; +typedef daeTArray domLibrary_physics_models_Array; + +class domLibrary_physics_scenes; + +typedef daeSmartRef domLibrary_physics_scenesRef; +typedef daeTArray domLibrary_physics_scenes_Array; + +class domLibrary_visual_scenes; + +typedef daeSmartRef domLibrary_visual_scenesRef; +typedef daeTArray domLibrary_visual_scenes_Array; + +class domFx_profile_abstract; + +typedef daeSmartRef domFx_profile_abstractRef; +typedef daeTArray domFx_profile_abstract_Array; + +class domEffect; + +typedef daeSmartRef domEffectRef; +typedef daeTArray domEffect_Array; + +class domGl_hook_abstract; + +typedef daeSmartRef domGl_hook_abstractRef; +typedef daeTArray domGl_hook_abstract_Array; + +class domProfile_GLSL; + +typedef daeSmartRef domProfile_GLSLRef; +typedef daeTArray domProfile_GLSL_Array; + +class domProfile_COMMON; + +typedef daeSmartRef domProfile_COMMONRef; +typedef daeTArray domProfile_COMMON_Array; + +class domProfile_CG; + +typedef daeSmartRef domProfile_CGRef; +typedef daeTArray domProfile_CG_Array; + +class domProfile_GLES; + +typedef daeSmartRef domProfile_GLESRef; +typedef daeTArray domProfile_GLES_Array; + +class domBox; + +typedef daeSmartRef domBoxRef; +typedef daeTArray domBox_Array; + +class domPlane; + +typedef daeSmartRef domPlaneRef; +typedef daeTArray domPlane_Array; + +class domSphere; + +typedef daeSmartRef domSphereRef; +typedef daeTArray domSphere_Array; + +class domEllipsoid; + +typedef daeSmartRef domEllipsoidRef; +typedef daeTArray domEllipsoid_Array; + +class domCylinder; + +typedef daeSmartRef domCylinderRef; +typedef daeTArray domCylinder_Array; + +class domTapered_cylinder; + +typedef daeSmartRef domTapered_cylinderRef; +typedef daeTArray domTapered_cylinder_Array; + +class domCapsule; + +typedef daeSmartRef domCapsuleRef; +typedef daeTArray domCapsule_Array; + +class domTapered_capsule; + +typedef daeSmartRef domTapered_capsuleRef; +typedef daeTArray domTapered_capsule_Array; + +class domConvex_mesh; + +typedef daeSmartRef domConvex_meshRef; +typedef daeTArray domConvex_mesh_Array; + +class domForce_field; + +typedef daeSmartRef domForce_fieldRef; +typedef daeTArray domForce_field_Array; + +class domPhysics_material; + +typedef daeSmartRef domPhysics_materialRef; +typedef daeTArray domPhysics_material_Array; + +class domPhysics_scene; + +typedef daeSmartRef domPhysics_sceneRef; +typedef daeTArray domPhysics_scene_Array; + +class domRigid_body; + +typedef daeSmartRef domRigid_bodyRef; +typedef daeTArray domRigid_body_Array; + +class domRigid_constraint; + +typedef daeSmartRef domRigid_constraintRef; +typedef daeTArray domRigid_constraint_Array; + +class domPhysics_model; + +typedef daeSmartRef domPhysics_modelRef; +typedef daeTArray domPhysics_model_Array; + + +#endif //__DOM_ELEMENTS_H__ + diff --git a/Engine/lib/collada/include/1.4/dom/domEllipsoid.h b/Engine/lib/collada/include/1.4/dom/domEllipsoid.h new file mode 100644 index 000000000..f5577a726 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domEllipsoid.h @@ -0,0 +1,133 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domEllipsoid_h__ +#define __domEllipsoid_h__ + +#include +#include +#include + +class DAE; + +class domEllipsoid : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ELLIPSOID; } + static daeInt ID() { return 773; } + virtual daeInt typeID() const { return ID(); } +public: + class domSize; + + typedef daeSmartRef domSizeRef; + typedef daeTArray domSize_Array; + + class domSize : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SIZE; } + static daeInt ID() { return 774; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFloat3 value of the text data of this element. + */ + domFloat3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domFloat3 reference of the _value array. + */ + domFloat3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domFloat3 reference of the _value array. + */ + const domFloat3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domFloat3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domSize(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domSize() {} + /** + * Overloaded assignment operator + */ + virtual domSize &operator=( const domSize &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Element + domSizeRef elemSize; + +public: //Accessors and Mutators + /** + * Gets the size element. + * @return a daeSmartRef to the size element. + */ + const domSizeRef getSize() const { return elemSize; } +protected: + /** + * Constructor + */ + domEllipsoid(DAE& dae) : daeElement(dae), elemSize() {} + /** + * Destructor + */ + virtual ~domEllipsoid() {} + /** + * Overloaded assignment operator + */ + virtual domEllipsoid &operator=( const domEllipsoid &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domExtra.h b/Engine/lib/collada/include/1.4/dom/domExtra.h new file mode 100644 index 000000000..a50306b75 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domExtra.h @@ -0,0 +1,143 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domExtra_h__ +#define __domExtra_h__ + +#include +#include +#include + +#include +#include +class DAE; + +/** + * The extra element declares additional information regarding its parent + * element. + */ +class domExtra : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::EXTRA; } + static daeInt ID() { return 679; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; +/** + * The type attribute indicates the type of the value data. This text string + * must be understood by the application. Optional attribute. + */ + xsNMTOKEN attrType; + +protected: // Elements +/** + * The extra element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * This element must contain at least one non-common profile technique. + * @see domTechnique + */ + domTechnique_Array elemTechnique_array; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the type attribute. + * @return Returns a xsNMTOKEN of the type attribute. + */ + xsNMTOKEN getType() const { return attrType; } + /** + * Sets the type attribute. + * @param atType The new value for the type attribute. + */ + void setType( xsNMTOKEN atType ) { *(daeStringRef*)&attrType = atType; _validAttributeArray[2] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the technique element array. + * @return Returns a reference to the array of technique elements. + */ + domTechnique_Array &getTechnique_array() { return elemTechnique_array; } + /** + * Gets the technique element array. + * @return Returns a constant reference to the array of technique elements. + */ + const domTechnique_Array &getTechnique_array() const { return elemTechnique_array; } +protected: + /** + * Constructor + */ + domExtra(DAE& dae) : daeElement(dae), attrId(), attrName(), attrType(), elemAsset(), elemTechnique_array() {} + /** + * Destructor + */ + virtual ~domExtra() {} + /** + * Overloaded assignment operator + */ + virtual domExtra &operator=( const domExtra &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domFloat_array.h b/Engine/lib/collada/include/1.4/dom/domFloat_array.h new file mode 100644 index 000000000..4e5e1690c --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domFloat_array.h @@ -0,0 +1,171 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domFloat_array_h__ +#define __domFloat_array_h__ + +#include +#include +#include + +class DAE; + +/** + * The float_array element declares the storage for a homogenous array of + * floating point values. + */ +class domFloat_array : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT_ARRAY; } + static daeInt ID() { return 607; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; +/** + * The count attribute indicates the number of values in the array. Required + * attribute. + */ + domUint attrCount; +/** + * The digits attribute indicates the number of significant decimal digits + * of the float values that can be contained in the array. The default value + * is 6. Optional attribute. + */ + xsShort attrDigits; +/** + * The magnitude attribute indicates the largest exponent of the float values + * that can be contained in the array. The default value is 38. Optional + * attribute. + */ + xsShort attrMagnitude; + +protected: // Value + /** + * The domListOfFloats value of the text data of this element. + */ + domListOfFloats _value; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the count attribute. + * @return Returns a domUint of the count attribute. + */ + domUint getCount() const { return attrCount; } + /** + * Sets the count attribute. + * @param atCount The new value for the count attribute. + */ + void setCount( domUint atCount ) { attrCount = atCount; _validAttributeArray[2] = true; } + + /** + * Gets the digits attribute. + * @return Returns a xsShort of the digits attribute. + */ + xsShort getDigits() const { return attrDigits; } + /** + * Sets the digits attribute. + * @param atDigits The new value for the digits attribute. + */ + void setDigits( xsShort atDigits ) { attrDigits = atDigits; _validAttributeArray[3] = true; } + + /** + * Gets the magnitude attribute. + * @return Returns a xsShort of the magnitude attribute. + */ + xsShort getMagnitude() const { return attrMagnitude; } + /** + * Sets the magnitude attribute. + * @param atMagnitude The new value for the magnitude attribute. + */ + void setMagnitude( xsShort atMagnitude ) { attrMagnitude = atMagnitude; _validAttributeArray[4] = true; } + + /** + * Gets the _value array. + * @return Returns a domListOfFloats reference of the _value array. + */ + domListOfFloats &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domListOfFloats reference of the _value array. + */ + const domListOfFloats &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domListOfFloats &val ) { _value = val; } + +protected: + /** + * Constructor + */ + domFloat_array(DAE& dae) : daeElement(dae), attrId(), attrName(), attrCount(), attrDigits(), attrMagnitude(), _value() {} + /** + * Destructor + */ + virtual ~domFloat_array() {} + /** + * Overloaded assignment operator + */ + virtual domFloat_array &operator=( const domFloat_array &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domForce_field.h b/Engine/lib/collada/include/1.4/dom/domForce_field.h new file mode 100644 index 000000000..64c2065e4 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domForce_field.h @@ -0,0 +1,142 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domForce_field_h__ +#define __domForce_field_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * A general container for force-fields. At the moment, it only has techniques + * and extra elements. + */ +class domForce_field : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FORCE_FIELD; } + static daeInt ID() { return 790; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The force_field element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * This element must contain at least one non-common profile technique. + * @see domTechnique + */ + domTechnique_Array elemTechnique_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the technique element array. + * @return Returns a reference to the array of technique elements. + */ + domTechnique_Array &getTechnique_array() { return elemTechnique_array; } + /** + * Gets the technique element array. + * @return Returns a constant reference to the array of technique elements. + */ + const domTechnique_Array &getTechnique_array() const { return elemTechnique_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domForce_field(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemTechnique_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domForce_field() {} + /** + * Overloaded assignment operator + */ + virtual domForce_field &operator=( const domForce_field &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domFx_annotate_common.h b/Engine/lib/collada/include/1.4/dom/domFx_annotate_common.h new file mode 100644 index 000000000..f35b21c07 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domFx_annotate_common.h @@ -0,0 +1,115 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domFx_annotate_common_h__ +#define __domFx_annotate_common_h__ + +#include +#include +#include + +#include +class DAE; + +class domFx_annotate_common_complexType +{ +protected: // Attribute + xsNCName attrName; + +protected: // Element + domFx_annotate_type_commonRef elemFx_annotate_type_common; + +public: //Accessors and Mutators + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName;} + + /** + * Gets the fx_annotate_type_common element. + * @return a daeSmartRef to the fx_annotate_type_common element. + */ + const domFx_annotate_type_commonRef getFx_annotate_type_common() const { return elemFx_annotate_type_common; } +protected: + /** + * Constructor + */ + domFx_annotate_common_complexType(DAE& dae, daeElement* elt) : attrName(), elemFx_annotate_type_common() {} + /** + * Destructor + */ + virtual ~domFx_annotate_common_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domFx_annotate_common_complexType &operator=( const domFx_annotate_common_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domFx_annotate_common_complexType. + */ +class domFx_annotate_common : public daeElement, public domFx_annotate_common_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FX_ANNOTATE_COMMON; } + static daeInt ID() { return 91; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[0] = true; } + +protected: + /** + * Constructor + */ + domFx_annotate_common(DAE& dae) : daeElement(dae), domFx_annotate_common_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domFx_annotate_common() {} + /** + * Overloaded assignment operator + */ + virtual domFx_annotate_common &operator=( const domFx_annotate_common &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domFx_annotate_type_common.h b/Engine/lib/collada/include/1.4/dom/domFx_annotate_type_common.h new file mode 100644 index 000000000..9f9f1c919 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domFx_annotate_type_common.h @@ -0,0 +1,1176 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domFx_annotate_type_common_h__ +#define __domFx_annotate_type_common_h__ + +#include +#include +#include + +class DAE; + +/** + * A group that specifies the allowable types for an annotation. + */ +class domFx_annotate_type_common : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FX_ANNOTATE_TYPE_COMMON; } + static daeInt ID() { return 171; } + virtual daeInt typeID() const { return ID(); } +public: + class domBool; + + typedef daeSmartRef domBoolRef; + typedef daeTArray domBool_Array; + + class domBool : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL; } + static daeInt ID() { return 172; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domBool value of the text data of this element. + */ + ::domBool _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a ::domBool of the value. + */ + ::domBool getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( ::domBool val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool() {} + /** + * Overloaded assignment operator + */ + virtual domBool &operator=( const domBool &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool2; + + typedef daeSmartRef domBool2Ref; + typedef daeTArray domBool2_Array; + + class domBool2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL2; } + static daeInt ID() { return 173; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domBool2 value of the text data of this element. + */ + ::domBool2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domBool2 reference of the _value array. + */ + ::domBool2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domBool2 reference of the _value array. + */ + const ::domBool2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domBool2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool2() {} + /** + * Overloaded assignment operator + */ + virtual domBool2 &operator=( const domBool2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool3; + + typedef daeSmartRef domBool3Ref; + typedef daeTArray domBool3_Array; + + class domBool3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL3; } + static daeInt ID() { return 174; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domBool3 value of the text data of this element. + */ + ::domBool3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domBool3 reference of the _value array. + */ + ::domBool3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domBool3 reference of the _value array. + */ + const ::domBool3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domBool3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool3() {} + /** + * Overloaded assignment operator + */ + virtual domBool3 &operator=( const domBool3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool4; + + typedef daeSmartRef domBool4Ref; + typedef daeTArray domBool4_Array; + + class domBool4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL4; } + static daeInt ID() { return 175; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domBool4 value of the text data of this element. + */ + ::domBool4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domBool4 reference of the _value array. + */ + ::domBool4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domBool4 reference of the _value array. + */ + const ::domBool4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domBool4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool4() {} + /** + * Overloaded assignment operator + */ + virtual domBool4 &operator=( const domBool4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt; + + typedef daeSmartRef domIntRef; + typedef daeTArray domInt_Array; + + class domInt : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT; } + static daeInt ID() { return 176; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domInt value of the text data of this element. + */ + ::domInt _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a ::domInt of the value. + */ + ::domInt getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( ::domInt val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt() {} + /** + * Overloaded assignment operator + */ + virtual domInt &operator=( const domInt &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt2; + + typedef daeSmartRef domInt2Ref; + typedef daeTArray domInt2_Array; + + class domInt2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT2; } + static daeInt ID() { return 177; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domInt2 value of the text data of this element. + */ + ::domInt2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domInt2 reference of the _value array. + */ + ::domInt2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domInt2 reference of the _value array. + */ + const ::domInt2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domInt2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt2() {} + /** + * Overloaded assignment operator + */ + virtual domInt2 &operator=( const domInt2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt3; + + typedef daeSmartRef domInt3Ref; + typedef daeTArray domInt3_Array; + + class domInt3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT3; } + static daeInt ID() { return 178; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domInt3 value of the text data of this element. + */ + ::domInt3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domInt3 reference of the _value array. + */ + ::domInt3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domInt3 reference of the _value array. + */ + const ::domInt3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domInt3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt3() {} + /** + * Overloaded assignment operator + */ + virtual domInt3 &operator=( const domInt3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt4; + + typedef daeSmartRef domInt4Ref; + typedef daeTArray domInt4_Array; + + class domInt4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT4; } + static daeInt ID() { return 179; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domInt4 value of the text data of this element. + */ + ::domInt4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domInt4 reference of the _value array. + */ + ::domInt4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domInt4 reference of the _value array. + */ + const ::domInt4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domInt4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt4() {} + /** + * Overloaded assignment operator + */ + virtual domInt4 &operator=( const domInt4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat; + + typedef daeSmartRef domFloatRef; + typedef daeTArray domFloat_Array; + + class domFloat : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT; } + static daeInt ID() { return 180; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat value of the text data of this element. + */ + ::domFloat _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a ::domFloat of the value. + */ + ::domFloat getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( ::domFloat val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat() {} + /** + * Overloaded assignment operator + */ + virtual domFloat &operator=( const domFloat &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat2; + + typedef daeSmartRef domFloat2Ref; + typedef daeTArray domFloat2_Array; + + class domFloat2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT2; } + static daeInt ID() { return 181; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat2 value of the text data of this element. + */ + ::domFloat2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat2 reference of the _value array. + */ + ::domFloat2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat2 reference of the _value array. + */ + const ::domFloat2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat2() {} + /** + * Overloaded assignment operator + */ + virtual domFloat2 &operator=( const domFloat2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat3; + + typedef daeSmartRef domFloat3Ref; + typedef daeTArray domFloat3_Array; + + class domFloat3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT3; } + static daeInt ID() { return 182; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat3 value of the text data of this element. + */ + ::domFloat3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat3 reference of the _value array. + */ + ::domFloat3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat3 reference of the _value array. + */ + const ::domFloat3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat3() {} + /** + * Overloaded assignment operator + */ + virtual domFloat3 &operator=( const domFloat3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat4; + + typedef daeSmartRef domFloat4Ref; + typedef daeTArray domFloat4_Array; + + class domFloat4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT4; } + static daeInt ID() { return 183; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat4 value of the text data of this element. + */ + ::domFloat4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat4 reference of the _value array. + */ + ::domFloat4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat4 reference of the _value array. + */ + const ::domFloat4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat4() {} + /** + * Overloaded assignment operator + */ + virtual domFloat4 &operator=( const domFloat4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat2x2; + + typedef daeSmartRef domFloat2x2Ref; + typedef daeTArray domFloat2x2_Array; + + class domFloat2x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT2X2; } + static daeInt ID() { return 184; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat2x2 value of the text data of this element. + */ + ::domFloat2x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat2x2 reference of the _value array. + */ + ::domFloat2x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat2x2 reference of the _value array. + */ + const ::domFloat2x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat2x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat2x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat2x2() {} + /** + * Overloaded assignment operator + */ + virtual domFloat2x2 &operator=( const domFloat2x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat3x3; + + typedef daeSmartRef domFloat3x3Ref; + typedef daeTArray domFloat3x3_Array; + + class domFloat3x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT3X3; } + static daeInt ID() { return 185; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat3x3 value of the text data of this element. + */ + ::domFloat3x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat3x3 reference of the _value array. + */ + ::domFloat3x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat3x3 reference of the _value array. + */ + const ::domFloat3x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat3x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat3x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat3x3() {} + /** + * Overloaded assignment operator + */ + virtual domFloat3x3 &operator=( const domFloat3x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat4x4; + + typedef daeSmartRef domFloat4x4Ref; + typedef daeTArray domFloat4x4_Array; + + class domFloat4x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT4X4; } + static daeInt ID() { return 186; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat4x4 value of the text data of this element. + */ + ::domFloat4x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat4x4 reference of the _value array. + */ + ::domFloat4x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat4x4 reference of the _value array. + */ + const ::domFloat4x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat4x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat4x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat4x4() {} + /** + * Overloaded assignment operator + */ + virtual domFloat4x4 &operator=( const domFloat4x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domString; + + typedef daeSmartRef domStringRef; + typedef daeTArray domString_Array; + + class domString : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::STRING; } + static daeInt ID() { return 187; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::xsString value of the text data of this element. + */ + ::xsString _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a ::xsString of the value. + */ + ::xsString getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( ::xsString val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domString(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domString() {} + /** + * Overloaded assignment operator + */ + virtual domString &operator=( const domString &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements + domBoolRef elemBool; + domBool2Ref elemBool2; + domBool3Ref elemBool3; + domBool4Ref elemBool4; + domIntRef elemInt; + domInt2Ref elemInt2; + domInt3Ref elemInt3; + domInt4Ref elemInt4; + domFloatRef elemFloat; + domFloat2Ref elemFloat2; + domFloat3Ref elemFloat3; + domFloat4Ref elemFloat4; + domFloat2x2Ref elemFloat2x2; + domFloat3x3Ref elemFloat3x3; + domFloat4x4Ref elemFloat4x4; + domStringRef elemString; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the bool element. + * @return a daeSmartRef to the bool element. + */ + const domBoolRef getBool() const { return elemBool; } + /** + * Gets the bool2 element. + * @return a daeSmartRef to the bool2 element. + */ + const domBool2Ref getBool2() const { return elemBool2; } + /** + * Gets the bool3 element. + * @return a daeSmartRef to the bool3 element. + */ + const domBool3Ref getBool3() const { return elemBool3; } + /** + * Gets the bool4 element. + * @return a daeSmartRef to the bool4 element. + */ + const domBool4Ref getBool4() const { return elemBool4; } + /** + * Gets the int element. + * @return a daeSmartRef to the int element. + */ + const domIntRef getInt() const { return elemInt; } + /** + * Gets the int2 element. + * @return a daeSmartRef to the int2 element. + */ + const domInt2Ref getInt2() const { return elemInt2; } + /** + * Gets the int3 element. + * @return a daeSmartRef to the int3 element. + */ + const domInt3Ref getInt3() const { return elemInt3; } + /** + * Gets the int4 element. + * @return a daeSmartRef to the int4 element. + */ + const domInt4Ref getInt4() const { return elemInt4; } + /** + * Gets the float element. + * @return a daeSmartRef to the float element. + */ + const domFloatRef getFloat() const { return elemFloat; } + /** + * Gets the float2 element. + * @return a daeSmartRef to the float2 element. + */ + const domFloat2Ref getFloat2() const { return elemFloat2; } + /** + * Gets the float3 element. + * @return a daeSmartRef to the float3 element. + */ + const domFloat3Ref getFloat3() const { return elemFloat3; } + /** + * Gets the float4 element. + * @return a daeSmartRef to the float4 element. + */ + const domFloat4Ref getFloat4() const { return elemFloat4; } + /** + * Gets the float2x2 element. + * @return a daeSmartRef to the float2x2 element. + */ + const domFloat2x2Ref getFloat2x2() const { return elemFloat2x2; } + /** + * Gets the float3x3 element. + * @return a daeSmartRef to the float3x3 element. + */ + const domFloat3x3Ref getFloat3x3() const { return elemFloat3x3; } + /** + * Gets the float4x4 element. + * @return a daeSmartRef to the float4x4 element. + */ + const domFloat4x4Ref getFloat4x4() const { return elemFloat4x4; } + /** + * Gets the string element. + * @return a daeSmartRef to the string element. + */ + const domStringRef getString() const { return elemString; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domFx_annotate_type_common(DAE& dae) : daeElement(dae), elemBool(), elemBool2(), elemBool3(), elemBool4(), elemInt(), elemInt2(), elemInt3(), elemInt4(), elemFloat(), elemFloat2(), elemFloat3(), elemFloat4(), elemFloat2x2(), elemFloat3x3(), elemFloat4x4(), elemString() {} + /** + * Destructor + */ + virtual ~domFx_annotate_type_common() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domFx_annotate_type_common &operator=( const domFx_annotate_type_common &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domFx_basic_type_common.h b/Engine/lib/collada/include/1.4/dom/domFx_basic_type_common.h new file mode 100644 index 000000000..7011433e6 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domFx_basic_type_common.h @@ -0,0 +1,2117 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domFx_basic_type_common_h__ +#define __domFx_basic_type_common_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +class DAE; + +/** + * A group that specifies the allowable types for effect scoped parameters. + */ +class domFx_basic_type_common : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FX_BASIC_TYPE_COMMON; } + static daeInt ID() { return 188; } + virtual daeInt typeID() const { return ID(); } +public: + class domBool; + + typedef daeSmartRef domBoolRef; + typedef daeTArray domBool_Array; + + class domBool : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL; } + static daeInt ID() { return 189; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domBool value of the text data of this element. + */ + ::domBool _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a ::domBool of the value. + */ + ::domBool getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( ::domBool val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool() {} + /** + * Overloaded assignment operator + */ + virtual domBool &operator=( const domBool &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool2; + + typedef daeSmartRef domBool2Ref; + typedef daeTArray domBool2_Array; + + class domBool2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL2; } + static daeInt ID() { return 190; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domBool2 value of the text data of this element. + */ + ::domBool2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domBool2 reference of the _value array. + */ + ::domBool2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domBool2 reference of the _value array. + */ + const ::domBool2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domBool2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool2() {} + /** + * Overloaded assignment operator + */ + virtual domBool2 &operator=( const domBool2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool3; + + typedef daeSmartRef domBool3Ref; + typedef daeTArray domBool3_Array; + + class domBool3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL3; } + static daeInt ID() { return 191; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domBool3 value of the text data of this element. + */ + ::domBool3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domBool3 reference of the _value array. + */ + ::domBool3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domBool3 reference of the _value array. + */ + const ::domBool3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domBool3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool3() {} + /** + * Overloaded assignment operator + */ + virtual domBool3 &operator=( const domBool3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool4; + + typedef daeSmartRef domBool4Ref; + typedef daeTArray domBool4_Array; + + class domBool4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL4; } + static daeInt ID() { return 192; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domBool4 value of the text data of this element. + */ + ::domBool4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domBool4 reference of the _value array. + */ + ::domBool4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domBool4 reference of the _value array. + */ + const ::domBool4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domBool4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool4() {} + /** + * Overloaded assignment operator + */ + virtual domBool4 &operator=( const domBool4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt; + + typedef daeSmartRef domIntRef; + typedef daeTArray domInt_Array; + + class domInt : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT; } + static daeInt ID() { return 193; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domInt value of the text data of this element. + */ + ::domInt _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a ::domInt of the value. + */ + ::domInt getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( ::domInt val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt() {} + /** + * Overloaded assignment operator + */ + virtual domInt &operator=( const domInt &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt2; + + typedef daeSmartRef domInt2Ref; + typedef daeTArray domInt2_Array; + + class domInt2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT2; } + static daeInt ID() { return 194; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domInt2 value of the text data of this element. + */ + ::domInt2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domInt2 reference of the _value array. + */ + ::domInt2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domInt2 reference of the _value array. + */ + const ::domInt2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domInt2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt2() {} + /** + * Overloaded assignment operator + */ + virtual domInt2 &operator=( const domInt2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt3; + + typedef daeSmartRef domInt3Ref; + typedef daeTArray domInt3_Array; + + class domInt3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT3; } + static daeInt ID() { return 195; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domInt3 value of the text data of this element. + */ + ::domInt3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domInt3 reference of the _value array. + */ + ::domInt3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domInt3 reference of the _value array. + */ + const ::domInt3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domInt3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt3() {} + /** + * Overloaded assignment operator + */ + virtual domInt3 &operator=( const domInt3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt4; + + typedef daeSmartRef domInt4Ref; + typedef daeTArray domInt4_Array; + + class domInt4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT4; } + static daeInt ID() { return 196; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domInt4 value of the text data of this element. + */ + ::domInt4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domInt4 reference of the _value array. + */ + ::domInt4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domInt4 reference of the _value array. + */ + const ::domInt4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domInt4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt4() {} + /** + * Overloaded assignment operator + */ + virtual domInt4 &operator=( const domInt4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat; + + typedef daeSmartRef domFloatRef; + typedef daeTArray domFloat_Array; + + class domFloat : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT; } + static daeInt ID() { return 197; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat value of the text data of this element. + */ + ::domFloat _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a ::domFloat of the value. + */ + ::domFloat getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( ::domFloat val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat() {} + /** + * Overloaded assignment operator + */ + virtual domFloat &operator=( const domFloat &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat2; + + typedef daeSmartRef domFloat2Ref; + typedef daeTArray domFloat2_Array; + + class domFloat2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT2; } + static daeInt ID() { return 198; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat2 value of the text data of this element. + */ + ::domFloat2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat2 reference of the _value array. + */ + ::domFloat2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat2 reference of the _value array. + */ + const ::domFloat2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat2() {} + /** + * Overloaded assignment operator + */ + virtual domFloat2 &operator=( const domFloat2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat3; + + typedef daeSmartRef domFloat3Ref; + typedef daeTArray domFloat3_Array; + + class domFloat3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT3; } + static daeInt ID() { return 199; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat3 value of the text data of this element. + */ + ::domFloat3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat3 reference of the _value array. + */ + ::domFloat3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat3 reference of the _value array. + */ + const ::domFloat3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat3() {} + /** + * Overloaded assignment operator + */ + virtual domFloat3 &operator=( const domFloat3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat4; + + typedef daeSmartRef domFloat4Ref; + typedef daeTArray domFloat4_Array; + + class domFloat4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT4; } + static daeInt ID() { return 200; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat4 value of the text data of this element. + */ + ::domFloat4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat4 reference of the _value array. + */ + ::domFloat4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat4 reference of the _value array. + */ + const ::domFloat4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat4() {} + /** + * Overloaded assignment operator + */ + virtual domFloat4 &operator=( const domFloat4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat1x1; + + typedef daeSmartRef domFloat1x1Ref; + typedef daeTArray domFloat1x1_Array; + + class domFloat1x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT1X1; } + static daeInt ID() { return 201; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat value of the text data of this element. + */ + ::domFloat _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a ::domFloat of the value. + */ + ::domFloat getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( ::domFloat val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat1x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat1x1() {} + /** + * Overloaded assignment operator + */ + virtual domFloat1x1 &operator=( const domFloat1x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat1x2; + + typedef daeSmartRef domFloat1x2Ref; + typedef daeTArray domFloat1x2_Array; + + class domFloat1x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT1X2; } + static daeInt ID() { return 202; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat2 value of the text data of this element. + */ + ::domFloat2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat2 reference of the _value array. + */ + ::domFloat2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat2 reference of the _value array. + */ + const ::domFloat2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat1x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat1x2() {} + /** + * Overloaded assignment operator + */ + virtual domFloat1x2 &operator=( const domFloat1x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat1x3; + + typedef daeSmartRef domFloat1x3Ref; + typedef daeTArray domFloat1x3_Array; + + class domFloat1x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT1X3; } + static daeInt ID() { return 203; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat3 value of the text data of this element. + */ + ::domFloat3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat3 reference of the _value array. + */ + ::domFloat3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat3 reference of the _value array. + */ + const ::domFloat3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat1x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat1x3() {} + /** + * Overloaded assignment operator + */ + virtual domFloat1x3 &operator=( const domFloat1x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat1x4; + + typedef daeSmartRef domFloat1x4Ref; + typedef daeTArray domFloat1x4_Array; + + class domFloat1x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT1X4; } + static daeInt ID() { return 204; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat4 value of the text data of this element. + */ + ::domFloat4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat4 reference of the _value array. + */ + ::domFloat4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat4 reference of the _value array. + */ + const ::domFloat4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat1x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat1x4() {} + /** + * Overloaded assignment operator + */ + virtual domFloat1x4 &operator=( const domFloat1x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat2x1; + + typedef daeSmartRef domFloat2x1Ref; + typedef daeTArray domFloat2x1_Array; + + class domFloat2x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT2X1; } + static daeInt ID() { return 205; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat2 value of the text data of this element. + */ + ::domFloat2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat2 reference of the _value array. + */ + ::domFloat2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat2 reference of the _value array. + */ + const ::domFloat2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat2x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat2x1() {} + /** + * Overloaded assignment operator + */ + virtual domFloat2x1 &operator=( const domFloat2x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat2x2; + + typedef daeSmartRef domFloat2x2Ref; + typedef daeTArray domFloat2x2_Array; + + class domFloat2x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT2X2; } + static daeInt ID() { return 206; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat2x2 value of the text data of this element. + */ + ::domFloat2x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat2x2 reference of the _value array. + */ + ::domFloat2x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat2x2 reference of the _value array. + */ + const ::domFloat2x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat2x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat2x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat2x2() {} + /** + * Overloaded assignment operator + */ + virtual domFloat2x2 &operator=( const domFloat2x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat2x3; + + typedef daeSmartRef domFloat2x3Ref; + typedef daeTArray domFloat2x3_Array; + + class domFloat2x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT2X3; } + static daeInt ID() { return 207; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat2x3 value of the text data of this element. + */ + ::domFloat2x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat2x3 reference of the _value array. + */ + ::domFloat2x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat2x3 reference of the _value array. + */ + const ::domFloat2x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat2x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat2x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat2x3() {} + /** + * Overloaded assignment operator + */ + virtual domFloat2x3 &operator=( const domFloat2x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat2x4; + + typedef daeSmartRef domFloat2x4Ref; + typedef daeTArray domFloat2x4_Array; + + class domFloat2x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT2X4; } + static daeInt ID() { return 208; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat2x4 value of the text data of this element. + */ + ::domFloat2x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat2x4 reference of the _value array. + */ + ::domFloat2x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat2x4 reference of the _value array. + */ + const ::domFloat2x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat2x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat2x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat2x4() {} + /** + * Overloaded assignment operator + */ + virtual domFloat2x4 &operator=( const domFloat2x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat3x1; + + typedef daeSmartRef domFloat3x1Ref; + typedef daeTArray domFloat3x1_Array; + + class domFloat3x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT3X1; } + static daeInt ID() { return 209; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat3 value of the text data of this element. + */ + ::domFloat3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat3 reference of the _value array. + */ + ::domFloat3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat3 reference of the _value array. + */ + const ::domFloat3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat3x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat3x1() {} + /** + * Overloaded assignment operator + */ + virtual domFloat3x1 &operator=( const domFloat3x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat3x2; + + typedef daeSmartRef domFloat3x2Ref; + typedef daeTArray domFloat3x2_Array; + + class domFloat3x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT3X2; } + static daeInt ID() { return 210; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat3x2 value of the text data of this element. + */ + ::domFloat3x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat3x2 reference of the _value array. + */ + ::domFloat3x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat3x2 reference of the _value array. + */ + const ::domFloat3x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat3x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat3x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat3x2() {} + /** + * Overloaded assignment operator + */ + virtual domFloat3x2 &operator=( const domFloat3x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat3x3; + + typedef daeSmartRef domFloat3x3Ref; + typedef daeTArray domFloat3x3_Array; + + class domFloat3x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT3X3; } + static daeInt ID() { return 211; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat3x3 value of the text data of this element. + */ + ::domFloat3x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat3x3 reference of the _value array. + */ + ::domFloat3x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat3x3 reference of the _value array. + */ + const ::domFloat3x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat3x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat3x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat3x3() {} + /** + * Overloaded assignment operator + */ + virtual domFloat3x3 &operator=( const domFloat3x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat3x4; + + typedef daeSmartRef domFloat3x4Ref; + typedef daeTArray domFloat3x4_Array; + + class domFloat3x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT3X4; } + static daeInt ID() { return 212; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat3x4 value of the text data of this element. + */ + ::domFloat3x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat3x4 reference of the _value array. + */ + ::domFloat3x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat3x4 reference of the _value array. + */ + const ::domFloat3x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat3x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat3x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat3x4() {} + /** + * Overloaded assignment operator + */ + virtual domFloat3x4 &operator=( const domFloat3x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat4x1; + + typedef daeSmartRef domFloat4x1Ref; + typedef daeTArray domFloat4x1_Array; + + class domFloat4x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT4X1; } + static daeInt ID() { return 213; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat4 value of the text data of this element. + */ + ::domFloat4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat4 reference of the _value array. + */ + ::domFloat4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat4 reference of the _value array. + */ + const ::domFloat4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat4x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat4x1() {} + /** + * Overloaded assignment operator + */ + virtual domFloat4x1 &operator=( const domFloat4x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat4x2; + + typedef daeSmartRef domFloat4x2Ref; + typedef daeTArray domFloat4x2_Array; + + class domFloat4x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT4X2; } + static daeInt ID() { return 214; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat4x2 value of the text data of this element. + */ + ::domFloat4x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat4x2 reference of the _value array. + */ + ::domFloat4x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat4x2 reference of the _value array. + */ + const ::domFloat4x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat4x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat4x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat4x2() {} + /** + * Overloaded assignment operator + */ + virtual domFloat4x2 &operator=( const domFloat4x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat4x3; + + typedef daeSmartRef domFloat4x3Ref; + typedef daeTArray domFloat4x3_Array; + + class domFloat4x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT4X3; } + static daeInt ID() { return 215; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat4x3 value of the text data of this element. + */ + ::domFloat4x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat4x3 reference of the _value array. + */ + ::domFloat4x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat4x3 reference of the _value array. + */ + const ::domFloat4x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat4x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat4x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat4x3() {} + /** + * Overloaded assignment operator + */ + virtual domFloat4x3 &operator=( const domFloat4x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat4x4; + + typedef daeSmartRef domFloat4x4Ref; + typedef daeTArray domFloat4x4_Array; + + class domFloat4x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT4X4; } + static daeInt ID() { return 216; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat4x4 value of the text data of this element. + */ + ::domFloat4x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat4x4 reference of the _value array. + */ + ::domFloat4x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat4x4 reference of the _value array. + */ + const ::domFloat4x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat4x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat4x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat4x4() {} + /** + * Overloaded assignment operator + */ + virtual domFloat4x4 &operator=( const domFloat4x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domEnum; + + typedef daeSmartRef domEnumRef; + typedef daeTArray domEnum_Array; + + class domEnum : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ENUM; } + static daeInt ID() { return 217; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsString value of the text data of this element. + */ + xsString _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsString of the value. + */ + xsString getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsString val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domEnum(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domEnum() {} + /** + * Overloaded assignment operator + */ + virtual domEnum &operator=( const domEnum &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements + domBoolRef elemBool; + domBool2Ref elemBool2; + domBool3Ref elemBool3; + domBool4Ref elemBool4; + domIntRef elemInt; + domInt2Ref elemInt2; + domInt3Ref elemInt3; + domInt4Ref elemInt4; + domFloatRef elemFloat; + domFloat2Ref elemFloat2; + domFloat3Ref elemFloat3; + domFloat4Ref elemFloat4; + domFloat1x1Ref elemFloat1x1; + domFloat1x2Ref elemFloat1x2; + domFloat1x3Ref elemFloat1x3; + domFloat1x4Ref elemFloat1x4; + domFloat2x1Ref elemFloat2x1; + domFloat2x2Ref elemFloat2x2; + domFloat2x3Ref elemFloat2x3; + domFloat2x4Ref elemFloat2x4; + domFloat3x1Ref elemFloat3x1; + domFloat3x2Ref elemFloat3x2; + domFloat3x3Ref elemFloat3x3; + domFloat3x4Ref elemFloat3x4; + domFloat4x1Ref elemFloat4x1; + domFloat4x2Ref elemFloat4x2; + domFloat4x3Ref elemFloat4x3; + domFloat4x4Ref elemFloat4x4; + domFx_surface_commonRef elemSurface; + domFx_sampler1D_commonRef elemSampler1D; + domFx_sampler2D_commonRef elemSampler2D; + domFx_sampler3D_commonRef elemSampler3D; + domFx_samplerCUBE_commonRef elemSamplerCUBE; + domFx_samplerRECT_commonRef elemSamplerRECT; + domFx_samplerDEPTH_commonRef elemSamplerDEPTH; + domEnumRef elemEnum; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the bool element. + * @return a daeSmartRef to the bool element. + */ + const domBoolRef getBool() const { return elemBool; } + /** + * Gets the bool2 element. + * @return a daeSmartRef to the bool2 element. + */ + const domBool2Ref getBool2() const { return elemBool2; } + /** + * Gets the bool3 element. + * @return a daeSmartRef to the bool3 element. + */ + const domBool3Ref getBool3() const { return elemBool3; } + /** + * Gets the bool4 element. + * @return a daeSmartRef to the bool4 element. + */ + const domBool4Ref getBool4() const { return elemBool4; } + /** + * Gets the int element. + * @return a daeSmartRef to the int element. + */ + const domIntRef getInt() const { return elemInt; } + /** + * Gets the int2 element. + * @return a daeSmartRef to the int2 element. + */ + const domInt2Ref getInt2() const { return elemInt2; } + /** + * Gets the int3 element. + * @return a daeSmartRef to the int3 element. + */ + const domInt3Ref getInt3() const { return elemInt3; } + /** + * Gets the int4 element. + * @return a daeSmartRef to the int4 element. + */ + const domInt4Ref getInt4() const { return elemInt4; } + /** + * Gets the float element. + * @return a daeSmartRef to the float element. + */ + const domFloatRef getFloat() const { return elemFloat; } + /** + * Gets the float2 element. + * @return a daeSmartRef to the float2 element. + */ + const domFloat2Ref getFloat2() const { return elemFloat2; } + /** + * Gets the float3 element. + * @return a daeSmartRef to the float3 element. + */ + const domFloat3Ref getFloat3() const { return elemFloat3; } + /** + * Gets the float4 element. + * @return a daeSmartRef to the float4 element. + */ + const domFloat4Ref getFloat4() const { return elemFloat4; } + /** + * Gets the float1x1 element. + * @return a daeSmartRef to the float1x1 element. + */ + const domFloat1x1Ref getFloat1x1() const { return elemFloat1x1; } + /** + * Gets the float1x2 element. + * @return a daeSmartRef to the float1x2 element. + */ + const domFloat1x2Ref getFloat1x2() const { return elemFloat1x2; } + /** + * Gets the float1x3 element. + * @return a daeSmartRef to the float1x3 element. + */ + const domFloat1x3Ref getFloat1x3() const { return elemFloat1x3; } + /** + * Gets the float1x4 element. + * @return a daeSmartRef to the float1x4 element. + */ + const domFloat1x4Ref getFloat1x4() const { return elemFloat1x4; } + /** + * Gets the float2x1 element. + * @return a daeSmartRef to the float2x1 element. + */ + const domFloat2x1Ref getFloat2x1() const { return elemFloat2x1; } + /** + * Gets the float2x2 element. + * @return a daeSmartRef to the float2x2 element. + */ + const domFloat2x2Ref getFloat2x2() const { return elemFloat2x2; } + /** + * Gets the float2x3 element. + * @return a daeSmartRef to the float2x3 element. + */ + const domFloat2x3Ref getFloat2x3() const { return elemFloat2x3; } + /** + * Gets the float2x4 element. + * @return a daeSmartRef to the float2x4 element. + */ + const domFloat2x4Ref getFloat2x4() const { return elemFloat2x4; } + /** + * Gets the float3x1 element. + * @return a daeSmartRef to the float3x1 element. + */ + const domFloat3x1Ref getFloat3x1() const { return elemFloat3x1; } + /** + * Gets the float3x2 element. + * @return a daeSmartRef to the float3x2 element. + */ + const domFloat3x2Ref getFloat3x2() const { return elemFloat3x2; } + /** + * Gets the float3x3 element. + * @return a daeSmartRef to the float3x3 element. + */ + const domFloat3x3Ref getFloat3x3() const { return elemFloat3x3; } + /** + * Gets the float3x4 element. + * @return a daeSmartRef to the float3x4 element. + */ + const domFloat3x4Ref getFloat3x4() const { return elemFloat3x4; } + /** + * Gets the float4x1 element. + * @return a daeSmartRef to the float4x1 element. + */ + const domFloat4x1Ref getFloat4x1() const { return elemFloat4x1; } + /** + * Gets the float4x2 element. + * @return a daeSmartRef to the float4x2 element. + */ + const domFloat4x2Ref getFloat4x2() const { return elemFloat4x2; } + /** + * Gets the float4x3 element. + * @return a daeSmartRef to the float4x3 element. + */ + const domFloat4x3Ref getFloat4x3() const { return elemFloat4x3; } + /** + * Gets the float4x4 element. + * @return a daeSmartRef to the float4x4 element. + */ + const domFloat4x4Ref getFloat4x4() const { return elemFloat4x4; } + /** + * Gets the surface element. + * @return a daeSmartRef to the surface element. + */ + const domFx_surface_commonRef getSurface() const { return elemSurface; } + /** + * Gets the sampler1D element. + * @return a daeSmartRef to the sampler1D element. + */ + const domFx_sampler1D_commonRef getSampler1D() const { return elemSampler1D; } + /** + * Gets the sampler2D element. + * @return a daeSmartRef to the sampler2D element. + */ + const domFx_sampler2D_commonRef getSampler2D() const { return elemSampler2D; } + /** + * Gets the sampler3D element. + * @return a daeSmartRef to the sampler3D element. + */ + const domFx_sampler3D_commonRef getSampler3D() const { return elemSampler3D; } + /** + * Gets the samplerCUBE element. + * @return a daeSmartRef to the samplerCUBE element. + */ + const domFx_samplerCUBE_commonRef getSamplerCUBE() const { return elemSamplerCUBE; } + /** + * Gets the samplerRECT element. + * @return a daeSmartRef to the samplerRECT element. + */ + const domFx_samplerRECT_commonRef getSamplerRECT() const { return elemSamplerRECT; } + /** + * Gets the samplerDEPTH element. + * @return a daeSmartRef to the samplerDEPTH element. + */ + const domFx_samplerDEPTH_commonRef getSamplerDEPTH() const { return elemSamplerDEPTH; } + /** + * Gets the enum element. + * @return a daeSmartRef to the enum element. + */ + const domEnumRef getEnum() const { return elemEnum; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domFx_basic_type_common(DAE& dae) : daeElement(dae), elemBool(), elemBool2(), elemBool3(), elemBool4(), elemInt(), elemInt2(), elemInt3(), elemInt4(), elemFloat(), elemFloat2(), elemFloat3(), elemFloat4(), elemFloat1x1(), elemFloat1x2(), elemFloat1x3(), elemFloat1x4(), elemFloat2x1(), elemFloat2x2(), elemFloat2x3(), elemFloat2x4(), elemFloat3x1(), elemFloat3x2(), elemFloat3x3(), elemFloat3x4(), elemFloat4x1(), elemFloat4x2(), elemFloat4x3(), elemFloat4x4(), elemSurface(), elemSampler1D(), elemSampler2D(), elemSampler3D(), elemSamplerCUBE(), elemSamplerRECT(), elemSamplerDEPTH(), elemEnum() {} + /** + * Destructor + */ + virtual ~domFx_basic_type_common() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domFx_basic_type_common &operator=( const domFx_basic_type_common &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domFx_clearcolor_common.h b/Engine/lib/collada/include/1.4/dom/domFx_clearcolor_common.h new file mode 100644 index 000000000..21c861351 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domFx_clearcolor_common.h @@ -0,0 +1,128 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domFx_clearcolor_common_h__ +#define __domFx_clearcolor_common_h__ + +#include +#include +#include + +class DAE; + +class domFx_clearcolor_common_complexType +{ +protected: // Attribute + xsNonNegativeInteger attrIndex; + +protected: // Value + /** + * The domFx_color_common value of the text data of this element. + */ + domFx_color_common _value; + +public: //Accessors and Mutators + /** + * Gets the index attribute. + * @return Returns a xsNonNegativeInteger of the index attribute. + */ + xsNonNegativeInteger getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( xsNonNegativeInteger atIndex ) { attrIndex = atIndex; } + + /** + * Gets the _value array. + * @return Returns a domFx_color_common reference of the _value array. + */ + domFx_color_common &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domFx_color_common reference of the _value array. + */ + const domFx_color_common &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domFx_color_common &val ) { _value = val; } + +protected: + /** + * Constructor + */ + domFx_clearcolor_common_complexType(DAE& dae, daeElement* elt) : attrIndex(), _value() {} + /** + * Destructor + */ + virtual ~domFx_clearcolor_common_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domFx_clearcolor_common_complexType &operator=( const domFx_clearcolor_common_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domFx_clearcolor_common_complexType. + */ +class domFx_clearcolor_common : public daeElement, public domFx_clearcolor_common_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FX_CLEARCOLOR_COMMON; } + static daeInt ID() { return 88; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the index attribute. + * @return Returns a xsNonNegativeInteger of the index attribute. + */ + xsNonNegativeInteger getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( xsNonNegativeInteger atIndex ) { attrIndex = atIndex; _validAttributeArray[0] = true; } + +protected: + /** + * Constructor + */ + domFx_clearcolor_common(DAE& dae) : daeElement(dae), domFx_clearcolor_common_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domFx_clearcolor_common() {} + /** + * Overloaded assignment operator + */ + virtual domFx_clearcolor_common &operator=( const domFx_clearcolor_common &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domFx_cleardepth_common.h b/Engine/lib/collada/include/1.4/dom/domFx_cleardepth_common.h new file mode 100644 index 000000000..1fb444a0a --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domFx_cleardepth_common.h @@ -0,0 +1,123 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domFx_cleardepth_common_h__ +#define __domFx_cleardepth_common_h__ + +#include +#include +#include + +class DAE; + +class domFx_cleardepth_common_complexType +{ +protected: // Attribute + xsNonNegativeInteger attrIndex; + +protected: // Value + /** + * The domFloat value of the text data of this element. + */ + domFloat _value; + +public: //Accessors and Mutators + /** + * Gets the index attribute. + * @return Returns a xsNonNegativeInteger of the index attribute. + */ + xsNonNegativeInteger getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( xsNonNegativeInteger atIndex ) { attrIndex = atIndex; } + + /** + * Gets the value of this element. + * @return a domFloat of the value. + */ + domFloat getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFloat val ) { _value = val; } + +protected: + /** + * Constructor + */ + domFx_cleardepth_common_complexType(DAE& dae, daeElement* elt) : attrIndex(), _value() {} + /** + * Destructor + */ + virtual ~domFx_cleardepth_common_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domFx_cleardepth_common_complexType &operator=( const domFx_cleardepth_common_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domFx_cleardepth_common_complexType. + */ +class domFx_cleardepth_common : public daeElement, public domFx_cleardepth_common_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FX_CLEARDEPTH_COMMON; } + static daeInt ID() { return 89; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the index attribute. + * @return Returns a xsNonNegativeInteger of the index attribute. + */ + xsNonNegativeInteger getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( xsNonNegativeInteger atIndex ) { attrIndex = atIndex; _validAttributeArray[0] = true; } + +protected: + /** + * Constructor + */ + domFx_cleardepth_common(DAE& dae) : daeElement(dae), domFx_cleardepth_common_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domFx_cleardepth_common() {} + /** + * Overloaded assignment operator + */ + virtual domFx_cleardepth_common &operator=( const domFx_cleardepth_common &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domFx_clearstencil_common.h b/Engine/lib/collada/include/1.4/dom/domFx_clearstencil_common.h new file mode 100644 index 000000000..492642e07 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domFx_clearstencil_common.h @@ -0,0 +1,123 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domFx_clearstencil_common_h__ +#define __domFx_clearstencil_common_h__ + +#include +#include +#include + +class DAE; + +class domFx_clearstencil_common_complexType +{ +protected: // Attribute + xsNonNegativeInteger attrIndex; + +protected: // Value + /** + * The xsByte value of the text data of this element. + */ + xsByte _value; + +public: //Accessors and Mutators + /** + * Gets the index attribute. + * @return Returns a xsNonNegativeInteger of the index attribute. + */ + xsNonNegativeInteger getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( xsNonNegativeInteger atIndex ) { attrIndex = atIndex; } + + /** + * Gets the value of this element. + * @return a xsByte of the value. + */ + xsByte getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsByte val ) { _value = val; } + +protected: + /** + * Constructor + */ + domFx_clearstencil_common_complexType(DAE& dae, daeElement* elt) : attrIndex(), _value() {} + /** + * Destructor + */ + virtual ~domFx_clearstencil_common_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domFx_clearstencil_common_complexType &operator=( const domFx_clearstencil_common_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domFx_clearstencil_common_complexType. + */ +class domFx_clearstencil_common : public daeElement, public domFx_clearstencil_common_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FX_CLEARSTENCIL_COMMON; } + static daeInt ID() { return 90; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the index attribute. + * @return Returns a xsNonNegativeInteger of the index attribute. + */ + xsNonNegativeInteger getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( xsNonNegativeInteger atIndex ) { attrIndex = atIndex; _validAttributeArray[0] = true; } + +protected: + /** + * Constructor + */ + domFx_clearstencil_common(DAE& dae) : daeElement(dae), domFx_clearstencil_common_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domFx_clearstencil_common() {} + /** + * Overloaded assignment operator + */ + virtual domFx_clearstencil_common &operator=( const domFx_clearstencil_common &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domFx_code_profile.h b/Engine/lib/collada/include/1.4/dom/domFx_code_profile.h new file mode 100644 index 000000000..96442be6b --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domFx_code_profile.h @@ -0,0 +1,132 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domFx_code_profile_h__ +#define __domFx_code_profile_h__ + +#include +#include +#include + +class DAE; + +/** + * The fx_code_profile type allows you to specify an inline block of source + * code. + */ +class domFx_code_profile_complexType +{ +protected: // Attribute +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; + +protected: // Value + /** + * The xsString value of the text data of this element. + */ + xsString _value; + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid;} + + /** + * Gets the value of this element. + * @return Returns a xsString of the value. + */ + xsString getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsString val ) { *(daeStringRef*)&_value = val; } + +protected: + /** + * Constructor + */ + domFx_code_profile_complexType(DAE& dae, daeElement* elt) : attrSid(), _value() {} + /** + * Destructor + */ + virtual ~domFx_code_profile_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domFx_code_profile_complexType &operator=( const domFx_code_profile_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domFx_code_profile_complexType. + */ +class domFx_code_profile : public daeElement, public domFx_code_profile_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FX_CODE_PROFILE; } + static daeInt ID() { return 96; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + +protected: + /** + * Constructor + */ + domFx_code_profile(DAE& dae) : daeElement(dae), domFx_code_profile_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domFx_code_profile() {} + /** + * Overloaded assignment operator + */ + virtual domFx_code_profile &operator=( const domFx_code_profile &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domFx_colortarget_common.h b/Engine/lib/collada/include/1.4/dom/domFx_colortarget_common.h new file mode 100644 index 000000000..49e00331f --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domFx_colortarget_common.h @@ -0,0 +1,192 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domFx_colortarget_common_h__ +#define __domFx_colortarget_common_h__ + +#include +#include +#include + +class DAE; + +class domFx_colortarget_common_complexType +{ +protected: // Attributes + xsNonNegativeInteger attrIndex; + domFx_surface_face_enum attrFace; + xsNonNegativeInteger attrMip; + xsNonNegativeInteger attrSlice; + +protected: // Value + /** + * The xsNCName value of the text data of this element. + */ + xsNCName _value; + +public: //Accessors and Mutators + /** + * Gets the index attribute. + * @return Returns a xsNonNegativeInteger of the index attribute. + */ + xsNonNegativeInteger getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( xsNonNegativeInteger atIndex ) { attrIndex = atIndex; } + + /** + * Gets the face attribute. + * @return Returns a domFx_surface_face_enum of the face attribute. + */ + domFx_surface_face_enum getFace() const { return attrFace; } + /** + * Sets the face attribute. + * @param atFace The new value for the face attribute. + */ + void setFace( domFx_surface_face_enum atFace ) { attrFace = atFace; } + + /** + * Gets the mip attribute. + * @return Returns a xsNonNegativeInteger of the mip attribute. + */ + xsNonNegativeInteger getMip() const { return attrMip; } + /** + * Sets the mip attribute. + * @param atMip The new value for the mip attribute. + */ + void setMip( xsNonNegativeInteger atMip ) { attrMip = atMip; } + + /** + * Gets the slice attribute. + * @return Returns a xsNonNegativeInteger of the slice attribute. + */ + xsNonNegativeInteger getSlice() const { return attrSlice; } + /** + * Sets the slice attribute. + * @param atSlice The new value for the slice attribute. + */ + void setSlice( xsNonNegativeInteger atSlice ) { attrSlice = atSlice; } + + /** + * Gets the value of this element. + * @return Returns a xsNCName of the value. + */ + xsNCName getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNCName val ) { *(daeStringRef*)&_value = val; } + +protected: + /** + * Constructor + */ + domFx_colortarget_common_complexType(DAE& dae, daeElement* elt) : attrIndex(), attrFace(), attrMip(), attrSlice(), _value() {} + /** + * Destructor + */ + virtual ~domFx_colortarget_common_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domFx_colortarget_common_complexType &operator=( const domFx_colortarget_common_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domFx_colortarget_common_complexType. + */ +class domFx_colortarget_common : public daeElement, public domFx_colortarget_common_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FX_COLORTARGET_COMMON; } + static daeInt ID() { return 85; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the index attribute. + * @return Returns a xsNonNegativeInteger of the index attribute. + */ + xsNonNegativeInteger getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( xsNonNegativeInteger atIndex ) { attrIndex = atIndex; _validAttributeArray[0] = true; } + + /** + * Gets the face attribute. + * @return Returns a domFx_surface_face_enum of the face attribute. + */ + domFx_surface_face_enum getFace() const { return attrFace; } + /** + * Sets the face attribute. + * @param atFace The new value for the face attribute. + */ + void setFace( domFx_surface_face_enum atFace ) { attrFace = atFace; _validAttributeArray[1] = true; } + + /** + * Gets the mip attribute. + * @return Returns a xsNonNegativeInteger of the mip attribute. + */ + xsNonNegativeInteger getMip() const { return attrMip; } + /** + * Sets the mip attribute. + * @param atMip The new value for the mip attribute. + */ + void setMip( xsNonNegativeInteger atMip ) { attrMip = atMip; _validAttributeArray[2] = true; } + + /** + * Gets the slice attribute. + * @return Returns a xsNonNegativeInteger of the slice attribute. + */ + xsNonNegativeInteger getSlice() const { return attrSlice; } + /** + * Sets the slice attribute. + * @param atSlice The new value for the slice attribute. + */ + void setSlice( xsNonNegativeInteger atSlice ) { attrSlice = atSlice; _validAttributeArray[3] = true; } + +protected: + /** + * Constructor + */ + domFx_colortarget_common(DAE& dae) : daeElement(dae), domFx_colortarget_common_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domFx_colortarget_common() {} + /** + * Overloaded assignment operator + */ + virtual domFx_colortarget_common &operator=( const domFx_colortarget_common &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domFx_depthtarget_common.h b/Engine/lib/collada/include/1.4/dom/domFx_depthtarget_common.h new file mode 100644 index 000000000..f8478bc3c --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domFx_depthtarget_common.h @@ -0,0 +1,192 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domFx_depthtarget_common_h__ +#define __domFx_depthtarget_common_h__ + +#include +#include +#include + +class DAE; + +class domFx_depthtarget_common_complexType +{ +protected: // Attributes + xsNonNegativeInteger attrIndex; + domFx_surface_face_enum attrFace; + xsNonNegativeInteger attrMip; + xsNonNegativeInteger attrSlice; + +protected: // Value + /** + * The xsNCName value of the text data of this element. + */ + xsNCName _value; + +public: //Accessors and Mutators + /** + * Gets the index attribute. + * @return Returns a xsNonNegativeInteger of the index attribute. + */ + xsNonNegativeInteger getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( xsNonNegativeInteger atIndex ) { attrIndex = atIndex; } + + /** + * Gets the face attribute. + * @return Returns a domFx_surface_face_enum of the face attribute. + */ + domFx_surface_face_enum getFace() const { return attrFace; } + /** + * Sets the face attribute. + * @param atFace The new value for the face attribute. + */ + void setFace( domFx_surface_face_enum atFace ) { attrFace = atFace; } + + /** + * Gets the mip attribute. + * @return Returns a xsNonNegativeInteger of the mip attribute. + */ + xsNonNegativeInteger getMip() const { return attrMip; } + /** + * Sets the mip attribute. + * @param atMip The new value for the mip attribute. + */ + void setMip( xsNonNegativeInteger atMip ) { attrMip = atMip; } + + /** + * Gets the slice attribute. + * @return Returns a xsNonNegativeInteger of the slice attribute. + */ + xsNonNegativeInteger getSlice() const { return attrSlice; } + /** + * Sets the slice attribute. + * @param atSlice The new value for the slice attribute. + */ + void setSlice( xsNonNegativeInteger atSlice ) { attrSlice = atSlice; } + + /** + * Gets the value of this element. + * @return Returns a xsNCName of the value. + */ + xsNCName getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNCName val ) { *(daeStringRef*)&_value = val; } + +protected: + /** + * Constructor + */ + domFx_depthtarget_common_complexType(DAE& dae, daeElement* elt) : attrIndex(), attrFace(), attrMip(), attrSlice(), _value() {} + /** + * Destructor + */ + virtual ~domFx_depthtarget_common_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domFx_depthtarget_common_complexType &operator=( const domFx_depthtarget_common_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domFx_depthtarget_common_complexType. + */ +class domFx_depthtarget_common : public daeElement, public domFx_depthtarget_common_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FX_DEPTHTARGET_COMMON; } + static daeInt ID() { return 86; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the index attribute. + * @return Returns a xsNonNegativeInteger of the index attribute. + */ + xsNonNegativeInteger getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( xsNonNegativeInteger atIndex ) { attrIndex = atIndex; _validAttributeArray[0] = true; } + + /** + * Gets the face attribute. + * @return Returns a domFx_surface_face_enum of the face attribute. + */ + domFx_surface_face_enum getFace() const { return attrFace; } + /** + * Sets the face attribute. + * @param atFace The new value for the face attribute. + */ + void setFace( domFx_surface_face_enum atFace ) { attrFace = atFace; _validAttributeArray[1] = true; } + + /** + * Gets the mip attribute. + * @return Returns a xsNonNegativeInteger of the mip attribute. + */ + xsNonNegativeInteger getMip() const { return attrMip; } + /** + * Sets the mip attribute. + * @param atMip The new value for the mip attribute. + */ + void setMip( xsNonNegativeInteger atMip ) { attrMip = atMip; _validAttributeArray[2] = true; } + + /** + * Gets the slice attribute. + * @return Returns a xsNonNegativeInteger of the slice attribute. + */ + xsNonNegativeInteger getSlice() const { return attrSlice; } + /** + * Sets the slice attribute. + * @param atSlice The new value for the slice attribute. + */ + void setSlice( xsNonNegativeInteger atSlice ) { attrSlice = atSlice; _validAttributeArray[3] = true; } + +protected: + /** + * Constructor + */ + domFx_depthtarget_common(DAE& dae) : daeElement(dae), domFx_depthtarget_common_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domFx_depthtarget_common() {} + /** + * Overloaded assignment operator + */ + virtual domFx_depthtarget_common &operator=( const domFx_depthtarget_common &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domFx_include_common.h b/Engine/lib/collada/include/1.4/dom/domFx_include_common.h new file mode 100644 index 000000000..8b52d9c3c --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domFx_include_common.h @@ -0,0 +1,165 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domFx_include_common_h__ +#define __domFx_include_common_h__ + +#include +#include +#include + +class DAE; + +/** + * The include element is used to import source code or precompiled binary + * shaders into the FX Runtime by referencing an external resource. + */ +class domFx_include_common_complexType +{ +protected: // Attributes +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; +/** + * The url attribute refers to resource. This may refer to a local resource + * using a relative URL fragment identifier that begins with the “#” + * character. The url attribute may refer to an external resource using an + * absolute or relative URL. + */ + xsAnyURI attrUrl; + + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid;} + + /** + * Gets the url attribute. + * @return Returns a xsAnyURI reference of the url attribute. + */ + xsAnyURI &getUrl() { return attrUrl; } + /** + * Gets the url attribute. + * @return Returns a constant xsAnyURI reference of the url attribute. + */ + const xsAnyURI &getUrl() const { return attrUrl; } + /** + * Sets the url attribute. + * @param atUrl The new value for the url attribute. + */ + void setUrl( const xsAnyURI &atUrl ) { attrUrl = atUrl; } + /** + * Sets the url attribute. + * @param atUrl The new value for the url attribute. + */ + void setUrl( xsString atUrl ) { attrUrl = atUrl; } + +protected: + /** + * Constructor + */ + domFx_include_common_complexType(DAE& dae, daeElement* elt) : attrSid(), attrUrl(dae, *elt) {} + /** + * Destructor + */ + virtual ~domFx_include_common_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domFx_include_common_complexType &operator=( const domFx_include_common_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domFx_include_common_complexType. + */ +class domFx_include_common : public daeElement, public domFx_include_common_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FX_INCLUDE_COMMON; } + static daeInt ID() { return 92; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + + /** + * Gets the url attribute. + * @return Returns a xsAnyURI reference of the url attribute. + */ + xsAnyURI &getUrl() { return attrUrl; } + /** + * Gets the url attribute. + * @return Returns a constant xsAnyURI reference of the url attribute. + */ + const xsAnyURI &getUrl() const { return attrUrl; } + /** + * Sets the url attribute. + * @param atUrl The new value for the url attribute. + */ + void setUrl( const xsAnyURI &atUrl ) { attrUrl = atUrl; _validAttributeArray[1] = true; } + /** + * Sets the url attribute. + * @param atUrl The new value for the url attribute. + */ + void setUrl( xsString atUrl ) { attrUrl = atUrl; _validAttributeArray[1] = true; } + +protected: + /** + * Constructor + */ + domFx_include_common(DAE& dae) : daeElement(dae), domFx_include_common_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domFx_include_common() {} + /** + * Overloaded assignment operator + */ + virtual domFx_include_common &operator=( const domFx_include_common &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domFx_newparam_common.h b/Engine/lib/collada/include/1.4/dom/domFx_newparam_common.h new file mode 100644 index 000000000..90d0937f0 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domFx_newparam_common.h @@ -0,0 +1,284 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domFx_newparam_common_h__ +#define __domFx_newparam_common_h__ + +#include +#include +#include + +#include +#include +class DAE; + +/** + * This element creates a new, named param object in the FX Runtime, assigns + * it a type, an initial value, and additional attributes at declaration time. + */ +class domFx_newparam_common_complexType +{ +public: + class domSemantic; + + typedef daeSmartRef domSemanticRef; + typedef daeTArray domSemantic_Array; + +/** + * The semantic element allows you to specify a semantic for this new param. + */ + class domSemantic : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SEMANTIC; } + static daeInt ID() { return 93; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsNCName value of the text data of this element. + */ + xsNCName _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsNCName of the value. + */ + xsNCName getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNCName val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domSemantic(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domSemantic() {} + /** + * Overloaded assignment operator + */ + virtual domSemantic &operator=( const domSemantic &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domModifier; + + typedef daeSmartRef domModifierRef; + typedef daeTArray domModifier_Array; + +/** + * The modifier element allows you to specify a modifier for this new param. + */ + class domModifier : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MODIFIER; } + static daeInt ID() { return 94; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_modifier_enum_common value of the text data of this element. + */ + domFx_modifier_enum_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_modifier_enum_common of the value. + */ + domFx_modifier_enum_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_modifier_enum_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domModifier(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domModifier() {} + /** + * Overloaded assignment operator + */ + virtual domModifier &operator=( const domModifier &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attribute +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; + +protected: // Elements +/** + * The annotate element allows you to specify an annotation for this new param. + * @see domAnnotate + */ + domFx_annotate_common_Array elemAnnotate_array; +/** + * The semantic element allows you to specify a semantic for this new param. + * @see domSemantic + */ + domSemanticRef elemSemantic; +/** + * The modifier element allows you to specify a modifier for this new param. + * @see domModifier + */ + domModifierRef elemModifier; + domFx_basic_type_commonRef elemFx_basic_type_common; + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid;} + + /** + * Gets the annotate element array. + * @return Returns a reference to the array of annotate elements. + */ + domFx_annotate_common_Array &getAnnotate_array() { return elemAnnotate_array; } + /** + * Gets the annotate element array. + * @return Returns a constant reference to the array of annotate elements. + */ + const domFx_annotate_common_Array &getAnnotate_array() const { return elemAnnotate_array; } + /** + * Gets the semantic element. + * @return a daeSmartRef to the semantic element. + */ + const domSemanticRef getSemantic() const { return elemSemantic; } + /** + * Gets the modifier element. + * @return a daeSmartRef to the modifier element. + */ + const domModifierRef getModifier() const { return elemModifier; } + /** + * Gets the fx_basic_type_common element. + * @return a daeSmartRef to the fx_basic_type_common element. + */ + const domFx_basic_type_commonRef getFx_basic_type_common() const { return elemFx_basic_type_common; } +protected: + /** + * Constructor + */ + domFx_newparam_common_complexType(DAE& dae, daeElement* elt) : attrSid(), elemAnnotate_array(), elemSemantic(), elemModifier(), elemFx_basic_type_common() {} + /** + * Destructor + */ + virtual ~domFx_newparam_common_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domFx_newparam_common_complexType &operator=( const domFx_newparam_common_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domFx_newparam_common_complexType. + */ +class domFx_newparam_common : public daeElement, public domFx_newparam_common_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FX_NEWPARAM_COMMON; } + static daeInt ID() { return 95; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + +protected: + /** + * Constructor + */ + domFx_newparam_common(DAE& dae) : daeElement(dae), domFx_newparam_common_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domFx_newparam_common() {} + /** + * Overloaded assignment operator + */ + virtual domFx_newparam_common &operator=( const domFx_newparam_common &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domFx_profile_abstract.h b/Engine/lib/collada/include/1.4/dom/domFx_profile_abstract.h new file mode 100644 index 000000000..53ad37a7b --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domFx_profile_abstract.h @@ -0,0 +1,63 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domFx_profile_abstract_h__ +#define __domFx_profile_abstract_h__ + +#include +#include +#include + +class DAE; + +/** + * The fx_profile_abstract element is only used as a substitution group hook + * for COLLADA FX profiles. + */ +class domFx_profile_abstract : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FX_PROFILE_ABSTRACT; } + static daeInt ID() { return 727; } + virtual daeInt typeID() const { return ID(); } + +protected: + /** + * Constructor + */ + domFx_profile_abstract(DAE& dae) : daeElement(dae) {} + /** + * Destructor + */ + virtual ~domFx_profile_abstract() {} + /** + * Overloaded assignment operator + */ + virtual domFx_profile_abstract &operator=( const domFx_profile_abstract &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domFx_sampler1D_common.h b/Engine/lib/collada/include/1.4/dom/domFx_sampler1D_common.h new file mode 100644 index 000000000..e662e204c --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domFx_sampler1D_common.h @@ -0,0 +1,616 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domFx_sampler1D_common_h__ +#define __domFx_sampler1D_common_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * A one-dimensional texture sampler. + */ +class domFx_sampler1D_common_complexType +{ +public: + class domSource; + + typedef daeSmartRef domSourceRef; + typedef daeTArray domSource_Array; + + class domSource : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SOURCE; } + static daeInt ID() { return 28; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsNCName value of the text data of this element. + */ + xsNCName _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsNCName of the value. + */ + xsNCName getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNCName val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domSource(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domSource() {} + /** + * Overloaded assignment operator + */ + virtual domSource &operator=( const domSource &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domWrap_s; + + typedef daeSmartRef domWrap_sRef; + typedef daeTArray domWrap_s_Array; + + class domWrap_s : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::WRAP_S; } + static daeInt ID() { return 29; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_wrap_common value of the text data of this element. + */ + domFx_sampler_wrap_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_wrap_common of the value. + */ + domFx_sampler_wrap_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_wrap_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domWrap_s(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domWrap_s() {} + /** + * Overloaded assignment operator + */ + virtual domWrap_s &operator=( const domWrap_s &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMinfilter; + + typedef daeSmartRef domMinfilterRef; + typedef daeTArray domMinfilter_Array; + + class domMinfilter : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MINFILTER; } + static daeInt ID() { return 30; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_filter_common value of the text data of this element. + */ + domFx_sampler_filter_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_filter_common of the value. + */ + domFx_sampler_filter_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_filter_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMinfilter(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMinfilter() {} + /** + * Overloaded assignment operator + */ + virtual domMinfilter &operator=( const domMinfilter &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMagfilter; + + typedef daeSmartRef domMagfilterRef; + typedef daeTArray domMagfilter_Array; + + class domMagfilter : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MAGFILTER; } + static daeInt ID() { return 31; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_filter_common value of the text data of this element. + */ + domFx_sampler_filter_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_filter_common of the value. + */ + domFx_sampler_filter_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_filter_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMagfilter(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMagfilter() {} + /** + * Overloaded assignment operator + */ + virtual domMagfilter &operator=( const domMagfilter &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMipfilter; + + typedef daeSmartRef domMipfilterRef; + typedef daeTArray domMipfilter_Array; + + class domMipfilter : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MIPFILTER; } + static daeInt ID() { return 32; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_filter_common value of the text data of this element. + */ + domFx_sampler_filter_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_filter_common of the value. + */ + domFx_sampler_filter_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_filter_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMipfilter(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMipfilter() {} + /** + * Overloaded assignment operator + */ + virtual domMipfilter &operator=( const domMipfilter &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBorder_color; + + typedef daeSmartRef domBorder_colorRef; + typedef daeTArray domBorder_color_Array; + + class domBorder_color : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BORDER_COLOR; } + static daeInt ID() { return 33; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_color_common value of the text data of this element. + */ + domFx_color_common _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domFx_color_common reference of the _value array. + */ + domFx_color_common &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domFx_color_common reference of the _value array. + */ + const domFx_color_common &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domFx_color_common &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBorder_color(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBorder_color() {} + /** + * Overloaded assignment operator + */ + virtual domBorder_color &operator=( const domBorder_color &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMipmap_maxlevel; + + typedef daeSmartRef domMipmap_maxlevelRef; + typedef daeTArray domMipmap_maxlevel_Array; + + class domMipmap_maxlevel : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MIPMAP_MAXLEVEL; } + static daeInt ID() { return 34; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsUnsignedByte value of the text data of this element. + */ + xsUnsignedByte _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a xsUnsignedByte of the value. + */ + xsUnsignedByte getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsUnsignedByte val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMipmap_maxlevel(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMipmap_maxlevel() {} + /** + * Overloaded assignment operator + */ + virtual domMipmap_maxlevel &operator=( const domMipmap_maxlevel &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMipmap_bias; + + typedef daeSmartRef domMipmap_biasRef; + typedef daeTArray domMipmap_bias_Array; + + class domMipmap_bias : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MIPMAP_BIAS; } + static daeInt ID() { return 35; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsFloat value of the text data of this element. + */ + xsFloat _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a xsFloat of the value. + */ + xsFloat getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsFloat val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMipmap_bias(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMipmap_bias() {} + /** + * Overloaded assignment operator + */ + virtual domMipmap_bias &operator=( const domMipmap_bias &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements + domSourceRef elemSource; + domWrap_sRef elemWrap_s; + domMinfilterRef elemMinfilter; + domMagfilterRef elemMagfilter; + domMipfilterRef elemMipfilter; + domBorder_colorRef elemBorder_color; + domMipmap_maxlevelRef elemMipmap_maxlevel; + domMipmap_biasRef elemMipmap_bias; + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the source element. + * @return a daeSmartRef to the source element. + */ + const domSourceRef getSource() const { return elemSource; } + /** + * Gets the wrap_s element. + * @return a daeSmartRef to the wrap_s element. + */ + const domWrap_sRef getWrap_s() const { return elemWrap_s; } + /** + * Gets the minfilter element. + * @return a daeSmartRef to the minfilter element. + */ + const domMinfilterRef getMinfilter() const { return elemMinfilter; } + /** + * Gets the magfilter element. + * @return a daeSmartRef to the magfilter element. + */ + const domMagfilterRef getMagfilter() const { return elemMagfilter; } + /** + * Gets the mipfilter element. + * @return a daeSmartRef to the mipfilter element. + */ + const domMipfilterRef getMipfilter() const { return elemMipfilter; } + /** + * Gets the border_color element. + * @return a daeSmartRef to the border_color element. + */ + const domBorder_colorRef getBorder_color() const { return elemBorder_color; } + /** + * Gets the mipmap_maxlevel element. + * @return a daeSmartRef to the mipmap_maxlevel element. + */ + const domMipmap_maxlevelRef getMipmap_maxlevel() const { return elemMipmap_maxlevel; } + /** + * Gets the mipmap_bias element. + * @return a daeSmartRef to the mipmap_bias element. + */ + const domMipmap_biasRef getMipmap_bias() const { return elemMipmap_bias; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domFx_sampler1D_common_complexType(DAE& dae, daeElement* elt) : elemSource(), elemWrap_s(), elemMinfilter(), elemMagfilter(), elemMipfilter(), elemBorder_color(), elemMipmap_maxlevel(), elemMipmap_bias(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domFx_sampler1D_common_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domFx_sampler1D_common_complexType &operator=( const domFx_sampler1D_common_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domFx_sampler1D_common_complexType. + */ +class domFx_sampler1D_common : public daeElement, public domFx_sampler1D_common_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FX_SAMPLER1D_COMMON; } + static daeInt ID() { return 36; } + virtual daeInt typeID() const { return ID(); } +protected: + /** + * Constructor + */ + domFx_sampler1D_common(DAE& dae) : daeElement(dae), domFx_sampler1D_common_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domFx_sampler1D_common() {} + /** + * Overloaded assignment operator + */ + virtual domFx_sampler1D_common &operator=( const domFx_sampler1D_common &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domFx_sampler2D_common.h b/Engine/lib/collada/include/1.4/dom/domFx_sampler2D_common.h new file mode 100644 index 000000000..5e3c35991 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domFx_sampler2D_common.h @@ -0,0 +1,680 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domFx_sampler2D_common_h__ +#define __domFx_sampler2D_common_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * A two-dimensional texture sampler. + */ +class domFx_sampler2D_common_complexType +{ +public: + class domSource; + + typedef daeSmartRef domSourceRef; + typedef daeTArray domSource_Array; + + class domSource : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SOURCE; } + static daeInt ID() { return 37; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsNCName value of the text data of this element. + */ + xsNCName _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsNCName of the value. + */ + xsNCName getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNCName val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domSource(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domSource() {} + /** + * Overloaded assignment operator + */ + virtual domSource &operator=( const domSource &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domWrap_s; + + typedef daeSmartRef domWrap_sRef; + typedef daeTArray domWrap_s_Array; + + class domWrap_s : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::WRAP_S; } + static daeInt ID() { return 38; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_wrap_common value of the text data of this element. + */ + domFx_sampler_wrap_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_wrap_common of the value. + */ + domFx_sampler_wrap_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_wrap_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domWrap_s(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domWrap_s() {} + /** + * Overloaded assignment operator + */ + virtual domWrap_s &operator=( const domWrap_s &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domWrap_t; + + typedef daeSmartRef domWrap_tRef; + typedef daeTArray domWrap_t_Array; + + class domWrap_t : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::WRAP_T; } + static daeInt ID() { return 39; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_wrap_common value of the text data of this element. + */ + domFx_sampler_wrap_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_wrap_common of the value. + */ + domFx_sampler_wrap_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_wrap_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domWrap_t(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domWrap_t() {} + /** + * Overloaded assignment operator + */ + virtual domWrap_t &operator=( const domWrap_t &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMinfilter; + + typedef daeSmartRef domMinfilterRef; + typedef daeTArray domMinfilter_Array; + + class domMinfilter : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MINFILTER; } + static daeInt ID() { return 40; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_filter_common value of the text data of this element. + */ + domFx_sampler_filter_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_filter_common of the value. + */ + domFx_sampler_filter_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_filter_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMinfilter(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMinfilter() {} + /** + * Overloaded assignment operator + */ + virtual domMinfilter &operator=( const domMinfilter &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMagfilter; + + typedef daeSmartRef domMagfilterRef; + typedef daeTArray domMagfilter_Array; + + class domMagfilter : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MAGFILTER; } + static daeInt ID() { return 41; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_filter_common value of the text data of this element. + */ + domFx_sampler_filter_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_filter_common of the value. + */ + domFx_sampler_filter_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_filter_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMagfilter(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMagfilter() {} + /** + * Overloaded assignment operator + */ + virtual domMagfilter &operator=( const domMagfilter &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMipfilter; + + typedef daeSmartRef domMipfilterRef; + typedef daeTArray domMipfilter_Array; + + class domMipfilter : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MIPFILTER; } + static daeInt ID() { return 42; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_filter_common value of the text data of this element. + */ + domFx_sampler_filter_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_filter_common of the value. + */ + domFx_sampler_filter_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_filter_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMipfilter(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMipfilter() {} + /** + * Overloaded assignment operator + */ + virtual domMipfilter &operator=( const domMipfilter &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBorder_color; + + typedef daeSmartRef domBorder_colorRef; + typedef daeTArray domBorder_color_Array; + + class domBorder_color : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BORDER_COLOR; } + static daeInt ID() { return 43; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_color_common value of the text data of this element. + */ + domFx_color_common _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domFx_color_common reference of the _value array. + */ + domFx_color_common &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domFx_color_common reference of the _value array. + */ + const domFx_color_common &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domFx_color_common &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBorder_color(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBorder_color() {} + /** + * Overloaded assignment operator + */ + virtual domBorder_color &operator=( const domBorder_color &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMipmap_maxlevel; + + typedef daeSmartRef domMipmap_maxlevelRef; + typedef daeTArray domMipmap_maxlevel_Array; + + class domMipmap_maxlevel : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MIPMAP_MAXLEVEL; } + static daeInt ID() { return 44; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsUnsignedByte value of the text data of this element. + */ + xsUnsignedByte _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a xsUnsignedByte of the value. + */ + xsUnsignedByte getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsUnsignedByte val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMipmap_maxlevel(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMipmap_maxlevel() {} + /** + * Overloaded assignment operator + */ + virtual domMipmap_maxlevel &operator=( const domMipmap_maxlevel &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMipmap_bias; + + typedef daeSmartRef domMipmap_biasRef; + typedef daeTArray domMipmap_bias_Array; + + class domMipmap_bias : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MIPMAP_BIAS; } + static daeInt ID() { return 45; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsFloat value of the text data of this element. + */ + xsFloat _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a xsFloat of the value. + */ + xsFloat getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsFloat val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMipmap_bias(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMipmap_bias() {} + /** + * Overloaded assignment operator + */ + virtual domMipmap_bias &operator=( const domMipmap_bias &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements + domSourceRef elemSource; + domWrap_sRef elemWrap_s; + domWrap_tRef elemWrap_t; + domMinfilterRef elemMinfilter; + domMagfilterRef elemMagfilter; + domMipfilterRef elemMipfilter; + domBorder_colorRef elemBorder_color; + domMipmap_maxlevelRef elemMipmap_maxlevel; + domMipmap_biasRef elemMipmap_bias; + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the source element. + * @return a daeSmartRef to the source element. + */ + const domSourceRef getSource() const { return elemSource; } + /** + * Gets the wrap_s element. + * @return a daeSmartRef to the wrap_s element. + */ + const domWrap_sRef getWrap_s() const { return elemWrap_s; } + /** + * Gets the wrap_t element. + * @return a daeSmartRef to the wrap_t element. + */ + const domWrap_tRef getWrap_t() const { return elemWrap_t; } + /** + * Gets the minfilter element. + * @return a daeSmartRef to the minfilter element. + */ + const domMinfilterRef getMinfilter() const { return elemMinfilter; } + /** + * Gets the magfilter element. + * @return a daeSmartRef to the magfilter element. + */ + const domMagfilterRef getMagfilter() const { return elemMagfilter; } + /** + * Gets the mipfilter element. + * @return a daeSmartRef to the mipfilter element. + */ + const domMipfilterRef getMipfilter() const { return elemMipfilter; } + /** + * Gets the border_color element. + * @return a daeSmartRef to the border_color element. + */ + const domBorder_colorRef getBorder_color() const { return elemBorder_color; } + /** + * Gets the mipmap_maxlevel element. + * @return a daeSmartRef to the mipmap_maxlevel element. + */ + const domMipmap_maxlevelRef getMipmap_maxlevel() const { return elemMipmap_maxlevel; } + /** + * Gets the mipmap_bias element. + * @return a daeSmartRef to the mipmap_bias element. + */ + const domMipmap_biasRef getMipmap_bias() const { return elemMipmap_bias; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domFx_sampler2D_common_complexType(DAE& dae, daeElement* elt) : elemSource(), elemWrap_s(), elemWrap_t(), elemMinfilter(), elemMagfilter(), elemMipfilter(), elemBorder_color(), elemMipmap_maxlevel(), elemMipmap_bias(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domFx_sampler2D_common_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domFx_sampler2D_common_complexType &operator=( const domFx_sampler2D_common_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domFx_sampler2D_common_complexType. + */ +class domFx_sampler2D_common : public daeElement, public domFx_sampler2D_common_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FX_SAMPLER2D_COMMON; } + static daeInt ID() { return 46; } + virtual daeInt typeID() const { return ID(); } +protected: + /** + * Constructor + */ + domFx_sampler2D_common(DAE& dae) : daeElement(dae), domFx_sampler2D_common_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domFx_sampler2D_common() {} + /** + * Overloaded assignment operator + */ + virtual domFx_sampler2D_common &operator=( const domFx_sampler2D_common &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domFx_sampler3D_common.h b/Engine/lib/collada/include/1.4/dom/domFx_sampler3D_common.h new file mode 100644 index 000000000..ec93ac247 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domFx_sampler3D_common.h @@ -0,0 +1,744 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domFx_sampler3D_common_h__ +#define __domFx_sampler3D_common_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * A three-dimensional texture sampler. + */ +class domFx_sampler3D_common_complexType +{ +public: + class domSource; + + typedef daeSmartRef domSourceRef; + typedef daeTArray domSource_Array; + + class domSource : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SOURCE; } + static daeInt ID() { return 47; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsNCName value of the text data of this element. + */ + xsNCName _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsNCName of the value. + */ + xsNCName getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNCName val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domSource(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domSource() {} + /** + * Overloaded assignment operator + */ + virtual domSource &operator=( const domSource &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domWrap_s; + + typedef daeSmartRef domWrap_sRef; + typedef daeTArray domWrap_s_Array; + + class domWrap_s : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::WRAP_S; } + static daeInt ID() { return 48; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_wrap_common value of the text data of this element. + */ + domFx_sampler_wrap_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_wrap_common of the value. + */ + domFx_sampler_wrap_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_wrap_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domWrap_s(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domWrap_s() {} + /** + * Overloaded assignment operator + */ + virtual domWrap_s &operator=( const domWrap_s &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domWrap_t; + + typedef daeSmartRef domWrap_tRef; + typedef daeTArray domWrap_t_Array; + + class domWrap_t : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::WRAP_T; } + static daeInt ID() { return 49; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_wrap_common value of the text data of this element. + */ + domFx_sampler_wrap_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_wrap_common of the value. + */ + domFx_sampler_wrap_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_wrap_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domWrap_t(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domWrap_t() {} + /** + * Overloaded assignment operator + */ + virtual domWrap_t &operator=( const domWrap_t &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domWrap_p; + + typedef daeSmartRef domWrap_pRef; + typedef daeTArray domWrap_p_Array; + + class domWrap_p : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::WRAP_P; } + static daeInt ID() { return 50; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_wrap_common value of the text data of this element. + */ + domFx_sampler_wrap_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_wrap_common of the value. + */ + domFx_sampler_wrap_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_wrap_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domWrap_p(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domWrap_p() {} + /** + * Overloaded assignment operator + */ + virtual domWrap_p &operator=( const domWrap_p &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMinfilter; + + typedef daeSmartRef domMinfilterRef; + typedef daeTArray domMinfilter_Array; + + class domMinfilter : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MINFILTER; } + static daeInt ID() { return 51; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_filter_common value of the text data of this element. + */ + domFx_sampler_filter_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_filter_common of the value. + */ + domFx_sampler_filter_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_filter_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMinfilter(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMinfilter() {} + /** + * Overloaded assignment operator + */ + virtual domMinfilter &operator=( const domMinfilter &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMagfilter; + + typedef daeSmartRef domMagfilterRef; + typedef daeTArray domMagfilter_Array; + + class domMagfilter : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MAGFILTER; } + static daeInt ID() { return 52; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_filter_common value of the text data of this element. + */ + domFx_sampler_filter_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_filter_common of the value. + */ + domFx_sampler_filter_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_filter_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMagfilter(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMagfilter() {} + /** + * Overloaded assignment operator + */ + virtual domMagfilter &operator=( const domMagfilter &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMipfilter; + + typedef daeSmartRef domMipfilterRef; + typedef daeTArray domMipfilter_Array; + + class domMipfilter : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MIPFILTER; } + static daeInt ID() { return 53; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_filter_common value of the text data of this element. + */ + domFx_sampler_filter_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_filter_common of the value. + */ + domFx_sampler_filter_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_filter_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMipfilter(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMipfilter() {} + /** + * Overloaded assignment operator + */ + virtual domMipfilter &operator=( const domMipfilter &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBorder_color; + + typedef daeSmartRef domBorder_colorRef; + typedef daeTArray domBorder_color_Array; + + class domBorder_color : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BORDER_COLOR; } + static daeInt ID() { return 54; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_color_common value of the text data of this element. + */ + domFx_color_common _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domFx_color_common reference of the _value array. + */ + domFx_color_common &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domFx_color_common reference of the _value array. + */ + const domFx_color_common &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domFx_color_common &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBorder_color(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBorder_color() {} + /** + * Overloaded assignment operator + */ + virtual domBorder_color &operator=( const domBorder_color &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMipmap_maxlevel; + + typedef daeSmartRef domMipmap_maxlevelRef; + typedef daeTArray domMipmap_maxlevel_Array; + + class domMipmap_maxlevel : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MIPMAP_MAXLEVEL; } + static daeInt ID() { return 55; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsUnsignedByte value of the text data of this element. + */ + xsUnsignedByte _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a xsUnsignedByte of the value. + */ + xsUnsignedByte getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsUnsignedByte val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMipmap_maxlevel(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMipmap_maxlevel() {} + /** + * Overloaded assignment operator + */ + virtual domMipmap_maxlevel &operator=( const domMipmap_maxlevel &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMipmap_bias; + + typedef daeSmartRef domMipmap_biasRef; + typedef daeTArray domMipmap_bias_Array; + + class domMipmap_bias : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MIPMAP_BIAS; } + static daeInt ID() { return 56; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsFloat value of the text data of this element. + */ + xsFloat _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a xsFloat of the value. + */ + xsFloat getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsFloat val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMipmap_bias(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMipmap_bias() {} + /** + * Overloaded assignment operator + */ + virtual domMipmap_bias &operator=( const domMipmap_bias &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements + domSourceRef elemSource; + domWrap_sRef elemWrap_s; + domWrap_tRef elemWrap_t; + domWrap_pRef elemWrap_p; + domMinfilterRef elemMinfilter; + domMagfilterRef elemMagfilter; + domMipfilterRef elemMipfilter; + domBorder_colorRef elemBorder_color; + domMipmap_maxlevelRef elemMipmap_maxlevel; + domMipmap_biasRef elemMipmap_bias; + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the source element. + * @return a daeSmartRef to the source element. + */ + const domSourceRef getSource() const { return elemSource; } + /** + * Gets the wrap_s element. + * @return a daeSmartRef to the wrap_s element. + */ + const domWrap_sRef getWrap_s() const { return elemWrap_s; } + /** + * Gets the wrap_t element. + * @return a daeSmartRef to the wrap_t element. + */ + const domWrap_tRef getWrap_t() const { return elemWrap_t; } + /** + * Gets the wrap_p element. + * @return a daeSmartRef to the wrap_p element. + */ + const domWrap_pRef getWrap_p() const { return elemWrap_p; } + /** + * Gets the minfilter element. + * @return a daeSmartRef to the minfilter element. + */ + const domMinfilterRef getMinfilter() const { return elemMinfilter; } + /** + * Gets the magfilter element. + * @return a daeSmartRef to the magfilter element. + */ + const domMagfilterRef getMagfilter() const { return elemMagfilter; } + /** + * Gets the mipfilter element. + * @return a daeSmartRef to the mipfilter element. + */ + const domMipfilterRef getMipfilter() const { return elemMipfilter; } + /** + * Gets the border_color element. + * @return a daeSmartRef to the border_color element. + */ + const domBorder_colorRef getBorder_color() const { return elemBorder_color; } + /** + * Gets the mipmap_maxlevel element. + * @return a daeSmartRef to the mipmap_maxlevel element. + */ + const domMipmap_maxlevelRef getMipmap_maxlevel() const { return elemMipmap_maxlevel; } + /** + * Gets the mipmap_bias element. + * @return a daeSmartRef to the mipmap_bias element. + */ + const domMipmap_biasRef getMipmap_bias() const { return elemMipmap_bias; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domFx_sampler3D_common_complexType(DAE& dae, daeElement* elt) : elemSource(), elemWrap_s(), elemWrap_t(), elemWrap_p(), elemMinfilter(), elemMagfilter(), elemMipfilter(), elemBorder_color(), elemMipmap_maxlevel(), elemMipmap_bias(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domFx_sampler3D_common_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domFx_sampler3D_common_complexType &operator=( const domFx_sampler3D_common_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domFx_sampler3D_common_complexType. + */ +class domFx_sampler3D_common : public daeElement, public domFx_sampler3D_common_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FX_SAMPLER3D_COMMON; } + static daeInt ID() { return 57; } + virtual daeInt typeID() const { return ID(); } +protected: + /** + * Constructor + */ + domFx_sampler3D_common(DAE& dae) : daeElement(dae), domFx_sampler3D_common_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domFx_sampler3D_common() {} + /** + * Overloaded assignment operator + */ + virtual domFx_sampler3D_common &operator=( const domFx_sampler3D_common &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domFx_samplerCUBE_common.h b/Engine/lib/collada/include/1.4/dom/domFx_samplerCUBE_common.h new file mode 100644 index 000000000..91e719ce3 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domFx_samplerCUBE_common.h @@ -0,0 +1,744 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domFx_samplerCUBE_common_h__ +#define __domFx_samplerCUBE_common_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * A texture sampler for cube maps. + */ +class domFx_samplerCUBE_common_complexType +{ +public: + class domSource; + + typedef daeSmartRef domSourceRef; + typedef daeTArray domSource_Array; + + class domSource : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SOURCE; } + static daeInt ID() { return 58; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsNCName value of the text data of this element. + */ + xsNCName _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsNCName of the value. + */ + xsNCName getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNCName val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domSource(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domSource() {} + /** + * Overloaded assignment operator + */ + virtual domSource &operator=( const domSource &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domWrap_s; + + typedef daeSmartRef domWrap_sRef; + typedef daeTArray domWrap_s_Array; + + class domWrap_s : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::WRAP_S; } + static daeInt ID() { return 59; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_wrap_common value of the text data of this element. + */ + domFx_sampler_wrap_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_wrap_common of the value. + */ + domFx_sampler_wrap_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_wrap_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domWrap_s(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domWrap_s() {} + /** + * Overloaded assignment operator + */ + virtual domWrap_s &operator=( const domWrap_s &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domWrap_t; + + typedef daeSmartRef domWrap_tRef; + typedef daeTArray domWrap_t_Array; + + class domWrap_t : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::WRAP_T; } + static daeInt ID() { return 60; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_wrap_common value of the text data of this element. + */ + domFx_sampler_wrap_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_wrap_common of the value. + */ + domFx_sampler_wrap_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_wrap_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domWrap_t(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domWrap_t() {} + /** + * Overloaded assignment operator + */ + virtual domWrap_t &operator=( const domWrap_t &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domWrap_p; + + typedef daeSmartRef domWrap_pRef; + typedef daeTArray domWrap_p_Array; + + class domWrap_p : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::WRAP_P; } + static daeInt ID() { return 61; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_wrap_common value of the text data of this element. + */ + domFx_sampler_wrap_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_wrap_common of the value. + */ + domFx_sampler_wrap_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_wrap_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domWrap_p(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domWrap_p() {} + /** + * Overloaded assignment operator + */ + virtual domWrap_p &operator=( const domWrap_p &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMinfilter; + + typedef daeSmartRef domMinfilterRef; + typedef daeTArray domMinfilter_Array; + + class domMinfilter : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MINFILTER; } + static daeInt ID() { return 62; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_filter_common value of the text data of this element. + */ + domFx_sampler_filter_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_filter_common of the value. + */ + domFx_sampler_filter_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_filter_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMinfilter(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMinfilter() {} + /** + * Overloaded assignment operator + */ + virtual domMinfilter &operator=( const domMinfilter &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMagfilter; + + typedef daeSmartRef domMagfilterRef; + typedef daeTArray domMagfilter_Array; + + class domMagfilter : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MAGFILTER; } + static daeInt ID() { return 63; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_filter_common value of the text data of this element. + */ + domFx_sampler_filter_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_filter_common of the value. + */ + domFx_sampler_filter_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_filter_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMagfilter(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMagfilter() {} + /** + * Overloaded assignment operator + */ + virtual domMagfilter &operator=( const domMagfilter &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMipfilter; + + typedef daeSmartRef domMipfilterRef; + typedef daeTArray domMipfilter_Array; + + class domMipfilter : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MIPFILTER; } + static daeInt ID() { return 64; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_filter_common value of the text data of this element. + */ + domFx_sampler_filter_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_filter_common of the value. + */ + domFx_sampler_filter_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_filter_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMipfilter(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMipfilter() {} + /** + * Overloaded assignment operator + */ + virtual domMipfilter &operator=( const domMipfilter &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBorder_color; + + typedef daeSmartRef domBorder_colorRef; + typedef daeTArray domBorder_color_Array; + + class domBorder_color : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BORDER_COLOR; } + static daeInt ID() { return 65; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_color_common value of the text data of this element. + */ + domFx_color_common _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domFx_color_common reference of the _value array. + */ + domFx_color_common &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domFx_color_common reference of the _value array. + */ + const domFx_color_common &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domFx_color_common &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBorder_color(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBorder_color() {} + /** + * Overloaded assignment operator + */ + virtual domBorder_color &operator=( const domBorder_color &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMipmap_maxlevel; + + typedef daeSmartRef domMipmap_maxlevelRef; + typedef daeTArray domMipmap_maxlevel_Array; + + class domMipmap_maxlevel : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MIPMAP_MAXLEVEL; } + static daeInt ID() { return 66; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsUnsignedByte value of the text data of this element. + */ + xsUnsignedByte _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a xsUnsignedByte of the value. + */ + xsUnsignedByte getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsUnsignedByte val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMipmap_maxlevel(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMipmap_maxlevel() {} + /** + * Overloaded assignment operator + */ + virtual domMipmap_maxlevel &operator=( const domMipmap_maxlevel &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMipmap_bias; + + typedef daeSmartRef domMipmap_biasRef; + typedef daeTArray domMipmap_bias_Array; + + class domMipmap_bias : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MIPMAP_BIAS; } + static daeInt ID() { return 67; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsFloat value of the text data of this element. + */ + xsFloat _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a xsFloat of the value. + */ + xsFloat getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsFloat val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMipmap_bias(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMipmap_bias() {} + /** + * Overloaded assignment operator + */ + virtual domMipmap_bias &operator=( const domMipmap_bias &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements + domSourceRef elemSource; + domWrap_sRef elemWrap_s; + domWrap_tRef elemWrap_t; + domWrap_pRef elemWrap_p; + domMinfilterRef elemMinfilter; + domMagfilterRef elemMagfilter; + domMipfilterRef elemMipfilter; + domBorder_colorRef elemBorder_color; + domMipmap_maxlevelRef elemMipmap_maxlevel; + domMipmap_biasRef elemMipmap_bias; + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the source element. + * @return a daeSmartRef to the source element. + */ + const domSourceRef getSource() const { return elemSource; } + /** + * Gets the wrap_s element. + * @return a daeSmartRef to the wrap_s element. + */ + const domWrap_sRef getWrap_s() const { return elemWrap_s; } + /** + * Gets the wrap_t element. + * @return a daeSmartRef to the wrap_t element. + */ + const domWrap_tRef getWrap_t() const { return elemWrap_t; } + /** + * Gets the wrap_p element. + * @return a daeSmartRef to the wrap_p element. + */ + const domWrap_pRef getWrap_p() const { return elemWrap_p; } + /** + * Gets the minfilter element. + * @return a daeSmartRef to the minfilter element. + */ + const domMinfilterRef getMinfilter() const { return elemMinfilter; } + /** + * Gets the magfilter element. + * @return a daeSmartRef to the magfilter element. + */ + const domMagfilterRef getMagfilter() const { return elemMagfilter; } + /** + * Gets the mipfilter element. + * @return a daeSmartRef to the mipfilter element. + */ + const domMipfilterRef getMipfilter() const { return elemMipfilter; } + /** + * Gets the border_color element. + * @return a daeSmartRef to the border_color element. + */ + const domBorder_colorRef getBorder_color() const { return elemBorder_color; } + /** + * Gets the mipmap_maxlevel element. + * @return a daeSmartRef to the mipmap_maxlevel element. + */ + const domMipmap_maxlevelRef getMipmap_maxlevel() const { return elemMipmap_maxlevel; } + /** + * Gets the mipmap_bias element. + * @return a daeSmartRef to the mipmap_bias element. + */ + const domMipmap_biasRef getMipmap_bias() const { return elemMipmap_bias; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domFx_samplerCUBE_common_complexType(DAE& dae, daeElement* elt) : elemSource(), elemWrap_s(), elemWrap_t(), elemWrap_p(), elemMinfilter(), elemMagfilter(), elemMipfilter(), elemBorder_color(), elemMipmap_maxlevel(), elemMipmap_bias(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domFx_samplerCUBE_common_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domFx_samplerCUBE_common_complexType &operator=( const domFx_samplerCUBE_common_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domFx_samplerCUBE_common_complexType. + */ +class domFx_samplerCUBE_common : public daeElement, public domFx_samplerCUBE_common_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FX_SAMPLERCUBE_COMMON; } + static daeInt ID() { return 68; } + virtual daeInt typeID() const { return ID(); } +protected: + /** + * Constructor + */ + domFx_samplerCUBE_common(DAE& dae) : daeElement(dae), domFx_samplerCUBE_common_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domFx_samplerCUBE_common() {} + /** + * Overloaded assignment operator + */ + virtual domFx_samplerCUBE_common &operator=( const domFx_samplerCUBE_common &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domFx_samplerDEPTH_common.h b/Engine/lib/collada/include/1.4/dom/domFx_samplerDEPTH_common.h new file mode 100644 index 000000000..15a637b0e --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domFx_samplerDEPTH_common.h @@ -0,0 +1,419 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domFx_samplerDEPTH_common_h__ +#define __domFx_samplerDEPTH_common_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * A texture sampler for depth maps. + */ +class domFx_samplerDEPTH_common_complexType +{ +public: + class domSource; + + typedef daeSmartRef domSourceRef; + typedef daeTArray domSource_Array; + + class domSource : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SOURCE; } + static daeInt ID() { return 79; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsNCName value of the text data of this element. + */ + xsNCName _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsNCName of the value. + */ + xsNCName getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNCName val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domSource(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domSource() {} + /** + * Overloaded assignment operator + */ + virtual domSource &operator=( const domSource &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domWrap_s; + + typedef daeSmartRef domWrap_sRef; + typedef daeTArray domWrap_s_Array; + + class domWrap_s : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::WRAP_S; } + static daeInt ID() { return 80; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_wrap_common value of the text data of this element. + */ + domFx_sampler_wrap_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_wrap_common of the value. + */ + domFx_sampler_wrap_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_wrap_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domWrap_s(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domWrap_s() {} + /** + * Overloaded assignment operator + */ + virtual domWrap_s &operator=( const domWrap_s &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domWrap_t; + + typedef daeSmartRef domWrap_tRef; + typedef daeTArray domWrap_t_Array; + + class domWrap_t : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::WRAP_T; } + static daeInt ID() { return 81; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_wrap_common value of the text data of this element. + */ + domFx_sampler_wrap_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_wrap_common of the value. + */ + domFx_sampler_wrap_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_wrap_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domWrap_t(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domWrap_t() {} + /** + * Overloaded assignment operator + */ + virtual domWrap_t &operator=( const domWrap_t &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMinfilter; + + typedef daeSmartRef domMinfilterRef; + typedef daeTArray domMinfilter_Array; + + class domMinfilter : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MINFILTER; } + static daeInt ID() { return 82; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_filter_common value of the text data of this element. + */ + domFx_sampler_filter_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_filter_common of the value. + */ + domFx_sampler_filter_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_filter_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMinfilter(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMinfilter() {} + /** + * Overloaded assignment operator + */ + virtual domMinfilter &operator=( const domMinfilter &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMagfilter; + + typedef daeSmartRef domMagfilterRef; + typedef daeTArray domMagfilter_Array; + + class domMagfilter : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MAGFILTER; } + static daeInt ID() { return 83; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_filter_common value of the text data of this element. + */ + domFx_sampler_filter_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_filter_common of the value. + */ + domFx_sampler_filter_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_filter_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMagfilter(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMagfilter() {} + /** + * Overloaded assignment operator + */ + virtual domMagfilter &operator=( const domMagfilter &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements + domSourceRef elemSource; + domWrap_sRef elemWrap_s; + domWrap_tRef elemWrap_t; + domMinfilterRef elemMinfilter; + domMagfilterRef elemMagfilter; + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the source element. + * @return a daeSmartRef to the source element. + */ + const domSourceRef getSource() const { return elemSource; } + /** + * Gets the wrap_s element. + * @return a daeSmartRef to the wrap_s element. + */ + const domWrap_sRef getWrap_s() const { return elemWrap_s; } + /** + * Gets the wrap_t element. + * @return a daeSmartRef to the wrap_t element. + */ + const domWrap_tRef getWrap_t() const { return elemWrap_t; } + /** + * Gets the minfilter element. + * @return a daeSmartRef to the minfilter element. + */ + const domMinfilterRef getMinfilter() const { return elemMinfilter; } + /** + * Gets the magfilter element. + * @return a daeSmartRef to the magfilter element. + */ + const domMagfilterRef getMagfilter() const { return elemMagfilter; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domFx_samplerDEPTH_common_complexType(DAE& dae, daeElement* elt) : elemSource(), elemWrap_s(), elemWrap_t(), elemMinfilter(), elemMagfilter(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domFx_samplerDEPTH_common_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domFx_samplerDEPTH_common_complexType &operator=( const domFx_samplerDEPTH_common_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domFx_samplerDEPTH_common_complexType. + */ +class domFx_samplerDEPTH_common : public daeElement, public domFx_samplerDEPTH_common_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FX_SAMPLERDEPTH_COMMON; } + static daeInt ID() { return 84; } + virtual daeInt typeID() const { return ID(); } +protected: + /** + * Constructor + */ + domFx_samplerDEPTH_common(DAE& dae) : daeElement(dae), domFx_samplerDEPTH_common_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domFx_samplerDEPTH_common() {} + /** + * Overloaded assignment operator + */ + virtual domFx_samplerDEPTH_common &operator=( const domFx_samplerDEPTH_common &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domFx_samplerRECT_common.h b/Engine/lib/collada/include/1.4/dom/domFx_samplerRECT_common.h new file mode 100644 index 000000000..45df67a6c --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domFx_samplerRECT_common.h @@ -0,0 +1,680 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domFx_samplerRECT_common_h__ +#define __domFx_samplerRECT_common_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * A two-dimensional texture sampler. + */ +class domFx_samplerRECT_common_complexType +{ +public: + class domSource; + + typedef daeSmartRef domSourceRef; + typedef daeTArray domSource_Array; + + class domSource : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SOURCE; } + static daeInt ID() { return 69; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsNCName value of the text data of this element. + */ + xsNCName _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsNCName of the value. + */ + xsNCName getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNCName val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domSource(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domSource() {} + /** + * Overloaded assignment operator + */ + virtual domSource &operator=( const domSource &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domWrap_s; + + typedef daeSmartRef domWrap_sRef; + typedef daeTArray domWrap_s_Array; + + class domWrap_s : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::WRAP_S; } + static daeInt ID() { return 70; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_wrap_common value of the text data of this element. + */ + domFx_sampler_wrap_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_wrap_common of the value. + */ + domFx_sampler_wrap_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_wrap_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domWrap_s(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domWrap_s() {} + /** + * Overloaded assignment operator + */ + virtual domWrap_s &operator=( const domWrap_s &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domWrap_t; + + typedef daeSmartRef domWrap_tRef; + typedef daeTArray domWrap_t_Array; + + class domWrap_t : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::WRAP_T; } + static daeInt ID() { return 71; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_wrap_common value of the text data of this element. + */ + domFx_sampler_wrap_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_wrap_common of the value. + */ + domFx_sampler_wrap_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_wrap_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domWrap_t(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domWrap_t() {} + /** + * Overloaded assignment operator + */ + virtual domWrap_t &operator=( const domWrap_t &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMinfilter; + + typedef daeSmartRef domMinfilterRef; + typedef daeTArray domMinfilter_Array; + + class domMinfilter : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MINFILTER; } + static daeInt ID() { return 72; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_filter_common value of the text data of this element. + */ + domFx_sampler_filter_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_filter_common of the value. + */ + domFx_sampler_filter_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_filter_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMinfilter(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMinfilter() {} + /** + * Overloaded assignment operator + */ + virtual domMinfilter &operator=( const domMinfilter &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMagfilter; + + typedef daeSmartRef domMagfilterRef; + typedef daeTArray domMagfilter_Array; + + class domMagfilter : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MAGFILTER; } + static daeInt ID() { return 73; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_filter_common value of the text data of this element. + */ + domFx_sampler_filter_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_filter_common of the value. + */ + domFx_sampler_filter_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_filter_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMagfilter(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMagfilter() {} + /** + * Overloaded assignment operator + */ + virtual domMagfilter &operator=( const domMagfilter &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMipfilter; + + typedef daeSmartRef domMipfilterRef; + typedef daeTArray domMipfilter_Array; + + class domMipfilter : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MIPFILTER; } + static daeInt ID() { return 74; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_filter_common value of the text data of this element. + */ + domFx_sampler_filter_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_filter_common of the value. + */ + domFx_sampler_filter_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_filter_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMipfilter(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMipfilter() {} + /** + * Overloaded assignment operator + */ + virtual domMipfilter &operator=( const domMipfilter &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBorder_color; + + typedef daeSmartRef domBorder_colorRef; + typedef daeTArray domBorder_color_Array; + + class domBorder_color : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BORDER_COLOR; } + static daeInt ID() { return 75; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_color_common value of the text data of this element. + */ + domFx_color_common _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domFx_color_common reference of the _value array. + */ + domFx_color_common &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domFx_color_common reference of the _value array. + */ + const domFx_color_common &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domFx_color_common &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBorder_color(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBorder_color() {} + /** + * Overloaded assignment operator + */ + virtual domBorder_color &operator=( const domBorder_color &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMipmap_maxlevel; + + typedef daeSmartRef domMipmap_maxlevelRef; + typedef daeTArray domMipmap_maxlevel_Array; + + class domMipmap_maxlevel : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MIPMAP_MAXLEVEL; } + static daeInt ID() { return 76; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsUnsignedByte value of the text data of this element. + */ + xsUnsignedByte _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a xsUnsignedByte of the value. + */ + xsUnsignedByte getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsUnsignedByte val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMipmap_maxlevel(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMipmap_maxlevel() {} + /** + * Overloaded assignment operator + */ + virtual domMipmap_maxlevel &operator=( const domMipmap_maxlevel &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMipmap_bias; + + typedef daeSmartRef domMipmap_biasRef; + typedef daeTArray domMipmap_bias_Array; + + class domMipmap_bias : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MIPMAP_BIAS; } + static daeInt ID() { return 77; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsFloat value of the text data of this element. + */ + xsFloat _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a xsFloat of the value. + */ + xsFloat getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsFloat val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMipmap_bias(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMipmap_bias() {} + /** + * Overloaded assignment operator + */ + virtual domMipmap_bias &operator=( const domMipmap_bias &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements + domSourceRef elemSource; + domWrap_sRef elemWrap_s; + domWrap_tRef elemWrap_t; + domMinfilterRef elemMinfilter; + domMagfilterRef elemMagfilter; + domMipfilterRef elemMipfilter; + domBorder_colorRef elemBorder_color; + domMipmap_maxlevelRef elemMipmap_maxlevel; + domMipmap_biasRef elemMipmap_bias; + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the source element. + * @return a daeSmartRef to the source element. + */ + const domSourceRef getSource() const { return elemSource; } + /** + * Gets the wrap_s element. + * @return a daeSmartRef to the wrap_s element. + */ + const domWrap_sRef getWrap_s() const { return elemWrap_s; } + /** + * Gets the wrap_t element. + * @return a daeSmartRef to the wrap_t element. + */ + const domWrap_tRef getWrap_t() const { return elemWrap_t; } + /** + * Gets the minfilter element. + * @return a daeSmartRef to the minfilter element. + */ + const domMinfilterRef getMinfilter() const { return elemMinfilter; } + /** + * Gets the magfilter element. + * @return a daeSmartRef to the magfilter element. + */ + const domMagfilterRef getMagfilter() const { return elemMagfilter; } + /** + * Gets the mipfilter element. + * @return a daeSmartRef to the mipfilter element. + */ + const domMipfilterRef getMipfilter() const { return elemMipfilter; } + /** + * Gets the border_color element. + * @return a daeSmartRef to the border_color element. + */ + const domBorder_colorRef getBorder_color() const { return elemBorder_color; } + /** + * Gets the mipmap_maxlevel element. + * @return a daeSmartRef to the mipmap_maxlevel element. + */ + const domMipmap_maxlevelRef getMipmap_maxlevel() const { return elemMipmap_maxlevel; } + /** + * Gets the mipmap_bias element. + * @return a daeSmartRef to the mipmap_bias element. + */ + const domMipmap_biasRef getMipmap_bias() const { return elemMipmap_bias; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domFx_samplerRECT_common_complexType(DAE& dae, daeElement* elt) : elemSource(), elemWrap_s(), elemWrap_t(), elemMinfilter(), elemMagfilter(), elemMipfilter(), elemBorder_color(), elemMipmap_maxlevel(), elemMipmap_bias(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domFx_samplerRECT_common_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domFx_samplerRECT_common_complexType &operator=( const domFx_samplerRECT_common_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domFx_samplerRECT_common_complexType. + */ +class domFx_samplerRECT_common : public daeElement, public domFx_samplerRECT_common_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FX_SAMPLERRECT_COMMON; } + static daeInt ID() { return 78; } + virtual daeInt typeID() const { return ID(); } +protected: + /** + * Constructor + */ + domFx_samplerRECT_common(DAE& dae) : daeElement(dae), domFx_samplerRECT_common_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domFx_samplerRECT_common() {} + /** + * Overloaded assignment operator + */ + virtual domFx_samplerRECT_common &operator=( const domFx_samplerRECT_common &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domFx_stenciltarget_common.h b/Engine/lib/collada/include/1.4/dom/domFx_stenciltarget_common.h new file mode 100644 index 000000000..7f5d47858 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domFx_stenciltarget_common.h @@ -0,0 +1,192 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domFx_stenciltarget_common_h__ +#define __domFx_stenciltarget_common_h__ + +#include +#include +#include + +class DAE; + +class domFx_stenciltarget_common_complexType +{ +protected: // Attributes + xsNonNegativeInteger attrIndex; + domFx_surface_face_enum attrFace; + xsNonNegativeInteger attrMip; + xsNonNegativeInteger attrSlice; + +protected: // Value + /** + * The xsNCName value of the text data of this element. + */ + xsNCName _value; + +public: //Accessors and Mutators + /** + * Gets the index attribute. + * @return Returns a xsNonNegativeInteger of the index attribute. + */ + xsNonNegativeInteger getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( xsNonNegativeInteger atIndex ) { attrIndex = atIndex; } + + /** + * Gets the face attribute. + * @return Returns a domFx_surface_face_enum of the face attribute. + */ + domFx_surface_face_enum getFace() const { return attrFace; } + /** + * Sets the face attribute. + * @param atFace The new value for the face attribute. + */ + void setFace( domFx_surface_face_enum atFace ) { attrFace = atFace; } + + /** + * Gets the mip attribute. + * @return Returns a xsNonNegativeInteger of the mip attribute. + */ + xsNonNegativeInteger getMip() const { return attrMip; } + /** + * Sets the mip attribute. + * @param atMip The new value for the mip attribute. + */ + void setMip( xsNonNegativeInteger atMip ) { attrMip = atMip; } + + /** + * Gets the slice attribute. + * @return Returns a xsNonNegativeInteger of the slice attribute. + */ + xsNonNegativeInteger getSlice() const { return attrSlice; } + /** + * Sets the slice attribute. + * @param atSlice The new value for the slice attribute. + */ + void setSlice( xsNonNegativeInteger atSlice ) { attrSlice = atSlice; } + + /** + * Gets the value of this element. + * @return Returns a xsNCName of the value. + */ + xsNCName getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNCName val ) { *(daeStringRef*)&_value = val; } + +protected: + /** + * Constructor + */ + domFx_stenciltarget_common_complexType(DAE& dae, daeElement* elt) : attrIndex(), attrFace(), attrMip(), attrSlice(), _value() {} + /** + * Destructor + */ + virtual ~domFx_stenciltarget_common_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domFx_stenciltarget_common_complexType &operator=( const domFx_stenciltarget_common_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domFx_stenciltarget_common_complexType. + */ +class domFx_stenciltarget_common : public daeElement, public domFx_stenciltarget_common_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FX_STENCILTARGET_COMMON; } + static daeInt ID() { return 87; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the index attribute. + * @return Returns a xsNonNegativeInteger of the index attribute. + */ + xsNonNegativeInteger getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( xsNonNegativeInteger atIndex ) { attrIndex = atIndex; _validAttributeArray[0] = true; } + + /** + * Gets the face attribute. + * @return Returns a domFx_surface_face_enum of the face attribute. + */ + domFx_surface_face_enum getFace() const { return attrFace; } + /** + * Sets the face attribute. + * @param atFace The new value for the face attribute. + */ + void setFace( domFx_surface_face_enum atFace ) { attrFace = atFace; _validAttributeArray[1] = true; } + + /** + * Gets the mip attribute. + * @return Returns a xsNonNegativeInteger of the mip attribute. + */ + xsNonNegativeInteger getMip() const { return attrMip; } + /** + * Sets the mip attribute. + * @param atMip The new value for the mip attribute. + */ + void setMip( xsNonNegativeInteger atMip ) { attrMip = atMip; _validAttributeArray[2] = true; } + + /** + * Gets the slice attribute. + * @return Returns a xsNonNegativeInteger of the slice attribute. + */ + xsNonNegativeInteger getSlice() const { return attrSlice; } + /** + * Sets the slice attribute. + * @param atSlice The new value for the slice attribute. + */ + void setSlice( xsNonNegativeInteger atSlice ) { attrSlice = atSlice; _validAttributeArray[3] = true; } + +protected: + /** + * Constructor + */ + domFx_stenciltarget_common(DAE& dae) : daeElement(dae), domFx_stenciltarget_common_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domFx_stenciltarget_common() {} + /** + * Overloaded assignment operator + */ + virtual domFx_stenciltarget_common &operator=( const domFx_stenciltarget_common &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domFx_surface_common.h b/Engine/lib/collada/include/1.4/dom/domFx_surface_common.h new file mode 100644 index 000000000..dd812b6f4 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domFx_surface_common.h @@ -0,0 +1,568 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domFx_surface_common_h__ +#define __domFx_surface_common_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * The fx_surface_common type is used to declare a resource that can be used + * both as the source for texture samples and as the target of a rendering + * pass. + */ +class domFx_surface_common_complexType +{ +public: + class domFormat; + + typedef daeSmartRef domFormatRef; + typedef daeTArray domFormat_Array; + +/** + * Contains a string representing the profile and platform specific texel + * format that the author would like this surface to use. If this element + * is not specified then the application will use a common format R8G8B8A8 + * with linear color gradient, not sRGB. + */ + class domFormat : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FORMAT; } + static daeInt ID() { return 22; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsToken value of the text data of this element. + */ + xsToken _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsToken of the value. + */ + xsToken getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsToken val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domFormat(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFormat() {} + /** + * Overloaded assignment operator + */ + virtual domFormat &operator=( const domFormat &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domSize; + + typedef daeSmartRef domSizeRef; + typedef daeTArray domSize_Array; + +/** + * The surface should be sized to these exact dimensions + */ + class domSize : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SIZE; } + static daeInt ID() { return 23; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domInt3 value of the text data of this element. + */ + domInt3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domInt3 reference of the _value array. + */ + domInt3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domInt3 reference of the _value array. + */ + const domInt3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domInt3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domSize(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domSize() {} + /** + * Overloaded assignment operator + */ + virtual domSize &operator=( const domSize &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domViewport_ratio; + + typedef daeSmartRef domViewport_ratioRef; + typedef daeTArray domViewport_ratio_Array; + +/** + * The surface should be sized to a dimension based on this ratio of the viewport's + * dimensions in pixels + */ + class domViewport_ratio : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::VIEWPORT_RATIO; } + static daeInt ID() { return 24; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFloat2 value of the text data of this element. + */ + domFloat2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domFloat2 reference of the _value array. + */ + domFloat2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domFloat2 reference of the _value array. + */ + const domFloat2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domFloat2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domViewport_ratio(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domViewport_ratio() {} + /** + * Overloaded assignment operator + */ + virtual domViewport_ratio &operator=( const domViewport_ratio &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMip_levels; + + typedef daeSmartRef domMip_levelsRef; + typedef daeTArray domMip_levels_Array; + +/** + * the surface should contain the following number of MIP levels. If this + * element is not present it is assumed that all miplevels exist until a dimension + * becomes 1 texel. To create a surface that has only one level of mip maps + * (mip=0) set this to 1. If the value is 0 the result is the same as if + * mip_levels was unspecified, all possible mip_levels will exist. + */ + class domMip_levels : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MIP_LEVELS; } + static daeInt ID() { return 25; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsUnsignedInt value of the text data of this element. + */ + xsUnsignedInt _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a xsUnsignedInt of the value. + */ + xsUnsignedInt getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsUnsignedInt val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMip_levels(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMip_levels() {} + /** + * Overloaded assignment operator + */ + virtual domMip_levels &operator=( const domMip_levels &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMipmap_generate; + + typedef daeSmartRef domMipmap_generateRef; + typedef daeTArray domMipmap_generate_Array; + +/** + * By default it is assumed that mipmaps are supplied by the author so, if + * not all subsurfaces are initialized, it is invalid and will result in profile + * and platform specific behavior unless mipmap_generate is responsible for + * initializing the remainder of the sub-surfaces + */ + class domMipmap_generate : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MIPMAP_GENERATE; } + static daeInt ID() { return 26; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsBoolean value of the text data of this element. + */ + xsBoolean _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a xsBoolean of the value. + */ + xsBoolean getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsBoolean val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMipmap_generate(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMipmap_generate() {} + /** + * Overloaded assignment operator + */ + virtual domMipmap_generate &operator=( const domMipmap_generate &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attribute +/** + * Specifying the type of a surface is mandatory though the type may be "UNTYPED". + * When a surface is typed as UNTYPED, it is said to be temporarily untyped + * and instead will be typed later by the context it is used in such as which + * samplers reference it in that are used in a particular technique or pass. + * If there is a type mismatch between what is set into it later and what + * the runtime decides the type should be the result in profile and platform + * specific behavior. + */ + domFx_surface_type_enum attrType; + +protected: // Elements +/** + * The common set of initalization options for surfaces. Choose which is + * appropriate for your surface based on the type attribute and other characteristics + * described by the annotation docs on the choiced child elements of this + * type. @see domFx_surface_init_common + */ + domFx_surface_init_commonRef elemFx_surface_init_common; +/** + * Contains a string representing the profile and platform specific texel + * format that the author would like this surface to use. If this element + * is not specified then the application will use a common format R8G8B8A8 + * with linear color gradient, not sRGB. @see domFormat + */ + domFormatRef elemFormat; +/** + * If the exact format cannot be resolved via the "format" element then the + * format_hint will describe the important features of the format so that + * the application may select a compatable or close format @see domFormat_hint + */ + domFx_surface_format_hint_commonRef elemFormat_hint; +/** + * The surface should be sized to these exact dimensions @see domSize + */ + domSizeRef elemSize; +/** + * The surface should be sized to a dimension based on this ratio of the viewport's + * dimensions in pixels @see domViewport_ratio + */ + domViewport_ratioRef elemViewport_ratio; +/** + * the surface should contain the following number of MIP levels. If this + * element is not present it is assumed that all miplevels exist until a dimension + * becomes 1 texel. To create a surface that has only one level of mip maps + * (mip=0) set this to 1. If the value is 0 the result is the same as if + * mip_levels was unspecified, all possible mip_levels will exist. @see domMip_levels + */ + domMip_levelsRef elemMip_levels; +/** + * By default it is assumed that mipmaps are supplied by the author so, if + * not all subsurfaces are initialized, it is invalid and will result in profile + * and platform specific behavior unless mipmap_generate is responsible for + * initializing the remainder of the sub-surfaces @see domMipmap_generate + */ + domMipmap_generateRef elemMipmap_generate; + domExtra_Array elemExtra_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the type attribute. + * @return Returns a domFx_surface_type_enum of the type attribute. + */ + domFx_surface_type_enum getType() const { return attrType; } + /** + * Sets the type attribute. + * @param atType The new value for the type attribute. + */ + void setType( domFx_surface_type_enum atType ) { attrType = atType; } + + /** + * Gets the fx_surface_init_common element. + * @return a daeSmartRef to the fx_surface_init_common element. + */ + const domFx_surface_init_commonRef getFx_surface_init_common() const { return elemFx_surface_init_common; } + /** + * Gets the format element. + * @return a daeSmartRef to the format element. + */ + const domFormatRef getFormat() const { return elemFormat; } + /** + * Gets the format_hint element. + * @return a daeSmartRef to the format_hint element. + */ + const domFx_surface_format_hint_commonRef getFormat_hint() const { return elemFormat_hint; } + /** + * Gets the size element. + * @return a daeSmartRef to the size element. + */ + const domSizeRef getSize() const { return elemSize; } + /** + * Gets the viewport_ratio element. + * @return a daeSmartRef to the viewport_ratio element. + */ + const domViewport_ratioRef getViewport_ratio() const { return elemViewport_ratio; } + /** + * Gets the mip_levels element. + * @return a daeSmartRef to the mip_levels element. + */ + const domMip_levelsRef getMip_levels() const { return elemMip_levels; } + /** + * Gets the mipmap_generate element. + * @return a daeSmartRef to the mipmap_generate element. + */ + const domMipmap_generateRef getMipmap_generate() const { return elemMipmap_generate; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domFx_surface_common_complexType(DAE& dae, daeElement* elt) : attrType(), elemFx_surface_init_common(), elemFormat(), elemFormat_hint(), elemSize(), elemViewport_ratio(), elemMip_levels(), elemMipmap_generate(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domFx_surface_common_complexType() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domFx_surface_common_complexType &operator=( const domFx_surface_common_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domFx_surface_common_complexType. + */ +class domFx_surface_common : public daeElement, public domFx_surface_common_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FX_SURFACE_COMMON; } + static daeInt ID() { return 27; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the type attribute. + * @return Returns a domFx_surface_type_enum of the type attribute. + */ + domFx_surface_type_enum getType() const { return attrType; } + /** + * Sets the type attribute. + * @param atType The new value for the type attribute. + */ + void setType( domFx_surface_type_enum atType ) { attrType = atType; _validAttributeArray[0] = true; } + +protected: + /** + * Constructor + */ + domFx_surface_common(DAE& dae) : daeElement(dae), domFx_surface_common_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domFx_surface_common() {} + /** + * Overloaded assignment operator + */ + virtual domFx_surface_common &operator=( const domFx_surface_common &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domFx_surface_format_hint_common.h b/Engine/lib/collada/include/1.4/dom/domFx_surface_format_hint_common.h new file mode 100644 index 000000000..6a53ecf09 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domFx_surface_format_hint_common.h @@ -0,0 +1,402 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domFx_surface_format_hint_common_h__ +#define __domFx_surface_format_hint_common_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * If the exact format cannot be resolve via other methods then the format_hint + * will describe the important features of the format so that the application + * may select a compatable or close format + */ +class domFx_surface_format_hint_common_complexType +{ +public: + class domChannels; + + typedef daeSmartRef domChannelsRef; + typedef daeTArray domChannels_Array; + +/** + * The per-texel layout of the format. The length of the string indicate + * how many channels there are and the letter respresents the name of the + * channel. There are typically 0 to 4 channels. + */ + class domChannels : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CHANNELS; } + static daeInt ID() { return 6; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_surface_format_hint_channels_enum value of the text data of this element. + */ + domFx_surface_format_hint_channels_enum _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_surface_format_hint_channels_enum of the value. + */ + domFx_surface_format_hint_channels_enum getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_surface_format_hint_channels_enum val ) { _value = val; } + + protected: + /** + * Constructor + */ + domChannels(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domChannels() {} + /** + * Overloaded assignment operator + */ + virtual domChannels &operator=( const domChannels &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domRange; + + typedef daeSmartRef domRangeRef; + typedef daeTArray domRange_Array; + +/** + * Each channel represents a range of values. Some example ranges are signed + * or unsigned integers, or between between a clamped range such as 0.0f to + * 1.0f, or high dynamic range via floating point + */ + class domRange : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::RANGE; } + static daeInt ID() { return 7; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_surface_format_hint_range_enum value of the text data of this element. + */ + domFx_surface_format_hint_range_enum _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_surface_format_hint_range_enum of the value. + */ + domFx_surface_format_hint_range_enum getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_surface_format_hint_range_enum val ) { _value = val; } + + protected: + /** + * Constructor + */ + domRange(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domRange() {} + /** + * Overloaded assignment operator + */ + virtual domRange &operator=( const domRange &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPrecision; + + typedef daeSmartRef domPrecisionRef; + typedef daeTArray domPrecision_Array; + +/** + * Each channel of the texel has a precision. Typically these are all linked + * together. An exact format lay lower the precision of an individual channel + * but applying a higher precision by linking the channels together may still + * convey the same information. + */ + class domPrecision : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PRECISION; } + static daeInt ID() { return 8; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_surface_format_hint_precision_enum value of the text data of this element. + */ + domFx_surface_format_hint_precision_enum _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_surface_format_hint_precision_enum of the value. + */ + domFx_surface_format_hint_precision_enum getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_surface_format_hint_precision_enum val ) { _value = val; } + + protected: + /** + * Constructor + */ + domPrecision(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domPrecision() {} + /** + * Overloaded assignment operator + */ + virtual domPrecision &operator=( const domPrecision &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domOption; + + typedef daeSmartRef domOptionRef; + typedef daeTArray domOption_Array; + +/** + * Additional hints about data relationships and other things to help the + * application pick the best format. + */ + class domOption : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::OPTION; } + static daeInt ID() { return 9; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_surface_format_hint_option_enum value of the text data of this element. + */ + domFx_surface_format_hint_option_enum _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_surface_format_hint_option_enum of the value. + */ + domFx_surface_format_hint_option_enum getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_surface_format_hint_option_enum val ) { _value = val; } + + protected: + /** + * Constructor + */ + domOption(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domOption() {} + /** + * Overloaded assignment operator + */ + virtual domOption &operator=( const domOption &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements +/** + * The per-texel layout of the format. The length of the string indicate + * how many channels there are and the letter respresents the name of the + * channel. There are typically 0 to 4 channels. @see domChannels + */ + domChannelsRef elemChannels; +/** + * Each channel represents a range of values. Some example ranges are signed + * or unsigned integers, or between between a clamped range such as 0.0f to + * 1.0f, or high dynamic range via floating point @see domRange + */ + domRangeRef elemRange; +/** + * Each channel of the texel has a precision. Typically these are all linked + * together. An exact format lay lower the precision of an individual channel + * but applying a higher precision by linking the channels together may still + * convey the same information. @see domPrecision + */ + domPrecisionRef elemPrecision; +/** + * Additional hints about data relationships and other things to help the + * application pick the best format. @see domOption + */ + domOption_Array elemOption_array; + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the channels element. + * @return a daeSmartRef to the channels element. + */ + const domChannelsRef getChannels() const { return elemChannels; } + /** + * Gets the range element. + * @return a daeSmartRef to the range element. + */ + const domRangeRef getRange() const { return elemRange; } + /** + * Gets the precision element. + * @return a daeSmartRef to the precision element. + */ + const domPrecisionRef getPrecision() const { return elemPrecision; } + /** + * Gets the option element array. + * @return Returns a reference to the array of option elements. + */ + domOption_Array &getOption_array() { return elemOption_array; } + /** + * Gets the option element array. + * @return Returns a constant reference to the array of option elements. + */ + const domOption_Array &getOption_array() const { return elemOption_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domFx_surface_format_hint_common_complexType(DAE& dae, daeElement* elt) : elemChannels(), elemRange(), elemPrecision(), elemOption_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domFx_surface_format_hint_common_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domFx_surface_format_hint_common_complexType &operator=( const domFx_surface_format_hint_common_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domFx_surface_format_hint_common_complexType. + */ +class domFx_surface_format_hint_common : public daeElement, public domFx_surface_format_hint_common_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FX_SURFACE_FORMAT_HINT_COMMON; } + static daeInt ID() { return 10; } + virtual daeInt typeID() const { return ID(); } +protected: + /** + * Constructor + */ + domFx_surface_format_hint_common(DAE& dae) : daeElement(dae), domFx_surface_format_hint_common_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domFx_surface_format_hint_common() {} + /** + * Overloaded assignment operator + */ + virtual domFx_surface_format_hint_common &operator=( const domFx_surface_format_hint_common &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domFx_surface_init_common.h b/Engine/lib/collada/include/1.4/dom/domFx_surface_init_common.h new file mode 100644 index 000000000..8ff1721f7 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domFx_surface_init_common.h @@ -0,0 +1,259 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domFx_surface_init_common_h__ +#define __domFx_surface_init_common_h__ + +#include +#include +#include + +#include +#include +#include +#include +class DAE; + +/** + * The common set of initalization options for surfaces. Choose which is + * appropriate for your surface based on type and other characteristics. described + * by the annotation docs on the child elements. + */ +class domFx_surface_init_common : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FX_SURFACE_INIT_COMMON; } + static daeInt ID() { return 168; } + virtual daeInt typeID() const { return ID(); } +public: + class domInit_as_null; + + typedef daeSmartRef domInit_as_nullRef; + typedef daeTArray domInit_as_null_Array; + +/** + * This surface is intended to be initialized later externally by a "setparam" + * element. If it is used before being initialized there is profile and platform + * specific behavior. Most elements on the surface element containing this + * will be ignored including mip_levels, mipmap_generate, size, viewport_ratio, + * and format. + */ + class domInit_as_null : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INIT_AS_NULL; } + static daeInt ID() { return 169; } + virtual daeInt typeID() const { return ID(); } + + protected: + /** + * Constructor + */ + domInit_as_null(DAE& dae) : daeElement(dae) {} + /** + * Destructor + */ + virtual ~domInit_as_null() {} + /** + * Overloaded assignment operator + */ + virtual domInit_as_null &operator=( const domInit_as_null &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInit_as_target; + + typedef daeSmartRef domInit_as_targetRef; + typedef daeTArray domInit_as_target_Array; + +/** + * Init as a target for depth, stencil, or color. It does not need image + * data. Surface should not have mipmap_generate when using this. + */ + class domInit_as_target : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INIT_AS_TARGET; } + static daeInt ID() { return 170; } + virtual daeInt typeID() const { return ID(); } + + protected: + /** + * Constructor + */ + domInit_as_target(DAE& dae) : daeElement(dae) {} + /** + * Destructor + */ + virtual ~domInit_as_target() {} + /** + * Overloaded assignment operator + */ + virtual domInit_as_target &operator=( const domInit_as_target &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements +/** + * This surface is intended to be initialized later externally by a "setparam" + * element. If it is used before being initialized there is profile and platform + * specific behavior. Most elements on the surface element containing this + * will be ignored including mip_levels, mipmap_generate, size, viewport_ratio, + * and format. @see domInit_as_null + */ + domInit_as_nullRef elemInit_as_null; +/** + * Init as a target for depth, stencil, or color. It does not need image + * data. Surface should not have mipmap_generate when using this. @see domInit_as_target + */ + domInit_as_targetRef elemInit_as_target; +/** + * Init a CUBE from a compound image such as DDS @see domInit_cube + */ + domFx_surface_init_cube_commonRef elemInit_cube; +/** + * Init a 3D from a compound image such as DDS @see domInit_volume + */ + domFx_surface_init_volume_commonRef elemInit_volume; +/** + * Init a 1D,2D,RECT,DEPTH from a compound image such as DDS @see domInit_planar + */ + domFx_surface_init_planar_commonRef elemInit_planar; +/** + * Initialize the surface one sub-surface at a time by specifying combinations + * of mip, face, and slice which make sense for a particular surface type. + * Each sub-surface is initialized by a common 2D image, not a complex compound + * image such as DDS. If not all subsurfaces are initialized, it is invalid + * and will result in profile and platform specific behavior unless mipmap_generate + * is responsible for initializing the remainder of the sub-surfaces @see + * domInit_from + */ + domFx_surface_init_from_common_Array elemInit_from_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the init_as_null element. + * @return a daeSmartRef to the init_as_null element. + */ + const domInit_as_nullRef getInit_as_null() const { return elemInit_as_null; } + /** + * Gets the init_as_target element. + * @return a daeSmartRef to the init_as_target element. + */ + const domInit_as_targetRef getInit_as_target() const { return elemInit_as_target; } + /** + * Gets the init_cube element. + * @return a daeSmartRef to the init_cube element. + */ + const domFx_surface_init_cube_commonRef getInit_cube() const { return elemInit_cube; } + /** + * Gets the init_volume element. + * @return a daeSmartRef to the init_volume element. + */ + const domFx_surface_init_volume_commonRef getInit_volume() const { return elemInit_volume; } + /** + * Gets the init_planar element. + * @return a daeSmartRef to the init_planar element. + */ + const domFx_surface_init_planar_commonRef getInit_planar() const { return elemInit_planar; } + /** + * Gets the init_from element array. + * @return Returns a reference to the array of init_from elements. + */ + domFx_surface_init_from_common_Array &getInit_from_array() { return elemInit_from_array; } + /** + * Gets the init_from element array. + * @return Returns a constant reference to the array of init_from elements. + */ + const domFx_surface_init_from_common_Array &getInit_from_array() const { return elemInit_from_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domFx_surface_init_common(DAE& dae) : daeElement(dae), elemInit_as_null(), elemInit_as_target(), elemInit_cube(), elemInit_volume(), elemInit_planar(), elemInit_from_array() {} + /** + * Destructor + */ + virtual ~domFx_surface_init_common() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domFx_surface_init_common &operator=( const domFx_surface_init_common &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domFx_surface_init_cube_common.h b/Engine/lib/collada/include/1.4/dom/domFx_surface_init_cube_common.h new file mode 100644 index 000000000..863102841 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domFx_surface_init_cube_common.h @@ -0,0 +1,414 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domFx_surface_init_cube_common_h__ +#define __domFx_surface_init_cube_common_h__ + +#include +#include +#include + +class DAE; + +class domFx_surface_init_cube_common_complexType +{ +public: + class domAll; + + typedef daeSmartRef domAllRef; + typedef daeTArray domAll_Array; + +/** + * Init the entire surface with one compound image such as DDS + */ + class domAll : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ALL; } + static daeInt ID() { return 16; } + virtual daeInt typeID() const { return ID(); } + protected: // Attribute + xsIDREF attrRef; + + + public: //Accessors and Mutators + /** + * Gets the ref attribute. + * @return Returns a xsIDREF reference of the ref attribute. + */ + xsIDREF &getRef() { return attrRef; } + /** + * Gets the ref attribute. + * @return Returns a constant xsIDREF reference of the ref attribute. + */ + const xsIDREF &getRef() const{ return attrRef; } + /** + * Sets the ref attribute. + * @param atRef The new value for the ref attribute. + */ + void setRef( const xsIDREF &atRef ) { attrRef = atRef; _validAttributeArray[0] = true; } + + protected: + /** + * Constructor + */ + domAll(DAE& dae) : daeElement(dae), attrRef(*this) {} + /** + * Destructor + */ + virtual ~domAll() {} + /** + * Overloaded assignment operator + */ + virtual domAll &operator=( const domAll &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPrimary; + + typedef daeSmartRef domPrimaryRef; + typedef daeTArray domPrimary_Array; + +/** + * Init all primary mip level 0 subsurfaces with one compound image such as + * DDS. Use of this element expects that the surface has element mip_levels=0 + * or mipmap_generate. + */ + class domPrimary : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PRIMARY; } + static daeInt ID() { return 17; } + virtual daeInt typeID() const { return ID(); } + public: + class domOrder; + + typedef daeSmartRef domOrderRef; + typedef daeTArray domOrder_Array; + +/** + * If the image dues not natively describe the face ordering then this series + * of order elements will describe which face the index belongs too + */ + class domOrder : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ORDER; } + static daeInt ID() { return 18; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_surface_face_enum value of the text data of this element. + */ + domFx_surface_face_enum _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_surface_face_enum of the value. + */ + domFx_surface_face_enum getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_surface_face_enum val ) { _value = val; } + + protected: + /** + * Constructor + */ + domOrder(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domOrder() {} + /** + * Overloaded assignment operator + */ + virtual domOrder &operator=( const domOrder &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + protected: // Attribute + xsIDREF attrRef; + + protected: // Element +/** + * If the image dues not natively describe the face ordering then this series + * of order elements will describe which face the index belongs too @see domOrder + */ + domOrder_Array elemOrder_array; + + public: //Accessors and Mutators + /** + * Gets the ref attribute. + * @return Returns a xsIDREF reference of the ref attribute. + */ + xsIDREF &getRef() { return attrRef; } + /** + * Gets the ref attribute. + * @return Returns a constant xsIDREF reference of the ref attribute. + */ + const xsIDREF &getRef() const{ return attrRef; } + /** + * Sets the ref attribute. + * @param atRef The new value for the ref attribute. + */ + void setRef( const xsIDREF &atRef ) { attrRef = atRef; _validAttributeArray[0] = true; } + + /** + * Gets the order element array. + * @return Returns a reference to the array of order elements. + */ + domOrder_Array &getOrder_array() { return elemOrder_array; } + /** + * Gets the order element array. + * @return Returns a constant reference to the array of order elements. + */ + const domOrder_Array &getOrder_array() const { return elemOrder_array; } + protected: + /** + * Constructor + */ + domPrimary(DAE& dae) : daeElement(dae), attrRef(*this), elemOrder_array() {} + /** + * Destructor + */ + virtual ~domPrimary() {} + /** + * Overloaded assignment operator + */ + virtual domPrimary &operator=( const domPrimary &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFace; + + typedef daeSmartRef domFaceRef; + typedef daeTArray domFace_Array; + +/** + * Init each face mipchain with one compound image such as DDS + */ + class domFace : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FACE; } + static daeInt ID() { return 19; } + virtual daeInt typeID() const { return ID(); } + protected: // Attribute + xsIDREF attrRef; + + + public: //Accessors and Mutators + /** + * Gets the ref attribute. + * @return Returns a xsIDREF reference of the ref attribute. + */ + xsIDREF &getRef() { return attrRef; } + /** + * Gets the ref attribute. + * @return Returns a constant xsIDREF reference of the ref attribute. + */ + const xsIDREF &getRef() const{ return attrRef; } + /** + * Sets the ref attribute. + * @param atRef The new value for the ref attribute. + */ + void setRef( const xsIDREF &atRef ) { attrRef = atRef; _validAttributeArray[0] = true; } + + protected: + /** + * Constructor + */ + domFace(DAE& dae) : daeElement(dae), attrRef(*this) {} + /** + * Destructor + */ + virtual ~domFace() {} + /** + * Overloaded assignment operator + */ + virtual domFace &operator=( const domFace &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements +/** + * Init the entire surface with one compound image such as DDS @see domAll + */ + domAllRef elemAll; +/** + * Init all primary mip level 0 subsurfaces with one compound image such as + * DDS. Use of this element expects that the surface has element mip_levels=0 + * or mipmap_generate. @see domPrimary + */ + domPrimaryRef elemPrimary; +/** + * Init each face mipchain with one compound image such as DDS @see domFace + */ + domFace_Array elemFace_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the all element. + * @return a daeSmartRef to the all element. + */ + const domAllRef getAll() const { return elemAll; } + /** + * Gets the primary element. + * @return a daeSmartRef to the primary element. + */ + const domPrimaryRef getPrimary() const { return elemPrimary; } + /** + * Gets the face element array. + * @return Returns a reference to the array of face elements. + */ + domFace_Array &getFace_array() { return elemFace_array; } + /** + * Gets the face element array. + * @return Returns a constant reference to the array of face elements. + */ + const domFace_Array &getFace_array() const { return elemFace_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domFx_surface_init_cube_common_complexType(DAE& dae, daeElement* elt) : elemAll(), elemPrimary(), elemFace_array() {} + /** + * Destructor + */ + virtual ~domFx_surface_init_cube_common_complexType() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domFx_surface_init_cube_common_complexType &operator=( const domFx_surface_init_cube_common_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domFx_surface_init_cube_common_complexType. + */ +class domFx_surface_init_cube_common : public daeElement, public domFx_surface_init_cube_common_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FX_SURFACE_INIT_CUBE_COMMON; } + static daeInt ID() { return 20; } + virtual daeInt typeID() const { return ID(); } +protected: + /** + * Constructor + */ + domFx_surface_init_cube_common(DAE& dae) : daeElement(dae), domFx_surface_init_cube_common_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domFx_surface_init_cube_common() {} + /** + * Overloaded assignment operator + */ + virtual domFx_surface_init_cube_common &operator=( const domFx_surface_init_cube_common &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domFx_surface_init_from_common.h b/Engine/lib/collada/include/1.4/dom/domFx_surface_init_from_common.h new file mode 100644 index 000000000..52b9570f8 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domFx_surface_init_from_common.h @@ -0,0 +1,178 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domFx_surface_init_from_common_h__ +#define __domFx_surface_init_from_common_h__ + +#include +#include +#include + +class DAE; + +/** + * This element is an IDREF which specifies the image to use to initialize + * a specific mip of a 1D or 2D surface, 3D slice, or Cube face. + */ +class domFx_surface_init_from_common_complexType +{ +protected: // Attributes + xsUnsignedInt attrMip; + xsUnsignedInt attrSlice; + domFx_surface_face_enum attrFace; + +protected: // Value + /** + * The xsIDREF value of the text data of this element. + */ + xsIDREF _value; + +public: //Accessors and Mutators + /** + * Gets the mip attribute. + * @return Returns a xsUnsignedInt of the mip attribute. + */ + xsUnsignedInt getMip() const { return attrMip; } + /** + * Sets the mip attribute. + * @param atMip The new value for the mip attribute. + */ + void setMip( xsUnsignedInt atMip ) { attrMip = atMip; } + + /** + * Gets the slice attribute. + * @return Returns a xsUnsignedInt of the slice attribute. + */ + xsUnsignedInt getSlice() const { return attrSlice; } + /** + * Sets the slice attribute. + * @param atSlice The new value for the slice attribute. + */ + void setSlice( xsUnsignedInt atSlice ) { attrSlice = atSlice; } + + /** + * Gets the face attribute. + * @return Returns a domFx_surface_face_enum of the face attribute. + */ + domFx_surface_face_enum getFace() const { return attrFace; } + /** + * Sets the face attribute. + * @param atFace The new value for the face attribute. + */ + void setFace( domFx_surface_face_enum atFace ) { attrFace = atFace; } + + /** + * Gets the value of this element. + * @return Returns a xsIDREF of the value. + */ + xsIDREF &getValue() { return _value; } + /** + * Gets the value of this element. + * @return Returns a constant xsIDREF of the value. + */ + const xsIDREF &getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( const xsIDREF &val ) { _value = val; } + +protected: + /** + * Constructor + */ + domFx_surface_init_from_common_complexType(DAE& dae, daeElement* elt) : attrMip(), attrSlice(), attrFace(), _value(*elt) {} + /** + * Destructor + */ + virtual ~domFx_surface_init_from_common_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domFx_surface_init_from_common_complexType &operator=( const domFx_surface_init_from_common_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domFx_surface_init_from_common_complexType. + */ +class domFx_surface_init_from_common : public daeElement, public domFx_surface_init_from_common_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FX_SURFACE_INIT_FROM_COMMON; } + static daeInt ID() { return 21; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the mip attribute. + * @return Returns a xsUnsignedInt of the mip attribute. + */ + xsUnsignedInt getMip() const { return attrMip; } + /** + * Sets the mip attribute. + * @param atMip The new value for the mip attribute. + */ + void setMip( xsUnsignedInt atMip ) { attrMip = atMip; _validAttributeArray[0] = true; } + + /** + * Gets the slice attribute. + * @return Returns a xsUnsignedInt of the slice attribute. + */ + xsUnsignedInt getSlice() const { return attrSlice; } + /** + * Sets the slice attribute. + * @param atSlice The new value for the slice attribute. + */ + void setSlice( xsUnsignedInt atSlice ) { attrSlice = atSlice; _validAttributeArray[1] = true; } + + /** + * Gets the face attribute. + * @return Returns a domFx_surface_face_enum of the face attribute. + */ + domFx_surface_face_enum getFace() const { return attrFace; } + /** + * Sets the face attribute. + * @param atFace The new value for the face attribute. + */ + void setFace( domFx_surface_face_enum atFace ) { attrFace = atFace; _validAttributeArray[2] = true; } + +protected: + /** + * Constructor + */ + domFx_surface_init_from_common(DAE& dae) : daeElement(dae), domFx_surface_init_from_common_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domFx_surface_init_from_common() {} + /** + * Overloaded assignment operator + */ + virtual domFx_surface_init_from_common &operator=( const domFx_surface_init_from_common &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domFx_surface_init_planar_common.h b/Engine/lib/collada/include/1.4/dom/domFx_surface_init_planar_common.h new file mode 100644 index 000000000..04dfe2d33 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domFx_surface_init_planar_common.h @@ -0,0 +1,184 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domFx_surface_init_planar_common_h__ +#define __domFx_surface_init_planar_common_h__ + +#include +#include +#include + +class DAE; + +/** + * For 1D, 2D, RECT surface types + */ +class domFx_surface_init_planar_common_complexType +{ +public: + class domAll; + + typedef daeSmartRef domAllRef; + typedef daeTArray domAll_Array; + +/** + * Init the entire surface with one compound image such as DDS + */ + class domAll : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ALL; } + static daeInt ID() { return 11; } + virtual daeInt typeID() const { return ID(); } + protected: // Attribute + xsIDREF attrRef; + + + public: //Accessors and Mutators + /** + * Gets the ref attribute. + * @return Returns a xsIDREF reference of the ref attribute. + */ + xsIDREF &getRef() { return attrRef; } + /** + * Gets the ref attribute. + * @return Returns a constant xsIDREF reference of the ref attribute. + */ + const xsIDREF &getRef() const{ return attrRef; } + /** + * Sets the ref attribute. + * @param atRef The new value for the ref attribute. + */ + void setRef( const xsIDREF &atRef ) { attrRef = atRef; _validAttributeArray[0] = true; } + + protected: + /** + * Constructor + */ + domAll(DAE& dae) : daeElement(dae), attrRef(*this) {} + /** + * Destructor + */ + virtual ~domAll() {} + /** + * Overloaded assignment operator + */ + virtual domAll &operator=( const domAll &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Element +/** + * Init the entire surface with one compound image such as DDS @see domAll + */ + domAllRef elemAll; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the all element. + * @return a daeSmartRef to the all element. + */ + const domAllRef getAll() const { return elemAll; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domFx_surface_init_planar_common_complexType(DAE& dae, daeElement* elt) : elemAll() {} + /** + * Destructor + */ + virtual ~domFx_surface_init_planar_common_complexType() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domFx_surface_init_planar_common_complexType &operator=( const domFx_surface_init_planar_common_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domFx_surface_init_planar_common_complexType. + */ +class domFx_surface_init_planar_common : public daeElement, public domFx_surface_init_planar_common_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FX_SURFACE_INIT_PLANAR_COMMON; } + static daeInt ID() { return 12; } + virtual daeInt typeID() const { return ID(); } +protected: + /** + * Constructor + */ + domFx_surface_init_planar_common(DAE& dae) : daeElement(dae), domFx_surface_init_planar_common_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domFx_surface_init_planar_common() {} + /** + * Overloaded assignment operator + */ + virtual domFx_surface_init_planar_common &operator=( const domFx_surface_init_planar_common &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domFx_surface_init_volume_common.h b/Engine/lib/collada/include/1.4/dom/domFx_surface_init_volume_common.h new file mode 100644 index 000000000..a002cf78b --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domFx_surface_init_volume_common.h @@ -0,0 +1,256 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domFx_surface_init_volume_common_h__ +#define __domFx_surface_init_volume_common_h__ + +#include +#include +#include + +class DAE; + +class domFx_surface_init_volume_common_complexType +{ +public: + class domAll; + + typedef daeSmartRef domAllRef; + typedef daeTArray domAll_Array; + +/** + * Init the entire surface with one compound image such as DDS + */ + class domAll : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ALL; } + static daeInt ID() { return 13; } + virtual daeInt typeID() const { return ID(); } + protected: // Attribute + xsIDREF attrRef; + + + public: //Accessors and Mutators + /** + * Gets the ref attribute. + * @return Returns a xsIDREF reference of the ref attribute. + */ + xsIDREF &getRef() { return attrRef; } + /** + * Gets the ref attribute. + * @return Returns a constant xsIDREF reference of the ref attribute. + */ + const xsIDREF &getRef() const{ return attrRef; } + /** + * Sets the ref attribute. + * @param atRef The new value for the ref attribute. + */ + void setRef( const xsIDREF &atRef ) { attrRef = atRef; _validAttributeArray[0] = true; } + + protected: + /** + * Constructor + */ + domAll(DAE& dae) : daeElement(dae), attrRef(*this) {} + /** + * Destructor + */ + virtual ~domAll() {} + /** + * Overloaded assignment operator + */ + virtual domAll &operator=( const domAll &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPrimary; + + typedef daeSmartRef domPrimaryRef; + typedef daeTArray domPrimary_Array; + +/** + * Init mip level 0 of the surface with one compound image such as DDS. Use + * of this element expects that the surface has element mip_levels=0 or mipmap_generate. + */ + class domPrimary : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PRIMARY; } + static daeInt ID() { return 14; } + virtual daeInt typeID() const { return ID(); } + protected: // Attribute + xsIDREF attrRef; + + + public: //Accessors and Mutators + /** + * Gets the ref attribute. + * @return Returns a xsIDREF reference of the ref attribute. + */ + xsIDREF &getRef() { return attrRef; } + /** + * Gets the ref attribute. + * @return Returns a constant xsIDREF reference of the ref attribute. + */ + const xsIDREF &getRef() const{ return attrRef; } + /** + * Sets the ref attribute. + * @param atRef The new value for the ref attribute. + */ + void setRef( const xsIDREF &atRef ) { attrRef = atRef; _validAttributeArray[0] = true; } + + protected: + /** + * Constructor + */ + domPrimary(DAE& dae) : daeElement(dae), attrRef(*this) {} + /** + * Destructor + */ + virtual ~domPrimary() {} + /** + * Overloaded assignment operator + */ + virtual domPrimary &operator=( const domPrimary &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements +/** + * Init the entire surface with one compound image such as DDS @see domAll + */ + domAllRef elemAll; +/** + * Init mip level 0 of the surface with one compound image such as DDS. Use + * of this element expects that the surface has element mip_levels=0 or mipmap_generate. + * @see domPrimary + */ + domPrimaryRef elemPrimary; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the all element. + * @return a daeSmartRef to the all element. + */ + const domAllRef getAll() const { return elemAll; } + /** + * Gets the primary element. + * @return a daeSmartRef to the primary element. + */ + const domPrimaryRef getPrimary() const { return elemPrimary; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domFx_surface_init_volume_common_complexType(DAE& dae, daeElement* elt) : elemAll(), elemPrimary() {} + /** + * Destructor + */ + virtual ~domFx_surface_init_volume_common_complexType() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domFx_surface_init_volume_common_complexType &operator=( const domFx_surface_init_volume_common_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domFx_surface_init_volume_common_complexType. + */ +class domFx_surface_init_volume_common : public daeElement, public domFx_surface_init_volume_common_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FX_SURFACE_INIT_VOLUME_COMMON; } + static daeInt ID() { return 15; } + virtual daeInt typeID() const { return ID(); } +protected: + /** + * Constructor + */ + domFx_surface_init_volume_common(DAE& dae) : daeElement(dae), domFx_surface_init_volume_common_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domFx_surface_init_volume_common() {} + /** + * Overloaded assignment operator + */ + virtual domFx_surface_init_volume_common &operator=( const domFx_surface_init_volume_common &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGeometry.h b/Engine/lib/collada/include/1.4/dom/domGeometry.h new file mode 100644 index 000000000..d02d92632 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGeometry.h @@ -0,0 +1,180 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGeometry_h__ +#define __domGeometry_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include +class DAE; + +/** + * Geometry describes the visual shape and appearance of an object in the + * scene. The geometry element categorizes the declaration of geometric information. + * Geometry is a branch of mathematics that deals with the measurement, properties, + * and relationships of points, lines, angles, surfaces, and solids. + */ +class domGeometry : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GEOMETRY; } + static daeInt ID() { return 613; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The geometry element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * The geometry element may contain only one mesh or convex_mesh. @see domConvex_mesh + */ + domConvex_meshRef elemConvex_mesh; +/** + * The geometry element may contain only one mesh or convex_mesh. @see domMesh + */ + domMeshRef elemMesh; + domSplineRef elemSpline; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the convex_mesh element. + * @return a daeSmartRef to the convex_mesh element. + */ + const domConvex_meshRef getConvex_mesh() const { return elemConvex_mesh; } + /** + * Gets the mesh element. + * @return a daeSmartRef to the mesh element. + */ + const domMeshRef getMesh() const { return elemMesh; } + /** + * Gets the spline element. + * @return a daeSmartRef to the spline element. + */ + const domSplineRef getSpline() const { return elemSpline; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domGeometry(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemConvex_mesh(), elemMesh(), elemSpline(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domGeometry() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domGeometry &operator=( const domGeometry &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGl_hook_abstract.h b/Engine/lib/collada/include/1.4/dom/domGl_hook_abstract.h new file mode 100644 index 000000000..a8a02f6de --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGl_hook_abstract.h @@ -0,0 +1,59 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGl_hook_abstract_h__ +#define __domGl_hook_abstract_h__ + +#include +#include +#include + +class DAE; + +class domGl_hook_abstract : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GL_HOOK_ABSTRACT; } + static daeInt ID() { return 729; } + virtual daeInt typeID() const { return ID(); } + +protected: + /** + * Constructor + */ + domGl_hook_abstract(DAE& dae) : daeElement(dae) {} + /** + * Destructor + */ + virtual ~domGl_hook_abstract() {} + /** + * Overloaded assignment operator + */ + virtual domGl_hook_abstract &operator=( const domGl_hook_abstract &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGl_pipeline_settings.h b/Engine/lib/collada/include/1.4/dom/domGl_pipeline_settings.h new file mode 100644 index 000000000..24e584109 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGl_pipeline_settings.h @@ -0,0 +1,10825 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGl_pipeline_settings_h__ +#define __domGl_pipeline_settings_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +class DAE; + +/** + * A group that defines all of the renderstates used for the CG and GLSL profiles. + */ +class domGl_pipeline_settings : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GL_PIPELINE_SETTINGS; } + static daeInt ID() { return 218; } + virtual daeInt typeID() const { return ID(); } +public: + class domAlpha_func; + + typedef daeSmartRef domAlpha_funcRef; + typedef daeTArray domAlpha_func_Array; + + class domAlpha_func : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ALPHA_FUNC; } + static daeInt ID() { return 219; } + virtual daeInt typeID() const { return ID(); } + public: + class domFunc; + + typedef daeSmartRef domFuncRef; + typedef daeTArray domFunc_Array; + + class domFunc : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FUNC; } + static daeInt ID() { return 220; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_func_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_func_type of the value attribute. + */ + domGl_func_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_func_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domFunc(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domFunc() {} + /** + * Overloaded assignment operator + */ + virtual domFunc &operator=( const domFunc &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domValue; + + typedef daeSmartRef domValueRef; + typedef daeTArray domValue_Array; + + class domValue : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::VALUE; } + static daeInt ID() { return 221; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_alpha_value_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_alpha_value_type of the value attribute. + */ + domGl_alpha_value_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_alpha_value_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domValue(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domValue() {} + /** + * Overloaded assignment operator + */ + virtual domValue &operator=( const domValue &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements + domFuncRef elemFunc; + domValueRef elemValue; + + public: //Accessors and Mutators + /** + * Gets the func element. + * @return a daeSmartRef to the func element. + */ + const domFuncRef getFunc() const { return elemFunc; } + /** + * Gets the value element. + * @return a daeSmartRef to the value element. + */ + const domValueRef getValue() const { return elemValue; } + protected: + /** + * Constructor + */ + domAlpha_func(DAE& dae) : daeElement(dae), elemFunc(), elemValue() {} + /** + * Destructor + */ + virtual ~domAlpha_func() {} + /** + * Overloaded assignment operator + */ + virtual domAlpha_func &operator=( const domAlpha_func &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBlend_func; + + typedef daeSmartRef domBlend_funcRef; + typedef daeTArray domBlend_func_Array; + + class domBlend_func : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BLEND_FUNC; } + static daeInt ID() { return 222; } + virtual daeInt typeID() const { return ID(); } + public: + class domSrc; + + typedef daeSmartRef domSrcRef; + typedef daeTArray domSrc_Array; + + class domSrc : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SRC; } + static daeInt ID() { return 223; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_blend_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_blend_type of the value attribute. + */ + domGl_blend_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_blend_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domSrc(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domSrc() {} + /** + * Overloaded assignment operator + */ + virtual domSrc &operator=( const domSrc &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domDest; + + typedef daeSmartRef domDestRef; + typedef daeTArray domDest_Array; + + class domDest : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::DEST; } + static daeInt ID() { return 224; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_blend_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_blend_type of the value attribute. + */ + domGl_blend_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_blend_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domDest(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domDest() {} + /** + * Overloaded assignment operator + */ + virtual domDest &operator=( const domDest &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements + domSrcRef elemSrc; + domDestRef elemDest; + + public: //Accessors and Mutators + /** + * Gets the src element. + * @return a daeSmartRef to the src element. + */ + const domSrcRef getSrc() const { return elemSrc; } + /** + * Gets the dest element. + * @return a daeSmartRef to the dest element. + */ + const domDestRef getDest() const { return elemDest; } + protected: + /** + * Constructor + */ + domBlend_func(DAE& dae) : daeElement(dae), elemSrc(), elemDest() {} + /** + * Destructor + */ + virtual ~domBlend_func() {} + /** + * Overloaded assignment operator + */ + virtual domBlend_func &operator=( const domBlend_func &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBlend_func_separate; + + typedef daeSmartRef domBlend_func_separateRef; + typedef daeTArray domBlend_func_separate_Array; + + class domBlend_func_separate : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BLEND_FUNC_SEPARATE; } + static daeInt ID() { return 225; } + virtual daeInt typeID() const { return ID(); } + public: + class domSrc_rgb; + + typedef daeSmartRef domSrc_rgbRef; + typedef daeTArray domSrc_rgb_Array; + + class domSrc_rgb : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SRC_RGB; } + static daeInt ID() { return 226; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_blend_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_blend_type of the value attribute. + */ + domGl_blend_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_blend_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domSrc_rgb(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domSrc_rgb() {} + /** + * Overloaded assignment operator + */ + virtual domSrc_rgb &operator=( const domSrc_rgb &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domDest_rgb; + + typedef daeSmartRef domDest_rgbRef; + typedef daeTArray domDest_rgb_Array; + + class domDest_rgb : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::DEST_RGB; } + static daeInt ID() { return 227; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_blend_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_blend_type of the value attribute. + */ + domGl_blend_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_blend_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domDest_rgb(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domDest_rgb() {} + /** + * Overloaded assignment operator + */ + virtual domDest_rgb &operator=( const domDest_rgb &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domSrc_alpha; + + typedef daeSmartRef domSrc_alphaRef; + typedef daeTArray domSrc_alpha_Array; + + class domSrc_alpha : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SRC_ALPHA; } + static daeInt ID() { return 228; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_blend_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_blend_type of the value attribute. + */ + domGl_blend_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_blend_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domSrc_alpha(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domSrc_alpha() {} + /** + * Overloaded assignment operator + */ + virtual domSrc_alpha &operator=( const domSrc_alpha &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domDest_alpha; + + typedef daeSmartRef domDest_alphaRef; + typedef daeTArray domDest_alpha_Array; + + class domDest_alpha : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::DEST_ALPHA; } + static daeInt ID() { return 229; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_blend_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_blend_type of the value attribute. + */ + domGl_blend_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_blend_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domDest_alpha(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domDest_alpha() {} + /** + * Overloaded assignment operator + */ + virtual domDest_alpha &operator=( const domDest_alpha &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements + domSrc_rgbRef elemSrc_rgb; + domDest_rgbRef elemDest_rgb; + domSrc_alphaRef elemSrc_alpha; + domDest_alphaRef elemDest_alpha; + + public: //Accessors and Mutators + /** + * Gets the src_rgb element. + * @return a daeSmartRef to the src_rgb element. + */ + const domSrc_rgbRef getSrc_rgb() const { return elemSrc_rgb; } + /** + * Gets the dest_rgb element. + * @return a daeSmartRef to the dest_rgb element. + */ + const domDest_rgbRef getDest_rgb() const { return elemDest_rgb; } + /** + * Gets the src_alpha element. + * @return a daeSmartRef to the src_alpha element. + */ + const domSrc_alphaRef getSrc_alpha() const { return elemSrc_alpha; } + /** + * Gets the dest_alpha element. + * @return a daeSmartRef to the dest_alpha element. + */ + const domDest_alphaRef getDest_alpha() const { return elemDest_alpha; } + protected: + /** + * Constructor + */ + domBlend_func_separate(DAE& dae) : daeElement(dae), elemSrc_rgb(), elemDest_rgb(), elemSrc_alpha(), elemDest_alpha() {} + /** + * Destructor + */ + virtual ~domBlend_func_separate() {} + /** + * Overloaded assignment operator + */ + virtual domBlend_func_separate &operator=( const domBlend_func_separate &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBlend_equation; + + typedef daeSmartRef domBlend_equationRef; + typedef daeTArray domBlend_equation_Array; + + class domBlend_equation : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BLEND_EQUATION; } + static daeInt ID() { return 230; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_blend_equation_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_blend_equation_type of the value attribute. + */ + domGl_blend_equation_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_blend_equation_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domBlend_equation(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domBlend_equation() {} + /** + * Overloaded assignment operator + */ + virtual domBlend_equation &operator=( const domBlend_equation &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBlend_equation_separate; + + typedef daeSmartRef domBlend_equation_separateRef; + typedef daeTArray domBlend_equation_separate_Array; + + class domBlend_equation_separate : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BLEND_EQUATION_SEPARATE; } + static daeInt ID() { return 231; } + virtual daeInt typeID() const { return ID(); } + public: + class domRgb; + + typedef daeSmartRef domRgbRef; + typedef daeTArray domRgb_Array; + + class domRgb : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::RGB; } + static daeInt ID() { return 232; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_blend_equation_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_blend_equation_type of the value attribute. + */ + domGl_blend_equation_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_blend_equation_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domRgb(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domRgb() {} + /** + * Overloaded assignment operator + */ + virtual domRgb &operator=( const domRgb &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domAlpha; + + typedef daeSmartRef domAlphaRef; + typedef daeTArray domAlpha_Array; + + class domAlpha : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ALPHA; } + static daeInt ID() { return 233; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_blend_equation_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_blend_equation_type of the value attribute. + */ + domGl_blend_equation_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_blend_equation_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domAlpha(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domAlpha() {} + /** + * Overloaded assignment operator + */ + virtual domAlpha &operator=( const domAlpha &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements + domRgbRef elemRgb; + domAlphaRef elemAlpha; + + public: //Accessors and Mutators + /** + * Gets the rgb element. + * @return a daeSmartRef to the rgb element. + */ + const domRgbRef getRgb() const { return elemRgb; } + /** + * Gets the alpha element. + * @return a daeSmartRef to the alpha element. + */ + const domAlphaRef getAlpha() const { return elemAlpha; } + protected: + /** + * Constructor + */ + domBlend_equation_separate(DAE& dae) : daeElement(dae), elemRgb(), elemAlpha() {} + /** + * Destructor + */ + virtual ~domBlend_equation_separate() {} + /** + * Overloaded assignment operator + */ + virtual domBlend_equation_separate &operator=( const domBlend_equation_separate &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domColor_material; + + typedef daeSmartRef domColor_materialRef; + typedef daeTArray domColor_material_Array; + + class domColor_material : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::COLOR_MATERIAL; } + static daeInt ID() { return 234; } + virtual daeInt typeID() const { return ID(); } + public: + class domFace; + + typedef daeSmartRef domFaceRef; + typedef daeTArray domFace_Array; + + class domFace : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FACE; } + static daeInt ID() { return 235; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_face_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_face_type of the value attribute. + */ + domGl_face_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_face_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domFace(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domFace() {} + /** + * Overloaded assignment operator + */ + virtual domFace &operator=( const domFace &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMode; + + typedef daeSmartRef domModeRef; + typedef daeTArray domMode_Array; + + class domMode : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MODE; } + static daeInt ID() { return 236; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_material_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_material_type of the value attribute. + */ + domGl_material_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_material_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domMode(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domMode() {} + /** + * Overloaded assignment operator + */ + virtual domMode &operator=( const domMode &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements + domFaceRef elemFace; + domModeRef elemMode; + + public: //Accessors and Mutators + /** + * Gets the face element. + * @return a daeSmartRef to the face element. + */ + const domFaceRef getFace() const { return elemFace; } + /** + * Gets the mode element. + * @return a daeSmartRef to the mode element. + */ + const domModeRef getMode() const { return elemMode; } + protected: + /** + * Constructor + */ + domColor_material(DAE& dae) : daeElement(dae), elemFace(), elemMode() {} + /** + * Destructor + */ + virtual ~domColor_material() {} + /** + * Overloaded assignment operator + */ + virtual domColor_material &operator=( const domColor_material &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domCull_face; + + typedef daeSmartRef domCull_faceRef; + typedef daeTArray domCull_face_Array; + + class domCull_face : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CULL_FACE; } + static daeInt ID() { return 237; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_face_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_face_type of the value attribute. + */ + domGl_face_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_face_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domCull_face(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domCull_face() {} + /** + * Overloaded assignment operator + */ + virtual domCull_face &operator=( const domCull_face &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domDepth_func; + + typedef daeSmartRef domDepth_funcRef; + typedef daeTArray domDepth_func_Array; + + class domDepth_func : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::DEPTH_FUNC; } + static daeInt ID() { return 238; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_func_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_func_type of the value attribute. + */ + domGl_func_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_func_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domDepth_func(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domDepth_func() {} + /** + * Overloaded assignment operator + */ + virtual domDepth_func &operator=( const domDepth_func &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFog_mode; + + typedef daeSmartRef domFog_modeRef; + typedef daeTArray domFog_mode_Array; + + class domFog_mode : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FOG_MODE; } + static daeInt ID() { return 239; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_fog_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_fog_type of the value attribute. + */ + domGl_fog_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_fog_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domFog_mode(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domFog_mode() {} + /** + * Overloaded assignment operator + */ + virtual domFog_mode &operator=( const domFog_mode &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFog_coord_src; + + typedef daeSmartRef domFog_coord_srcRef; + typedef daeTArray domFog_coord_src_Array; + + class domFog_coord_src : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FOG_COORD_SRC; } + static daeInt ID() { return 240; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_fog_coord_src_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_fog_coord_src_type of the value attribute. + */ + domGl_fog_coord_src_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_fog_coord_src_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domFog_coord_src(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domFog_coord_src() {} + /** + * Overloaded assignment operator + */ + virtual domFog_coord_src &operator=( const domFog_coord_src &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFront_face; + + typedef daeSmartRef domFront_faceRef; + typedef daeTArray domFront_face_Array; + + class domFront_face : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FRONT_FACE; } + static daeInt ID() { return 241; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_front_face_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_front_face_type of the value attribute. + */ + domGl_front_face_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_front_face_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domFront_face(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domFront_face() {} + /** + * Overloaded assignment operator + */ + virtual domFront_face &operator=( const domFront_face &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_model_color_control; + + typedef daeSmartRef domLight_model_color_controlRef; + typedef daeTArray domLight_model_color_control_Array; + + class domLight_model_color_control : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_MODEL_COLOR_CONTROL; } + static daeInt ID() { return 242; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_light_model_color_control_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_light_model_color_control_type of the value attribute. + */ + domGl_light_model_color_control_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_light_model_color_control_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domLight_model_color_control(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domLight_model_color_control() {} + /** + * Overloaded assignment operator + */ + virtual domLight_model_color_control &operator=( const domLight_model_color_control &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLogic_op; + + typedef daeSmartRef domLogic_opRef; + typedef daeTArray domLogic_op_Array; + + class domLogic_op : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LOGIC_OP; } + static daeInt ID() { return 243; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_logic_op_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_logic_op_type of the value attribute. + */ + domGl_logic_op_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_logic_op_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domLogic_op(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domLogic_op() {} + /** + * Overloaded assignment operator + */ + virtual domLogic_op &operator=( const domLogic_op &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPolygon_mode; + + typedef daeSmartRef domPolygon_modeRef; + typedef daeTArray domPolygon_mode_Array; + + class domPolygon_mode : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::POLYGON_MODE; } + static daeInt ID() { return 244; } + virtual daeInt typeID() const { return ID(); } + public: + class domFace; + + typedef daeSmartRef domFaceRef; + typedef daeTArray domFace_Array; + + class domFace : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FACE; } + static daeInt ID() { return 245; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_face_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_face_type of the value attribute. + */ + domGl_face_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_face_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domFace(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domFace() {} + /** + * Overloaded assignment operator + */ + virtual domFace &operator=( const domFace &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMode; + + typedef daeSmartRef domModeRef; + typedef daeTArray domMode_Array; + + class domMode : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MODE; } + static daeInt ID() { return 246; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_polygon_mode_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_polygon_mode_type of the value attribute. + */ + domGl_polygon_mode_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_polygon_mode_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domMode(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domMode() {} + /** + * Overloaded assignment operator + */ + virtual domMode &operator=( const domMode &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements + domFaceRef elemFace; + domModeRef elemMode; + + public: //Accessors and Mutators + /** + * Gets the face element. + * @return a daeSmartRef to the face element. + */ + const domFaceRef getFace() const { return elemFace; } + /** + * Gets the mode element. + * @return a daeSmartRef to the mode element. + */ + const domModeRef getMode() const { return elemMode; } + protected: + /** + * Constructor + */ + domPolygon_mode(DAE& dae) : daeElement(dae), elemFace(), elemMode() {} + /** + * Destructor + */ + virtual ~domPolygon_mode() {} + /** + * Overloaded assignment operator + */ + virtual domPolygon_mode &operator=( const domPolygon_mode &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domShade_model; + + typedef daeSmartRef domShade_modelRef; + typedef daeTArray domShade_model_Array; + + class domShade_model : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SHADE_MODEL; } + static daeInt ID() { return 247; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_shade_model_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_shade_model_type of the value attribute. + */ + domGl_shade_model_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_shade_model_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domShade_model(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domShade_model() {} + /** + * Overloaded assignment operator + */ + virtual domShade_model &operator=( const domShade_model &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domStencil_func; + + typedef daeSmartRef domStencil_funcRef; + typedef daeTArray domStencil_func_Array; + + class domStencil_func : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::STENCIL_FUNC; } + static daeInt ID() { return 248; } + virtual daeInt typeID() const { return ID(); } + public: + class domFunc; + + typedef daeSmartRef domFuncRef; + typedef daeTArray domFunc_Array; + + class domFunc : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FUNC; } + static daeInt ID() { return 249; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_func_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_func_type of the value attribute. + */ + domGl_func_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_func_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domFunc(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domFunc() {} + /** + * Overloaded assignment operator + */ + virtual domFunc &operator=( const domFunc &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domRef; + + typedef daeSmartRef domRefRef; + typedef daeTArray domRef_Array; + + class domRef : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::REF; } + static daeInt ID() { return 250; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + xsUnsignedByte attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a xsUnsignedByte of the value attribute. + */ + xsUnsignedByte getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( xsUnsignedByte atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domRef(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domRef() {} + /** + * Overloaded assignment operator + */ + virtual domRef &operator=( const domRef &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMask; + + typedef daeSmartRef domMaskRef; + typedef daeTArray domMask_Array; + + class domMask : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MASK; } + static daeInt ID() { return 251; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + xsUnsignedByte attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a xsUnsignedByte of the value attribute. + */ + xsUnsignedByte getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( xsUnsignedByte atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domMask(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domMask() {} + /** + * Overloaded assignment operator + */ + virtual domMask &operator=( const domMask &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements + domFuncRef elemFunc; + domRefRef elemRef; + domMaskRef elemMask; + + public: //Accessors and Mutators + /** + * Gets the func element. + * @return a daeSmartRef to the func element. + */ + const domFuncRef getFunc() const { return elemFunc; } + /** + * Gets the ref element. + * @return a daeSmartRef to the ref element. + */ + const domRefRef getRef() const { return elemRef; } + /** + * Gets the mask element. + * @return a daeSmartRef to the mask element. + */ + const domMaskRef getMask() const { return elemMask; } + protected: + /** + * Constructor + */ + domStencil_func(DAE& dae) : daeElement(dae), elemFunc(), elemRef(), elemMask() {} + /** + * Destructor + */ + virtual ~domStencil_func() {} + /** + * Overloaded assignment operator + */ + virtual domStencil_func &operator=( const domStencil_func &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domStencil_op; + + typedef daeSmartRef domStencil_opRef; + typedef daeTArray domStencil_op_Array; + + class domStencil_op : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::STENCIL_OP; } + static daeInt ID() { return 252; } + virtual daeInt typeID() const { return ID(); } + public: + class domFail; + + typedef daeSmartRef domFailRef; + typedef daeTArray domFail_Array; + + class domFail : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FAIL; } + static daeInt ID() { return 253; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_stencil_op_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_stencil_op_type of the value attribute. + */ + domGl_stencil_op_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_stencil_op_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domFail(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domFail() {} + /** + * Overloaded assignment operator + */ + virtual domFail &operator=( const domFail &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domZfail; + + typedef daeSmartRef domZfailRef; + typedef daeTArray domZfail_Array; + + class domZfail : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ZFAIL; } + static daeInt ID() { return 254; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_stencil_op_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_stencil_op_type of the value attribute. + */ + domGl_stencil_op_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_stencil_op_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domZfail(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domZfail() {} + /** + * Overloaded assignment operator + */ + virtual domZfail &operator=( const domZfail &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domZpass; + + typedef daeSmartRef domZpassRef; + typedef daeTArray domZpass_Array; + + class domZpass : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ZPASS; } + static daeInt ID() { return 255; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_stencil_op_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_stencil_op_type of the value attribute. + */ + domGl_stencil_op_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_stencil_op_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domZpass(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domZpass() {} + /** + * Overloaded assignment operator + */ + virtual domZpass &operator=( const domZpass &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements + domFailRef elemFail; + domZfailRef elemZfail; + domZpassRef elemZpass; + + public: //Accessors and Mutators + /** + * Gets the fail element. + * @return a daeSmartRef to the fail element. + */ + const domFailRef getFail() const { return elemFail; } + /** + * Gets the zfail element. + * @return a daeSmartRef to the zfail element. + */ + const domZfailRef getZfail() const { return elemZfail; } + /** + * Gets the zpass element. + * @return a daeSmartRef to the zpass element. + */ + const domZpassRef getZpass() const { return elemZpass; } + protected: + /** + * Constructor + */ + domStencil_op(DAE& dae) : daeElement(dae), elemFail(), elemZfail(), elemZpass() {} + /** + * Destructor + */ + virtual ~domStencil_op() {} + /** + * Overloaded assignment operator + */ + virtual domStencil_op &operator=( const domStencil_op &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domStencil_func_separate; + + typedef daeSmartRef domStencil_func_separateRef; + typedef daeTArray domStencil_func_separate_Array; + + class domStencil_func_separate : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::STENCIL_FUNC_SEPARATE; } + static daeInt ID() { return 256; } + virtual daeInt typeID() const { return ID(); } + public: + class domFront; + + typedef daeSmartRef domFrontRef; + typedef daeTArray domFront_Array; + + class domFront : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FRONT; } + static daeInt ID() { return 257; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_func_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_func_type of the value attribute. + */ + domGl_func_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_func_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domFront(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domFront() {} + /** + * Overloaded assignment operator + */ + virtual domFront &operator=( const domFront &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBack; + + typedef daeSmartRef domBackRef; + typedef daeTArray domBack_Array; + + class domBack : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BACK; } + static daeInt ID() { return 258; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_func_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_func_type of the value attribute. + */ + domGl_func_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_func_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domBack(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domBack() {} + /** + * Overloaded assignment operator + */ + virtual domBack &operator=( const domBack &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domRef; + + typedef daeSmartRef domRefRef; + typedef daeTArray domRef_Array; + + class domRef : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::REF; } + static daeInt ID() { return 259; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + xsUnsignedByte attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a xsUnsignedByte of the value attribute. + */ + xsUnsignedByte getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( xsUnsignedByte atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domRef(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domRef() {} + /** + * Overloaded assignment operator + */ + virtual domRef &operator=( const domRef &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMask; + + typedef daeSmartRef domMaskRef; + typedef daeTArray domMask_Array; + + class domMask : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MASK; } + static daeInt ID() { return 260; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + xsUnsignedByte attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a xsUnsignedByte of the value attribute. + */ + xsUnsignedByte getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( xsUnsignedByte atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domMask(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domMask() {} + /** + * Overloaded assignment operator + */ + virtual domMask &operator=( const domMask &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements + domFrontRef elemFront; + domBackRef elemBack; + domRefRef elemRef; + domMaskRef elemMask; + + public: //Accessors and Mutators + /** + * Gets the front element. + * @return a daeSmartRef to the front element. + */ + const domFrontRef getFront() const { return elemFront; } + /** + * Gets the back element. + * @return a daeSmartRef to the back element. + */ + const domBackRef getBack() const { return elemBack; } + /** + * Gets the ref element. + * @return a daeSmartRef to the ref element. + */ + const domRefRef getRef() const { return elemRef; } + /** + * Gets the mask element. + * @return a daeSmartRef to the mask element. + */ + const domMaskRef getMask() const { return elemMask; } + protected: + /** + * Constructor + */ + domStencil_func_separate(DAE& dae) : daeElement(dae), elemFront(), elemBack(), elemRef(), elemMask() {} + /** + * Destructor + */ + virtual ~domStencil_func_separate() {} + /** + * Overloaded assignment operator + */ + virtual domStencil_func_separate &operator=( const domStencil_func_separate &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domStencil_op_separate; + + typedef daeSmartRef domStencil_op_separateRef; + typedef daeTArray domStencil_op_separate_Array; + + class domStencil_op_separate : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::STENCIL_OP_SEPARATE; } + static daeInt ID() { return 261; } + virtual daeInt typeID() const { return ID(); } + public: + class domFace; + + typedef daeSmartRef domFaceRef; + typedef daeTArray domFace_Array; + + class domFace : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FACE; } + static daeInt ID() { return 262; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_face_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_face_type of the value attribute. + */ + domGl_face_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_face_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domFace(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domFace() {} + /** + * Overloaded assignment operator + */ + virtual domFace &operator=( const domFace &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFail; + + typedef daeSmartRef domFailRef; + typedef daeTArray domFail_Array; + + class domFail : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FAIL; } + static daeInt ID() { return 263; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_stencil_op_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_stencil_op_type of the value attribute. + */ + domGl_stencil_op_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_stencil_op_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domFail(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domFail() {} + /** + * Overloaded assignment operator + */ + virtual domFail &operator=( const domFail &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domZfail; + + typedef daeSmartRef domZfailRef; + typedef daeTArray domZfail_Array; + + class domZfail : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ZFAIL; } + static daeInt ID() { return 264; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_stencil_op_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_stencil_op_type of the value attribute. + */ + domGl_stencil_op_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_stencil_op_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domZfail(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domZfail() {} + /** + * Overloaded assignment operator + */ + virtual domZfail &operator=( const domZfail &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domZpass; + + typedef daeSmartRef domZpassRef; + typedef daeTArray domZpass_Array; + + class domZpass : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ZPASS; } + static daeInt ID() { return 265; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_stencil_op_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_stencil_op_type of the value attribute. + */ + domGl_stencil_op_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_stencil_op_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domZpass(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domZpass() {} + /** + * Overloaded assignment operator + */ + virtual domZpass &operator=( const domZpass &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements + domFaceRef elemFace; + domFailRef elemFail; + domZfailRef elemZfail; + domZpassRef elemZpass; + + public: //Accessors and Mutators + /** + * Gets the face element. + * @return a daeSmartRef to the face element. + */ + const domFaceRef getFace() const { return elemFace; } + /** + * Gets the fail element. + * @return a daeSmartRef to the fail element. + */ + const domFailRef getFail() const { return elemFail; } + /** + * Gets the zfail element. + * @return a daeSmartRef to the zfail element. + */ + const domZfailRef getZfail() const { return elemZfail; } + /** + * Gets the zpass element. + * @return a daeSmartRef to the zpass element. + */ + const domZpassRef getZpass() const { return elemZpass; } + protected: + /** + * Constructor + */ + domStencil_op_separate(DAE& dae) : daeElement(dae), elemFace(), elemFail(), elemZfail(), elemZpass() {} + /** + * Destructor + */ + virtual ~domStencil_op_separate() {} + /** + * Overloaded assignment operator + */ + virtual domStencil_op_separate &operator=( const domStencil_op_separate &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domStencil_mask_separate; + + typedef daeSmartRef domStencil_mask_separateRef; + typedef daeTArray domStencil_mask_separate_Array; + + class domStencil_mask_separate : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::STENCIL_MASK_SEPARATE; } + static daeInt ID() { return 266; } + virtual daeInt typeID() const { return ID(); } + public: + class domFace; + + typedef daeSmartRef domFaceRef; + typedef daeTArray domFace_Array; + + class domFace : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FACE; } + static daeInt ID() { return 267; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_face_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_face_type of the value attribute. + */ + domGl_face_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_face_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domFace(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domFace() {} + /** + * Overloaded assignment operator + */ + virtual domFace &operator=( const domFace &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMask; + + typedef daeSmartRef domMaskRef; + typedef daeTArray domMask_Array; + + class domMask : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MASK; } + static daeInt ID() { return 268; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + xsUnsignedByte attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a xsUnsignedByte of the value attribute. + */ + xsUnsignedByte getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( xsUnsignedByte atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domMask(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domMask() {} + /** + * Overloaded assignment operator + */ + virtual domMask &operator=( const domMask &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements + domFaceRef elemFace; + domMaskRef elemMask; + + public: //Accessors and Mutators + /** + * Gets the face element. + * @return a daeSmartRef to the face element. + */ + const domFaceRef getFace() const { return elemFace; } + /** + * Gets the mask element. + * @return a daeSmartRef to the mask element. + */ + const domMaskRef getMask() const { return elemMask; } + protected: + /** + * Constructor + */ + domStencil_mask_separate(DAE& dae) : daeElement(dae), elemFace(), elemMask() {} + /** + * Destructor + */ + virtual ~domStencil_mask_separate() {} + /** + * Overloaded assignment operator + */ + virtual domStencil_mask_separate &operator=( const domStencil_mask_separate &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_enable; + + typedef daeSmartRef domLight_enableRef; + typedef daeTArray domLight_enable_Array; + + class domLight_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_ENABLE; } + static daeInt ID() { return 269; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + domGL_MAX_LIGHTS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGL_MAX_LIGHTS_index of the index attribute. + */ + domGL_MAX_LIGHTS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGL_MAX_LIGHTS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domLight_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domLight_enable() {} + /** + * Overloaded assignment operator + */ + virtual domLight_enable &operator=( const domLight_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_ambient; + + typedef daeSmartRef domLight_ambientRef; + typedef daeTArray domLight_ambient_Array; + + class domLight_ambient : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_AMBIENT; } + static daeInt ID() { return 270; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4 attrValue; + xsNCName attrParam; + domGL_MAX_LIGHTS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4 reference of the value array attribute. + */ + domFloat4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4 reference of the value array attribute. + */ + const domFloat4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGL_MAX_LIGHTS_index of the index attribute. + */ + domGL_MAX_LIGHTS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGL_MAX_LIGHTS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domLight_ambient(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domLight_ambient() {} + /** + * Overloaded assignment operator + */ + virtual domLight_ambient &operator=( const domLight_ambient &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_diffuse; + + typedef daeSmartRef domLight_diffuseRef; + typedef daeTArray domLight_diffuse_Array; + + class domLight_diffuse : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_DIFFUSE; } + static daeInt ID() { return 271; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4 attrValue; + xsNCName attrParam; + domGL_MAX_LIGHTS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4 reference of the value array attribute. + */ + domFloat4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4 reference of the value array attribute. + */ + const domFloat4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGL_MAX_LIGHTS_index of the index attribute. + */ + domGL_MAX_LIGHTS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGL_MAX_LIGHTS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domLight_diffuse(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domLight_diffuse() {} + /** + * Overloaded assignment operator + */ + virtual domLight_diffuse &operator=( const domLight_diffuse &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_specular; + + typedef daeSmartRef domLight_specularRef; + typedef daeTArray domLight_specular_Array; + + class domLight_specular : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_SPECULAR; } + static daeInt ID() { return 272; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4 attrValue; + xsNCName attrParam; + domGL_MAX_LIGHTS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4 reference of the value array attribute. + */ + domFloat4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4 reference of the value array attribute. + */ + const domFloat4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGL_MAX_LIGHTS_index of the index attribute. + */ + domGL_MAX_LIGHTS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGL_MAX_LIGHTS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domLight_specular(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domLight_specular() {} + /** + * Overloaded assignment operator + */ + virtual domLight_specular &operator=( const domLight_specular &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_position; + + typedef daeSmartRef domLight_positionRef; + typedef daeTArray domLight_position_Array; + + class domLight_position : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_POSITION; } + static daeInt ID() { return 273; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4 attrValue; + xsNCName attrParam; + domGL_MAX_LIGHTS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4 reference of the value array attribute. + */ + domFloat4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4 reference of the value array attribute. + */ + const domFloat4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGL_MAX_LIGHTS_index of the index attribute. + */ + domGL_MAX_LIGHTS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGL_MAX_LIGHTS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domLight_position(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domLight_position() {} + /** + * Overloaded assignment operator + */ + virtual domLight_position &operator=( const domLight_position &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_constant_attenuation; + + typedef daeSmartRef domLight_constant_attenuationRef; + typedef daeTArray domLight_constant_attenuation_Array; + + class domLight_constant_attenuation : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_CONSTANT_ATTENUATION; } + static daeInt ID() { return 274; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + domGL_MAX_LIGHTS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGL_MAX_LIGHTS_index of the index attribute. + */ + domGL_MAX_LIGHTS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGL_MAX_LIGHTS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domLight_constant_attenuation(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domLight_constant_attenuation() {} + /** + * Overloaded assignment operator + */ + virtual domLight_constant_attenuation &operator=( const domLight_constant_attenuation &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_linear_attenuation; + + typedef daeSmartRef domLight_linear_attenuationRef; + typedef daeTArray domLight_linear_attenuation_Array; + + class domLight_linear_attenuation : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_LINEAR_ATTENUATION; } + static daeInt ID() { return 275; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + domGL_MAX_LIGHTS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGL_MAX_LIGHTS_index of the index attribute. + */ + domGL_MAX_LIGHTS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGL_MAX_LIGHTS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domLight_linear_attenuation(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domLight_linear_attenuation() {} + /** + * Overloaded assignment operator + */ + virtual domLight_linear_attenuation &operator=( const domLight_linear_attenuation &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_quadratic_attenuation; + + typedef daeSmartRef domLight_quadratic_attenuationRef; + typedef daeTArray domLight_quadratic_attenuation_Array; + + class domLight_quadratic_attenuation : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_QUADRATIC_ATTENUATION; } + static daeInt ID() { return 276; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + domGL_MAX_LIGHTS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGL_MAX_LIGHTS_index of the index attribute. + */ + domGL_MAX_LIGHTS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGL_MAX_LIGHTS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domLight_quadratic_attenuation(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domLight_quadratic_attenuation() {} + /** + * Overloaded assignment operator + */ + virtual domLight_quadratic_attenuation &operator=( const domLight_quadratic_attenuation &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_spot_cutoff; + + typedef daeSmartRef domLight_spot_cutoffRef; + typedef daeTArray domLight_spot_cutoff_Array; + + class domLight_spot_cutoff : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_SPOT_CUTOFF; } + static daeInt ID() { return 277; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + domGL_MAX_LIGHTS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGL_MAX_LIGHTS_index of the index attribute. + */ + domGL_MAX_LIGHTS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGL_MAX_LIGHTS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domLight_spot_cutoff(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domLight_spot_cutoff() {} + /** + * Overloaded assignment operator + */ + virtual domLight_spot_cutoff &operator=( const domLight_spot_cutoff &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_spot_direction; + + typedef daeSmartRef domLight_spot_directionRef; + typedef daeTArray domLight_spot_direction_Array; + + class domLight_spot_direction : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_SPOT_DIRECTION; } + static daeInt ID() { return 278; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat3 attrValue; + xsNCName attrParam; + domGL_MAX_LIGHTS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat3 reference of the value array attribute. + */ + domFloat3 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat3 reference of the value array attribute. + */ + const domFloat3 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat3 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGL_MAX_LIGHTS_index of the index attribute. + */ + domGL_MAX_LIGHTS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGL_MAX_LIGHTS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domLight_spot_direction(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domLight_spot_direction() {} + /** + * Overloaded assignment operator + */ + virtual domLight_spot_direction &operator=( const domLight_spot_direction &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_spot_exponent; + + typedef daeSmartRef domLight_spot_exponentRef; + typedef daeTArray domLight_spot_exponent_Array; + + class domLight_spot_exponent : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_SPOT_EXPONENT; } + static daeInt ID() { return 279; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + domGL_MAX_LIGHTS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGL_MAX_LIGHTS_index of the index attribute. + */ + domGL_MAX_LIGHTS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGL_MAX_LIGHTS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domLight_spot_exponent(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domLight_spot_exponent() {} + /** + * Overloaded assignment operator + */ + virtual domLight_spot_exponent &operator=( const domLight_spot_exponent &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domTexture1D; + + typedef daeSmartRef domTexture1DRef; + typedef daeTArray domTexture1D_Array; + + class domTexture1D : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TEXTURE1D; } + static daeInt ID() { return 280; } + virtual daeInt typeID() const { return ID(); } + public: + class domParam; + + typedef daeSmartRef domParamRef; + typedef daeTArray domParam_Array; + + class domParam : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PARAM; } + static daeInt ID() { return 281; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsNCName value of the text data of this element. + */ + xsNCName _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsNCName of the value. + */ + xsNCName getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNCName val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domParam(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domParam() {} + /** + * Overloaded assignment operator + */ + virtual domParam &operator=( const domParam &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + protected: // Attribute + domGL_MAX_TEXTURE_IMAGE_UNITS_index attrIndex; + + protected: // Elements + domGl_sampler1DRef elemValue; + domParamRef elemParam; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the index attribute. + * @return Returns a domGL_MAX_TEXTURE_IMAGE_UNITS_index of the index attribute. + */ + domGL_MAX_TEXTURE_IMAGE_UNITS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGL_MAX_TEXTURE_IMAGE_UNITS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[0] = true; } + + /** + * Gets the value element. + * @return a daeSmartRef to the value element. + */ + const domGl_sampler1DRef getValue() const { return elemValue; } + /** + * Gets the param element. + * @return a daeSmartRef to the param element. + */ + const domParamRef getParam() const { return elemParam; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domTexture1D(DAE& dae) : daeElement(dae), attrIndex(), elemValue(), elemParam() {} + /** + * Destructor + */ + virtual ~domTexture1D() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domTexture1D &operator=( const domTexture1D &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domTexture2D; + + typedef daeSmartRef domTexture2DRef; + typedef daeTArray domTexture2D_Array; + + class domTexture2D : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TEXTURE2D; } + static daeInt ID() { return 282; } + virtual daeInt typeID() const { return ID(); } + public: + class domParam; + + typedef daeSmartRef domParamRef; + typedef daeTArray domParam_Array; + + class domParam : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PARAM; } + static daeInt ID() { return 283; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsNCName value of the text data of this element. + */ + xsNCName _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsNCName of the value. + */ + xsNCName getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNCName val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domParam(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domParam() {} + /** + * Overloaded assignment operator + */ + virtual domParam &operator=( const domParam &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + protected: // Attribute + domGL_MAX_TEXTURE_IMAGE_UNITS_index attrIndex; + + protected: // Elements + domGl_sampler2DRef elemValue; + domParamRef elemParam; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the index attribute. + * @return Returns a domGL_MAX_TEXTURE_IMAGE_UNITS_index of the index attribute. + */ + domGL_MAX_TEXTURE_IMAGE_UNITS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGL_MAX_TEXTURE_IMAGE_UNITS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[0] = true; } + + /** + * Gets the value element. + * @return a daeSmartRef to the value element. + */ + const domGl_sampler2DRef getValue() const { return elemValue; } + /** + * Gets the param element. + * @return a daeSmartRef to the param element. + */ + const domParamRef getParam() const { return elemParam; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domTexture2D(DAE& dae) : daeElement(dae), attrIndex(), elemValue(), elemParam() {} + /** + * Destructor + */ + virtual ~domTexture2D() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domTexture2D &operator=( const domTexture2D &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domTexture3D; + + typedef daeSmartRef domTexture3DRef; + typedef daeTArray domTexture3D_Array; + + class domTexture3D : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TEXTURE3D; } + static daeInt ID() { return 284; } + virtual daeInt typeID() const { return ID(); } + public: + class domParam; + + typedef daeSmartRef domParamRef; + typedef daeTArray domParam_Array; + + class domParam : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PARAM; } + static daeInt ID() { return 285; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsNCName value of the text data of this element. + */ + xsNCName _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsNCName of the value. + */ + xsNCName getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNCName val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domParam(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domParam() {} + /** + * Overloaded assignment operator + */ + virtual domParam &operator=( const domParam &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + protected: // Attribute + domGL_MAX_TEXTURE_IMAGE_UNITS_index attrIndex; + + protected: // Elements + domGl_sampler3DRef elemValue; + domParamRef elemParam; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the index attribute. + * @return Returns a domGL_MAX_TEXTURE_IMAGE_UNITS_index of the index attribute. + */ + domGL_MAX_TEXTURE_IMAGE_UNITS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGL_MAX_TEXTURE_IMAGE_UNITS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[0] = true; } + + /** + * Gets the value element. + * @return a daeSmartRef to the value element. + */ + const domGl_sampler3DRef getValue() const { return elemValue; } + /** + * Gets the param element. + * @return a daeSmartRef to the param element. + */ + const domParamRef getParam() const { return elemParam; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domTexture3D(DAE& dae) : daeElement(dae), attrIndex(), elemValue(), elemParam() {} + /** + * Destructor + */ + virtual ~domTexture3D() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domTexture3D &operator=( const domTexture3D &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domTextureCUBE; + + typedef daeSmartRef domTextureCUBERef; + typedef daeTArray domTextureCUBE_Array; + + class domTextureCUBE : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TEXTURECUBE; } + static daeInt ID() { return 286; } + virtual daeInt typeID() const { return ID(); } + public: + class domParam; + + typedef daeSmartRef domParamRef; + typedef daeTArray domParam_Array; + + class domParam : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PARAM; } + static daeInt ID() { return 287; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsNCName value of the text data of this element. + */ + xsNCName _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsNCName of the value. + */ + xsNCName getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNCName val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domParam(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domParam() {} + /** + * Overloaded assignment operator + */ + virtual domParam &operator=( const domParam &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + protected: // Attribute + domGL_MAX_TEXTURE_IMAGE_UNITS_index attrIndex; + + protected: // Elements + domGl_samplerCUBERef elemValue; + domParamRef elemParam; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the index attribute. + * @return Returns a domGL_MAX_TEXTURE_IMAGE_UNITS_index of the index attribute. + */ + domGL_MAX_TEXTURE_IMAGE_UNITS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGL_MAX_TEXTURE_IMAGE_UNITS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[0] = true; } + + /** + * Gets the value element. + * @return a daeSmartRef to the value element. + */ + const domGl_samplerCUBERef getValue() const { return elemValue; } + /** + * Gets the param element. + * @return a daeSmartRef to the param element. + */ + const domParamRef getParam() const { return elemParam; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domTextureCUBE(DAE& dae) : daeElement(dae), attrIndex(), elemValue(), elemParam() {} + /** + * Destructor + */ + virtual ~domTextureCUBE() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domTextureCUBE &operator=( const domTextureCUBE &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domTextureRECT; + + typedef daeSmartRef domTextureRECTRef; + typedef daeTArray domTextureRECT_Array; + + class domTextureRECT : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TEXTURERECT; } + static daeInt ID() { return 288; } + virtual daeInt typeID() const { return ID(); } + public: + class domParam; + + typedef daeSmartRef domParamRef; + typedef daeTArray domParam_Array; + + class domParam : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PARAM; } + static daeInt ID() { return 289; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsNCName value of the text data of this element. + */ + xsNCName _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsNCName of the value. + */ + xsNCName getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNCName val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domParam(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domParam() {} + /** + * Overloaded assignment operator + */ + virtual domParam &operator=( const domParam &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + protected: // Attribute + domGL_MAX_TEXTURE_IMAGE_UNITS_index attrIndex; + + protected: // Elements + domGl_samplerRECTRef elemValue; + domParamRef elemParam; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the index attribute. + * @return Returns a domGL_MAX_TEXTURE_IMAGE_UNITS_index of the index attribute. + */ + domGL_MAX_TEXTURE_IMAGE_UNITS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGL_MAX_TEXTURE_IMAGE_UNITS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[0] = true; } + + /** + * Gets the value element. + * @return a daeSmartRef to the value element. + */ + const domGl_samplerRECTRef getValue() const { return elemValue; } + /** + * Gets the param element. + * @return a daeSmartRef to the param element. + */ + const domParamRef getParam() const { return elemParam; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domTextureRECT(DAE& dae) : daeElement(dae), attrIndex(), elemValue(), elemParam() {} + /** + * Destructor + */ + virtual ~domTextureRECT() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domTextureRECT &operator=( const domTextureRECT &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domTextureDEPTH; + + typedef daeSmartRef domTextureDEPTHRef; + typedef daeTArray domTextureDEPTH_Array; + + class domTextureDEPTH : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TEXTUREDEPTH; } + static daeInt ID() { return 290; } + virtual daeInt typeID() const { return ID(); } + public: + class domParam; + + typedef daeSmartRef domParamRef; + typedef daeTArray domParam_Array; + + class domParam : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PARAM; } + static daeInt ID() { return 291; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsNCName value of the text data of this element. + */ + xsNCName _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsNCName of the value. + */ + xsNCName getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNCName val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domParam(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domParam() {} + /** + * Overloaded assignment operator + */ + virtual domParam &operator=( const domParam &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + protected: // Attribute + domGL_MAX_TEXTURE_IMAGE_UNITS_index attrIndex; + + protected: // Elements + domGl_samplerDEPTHRef elemValue; + domParamRef elemParam; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the index attribute. + * @return Returns a domGL_MAX_TEXTURE_IMAGE_UNITS_index of the index attribute. + */ + domGL_MAX_TEXTURE_IMAGE_UNITS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGL_MAX_TEXTURE_IMAGE_UNITS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[0] = true; } + + /** + * Gets the value element. + * @return a daeSmartRef to the value element. + */ + const domGl_samplerDEPTHRef getValue() const { return elemValue; } + /** + * Gets the param element. + * @return a daeSmartRef to the param element. + */ + const domParamRef getParam() const { return elemParam; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domTextureDEPTH(DAE& dae) : daeElement(dae), attrIndex(), elemValue(), elemParam() {} + /** + * Destructor + */ + virtual ~domTextureDEPTH() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domTextureDEPTH &operator=( const domTextureDEPTH &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domTexture1D_enable; + + typedef daeSmartRef domTexture1D_enableRef; + typedef daeTArray domTexture1D_enable_Array; + + class domTexture1D_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TEXTURE1D_ENABLE; } + static daeInt ID() { return 292; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + domGL_MAX_TEXTURE_IMAGE_UNITS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGL_MAX_TEXTURE_IMAGE_UNITS_index of the index attribute. + */ + domGL_MAX_TEXTURE_IMAGE_UNITS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGL_MAX_TEXTURE_IMAGE_UNITS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domTexture1D_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domTexture1D_enable() {} + /** + * Overloaded assignment operator + */ + virtual domTexture1D_enable &operator=( const domTexture1D_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domTexture2D_enable; + + typedef daeSmartRef domTexture2D_enableRef; + typedef daeTArray domTexture2D_enable_Array; + + class domTexture2D_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TEXTURE2D_ENABLE; } + static daeInt ID() { return 293; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + domGL_MAX_TEXTURE_IMAGE_UNITS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGL_MAX_TEXTURE_IMAGE_UNITS_index of the index attribute. + */ + domGL_MAX_TEXTURE_IMAGE_UNITS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGL_MAX_TEXTURE_IMAGE_UNITS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domTexture2D_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domTexture2D_enable() {} + /** + * Overloaded assignment operator + */ + virtual domTexture2D_enable &operator=( const domTexture2D_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domTexture3D_enable; + + typedef daeSmartRef domTexture3D_enableRef; + typedef daeTArray domTexture3D_enable_Array; + + class domTexture3D_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TEXTURE3D_ENABLE; } + static daeInt ID() { return 294; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + domGL_MAX_TEXTURE_IMAGE_UNITS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGL_MAX_TEXTURE_IMAGE_UNITS_index of the index attribute. + */ + domGL_MAX_TEXTURE_IMAGE_UNITS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGL_MAX_TEXTURE_IMAGE_UNITS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domTexture3D_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domTexture3D_enable() {} + /** + * Overloaded assignment operator + */ + virtual domTexture3D_enable &operator=( const domTexture3D_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domTextureCUBE_enable; + + typedef daeSmartRef domTextureCUBE_enableRef; + typedef daeTArray domTextureCUBE_enable_Array; + + class domTextureCUBE_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TEXTURECUBE_ENABLE; } + static daeInt ID() { return 295; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + domGL_MAX_TEXTURE_IMAGE_UNITS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGL_MAX_TEXTURE_IMAGE_UNITS_index of the index attribute. + */ + domGL_MAX_TEXTURE_IMAGE_UNITS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGL_MAX_TEXTURE_IMAGE_UNITS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domTextureCUBE_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domTextureCUBE_enable() {} + /** + * Overloaded assignment operator + */ + virtual domTextureCUBE_enable &operator=( const domTextureCUBE_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domTextureRECT_enable; + + typedef daeSmartRef domTextureRECT_enableRef; + typedef daeTArray domTextureRECT_enable_Array; + + class domTextureRECT_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TEXTURERECT_ENABLE; } + static daeInt ID() { return 296; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + domGL_MAX_TEXTURE_IMAGE_UNITS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGL_MAX_TEXTURE_IMAGE_UNITS_index of the index attribute. + */ + domGL_MAX_TEXTURE_IMAGE_UNITS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGL_MAX_TEXTURE_IMAGE_UNITS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domTextureRECT_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domTextureRECT_enable() {} + /** + * Overloaded assignment operator + */ + virtual domTextureRECT_enable &operator=( const domTextureRECT_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domTextureDEPTH_enable; + + typedef daeSmartRef domTextureDEPTH_enableRef; + typedef daeTArray domTextureDEPTH_enable_Array; + + class domTextureDEPTH_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TEXTUREDEPTH_ENABLE; } + static daeInt ID() { return 297; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + domGL_MAX_TEXTURE_IMAGE_UNITS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGL_MAX_TEXTURE_IMAGE_UNITS_index of the index attribute. + */ + domGL_MAX_TEXTURE_IMAGE_UNITS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGL_MAX_TEXTURE_IMAGE_UNITS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domTextureDEPTH_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domTextureDEPTH_enable() {} + /** + * Overloaded assignment operator + */ + virtual domTextureDEPTH_enable &operator=( const domTextureDEPTH_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domTexture_env_color; + + typedef daeSmartRef domTexture_env_colorRef; + typedef daeTArray domTexture_env_color_Array; + + class domTexture_env_color : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TEXTURE_ENV_COLOR; } + static daeInt ID() { return 298; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4 attrValue; + xsNCName attrParam; + domGL_MAX_TEXTURE_IMAGE_UNITS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4 reference of the value array attribute. + */ + domFloat4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4 reference of the value array attribute. + */ + const domFloat4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGL_MAX_TEXTURE_IMAGE_UNITS_index of the index attribute. + */ + domGL_MAX_TEXTURE_IMAGE_UNITS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGL_MAX_TEXTURE_IMAGE_UNITS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domTexture_env_color(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domTexture_env_color() {} + /** + * Overloaded assignment operator + */ + virtual domTexture_env_color &operator=( const domTexture_env_color &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domTexture_env_mode; + + typedef daeSmartRef domTexture_env_modeRef; + typedef daeTArray domTexture_env_mode_Array; + + class domTexture_env_mode : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TEXTURE_ENV_MODE; } + static daeInt ID() { return 299; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domString attrValue; + xsNCName attrParam; + domGL_MAX_TEXTURE_IMAGE_UNITS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domString of the value attribute. + */ + domString getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domString atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGL_MAX_TEXTURE_IMAGE_UNITS_index of the index attribute. + */ + domGL_MAX_TEXTURE_IMAGE_UNITS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGL_MAX_TEXTURE_IMAGE_UNITS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domTexture_env_mode(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domTexture_env_mode() {} + /** + * Overloaded assignment operator + */ + virtual domTexture_env_mode &operator=( const domTexture_env_mode &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domClip_plane; + + typedef daeSmartRef domClip_planeRef; + typedef daeTArray domClip_plane_Array; + + class domClip_plane : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CLIP_PLANE; } + static daeInt ID() { return 300; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4 attrValue; + xsNCName attrParam; + domGL_MAX_CLIP_PLANES_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4 reference of the value array attribute. + */ + domFloat4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4 reference of the value array attribute. + */ + const domFloat4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGL_MAX_CLIP_PLANES_index of the index attribute. + */ + domGL_MAX_CLIP_PLANES_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGL_MAX_CLIP_PLANES_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domClip_plane(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domClip_plane() {} + /** + * Overloaded assignment operator + */ + virtual domClip_plane &operator=( const domClip_plane &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domClip_plane_enable; + + typedef daeSmartRef domClip_plane_enableRef; + typedef daeTArray domClip_plane_enable_Array; + + class domClip_plane_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CLIP_PLANE_ENABLE; } + static daeInt ID() { return 301; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + domGL_MAX_CLIP_PLANES_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGL_MAX_CLIP_PLANES_index of the index attribute. + */ + domGL_MAX_CLIP_PLANES_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGL_MAX_CLIP_PLANES_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domClip_plane_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domClip_plane_enable() {} + /** + * Overloaded assignment operator + */ + virtual domClip_plane_enable &operator=( const domClip_plane_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBlend_color; + + typedef daeSmartRef domBlend_colorRef; + typedef daeTArray domBlend_color_Array; + + class domBlend_color : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BLEND_COLOR; } + static daeInt ID() { return 302; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4 reference of the value array attribute. + */ + domFloat4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4 reference of the value array attribute. + */ + const domFloat4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domBlend_color(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domBlend_color() {} + /** + * Overloaded assignment operator + */ + virtual domBlend_color &operator=( const domBlend_color &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domClear_color; + + typedef daeSmartRef domClear_colorRef; + typedef daeTArray domClear_color_Array; + + class domClear_color : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CLEAR_COLOR; } + static daeInt ID() { return 303; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4 reference of the value array attribute. + */ + domFloat4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4 reference of the value array attribute. + */ + const domFloat4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domClear_color(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domClear_color() {} + /** + * Overloaded assignment operator + */ + virtual domClear_color &operator=( const domClear_color &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domClear_stencil; + + typedef daeSmartRef domClear_stencilRef; + typedef daeTArray domClear_stencil_Array; + + class domClear_stencil : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CLEAR_STENCIL; } + static daeInt ID() { return 304; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domInt attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domInt of the value attribute. + */ + domInt getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domInt atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domClear_stencil(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domClear_stencil() {} + /** + * Overloaded assignment operator + */ + virtual domClear_stencil &operator=( const domClear_stencil &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domClear_depth; + + typedef daeSmartRef domClear_depthRef; + typedef daeTArray domClear_depth_Array; + + class domClear_depth : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CLEAR_DEPTH; } + static daeInt ID() { return 305; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domClear_depth(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domClear_depth() {} + /** + * Overloaded assignment operator + */ + virtual domClear_depth &operator=( const domClear_depth &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domColor_mask; + + typedef daeSmartRef domColor_maskRef; + typedef daeTArray domColor_mask_Array; + + class domColor_mask : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::COLOR_MASK; } + static daeInt ID() { return 306; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool4 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domBool4 reference of the value array attribute. + */ + domBool4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domBool4 reference of the value array attribute. + */ + const domBool4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domBool4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domColor_mask(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domColor_mask() {} + /** + * Overloaded assignment operator + */ + virtual domColor_mask &operator=( const domColor_mask &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domDepth_bounds; + + typedef daeSmartRef domDepth_boundsRef; + typedef daeTArray domDepth_bounds_Array; + + class domDepth_bounds : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::DEPTH_BOUNDS; } + static daeInt ID() { return 307; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat2 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat2 reference of the value array attribute. + */ + domFloat2 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat2 reference of the value array attribute. + */ + const domFloat2 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat2 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domDepth_bounds(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domDepth_bounds() {} + /** + * Overloaded assignment operator + */ + virtual domDepth_bounds &operator=( const domDepth_bounds &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domDepth_mask; + + typedef daeSmartRef domDepth_maskRef; + typedef daeTArray domDepth_mask_Array; + + class domDepth_mask : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::DEPTH_MASK; } + static daeInt ID() { return 308; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domDepth_mask(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domDepth_mask() {} + /** + * Overloaded assignment operator + */ + virtual domDepth_mask &operator=( const domDepth_mask &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domDepth_range; + + typedef daeSmartRef domDepth_rangeRef; + typedef daeTArray domDepth_range_Array; + + class domDepth_range : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::DEPTH_RANGE; } + static daeInt ID() { return 309; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat2 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat2 reference of the value array attribute. + */ + domFloat2 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat2 reference of the value array attribute. + */ + const domFloat2 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat2 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domDepth_range(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domDepth_range() {} + /** + * Overloaded assignment operator + */ + virtual domDepth_range &operator=( const domDepth_range &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFog_density; + + typedef daeSmartRef domFog_densityRef; + typedef daeTArray domFog_density_Array; + + class domFog_density : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FOG_DENSITY; } + static daeInt ID() { return 310; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domFog_density(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domFog_density() {} + /** + * Overloaded assignment operator + */ + virtual domFog_density &operator=( const domFog_density &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFog_start; + + typedef daeSmartRef domFog_startRef; + typedef daeTArray domFog_start_Array; + + class domFog_start : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FOG_START; } + static daeInt ID() { return 311; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domFog_start(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domFog_start() {} + /** + * Overloaded assignment operator + */ + virtual domFog_start &operator=( const domFog_start &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFog_end; + + typedef daeSmartRef domFog_endRef; + typedef daeTArray domFog_end_Array; + + class domFog_end : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FOG_END; } + static daeInt ID() { return 312; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domFog_end(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domFog_end() {} + /** + * Overloaded assignment operator + */ + virtual domFog_end &operator=( const domFog_end &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFog_color; + + typedef daeSmartRef domFog_colorRef; + typedef daeTArray domFog_color_Array; + + class domFog_color : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FOG_COLOR; } + static daeInt ID() { return 313; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4 reference of the value array attribute. + */ + domFloat4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4 reference of the value array attribute. + */ + const domFloat4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domFog_color(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domFog_color() {} + /** + * Overloaded assignment operator + */ + virtual domFog_color &operator=( const domFog_color &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_model_ambient; + + typedef daeSmartRef domLight_model_ambientRef; + typedef daeTArray domLight_model_ambient_Array; + + class domLight_model_ambient : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_MODEL_AMBIENT; } + static daeInt ID() { return 314; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4 reference of the value array attribute. + */ + domFloat4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4 reference of the value array attribute. + */ + const domFloat4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domLight_model_ambient(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domLight_model_ambient() {} + /** + * Overloaded assignment operator + */ + virtual domLight_model_ambient &operator=( const domLight_model_ambient &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLighting_enable; + + typedef daeSmartRef domLighting_enableRef; + typedef daeTArray domLighting_enable_Array; + + class domLighting_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHTING_ENABLE; } + static daeInt ID() { return 315; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domLighting_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domLighting_enable() {} + /** + * Overloaded assignment operator + */ + virtual domLighting_enable &operator=( const domLighting_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLine_stipple; + + typedef daeSmartRef domLine_stippleRef; + typedef daeTArray domLine_stipple_Array; + + class domLine_stipple : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LINE_STIPPLE; } + static daeInt ID() { return 316; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domInt2 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domInt2 reference of the value array attribute. + */ + domInt2 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domInt2 reference of the value array attribute. + */ + const domInt2 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domInt2 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domLine_stipple(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domLine_stipple() {} + /** + * Overloaded assignment operator + */ + virtual domLine_stipple &operator=( const domLine_stipple &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLine_width; + + typedef daeSmartRef domLine_widthRef; + typedef daeTArray domLine_width_Array; + + class domLine_width : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LINE_WIDTH; } + static daeInt ID() { return 317; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domLine_width(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domLine_width() {} + /** + * Overloaded assignment operator + */ + virtual domLine_width &operator=( const domLine_width &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMaterial_ambient; + + typedef daeSmartRef domMaterial_ambientRef; + typedef daeTArray domMaterial_ambient_Array; + + class domMaterial_ambient : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MATERIAL_AMBIENT; } + static daeInt ID() { return 318; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4 reference of the value array attribute. + */ + domFloat4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4 reference of the value array attribute. + */ + const domFloat4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domMaterial_ambient(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domMaterial_ambient() {} + /** + * Overloaded assignment operator + */ + virtual domMaterial_ambient &operator=( const domMaterial_ambient &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMaterial_diffuse; + + typedef daeSmartRef domMaterial_diffuseRef; + typedef daeTArray domMaterial_diffuse_Array; + + class domMaterial_diffuse : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MATERIAL_DIFFUSE; } + static daeInt ID() { return 319; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4 reference of the value array attribute. + */ + domFloat4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4 reference of the value array attribute. + */ + const domFloat4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domMaterial_diffuse(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domMaterial_diffuse() {} + /** + * Overloaded assignment operator + */ + virtual domMaterial_diffuse &operator=( const domMaterial_diffuse &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMaterial_emission; + + typedef daeSmartRef domMaterial_emissionRef; + typedef daeTArray domMaterial_emission_Array; + + class domMaterial_emission : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MATERIAL_EMISSION; } + static daeInt ID() { return 320; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4 reference of the value array attribute. + */ + domFloat4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4 reference of the value array attribute. + */ + const domFloat4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domMaterial_emission(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domMaterial_emission() {} + /** + * Overloaded assignment operator + */ + virtual domMaterial_emission &operator=( const domMaterial_emission &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMaterial_shininess; + + typedef daeSmartRef domMaterial_shininessRef; + typedef daeTArray domMaterial_shininess_Array; + + class domMaterial_shininess : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MATERIAL_SHININESS; } + static daeInt ID() { return 321; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domMaterial_shininess(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domMaterial_shininess() {} + /** + * Overloaded assignment operator + */ + virtual domMaterial_shininess &operator=( const domMaterial_shininess &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMaterial_specular; + + typedef daeSmartRef domMaterial_specularRef; + typedef daeTArray domMaterial_specular_Array; + + class domMaterial_specular : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MATERIAL_SPECULAR; } + static daeInt ID() { return 322; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4 reference of the value array attribute. + */ + domFloat4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4 reference of the value array attribute. + */ + const domFloat4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domMaterial_specular(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domMaterial_specular() {} + /** + * Overloaded assignment operator + */ + virtual domMaterial_specular &operator=( const domMaterial_specular &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domModel_view_matrix; + + typedef daeSmartRef domModel_view_matrixRef; + typedef daeTArray domModel_view_matrix_Array; + + class domModel_view_matrix : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MODEL_VIEW_MATRIX; } + static daeInt ID() { return 323; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4x4 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4x4 reference of the value array attribute. + */ + domFloat4x4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4x4 reference of the value array attribute. + */ + const domFloat4x4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4x4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domModel_view_matrix(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domModel_view_matrix() {} + /** + * Overloaded assignment operator + */ + virtual domModel_view_matrix &operator=( const domModel_view_matrix &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPoint_distance_attenuation; + + typedef daeSmartRef domPoint_distance_attenuationRef; + typedef daeTArray domPoint_distance_attenuation_Array; + + class domPoint_distance_attenuation : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::POINT_DISTANCE_ATTENUATION; } + static daeInt ID() { return 324; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat3 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat3 reference of the value array attribute. + */ + domFloat3 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat3 reference of the value array attribute. + */ + const domFloat3 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat3 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domPoint_distance_attenuation(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domPoint_distance_attenuation() {} + /** + * Overloaded assignment operator + */ + virtual domPoint_distance_attenuation &operator=( const domPoint_distance_attenuation &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPoint_fade_threshold_size; + + typedef daeSmartRef domPoint_fade_threshold_sizeRef; + typedef daeTArray domPoint_fade_threshold_size_Array; + + class domPoint_fade_threshold_size : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::POINT_FADE_THRESHOLD_SIZE; } + static daeInt ID() { return 325; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domPoint_fade_threshold_size(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domPoint_fade_threshold_size() {} + /** + * Overloaded assignment operator + */ + virtual domPoint_fade_threshold_size &operator=( const domPoint_fade_threshold_size &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPoint_size; + + typedef daeSmartRef domPoint_sizeRef; + typedef daeTArray domPoint_size_Array; + + class domPoint_size : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::POINT_SIZE; } + static daeInt ID() { return 326; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domPoint_size(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domPoint_size() {} + /** + * Overloaded assignment operator + */ + virtual domPoint_size &operator=( const domPoint_size &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPoint_size_min; + + typedef daeSmartRef domPoint_size_minRef; + typedef daeTArray domPoint_size_min_Array; + + class domPoint_size_min : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::POINT_SIZE_MIN; } + static daeInt ID() { return 327; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domPoint_size_min(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domPoint_size_min() {} + /** + * Overloaded assignment operator + */ + virtual domPoint_size_min &operator=( const domPoint_size_min &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPoint_size_max; + + typedef daeSmartRef domPoint_size_maxRef; + typedef daeTArray domPoint_size_max_Array; + + class domPoint_size_max : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::POINT_SIZE_MAX; } + static daeInt ID() { return 328; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domPoint_size_max(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domPoint_size_max() {} + /** + * Overloaded assignment operator + */ + virtual domPoint_size_max &operator=( const domPoint_size_max &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPolygon_offset; + + typedef daeSmartRef domPolygon_offsetRef; + typedef daeTArray domPolygon_offset_Array; + + class domPolygon_offset : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::POLYGON_OFFSET; } + static daeInt ID() { return 329; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat2 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat2 reference of the value array attribute. + */ + domFloat2 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat2 reference of the value array attribute. + */ + const domFloat2 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat2 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domPolygon_offset(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domPolygon_offset() {} + /** + * Overloaded assignment operator + */ + virtual domPolygon_offset &operator=( const domPolygon_offset &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domProjection_matrix; + + typedef daeSmartRef domProjection_matrixRef; + typedef daeTArray domProjection_matrix_Array; + + class domProjection_matrix : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PROJECTION_MATRIX; } + static daeInt ID() { return 330; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4x4 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4x4 reference of the value array attribute. + */ + domFloat4x4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4x4 reference of the value array attribute. + */ + const domFloat4x4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4x4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domProjection_matrix(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domProjection_matrix() {} + /** + * Overloaded assignment operator + */ + virtual domProjection_matrix &operator=( const domProjection_matrix &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domScissor; + + typedef daeSmartRef domScissorRef; + typedef daeTArray domScissor_Array; + + class domScissor : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SCISSOR; } + static daeInt ID() { return 331; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domInt4 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domInt4 reference of the value array attribute. + */ + domInt4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domInt4 reference of the value array attribute. + */ + const domInt4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domInt4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domScissor(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domScissor() {} + /** + * Overloaded assignment operator + */ + virtual domScissor &operator=( const domScissor &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domStencil_mask; + + typedef daeSmartRef domStencil_maskRef; + typedef daeTArray domStencil_mask_Array; + + class domStencil_mask : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::STENCIL_MASK; } + static daeInt ID() { return 332; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domInt attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domInt of the value attribute. + */ + domInt getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domInt atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domStencil_mask(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domStencil_mask() {} + /** + * Overloaded assignment operator + */ + virtual domStencil_mask &operator=( const domStencil_mask &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domAlpha_test_enable; + + typedef daeSmartRef domAlpha_test_enableRef; + typedef daeTArray domAlpha_test_enable_Array; + + class domAlpha_test_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ALPHA_TEST_ENABLE; } + static daeInt ID() { return 333; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domAlpha_test_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domAlpha_test_enable() {} + /** + * Overloaded assignment operator + */ + virtual domAlpha_test_enable &operator=( const domAlpha_test_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domAuto_normal_enable; + + typedef daeSmartRef domAuto_normal_enableRef; + typedef daeTArray domAuto_normal_enable_Array; + + class domAuto_normal_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::AUTO_NORMAL_ENABLE; } + static daeInt ID() { return 334; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domAuto_normal_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domAuto_normal_enable() {} + /** + * Overloaded assignment operator + */ + virtual domAuto_normal_enable &operator=( const domAuto_normal_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBlend_enable; + + typedef daeSmartRef domBlend_enableRef; + typedef daeTArray domBlend_enable_Array; + + class domBlend_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BLEND_ENABLE; } + static daeInt ID() { return 335; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domBlend_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domBlend_enable() {} + /** + * Overloaded assignment operator + */ + virtual domBlend_enable &operator=( const domBlend_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domColor_logic_op_enable; + + typedef daeSmartRef domColor_logic_op_enableRef; + typedef daeTArray domColor_logic_op_enable_Array; + + class domColor_logic_op_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::COLOR_LOGIC_OP_ENABLE; } + static daeInt ID() { return 336; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domColor_logic_op_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domColor_logic_op_enable() {} + /** + * Overloaded assignment operator + */ + virtual domColor_logic_op_enable &operator=( const domColor_logic_op_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domColor_material_enable; + + typedef daeSmartRef domColor_material_enableRef; + typedef daeTArray domColor_material_enable_Array; + + class domColor_material_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::COLOR_MATERIAL_ENABLE; } + static daeInt ID() { return 337; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domColor_material_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domColor_material_enable() {} + /** + * Overloaded assignment operator + */ + virtual domColor_material_enable &operator=( const domColor_material_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domCull_face_enable; + + typedef daeSmartRef domCull_face_enableRef; + typedef daeTArray domCull_face_enable_Array; + + class domCull_face_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CULL_FACE_ENABLE; } + static daeInt ID() { return 338; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domCull_face_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domCull_face_enable() {} + /** + * Overloaded assignment operator + */ + virtual domCull_face_enable &operator=( const domCull_face_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domDepth_bounds_enable; + + typedef daeSmartRef domDepth_bounds_enableRef; + typedef daeTArray domDepth_bounds_enable_Array; + + class domDepth_bounds_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::DEPTH_BOUNDS_ENABLE; } + static daeInt ID() { return 339; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domDepth_bounds_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domDepth_bounds_enable() {} + /** + * Overloaded assignment operator + */ + virtual domDepth_bounds_enable &operator=( const domDepth_bounds_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domDepth_clamp_enable; + + typedef daeSmartRef domDepth_clamp_enableRef; + typedef daeTArray domDepth_clamp_enable_Array; + + class domDepth_clamp_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::DEPTH_CLAMP_ENABLE; } + static daeInt ID() { return 340; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domDepth_clamp_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domDepth_clamp_enable() {} + /** + * Overloaded assignment operator + */ + virtual domDepth_clamp_enable &operator=( const domDepth_clamp_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domDepth_test_enable; + + typedef daeSmartRef domDepth_test_enableRef; + typedef daeTArray domDepth_test_enable_Array; + + class domDepth_test_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::DEPTH_TEST_ENABLE; } + static daeInt ID() { return 341; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domDepth_test_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domDepth_test_enable() {} + /** + * Overloaded assignment operator + */ + virtual domDepth_test_enable &operator=( const domDepth_test_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domDither_enable; + + typedef daeSmartRef domDither_enableRef; + typedef daeTArray domDither_enable_Array; + + class domDither_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::DITHER_ENABLE; } + static daeInt ID() { return 342; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domDither_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domDither_enable() {} + /** + * Overloaded assignment operator + */ + virtual domDither_enable &operator=( const domDither_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFog_enable; + + typedef daeSmartRef domFog_enableRef; + typedef daeTArray domFog_enable_Array; + + class domFog_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FOG_ENABLE; } + static daeInt ID() { return 343; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domFog_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domFog_enable() {} + /** + * Overloaded assignment operator + */ + virtual domFog_enable &operator=( const domFog_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_model_local_viewer_enable; + + typedef daeSmartRef domLight_model_local_viewer_enableRef; + typedef daeTArray domLight_model_local_viewer_enable_Array; + + class domLight_model_local_viewer_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_MODEL_LOCAL_VIEWER_ENABLE; } + static daeInt ID() { return 344; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domLight_model_local_viewer_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domLight_model_local_viewer_enable() {} + /** + * Overloaded assignment operator + */ + virtual domLight_model_local_viewer_enable &operator=( const domLight_model_local_viewer_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_model_two_side_enable; + + typedef daeSmartRef domLight_model_two_side_enableRef; + typedef daeTArray domLight_model_two_side_enable_Array; + + class domLight_model_two_side_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_MODEL_TWO_SIDE_ENABLE; } + static daeInt ID() { return 345; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domLight_model_two_side_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domLight_model_two_side_enable() {} + /** + * Overloaded assignment operator + */ + virtual domLight_model_two_side_enable &operator=( const domLight_model_two_side_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLine_smooth_enable; + + typedef daeSmartRef domLine_smooth_enableRef; + typedef daeTArray domLine_smooth_enable_Array; + + class domLine_smooth_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LINE_SMOOTH_ENABLE; } + static daeInt ID() { return 346; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domLine_smooth_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domLine_smooth_enable() {} + /** + * Overloaded assignment operator + */ + virtual domLine_smooth_enable &operator=( const domLine_smooth_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLine_stipple_enable; + + typedef daeSmartRef domLine_stipple_enableRef; + typedef daeTArray domLine_stipple_enable_Array; + + class domLine_stipple_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LINE_STIPPLE_ENABLE; } + static daeInt ID() { return 347; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domLine_stipple_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domLine_stipple_enable() {} + /** + * Overloaded assignment operator + */ + virtual domLine_stipple_enable &operator=( const domLine_stipple_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLogic_op_enable; + + typedef daeSmartRef domLogic_op_enableRef; + typedef daeTArray domLogic_op_enable_Array; + + class domLogic_op_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LOGIC_OP_ENABLE; } + static daeInt ID() { return 348; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domLogic_op_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domLogic_op_enable() {} + /** + * Overloaded assignment operator + */ + virtual domLogic_op_enable &operator=( const domLogic_op_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMultisample_enable; + + typedef daeSmartRef domMultisample_enableRef; + typedef daeTArray domMultisample_enable_Array; + + class domMultisample_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MULTISAMPLE_ENABLE; } + static daeInt ID() { return 349; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domMultisample_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domMultisample_enable() {} + /** + * Overloaded assignment operator + */ + virtual domMultisample_enable &operator=( const domMultisample_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domNormalize_enable; + + typedef daeSmartRef domNormalize_enableRef; + typedef daeTArray domNormalize_enable_Array; + + class domNormalize_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::NORMALIZE_ENABLE; } + static daeInt ID() { return 350; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domNormalize_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domNormalize_enable() {} + /** + * Overloaded assignment operator + */ + virtual domNormalize_enable &operator=( const domNormalize_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPoint_smooth_enable; + + typedef daeSmartRef domPoint_smooth_enableRef; + typedef daeTArray domPoint_smooth_enable_Array; + + class domPoint_smooth_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::POINT_SMOOTH_ENABLE; } + static daeInt ID() { return 351; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domPoint_smooth_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domPoint_smooth_enable() {} + /** + * Overloaded assignment operator + */ + virtual domPoint_smooth_enable &operator=( const domPoint_smooth_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPolygon_offset_fill_enable; + + typedef daeSmartRef domPolygon_offset_fill_enableRef; + typedef daeTArray domPolygon_offset_fill_enable_Array; + + class domPolygon_offset_fill_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::POLYGON_OFFSET_FILL_ENABLE; } + static daeInt ID() { return 352; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domPolygon_offset_fill_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domPolygon_offset_fill_enable() {} + /** + * Overloaded assignment operator + */ + virtual domPolygon_offset_fill_enable &operator=( const domPolygon_offset_fill_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPolygon_offset_line_enable; + + typedef daeSmartRef domPolygon_offset_line_enableRef; + typedef daeTArray domPolygon_offset_line_enable_Array; + + class domPolygon_offset_line_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::POLYGON_OFFSET_LINE_ENABLE; } + static daeInt ID() { return 353; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domPolygon_offset_line_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domPolygon_offset_line_enable() {} + /** + * Overloaded assignment operator + */ + virtual domPolygon_offset_line_enable &operator=( const domPolygon_offset_line_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPolygon_offset_point_enable; + + typedef daeSmartRef domPolygon_offset_point_enableRef; + typedef daeTArray domPolygon_offset_point_enable_Array; + + class domPolygon_offset_point_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::POLYGON_OFFSET_POINT_ENABLE; } + static daeInt ID() { return 354; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domPolygon_offset_point_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domPolygon_offset_point_enable() {} + /** + * Overloaded assignment operator + */ + virtual domPolygon_offset_point_enable &operator=( const domPolygon_offset_point_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPolygon_smooth_enable; + + typedef daeSmartRef domPolygon_smooth_enableRef; + typedef daeTArray domPolygon_smooth_enable_Array; + + class domPolygon_smooth_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::POLYGON_SMOOTH_ENABLE; } + static daeInt ID() { return 355; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domPolygon_smooth_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domPolygon_smooth_enable() {} + /** + * Overloaded assignment operator + */ + virtual domPolygon_smooth_enable &operator=( const domPolygon_smooth_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPolygon_stipple_enable; + + typedef daeSmartRef domPolygon_stipple_enableRef; + typedef daeTArray domPolygon_stipple_enable_Array; + + class domPolygon_stipple_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::POLYGON_STIPPLE_ENABLE; } + static daeInt ID() { return 356; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domPolygon_stipple_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domPolygon_stipple_enable() {} + /** + * Overloaded assignment operator + */ + virtual domPolygon_stipple_enable &operator=( const domPolygon_stipple_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domRescale_normal_enable; + + typedef daeSmartRef domRescale_normal_enableRef; + typedef daeTArray domRescale_normal_enable_Array; + + class domRescale_normal_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::RESCALE_NORMAL_ENABLE; } + static daeInt ID() { return 357; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domRescale_normal_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domRescale_normal_enable() {} + /** + * Overloaded assignment operator + */ + virtual domRescale_normal_enable &operator=( const domRescale_normal_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domSample_alpha_to_coverage_enable; + + typedef daeSmartRef domSample_alpha_to_coverage_enableRef; + typedef daeTArray domSample_alpha_to_coverage_enable_Array; + + class domSample_alpha_to_coverage_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SAMPLE_ALPHA_TO_COVERAGE_ENABLE; } + static daeInt ID() { return 358; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domSample_alpha_to_coverage_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domSample_alpha_to_coverage_enable() {} + /** + * Overloaded assignment operator + */ + virtual domSample_alpha_to_coverage_enable &operator=( const domSample_alpha_to_coverage_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domSample_alpha_to_one_enable; + + typedef daeSmartRef domSample_alpha_to_one_enableRef; + typedef daeTArray domSample_alpha_to_one_enable_Array; + + class domSample_alpha_to_one_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SAMPLE_ALPHA_TO_ONE_ENABLE; } + static daeInt ID() { return 359; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domSample_alpha_to_one_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domSample_alpha_to_one_enable() {} + /** + * Overloaded assignment operator + */ + virtual domSample_alpha_to_one_enable &operator=( const domSample_alpha_to_one_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domSample_coverage_enable; + + typedef daeSmartRef domSample_coverage_enableRef; + typedef daeTArray domSample_coverage_enable_Array; + + class domSample_coverage_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SAMPLE_COVERAGE_ENABLE; } + static daeInt ID() { return 360; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domSample_coverage_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domSample_coverage_enable() {} + /** + * Overloaded assignment operator + */ + virtual domSample_coverage_enable &operator=( const domSample_coverage_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domScissor_test_enable; + + typedef daeSmartRef domScissor_test_enableRef; + typedef daeTArray domScissor_test_enable_Array; + + class domScissor_test_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SCISSOR_TEST_ENABLE; } + static daeInt ID() { return 361; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domScissor_test_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domScissor_test_enable() {} + /** + * Overloaded assignment operator + */ + virtual domScissor_test_enable &operator=( const domScissor_test_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domStencil_test_enable; + + typedef daeSmartRef domStencil_test_enableRef; + typedef daeTArray domStencil_test_enable_Array; + + class domStencil_test_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::STENCIL_TEST_ENABLE; } + static daeInt ID() { return 362; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domStencil_test_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domStencil_test_enable() {} + /** + * Overloaded assignment operator + */ + virtual domStencil_test_enable &operator=( const domStencil_test_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements + domAlpha_funcRef elemAlpha_func; + domBlend_funcRef elemBlend_func; + domBlend_func_separateRef elemBlend_func_separate; + domBlend_equationRef elemBlend_equation; + domBlend_equation_separateRef elemBlend_equation_separate; + domColor_materialRef elemColor_material; + domCull_faceRef elemCull_face; + domDepth_funcRef elemDepth_func; + domFog_modeRef elemFog_mode; + domFog_coord_srcRef elemFog_coord_src; + domFront_faceRef elemFront_face; + domLight_model_color_controlRef elemLight_model_color_control; + domLogic_opRef elemLogic_op; + domPolygon_modeRef elemPolygon_mode; + domShade_modelRef elemShade_model; + domStencil_funcRef elemStencil_func; + domStencil_opRef elemStencil_op; + domStencil_func_separateRef elemStencil_func_separate; + domStencil_op_separateRef elemStencil_op_separate; + domStencil_mask_separateRef elemStencil_mask_separate; + domLight_enableRef elemLight_enable; + domLight_ambientRef elemLight_ambient; + domLight_diffuseRef elemLight_diffuse; + domLight_specularRef elemLight_specular; + domLight_positionRef elemLight_position; + domLight_constant_attenuationRef elemLight_constant_attenuation; + domLight_linear_attenuationRef elemLight_linear_attenuation; + domLight_quadratic_attenuationRef elemLight_quadratic_attenuation; + domLight_spot_cutoffRef elemLight_spot_cutoff; + domLight_spot_directionRef elemLight_spot_direction; + domLight_spot_exponentRef elemLight_spot_exponent; + domTexture1DRef elemTexture1D; + domTexture2DRef elemTexture2D; + domTexture3DRef elemTexture3D; + domTextureCUBERef elemTextureCUBE; + domTextureRECTRef elemTextureRECT; + domTextureDEPTHRef elemTextureDEPTH; + domTexture1D_enableRef elemTexture1D_enable; + domTexture2D_enableRef elemTexture2D_enable; + domTexture3D_enableRef elemTexture3D_enable; + domTextureCUBE_enableRef elemTextureCUBE_enable; + domTextureRECT_enableRef elemTextureRECT_enable; + domTextureDEPTH_enableRef elemTextureDEPTH_enable; + domTexture_env_colorRef elemTexture_env_color; + domTexture_env_modeRef elemTexture_env_mode; + domClip_planeRef elemClip_plane; + domClip_plane_enableRef elemClip_plane_enable; + domBlend_colorRef elemBlend_color; + domClear_colorRef elemClear_color; + domClear_stencilRef elemClear_stencil; + domClear_depthRef elemClear_depth; + domColor_maskRef elemColor_mask; + domDepth_boundsRef elemDepth_bounds; + domDepth_maskRef elemDepth_mask; + domDepth_rangeRef elemDepth_range; + domFog_densityRef elemFog_density; + domFog_startRef elemFog_start; + domFog_endRef elemFog_end; + domFog_colorRef elemFog_color; + domLight_model_ambientRef elemLight_model_ambient; + domLighting_enableRef elemLighting_enable; + domLine_stippleRef elemLine_stipple; + domLine_widthRef elemLine_width; + domMaterial_ambientRef elemMaterial_ambient; + domMaterial_diffuseRef elemMaterial_diffuse; + domMaterial_emissionRef elemMaterial_emission; + domMaterial_shininessRef elemMaterial_shininess; + domMaterial_specularRef elemMaterial_specular; + domModel_view_matrixRef elemModel_view_matrix; + domPoint_distance_attenuationRef elemPoint_distance_attenuation; + domPoint_fade_threshold_sizeRef elemPoint_fade_threshold_size; + domPoint_sizeRef elemPoint_size; + domPoint_size_minRef elemPoint_size_min; + domPoint_size_maxRef elemPoint_size_max; + domPolygon_offsetRef elemPolygon_offset; + domProjection_matrixRef elemProjection_matrix; + domScissorRef elemScissor; + domStencil_maskRef elemStencil_mask; + domAlpha_test_enableRef elemAlpha_test_enable; + domAuto_normal_enableRef elemAuto_normal_enable; + domBlend_enableRef elemBlend_enable; + domColor_logic_op_enableRef elemColor_logic_op_enable; + domColor_material_enableRef elemColor_material_enable; + domCull_face_enableRef elemCull_face_enable; + domDepth_bounds_enableRef elemDepth_bounds_enable; + domDepth_clamp_enableRef elemDepth_clamp_enable; + domDepth_test_enableRef elemDepth_test_enable; + domDither_enableRef elemDither_enable; + domFog_enableRef elemFog_enable; + domLight_model_local_viewer_enableRef elemLight_model_local_viewer_enable; + domLight_model_two_side_enableRef elemLight_model_two_side_enable; + domLine_smooth_enableRef elemLine_smooth_enable; + domLine_stipple_enableRef elemLine_stipple_enable; + domLogic_op_enableRef elemLogic_op_enable; + domMultisample_enableRef elemMultisample_enable; + domNormalize_enableRef elemNormalize_enable; + domPoint_smooth_enableRef elemPoint_smooth_enable; + domPolygon_offset_fill_enableRef elemPolygon_offset_fill_enable; + domPolygon_offset_line_enableRef elemPolygon_offset_line_enable; + domPolygon_offset_point_enableRef elemPolygon_offset_point_enable; + domPolygon_smooth_enableRef elemPolygon_smooth_enable; + domPolygon_stipple_enableRef elemPolygon_stipple_enable; + domRescale_normal_enableRef elemRescale_normal_enable; + domSample_alpha_to_coverage_enableRef elemSample_alpha_to_coverage_enable; + domSample_alpha_to_one_enableRef elemSample_alpha_to_one_enable; + domSample_coverage_enableRef elemSample_coverage_enable; + domScissor_test_enableRef elemScissor_test_enable; + domStencil_test_enableRef elemStencil_test_enable; + domGl_hook_abstractRef elemGl_hook_abstract; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the alpha_func element. + * @return a daeSmartRef to the alpha_func element. + */ + const domAlpha_funcRef getAlpha_func() const { return elemAlpha_func; } + /** + * Gets the blend_func element. + * @return a daeSmartRef to the blend_func element. + */ + const domBlend_funcRef getBlend_func() const { return elemBlend_func; } + /** + * Gets the blend_func_separate element. + * @return a daeSmartRef to the blend_func_separate element. + */ + const domBlend_func_separateRef getBlend_func_separate() const { return elemBlend_func_separate; } + /** + * Gets the blend_equation element. + * @return a daeSmartRef to the blend_equation element. + */ + const domBlend_equationRef getBlend_equation() const { return elemBlend_equation; } + /** + * Gets the blend_equation_separate element. + * @return a daeSmartRef to the blend_equation_separate element. + */ + const domBlend_equation_separateRef getBlend_equation_separate() const { return elemBlend_equation_separate; } + /** + * Gets the color_material element. + * @return a daeSmartRef to the color_material element. + */ + const domColor_materialRef getColor_material() const { return elemColor_material; } + /** + * Gets the cull_face element. + * @return a daeSmartRef to the cull_face element. + */ + const domCull_faceRef getCull_face() const { return elemCull_face; } + /** + * Gets the depth_func element. + * @return a daeSmartRef to the depth_func element. + */ + const domDepth_funcRef getDepth_func() const { return elemDepth_func; } + /** + * Gets the fog_mode element. + * @return a daeSmartRef to the fog_mode element. + */ + const domFog_modeRef getFog_mode() const { return elemFog_mode; } + /** + * Gets the fog_coord_src element. + * @return a daeSmartRef to the fog_coord_src element. + */ + const domFog_coord_srcRef getFog_coord_src() const { return elemFog_coord_src; } + /** + * Gets the front_face element. + * @return a daeSmartRef to the front_face element. + */ + const domFront_faceRef getFront_face() const { return elemFront_face; } + /** + * Gets the light_model_color_control element. + * @return a daeSmartRef to the light_model_color_control element. + */ + const domLight_model_color_controlRef getLight_model_color_control() const { return elemLight_model_color_control; } + /** + * Gets the logic_op element. + * @return a daeSmartRef to the logic_op element. + */ + const domLogic_opRef getLogic_op() const { return elemLogic_op; } + /** + * Gets the polygon_mode element. + * @return a daeSmartRef to the polygon_mode element. + */ + const domPolygon_modeRef getPolygon_mode() const { return elemPolygon_mode; } + /** + * Gets the shade_model element. + * @return a daeSmartRef to the shade_model element. + */ + const domShade_modelRef getShade_model() const { return elemShade_model; } + /** + * Gets the stencil_func element. + * @return a daeSmartRef to the stencil_func element. + */ + const domStencil_funcRef getStencil_func() const { return elemStencil_func; } + /** + * Gets the stencil_op element. + * @return a daeSmartRef to the stencil_op element. + */ + const domStencil_opRef getStencil_op() const { return elemStencil_op; } + /** + * Gets the stencil_func_separate element. + * @return a daeSmartRef to the stencil_func_separate element. + */ + const domStencil_func_separateRef getStencil_func_separate() const { return elemStencil_func_separate; } + /** + * Gets the stencil_op_separate element. + * @return a daeSmartRef to the stencil_op_separate element. + */ + const domStencil_op_separateRef getStencil_op_separate() const { return elemStencil_op_separate; } + /** + * Gets the stencil_mask_separate element. + * @return a daeSmartRef to the stencil_mask_separate element. + */ + const domStencil_mask_separateRef getStencil_mask_separate() const { return elemStencil_mask_separate; } + /** + * Gets the light_enable element. + * @return a daeSmartRef to the light_enable element. + */ + const domLight_enableRef getLight_enable() const { return elemLight_enable; } + /** + * Gets the light_ambient element. + * @return a daeSmartRef to the light_ambient element. + */ + const domLight_ambientRef getLight_ambient() const { return elemLight_ambient; } + /** + * Gets the light_diffuse element. + * @return a daeSmartRef to the light_diffuse element. + */ + const domLight_diffuseRef getLight_diffuse() const { return elemLight_diffuse; } + /** + * Gets the light_specular element. + * @return a daeSmartRef to the light_specular element. + */ + const domLight_specularRef getLight_specular() const { return elemLight_specular; } + /** + * Gets the light_position element. + * @return a daeSmartRef to the light_position element. + */ + const domLight_positionRef getLight_position() const { return elemLight_position; } + /** + * Gets the light_constant_attenuation element. + * @return a daeSmartRef to the light_constant_attenuation element. + */ + const domLight_constant_attenuationRef getLight_constant_attenuation() const { return elemLight_constant_attenuation; } + /** + * Gets the light_linear_attenuation element. + * @return a daeSmartRef to the light_linear_attenuation element. + */ + const domLight_linear_attenuationRef getLight_linear_attenuation() const { return elemLight_linear_attenuation; } + /** + * Gets the light_quadratic_attenuation element. + * @return a daeSmartRef to the light_quadratic_attenuation element. + */ + const domLight_quadratic_attenuationRef getLight_quadratic_attenuation() const { return elemLight_quadratic_attenuation; } + /** + * Gets the light_spot_cutoff element. + * @return a daeSmartRef to the light_spot_cutoff element. + */ + const domLight_spot_cutoffRef getLight_spot_cutoff() const { return elemLight_spot_cutoff; } + /** + * Gets the light_spot_direction element. + * @return a daeSmartRef to the light_spot_direction element. + */ + const domLight_spot_directionRef getLight_spot_direction() const { return elemLight_spot_direction; } + /** + * Gets the light_spot_exponent element. + * @return a daeSmartRef to the light_spot_exponent element. + */ + const domLight_spot_exponentRef getLight_spot_exponent() const { return elemLight_spot_exponent; } + /** + * Gets the texture1D element. + * @return a daeSmartRef to the texture1D element. + */ + const domTexture1DRef getTexture1D() const { return elemTexture1D; } + /** + * Gets the texture2D element. + * @return a daeSmartRef to the texture2D element. + */ + const domTexture2DRef getTexture2D() const { return elemTexture2D; } + /** + * Gets the texture3D element. + * @return a daeSmartRef to the texture3D element. + */ + const domTexture3DRef getTexture3D() const { return elemTexture3D; } + /** + * Gets the textureCUBE element. + * @return a daeSmartRef to the textureCUBE element. + */ + const domTextureCUBERef getTextureCUBE() const { return elemTextureCUBE; } + /** + * Gets the textureRECT element. + * @return a daeSmartRef to the textureRECT element. + */ + const domTextureRECTRef getTextureRECT() const { return elemTextureRECT; } + /** + * Gets the textureDEPTH element. + * @return a daeSmartRef to the textureDEPTH element. + */ + const domTextureDEPTHRef getTextureDEPTH() const { return elemTextureDEPTH; } + /** + * Gets the texture1D_enable element. + * @return a daeSmartRef to the texture1D_enable element. + */ + const domTexture1D_enableRef getTexture1D_enable() const { return elemTexture1D_enable; } + /** + * Gets the texture2D_enable element. + * @return a daeSmartRef to the texture2D_enable element. + */ + const domTexture2D_enableRef getTexture2D_enable() const { return elemTexture2D_enable; } + /** + * Gets the texture3D_enable element. + * @return a daeSmartRef to the texture3D_enable element. + */ + const domTexture3D_enableRef getTexture3D_enable() const { return elemTexture3D_enable; } + /** + * Gets the textureCUBE_enable element. + * @return a daeSmartRef to the textureCUBE_enable element. + */ + const domTextureCUBE_enableRef getTextureCUBE_enable() const { return elemTextureCUBE_enable; } + /** + * Gets the textureRECT_enable element. + * @return a daeSmartRef to the textureRECT_enable element. + */ + const domTextureRECT_enableRef getTextureRECT_enable() const { return elemTextureRECT_enable; } + /** + * Gets the textureDEPTH_enable element. + * @return a daeSmartRef to the textureDEPTH_enable element. + */ + const domTextureDEPTH_enableRef getTextureDEPTH_enable() const { return elemTextureDEPTH_enable; } + /** + * Gets the texture_env_color element. + * @return a daeSmartRef to the texture_env_color element. + */ + const domTexture_env_colorRef getTexture_env_color() const { return elemTexture_env_color; } + /** + * Gets the texture_env_mode element. + * @return a daeSmartRef to the texture_env_mode element. + */ + const domTexture_env_modeRef getTexture_env_mode() const { return elemTexture_env_mode; } + /** + * Gets the clip_plane element. + * @return a daeSmartRef to the clip_plane element. + */ + const domClip_planeRef getClip_plane() const { return elemClip_plane; } + /** + * Gets the clip_plane_enable element. + * @return a daeSmartRef to the clip_plane_enable element. + */ + const domClip_plane_enableRef getClip_plane_enable() const { return elemClip_plane_enable; } + /** + * Gets the blend_color element. + * @return a daeSmartRef to the blend_color element. + */ + const domBlend_colorRef getBlend_color() const { return elemBlend_color; } + /** + * Gets the clear_color element. + * @return a daeSmartRef to the clear_color element. + */ + const domClear_colorRef getClear_color() const { return elemClear_color; } + /** + * Gets the clear_stencil element. + * @return a daeSmartRef to the clear_stencil element. + */ + const domClear_stencilRef getClear_stencil() const { return elemClear_stencil; } + /** + * Gets the clear_depth element. + * @return a daeSmartRef to the clear_depth element. + */ + const domClear_depthRef getClear_depth() const { return elemClear_depth; } + /** + * Gets the color_mask element. + * @return a daeSmartRef to the color_mask element. + */ + const domColor_maskRef getColor_mask() const { return elemColor_mask; } + /** + * Gets the depth_bounds element. + * @return a daeSmartRef to the depth_bounds element. + */ + const domDepth_boundsRef getDepth_bounds() const { return elemDepth_bounds; } + /** + * Gets the depth_mask element. + * @return a daeSmartRef to the depth_mask element. + */ + const domDepth_maskRef getDepth_mask() const { return elemDepth_mask; } + /** + * Gets the depth_range element. + * @return a daeSmartRef to the depth_range element. + */ + const domDepth_rangeRef getDepth_range() const { return elemDepth_range; } + /** + * Gets the fog_density element. + * @return a daeSmartRef to the fog_density element. + */ + const domFog_densityRef getFog_density() const { return elemFog_density; } + /** + * Gets the fog_start element. + * @return a daeSmartRef to the fog_start element. + */ + const domFog_startRef getFog_start() const { return elemFog_start; } + /** + * Gets the fog_end element. + * @return a daeSmartRef to the fog_end element. + */ + const domFog_endRef getFog_end() const { return elemFog_end; } + /** + * Gets the fog_color element. + * @return a daeSmartRef to the fog_color element. + */ + const domFog_colorRef getFog_color() const { return elemFog_color; } + /** + * Gets the light_model_ambient element. + * @return a daeSmartRef to the light_model_ambient element. + */ + const domLight_model_ambientRef getLight_model_ambient() const { return elemLight_model_ambient; } + /** + * Gets the lighting_enable element. + * @return a daeSmartRef to the lighting_enable element. + */ + const domLighting_enableRef getLighting_enable() const { return elemLighting_enable; } + /** + * Gets the line_stipple element. + * @return a daeSmartRef to the line_stipple element. + */ + const domLine_stippleRef getLine_stipple() const { return elemLine_stipple; } + /** + * Gets the line_width element. + * @return a daeSmartRef to the line_width element. + */ + const domLine_widthRef getLine_width() const { return elemLine_width; } + /** + * Gets the material_ambient element. + * @return a daeSmartRef to the material_ambient element. + */ + const domMaterial_ambientRef getMaterial_ambient() const { return elemMaterial_ambient; } + /** + * Gets the material_diffuse element. + * @return a daeSmartRef to the material_diffuse element. + */ + const domMaterial_diffuseRef getMaterial_diffuse() const { return elemMaterial_diffuse; } + /** + * Gets the material_emission element. + * @return a daeSmartRef to the material_emission element. + */ + const domMaterial_emissionRef getMaterial_emission() const { return elemMaterial_emission; } + /** + * Gets the material_shininess element. + * @return a daeSmartRef to the material_shininess element. + */ + const domMaterial_shininessRef getMaterial_shininess() const { return elemMaterial_shininess; } + /** + * Gets the material_specular element. + * @return a daeSmartRef to the material_specular element. + */ + const domMaterial_specularRef getMaterial_specular() const { return elemMaterial_specular; } + /** + * Gets the model_view_matrix element. + * @return a daeSmartRef to the model_view_matrix element. + */ + const domModel_view_matrixRef getModel_view_matrix() const { return elemModel_view_matrix; } + /** + * Gets the point_distance_attenuation element. + * @return a daeSmartRef to the point_distance_attenuation element. + */ + const domPoint_distance_attenuationRef getPoint_distance_attenuation() const { return elemPoint_distance_attenuation; } + /** + * Gets the point_fade_threshold_size element. + * @return a daeSmartRef to the point_fade_threshold_size element. + */ + const domPoint_fade_threshold_sizeRef getPoint_fade_threshold_size() const { return elemPoint_fade_threshold_size; } + /** + * Gets the point_size element. + * @return a daeSmartRef to the point_size element. + */ + const domPoint_sizeRef getPoint_size() const { return elemPoint_size; } + /** + * Gets the point_size_min element. + * @return a daeSmartRef to the point_size_min element. + */ + const domPoint_size_minRef getPoint_size_min() const { return elemPoint_size_min; } + /** + * Gets the point_size_max element. + * @return a daeSmartRef to the point_size_max element. + */ + const domPoint_size_maxRef getPoint_size_max() const { return elemPoint_size_max; } + /** + * Gets the polygon_offset element. + * @return a daeSmartRef to the polygon_offset element. + */ + const domPolygon_offsetRef getPolygon_offset() const { return elemPolygon_offset; } + /** + * Gets the projection_matrix element. + * @return a daeSmartRef to the projection_matrix element. + */ + const domProjection_matrixRef getProjection_matrix() const { return elemProjection_matrix; } + /** + * Gets the scissor element. + * @return a daeSmartRef to the scissor element. + */ + const domScissorRef getScissor() const { return elemScissor; } + /** + * Gets the stencil_mask element. + * @return a daeSmartRef to the stencil_mask element. + */ + const domStencil_maskRef getStencil_mask() const { return elemStencil_mask; } + /** + * Gets the alpha_test_enable element. + * @return a daeSmartRef to the alpha_test_enable element. + */ + const domAlpha_test_enableRef getAlpha_test_enable() const { return elemAlpha_test_enable; } + /** + * Gets the auto_normal_enable element. + * @return a daeSmartRef to the auto_normal_enable element. + */ + const domAuto_normal_enableRef getAuto_normal_enable() const { return elemAuto_normal_enable; } + /** + * Gets the blend_enable element. + * @return a daeSmartRef to the blend_enable element. + */ + const domBlend_enableRef getBlend_enable() const { return elemBlend_enable; } + /** + * Gets the color_logic_op_enable element. + * @return a daeSmartRef to the color_logic_op_enable element. + */ + const domColor_logic_op_enableRef getColor_logic_op_enable() const { return elemColor_logic_op_enable; } + /** + * Gets the color_material_enable element. + * @return a daeSmartRef to the color_material_enable element. + */ + const domColor_material_enableRef getColor_material_enable() const { return elemColor_material_enable; } + /** + * Gets the cull_face_enable element. + * @return a daeSmartRef to the cull_face_enable element. + */ + const domCull_face_enableRef getCull_face_enable() const { return elemCull_face_enable; } + /** + * Gets the depth_bounds_enable element. + * @return a daeSmartRef to the depth_bounds_enable element. + */ + const domDepth_bounds_enableRef getDepth_bounds_enable() const { return elemDepth_bounds_enable; } + /** + * Gets the depth_clamp_enable element. + * @return a daeSmartRef to the depth_clamp_enable element. + */ + const domDepth_clamp_enableRef getDepth_clamp_enable() const { return elemDepth_clamp_enable; } + /** + * Gets the depth_test_enable element. + * @return a daeSmartRef to the depth_test_enable element. + */ + const domDepth_test_enableRef getDepth_test_enable() const { return elemDepth_test_enable; } + /** + * Gets the dither_enable element. + * @return a daeSmartRef to the dither_enable element. + */ + const domDither_enableRef getDither_enable() const { return elemDither_enable; } + /** + * Gets the fog_enable element. + * @return a daeSmartRef to the fog_enable element. + */ + const domFog_enableRef getFog_enable() const { return elemFog_enable; } + /** + * Gets the light_model_local_viewer_enable element. + * @return a daeSmartRef to the light_model_local_viewer_enable element. + */ + const domLight_model_local_viewer_enableRef getLight_model_local_viewer_enable() const { return elemLight_model_local_viewer_enable; } + /** + * Gets the light_model_two_side_enable element. + * @return a daeSmartRef to the light_model_two_side_enable element. + */ + const domLight_model_two_side_enableRef getLight_model_two_side_enable() const { return elemLight_model_two_side_enable; } + /** + * Gets the line_smooth_enable element. + * @return a daeSmartRef to the line_smooth_enable element. + */ + const domLine_smooth_enableRef getLine_smooth_enable() const { return elemLine_smooth_enable; } + /** + * Gets the line_stipple_enable element. + * @return a daeSmartRef to the line_stipple_enable element. + */ + const domLine_stipple_enableRef getLine_stipple_enable() const { return elemLine_stipple_enable; } + /** + * Gets the logic_op_enable element. + * @return a daeSmartRef to the logic_op_enable element. + */ + const domLogic_op_enableRef getLogic_op_enable() const { return elemLogic_op_enable; } + /** + * Gets the multisample_enable element. + * @return a daeSmartRef to the multisample_enable element. + */ + const domMultisample_enableRef getMultisample_enable() const { return elemMultisample_enable; } + /** + * Gets the normalize_enable element. + * @return a daeSmartRef to the normalize_enable element. + */ + const domNormalize_enableRef getNormalize_enable() const { return elemNormalize_enable; } + /** + * Gets the point_smooth_enable element. + * @return a daeSmartRef to the point_smooth_enable element. + */ + const domPoint_smooth_enableRef getPoint_smooth_enable() const { return elemPoint_smooth_enable; } + /** + * Gets the polygon_offset_fill_enable element. + * @return a daeSmartRef to the polygon_offset_fill_enable element. + */ + const domPolygon_offset_fill_enableRef getPolygon_offset_fill_enable() const { return elemPolygon_offset_fill_enable; } + /** + * Gets the polygon_offset_line_enable element. + * @return a daeSmartRef to the polygon_offset_line_enable element. + */ + const domPolygon_offset_line_enableRef getPolygon_offset_line_enable() const { return elemPolygon_offset_line_enable; } + /** + * Gets the polygon_offset_point_enable element. + * @return a daeSmartRef to the polygon_offset_point_enable element. + */ + const domPolygon_offset_point_enableRef getPolygon_offset_point_enable() const { return elemPolygon_offset_point_enable; } + /** + * Gets the polygon_smooth_enable element. + * @return a daeSmartRef to the polygon_smooth_enable element. + */ + const domPolygon_smooth_enableRef getPolygon_smooth_enable() const { return elemPolygon_smooth_enable; } + /** + * Gets the polygon_stipple_enable element. + * @return a daeSmartRef to the polygon_stipple_enable element. + */ + const domPolygon_stipple_enableRef getPolygon_stipple_enable() const { return elemPolygon_stipple_enable; } + /** + * Gets the rescale_normal_enable element. + * @return a daeSmartRef to the rescale_normal_enable element. + */ + const domRescale_normal_enableRef getRescale_normal_enable() const { return elemRescale_normal_enable; } + /** + * Gets the sample_alpha_to_coverage_enable element. + * @return a daeSmartRef to the sample_alpha_to_coverage_enable element. + */ + const domSample_alpha_to_coverage_enableRef getSample_alpha_to_coverage_enable() const { return elemSample_alpha_to_coverage_enable; } + /** + * Gets the sample_alpha_to_one_enable element. + * @return a daeSmartRef to the sample_alpha_to_one_enable element. + */ + const domSample_alpha_to_one_enableRef getSample_alpha_to_one_enable() const { return elemSample_alpha_to_one_enable; } + /** + * Gets the sample_coverage_enable element. + * @return a daeSmartRef to the sample_coverage_enable element. + */ + const domSample_coverage_enableRef getSample_coverage_enable() const { return elemSample_coverage_enable; } + /** + * Gets the scissor_test_enable element. + * @return a daeSmartRef to the scissor_test_enable element. + */ + const domScissor_test_enableRef getScissor_test_enable() const { return elemScissor_test_enable; } + /** + * Gets the stencil_test_enable element. + * @return a daeSmartRef to the stencil_test_enable element. + */ + const domStencil_test_enableRef getStencil_test_enable() const { return elemStencil_test_enable; } + /** + * Gets the gl_hook_abstract element. + * @return a daeSmartRef to the gl_hook_abstract element. + */ + const domGl_hook_abstractRef getGl_hook_abstract() const { return elemGl_hook_abstract; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domGl_pipeline_settings(DAE& dae) : daeElement(dae), elemAlpha_func(), elemBlend_func(), elemBlend_func_separate(), elemBlend_equation(), elemBlend_equation_separate(), elemColor_material(), elemCull_face(), elemDepth_func(), elemFog_mode(), elemFog_coord_src(), elemFront_face(), elemLight_model_color_control(), elemLogic_op(), elemPolygon_mode(), elemShade_model(), elemStencil_func(), elemStencil_op(), elemStencil_func_separate(), elemStencil_op_separate(), elemStencil_mask_separate(), elemLight_enable(), elemLight_ambient(), elemLight_diffuse(), elemLight_specular(), elemLight_position(), elemLight_constant_attenuation(), elemLight_linear_attenuation(), elemLight_quadratic_attenuation(), elemLight_spot_cutoff(), elemLight_spot_direction(), elemLight_spot_exponent(), elemTexture1D(), elemTexture2D(), elemTexture3D(), elemTextureCUBE(), elemTextureRECT(), elemTextureDEPTH(), elemTexture1D_enable(), elemTexture2D_enable(), elemTexture3D_enable(), elemTextureCUBE_enable(), elemTextureRECT_enable(), elemTextureDEPTH_enable(), elemTexture_env_color(), elemTexture_env_mode(), elemClip_plane(), elemClip_plane_enable(), elemBlend_color(), elemClear_color(), elemClear_stencil(), elemClear_depth(), elemColor_mask(), elemDepth_bounds(), elemDepth_mask(), elemDepth_range(), elemFog_density(), elemFog_start(), elemFog_end(), elemFog_color(), elemLight_model_ambient(), elemLighting_enable(), elemLine_stipple(), elemLine_width(), elemMaterial_ambient(), elemMaterial_diffuse(), elemMaterial_emission(), elemMaterial_shininess(), elemMaterial_specular(), elemModel_view_matrix(), elemPoint_distance_attenuation(), elemPoint_fade_threshold_size(), elemPoint_size(), elemPoint_size_min(), elemPoint_size_max(), elemPolygon_offset(), elemProjection_matrix(), elemScissor(), elemStencil_mask(), elemAlpha_test_enable(), elemAuto_normal_enable(), elemBlend_enable(), elemColor_logic_op_enable(), elemColor_material_enable(), elemCull_face_enable(), elemDepth_bounds_enable(), elemDepth_clamp_enable(), elemDepth_test_enable(), elemDither_enable(), elemFog_enable(), elemLight_model_local_viewer_enable(), elemLight_model_two_side_enable(), elemLine_smooth_enable(), elemLine_stipple_enable(), elemLogic_op_enable(), elemMultisample_enable(), elemNormalize_enable(), elemPoint_smooth_enable(), elemPolygon_offset_fill_enable(), elemPolygon_offset_line_enable(), elemPolygon_offset_point_enable(), elemPolygon_smooth_enable(), elemPolygon_stipple_enable(), elemRescale_normal_enable(), elemSample_alpha_to_coverage_enable(), elemSample_alpha_to_one_enable(), elemSample_coverage_enable(), elemScissor_test_enable(), elemStencil_test_enable(), elemGl_hook_abstract() {} + /** + * Destructor + */ + virtual ~domGl_pipeline_settings() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domGl_pipeline_settings &operator=( const domGl_pipeline_settings &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGl_sampler1D.h b/Engine/lib/collada/include/1.4/dom/domGl_sampler1D.h new file mode 100644 index 000000000..9aa87299d --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGl_sampler1D.h @@ -0,0 +1,83 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGl_sampler1D_h__ +#define __domGl_sampler1D_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * A one-dimensional texture sampler for the GLSL profile. + */ +class domGl_sampler1D_complexType : public domFx_sampler1D_common_complexType +{ + +protected: + /** + * Constructor + */ + domGl_sampler1D_complexType(DAE& dae, daeElement* elt) : domFx_sampler1D_common_complexType(dae, elt) {} + /** + * Destructor + */ + virtual ~domGl_sampler1D_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domGl_sampler1D_complexType &operator=( const domGl_sampler1D_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domGl_sampler1D_complexType. + */ +class domGl_sampler1D : public daeElement, public domGl_sampler1D_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GL_SAMPLER1D; } + static daeInt ID() { return 97; } + virtual daeInt typeID() const { return ID(); } +protected: + /** + * Constructor + */ + domGl_sampler1D(DAE& dae) : daeElement(dae), domGl_sampler1D_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domGl_sampler1D() {} + /** + * Overloaded assignment operator + */ + virtual domGl_sampler1D &operator=( const domGl_sampler1D &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGl_sampler2D.h b/Engine/lib/collada/include/1.4/dom/domGl_sampler2D.h new file mode 100644 index 000000000..76dd03f5b --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGl_sampler2D.h @@ -0,0 +1,83 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGl_sampler2D_h__ +#define __domGl_sampler2D_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * A two-dimensional texture sampler for the GLSL profile. + */ +class domGl_sampler2D_complexType : public domFx_sampler2D_common_complexType +{ + +protected: + /** + * Constructor + */ + domGl_sampler2D_complexType(DAE& dae, daeElement* elt) : domFx_sampler2D_common_complexType(dae, elt) {} + /** + * Destructor + */ + virtual ~domGl_sampler2D_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domGl_sampler2D_complexType &operator=( const domGl_sampler2D_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domGl_sampler2D_complexType. + */ +class domGl_sampler2D : public daeElement, public domGl_sampler2D_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GL_SAMPLER2D; } + static daeInt ID() { return 98; } + virtual daeInt typeID() const { return ID(); } +protected: + /** + * Constructor + */ + domGl_sampler2D(DAE& dae) : daeElement(dae), domGl_sampler2D_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domGl_sampler2D() {} + /** + * Overloaded assignment operator + */ + virtual domGl_sampler2D &operator=( const domGl_sampler2D &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGl_sampler3D.h b/Engine/lib/collada/include/1.4/dom/domGl_sampler3D.h new file mode 100644 index 000000000..155243a01 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGl_sampler3D.h @@ -0,0 +1,83 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGl_sampler3D_h__ +#define __domGl_sampler3D_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * A three-dimensional texture sampler for the GLSL profile. + */ +class domGl_sampler3D_complexType : public domFx_sampler3D_common_complexType +{ + +protected: + /** + * Constructor + */ + domGl_sampler3D_complexType(DAE& dae, daeElement* elt) : domFx_sampler3D_common_complexType(dae, elt) {} + /** + * Destructor + */ + virtual ~domGl_sampler3D_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domGl_sampler3D_complexType &operator=( const domGl_sampler3D_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domGl_sampler3D_complexType. + */ +class domGl_sampler3D : public daeElement, public domGl_sampler3D_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GL_SAMPLER3D; } + static daeInt ID() { return 99; } + virtual daeInt typeID() const { return ID(); } +protected: + /** + * Constructor + */ + domGl_sampler3D(DAE& dae) : daeElement(dae), domGl_sampler3D_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domGl_sampler3D() {} + /** + * Overloaded assignment operator + */ + virtual domGl_sampler3D &operator=( const domGl_sampler3D &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGl_samplerCUBE.h b/Engine/lib/collada/include/1.4/dom/domGl_samplerCUBE.h new file mode 100644 index 000000000..b1511bb99 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGl_samplerCUBE.h @@ -0,0 +1,83 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGl_samplerCUBE_h__ +#define __domGl_samplerCUBE_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * A cube map texture sampler for the GLSL profile. + */ +class domGl_samplerCUBE_complexType : public domFx_samplerCUBE_common_complexType +{ + +protected: + /** + * Constructor + */ + domGl_samplerCUBE_complexType(DAE& dae, daeElement* elt) : domFx_samplerCUBE_common_complexType(dae, elt) {} + /** + * Destructor + */ + virtual ~domGl_samplerCUBE_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domGl_samplerCUBE_complexType &operator=( const domGl_samplerCUBE_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domGl_samplerCUBE_complexType. + */ +class domGl_samplerCUBE : public daeElement, public domGl_samplerCUBE_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GL_SAMPLERCUBE; } + static daeInt ID() { return 100; } + virtual daeInt typeID() const { return ID(); } +protected: + /** + * Constructor + */ + domGl_samplerCUBE(DAE& dae) : daeElement(dae), domGl_samplerCUBE_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domGl_samplerCUBE() {} + /** + * Overloaded assignment operator + */ + virtual domGl_samplerCUBE &operator=( const domGl_samplerCUBE &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGl_samplerDEPTH.h b/Engine/lib/collada/include/1.4/dom/domGl_samplerDEPTH.h new file mode 100644 index 000000000..6b3f8e010 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGl_samplerDEPTH.h @@ -0,0 +1,83 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGl_samplerDEPTH_h__ +#define __domGl_samplerDEPTH_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * A depth texture sampler for the GLSL profile. + */ +class domGl_samplerDEPTH_complexType : public domFx_samplerDEPTH_common_complexType +{ + +protected: + /** + * Constructor + */ + domGl_samplerDEPTH_complexType(DAE& dae, daeElement* elt) : domFx_samplerDEPTH_common_complexType(dae, elt) {} + /** + * Destructor + */ + virtual ~domGl_samplerDEPTH_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domGl_samplerDEPTH_complexType &operator=( const domGl_samplerDEPTH_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domGl_samplerDEPTH_complexType. + */ +class domGl_samplerDEPTH : public daeElement, public domGl_samplerDEPTH_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GL_SAMPLERDEPTH; } + static daeInt ID() { return 102; } + virtual daeInt typeID() const { return ID(); } +protected: + /** + * Constructor + */ + domGl_samplerDEPTH(DAE& dae) : daeElement(dae), domGl_samplerDEPTH_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domGl_samplerDEPTH() {} + /** + * Overloaded assignment operator + */ + virtual domGl_samplerDEPTH &operator=( const domGl_samplerDEPTH &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGl_samplerRECT.h b/Engine/lib/collada/include/1.4/dom/domGl_samplerRECT.h new file mode 100644 index 000000000..77b9afd9d --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGl_samplerRECT.h @@ -0,0 +1,83 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGl_samplerRECT_h__ +#define __domGl_samplerRECT_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * A two-dimensional texture sampler for the GLSL profile. + */ +class domGl_samplerRECT_complexType : public domFx_samplerRECT_common_complexType +{ + +protected: + /** + * Constructor + */ + domGl_samplerRECT_complexType(DAE& dae, daeElement* elt) : domFx_samplerRECT_common_complexType(dae, elt) {} + /** + * Destructor + */ + virtual ~domGl_samplerRECT_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domGl_samplerRECT_complexType &operator=( const domGl_samplerRECT_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domGl_samplerRECT_complexType. + */ +class domGl_samplerRECT : public daeElement, public domGl_samplerRECT_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GL_SAMPLERRECT; } + static daeInt ID() { return 101; } + virtual daeInt typeID() const { return ID(); } +protected: + /** + * Constructor + */ + domGl_samplerRECT(DAE& dae) : daeElement(dae), domGl_samplerRECT_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domGl_samplerRECT() {} + /** + * Overloaded assignment operator + */ + virtual domGl_samplerRECT &operator=( const domGl_samplerRECT &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGles_basic_type_common.h b/Engine/lib/collada/include/1.4/dom/domGles_basic_type_common.h new file mode 100644 index 000000000..f1137a090 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGles_basic_type_common.h @@ -0,0 +1,2096 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGles_basic_type_common_h__ +#define __domGles_basic_type_common_h__ + +#include +#include +#include + +#include +#include +#include +#include +class DAE; + +/** + * A group that defines the available variable types for GLES parameters. + */ +class domGles_basic_type_common : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GLES_BASIC_TYPE_COMMON; } + static daeInt ID() { return 572; } + virtual daeInt typeID() const { return ID(); } +public: + class domBool; + + typedef daeSmartRef domBoolRef; + typedef daeTArray domBool_Array; + + class domBool : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL; } + static daeInt ID() { return 573; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domBool value of the text data of this element. + */ + ::domBool _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a ::domBool of the value. + */ + ::domBool getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( ::domBool val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool() {} + /** + * Overloaded assignment operator + */ + virtual domBool &operator=( const domBool &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool2; + + typedef daeSmartRef domBool2Ref; + typedef daeTArray domBool2_Array; + + class domBool2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL2; } + static daeInt ID() { return 574; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domBool2 value of the text data of this element. + */ + ::domBool2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domBool2 reference of the _value array. + */ + ::domBool2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domBool2 reference of the _value array. + */ + const ::domBool2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domBool2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool2() {} + /** + * Overloaded assignment operator + */ + virtual domBool2 &operator=( const domBool2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool3; + + typedef daeSmartRef domBool3Ref; + typedef daeTArray domBool3_Array; + + class domBool3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL3; } + static daeInt ID() { return 575; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domBool3 value of the text data of this element. + */ + ::domBool3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domBool3 reference of the _value array. + */ + ::domBool3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domBool3 reference of the _value array. + */ + const ::domBool3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domBool3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool3() {} + /** + * Overloaded assignment operator + */ + virtual domBool3 &operator=( const domBool3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool4; + + typedef daeSmartRef domBool4Ref; + typedef daeTArray domBool4_Array; + + class domBool4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL4; } + static daeInt ID() { return 576; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domBool4 value of the text data of this element. + */ + ::domBool4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domBool4 reference of the _value array. + */ + ::domBool4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domBool4 reference of the _value array. + */ + const ::domBool4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domBool4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool4() {} + /** + * Overloaded assignment operator + */ + virtual domBool4 &operator=( const domBool4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt; + + typedef daeSmartRef domIntRef; + typedef daeTArray domInt_Array; + + class domInt : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT; } + static daeInt ID() { return 577; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domInt value of the text data of this element. + */ + ::domInt _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a ::domInt of the value. + */ + ::domInt getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( ::domInt val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt() {} + /** + * Overloaded assignment operator + */ + virtual domInt &operator=( const domInt &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt2; + + typedef daeSmartRef domInt2Ref; + typedef daeTArray domInt2_Array; + + class domInt2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT2; } + static daeInt ID() { return 578; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domInt2 value of the text data of this element. + */ + ::domInt2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domInt2 reference of the _value array. + */ + ::domInt2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domInt2 reference of the _value array. + */ + const ::domInt2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domInt2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt2() {} + /** + * Overloaded assignment operator + */ + virtual domInt2 &operator=( const domInt2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt3; + + typedef daeSmartRef domInt3Ref; + typedef daeTArray domInt3_Array; + + class domInt3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT3; } + static daeInt ID() { return 579; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domInt3 value of the text data of this element. + */ + ::domInt3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domInt3 reference of the _value array. + */ + ::domInt3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domInt3 reference of the _value array. + */ + const ::domInt3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domInt3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt3() {} + /** + * Overloaded assignment operator + */ + virtual domInt3 &operator=( const domInt3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt4; + + typedef daeSmartRef domInt4Ref; + typedef daeTArray domInt4_Array; + + class domInt4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT4; } + static daeInt ID() { return 580; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domInt4 value of the text data of this element. + */ + ::domInt4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domInt4 reference of the _value array. + */ + ::domInt4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domInt4 reference of the _value array. + */ + const ::domInt4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domInt4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt4() {} + /** + * Overloaded assignment operator + */ + virtual domInt4 &operator=( const domInt4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat; + + typedef daeSmartRef domFloatRef; + typedef daeTArray domFloat_Array; + + class domFloat : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT; } + static daeInt ID() { return 581; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat value of the text data of this element. + */ + ::domFloat _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a ::domFloat of the value. + */ + ::domFloat getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( ::domFloat val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat() {} + /** + * Overloaded assignment operator + */ + virtual domFloat &operator=( const domFloat &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat2; + + typedef daeSmartRef domFloat2Ref; + typedef daeTArray domFloat2_Array; + + class domFloat2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT2; } + static daeInt ID() { return 582; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat2 value of the text data of this element. + */ + ::domFloat2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat2 reference of the _value array. + */ + ::domFloat2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat2 reference of the _value array. + */ + const ::domFloat2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat2() {} + /** + * Overloaded assignment operator + */ + virtual domFloat2 &operator=( const domFloat2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat3; + + typedef daeSmartRef domFloat3Ref; + typedef daeTArray domFloat3_Array; + + class domFloat3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT3; } + static daeInt ID() { return 583; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat3 value of the text data of this element. + */ + ::domFloat3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat3 reference of the _value array. + */ + ::domFloat3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat3 reference of the _value array. + */ + const ::domFloat3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat3() {} + /** + * Overloaded assignment operator + */ + virtual domFloat3 &operator=( const domFloat3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat4; + + typedef daeSmartRef domFloat4Ref; + typedef daeTArray domFloat4_Array; + + class domFloat4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT4; } + static daeInt ID() { return 584; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat4 value of the text data of this element. + */ + ::domFloat4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat4 reference of the _value array. + */ + ::domFloat4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat4 reference of the _value array. + */ + const ::domFloat4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat4() {} + /** + * Overloaded assignment operator + */ + virtual domFloat4 &operator=( const domFloat4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat1x1; + + typedef daeSmartRef domFloat1x1Ref; + typedef daeTArray domFloat1x1_Array; + + class domFloat1x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT1X1; } + static daeInt ID() { return 585; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat value of the text data of this element. + */ + ::domFloat _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a ::domFloat of the value. + */ + ::domFloat getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( ::domFloat val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat1x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat1x1() {} + /** + * Overloaded assignment operator + */ + virtual domFloat1x1 &operator=( const domFloat1x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat1x2; + + typedef daeSmartRef domFloat1x2Ref; + typedef daeTArray domFloat1x2_Array; + + class domFloat1x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT1X2; } + static daeInt ID() { return 586; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat2 value of the text data of this element. + */ + ::domFloat2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat2 reference of the _value array. + */ + ::domFloat2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat2 reference of the _value array. + */ + const ::domFloat2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat1x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat1x2() {} + /** + * Overloaded assignment operator + */ + virtual domFloat1x2 &operator=( const domFloat1x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat1x3; + + typedef daeSmartRef domFloat1x3Ref; + typedef daeTArray domFloat1x3_Array; + + class domFloat1x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT1X3; } + static daeInt ID() { return 587; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat3 value of the text data of this element. + */ + ::domFloat3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat3 reference of the _value array. + */ + ::domFloat3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat3 reference of the _value array. + */ + const ::domFloat3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat1x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat1x3() {} + /** + * Overloaded assignment operator + */ + virtual domFloat1x3 &operator=( const domFloat1x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat1x4; + + typedef daeSmartRef domFloat1x4Ref; + typedef daeTArray domFloat1x4_Array; + + class domFloat1x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT1X4; } + static daeInt ID() { return 588; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat4 value of the text data of this element. + */ + ::domFloat4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat4 reference of the _value array. + */ + ::domFloat4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat4 reference of the _value array. + */ + const ::domFloat4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat1x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat1x4() {} + /** + * Overloaded assignment operator + */ + virtual domFloat1x4 &operator=( const domFloat1x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat2x1; + + typedef daeSmartRef domFloat2x1Ref; + typedef daeTArray domFloat2x1_Array; + + class domFloat2x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT2X1; } + static daeInt ID() { return 589; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat2 value of the text data of this element. + */ + ::domFloat2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat2 reference of the _value array. + */ + ::domFloat2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat2 reference of the _value array. + */ + const ::domFloat2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat2x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat2x1() {} + /** + * Overloaded assignment operator + */ + virtual domFloat2x1 &operator=( const domFloat2x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat2x2; + + typedef daeSmartRef domFloat2x2Ref; + typedef daeTArray domFloat2x2_Array; + + class domFloat2x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT2X2; } + static daeInt ID() { return 590; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat2x2 value of the text data of this element. + */ + ::domFloat2x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat2x2 reference of the _value array. + */ + ::domFloat2x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat2x2 reference of the _value array. + */ + const ::domFloat2x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat2x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat2x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat2x2() {} + /** + * Overloaded assignment operator + */ + virtual domFloat2x2 &operator=( const domFloat2x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat2x3; + + typedef daeSmartRef domFloat2x3Ref; + typedef daeTArray domFloat2x3_Array; + + class domFloat2x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT2X3; } + static daeInt ID() { return 591; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat2x3 value of the text data of this element. + */ + ::domFloat2x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat2x3 reference of the _value array. + */ + ::domFloat2x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat2x3 reference of the _value array. + */ + const ::domFloat2x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat2x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat2x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat2x3() {} + /** + * Overloaded assignment operator + */ + virtual domFloat2x3 &operator=( const domFloat2x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat2x4; + + typedef daeSmartRef domFloat2x4Ref; + typedef daeTArray domFloat2x4_Array; + + class domFloat2x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT2X4; } + static daeInt ID() { return 592; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat2x4 value of the text data of this element. + */ + ::domFloat2x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat2x4 reference of the _value array. + */ + ::domFloat2x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat2x4 reference of the _value array. + */ + const ::domFloat2x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat2x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat2x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat2x4() {} + /** + * Overloaded assignment operator + */ + virtual domFloat2x4 &operator=( const domFloat2x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat3x1; + + typedef daeSmartRef domFloat3x1Ref; + typedef daeTArray domFloat3x1_Array; + + class domFloat3x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT3X1; } + static daeInt ID() { return 593; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat3 value of the text data of this element. + */ + ::domFloat3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat3 reference of the _value array. + */ + ::domFloat3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat3 reference of the _value array. + */ + const ::domFloat3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat3x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat3x1() {} + /** + * Overloaded assignment operator + */ + virtual domFloat3x1 &operator=( const domFloat3x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat3x2; + + typedef daeSmartRef domFloat3x2Ref; + typedef daeTArray domFloat3x2_Array; + + class domFloat3x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT3X2; } + static daeInt ID() { return 594; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat3x2 value of the text data of this element. + */ + ::domFloat3x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat3x2 reference of the _value array. + */ + ::domFloat3x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat3x2 reference of the _value array. + */ + const ::domFloat3x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat3x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat3x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat3x2() {} + /** + * Overloaded assignment operator + */ + virtual domFloat3x2 &operator=( const domFloat3x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat3x3; + + typedef daeSmartRef domFloat3x3Ref; + typedef daeTArray domFloat3x3_Array; + + class domFloat3x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT3X3; } + static daeInt ID() { return 595; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat3x3 value of the text data of this element. + */ + ::domFloat3x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat3x3 reference of the _value array. + */ + ::domFloat3x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat3x3 reference of the _value array. + */ + const ::domFloat3x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat3x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat3x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat3x3() {} + /** + * Overloaded assignment operator + */ + virtual domFloat3x3 &operator=( const domFloat3x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat3x4; + + typedef daeSmartRef domFloat3x4Ref; + typedef daeTArray domFloat3x4_Array; + + class domFloat3x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT3X4; } + static daeInt ID() { return 596; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat3x4 value of the text data of this element. + */ + ::domFloat3x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat3x4 reference of the _value array. + */ + ::domFloat3x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat3x4 reference of the _value array. + */ + const ::domFloat3x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat3x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat3x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat3x4() {} + /** + * Overloaded assignment operator + */ + virtual domFloat3x4 &operator=( const domFloat3x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat4x1; + + typedef daeSmartRef domFloat4x1Ref; + typedef daeTArray domFloat4x1_Array; + + class domFloat4x1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT4X1; } + static daeInt ID() { return 597; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat4 value of the text data of this element. + */ + ::domFloat4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat4 reference of the _value array. + */ + ::domFloat4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat4 reference of the _value array. + */ + const ::domFloat4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat4x1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat4x1() {} + /** + * Overloaded assignment operator + */ + virtual domFloat4x1 &operator=( const domFloat4x1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat4x2; + + typedef daeSmartRef domFloat4x2Ref; + typedef daeTArray domFloat4x2_Array; + + class domFloat4x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT4X2; } + static daeInt ID() { return 598; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat4x2 value of the text data of this element. + */ + ::domFloat4x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat4x2 reference of the _value array. + */ + ::domFloat4x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat4x2 reference of the _value array. + */ + const ::domFloat4x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat4x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat4x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat4x2() {} + /** + * Overloaded assignment operator + */ + virtual domFloat4x2 &operator=( const domFloat4x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat4x3; + + typedef daeSmartRef domFloat4x3Ref; + typedef daeTArray domFloat4x3_Array; + + class domFloat4x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT4X3; } + static daeInt ID() { return 599; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat4x3 value of the text data of this element. + */ + ::domFloat4x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat4x3 reference of the _value array. + */ + ::domFloat4x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat4x3 reference of the _value array. + */ + const ::domFloat4x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat4x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat4x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat4x3() {} + /** + * Overloaded assignment operator + */ + virtual domFloat4x3 &operator=( const domFloat4x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat4x4; + + typedef daeSmartRef domFloat4x4Ref; + typedef daeTArray domFloat4x4_Array; + + class domFloat4x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT4X4; } + static daeInt ID() { return 600; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The ::domFloat4x4 value of the text data of this element. + */ + ::domFloat4x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a ::domFloat4x4 reference of the _value array. + */ + ::domFloat4x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant ::domFloat4x4 reference of the _value array. + */ + const ::domFloat4x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const ::domFloat4x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat4x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat4x4() {} + /** + * Overloaded assignment operator + */ + virtual domFloat4x4 &operator=( const domFloat4x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domEnum; + + typedef daeSmartRef domEnumRef; + typedef daeTArray domEnum_Array; + + class domEnum : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ENUM; } + static daeInt ID() { return 601; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domGles_enumeration value of the text data of this element. + */ + domGles_enumeration _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domGles_enumeration of the value. + */ + domGles_enumeration getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domGles_enumeration val ) { _value = val; } + + protected: + /** + * Constructor + */ + domEnum(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domEnum() {} + /** + * Overloaded assignment operator + */ + virtual domEnum &operator=( const domEnum &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements + domBoolRef elemBool; + domBool2Ref elemBool2; + domBool3Ref elemBool3; + domBool4Ref elemBool4; + domIntRef elemInt; + domInt2Ref elemInt2; + domInt3Ref elemInt3; + domInt4Ref elemInt4; + domFloatRef elemFloat; + domFloat2Ref elemFloat2; + domFloat3Ref elemFloat3; + domFloat4Ref elemFloat4; + domFloat1x1Ref elemFloat1x1; + domFloat1x2Ref elemFloat1x2; + domFloat1x3Ref elemFloat1x3; + domFloat1x4Ref elemFloat1x4; + domFloat2x1Ref elemFloat2x1; + domFloat2x2Ref elemFloat2x2; + domFloat2x3Ref elemFloat2x3; + domFloat2x4Ref elemFloat2x4; + domFloat3x1Ref elemFloat3x1; + domFloat3x2Ref elemFloat3x2; + domFloat3x3Ref elemFloat3x3; + domFloat3x4Ref elemFloat3x4; + domFloat4x1Ref elemFloat4x1; + domFloat4x2Ref elemFloat4x2; + domFloat4x3Ref elemFloat4x3; + domFloat4x4Ref elemFloat4x4; + domFx_surface_commonRef elemSurface; + domGles_texture_pipelineRef elemTexture_pipeline; + domGles_sampler_stateRef elemSampler_state; + domGles_texture_unitRef elemTexture_unit; + domEnumRef elemEnum; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the bool element. + * @return a daeSmartRef to the bool element. + */ + const domBoolRef getBool() const { return elemBool; } + /** + * Gets the bool2 element. + * @return a daeSmartRef to the bool2 element. + */ + const domBool2Ref getBool2() const { return elemBool2; } + /** + * Gets the bool3 element. + * @return a daeSmartRef to the bool3 element. + */ + const domBool3Ref getBool3() const { return elemBool3; } + /** + * Gets the bool4 element. + * @return a daeSmartRef to the bool4 element. + */ + const domBool4Ref getBool4() const { return elemBool4; } + /** + * Gets the int element. + * @return a daeSmartRef to the int element. + */ + const domIntRef getInt() const { return elemInt; } + /** + * Gets the int2 element. + * @return a daeSmartRef to the int2 element. + */ + const domInt2Ref getInt2() const { return elemInt2; } + /** + * Gets the int3 element. + * @return a daeSmartRef to the int3 element. + */ + const domInt3Ref getInt3() const { return elemInt3; } + /** + * Gets the int4 element. + * @return a daeSmartRef to the int4 element. + */ + const domInt4Ref getInt4() const { return elemInt4; } + /** + * Gets the float element. + * @return a daeSmartRef to the float element. + */ + const domFloatRef getFloat() const { return elemFloat; } + /** + * Gets the float2 element. + * @return a daeSmartRef to the float2 element. + */ + const domFloat2Ref getFloat2() const { return elemFloat2; } + /** + * Gets the float3 element. + * @return a daeSmartRef to the float3 element. + */ + const domFloat3Ref getFloat3() const { return elemFloat3; } + /** + * Gets the float4 element. + * @return a daeSmartRef to the float4 element. + */ + const domFloat4Ref getFloat4() const { return elemFloat4; } + /** + * Gets the float1x1 element. + * @return a daeSmartRef to the float1x1 element. + */ + const domFloat1x1Ref getFloat1x1() const { return elemFloat1x1; } + /** + * Gets the float1x2 element. + * @return a daeSmartRef to the float1x2 element. + */ + const domFloat1x2Ref getFloat1x2() const { return elemFloat1x2; } + /** + * Gets the float1x3 element. + * @return a daeSmartRef to the float1x3 element. + */ + const domFloat1x3Ref getFloat1x3() const { return elemFloat1x3; } + /** + * Gets the float1x4 element. + * @return a daeSmartRef to the float1x4 element. + */ + const domFloat1x4Ref getFloat1x4() const { return elemFloat1x4; } + /** + * Gets the float2x1 element. + * @return a daeSmartRef to the float2x1 element. + */ + const domFloat2x1Ref getFloat2x1() const { return elemFloat2x1; } + /** + * Gets the float2x2 element. + * @return a daeSmartRef to the float2x2 element. + */ + const domFloat2x2Ref getFloat2x2() const { return elemFloat2x2; } + /** + * Gets the float2x3 element. + * @return a daeSmartRef to the float2x3 element. + */ + const domFloat2x3Ref getFloat2x3() const { return elemFloat2x3; } + /** + * Gets the float2x4 element. + * @return a daeSmartRef to the float2x4 element. + */ + const domFloat2x4Ref getFloat2x4() const { return elemFloat2x4; } + /** + * Gets the float3x1 element. + * @return a daeSmartRef to the float3x1 element. + */ + const domFloat3x1Ref getFloat3x1() const { return elemFloat3x1; } + /** + * Gets the float3x2 element. + * @return a daeSmartRef to the float3x2 element. + */ + const domFloat3x2Ref getFloat3x2() const { return elemFloat3x2; } + /** + * Gets the float3x3 element. + * @return a daeSmartRef to the float3x3 element. + */ + const domFloat3x3Ref getFloat3x3() const { return elemFloat3x3; } + /** + * Gets the float3x4 element. + * @return a daeSmartRef to the float3x4 element. + */ + const domFloat3x4Ref getFloat3x4() const { return elemFloat3x4; } + /** + * Gets the float4x1 element. + * @return a daeSmartRef to the float4x1 element. + */ + const domFloat4x1Ref getFloat4x1() const { return elemFloat4x1; } + /** + * Gets the float4x2 element. + * @return a daeSmartRef to the float4x2 element. + */ + const domFloat4x2Ref getFloat4x2() const { return elemFloat4x2; } + /** + * Gets the float4x3 element. + * @return a daeSmartRef to the float4x3 element. + */ + const domFloat4x3Ref getFloat4x3() const { return elemFloat4x3; } + /** + * Gets the float4x4 element. + * @return a daeSmartRef to the float4x4 element. + */ + const domFloat4x4Ref getFloat4x4() const { return elemFloat4x4; } + /** + * Gets the surface element. + * @return a daeSmartRef to the surface element. + */ + const domFx_surface_commonRef getSurface() const { return elemSurface; } + /** + * Gets the texture_pipeline element. + * @return a daeSmartRef to the texture_pipeline element. + */ + const domGles_texture_pipelineRef getTexture_pipeline() const { return elemTexture_pipeline; } + /** + * Gets the sampler_state element. + * @return a daeSmartRef to the sampler_state element. + */ + const domGles_sampler_stateRef getSampler_state() const { return elemSampler_state; } + /** + * Gets the texture_unit element. + * @return a daeSmartRef to the texture_unit element. + */ + const domGles_texture_unitRef getTexture_unit() const { return elemTexture_unit; } + /** + * Gets the enum element. + * @return a daeSmartRef to the enum element. + */ + const domEnumRef getEnum() const { return elemEnum; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domGles_basic_type_common(DAE& dae) : daeElement(dae), elemBool(), elemBool2(), elemBool3(), elemBool4(), elemInt(), elemInt2(), elemInt3(), elemInt4(), elemFloat(), elemFloat2(), elemFloat3(), elemFloat4(), elemFloat1x1(), elemFloat1x2(), elemFloat1x3(), elemFloat1x4(), elemFloat2x1(), elemFloat2x2(), elemFloat2x3(), elemFloat2x4(), elemFloat3x1(), elemFloat3x2(), elemFloat3x3(), elemFloat3x4(), elemFloat4x1(), elemFloat4x2(), elemFloat4x3(), elemFloat4x4(), elemSurface(), elemTexture_pipeline(), elemSampler_state(), elemTexture_unit(), elemEnum() {} + /** + * Destructor + */ + virtual ~domGles_basic_type_common() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domGles_basic_type_common &operator=( const domGles_basic_type_common &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGles_newparam.h b/Engine/lib/collada/include/1.4/dom/domGles_newparam.h new file mode 100644 index 000000000..5b2b3d4da --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGles_newparam.h @@ -0,0 +1,284 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGles_newparam_h__ +#define __domGles_newparam_h__ + +#include +#include +#include + +#include +#include +class DAE; + +/** + * Create a new, named param object in the GLES Runtime, assign it a type, + * an initial value, and additional attributes at declaration time. + */ +class domGles_newparam_complexType +{ +public: + class domSemantic; + + typedef daeSmartRef domSemanticRef; + typedef daeTArray domSemantic_Array; + +/** + * The semantic element allows you to specify a semantic for this new param. + */ + class domSemantic : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SEMANTIC; } + static daeInt ID() { return 165; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsNCName value of the text data of this element. + */ + xsNCName _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsNCName of the value. + */ + xsNCName getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNCName val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domSemantic(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domSemantic() {} + /** + * Overloaded assignment operator + */ + virtual domSemantic &operator=( const domSemantic &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domModifier; + + typedef daeSmartRef domModifierRef; + typedef daeTArray domModifier_Array; + +/** + * The modifier element allows you to specify a modifier for this new param. + */ + class domModifier : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MODIFIER; } + static daeInt ID() { return 166; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_modifier_enum_common value of the text data of this element. + */ + domFx_modifier_enum_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_modifier_enum_common of the value. + */ + domFx_modifier_enum_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_modifier_enum_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domModifier(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domModifier() {} + /** + * Overloaded assignment operator + */ + virtual domModifier &operator=( const domModifier &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attribute +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. + */ + xsNCName attrSid; + +protected: // Elements +/** + * The annotate element allows you to specify an annotation for this new param. + * @see domAnnotate + */ + domFx_annotate_common_Array elemAnnotate_array; +/** + * The semantic element allows you to specify a semantic for this new param. + * @see domSemantic + */ + domSemanticRef elemSemantic; +/** + * The modifier element allows you to specify a modifier for this new param. + * @see domModifier + */ + domModifierRef elemModifier; + domGles_basic_type_commonRef elemGles_basic_type_common; + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid;} + + /** + * Gets the annotate element array. + * @return Returns a reference to the array of annotate elements. + */ + domFx_annotate_common_Array &getAnnotate_array() { return elemAnnotate_array; } + /** + * Gets the annotate element array. + * @return Returns a constant reference to the array of annotate elements. + */ + const domFx_annotate_common_Array &getAnnotate_array() const { return elemAnnotate_array; } + /** + * Gets the semantic element. + * @return a daeSmartRef to the semantic element. + */ + const domSemanticRef getSemantic() const { return elemSemantic; } + /** + * Gets the modifier element. + * @return a daeSmartRef to the modifier element. + */ + const domModifierRef getModifier() const { return elemModifier; } + /** + * Gets the gles_basic_type_common element. + * @return a daeSmartRef to the gles_basic_type_common element. + */ + const domGles_basic_type_commonRef getGles_basic_type_common() const { return elemGles_basic_type_common; } +protected: + /** + * Constructor + */ + domGles_newparam_complexType(DAE& dae, daeElement* elt) : attrSid(), elemAnnotate_array(), elemSemantic(), elemModifier(), elemGles_basic_type_common() {} + /** + * Destructor + */ + virtual ~domGles_newparam_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domGles_newparam_complexType &operator=( const domGles_newparam_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domGles_newparam_complexType. + */ +class domGles_newparam : public daeElement, public domGles_newparam_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GLES_NEWPARAM; } + static daeInt ID() { return 167; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + +protected: + /** + * Constructor + */ + domGles_newparam(DAE& dae) : daeElement(dae), domGles_newparam_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domGles_newparam() {} + /** + * Overloaded assignment operator + */ + virtual domGles_newparam &operator=( const domGles_newparam &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGles_pipeline_settings.h b/Engine/lib/collada/include/1.4/dom/domGles_pipeline_settings.h new file mode 100644 index 000000000..7c9b4f5fb --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGles_pipeline_settings.h @@ -0,0 +1,6315 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGles_pipeline_settings_h__ +#define __domGles_pipeline_settings_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * A group that contains the renderstates available for the GLES profile. + */ +class domGles_pipeline_settings : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GLES_PIPELINE_SETTINGS; } + static daeInt ID() { return 488; } + virtual daeInt typeID() const { return ID(); } +public: + class domAlpha_func; + + typedef daeSmartRef domAlpha_funcRef; + typedef daeTArray domAlpha_func_Array; + + class domAlpha_func : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ALPHA_FUNC; } + static daeInt ID() { return 489; } + virtual daeInt typeID() const { return ID(); } + public: + class domFunc; + + typedef daeSmartRef domFuncRef; + typedef daeTArray domFunc_Array; + + class domFunc : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FUNC; } + static daeInt ID() { return 490; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_func_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_func_type of the value attribute. + */ + domGl_func_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_func_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domFunc(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domFunc() {} + /** + * Overloaded assignment operator + */ + virtual domFunc &operator=( const domFunc &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domValue; + + typedef daeSmartRef domValueRef; + typedef daeTArray domValue_Array; + + class domValue : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::VALUE; } + static daeInt ID() { return 491; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_alpha_value_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_alpha_value_type of the value attribute. + */ + domGl_alpha_value_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_alpha_value_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domValue(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domValue() {} + /** + * Overloaded assignment operator + */ + virtual domValue &operator=( const domValue &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements + domFuncRef elemFunc; + domValueRef elemValue; + + public: //Accessors and Mutators + /** + * Gets the func element. + * @return a daeSmartRef to the func element. + */ + const domFuncRef getFunc() const { return elemFunc; } + /** + * Gets the value element. + * @return a daeSmartRef to the value element. + */ + const domValueRef getValue() const { return elemValue; } + protected: + /** + * Constructor + */ + domAlpha_func(DAE& dae) : daeElement(dae), elemFunc(), elemValue() {} + /** + * Destructor + */ + virtual ~domAlpha_func() {} + /** + * Overloaded assignment operator + */ + virtual domAlpha_func &operator=( const domAlpha_func &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBlend_func; + + typedef daeSmartRef domBlend_funcRef; + typedef daeTArray domBlend_func_Array; + + class domBlend_func : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BLEND_FUNC; } + static daeInt ID() { return 492; } + virtual daeInt typeID() const { return ID(); } + public: + class domSrc; + + typedef daeSmartRef domSrcRef; + typedef daeTArray domSrc_Array; + + class domSrc : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SRC; } + static daeInt ID() { return 493; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_blend_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_blend_type of the value attribute. + */ + domGl_blend_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_blend_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domSrc(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domSrc() {} + /** + * Overloaded assignment operator + */ + virtual domSrc &operator=( const domSrc &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domDest; + + typedef daeSmartRef domDestRef; + typedef daeTArray domDest_Array; + + class domDest : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::DEST; } + static daeInt ID() { return 494; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_blend_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_blend_type of the value attribute. + */ + domGl_blend_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_blend_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domDest(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domDest() {} + /** + * Overloaded assignment operator + */ + virtual domDest &operator=( const domDest &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements + domSrcRef elemSrc; + domDestRef elemDest; + + public: //Accessors and Mutators + /** + * Gets the src element. + * @return a daeSmartRef to the src element. + */ + const domSrcRef getSrc() const { return elemSrc; } + /** + * Gets the dest element. + * @return a daeSmartRef to the dest element. + */ + const domDestRef getDest() const { return elemDest; } + protected: + /** + * Constructor + */ + domBlend_func(DAE& dae) : daeElement(dae), elemSrc(), elemDest() {} + /** + * Destructor + */ + virtual ~domBlend_func() {} + /** + * Overloaded assignment operator + */ + virtual domBlend_func &operator=( const domBlend_func &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domClear_color; + + typedef daeSmartRef domClear_colorRef; + typedef daeTArray domClear_color_Array; + + class domClear_color : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CLEAR_COLOR; } + static daeInt ID() { return 495; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4 reference of the value array attribute. + */ + domFloat4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4 reference of the value array attribute. + */ + const domFloat4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domClear_color(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domClear_color() {} + /** + * Overloaded assignment operator + */ + virtual domClear_color &operator=( const domClear_color &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domClear_stencil; + + typedef daeSmartRef domClear_stencilRef; + typedef daeTArray domClear_stencil_Array; + + class domClear_stencil : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CLEAR_STENCIL; } + static daeInt ID() { return 496; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domInt attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domInt of the value attribute. + */ + domInt getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domInt atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domClear_stencil(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domClear_stencil() {} + /** + * Overloaded assignment operator + */ + virtual domClear_stencil &operator=( const domClear_stencil &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domClear_depth; + + typedef daeSmartRef domClear_depthRef; + typedef daeTArray domClear_depth_Array; + + class domClear_depth : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CLEAR_DEPTH; } + static daeInt ID() { return 497; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domClear_depth(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domClear_depth() {} + /** + * Overloaded assignment operator + */ + virtual domClear_depth &operator=( const domClear_depth &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domClip_plane; + + typedef daeSmartRef domClip_planeRef; + typedef daeTArray domClip_plane_Array; + + class domClip_plane : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CLIP_PLANE; } + static daeInt ID() { return 498; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool4 attrValue; + xsNCName attrParam; + domGLES_MAX_CLIP_PLANES_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domBool4 reference of the value array attribute. + */ + domBool4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domBool4 reference of the value array attribute. + */ + const domBool4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domBool4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGLES_MAX_CLIP_PLANES_index of the index attribute. + */ + domGLES_MAX_CLIP_PLANES_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGLES_MAX_CLIP_PLANES_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domClip_plane(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domClip_plane() {} + /** + * Overloaded assignment operator + */ + virtual domClip_plane &operator=( const domClip_plane &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domColor_mask; + + typedef daeSmartRef domColor_maskRef; + typedef daeTArray domColor_mask_Array; + + class domColor_mask : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::COLOR_MASK; } + static daeInt ID() { return 499; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool4 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domBool4 reference of the value array attribute. + */ + domBool4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domBool4 reference of the value array attribute. + */ + const domBool4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domBool4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domColor_mask(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domColor_mask() {} + /** + * Overloaded assignment operator + */ + virtual domColor_mask &operator=( const domColor_mask &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domCull_face; + + typedef daeSmartRef domCull_faceRef; + typedef daeTArray domCull_face_Array; + + class domCull_face : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CULL_FACE; } + static daeInt ID() { return 500; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_face_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_face_type of the value attribute. + */ + domGl_face_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_face_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domCull_face(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domCull_face() {} + /** + * Overloaded assignment operator + */ + virtual domCull_face &operator=( const domCull_face &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domDepth_func; + + typedef daeSmartRef domDepth_funcRef; + typedef daeTArray domDepth_func_Array; + + class domDepth_func : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::DEPTH_FUNC; } + static daeInt ID() { return 501; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_func_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_func_type of the value attribute. + */ + domGl_func_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_func_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domDepth_func(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domDepth_func() {} + /** + * Overloaded assignment operator + */ + virtual domDepth_func &operator=( const domDepth_func &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domDepth_mask; + + typedef daeSmartRef domDepth_maskRef; + typedef daeTArray domDepth_mask_Array; + + class domDepth_mask : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::DEPTH_MASK; } + static daeInt ID() { return 502; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domDepth_mask(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domDepth_mask() {} + /** + * Overloaded assignment operator + */ + virtual domDepth_mask &operator=( const domDepth_mask &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domDepth_range; + + typedef daeSmartRef domDepth_rangeRef; + typedef daeTArray domDepth_range_Array; + + class domDepth_range : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::DEPTH_RANGE; } + static daeInt ID() { return 503; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat2 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat2 reference of the value array attribute. + */ + domFloat2 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat2 reference of the value array attribute. + */ + const domFloat2 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat2 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domDepth_range(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domDepth_range() {} + /** + * Overloaded assignment operator + */ + virtual domDepth_range &operator=( const domDepth_range &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFog_color; + + typedef daeSmartRef domFog_colorRef; + typedef daeTArray domFog_color_Array; + + class domFog_color : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FOG_COLOR; } + static daeInt ID() { return 504; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4 reference of the value array attribute. + */ + domFloat4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4 reference of the value array attribute. + */ + const domFloat4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domFog_color(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domFog_color() {} + /** + * Overloaded assignment operator + */ + virtual domFog_color &operator=( const domFog_color &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFog_density; + + typedef daeSmartRef domFog_densityRef; + typedef daeTArray domFog_density_Array; + + class domFog_density : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FOG_DENSITY; } + static daeInt ID() { return 505; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domFog_density(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domFog_density() {} + /** + * Overloaded assignment operator + */ + virtual domFog_density &operator=( const domFog_density &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFog_mode; + + typedef daeSmartRef domFog_modeRef; + typedef daeTArray domFog_mode_Array; + + class domFog_mode : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FOG_MODE; } + static daeInt ID() { return 506; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_fog_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_fog_type of the value attribute. + */ + domGl_fog_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_fog_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domFog_mode(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domFog_mode() {} + /** + * Overloaded assignment operator + */ + virtual domFog_mode &operator=( const domFog_mode &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFog_start; + + typedef daeSmartRef domFog_startRef; + typedef daeTArray domFog_start_Array; + + class domFog_start : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FOG_START; } + static daeInt ID() { return 507; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domFog_start(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domFog_start() {} + /** + * Overloaded assignment operator + */ + virtual domFog_start &operator=( const domFog_start &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFog_end; + + typedef daeSmartRef domFog_endRef; + typedef daeTArray domFog_end_Array; + + class domFog_end : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FOG_END; } + static daeInt ID() { return 508; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domFog_end(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domFog_end() {} + /** + * Overloaded assignment operator + */ + virtual domFog_end &operator=( const domFog_end &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFront_face; + + typedef daeSmartRef domFront_faceRef; + typedef daeTArray domFront_face_Array; + + class domFront_face : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FRONT_FACE; } + static daeInt ID() { return 509; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_front_face_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_front_face_type of the value attribute. + */ + domGl_front_face_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_front_face_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domFront_face(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domFront_face() {} + /** + * Overloaded assignment operator + */ + virtual domFront_face &operator=( const domFront_face &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domTexture_pipeline; + + typedef daeSmartRef domTexture_pipelineRef; + typedef daeTArray domTexture_pipeline_Array; + + class domTexture_pipeline : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TEXTURE_PIPELINE; } + static daeInt ID() { return 510; } + virtual daeInt typeID() const { return ID(); } + protected: // Attribute + xsNCName attrParam; + + protected: // Element + domGles_texture_pipelineRef elemValue; + + public: //Accessors and Mutators + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[0] = true; } + + /** + * Gets the value element. + * @return a daeSmartRef to the value element. + */ + const domGles_texture_pipelineRef getValue() const { return elemValue; } + protected: + /** + * Constructor + */ + domTexture_pipeline(DAE& dae) : daeElement(dae), attrParam(), elemValue() {} + /** + * Destructor + */ + virtual ~domTexture_pipeline() {} + /** + * Overloaded assignment operator + */ + virtual domTexture_pipeline &operator=( const domTexture_pipeline &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLogic_op; + + typedef daeSmartRef domLogic_opRef; + typedef daeTArray domLogic_op_Array; + + class domLogic_op : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LOGIC_OP; } + static daeInt ID() { return 511; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_logic_op_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_logic_op_type of the value attribute. + */ + domGl_logic_op_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_logic_op_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domLogic_op(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domLogic_op() {} + /** + * Overloaded assignment operator + */ + virtual domLogic_op &operator=( const domLogic_op &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_ambient; + + typedef daeSmartRef domLight_ambientRef; + typedef daeTArray domLight_ambient_Array; + + class domLight_ambient : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_AMBIENT; } + static daeInt ID() { return 512; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4 attrValue; + xsNCName attrParam; + domGLES_MAX_LIGHTS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4 reference of the value array attribute. + */ + domFloat4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4 reference of the value array attribute. + */ + const domFloat4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGLES_MAX_LIGHTS_index of the index attribute. + */ + domGLES_MAX_LIGHTS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGLES_MAX_LIGHTS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domLight_ambient(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domLight_ambient() {} + /** + * Overloaded assignment operator + */ + virtual domLight_ambient &operator=( const domLight_ambient &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_diffuse; + + typedef daeSmartRef domLight_diffuseRef; + typedef daeTArray domLight_diffuse_Array; + + class domLight_diffuse : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_DIFFUSE; } + static daeInt ID() { return 513; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4 attrValue; + xsNCName attrParam; + domGLES_MAX_LIGHTS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4 reference of the value array attribute. + */ + domFloat4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4 reference of the value array attribute. + */ + const domFloat4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGLES_MAX_LIGHTS_index of the index attribute. + */ + domGLES_MAX_LIGHTS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGLES_MAX_LIGHTS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domLight_diffuse(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domLight_diffuse() {} + /** + * Overloaded assignment operator + */ + virtual domLight_diffuse &operator=( const domLight_diffuse &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_specular; + + typedef daeSmartRef domLight_specularRef; + typedef daeTArray domLight_specular_Array; + + class domLight_specular : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_SPECULAR; } + static daeInt ID() { return 514; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4 attrValue; + xsNCName attrParam; + domGLES_MAX_LIGHTS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4 reference of the value array attribute. + */ + domFloat4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4 reference of the value array attribute. + */ + const domFloat4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGLES_MAX_LIGHTS_index of the index attribute. + */ + domGLES_MAX_LIGHTS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGLES_MAX_LIGHTS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domLight_specular(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domLight_specular() {} + /** + * Overloaded assignment operator + */ + virtual domLight_specular &operator=( const domLight_specular &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_position; + + typedef daeSmartRef domLight_positionRef; + typedef daeTArray domLight_position_Array; + + class domLight_position : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_POSITION; } + static daeInt ID() { return 515; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4 attrValue; + xsNCName attrParam; + domGLES_MAX_LIGHTS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4 reference of the value array attribute. + */ + domFloat4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4 reference of the value array attribute. + */ + const domFloat4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGLES_MAX_LIGHTS_index of the index attribute. + */ + domGLES_MAX_LIGHTS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGLES_MAX_LIGHTS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domLight_position(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domLight_position() {} + /** + * Overloaded assignment operator + */ + virtual domLight_position &operator=( const domLight_position &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_constant_attenuation; + + typedef daeSmartRef domLight_constant_attenuationRef; + typedef daeTArray domLight_constant_attenuation_Array; + + class domLight_constant_attenuation : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_CONSTANT_ATTENUATION; } + static daeInt ID() { return 516; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + domGLES_MAX_LIGHTS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGLES_MAX_LIGHTS_index of the index attribute. + */ + domGLES_MAX_LIGHTS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGLES_MAX_LIGHTS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domLight_constant_attenuation(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domLight_constant_attenuation() {} + /** + * Overloaded assignment operator + */ + virtual domLight_constant_attenuation &operator=( const domLight_constant_attenuation &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_linear_attenutation; + + typedef daeSmartRef domLight_linear_attenutationRef; + typedef daeTArray domLight_linear_attenutation_Array; + + class domLight_linear_attenutation : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_LINEAR_ATTENUTATION; } + static daeInt ID() { return 517; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + domGLES_MAX_LIGHTS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGLES_MAX_LIGHTS_index of the index attribute. + */ + domGLES_MAX_LIGHTS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGLES_MAX_LIGHTS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domLight_linear_attenutation(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domLight_linear_attenutation() {} + /** + * Overloaded assignment operator + */ + virtual domLight_linear_attenutation &operator=( const domLight_linear_attenutation &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_quadratic_attenuation; + + typedef daeSmartRef domLight_quadratic_attenuationRef; + typedef daeTArray domLight_quadratic_attenuation_Array; + + class domLight_quadratic_attenuation : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_QUADRATIC_ATTENUATION; } + static daeInt ID() { return 518; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + domGLES_MAX_LIGHTS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGLES_MAX_LIGHTS_index of the index attribute. + */ + domGLES_MAX_LIGHTS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGLES_MAX_LIGHTS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domLight_quadratic_attenuation(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domLight_quadratic_attenuation() {} + /** + * Overloaded assignment operator + */ + virtual domLight_quadratic_attenuation &operator=( const domLight_quadratic_attenuation &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_spot_cutoff; + + typedef daeSmartRef domLight_spot_cutoffRef; + typedef daeTArray domLight_spot_cutoff_Array; + + class domLight_spot_cutoff : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_SPOT_CUTOFF; } + static daeInt ID() { return 519; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + domGLES_MAX_LIGHTS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGLES_MAX_LIGHTS_index of the index attribute. + */ + domGLES_MAX_LIGHTS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGLES_MAX_LIGHTS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domLight_spot_cutoff(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domLight_spot_cutoff() {} + /** + * Overloaded assignment operator + */ + virtual domLight_spot_cutoff &operator=( const domLight_spot_cutoff &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_spot_direction; + + typedef daeSmartRef domLight_spot_directionRef; + typedef daeTArray domLight_spot_direction_Array; + + class domLight_spot_direction : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_SPOT_DIRECTION; } + static daeInt ID() { return 520; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat3 attrValue; + xsNCName attrParam; + domGLES_MAX_LIGHTS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat3 reference of the value array attribute. + */ + domFloat3 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat3 reference of the value array attribute. + */ + const domFloat3 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat3 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGLES_MAX_LIGHTS_index of the index attribute. + */ + domGLES_MAX_LIGHTS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGLES_MAX_LIGHTS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domLight_spot_direction(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domLight_spot_direction() {} + /** + * Overloaded assignment operator + */ + virtual domLight_spot_direction &operator=( const domLight_spot_direction &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_spot_exponent; + + typedef daeSmartRef domLight_spot_exponentRef; + typedef daeTArray domLight_spot_exponent_Array; + + class domLight_spot_exponent : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_SPOT_EXPONENT; } + static daeInt ID() { return 521; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + domGLES_MAX_LIGHTS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGLES_MAX_LIGHTS_index of the index attribute. + */ + domGLES_MAX_LIGHTS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGLES_MAX_LIGHTS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domLight_spot_exponent(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domLight_spot_exponent() {} + /** + * Overloaded assignment operator + */ + virtual domLight_spot_exponent &operator=( const domLight_spot_exponent &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_model_ambient; + + typedef daeSmartRef domLight_model_ambientRef; + typedef daeTArray domLight_model_ambient_Array; + + class domLight_model_ambient : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_MODEL_AMBIENT; } + static daeInt ID() { return 522; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4 reference of the value array attribute. + */ + domFloat4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4 reference of the value array attribute. + */ + const domFloat4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domLight_model_ambient(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domLight_model_ambient() {} + /** + * Overloaded assignment operator + */ + virtual domLight_model_ambient &operator=( const domLight_model_ambient &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLine_width; + + typedef daeSmartRef domLine_widthRef; + typedef daeTArray domLine_width_Array; + + class domLine_width : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LINE_WIDTH; } + static daeInt ID() { return 523; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domLine_width(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domLine_width() {} + /** + * Overloaded assignment operator + */ + virtual domLine_width &operator=( const domLine_width &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMaterial_ambient; + + typedef daeSmartRef domMaterial_ambientRef; + typedef daeTArray domMaterial_ambient_Array; + + class domMaterial_ambient : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MATERIAL_AMBIENT; } + static daeInt ID() { return 524; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4 reference of the value array attribute. + */ + domFloat4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4 reference of the value array attribute. + */ + const domFloat4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domMaterial_ambient(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domMaterial_ambient() {} + /** + * Overloaded assignment operator + */ + virtual domMaterial_ambient &operator=( const domMaterial_ambient &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMaterial_diffuse; + + typedef daeSmartRef domMaterial_diffuseRef; + typedef daeTArray domMaterial_diffuse_Array; + + class domMaterial_diffuse : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MATERIAL_DIFFUSE; } + static daeInt ID() { return 525; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4 reference of the value array attribute. + */ + domFloat4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4 reference of the value array attribute. + */ + const domFloat4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domMaterial_diffuse(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domMaterial_diffuse() {} + /** + * Overloaded assignment operator + */ + virtual domMaterial_diffuse &operator=( const domMaterial_diffuse &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMaterial_emission; + + typedef daeSmartRef domMaterial_emissionRef; + typedef daeTArray domMaterial_emission_Array; + + class domMaterial_emission : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MATERIAL_EMISSION; } + static daeInt ID() { return 526; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4 reference of the value array attribute. + */ + domFloat4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4 reference of the value array attribute. + */ + const domFloat4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domMaterial_emission(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domMaterial_emission() {} + /** + * Overloaded assignment operator + */ + virtual domMaterial_emission &operator=( const domMaterial_emission &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMaterial_shininess; + + typedef daeSmartRef domMaterial_shininessRef; + typedef daeTArray domMaterial_shininess_Array; + + class domMaterial_shininess : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MATERIAL_SHININESS; } + static daeInt ID() { return 527; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domMaterial_shininess(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domMaterial_shininess() {} + /** + * Overloaded assignment operator + */ + virtual domMaterial_shininess &operator=( const domMaterial_shininess &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMaterial_specular; + + typedef daeSmartRef domMaterial_specularRef; + typedef daeTArray domMaterial_specular_Array; + + class domMaterial_specular : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MATERIAL_SPECULAR; } + static daeInt ID() { return 528; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4 reference of the value array attribute. + */ + domFloat4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4 reference of the value array attribute. + */ + const domFloat4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domMaterial_specular(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domMaterial_specular() {} + /** + * Overloaded assignment operator + */ + virtual domMaterial_specular &operator=( const domMaterial_specular &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domModel_view_matrix; + + typedef daeSmartRef domModel_view_matrixRef; + typedef daeTArray domModel_view_matrix_Array; + + class domModel_view_matrix : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MODEL_VIEW_MATRIX; } + static daeInt ID() { return 529; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4x4 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4x4 reference of the value array attribute. + */ + domFloat4x4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4x4 reference of the value array attribute. + */ + const domFloat4x4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4x4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domModel_view_matrix(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domModel_view_matrix() {} + /** + * Overloaded assignment operator + */ + virtual domModel_view_matrix &operator=( const domModel_view_matrix &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPoint_distance_attenuation; + + typedef daeSmartRef domPoint_distance_attenuationRef; + typedef daeTArray domPoint_distance_attenuation_Array; + + class domPoint_distance_attenuation : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::POINT_DISTANCE_ATTENUATION; } + static daeInt ID() { return 530; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat3 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat3 reference of the value array attribute. + */ + domFloat3 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat3 reference of the value array attribute. + */ + const domFloat3 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat3 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domPoint_distance_attenuation(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domPoint_distance_attenuation() {} + /** + * Overloaded assignment operator + */ + virtual domPoint_distance_attenuation &operator=( const domPoint_distance_attenuation &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPoint_fade_threshold_size; + + typedef daeSmartRef domPoint_fade_threshold_sizeRef; + typedef daeTArray domPoint_fade_threshold_size_Array; + + class domPoint_fade_threshold_size : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::POINT_FADE_THRESHOLD_SIZE; } + static daeInt ID() { return 531; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domPoint_fade_threshold_size(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domPoint_fade_threshold_size() {} + /** + * Overloaded assignment operator + */ + virtual domPoint_fade_threshold_size &operator=( const domPoint_fade_threshold_size &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPoint_size; + + typedef daeSmartRef domPoint_sizeRef; + typedef daeTArray domPoint_size_Array; + + class domPoint_size : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::POINT_SIZE; } + static daeInt ID() { return 532; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domPoint_size(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domPoint_size() {} + /** + * Overloaded assignment operator + */ + virtual domPoint_size &operator=( const domPoint_size &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPoint_size_min; + + typedef daeSmartRef domPoint_size_minRef; + typedef daeTArray domPoint_size_min_Array; + + class domPoint_size_min : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::POINT_SIZE_MIN; } + static daeInt ID() { return 533; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domPoint_size_min(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domPoint_size_min() {} + /** + * Overloaded assignment operator + */ + virtual domPoint_size_min &operator=( const domPoint_size_min &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPoint_size_max; + + typedef daeSmartRef domPoint_size_maxRef; + typedef daeTArray domPoint_size_max_Array; + + class domPoint_size_max : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::POINT_SIZE_MAX; } + static daeInt ID() { return 534; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domFloat of the value attribute. + */ + domFloat getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domFloat atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domPoint_size_max(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domPoint_size_max() {} + /** + * Overloaded assignment operator + */ + virtual domPoint_size_max &operator=( const domPoint_size_max &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPolygon_offset; + + typedef daeSmartRef domPolygon_offsetRef; + typedef daeTArray domPolygon_offset_Array; + + class domPolygon_offset : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::POLYGON_OFFSET; } + static daeInt ID() { return 535; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat2 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat2 reference of the value array attribute. + */ + domFloat2 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat2 reference of the value array attribute. + */ + const domFloat2 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat2 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domPolygon_offset(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domPolygon_offset() {} + /** + * Overloaded assignment operator + */ + virtual domPolygon_offset &operator=( const domPolygon_offset &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domProjection_matrix; + + typedef daeSmartRef domProjection_matrixRef; + typedef daeTArray domProjection_matrix_Array; + + class domProjection_matrix : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PROJECTION_MATRIX; } + static daeInt ID() { return 536; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domFloat4x4 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4x4 reference of the value array attribute. + */ + domFloat4x4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4x4 reference of the value array attribute. + */ + const domFloat4x4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4x4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domProjection_matrix(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domProjection_matrix() {} + /** + * Overloaded assignment operator + */ + virtual domProjection_matrix &operator=( const domProjection_matrix &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domScissor; + + typedef daeSmartRef domScissorRef; + typedef daeTArray domScissor_Array; + + class domScissor : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SCISSOR; } + static daeInt ID() { return 537; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domInt4 attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domInt4 reference of the value array attribute. + */ + domInt4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domInt4 reference of the value array attribute. + */ + const domInt4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domInt4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domScissor(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domScissor() {} + /** + * Overloaded assignment operator + */ + virtual domScissor &operator=( const domScissor &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domShade_model; + + typedef daeSmartRef domShade_modelRef; + typedef daeTArray domShade_model_Array; + + class domShade_model : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SHADE_MODEL; } + static daeInt ID() { return 538; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_shade_model_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_shade_model_type of the value attribute. + */ + domGl_shade_model_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_shade_model_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domShade_model(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domShade_model() {} + /** + * Overloaded assignment operator + */ + virtual domShade_model &operator=( const domShade_model &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domStencil_func; + + typedef daeSmartRef domStencil_funcRef; + typedef daeTArray domStencil_func_Array; + + class domStencil_func : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::STENCIL_FUNC; } + static daeInt ID() { return 539; } + virtual daeInt typeID() const { return ID(); } + public: + class domFunc; + + typedef daeSmartRef domFuncRef; + typedef daeTArray domFunc_Array; + + class domFunc : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FUNC; } + static daeInt ID() { return 540; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGl_func_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGl_func_type of the value attribute. + */ + domGl_func_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGl_func_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domFunc(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domFunc() {} + /** + * Overloaded assignment operator + */ + virtual domFunc &operator=( const domFunc &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domRef; + + typedef daeSmartRef domRefRef; + typedef daeTArray domRef_Array; + + class domRef : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::REF; } + static daeInt ID() { return 541; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + xsUnsignedByte attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a xsUnsignedByte of the value attribute. + */ + xsUnsignedByte getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( xsUnsignedByte atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domRef(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domRef() {} + /** + * Overloaded assignment operator + */ + virtual domRef &operator=( const domRef &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMask; + + typedef daeSmartRef domMaskRef; + typedef daeTArray domMask_Array; + + class domMask : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MASK; } + static daeInt ID() { return 542; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + xsUnsignedByte attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a xsUnsignedByte of the value attribute. + */ + xsUnsignedByte getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( xsUnsignedByte atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domMask(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domMask() {} + /** + * Overloaded assignment operator + */ + virtual domMask &operator=( const domMask &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements + domFuncRef elemFunc; + domRefRef elemRef; + domMaskRef elemMask; + + public: //Accessors and Mutators + /** + * Gets the func element. + * @return a daeSmartRef to the func element. + */ + const domFuncRef getFunc() const { return elemFunc; } + /** + * Gets the ref element. + * @return a daeSmartRef to the ref element. + */ + const domRefRef getRef() const { return elemRef; } + /** + * Gets the mask element. + * @return a daeSmartRef to the mask element. + */ + const domMaskRef getMask() const { return elemMask; } + protected: + /** + * Constructor + */ + domStencil_func(DAE& dae) : daeElement(dae), elemFunc(), elemRef(), elemMask() {} + /** + * Destructor + */ + virtual ~domStencil_func() {} + /** + * Overloaded assignment operator + */ + virtual domStencil_func &operator=( const domStencil_func &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domStencil_mask; + + typedef daeSmartRef domStencil_maskRef; + typedef daeTArray domStencil_mask_Array; + + class domStencil_mask : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::STENCIL_MASK; } + static daeInt ID() { return 543; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domInt attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domInt of the value attribute. + */ + domInt getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domInt atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domStencil_mask(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domStencil_mask() {} + /** + * Overloaded assignment operator + */ + virtual domStencil_mask &operator=( const domStencil_mask &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domStencil_op; + + typedef daeSmartRef domStencil_opRef; + typedef daeTArray domStencil_op_Array; + + class domStencil_op : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::STENCIL_OP; } + static daeInt ID() { return 544; } + virtual daeInt typeID() const { return ID(); } + public: + class domFail; + + typedef daeSmartRef domFailRef; + typedef daeTArray domFail_Array; + + class domFail : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FAIL; } + static daeInt ID() { return 545; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGles_stencil_op_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGles_stencil_op_type of the value attribute. + */ + domGles_stencil_op_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGles_stencil_op_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domFail(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domFail() {} + /** + * Overloaded assignment operator + */ + virtual domFail &operator=( const domFail &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domZfail; + + typedef daeSmartRef domZfailRef; + typedef daeTArray domZfail_Array; + + class domZfail : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ZFAIL; } + static daeInt ID() { return 546; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGles_stencil_op_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGles_stencil_op_type of the value attribute. + */ + domGles_stencil_op_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGles_stencil_op_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domZfail(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domZfail() {} + /** + * Overloaded assignment operator + */ + virtual domZfail &operator=( const domZfail &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domZpass; + + typedef daeSmartRef domZpassRef; + typedef daeTArray domZpass_Array; + + class domZpass : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ZPASS; } + static daeInt ID() { return 547; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domGles_stencil_op_type attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domGles_stencil_op_type of the value attribute. + */ + domGles_stencil_op_type getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domGles_stencil_op_type atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domZpass(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domZpass() {} + /** + * Overloaded assignment operator + */ + virtual domZpass &operator=( const domZpass &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements + domFailRef elemFail; + domZfailRef elemZfail; + domZpassRef elemZpass; + + public: //Accessors and Mutators + /** + * Gets the fail element. + * @return a daeSmartRef to the fail element. + */ + const domFailRef getFail() const { return elemFail; } + /** + * Gets the zfail element. + * @return a daeSmartRef to the zfail element. + */ + const domZfailRef getZfail() const { return elemZfail; } + /** + * Gets the zpass element. + * @return a daeSmartRef to the zpass element. + */ + const domZpassRef getZpass() const { return elemZpass; } + protected: + /** + * Constructor + */ + domStencil_op(DAE& dae) : daeElement(dae), elemFail(), elemZfail(), elemZpass() {} + /** + * Destructor + */ + virtual ~domStencil_op() {} + /** + * Overloaded assignment operator + */ + virtual domStencil_op &operator=( const domStencil_op &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domAlpha_test_enable; + + typedef daeSmartRef domAlpha_test_enableRef; + typedef daeTArray domAlpha_test_enable_Array; + + class domAlpha_test_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ALPHA_TEST_ENABLE; } + static daeInt ID() { return 548; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domAlpha_test_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domAlpha_test_enable() {} + /** + * Overloaded assignment operator + */ + virtual domAlpha_test_enable &operator=( const domAlpha_test_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBlend_enable; + + typedef daeSmartRef domBlend_enableRef; + typedef daeTArray domBlend_enable_Array; + + class domBlend_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BLEND_ENABLE; } + static daeInt ID() { return 549; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domBlend_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domBlend_enable() {} + /** + * Overloaded assignment operator + */ + virtual domBlend_enable &operator=( const domBlend_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domClip_plane_enable; + + typedef daeSmartRef domClip_plane_enableRef; + typedef daeTArray domClip_plane_enable_Array; + + class domClip_plane_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CLIP_PLANE_ENABLE; } + static daeInt ID() { return 550; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + domGLES_MAX_CLIP_PLANES_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGLES_MAX_CLIP_PLANES_index of the index attribute. + */ + domGLES_MAX_CLIP_PLANES_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGLES_MAX_CLIP_PLANES_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domClip_plane_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domClip_plane_enable() {} + /** + * Overloaded assignment operator + */ + virtual domClip_plane_enable &operator=( const domClip_plane_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domColor_logic_op_enable; + + typedef daeSmartRef domColor_logic_op_enableRef; + typedef daeTArray domColor_logic_op_enable_Array; + + class domColor_logic_op_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::COLOR_LOGIC_OP_ENABLE; } + static daeInt ID() { return 551; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domColor_logic_op_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domColor_logic_op_enable() {} + /** + * Overloaded assignment operator + */ + virtual domColor_logic_op_enable &operator=( const domColor_logic_op_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domColor_material_enable; + + typedef daeSmartRef domColor_material_enableRef; + typedef daeTArray domColor_material_enable_Array; + + class domColor_material_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::COLOR_MATERIAL_ENABLE; } + static daeInt ID() { return 552; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domColor_material_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domColor_material_enable() {} + /** + * Overloaded assignment operator + */ + virtual domColor_material_enable &operator=( const domColor_material_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domCull_face_enable; + + typedef daeSmartRef domCull_face_enableRef; + typedef daeTArray domCull_face_enable_Array; + + class domCull_face_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CULL_FACE_ENABLE; } + static daeInt ID() { return 553; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domCull_face_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domCull_face_enable() {} + /** + * Overloaded assignment operator + */ + virtual domCull_face_enable &operator=( const domCull_face_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domDepth_test_enable; + + typedef daeSmartRef domDepth_test_enableRef; + typedef daeTArray domDepth_test_enable_Array; + + class domDepth_test_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::DEPTH_TEST_ENABLE; } + static daeInt ID() { return 554; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domDepth_test_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domDepth_test_enable() {} + /** + * Overloaded assignment operator + */ + virtual domDepth_test_enable &operator=( const domDepth_test_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domDither_enable; + + typedef daeSmartRef domDither_enableRef; + typedef daeTArray domDither_enable_Array; + + class domDither_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::DITHER_ENABLE; } + static daeInt ID() { return 555; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domDither_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domDither_enable() {} + /** + * Overloaded assignment operator + */ + virtual domDither_enable &operator=( const domDither_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFog_enable; + + typedef daeSmartRef domFog_enableRef; + typedef daeTArray domFog_enable_Array; + + class domFog_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FOG_ENABLE; } + static daeInt ID() { return 556; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domFog_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domFog_enable() {} + /** + * Overloaded assignment operator + */ + virtual domFog_enable &operator=( const domFog_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domTexture_pipeline_enable; + + typedef daeSmartRef domTexture_pipeline_enableRef; + typedef daeTArray domTexture_pipeline_enable_Array; + + class domTexture_pipeline_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TEXTURE_PIPELINE_ENABLE; } + static daeInt ID() { return 557; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domTexture_pipeline_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domTexture_pipeline_enable() {} + /** + * Overloaded assignment operator + */ + virtual domTexture_pipeline_enable &operator=( const domTexture_pipeline_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_enable; + + typedef daeSmartRef domLight_enableRef; + typedef daeTArray domLight_enable_Array; + + class domLight_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_ENABLE; } + static daeInt ID() { return 558; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + domGLES_MAX_LIGHTS_index attrIndex; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + /** + * Gets the index attribute. + * @return Returns a domGLES_MAX_LIGHTS_index of the index attribute. + */ + domGLES_MAX_LIGHTS_index getIndex() const { return attrIndex; } + /** + * Sets the index attribute. + * @param atIndex The new value for the index attribute. + */ + void setIndex( domGLES_MAX_LIGHTS_index atIndex ) { attrIndex = atIndex; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domLight_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam(), attrIndex() {} + /** + * Destructor + */ + virtual ~domLight_enable() {} + /** + * Overloaded assignment operator + */ + virtual domLight_enable &operator=( const domLight_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLighting_enable; + + typedef daeSmartRef domLighting_enableRef; + typedef daeTArray domLighting_enable_Array; + + class domLighting_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHTING_ENABLE; } + static daeInt ID() { return 559; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domLighting_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domLighting_enable() {} + /** + * Overloaded assignment operator + */ + virtual domLighting_enable &operator=( const domLighting_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLight_model_two_side_enable; + + typedef daeSmartRef domLight_model_two_side_enableRef; + typedef daeTArray domLight_model_two_side_enable_Array; + + class domLight_model_two_side_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT_MODEL_TWO_SIDE_ENABLE; } + static daeInt ID() { return 560; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domLight_model_two_side_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domLight_model_two_side_enable() {} + /** + * Overloaded assignment operator + */ + virtual domLight_model_two_side_enable &operator=( const domLight_model_two_side_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLine_smooth_enable; + + typedef daeSmartRef domLine_smooth_enableRef; + typedef daeTArray domLine_smooth_enable_Array; + + class domLine_smooth_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LINE_SMOOTH_ENABLE; } + static daeInt ID() { return 561; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domLine_smooth_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domLine_smooth_enable() {} + /** + * Overloaded assignment operator + */ + virtual domLine_smooth_enable &operator=( const domLine_smooth_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMultisample_enable; + + typedef daeSmartRef domMultisample_enableRef; + typedef daeTArray domMultisample_enable_Array; + + class domMultisample_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MULTISAMPLE_ENABLE; } + static daeInt ID() { return 562; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domMultisample_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domMultisample_enable() {} + /** + * Overloaded assignment operator + */ + virtual domMultisample_enable &operator=( const domMultisample_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domNormalize_enable; + + typedef daeSmartRef domNormalize_enableRef; + typedef daeTArray domNormalize_enable_Array; + + class domNormalize_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::NORMALIZE_ENABLE; } + static daeInt ID() { return 563; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domNormalize_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domNormalize_enable() {} + /** + * Overloaded assignment operator + */ + virtual domNormalize_enable &operator=( const domNormalize_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPoint_smooth_enable; + + typedef daeSmartRef domPoint_smooth_enableRef; + typedef daeTArray domPoint_smooth_enable_Array; + + class domPoint_smooth_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::POINT_SMOOTH_ENABLE; } + static daeInt ID() { return 564; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domPoint_smooth_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domPoint_smooth_enable() {} + /** + * Overloaded assignment operator + */ + virtual domPoint_smooth_enable &operator=( const domPoint_smooth_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPolygon_offset_fill_enable; + + typedef daeSmartRef domPolygon_offset_fill_enableRef; + typedef daeTArray domPolygon_offset_fill_enable_Array; + + class domPolygon_offset_fill_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::POLYGON_OFFSET_FILL_ENABLE; } + static daeInt ID() { return 565; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domPolygon_offset_fill_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domPolygon_offset_fill_enable() {} + /** + * Overloaded assignment operator + */ + virtual domPolygon_offset_fill_enable &operator=( const domPolygon_offset_fill_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domRescale_normal_enable; + + typedef daeSmartRef domRescale_normal_enableRef; + typedef daeTArray domRescale_normal_enable_Array; + + class domRescale_normal_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::RESCALE_NORMAL_ENABLE; } + static daeInt ID() { return 566; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domRescale_normal_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domRescale_normal_enable() {} + /** + * Overloaded assignment operator + */ + virtual domRescale_normal_enable &operator=( const domRescale_normal_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domSample_alpha_to_coverage_enable; + + typedef daeSmartRef domSample_alpha_to_coverage_enableRef; + typedef daeTArray domSample_alpha_to_coverage_enable_Array; + + class domSample_alpha_to_coverage_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SAMPLE_ALPHA_TO_COVERAGE_ENABLE; } + static daeInt ID() { return 567; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domSample_alpha_to_coverage_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domSample_alpha_to_coverage_enable() {} + /** + * Overloaded assignment operator + */ + virtual domSample_alpha_to_coverage_enable &operator=( const domSample_alpha_to_coverage_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domSample_alpha_to_one_enable; + + typedef daeSmartRef domSample_alpha_to_one_enableRef; + typedef daeTArray domSample_alpha_to_one_enable_Array; + + class domSample_alpha_to_one_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SAMPLE_ALPHA_TO_ONE_ENABLE; } + static daeInt ID() { return 568; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domSample_alpha_to_one_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domSample_alpha_to_one_enable() {} + /** + * Overloaded assignment operator + */ + virtual domSample_alpha_to_one_enable &operator=( const domSample_alpha_to_one_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domSample_coverage_enable; + + typedef daeSmartRef domSample_coverage_enableRef; + typedef daeTArray domSample_coverage_enable_Array; + + class domSample_coverage_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SAMPLE_COVERAGE_ENABLE; } + static daeInt ID() { return 569; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domSample_coverage_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domSample_coverage_enable() {} + /** + * Overloaded assignment operator + */ + virtual domSample_coverage_enable &operator=( const domSample_coverage_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domScissor_test_enable; + + typedef daeSmartRef domScissor_test_enableRef; + typedef daeTArray domScissor_test_enable_Array; + + class domScissor_test_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SCISSOR_TEST_ENABLE; } + static daeInt ID() { return 570; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domScissor_test_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domScissor_test_enable() {} + /** + * Overloaded assignment operator + */ + virtual domScissor_test_enable &operator=( const domScissor_test_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domStencil_test_enable; + + typedef daeSmartRef domStencil_test_enableRef; + typedef daeTArray domStencil_test_enable_Array; + + class domStencil_test_enable : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::STENCIL_TEST_ENABLE; } + static daeInt ID() { return 571; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes + domBool attrValue; + xsNCName attrParam; + + + public: //Accessors and Mutators + /** + * Gets the value attribute. + * @return Returns a domBool of the value attribute. + */ + domBool getValue() const { return attrValue; } + /** + * Sets the value attribute. + * @param atValue The new value for the value attribute. + */ + void setValue( domBool atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domStencil_test_enable(DAE& dae) : daeElement(dae), attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domStencil_test_enable() {} + /** + * Overloaded assignment operator + */ + virtual domStencil_test_enable &operator=( const domStencil_test_enable &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements + domAlpha_funcRef elemAlpha_func; + domBlend_funcRef elemBlend_func; + domClear_colorRef elemClear_color; + domClear_stencilRef elemClear_stencil; + domClear_depthRef elemClear_depth; + domClip_planeRef elemClip_plane; + domColor_maskRef elemColor_mask; + domCull_faceRef elemCull_face; + domDepth_funcRef elemDepth_func; + domDepth_maskRef elemDepth_mask; + domDepth_rangeRef elemDepth_range; + domFog_colorRef elemFog_color; + domFog_densityRef elemFog_density; + domFog_modeRef elemFog_mode; + domFog_startRef elemFog_start; + domFog_endRef elemFog_end; + domFront_faceRef elemFront_face; + domTexture_pipelineRef elemTexture_pipeline; + domLogic_opRef elemLogic_op; + domLight_ambientRef elemLight_ambient; + domLight_diffuseRef elemLight_diffuse; + domLight_specularRef elemLight_specular; + domLight_positionRef elemLight_position; + domLight_constant_attenuationRef elemLight_constant_attenuation; + domLight_linear_attenutationRef elemLight_linear_attenutation; + domLight_quadratic_attenuationRef elemLight_quadratic_attenuation; + domLight_spot_cutoffRef elemLight_spot_cutoff; + domLight_spot_directionRef elemLight_spot_direction; + domLight_spot_exponentRef elemLight_spot_exponent; + domLight_model_ambientRef elemLight_model_ambient; + domLine_widthRef elemLine_width; + domMaterial_ambientRef elemMaterial_ambient; + domMaterial_diffuseRef elemMaterial_diffuse; + domMaterial_emissionRef elemMaterial_emission; + domMaterial_shininessRef elemMaterial_shininess; + domMaterial_specularRef elemMaterial_specular; + domModel_view_matrixRef elemModel_view_matrix; + domPoint_distance_attenuationRef elemPoint_distance_attenuation; + domPoint_fade_threshold_sizeRef elemPoint_fade_threshold_size; + domPoint_sizeRef elemPoint_size; + domPoint_size_minRef elemPoint_size_min; + domPoint_size_maxRef elemPoint_size_max; + domPolygon_offsetRef elemPolygon_offset; + domProjection_matrixRef elemProjection_matrix; + domScissorRef elemScissor; + domShade_modelRef elemShade_model; + domStencil_funcRef elemStencil_func; + domStencil_maskRef elemStencil_mask; + domStencil_opRef elemStencil_op; + domAlpha_test_enableRef elemAlpha_test_enable; + domBlend_enableRef elemBlend_enable; + domClip_plane_enableRef elemClip_plane_enable; + domColor_logic_op_enableRef elemColor_logic_op_enable; + domColor_material_enableRef elemColor_material_enable; + domCull_face_enableRef elemCull_face_enable; + domDepth_test_enableRef elemDepth_test_enable; + domDither_enableRef elemDither_enable; + domFog_enableRef elemFog_enable; + domTexture_pipeline_enableRef elemTexture_pipeline_enable; + domLight_enableRef elemLight_enable; + domLighting_enableRef elemLighting_enable; + domLight_model_two_side_enableRef elemLight_model_two_side_enable; + domLine_smooth_enableRef elemLine_smooth_enable; + domMultisample_enableRef elemMultisample_enable; + domNormalize_enableRef elemNormalize_enable; + domPoint_smooth_enableRef elemPoint_smooth_enable; + domPolygon_offset_fill_enableRef elemPolygon_offset_fill_enable; + domRescale_normal_enableRef elemRescale_normal_enable; + domSample_alpha_to_coverage_enableRef elemSample_alpha_to_coverage_enable; + domSample_alpha_to_one_enableRef elemSample_alpha_to_one_enable; + domSample_coverage_enableRef elemSample_coverage_enable; + domScissor_test_enableRef elemScissor_test_enable; + domStencil_test_enableRef elemStencil_test_enable; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the alpha_func element. + * @return a daeSmartRef to the alpha_func element. + */ + const domAlpha_funcRef getAlpha_func() const { return elemAlpha_func; } + /** + * Gets the blend_func element. + * @return a daeSmartRef to the blend_func element. + */ + const domBlend_funcRef getBlend_func() const { return elemBlend_func; } + /** + * Gets the clear_color element. + * @return a daeSmartRef to the clear_color element. + */ + const domClear_colorRef getClear_color() const { return elemClear_color; } + /** + * Gets the clear_stencil element. + * @return a daeSmartRef to the clear_stencil element. + */ + const domClear_stencilRef getClear_stencil() const { return elemClear_stencil; } + /** + * Gets the clear_depth element. + * @return a daeSmartRef to the clear_depth element. + */ + const domClear_depthRef getClear_depth() const { return elemClear_depth; } + /** + * Gets the clip_plane element. + * @return a daeSmartRef to the clip_plane element. + */ + const domClip_planeRef getClip_plane() const { return elemClip_plane; } + /** + * Gets the color_mask element. + * @return a daeSmartRef to the color_mask element. + */ + const domColor_maskRef getColor_mask() const { return elemColor_mask; } + /** + * Gets the cull_face element. + * @return a daeSmartRef to the cull_face element. + */ + const domCull_faceRef getCull_face() const { return elemCull_face; } + /** + * Gets the depth_func element. + * @return a daeSmartRef to the depth_func element. + */ + const domDepth_funcRef getDepth_func() const { return elemDepth_func; } + /** + * Gets the depth_mask element. + * @return a daeSmartRef to the depth_mask element. + */ + const domDepth_maskRef getDepth_mask() const { return elemDepth_mask; } + /** + * Gets the depth_range element. + * @return a daeSmartRef to the depth_range element. + */ + const domDepth_rangeRef getDepth_range() const { return elemDepth_range; } + /** + * Gets the fog_color element. + * @return a daeSmartRef to the fog_color element. + */ + const domFog_colorRef getFog_color() const { return elemFog_color; } + /** + * Gets the fog_density element. + * @return a daeSmartRef to the fog_density element. + */ + const domFog_densityRef getFog_density() const { return elemFog_density; } + /** + * Gets the fog_mode element. + * @return a daeSmartRef to the fog_mode element. + */ + const domFog_modeRef getFog_mode() const { return elemFog_mode; } + /** + * Gets the fog_start element. + * @return a daeSmartRef to the fog_start element. + */ + const domFog_startRef getFog_start() const { return elemFog_start; } + /** + * Gets the fog_end element. + * @return a daeSmartRef to the fog_end element. + */ + const domFog_endRef getFog_end() const { return elemFog_end; } + /** + * Gets the front_face element. + * @return a daeSmartRef to the front_face element. + */ + const domFront_faceRef getFront_face() const { return elemFront_face; } + /** + * Gets the texture_pipeline element. + * @return a daeSmartRef to the texture_pipeline element. + */ + const domTexture_pipelineRef getTexture_pipeline() const { return elemTexture_pipeline; } + /** + * Gets the logic_op element. + * @return a daeSmartRef to the logic_op element. + */ + const domLogic_opRef getLogic_op() const { return elemLogic_op; } + /** + * Gets the light_ambient element. + * @return a daeSmartRef to the light_ambient element. + */ + const domLight_ambientRef getLight_ambient() const { return elemLight_ambient; } + /** + * Gets the light_diffuse element. + * @return a daeSmartRef to the light_diffuse element. + */ + const domLight_diffuseRef getLight_diffuse() const { return elemLight_diffuse; } + /** + * Gets the light_specular element. + * @return a daeSmartRef to the light_specular element. + */ + const domLight_specularRef getLight_specular() const { return elemLight_specular; } + /** + * Gets the light_position element. + * @return a daeSmartRef to the light_position element. + */ + const domLight_positionRef getLight_position() const { return elemLight_position; } + /** + * Gets the light_constant_attenuation element. + * @return a daeSmartRef to the light_constant_attenuation element. + */ + const domLight_constant_attenuationRef getLight_constant_attenuation() const { return elemLight_constant_attenuation; } + /** + * Gets the light_linear_attenutation element. + * @return a daeSmartRef to the light_linear_attenutation element. + */ + const domLight_linear_attenutationRef getLight_linear_attenutation() const { return elemLight_linear_attenutation; } + /** + * Gets the light_quadratic_attenuation element. + * @return a daeSmartRef to the light_quadratic_attenuation element. + */ + const domLight_quadratic_attenuationRef getLight_quadratic_attenuation() const { return elemLight_quadratic_attenuation; } + /** + * Gets the light_spot_cutoff element. + * @return a daeSmartRef to the light_spot_cutoff element. + */ + const domLight_spot_cutoffRef getLight_spot_cutoff() const { return elemLight_spot_cutoff; } + /** + * Gets the light_spot_direction element. + * @return a daeSmartRef to the light_spot_direction element. + */ + const domLight_spot_directionRef getLight_spot_direction() const { return elemLight_spot_direction; } + /** + * Gets the light_spot_exponent element. + * @return a daeSmartRef to the light_spot_exponent element. + */ + const domLight_spot_exponentRef getLight_spot_exponent() const { return elemLight_spot_exponent; } + /** + * Gets the light_model_ambient element. + * @return a daeSmartRef to the light_model_ambient element. + */ + const domLight_model_ambientRef getLight_model_ambient() const { return elemLight_model_ambient; } + /** + * Gets the line_width element. + * @return a daeSmartRef to the line_width element. + */ + const domLine_widthRef getLine_width() const { return elemLine_width; } + /** + * Gets the material_ambient element. + * @return a daeSmartRef to the material_ambient element. + */ + const domMaterial_ambientRef getMaterial_ambient() const { return elemMaterial_ambient; } + /** + * Gets the material_diffuse element. + * @return a daeSmartRef to the material_diffuse element. + */ + const domMaterial_diffuseRef getMaterial_diffuse() const { return elemMaterial_diffuse; } + /** + * Gets the material_emission element. + * @return a daeSmartRef to the material_emission element. + */ + const domMaterial_emissionRef getMaterial_emission() const { return elemMaterial_emission; } + /** + * Gets the material_shininess element. + * @return a daeSmartRef to the material_shininess element. + */ + const domMaterial_shininessRef getMaterial_shininess() const { return elemMaterial_shininess; } + /** + * Gets the material_specular element. + * @return a daeSmartRef to the material_specular element. + */ + const domMaterial_specularRef getMaterial_specular() const { return elemMaterial_specular; } + /** + * Gets the model_view_matrix element. + * @return a daeSmartRef to the model_view_matrix element. + */ + const domModel_view_matrixRef getModel_view_matrix() const { return elemModel_view_matrix; } + /** + * Gets the point_distance_attenuation element. + * @return a daeSmartRef to the point_distance_attenuation element. + */ + const domPoint_distance_attenuationRef getPoint_distance_attenuation() const { return elemPoint_distance_attenuation; } + /** + * Gets the point_fade_threshold_size element. + * @return a daeSmartRef to the point_fade_threshold_size element. + */ + const domPoint_fade_threshold_sizeRef getPoint_fade_threshold_size() const { return elemPoint_fade_threshold_size; } + /** + * Gets the point_size element. + * @return a daeSmartRef to the point_size element. + */ + const domPoint_sizeRef getPoint_size() const { return elemPoint_size; } + /** + * Gets the point_size_min element. + * @return a daeSmartRef to the point_size_min element. + */ + const domPoint_size_minRef getPoint_size_min() const { return elemPoint_size_min; } + /** + * Gets the point_size_max element. + * @return a daeSmartRef to the point_size_max element. + */ + const domPoint_size_maxRef getPoint_size_max() const { return elemPoint_size_max; } + /** + * Gets the polygon_offset element. + * @return a daeSmartRef to the polygon_offset element. + */ + const domPolygon_offsetRef getPolygon_offset() const { return elemPolygon_offset; } + /** + * Gets the projection_matrix element. + * @return a daeSmartRef to the projection_matrix element. + */ + const domProjection_matrixRef getProjection_matrix() const { return elemProjection_matrix; } + /** + * Gets the scissor element. + * @return a daeSmartRef to the scissor element. + */ + const domScissorRef getScissor() const { return elemScissor; } + /** + * Gets the shade_model element. + * @return a daeSmartRef to the shade_model element. + */ + const domShade_modelRef getShade_model() const { return elemShade_model; } + /** + * Gets the stencil_func element. + * @return a daeSmartRef to the stencil_func element. + */ + const domStencil_funcRef getStencil_func() const { return elemStencil_func; } + /** + * Gets the stencil_mask element. + * @return a daeSmartRef to the stencil_mask element. + */ + const domStencil_maskRef getStencil_mask() const { return elemStencil_mask; } + /** + * Gets the stencil_op element. + * @return a daeSmartRef to the stencil_op element. + */ + const domStencil_opRef getStencil_op() const { return elemStencil_op; } + /** + * Gets the alpha_test_enable element. + * @return a daeSmartRef to the alpha_test_enable element. + */ + const domAlpha_test_enableRef getAlpha_test_enable() const { return elemAlpha_test_enable; } + /** + * Gets the blend_enable element. + * @return a daeSmartRef to the blend_enable element. + */ + const domBlend_enableRef getBlend_enable() const { return elemBlend_enable; } + /** + * Gets the clip_plane_enable element. + * @return a daeSmartRef to the clip_plane_enable element. + */ + const domClip_plane_enableRef getClip_plane_enable() const { return elemClip_plane_enable; } + /** + * Gets the color_logic_op_enable element. + * @return a daeSmartRef to the color_logic_op_enable element. + */ + const domColor_logic_op_enableRef getColor_logic_op_enable() const { return elemColor_logic_op_enable; } + /** + * Gets the color_material_enable element. + * @return a daeSmartRef to the color_material_enable element. + */ + const domColor_material_enableRef getColor_material_enable() const { return elemColor_material_enable; } + /** + * Gets the cull_face_enable element. + * @return a daeSmartRef to the cull_face_enable element. + */ + const domCull_face_enableRef getCull_face_enable() const { return elemCull_face_enable; } + /** + * Gets the depth_test_enable element. + * @return a daeSmartRef to the depth_test_enable element. + */ + const domDepth_test_enableRef getDepth_test_enable() const { return elemDepth_test_enable; } + /** + * Gets the dither_enable element. + * @return a daeSmartRef to the dither_enable element. + */ + const domDither_enableRef getDither_enable() const { return elemDither_enable; } + /** + * Gets the fog_enable element. + * @return a daeSmartRef to the fog_enable element. + */ + const domFog_enableRef getFog_enable() const { return elemFog_enable; } + /** + * Gets the texture_pipeline_enable element. + * @return a daeSmartRef to the texture_pipeline_enable element. + */ + const domTexture_pipeline_enableRef getTexture_pipeline_enable() const { return elemTexture_pipeline_enable; } + /** + * Gets the light_enable element. + * @return a daeSmartRef to the light_enable element. + */ + const domLight_enableRef getLight_enable() const { return elemLight_enable; } + /** + * Gets the lighting_enable element. + * @return a daeSmartRef to the lighting_enable element. + */ + const domLighting_enableRef getLighting_enable() const { return elemLighting_enable; } + /** + * Gets the light_model_two_side_enable element. + * @return a daeSmartRef to the light_model_two_side_enable element. + */ + const domLight_model_two_side_enableRef getLight_model_two_side_enable() const { return elemLight_model_two_side_enable; } + /** + * Gets the line_smooth_enable element. + * @return a daeSmartRef to the line_smooth_enable element. + */ + const domLine_smooth_enableRef getLine_smooth_enable() const { return elemLine_smooth_enable; } + /** + * Gets the multisample_enable element. + * @return a daeSmartRef to the multisample_enable element. + */ + const domMultisample_enableRef getMultisample_enable() const { return elemMultisample_enable; } + /** + * Gets the normalize_enable element. + * @return a daeSmartRef to the normalize_enable element. + */ + const domNormalize_enableRef getNormalize_enable() const { return elemNormalize_enable; } + /** + * Gets the point_smooth_enable element. + * @return a daeSmartRef to the point_smooth_enable element. + */ + const domPoint_smooth_enableRef getPoint_smooth_enable() const { return elemPoint_smooth_enable; } + /** + * Gets the polygon_offset_fill_enable element. + * @return a daeSmartRef to the polygon_offset_fill_enable element. + */ + const domPolygon_offset_fill_enableRef getPolygon_offset_fill_enable() const { return elemPolygon_offset_fill_enable; } + /** + * Gets the rescale_normal_enable element. + * @return a daeSmartRef to the rescale_normal_enable element. + */ + const domRescale_normal_enableRef getRescale_normal_enable() const { return elemRescale_normal_enable; } + /** + * Gets the sample_alpha_to_coverage_enable element. + * @return a daeSmartRef to the sample_alpha_to_coverage_enable element. + */ + const domSample_alpha_to_coverage_enableRef getSample_alpha_to_coverage_enable() const { return elemSample_alpha_to_coverage_enable; } + /** + * Gets the sample_alpha_to_one_enable element. + * @return a daeSmartRef to the sample_alpha_to_one_enable element. + */ + const domSample_alpha_to_one_enableRef getSample_alpha_to_one_enable() const { return elemSample_alpha_to_one_enable; } + /** + * Gets the sample_coverage_enable element. + * @return a daeSmartRef to the sample_coverage_enable element. + */ + const domSample_coverage_enableRef getSample_coverage_enable() const { return elemSample_coverage_enable; } + /** + * Gets the scissor_test_enable element. + * @return a daeSmartRef to the scissor_test_enable element. + */ + const domScissor_test_enableRef getScissor_test_enable() const { return elemScissor_test_enable; } + /** + * Gets the stencil_test_enable element. + * @return a daeSmartRef to the stencil_test_enable element. + */ + const domStencil_test_enableRef getStencil_test_enable() const { return elemStencil_test_enable; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domGles_pipeline_settings(DAE& dae) : daeElement(dae), elemAlpha_func(), elemBlend_func(), elemClear_color(), elemClear_stencil(), elemClear_depth(), elemClip_plane(), elemColor_mask(), elemCull_face(), elemDepth_func(), elemDepth_mask(), elemDepth_range(), elemFog_color(), elemFog_density(), elemFog_mode(), elemFog_start(), elemFog_end(), elemFront_face(), elemTexture_pipeline(), elemLogic_op(), elemLight_ambient(), elemLight_diffuse(), elemLight_specular(), elemLight_position(), elemLight_constant_attenuation(), elemLight_linear_attenutation(), elemLight_quadratic_attenuation(), elemLight_spot_cutoff(), elemLight_spot_direction(), elemLight_spot_exponent(), elemLight_model_ambient(), elemLine_width(), elemMaterial_ambient(), elemMaterial_diffuse(), elemMaterial_emission(), elemMaterial_shininess(), elemMaterial_specular(), elemModel_view_matrix(), elemPoint_distance_attenuation(), elemPoint_fade_threshold_size(), elemPoint_size(), elemPoint_size_min(), elemPoint_size_max(), elemPolygon_offset(), elemProjection_matrix(), elemScissor(), elemShade_model(), elemStencil_func(), elemStencil_mask(), elemStencil_op(), elemAlpha_test_enable(), elemBlend_enable(), elemClip_plane_enable(), elemColor_logic_op_enable(), elemColor_material_enable(), elemCull_face_enable(), elemDepth_test_enable(), elemDither_enable(), elemFog_enable(), elemTexture_pipeline_enable(), elemLight_enable(), elemLighting_enable(), elemLight_model_two_side_enable(), elemLine_smooth_enable(), elemMultisample_enable(), elemNormalize_enable(), elemPoint_smooth_enable(), elemPolygon_offset_fill_enable(), elemRescale_normal_enable(), elemSample_alpha_to_coverage_enable(), elemSample_alpha_to_one_enable(), elemSample_coverage_enable(), elemScissor_test_enable(), elemStencil_test_enable() {} + /** + * Destructor + */ + virtual ~domGles_pipeline_settings() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domGles_pipeline_settings &operator=( const domGles_pipeline_settings &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGles_sampler_state.h b/Engine/lib/collada/include/1.4/dom/domGles_sampler_state.h new file mode 100644 index 000000000..65bdb4820 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGles_sampler_state.h @@ -0,0 +1,583 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGles_sampler_state_h__ +#define __domGles_sampler_state_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * Two-dimensional texture sampler state for profile_GLES. This is a bundle + * of sampler-specific states that will be referenced by one or more texture_units. + */ +class domGles_sampler_state_complexType +{ +public: + class domWrap_s; + + typedef daeSmartRef domWrap_sRef; + typedef daeTArray domWrap_s_Array; + + class domWrap_s : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::WRAP_S; } + static daeInt ID() { return 157; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domGles_sampler_wrap value of the text data of this element. + */ + domGles_sampler_wrap _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domGles_sampler_wrap of the value. + */ + domGles_sampler_wrap getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domGles_sampler_wrap val ) { _value = val; } + + protected: + /** + * Constructor + */ + domWrap_s(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domWrap_s() {} + /** + * Overloaded assignment operator + */ + virtual domWrap_s &operator=( const domWrap_s &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domWrap_t; + + typedef daeSmartRef domWrap_tRef; + typedef daeTArray domWrap_t_Array; + + class domWrap_t : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::WRAP_T; } + static daeInt ID() { return 158; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domGles_sampler_wrap value of the text data of this element. + */ + domGles_sampler_wrap _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domGles_sampler_wrap of the value. + */ + domGles_sampler_wrap getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domGles_sampler_wrap val ) { _value = val; } + + protected: + /** + * Constructor + */ + domWrap_t(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domWrap_t() {} + /** + * Overloaded assignment operator + */ + virtual domWrap_t &operator=( const domWrap_t &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMinfilter; + + typedef daeSmartRef domMinfilterRef; + typedef daeTArray domMinfilter_Array; + + class domMinfilter : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MINFILTER; } + static daeInt ID() { return 159; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_filter_common value of the text data of this element. + */ + domFx_sampler_filter_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_filter_common of the value. + */ + domFx_sampler_filter_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_filter_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMinfilter(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMinfilter() {} + /** + * Overloaded assignment operator + */ + virtual domMinfilter &operator=( const domMinfilter &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMagfilter; + + typedef daeSmartRef domMagfilterRef; + typedef daeTArray domMagfilter_Array; + + class domMagfilter : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MAGFILTER; } + static daeInt ID() { return 160; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_filter_common value of the text data of this element. + */ + domFx_sampler_filter_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_filter_common of the value. + */ + domFx_sampler_filter_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_filter_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMagfilter(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMagfilter() {} + /** + * Overloaded assignment operator + */ + virtual domMagfilter &operator=( const domMagfilter &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMipfilter; + + typedef daeSmartRef domMipfilterRef; + typedef daeTArray domMipfilter_Array; + + class domMipfilter : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MIPFILTER; } + static daeInt ID() { return 161; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_sampler_filter_common value of the text data of this element. + */ + domFx_sampler_filter_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_sampler_filter_common of the value. + */ + domFx_sampler_filter_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_sampler_filter_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMipfilter(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMipfilter() {} + /** + * Overloaded assignment operator + */ + virtual domMipfilter &operator=( const domMipfilter &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMipmap_maxlevel; + + typedef daeSmartRef domMipmap_maxlevelRef; + typedef daeTArray domMipmap_maxlevel_Array; + + class domMipmap_maxlevel : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MIPMAP_MAXLEVEL; } + static daeInt ID() { return 162; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsUnsignedByte value of the text data of this element. + */ + xsUnsignedByte _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a xsUnsignedByte of the value. + */ + xsUnsignedByte getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsUnsignedByte val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMipmap_maxlevel(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMipmap_maxlevel() {} + /** + * Overloaded assignment operator + */ + virtual domMipmap_maxlevel &operator=( const domMipmap_maxlevel &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMipmap_bias; + + typedef daeSmartRef domMipmap_biasRef; + typedef daeTArray domMipmap_bias_Array; + + class domMipmap_bias : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MIPMAP_BIAS; } + static daeInt ID() { return 163; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsFloat value of the text data of this element. + */ + xsFloat _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a xsFloat of the value. + */ + xsFloat getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsFloat val ) { _value = val; } + + protected: + /** + * Constructor + */ + domMipmap_bias(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domMipmap_bias() {} + /** + * Overloaded assignment operator + */ + virtual domMipmap_bias &operator=( const domMipmap_bias &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attribute +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; + +protected: // Elements + domWrap_sRef elemWrap_s; + domWrap_tRef elemWrap_t; + domMinfilterRef elemMinfilter; + domMagfilterRef elemMagfilter; + domMipfilterRef elemMipfilter; + domMipmap_maxlevelRef elemMipmap_maxlevel; + domMipmap_biasRef elemMipmap_bias; +/** + * The extra element may appear any number of times. OpenGL ES extensions + * may be used here. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid;} + + /** + * Gets the wrap_s element. + * @return a daeSmartRef to the wrap_s element. + */ + const domWrap_sRef getWrap_s() const { return elemWrap_s; } + /** + * Gets the wrap_t element. + * @return a daeSmartRef to the wrap_t element. + */ + const domWrap_tRef getWrap_t() const { return elemWrap_t; } + /** + * Gets the minfilter element. + * @return a daeSmartRef to the minfilter element. + */ + const domMinfilterRef getMinfilter() const { return elemMinfilter; } + /** + * Gets the magfilter element. + * @return a daeSmartRef to the magfilter element. + */ + const domMagfilterRef getMagfilter() const { return elemMagfilter; } + /** + * Gets the mipfilter element. + * @return a daeSmartRef to the mipfilter element. + */ + const domMipfilterRef getMipfilter() const { return elemMipfilter; } + /** + * Gets the mipmap_maxlevel element. + * @return a daeSmartRef to the mipmap_maxlevel element. + */ + const domMipmap_maxlevelRef getMipmap_maxlevel() const { return elemMipmap_maxlevel; } + /** + * Gets the mipmap_bias element. + * @return a daeSmartRef to the mipmap_bias element. + */ + const domMipmap_biasRef getMipmap_bias() const { return elemMipmap_bias; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domGles_sampler_state_complexType(DAE& dae, daeElement* elt) : attrSid(), elemWrap_s(), elemWrap_t(), elemMinfilter(), elemMagfilter(), elemMipfilter(), elemMipmap_maxlevel(), elemMipmap_bias(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domGles_sampler_state_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domGles_sampler_state_complexType &operator=( const domGles_sampler_state_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domGles_sampler_state_complexType. + */ +class domGles_sampler_state : public daeElement, public domGles_sampler_state_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GLES_SAMPLER_STATE; } + static daeInt ID() { return 164; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + +protected: + /** + * Constructor + */ + domGles_sampler_state(DAE& dae) : daeElement(dae), domGles_sampler_state_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domGles_sampler_state() {} + /** + * Overloaded assignment operator + */ + virtual domGles_sampler_state &operator=( const domGles_sampler_state &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGles_texcombiner_argumentAlpha_type.h b/Engine/lib/collada/include/1.4/dom/domGles_texcombiner_argumentAlpha_type.h new file mode 100644 index 000000000..162b44e13 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGles_texcombiner_argumentAlpha_type.h @@ -0,0 +1,153 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGles_texcombiner_argumentAlpha_type_h__ +#define __domGles_texcombiner_argumentAlpha_type_h__ + +#include +#include +#include + +class DAE; + +class domGles_texcombiner_argumentAlpha_type_complexType +{ +protected: // Attributes + domGles_texcombiner_source_enums attrSource; + domGles_texcombiner_operandAlpha_enums attrOperand; + xsNCName attrUnit; + + +public: //Accessors and Mutators + /** + * Gets the source attribute. + * @return Returns a domGles_texcombiner_source_enums of the source attribute. + */ + domGles_texcombiner_source_enums getSource() const { return attrSource; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( domGles_texcombiner_source_enums atSource ) { attrSource = atSource; } + + /** + * Gets the operand attribute. + * @return Returns a domGles_texcombiner_operandAlpha_enums of the operand attribute. + */ + domGles_texcombiner_operandAlpha_enums getOperand() const { return attrOperand; } + /** + * Sets the operand attribute. + * @param atOperand The new value for the operand attribute. + */ + void setOperand( domGles_texcombiner_operandAlpha_enums atOperand ) { attrOperand = atOperand; } + + /** + * Gets the unit attribute. + * @return Returns a xsNCName of the unit attribute. + */ + xsNCName getUnit() const { return attrUnit; } + /** + * Sets the unit attribute. + * @param atUnit The new value for the unit attribute. + */ + void setUnit( xsNCName atUnit ) { *(daeStringRef*)&attrUnit = atUnit;} + +protected: + /** + * Constructor + */ + domGles_texcombiner_argumentAlpha_type_complexType(DAE& dae, daeElement* elt) : attrSource(), attrOperand(), attrUnit() {} + /** + * Destructor + */ + virtual ~domGles_texcombiner_argumentAlpha_type_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domGles_texcombiner_argumentAlpha_type_complexType &operator=( const domGles_texcombiner_argumentAlpha_type_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domGles_texcombiner_argumentAlpha_type_complexType. + */ +class domGles_texcombiner_argumentAlpha_type : public daeElement, public domGles_texcombiner_argumentAlpha_type_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GLES_TEXCOMBINER_ARGUMENTALPHA_TYPE; } + static daeInt ID() { return 148; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the source attribute. + * @return Returns a domGles_texcombiner_source_enums of the source attribute. + */ + domGles_texcombiner_source_enums getSource() const { return attrSource; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( domGles_texcombiner_source_enums atSource ) { attrSource = atSource; _validAttributeArray[0] = true; } + + /** + * Gets the operand attribute. + * @return Returns a domGles_texcombiner_operandAlpha_enums of the operand attribute. + */ + domGles_texcombiner_operandAlpha_enums getOperand() const { return attrOperand; } + /** + * Sets the operand attribute. + * @param atOperand The new value for the operand attribute. + */ + void setOperand( domGles_texcombiner_operandAlpha_enums atOperand ) { attrOperand = atOperand; _validAttributeArray[1] = true; } + + /** + * Gets the unit attribute. + * @return Returns a xsNCName of the unit attribute. + */ + xsNCName getUnit() const { return attrUnit; } + /** + * Sets the unit attribute. + * @param atUnit The new value for the unit attribute. + */ + void setUnit( xsNCName atUnit ) { *(daeStringRef*)&attrUnit = atUnit; _validAttributeArray[2] = true; } + +protected: + /** + * Constructor + */ + domGles_texcombiner_argumentAlpha_type(DAE& dae) : daeElement(dae), domGles_texcombiner_argumentAlpha_type_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domGles_texcombiner_argumentAlpha_type() {} + /** + * Overloaded assignment operator + */ + virtual domGles_texcombiner_argumentAlpha_type &operator=( const domGles_texcombiner_argumentAlpha_type &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGles_texcombiner_argumentRGB_type.h b/Engine/lib/collada/include/1.4/dom/domGles_texcombiner_argumentRGB_type.h new file mode 100644 index 000000000..967f0d2c0 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGles_texcombiner_argumentRGB_type.h @@ -0,0 +1,153 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGles_texcombiner_argumentRGB_type_h__ +#define __domGles_texcombiner_argumentRGB_type_h__ + +#include +#include +#include + +class DAE; + +class domGles_texcombiner_argumentRGB_type_complexType +{ +protected: // Attributes + domGles_texcombiner_source_enums attrSource; + domGles_texcombiner_operandRGB_enums attrOperand; + xsNCName attrUnit; + + +public: //Accessors and Mutators + /** + * Gets the source attribute. + * @return Returns a domGles_texcombiner_source_enums of the source attribute. + */ + domGles_texcombiner_source_enums getSource() const { return attrSource; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( domGles_texcombiner_source_enums atSource ) { attrSource = atSource; } + + /** + * Gets the operand attribute. + * @return Returns a domGles_texcombiner_operandRGB_enums of the operand attribute. + */ + domGles_texcombiner_operandRGB_enums getOperand() const { return attrOperand; } + /** + * Sets the operand attribute. + * @param atOperand The new value for the operand attribute. + */ + void setOperand( domGles_texcombiner_operandRGB_enums atOperand ) { attrOperand = atOperand; } + + /** + * Gets the unit attribute. + * @return Returns a xsNCName of the unit attribute. + */ + xsNCName getUnit() const { return attrUnit; } + /** + * Sets the unit attribute. + * @param atUnit The new value for the unit attribute. + */ + void setUnit( xsNCName atUnit ) { *(daeStringRef*)&attrUnit = atUnit;} + +protected: + /** + * Constructor + */ + domGles_texcombiner_argumentRGB_type_complexType(DAE& dae, daeElement* elt) : attrSource(), attrOperand(), attrUnit() {} + /** + * Destructor + */ + virtual ~domGles_texcombiner_argumentRGB_type_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domGles_texcombiner_argumentRGB_type_complexType &operator=( const domGles_texcombiner_argumentRGB_type_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domGles_texcombiner_argumentRGB_type_complexType. + */ +class domGles_texcombiner_argumentRGB_type : public daeElement, public domGles_texcombiner_argumentRGB_type_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GLES_TEXCOMBINER_ARGUMENTRGB_TYPE; } + static daeInt ID() { return 147; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the source attribute. + * @return Returns a domGles_texcombiner_source_enums of the source attribute. + */ + domGles_texcombiner_source_enums getSource() const { return attrSource; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( domGles_texcombiner_source_enums atSource ) { attrSource = atSource; _validAttributeArray[0] = true; } + + /** + * Gets the operand attribute. + * @return Returns a domGles_texcombiner_operandRGB_enums of the operand attribute. + */ + domGles_texcombiner_operandRGB_enums getOperand() const { return attrOperand; } + /** + * Sets the operand attribute. + * @param atOperand The new value for the operand attribute. + */ + void setOperand( domGles_texcombiner_operandRGB_enums atOperand ) { attrOperand = atOperand; _validAttributeArray[1] = true; } + + /** + * Gets the unit attribute. + * @return Returns a xsNCName of the unit attribute. + */ + xsNCName getUnit() const { return attrUnit; } + /** + * Sets the unit attribute. + * @param atUnit The new value for the unit attribute. + */ + void setUnit( xsNCName atUnit ) { *(daeStringRef*)&attrUnit = atUnit; _validAttributeArray[2] = true; } + +protected: + /** + * Constructor + */ + domGles_texcombiner_argumentRGB_type(DAE& dae) : daeElement(dae), domGles_texcombiner_argumentRGB_type_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domGles_texcombiner_argumentRGB_type() {} + /** + * Overloaded assignment operator + */ + virtual domGles_texcombiner_argumentRGB_type &operator=( const domGles_texcombiner_argumentRGB_type &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGles_texcombiner_commandAlpha_type.h b/Engine/lib/collada/include/1.4/dom/domGles_texcombiner_commandAlpha_type.h new file mode 100644 index 000000000..6c1ed06a1 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGles_texcombiner_commandAlpha_type.h @@ -0,0 +1,143 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGles_texcombiner_commandAlpha_type_h__ +#define __domGles_texcombiner_commandAlpha_type_h__ + +#include +#include +#include + +#include +class DAE; + +class domGles_texcombiner_commandAlpha_type_complexType +{ +protected: // Attributes + domGles_texcombiner_operatorAlpha_enums attrOperator; + xsFloat attrScale; + +protected: // Element + domGles_texcombiner_argumentAlpha_type_Array elemArgument_array; + +public: //Accessors and Mutators + /** + * Gets the operator attribute. + * @return Returns a domGles_texcombiner_operatorAlpha_enums of the operator attribute. + */ + domGles_texcombiner_operatorAlpha_enums getOperator() const { return attrOperator; } + /** + * Sets the operator attribute. + * @param atOperator The new value for the operator attribute. + */ + void setOperator( domGles_texcombiner_operatorAlpha_enums atOperator ) { attrOperator = atOperator; } + + /** + * Gets the scale attribute. + * @return Returns a xsFloat of the scale attribute. + */ + xsFloat getScale() const { return attrScale; } + /** + * Sets the scale attribute. + * @param atScale The new value for the scale attribute. + */ + void setScale( xsFloat atScale ) { attrScale = atScale; } + + /** + * Gets the argument element array. + * @return Returns a reference to the array of argument elements. + */ + domGles_texcombiner_argumentAlpha_type_Array &getArgument_array() { return elemArgument_array; } + /** + * Gets the argument element array. + * @return Returns a constant reference to the array of argument elements. + */ + const domGles_texcombiner_argumentAlpha_type_Array &getArgument_array() const { return elemArgument_array; } +protected: + /** + * Constructor + */ + domGles_texcombiner_commandAlpha_type_complexType(DAE& dae, daeElement* elt) : attrOperator(), attrScale(), elemArgument_array() {} + /** + * Destructor + */ + virtual ~domGles_texcombiner_commandAlpha_type_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domGles_texcombiner_commandAlpha_type_complexType &operator=( const domGles_texcombiner_commandAlpha_type_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domGles_texcombiner_commandAlpha_type_complexType. + */ +class domGles_texcombiner_commandAlpha_type : public daeElement, public domGles_texcombiner_commandAlpha_type_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GLES_TEXCOMBINER_COMMANDALPHA_TYPE; } + static daeInt ID() { return 150; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the operator attribute. + * @return Returns a domGles_texcombiner_operatorAlpha_enums of the operator attribute. + */ + domGles_texcombiner_operatorAlpha_enums getOperator() const { return attrOperator; } + /** + * Sets the operator attribute. + * @param atOperator The new value for the operator attribute. + */ + void setOperator( domGles_texcombiner_operatorAlpha_enums atOperator ) { attrOperator = atOperator; _validAttributeArray[0] = true; } + + /** + * Gets the scale attribute. + * @return Returns a xsFloat of the scale attribute. + */ + xsFloat getScale() const { return attrScale; } + /** + * Sets the scale attribute. + * @param atScale The new value for the scale attribute. + */ + void setScale( xsFloat atScale ) { attrScale = atScale; _validAttributeArray[1] = true; } + +protected: + /** + * Constructor + */ + domGles_texcombiner_commandAlpha_type(DAE& dae) : daeElement(dae), domGles_texcombiner_commandAlpha_type_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domGles_texcombiner_commandAlpha_type() {} + /** + * Overloaded assignment operator + */ + virtual domGles_texcombiner_commandAlpha_type &operator=( const domGles_texcombiner_commandAlpha_type &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGles_texcombiner_commandRGB_type.h b/Engine/lib/collada/include/1.4/dom/domGles_texcombiner_commandRGB_type.h new file mode 100644 index 000000000..a35294f4b --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGles_texcombiner_commandRGB_type.h @@ -0,0 +1,147 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGles_texcombiner_commandRGB_type_h__ +#define __domGles_texcombiner_commandRGB_type_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * Defines the RGB portion of a texture_pipeline command. This is a combiner-mode + * texturing operation. + */ +class domGles_texcombiner_commandRGB_type_complexType +{ +protected: // Attributes + domGles_texcombiner_operatorRGB_enums attrOperator; + xsFloat attrScale; + +protected: // Element + domGles_texcombiner_argumentRGB_type_Array elemArgument_array; + +public: //Accessors and Mutators + /** + * Gets the operator attribute. + * @return Returns a domGles_texcombiner_operatorRGB_enums of the operator attribute. + */ + domGles_texcombiner_operatorRGB_enums getOperator() const { return attrOperator; } + /** + * Sets the operator attribute. + * @param atOperator The new value for the operator attribute. + */ + void setOperator( domGles_texcombiner_operatorRGB_enums atOperator ) { attrOperator = atOperator; } + + /** + * Gets the scale attribute. + * @return Returns a xsFloat of the scale attribute. + */ + xsFloat getScale() const { return attrScale; } + /** + * Sets the scale attribute. + * @param atScale The new value for the scale attribute. + */ + void setScale( xsFloat atScale ) { attrScale = atScale; } + + /** + * Gets the argument element array. + * @return Returns a reference to the array of argument elements. + */ + domGles_texcombiner_argumentRGB_type_Array &getArgument_array() { return elemArgument_array; } + /** + * Gets the argument element array. + * @return Returns a constant reference to the array of argument elements. + */ + const domGles_texcombiner_argumentRGB_type_Array &getArgument_array() const { return elemArgument_array; } +protected: + /** + * Constructor + */ + domGles_texcombiner_commandRGB_type_complexType(DAE& dae, daeElement* elt) : attrOperator(), attrScale(), elemArgument_array() {} + /** + * Destructor + */ + virtual ~domGles_texcombiner_commandRGB_type_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domGles_texcombiner_commandRGB_type_complexType &operator=( const domGles_texcombiner_commandRGB_type_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domGles_texcombiner_commandRGB_type_complexType. + */ +class domGles_texcombiner_commandRGB_type : public daeElement, public domGles_texcombiner_commandRGB_type_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GLES_TEXCOMBINER_COMMANDRGB_TYPE; } + static daeInt ID() { return 149; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the operator attribute. + * @return Returns a domGles_texcombiner_operatorRGB_enums of the operator attribute. + */ + domGles_texcombiner_operatorRGB_enums getOperator() const { return attrOperator; } + /** + * Sets the operator attribute. + * @param atOperator The new value for the operator attribute. + */ + void setOperator( domGles_texcombiner_operatorRGB_enums atOperator ) { attrOperator = atOperator; _validAttributeArray[0] = true; } + + /** + * Gets the scale attribute. + * @return Returns a xsFloat of the scale attribute. + */ + xsFloat getScale() const { return attrScale; } + /** + * Sets the scale attribute. + * @param atScale The new value for the scale attribute. + */ + void setScale( xsFloat atScale ) { attrScale = atScale; _validAttributeArray[1] = true; } + +protected: + /** + * Constructor + */ + domGles_texcombiner_commandRGB_type(DAE& dae) : daeElement(dae), domGles_texcombiner_commandRGB_type_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domGles_texcombiner_commandRGB_type() {} + /** + * Overloaded assignment operator + */ + virtual domGles_texcombiner_commandRGB_type &operator=( const domGles_texcombiner_commandRGB_type &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGles_texcombiner_command_type.h b/Engine/lib/collada/include/1.4/dom/domGles_texcombiner_command_type.h new file mode 100644 index 000000000..a8673c2c4 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGles_texcombiner_command_type.h @@ -0,0 +1,103 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGles_texcombiner_command_type_h__ +#define __domGles_texcombiner_command_type_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +class domGles_texcombiner_command_type_complexType +{ + +protected: // Elements + domGles_texture_constant_typeRef elemConstant; + domGles_texcombiner_commandRGB_typeRef elemRGB; + domGles_texcombiner_commandAlpha_typeRef elemAlpha; + +public: //Accessors and Mutators + /** + * Gets the constant element. + * @return a daeSmartRef to the constant element. + */ + const domGles_texture_constant_typeRef getConstant() const { return elemConstant; } + /** + * Gets the RGB element. + * @return a daeSmartRef to the RGB element. + */ + const domGles_texcombiner_commandRGB_typeRef getRGB() const { return elemRGB; } + /** + * Gets the alpha element. + * @return a daeSmartRef to the alpha element. + */ + const domGles_texcombiner_commandAlpha_typeRef getAlpha() const { return elemAlpha; } +protected: + /** + * Constructor + */ + domGles_texcombiner_command_type_complexType(DAE& dae, daeElement* elt) : elemConstant(), elemRGB(), elemAlpha() {} + /** + * Destructor + */ + virtual ~domGles_texcombiner_command_type_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domGles_texcombiner_command_type_complexType &operator=( const domGles_texcombiner_command_type_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domGles_texcombiner_command_type_complexType. + */ +class domGles_texcombiner_command_type : public daeElement, public domGles_texcombiner_command_type_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GLES_TEXCOMBINER_COMMAND_TYPE; } + static daeInt ID() { return 151; } + virtual daeInt typeID() const { return ID(); } +protected: + /** + * Constructor + */ + domGles_texcombiner_command_type(DAE& dae) : daeElement(dae), domGles_texcombiner_command_type_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domGles_texcombiner_command_type() {} + /** + * Overloaded assignment operator + */ + virtual domGles_texcombiner_command_type &operator=( const domGles_texcombiner_command_type &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGles_texenv_command_type.h b/Engine/lib/collada/include/1.4/dom/domGles_texenv_command_type.h new file mode 100644 index 000000000..922bcb262 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGles_texenv_command_type.h @@ -0,0 +1,138 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGles_texenv_command_type_h__ +#define __domGles_texenv_command_type_h__ + +#include +#include +#include + +#include +class DAE; + +class domGles_texenv_command_type_complexType +{ +protected: // Attributes + domGles_texenv_mode_enums attrOperator; + xsNCName attrUnit; + +protected: // Element + domGles_texture_constant_typeRef elemConstant; + +public: //Accessors and Mutators + /** + * Gets the operator attribute. + * @return Returns a domGles_texenv_mode_enums of the operator attribute. + */ + domGles_texenv_mode_enums getOperator() const { return attrOperator; } + /** + * Sets the operator attribute. + * @param atOperator The new value for the operator attribute. + */ + void setOperator( domGles_texenv_mode_enums atOperator ) { attrOperator = atOperator; } + + /** + * Gets the unit attribute. + * @return Returns a xsNCName of the unit attribute. + */ + xsNCName getUnit() const { return attrUnit; } + /** + * Sets the unit attribute. + * @param atUnit The new value for the unit attribute. + */ + void setUnit( xsNCName atUnit ) { *(daeStringRef*)&attrUnit = atUnit;} + + /** + * Gets the constant element. + * @return a daeSmartRef to the constant element. + */ + const domGles_texture_constant_typeRef getConstant() const { return elemConstant; } +protected: + /** + * Constructor + */ + domGles_texenv_command_type_complexType(DAE& dae, daeElement* elt) : attrOperator(), attrUnit(), elemConstant() {} + /** + * Destructor + */ + virtual ~domGles_texenv_command_type_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domGles_texenv_command_type_complexType &operator=( const domGles_texenv_command_type_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domGles_texenv_command_type_complexType. + */ +class domGles_texenv_command_type : public daeElement, public domGles_texenv_command_type_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GLES_TEXENV_COMMAND_TYPE; } + static daeInt ID() { return 146; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the operator attribute. + * @return Returns a domGles_texenv_mode_enums of the operator attribute. + */ + domGles_texenv_mode_enums getOperator() const { return attrOperator; } + /** + * Sets the operator attribute. + * @param atOperator The new value for the operator attribute. + */ + void setOperator( domGles_texenv_mode_enums atOperator ) { attrOperator = atOperator; _validAttributeArray[0] = true; } + + /** + * Gets the unit attribute. + * @return Returns a xsNCName of the unit attribute. + */ + xsNCName getUnit() const { return attrUnit; } + /** + * Sets the unit attribute. + * @param atUnit The new value for the unit attribute. + */ + void setUnit( xsNCName atUnit ) { *(daeStringRef*)&attrUnit = atUnit; _validAttributeArray[1] = true; } + +protected: + /** + * Constructor + */ + domGles_texenv_command_type(DAE& dae) : daeElement(dae), domGles_texenv_command_type_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domGles_texenv_command_type() {} + /** + * Overloaded assignment operator + */ + virtual domGles_texenv_command_type &operator=( const domGles_texenv_command_type &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGles_texture_constant_type.h b/Engine/lib/collada/include/1.4/dom/domGles_texture_constant_type.h new file mode 100644 index 000000000..a63eb4f8a --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGles_texture_constant_type.h @@ -0,0 +1,140 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGles_texture_constant_type_h__ +#define __domGles_texture_constant_type_h__ + +#include +#include +#include + +class DAE; + +class domGles_texture_constant_type_complexType +{ +protected: // Attributes + domFloat4 attrValue; + xsNCName attrParam; + + +public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4 reference of the value array attribute. + */ + domFloat4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4 reference of the value array attribute. + */ + const domFloat4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4 &atValue ) { attrValue = atValue; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam;} + +protected: + /** + * Constructor + */ + domGles_texture_constant_type_complexType(DAE& dae, daeElement* elt) : attrValue(), attrParam() {} + /** + * Destructor + */ + virtual ~domGles_texture_constant_type_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domGles_texture_constant_type_complexType &operator=( const domGles_texture_constant_type_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domGles_texture_constant_type_complexType. + */ +class domGles_texture_constant_type : public daeElement, public domGles_texture_constant_type_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GLES_TEXTURE_CONSTANT_TYPE; } + static daeInt ID() { return 145; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the value array attribute. + * @return Returns a domFloat4 reference of the value array attribute. + */ + domFloat4 &getValue() { return attrValue; } + /** + * Gets the value array attribute. + * @return Returns a constant domFloat4 reference of the value array attribute. + */ + const domFloat4 &getValue() const { return attrValue; } + /** + * Sets the value array attribute. + * @param atValue The new value for the value array attribute. + */ + void setValue( const domFloat4 &atValue ) { attrValue = atValue; _validAttributeArray[0] = true; } + + /** + * Gets the param attribute. + * @return Returns a xsNCName of the param attribute. + */ + xsNCName getParam() const { return attrParam; } + /** + * Sets the param attribute. + * @param atParam The new value for the param attribute. + */ + void setParam( xsNCName atParam ) { *(daeStringRef*)&attrParam = atParam; _validAttributeArray[1] = true; } + +protected: + /** + * Constructor + */ + domGles_texture_constant_type(DAE& dae) : daeElement(dae), domGles_texture_constant_type_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domGles_texture_constant_type() {} + /** + * Overloaded assignment operator + */ + virtual domGles_texture_constant_type &operator=( const domGles_texture_constant_type &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGles_texture_pipeline.h b/Engine/lib/collada/include/1.4/dom/domGles_texture_pipeline.h new file mode 100644 index 000000000..28f90ef52 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGles_texture_pipeline.h @@ -0,0 +1,190 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGles_texture_pipeline_h__ +#define __domGles_texture_pipeline_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * Defines a set of texturing commands that will be converted into multitexturing + * operations using glTexEnv in regular and combiner mode. + */ +class domGles_texture_pipeline_complexType +{ +protected: // Attribute +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; + +protected: // Elements +/** + * Defines a texture_pipeline command. This is a combiner-mode texturing operation. + * @see domTexcombiner + */ + domGles_texcombiner_command_type_Array elemTexcombiner_array; +/** + * Defines a texture_pipeline command. It is a simple noncombiner mode of + * texturing operations. @see domTexenv + */ + domGles_texenv_command_type_Array elemTexenv_array; +/** + * The extra element may appear any number of times. OpenGL ES extensions + * may be used here. @see domExtra + */ + domExtra_Array elemExtra_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid;} + + /** + * Gets the texcombiner element array. + * @return Returns a reference to the array of texcombiner elements. + */ + domGles_texcombiner_command_type_Array &getTexcombiner_array() { return elemTexcombiner_array; } + /** + * Gets the texcombiner element array. + * @return Returns a constant reference to the array of texcombiner elements. + */ + const domGles_texcombiner_command_type_Array &getTexcombiner_array() const { return elemTexcombiner_array; } + /** + * Gets the texenv element array. + * @return Returns a reference to the array of texenv elements. + */ + domGles_texenv_command_type_Array &getTexenv_array() { return elemTexenv_array; } + /** + * Gets the texenv element array. + * @return Returns a constant reference to the array of texenv elements. + */ + const domGles_texenv_command_type_Array &getTexenv_array() const { return elemTexenv_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domGles_texture_pipeline_complexType(DAE& dae, daeElement* elt) : attrSid(), elemTexcombiner_array(), elemTexenv_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domGles_texture_pipeline_complexType() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domGles_texture_pipeline_complexType &operator=( const domGles_texture_pipeline_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domGles_texture_pipeline_complexType. + */ +class domGles_texture_pipeline : public daeElement, public domGles_texture_pipeline_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GLES_TEXTURE_PIPELINE; } + static daeInt ID() { return 152; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + +protected: + /** + * Constructor + */ + domGles_texture_pipeline(DAE& dae) : daeElement(dae), domGles_texture_pipeline_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domGles_texture_pipeline() {} + /** + * Overloaded assignment operator + */ + virtual domGles_texture_pipeline &operator=( const domGles_texture_pipeline &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGles_texture_unit.h b/Engine/lib/collada/include/1.4/dom/domGles_texture_unit.h new file mode 100644 index 000000000..c837720c5 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGles_texture_unit.h @@ -0,0 +1,316 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGles_texture_unit_h__ +#define __domGles_texture_unit_h__ + +#include +#include +#include + +#include +class DAE; + +class domGles_texture_unit_complexType +{ +public: + class domSurface; + + typedef daeSmartRef domSurfaceRef; + typedef daeTArray domSurface_Array; + + class domSurface : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SURFACE; } + static daeInt ID() { return 153; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsNCName value of the text data of this element. + */ + xsNCName _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsNCName of the value. + */ + xsNCName getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNCName val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domSurface(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domSurface() {} + /** + * Overloaded assignment operator + */ + virtual domSurface &operator=( const domSurface &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domSampler_state; + + typedef daeSmartRef domSampler_stateRef; + typedef daeTArray domSampler_state_Array; + + class domSampler_state : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SAMPLER_STATE; } + static daeInt ID() { return 154; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsNCName value of the text data of this element. + */ + xsNCName _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsNCName of the value. + */ + xsNCName getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNCName val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domSampler_state(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domSampler_state() {} + /** + * Overloaded assignment operator + */ + virtual domSampler_state &operator=( const domSampler_state &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domTexcoord; + + typedef daeSmartRef domTexcoordRef; + typedef daeTArray domTexcoord_Array; + + class domTexcoord : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TEXCOORD; } + static daeInt ID() { return 155; } + virtual daeInt typeID() const { return ID(); } + protected: // Attribute + xsNCName attrSemantic; + + + public: //Accessors and Mutators + /** + * Gets the semantic attribute. + * @return Returns a xsNCName of the semantic attribute. + */ + xsNCName getSemantic() const { return attrSemantic; } + /** + * Sets the semantic attribute. + * @param atSemantic The new value for the semantic attribute. + */ + void setSemantic( xsNCName atSemantic ) { *(daeStringRef*)&attrSemantic = atSemantic; _validAttributeArray[0] = true; } + + protected: + /** + * Constructor + */ + domTexcoord(DAE& dae) : daeElement(dae), attrSemantic() {} + /** + * Destructor + */ + virtual ~domTexcoord() {} + /** + * Overloaded assignment operator + */ + virtual domTexcoord &operator=( const domTexcoord &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attribute +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; + +protected: // Elements + domSurfaceRef elemSurface; + domSampler_stateRef elemSampler_state; + domTexcoordRef elemTexcoord; + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid;} + + /** + * Gets the surface element. + * @return a daeSmartRef to the surface element. + */ + const domSurfaceRef getSurface() const { return elemSurface; } + /** + * Gets the sampler_state element. + * @return a daeSmartRef to the sampler_state element. + */ + const domSampler_stateRef getSampler_state() const { return elemSampler_state; } + /** + * Gets the texcoord element. + * @return a daeSmartRef to the texcoord element. + */ + const domTexcoordRef getTexcoord() const { return elemTexcoord; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domGles_texture_unit_complexType(DAE& dae, daeElement* elt) : attrSid(), elemSurface(), elemSampler_state(), elemTexcoord(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domGles_texture_unit_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domGles_texture_unit_complexType &operator=( const domGles_texture_unit_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domGles_texture_unit_complexType. + */ +class domGles_texture_unit : public daeElement, public domGles_texture_unit_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GLES_TEXTURE_UNIT; } + static daeInt ID() { return 156; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + +protected: + /** + * Constructor + */ + domGles_texture_unit(DAE& dae) : daeElement(dae), domGles_texture_unit_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domGles_texture_unit() {} + /** + * Overloaded assignment operator + */ + virtual domGles_texture_unit &operator=( const domGles_texture_unit &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGlsl_newarray_type.h b/Engine/lib/collada/include/1.4/dom/domGlsl_newarray_type.h new file mode 100644 index 000000000..9457be05e --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGlsl_newarray_type.h @@ -0,0 +1,168 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGlsl_newarray_type_h__ +#define __domGlsl_newarray_type_h__ + +#include +#include +#include + +#include +#include +class DAE; + +/** + * The glsl_newarray_type is used to creates a parameter of a one-dimensional + * array type. + */ +class domGlsl_newarray_type_complexType +{ +protected: // Attribute +/** + * The length attribute specifies the length of the array. + */ + xsPositiveInteger attrLength; + +protected: // Elements + domGlsl_param_type_Array elemGlsl_param_type_array; +/** + * You may recursively nest glsl_newarray elements to create multidimensional + * arrays. @see domArray + */ + domGlsl_newarray_type_Array elemArray_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the length attribute. + * @return Returns a xsPositiveInteger of the length attribute. + */ + xsPositiveInteger getLength() const { return attrLength; } + /** + * Sets the length attribute. + * @param atLength The new value for the length attribute. + */ + void setLength( xsPositiveInteger atLength ) { attrLength = atLength; } + + /** + * Gets the glsl_param_type element array. + * @return Returns a reference to the array of glsl_param_type elements. + */ + domGlsl_param_type_Array &getGlsl_param_type_array() { return elemGlsl_param_type_array; } + /** + * Gets the glsl_param_type element array. + * @return Returns a constant reference to the array of glsl_param_type elements. + */ + const domGlsl_param_type_Array &getGlsl_param_type_array() const { return elemGlsl_param_type_array; } + /** + * Gets the array element array. + * @return Returns a reference to the array of array elements. + */ + domGlsl_newarray_type_Array &getArray_array() { return elemArray_array; } + /** + * Gets the array element array. + * @return Returns a constant reference to the array of array elements. + */ + const domGlsl_newarray_type_Array &getArray_array() const { return elemArray_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domGlsl_newarray_type_complexType(DAE& dae, daeElement* elt) : attrLength(), elemGlsl_param_type_array(), elemArray_array() {} + /** + * Destructor + */ + virtual ~domGlsl_newarray_type_complexType() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domGlsl_newarray_type_complexType &operator=( const domGlsl_newarray_type_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domGlsl_newarray_type_complexType. + */ +class domGlsl_newarray_type : public daeElement, public domGlsl_newarray_type_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GLSL_NEWARRAY_TYPE; } + static daeInt ID() { return 103; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the length attribute. + * @return Returns a xsPositiveInteger of the length attribute. + */ + xsPositiveInteger getLength() const { return attrLength; } + /** + * Sets the length attribute. + * @param atLength The new value for the length attribute. + */ + void setLength( xsPositiveInteger atLength ) { attrLength = atLength; _validAttributeArray[0] = true; } + +protected: + /** + * Constructor + */ + domGlsl_newarray_type(DAE& dae) : daeElement(dae), domGlsl_newarray_type_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domGlsl_newarray_type() {} + /** + * Overloaded assignment operator + */ + virtual domGlsl_newarray_type &operator=( const domGlsl_newarray_type &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGlsl_newparam.h b/Engine/lib/collada/include/1.4/dom/domGlsl_newparam.h new file mode 100644 index 000000000..c55b16327 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGlsl_newparam.h @@ -0,0 +1,289 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGlsl_newparam_h__ +#define __domGlsl_newparam_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +class domGlsl_newparam_complexType +{ +public: + class domSemantic; + + typedef daeSmartRef domSemanticRef; + typedef daeTArray domSemantic_Array; + + class domSemantic : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SEMANTIC; } + static daeInt ID() { return 108; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsNCName value of the text data of this element. + */ + xsNCName _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsNCName of the value. + */ + xsNCName getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNCName val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domSemantic(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domSemantic() {} + /** + * Overloaded assignment operator + */ + virtual domSemantic &operator=( const domSemantic &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domModifier; + + typedef daeSmartRef domModifierRef; + typedef daeTArray domModifier_Array; + + class domModifier : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MODIFIER; } + static daeInt ID() { return 109; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_modifier_enum_common value of the text data of this element. + */ + domFx_modifier_enum_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_modifier_enum_common of the value. + */ + domFx_modifier_enum_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_modifier_enum_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domModifier(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domModifier() {} + /** + * Overloaded assignment operator + */ + virtual domModifier &operator=( const domModifier &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attribute + domGlsl_identifier attrSid; + +protected: // Elements + domFx_annotate_common_Array elemAnnotate_array; + domSemanticRef elemSemantic; + domModifierRef elemModifier; + domGlsl_param_typeRef elemGlsl_param_type; + domGlsl_newarray_typeRef elemArray; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a domGlsl_identifier of the sid attribute. + */ + domGlsl_identifier getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( domGlsl_identifier atSid ) { attrSid = atSid; } + + /** + * Gets the annotate element array. + * @return Returns a reference to the array of annotate elements. + */ + domFx_annotate_common_Array &getAnnotate_array() { return elemAnnotate_array; } + /** + * Gets the annotate element array. + * @return Returns a constant reference to the array of annotate elements. + */ + const domFx_annotate_common_Array &getAnnotate_array() const { return elemAnnotate_array; } + /** + * Gets the semantic element. + * @return a daeSmartRef to the semantic element. + */ + const domSemanticRef getSemantic() const { return elemSemantic; } + /** + * Gets the modifier element. + * @return a daeSmartRef to the modifier element. + */ + const domModifierRef getModifier() const { return elemModifier; } + /** + * Gets the glsl_param_type element. + * @return a daeSmartRef to the glsl_param_type element. + */ + const domGlsl_param_typeRef getGlsl_param_type() const { return elemGlsl_param_type; } + /** + * Gets the array element. + * @return a daeSmartRef to the array element. + */ + const domGlsl_newarray_typeRef getArray() const { return elemArray; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domGlsl_newparam_complexType(DAE& dae, daeElement* elt) : attrSid(), elemAnnotate_array(), elemSemantic(), elemModifier(), elemGlsl_param_type(), elemArray() {} + /** + * Destructor + */ + virtual ~domGlsl_newparam_complexType() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domGlsl_newparam_complexType &operator=( const domGlsl_newparam_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domGlsl_newparam_complexType. + */ +class domGlsl_newparam : public daeElement, public domGlsl_newparam_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GLSL_NEWPARAM; } + static daeInt ID() { return 110; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a domGlsl_identifier of the sid attribute. + */ + domGlsl_identifier getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( domGlsl_identifier atSid ) { attrSid = atSid; _validAttributeArray[0] = true; } + +protected: + /** + * Constructor + */ + domGlsl_newparam(DAE& dae) : daeElement(dae), domGlsl_newparam_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domGlsl_newparam() {} + /** + * Overloaded assignment operator + */ + virtual domGlsl_newparam &operator=( const domGlsl_newparam &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGlsl_param_type.h b/Engine/lib/collada/include/1.4/dom/domGlsl_param_type.h new file mode 100644 index 000000000..4ed65a807 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGlsl_param_type.h @@ -0,0 +1,1225 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGlsl_param_type_h__ +#define __domGlsl_param_type_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +class DAE; + +/** + * A group that specifies the allowable types for GLSL profile parameters. + */ +class domGlsl_param_type : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GLSL_PARAM_TYPE; } + static daeInt ID() { return 363; } + virtual daeInt typeID() const { return ID(); } +public: + class domBool; + + typedef daeSmartRef domBoolRef; + typedef daeTArray domBool_Array; + + class domBool : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL; } + static daeInt ID() { return 364; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domGlsl_bool value of the text data of this element. + */ + domGlsl_bool _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domGlsl_bool of the value. + */ + domGlsl_bool getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domGlsl_bool val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool() {} + /** + * Overloaded assignment operator + */ + virtual domBool &operator=( const domBool &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool2; + + typedef daeSmartRef domBool2Ref; + typedef daeTArray domBool2_Array; + + class domBool2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL2; } + static daeInt ID() { return 365; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domGlsl_bool2 value of the text data of this element. + */ + domGlsl_bool2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domGlsl_bool2 reference of the _value array. + */ + domGlsl_bool2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domGlsl_bool2 reference of the _value array. + */ + const domGlsl_bool2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domGlsl_bool2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool2() {} + /** + * Overloaded assignment operator + */ + virtual domBool2 &operator=( const domBool2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool3; + + typedef daeSmartRef domBool3Ref; + typedef daeTArray domBool3_Array; + + class domBool3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL3; } + static daeInt ID() { return 366; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domGlsl_bool3 value of the text data of this element. + */ + domGlsl_bool3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domGlsl_bool3 reference of the _value array. + */ + domGlsl_bool3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domGlsl_bool3 reference of the _value array. + */ + const domGlsl_bool3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domGlsl_bool3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool3() {} + /** + * Overloaded assignment operator + */ + virtual domBool3 &operator=( const domBool3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBool4; + + typedef daeSmartRef domBool4Ref; + typedef daeTArray domBool4_Array; + + class domBool4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BOOL4; } + static daeInt ID() { return 367; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domGlsl_bool4 value of the text data of this element. + */ + domGlsl_bool4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domGlsl_bool4 reference of the _value array. + */ + domGlsl_bool4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domGlsl_bool4 reference of the _value array. + */ + const domGlsl_bool4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domGlsl_bool4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBool4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBool4() {} + /** + * Overloaded assignment operator + */ + virtual domBool4 &operator=( const domBool4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat; + + typedef daeSmartRef domFloatRef; + typedef daeTArray domFloat_Array; + + class domFloat : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT; } + static daeInt ID() { return 368; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domGlsl_float value of the text data of this element. + */ + domGlsl_float _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domGlsl_float of the value. + */ + domGlsl_float getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domGlsl_float val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat() {} + /** + * Overloaded assignment operator + */ + virtual domFloat &operator=( const domFloat &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat2; + + typedef daeSmartRef domFloat2Ref; + typedef daeTArray domFloat2_Array; + + class domFloat2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT2; } + static daeInt ID() { return 369; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domGlsl_float2 value of the text data of this element. + */ + domGlsl_float2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domGlsl_float2 reference of the _value array. + */ + domGlsl_float2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domGlsl_float2 reference of the _value array. + */ + const domGlsl_float2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domGlsl_float2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat2() {} + /** + * Overloaded assignment operator + */ + virtual domFloat2 &operator=( const domFloat2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat3; + + typedef daeSmartRef domFloat3Ref; + typedef daeTArray domFloat3_Array; + + class domFloat3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT3; } + static daeInt ID() { return 370; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domGlsl_float3 value of the text data of this element. + */ + domGlsl_float3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domGlsl_float3 reference of the _value array. + */ + domGlsl_float3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domGlsl_float3 reference of the _value array. + */ + const domGlsl_float3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domGlsl_float3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat3() {} + /** + * Overloaded assignment operator + */ + virtual domFloat3 &operator=( const domFloat3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat4; + + typedef daeSmartRef domFloat4Ref; + typedef daeTArray domFloat4_Array; + + class domFloat4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT4; } + static daeInt ID() { return 371; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domGlsl_float4 value of the text data of this element. + */ + domGlsl_float4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domGlsl_float4 reference of the _value array. + */ + domGlsl_float4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domGlsl_float4 reference of the _value array. + */ + const domGlsl_float4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domGlsl_float4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat4() {} + /** + * Overloaded assignment operator + */ + virtual domFloat4 &operator=( const domFloat4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat2x2; + + typedef daeSmartRef domFloat2x2Ref; + typedef daeTArray domFloat2x2_Array; + + class domFloat2x2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT2X2; } + static daeInt ID() { return 372; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domGlsl_float2x2 value of the text data of this element. + */ + domGlsl_float2x2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domGlsl_float2x2 reference of the _value array. + */ + domGlsl_float2x2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domGlsl_float2x2 reference of the _value array. + */ + const domGlsl_float2x2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domGlsl_float2x2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat2x2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat2x2() {} + /** + * Overloaded assignment operator + */ + virtual domFloat2x2 &operator=( const domFloat2x2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat3x3; + + typedef daeSmartRef domFloat3x3Ref; + typedef daeTArray domFloat3x3_Array; + + class domFloat3x3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT3X3; } + static daeInt ID() { return 373; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domGlsl_float3x3 value of the text data of this element. + */ + domGlsl_float3x3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domGlsl_float3x3 reference of the _value array. + */ + domGlsl_float3x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domGlsl_float3x3 reference of the _value array. + */ + const domGlsl_float3x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domGlsl_float3x3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat3x3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat3x3() {} + /** + * Overloaded assignment operator + */ + virtual domFloat3x3 &operator=( const domFloat3x3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domFloat4x4; + + typedef daeSmartRef domFloat4x4Ref; + typedef daeTArray domFloat4x4_Array; + + class domFloat4x4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::FLOAT4X4; } + static daeInt ID() { return 374; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domGlsl_float4x4 value of the text data of this element. + */ + domGlsl_float4x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domGlsl_float4x4 reference of the _value array. + */ + domGlsl_float4x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domGlsl_float4x4 reference of the _value array. + */ + const domGlsl_float4x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domGlsl_float4x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domFloat4x4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domFloat4x4() {} + /** + * Overloaded assignment operator + */ + virtual domFloat4x4 &operator=( const domFloat4x4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt; + + typedef daeSmartRef domIntRef; + typedef daeTArray domInt_Array; + + class domInt : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT; } + static daeInt ID() { return 375; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domGlsl_int value of the text data of this element. + */ + domGlsl_int _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domGlsl_int of the value. + */ + domGlsl_int getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domGlsl_int val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt() {} + /** + * Overloaded assignment operator + */ + virtual domInt &operator=( const domInt &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt2; + + typedef daeSmartRef domInt2Ref; + typedef daeTArray domInt2_Array; + + class domInt2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT2; } + static daeInt ID() { return 376; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domGlsl_int2 value of the text data of this element. + */ + domGlsl_int2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domGlsl_int2 reference of the _value array. + */ + domGlsl_int2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domGlsl_int2 reference of the _value array. + */ + const domGlsl_int2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domGlsl_int2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt2() {} + /** + * Overloaded assignment operator + */ + virtual domInt2 &operator=( const domInt2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt3; + + typedef daeSmartRef domInt3Ref; + typedef daeTArray domInt3_Array; + + class domInt3 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT3; } + static daeInt ID() { return 377; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domGlsl_int3 value of the text data of this element. + */ + domGlsl_int3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domGlsl_int3 reference of the _value array. + */ + domGlsl_int3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domGlsl_int3 reference of the _value array. + */ + const domGlsl_int3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domGlsl_int3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt3(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt3() {} + /** + * Overloaded assignment operator + */ + virtual domInt3 &operator=( const domInt3 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInt4; + + typedef daeSmartRef domInt4Ref; + typedef daeTArray domInt4_Array; + + class domInt4 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT4; } + static daeInt ID() { return 378; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domGlsl_int4 value of the text data of this element. + */ + domGlsl_int4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domGlsl_int4 reference of the _value array. + */ + domGlsl_int4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domGlsl_int4 reference of the _value array. + */ + const domGlsl_int4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domGlsl_int4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInt4(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domInt4() {} + /** + * Overloaded assignment operator + */ + virtual domInt4 &operator=( const domInt4 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domEnum; + + typedef daeSmartRef domEnumRef; + typedef daeTArray domEnum_Array; + + class domEnum : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ENUM; } + static daeInt ID() { return 379; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domGl_enumeration value of the text data of this element. + */ + domGl_enumeration _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domGl_enumeration of the value. + */ + domGl_enumeration getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domGl_enumeration val ) { _value = val; } + + protected: + /** + * Constructor + */ + domEnum(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domEnum() {} + /** + * Overloaded assignment operator + */ + virtual domEnum &operator=( const domEnum &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements + domBoolRef elemBool; + domBool2Ref elemBool2; + domBool3Ref elemBool3; + domBool4Ref elemBool4; + domFloatRef elemFloat; + domFloat2Ref elemFloat2; + domFloat3Ref elemFloat3; + domFloat4Ref elemFloat4; + domFloat2x2Ref elemFloat2x2; + domFloat3x3Ref elemFloat3x3; + domFloat4x4Ref elemFloat4x4; + domIntRef elemInt; + domInt2Ref elemInt2; + domInt3Ref elemInt3; + domInt4Ref elemInt4; + domGlsl_surface_typeRef elemSurface; + domGl_sampler1DRef elemSampler1D; + domGl_sampler2DRef elemSampler2D; + domGl_sampler3DRef elemSampler3D; + domGl_samplerCUBERef elemSamplerCUBE; + domGl_samplerRECTRef elemSamplerRECT; + domGl_samplerDEPTHRef elemSamplerDEPTH; + domEnumRef elemEnum; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the bool element. + * @return a daeSmartRef to the bool element. + */ + const domBoolRef getBool() const { return elemBool; } + /** + * Gets the bool2 element. + * @return a daeSmartRef to the bool2 element. + */ + const domBool2Ref getBool2() const { return elemBool2; } + /** + * Gets the bool3 element. + * @return a daeSmartRef to the bool3 element. + */ + const domBool3Ref getBool3() const { return elemBool3; } + /** + * Gets the bool4 element. + * @return a daeSmartRef to the bool4 element. + */ + const domBool4Ref getBool4() const { return elemBool4; } + /** + * Gets the float element. + * @return a daeSmartRef to the float element. + */ + const domFloatRef getFloat() const { return elemFloat; } + /** + * Gets the float2 element. + * @return a daeSmartRef to the float2 element. + */ + const domFloat2Ref getFloat2() const { return elemFloat2; } + /** + * Gets the float3 element. + * @return a daeSmartRef to the float3 element. + */ + const domFloat3Ref getFloat3() const { return elemFloat3; } + /** + * Gets the float4 element. + * @return a daeSmartRef to the float4 element. + */ + const domFloat4Ref getFloat4() const { return elemFloat4; } + /** + * Gets the float2x2 element. + * @return a daeSmartRef to the float2x2 element. + */ + const domFloat2x2Ref getFloat2x2() const { return elemFloat2x2; } + /** + * Gets the float3x3 element. + * @return a daeSmartRef to the float3x3 element. + */ + const domFloat3x3Ref getFloat3x3() const { return elemFloat3x3; } + /** + * Gets the float4x4 element. + * @return a daeSmartRef to the float4x4 element. + */ + const domFloat4x4Ref getFloat4x4() const { return elemFloat4x4; } + /** + * Gets the int element. + * @return a daeSmartRef to the int element. + */ + const domIntRef getInt() const { return elemInt; } + /** + * Gets the int2 element. + * @return a daeSmartRef to the int2 element. + */ + const domInt2Ref getInt2() const { return elemInt2; } + /** + * Gets the int3 element. + * @return a daeSmartRef to the int3 element. + */ + const domInt3Ref getInt3() const { return elemInt3; } + /** + * Gets the int4 element. + * @return a daeSmartRef to the int4 element. + */ + const domInt4Ref getInt4() const { return elemInt4; } + /** + * Gets the surface element. + * @return a daeSmartRef to the surface element. + */ + const domGlsl_surface_typeRef getSurface() const { return elemSurface; } + /** + * Gets the sampler1D element. + * @return a daeSmartRef to the sampler1D element. + */ + const domGl_sampler1DRef getSampler1D() const { return elemSampler1D; } + /** + * Gets the sampler2D element. + * @return a daeSmartRef to the sampler2D element. + */ + const domGl_sampler2DRef getSampler2D() const { return elemSampler2D; } + /** + * Gets the sampler3D element. + * @return a daeSmartRef to the sampler3D element. + */ + const domGl_sampler3DRef getSampler3D() const { return elemSampler3D; } + /** + * Gets the samplerCUBE element. + * @return a daeSmartRef to the samplerCUBE element. + */ + const domGl_samplerCUBERef getSamplerCUBE() const { return elemSamplerCUBE; } + /** + * Gets the samplerRECT element. + * @return a daeSmartRef to the samplerRECT element. + */ + const domGl_samplerRECTRef getSamplerRECT() const { return elemSamplerRECT; } + /** + * Gets the samplerDEPTH element. + * @return a daeSmartRef to the samplerDEPTH element. + */ + const domGl_samplerDEPTHRef getSamplerDEPTH() const { return elemSamplerDEPTH; } + /** + * Gets the enum element. + * @return a daeSmartRef to the enum element. + */ + const domEnumRef getEnum() const { return elemEnum; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domGlsl_param_type(DAE& dae) : daeElement(dae), elemBool(), elemBool2(), elemBool3(), elemBool4(), elemFloat(), elemFloat2(), elemFloat3(), elemFloat4(), elemFloat2x2(), elemFloat3x3(), elemFloat4x4(), elemInt(), elemInt2(), elemInt3(), elemInt4(), elemSurface(), elemSampler1D(), elemSampler2D(), elemSampler3D(), elemSamplerCUBE(), elemSamplerRECT(), elemSamplerDEPTH(), elemEnum() {} + /** + * Destructor + */ + virtual ~domGlsl_param_type() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domGlsl_param_type &operator=( const domGlsl_param_type &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGlsl_setarray_type.h b/Engine/lib/collada/include/1.4/dom/domGlsl_setarray_type.h new file mode 100644 index 000000000..ac85dec8c --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGlsl_setarray_type.h @@ -0,0 +1,168 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGlsl_setarray_type_h__ +#define __domGlsl_setarray_type_h__ + +#include +#include +#include + +#include +#include +class DAE; + +/** + * The glsl_newarray_type is used to creates a parameter of a one-dimensional + * array type. + */ +class domGlsl_setarray_type_complexType +{ +protected: // Attribute +/** + * The length attribute specifies the length of the array. + */ + xsPositiveInteger attrLength; + +protected: // Elements + domGlsl_param_type_Array elemGlsl_param_type_array; +/** + * You may recursively nest glsl_newarray elements to create multidimensional + * arrays. @see domArray + */ + domGlsl_setarray_type_Array elemArray_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the length attribute. + * @return Returns a xsPositiveInteger of the length attribute. + */ + xsPositiveInteger getLength() const { return attrLength; } + /** + * Sets the length attribute. + * @param atLength The new value for the length attribute. + */ + void setLength( xsPositiveInteger atLength ) { attrLength = atLength; } + + /** + * Gets the glsl_param_type element array. + * @return Returns a reference to the array of glsl_param_type elements. + */ + domGlsl_param_type_Array &getGlsl_param_type_array() { return elemGlsl_param_type_array; } + /** + * Gets the glsl_param_type element array. + * @return Returns a constant reference to the array of glsl_param_type elements. + */ + const domGlsl_param_type_Array &getGlsl_param_type_array() const { return elemGlsl_param_type_array; } + /** + * Gets the array element array. + * @return Returns a reference to the array of array elements. + */ + domGlsl_setarray_type_Array &getArray_array() { return elemArray_array; } + /** + * Gets the array element array. + * @return Returns a constant reference to the array of array elements. + */ + const domGlsl_setarray_type_Array &getArray_array() const { return elemArray_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domGlsl_setarray_type_complexType(DAE& dae, daeElement* elt) : attrLength(), elemGlsl_param_type_array(), elemArray_array() {} + /** + * Destructor + */ + virtual ~domGlsl_setarray_type_complexType() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domGlsl_setarray_type_complexType &operator=( const domGlsl_setarray_type_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domGlsl_setarray_type_complexType. + */ +class domGlsl_setarray_type : public daeElement, public domGlsl_setarray_type_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GLSL_SETARRAY_TYPE; } + static daeInt ID() { return 104; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the length attribute. + * @return Returns a xsPositiveInteger of the length attribute. + */ + xsPositiveInteger getLength() const { return attrLength; } + /** + * Sets the length attribute. + * @param atLength The new value for the length attribute. + */ + void setLength( xsPositiveInteger atLength ) { attrLength = atLength; _validAttributeArray[0] = true; } + +protected: + /** + * Constructor + */ + domGlsl_setarray_type(DAE& dae) : daeElement(dae), domGlsl_setarray_type_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domGlsl_setarray_type() {} + /** + * Overloaded assignment operator + */ + virtual domGlsl_setarray_type &operator=( const domGlsl_setarray_type &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGlsl_setparam.h b/Engine/lib/collada/include/1.4/dom/domGlsl_setparam.h new file mode 100644 index 000000000..9744463d3 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGlsl_setparam.h @@ -0,0 +1,182 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGlsl_setparam_h__ +#define __domGlsl_setparam_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +class domGlsl_setparam_complexType +{ +protected: // Attributes + domGlsl_identifier attrRef; + xsNCName attrProgram; + +protected: // Elements + domFx_annotate_common_Array elemAnnotate_array; + domGlsl_param_typeRef elemGlsl_param_type; + domGlsl_setarray_typeRef elemArray; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the ref attribute. + * @return Returns a domGlsl_identifier of the ref attribute. + */ + domGlsl_identifier getRef() const { return attrRef; } + /** + * Sets the ref attribute. + * @param atRef The new value for the ref attribute. + */ + void setRef( domGlsl_identifier atRef ) { attrRef = atRef; } + + /** + * Gets the program attribute. + * @return Returns a xsNCName of the program attribute. + */ + xsNCName getProgram() const { return attrProgram; } + /** + * Sets the program attribute. + * @param atProgram The new value for the program attribute. + */ + void setProgram( xsNCName atProgram ) { *(daeStringRef*)&attrProgram = atProgram;} + + /** + * Gets the annotate element array. + * @return Returns a reference to the array of annotate elements. + */ + domFx_annotate_common_Array &getAnnotate_array() { return elemAnnotate_array; } + /** + * Gets the annotate element array. + * @return Returns a constant reference to the array of annotate elements. + */ + const domFx_annotate_common_Array &getAnnotate_array() const { return elemAnnotate_array; } + /** + * Gets the glsl_param_type element. + * @return a daeSmartRef to the glsl_param_type element. + */ + const domGlsl_param_typeRef getGlsl_param_type() const { return elemGlsl_param_type; } + /** + * Gets the array element. + * @return a daeSmartRef to the array element. + */ + const domGlsl_setarray_typeRef getArray() const { return elemArray; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domGlsl_setparam_complexType(DAE& dae, daeElement* elt) : attrRef(), attrProgram(), elemAnnotate_array(), elemGlsl_param_type(), elemArray() {} + /** + * Destructor + */ + virtual ~domGlsl_setparam_complexType() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domGlsl_setparam_complexType &operator=( const domGlsl_setparam_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domGlsl_setparam_complexType. + */ +class domGlsl_setparam : public daeElement, public domGlsl_setparam_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GLSL_SETPARAM; } + static daeInt ID() { return 112; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the ref attribute. + * @return Returns a domGlsl_identifier of the ref attribute. + */ + domGlsl_identifier getRef() const { return attrRef; } + /** + * Sets the ref attribute. + * @param atRef The new value for the ref attribute. + */ + void setRef( domGlsl_identifier atRef ) { attrRef = atRef; _validAttributeArray[0] = true; } + + /** + * Gets the program attribute. + * @return Returns a xsNCName of the program attribute. + */ + xsNCName getProgram() const { return attrProgram; } + /** + * Sets the program attribute. + * @param atProgram The new value for the program attribute. + */ + void setProgram( xsNCName atProgram ) { *(daeStringRef*)&attrProgram = atProgram; _validAttributeArray[1] = true; } + +protected: + /** + * Constructor + */ + domGlsl_setparam(DAE& dae) : daeElement(dae), domGlsl_setparam_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domGlsl_setparam() {} + /** + * Overloaded assignment operator + */ + virtual domGlsl_setparam &operator=( const domGlsl_setparam &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGlsl_setparam_simple.h b/Engine/lib/collada/include/1.4/dom/domGlsl_setparam_simple.h new file mode 100644 index 000000000..75f63917a --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGlsl_setparam_simple.h @@ -0,0 +1,127 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGlsl_setparam_simple_h__ +#define __domGlsl_setparam_simple_h__ + +#include +#include +#include + +#include +#include +class DAE; + +class domGlsl_setparam_simple_complexType +{ +protected: // Attribute + domGlsl_identifier attrRef; + +protected: // Elements + domFx_annotate_common_Array elemAnnotate_array; + domGlsl_param_typeRef elemGlsl_param_type; + +public: //Accessors and Mutators + /** + * Gets the ref attribute. + * @return Returns a domGlsl_identifier of the ref attribute. + */ + domGlsl_identifier getRef() const { return attrRef; } + /** + * Sets the ref attribute. + * @param atRef The new value for the ref attribute. + */ + void setRef( domGlsl_identifier atRef ) { attrRef = atRef; } + + /** + * Gets the annotate element array. + * @return Returns a reference to the array of annotate elements. + */ + domFx_annotate_common_Array &getAnnotate_array() { return elemAnnotate_array; } + /** + * Gets the annotate element array. + * @return Returns a constant reference to the array of annotate elements. + */ + const domFx_annotate_common_Array &getAnnotate_array() const { return elemAnnotate_array; } + /** + * Gets the glsl_param_type element. + * @return a daeSmartRef to the glsl_param_type element. + */ + const domGlsl_param_typeRef getGlsl_param_type() const { return elemGlsl_param_type; } +protected: + /** + * Constructor + */ + domGlsl_setparam_simple_complexType(DAE& dae, daeElement* elt) : attrRef(), elemAnnotate_array(), elemGlsl_param_type() {} + /** + * Destructor + */ + virtual ~domGlsl_setparam_simple_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domGlsl_setparam_simple_complexType &operator=( const domGlsl_setparam_simple_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domGlsl_setparam_simple_complexType. + */ +class domGlsl_setparam_simple : public daeElement, public domGlsl_setparam_simple_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GLSL_SETPARAM_SIMPLE; } + static daeInt ID() { return 111; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the ref attribute. + * @return Returns a domGlsl_identifier of the ref attribute. + */ + domGlsl_identifier getRef() const { return attrRef; } + /** + * Sets the ref attribute. + * @param atRef The new value for the ref attribute. + */ + void setRef( domGlsl_identifier atRef ) { attrRef = atRef; _validAttributeArray[0] = true; } + +protected: + /** + * Constructor + */ + domGlsl_setparam_simple(DAE& dae) : daeElement(dae), domGlsl_setparam_simple_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domGlsl_setparam_simple() {} + /** + * Overloaded assignment operator + */ + virtual domGlsl_setparam_simple &operator=( const domGlsl_setparam_simple &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domGlsl_surface_type.h b/Engine/lib/collada/include/1.4/dom/domGlsl_surface_type.h new file mode 100644 index 000000000..b4e3ce490 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domGlsl_surface_type.h @@ -0,0 +1,318 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domGlsl_surface_type_h__ +#define __domGlsl_surface_type_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include +class DAE; + +/** + * A surface type for the GLSL profile. This surface inherits from the fx_surface_common + * type and adds the ability to programmatically generate textures. + */ +class domGlsl_surface_type_complexType : public domFx_surface_common_complexType +{ +public: + class domGenerator; + + typedef daeSmartRef domGeneratorRef; + typedef daeTArray domGenerator_Array; + +/** + * A procedural surface generator. + */ + class domGenerator : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GENERATOR; } + static daeInt ID() { return 105; } + virtual daeInt typeID() const { return ID(); } + public: + class domName; + + typedef daeSmartRef domNameRef; + typedef daeTArray domName_Array; + +/** + * The entry symbol for the shader function. + */ + class domName : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::NAME; } + static daeInt ID() { return 106; } + virtual daeInt typeID() const { return ID(); } + protected: // Attribute + xsNCName attrSource; + + protected: // Value + /** + * The xsNCName value of the text data of this element. + */ + xsNCName _value; + + public: //Accessors and Mutators + /** + * Gets the source attribute. + * @return Returns a xsNCName of the source attribute. + */ + xsNCName getSource() const { return attrSource; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( xsNCName atSource ) { *(daeStringRef*)&attrSource = atSource; _validAttributeArray[0] = true; } + + /** + * Gets the value of this element. + * @return Returns a xsNCName of the value. + */ + xsNCName getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNCName val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domName(DAE& dae) : daeElement(dae), attrSource(), _value() {} + /** + * Destructor + */ + virtual ~domName() {} + /** + * Overloaded assignment operator + */ + virtual domName &operator=( const domName &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements +/** + * The annotate element allows you to specify an annotation for this surface + * generator. @see domAnnotate + */ + domFx_annotate_common_Array elemAnnotate_array; +/** + * The code element allows you to embed GLSL code to use for this surface + * generator. @see domCode + */ + domFx_code_profile_Array elemCode_array; +/** + * The include element allows you to import GLSL code to use for this surface + * generator. @see domInclude + */ + domFx_include_common_Array elemInclude_array; +/** + * The entry symbol for the shader function. @see domName + */ + domNameRef elemName; +/** + * The setparam element allows you to assign a new value to a previously defined + * parameter. @see domSetparam + */ + domGlsl_setparam_simple_Array elemSetparam_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the annotate element array. + * @return Returns a reference to the array of annotate elements. + */ + domFx_annotate_common_Array &getAnnotate_array() { return elemAnnotate_array; } + /** + * Gets the annotate element array. + * @return Returns a constant reference to the array of annotate elements. + */ + const domFx_annotate_common_Array &getAnnotate_array() const { return elemAnnotate_array; } + /** + * Gets the code element array. + * @return Returns a reference to the array of code elements. + */ + domFx_code_profile_Array &getCode_array() { return elemCode_array; } + /** + * Gets the code element array. + * @return Returns a constant reference to the array of code elements. + */ + const domFx_code_profile_Array &getCode_array() const { return elemCode_array; } + /** + * Gets the include element array. + * @return Returns a reference to the array of include elements. + */ + domFx_include_common_Array &getInclude_array() { return elemInclude_array; } + /** + * Gets the include element array. + * @return Returns a constant reference to the array of include elements. + */ + const domFx_include_common_Array &getInclude_array() const { return elemInclude_array; } + /** + * Gets the name element. + * @return a daeSmartRef to the name element. + */ + const domNameRef getName() const { return elemName; } + /** + * Gets the setparam element array. + * @return Returns a reference to the array of setparam elements. + */ + domGlsl_setparam_simple_Array &getSetparam_array() { return elemSetparam_array; } + /** + * Gets the setparam element array. + * @return Returns a constant reference to the array of setparam elements. + */ + const domGlsl_setparam_simple_Array &getSetparam_array() const { return elemSetparam_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domGenerator(DAE& dae) : daeElement(dae), elemAnnotate_array(), elemCode_array(), elemInclude_array(), elemName(), elemSetparam_array() {} + /** + * Destructor + */ + virtual ~domGenerator() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domGenerator &operator=( const domGenerator &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Element +/** + * A procedural surface generator. @see domGenerator + */ + domGeneratorRef elemGenerator; + +public: //Accessors and Mutators + /** + * Gets the generator element. + * @return a daeSmartRef to the generator element. + */ + const domGeneratorRef getGenerator() const { return elemGenerator; } +protected: + /** + * Constructor + */ + domGlsl_surface_type_complexType(DAE& dae, daeElement* elt) : domFx_surface_common_complexType(dae, elt), elemGenerator() {} + /** + * Destructor + */ + virtual ~domGlsl_surface_type_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domGlsl_surface_type_complexType &operator=( const domGlsl_surface_type_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domGlsl_surface_type_complexType. + */ +class domGlsl_surface_type : public daeElement, public domGlsl_surface_type_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::GLSL_SURFACE_TYPE; } + static daeInt ID() { return 107; } + virtual daeInt typeID() const { return ID(); } +protected: + /** + * Constructor + */ + domGlsl_surface_type(DAE& dae) : daeElement(dae), domGlsl_surface_type_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domGlsl_surface_type() {} + /** + * Overloaded assignment operator + */ + virtual domGlsl_surface_type &operator=( const domGlsl_surface_type &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domIDREF_array.h b/Engine/lib/collada/include/1.4/dom/domIDREF_array.h new file mode 100644 index 000000000..9f3751d02 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domIDREF_array.h @@ -0,0 +1,137 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domIDREF_array_h__ +#define __domIDREF_array_h__ + +#include +#include +#include + +class DAE; + +/** + * The IDREF_array element declares the storage for a homogenous array of + * ID reference values. + */ +class domIDREF_array : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::IDREF_ARRAY; } + static daeInt ID() { return 604; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; +/** + * The count attribute indicates the number of values in the array. Required + * attribute. + */ + domUint attrCount; + +protected: // Value + /** + * The xsIDREFS value of the text data of this element. + */ + xsIDREFS _value; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the count attribute. + * @return Returns a domUint of the count attribute. + */ + domUint getCount() const { return attrCount; } + /** + * Sets the count attribute. + * @param atCount The new value for the count attribute. + */ + void setCount( domUint atCount ) { attrCount = atCount; _validAttributeArray[2] = true; } + + /** + * Gets the _value array. + * @return Returns a xsIDREFS reference of the _value array. + */ + xsIDREFS &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant xsIDREFS reference of the _value array. + */ + const xsIDREFS &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const xsIDREFS &val ) { _value = val; } + +protected: + /** + * Constructor + */ + domIDREF_array(DAE& dae) : daeElement(dae), attrId(), attrName(), attrCount(), _value(new xsIDREF(*this)) {} + /** + * Destructor + */ + virtual ~domIDREF_array() {} + /** + * Overloaded assignment operator + */ + virtual domIDREF_array &operator=( const domIDREF_array &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domImage.h b/Engine/lib/collada/include/1.4/dom/domImage.h new file mode 100644 index 000000000..7eea767f2 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domImage.h @@ -0,0 +1,380 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domImage_h__ +#define __domImage_h__ + +#include +#include +#include + +#include +#include +class DAE; + +/** + * The image element declares the storage for the graphical representation + * of an object. The image element best describes raster image data, but + * can conceivably handle other forms of imagery. The image elements allows + * for specifying an external image file with the init_from element or embed + * image data with the data element. + */ +class domImage : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::IMAGE; } + static daeInt ID() { return 635; } + virtual daeInt typeID() const { return ID(); } +public: + class domData; + + typedef daeSmartRef domDataRef; + typedef daeTArray domData_Array; + +/** + * The data child element contains a sequence of hexadecimal encoded binary + * octets representing the embedded image data. + */ + class domData : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::DATA; } + static daeInt ID() { return 636; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domListOfHexBinary value of the text data of this element. + */ + domListOfHexBinary _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domListOfHexBinary reference of the _value array. + */ + domListOfHexBinary &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domListOfHexBinary reference of the _value array. + */ + const domListOfHexBinary &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domListOfHexBinary &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domData(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domData() {} + /** + * Overloaded assignment operator + */ + virtual domData &operator=( const domData &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInit_from; + + typedef daeSmartRef domInit_fromRef; + typedef daeTArray domInit_from_Array; + +/** + * The init_from element allows you to specify an external image file to use + * for the image element. + */ + class domInit_from : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INIT_FROM; } + static daeInt ID() { return 637; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsAnyURI value of the text data of this element. + */ + xsAnyURI _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsAnyURI of the value. + */ + xsAnyURI &getValue() { return _value; } + /** + * Gets the value of this element. + * @return Returns a constant xsAnyURI of the value. + */ + const xsAnyURI &getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( const xsAnyURI &val ) { _value = val; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsString val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInit_from(DAE& dae) : daeElement(dae), _value(dae, *this) {} + /** + * Destructor + */ + virtual ~domInit_from() {} + /** + * Overloaded assignment operator + */ + virtual domInit_from &operator=( const domInit_from &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; +/** + * The format attribute is a text string value that indicates the image format. + * Optional attribute. + */ + xsToken attrFormat; +/** + * The height attribute is an integer value that indicates the height of + * the image in pixel units. Optional attribute. + */ + domUint attrHeight; +/** + * The width attribute is an integer value that indicates the width of the + * image in pixel units. Optional attribute. + */ + domUint attrWidth; +/** + * The depth attribute is an integer value that indicates the depth of the + * image in pixel units. A 2-D image has a depth of 1, which is also the + * default value. Optional attribute. + */ + domUint attrDepth; + +protected: // Elements +/** + * The image element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * The data child element contains a sequence of hexadecimal encoded binary + * octets representing the embedded image data. @see domData + */ + domDataRef elemData; +/** + * The init_from element allows you to specify an external image file to use + * for the image element. @see domInit_from + */ + domInit_fromRef elemInit_from; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the format attribute. + * @return Returns a xsToken of the format attribute. + */ + xsToken getFormat() const { return attrFormat; } + /** + * Sets the format attribute. + * @param atFormat The new value for the format attribute. + */ + void setFormat( xsToken atFormat ) { *(daeStringRef*)&attrFormat = atFormat; _validAttributeArray[2] = true; } + + /** + * Gets the height attribute. + * @return Returns a domUint of the height attribute. + */ + domUint getHeight() const { return attrHeight; } + /** + * Sets the height attribute. + * @param atHeight The new value for the height attribute. + */ + void setHeight( domUint atHeight ) { attrHeight = atHeight; _validAttributeArray[3] = true; } + + /** + * Gets the width attribute. + * @return Returns a domUint of the width attribute. + */ + domUint getWidth() const { return attrWidth; } + /** + * Sets the width attribute. + * @param atWidth The new value for the width attribute. + */ + void setWidth( domUint atWidth ) { attrWidth = atWidth; _validAttributeArray[4] = true; } + + /** + * Gets the depth attribute. + * @return Returns a domUint of the depth attribute. + */ + domUint getDepth() const { return attrDepth; } + /** + * Sets the depth attribute. + * @param atDepth The new value for the depth attribute. + */ + void setDepth( domUint atDepth ) { attrDepth = atDepth; _validAttributeArray[5] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the data element. + * @return a daeSmartRef to the data element. + */ + const domDataRef getData() const { return elemData; } + /** + * Gets the init_from element. + * @return a daeSmartRef to the init_from element. + */ + const domInit_fromRef getInit_from() const { return elemInit_from; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domImage(DAE& dae) : daeElement(dae), attrId(), attrName(), attrFormat(), attrHeight(), attrWidth(), attrDepth(), elemAsset(), elemData(), elemInit_from(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domImage() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domImage &operator=( const domImage &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domInputGlobal.h b/Engine/lib/collada/include/1.4/dom/domInputGlobal.h new file mode 100644 index 000000000..e4e6b9f58 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domInputGlobal.h @@ -0,0 +1,162 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domInputGlobal_h__ +#define __domInputGlobal_h__ + +#include +#include +#include + +class DAE; + +/** + * The InputGlobal type is used to represent inputs that can reference external + * resources. + */ +class domInputGlobal_complexType +{ +protected: // Attributes +/** + * The semantic attribute is the user-defined meaning of the input connection. + * Required attribute. + */ + xsNMTOKEN attrSemantic; +/** + * The source attribute indicates the location of the data source. Required + * attribute. + */ + xsAnyURI attrSource; + + +public: //Accessors and Mutators + /** + * Gets the semantic attribute. + * @return Returns a xsNMTOKEN of the semantic attribute. + */ + xsNMTOKEN getSemantic() const { return attrSemantic; } + /** + * Sets the semantic attribute. + * @param atSemantic The new value for the semantic attribute. + */ + void setSemantic( xsNMTOKEN atSemantic ) { *(daeStringRef*)&attrSemantic = atSemantic;} + + /** + * Gets the source attribute. + * @return Returns a xsAnyURI reference of the source attribute. + */ + xsAnyURI &getSource() { return attrSource; } + /** + * Gets the source attribute. + * @return Returns a constant xsAnyURI reference of the source attribute. + */ + const xsAnyURI &getSource() const { return attrSource; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( const xsAnyURI &atSource ) { attrSource = atSource; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( xsString atSource ) { attrSource = atSource; } + +protected: + /** + * Constructor + */ + domInputGlobal_complexType(DAE& dae, daeElement* elt) : attrSemantic(), attrSource(dae, *elt) {} + /** + * Destructor + */ + virtual ~domInputGlobal_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domInputGlobal_complexType &operator=( const domInputGlobal_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domInputGlobal_complexType. + */ +class domInputGlobal : public daeElement, public domInputGlobal_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INPUTGLOBAL; } + static daeInt ID() { return 0; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the semantic attribute. + * @return Returns a xsNMTOKEN of the semantic attribute. + */ + xsNMTOKEN getSemantic() const { return attrSemantic; } + /** + * Sets the semantic attribute. + * @param atSemantic The new value for the semantic attribute. + */ + void setSemantic( xsNMTOKEN atSemantic ) { *(daeStringRef*)&attrSemantic = atSemantic; _validAttributeArray[0] = true; } + + /** + * Gets the source attribute. + * @return Returns a xsAnyURI reference of the source attribute. + */ + xsAnyURI &getSource() { return attrSource; } + /** + * Gets the source attribute. + * @return Returns a constant xsAnyURI reference of the source attribute. + */ + const xsAnyURI &getSource() const { return attrSource; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( const xsAnyURI &atSource ) { attrSource = atSource; _validAttributeArray[1] = true; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( xsString atSource ) { attrSource = atSource; _validAttributeArray[1] = true; } + +protected: + /** + * Constructor + */ + domInputGlobal(DAE& dae) : daeElement(dae), domInputGlobal_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domInputGlobal() {} + /** + * Overloaded assignment operator + */ + virtual domInputGlobal &operator=( const domInputGlobal &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domInputLocal.h b/Engine/lib/collada/include/1.4/dom/domInputLocal.h new file mode 100644 index 000000000..0c2d16f2e --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domInputLocal.h @@ -0,0 +1,162 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domInputLocal_h__ +#define __domInputLocal_h__ + +#include +#include +#include + +class DAE; + +/** + * The InputLocal type is used to represent inputs that can only reference + * resources declared in the same document. + */ +class domInputLocal_complexType +{ +protected: // Attributes +/** + * The semantic attribute is the user-defined meaning of the input connection. + * Required attribute. + */ + xsNMTOKEN attrSemantic; +/** + * The source attribute indicates the location of the data source. Required + * attribute. + */ + domURIFragmentType attrSource; + + +public: //Accessors and Mutators + /** + * Gets the semantic attribute. + * @return Returns a xsNMTOKEN of the semantic attribute. + */ + xsNMTOKEN getSemantic() const { return attrSemantic; } + /** + * Sets the semantic attribute. + * @param atSemantic The new value for the semantic attribute. + */ + void setSemantic( xsNMTOKEN atSemantic ) { *(daeStringRef*)&attrSemantic = atSemantic;} + + /** + * Gets the source attribute. + * @return Returns a domURIFragmentType reference of the source attribute. + */ + domURIFragmentType &getSource() { return attrSource; } + /** + * Gets the source attribute. + * @return Returns a constant domURIFragmentType reference of the source attribute. + */ + const domURIFragmentType &getSource() const { return attrSource; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( const domURIFragmentType &atSource ) { attrSource = atSource; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( xsString atSource ) { attrSource = atSource; } + +protected: + /** + * Constructor + */ + domInputLocal_complexType(DAE& dae, daeElement* elt) : attrSemantic(), attrSource(dae, *elt) {} + /** + * Destructor + */ + virtual ~domInputLocal_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domInputLocal_complexType &operator=( const domInputLocal_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domInputLocal_complexType. + */ +class domInputLocal : public daeElement, public domInputLocal_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INPUTLOCAL; } + static daeInt ID() { return 1; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the semantic attribute. + * @return Returns a xsNMTOKEN of the semantic attribute. + */ + xsNMTOKEN getSemantic() const { return attrSemantic; } + /** + * Sets the semantic attribute. + * @param atSemantic The new value for the semantic attribute. + */ + void setSemantic( xsNMTOKEN atSemantic ) { *(daeStringRef*)&attrSemantic = atSemantic; _validAttributeArray[0] = true; } + + /** + * Gets the source attribute. + * @return Returns a domURIFragmentType reference of the source attribute. + */ + domURIFragmentType &getSource() { return attrSource; } + /** + * Gets the source attribute. + * @return Returns a constant domURIFragmentType reference of the source attribute. + */ + const domURIFragmentType &getSource() const { return attrSource; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( const domURIFragmentType &atSource ) { attrSource = atSource; _validAttributeArray[1] = true; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( xsString atSource ) { attrSource = atSource; _validAttributeArray[1] = true; } + +protected: + /** + * Constructor + */ + domInputLocal(DAE& dae) : daeElement(dae), domInputLocal_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domInputLocal() {} + /** + * Overloaded assignment operator + */ + virtual domInputLocal &operator=( const domInputLocal &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domInputLocalOffset.h b/Engine/lib/collada/include/1.4/dom/domInputLocalOffset.h new file mode 100644 index 000000000..10f8d93d6 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domInputLocalOffset.h @@ -0,0 +1,218 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domInputLocalOffset_h__ +#define __domInputLocalOffset_h__ + +#include +#include +#include + +class DAE; + +/** + * The InputLocalOffset type is used to represent indexed inputs that can + * only reference resources declared in the same document. + */ +class domInputLocalOffset_complexType +{ +protected: // Attributes +/** + * The offset attribute represents the offset into the list of indices. + * If two input elements share the same offset, they will be indexed the + * same. This works as a simple form of compression for the list of indices + * as well as defining the order the inputs should be used in. Required attribute. + */ + domUint attrOffset; +/** + * The semantic attribute is the user-defined meaning of the input connection. + * Required attribute. + */ + xsNMTOKEN attrSemantic; +/** + * The source attribute indicates the location of the data source. Required + * attribute. + */ + domURIFragmentType attrSource; +/** + * The set attribute indicates which inputs should be grouped together as + * a single set. This is helpful when multiple inputs share the same semantics. + */ + domUint attrSet; + + +public: //Accessors and Mutators + /** + * Gets the offset attribute. + * @return Returns a domUint of the offset attribute. + */ + domUint getOffset() const { return attrOffset; } + /** + * Sets the offset attribute. + * @param atOffset The new value for the offset attribute. + */ + void setOffset( domUint atOffset ) { attrOffset = atOffset; } + + /** + * Gets the semantic attribute. + * @return Returns a xsNMTOKEN of the semantic attribute. + */ + xsNMTOKEN getSemantic() const { return attrSemantic; } + /** + * Sets the semantic attribute. + * @param atSemantic The new value for the semantic attribute. + */ + void setSemantic( xsNMTOKEN atSemantic ) { *(daeStringRef*)&attrSemantic = atSemantic;} + + /** + * Gets the source attribute. + * @return Returns a domURIFragmentType reference of the source attribute. + */ + domURIFragmentType &getSource() { return attrSource; } + /** + * Gets the source attribute. + * @return Returns a constant domURIFragmentType reference of the source attribute. + */ + const domURIFragmentType &getSource() const { return attrSource; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( const domURIFragmentType &atSource ) { attrSource = atSource; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( xsString atSource ) { attrSource = atSource; } + + /** + * Gets the set attribute. + * @return Returns a domUint of the set attribute. + */ + domUint getSet() const { return attrSet; } + /** + * Sets the set attribute. + * @param atSet The new value for the set attribute. + */ + void setSet( domUint atSet ) { attrSet = atSet; } + +protected: + /** + * Constructor + */ + domInputLocalOffset_complexType(DAE& dae, daeElement* elt) : attrOffset(), attrSemantic(), attrSource(dae, *elt), attrSet() {} + /** + * Destructor + */ + virtual ~domInputLocalOffset_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domInputLocalOffset_complexType &operator=( const domInputLocalOffset_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domInputLocalOffset_complexType. + */ +class domInputLocalOffset : public daeElement, public domInputLocalOffset_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INPUTLOCALOFFSET; } + static daeInt ID() { return 2; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the offset attribute. + * @return Returns a domUint of the offset attribute. + */ + domUint getOffset() const { return attrOffset; } + /** + * Sets the offset attribute. + * @param atOffset The new value for the offset attribute. + */ + void setOffset( domUint atOffset ) { attrOffset = atOffset; _validAttributeArray[0] = true; } + + /** + * Gets the semantic attribute. + * @return Returns a xsNMTOKEN of the semantic attribute. + */ + xsNMTOKEN getSemantic() const { return attrSemantic; } + /** + * Sets the semantic attribute. + * @param atSemantic The new value for the semantic attribute. + */ + void setSemantic( xsNMTOKEN atSemantic ) { *(daeStringRef*)&attrSemantic = atSemantic; _validAttributeArray[1] = true; } + + /** + * Gets the source attribute. + * @return Returns a domURIFragmentType reference of the source attribute. + */ + domURIFragmentType &getSource() { return attrSource; } + /** + * Gets the source attribute. + * @return Returns a constant domURIFragmentType reference of the source attribute. + */ + const domURIFragmentType &getSource() const { return attrSource; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( const domURIFragmentType &atSource ) { attrSource = atSource; _validAttributeArray[2] = true; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( xsString atSource ) { attrSource = atSource; _validAttributeArray[2] = true; } + + /** + * Gets the set attribute. + * @return Returns a domUint of the set attribute. + */ + domUint getSet() const { return attrSet; } + /** + * Sets the set attribute. + * @param atSet The new value for the set attribute. + */ + void setSet( domUint atSet ) { attrSet = atSet; _validAttributeArray[3] = true; } + +protected: + /** + * Constructor + */ + domInputLocalOffset(DAE& dae) : daeElement(dae), domInputLocalOffset_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domInputLocalOffset() {} + /** + * Overloaded assignment operator + */ + virtual domInputLocalOffset &operator=( const domInputLocalOffset &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domInstanceWithExtra.h b/Engine/lib/collada/include/1.4/dom/domInstanceWithExtra.h new file mode 100644 index 000000000..139a39fad --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domInstanceWithExtra.h @@ -0,0 +1,208 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domInstanceWithExtra_h__ +#define __domInstanceWithExtra_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * The InstanceWithExtra type is used for all generic instance elements. A + * generic instance element is one which does not have any specific child + * elements declared. + */ +class domInstanceWithExtra_complexType +{ +protected: // Attributes +/** + * The url attribute refers to resource to instantiate. This may refer to + * a local resource using a relative URL fragment identifier that begins + * with the “#” character. The url attribute may refer to an external + * resource using an absolute or relative URL. + */ + xsAnyURI attrUrl; +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Element +/** + * The extra element may occur any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the url attribute. + * @return Returns a xsAnyURI reference of the url attribute. + */ + xsAnyURI &getUrl() { return attrUrl; } + /** + * Gets the url attribute. + * @return Returns a constant xsAnyURI reference of the url attribute. + */ + const xsAnyURI &getUrl() const { return attrUrl; } + /** + * Sets the url attribute. + * @param atUrl The new value for the url attribute. + */ + void setUrl( const xsAnyURI &atUrl ) { attrUrl = atUrl; } + /** + * Sets the url attribute. + * @param atUrl The new value for the url attribute. + */ + void setUrl( xsString atUrl ) { attrUrl = atUrl; } + + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid;} + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName;} + + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domInstanceWithExtra_complexType(DAE& dae, daeElement* elt) : attrUrl(dae, *elt), attrSid(), attrName(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domInstanceWithExtra_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domInstanceWithExtra_complexType &operator=( const domInstanceWithExtra_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domInstanceWithExtra_complexType. + */ +class domInstanceWithExtra : public daeElement, public domInstanceWithExtra_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INSTANCEWITHEXTRA; } + static daeInt ID() { return 3; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the url attribute. + * @return Returns a xsAnyURI reference of the url attribute. + */ + xsAnyURI &getUrl() { return attrUrl; } + /** + * Gets the url attribute. + * @return Returns a constant xsAnyURI reference of the url attribute. + */ + const xsAnyURI &getUrl() const { return attrUrl; } + /** + * Sets the url attribute. + * @param atUrl The new value for the url attribute. + */ + void setUrl( const xsAnyURI &atUrl ) { attrUrl = atUrl; _validAttributeArray[0] = true; } + /** + * Sets the url attribute. + * @param atUrl The new value for the url attribute. + */ + void setUrl( xsString atUrl ) { attrUrl = atUrl; _validAttributeArray[0] = true; } + + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[1] = true; } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[2] = true; } + +protected: + /** + * Constructor + */ + domInstanceWithExtra(DAE& dae) : daeElement(dae), domInstanceWithExtra_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domInstanceWithExtra() {} + /** + * Overloaded assignment operator + */ + virtual domInstanceWithExtra &operator=( const domInstanceWithExtra &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domInstance_camera.h b/Engine/lib/collada/include/1.4/dom/domInstance_camera.h new file mode 100644 index 000000000..848b989e4 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domInstance_camera.h @@ -0,0 +1,107 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domInstance_camera_h__ +#define __domInstance_camera_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * The instance_camera element declares the instantiation of a COLLADA camera + * resource. + */ +class domInstance_camera : public daeElement, public domInstanceWithExtra_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INSTANCE_CAMERA; } + static daeInt ID() { return 688; } + virtual daeInt typeID() const { return ID(); } + + /** + * Gets the url attribute. + * @return Returns a xsAnyURI reference of the url attribute. + */ + xsAnyURI &getUrl() { return attrUrl; } + /** + * Gets the url attribute. + * @return Returns a constant xsAnyURI reference of the url attribute. + */ + const xsAnyURI &getUrl() const { return attrUrl; } + /** + * Sets the url attribute. + * @param atUrl The new value for the url attribute. + */ + void setUrl( const xsAnyURI &atUrl ) { attrUrl = atUrl; _validAttributeArray[0] = true; } + /** + * Sets the url attribute. + * @param atUrl The new value for the url attribute. + */ + void setUrl( xsString atUrl ) { attrUrl = atUrl; _validAttributeArray[0] = true; } + + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[1] = true; } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[2] = true; } + +protected: + /** + * Constructor + */ + domInstance_camera(DAE& dae) : daeElement(dae), domInstanceWithExtra_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domInstance_camera() {} + /** + * Overloaded assignment operator + */ + virtual domInstance_camera &operator=( const domInstance_camera &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domInstance_controller.h b/Engine/lib/collada/include/1.4/dom/domInstance_controller.h new file mode 100644 index 000000000..7e2bcc339 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domInstance_controller.h @@ -0,0 +1,244 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domInstance_controller_h__ +#define __domInstance_controller_h__ + +#include +#include +#include + +#include +#include +class DAE; + +/** + * The instance_controller element declares the instantiation of a COLLADA + * controller resource. + */ +class domInstance_controller : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INSTANCE_CONTROLLER; } + static daeInt ID() { return 689; } + virtual daeInt typeID() const { return ID(); } +public: + class domSkeleton; + + typedef daeSmartRef domSkeletonRef; + typedef daeTArray domSkeleton_Array; + +/** + * The skeleton element is used to indicate where a skin controller is to + * start to search for the joint nodes it needs. This element is meaningless + * for morph controllers. + */ + class domSkeleton : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SKELETON; } + static daeInt ID() { return 690; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsAnyURI value of the text data of this element. + */ + xsAnyURI _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsAnyURI of the value. + */ + xsAnyURI &getValue() { return _value; } + /** + * Gets the value of this element. + * @return Returns a constant xsAnyURI of the value. + */ + const xsAnyURI &getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( const xsAnyURI &val ) { _value = val; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsString val ) { _value = val; } + + protected: + /** + * Constructor + */ + domSkeleton(DAE& dae) : daeElement(dae), _value(dae, *this) {} + /** + * Destructor + */ + virtual ~domSkeleton() {} + /** + * Overloaded assignment operator + */ + virtual domSkeleton &operator=( const domSkeleton &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attributes +/** + * The url attribute refers to resource. This may refer to a local resource + * using a relative URL fragment identifier that begins with the “#” + * character. The url attribute may refer to an external resource using an + * absolute or relative URL. + */ + xsAnyURI attrUrl; +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The skeleton element is used to indicate where a skin controller is to + * start to search for the joint nodes it needs. This element is meaningless + * for morph controllers. @see domSkeleton + */ + domSkeleton_Array elemSkeleton_array; +/** + * Bind a specific material to a piece of geometry, binding varying and uniform + * parameters at the same time. @see domBind_material + */ + domBind_materialRef elemBind_material; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the url attribute. + * @return Returns a xsAnyURI reference of the url attribute. + */ + xsAnyURI &getUrl() { return attrUrl; } + /** + * Gets the url attribute. + * @return Returns a constant xsAnyURI reference of the url attribute. + */ + const xsAnyURI &getUrl() const { return attrUrl; } + /** + * Sets the url attribute. + * @param atUrl The new value for the url attribute. + */ + void setUrl( const xsAnyURI &atUrl ) { attrUrl = atUrl; _validAttributeArray[0] = true; } + /** + * Sets the url attribute. + * @param atUrl The new value for the url attribute. + */ + void setUrl( xsString atUrl ) { attrUrl = atUrl; _validAttributeArray[0] = true; } + + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[1] = true; } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[2] = true; } + + /** + * Gets the skeleton element array. + * @return Returns a reference to the array of skeleton elements. + */ + domSkeleton_Array &getSkeleton_array() { return elemSkeleton_array; } + /** + * Gets the skeleton element array. + * @return Returns a constant reference to the array of skeleton elements. + */ + const domSkeleton_Array &getSkeleton_array() const { return elemSkeleton_array; } + /** + * Gets the bind_material element. + * @return a daeSmartRef to the bind_material element. + */ + const domBind_materialRef getBind_material() const { return elemBind_material; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domInstance_controller(DAE& dae) : daeElement(dae), attrUrl(dae, *this), attrSid(), attrName(), elemSkeleton_array(), elemBind_material(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domInstance_controller() {} + /** + * Overloaded assignment operator + */ + virtual domInstance_controller &operator=( const domInstance_controller &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domInstance_effect.h b/Engine/lib/collada/include/1.4/dom/domInstance_effect.h new file mode 100644 index 000000000..578218059 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domInstance_effect.h @@ -0,0 +1,332 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domInstance_effect_h__ +#define __domInstance_effect_h__ + +#include +#include +#include + +#include +#include +class DAE; + +/** + * The instance_effect element declares the instantiation of a COLLADA effect + * resource. + */ +class domInstance_effect : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INSTANCE_EFFECT; } + static daeInt ID() { return 691; } + virtual daeInt typeID() const { return ID(); } +public: + class domTechnique_hint; + + typedef daeSmartRef domTechnique_hintRef; + typedef daeTArray domTechnique_hint_Array; + +/** + * Add a hint for a platform of which technique to use in this effect. + */ + class domTechnique_hint : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TECHNIQUE_HINT; } + static daeInt ID() { return 692; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes +/** + * A platform defines a string that specifies which platform this is hint + * is aimed for. + */ + xsNCName attrPlatform; +/** + * A profile defines a string that specifies which API profile this is hint + * is aimed for. + */ + xsNCName attrProfile; +/** + * A reference to the technique to use for the specified platform. + */ + xsNCName attrRef; + + + public: //Accessors and Mutators + /** + * Gets the platform attribute. + * @return Returns a xsNCName of the platform attribute. + */ + xsNCName getPlatform() const { return attrPlatform; } + /** + * Sets the platform attribute. + * @param atPlatform The new value for the platform attribute. + */ + void setPlatform( xsNCName atPlatform ) { *(daeStringRef*)&attrPlatform = atPlatform; _validAttributeArray[0] = true; } + + /** + * Gets the profile attribute. + * @return Returns a xsNCName of the profile attribute. + */ + xsNCName getProfile() const { return attrProfile; } + /** + * Sets the profile attribute. + * @param atProfile The new value for the profile attribute. + */ + void setProfile( xsNCName atProfile ) { *(daeStringRef*)&attrProfile = atProfile; _validAttributeArray[1] = true; } + + /** + * Gets the ref attribute. + * @return Returns a xsNCName of the ref attribute. + */ + xsNCName getRef() const { return attrRef; } + /** + * Sets the ref attribute. + * @param atRef The new value for the ref attribute. + */ + void setRef( xsNCName atRef ) { *(daeStringRef*)&attrRef = atRef; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domTechnique_hint(DAE& dae) : daeElement(dae), attrPlatform(), attrProfile(), attrRef() {} + /** + * Destructor + */ + virtual ~domTechnique_hint() {} + /** + * Overloaded assignment operator + */ + virtual domTechnique_hint &operator=( const domTechnique_hint &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domSetparam; + + typedef daeSmartRef domSetparamRef; + typedef daeTArray domSetparam_Array; + +/** + * Assigns a new value to a previously defined parameter + */ + class domSetparam : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SETPARAM; } + static daeInt ID() { return 693; } + virtual daeInt typeID() const { return ID(); } + protected: // Attribute + xsToken attrRef; + + protected: // Element + domFx_basic_type_commonRef elemFx_basic_type_common; + + public: //Accessors and Mutators + /** + * Gets the ref attribute. + * @return Returns a xsToken of the ref attribute. + */ + xsToken getRef() const { return attrRef; } + /** + * Sets the ref attribute. + * @param atRef The new value for the ref attribute. + */ + void setRef( xsToken atRef ) { *(daeStringRef*)&attrRef = atRef; _validAttributeArray[0] = true; } + + /** + * Gets the fx_basic_type_common element. + * @return a daeSmartRef to the fx_basic_type_common element. + */ + const domFx_basic_type_commonRef getFx_basic_type_common() const { return elemFx_basic_type_common; } + protected: + /** + * Constructor + */ + domSetparam(DAE& dae) : daeElement(dae), attrRef(), elemFx_basic_type_common() {} + /** + * Destructor + */ + virtual ~domSetparam() {} + /** + * Overloaded assignment operator + */ + virtual domSetparam &operator=( const domSetparam &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attributes +/** + * The url attribute refers to resource. This may refer to a local resource + * using a relative URL fragment identifier that begins with the “#” + * character. The url attribute may refer to an external resource using an + * absolute or relative URL. + */ + xsAnyURI attrUrl; +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * Add a hint for a platform of which technique to use in this effect. @see + * domTechnique_hint + */ + domTechnique_hint_Array elemTechnique_hint_array; +/** + * Assigns a new value to a previously defined parameter @see domSetparam + */ + domSetparam_Array elemSetparam_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the url attribute. + * @return Returns a xsAnyURI reference of the url attribute. + */ + xsAnyURI &getUrl() { return attrUrl; } + /** + * Gets the url attribute. + * @return Returns a constant xsAnyURI reference of the url attribute. + */ + const xsAnyURI &getUrl() const { return attrUrl; } + /** + * Sets the url attribute. + * @param atUrl The new value for the url attribute. + */ + void setUrl( const xsAnyURI &atUrl ) { attrUrl = atUrl; _validAttributeArray[0] = true; } + /** + * Sets the url attribute. + * @param atUrl The new value for the url attribute. + */ + void setUrl( xsString atUrl ) { attrUrl = atUrl; _validAttributeArray[0] = true; } + + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[1] = true; } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[2] = true; } + + /** + * Gets the technique_hint element array. + * @return Returns a reference to the array of technique_hint elements. + */ + domTechnique_hint_Array &getTechnique_hint_array() { return elemTechnique_hint_array; } + /** + * Gets the technique_hint element array. + * @return Returns a constant reference to the array of technique_hint elements. + */ + const domTechnique_hint_Array &getTechnique_hint_array() const { return elemTechnique_hint_array; } + /** + * Gets the setparam element array. + * @return Returns a reference to the array of setparam elements. + */ + domSetparam_Array &getSetparam_array() { return elemSetparam_array; } + /** + * Gets the setparam element array. + * @return Returns a constant reference to the array of setparam elements. + */ + const domSetparam_Array &getSetparam_array() const { return elemSetparam_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domInstance_effect(DAE& dae) : daeElement(dae), attrUrl(dae, *this), attrSid(), attrName(), elemTechnique_hint_array(), elemSetparam_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domInstance_effect() {} + /** + * Overloaded assignment operator + */ + virtual domInstance_effect &operator=( const domInstance_effect &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domInstance_force_field.h b/Engine/lib/collada/include/1.4/dom/domInstance_force_field.h new file mode 100644 index 000000000..0547b3bc9 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domInstance_force_field.h @@ -0,0 +1,107 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domInstance_force_field_h__ +#define __domInstance_force_field_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * The instance_force_field element declares the instantiation of a COLLADA + * force_field resource. + */ +class domInstance_force_field : public daeElement, public domInstanceWithExtra_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INSTANCE_FORCE_FIELD; } + static daeInt ID() { return 694; } + virtual daeInt typeID() const { return ID(); } + + /** + * Gets the url attribute. + * @return Returns a xsAnyURI reference of the url attribute. + */ + xsAnyURI &getUrl() { return attrUrl; } + /** + * Gets the url attribute. + * @return Returns a constant xsAnyURI reference of the url attribute. + */ + const xsAnyURI &getUrl() const { return attrUrl; } + /** + * Sets the url attribute. + * @param atUrl The new value for the url attribute. + */ + void setUrl( const xsAnyURI &atUrl ) { attrUrl = atUrl; _validAttributeArray[0] = true; } + /** + * Sets the url attribute. + * @param atUrl The new value for the url attribute. + */ + void setUrl( xsString atUrl ) { attrUrl = atUrl; _validAttributeArray[0] = true; } + + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[1] = true; } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[2] = true; } + +protected: + /** + * Constructor + */ + domInstance_force_field(DAE& dae) : daeElement(dae), domInstanceWithExtra_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domInstance_force_field() {} + /** + * Overloaded assignment operator + */ + virtual domInstance_force_field &operator=( const domInstance_force_field &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domInstance_geometry.h b/Engine/lib/collada/include/1.4/dom/domInstance_geometry.h new file mode 100644 index 000000000..3ed258d9a --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domInstance_geometry.h @@ -0,0 +1,153 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domInstance_geometry_h__ +#define __domInstance_geometry_h__ + +#include +#include +#include + +#include +#include +class DAE; + +/** + * The instance_geometry element declares the instantiation of a COLLADA geometry + * resource. + */ +class domInstance_geometry : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INSTANCE_GEOMETRY; } + static daeInt ID() { return 695; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The url attribute refers to resource. This may refer to a local resource + * using a relative URL fragment identifier that begins with the “#” + * character. The url attribute may refer to an external resource using an + * absolute or relative URL. + */ + xsAnyURI attrUrl; +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * Bind a specific material to a piece of geometry, binding varying and uniform + * parameters at the same time. @see domBind_material + */ + domBind_materialRef elemBind_material; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the url attribute. + * @return Returns a xsAnyURI reference of the url attribute. + */ + xsAnyURI &getUrl() { return attrUrl; } + /** + * Gets the url attribute. + * @return Returns a constant xsAnyURI reference of the url attribute. + */ + const xsAnyURI &getUrl() const { return attrUrl; } + /** + * Sets the url attribute. + * @param atUrl The new value for the url attribute. + */ + void setUrl( const xsAnyURI &atUrl ) { attrUrl = atUrl; _validAttributeArray[0] = true; } + /** + * Sets the url attribute. + * @param atUrl The new value for the url attribute. + */ + void setUrl( xsString atUrl ) { attrUrl = atUrl; _validAttributeArray[0] = true; } + + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[1] = true; } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[2] = true; } + + /** + * Gets the bind_material element. + * @return a daeSmartRef to the bind_material element. + */ + const domBind_materialRef getBind_material() const { return elemBind_material; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domInstance_geometry(DAE& dae) : daeElement(dae), attrUrl(dae, *this), attrSid(), attrName(), elemBind_material(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domInstance_geometry() {} + /** + * Overloaded assignment operator + */ + virtual domInstance_geometry &operator=( const domInstance_geometry &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domInstance_light.h b/Engine/lib/collada/include/1.4/dom/domInstance_light.h new file mode 100644 index 000000000..bf17d1252 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domInstance_light.h @@ -0,0 +1,107 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domInstance_light_h__ +#define __domInstance_light_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * The instance_light element declares the instantiation of a COLLADA light + * resource. + */ +class domInstance_light : public daeElement, public domInstanceWithExtra_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INSTANCE_LIGHT; } + static daeInt ID() { return 696; } + virtual daeInt typeID() const { return ID(); } + + /** + * Gets the url attribute. + * @return Returns a xsAnyURI reference of the url attribute. + */ + xsAnyURI &getUrl() { return attrUrl; } + /** + * Gets the url attribute. + * @return Returns a constant xsAnyURI reference of the url attribute. + */ + const xsAnyURI &getUrl() const { return attrUrl; } + /** + * Sets the url attribute. + * @param atUrl The new value for the url attribute. + */ + void setUrl( const xsAnyURI &atUrl ) { attrUrl = atUrl; _validAttributeArray[0] = true; } + /** + * Sets the url attribute. + * @param atUrl The new value for the url attribute. + */ + void setUrl( xsString atUrl ) { attrUrl = atUrl; _validAttributeArray[0] = true; } + + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[1] = true; } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[2] = true; } + +protected: + /** + * Constructor + */ + domInstance_light(DAE& dae) : daeElement(dae), domInstanceWithExtra_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domInstance_light() {} + /** + * Overloaded assignment operator + */ + virtual domInstance_light &operator=( const domInstance_light &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domInstance_material.h b/Engine/lib/collada/include/1.4/dom/domInstance_material.h new file mode 100644 index 000000000..e97ff8331 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domInstance_material.h @@ -0,0 +1,358 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domInstance_material_h__ +#define __domInstance_material_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * The instance_material element declares the instantiation of a COLLADA material + * resource. + */ +class domInstance_material : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INSTANCE_MATERIAL; } + static daeInt ID() { return 697; } + virtual daeInt typeID() const { return ID(); } +public: + class domBind; + + typedef daeSmartRef domBindRef; + typedef daeTArray domBind_Array; + +/** + * The bind element binds values to effect parameters upon instantiation. + */ + class domBind : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BIND; } + static daeInt ID() { return 698; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes +/** + * The semantic attribute specifies which effect parameter to bind. + */ + xsNCName attrSemantic; +/** + * The target attribute specifies the location of the value to bind to the + * specified semantic. This text string is a path-name following a simple + * syntax described in the “Addressing Syntax” section. + */ + xsToken attrTarget; + + + public: //Accessors and Mutators + /** + * Gets the semantic attribute. + * @return Returns a xsNCName of the semantic attribute. + */ + xsNCName getSemantic() const { return attrSemantic; } + /** + * Sets the semantic attribute. + * @param atSemantic The new value for the semantic attribute. + */ + void setSemantic( xsNCName atSemantic ) { *(daeStringRef*)&attrSemantic = atSemantic; _validAttributeArray[0] = true; } + + /** + * Gets the target attribute. + * @return Returns a xsToken of the target attribute. + */ + xsToken getTarget() const { return attrTarget; } + /** + * Sets the target attribute. + * @param atTarget The new value for the target attribute. + */ + void setTarget( xsToken atTarget ) { *(daeStringRef*)&attrTarget = atTarget; _validAttributeArray[1] = true; } + + protected: + /** + * Constructor + */ + domBind(DAE& dae) : daeElement(dae), attrSemantic(), attrTarget() {} + /** + * Destructor + */ + virtual ~domBind() {} + /** + * Overloaded assignment operator + */ + virtual domBind &operator=( const domBind &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBind_vertex_input; + + typedef daeSmartRef domBind_vertex_inputRef; + typedef daeTArray domBind_vertex_input_Array; + +/** + * The bind_vertex_input element binds vertex inputs to effect parameters + * upon instantiation. + */ + class domBind_vertex_input : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BIND_VERTEX_INPUT; } + static daeInt ID() { return 699; } + virtual daeInt typeID() const { return ID(); } + protected: // Attributes +/** + * The semantic attribute specifies which effect parameter to bind. + */ + xsNCName attrSemantic; +/** + * The input_semantic attribute specifies which input semantic to bind. + */ + xsNCName attrInput_semantic; +/** + * The input_set attribute specifies which input set to bind. + */ + domUint attrInput_set; + + + public: //Accessors and Mutators + /** + * Gets the semantic attribute. + * @return Returns a xsNCName of the semantic attribute. + */ + xsNCName getSemantic() const { return attrSemantic; } + /** + * Sets the semantic attribute. + * @param atSemantic The new value for the semantic attribute. + */ + void setSemantic( xsNCName atSemantic ) { *(daeStringRef*)&attrSemantic = atSemantic; _validAttributeArray[0] = true; } + + /** + * Gets the input_semantic attribute. + * @return Returns a xsNCName of the input_semantic attribute. + */ + xsNCName getInput_semantic() const { return attrInput_semantic; } + /** + * Sets the input_semantic attribute. + * @param atInput_semantic The new value for the input_semantic attribute. + */ + void setInput_semantic( xsNCName atInput_semantic ) { *(daeStringRef*)&attrInput_semantic = atInput_semantic; _validAttributeArray[1] = true; } + + /** + * Gets the input_set attribute. + * @return Returns a domUint of the input_set attribute. + */ + domUint getInput_set() const { return attrInput_set; } + /** + * Sets the input_set attribute. + * @param atInput_set The new value for the input_set attribute. + */ + void setInput_set( domUint atInput_set ) { attrInput_set = atInput_set; _validAttributeArray[2] = true; } + + protected: + /** + * Constructor + */ + domBind_vertex_input(DAE& dae) : daeElement(dae), attrSemantic(), attrInput_semantic(), attrInput_set() {} + /** + * Destructor + */ + virtual ~domBind_vertex_input() {} + /** + * Overloaded assignment operator + */ + virtual domBind_vertex_input &operator=( const domBind_vertex_input &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attributes +/** + * The symbol attribute specifies which symbol defined from within the geometry + * this material binds to. + */ + xsNCName attrSymbol; +/** + * The target attribute specifies the URL of the location of the object to + * instantiate. + */ + xsAnyURI attrTarget; +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The bind element binds values to effect parameters upon instantiation. + * @see domBind + */ + domBind_Array elemBind_array; +/** + * The bind_vertex_input element binds vertex inputs to effect parameters + * upon instantiation. @see domBind_vertex_input + */ + domBind_vertex_input_Array elemBind_vertex_input_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the symbol attribute. + * @return Returns a xsNCName of the symbol attribute. + */ + xsNCName getSymbol() const { return attrSymbol; } + /** + * Sets the symbol attribute. + * @param atSymbol The new value for the symbol attribute. + */ + void setSymbol( xsNCName atSymbol ) { *(daeStringRef*)&attrSymbol = atSymbol; _validAttributeArray[0] = true; } + + /** + * Gets the target attribute. + * @return Returns a xsAnyURI reference of the target attribute. + */ + xsAnyURI &getTarget() { return attrTarget; } + /** + * Gets the target attribute. + * @return Returns a constant xsAnyURI reference of the target attribute. + */ + const xsAnyURI &getTarget() const { return attrTarget; } + /** + * Sets the target attribute. + * @param atTarget The new value for the target attribute. + */ + void setTarget( const xsAnyURI &atTarget ) { attrTarget = atTarget; _validAttributeArray[1] = true; } + /** + * Sets the target attribute. + * @param atTarget The new value for the target attribute. + */ + void setTarget( xsString atTarget ) { attrTarget = atTarget; _validAttributeArray[1] = true; } + + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[2] = true; } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[3] = true; } + + /** + * Gets the bind element array. + * @return Returns a reference to the array of bind elements. + */ + domBind_Array &getBind_array() { return elemBind_array; } + /** + * Gets the bind element array. + * @return Returns a constant reference to the array of bind elements. + */ + const domBind_Array &getBind_array() const { return elemBind_array; } + /** + * Gets the bind_vertex_input element array. + * @return Returns a reference to the array of bind_vertex_input elements. + */ + domBind_vertex_input_Array &getBind_vertex_input_array() { return elemBind_vertex_input_array; } + /** + * Gets the bind_vertex_input element array. + * @return Returns a constant reference to the array of bind_vertex_input elements. + */ + const domBind_vertex_input_Array &getBind_vertex_input_array() const { return elemBind_vertex_input_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domInstance_material(DAE& dae) : daeElement(dae), attrSymbol(), attrTarget(dae, *this), attrSid(), attrName(), elemBind_array(), elemBind_vertex_input_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domInstance_material() {} + /** + * Overloaded assignment operator + */ + virtual domInstance_material &operator=( const domInstance_material &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domInstance_node.h b/Engine/lib/collada/include/1.4/dom/domInstance_node.h new file mode 100644 index 000000000..3908b7bad --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domInstance_node.h @@ -0,0 +1,107 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domInstance_node_h__ +#define __domInstance_node_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * The instance_node element declares the instantiation of a COLLADA node + * resource. + */ +class domInstance_node : public daeElement, public domInstanceWithExtra_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INSTANCE_NODE; } + static daeInt ID() { return 700; } + virtual daeInt typeID() const { return ID(); } + + /** + * Gets the url attribute. + * @return Returns a xsAnyURI reference of the url attribute. + */ + xsAnyURI &getUrl() { return attrUrl; } + /** + * Gets the url attribute. + * @return Returns a constant xsAnyURI reference of the url attribute. + */ + const xsAnyURI &getUrl() const { return attrUrl; } + /** + * Sets the url attribute. + * @param atUrl The new value for the url attribute. + */ + void setUrl( const xsAnyURI &atUrl ) { attrUrl = atUrl; _validAttributeArray[0] = true; } + /** + * Sets the url attribute. + * @param atUrl The new value for the url attribute. + */ + void setUrl( xsString atUrl ) { attrUrl = atUrl; _validAttributeArray[0] = true; } + + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[1] = true; } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[2] = true; } + +protected: + /** + * Constructor + */ + domInstance_node(DAE& dae) : daeElement(dae), domInstanceWithExtra_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domInstance_node() {} + /** + * Overloaded assignment operator + */ + virtual domInstance_node &operator=( const domInstance_node &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domInstance_physics_material.h b/Engine/lib/collada/include/1.4/dom/domInstance_physics_material.h new file mode 100644 index 000000000..29dcb5baa --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domInstance_physics_material.h @@ -0,0 +1,107 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domInstance_physics_material_h__ +#define __domInstance_physics_material_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * The instance_physics_material element declares the instantiation of a COLLADA + * physics_material resource. + */ +class domInstance_physics_material : public daeElement, public domInstanceWithExtra_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INSTANCE_PHYSICS_MATERIAL; } + static daeInt ID() { return 701; } + virtual daeInt typeID() const { return ID(); } + + /** + * Gets the url attribute. + * @return Returns a xsAnyURI reference of the url attribute. + */ + xsAnyURI &getUrl() { return attrUrl; } + /** + * Gets the url attribute. + * @return Returns a constant xsAnyURI reference of the url attribute. + */ + const xsAnyURI &getUrl() const { return attrUrl; } + /** + * Sets the url attribute. + * @param atUrl The new value for the url attribute. + */ + void setUrl( const xsAnyURI &atUrl ) { attrUrl = atUrl; _validAttributeArray[0] = true; } + /** + * Sets the url attribute. + * @param atUrl The new value for the url attribute. + */ + void setUrl( xsString atUrl ) { attrUrl = atUrl; _validAttributeArray[0] = true; } + + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[1] = true; } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[2] = true; } + +protected: + /** + * Constructor + */ + domInstance_physics_material(DAE& dae) : daeElement(dae), domInstanceWithExtra_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domInstance_physics_material() {} + /** + * Overloaded assignment operator + */ + virtual domInstance_physics_material &operator=( const domInstance_physics_material &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domInstance_physics_model.h b/Engine/lib/collada/include/1.4/dom/domInstance_physics_model.h new file mode 100644 index 000000000..67912b668 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domInstance_physics_model.h @@ -0,0 +1,218 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domInstance_physics_model_h__ +#define __domInstance_physics_model_h__ + +#include +#include +#include + +#include +#include +#include +#include +class DAE; + +/** + * This element allows instancing physics model within another physics model, + * or in a physics scene. + */ +class domInstance_physics_model : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INSTANCE_PHYSICS_MODEL; } + static daeInt ID() { return 702; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The url attribute refers to resource. This may refer to a local resource + * using a relative URL fragment identifier that begins with the “#” + * character. The url attribute may refer to an external resource using an + * absolute or relative URL. + */ + xsAnyURI attrUrl; +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; +/** + * The parent attribute points to the id of a node in the visual scene. This + * allows a physics model to be instantiated under a specific transform node, + * which will dictate the initial position and orientation, and could be + * animated to influence kinematic rigid bodies. + */ + xsAnyURI attrParent; + +protected: // Elements +/** + * The instance_physics_model element may instance any number of force_field + * elements. @see domInstance_force_field + */ + domInstance_force_field_Array elemInstance_force_field_array; +/** + * The instance_physics_model element may instance any number of rigid_body + * elements. @see domInstance_rigid_body + */ + domInstance_rigid_body_Array elemInstance_rigid_body_array; +/** + * The instance_physics_model element may instance any number of rigid_constraint + * elements. @see domInstance_rigid_constraint + */ + domInstance_rigid_constraint_Array elemInstance_rigid_constraint_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the url attribute. + * @return Returns a xsAnyURI reference of the url attribute. + */ + xsAnyURI &getUrl() { return attrUrl; } + /** + * Gets the url attribute. + * @return Returns a constant xsAnyURI reference of the url attribute. + */ + const xsAnyURI &getUrl() const { return attrUrl; } + /** + * Sets the url attribute. + * @param atUrl The new value for the url attribute. + */ + void setUrl( const xsAnyURI &atUrl ) { attrUrl = atUrl; _validAttributeArray[0] = true; } + /** + * Sets the url attribute. + * @param atUrl The new value for the url attribute. + */ + void setUrl( xsString atUrl ) { attrUrl = atUrl; _validAttributeArray[0] = true; } + + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[1] = true; } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[2] = true; } + + /** + * Gets the parent attribute. + * @return Returns a xsAnyURI reference of the parent attribute. + */ + xsAnyURI &getParent() { return attrParent; } + /** + * Gets the parent attribute. + * @return Returns a constant xsAnyURI reference of the parent attribute. + */ + const xsAnyURI &getParent() const { return attrParent; } + /** + * Sets the parent attribute. + * @param atParent The new value for the parent attribute. + */ + void setParent( const xsAnyURI &atParent ) { attrParent = atParent; _validAttributeArray[3] = true; } + /** + * Sets the parent attribute. + * @param atParent The new value for the parent attribute. + */ + void setParent( xsString atParent ) { attrParent = atParent; _validAttributeArray[3] = true; } + + /** + * Gets the instance_force_field element array. + * @return Returns a reference to the array of instance_force_field elements. + */ + domInstance_force_field_Array &getInstance_force_field_array() { return elemInstance_force_field_array; } + /** + * Gets the instance_force_field element array. + * @return Returns a constant reference to the array of instance_force_field elements. + */ + const domInstance_force_field_Array &getInstance_force_field_array() const { return elemInstance_force_field_array; } + /** + * Gets the instance_rigid_body element array. + * @return Returns a reference to the array of instance_rigid_body elements. + */ + domInstance_rigid_body_Array &getInstance_rigid_body_array() { return elemInstance_rigid_body_array; } + /** + * Gets the instance_rigid_body element array. + * @return Returns a constant reference to the array of instance_rigid_body elements. + */ + const domInstance_rigid_body_Array &getInstance_rigid_body_array() const { return elemInstance_rigid_body_array; } + /** + * Gets the instance_rigid_constraint element array. + * @return Returns a reference to the array of instance_rigid_constraint elements. + */ + domInstance_rigid_constraint_Array &getInstance_rigid_constraint_array() { return elemInstance_rigid_constraint_array; } + /** + * Gets the instance_rigid_constraint element array. + * @return Returns a constant reference to the array of instance_rigid_constraint elements. + */ + const domInstance_rigid_constraint_Array &getInstance_rigid_constraint_array() const { return elemInstance_rigid_constraint_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domInstance_physics_model(DAE& dae) : daeElement(dae), attrUrl(dae, *this), attrSid(), attrName(), attrParent(dae, *this), elemInstance_force_field_array(), elemInstance_rigid_body_array(), elemInstance_rigid_constraint_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domInstance_physics_model() {} + /** + * Overloaded assignment operator + */ + virtual domInstance_physics_model &operator=( const domInstance_physics_model &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domInstance_rigid_body.h b/Engine/lib/collada/include/1.4/dom/domInstance_rigid_body.h new file mode 100644 index 000000000..aab4c967a --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domInstance_rigid_body.h @@ -0,0 +1,899 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domInstance_rigid_body_h__ +#define __domInstance_rigid_body_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +class DAE; + +/** + * This element allows instancing a rigid_body within an instance_physics_model. + */ +class domInstance_rigid_body : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INSTANCE_RIGID_BODY; } + static daeInt ID() { return 703; } + virtual daeInt typeID() const { return ID(); } +public: + class domTechnique_common; + + typedef daeSmartRef domTechnique_commonRef; + typedef daeTArray domTechnique_common_Array; + +/** + * The technique_common element specifies the instance_rigid_body information + * for the common profile which all COLLADA implementations need to support. + */ + class domTechnique_common : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TECHNIQUE_COMMON; } + static daeInt ID() { return 704; } + virtual daeInt typeID() const { return ID(); } + public: + class domAngular_velocity; + + typedef daeSmartRef domAngular_velocityRef; + typedef daeTArray domAngular_velocity_Array; + +/** + * Specifies the initial angular velocity of the rigid_body instance in degrees + * per second around each axis, in the form of an X-Y-Z Euler rotation. + */ + class domAngular_velocity : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ANGULAR_VELOCITY; } + static daeInt ID() { return 705; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFloat3 value of the text data of this element. + */ + domFloat3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domFloat3 reference of the _value array. + */ + domFloat3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domFloat3 reference of the _value array. + */ + const domFloat3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domFloat3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domAngular_velocity(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domAngular_velocity() {} + /** + * Overloaded assignment operator + */ + virtual domAngular_velocity &operator=( const domAngular_velocity &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domVelocity; + + typedef daeSmartRef domVelocityRef; + typedef daeTArray domVelocity_Array; + +/** + * Specifies the initial linear velocity of the rigid_body instance. + */ + class domVelocity : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::VELOCITY; } + static daeInt ID() { return 706; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFloat3 value of the text data of this element. + */ + domFloat3 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domFloat3 reference of the _value array. + */ + domFloat3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domFloat3 reference of the _value array. + */ + const domFloat3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domFloat3 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domVelocity(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domVelocity() {} + /** + * Overloaded assignment operator + */ + virtual domVelocity &operator=( const domVelocity &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domDynamic; + + typedef daeSmartRef domDynamicRef; + typedef daeTArray domDynamic_Array; + + class domDynamic : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::DYNAMIC; } + static daeInt ID() { return 707; } + virtual daeInt typeID() const { return ID(); } + protected: // Attribute +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; + + protected: // Value + /** + * The domBool value of the text data of this element. + */ + domBool _value; + + public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + + /** + * Gets the value of this element. + * @return a domBool of the value. + */ + domBool getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domBool val ) { _value = val; } + + protected: + /** + * Constructor + */ + domDynamic(DAE& dae) : daeElement(dae), attrSid(), _value() {} + /** + * Destructor + */ + virtual ~domDynamic() {} + /** + * Overloaded assignment operator + */ + virtual domDynamic &operator=( const domDynamic &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMass_frame; + + typedef daeSmartRef domMass_frameRef; + typedef daeTArray domMass_frame_Array; + + class domMass_frame : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MASS_FRAME; } + static daeInt ID() { return 708; } + virtual daeInt typeID() const { return ID(); } + + protected: // Elements + domTranslate_Array elemTranslate_array; + domRotate_Array elemRotate_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the translate element array. + * @return Returns a reference to the array of translate elements. + */ + domTranslate_Array &getTranslate_array() { return elemTranslate_array; } + /** + * Gets the translate element array. + * @return Returns a constant reference to the array of translate elements. + */ + const domTranslate_Array &getTranslate_array() const { return elemTranslate_array; } + /** + * Gets the rotate element array. + * @return Returns a reference to the array of rotate elements. + */ + domRotate_Array &getRotate_array() { return elemRotate_array; } + /** + * Gets the rotate element array. + * @return Returns a constant reference to the array of rotate elements. + */ + const domRotate_Array &getRotate_array() const { return elemRotate_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domMass_frame(DAE& dae) : daeElement(dae), elemTranslate_array(), elemRotate_array() {} + /** + * Destructor + */ + virtual ~domMass_frame() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domMass_frame &operator=( const domMass_frame &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domShape; + + typedef daeSmartRef domShapeRef; + typedef daeTArray domShape_Array; + + class domShape : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SHAPE; } + static daeInt ID() { return 709; } + virtual daeInt typeID() const { return ID(); } + public: + class domHollow; + + typedef daeSmartRef domHollowRef; + typedef daeTArray domHollow_Array; + + class domHollow : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HOLLOW; } + static daeInt ID() { return 710; } + virtual daeInt typeID() const { return ID(); } + protected: // Attribute +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; + + protected: // Value + /** + * The domBool value of the text data of this element. + */ + domBool _value; + + public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + + /** + * Gets the value of this element. + * @return a domBool of the value. + */ + domBool getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domBool val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHollow(DAE& dae) : daeElement(dae), attrSid(), _value() {} + /** + * Destructor + */ + virtual ~domHollow() {} + /** + * Overloaded assignment operator + */ + virtual domHollow &operator=( const domHollow &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements + domHollowRef elemHollow; + domTargetableFloatRef elemMass; + domTargetableFloatRef elemDensity; + domInstance_physics_materialRef elemInstance_physics_material; + domPhysics_materialRef elemPhysics_material; + domInstance_geometryRef elemInstance_geometry; + domPlaneRef elemPlane; + domBoxRef elemBox; + domSphereRef elemSphere; + domCylinderRef elemCylinder; + domTapered_cylinderRef elemTapered_cylinder; + domCapsuleRef elemCapsule; + domTapered_capsuleRef elemTapered_capsule; + domTranslate_Array elemTranslate_array; + domRotate_Array elemRotate_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the hollow element. + * @return a daeSmartRef to the hollow element. + */ + const domHollowRef getHollow() const { return elemHollow; } + /** + * Gets the mass element. + * @return a daeSmartRef to the mass element. + */ + const domTargetableFloatRef getMass() const { return elemMass; } + /** + * Gets the density element. + * @return a daeSmartRef to the density element. + */ + const domTargetableFloatRef getDensity() const { return elemDensity; } + /** + * Gets the instance_physics_material element. + * @return a daeSmartRef to the instance_physics_material element. + */ + const domInstance_physics_materialRef getInstance_physics_material() const { return elemInstance_physics_material; } + /** + * Gets the physics_material element. + * @return a daeSmartRef to the physics_material element. + */ + const domPhysics_materialRef getPhysics_material() const { return elemPhysics_material; } + /** + * Gets the instance_geometry element. + * @return a daeSmartRef to the instance_geometry element. + */ + const domInstance_geometryRef getInstance_geometry() const { return elemInstance_geometry; } + /** + * Gets the plane element. + * @return a daeSmartRef to the plane element. + */ + const domPlaneRef getPlane() const { return elemPlane; } + /** + * Gets the box element. + * @return a daeSmartRef to the box element. + */ + const domBoxRef getBox() const { return elemBox; } + /** + * Gets the sphere element. + * @return a daeSmartRef to the sphere element. + */ + const domSphereRef getSphere() const { return elemSphere; } + /** + * Gets the cylinder element. + * @return a daeSmartRef to the cylinder element. + */ + const domCylinderRef getCylinder() const { return elemCylinder; } + /** + * Gets the tapered_cylinder element. + * @return a daeSmartRef to the tapered_cylinder element. + */ + const domTapered_cylinderRef getTapered_cylinder() const { return elemTapered_cylinder; } + /** + * Gets the capsule element. + * @return a daeSmartRef to the capsule element. + */ + const domCapsuleRef getCapsule() const { return elemCapsule; } + /** + * Gets the tapered_capsule element. + * @return a daeSmartRef to the tapered_capsule element. + */ + const domTapered_capsuleRef getTapered_capsule() const { return elemTapered_capsule; } + /** + * Gets the translate element array. + * @return Returns a reference to the array of translate elements. + */ + domTranslate_Array &getTranslate_array() { return elemTranslate_array; } + /** + * Gets the translate element array. + * @return Returns a constant reference to the array of translate elements. + */ + const domTranslate_Array &getTranslate_array() const { return elemTranslate_array; } + /** + * Gets the rotate element array. + * @return Returns a reference to the array of rotate elements. + */ + domRotate_Array &getRotate_array() { return elemRotate_array; } + /** + * Gets the rotate element array. + * @return Returns a constant reference to the array of rotate elements. + */ + const domRotate_Array &getRotate_array() const { return elemRotate_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domShape(DAE& dae) : daeElement(dae), elemHollow(), elemMass(), elemDensity(), elemInstance_physics_material(), elemPhysics_material(), elemInstance_geometry(), elemPlane(), elemBox(), elemSphere(), elemCylinder(), elemTapered_cylinder(), elemCapsule(), elemTapered_capsule(), elemTranslate_array(), elemRotate_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domShape() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domShape &operator=( const domShape &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements +/** + * Specifies the initial angular velocity of the rigid_body instance in degrees + * per second around each axis, in the form of an X-Y-Z Euler rotation. @see + * domAngular_velocity + */ + domAngular_velocityRef elemAngular_velocity; +/** + * Specifies the initial linear velocity of the rigid_body instance. @see + * domVelocity + */ + domVelocityRef elemVelocity; + domDynamicRef elemDynamic; + domTargetableFloatRef elemMass; + domMass_frameRef elemMass_frame; + domTargetableFloat3Ref elemInertia; + domInstance_physics_materialRef elemInstance_physics_material; + domPhysics_materialRef elemPhysics_material; + domShape_Array elemShape_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the angular_velocity element. + * @return a daeSmartRef to the angular_velocity element. + */ + const domAngular_velocityRef getAngular_velocity() const { return elemAngular_velocity; } + /** + * Gets the velocity element. + * @return a daeSmartRef to the velocity element. + */ + const domVelocityRef getVelocity() const { return elemVelocity; } + /** + * Gets the dynamic element. + * @return a daeSmartRef to the dynamic element. + */ + const domDynamicRef getDynamic() const { return elemDynamic; } + /** + * Gets the mass element. + * @return a daeSmartRef to the mass element. + */ + const domTargetableFloatRef getMass() const { return elemMass; } + /** + * Gets the mass_frame element. + * @return a daeSmartRef to the mass_frame element. + */ + const domMass_frameRef getMass_frame() const { return elemMass_frame; } + /** + * Gets the inertia element. + * @return a daeSmartRef to the inertia element. + */ + const domTargetableFloat3Ref getInertia() const { return elemInertia; } + /** + * Gets the instance_physics_material element. + * @return a daeSmartRef to the instance_physics_material element. + */ + const domInstance_physics_materialRef getInstance_physics_material() const { return elemInstance_physics_material; } + /** + * Gets the physics_material element. + * @return a daeSmartRef to the physics_material element. + */ + const domPhysics_materialRef getPhysics_material() const { return elemPhysics_material; } + /** + * Gets the shape element array. + * @return Returns a reference to the array of shape elements. + */ + domShape_Array &getShape_array() { return elemShape_array; } + /** + * Gets the shape element array. + * @return Returns a constant reference to the array of shape elements. + */ + const domShape_Array &getShape_array() const { return elemShape_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domTechnique_common(DAE& dae) : daeElement(dae), elemAngular_velocity(), elemVelocity(), elemDynamic(), elemMass(), elemMass_frame(), elemInertia(), elemInstance_physics_material(), elemPhysics_material(), elemShape_array() {} + /** + * Destructor + */ + virtual ~domTechnique_common() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domTechnique_common &operator=( const domTechnique_common &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attributes +/** + * The body attribute indicates which rigid_body to instantiate. Required + * attribute. + */ + xsNCName attrBody; +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; +/** + * The target attribute indicates which node is influenced by this rigid_body + * instance. Required attribute + */ + xsAnyURI attrTarget; + +protected: // Elements +/** + * The technique_common element specifies the instance_rigid_body information + * for the common profile which all COLLADA implementations need to support. + * @see domTechnique_common + */ + domTechnique_commonRef elemTechnique_common; +/** + * This element may contain any number of non-common profile techniques. + * @see domTechnique + */ + domTechnique_Array elemTechnique_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the body attribute. + * @return Returns a xsNCName of the body attribute. + */ + xsNCName getBody() const { return attrBody; } + /** + * Sets the body attribute. + * @param atBody The new value for the body attribute. + */ + void setBody( xsNCName atBody ) { *(daeStringRef*)&attrBody = atBody; _validAttributeArray[0] = true; } + + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[1] = true; } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[2] = true; } + + /** + * Gets the target attribute. + * @return Returns a xsAnyURI reference of the target attribute. + */ + xsAnyURI &getTarget() { return attrTarget; } + /** + * Gets the target attribute. + * @return Returns a constant xsAnyURI reference of the target attribute. + */ + const xsAnyURI &getTarget() const { return attrTarget; } + /** + * Sets the target attribute. + * @param atTarget The new value for the target attribute. + */ + void setTarget( const xsAnyURI &atTarget ) { attrTarget = atTarget; _validAttributeArray[3] = true; } + /** + * Sets the target attribute. + * @param atTarget The new value for the target attribute. + */ + void setTarget( xsString atTarget ) { attrTarget = atTarget; _validAttributeArray[3] = true; } + + /** + * Gets the technique_common element. + * @return a daeSmartRef to the technique_common element. + */ + const domTechnique_commonRef getTechnique_common() const { return elemTechnique_common; } + /** + * Gets the technique element array. + * @return Returns a reference to the array of technique elements. + */ + domTechnique_Array &getTechnique_array() { return elemTechnique_array; } + /** + * Gets the technique element array. + * @return Returns a constant reference to the array of technique elements. + */ + const domTechnique_Array &getTechnique_array() const { return elemTechnique_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domInstance_rigid_body(DAE& dae) : daeElement(dae), attrBody(), attrSid(), attrName(), attrTarget(dae, *this), elemTechnique_common(), elemTechnique_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domInstance_rigid_body() {} + /** + * Overloaded assignment operator + */ + virtual domInstance_rigid_body &operator=( const domInstance_rigid_body &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domInstance_rigid_constraint.h b/Engine/lib/collada/include/1.4/dom/domInstance_rigid_constraint.h new file mode 100644 index 000000000..b04a538c6 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domInstance_rigid_constraint.h @@ -0,0 +1,129 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domInstance_rigid_constraint_h__ +#define __domInstance_rigid_constraint_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * This element allows instancing a rigid_constraint within an instance_physics_model. + */ +class domInstance_rigid_constraint : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INSTANCE_RIGID_CONSTRAINT; } + static daeInt ID() { return 711; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The constraint attribute indicates which rigid_constraing to instantiate. + * Required attribute. + */ + xsNCName attrConstraint; +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Element +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the constraint attribute. + * @return Returns a xsNCName of the constraint attribute. + */ + xsNCName getConstraint() const { return attrConstraint; } + /** + * Sets the constraint attribute. + * @param atConstraint The new value for the constraint attribute. + */ + void setConstraint( xsNCName atConstraint ) { *(daeStringRef*)&attrConstraint = atConstraint; _validAttributeArray[0] = true; } + + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[1] = true; } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[2] = true; } + + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domInstance_rigid_constraint(DAE& dae) : daeElement(dae), attrConstraint(), attrSid(), attrName(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domInstance_rigid_constraint() {} + /** + * Overloaded assignment operator + */ + virtual domInstance_rigid_constraint &operator=( const domInstance_rigid_constraint &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domInt_array.h b/Engine/lib/collada/include/1.4/dom/domInt_array.h new file mode 100644 index 000000000..7292b7cea --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domInt_array.h @@ -0,0 +1,170 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domInt_array_h__ +#define __domInt_array_h__ + +#include +#include +#include + +class DAE; + +/** + * The int_array element declares the storage for a homogenous array of integer + * values. + */ +class domInt_array : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INT_ARRAY; } + static daeInt ID() { return 608; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; +/** + * The count attribute indicates the number of values in the array. Required + * attribute. + */ + domUint attrCount; +/** + * The minInclusive attribute indicates the smallest integer value that can + * be contained in the array. The default value is –2147483648. Optional + * attribute. + */ + xsInteger attrMinInclusive; +/** + * The maxInclusive attribute indicates the largest integer value that can + * be contained in the array. The default value is 2147483647. Optional attribute. + */ + xsInteger attrMaxInclusive; + +protected: // Value + /** + * The domListOfInts value of the text data of this element. + */ + domListOfInts _value; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the count attribute. + * @return Returns a domUint of the count attribute. + */ + domUint getCount() const { return attrCount; } + /** + * Sets the count attribute. + * @param atCount The new value for the count attribute. + */ + void setCount( domUint atCount ) { attrCount = atCount; _validAttributeArray[2] = true; } + + /** + * Gets the minInclusive attribute. + * @return Returns a xsInteger of the minInclusive attribute. + */ + xsInteger getMinInclusive() const { return attrMinInclusive; } + /** + * Sets the minInclusive attribute. + * @param atMinInclusive The new value for the minInclusive attribute. + */ + void setMinInclusive( xsInteger atMinInclusive ) { attrMinInclusive = atMinInclusive; _validAttributeArray[3] = true; } + + /** + * Gets the maxInclusive attribute. + * @return Returns a xsInteger of the maxInclusive attribute. + */ + xsInteger getMaxInclusive() const { return attrMaxInclusive; } + /** + * Sets the maxInclusive attribute. + * @param atMaxInclusive The new value for the maxInclusive attribute. + */ + void setMaxInclusive( xsInteger atMaxInclusive ) { attrMaxInclusive = atMaxInclusive; _validAttributeArray[4] = true; } + + /** + * Gets the _value array. + * @return Returns a domListOfInts reference of the _value array. + */ + domListOfInts &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domListOfInts reference of the _value array. + */ + const domListOfInts &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domListOfInts &val ) { _value = val; } + +protected: + /** + * Constructor + */ + domInt_array(DAE& dae) : daeElement(dae), attrId(), attrName(), attrCount(), attrMinInclusive(), attrMaxInclusive(), _value() {} + /** + * Destructor + */ + virtual ~domInt_array() {} + /** + * Overloaded assignment operator + */ + virtual domInt_array &operator=( const domInt_array &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domLibrary_animation_clips.h b/Engine/lib/collada/include/1.4/dom/domLibrary_animation_clips.h new file mode 100644 index 000000000..1416c8732 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domLibrary_animation_clips.h @@ -0,0 +1,142 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domLibrary_animation_clips_h__ +#define __domLibrary_animation_clips_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * The library_animation_clips element declares a module of animation_clip + * elements. + */ +class domLibrary_animation_clips : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIBRARY_ANIMATION_CLIPS; } + static daeInt ID() { return 713; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The library_animation_clips element may contain an asset element. @see + * domAsset + */ + domAssetRef elemAsset; +/** + * There must be at least one animation_clip element. @see domAnimation_clip + */ + domAnimation_clip_Array elemAnimation_clip_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the animation_clip element array. + * @return Returns a reference to the array of animation_clip elements. + */ + domAnimation_clip_Array &getAnimation_clip_array() { return elemAnimation_clip_array; } + /** + * Gets the animation_clip element array. + * @return Returns a constant reference to the array of animation_clip elements. + */ + const domAnimation_clip_Array &getAnimation_clip_array() const { return elemAnimation_clip_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domLibrary_animation_clips(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemAnimation_clip_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domLibrary_animation_clips() {} + /** + * Overloaded assignment operator + */ + virtual domLibrary_animation_clips &operator=( const domLibrary_animation_clips &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domLibrary_animations.h b/Engine/lib/collada/include/1.4/dom/domLibrary_animations.h new file mode 100644 index 000000000..b8527c85a --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domLibrary_animations.h @@ -0,0 +1,140 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domLibrary_animations_h__ +#define __domLibrary_animations_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * The library_animations element declares a module of animation elements. + */ +class domLibrary_animations : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIBRARY_ANIMATIONS; } + static daeInt ID() { return 712; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The library_animations element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * There must be at least one animation element. @see domAnimation + */ + domAnimation_Array elemAnimation_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the animation element array. + * @return Returns a reference to the array of animation elements. + */ + domAnimation_Array &getAnimation_array() { return elemAnimation_array; } + /** + * Gets the animation element array. + * @return Returns a constant reference to the array of animation elements. + */ + const domAnimation_Array &getAnimation_array() const { return elemAnimation_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domLibrary_animations(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemAnimation_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domLibrary_animations() {} + /** + * Overloaded assignment operator + */ + virtual domLibrary_animations &operator=( const domLibrary_animations &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domLibrary_cameras.h b/Engine/lib/collada/include/1.4/dom/domLibrary_cameras.h new file mode 100644 index 000000000..fc8758efd --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domLibrary_cameras.h @@ -0,0 +1,140 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domLibrary_cameras_h__ +#define __domLibrary_cameras_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * The library_cameras element declares a module of camera elements. + */ +class domLibrary_cameras : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIBRARY_CAMERAS; } + static daeInt ID() { return 714; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The library_cameras element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * There must be at least one camera element. @see domCamera + */ + domCamera_Array elemCamera_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the camera element array. + * @return Returns a reference to the array of camera elements. + */ + domCamera_Array &getCamera_array() { return elemCamera_array; } + /** + * Gets the camera element array. + * @return Returns a constant reference to the array of camera elements. + */ + const domCamera_Array &getCamera_array() const { return elemCamera_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domLibrary_cameras(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemCamera_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domLibrary_cameras() {} + /** + * Overloaded assignment operator + */ + virtual domLibrary_cameras &operator=( const domLibrary_cameras &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domLibrary_controllers.h b/Engine/lib/collada/include/1.4/dom/domLibrary_controllers.h new file mode 100644 index 000000000..56950e5c1 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domLibrary_controllers.h @@ -0,0 +1,140 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domLibrary_controllers_h__ +#define __domLibrary_controllers_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * The library_controllers element declares a module of controller elements. + */ +class domLibrary_controllers : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIBRARY_CONTROLLERS; } + static daeInt ID() { return 715; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The library_controllers element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * There must be at least one controller element. @see domController + */ + domController_Array elemController_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the controller element array. + * @return Returns a reference to the array of controller elements. + */ + domController_Array &getController_array() { return elemController_array; } + /** + * Gets the controller element array. + * @return Returns a constant reference to the array of controller elements. + */ + const domController_Array &getController_array() const { return elemController_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domLibrary_controllers(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemController_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domLibrary_controllers() {} + /** + * Overloaded assignment operator + */ + virtual domLibrary_controllers &operator=( const domLibrary_controllers &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domLibrary_effects.h b/Engine/lib/collada/include/1.4/dom/domLibrary_effects.h new file mode 100644 index 000000000..5bb717a98 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domLibrary_effects.h @@ -0,0 +1,140 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domLibrary_effects_h__ +#define __domLibrary_effects_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * The library_effects element declares a module of effect elements. + */ +class domLibrary_effects : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIBRARY_EFFECTS; } + static daeInt ID() { return 717; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The library_effects element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * There must be at least one effect element. @see domEffect + */ + domEffect_Array elemEffect_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the effect element array. + * @return Returns a reference to the array of effect elements. + */ + domEffect_Array &getEffect_array() { return elemEffect_array; } + /** + * Gets the effect element array. + * @return Returns a constant reference to the array of effect elements. + */ + const domEffect_Array &getEffect_array() const { return elemEffect_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domLibrary_effects(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemEffect_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domLibrary_effects() {} + /** + * Overloaded assignment operator + */ + virtual domLibrary_effects &operator=( const domLibrary_effects &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domLibrary_force_fields.h b/Engine/lib/collada/include/1.4/dom/domLibrary_force_fields.h new file mode 100644 index 000000000..915502837 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domLibrary_force_fields.h @@ -0,0 +1,140 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domLibrary_force_fields_h__ +#define __domLibrary_force_fields_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * The library_force_fields element declares a module of force_field elements. + */ +class domLibrary_force_fields : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIBRARY_FORCE_FIELDS; } + static daeInt ID() { return 718; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The library_force_fields element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * There must be at least one force_field element. @see domForce_field + */ + domForce_field_Array elemForce_field_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the force_field element array. + * @return Returns a reference to the array of force_field elements. + */ + domForce_field_Array &getForce_field_array() { return elemForce_field_array; } + /** + * Gets the force_field element array. + * @return Returns a constant reference to the array of force_field elements. + */ + const domForce_field_Array &getForce_field_array() const { return elemForce_field_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domLibrary_force_fields(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemForce_field_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domLibrary_force_fields() {} + /** + * Overloaded assignment operator + */ + virtual domLibrary_force_fields &operator=( const domLibrary_force_fields &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domLibrary_geometries.h b/Engine/lib/collada/include/1.4/dom/domLibrary_geometries.h new file mode 100644 index 000000000..169417738 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domLibrary_geometries.h @@ -0,0 +1,140 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domLibrary_geometries_h__ +#define __domLibrary_geometries_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * The library_geometries element declares a module of geometry elements. + */ +class domLibrary_geometries : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIBRARY_GEOMETRIES; } + static daeInt ID() { return 716; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The library_geometries element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * There must be at least one geometry element. @see domGeometry + */ + domGeometry_Array elemGeometry_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the geometry element array. + * @return Returns a reference to the array of geometry elements. + */ + domGeometry_Array &getGeometry_array() { return elemGeometry_array; } + /** + * Gets the geometry element array. + * @return Returns a constant reference to the array of geometry elements. + */ + const domGeometry_Array &getGeometry_array() const { return elemGeometry_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domLibrary_geometries(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemGeometry_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domLibrary_geometries() {} + /** + * Overloaded assignment operator + */ + virtual domLibrary_geometries &operator=( const domLibrary_geometries &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domLibrary_images.h b/Engine/lib/collada/include/1.4/dom/domLibrary_images.h new file mode 100644 index 000000000..07788f400 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domLibrary_images.h @@ -0,0 +1,140 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domLibrary_images_h__ +#define __domLibrary_images_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * The library_images element declares a module of image elements. + */ +class domLibrary_images : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIBRARY_IMAGES; } + static daeInt ID() { return 719; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The library_images element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * There must be at least one image element. @see domImage + */ + domImage_Array elemImage_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the image element array. + * @return Returns a reference to the array of image elements. + */ + domImage_Array &getImage_array() { return elemImage_array; } + /** + * Gets the image element array. + * @return Returns a constant reference to the array of image elements. + */ + const domImage_Array &getImage_array() const { return elemImage_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domLibrary_images(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemImage_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domLibrary_images() {} + /** + * Overloaded assignment operator + */ + virtual domLibrary_images &operator=( const domLibrary_images &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domLibrary_lights.h b/Engine/lib/collada/include/1.4/dom/domLibrary_lights.h new file mode 100644 index 000000000..e63f4ecc3 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domLibrary_lights.h @@ -0,0 +1,140 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domLibrary_lights_h__ +#define __domLibrary_lights_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * The library_lights element declares a module of light elements. + */ +class domLibrary_lights : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIBRARY_LIGHTS; } + static daeInt ID() { return 720; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The library_lights element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * There must be at least one light element. @see domLight + */ + domLight_Array elemLight_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the light element array. + * @return Returns a reference to the array of light elements. + */ + domLight_Array &getLight_array() { return elemLight_array; } + /** + * Gets the light element array. + * @return Returns a constant reference to the array of light elements. + */ + const domLight_Array &getLight_array() const { return elemLight_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domLibrary_lights(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemLight_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domLibrary_lights() {} + /** + * Overloaded assignment operator + */ + virtual domLibrary_lights &operator=( const domLibrary_lights &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domLibrary_materials.h b/Engine/lib/collada/include/1.4/dom/domLibrary_materials.h new file mode 100644 index 000000000..c7c6c1c21 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domLibrary_materials.h @@ -0,0 +1,140 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domLibrary_materials_h__ +#define __domLibrary_materials_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * The library_materials element declares a module of material elements. + */ +class domLibrary_materials : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIBRARY_MATERIALS; } + static daeInt ID() { return 721; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The library_materials element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * There must be at least one material element. @see domMaterial + */ + domMaterial_Array elemMaterial_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the material element array. + * @return Returns a reference to the array of material elements. + */ + domMaterial_Array &getMaterial_array() { return elemMaterial_array; } + /** + * Gets the material element array. + * @return Returns a constant reference to the array of material elements. + */ + const domMaterial_Array &getMaterial_array() const { return elemMaterial_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domLibrary_materials(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemMaterial_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domLibrary_materials() {} + /** + * Overloaded assignment operator + */ + virtual domLibrary_materials &operator=( const domLibrary_materials &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domLibrary_nodes.h b/Engine/lib/collada/include/1.4/dom/domLibrary_nodes.h new file mode 100644 index 000000000..b4740d610 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domLibrary_nodes.h @@ -0,0 +1,140 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domLibrary_nodes_h__ +#define __domLibrary_nodes_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * The library_nodes element declares a module of node elements. + */ +class domLibrary_nodes : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIBRARY_NODES; } + static daeInt ID() { return 722; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The library_nodes element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * There must be at least one node element. @see domNode + */ + domNode_Array elemNode_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the node element array. + * @return Returns a reference to the array of node elements. + */ + domNode_Array &getNode_array() { return elemNode_array; } + /** + * Gets the node element array. + * @return Returns a constant reference to the array of node elements. + */ + const domNode_Array &getNode_array() const { return elemNode_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domLibrary_nodes(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemNode_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domLibrary_nodes() {} + /** + * Overloaded assignment operator + */ + virtual domLibrary_nodes &operator=( const domLibrary_nodes &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domLibrary_physics_materials.h b/Engine/lib/collada/include/1.4/dom/domLibrary_physics_materials.h new file mode 100644 index 000000000..31a99426b --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domLibrary_physics_materials.h @@ -0,0 +1,142 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domLibrary_physics_materials_h__ +#define __domLibrary_physics_materials_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * The library_physics_materials element declares a module of physics_material + * elements. + */ +class domLibrary_physics_materials : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIBRARY_PHYSICS_MATERIALS; } + static daeInt ID() { return 723; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The library_physics_materials element may contain an asset element. @see + * domAsset + */ + domAssetRef elemAsset; +/** + * There must be at least one physics_material element. @see domPhysics_material + */ + domPhysics_material_Array elemPhysics_material_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the physics_material element array. + * @return Returns a reference to the array of physics_material elements. + */ + domPhysics_material_Array &getPhysics_material_array() { return elemPhysics_material_array; } + /** + * Gets the physics_material element array. + * @return Returns a constant reference to the array of physics_material elements. + */ + const domPhysics_material_Array &getPhysics_material_array() const { return elemPhysics_material_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domLibrary_physics_materials(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemPhysics_material_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domLibrary_physics_materials() {} + /** + * Overloaded assignment operator + */ + virtual domLibrary_physics_materials &operator=( const domLibrary_physics_materials &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domLibrary_physics_models.h b/Engine/lib/collada/include/1.4/dom/domLibrary_physics_models.h new file mode 100644 index 000000000..56f5384d7 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domLibrary_physics_models.h @@ -0,0 +1,141 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domLibrary_physics_models_h__ +#define __domLibrary_physics_models_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * The library_physics_models element declares a module of physics_model elements. + */ +class domLibrary_physics_models : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIBRARY_PHYSICS_MODELS; } + static daeInt ID() { return 724; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The library_physics_models element may contain an asset element. @see + * domAsset + */ + domAssetRef elemAsset; +/** + * There must be at least one physics_model element. @see domPhysics_model + */ + domPhysics_model_Array elemPhysics_model_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the physics_model element array. + * @return Returns a reference to the array of physics_model elements. + */ + domPhysics_model_Array &getPhysics_model_array() { return elemPhysics_model_array; } + /** + * Gets the physics_model element array. + * @return Returns a constant reference to the array of physics_model elements. + */ + const domPhysics_model_Array &getPhysics_model_array() const { return elemPhysics_model_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domLibrary_physics_models(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemPhysics_model_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domLibrary_physics_models() {} + /** + * Overloaded assignment operator + */ + virtual domLibrary_physics_models &operator=( const domLibrary_physics_models &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domLibrary_physics_scenes.h b/Engine/lib/collada/include/1.4/dom/domLibrary_physics_scenes.h new file mode 100644 index 000000000..d48692f4d --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domLibrary_physics_scenes.h @@ -0,0 +1,141 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domLibrary_physics_scenes_h__ +#define __domLibrary_physics_scenes_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * The library_physics_scenes element declares a module of physics_scene elements. + */ +class domLibrary_physics_scenes : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIBRARY_PHYSICS_SCENES; } + static daeInt ID() { return 725; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The library_physics_scenes element may contain an asset element. @see + * domAsset + */ + domAssetRef elemAsset; +/** + * There must be at least one physics_scene element. @see domPhysics_scene + */ + domPhysics_scene_Array elemPhysics_scene_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the physics_scene element array. + * @return Returns a reference to the array of physics_scene elements. + */ + domPhysics_scene_Array &getPhysics_scene_array() { return elemPhysics_scene_array; } + /** + * Gets the physics_scene element array. + * @return Returns a constant reference to the array of physics_scene elements. + */ + const domPhysics_scene_Array &getPhysics_scene_array() const { return elemPhysics_scene_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domLibrary_physics_scenes(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemPhysics_scene_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domLibrary_physics_scenes() {} + /** + * Overloaded assignment operator + */ + virtual domLibrary_physics_scenes &operator=( const domLibrary_physics_scenes &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domLibrary_visual_scenes.h b/Engine/lib/collada/include/1.4/dom/domLibrary_visual_scenes.h new file mode 100644 index 000000000..7cf48d66b --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domLibrary_visual_scenes.h @@ -0,0 +1,141 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domLibrary_visual_scenes_h__ +#define __domLibrary_visual_scenes_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * The library_visual_scenes element declares a module of visual_scene elements. + */ +class domLibrary_visual_scenes : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIBRARY_VISUAL_SCENES; } + static daeInt ID() { return 726; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The library_visual_scenes element may contain an asset element. @see + * domAsset + */ + domAssetRef elemAsset; +/** + * There must be at least one visual_scene element. @see domVisual_scene + */ + domVisual_scene_Array elemVisual_scene_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the visual_scene element array. + * @return Returns a reference to the array of visual_scene elements. + */ + domVisual_scene_Array &getVisual_scene_array() { return elemVisual_scene_array; } + /** + * Gets the visual_scene element array. + * @return Returns a constant reference to the array of visual_scene elements. + */ + const domVisual_scene_Array &getVisual_scene_array() const { return elemVisual_scene_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domLibrary_visual_scenes(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemVisual_scene_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domLibrary_visual_scenes() {} + /** + * Overloaded assignment operator + */ + virtual domLibrary_visual_scenes &operator=( const domLibrary_visual_scenes &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domLight.h b/Engine/lib/collada/include/1.4/dom/domLight.h new file mode 100644 index 000000000..41d25cdbe --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domLight.h @@ -0,0 +1,620 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domLight_h__ +#define __domLight_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include +class DAE; + +/** + * The light element declares a light source that illuminates the scene. Light + * sources have many different properties and radiate light in many different + * patterns and frequencies. + */ +class domLight : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIGHT; } + static daeInt ID() { return 638; } + virtual daeInt typeID() const { return ID(); } +public: + class domTechnique_common; + + typedef daeSmartRef domTechnique_commonRef; + typedef daeTArray domTechnique_common_Array; + +/** + * The technique_common element specifies the light information for the common + * profile which all COLLADA implementations need to support. + */ + class domTechnique_common : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TECHNIQUE_COMMON; } + static daeInt ID() { return 639; } + virtual daeInt typeID() const { return ID(); } + public: + class domAmbient; + + typedef daeSmartRef domAmbientRef; + typedef daeTArray domAmbient_Array; + +/** + * The ambient element declares the parameters required to describe an ambient + * light source. An ambient light is one that lights everything evenly, + * regardless of location or orientation. + */ + class domAmbient : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::AMBIENT; } + static daeInt ID() { return 640; } + virtual daeInt typeID() const { return ID(); } + + protected: // Element +/** + * The color element contains three floating point numbers specifying the + * color of the light. The color element must occur exactly once. @see domColor + */ + domTargetableFloat3Ref elemColor; + + public: //Accessors and Mutators + /** + * Gets the color element. + * @return a daeSmartRef to the color element. + */ + const domTargetableFloat3Ref getColor() const { return elemColor; } + protected: + /** + * Constructor + */ + domAmbient(DAE& dae) : daeElement(dae), elemColor() {} + /** + * Destructor + */ + virtual ~domAmbient() {} + /** + * Overloaded assignment operator + */ + virtual domAmbient &operator=( const domAmbient &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domDirectional; + + typedef daeSmartRef domDirectionalRef; + typedef daeTArray domDirectional_Array; + +/** + * The directional element declares the parameters required to describe a + * directional light source. A directional light is one that lights everything + * from the same direction, regardless of location. The light’s default + * direction vector in local coordinates is [0,0,-1], pointing down the -Z + * axis. The actual direction of the light is defined by the transform of + * the node where the light is instantiated. + */ + class domDirectional : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::DIRECTIONAL; } + static daeInt ID() { return 641; } + virtual daeInt typeID() const { return ID(); } + + protected: // Element +/** + * The color element contains three floating point numbers specifying the + * color of the light. The color element must occur exactly once. @see domColor + */ + domTargetableFloat3Ref elemColor; + + public: //Accessors and Mutators + /** + * Gets the color element. + * @return a daeSmartRef to the color element. + */ + const domTargetableFloat3Ref getColor() const { return elemColor; } + protected: + /** + * Constructor + */ + domDirectional(DAE& dae) : daeElement(dae), elemColor() {} + /** + * Destructor + */ + virtual ~domDirectional() {} + /** + * Overloaded assignment operator + */ + virtual domDirectional &operator=( const domDirectional &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPoint; + + typedef daeSmartRef domPointRef; + typedef daeTArray domPoint_Array; + +/** + * The point element declares the parameters required to describe a point + * light source. A point light source radiates light in all directions from + * a known location in space. The intensity of a point light source is attenuated + * as the distance to the light source increases. The position of the light + * is defined by the transform of the node in which it is instantiated. + */ + class domPoint : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::POINT; } + static daeInt ID() { return 642; } + virtual daeInt typeID() const { return ID(); } + + protected: // Elements +/** + * The color element contains three floating point numbers specifying the + * color of the light. The color element must occur exactly once. @see domColor + */ + domTargetableFloat3Ref elemColor; +/** + * The constant_attenuation is used to calculate the total attenuation of + * this light given a distance. The equation used is A = constant_attenuation + * + Dist*linear_attenuation + Dist^2*quadratic_attenuation. @see domConstant_attenuation + */ + domTargetableFloatRef elemConstant_attenuation; +/** + * The linear_attenuation is used to calculate the total attenuation of this + * light given a distance. The equation used is A = constant_attenuation + * + Dist*linear_attenuation + Dist^2*quadratic_attenuation. @see domLinear_attenuation + */ + domTargetableFloatRef elemLinear_attenuation; +/** + * The quadratic_attenuation is used to calculate the total attenuation of + * this light given a distance. The equation used is A = constant_attenuation + * + Dist*linear_attenuation + Dist^2*quadratic_attenuation. @see domQuadratic_attenuation + */ + domTargetableFloatRef elemQuadratic_attenuation; + + public: //Accessors and Mutators + /** + * Gets the color element. + * @return a daeSmartRef to the color element. + */ + const domTargetableFloat3Ref getColor() const { return elemColor; } + /** + * Gets the constant_attenuation element. + * @return a daeSmartRef to the constant_attenuation element. + */ + const domTargetableFloatRef getConstant_attenuation() const { return elemConstant_attenuation; } + /** + * Gets the linear_attenuation element. + * @return a daeSmartRef to the linear_attenuation element. + */ + const domTargetableFloatRef getLinear_attenuation() const { return elemLinear_attenuation; } + /** + * Gets the quadratic_attenuation element. + * @return a daeSmartRef to the quadratic_attenuation element. + */ + const domTargetableFloatRef getQuadratic_attenuation() const { return elemQuadratic_attenuation; } + protected: + /** + * Constructor + */ + domPoint(DAE& dae) : daeElement(dae), elemColor(), elemConstant_attenuation(), elemLinear_attenuation(), elemQuadratic_attenuation() {} + /** + * Destructor + */ + virtual ~domPoint() {} + /** + * Overloaded assignment operator + */ + virtual domPoint &operator=( const domPoint &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domSpot; + + typedef daeSmartRef domSpotRef; + typedef daeTArray domSpot_Array; + +/** + * The spot element declares the parameters required to describe a spot light + * source. A spot light source radiates light in one direction from a known + * location in space. The light radiates from the spot light source in a + * cone shape. The intensity of the light is attenuated as the radiation + * angle increases away from the direction of the light source. The intensity + * of a spot light source is also attenuated as the distance to the light + * source increases. The position of the light is defined by the transform + * of the node in which it is instantiated. The light’s default direction + * vector in local coordinates is [0,0,-1], pointing down the -Z axis. The + * actual direction of the light is defined by the transform of the node + * where the light is instantiated. + */ + class domSpot : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SPOT; } + static daeInt ID() { return 643; } + virtual daeInt typeID() const { return ID(); } + + protected: // Elements +/** + * The color element contains three floating point numbers specifying the + * color of the light. The color element must occur exactly once. @see domColor + */ + domTargetableFloat3Ref elemColor; +/** + * The constant_attenuation is used to calculate the total attenuation of + * this light given a distance. The equation used is A = constant_attenuation + * + Dist*linear_attenuation + Dist^2*quadratic_attenuation. @see domConstant_attenuation + */ + domTargetableFloatRef elemConstant_attenuation; +/** + * The linear_attenuation is used to calculate the total attenuation of this + * light given a distance. The equation used is A = constant_attenuation + * + Dist*linear_attenuation + Dist^2*quadratic_attenuation. @see domLinear_attenuation + */ + domTargetableFloatRef elemLinear_attenuation; +/** + * The quadratic_attenuation is used to calculate the total attenuation of + * this light given a distance. The equation used is A = constant_attenuation + * + Dist*linear_attenuation + Dist^2*quadratic_attenuation. @see domQuadratic_attenuation + */ + domTargetableFloatRef elemQuadratic_attenuation; +/** + * The falloff_angle is used to specify the amount of attenuation based on + * the direction of the light. @see domFalloff_angle + */ + domTargetableFloatRef elemFalloff_angle; +/** + * The falloff_exponent is used to specify the amount of attenuation based + * on the direction of the light. @see domFalloff_exponent + */ + domTargetableFloatRef elemFalloff_exponent; + + public: //Accessors and Mutators + /** + * Gets the color element. + * @return a daeSmartRef to the color element. + */ + const domTargetableFloat3Ref getColor() const { return elemColor; } + /** + * Gets the constant_attenuation element. + * @return a daeSmartRef to the constant_attenuation element. + */ + const domTargetableFloatRef getConstant_attenuation() const { return elemConstant_attenuation; } + /** + * Gets the linear_attenuation element. + * @return a daeSmartRef to the linear_attenuation element. + */ + const domTargetableFloatRef getLinear_attenuation() const { return elemLinear_attenuation; } + /** + * Gets the quadratic_attenuation element. + * @return a daeSmartRef to the quadratic_attenuation element. + */ + const domTargetableFloatRef getQuadratic_attenuation() const { return elemQuadratic_attenuation; } + /** + * Gets the falloff_angle element. + * @return a daeSmartRef to the falloff_angle element. + */ + const domTargetableFloatRef getFalloff_angle() const { return elemFalloff_angle; } + /** + * Gets the falloff_exponent element. + * @return a daeSmartRef to the falloff_exponent element. + */ + const domTargetableFloatRef getFalloff_exponent() const { return elemFalloff_exponent; } + protected: + /** + * Constructor + */ + domSpot(DAE& dae) : daeElement(dae), elemColor(), elemConstant_attenuation(), elemLinear_attenuation(), elemQuadratic_attenuation(), elemFalloff_angle(), elemFalloff_exponent() {} + /** + * Destructor + */ + virtual ~domSpot() {} + /** + * Overloaded assignment operator + */ + virtual domSpot &operator=( const domSpot &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements +/** + * The ambient element declares the parameters required to describe an ambient + * light source. An ambient light is one that lights everything evenly, + * regardless of location or orientation. @see domAmbient + */ + domAmbientRef elemAmbient; +/** + * The directional element declares the parameters required to describe a + * directional light source. A directional light is one that lights everything + * from the same direction, regardless of location. The light’s default + * direction vector in local coordinates is [0,0,-1], pointing down the -Z + * axis. The actual direction of the light is defined by the transform of + * the node where the light is instantiated. @see domDirectional + */ + domDirectionalRef elemDirectional; +/** + * The point element declares the parameters required to describe a point + * light source. A point light source radiates light in all directions from + * a known location in space. The intensity of a point light source is attenuated + * as the distance to the light source increases. The position of the light + * is defined by the transform of the node in which it is instantiated. @see + * domPoint + */ + domPointRef elemPoint; +/** + * The spot element declares the parameters required to describe a spot light + * source. A spot light source radiates light in one direction from a known + * location in space. The light radiates from the spot light source in a + * cone shape. The intensity of the light is attenuated as the radiation + * angle increases away from the direction of the light source. The intensity + * of a spot light source is also attenuated as the distance to the light + * source increases. The position of the light is defined by the transform + * of the node in which it is instantiated. The light’s default direction + * vector in local coordinates is [0,0,-1], pointing down the -Z axis. The + * actual direction of the light is defined by the transform of the node + * where the light is instantiated. @see domSpot + */ + domSpotRef elemSpot; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the ambient element. + * @return a daeSmartRef to the ambient element. + */ + const domAmbientRef getAmbient() const { return elemAmbient; } + /** + * Gets the directional element. + * @return a daeSmartRef to the directional element. + */ + const domDirectionalRef getDirectional() const { return elemDirectional; } + /** + * Gets the point element. + * @return a daeSmartRef to the point element. + */ + const domPointRef getPoint() const { return elemPoint; } + /** + * Gets the spot element. + * @return a daeSmartRef to the spot element. + */ + const domSpotRef getSpot() const { return elemSpot; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domTechnique_common(DAE& dae) : daeElement(dae), elemAmbient(), elemDirectional(), elemPoint(), elemSpot() {} + /** + * Destructor + */ + virtual ~domTechnique_common() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domTechnique_common &operator=( const domTechnique_common &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The light element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * The technique_common element specifies the light information for the common + * profile which all COLLADA implementations need to support. @see domTechnique_common + */ + domTechnique_commonRef elemTechnique_common; +/** + * This element may contain any number of non-common profile techniques. + * @see domTechnique + */ + domTechnique_Array elemTechnique_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the technique_common element. + * @return a daeSmartRef to the technique_common element. + */ + const domTechnique_commonRef getTechnique_common() const { return elemTechnique_common; } + /** + * Gets the technique element array. + * @return Returns a reference to the array of technique elements. + */ + domTechnique_Array &getTechnique_array() { return elemTechnique_array; } + /** + * Gets the technique element array. + * @return Returns a constant reference to the array of technique elements. + */ + const domTechnique_Array &getTechnique_array() const { return elemTechnique_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domLight(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemTechnique_common(), elemTechnique_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domLight() {} + /** + * Overloaded assignment operator + */ + virtual domLight &operator=( const domLight &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domLines.h b/Engine/lib/collada/include/1.4/dom/domLines.h new file mode 100644 index 000000000..3ebc66332 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domLines.h @@ -0,0 +1,160 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domLines_h__ +#define __domLines_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * The lines element provides the information needed to bind vertex attributes + * together and then organize those vertices into individual lines. Each + * line described by the mesh has two vertices. The first line is formed + * from first and second vertices. The second line is formed from the third + * and fourth vertices and so on. + */ +class domLines : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LINES; } + static daeInt ID() { return 618; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; +/** + * The count attribute indicates the number of line primitives. Required + * attribute. + */ + domUint attrCount; +/** + * The material attribute declares a symbol for a material. This symbol is + * bound to a material at the time of instantiation. If the material attribute + * is not specified then the lighting and shading results are application + * defined. Optional attribute. + */ + xsNCName attrMaterial; + +protected: // Elements +/** + * The input element may occur any number of times. This input is a local + * input with the offset and set attributes. @see domInput + */ + domInputLocalOffset_Array elemInput_array; +/** + * The p element may occur once. @see domP + */ + domPRef elemP; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[0] = true; } + + /** + * Gets the count attribute. + * @return Returns a domUint of the count attribute. + */ + domUint getCount() const { return attrCount; } + /** + * Sets the count attribute. + * @param atCount The new value for the count attribute. + */ + void setCount( domUint atCount ) { attrCount = atCount; _validAttributeArray[1] = true; } + + /** + * Gets the material attribute. + * @return Returns a xsNCName of the material attribute. + */ + xsNCName getMaterial() const { return attrMaterial; } + /** + * Sets the material attribute. + * @param atMaterial The new value for the material attribute. + */ + void setMaterial( xsNCName atMaterial ) { *(daeStringRef*)&attrMaterial = atMaterial; _validAttributeArray[2] = true; } + + /** + * Gets the input element array. + * @return Returns a reference to the array of input elements. + */ + domInputLocalOffset_Array &getInput_array() { return elemInput_array; } + /** + * Gets the input element array. + * @return Returns a constant reference to the array of input elements. + */ + const domInputLocalOffset_Array &getInput_array() const { return elemInput_array; } + /** + * Gets the p element. + * @return a daeSmartRef to the p element. + */ + const domPRef getP() const { return elemP; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domLines(DAE& dae) : daeElement(dae), attrName(), attrCount(), attrMaterial(), elemInput_array(), elemP(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domLines() {} + /** + * Overloaded assignment operator + */ + virtual domLines &operator=( const domLines &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domLinestrips.h b/Engine/lib/collada/include/1.4/dom/domLinestrips.h new file mode 100644 index 000000000..de2043916 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domLinestrips.h @@ -0,0 +1,165 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domLinestrips_h__ +#define __domLinestrips_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * The linestrips element provides the information needed to bind vertex attributes + * together and then organize those vertices into connected line-strips. + * Each line-strip described by the mesh has an arbitrary number of vertices. + * Each line segment within the line-strip is formed from the current vertex + * and the preceding vertex. + */ +class domLinestrips : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LINESTRIPS; } + static daeInt ID() { return 619; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; +/** + * The count attribute indicates the number of linestrip primitives. Required + * attribute. + */ + domUint attrCount; +/** + * The material attribute declares a symbol for a material. This symbol is + * bound to a material at the time of instantiation. If the material attribute + * is not specified then the lighting and shading results are application + * defined. Optional attribute. + */ + xsNCName attrMaterial; + +protected: // Elements +/** + * The input element may occur any number of times. This input is a local + * input with the offset and set attributes. @see domInput + */ + domInputLocalOffset_Array elemInput_array; +/** + * The linestrips element may have any number of p elements. @see domP + */ + domP_Array elemP_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[0] = true; } + + /** + * Gets the count attribute. + * @return Returns a domUint of the count attribute. + */ + domUint getCount() const { return attrCount; } + /** + * Sets the count attribute. + * @param atCount The new value for the count attribute. + */ + void setCount( domUint atCount ) { attrCount = atCount; _validAttributeArray[1] = true; } + + /** + * Gets the material attribute. + * @return Returns a xsNCName of the material attribute. + */ + xsNCName getMaterial() const { return attrMaterial; } + /** + * Sets the material attribute. + * @param atMaterial The new value for the material attribute. + */ + void setMaterial( xsNCName atMaterial ) { *(daeStringRef*)&attrMaterial = atMaterial; _validAttributeArray[2] = true; } + + /** + * Gets the input element array. + * @return Returns a reference to the array of input elements. + */ + domInputLocalOffset_Array &getInput_array() { return elemInput_array; } + /** + * Gets the input element array. + * @return Returns a constant reference to the array of input elements. + */ + const domInputLocalOffset_Array &getInput_array() const { return elemInput_array; } + /** + * Gets the p element array. + * @return Returns a reference to the array of p elements. + */ + domP_Array &getP_array() { return elemP_array; } + /** + * Gets the p element array. + * @return Returns a constant reference to the array of p elements. + */ + const domP_Array &getP_array() const { return elemP_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domLinestrips(DAE& dae) : daeElement(dae), attrName(), attrCount(), attrMaterial(), elemInput_array(), elemP_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domLinestrips() {} + /** + * Overloaded assignment operator + */ + virtual domLinestrips &operator=( const domLinestrips &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domLookat.h b/Engine/lib/collada/include/1.4/dom/domLookat.h new file mode 100644 index 000000000..c0cbceee2 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domLookat.h @@ -0,0 +1,106 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domLookat_h__ +#define __domLookat_h__ + +#include +#include +#include + +class DAE; + +/** + * The lookat element contains a position and orientation transformation suitable + * for aiming a camera. The lookat element contains three mathematical vectors + * within it that describe: 1.The position of the object; 2.The position + * of the interest point; 3.The direction that points up. + */ +class domLookat : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LOOKAT; } + static daeInt ID() { return 629; } + virtual daeInt typeID() const { return ID(); } +protected: // Attribute +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; + +protected: // Value + /** + * The domFloat3x3 value of the text data of this element. + */ + domFloat3x3 _value; + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + + /** + * Gets the _value array. + * @return Returns a domFloat3x3 reference of the _value array. + */ + domFloat3x3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domFloat3x3 reference of the _value array. + */ + const domFloat3x3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domFloat3x3 &val ) { _value = val; } + +protected: + /** + * Constructor + */ + domLookat(DAE& dae) : daeElement(dae), attrSid(), _value() {} + /** + * Destructor + */ + virtual ~domLookat() {} + /** + * Overloaded assignment operator + */ + virtual domLookat &operator=( const domLookat &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domMaterial.h b/Engine/lib/collada/include/1.4/dom/domMaterial.h new file mode 100644 index 000000000..206e4414e --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domMaterial.h @@ -0,0 +1,135 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domMaterial_h__ +#define __domMaterial_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * Materials describe the visual appearance of a geometric object. + */ +class domMaterial : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MATERIAL; } + static daeInt ID() { return 644; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The material element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * The material must instance an effect. @see domInstance_effect + */ + domInstance_effectRef elemInstance_effect; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the instance_effect element. + * @return a daeSmartRef to the instance_effect element. + */ + const domInstance_effectRef getInstance_effect() const { return elemInstance_effect; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domMaterial(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemInstance_effect(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domMaterial() {} + /** + * Overloaded assignment operator + */ + virtual domMaterial &operator=( const domMaterial &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domMatrix.h b/Engine/lib/collada/include/1.4/dom/domMatrix.h new file mode 100644 index 000000000..1c682ed53 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domMatrix.h @@ -0,0 +1,105 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domMatrix_h__ +#define __domMatrix_h__ + +#include +#include +#include + +class DAE; + +/** + * Matrix transformations embody mathematical changes to points within a coordinate + * systems or the coordinate system itself. The matrix element contains a + * 4-by-4 matrix of floating-point values. + */ +class domMatrix : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MATRIX; } + static daeInt ID() { return 630; } + virtual daeInt typeID() const { return ID(); } +protected: // Attribute +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; + +protected: // Value + /** + * The domFloat4x4 value of the text data of this element. + */ + domFloat4x4 _value; + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + + /** + * Gets the _value array. + * @return Returns a domFloat4x4 reference of the _value array. + */ + domFloat4x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domFloat4x4 reference of the _value array. + */ + const domFloat4x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domFloat4x4 &val ) { _value = val; } + +protected: + /** + * Constructor + */ + domMatrix(DAE& dae) : daeElement(dae), attrSid(), _value() {} + /** + * Destructor + */ + virtual ~domMatrix() {} + /** + * Overloaded assignment operator + */ + virtual domMatrix &operator=( const domMatrix &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domMesh.h b/Engine/lib/collada/include/1.4/dom/domMesh.h new file mode 100644 index 000000000..19722da8d --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domMesh.h @@ -0,0 +1,237 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domMesh_h__ +#define __domMesh_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +class DAE; + +/** + * The mesh element contains vertex and primitive information sufficient to + * describe basic geometric meshes. + */ +class domMesh : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MESH; } + static daeInt ID() { return 614; } + virtual daeInt typeID() const { return ID(); } + +protected: // Elements +/** + * The mesh element must contain one or more source elements. @see domSource + */ + domSource_Array elemSource_array; +/** + * The mesh element must contain one vertices element. @see domVertices + */ + domVerticesRef elemVertices; +/** + * The mesh element may contain any number of lines elements. @see domLines + */ + domLines_Array elemLines_array; +/** + * The mesh element may contain any number of linestrips elements. @see + * domLinestrips + */ + domLinestrips_Array elemLinestrips_array; +/** + * The mesh element may contain any number of polygons elements. @see domPolygons + */ + domPolygons_Array elemPolygons_array; +/** + * The mesh element may contain any number of polylist elements. @see domPolylist + */ + domPolylist_Array elemPolylist_array; +/** + * The mesh element may contain any number of triangles elements. @see domTriangles + */ + domTriangles_Array elemTriangles_array; +/** + * The mesh element may contain any number of trifans elements. @see domTrifans + */ + domTrifans_Array elemTrifans_array; +/** + * The mesh element may contain any number of tristrips elements. @see domTristrips + */ + domTristrips_Array elemTristrips_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the source element array. + * @return Returns a reference to the array of source elements. + */ + domSource_Array &getSource_array() { return elemSource_array; } + /** + * Gets the source element array. + * @return Returns a constant reference to the array of source elements. + */ + const domSource_Array &getSource_array() const { return elemSource_array; } + /** + * Gets the vertices element. + * @return a daeSmartRef to the vertices element. + */ + const domVerticesRef getVertices() const { return elemVertices; } + /** + * Gets the lines element array. + * @return Returns a reference to the array of lines elements. + */ + domLines_Array &getLines_array() { return elemLines_array; } + /** + * Gets the lines element array. + * @return Returns a constant reference to the array of lines elements. + */ + const domLines_Array &getLines_array() const { return elemLines_array; } + /** + * Gets the linestrips element array. + * @return Returns a reference to the array of linestrips elements. + */ + domLinestrips_Array &getLinestrips_array() { return elemLinestrips_array; } + /** + * Gets the linestrips element array. + * @return Returns a constant reference to the array of linestrips elements. + */ + const domLinestrips_Array &getLinestrips_array() const { return elemLinestrips_array; } + /** + * Gets the polygons element array. + * @return Returns a reference to the array of polygons elements. + */ + domPolygons_Array &getPolygons_array() { return elemPolygons_array; } + /** + * Gets the polygons element array. + * @return Returns a constant reference to the array of polygons elements. + */ + const domPolygons_Array &getPolygons_array() const { return elemPolygons_array; } + /** + * Gets the polylist element array. + * @return Returns a reference to the array of polylist elements. + */ + domPolylist_Array &getPolylist_array() { return elemPolylist_array; } + /** + * Gets the polylist element array. + * @return Returns a constant reference to the array of polylist elements. + */ + const domPolylist_Array &getPolylist_array() const { return elemPolylist_array; } + /** + * Gets the triangles element array. + * @return Returns a reference to the array of triangles elements. + */ + domTriangles_Array &getTriangles_array() { return elemTriangles_array; } + /** + * Gets the triangles element array. + * @return Returns a constant reference to the array of triangles elements. + */ + const domTriangles_Array &getTriangles_array() const { return elemTriangles_array; } + /** + * Gets the trifans element array. + * @return Returns a reference to the array of trifans elements. + */ + domTrifans_Array &getTrifans_array() { return elemTrifans_array; } + /** + * Gets the trifans element array. + * @return Returns a constant reference to the array of trifans elements. + */ + const domTrifans_Array &getTrifans_array() const { return elemTrifans_array; } + /** + * Gets the tristrips element array. + * @return Returns a reference to the array of tristrips elements. + */ + domTristrips_Array &getTristrips_array() { return elemTristrips_array; } + /** + * Gets the tristrips element array. + * @return Returns a constant reference to the array of tristrips elements. + */ + const domTristrips_Array &getTristrips_array() const { return elemTristrips_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domMesh(DAE& dae) : daeElement(dae), elemSource_array(), elemVertices(), elemLines_array(), elemLinestrips_array(), elemPolygons_array(), elemPolylist_array(), elemTriangles_array(), elemTrifans_array(), elemTristrips_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domMesh() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domMesh &operator=( const domMesh &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domMorph.h b/Engine/lib/collada/include/1.4/dom/domMorph.h new file mode 100644 index 000000000..3f8c3f10e --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domMorph.h @@ -0,0 +1,229 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domMorph_h__ +#define __domMorph_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * The morph element describes the data required to blend between sets of + * static meshes. Each possible mesh that can be blended (a morph target) + * must be specified. + */ +class domMorph : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MORPH; } + static daeInt ID() { return 662; } + virtual daeInt typeID() const { return ID(); } +public: + class domTargets; + + typedef daeSmartRef domTargetsRef; + typedef daeTArray domTargets_Array; + +/** + * The targets element declares the morph targets, their weights and any user + * defined attributes associated with them. + */ + class domTargets : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TARGETS; } + static daeInt ID() { return 663; } + virtual daeInt typeID() const { return ID(); } + + protected: // Elements +/** + * The input element must occur at least twice. These inputs are local inputs. + * @see domInput + */ + domInputLocal_Array elemInput_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + + public: //Accessors and Mutators + /** + * Gets the input element array. + * @return Returns a reference to the array of input elements. + */ + domInputLocal_Array &getInput_array() { return elemInput_array; } + /** + * Gets the input element array. + * @return Returns a constant reference to the array of input elements. + */ + const domInputLocal_Array &getInput_array() const { return elemInput_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + protected: + /** + * Constructor + */ + domTargets(DAE& dae) : daeElement(dae), elemInput_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domTargets() {} + /** + * Overloaded assignment operator + */ + virtual domTargets &operator=( const domTargets &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attributes +/** + * The method attribute specifies the which blending technique to use. The + * accepted values are NORMALIZED, and RELATIVE. The default value if not + * specified is NORMALIZED. Optional attribute. + */ + domMorphMethodType attrMethod; +/** + * The source attribute indicates the base mesh. Required attribute. + */ + xsAnyURI attrSource; + +protected: // Elements +/** + * The morph element must contain at least two source elements. @see domSource + */ + domSource_Array elemSource_array; +/** + * The targets element declares the morph targets, their weights and any user + * defined attributes associated with them. @see domTargets + */ + domTargetsRef elemTargets; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the method attribute. + * @return Returns a domMorphMethodType of the method attribute. + */ + domMorphMethodType getMethod() const { return attrMethod; } + /** + * Sets the method attribute. + * @param atMethod The new value for the method attribute. + */ + void setMethod( domMorphMethodType atMethod ) { attrMethod = atMethod; _validAttributeArray[0] = true; } + + /** + * Gets the source attribute. + * @return Returns a xsAnyURI reference of the source attribute. + */ + xsAnyURI &getSource() { return attrSource; } + /** + * Gets the source attribute. + * @return Returns a constant xsAnyURI reference of the source attribute. + */ + const xsAnyURI &getSource() const { return attrSource; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( const xsAnyURI &atSource ) { attrSource = atSource; _validAttributeArray[1] = true; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( xsString atSource ) { attrSource = atSource; _validAttributeArray[1] = true; } + + /** + * Gets the source element array. + * @return Returns a reference to the array of source elements. + */ + domSource_Array &getSource_array() { return elemSource_array; } + /** + * Gets the source element array. + * @return Returns a constant reference to the array of source elements. + */ + const domSource_Array &getSource_array() const { return elemSource_array; } + /** + * Gets the targets element. + * @return a daeSmartRef to the targets element. + */ + const domTargetsRef getTargets() const { return elemTargets; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domMorph(DAE& dae) : daeElement(dae), attrMethod(), attrSource(dae, *this), elemSource_array(), elemTargets(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domMorph() {} + /** + * Overloaded assignment operator + */ + virtual domMorph &operator=( const domMorph &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domName_array.h b/Engine/lib/collada/include/1.4/dom/domName_array.h new file mode 100644 index 000000000..dbd9cc1b2 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domName_array.h @@ -0,0 +1,137 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domName_array_h__ +#define __domName_array_h__ + +#include +#include +#include + +class DAE; + +/** + * The Name_array element declares the storage for a homogenous array of Name + * string values. + */ +class domName_array : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::NAME_ARRAY; } + static daeInt ID() { return 605; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; +/** + * The count attribute indicates the number of values in the array. Required + * attribute. + */ + domUint attrCount; + +protected: // Value + /** + * The domListOfNames value of the text data of this element. + */ + domListOfNames _value; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the count attribute. + * @return Returns a domUint of the count attribute. + */ + domUint getCount() const { return attrCount; } + /** + * Sets the count attribute. + * @param atCount The new value for the count attribute. + */ + void setCount( domUint atCount ) { attrCount = atCount; _validAttributeArray[2] = true; } + + /** + * Gets the _value array. + * @return Returns a domListOfNames reference of the _value array. + */ + domListOfNames &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domListOfNames reference of the _value array. + */ + const domListOfNames &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domListOfNames &val ) { _value = val; } + +protected: + /** + * Constructor + */ + domName_array(DAE& dae) : daeElement(dae), attrId(), attrName(), attrCount(), _value() {} + /** + * Destructor + */ + virtual ~domName_array() {} + /** + * Overloaded assignment operator + */ + virtual domName_array &operator=( const domName_array &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domNode.h b/Engine/lib/collada/include/1.4/dom/domNode.h new file mode 100644 index 000000000..4385df01a --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domNode.h @@ -0,0 +1,390 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domNode_h__ +#define __domNode_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +class DAE; + +/** + * Nodes embody the hierarchical relationship of elements in the scene. + */ +class domNode : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::NODE; } + static daeInt ID() { return 681; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; +/** + * The type attribute indicates the type of the node element. The default + * value is “NODE”. Optional attribute. + */ + domNodeType attrType; +/** + * The layer attribute indicates the names of the layers to which this node + * belongs. For example, a value of “foreground glowing” indicates that + * this node belongs to both the â€foreground’ layer and the â€glowing’ + * layer. The default value is empty, indicating that the node doesn’t + * belong to any layer. Optional attribute. + */ + domListOfNames attrLayer; + +protected: // Elements +/** + * The node element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * The node element may contain any number of lookat elements. @see domLookat + */ + domLookat_Array elemLookat_array; +/** + * The node element may contain any number of matrix elements. @see domMatrix + */ + domMatrix_Array elemMatrix_array; +/** + * The node element may contain any number of rotate elements. @see domRotate + */ + domRotate_Array elemRotate_array; +/** + * The node element may contain any number of scale elements. @see domScale + */ + domScale_Array elemScale_array; +/** + * The node element may contain any number of skew elements. @see domSkew + */ + domSkew_Array elemSkew_array; +/** + * The node element may contain any number of translate elements. @see domTranslate + */ + domTranslate_Array elemTranslate_array; +/** + * The node element may instance any number of camera objects. @see domInstance_camera + */ + domInstance_camera_Array elemInstance_camera_array; +/** + * The node element may instance any number of controller objects. @see + * domInstance_controller + */ + domInstance_controller_Array elemInstance_controller_array; +/** + * The node element may instance any number of geometry objects. @see domInstance_geometry + */ + domInstance_geometry_Array elemInstance_geometry_array; +/** + * The node element may instance any number of light objects. @see domInstance_light + */ + domInstance_light_Array elemInstance_light_array; +/** + * The node element may instance any number of node elements or hierarchies + * objects. @see domInstance_node + */ + domInstance_node_Array elemInstance_node_array; +/** + * The node element may be hierarchical and be the parent of any number of + * other node elements. @see domNode + */ + domNode_Array elemNode_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[2] = true; } + + /** + * Gets the type attribute. + * @return Returns a domNodeType of the type attribute. + */ + domNodeType getType() const { return attrType; } + /** + * Sets the type attribute. + * @param atType The new value for the type attribute. + */ + void setType( domNodeType atType ) { attrType = atType; _validAttributeArray[3] = true; } + + /** + * Gets the layer array attribute. + * @return Returns a domListOfNames reference of the layer array attribute. + */ + domListOfNames &getLayer() { return attrLayer; } + /** + * Gets the layer array attribute. + * @return Returns a constant domListOfNames reference of the layer array attribute. + */ + const domListOfNames &getLayer() const { return attrLayer; } + /** + * Sets the layer array attribute. + * @param atLayer The new value for the layer array attribute. + */ + void setLayer( const domListOfNames &atLayer ) { attrLayer = atLayer; _validAttributeArray[4] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the lookat element array. + * @return Returns a reference to the array of lookat elements. + */ + domLookat_Array &getLookat_array() { return elemLookat_array; } + /** + * Gets the lookat element array. + * @return Returns a constant reference to the array of lookat elements. + */ + const domLookat_Array &getLookat_array() const { return elemLookat_array; } + /** + * Gets the matrix element array. + * @return Returns a reference to the array of matrix elements. + */ + domMatrix_Array &getMatrix_array() { return elemMatrix_array; } + /** + * Gets the matrix element array. + * @return Returns a constant reference to the array of matrix elements. + */ + const domMatrix_Array &getMatrix_array() const { return elemMatrix_array; } + /** + * Gets the rotate element array. + * @return Returns a reference to the array of rotate elements. + */ + domRotate_Array &getRotate_array() { return elemRotate_array; } + /** + * Gets the rotate element array. + * @return Returns a constant reference to the array of rotate elements. + */ + const domRotate_Array &getRotate_array() const { return elemRotate_array; } + /** + * Gets the scale element array. + * @return Returns a reference to the array of scale elements. + */ + domScale_Array &getScale_array() { return elemScale_array; } + /** + * Gets the scale element array. + * @return Returns a constant reference to the array of scale elements. + */ + const domScale_Array &getScale_array() const { return elemScale_array; } + /** + * Gets the skew element array. + * @return Returns a reference to the array of skew elements. + */ + domSkew_Array &getSkew_array() { return elemSkew_array; } + /** + * Gets the skew element array. + * @return Returns a constant reference to the array of skew elements. + */ + const domSkew_Array &getSkew_array() const { return elemSkew_array; } + /** + * Gets the translate element array. + * @return Returns a reference to the array of translate elements. + */ + domTranslate_Array &getTranslate_array() { return elemTranslate_array; } + /** + * Gets the translate element array. + * @return Returns a constant reference to the array of translate elements. + */ + const domTranslate_Array &getTranslate_array() const { return elemTranslate_array; } + /** + * Gets the instance_camera element array. + * @return Returns a reference to the array of instance_camera elements. + */ + domInstance_camera_Array &getInstance_camera_array() { return elemInstance_camera_array; } + /** + * Gets the instance_camera element array. + * @return Returns a constant reference to the array of instance_camera elements. + */ + const domInstance_camera_Array &getInstance_camera_array() const { return elemInstance_camera_array; } + /** + * Gets the instance_controller element array. + * @return Returns a reference to the array of instance_controller elements. + */ + domInstance_controller_Array &getInstance_controller_array() { return elemInstance_controller_array; } + /** + * Gets the instance_controller element array. + * @return Returns a constant reference to the array of instance_controller elements. + */ + const domInstance_controller_Array &getInstance_controller_array() const { return elemInstance_controller_array; } + /** + * Gets the instance_geometry element array. + * @return Returns a reference to the array of instance_geometry elements. + */ + domInstance_geometry_Array &getInstance_geometry_array() { return elemInstance_geometry_array; } + /** + * Gets the instance_geometry element array. + * @return Returns a constant reference to the array of instance_geometry elements. + */ + const domInstance_geometry_Array &getInstance_geometry_array() const { return elemInstance_geometry_array; } + /** + * Gets the instance_light element array. + * @return Returns a reference to the array of instance_light elements. + */ + domInstance_light_Array &getInstance_light_array() { return elemInstance_light_array; } + /** + * Gets the instance_light element array. + * @return Returns a constant reference to the array of instance_light elements. + */ + const domInstance_light_Array &getInstance_light_array() const { return elemInstance_light_array; } + /** + * Gets the instance_node element array. + * @return Returns a reference to the array of instance_node elements. + */ + domInstance_node_Array &getInstance_node_array() { return elemInstance_node_array; } + /** + * Gets the instance_node element array. + * @return Returns a constant reference to the array of instance_node elements. + */ + const domInstance_node_Array &getInstance_node_array() const { return elemInstance_node_array; } + /** + * Gets the node element array. + * @return Returns a reference to the array of node elements. + */ + domNode_Array &getNode_array() { return elemNode_array; } + /** + * Gets the node element array. + * @return Returns a constant reference to the array of node elements. + */ + const domNode_Array &getNode_array() const { return elemNode_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domNode(DAE& dae) : daeElement(dae), attrId(), attrName(), attrSid(), attrType(), attrLayer(), elemAsset(), elemLookat_array(), elemMatrix_array(), elemRotate_array(), elemScale_array(), elemSkew_array(), elemTranslate_array(), elemInstance_camera_array(), elemInstance_controller_array(), elemInstance_geometry_array(), elemInstance_light_array(), elemInstance_node_array(), elemNode_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domNode() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domNode &operator=( const domNode &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domP.h b/Engine/lib/collada/include/1.4/dom/domP.h new file mode 100644 index 000000000..4d181388a --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domP.h @@ -0,0 +1,88 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domP_h__ +#define __domP_h__ + +#include +#include +#include + +class DAE; + +/** + * The p element represents primitive data for the primitive types (lines, + * linestrips, polygons, polylist, triangles, trifans, tristrips). The p + * element contains indices that reference into the parent's source elements + * referenced by the input elements. + */ +class domP : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::P; } + static daeInt ID() { return 617; } + virtual daeInt typeID() const { return ID(); } + +protected: // Value + /** + * The domListOfUInts value of the text data of this element. + */ + domListOfUInts _value; + +public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domListOfUInts reference of the _value array. + */ + domListOfUInts &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domListOfUInts reference of the _value array. + */ + const domListOfUInts &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domListOfUInts &val ) { _value = val; } + +protected: + /** + * Constructor + */ + domP(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domP() {} + /** + * Overloaded assignment operator + */ + virtual domP &operator=( const domP &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domParam.h b/Engine/lib/collada/include/1.4/dom/domParam.h new file mode 100644 index 000000000..664eed816 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domParam.h @@ -0,0 +1,146 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domParam_h__ +#define __domParam_h__ + +#include +#include +#include + +class DAE; + +/** + * The param element declares parametric information regarding its parent + * element. + */ +class domParam : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PARAM; } + static daeInt ID() { return 610; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; +/** + * The semantic attribute is the user-defined meaning of the parameter. Optional + * attribute. + */ + xsNMTOKEN attrSemantic; +/** + * The type attribute indicates the type of the value data. This text string + * must be understood by the application. Required attribute. + */ + xsNMTOKEN attrType; + +protected: // Value + /** + * The xsString value of the text data of this element. + */ + xsString _value; + +public: //Accessors and Mutators + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[0] = true; } + + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[1] = true; } + + /** + * Gets the semantic attribute. + * @return Returns a xsNMTOKEN of the semantic attribute. + */ + xsNMTOKEN getSemantic() const { return attrSemantic; } + /** + * Sets the semantic attribute. + * @param atSemantic The new value for the semantic attribute. + */ + void setSemantic( xsNMTOKEN atSemantic ) { *(daeStringRef*)&attrSemantic = atSemantic; _validAttributeArray[2] = true; } + + /** + * Gets the type attribute. + * @return Returns a xsNMTOKEN of the type attribute. + */ + xsNMTOKEN getType() const { return attrType; } + /** + * Sets the type attribute. + * @param atType The new value for the type attribute. + */ + void setType( xsNMTOKEN atType ) { *(daeStringRef*)&attrType = atType; _validAttributeArray[3] = true; } + + /** + * Gets the value of this element. + * @return Returns a xsString of the value. + */ + xsString getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsString val ) { *(daeStringRef*)&_value = val; } + +protected: + /** + * Constructor + */ + domParam(DAE& dae) : daeElement(dae), attrName(), attrSid(), attrSemantic(), attrType(), _value() {} + /** + * Destructor + */ + virtual ~domParam() {} + /** + * Overloaded assignment operator + */ + virtual domParam &operator=( const domParam &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domPhysics_material.h b/Engine/lib/collada/include/1.4/dom/domPhysics_material.h new file mode 100644 index 000000000..722abf65b --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domPhysics_material.h @@ -0,0 +1,232 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domPhysics_material_h__ +#define __domPhysics_material_h__ + +#include +#include +#include + +#include +#include +#include +#include +class DAE; + +/** + * This element defines the physical properties of an object. It contains + * a technique/profile with parameters. The COMMON profile defines the built-in + * names, such as static_friction. + */ +class domPhysics_material : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PHYSICS_MATERIAL; } + static daeInt ID() { return 791; } + virtual daeInt typeID() const { return ID(); } +public: + class domTechnique_common; + + typedef daeSmartRef domTechnique_commonRef; + typedef daeTArray domTechnique_common_Array; + +/** + * The technique_common element specifies the physics_material information + * for the common profile which all COLLADA implementations need to support. + */ + class domTechnique_common : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TECHNIQUE_COMMON; } + static daeInt ID() { return 792; } + virtual daeInt typeID() const { return ID(); } + + protected: // Elements +/** + * Dynamic friction coefficient @see domDynamic_friction + */ + domTargetableFloatRef elemDynamic_friction; +/** + * The proportion of the kinetic energy preserved in the impact (typically + * ranges from 0.0 to 1.0) @see domRestitution + */ + domTargetableFloatRef elemRestitution; +/** + * Static friction coefficient @see domStatic_friction + */ + domTargetableFloatRef elemStatic_friction; + + public: //Accessors and Mutators + /** + * Gets the dynamic_friction element. + * @return a daeSmartRef to the dynamic_friction element. + */ + const domTargetableFloatRef getDynamic_friction() const { return elemDynamic_friction; } + /** + * Gets the restitution element. + * @return a daeSmartRef to the restitution element. + */ + const domTargetableFloatRef getRestitution() const { return elemRestitution; } + /** + * Gets the static_friction element. + * @return a daeSmartRef to the static_friction element. + */ + const domTargetableFloatRef getStatic_friction() const { return elemStatic_friction; } + protected: + /** + * Constructor + */ + domTechnique_common(DAE& dae) : daeElement(dae), elemDynamic_friction(), elemRestitution(), elemStatic_friction() {} + /** + * Destructor + */ + virtual ~domTechnique_common() {} + /** + * Overloaded assignment operator + */ + virtual domTechnique_common &operator=( const domTechnique_common &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The physics_material element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * The technique_common element specifies the physics_material information + * for the common profile which all COLLADA implementations need to support. + * @see domTechnique_common + */ + domTechnique_commonRef elemTechnique_common; +/** + * This element may contain any number of non-common profile techniques. + * @see domTechnique + */ + domTechnique_Array elemTechnique_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the technique_common element. + * @return a daeSmartRef to the technique_common element. + */ + const domTechnique_commonRef getTechnique_common() const { return elemTechnique_common; } + /** + * Gets the technique element array. + * @return Returns a reference to the array of technique elements. + */ + domTechnique_Array &getTechnique_array() { return elemTechnique_array; } + /** + * Gets the technique element array. + * @return Returns a constant reference to the array of technique elements. + */ + const domTechnique_Array &getTechnique_array() const { return elemTechnique_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domPhysics_material(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemTechnique_common(), elemTechnique_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domPhysics_material() {} + /** + * Overloaded assignment operator + */ + virtual domPhysics_material &operator=( const domPhysics_material &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domPhysics_model.h b/Engine/lib/collada/include/1.4/dom/domPhysics_model.h new file mode 100644 index 000000000..893ee3ae1 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domPhysics_model.h @@ -0,0 +1,174 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domPhysics_model_h__ +#define __domPhysics_model_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include +class DAE; + +/** + * This element allows for building complex combinations of rigid-bodies and + * constraints that may be instantiated multiple times. + */ +class domPhysics_model : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PHYSICS_MODEL; } + static daeInt ID() { return 813; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The physics_model element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * The physics_model may define any number of rigid_body elements. @see + * domRigid_body + */ + domRigid_body_Array elemRigid_body_array; +/** + * The physics_model may define any number of rigid_constraint elements. + * @see domRigid_constraint + */ + domRigid_constraint_Array elemRigid_constraint_array; +/** + * The physics_model may instance any number of other physics_model elements. + * @see domInstance_physics_model + */ + domInstance_physics_model_Array elemInstance_physics_model_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the rigid_body element array. + * @return Returns a reference to the array of rigid_body elements. + */ + domRigid_body_Array &getRigid_body_array() { return elemRigid_body_array; } + /** + * Gets the rigid_body element array. + * @return Returns a constant reference to the array of rigid_body elements. + */ + const domRigid_body_Array &getRigid_body_array() const { return elemRigid_body_array; } + /** + * Gets the rigid_constraint element array. + * @return Returns a reference to the array of rigid_constraint elements. + */ + domRigid_constraint_Array &getRigid_constraint_array() { return elemRigid_constraint_array; } + /** + * Gets the rigid_constraint element array. + * @return Returns a constant reference to the array of rigid_constraint elements. + */ + const domRigid_constraint_Array &getRigid_constraint_array() const { return elemRigid_constraint_array; } + /** + * Gets the instance_physics_model element array. + * @return Returns a reference to the array of instance_physics_model elements. + */ + domInstance_physics_model_Array &getInstance_physics_model_array() { return elemInstance_physics_model_array; } + /** + * Gets the instance_physics_model element array. + * @return Returns a constant reference to the array of instance_physics_model elements. + */ + const domInstance_physics_model_Array &getInstance_physics_model_array() const { return elemInstance_physics_model_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domPhysics_model(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemRigid_body_array(), elemRigid_constraint_array(), elemInstance_physics_model_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domPhysics_model() {} + /** + * Overloaded assignment operator + */ + virtual domPhysics_model &operator=( const domPhysics_model &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domPhysics_scene.h b/Engine/lib/collada/include/1.4/dom/domPhysics_scene.h new file mode 100644 index 000000000..6b128bdc8 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domPhysics_scene.h @@ -0,0 +1,248 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domPhysics_scene_h__ +#define __domPhysics_scene_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +class DAE; + +class domPhysics_scene : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PHYSICS_SCENE; } + static daeInt ID() { return 793; } + virtual daeInt typeID() const { return ID(); } +public: + class domTechnique_common; + + typedef daeSmartRef domTechnique_commonRef; + typedef daeTArray domTechnique_common_Array; + +/** + * The technique_common element specifies the physics_scene information for + * the common profile which all COLLADA implementations need to support. + */ + class domTechnique_common : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TECHNIQUE_COMMON; } + static daeInt ID() { return 794; } + virtual daeInt typeID() const { return ID(); } + + protected: // Elements +/** + * The gravity vector to use for the physics_scene. @see domGravity + */ + domTargetableFloat3Ref elemGravity; +/** + * The time_step for the physics_scene. @see domTime_step + */ + domTargetableFloatRef elemTime_step; + + public: //Accessors and Mutators + /** + * Gets the gravity element. + * @return a daeSmartRef to the gravity element. + */ + const domTargetableFloat3Ref getGravity() const { return elemGravity; } + /** + * Gets the time_step element. + * @return a daeSmartRef to the time_step element. + */ + const domTargetableFloatRef getTime_step() const { return elemTime_step; } + protected: + /** + * Constructor + */ + domTechnique_common(DAE& dae) : daeElement(dae), elemGravity(), elemTime_step() {} + /** + * Destructor + */ + virtual ~domTechnique_common() {} + /** + * Overloaded assignment operator + */ + virtual domTechnique_common &operator=( const domTechnique_common &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The physics_scene element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * There may be any number of instance_force_field elements. @see domInstance_force_field + */ + domInstance_force_field_Array elemInstance_force_field_array; +/** + * There may be any number of instance_physics_model elements. @see domInstance_physics_model + */ + domInstance_physics_model_Array elemInstance_physics_model_array; +/** + * The technique_common element specifies the physics_scene information for + * the common profile which all COLLADA implementations need to support. + * @see domTechnique_common + */ + domTechnique_commonRef elemTechnique_common; +/** + * This element may contain any number of non-common profile techniques. + * @see domTechnique + */ + domTechnique_Array elemTechnique_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the instance_force_field element array. + * @return Returns a reference to the array of instance_force_field elements. + */ + domInstance_force_field_Array &getInstance_force_field_array() { return elemInstance_force_field_array; } + /** + * Gets the instance_force_field element array. + * @return Returns a constant reference to the array of instance_force_field elements. + */ + const domInstance_force_field_Array &getInstance_force_field_array() const { return elemInstance_force_field_array; } + /** + * Gets the instance_physics_model element array. + * @return Returns a reference to the array of instance_physics_model elements. + */ + domInstance_physics_model_Array &getInstance_physics_model_array() { return elemInstance_physics_model_array; } + /** + * Gets the instance_physics_model element array. + * @return Returns a constant reference to the array of instance_physics_model elements. + */ + const domInstance_physics_model_Array &getInstance_physics_model_array() const { return elemInstance_physics_model_array; } + /** + * Gets the technique_common element. + * @return a daeSmartRef to the technique_common element. + */ + const domTechnique_commonRef getTechnique_common() const { return elemTechnique_common; } + /** + * Gets the technique element array. + * @return Returns a reference to the array of technique elements. + */ + domTechnique_Array &getTechnique_array() { return elemTechnique_array; } + /** + * Gets the technique element array. + * @return Returns a constant reference to the array of technique elements. + */ + const domTechnique_Array &getTechnique_array() const { return elemTechnique_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domPhysics_scene(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemInstance_force_field_array(), elemInstance_physics_model_array(), elemTechnique_common(), elemTechnique_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domPhysics_scene() {} + /** + * Overloaded assignment operator + */ + virtual domPhysics_scene &operator=( const domPhysics_scene &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domPlane.h b/Engine/lib/collada/include/1.4/dom/domPlane.h new file mode 100644 index 000000000..8d143c026 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domPlane.h @@ -0,0 +1,159 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domPlane_h__ +#define __domPlane_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * An infinite plane primitive. + */ +class domPlane : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PLANE; } + static daeInt ID() { return 769; } + virtual daeInt typeID() const { return ID(); } +public: + class domEquation; + + typedef daeSmartRef domEquationRef; + typedef daeTArray domEquation_Array; + +/** + * 4 float values that represent the coefficients for the plane’s equation: + * Ax + By + Cz + D = 0 + */ + class domEquation : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::EQUATION; } + static daeInt ID() { return 770; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFloat4 value of the text data of this element. + */ + domFloat4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domFloat4 reference of the _value array. + */ + domFloat4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domFloat4 reference of the _value array. + */ + const domFloat4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domFloat4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domEquation(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domEquation() {} + /** + * Overloaded assignment operator + */ + virtual domEquation &operator=( const domEquation &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements +/** + * 4 float values that represent the coefficients for the plane’s equation: + * Ax + By + Cz + D = 0 @see domEquation + */ + domEquationRef elemEquation; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the equation element. + * @return a daeSmartRef to the equation element. + */ + const domEquationRef getEquation() const { return elemEquation; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domPlane(DAE& dae) : daeElement(dae), elemEquation(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domPlane() {} + /** + * Overloaded assignment operator + */ + virtual domPlane &operator=( const domPlane &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domPolygons.h b/Engine/lib/collada/include/1.4/dom/domPolygons.h new file mode 100644 index 000000000..79bd0b59b --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domPolygons.h @@ -0,0 +1,344 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domPolygons_h__ +#define __domPolygons_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * The polygons element provides the information needed to bind vertex attributes + * together and then organize those vertices into individual polygons. The + * polygons described can contain arbitrary numbers of vertices. These polygons + * may be self intersecting and may also contain holes. + */ +class domPolygons : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::POLYGONS; } + static daeInt ID() { return 620; } + virtual daeInt typeID() const { return ID(); } +public: + class domPh; + + typedef daeSmartRef domPhRef; + typedef daeTArray domPh_Array; + +/** + * The ph element descripes a polygon with holes. + */ + class domPh : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PH; } + static daeInt ID() { return 621; } + virtual daeInt typeID() const { return ID(); } + public: + class domH; + + typedef daeSmartRef domHRef; + typedef daeTArray domH_Array; + +/** + * The h element represents a hole in the polygon specified. There must be + * at least one h element. + */ + class domH : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::H; } + static daeInt ID() { return 622; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domListOfUInts value of the text data of this element. + */ + domListOfUInts _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domListOfUInts reference of the _value array. + */ + domListOfUInts &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domListOfUInts reference of the _value array. + */ + const domListOfUInts &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domListOfUInts &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domH(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domH() {} + /** + * Overloaded assignment operator + */ + virtual domH &operator=( const domH &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements +/** + * Theere may only be one p element. @see domP + */ + domPRef elemP; +/** + * The h element represents a hole in the polygon specified. There must be + * at least one h element. @see domH + */ + domH_Array elemH_array; + + public: //Accessors and Mutators + /** + * Gets the p element. + * @return a daeSmartRef to the p element. + */ + const domPRef getP() const { return elemP; } + /** + * Gets the h element array. + * @return Returns a reference to the array of h elements. + */ + domH_Array &getH_array() { return elemH_array; } + /** + * Gets the h element array. + * @return Returns a constant reference to the array of h elements. + */ + const domH_Array &getH_array() const { return elemH_array; } + protected: + /** + * Constructor + */ + domPh(DAE& dae) : daeElement(dae), elemP(), elemH_array() {} + /** + * Destructor + */ + virtual ~domPh() {} + /** + * Overloaded assignment operator + */ + virtual domPh &operator=( const domPh &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attributes +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; +/** + * The count attribute indicates the number of polygon primitives. Required + * attribute. + */ + domUint attrCount; +/** + * The material attribute declares a symbol for a material. This symbol is + * bound to a material at the time of instantiation. If the material attribute + * is not specified then the lighting and shading results are application + * defined. Optional attribute. + */ + xsNCName attrMaterial; + +protected: // Elements +/** + * The input element may occur any number of times. This input is a local + * input with the offset and set attributes. @see domInput + */ + domInputLocalOffset_Array elemInput_array; +/** + * The p element may occur any number of times. @see domP + */ + domP_Array elemP_array; +/** + * The ph element descripes a polygon with holes. @see domPh + */ + domPh_Array elemPh_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[0] = true; } + + /** + * Gets the count attribute. + * @return Returns a domUint of the count attribute. + */ + domUint getCount() const { return attrCount; } + /** + * Sets the count attribute. + * @param atCount The new value for the count attribute. + */ + void setCount( domUint atCount ) { attrCount = atCount; _validAttributeArray[1] = true; } + + /** + * Gets the material attribute. + * @return Returns a xsNCName of the material attribute. + */ + xsNCName getMaterial() const { return attrMaterial; } + /** + * Sets the material attribute. + * @param atMaterial The new value for the material attribute. + */ + void setMaterial( xsNCName atMaterial ) { *(daeStringRef*)&attrMaterial = atMaterial; _validAttributeArray[2] = true; } + + /** + * Gets the input element array. + * @return Returns a reference to the array of input elements. + */ + domInputLocalOffset_Array &getInput_array() { return elemInput_array; } + /** + * Gets the input element array. + * @return Returns a constant reference to the array of input elements. + */ + const domInputLocalOffset_Array &getInput_array() const { return elemInput_array; } + /** + * Gets the p element array. + * @return Returns a reference to the array of p elements. + */ + domP_Array &getP_array() { return elemP_array; } + /** + * Gets the p element array. + * @return Returns a constant reference to the array of p elements. + */ + const domP_Array &getP_array() const { return elemP_array; } + /** + * Gets the ph element array. + * @return Returns a reference to the array of ph elements. + */ + domPh_Array &getPh_array() { return elemPh_array; } + /** + * Gets the ph element array. + * @return Returns a constant reference to the array of ph elements. + */ + const domPh_Array &getPh_array() const { return elemPh_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domPolygons(DAE& dae) : daeElement(dae), attrName(), attrCount(), attrMaterial(), elemInput_array(), elemP_array(), elemPh_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domPolygons() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domPolygons &operator=( const domPolygons &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domPolylist.h b/Engine/lib/collada/include/1.4/dom/domPolylist.h new file mode 100644 index 000000000..e2b9f8a17 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domPolylist.h @@ -0,0 +1,241 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domPolylist_h__ +#define __domPolylist_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * The polylist element provides the information needed to bind vertex attributes + * together and then organize those vertices into individual polygons. The + * polygons described in polylist can contain arbitrary numbers of vertices. + * Unlike the polygons element, the polylist element cannot contain polygons + * with holes. + */ +class domPolylist : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::POLYLIST; } + static daeInt ID() { return 623; } + virtual daeInt typeID() const { return ID(); } +public: + class domVcount; + + typedef daeSmartRef domVcountRef; + typedef daeTArray domVcount_Array; + +/** + * The vcount element contains a list of integers describing the number of + * sides for each polygon described by the polylist element. The vcount element + * may occur once. + */ + class domVcount : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::VCOUNT; } + static daeInt ID() { return 624; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domListOfUInts value of the text data of this element. + */ + domListOfUInts _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domListOfUInts reference of the _value array. + */ + domListOfUInts &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domListOfUInts reference of the _value array. + */ + const domListOfUInts &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domListOfUInts &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domVcount(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domVcount() {} + /** + * Overloaded assignment operator + */ + virtual domVcount &operator=( const domVcount &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attributes +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; +/** + * The count attribute indicates the number of polygon primitives. Required + * attribute. + */ + domUint attrCount; +/** + * The material attribute declares a symbol for a material. This symbol is + * bound to a material at the time of instantiation. If the material attribute + * is not specified then the lighting and shading results are application + * defined. Optional attribute. + */ + xsNCName attrMaterial; + +protected: // Elements +/** + * The input element may occur any number of times. This input is a local + * input with the offset and set attributes. @see domInput + */ + domInputLocalOffset_Array elemInput_array; +/** + * The vcount element contains a list of integers describing the number of + * sides for each polygon described by the polylist element. The vcount element + * may occur once. @see domVcount + */ + domVcountRef elemVcount; +/** + * The p element may occur once. @see domP + */ + domPRef elemP; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[0] = true; } + + /** + * Gets the count attribute. + * @return Returns a domUint of the count attribute. + */ + domUint getCount() const { return attrCount; } + /** + * Sets the count attribute. + * @param atCount The new value for the count attribute. + */ + void setCount( domUint atCount ) { attrCount = atCount; _validAttributeArray[1] = true; } + + /** + * Gets the material attribute. + * @return Returns a xsNCName of the material attribute. + */ + xsNCName getMaterial() const { return attrMaterial; } + /** + * Sets the material attribute. + * @param atMaterial The new value for the material attribute. + */ + void setMaterial( xsNCName atMaterial ) { *(daeStringRef*)&attrMaterial = atMaterial; _validAttributeArray[2] = true; } + + /** + * Gets the input element array. + * @return Returns a reference to the array of input elements. + */ + domInputLocalOffset_Array &getInput_array() { return elemInput_array; } + /** + * Gets the input element array. + * @return Returns a constant reference to the array of input elements. + */ + const domInputLocalOffset_Array &getInput_array() const { return elemInput_array; } + /** + * Gets the vcount element. + * @return a daeSmartRef to the vcount element. + */ + const domVcountRef getVcount() const { return elemVcount; } + /** + * Gets the p element. + * @return a daeSmartRef to the p element. + */ + const domPRef getP() const { return elemP; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domPolylist(DAE& dae) : daeElement(dae), attrName(), attrCount(), attrMaterial(), elemInput_array(), elemVcount(), elemP(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domPolylist() {} + /** + * Overloaded assignment operator + */ + virtual domPolylist &operator=( const domPolylist &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domProfile_CG.h b/Engine/lib/collada/include/1.4/dom/domProfile_CG.h new file mode 100644 index 000000000..0d3ed37e4 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domProfile_CG.h @@ -0,0 +1,1176 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domProfile_CG_h__ +#define __domProfile_CG_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +class DAE; + +/** + * Opens a block of CG platform-specific data types and technique declarations. + */ +class domProfile_CG : public domFx_profile_abstract +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PROFILE_CG; } + static daeInt ID() { return 746; } + virtual daeInt typeID() const { return ID(); } +public: + class domTechnique; + + typedef daeSmartRef domTechniqueRef; + typedef daeTArray domTechnique_Array; + +/** + * Holds a description of the textures, samplers, shaders, parameters, and + * passes necessary for rendering this effect using one method. + */ + class domTechnique : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TECHNIQUE; } + static daeInt ID() { return 747; } + virtual daeInt typeID() const { return ID(); } + public: + class domPass; + + typedef daeSmartRef domPassRef; + typedef daeTArray domPass_Array; + +/** + * A static declaration of all the render states, shaders, and settings for + * one rendering pipeline. + */ + class domPass : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PASS; } + static daeInt ID() { return 748; } + virtual daeInt typeID() const { return ID(); } + public: + class domDraw; + + typedef daeSmartRef domDrawRef; + typedef daeTArray domDraw_Array; + + class domDraw : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::DRAW; } + static daeInt ID() { return 749; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_draw_common value of the text data of this element. + */ + domFx_draw_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_draw_common of the value. + */ + domFx_draw_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_draw_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domDraw(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domDraw() {} + /** + * Overloaded assignment operator + */ + virtual domDraw &operator=( const domDraw &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domShader; + + typedef daeSmartRef domShaderRef; + typedef daeTArray domShader_Array; + +/** + * Declare and prepare a shader for execution in the rendering pipeline of + * a pass. + */ + class domShader : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SHADER; } + static daeInt ID() { return 750; } + virtual daeInt typeID() const { return ID(); } + public: + class domCompiler_target; + + typedef daeSmartRef domCompiler_targetRef; + typedef daeTArray domCompiler_target_Array; + + class domCompiler_target : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::COMPILER_TARGET; } + static daeInt ID() { return 751; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsNMTOKEN value of the text data of this element. + */ + xsNMTOKEN _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsNMTOKEN of the value. + */ + xsNMTOKEN getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNMTOKEN val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domCompiler_target(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domCompiler_target() {} + /** + * Overloaded assignment operator + */ + virtual domCompiler_target &operator=( const domCompiler_target &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domCompiler_options; + + typedef daeSmartRef domCompiler_optionsRef; + typedef daeTArray domCompiler_options_Array; + +/** + * A string containing command-line operations for the shader compiler. + */ + class domCompiler_options : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::COMPILER_OPTIONS; } + static daeInt ID() { return 752; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsString value of the text data of this element. + */ + xsString _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsString of the value. + */ + xsString getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsString val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domCompiler_options(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domCompiler_options() {} + /** + * Overloaded assignment operator + */ + virtual domCompiler_options &operator=( const domCompiler_options &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domName; + + typedef daeSmartRef domNameRef; + typedef daeTArray domName_Array; + +/** + * The entry symbol for the shader function. + */ + class domName : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::NAME; } + static daeInt ID() { return 753; } + virtual daeInt typeID() const { return ID(); } + protected: // Attribute + xsNCName attrSource; + + protected: // Value + /** + * The xsNCName value of the text data of this element. + */ + xsNCName _value; + + public: //Accessors and Mutators + /** + * Gets the source attribute. + * @return Returns a xsNCName of the source attribute. + */ + xsNCName getSource() const { return attrSource; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( xsNCName atSource ) { *(daeStringRef*)&attrSource = atSource; _validAttributeArray[0] = true; } + + /** + * Gets the value of this element. + * @return Returns a xsNCName of the value. + */ + xsNCName getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNCName val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domName(DAE& dae) : daeElement(dae), attrSource(), _value() {} + /** + * Destructor + */ + virtual ~domName() {} + /** + * Overloaded assignment operator + */ + virtual domName &operator=( const domName &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBind; + + typedef daeSmartRef domBindRef; + typedef daeTArray domBind_Array; + +/** + * Binds values to uniform inputs of a shader. + */ + class domBind : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BIND; } + static daeInt ID() { return 754; } + virtual daeInt typeID() const { return ID(); } + public: + class domParam; + + typedef daeSmartRef domParamRef; + typedef daeTArray domParam_Array; + +/** + * References a predefined parameter in shader binding declarations. + */ + class domParam : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PARAM; } + static daeInt ID() { return 755; } + virtual daeInt typeID() const { return ID(); } + protected: // Attribute + xsNCName attrRef; + + + public: //Accessors and Mutators + /** + * Gets the ref attribute. + * @return Returns a xsNCName of the ref attribute. + */ + xsNCName getRef() const { return attrRef; } + /** + * Sets the ref attribute. + * @param atRef The new value for the ref attribute. + */ + void setRef( xsNCName atRef ) { *(daeStringRef*)&attrRef = atRef; _validAttributeArray[0] = true; } + + protected: + /** + * Constructor + */ + domParam(DAE& dae) : daeElement(dae), attrRef() {} + /** + * Destructor + */ + virtual ~domParam() {} + /** + * Overloaded assignment operator + */ + virtual domParam &operator=( const domParam &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + protected: // Attribute +/** + * The identifier for a uniform input parameter to the shader (a formal function + * parameter or in-scope global) that will be bound to an external resource. + */ + xsNCName attrSymbol; + + protected: // Elements + domCg_param_typeRef elemCg_param_type; +/** + * References a predefined parameter in shader binding declarations. @see + * domParam + */ + domParamRef elemParam; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the symbol attribute. + * @return Returns a xsNCName of the symbol attribute. + */ + xsNCName getSymbol() const { return attrSymbol; } + /** + * Sets the symbol attribute. + * @param atSymbol The new value for the symbol attribute. + */ + void setSymbol( xsNCName atSymbol ) { *(daeStringRef*)&attrSymbol = atSymbol; _validAttributeArray[0] = true; } + + /** + * Gets the cg_param_type element. + * @return a daeSmartRef to the cg_param_type element. + */ + const domCg_param_typeRef getCg_param_type() const { return elemCg_param_type; } + /** + * Gets the param element. + * @return a daeSmartRef to the param element. + */ + const domParamRef getParam() const { return elemParam; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domBind(DAE& dae) : daeElement(dae), attrSymbol(), elemCg_param_type(), elemParam() {} + /** + * Destructor + */ + virtual ~domBind() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domBind &operator=( const domBind &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + protected: // Attribute +/** + * In which pipeline stage this programmable shader is designed to execute, + * for example, VERTEX, FRAGMENT, etc. + */ + domCg_pipeline_stage attrStage; + + protected: // Elements + domFx_annotate_common_Array elemAnnotate_array; + domCompiler_targetRef elemCompiler_target; +/** + * A string containing command-line operations for the shader compiler. @see + * domCompiler_options + */ + domCompiler_optionsRef elemCompiler_options; +/** + * The entry symbol for the shader function. @see domName + */ + domNameRef elemName; +/** + * Binds values to uniform inputs of a shader. @see domBind + */ + domBind_Array elemBind_array; + + public: //Accessors and Mutators + /** + * Gets the stage attribute. + * @return Returns a domCg_pipeline_stage of the stage attribute. + */ + domCg_pipeline_stage getStage() const { return attrStage; } + /** + * Sets the stage attribute. + * @param atStage The new value for the stage attribute. + */ + void setStage( domCg_pipeline_stage atStage ) { attrStage = atStage; _validAttributeArray[0] = true; } + + /** + * Gets the annotate element array. + * @return Returns a reference to the array of annotate elements. + */ + domFx_annotate_common_Array &getAnnotate_array() { return elemAnnotate_array; } + /** + * Gets the annotate element array. + * @return Returns a constant reference to the array of annotate elements. + */ + const domFx_annotate_common_Array &getAnnotate_array() const { return elemAnnotate_array; } + /** + * Gets the compiler_target element. + * @return a daeSmartRef to the compiler_target element. + */ + const domCompiler_targetRef getCompiler_target() const { return elemCompiler_target; } + /** + * Gets the compiler_options element. + * @return a daeSmartRef to the compiler_options element. + */ + const domCompiler_optionsRef getCompiler_options() const { return elemCompiler_options; } + /** + * Gets the name element. + * @return a daeSmartRef to the name element. + */ + const domNameRef getName() const { return elemName; } + /** + * Gets the bind element array. + * @return Returns a reference to the array of bind elements. + */ + domBind_Array &getBind_array() { return elemBind_array; } + /** + * Gets the bind element array. + * @return Returns a constant reference to the array of bind elements. + */ + const domBind_Array &getBind_array() const { return elemBind_array; } + protected: + /** + * Constructor + */ + domShader(DAE& dae) : daeElement(dae), attrStage(), elemAnnotate_array(), elemCompiler_target(), elemCompiler_options(), elemName(), elemBind_array() {} + /** + * Destructor + */ + virtual ~domShader() {} + /** + * Overloaded assignment operator + */ + virtual domShader &operator=( const domShader &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + protected: // Attribute +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; + + protected: // Elements + domFx_annotate_common_Array elemAnnotate_array; + domFx_colortarget_common_Array elemColor_target_array; + domFx_depthtarget_common_Array elemDepth_target_array; + domFx_stenciltarget_common_Array elemStencil_target_array; + domFx_clearcolor_common_Array elemColor_clear_array; + domFx_cleardepth_common_Array elemDepth_clear_array; + domFx_clearstencil_common_Array elemStencil_clear_array; + domDrawRef elemDraw; + domGl_pipeline_settings_Array elemGl_pipeline_settings_array; +/** + * Declare and prepare a shader for execution in the rendering pipeline of + * a pass. @see domShader + */ + domShader_Array elemShader_array; + domExtra_Array elemExtra_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + + /** + * Gets the annotate element array. + * @return Returns a reference to the array of annotate elements. + */ + domFx_annotate_common_Array &getAnnotate_array() { return elemAnnotate_array; } + /** + * Gets the annotate element array. + * @return Returns a constant reference to the array of annotate elements. + */ + const domFx_annotate_common_Array &getAnnotate_array() const { return elemAnnotate_array; } + /** + * Gets the color_target element array. + * @return Returns a reference to the array of color_target elements. + */ + domFx_colortarget_common_Array &getColor_target_array() { return elemColor_target_array; } + /** + * Gets the color_target element array. + * @return Returns a constant reference to the array of color_target elements. + */ + const domFx_colortarget_common_Array &getColor_target_array() const { return elemColor_target_array; } + /** + * Gets the depth_target element array. + * @return Returns a reference to the array of depth_target elements. + */ + domFx_depthtarget_common_Array &getDepth_target_array() { return elemDepth_target_array; } + /** + * Gets the depth_target element array. + * @return Returns a constant reference to the array of depth_target elements. + */ + const domFx_depthtarget_common_Array &getDepth_target_array() const { return elemDepth_target_array; } + /** + * Gets the stencil_target element array. + * @return Returns a reference to the array of stencil_target elements. + */ + domFx_stenciltarget_common_Array &getStencil_target_array() { return elemStencil_target_array; } + /** + * Gets the stencil_target element array. + * @return Returns a constant reference to the array of stencil_target elements. + */ + const domFx_stenciltarget_common_Array &getStencil_target_array() const { return elemStencil_target_array; } + /** + * Gets the color_clear element array. + * @return Returns a reference to the array of color_clear elements. + */ + domFx_clearcolor_common_Array &getColor_clear_array() { return elemColor_clear_array; } + /** + * Gets the color_clear element array. + * @return Returns a constant reference to the array of color_clear elements. + */ + const domFx_clearcolor_common_Array &getColor_clear_array() const { return elemColor_clear_array; } + /** + * Gets the depth_clear element array. + * @return Returns a reference to the array of depth_clear elements. + */ + domFx_cleardepth_common_Array &getDepth_clear_array() { return elemDepth_clear_array; } + /** + * Gets the depth_clear element array. + * @return Returns a constant reference to the array of depth_clear elements. + */ + const domFx_cleardepth_common_Array &getDepth_clear_array() const { return elemDepth_clear_array; } + /** + * Gets the stencil_clear element array. + * @return Returns a reference to the array of stencil_clear elements. + */ + domFx_clearstencil_common_Array &getStencil_clear_array() { return elemStencil_clear_array; } + /** + * Gets the stencil_clear element array. + * @return Returns a constant reference to the array of stencil_clear elements. + */ + const domFx_clearstencil_common_Array &getStencil_clear_array() const { return elemStencil_clear_array; } + /** + * Gets the draw element. + * @return a daeSmartRef to the draw element. + */ + const domDrawRef getDraw() const { return elemDraw; } + /** + * Gets the gl_pipeline_settings element array. + * @return Returns a reference to the array of gl_pipeline_settings elements. + */ + domGl_pipeline_settings_Array &getGl_pipeline_settings_array() { return elemGl_pipeline_settings_array; } + /** + * Gets the gl_pipeline_settings element array. + * @return Returns a constant reference to the array of gl_pipeline_settings elements. + */ + const domGl_pipeline_settings_Array &getGl_pipeline_settings_array() const { return elemGl_pipeline_settings_array; } + /** + * Gets the shader element array. + * @return Returns a reference to the array of shader elements. + */ + domShader_Array &getShader_array() { return elemShader_array; } + /** + * Gets the shader element array. + * @return Returns a constant reference to the array of shader elements. + */ + const domShader_Array &getShader_array() const { return elemShader_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domPass(DAE& dae) : daeElement(dae), attrSid(), elemAnnotate_array(), elemColor_target_array(), elemDepth_target_array(), elemStencil_target_array(), elemColor_clear_array(), elemDepth_clear_array(), elemStencil_clear_array(), elemDraw(), elemGl_pipeline_settings_array(), elemShader_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domPass() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domPass &operator=( const domPass &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; + + protected: // Elements +/** + * The technique element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; + domFx_annotate_common_Array elemAnnotate_array; + domFx_code_profile_Array elemCode_array; + domFx_include_common_Array elemInclude_array; + domImage_Array elemImage_array; + domCg_newparam_Array elemNewparam_array; + domCg_setparam_Array elemSetparam_array; +/** + * A static declaration of all the render states, shaders, and settings for + * one rendering pipeline. @see domPass + */ + domPass_Array elemPass_array; + domExtra_Array elemExtra_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the annotate element array. + * @return Returns a reference to the array of annotate elements. + */ + domFx_annotate_common_Array &getAnnotate_array() { return elemAnnotate_array; } + /** + * Gets the annotate element array. + * @return Returns a constant reference to the array of annotate elements. + */ + const domFx_annotate_common_Array &getAnnotate_array() const { return elemAnnotate_array; } + /** + * Gets the code element array. + * @return Returns a reference to the array of code elements. + */ + domFx_code_profile_Array &getCode_array() { return elemCode_array; } + /** + * Gets the code element array. + * @return Returns a constant reference to the array of code elements. + */ + const domFx_code_profile_Array &getCode_array() const { return elemCode_array; } + /** + * Gets the include element array. + * @return Returns a reference to the array of include elements. + */ + domFx_include_common_Array &getInclude_array() { return elemInclude_array; } + /** + * Gets the include element array. + * @return Returns a constant reference to the array of include elements. + */ + const domFx_include_common_Array &getInclude_array() const { return elemInclude_array; } + /** + * Gets the image element array. + * @return Returns a reference to the array of image elements. + */ + domImage_Array &getImage_array() { return elemImage_array; } + /** + * Gets the image element array. + * @return Returns a constant reference to the array of image elements. + */ + const domImage_Array &getImage_array() const { return elemImage_array; } + /** + * Gets the newparam element array. + * @return Returns a reference to the array of newparam elements. + */ + domCg_newparam_Array &getNewparam_array() { return elemNewparam_array; } + /** + * Gets the newparam element array. + * @return Returns a constant reference to the array of newparam elements. + */ + const domCg_newparam_Array &getNewparam_array() const { return elemNewparam_array; } + /** + * Gets the setparam element array. + * @return Returns a reference to the array of setparam elements. + */ + domCg_setparam_Array &getSetparam_array() { return elemSetparam_array; } + /** + * Gets the setparam element array. + * @return Returns a constant reference to the array of setparam elements. + */ + const domCg_setparam_Array &getSetparam_array() const { return elemSetparam_array; } + /** + * Gets the pass element array. + * @return Returns a reference to the array of pass elements. + */ + domPass_Array &getPass_array() { return elemPass_array; } + /** + * Gets the pass element array. + * @return Returns a constant reference to the array of pass elements. + */ + const domPass_Array &getPass_array() const { return elemPass_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domTechnique(DAE& dae) : daeElement(dae), attrId(), attrSid(), elemAsset(), elemAnnotate_array(), elemCode_array(), elemInclude_array(), elemImage_array(), elemNewparam_array(), elemSetparam_array(), elemPass_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domTechnique() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domTechnique &operator=( const domTechnique &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The type of platform. This is a vendor-defined character string that indicates + * the platform or capability target for the technique. Optional + */ + xsNCName attrPlatform; + +protected: // Elements + domAssetRef elemAsset; + domFx_code_profile_Array elemCode_array; + domFx_include_common_Array elemInclude_array; + domImage_Array elemImage_array; + domCg_newparam_Array elemNewparam_array; +/** + * Holds a description of the textures, samplers, shaders, parameters, and + * passes necessary for rendering this effect using one method. @see domTechnique + */ + domTechnique_Array elemTechnique_array; + domExtra_Array elemExtra_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the platform attribute. + * @return Returns a xsNCName of the platform attribute. + */ + xsNCName getPlatform() const { return attrPlatform; } + /** + * Sets the platform attribute. + * @param atPlatform The new value for the platform attribute. + */ + void setPlatform( xsNCName atPlatform ) { *(daeStringRef*)&attrPlatform = atPlatform; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the code element array. + * @return Returns a reference to the array of code elements. + */ + domFx_code_profile_Array &getCode_array() { return elemCode_array; } + /** + * Gets the code element array. + * @return Returns a constant reference to the array of code elements. + */ + const domFx_code_profile_Array &getCode_array() const { return elemCode_array; } + /** + * Gets the include element array. + * @return Returns a reference to the array of include elements. + */ + domFx_include_common_Array &getInclude_array() { return elemInclude_array; } + /** + * Gets the include element array. + * @return Returns a constant reference to the array of include elements. + */ + const domFx_include_common_Array &getInclude_array() const { return elemInclude_array; } + /** + * Gets the image element array. + * @return Returns a reference to the array of image elements. + */ + domImage_Array &getImage_array() { return elemImage_array; } + /** + * Gets the image element array. + * @return Returns a constant reference to the array of image elements. + */ + const domImage_Array &getImage_array() const { return elemImage_array; } + /** + * Gets the newparam element array. + * @return Returns a reference to the array of newparam elements. + */ + domCg_newparam_Array &getNewparam_array() { return elemNewparam_array; } + /** + * Gets the newparam element array. + * @return Returns a constant reference to the array of newparam elements. + */ + const domCg_newparam_Array &getNewparam_array() const { return elemNewparam_array; } + /** + * Gets the technique element array. + * @return Returns a reference to the array of technique elements. + */ + domTechnique_Array &getTechnique_array() { return elemTechnique_array; } + /** + * Gets the technique element array. + * @return Returns a constant reference to the array of technique elements. + */ + const domTechnique_Array &getTechnique_array() const { return elemTechnique_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domProfile_CG(DAE& dae) : domFx_profile_abstract(dae), attrId(), attrPlatform(), elemAsset(), elemCode_array(), elemInclude_array(), elemImage_array(), elemNewparam_array(), elemTechnique_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domProfile_CG() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domProfile_CG &operator=( const domProfile_CG &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domProfile_COMMON.h b/Engine/lib/collada/include/1.4/dom/domProfile_COMMON.h new file mode 100644 index 000000000..2cb40bf43 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domProfile_COMMON.h @@ -0,0 +1,728 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domProfile_COMMON_h__ +#define __domProfile_COMMON_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +class DAE; + +/** + * Opens a block of COMMON platform-specific data types and technique declarations. + */ +class domProfile_COMMON : public domFx_profile_abstract +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PROFILE_COMMON; } + static daeInt ID() { return 740; } + virtual daeInt typeID() const { return ID(); } +public: + class domTechnique; + + typedef daeSmartRef domTechniqueRef; + typedef daeTArray domTechnique_Array; + +/** + * Holds a description of the textures, samplers, shaders, parameters, and + * passes necessary for rendering this effect using one method. + */ + class domTechnique : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TECHNIQUE; } + static daeInt ID() { return 741; } + virtual daeInt typeID() const { return ID(); } + public: + class domConstant; + + typedef daeSmartRef domConstantRef; + typedef daeTArray domConstant_Array; + + class domConstant : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CONSTANT; } + static daeInt ID() { return 742; } + virtual daeInt typeID() const { return ID(); } + + protected: // Elements + domCommon_color_or_texture_typeRef elemEmission; + domCommon_color_or_texture_typeRef elemReflective; + domCommon_float_or_param_typeRef elemReflectivity; + domCommon_transparent_typeRef elemTransparent; + domCommon_float_or_param_typeRef elemTransparency; + domCommon_float_or_param_typeRef elemIndex_of_refraction; + + public: //Accessors and Mutators + /** + * Gets the emission element. + * @return a daeSmartRef to the emission element. + */ + const domCommon_color_or_texture_typeRef getEmission() const { return elemEmission; } + /** + * Gets the reflective element. + * @return a daeSmartRef to the reflective element. + */ + const domCommon_color_or_texture_typeRef getReflective() const { return elemReflective; } + /** + * Gets the reflectivity element. + * @return a daeSmartRef to the reflectivity element. + */ + const domCommon_float_or_param_typeRef getReflectivity() const { return elemReflectivity; } + /** + * Gets the transparent element. + * @return a daeSmartRef to the transparent element. + */ + const domCommon_transparent_typeRef getTransparent() const { return elemTransparent; } + /** + * Gets the transparency element. + * @return a daeSmartRef to the transparency element. + */ + const domCommon_float_or_param_typeRef getTransparency() const { return elemTransparency; } + /** + * Gets the index_of_refraction element. + * @return a daeSmartRef to the index_of_refraction element. + */ + const domCommon_float_or_param_typeRef getIndex_of_refraction() const { return elemIndex_of_refraction; } + protected: + /** + * Constructor + */ + domConstant(DAE& dae) : daeElement(dae), elemEmission(), elemReflective(), elemReflectivity(), elemTransparent(), elemTransparency(), elemIndex_of_refraction() {} + /** + * Destructor + */ + virtual ~domConstant() {} + /** + * Overloaded assignment operator + */ + virtual domConstant &operator=( const domConstant &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLambert; + + typedef daeSmartRef domLambertRef; + typedef daeTArray domLambert_Array; + + class domLambert : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LAMBERT; } + static daeInt ID() { return 743; } + virtual daeInt typeID() const { return ID(); } + + protected: // Elements + domCommon_color_or_texture_typeRef elemEmission; + domCommon_color_or_texture_typeRef elemAmbient; + domCommon_color_or_texture_typeRef elemDiffuse; + domCommon_color_or_texture_typeRef elemReflective; + domCommon_float_or_param_typeRef elemReflectivity; + domCommon_transparent_typeRef elemTransparent; + domCommon_float_or_param_typeRef elemTransparency; + domCommon_float_or_param_typeRef elemIndex_of_refraction; + + public: //Accessors and Mutators + /** + * Gets the emission element. + * @return a daeSmartRef to the emission element. + */ + const domCommon_color_or_texture_typeRef getEmission() const { return elemEmission; } + /** + * Gets the ambient element. + * @return a daeSmartRef to the ambient element. + */ + const domCommon_color_or_texture_typeRef getAmbient() const { return elemAmbient; } + /** + * Gets the diffuse element. + * @return a daeSmartRef to the diffuse element. + */ + const domCommon_color_or_texture_typeRef getDiffuse() const { return elemDiffuse; } + /** + * Gets the reflective element. + * @return a daeSmartRef to the reflective element. + */ + const domCommon_color_or_texture_typeRef getReflective() const { return elemReflective; } + /** + * Gets the reflectivity element. + * @return a daeSmartRef to the reflectivity element. + */ + const domCommon_float_or_param_typeRef getReflectivity() const { return elemReflectivity; } + /** + * Gets the transparent element. + * @return a daeSmartRef to the transparent element. + */ + const domCommon_transparent_typeRef getTransparent() const { return elemTransparent; } + /** + * Gets the transparency element. + * @return a daeSmartRef to the transparency element. + */ + const domCommon_float_or_param_typeRef getTransparency() const { return elemTransparency; } + /** + * Gets the index_of_refraction element. + * @return a daeSmartRef to the index_of_refraction element. + */ + const domCommon_float_or_param_typeRef getIndex_of_refraction() const { return elemIndex_of_refraction; } + protected: + /** + * Constructor + */ + domLambert(DAE& dae) : daeElement(dae), elemEmission(), elemAmbient(), elemDiffuse(), elemReflective(), elemReflectivity(), elemTransparent(), elemTransparency(), elemIndex_of_refraction() {} + /** + * Destructor + */ + virtual ~domLambert() {} + /** + * Overloaded assignment operator + */ + virtual domLambert &operator=( const domLambert &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPhong; + + typedef daeSmartRef domPhongRef; + typedef daeTArray domPhong_Array; + + class domPhong : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PHONG; } + static daeInt ID() { return 744; } + virtual daeInt typeID() const { return ID(); } + + protected: // Elements + domCommon_color_or_texture_typeRef elemEmission; + domCommon_color_or_texture_typeRef elemAmbient; + domCommon_color_or_texture_typeRef elemDiffuse; + domCommon_color_or_texture_typeRef elemSpecular; + domCommon_float_or_param_typeRef elemShininess; + domCommon_color_or_texture_typeRef elemReflective; + domCommon_float_or_param_typeRef elemReflectivity; + domCommon_transparent_typeRef elemTransparent; + domCommon_float_or_param_typeRef elemTransparency; + domCommon_float_or_param_typeRef elemIndex_of_refraction; + + public: //Accessors and Mutators + /** + * Gets the emission element. + * @return a daeSmartRef to the emission element. + */ + const domCommon_color_or_texture_typeRef getEmission() const { return elemEmission; } + /** + * Gets the ambient element. + * @return a daeSmartRef to the ambient element. + */ + const domCommon_color_or_texture_typeRef getAmbient() const { return elemAmbient; } + /** + * Gets the diffuse element. + * @return a daeSmartRef to the diffuse element. + */ + const domCommon_color_or_texture_typeRef getDiffuse() const { return elemDiffuse; } + /** + * Gets the specular element. + * @return a daeSmartRef to the specular element. + */ + const domCommon_color_or_texture_typeRef getSpecular() const { return elemSpecular; } + /** + * Gets the shininess element. + * @return a daeSmartRef to the shininess element. + */ + const domCommon_float_or_param_typeRef getShininess() const { return elemShininess; } + /** + * Gets the reflective element. + * @return a daeSmartRef to the reflective element. + */ + const domCommon_color_or_texture_typeRef getReflective() const { return elemReflective; } + /** + * Gets the reflectivity element. + * @return a daeSmartRef to the reflectivity element. + */ + const domCommon_float_or_param_typeRef getReflectivity() const { return elemReflectivity; } + /** + * Gets the transparent element. + * @return a daeSmartRef to the transparent element. + */ + const domCommon_transparent_typeRef getTransparent() const { return elemTransparent; } + /** + * Gets the transparency element. + * @return a daeSmartRef to the transparency element. + */ + const domCommon_float_or_param_typeRef getTransparency() const { return elemTransparency; } + /** + * Gets the index_of_refraction element. + * @return a daeSmartRef to the index_of_refraction element. + */ + const domCommon_float_or_param_typeRef getIndex_of_refraction() const { return elemIndex_of_refraction; } + protected: + /** + * Constructor + */ + domPhong(DAE& dae) : daeElement(dae), elemEmission(), elemAmbient(), elemDiffuse(), elemSpecular(), elemShininess(), elemReflective(), elemReflectivity(), elemTransparent(), elemTransparency(), elemIndex_of_refraction() {} + /** + * Destructor + */ + virtual ~domPhong() {} + /** + * Overloaded assignment operator + */ + virtual domPhong &operator=( const domPhong &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBlinn; + + typedef daeSmartRef domBlinnRef; + typedef daeTArray domBlinn_Array; + + class domBlinn : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BLINN; } + static daeInt ID() { return 745; } + virtual daeInt typeID() const { return ID(); } + + protected: // Elements + domCommon_color_or_texture_typeRef elemEmission; + domCommon_color_or_texture_typeRef elemAmbient; + domCommon_color_or_texture_typeRef elemDiffuse; + domCommon_color_or_texture_typeRef elemSpecular; + domCommon_float_or_param_typeRef elemShininess; + domCommon_color_or_texture_typeRef elemReflective; + domCommon_float_or_param_typeRef elemReflectivity; + domCommon_transparent_typeRef elemTransparent; + domCommon_float_or_param_typeRef elemTransparency; + domCommon_float_or_param_typeRef elemIndex_of_refraction; + + public: //Accessors and Mutators + /** + * Gets the emission element. + * @return a daeSmartRef to the emission element. + */ + const domCommon_color_or_texture_typeRef getEmission() const { return elemEmission; } + /** + * Gets the ambient element. + * @return a daeSmartRef to the ambient element. + */ + const domCommon_color_or_texture_typeRef getAmbient() const { return elemAmbient; } + /** + * Gets the diffuse element. + * @return a daeSmartRef to the diffuse element. + */ + const domCommon_color_or_texture_typeRef getDiffuse() const { return elemDiffuse; } + /** + * Gets the specular element. + * @return a daeSmartRef to the specular element. + */ + const domCommon_color_or_texture_typeRef getSpecular() const { return elemSpecular; } + /** + * Gets the shininess element. + * @return a daeSmartRef to the shininess element. + */ + const domCommon_float_or_param_typeRef getShininess() const { return elemShininess; } + /** + * Gets the reflective element. + * @return a daeSmartRef to the reflective element. + */ + const domCommon_color_or_texture_typeRef getReflective() const { return elemReflective; } + /** + * Gets the reflectivity element. + * @return a daeSmartRef to the reflectivity element. + */ + const domCommon_float_or_param_typeRef getReflectivity() const { return elemReflectivity; } + /** + * Gets the transparent element. + * @return a daeSmartRef to the transparent element. + */ + const domCommon_transparent_typeRef getTransparent() const { return elemTransparent; } + /** + * Gets the transparency element. + * @return a daeSmartRef to the transparency element. + */ + const domCommon_float_or_param_typeRef getTransparency() const { return elemTransparency; } + /** + * Gets the index_of_refraction element. + * @return a daeSmartRef to the index_of_refraction element. + */ + const domCommon_float_or_param_typeRef getIndex_of_refraction() const { return elemIndex_of_refraction; } + protected: + /** + * Constructor + */ + domBlinn(DAE& dae) : daeElement(dae), elemEmission(), elemAmbient(), elemDiffuse(), elemSpecular(), elemShininess(), elemReflective(), elemReflectivity(), elemTransparent(), elemTransparency(), elemIndex_of_refraction() {} + /** + * Destructor + */ + virtual ~domBlinn() {} + /** + * Overloaded assignment operator + */ + virtual domBlinn &operator=( const domBlinn &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; + + protected: // Elements +/** + * The technique element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; + domImage_Array elemImage_array; + domCommon_newparam_type_Array elemNewparam_array; + domConstantRef elemConstant; + domLambertRef elemLambert; + domPhongRef elemPhong; + domBlinnRef elemBlinn; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the image element array. + * @return Returns a reference to the array of image elements. + */ + domImage_Array &getImage_array() { return elemImage_array; } + /** + * Gets the image element array. + * @return Returns a constant reference to the array of image elements. + */ + const domImage_Array &getImage_array() const { return elemImage_array; } + /** + * Gets the newparam element array. + * @return Returns a reference to the array of newparam elements. + */ + domCommon_newparam_type_Array &getNewparam_array() { return elemNewparam_array; } + /** + * Gets the newparam element array. + * @return Returns a constant reference to the array of newparam elements. + */ + const domCommon_newparam_type_Array &getNewparam_array() const { return elemNewparam_array; } + /** + * Gets the constant element. + * @return a daeSmartRef to the constant element. + */ + const domConstantRef getConstant() const { return elemConstant; } + /** + * Gets the lambert element. + * @return a daeSmartRef to the lambert element. + */ + const domLambertRef getLambert() const { return elemLambert; } + /** + * Gets the phong element. + * @return a daeSmartRef to the phong element. + */ + const domPhongRef getPhong() const { return elemPhong; } + /** + * Gets the blinn element. + * @return a daeSmartRef to the blinn element. + */ + const domBlinnRef getBlinn() const { return elemBlinn; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domTechnique(DAE& dae) : daeElement(dae), attrId(), attrSid(), elemAsset(), elemImage_array(), elemNewparam_array(), elemConstant(), elemLambert(), elemPhong(), elemBlinn(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domTechnique() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domTechnique &operator=( const domTechnique &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attribute +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; + +protected: // Elements + domAssetRef elemAsset; + domImage_Array elemImage_array; + domCommon_newparam_type_Array elemNewparam_array; +/** + * Holds a description of the textures, samplers, shaders, parameters, and + * passes necessary for rendering this effect using one method. @see domTechnique + */ + domTechniqueRef elemTechnique; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the image element array. + * @return Returns a reference to the array of image elements. + */ + domImage_Array &getImage_array() { return elemImage_array; } + /** + * Gets the image element array. + * @return Returns a constant reference to the array of image elements. + */ + const domImage_Array &getImage_array() const { return elemImage_array; } + /** + * Gets the newparam element array. + * @return Returns a reference to the array of newparam elements. + */ + domCommon_newparam_type_Array &getNewparam_array() { return elemNewparam_array; } + /** + * Gets the newparam element array. + * @return Returns a constant reference to the array of newparam elements. + */ + const domCommon_newparam_type_Array &getNewparam_array() const { return elemNewparam_array; } + /** + * Gets the technique element. + * @return a daeSmartRef to the technique element. + */ + const domTechniqueRef getTechnique() const { return elemTechnique; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domProfile_COMMON(DAE& dae) : domFx_profile_abstract(dae), attrId(), elemAsset(), elemImage_array(), elemNewparam_array(), elemTechnique(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domProfile_COMMON() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domProfile_COMMON &operator=( const domProfile_COMMON &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domProfile_GLES.h b/Engine/lib/collada/include/1.4/dom/domProfile_GLES.h new file mode 100644 index 000000000..18f5483af --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domProfile_GLES.h @@ -0,0 +1,1023 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domProfile_GLES_h__ +#define __domProfile_GLES_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +class DAE; + +/** + * Opens a block of GLES platform-specific data types and technique declarations. + */ +class domProfile_GLES : public domFx_profile_abstract +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PROFILE_GLES; } + static daeInt ID() { return 756; } + virtual daeInt typeID() const { return ID(); } +public: + class domTechnique; + + typedef daeSmartRef domTechniqueRef; + typedef daeTArray domTechnique_Array; + +/** + * Holds a description of the textures, samplers, shaders, parameters, and + * passes necessary for rendering this effect using one method. + */ + class domTechnique : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TECHNIQUE; } + static daeInt ID() { return 757; } + virtual daeInt typeID() const { return ID(); } + public: + class domSetparam; + + typedef daeSmartRef domSetparamRef; + typedef daeTArray domSetparam_Array; + + class domSetparam : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SETPARAM; } + static daeInt ID() { return 758; } + virtual daeInt typeID() const { return ID(); } + protected: // Attribute + xsNCName attrRef; + + protected: // Elements + domFx_annotate_common_Array elemAnnotate_array; + domGles_basic_type_commonRef elemGles_basic_type_common; + + public: //Accessors and Mutators + /** + * Gets the ref attribute. + * @return Returns a xsNCName of the ref attribute. + */ + xsNCName getRef() const { return attrRef; } + /** + * Sets the ref attribute. + * @param atRef The new value for the ref attribute. + */ + void setRef( xsNCName atRef ) { *(daeStringRef*)&attrRef = atRef; _validAttributeArray[0] = true; } + + /** + * Gets the annotate element array. + * @return Returns a reference to the array of annotate elements. + */ + domFx_annotate_common_Array &getAnnotate_array() { return elemAnnotate_array; } + /** + * Gets the annotate element array. + * @return Returns a constant reference to the array of annotate elements. + */ + const domFx_annotate_common_Array &getAnnotate_array() const { return elemAnnotate_array; } + /** + * Gets the gles_basic_type_common element. + * @return a daeSmartRef to the gles_basic_type_common element. + */ + const domGles_basic_type_commonRef getGles_basic_type_common() const { return elemGles_basic_type_common; } + protected: + /** + * Constructor + */ + domSetparam(DAE& dae) : daeElement(dae), attrRef(), elemAnnotate_array(), elemGles_basic_type_common() {} + /** + * Destructor + */ + virtual ~domSetparam() {} + /** + * Overloaded assignment operator + */ + virtual domSetparam &operator=( const domSetparam &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domPass; + + typedef daeSmartRef domPassRef; + typedef daeTArray domPass_Array; + +/** + * A static declaration of all the render states, shaders, and settings for + * one rendering pipeline. + */ + class domPass : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PASS; } + static daeInt ID() { return 759; } + virtual daeInt typeID() const { return ID(); } + public: + class domColor_target; + + typedef daeSmartRef domColor_targetRef; + typedef daeTArray domColor_target_Array; + + class domColor_target : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::COLOR_TARGET; } + static daeInt ID() { return 760; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domGles_rendertarget_common value of the text data of this element. + */ + domGles_rendertarget_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domGles_rendertarget_common of the value. + */ + domGles_rendertarget_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domGles_rendertarget_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domColor_target(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domColor_target() {} + /** + * Overloaded assignment operator + */ + virtual domColor_target &operator=( const domColor_target &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domDepth_target; + + typedef daeSmartRef domDepth_targetRef; + typedef daeTArray domDepth_target_Array; + + class domDepth_target : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::DEPTH_TARGET; } + static daeInt ID() { return 761; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domGles_rendertarget_common value of the text data of this element. + */ + domGles_rendertarget_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domGles_rendertarget_common of the value. + */ + domGles_rendertarget_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domGles_rendertarget_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domDepth_target(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domDepth_target() {} + /** + * Overloaded assignment operator + */ + virtual domDepth_target &operator=( const domDepth_target &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domStencil_target; + + typedef daeSmartRef domStencil_targetRef; + typedef daeTArray domStencil_target_Array; + + class domStencil_target : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::STENCIL_TARGET; } + static daeInt ID() { return 762; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domGles_rendertarget_common value of the text data of this element. + */ + domGles_rendertarget_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domGles_rendertarget_common of the value. + */ + domGles_rendertarget_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domGles_rendertarget_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domStencil_target(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domStencil_target() {} + /** + * Overloaded assignment operator + */ + virtual domStencil_target &operator=( const domStencil_target &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domColor_clear; + + typedef daeSmartRef domColor_clearRef; + typedef daeTArray domColor_clear_Array; + + class domColor_clear : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::COLOR_CLEAR; } + static daeInt ID() { return 763; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_color_common value of the text data of this element. + */ + domFx_color_common _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domFx_color_common reference of the _value array. + */ + domFx_color_common &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domFx_color_common reference of the _value array. + */ + const domFx_color_common &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domFx_color_common &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domColor_clear(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domColor_clear() {} + /** + * Overloaded assignment operator + */ + virtual domColor_clear &operator=( const domColor_clear &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domDepth_clear; + + typedef daeSmartRef domDepth_clearRef; + typedef daeTArray domDepth_clear_Array; + + class domDepth_clear : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::DEPTH_CLEAR; } + static daeInt ID() { return 764; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFloat value of the text data of this element. + */ + domFloat _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFloat of the value. + */ + domFloat getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFloat val ) { _value = val; } + + protected: + /** + * Constructor + */ + domDepth_clear(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domDepth_clear() {} + /** + * Overloaded assignment operator + */ + virtual domDepth_clear &operator=( const domDepth_clear &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domStencil_clear; + + typedef daeSmartRef domStencil_clearRef; + typedef daeTArray domStencil_clear_Array; + + class domStencil_clear : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::STENCIL_CLEAR; } + static daeInt ID() { return 765; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsByte value of the text data of this element. + */ + xsByte _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a xsByte of the value. + */ + xsByte getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsByte val ) { _value = val; } + + protected: + /** + * Constructor + */ + domStencil_clear(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domStencil_clear() {} + /** + * Overloaded assignment operator + */ + virtual domStencil_clear &operator=( const domStencil_clear &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domDraw; + + typedef daeSmartRef domDrawRef; + typedef daeTArray domDraw_Array; + + class domDraw : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::DRAW; } + static daeInt ID() { return 766; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_draw_common value of the text data of this element. + */ + domFx_draw_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_draw_common of the value. + */ + domFx_draw_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_draw_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domDraw(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domDraw() {} + /** + * Overloaded assignment operator + */ + virtual domDraw &operator=( const domDraw &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + protected: // Attribute +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; + + protected: // Elements + domFx_annotate_common_Array elemAnnotate_array; + domColor_targetRef elemColor_target; + domDepth_targetRef elemDepth_target; + domStencil_targetRef elemStencil_target; + domColor_clearRef elemColor_clear; + domDepth_clearRef elemDepth_clear; + domStencil_clearRef elemStencil_clear; + domDrawRef elemDraw; + domGles_pipeline_settings_Array elemGles_pipeline_settings_array; + domExtra_Array elemExtra_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + + /** + * Gets the annotate element array. + * @return Returns a reference to the array of annotate elements. + */ + domFx_annotate_common_Array &getAnnotate_array() { return elemAnnotate_array; } + /** + * Gets the annotate element array. + * @return Returns a constant reference to the array of annotate elements. + */ + const domFx_annotate_common_Array &getAnnotate_array() const { return elemAnnotate_array; } + /** + * Gets the color_target element. + * @return a daeSmartRef to the color_target element. + */ + const domColor_targetRef getColor_target() const { return elemColor_target; } + /** + * Gets the depth_target element. + * @return a daeSmartRef to the depth_target element. + */ + const domDepth_targetRef getDepth_target() const { return elemDepth_target; } + /** + * Gets the stencil_target element. + * @return a daeSmartRef to the stencil_target element. + */ + const domStencil_targetRef getStencil_target() const { return elemStencil_target; } + /** + * Gets the color_clear element. + * @return a daeSmartRef to the color_clear element. + */ + const domColor_clearRef getColor_clear() const { return elemColor_clear; } + /** + * Gets the depth_clear element. + * @return a daeSmartRef to the depth_clear element. + */ + const domDepth_clearRef getDepth_clear() const { return elemDepth_clear; } + /** + * Gets the stencil_clear element. + * @return a daeSmartRef to the stencil_clear element. + */ + const domStencil_clearRef getStencil_clear() const { return elemStencil_clear; } + /** + * Gets the draw element. + * @return a daeSmartRef to the draw element. + */ + const domDrawRef getDraw() const { return elemDraw; } + /** + * Gets the gles_pipeline_settings element array. + * @return Returns a reference to the array of gles_pipeline_settings elements. + */ + domGles_pipeline_settings_Array &getGles_pipeline_settings_array() { return elemGles_pipeline_settings_array; } + /** + * Gets the gles_pipeline_settings element array. + * @return Returns a constant reference to the array of gles_pipeline_settings elements. + */ + const domGles_pipeline_settings_Array &getGles_pipeline_settings_array() const { return elemGles_pipeline_settings_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domPass(DAE& dae) : daeElement(dae), attrSid(), elemAnnotate_array(), elemColor_target(), elemDepth_target(), elemStencil_target(), elemColor_clear(), elemDepth_clear(), elemStencil_clear(), elemDraw(), elemGles_pipeline_settings_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domPass() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domPass &operator=( const domPass &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + protected: // Attributes + xsID attrId; +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. + */ + xsNCName attrSid; + + protected: // Elements + domAssetRef elemAsset; + domFx_annotate_common_Array elemAnnotate_array; + domImage_Array elemImage_array; + domGles_newparam_Array elemNewparam_array; + domSetparam_Array elemSetparam_array; +/** + * A static declaration of all the render states, shaders, and settings for + * one rendering pipeline. @see domPass + */ + domPass_Array elemPass_array; + domExtra_Array elemExtra_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the annotate element array. + * @return Returns a reference to the array of annotate elements. + */ + domFx_annotate_common_Array &getAnnotate_array() { return elemAnnotate_array; } + /** + * Gets the annotate element array. + * @return Returns a constant reference to the array of annotate elements. + */ + const domFx_annotate_common_Array &getAnnotate_array() const { return elemAnnotate_array; } + /** + * Gets the image element array. + * @return Returns a reference to the array of image elements. + */ + domImage_Array &getImage_array() { return elemImage_array; } + /** + * Gets the image element array. + * @return Returns a constant reference to the array of image elements. + */ + const domImage_Array &getImage_array() const { return elemImage_array; } + /** + * Gets the newparam element array. + * @return Returns a reference to the array of newparam elements. + */ + domGles_newparam_Array &getNewparam_array() { return elemNewparam_array; } + /** + * Gets the newparam element array. + * @return Returns a constant reference to the array of newparam elements. + */ + const domGles_newparam_Array &getNewparam_array() const { return elemNewparam_array; } + /** + * Gets the setparam element array. + * @return Returns a reference to the array of setparam elements. + */ + domSetparam_Array &getSetparam_array() { return elemSetparam_array; } + /** + * Gets the setparam element array. + * @return Returns a constant reference to the array of setparam elements. + */ + const domSetparam_Array &getSetparam_array() const { return elemSetparam_array; } + /** + * Gets the pass element array. + * @return Returns a reference to the array of pass elements. + */ + domPass_Array &getPass_array() { return elemPass_array; } + /** + * Gets the pass element array. + * @return Returns a constant reference to the array of pass elements. + */ + const domPass_Array &getPass_array() const { return elemPass_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domTechnique(DAE& dae) : daeElement(dae), attrId(), attrSid(), elemAsset(), elemAnnotate_array(), elemImage_array(), elemNewparam_array(), elemSetparam_array(), elemPass_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domTechnique() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domTechnique &operator=( const domTechnique &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The type of platform. This is a vendor-defined character string that indicates + * the platform or capability target for the technique. Optional + */ + xsNCName attrPlatform; + +protected: // Elements + domAssetRef elemAsset; + domImage_Array elemImage_array; + domGles_newparam_Array elemNewparam_array; +/** + * Holds a description of the textures, samplers, shaders, parameters, and + * passes necessary for rendering this effect using one method. @see domTechnique + */ + domTechnique_Array elemTechnique_array; + domExtra_Array elemExtra_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the platform attribute. + * @return Returns a xsNCName of the platform attribute. + */ + xsNCName getPlatform() const { return attrPlatform; } + /** + * Sets the platform attribute. + * @param atPlatform The new value for the platform attribute. + */ + void setPlatform( xsNCName atPlatform ) { *(daeStringRef*)&attrPlatform = atPlatform; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the image element array. + * @return Returns a reference to the array of image elements. + */ + domImage_Array &getImage_array() { return elemImage_array; } + /** + * Gets the image element array. + * @return Returns a constant reference to the array of image elements. + */ + const domImage_Array &getImage_array() const { return elemImage_array; } + /** + * Gets the newparam element array. + * @return Returns a reference to the array of newparam elements. + */ + domGles_newparam_Array &getNewparam_array() { return elemNewparam_array; } + /** + * Gets the newparam element array. + * @return Returns a constant reference to the array of newparam elements. + */ + const domGles_newparam_Array &getNewparam_array() const { return elemNewparam_array; } + /** + * Gets the technique element array. + * @return Returns a reference to the array of technique elements. + */ + domTechnique_Array &getTechnique_array() { return elemTechnique_array; } + /** + * Gets the technique element array. + * @return Returns a constant reference to the array of technique elements. + */ + const domTechnique_Array &getTechnique_array() const { return elemTechnique_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domProfile_GLES(DAE& dae) : domFx_profile_abstract(dae), attrId(), attrPlatform(), elemAsset(), elemImage_array(), elemNewparam_array(), elemTechnique_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domProfile_GLES() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domProfile_GLES &operator=( const domProfile_GLES &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domProfile_GLSL.h b/Engine/lib/collada/include/1.4/dom/domProfile_GLSL.h new file mode 100644 index 000000000..d00c6c6a1 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domProfile_GLSL.h @@ -0,0 +1,1152 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domProfile_GLSL_h__ +#define __domProfile_GLSL_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +class DAE; + +/** + * Opens a block of GLSL platform-specific data types and technique declarations. + */ +class domProfile_GLSL : public domFx_profile_abstract +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PROFILE_GLSL; } + static daeInt ID() { return 730; } + virtual daeInt typeID() const { return ID(); } +public: + class domTechnique; + + typedef daeSmartRef domTechniqueRef; + typedef daeTArray domTechnique_Array; + +/** + * Holds a description of the textures, samplers, shaders, parameters, and + * passes necessary for rendering this effect using one method. + */ + class domTechnique : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TECHNIQUE; } + static daeInt ID() { return 731; } + virtual daeInt typeID() const { return ID(); } + public: + class domPass; + + typedef daeSmartRef domPassRef; + typedef daeTArray domPass_Array; + +/** + * A static declaration of all the render states, shaders, and settings for + * one rendering pipeline. + */ + class domPass : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PASS; } + static daeInt ID() { return 732; } + virtual daeInt typeID() const { return ID(); } + public: + class domDraw; + + typedef daeSmartRef domDrawRef; + typedef daeTArray domDraw_Array; + + class domDraw : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::DRAW; } + static daeInt ID() { return 733; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFx_draw_common value of the text data of this element. + */ + domFx_draw_common _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFx_draw_common of the value. + */ + domFx_draw_common getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFx_draw_common val ) { _value = val; } + + protected: + /** + * Constructor + */ + domDraw(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domDraw() {} + /** + * Overloaded assignment operator + */ + virtual domDraw &operator=( const domDraw &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domShader; + + typedef daeSmartRef domShaderRef; + typedef daeTArray domShader_Array; + +/** + * Declare and prepare a shader for execution in the rendering pipeline of + * a pass. + */ + class domShader : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SHADER; } + static daeInt ID() { return 734; } + virtual daeInt typeID() const { return ID(); } + public: + class domCompiler_target; + + typedef daeSmartRef domCompiler_targetRef; + typedef daeTArray domCompiler_target_Array; + +/** + * A string declaring which profile or platform the compiler is targeting + * this shader for. + */ + class domCompiler_target : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::COMPILER_TARGET; } + static daeInt ID() { return 735; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsNMTOKEN value of the text data of this element. + */ + xsNMTOKEN _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsNMTOKEN of the value. + */ + xsNMTOKEN getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNMTOKEN val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domCompiler_target(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domCompiler_target() {} + /** + * Overloaded assignment operator + */ + virtual domCompiler_target &operator=( const domCompiler_target &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domCompiler_options; + + typedef daeSmartRef domCompiler_optionsRef; + typedef daeTArray domCompiler_options_Array; + +/** + * A string containing command-line operations for the shader compiler. + */ + class domCompiler_options : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::COMPILER_OPTIONS; } + static daeInt ID() { return 736; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsString value of the text data of this element. + */ + xsString _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsString of the value. + */ + xsString getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsString val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domCompiler_options(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domCompiler_options() {} + /** + * Overloaded assignment operator + */ + virtual domCompiler_options &operator=( const domCompiler_options &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domName; + + typedef daeSmartRef domNameRef; + typedef daeTArray domName_Array; + +/** + * The entry symbol for the shader function. + */ + class domName : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::NAME; } + static daeInt ID() { return 737; } + virtual daeInt typeID() const { return ID(); } + protected: // Attribute + xsNCName attrSource; + + protected: // Value + /** + * The xsNCName value of the text data of this element. + */ + xsNCName _value; + + public: //Accessors and Mutators + /** + * Gets the source attribute. + * @return Returns a xsNCName of the source attribute. + */ + xsNCName getSource() const { return attrSource; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( xsNCName atSource ) { *(daeStringRef*)&attrSource = atSource; _validAttributeArray[0] = true; } + + /** + * Gets the value of this element. + * @return Returns a xsNCName of the value. + */ + xsNCName getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNCName val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domName(DAE& dae) : daeElement(dae), attrSource(), _value() {} + /** + * Destructor + */ + virtual ~domName() {} + /** + * Overloaded assignment operator + */ + virtual domName &operator=( const domName &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domBind; + + typedef daeSmartRef domBindRef; + typedef daeTArray domBind_Array; + +/** + * Binds values to uniform inputs of a shader. + */ + class domBind : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BIND; } + static daeInt ID() { return 738; } + virtual daeInt typeID() const { return ID(); } + public: + class domParam; + + typedef daeSmartRef domParamRef; + typedef daeTArray domParam_Array; + + class domParam : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::PARAM; } + static daeInt ID() { return 739; } + virtual daeInt typeID() const { return ID(); } + protected: // Attribute + xsString attrRef; + + + public: //Accessors and Mutators + /** + * Gets the ref attribute. + * @return Returns a xsString of the ref attribute. + */ + xsString getRef() const { return attrRef; } + /** + * Sets the ref attribute. + * @param atRef The new value for the ref attribute. + */ + void setRef( xsString atRef ) { *(daeStringRef*)&attrRef = atRef; _validAttributeArray[0] = true; } + + protected: + /** + * Constructor + */ + domParam(DAE& dae) : daeElement(dae), attrRef() {} + /** + * Destructor + */ + virtual ~domParam() {} + /** + * Overloaded assignment operator + */ + virtual domParam &operator=( const domParam &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + protected: // Attribute +/** + * The identifier for a uniform input parameter to the shader (a formal function + * parameter or in-scope global) that will be bound to an external resource. + */ + xsNCName attrSymbol; + + protected: // Elements + domGlsl_param_typeRef elemGlsl_param_type; + domParamRef elemParam; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the symbol attribute. + * @return Returns a xsNCName of the symbol attribute. + */ + xsNCName getSymbol() const { return attrSymbol; } + /** + * Sets the symbol attribute. + * @param atSymbol The new value for the symbol attribute. + */ + void setSymbol( xsNCName atSymbol ) { *(daeStringRef*)&attrSymbol = atSymbol; _validAttributeArray[0] = true; } + + /** + * Gets the glsl_param_type element. + * @return a daeSmartRef to the glsl_param_type element. + */ + const domGlsl_param_typeRef getGlsl_param_type() const { return elemGlsl_param_type; } + /** + * Gets the param element. + * @return a daeSmartRef to the param element. + */ + const domParamRef getParam() const { return elemParam; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domBind(DAE& dae) : daeElement(dae), attrSymbol(), elemGlsl_param_type(), elemParam() {} + /** + * Destructor + */ + virtual ~domBind() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domBind &operator=( const domBind &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + protected: // Attribute +/** + * In which pipeline stage this programmable shader is designed to execute, + * for example, VERTEX, FRAGMENT, etc. + */ + domGlsl_pipeline_stage attrStage; + + protected: // Elements + domFx_annotate_common_Array elemAnnotate_array; +/** + * A string declaring which profile or platform the compiler is targeting + * this shader for. @see domCompiler_target + */ + domCompiler_targetRef elemCompiler_target; +/** + * A string containing command-line operations for the shader compiler. @see + * domCompiler_options + */ + domCompiler_optionsRef elemCompiler_options; +/** + * The entry symbol for the shader function. @see domName + */ + domNameRef elemName; +/** + * Binds values to uniform inputs of a shader. @see domBind + */ + domBind_Array elemBind_array; + + public: //Accessors and Mutators + /** + * Gets the stage attribute. + * @return Returns a domGlsl_pipeline_stage of the stage attribute. + */ + domGlsl_pipeline_stage getStage() const { return attrStage; } + /** + * Sets the stage attribute. + * @param atStage The new value for the stage attribute. + */ + void setStage( domGlsl_pipeline_stage atStage ) { attrStage = atStage; _validAttributeArray[0] = true; } + + /** + * Gets the annotate element array. + * @return Returns a reference to the array of annotate elements. + */ + domFx_annotate_common_Array &getAnnotate_array() { return elemAnnotate_array; } + /** + * Gets the annotate element array. + * @return Returns a constant reference to the array of annotate elements. + */ + const domFx_annotate_common_Array &getAnnotate_array() const { return elemAnnotate_array; } + /** + * Gets the compiler_target element. + * @return a daeSmartRef to the compiler_target element. + */ + const domCompiler_targetRef getCompiler_target() const { return elemCompiler_target; } + /** + * Gets the compiler_options element. + * @return a daeSmartRef to the compiler_options element. + */ + const domCompiler_optionsRef getCompiler_options() const { return elemCompiler_options; } + /** + * Gets the name element. + * @return a daeSmartRef to the name element. + */ + const domNameRef getName() const { return elemName; } + /** + * Gets the bind element array. + * @return Returns a reference to the array of bind elements. + */ + domBind_Array &getBind_array() { return elemBind_array; } + /** + * Gets the bind element array. + * @return Returns a constant reference to the array of bind elements. + */ + const domBind_Array &getBind_array() const { return elemBind_array; } + protected: + /** + * Constructor + */ + domShader(DAE& dae) : daeElement(dae), attrStage(), elemAnnotate_array(), elemCompiler_target(), elemCompiler_options(), elemName(), elemBind_array() {} + /** + * Destructor + */ + virtual ~domShader() {} + /** + * Overloaded assignment operator + */ + virtual domShader &operator=( const domShader &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + protected: // Attribute +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; + + protected: // Elements + domFx_annotate_common_Array elemAnnotate_array; + domFx_colortarget_common_Array elemColor_target_array; + domFx_depthtarget_common_Array elemDepth_target_array; + domFx_stenciltarget_common_Array elemStencil_target_array; + domFx_clearcolor_common_Array elemColor_clear_array; + domFx_cleardepth_common_Array elemDepth_clear_array; + domFx_clearstencil_common_Array elemStencil_clear_array; + domDrawRef elemDraw; + domGl_pipeline_settings_Array elemGl_pipeline_settings_array; +/** + * Declare and prepare a shader for execution in the rendering pipeline of + * a pass. @see domShader + */ + domShader_Array elemShader_array; + domExtra_Array elemExtra_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + + /** + * Gets the annotate element array. + * @return Returns a reference to the array of annotate elements. + */ + domFx_annotate_common_Array &getAnnotate_array() { return elemAnnotate_array; } + /** + * Gets the annotate element array. + * @return Returns a constant reference to the array of annotate elements. + */ + const domFx_annotate_common_Array &getAnnotate_array() const { return elemAnnotate_array; } + /** + * Gets the color_target element array. + * @return Returns a reference to the array of color_target elements. + */ + domFx_colortarget_common_Array &getColor_target_array() { return elemColor_target_array; } + /** + * Gets the color_target element array. + * @return Returns a constant reference to the array of color_target elements. + */ + const domFx_colortarget_common_Array &getColor_target_array() const { return elemColor_target_array; } + /** + * Gets the depth_target element array. + * @return Returns a reference to the array of depth_target elements. + */ + domFx_depthtarget_common_Array &getDepth_target_array() { return elemDepth_target_array; } + /** + * Gets the depth_target element array. + * @return Returns a constant reference to the array of depth_target elements. + */ + const domFx_depthtarget_common_Array &getDepth_target_array() const { return elemDepth_target_array; } + /** + * Gets the stencil_target element array. + * @return Returns a reference to the array of stencil_target elements. + */ + domFx_stenciltarget_common_Array &getStencil_target_array() { return elemStencil_target_array; } + /** + * Gets the stencil_target element array. + * @return Returns a constant reference to the array of stencil_target elements. + */ + const domFx_stenciltarget_common_Array &getStencil_target_array() const { return elemStencil_target_array; } + /** + * Gets the color_clear element array. + * @return Returns a reference to the array of color_clear elements. + */ + domFx_clearcolor_common_Array &getColor_clear_array() { return elemColor_clear_array; } + /** + * Gets the color_clear element array. + * @return Returns a constant reference to the array of color_clear elements. + */ + const domFx_clearcolor_common_Array &getColor_clear_array() const { return elemColor_clear_array; } + /** + * Gets the depth_clear element array. + * @return Returns a reference to the array of depth_clear elements. + */ + domFx_cleardepth_common_Array &getDepth_clear_array() { return elemDepth_clear_array; } + /** + * Gets the depth_clear element array. + * @return Returns a constant reference to the array of depth_clear elements. + */ + const domFx_cleardepth_common_Array &getDepth_clear_array() const { return elemDepth_clear_array; } + /** + * Gets the stencil_clear element array. + * @return Returns a reference to the array of stencil_clear elements. + */ + domFx_clearstencil_common_Array &getStencil_clear_array() { return elemStencil_clear_array; } + /** + * Gets the stencil_clear element array. + * @return Returns a constant reference to the array of stencil_clear elements. + */ + const domFx_clearstencil_common_Array &getStencil_clear_array() const { return elemStencil_clear_array; } + /** + * Gets the draw element. + * @return a daeSmartRef to the draw element. + */ + const domDrawRef getDraw() const { return elemDraw; } + /** + * Gets the gl_pipeline_settings element array. + * @return Returns a reference to the array of gl_pipeline_settings elements. + */ + domGl_pipeline_settings_Array &getGl_pipeline_settings_array() { return elemGl_pipeline_settings_array; } + /** + * Gets the gl_pipeline_settings element array. + * @return Returns a constant reference to the array of gl_pipeline_settings elements. + */ + const domGl_pipeline_settings_Array &getGl_pipeline_settings_array() const { return elemGl_pipeline_settings_array; } + /** + * Gets the shader element array. + * @return Returns a reference to the array of shader elements. + */ + domShader_Array &getShader_array() { return elemShader_array; } + /** + * Gets the shader element array. + * @return Returns a constant reference to the array of shader elements. + */ + const domShader_Array &getShader_array() const { return elemShader_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domPass(DAE& dae) : daeElement(dae), attrSid(), elemAnnotate_array(), elemColor_target_array(), elemDepth_target_array(), elemStencil_target_array(), elemColor_clear_array(), elemDepth_clear_array(), elemStencil_clear_array(), elemDraw(), elemGl_pipeline_settings_array(), elemShader_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domPass() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domPass &operator=( const domPass &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; + + protected: // Elements + domFx_annotate_common_Array elemAnnotate_array; + domFx_code_profile_Array elemCode_array; + domFx_include_common_Array elemInclude_array; + domImage_Array elemImage_array; + domGlsl_newparam_Array elemNewparam_array; + domGlsl_setparam_Array elemSetparam_array; +/** + * A static declaration of all the render states, shaders, and settings for + * one rendering pipeline. @see domPass + */ + domPass_Array elemPass_array; + domExtra_Array elemExtra_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[1] = true; } + + /** + * Gets the annotate element array. + * @return Returns a reference to the array of annotate elements. + */ + domFx_annotate_common_Array &getAnnotate_array() { return elemAnnotate_array; } + /** + * Gets the annotate element array. + * @return Returns a constant reference to the array of annotate elements. + */ + const domFx_annotate_common_Array &getAnnotate_array() const { return elemAnnotate_array; } + /** + * Gets the code element array. + * @return Returns a reference to the array of code elements. + */ + domFx_code_profile_Array &getCode_array() { return elemCode_array; } + /** + * Gets the code element array. + * @return Returns a constant reference to the array of code elements. + */ + const domFx_code_profile_Array &getCode_array() const { return elemCode_array; } + /** + * Gets the include element array. + * @return Returns a reference to the array of include elements. + */ + domFx_include_common_Array &getInclude_array() { return elemInclude_array; } + /** + * Gets the include element array. + * @return Returns a constant reference to the array of include elements. + */ + const domFx_include_common_Array &getInclude_array() const { return elemInclude_array; } + /** + * Gets the image element array. + * @return Returns a reference to the array of image elements. + */ + domImage_Array &getImage_array() { return elemImage_array; } + /** + * Gets the image element array. + * @return Returns a constant reference to the array of image elements. + */ + const domImage_Array &getImage_array() const { return elemImage_array; } + /** + * Gets the newparam element array. + * @return Returns a reference to the array of newparam elements. + */ + domGlsl_newparam_Array &getNewparam_array() { return elemNewparam_array; } + /** + * Gets the newparam element array. + * @return Returns a constant reference to the array of newparam elements. + */ + const domGlsl_newparam_Array &getNewparam_array() const { return elemNewparam_array; } + /** + * Gets the setparam element array. + * @return Returns a reference to the array of setparam elements. + */ + domGlsl_setparam_Array &getSetparam_array() { return elemSetparam_array; } + /** + * Gets the setparam element array. + * @return Returns a constant reference to the array of setparam elements. + */ + const domGlsl_setparam_Array &getSetparam_array() const { return elemSetparam_array; } + /** + * Gets the pass element array. + * @return Returns a reference to the array of pass elements. + */ + domPass_Array &getPass_array() { return elemPass_array; } + /** + * Gets the pass element array. + * @return Returns a constant reference to the array of pass elements. + */ + const domPass_Array &getPass_array() const { return elemPass_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domTechnique(DAE& dae) : daeElement(dae), attrId(), attrSid(), elemAnnotate_array(), elemCode_array(), elemInclude_array(), elemImage_array(), elemNewparam_array(), elemSetparam_array(), elemPass_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domTechnique() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domTechnique &operator=( const domTechnique &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attribute +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; + +protected: // Elements + domAssetRef elemAsset; + domFx_code_profile_Array elemCode_array; + domFx_include_common_Array elemInclude_array; + domImage_Array elemImage_array; + domGlsl_newparam_Array elemNewparam_array; +/** + * Holds a description of the textures, samplers, shaders, parameters, and + * passes necessary for rendering this effect using one method. @see domTechnique + */ + domTechnique_Array elemTechnique_array; + domExtra_Array elemExtra_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the code element array. + * @return Returns a reference to the array of code elements. + */ + domFx_code_profile_Array &getCode_array() { return elemCode_array; } + /** + * Gets the code element array. + * @return Returns a constant reference to the array of code elements. + */ + const domFx_code_profile_Array &getCode_array() const { return elemCode_array; } + /** + * Gets the include element array. + * @return Returns a reference to the array of include elements. + */ + domFx_include_common_Array &getInclude_array() { return elemInclude_array; } + /** + * Gets the include element array. + * @return Returns a constant reference to the array of include elements. + */ + const domFx_include_common_Array &getInclude_array() const { return elemInclude_array; } + /** + * Gets the image element array. + * @return Returns a reference to the array of image elements. + */ + domImage_Array &getImage_array() { return elemImage_array; } + /** + * Gets the image element array. + * @return Returns a constant reference to the array of image elements. + */ + const domImage_Array &getImage_array() const { return elemImage_array; } + /** + * Gets the newparam element array. + * @return Returns a reference to the array of newparam elements. + */ + domGlsl_newparam_Array &getNewparam_array() { return elemNewparam_array; } + /** + * Gets the newparam element array. + * @return Returns a constant reference to the array of newparam elements. + */ + const domGlsl_newparam_Array &getNewparam_array() const { return elemNewparam_array; } + /** + * Gets the technique element array. + * @return Returns a reference to the array of technique elements. + */ + domTechnique_Array &getTechnique_array() { return elemTechnique_array; } + /** + * Gets the technique element array. + * @return Returns a constant reference to the array of technique elements. + */ + const domTechnique_Array &getTechnique_array() const { return elemTechnique_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domProfile_GLSL(DAE& dae) : domFx_profile_abstract(dae), attrId(), elemAsset(), elemCode_array(), elemInclude_array(), elemImage_array(), elemNewparam_array(), elemTechnique_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domProfile_GLSL() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domProfile_GLSL &operator=( const domProfile_GLSL &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domRigid_body.h b/Engine/lib/collada/include/1.4/dom/domRigid_body.h new file mode 100644 index 000000000..4a0fcef23 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domRigid_body.h @@ -0,0 +1,792 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domRigid_body_h__ +#define __domRigid_body_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +class DAE; + +/** + * This element allows for describing simulated bodies that do not deform. + * These bodies may or may not be connected by constraints (hinge, ball-joint + * etc.). Rigid-bodies, constraints etc. are encapsulated in physics_model + * elements to allow for instantiating complex models. + */ +class domRigid_body : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::RIGID_BODY; } + static daeInt ID() { return 795; } + virtual daeInt typeID() const { return ID(); } +public: + class domTechnique_common; + + typedef daeSmartRef domTechnique_commonRef; + typedef daeTArray domTechnique_common_Array; + +/** + * The technique_common element specifies the rigid_body information for the + * common profile which all COLLADA implementations need to support. + */ + class domTechnique_common : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TECHNIQUE_COMMON; } + static daeInt ID() { return 796; } + virtual daeInt typeID() const { return ID(); } + public: + class domDynamic; + + typedef daeSmartRef domDynamicRef; + typedef daeTArray domDynamic_Array; + +/** + * If false, the rigid_body is not moveable + */ + class domDynamic : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::DYNAMIC; } + static daeInt ID() { return 797; } + virtual daeInt typeID() const { return ID(); } + protected: // Attribute +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; + + protected: // Value + /** + * The domBool value of the text data of this element. + */ + domBool _value; + + public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + + /** + * Gets the value of this element. + * @return a domBool of the value. + */ + domBool getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domBool val ) { _value = val; } + + protected: + /** + * Constructor + */ + domDynamic(DAE& dae) : daeElement(dae), attrSid(), _value() {} + /** + * Destructor + */ + virtual ~domDynamic() {} + /** + * Overloaded assignment operator + */ + virtual domDynamic &operator=( const domDynamic &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domMass_frame; + + typedef daeSmartRef domMass_frameRef; + typedef daeTArray domMass_frame_Array; + +/** + * Defines the center and orientation of mass of the rigid-body relative to + * the local origin of the “root” shape.This makes the off-diagonal elements + * of the inertia tensor (products of inertia) all 0 and allows us to just + * store the diagonal elements (moments of inertia). + */ + class domMass_frame : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::MASS_FRAME; } + static daeInt ID() { return 798; } + virtual daeInt typeID() const { return ID(); } + + protected: // Elements + domTranslate_Array elemTranslate_array; + domRotate_Array elemRotate_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the translate element array. + * @return Returns a reference to the array of translate elements. + */ + domTranslate_Array &getTranslate_array() { return elemTranslate_array; } + /** + * Gets the translate element array. + * @return Returns a constant reference to the array of translate elements. + */ + const domTranslate_Array &getTranslate_array() const { return elemTranslate_array; } + /** + * Gets the rotate element array. + * @return Returns a reference to the array of rotate elements. + */ + domRotate_Array &getRotate_array() { return elemRotate_array; } + /** + * Gets the rotate element array. + * @return Returns a constant reference to the array of rotate elements. + */ + const domRotate_Array &getRotate_array() const { return elemRotate_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domMass_frame(DAE& dae) : daeElement(dae), elemTranslate_array(), elemRotate_array() {} + /** + * Destructor + */ + virtual ~domMass_frame() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domMass_frame &operator=( const domMass_frame &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domShape; + + typedef daeSmartRef domShapeRef; + typedef daeTArray domShape_Array; + +/** + * This element allows for describing components of a rigid_body. + */ + class domShape : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SHAPE; } + static daeInt ID() { return 799; } + virtual daeInt typeID() const { return ID(); } + public: + class domHollow; + + typedef daeSmartRef domHollowRef; + typedef daeTArray domHollow_Array; + +/** + * If true, the mass is distributed along the surface of the shape + */ + class domHollow : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HOLLOW; } + static daeInt ID() { return 800; } + virtual daeInt typeID() const { return ID(); } + protected: // Attribute +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; + + protected: // Value + /** + * The domBool value of the text data of this element. + */ + domBool _value; + + public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + + /** + * Gets the value of this element. + * @return a domBool of the value. + */ + domBool getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domBool val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHollow(DAE& dae) : daeElement(dae), attrSid(), _value() {} + /** + * Destructor + */ + virtual ~domHollow() {} + /** + * Overloaded assignment operator + */ + virtual domHollow &operator=( const domHollow &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements +/** + * If true, the mass is distributed along the surface of the shape @see domHollow + */ + domHollowRef elemHollow; +/** + * The mass of the shape. @see domMass + */ + domTargetableFloatRef elemMass; +/** + * The density of the shape. @see domDensity + */ + domTargetableFloatRef elemDensity; +/** + * References a physics_material for the shape. @see domInstance_physics_material + */ + domInstance_physics_materialRef elemInstance_physics_material; +/** + * Defines a physics_material for the shape. @see domPhysics_material + */ + domPhysics_materialRef elemPhysics_material; +/** + * Instances a geometry to use to define this shape. @see domInstance_geometry + */ + domInstance_geometryRef elemInstance_geometry; +/** + * Defines a plane to use for this shape. @see domPlane + */ + domPlaneRef elemPlane; +/** + * Defines a box to use for this shape. @see domBox + */ + domBoxRef elemBox; +/** + * Defines a sphere to use for this shape. @see domSphere + */ + domSphereRef elemSphere; +/** + * Defines a cyliner to use for this shape. @see domCylinder + */ + domCylinderRef elemCylinder; +/** + * Defines a tapered_cylinder to use for this shape. @see domTapered_cylinder + */ + domTapered_cylinderRef elemTapered_cylinder; +/** + * Defines a capsule to use for this shape. @see domCapsule + */ + domCapsuleRef elemCapsule; +/** + * Defines a tapered_capsule to use for this shape. @see domTapered_capsule + */ + domTapered_capsuleRef elemTapered_capsule; +/** + * Allows a tranformation for the shape. @see domTranslate + */ + domTranslate_Array elemTranslate_array; +/** + * Allows a tranformation for the shape. @see domRotate + */ + domRotate_Array elemRotate_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the hollow element. + * @return a daeSmartRef to the hollow element. + */ + const domHollowRef getHollow() const { return elemHollow; } + /** + * Gets the mass element. + * @return a daeSmartRef to the mass element. + */ + const domTargetableFloatRef getMass() const { return elemMass; } + /** + * Gets the density element. + * @return a daeSmartRef to the density element. + */ + const domTargetableFloatRef getDensity() const { return elemDensity; } + /** + * Gets the instance_physics_material element. + * @return a daeSmartRef to the instance_physics_material element. + */ + const domInstance_physics_materialRef getInstance_physics_material() const { return elemInstance_physics_material; } + /** + * Gets the physics_material element. + * @return a daeSmartRef to the physics_material element. + */ + const domPhysics_materialRef getPhysics_material() const { return elemPhysics_material; } + /** + * Gets the instance_geometry element. + * @return a daeSmartRef to the instance_geometry element. + */ + const domInstance_geometryRef getInstance_geometry() const { return elemInstance_geometry; } + /** + * Gets the plane element. + * @return a daeSmartRef to the plane element. + */ + const domPlaneRef getPlane() const { return elemPlane; } + /** + * Gets the box element. + * @return a daeSmartRef to the box element. + */ + const domBoxRef getBox() const { return elemBox; } + /** + * Gets the sphere element. + * @return a daeSmartRef to the sphere element. + */ + const domSphereRef getSphere() const { return elemSphere; } + /** + * Gets the cylinder element. + * @return a daeSmartRef to the cylinder element. + */ + const domCylinderRef getCylinder() const { return elemCylinder; } + /** + * Gets the tapered_cylinder element. + * @return a daeSmartRef to the tapered_cylinder element. + */ + const domTapered_cylinderRef getTapered_cylinder() const { return elemTapered_cylinder; } + /** + * Gets the capsule element. + * @return a daeSmartRef to the capsule element. + */ + const domCapsuleRef getCapsule() const { return elemCapsule; } + /** + * Gets the tapered_capsule element. + * @return a daeSmartRef to the tapered_capsule element. + */ + const domTapered_capsuleRef getTapered_capsule() const { return elemTapered_capsule; } + /** + * Gets the translate element array. + * @return Returns a reference to the array of translate elements. + */ + domTranslate_Array &getTranslate_array() { return elemTranslate_array; } + /** + * Gets the translate element array. + * @return Returns a constant reference to the array of translate elements. + */ + const domTranslate_Array &getTranslate_array() const { return elemTranslate_array; } + /** + * Gets the rotate element array. + * @return Returns a reference to the array of rotate elements. + */ + domRotate_Array &getRotate_array() { return elemRotate_array; } + /** + * Gets the rotate element array. + * @return Returns a constant reference to the array of rotate elements. + */ + const domRotate_Array &getRotate_array() const { return elemRotate_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domShape(DAE& dae) : daeElement(dae), elemHollow(), elemMass(), elemDensity(), elemInstance_physics_material(), elemPhysics_material(), elemInstance_geometry(), elemPlane(), elemBox(), elemSphere(), elemCylinder(), elemTapered_cylinder(), elemCapsule(), elemTapered_capsule(), elemTranslate_array(), elemRotate_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domShape() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domShape &operator=( const domShape &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements +/** + * If false, the rigid_body is not moveable @see domDynamic + */ + domDynamicRef elemDynamic; +/** + * The total mass of the rigid-body @see domMass + */ + domTargetableFloatRef elemMass; +/** + * Defines the center and orientation of mass of the rigid-body relative to + * the local origin of the “root” shape.This makes the off-diagonal elements + * of the inertia tensor (products of inertia) all 0 and allows us to just + * store the diagonal elements (moments of inertia). @see domMass_frame + */ + domMass_frameRef elemMass_frame; +/** + * float3 – The diagonal elements of the inertia tensor (moments of inertia), + * which is represented in the local frame of the center of mass. See above. + * @see domInertia + */ + domTargetableFloat3Ref elemInertia; +/** + * References a physics_material for the rigid_body. @see domInstance_physics_material + */ + domInstance_physics_materialRef elemInstance_physics_material; +/** + * Defines a physics_material for the rigid_body. @see domPhysics_material + */ + domPhysics_materialRef elemPhysics_material; +/** + * This element allows for describing components of a rigid_body. @see domShape + */ + domShape_Array elemShape_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the dynamic element. + * @return a daeSmartRef to the dynamic element. + */ + const domDynamicRef getDynamic() const { return elemDynamic; } + /** + * Gets the mass element. + * @return a daeSmartRef to the mass element. + */ + const domTargetableFloatRef getMass() const { return elemMass; } + /** + * Gets the mass_frame element. + * @return a daeSmartRef to the mass_frame element. + */ + const domMass_frameRef getMass_frame() const { return elemMass_frame; } + /** + * Gets the inertia element. + * @return a daeSmartRef to the inertia element. + */ + const domTargetableFloat3Ref getInertia() const { return elemInertia; } + /** + * Gets the instance_physics_material element. + * @return a daeSmartRef to the instance_physics_material element. + */ + const domInstance_physics_materialRef getInstance_physics_material() const { return elemInstance_physics_material; } + /** + * Gets the physics_material element. + * @return a daeSmartRef to the physics_material element. + */ + const domPhysics_materialRef getPhysics_material() const { return elemPhysics_material; } + /** + * Gets the shape element array. + * @return Returns a reference to the array of shape elements. + */ + domShape_Array &getShape_array() { return elemShape_array; } + /** + * Gets the shape element array. + * @return Returns a constant reference to the array of shape elements. + */ + const domShape_Array &getShape_array() const { return elemShape_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domTechnique_common(DAE& dae) : daeElement(dae), elemDynamic(), elemMass(), elemMass_frame(), elemInertia(), elemInstance_physics_material(), elemPhysics_material(), elemShape_array() {} + /** + * Destructor + */ + virtual ~domTechnique_common() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domTechnique_common &operator=( const domTechnique_common &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attributes +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The technique_common element specifies the rigid_body information for the + * common profile which all COLLADA implementations need to support. @see + * domTechnique_common + */ + domTechnique_commonRef elemTechnique_common; +/** + * This element may contain any number of non-common profile techniques. + * @see domTechnique + */ + domTechnique_Array elemTechnique_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the technique_common element. + * @return a daeSmartRef to the technique_common element. + */ + const domTechnique_commonRef getTechnique_common() const { return elemTechnique_common; } + /** + * Gets the technique element array. + * @return Returns a reference to the array of technique elements. + */ + domTechnique_Array &getTechnique_array() { return elemTechnique_array; } + /** + * Gets the technique element array. + * @return Returns a constant reference to the array of technique elements. + */ + const domTechnique_Array &getTechnique_array() const { return elemTechnique_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domRigid_body(DAE& dae) : daeElement(dae), attrSid(), attrName(), elemTechnique_common(), elemTechnique_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domRigid_body() {} + /** + * Overloaded assignment operator + */ + virtual domRigid_body &operator=( const domRigid_body &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domRigid_constraint.h b/Engine/lib/collada/include/1.4/dom/domRigid_constraint.h new file mode 100644 index 000000000..c6f0e8f5f --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domRigid_constraint.h @@ -0,0 +1,1107 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domRigid_constraint_h__ +#define __domRigid_constraint_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +class DAE; + +/** + * This element allows for connecting components, such as rigid_body into + * complex physics models with moveable parts. + */ +class domRigid_constraint : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::RIGID_CONSTRAINT; } + static daeInt ID() { return 801; } + virtual daeInt typeID() const { return ID(); } +public: + class domRef_attachment; + + typedef daeSmartRef domRef_attachmentRef; + typedef daeTArray domRef_attachment_Array; + +/** + * Defines the attachment (to a rigid_body or a node) to be used as the reference-frame. + */ + class domRef_attachment : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::REF_ATTACHMENT; } + static daeInt ID() { return 802; } + virtual daeInt typeID() const { return ID(); } + protected: // Attribute +/** + * The “rigid_body” attribute is a relative reference to a rigid-body + * within the same physics_model. + */ + xsAnyURI attrRigid_body; + + protected: // Elements +/** + * Allows you to "position" the attachment point. @see domTranslate + */ + domTranslate_Array elemTranslate_array; +/** + * Allows you to "position" the attachment point. @see domRotate + */ + domRotate_Array elemRotate_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the rigid_body attribute. + * @return Returns a xsAnyURI reference of the rigid_body attribute. + */ + xsAnyURI &getRigid_body() { return attrRigid_body; } + /** + * Gets the rigid_body attribute. + * @return Returns a constant xsAnyURI reference of the rigid_body attribute. + */ + const xsAnyURI &getRigid_body() const { return attrRigid_body; } + /** + * Sets the rigid_body attribute. + * @param atRigid_body The new value for the rigid_body attribute. + */ + void setRigid_body( const xsAnyURI &atRigid_body ) { attrRigid_body = atRigid_body; _validAttributeArray[0] = true; } + /** + * Sets the rigid_body attribute. + * @param atRigid_body The new value for the rigid_body attribute. + */ + void setRigid_body( xsString atRigid_body ) { attrRigid_body = atRigid_body; _validAttributeArray[0] = true; } + + /** + * Gets the translate element array. + * @return Returns a reference to the array of translate elements. + */ + domTranslate_Array &getTranslate_array() { return elemTranslate_array; } + /** + * Gets the translate element array. + * @return Returns a constant reference to the array of translate elements. + */ + const domTranslate_Array &getTranslate_array() const { return elemTranslate_array; } + /** + * Gets the rotate element array. + * @return Returns a reference to the array of rotate elements. + */ + domRotate_Array &getRotate_array() { return elemRotate_array; } + /** + * Gets the rotate element array. + * @return Returns a constant reference to the array of rotate elements. + */ + const domRotate_Array &getRotate_array() const { return elemRotate_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domRef_attachment(DAE& dae) : daeElement(dae), attrRigid_body(dae, *this), elemTranslate_array(), elemRotate_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domRef_attachment() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domRef_attachment &operator=( const domRef_attachment &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domAttachment; + + typedef daeSmartRef domAttachmentRef; + typedef daeTArray domAttachment_Array; + +/** + * Defines an attachment to a rigid-body or a node. + */ + class domAttachment : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ATTACHMENT; } + static daeInt ID() { return 803; } + virtual daeInt typeID() const { return ID(); } + protected: // Attribute +/** + * The “rigid_body” attribute is a relative reference to a rigid-body + * within the same physics_model. + */ + xsAnyURI attrRigid_body; + + protected: // Elements +/** + * Allows you to "position" the attachment point. @see domTranslate + */ + domTranslate_Array elemTranslate_array; +/** + * Allows you to "position" the attachment point. @see domRotate + */ + domRotate_Array elemRotate_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + + public: //Accessors and Mutators + /** + * Gets the rigid_body attribute. + * @return Returns a xsAnyURI reference of the rigid_body attribute. + */ + xsAnyURI &getRigid_body() { return attrRigid_body; } + /** + * Gets the rigid_body attribute. + * @return Returns a constant xsAnyURI reference of the rigid_body attribute. + */ + const xsAnyURI &getRigid_body() const { return attrRigid_body; } + /** + * Sets the rigid_body attribute. + * @param atRigid_body The new value for the rigid_body attribute. + */ + void setRigid_body( const xsAnyURI &atRigid_body ) { attrRigid_body = atRigid_body; _validAttributeArray[0] = true; } + /** + * Sets the rigid_body attribute. + * @param atRigid_body The new value for the rigid_body attribute. + */ + void setRigid_body( xsString atRigid_body ) { attrRigid_body = atRigid_body; _validAttributeArray[0] = true; } + + /** + * Gets the translate element array. + * @return Returns a reference to the array of translate elements. + */ + domTranslate_Array &getTranslate_array() { return elemTranslate_array; } + /** + * Gets the translate element array. + * @return Returns a constant reference to the array of translate elements. + */ + const domTranslate_Array &getTranslate_array() const { return elemTranslate_array; } + /** + * Gets the rotate element array. + * @return Returns a reference to the array of rotate elements. + */ + domRotate_Array &getRotate_array() { return elemRotate_array; } + /** + * Gets the rotate element array. + * @return Returns a constant reference to the array of rotate elements. + */ + const domRotate_Array &getRotate_array() const { return elemRotate_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + protected: + /** + * Constructor + */ + domAttachment(DAE& dae) : daeElement(dae), attrRigid_body(dae, *this), elemTranslate_array(), elemRotate_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domAttachment() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domAttachment &operator=( const domAttachment &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domTechnique_common; + + typedef daeSmartRef domTechnique_commonRef; + typedef daeTArray domTechnique_common_Array; + +/** + * The technique_common element specifies the rigid_constraint information + * for the common profile which all COLLADA implementations need to support. + */ + class domTechnique_common : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TECHNIQUE_COMMON; } + static daeInt ID() { return 804; } + virtual daeInt typeID() const { return ID(); } + public: + class domEnabled; + + typedef daeSmartRef domEnabledRef; + typedef daeTArray domEnabled_Array; + +/** + * If false, the constraint doesn’t exert any force or influence on the + * rigid bodies. + */ + class domEnabled : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ENABLED; } + static daeInt ID() { return 805; } + virtual daeInt typeID() const { return ID(); } + protected: // Attribute +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; + + protected: // Value + /** + * The domBool value of the text data of this element. + */ + domBool _value; + + public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + + /** + * Gets the value of this element. + * @return a domBool of the value. + */ + domBool getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domBool val ) { _value = val; } + + protected: + /** + * Constructor + */ + domEnabled(DAE& dae) : daeElement(dae), attrSid(), _value() {} + /** + * Destructor + */ + virtual ~domEnabled() {} + /** + * Overloaded assignment operator + */ + virtual domEnabled &operator=( const domEnabled &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domInterpenetrate; + + typedef daeSmartRef domInterpenetrateRef; + typedef daeTArray domInterpenetrate_Array; + +/** + * Indicates whether the attached rigid bodies may inter-penetrate. + */ + class domInterpenetrate : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::INTERPENETRATE; } + static daeInt ID() { return 806; } + virtual daeInt typeID() const { return ID(); } + protected: // Attribute +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; + + protected: // Value + /** + * The domBool value of the text data of this element. + */ + domBool _value; + + public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + + /** + * Gets the value of this element. + * @return a domBool of the value. + */ + domBool getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domBool val ) { _value = val; } + + protected: + /** + * Constructor + */ + domInterpenetrate(DAE& dae) : daeElement(dae), attrSid(), _value() {} + /** + * Destructor + */ + virtual ~domInterpenetrate() {} + /** + * Overloaded assignment operator + */ + virtual domInterpenetrate &operator=( const domInterpenetrate &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLimits; + + typedef daeSmartRef domLimitsRef; + typedef daeTArray domLimits_Array; + +/** + * The limits element provides a flexible way to specify the constraint limits + * (degrees of freedom and ranges). + */ + class domLimits : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LIMITS; } + static daeInt ID() { return 807; } + virtual daeInt typeID() const { return ID(); } + public: + class domSwing_cone_and_twist; + + typedef daeSmartRef domSwing_cone_and_twistRef; + typedef daeTArray domSwing_cone_and_twist_Array; + +/** + * The swing_cone_and_twist element describes the angular limits along each + * rotation axis in degrees. The the X and Y limits describe a “swing cone” + * and the Z limits describe the “twist angle” range + */ + class domSwing_cone_and_twist : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SWING_CONE_AND_TWIST; } + static daeInt ID() { return 808; } + virtual daeInt typeID() const { return ID(); } + + protected: // Elements +/** + * The minimum values for the limit. @see domMin + */ + domTargetableFloat3Ref elemMin; +/** + * The maximum values for the limit. @see domMax + */ + domTargetableFloat3Ref elemMax; + + public: //Accessors and Mutators + /** + * Gets the min element. + * @return a daeSmartRef to the min element. + */ + const domTargetableFloat3Ref getMin() const { return elemMin; } + /** + * Gets the max element. + * @return a daeSmartRef to the max element. + */ + const domTargetableFloat3Ref getMax() const { return elemMax; } + protected: + /** + * Constructor + */ + domSwing_cone_and_twist(DAE& dae) : daeElement(dae), elemMin(), elemMax() {} + /** + * Destructor + */ + virtual ~domSwing_cone_and_twist() {} + /** + * Overloaded assignment operator + */ + virtual domSwing_cone_and_twist &operator=( const domSwing_cone_and_twist &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLinear; + + typedef daeSmartRef domLinearRef; + typedef daeTArray domLinear_Array; + +/** + * The linear element describes linear (translational) limits along each axis. + */ + class domLinear : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LINEAR; } + static daeInt ID() { return 809; } + virtual daeInt typeID() const { return ID(); } + + protected: // Elements +/** + * The minimum values for the limit. @see domMin + */ + domTargetableFloat3Ref elemMin; +/** + * The maximum values for the limit. @see domMax + */ + domTargetableFloat3Ref elemMax; + + public: //Accessors and Mutators + /** + * Gets the min element. + * @return a daeSmartRef to the min element. + */ + const domTargetableFloat3Ref getMin() const { return elemMin; } + /** + * Gets the max element. + * @return a daeSmartRef to the max element. + */ + const domTargetableFloat3Ref getMax() const { return elemMax; } + protected: + /** + * Constructor + */ + domLinear(DAE& dae) : daeElement(dae), elemMin(), elemMax() {} + /** + * Destructor + */ + virtual ~domLinear() {} + /** + * Overloaded assignment operator + */ + virtual domLinear &operator=( const domLinear &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements +/** + * The swing_cone_and_twist element describes the angular limits along each + * rotation axis in degrees. The the X and Y limits describe a “swing cone” + * and the Z limits describe the “twist angle” range @see domSwing_cone_and_twist + */ + domSwing_cone_and_twistRef elemSwing_cone_and_twist; +/** + * The linear element describes linear (translational) limits along each axis. + * @see domLinear + */ + domLinearRef elemLinear; + + public: //Accessors and Mutators + /** + * Gets the swing_cone_and_twist element. + * @return a daeSmartRef to the swing_cone_and_twist element. + */ + const domSwing_cone_and_twistRef getSwing_cone_and_twist() const { return elemSwing_cone_and_twist; } + /** + * Gets the linear element. + * @return a daeSmartRef to the linear element. + */ + const domLinearRef getLinear() const { return elemLinear; } + protected: + /** + * Constructor + */ + domLimits(DAE& dae) : daeElement(dae), elemSwing_cone_and_twist(), elemLinear() {} + /** + * Destructor + */ + virtual ~domLimits() {} + /** + * Overloaded assignment operator + */ + virtual domLimits &operator=( const domLimits &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domSpring; + + typedef daeSmartRef domSpringRef; + typedef daeTArray domSpring_Array; + +/** + * Spring, based on distance (“LINEAR”) or angle (“ANGULAR”). + */ + class domSpring : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SPRING; } + static daeInt ID() { return 810; } + virtual daeInt typeID() const { return ID(); } + public: + class domAngular; + + typedef daeSmartRef domAngularRef; + typedef daeTArray domAngular_Array; + +/** + * The angular spring properties. + */ + class domAngular : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ANGULAR; } + static daeInt ID() { return 811; } + virtual daeInt typeID() const { return ID(); } + + protected: // Elements +/** + * The stiffness (also called spring coefficient) has units of force/angle + * in degrees. @see domStiffness + */ + domTargetableFloatRef elemStiffness; +/** + * The spring damping coefficient. @see domDamping + */ + domTargetableFloatRef elemDamping; +/** + * The spring's target or resting distance. @see domTarget_value + */ + domTargetableFloatRef elemTarget_value; + + public: //Accessors and Mutators + /** + * Gets the stiffness element. + * @return a daeSmartRef to the stiffness element. + */ + const domTargetableFloatRef getStiffness() const { return elemStiffness; } + /** + * Gets the damping element. + * @return a daeSmartRef to the damping element. + */ + const domTargetableFloatRef getDamping() const { return elemDamping; } + /** + * Gets the target_value element. + * @return a daeSmartRef to the target_value element. + */ + const domTargetableFloatRef getTarget_value() const { return elemTarget_value; } + protected: + /** + * Constructor + */ + domAngular(DAE& dae) : daeElement(dae), elemStiffness(), elemDamping(), elemTarget_value() {} + /** + * Destructor + */ + virtual ~domAngular() {} + /** + * Overloaded assignment operator + */ + virtual domAngular &operator=( const domAngular &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domLinear; + + typedef daeSmartRef domLinearRef; + typedef daeTArray domLinear_Array; + +/** + * The linear spring properties. + */ + class domLinear : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LINEAR; } + static daeInt ID() { return 812; } + virtual daeInt typeID() const { return ID(); } + + protected: // Elements +/** + * The stiffness (also called spring coefficient) has units of force/distance. + * @see domStiffness + */ + domTargetableFloatRef elemStiffness; +/** + * The spring damping coefficient. @see domDamping + */ + domTargetableFloatRef elemDamping; +/** + * The spring's target or resting distance. @see domTarget_value + */ + domTargetableFloatRef elemTarget_value; + + public: //Accessors and Mutators + /** + * Gets the stiffness element. + * @return a daeSmartRef to the stiffness element. + */ + const domTargetableFloatRef getStiffness() const { return elemStiffness; } + /** + * Gets the damping element. + * @return a daeSmartRef to the damping element. + */ + const domTargetableFloatRef getDamping() const { return elemDamping; } + /** + * Gets the target_value element. + * @return a daeSmartRef to the target_value element. + */ + const domTargetableFloatRef getTarget_value() const { return elemTarget_value; } + protected: + /** + * Constructor + */ + domLinear(DAE& dae) : daeElement(dae), elemStiffness(), elemDamping(), elemTarget_value() {} + /** + * Destructor + */ + virtual ~domLinear() {} + /** + * Overloaded assignment operator + */ + virtual domLinear &operator=( const domLinear &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements +/** + * The angular spring properties. @see domAngular + */ + domAngularRef elemAngular; +/** + * The linear spring properties. @see domLinear + */ + domLinearRef elemLinear; + + public: //Accessors and Mutators + /** + * Gets the angular element. + * @return a daeSmartRef to the angular element. + */ + const domAngularRef getAngular() const { return elemAngular; } + /** + * Gets the linear element. + * @return a daeSmartRef to the linear element. + */ + const domLinearRef getLinear() const { return elemLinear; } + protected: + /** + * Constructor + */ + domSpring(DAE& dae) : daeElement(dae), elemAngular(), elemLinear() {} + /** + * Destructor + */ + virtual ~domSpring() {} + /** + * Overloaded assignment operator + */ + virtual domSpring &operator=( const domSpring &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + + protected: // Elements +/** + * If false, the constraint doesn’t exert any force or influence on the + * rigid bodies. @see domEnabled + */ + domEnabledRef elemEnabled; +/** + * Indicates whether the attached rigid bodies may inter-penetrate. @see domInterpenetrate + */ + domInterpenetrateRef elemInterpenetrate; +/** + * The limits element provides a flexible way to specify the constraint limits + * (degrees of freedom and ranges). @see domLimits + */ + domLimitsRef elemLimits; +/** + * Spring, based on distance (“LINEAR”) or angle (“ANGULAR”). @see + * domSpring + */ + domSpringRef elemSpring; + + public: //Accessors and Mutators + /** + * Gets the enabled element. + * @return a daeSmartRef to the enabled element. + */ + const domEnabledRef getEnabled() const { return elemEnabled; } + /** + * Gets the interpenetrate element. + * @return a daeSmartRef to the interpenetrate element. + */ + const domInterpenetrateRef getInterpenetrate() const { return elemInterpenetrate; } + /** + * Gets the limits element. + * @return a daeSmartRef to the limits element. + */ + const domLimitsRef getLimits() const { return elemLimits; } + /** + * Gets the spring element. + * @return a daeSmartRef to the spring element. + */ + const domSpringRef getSpring() const { return elemSpring; } + protected: + /** + * Constructor + */ + domTechnique_common(DAE& dae) : daeElement(dae), elemEnabled(), elemInterpenetrate(), elemLimits(), elemSpring() {} + /** + * Destructor + */ + virtual ~domTechnique_common() {} + /** + * Overloaded assignment operator + */ + virtual domTechnique_common &operator=( const domTechnique_common &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attributes +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * Defines the attachment (to a rigid_body or a node) to be used as the reference-frame. + * @see domRef_attachment + */ + domRef_attachmentRef elemRef_attachment; +/** + * Defines an attachment to a rigid-body or a node. @see domAttachment + */ + domAttachmentRef elemAttachment; +/** + * The technique_common element specifies the rigid_constraint information + * for the common profile which all COLLADA implementations need to support. + * @see domTechnique_common + */ + domTechnique_commonRef elemTechnique_common; +/** + * This element may contain any number of non-common profile techniques. + * @see domTechnique + */ + domTechnique_Array elemTechnique_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the ref_attachment element. + * @return a daeSmartRef to the ref_attachment element. + */ + const domRef_attachmentRef getRef_attachment() const { return elemRef_attachment; } + /** + * Gets the attachment element. + * @return a daeSmartRef to the attachment element. + */ + const domAttachmentRef getAttachment() const { return elemAttachment; } + /** + * Gets the technique_common element. + * @return a daeSmartRef to the technique_common element. + */ + const domTechnique_commonRef getTechnique_common() const { return elemTechnique_common; } + /** + * Gets the technique element array. + * @return Returns a reference to the array of technique elements. + */ + domTechnique_Array &getTechnique_array() { return elemTechnique_array; } + /** + * Gets the technique element array. + * @return Returns a constant reference to the array of technique elements. + */ + const domTechnique_Array &getTechnique_array() const { return elemTechnique_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domRigid_constraint(DAE& dae) : daeElement(dae), attrSid(), attrName(), elemRef_attachment(), elemAttachment(), elemTechnique_common(), elemTechnique_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domRigid_constraint() {} + /** + * Overloaded assignment operator + */ + virtual domRigid_constraint &operator=( const domRigid_constraint &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domRotate.h b/Engine/lib/collada/include/1.4/dom/domRotate.h new file mode 100644 index 000000000..13481fdb7 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domRotate.h @@ -0,0 +1,104 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domRotate_h__ +#define __domRotate_h__ + +#include +#include +#include + +class DAE; + +/** + * The rotate element contains an angle and a mathematical vector that represents + * the axis of rotation. + */ +class domRotate : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ROTATE; } + static daeInt ID() { return 631; } + virtual daeInt typeID() const { return ID(); } +protected: // Attribute +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; + +protected: // Value + /** + * The domFloat4 value of the text data of this element. + */ + domFloat4 _value; + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + + /** + * Gets the _value array. + * @return Returns a domFloat4 reference of the _value array. + */ + domFloat4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domFloat4 reference of the _value array. + */ + const domFloat4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domFloat4 &val ) { _value = val; } + +protected: + /** + * Constructor + */ + domRotate(DAE& dae) : daeElement(dae), attrSid(), _value() {} + /** + * Destructor + */ + virtual ~domRotate() {} + /** + * Overloaded assignment operator + */ + virtual domRotate &operator=( const domRotate &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domSampler.h b/Engine/lib/collada/include/1.4/dom/domSampler.h new file mode 100644 index 000000000..56b32c2f6 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domSampler.h @@ -0,0 +1,103 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domSampler_h__ +#define __domSampler_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * The sampler element declares an N-dimensional function used for animation. + * Animation function curves are represented by 1-D sampler elements in COLLADA. + * The sampler defines sampling points and how to interpolate between them. + */ +class domSampler : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SAMPLER; } + static daeInt ID() { return 654; } + virtual daeInt typeID() const { return ID(); } +protected: // Attribute +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; + +protected: // Element +/** + * The input element must occur at least one time. These inputs are local + * inputs. @see domInput + */ + domInputLocal_Array elemInput_array; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the input element array. + * @return Returns a reference to the array of input elements. + */ + domInputLocal_Array &getInput_array() { return elemInput_array; } + /** + * Gets the input element array. + * @return Returns a constant reference to the array of input elements. + */ + const domInputLocal_Array &getInput_array() const { return elemInput_array; } +protected: + /** + * Constructor + */ + domSampler(DAE& dae) : daeElement(dae), attrId(), elemInput_array() {} + /** + * Destructor + */ + virtual ~domSampler() {} + /** + * Overloaded assignment operator + */ + virtual domSampler &operator=( const domSampler &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domScale.h b/Engine/lib/collada/include/1.4/dom/domScale.h new file mode 100644 index 000000000..130dfd0cc --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domScale.h @@ -0,0 +1,75 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domScale_h__ +#define __domScale_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * The scale element contains a mathematical vector that represents the relative + * proportions of the X, Y and Z axes of a coordinated system. + */ +class domScale : public daeElement, public domTargetableFloat3_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SCALE; } + static daeInt ID() { return 632; } + virtual daeInt typeID() const { return ID(); } + + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + +protected: + /** + * Constructor + */ + domScale(DAE& dae) : daeElement(dae), domTargetableFloat3_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domScale() {} + /** + * Overloaded assignment operator + */ + virtual domScale &operator=( const domScale &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domSkew.h b/Engine/lib/collada/include/1.4/dom/domSkew.h new file mode 100644 index 000000000..5160432a4 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domSkew.h @@ -0,0 +1,104 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domSkew_h__ +#define __domSkew_h__ + +#include +#include +#include + +class DAE; + +/** + * The skew element contains an angle and two mathematical vectors that represent + * the axis of rotation and the axis of translation. + */ +class domSkew : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SKEW; } + static daeInt ID() { return 633; } + virtual daeInt typeID() const { return ID(); } +protected: // Attribute +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; + +protected: // Value + /** + * The domFloat7 value of the text data of this element. + */ + domFloat7 _value; + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + + /** + * Gets the _value array. + * @return Returns a domFloat7 reference of the _value array. + */ + domFloat7 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domFloat7 reference of the _value array. + */ + const domFloat7 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domFloat7 &val ) { _value = val; } + +protected: + /** + * Constructor + */ + domSkew(DAE& dae) : daeElement(dae), attrSid(), _value() {} + /** + * Destructor + */ + virtual ~domSkew() {} + /** + * Overloaded assignment operator + */ + virtual domSkew &operator=( const domSkew &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domSkin.h b/Engine/lib/collada/include/1.4/dom/domSkin.h new file mode 100644 index 000000000..a129d2703 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domSkin.h @@ -0,0 +1,559 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domSkin_h__ +#define __domSkin_h__ + +#include +#include +#include + +#include +#include +#include +#include +class DAE; + +/** + * The skin element contains vertex and primitive information sufficient to + * describe blend-weight skinning. + */ +class domSkin : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SKIN; } + static daeInt ID() { return 656; } + virtual daeInt typeID() const { return ID(); } +public: + class domBind_shape_matrix; + + typedef daeSmartRef domBind_shape_matrixRef; + typedef daeTArray domBind_shape_matrix_Array; + +/** + * This provides extra information about the position and orientation of the + * base mesh before binding. If bind_shape_matrix is not specified then an + * identity matrix may be used as the bind_shape_matrix. The bind_shape_matrix + * element may occur zero or one times. + */ + class domBind_shape_matrix : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::BIND_SHAPE_MATRIX; } + static daeInt ID() { return 657; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFloat4x4 value of the text data of this element. + */ + domFloat4x4 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domFloat4x4 reference of the _value array. + */ + domFloat4x4 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domFloat4x4 reference of the _value array. + */ + const domFloat4x4 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domFloat4x4 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domBind_shape_matrix(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domBind_shape_matrix() {} + /** + * Overloaded assignment operator + */ + virtual domBind_shape_matrix &operator=( const domBind_shape_matrix &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domJoints; + + typedef daeSmartRef domJointsRef; + typedef daeTArray domJoints_Array; + +/** + * The joints element associates joint, or skeleton, nodes with attribute + * data. In COLLADA, this is specified by the inverse bind matrix of each + * joint (influence) in the skeleton. + */ + class domJoints : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::JOINTS; } + static daeInt ID() { return 658; } + virtual daeInt typeID() const { return ID(); } + + protected: // Elements +/** + * The input element must occur at least twice. These inputs are local inputs. + * @see domInput + */ + domInputLocal_Array elemInput_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + + public: //Accessors and Mutators + /** + * Gets the input element array. + * @return Returns a reference to the array of input elements. + */ + domInputLocal_Array &getInput_array() { return elemInput_array; } + /** + * Gets the input element array. + * @return Returns a constant reference to the array of input elements. + */ + const domInputLocal_Array &getInput_array() const { return elemInput_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + protected: + /** + * Constructor + */ + domJoints(DAE& dae) : daeElement(dae), elemInput_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domJoints() {} + /** + * Overloaded assignment operator + */ + virtual domJoints &operator=( const domJoints &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domVertex_weights; + + typedef daeSmartRef domVertex_weightsRef; + typedef daeTArray domVertex_weights_Array; + +/** + * The vertex_weights element associates a set of joint-weight pairs with + * each vertex in the base mesh. + */ + class domVertex_weights : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::VERTEX_WEIGHTS; } + static daeInt ID() { return 659; } + virtual daeInt typeID() const { return ID(); } + public: + class domVcount; + + typedef daeSmartRef domVcountRef; + typedef daeTArray domVcount_Array; + +/** + * The vcount element contains a list of integers describing the number of + * influences for each vertex. The vcount element may occur once. + */ + class domVcount : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::VCOUNT; } + static daeInt ID() { return 660; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domListOfUInts value of the text data of this element. + */ + domListOfUInts _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domListOfUInts reference of the _value array. + */ + domListOfUInts &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domListOfUInts reference of the _value array. + */ + const domListOfUInts &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domListOfUInts &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domVcount(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domVcount() {} + /** + * Overloaded assignment operator + */ + virtual domVcount &operator=( const domVcount &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domV; + + typedef daeSmartRef domVRef; + typedef daeTArray domV_Array; + +/** + * The v element describes which bones and attributes are associated with + * each vertex. An index of –1 into the array of joints refers to the + * bind shape. Weights should be normalized before use. The v element must + * occur zero or one times. + */ + class domV : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::V; } + static daeInt ID() { return 661; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domListOfInts value of the text data of this element. + */ + domListOfInts _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domListOfInts reference of the _value array. + */ + domListOfInts &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domListOfInts reference of the _value array. + */ + const domListOfInts &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domListOfInts &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domV(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domV() {} + /** + * Overloaded assignment operator + */ + virtual domV &operator=( const domV &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + protected: // Attribute +/** + * The count attribute describes the number of vertices in the base mesh. + * Required element. + */ + domUint attrCount; + + protected: // Elements +/** + * The input element must occur at least twice. @see domInput + */ + domInputLocalOffset_Array elemInput_array; +/** + * The vcount element contains a list of integers describing the number of + * influences for each vertex. The vcount element may occur once. @see domVcount + */ + domVcountRef elemVcount; +/** + * The v element describes which bones and attributes are associated with + * each vertex. An index of –1 into the array of joints refers to the + * bind shape. Weights should be normalized before use. The v element must + * occur zero or one times. @see domV + */ + domVRef elemV; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + + public: //Accessors and Mutators + /** + * Gets the count attribute. + * @return Returns a domUint of the count attribute. + */ + domUint getCount() const { return attrCount; } + /** + * Sets the count attribute. + * @param atCount The new value for the count attribute. + */ + void setCount( domUint atCount ) { attrCount = atCount; _validAttributeArray[0] = true; } + + /** + * Gets the input element array. + * @return Returns a reference to the array of input elements. + */ + domInputLocalOffset_Array &getInput_array() { return elemInput_array; } + /** + * Gets the input element array. + * @return Returns a constant reference to the array of input elements. + */ + const domInputLocalOffset_Array &getInput_array() const { return elemInput_array; } + /** + * Gets the vcount element. + * @return a daeSmartRef to the vcount element. + */ + const domVcountRef getVcount() const { return elemVcount; } + /** + * Gets the v element. + * @return a daeSmartRef to the v element. + */ + const domVRef getV() const { return elemV; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + protected: + /** + * Constructor + */ + domVertex_weights(DAE& dae) : daeElement(dae), attrCount(), elemInput_array(), elemVcount(), elemV(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domVertex_weights() {} + /** + * Overloaded assignment operator + */ + virtual domVertex_weights &operator=( const domVertex_weights &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attribute +/** + * The source attribute contains a URI reference to the base mesh, (a static + * mesh or a morphed mesh). This also provides the bind-shape of the skinned + * mesh. Required attribute. + */ + xsAnyURI attrSource; + +protected: // Elements +/** + * This provides extra information about the position and orientation of the + * base mesh before binding. If bind_shape_matrix is not specified then an + * identity matrix may be used as the bind_shape_matrix. The bind_shape_matrix + * element may occur zero or one times. @see domBind_shape_matrix + */ + domBind_shape_matrixRef elemBind_shape_matrix; +/** + * The skin element must contain at least three source elements. @see domSource + */ + domSource_Array elemSource_array; +/** + * The joints element associates joint, or skeleton, nodes with attribute + * data. In COLLADA, this is specified by the inverse bind matrix of each + * joint (influence) in the skeleton. @see domJoints + */ + domJointsRef elemJoints; +/** + * The vertex_weights element associates a set of joint-weight pairs with + * each vertex in the base mesh. @see domVertex_weights + */ + domVertex_weightsRef elemVertex_weights; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the source attribute. + * @return Returns a xsAnyURI reference of the source attribute. + */ + xsAnyURI &getSource() { return attrSource; } + /** + * Gets the source attribute. + * @return Returns a constant xsAnyURI reference of the source attribute. + */ + const xsAnyURI &getSource() const { return attrSource; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( const xsAnyURI &atSource ) { attrSource = atSource; _validAttributeArray[0] = true; } + /** + * Sets the source attribute. + * @param atSource The new value for the source attribute. + */ + void setSource( xsString atSource ) { attrSource = atSource; _validAttributeArray[0] = true; } + + /** + * Gets the bind_shape_matrix element. + * @return a daeSmartRef to the bind_shape_matrix element. + */ + const domBind_shape_matrixRef getBind_shape_matrix() const { return elemBind_shape_matrix; } + /** + * Gets the source element array. + * @return Returns a reference to the array of source elements. + */ + domSource_Array &getSource_array() { return elemSource_array; } + /** + * Gets the source element array. + * @return Returns a constant reference to the array of source elements. + */ + const domSource_Array &getSource_array() const { return elemSource_array; } + /** + * Gets the joints element. + * @return a daeSmartRef to the joints element. + */ + const domJointsRef getJoints() const { return elemJoints; } + /** + * Gets the vertex_weights element. + * @return a daeSmartRef to the vertex_weights element. + */ + const domVertex_weightsRef getVertex_weights() const { return elemVertex_weights; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domSkin(DAE& dae) : daeElement(dae), attrSource(dae, *this), elemBind_shape_matrix(), elemSource_array(), elemJoints(), elemVertex_weights(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domSkin() {} + /** + * Overloaded assignment operator + */ + virtual domSkin &operator=( const domSkin &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domSource.h b/Engine/lib/collada/include/1.4/dom/domSource.h new file mode 100644 index 000000000..ccc49d032 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domSource.h @@ -0,0 +1,272 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domSource_h__ +#define __domSource_h__ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +class DAE; + +/** + * The source element declares a data repository that provides values according + * to the semantics of an input element that refers to it. + */ +class domSource : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SOURCE; } + static daeInt ID() { return 611; } + virtual daeInt typeID() const { return ID(); } +public: + class domTechnique_common; + + typedef daeSmartRef domTechnique_commonRef; + typedef daeTArray domTechnique_common_Array; + +/** + * The technique common specifies the common method for accessing this source + * element's data. + */ + class domTechnique_common : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TECHNIQUE_COMMON; } + static daeInt ID() { return 612; } + virtual daeInt typeID() const { return ID(); } + + protected: // Element +/** + * The source's technique_common must have one and only one accessor. @see + * domAccessor + */ + domAccessorRef elemAccessor; + + public: //Accessors and Mutators + /** + * Gets the accessor element. + * @return a daeSmartRef to the accessor element. + */ + const domAccessorRef getAccessor() const { return elemAccessor; } + protected: + /** + * Constructor + */ + domTechnique_common(DAE& dae) : daeElement(dae), elemAccessor() {} + /** + * Destructor + */ + virtual ~domTechnique_common() {} + /** + * Overloaded assignment operator + */ + virtual domTechnique_common &operator=( const domTechnique_common &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Required attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The source element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * The source element may contain an IDREF_array. @see domIDREF_array + */ + domIDREF_arrayRef elemIDREF_array; +/** + * The source element may contain a Name_array. @see domName_array + */ + domName_arrayRef elemName_array; +/** + * The source element may contain a bool_array. @see domBool_array + */ + domBool_arrayRef elemBool_array; +/** + * The source element may contain a float_array. @see domFloat_array + */ + domFloat_arrayRef elemFloat_array; +/** + * The source element may contain an int_array. @see domInt_array + */ + domInt_arrayRef elemInt_array; +/** + * The technique common specifies the common method for accessing this source + * element's data. @see domTechnique_common + */ + domTechnique_commonRef elemTechnique_common; +/** + * This element may contain any number of non-common profile techniques. + * @see domTechnique + */ + domTechnique_Array elemTechnique_array; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + /** + * Used to store information needed for some content model objects. + */ + daeTArray< daeCharArray * > _CMData; + + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the IDREF_array element. + * @return a daeSmartRef to the IDREF_array element. + */ + const domIDREF_arrayRef getIDREF_array() const { return elemIDREF_array; } + /** + * Gets the Name_array element. + * @return a daeSmartRef to the Name_array element. + */ + const domName_arrayRef getName_array() const { return elemName_array; } + /** + * Gets the bool_array element. + * @return a daeSmartRef to the bool_array element. + */ + const domBool_arrayRef getBool_array() const { return elemBool_array; } + /** + * Gets the float_array element. + * @return a daeSmartRef to the float_array element. + */ + const domFloat_arrayRef getFloat_array() const { return elemFloat_array; } + /** + * Gets the int_array element. + * @return a daeSmartRef to the int_array element. + */ + const domInt_arrayRef getInt_array() const { return elemInt_array; } + /** + * Gets the technique_common element. + * @return a daeSmartRef to the technique_common element. + */ + const domTechnique_commonRef getTechnique_common() const { return elemTechnique_common; } + /** + * Gets the technique element array. + * @return Returns a reference to the array of technique elements. + */ + domTechnique_Array &getTechnique_array() { return elemTechnique_array; } + /** + * Gets the technique element array. + * @return Returns a constant reference to the array of technique elements. + */ + const domTechnique_Array &getTechnique_array() const { return elemTechnique_array; } + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domSource(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemIDREF_array(), elemName_array(), elemBool_array(), elemFloat_array(), elemInt_array(), elemTechnique_common(), elemTechnique_array() {} + /** + * Destructor + */ + virtual ~domSource() { daeElement::deleteCMDataArray(_CMData); } + /** + * Overloaded assignment operator + */ + virtual domSource &operator=( const domSource &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domSphere.h b/Engine/lib/collada/include/1.4/dom/domSphere.h new file mode 100644 index 000000000..7c730d77c --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domSphere.h @@ -0,0 +1,152 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domSphere_h__ +#define __domSphere_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * A centered sphere primitive. + */ +class domSphere : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SPHERE; } + static daeInt ID() { return 771; } + virtual daeInt typeID() const { return ID(); } +public: + class domRadius; + + typedef daeSmartRef domRadiusRef; + typedef daeTArray domRadius_Array; + +/** + * A float value that represents the radius of the sphere + */ + class domRadius : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::RADIUS; } + static daeInt ID() { return 772; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFloat value of the text data of this element. + */ + domFloat _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFloat of the value. + */ + domFloat getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFloat val ) { _value = val; } + + protected: + /** + * Constructor + */ + domRadius(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domRadius() {} + /** + * Overloaded assignment operator + */ + virtual domRadius &operator=( const domRadius &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements +/** + * A float value that represents the radius of the sphere @see domRadius + */ + domRadiusRef elemRadius; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the radius element. + * @return a daeSmartRef to the radius element. + */ + const domRadiusRef getRadius() const { return elemRadius; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domSphere(DAE& dae) : daeElement(dae), elemRadius(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domSphere() {} + /** + * Overloaded assignment operator + */ + virtual domSphere &operator=( const domSphere &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domSpline.h b/Engine/lib/collada/include/1.4/dom/domSpline.h new file mode 100644 index 000000000..e631c75e3 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domSpline.h @@ -0,0 +1,198 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domSpline_h__ +#define __domSpline_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * The spline element contains control vertex information sufficient to describe + * basic splines. + */ +class domSpline : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::SPLINE; } + static daeInt ID() { return 615; } + virtual daeInt typeID() const { return ID(); } +public: + class domControl_vertices; + + typedef daeSmartRef domControl_verticesRef; + typedef daeTArray domControl_vertices_Array; + +/** + * The control vertices element must occur exactly one time. It is used + * to describe the CVs of the spline. + */ + class domControl_vertices : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::CONTROL_VERTICES; } + static daeInt ID() { return 616; } + virtual daeInt typeID() const { return ID(); } + + protected: // Elements +/** + * The input element must occur at least one time. These inputs are local + * inputs. @see domInput + */ + domInputLocal_Array elemInput_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + + public: //Accessors and Mutators + /** + * Gets the input element array. + * @return Returns a reference to the array of input elements. + */ + domInputLocal_Array &getInput_array() { return elemInput_array; } + /** + * Gets the input element array. + * @return Returns a constant reference to the array of input elements. + */ + const domInputLocal_Array &getInput_array() const { return elemInput_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } + protected: + /** + * Constructor + */ + domControl_vertices(DAE& dae) : daeElement(dae), elemInput_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domControl_vertices() {} + /** + * Overloaded assignment operator + */ + virtual domControl_vertices &operator=( const domControl_vertices &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attribute + domBool attrClosed; + +protected: // Elements +/** + * The mesh element must contain one or more source elements. @see domSource + */ + domSource_Array elemSource_array; +/** + * The control vertices element must occur exactly one time. It is used + * to describe the CVs of the spline. @see domControl_vertices + */ + domControl_verticesRef elemControl_vertices; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the closed attribute. + * @return Returns a domBool of the closed attribute. + */ + domBool getClosed() const { return attrClosed; } + /** + * Sets the closed attribute. + * @param atClosed The new value for the closed attribute. + */ + void setClosed( domBool atClosed ) { attrClosed = atClosed; _validAttributeArray[0] = true; } + + /** + * Gets the source element array. + * @return Returns a reference to the array of source elements. + */ + domSource_Array &getSource_array() { return elemSource_array; } + /** + * Gets the source element array. + * @return Returns a constant reference to the array of source elements. + */ + const domSource_Array &getSource_array() const { return elemSource_array; } + /** + * Gets the control_vertices element. + * @return a daeSmartRef to the control_vertices element. + */ + const domControl_verticesRef getControl_vertices() const { return elemControl_vertices; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domSpline(DAE& dae) : daeElement(dae), attrClosed(), elemSource_array(), elemControl_vertices(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domSpline() {} + /** + * Overloaded assignment operator + */ + virtual domSpline &operator=( const domSpline &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domTapered_capsule.h b/Engine/lib/collada/include/1.4/dom/domTapered_capsule.h new file mode 100644 index 000000000..cee6bdaa6 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domTapered_capsule.h @@ -0,0 +1,311 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domTapered_capsule_h__ +#define __domTapered_capsule_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * A tapered capsule primitive that is centered on, and aligned with, the + * local Y axis. + */ +class domTapered_capsule : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TAPERED_CAPSULE; } + static daeInt ID() { return 785; } + virtual daeInt typeID() const { return ID(); } +public: + class domHeight; + + typedef daeSmartRef domHeightRef; + typedef daeTArray domHeight_Array; + +/** + * A float value that represents the length of the line segment connecting + * the centers of the capping hemispheres. + */ + class domHeight : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HEIGHT; } + static daeInt ID() { return 786; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFloat value of the text data of this element. + */ + domFloat _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFloat of the value. + */ + domFloat getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFloat val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHeight(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domHeight() {} + /** + * Overloaded assignment operator + */ + virtual domHeight &operator=( const domHeight &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domRadius1; + + typedef daeSmartRef domRadius1Ref; + typedef daeTArray domRadius1_Array; + +/** + * Two float values that represent the radii of the tapered capsule at the + * positive (height/2) Y value.Both ends of the tapered capsule may be elliptical. + */ + class domRadius1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::RADIUS1; } + static daeInt ID() { return 787; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFloat2 value of the text data of this element. + */ + domFloat2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domFloat2 reference of the _value array. + */ + domFloat2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domFloat2 reference of the _value array. + */ + const domFloat2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domFloat2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domRadius1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domRadius1() {} + /** + * Overloaded assignment operator + */ + virtual domRadius1 &operator=( const domRadius1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domRadius2; + + typedef daeSmartRef domRadius2Ref; + typedef daeTArray domRadius2_Array; + +/** + * Two float values that represent the radii of the tapered capsule at the + * negative (height/2) Y value.Both ends of the tapered capsule may be elliptical. + */ + class domRadius2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::RADIUS2; } + static daeInt ID() { return 788; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFloat2 value of the text data of this element. + */ + domFloat2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domFloat2 reference of the _value array. + */ + domFloat2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domFloat2 reference of the _value array. + */ + const domFloat2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domFloat2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domRadius2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domRadius2() {} + /** + * Overloaded assignment operator + */ + virtual domRadius2 &operator=( const domRadius2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements +/** + * A float value that represents the length of the line segment connecting + * the centers of the capping hemispheres. @see domHeight + */ + domHeightRef elemHeight; +/** + * Two float values that represent the radii of the tapered capsule at the + * positive (height/2) Y value.Both ends of the tapered capsule may be elliptical. + * @see domRadius1 + */ + domRadius1Ref elemRadius1; +/** + * Two float values that represent the radii of the tapered capsule at the + * negative (height/2) Y value.Both ends of the tapered capsule may be elliptical. + * @see domRadius2 + */ + domRadius2Ref elemRadius2; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the height element. + * @return a daeSmartRef to the height element. + */ + const domHeightRef getHeight() const { return elemHeight; } + /** + * Gets the radius1 element. + * @return a daeSmartRef to the radius1 element. + */ + const domRadius1Ref getRadius1() const { return elemRadius1; } + /** + * Gets the radius2 element. + * @return a daeSmartRef to the radius2 element. + */ + const domRadius2Ref getRadius2() const { return elemRadius2; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domTapered_capsule(DAE& dae) : daeElement(dae), elemHeight(), elemRadius1(), elemRadius2(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domTapered_capsule() {} + /** + * Overloaded assignment operator + */ + virtual domTapered_capsule &operator=( const domTapered_capsule &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domTapered_cylinder.h b/Engine/lib/collada/include/1.4/dom/domTapered_cylinder.h new file mode 100644 index 000000000..6074f4a84 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domTapered_cylinder.h @@ -0,0 +1,311 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domTapered_cylinder_h__ +#define __domTapered_cylinder_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * A tapered cylinder primitive that is centered on and aligned with the local + * Y axis. + */ +class domTapered_cylinder : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TAPERED_CYLINDER; } + static daeInt ID() { return 778; } + virtual daeInt typeID() const { return ID(); } +public: + class domHeight; + + typedef daeSmartRef domHeightRef; + typedef daeTArray domHeight_Array; + +/** + * A float value that represents the length of the cylinder along the Y axis. + */ + class domHeight : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::HEIGHT; } + static daeInt ID() { return 779; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFloat value of the text data of this element. + */ + domFloat _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return a domFloat of the value. + */ + domFloat getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFloat val ) { _value = val; } + + protected: + /** + * Constructor + */ + domHeight(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domHeight() {} + /** + * Overloaded assignment operator + */ + virtual domHeight &operator=( const domHeight &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domRadius1; + + typedef daeSmartRef domRadius1Ref; + typedef daeTArray domRadius1_Array; + +/** + * Two float values that represent the radii of the tapered cylinder at the + * positive (height/2) Y value. Both ends of the tapered cylinder may be + * elliptical. + */ + class domRadius1 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::RADIUS1; } + static daeInt ID() { return 780; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFloat2 value of the text data of this element. + */ + domFloat2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domFloat2 reference of the _value array. + */ + domFloat2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domFloat2 reference of the _value array. + */ + const domFloat2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domFloat2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domRadius1(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domRadius1() {} + /** + * Overloaded assignment operator + */ + virtual domRadius1 &operator=( const domRadius1 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + class domRadius2; + + typedef daeSmartRef domRadius2Ref; + typedef daeTArray domRadius2_Array; + +/** + * Two float values that represent the radii of the tapered cylinder at the + * negative (height/2) Y value.Both ends of the tapered cylinder may be elliptical. + */ + class domRadius2 : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::RADIUS2; } + static daeInt ID() { return 781; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The domFloat2 value of the text data of this element. + */ + domFloat2 _value; + + public: //Accessors and Mutators + /** + * Gets the _value array. + * @return Returns a domFloat2 reference of the _value array. + */ + domFloat2 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domFloat2 reference of the _value array. + */ + const domFloat2 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domFloat2 &val ) { _value = val; } + + protected: + /** + * Constructor + */ + domRadius2(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domRadius2() {} + /** + * Overloaded assignment operator + */ + virtual domRadius2 &operator=( const domRadius2 &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + +protected: // Elements +/** + * A float value that represents the length of the cylinder along the Y axis. + * @see domHeight + */ + domHeightRef elemHeight; +/** + * Two float values that represent the radii of the tapered cylinder at the + * positive (height/2) Y value. Both ends of the tapered cylinder may be + * elliptical. @see domRadius1 + */ + domRadius1Ref elemRadius1; +/** + * Two float values that represent the radii of the tapered cylinder at the + * negative (height/2) Y value.Both ends of the tapered cylinder may be elliptical. + * @see domRadius2 + */ + domRadius2Ref elemRadius2; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the height element. + * @return a daeSmartRef to the height element. + */ + const domHeightRef getHeight() const { return elemHeight; } + /** + * Gets the radius1 element. + * @return a daeSmartRef to the radius1 element. + */ + const domRadius1Ref getRadius1() const { return elemRadius1; } + /** + * Gets the radius2 element. + * @return a daeSmartRef to the radius2 element. + */ + const domRadius2Ref getRadius2() const { return elemRadius2; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domTapered_cylinder(DAE& dae) : daeElement(dae), elemHeight(), elemRadius1(), elemRadius2(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domTapered_cylinder() {} + /** + * Overloaded assignment operator + */ + virtual domTapered_cylinder &operator=( const domTapered_cylinder &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domTargetableFloat.h b/Engine/lib/collada/include/1.4/dom/domTargetableFloat.h new file mode 100644 index 000000000..460ebd8d0 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domTargetableFloat.h @@ -0,0 +1,132 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domTargetableFloat_h__ +#define __domTargetableFloat_h__ + +#include +#include +#include + +class DAE; + +/** + * The TargetableFloat type is used to represent elements which contain a + * single float value which can be targeted for animation. + */ +class domTargetableFloat_complexType +{ +protected: // Attribute +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; + +protected: // Value + /** + * The domFloat value of the text data of this element. + */ + domFloat _value; + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid;} + + /** + * Gets the value of this element. + * @return a domFloat of the value. + */ + domFloat getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( domFloat val ) { _value = val; } + +protected: + /** + * Constructor + */ + domTargetableFloat_complexType(DAE& dae, daeElement* elt) : attrSid(), _value() {} + /** + * Destructor + */ + virtual ~domTargetableFloat_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domTargetableFloat_complexType &operator=( const domTargetableFloat_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domTargetableFloat_complexType. + */ +class domTargetableFloat : public daeElement, public domTargetableFloat_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TARGETABLEFLOAT; } + static daeInt ID() { return 4; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + +protected: + /** + * Constructor + */ + domTargetableFloat(DAE& dae) : daeElement(dae), domTargetableFloat_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domTargetableFloat() {} + /** + * Overloaded assignment operator + */ + virtual domTargetableFloat &operator=( const domTargetableFloat &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domTargetableFloat3.h b/Engine/lib/collada/include/1.4/dom/domTargetableFloat3.h new file mode 100644 index 000000000..9ee9103de --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domTargetableFloat3.h @@ -0,0 +1,137 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domTargetableFloat3_h__ +#define __domTargetableFloat3_h__ + +#include +#include +#include + +class DAE; + +/** + * The TargetableFloat3 type is used to represent elements which contain a + * float3 value which can be targeted for animation. + */ +class domTargetableFloat3_complexType +{ +protected: // Attribute +/** + * The sid attribute is a text string value containing the sub-identifier + * of this element. This value must be unique within the scope of the parent + * element. Optional attribute. + */ + xsNCName attrSid; + +protected: // Value + /** + * The domFloat3 value of the text data of this element. + */ + domFloat3 _value; + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid;} + + /** + * Gets the _value array. + * @return Returns a domFloat3 reference of the _value array. + */ + domFloat3 &getValue() { return _value; } + /** + * Gets the _value array. + * @return Returns a constant domFloat3 reference of the _value array. + */ + const domFloat3 &getValue() const { return _value; } + /** + * Sets the _value array. + * @param val The new value for the _value array. + */ + void setValue( const domFloat3 &val ) { _value = val; } + +protected: + /** + * Constructor + */ + domTargetableFloat3_complexType(DAE& dae, daeElement* elt) : attrSid(), _value() {} + /** + * Destructor + */ + virtual ~domTargetableFloat3_complexType() {} + /** + * Overloaded assignment operator + */ + virtual domTargetableFloat3_complexType &operator=( const domTargetableFloat3_complexType &cpy ) { (void)cpy; return *this; } +}; + +/** + * An element of type domTargetableFloat3_complexType. + */ +class domTargetableFloat3 : public daeElement, public domTargetableFloat3_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TARGETABLEFLOAT3; } + static daeInt ID() { return 5; } + virtual daeInt typeID() const { return ID(); } + +public: //Accessors and Mutators + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + +protected: + /** + * Constructor + */ + domTargetableFloat3(DAE& dae) : daeElement(dae), domTargetableFloat3_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domTargetableFloat3() {} + /** + * Overloaded assignment operator + */ + virtual domTargetableFloat3 &operator=( const domTargetableFloat3 &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domTechnique.h b/Engine/lib/collada/include/1.4/dom/domTechnique.h new file mode 100644 index 000000000..b5381e9d1 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domTechnique.h @@ -0,0 +1,130 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domTechnique_h__ +#define __domTechnique_h__ + +#include +#include +#include + +class DAE; + +/** + * The technique element declares the information used to process some portion + * of the content. Each technique conforms to an associated profile. Techniques + * generally act as a “switch”. If more than one is present for a particular + * portion of content, on import, one or the other is picked, but usually + * not both. Selection should be based on which profile the importing application + * can support. Techniques contain application data and programs, making them + * assets that can be managed as a unit. + */ +class domTechnique : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TECHNIQUE; } + static daeInt ID() { return 680; } + virtual daeInt typeID() const { return ID(); } +protected: // Attribute + /** + * This element may specify its own xmlns. + */ + xsAnyURI attrXmlns; +/** + * The profile attribute indicates the type of profile. This is a vendor + * defined character string that indicates the platform or capability target + * for the technique. Required attribute. + */ + xsNMTOKEN attrProfile; + +protected: // Element + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + + +public: //Accessors and Mutators + /** + * Gets the xmlns attribute. + * @return Returns a xsAnyURI reference of the xmlns attribute. + */ + xsAnyURI &getXmlns() { return attrXmlns; } + /** + * Gets the xmlns attribute. + * @return Returns a constant xsAnyURI reference of the xmlns attribute. + */ + const xsAnyURI &getXmlns() const { return attrXmlns; } + /** + * Sets the xmlns attribute. + * @param xmlns The new value for the xmlns attribute. + */ + void setXmlns( const xsAnyURI &xmlns ) { attrXmlns = xmlns; + _validAttributeArray[0] = true; } + + /** + * Gets the profile attribute. + * @return Returns a xsNMTOKEN of the profile attribute. + */ + xsNMTOKEN getProfile() const { return attrProfile; } + /** + * Sets the profile attribute. + * @param atProfile The new value for the profile attribute. + */ + void setProfile( xsNMTOKEN atProfile ) { *(daeStringRef*)&attrProfile = atProfile; _validAttributeArray[1] = true; } + + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + +protected: + /** + * Constructor + */ + domTechnique(DAE& dae) : daeElement(dae), attrXmlns(dae, *this), attrProfile() {} + /** + * Destructor + */ + virtual ~domTechnique() {} + /** + * Overloaded assignment operator + */ + virtual domTechnique &operator=( const domTechnique &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domTranslate.h b/Engine/lib/collada/include/1.4/dom/domTranslate.h new file mode 100644 index 000000000..4e8f57437 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domTranslate.h @@ -0,0 +1,75 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domTranslate_h__ +#define __domTranslate_h__ + +#include +#include +#include + +#include +class DAE; + +/** + * The translate element contains a mathematical vector that represents the + * distance along the X, Y and Z-axes. + */ +class domTranslate : public daeElement, public domTargetableFloat3_complexType +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TRANSLATE; } + static daeInt ID() { return 634; } + virtual daeInt typeID() const { return ID(); } + + /** + * Gets the sid attribute. + * @return Returns a xsNCName of the sid attribute. + */ + xsNCName getSid() const { return attrSid; } + /** + * Sets the sid attribute. + * @param atSid The new value for the sid attribute. + */ + void setSid( xsNCName atSid ) { *(daeStringRef*)&attrSid = atSid; _validAttributeArray[0] = true; } + +protected: + /** + * Constructor + */ + domTranslate(DAE& dae) : daeElement(dae), domTargetableFloat3_complexType(dae, this) {} + /** + * Destructor + */ + virtual ~domTranslate() {} + /** + * Overloaded assignment operator + */ + virtual domTranslate &operator=( const domTranslate &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domTriangles.h b/Engine/lib/collada/include/1.4/dom/domTriangles.h new file mode 100644 index 000000000..d0e0ade59 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domTriangles.h @@ -0,0 +1,160 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domTriangles_h__ +#define __domTriangles_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * The triangles element provides the information needed to bind vertex attributes + * together and then organize those vertices into individual triangles.Each + * triangle described by the mesh has three vertices. The first triangle + * is formed from the first, second, and third vertices. The second triangle + * is formed from the fourth, fifth, and sixth vertices, and so on. + */ +class domTriangles : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TRIANGLES; } + static daeInt ID() { return 625; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; +/** + * The count attribute indicates the number of triangle primitives. Required + * attribute. + */ + domUint attrCount; +/** + * The material attribute declares a symbol for a material. This symbol is + * bound to a material at the time of instantiation. Optional attribute. + * If the material attribute is not specified then the lighting and shading + * results are application defined. + */ + xsNCName attrMaterial; + +protected: // Elements +/** + * The input element may occur any number of times. This input is a local + * input with the offset and set attributes. @see domInput + */ + domInputLocalOffset_Array elemInput_array; +/** + * The triangles element may have any number of p elements. @see domP + */ + domPRef elemP; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[0] = true; } + + /** + * Gets the count attribute. + * @return Returns a domUint of the count attribute. + */ + domUint getCount() const { return attrCount; } + /** + * Sets the count attribute. + * @param atCount The new value for the count attribute. + */ + void setCount( domUint atCount ) { attrCount = atCount; _validAttributeArray[1] = true; } + + /** + * Gets the material attribute. + * @return Returns a xsNCName of the material attribute. + */ + xsNCName getMaterial() const { return attrMaterial; } + /** + * Sets the material attribute. + * @param atMaterial The new value for the material attribute. + */ + void setMaterial( xsNCName atMaterial ) { *(daeStringRef*)&attrMaterial = atMaterial; _validAttributeArray[2] = true; } + + /** + * Gets the input element array. + * @return Returns a reference to the array of input elements. + */ + domInputLocalOffset_Array &getInput_array() { return elemInput_array; } + /** + * Gets the input element array. + * @return Returns a constant reference to the array of input elements. + */ + const domInputLocalOffset_Array &getInput_array() const { return elemInput_array; } + /** + * Gets the p element. + * @return a daeSmartRef to the p element. + */ + const domPRef getP() const { return elemP; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domTriangles(DAE& dae) : daeElement(dae), attrName(), attrCount(), attrMaterial(), elemInput_array(), elemP(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domTriangles() {} + /** + * Overloaded assignment operator + */ + virtual domTriangles &operator=( const domTriangles &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domTrifans.h b/Engine/lib/collada/include/1.4/dom/domTrifans.h new file mode 100644 index 000000000..434c1766e --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domTrifans.h @@ -0,0 +1,165 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domTrifans_h__ +#define __domTrifans_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * The trifans element provides the information needed to bind vertex attributes + * together and then organize those vertices into connected triangles. Each + * triangle described by the mesh has three vertices. The first triangle + * is formed from first, second, and third vertices. Each subsequent triangle + * is formed from the current vertex, reusing the first and the previous vertices. + */ +class domTrifans : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TRIFANS; } + static daeInt ID() { return 626; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; +/** + * The count attribute indicates the number of triangle fan primitives. Required + * attribute. + */ + domUint attrCount; +/** + * The material attribute declares a symbol for a material. This symbol is + * bound to a material at the time of instantiation. If the material attribute + * is not specified then the lighting and shading results are application + * defined. Optional attribute. + */ + xsNCName attrMaterial; + +protected: // Elements +/** + * The input element may occur any number of times. This input is a local + * input with the offset and set attributes. @see domInput + */ + domInputLocalOffset_Array elemInput_array; +/** + * The trifans element may have any number of p elements. @see domP + */ + domP_Array elemP_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[0] = true; } + + /** + * Gets the count attribute. + * @return Returns a domUint of the count attribute. + */ + domUint getCount() const { return attrCount; } + /** + * Sets the count attribute. + * @param atCount The new value for the count attribute. + */ + void setCount( domUint atCount ) { attrCount = atCount; _validAttributeArray[1] = true; } + + /** + * Gets the material attribute. + * @return Returns a xsNCName of the material attribute. + */ + xsNCName getMaterial() const { return attrMaterial; } + /** + * Sets the material attribute. + * @param atMaterial The new value for the material attribute. + */ + void setMaterial( xsNCName atMaterial ) { *(daeStringRef*)&attrMaterial = atMaterial; _validAttributeArray[2] = true; } + + /** + * Gets the input element array. + * @return Returns a reference to the array of input elements. + */ + domInputLocalOffset_Array &getInput_array() { return elemInput_array; } + /** + * Gets the input element array. + * @return Returns a constant reference to the array of input elements. + */ + const domInputLocalOffset_Array &getInput_array() const { return elemInput_array; } + /** + * Gets the p element array. + * @return Returns a reference to the array of p elements. + */ + domP_Array &getP_array() { return elemP_array; } + /** + * Gets the p element array. + * @return Returns a constant reference to the array of p elements. + */ + const domP_Array &getP_array() const { return elemP_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domTrifans(DAE& dae) : daeElement(dae), attrName(), attrCount(), attrMaterial(), elemInput_array(), elemP_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domTrifans() {} + /** + * Overloaded assignment operator + */ + virtual domTrifans &operator=( const domTrifans &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domTristrips.h b/Engine/lib/collada/include/1.4/dom/domTristrips.h new file mode 100644 index 000000000..d4cccb7ab --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domTristrips.h @@ -0,0 +1,165 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domTristrips_h__ +#define __domTristrips_h__ + +#include +#include +#include + +#include +#include +#include +class DAE; + +/** + * The tristrips element provides the information needed to bind vertex attributes + * together and then organize those vertices into connected triangles. Each + * triangle described by the mesh has three vertices. The first triangle + * is formed from first, second, and third vertices. Each subsequent triangle + * is formed from the current vertex, reusing the previous two vertices. + */ +class domTristrips : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::TRISTRIPS; } + static daeInt ID() { return 627; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; +/** + * The count attribute indicates the number of triangle strip primitives. + * Required attribute. + */ + domUint attrCount; +/** + * The material attribute declares a symbol for a material. This symbol is + * bound to a material at the time of instantiation. If the material attribute + * is not specified then the lighting and shading results are application + * defined. Optional attribute. + */ + xsNCName attrMaterial; + +protected: // Elements +/** + * The input element may occur any number of times. This input is a local + * input with the offset and set attributes. @see domInput + */ + domInputLocalOffset_Array elemInput_array; +/** + * The tristrips element may have any number of p elements. @see domP + */ + domP_Array elemP_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[0] = true; } + + /** + * Gets the count attribute. + * @return Returns a domUint of the count attribute. + */ + domUint getCount() const { return attrCount; } + /** + * Sets the count attribute. + * @param atCount The new value for the count attribute. + */ + void setCount( domUint atCount ) { attrCount = atCount; _validAttributeArray[1] = true; } + + /** + * Gets the material attribute. + * @return Returns a xsNCName of the material attribute. + */ + xsNCName getMaterial() const { return attrMaterial; } + /** + * Sets the material attribute. + * @param atMaterial The new value for the material attribute. + */ + void setMaterial( xsNCName atMaterial ) { *(daeStringRef*)&attrMaterial = atMaterial; _validAttributeArray[2] = true; } + + /** + * Gets the input element array. + * @return Returns a reference to the array of input elements. + */ + domInputLocalOffset_Array &getInput_array() { return elemInput_array; } + /** + * Gets the input element array. + * @return Returns a constant reference to the array of input elements. + */ + const domInputLocalOffset_Array &getInput_array() const { return elemInput_array; } + /** + * Gets the p element array. + * @return Returns a reference to the array of p elements. + */ + domP_Array &getP_array() { return elemP_array; } + /** + * Gets the p element array. + * @return Returns a constant reference to the array of p elements. + */ + const domP_Array &getP_array() const { return elemP_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domTristrips(DAE& dae) : daeElement(dae), attrName(), attrCount(), attrMaterial(), elemInput_array(), elemP_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domTristrips() {} + /** + * Overloaded assignment operator + */ + virtual domTristrips &operator=( const domTristrips &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domTypes.h b/Engine/lib/collada/include/1.4/dom/domTypes.h new file mode 100644 index 000000000..f6a1f09bd --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domTypes.h @@ -0,0 +1,1248 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DOM_TYPES_H__ +#define __DOM_TYPES_H__ + +#include + +typedef xsBoolean domBool; +typedef xsDateTime domDateTime; +typedef xsDouble domFloat; +typedef xsLong domInt; +typedef xsName domName; +typedef xsString domString; +typedef xsToken domToken; +typedef xsUnsignedLong domUint; +typedef daeTArray domListOfBools; +typedef daeTArray domListOfFloats; +typedef xsHexBinaryArray domListOfHexBinary; +typedef daeTArray domListOfInts; +typedef daeTArray domListOfNames; +typedef daeTArray domListOfTokens; +typedef daeTArray domListOfUInts; +typedef domListOfBools domBool2; +typedef domListOfBools domBool3; +typedef domListOfBools domBool4; +typedef domListOfFloats domFloat2; +typedef domListOfFloats domFloat3; +typedef domListOfFloats domFloat4; +typedef domListOfFloats domFloat7; +typedef domListOfFloats domFloat2x2; +typedef domListOfFloats domFloat3x3; +typedef domListOfFloats domFloat4x4; +typedef domListOfFloats domFloat2x3; +typedef domListOfFloats domFloat2x4; +typedef domListOfFloats domFloat3x2; +typedef domListOfFloats domFloat3x4; +typedef domListOfFloats domFloat4x2; +typedef domListOfFloats domFloat4x3; +typedef domListOfInts domInt2; +typedef domListOfInts domInt3; +typedef domListOfInts domInt4; +typedef domListOfInts domInt2x2; +typedef domListOfInts domInt3x3; +typedef domListOfInts domInt4x4; +/** + * This type is used for URI reference which can only reference a resource + * declared within it's same document. + */ +typedef xsAnyURI domURIFragmentType; +typedef domFloat4 domFx_color_common; +typedef xsString domFx_draw_common; +typedef xsNonNegativeInteger domGL_MAX_LIGHTS_index; +typedef xsNonNegativeInteger domGL_MAX_CLIP_PLANES_index; +typedef xsNonNegativeInteger domGL_MAX_TEXTURE_IMAGE_UNITS_index; +typedef xsFloat domGl_alpha_value_type; +typedef xsFloat domGlsl_float; +typedef xsInt domGlsl_int; +typedef xsBoolean domGlsl_bool; +typedef daeTArray domGlsl_ListOfBool; +typedef daeTArray domGlsl_ListOfFloat; +typedef daeTArray domGlsl_ListOfInt; +typedef domGlsl_ListOfBool domGlsl_bool2; +typedef domGlsl_ListOfBool domGlsl_bool3; +typedef domGlsl_ListOfBool domGlsl_bool4; +typedef domGlsl_ListOfFloat domGlsl_float2; +typedef domGlsl_ListOfFloat domGlsl_float3; +typedef domGlsl_ListOfFloat domGlsl_float4; +typedef domGlsl_ListOfFloat domGlsl_float2x2; +typedef domGlsl_ListOfFloat domGlsl_float3x3; +typedef domGlsl_ListOfFloat domGlsl_float4x4; +typedef domGlsl_ListOfInt domGlsl_int2; +typedef domGlsl_ListOfInt domGlsl_int3; +typedef domGlsl_ListOfInt domGlsl_int4; +typedef xsToken domGlsl_identifier; +typedef xsBoolean domCg_bool; +typedef xsFloat domCg_float; +typedef xsInt domCg_int; +typedef xsFloat domCg_half; +typedef xsFloat domCg_fixed; +typedef xsBoolean domCg_bool1; +typedef xsFloat domCg_float1; +typedef xsInt domCg_int1; +typedef xsFloat domCg_half1; +typedef xsFloat domCg_fixed1; +typedef daeTArray domCg_ListOfBool; +typedef daeTArray domCg_ListOfFloat; +typedef daeTArray domCg_ListOfInt; +typedef daeTArray domCg_ListOfHalf; +typedef daeTArray domCg_ListOfFixed; +typedef domCg_ListOfBool domCg_bool2; +typedef domCg_ListOfBool domCg_bool3; +typedef domCg_ListOfBool domCg_bool4; +typedef domCg_ListOfBool domCg_bool1x1; +typedef domCg_ListOfBool domCg_bool1x2; +typedef domCg_ListOfBool domCg_bool1x3; +typedef domCg_ListOfBool domCg_bool1x4; +typedef domCg_ListOfBool domCg_bool2x1; +typedef domCg_ListOfBool domCg_bool2x2; +typedef domCg_ListOfBool domCg_bool2x3; +typedef domCg_ListOfBool domCg_bool2x4; +typedef domCg_ListOfBool domCg_bool3x1; +typedef domCg_ListOfBool domCg_bool3x2; +typedef domCg_ListOfBool domCg_bool3x3; +typedef domCg_ListOfBool domCg_bool3x4; +typedef domCg_ListOfBool domCg_bool4x1; +typedef domCg_ListOfBool domCg_bool4x2; +typedef domCg_ListOfBool domCg_bool4x3; +typedef domCg_ListOfBool domCg_bool4x4; +typedef domCg_ListOfFloat domCg_float2; +typedef domCg_ListOfFloat domCg_float3; +typedef domCg_ListOfFloat domCg_float4; +typedef domCg_ListOfFloat domCg_float1x1; +typedef domCg_ListOfFloat domCg_float1x2; +typedef domCg_ListOfFloat domCg_float1x3; +typedef domCg_ListOfFloat domCg_float1x4; +typedef domCg_ListOfFloat domCg_float2x1; +typedef domCg_ListOfFloat domCg_float2x2; +typedef domCg_ListOfFloat domCg_float2x3; +typedef domCg_ListOfFloat domCg_float2x4; +typedef domCg_ListOfFloat domCg_float3x1; +typedef domCg_ListOfFloat domCg_float3x2; +typedef domCg_ListOfFloat domCg_float3x3; +typedef domCg_ListOfFloat domCg_float3x4; +typedef domCg_ListOfFloat domCg_float4x1; +typedef domCg_ListOfFloat domCg_float4x2; +typedef domCg_ListOfFloat domCg_float4x3; +typedef domCg_ListOfFloat domCg_float4x4; +typedef domCg_ListOfInt domCg_int2; +typedef domCg_ListOfInt domCg_int3; +typedef domCg_ListOfInt domCg_int4; +typedef domCg_ListOfInt domCg_int1x1; +typedef domCg_ListOfInt domCg_int1x2; +typedef domCg_ListOfInt domCg_int1x3; +typedef domCg_ListOfInt domCg_int1x4; +typedef domCg_ListOfInt domCg_int2x1; +typedef domCg_ListOfInt domCg_int2x2; +typedef domCg_ListOfInt domCg_int2x3; +typedef domCg_ListOfInt domCg_int2x4; +typedef domCg_ListOfInt domCg_int3x1; +typedef domCg_ListOfInt domCg_int3x2; +typedef domCg_ListOfInt domCg_int3x3; +typedef domCg_ListOfInt domCg_int3x4; +typedef domCg_ListOfInt domCg_int4x1; +typedef domCg_ListOfInt domCg_int4x2; +typedef domCg_ListOfInt domCg_int4x3; +typedef domCg_ListOfInt domCg_int4x4; +typedef domCg_ListOfHalf domCg_half2; +typedef domCg_ListOfHalf domCg_half3; +typedef domCg_ListOfHalf domCg_half4; +typedef domCg_ListOfHalf domCg_half1x1; +typedef domCg_ListOfHalf domCg_half1x2; +typedef domCg_ListOfHalf domCg_half1x3; +typedef domCg_ListOfHalf domCg_half1x4; +typedef domCg_ListOfHalf domCg_half2x1; +typedef domCg_ListOfHalf domCg_half2x2; +typedef domCg_ListOfHalf domCg_half2x3; +typedef domCg_ListOfHalf domCg_half2x4; +typedef domCg_ListOfHalf domCg_half3x1; +typedef domCg_ListOfHalf domCg_half3x2; +typedef domCg_ListOfHalf domCg_half3x3; +typedef domCg_ListOfHalf domCg_half3x4; +typedef domCg_ListOfHalf domCg_half4x1; +typedef domCg_ListOfHalf domCg_half4x2; +typedef domCg_ListOfHalf domCg_half4x3; +typedef domCg_ListOfHalf domCg_half4x4; +typedef domCg_ListOfFixed domCg_fixed2; +typedef domCg_ListOfFixed domCg_fixed3; +typedef domCg_ListOfFixed domCg_fixed4; +typedef domCg_ListOfFixed domCg_fixed1x1; +typedef domCg_ListOfFixed domCg_fixed1x2; +typedef domCg_ListOfFixed domCg_fixed1x3; +typedef domCg_ListOfFixed domCg_fixed1x4; +typedef domCg_ListOfFixed domCg_fixed2x1; +typedef domCg_ListOfFixed domCg_fixed2x2; +typedef domCg_ListOfFixed domCg_fixed2x3; +typedef domCg_ListOfFixed domCg_fixed2x4; +typedef domCg_ListOfFixed domCg_fixed3x1; +typedef domCg_ListOfFixed domCg_fixed3x2; +typedef domCg_ListOfFixed domCg_fixed3x3; +typedef domCg_ListOfFixed domCg_fixed3x4; +typedef domCg_ListOfFixed domCg_fixed4x1; +typedef domCg_ListOfFixed domCg_fixed4x2; +typedef domCg_ListOfFixed domCg_fixed4x3; +typedef domCg_ListOfFixed domCg_fixed4x4; +typedef xsToken domCg_identifier; +typedef xsNonNegativeInteger domGLES_MAX_LIGHTS_index; +typedef xsNonNegativeInteger domGLES_MAX_CLIP_PLANES_index; +typedef xsNonNegativeInteger domGLES_MAX_TEXTURE_COORDS_index; +typedef xsNonNegativeInteger domGLES_MAX_TEXTURE_IMAGE_UNITS_index; +typedef xsNonNegativeInteger domGles_texcombiner_argument_index_type; +typedef xsNCName domGles_rendertarget_common; + +/** + * An enumuerated type specifying the acceptable morph methods. + */ +enum domMorphMethodType { + MORPHMETHODTYPE_NORMALIZED, + MORPHMETHODTYPE_RELATIVE, + MORPHMETHODTYPE_COUNT = 2 +}; + +/** + * An enumerated type specifying the acceptable node types. + */ +enum domNodeType { + NODETYPE_JOINT, + NODETYPE_NODE, + NODETYPE_COUNT = 2 +}; + +/** + * An enumerated type specifying the acceptable up-axis values. + */ +enum domUpAxisType { + UPAXISTYPE_X_UP, + UPAXISTYPE_Y_UP, + UPAXISTYPE_Z_UP, + UPAXISTYPE_COUNT = 3 +}; + +/** + * An enumerated type specifying the acceptable document versions. + */ +enum domVersionType { + VERSIONTYPE_1_4_0, + VERSIONTYPE_1_4_1, + VERSIONTYPE_COUNT = 2 +}; + +enum domFx_opaque_enum { + FX_OPAQUE_ENUM_A_ONE, /**< When a transparent opaque attribute is set to A_ONE, it means the transparency information will be taken from the alpha channel of the color, texture, or parameter supplying the value. The value of 1.0 is opaque in this mode. */ + FX_OPAQUE_ENUM_RGB_ZERO, /**< When a transparent opaque attribute is set to RGB_ZERO, it means the transparency information will be taken from the red, green, and blue channels of the color, texture, or parameter supplying the value. Each channel is modulated independently. The value of 0.0 is opaque in this mode. */ + FX_OPAQUE_ENUM_COUNT = 2 +}; + +enum domFx_surface_type_enum { + FX_SURFACE_TYPE_ENUM_UNTYPED, /**< When a surface's type attribute is set to UNTYPED, its type is initially unknown and established later by the context in which it is used, such as by a texture sampler that references it. A surface of any other type may be changed into an UNTYPED surface at run-time, as if it were created by , using . If there is a type mismatch between a operation and what the run-time decides the type should be, the result is profile- and platform-specific behavior. */ + FX_SURFACE_TYPE_ENUM_1D, + FX_SURFACE_TYPE_ENUM_2D, + FX_SURFACE_TYPE_ENUM_3D, + FX_SURFACE_TYPE_ENUM_RECT, + FX_SURFACE_TYPE_ENUM_CUBE, + FX_SURFACE_TYPE_ENUM_DEPTH, + FX_SURFACE_TYPE_ENUM_COUNT = 7 +}; + +enum domFx_surface_face_enum { + FX_SURFACE_FACE_ENUM_POSITIVE_X, + FX_SURFACE_FACE_ENUM_NEGATIVE_X, + FX_SURFACE_FACE_ENUM_POSITIVE_Y, + FX_SURFACE_FACE_ENUM_NEGATIVE_Y, + FX_SURFACE_FACE_ENUM_POSITIVE_Z, + FX_SURFACE_FACE_ENUM_NEGATIVE_Z, + FX_SURFACE_FACE_ENUM_COUNT = 6 +}; + +/** + * The per-texel layout of the format. The length of the string indicate + * how many channels there are and the letter respresents the name of the + * channel. There are typically 0 to 4 channels. + */ +enum domFx_surface_format_hint_channels_enum { + FX_SURFACE_FORMAT_HINT_CHANNELS_ENUM_RGB, /**< RGB color map */ + FX_SURFACE_FORMAT_HINT_CHANNELS_ENUM_RGBA, /**< RGB color + Alpha map often used for color + transparency or other things packed into channel A like specular power */ + FX_SURFACE_FORMAT_HINT_CHANNELS_ENUM_L, /**< Luminance map often used for light mapping */ + FX_SURFACE_FORMAT_HINT_CHANNELS_ENUM_LA, /**< Luminance+Alpha map often used for light mapping */ + FX_SURFACE_FORMAT_HINT_CHANNELS_ENUM_D, /**< Depth map often used for displacement, parellax, relief, or shadow mapping */ + FX_SURFACE_FORMAT_HINT_CHANNELS_ENUM_XYZ, /**< Typically used for normal maps or 3component displacement maps. */ + FX_SURFACE_FORMAT_HINT_CHANNELS_ENUM_XYZW, /**< Typically used for normal maps where W is the depth for relief or parrallax mapping */ + FX_SURFACE_FORMAT_HINT_CHANNELS_ENUM_COUNT = 7 +}; + +/** + * Each channel of the texel has a precision. Typically these are all linked + * together. An exact format lay lower the precision of an individual channel + * but applying a higher precision by linking the channels together may still + * convey the same information. + */ +enum domFx_surface_format_hint_precision_enum { + FX_SURFACE_FORMAT_HINT_PRECISION_ENUM_LOW, /**< For integers this typically represents 8 bits. For floats typically 16 bits. */ + FX_SURFACE_FORMAT_HINT_PRECISION_ENUM_MID, /**< For integers this typically represents 8 to 24 bits. For floats typically 16 to 32 bits. */ + FX_SURFACE_FORMAT_HINT_PRECISION_ENUM_HIGH, /**< For integers this typically represents 16 to 32 bits. For floats typically 24 to 32 bits. */ + FX_SURFACE_FORMAT_HINT_PRECISION_ENUM_COUNT = 3 +}; + +/** + * Each channel represents a range of values. Some example ranges are signed + * or unsigned integers, or between between a clamped range such as 0.0f to + * 1.0f, or high dynamic range via floating point + */ +enum domFx_surface_format_hint_range_enum { + FX_SURFACE_FORMAT_HINT_RANGE_ENUM_SNORM, /**< Format is representing a decimal value that remains within the -1 to 1 range. Implimentation could be integer-fixedpoint or floats. */ + FX_SURFACE_FORMAT_HINT_RANGE_ENUM_UNORM, /**< Format is representing a decimal value that remains within the 0 to 1 range. Implimentation could be integer-fixedpoint or floats. */ + FX_SURFACE_FORMAT_HINT_RANGE_ENUM_SINT, /**< Format is representing signed integer numbers. (ex. 8bits = -128 to 127) */ + FX_SURFACE_FORMAT_HINT_RANGE_ENUM_UINT, /**< Format is representing unsigned integer numbers. (ex. 8bits = 0 to 255) */ + FX_SURFACE_FORMAT_HINT_RANGE_ENUM_FLOAT, /**< Format should support full floating point ranges. High precision is expected to be 32bit. Mid precision may be 16 to 32 bit. Low precision is expected to be 16 bit. */ + FX_SURFACE_FORMAT_HINT_RANGE_ENUM_COUNT = 5 +}; + +/** + * Additional hints about data relationships and other things to help the + * application pick the best format. + */ +enum domFx_surface_format_hint_option_enum { + FX_SURFACE_FORMAT_HINT_OPTION_ENUM_SRGB_GAMMA, /**< colors are stored with respect to the sRGB 2.2 gamma curve rather than linear */ + FX_SURFACE_FORMAT_HINT_OPTION_ENUM_NORMALIZED3, /**< the texel's XYZ/RGB should be normalized such as in a normal map. */ + FX_SURFACE_FORMAT_HINT_OPTION_ENUM_NORMALIZED4, /**< the texel's XYZW/RGBA should be normalized such as in a normal map. */ + FX_SURFACE_FORMAT_HINT_OPTION_ENUM_COMPRESSABLE, /**< The surface may use run-time compression. Considering the best compression based on desired, channel, range, precision, and options */ + FX_SURFACE_FORMAT_HINT_OPTION_ENUM_COUNT = 4 +}; + +enum domFx_sampler_wrap_common { + FX_SAMPLER_WRAP_COMMON_NONE, + FX_SAMPLER_WRAP_COMMON_WRAP, + FX_SAMPLER_WRAP_COMMON_MIRROR, + FX_SAMPLER_WRAP_COMMON_CLAMP, + FX_SAMPLER_WRAP_COMMON_BORDER, + FX_SAMPLER_WRAP_COMMON_COUNT = 5 +}; + +enum domFx_sampler_filter_common { + FX_SAMPLER_FILTER_COMMON_NONE, + FX_SAMPLER_FILTER_COMMON_NEAREST, + FX_SAMPLER_FILTER_COMMON_LINEAR, + FX_SAMPLER_FILTER_COMMON_NEAREST_MIPMAP_NEAREST, + FX_SAMPLER_FILTER_COMMON_LINEAR_MIPMAP_NEAREST, + FX_SAMPLER_FILTER_COMMON_NEAREST_MIPMAP_LINEAR, + FX_SAMPLER_FILTER_COMMON_LINEAR_MIPMAP_LINEAR, + FX_SAMPLER_FILTER_COMMON_COUNT = 7 +}; + +enum domFx_modifier_enum_common { + FX_MODIFIER_ENUM_COMMON_CONST, + FX_MODIFIER_ENUM_COMMON_UNIFORM, + FX_MODIFIER_ENUM_COMMON_VARYING, + FX_MODIFIER_ENUM_COMMON_STATIC, + FX_MODIFIER_ENUM_COMMON_VOLATILE, + FX_MODIFIER_ENUM_COMMON_EXTERN, + FX_MODIFIER_ENUM_COMMON_SHARED, + FX_MODIFIER_ENUM_COMMON_COUNT = 7 +}; + +enum domFx_pipeline_stage_common { + FX_PIPELINE_STAGE_COMMON_VERTEXPROGRAM, + FX_PIPELINE_STAGE_COMMON_FRAGMENTPROGRAM, + FX_PIPELINE_STAGE_COMMON_VERTEXSHADER, + FX_PIPELINE_STAGE_COMMON_PIXELSHADER, + FX_PIPELINE_STAGE_COMMON_COUNT = 4 +}; + +enum domGl_blend_type { + GL_BLEND_TYPE_ZERO = 0x0, + GL_BLEND_TYPE_ONE = 0x1, + GL_BLEND_TYPE_SRC_COLOR = 0x0300, + GL_BLEND_TYPE_ONE_MINUS_SRC_COLOR = 0x0301, + GL_BLEND_TYPE_DEST_COLOR = 0x0306, + GL_BLEND_TYPE_ONE_MINUS_DEST_COLOR = 0x0307, + GL_BLEND_TYPE_SRC_ALPHA = 0x0302, + GL_BLEND_TYPE_ONE_MINUS_SRC_ALPHA = 0x0303, + GL_BLEND_TYPE_DST_ALPHA = 0x0304, + GL_BLEND_TYPE_ONE_MINUS_DST_ALPHA = 0x0305, + GL_BLEND_TYPE_CONSTANT_COLOR = 0x8001, + GL_BLEND_TYPE_ONE_MINUS_CONSTANT_COLOR = 0x8002, + GL_BLEND_TYPE_CONSTANT_ALPHA = 0x8003, + GL_BLEND_TYPE_ONE_MINUS_CONSTANT_ALPHA = 0x8004, + GL_BLEND_TYPE_SRC_ALPHA_SATURATE = 0x0308, + GL_BLEND_TYPE_COUNT = 15 +}; + +enum domGl_face_type { + GL_FACE_TYPE_FRONT = 0x0404, + GL_FACE_TYPE_BACK = 0x0405, + GL_FACE_TYPE_FRONT_AND_BACK = 0x0408, + GL_FACE_TYPE_COUNT = 3 +}; + +enum domGl_blend_equation_type { + GL_BLEND_EQUATION_TYPE_FUNC_ADD = 0x8006, + GL_BLEND_EQUATION_TYPE_FUNC_SUBTRACT = 0x800A, + GL_BLEND_EQUATION_TYPE_FUNC_REVERSE_SUBTRACT = 0x800B, + GL_BLEND_EQUATION_TYPE_MIN = 0x8007, + GL_BLEND_EQUATION_TYPE_MAX = 0x8008, + GL_BLEND_EQUATION_TYPE_COUNT = 5 +}; + +enum domGl_func_type { + GL_FUNC_TYPE_NEVER = 0x0200, + GL_FUNC_TYPE_LESS = 0x0201, + GL_FUNC_TYPE_LEQUAL = 0x0203, + GL_FUNC_TYPE_EQUAL = 0x0202, + GL_FUNC_TYPE_GREATER = 0x0204, + GL_FUNC_TYPE_NOTEQUAL = 0x0205, + GL_FUNC_TYPE_GEQUAL = 0x0206, + GL_FUNC_TYPE_ALWAYS = 0x0207, + GL_FUNC_TYPE_COUNT = 8 +}; + +enum domGl_stencil_op_type { + GL_STENCIL_OP_TYPE_KEEP = 0x1E00, + GL_STENCIL_OP_TYPE_ZERO = 0x0, + GL_STENCIL_OP_TYPE_REPLACE = 0x1E01, + GL_STENCIL_OP_TYPE_INCR = 0x1E02, + GL_STENCIL_OP_TYPE_DECR = 0x1E03, + GL_STENCIL_OP_TYPE_INVERT = 0x150A, + GL_STENCIL_OP_TYPE_INCR_WRAP = 0x8507, + GL_STENCIL_OP_TYPE_DECR_WRAP = 0x8508, + GL_STENCIL_OP_TYPE_COUNT = 8 +}; + +enum domGl_material_type { + GL_MATERIAL_TYPE_EMISSION = 0x1600, + GL_MATERIAL_TYPE_AMBIENT = 0x1200, + GL_MATERIAL_TYPE_DIFFUSE = 0x1201, + GL_MATERIAL_TYPE_SPECULAR = 0x1202, + GL_MATERIAL_TYPE_AMBIENT_AND_DIFFUSE = 0x1602, + GL_MATERIAL_TYPE_COUNT = 5 +}; + +enum domGl_fog_type { + GL_FOG_TYPE_LINEAR = 0x2601, + GL_FOG_TYPE_EXP = 0x0800, + GL_FOG_TYPE_EXP2 = 0x0801, + GL_FOG_TYPE_COUNT = 3 +}; + +enum domGl_fog_coord_src_type { + GL_FOG_COORD_SRC_TYPE_FOG_COORDINATE = 0x8451, + GL_FOG_COORD_SRC_TYPE_FRAGMENT_DEPTH = 0x8452, + GL_FOG_COORD_SRC_TYPE_COUNT = 2 +}; + +enum domGl_front_face_type { + GL_FRONT_FACE_TYPE_CW = 0x0900, + GL_FRONT_FACE_TYPE_CCW = 0x0901, + GL_FRONT_FACE_TYPE_COUNT = 2 +}; + +enum domGl_light_model_color_control_type { + GL_LIGHT_MODEL_COLOR_CONTROL_TYPE_SINGLE_COLOR = 0x81F9, + GL_LIGHT_MODEL_COLOR_CONTROL_TYPE_SEPARATE_SPECULAR_COLOR = 0x81FA, + GL_LIGHT_MODEL_COLOR_CONTROL_TYPE_COUNT = 2 +}; + +enum domGl_logic_op_type { + GL_LOGIC_OP_TYPE_CLEAR = 0x1500, + GL_LOGIC_OP_TYPE_AND = 0x1501, + GL_LOGIC_OP_TYPE_AND_REVERSE = 0x1502, + GL_LOGIC_OP_TYPE_COPY = 0x1503, + GL_LOGIC_OP_TYPE_AND_INVERTED = 0x1504, + GL_LOGIC_OP_TYPE_NOOP = 0x1505, + GL_LOGIC_OP_TYPE_XOR = 0x1506, + GL_LOGIC_OP_TYPE_OR = 0x1507, + GL_LOGIC_OP_TYPE_NOR = 0x1508, + GL_LOGIC_OP_TYPE_EQUIV = 0x1509, + GL_LOGIC_OP_TYPE_INVERT = 0x150A, + GL_LOGIC_OP_TYPE_OR_REVERSE = 0x150B, + GL_LOGIC_OP_TYPE_COPY_INVERTED = 0x150C, + GL_LOGIC_OP_TYPE_NAND = 0x150E, + GL_LOGIC_OP_TYPE_SET = 0x150F, + GL_LOGIC_OP_TYPE_COUNT = 15 +}; + +enum domGl_polygon_mode_type { + GL_POLYGON_MODE_TYPE_POINT = 0x1B00, + GL_POLYGON_MODE_TYPE_LINE = 0x1B01, + GL_POLYGON_MODE_TYPE_FILL = 0x1B02, + GL_POLYGON_MODE_TYPE_COUNT = 3 +}; + +enum domGl_shade_model_type { + GL_SHADE_MODEL_TYPE_FLAT = 0x1D00, + GL_SHADE_MODEL_TYPE_SMOOTH = 0x1D01, + GL_SHADE_MODEL_TYPE_COUNT = 2 +}; + +enum domGlsl_pipeline_stage { + GLSL_PIPELINE_STAGE_VERTEXPROGRAM, + GLSL_PIPELINE_STAGE_FRAGMENTPROGRAM, + GLSL_PIPELINE_STAGE_COUNT = 2 +}; + +enum domCg_pipeline_stage { + CG_PIPELINE_STAGE_VERTEX, + CG_PIPELINE_STAGE_FRAGMENT, + CG_PIPELINE_STAGE_COUNT = 2 +}; + +enum domGles_texenv_mode_enums { + GLES_TEXENV_MODE_ENUMS_REPLACE = 0x1E01, + GLES_TEXENV_MODE_ENUMS_MODULATE = 0x2100, + GLES_TEXENV_MODE_ENUMS_DECAL = 0x2101, + GLES_TEXENV_MODE_ENUMS_BLEND = 0x0BE2, + GLES_TEXENV_MODE_ENUMS_ADD = 0x0104, + GLES_TEXENV_MODE_ENUMS_COUNT = 5 +}; + +enum domGles_texcombiner_operatorRGB_enums { + GLES_TEXCOMBINER_OPERATORRGB_ENUMS_REPLACE = 0x1E01, + GLES_TEXCOMBINER_OPERATORRGB_ENUMS_MODULATE = 0x2100, + GLES_TEXCOMBINER_OPERATORRGB_ENUMS_ADD = 0x0104, + GLES_TEXCOMBINER_OPERATORRGB_ENUMS_ADD_SIGNED = 0x8574, + GLES_TEXCOMBINER_OPERATORRGB_ENUMS_INTERPOLATE = 0x8575, + GLES_TEXCOMBINER_OPERATORRGB_ENUMS_SUBTRACT = 0x84E7, + GLES_TEXCOMBINER_OPERATORRGB_ENUMS_DOT3_RGB = 0x86AE, + GLES_TEXCOMBINER_OPERATORRGB_ENUMS_DOT3_RGBA = 0x86AF, + GLES_TEXCOMBINER_OPERATORRGB_ENUMS_COUNT = 8 +}; + +enum domGles_texcombiner_operatorAlpha_enums { + GLES_TEXCOMBINER_OPERATORALPHA_ENUMS_REPLACE = 0x1E01, + GLES_TEXCOMBINER_OPERATORALPHA_ENUMS_MODULATE = 0x2100, + GLES_TEXCOMBINER_OPERATORALPHA_ENUMS_ADD = 0x0104, + GLES_TEXCOMBINER_OPERATORALPHA_ENUMS_ADD_SIGNED = 0x8574, + GLES_TEXCOMBINER_OPERATORALPHA_ENUMS_INTERPOLATE = 0x8575, + GLES_TEXCOMBINER_OPERATORALPHA_ENUMS_SUBTRACT = 0x84E7, + GLES_TEXCOMBINER_OPERATORALPHA_ENUMS_COUNT = 6 +}; + +enum domGles_texcombiner_source_enums { + GLES_TEXCOMBINER_SOURCE_ENUMS_TEXTURE = 0x1702, + GLES_TEXCOMBINER_SOURCE_ENUMS_CONSTANT = 0x8576, + GLES_TEXCOMBINER_SOURCE_ENUMS_PRIMARY = 0x8577, + GLES_TEXCOMBINER_SOURCE_ENUMS_PREVIOUS = 0x8578, + GLES_TEXCOMBINER_SOURCE_ENUMS_COUNT = 4 +}; + +enum domGles_texcombiner_operandRGB_enums { + GLES_TEXCOMBINER_OPERANDRGB_ENUMS_SRC_COLOR = 0x0300, + GLES_TEXCOMBINER_OPERANDRGB_ENUMS_ONE_MINUS_SRC_COLOR = 0x0301, + GLES_TEXCOMBINER_OPERANDRGB_ENUMS_SRC_ALPHA = 0x0302, + GLES_TEXCOMBINER_OPERANDRGB_ENUMS_ONE_MINUS_SRC_ALPHA = 0x0303, + GLES_TEXCOMBINER_OPERANDRGB_ENUMS_COUNT = 4 +}; + +enum domGles_texcombiner_operandAlpha_enums { + GLES_TEXCOMBINER_OPERANDALPHA_ENUMS_SRC_ALPHA = 0x0302, + GLES_TEXCOMBINER_OPERANDALPHA_ENUMS_ONE_MINUS_SRC_ALPHA = 0x0303, + GLES_TEXCOMBINER_OPERANDALPHA_ENUMS_COUNT = 2 +}; + +enum domGles_sampler_wrap { + GLES_SAMPLER_WRAP_REPEAT, + GLES_SAMPLER_WRAP_CLAMP, + GLES_SAMPLER_WRAP_CLAMP_TO_EDGE, + GLES_SAMPLER_WRAP_MIRRORED_REPEAT, /**< supported by GLES 1.1 only */ + GLES_SAMPLER_WRAP_COUNT = 4 +}; + +enum domGles_stencil_op_type { + GLES_STENCIL_OP_TYPE_KEEP = 0x1E00, + GLES_STENCIL_OP_TYPE_ZERO = 0x0, + GLES_STENCIL_OP_TYPE_REPLACE = 0x1E01, + GLES_STENCIL_OP_TYPE_INCR = 0x1E02, + GLES_STENCIL_OP_TYPE_DECR = 0x1E03, + GLES_STENCIL_OP_TYPE_INVERT = 0x150A, + GLES_STENCIL_OP_TYPE_COUNT = 6 +}; + +enum domSpringType { + SPRINGTYPE_LINEAR, + SPRINGTYPE_ANGULAR, + SPRINGTYPE_COUNT = 2 +}; + +enum domGl_enumeration { + GL_ENUMERATION_ZERO = 0x0, + GL_ENUMERATION_ONE = 0x1, + GL_ENUMERATION_SRC_COLOR = 0x0300, + GL_ENUMERATION_ONE_MINUS_SRC_COLOR = 0x0301, + GL_ENUMERATION_DEST_COLOR = 0x0306, + GL_ENUMERATION_ONE_MINUS_DEST_COLOR = 0x0307, + GL_ENUMERATION_SRC_ALPHA = 0x0302, + GL_ENUMERATION_ONE_MINUS_SRC_ALPHA = 0x0303, + GL_ENUMERATION_DST_ALPHA = 0x0304, + GL_ENUMERATION_ONE_MINUS_DST_ALPHA = 0x0305, + GL_ENUMERATION_CONSTANT_COLOR = 0x8001, + GL_ENUMERATION_ONE_MINUS_CONSTANT_COLOR = 0x8002, + GL_ENUMERATION_CONSTANT_ALPHA = 0x8003, + GL_ENUMERATION_ONE_MINUS_CONSTANT_ALPHA = 0x8004, + GL_ENUMERATION_SRC_ALPHA_SATURATE = 0x0308, + GL_ENUMERATION_FRONT = 0x0404, + GL_ENUMERATION_BACK = 0x0405, + GL_ENUMERATION_FRONT_AND_BACK = 0x0408, + GL_ENUMERATION_FUNC_ADD = 0x8006, + GL_ENUMERATION_FUNC_SUBTRACT = 0x800A, + GL_ENUMERATION_FUNC_REVERSE_SUBTRACT = 0x800B, + GL_ENUMERATION_MIN = 0x8007, + GL_ENUMERATION_MAX = 0x8008, + GL_ENUMERATION_NEVER = 0x0200, + GL_ENUMERATION_LESS = 0x0201, + GL_ENUMERATION_LEQUAL = 0x0203, + GL_ENUMERATION_EQUAL = 0x0202, + GL_ENUMERATION_GREATER = 0x0204, + GL_ENUMERATION_NOTEQUAL = 0x0205, + GL_ENUMERATION_GEQUAL = 0x0206, + GL_ENUMERATION_ALWAYS = 0x0207, + GL_ENUMERATION_KEEP = 0x1E00, + GL_ENUMERATION_REPLACE = 0x1E01, + GL_ENUMERATION_INCR = 0x1E02, + GL_ENUMERATION_DECR = 0x1E03, + GL_ENUMERATION_INVERT = 0x150A, + GL_ENUMERATION_INCR_WRAP = 0x8507, + GL_ENUMERATION_DECR_WRAP = 0x8508, + GL_ENUMERATION_EMISSION = 0x1600, + GL_ENUMERATION_AMBIENT = 0x1200, + GL_ENUMERATION_DIFFUSE = 0x1201, + GL_ENUMERATION_SPECULAR = 0x1202, + GL_ENUMERATION_AMBIENT_AND_DIFFUSE = 0x1602, + GL_ENUMERATION_LINEAR = 0x2601, + GL_ENUMERATION_EXP = 0x0800, + GL_ENUMERATION_EXP2 = 0x0801, + GL_ENUMERATION_FOG_COORDINATE = 0x8451, + GL_ENUMERATION_FRAGMENT_DEPTH = 0x8452, + GL_ENUMERATION_CW = 0x0900, + GL_ENUMERATION_CCW = 0x0901, + GL_ENUMERATION_SINGLE_COLOR = 0x81F9, + GL_ENUMERATION_SEPARATE_SPECULAR_COLOR = 0x81FA, + GL_ENUMERATION_CLEAR = 0x1500, + GL_ENUMERATION_AND = 0x1501, + GL_ENUMERATION_AND_REVERSE = 0x1502, + GL_ENUMERATION_COPY = 0x1503, + GL_ENUMERATION_AND_INVERTED = 0x1504, + GL_ENUMERATION_NOOP = 0x1505, + GL_ENUMERATION_XOR = 0x1506, + GL_ENUMERATION_OR = 0x1507, + GL_ENUMERATION_NOR = 0x1508, + GL_ENUMERATION_EQUIV = 0x1509, + GL_ENUMERATION_OR_REVERSE = 0x150B, + GL_ENUMERATION_COPY_INVERTED = 0x150C, + GL_ENUMERATION_NAND = 0x150E, + GL_ENUMERATION_SET = 0x150F, + GL_ENUMERATION_POINT = 0x1B00, + GL_ENUMERATION_LINE = 0x1B01, + GL_ENUMERATION_FILL = 0x1B02, + GL_ENUMERATION_FLAT = 0x1D00, + GL_ENUMERATION_SMOOTH = 0x1D01, + GL_ENUMERATION_COUNT = 72 +}; + +enum domGles_enumeration { + GLES_ENUMERATION_ZERO = 0x0, + GLES_ENUMERATION_ONE = 0x1, + GLES_ENUMERATION_SRC_COLOR = 0x0300, + GLES_ENUMERATION_ONE_MINUS_SRC_COLOR = 0x0301, + GLES_ENUMERATION_DEST_COLOR = 0x0306, + GLES_ENUMERATION_ONE_MINUS_DEST_COLOR = 0x0307, + GLES_ENUMERATION_SRC_ALPHA = 0x0302, + GLES_ENUMERATION_ONE_MINUS_SRC_ALPHA = 0x0303, + GLES_ENUMERATION_DST_ALPHA = 0x0304, + GLES_ENUMERATION_ONE_MINUS_DST_ALPHA = 0x0305, + GLES_ENUMERATION_CONSTANT_COLOR = 0x8001, + GLES_ENUMERATION_ONE_MINUS_CONSTANT_COLOR = 0x8002, + GLES_ENUMERATION_CONSTANT_ALPHA = 0x8003, + GLES_ENUMERATION_ONE_MINUS_CONSTANT_ALPHA = 0x8004, + GLES_ENUMERATION_SRC_ALPHA_SATURATE = 0x0308, + GLES_ENUMERATION_FRONT = 0x0404, + GLES_ENUMERATION_BACK = 0x0405, + GLES_ENUMERATION_FRONT_AND_BACK = 0x0408, + GLES_ENUMERATION_NEVER = 0x0200, + GLES_ENUMERATION_LESS = 0x0201, + GLES_ENUMERATION_LEQUAL = 0x0203, + GLES_ENUMERATION_EQUAL = 0x0202, + GLES_ENUMERATION_GREATER = 0x0204, + GLES_ENUMERATION_NOTEQUAL = 0x0205, + GLES_ENUMERATION_GEQUAL = 0x0206, + GLES_ENUMERATION_ALWAYS = 0x0207, + GLES_ENUMERATION_KEEP = 0x1E00, + GLES_ENUMERATION_REPLACE = 0x1E01, + GLES_ENUMERATION_INCR = 0x1E02, + GLES_ENUMERATION_DECR = 0x1E03, + GLES_ENUMERATION_INVERT = 0x150A, + GLES_ENUMERATION_INCR_WRAP = 0x8507, + GLES_ENUMERATION_DECR_WRAP = 0x8508, + GLES_ENUMERATION_EMISSION = 0x1600, + GLES_ENUMERATION_AMBIENT = 0x1200, + GLES_ENUMERATION_DIFFUSE = 0x1201, + GLES_ENUMERATION_SPECULAR = 0x1202, + GLES_ENUMERATION_AMBIENT_AND_DIFFUSE = 0x1602, + GLES_ENUMERATION_LINEAR = 0x2601, + GLES_ENUMERATION_EXP = 0x0800, + GLES_ENUMERATION_EXP2 = 0x0801, + GLES_ENUMERATION_CW = 0x0900, + GLES_ENUMERATION_CCW = 0x0901, + GLES_ENUMERATION_SINGLE_COLOR = 0x81F9, + GLES_ENUMERATION_SEPARATE_SPECULAR_COLOR = 0x81FA, + GLES_ENUMERATION_CLEAR = 0x1500, + GLES_ENUMERATION_AND = 0x1501, + GLES_ENUMERATION_AND_REVERSE = 0x1502, + GLES_ENUMERATION_COPY = 0x1503, + GLES_ENUMERATION_AND_INVERTED = 0x1504, + GLES_ENUMERATION_NOOP = 0x1505, + GLES_ENUMERATION_XOR = 0x1506, + GLES_ENUMERATION_OR = 0x1507, + GLES_ENUMERATION_NOR = 0x1508, + GLES_ENUMERATION_EQUIV = 0x1509, + GLES_ENUMERATION_OR_REVERSE = 0x150B, + GLES_ENUMERATION_COPY_INVERTED = 0x150C, + GLES_ENUMERATION_NAND = 0x150E, + GLES_ENUMERATION_SET = 0x150F, + GLES_ENUMERATION_POINT = 0x1B00, + GLES_ENUMERATION_LINE = 0x1B01, + GLES_ENUMERATION_FILL = 0x1B02, + GLES_ENUMERATION_FLAT = 0x1D00, + GLES_ENUMERATION_SMOOTH = 0x1D01, + GLES_ENUMERATION_COUNT = 65 +}; + +//Element Type Enum +namespace COLLADA_TYPE +{ + const int + NO_TYPE = 0, + ANY = 1, + INPUTGLOBAL = 2, + INPUTLOCAL = 3, + INPUTLOCALOFFSET = 4, + INSTANCEWITHEXTRA = 5, + TARGETABLEFLOAT = 6, + TARGETABLEFLOAT3 = 7, + FX_SURFACE_FORMAT_HINT_COMMON = 8, + CHANNELS = 9, + RANGE = 10, + PRECISION = 11, + OPTION = 12, + FX_SURFACE_INIT_PLANAR_COMMON = 13, + ALL = 14, + FX_SURFACE_INIT_VOLUME_COMMON = 15, + PRIMARY = 16, + FX_SURFACE_INIT_CUBE_COMMON = 17, + ORDER = 18, + FACE = 19, + FX_SURFACE_INIT_FROM_COMMON = 20, + FX_SURFACE_COMMON = 21, + FORMAT = 22, + SIZE = 23, + VIEWPORT_RATIO = 24, + MIP_LEVELS = 25, + MIPMAP_GENERATE = 26, + FX_SAMPLER1D_COMMON = 27, + SOURCE = 28, + WRAP_S = 29, + MINFILTER = 30, + MAGFILTER = 31, + MIPFILTER = 32, + BORDER_COLOR = 33, + MIPMAP_MAXLEVEL = 34, + MIPMAP_BIAS = 35, + FX_SAMPLER2D_COMMON = 36, + WRAP_T = 37, + FX_SAMPLER3D_COMMON = 38, + WRAP_P = 39, + FX_SAMPLERCUBE_COMMON = 40, + FX_SAMPLERRECT_COMMON = 41, + FX_SAMPLERDEPTH_COMMON = 42, + FX_COLORTARGET_COMMON = 43, + FX_DEPTHTARGET_COMMON = 44, + FX_STENCILTARGET_COMMON = 45, + FX_CLEARCOLOR_COMMON = 46, + FX_CLEARDEPTH_COMMON = 47, + FX_CLEARSTENCIL_COMMON = 48, + FX_ANNOTATE_COMMON = 49, + FX_INCLUDE_COMMON = 50, + FX_NEWPARAM_COMMON = 51, + SEMANTIC = 52, + MODIFIER = 53, + FX_CODE_PROFILE = 54, + GL_SAMPLER1D = 55, + GL_SAMPLER2D = 56, + GL_SAMPLER3D = 57, + GL_SAMPLERCUBE = 58, + GL_SAMPLERRECT = 59, + GL_SAMPLERDEPTH = 60, + GLSL_NEWARRAY_TYPE = 61, + GLSL_SETARRAY_TYPE = 62, + GLSL_SURFACE_TYPE = 63, + GENERATOR = 64, + NAME = 65, + GLSL_NEWPARAM = 66, + GLSL_SETPARAM_SIMPLE = 67, + GLSL_SETPARAM = 68, + COMMON_FLOAT_OR_PARAM_TYPE = 69, + FLOAT = 70, + PARAM = 71, + COMMON_COLOR_OR_TEXTURE_TYPE = 72, + COLOR = 73, + TEXTURE = 74, + COMMON_TRANSPARENT_TYPE = 75, + COMMON_NEWPARAM_TYPE = 76, + FLOAT2 = 77, + FLOAT3 = 78, + FLOAT4 = 79, + CG_SAMPLER1D = 80, + CG_SAMPLER2D = 81, + CG_SAMPLER3D = 82, + CG_SAMPLERCUBE = 83, + CG_SAMPLERRECT = 84, + CG_SAMPLERDEPTH = 85, + CG_CONNECT_PARAM = 86, + CG_NEWARRAY_TYPE = 87, + CG_SETARRAY_TYPE = 88, + CG_SETUSER_TYPE = 89, + CG_SURFACE_TYPE = 90, + CG_NEWPARAM = 91, + CG_SETPARAM_SIMPLE = 92, + CG_SETPARAM = 93, + GLES_TEXTURE_CONSTANT_TYPE = 94, + GLES_TEXENV_COMMAND_TYPE = 95, + GLES_TEXCOMBINER_ARGUMENTRGB_TYPE = 96, + GLES_TEXCOMBINER_ARGUMENTALPHA_TYPE = 97, + GLES_TEXCOMBINER_COMMANDRGB_TYPE = 98, + GLES_TEXCOMBINER_COMMANDALPHA_TYPE = 99, + GLES_TEXCOMBINER_COMMAND_TYPE = 100, + GLES_TEXTURE_PIPELINE = 101, + GLES_TEXTURE_UNIT = 102, + SURFACE = 103, + SAMPLER_STATE = 104, + TEXCOORD = 105, + GLES_SAMPLER_STATE = 106, + GLES_NEWPARAM = 107, + FX_SURFACE_INIT_COMMON = 108, + INIT_AS_NULL = 109, + INIT_AS_TARGET = 110, + FX_ANNOTATE_TYPE_COMMON = 111, + BOOL = 112, + BOOL2 = 113, + BOOL3 = 114, + BOOL4 = 115, + INT = 116, + INT2 = 117, + INT3 = 118, + INT4 = 119, + FLOAT2X2 = 120, + FLOAT3X3 = 121, + FLOAT4X4 = 122, + STRING = 123, + FX_BASIC_TYPE_COMMON = 124, + FLOAT1X1 = 125, + FLOAT1X2 = 126, + FLOAT1X3 = 127, + FLOAT1X4 = 128, + FLOAT2X1 = 129, + FLOAT2X3 = 130, + FLOAT2X4 = 131, + FLOAT3X1 = 132, + FLOAT3X2 = 133, + FLOAT3X4 = 134, + FLOAT4X1 = 135, + FLOAT4X2 = 136, + FLOAT4X3 = 137, + ENUM = 138, + GL_PIPELINE_SETTINGS = 139, + ALPHA_FUNC = 140, + FUNC = 141, + VALUE = 142, + BLEND_FUNC = 143, + SRC = 144, + DEST = 145, + BLEND_FUNC_SEPARATE = 146, + SRC_RGB = 147, + DEST_RGB = 148, + SRC_ALPHA = 149, + DEST_ALPHA = 150, + BLEND_EQUATION = 151, + BLEND_EQUATION_SEPARATE = 152, + RGB = 153, + ALPHA = 154, + COLOR_MATERIAL = 155, + MODE = 156, + CULL_FACE = 157, + DEPTH_FUNC = 158, + FOG_MODE = 159, + FOG_COORD_SRC = 160, + FRONT_FACE = 161, + LIGHT_MODEL_COLOR_CONTROL = 162, + LOGIC_OP = 163, + POLYGON_MODE = 164, + SHADE_MODEL = 165, + STENCIL_FUNC = 166, + REF = 167, + MASK = 168, + STENCIL_OP = 169, + FAIL = 170, + ZFAIL = 171, + ZPASS = 172, + STENCIL_FUNC_SEPARATE = 173, + FRONT = 174, + BACK = 175, + STENCIL_OP_SEPARATE = 176, + STENCIL_MASK_SEPARATE = 177, + LIGHT_ENABLE = 178, + LIGHT_AMBIENT = 179, + LIGHT_DIFFUSE = 180, + LIGHT_SPECULAR = 181, + LIGHT_POSITION = 182, + LIGHT_CONSTANT_ATTENUATION = 183, + LIGHT_LINEAR_ATTENUATION = 184, + LIGHT_QUADRATIC_ATTENUATION = 185, + LIGHT_SPOT_CUTOFF = 186, + LIGHT_SPOT_DIRECTION = 187, + LIGHT_SPOT_EXPONENT = 188, + TEXTURE1D = 189, + TEXTURE2D = 190, + TEXTURE3D = 191, + TEXTURECUBE = 192, + TEXTURERECT = 193, + TEXTUREDEPTH = 194, + TEXTURE1D_ENABLE = 195, + TEXTURE2D_ENABLE = 196, + TEXTURE3D_ENABLE = 197, + TEXTURECUBE_ENABLE = 198, + TEXTURERECT_ENABLE = 199, + TEXTUREDEPTH_ENABLE = 200, + TEXTURE_ENV_COLOR = 201, + TEXTURE_ENV_MODE = 202, + CLIP_PLANE = 203, + CLIP_PLANE_ENABLE = 204, + BLEND_COLOR = 205, + CLEAR_COLOR = 206, + CLEAR_STENCIL = 207, + CLEAR_DEPTH = 208, + COLOR_MASK = 209, + DEPTH_BOUNDS = 210, + DEPTH_MASK = 211, + DEPTH_RANGE = 212, + FOG_DENSITY = 213, + FOG_START = 214, + FOG_END = 215, + FOG_COLOR = 216, + LIGHT_MODEL_AMBIENT = 217, + LIGHTING_ENABLE = 218, + LINE_STIPPLE = 219, + LINE_WIDTH = 220, + MATERIAL_AMBIENT = 221, + MATERIAL_DIFFUSE = 222, + MATERIAL_EMISSION = 223, + MATERIAL_SHININESS = 224, + MATERIAL_SPECULAR = 225, + MODEL_VIEW_MATRIX = 226, + POINT_DISTANCE_ATTENUATION = 227, + POINT_FADE_THRESHOLD_SIZE = 228, + POINT_SIZE = 229, + POINT_SIZE_MIN = 230, + POINT_SIZE_MAX = 231, + POLYGON_OFFSET = 232, + PROJECTION_MATRIX = 233, + SCISSOR = 234, + STENCIL_MASK = 235, + ALPHA_TEST_ENABLE = 236, + AUTO_NORMAL_ENABLE = 237, + BLEND_ENABLE = 238, + COLOR_LOGIC_OP_ENABLE = 239, + COLOR_MATERIAL_ENABLE = 240, + CULL_FACE_ENABLE = 241, + DEPTH_BOUNDS_ENABLE = 242, + DEPTH_CLAMP_ENABLE = 243, + DEPTH_TEST_ENABLE = 244, + DITHER_ENABLE = 245, + FOG_ENABLE = 246, + LIGHT_MODEL_LOCAL_VIEWER_ENABLE = 247, + LIGHT_MODEL_TWO_SIDE_ENABLE = 248, + LINE_SMOOTH_ENABLE = 249, + LINE_STIPPLE_ENABLE = 250, + LOGIC_OP_ENABLE = 251, + MULTISAMPLE_ENABLE = 252, + NORMALIZE_ENABLE = 253, + POINT_SMOOTH_ENABLE = 254, + POLYGON_OFFSET_FILL_ENABLE = 255, + POLYGON_OFFSET_LINE_ENABLE = 256, + POLYGON_OFFSET_POINT_ENABLE = 257, + POLYGON_SMOOTH_ENABLE = 258, + POLYGON_STIPPLE_ENABLE = 259, + RESCALE_NORMAL_ENABLE = 260, + SAMPLE_ALPHA_TO_COVERAGE_ENABLE = 261, + SAMPLE_ALPHA_TO_ONE_ENABLE = 262, + SAMPLE_COVERAGE_ENABLE = 263, + SCISSOR_TEST_ENABLE = 264, + STENCIL_TEST_ENABLE = 265, + GLSL_PARAM_TYPE = 266, + CG_PARAM_TYPE = 267, + BOOL1 = 268, + BOOL1X1 = 269, + BOOL1X2 = 270, + BOOL1X3 = 271, + BOOL1X4 = 272, + BOOL2X1 = 273, + BOOL2X2 = 274, + BOOL2X3 = 275, + BOOL2X4 = 276, + BOOL3X1 = 277, + BOOL3X2 = 278, + BOOL3X3 = 279, + BOOL3X4 = 280, + BOOL4X1 = 281, + BOOL4X2 = 282, + BOOL4X3 = 283, + BOOL4X4 = 284, + FLOAT1 = 285, + INT1 = 286, + INT1X1 = 287, + INT1X2 = 288, + INT1X3 = 289, + INT1X4 = 290, + INT2X1 = 291, + INT2X2 = 292, + INT2X3 = 293, + INT2X4 = 294, + INT3X1 = 295, + INT3X2 = 296, + INT3X3 = 297, + INT3X4 = 298, + INT4X1 = 299, + INT4X2 = 300, + INT4X3 = 301, + INT4X4 = 302, + HALF = 303, + HALF1 = 304, + HALF2 = 305, + HALF3 = 306, + HALF4 = 307, + HALF1X1 = 308, + HALF1X2 = 309, + HALF1X3 = 310, + HALF1X4 = 311, + HALF2X1 = 312, + HALF2X2 = 313, + HALF2X3 = 314, + HALF2X4 = 315, + HALF3X1 = 316, + HALF3X2 = 317, + HALF3X3 = 318, + HALF3X4 = 319, + HALF4X1 = 320, + HALF4X2 = 321, + HALF4X3 = 322, + HALF4X4 = 323, + FIXED = 324, + FIXED1 = 325, + FIXED2 = 326, + FIXED3 = 327, + FIXED4 = 328, + FIXED1X1 = 329, + FIXED1X2 = 330, + FIXED1X3 = 331, + FIXED1X4 = 332, + FIXED2X1 = 333, + FIXED2X2 = 334, + FIXED2X3 = 335, + FIXED2X4 = 336, + FIXED3X1 = 337, + FIXED3X2 = 338, + FIXED3X3 = 339, + FIXED3X4 = 340, + FIXED4X1 = 341, + FIXED4X2 = 342, + FIXED4X3 = 343, + FIXED4X4 = 344, + GLES_PIPELINE_SETTINGS = 345, + TEXTURE_PIPELINE = 346, + LIGHT_LINEAR_ATTENUTATION = 347, + TEXTURE_PIPELINE_ENABLE = 348, + GLES_BASIC_TYPE_COMMON = 349, + COLLADA = 350, + SCENE = 351, + IDREF_ARRAY = 352, + NAME_ARRAY = 353, + BOOL_ARRAY = 354, + FLOAT_ARRAY = 355, + INT_ARRAY = 356, + ACCESSOR = 357, + TECHNIQUE_COMMON = 358, + GEOMETRY = 359, + MESH = 360, + SPLINE = 361, + CONTROL_VERTICES = 362, + P = 363, + LINES = 364, + LINESTRIPS = 365, + POLYGONS = 366, + PH = 367, + H = 368, + POLYLIST = 369, + VCOUNT = 370, + TRIANGLES = 371, + TRIFANS = 372, + TRISTRIPS = 373, + VERTICES = 374, + LOOKAT = 375, + MATRIX = 376, + ROTATE = 377, + SCALE = 378, + SKEW = 379, + TRANSLATE = 380, + IMAGE = 381, + DATA = 382, + INIT_FROM = 383, + LIGHT = 384, + AMBIENT = 385, + DIRECTIONAL = 386, + POINT = 387, + SPOT = 388, + MATERIAL = 389, + CAMERA = 390, + OPTICS = 391, + ORTHOGRAPHIC = 392, + PERSPECTIVE = 393, + IMAGER = 394, + ANIMATION = 395, + ANIMATION_CLIP = 396, + CHANNEL = 397, + SAMPLER = 398, + CONTROLLER = 399, + SKIN = 400, + BIND_SHAPE_MATRIX = 401, + JOINTS = 402, + VERTEX_WEIGHTS = 403, + V = 404, + MORPH = 405, + TARGETS = 406, + ASSET = 407, + CONTRIBUTOR = 408, + AUTHOR = 409, + AUTHORING_TOOL = 410, + COMMENTS = 411, + COPYRIGHT = 412, + SOURCE_DATA = 413, + CREATED = 414, + KEYWORDS = 415, + MODIFIED = 416, + REVISION = 417, + SUBJECT = 418, + TITLE = 419, + UNIT = 420, + UP_AXIS = 421, + EXTRA = 422, + TECHNIQUE = 423, + NODE = 424, + VISUAL_SCENE = 425, + EVALUATE_SCENE = 426, + RENDER = 427, + LAYER = 428, + BIND_MATERIAL = 429, + INSTANCE_CAMERA = 430, + INSTANCE_CONTROLLER = 431, + SKELETON = 432, + INSTANCE_EFFECT = 433, + TECHNIQUE_HINT = 434, + SETPARAM = 435, + INSTANCE_FORCE_FIELD = 436, + INSTANCE_GEOMETRY = 437, + INSTANCE_LIGHT = 438, + INSTANCE_MATERIAL = 439, + BIND = 440, + BIND_VERTEX_INPUT = 441, + INSTANCE_NODE = 442, + INSTANCE_PHYSICS_MATERIAL = 443, + INSTANCE_PHYSICS_MODEL = 444, + INSTANCE_RIGID_BODY = 445, + ANGULAR_VELOCITY = 446, + VELOCITY = 447, + DYNAMIC = 448, + MASS_FRAME = 449, + SHAPE = 450, + HOLLOW = 451, + INSTANCE_RIGID_CONSTRAINT = 452, + LIBRARY_ANIMATIONS = 453, + LIBRARY_ANIMATION_CLIPS = 454, + LIBRARY_CAMERAS = 455, + LIBRARY_CONTROLLERS = 456, + LIBRARY_GEOMETRIES = 457, + LIBRARY_EFFECTS = 458, + LIBRARY_FORCE_FIELDS = 459, + LIBRARY_IMAGES = 460, + LIBRARY_LIGHTS = 461, + LIBRARY_MATERIALS = 462, + LIBRARY_NODES = 463, + LIBRARY_PHYSICS_MATERIALS = 464, + LIBRARY_PHYSICS_MODELS = 465, + LIBRARY_PHYSICS_SCENES = 466, + LIBRARY_VISUAL_SCENES = 467, + FX_PROFILE_ABSTRACT = 468, + EFFECT = 469, + GL_HOOK_ABSTRACT = 470, + PROFILE_GLSL = 471, + PASS = 472, + DRAW = 473, + SHADER = 474, + COMPILER_TARGET = 475, + COMPILER_OPTIONS = 476, + PROFILE_COMMON = 477, + CONSTANT = 478, + LAMBERT = 479, + PHONG = 480, + BLINN = 481, + PROFILE_CG = 482, + PROFILE_GLES = 483, + COLOR_TARGET = 484, + DEPTH_TARGET = 485, + STENCIL_TARGET = 486, + COLOR_CLEAR = 487, + DEPTH_CLEAR = 488, + STENCIL_CLEAR = 489, + BOX = 490, + HALF_EXTENTS = 491, + PLANE = 492, + EQUATION = 493, + SPHERE = 494, + RADIUS = 495, + ELLIPSOID = 496, + CYLINDER = 497, + HEIGHT = 498, + TAPERED_CYLINDER = 499, + RADIUS1 = 500, + RADIUS2 = 501, + CAPSULE = 502, + TAPERED_CAPSULE = 503, + CONVEX_MESH = 504, + FORCE_FIELD = 505, + PHYSICS_MATERIAL = 506, + PHYSICS_SCENE = 507, + RIGID_BODY = 508, + RIGID_CONSTRAINT = 509, + REF_ATTACHMENT = 510, + ATTACHMENT = 511, + ENABLED = 512, + INTERPENETRATE = 513, + LIMITS = 514, + SWING_CONE_AND_TWIST = 515, + LINEAR = 516, + SPRING = 517, + ANGULAR = 518, + PHYSICS_MODEL = 519; +} + +// Returns the total number of schema types/dom* classes +daeInt DLLSPEC colladaTypeCount(); + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domVertices.h b/Engine/lib/collada/include/1.4/dom/domVertices.h new file mode 100644 index 000000000..f8c71b7a6 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domVertices.h @@ -0,0 +1,134 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domVertices_h__ +#define __domVertices_h__ + +#include +#include +#include + +#include +#include +class DAE; + +/** + * The vertices element declares the attributes and identity of mesh-vertices. + * The vertices element describes mesh-vertices in a mesh geometry. The mesh-vertices + * represent the position (identity) of the vertices comprising the mesh + * and other vertex attributes that are invariant to tessellation. + */ +class domVertices : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::VERTICES; } + static daeInt ID() { return 628; } + virtual daeInt typeID() const { return ID(); } +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Required attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The input element must occur at least one time. These inputs are local + * inputs. @see domInput + */ + domInputLocal_Array elemInput_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the input element array. + * @return Returns a reference to the array of input elements. + */ + domInputLocal_Array &getInput_array() { return elemInput_array; } + /** + * Gets the input element array. + * @return Returns a constant reference to the array of input elements. + */ + const domInputLocal_Array &getInput_array() const { return elemInput_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domVertices(DAE& dae) : daeElement(dae), attrId(), attrName(), elemInput_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domVertices() {} + /** + * Overloaded assignment operator + */ + virtual domVertices &operator=( const domVertices &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/1.4/dom/domVisual_scene.h b/Engine/lib/collada/include/1.4/dom/domVisual_scene.h new file mode 100644 index 000000000..79d0045f3 --- /dev/null +++ b/Engine/lib/collada/include/1.4/dom/domVisual_scene.h @@ -0,0 +1,407 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __domVisual_scene_h__ +#define __domVisual_scene_h__ + +#include +#include +#include + +#include +#include +#include +#include +class DAE; + +/** + * The visual_scene element declares the base of the visual_scene hierarchy + * or scene graph. The scene contains elements that comprise much of the + * visual and transformational information content as created by the authoring + * tools. + */ +class domVisual_scene : public daeElement +{ +public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::VISUAL_SCENE; } + static daeInt ID() { return 682; } + virtual daeInt typeID() const { return ID(); } +public: + class domEvaluate_scene; + + typedef daeSmartRef domEvaluate_sceneRef; + typedef daeTArray domEvaluate_scene_Array; + +/** + * The evaluate_scene element declares information specifying a specific way + * to evaluate this visual_scene. There may be any number of evaluate_scene + * elements. + */ + class domEvaluate_scene : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::EVALUATE_SCENE; } + static daeInt ID() { return 683; } + virtual daeInt typeID() const { return ID(); } + public: + class domRender; + + typedef daeSmartRef domRenderRef; + typedef daeTArray domRender_Array; + +/** + * The render element describes one effect pass to evaluate the scene. There + * must be at least one render element. + */ + class domRender : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::RENDER; } + static daeInt ID() { return 684; } + virtual daeInt typeID() const { return ID(); } + public: + class domLayer; + + typedef daeSmartRef domLayerRef; + typedef daeTArray domLayer_Array; + +/** + * The layer element specifies which layer to render in this compositing step + * while evaluating the scene. You may specify any number of layers. + */ + class domLayer : public daeElement + { + public: + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::LAYER; } + static daeInt ID() { return 685; } + virtual daeInt typeID() const { return ID(); } + + protected: // Value + /** + * The xsNCName value of the text data of this element. + */ + xsNCName _value; + + public: //Accessors and Mutators + /** + * Gets the value of this element. + * @return Returns a xsNCName of the value. + */ + xsNCName getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( xsNCName val ) { *(daeStringRef*)&_value = val; } + + protected: + /** + * Constructor + */ + domLayer(DAE& dae) : daeElement(dae), _value() {} + /** + * Destructor + */ + virtual ~domLayer() {} + /** + * Overloaded assignment operator + */ + virtual domLayer &operator=( const domLayer &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + protected: // Attribute +/** + * The camera_node attribute refers to a node that contains a camera describing + * the viewpoint to render this compositing step from. + */ + xsAnyURI attrCamera_node; + + protected: // Elements +/** + * The layer element specifies which layer to render in this compositing step + * while evaluating the scene. You may specify any number of layers. @see + * domLayer + */ + domLayer_Array elemLayer_array; +/** + * The instance_effect element specifies which effect to render in this compositing + * step while evaluating the scene. @see domInstance_effect + */ + domInstance_effectRef elemInstance_effect; + + public: //Accessors and Mutators + /** + * Gets the camera_node attribute. + * @return Returns a xsAnyURI reference of the camera_node attribute. + */ + xsAnyURI &getCamera_node() { return attrCamera_node; } + /** + * Gets the camera_node attribute. + * @return Returns a constant xsAnyURI reference of the camera_node attribute. + */ + const xsAnyURI &getCamera_node() const { return attrCamera_node; } + /** + * Sets the camera_node attribute. + * @param atCamera_node The new value for the camera_node attribute. + */ + void setCamera_node( const xsAnyURI &atCamera_node ) { attrCamera_node = atCamera_node; _validAttributeArray[0] = true; } + /** + * Sets the camera_node attribute. + * @param atCamera_node The new value for the camera_node attribute. + */ + void setCamera_node( xsString atCamera_node ) { attrCamera_node = atCamera_node; _validAttributeArray[0] = true; } + + /** + * Gets the layer element array. + * @return Returns a reference to the array of layer elements. + */ + domLayer_Array &getLayer_array() { return elemLayer_array; } + /** + * Gets the layer element array. + * @return Returns a constant reference to the array of layer elements. + */ + const domLayer_Array &getLayer_array() const { return elemLayer_array; } + /** + * Gets the instance_effect element. + * @return a daeSmartRef to the instance_effect element. + */ + const domInstance_effectRef getInstance_effect() const { return elemInstance_effect; } + protected: + /** + * Constructor + */ + domRender(DAE& dae) : daeElement(dae), attrCamera_node(dae, *this), elemLayer_array(), elemInstance_effect() {} + /** + * Destructor + */ + virtual ~domRender() {} + /** + * Overloaded assignment operator + */ + virtual domRender &operator=( const domRender &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + + protected: // Attribute +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + + protected: // Element +/** + * The render element describes one effect pass to evaluate the scene. There + * must be at least one render element. @see domRender + */ + domRender_Array elemRender_array; + + public: //Accessors and Mutators + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[0] = true; } + + /** + * Gets the render element array. + * @return Returns a reference to the array of render elements. + */ + domRender_Array &getRender_array() { return elemRender_array; } + /** + * Gets the render element array. + * @return Returns a constant reference to the array of render elements. + */ + const domRender_Array &getRender_array() const { return elemRender_array; } + protected: + /** + * Constructor + */ + domEvaluate_scene(DAE& dae) : daeElement(dae), attrName(), elemRender_array() {} + /** + * Destructor + */ + virtual ~domEvaluate_scene() {} + /** + * Overloaded assignment operator + */ + virtual domEvaluate_scene &operator=( const domEvaluate_scene &cpy ) { (void)cpy; return *this; } + + public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + }; + + +protected: // Attributes +/** + * The id attribute is a text string containing the unique identifier of + * this element. This value must be unique within the instance document. + * Optional attribute. + */ + xsID attrId; +/** + * The name attribute is the text string name of this element. Optional attribute. + */ + xsNCName attrName; + +protected: // Elements +/** + * The visual_scene element may contain an asset element. @see domAsset + */ + domAssetRef elemAsset; +/** + * The visual_scene element must have at least one node element. @see domNode + */ + domNode_Array elemNode_array; +/** + * The evaluate_scene element declares information specifying a specific way + * to evaluate this visual_scene. There may be any number of evaluate_scene + * elements. @see domEvaluate_scene + */ + domEvaluate_scene_Array elemEvaluate_scene_array; +/** + * The extra element may appear any number of times. @see domExtra + */ + domExtra_Array elemExtra_array; + +public: //Accessors and Mutators + /** + * Gets the id attribute. + * @return Returns a xsID of the id attribute. + */ + xsID getId() const { return attrId; } + /** + * Sets the id attribute. + * @param atId The new value for the id attribute. + */ + void setId( xsID atId ) { *(daeStringRef*)&attrId = atId; _validAttributeArray[0] = true; + if( _document != NULL ) _document->changeElementID( this, attrId ); + } + + /** + * Gets the name attribute. + * @return Returns a xsNCName of the name attribute. + */ + xsNCName getName() const { return attrName; } + /** + * Sets the name attribute. + * @param atName The new value for the name attribute. + */ + void setName( xsNCName atName ) { *(daeStringRef*)&attrName = atName; _validAttributeArray[1] = true; } + + /** + * Gets the asset element. + * @return a daeSmartRef to the asset element. + */ + const domAssetRef getAsset() const { return elemAsset; } + /** + * Gets the node element array. + * @return Returns a reference to the array of node elements. + */ + domNode_Array &getNode_array() { return elemNode_array; } + /** + * Gets the node element array. + * @return Returns a constant reference to the array of node elements. + */ + const domNode_Array &getNode_array() const { return elemNode_array; } + /** + * Gets the evaluate_scene element array. + * @return Returns a reference to the array of evaluate_scene elements. + */ + domEvaluate_scene_Array &getEvaluate_scene_array() { return elemEvaluate_scene_array; } + /** + * Gets the evaluate_scene element array. + * @return Returns a constant reference to the array of evaluate_scene elements. + */ + const domEvaluate_scene_Array &getEvaluate_scene_array() const { return elemEvaluate_scene_array; } + /** + * Gets the extra element array. + * @return Returns a reference to the array of extra elements. + */ + domExtra_Array &getExtra_array() { return elemExtra_array; } + /** + * Gets the extra element array. + * @return Returns a constant reference to the array of extra elements. + */ + const domExtra_Array &getExtra_array() const { return elemExtra_array; } +protected: + /** + * Constructor + */ + domVisual_scene(DAE& dae) : daeElement(dae), attrId(), attrName(), elemAsset(), elemNode_array(), elemEvaluate_scene_array(), elemExtra_array() {} + /** + * Destructor + */ + virtual ~domVisual_scene() {} + /** + * Overloaded assignment operator + */ + virtual domVisual_scene &operator=( const domVisual_scene &cpy ) { (void)cpy; return *this; } + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * If a daeMetaElement already exists it will return that instead of creating a new one. + * @return A daeMetaElement describing this COLLADA element. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); +}; + + +#endif diff --git a/Engine/lib/collada/include/dae.h b/Engine/lib/collada/include/dae.h new file mode 100644 index 000000000..d6f217ed8 --- /dev/null +++ b/Engine/lib/collada/include/dae.h @@ -0,0 +1,212 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE__ +#define __DAE__ + +// Need to include to use std::auto_ptr with gcc-4.3 - Andrew Galante, GG 8/2/2009 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class domCOLLADA; +typedef daeSmartRef domCOLLADARef; +class daeDatabase; + +// The DAE class is the core interface via which you interact with the DOM. It +// has methods to load/save documents, get the root element of each document, +// etc. Although internally the DOM works exclusively with URIs, the methods of +// the DAE class that take document paths can take URIs or OS-specific file +// paths. +class DLLSPEC DAE +{ +public: + // Constructor. If no database or IO plugin are provided, a default database and + // IO plugin will be used. + DAE(daeDatabase* database = NULL, daeIOPlugin* ioPlugin = NULL) + : atomicTypes(*this), + baseUri(*this, cdom::getCurrentDirAsUri().c_str()) + { + // See the end of the thread linked below for an explanation of why we have the DAE + // constructor set up this way. Basically, I'm going to be changing the build output + // location, and when this happens people sometimes continue to link against the old + // libraries by accident (e.g. if they just do an svn update). By introducing a new + // function that gets called from a function in a header file, I'm ensuring that someone + // who tries linking against old libraries will get a link error. This may not sound + // very nice, but it's certainly better than getting bizarre runtime crashes. + // https://collada.org/public_forum/viewtopic.php?t=771&sid=f13c34f2d17ca720c5021bccbe5128b7 + init(database, ioPlugin); + dummyFunction1(); + } + + virtual ~DAE(); + + // Release all memory used by the DOM. You never need to call this explicitly. It's + // called automatically when all DAE objects go out of scope. + static void cleanup(); + +public: + // Database setup + virtual daeDatabase* getDatabase(); + virtual daeInt setDatabase(daeDatabase* database); + + // IO Plugin setup + virtual daeIOPlugin* getIOPlugin(); + virtual daeInt setIOPlugin(daeIOPlugin* plugin); + + // Creates a new document, returning null on failure. + virtual domCOLLADA* add(const std::string& path); + // Opens an existing document, returning null on failure. + virtual domCOLLADA* open(const std::string& path); + // Opens a document from memory, returning null on failure. + virtual domCOLLADA* openFromMemory(const std::string& path, daeString buffer); + // Write a document to the path specified by the document's URI, returning false on failure. + virtual bool write(const std::string& path); + // Write a document to the path specified in the second parameter, returning false on failure. + virtual bool writeTo(const std::string& docPath, const std::string& pathToWriteTo); + // Writes all documents, returning false if any document failed to write. + virtual bool writeAll(); + // Close a specific document, unloading all memory used by the document. Returns false on failure. + virtual void close(const std::string& path); + // Remove all loaded documents. Always returns DAE_OK. + virtual daeInt clear(); + + // Returns the total number of documents. + virtual int getDocCount(); + // Returns the i'th document . + virtual daeDocument* getDoc(int i); + // Returns a document matching the path. + virtual daeDocument* getDoc(const std::string& path); + + // Get the root domCOLLADA object corresponding to a particular document. + virtual domCOLLADA* getRoot(const std::string& path); + // Set the root domCOLLADA object corresponding to a particular document, returning false on failure. + virtual bool setRoot(const std::string& path, domCOLLADA* root); + + // Returns the Collada version, i.e. 1.4, 1.5, etc. Note that this _isn't_ the + // same as the DOM version (1.3, 2.0, ...). + virtual daeString getDomVersion(); + + // Returns the (modifiable) list of atomic type objects. + daeAtomicTypeList& getAtomicTypes(); + + // Get/set a daeMetaElement object given the meta object's type ID. + daeMetaElement* getMeta(daeInt typeID); + void setMeta(daeInt typeID, daeMetaElement& meta); + + // Get all daeMetaElement objects. + daeMetaElementRefArray& getAllMetas(); + + // Returns the list of URI resolvers. You can modify the list to add new resolvers. + daeURIResolverList& getURIResolvers(); + + // The base URI used for resolving relative URI references. + daeURI& getBaseURI(); + void setBaseURI(const daeURI& uri); + void setBaseURI(const std::string& uri); + + // Returns the list of ID reference resolvers. You can modify the list to add new + // resolvers. + daeIDRefResolverList& getIDRefResolvers(); + + // Meant for internal DOM use only. + daeRawRefCache& getRawRefCache(); + daeSidRefCache& getSidRefCache(); + + // These functions specify the client's character encoding for the DOM. The + // default is Utf8, but if you specify Latin1 then the DOM will use libxml's + // character conversion functions to convert to Utf8 when writing data and + // convert to Latin1 when reading data. This can help with the handling of + // non-ASCII characters on Windows. Only when using libxml for xml I/O does + // any character conversion occur. + // + // Most people can probably just ignore this completely. If you have trouble + // with non-ASCII characters on Windows, try setting the char encoding to + // Latin1 to see if that helps. + // + // Frankly this certainly isn't the best way of handling non-ASCII character + // support on Windows, so this interface is a likely target for significant + // changes in the future. + // + // See this Sourceforge thread for more info: + // http://sourceforge.net/tracker/index.php?func=detail&aid=1818473&group_id=157838&atid=805426 + // + enum charEncoding { + Utf8, + Latin1 + }; + + // Global encoding setting. Defaults to Utf8. Set this if you want to make a + // char encoding change and apply it to all DAE objects. + static charEncoding getGlobalCharEncoding(); + static void setGlobalCharEncoding(charEncoding encoding); + + // Local encoding setting. If set, overrides the global setting. Useful for setting + // a specific char encoding for a single DAE object but not for all DAE objects. + charEncoding getCharEncoding(); + void setCharEncoding(charEncoding encoding); + + // Deprecated. Alternative methods are given. + virtual daeInt load(daeString uri, daeString docBuffer = NULL); // Use open + virtual daeInt save(daeString uri, daeBool replace=true); // Use write + virtual daeInt save(daeUInt documentIndex, daeBool replace=true); // Use write + virtual daeInt saveAs(daeString uriToSaveTo, daeString docUri, daeBool replace=true); // Use writeTo + virtual daeInt saveAs(daeString uriToSaveTo, daeUInt documentIndex=0, daeBool replace=true); // Use writeTo + virtual daeInt unload(daeString uri); // Use close + virtual domCOLLADA* getDom(daeString uri); // use getRoot + virtual daeInt setDom(daeString uri, domCOLLADA* dom); // use setRoot + +private: + void init(daeDatabase* database, daeIOPlugin* ioPlugin); + void dummyFunction1(); + std::string makeFullUri(const std::string& path); + domCOLLADA* openCommon(const std::string& path, daeString buffer); + bool writeCommon(const std::string& docPath, const std::string& pathToWriteTo, bool replace); + + daeDatabase *database; + daeIOPlugin *plugin; + bool defaultDatabase; + bool defaultPlugin; + daeAtomicTypeList atomicTypes; + daeMetaElementRefArray metas; + daeURI baseUri; + daeURIResolverList uriResolvers; + daeIDRefResolverList idRefResolvers; + daeRawRefCache rawRefCache; + daeSidRefCache sidRefCache; + + std::auto_ptr localCharEncoding; + static charEncoding globalCharEncoding; +}; + + +template +inline T *daeSafeCast(daeElement *element) +{ + if (element && element->typeID() == T::ID()) + return (T*)element; + return NULL; +} + + +#endif // __DAE_INTERFACE__ diff --git a/Engine/lib/collada/include/dae/daeArray.h b/Engine/lib/collada/include/dae/daeArray.h new file mode 100644 index 000000000..33c012918 --- /dev/null +++ b/Engine/lib/collada/include/dae/daeArray.h @@ -0,0 +1,706 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_ARRAY_H__ +#define __DAE_ARRAY_H__ +#include +#include + +class daeAtomicType; + +/** + * COLLADA C++ class that implements storage for resizable array containers. + */ +class daeArray +{ +protected: + size_t _count; + size_t _capacity; + daeMemoryRef _data; + size_t _elementSize; + daeAtomicType* _type; +public: + /** + * Constructor + */ + DLLSPEC daeArray(); + /** + * Destructor + */ + virtual DLLSPEC ~daeArray(); + /** + * Clears the contents of the array. Do not use this function if the array contains @c daeSmartRef objects and the + * @c dom* class the array belongs to has a @c _contents member. + * + * Many @c dom* objects have a @c _contents member that stores the original creation order of the @c daeElements + * that are their children. If you use @c clear() on a @c daeArray of @c daeSmartRef derived objects, these + * objects will not be removed from @c _contents, which can cause problems when you + * save the data. We recommended that @c clear() not be used on arrays that are part of a @c dom* object. + */ + virtual DLLSPEC void clear() = 0; + /** + * Sets the size of an element in the array. This clears and reinitializes the array. + * @param elementSize Size of an element in the array. + */ + DLLSPEC void setElementSize(size_t elementSize); + /** + * Gets the size of an element in this array. + * @return Returns the size of an element in this array. + */ + size_t getElementSize() const {return _elementSize;} + /** + * Grows the array to the specified size and sets the @c daeArray to that size. + * @param cnt Size to grow the array to. + */ + virtual void setCount(size_t cnt) = 0; + /** + * Gets the number of items stored in this @c daeArray. + * @return Returns the number of items stored in this @c daeArray. + */ + size_t getCount() const {return _count;} + /** + * Increases the capacity of the @c daeArray. + * @param minCapacity The minimum array capacity (the actual resulting capacity may be higher). + */ + virtual void grow(size_t minCapacity) = 0; + /** + * Gets the current capacity of the array, the biggest it can get without incurring a realloc. + * @return Returns the capacity of the array. + */ + size_t getCapacity() const {return _capacity;} + /** + * Gets a pointer to the raw memory of a particular element. + * @return Returns a pointer to the memory for the raw data. + */ + daeMemoryRef getRaw(size_t index) const {return _data + index*_elementSize;} + /** + * Removes an item at a specific index in the @c daeArray. + * @param index Index number of the item to delete. + * @return Returns DAE_OK if success, a negative value defined in daeError.h otherwise. + * @note The @c daeElement objects sometimes list + * objects in two places, the class member and the @c _contents array, when you remove something from the + * dom, you must remove it from both places. + */ + virtual daeInt removeIndex(size_t index) = 0; + + // Provided for backward compatibility only. Don't use these. + void setRawCount(size_t cnt) { setCount(cnt); } // Use setCount instead + daeMemoryRef getRawData() const {return _data;} // Use getRaw instead +}; + +/** + * COLLADA C++ templated version of @c daeArray for storing items of various types. + */ +template +class daeTArray : public daeArray +{ +protected: + T* prototype; +public: + /** + * Constructor. + */ + daeTArray() { + _elementSize = sizeof( T ); + prototype = NULL; + } + /** + * Constructor. + */ + explicit daeTArray(T* prototype) : prototype(prototype) { + _elementSize = sizeof( T ); + } + /** + * Copy Constructor + */ + daeTArray( const daeTArray &cpy ) : daeArray() { + prototype = NULL; + *this = cpy; + } + /** + * Constructor that takes one element and turns into an array + */ + explicit daeTArray( const T &el ) { + _elementSize = sizeof(T); + prototype = NULL; + append( el ); + } + /** + * Destructor. + */ + virtual ~daeTArray() { + clear(); + delete prototype; + } + /** + * Frees the memory in this array and resets it to it's initial state. + */ + virtual void clear() + { + for(size_t i=0;i<_count;i++) + ((T*)_data + i)->~T(); + free(_data); + _count = 0; + _capacity = 0; + _data = NULL; + } + + /** + * Increases the capacity of the @c daeArray. + * @param minCapacity The minimum array capacity (the actual resulting capacity may be higher). + */ + void grow(size_t minCapacity) { + if (minCapacity <= _capacity) + return; + + size_t newCapacity = _capacity == 0 ? 1 : _capacity; + while(newCapacity < minCapacity) + newCapacity *= 2; + + T* newData = (T*)malloc(newCapacity*_elementSize); + for (size_t i = 0; i < _count; i++) { + new (&newData[i]) T(get(i)); + ((T*)_data + i)->~T(); + } + + if (_data != NULL) + free(_data); + + _data = (daeMemoryRef)newData; + _capacity = newCapacity; + } + + /** + * Removes an item at a specific index in the @c daeArray. + * @param index Index number of the item to delete. + * @return Returns DAE_OK if success, a negative value defined in daeError.h otherwise. + * @note The @c daeElement objects sometimes list + * objects in two places, the class member and the @c _contents array, when you remove something from the + * dom, you must remove it from both places. + */ + virtual daeInt removeIndex(size_t index) + { + if (index >= _count) + return(DAE_ERR_INVALID_CALL); + + for (size_t i = index; i < _count-1; i++) + *((T*)_data+i) = *((T*)_data+i+1); + ((T*)_data+(_count-1))->~T(); + _count--; + return DAE_OK; + } + + /** + * Resets the number of elements in the array. If the array increases in size, the new + * elements will be initialized to the specified value. + * @param nElements The new size of the array. + * @param value The value new elements will be initialized to. + * @note Shrinking the array does NOT free up memory. + */ + void setCount(size_t nElements, const T& value) + { + grow(nElements); + // Destruct the elements that are being chopped off + for (size_t i = nElements; i < _count; i++) + ((T*)_data+i)->~T(); + // Use value to initialize the new elements + for (size_t i = _count; i < nElements; i++) + new ((void*)((T*)_data+i)) T(value); + _count = nElements; + } + + /** + * Resets the number of elements in the array. If the array increases in size, the new + * elements will be initialized with a default constructor. + * @param nElements The new size of the array. + * @note Shrinking the array does NOT free up memory. + */ + virtual void setCount(size_t nElements) { + if (prototype) + setCount(nElements, *prototype); + else + setCount(nElements, T()); + } + + /** + * Sets a specific index in the @c daeArray, growing the array if necessary. + * @param index Index of the object to set, asserts if the index is out of bounds. + * @param value Value to store at index in the array. + */ + void set(size_t index, const T& value) { + if (index >= _count) + setCount(index+1); + ((T*)_data)[index] = value; + } + + /** + * Gets the object at a specific index in the @c daeArray. + * @param index Index of the object to get, asserts if the index is out of bounds. + * @return Returns the object at index. + */ + T& get(size_t index) { + assert(index < _count); + return ((T*)_data)[index]; } + /** + * Gets the object at a specific index in the @c daeArray. + * @param index Index of the object to get, asserts if the index is out of bounds. + * @return Returns the object at index. + */ + const T& get(size_t index) const { + assert(index < _count); + return ((T*)_data)[index]; } + + /** + * Appends a new object to the end of the @c daeArray. + * @param value Value of the object to append. + * @return Returns the index of the new object. + */ + size_t append(const T& value) { + set(_count, value); + return _count-1; + } + + /** + * Appends a unique object to the end of the @c daeArray. + * Functions the same as @c append(), but does nothing if the value is already in the @c daeArray. + * @param value Value of the object to append. + * @return Returns the index where this value was appended. If the value already exists in the array, + * returns the index in this array where the value was found. + */ + size_t appendUnique(const T& value) { + size_t ret; + if (find(value,ret) != DAE_OK) + return append(value); + else + return ret; + } + + /** + * Adds a new item to the front of the @c daeArray. + * @param value Item to be added. + */ + void prepend(const T& value) { + insertAt(0, value); + } + + /** + * Removes an item from the @c daeArray. + * @param value A reference to the item to delete. + * @return Returns DAE_OK if success, a negative value defined in daeError.h otherwise. + * @note The @c daeElement objects sometimes list + * objects in two places, the class member and the @c _contents array, when you remove something from the + * do, you must remove it from both places. + */ + daeInt remove(const T& value, size_t *idx = NULL ) + { + size_t index; + if(find(value,index) == DAE_OK) + { + if ( idx != NULL ) { + *idx = index; + } + return(removeIndex( index )); + } + else + { + return(DAE_ERR_INVALID_CALL); + } + } + /** + * Finds an item from the @c daeArray. + * @param value A reference to the item to find. + * @param index If the function returns DAE_OK, this is set to the index where the value appears in the array. + * @return Returns DAE_OK if no error or DAE_ERR_QUERY_NO_MATCH if the value was not found. + */ + daeInt find(const T& value, size_t &index) const + { + size_t i; + for(i=0;i<_count;i++) + { + if (((T*)_data)[i] == value) + { + index = i; + return DAE_OK; + } + } + + return DAE_ERR_QUERY_NO_MATCH; + } + /** + * Just like the previous function, but has a more reasonable interface. + * @param value The value to find. + * @return Returns a pointer to the value if found, null otherwise. + */ + T* find(const T& value) const { + size_t i; + if (find(value, i) == DAE_OK) + return get(i); + return NULL; + } + /** + * Gets the object at a specific index in the @c daeArray. + * @param index Index of the object to get, asserts if the index is out of bounds. + * @return Returns the object at @c index. + */ + T& operator[](size_t index) { + assert(index < _count); + return ((T*)_data)[index]; } + /** + * Gets the object at a specific index in the @c daeArray. + * @param index Index of the object to get, asserts if the index is out of bounds. + * @return Returns the object at @c index. + */ + const T& operator[](size_t index) const { + assert(index < _count); + return ((T*)_data)[index]; } + + /** + * Inserts the specified number of elements at a specific location in the array. + * @param index Index into the array where the elements will be inserted + * @param n The number of elements to insert + * @param val The value to insert + */ + void insert(size_t index, size_t n, const T& val = T()) { + if (index >= _count) { + // Append to the end of the array + size_t oldCount = _count; + setCount(index + n); + for (size_t i = oldCount; i < _count; i++) + get(i) = val; + } + else { + setCount(_count + n); + for (size_t i = _count-1; i >= index+n; i--) + get(i) = get(i-n); + for (size_t i = index; i < index+n; i++) + get(i) = val; + } + } + + /** + * Inserts an object at a specific index in the daeArray, growing the array if neccessary + * @param index Index into the array for where to place the object + * @param value The object to append + */ + void insertAt(size_t index, const T& value) { + insert(index, 1); + get(index) = value; + } + + /** + * Overloaded assignment operator. + * @param other A reference to the array to copy + * @return A reference to this object. + */ + daeTArray &operator=( const daeTArray &other ) { + if (this != &other) { + clear(); + _elementSize = other._elementSize; + _type = other._type; + grow(other._count); + for(size_t i=0;i& other) { + if (getCount() != other.getCount()) + return false; + for (size_t i = 0; i < getCount(); i++) + if (get(i) != other.get(i)) + return false; + return true; + } + + //some helpers + /** + * Sets the array to the contain the two values specified. + * @param one The first value. + * @param two The second value. + */ + void set2( const T &one, const T &two ) + { + setCount( 2 ); + set( 0, one ); + set( 1, two ); + } + /** + * Sets the array to the contain the three values specified. + * @param one The first value. + * @param two The second value. + * @param three The third value. + */ + void set3( const T &one, const T &two, const T &three ) + { + setCount( 3 ); + set( 0, one ); + set( 1, two ); + set( 2, three ); + } + /** + * Sets the array to the contain the four values specified. + * @param one The first value. + * @param two The second value. + * @param three The third value. + * @param four The fourth value. + */ + void set4( const T &one, const T &two, const T &three, const T &four ) + { + setCount( 4 ); + set( 0, one ); + set( 1, two ); + set( 2, three ); + set( 3, four ); + } + + /** + * Sets the values in the array at the specified location to the contain the two + * values specified. This function will grow the array if needed. + * @param index The position in the array to start setting. + * @param one The first value. + * @param two The second value. + */ + void set2at( size_t index, const T &one, const T &two ) + { + set( index, one ); + set( index+1, two ); + } + /** + * Sets the values in the array at the specified location to the contain the three + * values specified. This function will grow the array if needed. + * @param index The position in the array to start setting. + * @param one The first value. + * @param two The second value. + * @param three The third value. + */ + void set3at( size_t index, const T &one, const T &two, const T &three ) + { + set( index, one ); + set( index+1, two ); + set( index+2, three ); + } + /** + * Sets the values in the array at the specified location to the contain the four + * values specified. This function will grow the array if needed. + * @param index The position in the array to start setting. + * @param one The first value. + * @param two The second value. + * @param three The third value. + * @param four The fourth value. + */ + void set4at( size_t index, const T &one, const T &two, const T &three, const T &four ) + { + set( index, one ); + set( index+1, two ); + set( index+2, three ); + set( index+3, four ); + } + + /** + * Appends two values to the array. + * @param one The first value. + * @param two The second value. + */ + void append2( const T &one, const T &two ) + { + append( one ); + append( two ); + } + /** + * Appends three values to the array. + * @param one The first value. + * @param two The second value. + * @param three The third value. + */ + void append3( const T &one, const T &two, const T &three ) + { + append( one ); + append( two ); + append( three ); + } + /** + * Appends four values to the array. + * @param one The first value. + * @param two The second value. + * @param three The third value. + * @param four The fourth value. + */ + void append4( const T &one, const T &two, const T &three, const T &four ) + { + append( one ); + append( two ); + append( three ); + append( four ); + } + + /** + * Inserts two values into the array at the specified location. + * @param index The position in the array to start inserting. + * @param one The first value. + * @param two The second value. + */ + void insert2at( size_t index, const T &one, const T &two ) + { + insert(index, 2); + set(index, one); + set(index+1, two); + } + /** + * Inserts three values into the array at the specified location. + * @param index The position in the array to start inserting. + * @param one The first value. + * @param two The second value. + * @param three The third value. + */ + void insert3at( size_t index, const T &one, const T &two, const T &three ) + { + insert(index, 3); + set( index, one ); + set( index+1, two ); + set( index+2, three ); + } + /** + * Inserts four values into the array at the specified location. + * @param index The position in the array to start inserting. + * @param one The first value. + * @param two The second value. + * @param three The third value. + * @param four The fourth value. + */ + void insert4at( size_t index, const T &one, const T &two, const T &three, const T &four ) + { + insert(index, 4); + set( index, one ); + set( index+1, two ); + set( index+2, three ); + set( index+4, four ); + } + + /** + * Gets two values from the array at the specified location. + * @param index The position in the array to start getting. + * @param one Variable to store the first value. + * @param two Variable to store the second value. + * @return Returns The number of elements retrieved. + */ + daeInt get2at( size_t index, T &one, T &two ) + { + daeInt retVal = 0; + if ( index < _count ) + { + one = get(index); + retVal++; + } + if ( index+1 < _count ) + { + two = get(index+1); + retVal++; + } + return retVal; + } + /** + * Gets three values from the array at the specified location. + * @param index The position in the array to start getting. + * @param one Variable to store the first value. + * @param two Variable to store the second value. + * @param three Variable to store the third value. + * @return Returns The number of elements retrieved. + */ + daeInt get3at( size_t index, T &one, T &two, T &three ) + { + daeInt retVal = 0; + if ( index < _count ) + { + one = get(index); + retVal++; + } + if ( index+1 < _count ) + { + two = get(index+1); + retVal++; + } + if ( index+2 < _count ) + { + three = get(index+2); + retVal++; + } + return retVal; + } + /** + * Gets four values from the array at the specified location. + * @param index The position in the array to start getting. + * @param one Variable to store the first value. + * @param two Variable to store the second value. + * @param three Variable to store the third value. + * @param four Variable to store the fourth value. + * @return Returns The number of elements retrieved. + */ + daeInt get4at( size_t index, T &one, T &two, T &three, T &four ) + { + daeInt retVal = 0; + if ( index < _count ) + { + one = get(index); + retVal++; + } + if ( index+1 < _count ) + { + two = get(index+1); + retVal++; + } + if ( index+2 < _count ) + { + three = get(index+2); + retVal++; + } + if ( index+3 < _count ) + { + four = get(index+3); + retVal++; + } + return retVal; + } + + /** + * Appends a number of elements to this array from a C native array. + * @param num The number of elements to append. + * @param array The C native array that contains the values to append. + */ + void appendArray( size_t num, T *array ) + { + if ( array == NULL ) + return; + + for ( size_t i = 0; i < num; i++ ) + append( array[i] ); + } + /** + * Appends a number of elements to this array from another daeTArray. + * @param array The daeTArray that contains the values to append. + */ + void appendArray( const daeTArray &array ){ + size_t num = array.getCount(); + for ( size_t i = 0; i < num; i++ ) + append( array[i] ); + } +}; + + +#endif //__DAE_ARRAY_H__ diff --git a/Engine/lib/collada/include/dae/daeArrayTypes.h b/Engine/lib/collada/include/dae/daeArrayTypes.h new file mode 100644 index 000000000..13781e029 --- /dev/null +++ b/Engine/lib/collada/include/dae/daeArrayTypes.h @@ -0,0 +1,30 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_ARRAY_TYPES_H__ +#define __DAE_ARRAY_TYPES_H__ + +#include +#include +typedef daeTArray daeIntArray; +typedef daeTArray daeUIntArray; +typedef daeTArray daeFloatArray; +typedef daeTArray daeEnumArray; +typedef daeTArray daeStringArray; +typedef daeTArray daeCharArray; +typedef daeTArray daeBoolArray; +typedef daeTArray daeDoubleArray; +typedef daeTArray daeLongArray; +typedef daeTArray daeShortArray; + +#endif //__DAE_ARRAY_TYPES_H__ diff --git a/Engine/lib/collada/include/dae/daeAtomicType.h b/Engine/lib/collada/include/dae/daeAtomicType.h new file mode 100644 index 000000000..5c6d28a25 --- /dev/null +++ b/Engine/lib/collada/include/dae/daeAtomicType.h @@ -0,0 +1,731 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_ATOMIC_TYPE_H__ +#define __DAE_ATOMIC_TYPE_H__ + +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#endif + +class DAE; +class daeAtomicType; +class daeMetaElement; + +typedef daeTArray daeAtomicTypeArray; +class daeMetaAttribute; +typedef daeSmartRef daeMetaAttributeRef; + +/** + * The @c daeAtomicType class implements a standard interface for + * data elements in the reflective object system. + * + * @c daeAtomicType provides a central virtual interface that can be + * used by the rest of the reflective object system. + * + * The atomic type system if very useful for file IO and building + * automatic tools for data inspection and manipulation, + * such as hierarchy examiners and object editors. + * + * Types provide the following symantic operations: + * - @c print() + * - @c memoryToString() + * - @c stringToMemory() + * + * Types are also able to align data pointers appropriately. + */ +class DLLSPEC daeAtomicType +{ +public: + /** + * destructor + */ + virtual ~daeAtomicType() {} + + /** + * constructor + */ + daeAtomicType(DAE& dae); + +public: + /** + * Prints an atomic typed element into a destination string. + * @param src Source of the raw data from which to get the typed items. + * @param dst Destination to output the string version of the elements to. + * @return Returns true if the operation was successful, false if not successful. + */ + virtual daeBool memoryToString(daeChar* src, std::ostringstream& dst) = 0; + + /** + * Reads an atomic typed item into the destination runtime memory. + * @param src Source string. + * @param dst Raw binary location to store the resulting value. + * @return Returns true if the operation was successful, false if not successful. + */ + virtual daeBool stringToMemory(daeChar* src, daeChar* dst); + + /** + * Converts an array of atomic items into a whitespace separated string. + * @param array The array of data. + * @param buffer The buffer to write into. + */ + virtual void arrayToString(daeArray& array, std::ostringstream& buffer); + + /** + * Reads a whitespace separated list of atomic items into an array. The array is + * cleared before writing into it. + * @param src Whitespace separated list of items. + * @param array The output array of data. + * @return Returns true if the operation was successful, false otherwise. + */ + virtual daeBool stringToArray(daeChar* src, daeArray& array); + + /** + * Creates a new object of the appropriate type for this daeAtomicType and returns it + * as a pointer. The return value must be freed by calling destroy. + * @return Returns a pointer to a new value. The memory must be freed by calling destroy. + */ + virtual daeMemoryRef create() = 0; + + /** + * Deletes an object previously allocated with create. + * @param obj The object previously allocated with create. + */ + virtual void destroy(daeMemoryRef obj) = 0; + + /** + * Creates a daeTArray of the appropriate type (e.g. daeTArray, daeTArray) + * and returns it as a daeArray*. + * @return Returns a daeArray*. This array should be freed by the caller with + * operator delete. + */ + virtual daeArray* createArray() = 0; + + /** + * Performs a virtual comparison operation between two values of the same atomic type. + * @param value1 Memory location of the first value. + * @param value2 Memory location of the second value. + * @return Returns a positive integer if value1 > value2, a negative integer if + * value1 < value2, and 0 if value1 == value2. + */ + virtual daeInt compare(daeChar* value1, daeChar* value2); + + /** + * Array version of the compare function. + * @param value1 First array to compare. + * @param value2 Second array to compare. + * @return Returns a positive integer if value1 > value2, a negative integer if + * value1 < value2, and 0 if value1 == value2. + */ + virtual daeInt compareArray(daeArray& value1, daeArray& value2); + + /** + * Performs a virtual copy operation. + * @param src Memory location of the value to copy from. + * @param dst Memory location of the value to copy to. + */ + virtual void copy(daeChar* src, daeChar* dst) = 0; + + /** + * Array version of the copy function. + * @param src Array to copy from. + * @param dst Array to copy to. + */ + virtual void copyArray(daeArray& src, daeArray& dst); + + /** + * Gets the array of strings as name bindings for this type. + * @return Returns the array of strings. + */ + daeStringRefArray& getNameBindings() { return _nameBindings; } + + /** + * Gets the enum associated with this atomic type. This is not scalable and only + * works for base types, otherwise 'extension' is used. + * @return Returns the enum associated with this atomic type. + */ + daeEnum getTypeEnum() { return _typeEnum; } + + /** + * Gets the size in bytes for this atomic type. + * @return Returns the size of the atomic type in bytes. + */ + daeInt getSize() { return _size; } + + /** + * Gets the scanf format used for this type. + * @return Returns the scanf format. + * @note + * Warning - this field is only for convenience and may not always work. + * It is used only when the read functions are left to the base + * implementation. + */ + daeStringRef getScanFormat() { return _scanFormat; } + + /** + * Gets the printf format used for this type. + * @return Returns the printf format. + * @note + * Warning - this field is only for convenience and may not always work. + * It is used only when the print functions are left to the base + * implementation. + */ + daeStringRef getPrintFormat() { return _printFormat; } + + /** + * Gets the alignment in bytes necessary for this type on this + * platform. + * @return Returns the alignment in bytes. + */ + daeInt getAlignment() { return _alignment; } + + /** + * Gets the string associated with this type. + * @return Returns the string associated with this type. + */ + daeStringRef getTypeString() { return _typeString; } + + /** + * Performs an alignment based on the alignment for this type. + * @param ptr Pointer to be aligned. + * @return Returns the aligned pointer computed via + * (ptr+alignment-1)&(~(alignment-1). + * + */ + daeChar* align(daeChar* ptr) { + return (daeChar*)(((intptr_t)(ptr+_alignment-1))&(~(_alignment - 1))); } + + /** + * Notifies an object when the containing document changes. + * @param value Memory location of the atomic type value. + * @param doc The new document. + */ + virtual void setDocument(daeChar* value, daeDocument* doc) { } + + /** + * Same as the previous method, but works on an array of objects. + * @param values Array of the atomic type values. + * @param doc The new document. + */ + virtual void setDocument(daeArray& array, daeDocument* doc) { } + +protected: + DAE* _dae; + daeInt _size; + daeInt _alignment; + daeEnum _typeEnum; + daeStringRef _typeString; + daeStringRef _printFormat; + daeStringRef _scanFormat; + daeInt _maxStringLength; + +public: + /** + * An array of strings as name bindings for this type. + */ + daeStringRefArray _nameBindings; + +public: // Static Interface + /** An enum for identifying the different atomic types */ + enum daeAtomicTypes { + /** bool atomic type */ + BoolType, + /** enum atomic type */ + EnumType, + /** character atomic type */ + CharType, + /** short integer atomic type */ + ShortType, + /** integer atomic type */ + IntType, + /** unsigned integer atomic type */ + UIntType, + /** long integer atomic type */ + LongType, + /** unsigned long integer atomic type */ + ULongType, + /** floating point atomic type */ + FloatType, + /** double precision floating point atomic type */ + DoubleType, + /** string reference atomic type */ + StringRefType, + /** element reference atomic type */ + ElementRefType, + /** memory reference atomic type */ + MemoryRefType, + /** void reference atomic type */ + RawRefType, + /** resolver atomic type */ + ResolverType, + /** ID resolver atomic type */ + IDResolverType, + /** string token atomic type */ + TokenType, + /** extension atomic type */ + ExtensionType + }; +}; + + +// This is a container class for storing a modifiable list of daeAtomicType objects. +class DLLSPEC daeAtomicTypeList { +public: + daeAtomicTypeList(DAE& dae); + ~daeAtomicTypeList(); + + /** + * Appends a new type to the list. + * @param t Type to append. + * @return Returns the index of the type in the list. + */ + daeInt append(daeAtomicType* t); + + /** + * Gets a type from the list of types, based on its index. + * @param index Index of the type to retrieve. + * @return Returns the @c daeAtomicType indicated by index. + */ + const daeAtomicType* getByIndex(daeInt index); + + /** + * Gets the number of atomic types in the list. + * @return Returns the number of atomic types in the list. + */ + daeInt getCount(); + + /** + * Finds a type by its string name. + * @param type String name of the type. + * @return Returns the type with the corresponding name. + */ + daeAtomicType* get(daeStringRef type); + + /** + * Finds a type by its enum. + * @param type Enum representing the desired type. + * @return Returns the type with the corresponding enum. + */ + daeAtomicType* get(daeEnum type); + +private: + daeAtomicTypeArray types; +}; + + +/** + * The @c daeBoolType class is derived from @c daeAtomicType, and implements + * the reflective system for objects of type @c daeBool. + */ +class DLLSPEC daeBoolType : public daeAtomicType +{ +public: + /** + * Constructor + */ + daeBoolType(DAE& dae); +public: + virtual daeBool memoryToString(daeChar* src, std::ostringstream& dst); + + virtual daeBool stringToMemory(daeChar* src, daeChar* dst); + + virtual daeMemoryRef create(); + + virtual void destroy(daeMemoryRef obj); + + virtual void copy(daeChar* src, daeChar* dst); + + virtual daeArray* createArray(); +}; + +/** + * The @c daeIntType class is derived from @c daeAtomicType, and implements + * the reflective system for objects of type @c daeInt. + */ +class DLLSPEC daeIntType : public daeAtomicType +{ +public: + /** + * Constructor + */ + daeIntType(DAE& dae); +public: + virtual daeBool memoryToString(daeChar* src, std::ostringstream& dst); + + virtual daeMemoryRef create(); + + virtual void destroy(daeMemoryRef obj); + + virtual void copy(daeChar* src, daeChar* dst); + + virtual daeArray* createArray(); +}; + +/** + * The @c daeLongType class is derived from @c daeAtomicType, and implements + * the reflective system for objects of type @c daeLong. + */ +class DLLSPEC daeLongType : public daeAtomicType +{ +public: + /** + * Constructor + */ + daeLongType(DAE& dae); +public: + virtual daeBool memoryToString(daeChar* src, std::ostringstream& dst); + + virtual daeMemoryRef create(); + + virtual void destroy(daeMemoryRef obj); + + virtual void copy(daeChar* src, daeChar* dst); + + virtual daeArray* createArray(); +}; + +/** + * The @c daeUIntType class is derived from @c daeAtomicType, and implements + * the reflective system for objects of type @c daeUInt. + */ +class DLLSPEC daeUIntType : public daeAtomicType +{ +public: + /** + * Constructor + */ + daeUIntType(DAE& dae); +public: + virtual daeBool memoryToString(daeChar* src, std::ostringstream& dst); + + virtual daeMemoryRef create(); + + virtual void destroy(daeMemoryRef obj); + + virtual void copy(daeChar* src, daeChar* dst); + + virtual daeArray* createArray(); +}; + +/** + * The @c daeUIntType class is derived from @c daeAtomicType, and implements + * the reflective system for objects of type @c daeUInt. + */ +class DLLSPEC daeULongType : public daeAtomicType +{ +public: + /** + * Constructor + */ + daeULongType(DAE& dae); +public: + virtual daeBool memoryToString(daeChar* src, std::ostringstream& dst); + + virtual daeMemoryRef create(); + + virtual void destroy(daeMemoryRef obj); + + virtual void copy(daeChar* src, daeChar* dst); + + virtual daeArray* createArray(); +}; + +/** + * The @c daeShortType is derived from @c daeAtomicType, and implements + * the reflective system for objects of type @c daeShort. + */ +class DLLSPEC daeShortType : public daeAtomicType +{ +public: + /** + * Constructor + */ + daeShortType(DAE& dae); +public: + virtual daeBool memoryToString(daeChar* src, std::ostringstream& dst); + + virtual daeMemoryRef create(); + + virtual void destroy(daeMemoryRef obj); + + virtual void copy(daeChar* src, daeChar* dst); + + virtual daeArray* createArray(); +}; + +/** + * The @c daeFloatType is derived from @c daeAtomicType, and implements + * the reflective system for objects of type @c daeFloat. + */ +class DLLSPEC daeFloatType : public daeAtomicType +{ +public: + /** + * Constructor + */ + daeFloatType(DAE& dae); +public: + virtual daeBool memoryToString(daeChar* src, std::ostringstream& dst); + + virtual daeBool stringToMemory(daeChar* src, daeChar* dst); + + virtual daeMemoryRef create(); + + virtual void destroy(daeMemoryRef obj); + + virtual void copy(daeChar* src, daeChar* dst); + + virtual daeArray* createArray(); +}; + +/** + * The @c daeDoubleType is derived from @c daeAtomicType, and implements + * the reflective system for objects of type @c daeDouble. + */ +class DLLSPEC daeDoubleType : public daeAtomicType +{ +public: + /** + * Constructor + */ + daeDoubleType(DAE& dae); +public: + virtual daeBool memoryToString(daeChar* src, std::ostringstream& dst); + + virtual daeBool stringToMemory(daeChar* src, daeChar* dst); + + virtual daeMemoryRef create(); + + virtual void destroy(daeMemoryRef obj); + + virtual void copy(daeChar* src, daeChar* dst); + + virtual daeArray* createArray(); +}; + +/** + * The @c daeStringRefType class is derived from @c daeAtomicType, and implements + * the reflective system for objects of type @c daeStringRef. + */ +class DLLSPEC daeStringRefType : public daeAtomicType +{ +public: + /** + * Constructor + */ + daeStringRefType(DAE& dae); +public: + virtual daeBool memoryToString(daeChar* src, std::ostringstream& dst); + + virtual daeBool stringToMemory(daeChar* src, daeChar* dst); + + virtual daeInt compare(daeChar* value1, daeChar* value2); + + virtual daeMemoryRef create(); + + virtual void destroy(daeMemoryRef obj); + + virtual void copy(daeChar* src, daeChar* dst); + + virtual daeArray* createArray(); +}; + +/** + * The @c daeTokenType class is derived from @c daeStringRefType, and implements + * the reflective system for objects of type daeStringRef, with specialized + * treatment from the parser. + */ +class DLLSPEC daeTokenType : public daeStringRefType +{ +public: + /** + * Constructor + */ + daeTokenType(DAE& dae); + +public: + virtual daeBool stringToMemory(daeChar* src, daeChar* dst); + + virtual daeMemoryRef create(); + + virtual void destroy(daeMemoryRef obj); + + virtual void copy(daeChar* src, daeChar* dst); + + virtual daeArray* createArray(); +}; + +/** + * The @c daeElementRefType class is derived from @c daeAtomicType, and implements + * the reflective system for objects of type @c daeElementRef. + */ +class DLLSPEC daeElementRefType : public daeAtomicType +{ +public: + /** + * The @c daeMetaElement for the type this @c daeElementRefType represents. + */ + daeMetaElement* _elementType; + +public: + /** + * Constructor + */ + daeElementRefType(DAE& dae); +public: + virtual daeBool memoryToString(daeChar* src, std::ostringstream& dst); + + virtual daeMemoryRef create(); + + virtual void destroy(daeMemoryRef obj); + + virtual void copy(daeChar* src, daeChar* dst); + + virtual daeArray* createArray(); +}; + +/** + * The @c daeEnumType class is derived from @c daeAtomicType, and implements + * the reflective system for objects of type daeEnum. + */ +class DLLSPEC daeEnumType: public daeAtomicType +{ +public: + /** + * The array which contains the values used in this enum. + */ + daeEnumArray* _values; + /** + * The array which contains the strings to associate with the values used in this enum. + */ + daeStringRefArray* _strings; + +public: + /** + * Constructor + */ + daeEnumType(DAE& dae); + + /** + * Destructor + */ + ~daeEnumType(); + +public: + virtual daeBool memoryToString(daeChar* src, std::ostringstream& dst); + + virtual daeBool stringToMemory(daeChar* src, daeChar* dst); + + virtual daeMemoryRef create(); + + virtual void destroy(daeMemoryRef obj); + + virtual void copy(daeChar* src, daeChar* dst); + + virtual daeArray* createArray(); +}; + +/** + * The @c daeRawRefType class is derived from @c daeAtomicType, and implements + * the reflective system for objects of type @c daeRawRef. + */ +class DLLSPEC daeRawRefType: public daeAtomicType +{ +public: + /** + * Constructor. + */ + daeRawRefType(DAE& dae); + +public: + virtual daeBool memoryToString(daeChar* src, std::ostringstream& dst); + + virtual daeMemoryRef create(); + + virtual void destroy(daeMemoryRef obj); + + virtual void copy(daeChar* src, daeChar* dst); + + virtual daeArray* createArray(); +}; + +/** + * The @c daeResolverType class is derived from @c daeAtomicType, and implements + * the reflective system for objects of type @c daeResolver. + */ +class DLLSPEC daeResolverType : public daeAtomicType +{ +public: + /** + * Constructor. + */ + daeResolverType(DAE& dae); +public: + virtual daeBool memoryToString(daeChar* src, std::ostringstream& dst); + + virtual daeBool stringToMemory(daeChar* src, daeChar* dst); + + virtual daeInt compare(daeChar* value1, daeChar* value2); + + virtual daeMemoryRef create(); + + virtual void destroy(daeMemoryRef obj); + + virtual void copy(daeChar* src, daeChar* dst); + + virtual daeArray* createArray(); + + virtual void setDocument(daeChar* value, daeDocument* doc); + + virtual void setDocument(daeArray& array, daeDocument* doc); +}; + +/** + * The @c daeIDResolverType class is derived from @c daeAtomicType, and implements + * the reflective system for objects of type @c daeIDResolver. + */ +class DLLSPEC daeIDResolverType : public daeAtomicType +{ +public: + /** + * Constructor. + */ + daeIDResolverType(DAE& dae); +public: + virtual daeBool memoryToString(daeChar* src, std::ostringstream& dst); + + virtual daeBool stringToMemory(daeChar* src, daeChar* dst); + + virtual daeInt compare(daeChar* value1, daeChar* value2); + + virtual daeMemoryRef create(); + + virtual void destroy(daeMemoryRef obj); + + virtual void copy(daeChar* src, daeChar* dst); + + virtual daeArray* createArray(); +}; + + + +#endif // __DAE_ATOMIC_TYPE_H__ + + + diff --git a/Engine/lib/collada/include/dae/daeDatabase.h b/Engine/lib/collada/include/dae/daeDatabase.h new file mode 100644 index 000000000..f52743aaa --- /dev/null +++ b/Engine/lib/collada/include/dae/daeDatabase.h @@ -0,0 +1,334 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_DATABASE__ +#define __DAE_DATABASE__ + +#include +#include +#include +#include +#include +#include + + +/** + * The @c daeDatabase class defines the COLLADA runtime database interface. + */ +class DLLSPEC daeDatabase +{ +public: + /** + * Constructor + */ + daeDatabase(DAE& dae); + + /** + * Destructor + */ + virtual ~daeDatabase() {} + + /** + * Get the associated DAE object. + * @return The associated DAE object. + */ + virtual DAE* getDAE(); + + /** + * Creates a new document, defining its root as the dom object; returns an error if the document name already exists. + * @param name Name of the new document, must be a valid URI. + * @param dom Existing @c domCOLLADA root element of the document + * @param document Pointer to a @c daeDocument pointer that receives the document created + * @return Returns @c DAE_OK if the document was created successfully, otherwise returns a negative value as defined in daeError.h. + * @note The @c daeElement passed in as dom should always be a @c domCOLLADA object, the API may enforce this in the future. + * @deprecated This function will be removed in future versions. Please use createDocument. + */ + virtual daeInt insertDocument(daeString name, daeElement* dom, daeDocument** document = NULL) = 0; + /** + * Creates a new @c domCOLLADA root element and a new document; returns an error if the document name already exists. + * @param name Name of the new document, must be a valid URI. + * @param document Pointer to a @c daeDocument pointer that receives the document created + * @return Returns DAE_OK if the document was created successfully, otherwise returns a negative value as defined in daeError.h. + * @deprecated This function will be removed in future versions. Please use createDocument. + */ + virtual daeInt insertDocument(daeString name, daeDocument** document = NULL) = 0; + /** + * Creates a new document, defining its root as the dom object; returns an error if the document name already exists. + * @param name Name of the new document, must be a valid URI. + * @param dom Existing @c domCOLLADA root element of the document + * @param document Pointer to a @c daeDocument pointer that receives the document created + * @return Returns @c DAE_OK if the document was created successfully, otherwise returns a negative value as defined in daeError.h. + * @note The @c daeElement passed in as dom should always be a @c domCOLLADA object, the API may enforce this in the future. + */ + virtual daeInt createDocument(daeString name, daeElement* dom, daeDocument** document = NULL) = 0; + /** + * Creates a new @c domCOLLADA root element and a new document; returns an error if the document name already exists. + * @param name Name of the new document, must be a valid URI. + * @param document Pointer to a @c daeDocument pointer that receives the document created + * @return Returns DAE_OK if the document was created successfully, otherwise returns a negative value as defined in daeError.h. + */ + virtual daeInt createDocument(daeString name, daeDocument** document = NULL) = 0; + + /** + * Inserts an already existing document into the database. + * @param c The document to insert. + * @return Returns DAE_OK if the document was inserted successfully, otherwise returns a negative value as defined in daeError.h. + */ + virtual daeInt insertDocument( daeDocument *c ) = 0; + + /** + * Removes a document from the database. + * @param document Document to remove from the database + * @return Returns DAE_OK if the document was successfully removed, otherwise returns a negative value as defined in daeError.h. + */ + virtual daeInt removeDocument(daeDocument* document) = 0; + /** + * Gets the number of documents. + * @return Returns the number of documents. + */ + virtual daeUInt getDocumentCount() = 0; + /** + * Gets a document based on the document index. + * @param index Index of the document to get. + * @return Returns a pointer on the document, or NULL if not found. + */ + virtual daeDocument* getDocument(daeUInt index) = 0; + /** + * Gets a document based on the document index. + * @param index Index of the document to get. + * @return Returns a pointer on the document, or NULL if not found. + */ + daeDocument* getDoc(daeUInt index); + /** + * Gets a document based on the document name. + * @param name The name of the document as a URI. + * @param skipUriNormalization Use the document name as is; don't normalize it first. + * This is mostly for improved performance. + * @return Returns a pointer to the document, or NULL if not found. + * @note If the URI contains a fragment, the fragment is stripped off. + */ + virtual daeDocument* getDocument(daeString name, bool skipUriNormalization = false) = 0; + /** + * Gets a document name. + * @param index Index of the document to get. + * @return Returns the name of the document at the given index. + */ + virtual daeString getDocumentName(daeUInt index) = 0; + /** + * Indicates if a document is loaded or not. + * @param name Name of the document as a URI. + * @return Returns true if the document is loaded, false otherwise. + * @note If the URI contains a fragment, the fragment is stripped off. + */ + virtual daeBool isDocumentLoaded(daeString name) = 0; + + /** + * Inserts a @c daeElement into the runtime database. + * @param document Document in which the @c daeElement lives. + * @param element @c daeElement to insert in the database + * @return Returns @c DAE_OK if element successfully inserted, otherwise returns a negative value as defined in daeError.h. + */ + virtual daeInt insertElement(daeDocument* document, + daeElement* element) = 0; + /** + * Removes a @c daeElement from the runtime database; not implemented in the reference STL implementation. + * @param document Document in which the @c daeElement lives. + * @param element Element to remove. + * @return Returns @c DAE_OK if element successfully removed, otherwise returns a negative value as defined in daeError.h. + */ + virtual daeInt removeElement(daeDocument* document, + daeElement* element) = 0; + /** + * Updates the database to reflect a change to the ID of a @c daeElement. + * @param element @c daeElement whose ID is going to change. + * @param newID The ID that will be assigned to the element. + * @return Returns @c DAE_OK if the database was successfully updated, otherwise returns a negative value as defined in daeError.h. + * @note The database doesn't actually change the ID of the element, it + * merely updates its internal structures to reflect the change. It's + * expected that the ID will be assigned to the element by someone else. + */ + virtual daeInt changeElementID(daeElement* element, + daeString newID) = 0; + + /** + * Updates the database to reflect a change to the sid of a @c daeElement. + * @param element @c daeElement whose sid is going to change. + * @param newSID The sid that will be assigned to the element. + * @return Returns @c DAE_OK if the database was successfully updated, otherwise returns a negative value as defined in daeError.h. + * @note The database doesn't actually change the sid of the element, it + * merely updates its internal structures to reflect the change. It's + * expected that the sid will be assigned to the element by someone else. + * Note - This function currently isn't implemented in the default database. + */ + virtual daeInt changeElementSID(daeElement* element, + daeString newSID) = 0; + + /** + * Unloads all of the documents of the runtime database. + * This function frees all the @c dom* objects created so far, + * except any objects on which you still have a smart pointer reference (@c daeSmartRef). + * @return Returns @c DAE_OK if all documents successfully unloaded, otherwise returns a negative value as defined in daeError.h. + */ + virtual daeInt clear() = 0; + + /** + * Lookup elements by ID, searching through all documents. + * @param id The ID to match on. + * @return The array of matching elements. + */ + virtual std::vector idLookup(const std::string& id) = 0; + + /** + * Find an element with the given ID in a specific document. + * @param id The ID to match on. + * @param doc The document to search in. + * @return The matching element if one is found, NULL otherwise. + */ + daeElement* idLookup(const std::string& id, daeDocument* doc); + + /** + * Lookup elements by type ID. + * @param typeID The type to match on, e.g. domNode::ID(). + * @param doc The document to search in, or NULL to search in all documents. + * @return The array of matching elements. + */ + std::vector typeLookup(daeInt typeID, daeDocument* doc = NULL); + + /** + * Same as the previous method, but returns the array of matching elements via a + * reference parameter for additional efficiency. + * @param typeID The type to match on, e.g. domNode::ID(). + * @param matchingElements The array of matching elements. + * @param doc The document to search in, or NULL to search in all documents. + */ + virtual void typeLookup(daeInt typeID, + std::vector& matchingElements, + daeDocument* doc = NULL) = 0; + + /** + * Lookup elements by type ID. + * @param doc The document to search in, or NULL to search in all documents. + * @return The array of matching elements. + */ + template + std::vector typeLookup(daeDocument* doc = NULL) { + std::vector result; + typeLookup(result, doc); + return result; + } + + /** + * Same as the previous method, but returns the array of matching elements via a + * reference parameter for additional efficiency. + * @param matchingElements The array of matching elements. + * @param doc The document to search in, or NULL to search in all documents. + */ + template void + typeLookup(std::vector& matchingElements, daeDocument* doc = NULL) { + std::vector elts; + typeLookup(T::ID(), elts, doc); + matchingElements.clear(); + matchingElements.reserve(elts.size()); + for (size_t i = 0; i < elts.size(); i++) + matchingElements.push_back((T*)elts[i]); + } + + /** + * Lookup elements by sid. + * @param sid The sid to match on. + * @param doc The document to search in, or NULL to search in all documents. + * @return The array of matching elements. + * Note - This function currently isn't implemented in the default database. + */ + std::vector sidLookup(const std::string& sid, daeDocument* doc = NULL); + + /** + * Same as the previous method, but the results are returned via a parameter instead + * of a return value, for extra efficiency. + * @param sid The sid to match on. + * @param matchingElements The array of matching elements. + * @param doc The document to search in, or NULL to search in all documents. + * Note - This function currently isn't implemented in the default database. + */ + virtual void sidLookup(const std::string& sid, + std::vector& matchingElements, + daeDocument* doc = NULL) = 0; + + /** + * Sets the top meta object. + * Called by @c dae::setDatabase() when the database changes. It passes to this function the + * top meta object, which is the root of a + * hierarchy of @c daeMetaElement objects. This top meta object is capable of creating + * any of the root objects in the DOM tree. + * @param _topMeta Top meta object to use to create objects to fill the database. + * @return Returns DAE_OK if successful, otherwise returns a negative value defined in daeError.h. + */ + virtual daeInt setMeta(daeMetaElement *_topMeta) = 0; + +public: + // The following methods are deprecated, and it's recommended that you don't use them. + // Where appropriate, alternative methods are specified. + + virtual daeUInt getTypeCount() = 0; + virtual daeString getTypeName(daeUInt index) = 0; + + // Instead of the following two methods, use idLookup or typeLookup. + virtual daeUInt getElementCount(daeString name = NULL, + daeString type = NULL, + daeString file = NULL) = 0; + virtual daeInt getElement(daeElement** pElement, + daeInt index, + daeString name = NULL, + daeString type = NULL, + daeString file = NULL ) = 0; + + inline daeInt insertCollection(daeString name, daeElement* dom, daeDocument** document = NULL) { + return insertDocument( name, dom, document ); + } + inline daeInt insertCollection(daeString name, daeDocument** document = NULL) { + return insertDocument( name, document ); + } + inline daeInt createCollection(daeString name, daeElement* dom, daeDocument** document = NULL) { + return createDocument( name, dom, document ); + } + inline daeInt createCollection(daeString name, daeDocument** document = NULL) { + return createDocument( name, document ); + } + inline daeInt insertCollection( daeDocument *c ) { + return insertDocument( c ); + } + inline daeInt removeCollection(daeDocument* document) { + return removeDocument( document ); + } + inline daeUInt getCollectionCount() { + return getDocumentCount(); + } + inline daeDocument* getCollection(daeUInt index) { + return getDocument( index ); + } + inline daeDocument* getCollection(daeString name) { + return getDocument( name ); + } + inline daeString getCollectionName(daeUInt index) { + return getDocumentName( index ); + } + inline daeBool isCollectionLoaded(daeString name) { + return isDocumentLoaded( name ); + } + +protected: + DAE& dae; +}; + +#endif //__DAE_DATABASE__ + diff --git a/Engine/lib/collada/include/dae/daeDocument.h b/Engine/lib/collada/include/dae/daeDocument.h new file mode 100644 index 000000000..d96e7081b --- /dev/null +++ b/Engine/lib/collada/include/dae/daeDocument.h @@ -0,0 +1,153 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_DOCUMENT__ +#define __DAE_DOCUMENT__ + +#include +#include +#include +#include + +class DAE; +class daeDatabase; + +/** + * The @c daeDocument class implements a COLLADA runtime database entry. + */ +class DLLSPEC daeDocument +{ +public: + /** + * Constructor + * @param dae The dae that owns this document. + */ + daeDocument(DAE& dae); + + /** + * Destructor + */ + ~daeDocument(); + + /** + * Accessor to get the @c domCollada associated with this document. + * @return A @c daeElementRef for the @c domCollada that is the root of this document. + * @note This function should really return a domColladaRef, + * but we're trying to avoid having @c dae classes depend on generated dom classes. + */ + daeElement* getDomRoot() const {return(dom);} + /** + * Accessor to set the domCollada associated with this document + * @param domRoot the domCollada that is the root of this document + * @remarks Should really require a domColladaRef but we're trying to avoid having dae classes depend on generated dom classes. + */ + void setDomRoot(daeElement* domRoot) {dom = domRoot; domRoot->setDocument(this); } + /** + * Accessor to get the URI associated with the document in this document; + * this is currently set to the URI from which the document was loaded, but + * is blank if the document was created with @c insertDocument(). + * @return Returns a pointer to the URI for this document. + * @note This is the full URI of the document and not the document base URI. + */ + daeURI* getDocumentURI() {return (&uri);} + + /** + * Const accessor to get the URI associated with the document in this collection; + * this is currently set to the URI from which the collection was loaded, but + * is blank if the collection was created with @c insertCollection(). + * @return Returns a pointer to the URI for this collection. + * @note This is the full URI of the document and not the document base URI. + */ + const daeURI* getDocumentURI() const {return (&uri);} + + /** + * Accessor to get the DAE that owns this document. + * @return Returns the DAE that owns this document. + */ + DAE* getDAE(); + + /** + * Accessor to get the database associated with this document. + * @return Returns the database associated with this document. + */ + daeDatabase* getDatabase(); + + /** + * This function is used to track how a document gets modified. It gets called internally. + * @param element The element that was added to this document. + * @note This function is called internally and not meant to be called by the client application. + * Calling this function from the client application may result in unexpected behavior. + */ + void insertElement( daeElementRef element ); + /** + * This function is used to track how a document gets modified. It gets called internally. + * @param element The element that was removed from this document. + * @note This function is called internally and not meant to be called by the client application. + * Calling this function from the client application may result in unexpected behavior. + */ + void removeElement( daeElementRef element ); + /** + * This function is used to track how a document gets modified. It gets called internally. + * @param element The element whose ID is about to be changed. + * @param newID The ID that is going to be assigned to the element. + * @note This function is called internally and not meant to be called by the client application. + * Calling this function from the client application may result in unexpected behavior. + */ + void changeElementID( daeElementRef element, daeString newID ); + /** + * This function is just like changeElementID, except it keeps track of sids instead of IDs. + * @param element The element whose sid is about to be changed. + * @param newSID The sid that is going to be assigned to the element. + * @note This function is called internally and not meant to be called by the client application. + * Calling this function from the client application may result in unexpected behavior. + */ + void changeElementSID( daeElementRef element, daeString newSID ); + /** + * Adds a URI to the list of external references in this document. + * @param uri The URI that is the external reference. + * @note This function gets called internally from daeURI upon trying to resolve an element. + * Calling this function in your client code my result in unexpected behavior. + */ + void addExternalReference( daeURI &uri ); + /** + * Gets a list of all the documents that are referenced from URI contained within this document. + * @return Returns a list of URI strings, each being a URI which is referenced from within this document. + */ + const daeStringRefArray &getReferencedDocuments() const { return referencedDocuments; } + +private: + /** + * The DAE that owns this document. The DAE's database is notified by the document when + * elements are inserted, removed, or have their ID changed. + */ + DAE* dae; + + /** + * Top Level element for of the document, always a domCollada + * @remarks This member will eventually be taken private, use getDomRoot() to access it. + */ + daeElementRef dom; + + /** + * The URI of the document, may be blank if the document wasn't loaded from a URI + * @remarks This member will eventually be taken private, use getDocumentURI() to access it. + */ + daeURI uri; + + daeStringRefArray referencedDocuments; +}; + +typedef daeDocument daeCollection; + +#endif + diff --git a/Engine/lib/collada/include/dae/daeDom.h b/Engine/lib/collada/include/dae/daeDom.h new file mode 100644 index 000000000..f329f8e08 --- /dev/null +++ b/Engine/lib/collada/include/dae/daeDom.h @@ -0,0 +1,22 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_DOM__ +#define __DAE_DOM__ + +class daeMetaElement; +class DAE; + +daeMetaElement* initializeDomMeta(DAE& dae); + +#endif //__DAE_DOM__ diff --git a/Engine/lib/collada/include/dae/daeDomTypes.h b/Engine/lib/collada/include/dae/daeDomTypes.h new file mode 100644 index 000000000..17363ce64 --- /dev/null +++ b/Engine/lib/collada/include/dae/daeDomTypes.h @@ -0,0 +1,68 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_DOM_TYPES__ +#define __DAE_DOM_TYPES__ + +#include +#include +#include +#include +#include + +//This line is used as a workaround because the array types enum is invalid when autogenerated +//typedef daeString domArrayTypes; // ENUM +typedef daeElement domElement; + +typedef daeURI xsAnyURI; +typedef daeString xsDateTime; + +typedef daeString xsID; +typedef daeIDRef xsIDREF; +typedef daeTArray xsIDREFS; +typedef daeString xsNCName; +typedef daeString xsNMTOKEN; +typedef daeString xsName; +typedef daeString xsToken; +typedef daeString xsString; +typedef daeBool xsBoolean; +typedef daeShort xsShort; +typedef daeInt xsInt; +typedef daeLong xsInteger; +typedef daeUInt xsNonNegativeInteger; +typedef daeLong xsLong; +typedef daeFloat xsFloat; +typedef daeDouble xsDouble; +typedef daeDouble xsDecimal; +typedef daeCharArray xsHexBinaryArray; +typedef daeBoolArray xsBooleanArray; +typedef daeFloatArray xsFloatArray; +typedef daeDoubleArray xsDoubleArray; +typedef daeShortArray xsShortArray; +typedef daeIntArray xsIntegerArray; +typedef daeLongArray xsLongArray; +typedef daeStringRefArray xsNameArray; +typedef daeStringRefArray xsNCNameArray; +typedef daeStringRefArray xsTokenArray; + +typedef daeChar xsByte; +typedef daeUChar xsUnsignedByte; +typedef daeUInt xsUnsignedInt; +typedef daeUInt xsPositiveInteger; +typedef daeULong xsUnsignedLong; + + +#define daeTSmartRef daeSmartRef + +#endif //__DAE_DOM_TYPES__ + diff --git a/Engine/lib/collada/include/dae/daeElement.h b/Engine/lib/collada/include/dae/daeElement.h new file mode 100644 index 000000000..84fa26061 --- /dev/null +++ b/Engine/lib/collada/include/dae/daeElement.h @@ -0,0 +1,556 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_ELEMENT_H__ +#define __DAE_ELEMENT_H__ +#include +#include +#include +#include +#include +#include +#include + +//#ifndef NO_MALLOC_HEADER +//#include +//#endif + +namespace COLLADA_TYPE +{ + typedef int TypeEnum; +} + +class DAE; +class daeMetaElement; +class daeMetaAttribute; +class daeDocument; +class daeURI; + +/** + * The @c daeElement class represents an instance of a COLLADA "Element"; + * it is the main base class for the COLLADA Dom. + * Features of this class include: + * - Uses factory concepts defined via daeMetaElement + * - Composed of attributes, content elements and content values + * - Reference counted via daeSmartRef + * - Contains information for XML base URI, and XML containing element + */ +class DLLSPEC daeElement : public daeRefCountedObj +{ +public: + /** + * Macro that defines new and delete overrides for this class + */ + DAE_ALLOC +protected: + daeElement* _parent; + daeDocument* _document; + daeMetaElement* _meta; + daeString _elementName; + daeBoolArray _validAttributeArray; + void* _userData; + +protected: + daeElement( const daeElement &cpy ) : daeRefCountedObj() { (void)cpy; }; + virtual daeElement &operator=( const daeElement &cpy ) { (void)cpy; return *this; } + + void init(); + + // These functions are called internally. + void setDocument( daeDocument* c, bool notifyDocument ); + daeElement* simpleAdd(daeString name, int index = -1); + +public: + /** + * Element Constructor. + * @note This should not be used externally. + * Use factories to create elements + */ + daeElement(); + /** + * Element Constructor. + * @note This should not be used externally. + * Use factories to create elements + */ + daeElement(DAE& dae); + + /** + * Element Destructor. + * @note This should not be used externally, + * if daeSmartRefs are being used. + */ + virtual ~daeElement(); + + /** + * Sets up a @c daeElement. Called on all @c daeElements as part of their initialization. + * @param meta Meta element to use to configure this element. + * @note Should not be called externally. + */ + void setup(daeMetaElement* meta); + + // These functions are for adding child elements. They return null if adding + // the element failed. + daeElement* add(daeString name, int index = -1); + daeElement* add(daeElement* elt, int index = -1); + daeElement* addBefore(daeElement* elt, daeElement* index); + daeElement* addAfter(daeElement* elt, daeElement* index); + + // These functions are deprecated. Use 'add' instead. + daeElement* createAndPlace(daeString elementName); + daeElement* createAndPlaceAt(daeInt index, daeString elementName); + daeBool placeElement(daeElement* element); + daeBool placeElementAt(daeInt index, daeElement* element); + daeBool placeElementBefore( daeElement* marker, daeElement *element ); + daeBool placeElementAfter( daeElement* marker, daeElement *element ); + + /** + * Finds the last index into the array of children of the name specified. + * @param elementName The name to look for. + * @return Returns the index into the children array of the last element with name elementName. -1 if + * there are no children of name elementName. + */ + daeInt findLastIndexOf( daeString elementName ); + + /** + * Removes the specified element from it parent, the @c this element. + * This function is the opposite of @c placeElement(). It removes the specified + * element from the _contents array, and from wherever else it appears + * inside of the @c this element. Use this function instead of @c clear(), @c remove() or @c delete() + * if you want to keep the _contents field up-to-date. + * + * @param element Element to be removed in the @c this container. + * @return Returns true if the element was successfully removed, false otherwise. + */ + daeBool removeChildElement(daeElement* element); + + /** + * Removes the specified element from its parent element. + * This function is the opposite of @c placeElement(). It removes the specified + * element from both the _contents array and from wherever else it appears + * inside of its parent. The function itself finds the parent, and is defined as a static method, + * since removing the element from its parent may result in the deletion of the element. + * If the element has no parent, nothing is done. + * + * Use this function instead of @c clear(), @c remove() or @c delete() + * if you want to keep _contents up-to-date. + * + * @param element Element to remove from its parent container, the function finds the parent element. + * @return Returns true if the element was successfully removed, false otherwise. + */ + static daeBool removeFromParent(daeElement* element) + { + if(element != NULL && element->_parent != NULL) + return(element->_parent->removeChildElement(element)); + return false; + }; + + /** + * Returns the number of attributes in this element. + * @return The number of attributes this element has. + */ + size_t getAttributeCount(); + + /** + * Returns the daeMetaAttribute object corresponding to the attribute specified. + * @param name The name of the attribute to find. + * @return Returns the corresponding daeMetaAttribute object or NULL if this element + * doesn't have the specified attribute. + */ + daeMetaAttribute* getAttributeObject(daeString name); + + /** + * Returns the daeMetaAttribute object corresponding to attribute i. + * @param i The index of the attribute to find. + * @return Returns the corresponding daeMetaAttribute object + */ + daeMetaAttribute* getAttributeObject(size_t i); + + /** + * Returns the name of the attribute at the specified index. + * @param i The index of the attribute whose name should be retrieved. + * @return Returns the name of the attribute, or "" if the index is out of range. + */ + std::string getAttributeName(size_t i); + + /** + * Checks if this element can have the attribute specified. + * @param name The name of the attribute to look for. + * @return Returns true is this element can have an attribute with the name specified. False otherwise. + */ + daeBool hasAttribute(daeString name); + + /** + * Checks if an attribute has been set either by being loaded from the COLLADA document or set + * programmatically. + * @param name The name of the attribute to check. + * @return Returns true if the attribute has been set. False if the attribute hasn't been set + * or doesn't exist for this element. + */ + daeBool isAttributeSet(daeString name); + + /** + * Gets an attribute's value as a string. + * @param name The name of the attribute. + * @return The value of the attribute. Returns an empty string if this element doesn't + * have the specified attribute. + */ + std::string getAttribute(daeString name); + + /** + * Just like the previous method, this method gets an attribute's value as a string. It + * takes the string as a reference parameter instead of returning it, for extra efficiency. + * @param name The name of the attribute. + * @param A string in which to store the value of the attribute. This will be set to an empty + * string if this element doesn't have the specified attribute. + */ + void getAttribute(daeString name, std::string& value); + + /** + * Gets an attribute's value as a string. + * @param i The index of the attribute to retrieve. + * @return The value of the attribute. + */ + std::string getAttribute(size_t i); + + /** + * Just like the previous method, this method gets an attribute's value as a string. It + * takes the string as a reference parameter instead of returning it, for extra efficiency. + * @param i The index of the attribute to retrieve. + * @param A string in which to store the value of the attribute. + */ + void getAttribute(size_t i, std::string& value); + + struct DLLSPEC attr { + attr(); + attr(const std::string& name, const std::string& value); + + std::string name; + std::string value; + }; + + /** + * Returns an array containing all the attributes of this element. + * @return A daeArray of attr objects. + */ + daeTArray getAttributes(); + + /** + * Just like the previous method, this method returns an array containing all the attributes + * of this element. It returns the result via a reference parameter for extra efficiency. + * @param attrs The array of attr objects to return. + */ + void getAttributes(daeTArray& attrs); + + /** + * Sets the attribute to the specified value. + * @param name Attribute to set. + * @param value Value to apply to the attribute. + * @return Returns true if the attribute was found and the value was set, false otherwise. + */ + virtual daeBool setAttribute(daeString name, daeString value); + + /** + * Sets the attribute at the specified index to the given value. + * @param i Index of the attribute to set. + * @param value Value to apply to the attribute. + * @return Returns true if the attribute was found and the value was set, false otherwise. + */ + virtual daeBool setAttribute(size_t i, daeString value); + + /** + * Returns the daeMetaAttribute object corresponding to the character data for this element. + * @return Returns a daeMetaAttribute object or NULL if this element doesn't have + * character data. + */ + daeMetaAttribute* getCharDataObject(); + + /** + * Checks if this element can have character data. + * @return Returns true if this element can have character data, false otherwise. + */ + daeBool hasCharData(); + + /** + * Returns this element's character data as a string. + * @return A string containing this element's character data, or an empty string + * if this element can't have character data. + */ + std::string getCharData(); + + /** + * Similar to the previous method, but fills a string passed in by the user for efficiency. + * @param data The string to be filled with this element's character content. The + * string is set to an empty string if this element can't have character data. + */ + void getCharData(std::string& data); + + /** + * Sets this element's character data. + * @param data The new character data of this element. + * @return Returns true if this element can have character data and the character data + * was successfully changed, false otherwise. + */ + daeBool setCharData(const std::string& data); + + // These functions are deprecated. + daeMemoryRef getAttributeValue(daeString name); // Use getAttribute or getAttributeObject instead. + daeBool hasValue(); // Use hasCharData instead. + daeMemoryRef getValuePointer(); // Use getCharData or getCharDataObject instead. + + /** + * Finds the database document associated with @c this element. + * @return Returns the @c daeDocument representing the containing file or database + * group. + */ + daeDocument* getDocument() const { return _document; } + + /** + * Deprecated. + */ + daeDocument* getCollection() const { return _document; } + + /** + * Get the associated DAE object. + * @return The associated DAE object. + */ + DAE* getDAE(); + + /** + * Sets the database document associated with this element. + * @param c The daeDocument to associate with this element. + */ + void setDocument(daeDocument* c) { setDocument( c, true ); } + /** + * Deprecated. + */ + void setCollection(daeDocument* c ); + + /** + * Gets the URI of the document containing this element, note that this is NOT the URI of the element. + * @return Returns a pointer to the daeURI of the document containing this element. + */ + daeURI* getDocumentURI() const; + + /** + * Creates an element via the element factory system. This creation + * is based @em only on potential child elements of this element. + * @param elementName Class name of the subelement to create. + * @return Returns the created @c daeElement, if it was successfully created. + */ + daeSmartRef createElement(daeString elementName); + + /** + * Gets the container element for @c this element. + * If @c createAndPlace() was used to create the element, its parent is the the caller of @c createAndPlace(). + * @return Returns the parent element, if @c this is not the top level element. + */ + daeElement* getParentElement() { return _parent;} + /** + * Deprecated. Use getParentElement() + * @deprecated + */ + daeElement* getXMLParentElement() { return _parent;} + /** + * Sets the parent element for this element. + * @param newParent The element which is the new parent element for this element. + * @note This function is called internally and not meant to be called form the client application. + */ + void setParentElement( daeElement *parent ) { _parent = parent; } + + // These are helper structures to let the xml hierarchy search functions know when we've + // found a match. You can implement a custom matcher by inheriting from this structure, + // just like matchName and matchType. + struct DLLSPEC matchElement { + virtual bool operator()(daeElement* elt) const = 0; + virtual ~matchElement() { }; + }; + + // Matches an element by name + struct DLLSPEC matchName : public matchElement { + matchName(daeString name); + virtual bool operator()(daeElement* elt) const; + std::string name; + }; + + // Matches an element by schema type + struct DLLSPEC matchType : public matchElement { + matchType(daeInt typeID); + virtual bool operator()(daeElement* elt) const; + daeInt typeID; + }; + + // Returns a matching child element. By "child", I mean one hierarchy level beneath the + // current element. This function is basically the same as getDescendant, except that it + // only goes one level deep. + daeElement* getChild(const matchElement& matcher); + + // Performs a breadth-first search and returns a matching descendant element. A "descendant + // element" is an element beneath the current element in the xml hierarchy. + daeElement* getDescendant(const matchElement& matcher); + + // Returns the parent element. + daeElement* getParent(); + + // Searches up through the xml hiearchy and returns a matching element. + daeElement* getAncestor(const matchElement& matcher); + + // These functions perform the same as the functions above, except that they take the element + // name to match as a string. This makes these functions a little simpler to use if you're + // matching based on element name, which is assumed to be the most common case. Instead of + // "getChild(matchName(eltName))", you can just write "getChild(eltName)". + daeElement* getChild(daeString eltName); + daeElement* getDescendant(daeString eltName); + daeElement* getAncestor(daeString eltName); + + /** + * Gets the associated Meta information for this element. This + * Meta also acts as a factory. See @c daeMetaElement documentation for more + * information. + * @return Returns the associated meta information. + */ + inline daeMetaElement* getMeta() { return _meta; } + + // These functions are deprecated. Use typeID instead. + virtual COLLADA_TYPE::TypeEnum getElementType() const { return (COLLADA_TYPE::TypeEnum)0; } + daeString getTypeName() const; + + /** + * Returns this element's type ID. Every element is an instance of a type specified in + * the Collada schema, and every schema type has a unique ID. + * @return The element's type ID. + */ + virtual daeInt typeID() const = 0; + + /** + * Gets this element's name. + * @return Returns the string for the name. + * @remarks This function returns NULL if the element's name is identical to it's type's name. + */ + daeString getElementName() const; + /** + * Sets this element's name. + * @param nm Specifies the string to use as the element's name. + * @remarks Use caution when using this function since you can easily create invalid COLLADA documents. + */ + void setElementName( daeString nm ); + + /** + * Gets the element ID if it exists. + * @return Returns the value of the ID attribute, if there is such + * an attribute on this element type. + * @return the string for the element ID if it exists. + */ + daeString getID() const; + + /** + * Gets the children/sub-elements of this element. + * This is a helper function used to easily access an element's children without the use of the + * _meta objects. This function adds the convenience of the _contents array to elements that do + * not contain a _contents array. + * @return The return value. An elementref array to append this element's children to. + */ + daeTArray< daeSmartRef > getChildren(); + + /** + * Same as the previous function, but returns the result via a parameter instead + * of a return value, for extra efficiency. + * @param array The return value. An elementref array to append this element's children to. + */ + //void getChildren( daeElementRefArray &array ); + void getChildren( daeTArray > &array ); + + /** + * Gets all the children of a particular type. + * @return An array containing the matching child elements. + */ + template + daeTArray< daeSmartRef > getChildrenByType() { + daeTArray< daeSmartRef > result; + getChildrenByType(result); + return result; + } + + /** + * Same as the previous function, but returns the result via a parameter instead + * of a return value, for extra efficiency. + * @return An array containing the matching child elements. + */ + template + void getChildrenByType(daeTArray< daeSmartRef >& matchingChildren) { + matchingChildren.setCount(0); + daeTArray< daeSmartRef > children; + getChildren(children); + for (size_t i = 0; i < children.getCount(); i++) + if (children[i]->typeID() == T::ID()) + matchingChildren.append((T*)children[i].cast()); + } + + /** + * Clones/deep copies this @c daeElement and all of it's subtree. + * @param idSuffix A string to append to the copied element's ID, if one exists. + * Default is no ID mangling. + * @param nameSuffix A string to append to the copied element's name, if one exists. + * Default is no name mangling. + * @return Returns a @c daeElement smartref of the copy of this element. + */ + daeSmartRef clone( daeString idSuffix = NULL, daeString nameSuffix = NULL ); + + // Class for reporting info about element comparisons + struct DLLSPEC compareResult { + int compareValue; // > 0 if elt1 > elt2, + // < 0 if elt1 < elt2, + // = 0 if elt1 = elt2 + daeElement* elt1; + daeElement* elt2; + bool nameMismatch; // true if the names didn't match + std::string attrMismatch; // The name of the mismatched attribute, or "" if there was no attr mismatch + bool charDataMismatch; // true if the char data didn't match + bool childCountMismatch; // true if the number of children didn't match + + compareResult(); + std::string format(); // Write to a string + }; + + // Function for doing a generic, recursive comparison of two xml elements. It + // also provides a full element ordering, so that you could store elements in + // a map or a set. Return val is > 0 if elt1 > elt2, < 0 if elt1 < elt2, and 0 + // if elt1 == elt2. + static int compare(daeElement& elt1, daeElement& elt2); + + // Same as the previous function, but returns a full compareResult object. + static compareResult compareWithFullResult(daeElement& elt1, daeElement& elt2); + + /** + * Sets the user data pointer attached to this element. + * @param data User's custom data to store. + */ + void setUserData(void* data); + + /** + * Gets the user data pointer attached to this element. + * @return User data pointer previously set with setUserData. + */ + void* getUserData(); + +public: + // This function is called internally + static void deleteCMDataArray(daeTArray& cmData); +}; + +#include +typedef daeSmartRef daeElementRef; +typedef daeSmartRef daeElementConstRef; +//#include +typedef daeTArray daeElementRefArray; + +#endif //__DAE_ELEMENT_H__ diff --git a/Engine/lib/collada/include/dae/daeError.h b/Engine/lib/collada/include/dae/daeError.h new file mode 100644 index 000000000..2dbfc91f1 --- /dev/null +++ b/Engine/lib/collada/include/dae/daeError.h @@ -0,0 +1,51 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE__ERROR__ +#define __DAE__ERROR__ + +#include + +/** Success */ +#define DAE_OK 0 +/** Fatal Error, should never be returned unless there is a bug in the library. */ +#define DAE_ERR_FATAL -1 +/** Call invalid, the combination of parameters given is invalid. */ +#define DAE_ERR_INVALID_CALL -2 +/** Generic error */ +#define DAE_ERROR -3 +/** IO error, the file hasn't been found or there is a problem with the IO plugin. */ +#define DAE_ERR_BACKEND_IO -100 +/** The IOPlugin backend wasn't able to successfully validate the data. */ +#define DAE_ERR_BACKEND_VALIDATION -101 +/** The IOPlugin tried to write to a file that already exists and the "replace" parameter was set to false */ +#define DAE_ERR_BACKEND_FILE_EXISTS -102 +/** Error in the syntax of the query. */ +#define DAE_ERR_QUERY_SYNTAX -200 +/** No match to the search criteria. */ +#define DAE_ERR_QUERY_NO_MATCH -201 +/** A document with that name already exists. */ +#define DAE_ERR_COLLECTION_ALREADY_EXISTS -202 +/** A document with that name does not exist. */ +#define DAE_ERR_COLLECTION_DOES_NOT_EXIST -203 +/** Function is not implemented. */ +#define DAE_ERR_NOT_IMPLEMENTED -1000 + + +/** Gets the ASCII error string. +* @param errorCode Error code returned by a function of the API. +* @return Returns an English string describing the error. +*/ +DLLSPEC const char *daeErrorString(int errorCode); + +#endif //__DAE__ERROR__ diff --git a/Engine/lib/collada/include/dae/daeErrorHandler.h b/Engine/lib/collada/include/dae/daeErrorHandler.h new file mode 100644 index 000000000..6d6499486 --- /dev/null +++ b/Engine/lib/collada/include/dae/daeErrorHandler.h @@ -0,0 +1,66 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef _DAE_ERROR_HANDLER_ +#define _DAE_ERROR_HANDLER_ + +#include +#include + +/** + * The @c daeErrorHandler class is a plugin that allows the use to overwrite how error and warning + * messages get handled in the client application. An example of this would be a class that reports + * the message to a gui front end instead of just printing on stdout. + */ +class DLLSPEC daeErrorHandler { +public: + /** + * Constructor. + */ + daeErrorHandler(); + /** + * Destructor. + */ + virtual ~daeErrorHandler(); + + /** + * This function is called when there is an error and a string needs to be sent to the user. + * You must overwrite this function in your plugin. + * @param msg Error message. + */ + virtual void handleError( daeString msg ) = 0; + /** + * This function is called when there is a warning and a string needs to be sent to the user. + * You must overwrite this function in your plugin. + * @param msg Warning message. + */ + virtual void handleWarning( daeString msg ) = 0; + + /** + * Sets the daeErrorHandler to the one specified. + * @param eh The new daeErrorHandler to use. Passing in NULL results in the default plugin being used. + */ + static void setErrorHandler( daeErrorHandler *eh ); + /** + * Returns the current daeErrorHandlerPlugin. A program has one globally-accessible + * daeErrorHandler active at a time. + * @return The current daeErrorHandler. + */ + static daeErrorHandler *get(); + +private: + static daeErrorHandler *_instance; + static std::auto_ptr _defaultInstance; +}; + +#endif diff --git a/Engine/lib/collada/include/dae/daeGCCPlatform.h b/Engine/lib/collada/include/dae/daeGCCPlatform.h new file mode 100644 index 000000000..5dbf11b70 --- /dev/null +++ b/Engine/lib/collada/include/dae/daeGCCPlatform.h @@ -0,0 +1,30 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_GCC_PLATFORM_H__ +#define __DAE_GCC_PLATFORM_H__ + +#define PLATFORM_INT8 char +#define PLATFORM_INT16 short +#define PLATFORM_INT32 int +#define PLATFORM_INT64 long long +#define PLATFORM_UINT8 unsigned char +#define PLATFORM_UINT16 unsigned short +#define PLATFORM_UINT32 unsigned int +#define PLATFORM_UINT64 unsigned long long +#define PLATFORM_FLOAT32 float +#define PLATFORM_FLOAT64 double + +#define DLLSPEC + +#endif diff --git a/Engine/lib/collada/include/dae/daeIDRef.h b/Engine/lib/collada/include/dae/daeIDRef.h new file mode 100644 index 000000000..abbb14633 --- /dev/null +++ b/Engine/lib/collada/include/dae/daeIDRef.h @@ -0,0 +1,235 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_IDREF_H__ +#define __DAE_IDREF_H__ + +#include +#include +#include +class DAE; + +/** + * The @c daeIDRef is a simple class designed to aid in the parsing and resolution of + * ID references inside of COLLADA elements. + * A @c daeIDRef is created for every IDREF data type in the COLLADA schema. + * It also has the capability to attempt to resolve this reference + * into a @c daeElement. If a @c daeIDRef is stored within a @c daeElement it fills + * in its container field to point to the containing element. + * + * The main API is the @c daeIDRef::resolveElement() will use a @c daeIDRefResolver + * to search for the @c daeElement inside of a @c daeDatabase. + * + */ +class DLLSPEC daeIDRef +{ +public: + /** + * An enum describing the status of the ID resolution process. + */ + enum ResolveState{ + /** No ID specified */ + id_empty, + /** ID specified but not resolved */ + id_loaded, + /** ID resolution pending */ + id_pending, + /** ID resolved correctly */ + id_success, + /** Resolution failed because ID was not found */ + id_failed_id_not_found, + /** Resolution failed because ID was invalid */ + id_failed_invalid_id, + /** Resoltion failed due to invalid reference */ + id_failed_invalid_reference, + /** Resolution failed due to an external error */ + id_failed_externalization, + /** Resolution failed because we don't have a document in which to search for the element. + This means you probably forgot to set a container element. */ + id_failed_no_document + }; + +private: + /** ID used to refer to another element */ + std::string id; + + /** Element that owns this ID (if any) */ + daeElement* container; + +public: + /** + * Simple Constructor + */ + daeIDRef(); + + /** + * Constructs an id reference via a string, using @c setID(); loads the status. + * @param id ID to construct a reference for, passed to @c setID() automatically. + */ + daeIDRef(daeString id); + + /** + * Constructs a new id reference by copying an existing one. + * @param constructFromIDRef @c daeIDRef to copy into this one. + */ + daeIDRef(const daeIDRef& constructFromIDRef); + + /** + * Constructs an id reference with a container element + * @param container The container element. + */ + daeIDRef(daeElement& container); + + /** + * Gets the ID string + * @return Returns the full ID string from id. + */ + daeString getID() const; + + /** + * Copies ID into the id data member. + * After the call to @c setID(), the state is set to @c id_loaded + * @param ID String to use to configure this @c daeIDRef. + */ + void setID(daeString ID); + + /** + * Gets the element that this URI resolves to in memory. + * @return Returns a ref to the element. + */ + daeElement* getElement() const; + + /** + * Gets a pointer to the @c daeElement that contains this URI. + * @return Returns the pointer to the containing daeElmement. + */ + daeElement* getContainer() const; + + /** + * Sets the pointer to the @c daeElement that contains this URI. + * @param cont Pointer to the containing @c daeElmement. + */ + void setContainer(daeElement* cont); + + /** + * Outputs all components of this @c daeIDRef to stderr. + */ + void print(); + + /** + * Resets this @c daeIDRef; frees all string references + * and returns state to @c empty. + */ + void reset(); + + /** + * Initializes the @c daeIDREf, setting id, element, and container to NULL. + */ + void initialize(); + + /** + * Comparison operator. + * @return Returns true if URI's are equal. + */ + bool operator==(const daeIDRef& other) const; + + /** + * Assignment operator. + * @return Returns a reference to this object. + */ + daeIDRef &operator=( const daeIDRef& other); + + // These methods are only provided for backwards compatibility. Use the listed alternatives. + daeIDRef &get( daeUInt idx ); // Never should have existed. No alternative. + size_t getCount() const; // Never should have existed. No alternative. + daeIDRef& operator[](size_t index); // Never should have existed. No alternative. + void resolveElement( daeString typeNameHint = NULL ); // Call getElement. No separate "resolve" step needed. + void resolveID(); // Never should have existed. No alternative. + void validate(); // Never should have existed. No alternative. + void copyFrom(const daeIDRef& from); // Use the assignment operator instead. + ResolveState getState() const; // Never should have existed. No alternative. +}; + +/** + * The @c daeIDRefResolver class is the plugin point for @c daeIDRef resolution. + * This class is an abstract base class that defines an interface for + * resolving @c daeIDRefs. + */ +class DLLSPEC daeIDRefResolver +{ +public: + /** + * Constructor + */ + daeIDRefResolver(DAE& dae); + + /** + * Destructor + */ + virtual ~daeIDRefResolver(); + + /** + * Provides an abstract interface to convert a @c daeIDRef into a @c daeElement. + * @param id The ID of the element to find. + * @param doc The document containing the element. + * @return Returns a daeElement with matching ID, if one is found. + */ + virtual daeElement* resolveElement(const std::string& id, daeDocument* doc) = 0; + + + /** + * Gets the name of this resolver. + * @return Returns the string name. + */ + virtual daeString getName() = 0; + +protected: + DAE* dae; +}; + + +/** + * The @c daeDefaultIDRefResolver resolves a @c daeIDRef by checking with a database. + * It is a concrete implementation for @c daeIDRefResolver. + */ +class DLLSPEC daeDefaultIDRefResolver : public daeIDRefResolver +{ +public: + daeDefaultIDRefResolver(DAE& dae); + ~daeDefaultIDRefResolver(); + virtual daeElement* resolveElement(const std::string& id, daeDocument* doc); + virtual daeString getName(); +}; + + +// This is a container class for storing a modifiable list of daeIDRefResolver objects. +class DLLSPEC daeIDRefResolverList { +public: + daeIDRefResolverList(); + ~daeIDRefResolverList(); + + void addResolver(daeIDRefResolver* resolver); + void removeResolver(daeIDRefResolver* resolver); + + daeElement* resolveElement(const std::string& id, daeDocument* doc); + +private: + // Disabled copy constructor/assignment operator + daeIDRefResolverList(const daeIDRefResolverList& resolverList) { }; + daeIDRefResolverList& operator=(const daeIDRefResolverList& resolverList) { return *this; }; + + daeTArray resolvers; +}; + + +#endif //__DAE_IDREF_H__ diff --git a/Engine/lib/collada/include/dae/daeIOPlugin.h b/Engine/lib/collada/include/dae/daeIOPlugin.h new file mode 100644 index 000000000..518a76fd0 --- /dev/null +++ b/Engine/lib/collada/include/dae/daeIOPlugin.h @@ -0,0 +1,133 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_IOPLUGIN__ +#define __DAE_IOPLUGIN__ + +#include +#include +#include +class daeDatabase; +class daeMetaElement; +class daeURI; +class daeDocument; + +/** +* The @c daeIOPlugin class provides the input/output plugin interface, which is +* the interface between the COLLADA runtime and the backend storage. A native +* COLLADA XML plugin implementation is provided along with this interface. +*/ +class DLLSPEC daeIOPlugin +{ +public: + /** + * Destructor + */ + virtual ~daeIOPlugin() {} + /** + * Sets the top meta object. + * Called by @c dae::setIOPlugin() when the IO plugin changes. It passes to this function the + * top meta object, which is the root of a + * hierarchy of @c daeMetaElement objects. This top meta object is capable of creating + * any of the root objects in the DOM tree. + * @param topMeta Top meta object to use to create objects to fill the database. + * @return Returns DAE_OK if successful, otherwise returns a negative value defined in daeError.h. + */ + virtual daeInt setMeta(daeMetaElement *topMeta) = 0; + + /** @name Database setup */ + //@{ + /** + * Sets the database to use. + * All @c daeIOPlugins use the same interface to the @c daeDatabase, + * @c setDatabase() tells the @c daeIOPlugin which @c daeDatabase object it should use + * for storage and queries. + * @param database Database to set. + */ + virtual void setDatabase(daeDatabase* database) = 0; + //@} + + + /** @name Operations */ + //@{ + /** + * Imports content into the database from an input. + * The input can be a file, a database or another runtime. + * @param uri the URI of the COLLADA document to load, not all plugins accept all types of URIs, + * check the documentation for the IO plugin you are using. + * @param docBuffer A string containing the text of the document to load. This is an optional attribute + * and should only be used if the document has already been loaded into memory. + * @return Returns DAE_OK if successfully loaded, otherwise returns a negative value defined in daeError.h. + * @see @c DAE::load(). + */ + virtual daeInt read(const daeURI& uri, daeString docBuffer) = 0; + + /** @name Operations */ + //@{ + /** + * Writes a specific document to an output. + * @param name URI to write the document to, not all IO plugins support all types of URIs + * check the documentation for the IO plugin you are using. + * @param document Pointer to the document that we're going to write out. + * @param replace True if write should overwrite an existing file. False otherwise. + * @return Returns DAE_OK if success, a negative value defined in daeError.h otherwise. + * @see @c DAE::saveAs() + */ + virtual daeInt write(const daeURI& name, daeDocument *document, daeBool replace) = 0; + //@} + + /** + * Returns a list of the URI protocols that this plugin supports. + * @return Returns a daeArray containing the supported protocols. + */ + virtual const std::vector& getSupportedProtocols() { + return supportedProtocols; + } + + /** + * setOption allows you to set options for this IOPlugin. Which options a plugin supports is + * dependent on the plugin itself. There is currently no list of options that plugins are + * suggested to implement. + * @param option The option to set. + * @param value The value to set the option. + * @return Returns DAE_OK upon success. + */ + virtual daeInt setOption( daeString option, daeString value ) = 0; + + /** + * getOption retrieves the value of an option from this IOPlugin. Which options a plugin supports is + * dependent on the plugin itself. + * @param option The option to get. + * @return Returns the string value of the option or NULL if option is not valid. + */ + virtual daeString getOption( daeString option ) = 0; + +protected: + // This is an array of the URI protocols supported by this plugin, e.g. "http", "file", + // etc. Each plugin should initialize this variable in the constructor. + std::vector supportedProtocols; +}; + + +class DLLSPEC daeIOEmpty : public daeIOPlugin { +public: + virtual daeInt setMeta(daeMetaElement *topMeta) { return DAE_ERROR; } + virtual void setDatabase(daeDatabase* database) { } + virtual daeInt read(const daeURI& uri, daeString docBuffer) { return DAE_ERROR; } + virtual daeInt write(const daeURI& name, daeDocument *document, daeBool replace) { return DAE_ERROR; } + virtual daeInt setOption( daeString option, daeString value ) { return DAE_ERROR; } + virtual daeString getOption( daeString option ) { return ""; } +}; + + +#endif // __DAE_IOPLUGIN__ diff --git a/Engine/lib/collada/include/dae/daeIOPluginCommon.h b/Engine/lib/collada/include/dae/daeIOPluginCommon.h new file mode 100644 index 000000000..83ca2b6aa --- /dev/null +++ b/Engine/lib/collada/include/dae/daeIOPluginCommon.h @@ -0,0 +1,68 @@ +/* + * Copyright 2007 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_IO_PLUGIN_COMMON__ +#define __DAE_IO_PLUGIN_COMMON__ + +#include +#include +#include +#include +#include + +class daeMetaElement; +class daeDocument; + +/** + * The @c daeIOPluginCommon class was created to serve as a base class for the common functionality + * between the daeLIBXMLPlugin and daeTinyXMLPlugin classes. + */ +class DLLSPEC daeIOPluginCommon : public daeIOPlugin { +public: + /** + * Constructor. + */ + daeIOPluginCommon(); + /** + * Destructor. + */ + virtual ~daeIOPluginCommon(); + + virtual daeInt setMeta(daeMetaElement *topMeta); + + // Database setup + virtual void setDatabase(daeDatabase* database); + + // Operations + virtual daeInt read(const daeURI& uri, daeString docBuffer); + +protected: + daeDatabase* database; + + // On failure, these functions return NULL + virtual daeElementRef readFromFile(const daeURI& uri) = 0; + virtual daeElementRef readFromMemory(daeString buffer, const daeURI& baseUri) = 0; + + // Reading support for subclasses + typedef std::pair attrPair; + daeElementRef beginReadElement(daeElement* parentElement, + daeString elementName, + const std::vector& attributes, + daeInt lineNumber); + bool readElementText(daeElement* element, daeString text, daeInt elementLineNumber); + +private: + daeMetaElement* topMeta; +}; + +#endif //__DAE_IO_PLUGIN_COMMON__ diff --git a/Engine/lib/collada/include/dae/daeMemorySystem.h b/Engine/lib/collada/include/dae/daeMemorySystem.h new file mode 100644 index 000000000..bba2dad6a --- /dev/null +++ b/Engine/lib/collada/include/dae/daeMemorySystem.h @@ -0,0 +1,62 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_MEMORY_SYSTEM_H__ +#define __DAE_MEMORY_SYSTEM_H__ + +#include + +/** + * The @c daeMemorySystem class is a simple wrapper for memory operations. + * Every allocation passes a string pool name such that + * in the future different pools can be used based on allocation type. + * Currently the system is just a pass-through to system @c malloc. + */ +class daeMemorySystem +{ +public: + /** + * Provides a wrapper malloc with pool field. + * @param pool String name of the pool to use for this allocation. + * @param n Number of bytes to allocate. + * @return Returns the memory allocated if successful, or NULL if not. + */ + static DLLSPEC daeRawRef alloc(daeString pool, size_t n); + + /** + * Provides a wrapper free with pool argument. + * @param pool Pool the memory should be freed from. + * @param mem Memory to be freed. + */ + static DLLSPEC void dealloc(daeString pool, daeRawRef mem); +}; + +// (steveT) These new/delete overrides aren't complete. What about new[] and delete[]? +// Standard new should throw a bad_alloc exception, and a nothrow new should be provided +// that returns null instead of throwing bad_alloc. Because of these problems, plus the +// fact that we currently don't benefit in any way from overriding new and delete, this +// code is currently disabled. + +#if 0 +#define DAE_ALLOC \ + /* Standard new/delete */ \ + inline void* operator new(size_t size) { return daeMemorySystem::alloc("meta", size); } \ + inline void operator delete(void* p) { daeMemorySystem::dealloc("meta", p); } \ + /* Placement new/delete */ \ + inline void* operator new(size_t, void* p) { return p; } \ + inline void operator delete(void*, void*) { } +#endif + +#define DAE_ALLOC + +#endif // __DAE_MEMORY_H__ diff --git a/Engine/lib/collada/include/dae/daeMetaAny.h b/Engine/lib/collada/include/dae/daeMetaAny.h new file mode 100644 index 000000000..92e8ba2d4 --- /dev/null +++ b/Engine/lib/collada/include/dae/daeMetaAny.h @@ -0,0 +1,44 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_META_ANY_H__ +#define __DAE_META_ANY_H__ + +#include + +/** + * The daeMetaAny class defines the behavior of an xs:any content model in the COLLADA Schema. + */ +class daeMetaAny : public daeMetaCMPolicy +{ +public: + /** + * Constructor. + * @param container The daeMetaElement that this policy object belongs to. + * @param parent The daeMetaCMPolicy parent of this policy object. + * @param odinal The ordinal value offset of this specific policy object. Used for maintaining the + * correct order of child elements. + * @param minO The minimum number of times this CMPolicy object must appear. This value comes from the COLLADA schema. + * @param maxO The maximum number of times this CMPolicy object may appear. This value comes from the COLLADA schema. + */ + daeMetaAny( daeMetaElement *container, daeMetaCMPolicy *parent = NULL, daeUInt ordinal = 0, daeInt minO = 1, daeInt maxO = 1 ); + ~daeMetaAny(); + + daeElement *placeElement( daeElement *parent, daeElement *child, daeUInt &ordinal, daeInt offset = 0, daeElement* before = NULL, daeElement *after = NULL ); + daeBool removeElement(daeElement* parent, daeElement* child); + daeMetaElement *findChild( daeString elementName ); + void getChildren( daeElement* parent, daeElementRefArray &array ); +}; + +#endif + diff --git a/Engine/lib/collada/include/dae/daeMetaAttribute.h b/Engine/lib/collada/include/dae/daeMetaAttribute.h new file mode 100644 index 000000000..6f9d5f851 --- /dev/null +++ b/Engine/lib/collada/include/dae/daeMetaAttribute.h @@ -0,0 +1,325 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_META_ATTRIBUTE_H__ +#define __DAE_META_ATTRIBUTE_H__ + +#include +#include +#include +#include +#include +#include +#include + +class daeElement; +class daeMetaElement; +class daeMetaAttribute; +class daeMetaElementAttribute; + +/** + * The @c daeMetaAttribute class describes one attribute in a C++ COLLADA dom element. + * + * In the case of the C++ object model a conceptual attribute can be + * either a dom attribute, a dom element, or a dom value. + * Essentially, the meta attribute describes fields on the C++ class. + * However these attributes are stored separately in the containing meta + * @c daeMetaElement. + * @c daeMetaAttributes always exist inside of @c daeMetaElements. + * Each @c daeMetaAttribute has certain semantic operations it is capable of + * including @c set(), @c get(), and @c print(). + * @c daeMetaAttributes use the @c daeAtomicType system as their underlying semantic + * implementation, but contain additional information about the packaging + * of the atomic types into the C++ dom classes such as offset, and + * array information. + */ +class DLLSPEC daeMetaAttribute : public daeRefCountedObj +{ +protected: + daeStringRef _name; + daeInt _offset; + daeAtomicType* _type; + daeMetaElement* _container; + std::string _defaultString; + daeMemoryRef _defaultValue; + daeBool _isRequired; + +public: + /** + * Constructor + */ + daeMetaAttribute(); + + /** + * Destructor + */ + virtual ~daeMetaAttribute(); +public: + /** + * Determines if the schema indicates that this is a required attribute. + * @return Returns true if this is a required attribute, false if not. + */ + daeBool getIsRequired() {return _isRequired; } + /** + * Sets the value that indicates that this attribute is required by the schema. If set, the attribute + * will always be exported by the API regardless of its value. + * @param isRequired Indicates if the schema says this attribute is required, true if it is, false if not. + */ + void setIsRequired(daeBool isRequired) {_isRequired = isRequired;} + /** + * Sets the byte offset (from @c this) where this attribute's storage is + * found in its container element class. + * @param offset Integer byte offset from @c this pointer. + */ + void setOffset(daeInt offset) { _offset = offset; } + + /** + * Gets the byte offset (from @ this) where this attribute's storage is + * found in its container element class. + * @return Returns the integer byte offset from @c this pointer for this attribute. + */ + daeInt getOffset() { return _offset; } + + /** + * Sets the name of the attribute. + * @param name @c daeString that is directly stored as a pointer + * without being copied. + */ + void setName(daeString name) { _name = name; } + + /** + * Gets the name of this attribute. + * @return Returnsthe name of this attribute. + */ + daeStringRef getName() { return _name; } + + /** + * Sets the type of the attribute. + * @param type @c daeAtomicType to use for interacting with this + * attribute in a containing @c daeElement. + */ + void setType(daeAtomicType* type) { _type = type; } + + /** + * Gets the @c daeAtomicType used by this attribute. + * @return Returns the @c daeAtomicType that this attribute uses for its + * implementation. + */ + daeAtomicType* getType() { return _type; } + + /** + * Sets the default for this attribute via a string. + * @param defaultVal @c daeString representing the default value. + */ + virtual void setDefaultString(daeString defaultVal); + + /** + * Sets the default for this attribute via a memory pointer. + * @param defaultVal @c daeMemoryRef representing the default value. + */ + virtual void setDefaultValue(daeMemoryRef defaultVal); + + /** + * Gets the default for this attribute as a string. + * @return Returns a @c daeString representing the default value. + */ + daeString getDefaultString(); + + /** + * Gets the default for this attribute as a memory value. + * @return Returns a @c daeMemoryRef representing the default value. + */ + daeMemoryRef getDefaultValue(); + + /** + * Sets the containing @c daeMetaElement for this attribute. + * @param container Element on which this @c daeMetaAttribute belongs. + */ + void setContainer(daeMetaElement* container) { _container = container; } + + /** + * Gets the containing @c daeMetaElement for this attribute. + * @return Returns the @c daeMetaElement to which this @c daeAttribute belongs. + */ + daeMetaElement* getContainer() { return _container; } + + /** + * Notifies an attribute when the containing document changes. + */ + virtual void setDocument(daeElement* e, daeDocument* doc); + + /** + * Converts an element's attribute value to a string. + */ + virtual void memoryToString(daeElement* e, std::ostringstream& buffer); + + /** + * Converts a string to a memory value in the specified element. + */ + virtual void stringToMemory(daeElement* e, daeString s); + + /** + * Gets the attribute's memory pointer from containing element e. + * @param e Containing element from which to get the value. + * @return Returns the memory pointer corresponding to this attribute out of parent element e. + */ + virtual daeMemoryRef get(daeElement* e); + + /** + * Gets if this attribute is an array attribute. + * @return Returns true if this attribute is an array type. + */ + virtual daeBool isArrayAttribute() { return false; } + +public: + /** + * Gets the number of bytes for this attribute. + * @return Returns the number of bytes in the C++ COLLADA dom element for this + * attribute. + */ + virtual daeInt getSize(); + + /** + * Gets the alignment in bytes on the class of this meta attribute type. + * @return Returns the alignment in bytes. + */ + virtual daeInt getAlignment(); + + /** + * Copies the value of this attribute from fromElement into toElement. + * @param toElement Pointer to a @c daeElement to copy this attribute to. + * @param fromElement Pointer to a @c daeElement to copy this attribute from. + */ + virtual void copy(daeElement* toElement, daeElement* fromElement); + + /** + * Copies the default value of this attribute to the element + * @param element Pointer to a @c daeElement to copy the default value to. + */ + virtual void copyDefault(daeElement* element); + + /** + * Compares the value of this attribute in the given elements. + * @param elt1 The first element whose attribute value should be compared. + * @param elt2 The second element whose attribute value should be compared. + * @return Returns a positive integer if value1 > value2, a negative integer if + * value1 < value2, and 0 if value1 == value2. + */ + virtual daeInt compare(daeElement* elt1, daeElement* elt2); + + /** + * Compares the value of this attribute from the given element to the default value + * of this attribute (if one exists). + * @param e The element whose value should be compared to the default value. + * @return Returns a positive integer if value > default, a negative integer if + * value < default, and 0 if value == default. + */ + virtual daeInt compareToDefault(daeElement* e); + +public: + // These methods are deprecated. + virtual daeChar* getWritableMemory(daeElement* e); // Use get instead. + virtual void set(daeElement* element, daeString s); // Use stringToMemory instead. +}; + + +/** + * The @c daeMetaArrayAttribute class is simple a wrapper that implements + * an array of atomic types rather than a singleton. + * The corresponding storage is an array + * and the corresponding operations are implemented on the array + * data structure rather than on inlined storage in elements. + */ +class DLLSPEC daeMetaArrayAttribute : public daeMetaAttribute +{ +public: + virtual ~daeMetaArrayAttribute(); + + /** + * Defines the override version of this method from @c daeMetaAttribute. + * @param toElement Pointer to a @c daeElement to copy this attribute to. + * @param fromElement Pointer to a @c daeElement to copy this attribute from. + */ + virtual void copy(daeElement* toElement, daeElement* fromElement); + + /** + * Copies the default value of this attribute to the element + * @param element Pointer to a @c daeElement to copy the default value to. + */ + virtual void copyDefault(daeElement* element); + + /** + * Compares the value of this attribute in the given elements. + * @param elt1 The first element whose attribute value should be compared. + * @param elt2 The second element whose attribute value should be compared. + * @return Returns a positive integer if value1 > value2, a negative integer if + * value1 < value2, and 0 if value1 == value2. + */ + virtual daeInt compare(daeElement* elt1, daeElement* elt2); + + /** + * Compares the value of this attribute from the given element to the default value + * of this attribute (if one exists). + * @param e The element whose value should be compared to the default value. + * @return Returns a positive integer if value > default, a negative integer if + * value < default, and 0 if value == default. + */ + virtual daeInt compareToDefault(daeElement* e); + + /** + * Converts an element's attribute value to a string. + */ + virtual void memoryToString(daeElement* e, std::ostringstream& buffer); + + /** + * Converts a string to a memory value in the specified element. + */ + virtual void stringToMemory(daeElement* e, daeString s); + + /** + * Sets the default for this attribute via a string. + * @param defaultVal @c daeString representing the default value. + */ + virtual void setDefaultString(daeString defaultVal); + + /** + * Sets the default for this attribute via a memory pointer. + * @param defaultVal @c daeMemoryRef representing the default value. + */ + virtual void setDefaultValue(daeMemoryRef defaultVal); + + /** + * Gets if this attribute is an array attribute. + * @return Returns true if this attribute is an array type. + */ + virtual daeBool isArrayAttribute() { return true; } + + /** + * Notifies an attribute when the containing document changes. + */ + virtual void setDocument(daeElement* e, daeDocument* doc); +}; + + +typedef daeSmartRef daeMetaAttributeRef; + +typedef daeTArray daeMetaAttributeRefArray; +typedef daeTArray daeMetaAttributePtrArray; + +#endif //__DAE_META_ATTRIBUTE_H__ + + + + + + diff --git a/Engine/lib/collada/include/dae/daeMetaCMPolicy.h b/Engine/lib/collada/include/dae/daeMetaCMPolicy.h new file mode 100644 index 000000000..9fb7e6de4 --- /dev/null +++ b/Engine/lib/collada/include/dae/daeMetaCMPolicy.h @@ -0,0 +1,120 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_META_CM_POLICY_H__ +#define __DAE_META_CM_POLICY_H__ + +#include +#include +//class daeElement; +class daeMetaElement; + +/** + * The daeMetaCMPolicy class is the base class for the content model policy classes which are used to + * describe the availability and ordering of an element's children. + */ +class daeMetaCMPolicy +{ +public: + /** + * Places an element into the parent element based on this content model policy object. + * @param parent The parent element for which the child element will be placed. + * @param child The new child element. + * @param ordinal A reference to a daeUInt which holds the ordinal return value for a placed child. Used + * to maintain proper ording of child elements. + * @param offset The offset to used when attempting to place this element. Affects comparison against + * minOccurs and maxOccurs. + * @param before The element that the child should appear before. Optional. + * @param after The element that the child should appear after. Optional. + * @return Returns The child element that was placed within this content model object or any of its + * children. NULL if placement failed. + */ + virtual daeElement *placeElement( daeElement *parent, daeElement *child, daeUInt &ordinal, daeInt offset = 0, daeElement* before = NULL, daeElement *after = NULL ) = 0; + /** + * Removes an element from the parent based on this content model object. + * @param parent The parent element for which child you want to remove. + * @param child The child that will be removed from the parent. + * @return Returns true if the child was successfully removed from this content model object or any of + * its children. False otherwise. + */ + virtual daeBool removeElement(daeElement* parent, daeElement* child ) = 0; + /** + * Gets the daeMetaElement of an acceptable child of this content model object. + * @param elementName The name of the element whos metaElement information you are interested in. + * @return Returns a pointer to a daeMetaElement class that describes the element interested in. + * Returns NULL if the element is not valid in this content model. + */ + virtual daeMetaElement *findChild( daeString elementName ) = 0; + /** + * Populates an array with the children of parent based on this content model object. + * @param parent The parent element whos children you want. + * @param array The array where you the children will be appended to. + */ + virtual void getChildren( daeElement* parent, daeElementRefArray &array ) = 0; + + /** + * Adds a child to this content model object. + * @param p The child content model policy object. + */ + void appendChild( daeMetaCMPolicy *p ) { _children.append( p ); } + + /** + * Gets the parent of this content model policy object. + * @return Returns a pointer to the parent node. + */ + daeMetaCMPolicy *getParent() { return _parent; } + + /** + * Sets the maximum ordinal value of this policy objects children. Used to keep proper ordering for + * cm objects that may appear multiple times. + * @param ord The maximum ordinal value for this content model object. + */ + void setMaxOrdinal( daeUInt ord ) { _maxOrdinal = ord; } + +protected: + /** + * Constructor. + * @param container The daeMetaElement that this policy object belongs to. + * @param parent The daeMetaCMPolicy parent of this policy object. + * @param odinal The ordinal value offset of this specific policy object. Used for maintaining the + * correct order of child elements. + * @param minO The minimum number of times this CMPolicy object must appear. This value comes from the COLLADA schema. + * @param maxO The maximum number of times this CMPolicy object may appear. This value comes from the COLLADA schema. + */ + daeMetaCMPolicy( daeMetaElement *container ,daeMetaCMPolicy *parent, daeUInt ordinal, + daeInt minO, daeInt maxO ) : _container( container ), _parent( parent ), _minOccurs( minO ), + _maxOccurs( maxO ), _maxOrdinal( 0 ), _ordinalOffset( ordinal ) {} + +public: + /** + * Destructor. + */ + virtual ~daeMetaCMPolicy(); + +protected: + daeMetaElement * _container; + daeMetaCMPolicy * _parent; + daeTArray _children; + + /** Minimum number of times this meta element can occur. */ + daeInt _minOccurs; + /** Maximum number of times this meta element can occur. -1 for unbounded */ + daeInt _maxOccurs; + + daeUInt _maxOrdinal; + daeUInt _ordinalOffset; + +}; + +#endif + diff --git a/Engine/lib/collada/include/dae/daeMetaChoice.h b/Engine/lib/collada/include/dae/daeMetaChoice.h new file mode 100644 index 000000000..60809bb56 --- /dev/null +++ b/Engine/lib/collada/include/dae/daeMetaChoice.h @@ -0,0 +1,48 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_META_CHOICE_H__ +#define __DAE_META_CHOICE_H__ + +#include + +/** + * The daeMetaChoice class defines the behavior of an xs:choice content model in the COLLADA Schema. + */ +class daeMetaChoice : public daeMetaCMPolicy +{ +public: + /** + * Constructor. + * @param container The daeMetaElement that this policy object belongs to. + * @param parent The daeMetaCMPolicy parent of this policy object. + * @param choiceNum An unsigned integer that represents which index in an element's CMData array coresponds to this choice's data. + * @param odinal The ordinal value offset of this specific policy object. Used for maintaining the + * correct order of child elements. + * @param minO The minimum number of times this CMPolicy object must appear. This value comes from the COLLADA schema. + * @param maxO The maximum number of times this CMPolicy object may appear. This value comes from the COLLADA schema. + */ + daeMetaChoice( daeMetaElement *container, daeMetaCMPolicy *parent = NULL, daeUInt choiceNum = 0, daeUInt ordinal = 0, daeInt minO = 1, daeInt maxO = 1 ); + ~daeMetaChoice(); + + daeElement *placeElement( daeElement *parent, daeElement *child, daeUInt &ordinal, daeInt offset = 0, daeElement* before = NULL, daeElement *after = NULL ); + daeBool removeElement(daeElement* parent, daeElement* child); + daeMetaElement *findChild( daeString elementName ); + void getChildren( daeElement* parent, daeElementRefArray &array ); + +private: + daeUInt _choiceNum; +}; + +#endif + diff --git a/Engine/lib/collada/include/dae/daeMetaElement.h b/Engine/lib/collada/include/dae/daeMetaElement.h new file mode 100644 index 000000000..4e6ef49a4 --- /dev/null +++ b/Engine/lib/collada/include/dae/daeMetaElement.h @@ -0,0 +1,368 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_META_ELEMENT_H__ +#define __DAE_META_ELEMENT_H__ + +#include +#include +#include +#include +#include + +class DAE; +class daeMetaCMPolicy; +class daeMetaElementArrayAttribute; + +typedef daeElementRef (*daeElementConstructFunctionPtr)(DAE& dae); + +/** + * Each instance of the @c daeMetaElement class describes a C++ COLLADA dom + * element type. + * @par + * The meta information in @c daeMetaElement is a combination of the information + * required to create and maintain C++ object instances and + * the information necessary to parse and construct a hierarchy of COLLADA + * elements. + * @par + * @c daeMetaElement objects also act as factories for C++ COLLADA dom classes where + * each @c daeElement is capable of creating an instance of the class it describes. + * Further, each @c daeMetaElement contains references to other @c daeMetaElements + * for potential XML children elements. This enables this system to easily + * create @c daeElements of the appropriate type while navigating through XML + * recursive parse. + * @par + * See @c daeElement for information about the functionality that every @c daeElement implements. + */ +class daeMetaElement : public daeRefCountedObj +{ +protected: + daeStringRef _name; + + daeElementConstructFunctionPtr _createFunc; + daeInt _elementSize; + + daeMetaAttributeRefArray _metaAttributes; + daeMetaAttributeRef _metaValue; + daeMetaElementArrayAttribute* _metaContents; + daeMetaArrayAttribute* _metaContentsOrder; + + daeMetaAttributeRef _metaID; + + daeBool _isTrackableForQueries; + daeBool _usesStringContents; + + daeBool _isTransparent; + daeBool _isAbstract; + daeBool _allowsAny; + daeBool _innerClass; + + daeMetaCMPolicy* _contentModel; + daeMetaArrayAttribute* _metaCMData; + daeUInt _numMetaChoices; + + DAE& dae; + +public: + /** + * Constructor + */ + DLLSPEC daeMetaElement(DAE& dae); + + /** + * Destructor + */ + DLLSPEC ~daeMetaElement(); + +public: // public accessors + + /** + * Gets the DAE object that owns this daeMetaElement. + * @return Returns the owning DAE. + */ + DAE* getDAE(); + + /** + * Determines if elements of this type is an inner class. + * @return Returns true if this element type is an inner class. + */ + daeBool getIsInnerClass() { return _innerClass; } + /** + * Sets if elements of this type are inner classes. + * @param abstract True if this type is an inner class. + */ + void setIsInnerClass( daeBool ic ) { _innerClass = ic; } + /** + * Determines if elements of this type can be placed in the object model. + * @return Returns true if this element type is abstract, false otherwise. + */ + daeBool getIsAbstract() { return _isAbstract; } + /** + * Determines if elements of this type should have an element tag printed when saving. + * @return Returns true if this element type should not have a tag, false otherwise. + */ + daeBool getIsTransparent() { return _isTransparent; } + /** + * Sets if elements of this type are abstract. + * @param abstract True if this type is abstract. + */ + void setIsAbstract( daeBool abstract ) { _isAbstract = abstract; } + /** + * Sets whether or not elements of this type should have an element tag printed when saving. + * @param transparent True if this type is transparent. + */ + void setIsTransparent( daeBool transparent ) { _isTransparent = transparent; } + + /** + * Determines if elements of this type should be tracked + * for daeDatabase queries. + * @return Returns true if this element type should be tracked + */ + daeBool getIsTrackableForQueries() { return _isTrackableForQueries; } + + /** + * Sets whether elements of this type should be tracked + * for @c daeDatabase queries. + * @param trackable Indicates whether this element should be tracked. + * A value of true indicates this element type should be tracked and be available for + * database queries. + */ + void setIsTrackableForQueries(daeBool trackable) { + _isTrackableForQueries = trackable; } + + /** + * Determines if elements of this type allow for any element as a child. + * @return Returns true if this element can have any child element, false otherwise. + */ + daeBool getAllowsAny() { return _allowsAny; } + /** + * Sets if elements of this type allow for any element as a child. + * @param allows True if this element allows for any child element, false otherwise. + */ + void setAllowsAny( daeBool allows ) { _allowsAny = allows; } + + /** + * Gets the @c daeMetaAttribute for the non-element contents of a @c daeElement. + * This corresponds to a @c daeMetaFloatAttribute, @c daeMetaFloatArrayAttribute, + * et cetera. + * @return Returns the @c daeMetaAttribute pointer for the non-element contents of + * this element type. + */ + daeMetaAttribute* getValueAttribute() { return _metaValue; } + + /** + * Gets the @c daeMetaAttribute for the ID attribute of a @c daeElement. + * @return Returns the ID @c daeMetaAttribute, or NULL if the element type + * does not have an ID attribute. + */ + daeMetaAttribute* getIDAttribute() { return _metaID; } + + /** + * Gets the name of this element type. + * @return Returns the name of this element type. + */ + daeStringRef getName() { return _name; } + + /** + * Sets the name of this element type. + * @param s String name to set. + */ + void setName(daeString s) { _name = s; } + + /** + * Gets the array of all known attributes on this element type. + * This includes all meta attributes except those describing child + * elements. It does include the value element. + * @return Returns the array of @c daeMetaAttributeRefs. + */ + daeMetaAttributeRefArray& getMetaAttributes() { + return _metaAttributes; } + + /** + * Gets the attribute which has a name as provided by the s parameter. + * @param s String containing the desired attribute's name. + * @return Returns the corresponding @c daeMetaAttribute, or NULL if none found. + */ + DLLSPEC daeMetaAttribute* getMetaAttribute(daeString s); + + /** + * Sets the size in bytes of each instance of this element type. + * Used for factory element creation. + * @param size Number of bytes for each C++ element instance. + */ + void setElementSize(daeInt size) {_elementSize = size;} + + /** + * Gets the size in bytes of each instance of this element type. + * Used for factory element creation. + * @return Returns the number of bytes for each C++ element instance. + */ + daeInt getElementSize() { return _elementSize;} + +public: + /** + * Registers with the reflective object system that the dom class described by this @c daeMetaElement + * contains a _contents array. This method is @em only for @c daeMetaElement contstuction, and + * should only be called by the system as it sets up the Reflective Object System. + * @param offset Byte offset for the contents field in the C++ element class. + */ + DLLSPEC void addContents(daeInt offset); + /** + * Registers with the reflective object system the array that stores the _contents ordering. This method is @em + * only for @c daeMetaElement contstuction, and should only be called by the system as it sets up the Reflective + * Object System. + * @param offset Byte offset for the contents order array in the C++ element class. + */ + DLLSPEC void addContentsOrder( daeInt offset ); + /** + * Registers with the reflective object system that the dom class described by this @c daeMetaElement + * contains at least one choice group in the content model for this element. This method is @em only + * for @c daeMetaElement contstuction, and should only be called by the system as it sets up the + * Reflective Object System. + * @param offset Byte offset for the contents field in the C++ element class. + * @param numChoices The number of choice content model blocks there are for this element type. + */ + DLLSPEC void addCMDataArray( daeInt offset, daeUInt numChoices ); + + /** + * Gets the attribute associated with the contents meta information. + * @see @c addContents() + * @return Returns the @c daeMetaElementArrayAttribute. + */ + daeMetaElementArrayAttribute* getContents() { return _metaContents; } + /** + * Gets the attribute associated with the CMData array meta information. + * @see @c addCMDataArray() + * @return Returns the @c daeMetaArrayAttribute for the CMData of an element. + */ + daeMetaArrayAttribute* getMetaCMData() { return _metaCMData; } + /** + * Gets the number of choice content model blocks there are for this element type. + * @return Returns the number of daeMetaChoice's there are in the content model. + */ + daeUInt getNumChoices() const { return _numMetaChoices; } + + /** + * Appends a @c daeMetaAttribute that represents a field corresponding to an + * XML attribute to the C++ version of this element type. + * @param attr Attribute to append to this element types list + * of potential attributes. + */ + DLLSPEC void appendAttribute(daeMetaAttribute* attr); + + /** + * Registers the function that can construct a C++ instance of this class. + * Necessary for the factory system such that C++ can still call @c new and the + * @c vptr will still be initialized even when constructed via the factory system. + * @param func Pointer to a function that does object construction. + */ + void registerClass(daeElementConstructFunctionPtr func) { + _createFunc = func; } + + /** + * Validates this class to be used by the runtime c++ object model + * including factory creation. + */ + DLLSPEC void validate(); + + /** + * Places a child element into the parent element where the + * calling object is the @c daeMetaElement for the parent element. + * @param parent Element to act as the container. + * @param child Child element to place in the parent. + * @return Returns true if the operation was successful, false otherwise. + */ + DLLSPEC daeBool place(daeElement *parent, daeElement *child, daeUInt *ordinal = NULL); + /** + * Places a child element into the parent element at a specific location + * where the calling object is the @c daeMetaElement for the parent element. + * @param index The location in the contents array to insert. + * @param parent Element to act as the container. + * @param child Child element to place in the parent. + * @return Returns true if the operation was successful, false otherwise. + * @note This should only be called on elements that have a _contents array. Elements without + * a _contents array will be placed normally. + */ + DLLSPEC daeBool placeAt( daeInt index, daeElement *parent, daeElement *child ); + /** + * Places a child element into the parent element at a specific location which is right + * before the marker element. + * @param marker The element location in the contents array to insert before. + * @param parent Element to act as the container. + * @param child Child element to place in the parent. + * @return Returns true if the operation was successful, false otherwise. + */ + DLLSPEC daeBool placeBefore( daeElement* marker, daeElement *parent, daeElement *child, daeUInt *ordinal = NULL ); + /** + * Places a child element into the parent element at a specific location which is right + * after the marker element. + * @param marker The element location in the contents array to insert after. + * @param parent Element to act as the container. + * @param child Child element to place in the parent. + * @return Returns true if the operation was successful, false otherwise. + */ + DLLSPEC daeBool placeAfter( daeElement* marker, daeElement *parent, daeElement *child, daeUInt *ordinal = NULL ); + + /** + * Removes a child element from its parent element. + * @param parent Element That is the parent. + * @param child Child element to remove. + * @return Returns true if the operation was successful, false otherwise. + */ + DLLSPEC daeBool remove( daeElement *parent, daeElement *child ); + /** + * Gets all of the children from an element of this type. + * @param parent The element that you want to get the children from. + * @param array The return value. An elementref array to append this element's children to. + */ + DLLSPEC void getChildren( daeElement* parent, daeElementRefArray &array ); + + /** + * Invokes the factory element creation routine set by @c registerConstructor() + * to return a C++ COLLADA Object Model instance of this element type. + * @return Returns a created @c daeElement of appropriate type via the + * object creation function and the daeElement::setup() function. + */ + DLLSPEC daeElementRef create(); + + /** + * Looks through the list of potential child elements + * for this element type finding the corresponding element type; if a corresponding element type + * is found, use that type as a factory and return an instance of that + * child type. Typically @c place() is called after @c create(childelementname) + * @param childElementTypeName Type name to create. + * @return Returns the created element if the type was found as a potential child element. + */ + DLLSPEC daeElementRef create(daeString childElementTypeName); + + /** + * Gets the root of the content model policy tree. + * @return Returns the root element of the tree of content model policy elements. + */ + daeMetaCMPolicy *getCMRoot() { return _contentModel; } + /** + * Sets the root of the content model policy tree. + * @param cm The root element of the tree of content model policy elements. + */ + DLLSPEC void setCMRoot( daeMetaCMPolicy *cm ); +}; + +typedef daeSmartRef daeMetaElementRef; +typedef daeTArray daeMetaElementRefArray; + +#endif //__DAE_META_ELEMENT_H__ + + + + + diff --git a/Engine/lib/collada/include/dae/daeMetaElementAttribute.h b/Engine/lib/collada/include/dae/daeMetaElementAttribute.h new file mode 100644 index 000000000..3ebdc7aec --- /dev/null +++ b/Engine/lib/collada/include/dae/daeMetaElementAttribute.h @@ -0,0 +1,189 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_META_ELEMENT_ATTRIBUTE_H__ +#define __DAE_META_ELEMENT_ATTRIBUTE_H__ + +#include +#include +#include + +class daeMetaElement; +class daeElement; +class daeDocument; + +/** +* The @c daeMetaElementAttribute class represents a content model object that is an element. +*/ +class daeMetaElementAttribute : public daeMetaAttribute, public daeMetaCMPolicy +{ +public: + /** The metaElement that describes the element type of this attribute */ + daeMetaElement* _elementType; + +public: + /** + * Constructor. + * @param container The daeMetaElement that this policy object belongs to. + * @param parent The daeMetaCMPolicy parent of this policy object. + * @param odinal The ordinal value offset of this specific policy object. Used for maintaining the + * correct order of child elements. + * @param minO The minimum number of times this CMPolicy object must appear. This value comes from the COLLADA schema. + * @param maxO The maximum number of times this CMPolicy object may appear. This value comes from the COLLADA schema. + */ + daeMetaElementAttribute( daeMetaElement *container, daeMetaCMPolicy *parent = NULL, daeUInt ordinal = 0, daeInt minO = 1, daeInt maxO = 1); + /** + * Destructor + */ + virtual ~daeMetaElementAttribute(); + +public: + + virtual daeElement *placeElement(daeElement* parent, daeElement* child, daeUInt &ordinal, daeInt offset = 0, daeElement* before = NULL, daeElement *after = NULL); + virtual daeBool removeElement(daeElement* parent, daeElement* child); + + daeMetaElement *findChild( daeString elementName ); + + virtual void getChildren( daeElement* parent, daeElementRefArray &array ); + +public: + /** + * Sets the element type for the element that this attribute points to. + * @param elementType @c daeMetaElement representing the type. + */ + void setElementType(daeMetaElement *elementType) { + _elementType = elementType; } + + /** + * Gets the element type for the element that this attribute points to. + * @return Returns the @c daeMetaElement representing the type. + */ + daeMetaElement* getElementType() { return _elementType; } + + /** + * Sets the database document associated with this element. + * @param parent The daeElement to set the document. + * @param c The @c daeDocument to associate with this element. + */ + virtual void setDocument(daeElement *parent, daeDocument* c ); + inline void setCollection(daeElement *parent, daeDocument* c ) { + setDocument( parent, c ); + } + + /** + * Gets the number of elements associated with this attribute in instance e. + * @param e Containing element to run the operation on. + * @return Returns the number of elements associated with this attribute + * in instance e. + */ + virtual daeInt getCount(daeElement* e); + + /** + * Gets an element from containing element e based on index. + * @param e Containing element from which to get the element. + * @param index Index of the element to retrieve if indeed + * there is an array of elements rather than a singleton. + * @return Returns the associated element out of parent element e, based on index, if necessary. + */ + virtual daeMemoryRef get(daeElement* e, daeInt index); + + /** + * Defines the override version of base method. + * @param element Element on which to set this attribute. + * @param s String containing the value to be converted via the + * atomic type system. + */ + virtual void set(daeElement* element, daeString s); + /** + * Defines the override version of base method. + * @param toElement Pointer to a @c daeElement to copy this attribute to. + * @param fromElement Pointer to a @c daeElement to copy this attribute from. + */ + virtual void copy(daeElement* toElement, daeElement* fromElement); + + /** + * Gets if this attribute is an array attribute. + * @return Returns true if this attribute is an array type. + */ + virtual daeBool isArrayAttribute() { return false; } +}; +typedef daeSmartRef daeMetaElementAttributeRef; +typedef daeTArray daeMetaElementAttributeArray; + + +/** + * The @c daeMetaElementArrayAttribute class is similar to daeMetaElementAttribute + * except that this meta attribute describes an array of elements rather than a singleton. + */ +class daeMetaElementArrayAttribute : public daeMetaElementAttribute +{ +public: + /** + * Constructor. + * @param container The daeMetaElement that this policy object belongs to. + * @param parent The daeMetaCMPolicy parent of this policy object. + * @param odinal The ordinal value offset of this specific policy object. Used for maintaining the + * correct order of child elements. + * @param minO The minimum number of times this CMPolicy object must appear. This value comes from the COLLADA schema. + * @param maxO The maximum number of times this CMPolicy object may appear. This value comes from the COLLADA schema. + */ + daeMetaElementArrayAttribute(daeMetaElement *container, daeMetaCMPolicy *parent = NULL, daeUInt ordinal = 0, daeInt minO = 1, daeInt maxO = 1); + ~daeMetaElementArrayAttribute(); +public: + virtual daeElement *placeElement(daeElement* parent, daeElement* child, daeUInt &ordinal, daeInt offset = 0, daeElement* before = NULL, daeElement *after = NULL); + virtual daeBool removeElement(daeElement* parent, daeElement* child); + + void getChildren( daeElement* parent, daeElementRefArray &array ); + + /** + * Sets the database document associated with this element. + * @param c The @c daeDocument to associate with this element. + */ + virtual void setDocument(daeElement *parent, daeDocument* c ); + inline void setCollection(daeElement *parent, daeDocument* c ) { + setDocument( parent, c ); + } + + /** + * Defines the override version of this method from @c daeMetaElement. + * @param e Containing element to run the operation on. + * @return Returns the number of particles associated with this attribute + * in instance e. + */ + virtual daeInt getCount(daeElement* e); + /** + * Defines the override version of this method from @c daeMetaElement. + * @param e Containing element from which to get the element. + * @param index Index of the particle to retrieve if indeed + * there is an array of elements rather than a singleton. + * @return Returns the associated particle out of parent element e, based on index, if necessary. + */ + virtual daeMemoryRef get(daeElement* e, daeInt index); + /** + * Defines the override version of this method from @c daeMetaElement. + * @param toElement Pointer to a @c daeElement to copy this attribute to. + * @param fromElement Pointer to a @c daeElement to copy this attribute from. + */ + virtual void copy(daeElement* toElement, daeElement* fromElement); + + /** + * Gets if this attribute is an array attribute. + * @return Returns true if this attribute is an array type. + */ + virtual daeBool isArrayAttribute() { return true; } +}; +typedef daeSmartRef daeMetaElementArrayAttributeRef; +typedef daeTArray daeMetaElementArrayAttributeArray; + +#endif + diff --git a/Engine/lib/collada/include/dae/daeMetaGroup.h b/Engine/lib/collada/include/dae/daeMetaGroup.h new file mode 100644 index 000000000..f03d12a66 --- /dev/null +++ b/Engine/lib/collada/include/dae/daeMetaGroup.h @@ -0,0 +1,55 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_META_GROUP_H__ +#define __DAE_META_GROUP_H__ + +#include + +class daeMetaElementAttribute; + +/** + * The daeMetaGroup class defines the behavior of an xs:group ref content model from the COLLADA Schema. + */ +class daeMetaGroup : public daeMetaCMPolicy +{ +public: + /** + * Constructor. + * @param econ The daeMetaElementAttribute that represents the group element in the parent. + * @param container The daeMetaElement that this policy object belongs to. + * @param parent The daeMetaCMPolicy parent of this policy object. + * @param odinal The ordinal value offset of this specific policy object. Used for maintaining the + * correct order of child elements. + * @param minO The minimum number of times this CMPolicy object must appear. This value comes from the COLLADA schema. + * @param maxO The maximum number of times this CMPolicy object may appear. This value comes from the COLLADA schema. + */ + daeMetaGroup( daeMetaElementAttribute *econ, daeMetaElement *container, daeMetaCMPolicy *parent = NULL, + daeUInt ordinal = 0, daeInt minO = 1, daeInt maxO = 1 ); + + /** + * Destructor. + */ + ~daeMetaGroup(); + + daeElement *placeElement( daeElement *parent, daeElement *child, daeUInt &ordinal, daeInt offset = 0, daeElement* before = NULL, daeElement *after = NULL ); + daeBool removeElement(daeElement* parent, daeElement* child); + daeMetaElement *findChild( daeString elementName ); + void getChildren( daeElement* parent, daeElementRefArray &array ); + +protected: + daeMetaElementAttribute *_elementContainer; +}; + +#endif + diff --git a/Engine/lib/collada/include/dae/daeMetaSequence.h b/Engine/lib/collada/include/dae/daeMetaSequence.h new file mode 100644 index 000000000..b2028b47a --- /dev/null +++ b/Engine/lib/collada/include/dae/daeMetaSequence.h @@ -0,0 +1,51 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_META_SEQUENCE_H__ +#define __DAE_META_SEQUENCE_H__ + +#include + +/** + * The daeMetaSequence class defines the behavior of an xs:sequence content model in the COLLADA Schema. + */ +class daeMetaSequence : public daeMetaCMPolicy +{ +public: + /** + * Constructor. + * @param container The daeMetaElement that this policy object belongs to. + * @param parent The daeMetaCMPolicy parent of this policy object. + * @param odinal The ordinal value offset of this specific policy object. Used for maintaining the + * correct order of child elements. + * @param minO The minimum number of times this CMPolicy object must appear. This value comes from the COLLADA schema. + * @param maxO The maximum number of times this CMPolicy object may appear. This value comes from the COLLADA schema. + */ + daeMetaSequence( daeMetaElement *container, daeMetaCMPolicy *parent = NULL, daeUInt ordinal = 0, daeInt minO = 1, daeInt maxO = 1 ); + + /** + * Destructor. + */ + ~daeMetaSequence(); + + daeElement *placeElement( daeElement *parent, daeElement *child, daeUInt &ordinal, daeInt offset = 0, daeElement* before = NULL, daeElement *after = NULL ); + daeBool removeElement(daeElement* parent, daeElement* child); + daeMetaElement *findChild( daeString elementName ); + void getChildren( daeElement* parent, daeElementRefArray &array ); + +}; + + +#endif + + diff --git a/Engine/lib/collada/include/dae/daePlatform.h b/Engine/lib/collada/include/dae/daePlatform.h new file mode 100644 index 000000000..cc2dd6a11 --- /dev/null +++ b/Engine/lib/collada/include/dae/daePlatform.h @@ -0,0 +1,39 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_PLATFORM_H__ +#define __DAE_PLATFORM_H__ + +#ifdef WIN32 +#include +#elif defined( __GCC__ ) +#include +#else +// Use some generic settings +#include + +#define PLATFORM_INT8 char +#define PLATFORM_INT16 short +#define PLATFORM_INT32 int +#define PLATFORM_INT64 long long +#define PLATFORM_UINT8 unsigned char +#define PLATFORM_UINT16 unsigned short +#define PLATFORM_UINT32 unsigned int +#define PLATFORM_UINT64 unsigned long long +#define PLATFORM_FLOAT32 float +#define PLATFORM_FLOAT64 double + +#define DLLSPEC +#endif + +#endif diff --git a/Engine/lib/collada/include/dae/daeRawResolver.h b/Engine/lib/collada/include/dae/daeRawResolver.h new file mode 100644 index 000000000..17dc47c66 --- /dev/null +++ b/Engine/lib/collada/include/dae/daeRawResolver.h @@ -0,0 +1,57 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_RAWRESOLVER_H__ +#define __DAE_RAWRESOLVER_H__ + +#include +#include +#include +class DAE; + +/** + * The @c daeRawResolver class derives from @c daeURIResolver and implements + * the .raw backend resolver for raw binary data. + */ +class DLLSPEC daeRawResolver : public daeURIResolver +{ +public: + /** + * Constructor. + */ + daeRawResolver(DAE& dae); + /** + * Destructor. + */ + ~daeRawResolver(); + +public: // Abstract Interface + virtual daeElement* resolveElement(const daeURI& uri); + virtual daeString getName(); +}; + +// A simple class to make speed up the process of resolving a .raw URI. +// The result of the resolve is cached for future use. +// This is meant for DOM internal use only. +class DLLSPEC daeRawRefCache { +public: + daeElement* lookup(const daeURI& uri); + void add(const daeURI& uri, daeElement* elt); + void remove(const daeURI& uri); + void clear(); + +private: + std::map lookupTable; +}; + +#endif diff --git a/Engine/lib/collada/include/dae/daeRefCountedObj.h b/Engine/lib/collada/include/dae/daeRefCountedObj.h new file mode 100644 index 000000000..9adaa6024 --- /dev/null +++ b/Engine/lib/collada/include/dae/daeRefCountedObj.h @@ -0,0 +1,46 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef daeRefCountedObj_h +#define daeRefCountedObj_h + +#include +#include + +class DLLSPEC daeRefCountedObj { +protected: + mutable daeInt _refCount; + +public: + daeRefCountedObj(); + virtual ~daeRefCountedObj(); + + /** + * Decrements the reference count and deletes the object if reference count is zero. + * @note Should not be used externally if daeSmartRefs are being used, they call it + * automatically. + */ + void release() const; + + /** + * Increments the reference count of this element. + * @note Should not be used externally if daeSmartRefs are being used, they call it + * automatically. + */ + void ref() const; +}; + +void DLLSPEC checkedRelease(const daeRefCountedObj* obj); +void DLLSPEC checkedRef(const daeRefCountedObj* obj); + +#endif diff --git a/Engine/lib/collada/include/dae/daeSIDResolver.h b/Engine/lib/collada/include/dae/daeSIDResolver.h new file mode 100644 index 000000000..ec2538a58 --- /dev/null +++ b/Engine/lib/collada/include/dae/daeSIDResolver.h @@ -0,0 +1,179 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_SIDRESOLVER_H__ +#define __DAE_SIDRESOLVER_H__ + +#include +#include +#include +#include + + +// This is an alternative to the daeSIDResolver class. It's recommended you use +// this class instead. Typical usage: get the element a sid ref points to. For +// example, if you want to find the element with sid 'sampler' using a +// daeElement pointer named 'effect', that would look like this: +// daeElement* elt = daeSidRef("sampler", effect).resolve().elt +struct DLLSPEC daeSidRef { + // A helper class for returning all the data retrieved when a sid is resolved. + struct DLLSPEC resolveData { + resolveData(); + resolveData(daeElement* elt, daeDoubleArray* array, daeDouble* scalar); + + daeElement* elt; + daeDoubleArray* array; + daeDouble* scalar; + }; + + daeSidRef(); + daeSidRef(const std::string& sidRef, daeElement* referenceElt, const std::string& profile = ""); + bool operator<(const daeSidRef& other) const; + + resolveData resolve(); + + std::string sidRef; + daeElement* refElt; + std::string profile; +}; + + +/** + * The daeSIDResolver class is designed to resolve sid references within a COLLADA document. + * The rules for sid resolution are set forth by the Addressing Syntax section in Chapter 3 of the + * COLLADA specification which can be found at https://www.khronos.org/collada . + * This resolver always attempts to resolve to the daeElement which is referenced. If the element contains + * a daeDoubleArray (domFloatArray) value, the resolver will set the pointer to that array. The + * resolver will also do this if the sid target points to a element which has a as + * a child. If the sid target specifies a value, i.e. blah.X or blah(6), the resolver will attempt to + * get a pointer to that specific value. The resolver only attempts to resolve to that level for values which + * are defined in the COMMON profile glossary of the COLLADA specification, or values reference with the (#) + * syntax. You can check the return value from getState() to see which level of resolution is possible. + */ +class DLLSPEC daeSIDResolver +{ +public: + /** + * An enum describing the status of the SID resolution process. + */ + enum ResolveState{ + /** No target specified */ + target_empty, + /** target specified but not resolved */ + target_loaded, + /** Resolution failed because target was not found */ + sid_failed_not_found, + /** Resolution successful to the Element level */ + sid_success_element, + /** Resolution successful to the Double Array level */ + sid_success_array, + /** Resolution successful to the Double level */ + sid_success_double + }; + + /** + * Constructor. + * @param container The element which contains the target that you want to resolve. + * @param target The target string which needs to be resolved. + * @param platform The platform name of the technique to use. A NULL value indicates the common platform. + */ + daeSIDResolver( daeElement *container, daeString target, daeString platform = NULL ); + + /** + * Gets the target string. + * @return Returns the target string of this SID resolver. + */ + daeString getTarget() const; + /** + * Sets the target string. + * @param t The new target string for this resolver. + */ + void setTarget( daeString t ); + + /** + * Gets the name of the profile to use when resolving. + * @return Returns the name of the profile or NULL for the common profile. + */ + daeString getProfile() const; + /** + * Sets the profile to use when resolving. + * @param p The profile name of the technique to use. A NULL value indicates the common profile. + */ + void setProfile( daeString p ); + + /** + * Gets a pointer to the @c daeElement that contains the target to resolve. + * @return Returns the pointer to the containing daeElmement. + */ + daeElement* getContainer() const; + /** + * Sets the pointer to the @c daeElement that contains the target to resolve. + * @param element Pointer to the containing @c daeElmement. + */ + void setContainer(daeElement* element); + + /** + * Gets the element that this SID resolves to. + * @return Returns the element that the URI resolves to. + */ + daeElement* getElement(); + + /** + * Gets the value array of the element that the SID resolves to. + * @return Returns a pointer to the value array that the SID resolves to + * @note The daeSIDResolver can only resolve to this level for daeDoubleArray values. + */ + daeDoubleArray *getDoubleArray(); + + /** + * Gets a pointer to the particle this target resolved to. + * @return Returns a pointer to a double value which is the fully resolved target. + * @note The daeSIDResolver can only resolve to this level for domDouble values and only if the + * final symbolic name is from the COMMON profile or a cardinal value is specified. + * @note The daeSIDResolver assumes the value is a 4x4 matrix if there are 2 cardinal values specified. + */ + daeDouble *getDouble(); + + // This method is deprecated. Don't use it. + ResolveState getState() const; + +private: + // This data is provided by the user + std::string target; + std::string profile; + daeElement* container; +}; + + +// A class to make sid ref lookups faster. Meant for DOM internal use only. +class DLLSPEC daeSidRefCache { +public: + daeSidRefCache(); + + daeSidRef::resolveData lookup(const daeSidRef& sidRef); + void add(const daeSidRef& sidRef, const daeSidRef::resolveData& data); + void clear(); + + // For debugging/testing + bool empty(); + int misses(); + int hits(); + +private: + std::map lookupTable; + int hitCount; + int missCount; +}; + +#endif + diff --git a/Engine/lib/collada/include/dae/daeSmartRef.h b/Engine/lib/collada/include/dae/daeSmartRef.h new file mode 100644 index 000000000..decf7baf2 --- /dev/null +++ b/Engine/lib/collada/include/dae/daeSmartRef.h @@ -0,0 +1,139 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_SMARTREF_H__ +#define __DAE_SMARTREF_H__ + +#include +#include + +/** + * The @c daeSmartRef template class automates reference counting for + * objects derived from @c daeRefCountedObj. + */ +template class daeSmartRef +{ +public: + /** + * Constructor + */ + inline daeSmartRef() : _ptr(NULL) { } + + /** + * Destructor + */ + inline ~daeSmartRef() { + checkedRelease(_ptr); + } + + /** + * Copy Constructor that will convert from one template to the other. + * @param smartRef a daeSmartRef to the object to copy from. + */ + template + inline daeSmartRef(const daeSmartRef& smartRef) : _ptr(smartRef.cast()) { + checkedRef(_ptr); + } + + /** + * Function that returns a pointer to object being reference counted. + * @return the object being reference counted. + */ + inline T* cast() const { return _ptr; } + + /** + * Copy Constructor. + * @param smartRef a daeSmartRef of the same template type to copy from + */ + inline daeSmartRef(const daeSmartRef& smartRef) : _ptr(smartRef._ptr) { + checkedRef(_ptr); + } + + /** + * Constructor + * @param ptr a pointer to an object of the same template type. + */ + inline daeSmartRef(T* ptr) : _ptr(ptr) { + checkedRef(_ptr); + } + + /** + * Overloaded assignment operator which will convert between template types. + * @param smartRef a daeSmartRef to the object to copy from. + * @return Returns a reference to this object. + */ + template + inline const daeSmartRef& operator=(const daeSmartRef& smartRef) { + T* ptr = smartRef.cast(); + checkedRef(ptr); + checkedRelease(_ptr); + _ptr = ptr; + return *this; } + + /** + * Overloaded assignment operator. + * @param other a daeSmartRef to the object to copy from. Must be of the same template type. + * @return Returns a reference to this object. + */ + inline const daeSmartRef& operator=(const daeSmartRef& other) { + T* ptr = other._ptr; + checkedRef(ptr); + checkedRelease(_ptr); + _ptr = ptr; + return *this; } + + /** + * Overloaded assignment operator. + * @param ptr a pointer to the object to copy from. Must be of the same template type. + * @return Returns a reference to this object. + */ + inline const daeSmartRef& operator=(T* ptr) { + checkedRef(ptr); + checkedRelease(_ptr); + _ptr = ptr; + return *this; } + + /** + * Overloaded member selection operator. + * @return a pointer of the template class to the object. + */ + inline T* operator->() const { + assert (_ptr != (T*)NULL); return _ptr; } + + /** + * Overloaded cast operator. + * @return a pointer of the template class to the object. + */ + inline operator T*() const { + return _ptr; } + + /** + * Static cast function. + * @param smartRef a smartRef to cast from + * @return a pointer to an object of this template class + */ + template + inline static T* staticCast(const daeSmartRef& smartRef) { + return static_cast(smartRef.cast()); } + +private: + /* The pointer to the element which is being reference counted */ + T* _ptr; +}; + +#endif // __DAE_SMARTREF_H__ + + + + + diff --git a/Engine/lib/collada/include/dae/daeStandardURIResolver.h b/Engine/lib/collada/include/dae/daeStandardURIResolver.h new file mode 100644 index 000000000..0c92bcc6e --- /dev/null +++ b/Engine/lib/collada/include/dae/daeStandardURIResolver.h @@ -0,0 +1,44 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_STANDARD_URI_RESOLVER__ +#define __DAE_STANDARD_URI_RESOVLER__ + +#include +#include "dae/daeURI.h" +class DAE; + +/** + * The @c daeStandardURIResolver class derives from @c daeURIResolver and implements + * the default XML backend resolver. + */ +class daeStandardURIResolver : public daeURIResolver +{ +public: + /** + * Constructor. + * @param database The @c daeDatabase used. + * @param plugin The @c daeIOPlugin used. + */ + DLLSPEC daeStandardURIResolver(DAE& dae); + /** + * Destructor. + */ + DLLSPEC ~daeStandardURIResolver(); + +public: // Abstract Interface + virtual DLLSPEC daeElement* resolveElement(const daeURI& uri); + virtual DLLSPEC daeString getName(); +}; + +#endif //__DAE_STANDARD_URI_RESOLVER__ diff --git a/Engine/lib/collada/include/dae/daeStringRef.h b/Engine/lib/collada/include/dae/daeStringRef.h new file mode 100644 index 000000000..aee01753d --- /dev/null +++ b/Engine/lib/collada/include/dae/daeStringRef.h @@ -0,0 +1,108 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_STRING_REF_H__ +#define __DAE_STRING_REF_H__ + +#include +#include + +/** + *Defines the @c daeStringRef class. + */ +class daeStringRef +{ +public: + /** + * Macro that defines new and delete overrides for this class + */ + DAE_ALLOC +private: + daeString _string; + static daeStringTable &_stringTable(); +public: + + /** + * Destructor + */ + inline ~daeStringRef() { _string = NULL; } + + /** + * Constructor + */ + inline daeStringRef() { _string = NULL; } + + /** + * Constructor that copies from another @c daeStringRef. + * @param other Reference to copy from. + */ + inline daeStringRef(const daeStringRef& other) { + _string = other._string; } + + /** + * Constructor that creates from a const char *. + * @param string External string to create from. + */ + DLLSPEC daeStringRef(daeString string); + + /** + * Assignment operator. + * @param other The daeStringRef to copy. + * @return A reference to this object. + */ + inline const daeStringRef& operator= (const daeStringRef& other) { + _string = other._string; + return *this; + } + + /** + * Sets a string from an external const char *. + * @param string The daeString to copy. + * @return A reference to this object. + */ + DLLSPEC const daeStringRef& set(daeString string); + + /** + * Assignment operator from an external const char *. + * @param string The daeString to copy. + * @return A reference to this object. + */ + DLLSPEC const daeStringRef& operator= (daeString string); + + /** + * Cast operator that returns a const char *. + */ + inline operator daeString() const { return _string; } + + /** + * Comparison operator, the comparison is done via pointers as both + * strings will have same pointer if they are the same address + * @param other The daeStringRef to compare + * @return True if strings are equal. False otherwise. + */ + inline bool operator==(const daeStringRef& other) const{ + //return (other._string == _string); } + return (!strcmp(other._string, _string)); } + +//Contributed by Nus - Wed, 08 Nov 2006 + /** + * Release string table... + */ + static void releaseStringTable(void); +//-------------------- +}; + +typedef daeTArray daeStringRefArray; +typedef daeTArray daeStringRefArrayArray; + +#endif //__DAE_STRING_REF_H__ diff --git a/Engine/lib/collada/include/dae/daeStringTable.h b/Engine/lib/collada/include/dae/daeStringTable.h new file mode 100644 index 000000000..b0d547d01 --- /dev/null +++ b/Engine/lib/collada/include/dae/daeStringTable.h @@ -0,0 +1,64 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_STRING_TABLE_H__ +#define __DAE_STRING_TABLE_H__ +#include +#include + +/** + * The @c daeStringTable is a simple string table class to hold a float list of strings + * without a lot of allocations. + */ +class daeStringTable +{ +public: // allocate/construct/destruct/deallocate + /** + * Macro that defines new and delete overrides for this class + */ + DAE_ALLOC + /** + * Constructor which specifies fixed buffer size. + * @param stringBufferSize The size of the buffer to create for string allocation. + */ + DLLSPEC daeStringTable(int stringBufferSize = 1024*1024); + + /** + * Destructor. + */ + ~daeStringTable() { clear(); } + +public: // INTERFACE + /** + * Allocates a string from the table. + * @param string const char * to copy into the table. + * @return Returns an allocated string. + */ + DLLSPEC daeString allocString(daeString string); + + /** + * Clears the storage. + */ + DLLSPEC void clear(); + +private: // MEMBERS + size_t _stringBufferSize; + size_t _stringBufferIndex; + daeStringArray _stringBuffersList; + + daeString allocateBuffer(); + + daeString _empty; +}; + +#endif //__DAE_STRING_TABLE_H__ diff --git a/Engine/lib/collada/include/dae/daeTinyXMLPlugin.h b/Engine/lib/collada/include/dae/daeTinyXMLPlugin.h new file mode 100644 index 000000000..06b55dbe7 --- /dev/null +++ b/Engine/lib/collada/include/dae/daeTinyXMLPlugin.h @@ -0,0 +1,75 @@ +/* + * Copyright 2007 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_TINYXMLPLUGIN__ +#define __DAE_TINYXMLPLUGIN__ + +#include +#include +#include +#include +#include + +class TiXmlDocument; +class TiXmlElement; + +class daeTinyXMLPlugin : public daeIOPluginCommon +{ +public: + // Constructor / destructor + /** + * Constructor. + */ + DLLSPEC daeTinyXMLPlugin(); + /** + * Destructor. + */ + virtual DLLSPEC ~daeTinyXMLPlugin(); + + // Operations + virtual DLLSPEC daeInt write(const daeURI& name, daeDocument *document, daeBool replace); + + /** + * setOption allows you to set options for this IOPlugin. Which options a plugin supports is + * dependent on the plugin itself. There is currently no list of options that plugins are + * suggested to implement. daeLibXML2Plugin supports only one option, "saveRawBinary". Set to + * "true" to save float_array data as a .raw binary file. The daeRawResolver will convert the + * data back into COLLADA domFloat_array elements upon load. + * @param option The option to set. + * @param value The value to set the option. + * @return Returns DAE_OK upon success. + */ + virtual DLLSPEC daeInt setOption( daeString option, daeString value ); + + /** + * getOption retrieves the value of an option from this IOPlugin. Which options a plugin supports is + * dependent on the plugin itself. + * @param option The option to get. + * @return Returns the string value of the option or NULL if option is not valid. + */ + virtual DLLSPEC daeString getOption( daeString option ); + +private: + TiXmlDocument* m_doc; + std::list m_elements; + + virtual daeElementRef readFromFile(const daeURI& uri); + virtual daeElementRef readFromMemory(daeString buffer, const daeURI& baseUri); + daeElementRef readElement(TiXmlElement* tinyXmlElement, daeElement* parentElement); + + void writeElement( daeElement* element ); + void writeAttribute( daeMetaAttribute* attr, daeElement* element ); + void writeValue( daeElement* element ); +}; + +#endif //__DAE_TINYXMLPLUGIN__ diff --git a/Engine/lib/collada/include/dae/daeTypes.h b/Engine/lib/collada/include/dae/daeTypes.h new file mode 100644 index 000000000..27e560cdd --- /dev/null +++ b/Engine/lib/collada/include/dae/daeTypes.h @@ -0,0 +1,55 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_TYPES_H__ +#define __DAE_TYPES_H__ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define daeOffsetOf(class, member) \ + ((size_t)&(((class*)0x0100)->member) - (size_t)0x0100) + +typedef PLATFORM_INT8 daeChar; +typedef PLATFORM_INT16 daeShort; +typedef PLATFORM_INT32 daeInt; +typedef PLATFORM_INT64 daeLong; +typedef PLATFORM_UINT8 daeUChar; +typedef PLATFORM_UINT16 daeUShort; +typedef PLATFORM_UINT32 daeUInt; +typedef PLATFORM_UINT64 daeULong; +typedef PLATFORM_FLOAT32 daeFloat; +typedef PLATFORM_FLOAT64 daeDouble; + +// base types + +typedef const char* daeString; +typedef bool daeBool; +typedef const void* daeConstRawRef; +typedef void* daeRawRef; +typedef daeInt daeEnum; +typedef daeChar* daeMemoryRef; + +typedef daeChar daeFixedName[512]; + +#include +#include + +#endif //__DAE_TYPES_H__ diff --git a/Engine/lib/collada/include/dae/daeURI.h b/Engine/lib/collada/include/dae/daeURI.h new file mode 100644 index 000000000..ffb93585d --- /dev/null +++ b/Engine/lib/collada/include/dae/daeURI.h @@ -0,0 +1,498 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_URI_H__ +#define __DAE_URI_H__ + +#include +#include +#include +#include +class DAE; + +/** + * The @c daeURI is a simple class designed to aid in the parsing and resolution + * of URI references inside COLLADA elements. + * A @c daeURI is created for every @c anyURL and @c IDREF in the COLLADA schema. + * For example, the element has the url= attribute of type @c anyURL, and the + * element has the target= attribute of type @c IDREF. + * The @c daeURI class contains a URI string; the @c set() method breaks the string into + * its components including scheme, authority, path (directory), and fragment. + * It also has the capability to attempt to resolve this reference + * into a @c daeElement, through the method @c resolveElement(). + * If a @c daeURI is stored within a @c daeElement, it fills + * its container field to point to the containing element. + * + * The main API on the @c daeURI, @c resolveElement(), uses a @c daeURIResolver + * to search for the @c daeElement inside a @c daeDatabase. + * + * URIs are resolved hierarchically, where each URI is resolved based on + * the following criteria via itself and its element's base URI, which represents the + * URI of the document that contains the element, retrieved by + * daeElement::getBaseURI(). + * If no base URI is provided, then the application URI + * is used as a base. + * + * The URI resolution order for the COLLADA DOM is as follows: + * - Absolute URI is specified (see definition below): + * The URI ignores its parent/base URI when validating. + * - Relative URI is specified: + * The URI uses the base URI to provide the scheme, authority, and base path. + * This URI's path is appended to the path given the the base URI. + * This URI's file and ID are used. + * - Each level of URI is resolved in this way against the base URI of the + * containing file until the top level is reached. Then the application URI + * is used as the default. + * + * Definition of Absolute URI: + * For the purposes of the COLLADA DOM, a URI is considered absolute + * if it starts by specifying a scheme. + * For example, + * - file:///c:/data/foo.dae#myScene is an absolute URI. + * - foo.dae#myScene is relative. + * - foo.dae is a top-level file reference and is relative. + * If the URI does not include a pound sign (#), the fragment is empty. + */ +class DLLSPEC daeURI +{ +private: + daeElement* internalResolveElement() const; + +public: + /** + * An enum describing the status of the URI resolution process. + * This is pretty much entirely useless now. Just use the various accessors + * to query the state of the uri. + */ + enum ResolveState{ + /** No URI specified */ + uri_empty, + /** URI specified but unresolved */ + uri_loaded, + /** Resolution pending */ + uri_pending, + /** Resolution successful */ + uri_success, + /** Failure due to unsupported URI scheme */ + uri_failed_unsupported_protocol, + /** Failure because the file was not found */ + uri_failed_file_not_found, + /** Failure because the fragment was not found */ + uri_failed_id_not_found, + /** Failure due to an invalid fragment */ + uri_failed_invalid_id, + /** A flag specifying that the URI should be resolved locally to its own document */ + uri_resolve_local, + /** A flag specifying that the URI should be resolved using this relative URI */ + uri_resolve_relative, + /** A flag specifying that the URI should be resolved using this absolute URI */ + uri_resolve_absolute, + /** Failure due to an invalid reference */ + uri_failed_invalid_reference, + /** Failure due to an external error */ + uri_failed_externalization, + /** Failure due to missing document */ + uri_failed_missing_container, + /** Failure because automatic loading of a document is turned off */ + uri_failed_external_document + }; + +private: + // All daeURIs have a pointer to a master DAE that they use to access global information. + mutable DAE* dae; + + /** Resolved version of the URI */ + std::string uriString; + + /** Original URI before resolution */ + std::string originalURIString; + + /** scheme component */ + std::string _scheme; + /** authority component */ + std::string _authority; + /** path component */ + std::string _path; + /** query component */ + std::string _query; + /** fragment component */ + std::string _fragment; + /** Pointer to the element that owns this URI */ + daeElement* container; + +public: + /** + * Constructs a daeURI object that contains no URI reference. + * @param dae The DAE associated with this daeURI. + * current working directory. + */ + daeURI(DAE& dae); + /** + * Destructor + */ + ~daeURI(); + + /** + * Constructs a daeURI object from a URI passed in as a string. + * @param dae The DAE associated with this daeURI. + * @param URIString Passed to set() automatically. + * @param nofrag If true, the fragment part of the URI is stripped off before construction. + */ + daeURI(DAE& dae, const std::string& URIString, daeBool nofrag = false); + + /** + * Constructs a daeURI object using a baseURI and a uriString. + * Calls set(URIString), and @c validate(baseURI). + * @param baseURI Base URI to resolve against. + * @param URIString String designating this URI. + */ + daeURI(const daeURI& baseURI, const std::string& URIString); + + /** + * Constructs a daeURI object based on a simple copy from an existing @c daeURI. + * @param constructFromURI URI to copy into this one. + */ + daeURI(const daeURI& constructFromURI); + + /** + * Constructs a daeURI given a container element and a URI string. + * @param container The container element. + * @param uriString the URI string. + */ + daeURI(daeElement& container, const std::string& uriString = ""); + + // This constructor is for internal DOM purposes only. For client code, use the constructor + // that takes only a daeElement instead of this one. + daeURI(DAE& dae, daeElement& container, const std::string& uriString = ""); + + /** + * Gets the DAE objects associated with this daeURI. + * @return Returns a pointer to the associated DAE. This will never return null. + */ + DAE* getDAE() const; + + // Returns the fully resolved URI as a string + const std::string& str() const; + // Returns the URI as originally set (i.e. not resolved against the base URI) + const std::string& originalStr() const; + + // Old C string versions of the previous functions + daeString getURI() const; // Alias for str() + daeString getOriginalURI() const; // Alias for originalStr(); + + // Setter function for setting the full uri. + void set(const std::string& uriStr, const daeURI* baseURI = NULL); + // Setter function for setting the individual uri components. + void set(const std::string& scheme, + const std::string& authority, + const std::string& path, + const std::string& query, + const std::string& fragment, + const daeURI* baseURI = NULL); + + // Old C string function. Alias for set(). + void setURI(daeString uriStr, const daeURI* baseURI = NULL); + + // std::string based component accessors. + const std::string& scheme() const; + const std::string& authority() const; + const std::string& path() const; + const std::string& query() const; + const std::string& fragment() const; + const std::string& id() const; // Alias for fragment() + + // Component setter functions. If you're going to be calling multiple setters, as in + // uri.path(path); + // uri.fragment(frag); + // it'd be more efficient to call uri.set once instead. + void scheme(const std::string& scheme); + void authority(const std::string& authority); + void path(const std::string& path); + void query(const std::string& query); + void fragment(const std::string& fragment); + void id(const std::string& id); // Alias for uri.fragment(frag) + + // Retrieves the individual path components. For example, in a uri of the form + // file:/folder/file.dae, dir = /folder/, baseName = file, ext = .dae + void pathComponents(std::string& dir, std::string& baseName, std::string& ext) const; + + // Individual path component accessors. If you need access to multiple path + // components, calling pathComponents() will be faster. + std::string pathDir() const; // daeURI("/folder/file.dae").pathDir() == "/folder/" + std::string pathFileBase() const; // daeURI("/folder/file.dae").pathFileBase() == "file" + std::string pathExt() const; // daeURI("/folder/file.dae").pathExt() == ".dae" + std::string pathFile() const; // daeURI("/folder/file.dae").pathFile() == "file.dae" + + // Path component setter. + void path(const std::string& dir, const std::string& baseName, const std::string& ext); + + // Individual path component setters. If you're going to be calling multiple setters, + // it'd be more efficient to call set() instead. + void pathDir(const std::string& dir); + void pathFileBase(const std::string& baseName); + void pathExt(const std::string& ext); + void pathFile(const std::string& file); + + // The older C string accessors. Aliases for the std::string based component accessors. + daeString getScheme() const; + daeString getProtocol() const; // Alias for getScheme() + daeString getAuthority() const; + daeString getPath() const; + daeString getQuery() const; + daeString getFragment() const; + daeString getID() const; // Alias for getFragment() + // Same as getPath(), but puts the result in the destination buffer. This is only here + // for backward compatibility. Use getPath() instead. + daeBool getPath(daeChar* dest, daeInt size) const; + + /** + * Gets the element that this URI resolves to in memory. + * @return Returns a ref to the element. + */ + daeElementRef getElement() const; + + // Returns the document that this URI references, or null if the document + // hasn't been loaded yet. + daeDocument* getReferencedDocument() const; + + /** + * Gets a pointer to the @c daeElement that contains this URI. + * @return Returns the pointer to the containing daeElmement. + */ + inline daeElement* getContainer() const {return(container);}; + + /** + * Sets the pointer to the @c daeElement that contains this URI. + * @param cont Pointer to the containing @c daeElmement. + */ + void setContainer(daeElement* container); + + /** + * Gets if this URI resolves to an element that is not contained in the same document as the URI. + * @return Returns true if the URI references an external element. False otherwise. + */ + daeBool isExternalReference() const; + + /** + * Copies the URI specified in from into @c this. + * Performs a simple copy without validating the URI. + * @param from URI to copy from. + */ + void copyFrom(const daeURI& from); + + /** + * Outputs all components of this URI to stderr. + * Useful for debugging URIs, this outputs each part of the URI separately. + */ + void print(); + + /** + * Makes the "originalURI" in this URI relative to some other uri + * @param uri the URI to make "this" relative to. + * @note this is experimental and not fully tested, please don't use in critical code yet. + */ + int makeRelativeTo(const daeURI* uri); + + /** + * Comparison operator. + * @return Returns true if URI's are equal. + */ + inline bool operator==(const daeURI& other) const { + return uriString == other.uriString; + } + + daeURI& operator=(const daeURI& other); + daeURI& operator=(const std::string& uri); + + // These methods are deprecated. + void resolveElement(); // Call getElement directly. + void validate(const daeURI* baseURI = NULL); // Shouldn't ever need to call this. + ResolveState getState() const; // Call getElement to see if resolving succeeded. + void setState(ResolveState newState); // Don't call this. + +private: + /** + * Resets this URI; frees all string references + * and returns state to @c empty. + */ + void reset(); + + /** + * Provides a shared initialization for all constructors + */ + void initialize(); +public: + /** + * Performs RFC2396 path normalization. + * @param path Path to be normalized. + */ + static void normalizeURIPath(char* path); +}; + +class daeURIResolver; +typedef daeTArray daeURIResolverPtrArray; + +/** + * The @c daeURIResolver class is the plugin point for URI resolution. + * This class is an abstract base class that defines an interface for + * resolving URIs. + * Every URI is passed through this list of @c daeURIResolvers for resolution. + * The list is ordered on a first come, first serve basis, and resolution + * terminates after any resolver instance resolves the URI. + */ +class DLLSPEC daeURIResolver +{ +public: + /** + * Constructor + * @param dae The associated dae object. + */ + daeURIResolver(DAE& dae); + + /** + * Destructor + */ + virtual ~daeURIResolver(); + + /** + * Sets a flag that tells the URI resolver whether or not to load a separate document if a URI + * being resolved points to one. + * @param load Set to true if you want the URI Resolver to automatically load other documents to + * resolve URIs. + */ + static void setAutoLoadExternalDocuments( daeBool load ); + + /** + * Gets a flag that tells if the URI resolver is set to load an external document if a URI + * being resolved points to one. + * @return Returns true if the resolver will automatically load documents to resolve a URI. + * False otherwise. + */ + static daeBool getAutoLoadExternalDocuments(); + + /** + * Provides an abstract interface for converting a @c daeURI into a @c daeElement + * @param uri @c daeURI to resolve. + * @return Returns the resolved element, or null if resolving failed. + * returns false otherwise. + */ + virtual daeElement* resolveElement(const daeURI& uri) = 0; + + /** + * Gets the name of this resolver. + * @return Returns the resolver name as a string. + */ + virtual daeString getName() = 0; + +protected: + static daeBool _loadExternalDocuments; + DAE* dae; +}; + + +// This is a container class for storing a modifiable list of daeURIResolver objects. +class DLLSPEC daeURIResolverList { +public: + daeURIResolverList(); + ~daeURIResolverList(); + + daeTArray& list(); + daeElement* resolveElement(const daeURI& uri); + +private: + // Disabled copy constructor/assignment operator + daeURIResolverList(const daeURIResolverList& resolverList) { }; + daeURIResolverList& operator=(const daeURIResolverList& resolverList) { return *this; }; + + daeTArray resolvers; +}; + + +// Helper functions for file path <--> URI conversion +namespace cdom { + // Takes a uri reference and parses it into its components. + DLLSPEC bool parseUriRef(const std::string& uriRef, + std::string& scheme, + std::string& authority, + std::string& path, + std::string& query, + std::string& fragment); + + // Takes the uri components of a uri ref and combines them. + // + // The 'forceLibxmlCompatible' param is meant to work around bugs in the file + // scheme uri handling of libxml. It causes the function to output a uri + // that's fully compatible with libxml. It only modifies file scheme uris, + // since uris with other schemes seem to work fine. + // + // The known libxml uri bugs are as follows: + // 1) libxml won't write files when given file scheme URIs with an empty + // authority, as in "file:/home". + // 2) libxml won't read or write Windows UNC paths represented with the + // machine name in the authority, as in "file://otherMachine/folder/file.dae" + // 3) On Windows, libxml won't read or write paths that don't have a drive + // letter, as in "/folder/file.dae". + DLLSPEC std::string assembleUri(const std::string& scheme, + const std::string& authority, + const std::string& path, + const std::string& query, + const std::string& fragment, + bool forceLibxmlCompatible = false); + + // A wrapper function for calling assembleUri to create a URI that's compatible + // with libxml. + DLLSPEC std::string fixUriForLibxml(const std::string& uriRef); + + // This function takes a file path in the OS's native format and converts it to + // a URI reference. If a relative path is given, a relative URI reference is + // returned. If an absolute path is given, a relative URI reference containing + // a fully specified path is returned. Spaces are encoded as %20. The 'type' + // parameter indicates the format of the nativePath. + // + // Examples - Windows + // nativePathToUri("C:\myFolder\myFile.dae") --> "/C:/myFolder/myFile.dae" + // nativePathToUri("\myFolder\myFile.dae") --> "/myFolder/myFile.dae" + // nativePathToUri("..\myFolder\myFile.dae") --> "../myFolder/myFile.dae" + // nativePathToUri("\\otherComputer\myFile.dae") --> "//otherComputer/myFile.dae" + // + // Examples - Linux/Mac + // nativePathToUri("/myFolder/myFile.dae") --> "/myFolder/myFile.dae" + // nativePathToUri("../myFolder/myFile.dae") --> "../myFolder/myFile.dae" + // nativePathToUri("/my folder/my file.dae") --> "/my%20folder/my%20file.dae" + DLLSPEC std::string nativePathToUri(const std::string& nativePath, + systemType type = getSystemType()); + + // This function takes a URI reference and converts it to an OS file path. Conversion + // can fail if the URI reference is ill-formed, or if the URI contains a scheme other + // than "file", in which case an empty string is returned. The 'type' parameter + // indicates the format of the returned native path. + // + // Examples - Windows + // uriToNativePath("../folder/file.dae") --> "..\folder\file.dae" + // uriToNativePath("/folder/file.dae") --> "\folder\file.dae" + // uriToNativePath("file:/C:/folder/file.dae") --> "C:\folder\file.dae" + // uriToNativePath("file://otherComputer/file.dae") --> "\\otherComputer\file.dae" + // uriToNativePath("http://www.slashdot.org") --> "" (it's not a file scheme URI!) + // + // Examples - Linux/Mac + // uriToNativePath("../folder/file.dae") --> "../folder/file.dae" + // uriToNativePath("file:/folder/file.dae") --> "/folder/file.dae" + // uriToNativePath("http://www.slashdot.org") --> "" (it's not a file scheme URI!) + DLLSPEC std::string uriToNativePath(const std::string& uriRef, + systemType type = getSystemType()); + + DLLSPEC std::string filePathToUri(const std::string& filePath); // Alias for nativePathToUri + DLLSPEC std::string uriToFilePath(const std::string& uriRef); // Alias for uriToNativePath +} + +#endif //__DAE_URI_H__ diff --git a/Engine/lib/collada/include/dae/daeUtils.h b/Engine/lib/collada/include/dae/daeUtils.h new file mode 100644 index 000000000..635f8e62a --- /dev/null +++ b/Engine/lib/collada/include/dae/daeUtils.h @@ -0,0 +1,67 @@ +// A home for commonly used utility functions. These are mostly for internal DOM +// use, but the automated tests use some of these functions, so we'll export +// them. +#ifndef daeUtils_h +#define daeUtils_h + +#include +#include +#include +#include +#include + +namespace cdom { + // System type info. We only need to distinguish between Posix and Winodws for now. + enum systemType { + Posix, + Windows + }; + + // Get the system type at runtime. + DLLSPEC systemType getSystemType(); + + // String replace function. Usage: replace("abcdef", "cd", "12") --> "ab12ef". + DLLSPEC std::string replace(const std::string& s, + const std::string& replace, + const std::string& replaceWith); + + // Usage: + // tokenize("this/is some#text", "/#", true) --> ("this" "/" "is some" "#" "text") + // tokenize("this is some text", " ", false) --> ("this" "is" "some" "text") + DLLSPEC std::list tokenize(const std::string& s, + const std::string& separators, + bool separatorsInResult = false); + // Same as the previous function, but returns the result via a parameter to avoid an object copy. + DLLSPEC void tokenize(const std::string& s, + const std::string& separators, + /* out */ std::list& tokens, + bool separatorsInResult = false); + + typedef std::list::iterator tokenIter; + + DLLSPEC std::vector makeStringArray(const char* s, ...); + DLLSPEC std::list makeStringList(const char* s, ...); + + DLLSPEC std::string getCurrentDir(); + DLLSPEC std::string getCurrentDirAsUri(); + + DLLSPEC int strcasecmp(const char* str1, const char* str2); + DLLSPEC std::string tolower(const std::string& s); + + // Disable VS warning +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4267) +#endif + template + std::string toString(const T& val) { + std::ostringstream stream; + stream << val; + return stream.str(); + } +#ifdef _MSC_VER +#pragma warning(pop) +#endif +} + +#endif diff --git a/Engine/lib/collada/include/dae/daeWin32Platform.h b/Engine/lib/collada/include/dae/daeWin32Platform.h new file mode 100644 index 000000000..6d6dc9245 --- /dev/null +++ b/Engine/lib/collada/include/dae/daeWin32Platform.h @@ -0,0 +1,58 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_WIN32_PLATFORM_H__ +#define __DAE_WIN32_PLATFORM_H__ + +#define PLATFORM_INT8 __int8 +#define PLATFORM_INT16 __int16 +#define PLATFORM_INT32 __int32 +#define PLATFORM_INT64 __int64 +#define PLATFORM_UINT8 unsigned __int8 +#define PLATFORM_UINT16 unsigned __int16 +#define PLATFORM_UINT32 unsigned __int32 +#define PLATFORM_UINT64 unsigned __int64 +#define PLATFORM_FLOAT32 float +#define PLATFORM_FLOAT64 double + +#if _MSC_VER <= 1200 +typedef int intptr_t; +#endif + +#ifdef DOM_DYNAMIC + +#ifdef DOM_EXPORT +#define DLLSPEC __declspec( dllexport ) +#else +#define DLLSPEC __declspec( dllimport ) +#endif + +#else +#define DLLSPEC +#endif + +// GCC doesn't understand "#pragma warning" +#ifdef _MSC_VER +// class 'std::auto_ptr<_Ty>' needs to have dll-interface to be used by clients of class 'daeErrorHandler' +#pragma warning(disable: 4251) +// warning C4100: 'profile' : unreferenced formal parameter +#pragma warning(disable: 4100) +// warning C4355: 'this' : used in base member initializer list +#pragma warning(disable: 4355) +// warning C4512: 'daeDatabase' : assignment operator could not be generated +#pragma warning(disable: 4512) +// warning LNK4099: Missing pdb file for PCRE +#pragma warning(disable: 4099) +#endif + +#endif diff --git a/Engine/lib/collada/include/dae/domAny.h b/Engine/lib/collada/include/dae/domAny.h new file mode 100644 index 000000000..fe734b66d --- /dev/null +++ b/Engine/lib/collada/include/dae/domAny.h @@ -0,0 +1,146 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ +#ifndef __domAny_h__ +#define __domAny_h__ + +#include +#include +#include +#include +#include +#include + +/** + * The domAny class allows for weakly typed xml elements. This class is used anywhere in the + * COLLADA schema where an xs:any element appears. The content and type information for a domAny + * object is generated at runtime. + */ +class domAny : public daeElement +{ + friend class domAnyAttribute; +protected: // Attribute + /** + * The array of daeStrings to hold attribute data for this element. + */ + daeTArray attrs; + /** + * The domString value of the text data of this element. + */ + daeString _value; + /** + * Used to preserve order in elements that do not specify strict sequencing of sub-elements. + */ + daeElementRefArray _contents; + /** + * Used to preserve order in elements that have a complex content model. + */ + daeUIntArray _contentsOrder; + +public: + /** + * Gets the _contents array. + * @return Returns a reference to the _contents element array. + */ + daeElementRefArray &getContents() { return _contents; } + /** + * Gets the _contents array. + * @return Returns a constant reference to the _contents element array. + */ + const daeElementRefArray &getContents() const { return _contents; } + + /** + * Gets the number of attributes this element has. + * @return Returns the number of attributes on this element. + */ + daeUInt getAttributeCount() const { return (daeUInt)_meta->getMetaAttributes().getCount(); } + /** + * Gets an attribute's name. + * @param index The index into the attribute list. + * @return Returns the attribute's name. + */ + daeString getAttributeName( daeUInt index ) const { return _meta->getMetaAttributes()[index]->getName(); } + /** + * Gets an attribute's value. + * @param index The index into the attribute list. + * @return Returns the attribute's value as a string. + */ + daeString getAttributeValue( daeUInt index ) const { return attrs[ index ]; } + /** + * Gets the value of this element. + * @return Returns a daeString of the value. + */ + daeString getValue() const { return _value; } + /** + * Sets the _value of this element. + * @param val The new value for this element. + */ + void setValue( daeString val ) { *(daeStringRef*)&_value = val; } + + /** + * Gets the element type. + * @return Returns the COLLADA_TYPE::TypeEnum value corresponding to this element's type. + */ + virtual COLLADA_TYPE::TypeEnum getElementType() const { return COLLADA_TYPE::ANY; } + + static daeInt ID() { return colladaTypeCount()-1; } + virtual daeInt typeID() const { return colladaTypeCount()-1; } + +protected: + /** + * Constructor + */ + domAny() : _value() {} + /** + * Destructor + */ + virtual ~domAny(); + /** + * Copy Constructor + */ + domAny( const domAny &cpy ) : daeElement() { (void)cpy; } + /** + * Overloaded assignment operator + */ + virtual domAny &operator=( const domAny &cpy ) { (void)cpy; return *this; } + +public: //METHODS + /** + * Override of the Base class method. Creates and registers an attribute field with its meta + * and assigns its value as the attrValue String. + * @param attrName Attribute to set. + * @param attrValue String-based value to apply to the attribute. + * @return Returns true if the attribute was created and the value was set, false otherwise. + */ + virtual DLLSPEC daeBool setAttribute(daeString attrName, daeString attrValue); + +public: // STATIC METHODS + /** + * Creates an instance of this class and returns a daeElementRef referencing it. + * @return a daeElementRef referencing an instance of this object. + */ + static DLLSPEC daeElementRef create(DAE& dae); + /** + * Creates a daeMetaElement object that describes this element in the meta object reflection framework. + * @return A daeMetaElement describing this COLLADA element. + * @remarks Unlike other dom* elements, domAny will always create a new daeMetaElement when this + * function is called. + */ + static DLLSPEC daeMetaElement* registerElement(DAE& dae); + +}; + +typedef daeSmartRef domAnyRef; +typedef daeTArray domAny_Array; + +#endif + diff --git a/Engine/lib/collada/include/dom.h b/Engine/lib/collada/include/dom.h new file mode 100644 index 000000000..a687c133b --- /dev/null +++ b/Engine/lib/collada/include/dom.h @@ -0,0 +1,29 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ +#ifndef __DOM__ +#define __DOM__ + +class DAE; +class daeMetaElement; + +extern daeString COLLADA_VERSION; +extern daeString COLLADA_NAMESPACE; + +// Register all types +void registerDomTypes(DAE& dae); + +// Register all elements +daeMetaElement* registerDomElements(DAE& dae); + + +#endif // __DOM_INTERFACE__ diff --git a/Engine/lib/collada/include/modules/daeLIBXMLPlugin.h b/Engine/lib/collada/include/modules/daeLIBXMLPlugin.h new file mode 100644 index 000000000..34a8a42a6 --- /dev/null +++ b/Engine/lib/collada/include/modules/daeLIBXMLPlugin.h @@ -0,0 +1,92 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_LIBXMLPLUGIN__ +#define __DAE_LIBXMLPLUGIN__ + +#include +#include +#include +#include + +struct _xmlTextReader; +struct _xmlTextWriter; +class DAE; + +/** + * The @c daeLIBXMLPlugin class derives from @c daeIOPluginCommon and implements an XML + * input/output backend using libxml2 as a parser. When using this plugin, DAE::load() expects + * an rfc 2396 compliant URI, any URI supported by libxml2 should be properly + * handled including ones with network schemes and authority. If the URI contains a fragment it will be ignored + * and the entire referenced document will be loaded. DAE::saveAs will only + * handle a filename path at present (ie: no scheme or authority). + */ +class DLLSPEC daeLIBXMLPlugin : public daeIOPluginCommon +{ +public: + // Constructor / destructor + /** + * Constructor. + */ + daeLIBXMLPlugin(DAE& dae); + /** + * Destructor. + */ + virtual ~daeLIBXMLPlugin(); + + // Operations + virtual daeInt write(const daeURI& name, daeDocument *document, daeBool replace); + + /** + * setOption allows you to set options for this IOPlugin. Which options a plugin supports is + * dependent on the plugin itself. There is currently no list of options that plugins are + * suggested to implement. daeLibXML2Plugin supports only one option, "saveRawBinary". Set to + * "true" to save float_array data as a .raw binary file. The daeRawResolver will convert the + * data back into COLLADA domFloat_array elements upon load. + * @param option The option to set. + * @param value The value to set the option. + * @return Returns DAE_OK upon success. + */ + virtual daeInt setOption( daeString option, daeString value ); + + /** + * getOption retrieves the value of an option from this IOPlugin. Which options a plugin supports is + * dependent on the plugin itself. + * @param option The option to get. + * @return Returns the string value of the option or NULL if option is not valid. + */ + virtual daeString getOption( daeString option ); + +private: + DAE& dae; + + _xmlTextWriter *writer; + + FILE *rawFile; + unsigned long rawByteCount; + daeURI rawRelPath; + bool saveRawFile; + + virtual daeElementRef readFromFile(const daeURI& uri); + virtual daeElementRef readFromMemory(daeString buffer, const daeURI& baseUri); + daeElementRef read(_xmlTextReader* reader); + daeElementRef readElement(_xmlTextReader* reader, daeElement* parentElement); + + void writeElement( daeElement* element ); + void writeAttribute( daeMetaAttribute* attr, daeElement* element); + void writeValue(daeElement* element); + + void writeRawSource( daeElement* src ); +}; + +#endif //__DAE_LIBXMLPLUGIN__ diff --git a/Engine/lib/collada/include/modules/daeSTLDatabase.h b/Engine/lib/collada/include/modules/daeSTLDatabase.h new file mode 100644 index 000000000..28e38c7fe --- /dev/null +++ b/Engine/lib/collada/include/modules/daeSTLDatabase.h @@ -0,0 +1,119 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef __DAE_STLDATABASE__ +#define __DAE_STLDATABASE__ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +/** + * The @c daeSTLDatabase class derives from @c daeDatabase and implements + * the default database. + */ +class DLLSPEC daeSTLDatabase : public daeDatabase +{ +public: + /** + * Constructor + */ + daeSTLDatabase(DAE& dae); + /** + * Destructor + */ + virtual ~daeSTLDatabase(); + +public: + // Element Types of all Elements + virtual daeUInt getTypeCount(); + virtual daeString getTypeName(daeUInt index); + virtual daeInt setMeta(daeMetaElement *_topMeta); + + // Documents + virtual daeInt insertDocument(daeString name, daeElement* dom, daeDocument** document = NULL); + virtual daeInt insertDocument(daeString name, daeDocument** document = NULL); + virtual daeInt createDocument(daeString name, daeElement* dom, daeDocument** document = NULL); + virtual daeInt createDocument(daeString name, daeDocument** document = NULL); + virtual daeInt insertDocument( daeDocument *c ); + + virtual daeInt removeDocument(daeDocument* document); + virtual daeUInt getDocumentCount(); + virtual daeDocument* getDocument(daeUInt index); + virtual daeDocument* getDocument(daeString name, bool skipUriNormalization = false); + virtual daeString getDocumentName(daeUInt index); + virtual daeBool isDocumentLoaded(daeString name); + + // Elements + virtual daeInt insertElement(daeDocument* document, daeElement* element); + virtual daeInt removeElement(daeDocument* document, daeElement* element); + virtual daeInt changeElementID(daeElement* element, daeString newID); + virtual daeInt changeElementSID(daeElement* element, daeString newSID); // Not implemented + virtual daeInt clear(); + + virtual std::vector idLookup(const std::string& id); + + virtual void typeLookup(daeInt typeID, + std::vector& matchingElements, + daeDocument* doc = NULL); + + // Currently not implemented, but you can uncomment some code in daeSTLDatabase.cpp to get + // it working. + virtual void sidLookup(const std::string& sid, + std::vector& matchingElements, + daeDocument* doc = NULL); + + // Deprecated. Don't use these. Use idLookup or typeLookup instead. + virtual daeUInt getElementCount(daeString name = NULL, + daeString type = NULL, + daeString file = NULL); + virtual daeInt getElement(daeElement** pElement, + daeInt index, + daeString name = NULL, + daeString type = NULL, + daeString file = NULL); + +private: + + std::map< std::string, std::vector< daeElement* > > elements; // type name --> element lookup table (deprecated) + + std::multimap typeMap; // type ID --> element lookup table + typedef std::multimap::iterator typeMapIter; + typedef std::pair typeMapPair; + typedef std::pair typeMapRange; + + std::multimap< std::string, daeElement* > elementsIDMap; //map for elements keyed on ID + typedef std::multimap::iterator idMapIter; + typedef std::pair idMapPair; + typedef std::pair idMapRange; + + std::multimap< std::string, daeElement* > sidMap; // sid --> element lookup table + typedef std::multimap::iterator sidMapIter; + typedef std::pair sidMapPair; + typedef std::pair sidMapRange; + + std::vector documents; + daeMetaElement* topMeta; + + daeInt insertChildren( daeDocument *c, daeElement *element ); + daeInt removeChildren( daeDocument *c, daeElement *element ); +}; + +#endif // __DAE_STLDATABASE__ diff --git a/Engine/lib/collada/include/modules/stdErrPlugin.h b/Engine/lib/collada/include/modules/stdErrPlugin.h new file mode 100644 index 000000000..a2dbec698 --- /dev/null +++ b/Engine/lib/collada/include/modules/stdErrPlugin.h @@ -0,0 +1,51 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#ifndef _STDERR_PLUGIN_ +#define _STDERR_PLUGIN_ + +#include +#include + +/** + * The @c stdErrPlugin class is the default implementation of daeErrorHandler. It routes the Error + * and Warning messaged to stdout. + */ +class DLLSPEC stdErrPlugin : public daeErrorHandler { +public: + stdErrPlugin(); + virtual ~stdErrPlugin(); + +public: + void handleError( daeString msg ); + void handleWarning( daeString msg ); +}; + +/** + * The @c quietErrorHandler class is an alternative implementation of daeErrorHandler. It suppresses + * error and warning messages. The easiest way to use it is like this: + * daeErrorHandler::setErrorHandler(&quietErrorHandler::getInstance()); + */ +class DLLSPEC quietErrorHandler : public daeErrorHandler { +public: + quietErrorHandler() { } + void handleError(daeString msg) { } + void handleWarning(daeString msg) { } + + static quietErrorHandler& getInstance() { return theInstance; } + +private: + static quietErrorHandler theInstance; +}; + +#endif diff --git a/Engine/lib/collada/src/1.4/dom/domAccessor.cpp b/Engine/lib/collada/src/1.4/dom/domAccessor.cpp new file mode 100644 index 000000000..5a9cabdc9 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domAccessor.cpp @@ -0,0 +1,108 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domAccessor::create(DAE& dae) +{ + domAccessorRef ref = new domAccessor(dae); + return ref; +} + + +daeMetaElement * +domAccessor::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "accessor" ); + meta->registerClass(domAccessor::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "param" ); + mea->setOffset( daeOffsetOf(domAccessor,elemParam_array) ); + mea->setElementType( domParam::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + + // Add attribute: count + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "count" ); + ma->setType( dae.getAtomicTypes().get("Uint")); + ma->setOffset( daeOffsetOf( domAccessor , attrCount )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: offset + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "offset" ); + ma->setType( dae.getAtomicTypes().get("Uint")); + ma->setOffset( daeOffsetOf( domAccessor , attrOffset )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + + meta->appendAttribute(ma); + } + + // Add attribute: source + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "source" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domAccessor , attrSource )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: stride + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "stride" ); + ma->setType( dae.getAtomicTypes().get("Uint")); + ma->setOffset( daeOffsetOf( domAccessor , attrStride )); + ma->setContainer( meta ); + ma->setDefaultString( "1"); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domAccessor)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domAnimation.cpp b/Engine/lib/collada/src/1.4/dom/domAnimation.cpp new file mode 100644 index 000000000..b71398694 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domAnimation.cpp @@ -0,0 +1,177 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domAnimation::create(DAE& dae) +{ + domAnimationRef ref = new domAnimation(dae); + return ref; +} + + +daeMetaElement * +domAnimation::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "animation" ); + meta->registerClass(domAnimation::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domAnimation,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 1, 1, 1 ); + + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, -1 ); + mea->setName( "source" ); + mea->setOffset( daeOffsetOf(domAnimation,elemSource_array) ); + mea->setElementType( domSource::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 1, 1, 1, 1 ); + + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, -1 ); + mea->setName( "sampler" ); + mea->setOffset( daeOffsetOf(domAnimation,elemSampler_array) ); + mea->setElementType( domSampler::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 1, -1 ); + mea->setName( "channel" ); + mea->setOffset( daeOffsetOf(domAnimation,elemChannel_array) ); + mea->setElementType( domChannel::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "animation" ); + mea->setOffset( daeOffsetOf(domAnimation,elemAnimation_array) ); + mea->setElementType( domAnimation::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3, 1, -1 ); + mea->setName( "animation" ); + mea->setOffset( daeOffsetOf(domAnimation,elemAnimation_array) ); + mea->setElementType( domAnimation::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm->setMaxOrdinal( 1 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm = new daeMetaSequence( meta, cm, 2, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, -1 ); + mea->setName( "sampler" ); + mea->setOffset( daeOffsetOf(domAnimation,elemSampler_array) ); + mea->setElementType( domSampler::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 1, -1 ); + mea->setName( "channel" ); + mea->setOffset( daeOffsetOf(domAnimation,elemChannel_array) ); + mea->setElementType( domChannel::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "animation" ); + mea->setOffset( daeOffsetOf(domAnimation,elemAnimation_array) ); + mea->setElementType( domAnimation::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementArrayAttribute( meta, cm, 5, 1, -1 ); + mea->setName( "animation" ); + mea->setOffset( daeOffsetOf(domAnimation,elemAnimation_array) ); + mea->setElementType( domAnimation::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 4 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domAnimation,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domAnimation,_contents)); + meta->addContentsOrder(daeOffsetOf(domAnimation,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domAnimation,_CMData), 2); + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domAnimation , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domAnimation , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domAnimation)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domAnimation_clip.cpp b/Engine/lib/collada/src/1.4/dom/domAnimation_clip.cpp new file mode 100644 index 000000000..430f2b68a --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domAnimation_clip.cpp @@ -0,0 +1,118 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domAnimation_clip::create(DAE& dae) +{ + domAnimation_clipRef ref = new domAnimation_clip(dae); + return ref; +} + + +daeMetaElement * +domAnimation_clip::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "animation_clip" ); + meta->registerClass(domAnimation_clip::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domAnimation_clip,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 1, -1 ); + mea->setName( "instance_animation" ); + mea->setOffset( daeOffsetOf(domAnimation_clip,elemInstance_animation_array) ); + mea->setElementType( domInstanceWithExtra::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domAnimation_clip,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domAnimation_clip , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domAnimation_clip , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: start + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "start" ); + ma->setType( dae.getAtomicTypes().get("xsDouble")); + ma->setOffset( daeOffsetOf( domAnimation_clip , attrStart )); + ma->setContainer( meta ); + ma->setDefaultString( "0.0"); + + meta->appendAttribute(ma); + } + + // Add attribute: end + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "end" ); + ma->setType( dae.getAtomicTypes().get("xsDouble")); + ma->setOffset( daeOffsetOf( domAnimation_clip , attrEnd )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domAnimation_clip)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domAsset.cpp b/Engine/lib/collada/src/1.4/dom/domAsset.cpp new file mode 100644 index 000000000..aa82c040c --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domAsset.cpp @@ -0,0 +1,655 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domAsset::create(DAE& dae) +{ + domAssetRef ref = new domAsset(dae); + return ref; +} + + +daeMetaElement * +domAsset::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "asset" ); + meta->registerClass(domAsset::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "contributor" ); + mea->setOffset( daeOffsetOf(domAsset,elemContributor_array) ); + mea->setElementType( domAsset::domContributor::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "created" ); + mea->setOffset( daeOffsetOf(domAsset,elemCreated) ); + mea->setElementType( domAsset::domCreated::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "keywords" ); + mea->setOffset( daeOffsetOf(domAsset,elemKeywords) ); + mea->setElementType( domAsset::domKeywords::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 1, 1 ); + mea->setName( "modified" ); + mea->setOffset( daeOffsetOf(domAsset,elemModified) ); + mea->setElementType( domAsset::domModified::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "revision" ); + mea->setOffset( daeOffsetOf(domAsset,elemRevision) ); + mea->setElementType( domAsset::domRevision::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 5, 0, 1 ); + mea->setName( "subject" ); + mea->setOffset( daeOffsetOf(domAsset,elemSubject) ); + mea->setElementType( domAsset::domSubject::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 6, 0, 1 ); + mea->setName( "title" ); + mea->setOffset( daeOffsetOf(domAsset,elemTitle) ); + mea->setElementType( domAsset::domTitle::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 7, 0, 1 ); + mea->setName( "unit" ); + mea->setOffset( daeOffsetOf(domAsset,elemUnit) ); + mea->setElementType( domAsset::domUnit::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 8, 0, 1 ); + mea->setName( "up_axis" ); + mea->setOffset( daeOffsetOf(domAsset,elemUp_axis) ); + mea->setElementType( domAsset::domUp_axis::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 8 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domAsset)); + meta->validate(); + + return meta; +} + +daeElementRef +domAsset::domContributor::create(DAE& dae) +{ + domAsset::domContributorRef ref = new domAsset::domContributor(dae); + return ref; +} + + +daeMetaElement * +domAsset::domContributor::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "contributor" ); + meta->registerClass(domAsset::domContributor::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "author" ); + mea->setOffset( daeOffsetOf(domAsset::domContributor,elemAuthor) ); + mea->setElementType( domAsset::domContributor::domAuthor::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "authoring_tool" ); + mea->setOffset( daeOffsetOf(domAsset::domContributor,elemAuthoring_tool) ); + mea->setElementType( domAsset::domContributor::domAuthoring_tool::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "comments" ); + mea->setOffset( daeOffsetOf(domAsset::domContributor,elemComments) ); + mea->setElementType( domAsset::domContributor::domComments::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "copyright" ); + mea->setOffset( daeOffsetOf(domAsset::domContributor,elemCopyright) ); + mea->setElementType( domAsset::domContributor::domCopyright::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "source_data" ); + mea->setOffset( daeOffsetOf(domAsset::domContributor,elemSource_data) ); + mea->setElementType( domAsset::domContributor::domSource_data::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 4 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domAsset::domContributor)); + meta->validate(); + + return meta; +} + +daeElementRef +domAsset::domContributor::domAuthor::create(DAE& dae) +{ + domAsset::domContributor::domAuthorRef ref = new domAsset::domContributor::domAuthor(dae); + return ref; +} + + +daeMetaElement * +domAsset::domContributor::domAuthor::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "author" ); + meta->registerClass(domAsset::domContributor::domAuthor::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsString")); + ma->setOffset( daeOffsetOf( domAsset::domContributor::domAuthor , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domAsset::domContributor::domAuthor)); + meta->validate(); + + return meta; +} + +daeElementRef +domAsset::domContributor::domAuthoring_tool::create(DAE& dae) +{ + domAsset::domContributor::domAuthoring_toolRef ref = new domAsset::domContributor::domAuthoring_tool(dae); + return ref; +} + + +daeMetaElement * +domAsset::domContributor::domAuthoring_tool::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "authoring_tool" ); + meta->registerClass(domAsset::domContributor::domAuthoring_tool::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsString")); + ma->setOffset( daeOffsetOf( domAsset::domContributor::domAuthoring_tool , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domAsset::domContributor::domAuthoring_tool)); + meta->validate(); + + return meta; +} + +daeElementRef +domAsset::domContributor::domComments::create(DAE& dae) +{ + domAsset::domContributor::domCommentsRef ref = new domAsset::domContributor::domComments(dae); + return ref; +} + + +daeMetaElement * +domAsset::domContributor::domComments::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "comments" ); + meta->registerClass(domAsset::domContributor::domComments::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsString")); + ma->setOffset( daeOffsetOf( domAsset::domContributor::domComments , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domAsset::domContributor::domComments)); + meta->validate(); + + return meta; +} + +daeElementRef +domAsset::domContributor::domCopyright::create(DAE& dae) +{ + domAsset::domContributor::domCopyrightRef ref = new domAsset::domContributor::domCopyright(dae); + return ref; +} + + +daeMetaElement * +domAsset::domContributor::domCopyright::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "copyright" ); + meta->registerClass(domAsset::domContributor::domCopyright::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsString")); + ma->setOffset( daeOffsetOf( domAsset::domContributor::domCopyright , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domAsset::domContributor::domCopyright)); + meta->validate(); + + return meta; +} + +daeElementRef +domAsset::domContributor::domSource_data::create(DAE& dae) +{ + domAsset::domContributor::domSource_dataRef ref = new domAsset::domContributor::domSource_data(dae); + return ref; +} + + +daeMetaElement * +domAsset::domContributor::domSource_data::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "source_data" ); + meta->registerClass(domAsset::domContributor::domSource_data::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domAsset::domContributor::domSource_data , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domAsset::domContributor::domSource_data)); + meta->validate(); + + return meta; +} + +daeElementRef +domAsset::domCreated::create(DAE& dae) +{ + domAsset::domCreatedRef ref = new domAsset::domCreated(dae); + return ref; +} + + +daeMetaElement * +domAsset::domCreated::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "created" ); + meta->registerClass(domAsset::domCreated::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsDateTime")); + ma->setOffset( daeOffsetOf( domAsset::domCreated , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domAsset::domCreated)); + meta->validate(); + + return meta; +} + +daeElementRef +domAsset::domKeywords::create(DAE& dae) +{ + domAsset::domKeywordsRef ref = new domAsset::domKeywords(dae); + return ref; +} + + +daeMetaElement * +domAsset::domKeywords::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "keywords" ); + meta->registerClass(domAsset::domKeywords::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsString")); + ma->setOffset( daeOffsetOf( domAsset::domKeywords , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domAsset::domKeywords)); + meta->validate(); + + return meta; +} + +daeElementRef +domAsset::domModified::create(DAE& dae) +{ + domAsset::domModifiedRef ref = new domAsset::domModified(dae); + return ref; +} + + +daeMetaElement * +domAsset::domModified::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "modified" ); + meta->registerClass(domAsset::domModified::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsDateTime")); + ma->setOffset( daeOffsetOf( domAsset::domModified , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domAsset::domModified)); + meta->validate(); + + return meta; +} + +daeElementRef +domAsset::domRevision::create(DAE& dae) +{ + domAsset::domRevisionRef ref = new domAsset::domRevision(dae); + return ref; +} + + +daeMetaElement * +domAsset::domRevision::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "revision" ); + meta->registerClass(domAsset::domRevision::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsString")); + ma->setOffset( daeOffsetOf( domAsset::domRevision , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domAsset::domRevision)); + meta->validate(); + + return meta; +} + +daeElementRef +domAsset::domSubject::create(DAE& dae) +{ + domAsset::domSubjectRef ref = new domAsset::domSubject(dae); + return ref; +} + + +daeMetaElement * +domAsset::domSubject::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "subject" ); + meta->registerClass(domAsset::domSubject::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsString")); + ma->setOffset( daeOffsetOf( domAsset::domSubject , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domAsset::domSubject)); + meta->validate(); + + return meta; +} + +daeElementRef +domAsset::domTitle::create(DAE& dae) +{ + domAsset::domTitleRef ref = new domAsset::domTitle(dae); + return ref; +} + + +daeMetaElement * +domAsset::domTitle::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "title" ); + meta->registerClass(domAsset::domTitle::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsString")); + ma->setOffset( daeOffsetOf( domAsset::domTitle , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domAsset::domTitle)); + meta->validate(); + + return meta; +} + +daeElementRef +domAsset::domUnit::create(DAE& dae) +{ + domAsset::domUnitRef ref = new domAsset::domUnit(dae); + return ref; +} + + +daeMetaElement * +domAsset::domUnit::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "unit" ); + meta->registerClass(domAsset::domUnit::create); + + meta->setIsInnerClass( true ); + + // Add attribute: meter + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "meter" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domAsset::domUnit , attrMeter )); + ma->setContainer( meta ); + ma->setDefaultString( "1.0"); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNMTOKEN")); + ma->setOffset( daeOffsetOf( domAsset::domUnit , attrName )); + ma->setContainer( meta ); + ma->setDefaultString( "meter"); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domAsset::domUnit)); + meta->validate(); + + return meta; +} + +daeElementRef +domAsset::domUp_axis::create(DAE& dae) +{ + domAsset::domUp_axisRef ref = new domAsset::domUp_axis(dae); + return ref; +} + + +daeMetaElement * +domAsset::domUp_axis::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "up_axis" ); + meta->registerClass(domAsset::domUp_axis::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("UpAxisType")); + ma->setOffset( daeOffsetOf( domAsset::domUp_axis , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domAsset::domUp_axis)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domBind_material.cpp b/Engine/lib/collada/src/1.4/dom/domBind_material.cpp new file mode 100644 index 000000000..09a172287 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domBind_material.cpp @@ -0,0 +1,118 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domBind_material::create(DAE& dae) +{ + domBind_materialRef ref = new domBind_material(dae); + return ref; +} + + +daeMetaElement * +domBind_material::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bind_material" ); + meta->registerClass(domBind_material::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "param" ); + mea->setOffset( daeOffsetOf(domBind_material,elemParam_array) ); + mea->setElementType( domParam::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "technique_common" ); + mea->setOffset( daeOffsetOf(domBind_material,elemTechnique_common) ); + mea->setElementType( domBind_material::domTechnique_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "technique" ); + mea->setOffset( daeOffsetOf(domBind_material,elemTechnique_array) ); + mea->setElementType( domTechnique::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domBind_material,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domBind_material)); + meta->validate(); + + return meta; +} + +daeElementRef +domBind_material::domTechnique_common::create(DAE& dae) +{ + domBind_material::domTechnique_commonRef ref = new domBind_material::domTechnique_common(dae); + return ref; +} + + +daeMetaElement * +domBind_material::domTechnique_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "technique_common" ); + meta->registerClass(domBind_material::domTechnique_common::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, -1 ); + mea->setName( "instance_material" ); + mea->setOffset( daeOffsetOf(domBind_material::domTechnique_common,elemInstance_material_array) ); + mea->setElementType( domInstance_material::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domBind_material::domTechnique_common)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domBool_array.cpp b/Engine/lib/collada/src/1.4/dom/domBool_array.cpp new file mode 100644 index 000000000..053f6107f --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domBool_array.cpp @@ -0,0 +1,92 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domBool_array::create(DAE& dae) +{ + domBool_arrayRef ref = new domBool_array(dae); + return ref; +} + + +daeMetaElement * +domBool_array::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool_array" ); + meta->registerClass(domBool_array::create); + + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("ListOfBools")); + ma->setOffset( daeOffsetOf( domBool_array , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domBool_array , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domBool_array , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: count + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "count" ); + ma->setType( dae.getAtomicTypes().get("Uint")); + ma->setOffset( daeOffsetOf( domBool_array , attrCount )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domBool_array)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domBox.cpp b/Engine/lib/collada/src/1.4/dom/domBox.cpp new file mode 100644 index 000000000..d258ad6e7 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domBox.cpp @@ -0,0 +1,103 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domBox::create(DAE& dae) +{ + domBoxRef ref = new domBox(dae); + return ref; +} + + +daeMetaElement * +domBox::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "box" ); + meta->registerClass(domBox::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "half_extents" ); + mea->setOffset( daeOffsetOf(domBox,elemHalf_extents) ); + mea->setElementType( domBox::domHalf_extents::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domBox,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domBox)); + meta->validate(); + + return meta; +} + +daeElementRef +domBox::domHalf_extents::create(DAE& dae) +{ + domBox::domHalf_extentsRef ref = new domBox::domHalf_extents(dae); + return ref; +} + + +daeMetaElement * +domBox::domHalf_extents::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "half_extents" ); + meta->registerClass(domBox::domHalf_extents::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float3")); + ma->setOffset( daeOffsetOf( domBox::domHalf_extents , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domBox::domHalf_extents)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domCOLLADA.cpp b/Engine/lib/collada/src/1.4/dom/domCOLLADA.cpp new file mode 100644 index 000000000..d498c0e15 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domCOLLADA.cpp @@ -0,0 +1,264 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern daeString COLLADA_VERSION; +extern daeString COLLADA_NAMESPACE; + +daeElementRef +domCOLLADA::create(DAE& dae) +{ + domCOLLADARef ref = new domCOLLADA(dae); + ref->_meta = dae.getMeta(domCOLLADA::ID()); + ref->setAttribute("version", COLLADA_VERSION ); + ref->setAttribute("xmlns", COLLADA_NAMESPACE ); + ref->_meta = NULL; + return ref; +} + + +daeMetaElement * +domCOLLADA::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "COLLADA" ); + meta->registerClass(domCOLLADA::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domCOLLADA,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 1, 0, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "library_animations" ); + mea->setOffset( daeOffsetOf(domCOLLADA,elemLibrary_animations_array) ); + mea->setElementType( domLibrary_animations::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "library_animation_clips" ); + mea->setOffset( daeOffsetOf(domCOLLADA,elemLibrary_animation_clips_array) ); + mea->setElementType( domLibrary_animation_clips::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "library_cameras" ); + mea->setOffset( daeOffsetOf(domCOLLADA,elemLibrary_cameras_array) ); + mea->setElementType( domLibrary_cameras::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "library_controllers" ); + mea->setOffset( daeOffsetOf(domCOLLADA,elemLibrary_controllers_array) ); + mea->setElementType( domLibrary_controllers::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "library_geometries" ); + mea->setOffset( daeOffsetOf(domCOLLADA,elemLibrary_geometries_array) ); + mea->setElementType( domLibrary_geometries::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "library_effects" ); + mea->setOffset( daeOffsetOf(domCOLLADA,elemLibrary_effects_array) ); + mea->setElementType( domLibrary_effects::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "library_force_fields" ); + mea->setOffset( daeOffsetOf(domCOLLADA,elemLibrary_force_fields_array) ); + mea->setElementType( domLibrary_force_fields::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "library_images" ); + mea->setOffset( daeOffsetOf(domCOLLADA,elemLibrary_images_array) ); + mea->setElementType( domLibrary_images::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "library_lights" ); + mea->setOffset( daeOffsetOf(domCOLLADA,elemLibrary_lights_array) ); + mea->setElementType( domLibrary_lights::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "library_materials" ); + mea->setOffset( daeOffsetOf(domCOLLADA,elemLibrary_materials_array) ); + mea->setElementType( domLibrary_materials::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "library_nodes" ); + mea->setOffset( daeOffsetOf(domCOLLADA,elemLibrary_nodes_array) ); + mea->setElementType( domLibrary_nodes::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "library_physics_materials" ); + mea->setOffset( daeOffsetOf(domCOLLADA,elemLibrary_physics_materials_array) ); + mea->setElementType( domLibrary_physics_materials::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "library_physics_models" ); + mea->setOffset( daeOffsetOf(domCOLLADA,elemLibrary_physics_models_array) ); + mea->setElementType( domLibrary_physics_models::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "library_physics_scenes" ); + mea->setOffset( daeOffsetOf(domCOLLADA,elemLibrary_physics_scenes_array) ); + mea->setElementType( domLibrary_physics_scenes::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "library_visual_scenes" ); + mea->setOffset( daeOffsetOf(domCOLLADA,elemLibrary_visual_scenes_array) ); + mea->setElementType( domLibrary_visual_scenes::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementAttribute( meta, cm, 3002, 0, 1 ); + mea->setName( "scene" ); + mea->setOffset( daeOffsetOf(domCOLLADA,elemScene) ); + mea->setElementType( domCOLLADA::domScene::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3003, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domCOLLADA,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3003 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domCOLLADA,_contents)); + meta->addContentsOrder(daeOffsetOf(domCOLLADA,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domCOLLADA,_CMData), 1); // Add attribute: xmlns + { + daeMetaAttribute* ma = new daeMetaAttribute; + ma->setName( "xmlns" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domCOLLADA , attrXmlns )); + ma->setContainer( meta ); + //ma->setIsRequired( true ); + meta->appendAttribute(ma); + } + + // Add attribute: version + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "version" ); + ma->setType( dae.getAtomicTypes().get("VersionType")); + ma->setOffset( daeOffsetOf( domCOLLADA , attrVersion )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: xml_base + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "xml_base" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domCOLLADA , attrXml_base )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCOLLADA)); + meta->validate(); + + return meta; +} + +daeElementRef +domCOLLADA::domScene::create(DAE& dae) +{ + domCOLLADA::domSceneRef ref = new domCOLLADA::domScene(dae); + return ref; +} + + +daeMetaElement * +domCOLLADA::domScene::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "scene" ); + meta->registerClass(domCOLLADA::domScene::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "instance_physics_scene" ); + mea->setOffset( daeOffsetOf(domCOLLADA::domScene,elemInstance_physics_scene_array) ); + mea->setElementType( domInstanceWithExtra::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "instance_visual_scene" ); + mea->setOffset( daeOffsetOf(domCOLLADA::domScene,elemInstance_visual_scene) ); + mea->setElementType( domInstanceWithExtra::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domCOLLADA::domScene,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domCOLLADA::domScene)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domCamera.cpp b/Engine/lib/collada/src/1.4/dom/domCamera.cpp new file mode 100644 index 000000000..c54888d6c --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domCamera.cpp @@ -0,0 +1,452 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domCamera::create(DAE& dae) +{ + domCameraRef ref = new domCamera(dae); + return ref; +} + + +daeMetaElement * +domCamera::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "camera" ); + meta->registerClass(domCamera::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domCamera,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "optics" ); + mea->setOffset( daeOffsetOf(domCamera,elemOptics) ); + mea->setElementType( domCamera::domOptics::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "imager" ); + mea->setOffset( daeOffsetOf(domCamera,elemImager) ); + mea->setElementType( domCamera::domImager::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domCamera,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3 ); + meta->setCMRoot( cm ); + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domCamera , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domCamera , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCamera)); + meta->validate(); + + return meta; +} + +daeElementRef +domCamera::domOptics::create(DAE& dae) +{ + domCamera::domOpticsRef ref = new domCamera::domOptics(dae); + return ref; +} + + +daeMetaElement * +domCamera::domOptics::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "optics" ); + meta->registerClass(domCamera::domOptics::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "technique_common" ); + mea->setOffset( daeOffsetOf(domCamera::domOptics,elemTechnique_common) ); + mea->setElementType( domCamera::domOptics::domTechnique_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 0, -1 ); + mea->setName( "technique" ); + mea->setOffset( daeOffsetOf(domCamera::domOptics,elemTechnique_array) ); + mea->setElementType( domTechnique::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domCamera::domOptics,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domCamera::domOptics)); + meta->validate(); + + return meta; +} + +daeElementRef +domCamera::domOptics::domTechnique_common::create(DAE& dae) +{ + domCamera::domOptics::domTechnique_commonRef ref = new domCamera::domOptics::domTechnique_common(dae); + return ref; +} + + +daeMetaElement * +domCamera::domOptics::domTechnique_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "technique_common" ); + meta->registerClass(domCamera::domOptics::domTechnique_common::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "orthographic" ); + mea->setOffset( daeOffsetOf(domCamera::domOptics::domTechnique_common,elemOrthographic) ); + mea->setElementType( domCamera::domOptics::domTechnique_common::domOrthographic::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "perspective" ); + mea->setOffset( daeOffsetOf(domCamera::domOptics::domTechnique_common,elemPerspective) ); + mea->setElementType( domCamera::domOptics::domTechnique_common::domPerspective::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domCamera::domOptics::domTechnique_common,_contents)); + meta->addContentsOrder(daeOffsetOf(domCamera::domOptics::domTechnique_common,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domCamera::domOptics::domTechnique_common,_CMData), 1); + meta->setElementSize(sizeof(domCamera::domOptics::domTechnique_common)); + meta->validate(); + + return meta; +} + +daeElementRef +domCamera::domOptics::domTechnique_common::domOrthographic::create(DAE& dae) +{ + domCamera::domOptics::domTechnique_common::domOrthographicRef ref = new domCamera::domOptics::domTechnique_common::domOrthographic(dae); + return ref; +} + + +daeMetaElement * +domCamera::domOptics::domTechnique_common::domOrthographic::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "orthographic" ); + meta->registerClass(domCamera::domOptics::domTechnique_common::domOrthographic::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + cm = new daeMetaChoice( meta, cm, 0, 0, 1, 1 ); + + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "xmag" ); + mea->setOffset( daeOffsetOf(domCamera::domOptics::domTechnique_common::domOrthographic,elemXmag) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 1, 1, 0, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "ymag" ); + mea->setOffset( daeOffsetOf(domCamera::domOptics::domTechnique_common::domOrthographic,elemYmag) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "aspect_ratio" ); + mea->setOffset( daeOffsetOf(domCamera::domOptics::domTechnique_common::domOrthographic,elemAspect_ratio) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm->setMaxOrdinal( 1 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm = new daeMetaSequence( meta, cm, 2, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "ymag" ); + mea->setOffset( daeOffsetOf(domCamera::domOptics::domTechnique_common::domOrthographic,elemYmag) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "aspect_ratio" ); + mea->setOffset( daeOffsetOf(domCamera::domOptics::domTechnique_common::domOrthographic,elemAspect_ratio) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm->setMaxOrdinal( 3 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "znear" ); + mea->setOffset( daeOffsetOf(domCamera::domOptics::domTechnique_common::domOrthographic,elemZnear) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 1, 1 ); + mea->setName( "zfar" ); + mea->setOffset( daeOffsetOf(domCamera::domOptics::domTechnique_common::domOrthographic,elemZfar) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domCamera::domOptics::domTechnique_common::domOrthographic,_contents)); + meta->addContentsOrder(daeOffsetOf(domCamera::domOptics::domTechnique_common::domOrthographic,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domCamera::domOptics::domTechnique_common::domOrthographic,_CMData), 2); + meta->setElementSize(sizeof(domCamera::domOptics::domTechnique_common::domOrthographic)); + meta->validate(); + + return meta; +} + +daeElementRef +domCamera::domOptics::domTechnique_common::domPerspective::create(DAE& dae) +{ + domCamera::domOptics::domTechnique_common::domPerspectiveRef ref = new domCamera::domOptics::domTechnique_common::domPerspective(dae); + return ref; +} + + +daeMetaElement * +domCamera::domOptics::domTechnique_common::domPerspective::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "perspective" ); + meta->registerClass(domCamera::domOptics::domTechnique_common::domPerspective::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + cm = new daeMetaChoice( meta, cm, 0, 0, 1, 1 ); + + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "xfov" ); + mea->setOffset( daeOffsetOf(domCamera::domOptics::domTechnique_common::domPerspective,elemXfov) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 1, 1, 0, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "yfov" ); + mea->setOffset( daeOffsetOf(domCamera::domOptics::domTechnique_common::domPerspective,elemYfov) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "aspect_ratio" ); + mea->setOffset( daeOffsetOf(domCamera::domOptics::domTechnique_common::domPerspective,elemAspect_ratio) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm->setMaxOrdinal( 1 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "yfov" ); + mea->setOffset( daeOffsetOf(domCamera::domOptics::domTechnique_common::domPerspective,elemYfov) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "aspect_ratio" ); + mea->setOffset( daeOffsetOf(domCamera::domOptics::domTechnique_common::domPerspective,elemAspect_ratio) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm->setMaxOrdinal( 3 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "znear" ); + mea->setOffset( daeOffsetOf(domCamera::domOptics::domTechnique_common::domPerspective,elemZnear) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 1, 1 ); + mea->setName( "zfar" ); + mea->setOffset( daeOffsetOf(domCamera::domOptics::domTechnique_common::domPerspective,elemZfar) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domCamera::domOptics::domTechnique_common::domPerspective,_contents)); + meta->addContentsOrder(daeOffsetOf(domCamera::domOptics::domTechnique_common::domPerspective,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domCamera::domOptics::domTechnique_common::domPerspective,_CMData), 2); + meta->setElementSize(sizeof(domCamera::domOptics::domTechnique_common::domPerspective)); + meta->validate(); + + return meta; +} + +daeElementRef +domCamera::domImager::create(DAE& dae) +{ + domCamera::domImagerRef ref = new domCamera::domImager(dae); + return ref; +} + + +daeMetaElement * +domCamera::domImager::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "imager" ); + meta->registerClass(domCamera::domImager::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, -1 ); + mea->setName( "technique" ); + mea->setOffset( daeOffsetOf(domCamera::domImager,elemTechnique_array) ); + mea->setElementType( domTechnique::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domCamera::domImager,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domCamera::domImager)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domCapsule.cpp b/Engine/lib/collada/src/1.4/dom/domCapsule.cpp new file mode 100644 index 000000000..c5523cfbd --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domCapsule.cpp @@ -0,0 +1,145 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domCapsule::create(DAE& dae) +{ + domCapsuleRef ref = new domCapsule(dae); + return ref; +} + + +daeMetaElement * +domCapsule::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "capsule" ); + meta->registerClass(domCapsule::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "height" ); + mea->setOffset( daeOffsetOf(domCapsule,elemHeight) ); + mea->setElementType( domCapsule::domHeight::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "radius" ); + mea->setOffset( daeOffsetOf(domCapsule,elemRadius) ); + mea->setElementType( domCapsule::domRadius::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domCapsule,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domCapsule)); + meta->validate(); + + return meta; +} + +daeElementRef +domCapsule::domHeight::create(DAE& dae) +{ + domCapsule::domHeightRef ref = new domCapsule::domHeight(dae); + return ref; +} + + +daeMetaElement * +domCapsule::domHeight::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "height" ); + meta->registerClass(domCapsule::domHeight::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domCapsule::domHeight , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCapsule::domHeight)); + meta->validate(); + + return meta; +} + +daeElementRef +domCapsule::domRadius::create(DAE& dae) +{ + domCapsule::domRadiusRef ref = new domCapsule::domRadius(dae); + return ref; +} + + +daeMetaElement * +domCapsule::domRadius::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "radius" ); + meta->registerClass(domCapsule::domRadius::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float2")); + ma->setOffset( daeOffsetOf( domCapsule::domRadius , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCapsule::domRadius)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domCg_connect_param.cpp b/Engine/lib/collada/src/1.4/dom/domCg_connect_param.cpp new file mode 100644 index 000000000..467a41a48 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domCg_connect_param.cpp @@ -0,0 +1,61 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domCg_connect_param::create(DAE& dae) +{ + domCg_connect_paramRef ref = new domCg_connect_param(dae); + return ref; +} + + +daeMetaElement * +domCg_connect_param::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "cg_connect_param" ); + meta->registerClass(domCg_connect_param::create); + + + // Add attribute: ref + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "ref" ); + ma->setType( dae.getAtomicTypes().get("Cg_identifier")); + ma->setOffset( daeOffsetOf( domCg_connect_param , attrRef )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_connect_param)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domCg_newarray_type.cpp b/Engine/lib/collada/src/1.4/dom/domCg_newarray_type.cpp new file mode 100644 index 000000000..b9cc878a1 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domCg_newarray_type.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domCg_newarray_type::create(DAE& dae) +{ + domCg_newarray_typeRef ref = new domCg_newarray_type(dae); + return ref; +} + + +daeMetaElement * +domCg_newarray_type::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "cg_newarray_type" ); + meta->registerClass(domCg_newarray_type::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 0, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "cg_param_type" ); + mea->setOffset( daeOffsetOf(domCg_newarray_type,elemCg_param_type_array) ); + mea->setElementType( domCg_param_type::registerElement(dae) ); + cm->appendChild( new daeMetaGroup( mea, meta, cm, 0, 1, 1 ) ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "array" ); + mea->setOffset( daeOffsetOf(domCg_newarray_type,elemArray_array) ); + mea->setElementType( domCg_newarray_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "usertype" ); + mea->setOffset( daeOffsetOf(domCg_newarray_type,elemUsertype_array) ); + mea->setElementType( domCg_setuser_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "connect_param" ); + mea->setOffset( daeOffsetOf(domCg_newarray_type,elemConnect_param_array) ); + mea->setElementType( domCg_connect_param::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3000 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domCg_newarray_type,_contents)); + meta->addContentsOrder(daeOffsetOf(domCg_newarray_type,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domCg_newarray_type,_CMData), 1); + // Add attribute: length + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "length" ); + ma->setType( dae.getAtomicTypes().get("xsPositiveInteger")); + ma->setOffset( daeOffsetOf( domCg_newarray_type , attrLength )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_newarray_type)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domCg_newparam.cpp b/Engine/lib/collada/src/1.4/dom/domCg_newparam.cpp new file mode 100644 index 000000000..f2412b7d9 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domCg_newparam.cpp @@ -0,0 +1,185 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domCg_newparam::create(DAE& dae) +{ + domCg_newparamRef ref = new domCg_newparam(dae); + return ref; +} + + +daeMetaElement * +domCg_newparam::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "cg_newparam" ); + meta->registerClass(domCg_newparam::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "annotate" ); + mea->setOffset( daeOffsetOf(domCg_newparam,elemAnnotate_array) ); + mea->setElementType( domFx_annotate_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "semantic" ); + mea->setOffset( daeOffsetOf(domCg_newparam,elemSemantic) ); + mea->setElementType( domCg_newparam::domSemantic::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "modifier" ); + mea->setOffset( daeOffsetOf(domCg_newparam,elemModifier) ); + mea->setElementType( domCg_newparam::domModifier::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 3, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "cg_param_type" ); + mea->setOffset( daeOffsetOf(domCg_newparam,elemCg_param_type) ); + mea->setElementType( domCg_param_type::registerElement(dae) ); + cm->appendChild( new daeMetaGroup( mea, meta, cm, 0, 1, 1 ) ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "usertype" ); + mea->setOffset( daeOffsetOf(domCg_newparam,elemUsertype) ); + mea->setElementType( domCg_setuser_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "array" ); + mea->setOffset( daeOffsetOf(domCg_newparam,elemArray) ); + mea->setElementType( domCg_newarray_type::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm->setMaxOrdinal( 3 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domCg_newparam,_contents)); + meta->addContentsOrder(daeOffsetOf(domCg_newparam,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domCg_newparam,_CMData), 1); + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("Cg_identifier")); + ma->setOffset( daeOffsetOf( domCg_newparam , attrSid )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_newparam)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_newparam::domSemantic::create(DAE& dae) +{ + domCg_newparam::domSemanticRef ref = new domCg_newparam::domSemantic(dae); + return ref; +} + + +daeMetaElement * +domCg_newparam::domSemantic::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "semantic" ); + meta->registerClass(domCg_newparam::domSemantic::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domCg_newparam::domSemantic , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_newparam::domSemantic)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_newparam::domModifier::create(DAE& dae) +{ + domCg_newparam::domModifierRef ref = new domCg_newparam::domModifier(dae); + return ref; +} + + +daeMetaElement * +domCg_newparam::domModifier::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "modifier" ); + meta->registerClass(domCg_newparam::domModifier::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_modifier_enum_common")); + ma->setOffset( daeOffsetOf( domCg_newparam::domModifier , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_newparam::domModifier)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domCg_param_type.cpp b/Engine/lib/collada/src/1.4/dom/domCg_param_type.cpp new file mode 100644 index 000000000..eb9b5a308 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domCg_param_type.cpp @@ -0,0 +1,4596 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domCg_param_type::create(DAE& dae) +{ + domCg_param_typeRef ref = new domCg_param_type(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "cg_param_type" ); + meta->registerClass(domCg_param_type::create); + + meta->setIsTransparent( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemBool) ); + mea->setElementType( domCg_param_type::domBool::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool1" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemBool1) ); + mea->setElementType( domCg_param_type::domBool1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool2" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemBool2) ); + mea->setElementType( domCg_param_type::domBool2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool3" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemBool3) ); + mea->setElementType( domCg_param_type::domBool3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool4" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemBool4) ); + mea->setElementType( domCg_param_type::domBool4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool1x1" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemBool1x1) ); + mea->setElementType( domCg_param_type::domBool1x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool1x2" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemBool1x2) ); + mea->setElementType( domCg_param_type::domBool1x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool1x3" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemBool1x3) ); + mea->setElementType( domCg_param_type::domBool1x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool1x4" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemBool1x4) ); + mea->setElementType( domCg_param_type::domBool1x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool2x1" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemBool2x1) ); + mea->setElementType( domCg_param_type::domBool2x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool2x2" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemBool2x2) ); + mea->setElementType( domCg_param_type::domBool2x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool2x3" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemBool2x3) ); + mea->setElementType( domCg_param_type::domBool2x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool2x4" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemBool2x4) ); + mea->setElementType( domCg_param_type::domBool2x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool3x1" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemBool3x1) ); + mea->setElementType( domCg_param_type::domBool3x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool3x2" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemBool3x2) ); + mea->setElementType( domCg_param_type::domBool3x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool3x3" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemBool3x3) ); + mea->setElementType( domCg_param_type::domBool3x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool3x4" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemBool3x4) ); + mea->setElementType( domCg_param_type::domBool3x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool4x1" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemBool4x1) ); + mea->setElementType( domCg_param_type::domBool4x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool4x2" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemBool4x2) ); + mea->setElementType( domCg_param_type::domBool4x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool4x3" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemBool4x3) ); + mea->setElementType( domCg_param_type::domBool4x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool4x4" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemBool4x4) ); + mea->setElementType( domCg_param_type::domBool4x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFloat) ); + mea->setElementType( domCg_param_type::domFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float1" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFloat1) ); + mea->setElementType( domCg_param_type::domFloat1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float2" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFloat2) ); + mea->setElementType( domCg_param_type::domFloat2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float3" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFloat3) ); + mea->setElementType( domCg_param_type::domFloat3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float4" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFloat4) ); + mea->setElementType( domCg_param_type::domFloat4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float1x1" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFloat1x1) ); + mea->setElementType( domCg_param_type::domFloat1x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float1x2" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFloat1x2) ); + mea->setElementType( domCg_param_type::domFloat1x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float1x3" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFloat1x3) ); + mea->setElementType( domCg_param_type::domFloat1x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float1x4" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFloat1x4) ); + mea->setElementType( domCg_param_type::domFloat1x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float2x1" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFloat2x1) ); + mea->setElementType( domCg_param_type::domFloat2x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float2x2" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFloat2x2) ); + mea->setElementType( domCg_param_type::domFloat2x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float2x3" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFloat2x3) ); + mea->setElementType( domCg_param_type::domFloat2x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float2x4" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFloat2x4) ); + mea->setElementType( domCg_param_type::domFloat2x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float3x1" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFloat3x1) ); + mea->setElementType( domCg_param_type::domFloat3x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float3x2" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFloat3x2) ); + mea->setElementType( domCg_param_type::domFloat3x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float3x3" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFloat3x3) ); + mea->setElementType( domCg_param_type::domFloat3x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float3x4" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFloat3x4) ); + mea->setElementType( domCg_param_type::domFloat3x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float4x1" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFloat4x1) ); + mea->setElementType( domCg_param_type::domFloat4x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float4x2" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFloat4x2) ); + mea->setElementType( domCg_param_type::domFloat4x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float4x3" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFloat4x3) ); + mea->setElementType( domCg_param_type::domFloat4x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float4x4" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFloat4x4) ); + mea->setElementType( domCg_param_type::domFloat4x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemInt) ); + mea->setElementType( domCg_param_type::domInt::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int1" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemInt1) ); + mea->setElementType( domCg_param_type::domInt1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int2" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemInt2) ); + mea->setElementType( domCg_param_type::domInt2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int3" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemInt3) ); + mea->setElementType( domCg_param_type::domInt3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int4" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemInt4) ); + mea->setElementType( domCg_param_type::domInt4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int1x1" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemInt1x1) ); + mea->setElementType( domCg_param_type::domInt1x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int1x2" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemInt1x2) ); + mea->setElementType( domCg_param_type::domInt1x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int1x3" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemInt1x3) ); + mea->setElementType( domCg_param_type::domInt1x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int1x4" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemInt1x4) ); + mea->setElementType( domCg_param_type::domInt1x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int2x1" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemInt2x1) ); + mea->setElementType( domCg_param_type::domInt2x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int2x2" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemInt2x2) ); + mea->setElementType( domCg_param_type::domInt2x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int2x3" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemInt2x3) ); + mea->setElementType( domCg_param_type::domInt2x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int2x4" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemInt2x4) ); + mea->setElementType( domCg_param_type::domInt2x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int3x1" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemInt3x1) ); + mea->setElementType( domCg_param_type::domInt3x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int3x2" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemInt3x2) ); + mea->setElementType( domCg_param_type::domInt3x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int3x3" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemInt3x3) ); + mea->setElementType( domCg_param_type::domInt3x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int3x4" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemInt3x4) ); + mea->setElementType( domCg_param_type::domInt3x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int4x1" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemInt4x1) ); + mea->setElementType( domCg_param_type::domInt4x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int4x2" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemInt4x2) ); + mea->setElementType( domCg_param_type::domInt4x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int4x3" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemInt4x3) ); + mea->setElementType( domCg_param_type::domInt4x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int4x4" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemInt4x4) ); + mea->setElementType( domCg_param_type::domInt4x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "half" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemHalf) ); + mea->setElementType( domCg_param_type::domHalf::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "half1" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemHalf1) ); + mea->setElementType( domCg_param_type::domHalf1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "half2" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemHalf2) ); + mea->setElementType( domCg_param_type::domHalf2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "half3" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemHalf3) ); + mea->setElementType( domCg_param_type::domHalf3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "half4" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemHalf4) ); + mea->setElementType( domCg_param_type::domHalf4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "half1x1" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemHalf1x1) ); + mea->setElementType( domCg_param_type::domHalf1x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "half1x2" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemHalf1x2) ); + mea->setElementType( domCg_param_type::domHalf1x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "half1x3" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemHalf1x3) ); + mea->setElementType( domCg_param_type::domHalf1x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "half1x4" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemHalf1x4) ); + mea->setElementType( domCg_param_type::domHalf1x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "half2x1" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemHalf2x1) ); + mea->setElementType( domCg_param_type::domHalf2x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "half2x2" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemHalf2x2) ); + mea->setElementType( domCg_param_type::domHalf2x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "half2x3" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemHalf2x3) ); + mea->setElementType( domCg_param_type::domHalf2x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "half2x4" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemHalf2x4) ); + mea->setElementType( domCg_param_type::domHalf2x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "half3x1" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemHalf3x1) ); + mea->setElementType( domCg_param_type::domHalf3x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "half3x2" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemHalf3x2) ); + mea->setElementType( domCg_param_type::domHalf3x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "half3x3" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemHalf3x3) ); + mea->setElementType( domCg_param_type::domHalf3x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "half3x4" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemHalf3x4) ); + mea->setElementType( domCg_param_type::domHalf3x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "half4x1" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemHalf4x1) ); + mea->setElementType( domCg_param_type::domHalf4x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "half4x2" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemHalf4x2) ); + mea->setElementType( domCg_param_type::domHalf4x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "half4x3" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemHalf4x3) ); + mea->setElementType( domCg_param_type::domHalf4x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "half4x4" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemHalf4x4) ); + mea->setElementType( domCg_param_type::domHalf4x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fixed" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFixed) ); + mea->setElementType( domCg_param_type::domFixed::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fixed1" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFixed1) ); + mea->setElementType( domCg_param_type::domFixed1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fixed2" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFixed2) ); + mea->setElementType( domCg_param_type::domFixed2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fixed3" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFixed3) ); + mea->setElementType( domCg_param_type::domFixed3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fixed4" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFixed4) ); + mea->setElementType( domCg_param_type::domFixed4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fixed1x1" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFixed1x1) ); + mea->setElementType( domCg_param_type::domFixed1x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fixed1x2" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFixed1x2) ); + mea->setElementType( domCg_param_type::domFixed1x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fixed1x3" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFixed1x3) ); + mea->setElementType( domCg_param_type::domFixed1x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fixed1x4" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFixed1x4) ); + mea->setElementType( domCg_param_type::domFixed1x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fixed2x1" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFixed2x1) ); + mea->setElementType( domCg_param_type::domFixed2x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fixed2x2" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFixed2x2) ); + mea->setElementType( domCg_param_type::domFixed2x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fixed2x3" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFixed2x3) ); + mea->setElementType( domCg_param_type::domFixed2x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fixed2x4" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFixed2x4) ); + mea->setElementType( domCg_param_type::domFixed2x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fixed3x1" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFixed3x1) ); + mea->setElementType( domCg_param_type::domFixed3x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fixed3x2" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFixed3x2) ); + mea->setElementType( domCg_param_type::domFixed3x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fixed3x3" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFixed3x3) ); + mea->setElementType( domCg_param_type::domFixed3x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fixed3x4" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFixed3x4) ); + mea->setElementType( domCg_param_type::domFixed3x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fixed4x1" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFixed4x1) ); + mea->setElementType( domCg_param_type::domFixed4x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fixed4x2" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFixed4x2) ); + mea->setElementType( domCg_param_type::domFixed4x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fixed4x3" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFixed4x3) ); + mea->setElementType( domCg_param_type::domFixed4x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fixed4x4" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemFixed4x4) ); + mea->setElementType( domCg_param_type::domFixed4x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "surface" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemSurface) ); + mea->setElementType( domCg_surface_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "sampler1D" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemSampler1D) ); + mea->setElementType( domCg_sampler1D::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "sampler2D" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemSampler2D) ); + mea->setElementType( domCg_sampler2D::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "sampler3D" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemSampler3D) ); + mea->setElementType( domCg_sampler3D::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "samplerRECT" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemSamplerRECT) ); + mea->setElementType( domCg_samplerRECT::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "samplerCUBE" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemSamplerCUBE) ); + mea->setElementType( domCg_samplerCUBE::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "samplerDEPTH" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemSamplerDEPTH) ); + mea->setElementType( domCg_samplerDEPTH::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "string" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemString) ); + mea->setElementType( domCg_param_type::domString::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "enum" ); + mea->setOffset( daeOffsetOf(domCg_param_type,elemEnum) ); + mea->setElementType( domCg_param_type::domEnum::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domCg_param_type,_contents)); + meta->addContentsOrder(daeOffsetOf(domCg_param_type,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domCg_param_type,_CMData), 1); + meta->setElementSize(sizeof(domCg_param_type)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domBool::create(DAE& dae) +{ + domCg_param_type::domBoolRef ref = new domCg_param_type::domBool(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domBool::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool" ); + meta->registerClass(domCg_param_type::domBool::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_bool")); + ma->setOffset( daeOffsetOf( domCg_param_type::domBool , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domBool)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domBool1::create(DAE& dae) +{ + domCg_param_type::domBool1Ref ref = new domCg_param_type::domBool1(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domBool1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool1" ); + meta->registerClass(domCg_param_type::domBool1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_bool1")); + ma->setOffset( daeOffsetOf( domCg_param_type::domBool1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domBool1)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domBool2::create(DAE& dae) +{ + domCg_param_type::domBool2Ref ref = new domCg_param_type::domBool2(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domBool2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool2" ); + meta->registerClass(domCg_param_type::domBool2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_bool2")); + ma->setOffset( daeOffsetOf( domCg_param_type::domBool2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domBool2)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domBool3::create(DAE& dae) +{ + domCg_param_type::domBool3Ref ref = new domCg_param_type::domBool3(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domBool3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool3" ); + meta->registerClass(domCg_param_type::domBool3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_bool3")); + ma->setOffset( daeOffsetOf( domCg_param_type::domBool3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domBool3)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domBool4::create(DAE& dae) +{ + domCg_param_type::domBool4Ref ref = new domCg_param_type::domBool4(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domBool4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool4" ); + meta->registerClass(domCg_param_type::domBool4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_bool4")); + ma->setOffset( daeOffsetOf( domCg_param_type::domBool4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domBool4)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domBool1x1::create(DAE& dae) +{ + domCg_param_type::domBool1x1Ref ref = new domCg_param_type::domBool1x1(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domBool1x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool1x1" ); + meta->registerClass(domCg_param_type::domBool1x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_bool1x1")); + ma->setOffset( daeOffsetOf( domCg_param_type::domBool1x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domBool1x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domBool1x2::create(DAE& dae) +{ + domCg_param_type::domBool1x2Ref ref = new domCg_param_type::domBool1x2(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domBool1x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool1x2" ); + meta->registerClass(domCg_param_type::domBool1x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_bool1x2")); + ma->setOffset( daeOffsetOf( domCg_param_type::domBool1x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domBool1x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domBool1x3::create(DAE& dae) +{ + domCg_param_type::domBool1x3Ref ref = new domCg_param_type::domBool1x3(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domBool1x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool1x3" ); + meta->registerClass(domCg_param_type::domBool1x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_bool1x3")); + ma->setOffset( daeOffsetOf( domCg_param_type::domBool1x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domBool1x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domBool1x4::create(DAE& dae) +{ + domCg_param_type::domBool1x4Ref ref = new domCg_param_type::domBool1x4(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domBool1x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool1x4" ); + meta->registerClass(domCg_param_type::domBool1x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_bool1x4")); + ma->setOffset( daeOffsetOf( domCg_param_type::domBool1x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domBool1x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domBool2x1::create(DAE& dae) +{ + domCg_param_type::domBool2x1Ref ref = new domCg_param_type::domBool2x1(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domBool2x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool2x1" ); + meta->registerClass(domCg_param_type::domBool2x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_bool2x1")); + ma->setOffset( daeOffsetOf( domCg_param_type::domBool2x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domBool2x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domBool2x2::create(DAE& dae) +{ + domCg_param_type::domBool2x2Ref ref = new domCg_param_type::domBool2x2(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domBool2x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool2x2" ); + meta->registerClass(domCg_param_type::domBool2x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_bool2x2")); + ma->setOffset( daeOffsetOf( domCg_param_type::domBool2x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domBool2x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domBool2x3::create(DAE& dae) +{ + domCg_param_type::domBool2x3Ref ref = new domCg_param_type::domBool2x3(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domBool2x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool2x3" ); + meta->registerClass(domCg_param_type::domBool2x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_bool2x3")); + ma->setOffset( daeOffsetOf( domCg_param_type::domBool2x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domBool2x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domBool2x4::create(DAE& dae) +{ + domCg_param_type::domBool2x4Ref ref = new domCg_param_type::domBool2x4(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domBool2x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool2x4" ); + meta->registerClass(domCg_param_type::domBool2x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_bool2x4")); + ma->setOffset( daeOffsetOf( domCg_param_type::domBool2x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domBool2x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domBool3x1::create(DAE& dae) +{ + domCg_param_type::domBool3x1Ref ref = new domCg_param_type::domBool3x1(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domBool3x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool3x1" ); + meta->registerClass(domCg_param_type::domBool3x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_bool3x1")); + ma->setOffset( daeOffsetOf( domCg_param_type::domBool3x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domBool3x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domBool3x2::create(DAE& dae) +{ + domCg_param_type::domBool3x2Ref ref = new domCg_param_type::domBool3x2(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domBool3x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool3x2" ); + meta->registerClass(domCg_param_type::domBool3x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_bool3x2")); + ma->setOffset( daeOffsetOf( domCg_param_type::domBool3x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domBool3x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domBool3x3::create(DAE& dae) +{ + domCg_param_type::domBool3x3Ref ref = new domCg_param_type::domBool3x3(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domBool3x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool3x3" ); + meta->registerClass(domCg_param_type::domBool3x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_bool3x3")); + ma->setOffset( daeOffsetOf( domCg_param_type::domBool3x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domBool3x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domBool3x4::create(DAE& dae) +{ + domCg_param_type::domBool3x4Ref ref = new domCg_param_type::domBool3x4(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domBool3x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool3x4" ); + meta->registerClass(domCg_param_type::domBool3x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_bool3x4")); + ma->setOffset( daeOffsetOf( domCg_param_type::domBool3x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domBool3x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domBool4x1::create(DAE& dae) +{ + domCg_param_type::domBool4x1Ref ref = new domCg_param_type::domBool4x1(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domBool4x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool4x1" ); + meta->registerClass(domCg_param_type::domBool4x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_bool4x1")); + ma->setOffset( daeOffsetOf( domCg_param_type::domBool4x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domBool4x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domBool4x2::create(DAE& dae) +{ + domCg_param_type::domBool4x2Ref ref = new domCg_param_type::domBool4x2(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domBool4x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool4x2" ); + meta->registerClass(domCg_param_type::domBool4x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_bool4x2")); + ma->setOffset( daeOffsetOf( domCg_param_type::domBool4x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domBool4x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domBool4x3::create(DAE& dae) +{ + domCg_param_type::domBool4x3Ref ref = new domCg_param_type::domBool4x3(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domBool4x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool4x3" ); + meta->registerClass(domCg_param_type::domBool4x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_bool4x3")); + ma->setOffset( daeOffsetOf( domCg_param_type::domBool4x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domBool4x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domBool4x4::create(DAE& dae) +{ + domCg_param_type::domBool4x4Ref ref = new domCg_param_type::domBool4x4(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domBool4x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool4x4" ); + meta->registerClass(domCg_param_type::domBool4x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_bool4x4")); + ma->setOffset( daeOffsetOf( domCg_param_type::domBool4x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domBool4x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFloat::create(DAE& dae) +{ + domCg_param_type::domFloatRef ref = new domCg_param_type::domFloat(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFloat::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float" ); + meta->registerClass(domCg_param_type::domFloat::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_float")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFloat , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFloat)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFloat1::create(DAE& dae) +{ + domCg_param_type::domFloat1Ref ref = new domCg_param_type::domFloat1(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFloat1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float1" ); + meta->registerClass(domCg_param_type::domFloat1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_float1")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFloat1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFloat1)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFloat2::create(DAE& dae) +{ + domCg_param_type::domFloat2Ref ref = new domCg_param_type::domFloat2(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFloat2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float2" ); + meta->registerClass(domCg_param_type::domFloat2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_float2")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFloat2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFloat2)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFloat3::create(DAE& dae) +{ + domCg_param_type::domFloat3Ref ref = new domCg_param_type::domFloat3(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFloat3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float3" ); + meta->registerClass(domCg_param_type::domFloat3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_float3")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFloat3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFloat3)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFloat4::create(DAE& dae) +{ + domCg_param_type::domFloat4Ref ref = new domCg_param_type::domFloat4(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFloat4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float4" ); + meta->registerClass(domCg_param_type::domFloat4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_float4")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFloat4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFloat4)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFloat1x1::create(DAE& dae) +{ + domCg_param_type::domFloat1x1Ref ref = new domCg_param_type::domFloat1x1(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFloat1x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float1x1" ); + meta->registerClass(domCg_param_type::domFloat1x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_float1x1")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFloat1x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFloat1x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFloat1x2::create(DAE& dae) +{ + domCg_param_type::domFloat1x2Ref ref = new domCg_param_type::domFloat1x2(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFloat1x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float1x2" ); + meta->registerClass(domCg_param_type::domFloat1x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_float1x2")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFloat1x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFloat1x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFloat1x3::create(DAE& dae) +{ + domCg_param_type::domFloat1x3Ref ref = new domCg_param_type::domFloat1x3(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFloat1x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float1x3" ); + meta->registerClass(domCg_param_type::domFloat1x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_float1x3")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFloat1x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFloat1x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFloat1x4::create(DAE& dae) +{ + domCg_param_type::domFloat1x4Ref ref = new domCg_param_type::domFloat1x4(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFloat1x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float1x4" ); + meta->registerClass(domCg_param_type::domFloat1x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_float1x4")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFloat1x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFloat1x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFloat2x1::create(DAE& dae) +{ + domCg_param_type::domFloat2x1Ref ref = new domCg_param_type::domFloat2x1(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFloat2x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float2x1" ); + meta->registerClass(domCg_param_type::domFloat2x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_float2x1")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFloat2x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFloat2x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFloat2x2::create(DAE& dae) +{ + domCg_param_type::domFloat2x2Ref ref = new domCg_param_type::domFloat2x2(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFloat2x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float2x2" ); + meta->registerClass(domCg_param_type::domFloat2x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_float2x2")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFloat2x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFloat2x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFloat2x3::create(DAE& dae) +{ + domCg_param_type::domFloat2x3Ref ref = new domCg_param_type::domFloat2x3(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFloat2x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float2x3" ); + meta->registerClass(domCg_param_type::domFloat2x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_float2x3")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFloat2x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFloat2x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFloat2x4::create(DAE& dae) +{ + domCg_param_type::domFloat2x4Ref ref = new domCg_param_type::domFloat2x4(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFloat2x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float2x4" ); + meta->registerClass(domCg_param_type::domFloat2x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_float2x4")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFloat2x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFloat2x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFloat3x1::create(DAE& dae) +{ + domCg_param_type::domFloat3x1Ref ref = new domCg_param_type::domFloat3x1(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFloat3x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float3x1" ); + meta->registerClass(domCg_param_type::domFloat3x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_float3x1")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFloat3x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFloat3x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFloat3x2::create(DAE& dae) +{ + domCg_param_type::domFloat3x2Ref ref = new domCg_param_type::domFloat3x2(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFloat3x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float3x2" ); + meta->registerClass(domCg_param_type::domFloat3x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_float3x2")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFloat3x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFloat3x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFloat3x3::create(DAE& dae) +{ + domCg_param_type::domFloat3x3Ref ref = new domCg_param_type::domFloat3x3(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFloat3x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float3x3" ); + meta->registerClass(domCg_param_type::domFloat3x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_float3x3")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFloat3x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFloat3x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFloat3x4::create(DAE& dae) +{ + domCg_param_type::domFloat3x4Ref ref = new domCg_param_type::domFloat3x4(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFloat3x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float3x4" ); + meta->registerClass(domCg_param_type::domFloat3x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_float3x4")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFloat3x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFloat3x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFloat4x1::create(DAE& dae) +{ + domCg_param_type::domFloat4x1Ref ref = new domCg_param_type::domFloat4x1(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFloat4x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float4x1" ); + meta->registerClass(domCg_param_type::domFloat4x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_float4x1")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFloat4x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFloat4x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFloat4x2::create(DAE& dae) +{ + domCg_param_type::domFloat4x2Ref ref = new domCg_param_type::domFloat4x2(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFloat4x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float4x2" ); + meta->registerClass(domCg_param_type::domFloat4x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_float4x2")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFloat4x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFloat4x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFloat4x3::create(DAE& dae) +{ + domCg_param_type::domFloat4x3Ref ref = new domCg_param_type::domFloat4x3(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFloat4x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float4x3" ); + meta->registerClass(domCg_param_type::domFloat4x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_float4x3")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFloat4x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFloat4x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFloat4x4::create(DAE& dae) +{ + domCg_param_type::domFloat4x4Ref ref = new domCg_param_type::domFloat4x4(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFloat4x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float4x4" ); + meta->registerClass(domCg_param_type::domFloat4x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_float4x4")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFloat4x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFloat4x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domInt::create(DAE& dae) +{ + domCg_param_type::domIntRef ref = new domCg_param_type::domInt(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domInt::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int" ); + meta->registerClass(domCg_param_type::domInt::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_int")); + ma->setOffset( daeOffsetOf( domCg_param_type::domInt , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domInt)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domInt1::create(DAE& dae) +{ + domCg_param_type::domInt1Ref ref = new domCg_param_type::domInt1(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domInt1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int1" ); + meta->registerClass(domCg_param_type::domInt1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_int1")); + ma->setOffset( daeOffsetOf( domCg_param_type::domInt1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domInt1)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domInt2::create(DAE& dae) +{ + domCg_param_type::domInt2Ref ref = new domCg_param_type::domInt2(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domInt2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int2" ); + meta->registerClass(domCg_param_type::domInt2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_int2")); + ma->setOffset( daeOffsetOf( domCg_param_type::domInt2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domInt2)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domInt3::create(DAE& dae) +{ + domCg_param_type::domInt3Ref ref = new domCg_param_type::domInt3(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domInt3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int3" ); + meta->registerClass(domCg_param_type::domInt3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_int3")); + ma->setOffset( daeOffsetOf( domCg_param_type::domInt3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domInt3)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domInt4::create(DAE& dae) +{ + domCg_param_type::domInt4Ref ref = new domCg_param_type::domInt4(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domInt4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int4" ); + meta->registerClass(domCg_param_type::domInt4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_int4")); + ma->setOffset( daeOffsetOf( domCg_param_type::domInt4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domInt4)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domInt1x1::create(DAE& dae) +{ + domCg_param_type::domInt1x1Ref ref = new domCg_param_type::domInt1x1(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domInt1x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int1x1" ); + meta->registerClass(domCg_param_type::domInt1x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_int1x1")); + ma->setOffset( daeOffsetOf( domCg_param_type::domInt1x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domInt1x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domInt1x2::create(DAE& dae) +{ + domCg_param_type::domInt1x2Ref ref = new domCg_param_type::domInt1x2(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domInt1x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int1x2" ); + meta->registerClass(domCg_param_type::domInt1x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_int1x2")); + ma->setOffset( daeOffsetOf( domCg_param_type::domInt1x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domInt1x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domInt1x3::create(DAE& dae) +{ + domCg_param_type::domInt1x3Ref ref = new domCg_param_type::domInt1x3(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domInt1x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int1x3" ); + meta->registerClass(domCg_param_type::domInt1x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_int1x3")); + ma->setOffset( daeOffsetOf( domCg_param_type::domInt1x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domInt1x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domInt1x4::create(DAE& dae) +{ + domCg_param_type::domInt1x4Ref ref = new domCg_param_type::domInt1x4(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domInt1x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int1x4" ); + meta->registerClass(domCg_param_type::domInt1x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_int1x4")); + ma->setOffset( daeOffsetOf( domCg_param_type::domInt1x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domInt1x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domInt2x1::create(DAE& dae) +{ + domCg_param_type::domInt2x1Ref ref = new domCg_param_type::domInt2x1(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domInt2x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int2x1" ); + meta->registerClass(domCg_param_type::domInt2x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_int2x1")); + ma->setOffset( daeOffsetOf( domCg_param_type::domInt2x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domInt2x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domInt2x2::create(DAE& dae) +{ + domCg_param_type::domInt2x2Ref ref = new domCg_param_type::domInt2x2(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domInt2x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int2x2" ); + meta->registerClass(domCg_param_type::domInt2x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_int2x2")); + ma->setOffset( daeOffsetOf( domCg_param_type::domInt2x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domInt2x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domInt2x3::create(DAE& dae) +{ + domCg_param_type::domInt2x3Ref ref = new domCg_param_type::domInt2x3(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domInt2x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int2x3" ); + meta->registerClass(domCg_param_type::domInt2x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_int2x3")); + ma->setOffset( daeOffsetOf( domCg_param_type::domInt2x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domInt2x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domInt2x4::create(DAE& dae) +{ + domCg_param_type::domInt2x4Ref ref = new domCg_param_type::domInt2x4(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domInt2x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int2x4" ); + meta->registerClass(domCg_param_type::domInt2x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_int2x4")); + ma->setOffset( daeOffsetOf( domCg_param_type::domInt2x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domInt2x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domInt3x1::create(DAE& dae) +{ + domCg_param_type::domInt3x1Ref ref = new domCg_param_type::domInt3x1(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domInt3x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int3x1" ); + meta->registerClass(domCg_param_type::domInt3x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_int3x1")); + ma->setOffset( daeOffsetOf( domCg_param_type::domInt3x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domInt3x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domInt3x2::create(DAE& dae) +{ + domCg_param_type::domInt3x2Ref ref = new domCg_param_type::domInt3x2(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domInt3x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int3x2" ); + meta->registerClass(domCg_param_type::domInt3x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_int3x2")); + ma->setOffset( daeOffsetOf( domCg_param_type::domInt3x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domInt3x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domInt3x3::create(DAE& dae) +{ + domCg_param_type::domInt3x3Ref ref = new domCg_param_type::domInt3x3(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domInt3x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int3x3" ); + meta->registerClass(domCg_param_type::domInt3x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_int3x3")); + ma->setOffset( daeOffsetOf( domCg_param_type::domInt3x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domInt3x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domInt3x4::create(DAE& dae) +{ + domCg_param_type::domInt3x4Ref ref = new domCg_param_type::domInt3x4(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domInt3x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int3x4" ); + meta->registerClass(domCg_param_type::domInt3x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_int3x4")); + ma->setOffset( daeOffsetOf( domCg_param_type::domInt3x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domInt3x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domInt4x1::create(DAE& dae) +{ + domCg_param_type::domInt4x1Ref ref = new domCg_param_type::domInt4x1(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domInt4x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int4x1" ); + meta->registerClass(domCg_param_type::domInt4x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_int4x1")); + ma->setOffset( daeOffsetOf( domCg_param_type::domInt4x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domInt4x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domInt4x2::create(DAE& dae) +{ + domCg_param_type::domInt4x2Ref ref = new domCg_param_type::domInt4x2(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domInt4x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int4x2" ); + meta->registerClass(domCg_param_type::domInt4x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_int4x2")); + ma->setOffset( daeOffsetOf( domCg_param_type::domInt4x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domInt4x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domInt4x3::create(DAE& dae) +{ + domCg_param_type::domInt4x3Ref ref = new domCg_param_type::domInt4x3(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domInt4x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int4x3" ); + meta->registerClass(domCg_param_type::domInt4x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_int4x3")); + ma->setOffset( daeOffsetOf( domCg_param_type::domInt4x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domInt4x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domInt4x4::create(DAE& dae) +{ + domCg_param_type::domInt4x4Ref ref = new domCg_param_type::domInt4x4(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domInt4x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int4x4" ); + meta->registerClass(domCg_param_type::domInt4x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_int4x4")); + ma->setOffset( daeOffsetOf( domCg_param_type::domInt4x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domInt4x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domHalf::create(DAE& dae) +{ + domCg_param_type::domHalfRef ref = new domCg_param_type::domHalf(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domHalf::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "half" ); + meta->registerClass(domCg_param_type::domHalf::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_half")); + ma->setOffset( daeOffsetOf( domCg_param_type::domHalf , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domHalf)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domHalf1::create(DAE& dae) +{ + domCg_param_type::domHalf1Ref ref = new domCg_param_type::domHalf1(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domHalf1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "half1" ); + meta->registerClass(domCg_param_type::domHalf1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_half1")); + ma->setOffset( daeOffsetOf( domCg_param_type::domHalf1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domHalf1)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domHalf2::create(DAE& dae) +{ + domCg_param_type::domHalf2Ref ref = new domCg_param_type::domHalf2(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domHalf2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "half2" ); + meta->registerClass(domCg_param_type::domHalf2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_half2")); + ma->setOffset( daeOffsetOf( domCg_param_type::domHalf2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domHalf2)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domHalf3::create(DAE& dae) +{ + domCg_param_type::domHalf3Ref ref = new domCg_param_type::domHalf3(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domHalf3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "half3" ); + meta->registerClass(domCg_param_type::domHalf3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_half3")); + ma->setOffset( daeOffsetOf( domCg_param_type::domHalf3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domHalf3)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domHalf4::create(DAE& dae) +{ + domCg_param_type::domHalf4Ref ref = new domCg_param_type::domHalf4(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domHalf4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "half4" ); + meta->registerClass(domCg_param_type::domHalf4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_half4")); + ma->setOffset( daeOffsetOf( domCg_param_type::domHalf4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domHalf4)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domHalf1x1::create(DAE& dae) +{ + domCg_param_type::domHalf1x1Ref ref = new domCg_param_type::domHalf1x1(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domHalf1x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "half1x1" ); + meta->registerClass(domCg_param_type::domHalf1x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_half1x1")); + ma->setOffset( daeOffsetOf( domCg_param_type::domHalf1x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domHalf1x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domHalf1x2::create(DAE& dae) +{ + domCg_param_type::domHalf1x2Ref ref = new domCg_param_type::domHalf1x2(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domHalf1x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "half1x2" ); + meta->registerClass(domCg_param_type::domHalf1x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_half1x2")); + ma->setOffset( daeOffsetOf( domCg_param_type::domHalf1x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domHalf1x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domHalf1x3::create(DAE& dae) +{ + domCg_param_type::domHalf1x3Ref ref = new domCg_param_type::domHalf1x3(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domHalf1x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "half1x3" ); + meta->registerClass(domCg_param_type::domHalf1x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_half1x3")); + ma->setOffset( daeOffsetOf( domCg_param_type::domHalf1x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domHalf1x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domHalf1x4::create(DAE& dae) +{ + domCg_param_type::domHalf1x4Ref ref = new domCg_param_type::domHalf1x4(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domHalf1x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "half1x4" ); + meta->registerClass(domCg_param_type::domHalf1x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_half1x4")); + ma->setOffset( daeOffsetOf( domCg_param_type::domHalf1x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domHalf1x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domHalf2x1::create(DAE& dae) +{ + domCg_param_type::domHalf2x1Ref ref = new domCg_param_type::domHalf2x1(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domHalf2x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "half2x1" ); + meta->registerClass(domCg_param_type::domHalf2x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_half2x1")); + ma->setOffset( daeOffsetOf( domCg_param_type::domHalf2x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domHalf2x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domHalf2x2::create(DAE& dae) +{ + domCg_param_type::domHalf2x2Ref ref = new domCg_param_type::domHalf2x2(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domHalf2x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "half2x2" ); + meta->registerClass(domCg_param_type::domHalf2x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_half2x2")); + ma->setOffset( daeOffsetOf( domCg_param_type::domHalf2x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domHalf2x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domHalf2x3::create(DAE& dae) +{ + domCg_param_type::domHalf2x3Ref ref = new domCg_param_type::domHalf2x3(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domHalf2x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "half2x3" ); + meta->registerClass(domCg_param_type::domHalf2x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_half2x3")); + ma->setOffset( daeOffsetOf( domCg_param_type::domHalf2x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domHalf2x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domHalf2x4::create(DAE& dae) +{ + domCg_param_type::domHalf2x4Ref ref = new domCg_param_type::domHalf2x4(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domHalf2x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "half2x4" ); + meta->registerClass(domCg_param_type::domHalf2x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_half2x4")); + ma->setOffset( daeOffsetOf( domCg_param_type::domHalf2x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domHalf2x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domHalf3x1::create(DAE& dae) +{ + domCg_param_type::domHalf3x1Ref ref = new domCg_param_type::domHalf3x1(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domHalf3x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "half3x1" ); + meta->registerClass(domCg_param_type::domHalf3x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_half3x1")); + ma->setOffset( daeOffsetOf( domCg_param_type::domHalf3x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domHalf3x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domHalf3x2::create(DAE& dae) +{ + domCg_param_type::domHalf3x2Ref ref = new domCg_param_type::domHalf3x2(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domHalf3x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "half3x2" ); + meta->registerClass(domCg_param_type::domHalf3x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_half3x2")); + ma->setOffset( daeOffsetOf( domCg_param_type::domHalf3x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domHalf3x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domHalf3x3::create(DAE& dae) +{ + domCg_param_type::domHalf3x3Ref ref = new domCg_param_type::domHalf3x3(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domHalf3x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "half3x3" ); + meta->registerClass(domCg_param_type::domHalf3x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_half3x3")); + ma->setOffset( daeOffsetOf( domCg_param_type::domHalf3x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domHalf3x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domHalf3x4::create(DAE& dae) +{ + domCg_param_type::domHalf3x4Ref ref = new domCg_param_type::domHalf3x4(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domHalf3x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "half3x4" ); + meta->registerClass(domCg_param_type::domHalf3x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_half3x4")); + ma->setOffset( daeOffsetOf( domCg_param_type::domHalf3x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domHalf3x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domHalf4x1::create(DAE& dae) +{ + domCg_param_type::domHalf4x1Ref ref = new domCg_param_type::domHalf4x1(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domHalf4x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "half4x1" ); + meta->registerClass(domCg_param_type::domHalf4x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_half4x1")); + ma->setOffset( daeOffsetOf( domCg_param_type::domHalf4x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domHalf4x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domHalf4x2::create(DAE& dae) +{ + domCg_param_type::domHalf4x2Ref ref = new domCg_param_type::domHalf4x2(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domHalf4x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "half4x2" ); + meta->registerClass(domCg_param_type::domHalf4x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_half4x2")); + ma->setOffset( daeOffsetOf( domCg_param_type::domHalf4x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domHalf4x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domHalf4x3::create(DAE& dae) +{ + domCg_param_type::domHalf4x3Ref ref = new domCg_param_type::domHalf4x3(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domHalf4x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "half4x3" ); + meta->registerClass(domCg_param_type::domHalf4x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_half4x3")); + ma->setOffset( daeOffsetOf( domCg_param_type::domHalf4x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domHalf4x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domHalf4x4::create(DAE& dae) +{ + domCg_param_type::domHalf4x4Ref ref = new domCg_param_type::domHalf4x4(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domHalf4x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "half4x4" ); + meta->registerClass(domCg_param_type::domHalf4x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_half4x4")); + ma->setOffset( daeOffsetOf( domCg_param_type::domHalf4x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domHalf4x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFixed::create(DAE& dae) +{ + domCg_param_type::domFixedRef ref = new domCg_param_type::domFixed(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFixed::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fixed" ); + meta->registerClass(domCg_param_type::domFixed::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_fixed")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFixed , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFixed)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFixed1::create(DAE& dae) +{ + domCg_param_type::domFixed1Ref ref = new domCg_param_type::domFixed1(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFixed1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fixed1" ); + meta->registerClass(domCg_param_type::domFixed1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_fixed1")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFixed1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFixed1)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFixed2::create(DAE& dae) +{ + domCg_param_type::domFixed2Ref ref = new domCg_param_type::domFixed2(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFixed2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fixed2" ); + meta->registerClass(domCg_param_type::domFixed2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_fixed2")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFixed2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFixed2)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFixed3::create(DAE& dae) +{ + domCg_param_type::domFixed3Ref ref = new domCg_param_type::domFixed3(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFixed3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fixed3" ); + meta->registerClass(domCg_param_type::domFixed3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_fixed3")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFixed3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFixed3)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFixed4::create(DAE& dae) +{ + domCg_param_type::domFixed4Ref ref = new domCg_param_type::domFixed4(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFixed4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fixed4" ); + meta->registerClass(domCg_param_type::domFixed4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_fixed4")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFixed4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFixed4)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFixed1x1::create(DAE& dae) +{ + domCg_param_type::domFixed1x1Ref ref = new domCg_param_type::domFixed1x1(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFixed1x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fixed1x1" ); + meta->registerClass(domCg_param_type::domFixed1x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_fixed1x1")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFixed1x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFixed1x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFixed1x2::create(DAE& dae) +{ + domCg_param_type::domFixed1x2Ref ref = new domCg_param_type::domFixed1x2(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFixed1x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fixed1x2" ); + meta->registerClass(domCg_param_type::domFixed1x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_fixed1x2")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFixed1x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFixed1x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFixed1x3::create(DAE& dae) +{ + domCg_param_type::domFixed1x3Ref ref = new domCg_param_type::domFixed1x3(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFixed1x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fixed1x3" ); + meta->registerClass(domCg_param_type::domFixed1x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_fixed1x3")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFixed1x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFixed1x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFixed1x4::create(DAE& dae) +{ + domCg_param_type::domFixed1x4Ref ref = new domCg_param_type::domFixed1x4(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFixed1x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fixed1x4" ); + meta->registerClass(domCg_param_type::domFixed1x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_fixed1x4")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFixed1x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFixed1x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFixed2x1::create(DAE& dae) +{ + domCg_param_type::domFixed2x1Ref ref = new domCg_param_type::domFixed2x1(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFixed2x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fixed2x1" ); + meta->registerClass(domCg_param_type::domFixed2x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_fixed2x1")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFixed2x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFixed2x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFixed2x2::create(DAE& dae) +{ + domCg_param_type::domFixed2x2Ref ref = new domCg_param_type::domFixed2x2(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFixed2x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fixed2x2" ); + meta->registerClass(domCg_param_type::domFixed2x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_fixed2x2")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFixed2x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFixed2x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFixed2x3::create(DAE& dae) +{ + domCg_param_type::domFixed2x3Ref ref = new domCg_param_type::domFixed2x3(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFixed2x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fixed2x3" ); + meta->registerClass(domCg_param_type::domFixed2x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_fixed2x3")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFixed2x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFixed2x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFixed2x4::create(DAE& dae) +{ + domCg_param_type::domFixed2x4Ref ref = new domCg_param_type::domFixed2x4(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFixed2x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fixed2x4" ); + meta->registerClass(domCg_param_type::domFixed2x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_fixed2x4")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFixed2x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFixed2x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFixed3x1::create(DAE& dae) +{ + domCg_param_type::domFixed3x1Ref ref = new domCg_param_type::domFixed3x1(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFixed3x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fixed3x1" ); + meta->registerClass(domCg_param_type::domFixed3x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_fixed3x1")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFixed3x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFixed3x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFixed3x2::create(DAE& dae) +{ + domCg_param_type::domFixed3x2Ref ref = new domCg_param_type::domFixed3x2(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFixed3x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fixed3x2" ); + meta->registerClass(domCg_param_type::domFixed3x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_fixed3x2")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFixed3x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFixed3x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFixed3x3::create(DAE& dae) +{ + domCg_param_type::domFixed3x3Ref ref = new domCg_param_type::domFixed3x3(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFixed3x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fixed3x3" ); + meta->registerClass(domCg_param_type::domFixed3x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_fixed3x3")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFixed3x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFixed3x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFixed3x4::create(DAE& dae) +{ + domCg_param_type::domFixed3x4Ref ref = new domCg_param_type::domFixed3x4(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFixed3x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fixed3x4" ); + meta->registerClass(domCg_param_type::domFixed3x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_fixed3x4")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFixed3x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFixed3x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFixed4x1::create(DAE& dae) +{ + domCg_param_type::domFixed4x1Ref ref = new domCg_param_type::domFixed4x1(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFixed4x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fixed4x1" ); + meta->registerClass(domCg_param_type::domFixed4x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_fixed4x1")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFixed4x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFixed4x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFixed4x2::create(DAE& dae) +{ + domCg_param_type::domFixed4x2Ref ref = new domCg_param_type::domFixed4x2(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFixed4x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fixed4x2" ); + meta->registerClass(domCg_param_type::domFixed4x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_fixed4x2")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFixed4x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFixed4x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFixed4x3::create(DAE& dae) +{ + domCg_param_type::domFixed4x3Ref ref = new domCg_param_type::domFixed4x3(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFixed4x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fixed4x3" ); + meta->registerClass(domCg_param_type::domFixed4x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_fixed4x3")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFixed4x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFixed4x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domFixed4x4::create(DAE& dae) +{ + domCg_param_type::domFixed4x4Ref ref = new domCg_param_type::domFixed4x4(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domFixed4x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fixed4x4" ); + meta->registerClass(domCg_param_type::domFixed4x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Cg_fixed4x4")); + ma->setOffset( daeOffsetOf( domCg_param_type::domFixed4x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domFixed4x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domString::create(DAE& dae) +{ + domCg_param_type::domStringRef ref = new domCg_param_type::domString(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domString::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "string" ); + meta->registerClass(domCg_param_type::domString::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsString")); + ma->setOffset( daeOffsetOf( domCg_param_type::domString , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domString)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_param_type::domEnum::create(DAE& dae) +{ + domCg_param_type::domEnumRef ref = new domCg_param_type::domEnum(dae); + return ref; +} + + +daeMetaElement * +domCg_param_type::domEnum::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "enum" ); + meta->registerClass(domCg_param_type::domEnum::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Gl_enumeration")); + ma->setOffset( daeOffsetOf( domCg_param_type::domEnum , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_param_type::domEnum)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domCg_sampler1D.cpp b/Engine/lib/collada/src/1.4/dom/domCg_sampler1D.cpp new file mode 100644 index 000000000..4917ea12b --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domCg_sampler1D.cpp @@ -0,0 +1,115 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domCg_sampler1D::create(DAE& dae) +{ + domCg_sampler1DRef ref = new domCg_sampler1D(dae); + return ref; +} + + +daeMetaElement * +domCg_sampler1D::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "cg_sampler1D" ); + meta->registerClass(domCg_sampler1D::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "source" ); + mea->setOffset( daeOffsetOf(domCg_sampler1D,elemSource) ); + mea->setElementType( domSource::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "wrap_s" ); + mea->setOffset( daeOffsetOf(domCg_sampler1D,elemWrap_s) ); + mea->setElementType( domWrap_s::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "minfilter" ); + mea->setOffset( daeOffsetOf(domCg_sampler1D,elemMinfilter) ); + mea->setElementType( domMinfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "magfilter" ); + mea->setOffset( daeOffsetOf(domCg_sampler1D,elemMagfilter) ); + mea->setElementType( domMagfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "mipfilter" ); + mea->setOffset( daeOffsetOf(domCg_sampler1D,elemMipfilter) ); + mea->setElementType( domMipfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 5, 0, 1 ); + mea->setName( "border_color" ); + mea->setOffset( daeOffsetOf(domCg_sampler1D,elemBorder_color) ); + mea->setElementType( domBorder_color::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 6, 0, 1 ); + mea->setName( "mipmap_maxlevel" ); + mea->setOffset( daeOffsetOf(domCg_sampler1D,elemMipmap_maxlevel) ); + mea->setElementType( domMipmap_maxlevel::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 7, 0, 1 ); + mea->setName( "mipmap_bias" ); + mea->setOffset( daeOffsetOf(domCg_sampler1D,elemMipmap_bias) ); + mea->setElementType( domMipmap_bias::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 8, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domCg_sampler1D,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 8 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm->setMaxOrdinal( 8 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domCg_sampler1D)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domCg_sampler2D.cpp b/Engine/lib/collada/src/1.4/dom/domCg_sampler2D.cpp new file mode 100644 index 000000000..4e3a3e2be --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domCg_sampler2D.cpp @@ -0,0 +1,121 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domCg_sampler2D::create(DAE& dae) +{ + domCg_sampler2DRef ref = new domCg_sampler2D(dae); + return ref; +} + + +daeMetaElement * +domCg_sampler2D::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "cg_sampler2D" ); + meta->registerClass(domCg_sampler2D::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "source" ); + mea->setOffset( daeOffsetOf(domCg_sampler2D,elemSource) ); + mea->setElementType( domSource::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "wrap_s" ); + mea->setOffset( daeOffsetOf(domCg_sampler2D,elemWrap_s) ); + mea->setElementType( domWrap_s::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "wrap_t" ); + mea->setOffset( daeOffsetOf(domCg_sampler2D,elemWrap_t) ); + mea->setElementType( domWrap_t::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "minfilter" ); + mea->setOffset( daeOffsetOf(domCg_sampler2D,elemMinfilter) ); + mea->setElementType( domMinfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "magfilter" ); + mea->setOffset( daeOffsetOf(domCg_sampler2D,elemMagfilter) ); + mea->setElementType( domMagfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 5, 0, 1 ); + mea->setName( "mipfilter" ); + mea->setOffset( daeOffsetOf(domCg_sampler2D,elemMipfilter) ); + mea->setElementType( domMipfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 6, 0, 1 ); + mea->setName( "border_color" ); + mea->setOffset( daeOffsetOf(domCg_sampler2D,elemBorder_color) ); + mea->setElementType( domBorder_color::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 7, 0, 1 ); + mea->setName( "mipmap_maxlevel" ); + mea->setOffset( daeOffsetOf(domCg_sampler2D,elemMipmap_maxlevel) ); + mea->setElementType( domMipmap_maxlevel::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 8, 0, 1 ); + mea->setName( "mipmap_bias" ); + mea->setOffset( daeOffsetOf(domCg_sampler2D,elemMipmap_bias) ); + mea->setElementType( domMipmap_bias::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 9, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domCg_sampler2D,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 9 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm->setMaxOrdinal( 9 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domCg_sampler2D)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domCg_sampler3D.cpp b/Engine/lib/collada/src/1.4/dom/domCg_sampler3D.cpp new file mode 100644 index 000000000..1f53a9545 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domCg_sampler3D.cpp @@ -0,0 +1,127 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domCg_sampler3D::create(DAE& dae) +{ + domCg_sampler3DRef ref = new domCg_sampler3D(dae); + return ref; +} + + +daeMetaElement * +domCg_sampler3D::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "cg_sampler3D" ); + meta->registerClass(domCg_sampler3D::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "source" ); + mea->setOffset( daeOffsetOf(domCg_sampler3D,elemSource) ); + mea->setElementType( domSource::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "wrap_s" ); + mea->setOffset( daeOffsetOf(domCg_sampler3D,elemWrap_s) ); + mea->setElementType( domWrap_s::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "wrap_t" ); + mea->setOffset( daeOffsetOf(domCg_sampler3D,elemWrap_t) ); + mea->setElementType( domWrap_t::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "wrap_p" ); + mea->setOffset( daeOffsetOf(domCg_sampler3D,elemWrap_p) ); + mea->setElementType( domWrap_p::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "minfilter" ); + mea->setOffset( daeOffsetOf(domCg_sampler3D,elemMinfilter) ); + mea->setElementType( domMinfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 5, 0, 1 ); + mea->setName( "magfilter" ); + mea->setOffset( daeOffsetOf(domCg_sampler3D,elemMagfilter) ); + mea->setElementType( domMagfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 6, 0, 1 ); + mea->setName( "mipfilter" ); + mea->setOffset( daeOffsetOf(domCg_sampler3D,elemMipfilter) ); + mea->setElementType( domMipfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 7, 0, 1 ); + mea->setName( "border_color" ); + mea->setOffset( daeOffsetOf(domCg_sampler3D,elemBorder_color) ); + mea->setElementType( domBorder_color::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 8, 0, 1 ); + mea->setName( "mipmap_maxlevel" ); + mea->setOffset( daeOffsetOf(domCg_sampler3D,elemMipmap_maxlevel) ); + mea->setElementType( domMipmap_maxlevel::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 9, 0, 1 ); + mea->setName( "mipmap_bias" ); + mea->setOffset( daeOffsetOf(domCg_sampler3D,elemMipmap_bias) ); + mea->setElementType( domMipmap_bias::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 10, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domCg_sampler3D,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 10 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm->setMaxOrdinal( 10 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domCg_sampler3D)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domCg_samplerCUBE.cpp b/Engine/lib/collada/src/1.4/dom/domCg_samplerCUBE.cpp new file mode 100644 index 000000000..6a3ab40df --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domCg_samplerCUBE.cpp @@ -0,0 +1,127 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domCg_samplerCUBE::create(DAE& dae) +{ + domCg_samplerCUBERef ref = new domCg_samplerCUBE(dae); + return ref; +} + + +daeMetaElement * +domCg_samplerCUBE::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "cg_samplerCUBE" ); + meta->registerClass(domCg_samplerCUBE::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "source" ); + mea->setOffset( daeOffsetOf(domCg_samplerCUBE,elemSource) ); + mea->setElementType( domSource::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "wrap_s" ); + mea->setOffset( daeOffsetOf(domCg_samplerCUBE,elemWrap_s) ); + mea->setElementType( domWrap_s::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "wrap_t" ); + mea->setOffset( daeOffsetOf(domCg_samplerCUBE,elemWrap_t) ); + mea->setElementType( domWrap_t::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "wrap_p" ); + mea->setOffset( daeOffsetOf(domCg_samplerCUBE,elemWrap_p) ); + mea->setElementType( domWrap_p::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "minfilter" ); + mea->setOffset( daeOffsetOf(domCg_samplerCUBE,elemMinfilter) ); + mea->setElementType( domMinfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 5, 0, 1 ); + mea->setName( "magfilter" ); + mea->setOffset( daeOffsetOf(domCg_samplerCUBE,elemMagfilter) ); + mea->setElementType( domMagfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 6, 0, 1 ); + mea->setName( "mipfilter" ); + mea->setOffset( daeOffsetOf(domCg_samplerCUBE,elemMipfilter) ); + mea->setElementType( domMipfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 7, 0, 1 ); + mea->setName( "border_color" ); + mea->setOffset( daeOffsetOf(domCg_samplerCUBE,elemBorder_color) ); + mea->setElementType( domBorder_color::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 8, 0, 1 ); + mea->setName( "mipmap_maxlevel" ); + mea->setOffset( daeOffsetOf(domCg_samplerCUBE,elemMipmap_maxlevel) ); + mea->setElementType( domMipmap_maxlevel::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 9, 0, 1 ); + mea->setName( "mipmap_bias" ); + mea->setOffset( daeOffsetOf(domCg_samplerCUBE,elemMipmap_bias) ); + mea->setElementType( domMipmap_bias::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 10, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domCg_samplerCUBE,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 10 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm->setMaxOrdinal( 10 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domCg_samplerCUBE)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domCg_samplerDEPTH.cpp b/Engine/lib/collada/src/1.4/dom/domCg_samplerDEPTH.cpp new file mode 100644 index 000000000..3db2cbb82 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domCg_samplerDEPTH.cpp @@ -0,0 +1,97 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domCg_samplerDEPTH::create(DAE& dae) +{ + domCg_samplerDEPTHRef ref = new domCg_samplerDEPTH(dae); + return ref; +} + + +daeMetaElement * +domCg_samplerDEPTH::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "cg_samplerDEPTH" ); + meta->registerClass(domCg_samplerDEPTH::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "source" ); + mea->setOffset( daeOffsetOf(domCg_samplerDEPTH,elemSource) ); + mea->setElementType( domSource::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "wrap_s" ); + mea->setOffset( daeOffsetOf(domCg_samplerDEPTH,elemWrap_s) ); + mea->setElementType( domWrap_s::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "wrap_t" ); + mea->setOffset( daeOffsetOf(domCg_samplerDEPTH,elemWrap_t) ); + mea->setElementType( domWrap_t::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "minfilter" ); + mea->setOffset( daeOffsetOf(domCg_samplerDEPTH,elemMinfilter) ); + mea->setElementType( domMinfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "magfilter" ); + mea->setOffset( daeOffsetOf(domCg_samplerDEPTH,elemMagfilter) ); + mea->setElementType( domMagfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 5, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domCg_samplerDEPTH,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 5 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm->setMaxOrdinal( 5 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domCg_samplerDEPTH)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domCg_samplerRECT.cpp b/Engine/lib/collada/src/1.4/dom/domCg_samplerRECT.cpp new file mode 100644 index 000000000..11f54faee --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domCg_samplerRECT.cpp @@ -0,0 +1,121 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domCg_samplerRECT::create(DAE& dae) +{ + domCg_samplerRECTRef ref = new domCg_samplerRECT(dae); + return ref; +} + + +daeMetaElement * +domCg_samplerRECT::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "cg_samplerRECT" ); + meta->registerClass(domCg_samplerRECT::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "source" ); + mea->setOffset( daeOffsetOf(domCg_samplerRECT,elemSource) ); + mea->setElementType( domSource::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "wrap_s" ); + mea->setOffset( daeOffsetOf(domCg_samplerRECT,elemWrap_s) ); + mea->setElementType( domWrap_s::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "wrap_t" ); + mea->setOffset( daeOffsetOf(domCg_samplerRECT,elemWrap_t) ); + mea->setElementType( domWrap_t::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "minfilter" ); + mea->setOffset( daeOffsetOf(domCg_samplerRECT,elemMinfilter) ); + mea->setElementType( domMinfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "magfilter" ); + mea->setOffset( daeOffsetOf(domCg_samplerRECT,elemMagfilter) ); + mea->setElementType( domMagfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 5, 0, 1 ); + mea->setName( "mipfilter" ); + mea->setOffset( daeOffsetOf(domCg_samplerRECT,elemMipfilter) ); + mea->setElementType( domMipfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 6, 0, 1 ); + mea->setName( "border_color" ); + mea->setOffset( daeOffsetOf(domCg_samplerRECT,elemBorder_color) ); + mea->setElementType( domBorder_color::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 7, 0, 1 ); + mea->setName( "mipmap_maxlevel" ); + mea->setOffset( daeOffsetOf(domCg_samplerRECT,elemMipmap_maxlevel) ); + mea->setElementType( domMipmap_maxlevel::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 8, 0, 1 ); + mea->setName( "mipmap_bias" ); + mea->setOffset( daeOffsetOf(domCg_samplerRECT,elemMipmap_bias) ); + mea->setElementType( domMipmap_bias::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 9, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domCg_samplerRECT,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 9 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm->setMaxOrdinal( 9 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domCg_samplerRECT)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domCg_setarray_type.cpp b/Engine/lib/collada/src/1.4/dom/domCg_setarray_type.cpp new file mode 100644 index 000000000..9e862802f --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domCg_setarray_type.cpp @@ -0,0 +1,89 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domCg_setarray_type::create(DAE& dae) +{ + domCg_setarray_typeRef ref = new domCg_setarray_type(dae); + return ref; +} + + +daeMetaElement * +domCg_setarray_type::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "cg_setarray_type" ); + meta->registerClass(domCg_setarray_type::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 0, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "cg_param_type" ); + mea->setOffset( daeOffsetOf(domCg_setarray_type,elemCg_param_type_array) ); + mea->setElementType( domCg_param_type::registerElement(dae) ); + cm->appendChild( new daeMetaGroup( mea, meta, cm, 0, 1, 1 ) ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "array" ); + mea->setOffset( daeOffsetOf(domCg_setarray_type,elemArray_array) ); + mea->setElementType( domCg_setarray_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "usertype" ); + mea->setOffset( daeOffsetOf(domCg_setarray_type,elemUsertype_array) ); + mea->setElementType( domCg_setuser_type::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3000 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domCg_setarray_type,_contents)); + meta->addContentsOrder(daeOffsetOf(domCg_setarray_type,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domCg_setarray_type,_CMData), 1); + // Add attribute: length + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "length" ); + ma->setType( dae.getAtomicTypes().get("xsPositiveInteger")); + ma->setOffset( daeOffsetOf( domCg_setarray_type , attrLength )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_setarray_type)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domCg_setparam.cpp b/Engine/lib/collada/src/1.4/dom/domCg_setparam.cpp new file mode 100644 index 000000000..932f34984 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domCg_setparam.cpp @@ -0,0 +1,106 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domCg_setparam::create(DAE& dae) +{ + domCg_setparamRef ref = new domCg_setparam(dae); + return ref; +} + + +daeMetaElement * +domCg_setparam::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "cg_setparam" ); + meta->registerClass(domCg_setparam::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "cg_param_type" ); + mea->setOffset( daeOffsetOf(domCg_setparam,elemCg_param_type) ); + mea->setElementType( domCg_param_type::registerElement(dae) ); + cm->appendChild( new daeMetaGroup( mea, meta, cm, 0, 1, 1 ) ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "usertype" ); + mea->setOffset( daeOffsetOf(domCg_setparam,elemUsertype) ); + mea->setElementType( domCg_setuser_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "array" ); + mea->setOffset( daeOffsetOf(domCg_setparam,elemArray) ); + mea->setElementType( domCg_setarray_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "connect_param" ); + mea->setOffset( daeOffsetOf(domCg_setparam,elemConnect_param) ); + mea->setElementType( domCg_connect_param::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domCg_setparam,_contents)); + meta->addContentsOrder(daeOffsetOf(domCg_setparam,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domCg_setparam,_CMData), 1); + // Add attribute: ref + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "ref" ); + ma->setType( dae.getAtomicTypes().get("Cg_identifier")); + ma->setOffset( daeOffsetOf( domCg_setparam , attrRef )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: program + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "program" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domCg_setparam , attrProgram )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_setparam)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domCg_setparam_simple.cpp b/Engine/lib/collada/src/1.4/dom/domCg_setparam_simple.cpp new file mode 100644 index 000000000..6fd485216 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domCg_setparam_simple.cpp @@ -0,0 +1,79 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domCg_setparam_simple::create(DAE& dae) +{ + domCg_setparam_simpleRef ref = new domCg_setparam_simple(dae); + return ref; +} + + +daeMetaElement * +domCg_setparam_simple::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "cg_setparam_simple" ); + meta->registerClass(domCg_setparam_simple::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "annotate" ); + mea->setOffset( daeOffsetOf(domCg_setparam_simple,elemAnnotate_array) ); + mea->setElementType( domFx_annotate_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "cg_param_type" ); + mea->setOffset( daeOffsetOf(domCg_setparam_simple,elemCg_param_type) ); + mea->setElementType( domCg_param_type::registerElement(dae) ); + cm->appendChild( new daeMetaGroup( mea, meta, cm, 1, 1, 1 ) ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + // Add attribute: ref + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "ref" ); + ma->setType( dae.getAtomicTypes().get("Cg_identifier")); + ma->setOffset( daeOffsetOf( domCg_setparam_simple , attrRef )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_setparam_simple)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domCg_setuser_type.cpp b/Engine/lib/collada/src/1.4/dom/domCg_setuser_type.cpp new file mode 100644 index 000000000..c716d0b8a --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domCg_setuser_type.cpp @@ -0,0 +1,119 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domCg_setuser_type::create(DAE& dae) +{ + domCg_setuser_typeRef ref = new domCg_setuser_type(dae); + return ref; +} + + +daeMetaElement * +domCg_setuser_type::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "cg_setuser_type" ); + meta->registerClass(domCg_setuser_type::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 0, 1 ); + + cm = new daeMetaChoice( meta, cm, 1, 0, 1, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "cg_param_type" ); + mea->setOffset( daeOffsetOf(domCg_setuser_type,elemCg_param_type_array) ); + mea->setElementType( domCg_param_type::registerElement(dae) ); + cm->appendChild( new daeMetaGroup( mea, meta, cm, 0, 1, 1 ) ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "array" ); + mea->setOffset( daeOffsetOf(domCg_setuser_type,elemArray_array) ); + mea->setElementType( domCg_setarray_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "usertype" ); + mea->setOffset( daeOffsetOf(domCg_setuser_type,elemUsertype_array) ); + mea->setElementType( domCg_setuser_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "connect_param" ); + mea->setOffset( daeOffsetOf(domCg_setuser_type,elemConnect_param_array) ); + mea->setElementType( domCg_connect_param::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3001, 1, -1 ); + mea->setName( "setparam" ); + mea->setOffset( daeOffsetOf(domCg_setuser_type,elemSetparam_array) ); + mea->setElementType( domCg_setparam::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domCg_setuser_type,_contents)); + meta->addContentsOrder(daeOffsetOf(domCg_setuser_type,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domCg_setuser_type,_CMData), 2); + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("Cg_identifier")); + ma->setOffset( daeOffsetOf( domCg_setuser_type , attrName )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: source + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "source" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domCg_setuser_type , attrSource )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_setuser_type)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domCg_surface_type.cpp b/Engine/lib/collada/src/1.4/dom/domCg_surface_type.cpp new file mode 100644 index 000000000..e20b8242c --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domCg_surface_type.cpp @@ -0,0 +1,264 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domCg_surface_type::create(DAE& dae) +{ + domCg_surface_typeRef ref = new domCg_surface_type(dae); + return ref; +} + + +daeMetaElement * +domCg_surface_type::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "cg_surface_type" ); + meta->registerClass(domCg_surface_type::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "fx_surface_init_common" ); + mea->setOffset( daeOffsetOf(domCg_surface_type,elemFx_surface_init_common) ); + mea->setElementType( domFx_surface_init_common::registerElement(dae) ); + cm->appendChild( new daeMetaGroup( mea, meta, cm, 0, 0, 1 ) ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "format" ); + mea->setOffset( daeOffsetOf(domCg_surface_type,elemFormat) ); + mea->setElementType( domFormat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "format_hint" ); + mea->setOffset( daeOffsetOf(domCg_surface_type,elemFormat_hint) ); + mea->setElementType( domFx_surface_format_hint_common::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 3, 0, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "size" ); + mea->setOffset( daeOffsetOf(domCg_surface_type,elemSize) ); + mea->setElementType( domSize::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "viewport_ratio" ); + mea->setOffset( daeOffsetOf(domCg_surface_type,elemViewport_ratio) ); + mea->setElementType( domViewport_ratio::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "mip_levels" ); + mea->setOffset( daeOffsetOf(domCg_surface_type,elemMip_levels) ); + mea->setElementType( domMip_levels::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 5, 0, 1 ); + mea->setName( "mipmap_generate" ); + mea->setOffset( daeOffsetOf(domCg_surface_type,elemMipmap_generate) ); + mea->setElementType( domMipmap_generate::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 6, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domCg_surface_type,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 6 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm = new daeMetaSequence( meta, cm, 7, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "generator" ); + mea->setOffset( daeOffsetOf(domCg_surface_type,elemGenerator) ); + mea->setElementType( domGenerator::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm->setMaxOrdinal( 7 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domCg_surface_type,_contents)); + meta->addContentsOrder(daeOffsetOf(domCg_surface_type,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domCg_surface_type,_CMData), 1); + // Add attribute: type + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "type" ); + ma->setType( dae.getAtomicTypes().get("Fx_surface_type_enum")); + ma->setOffset( daeOffsetOf( domCg_surface_type , attrType )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_surface_type)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_surface_type::domGenerator::create(DAE& dae) +{ + domCg_surface_type::domGeneratorRef ref = new domCg_surface_type::domGenerator(dae); + return ref; +} + + +daeMetaElement * +domCg_surface_type::domGenerator::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "generator" ); + meta->registerClass(domCg_surface_type::domGenerator::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "annotate" ); + mea->setOffset( daeOffsetOf(domCg_surface_type::domGenerator,elemAnnotate_array) ); + mea->setElementType( domFx_annotate_common::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 1, 1, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "code" ); + mea->setOffset( daeOffsetOf(domCg_surface_type::domGenerator,elemCode_array) ); + mea->setElementType( domFx_code_profile::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "include" ); + mea->setOffset( daeOffsetOf(domCg_surface_type::domGenerator,elemInclude_array) ); + mea->setElementType( domFx_include_common::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementAttribute( meta, cm, 3002, 1, 1 ); + mea->setName( "name" ); + mea->setOffset( daeOffsetOf(domCg_surface_type::domGenerator,elemName) ); + mea->setElementType( domCg_surface_type::domGenerator::domName::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3003, 0, -1 ); + mea->setName( "setparam" ); + mea->setOffset( daeOffsetOf(domCg_surface_type::domGenerator,elemSetparam_array) ); + mea->setElementType( domCg_setparam_simple::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3003 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domCg_surface_type::domGenerator,_contents)); + meta->addContentsOrder(daeOffsetOf(domCg_surface_type::domGenerator,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domCg_surface_type::domGenerator,_CMData), 1); + meta->setElementSize(sizeof(domCg_surface_type::domGenerator)); + meta->validate(); + + return meta; +} + +daeElementRef +domCg_surface_type::domGenerator::domName::create(DAE& dae) +{ + domCg_surface_type::domGenerator::domNameRef ref = new domCg_surface_type::domGenerator::domName(dae); + return ref; +} + + +daeMetaElement * +domCg_surface_type::domGenerator::domName::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "name" ); + meta->registerClass(domCg_surface_type::domGenerator::domName::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domCg_surface_type::domGenerator::domName , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: source + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "source" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domCg_surface_type::domGenerator::domName , attrSource )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCg_surface_type::domGenerator::domName)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domChannel.cpp b/Engine/lib/collada/src/1.4/dom/domChannel.cpp new file mode 100644 index 000000000..c13704cfe --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domChannel.cpp @@ -0,0 +1,73 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domChannel::create(DAE& dae) +{ + domChannelRef ref = new domChannel(dae); + return ref; +} + + +daeMetaElement * +domChannel::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "channel" ); + meta->registerClass(domChannel::create); + + + // Add attribute: source + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "source" ); + ma->setType( dae.getAtomicTypes().get("URIFragmentType")); + ma->setOffset( daeOffsetOf( domChannel , attrSource )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: target + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "target" ); + ma->setType( dae.getAtomicTypes().get("xsToken")); + ma->setOffset( daeOffsetOf( domChannel , attrTarget )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domChannel)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domCommon_color_or_texture_type.cpp b/Engine/lib/collada/src/1.4/dom/domCommon_color_or_texture_type.cpp new file mode 100644 index 000000000..98dfd5047 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domCommon_color_or_texture_type.cpp @@ -0,0 +1,226 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domCommon_color_or_texture_type::create(DAE& dae) +{ + domCommon_color_or_texture_typeRef ref = new domCommon_color_or_texture_type(dae); + return ref; +} + + +daeMetaElement * +domCommon_color_or_texture_type::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "common_color_or_texture_type" ); + meta->registerClass(domCommon_color_or_texture_type::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "color" ); + mea->setOffset( daeOffsetOf(domCommon_color_or_texture_type,elemColor) ); + mea->setElementType( domCommon_color_or_texture_type::domColor::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "param" ); + mea->setOffset( daeOffsetOf(domCommon_color_or_texture_type,elemParam) ); + mea->setElementType( domCommon_color_or_texture_type::domParam::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "texture" ); + mea->setOffset( daeOffsetOf(domCommon_color_or_texture_type,elemTexture) ); + mea->setElementType( domCommon_color_or_texture_type::domTexture::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domCommon_color_or_texture_type,_contents)); + meta->addContentsOrder(daeOffsetOf(domCommon_color_or_texture_type,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domCommon_color_or_texture_type,_CMData), 1); + meta->setElementSize(sizeof(domCommon_color_or_texture_type)); + meta->validate(); + + return meta; +} + +daeElementRef +domCommon_color_or_texture_type::domColor::create(DAE& dae) +{ + domCommon_color_or_texture_type::domColorRef ref = new domCommon_color_or_texture_type::domColor(dae); + return ref; +} + + +daeMetaElement * +domCommon_color_or_texture_type::domColor::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "color" ); + meta->registerClass(domCommon_color_or_texture_type::domColor::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_color_common")); + ma->setOffset( daeOffsetOf( domCommon_color_or_texture_type::domColor , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domCommon_color_or_texture_type::domColor , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCommon_color_or_texture_type::domColor)); + meta->validate(); + + return meta; +} + +daeElementRef +domCommon_color_or_texture_type::domParam::create(DAE& dae) +{ + domCommon_color_or_texture_type::domParamRef ref = new domCommon_color_or_texture_type::domParam(dae); + return ref; +} + + +daeMetaElement * +domCommon_color_or_texture_type::domParam::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "param" ); + meta->registerClass(domCommon_color_or_texture_type::domParam::create); + + meta->setIsInnerClass( true ); + + // Add attribute: ref + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "ref" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domCommon_color_or_texture_type::domParam , attrRef )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCommon_color_or_texture_type::domParam)); + meta->validate(); + + return meta; +} + +daeElementRef +domCommon_color_or_texture_type::domTexture::create(DAE& dae) +{ + domCommon_color_or_texture_type::domTextureRef ref = new domCommon_color_or_texture_type::domTexture(dae); + return ref; +} + + +daeMetaElement * +domCommon_color_or_texture_type::domTexture::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "texture" ); + meta->registerClass(domCommon_color_or_texture_type::domTexture::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domCommon_color_or_texture_type::domTexture,elemExtra) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + + // Add attribute: texture + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "texture" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domCommon_color_or_texture_type::domTexture , attrTexture )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: texcoord + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "texcoord" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domCommon_color_or_texture_type::domTexture , attrTexcoord )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCommon_color_or_texture_type::domTexture)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domCommon_float_or_param_type.cpp b/Engine/lib/collada/src/1.4/dom/domCommon_float_or_param_type.cpp new file mode 100644 index 000000000..1aa28e0fb --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domCommon_float_or_param_type.cpp @@ -0,0 +1,157 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domCommon_float_or_param_type::create(DAE& dae) +{ + domCommon_float_or_param_typeRef ref = new domCommon_float_or_param_type(dae); + return ref; +} + + +daeMetaElement * +domCommon_float_or_param_type::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "common_float_or_param_type" ); + meta->registerClass(domCommon_float_or_param_type::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float" ); + mea->setOffset( daeOffsetOf(domCommon_float_or_param_type,elemFloat) ); + mea->setElementType( domCommon_float_or_param_type::domFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "param" ); + mea->setOffset( daeOffsetOf(domCommon_float_or_param_type,elemParam) ); + mea->setElementType( domCommon_float_or_param_type::domParam::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domCommon_float_or_param_type,_contents)); + meta->addContentsOrder(daeOffsetOf(domCommon_float_or_param_type,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domCommon_float_or_param_type,_CMData), 1); + meta->setElementSize(sizeof(domCommon_float_or_param_type)); + meta->validate(); + + return meta; +} + +daeElementRef +domCommon_float_or_param_type::domFloat::create(DAE& dae) +{ + domCommon_float_or_param_type::domFloatRef ref = new domCommon_float_or_param_type::domFloat(dae); + return ref; +} + + +daeMetaElement * +domCommon_float_or_param_type::domFloat::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float" ); + meta->registerClass(domCommon_float_or_param_type::domFloat::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domCommon_float_or_param_type::domFloat , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domCommon_float_or_param_type::domFloat , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCommon_float_or_param_type::domFloat)); + meta->validate(); + + return meta; +} + +daeElementRef +domCommon_float_or_param_type::domParam::create(DAE& dae) +{ + domCommon_float_or_param_type::domParamRef ref = new domCommon_float_or_param_type::domParam(dae); + return ref; +} + + +daeMetaElement * +domCommon_float_or_param_type::domParam::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "param" ); + meta->registerClass(domCommon_float_or_param_type::domParam::create); + + meta->setIsInnerClass( true ); + + // Add attribute: ref + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "ref" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domCommon_float_or_param_type::domParam , attrRef )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCommon_float_or_param_type::domParam)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domCommon_newparam_type.cpp b/Engine/lib/collada/src/1.4/dom/domCommon_newparam_type.cpp new file mode 100644 index 000000000..7ae313fa8 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domCommon_newparam_type.cpp @@ -0,0 +1,299 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domCommon_newparam_type::create(DAE& dae) +{ + domCommon_newparam_typeRef ref = new domCommon_newparam_type(dae); + return ref; +} + + +daeMetaElement * +domCommon_newparam_type::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "common_newparam_type" ); + meta->registerClass(domCommon_newparam_type::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "semantic" ); + mea->setOffset( daeOffsetOf(domCommon_newparam_type,elemSemantic) ); + mea->setElementType( domCommon_newparam_type::domSemantic::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 1, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float" ); + mea->setOffset( daeOffsetOf(domCommon_newparam_type,elemFloat) ); + mea->setElementType( domCommon_newparam_type::domFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float2" ); + mea->setOffset( daeOffsetOf(domCommon_newparam_type,elemFloat2) ); + mea->setElementType( domCommon_newparam_type::domFloat2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float3" ); + mea->setOffset( daeOffsetOf(domCommon_newparam_type,elemFloat3) ); + mea->setElementType( domCommon_newparam_type::domFloat3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float4" ); + mea->setOffset( daeOffsetOf(domCommon_newparam_type,elemFloat4) ); + mea->setElementType( domCommon_newparam_type::domFloat4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "surface" ); + mea->setOffset( daeOffsetOf(domCommon_newparam_type,elemSurface) ); + mea->setElementType( domFx_surface_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "sampler2D" ); + mea->setOffset( daeOffsetOf(domCommon_newparam_type,elemSampler2D) ); + mea->setElementType( domFx_sampler2D_common::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domCommon_newparam_type,_contents)); + meta->addContentsOrder(daeOffsetOf(domCommon_newparam_type,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domCommon_newparam_type,_CMData), 1); + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domCommon_newparam_type , attrSid )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCommon_newparam_type)); + meta->validate(); + + return meta; +} + +daeElementRef +domCommon_newparam_type::domSemantic::create(DAE& dae) +{ + domCommon_newparam_type::domSemanticRef ref = new domCommon_newparam_type::domSemantic(dae); + return ref; +} + + +daeMetaElement * +domCommon_newparam_type::domSemantic::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "semantic" ); + meta->registerClass(domCommon_newparam_type::domSemantic::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domCommon_newparam_type::domSemantic , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCommon_newparam_type::domSemantic)); + meta->validate(); + + return meta; +} + +daeElementRef +domCommon_newparam_type::domFloat::create(DAE& dae) +{ + domCommon_newparam_type::domFloatRef ref = new domCommon_newparam_type::domFloat(dae); + return ref; +} + + +daeMetaElement * +domCommon_newparam_type::domFloat::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float" ); + meta->registerClass(domCommon_newparam_type::domFloat::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domCommon_newparam_type::domFloat , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCommon_newparam_type::domFloat)); + meta->validate(); + + return meta; +} + +daeElementRef +domCommon_newparam_type::domFloat2::create(DAE& dae) +{ + domCommon_newparam_type::domFloat2Ref ref = new domCommon_newparam_type::domFloat2(dae); + return ref; +} + + +daeMetaElement * +domCommon_newparam_type::domFloat2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float2" ); + meta->registerClass(domCommon_newparam_type::domFloat2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float2")); + ma->setOffset( daeOffsetOf( domCommon_newparam_type::domFloat2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCommon_newparam_type::domFloat2)); + meta->validate(); + + return meta; +} + +daeElementRef +domCommon_newparam_type::domFloat3::create(DAE& dae) +{ + domCommon_newparam_type::domFloat3Ref ref = new domCommon_newparam_type::domFloat3(dae); + return ref; +} + + +daeMetaElement * +domCommon_newparam_type::domFloat3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float3" ); + meta->registerClass(domCommon_newparam_type::domFloat3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float3")); + ma->setOffset( daeOffsetOf( domCommon_newparam_type::domFloat3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCommon_newparam_type::domFloat3)); + meta->validate(); + + return meta; +} + +daeElementRef +domCommon_newparam_type::domFloat4::create(DAE& dae) +{ + domCommon_newparam_type::domFloat4Ref ref = new domCommon_newparam_type::domFloat4(dae); + return ref; +} + + +daeMetaElement * +domCommon_newparam_type::domFloat4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float4" ); + meta->registerClass(domCommon_newparam_type::domFloat4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domCommon_newparam_type::domFloat4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCommon_newparam_type::domFloat4)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domCommon_transparent_type.cpp b/Engine/lib/collada/src/1.4/dom/domCommon_transparent_type.cpp new file mode 100644 index 000000000..74e0b06f7 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domCommon_transparent_type.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domCommon_transparent_type::create(DAE& dae) +{ + domCommon_transparent_typeRef ref = new domCommon_transparent_type(dae); + return ref; +} + + +daeMetaElement * +domCommon_transparent_type::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "common_transparent_type" ); + meta->registerClass(domCommon_transparent_type::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + cm = new daeMetaChoice( meta, cm, 0, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "color" ); + mea->setOffset( daeOffsetOf(domCommon_transparent_type,elemColor) ); + mea->setElementType( domColor::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "param" ); + mea->setOffset( daeOffsetOf(domCommon_transparent_type,elemParam) ); + mea->setElementType( domParam::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "texture" ); + mea->setOffset( daeOffsetOf(domCommon_transparent_type,elemTexture) ); + mea->setElementType( domTexture::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domCommon_transparent_type,_contents)); + meta->addContentsOrder(daeOffsetOf(domCommon_transparent_type,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domCommon_transparent_type,_CMData), 1); + // Add attribute: opaque + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "opaque" ); + ma->setType( dae.getAtomicTypes().get("Fx_opaque_enum")); + ma->setOffset( daeOffsetOf( domCommon_transparent_type , attrOpaque )); + ma->setContainer( meta ); + ma->setDefaultString( "A_ONE"); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCommon_transparent_type)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domConstants.cpp b/Engine/lib/collada/src/1.4/dom/domConstants.cpp new file mode 100644 index 000000000..7c1444610 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domConstants.cpp @@ -0,0 +1,1088 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include + +DLLSPEC daeString COLLADA_VERSION = "1.4.1"; +DLLSPEC daeString COLLADA_NAMESPACE = "http://www.collada.org/2005/11/COLLADASchema"; + +DLLSPEC daeString COMMON_PROFILE_INPUT_BINORMAL = "BINORMAL"; +DLLSPEC daeString COMMON_PROFILE_INPUT_COLOR = "COLOR"; +DLLSPEC daeString COMMON_PROFILE_INPUT_CONTINUITY = "CONTINUITY"; +DLLSPEC daeString COMMON_PROFILE_INPUT_IMAGE = "IMAGE"; +DLLSPEC daeString COMMON_PROFILE_INPUT_IN_TANGENT = "IN_TANGENT"; +DLLSPEC daeString COMMON_PROFILE_INPUT_INPUT = "INPUT"; +DLLSPEC daeString COMMON_PROFILE_INPUT_INTERPOLATION = "INTERPOLATION"; +DLLSPEC daeString COMMON_PROFILE_INPUT_INV_BIND_MATRIX = "INV_BIND_MATRIX"; +DLLSPEC daeString COMMON_PROFILE_INPUT_JOINT = "JOINT"; +DLLSPEC daeString COMMON_PROFILE_INPUT_LINEAR_STEPS = "LINEAR_STEPS"; +DLLSPEC daeString COMMON_PROFILE_INPUT_MORPH_TARGET = "MORPH_TARGET"; +DLLSPEC daeString COMMON_PROFILE_INPUT_MORPH_WEIGHT = "MORPH_WEIGHT"; +DLLSPEC daeString COMMON_PROFILE_INPUT_NORMAL = "NORMAL"; +DLLSPEC daeString COMMON_PROFILE_INPUT_OUTPUT = "OUTPUT"; +DLLSPEC daeString COMMON_PROFILE_INPUT_OUT_TANGENT = "OUT_TANGENT"; +DLLSPEC daeString COMMON_PROFILE_INPUT_POSITION = "POSITION"; +DLLSPEC daeString COMMON_PROFILE_INPUT_TANGENT = "TANGENT"; +DLLSPEC daeString COMMON_PROFILE_INPUT_TEXBINORMAL = "TEXBINORMAL"; +DLLSPEC daeString COMMON_PROFILE_INPUT_TEXCOORD = "TEXCOORD"; +DLLSPEC daeString COMMON_PROFILE_INPUT_TEXTANGENT = "TEXTANGENT"; +DLLSPEC daeString COMMON_PROFILE_INPUT_UV = "UV"; +DLLSPEC daeString COMMON_PROFILE_INPUT_VERTEX = "VERTEX"; +DLLSPEC daeString COMMON_PROFILE_INPUT_WEIGHT = "WEIGHT"; + +DLLSPEC daeString COMMON_PROFILE_PARAM_A = "A"; +DLLSPEC daeString COMMON_PROFILE_PARAM_ANGLE = "ANGLE"; +DLLSPEC daeString COMMON_PROFILE_PARAM_B = "B"; +DLLSPEC daeString COMMON_PROFILE_PARAM_DOUBLE_SIDED = "DOUBLE_SIDED"; +DLLSPEC daeString COMMON_PROFILE_PARAM_G = "G"; +DLLSPEC daeString COMMON_PROFILE_PARAM_P = "P"; +DLLSPEC daeString COMMON_PROFILE_PARAM_Q = "Q"; +DLLSPEC daeString COMMON_PROFILE_PARAM_R = "R"; +DLLSPEC daeString COMMON_PROFILE_PARAM_S = "S"; +DLLSPEC daeString COMMON_PROFILE_PARAM_T = "T"; +DLLSPEC daeString COMMON_PROFILE_PARAM_TIME = "TIME"; +DLLSPEC daeString COMMON_PROFILE_PARAM_U = "U"; +DLLSPEC daeString COMMON_PROFILE_PARAM_V = "V"; +DLLSPEC daeString COMMON_PROFILE_PARAM_W = "W"; +DLLSPEC daeString COMMON_PROFILE_PARAM_X = "X"; +DLLSPEC daeString COMMON_PROFILE_PARAM_Y = "Y"; +DLLSPEC daeString COMMON_PROFILE_PARAM_Z = "Z"; + + +DLLSPEC daeString COLLADA_TYPE_INPUTGLOBAL = "InputGlobal"; +DLLSPEC daeString COLLADA_TYPE_INPUTLOCAL = "InputLocal"; +DLLSPEC daeString COLLADA_TYPE_INPUTLOCALOFFSET = "InputLocalOffset"; +DLLSPEC daeString COLLADA_TYPE_INSTANCEWITHEXTRA = "InstanceWithExtra"; +DLLSPEC daeString COLLADA_TYPE_TARGETABLEFLOAT = "TargetableFloat"; +DLLSPEC daeString COLLADA_TYPE_TARGETABLEFLOAT3 = "TargetableFloat3"; +DLLSPEC daeString COLLADA_TYPE_FX_SURFACE_FORMAT_HINT_COMMON = "fx_surface_format_hint_common"; +DLLSPEC daeString COLLADA_TYPE_CHANNELS = "channels"; +DLLSPEC daeString COLLADA_TYPE_RANGE = "range"; +DLLSPEC daeString COLLADA_TYPE_PRECISION = "precision"; +DLLSPEC daeString COLLADA_TYPE_OPTION = "option"; +DLLSPEC daeString COLLADA_TYPE_FX_SURFACE_INIT_PLANAR_COMMON = "fx_surface_init_planar_common"; +DLLSPEC daeString COLLADA_TYPE_ALL = "all"; +DLLSPEC daeString COLLADA_TYPE_FX_SURFACE_INIT_VOLUME_COMMON = "fx_surface_init_volume_common"; +DLLSPEC daeString COLLADA_TYPE_PRIMARY = "primary"; +DLLSPEC daeString COLLADA_TYPE_FX_SURFACE_INIT_CUBE_COMMON = "fx_surface_init_cube_common"; +DLLSPEC daeString COLLADA_TYPE_ORDER = "order"; +DLLSPEC daeString COLLADA_TYPE_FACE = "face"; +DLLSPEC daeString COLLADA_TYPE_FX_SURFACE_INIT_FROM_COMMON = "fx_surface_init_from_common"; +DLLSPEC daeString COLLADA_TYPE_FX_SURFACE_COMMON = "fx_surface_common"; +DLLSPEC daeString COLLADA_TYPE_FORMAT = "format"; +DLLSPEC daeString COLLADA_TYPE_SIZE = "size"; +DLLSPEC daeString COLLADA_TYPE_VIEWPORT_RATIO = "viewport_ratio"; +DLLSPEC daeString COLLADA_TYPE_MIP_LEVELS = "mip_levels"; +DLLSPEC daeString COLLADA_TYPE_MIPMAP_GENERATE = "mipmap_generate"; +DLLSPEC daeString COLLADA_TYPE_FX_SAMPLER1D_COMMON = "fx_sampler1D_common"; +DLLSPEC daeString COLLADA_TYPE_SOURCE = "source"; +DLLSPEC daeString COLLADA_TYPE_WRAP_S = "wrap_s"; +DLLSPEC daeString COLLADA_TYPE_MINFILTER = "minfilter"; +DLLSPEC daeString COLLADA_TYPE_MAGFILTER = "magfilter"; +DLLSPEC daeString COLLADA_TYPE_MIPFILTER = "mipfilter"; +DLLSPEC daeString COLLADA_TYPE_BORDER_COLOR = "border_color"; +DLLSPEC daeString COLLADA_TYPE_MIPMAP_MAXLEVEL = "mipmap_maxlevel"; +DLLSPEC daeString COLLADA_TYPE_MIPMAP_BIAS = "mipmap_bias"; +DLLSPEC daeString COLLADA_TYPE_FX_SAMPLER2D_COMMON = "fx_sampler2D_common"; +DLLSPEC daeString COLLADA_TYPE_WRAP_T = "wrap_t"; +DLLSPEC daeString COLLADA_TYPE_FX_SAMPLER3D_COMMON = "fx_sampler3D_common"; +DLLSPEC daeString COLLADA_TYPE_WRAP_P = "wrap_p"; +DLLSPEC daeString COLLADA_TYPE_FX_SAMPLERCUBE_COMMON = "fx_samplerCUBE_common"; +DLLSPEC daeString COLLADA_TYPE_FX_SAMPLERRECT_COMMON = "fx_samplerRECT_common"; +DLLSPEC daeString COLLADA_TYPE_FX_SAMPLERDEPTH_COMMON = "fx_samplerDEPTH_common"; +DLLSPEC daeString COLLADA_TYPE_FX_COLORTARGET_COMMON = "fx_colortarget_common"; +DLLSPEC daeString COLLADA_TYPE_FX_DEPTHTARGET_COMMON = "fx_depthtarget_common"; +DLLSPEC daeString COLLADA_TYPE_FX_STENCILTARGET_COMMON = "fx_stenciltarget_common"; +DLLSPEC daeString COLLADA_TYPE_FX_CLEARCOLOR_COMMON = "fx_clearcolor_common"; +DLLSPEC daeString COLLADA_TYPE_FX_CLEARDEPTH_COMMON = "fx_cleardepth_common"; +DLLSPEC daeString COLLADA_TYPE_FX_CLEARSTENCIL_COMMON = "fx_clearstencil_common"; +DLLSPEC daeString COLLADA_TYPE_FX_ANNOTATE_COMMON = "fx_annotate_common"; +DLLSPEC daeString COLLADA_TYPE_FX_INCLUDE_COMMON = "fx_include_common"; +DLLSPEC daeString COLLADA_TYPE_FX_NEWPARAM_COMMON = "fx_newparam_common"; +DLLSPEC daeString COLLADA_TYPE_SEMANTIC = "semantic"; +DLLSPEC daeString COLLADA_TYPE_MODIFIER = "modifier"; +DLLSPEC daeString COLLADA_TYPE_FX_CODE_PROFILE = "fx_code_profile"; +DLLSPEC daeString COLLADA_TYPE_GL_SAMPLER1D = "gl_sampler1D"; +DLLSPEC daeString COLLADA_TYPE_GL_SAMPLER2D = "gl_sampler2D"; +DLLSPEC daeString COLLADA_TYPE_GL_SAMPLER3D = "gl_sampler3D"; +DLLSPEC daeString COLLADA_TYPE_GL_SAMPLERCUBE = "gl_samplerCUBE"; +DLLSPEC daeString COLLADA_TYPE_GL_SAMPLERRECT = "gl_samplerRECT"; +DLLSPEC daeString COLLADA_TYPE_GL_SAMPLERDEPTH = "gl_samplerDEPTH"; +DLLSPEC daeString COLLADA_TYPE_GLSL_NEWARRAY_TYPE = "glsl_newarray_type"; +DLLSPEC daeString COLLADA_TYPE_GLSL_SETARRAY_TYPE = "glsl_setarray_type"; +DLLSPEC daeString COLLADA_TYPE_GLSL_SURFACE_TYPE = "glsl_surface_type"; +DLLSPEC daeString COLLADA_TYPE_GENERATOR = "generator"; +DLLSPEC daeString COLLADA_TYPE_NAME = "name"; +DLLSPEC daeString COLLADA_TYPE_GLSL_NEWPARAM = "glsl_newparam"; +DLLSPEC daeString COLLADA_TYPE_GLSL_SETPARAM_SIMPLE = "glsl_setparam_simple"; +DLLSPEC daeString COLLADA_TYPE_GLSL_SETPARAM = "glsl_setparam"; +DLLSPEC daeString COLLADA_TYPE_COMMON_FLOAT_OR_PARAM_TYPE = "common_float_or_param_type"; +DLLSPEC daeString COLLADA_TYPE_FLOAT = "float"; +DLLSPEC daeString COLLADA_TYPE_PARAM = "param"; +DLLSPEC daeString COLLADA_TYPE_COMMON_COLOR_OR_TEXTURE_TYPE = "common_color_or_texture_type"; +DLLSPEC daeString COLLADA_TYPE_COLOR = "color"; +DLLSPEC daeString COLLADA_TYPE_TEXTURE = "texture"; +DLLSPEC daeString COLLADA_TYPE_COMMON_TRANSPARENT_TYPE = "common_transparent_type"; +DLLSPEC daeString COLLADA_TYPE_COMMON_NEWPARAM_TYPE = "common_newparam_type"; +DLLSPEC daeString COLLADA_TYPE_FLOAT2 = "float2"; +DLLSPEC daeString COLLADA_TYPE_FLOAT3 = "float3"; +DLLSPEC daeString COLLADA_TYPE_FLOAT4 = "float4"; +DLLSPEC daeString COLLADA_TYPE_CG_SAMPLER1D = "cg_sampler1D"; +DLLSPEC daeString COLLADA_TYPE_CG_SAMPLER2D = "cg_sampler2D"; +DLLSPEC daeString COLLADA_TYPE_CG_SAMPLER3D = "cg_sampler3D"; +DLLSPEC daeString COLLADA_TYPE_CG_SAMPLERCUBE = "cg_samplerCUBE"; +DLLSPEC daeString COLLADA_TYPE_CG_SAMPLERRECT = "cg_samplerRECT"; +DLLSPEC daeString COLLADA_TYPE_CG_SAMPLERDEPTH = "cg_samplerDEPTH"; +DLLSPEC daeString COLLADA_TYPE_CG_CONNECT_PARAM = "cg_connect_param"; +DLLSPEC daeString COLLADA_TYPE_CG_NEWARRAY_TYPE = "cg_newarray_type"; +DLLSPEC daeString COLLADA_TYPE_CG_SETARRAY_TYPE = "cg_setarray_type"; +DLLSPEC daeString COLLADA_TYPE_CG_SETUSER_TYPE = "cg_setuser_type"; +DLLSPEC daeString COLLADA_TYPE_CG_SURFACE_TYPE = "cg_surface_type"; +DLLSPEC daeString COLLADA_TYPE_CG_NEWPARAM = "cg_newparam"; +DLLSPEC daeString COLLADA_TYPE_CG_SETPARAM_SIMPLE = "cg_setparam_simple"; +DLLSPEC daeString COLLADA_TYPE_CG_SETPARAM = "cg_setparam"; +DLLSPEC daeString COLLADA_TYPE_GLES_TEXTURE_CONSTANT_TYPE = "gles_texture_constant_type"; +DLLSPEC daeString COLLADA_TYPE_GLES_TEXENV_COMMAND_TYPE = "gles_texenv_command_type"; +DLLSPEC daeString COLLADA_TYPE_GLES_TEXCOMBINER_ARGUMENTRGB_TYPE = "gles_texcombiner_argumentRGB_type"; +DLLSPEC daeString COLLADA_TYPE_GLES_TEXCOMBINER_ARGUMENTALPHA_TYPE = "gles_texcombiner_argumentAlpha_type"; +DLLSPEC daeString COLLADA_TYPE_GLES_TEXCOMBINER_COMMANDRGB_TYPE = "gles_texcombiner_commandRGB_type"; +DLLSPEC daeString COLLADA_TYPE_GLES_TEXCOMBINER_COMMANDALPHA_TYPE = "gles_texcombiner_commandAlpha_type"; +DLLSPEC daeString COLLADA_TYPE_GLES_TEXCOMBINER_COMMAND_TYPE = "gles_texcombiner_command_type"; +DLLSPEC daeString COLLADA_TYPE_GLES_TEXTURE_PIPELINE = "gles_texture_pipeline"; +DLLSPEC daeString COLLADA_TYPE_GLES_TEXTURE_UNIT = "gles_texture_unit"; +DLLSPEC daeString COLLADA_TYPE_SURFACE = "surface"; +DLLSPEC daeString COLLADA_TYPE_SAMPLER_STATE = "sampler_state"; +DLLSPEC daeString COLLADA_TYPE_TEXCOORD = "texcoord"; +DLLSPEC daeString COLLADA_TYPE_GLES_SAMPLER_STATE = "gles_sampler_state"; +DLLSPEC daeString COLLADA_TYPE_GLES_NEWPARAM = "gles_newparam"; +DLLSPEC daeString COLLADA_TYPE_FX_SURFACE_INIT_COMMON = "fx_surface_init_common"; +DLLSPEC daeString COLLADA_TYPE_INIT_AS_NULL = "init_as_null"; +DLLSPEC daeString COLLADA_TYPE_INIT_AS_TARGET = "init_as_target"; +DLLSPEC daeString COLLADA_TYPE_FX_ANNOTATE_TYPE_COMMON = "fx_annotate_type_common"; +DLLSPEC daeString COLLADA_TYPE_BOOL = "bool"; +DLLSPEC daeString COLLADA_TYPE_BOOL2 = "bool2"; +DLLSPEC daeString COLLADA_TYPE_BOOL3 = "bool3"; +DLLSPEC daeString COLLADA_TYPE_BOOL4 = "bool4"; +DLLSPEC daeString COLLADA_TYPE_INT = "int"; +DLLSPEC daeString COLLADA_TYPE_INT2 = "int2"; +DLLSPEC daeString COLLADA_TYPE_INT3 = "int3"; +DLLSPEC daeString COLLADA_TYPE_INT4 = "int4"; +DLLSPEC daeString COLLADA_TYPE_FLOAT2X2 = "float2x2"; +DLLSPEC daeString COLLADA_TYPE_FLOAT3X3 = "float3x3"; +DLLSPEC daeString COLLADA_TYPE_FLOAT4X4 = "float4x4"; +DLLSPEC daeString COLLADA_TYPE_STRING = "string"; +DLLSPEC daeString COLLADA_TYPE_FX_BASIC_TYPE_COMMON = "fx_basic_type_common"; +DLLSPEC daeString COLLADA_TYPE_FLOAT1X1 = "float1x1"; +DLLSPEC daeString COLLADA_TYPE_FLOAT1X2 = "float1x2"; +DLLSPEC daeString COLLADA_TYPE_FLOAT1X3 = "float1x3"; +DLLSPEC daeString COLLADA_TYPE_FLOAT1X4 = "float1x4"; +DLLSPEC daeString COLLADA_TYPE_FLOAT2X1 = "float2x1"; +DLLSPEC daeString COLLADA_TYPE_FLOAT2X3 = "float2x3"; +DLLSPEC daeString COLLADA_TYPE_FLOAT2X4 = "float2x4"; +DLLSPEC daeString COLLADA_TYPE_FLOAT3X1 = "float3x1"; +DLLSPEC daeString COLLADA_TYPE_FLOAT3X2 = "float3x2"; +DLLSPEC daeString COLLADA_TYPE_FLOAT3X4 = "float3x4"; +DLLSPEC daeString COLLADA_TYPE_FLOAT4X1 = "float4x1"; +DLLSPEC daeString COLLADA_TYPE_FLOAT4X2 = "float4x2"; +DLLSPEC daeString COLLADA_TYPE_FLOAT4X3 = "float4x3"; +DLLSPEC daeString COLLADA_TYPE_ENUM = "enum"; +DLLSPEC daeString COLLADA_TYPE_GL_PIPELINE_SETTINGS = "gl_pipeline_settings"; +DLLSPEC daeString COLLADA_TYPE_ALPHA_FUNC = "alpha_func"; +DLLSPEC daeString COLLADA_TYPE_FUNC = "func"; +DLLSPEC daeString COLLADA_TYPE_VALUE = "value"; +DLLSPEC daeString COLLADA_TYPE_BLEND_FUNC = "blend_func"; +DLLSPEC daeString COLLADA_TYPE_SRC = "src"; +DLLSPEC daeString COLLADA_TYPE_DEST = "dest"; +DLLSPEC daeString COLLADA_TYPE_BLEND_FUNC_SEPARATE = "blend_func_separate"; +DLLSPEC daeString COLLADA_TYPE_SRC_RGB = "src_rgb"; +DLLSPEC daeString COLLADA_TYPE_DEST_RGB = "dest_rgb"; +DLLSPEC daeString COLLADA_TYPE_SRC_ALPHA = "src_alpha"; +DLLSPEC daeString COLLADA_TYPE_DEST_ALPHA = "dest_alpha"; +DLLSPEC daeString COLLADA_TYPE_BLEND_EQUATION = "blend_equation"; +DLLSPEC daeString COLLADA_TYPE_BLEND_EQUATION_SEPARATE = "blend_equation_separate"; +DLLSPEC daeString COLLADA_TYPE_RGB = "rgb"; +DLLSPEC daeString COLLADA_TYPE_ALPHA = "alpha"; +DLLSPEC daeString COLLADA_TYPE_COLOR_MATERIAL = "color_material"; +DLLSPEC daeString COLLADA_TYPE_MODE = "mode"; +DLLSPEC daeString COLLADA_TYPE_CULL_FACE = "cull_face"; +DLLSPEC daeString COLLADA_TYPE_DEPTH_FUNC = "depth_func"; +DLLSPEC daeString COLLADA_TYPE_FOG_MODE = "fog_mode"; +DLLSPEC daeString COLLADA_TYPE_FOG_COORD_SRC = "fog_coord_src"; +DLLSPEC daeString COLLADA_TYPE_FRONT_FACE = "front_face"; +DLLSPEC daeString COLLADA_TYPE_LIGHT_MODEL_COLOR_CONTROL = "light_model_color_control"; +DLLSPEC daeString COLLADA_TYPE_LOGIC_OP = "logic_op"; +DLLSPEC daeString COLLADA_TYPE_POLYGON_MODE = "polygon_mode"; +DLLSPEC daeString COLLADA_TYPE_SHADE_MODEL = "shade_model"; +DLLSPEC daeString COLLADA_TYPE_STENCIL_FUNC = "stencil_func"; +DLLSPEC daeString COLLADA_TYPE_REF = "ref"; +DLLSPEC daeString COLLADA_TYPE_MASK = "mask"; +DLLSPEC daeString COLLADA_TYPE_STENCIL_OP = "stencil_op"; +DLLSPEC daeString COLLADA_TYPE_FAIL = "fail"; +DLLSPEC daeString COLLADA_TYPE_ZFAIL = "zfail"; +DLLSPEC daeString COLLADA_TYPE_ZPASS = "zpass"; +DLLSPEC daeString COLLADA_TYPE_STENCIL_FUNC_SEPARATE = "stencil_func_separate"; +DLLSPEC daeString COLLADA_TYPE_FRONT = "front"; +DLLSPEC daeString COLLADA_TYPE_BACK = "back"; +DLLSPEC daeString COLLADA_TYPE_STENCIL_OP_SEPARATE = "stencil_op_separate"; +DLLSPEC daeString COLLADA_TYPE_STENCIL_MASK_SEPARATE = "stencil_mask_separate"; +DLLSPEC daeString COLLADA_TYPE_LIGHT_ENABLE = "light_enable"; +DLLSPEC daeString COLLADA_TYPE_LIGHT_AMBIENT = "light_ambient"; +DLLSPEC daeString COLLADA_TYPE_LIGHT_DIFFUSE = "light_diffuse"; +DLLSPEC daeString COLLADA_TYPE_LIGHT_SPECULAR = "light_specular"; +DLLSPEC daeString COLLADA_TYPE_LIGHT_POSITION = "light_position"; +DLLSPEC daeString COLLADA_TYPE_LIGHT_CONSTANT_ATTENUATION = "light_constant_attenuation"; +DLLSPEC daeString COLLADA_TYPE_LIGHT_LINEAR_ATTENUATION = "light_linear_attenuation"; +DLLSPEC daeString COLLADA_TYPE_LIGHT_QUADRATIC_ATTENUATION = "light_quadratic_attenuation"; +DLLSPEC daeString COLLADA_TYPE_LIGHT_SPOT_CUTOFF = "light_spot_cutoff"; +DLLSPEC daeString COLLADA_TYPE_LIGHT_SPOT_DIRECTION = "light_spot_direction"; +DLLSPEC daeString COLLADA_TYPE_LIGHT_SPOT_EXPONENT = "light_spot_exponent"; +DLLSPEC daeString COLLADA_TYPE_TEXTURE1D = "texture1D"; +DLLSPEC daeString COLLADA_TYPE_TEXTURE2D = "texture2D"; +DLLSPEC daeString COLLADA_TYPE_TEXTURE3D = "texture3D"; +DLLSPEC daeString COLLADA_TYPE_TEXTURECUBE = "textureCUBE"; +DLLSPEC daeString COLLADA_TYPE_TEXTURERECT = "textureRECT"; +DLLSPEC daeString COLLADA_TYPE_TEXTUREDEPTH = "textureDEPTH"; +DLLSPEC daeString COLLADA_TYPE_TEXTURE1D_ENABLE = "texture1D_enable"; +DLLSPEC daeString COLLADA_TYPE_TEXTURE2D_ENABLE = "texture2D_enable"; +DLLSPEC daeString COLLADA_TYPE_TEXTURE3D_ENABLE = "texture3D_enable"; +DLLSPEC daeString COLLADA_TYPE_TEXTURECUBE_ENABLE = "textureCUBE_enable"; +DLLSPEC daeString COLLADA_TYPE_TEXTURERECT_ENABLE = "textureRECT_enable"; +DLLSPEC daeString COLLADA_TYPE_TEXTUREDEPTH_ENABLE = "textureDEPTH_enable"; +DLLSPEC daeString COLLADA_TYPE_TEXTURE_ENV_COLOR = "texture_env_color"; +DLLSPEC daeString COLLADA_TYPE_TEXTURE_ENV_MODE = "texture_env_mode"; +DLLSPEC daeString COLLADA_TYPE_CLIP_PLANE = "clip_plane"; +DLLSPEC daeString COLLADA_TYPE_CLIP_PLANE_ENABLE = "clip_plane_enable"; +DLLSPEC daeString COLLADA_TYPE_BLEND_COLOR = "blend_color"; +DLLSPEC daeString COLLADA_TYPE_CLEAR_COLOR = "clear_color"; +DLLSPEC daeString COLLADA_TYPE_CLEAR_STENCIL = "clear_stencil"; +DLLSPEC daeString COLLADA_TYPE_CLEAR_DEPTH = "clear_depth"; +DLLSPEC daeString COLLADA_TYPE_COLOR_MASK = "color_mask"; +DLLSPEC daeString COLLADA_TYPE_DEPTH_BOUNDS = "depth_bounds"; +DLLSPEC daeString COLLADA_TYPE_DEPTH_MASK = "depth_mask"; +DLLSPEC daeString COLLADA_TYPE_DEPTH_RANGE = "depth_range"; +DLLSPEC daeString COLLADA_TYPE_FOG_DENSITY = "fog_density"; +DLLSPEC daeString COLLADA_TYPE_FOG_START = "fog_start"; +DLLSPEC daeString COLLADA_TYPE_FOG_END = "fog_end"; +DLLSPEC daeString COLLADA_TYPE_FOG_COLOR = "fog_color"; +DLLSPEC daeString COLLADA_TYPE_LIGHT_MODEL_AMBIENT = "light_model_ambient"; +DLLSPEC daeString COLLADA_TYPE_LIGHTING_ENABLE = "lighting_enable"; +DLLSPEC daeString COLLADA_TYPE_LINE_STIPPLE = "line_stipple"; +DLLSPEC daeString COLLADA_TYPE_LINE_WIDTH = "line_width"; +DLLSPEC daeString COLLADA_TYPE_MATERIAL_AMBIENT = "material_ambient"; +DLLSPEC daeString COLLADA_TYPE_MATERIAL_DIFFUSE = "material_diffuse"; +DLLSPEC daeString COLLADA_TYPE_MATERIAL_EMISSION = "material_emission"; +DLLSPEC daeString COLLADA_TYPE_MATERIAL_SHININESS = "material_shininess"; +DLLSPEC daeString COLLADA_TYPE_MATERIAL_SPECULAR = "material_specular"; +DLLSPEC daeString COLLADA_TYPE_MODEL_VIEW_MATRIX = "model_view_matrix"; +DLLSPEC daeString COLLADA_TYPE_POINT_DISTANCE_ATTENUATION = "point_distance_attenuation"; +DLLSPEC daeString COLLADA_TYPE_POINT_FADE_THRESHOLD_SIZE = "point_fade_threshold_size"; +DLLSPEC daeString COLLADA_TYPE_POINT_SIZE = "point_size"; +DLLSPEC daeString COLLADA_TYPE_POINT_SIZE_MIN = "point_size_min"; +DLLSPEC daeString COLLADA_TYPE_POINT_SIZE_MAX = "point_size_max"; +DLLSPEC daeString COLLADA_TYPE_POLYGON_OFFSET = "polygon_offset"; +DLLSPEC daeString COLLADA_TYPE_PROJECTION_MATRIX = "projection_matrix"; +DLLSPEC daeString COLLADA_TYPE_SCISSOR = "scissor"; +DLLSPEC daeString COLLADA_TYPE_STENCIL_MASK = "stencil_mask"; +DLLSPEC daeString COLLADA_TYPE_ALPHA_TEST_ENABLE = "alpha_test_enable"; +DLLSPEC daeString COLLADA_TYPE_AUTO_NORMAL_ENABLE = "auto_normal_enable"; +DLLSPEC daeString COLLADA_TYPE_BLEND_ENABLE = "blend_enable"; +DLLSPEC daeString COLLADA_TYPE_COLOR_LOGIC_OP_ENABLE = "color_logic_op_enable"; +DLLSPEC daeString COLLADA_TYPE_COLOR_MATERIAL_ENABLE = "color_material_enable"; +DLLSPEC daeString COLLADA_TYPE_CULL_FACE_ENABLE = "cull_face_enable"; +DLLSPEC daeString COLLADA_TYPE_DEPTH_BOUNDS_ENABLE = "depth_bounds_enable"; +DLLSPEC daeString COLLADA_TYPE_DEPTH_CLAMP_ENABLE = "depth_clamp_enable"; +DLLSPEC daeString COLLADA_TYPE_DEPTH_TEST_ENABLE = "depth_test_enable"; +DLLSPEC daeString COLLADA_TYPE_DITHER_ENABLE = "dither_enable"; +DLLSPEC daeString COLLADA_TYPE_FOG_ENABLE = "fog_enable"; +DLLSPEC daeString COLLADA_TYPE_LIGHT_MODEL_LOCAL_VIEWER_ENABLE = "light_model_local_viewer_enable"; +DLLSPEC daeString COLLADA_TYPE_LIGHT_MODEL_TWO_SIDE_ENABLE = "light_model_two_side_enable"; +DLLSPEC daeString COLLADA_TYPE_LINE_SMOOTH_ENABLE = "line_smooth_enable"; +DLLSPEC daeString COLLADA_TYPE_LINE_STIPPLE_ENABLE = "line_stipple_enable"; +DLLSPEC daeString COLLADA_TYPE_LOGIC_OP_ENABLE = "logic_op_enable"; +DLLSPEC daeString COLLADA_TYPE_MULTISAMPLE_ENABLE = "multisample_enable"; +DLLSPEC daeString COLLADA_TYPE_NORMALIZE_ENABLE = "normalize_enable"; +DLLSPEC daeString COLLADA_TYPE_POINT_SMOOTH_ENABLE = "point_smooth_enable"; +DLLSPEC daeString COLLADA_TYPE_POLYGON_OFFSET_FILL_ENABLE = "polygon_offset_fill_enable"; +DLLSPEC daeString COLLADA_TYPE_POLYGON_OFFSET_LINE_ENABLE = "polygon_offset_line_enable"; +DLLSPEC daeString COLLADA_TYPE_POLYGON_OFFSET_POINT_ENABLE = "polygon_offset_point_enable"; +DLLSPEC daeString COLLADA_TYPE_POLYGON_SMOOTH_ENABLE = "polygon_smooth_enable"; +DLLSPEC daeString COLLADA_TYPE_POLYGON_STIPPLE_ENABLE = "polygon_stipple_enable"; +DLLSPEC daeString COLLADA_TYPE_RESCALE_NORMAL_ENABLE = "rescale_normal_enable"; +DLLSPEC daeString COLLADA_TYPE_SAMPLE_ALPHA_TO_COVERAGE_ENABLE = "sample_alpha_to_coverage_enable"; +DLLSPEC daeString COLLADA_TYPE_SAMPLE_ALPHA_TO_ONE_ENABLE = "sample_alpha_to_one_enable"; +DLLSPEC daeString COLLADA_TYPE_SAMPLE_COVERAGE_ENABLE = "sample_coverage_enable"; +DLLSPEC daeString COLLADA_TYPE_SCISSOR_TEST_ENABLE = "scissor_test_enable"; +DLLSPEC daeString COLLADA_TYPE_STENCIL_TEST_ENABLE = "stencil_test_enable"; +DLLSPEC daeString COLLADA_TYPE_GLSL_PARAM_TYPE = "glsl_param_type"; +DLLSPEC daeString COLLADA_TYPE_CG_PARAM_TYPE = "cg_param_type"; +DLLSPEC daeString COLLADA_TYPE_BOOL1 = "bool1"; +DLLSPEC daeString COLLADA_TYPE_BOOL1X1 = "bool1x1"; +DLLSPEC daeString COLLADA_TYPE_BOOL1X2 = "bool1x2"; +DLLSPEC daeString COLLADA_TYPE_BOOL1X3 = "bool1x3"; +DLLSPEC daeString COLLADA_TYPE_BOOL1X4 = "bool1x4"; +DLLSPEC daeString COLLADA_TYPE_BOOL2X1 = "bool2x1"; +DLLSPEC daeString COLLADA_TYPE_BOOL2X2 = "bool2x2"; +DLLSPEC daeString COLLADA_TYPE_BOOL2X3 = "bool2x3"; +DLLSPEC daeString COLLADA_TYPE_BOOL2X4 = "bool2x4"; +DLLSPEC daeString COLLADA_TYPE_BOOL3X1 = "bool3x1"; +DLLSPEC daeString COLLADA_TYPE_BOOL3X2 = "bool3x2"; +DLLSPEC daeString COLLADA_TYPE_BOOL3X3 = "bool3x3"; +DLLSPEC daeString COLLADA_TYPE_BOOL3X4 = "bool3x4"; +DLLSPEC daeString COLLADA_TYPE_BOOL4X1 = "bool4x1"; +DLLSPEC daeString COLLADA_TYPE_BOOL4X2 = "bool4x2"; +DLLSPEC daeString COLLADA_TYPE_BOOL4X3 = "bool4x3"; +DLLSPEC daeString COLLADA_TYPE_BOOL4X4 = "bool4x4"; +DLLSPEC daeString COLLADA_TYPE_FLOAT1 = "float1"; +DLLSPEC daeString COLLADA_TYPE_INT1 = "int1"; +DLLSPEC daeString COLLADA_TYPE_INT1X1 = "int1x1"; +DLLSPEC daeString COLLADA_TYPE_INT1X2 = "int1x2"; +DLLSPEC daeString COLLADA_TYPE_INT1X3 = "int1x3"; +DLLSPEC daeString COLLADA_TYPE_INT1X4 = "int1x4"; +DLLSPEC daeString COLLADA_TYPE_INT2X1 = "int2x1"; +DLLSPEC daeString COLLADA_TYPE_INT2X2 = "int2x2"; +DLLSPEC daeString COLLADA_TYPE_INT2X3 = "int2x3"; +DLLSPEC daeString COLLADA_TYPE_INT2X4 = "int2x4"; +DLLSPEC daeString COLLADA_TYPE_INT3X1 = "int3x1"; +DLLSPEC daeString COLLADA_TYPE_INT3X2 = "int3x2"; +DLLSPEC daeString COLLADA_TYPE_INT3X3 = "int3x3"; +DLLSPEC daeString COLLADA_TYPE_INT3X4 = "int3x4"; +DLLSPEC daeString COLLADA_TYPE_INT4X1 = "int4x1"; +DLLSPEC daeString COLLADA_TYPE_INT4X2 = "int4x2"; +DLLSPEC daeString COLLADA_TYPE_INT4X3 = "int4x3"; +DLLSPEC daeString COLLADA_TYPE_INT4X4 = "int4x4"; +DLLSPEC daeString COLLADA_TYPE_HALF = "half"; +DLLSPEC daeString COLLADA_TYPE_HALF1 = "half1"; +DLLSPEC daeString COLLADA_TYPE_HALF2 = "half2"; +DLLSPEC daeString COLLADA_TYPE_HALF3 = "half3"; +DLLSPEC daeString COLLADA_TYPE_HALF4 = "half4"; +DLLSPEC daeString COLLADA_TYPE_HALF1X1 = "half1x1"; +DLLSPEC daeString COLLADA_TYPE_HALF1X2 = "half1x2"; +DLLSPEC daeString COLLADA_TYPE_HALF1X3 = "half1x3"; +DLLSPEC daeString COLLADA_TYPE_HALF1X4 = "half1x4"; +DLLSPEC daeString COLLADA_TYPE_HALF2X1 = "half2x1"; +DLLSPEC daeString COLLADA_TYPE_HALF2X2 = "half2x2"; +DLLSPEC daeString COLLADA_TYPE_HALF2X3 = "half2x3"; +DLLSPEC daeString COLLADA_TYPE_HALF2X4 = "half2x4"; +DLLSPEC daeString COLLADA_TYPE_HALF3X1 = "half3x1"; +DLLSPEC daeString COLLADA_TYPE_HALF3X2 = "half3x2"; +DLLSPEC daeString COLLADA_TYPE_HALF3X3 = "half3x3"; +DLLSPEC daeString COLLADA_TYPE_HALF3X4 = "half3x4"; +DLLSPEC daeString COLLADA_TYPE_HALF4X1 = "half4x1"; +DLLSPEC daeString COLLADA_TYPE_HALF4X2 = "half4x2"; +DLLSPEC daeString COLLADA_TYPE_HALF4X3 = "half4x3"; +DLLSPEC daeString COLLADA_TYPE_HALF4X4 = "half4x4"; +DLLSPEC daeString COLLADA_TYPE_FIXED = "fixed"; +DLLSPEC daeString COLLADA_TYPE_FIXED1 = "fixed1"; +DLLSPEC daeString COLLADA_TYPE_FIXED2 = "fixed2"; +DLLSPEC daeString COLLADA_TYPE_FIXED3 = "fixed3"; +DLLSPEC daeString COLLADA_TYPE_FIXED4 = "fixed4"; +DLLSPEC daeString COLLADA_TYPE_FIXED1X1 = "fixed1x1"; +DLLSPEC daeString COLLADA_TYPE_FIXED1X2 = "fixed1x2"; +DLLSPEC daeString COLLADA_TYPE_FIXED1X3 = "fixed1x3"; +DLLSPEC daeString COLLADA_TYPE_FIXED1X4 = "fixed1x4"; +DLLSPEC daeString COLLADA_TYPE_FIXED2X1 = "fixed2x1"; +DLLSPEC daeString COLLADA_TYPE_FIXED2X2 = "fixed2x2"; +DLLSPEC daeString COLLADA_TYPE_FIXED2X3 = "fixed2x3"; +DLLSPEC daeString COLLADA_TYPE_FIXED2X4 = "fixed2x4"; +DLLSPEC daeString COLLADA_TYPE_FIXED3X1 = "fixed3x1"; +DLLSPEC daeString COLLADA_TYPE_FIXED3X2 = "fixed3x2"; +DLLSPEC daeString COLLADA_TYPE_FIXED3X3 = "fixed3x3"; +DLLSPEC daeString COLLADA_TYPE_FIXED3X4 = "fixed3x4"; +DLLSPEC daeString COLLADA_TYPE_FIXED4X1 = "fixed4x1"; +DLLSPEC daeString COLLADA_TYPE_FIXED4X2 = "fixed4x2"; +DLLSPEC daeString COLLADA_TYPE_FIXED4X3 = "fixed4x3"; +DLLSPEC daeString COLLADA_TYPE_FIXED4X4 = "fixed4x4"; +DLLSPEC daeString COLLADA_TYPE_GLES_PIPELINE_SETTINGS = "gles_pipeline_settings"; +DLLSPEC daeString COLLADA_TYPE_TEXTURE_PIPELINE = "texture_pipeline"; +DLLSPEC daeString COLLADA_TYPE_LIGHT_LINEAR_ATTENUTATION = "light_linear_attenutation"; +DLLSPEC daeString COLLADA_TYPE_TEXTURE_PIPELINE_ENABLE = "texture_pipeline_enable"; +DLLSPEC daeString COLLADA_TYPE_GLES_BASIC_TYPE_COMMON = "gles_basic_type_common"; +DLLSPEC daeString COLLADA_TYPE_COLLADA = "COLLADA"; +DLLSPEC daeString COLLADA_TYPE_SCENE = "scene"; +DLLSPEC daeString COLLADA_TYPE_IDREF_ARRAY = "IDREF_array"; +DLLSPEC daeString COLLADA_TYPE_NAME_ARRAY = "Name_array"; +DLLSPEC daeString COLLADA_TYPE_BOOL_ARRAY = "bool_array"; +DLLSPEC daeString COLLADA_TYPE_FLOAT_ARRAY = "float_array"; +DLLSPEC daeString COLLADA_TYPE_INT_ARRAY = "int_array"; +DLLSPEC daeString COLLADA_TYPE_ACCESSOR = "accessor"; +DLLSPEC daeString COLLADA_TYPE_TECHNIQUE_COMMON = "technique_common"; +DLLSPEC daeString COLLADA_TYPE_GEOMETRY = "geometry"; +DLLSPEC daeString COLLADA_TYPE_MESH = "mesh"; +DLLSPEC daeString COLLADA_TYPE_SPLINE = "spline"; +DLLSPEC daeString COLLADA_TYPE_CONTROL_VERTICES = "control_vertices"; +DLLSPEC daeString COLLADA_TYPE_P = "p"; +DLLSPEC daeString COLLADA_TYPE_LINES = "lines"; +DLLSPEC daeString COLLADA_TYPE_LINESTRIPS = "linestrips"; +DLLSPEC daeString COLLADA_TYPE_POLYGONS = "polygons"; +DLLSPEC daeString COLLADA_TYPE_PH = "ph"; +DLLSPEC daeString COLLADA_TYPE_H = "h"; +DLLSPEC daeString COLLADA_TYPE_POLYLIST = "polylist"; +DLLSPEC daeString COLLADA_TYPE_VCOUNT = "vcount"; +DLLSPEC daeString COLLADA_TYPE_TRIANGLES = "triangles"; +DLLSPEC daeString COLLADA_TYPE_TRIFANS = "trifans"; +DLLSPEC daeString COLLADA_TYPE_TRISTRIPS = "tristrips"; +DLLSPEC daeString COLLADA_TYPE_VERTICES = "vertices"; +DLLSPEC daeString COLLADA_TYPE_LOOKAT = "lookat"; +DLLSPEC daeString COLLADA_TYPE_MATRIX = "matrix"; +DLLSPEC daeString COLLADA_TYPE_ROTATE = "rotate"; +DLLSPEC daeString COLLADA_TYPE_SCALE = "scale"; +DLLSPEC daeString COLLADA_TYPE_SKEW = "skew"; +DLLSPEC daeString COLLADA_TYPE_TRANSLATE = "translate"; +DLLSPEC daeString COLLADA_TYPE_IMAGE = "image"; +DLLSPEC daeString COLLADA_TYPE_DATA = "data"; +DLLSPEC daeString COLLADA_TYPE_INIT_FROM = "init_from"; +DLLSPEC daeString COLLADA_TYPE_LIGHT = "light"; +DLLSPEC daeString COLLADA_TYPE_AMBIENT = "ambient"; +DLLSPEC daeString COLLADA_TYPE_DIRECTIONAL = "directional"; +DLLSPEC daeString COLLADA_TYPE_POINT = "point"; +DLLSPEC daeString COLLADA_TYPE_SPOT = "spot"; +DLLSPEC daeString COLLADA_TYPE_MATERIAL = "material"; +DLLSPEC daeString COLLADA_TYPE_CAMERA = "camera"; +DLLSPEC daeString COLLADA_TYPE_OPTICS = "optics"; +DLLSPEC daeString COLLADA_TYPE_ORTHOGRAPHIC = "orthographic"; +DLLSPEC daeString COLLADA_TYPE_PERSPECTIVE = "perspective"; +DLLSPEC daeString COLLADA_TYPE_IMAGER = "imager"; +DLLSPEC daeString COLLADA_TYPE_ANIMATION = "animation"; +DLLSPEC daeString COLLADA_TYPE_ANIMATION_CLIP = "animation_clip"; +DLLSPEC daeString COLLADA_TYPE_CHANNEL = "channel"; +DLLSPEC daeString COLLADA_TYPE_SAMPLER = "sampler"; +DLLSPEC daeString COLLADA_TYPE_CONTROLLER = "controller"; +DLLSPEC daeString COLLADA_TYPE_SKIN = "skin"; +DLLSPEC daeString COLLADA_TYPE_BIND_SHAPE_MATRIX = "bind_shape_matrix"; +DLLSPEC daeString COLLADA_TYPE_JOINTS = "joints"; +DLLSPEC daeString COLLADA_TYPE_VERTEX_WEIGHTS = "vertex_weights"; +DLLSPEC daeString COLLADA_TYPE_V = "v"; +DLLSPEC daeString COLLADA_TYPE_MORPH = "morph"; +DLLSPEC daeString COLLADA_TYPE_TARGETS = "targets"; +DLLSPEC daeString COLLADA_TYPE_ASSET = "asset"; +DLLSPEC daeString COLLADA_TYPE_CONTRIBUTOR = "contributor"; +DLLSPEC daeString COLLADA_TYPE_AUTHOR = "author"; +DLLSPEC daeString COLLADA_TYPE_AUTHORING_TOOL = "authoring_tool"; +DLLSPEC daeString COLLADA_TYPE_COMMENTS = "comments"; +DLLSPEC daeString COLLADA_TYPE_COPYRIGHT = "copyright"; +DLLSPEC daeString COLLADA_TYPE_SOURCE_DATA = "source_data"; +DLLSPEC daeString COLLADA_TYPE_CREATED = "created"; +DLLSPEC daeString COLLADA_TYPE_KEYWORDS = "keywords"; +DLLSPEC daeString COLLADA_TYPE_MODIFIED = "modified"; +DLLSPEC daeString COLLADA_TYPE_REVISION = "revision"; +DLLSPEC daeString COLLADA_TYPE_SUBJECT = "subject"; +DLLSPEC daeString COLLADA_TYPE_TITLE = "title"; +DLLSPEC daeString COLLADA_TYPE_UNIT = "unit"; +DLLSPEC daeString COLLADA_TYPE_UP_AXIS = "up_axis"; +DLLSPEC daeString COLLADA_TYPE_EXTRA = "extra"; +DLLSPEC daeString COLLADA_TYPE_TECHNIQUE = "technique"; +DLLSPEC daeString COLLADA_TYPE_NODE = "node"; +DLLSPEC daeString COLLADA_TYPE_VISUAL_SCENE = "visual_scene"; +DLLSPEC daeString COLLADA_TYPE_EVALUATE_SCENE = "evaluate_scene"; +DLLSPEC daeString COLLADA_TYPE_RENDER = "render"; +DLLSPEC daeString COLLADA_TYPE_LAYER = "layer"; +DLLSPEC daeString COLLADA_TYPE_BIND_MATERIAL = "bind_material"; +DLLSPEC daeString COLLADA_TYPE_INSTANCE_CAMERA = "instance_camera"; +DLLSPEC daeString COLLADA_TYPE_INSTANCE_CONTROLLER = "instance_controller"; +DLLSPEC daeString COLLADA_TYPE_SKELETON = "skeleton"; +DLLSPEC daeString COLLADA_TYPE_INSTANCE_EFFECT = "instance_effect"; +DLLSPEC daeString COLLADA_TYPE_TECHNIQUE_HINT = "technique_hint"; +DLLSPEC daeString COLLADA_TYPE_SETPARAM = "setparam"; +DLLSPEC daeString COLLADA_TYPE_INSTANCE_FORCE_FIELD = "instance_force_field"; +DLLSPEC daeString COLLADA_TYPE_INSTANCE_GEOMETRY = "instance_geometry"; +DLLSPEC daeString COLLADA_TYPE_INSTANCE_LIGHT = "instance_light"; +DLLSPEC daeString COLLADA_TYPE_INSTANCE_MATERIAL = "instance_material"; +DLLSPEC daeString COLLADA_TYPE_BIND = "bind"; +DLLSPEC daeString COLLADA_TYPE_BIND_VERTEX_INPUT = "bind_vertex_input"; +DLLSPEC daeString COLLADA_TYPE_INSTANCE_NODE = "instance_node"; +DLLSPEC daeString COLLADA_TYPE_INSTANCE_PHYSICS_MATERIAL = "instance_physics_material"; +DLLSPEC daeString COLLADA_TYPE_INSTANCE_PHYSICS_MODEL = "instance_physics_model"; +DLLSPEC daeString COLLADA_TYPE_INSTANCE_RIGID_BODY = "instance_rigid_body"; +DLLSPEC daeString COLLADA_TYPE_ANGULAR_VELOCITY = "angular_velocity"; +DLLSPEC daeString COLLADA_TYPE_VELOCITY = "velocity"; +DLLSPEC daeString COLLADA_TYPE_DYNAMIC = "dynamic"; +DLLSPEC daeString COLLADA_TYPE_MASS_FRAME = "mass_frame"; +DLLSPEC daeString COLLADA_TYPE_SHAPE = "shape"; +DLLSPEC daeString COLLADA_TYPE_HOLLOW = "hollow"; +DLLSPEC daeString COLLADA_TYPE_INSTANCE_RIGID_CONSTRAINT = "instance_rigid_constraint"; +DLLSPEC daeString COLLADA_TYPE_LIBRARY_ANIMATIONS = "library_animations"; +DLLSPEC daeString COLLADA_TYPE_LIBRARY_ANIMATION_CLIPS = "library_animation_clips"; +DLLSPEC daeString COLLADA_TYPE_LIBRARY_CAMERAS = "library_cameras"; +DLLSPEC daeString COLLADA_TYPE_LIBRARY_CONTROLLERS = "library_controllers"; +DLLSPEC daeString COLLADA_TYPE_LIBRARY_GEOMETRIES = "library_geometries"; +DLLSPEC daeString COLLADA_TYPE_LIBRARY_EFFECTS = "library_effects"; +DLLSPEC daeString COLLADA_TYPE_LIBRARY_FORCE_FIELDS = "library_force_fields"; +DLLSPEC daeString COLLADA_TYPE_LIBRARY_IMAGES = "library_images"; +DLLSPEC daeString COLLADA_TYPE_LIBRARY_LIGHTS = "library_lights"; +DLLSPEC daeString COLLADA_TYPE_LIBRARY_MATERIALS = "library_materials"; +DLLSPEC daeString COLLADA_TYPE_LIBRARY_NODES = "library_nodes"; +DLLSPEC daeString COLLADA_TYPE_LIBRARY_PHYSICS_MATERIALS = "library_physics_materials"; +DLLSPEC daeString COLLADA_TYPE_LIBRARY_PHYSICS_MODELS = "library_physics_models"; +DLLSPEC daeString COLLADA_TYPE_LIBRARY_PHYSICS_SCENES = "library_physics_scenes"; +DLLSPEC daeString COLLADA_TYPE_LIBRARY_VISUAL_SCENES = "library_visual_scenes"; +DLLSPEC daeString COLLADA_TYPE_FX_PROFILE_ABSTRACT = "fx_profile_abstract"; +DLLSPEC daeString COLLADA_TYPE_EFFECT = "effect"; +DLLSPEC daeString COLLADA_TYPE_GL_HOOK_ABSTRACT = "gl_hook_abstract"; +DLLSPEC daeString COLLADA_TYPE_PROFILE_GLSL = "profile_GLSL"; +DLLSPEC daeString COLLADA_TYPE_PASS = "pass"; +DLLSPEC daeString COLLADA_TYPE_DRAW = "draw"; +DLLSPEC daeString COLLADA_TYPE_SHADER = "shader"; +DLLSPEC daeString COLLADA_TYPE_COMPILER_TARGET = "compiler_target"; +DLLSPEC daeString COLLADA_TYPE_COMPILER_OPTIONS = "compiler_options"; +DLLSPEC daeString COLLADA_TYPE_PROFILE_COMMON = "profile_COMMON"; +DLLSPEC daeString COLLADA_TYPE_CONSTANT = "constant"; +DLLSPEC daeString COLLADA_TYPE_LAMBERT = "lambert"; +DLLSPEC daeString COLLADA_TYPE_PHONG = "phong"; +DLLSPEC daeString COLLADA_TYPE_BLINN = "blinn"; +DLLSPEC daeString COLLADA_TYPE_PROFILE_CG = "profile_CG"; +DLLSPEC daeString COLLADA_TYPE_PROFILE_GLES = "profile_GLES"; +DLLSPEC daeString COLLADA_TYPE_COLOR_TARGET = "color_target"; +DLLSPEC daeString COLLADA_TYPE_DEPTH_TARGET = "depth_target"; +DLLSPEC daeString COLLADA_TYPE_STENCIL_TARGET = "stencil_target"; +DLLSPEC daeString COLLADA_TYPE_COLOR_CLEAR = "color_clear"; +DLLSPEC daeString COLLADA_TYPE_DEPTH_CLEAR = "depth_clear"; +DLLSPEC daeString COLLADA_TYPE_STENCIL_CLEAR = "stencil_clear"; +DLLSPEC daeString COLLADA_TYPE_BOX = "box"; +DLLSPEC daeString COLLADA_TYPE_HALF_EXTENTS = "half_extents"; +DLLSPEC daeString COLLADA_TYPE_PLANE = "plane"; +DLLSPEC daeString COLLADA_TYPE_EQUATION = "equation"; +DLLSPEC daeString COLLADA_TYPE_SPHERE = "sphere"; +DLLSPEC daeString COLLADA_TYPE_RADIUS = "radius"; +DLLSPEC daeString COLLADA_TYPE_ELLIPSOID = "ellipsoid"; +DLLSPEC daeString COLLADA_TYPE_CYLINDER = "cylinder"; +DLLSPEC daeString COLLADA_TYPE_HEIGHT = "height"; +DLLSPEC daeString COLLADA_TYPE_TAPERED_CYLINDER = "tapered_cylinder"; +DLLSPEC daeString COLLADA_TYPE_RADIUS1 = "radius1"; +DLLSPEC daeString COLLADA_TYPE_RADIUS2 = "radius2"; +DLLSPEC daeString COLLADA_TYPE_CAPSULE = "capsule"; +DLLSPEC daeString COLLADA_TYPE_TAPERED_CAPSULE = "tapered_capsule"; +DLLSPEC daeString COLLADA_TYPE_CONVEX_MESH = "convex_mesh"; +DLLSPEC daeString COLLADA_TYPE_FORCE_FIELD = "force_field"; +DLLSPEC daeString COLLADA_TYPE_PHYSICS_MATERIAL = "physics_material"; +DLLSPEC daeString COLLADA_TYPE_PHYSICS_SCENE = "physics_scene"; +DLLSPEC daeString COLLADA_TYPE_RIGID_BODY = "rigid_body"; +DLLSPEC daeString COLLADA_TYPE_RIGID_CONSTRAINT = "rigid_constraint"; +DLLSPEC daeString COLLADA_TYPE_REF_ATTACHMENT = "ref_attachment"; +DLLSPEC daeString COLLADA_TYPE_ATTACHMENT = "attachment"; +DLLSPEC daeString COLLADA_TYPE_ENABLED = "enabled"; +DLLSPEC daeString COLLADA_TYPE_INTERPENETRATE = "interpenetrate"; +DLLSPEC daeString COLLADA_TYPE_LIMITS = "limits"; +DLLSPEC daeString COLLADA_TYPE_SWING_CONE_AND_TWIST = "swing_cone_and_twist"; +DLLSPEC daeString COLLADA_TYPE_LINEAR = "linear"; +DLLSPEC daeString COLLADA_TYPE_SPRING = "spring"; +DLLSPEC daeString COLLADA_TYPE_ANGULAR = "angular"; +DLLSPEC daeString COLLADA_TYPE_PHYSICS_MODEL = "physics_model"; + +DLLSPEC daeString COLLADA_ELEMENT_COLLADA = "COLLADA"; +DLLSPEC daeString COLLADA_ELEMENT_EXTRA = "extra"; +DLLSPEC daeString COLLADA_ELEMENT_CHANNELS = "channels"; +DLLSPEC daeString COLLADA_ELEMENT_RANGE = "range"; +DLLSPEC daeString COLLADA_ELEMENT_PRECISION = "precision"; +DLLSPEC daeString COLLADA_ELEMENT_OPTION = "option"; +DLLSPEC daeString COLLADA_ELEMENT_ALL = "all"; +DLLSPEC daeString COLLADA_ELEMENT_PRIMARY = "primary"; +DLLSPEC daeString COLLADA_ELEMENT_FACE = "face"; +DLLSPEC daeString COLLADA_ELEMENT_ORDER = "order"; +DLLSPEC daeString COLLADA_ELEMENT_FX_SURFACE_INIT_COMMON = "fx_surface_init_common"; +DLLSPEC daeString COLLADA_ELEMENT_FORMAT = "format"; +DLLSPEC daeString COLLADA_ELEMENT_FORMAT_HINT = "format_hint"; +DLLSPEC daeString COLLADA_ELEMENT_SIZE = "size"; +DLLSPEC daeString COLLADA_ELEMENT_VIEWPORT_RATIO = "viewport_ratio"; +DLLSPEC daeString COLLADA_ELEMENT_MIP_LEVELS = "mip_levels"; +DLLSPEC daeString COLLADA_ELEMENT_MIPMAP_GENERATE = "mipmap_generate"; +DLLSPEC daeString COLLADA_ELEMENT_SOURCE = "source"; +DLLSPEC daeString COLLADA_ELEMENT_WRAP_S = "wrap_s"; +DLLSPEC daeString COLLADA_ELEMENT_MINFILTER = "minfilter"; +DLLSPEC daeString COLLADA_ELEMENT_MAGFILTER = "magfilter"; +DLLSPEC daeString COLLADA_ELEMENT_MIPFILTER = "mipfilter"; +DLLSPEC daeString COLLADA_ELEMENT_BORDER_COLOR = "border_color"; +DLLSPEC daeString COLLADA_ELEMENT_MIPMAP_MAXLEVEL = "mipmap_maxlevel"; +DLLSPEC daeString COLLADA_ELEMENT_MIPMAP_BIAS = "mipmap_bias"; +DLLSPEC daeString COLLADA_ELEMENT_WRAP_T = "wrap_t"; +DLLSPEC daeString COLLADA_ELEMENT_WRAP_P = "wrap_p"; +DLLSPEC daeString COLLADA_ELEMENT_FX_ANNOTATE_TYPE_COMMON = "fx_annotate_type_common"; +DLLSPEC daeString COLLADA_ELEMENT_ANNOTATE = "annotate"; +DLLSPEC daeString COLLADA_ELEMENT_SEMANTIC = "semantic"; +DLLSPEC daeString COLLADA_ELEMENT_MODIFIER = "modifier"; +DLLSPEC daeString COLLADA_ELEMENT_FX_BASIC_TYPE_COMMON = "fx_basic_type_common"; +DLLSPEC daeString COLLADA_ELEMENT_GLSL_PARAM_TYPE = "glsl_param_type"; +DLLSPEC daeString COLLADA_ELEMENT_ARRAY = "array"; +DLLSPEC daeString COLLADA_ELEMENT_GENERATOR = "generator"; +DLLSPEC daeString COLLADA_ELEMENT_CODE = "code"; +DLLSPEC daeString COLLADA_ELEMENT_INCLUDE = "include"; +DLLSPEC daeString COLLADA_ELEMENT_NAME = "name"; +DLLSPEC daeString COLLADA_ELEMENT_SETPARAM = "setparam"; +DLLSPEC daeString COLLADA_ELEMENT_FLOAT = "float"; +DLLSPEC daeString COLLADA_ELEMENT_PARAM = "param"; +DLLSPEC daeString COLLADA_ELEMENT_COLOR = "color"; +DLLSPEC daeString COLLADA_ELEMENT_TEXTURE = "texture"; +DLLSPEC daeString COLLADA_ELEMENT_FLOAT2 = "float2"; +DLLSPEC daeString COLLADA_ELEMENT_FLOAT3 = "float3"; +DLLSPEC daeString COLLADA_ELEMENT_FLOAT4 = "float4"; +DLLSPEC daeString COLLADA_ELEMENT_SURFACE = "surface"; +DLLSPEC daeString COLLADA_ELEMENT_SAMPLER2D = "sampler2D"; +DLLSPEC daeString COLLADA_ELEMENT_CG_PARAM_TYPE = "cg_param_type"; +DLLSPEC daeString COLLADA_ELEMENT_USERTYPE = "usertype"; +DLLSPEC daeString COLLADA_ELEMENT_CONNECT_PARAM = "connect_param"; +DLLSPEC daeString COLLADA_ELEMENT_CONSTANT = "constant"; +DLLSPEC daeString COLLADA_ELEMENT_ARGUMENT = "argument"; +DLLSPEC daeString COLLADA_ELEMENT_RGB = "RGB"; +DLLSPEC daeString COLLADA_ELEMENT_ALPHA = "alpha"; +DLLSPEC daeString COLLADA_ELEMENT_TEXCOMBINER = "texcombiner"; +DLLSPEC daeString COLLADA_ELEMENT_TEXENV = "texenv"; +DLLSPEC daeString COLLADA_ELEMENT_SAMPLER_STATE = "sampler_state"; +DLLSPEC daeString COLLADA_ELEMENT_TEXCOORD = "texcoord"; +DLLSPEC daeString COLLADA_ELEMENT_GLES_BASIC_TYPE_COMMON = "gles_basic_type_common"; +DLLSPEC daeString COLLADA_ELEMENT_INIT_AS_NULL = "init_as_null"; +DLLSPEC daeString COLLADA_ELEMENT_INIT_AS_TARGET = "init_as_target"; +DLLSPEC daeString COLLADA_ELEMENT_INIT_CUBE = "init_cube"; +DLLSPEC daeString COLLADA_ELEMENT_INIT_VOLUME = "init_volume"; +DLLSPEC daeString COLLADA_ELEMENT_INIT_PLANAR = "init_planar"; +DLLSPEC daeString COLLADA_ELEMENT_INIT_FROM = "init_from"; +DLLSPEC daeString COLLADA_ELEMENT_BOOL = "bool"; +DLLSPEC daeString COLLADA_ELEMENT_BOOL2 = "bool2"; +DLLSPEC daeString COLLADA_ELEMENT_BOOL3 = "bool3"; +DLLSPEC daeString COLLADA_ELEMENT_BOOL4 = "bool4"; +DLLSPEC daeString COLLADA_ELEMENT_INT = "int"; +DLLSPEC daeString COLLADA_ELEMENT_INT2 = "int2"; +DLLSPEC daeString COLLADA_ELEMENT_INT3 = "int3"; +DLLSPEC daeString COLLADA_ELEMENT_INT4 = "int4"; +DLLSPEC daeString COLLADA_ELEMENT_FLOAT2X2 = "float2x2"; +DLLSPEC daeString COLLADA_ELEMENT_FLOAT3X3 = "float3x3"; +DLLSPEC daeString COLLADA_ELEMENT_FLOAT4X4 = "float4x4"; +DLLSPEC daeString COLLADA_ELEMENT_STRING = "string"; +DLLSPEC daeString COLLADA_ELEMENT_FLOAT1X1 = "float1x1"; +DLLSPEC daeString COLLADA_ELEMENT_FLOAT1X2 = "float1x2"; +DLLSPEC daeString COLLADA_ELEMENT_FLOAT1X3 = "float1x3"; +DLLSPEC daeString COLLADA_ELEMENT_FLOAT1X4 = "float1x4"; +DLLSPEC daeString COLLADA_ELEMENT_FLOAT2X1 = "float2x1"; +DLLSPEC daeString COLLADA_ELEMENT_FLOAT2X3 = "float2x3"; +DLLSPEC daeString COLLADA_ELEMENT_FLOAT2X4 = "float2x4"; +DLLSPEC daeString COLLADA_ELEMENT_FLOAT3X1 = "float3x1"; +DLLSPEC daeString COLLADA_ELEMENT_FLOAT3X2 = "float3x2"; +DLLSPEC daeString COLLADA_ELEMENT_FLOAT3X4 = "float3x4"; +DLLSPEC daeString COLLADA_ELEMENT_FLOAT4X1 = "float4x1"; +DLLSPEC daeString COLLADA_ELEMENT_FLOAT4X2 = "float4x2"; +DLLSPEC daeString COLLADA_ELEMENT_FLOAT4X3 = "float4x3"; +DLLSPEC daeString COLLADA_ELEMENT_SAMPLER1D = "sampler1D"; +DLLSPEC daeString COLLADA_ELEMENT_SAMPLER3D = "sampler3D"; +DLLSPEC daeString COLLADA_ELEMENT_SAMPLERCUBE = "samplerCUBE"; +DLLSPEC daeString COLLADA_ELEMENT_SAMPLERRECT = "samplerRECT"; +DLLSPEC daeString COLLADA_ELEMENT_SAMPLERDEPTH = "samplerDEPTH"; +DLLSPEC daeString COLLADA_ELEMENT_ENUM = "enum"; +DLLSPEC daeString COLLADA_ELEMENT_ALPHA_FUNC = "alpha_func"; +DLLSPEC daeString COLLADA_ELEMENT_BLEND_FUNC = "blend_func"; +DLLSPEC daeString COLLADA_ELEMENT_BLEND_FUNC_SEPARATE = "blend_func_separate"; +DLLSPEC daeString COLLADA_ELEMENT_BLEND_EQUATION = "blend_equation"; +DLLSPEC daeString COLLADA_ELEMENT_BLEND_EQUATION_SEPARATE = "blend_equation_separate"; +DLLSPEC daeString COLLADA_ELEMENT_COLOR_MATERIAL = "color_material"; +DLLSPEC daeString COLLADA_ELEMENT_CULL_FACE = "cull_face"; +DLLSPEC daeString COLLADA_ELEMENT_DEPTH_FUNC = "depth_func"; +DLLSPEC daeString COLLADA_ELEMENT_FOG_MODE = "fog_mode"; +DLLSPEC daeString COLLADA_ELEMENT_FOG_COORD_SRC = "fog_coord_src"; +DLLSPEC daeString COLLADA_ELEMENT_FRONT_FACE = "front_face"; +DLLSPEC daeString COLLADA_ELEMENT_LIGHT_MODEL_COLOR_CONTROL = "light_model_color_control"; +DLLSPEC daeString COLLADA_ELEMENT_LOGIC_OP = "logic_op"; +DLLSPEC daeString COLLADA_ELEMENT_POLYGON_MODE = "polygon_mode"; +DLLSPEC daeString COLLADA_ELEMENT_SHADE_MODEL = "shade_model"; +DLLSPEC daeString COLLADA_ELEMENT_STENCIL_FUNC = "stencil_func"; +DLLSPEC daeString COLLADA_ELEMENT_STENCIL_OP = "stencil_op"; +DLLSPEC daeString COLLADA_ELEMENT_STENCIL_FUNC_SEPARATE = "stencil_func_separate"; +DLLSPEC daeString COLLADA_ELEMENT_STENCIL_OP_SEPARATE = "stencil_op_separate"; +DLLSPEC daeString COLLADA_ELEMENT_STENCIL_MASK_SEPARATE = "stencil_mask_separate"; +DLLSPEC daeString COLLADA_ELEMENT_LIGHT_ENABLE = "light_enable"; +DLLSPEC daeString COLLADA_ELEMENT_LIGHT_AMBIENT = "light_ambient"; +DLLSPEC daeString COLLADA_ELEMENT_LIGHT_DIFFUSE = "light_diffuse"; +DLLSPEC daeString COLLADA_ELEMENT_LIGHT_SPECULAR = "light_specular"; +DLLSPEC daeString COLLADA_ELEMENT_LIGHT_POSITION = "light_position"; +DLLSPEC daeString COLLADA_ELEMENT_LIGHT_CONSTANT_ATTENUATION = "light_constant_attenuation"; +DLLSPEC daeString COLLADA_ELEMENT_LIGHT_LINEAR_ATTENUATION = "light_linear_attenuation"; +DLLSPEC daeString COLLADA_ELEMENT_LIGHT_QUADRATIC_ATTENUATION = "light_quadratic_attenuation"; +DLLSPEC daeString COLLADA_ELEMENT_LIGHT_SPOT_CUTOFF = "light_spot_cutoff"; +DLLSPEC daeString COLLADA_ELEMENT_LIGHT_SPOT_DIRECTION = "light_spot_direction"; +DLLSPEC daeString COLLADA_ELEMENT_LIGHT_SPOT_EXPONENT = "light_spot_exponent"; +DLLSPEC daeString COLLADA_ELEMENT_TEXTURE1D = "texture1D"; +DLLSPEC daeString COLLADA_ELEMENT_TEXTURE2D = "texture2D"; +DLLSPEC daeString COLLADA_ELEMENT_TEXTURE3D = "texture3D"; +DLLSPEC daeString COLLADA_ELEMENT_TEXTURECUBE = "textureCUBE"; +DLLSPEC daeString COLLADA_ELEMENT_TEXTURERECT = "textureRECT"; +DLLSPEC daeString COLLADA_ELEMENT_TEXTUREDEPTH = "textureDEPTH"; +DLLSPEC daeString COLLADA_ELEMENT_TEXTURE1D_ENABLE = "texture1D_enable"; +DLLSPEC daeString COLLADA_ELEMENT_TEXTURE2D_ENABLE = "texture2D_enable"; +DLLSPEC daeString COLLADA_ELEMENT_TEXTURE3D_ENABLE = "texture3D_enable"; +DLLSPEC daeString COLLADA_ELEMENT_TEXTURECUBE_ENABLE = "textureCUBE_enable"; +DLLSPEC daeString COLLADA_ELEMENT_TEXTURERECT_ENABLE = "textureRECT_enable"; +DLLSPEC daeString COLLADA_ELEMENT_TEXTUREDEPTH_ENABLE = "textureDEPTH_enable"; +DLLSPEC daeString COLLADA_ELEMENT_TEXTURE_ENV_COLOR = "texture_env_color"; +DLLSPEC daeString COLLADA_ELEMENT_TEXTURE_ENV_MODE = "texture_env_mode"; +DLLSPEC daeString COLLADA_ELEMENT_CLIP_PLANE = "clip_plane"; +DLLSPEC daeString COLLADA_ELEMENT_CLIP_PLANE_ENABLE = "clip_plane_enable"; +DLLSPEC daeString COLLADA_ELEMENT_BLEND_COLOR = "blend_color"; +DLLSPEC daeString COLLADA_ELEMENT_CLEAR_COLOR = "clear_color"; +DLLSPEC daeString COLLADA_ELEMENT_CLEAR_STENCIL = "clear_stencil"; +DLLSPEC daeString COLLADA_ELEMENT_CLEAR_DEPTH = "clear_depth"; +DLLSPEC daeString COLLADA_ELEMENT_COLOR_MASK = "color_mask"; +DLLSPEC daeString COLLADA_ELEMENT_DEPTH_BOUNDS = "depth_bounds"; +DLLSPEC daeString COLLADA_ELEMENT_DEPTH_MASK = "depth_mask"; +DLLSPEC daeString COLLADA_ELEMENT_DEPTH_RANGE = "depth_range"; +DLLSPEC daeString COLLADA_ELEMENT_FOG_DENSITY = "fog_density"; +DLLSPEC daeString COLLADA_ELEMENT_FOG_START = "fog_start"; +DLLSPEC daeString COLLADA_ELEMENT_FOG_END = "fog_end"; +DLLSPEC daeString COLLADA_ELEMENT_FOG_COLOR = "fog_color"; +DLLSPEC daeString COLLADA_ELEMENT_LIGHT_MODEL_AMBIENT = "light_model_ambient"; +DLLSPEC daeString COLLADA_ELEMENT_LIGHTING_ENABLE = "lighting_enable"; +DLLSPEC daeString COLLADA_ELEMENT_LINE_STIPPLE = "line_stipple"; +DLLSPEC daeString COLLADA_ELEMENT_LINE_WIDTH = "line_width"; +DLLSPEC daeString COLLADA_ELEMENT_MATERIAL_AMBIENT = "material_ambient"; +DLLSPEC daeString COLLADA_ELEMENT_MATERIAL_DIFFUSE = "material_diffuse"; +DLLSPEC daeString COLLADA_ELEMENT_MATERIAL_EMISSION = "material_emission"; +DLLSPEC daeString COLLADA_ELEMENT_MATERIAL_SHININESS = "material_shininess"; +DLLSPEC daeString COLLADA_ELEMENT_MATERIAL_SPECULAR = "material_specular"; +DLLSPEC daeString COLLADA_ELEMENT_MODEL_VIEW_MATRIX = "model_view_matrix"; +DLLSPEC daeString COLLADA_ELEMENT_POINT_DISTANCE_ATTENUATION = "point_distance_attenuation"; +DLLSPEC daeString COLLADA_ELEMENT_POINT_FADE_THRESHOLD_SIZE = "point_fade_threshold_size"; +DLLSPEC daeString COLLADA_ELEMENT_POINT_SIZE = "point_size"; +DLLSPEC daeString COLLADA_ELEMENT_POINT_SIZE_MIN = "point_size_min"; +DLLSPEC daeString COLLADA_ELEMENT_POINT_SIZE_MAX = "point_size_max"; +DLLSPEC daeString COLLADA_ELEMENT_POLYGON_OFFSET = "polygon_offset"; +DLLSPEC daeString COLLADA_ELEMENT_PROJECTION_MATRIX = "projection_matrix"; +DLLSPEC daeString COLLADA_ELEMENT_SCISSOR = "scissor"; +DLLSPEC daeString COLLADA_ELEMENT_STENCIL_MASK = "stencil_mask"; +DLLSPEC daeString COLLADA_ELEMENT_ALPHA_TEST_ENABLE = "alpha_test_enable"; +DLLSPEC daeString COLLADA_ELEMENT_AUTO_NORMAL_ENABLE = "auto_normal_enable"; +DLLSPEC daeString COLLADA_ELEMENT_BLEND_ENABLE = "blend_enable"; +DLLSPEC daeString COLLADA_ELEMENT_COLOR_LOGIC_OP_ENABLE = "color_logic_op_enable"; +DLLSPEC daeString COLLADA_ELEMENT_COLOR_MATERIAL_ENABLE = "color_material_enable"; +DLLSPEC daeString COLLADA_ELEMENT_CULL_FACE_ENABLE = "cull_face_enable"; +DLLSPEC daeString COLLADA_ELEMENT_DEPTH_BOUNDS_ENABLE = "depth_bounds_enable"; +DLLSPEC daeString COLLADA_ELEMENT_DEPTH_CLAMP_ENABLE = "depth_clamp_enable"; +DLLSPEC daeString COLLADA_ELEMENT_DEPTH_TEST_ENABLE = "depth_test_enable"; +DLLSPEC daeString COLLADA_ELEMENT_DITHER_ENABLE = "dither_enable"; +DLLSPEC daeString COLLADA_ELEMENT_FOG_ENABLE = "fog_enable"; +DLLSPEC daeString COLLADA_ELEMENT_LIGHT_MODEL_LOCAL_VIEWER_ENABLE = "light_model_local_viewer_enable"; +DLLSPEC daeString COLLADA_ELEMENT_LIGHT_MODEL_TWO_SIDE_ENABLE = "light_model_two_side_enable"; +DLLSPEC daeString COLLADA_ELEMENT_LINE_SMOOTH_ENABLE = "line_smooth_enable"; +DLLSPEC daeString COLLADA_ELEMENT_LINE_STIPPLE_ENABLE = "line_stipple_enable"; +DLLSPEC daeString COLLADA_ELEMENT_LOGIC_OP_ENABLE = "logic_op_enable"; +DLLSPEC daeString COLLADA_ELEMENT_MULTISAMPLE_ENABLE = "multisample_enable"; +DLLSPEC daeString COLLADA_ELEMENT_NORMALIZE_ENABLE = "normalize_enable"; +DLLSPEC daeString COLLADA_ELEMENT_POINT_SMOOTH_ENABLE = "point_smooth_enable"; +DLLSPEC daeString COLLADA_ELEMENT_POLYGON_OFFSET_FILL_ENABLE = "polygon_offset_fill_enable"; +DLLSPEC daeString COLLADA_ELEMENT_POLYGON_OFFSET_LINE_ENABLE = "polygon_offset_line_enable"; +DLLSPEC daeString COLLADA_ELEMENT_POLYGON_OFFSET_POINT_ENABLE = "polygon_offset_point_enable"; +DLLSPEC daeString COLLADA_ELEMENT_POLYGON_SMOOTH_ENABLE = "polygon_smooth_enable"; +DLLSPEC daeString COLLADA_ELEMENT_POLYGON_STIPPLE_ENABLE = "polygon_stipple_enable"; +DLLSPEC daeString COLLADA_ELEMENT_RESCALE_NORMAL_ENABLE = "rescale_normal_enable"; +DLLSPEC daeString COLLADA_ELEMENT_SAMPLE_ALPHA_TO_COVERAGE_ENABLE = "sample_alpha_to_coverage_enable"; +DLLSPEC daeString COLLADA_ELEMENT_SAMPLE_ALPHA_TO_ONE_ENABLE = "sample_alpha_to_one_enable"; +DLLSPEC daeString COLLADA_ELEMENT_SAMPLE_COVERAGE_ENABLE = "sample_coverage_enable"; +DLLSPEC daeString COLLADA_ELEMENT_SCISSOR_TEST_ENABLE = "scissor_test_enable"; +DLLSPEC daeString COLLADA_ELEMENT_STENCIL_TEST_ENABLE = "stencil_test_enable"; +DLLSPEC daeString COLLADA_ELEMENT_GL_HOOK_ABSTRACT = "gl_hook_abstract"; +DLLSPEC daeString COLLADA_ELEMENT_FUNC = "func"; +DLLSPEC daeString COLLADA_ELEMENT_VALUE = "value"; +DLLSPEC daeString COLLADA_ELEMENT_SRC = "src"; +DLLSPEC daeString COLLADA_ELEMENT_DEST = "dest"; +DLLSPEC daeString COLLADA_ELEMENT_SRC_RGB = "src_rgb"; +DLLSPEC daeString COLLADA_ELEMENT_DEST_RGB = "dest_rgb"; +DLLSPEC daeString COLLADA_ELEMENT_SRC_ALPHA = "src_alpha"; +DLLSPEC daeString COLLADA_ELEMENT_DEST_ALPHA = "dest_alpha"; +DLLSPEC daeString COLLADA_ELEMENT_rgb = "rgb"; +DLLSPEC daeString COLLADA_ELEMENT_MODE = "mode"; +DLLSPEC daeString COLLADA_ELEMENT_REF = "ref"; +DLLSPEC daeString COLLADA_ELEMENT_MASK = "mask"; +DLLSPEC daeString COLLADA_ELEMENT_FAIL = "fail"; +DLLSPEC daeString COLLADA_ELEMENT_ZFAIL = "zfail"; +DLLSPEC daeString COLLADA_ELEMENT_ZPASS = "zpass"; +DLLSPEC daeString COLLADA_ELEMENT_FRONT = "front"; +DLLSPEC daeString COLLADA_ELEMENT_BACK = "back"; +DLLSPEC daeString COLLADA_ELEMENT_BOOL1 = "bool1"; +DLLSPEC daeString COLLADA_ELEMENT_BOOL1X1 = "bool1x1"; +DLLSPEC daeString COLLADA_ELEMENT_BOOL1X2 = "bool1x2"; +DLLSPEC daeString COLLADA_ELEMENT_BOOL1X3 = "bool1x3"; +DLLSPEC daeString COLLADA_ELEMENT_BOOL1X4 = "bool1x4"; +DLLSPEC daeString COLLADA_ELEMENT_BOOL2X1 = "bool2x1"; +DLLSPEC daeString COLLADA_ELEMENT_BOOL2X2 = "bool2x2"; +DLLSPEC daeString COLLADA_ELEMENT_BOOL2X3 = "bool2x3"; +DLLSPEC daeString COLLADA_ELEMENT_BOOL2X4 = "bool2x4"; +DLLSPEC daeString COLLADA_ELEMENT_BOOL3X1 = "bool3x1"; +DLLSPEC daeString COLLADA_ELEMENT_BOOL3X2 = "bool3x2"; +DLLSPEC daeString COLLADA_ELEMENT_BOOL3X3 = "bool3x3"; +DLLSPEC daeString COLLADA_ELEMENT_BOOL3X4 = "bool3x4"; +DLLSPEC daeString COLLADA_ELEMENT_BOOL4X1 = "bool4x1"; +DLLSPEC daeString COLLADA_ELEMENT_BOOL4X2 = "bool4x2"; +DLLSPEC daeString COLLADA_ELEMENT_BOOL4X3 = "bool4x3"; +DLLSPEC daeString COLLADA_ELEMENT_BOOL4X4 = "bool4x4"; +DLLSPEC daeString COLLADA_ELEMENT_FLOAT1 = "float1"; +DLLSPEC daeString COLLADA_ELEMENT_INT1 = "int1"; +DLLSPEC daeString COLLADA_ELEMENT_INT1X1 = "int1x1"; +DLLSPEC daeString COLLADA_ELEMENT_INT1X2 = "int1x2"; +DLLSPEC daeString COLLADA_ELEMENT_INT1X3 = "int1x3"; +DLLSPEC daeString COLLADA_ELEMENT_INT1X4 = "int1x4"; +DLLSPEC daeString COLLADA_ELEMENT_INT2X1 = "int2x1"; +DLLSPEC daeString COLLADA_ELEMENT_INT2X2 = "int2x2"; +DLLSPEC daeString COLLADA_ELEMENT_INT2X3 = "int2x3"; +DLLSPEC daeString COLLADA_ELEMENT_INT2X4 = "int2x4"; +DLLSPEC daeString COLLADA_ELEMENT_INT3X1 = "int3x1"; +DLLSPEC daeString COLLADA_ELEMENT_INT3X2 = "int3x2"; +DLLSPEC daeString COLLADA_ELEMENT_INT3X3 = "int3x3"; +DLLSPEC daeString COLLADA_ELEMENT_INT3X4 = "int3x4"; +DLLSPEC daeString COLLADA_ELEMENT_INT4X1 = "int4x1"; +DLLSPEC daeString COLLADA_ELEMENT_INT4X2 = "int4x2"; +DLLSPEC daeString COLLADA_ELEMENT_INT4X3 = "int4x3"; +DLLSPEC daeString COLLADA_ELEMENT_INT4X4 = "int4x4"; +DLLSPEC daeString COLLADA_ELEMENT_HALF = "half"; +DLLSPEC daeString COLLADA_ELEMENT_HALF1 = "half1"; +DLLSPEC daeString COLLADA_ELEMENT_HALF2 = "half2"; +DLLSPEC daeString COLLADA_ELEMENT_HALF3 = "half3"; +DLLSPEC daeString COLLADA_ELEMENT_HALF4 = "half4"; +DLLSPEC daeString COLLADA_ELEMENT_HALF1X1 = "half1x1"; +DLLSPEC daeString COLLADA_ELEMENT_HALF1X2 = "half1x2"; +DLLSPEC daeString COLLADA_ELEMENT_HALF1X3 = "half1x3"; +DLLSPEC daeString COLLADA_ELEMENT_HALF1X4 = "half1x4"; +DLLSPEC daeString COLLADA_ELEMENT_HALF2X1 = "half2x1"; +DLLSPEC daeString COLLADA_ELEMENT_HALF2X2 = "half2x2"; +DLLSPEC daeString COLLADA_ELEMENT_HALF2X3 = "half2x3"; +DLLSPEC daeString COLLADA_ELEMENT_HALF2X4 = "half2x4"; +DLLSPEC daeString COLLADA_ELEMENT_HALF3X1 = "half3x1"; +DLLSPEC daeString COLLADA_ELEMENT_HALF3X2 = "half3x2"; +DLLSPEC daeString COLLADA_ELEMENT_HALF3X3 = "half3x3"; +DLLSPEC daeString COLLADA_ELEMENT_HALF3X4 = "half3x4"; +DLLSPEC daeString COLLADA_ELEMENT_HALF4X1 = "half4x1"; +DLLSPEC daeString COLLADA_ELEMENT_HALF4X2 = "half4x2"; +DLLSPEC daeString COLLADA_ELEMENT_HALF4X3 = "half4x3"; +DLLSPEC daeString COLLADA_ELEMENT_HALF4X4 = "half4x4"; +DLLSPEC daeString COLLADA_ELEMENT_FIXED = "fixed"; +DLLSPEC daeString COLLADA_ELEMENT_FIXED1 = "fixed1"; +DLLSPEC daeString COLLADA_ELEMENT_FIXED2 = "fixed2"; +DLLSPEC daeString COLLADA_ELEMENT_FIXED3 = "fixed3"; +DLLSPEC daeString COLLADA_ELEMENT_FIXED4 = "fixed4"; +DLLSPEC daeString COLLADA_ELEMENT_FIXED1X1 = "fixed1x1"; +DLLSPEC daeString COLLADA_ELEMENT_FIXED1X2 = "fixed1x2"; +DLLSPEC daeString COLLADA_ELEMENT_FIXED1X3 = "fixed1x3"; +DLLSPEC daeString COLLADA_ELEMENT_FIXED1X4 = "fixed1x4"; +DLLSPEC daeString COLLADA_ELEMENT_FIXED2X1 = "fixed2x1"; +DLLSPEC daeString COLLADA_ELEMENT_FIXED2X2 = "fixed2x2"; +DLLSPEC daeString COLLADA_ELEMENT_FIXED2X3 = "fixed2x3"; +DLLSPEC daeString COLLADA_ELEMENT_FIXED2X4 = "fixed2x4"; +DLLSPEC daeString COLLADA_ELEMENT_FIXED3X1 = "fixed3x1"; +DLLSPEC daeString COLLADA_ELEMENT_FIXED3X2 = "fixed3x2"; +DLLSPEC daeString COLLADA_ELEMENT_FIXED3X3 = "fixed3x3"; +DLLSPEC daeString COLLADA_ELEMENT_FIXED3X4 = "fixed3x4"; +DLLSPEC daeString COLLADA_ELEMENT_FIXED4X1 = "fixed4x1"; +DLLSPEC daeString COLLADA_ELEMENT_FIXED4X2 = "fixed4x2"; +DLLSPEC daeString COLLADA_ELEMENT_FIXED4X3 = "fixed4x3"; +DLLSPEC daeString COLLADA_ELEMENT_FIXED4X4 = "fixed4x4"; +DLLSPEC daeString COLLADA_ELEMENT_TEXTURE_PIPELINE = "texture_pipeline"; +DLLSPEC daeString COLLADA_ELEMENT_LIGHT_LINEAR_ATTENUTATION = "light_linear_attenutation"; +DLLSPEC daeString COLLADA_ELEMENT_TEXTURE_PIPELINE_ENABLE = "texture_pipeline_enable"; +DLLSPEC daeString COLLADA_ELEMENT_TEXTURE_UNIT = "texture_unit"; +DLLSPEC daeString COLLADA_ELEMENT_ASSET = "asset"; +DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_ANIMATIONS = "library_animations"; +DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_ANIMATION_CLIPS = "library_animation_clips"; +DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_CAMERAS = "library_cameras"; +DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_CONTROLLERS = "library_controllers"; +DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_GEOMETRIES = "library_geometries"; +DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_EFFECTS = "library_effects"; +DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_FORCE_FIELDS = "library_force_fields"; +DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_IMAGES = "library_images"; +DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_LIGHTS = "library_lights"; +DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_MATERIALS = "library_materials"; +DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_NODES = "library_nodes"; +DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_PHYSICS_MATERIALS = "library_physics_materials"; +DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_PHYSICS_MODELS = "library_physics_models"; +DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_PHYSICS_SCENES = "library_physics_scenes"; +DLLSPEC daeString COLLADA_ELEMENT_LIBRARY_VISUAL_SCENES = "library_visual_scenes"; +DLLSPEC daeString COLLADA_ELEMENT_SCENE = "scene"; +DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_PHYSICS_SCENE = "instance_physics_scene"; +DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_VISUAL_SCENE = "instance_visual_scene"; +DLLSPEC daeString COLLADA_ELEMENT_IDREF_ARRAY = "IDREF_array"; +DLLSPEC daeString COLLADA_ELEMENT_NAME_ARRAY = "Name_array"; +DLLSPEC daeString COLLADA_ELEMENT_BOOL_ARRAY = "bool_array"; +DLLSPEC daeString COLLADA_ELEMENT_FLOAT_ARRAY = "float_array"; +DLLSPEC daeString COLLADA_ELEMENT_INT_ARRAY = "int_array"; +DLLSPEC daeString COLLADA_ELEMENT_TECHNIQUE_COMMON = "technique_common"; +DLLSPEC daeString COLLADA_ELEMENT_TECHNIQUE = "technique"; +DLLSPEC daeString COLLADA_ELEMENT_ACCESSOR = "accessor"; +DLLSPEC daeString COLLADA_ELEMENT_CONVEX_MESH = "convex_mesh"; +DLLSPEC daeString COLLADA_ELEMENT_MESH = "mesh"; +DLLSPEC daeString COLLADA_ELEMENT_SPLINE = "spline"; +DLLSPEC daeString COLLADA_ELEMENT_VERTICES = "vertices"; +DLLSPEC daeString COLLADA_ELEMENT_LINES = "lines"; +DLLSPEC daeString COLLADA_ELEMENT_LINESTRIPS = "linestrips"; +DLLSPEC daeString COLLADA_ELEMENT_POLYGONS = "polygons"; +DLLSPEC daeString COLLADA_ELEMENT_POLYLIST = "polylist"; +DLLSPEC daeString COLLADA_ELEMENT_TRIANGLES = "triangles"; +DLLSPEC daeString COLLADA_ELEMENT_TRIFANS = "trifans"; +DLLSPEC daeString COLLADA_ELEMENT_TRISTRIPS = "tristrips"; +DLLSPEC daeString COLLADA_ELEMENT_CONTROL_VERTICES = "control_vertices"; +DLLSPEC daeString COLLADA_ELEMENT_INPUT = "input"; +DLLSPEC daeString COLLADA_ELEMENT_P = "p"; +DLLSPEC daeString COLLADA_ELEMENT_PH = "ph"; +DLLSPEC daeString COLLADA_ELEMENT_H = "h"; +DLLSPEC daeString COLLADA_ELEMENT_VCOUNT = "vcount"; +DLLSPEC daeString COLLADA_ELEMENT_DATA = "data"; +DLLSPEC daeString COLLADA_ELEMENT_AMBIENT = "ambient"; +DLLSPEC daeString COLLADA_ELEMENT_DIRECTIONAL = "directional"; +DLLSPEC daeString COLLADA_ELEMENT_POINT = "point"; +DLLSPEC daeString COLLADA_ELEMENT_SPOT = "spot"; +DLLSPEC daeString COLLADA_ELEMENT_CONSTANT_ATTENUATION = "constant_attenuation"; +DLLSPEC daeString COLLADA_ELEMENT_LINEAR_ATTENUATION = "linear_attenuation"; +DLLSPEC daeString COLLADA_ELEMENT_QUADRATIC_ATTENUATION = "quadratic_attenuation"; +DLLSPEC daeString COLLADA_ELEMENT_FALLOFF_ANGLE = "falloff_angle"; +DLLSPEC daeString COLLADA_ELEMENT_FALLOFF_EXPONENT = "falloff_exponent"; +DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_EFFECT = "instance_effect"; +DLLSPEC daeString COLLADA_ELEMENT_OPTICS = "optics"; +DLLSPEC daeString COLLADA_ELEMENT_IMAGER = "imager"; +DLLSPEC daeString COLLADA_ELEMENT_ORTHOGRAPHIC = "orthographic"; +DLLSPEC daeString COLLADA_ELEMENT_PERSPECTIVE = "perspective"; +DLLSPEC daeString COLLADA_ELEMENT_XMAG = "xmag"; +DLLSPEC daeString COLLADA_ELEMENT_YMAG = "ymag"; +DLLSPEC daeString COLLADA_ELEMENT_ASPECT_RATIO = "aspect_ratio"; +DLLSPEC daeString COLLADA_ELEMENT_ZNEAR = "znear"; +DLLSPEC daeString COLLADA_ELEMENT_ZFAR = "zfar"; +DLLSPEC daeString COLLADA_ELEMENT_XFOV = "xfov"; +DLLSPEC daeString COLLADA_ELEMENT_YFOV = "yfov"; +DLLSPEC daeString COLLADA_ELEMENT_SAMPLER = "sampler"; +DLLSPEC daeString COLLADA_ELEMENT_CHANNEL = "channel"; +DLLSPEC daeString COLLADA_ELEMENT_ANIMATION = "animation"; +DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_ANIMATION = "instance_animation"; +DLLSPEC daeString COLLADA_ELEMENT_SKIN = "skin"; +DLLSPEC daeString COLLADA_ELEMENT_MORPH = "morph"; +DLLSPEC daeString COLLADA_ELEMENT_BIND_SHAPE_MATRIX = "bind_shape_matrix"; +DLLSPEC daeString COLLADA_ELEMENT_JOINTS = "joints"; +DLLSPEC daeString COLLADA_ELEMENT_VERTEX_WEIGHTS = "vertex_weights"; +DLLSPEC daeString COLLADA_ELEMENT_V = "v"; +DLLSPEC daeString COLLADA_ELEMENT_TARGETS = "targets"; +DLLSPEC daeString COLLADA_ELEMENT_CONTRIBUTOR = "contributor"; +DLLSPEC daeString COLLADA_ELEMENT_CREATED = "created"; +DLLSPEC daeString COLLADA_ELEMENT_KEYWORDS = "keywords"; +DLLSPEC daeString COLLADA_ELEMENT_MODIFIED = "modified"; +DLLSPEC daeString COLLADA_ELEMENT_REVISION = "revision"; +DLLSPEC daeString COLLADA_ELEMENT_SUBJECT = "subject"; +DLLSPEC daeString COLLADA_ELEMENT_TITLE = "title"; +DLLSPEC daeString COLLADA_ELEMENT_UNIT = "unit"; +DLLSPEC daeString COLLADA_ELEMENT_UP_AXIS = "up_axis"; +DLLSPEC daeString COLLADA_ELEMENT_AUTHOR = "author"; +DLLSPEC daeString COLLADA_ELEMENT_AUTHORING_TOOL = "authoring_tool"; +DLLSPEC daeString COLLADA_ELEMENT_COMMENTS = "comments"; +DLLSPEC daeString COLLADA_ELEMENT_COPYRIGHT = "copyright"; +DLLSPEC daeString COLLADA_ELEMENT_SOURCE_DATA = "source_data"; +DLLSPEC daeString COLLADA_ELEMENT_LOOKAT = "lookat"; +DLLSPEC daeString COLLADA_ELEMENT_MATRIX = "matrix"; +DLLSPEC daeString COLLADA_ELEMENT_ROTATE = "rotate"; +DLLSPEC daeString COLLADA_ELEMENT_SCALE = "scale"; +DLLSPEC daeString COLLADA_ELEMENT_SKEW = "skew"; +DLLSPEC daeString COLLADA_ELEMENT_TRANSLATE = "translate"; +DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_CAMERA = "instance_camera"; +DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_CONTROLLER = "instance_controller"; +DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_GEOMETRY = "instance_geometry"; +DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_LIGHT = "instance_light"; +DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_NODE = "instance_node"; +DLLSPEC daeString COLLADA_ELEMENT_NODE = "node"; +DLLSPEC daeString COLLADA_ELEMENT_EVALUATE_SCENE = "evaluate_scene"; +DLLSPEC daeString COLLADA_ELEMENT_RENDER = "render"; +DLLSPEC daeString COLLADA_ELEMENT_LAYER = "layer"; +DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_MATERIAL = "instance_material"; +DLLSPEC daeString COLLADA_ELEMENT_SKELETON = "skeleton"; +DLLSPEC daeString COLLADA_ELEMENT_BIND_MATERIAL = "bind_material"; +DLLSPEC daeString COLLADA_ELEMENT_TECHNIQUE_HINT = "technique_hint"; +DLLSPEC daeString COLLADA_ELEMENT_BIND = "bind"; +DLLSPEC daeString COLLADA_ELEMENT_BIND_VERTEX_INPUT = "bind_vertex_input"; +DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_FORCE_FIELD = "instance_force_field"; +DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_RIGID_BODY = "instance_rigid_body"; +DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_RIGID_CONSTRAINT = "instance_rigid_constraint"; +DLLSPEC daeString COLLADA_ELEMENT_ANGULAR_VELOCITY = "angular_velocity"; +DLLSPEC daeString COLLADA_ELEMENT_VELOCITY = "velocity"; +DLLSPEC daeString COLLADA_ELEMENT_DYNAMIC = "dynamic"; +DLLSPEC daeString COLLADA_ELEMENT_MASS = "mass"; +DLLSPEC daeString COLLADA_ELEMENT_MASS_FRAME = "mass_frame"; +DLLSPEC daeString COLLADA_ELEMENT_INERTIA = "inertia"; +DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_PHYSICS_MATERIAL = "instance_physics_material"; +DLLSPEC daeString COLLADA_ELEMENT_PHYSICS_MATERIAL = "physics_material"; +DLLSPEC daeString COLLADA_ELEMENT_SHAPE = "shape"; +DLLSPEC daeString COLLADA_ELEMENT_HOLLOW = "hollow"; +DLLSPEC daeString COLLADA_ELEMENT_DENSITY = "density"; +DLLSPEC daeString COLLADA_ELEMENT_PLANE = "plane"; +DLLSPEC daeString COLLADA_ELEMENT_BOX = "box"; +DLLSPEC daeString COLLADA_ELEMENT_SPHERE = "sphere"; +DLLSPEC daeString COLLADA_ELEMENT_CYLINDER = "cylinder"; +DLLSPEC daeString COLLADA_ELEMENT_TAPERED_CYLINDER = "tapered_cylinder"; +DLLSPEC daeString COLLADA_ELEMENT_CAPSULE = "capsule"; +DLLSPEC daeString COLLADA_ELEMENT_TAPERED_CAPSULE = "tapered_capsule"; +DLLSPEC daeString COLLADA_ELEMENT_ANIMATION_CLIP = "animation_clip"; +DLLSPEC daeString COLLADA_ELEMENT_CAMERA = "camera"; +DLLSPEC daeString COLLADA_ELEMENT_CONTROLLER = "controller"; +DLLSPEC daeString COLLADA_ELEMENT_GEOMETRY = "geometry"; +DLLSPEC daeString COLLADA_ELEMENT_EFFECT = "effect"; +DLLSPEC daeString COLLADA_ELEMENT_FORCE_FIELD = "force_field"; +DLLSPEC daeString COLLADA_ELEMENT_IMAGE = "image"; +DLLSPEC daeString COLLADA_ELEMENT_LIGHT = "light"; +DLLSPEC daeString COLLADA_ELEMENT_MATERIAL = "material"; +DLLSPEC daeString COLLADA_ELEMENT_PHYSICS_MODEL = "physics_model"; +DLLSPEC daeString COLLADA_ELEMENT_PHYSICS_SCENE = "physics_scene"; +DLLSPEC daeString COLLADA_ELEMENT_VISUAL_SCENE = "visual_scene"; +DLLSPEC daeString COLLADA_ELEMENT_NEWPARAM = "newparam"; +DLLSPEC daeString COLLADA_ELEMENT_FX_PROFILE_ABSTRACT = "fx_profile_abstract"; +DLLSPEC daeString COLLADA_ELEMENT_PROFILE_GLSL = "profile_GLSL"; +DLLSPEC daeString COLLADA_ELEMENT_PASS = "pass"; +DLLSPEC daeString COLLADA_ELEMENT_COLOR_TARGET = "color_target"; +DLLSPEC daeString COLLADA_ELEMENT_DEPTH_TARGET = "depth_target"; +DLLSPEC daeString COLLADA_ELEMENT_STENCIL_TARGET = "stencil_target"; +DLLSPEC daeString COLLADA_ELEMENT_COLOR_CLEAR = "color_clear"; +DLLSPEC daeString COLLADA_ELEMENT_DEPTH_CLEAR = "depth_clear"; +DLLSPEC daeString COLLADA_ELEMENT_STENCIL_CLEAR = "stencil_clear"; +DLLSPEC daeString COLLADA_ELEMENT_DRAW = "draw"; +DLLSPEC daeString COLLADA_ELEMENT_GL_PIPELINE_SETTINGS = "gl_pipeline_settings"; +DLLSPEC daeString COLLADA_ELEMENT_SHADER = "shader"; +DLLSPEC daeString COLLADA_ELEMENT_COMPILER_TARGET = "compiler_target"; +DLLSPEC daeString COLLADA_ELEMENT_COMPILER_OPTIONS = "compiler_options"; +DLLSPEC daeString COLLADA_ELEMENT_PROFILE_COMMON = "profile_COMMON"; +DLLSPEC daeString COLLADA_ELEMENT_LAMBERT = "lambert"; +DLLSPEC daeString COLLADA_ELEMENT_PHONG = "phong"; +DLLSPEC daeString COLLADA_ELEMENT_BLINN = "blinn"; +DLLSPEC daeString COLLADA_ELEMENT_EMISSION = "emission"; +DLLSPEC daeString COLLADA_ELEMENT_REFLECTIVE = "reflective"; +DLLSPEC daeString COLLADA_ELEMENT_REFLECTIVITY = "reflectivity"; +DLLSPEC daeString COLLADA_ELEMENT_TRANSPARENT = "transparent"; +DLLSPEC daeString COLLADA_ELEMENT_TRANSPARENCY = "transparency"; +DLLSPEC daeString COLLADA_ELEMENT_INDEX_OF_REFRACTION = "index_of_refraction"; +DLLSPEC daeString COLLADA_ELEMENT_DIFFUSE = "diffuse"; +DLLSPEC daeString COLLADA_ELEMENT_SPECULAR = "specular"; +DLLSPEC daeString COLLADA_ELEMENT_SHININESS = "shininess"; +DLLSPEC daeString COLLADA_ELEMENT_PROFILE_CG = "profile_CG"; +DLLSPEC daeString COLLADA_ELEMENT_PROFILE_GLES = "profile_GLES"; +DLLSPEC daeString COLLADA_ELEMENT_GLES_PIPELINE_SETTINGS = "gles_pipeline_settings"; +DLLSPEC daeString COLLADA_ELEMENT_HALF_EXTENTS = "half_extents"; +DLLSPEC daeString COLLADA_ELEMENT_EQUATION = "equation"; +DLLSPEC daeString COLLADA_ELEMENT_RADIUS = "radius"; +DLLSPEC daeString COLLADA_ELEMENT_HEIGHT = "height"; +DLLSPEC daeString COLLADA_ELEMENT_RADIUS1 = "radius1"; +DLLSPEC daeString COLLADA_ELEMENT_RADIUS2 = "radius2"; +DLLSPEC daeString COLLADA_ELEMENT_DYNAMIC_FRICTION = "dynamic_friction"; +DLLSPEC daeString COLLADA_ELEMENT_RESTITUTION = "restitution"; +DLLSPEC daeString COLLADA_ELEMENT_STATIC_FRICTION = "static_friction"; +DLLSPEC daeString COLLADA_ELEMENT_INSTANCE_PHYSICS_MODEL = "instance_physics_model"; +DLLSPEC daeString COLLADA_ELEMENT_GRAVITY = "gravity"; +DLLSPEC daeString COLLADA_ELEMENT_TIME_STEP = "time_step"; +DLLSPEC daeString COLLADA_ELEMENT_REF_ATTACHMENT = "ref_attachment"; +DLLSPEC daeString COLLADA_ELEMENT_ATTACHMENT = "attachment"; +DLLSPEC daeString COLLADA_ELEMENT_ENABLED = "enabled"; +DLLSPEC daeString COLLADA_ELEMENT_INTERPENETRATE = "interpenetrate"; +DLLSPEC daeString COLLADA_ELEMENT_LIMITS = "limits"; +DLLSPEC daeString COLLADA_ELEMENT_SPRING = "spring"; +DLLSPEC daeString COLLADA_ELEMENT_SWING_CONE_AND_TWIST = "swing_cone_and_twist"; +DLLSPEC daeString COLLADA_ELEMENT_LINEAR = "linear"; +DLLSPEC daeString COLLADA_ELEMENT_MIN = "min"; +DLLSPEC daeString COLLADA_ELEMENT_MAX = "max"; +DLLSPEC daeString COLLADA_ELEMENT_ANGULAR = "angular"; +DLLSPEC daeString COLLADA_ELEMENT_STIFFNESS = "stiffness"; +DLLSPEC daeString COLLADA_ELEMENT_DAMPING = "damping"; +DLLSPEC daeString COLLADA_ELEMENT_TARGET_VALUE = "target_value"; +DLLSPEC daeString COLLADA_ELEMENT_RIGID_BODY = "rigid_body"; +DLLSPEC daeString COLLADA_ELEMENT_RIGID_CONSTRAINT = "rigid_constraint"; diff --git a/Engine/lib/collada/src/1.4/dom/domController.cpp b/Engine/lib/collada/src/1.4/dom/domController.cpp new file mode 100644 index 000000000..40e03b364 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domController.cpp @@ -0,0 +1,111 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domController::create(DAE& dae) +{ + domControllerRef ref = new domController(dae); + return ref; +} + + +daeMetaElement * +domController::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "controller" ); + meta->registerClass(domController::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domController,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 1, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "skin" ); + mea->setOffset( daeOffsetOf(domController,elemSkin) ); + mea->setElementType( domSkin::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "morph" ); + mea->setOffset( daeOffsetOf(domController,elemMorph) ); + mea->setElementType( domMorph::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domController,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domController,_contents)); + meta->addContentsOrder(daeOffsetOf(domController,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domController,_CMData), 1); + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domController , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domController , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domController)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domConvex_mesh.cpp b/Engine/lib/collada/src/1.4/dom/domConvex_mesh.cpp new file mode 100644 index 000000000..211470cda --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domConvex_mesh.cpp @@ -0,0 +1,136 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domConvex_mesh::create(DAE& dae) +{ + domConvex_meshRef ref = new domConvex_mesh(dae); + return ref; +} + + +daeMetaElement * +domConvex_mesh::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "convex_mesh" ); + meta->registerClass(domConvex_mesh::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 0, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, -1 ); + mea->setName( "source" ); + mea->setOffset( daeOffsetOf(domConvex_mesh,elemSource_array) ); + mea->setElementType( domSource::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "vertices" ); + mea->setOffset( daeOffsetOf(domConvex_mesh,elemVertices) ); + mea->setElementType( domVertices::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 2, 0, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "lines" ); + mea->setOffset( daeOffsetOf(domConvex_mesh,elemLines_array) ); + mea->setElementType( domLines::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "linestrips" ); + mea->setOffset( daeOffsetOf(domConvex_mesh,elemLinestrips_array) ); + mea->setElementType( domLinestrips::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "polygons" ); + mea->setOffset( daeOffsetOf(domConvex_mesh,elemPolygons_array) ); + mea->setElementType( domPolygons::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "polylist" ); + mea->setOffset( daeOffsetOf(domConvex_mesh,elemPolylist_array) ); + mea->setElementType( domPolylist::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "triangles" ); + mea->setOffset( daeOffsetOf(domConvex_mesh,elemTriangles_array) ); + mea->setElementType( domTriangles::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "trifans" ); + mea->setOffset( daeOffsetOf(domConvex_mesh,elemTrifans_array) ); + mea->setElementType( domTrifans::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "tristrips" ); + mea->setOffset( daeOffsetOf(domConvex_mesh,elemTristrips_array) ); + mea->setElementType( domTristrips::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3003, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domConvex_mesh,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3003 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domConvex_mesh,_contents)); + meta->addContentsOrder(daeOffsetOf(domConvex_mesh,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domConvex_mesh,_CMData), 1); + // Add attribute: convex_hull_of + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "convex_hull_of" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domConvex_mesh , attrConvex_hull_of )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domConvex_mesh)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domCylinder.cpp b/Engine/lib/collada/src/1.4/dom/domCylinder.cpp new file mode 100644 index 000000000..7bbe8950e --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domCylinder.cpp @@ -0,0 +1,145 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domCylinder::create(DAE& dae) +{ + domCylinderRef ref = new domCylinder(dae); + return ref; +} + + +daeMetaElement * +domCylinder::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "cylinder" ); + meta->registerClass(domCylinder::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "height" ); + mea->setOffset( daeOffsetOf(domCylinder,elemHeight) ); + mea->setElementType( domCylinder::domHeight::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "radius" ); + mea->setOffset( daeOffsetOf(domCylinder,elemRadius) ); + mea->setElementType( domCylinder::domRadius::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domCylinder,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domCylinder)); + meta->validate(); + + return meta; +} + +daeElementRef +domCylinder::domHeight::create(DAE& dae) +{ + domCylinder::domHeightRef ref = new domCylinder::domHeight(dae); + return ref; +} + + +daeMetaElement * +domCylinder::domHeight::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "height" ); + meta->registerClass(domCylinder::domHeight::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domCylinder::domHeight , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCylinder::domHeight)); + meta->validate(); + + return meta; +} + +daeElementRef +domCylinder::domRadius::create(DAE& dae) +{ + domCylinder::domRadiusRef ref = new domCylinder::domRadius(dae); + return ref; +} + + +daeMetaElement * +domCylinder::domRadius::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "radius" ); + meta->registerClass(domCylinder::domRadius::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float2")); + ma->setOffset( daeOffsetOf( domCylinder::domRadius , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domCylinder::domRadius)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domEffect.cpp b/Engine/lib/collada/src/1.4/dom/domEffect.cpp new file mode 100644 index 000000000..a30776b26 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domEffect.cpp @@ -0,0 +1,150 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domEffect::create(DAE& dae) +{ + domEffectRef ref = new domEffect(dae); + return ref; +} + +#include +#include +#include +#include + +daeMetaElement * +domEffect::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "effect" ); + meta->registerClass(domEffect::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domEffect,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 0, -1 ); + mea->setName( "annotate" ); + mea->setOffset( daeOffsetOf(domEffect,elemAnnotate_array) ); + mea->setElementType( domFx_annotate_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "image" ); + mea->setOffset( daeOffsetOf(domEffect,elemImage_array) ); + mea->setElementType( domImage::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3, 0, -1 ); + mea->setName( "newparam" ); + mea->setOffset( daeOffsetOf(domEffect,elemNewparam_array) ); + mea->setElementType( domFx_newparam_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 4, 1, -1 ); + mea->setName( "fx_profile_abstract" ); + mea->setOffset( daeOffsetOf(domEffect,elemFx_profile_abstract_array) ); + mea->setElementType( domFx_profile_abstract::registerElement(dae) ); + cm->appendChild( mea ); + + + mea = new daeMetaElementArrayAttribute( meta, cm, 4, 1, -1 ); + mea->setName( "profile_GLSL" ); + mea->setOffset( daeOffsetOf(domEffect,elemFx_profile_abstract_array) ); + mea->setElementType( domProfile_GLSL::registerElement(dae) ); + cm->appendChild( mea ); + + + mea = new daeMetaElementArrayAttribute( meta, cm, 4, 1, -1 ); + mea->setName( "profile_COMMON" ); + mea->setOffset( daeOffsetOf(domEffect,elemFx_profile_abstract_array) ); + mea->setElementType( domProfile_COMMON::registerElement(dae) ); + cm->appendChild( mea ); + + + mea = new daeMetaElementArrayAttribute( meta, cm, 4, 1, -1 ); + mea->setName( "profile_CG" ); + mea->setOffset( daeOffsetOf(domEffect,elemFx_profile_abstract_array) ); + mea->setElementType( domProfile_CG::registerElement(dae) ); + cm->appendChild( mea ); + + + mea = new daeMetaElementArrayAttribute( meta, cm, 4, 1, -1 ); + mea->setName( "profile_GLES" ); + mea->setOffset( daeOffsetOf(domEffect,elemFx_profile_abstract_array) ); + mea->setElementType( domProfile_GLES::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 5, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domEffect,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 5 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domEffect,_contents)); + meta->addContentsOrder(daeOffsetOf(domEffect,_contentsOrder)); + + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domEffect , attrId )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domEffect , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domEffect)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domEllipsoid.cpp b/Engine/lib/collada/src/1.4/dom/domEllipsoid.cpp new file mode 100644 index 000000000..67a2550af --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domEllipsoid.cpp @@ -0,0 +1,97 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domEllipsoid::create(DAE& dae) +{ + domEllipsoidRef ref = new domEllipsoid(dae); + return ref; +} + + +daeMetaElement * +domEllipsoid::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "ellipsoid" ); + meta->registerClass(domEllipsoid::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "size" ); + mea->setOffset( daeOffsetOf(domEllipsoid,elemSize) ); + mea->setElementType( domEllipsoid::domSize::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domEllipsoid)); + meta->validate(); + + return meta; +} + +daeElementRef +domEllipsoid::domSize::create(DAE& dae) +{ + domEllipsoid::domSizeRef ref = new domEllipsoid::domSize(dae); + return ref; +} + + +daeMetaElement * +domEllipsoid::domSize::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "size" ); + meta->registerClass(domEllipsoid::domSize::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float3")); + ma->setOffset( daeOffsetOf( domEllipsoid::domSize , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domEllipsoid::domSize)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domExtra.cpp b/Engine/lib/collada/src/1.4/dom/domExtra.cpp new file mode 100644 index 000000000..4fa1e81f2 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domExtra.cpp @@ -0,0 +1,100 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domExtra::create(DAE& dae) +{ + domExtraRef ref = new domExtra(dae); + return ref; +} + + +daeMetaElement * +domExtra::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "extra" ); + meta->registerClass(domExtra::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domExtra,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 1, -1 ); + mea->setName( "technique" ); + mea->setOffset( daeOffsetOf(domExtra,elemTechnique_array) ); + mea->setElementType( domTechnique::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domExtra , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domExtra , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: type + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "type" ); + ma->setType( dae.getAtomicTypes().get("xsNMTOKEN")); + ma->setOffset( daeOffsetOf( domExtra , attrType )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domExtra)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domFloat_array.cpp b/Engine/lib/collada/src/1.4/dom/domFloat_array.cpp new file mode 100644 index 000000000..179de18f3 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domFloat_array.cpp @@ -0,0 +1,116 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domFloat_array::create(DAE& dae) +{ + domFloat_arrayRef ref = new domFloat_array(dae); + return ref; +} + + +daeMetaElement * +domFloat_array::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float_array" ); + meta->registerClass(domFloat_array::create); + + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("ListOfFloats")); + ma->setOffset( daeOffsetOf( domFloat_array , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domFloat_array , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domFloat_array , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: count + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "count" ); + ma->setType( dae.getAtomicTypes().get("Uint")); + ma->setOffset( daeOffsetOf( domFloat_array , attrCount )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: digits + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "digits" ); + ma->setType( dae.getAtomicTypes().get("xsShort")); + ma->setOffset( daeOffsetOf( domFloat_array , attrDigits )); + ma->setContainer( meta ); + ma->setDefaultString( "6"); + + meta->appendAttribute(ma); + } + + // Add attribute: magnitude + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "magnitude" ); + ma->setType( dae.getAtomicTypes().get("xsShort")); + ma->setOffset( daeOffsetOf( domFloat_array , attrMagnitude )); + ma->setContainer( meta ); + ma->setDefaultString( "38"); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFloat_array)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domForce_field.cpp b/Engine/lib/collada/src/1.4/dom/domForce_field.cpp new file mode 100644 index 000000000..16efb912d --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domForce_field.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domForce_field::create(DAE& dae) +{ + domForce_fieldRef ref = new domForce_field(dae); + return ref; +} + + +daeMetaElement * +domForce_field::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "force_field" ); + meta->registerClass(domForce_field::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domForce_field,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 1, -1 ); + mea->setName( "technique" ); + mea->setOffset( daeOffsetOf(domForce_field,elemTechnique_array) ); + mea->setElementType( domTechnique::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domForce_field,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domForce_field , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domForce_field , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domForce_field)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domFx_annotate_common.cpp b/Engine/lib/collada/src/1.4/dom/domFx_annotate_common.cpp new file mode 100644 index 000000000..1e8b224db --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domFx_annotate_common.cpp @@ -0,0 +1,73 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domFx_annotate_common::create(DAE& dae) +{ + domFx_annotate_commonRef ref = new domFx_annotate_common(dae); + return ref; +} + + +daeMetaElement * +domFx_annotate_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fx_annotate_common" ); + meta->registerClass(domFx_annotate_common::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fx_annotate_type_common" ); + mea->setOffset( daeOffsetOf(domFx_annotate_common,elemFx_annotate_type_common) ); + mea->setElementType( domFx_annotate_type_common::registerElement(dae) ); + cm->appendChild( new daeMetaGroup( mea, meta, cm, 0, 1, 1 ) ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domFx_annotate_common , attrName )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_annotate_common)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domFx_annotate_type_common.cpp b/Engine/lib/collada/src/1.4/dom/domFx_annotate_type_common.cpp new file mode 100644 index 000000000..992cb75d6 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domFx_annotate_type_common.cpp @@ -0,0 +1,732 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domFx_annotate_type_common::create(DAE& dae) +{ + domFx_annotate_type_commonRef ref = new domFx_annotate_type_common(dae); + return ref; +} + + +daeMetaElement * +domFx_annotate_type_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fx_annotate_type_common" ); + meta->registerClass(domFx_annotate_type_common::create); + + meta->setIsTransparent( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool" ); + mea->setOffset( daeOffsetOf(domFx_annotate_type_common,elemBool) ); + mea->setElementType( domFx_annotate_type_common::domBool::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool2" ); + mea->setOffset( daeOffsetOf(domFx_annotate_type_common,elemBool2) ); + mea->setElementType( domFx_annotate_type_common::domBool2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool3" ); + mea->setOffset( daeOffsetOf(domFx_annotate_type_common,elemBool3) ); + mea->setElementType( domFx_annotate_type_common::domBool3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool4" ); + mea->setOffset( daeOffsetOf(domFx_annotate_type_common,elemBool4) ); + mea->setElementType( domFx_annotate_type_common::domBool4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int" ); + mea->setOffset( daeOffsetOf(domFx_annotate_type_common,elemInt) ); + mea->setElementType( domFx_annotate_type_common::domInt::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int2" ); + mea->setOffset( daeOffsetOf(domFx_annotate_type_common,elemInt2) ); + mea->setElementType( domFx_annotate_type_common::domInt2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int3" ); + mea->setOffset( daeOffsetOf(domFx_annotate_type_common,elemInt3) ); + mea->setElementType( domFx_annotate_type_common::domInt3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int4" ); + mea->setOffset( daeOffsetOf(domFx_annotate_type_common,elemInt4) ); + mea->setElementType( domFx_annotate_type_common::domInt4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float" ); + mea->setOffset( daeOffsetOf(domFx_annotate_type_common,elemFloat) ); + mea->setElementType( domFx_annotate_type_common::domFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float2" ); + mea->setOffset( daeOffsetOf(domFx_annotate_type_common,elemFloat2) ); + mea->setElementType( domFx_annotate_type_common::domFloat2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float3" ); + mea->setOffset( daeOffsetOf(domFx_annotate_type_common,elemFloat3) ); + mea->setElementType( domFx_annotate_type_common::domFloat3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float4" ); + mea->setOffset( daeOffsetOf(domFx_annotate_type_common,elemFloat4) ); + mea->setElementType( domFx_annotate_type_common::domFloat4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float2x2" ); + mea->setOffset( daeOffsetOf(domFx_annotate_type_common,elemFloat2x2) ); + mea->setElementType( domFx_annotate_type_common::domFloat2x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float3x3" ); + mea->setOffset( daeOffsetOf(domFx_annotate_type_common,elemFloat3x3) ); + mea->setElementType( domFx_annotate_type_common::domFloat3x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float4x4" ); + mea->setOffset( daeOffsetOf(domFx_annotate_type_common,elemFloat4x4) ); + mea->setElementType( domFx_annotate_type_common::domFloat4x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "string" ); + mea->setOffset( daeOffsetOf(domFx_annotate_type_common,elemString) ); + mea->setElementType( domFx_annotate_type_common::domString::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domFx_annotate_type_common,_contents)); + meta->addContentsOrder(daeOffsetOf(domFx_annotate_type_common,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domFx_annotate_type_common,_CMData), 1); + meta->setElementSize(sizeof(domFx_annotate_type_common)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_annotate_type_common::domBool::create(DAE& dae) +{ + domFx_annotate_type_common::domBoolRef ref = new domFx_annotate_type_common::domBool(dae); + return ref; +} + + +daeMetaElement * +domFx_annotate_type_common::domBool::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool" ); + meta->registerClass(domFx_annotate_type_common::domBool::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domFx_annotate_type_common::domBool , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_annotate_type_common::domBool)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_annotate_type_common::domBool2::create(DAE& dae) +{ + domFx_annotate_type_common::domBool2Ref ref = new domFx_annotate_type_common::domBool2(dae); + return ref; +} + + +daeMetaElement * +domFx_annotate_type_common::domBool2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool2" ); + meta->registerClass(domFx_annotate_type_common::domBool2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Bool2")); + ma->setOffset( daeOffsetOf( domFx_annotate_type_common::domBool2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_annotate_type_common::domBool2)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_annotate_type_common::domBool3::create(DAE& dae) +{ + domFx_annotate_type_common::domBool3Ref ref = new domFx_annotate_type_common::domBool3(dae); + return ref; +} + + +daeMetaElement * +domFx_annotate_type_common::domBool3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool3" ); + meta->registerClass(domFx_annotate_type_common::domBool3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Bool3")); + ma->setOffset( daeOffsetOf( domFx_annotate_type_common::domBool3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_annotate_type_common::domBool3)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_annotate_type_common::domBool4::create(DAE& dae) +{ + domFx_annotate_type_common::domBool4Ref ref = new domFx_annotate_type_common::domBool4(dae); + return ref; +} + + +daeMetaElement * +domFx_annotate_type_common::domBool4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool4" ); + meta->registerClass(domFx_annotate_type_common::domBool4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Bool4")); + ma->setOffset( daeOffsetOf( domFx_annotate_type_common::domBool4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_annotate_type_common::domBool4)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_annotate_type_common::domInt::create(DAE& dae) +{ + domFx_annotate_type_common::domIntRef ref = new domFx_annotate_type_common::domInt(dae); + return ref; +} + + +daeMetaElement * +domFx_annotate_type_common::domInt::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int" ); + meta->registerClass(domFx_annotate_type_common::domInt::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Int")); + ma->setOffset( daeOffsetOf( domFx_annotate_type_common::domInt , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_annotate_type_common::domInt)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_annotate_type_common::domInt2::create(DAE& dae) +{ + domFx_annotate_type_common::domInt2Ref ref = new domFx_annotate_type_common::domInt2(dae); + return ref; +} + + +daeMetaElement * +domFx_annotate_type_common::domInt2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int2" ); + meta->registerClass(domFx_annotate_type_common::domInt2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Int2")); + ma->setOffset( daeOffsetOf( domFx_annotate_type_common::domInt2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_annotate_type_common::domInt2)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_annotate_type_common::domInt3::create(DAE& dae) +{ + domFx_annotate_type_common::domInt3Ref ref = new domFx_annotate_type_common::domInt3(dae); + return ref; +} + + +daeMetaElement * +domFx_annotate_type_common::domInt3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int3" ); + meta->registerClass(domFx_annotate_type_common::domInt3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Int3")); + ma->setOffset( daeOffsetOf( domFx_annotate_type_common::domInt3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_annotate_type_common::domInt3)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_annotate_type_common::domInt4::create(DAE& dae) +{ + domFx_annotate_type_common::domInt4Ref ref = new domFx_annotate_type_common::domInt4(dae); + return ref; +} + + +daeMetaElement * +domFx_annotate_type_common::domInt4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int4" ); + meta->registerClass(domFx_annotate_type_common::domInt4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Int4")); + ma->setOffset( daeOffsetOf( domFx_annotate_type_common::domInt4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_annotate_type_common::domInt4)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_annotate_type_common::domFloat::create(DAE& dae) +{ + domFx_annotate_type_common::domFloatRef ref = new domFx_annotate_type_common::domFloat(dae); + return ref; +} + + +daeMetaElement * +domFx_annotate_type_common::domFloat::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float" ); + meta->registerClass(domFx_annotate_type_common::domFloat::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domFx_annotate_type_common::domFloat , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_annotate_type_common::domFloat)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_annotate_type_common::domFloat2::create(DAE& dae) +{ + domFx_annotate_type_common::domFloat2Ref ref = new domFx_annotate_type_common::domFloat2(dae); + return ref; +} + + +daeMetaElement * +domFx_annotate_type_common::domFloat2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float2" ); + meta->registerClass(domFx_annotate_type_common::domFloat2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float2")); + ma->setOffset( daeOffsetOf( domFx_annotate_type_common::domFloat2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_annotate_type_common::domFloat2)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_annotate_type_common::domFloat3::create(DAE& dae) +{ + domFx_annotate_type_common::domFloat3Ref ref = new domFx_annotate_type_common::domFloat3(dae); + return ref; +} + + +daeMetaElement * +domFx_annotate_type_common::domFloat3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float3" ); + meta->registerClass(domFx_annotate_type_common::domFloat3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float3")); + ma->setOffset( daeOffsetOf( domFx_annotate_type_common::domFloat3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_annotate_type_common::domFloat3)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_annotate_type_common::domFloat4::create(DAE& dae) +{ + domFx_annotate_type_common::domFloat4Ref ref = new domFx_annotate_type_common::domFloat4(dae); + return ref; +} + + +daeMetaElement * +domFx_annotate_type_common::domFloat4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float4" ); + meta->registerClass(domFx_annotate_type_common::domFloat4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domFx_annotate_type_common::domFloat4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_annotate_type_common::domFloat4)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_annotate_type_common::domFloat2x2::create(DAE& dae) +{ + domFx_annotate_type_common::domFloat2x2Ref ref = new domFx_annotate_type_common::domFloat2x2(dae); + return ref; +} + + +daeMetaElement * +domFx_annotate_type_common::domFloat2x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float2x2" ); + meta->registerClass(domFx_annotate_type_common::domFloat2x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float2x2")); + ma->setOffset( daeOffsetOf( domFx_annotate_type_common::domFloat2x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_annotate_type_common::domFloat2x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_annotate_type_common::domFloat3x3::create(DAE& dae) +{ + domFx_annotate_type_common::domFloat3x3Ref ref = new domFx_annotate_type_common::domFloat3x3(dae); + return ref; +} + + +daeMetaElement * +domFx_annotate_type_common::domFloat3x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float3x3" ); + meta->registerClass(domFx_annotate_type_common::domFloat3x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float3x3")); + ma->setOffset( daeOffsetOf( domFx_annotate_type_common::domFloat3x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_annotate_type_common::domFloat3x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_annotate_type_common::domFloat4x4::create(DAE& dae) +{ + domFx_annotate_type_common::domFloat4x4Ref ref = new domFx_annotate_type_common::domFloat4x4(dae); + return ref; +} + + +daeMetaElement * +domFx_annotate_type_common::domFloat4x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float4x4" ); + meta->registerClass(domFx_annotate_type_common::domFloat4x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float4x4")); + ma->setOffset( daeOffsetOf( domFx_annotate_type_common::domFloat4x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_annotate_type_common::domFloat4x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_annotate_type_common::domString::create(DAE& dae) +{ + domFx_annotate_type_common::domStringRef ref = new domFx_annotate_type_common::domString(dae); + return ref; +} + + +daeMetaElement * +domFx_annotate_type_common::domString::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "string" ); + meta->registerClass(domFx_annotate_type_common::domString::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsString")); + ma->setOffset( daeOffsetOf( domFx_annotate_type_common::domString , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_annotate_type_common::domString)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domFx_basic_type_common.cpp b/Engine/lib/collada/src/1.4/dom/domFx_basic_type_common.cpp new file mode 100644 index 000000000..5788c5d8c --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domFx_basic_type_common.cpp @@ -0,0 +1,1320 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domFx_basic_type_common::create(DAE& dae) +{ + domFx_basic_type_commonRef ref = new domFx_basic_type_common(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fx_basic_type_common" ); + meta->registerClass(domFx_basic_type_common::create); + + meta->setIsTransparent( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemBool) ); + mea->setElementType( domFx_basic_type_common::domBool::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool2" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemBool2) ); + mea->setElementType( domFx_basic_type_common::domBool2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool3" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemBool3) ); + mea->setElementType( domFx_basic_type_common::domBool3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool4" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemBool4) ); + mea->setElementType( domFx_basic_type_common::domBool4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemInt) ); + mea->setElementType( domFx_basic_type_common::domInt::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int2" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemInt2) ); + mea->setElementType( domFx_basic_type_common::domInt2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int3" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemInt3) ); + mea->setElementType( domFx_basic_type_common::domInt3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int4" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemInt4) ); + mea->setElementType( domFx_basic_type_common::domInt4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemFloat) ); + mea->setElementType( domFx_basic_type_common::domFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float2" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemFloat2) ); + mea->setElementType( domFx_basic_type_common::domFloat2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float3" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemFloat3) ); + mea->setElementType( domFx_basic_type_common::domFloat3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float4" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemFloat4) ); + mea->setElementType( domFx_basic_type_common::domFloat4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float1x1" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemFloat1x1) ); + mea->setElementType( domFx_basic_type_common::domFloat1x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float1x2" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemFloat1x2) ); + mea->setElementType( domFx_basic_type_common::domFloat1x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float1x3" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemFloat1x3) ); + mea->setElementType( domFx_basic_type_common::domFloat1x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float1x4" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemFloat1x4) ); + mea->setElementType( domFx_basic_type_common::domFloat1x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float2x1" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemFloat2x1) ); + mea->setElementType( domFx_basic_type_common::domFloat2x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float2x2" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemFloat2x2) ); + mea->setElementType( domFx_basic_type_common::domFloat2x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float2x3" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemFloat2x3) ); + mea->setElementType( domFx_basic_type_common::domFloat2x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float2x4" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemFloat2x4) ); + mea->setElementType( domFx_basic_type_common::domFloat2x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float3x1" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemFloat3x1) ); + mea->setElementType( domFx_basic_type_common::domFloat3x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float3x2" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemFloat3x2) ); + mea->setElementType( domFx_basic_type_common::domFloat3x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float3x3" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemFloat3x3) ); + mea->setElementType( domFx_basic_type_common::domFloat3x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float3x4" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemFloat3x4) ); + mea->setElementType( domFx_basic_type_common::domFloat3x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float4x1" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemFloat4x1) ); + mea->setElementType( domFx_basic_type_common::domFloat4x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float4x2" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemFloat4x2) ); + mea->setElementType( domFx_basic_type_common::domFloat4x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float4x3" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemFloat4x3) ); + mea->setElementType( domFx_basic_type_common::domFloat4x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float4x4" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemFloat4x4) ); + mea->setElementType( domFx_basic_type_common::domFloat4x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "surface" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemSurface) ); + mea->setElementType( domFx_surface_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "sampler1D" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemSampler1D) ); + mea->setElementType( domFx_sampler1D_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "sampler2D" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemSampler2D) ); + mea->setElementType( domFx_sampler2D_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "sampler3D" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemSampler3D) ); + mea->setElementType( domFx_sampler3D_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "samplerCUBE" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemSamplerCUBE) ); + mea->setElementType( domFx_samplerCUBE_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "samplerRECT" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemSamplerRECT) ); + mea->setElementType( domFx_samplerRECT_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "samplerDEPTH" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemSamplerDEPTH) ); + mea->setElementType( domFx_samplerDEPTH_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "enum" ); + mea->setOffset( daeOffsetOf(domFx_basic_type_common,elemEnum) ); + mea->setElementType( domFx_basic_type_common::domEnum::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domFx_basic_type_common,_contents)); + meta->addContentsOrder(daeOffsetOf(domFx_basic_type_common,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domFx_basic_type_common,_CMData), 1); + meta->setElementSize(sizeof(domFx_basic_type_common)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domBool::create(DAE& dae) +{ + domFx_basic_type_common::domBoolRef ref = new domFx_basic_type_common::domBool(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domBool::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool" ); + meta->registerClass(domFx_basic_type_common::domBool::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domBool , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domBool)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domBool2::create(DAE& dae) +{ + domFx_basic_type_common::domBool2Ref ref = new domFx_basic_type_common::domBool2(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domBool2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool2" ); + meta->registerClass(domFx_basic_type_common::domBool2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Bool2")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domBool2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domBool2)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domBool3::create(DAE& dae) +{ + domFx_basic_type_common::domBool3Ref ref = new domFx_basic_type_common::domBool3(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domBool3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool3" ); + meta->registerClass(domFx_basic_type_common::domBool3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Bool3")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domBool3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domBool3)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domBool4::create(DAE& dae) +{ + domFx_basic_type_common::domBool4Ref ref = new domFx_basic_type_common::domBool4(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domBool4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool4" ); + meta->registerClass(domFx_basic_type_common::domBool4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Bool4")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domBool4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domBool4)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domInt::create(DAE& dae) +{ + domFx_basic_type_common::domIntRef ref = new domFx_basic_type_common::domInt(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domInt::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int" ); + meta->registerClass(domFx_basic_type_common::domInt::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Int")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domInt , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domInt)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domInt2::create(DAE& dae) +{ + domFx_basic_type_common::domInt2Ref ref = new domFx_basic_type_common::domInt2(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domInt2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int2" ); + meta->registerClass(domFx_basic_type_common::domInt2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Int2")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domInt2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domInt2)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domInt3::create(DAE& dae) +{ + domFx_basic_type_common::domInt3Ref ref = new domFx_basic_type_common::domInt3(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domInt3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int3" ); + meta->registerClass(domFx_basic_type_common::domInt3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Int3")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domInt3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domInt3)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domInt4::create(DAE& dae) +{ + domFx_basic_type_common::domInt4Ref ref = new domFx_basic_type_common::domInt4(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domInt4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int4" ); + meta->registerClass(domFx_basic_type_common::domInt4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Int4")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domInt4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domInt4)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domFloat::create(DAE& dae) +{ + domFx_basic_type_common::domFloatRef ref = new domFx_basic_type_common::domFloat(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domFloat::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float" ); + meta->registerClass(domFx_basic_type_common::domFloat::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domFloat , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domFloat)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domFloat2::create(DAE& dae) +{ + domFx_basic_type_common::domFloat2Ref ref = new domFx_basic_type_common::domFloat2(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domFloat2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float2" ); + meta->registerClass(domFx_basic_type_common::domFloat2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float2")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domFloat2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domFloat2)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domFloat3::create(DAE& dae) +{ + domFx_basic_type_common::domFloat3Ref ref = new domFx_basic_type_common::domFloat3(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domFloat3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float3" ); + meta->registerClass(domFx_basic_type_common::domFloat3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float3")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domFloat3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domFloat3)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domFloat4::create(DAE& dae) +{ + domFx_basic_type_common::domFloat4Ref ref = new domFx_basic_type_common::domFloat4(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domFloat4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float4" ); + meta->registerClass(domFx_basic_type_common::domFloat4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domFloat4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domFloat4)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domFloat1x1::create(DAE& dae) +{ + domFx_basic_type_common::domFloat1x1Ref ref = new domFx_basic_type_common::domFloat1x1(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domFloat1x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float1x1" ); + meta->registerClass(domFx_basic_type_common::domFloat1x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domFloat1x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domFloat1x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domFloat1x2::create(DAE& dae) +{ + domFx_basic_type_common::domFloat1x2Ref ref = new domFx_basic_type_common::domFloat1x2(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domFloat1x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float1x2" ); + meta->registerClass(domFx_basic_type_common::domFloat1x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float2")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domFloat1x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domFloat1x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domFloat1x3::create(DAE& dae) +{ + domFx_basic_type_common::domFloat1x3Ref ref = new domFx_basic_type_common::domFloat1x3(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domFloat1x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float1x3" ); + meta->registerClass(domFx_basic_type_common::domFloat1x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float3")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domFloat1x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domFloat1x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domFloat1x4::create(DAE& dae) +{ + domFx_basic_type_common::domFloat1x4Ref ref = new domFx_basic_type_common::domFloat1x4(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domFloat1x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float1x4" ); + meta->registerClass(domFx_basic_type_common::domFloat1x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domFloat1x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domFloat1x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domFloat2x1::create(DAE& dae) +{ + domFx_basic_type_common::domFloat2x1Ref ref = new domFx_basic_type_common::domFloat2x1(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domFloat2x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float2x1" ); + meta->registerClass(domFx_basic_type_common::domFloat2x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float2")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domFloat2x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domFloat2x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domFloat2x2::create(DAE& dae) +{ + domFx_basic_type_common::domFloat2x2Ref ref = new domFx_basic_type_common::domFloat2x2(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domFloat2x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float2x2" ); + meta->registerClass(domFx_basic_type_common::domFloat2x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float2x2")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domFloat2x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domFloat2x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domFloat2x3::create(DAE& dae) +{ + domFx_basic_type_common::domFloat2x3Ref ref = new domFx_basic_type_common::domFloat2x3(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domFloat2x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float2x3" ); + meta->registerClass(domFx_basic_type_common::domFloat2x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float2x3")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domFloat2x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domFloat2x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domFloat2x4::create(DAE& dae) +{ + domFx_basic_type_common::domFloat2x4Ref ref = new domFx_basic_type_common::domFloat2x4(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domFloat2x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float2x4" ); + meta->registerClass(domFx_basic_type_common::domFloat2x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float2x4")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domFloat2x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domFloat2x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domFloat3x1::create(DAE& dae) +{ + domFx_basic_type_common::domFloat3x1Ref ref = new domFx_basic_type_common::domFloat3x1(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domFloat3x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float3x1" ); + meta->registerClass(domFx_basic_type_common::domFloat3x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float3")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domFloat3x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domFloat3x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domFloat3x2::create(DAE& dae) +{ + domFx_basic_type_common::domFloat3x2Ref ref = new domFx_basic_type_common::domFloat3x2(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domFloat3x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float3x2" ); + meta->registerClass(domFx_basic_type_common::domFloat3x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float3x2")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domFloat3x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domFloat3x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domFloat3x3::create(DAE& dae) +{ + domFx_basic_type_common::domFloat3x3Ref ref = new domFx_basic_type_common::domFloat3x3(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domFloat3x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float3x3" ); + meta->registerClass(domFx_basic_type_common::domFloat3x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float3x3")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domFloat3x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domFloat3x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domFloat3x4::create(DAE& dae) +{ + domFx_basic_type_common::domFloat3x4Ref ref = new domFx_basic_type_common::domFloat3x4(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domFloat3x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float3x4" ); + meta->registerClass(domFx_basic_type_common::domFloat3x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float3x4")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domFloat3x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domFloat3x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domFloat4x1::create(DAE& dae) +{ + domFx_basic_type_common::domFloat4x1Ref ref = new domFx_basic_type_common::domFloat4x1(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domFloat4x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float4x1" ); + meta->registerClass(domFx_basic_type_common::domFloat4x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domFloat4x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domFloat4x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domFloat4x2::create(DAE& dae) +{ + domFx_basic_type_common::domFloat4x2Ref ref = new domFx_basic_type_common::domFloat4x2(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domFloat4x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float4x2" ); + meta->registerClass(domFx_basic_type_common::domFloat4x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float4x2")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domFloat4x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domFloat4x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domFloat4x3::create(DAE& dae) +{ + domFx_basic_type_common::domFloat4x3Ref ref = new domFx_basic_type_common::domFloat4x3(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domFloat4x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float4x3" ); + meta->registerClass(domFx_basic_type_common::domFloat4x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float4x3")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domFloat4x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domFloat4x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domFloat4x4::create(DAE& dae) +{ + domFx_basic_type_common::domFloat4x4Ref ref = new domFx_basic_type_common::domFloat4x4(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domFloat4x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float4x4" ); + meta->registerClass(domFx_basic_type_common::domFloat4x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float4x4")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domFloat4x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domFloat4x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_basic_type_common::domEnum::create(DAE& dae) +{ + domFx_basic_type_common::domEnumRef ref = new domFx_basic_type_common::domEnum(dae); + return ref; +} + + +daeMetaElement * +domFx_basic_type_common::domEnum::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "enum" ); + meta->registerClass(domFx_basic_type_common::domEnum::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsString")); + ma->setOffset( daeOffsetOf( domFx_basic_type_common::domEnum , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_basic_type_common::domEnum)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domFx_clearcolor_common.cpp b/Engine/lib/collada/src/1.4/dom/domFx_clearcolor_common.cpp new file mode 100644 index 000000000..fdd94cd41 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domFx_clearcolor_common.cpp @@ -0,0 +1,71 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domFx_clearcolor_common::create(DAE& dae) +{ + domFx_clearcolor_commonRef ref = new domFx_clearcolor_common(dae); + return ref; +} + + +daeMetaElement * +domFx_clearcolor_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fx_clearcolor_common" ); + meta->registerClass(domFx_clearcolor_common::create); + + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_color_common")); + ma->setOffset( daeOffsetOf( domFx_clearcolor_common , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("xsNonNegativeInteger")); + ma->setOffset( daeOffsetOf( domFx_clearcolor_common , attrIndex )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_clearcolor_common)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domFx_cleardepth_common.cpp b/Engine/lib/collada/src/1.4/dom/domFx_cleardepth_common.cpp new file mode 100644 index 000000000..521ba23b9 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domFx_cleardepth_common.cpp @@ -0,0 +1,71 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domFx_cleardepth_common::create(DAE& dae) +{ + domFx_cleardepth_commonRef ref = new domFx_cleardepth_common(dae); + return ref; +} + + +daeMetaElement * +domFx_cleardepth_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fx_cleardepth_common" ); + meta->registerClass(domFx_cleardepth_common::create); + + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domFx_cleardepth_common , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("xsNonNegativeInteger")); + ma->setOffset( daeOffsetOf( domFx_cleardepth_common , attrIndex )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_cleardepth_common)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domFx_clearstencil_common.cpp b/Engine/lib/collada/src/1.4/dom/domFx_clearstencil_common.cpp new file mode 100644 index 000000000..19764a905 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domFx_clearstencil_common.cpp @@ -0,0 +1,71 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domFx_clearstencil_common::create(DAE& dae) +{ + domFx_clearstencil_commonRef ref = new domFx_clearstencil_common(dae); + return ref; +} + + +daeMetaElement * +domFx_clearstencil_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fx_clearstencil_common" ); + meta->registerClass(domFx_clearstencil_common::create); + + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsByte")); + ma->setOffset( daeOffsetOf( domFx_clearstencil_common , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("xsNonNegativeInteger")); + ma->setOffset( daeOffsetOf( domFx_clearstencil_common , attrIndex )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_clearstencil_common)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domFx_code_profile.cpp b/Engine/lib/collada/src/1.4/dom/domFx_code_profile.cpp new file mode 100644 index 000000000..8b56874ab --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domFx_code_profile.cpp @@ -0,0 +1,70 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domFx_code_profile::create(DAE& dae) +{ + domFx_code_profileRef ref = new domFx_code_profile(dae); + return ref; +} + + +daeMetaElement * +domFx_code_profile::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fx_code_profile" ); + meta->registerClass(domFx_code_profile::create); + + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsString")); + ma->setOffset( daeOffsetOf( domFx_code_profile , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domFx_code_profile , attrSid )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_code_profile)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domFx_colortarget_common.cpp b/Engine/lib/collada/src/1.4/dom/domFx_colortarget_common.cpp new file mode 100644 index 000000000..4dc06cc6b --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domFx_colortarget_common.cpp @@ -0,0 +1,110 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domFx_colortarget_common::create(DAE& dae) +{ + domFx_colortarget_commonRef ref = new domFx_colortarget_common(dae); + return ref; +} + + +daeMetaElement * +domFx_colortarget_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fx_colortarget_common" ); + meta->registerClass(domFx_colortarget_common::create); + + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domFx_colortarget_common , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("xsNonNegativeInteger")); + ma->setOffset( daeOffsetOf( domFx_colortarget_common , attrIndex )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: face + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "face" ); + ma->setType( dae.getAtomicTypes().get("Fx_surface_face_enum")); + ma->setOffset( daeOffsetOf( domFx_colortarget_common , attrFace )); + ma->setContainer( meta ); + ma->setDefaultString( "POSITIVE_X"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: mip + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "mip" ); + ma->setType( dae.getAtomicTypes().get("xsNonNegativeInteger")); + ma->setOffset( daeOffsetOf( domFx_colortarget_common , attrMip )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: slice + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "slice" ); + ma->setType( dae.getAtomicTypes().get("xsNonNegativeInteger")); + ma->setOffset( daeOffsetOf( domFx_colortarget_common , attrSlice )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_colortarget_common)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domFx_depthtarget_common.cpp b/Engine/lib/collada/src/1.4/dom/domFx_depthtarget_common.cpp new file mode 100644 index 000000000..2e5cea74a --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domFx_depthtarget_common.cpp @@ -0,0 +1,110 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domFx_depthtarget_common::create(DAE& dae) +{ + domFx_depthtarget_commonRef ref = new domFx_depthtarget_common(dae); + return ref; +} + + +daeMetaElement * +domFx_depthtarget_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fx_depthtarget_common" ); + meta->registerClass(domFx_depthtarget_common::create); + + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domFx_depthtarget_common , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("xsNonNegativeInteger")); + ma->setOffset( daeOffsetOf( domFx_depthtarget_common , attrIndex )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: face + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "face" ); + ma->setType( dae.getAtomicTypes().get("Fx_surface_face_enum")); + ma->setOffset( daeOffsetOf( domFx_depthtarget_common , attrFace )); + ma->setContainer( meta ); + ma->setDefaultString( "POSITIVE_X"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: mip + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "mip" ); + ma->setType( dae.getAtomicTypes().get("xsNonNegativeInteger")); + ma->setOffset( daeOffsetOf( domFx_depthtarget_common , attrMip )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: slice + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "slice" ); + ma->setType( dae.getAtomicTypes().get("xsNonNegativeInteger")); + ma->setOffset( daeOffsetOf( domFx_depthtarget_common , attrSlice )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_depthtarget_common)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domFx_include_common.cpp b/Engine/lib/collada/src/1.4/dom/domFx_include_common.cpp new file mode 100644 index 000000000..621f9b038 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domFx_include_common.cpp @@ -0,0 +1,73 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domFx_include_common::create(DAE& dae) +{ + domFx_include_commonRef ref = new domFx_include_common(dae); + return ref; +} + + +daeMetaElement * +domFx_include_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fx_include_common" ); + meta->registerClass(domFx_include_common::create); + + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domFx_include_common , attrSid )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: url + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "url" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domFx_include_common , attrUrl )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_include_common)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domFx_newparam_common.cpp b/Engine/lib/collada/src/1.4/dom/domFx_newparam_common.cpp new file mode 100644 index 000000000..128c31cbc --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domFx_newparam_common.cpp @@ -0,0 +1,163 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domFx_newparam_common::create(DAE& dae) +{ + domFx_newparam_commonRef ref = new domFx_newparam_common(dae); + return ref; +} + + +daeMetaElement * +domFx_newparam_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fx_newparam_common" ); + meta->registerClass(domFx_newparam_common::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "annotate" ); + mea->setOffset( daeOffsetOf(domFx_newparam_common,elemAnnotate_array) ); + mea->setElementType( domFx_annotate_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "semantic" ); + mea->setOffset( daeOffsetOf(domFx_newparam_common,elemSemantic) ); + mea->setElementType( domFx_newparam_common::domSemantic::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "modifier" ); + mea->setOffset( daeOffsetOf(domFx_newparam_common,elemModifier) ); + mea->setElementType( domFx_newparam_common::domModifier::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 1, 1 ); + mea->setName( "fx_basic_type_common" ); + mea->setOffset( daeOffsetOf(domFx_newparam_common,elemFx_basic_type_common) ); + mea->setElementType( domFx_basic_type_common::registerElement(dae) ); + cm->appendChild( new daeMetaGroup( mea, meta, cm, 3, 1, 1 ) ); + + cm->setMaxOrdinal( 3 ); + meta->setCMRoot( cm ); + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domFx_newparam_common , attrSid )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_newparam_common)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_newparam_common::domSemantic::create(DAE& dae) +{ + domFx_newparam_common::domSemanticRef ref = new domFx_newparam_common::domSemantic(dae); + return ref; +} + + +daeMetaElement * +domFx_newparam_common::domSemantic::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "semantic" ); + meta->registerClass(domFx_newparam_common::domSemantic::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domFx_newparam_common::domSemantic , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_newparam_common::domSemantic)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_newparam_common::domModifier::create(DAE& dae) +{ + domFx_newparam_common::domModifierRef ref = new domFx_newparam_common::domModifier(dae); + return ref; +} + + +daeMetaElement * +domFx_newparam_common::domModifier::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "modifier" ); + meta->registerClass(domFx_newparam_common::domModifier::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_modifier_enum_common")); + ma->setOffset( daeOffsetOf( domFx_newparam_common::domModifier , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_newparam_common::domModifier)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domFx_profile_abstract.cpp b/Engine/lib/collada/src/1.4/dom/domFx_profile_abstract.cpp new file mode 100644 index 000000000..5c80402f9 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domFx_profile_abstract.cpp @@ -0,0 +1,50 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domFx_profile_abstract::create(DAE& dae) +{ + domFx_profile_abstractRef ref = new domFx_profile_abstract(dae); + return ref; +} + + +daeMetaElement * +domFx_profile_abstract::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fx_profile_abstract" ); + meta->registerClass(domFx_profile_abstract::create); + + meta->setIsAbstract( true ); + + meta->setElementSize(sizeof(domFx_profile_abstract)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domFx_sampler1D_common.cpp b/Engine/lib/collada/src/1.4/dom/domFx_sampler1D_common.cpp new file mode 100644 index 000000000..006b45397 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domFx_sampler1D_common.cpp @@ -0,0 +1,397 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domFx_sampler1D_common::create(DAE& dae) +{ + domFx_sampler1D_commonRef ref = new domFx_sampler1D_common(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler1D_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fx_sampler1D_common" ); + meta->registerClass(domFx_sampler1D_common::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "source" ); + mea->setOffset( daeOffsetOf(domFx_sampler1D_common,elemSource) ); + mea->setElementType( domFx_sampler1D_common::domSource::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "wrap_s" ); + mea->setOffset( daeOffsetOf(domFx_sampler1D_common,elemWrap_s) ); + mea->setElementType( domFx_sampler1D_common::domWrap_s::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "minfilter" ); + mea->setOffset( daeOffsetOf(domFx_sampler1D_common,elemMinfilter) ); + mea->setElementType( domFx_sampler1D_common::domMinfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "magfilter" ); + mea->setOffset( daeOffsetOf(domFx_sampler1D_common,elemMagfilter) ); + mea->setElementType( domFx_sampler1D_common::domMagfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "mipfilter" ); + mea->setOffset( daeOffsetOf(domFx_sampler1D_common,elemMipfilter) ); + mea->setElementType( domFx_sampler1D_common::domMipfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 5, 0, 1 ); + mea->setName( "border_color" ); + mea->setOffset( daeOffsetOf(domFx_sampler1D_common,elemBorder_color) ); + mea->setElementType( domFx_sampler1D_common::domBorder_color::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 6, 0, 1 ); + mea->setName( "mipmap_maxlevel" ); + mea->setOffset( daeOffsetOf(domFx_sampler1D_common,elemMipmap_maxlevel) ); + mea->setElementType( domFx_sampler1D_common::domMipmap_maxlevel::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 7, 0, 1 ); + mea->setName( "mipmap_bias" ); + mea->setOffset( daeOffsetOf(domFx_sampler1D_common,elemMipmap_bias) ); + mea->setElementType( domFx_sampler1D_common::domMipmap_bias::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 8, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domFx_sampler1D_common,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 8 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domFx_sampler1D_common)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_sampler1D_common::domSource::create(DAE& dae) +{ + domFx_sampler1D_common::domSourceRef ref = new domFx_sampler1D_common::domSource(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler1D_common::domSource::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "source" ); + meta->registerClass(domFx_sampler1D_common::domSource::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domFx_sampler1D_common::domSource , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_sampler1D_common::domSource)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_sampler1D_common::domWrap_s::create(DAE& dae) +{ + domFx_sampler1D_common::domWrap_sRef ref = new domFx_sampler1D_common::domWrap_s(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler1D_common::domWrap_s::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "wrap_s" ); + meta->registerClass(domFx_sampler1D_common::domWrap_s::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_wrap_common")); + ma->setOffset( daeOffsetOf( domFx_sampler1D_common::domWrap_s , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_sampler1D_common::domWrap_s)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_sampler1D_common::domMinfilter::create(DAE& dae) +{ + domFx_sampler1D_common::domMinfilterRef ref = new domFx_sampler1D_common::domMinfilter(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler1D_common::domMinfilter::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "minfilter" ); + meta->registerClass(domFx_sampler1D_common::domMinfilter::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_filter_common")); + ma->setOffset( daeOffsetOf( domFx_sampler1D_common::domMinfilter , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_sampler1D_common::domMinfilter)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_sampler1D_common::domMagfilter::create(DAE& dae) +{ + domFx_sampler1D_common::domMagfilterRef ref = new domFx_sampler1D_common::domMagfilter(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler1D_common::domMagfilter::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "magfilter" ); + meta->registerClass(domFx_sampler1D_common::domMagfilter::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_filter_common")); + ma->setOffset( daeOffsetOf( domFx_sampler1D_common::domMagfilter , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_sampler1D_common::domMagfilter)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_sampler1D_common::domMipfilter::create(DAE& dae) +{ + domFx_sampler1D_common::domMipfilterRef ref = new domFx_sampler1D_common::domMipfilter(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler1D_common::domMipfilter::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mipfilter" ); + meta->registerClass(domFx_sampler1D_common::domMipfilter::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_filter_common")); + ma->setOffset( daeOffsetOf( domFx_sampler1D_common::domMipfilter , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_sampler1D_common::domMipfilter)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_sampler1D_common::domBorder_color::create(DAE& dae) +{ + domFx_sampler1D_common::domBorder_colorRef ref = new domFx_sampler1D_common::domBorder_color(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler1D_common::domBorder_color::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "border_color" ); + meta->registerClass(domFx_sampler1D_common::domBorder_color::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_color_common")); + ma->setOffset( daeOffsetOf( domFx_sampler1D_common::domBorder_color , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_sampler1D_common::domBorder_color)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_sampler1D_common::domMipmap_maxlevel::create(DAE& dae) +{ + domFx_sampler1D_common::domMipmap_maxlevelRef ref = new domFx_sampler1D_common::domMipmap_maxlevel(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler1D_common::domMipmap_maxlevel::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mipmap_maxlevel" ); + meta->registerClass(domFx_sampler1D_common::domMipmap_maxlevel::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsUnsignedByte")); + ma->setOffset( daeOffsetOf( domFx_sampler1D_common::domMipmap_maxlevel , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_sampler1D_common::domMipmap_maxlevel)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_sampler1D_common::domMipmap_bias::create(DAE& dae) +{ + domFx_sampler1D_common::domMipmap_biasRef ref = new domFx_sampler1D_common::domMipmap_bias(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler1D_common::domMipmap_bias::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mipmap_bias" ); + meta->registerClass(domFx_sampler1D_common::domMipmap_bias::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsFloat")); + ma->setOffset( daeOffsetOf( domFx_sampler1D_common::domMipmap_bias , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_sampler1D_common::domMipmap_bias)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domFx_sampler2D_common.cpp b/Engine/lib/collada/src/1.4/dom/domFx_sampler2D_common.cpp new file mode 100644 index 000000000..74350112c --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domFx_sampler2D_common.cpp @@ -0,0 +1,439 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domFx_sampler2D_common::create(DAE& dae) +{ + domFx_sampler2D_commonRef ref = new domFx_sampler2D_common(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler2D_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fx_sampler2D_common" ); + meta->registerClass(domFx_sampler2D_common::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "source" ); + mea->setOffset( daeOffsetOf(domFx_sampler2D_common,elemSource) ); + mea->setElementType( domFx_sampler2D_common::domSource::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "wrap_s" ); + mea->setOffset( daeOffsetOf(domFx_sampler2D_common,elemWrap_s) ); + mea->setElementType( domFx_sampler2D_common::domWrap_s::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "wrap_t" ); + mea->setOffset( daeOffsetOf(domFx_sampler2D_common,elemWrap_t) ); + mea->setElementType( domFx_sampler2D_common::domWrap_t::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "minfilter" ); + mea->setOffset( daeOffsetOf(domFx_sampler2D_common,elemMinfilter) ); + mea->setElementType( domFx_sampler2D_common::domMinfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "magfilter" ); + mea->setOffset( daeOffsetOf(domFx_sampler2D_common,elemMagfilter) ); + mea->setElementType( domFx_sampler2D_common::domMagfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 5, 0, 1 ); + mea->setName( "mipfilter" ); + mea->setOffset( daeOffsetOf(domFx_sampler2D_common,elemMipfilter) ); + mea->setElementType( domFx_sampler2D_common::domMipfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 6, 0, 1 ); + mea->setName( "border_color" ); + mea->setOffset( daeOffsetOf(domFx_sampler2D_common,elemBorder_color) ); + mea->setElementType( domFx_sampler2D_common::domBorder_color::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 7, 0, 1 ); + mea->setName( "mipmap_maxlevel" ); + mea->setOffset( daeOffsetOf(domFx_sampler2D_common,elemMipmap_maxlevel) ); + mea->setElementType( domFx_sampler2D_common::domMipmap_maxlevel::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 8, 0, 1 ); + mea->setName( "mipmap_bias" ); + mea->setOffset( daeOffsetOf(domFx_sampler2D_common,elemMipmap_bias) ); + mea->setElementType( domFx_sampler2D_common::domMipmap_bias::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 9, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domFx_sampler2D_common,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 9 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domFx_sampler2D_common)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_sampler2D_common::domSource::create(DAE& dae) +{ + domFx_sampler2D_common::domSourceRef ref = new domFx_sampler2D_common::domSource(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler2D_common::domSource::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "source" ); + meta->registerClass(domFx_sampler2D_common::domSource::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domFx_sampler2D_common::domSource , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_sampler2D_common::domSource)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_sampler2D_common::domWrap_s::create(DAE& dae) +{ + domFx_sampler2D_common::domWrap_sRef ref = new domFx_sampler2D_common::domWrap_s(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler2D_common::domWrap_s::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "wrap_s" ); + meta->registerClass(domFx_sampler2D_common::domWrap_s::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_wrap_common")); + ma->setOffset( daeOffsetOf( domFx_sampler2D_common::domWrap_s , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_sampler2D_common::domWrap_s)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_sampler2D_common::domWrap_t::create(DAE& dae) +{ + domFx_sampler2D_common::domWrap_tRef ref = new domFx_sampler2D_common::domWrap_t(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler2D_common::domWrap_t::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "wrap_t" ); + meta->registerClass(domFx_sampler2D_common::domWrap_t::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_wrap_common")); + ma->setOffset( daeOffsetOf( domFx_sampler2D_common::domWrap_t , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_sampler2D_common::domWrap_t)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_sampler2D_common::domMinfilter::create(DAE& dae) +{ + domFx_sampler2D_common::domMinfilterRef ref = new domFx_sampler2D_common::domMinfilter(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler2D_common::domMinfilter::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "minfilter" ); + meta->registerClass(domFx_sampler2D_common::domMinfilter::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_filter_common")); + ma->setOffset( daeOffsetOf( domFx_sampler2D_common::domMinfilter , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_sampler2D_common::domMinfilter)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_sampler2D_common::domMagfilter::create(DAE& dae) +{ + domFx_sampler2D_common::domMagfilterRef ref = new domFx_sampler2D_common::domMagfilter(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler2D_common::domMagfilter::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "magfilter" ); + meta->registerClass(domFx_sampler2D_common::domMagfilter::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_filter_common")); + ma->setOffset( daeOffsetOf( domFx_sampler2D_common::domMagfilter , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_sampler2D_common::domMagfilter)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_sampler2D_common::domMipfilter::create(DAE& dae) +{ + domFx_sampler2D_common::domMipfilterRef ref = new domFx_sampler2D_common::domMipfilter(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler2D_common::domMipfilter::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mipfilter" ); + meta->registerClass(domFx_sampler2D_common::domMipfilter::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_filter_common")); + ma->setOffset( daeOffsetOf( domFx_sampler2D_common::domMipfilter , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_sampler2D_common::domMipfilter)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_sampler2D_common::domBorder_color::create(DAE& dae) +{ + domFx_sampler2D_common::domBorder_colorRef ref = new domFx_sampler2D_common::domBorder_color(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler2D_common::domBorder_color::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "border_color" ); + meta->registerClass(domFx_sampler2D_common::domBorder_color::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_color_common")); + ma->setOffset( daeOffsetOf( domFx_sampler2D_common::domBorder_color , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_sampler2D_common::domBorder_color)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_sampler2D_common::domMipmap_maxlevel::create(DAE& dae) +{ + domFx_sampler2D_common::domMipmap_maxlevelRef ref = new domFx_sampler2D_common::domMipmap_maxlevel(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler2D_common::domMipmap_maxlevel::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mipmap_maxlevel" ); + meta->registerClass(domFx_sampler2D_common::domMipmap_maxlevel::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsUnsignedByte")); + ma->setOffset( daeOffsetOf( domFx_sampler2D_common::domMipmap_maxlevel , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_sampler2D_common::domMipmap_maxlevel)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_sampler2D_common::domMipmap_bias::create(DAE& dae) +{ + domFx_sampler2D_common::domMipmap_biasRef ref = new domFx_sampler2D_common::domMipmap_bias(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler2D_common::domMipmap_bias::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mipmap_bias" ); + meta->registerClass(domFx_sampler2D_common::domMipmap_bias::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsFloat")); + ma->setOffset( daeOffsetOf( domFx_sampler2D_common::domMipmap_bias , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_sampler2D_common::domMipmap_bias)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domFx_sampler3D_common.cpp b/Engine/lib/collada/src/1.4/dom/domFx_sampler3D_common.cpp new file mode 100644 index 000000000..cc4e134bf --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domFx_sampler3D_common.cpp @@ -0,0 +1,481 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domFx_sampler3D_common::create(DAE& dae) +{ + domFx_sampler3D_commonRef ref = new domFx_sampler3D_common(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler3D_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fx_sampler3D_common" ); + meta->registerClass(domFx_sampler3D_common::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "source" ); + mea->setOffset( daeOffsetOf(domFx_sampler3D_common,elemSource) ); + mea->setElementType( domFx_sampler3D_common::domSource::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "wrap_s" ); + mea->setOffset( daeOffsetOf(domFx_sampler3D_common,elemWrap_s) ); + mea->setElementType( domFx_sampler3D_common::domWrap_s::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "wrap_t" ); + mea->setOffset( daeOffsetOf(domFx_sampler3D_common,elemWrap_t) ); + mea->setElementType( domFx_sampler3D_common::domWrap_t::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "wrap_p" ); + mea->setOffset( daeOffsetOf(domFx_sampler3D_common,elemWrap_p) ); + mea->setElementType( domFx_sampler3D_common::domWrap_p::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "minfilter" ); + mea->setOffset( daeOffsetOf(domFx_sampler3D_common,elemMinfilter) ); + mea->setElementType( domFx_sampler3D_common::domMinfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 5, 0, 1 ); + mea->setName( "magfilter" ); + mea->setOffset( daeOffsetOf(domFx_sampler3D_common,elemMagfilter) ); + mea->setElementType( domFx_sampler3D_common::domMagfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 6, 0, 1 ); + mea->setName( "mipfilter" ); + mea->setOffset( daeOffsetOf(domFx_sampler3D_common,elemMipfilter) ); + mea->setElementType( domFx_sampler3D_common::domMipfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 7, 0, 1 ); + mea->setName( "border_color" ); + mea->setOffset( daeOffsetOf(domFx_sampler3D_common,elemBorder_color) ); + mea->setElementType( domFx_sampler3D_common::domBorder_color::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 8, 0, 1 ); + mea->setName( "mipmap_maxlevel" ); + mea->setOffset( daeOffsetOf(domFx_sampler3D_common,elemMipmap_maxlevel) ); + mea->setElementType( domFx_sampler3D_common::domMipmap_maxlevel::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 9, 0, 1 ); + mea->setName( "mipmap_bias" ); + mea->setOffset( daeOffsetOf(domFx_sampler3D_common,elemMipmap_bias) ); + mea->setElementType( domFx_sampler3D_common::domMipmap_bias::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 10, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domFx_sampler3D_common,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 10 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domFx_sampler3D_common)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_sampler3D_common::domSource::create(DAE& dae) +{ + domFx_sampler3D_common::domSourceRef ref = new domFx_sampler3D_common::domSource(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler3D_common::domSource::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "source" ); + meta->registerClass(domFx_sampler3D_common::domSource::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domFx_sampler3D_common::domSource , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_sampler3D_common::domSource)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_sampler3D_common::domWrap_s::create(DAE& dae) +{ + domFx_sampler3D_common::domWrap_sRef ref = new domFx_sampler3D_common::domWrap_s(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler3D_common::domWrap_s::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "wrap_s" ); + meta->registerClass(domFx_sampler3D_common::domWrap_s::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_wrap_common")); + ma->setOffset( daeOffsetOf( domFx_sampler3D_common::domWrap_s , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_sampler3D_common::domWrap_s)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_sampler3D_common::domWrap_t::create(DAE& dae) +{ + domFx_sampler3D_common::domWrap_tRef ref = new domFx_sampler3D_common::domWrap_t(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler3D_common::domWrap_t::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "wrap_t" ); + meta->registerClass(domFx_sampler3D_common::domWrap_t::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_wrap_common")); + ma->setOffset( daeOffsetOf( domFx_sampler3D_common::domWrap_t , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_sampler3D_common::domWrap_t)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_sampler3D_common::domWrap_p::create(DAE& dae) +{ + domFx_sampler3D_common::domWrap_pRef ref = new domFx_sampler3D_common::domWrap_p(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler3D_common::domWrap_p::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "wrap_p" ); + meta->registerClass(domFx_sampler3D_common::domWrap_p::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_wrap_common")); + ma->setOffset( daeOffsetOf( domFx_sampler3D_common::domWrap_p , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_sampler3D_common::domWrap_p)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_sampler3D_common::domMinfilter::create(DAE& dae) +{ + domFx_sampler3D_common::domMinfilterRef ref = new domFx_sampler3D_common::domMinfilter(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler3D_common::domMinfilter::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "minfilter" ); + meta->registerClass(domFx_sampler3D_common::domMinfilter::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_filter_common")); + ma->setOffset( daeOffsetOf( domFx_sampler3D_common::domMinfilter , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_sampler3D_common::domMinfilter)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_sampler3D_common::domMagfilter::create(DAE& dae) +{ + domFx_sampler3D_common::domMagfilterRef ref = new domFx_sampler3D_common::domMagfilter(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler3D_common::domMagfilter::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "magfilter" ); + meta->registerClass(domFx_sampler3D_common::domMagfilter::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_filter_common")); + ma->setOffset( daeOffsetOf( domFx_sampler3D_common::domMagfilter , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_sampler3D_common::domMagfilter)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_sampler3D_common::domMipfilter::create(DAE& dae) +{ + domFx_sampler3D_common::domMipfilterRef ref = new domFx_sampler3D_common::domMipfilter(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler3D_common::domMipfilter::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mipfilter" ); + meta->registerClass(domFx_sampler3D_common::domMipfilter::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_filter_common")); + ma->setOffset( daeOffsetOf( domFx_sampler3D_common::domMipfilter , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_sampler3D_common::domMipfilter)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_sampler3D_common::domBorder_color::create(DAE& dae) +{ + domFx_sampler3D_common::domBorder_colorRef ref = new domFx_sampler3D_common::domBorder_color(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler3D_common::domBorder_color::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "border_color" ); + meta->registerClass(domFx_sampler3D_common::domBorder_color::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_color_common")); + ma->setOffset( daeOffsetOf( domFx_sampler3D_common::domBorder_color , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_sampler3D_common::domBorder_color)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_sampler3D_common::domMipmap_maxlevel::create(DAE& dae) +{ + domFx_sampler3D_common::domMipmap_maxlevelRef ref = new domFx_sampler3D_common::domMipmap_maxlevel(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler3D_common::domMipmap_maxlevel::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mipmap_maxlevel" ); + meta->registerClass(domFx_sampler3D_common::domMipmap_maxlevel::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsUnsignedByte")); + ma->setOffset( daeOffsetOf( domFx_sampler3D_common::domMipmap_maxlevel , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_sampler3D_common::domMipmap_maxlevel)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_sampler3D_common::domMipmap_bias::create(DAE& dae) +{ + domFx_sampler3D_common::domMipmap_biasRef ref = new domFx_sampler3D_common::domMipmap_bias(dae); + return ref; +} + + +daeMetaElement * +domFx_sampler3D_common::domMipmap_bias::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mipmap_bias" ); + meta->registerClass(domFx_sampler3D_common::domMipmap_bias::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsFloat")); + ma->setOffset( daeOffsetOf( domFx_sampler3D_common::domMipmap_bias , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_sampler3D_common::domMipmap_bias)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domFx_samplerCUBE_common.cpp b/Engine/lib/collada/src/1.4/dom/domFx_samplerCUBE_common.cpp new file mode 100644 index 000000000..916c607fd --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domFx_samplerCUBE_common.cpp @@ -0,0 +1,481 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domFx_samplerCUBE_common::create(DAE& dae) +{ + domFx_samplerCUBE_commonRef ref = new domFx_samplerCUBE_common(dae); + return ref; +} + + +daeMetaElement * +domFx_samplerCUBE_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fx_samplerCUBE_common" ); + meta->registerClass(domFx_samplerCUBE_common::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "source" ); + mea->setOffset( daeOffsetOf(domFx_samplerCUBE_common,elemSource) ); + mea->setElementType( domFx_samplerCUBE_common::domSource::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "wrap_s" ); + mea->setOffset( daeOffsetOf(domFx_samplerCUBE_common,elemWrap_s) ); + mea->setElementType( domFx_samplerCUBE_common::domWrap_s::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "wrap_t" ); + mea->setOffset( daeOffsetOf(domFx_samplerCUBE_common,elemWrap_t) ); + mea->setElementType( domFx_samplerCUBE_common::domWrap_t::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "wrap_p" ); + mea->setOffset( daeOffsetOf(domFx_samplerCUBE_common,elemWrap_p) ); + mea->setElementType( domFx_samplerCUBE_common::domWrap_p::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "minfilter" ); + mea->setOffset( daeOffsetOf(domFx_samplerCUBE_common,elemMinfilter) ); + mea->setElementType( domFx_samplerCUBE_common::domMinfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 5, 0, 1 ); + mea->setName( "magfilter" ); + mea->setOffset( daeOffsetOf(domFx_samplerCUBE_common,elemMagfilter) ); + mea->setElementType( domFx_samplerCUBE_common::domMagfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 6, 0, 1 ); + mea->setName( "mipfilter" ); + mea->setOffset( daeOffsetOf(domFx_samplerCUBE_common,elemMipfilter) ); + mea->setElementType( domFx_samplerCUBE_common::domMipfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 7, 0, 1 ); + mea->setName( "border_color" ); + mea->setOffset( daeOffsetOf(domFx_samplerCUBE_common,elemBorder_color) ); + mea->setElementType( domFx_samplerCUBE_common::domBorder_color::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 8, 0, 1 ); + mea->setName( "mipmap_maxlevel" ); + mea->setOffset( daeOffsetOf(domFx_samplerCUBE_common,elemMipmap_maxlevel) ); + mea->setElementType( domFx_samplerCUBE_common::domMipmap_maxlevel::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 9, 0, 1 ); + mea->setName( "mipmap_bias" ); + mea->setOffset( daeOffsetOf(domFx_samplerCUBE_common,elemMipmap_bias) ); + mea->setElementType( domFx_samplerCUBE_common::domMipmap_bias::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 10, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domFx_samplerCUBE_common,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 10 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domFx_samplerCUBE_common)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_samplerCUBE_common::domSource::create(DAE& dae) +{ + domFx_samplerCUBE_common::domSourceRef ref = new domFx_samplerCUBE_common::domSource(dae); + return ref; +} + + +daeMetaElement * +domFx_samplerCUBE_common::domSource::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "source" ); + meta->registerClass(domFx_samplerCUBE_common::domSource::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domFx_samplerCUBE_common::domSource , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_samplerCUBE_common::domSource)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_samplerCUBE_common::domWrap_s::create(DAE& dae) +{ + domFx_samplerCUBE_common::domWrap_sRef ref = new domFx_samplerCUBE_common::domWrap_s(dae); + return ref; +} + + +daeMetaElement * +domFx_samplerCUBE_common::domWrap_s::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "wrap_s" ); + meta->registerClass(domFx_samplerCUBE_common::domWrap_s::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_wrap_common")); + ma->setOffset( daeOffsetOf( domFx_samplerCUBE_common::domWrap_s , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_samplerCUBE_common::domWrap_s)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_samplerCUBE_common::domWrap_t::create(DAE& dae) +{ + domFx_samplerCUBE_common::domWrap_tRef ref = new domFx_samplerCUBE_common::domWrap_t(dae); + return ref; +} + + +daeMetaElement * +domFx_samplerCUBE_common::domWrap_t::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "wrap_t" ); + meta->registerClass(domFx_samplerCUBE_common::domWrap_t::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_wrap_common")); + ma->setOffset( daeOffsetOf( domFx_samplerCUBE_common::domWrap_t , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_samplerCUBE_common::domWrap_t)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_samplerCUBE_common::domWrap_p::create(DAE& dae) +{ + domFx_samplerCUBE_common::domWrap_pRef ref = new domFx_samplerCUBE_common::domWrap_p(dae); + return ref; +} + + +daeMetaElement * +domFx_samplerCUBE_common::domWrap_p::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "wrap_p" ); + meta->registerClass(domFx_samplerCUBE_common::domWrap_p::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_wrap_common")); + ma->setOffset( daeOffsetOf( domFx_samplerCUBE_common::domWrap_p , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_samplerCUBE_common::domWrap_p)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_samplerCUBE_common::domMinfilter::create(DAE& dae) +{ + domFx_samplerCUBE_common::domMinfilterRef ref = new domFx_samplerCUBE_common::domMinfilter(dae); + return ref; +} + + +daeMetaElement * +domFx_samplerCUBE_common::domMinfilter::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "minfilter" ); + meta->registerClass(domFx_samplerCUBE_common::domMinfilter::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_filter_common")); + ma->setOffset( daeOffsetOf( domFx_samplerCUBE_common::domMinfilter , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_samplerCUBE_common::domMinfilter)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_samplerCUBE_common::domMagfilter::create(DAE& dae) +{ + domFx_samplerCUBE_common::domMagfilterRef ref = new domFx_samplerCUBE_common::domMagfilter(dae); + return ref; +} + + +daeMetaElement * +domFx_samplerCUBE_common::domMagfilter::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "magfilter" ); + meta->registerClass(domFx_samplerCUBE_common::domMagfilter::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_filter_common")); + ma->setOffset( daeOffsetOf( domFx_samplerCUBE_common::domMagfilter , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_samplerCUBE_common::domMagfilter)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_samplerCUBE_common::domMipfilter::create(DAE& dae) +{ + domFx_samplerCUBE_common::domMipfilterRef ref = new domFx_samplerCUBE_common::domMipfilter(dae); + return ref; +} + + +daeMetaElement * +domFx_samplerCUBE_common::domMipfilter::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mipfilter" ); + meta->registerClass(domFx_samplerCUBE_common::domMipfilter::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_filter_common")); + ma->setOffset( daeOffsetOf( domFx_samplerCUBE_common::domMipfilter , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_samplerCUBE_common::domMipfilter)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_samplerCUBE_common::domBorder_color::create(DAE& dae) +{ + domFx_samplerCUBE_common::domBorder_colorRef ref = new domFx_samplerCUBE_common::domBorder_color(dae); + return ref; +} + + +daeMetaElement * +domFx_samplerCUBE_common::domBorder_color::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "border_color" ); + meta->registerClass(domFx_samplerCUBE_common::domBorder_color::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_color_common")); + ma->setOffset( daeOffsetOf( domFx_samplerCUBE_common::domBorder_color , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_samplerCUBE_common::domBorder_color)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_samplerCUBE_common::domMipmap_maxlevel::create(DAE& dae) +{ + domFx_samplerCUBE_common::domMipmap_maxlevelRef ref = new domFx_samplerCUBE_common::domMipmap_maxlevel(dae); + return ref; +} + + +daeMetaElement * +domFx_samplerCUBE_common::domMipmap_maxlevel::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mipmap_maxlevel" ); + meta->registerClass(domFx_samplerCUBE_common::domMipmap_maxlevel::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsUnsignedByte")); + ma->setOffset( daeOffsetOf( domFx_samplerCUBE_common::domMipmap_maxlevel , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_samplerCUBE_common::domMipmap_maxlevel)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_samplerCUBE_common::domMipmap_bias::create(DAE& dae) +{ + domFx_samplerCUBE_common::domMipmap_biasRef ref = new domFx_samplerCUBE_common::domMipmap_bias(dae); + return ref; +} + + +daeMetaElement * +domFx_samplerCUBE_common::domMipmap_bias::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mipmap_bias" ); + meta->registerClass(domFx_samplerCUBE_common::domMipmap_bias::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsFloat")); + ma->setOffset( daeOffsetOf( domFx_samplerCUBE_common::domMipmap_bias , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_samplerCUBE_common::domMipmap_bias)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domFx_samplerDEPTH_common.cpp b/Engine/lib/collada/src/1.4/dom/domFx_samplerDEPTH_common.cpp new file mode 100644 index 000000000..3ceb2e561 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domFx_samplerDEPTH_common.cpp @@ -0,0 +1,271 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domFx_samplerDEPTH_common::create(DAE& dae) +{ + domFx_samplerDEPTH_commonRef ref = new domFx_samplerDEPTH_common(dae); + return ref; +} + + +daeMetaElement * +domFx_samplerDEPTH_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fx_samplerDEPTH_common" ); + meta->registerClass(domFx_samplerDEPTH_common::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "source" ); + mea->setOffset( daeOffsetOf(domFx_samplerDEPTH_common,elemSource) ); + mea->setElementType( domFx_samplerDEPTH_common::domSource::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "wrap_s" ); + mea->setOffset( daeOffsetOf(domFx_samplerDEPTH_common,elemWrap_s) ); + mea->setElementType( domFx_samplerDEPTH_common::domWrap_s::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "wrap_t" ); + mea->setOffset( daeOffsetOf(domFx_samplerDEPTH_common,elemWrap_t) ); + mea->setElementType( domFx_samplerDEPTH_common::domWrap_t::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "minfilter" ); + mea->setOffset( daeOffsetOf(domFx_samplerDEPTH_common,elemMinfilter) ); + mea->setElementType( domFx_samplerDEPTH_common::domMinfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "magfilter" ); + mea->setOffset( daeOffsetOf(domFx_samplerDEPTH_common,elemMagfilter) ); + mea->setElementType( domFx_samplerDEPTH_common::domMagfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 5, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domFx_samplerDEPTH_common,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 5 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domFx_samplerDEPTH_common)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_samplerDEPTH_common::domSource::create(DAE& dae) +{ + domFx_samplerDEPTH_common::domSourceRef ref = new domFx_samplerDEPTH_common::domSource(dae); + return ref; +} + + +daeMetaElement * +domFx_samplerDEPTH_common::domSource::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "source" ); + meta->registerClass(domFx_samplerDEPTH_common::domSource::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domFx_samplerDEPTH_common::domSource , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_samplerDEPTH_common::domSource)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_samplerDEPTH_common::domWrap_s::create(DAE& dae) +{ + domFx_samplerDEPTH_common::domWrap_sRef ref = new domFx_samplerDEPTH_common::domWrap_s(dae); + return ref; +} + + +daeMetaElement * +domFx_samplerDEPTH_common::domWrap_s::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "wrap_s" ); + meta->registerClass(domFx_samplerDEPTH_common::domWrap_s::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_wrap_common")); + ma->setOffset( daeOffsetOf( domFx_samplerDEPTH_common::domWrap_s , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_samplerDEPTH_common::domWrap_s)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_samplerDEPTH_common::domWrap_t::create(DAE& dae) +{ + domFx_samplerDEPTH_common::domWrap_tRef ref = new domFx_samplerDEPTH_common::domWrap_t(dae); + return ref; +} + + +daeMetaElement * +domFx_samplerDEPTH_common::domWrap_t::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "wrap_t" ); + meta->registerClass(domFx_samplerDEPTH_common::domWrap_t::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_wrap_common")); + ma->setOffset( daeOffsetOf( domFx_samplerDEPTH_common::domWrap_t , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_samplerDEPTH_common::domWrap_t)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_samplerDEPTH_common::domMinfilter::create(DAE& dae) +{ + domFx_samplerDEPTH_common::domMinfilterRef ref = new domFx_samplerDEPTH_common::domMinfilter(dae); + return ref; +} + + +daeMetaElement * +domFx_samplerDEPTH_common::domMinfilter::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "minfilter" ); + meta->registerClass(domFx_samplerDEPTH_common::domMinfilter::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_filter_common")); + ma->setOffset( daeOffsetOf( domFx_samplerDEPTH_common::domMinfilter , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_samplerDEPTH_common::domMinfilter)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_samplerDEPTH_common::domMagfilter::create(DAE& dae) +{ + domFx_samplerDEPTH_common::domMagfilterRef ref = new domFx_samplerDEPTH_common::domMagfilter(dae); + return ref; +} + + +daeMetaElement * +domFx_samplerDEPTH_common::domMagfilter::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "magfilter" ); + meta->registerClass(domFx_samplerDEPTH_common::domMagfilter::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_filter_common")); + ma->setOffset( daeOffsetOf( domFx_samplerDEPTH_common::domMagfilter , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_samplerDEPTH_common::domMagfilter)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domFx_samplerRECT_common.cpp b/Engine/lib/collada/src/1.4/dom/domFx_samplerRECT_common.cpp new file mode 100644 index 000000000..f55c74eb1 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domFx_samplerRECT_common.cpp @@ -0,0 +1,439 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domFx_samplerRECT_common::create(DAE& dae) +{ + domFx_samplerRECT_commonRef ref = new domFx_samplerRECT_common(dae); + return ref; +} + + +daeMetaElement * +domFx_samplerRECT_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fx_samplerRECT_common" ); + meta->registerClass(domFx_samplerRECT_common::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "source" ); + mea->setOffset( daeOffsetOf(domFx_samplerRECT_common,elemSource) ); + mea->setElementType( domFx_samplerRECT_common::domSource::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "wrap_s" ); + mea->setOffset( daeOffsetOf(domFx_samplerRECT_common,elemWrap_s) ); + mea->setElementType( domFx_samplerRECT_common::domWrap_s::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "wrap_t" ); + mea->setOffset( daeOffsetOf(domFx_samplerRECT_common,elemWrap_t) ); + mea->setElementType( domFx_samplerRECT_common::domWrap_t::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "minfilter" ); + mea->setOffset( daeOffsetOf(domFx_samplerRECT_common,elemMinfilter) ); + mea->setElementType( domFx_samplerRECT_common::domMinfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "magfilter" ); + mea->setOffset( daeOffsetOf(domFx_samplerRECT_common,elemMagfilter) ); + mea->setElementType( domFx_samplerRECT_common::domMagfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 5, 0, 1 ); + mea->setName( "mipfilter" ); + mea->setOffset( daeOffsetOf(domFx_samplerRECT_common,elemMipfilter) ); + mea->setElementType( domFx_samplerRECT_common::domMipfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 6, 0, 1 ); + mea->setName( "border_color" ); + mea->setOffset( daeOffsetOf(domFx_samplerRECT_common,elemBorder_color) ); + mea->setElementType( domFx_samplerRECT_common::domBorder_color::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 7, 0, 1 ); + mea->setName( "mipmap_maxlevel" ); + mea->setOffset( daeOffsetOf(domFx_samplerRECT_common,elemMipmap_maxlevel) ); + mea->setElementType( domFx_samplerRECT_common::domMipmap_maxlevel::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 8, 0, 1 ); + mea->setName( "mipmap_bias" ); + mea->setOffset( daeOffsetOf(domFx_samplerRECT_common,elemMipmap_bias) ); + mea->setElementType( domFx_samplerRECT_common::domMipmap_bias::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 9, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domFx_samplerRECT_common,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 9 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domFx_samplerRECT_common)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_samplerRECT_common::domSource::create(DAE& dae) +{ + domFx_samplerRECT_common::domSourceRef ref = new domFx_samplerRECT_common::domSource(dae); + return ref; +} + + +daeMetaElement * +domFx_samplerRECT_common::domSource::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "source" ); + meta->registerClass(domFx_samplerRECT_common::domSource::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domFx_samplerRECT_common::domSource , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_samplerRECT_common::domSource)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_samplerRECT_common::domWrap_s::create(DAE& dae) +{ + domFx_samplerRECT_common::domWrap_sRef ref = new domFx_samplerRECT_common::domWrap_s(dae); + return ref; +} + + +daeMetaElement * +domFx_samplerRECT_common::domWrap_s::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "wrap_s" ); + meta->registerClass(domFx_samplerRECT_common::domWrap_s::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_wrap_common")); + ma->setOffset( daeOffsetOf( domFx_samplerRECT_common::domWrap_s , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_samplerRECT_common::domWrap_s)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_samplerRECT_common::domWrap_t::create(DAE& dae) +{ + domFx_samplerRECT_common::domWrap_tRef ref = new domFx_samplerRECT_common::domWrap_t(dae); + return ref; +} + + +daeMetaElement * +domFx_samplerRECT_common::domWrap_t::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "wrap_t" ); + meta->registerClass(domFx_samplerRECT_common::domWrap_t::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_wrap_common")); + ma->setOffset( daeOffsetOf( domFx_samplerRECT_common::domWrap_t , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_samplerRECT_common::domWrap_t)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_samplerRECT_common::domMinfilter::create(DAE& dae) +{ + domFx_samplerRECT_common::domMinfilterRef ref = new domFx_samplerRECT_common::domMinfilter(dae); + return ref; +} + + +daeMetaElement * +domFx_samplerRECT_common::domMinfilter::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "minfilter" ); + meta->registerClass(domFx_samplerRECT_common::domMinfilter::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_filter_common")); + ma->setOffset( daeOffsetOf( domFx_samplerRECT_common::domMinfilter , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_samplerRECT_common::domMinfilter)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_samplerRECT_common::domMagfilter::create(DAE& dae) +{ + domFx_samplerRECT_common::domMagfilterRef ref = new domFx_samplerRECT_common::domMagfilter(dae); + return ref; +} + + +daeMetaElement * +domFx_samplerRECT_common::domMagfilter::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "magfilter" ); + meta->registerClass(domFx_samplerRECT_common::domMagfilter::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_filter_common")); + ma->setOffset( daeOffsetOf( domFx_samplerRECT_common::domMagfilter , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_samplerRECT_common::domMagfilter)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_samplerRECT_common::domMipfilter::create(DAE& dae) +{ + domFx_samplerRECT_common::domMipfilterRef ref = new domFx_samplerRECT_common::domMipfilter(dae); + return ref; +} + + +daeMetaElement * +domFx_samplerRECT_common::domMipfilter::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mipfilter" ); + meta->registerClass(domFx_samplerRECT_common::domMipfilter::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_filter_common")); + ma->setOffset( daeOffsetOf( domFx_samplerRECT_common::domMipfilter , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_samplerRECT_common::domMipfilter)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_samplerRECT_common::domBorder_color::create(DAE& dae) +{ + domFx_samplerRECT_common::domBorder_colorRef ref = new domFx_samplerRECT_common::domBorder_color(dae); + return ref; +} + + +daeMetaElement * +domFx_samplerRECT_common::domBorder_color::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "border_color" ); + meta->registerClass(domFx_samplerRECT_common::domBorder_color::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_color_common")); + ma->setOffset( daeOffsetOf( domFx_samplerRECT_common::domBorder_color , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_samplerRECT_common::domBorder_color)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_samplerRECT_common::domMipmap_maxlevel::create(DAE& dae) +{ + domFx_samplerRECT_common::domMipmap_maxlevelRef ref = new domFx_samplerRECT_common::domMipmap_maxlevel(dae); + return ref; +} + + +daeMetaElement * +domFx_samplerRECT_common::domMipmap_maxlevel::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mipmap_maxlevel" ); + meta->registerClass(domFx_samplerRECT_common::domMipmap_maxlevel::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsUnsignedByte")); + ma->setOffset( daeOffsetOf( domFx_samplerRECT_common::domMipmap_maxlevel , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_samplerRECT_common::domMipmap_maxlevel)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_samplerRECT_common::domMipmap_bias::create(DAE& dae) +{ + domFx_samplerRECT_common::domMipmap_biasRef ref = new domFx_samplerRECT_common::domMipmap_bias(dae); + return ref; +} + + +daeMetaElement * +domFx_samplerRECT_common::domMipmap_bias::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mipmap_bias" ); + meta->registerClass(domFx_samplerRECT_common::domMipmap_bias::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsFloat")); + ma->setOffset( daeOffsetOf( domFx_samplerRECT_common::domMipmap_bias , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_samplerRECT_common::domMipmap_bias)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domFx_stenciltarget_common.cpp b/Engine/lib/collada/src/1.4/dom/domFx_stenciltarget_common.cpp new file mode 100644 index 000000000..92f6e643d --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domFx_stenciltarget_common.cpp @@ -0,0 +1,110 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domFx_stenciltarget_common::create(DAE& dae) +{ + domFx_stenciltarget_commonRef ref = new domFx_stenciltarget_common(dae); + return ref; +} + + +daeMetaElement * +domFx_stenciltarget_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fx_stenciltarget_common" ); + meta->registerClass(domFx_stenciltarget_common::create); + + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domFx_stenciltarget_common , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("xsNonNegativeInteger")); + ma->setOffset( daeOffsetOf( domFx_stenciltarget_common , attrIndex )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: face + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "face" ); + ma->setType( dae.getAtomicTypes().get("Fx_surface_face_enum")); + ma->setOffset( daeOffsetOf( domFx_stenciltarget_common , attrFace )); + ma->setContainer( meta ); + ma->setDefaultString( "POSITIVE_X"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: mip + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "mip" ); + ma->setType( dae.getAtomicTypes().get("xsNonNegativeInteger")); + ma->setOffset( daeOffsetOf( domFx_stenciltarget_common , attrMip )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: slice + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "slice" ); + ma->setType( dae.getAtomicTypes().get("xsNonNegativeInteger")); + ma->setOffset( daeOffsetOf( domFx_stenciltarget_common , attrSlice )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_stenciltarget_common)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domFx_surface_common.cpp b/Engine/lib/collada/src/1.4/dom/domFx_surface_common.cpp new file mode 100644 index 000000000..bd6e44ce6 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domFx_surface_common.cpp @@ -0,0 +1,305 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domFx_surface_common::create(DAE& dae) +{ + domFx_surface_commonRef ref = new domFx_surface_common(dae); + return ref; +} + + +daeMetaElement * +domFx_surface_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fx_surface_common" ); + meta->registerClass(domFx_surface_common::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "fx_surface_init_common" ); + mea->setOffset( daeOffsetOf(domFx_surface_common,elemFx_surface_init_common) ); + mea->setElementType( domFx_surface_init_common::registerElement(dae) ); + cm->appendChild( new daeMetaGroup( mea, meta, cm, 0, 0, 1 ) ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "format" ); + mea->setOffset( daeOffsetOf(domFx_surface_common,elemFormat) ); + mea->setElementType( domFx_surface_common::domFormat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "format_hint" ); + mea->setOffset( daeOffsetOf(domFx_surface_common,elemFormat_hint) ); + mea->setElementType( domFx_surface_format_hint_common::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 3, 0, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "size" ); + mea->setOffset( daeOffsetOf(domFx_surface_common,elemSize) ); + mea->setElementType( domFx_surface_common::domSize::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "viewport_ratio" ); + mea->setOffset( daeOffsetOf(domFx_surface_common,elemViewport_ratio) ); + mea->setElementType( domFx_surface_common::domViewport_ratio::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "mip_levels" ); + mea->setOffset( daeOffsetOf(domFx_surface_common,elemMip_levels) ); + mea->setElementType( domFx_surface_common::domMip_levels::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 5, 0, 1 ); + mea->setName( "mipmap_generate" ); + mea->setOffset( daeOffsetOf(domFx_surface_common,elemMipmap_generate) ); + mea->setElementType( domFx_surface_common::domMipmap_generate::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 6, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domFx_surface_common,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 6 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domFx_surface_common,_contents)); + meta->addContentsOrder(daeOffsetOf(domFx_surface_common,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domFx_surface_common,_CMData), 1); + // Add attribute: type + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "type" ); + ma->setType( dae.getAtomicTypes().get("Fx_surface_type_enum")); + ma->setOffset( daeOffsetOf( domFx_surface_common , attrType )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_surface_common)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_surface_common::domFormat::create(DAE& dae) +{ + domFx_surface_common::domFormatRef ref = new domFx_surface_common::domFormat(dae); + return ref; +} + + +daeMetaElement * +domFx_surface_common::domFormat::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "format" ); + meta->registerClass(domFx_surface_common::domFormat::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsToken")); + ma->setOffset( daeOffsetOf( domFx_surface_common::domFormat , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_surface_common::domFormat)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_surface_common::domSize::create(DAE& dae) +{ + domFx_surface_common::domSizeRef ref = new domFx_surface_common::domSize(dae); + return ref; +} + + +daeMetaElement * +domFx_surface_common::domSize::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "size" ); + meta->registerClass(domFx_surface_common::domSize::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Int3")); + ma->setOffset( daeOffsetOf( domFx_surface_common::domSize , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_surface_common::domSize)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_surface_common::domViewport_ratio::create(DAE& dae) +{ + domFx_surface_common::domViewport_ratioRef ref = new domFx_surface_common::domViewport_ratio(dae); + return ref; +} + + +daeMetaElement * +domFx_surface_common::domViewport_ratio::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "viewport_ratio" ); + meta->registerClass(domFx_surface_common::domViewport_ratio::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float2")); + ma->setOffset( daeOffsetOf( domFx_surface_common::domViewport_ratio , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_surface_common::domViewport_ratio)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_surface_common::domMip_levels::create(DAE& dae) +{ + domFx_surface_common::domMip_levelsRef ref = new domFx_surface_common::domMip_levels(dae); + return ref; +} + + +daeMetaElement * +domFx_surface_common::domMip_levels::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mip_levels" ); + meta->registerClass(domFx_surface_common::domMip_levels::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsUnsignedInt")); + ma->setOffset( daeOffsetOf( domFx_surface_common::domMip_levels , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_surface_common::domMip_levels)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_surface_common::domMipmap_generate::create(DAE& dae) +{ + domFx_surface_common::domMipmap_generateRef ref = new domFx_surface_common::domMipmap_generate(dae); + return ref; +} + + +daeMetaElement * +domFx_surface_common::domMipmap_generate::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mipmap_generate" ); + meta->registerClass(domFx_surface_common::domMipmap_generate::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsBoolean")); + ma->setOffset( daeOffsetOf( domFx_surface_common::domMipmap_generate , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_surface_common::domMipmap_generate)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domFx_surface_format_hint_common.cpp b/Engine/lib/collada/src/1.4/dom/domFx_surface_format_hint_common.cpp new file mode 100644 index 000000000..85b5b0c9c --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domFx_surface_format_hint_common.cpp @@ -0,0 +1,229 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domFx_surface_format_hint_common::create(DAE& dae) +{ + domFx_surface_format_hint_commonRef ref = new domFx_surface_format_hint_common(dae); + return ref; +} + + +daeMetaElement * +domFx_surface_format_hint_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fx_surface_format_hint_common" ); + meta->registerClass(domFx_surface_format_hint_common::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "channels" ); + mea->setOffset( daeOffsetOf(domFx_surface_format_hint_common,elemChannels) ); + mea->setElementType( domFx_surface_format_hint_common::domChannels::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "range" ); + mea->setOffset( daeOffsetOf(domFx_surface_format_hint_common,elemRange) ); + mea->setElementType( domFx_surface_format_hint_common::domRange::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "precision" ); + mea->setOffset( daeOffsetOf(domFx_surface_format_hint_common,elemPrecision) ); + mea->setElementType( domFx_surface_format_hint_common::domPrecision::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3, 0, -1 ); + mea->setName( "option" ); + mea->setOffset( daeOffsetOf(domFx_surface_format_hint_common,elemOption_array) ); + mea->setElementType( domFx_surface_format_hint_common::domOption::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 4, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domFx_surface_format_hint_common,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 4 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domFx_surface_format_hint_common)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_surface_format_hint_common::domChannels::create(DAE& dae) +{ + domFx_surface_format_hint_common::domChannelsRef ref = new domFx_surface_format_hint_common::domChannels(dae); + return ref; +} + + +daeMetaElement * +domFx_surface_format_hint_common::domChannels::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "channels" ); + meta->registerClass(domFx_surface_format_hint_common::domChannels::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_surface_format_hint_channels_enum")); + ma->setOffset( daeOffsetOf( domFx_surface_format_hint_common::domChannels , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_surface_format_hint_common::domChannels)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_surface_format_hint_common::domRange::create(DAE& dae) +{ + domFx_surface_format_hint_common::domRangeRef ref = new domFx_surface_format_hint_common::domRange(dae); + return ref; +} + + +daeMetaElement * +domFx_surface_format_hint_common::domRange::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "range" ); + meta->registerClass(domFx_surface_format_hint_common::domRange::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_surface_format_hint_range_enum")); + ma->setOffset( daeOffsetOf( domFx_surface_format_hint_common::domRange , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_surface_format_hint_common::domRange)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_surface_format_hint_common::domPrecision::create(DAE& dae) +{ + domFx_surface_format_hint_common::domPrecisionRef ref = new domFx_surface_format_hint_common::domPrecision(dae); + return ref; +} + + +daeMetaElement * +domFx_surface_format_hint_common::domPrecision::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "precision" ); + meta->registerClass(domFx_surface_format_hint_common::domPrecision::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_surface_format_hint_precision_enum")); + ma->setOffset( daeOffsetOf( domFx_surface_format_hint_common::domPrecision , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_surface_format_hint_common::domPrecision)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_surface_format_hint_common::domOption::create(DAE& dae) +{ + domFx_surface_format_hint_common::domOptionRef ref = new domFx_surface_format_hint_common::domOption(dae); + return ref; +} + + +daeMetaElement * +domFx_surface_format_hint_common::domOption::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "option" ); + meta->registerClass(domFx_surface_format_hint_common::domOption::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_surface_format_hint_option_enum")); + ma->setOffset( daeOffsetOf( domFx_surface_format_hint_common::domOption , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_surface_format_hint_common::domOption)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domFx_surface_init_common.cpp b/Engine/lib/collada/src/1.4/dom/domFx_surface_init_common.cpp new file mode 100644 index 000000000..df56fe9c8 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domFx_surface_init_common.cpp @@ -0,0 +1,150 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domFx_surface_init_common::create(DAE& dae) +{ + domFx_surface_init_commonRef ref = new domFx_surface_init_common(dae); + return ref; +} + + +daeMetaElement * +domFx_surface_init_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fx_surface_init_common" ); + meta->registerClass(domFx_surface_init_common::create); + + meta->setIsTransparent( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "init_as_null" ); + mea->setOffset( daeOffsetOf(domFx_surface_init_common,elemInit_as_null) ); + mea->setElementType( domFx_surface_init_common::domInit_as_null::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "init_as_target" ); + mea->setOffset( daeOffsetOf(domFx_surface_init_common,elemInit_as_target) ); + mea->setElementType( domFx_surface_init_common::domInit_as_target::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "init_cube" ); + mea->setOffset( daeOffsetOf(domFx_surface_init_common,elemInit_cube) ); + mea->setElementType( domFx_surface_init_cube_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "init_volume" ); + mea->setOffset( daeOffsetOf(domFx_surface_init_common,elemInit_volume) ); + mea->setElementType( domFx_surface_init_volume_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "init_planar" ); + mea->setOffset( daeOffsetOf(domFx_surface_init_common,elemInit_planar) ); + mea->setElementType( domFx_surface_init_planar_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, -1 ); + mea->setName( "init_from" ); + mea->setOffset( daeOffsetOf(domFx_surface_init_common,elemInit_from_array) ); + mea->setElementType( domFx_surface_init_from_common::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domFx_surface_init_common,_contents)); + meta->addContentsOrder(daeOffsetOf(domFx_surface_init_common,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domFx_surface_init_common,_CMData), 1); + meta->setElementSize(sizeof(domFx_surface_init_common)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_surface_init_common::domInit_as_null::create(DAE& dae) +{ + domFx_surface_init_common::domInit_as_nullRef ref = new domFx_surface_init_common::domInit_as_null(dae); + return ref; +} + + +daeMetaElement * +domFx_surface_init_common::domInit_as_null::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "init_as_null" ); + meta->registerClass(domFx_surface_init_common::domInit_as_null::create); + + meta->setIsInnerClass( true ); + + meta->setElementSize(sizeof(domFx_surface_init_common::domInit_as_null)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_surface_init_common::domInit_as_target::create(DAE& dae) +{ + domFx_surface_init_common::domInit_as_targetRef ref = new domFx_surface_init_common::domInit_as_target(dae); + return ref; +} + + +daeMetaElement * +domFx_surface_init_common::domInit_as_target::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "init_as_target" ); + meta->registerClass(domFx_surface_init_common::domInit_as_target::create); + + meta->setIsInnerClass( true ); + + meta->setElementSize(sizeof(domFx_surface_init_common::domInit_as_target)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domFx_surface_init_cube_common.cpp b/Engine/lib/collada/src/1.4/dom/domFx_surface_init_cube_common.cpp new file mode 100644 index 000000000..a59eec314 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domFx_surface_init_cube_common.cpp @@ -0,0 +1,242 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domFx_surface_init_cube_common::create(DAE& dae) +{ + domFx_surface_init_cube_commonRef ref = new domFx_surface_init_cube_common(dae); + return ref; +} + + +daeMetaElement * +domFx_surface_init_cube_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fx_surface_init_cube_common" ); + meta->registerClass(domFx_surface_init_cube_common::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "all" ); + mea->setOffset( daeOffsetOf(domFx_surface_init_cube_common,elemAll) ); + mea->setElementType( domFx_surface_init_cube_common::domAll::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "primary" ); + mea->setOffset( daeOffsetOf(domFx_surface_init_cube_common,elemPrimary) ); + mea->setElementType( domFx_surface_init_cube_common::domPrimary::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 6, 6 ); + mea->setName( "face" ); + mea->setOffset( daeOffsetOf(domFx_surface_init_cube_common,elemFace_array) ); + mea->setElementType( domFx_surface_init_cube_common::domFace::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domFx_surface_init_cube_common,_contents)); + meta->addContentsOrder(daeOffsetOf(domFx_surface_init_cube_common,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domFx_surface_init_cube_common,_CMData), 1); + meta->setElementSize(sizeof(domFx_surface_init_cube_common)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_surface_init_cube_common::domAll::create(DAE& dae) +{ + domFx_surface_init_cube_common::domAllRef ref = new domFx_surface_init_cube_common::domAll(dae); + return ref; +} + + +daeMetaElement * +domFx_surface_init_cube_common::domAll::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "all" ); + meta->registerClass(domFx_surface_init_cube_common::domAll::create); + + meta->setIsInnerClass( true ); + + // Add attribute: ref + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "ref" ); + ma->setType( dae.getAtomicTypes().get("xsIDREF")); + ma->setOffset( daeOffsetOf( domFx_surface_init_cube_common::domAll , attrRef )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_surface_init_cube_common::domAll)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_surface_init_cube_common::domPrimary::create(DAE& dae) +{ + domFx_surface_init_cube_common::domPrimaryRef ref = new domFx_surface_init_cube_common::domPrimary(dae); + return ref; +} + + +daeMetaElement * +domFx_surface_init_cube_common::domPrimary::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "primary" ); + meta->registerClass(domFx_surface_init_cube_common::domPrimary::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 0, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 6, 6 ); + mea->setName( "order" ); + mea->setOffset( daeOffsetOf(domFx_surface_init_cube_common::domPrimary,elemOrder_array) ); + mea->setElementType( domFx_surface_init_cube_common::domPrimary::domOrder::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + + // Add attribute: ref + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "ref" ); + ma->setType( dae.getAtomicTypes().get("xsIDREF")); + ma->setOffset( daeOffsetOf( domFx_surface_init_cube_common::domPrimary , attrRef )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_surface_init_cube_common::domPrimary)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_surface_init_cube_common::domPrimary::domOrder::create(DAE& dae) +{ + domFx_surface_init_cube_common::domPrimary::domOrderRef ref = new domFx_surface_init_cube_common::domPrimary::domOrder(dae); + return ref; +} + + +daeMetaElement * +domFx_surface_init_cube_common::domPrimary::domOrder::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "order" ); + meta->registerClass(domFx_surface_init_cube_common::domPrimary::domOrder::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_surface_face_enum")); + ma->setOffset( daeOffsetOf( domFx_surface_init_cube_common::domPrimary::domOrder , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_surface_init_cube_common::domPrimary::domOrder)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_surface_init_cube_common::domFace::create(DAE& dae) +{ + domFx_surface_init_cube_common::domFaceRef ref = new domFx_surface_init_cube_common::domFace(dae); + return ref; +} + + +daeMetaElement * +domFx_surface_init_cube_common::domFace::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "face" ); + meta->registerClass(domFx_surface_init_cube_common::domFace::create); + + meta->setIsInnerClass( true ); + + // Add attribute: ref + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "ref" ); + ma->setType( dae.getAtomicTypes().get("xsIDREF")); + ma->setOffset( daeOffsetOf( domFx_surface_init_cube_common::domFace , attrRef )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_surface_init_cube_common::domFace)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domFx_surface_init_from_common.cpp b/Engine/lib/collada/src/1.4/dom/domFx_surface_init_from_common.cpp new file mode 100644 index 000000000..7b15c3df1 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domFx_surface_init_from_common.cpp @@ -0,0 +1,94 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domFx_surface_init_from_common::create(DAE& dae) +{ + domFx_surface_init_from_commonRef ref = new domFx_surface_init_from_common(dae); + return ref; +} + + +daeMetaElement * +domFx_surface_init_from_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fx_surface_init_from_common" ); + meta->registerClass(domFx_surface_init_from_common::create); + + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsIDREF")); + ma->setOffset( daeOffsetOf( domFx_surface_init_from_common , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: mip + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "mip" ); + ma->setType( dae.getAtomicTypes().get("xsUnsignedInt")); + ma->setOffset( daeOffsetOf( domFx_surface_init_from_common , attrMip )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + + meta->appendAttribute(ma); + } + + // Add attribute: slice + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "slice" ); + ma->setType( dae.getAtomicTypes().get("xsUnsignedInt")); + ma->setOffset( daeOffsetOf( domFx_surface_init_from_common , attrSlice )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + + meta->appendAttribute(ma); + } + + // Add attribute: face + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "face" ); + ma->setType( dae.getAtomicTypes().get("Fx_surface_face_enum")); + ma->setOffset( daeOffsetOf( domFx_surface_init_from_common , attrFace )); + ma->setContainer( meta ); + ma->setDefaultString( "POSITIVE_X"); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_surface_init_from_common)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domFx_surface_init_planar_common.cpp b/Engine/lib/collada/src/1.4/dom/domFx_surface_init_planar_common.cpp new file mode 100644 index 000000000..f1a9e10ce --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domFx_surface_init_planar_common.cpp @@ -0,0 +1,104 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domFx_surface_init_planar_common::create(DAE& dae) +{ + domFx_surface_init_planar_commonRef ref = new domFx_surface_init_planar_common(dae); + return ref; +} + + +daeMetaElement * +domFx_surface_init_planar_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fx_surface_init_planar_common" ); + meta->registerClass(domFx_surface_init_planar_common::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "all" ); + mea->setOffset( daeOffsetOf(domFx_surface_init_planar_common,elemAll) ); + mea->setElementType( domFx_surface_init_planar_common::domAll::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domFx_surface_init_planar_common,_contents)); + meta->addContentsOrder(daeOffsetOf(domFx_surface_init_planar_common,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domFx_surface_init_planar_common,_CMData), 1); + meta->setElementSize(sizeof(domFx_surface_init_planar_common)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_surface_init_planar_common::domAll::create(DAE& dae) +{ + domFx_surface_init_planar_common::domAllRef ref = new domFx_surface_init_planar_common::domAll(dae); + return ref; +} + + +daeMetaElement * +domFx_surface_init_planar_common::domAll::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "all" ); + meta->registerClass(domFx_surface_init_planar_common::domAll::create); + + meta->setIsInnerClass( true ); + + // Add attribute: ref + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "ref" ); + ma->setType( dae.getAtomicTypes().get("xsIDREF")); + ma->setOffset( daeOffsetOf( domFx_surface_init_planar_common::domAll , attrRef )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_surface_init_planar_common::domAll)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domFx_surface_init_volume_common.cpp b/Engine/lib/collada/src/1.4/dom/domFx_surface_init_volume_common.cpp new file mode 100644 index 000000000..cfdd46620 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domFx_surface_init_volume_common.cpp @@ -0,0 +1,149 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domFx_surface_init_volume_common::create(DAE& dae) +{ + domFx_surface_init_volume_commonRef ref = new domFx_surface_init_volume_common(dae); + return ref; +} + + +daeMetaElement * +domFx_surface_init_volume_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fx_surface_init_volume_common" ); + meta->registerClass(domFx_surface_init_volume_common::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "all" ); + mea->setOffset( daeOffsetOf(domFx_surface_init_volume_common,elemAll) ); + mea->setElementType( domFx_surface_init_volume_common::domAll::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "primary" ); + mea->setOffset( daeOffsetOf(domFx_surface_init_volume_common,elemPrimary) ); + mea->setElementType( domFx_surface_init_volume_common::domPrimary::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domFx_surface_init_volume_common,_contents)); + meta->addContentsOrder(daeOffsetOf(domFx_surface_init_volume_common,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domFx_surface_init_volume_common,_CMData), 1); + meta->setElementSize(sizeof(domFx_surface_init_volume_common)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_surface_init_volume_common::domAll::create(DAE& dae) +{ + domFx_surface_init_volume_common::domAllRef ref = new domFx_surface_init_volume_common::domAll(dae); + return ref; +} + + +daeMetaElement * +domFx_surface_init_volume_common::domAll::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "all" ); + meta->registerClass(domFx_surface_init_volume_common::domAll::create); + + meta->setIsInnerClass( true ); + + // Add attribute: ref + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "ref" ); + ma->setType( dae.getAtomicTypes().get("xsIDREF")); + ma->setOffset( daeOffsetOf( domFx_surface_init_volume_common::domAll , attrRef )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_surface_init_volume_common::domAll)); + meta->validate(); + + return meta; +} + +daeElementRef +domFx_surface_init_volume_common::domPrimary::create(DAE& dae) +{ + domFx_surface_init_volume_common::domPrimaryRef ref = new domFx_surface_init_volume_common::domPrimary(dae); + return ref; +} + + +daeMetaElement * +domFx_surface_init_volume_common::domPrimary::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "primary" ); + meta->registerClass(domFx_surface_init_volume_common::domPrimary::create); + + meta->setIsInnerClass( true ); + + // Add attribute: ref + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "ref" ); + ma->setType( dae.getAtomicTypes().get("xsIDREF")); + ma->setOffset( daeOffsetOf( domFx_surface_init_volume_common::domPrimary , attrRef )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domFx_surface_init_volume_common::domPrimary)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGeometry.cpp b/Engine/lib/collada/src/1.4/dom/domGeometry.cpp new file mode 100644 index 000000000..54a928be8 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGeometry.cpp @@ -0,0 +1,117 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGeometry::create(DAE& dae) +{ + domGeometryRef ref = new domGeometry(dae); + return ref; +} + + +daeMetaElement * +domGeometry::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "geometry" ); + meta->registerClass(domGeometry::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domGeometry,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 1, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "convex_mesh" ); + mea->setOffset( daeOffsetOf(domGeometry,elemConvex_mesh) ); + mea->setElementType( domConvex_mesh::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "mesh" ); + mea->setOffset( daeOffsetOf(domGeometry,elemMesh) ); + mea->setElementType( domMesh::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "spline" ); + mea->setOffset( daeOffsetOf(domGeometry,elemSpline) ); + mea->setElementType( domSpline::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domGeometry,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domGeometry,_contents)); + meta->addContentsOrder(daeOffsetOf(domGeometry,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domGeometry,_CMData), 1); + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domGeometry , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGeometry , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGeometry)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGl_hook_abstract.cpp b/Engine/lib/collada/src/1.4/dom/domGl_hook_abstract.cpp new file mode 100644 index 000000000..904c11c29 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGl_hook_abstract.cpp @@ -0,0 +1,50 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGl_hook_abstract::create(DAE& dae) +{ + domGl_hook_abstractRef ref = new domGl_hook_abstract(dae); + return ref; +} + + +daeMetaElement * +domGl_hook_abstract::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "gl_hook_abstract" ); + meta->registerClass(domGl_hook_abstract::create); + + meta->setIsAbstract( true ); + + meta->setElementSize(sizeof(domGl_hook_abstract)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGl_pipeline_settings.cpp b/Engine/lib/collada/src/1.4/dom/domGl_pipeline_settings.cpp new file mode 100644 index 000000000..9630f6ded --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGl_pipeline_settings.cpp @@ -0,0 +1,8369 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGl_pipeline_settings::create(DAE& dae) +{ + domGl_pipeline_settingsRef ref = new domGl_pipeline_settings(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "gl_pipeline_settings" ); + meta->registerClass(domGl_pipeline_settings::create); + + meta->setIsTransparent( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "alpha_func" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemAlpha_func) ); + mea->setElementType( domGl_pipeline_settings::domAlpha_func::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "blend_func" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemBlend_func) ); + mea->setElementType( domGl_pipeline_settings::domBlend_func::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "blend_func_separate" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemBlend_func_separate) ); + mea->setElementType( domGl_pipeline_settings::domBlend_func_separate::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "blend_equation" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemBlend_equation) ); + mea->setElementType( domGl_pipeline_settings::domBlend_equation::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "blend_equation_separate" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemBlend_equation_separate) ); + mea->setElementType( domGl_pipeline_settings::domBlend_equation_separate::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "color_material" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemColor_material) ); + mea->setElementType( domGl_pipeline_settings::domColor_material::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "cull_face" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemCull_face) ); + mea->setElementType( domGl_pipeline_settings::domCull_face::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "depth_func" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemDepth_func) ); + mea->setElementType( domGl_pipeline_settings::domDepth_func::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fog_mode" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemFog_mode) ); + mea->setElementType( domGl_pipeline_settings::domFog_mode::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fog_coord_src" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemFog_coord_src) ); + mea->setElementType( domGl_pipeline_settings::domFog_coord_src::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "front_face" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemFront_face) ); + mea->setElementType( domGl_pipeline_settings::domFront_face::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_model_color_control" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemLight_model_color_control) ); + mea->setElementType( domGl_pipeline_settings::domLight_model_color_control::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "logic_op" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemLogic_op) ); + mea->setElementType( domGl_pipeline_settings::domLogic_op::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "polygon_mode" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemPolygon_mode) ); + mea->setElementType( domGl_pipeline_settings::domPolygon_mode::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "shade_model" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemShade_model) ); + mea->setElementType( domGl_pipeline_settings::domShade_model::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "stencil_func" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemStencil_func) ); + mea->setElementType( domGl_pipeline_settings::domStencil_func::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "stencil_op" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemStencil_op) ); + mea->setElementType( domGl_pipeline_settings::domStencil_op::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "stencil_func_separate" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemStencil_func_separate) ); + mea->setElementType( domGl_pipeline_settings::domStencil_func_separate::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "stencil_op_separate" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemStencil_op_separate) ); + mea->setElementType( domGl_pipeline_settings::domStencil_op_separate::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "stencil_mask_separate" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemStencil_mask_separate) ); + mea->setElementType( domGl_pipeline_settings::domStencil_mask_separate::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemLight_enable) ); + mea->setElementType( domGl_pipeline_settings::domLight_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_ambient" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemLight_ambient) ); + mea->setElementType( domGl_pipeline_settings::domLight_ambient::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_diffuse" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemLight_diffuse) ); + mea->setElementType( domGl_pipeline_settings::domLight_diffuse::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_specular" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemLight_specular) ); + mea->setElementType( domGl_pipeline_settings::domLight_specular::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_position" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemLight_position) ); + mea->setElementType( domGl_pipeline_settings::domLight_position::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_constant_attenuation" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemLight_constant_attenuation) ); + mea->setElementType( domGl_pipeline_settings::domLight_constant_attenuation::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_linear_attenuation" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemLight_linear_attenuation) ); + mea->setElementType( domGl_pipeline_settings::domLight_linear_attenuation::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_quadratic_attenuation" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemLight_quadratic_attenuation) ); + mea->setElementType( domGl_pipeline_settings::domLight_quadratic_attenuation::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_spot_cutoff" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemLight_spot_cutoff) ); + mea->setElementType( domGl_pipeline_settings::domLight_spot_cutoff::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_spot_direction" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemLight_spot_direction) ); + mea->setElementType( domGl_pipeline_settings::domLight_spot_direction::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_spot_exponent" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemLight_spot_exponent) ); + mea->setElementType( domGl_pipeline_settings::domLight_spot_exponent::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "texture1D" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemTexture1D) ); + mea->setElementType( domGl_pipeline_settings::domTexture1D::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "texture2D" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemTexture2D) ); + mea->setElementType( domGl_pipeline_settings::domTexture2D::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "texture3D" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemTexture3D) ); + mea->setElementType( domGl_pipeline_settings::domTexture3D::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "textureCUBE" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemTextureCUBE) ); + mea->setElementType( domGl_pipeline_settings::domTextureCUBE::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "textureRECT" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemTextureRECT) ); + mea->setElementType( domGl_pipeline_settings::domTextureRECT::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "textureDEPTH" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemTextureDEPTH) ); + mea->setElementType( domGl_pipeline_settings::domTextureDEPTH::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "texture1D_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemTexture1D_enable) ); + mea->setElementType( domGl_pipeline_settings::domTexture1D_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "texture2D_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemTexture2D_enable) ); + mea->setElementType( domGl_pipeline_settings::domTexture2D_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "texture3D_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemTexture3D_enable) ); + mea->setElementType( domGl_pipeline_settings::domTexture3D_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "textureCUBE_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemTextureCUBE_enable) ); + mea->setElementType( domGl_pipeline_settings::domTextureCUBE_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "textureRECT_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemTextureRECT_enable) ); + mea->setElementType( domGl_pipeline_settings::domTextureRECT_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "textureDEPTH_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemTextureDEPTH_enable) ); + mea->setElementType( domGl_pipeline_settings::domTextureDEPTH_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "texture_env_color" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemTexture_env_color) ); + mea->setElementType( domGl_pipeline_settings::domTexture_env_color::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "texture_env_mode" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemTexture_env_mode) ); + mea->setElementType( domGl_pipeline_settings::domTexture_env_mode::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "clip_plane" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemClip_plane) ); + mea->setElementType( domGl_pipeline_settings::domClip_plane::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "clip_plane_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemClip_plane_enable) ); + mea->setElementType( domGl_pipeline_settings::domClip_plane_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "blend_color" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemBlend_color) ); + mea->setElementType( domGl_pipeline_settings::domBlend_color::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "clear_color" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemClear_color) ); + mea->setElementType( domGl_pipeline_settings::domClear_color::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "clear_stencil" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemClear_stencil) ); + mea->setElementType( domGl_pipeline_settings::domClear_stencil::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "clear_depth" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemClear_depth) ); + mea->setElementType( domGl_pipeline_settings::domClear_depth::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "color_mask" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemColor_mask) ); + mea->setElementType( domGl_pipeline_settings::domColor_mask::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "depth_bounds" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemDepth_bounds) ); + mea->setElementType( domGl_pipeline_settings::domDepth_bounds::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "depth_mask" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemDepth_mask) ); + mea->setElementType( domGl_pipeline_settings::domDepth_mask::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "depth_range" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemDepth_range) ); + mea->setElementType( domGl_pipeline_settings::domDepth_range::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fog_density" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemFog_density) ); + mea->setElementType( domGl_pipeline_settings::domFog_density::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fog_start" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemFog_start) ); + mea->setElementType( domGl_pipeline_settings::domFog_start::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fog_end" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemFog_end) ); + mea->setElementType( domGl_pipeline_settings::domFog_end::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fog_color" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemFog_color) ); + mea->setElementType( domGl_pipeline_settings::domFog_color::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_model_ambient" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemLight_model_ambient) ); + mea->setElementType( domGl_pipeline_settings::domLight_model_ambient::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "lighting_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemLighting_enable) ); + mea->setElementType( domGl_pipeline_settings::domLighting_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "line_stipple" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemLine_stipple) ); + mea->setElementType( domGl_pipeline_settings::domLine_stipple::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "line_width" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemLine_width) ); + mea->setElementType( domGl_pipeline_settings::domLine_width::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "material_ambient" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemMaterial_ambient) ); + mea->setElementType( domGl_pipeline_settings::domMaterial_ambient::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "material_diffuse" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemMaterial_diffuse) ); + mea->setElementType( domGl_pipeline_settings::domMaterial_diffuse::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "material_emission" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemMaterial_emission) ); + mea->setElementType( domGl_pipeline_settings::domMaterial_emission::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "material_shininess" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemMaterial_shininess) ); + mea->setElementType( domGl_pipeline_settings::domMaterial_shininess::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "material_specular" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemMaterial_specular) ); + mea->setElementType( domGl_pipeline_settings::domMaterial_specular::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "model_view_matrix" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemModel_view_matrix) ); + mea->setElementType( domGl_pipeline_settings::domModel_view_matrix::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "point_distance_attenuation" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemPoint_distance_attenuation) ); + mea->setElementType( domGl_pipeline_settings::domPoint_distance_attenuation::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "point_fade_threshold_size" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemPoint_fade_threshold_size) ); + mea->setElementType( domGl_pipeline_settings::domPoint_fade_threshold_size::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "point_size" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemPoint_size) ); + mea->setElementType( domGl_pipeline_settings::domPoint_size::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "point_size_min" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemPoint_size_min) ); + mea->setElementType( domGl_pipeline_settings::domPoint_size_min::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "point_size_max" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemPoint_size_max) ); + mea->setElementType( domGl_pipeline_settings::domPoint_size_max::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "polygon_offset" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemPolygon_offset) ); + mea->setElementType( domGl_pipeline_settings::domPolygon_offset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "projection_matrix" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemProjection_matrix) ); + mea->setElementType( domGl_pipeline_settings::domProjection_matrix::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "scissor" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemScissor) ); + mea->setElementType( domGl_pipeline_settings::domScissor::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "stencil_mask" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemStencil_mask) ); + mea->setElementType( domGl_pipeline_settings::domStencil_mask::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "alpha_test_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemAlpha_test_enable) ); + mea->setElementType( domGl_pipeline_settings::domAlpha_test_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "auto_normal_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemAuto_normal_enable) ); + mea->setElementType( domGl_pipeline_settings::domAuto_normal_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "blend_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemBlend_enable) ); + mea->setElementType( domGl_pipeline_settings::domBlend_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "color_logic_op_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemColor_logic_op_enable) ); + mea->setElementType( domGl_pipeline_settings::domColor_logic_op_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "color_material_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemColor_material_enable) ); + mea->setElementType( domGl_pipeline_settings::domColor_material_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "cull_face_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemCull_face_enable) ); + mea->setElementType( domGl_pipeline_settings::domCull_face_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "depth_bounds_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemDepth_bounds_enable) ); + mea->setElementType( domGl_pipeline_settings::domDepth_bounds_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "depth_clamp_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemDepth_clamp_enable) ); + mea->setElementType( domGl_pipeline_settings::domDepth_clamp_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "depth_test_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemDepth_test_enable) ); + mea->setElementType( domGl_pipeline_settings::domDepth_test_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "dither_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemDither_enable) ); + mea->setElementType( domGl_pipeline_settings::domDither_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fog_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemFog_enable) ); + mea->setElementType( domGl_pipeline_settings::domFog_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_model_local_viewer_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemLight_model_local_viewer_enable) ); + mea->setElementType( domGl_pipeline_settings::domLight_model_local_viewer_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_model_two_side_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemLight_model_two_side_enable) ); + mea->setElementType( domGl_pipeline_settings::domLight_model_two_side_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "line_smooth_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemLine_smooth_enable) ); + mea->setElementType( domGl_pipeline_settings::domLine_smooth_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "line_stipple_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemLine_stipple_enable) ); + mea->setElementType( domGl_pipeline_settings::domLine_stipple_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "logic_op_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemLogic_op_enable) ); + mea->setElementType( domGl_pipeline_settings::domLogic_op_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "multisample_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemMultisample_enable) ); + mea->setElementType( domGl_pipeline_settings::domMultisample_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "normalize_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemNormalize_enable) ); + mea->setElementType( domGl_pipeline_settings::domNormalize_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "point_smooth_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemPoint_smooth_enable) ); + mea->setElementType( domGl_pipeline_settings::domPoint_smooth_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "polygon_offset_fill_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemPolygon_offset_fill_enable) ); + mea->setElementType( domGl_pipeline_settings::domPolygon_offset_fill_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "polygon_offset_line_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemPolygon_offset_line_enable) ); + mea->setElementType( domGl_pipeline_settings::domPolygon_offset_line_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "polygon_offset_point_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemPolygon_offset_point_enable) ); + mea->setElementType( domGl_pipeline_settings::domPolygon_offset_point_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "polygon_smooth_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemPolygon_smooth_enable) ); + mea->setElementType( domGl_pipeline_settings::domPolygon_smooth_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "polygon_stipple_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemPolygon_stipple_enable) ); + mea->setElementType( domGl_pipeline_settings::domPolygon_stipple_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "rescale_normal_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemRescale_normal_enable) ); + mea->setElementType( domGl_pipeline_settings::domRescale_normal_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "sample_alpha_to_coverage_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemSample_alpha_to_coverage_enable) ); + mea->setElementType( domGl_pipeline_settings::domSample_alpha_to_coverage_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "sample_alpha_to_one_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemSample_alpha_to_one_enable) ); + mea->setElementType( domGl_pipeline_settings::domSample_alpha_to_one_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "sample_coverage_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemSample_coverage_enable) ); + mea->setElementType( domGl_pipeline_settings::domSample_coverage_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "scissor_test_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemScissor_test_enable) ); + mea->setElementType( domGl_pipeline_settings::domScissor_test_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "stencil_test_enable" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemStencil_test_enable) ); + mea->setElementType( domGl_pipeline_settings::domStencil_test_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "gl_hook_abstract" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings,elemGl_hook_abstract) ); + mea->setElementType( domGl_hook_abstract::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domGl_pipeline_settings,_contents)); + meta->addContentsOrder(daeOffsetOf(domGl_pipeline_settings,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domGl_pipeline_settings,_CMData), 1); + meta->setElementSize(sizeof(domGl_pipeline_settings)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domAlpha_func::create(DAE& dae) +{ + domGl_pipeline_settings::domAlpha_funcRef ref = new domGl_pipeline_settings::domAlpha_func(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domAlpha_func::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "alpha_func" ); + meta->registerClass(domGl_pipeline_settings::domAlpha_func::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "func" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domAlpha_func,elemFunc) ); + mea->setElementType( domGl_pipeline_settings::domAlpha_func::domFunc::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "value" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domAlpha_func,elemValue) ); + mea->setElementType( domGl_pipeline_settings::domAlpha_func::domValue::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domGl_pipeline_settings::domAlpha_func)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domAlpha_func::domFunc::create(DAE& dae) +{ + domGl_pipeline_settings::domAlpha_func::domFuncRef ref = new domGl_pipeline_settings::domAlpha_func::domFunc(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domAlpha_func::domFunc::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "func" ); + meta->registerClass(domGl_pipeline_settings::domAlpha_func::domFunc::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_func_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domAlpha_func::domFunc , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "ALWAYS"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domAlpha_func::domFunc , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domAlpha_func::domFunc)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domAlpha_func::domValue::create(DAE& dae) +{ + domGl_pipeline_settings::domAlpha_func::domValueRef ref = new domGl_pipeline_settings::domAlpha_func::domValue(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domAlpha_func::domValue::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "value" ); + meta->registerClass(domGl_pipeline_settings::domAlpha_func::domValue::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_alpha_value_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domAlpha_func::domValue , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0.0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domAlpha_func::domValue , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domAlpha_func::domValue)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domBlend_func::create(DAE& dae) +{ + domGl_pipeline_settings::domBlend_funcRef ref = new domGl_pipeline_settings::domBlend_func(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domBlend_func::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "blend_func" ); + meta->registerClass(domGl_pipeline_settings::domBlend_func::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "src" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domBlend_func,elemSrc) ); + mea->setElementType( domGl_pipeline_settings::domBlend_func::domSrc::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "dest" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domBlend_func,elemDest) ); + mea->setElementType( domGl_pipeline_settings::domBlend_func::domDest::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domGl_pipeline_settings::domBlend_func)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domBlend_func::domSrc::create(DAE& dae) +{ + domGl_pipeline_settings::domBlend_func::domSrcRef ref = new domGl_pipeline_settings::domBlend_func::domSrc(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domBlend_func::domSrc::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "src" ); + meta->registerClass(domGl_pipeline_settings::domBlend_func::domSrc::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_blend_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domBlend_func::domSrc , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "ONE"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domBlend_func::domSrc , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domBlend_func::domSrc)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domBlend_func::domDest::create(DAE& dae) +{ + domGl_pipeline_settings::domBlend_func::domDestRef ref = new domGl_pipeline_settings::domBlend_func::domDest(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domBlend_func::domDest::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "dest" ); + meta->registerClass(domGl_pipeline_settings::domBlend_func::domDest::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_blend_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domBlend_func::domDest , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "ZERO"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domBlend_func::domDest , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domBlend_func::domDest)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domBlend_func_separate::create(DAE& dae) +{ + domGl_pipeline_settings::domBlend_func_separateRef ref = new domGl_pipeline_settings::domBlend_func_separate(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domBlend_func_separate::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "blend_func_separate" ); + meta->registerClass(domGl_pipeline_settings::domBlend_func_separate::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "src_rgb" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domBlend_func_separate,elemSrc_rgb) ); + mea->setElementType( domGl_pipeline_settings::domBlend_func_separate::domSrc_rgb::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "dest_rgb" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domBlend_func_separate,elemDest_rgb) ); + mea->setElementType( domGl_pipeline_settings::domBlend_func_separate::domDest_rgb::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 1, 1 ); + mea->setName( "src_alpha" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domBlend_func_separate,elemSrc_alpha) ); + mea->setElementType( domGl_pipeline_settings::domBlend_func_separate::domSrc_alpha::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 1, 1 ); + mea->setName( "dest_alpha" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domBlend_func_separate,elemDest_alpha) ); + mea->setElementType( domGl_pipeline_settings::domBlend_func_separate::domDest_alpha::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domGl_pipeline_settings::domBlend_func_separate)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domBlend_func_separate::domSrc_rgb::create(DAE& dae) +{ + domGl_pipeline_settings::domBlend_func_separate::domSrc_rgbRef ref = new domGl_pipeline_settings::domBlend_func_separate::domSrc_rgb(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domBlend_func_separate::domSrc_rgb::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "src_rgb" ); + meta->registerClass(domGl_pipeline_settings::domBlend_func_separate::domSrc_rgb::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_blend_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domBlend_func_separate::domSrc_rgb , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "ONE"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domBlend_func_separate::domSrc_rgb , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domBlend_func_separate::domSrc_rgb)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domBlend_func_separate::domDest_rgb::create(DAE& dae) +{ + domGl_pipeline_settings::domBlend_func_separate::domDest_rgbRef ref = new domGl_pipeline_settings::domBlend_func_separate::domDest_rgb(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domBlend_func_separate::domDest_rgb::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "dest_rgb" ); + meta->registerClass(domGl_pipeline_settings::domBlend_func_separate::domDest_rgb::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_blend_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domBlend_func_separate::domDest_rgb , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "ZERO"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domBlend_func_separate::domDest_rgb , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domBlend_func_separate::domDest_rgb)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domBlend_func_separate::domSrc_alpha::create(DAE& dae) +{ + domGl_pipeline_settings::domBlend_func_separate::domSrc_alphaRef ref = new domGl_pipeline_settings::domBlend_func_separate::domSrc_alpha(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domBlend_func_separate::domSrc_alpha::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "src_alpha" ); + meta->registerClass(domGl_pipeline_settings::domBlend_func_separate::domSrc_alpha::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_blend_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domBlend_func_separate::domSrc_alpha , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "ONE"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domBlend_func_separate::domSrc_alpha , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domBlend_func_separate::domSrc_alpha)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domBlend_func_separate::domDest_alpha::create(DAE& dae) +{ + domGl_pipeline_settings::domBlend_func_separate::domDest_alphaRef ref = new domGl_pipeline_settings::domBlend_func_separate::domDest_alpha(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domBlend_func_separate::domDest_alpha::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "dest_alpha" ); + meta->registerClass(domGl_pipeline_settings::domBlend_func_separate::domDest_alpha::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_blend_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domBlend_func_separate::domDest_alpha , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "ZERO"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domBlend_func_separate::domDest_alpha , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domBlend_func_separate::domDest_alpha)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domBlend_equation::create(DAE& dae) +{ + domGl_pipeline_settings::domBlend_equationRef ref = new domGl_pipeline_settings::domBlend_equation(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domBlend_equation::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "blend_equation" ); + meta->registerClass(domGl_pipeline_settings::domBlend_equation::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_blend_equation_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domBlend_equation , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "FUNC_ADD"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domBlend_equation , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domBlend_equation)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domBlend_equation_separate::create(DAE& dae) +{ + domGl_pipeline_settings::domBlend_equation_separateRef ref = new domGl_pipeline_settings::domBlend_equation_separate(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domBlend_equation_separate::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "blend_equation_separate" ); + meta->registerClass(domGl_pipeline_settings::domBlend_equation_separate::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "rgb" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domBlend_equation_separate,elemRgb) ); + mea->setElementType( domGl_pipeline_settings::domBlend_equation_separate::domRgb::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "alpha" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domBlend_equation_separate,elemAlpha) ); + mea->setElementType( domGl_pipeline_settings::domBlend_equation_separate::domAlpha::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domGl_pipeline_settings::domBlend_equation_separate)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domBlend_equation_separate::domRgb::create(DAE& dae) +{ + domGl_pipeline_settings::domBlend_equation_separate::domRgbRef ref = new domGl_pipeline_settings::domBlend_equation_separate::domRgb(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domBlend_equation_separate::domRgb::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "rgb" ); + meta->registerClass(domGl_pipeline_settings::domBlend_equation_separate::domRgb::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_blend_equation_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domBlend_equation_separate::domRgb , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "FUNC_ADD"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domBlend_equation_separate::domRgb , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domBlend_equation_separate::domRgb)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domBlend_equation_separate::domAlpha::create(DAE& dae) +{ + domGl_pipeline_settings::domBlend_equation_separate::domAlphaRef ref = new domGl_pipeline_settings::domBlend_equation_separate::domAlpha(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domBlend_equation_separate::domAlpha::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "alpha" ); + meta->registerClass(domGl_pipeline_settings::domBlend_equation_separate::domAlpha::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_blend_equation_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domBlend_equation_separate::domAlpha , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "FUNC_ADD"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domBlend_equation_separate::domAlpha , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domBlend_equation_separate::domAlpha)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domColor_material::create(DAE& dae) +{ + domGl_pipeline_settings::domColor_materialRef ref = new domGl_pipeline_settings::domColor_material(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domColor_material::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "color_material" ); + meta->registerClass(domGl_pipeline_settings::domColor_material::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "face" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domColor_material,elemFace) ); + mea->setElementType( domGl_pipeline_settings::domColor_material::domFace::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "mode" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domColor_material,elemMode) ); + mea->setElementType( domGl_pipeline_settings::domColor_material::domMode::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domGl_pipeline_settings::domColor_material)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domColor_material::domFace::create(DAE& dae) +{ + domGl_pipeline_settings::domColor_material::domFaceRef ref = new domGl_pipeline_settings::domColor_material::domFace(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domColor_material::domFace::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "face" ); + meta->registerClass(domGl_pipeline_settings::domColor_material::domFace::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_face_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domColor_material::domFace , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "FRONT_AND_BACK"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domColor_material::domFace , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domColor_material::domFace)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domColor_material::domMode::create(DAE& dae) +{ + domGl_pipeline_settings::domColor_material::domModeRef ref = new domGl_pipeline_settings::domColor_material::domMode(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domColor_material::domMode::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mode" ); + meta->registerClass(domGl_pipeline_settings::domColor_material::domMode::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_material_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domColor_material::domMode , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "AMBIENT_AND_DIFFUSE"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domColor_material::domMode , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domColor_material::domMode)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domCull_face::create(DAE& dae) +{ + domGl_pipeline_settings::domCull_faceRef ref = new domGl_pipeline_settings::domCull_face(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domCull_face::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "cull_face" ); + meta->registerClass(domGl_pipeline_settings::domCull_face::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_face_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domCull_face , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "BACK"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domCull_face , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domCull_face)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domDepth_func::create(DAE& dae) +{ + domGl_pipeline_settings::domDepth_funcRef ref = new domGl_pipeline_settings::domDepth_func(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domDepth_func::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "depth_func" ); + meta->registerClass(domGl_pipeline_settings::domDepth_func::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_func_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domDepth_func , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "ALWAYS"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domDepth_func , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domDepth_func)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domFog_mode::create(DAE& dae) +{ + domGl_pipeline_settings::domFog_modeRef ref = new domGl_pipeline_settings::domFog_mode(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domFog_mode::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fog_mode" ); + meta->registerClass(domGl_pipeline_settings::domFog_mode::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_fog_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domFog_mode , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "EXP"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domFog_mode , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domFog_mode)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domFog_coord_src::create(DAE& dae) +{ + domGl_pipeline_settings::domFog_coord_srcRef ref = new domGl_pipeline_settings::domFog_coord_src(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domFog_coord_src::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fog_coord_src" ); + meta->registerClass(domGl_pipeline_settings::domFog_coord_src::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_fog_coord_src_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domFog_coord_src , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "FOG_COORDINATE"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domFog_coord_src , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domFog_coord_src)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domFront_face::create(DAE& dae) +{ + domGl_pipeline_settings::domFront_faceRef ref = new domGl_pipeline_settings::domFront_face(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domFront_face::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "front_face" ); + meta->registerClass(domGl_pipeline_settings::domFront_face::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_front_face_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domFront_face , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "CCW"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domFront_face , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domFront_face)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domLight_model_color_control::create(DAE& dae) +{ + domGl_pipeline_settings::domLight_model_color_controlRef ref = new domGl_pipeline_settings::domLight_model_color_control(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domLight_model_color_control::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_model_color_control" ); + meta->registerClass(domGl_pipeline_settings::domLight_model_color_control::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_light_model_color_control_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_model_color_control , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "SINGLE_COLOR"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_model_color_control , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domLight_model_color_control)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domLogic_op::create(DAE& dae) +{ + domGl_pipeline_settings::domLogic_opRef ref = new domGl_pipeline_settings::domLogic_op(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domLogic_op::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "logic_op" ); + meta->registerClass(domGl_pipeline_settings::domLogic_op::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_logic_op_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLogic_op , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "COPY"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLogic_op , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domLogic_op)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domPolygon_mode::create(DAE& dae) +{ + domGl_pipeline_settings::domPolygon_modeRef ref = new domGl_pipeline_settings::domPolygon_mode(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domPolygon_mode::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "polygon_mode" ); + meta->registerClass(domGl_pipeline_settings::domPolygon_mode::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "face" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domPolygon_mode,elemFace) ); + mea->setElementType( domGl_pipeline_settings::domPolygon_mode::domFace::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "mode" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domPolygon_mode,elemMode) ); + mea->setElementType( domGl_pipeline_settings::domPolygon_mode::domMode::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domGl_pipeline_settings::domPolygon_mode)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domPolygon_mode::domFace::create(DAE& dae) +{ + domGl_pipeline_settings::domPolygon_mode::domFaceRef ref = new domGl_pipeline_settings::domPolygon_mode::domFace(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domPolygon_mode::domFace::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "face" ); + meta->registerClass(domGl_pipeline_settings::domPolygon_mode::domFace::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_face_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPolygon_mode::domFace , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "FRONT_AND_BACK"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPolygon_mode::domFace , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domPolygon_mode::domFace)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domPolygon_mode::domMode::create(DAE& dae) +{ + domGl_pipeline_settings::domPolygon_mode::domModeRef ref = new domGl_pipeline_settings::domPolygon_mode::domMode(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domPolygon_mode::domMode::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mode" ); + meta->registerClass(domGl_pipeline_settings::domPolygon_mode::domMode::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_polygon_mode_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPolygon_mode::domMode , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "FILL"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPolygon_mode::domMode , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domPolygon_mode::domMode)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domShade_model::create(DAE& dae) +{ + domGl_pipeline_settings::domShade_modelRef ref = new domGl_pipeline_settings::domShade_model(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domShade_model::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "shade_model" ); + meta->registerClass(domGl_pipeline_settings::domShade_model::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_shade_model_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domShade_model , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "SMOOTH"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domShade_model , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domShade_model)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domStencil_func::create(DAE& dae) +{ + domGl_pipeline_settings::domStencil_funcRef ref = new domGl_pipeline_settings::domStencil_func(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domStencil_func::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "stencil_func" ); + meta->registerClass(domGl_pipeline_settings::domStencil_func::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "func" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domStencil_func,elemFunc) ); + mea->setElementType( domGl_pipeline_settings::domStencil_func::domFunc::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "ref" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domStencil_func,elemRef) ); + mea->setElementType( domGl_pipeline_settings::domStencil_func::domRef::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 1, 1 ); + mea->setName( "mask" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domStencil_func,elemMask) ); + mea->setElementType( domGl_pipeline_settings::domStencil_func::domMask::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domGl_pipeline_settings::domStencil_func)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domStencil_func::domFunc::create(DAE& dae) +{ + domGl_pipeline_settings::domStencil_func::domFuncRef ref = new domGl_pipeline_settings::domStencil_func::domFunc(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domStencil_func::domFunc::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "func" ); + meta->registerClass(domGl_pipeline_settings::domStencil_func::domFunc::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_func_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_func::domFunc , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "ALWAYS"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_func::domFunc , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domStencil_func::domFunc)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domStencil_func::domRef::create(DAE& dae) +{ + domGl_pipeline_settings::domStencil_func::domRefRef ref = new domGl_pipeline_settings::domStencil_func::domRef(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domStencil_func::domRef::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "ref" ); + meta->registerClass(domGl_pipeline_settings::domStencil_func::domRef::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("xsUnsignedByte")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_func::domRef , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_func::domRef , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domStencil_func::domRef)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domStencil_func::domMask::create(DAE& dae) +{ + domGl_pipeline_settings::domStencil_func::domMaskRef ref = new domGl_pipeline_settings::domStencil_func::domMask(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domStencil_func::domMask::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mask" ); + meta->registerClass(domGl_pipeline_settings::domStencil_func::domMask::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("xsUnsignedByte")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_func::domMask , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "255"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_func::domMask , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domStencil_func::domMask)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domStencil_op::create(DAE& dae) +{ + domGl_pipeline_settings::domStencil_opRef ref = new domGl_pipeline_settings::domStencil_op(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domStencil_op::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "stencil_op" ); + meta->registerClass(domGl_pipeline_settings::domStencil_op::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fail" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domStencil_op,elemFail) ); + mea->setElementType( domGl_pipeline_settings::domStencil_op::domFail::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "zfail" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domStencil_op,elemZfail) ); + mea->setElementType( domGl_pipeline_settings::domStencil_op::domZfail::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 1, 1 ); + mea->setName( "zpass" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domStencil_op,elemZpass) ); + mea->setElementType( domGl_pipeline_settings::domStencil_op::domZpass::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domGl_pipeline_settings::domStencil_op)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domStencil_op::domFail::create(DAE& dae) +{ + domGl_pipeline_settings::domStencil_op::domFailRef ref = new domGl_pipeline_settings::domStencil_op::domFail(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domStencil_op::domFail::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fail" ); + meta->registerClass(domGl_pipeline_settings::domStencil_op::domFail::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_stencil_op_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_op::domFail , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "KEEP"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_op::domFail , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domStencil_op::domFail)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domStencil_op::domZfail::create(DAE& dae) +{ + domGl_pipeline_settings::domStencil_op::domZfailRef ref = new domGl_pipeline_settings::domStencil_op::domZfail(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domStencil_op::domZfail::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "zfail" ); + meta->registerClass(domGl_pipeline_settings::domStencil_op::domZfail::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_stencil_op_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_op::domZfail , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "KEEP"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_op::domZfail , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domStencil_op::domZfail)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domStencil_op::domZpass::create(DAE& dae) +{ + domGl_pipeline_settings::domStencil_op::domZpassRef ref = new domGl_pipeline_settings::domStencil_op::domZpass(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domStencil_op::domZpass::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "zpass" ); + meta->registerClass(domGl_pipeline_settings::domStencil_op::domZpass::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_stencil_op_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_op::domZpass , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "KEEP"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_op::domZpass , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domStencil_op::domZpass)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domStencil_func_separate::create(DAE& dae) +{ + domGl_pipeline_settings::domStencil_func_separateRef ref = new domGl_pipeline_settings::domStencil_func_separate(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domStencil_func_separate::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "stencil_func_separate" ); + meta->registerClass(domGl_pipeline_settings::domStencil_func_separate::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "front" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domStencil_func_separate,elemFront) ); + mea->setElementType( domGl_pipeline_settings::domStencil_func_separate::domFront::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "back" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domStencil_func_separate,elemBack) ); + mea->setElementType( domGl_pipeline_settings::domStencil_func_separate::domBack::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 1, 1 ); + mea->setName( "ref" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domStencil_func_separate,elemRef) ); + mea->setElementType( domGl_pipeline_settings::domStencil_func_separate::domRef::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 1, 1 ); + mea->setName( "mask" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domStencil_func_separate,elemMask) ); + mea->setElementType( domGl_pipeline_settings::domStencil_func_separate::domMask::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domGl_pipeline_settings::domStencil_func_separate)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domStencil_func_separate::domFront::create(DAE& dae) +{ + domGl_pipeline_settings::domStencil_func_separate::domFrontRef ref = new domGl_pipeline_settings::domStencil_func_separate::domFront(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domStencil_func_separate::domFront::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "front" ); + meta->registerClass(domGl_pipeline_settings::domStencil_func_separate::domFront::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_func_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_func_separate::domFront , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "ALWAYS"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_func_separate::domFront , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domStencil_func_separate::domFront)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domStencil_func_separate::domBack::create(DAE& dae) +{ + domGl_pipeline_settings::domStencil_func_separate::domBackRef ref = new domGl_pipeline_settings::domStencil_func_separate::domBack(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domStencil_func_separate::domBack::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "back" ); + meta->registerClass(domGl_pipeline_settings::domStencil_func_separate::domBack::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_func_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_func_separate::domBack , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "ALWAYS"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_func_separate::domBack , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domStencil_func_separate::domBack)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domStencil_func_separate::domRef::create(DAE& dae) +{ + domGl_pipeline_settings::domStencil_func_separate::domRefRef ref = new domGl_pipeline_settings::domStencil_func_separate::domRef(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domStencil_func_separate::domRef::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "ref" ); + meta->registerClass(domGl_pipeline_settings::domStencil_func_separate::domRef::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("xsUnsignedByte")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_func_separate::domRef , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_func_separate::domRef , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domStencil_func_separate::domRef)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domStencil_func_separate::domMask::create(DAE& dae) +{ + domGl_pipeline_settings::domStencil_func_separate::domMaskRef ref = new domGl_pipeline_settings::domStencil_func_separate::domMask(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domStencil_func_separate::domMask::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mask" ); + meta->registerClass(domGl_pipeline_settings::domStencil_func_separate::domMask::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("xsUnsignedByte")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_func_separate::domMask , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "255"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_func_separate::domMask , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domStencil_func_separate::domMask)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domStencil_op_separate::create(DAE& dae) +{ + domGl_pipeline_settings::domStencil_op_separateRef ref = new domGl_pipeline_settings::domStencil_op_separate(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domStencil_op_separate::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "stencil_op_separate" ); + meta->registerClass(domGl_pipeline_settings::domStencil_op_separate::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "face" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domStencil_op_separate,elemFace) ); + mea->setElementType( domGl_pipeline_settings::domStencil_op_separate::domFace::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "fail" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domStencil_op_separate,elemFail) ); + mea->setElementType( domGl_pipeline_settings::domStencil_op_separate::domFail::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 1, 1 ); + mea->setName( "zfail" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domStencil_op_separate,elemZfail) ); + mea->setElementType( domGl_pipeline_settings::domStencil_op_separate::domZfail::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 1, 1 ); + mea->setName( "zpass" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domStencil_op_separate,elemZpass) ); + mea->setElementType( domGl_pipeline_settings::domStencil_op_separate::domZpass::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domGl_pipeline_settings::domStencil_op_separate)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domStencil_op_separate::domFace::create(DAE& dae) +{ + domGl_pipeline_settings::domStencil_op_separate::domFaceRef ref = new domGl_pipeline_settings::domStencil_op_separate::domFace(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domStencil_op_separate::domFace::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "face" ); + meta->registerClass(domGl_pipeline_settings::domStencil_op_separate::domFace::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_face_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_op_separate::domFace , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "FRONT_AND_BACK"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_op_separate::domFace , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domStencil_op_separate::domFace)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domStencil_op_separate::domFail::create(DAE& dae) +{ + domGl_pipeline_settings::domStencil_op_separate::domFailRef ref = new domGl_pipeline_settings::domStencil_op_separate::domFail(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domStencil_op_separate::domFail::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fail" ); + meta->registerClass(domGl_pipeline_settings::domStencil_op_separate::domFail::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_stencil_op_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_op_separate::domFail , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "KEEP"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_op_separate::domFail , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domStencil_op_separate::domFail)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domStencil_op_separate::domZfail::create(DAE& dae) +{ + domGl_pipeline_settings::domStencil_op_separate::domZfailRef ref = new domGl_pipeline_settings::domStencil_op_separate::domZfail(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domStencil_op_separate::domZfail::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "zfail" ); + meta->registerClass(domGl_pipeline_settings::domStencil_op_separate::domZfail::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_stencil_op_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_op_separate::domZfail , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "KEEP"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_op_separate::domZfail , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domStencil_op_separate::domZfail)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domStencil_op_separate::domZpass::create(DAE& dae) +{ + domGl_pipeline_settings::domStencil_op_separate::domZpassRef ref = new domGl_pipeline_settings::domStencil_op_separate::domZpass(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domStencil_op_separate::domZpass::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "zpass" ); + meta->registerClass(domGl_pipeline_settings::domStencil_op_separate::domZpass::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_stencil_op_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_op_separate::domZpass , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "KEEP"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_op_separate::domZpass , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domStencil_op_separate::domZpass)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domStencil_mask_separate::create(DAE& dae) +{ + domGl_pipeline_settings::domStencil_mask_separateRef ref = new domGl_pipeline_settings::domStencil_mask_separate(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domStencil_mask_separate::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "stencil_mask_separate" ); + meta->registerClass(domGl_pipeline_settings::domStencil_mask_separate::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "face" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domStencil_mask_separate,elemFace) ); + mea->setElementType( domGl_pipeline_settings::domStencil_mask_separate::domFace::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "mask" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domStencil_mask_separate,elemMask) ); + mea->setElementType( domGl_pipeline_settings::domStencil_mask_separate::domMask::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domGl_pipeline_settings::domStencil_mask_separate)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domStencil_mask_separate::domFace::create(DAE& dae) +{ + domGl_pipeline_settings::domStencil_mask_separate::domFaceRef ref = new domGl_pipeline_settings::domStencil_mask_separate::domFace(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domStencil_mask_separate::domFace::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "face" ); + meta->registerClass(domGl_pipeline_settings::domStencil_mask_separate::domFace::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_face_type")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_mask_separate::domFace , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "FRONT_AND_BACK"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_mask_separate::domFace , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domStencil_mask_separate::domFace)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domStencil_mask_separate::domMask::create(DAE& dae) +{ + domGl_pipeline_settings::domStencil_mask_separate::domMaskRef ref = new domGl_pipeline_settings::domStencil_mask_separate::domMask(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domStencil_mask_separate::domMask::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mask" ); + meta->registerClass(domGl_pipeline_settings::domStencil_mask_separate::domMask::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("xsUnsignedByte")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_mask_separate::domMask , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "255"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_mask_separate::domMask , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domStencil_mask_separate::domMask)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domLight_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domLight_enableRef ref = new domGl_pipeline_settings::domLight_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domLight_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_enable" ); + meta->registerClass(domGl_pipeline_settings::domLight_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GL_MAX_LIGHTS_index")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_enable , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domLight_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domLight_ambient::create(DAE& dae) +{ + domGl_pipeline_settings::domLight_ambientRef ref = new domGl_pipeline_settings::domLight_ambient(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domLight_ambient::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_ambient" ); + meta->registerClass(domGl_pipeline_settings::domLight_ambient::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_ambient , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0 0 0 1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_ambient , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GL_MAX_LIGHTS_index")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_ambient , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domLight_ambient)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domLight_diffuse::create(DAE& dae) +{ + domGl_pipeline_settings::domLight_diffuseRef ref = new domGl_pipeline_settings::domLight_diffuse(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domLight_diffuse::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_diffuse" ); + meta->registerClass(domGl_pipeline_settings::domLight_diffuse::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_diffuse , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0 0 0 0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_diffuse , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GL_MAX_LIGHTS_index")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_diffuse , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domLight_diffuse)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domLight_specular::create(DAE& dae) +{ + domGl_pipeline_settings::domLight_specularRef ref = new domGl_pipeline_settings::domLight_specular(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domLight_specular::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_specular" ); + meta->registerClass(domGl_pipeline_settings::domLight_specular::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_specular , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0 0 0 0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_specular , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GL_MAX_LIGHTS_index")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_specular , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domLight_specular)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domLight_position::create(DAE& dae) +{ + domGl_pipeline_settings::domLight_positionRef ref = new domGl_pipeline_settings::domLight_position(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domLight_position::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_position" ); + meta->registerClass(domGl_pipeline_settings::domLight_position::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_position , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0 0 1 0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_position , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GL_MAX_LIGHTS_index")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_position , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domLight_position)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domLight_constant_attenuation::create(DAE& dae) +{ + domGl_pipeline_settings::domLight_constant_attenuationRef ref = new domGl_pipeline_settings::domLight_constant_attenuation(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domLight_constant_attenuation::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_constant_attenuation" ); + meta->registerClass(domGl_pipeline_settings::domLight_constant_attenuation::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_constant_attenuation , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_constant_attenuation , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GL_MAX_LIGHTS_index")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_constant_attenuation , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domLight_constant_attenuation)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domLight_linear_attenuation::create(DAE& dae) +{ + domGl_pipeline_settings::domLight_linear_attenuationRef ref = new domGl_pipeline_settings::domLight_linear_attenuation(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domLight_linear_attenuation::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_linear_attenuation" ); + meta->registerClass(domGl_pipeline_settings::domLight_linear_attenuation::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_linear_attenuation , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_linear_attenuation , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GL_MAX_LIGHTS_index")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_linear_attenuation , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domLight_linear_attenuation)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domLight_quadratic_attenuation::create(DAE& dae) +{ + domGl_pipeline_settings::domLight_quadratic_attenuationRef ref = new domGl_pipeline_settings::domLight_quadratic_attenuation(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domLight_quadratic_attenuation::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_quadratic_attenuation" ); + meta->registerClass(domGl_pipeline_settings::domLight_quadratic_attenuation::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_quadratic_attenuation , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_quadratic_attenuation , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GL_MAX_LIGHTS_index")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_quadratic_attenuation , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domLight_quadratic_attenuation)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domLight_spot_cutoff::create(DAE& dae) +{ + domGl_pipeline_settings::domLight_spot_cutoffRef ref = new domGl_pipeline_settings::domLight_spot_cutoff(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domLight_spot_cutoff::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_spot_cutoff" ); + meta->registerClass(domGl_pipeline_settings::domLight_spot_cutoff::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_spot_cutoff , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "180"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_spot_cutoff , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GL_MAX_LIGHTS_index")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_spot_cutoff , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domLight_spot_cutoff)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domLight_spot_direction::create(DAE& dae) +{ + domGl_pipeline_settings::domLight_spot_directionRef ref = new domGl_pipeline_settings::domLight_spot_direction(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domLight_spot_direction::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_spot_direction" ); + meta->registerClass(domGl_pipeline_settings::domLight_spot_direction::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float3")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_spot_direction , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0 0 -1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_spot_direction , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GL_MAX_LIGHTS_index")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_spot_direction , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domLight_spot_direction)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domLight_spot_exponent::create(DAE& dae) +{ + domGl_pipeline_settings::domLight_spot_exponentRef ref = new domGl_pipeline_settings::domLight_spot_exponent(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domLight_spot_exponent::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_spot_exponent" ); + meta->registerClass(domGl_pipeline_settings::domLight_spot_exponent::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_spot_exponent , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_spot_exponent , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GL_MAX_LIGHTS_index")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_spot_exponent , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domLight_spot_exponent)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domTexture1D::create(DAE& dae) +{ + domGl_pipeline_settings::domTexture1DRef ref = new domGl_pipeline_settings::domTexture1D(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domTexture1D::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "texture1D" ); + meta->registerClass(domGl_pipeline_settings::domTexture1D::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "value" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domTexture1D,elemValue) ); + mea->setElementType( domGl_sampler1D::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "param" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domTexture1D,elemParam) ); + mea->setElementType( domGl_pipeline_settings::domTexture1D::domParam::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domGl_pipeline_settings::domTexture1D,_contents)); + meta->addContentsOrder(daeOffsetOf(domGl_pipeline_settings::domTexture1D,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domGl_pipeline_settings::domTexture1D,_CMData), 1); + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GL_MAX_TEXTURE_IMAGE_UNITS_index")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTexture1D , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domTexture1D)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domTexture1D::domParam::create(DAE& dae) +{ + domGl_pipeline_settings::domTexture1D::domParamRef ref = new domGl_pipeline_settings::domTexture1D::domParam(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domTexture1D::domParam::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "param" ); + meta->registerClass(domGl_pipeline_settings::domTexture1D::domParam::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTexture1D::domParam , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domTexture1D::domParam)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domTexture2D::create(DAE& dae) +{ + domGl_pipeline_settings::domTexture2DRef ref = new domGl_pipeline_settings::domTexture2D(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domTexture2D::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "texture2D" ); + meta->registerClass(domGl_pipeline_settings::domTexture2D::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "value" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domTexture2D,elemValue) ); + mea->setElementType( domGl_sampler2D::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "param" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domTexture2D,elemParam) ); + mea->setElementType( domGl_pipeline_settings::domTexture2D::domParam::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domGl_pipeline_settings::domTexture2D,_contents)); + meta->addContentsOrder(daeOffsetOf(domGl_pipeline_settings::domTexture2D,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domGl_pipeline_settings::domTexture2D,_CMData), 1); + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GL_MAX_TEXTURE_IMAGE_UNITS_index")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTexture2D , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domTexture2D)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domTexture2D::domParam::create(DAE& dae) +{ + domGl_pipeline_settings::domTexture2D::domParamRef ref = new domGl_pipeline_settings::domTexture2D::domParam(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domTexture2D::domParam::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "param" ); + meta->registerClass(domGl_pipeline_settings::domTexture2D::domParam::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTexture2D::domParam , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domTexture2D::domParam)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domTexture3D::create(DAE& dae) +{ + domGl_pipeline_settings::domTexture3DRef ref = new domGl_pipeline_settings::domTexture3D(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domTexture3D::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "texture3D" ); + meta->registerClass(domGl_pipeline_settings::domTexture3D::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "value" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domTexture3D,elemValue) ); + mea->setElementType( domGl_sampler3D::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "param" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domTexture3D,elemParam) ); + mea->setElementType( domGl_pipeline_settings::domTexture3D::domParam::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domGl_pipeline_settings::domTexture3D,_contents)); + meta->addContentsOrder(daeOffsetOf(domGl_pipeline_settings::domTexture3D,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domGl_pipeline_settings::domTexture3D,_CMData), 1); + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GL_MAX_TEXTURE_IMAGE_UNITS_index")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTexture3D , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domTexture3D)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domTexture3D::domParam::create(DAE& dae) +{ + domGl_pipeline_settings::domTexture3D::domParamRef ref = new domGl_pipeline_settings::domTexture3D::domParam(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domTexture3D::domParam::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "param" ); + meta->registerClass(domGl_pipeline_settings::domTexture3D::domParam::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTexture3D::domParam , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domTexture3D::domParam)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domTextureCUBE::create(DAE& dae) +{ + domGl_pipeline_settings::domTextureCUBERef ref = new domGl_pipeline_settings::domTextureCUBE(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domTextureCUBE::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "textureCUBE" ); + meta->registerClass(domGl_pipeline_settings::domTextureCUBE::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "value" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domTextureCUBE,elemValue) ); + mea->setElementType( domGl_samplerCUBE::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "param" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domTextureCUBE,elemParam) ); + mea->setElementType( domGl_pipeline_settings::domTextureCUBE::domParam::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domGl_pipeline_settings::domTextureCUBE,_contents)); + meta->addContentsOrder(daeOffsetOf(domGl_pipeline_settings::domTextureCUBE,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domGl_pipeline_settings::domTextureCUBE,_CMData), 1); + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GL_MAX_TEXTURE_IMAGE_UNITS_index")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTextureCUBE , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domTextureCUBE)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domTextureCUBE::domParam::create(DAE& dae) +{ + domGl_pipeline_settings::domTextureCUBE::domParamRef ref = new domGl_pipeline_settings::domTextureCUBE::domParam(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domTextureCUBE::domParam::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "param" ); + meta->registerClass(domGl_pipeline_settings::domTextureCUBE::domParam::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTextureCUBE::domParam , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domTextureCUBE::domParam)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domTextureRECT::create(DAE& dae) +{ + domGl_pipeline_settings::domTextureRECTRef ref = new domGl_pipeline_settings::domTextureRECT(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domTextureRECT::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "textureRECT" ); + meta->registerClass(domGl_pipeline_settings::domTextureRECT::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "value" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domTextureRECT,elemValue) ); + mea->setElementType( domGl_samplerRECT::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "param" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domTextureRECT,elemParam) ); + mea->setElementType( domGl_pipeline_settings::domTextureRECT::domParam::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domGl_pipeline_settings::domTextureRECT,_contents)); + meta->addContentsOrder(daeOffsetOf(domGl_pipeline_settings::domTextureRECT,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domGl_pipeline_settings::domTextureRECT,_CMData), 1); + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GL_MAX_TEXTURE_IMAGE_UNITS_index")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTextureRECT , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domTextureRECT)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domTextureRECT::domParam::create(DAE& dae) +{ + domGl_pipeline_settings::domTextureRECT::domParamRef ref = new domGl_pipeline_settings::domTextureRECT::domParam(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domTextureRECT::domParam::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "param" ); + meta->registerClass(domGl_pipeline_settings::domTextureRECT::domParam::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTextureRECT::domParam , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domTextureRECT::domParam)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domTextureDEPTH::create(DAE& dae) +{ + domGl_pipeline_settings::domTextureDEPTHRef ref = new domGl_pipeline_settings::domTextureDEPTH(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domTextureDEPTH::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "textureDEPTH" ); + meta->registerClass(domGl_pipeline_settings::domTextureDEPTH::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "value" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domTextureDEPTH,elemValue) ); + mea->setElementType( domGl_samplerDEPTH::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "param" ); + mea->setOffset( daeOffsetOf(domGl_pipeline_settings::domTextureDEPTH,elemParam) ); + mea->setElementType( domGl_pipeline_settings::domTextureDEPTH::domParam::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domGl_pipeline_settings::domTextureDEPTH,_contents)); + meta->addContentsOrder(daeOffsetOf(domGl_pipeline_settings::domTextureDEPTH,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domGl_pipeline_settings::domTextureDEPTH,_CMData), 1); + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GL_MAX_TEXTURE_IMAGE_UNITS_index")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTextureDEPTH , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domTextureDEPTH)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domTextureDEPTH::domParam::create(DAE& dae) +{ + domGl_pipeline_settings::domTextureDEPTH::domParamRef ref = new domGl_pipeline_settings::domTextureDEPTH::domParam(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domTextureDEPTH::domParam::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "param" ); + meta->registerClass(domGl_pipeline_settings::domTextureDEPTH::domParam::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTextureDEPTH::domParam , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domTextureDEPTH::domParam)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domTexture1D_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domTexture1D_enableRef ref = new domGl_pipeline_settings::domTexture1D_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domTexture1D_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "texture1D_enable" ); + meta->registerClass(domGl_pipeline_settings::domTexture1D_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTexture1D_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTexture1D_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GL_MAX_TEXTURE_IMAGE_UNITS_index")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTexture1D_enable , attrIndex )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domTexture1D_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domTexture2D_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domTexture2D_enableRef ref = new domGl_pipeline_settings::domTexture2D_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domTexture2D_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "texture2D_enable" ); + meta->registerClass(domGl_pipeline_settings::domTexture2D_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTexture2D_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTexture2D_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GL_MAX_TEXTURE_IMAGE_UNITS_index")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTexture2D_enable , attrIndex )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domTexture2D_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domTexture3D_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domTexture3D_enableRef ref = new domGl_pipeline_settings::domTexture3D_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domTexture3D_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "texture3D_enable" ); + meta->registerClass(domGl_pipeline_settings::domTexture3D_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTexture3D_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTexture3D_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GL_MAX_TEXTURE_IMAGE_UNITS_index")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTexture3D_enable , attrIndex )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domTexture3D_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domTextureCUBE_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domTextureCUBE_enableRef ref = new domGl_pipeline_settings::domTextureCUBE_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domTextureCUBE_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "textureCUBE_enable" ); + meta->registerClass(domGl_pipeline_settings::domTextureCUBE_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTextureCUBE_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTextureCUBE_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GL_MAX_TEXTURE_IMAGE_UNITS_index")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTextureCUBE_enable , attrIndex )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domTextureCUBE_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domTextureRECT_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domTextureRECT_enableRef ref = new domGl_pipeline_settings::domTextureRECT_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domTextureRECT_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "textureRECT_enable" ); + meta->registerClass(domGl_pipeline_settings::domTextureRECT_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTextureRECT_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTextureRECT_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GL_MAX_TEXTURE_IMAGE_UNITS_index")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTextureRECT_enable , attrIndex )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domTextureRECT_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domTextureDEPTH_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domTextureDEPTH_enableRef ref = new domGl_pipeline_settings::domTextureDEPTH_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domTextureDEPTH_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "textureDEPTH_enable" ); + meta->registerClass(domGl_pipeline_settings::domTextureDEPTH_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTextureDEPTH_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTextureDEPTH_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GL_MAX_TEXTURE_IMAGE_UNITS_index")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTextureDEPTH_enable , attrIndex )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domTextureDEPTH_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domTexture_env_color::create(DAE& dae) +{ + domGl_pipeline_settings::domTexture_env_colorRef ref = new domGl_pipeline_settings::domTexture_env_color(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domTexture_env_color::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "texture_env_color" ); + meta->registerClass(domGl_pipeline_settings::domTexture_env_color::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTexture_env_color , attrValue )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTexture_env_color , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GL_MAX_TEXTURE_IMAGE_UNITS_index")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTexture_env_color , attrIndex )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domTexture_env_color)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domTexture_env_mode::create(DAE& dae) +{ + domGl_pipeline_settings::domTexture_env_modeRef ref = new domGl_pipeline_settings::domTexture_env_mode(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domTexture_env_mode::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "texture_env_mode" ); + meta->registerClass(domGl_pipeline_settings::domTexture_env_mode::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("String")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTexture_env_mode , attrValue )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTexture_env_mode , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GL_MAX_TEXTURE_IMAGE_UNITS_index")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domTexture_env_mode , attrIndex )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domTexture_env_mode)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domClip_plane::create(DAE& dae) +{ + domGl_pipeline_settings::domClip_planeRef ref = new domGl_pipeline_settings::domClip_plane(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domClip_plane::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "clip_plane" ); + meta->registerClass(domGl_pipeline_settings::domClip_plane::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domClip_plane , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0 0 0 0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domClip_plane , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GL_MAX_CLIP_PLANES_index")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domClip_plane , attrIndex )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domClip_plane)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domClip_plane_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domClip_plane_enableRef ref = new domGl_pipeline_settings::domClip_plane_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domClip_plane_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "clip_plane_enable" ); + meta->registerClass(domGl_pipeline_settings::domClip_plane_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domClip_plane_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domClip_plane_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GL_MAX_CLIP_PLANES_index")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domClip_plane_enable , attrIndex )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domClip_plane_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domBlend_color::create(DAE& dae) +{ + domGl_pipeline_settings::domBlend_colorRef ref = new domGl_pipeline_settings::domBlend_color(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domBlend_color::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "blend_color" ); + meta->registerClass(domGl_pipeline_settings::domBlend_color::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domBlend_color , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0 0 0 0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domBlend_color , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domBlend_color)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domClear_color::create(DAE& dae) +{ + domGl_pipeline_settings::domClear_colorRef ref = new domGl_pipeline_settings::domClear_color(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domClear_color::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "clear_color" ); + meta->registerClass(domGl_pipeline_settings::domClear_color::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domClear_color , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0 0 0 0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domClear_color , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domClear_color)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domClear_stencil::create(DAE& dae) +{ + domGl_pipeline_settings::domClear_stencilRef ref = new domGl_pipeline_settings::domClear_stencil(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domClear_stencil::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "clear_stencil" ); + meta->registerClass(domGl_pipeline_settings::domClear_stencil::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Int")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domClear_stencil , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domClear_stencil , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domClear_stencil)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domClear_depth::create(DAE& dae) +{ + domGl_pipeline_settings::domClear_depthRef ref = new domGl_pipeline_settings::domClear_depth(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domClear_depth::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "clear_depth" ); + meta->registerClass(domGl_pipeline_settings::domClear_depth::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domClear_depth , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domClear_depth , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domClear_depth)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domColor_mask::create(DAE& dae) +{ + domGl_pipeline_settings::domColor_maskRef ref = new domGl_pipeline_settings::domColor_mask(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domColor_mask::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "color_mask" ); + meta->registerClass(domGl_pipeline_settings::domColor_mask::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool4")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domColor_mask , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "true true true true"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domColor_mask , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domColor_mask)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domDepth_bounds::create(DAE& dae) +{ + domGl_pipeline_settings::domDepth_boundsRef ref = new domGl_pipeline_settings::domDepth_bounds(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domDepth_bounds::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "depth_bounds" ); + meta->registerClass(domGl_pipeline_settings::domDepth_bounds::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float2")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domDepth_bounds , attrValue )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domDepth_bounds , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domDepth_bounds)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domDepth_mask::create(DAE& dae) +{ + domGl_pipeline_settings::domDepth_maskRef ref = new domGl_pipeline_settings::domDepth_mask(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domDepth_mask::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "depth_mask" ); + meta->registerClass(domGl_pipeline_settings::domDepth_mask::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domDepth_mask , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "true"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domDepth_mask , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domDepth_mask)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domDepth_range::create(DAE& dae) +{ + domGl_pipeline_settings::domDepth_rangeRef ref = new domGl_pipeline_settings::domDepth_range(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domDepth_range::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "depth_range" ); + meta->registerClass(domGl_pipeline_settings::domDepth_range::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float2")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domDepth_range , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0 1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domDepth_range , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domDepth_range)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domFog_density::create(DAE& dae) +{ + domGl_pipeline_settings::domFog_densityRef ref = new domGl_pipeline_settings::domFog_density(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domFog_density::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fog_density" ); + meta->registerClass(domGl_pipeline_settings::domFog_density::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domFog_density , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domFog_density , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domFog_density)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domFog_start::create(DAE& dae) +{ + domGl_pipeline_settings::domFog_startRef ref = new domGl_pipeline_settings::domFog_start(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domFog_start::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fog_start" ); + meta->registerClass(domGl_pipeline_settings::domFog_start::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domFog_start , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domFog_start , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domFog_start)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domFog_end::create(DAE& dae) +{ + domGl_pipeline_settings::domFog_endRef ref = new domGl_pipeline_settings::domFog_end(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domFog_end::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fog_end" ); + meta->registerClass(domGl_pipeline_settings::domFog_end::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domFog_end , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domFog_end , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domFog_end)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domFog_color::create(DAE& dae) +{ + domGl_pipeline_settings::domFog_colorRef ref = new domGl_pipeline_settings::domFog_color(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domFog_color::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fog_color" ); + meta->registerClass(domGl_pipeline_settings::domFog_color::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domFog_color , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0 0 0 0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domFog_color , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domFog_color)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domLight_model_ambient::create(DAE& dae) +{ + domGl_pipeline_settings::domLight_model_ambientRef ref = new domGl_pipeline_settings::domLight_model_ambient(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domLight_model_ambient::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_model_ambient" ); + meta->registerClass(domGl_pipeline_settings::domLight_model_ambient::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_model_ambient , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0.2 0.2 0.2 1.0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_model_ambient , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domLight_model_ambient)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domLighting_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domLighting_enableRef ref = new domGl_pipeline_settings::domLighting_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domLighting_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "lighting_enable" ); + meta->registerClass(domGl_pipeline_settings::domLighting_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLighting_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLighting_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domLighting_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domLine_stipple::create(DAE& dae) +{ + domGl_pipeline_settings::domLine_stippleRef ref = new domGl_pipeline_settings::domLine_stipple(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domLine_stipple::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "line_stipple" ); + meta->registerClass(domGl_pipeline_settings::domLine_stipple::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Int2")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLine_stipple , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "1 65536"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLine_stipple , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domLine_stipple)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domLine_width::create(DAE& dae) +{ + domGl_pipeline_settings::domLine_widthRef ref = new domGl_pipeline_settings::domLine_width(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domLine_width::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "line_width" ); + meta->registerClass(domGl_pipeline_settings::domLine_width::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLine_width , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLine_width , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domLine_width)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domMaterial_ambient::create(DAE& dae) +{ + domGl_pipeline_settings::domMaterial_ambientRef ref = new domGl_pipeline_settings::domMaterial_ambient(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domMaterial_ambient::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "material_ambient" ); + meta->registerClass(domGl_pipeline_settings::domMaterial_ambient::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domMaterial_ambient , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0.2 0.2 0.2 1.0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domMaterial_ambient , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domMaterial_ambient)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domMaterial_diffuse::create(DAE& dae) +{ + domGl_pipeline_settings::domMaterial_diffuseRef ref = new domGl_pipeline_settings::domMaterial_diffuse(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domMaterial_diffuse::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "material_diffuse" ); + meta->registerClass(domGl_pipeline_settings::domMaterial_diffuse::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domMaterial_diffuse , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0.8 0.8 0.8 1.0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domMaterial_diffuse , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domMaterial_diffuse)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domMaterial_emission::create(DAE& dae) +{ + domGl_pipeline_settings::domMaterial_emissionRef ref = new domGl_pipeline_settings::domMaterial_emission(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domMaterial_emission::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "material_emission" ); + meta->registerClass(domGl_pipeline_settings::domMaterial_emission::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domMaterial_emission , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0 0 0 1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domMaterial_emission , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domMaterial_emission)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domMaterial_shininess::create(DAE& dae) +{ + domGl_pipeline_settings::domMaterial_shininessRef ref = new domGl_pipeline_settings::domMaterial_shininess(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domMaterial_shininess::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "material_shininess" ); + meta->registerClass(domGl_pipeline_settings::domMaterial_shininess::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domMaterial_shininess , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domMaterial_shininess , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domMaterial_shininess)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domMaterial_specular::create(DAE& dae) +{ + domGl_pipeline_settings::domMaterial_specularRef ref = new domGl_pipeline_settings::domMaterial_specular(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domMaterial_specular::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "material_specular" ); + meta->registerClass(domGl_pipeline_settings::domMaterial_specular::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domMaterial_specular , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0 0 0 1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domMaterial_specular , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domMaterial_specular)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domModel_view_matrix::create(DAE& dae) +{ + domGl_pipeline_settings::domModel_view_matrixRef ref = new domGl_pipeline_settings::domModel_view_matrix(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domModel_view_matrix::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "model_view_matrix" ); + meta->registerClass(domGl_pipeline_settings::domModel_view_matrix::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4x4")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domModel_view_matrix , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domModel_view_matrix , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domModel_view_matrix)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domPoint_distance_attenuation::create(DAE& dae) +{ + domGl_pipeline_settings::domPoint_distance_attenuationRef ref = new domGl_pipeline_settings::domPoint_distance_attenuation(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domPoint_distance_attenuation::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "point_distance_attenuation" ); + meta->registerClass(domGl_pipeline_settings::domPoint_distance_attenuation::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float3")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPoint_distance_attenuation , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "1 0 0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPoint_distance_attenuation , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domPoint_distance_attenuation)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domPoint_fade_threshold_size::create(DAE& dae) +{ + domGl_pipeline_settings::domPoint_fade_threshold_sizeRef ref = new domGl_pipeline_settings::domPoint_fade_threshold_size(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domPoint_fade_threshold_size::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "point_fade_threshold_size" ); + meta->registerClass(domGl_pipeline_settings::domPoint_fade_threshold_size::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPoint_fade_threshold_size , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPoint_fade_threshold_size , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domPoint_fade_threshold_size)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domPoint_size::create(DAE& dae) +{ + domGl_pipeline_settings::domPoint_sizeRef ref = new domGl_pipeline_settings::domPoint_size(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domPoint_size::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "point_size" ); + meta->registerClass(domGl_pipeline_settings::domPoint_size::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPoint_size , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPoint_size , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domPoint_size)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domPoint_size_min::create(DAE& dae) +{ + domGl_pipeline_settings::domPoint_size_minRef ref = new domGl_pipeline_settings::domPoint_size_min(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domPoint_size_min::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "point_size_min" ); + meta->registerClass(domGl_pipeline_settings::domPoint_size_min::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPoint_size_min , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPoint_size_min , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domPoint_size_min)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domPoint_size_max::create(DAE& dae) +{ + domGl_pipeline_settings::domPoint_size_maxRef ref = new domGl_pipeline_settings::domPoint_size_max(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domPoint_size_max::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "point_size_max" ); + meta->registerClass(domGl_pipeline_settings::domPoint_size_max::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPoint_size_max , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPoint_size_max , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domPoint_size_max)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domPolygon_offset::create(DAE& dae) +{ + domGl_pipeline_settings::domPolygon_offsetRef ref = new domGl_pipeline_settings::domPolygon_offset(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domPolygon_offset::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "polygon_offset" ); + meta->registerClass(domGl_pipeline_settings::domPolygon_offset::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float2")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPolygon_offset , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0 0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPolygon_offset , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domPolygon_offset)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domProjection_matrix::create(DAE& dae) +{ + domGl_pipeline_settings::domProjection_matrixRef ref = new domGl_pipeline_settings::domProjection_matrix(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domProjection_matrix::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "projection_matrix" ); + meta->registerClass(domGl_pipeline_settings::domProjection_matrix::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4x4")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domProjection_matrix , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domProjection_matrix , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domProjection_matrix)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domScissor::create(DAE& dae) +{ + domGl_pipeline_settings::domScissorRef ref = new domGl_pipeline_settings::domScissor(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domScissor::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "scissor" ); + meta->registerClass(domGl_pipeline_settings::domScissor::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Int4")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domScissor , attrValue )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domScissor , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domScissor)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domStencil_mask::create(DAE& dae) +{ + domGl_pipeline_settings::domStencil_maskRef ref = new domGl_pipeline_settings::domStencil_mask(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domStencil_mask::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "stencil_mask" ); + meta->registerClass(domGl_pipeline_settings::domStencil_mask::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Int")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_mask , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "4294967295"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_mask , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domStencil_mask)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domAlpha_test_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domAlpha_test_enableRef ref = new domGl_pipeline_settings::domAlpha_test_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domAlpha_test_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "alpha_test_enable" ); + meta->registerClass(domGl_pipeline_settings::domAlpha_test_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domAlpha_test_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domAlpha_test_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domAlpha_test_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domAuto_normal_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domAuto_normal_enableRef ref = new domGl_pipeline_settings::domAuto_normal_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domAuto_normal_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "auto_normal_enable" ); + meta->registerClass(domGl_pipeline_settings::domAuto_normal_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domAuto_normal_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domAuto_normal_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domAuto_normal_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domBlend_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domBlend_enableRef ref = new domGl_pipeline_settings::domBlend_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domBlend_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "blend_enable" ); + meta->registerClass(domGl_pipeline_settings::domBlend_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domBlend_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domBlend_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domBlend_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domColor_logic_op_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domColor_logic_op_enableRef ref = new domGl_pipeline_settings::domColor_logic_op_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domColor_logic_op_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "color_logic_op_enable" ); + meta->registerClass(domGl_pipeline_settings::domColor_logic_op_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domColor_logic_op_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domColor_logic_op_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domColor_logic_op_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domColor_material_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domColor_material_enableRef ref = new domGl_pipeline_settings::domColor_material_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domColor_material_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "color_material_enable" ); + meta->registerClass(domGl_pipeline_settings::domColor_material_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domColor_material_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "true"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domColor_material_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domColor_material_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domCull_face_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domCull_face_enableRef ref = new domGl_pipeline_settings::domCull_face_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domCull_face_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "cull_face_enable" ); + meta->registerClass(domGl_pipeline_settings::domCull_face_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domCull_face_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domCull_face_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domCull_face_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domDepth_bounds_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domDepth_bounds_enableRef ref = new domGl_pipeline_settings::domDepth_bounds_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domDepth_bounds_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "depth_bounds_enable" ); + meta->registerClass(domGl_pipeline_settings::domDepth_bounds_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domDepth_bounds_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domDepth_bounds_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domDepth_bounds_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domDepth_clamp_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domDepth_clamp_enableRef ref = new domGl_pipeline_settings::domDepth_clamp_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domDepth_clamp_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "depth_clamp_enable" ); + meta->registerClass(domGl_pipeline_settings::domDepth_clamp_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domDepth_clamp_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domDepth_clamp_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domDepth_clamp_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domDepth_test_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domDepth_test_enableRef ref = new domGl_pipeline_settings::domDepth_test_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domDepth_test_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "depth_test_enable" ); + meta->registerClass(domGl_pipeline_settings::domDepth_test_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domDepth_test_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domDepth_test_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domDepth_test_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domDither_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domDither_enableRef ref = new domGl_pipeline_settings::domDither_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domDither_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "dither_enable" ); + meta->registerClass(domGl_pipeline_settings::domDither_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domDither_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "true"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domDither_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domDither_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domFog_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domFog_enableRef ref = new domGl_pipeline_settings::domFog_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domFog_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fog_enable" ); + meta->registerClass(domGl_pipeline_settings::domFog_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domFog_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domFog_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domFog_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domLight_model_local_viewer_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domLight_model_local_viewer_enableRef ref = new domGl_pipeline_settings::domLight_model_local_viewer_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domLight_model_local_viewer_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_model_local_viewer_enable" ); + meta->registerClass(domGl_pipeline_settings::domLight_model_local_viewer_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_model_local_viewer_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_model_local_viewer_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domLight_model_local_viewer_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domLight_model_two_side_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domLight_model_two_side_enableRef ref = new domGl_pipeline_settings::domLight_model_two_side_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domLight_model_two_side_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_model_two_side_enable" ); + meta->registerClass(domGl_pipeline_settings::domLight_model_two_side_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_model_two_side_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLight_model_two_side_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domLight_model_two_side_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domLine_smooth_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domLine_smooth_enableRef ref = new domGl_pipeline_settings::domLine_smooth_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domLine_smooth_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "line_smooth_enable" ); + meta->registerClass(domGl_pipeline_settings::domLine_smooth_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLine_smooth_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLine_smooth_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domLine_smooth_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domLine_stipple_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domLine_stipple_enableRef ref = new domGl_pipeline_settings::domLine_stipple_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domLine_stipple_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "line_stipple_enable" ); + meta->registerClass(domGl_pipeline_settings::domLine_stipple_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLine_stipple_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLine_stipple_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domLine_stipple_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domLogic_op_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domLogic_op_enableRef ref = new domGl_pipeline_settings::domLogic_op_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domLogic_op_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "logic_op_enable" ); + meta->registerClass(domGl_pipeline_settings::domLogic_op_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLogic_op_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domLogic_op_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domLogic_op_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domMultisample_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domMultisample_enableRef ref = new domGl_pipeline_settings::domMultisample_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domMultisample_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "multisample_enable" ); + meta->registerClass(domGl_pipeline_settings::domMultisample_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domMultisample_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domMultisample_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domMultisample_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domNormalize_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domNormalize_enableRef ref = new domGl_pipeline_settings::domNormalize_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domNormalize_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "normalize_enable" ); + meta->registerClass(domGl_pipeline_settings::domNormalize_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domNormalize_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domNormalize_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domNormalize_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domPoint_smooth_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domPoint_smooth_enableRef ref = new domGl_pipeline_settings::domPoint_smooth_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domPoint_smooth_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "point_smooth_enable" ); + meta->registerClass(domGl_pipeline_settings::domPoint_smooth_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPoint_smooth_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPoint_smooth_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domPoint_smooth_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domPolygon_offset_fill_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domPolygon_offset_fill_enableRef ref = new domGl_pipeline_settings::domPolygon_offset_fill_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domPolygon_offset_fill_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "polygon_offset_fill_enable" ); + meta->registerClass(domGl_pipeline_settings::domPolygon_offset_fill_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPolygon_offset_fill_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPolygon_offset_fill_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domPolygon_offset_fill_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domPolygon_offset_line_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domPolygon_offset_line_enableRef ref = new domGl_pipeline_settings::domPolygon_offset_line_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domPolygon_offset_line_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "polygon_offset_line_enable" ); + meta->registerClass(domGl_pipeline_settings::domPolygon_offset_line_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPolygon_offset_line_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPolygon_offset_line_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domPolygon_offset_line_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domPolygon_offset_point_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domPolygon_offset_point_enableRef ref = new domGl_pipeline_settings::domPolygon_offset_point_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domPolygon_offset_point_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "polygon_offset_point_enable" ); + meta->registerClass(domGl_pipeline_settings::domPolygon_offset_point_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPolygon_offset_point_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPolygon_offset_point_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domPolygon_offset_point_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domPolygon_smooth_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domPolygon_smooth_enableRef ref = new domGl_pipeline_settings::domPolygon_smooth_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domPolygon_smooth_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "polygon_smooth_enable" ); + meta->registerClass(domGl_pipeline_settings::domPolygon_smooth_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPolygon_smooth_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPolygon_smooth_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domPolygon_smooth_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domPolygon_stipple_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domPolygon_stipple_enableRef ref = new domGl_pipeline_settings::domPolygon_stipple_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domPolygon_stipple_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "polygon_stipple_enable" ); + meta->registerClass(domGl_pipeline_settings::domPolygon_stipple_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPolygon_stipple_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domPolygon_stipple_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domPolygon_stipple_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domRescale_normal_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domRescale_normal_enableRef ref = new domGl_pipeline_settings::domRescale_normal_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domRescale_normal_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "rescale_normal_enable" ); + meta->registerClass(domGl_pipeline_settings::domRescale_normal_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domRescale_normal_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domRescale_normal_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domRescale_normal_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domSample_alpha_to_coverage_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domSample_alpha_to_coverage_enableRef ref = new domGl_pipeline_settings::domSample_alpha_to_coverage_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domSample_alpha_to_coverage_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "sample_alpha_to_coverage_enable" ); + meta->registerClass(domGl_pipeline_settings::domSample_alpha_to_coverage_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domSample_alpha_to_coverage_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domSample_alpha_to_coverage_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domSample_alpha_to_coverage_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domSample_alpha_to_one_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domSample_alpha_to_one_enableRef ref = new domGl_pipeline_settings::domSample_alpha_to_one_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domSample_alpha_to_one_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "sample_alpha_to_one_enable" ); + meta->registerClass(domGl_pipeline_settings::domSample_alpha_to_one_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domSample_alpha_to_one_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domSample_alpha_to_one_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domSample_alpha_to_one_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domSample_coverage_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domSample_coverage_enableRef ref = new domGl_pipeline_settings::domSample_coverage_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domSample_coverage_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "sample_coverage_enable" ); + meta->registerClass(domGl_pipeline_settings::domSample_coverage_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domSample_coverage_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domSample_coverage_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domSample_coverage_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domScissor_test_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domScissor_test_enableRef ref = new domGl_pipeline_settings::domScissor_test_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domScissor_test_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "scissor_test_enable" ); + meta->registerClass(domGl_pipeline_settings::domScissor_test_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domScissor_test_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domScissor_test_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domScissor_test_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGl_pipeline_settings::domStencil_test_enable::create(DAE& dae) +{ + domGl_pipeline_settings::domStencil_test_enableRef ref = new domGl_pipeline_settings::domStencil_test_enable(dae); + return ref; +} + + +daeMetaElement * +domGl_pipeline_settings::domStencil_test_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "stencil_test_enable" ); + meta->registerClass(domGl_pipeline_settings::domStencil_test_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_test_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGl_pipeline_settings::domStencil_test_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGl_pipeline_settings::domStencil_test_enable)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGl_sampler1D.cpp b/Engine/lib/collada/src/1.4/dom/domGl_sampler1D.cpp new file mode 100644 index 000000000..67c29e793 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGl_sampler1D.cpp @@ -0,0 +1,115 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGl_sampler1D::create(DAE& dae) +{ + domGl_sampler1DRef ref = new domGl_sampler1D(dae); + return ref; +} + + +daeMetaElement * +domGl_sampler1D::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "gl_sampler1D" ); + meta->registerClass(domGl_sampler1D::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "source" ); + mea->setOffset( daeOffsetOf(domGl_sampler1D,elemSource) ); + mea->setElementType( domSource::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "wrap_s" ); + mea->setOffset( daeOffsetOf(domGl_sampler1D,elemWrap_s) ); + mea->setElementType( domWrap_s::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "minfilter" ); + mea->setOffset( daeOffsetOf(domGl_sampler1D,elemMinfilter) ); + mea->setElementType( domMinfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "magfilter" ); + mea->setOffset( daeOffsetOf(domGl_sampler1D,elemMagfilter) ); + mea->setElementType( domMagfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "mipfilter" ); + mea->setOffset( daeOffsetOf(domGl_sampler1D,elemMipfilter) ); + mea->setElementType( domMipfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 5, 0, 1 ); + mea->setName( "border_color" ); + mea->setOffset( daeOffsetOf(domGl_sampler1D,elemBorder_color) ); + mea->setElementType( domBorder_color::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 6, 0, 1 ); + mea->setName( "mipmap_maxlevel" ); + mea->setOffset( daeOffsetOf(domGl_sampler1D,elemMipmap_maxlevel) ); + mea->setElementType( domMipmap_maxlevel::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 7, 0, 1 ); + mea->setName( "mipmap_bias" ); + mea->setOffset( daeOffsetOf(domGl_sampler1D,elemMipmap_bias) ); + mea->setElementType( domMipmap_bias::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 8, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domGl_sampler1D,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 8 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm->setMaxOrdinal( 8 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domGl_sampler1D)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGl_sampler2D.cpp b/Engine/lib/collada/src/1.4/dom/domGl_sampler2D.cpp new file mode 100644 index 000000000..cca77ec27 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGl_sampler2D.cpp @@ -0,0 +1,121 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGl_sampler2D::create(DAE& dae) +{ + domGl_sampler2DRef ref = new domGl_sampler2D(dae); + return ref; +} + + +daeMetaElement * +domGl_sampler2D::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "gl_sampler2D" ); + meta->registerClass(domGl_sampler2D::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "source" ); + mea->setOffset( daeOffsetOf(domGl_sampler2D,elemSource) ); + mea->setElementType( domSource::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "wrap_s" ); + mea->setOffset( daeOffsetOf(domGl_sampler2D,elemWrap_s) ); + mea->setElementType( domWrap_s::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "wrap_t" ); + mea->setOffset( daeOffsetOf(domGl_sampler2D,elemWrap_t) ); + mea->setElementType( domWrap_t::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "minfilter" ); + mea->setOffset( daeOffsetOf(domGl_sampler2D,elemMinfilter) ); + mea->setElementType( domMinfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "magfilter" ); + mea->setOffset( daeOffsetOf(domGl_sampler2D,elemMagfilter) ); + mea->setElementType( domMagfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 5, 0, 1 ); + mea->setName( "mipfilter" ); + mea->setOffset( daeOffsetOf(domGl_sampler2D,elemMipfilter) ); + mea->setElementType( domMipfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 6, 0, 1 ); + mea->setName( "border_color" ); + mea->setOffset( daeOffsetOf(domGl_sampler2D,elemBorder_color) ); + mea->setElementType( domBorder_color::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 7, 0, 1 ); + mea->setName( "mipmap_maxlevel" ); + mea->setOffset( daeOffsetOf(domGl_sampler2D,elemMipmap_maxlevel) ); + mea->setElementType( domMipmap_maxlevel::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 8, 0, 1 ); + mea->setName( "mipmap_bias" ); + mea->setOffset( daeOffsetOf(domGl_sampler2D,elemMipmap_bias) ); + mea->setElementType( domMipmap_bias::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 9, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domGl_sampler2D,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 9 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm->setMaxOrdinal( 9 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domGl_sampler2D)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGl_sampler3D.cpp b/Engine/lib/collada/src/1.4/dom/domGl_sampler3D.cpp new file mode 100644 index 000000000..9583c9243 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGl_sampler3D.cpp @@ -0,0 +1,127 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGl_sampler3D::create(DAE& dae) +{ + domGl_sampler3DRef ref = new domGl_sampler3D(dae); + return ref; +} + + +daeMetaElement * +domGl_sampler3D::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "gl_sampler3D" ); + meta->registerClass(domGl_sampler3D::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "source" ); + mea->setOffset( daeOffsetOf(domGl_sampler3D,elemSource) ); + mea->setElementType( domSource::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "wrap_s" ); + mea->setOffset( daeOffsetOf(domGl_sampler3D,elemWrap_s) ); + mea->setElementType( domWrap_s::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "wrap_t" ); + mea->setOffset( daeOffsetOf(domGl_sampler3D,elemWrap_t) ); + mea->setElementType( domWrap_t::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "wrap_p" ); + mea->setOffset( daeOffsetOf(domGl_sampler3D,elemWrap_p) ); + mea->setElementType( domWrap_p::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "minfilter" ); + mea->setOffset( daeOffsetOf(domGl_sampler3D,elemMinfilter) ); + mea->setElementType( domMinfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 5, 0, 1 ); + mea->setName( "magfilter" ); + mea->setOffset( daeOffsetOf(domGl_sampler3D,elemMagfilter) ); + mea->setElementType( domMagfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 6, 0, 1 ); + mea->setName( "mipfilter" ); + mea->setOffset( daeOffsetOf(domGl_sampler3D,elemMipfilter) ); + mea->setElementType( domMipfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 7, 0, 1 ); + mea->setName( "border_color" ); + mea->setOffset( daeOffsetOf(domGl_sampler3D,elemBorder_color) ); + mea->setElementType( domBorder_color::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 8, 0, 1 ); + mea->setName( "mipmap_maxlevel" ); + mea->setOffset( daeOffsetOf(domGl_sampler3D,elemMipmap_maxlevel) ); + mea->setElementType( domMipmap_maxlevel::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 9, 0, 1 ); + mea->setName( "mipmap_bias" ); + mea->setOffset( daeOffsetOf(domGl_sampler3D,elemMipmap_bias) ); + mea->setElementType( domMipmap_bias::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 10, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domGl_sampler3D,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 10 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm->setMaxOrdinal( 10 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domGl_sampler3D)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGl_samplerCUBE.cpp b/Engine/lib/collada/src/1.4/dom/domGl_samplerCUBE.cpp new file mode 100644 index 000000000..6769946ed --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGl_samplerCUBE.cpp @@ -0,0 +1,127 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGl_samplerCUBE::create(DAE& dae) +{ + domGl_samplerCUBERef ref = new domGl_samplerCUBE(dae); + return ref; +} + + +daeMetaElement * +domGl_samplerCUBE::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "gl_samplerCUBE" ); + meta->registerClass(domGl_samplerCUBE::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "source" ); + mea->setOffset( daeOffsetOf(domGl_samplerCUBE,elemSource) ); + mea->setElementType( domSource::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "wrap_s" ); + mea->setOffset( daeOffsetOf(domGl_samplerCUBE,elemWrap_s) ); + mea->setElementType( domWrap_s::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "wrap_t" ); + mea->setOffset( daeOffsetOf(domGl_samplerCUBE,elemWrap_t) ); + mea->setElementType( domWrap_t::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "wrap_p" ); + mea->setOffset( daeOffsetOf(domGl_samplerCUBE,elemWrap_p) ); + mea->setElementType( domWrap_p::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "minfilter" ); + mea->setOffset( daeOffsetOf(domGl_samplerCUBE,elemMinfilter) ); + mea->setElementType( domMinfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 5, 0, 1 ); + mea->setName( "magfilter" ); + mea->setOffset( daeOffsetOf(domGl_samplerCUBE,elemMagfilter) ); + mea->setElementType( domMagfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 6, 0, 1 ); + mea->setName( "mipfilter" ); + mea->setOffset( daeOffsetOf(domGl_samplerCUBE,elemMipfilter) ); + mea->setElementType( domMipfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 7, 0, 1 ); + mea->setName( "border_color" ); + mea->setOffset( daeOffsetOf(domGl_samplerCUBE,elemBorder_color) ); + mea->setElementType( domBorder_color::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 8, 0, 1 ); + mea->setName( "mipmap_maxlevel" ); + mea->setOffset( daeOffsetOf(domGl_samplerCUBE,elemMipmap_maxlevel) ); + mea->setElementType( domMipmap_maxlevel::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 9, 0, 1 ); + mea->setName( "mipmap_bias" ); + mea->setOffset( daeOffsetOf(domGl_samplerCUBE,elemMipmap_bias) ); + mea->setElementType( domMipmap_bias::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 10, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domGl_samplerCUBE,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 10 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm->setMaxOrdinal( 10 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domGl_samplerCUBE)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGl_samplerDEPTH.cpp b/Engine/lib/collada/src/1.4/dom/domGl_samplerDEPTH.cpp new file mode 100644 index 000000000..23a879b6d --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGl_samplerDEPTH.cpp @@ -0,0 +1,97 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGl_samplerDEPTH::create(DAE& dae) +{ + domGl_samplerDEPTHRef ref = new domGl_samplerDEPTH(dae); + return ref; +} + + +daeMetaElement * +domGl_samplerDEPTH::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "gl_samplerDEPTH" ); + meta->registerClass(domGl_samplerDEPTH::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "source" ); + mea->setOffset( daeOffsetOf(domGl_samplerDEPTH,elemSource) ); + mea->setElementType( domSource::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "wrap_s" ); + mea->setOffset( daeOffsetOf(domGl_samplerDEPTH,elemWrap_s) ); + mea->setElementType( domWrap_s::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "wrap_t" ); + mea->setOffset( daeOffsetOf(domGl_samplerDEPTH,elemWrap_t) ); + mea->setElementType( domWrap_t::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "minfilter" ); + mea->setOffset( daeOffsetOf(domGl_samplerDEPTH,elemMinfilter) ); + mea->setElementType( domMinfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "magfilter" ); + mea->setOffset( daeOffsetOf(domGl_samplerDEPTH,elemMagfilter) ); + mea->setElementType( domMagfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 5, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domGl_samplerDEPTH,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 5 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm->setMaxOrdinal( 5 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domGl_samplerDEPTH)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGl_samplerRECT.cpp b/Engine/lib/collada/src/1.4/dom/domGl_samplerRECT.cpp new file mode 100644 index 000000000..fec3afb0d --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGl_samplerRECT.cpp @@ -0,0 +1,121 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGl_samplerRECT::create(DAE& dae) +{ + domGl_samplerRECTRef ref = new domGl_samplerRECT(dae); + return ref; +} + + +daeMetaElement * +domGl_samplerRECT::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "gl_samplerRECT" ); + meta->registerClass(domGl_samplerRECT::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "source" ); + mea->setOffset( daeOffsetOf(domGl_samplerRECT,elemSource) ); + mea->setElementType( domSource::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "wrap_s" ); + mea->setOffset( daeOffsetOf(domGl_samplerRECT,elemWrap_s) ); + mea->setElementType( domWrap_s::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "wrap_t" ); + mea->setOffset( daeOffsetOf(domGl_samplerRECT,elemWrap_t) ); + mea->setElementType( domWrap_t::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "minfilter" ); + mea->setOffset( daeOffsetOf(domGl_samplerRECT,elemMinfilter) ); + mea->setElementType( domMinfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "magfilter" ); + mea->setOffset( daeOffsetOf(domGl_samplerRECT,elemMagfilter) ); + mea->setElementType( domMagfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 5, 0, 1 ); + mea->setName( "mipfilter" ); + mea->setOffset( daeOffsetOf(domGl_samplerRECT,elemMipfilter) ); + mea->setElementType( domMipfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 6, 0, 1 ); + mea->setName( "border_color" ); + mea->setOffset( daeOffsetOf(domGl_samplerRECT,elemBorder_color) ); + mea->setElementType( domBorder_color::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 7, 0, 1 ); + mea->setName( "mipmap_maxlevel" ); + mea->setOffset( daeOffsetOf(domGl_samplerRECT,elemMipmap_maxlevel) ); + mea->setElementType( domMipmap_maxlevel::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 8, 0, 1 ); + mea->setName( "mipmap_bias" ); + mea->setOffset( daeOffsetOf(domGl_samplerRECT,elemMipmap_bias) ); + mea->setElementType( domMipmap_bias::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 9, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domGl_samplerRECT,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 9 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm->setMaxOrdinal( 9 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domGl_samplerRECT)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGles_basic_type_common.cpp b/Engine/lib/collada/src/1.4/dom/domGles_basic_type_common.cpp new file mode 100644 index 000000000..a5c766470 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGles_basic_type_common.cpp @@ -0,0 +1,1302 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGles_basic_type_common::create(DAE& dae) +{ + domGles_basic_type_commonRef ref = new domGles_basic_type_common(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "gles_basic_type_common" ); + meta->registerClass(domGles_basic_type_common::create); + + meta->setIsTransparent( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemBool) ); + mea->setElementType( domGles_basic_type_common::domBool::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool2" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemBool2) ); + mea->setElementType( domGles_basic_type_common::domBool2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool3" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemBool3) ); + mea->setElementType( domGles_basic_type_common::domBool3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool4" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemBool4) ); + mea->setElementType( domGles_basic_type_common::domBool4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemInt) ); + mea->setElementType( domGles_basic_type_common::domInt::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int2" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemInt2) ); + mea->setElementType( domGles_basic_type_common::domInt2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int3" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemInt3) ); + mea->setElementType( domGles_basic_type_common::domInt3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int4" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemInt4) ); + mea->setElementType( domGles_basic_type_common::domInt4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemFloat) ); + mea->setElementType( domGles_basic_type_common::domFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float2" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemFloat2) ); + mea->setElementType( domGles_basic_type_common::domFloat2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float3" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemFloat3) ); + mea->setElementType( domGles_basic_type_common::domFloat3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float4" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemFloat4) ); + mea->setElementType( domGles_basic_type_common::domFloat4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float1x1" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemFloat1x1) ); + mea->setElementType( domGles_basic_type_common::domFloat1x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float1x2" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemFloat1x2) ); + mea->setElementType( domGles_basic_type_common::domFloat1x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float1x3" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemFloat1x3) ); + mea->setElementType( domGles_basic_type_common::domFloat1x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float1x4" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemFloat1x4) ); + mea->setElementType( domGles_basic_type_common::domFloat1x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float2x1" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemFloat2x1) ); + mea->setElementType( domGles_basic_type_common::domFloat2x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float2x2" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemFloat2x2) ); + mea->setElementType( domGles_basic_type_common::domFloat2x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float2x3" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemFloat2x3) ); + mea->setElementType( domGles_basic_type_common::domFloat2x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float2x4" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemFloat2x4) ); + mea->setElementType( domGles_basic_type_common::domFloat2x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float3x1" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemFloat3x1) ); + mea->setElementType( domGles_basic_type_common::domFloat3x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float3x2" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemFloat3x2) ); + mea->setElementType( domGles_basic_type_common::domFloat3x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float3x3" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemFloat3x3) ); + mea->setElementType( domGles_basic_type_common::domFloat3x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float3x4" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemFloat3x4) ); + mea->setElementType( domGles_basic_type_common::domFloat3x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float4x1" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemFloat4x1) ); + mea->setElementType( domGles_basic_type_common::domFloat4x1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float4x2" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemFloat4x2) ); + mea->setElementType( domGles_basic_type_common::domFloat4x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float4x3" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemFloat4x3) ); + mea->setElementType( domGles_basic_type_common::domFloat4x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float4x4" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemFloat4x4) ); + mea->setElementType( domGles_basic_type_common::domFloat4x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "surface" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemSurface) ); + mea->setElementType( domFx_surface_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "texture_pipeline" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemTexture_pipeline) ); + mea->setElementType( domGles_texture_pipeline::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "sampler_state" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemSampler_state) ); + mea->setElementType( domGles_sampler_state::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "texture_unit" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemTexture_unit) ); + mea->setElementType( domGles_texture_unit::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "enum" ); + mea->setOffset( daeOffsetOf(domGles_basic_type_common,elemEnum) ); + mea->setElementType( domGles_basic_type_common::domEnum::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domGles_basic_type_common,_contents)); + meta->addContentsOrder(daeOffsetOf(domGles_basic_type_common,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domGles_basic_type_common,_CMData), 1); + meta->setElementSize(sizeof(domGles_basic_type_common)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domBool::create(DAE& dae) +{ + domGles_basic_type_common::domBoolRef ref = new domGles_basic_type_common::domBool(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domBool::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool" ); + meta->registerClass(domGles_basic_type_common::domBool::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domBool , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domBool)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domBool2::create(DAE& dae) +{ + domGles_basic_type_common::domBool2Ref ref = new domGles_basic_type_common::domBool2(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domBool2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool2" ); + meta->registerClass(domGles_basic_type_common::domBool2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Bool2")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domBool2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domBool2)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domBool3::create(DAE& dae) +{ + domGles_basic_type_common::domBool3Ref ref = new domGles_basic_type_common::domBool3(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domBool3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool3" ); + meta->registerClass(domGles_basic_type_common::domBool3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Bool3")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domBool3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domBool3)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domBool4::create(DAE& dae) +{ + domGles_basic_type_common::domBool4Ref ref = new domGles_basic_type_common::domBool4(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domBool4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool4" ); + meta->registerClass(domGles_basic_type_common::domBool4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Bool4")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domBool4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domBool4)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domInt::create(DAE& dae) +{ + domGles_basic_type_common::domIntRef ref = new domGles_basic_type_common::domInt(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domInt::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int" ); + meta->registerClass(domGles_basic_type_common::domInt::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Int")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domInt , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domInt)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domInt2::create(DAE& dae) +{ + domGles_basic_type_common::domInt2Ref ref = new domGles_basic_type_common::domInt2(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domInt2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int2" ); + meta->registerClass(domGles_basic_type_common::domInt2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Int2")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domInt2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domInt2)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domInt3::create(DAE& dae) +{ + domGles_basic_type_common::domInt3Ref ref = new domGles_basic_type_common::domInt3(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domInt3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int3" ); + meta->registerClass(domGles_basic_type_common::domInt3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Int3")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domInt3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domInt3)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domInt4::create(DAE& dae) +{ + domGles_basic_type_common::domInt4Ref ref = new domGles_basic_type_common::domInt4(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domInt4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int4" ); + meta->registerClass(domGles_basic_type_common::domInt4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Int4")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domInt4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domInt4)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domFloat::create(DAE& dae) +{ + domGles_basic_type_common::domFloatRef ref = new domGles_basic_type_common::domFloat(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domFloat::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float" ); + meta->registerClass(domGles_basic_type_common::domFloat::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domFloat , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domFloat)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domFloat2::create(DAE& dae) +{ + domGles_basic_type_common::domFloat2Ref ref = new domGles_basic_type_common::domFloat2(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domFloat2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float2" ); + meta->registerClass(domGles_basic_type_common::domFloat2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float2")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domFloat2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domFloat2)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domFloat3::create(DAE& dae) +{ + domGles_basic_type_common::domFloat3Ref ref = new domGles_basic_type_common::domFloat3(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domFloat3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float3" ); + meta->registerClass(domGles_basic_type_common::domFloat3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float3")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domFloat3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domFloat3)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domFloat4::create(DAE& dae) +{ + domGles_basic_type_common::domFloat4Ref ref = new domGles_basic_type_common::domFloat4(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domFloat4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float4" ); + meta->registerClass(domGles_basic_type_common::domFloat4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domFloat4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domFloat4)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domFloat1x1::create(DAE& dae) +{ + domGles_basic_type_common::domFloat1x1Ref ref = new domGles_basic_type_common::domFloat1x1(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domFloat1x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float1x1" ); + meta->registerClass(domGles_basic_type_common::domFloat1x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domFloat1x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domFloat1x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domFloat1x2::create(DAE& dae) +{ + domGles_basic_type_common::domFloat1x2Ref ref = new domGles_basic_type_common::domFloat1x2(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domFloat1x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float1x2" ); + meta->registerClass(domGles_basic_type_common::domFloat1x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float2")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domFloat1x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domFloat1x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domFloat1x3::create(DAE& dae) +{ + domGles_basic_type_common::domFloat1x3Ref ref = new domGles_basic_type_common::domFloat1x3(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domFloat1x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float1x3" ); + meta->registerClass(domGles_basic_type_common::domFloat1x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float3")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domFloat1x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domFloat1x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domFloat1x4::create(DAE& dae) +{ + domGles_basic_type_common::domFloat1x4Ref ref = new domGles_basic_type_common::domFloat1x4(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domFloat1x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float1x4" ); + meta->registerClass(domGles_basic_type_common::domFloat1x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domFloat1x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domFloat1x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domFloat2x1::create(DAE& dae) +{ + domGles_basic_type_common::domFloat2x1Ref ref = new domGles_basic_type_common::domFloat2x1(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domFloat2x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float2x1" ); + meta->registerClass(domGles_basic_type_common::domFloat2x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float2")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domFloat2x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domFloat2x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domFloat2x2::create(DAE& dae) +{ + domGles_basic_type_common::domFloat2x2Ref ref = new domGles_basic_type_common::domFloat2x2(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domFloat2x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float2x2" ); + meta->registerClass(domGles_basic_type_common::domFloat2x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float2x2")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domFloat2x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domFloat2x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domFloat2x3::create(DAE& dae) +{ + domGles_basic_type_common::domFloat2x3Ref ref = new domGles_basic_type_common::domFloat2x3(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domFloat2x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float2x3" ); + meta->registerClass(domGles_basic_type_common::domFloat2x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float2x3")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domFloat2x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domFloat2x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domFloat2x4::create(DAE& dae) +{ + domGles_basic_type_common::domFloat2x4Ref ref = new domGles_basic_type_common::domFloat2x4(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domFloat2x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float2x4" ); + meta->registerClass(domGles_basic_type_common::domFloat2x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float2x4")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domFloat2x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domFloat2x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domFloat3x1::create(DAE& dae) +{ + domGles_basic_type_common::domFloat3x1Ref ref = new domGles_basic_type_common::domFloat3x1(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domFloat3x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float3x1" ); + meta->registerClass(domGles_basic_type_common::domFloat3x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float3")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domFloat3x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domFloat3x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domFloat3x2::create(DAE& dae) +{ + domGles_basic_type_common::domFloat3x2Ref ref = new domGles_basic_type_common::domFloat3x2(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domFloat3x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float3x2" ); + meta->registerClass(domGles_basic_type_common::domFloat3x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float3x2")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domFloat3x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domFloat3x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domFloat3x3::create(DAE& dae) +{ + domGles_basic_type_common::domFloat3x3Ref ref = new domGles_basic_type_common::domFloat3x3(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domFloat3x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float3x3" ); + meta->registerClass(domGles_basic_type_common::domFloat3x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float3x3")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domFloat3x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domFloat3x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domFloat3x4::create(DAE& dae) +{ + domGles_basic_type_common::domFloat3x4Ref ref = new domGles_basic_type_common::domFloat3x4(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domFloat3x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float3x4" ); + meta->registerClass(domGles_basic_type_common::domFloat3x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float3x4")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domFloat3x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domFloat3x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domFloat4x1::create(DAE& dae) +{ + domGles_basic_type_common::domFloat4x1Ref ref = new domGles_basic_type_common::domFloat4x1(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domFloat4x1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float4x1" ); + meta->registerClass(domGles_basic_type_common::domFloat4x1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domFloat4x1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domFloat4x1)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domFloat4x2::create(DAE& dae) +{ + domGles_basic_type_common::domFloat4x2Ref ref = new domGles_basic_type_common::domFloat4x2(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domFloat4x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float4x2" ); + meta->registerClass(domGles_basic_type_common::domFloat4x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float4x2")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domFloat4x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domFloat4x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domFloat4x3::create(DAE& dae) +{ + domGles_basic_type_common::domFloat4x3Ref ref = new domGles_basic_type_common::domFloat4x3(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domFloat4x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float4x3" ); + meta->registerClass(domGles_basic_type_common::domFloat4x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float4x3")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domFloat4x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domFloat4x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domFloat4x4::create(DAE& dae) +{ + domGles_basic_type_common::domFloat4x4Ref ref = new domGles_basic_type_common::domFloat4x4(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domFloat4x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float4x4" ); + meta->registerClass(domGles_basic_type_common::domFloat4x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float4x4")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domFloat4x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domFloat4x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_basic_type_common::domEnum::create(DAE& dae) +{ + domGles_basic_type_common::domEnumRef ref = new domGles_basic_type_common::domEnum(dae); + return ref; +} + + +daeMetaElement * +domGles_basic_type_common::domEnum::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "enum" ); + meta->registerClass(domGles_basic_type_common::domEnum::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Gles_enumeration")); + ma->setOffset( daeOffsetOf( domGles_basic_type_common::domEnum , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_basic_type_common::domEnum)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGles_newparam.cpp b/Engine/lib/collada/src/1.4/dom/domGles_newparam.cpp new file mode 100644 index 000000000..24a2f1917 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGles_newparam.cpp @@ -0,0 +1,163 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGles_newparam::create(DAE& dae) +{ + domGles_newparamRef ref = new domGles_newparam(dae); + return ref; +} + + +daeMetaElement * +domGles_newparam::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "gles_newparam" ); + meta->registerClass(domGles_newparam::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "annotate" ); + mea->setOffset( daeOffsetOf(domGles_newparam,elemAnnotate_array) ); + mea->setElementType( domFx_annotate_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "semantic" ); + mea->setOffset( daeOffsetOf(domGles_newparam,elemSemantic) ); + mea->setElementType( domGles_newparam::domSemantic::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "modifier" ); + mea->setOffset( daeOffsetOf(domGles_newparam,elemModifier) ); + mea->setElementType( domGles_newparam::domModifier::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 1, 1 ); + mea->setName( "gles_basic_type_common" ); + mea->setOffset( daeOffsetOf(domGles_newparam,elemGles_basic_type_common) ); + mea->setElementType( domGles_basic_type_common::registerElement(dae) ); + cm->appendChild( new daeMetaGroup( mea, meta, cm, 3, 1, 1 ) ); + + cm->setMaxOrdinal( 3 ); + meta->setCMRoot( cm ); + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_newparam , attrSid )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_newparam)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_newparam::domSemantic::create(DAE& dae) +{ + domGles_newparam::domSemanticRef ref = new domGles_newparam::domSemantic(dae); + return ref; +} + + +daeMetaElement * +domGles_newparam::domSemantic::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "semantic" ); + meta->registerClass(domGles_newparam::domSemantic::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_newparam::domSemantic , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_newparam::domSemantic)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_newparam::domModifier::create(DAE& dae) +{ + domGles_newparam::domModifierRef ref = new domGles_newparam::domModifier(dae); + return ref; +} + + +daeMetaElement * +domGles_newparam::domModifier::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "modifier" ); + meta->registerClass(domGles_newparam::domModifier::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_modifier_enum_common")); + ma->setOffset( daeOffsetOf( domGles_newparam::domModifier , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_newparam::domModifier)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGles_pipeline_settings.cpp b/Engine/lib/collada/src/1.4/dom/domGles_pipeline_settings.cpp new file mode 100644 index 000000000..79a2a604c --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGles_pipeline_settings.cpp @@ -0,0 +1,4946 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGles_pipeline_settings::create(DAE& dae) +{ + domGles_pipeline_settingsRef ref = new domGles_pipeline_settings(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "gles_pipeline_settings" ); + meta->registerClass(domGles_pipeline_settings::create); + + meta->setIsTransparent( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "alpha_func" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemAlpha_func) ); + mea->setElementType( domGles_pipeline_settings::domAlpha_func::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "blend_func" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemBlend_func) ); + mea->setElementType( domGles_pipeline_settings::domBlend_func::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "clear_color" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemClear_color) ); + mea->setElementType( domGles_pipeline_settings::domClear_color::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "clear_stencil" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemClear_stencil) ); + mea->setElementType( domGles_pipeline_settings::domClear_stencil::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "clear_depth" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemClear_depth) ); + mea->setElementType( domGles_pipeline_settings::domClear_depth::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "clip_plane" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemClip_plane) ); + mea->setElementType( domGles_pipeline_settings::domClip_plane::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "color_mask" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemColor_mask) ); + mea->setElementType( domGles_pipeline_settings::domColor_mask::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "cull_face" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemCull_face) ); + mea->setElementType( domGles_pipeline_settings::domCull_face::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "depth_func" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemDepth_func) ); + mea->setElementType( domGles_pipeline_settings::domDepth_func::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "depth_mask" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemDepth_mask) ); + mea->setElementType( domGles_pipeline_settings::domDepth_mask::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "depth_range" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemDepth_range) ); + mea->setElementType( domGles_pipeline_settings::domDepth_range::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fog_color" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemFog_color) ); + mea->setElementType( domGles_pipeline_settings::domFog_color::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fog_density" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemFog_density) ); + mea->setElementType( domGles_pipeline_settings::domFog_density::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fog_mode" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemFog_mode) ); + mea->setElementType( domGles_pipeline_settings::domFog_mode::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fog_start" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemFog_start) ); + mea->setElementType( domGles_pipeline_settings::domFog_start::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fog_end" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemFog_end) ); + mea->setElementType( domGles_pipeline_settings::domFog_end::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "front_face" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemFront_face) ); + mea->setElementType( domGles_pipeline_settings::domFront_face::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "texture_pipeline" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemTexture_pipeline) ); + mea->setElementType( domGles_pipeline_settings::domTexture_pipeline::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "logic_op" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemLogic_op) ); + mea->setElementType( domGles_pipeline_settings::domLogic_op::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_ambient" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemLight_ambient) ); + mea->setElementType( domGles_pipeline_settings::domLight_ambient::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_diffuse" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemLight_diffuse) ); + mea->setElementType( domGles_pipeline_settings::domLight_diffuse::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_specular" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemLight_specular) ); + mea->setElementType( domGles_pipeline_settings::domLight_specular::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_position" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemLight_position) ); + mea->setElementType( domGles_pipeline_settings::domLight_position::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_constant_attenuation" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemLight_constant_attenuation) ); + mea->setElementType( domGles_pipeline_settings::domLight_constant_attenuation::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_linear_attenutation" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemLight_linear_attenutation) ); + mea->setElementType( domGles_pipeline_settings::domLight_linear_attenutation::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_quadratic_attenuation" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemLight_quadratic_attenuation) ); + mea->setElementType( domGles_pipeline_settings::domLight_quadratic_attenuation::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_spot_cutoff" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemLight_spot_cutoff) ); + mea->setElementType( domGles_pipeline_settings::domLight_spot_cutoff::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_spot_direction" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemLight_spot_direction) ); + mea->setElementType( domGles_pipeline_settings::domLight_spot_direction::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_spot_exponent" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemLight_spot_exponent) ); + mea->setElementType( domGles_pipeline_settings::domLight_spot_exponent::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_model_ambient" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemLight_model_ambient) ); + mea->setElementType( domGles_pipeline_settings::domLight_model_ambient::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "line_width" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemLine_width) ); + mea->setElementType( domGles_pipeline_settings::domLine_width::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "material_ambient" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemMaterial_ambient) ); + mea->setElementType( domGles_pipeline_settings::domMaterial_ambient::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "material_diffuse" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemMaterial_diffuse) ); + mea->setElementType( domGles_pipeline_settings::domMaterial_diffuse::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "material_emission" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemMaterial_emission) ); + mea->setElementType( domGles_pipeline_settings::domMaterial_emission::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "material_shininess" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemMaterial_shininess) ); + mea->setElementType( domGles_pipeline_settings::domMaterial_shininess::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "material_specular" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemMaterial_specular) ); + mea->setElementType( domGles_pipeline_settings::domMaterial_specular::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "model_view_matrix" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemModel_view_matrix) ); + mea->setElementType( domGles_pipeline_settings::domModel_view_matrix::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "point_distance_attenuation" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemPoint_distance_attenuation) ); + mea->setElementType( domGles_pipeline_settings::domPoint_distance_attenuation::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "point_fade_threshold_size" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemPoint_fade_threshold_size) ); + mea->setElementType( domGles_pipeline_settings::domPoint_fade_threshold_size::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "point_size" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemPoint_size) ); + mea->setElementType( domGles_pipeline_settings::domPoint_size::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "point_size_min" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemPoint_size_min) ); + mea->setElementType( domGles_pipeline_settings::domPoint_size_min::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "point_size_max" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemPoint_size_max) ); + mea->setElementType( domGles_pipeline_settings::domPoint_size_max::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "polygon_offset" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemPolygon_offset) ); + mea->setElementType( domGles_pipeline_settings::domPolygon_offset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "projection_matrix" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemProjection_matrix) ); + mea->setElementType( domGles_pipeline_settings::domProjection_matrix::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "scissor" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemScissor) ); + mea->setElementType( domGles_pipeline_settings::domScissor::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "shade_model" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemShade_model) ); + mea->setElementType( domGles_pipeline_settings::domShade_model::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "stencil_func" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemStencil_func) ); + mea->setElementType( domGles_pipeline_settings::domStencil_func::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "stencil_mask" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemStencil_mask) ); + mea->setElementType( domGles_pipeline_settings::domStencil_mask::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "stencil_op" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemStencil_op) ); + mea->setElementType( domGles_pipeline_settings::domStencil_op::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "alpha_test_enable" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemAlpha_test_enable) ); + mea->setElementType( domGles_pipeline_settings::domAlpha_test_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "blend_enable" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemBlend_enable) ); + mea->setElementType( domGles_pipeline_settings::domBlend_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "clip_plane_enable" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemClip_plane_enable) ); + mea->setElementType( domGles_pipeline_settings::domClip_plane_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "color_logic_op_enable" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemColor_logic_op_enable) ); + mea->setElementType( domGles_pipeline_settings::domColor_logic_op_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "color_material_enable" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemColor_material_enable) ); + mea->setElementType( domGles_pipeline_settings::domColor_material_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "cull_face_enable" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemCull_face_enable) ); + mea->setElementType( domGles_pipeline_settings::domCull_face_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "depth_test_enable" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemDepth_test_enable) ); + mea->setElementType( domGles_pipeline_settings::domDepth_test_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "dither_enable" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemDither_enable) ); + mea->setElementType( domGles_pipeline_settings::domDither_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fog_enable" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemFog_enable) ); + mea->setElementType( domGles_pipeline_settings::domFog_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "texture_pipeline_enable" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemTexture_pipeline_enable) ); + mea->setElementType( domGles_pipeline_settings::domTexture_pipeline_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_enable" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemLight_enable) ); + mea->setElementType( domGles_pipeline_settings::domLight_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "lighting_enable" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemLighting_enable) ); + mea->setElementType( domGles_pipeline_settings::domLighting_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "light_model_two_side_enable" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemLight_model_two_side_enable) ); + mea->setElementType( domGles_pipeline_settings::domLight_model_two_side_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "line_smooth_enable" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemLine_smooth_enable) ); + mea->setElementType( domGles_pipeline_settings::domLine_smooth_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "multisample_enable" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemMultisample_enable) ); + mea->setElementType( domGles_pipeline_settings::domMultisample_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "normalize_enable" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemNormalize_enable) ); + mea->setElementType( domGles_pipeline_settings::domNormalize_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "point_smooth_enable" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemPoint_smooth_enable) ); + mea->setElementType( domGles_pipeline_settings::domPoint_smooth_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "polygon_offset_fill_enable" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemPolygon_offset_fill_enable) ); + mea->setElementType( domGles_pipeline_settings::domPolygon_offset_fill_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "rescale_normal_enable" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemRescale_normal_enable) ); + mea->setElementType( domGles_pipeline_settings::domRescale_normal_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "sample_alpha_to_coverage_enable" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemSample_alpha_to_coverage_enable) ); + mea->setElementType( domGles_pipeline_settings::domSample_alpha_to_coverage_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "sample_alpha_to_one_enable" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemSample_alpha_to_one_enable) ); + mea->setElementType( domGles_pipeline_settings::domSample_alpha_to_one_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "sample_coverage_enable" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemSample_coverage_enable) ); + mea->setElementType( domGles_pipeline_settings::domSample_coverage_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "scissor_test_enable" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemScissor_test_enable) ); + mea->setElementType( domGles_pipeline_settings::domScissor_test_enable::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "stencil_test_enable" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings,elemStencil_test_enable) ); + mea->setElementType( domGles_pipeline_settings::domStencil_test_enable::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domGles_pipeline_settings,_contents)); + meta->addContentsOrder(daeOffsetOf(domGles_pipeline_settings,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domGles_pipeline_settings,_CMData), 1); + meta->setElementSize(sizeof(domGles_pipeline_settings)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domAlpha_func::create(DAE& dae) +{ + domGles_pipeline_settings::domAlpha_funcRef ref = new domGles_pipeline_settings::domAlpha_func(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domAlpha_func::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "alpha_func" ); + meta->registerClass(domGles_pipeline_settings::domAlpha_func::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "func" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings::domAlpha_func,elemFunc) ); + mea->setElementType( domGles_pipeline_settings::domAlpha_func::domFunc::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "value" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings::domAlpha_func,elemValue) ); + mea->setElementType( domGles_pipeline_settings::domAlpha_func::domValue::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domGles_pipeline_settings::domAlpha_func)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domAlpha_func::domFunc::create(DAE& dae) +{ + domGles_pipeline_settings::domAlpha_func::domFuncRef ref = new domGles_pipeline_settings::domAlpha_func::domFunc(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domAlpha_func::domFunc::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "func" ); + meta->registerClass(domGles_pipeline_settings::domAlpha_func::domFunc::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_func_type")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domAlpha_func::domFunc , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "ALWAYS"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domAlpha_func::domFunc , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domAlpha_func::domFunc)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domAlpha_func::domValue::create(DAE& dae) +{ + domGles_pipeline_settings::domAlpha_func::domValueRef ref = new domGles_pipeline_settings::domAlpha_func::domValue(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domAlpha_func::domValue::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "value" ); + meta->registerClass(domGles_pipeline_settings::domAlpha_func::domValue::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_alpha_value_type")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domAlpha_func::domValue , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0.0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domAlpha_func::domValue , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domAlpha_func::domValue)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domBlend_func::create(DAE& dae) +{ + domGles_pipeline_settings::domBlend_funcRef ref = new domGles_pipeline_settings::domBlend_func(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domBlend_func::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "blend_func" ); + meta->registerClass(domGles_pipeline_settings::domBlend_func::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "src" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings::domBlend_func,elemSrc) ); + mea->setElementType( domGles_pipeline_settings::domBlend_func::domSrc::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "dest" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings::domBlend_func,elemDest) ); + mea->setElementType( domGles_pipeline_settings::domBlend_func::domDest::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domGles_pipeline_settings::domBlend_func)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domBlend_func::domSrc::create(DAE& dae) +{ + domGles_pipeline_settings::domBlend_func::domSrcRef ref = new domGles_pipeline_settings::domBlend_func::domSrc(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domBlend_func::domSrc::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "src" ); + meta->registerClass(domGles_pipeline_settings::domBlend_func::domSrc::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_blend_type")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domBlend_func::domSrc , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "ONE"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domBlend_func::domSrc , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domBlend_func::domSrc)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domBlend_func::domDest::create(DAE& dae) +{ + domGles_pipeline_settings::domBlend_func::domDestRef ref = new domGles_pipeline_settings::domBlend_func::domDest(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domBlend_func::domDest::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "dest" ); + meta->registerClass(domGles_pipeline_settings::domBlend_func::domDest::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_blend_type")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domBlend_func::domDest , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "ZERO"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domBlend_func::domDest , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domBlend_func::domDest)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domClear_color::create(DAE& dae) +{ + domGles_pipeline_settings::domClear_colorRef ref = new domGles_pipeline_settings::domClear_color(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domClear_color::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "clear_color" ); + meta->registerClass(domGles_pipeline_settings::domClear_color::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domClear_color , attrValue )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domClear_color , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domClear_color)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domClear_stencil::create(DAE& dae) +{ + domGles_pipeline_settings::domClear_stencilRef ref = new domGles_pipeline_settings::domClear_stencil(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domClear_stencil::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "clear_stencil" ); + meta->registerClass(domGles_pipeline_settings::domClear_stencil::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Int")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domClear_stencil , attrValue )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domClear_stencil , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domClear_stencil)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domClear_depth::create(DAE& dae) +{ + domGles_pipeline_settings::domClear_depthRef ref = new domGles_pipeline_settings::domClear_depth(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domClear_depth::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "clear_depth" ); + meta->registerClass(domGles_pipeline_settings::domClear_depth::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domClear_depth , attrValue )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domClear_depth , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domClear_depth)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domClip_plane::create(DAE& dae) +{ + domGles_pipeline_settings::domClip_planeRef ref = new domGles_pipeline_settings::domClip_plane(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domClip_plane::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "clip_plane" ); + meta->registerClass(domGles_pipeline_settings::domClip_plane::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool4")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domClip_plane , attrValue )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domClip_plane , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GLES_MAX_CLIP_PLANES_index")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domClip_plane , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domClip_plane)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domColor_mask::create(DAE& dae) +{ + domGles_pipeline_settings::domColor_maskRef ref = new domGles_pipeline_settings::domColor_mask(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domColor_mask::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "color_mask" ); + meta->registerClass(domGles_pipeline_settings::domColor_mask::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool4")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domColor_mask , attrValue )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domColor_mask , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domColor_mask)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domCull_face::create(DAE& dae) +{ + domGles_pipeline_settings::domCull_faceRef ref = new domGles_pipeline_settings::domCull_face(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domCull_face::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "cull_face" ); + meta->registerClass(domGles_pipeline_settings::domCull_face::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_face_type")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domCull_face , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "BACK"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domCull_face , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domCull_face)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domDepth_func::create(DAE& dae) +{ + domGles_pipeline_settings::domDepth_funcRef ref = new domGles_pipeline_settings::domDepth_func(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domDepth_func::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "depth_func" ); + meta->registerClass(domGles_pipeline_settings::domDepth_func::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_func_type")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domDepth_func , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "ALWAYS"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domDepth_func , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domDepth_func)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domDepth_mask::create(DAE& dae) +{ + domGles_pipeline_settings::domDepth_maskRef ref = new domGles_pipeline_settings::domDepth_mask(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domDepth_mask::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "depth_mask" ); + meta->registerClass(domGles_pipeline_settings::domDepth_mask::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domDepth_mask , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domDepth_mask , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domDepth_mask)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domDepth_range::create(DAE& dae) +{ + domGles_pipeline_settings::domDepth_rangeRef ref = new domGles_pipeline_settings::domDepth_range(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domDepth_range::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "depth_range" ); + meta->registerClass(domGles_pipeline_settings::domDepth_range::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float2")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domDepth_range , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0 1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domDepth_range , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domDepth_range)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domFog_color::create(DAE& dae) +{ + domGles_pipeline_settings::domFog_colorRef ref = new domGles_pipeline_settings::domFog_color(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domFog_color::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fog_color" ); + meta->registerClass(domGles_pipeline_settings::domFog_color::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domFog_color , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0 0 0 0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domFog_color , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domFog_color)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domFog_density::create(DAE& dae) +{ + domGles_pipeline_settings::domFog_densityRef ref = new domGles_pipeline_settings::domFog_density(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domFog_density::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fog_density" ); + meta->registerClass(domGles_pipeline_settings::domFog_density::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domFog_density , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domFog_density , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domFog_density)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domFog_mode::create(DAE& dae) +{ + domGles_pipeline_settings::domFog_modeRef ref = new domGles_pipeline_settings::domFog_mode(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domFog_mode::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fog_mode" ); + meta->registerClass(domGles_pipeline_settings::domFog_mode::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_fog_type")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domFog_mode , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "EXP"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domFog_mode , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domFog_mode)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domFog_start::create(DAE& dae) +{ + domGles_pipeline_settings::domFog_startRef ref = new domGles_pipeline_settings::domFog_start(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domFog_start::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fog_start" ); + meta->registerClass(domGles_pipeline_settings::domFog_start::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domFog_start , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domFog_start , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domFog_start)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domFog_end::create(DAE& dae) +{ + domGles_pipeline_settings::domFog_endRef ref = new domGles_pipeline_settings::domFog_end(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domFog_end::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fog_end" ); + meta->registerClass(domGles_pipeline_settings::domFog_end::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domFog_end , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domFog_end , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domFog_end)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domFront_face::create(DAE& dae) +{ + domGles_pipeline_settings::domFront_faceRef ref = new domGles_pipeline_settings::domFront_face(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domFront_face::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "front_face" ); + meta->registerClass(domGles_pipeline_settings::domFront_face::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_front_face_type")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domFront_face , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "CCW"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domFront_face , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domFront_face)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domTexture_pipeline::create(DAE& dae) +{ + domGles_pipeline_settings::domTexture_pipelineRef ref = new domGles_pipeline_settings::domTexture_pipeline(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domTexture_pipeline::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "texture_pipeline" ); + meta->registerClass(domGles_pipeline_settings::domTexture_pipeline::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "value" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings::domTexture_pipeline,elemValue) ); + mea->setElementType( domGles_texture_pipeline::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domTexture_pipeline , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domTexture_pipeline)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domLogic_op::create(DAE& dae) +{ + domGles_pipeline_settings::domLogic_opRef ref = new domGles_pipeline_settings::domLogic_op(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domLogic_op::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "logic_op" ); + meta->registerClass(domGles_pipeline_settings::domLogic_op::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_logic_op_type")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLogic_op , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "COPY"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLogic_op , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domLogic_op)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domLight_ambient::create(DAE& dae) +{ + domGles_pipeline_settings::domLight_ambientRef ref = new domGles_pipeline_settings::domLight_ambient(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domLight_ambient::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_ambient" ); + meta->registerClass(domGles_pipeline_settings::domLight_ambient::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_ambient , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0 0 0 1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_ambient , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GLES_MAX_LIGHTS_index")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_ambient , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domLight_ambient)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domLight_diffuse::create(DAE& dae) +{ + domGles_pipeline_settings::domLight_diffuseRef ref = new domGles_pipeline_settings::domLight_diffuse(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domLight_diffuse::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_diffuse" ); + meta->registerClass(domGles_pipeline_settings::domLight_diffuse::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_diffuse , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0 0 0 0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_diffuse , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GLES_MAX_LIGHTS_index")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_diffuse , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domLight_diffuse)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domLight_specular::create(DAE& dae) +{ + domGles_pipeline_settings::domLight_specularRef ref = new domGles_pipeline_settings::domLight_specular(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domLight_specular::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_specular" ); + meta->registerClass(domGles_pipeline_settings::domLight_specular::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_specular , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0 0 0 0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_specular , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GLES_MAX_LIGHTS_index")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_specular , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domLight_specular)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domLight_position::create(DAE& dae) +{ + domGles_pipeline_settings::domLight_positionRef ref = new domGles_pipeline_settings::domLight_position(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domLight_position::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_position" ); + meta->registerClass(domGles_pipeline_settings::domLight_position::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_position , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0 0 1 0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_position , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GLES_MAX_LIGHTS_index")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_position , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domLight_position)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domLight_constant_attenuation::create(DAE& dae) +{ + domGles_pipeline_settings::domLight_constant_attenuationRef ref = new domGles_pipeline_settings::domLight_constant_attenuation(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domLight_constant_attenuation::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_constant_attenuation" ); + meta->registerClass(domGles_pipeline_settings::domLight_constant_attenuation::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_constant_attenuation , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_constant_attenuation , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GLES_MAX_LIGHTS_index")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_constant_attenuation , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domLight_constant_attenuation)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domLight_linear_attenutation::create(DAE& dae) +{ + domGles_pipeline_settings::domLight_linear_attenutationRef ref = new domGles_pipeline_settings::domLight_linear_attenutation(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domLight_linear_attenutation::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_linear_attenutation" ); + meta->registerClass(domGles_pipeline_settings::domLight_linear_attenutation::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_linear_attenutation , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_linear_attenutation , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GLES_MAX_LIGHTS_index")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_linear_attenutation , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domLight_linear_attenutation)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domLight_quadratic_attenuation::create(DAE& dae) +{ + domGles_pipeline_settings::domLight_quadratic_attenuationRef ref = new domGles_pipeline_settings::domLight_quadratic_attenuation(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domLight_quadratic_attenuation::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_quadratic_attenuation" ); + meta->registerClass(domGles_pipeline_settings::domLight_quadratic_attenuation::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_quadratic_attenuation , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_quadratic_attenuation , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GLES_MAX_LIGHTS_index")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_quadratic_attenuation , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domLight_quadratic_attenuation)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domLight_spot_cutoff::create(DAE& dae) +{ + domGles_pipeline_settings::domLight_spot_cutoffRef ref = new domGles_pipeline_settings::domLight_spot_cutoff(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domLight_spot_cutoff::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_spot_cutoff" ); + meta->registerClass(domGles_pipeline_settings::domLight_spot_cutoff::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_spot_cutoff , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "180"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_spot_cutoff , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GLES_MAX_LIGHTS_index")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_spot_cutoff , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domLight_spot_cutoff)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domLight_spot_direction::create(DAE& dae) +{ + domGles_pipeline_settings::domLight_spot_directionRef ref = new domGles_pipeline_settings::domLight_spot_direction(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domLight_spot_direction::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_spot_direction" ); + meta->registerClass(domGles_pipeline_settings::domLight_spot_direction::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float3")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_spot_direction , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0 0 -1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_spot_direction , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GLES_MAX_LIGHTS_index")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_spot_direction , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domLight_spot_direction)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domLight_spot_exponent::create(DAE& dae) +{ + domGles_pipeline_settings::domLight_spot_exponentRef ref = new domGles_pipeline_settings::domLight_spot_exponent(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domLight_spot_exponent::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_spot_exponent" ); + meta->registerClass(domGles_pipeline_settings::domLight_spot_exponent::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_spot_exponent , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_spot_exponent , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GLES_MAX_LIGHTS_index")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_spot_exponent , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domLight_spot_exponent)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domLight_model_ambient::create(DAE& dae) +{ + domGles_pipeline_settings::domLight_model_ambientRef ref = new domGles_pipeline_settings::domLight_model_ambient(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domLight_model_ambient::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_model_ambient" ); + meta->registerClass(domGles_pipeline_settings::domLight_model_ambient::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_model_ambient , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0.2 0.2 0.2 1.0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_model_ambient , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domLight_model_ambient)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domLine_width::create(DAE& dae) +{ + domGles_pipeline_settings::domLine_widthRef ref = new domGles_pipeline_settings::domLine_width(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domLine_width::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "line_width" ); + meta->registerClass(domGles_pipeline_settings::domLine_width::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLine_width , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLine_width , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domLine_width)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domMaterial_ambient::create(DAE& dae) +{ + domGles_pipeline_settings::domMaterial_ambientRef ref = new domGles_pipeline_settings::domMaterial_ambient(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domMaterial_ambient::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "material_ambient" ); + meta->registerClass(domGles_pipeline_settings::domMaterial_ambient::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domMaterial_ambient , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0.2 0.2 0.2 1.0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domMaterial_ambient , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domMaterial_ambient)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domMaterial_diffuse::create(DAE& dae) +{ + domGles_pipeline_settings::domMaterial_diffuseRef ref = new domGles_pipeline_settings::domMaterial_diffuse(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domMaterial_diffuse::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "material_diffuse" ); + meta->registerClass(domGles_pipeline_settings::domMaterial_diffuse::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domMaterial_diffuse , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0.8 0.8 0.8 1.0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domMaterial_diffuse , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domMaterial_diffuse)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domMaterial_emission::create(DAE& dae) +{ + domGles_pipeline_settings::domMaterial_emissionRef ref = new domGles_pipeline_settings::domMaterial_emission(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domMaterial_emission::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "material_emission" ); + meta->registerClass(domGles_pipeline_settings::domMaterial_emission::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domMaterial_emission , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0 0 0 1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domMaterial_emission , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domMaterial_emission)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domMaterial_shininess::create(DAE& dae) +{ + domGles_pipeline_settings::domMaterial_shininessRef ref = new domGles_pipeline_settings::domMaterial_shininess(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domMaterial_shininess::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "material_shininess" ); + meta->registerClass(domGles_pipeline_settings::domMaterial_shininess::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domMaterial_shininess , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domMaterial_shininess , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domMaterial_shininess)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domMaterial_specular::create(DAE& dae) +{ + domGles_pipeline_settings::domMaterial_specularRef ref = new domGles_pipeline_settings::domMaterial_specular(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domMaterial_specular::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "material_specular" ); + meta->registerClass(domGles_pipeline_settings::domMaterial_specular::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domMaterial_specular , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0 0 0 1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domMaterial_specular , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domMaterial_specular)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domModel_view_matrix::create(DAE& dae) +{ + domGles_pipeline_settings::domModel_view_matrixRef ref = new domGles_pipeline_settings::domModel_view_matrix(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domModel_view_matrix::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "model_view_matrix" ); + meta->registerClass(domGles_pipeline_settings::domModel_view_matrix::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4x4")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domModel_view_matrix , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domModel_view_matrix , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domModel_view_matrix)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domPoint_distance_attenuation::create(DAE& dae) +{ + domGles_pipeline_settings::domPoint_distance_attenuationRef ref = new domGles_pipeline_settings::domPoint_distance_attenuation(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domPoint_distance_attenuation::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "point_distance_attenuation" ); + meta->registerClass(domGles_pipeline_settings::domPoint_distance_attenuation::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float3")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domPoint_distance_attenuation , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "1 0 0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domPoint_distance_attenuation , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domPoint_distance_attenuation)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domPoint_fade_threshold_size::create(DAE& dae) +{ + domGles_pipeline_settings::domPoint_fade_threshold_sizeRef ref = new domGles_pipeline_settings::domPoint_fade_threshold_size(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domPoint_fade_threshold_size::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "point_fade_threshold_size" ); + meta->registerClass(domGles_pipeline_settings::domPoint_fade_threshold_size::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domPoint_fade_threshold_size , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domPoint_fade_threshold_size , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domPoint_fade_threshold_size)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domPoint_size::create(DAE& dae) +{ + domGles_pipeline_settings::domPoint_sizeRef ref = new domGles_pipeline_settings::domPoint_size(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domPoint_size::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "point_size" ); + meta->registerClass(domGles_pipeline_settings::domPoint_size::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domPoint_size , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domPoint_size , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domPoint_size)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domPoint_size_min::create(DAE& dae) +{ + domGles_pipeline_settings::domPoint_size_minRef ref = new domGles_pipeline_settings::domPoint_size_min(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domPoint_size_min::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "point_size_min" ); + meta->registerClass(domGles_pipeline_settings::domPoint_size_min::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domPoint_size_min , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domPoint_size_min , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domPoint_size_min)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domPoint_size_max::create(DAE& dae) +{ + domGles_pipeline_settings::domPoint_size_maxRef ref = new domGles_pipeline_settings::domPoint_size_max(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domPoint_size_max::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "point_size_max" ); + meta->registerClass(domGles_pipeline_settings::domPoint_size_max::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domPoint_size_max , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domPoint_size_max , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domPoint_size_max)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domPolygon_offset::create(DAE& dae) +{ + domGles_pipeline_settings::domPolygon_offsetRef ref = new domGles_pipeline_settings::domPolygon_offset(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domPolygon_offset::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "polygon_offset" ); + meta->registerClass(domGles_pipeline_settings::domPolygon_offset::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float2")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domPolygon_offset , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0 0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domPolygon_offset , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domPolygon_offset)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domProjection_matrix::create(DAE& dae) +{ + domGles_pipeline_settings::domProjection_matrixRef ref = new domGles_pipeline_settings::domProjection_matrix(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domProjection_matrix::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "projection_matrix" ); + meta->registerClass(domGles_pipeline_settings::domProjection_matrix::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4x4")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domProjection_matrix , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domProjection_matrix , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domProjection_matrix)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domScissor::create(DAE& dae) +{ + domGles_pipeline_settings::domScissorRef ref = new domGles_pipeline_settings::domScissor(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domScissor::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "scissor" ); + meta->registerClass(domGles_pipeline_settings::domScissor::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Int4")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domScissor , attrValue )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domScissor , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domScissor)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domShade_model::create(DAE& dae) +{ + domGles_pipeline_settings::domShade_modelRef ref = new domGles_pipeline_settings::domShade_model(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domShade_model::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "shade_model" ); + meta->registerClass(domGles_pipeline_settings::domShade_model::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_shade_model_type")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domShade_model , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "SMOOTH"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domShade_model , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domShade_model)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domStencil_func::create(DAE& dae) +{ + domGles_pipeline_settings::domStencil_funcRef ref = new domGles_pipeline_settings::domStencil_func(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domStencil_func::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "stencil_func" ); + meta->registerClass(domGles_pipeline_settings::domStencil_func::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "func" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings::domStencil_func,elemFunc) ); + mea->setElementType( domGles_pipeline_settings::domStencil_func::domFunc::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "ref" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings::domStencil_func,elemRef) ); + mea->setElementType( domGles_pipeline_settings::domStencil_func::domRef::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 1, 1 ); + mea->setName( "mask" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings::domStencil_func,elemMask) ); + mea->setElementType( domGles_pipeline_settings::domStencil_func::domMask::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domGles_pipeline_settings::domStencil_func)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domStencil_func::domFunc::create(DAE& dae) +{ + domGles_pipeline_settings::domStencil_func::domFuncRef ref = new domGles_pipeline_settings::domStencil_func::domFunc(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domStencil_func::domFunc::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "func" ); + meta->registerClass(domGles_pipeline_settings::domStencil_func::domFunc::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gl_func_type")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domStencil_func::domFunc , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "ALWAYS"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domStencil_func::domFunc , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domStencil_func::domFunc)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domStencil_func::domRef::create(DAE& dae) +{ + domGles_pipeline_settings::domStencil_func::domRefRef ref = new domGles_pipeline_settings::domStencil_func::domRef(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domStencil_func::domRef::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "ref" ); + meta->registerClass(domGles_pipeline_settings::domStencil_func::domRef::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("xsUnsignedByte")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domStencil_func::domRef , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "0"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domStencil_func::domRef , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domStencil_func::domRef)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domStencil_func::domMask::create(DAE& dae) +{ + domGles_pipeline_settings::domStencil_func::domMaskRef ref = new domGles_pipeline_settings::domStencil_func::domMask(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domStencil_func::domMask::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mask" ); + meta->registerClass(domGles_pipeline_settings::domStencil_func::domMask::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("xsUnsignedByte")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domStencil_func::domMask , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "255"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domStencil_func::domMask , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domStencil_func::domMask)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domStencil_mask::create(DAE& dae) +{ + domGles_pipeline_settings::domStencil_maskRef ref = new domGles_pipeline_settings::domStencil_mask(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domStencil_mask::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "stencil_mask" ); + meta->registerClass(domGles_pipeline_settings::domStencil_mask::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Int")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domStencil_mask , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "4294967295"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domStencil_mask , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domStencil_mask)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domStencil_op::create(DAE& dae) +{ + domGles_pipeline_settings::domStencil_opRef ref = new domGles_pipeline_settings::domStencil_op(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domStencil_op::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "stencil_op" ); + meta->registerClass(domGles_pipeline_settings::domStencil_op::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fail" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings::domStencil_op,elemFail) ); + mea->setElementType( domGles_pipeline_settings::domStencil_op::domFail::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "zfail" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings::domStencil_op,elemZfail) ); + mea->setElementType( domGles_pipeline_settings::domStencil_op::domZfail::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 1, 1 ); + mea->setName( "zpass" ); + mea->setOffset( daeOffsetOf(domGles_pipeline_settings::domStencil_op,elemZpass) ); + mea->setElementType( domGles_pipeline_settings::domStencil_op::domZpass::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domGles_pipeline_settings::domStencil_op)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domStencil_op::domFail::create(DAE& dae) +{ + domGles_pipeline_settings::domStencil_op::domFailRef ref = new domGles_pipeline_settings::domStencil_op::domFail(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domStencil_op::domFail::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fail" ); + meta->registerClass(domGles_pipeline_settings::domStencil_op::domFail::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gles_stencil_op_type")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domStencil_op::domFail , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "KEEP"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domStencil_op::domFail , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domStencil_op::domFail)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domStencil_op::domZfail::create(DAE& dae) +{ + domGles_pipeline_settings::domStencil_op::domZfailRef ref = new domGles_pipeline_settings::domStencil_op::domZfail(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domStencil_op::domZfail::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "zfail" ); + meta->registerClass(domGles_pipeline_settings::domStencil_op::domZfail::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gles_stencil_op_type")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domStencil_op::domZfail , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "KEEP"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domStencil_op::domZfail , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domStencil_op::domZfail)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domStencil_op::domZpass::create(DAE& dae) +{ + domGles_pipeline_settings::domStencil_op::domZpassRef ref = new domGles_pipeline_settings::domStencil_op::domZpass(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domStencil_op::domZpass::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "zpass" ); + meta->registerClass(domGles_pipeline_settings::domStencil_op::domZpass::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Gles_stencil_op_type")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domStencil_op::domZpass , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "KEEP"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domStencil_op::domZpass , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domStencil_op::domZpass)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domAlpha_test_enable::create(DAE& dae) +{ + domGles_pipeline_settings::domAlpha_test_enableRef ref = new domGles_pipeline_settings::domAlpha_test_enable(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domAlpha_test_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "alpha_test_enable" ); + meta->registerClass(domGles_pipeline_settings::domAlpha_test_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domAlpha_test_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domAlpha_test_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domAlpha_test_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domBlend_enable::create(DAE& dae) +{ + domGles_pipeline_settings::domBlend_enableRef ref = new domGles_pipeline_settings::domBlend_enable(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domBlend_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "blend_enable" ); + meta->registerClass(domGles_pipeline_settings::domBlend_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domBlend_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domBlend_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domBlend_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domClip_plane_enable::create(DAE& dae) +{ + domGles_pipeline_settings::domClip_plane_enableRef ref = new domGles_pipeline_settings::domClip_plane_enable(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domClip_plane_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "clip_plane_enable" ); + meta->registerClass(domGles_pipeline_settings::domClip_plane_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domClip_plane_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domClip_plane_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GLES_MAX_CLIP_PLANES_index")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domClip_plane_enable , attrIndex )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domClip_plane_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domColor_logic_op_enable::create(DAE& dae) +{ + domGles_pipeline_settings::domColor_logic_op_enableRef ref = new domGles_pipeline_settings::domColor_logic_op_enable(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domColor_logic_op_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "color_logic_op_enable" ); + meta->registerClass(domGles_pipeline_settings::domColor_logic_op_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domColor_logic_op_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domColor_logic_op_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domColor_logic_op_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domColor_material_enable::create(DAE& dae) +{ + domGles_pipeline_settings::domColor_material_enableRef ref = new domGles_pipeline_settings::domColor_material_enable(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domColor_material_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "color_material_enable" ); + meta->registerClass(domGles_pipeline_settings::domColor_material_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domColor_material_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "true"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domColor_material_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domColor_material_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domCull_face_enable::create(DAE& dae) +{ + domGles_pipeline_settings::domCull_face_enableRef ref = new domGles_pipeline_settings::domCull_face_enable(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domCull_face_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "cull_face_enable" ); + meta->registerClass(domGles_pipeline_settings::domCull_face_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domCull_face_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domCull_face_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domCull_face_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domDepth_test_enable::create(DAE& dae) +{ + domGles_pipeline_settings::domDepth_test_enableRef ref = new domGles_pipeline_settings::domDepth_test_enable(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domDepth_test_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "depth_test_enable" ); + meta->registerClass(domGles_pipeline_settings::domDepth_test_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domDepth_test_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domDepth_test_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domDepth_test_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domDither_enable::create(DAE& dae) +{ + domGles_pipeline_settings::domDither_enableRef ref = new domGles_pipeline_settings::domDither_enable(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domDither_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "dither_enable" ); + meta->registerClass(domGles_pipeline_settings::domDither_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domDither_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domDither_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domDither_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domFog_enable::create(DAE& dae) +{ + domGles_pipeline_settings::domFog_enableRef ref = new domGles_pipeline_settings::domFog_enable(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domFog_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "fog_enable" ); + meta->registerClass(domGles_pipeline_settings::domFog_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domFog_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domFog_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domFog_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domTexture_pipeline_enable::create(DAE& dae) +{ + domGles_pipeline_settings::domTexture_pipeline_enableRef ref = new domGles_pipeline_settings::domTexture_pipeline_enable(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domTexture_pipeline_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "texture_pipeline_enable" ); + meta->registerClass(domGles_pipeline_settings::domTexture_pipeline_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domTexture_pipeline_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domTexture_pipeline_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domTexture_pipeline_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domLight_enable::create(DAE& dae) +{ + domGles_pipeline_settings::domLight_enableRef ref = new domGles_pipeline_settings::domLight_enable(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domLight_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_enable" ); + meta->registerClass(domGles_pipeline_settings::domLight_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: index + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "index" ); + ma->setType( dae.getAtomicTypes().get("GLES_MAX_LIGHTS_index")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_enable , attrIndex )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domLight_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domLighting_enable::create(DAE& dae) +{ + domGles_pipeline_settings::domLighting_enableRef ref = new domGles_pipeline_settings::domLighting_enable(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domLighting_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "lighting_enable" ); + meta->registerClass(domGles_pipeline_settings::domLighting_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLighting_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLighting_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domLighting_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domLight_model_two_side_enable::create(DAE& dae) +{ + domGles_pipeline_settings::domLight_model_two_side_enableRef ref = new domGles_pipeline_settings::domLight_model_two_side_enable(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domLight_model_two_side_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light_model_two_side_enable" ); + meta->registerClass(domGles_pipeline_settings::domLight_model_two_side_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_model_two_side_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLight_model_two_side_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domLight_model_two_side_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domLine_smooth_enable::create(DAE& dae) +{ + domGles_pipeline_settings::domLine_smooth_enableRef ref = new domGles_pipeline_settings::domLine_smooth_enable(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domLine_smooth_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "line_smooth_enable" ); + meta->registerClass(domGles_pipeline_settings::domLine_smooth_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLine_smooth_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domLine_smooth_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domLine_smooth_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domMultisample_enable::create(DAE& dae) +{ + domGles_pipeline_settings::domMultisample_enableRef ref = new domGles_pipeline_settings::domMultisample_enable(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domMultisample_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "multisample_enable" ); + meta->registerClass(domGles_pipeline_settings::domMultisample_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domMultisample_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domMultisample_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domMultisample_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domNormalize_enable::create(DAE& dae) +{ + domGles_pipeline_settings::domNormalize_enableRef ref = new domGles_pipeline_settings::domNormalize_enable(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domNormalize_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "normalize_enable" ); + meta->registerClass(domGles_pipeline_settings::domNormalize_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domNormalize_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domNormalize_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domNormalize_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domPoint_smooth_enable::create(DAE& dae) +{ + domGles_pipeline_settings::domPoint_smooth_enableRef ref = new domGles_pipeline_settings::domPoint_smooth_enable(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domPoint_smooth_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "point_smooth_enable" ); + meta->registerClass(domGles_pipeline_settings::domPoint_smooth_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domPoint_smooth_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domPoint_smooth_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domPoint_smooth_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domPolygon_offset_fill_enable::create(DAE& dae) +{ + domGles_pipeline_settings::domPolygon_offset_fill_enableRef ref = new domGles_pipeline_settings::domPolygon_offset_fill_enable(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domPolygon_offset_fill_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "polygon_offset_fill_enable" ); + meta->registerClass(domGles_pipeline_settings::domPolygon_offset_fill_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domPolygon_offset_fill_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domPolygon_offset_fill_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domPolygon_offset_fill_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domRescale_normal_enable::create(DAE& dae) +{ + domGles_pipeline_settings::domRescale_normal_enableRef ref = new domGles_pipeline_settings::domRescale_normal_enable(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domRescale_normal_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "rescale_normal_enable" ); + meta->registerClass(domGles_pipeline_settings::domRescale_normal_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domRescale_normal_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domRescale_normal_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domRescale_normal_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domSample_alpha_to_coverage_enable::create(DAE& dae) +{ + domGles_pipeline_settings::domSample_alpha_to_coverage_enableRef ref = new domGles_pipeline_settings::domSample_alpha_to_coverage_enable(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domSample_alpha_to_coverage_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "sample_alpha_to_coverage_enable" ); + meta->registerClass(domGles_pipeline_settings::domSample_alpha_to_coverage_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domSample_alpha_to_coverage_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domSample_alpha_to_coverage_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domSample_alpha_to_coverage_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domSample_alpha_to_one_enable::create(DAE& dae) +{ + domGles_pipeline_settings::domSample_alpha_to_one_enableRef ref = new domGles_pipeline_settings::domSample_alpha_to_one_enable(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domSample_alpha_to_one_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "sample_alpha_to_one_enable" ); + meta->registerClass(domGles_pipeline_settings::domSample_alpha_to_one_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domSample_alpha_to_one_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domSample_alpha_to_one_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domSample_alpha_to_one_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domSample_coverage_enable::create(DAE& dae) +{ + domGles_pipeline_settings::domSample_coverage_enableRef ref = new domGles_pipeline_settings::domSample_coverage_enable(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domSample_coverage_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "sample_coverage_enable" ); + meta->registerClass(domGles_pipeline_settings::domSample_coverage_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domSample_coverage_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domSample_coverage_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domSample_coverage_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domScissor_test_enable::create(DAE& dae) +{ + domGles_pipeline_settings::domScissor_test_enableRef ref = new domGles_pipeline_settings::domScissor_test_enable(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domScissor_test_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "scissor_test_enable" ); + meta->registerClass(domGles_pipeline_settings::domScissor_test_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domScissor_test_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domScissor_test_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domScissor_test_enable)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_pipeline_settings::domStencil_test_enable::create(DAE& dae) +{ + domGles_pipeline_settings::domStencil_test_enableRef ref = new domGles_pipeline_settings::domStencil_test_enable(dae); + return ref; +} + + +daeMetaElement * +domGles_pipeline_settings::domStencil_test_enable::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "stencil_test_enable" ); + meta->registerClass(domGles_pipeline_settings::domStencil_test_enable::create); + + meta->setIsInnerClass( true ); + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domStencil_test_enable , attrValue )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_pipeline_settings::domStencil_test_enable , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_pipeline_settings::domStencil_test_enable)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGles_sampler_state.cpp b/Engine/lib/collada/src/1.4/dom/domGles_sampler_state.cpp new file mode 100644 index 000000000..565ddd793 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGles_sampler_state.cpp @@ -0,0 +1,366 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGles_sampler_state::create(DAE& dae) +{ + domGles_sampler_stateRef ref = new domGles_sampler_state(dae); + return ref; +} + + +daeMetaElement * +domGles_sampler_state::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "gles_sampler_state" ); + meta->registerClass(domGles_sampler_state::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "wrap_s" ); + mea->setOffset( daeOffsetOf(domGles_sampler_state,elemWrap_s) ); + mea->setElementType( domGles_sampler_state::domWrap_s::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "wrap_t" ); + mea->setOffset( daeOffsetOf(domGles_sampler_state,elemWrap_t) ); + mea->setElementType( domGles_sampler_state::domWrap_t::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "minfilter" ); + mea->setOffset( daeOffsetOf(domGles_sampler_state,elemMinfilter) ); + mea->setElementType( domGles_sampler_state::domMinfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "magfilter" ); + mea->setOffset( daeOffsetOf(domGles_sampler_state,elemMagfilter) ); + mea->setElementType( domGles_sampler_state::domMagfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "mipfilter" ); + mea->setOffset( daeOffsetOf(domGles_sampler_state,elemMipfilter) ); + mea->setElementType( domGles_sampler_state::domMipfilter::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 5, 0, 1 ); + mea->setName( "mipmap_maxlevel" ); + mea->setOffset( daeOffsetOf(domGles_sampler_state,elemMipmap_maxlevel) ); + mea->setElementType( domGles_sampler_state::domMipmap_maxlevel::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 6, 0, 1 ); + mea->setName( "mipmap_bias" ); + mea->setOffset( daeOffsetOf(domGles_sampler_state,elemMipmap_bias) ); + mea->setElementType( domGles_sampler_state::domMipmap_bias::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 7, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domGles_sampler_state,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 7 ); + meta->setCMRoot( cm ); + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_sampler_state , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_sampler_state)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_sampler_state::domWrap_s::create(DAE& dae) +{ + domGles_sampler_state::domWrap_sRef ref = new domGles_sampler_state::domWrap_s(dae); + return ref; +} + + +daeMetaElement * +domGles_sampler_state::domWrap_s::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "wrap_s" ); + meta->registerClass(domGles_sampler_state::domWrap_s::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Gles_sampler_wrap")); + ma->setOffset( daeOffsetOf( domGles_sampler_state::domWrap_s , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_sampler_state::domWrap_s)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_sampler_state::domWrap_t::create(DAE& dae) +{ + domGles_sampler_state::domWrap_tRef ref = new domGles_sampler_state::domWrap_t(dae); + return ref; +} + + +daeMetaElement * +domGles_sampler_state::domWrap_t::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "wrap_t" ); + meta->registerClass(domGles_sampler_state::domWrap_t::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Gles_sampler_wrap")); + ma->setOffset( daeOffsetOf( domGles_sampler_state::domWrap_t , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_sampler_state::domWrap_t)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_sampler_state::domMinfilter::create(DAE& dae) +{ + domGles_sampler_state::domMinfilterRef ref = new domGles_sampler_state::domMinfilter(dae); + return ref; +} + + +daeMetaElement * +domGles_sampler_state::domMinfilter::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "minfilter" ); + meta->registerClass(domGles_sampler_state::domMinfilter::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_filter_common")); + ma->setOffset( daeOffsetOf( domGles_sampler_state::domMinfilter , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_sampler_state::domMinfilter)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_sampler_state::domMagfilter::create(DAE& dae) +{ + domGles_sampler_state::domMagfilterRef ref = new domGles_sampler_state::domMagfilter(dae); + return ref; +} + + +daeMetaElement * +domGles_sampler_state::domMagfilter::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "magfilter" ); + meta->registerClass(domGles_sampler_state::domMagfilter::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_filter_common")); + ma->setOffset( daeOffsetOf( domGles_sampler_state::domMagfilter , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_sampler_state::domMagfilter)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_sampler_state::domMipfilter::create(DAE& dae) +{ + domGles_sampler_state::domMipfilterRef ref = new domGles_sampler_state::domMipfilter(dae); + return ref; +} + + +daeMetaElement * +domGles_sampler_state::domMipfilter::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mipfilter" ); + meta->registerClass(domGles_sampler_state::domMipfilter::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_sampler_filter_common")); + ma->setOffset( daeOffsetOf( domGles_sampler_state::domMipfilter , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_sampler_state::domMipfilter)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_sampler_state::domMipmap_maxlevel::create(DAE& dae) +{ + domGles_sampler_state::domMipmap_maxlevelRef ref = new domGles_sampler_state::domMipmap_maxlevel(dae); + return ref; +} + + +daeMetaElement * +domGles_sampler_state::domMipmap_maxlevel::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mipmap_maxlevel" ); + meta->registerClass(domGles_sampler_state::domMipmap_maxlevel::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsUnsignedByte")); + ma->setOffset( daeOffsetOf( domGles_sampler_state::domMipmap_maxlevel , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_sampler_state::domMipmap_maxlevel)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_sampler_state::domMipmap_bias::create(DAE& dae) +{ + domGles_sampler_state::domMipmap_biasRef ref = new domGles_sampler_state::domMipmap_bias(dae); + return ref; +} + + +daeMetaElement * +domGles_sampler_state::domMipmap_bias::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mipmap_bias" ); + meta->registerClass(domGles_sampler_state::domMipmap_bias::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsFloat")); + ma->setOffset( daeOffsetOf( domGles_sampler_state::domMipmap_bias , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_sampler_state::domMipmap_bias)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGles_texcombiner_argumentAlpha_type.cpp b/Engine/lib/collada/src/1.4/dom/domGles_texcombiner_argumentAlpha_type.cpp new file mode 100644 index 000000000..b45298c5e --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGles_texcombiner_argumentAlpha_type.cpp @@ -0,0 +1,84 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGles_texcombiner_argumentAlpha_type::create(DAE& dae) +{ + domGles_texcombiner_argumentAlpha_typeRef ref = new domGles_texcombiner_argumentAlpha_type(dae); + return ref; +} + + +daeMetaElement * +domGles_texcombiner_argumentAlpha_type::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "gles_texcombiner_argumentAlpha_type" ); + meta->registerClass(domGles_texcombiner_argumentAlpha_type::create); + + + // Add attribute: source + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "source" ); + ma->setType( dae.getAtomicTypes().get("Gles_texcombiner_source_enums")); + ma->setOffset( daeOffsetOf( domGles_texcombiner_argumentAlpha_type , attrSource )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: operand + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "operand" ); + ma->setType( dae.getAtomicTypes().get("Gles_texcombiner_operandAlpha_enums")); + ma->setOffset( daeOffsetOf( domGles_texcombiner_argumentAlpha_type , attrOperand )); + ma->setContainer( meta ); + ma->setDefaultString( "SRC_ALPHA"); + + meta->appendAttribute(ma); + } + + // Add attribute: unit + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "unit" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_texcombiner_argumentAlpha_type , attrUnit )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_texcombiner_argumentAlpha_type)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGles_texcombiner_argumentRGB_type.cpp b/Engine/lib/collada/src/1.4/dom/domGles_texcombiner_argumentRGB_type.cpp new file mode 100644 index 000000000..f96fe91d5 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGles_texcombiner_argumentRGB_type.cpp @@ -0,0 +1,84 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGles_texcombiner_argumentRGB_type::create(DAE& dae) +{ + domGles_texcombiner_argumentRGB_typeRef ref = new domGles_texcombiner_argumentRGB_type(dae); + return ref; +} + + +daeMetaElement * +domGles_texcombiner_argumentRGB_type::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "gles_texcombiner_argumentRGB_type" ); + meta->registerClass(domGles_texcombiner_argumentRGB_type::create); + + + // Add attribute: source + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "source" ); + ma->setType( dae.getAtomicTypes().get("Gles_texcombiner_source_enums")); + ma->setOffset( daeOffsetOf( domGles_texcombiner_argumentRGB_type , attrSource )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: operand + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "operand" ); + ma->setType( dae.getAtomicTypes().get("Gles_texcombiner_operandRGB_enums")); + ma->setOffset( daeOffsetOf( domGles_texcombiner_argumentRGB_type , attrOperand )); + ma->setContainer( meta ); + ma->setDefaultString( "SRC_COLOR"); + + meta->appendAttribute(ma); + } + + // Add attribute: unit + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "unit" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_texcombiner_argumentRGB_type , attrUnit )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_texcombiner_argumentRGB_type)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGles_texcombiner_commandAlpha_type.cpp b/Engine/lib/collada/src/1.4/dom/domGles_texcombiner_commandAlpha_type.cpp new file mode 100644 index 000000000..9a6ef1900 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGles_texcombiner_commandAlpha_type.cpp @@ -0,0 +1,84 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGles_texcombiner_commandAlpha_type::create(DAE& dae) +{ + domGles_texcombiner_commandAlpha_typeRef ref = new domGles_texcombiner_commandAlpha_type(dae); + return ref; +} + + +daeMetaElement * +domGles_texcombiner_commandAlpha_type::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "gles_texcombiner_commandAlpha_type" ); + meta->registerClass(domGles_texcombiner_commandAlpha_type::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 3 ); + mea->setName( "argument" ); + mea->setOffset( daeOffsetOf(domGles_texcombiner_commandAlpha_type,elemArgument_array) ); + mea->setElementType( domGles_texcombiner_argumentAlpha_type::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + + // Add attribute: operator + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "operator" ); + ma->setType( dae.getAtomicTypes().get("Gles_texcombiner_operatorAlpha_enums")); + ma->setOffset( daeOffsetOf( domGles_texcombiner_commandAlpha_type , attrOperator )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: scale + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "scale" ); + ma->setType( dae.getAtomicTypes().get("xsFloat")); + ma->setOffset( daeOffsetOf( domGles_texcombiner_commandAlpha_type , attrScale )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_texcombiner_commandAlpha_type)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGles_texcombiner_commandRGB_type.cpp b/Engine/lib/collada/src/1.4/dom/domGles_texcombiner_commandRGB_type.cpp new file mode 100644 index 000000000..8519af955 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGles_texcombiner_commandRGB_type.cpp @@ -0,0 +1,84 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGles_texcombiner_commandRGB_type::create(DAE& dae) +{ + domGles_texcombiner_commandRGB_typeRef ref = new domGles_texcombiner_commandRGB_type(dae); + return ref; +} + + +daeMetaElement * +domGles_texcombiner_commandRGB_type::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "gles_texcombiner_commandRGB_type" ); + meta->registerClass(domGles_texcombiner_commandRGB_type::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 3 ); + mea->setName( "argument" ); + mea->setOffset( daeOffsetOf(domGles_texcombiner_commandRGB_type,elemArgument_array) ); + mea->setElementType( domGles_texcombiner_argumentRGB_type::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + + // Add attribute: operator + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "operator" ); + ma->setType( dae.getAtomicTypes().get("Gles_texcombiner_operatorRGB_enums")); + ma->setOffset( daeOffsetOf( domGles_texcombiner_commandRGB_type , attrOperator )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: scale + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "scale" ); + ma->setType( dae.getAtomicTypes().get("xsFloat")); + ma->setOffset( daeOffsetOf( domGles_texcombiner_commandRGB_type , attrScale )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_texcombiner_commandRGB_type)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGles_texcombiner_command_type.cpp b/Engine/lib/collada/src/1.4/dom/domGles_texcombiner_command_type.cpp new file mode 100644 index 000000000..c984ac024 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGles_texcombiner_command_type.cpp @@ -0,0 +1,73 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGles_texcombiner_command_type::create(DAE& dae) +{ + domGles_texcombiner_command_typeRef ref = new domGles_texcombiner_command_type(dae); + return ref; +} + + +daeMetaElement * +domGles_texcombiner_command_type::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "gles_texcombiner_command_type" ); + meta->registerClass(domGles_texcombiner_command_type::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "constant" ); + mea->setOffset( daeOffsetOf(domGles_texcombiner_command_type,elemConstant) ); + mea->setElementType( domGles_texture_constant_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "RGB" ); + mea->setOffset( daeOffsetOf(domGles_texcombiner_command_type,elemRGB) ); + mea->setElementType( domGles_texcombiner_commandRGB_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "alpha" ); + mea->setOffset( daeOffsetOf(domGles_texcombiner_command_type,elemAlpha) ); + mea->setElementType( domGles_texcombiner_commandAlpha_type::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domGles_texcombiner_command_type)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGles_texenv_command_type.cpp b/Engine/lib/collada/src/1.4/dom/domGles_texenv_command_type.cpp new file mode 100644 index 000000000..7737bd275 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGles_texenv_command_type.cpp @@ -0,0 +1,83 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGles_texenv_command_type::create(DAE& dae) +{ + domGles_texenv_command_typeRef ref = new domGles_texenv_command_type(dae); + return ref; +} + + +daeMetaElement * +domGles_texenv_command_type::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "gles_texenv_command_type" ); + meta->registerClass(domGles_texenv_command_type::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "constant" ); + mea->setOffset( daeOffsetOf(domGles_texenv_command_type,elemConstant) ); + mea->setElementType( domGles_texture_constant_type::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + + // Add attribute: operator + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "operator" ); + ma->setType( dae.getAtomicTypes().get("Gles_texenv_mode_enums")); + ma->setOffset( daeOffsetOf( domGles_texenv_command_type , attrOperator )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: unit + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "unit" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_texenv_command_type , attrUnit )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_texenv_command_type)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGles_texture_constant_type.cpp b/Engine/lib/collada/src/1.4/dom/domGles_texture_constant_type.cpp new file mode 100644 index 000000000..408bafe5d --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGles_texture_constant_type.cpp @@ -0,0 +1,73 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGles_texture_constant_type::create(DAE& dae) +{ + domGles_texture_constant_typeRef ref = new domGles_texture_constant_type(dae); + return ref; +} + + +daeMetaElement * +domGles_texture_constant_type::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "gles_texture_constant_type" ); + meta->registerClass(domGles_texture_constant_type::create); + + + // Add attribute: value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domGles_texture_constant_type , attrValue )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: param + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "param" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_texture_constant_type , attrParam )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_texture_constant_type)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGles_texture_pipeline.cpp b/Engine/lib/collada/src/1.4/dom/domGles_texture_pipeline.cpp new file mode 100644 index 000000000..728f449c1 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGles_texture_pipeline.cpp @@ -0,0 +1,88 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGles_texture_pipeline::create(DAE& dae) +{ + domGles_texture_pipelineRef ref = new domGles_texture_pipeline(dae); + return ref; +} + + +daeMetaElement * +domGles_texture_pipeline::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "gles_texture_pipeline" ); + meta->registerClass(domGles_texture_pipeline::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 1, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "texcombiner" ); + mea->setOffset( daeOffsetOf(domGles_texture_pipeline,elemTexcombiner_array) ); + mea->setElementType( domGles_texcombiner_command_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "texenv" ); + mea->setOffset( daeOffsetOf(domGles_texture_pipeline,elemTexenv_array) ); + mea->setElementType( domGles_texenv_command_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domGles_texture_pipeline,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3000 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domGles_texture_pipeline,_contents)); + meta->addContentsOrder(daeOffsetOf(domGles_texture_pipeline,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domGles_texture_pipeline,_CMData), 1); + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_texture_pipeline , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_texture_pipeline)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGles_texture_unit.cpp b/Engine/lib/collada/src/1.4/dom/domGles_texture_unit.cpp new file mode 100644 index 000000000..510b9cbcb --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGles_texture_unit.cpp @@ -0,0 +1,200 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGles_texture_unit::create(DAE& dae) +{ + domGles_texture_unitRef ref = new domGles_texture_unit(dae); + return ref; +} + + +daeMetaElement * +domGles_texture_unit::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "gles_texture_unit" ); + meta->registerClass(domGles_texture_unit::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "surface" ); + mea->setOffset( daeOffsetOf(domGles_texture_unit,elemSurface) ); + mea->setElementType( domGles_texture_unit::domSurface::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "sampler_state" ); + mea->setOffset( daeOffsetOf(domGles_texture_unit,elemSampler_state) ); + mea->setElementType( domGles_texture_unit::domSampler_state::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "texcoord" ); + mea->setOffset( daeOffsetOf(domGles_texture_unit,elemTexcoord) ); + mea->setElementType( domGles_texture_unit::domTexcoord::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domGles_texture_unit,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3 ); + meta->setCMRoot( cm ); + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_texture_unit , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_texture_unit)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_texture_unit::domSurface::create(DAE& dae) +{ + domGles_texture_unit::domSurfaceRef ref = new domGles_texture_unit::domSurface(dae); + return ref; +} + + +daeMetaElement * +domGles_texture_unit::domSurface::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "surface" ); + meta->registerClass(domGles_texture_unit::domSurface::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_texture_unit::domSurface , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_texture_unit::domSurface)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_texture_unit::domSampler_state::create(DAE& dae) +{ + domGles_texture_unit::domSampler_stateRef ref = new domGles_texture_unit::domSampler_state(dae); + return ref; +} + + +daeMetaElement * +domGles_texture_unit::domSampler_state::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "sampler_state" ); + meta->registerClass(domGles_texture_unit::domSampler_state::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_texture_unit::domSampler_state , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_texture_unit::domSampler_state)); + meta->validate(); + + return meta; +} + +daeElementRef +domGles_texture_unit::domTexcoord::create(DAE& dae) +{ + domGles_texture_unit::domTexcoordRef ref = new domGles_texture_unit::domTexcoord(dae); + return ref; +} + + +daeMetaElement * +domGles_texture_unit::domTexcoord::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "texcoord" ); + meta->registerClass(domGles_texture_unit::domTexcoord::create); + + meta->setIsInnerClass( true ); + + // Add attribute: semantic + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "semantic" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGles_texture_unit::domTexcoord , attrSemantic )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGles_texture_unit::domTexcoord)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGlsl_newarray_type.cpp b/Engine/lib/collada/src/1.4/dom/domGlsl_newarray_type.cpp new file mode 100644 index 000000000..4e0a0fc29 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGlsl_newarray_type.cpp @@ -0,0 +1,83 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGlsl_newarray_type::create(DAE& dae) +{ + domGlsl_newarray_typeRef ref = new domGlsl_newarray_type(dae); + return ref; +} + + +daeMetaElement * +domGlsl_newarray_type::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "glsl_newarray_type" ); + meta->registerClass(domGlsl_newarray_type::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 0, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "glsl_param_type" ); + mea->setOffset( daeOffsetOf(domGlsl_newarray_type,elemGlsl_param_type_array) ); + mea->setElementType( domGlsl_param_type::registerElement(dae) ); + cm->appendChild( new daeMetaGroup( mea, meta, cm, 0, 1, 1 ) ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "array" ); + mea->setOffset( daeOffsetOf(domGlsl_newarray_type,elemArray_array) ); + mea->setElementType( domGlsl_newarray_type::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3000 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domGlsl_newarray_type,_contents)); + meta->addContentsOrder(daeOffsetOf(domGlsl_newarray_type,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domGlsl_newarray_type,_CMData), 1); + // Add attribute: length + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "length" ); + ma->setType( dae.getAtomicTypes().get("xsPositiveInteger")); + ma->setOffset( daeOffsetOf( domGlsl_newarray_type , attrLength )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGlsl_newarray_type)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGlsl_newparam.cpp b/Engine/lib/collada/src/1.4/dom/domGlsl_newparam.cpp new file mode 100644 index 000000000..abd152c2d --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGlsl_newparam.cpp @@ -0,0 +1,179 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGlsl_newparam::create(DAE& dae) +{ + domGlsl_newparamRef ref = new domGlsl_newparam(dae); + return ref; +} + + +daeMetaElement * +domGlsl_newparam::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "glsl_newparam" ); + meta->registerClass(domGlsl_newparam::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "annotate" ); + mea->setOffset( daeOffsetOf(domGlsl_newparam,elemAnnotate_array) ); + mea->setElementType( domFx_annotate_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "semantic" ); + mea->setOffset( daeOffsetOf(domGlsl_newparam,elemSemantic) ); + mea->setElementType( domGlsl_newparam::domSemantic::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "modifier" ); + mea->setOffset( daeOffsetOf(domGlsl_newparam,elemModifier) ); + mea->setElementType( domGlsl_newparam::domModifier::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 3, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "glsl_param_type" ); + mea->setOffset( daeOffsetOf(domGlsl_newparam,elemGlsl_param_type) ); + mea->setElementType( domGlsl_param_type::registerElement(dae) ); + cm->appendChild( new daeMetaGroup( mea, meta, cm, 0, 1, 1 ) ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "array" ); + mea->setOffset( daeOffsetOf(domGlsl_newparam,elemArray) ); + mea->setElementType( domGlsl_newarray_type::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm->setMaxOrdinal( 3 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domGlsl_newparam,_contents)); + meta->addContentsOrder(daeOffsetOf(domGlsl_newparam,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domGlsl_newparam,_CMData), 1); + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("Glsl_identifier")); + ma->setOffset( daeOffsetOf( domGlsl_newparam , attrSid )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGlsl_newparam)); + meta->validate(); + + return meta; +} + +daeElementRef +domGlsl_newparam::domSemantic::create(DAE& dae) +{ + domGlsl_newparam::domSemanticRef ref = new domGlsl_newparam::domSemantic(dae); + return ref; +} + + +daeMetaElement * +domGlsl_newparam::domSemantic::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "semantic" ); + meta->registerClass(domGlsl_newparam::domSemantic::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGlsl_newparam::domSemantic , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGlsl_newparam::domSemantic)); + meta->validate(); + + return meta; +} + +daeElementRef +domGlsl_newparam::domModifier::create(DAE& dae) +{ + domGlsl_newparam::domModifierRef ref = new domGlsl_newparam::domModifier(dae); + return ref; +} + + +daeMetaElement * +domGlsl_newparam::domModifier::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "modifier" ); + meta->registerClass(domGlsl_newparam::domModifier::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_modifier_enum_common")); + ma->setOffset( daeOffsetOf( domGlsl_newparam::domModifier , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGlsl_newparam::domModifier)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGlsl_param_type.cpp b/Engine/lib/collada/src/1.4/dom/domGlsl_param_type.cpp new file mode 100644 index 000000000..be249ad93 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGlsl_param_type.cpp @@ -0,0 +1,774 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGlsl_param_type::create(DAE& dae) +{ + domGlsl_param_typeRef ref = new domGlsl_param_type(dae); + return ref; +} + + +daeMetaElement * +domGlsl_param_type::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "glsl_param_type" ); + meta->registerClass(domGlsl_param_type::create); + + meta->setIsTransparent( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool" ); + mea->setOffset( daeOffsetOf(domGlsl_param_type,elemBool) ); + mea->setElementType( domGlsl_param_type::domBool::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool2" ); + mea->setOffset( daeOffsetOf(domGlsl_param_type,elemBool2) ); + mea->setElementType( domGlsl_param_type::domBool2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool3" ); + mea->setOffset( daeOffsetOf(domGlsl_param_type,elemBool3) ); + mea->setElementType( domGlsl_param_type::domBool3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool4" ); + mea->setOffset( daeOffsetOf(domGlsl_param_type,elemBool4) ); + mea->setElementType( domGlsl_param_type::domBool4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float" ); + mea->setOffset( daeOffsetOf(domGlsl_param_type,elemFloat) ); + mea->setElementType( domGlsl_param_type::domFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float2" ); + mea->setOffset( daeOffsetOf(domGlsl_param_type,elemFloat2) ); + mea->setElementType( domGlsl_param_type::domFloat2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float3" ); + mea->setOffset( daeOffsetOf(domGlsl_param_type,elemFloat3) ); + mea->setElementType( domGlsl_param_type::domFloat3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float4" ); + mea->setOffset( daeOffsetOf(domGlsl_param_type,elemFloat4) ); + mea->setElementType( domGlsl_param_type::domFloat4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float2x2" ); + mea->setOffset( daeOffsetOf(domGlsl_param_type,elemFloat2x2) ); + mea->setElementType( domGlsl_param_type::domFloat2x2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float3x3" ); + mea->setOffset( daeOffsetOf(domGlsl_param_type,elemFloat3x3) ); + mea->setElementType( domGlsl_param_type::domFloat3x3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float4x4" ); + mea->setOffset( daeOffsetOf(domGlsl_param_type,elemFloat4x4) ); + mea->setElementType( domGlsl_param_type::domFloat4x4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int" ); + mea->setOffset( daeOffsetOf(domGlsl_param_type,elemInt) ); + mea->setElementType( domGlsl_param_type::domInt::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int2" ); + mea->setOffset( daeOffsetOf(domGlsl_param_type,elemInt2) ); + mea->setElementType( domGlsl_param_type::domInt2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int3" ); + mea->setOffset( daeOffsetOf(domGlsl_param_type,elemInt3) ); + mea->setElementType( domGlsl_param_type::domInt3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int4" ); + mea->setOffset( daeOffsetOf(domGlsl_param_type,elemInt4) ); + mea->setElementType( domGlsl_param_type::domInt4::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "surface" ); + mea->setOffset( daeOffsetOf(domGlsl_param_type,elemSurface) ); + mea->setElementType( domGlsl_surface_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "sampler1D" ); + mea->setOffset( daeOffsetOf(domGlsl_param_type,elemSampler1D) ); + mea->setElementType( domGl_sampler1D::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "sampler2D" ); + mea->setOffset( daeOffsetOf(domGlsl_param_type,elemSampler2D) ); + mea->setElementType( domGl_sampler2D::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "sampler3D" ); + mea->setOffset( daeOffsetOf(domGlsl_param_type,elemSampler3D) ); + mea->setElementType( domGl_sampler3D::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "samplerCUBE" ); + mea->setOffset( daeOffsetOf(domGlsl_param_type,elemSamplerCUBE) ); + mea->setElementType( domGl_samplerCUBE::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "samplerRECT" ); + mea->setOffset( daeOffsetOf(domGlsl_param_type,elemSamplerRECT) ); + mea->setElementType( domGl_samplerRECT::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "samplerDEPTH" ); + mea->setOffset( daeOffsetOf(domGlsl_param_type,elemSamplerDEPTH) ); + mea->setElementType( domGl_samplerDEPTH::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "enum" ); + mea->setOffset( daeOffsetOf(domGlsl_param_type,elemEnum) ); + mea->setElementType( domGlsl_param_type::domEnum::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domGlsl_param_type,_contents)); + meta->addContentsOrder(daeOffsetOf(domGlsl_param_type,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domGlsl_param_type,_CMData), 1); + meta->setElementSize(sizeof(domGlsl_param_type)); + meta->validate(); + + return meta; +} + +daeElementRef +domGlsl_param_type::domBool::create(DAE& dae) +{ + domGlsl_param_type::domBoolRef ref = new domGlsl_param_type::domBool(dae); + return ref; +} + + +daeMetaElement * +domGlsl_param_type::domBool::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool" ); + meta->registerClass(domGlsl_param_type::domBool::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Glsl_bool")); + ma->setOffset( daeOffsetOf( domGlsl_param_type::domBool , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGlsl_param_type::domBool)); + meta->validate(); + + return meta; +} + +daeElementRef +domGlsl_param_type::domBool2::create(DAE& dae) +{ + domGlsl_param_type::domBool2Ref ref = new domGlsl_param_type::domBool2(dae); + return ref; +} + + +daeMetaElement * +domGlsl_param_type::domBool2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool2" ); + meta->registerClass(domGlsl_param_type::domBool2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Glsl_bool2")); + ma->setOffset( daeOffsetOf( domGlsl_param_type::domBool2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGlsl_param_type::domBool2)); + meta->validate(); + + return meta; +} + +daeElementRef +domGlsl_param_type::domBool3::create(DAE& dae) +{ + domGlsl_param_type::domBool3Ref ref = new domGlsl_param_type::domBool3(dae); + return ref; +} + + +daeMetaElement * +domGlsl_param_type::domBool3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool3" ); + meta->registerClass(domGlsl_param_type::domBool3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Glsl_bool3")); + ma->setOffset( daeOffsetOf( domGlsl_param_type::domBool3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGlsl_param_type::domBool3)); + meta->validate(); + + return meta; +} + +daeElementRef +domGlsl_param_type::domBool4::create(DAE& dae) +{ + domGlsl_param_type::domBool4Ref ref = new domGlsl_param_type::domBool4(dae); + return ref; +} + + +daeMetaElement * +domGlsl_param_type::domBool4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bool4" ); + meta->registerClass(domGlsl_param_type::domBool4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Glsl_bool4")); + ma->setOffset( daeOffsetOf( domGlsl_param_type::domBool4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGlsl_param_type::domBool4)); + meta->validate(); + + return meta; +} + +daeElementRef +domGlsl_param_type::domFloat::create(DAE& dae) +{ + domGlsl_param_type::domFloatRef ref = new domGlsl_param_type::domFloat(dae); + return ref; +} + + +daeMetaElement * +domGlsl_param_type::domFloat::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float" ); + meta->registerClass(domGlsl_param_type::domFloat::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Glsl_float")); + ma->setOffset( daeOffsetOf( domGlsl_param_type::domFloat , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGlsl_param_type::domFloat)); + meta->validate(); + + return meta; +} + +daeElementRef +domGlsl_param_type::domFloat2::create(DAE& dae) +{ + domGlsl_param_type::domFloat2Ref ref = new domGlsl_param_type::domFloat2(dae); + return ref; +} + + +daeMetaElement * +domGlsl_param_type::domFloat2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float2" ); + meta->registerClass(domGlsl_param_type::domFloat2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Glsl_float2")); + ma->setOffset( daeOffsetOf( domGlsl_param_type::domFloat2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGlsl_param_type::domFloat2)); + meta->validate(); + + return meta; +} + +daeElementRef +domGlsl_param_type::domFloat3::create(DAE& dae) +{ + domGlsl_param_type::domFloat3Ref ref = new domGlsl_param_type::domFloat3(dae); + return ref; +} + + +daeMetaElement * +domGlsl_param_type::domFloat3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float3" ); + meta->registerClass(domGlsl_param_type::domFloat3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Glsl_float3")); + ma->setOffset( daeOffsetOf( domGlsl_param_type::domFloat3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGlsl_param_type::domFloat3)); + meta->validate(); + + return meta; +} + +daeElementRef +domGlsl_param_type::domFloat4::create(DAE& dae) +{ + domGlsl_param_type::domFloat4Ref ref = new domGlsl_param_type::domFloat4(dae); + return ref; +} + + +daeMetaElement * +domGlsl_param_type::domFloat4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float4" ); + meta->registerClass(domGlsl_param_type::domFloat4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Glsl_float4")); + ma->setOffset( daeOffsetOf( domGlsl_param_type::domFloat4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGlsl_param_type::domFloat4)); + meta->validate(); + + return meta; +} + +daeElementRef +domGlsl_param_type::domFloat2x2::create(DAE& dae) +{ + domGlsl_param_type::domFloat2x2Ref ref = new domGlsl_param_type::domFloat2x2(dae); + return ref; +} + + +daeMetaElement * +domGlsl_param_type::domFloat2x2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float2x2" ); + meta->registerClass(domGlsl_param_type::domFloat2x2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Glsl_float2x2")); + ma->setOffset( daeOffsetOf( domGlsl_param_type::domFloat2x2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGlsl_param_type::domFloat2x2)); + meta->validate(); + + return meta; +} + +daeElementRef +domGlsl_param_type::domFloat3x3::create(DAE& dae) +{ + domGlsl_param_type::domFloat3x3Ref ref = new domGlsl_param_type::domFloat3x3(dae); + return ref; +} + + +daeMetaElement * +domGlsl_param_type::domFloat3x3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float3x3" ); + meta->registerClass(domGlsl_param_type::domFloat3x3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Glsl_float3x3")); + ma->setOffset( daeOffsetOf( domGlsl_param_type::domFloat3x3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGlsl_param_type::domFloat3x3)); + meta->validate(); + + return meta; +} + +daeElementRef +domGlsl_param_type::domFloat4x4::create(DAE& dae) +{ + domGlsl_param_type::domFloat4x4Ref ref = new domGlsl_param_type::domFloat4x4(dae); + return ref; +} + + +daeMetaElement * +domGlsl_param_type::domFloat4x4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "float4x4" ); + meta->registerClass(domGlsl_param_type::domFloat4x4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Glsl_float4x4")); + ma->setOffset( daeOffsetOf( domGlsl_param_type::domFloat4x4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGlsl_param_type::domFloat4x4)); + meta->validate(); + + return meta; +} + +daeElementRef +domGlsl_param_type::domInt::create(DAE& dae) +{ + domGlsl_param_type::domIntRef ref = new domGlsl_param_type::domInt(dae); + return ref; +} + + +daeMetaElement * +domGlsl_param_type::domInt::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int" ); + meta->registerClass(domGlsl_param_type::domInt::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Glsl_int")); + ma->setOffset( daeOffsetOf( domGlsl_param_type::domInt , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGlsl_param_type::domInt)); + meta->validate(); + + return meta; +} + +daeElementRef +domGlsl_param_type::domInt2::create(DAE& dae) +{ + domGlsl_param_type::domInt2Ref ref = new domGlsl_param_type::domInt2(dae); + return ref; +} + + +daeMetaElement * +domGlsl_param_type::domInt2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int2" ); + meta->registerClass(domGlsl_param_type::domInt2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Glsl_int2")); + ma->setOffset( daeOffsetOf( domGlsl_param_type::domInt2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGlsl_param_type::domInt2)); + meta->validate(); + + return meta; +} + +daeElementRef +domGlsl_param_type::domInt3::create(DAE& dae) +{ + domGlsl_param_type::domInt3Ref ref = new domGlsl_param_type::domInt3(dae); + return ref; +} + + +daeMetaElement * +domGlsl_param_type::domInt3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int3" ); + meta->registerClass(domGlsl_param_type::domInt3::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Glsl_int3")); + ma->setOffset( daeOffsetOf( domGlsl_param_type::domInt3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGlsl_param_type::domInt3)); + meta->validate(); + + return meta; +} + +daeElementRef +domGlsl_param_type::domInt4::create(DAE& dae) +{ + domGlsl_param_type::domInt4Ref ref = new domGlsl_param_type::domInt4(dae); + return ref; +} + + +daeMetaElement * +domGlsl_param_type::domInt4::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int4" ); + meta->registerClass(domGlsl_param_type::domInt4::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Glsl_int4")); + ma->setOffset( daeOffsetOf( domGlsl_param_type::domInt4 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGlsl_param_type::domInt4)); + meta->validate(); + + return meta; +} + +daeElementRef +domGlsl_param_type::domEnum::create(DAE& dae) +{ + domGlsl_param_type::domEnumRef ref = new domGlsl_param_type::domEnum(dae); + return ref; +} + + +daeMetaElement * +domGlsl_param_type::domEnum::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "enum" ); + meta->registerClass(domGlsl_param_type::domEnum::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Gl_enumeration")); + ma->setOffset( daeOffsetOf( domGlsl_param_type::domEnum , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGlsl_param_type::domEnum)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGlsl_setarray_type.cpp b/Engine/lib/collada/src/1.4/dom/domGlsl_setarray_type.cpp new file mode 100644 index 000000000..54f4f38df --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGlsl_setarray_type.cpp @@ -0,0 +1,83 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGlsl_setarray_type::create(DAE& dae) +{ + domGlsl_setarray_typeRef ref = new domGlsl_setarray_type(dae); + return ref; +} + + +daeMetaElement * +domGlsl_setarray_type::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "glsl_setarray_type" ); + meta->registerClass(domGlsl_setarray_type::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 0, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "glsl_param_type" ); + mea->setOffset( daeOffsetOf(domGlsl_setarray_type,elemGlsl_param_type_array) ); + mea->setElementType( domGlsl_param_type::registerElement(dae) ); + cm->appendChild( new daeMetaGroup( mea, meta, cm, 0, 1, 1 ) ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "array" ); + mea->setOffset( daeOffsetOf(domGlsl_setarray_type,elemArray_array) ); + mea->setElementType( domGlsl_setarray_type::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3000 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domGlsl_setarray_type,_contents)); + meta->addContentsOrder(daeOffsetOf(domGlsl_setarray_type,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domGlsl_setarray_type,_CMData), 1); + // Add attribute: length + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "length" ); + ma->setType( dae.getAtomicTypes().get("xsPositiveInteger")); + ma->setOffset( daeOffsetOf( domGlsl_setarray_type , attrLength )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGlsl_setarray_type)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGlsl_setparam.cpp b/Engine/lib/collada/src/1.4/dom/domGlsl_setparam.cpp new file mode 100644 index 000000000..03093fc2d --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGlsl_setparam.cpp @@ -0,0 +1,106 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGlsl_setparam::create(DAE& dae) +{ + domGlsl_setparamRef ref = new domGlsl_setparam(dae); + return ref; +} + + +daeMetaElement * +domGlsl_setparam::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "glsl_setparam" ); + meta->registerClass(domGlsl_setparam::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "annotate" ); + mea->setOffset( daeOffsetOf(domGlsl_setparam,elemAnnotate_array) ); + mea->setElementType( domFx_annotate_common::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 1, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "glsl_param_type" ); + mea->setOffset( daeOffsetOf(domGlsl_setparam,elemGlsl_param_type) ); + mea->setElementType( domGlsl_param_type::registerElement(dae) ); + cm->appendChild( new daeMetaGroup( mea, meta, cm, 0, 1, 1 ) ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "array" ); + mea->setOffset( daeOffsetOf(domGlsl_setparam,elemArray) ); + mea->setElementType( domGlsl_setarray_type::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domGlsl_setparam,_contents)); + meta->addContentsOrder(daeOffsetOf(domGlsl_setparam,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domGlsl_setparam,_CMData), 1); + // Add attribute: ref + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "ref" ); + ma->setType( dae.getAtomicTypes().get("Glsl_identifier")); + ma->setOffset( daeOffsetOf( domGlsl_setparam , attrRef )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: program + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "program" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGlsl_setparam , attrProgram )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGlsl_setparam)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGlsl_setparam_simple.cpp b/Engine/lib/collada/src/1.4/dom/domGlsl_setparam_simple.cpp new file mode 100644 index 000000000..f55bf6ca5 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGlsl_setparam_simple.cpp @@ -0,0 +1,79 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGlsl_setparam_simple::create(DAE& dae) +{ + domGlsl_setparam_simpleRef ref = new domGlsl_setparam_simple(dae); + return ref; +} + + +daeMetaElement * +domGlsl_setparam_simple::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "glsl_setparam_simple" ); + meta->registerClass(domGlsl_setparam_simple::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "annotate" ); + mea->setOffset( daeOffsetOf(domGlsl_setparam_simple,elemAnnotate_array) ); + mea->setElementType( domFx_annotate_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "glsl_param_type" ); + mea->setOffset( daeOffsetOf(domGlsl_setparam_simple,elemGlsl_param_type) ); + mea->setElementType( domGlsl_param_type::registerElement(dae) ); + cm->appendChild( new daeMetaGroup( mea, meta, cm, 1, 1, 1 ) ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + // Add attribute: ref + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "ref" ); + ma->setType( dae.getAtomicTypes().get("Glsl_identifier")); + ma->setOffset( daeOffsetOf( domGlsl_setparam_simple , attrRef )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGlsl_setparam_simple)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domGlsl_surface_type.cpp b/Engine/lib/collada/src/1.4/dom/domGlsl_surface_type.cpp new file mode 100644 index 000000000..23f24e94c --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domGlsl_surface_type.cpp @@ -0,0 +1,264 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domGlsl_surface_type::create(DAE& dae) +{ + domGlsl_surface_typeRef ref = new domGlsl_surface_type(dae); + return ref; +} + + +daeMetaElement * +domGlsl_surface_type::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "glsl_surface_type" ); + meta->registerClass(domGlsl_surface_type::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "fx_surface_init_common" ); + mea->setOffset( daeOffsetOf(domGlsl_surface_type,elemFx_surface_init_common) ); + mea->setElementType( domFx_surface_init_common::registerElement(dae) ); + cm->appendChild( new daeMetaGroup( mea, meta, cm, 0, 0, 1 ) ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "format" ); + mea->setOffset( daeOffsetOf(domGlsl_surface_type,elemFormat) ); + mea->setElementType( domFormat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "format_hint" ); + mea->setOffset( daeOffsetOf(domGlsl_surface_type,elemFormat_hint) ); + mea->setElementType( domFx_surface_format_hint_common::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 3, 0, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "size" ); + mea->setOffset( daeOffsetOf(domGlsl_surface_type,elemSize) ); + mea->setElementType( domSize::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "viewport_ratio" ); + mea->setOffset( daeOffsetOf(domGlsl_surface_type,elemViewport_ratio) ); + mea->setElementType( domViewport_ratio::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "mip_levels" ); + mea->setOffset( daeOffsetOf(domGlsl_surface_type,elemMip_levels) ); + mea->setElementType( domMip_levels::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 5, 0, 1 ); + mea->setName( "mipmap_generate" ); + mea->setOffset( daeOffsetOf(domGlsl_surface_type,elemMipmap_generate) ); + mea->setElementType( domMipmap_generate::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 6, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domGlsl_surface_type,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 6 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm = new daeMetaSequence( meta, cm, 7, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "generator" ); + mea->setOffset( daeOffsetOf(domGlsl_surface_type,elemGenerator) ); + mea->setElementType( domGenerator::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm->setMaxOrdinal( 7 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domGlsl_surface_type,_contents)); + meta->addContentsOrder(daeOffsetOf(domGlsl_surface_type,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domGlsl_surface_type,_CMData), 1); + // Add attribute: type + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "type" ); + ma->setType( dae.getAtomicTypes().get("Fx_surface_type_enum")); + ma->setOffset( daeOffsetOf( domGlsl_surface_type , attrType )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGlsl_surface_type)); + meta->validate(); + + return meta; +} + +daeElementRef +domGlsl_surface_type::domGenerator::create(DAE& dae) +{ + domGlsl_surface_type::domGeneratorRef ref = new domGlsl_surface_type::domGenerator(dae); + return ref; +} + + +daeMetaElement * +domGlsl_surface_type::domGenerator::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "generator" ); + meta->registerClass(domGlsl_surface_type::domGenerator::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "annotate" ); + mea->setOffset( daeOffsetOf(domGlsl_surface_type::domGenerator,elemAnnotate_array) ); + mea->setElementType( domFx_annotate_common::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 1, 1, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "code" ); + mea->setOffset( daeOffsetOf(domGlsl_surface_type::domGenerator,elemCode_array) ); + mea->setElementType( domFx_code_profile::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "include" ); + mea->setOffset( daeOffsetOf(domGlsl_surface_type::domGenerator,elemInclude_array) ); + mea->setElementType( domFx_include_common::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementAttribute( meta, cm, 3002, 1, 1 ); + mea->setName( "name" ); + mea->setOffset( daeOffsetOf(domGlsl_surface_type::domGenerator,elemName) ); + mea->setElementType( domGlsl_surface_type::domGenerator::domName::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3003, 0, -1 ); + mea->setName( "setparam" ); + mea->setOffset( daeOffsetOf(domGlsl_surface_type::domGenerator,elemSetparam_array) ); + mea->setElementType( domGlsl_setparam_simple::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3003 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domGlsl_surface_type::domGenerator,_contents)); + meta->addContentsOrder(daeOffsetOf(domGlsl_surface_type::domGenerator,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domGlsl_surface_type::domGenerator,_CMData), 1); + meta->setElementSize(sizeof(domGlsl_surface_type::domGenerator)); + meta->validate(); + + return meta; +} + +daeElementRef +domGlsl_surface_type::domGenerator::domName::create(DAE& dae) +{ + domGlsl_surface_type::domGenerator::domNameRef ref = new domGlsl_surface_type::domGenerator::domName(dae); + return ref; +} + + +daeMetaElement * +domGlsl_surface_type::domGenerator::domName::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "name" ); + meta->registerClass(domGlsl_surface_type::domGenerator::domName::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGlsl_surface_type::domGenerator::domName , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: source + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "source" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domGlsl_surface_type::domGenerator::domName , attrSource )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domGlsl_surface_type::domGenerator::domName)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domIDREF_array.cpp b/Engine/lib/collada/src/1.4/dom/domIDREF_array.cpp new file mode 100644 index 000000000..2159e2e46 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domIDREF_array.cpp @@ -0,0 +1,92 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domIDREF_array::create(DAE& dae) +{ + domIDREF_arrayRef ref = new domIDREF_array(dae); + return ref; +} + + +daeMetaElement * +domIDREF_array::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "IDREF_array" ); + meta->registerClass(domIDREF_array::create); + + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsIDREFS")); + ma->setOffset( daeOffsetOf( domIDREF_array , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domIDREF_array , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domIDREF_array , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: count + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "count" ); + ma->setType( dae.getAtomicTypes().get("Uint")); + ma->setOffset( daeOffsetOf( domIDREF_array , attrCount )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domIDREF_array)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domImage.cpp b/Engine/lib/collada/src/1.4/dom/domImage.cpp new file mode 100644 index 000000000..632b5bf71 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domImage.cpp @@ -0,0 +1,228 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domImage::create(DAE& dae) +{ + domImageRef ref = new domImage(dae); + return ref; +} + + +daeMetaElement * +domImage::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "image" ); + meta->registerClass(domImage::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domImage,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 1, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "data" ); + mea->setOffset( daeOffsetOf(domImage,elemData) ); + mea->setElementType( domImage::domData::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "init_from" ); + mea->setOffset( daeOffsetOf(domImage,elemInit_from) ); + mea->setElementType( domImage::domInit_from::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domImage,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domImage,_contents)); + meta->addContentsOrder(daeOffsetOf(domImage,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domImage,_CMData), 1); + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domImage , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domImage , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: format + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "format" ); + ma->setType( dae.getAtomicTypes().get("xsToken")); + ma->setOffset( daeOffsetOf( domImage , attrFormat )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: height + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "height" ); + ma->setType( dae.getAtomicTypes().get("Uint")); + ma->setOffset( daeOffsetOf( domImage , attrHeight )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: width + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "width" ); + ma->setType( dae.getAtomicTypes().get("Uint")); + ma->setOffset( daeOffsetOf( domImage , attrWidth )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: depth + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "depth" ); + ma->setType( dae.getAtomicTypes().get("Uint")); + ma->setOffset( daeOffsetOf( domImage , attrDepth )); + ma->setContainer( meta ); + ma->setDefaultString( "1"); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domImage)); + meta->validate(); + + return meta; +} + +daeElementRef +domImage::domData::create(DAE& dae) +{ + domImage::domDataRef ref = new domImage::domData(dae); + return ref; +} + + +daeMetaElement * +domImage::domData::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "data" ); + meta->registerClass(domImage::domData::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("ListOfHexBinary")); + ma->setOffset( daeOffsetOf( domImage::domData , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domImage::domData)); + meta->validate(); + + return meta; +} + +daeElementRef +domImage::domInit_from::create(DAE& dae) +{ + domImage::domInit_fromRef ref = new domImage::domInit_from(dae); + return ref; +} + + +daeMetaElement * +domImage::domInit_from::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "init_from" ); + meta->registerClass(domImage::domInit_from::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domImage::domInit_from , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domImage::domInit_from)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domInputGlobal.cpp b/Engine/lib/collada/src/1.4/dom/domInputGlobal.cpp new file mode 100644 index 000000000..704105bf1 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domInputGlobal.cpp @@ -0,0 +1,73 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domInputGlobal::create(DAE& dae) +{ + domInputGlobalRef ref = new domInputGlobal(dae); + return ref; +} + + +daeMetaElement * +domInputGlobal::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "InputGlobal" ); + meta->registerClass(domInputGlobal::create); + + + // Add attribute: semantic + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "semantic" ); + ma->setType( dae.getAtomicTypes().get("xsNMTOKEN")); + ma->setOffset( daeOffsetOf( domInputGlobal , attrSemantic )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: source + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "source" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domInputGlobal , attrSource )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domInputGlobal)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domInputLocal.cpp b/Engine/lib/collada/src/1.4/dom/domInputLocal.cpp new file mode 100644 index 000000000..375a8cc5e --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domInputLocal.cpp @@ -0,0 +1,73 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domInputLocal::create(DAE& dae) +{ + domInputLocalRef ref = new domInputLocal(dae); + return ref; +} + + +daeMetaElement * +domInputLocal::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "InputLocal" ); + meta->registerClass(domInputLocal::create); + + + // Add attribute: semantic + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "semantic" ); + ma->setType( dae.getAtomicTypes().get("xsNMTOKEN")); + ma->setOffset( daeOffsetOf( domInputLocal , attrSemantic )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: source + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "source" ); + ma->setType( dae.getAtomicTypes().get("URIFragmentType")); + ma->setOffset( daeOffsetOf( domInputLocal , attrSource )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domInputLocal)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domInputLocalOffset.cpp b/Engine/lib/collada/src/1.4/dom/domInputLocalOffset.cpp new file mode 100644 index 000000000..e61e721fb --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domInputLocalOffset.cpp @@ -0,0 +1,96 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domInputLocalOffset::create(DAE& dae) +{ + domInputLocalOffsetRef ref = new domInputLocalOffset(dae); + return ref; +} + + +daeMetaElement * +domInputLocalOffset::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "InputLocalOffset" ); + meta->registerClass(domInputLocalOffset::create); + + + // Add attribute: offset + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "offset" ); + ma->setType( dae.getAtomicTypes().get("Uint")); + ma->setOffset( daeOffsetOf( domInputLocalOffset , attrOffset )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: semantic + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "semantic" ); + ma->setType( dae.getAtomicTypes().get("xsNMTOKEN")); + ma->setOffset( daeOffsetOf( domInputLocalOffset , attrSemantic )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: source + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "source" ); + ma->setType( dae.getAtomicTypes().get("URIFragmentType")); + ma->setOffset( daeOffsetOf( domInputLocalOffset , attrSource )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: set + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "set" ); + ma->setType( dae.getAtomicTypes().get("Uint")); + ma->setOffset( daeOffsetOf( domInputLocalOffset , attrSet )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domInputLocalOffset)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domInstanceWithExtra.cpp b/Engine/lib/collada/src/1.4/dom/domInstanceWithExtra.cpp new file mode 100644 index 000000000..9fcd4d7a1 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domInstanceWithExtra.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domInstanceWithExtra::create(DAE& dae) +{ + domInstanceWithExtraRef ref = new domInstanceWithExtra(dae); + return ref; +} + + +daeMetaElement * +domInstanceWithExtra::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "InstanceWithExtra" ); + meta->registerClass(domInstanceWithExtra::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domInstanceWithExtra,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + + // Add attribute: url + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "url" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domInstanceWithExtra , attrUrl )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstanceWithExtra , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstanceWithExtra , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domInstanceWithExtra)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domInstance_camera.cpp b/Engine/lib/collada/src/1.4/dom/domInstance_camera.cpp new file mode 100644 index 000000000..b7b4f1360 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domInstance_camera.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domInstance_camera::create(DAE& dae) +{ + domInstance_cameraRef ref = new domInstance_camera(dae); + return ref; +} + + +daeMetaElement * +domInstance_camera::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "instance_camera" ); + meta->registerClass(domInstance_camera::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domInstance_camera,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + + // Add attribute: url + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "url" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domInstance_camera , attrUrl )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_camera , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_camera , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domInstance_camera)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domInstance_controller.cpp b/Engine/lib/collada/src/1.4/dom/domInstance_controller.cpp new file mode 100644 index 000000000..8d40f90a8 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domInstance_controller.cpp @@ -0,0 +1,143 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domInstance_controller::create(DAE& dae) +{ + domInstance_controllerRef ref = new domInstance_controller(dae); + return ref; +} + + +daeMetaElement * +domInstance_controller::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "instance_controller" ); + meta->registerClass(domInstance_controller::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "skeleton" ); + mea->setOffset( daeOffsetOf(domInstance_controller,elemSkeleton_array) ); + mea->setElementType( domInstance_controller::domSkeleton::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "bind_material" ); + mea->setOffset( daeOffsetOf(domInstance_controller,elemBind_material) ); + mea->setElementType( domBind_material::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domInstance_controller,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: url + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "url" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domInstance_controller , attrUrl )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_controller , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_controller , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domInstance_controller)); + meta->validate(); + + return meta; +} + +daeElementRef +domInstance_controller::domSkeleton::create(DAE& dae) +{ + domInstance_controller::domSkeletonRef ref = new domInstance_controller::domSkeleton(dae); + return ref; +} + + +daeMetaElement * +domInstance_controller::domSkeleton::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "skeleton" ); + meta->registerClass(domInstance_controller::domSkeleton::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domInstance_controller::domSkeleton , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domInstance_controller::domSkeleton)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domInstance_effect.cpp b/Engine/lib/collada/src/1.4/dom/domInstance_effect.cpp new file mode 100644 index 000000000..d3c947f0a --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domInstance_effect.cpp @@ -0,0 +1,221 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domInstance_effect::create(DAE& dae) +{ + domInstance_effectRef ref = new domInstance_effect(dae); + return ref; +} + + +daeMetaElement * +domInstance_effect::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "instance_effect" ); + meta->registerClass(domInstance_effect::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "technique_hint" ); + mea->setOffset( daeOffsetOf(domInstance_effect,elemTechnique_hint_array) ); + mea->setElementType( domInstance_effect::domTechnique_hint::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 0, -1 ); + mea->setName( "setparam" ); + mea->setOffset( daeOffsetOf(domInstance_effect,elemSetparam_array) ); + mea->setElementType( domInstance_effect::domSetparam::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domInstance_effect,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: url + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "url" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domInstance_effect , attrUrl )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_effect , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_effect , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domInstance_effect)); + meta->validate(); + + return meta; +} + +daeElementRef +domInstance_effect::domTechnique_hint::create(DAE& dae) +{ + domInstance_effect::domTechnique_hintRef ref = new domInstance_effect::domTechnique_hint(dae); + return ref; +} + + +daeMetaElement * +domInstance_effect::domTechnique_hint::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "technique_hint" ); + meta->registerClass(domInstance_effect::domTechnique_hint::create); + + meta->setIsInnerClass( true ); + + // Add attribute: platform + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "platform" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_effect::domTechnique_hint , attrPlatform )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: profile + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "profile" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_effect::domTechnique_hint , attrProfile )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: ref + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "ref" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_effect::domTechnique_hint , attrRef )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domInstance_effect::domTechnique_hint)); + meta->validate(); + + return meta; +} + +daeElementRef +domInstance_effect::domSetparam::create(DAE& dae) +{ + domInstance_effect::domSetparamRef ref = new domInstance_effect::domSetparam(dae); + return ref; +} + + +daeMetaElement * +domInstance_effect::domSetparam::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "setparam" ); + meta->registerClass(domInstance_effect::domSetparam::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "fx_basic_type_common" ); + mea->setOffset( daeOffsetOf(domInstance_effect::domSetparam,elemFx_basic_type_common) ); + mea->setElementType( domFx_basic_type_common::registerElement(dae) ); + cm->appendChild( new daeMetaGroup( mea, meta, cm, 0, 1, 1 ) ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + + // Add attribute: ref + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "ref" ); + ma->setType( dae.getAtomicTypes().get("xsToken")); + ma->setOffset( daeOffsetOf( domInstance_effect::domSetparam , attrRef )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domInstance_effect::domSetparam)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domInstance_force_field.cpp b/Engine/lib/collada/src/1.4/dom/domInstance_force_field.cpp new file mode 100644 index 000000000..90e1c2580 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domInstance_force_field.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domInstance_force_field::create(DAE& dae) +{ + domInstance_force_fieldRef ref = new domInstance_force_field(dae); + return ref; +} + + +daeMetaElement * +domInstance_force_field::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "instance_force_field" ); + meta->registerClass(domInstance_force_field::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domInstance_force_field,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + + // Add attribute: url + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "url" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domInstance_force_field , attrUrl )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_force_field , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_force_field , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domInstance_force_field)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domInstance_geometry.cpp b/Engine/lib/collada/src/1.4/dom/domInstance_geometry.cpp new file mode 100644 index 000000000..21411e490 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domInstance_geometry.cpp @@ -0,0 +1,101 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domInstance_geometry::create(DAE& dae) +{ + domInstance_geometryRef ref = new domInstance_geometry(dae); + return ref; +} + + +daeMetaElement * +domInstance_geometry::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "instance_geometry" ); + meta->registerClass(domInstance_geometry::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "bind_material" ); + mea->setOffset( daeOffsetOf(domInstance_geometry,elemBind_material) ); + mea->setElementType( domBind_material::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domInstance_geometry,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + // Add attribute: url + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "url" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domInstance_geometry , attrUrl )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_geometry , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_geometry , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domInstance_geometry)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domInstance_light.cpp b/Engine/lib/collada/src/1.4/dom/domInstance_light.cpp new file mode 100644 index 000000000..b19065fbe --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domInstance_light.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domInstance_light::create(DAE& dae) +{ + domInstance_lightRef ref = new domInstance_light(dae); + return ref; +} + + +daeMetaElement * +domInstance_light::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "instance_light" ); + meta->registerClass(domInstance_light::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domInstance_light,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + + // Add attribute: url + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "url" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domInstance_light , attrUrl )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_light , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_light , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domInstance_light)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domInstance_material.cpp b/Engine/lib/collada/src/1.4/dom/domInstance_material.cpp new file mode 100644 index 000000000..29e9e9fd7 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domInstance_material.cpp @@ -0,0 +1,232 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domInstance_material::create(DAE& dae) +{ + domInstance_materialRef ref = new domInstance_material(dae); + return ref; +} + + +daeMetaElement * +domInstance_material::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "instance_material" ); + meta->registerClass(domInstance_material::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "bind" ); + mea->setOffset( daeOffsetOf(domInstance_material,elemBind_array) ); + mea->setElementType( domInstance_material::domBind::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 0, -1 ); + mea->setName( "bind_vertex_input" ); + mea->setOffset( daeOffsetOf(domInstance_material,elemBind_vertex_input_array) ); + mea->setElementType( domInstance_material::domBind_vertex_input::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domInstance_material,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: symbol + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "symbol" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_material , attrSymbol )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: target + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "target" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domInstance_material , attrTarget )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_material , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_material , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domInstance_material)); + meta->validate(); + + return meta; +} + +daeElementRef +domInstance_material::domBind::create(DAE& dae) +{ + domInstance_material::domBindRef ref = new domInstance_material::domBind(dae); + return ref; +} + + +daeMetaElement * +domInstance_material::domBind::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bind" ); + meta->registerClass(domInstance_material::domBind::create); + + meta->setIsInnerClass( true ); + + // Add attribute: semantic + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "semantic" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_material::domBind , attrSemantic )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: target + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "target" ); + ma->setType( dae.getAtomicTypes().get("xsToken")); + ma->setOffset( daeOffsetOf( domInstance_material::domBind , attrTarget )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domInstance_material::domBind)); + meta->validate(); + + return meta; +} + +daeElementRef +domInstance_material::domBind_vertex_input::create(DAE& dae) +{ + domInstance_material::domBind_vertex_inputRef ref = new domInstance_material::domBind_vertex_input(dae); + return ref; +} + + +daeMetaElement * +domInstance_material::domBind_vertex_input::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bind_vertex_input" ); + meta->registerClass(domInstance_material::domBind_vertex_input::create); + + meta->setIsInnerClass( true ); + + // Add attribute: semantic + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "semantic" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_material::domBind_vertex_input , attrSemantic )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: input_semantic + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "input_semantic" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_material::domBind_vertex_input , attrInput_semantic )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: input_set + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "input_set" ); + ma->setType( dae.getAtomicTypes().get("Uint")); + ma->setOffset( daeOffsetOf( domInstance_material::domBind_vertex_input , attrInput_set )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domInstance_material::domBind_vertex_input)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domInstance_node.cpp b/Engine/lib/collada/src/1.4/dom/domInstance_node.cpp new file mode 100644 index 000000000..c69f720c8 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domInstance_node.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domInstance_node::create(DAE& dae) +{ + domInstance_nodeRef ref = new domInstance_node(dae); + return ref; +} + + +daeMetaElement * +domInstance_node::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "instance_node" ); + meta->registerClass(domInstance_node::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domInstance_node,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + + // Add attribute: url + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "url" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domInstance_node , attrUrl )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_node , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_node , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domInstance_node)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domInstance_physics_material.cpp b/Engine/lib/collada/src/1.4/dom/domInstance_physics_material.cpp new file mode 100644 index 000000000..4b4e04cd6 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domInstance_physics_material.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domInstance_physics_material::create(DAE& dae) +{ + domInstance_physics_materialRef ref = new domInstance_physics_material(dae); + return ref; +} + + +daeMetaElement * +domInstance_physics_material::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "instance_physics_material" ); + meta->registerClass(domInstance_physics_material::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domInstance_physics_material,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + + // Add attribute: url + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "url" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domInstance_physics_material , attrUrl )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_physics_material , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_physics_material , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domInstance_physics_material)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domInstance_physics_model.cpp b/Engine/lib/collada/src/1.4/dom/domInstance_physics_model.cpp new file mode 100644 index 000000000..8bf86e176 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domInstance_physics_model.cpp @@ -0,0 +1,124 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domInstance_physics_model::create(DAE& dae) +{ + domInstance_physics_modelRef ref = new domInstance_physics_model(dae); + return ref; +} + + +daeMetaElement * +domInstance_physics_model::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "instance_physics_model" ); + meta->registerClass(domInstance_physics_model::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "instance_force_field" ); + mea->setOffset( daeOffsetOf(domInstance_physics_model,elemInstance_force_field_array) ); + mea->setElementType( domInstance_force_field::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 0, -1 ); + mea->setName( "instance_rigid_body" ); + mea->setOffset( daeOffsetOf(domInstance_physics_model,elemInstance_rigid_body_array) ); + mea->setElementType( domInstance_rigid_body::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "instance_rigid_constraint" ); + mea->setOffset( daeOffsetOf(domInstance_physics_model,elemInstance_rigid_constraint_array) ); + mea->setElementType( domInstance_rigid_constraint::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domInstance_physics_model,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3 ); + meta->setCMRoot( cm ); + + // Add attribute: url + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "url" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domInstance_physics_model , attrUrl )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_physics_model , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_physics_model , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: parent + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "parent" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domInstance_physics_model , attrParent )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domInstance_physics_model)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domInstance_rigid_body.cpp b/Engine/lib/collada/src/1.4/dom/domInstance_rigid_body.cpp new file mode 100644 index 000000000..b400a995d --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domInstance_rigid_body.cpp @@ -0,0 +1,582 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domInstance_rigid_body::create(DAE& dae) +{ + domInstance_rigid_bodyRef ref = new domInstance_rigid_body(dae); + return ref; +} + + +daeMetaElement * +domInstance_rigid_body::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "instance_rigid_body" ); + meta->registerClass(domInstance_rigid_body::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "technique_common" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body,elemTechnique_common) ); + mea->setElementType( domInstance_rigid_body::domTechnique_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 0, -1 ); + mea->setName( "technique" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body,elemTechnique_array) ); + mea->setElementType( domTechnique::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: body + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "body" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_rigid_body , attrBody )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_rigid_body , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_rigid_body , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: target + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "target" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domInstance_rigid_body , attrTarget )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domInstance_rigid_body)); + meta->validate(); + + return meta; +} + +daeElementRef +domInstance_rigid_body::domTechnique_common::create(DAE& dae) +{ + domInstance_rigid_body::domTechnique_commonRef ref = new domInstance_rigid_body::domTechnique_common(dae); + return ref; +} + + +daeMetaElement * +domInstance_rigid_body::domTechnique_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "technique_common" ); + meta->registerClass(domInstance_rigid_body::domTechnique_common::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "angular_velocity" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body::domTechnique_common,elemAngular_velocity) ); + mea->setElementType( domInstance_rigid_body::domTechnique_common::domAngular_velocity::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "velocity" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body::domTechnique_common,elemVelocity) ); + mea->setElementType( domInstance_rigid_body::domTechnique_common::domVelocity::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "dynamic" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body::domTechnique_common,elemDynamic) ); + mea->setElementType( domInstance_rigid_body::domTechnique_common::domDynamic::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "mass" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body::domTechnique_common,elemMass) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "mass_frame" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body::domTechnique_common,elemMass_frame) ); + mea->setElementType( domInstance_rigid_body::domTechnique_common::domMass_frame::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 5, 0, 1 ); + mea->setName( "inertia" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body::domTechnique_common,elemInertia) ); + mea->setElementType( domTargetableFloat3::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 6, 0, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "instance_physics_material" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body::domTechnique_common,elemInstance_physics_material) ); + mea->setElementType( domInstance_physics_material::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "physics_material" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body::domTechnique_common,elemPhysics_material) ); + mea->setElementType( domPhysics_material::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementArrayAttribute( meta, cm, 7, 0, -1 ); + mea->setName( "shape" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body::domTechnique_common,elemShape_array) ); + mea->setElementType( domInstance_rigid_body::domTechnique_common::domShape::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 7 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domInstance_rigid_body::domTechnique_common,_contents)); + meta->addContentsOrder(daeOffsetOf(domInstance_rigid_body::domTechnique_common,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domInstance_rigid_body::domTechnique_common,_CMData), 1); + meta->setElementSize(sizeof(domInstance_rigid_body::domTechnique_common)); + meta->validate(); + + return meta; +} + +daeElementRef +domInstance_rigid_body::domTechnique_common::domAngular_velocity::create(DAE& dae) +{ + domInstance_rigid_body::domTechnique_common::domAngular_velocityRef ref = new domInstance_rigid_body::domTechnique_common::domAngular_velocity(dae); + return ref; +} + + +daeMetaElement * +domInstance_rigid_body::domTechnique_common::domAngular_velocity::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "angular_velocity" ); + meta->registerClass(domInstance_rigid_body::domTechnique_common::domAngular_velocity::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float3")); + ma->setOffset( daeOffsetOf( domInstance_rigid_body::domTechnique_common::domAngular_velocity , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domInstance_rigid_body::domTechnique_common::domAngular_velocity)); + meta->validate(); + + return meta; +} + +daeElementRef +domInstance_rigid_body::domTechnique_common::domVelocity::create(DAE& dae) +{ + domInstance_rigid_body::domTechnique_common::domVelocityRef ref = new domInstance_rigid_body::domTechnique_common::domVelocity(dae); + return ref; +} + + +daeMetaElement * +domInstance_rigid_body::domTechnique_common::domVelocity::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "velocity" ); + meta->registerClass(domInstance_rigid_body::domTechnique_common::domVelocity::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float3")); + ma->setOffset( daeOffsetOf( domInstance_rigid_body::domTechnique_common::domVelocity , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domInstance_rigid_body::domTechnique_common::domVelocity)); + meta->validate(); + + return meta; +} + +daeElementRef +domInstance_rigid_body::domTechnique_common::domDynamic::create(DAE& dae) +{ + domInstance_rigid_body::domTechnique_common::domDynamicRef ref = new domInstance_rigid_body::domTechnique_common::domDynamic(dae); + return ref; +} + + +daeMetaElement * +domInstance_rigid_body::domTechnique_common::domDynamic::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "dynamic" ); + meta->registerClass(domInstance_rigid_body::domTechnique_common::domDynamic::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domInstance_rigid_body::domTechnique_common::domDynamic , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_rigid_body::domTechnique_common::domDynamic , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domInstance_rigid_body::domTechnique_common::domDynamic)); + meta->validate(); + + return meta; +} + +daeElementRef +domInstance_rigid_body::domTechnique_common::domMass_frame::create(DAE& dae) +{ + domInstance_rigid_body::domTechnique_common::domMass_frameRef ref = new domInstance_rigid_body::domTechnique_common::domMass_frame(dae); + return ref; +} + + +daeMetaElement * +domInstance_rigid_body::domTechnique_common::domMass_frame::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mass_frame" ); + meta->registerClass(domInstance_rigid_body::domTechnique_common::domMass_frame::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 1, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "translate" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body::domTechnique_common::domMass_frame,elemTranslate_array) ); + mea->setElementType( domTranslate::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "rotate" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body::domTechnique_common::domMass_frame,elemRotate_array) ); + mea->setElementType( domRotate::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3000 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domInstance_rigid_body::domTechnique_common::domMass_frame,_contents)); + meta->addContentsOrder(daeOffsetOf(domInstance_rigid_body::domTechnique_common::domMass_frame,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domInstance_rigid_body::domTechnique_common::domMass_frame,_CMData), 1); + meta->setElementSize(sizeof(domInstance_rigid_body::domTechnique_common::domMass_frame)); + meta->validate(); + + return meta; +} + +daeElementRef +domInstance_rigid_body::domTechnique_common::domShape::create(DAE& dae) +{ + domInstance_rigid_body::domTechnique_common::domShapeRef ref = new domInstance_rigid_body::domTechnique_common::domShape(dae); + return ref; +} + + +daeMetaElement * +domInstance_rigid_body::domTechnique_common::domShape::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "shape" ); + meta->registerClass(domInstance_rigid_body::domTechnique_common::domShape::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "hollow" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body::domTechnique_common::domShape,elemHollow) ); + mea->setElementType( domInstance_rigid_body::domTechnique_common::domShape::domHollow::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "mass" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body::domTechnique_common::domShape,elemMass) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "density" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body::domTechnique_common::domShape,elemDensity) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 3, 0, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "instance_physics_material" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body::domTechnique_common::domShape,elemInstance_physics_material) ); + mea->setElementType( domInstance_physics_material::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "physics_material" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body::domTechnique_common::domShape,elemPhysics_material) ); + mea->setElementType( domPhysics_material::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm = new daeMetaChoice( meta, cm, 1, 4, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "instance_geometry" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body::domTechnique_common::domShape,elemInstance_geometry) ); + mea->setElementType( domInstance_geometry::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "plane" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body::domTechnique_common::domShape,elemPlane) ); + mea->setElementType( domPlane::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "box" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body::domTechnique_common::domShape,elemBox) ); + mea->setElementType( domBox::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "sphere" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body::domTechnique_common::domShape,elemSphere) ); + mea->setElementType( domSphere::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "cylinder" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body::domTechnique_common::domShape,elemCylinder) ); + mea->setElementType( domCylinder::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "tapered_cylinder" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body::domTechnique_common::domShape,elemTapered_cylinder) ); + mea->setElementType( domTapered_cylinder::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "capsule" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body::domTechnique_common::domShape,elemCapsule) ); + mea->setElementType( domCapsule::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "tapered_capsule" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body::domTechnique_common::domShape,elemTapered_capsule) ); + mea->setElementType( domTapered_capsule::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm = new daeMetaChoice( meta, cm, 2, 5, 0, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "translate" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body::domTechnique_common::domShape,elemTranslate_array) ); + mea->setElementType( domTranslate::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "rotate" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body::domTechnique_common::domShape,elemRotate_array) ); + mea->setElementType( domRotate::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3006, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_body::domTechnique_common::domShape,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3006 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domInstance_rigid_body::domTechnique_common::domShape,_contents)); + meta->addContentsOrder(daeOffsetOf(domInstance_rigid_body::domTechnique_common::domShape,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domInstance_rigid_body::domTechnique_common::domShape,_CMData), 3); + meta->setElementSize(sizeof(domInstance_rigid_body::domTechnique_common::domShape)); + meta->validate(); + + return meta; +} + +daeElementRef +domInstance_rigid_body::domTechnique_common::domShape::domHollow::create(DAE& dae) +{ + domInstance_rigid_body::domTechnique_common::domShape::domHollowRef ref = new domInstance_rigid_body::domTechnique_common::domShape::domHollow(dae); + return ref; +} + + +daeMetaElement * +domInstance_rigid_body::domTechnique_common::domShape::domHollow::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "hollow" ); + meta->registerClass(domInstance_rigid_body::domTechnique_common::domShape::domHollow::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domInstance_rigid_body::domTechnique_common::domShape::domHollow , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_rigid_body::domTechnique_common::domShape::domHollow , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domInstance_rigid_body::domTechnique_common::domShape::domHollow)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domInstance_rigid_constraint.cpp b/Engine/lib/collada/src/1.4/dom/domInstance_rigid_constraint.cpp new file mode 100644 index 000000000..62aebe6c6 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domInstance_rigid_constraint.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domInstance_rigid_constraint::create(DAE& dae) +{ + domInstance_rigid_constraintRef ref = new domInstance_rigid_constraint(dae); + return ref; +} + + +daeMetaElement * +domInstance_rigid_constraint::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "instance_rigid_constraint" ); + meta->registerClass(domInstance_rigid_constraint::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domInstance_rigid_constraint,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + + // Add attribute: constraint + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "constraint" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_rigid_constraint , attrConstraint )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_rigid_constraint , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInstance_rigid_constraint , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domInstance_rigid_constraint)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domInt_array.cpp b/Engine/lib/collada/src/1.4/dom/domInt_array.cpp new file mode 100644 index 000000000..4257f0ed5 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domInt_array.cpp @@ -0,0 +1,116 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domInt_array::create(DAE& dae) +{ + domInt_arrayRef ref = new domInt_array(dae); + return ref; +} + + +daeMetaElement * +domInt_array::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "int_array" ); + meta->registerClass(domInt_array::create); + + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("ListOfInts")); + ma->setOffset( daeOffsetOf( domInt_array , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domInt_array , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domInt_array , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: count + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "count" ); + ma->setType( dae.getAtomicTypes().get("Uint")); + ma->setOffset( daeOffsetOf( domInt_array , attrCount )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: minInclusive + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "minInclusive" ); + ma->setType( dae.getAtomicTypes().get("xsInteger")); + ma->setOffset( daeOffsetOf( domInt_array , attrMinInclusive )); + ma->setContainer( meta ); + ma->setDefaultString( "-2147483648"); + + meta->appendAttribute(ma); + } + + // Add attribute: maxInclusive + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "maxInclusive" ); + ma->setType( dae.getAtomicTypes().get("xsInteger")); + ma->setOffset( daeOffsetOf( domInt_array , attrMaxInclusive )); + ma->setContainer( meta ); + ma->setDefaultString( "2147483647"); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domInt_array)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domLibrary_animation_clips.cpp b/Engine/lib/collada/src/1.4/dom/domLibrary_animation_clips.cpp new file mode 100644 index 000000000..e13b0d852 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domLibrary_animation_clips.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domLibrary_animation_clips::create(DAE& dae) +{ + domLibrary_animation_clipsRef ref = new domLibrary_animation_clips(dae); + return ref; +} + + +daeMetaElement * +domLibrary_animation_clips::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "library_animation_clips" ); + meta->registerClass(domLibrary_animation_clips::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domLibrary_animation_clips,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 1, -1 ); + mea->setName( "animation_clip" ); + mea->setOffset( daeOffsetOf(domLibrary_animation_clips,elemAnimation_clip_array) ); + mea->setElementType( domAnimation_clip::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domLibrary_animation_clips,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domLibrary_animation_clips , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domLibrary_animation_clips , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domLibrary_animation_clips)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domLibrary_animations.cpp b/Engine/lib/collada/src/1.4/dom/domLibrary_animations.cpp new file mode 100644 index 000000000..1daa1f138 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domLibrary_animations.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domLibrary_animations::create(DAE& dae) +{ + domLibrary_animationsRef ref = new domLibrary_animations(dae); + return ref; +} + + +daeMetaElement * +domLibrary_animations::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "library_animations" ); + meta->registerClass(domLibrary_animations::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domLibrary_animations,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 1, -1 ); + mea->setName( "animation" ); + mea->setOffset( daeOffsetOf(domLibrary_animations,elemAnimation_array) ); + mea->setElementType( domAnimation::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domLibrary_animations,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domLibrary_animations , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domLibrary_animations , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domLibrary_animations)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domLibrary_cameras.cpp b/Engine/lib/collada/src/1.4/dom/domLibrary_cameras.cpp new file mode 100644 index 000000000..0a2c12e0a --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domLibrary_cameras.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domLibrary_cameras::create(DAE& dae) +{ + domLibrary_camerasRef ref = new domLibrary_cameras(dae); + return ref; +} + + +daeMetaElement * +domLibrary_cameras::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "library_cameras" ); + meta->registerClass(domLibrary_cameras::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domLibrary_cameras,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 1, -1 ); + mea->setName( "camera" ); + mea->setOffset( daeOffsetOf(domLibrary_cameras,elemCamera_array) ); + mea->setElementType( domCamera::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domLibrary_cameras,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domLibrary_cameras , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domLibrary_cameras , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domLibrary_cameras)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domLibrary_controllers.cpp b/Engine/lib/collada/src/1.4/dom/domLibrary_controllers.cpp new file mode 100644 index 000000000..6f68afdad --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domLibrary_controllers.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domLibrary_controllers::create(DAE& dae) +{ + domLibrary_controllersRef ref = new domLibrary_controllers(dae); + return ref; +} + + +daeMetaElement * +domLibrary_controllers::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "library_controllers" ); + meta->registerClass(domLibrary_controllers::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domLibrary_controllers,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 1, -1 ); + mea->setName( "controller" ); + mea->setOffset( daeOffsetOf(domLibrary_controllers,elemController_array) ); + mea->setElementType( domController::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domLibrary_controllers,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domLibrary_controllers , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domLibrary_controllers , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domLibrary_controllers)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domLibrary_effects.cpp b/Engine/lib/collada/src/1.4/dom/domLibrary_effects.cpp new file mode 100644 index 000000000..a54b1b9c0 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domLibrary_effects.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domLibrary_effects::create(DAE& dae) +{ + domLibrary_effectsRef ref = new domLibrary_effects(dae); + return ref; +} + + +daeMetaElement * +domLibrary_effects::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "library_effects" ); + meta->registerClass(domLibrary_effects::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domLibrary_effects,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 1, -1 ); + mea->setName( "effect" ); + mea->setOffset( daeOffsetOf(domLibrary_effects,elemEffect_array) ); + mea->setElementType( domEffect::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domLibrary_effects,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domLibrary_effects , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domLibrary_effects , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domLibrary_effects)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domLibrary_force_fields.cpp b/Engine/lib/collada/src/1.4/dom/domLibrary_force_fields.cpp new file mode 100644 index 000000000..f0b60ea25 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domLibrary_force_fields.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domLibrary_force_fields::create(DAE& dae) +{ + domLibrary_force_fieldsRef ref = new domLibrary_force_fields(dae); + return ref; +} + + +daeMetaElement * +domLibrary_force_fields::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "library_force_fields" ); + meta->registerClass(domLibrary_force_fields::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domLibrary_force_fields,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 1, -1 ); + mea->setName( "force_field" ); + mea->setOffset( daeOffsetOf(domLibrary_force_fields,elemForce_field_array) ); + mea->setElementType( domForce_field::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domLibrary_force_fields,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domLibrary_force_fields , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domLibrary_force_fields , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domLibrary_force_fields)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domLibrary_geometries.cpp b/Engine/lib/collada/src/1.4/dom/domLibrary_geometries.cpp new file mode 100644 index 000000000..009f59c1f --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domLibrary_geometries.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domLibrary_geometries::create(DAE& dae) +{ + domLibrary_geometriesRef ref = new domLibrary_geometries(dae); + return ref; +} + + +daeMetaElement * +domLibrary_geometries::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "library_geometries" ); + meta->registerClass(domLibrary_geometries::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domLibrary_geometries,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 1, -1 ); + mea->setName( "geometry" ); + mea->setOffset( daeOffsetOf(domLibrary_geometries,elemGeometry_array) ); + mea->setElementType( domGeometry::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domLibrary_geometries,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domLibrary_geometries , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domLibrary_geometries , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domLibrary_geometries)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domLibrary_images.cpp b/Engine/lib/collada/src/1.4/dom/domLibrary_images.cpp new file mode 100644 index 000000000..b905743eb --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domLibrary_images.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domLibrary_images::create(DAE& dae) +{ + domLibrary_imagesRef ref = new domLibrary_images(dae); + return ref; +} + + +daeMetaElement * +domLibrary_images::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "library_images" ); + meta->registerClass(domLibrary_images::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domLibrary_images,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 1, -1 ); + mea->setName( "image" ); + mea->setOffset( daeOffsetOf(domLibrary_images,elemImage_array) ); + mea->setElementType( domImage::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domLibrary_images,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domLibrary_images , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domLibrary_images , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domLibrary_images)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domLibrary_lights.cpp b/Engine/lib/collada/src/1.4/dom/domLibrary_lights.cpp new file mode 100644 index 000000000..ce2bdf33d --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domLibrary_lights.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domLibrary_lights::create(DAE& dae) +{ + domLibrary_lightsRef ref = new domLibrary_lights(dae); + return ref; +} + + +daeMetaElement * +domLibrary_lights::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "library_lights" ); + meta->registerClass(domLibrary_lights::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domLibrary_lights,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 1, -1 ); + mea->setName( "light" ); + mea->setOffset( daeOffsetOf(domLibrary_lights,elemLight_array) ); + mea->setElementType( domLight::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domLibrary_lights,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domLibrary_lights , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domLibrary_lights , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domLibrary_lights)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domLibrary_materials.cpp b/Engine/lib/collada/src/1.4/dom/domLibrary_materials.cpp new file mode 100644 index 000000000..f235d9177 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domLibrary_materials.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domLibrary_materials::create(DAE& dae) +{ + domLibrary_materialsRef ref = new domLibrary_materials(dae); + return ref; +} + + +daeMetaElement * +domLibrary_materials::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "library_materials" ); + meta->registerClass(domLibrary_materials::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domLibrary_materials,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 1, -1 ); + mea->setName( "material" ); + mea->setOffset( daeOffsetOf(domLibrary_materials,elemMaterial_array) ); + mea->setElementType( domMaterial::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domLibrary_materials,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domLibrary_materials , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domLibrary_materials , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domLibrary_materials)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domLibrary_nodes.cpp b/Engine/lib/collada/src/1.4/dom/domLibrary_nodes.cpp new file mode 100644 index 000000000..3e755c9a4 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domLibrary_nodes.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domLibrary_nodes::create(DAE& dae) +{ + domLibrary_nodesRef ref = new domLibrary_nodes(dae); + return ref; +} + + +daeMetaElement * +domLibrary_nodes::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "library_nodes" ); + meta->registerClass(domLibrary_nodes::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domLibrary_nodes,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 1, -1 ); + mea->setName( "node" ); + mea->setOffset( daeOffsetOf(domLibrary_nodes,elemNode_array) ); + mea->setElementType( domNode::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domLibrary_nodes,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domLibrary_nodes , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domLibrary_nodes , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domLibrary_nodes)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domLibrary_physics_materials.cpp b/Engine/lib/collada/src/1.4/dom/domLibrary_physics_materials.cpp new file mode 100644 index 000000000..d7e5ba2f0 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domLibrary_physics_materials.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domLibrary_physics_materials::create(DAE& dae) +{ + domLibrary_physics_materialsRef ref = new domLibrary_physics_materials(dae); + return ref; +} + + +daeMetaElement * +domLibrary_physics_materials::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "library_physics_materials" ); + meta->registerClass(domLibrary_physics_materials::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domLibrary_physics_materials,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 1, -1 ); + mea->setName( "physics_material" ); + mea->setOffset( daeOffsetOf(domLibrary_physics_materials,elemPhysics_material_array) ); + mea->setElementType( domPhysics_material::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domLibrary_physics_materials,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domLibrary_physics_materials , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domLibrary_physics_materials , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domLibrary_physics_materials)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domLibrary_physics_models.cpp b/Engine/lib/collada/src/1.4/dom/domLibrary_physics_models.cpp new file mode 100644 index 000000000..f8db59a29 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domLibrary_physics_models.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domLibrary_physics_models::create(DAE& dae) +{ + domLibrary_physics_modelsRef ref = new domLibrary_physics_models(dae); + return ref; +} + + +daeMetaElement * +domLibrary_physics_models::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "library_physics_models" ); + meta->registerClass(domLibrary_physics_models::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domLibrary_physics_models,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 1, -1 ); + mea->setName( "physics_model" ); + mea->setOffset( daeOffsetOf(domLibrary_physics_models,elemPhysics_model_array) ); + mea->setElementType( domPhysics_model::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domLibrary_physics_models,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domLibrary_physics_models , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domLibrary_physics_models , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domLibrary_physics_models)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domLibrary_physics_scenes.cpp b/Engine/lib/collada/src/1.4/dom/domLibrary_physics_scenes.cpp new file mode 100644 index 000000000..c88074101 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domLibrary_physics_scenes.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domLibrary_physics_scenes::create(DAE& dae) +{ + domLibrary_physics_scenesRef ref = new domLibrary_physics_scenes(dae); + return ref; +} + + +daeMetaElement * +domLibrary_physics_scenes::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "library_physics_scenes" ); + meta->registerClass(domLibrary_physics_scenes::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domLibrary_physics_scenes,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 1, -1 ); + mea->setName( "physics_scene" ); + mea->setOffset( daeOffsetOf(domLibrary_physics_scenes,elemPhysics_scene_array) ); + mea->setElementType( domPhysics_scene::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domLibrary_physics_scenes,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domLibrary_physics_scenes , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domLibrary_physics_scenes , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domLibrary_physics_scenes)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domLibrary_visual_scenes.cpp b/Engine/lib/collada/src/1.4/dom/domLibrary_visual_scenes.cpp new file mode 100644 index 000000000..851d125e7 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domLibrary_visual_scenes.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domLibrary_visual_scenes::create(DAE& dae) +{ + domLibrary_visual_scenesRef ref = new domLibrary_visual_scenes(dae); + return ref; +} + + +daeMetaElement * +domLibrary_visual_scenes::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "library_visual_scenes" ); + meta->registerClass(domLibrary_visual_scenes::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domLibrary_visual_scenes,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 1, -1 ); + mea->setName( "visual_scene" ); + mea->setOffset( daeOffsetOf(domLibrary_visual_scenes,elemVisual_scene_array) ); + mea->setElementType( domVisual_scene::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domLibrary_visual_scenes,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domLibrary_visual_scenes , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domLibrary_visual_scenes , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domLibrary_visual_scenes)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domLight.cpp b/Engine/lib/collada/src/1.4/dom/domLight.cpp new file mode 100644 index 000000000..d1e589afc --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domLight.cpp @@ -0,0 +1,366 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domLight::create(DAE& dae) +{ + domLightRef ref = new domLight(dae); + return ref; +} + + +daeMetaElement * +domLight::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "light" ); + meta->registerClass(domLight::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domLight,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "technique_common" ); + mea->setOffset( daeOffsetOf(domLight,elemTechnique_common) ); + mea->setElementType( domLight::domTechnique_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "technique" ); + mea->setOffset( daeOffsetOf(domLight,elemTechnique_array) ); + mea->setElementType( domTechnique::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domLight,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3 ); + meta->setCMRoot( cm ); + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domLight , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domLight , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domLight)); + meta->validate(); + + return meta; +} + +daeElementRef +domLight::domTechnique_common::create(DAE& dae) +{ + domLight::domTechnique_commonRef ref = new domLight::domTechnique_common(dae); + return ref; +} + + +daeMetaElement * +domLight::domTechnique_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "technique_common" ); + meta->registerClass(domLight::domTechnique_common::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "ambient" ); + mea->setOffset( daeOffsetOf(domLight::domTechnique_common,elemAmbient) ); + mea->setElementType( domLight::domTechnique_common::domAmbient::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "directional" ); + mea->setOffset( daeOffsetOf(domLight::domTechnique_common,elemDirectional) ); + mea->setElementType( domLight::domTechnique_common::domDirectional::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "point" ); + mea->setOffset( daeOffsetOf(domLight::domTechnique_common,elemPoint) ); + mea->setElementType( domLight::domTechnique_common::domPoint::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "spot" ); + mea->setOffset( daeOffsetOf(domLight::domTechnique_common,elemSpot) ); + mea->setElementType( domLight::domTechnique_common::domSpot::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domLight::domTechnique_common,_contents)); + meta->addContentsOrder(daeOffsetOf(domLight::domTechnique_common,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domLight::domTechnique_common,_CMData), 1); + meta->setElementSize(sizeof(domLight::domTechnique_common)); + meta->validate(); + + return meta; +} + +daeElementRef +domLight::domTechnique_common::domAmbient::create(DAE& dae) +{ + domLight::domTechnique_common::domAmbientRef ref = new domLight::domTechnique_common::domAmbient(dae); + return ref; +} + + +daeMetaElement * +domLight::domTechnique_common::domAmbient::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "ambient" ); + meta->registerClass(domLight::domTechnique_common::domAmbient::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "color" ); + mea->setOffset( daeOffsetOf(domLight::domTechnique_common::domAmbient,elemColor) ); + mea->setElementType( domTargetableFloat3::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domLight::domTechnique_common::domAmbient)); + meta->validate(); + + return meta; +} + +daeElementRef +domLight::domTechnique_common::domDirectional::create(DAE& dae) +{ + domLight::domTechnique_common::domDirectionalRef ref = new domLight::domTechnique_common::domDirectional(dae); + return ref; +} + + +daeMetaElement * +domLight::domTechnique_common::domDirectional::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "directional" ); + meta->registerClass(domLight::domTechnique_common::domDirectional::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "color" ); + mea->setOffset( daeOffsetOf(domLight::domTechnique_common::domDirectional,elemColor) ); + mea->setElementType( domTargetableFloat3::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domLight::domTechnique_common::domDirectional)); + meta->validate(); + + return meta; +} + +daeElementRef +domLight::domTechnique_common::domPoint::create(DAE& dae) +{ + domLight::domTechnique_common::domPointRef ref = new domLight::domTechnique_common::domPoint(dae); + return ref; +} + + +daeMetaElement * +domLight::domTechnique_common::domPoint::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "point" ); + meta->registerClass(domLight::domTechnique_common::domPoint::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "color" ); + mea->setOffset( daeOffsetOf(domLight::domTechnique_common::domPoint,elemColor) ); + mea->setElementType( domTargetableFloat3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "constant_attenuation" ); + mea->setOffset( daeOffsetOf(domLight::domTechnique_common::domPoint,elemConstant_attenuation) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "linear_attenuation" ); + mea->setOffset( daeOffsetOf(domLight::domTechnique_common::domPoint,elemLinear_attenuation) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "quadratic_attenuation" ); + mea->setOffset( daeOffsetOf(domLight::domTechnique_common::domPoint,elemQuadratic_attenuation) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domLight::domTechnique_common::domPoint)); + meta->validate(); + + return meta; +} + +daeElementRef +domLight::domTechnique_common::domSpot::create(DAE& dae) +{ + domLight::domTechnique_common::domSpotRef ref = new domLight::domTechnique_common::domSpot(dae); + return ref; +} + + +daeMetaElement * +domLight::domTechnique_common::domSpot::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "spot" ); + meta->registerClass(domLight::domTechnique_common::domSpot::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "color" ); + mea->setOffset( daeOffsetOf(domLight::domTechnique_common::domSpot,elemColor) ); + mea->setElementType( domTargetableFloat3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "constant_attenuation" ); + mea->setOffset( daeOffsetOf(domLight::domTechnique_common::domSpot,elemConstant_attenuation) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "linear_attenuation" ); + mea->setOffset( daeOffsetOf(domLight::domTechnique_common::domSpot,elemLinear_attenuation) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "quadratic_attenuation" ); + mea->setOffset( daeOffsetOf(domLight::domTechnique_common::domSpot,elemQuadratic_attenuation) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "falloff_angle" ); + mea->setOffset( daeOffsetOf(domLight::domTechnique_common::domSpot,elemFalloff_angle) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 5, 0, 1 ); + mea->setName( "falloff_exponent" ); + mea->setOffset( daeOffsetOf(domLight::domTechnique_common::domSpot,elemFalloff_exponent) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 5 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domLight::domTechnique_common::domSpot)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domLines.cpp b/Engine/lib/collada/src/1.4/dom/domLines.cpp new file mode 100644 index 000000000..0c3873733 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domLines.cpp @@ -0,0 +1,107 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domLines::create(DAE& dae) +{ + domLinesRef ref = new domLines(dae); + return ref; +} + + +daeMetaElement * +domLines::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "lines" ); + meta->registerClass(domLines::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "input" ); + mea->setOffset( daeOffsetOf(domLines,elemInput_array) ); + mea->setElementType( domInputLocalOffset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "p" ); + mea->setOffset( daeOffsetOf(domLines,elemP) ); + mea->setElementType( domP::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domLines,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domLines , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: count + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "count" ); + ma->setType( dae.getAtomicTypes().get("Uint")); + ma->setOffset( daeOffsetOf( domLines , attrCount )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: material + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "material" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domLines , attrMaterial )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domLines)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domLinestrips.cpp b/Engine/lib/collada/src/1.4/dom/domLinestrips.cpp new file mode 100644 index 000000000..91937f6e6 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domLinestrips.cpp @@ -0,0 +1,107 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domLinestrips::create(DAE& dae) +{ + domLinestripsRef ref = new domLinestrips(dae); + return ref; +} + + +daeMetaElement * +domLinestrips::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "linestrips" ); + meta->registerClass(domLinestrips::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "input" ); + mea->setOffset( daeOffsetOf(domLinestrips,elemInput_array) ); + mea->setElementType( domInputLocalOffset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 0, -1 ); + mea->setName( "p" ); + mea->setOffset( daeOffsetOf(domLinestrips,elemP_array) ); + mea->setElementType( domP::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domLinestrips,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domLinestrips , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: count + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "count" ); + ma->setType( dae.getAtomicTypes().get("Uint")); + ma->setOffset( daeOffsetOf( domLinestrips , attrCount )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: material + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "material" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domLinestrips , attrMaterial )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domLinestrips)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domLookat.cpp b/Engine/lib/collada/src/1.4/dom/domLookat.cpp new file mode 100644 index 000000000..6ec29ac00 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domLookat.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domLookat::create(DAE& dae) +{ + domLookatRef ref = new domLookat(dae); + return ref; +} + + +daeMetaElement * +domLookat::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "lookat" ); + meta->registerClass(domLookat::create); + + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float3x3")); + ma->setOffset( daeOffsetOf( domLookat , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domLookat , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domLookat)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domMaterial.cpp b/Engine/lib/collada/src/1.4/dom/domMaterial.cpp new file mode 100644 index 000000000..42f73ac71 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domMaterial.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domMaterial::create(DAE& dae) +{ + domMaterialRef ref = new domMaterial(dae); + return ref; +} + + +daeMetaElement * +domMaterial::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "material" ); + meta->registerClass(domMaterial::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domMaterial,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "instance_effect" ); + mea->setOffset( daeOffsetOf(domMaterial,elemInstance_effect) ); + mea->setElementType( domInstance_effect::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domMaterial,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domMaterial , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domMaterial , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domMaterial)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domMatrix.cpp b/Engine/lib/collada/src/1.4/dom/domMatrix.cpp new file mode 100644 index 000000000..417059b71 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domMatrix.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domMatrix::create(DAE& dae) +{ + domMatrixRef ref = new domMatrix(dae); + return ref; +} + + +daeMetaElement * +domMatrix::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "matrix" ); + meta->registerClass(domMatrix::create); + + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float4x4")); + ma->setOffset( daeOffsetOf( domMatrix , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domMatrix , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domMatrix)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domMesh.cpp b/Engine/lib/collada/src/1.4/dom/domMesh.cpp new file mode 100644 index 000000000..08706cb97 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domMesh.cpp @@ -0,0 +1,125 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domMesh::create(DAE& dae) +{ + domMeshRef ref = new domMesh(dae); + return ref; +} + + +daeMetaElement * +domMesh::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mesh" ); + meta->registerClass(domMesh::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, -1 ); + mea->setName( "source" ); + mea->setOffset( daeOffsetOf(domMesh,elemSource_array) ); + mea->setElementType( domSource::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "vertices" ); + mea->setOffset( daeOffsetOf(domMesh,elemVertices) ); + mea->setElementType( domVertices::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 2, 0, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "lines" ); + mea->setOffset( daeOffsetOf(domMesh,elemLines_array) ); + mea->setElementType( domLines::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "linestrips" ); + mea->setOffset( daeOffsetOf(domMesh,elemLinestrips_array) ); + mea->setElementType( domLinestrips::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "polygons" ); + mea->setOffset( daeOffsetOf(domMesh,elemPolygons_array) ); + mea->setElementType( domPolygons::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "polylist" ); + mea->setOffset( daeOffsetOf(domMesh,elemPolylist_array) ); + mea->setElementType( domPolylist::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "triangles" ); + mea->setOffset( daeOffsetOf(domMesh,elemTriangles_array) ); + mea->setElementType( domTriangles::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "trifans" ); + mea->setOffset( daeOffsetOf(domMesh,elemTrifans_array) ); + mea->setElementType( domTrifans::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "tristrips" ); + mea->setOffset( daeOffsetOf(domMesh,elemTristrips_array) ); + mea->setElementType( domTristrips::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3003, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domMesh,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3003 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domMesh,_contents)); + meta->addContentsOrder(daeOffsetOf(domMesh,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domMesh,_CMData), 1); + meta->setElementSize(sizeof(domMesh)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domMorph.cpp b/Engine/lib/collada/src/1.4/dom/domMorph.cpp new file mode 100644 index 000000000..bd7e7ae26 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domMorph.cpp @@ -0,0 +1,142 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domMorph::create(DAE& dae) +{ + domMorphRef ref = new domMorph(dae); + return ref; +} + + +daeMetaElement * +domMorph::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "morph" ); + meta->registerClass(domMorph::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 2, -1 ); + mea->setName( "source" ); + mea->setOffset( daeOffsetOf(domMorph,elemSource_array) ); + mea->setElementType( domSource::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "targets" ); + mea->setOffset( daeOffsetOf(domMorph,elemTargets) ); + mea->setElementType( domMorph::domTargets::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domMorph,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: method + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "method" ); + ma->setType( dae.getAtomicTypes().get("MorphMethodType")); + ma->setOffset( daeOffsetOf( domMorph , attrMethod )); + ma->setContainer( meta ); + ma->setDefaultString( "NORMALIZED"); + + meta->appendAttribute(ma); + } + + // Add attribute: source + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "source" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domMorph , attrSource )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domMorph)); + meta->validate(); + + return meta; +} + +daeElementRef +domMorph::domTargets::create(DAE& dae) +{ + domMorph::domTargetsRef ref = new domMorph::domTargets(dae); + return ref; +} + + +daeMetaElement * +domMorph::domTargets::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "targets" ); + meta->registerClass(domMorph::domTargets::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 2, -1 ); + mea->setName( "input" ); + mea->setOffset( daeOffsetOf(domMorph::domTargets,elemInput_array) ); + mea->setElementType( domInputLocal::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domMorph::domTargets,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domMorph::domTargets)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domName_array.cpp b/Engine/lib/collada/src/1.4/dom/domName_array.cpp new file mode 100644 index 000000000..9b427a119 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domName_array.cpp @@ -0,0 +1,92 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domName_array::create(DAE& dae) +{ + domName_arrayRef ref = new domName_array(dae); + return ref; +} + + +daeMetaElement * +domName_array::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "Name_array" ); + meta->registerClass(domName_array::create); + + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("ListOfNames")); + ma->setOffset( daeOffsetOf( domName_array , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domName_array , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domName_array , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: count + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "count" ); + ma->setType( dae.getAtomicTypes().get("Uint")); + ma->setOffset( daeOffsetOf( domName_array , attrCount )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domName_array)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domNode.cpp b/Engine/lib/collada/src/1.4/dom/domNode.cpp new file mode 100644 index 000000000..33cdb0be3 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domNode.cpp @@ -0,0 +1,205 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domNode::create(DAE& dae) +{ + domNodeRef ref = new domNode(dae); + return ref; +} + + +daeMetaElement * +domNode::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "node" ); + meta->registerClass(domNode::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domNode,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 1, 0, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "lookat" ); + mea->setOffset( daeOffsetOf(domNode,elemLookat_array) ); + mea->setElementType( domLookat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "matrix" ); + mea->setOffset( daeOffsetOf(domNode,elemMatrix_array) ); + mea->setElementType( domMatrix::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "rotate" ); + mea->setOffset( daeOffsetOf(domNode,elemRotate_array) ); + mea->setElementType( domRotate::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "scale" ); + mea->setOffset( daeOffsetOf(domNode,elemScale_array) ); + mea->setElementType( domScale::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "skew" ); + mea->setOffset( daeOffsetOf(domNode,elemSkew_array) ); + mea->setElementType( domSkew::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "translate" ); + mea->setOffset( daeOffsetOf(domNode,elemTranslate_array) ); + mea->setElementType( domTranslate::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3002, 0, -1 ); + mea->setName( "instance_camera" ); + mea->setOffset( daeOffsetOf(domNode,elemInstance_camera_array) ); + mea->setElementType( domInstance_camera::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3003, 0, -1 ); + mea->setName( "instance_controller" ); + mea->setOffset( daeOffsetOf(domNode,elemInstance_controller_array) ); + mea->setElementType( domInstance_controller::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3004, 0, -1 ); + mea->setName( "instance_geometry" ); + mea->setOffset( daeOffsetOf(domNode,elemInstance_geometry_array) ); + mea->setElementType( domInstance_geometry::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3005, 0, -1 ); + mea->setName( "instance_light" ); + mea->setOffset( daeOffsetOf(domNode,elemInstance_light_array) ); + mea->setElementType( domInstance_light::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3006, 0, -1 ); + mea->setName( "instance_node" ); + mea->setOffset( daeOffsetOf(domNode,elemInstance_node_array) ); + mea->setElementType( domInstance_node::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3007, 0, -1 ); + mea->setName( "node" ); + mea->setOffset( daeOffsetOf(domNode,elemNode_array) ); + mea->setElementType( domNode::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3008, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domNode,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3008 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domNode,_contents)); + meta->addContentsOrder(daeOffsetOf(domNode,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domNode,_CMData), 1); + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domNode , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domNode , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domNode , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: type + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "type" ); + ma->setType( dae.getAtomicTypes().get("NodeType")); + ma->setOffset( daeOffsetOf( domNode , attrType )); + ma->setContainer( meta ); + ma->setDefaultString( "NODE"); + + meta->appendAttribute(ma); + } + + // Add attribute: layer + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "layer" ); + ma->setType( dae.getAtomicTypes().get("ListOfNames")); + ma->setOffset( daeOffsetOf( domNode , attrLayer )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domNode)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domP.cpp b/Engine/lib/collada/src/1.4/dom/domP.cpp new file mode 100644 index 000000000..a7e5bb3ea --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domP.cpp @@ -0,0 +1,58 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domP::create(DAE& dae) +{ + domPRef ref = new domP(dae); + return ref; +} + + +daeMetaElement * +domP::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "p" ); + meta->registerClass(domP::create); + + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("ListOfUInts")); + ma->setOffset( daeOffsetOf( domP , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domP)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domParam.cpp b/Engine/lib/collada/src/1.4/dom/domParam.cpp new file mode 100644 index 000000000..7f57b01d7 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domParam.cpp @@ -0,0 +1,103 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domParam::create(DAE& dae) +{ + domParamRef ref = new domParam(dae); + return ref; +} + + +daeMetaElement * +domParam::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "param" ); + meta->registerClass(domParam::create); + + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsString")); + ma->setOffset( daeOffsetOf( domParam , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domParam , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domParam , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: semantic + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "semantic" ); + ma->setType( dae.getAtomicTypes().get("xsNMTOKEN")); + ma->setOffset( daeOffsetOf( domParam , attrSemantic )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: type + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "type" ); + ma->setType( dae.getAtomicTypes().get("xsNMTOKEN")); + ma->setOffset( daeOffsetOf( domParam , attrType )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domParam)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domPhysics_material.cpp b/Engine/lib/collada/src/1.4/dom/domPhysics_material.cpp new file mode 100644 index 000000000..99c5b9204 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domPhysics_material.cpp @@ -0,0 +1,152 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domPhysics_material::create(DAE& dae) +{ + domPhysics_materialRef ref = new domPhysics_material(dae); + return ref; +} + + +daeMetaElement * +domPhysics_material::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "physics_material" ); + meta->registerClass(domPhysics_material::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domPhysics_material,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "technique_common" ); + mea->setOffset( daeOffsetOf(domPhysics_material,elemTechnique_common) ); + mea->setElementType( domPhysics_material::domTechnique_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "technique" ); + mea->setOffset( daeOffsetOf(domPhysics_material,elemTechnique_array) ); + mea->setElementType( domTechnique::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domPhysics_material,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3 ); + meta->setCMRoot( cm ); + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domPhysics_material , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domPhysics_material , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domPhysics_material)); + meta->validate(); + + return meta; +} + +daeElementRef +domPhysics_material::domTechnique_common::create(DAE& dae) +{ + domPhysics_material::domTechnique_commonRef ref = new domPhysics_material::domTechnique_common(dae); + return ref; +} + + +daeMetaElement * +domPhysics_material::domTechnique_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "technique_common" ); + meta->registerClass(domPhysics_material::domTechnique_common::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "dynamic_friction" ); + mea->setOffset( daeOffsetOf(domPhysics_material::domTechnique_common,elemDynamic_friction) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "restitution" ); + mea->setOffset( daeOffsetOf(domPhysics_material::domTechnique_common,elemRestitution) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "static_friction" ); + mea->setOffset( daeOffsetOf(domPhysics_material::domTechnique_common,elemStatic_friction) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domPhysics_material::domTechnique_common)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domPhysics_model.cpp b/Engine/lib/collada/src/1.4/dom/domPhysics_model.cpp new file mode 100644 index 000000000..c498c168a --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domPhysics_model.cpp @@ -0,0 +1,107 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domPhysics_model::create(DAE& dae) +{ + domPhysics_modelRef ref = new domPhysics_model(dae); + return ref; +} + + +daeMetaElement * +domPhysics_model::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "physics_model" ); + meta->registerClass(domPhysics_model::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domPhysics_model,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 0, -1 ); + mea->setName( "rigid_body" ); + mea->setOffset( daeOffsetOf(domPhysics_model,elemRigid_body_array) ); + mea->setElementType( domRigid_body::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "rigid_constraint" ); + mea->setOffset( daeOffsetOf(domPhysics_model,elemRigid_constraint_array) ); + mea->setElementType( domRigid_constraint::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3, 0, -1 ); + mea->setName( "instance_physics_model" ); + mea->setOffset( daeOffsetOf(domPhysics_model,elemInstance_physics_model_array) ); + mea->setElementType( domInstance_physics_model::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 4, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domPhysics_model,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 4 ); + meta->setCMRoot( cm ); + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domPhysics_model , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domPhysics_model , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domPhysics_model)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domPhysics_scene.cpp b/Engine/lib/collada/src/1.4/dom/domPhysics_scene.cpp new file mode 100644 index 000000000..a39d91ba0 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domPhysics_scene.cpp @@ -0,0 +1,158 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domPhysics_scene::create(DAE& dae) +{ + domPhysics_sceneRef ref = new domPhysics_scene(dae); + return ref; +} + + +daeMetaElement * +domPhysics_scene::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "physics_scene" ); + meta->registerClass(domPhysics_scene::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domPhysics_scene,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 0, -1 ); + mea->setName( "instance_force_field" ); + mea->setOffset( daeOffsetOf(domPhysics_scene,elemInstance_force_field_array) ); + mea->setElementType( domInstance_force_field::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "instance_physics_model" ); + mea->setOffset( daeOffsetOf(domPhysics_scene,elemInstance_physics_model_array) ); + mea->setElementType( domInstance_physics_model::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 1, 1 ); + mea->setName( "technique_common" ); + mea->setOffset( daeOffsetOf(domPhysics_scene,elemTechnique_common) ); + mea->setElementType( domPhysics_scene::domTechnique_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 4, 0, -1 ); + mea->setName( "technique" ); + mea->setOffset( daeOffsetOf(domPhysics_scene,elemTechnique_array) ); + mea->setElementType( domTechnique::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 5, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domPhysics_scene,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 5 ); + meta->setCMRoot( cm ); + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domPhysics_scene , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domPhysics_scene , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domPhysics_scene)); + meta->validate(); + + return meta; +} + +daeElementRef +domPhysics_scene::domTechnique_common::create(DAE& dae) +{ + domPhysics_scene::domTechnique_commonRef ref = new domPhysics_scene::domTechnique_common(dae); + return ref; +} + + +daeMetaElement * +domPhysics_scene::domTechnique_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "technique_common" ); + meta->registerClass(domPhysics_scene::domTechnique_common::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "gravity" ); + mea->setOffset( daeOffsetOf(domPhysics_scene::domTechnique_common,elemGravity) ); + mea->setElementType( domTargetableFloat3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "time_step" ); + mea->setOffset( daeOffsetOf(domPhysics_scene::domTechnique_common,elemTime_step) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domPhysics_scene::domTechnique_common)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domPlane.cpp b/Engine/lib/collada/src/1.4/dom/domPlane.cpp new file mode 100644 index 000000000..74006131b --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domPlane.cpp @@ -0,0 +1,103 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domPlane::create(DAE& dae) +{ + domPlaneRef ref = new domPlane(dae); + return ref; +} + + +daeMetaElement * +domPlane::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "plane" ); + meta->registerClass(domPlane::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "equation" ); + mea->setOffset( daeOffsetOf(domPlane,elemEquation) ); + mea->setElementType( domPlane::domEquation::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domPlane,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domPlane)); + meta->validate(); + + return meta; +} + +daeElementRef +domPlane::domEquation::create(DAE& dae) +{ + domPlane::domEquationRef ref = new domPlane::domEquation(dae); + return ref; +} + + +daeMetaElement * +domPlane::domEquation::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "equation" ); + meta->registerClass(domPlane::domEquation::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domPlane::domEquation , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domPlane::domEquation)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domPolygons.cpp b/Engine/lib/collada/src/1.4/dom/domPolygons.cpp new file mode 100644 index 000000000..3e7a1a897 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domPolygons.cpp @@ -0,0 +1,204 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domPolygons::create(DAE& dae) +{ + domPolygonsRef ref = new domPolygons(dae); + return ref; +} + + +daeMetaElement * +domPolygons::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "polygons" ); + meta->registerClass(domPolygons::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "input" ); + mea->setOffset( daeOffsetOf(domPolygons,elemInput_array) ); + mea->setElementType( domInputLocalOffset::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 1, 0, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "p" ); + mea->setOffset( daeOffsetOf(domPolygons,elemP_array) ); + mea->setElementType( domP::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "ph" ); + mea->setOffset( daeOffsetOf(domPolygons,elemPh_array) ); + mea->setElementType( domPolygons::domPh::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3002, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domPolygons,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3002 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domPolygons,_contents)); + meta->addContentsOrder(daeOffsetOf(domPolygons,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domPolygons,_CMData), 1); + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domPolygons , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: count + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "count" ); + ma->setType( dae.getAtomicTypes().get("Uint")); + ma->setOffset( daeOffsetOf( domPolygons , attrCount )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: material + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "material" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domPolygons , attrMaterial )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domPolygons)); + meta->validate(); + + return meta; +} + +daeElementRef +domPolygons::domPh::create(DAE& dae) +{ + domPolygons::domPhRef ref = new domPolygons::domPh(dae); + return ref; +} + + +daeMetaElement * +domPolygons::domPh::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "ph" ); + meta->registerClass(domPolygons::domPh::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "p" ); + mea->setOffset( daeOffsetOf(domPolygons::domPh,elemP) ); + mea->setElementType( domP::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 1, -1 ); + mea->setName( "h" ); + mea->setOffset( daeOffsetOf(domPolygons::domPh,elemH_array) ); + mea->setElementType( domPolygons::domPh::domH::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domPolygons::domPh)); + meta->validate(); + + return meta; +} + +daeElementRef +domPolygons::domPh::domH::create(DAE& dae) +{ + domPolygons::domPh::domHRef ref = new domPolygons::domPh::domH(dae); + return ref; +} + + +daeMetaElement * +domPolygons::domPh::domH::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "h" ); + meta->registerClass(domPolygons::domPh::domH::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("ListOfUInts")); + ma->setOffset( daeOffsetOf( domPolygons::domPh::domH , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domPolygons::domPh::domH)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domPolylist.cpp b/Engine/lib/collada/src/1.4/dom/domPolylist.cpp new file mode 100644 index 000000000..307480b65 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domPolylist.cpp @@ -0,0 +1,149 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domPolylist::create(DAE& dae) +{ + domPolylistRef ref = new domPolylist(dae); + return ref; +} + + +daeMetaElement * +domPolylist::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "polylist" ); + meta->registerClass(domPolylist::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "input" ); + mea->setOffset( daeOffsetOf(domPolylist,elemInput_array) ); + mea->setElementType( domInputLocalOffset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "vcount" ); + mea->setOffset( daeOffsetOf(domPolylist,elemVcount) ); + mea->setElementType( domPolylist::domVcount::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "p" ); + mea->setOffset( daeOffsetOf(domPolylist,elemP) ); + mea->setElementType( domP::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domPolylist,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3 ); + meta->setCMRoot( cm ); + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domPolylist , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: count + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "count" ); + ma->setType( dae.getAtomicTypes().get("Uint")); + ma->setOffset( daeOffsetOf( domPolylist , attrCount )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: material + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "material" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domPolylist , attrMaterial )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domPolylist)); + meta->validate(); + + return meta; +} + +daeElementRef +domPolylist::domVcount::create(DAE& dae) +{ + domPolylist::domVcountRef ref = new domPolylist::domVcount(dae); + return ref; +} + + +daeMetaElement * +domPolylist::domVcount::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "vcount" ); + meta->registerClass(domPolylist::domVcount::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("ListOfUInts")); + ma->setOffset( daeOffsetOf( domPolylist::domVcount , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domPolylist::domVcount)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domProfile_CG.cpp b/Engine/lib/collada/src/1.4/dom/domProfile_CG.cpp new file mode 100644 index 000000000..72924285d --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domProfile_CG.cpp @@ -0,0 +1,721 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domProfile_CG::create(DAE& dae) +{ + domProfile_CGRef ref = new domProfile_CG(dae); + return ref; +} + + +daeMetaElement * +domProfile_CG::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "profile_CG" ); + meta->registerClass(domProfile_CG::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domProfile_CG,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 1, 0, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "code" ); + mea->setOffset( daeOffsetOf(domProfile_CG,elemCode_array) ); + mea->setElementType( domFx_code_profile::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "include" ); + mea->setOffset( daeOffsetOf(domProfile_CG,elemInclude_array) ); + mea->setElementType( domFx_include_common::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm = new daeMetaChoice( meta, cm, 1, 3002, 0, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "image" ); + mea->setOffset( daeOffsetOf(domProfile_CG,elemImage_array) ); + mea->setElementType( domImage::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "newparam" ); + mea->setOffset( daeOffsetOf(domProfile_CG,elemNewparam_array) ); + mea->setElementType( domCg_newparam::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementArrayAttribute( meta, cm, 6003, 1, -1 ); + mea->setName( "technique" ); + mea->setOffset( daeOffsetOf(domProfile_CG,elemTechnique_array) ); + mea->setElementType( domProfile_CG::domTechnique::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 6004, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domProfile_CG,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 6004 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domProfile_CG,_contents)); + meta->addContentsOrder(daeOffsetOf(domProfile_CG,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domProfile_CG,_CMData), 2); + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domProfile_CG , attrId )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: platform + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "platform" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domProfile_CG , attrPlatform )); + ma->setContainer( meta ); + ma->setDefaultString( "PC"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_CG)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_CG::domTechnique::create(DAE& dae) +{ + domProfile_CG::domTechniqueRef ref = new domProfile_CG::domTechnique(dae); + return ref; +} + + +daeMetaElement * +domProfile_CG::domTechnique::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "technique" ); + meta->registerClass(domProfile_CG::domTechnique::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domProfile_CG::domTechnique,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 0, -1 ); + mea->setName( "annotate" ); + mea->setOffset( daeOffsetOf(domProfile_CG::domTechnique,elemAnnotate_array) ); + mea->setElementType( domFx_annotate_common::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 2, 0, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "code" ); + mea->setOffset( daeOffsetOf(domProfile_CG::domTechnique,elemCode_array) ); + mea->setElementType( domFx_code_profile::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "include" ); + mea->setOffset( daeOffsetOf(domProfile_CG::domTechnique,elemInclude_array) ); + mea->setElementType( domFx_include_common::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm = new daeMetaChoice( meta, cm, 1, 3003, 0, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "image" ); + mea->setOffset( daeOffsetOf(domProfile_CG::domTechnique,elemImage_array) ); + mea->setElementType( domImage::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "newparam" ); + mea->setOffset( daeOffsetOf(domProfile_CG::domTechnique,elemNewparam_array) ); + mea->setElementType( domCg_newparam::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "setparam" ); + mea->setOffset( daeOffsetOf(domProfile_CG::domTechnique,elemSetparam_array) ); + mea->setElementType( domCg_setparam::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementArrayAttribute( meta, cm, 6004, 1, -1 ); + mea->setName( "pass" ); + mea->setOffset( daeOffsetOf(domProfile_CG::domTechnique,elemPass_array) ); + mea->setElementType( domProfile_CG::domTechnique::domPass::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 6005, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domProfile_CG::domTechnique,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 6005 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domProfile_CG::domTechnique,_contents)); + meta->addContentsOrder(daeOffsetOf(domProfile_CG::domTechnique,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domProfile_CG::domTechnique,_CMData), 2); + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domProfile_CG::domTechnique , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domProfile_CG::domTechnique , attrSid )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_CG::domTechnique)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_CG::domTechnique::domPass::create(DAE& dae) +{ + domProfile_CG::domTechnique::domPassRef ref = new domProfile_CG::domTechnique::domPass(dae); + return ref; +} + + +daeMetaElement * +domProfile_CG::domTechnique::domPass::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "pass" ); + meta->registerClass(domProfile_CG::domTechnique::domPass::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "annotate" ); + mea->setOffset( daeOffsetOf(domProfile_CG::domTechnique::domPass,elemAnnotate_array) ); + mea->setElementType( domFx_annotate_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 0, -1 ); + mea->setName( "color_target" ); + mea->setOffset( daeOffsetOf(domProfile_CG::domTechnique::domPass,elemColor_target_array) ); + mea->setElementType( domFx_colortarget_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "depth_target" ); + mea->setOffset( daeOffsetOf(domProfile_CG::domTechnique::domPass,elemDepth_target_array) ); + mea->setElementType( domFx_depthtarget_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3, 0, -1 ); + mea->setName( "stencil_target" ); + mea->setOffset( daeOffsetOf(domProfile_CG::domTechnique::domPass,elemStencil_target_array) ); + mea->setElementType( domFx_stenciltarget_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 4, 0, -1 ); + mea->setName( "color_clear" ); + mea->setOffset( daeOffsetOf(domProfile_CG::domTechnique::domPass,elemColor_clear_array) ); + mea->setElementType( domFx_clearcolor_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 5, 0, -1 ); + mea->setName( "depth_clear" ); + mea->setOffset( daeOffsetOf(domProfile_CG::domTechnique::domPass,elemDepth_clear_array) ); + mea->setElementType( domFx_cleardepth_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 6, 0, -1 ); + mea->setName( "stencil_clear" ); + mea->setOffset( daeOffsetOf(domProfile_CG::domTechnique::domPass,elemStencil_clear_array) ); + mea->setElementType( domFx_clearstencil_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 7, 0, 1 ); + mea->setName( "draw" ); + mea->setOffset( daeOffsetOf(domProfile_CG::domTechnique::domPass,elemDraw) ); + mea->setElementType( domProfile_CG::domTechnique::domPass::domDraw::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 8, 1, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "gl_pipeline_settings" ); + mea->setOffset( daeOffsetOf(domProfile_CG::domTechnique::domPass,elemGl_pipeline_settings_array) ); + mea->setElementType( domGl_pipeline_settings::registerElement(dae) ); + cm->appendChild( new daeMetaGroup( mea, meta, cm, 0, 1, 1 ) ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "shader" ); + mea->setOffset( daeOffsetOf(domProfile_CG::domTechnique::domPass,elemShader_array) ); + mea->setElementType( domProfile_CG::domTechnique::domPass::domShader::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3009, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domProfile_CG::domTechnique::domPass,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3009 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domProfile_CG::domTechnique::domPass,_contents)); + meta->addContentsOrder(daeOffsetOf(domProfile_CG::domTechnique::domPass,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domProfile_CG::domTechnique::domPass,_CMData), 1); + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domProfile_CG::domTechnique::domPass , attrSid )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_CG::domTechnique::domPass)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_CG::domTechnique::domPass::domDraw::create(DAE& dae) +{ + domProfile_CG::domTechnique::domPass::domDrawRef ref = new domProfile_CG::domTechnique::domPass::domDraw(dae); + return ref; +} + + +daeMetaElement * +domProfile_CG::domTechnique::domPass::domDraw::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "draw" ); + meta->registerClass(domProfile_CG::domTechnique::domPass::domDraw::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_draw_common")); + ma->setOffset( daeOffsetOf( domProfile_CG::domTechnique::domPass::domDraw , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_CG::domTechnique::domPass::domDraw)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_CG::domTechnique::domPass::domShader::create(DAE& dae) +{ + domProfile_CG::domTechnique::domPass::domShaderRef ref = new domProfile_CG::domTechnique::domPass::domShader(dae); + return ref; +} + + +daeMetaElement * +domProfile_CG::domTechnique::domPass::domShader::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "shader" ); + meta->registerClass(domProfile_CG::domTechnique::domPass::domShader::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "annotate" ); + mea->setOffset( daeOffsetOf(domProfile_CG::domTechnique::domPass::domShader,elemAnnotate_array) ); + mea->setElementType( domFx_annotate_common::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaSequence( meta, cm, 1, 0, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "compiler_target" ); + mea->setOffset( daeOffsetOf(domProfile_CG::domTechnique::domPass::domShader,elemCompiler_target) ); + mea->setElementType( domProfile_CG::domTechnique::domPass::domShader::domCompiler_target::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "compiler_options" ); + mea->setOffset( daeOffsetOf(domProfile_CG::domTechnique::domPass::domShader,elemCompiler_options) ); + mea->setElementType( domProfile_CG::domTechnique::domPass::domShader::domCompiler_options::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementAttribute( meta, cm, 3, 1, 1 ); + mea->setName( "name" ); + mea->setOffset( daeOffsetOf(domProfile_CG::domTechnique::domPass::domShader,elemName) ); + mea->setElementType( domProfile_CG::domTechnique::domPass::domShader::domName::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 4, 0, -1 ); + mea->setName( "bind" ); + mea->setOffset( daeOffsetOf(domProfile_CG::domTechnique::domPass::domShader,elemBind_array) ); + mea->setElementType( domProfile_CG::domTechnique::domPass::domShader::domBind::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 4 ); + meta->setCMRoot( cm ); + + // Add attribute: stage + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "stage" ); + ma->setType( dae.getAtomicTypes().get("Cg_pipeline_stage")); + ma->setOffset( daeOffsetOf( domProfile_CG::domTechnique::domPass::domShader , attrStage )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_CG::domTechnique::domPass::domShader)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_CG::domTechnique::domPass::domShader::domCompiler_target::create(DAE& dae) +{ + domProfile_CG::domTechnique::domPass::domShader::domCompiler_targetRef ref = new domProfile_CG::domTechnique::domPass::domShader::domCompiler_target(dae); + return ref; +} + + +daeMetaElement * +domProfile_CG::domTechnique::domPass::domShader::domCompiler_target::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "compiler_target" ); + meta->registerClass(domProfile_CG::domTechnique::domPass::domShader::domCompiler_target::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNMTOKEN")); + ma->setOffset( daeOffsetOf( domProfile_CG::domTechnique::domPass::domShader::domCompiler_target , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_CG::domTechnique::domPass::domShader::domCompiler_target)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_CG::domTechnique::domPass::domShader::domCompiler_options::create(DAE& dae) +{ + domProfile_CG::domTechnique::domPass::domShader::domCompiler_optionsRef ref = new domProfile_CG::domTechnique::domPass::domShader::domCompiler_options(dae); + return ref; +} + + +daeMetaElement * +domProfile_CG::domTechnique::domPass::domShader::domCompiler_options::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "compiler_options" ); + meta->registerClass(domProfile_CG::domTechnique::domPass::domShader::domCompiler_options::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsString")); + ma->setOffset( daeOffsetOf( domProfile_CG::domTechnique::domPass::domShader::domCompiler_options , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_CG::domTechnique::domPass::domShader::domCompiler_options)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_CG::domTechnique::domPass::domShader::domName::create(DAE& dae) +{ + domProfile_CG::domTechnique::domPass::domShader::domNameRef ref = new domProfile_CG::domTechnique::domPass::domShader::domName(dae); + return ref; +} + + +daeMetaElement * +domProfile_CG::domTechnique::domPass::domShader::domName::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "name" ); + meta->registerClass(domProfile_CG::domTechnique::domPass::domShader::domName::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domProfile_CG::domTechnique::domPass::domShader::domName , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: source + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "source" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domProfile_CG::domTechnique::domPass::domShader::domName , attrSource )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_CG::domTechnique::domPass::domShader::domName)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_CG::domTechnique::domPass::domShader::domBind::create(DAE& dae) +{ + domProfile_CG::domTechnique::domPass::domShader::domBindRef ref = new domProfile_CG::domTechnique::domPass::domShader::domBind(dae); + return ref; +} + + +daeMetaElement * +domProfile_CG::domTechnique::domPass::domShader::domBind::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bind" ); + meta->registerClass(domProfile_CG::domTechnique::domPass::domShader::domBind::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "cg_param_type" ); + mea->setOffset( daeOffsetOf(domProfile_CG::domTechnique::domPass::domShader::domBind,elemCg_param_type) ); + mea->setElementType( domCg_param_type::registerElement(dae) ); + cm->appendChild( new daeMetaGroup( mea, meta, cm, 0, 1, 1 ) ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "param" ); + mea->setOffset( daeOffsetOf(domProfile_CG::domTechnique::domPass::domShader::domBind,elemParam) ); + mea->setElementType( domProfile_CG::domTechnique::domPass::domShader::domBind::domParam::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domProfile_CG::domTechnique::domPass::domShader::domBind,_contents)); + meta->addContentsOrder(daeOffsetOf(domProfile_CG::domTechnique::domPass::domShader::domBind,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domProfile_CG::domTechnique::domPass::domShader::domBind,_CMData), 1); + // Add attribute: symbol + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "symbol" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domProfile_CG::domTechnique::domPass::domShader::domBind , attrSymbol )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_CG::domTechnique::domPass::domShader::domBind)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_CG::domTechnique::domPass::domShader::domBind::domParam::create(DAE& dae) +{ + domProfile_CG::domTechnique::domPass::domShader::domBind::domParamRef ref = new domProfile_CG::domTechnique::domPass::domShader::domBind::domParam(dae); + return ref; +} + + +daeMetaElement * +domProfile_CG::domTechnique::domPass::domShader::domBind::domParam::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "param" ); + meta->registerClass(domProfile_CG::domTechnique::domPass::domShader::domBind::domParam::create); + + meta->setIsInnerClass( true ); + + // Add attribute: ref + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "ref" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domProfile_CG::domTechnique::domPass::domShader::domBind::domParam , attrRef )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_CG::domTechnique::domPass::domShader::domBind::domParam)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domProfile_COMMON.cpp b/Engine/lib/collada/src/1.4/dom/domProfile_COMMON.cpp new file mode 100644 index 000000000..d62c0b68c --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domProfile_COMMON.cpp @@ -0,0 +1,563 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domProfile_COMMON::create(DAE& dae) +{ + domProfile_COMMONRef ref = new domProfile_COMMON(dae); + return ref; +} + + +daeMetaElement * +domProfile_COMMON::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "profile_COMMON" ); + meta->registerClass(domProfile_COMMON::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 1, 0, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "image" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON,elemImage_array) ); + mea->setElementType( domImage::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "newparam" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON,elemNewparam_array) ); + mea->setElementType( domCommon_newparam_type::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementAttribute( meta, cm, 3002, 1, 1 ); + mea->setName( "technique" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON,elemTechnique) ); + mea->setElementType( domProfile_COMMON::domTechnique::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3003, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3003 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domProfile_COMMON,_contents)); + meta->addContentsOrder(daeOffsetOf(domProfile_COMMON,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domProfile_COMMON,_CMData), 1); + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domProfile_COMMON , attrId )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_COMMON)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_COMMON::domTechnique::create(DAE& dae) +{ + domProfile_COMMON::domTechniqueRef ref = new domProfile_COMMON::domTechnique(dae); + return ref; +} + + +daeMetaElement * +domProfile_COMMON::domTechnique::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "technique" ); + meta->registerClass(domProfile_COMMON::domTechnique::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 1, 0, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "image" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique,elemImage_array) ); + mea->setElementType( domImage::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "newparam" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique,elemNewparam_array) ); + mea->setElementType( domCommon_newparam_type::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm = new daeMetaChoice( meta, cm, 1, 3002, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "constant" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique,elemConstant) ); + mea->setElementType( domProfile_COMMON::domTechnique::domConstant::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "lambert" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique,elemLambert) ); + mea->setElementType( domProfile_COMMON::domTechnique::domLambert::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "phong" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique,elemPhong) ); + mea->setElementType( domProfile_COMMON::domTechnique::domPhong::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "blinn" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique,elemBlinn) ); + mea->setElementType( domProfile_COMMON::domTechnique::domBlinn::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3003, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3003 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domProfile_COMMON::domTechnique,_contents)); + meta->addContentsOrder(daeOffsetOf(domProfile_COMMON::domTechnique,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domProfile_COMMON::domTechnique,_CMData), 2); + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domProfile_COMMON::domTechnique , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domProfile_COMMON::domTechnique , attrSid )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_COMMON::domTechnique)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_COMMON::domTechnique::domConstant::create(DAE& dae) +{ + domProfile_COMMON::domTechnique::domConstantRef ref = new domProfile_COMMON::domTechnique::domConstant(dae); + return ref; +} + + +daeMetaElement * +domProfile_COMMON::domTechnique::domConstant::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "constant" ); + meta->registerClass(domProfile_COMMON::domTechnique::domConstant::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "emission" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domConstant,elemEmission) ); + mea->setElementType( domCommon_color_or_texture_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "reflective" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domConstant,elemReflective) ); + mea->setElementType( domCommon_color_or_texture_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "reflectivity" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domConstant,elemReflectivity) ); + mea->setElementType( domCommon_float_or_param_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "transparent" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domConstant,elemTransparent) ); + mea->setElementType( domCommon_transparent_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "transparency" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domConstant,elemTransparency) ); + mea->setElementType( domCommon_float_or_param_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 5, 0, 1 ); + mea->setName( "index_of_refraction" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domConstant,elemIndex_of_refraction) ); + mea->setElementType( domCommon_float_or_param_type::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 5 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domProfile_COMMON::domTechnique::domConstant)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_COMMON::domTechnique::domLambert::create(DAE& dae) +{ + domProfile_COMMON::domTechnique::domLambertRef ref = new domProfile_COMMON::domTechnique::domLambert(dae); + return ref; +} + + +daeMetaElement * +domProfile_COMMON::domTechnique::domLambert::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "lambert" ); + meta->registerClass(domProfile_COMMON::domTechnique::domLambert::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "emission" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domLambert,elemEmission) ); + mea->setElementType( domCommon_color_or_texture_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "ambient" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domLambert,elemAmbient) ); + mea->setElementType( domCommon_color_or_texture_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "diffuse" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domLambert,elemDiffuse) ); + mea->setElementType( domCommon_color_or_texture_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "reflective" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domLambert,elemReflective) ); + mea->setElementType( domCommon_color_or_texture_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "reflectivity" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domLambert,elemReflectivity) ); + mea->setElementType( domCommon_float_or_param_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 5, 0, 1 ); + mea->setName( "transparent" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domLambert,elemTransparent) ); + mea->setElementType( domCommon_transparent_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 6, 0, 1 ); + mea->setName( "transparency" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domLambert,elemTransparency) ); + mea->setElementType( domCommon_float_or_param_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 7, 0, 1 ); + mea->setName( "index_of_refraction" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domLambert,elemIndex_of_refraction) ); + mea->setElementType( domCommon_float_or_param_type::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 7 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domProfile_COMMON::domTechnique::domLambert)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_COMMON::domTechnique::domPhong::create(DAE& dae) +{ + domProfile_COMMON::domTechnique::domPhongRef ref = new domProfile_COMMON::domTechnique::domPhong(dae); + return ref; +} + + +daeMetaElement * +domProfile_COMMON::domTechnique::domPhong::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "phong" ); + meta->registerClass(domProfile_COMMON::domTechnique::domPhong::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "emission" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domPhong,elemEmission) ); + mea->setElementType( domCommon_color_or_texture_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "ambient" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domPhong,elemAmbient) ); + mea->setElementType( domCommon_color_or_texture_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "diffuse" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domPhong,elemDiffuse) ); + mea->setElementType( domCommon_color_or_texture_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "specular" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domPhong,elemSpecular) ); + mea->setElementType( domCommon_color_or_texture_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "shininess" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domPhong,elemShininess) ); + mea->setElementType( domCommon_float_or_param_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 5, 0, 1 ); + mea->setName( "reflective" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domPhong,elemReflective) ); + mea->setElementType( domCommon_color_or_texture_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 6, 0, 1 ); + mea->setName( "reflectivity" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domPhong,elemReflectivity) ); + mea->setElementType( domCommon_float_or_param_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 7, 0, 1 ); + mea->setName( "transparent" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domPhong,elemTransparent) ); + mea->setElementType( domCommon_transparent_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 8, 0, 1 ); + mea->setName( "transparency" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domPhong,elemTransparency) ); + mea->setElementType( domCommon_float_or_param_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 9, 0, 1 ); + mea->setName( "index_of_refraction" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domPhong,elemIndex_of_refraction) ); + mea->setElementType( domCommon_float_or_param_type::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 9 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domProfile_COMMON::domTechnique::domPhong)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_COMMON::domTechnique::domBlinn::create(DAE& dae) +{ + domProfile_COMMON::domTechnique::domBlinnRef ref = new domProfile_COMMON::domTechnique::domBlinn(dae); + return ref; +} + + +daeMetaElement * +domProfile_COMMON::domTechnique::domBlinn::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "blinn" ); + meta->registerClass(domProfile_COMMON::domTechnique::domBlinn::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "emission" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domBlinn,elemEmission) ); + mea->setElementType( domCommon_color_or_texture_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "ambient" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domBlinn,elemAmbient) ); + mea->setElementType( domCommon_color_or_texture_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "diffuse" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domBlinn,elemDiffuse) ); + mea->setElementType( domCommon_color_or_texture_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "specular" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domBlinn,elemSpecular) ); + mea->setElementType( domCommon_color_or_texture_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "shininess" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domBlinn,elemShininess) ); + mea->setElementType( domCommon_float_or_param_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 5, 0, 1 ); + mea->setName( "reflective" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domBlinn,elemReflective) ); + mea->setElementType( domCommon_color_or_texture_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 6, 0, 1 ); + mea->setName( "reflectivity" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domBlinn,elemReflectivity) ); + mea->setElementType( domCommon_float_or_param_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 7, 0, 1 ); + mea->setName( "transparent" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domBlinn,elemTransparent) ); + mea->setElementType( domCommon_transparent_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 8, 0, 1 ); + mea->setName( "transparency" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domBlinn,elemTransparency) ); + mea->setElementType( domCommon_float_or_param_type::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 9, 0, 1 ); + mea->setName( "index_of_refraction" ); + mea->setOffset( daeOffsetOf(domProfile_COMMON::domTechnique::domBlinn,elemIndex_of_refraction) ); + mea->setElementType( domCommon_float_or_param_type::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 9 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domProfile_COMMON::domTechnique::domBlinn)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domProfile_GLES.cpp b/Engine/lib/collada/src/1.4/dom/domProfile_GLES.cpp new file mode 100644 index 000000000..86e22e76a --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domProfile_GLES.cpp @@ -0,0 +1,652 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domProfile_GLES::create(DAE& dae) +{ + domProfile_GLESRef ref = new domProfile_GLES(dae); + return ref; +} + + +daeMetaElement * +domProfile_GLES::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "profile_GLES" ); + meta->registerClass(domProfile_GLES::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domProfile_GLES,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 1, 0, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "image" ); + mea->setOffset( daeOffsetOf(domProfile_GLES,elemImage_array) ); + mea->setElementType( domImage::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "newparam" ); + mea->setOffset( daeOffsetOf(domProfile_GLES,elemNewparam_array) ); + mea->setElementType( domGles_newparam::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3002, 1, -1 ); + mea->setName( "technique" ); + mea->setOffset( daeOffsetOf(domProfile_GLES,elemTechnique_array) ); + mea->setElementType( domProfile_GLES::domTechnique::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3003, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domProfile_GLES,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3003 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domProfile_GLES,_contents)); + meta->addContentsOrder(daeOffsetOf(domProfile_GLES,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domProfile_GLES,_CMData), 1); + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domProfile_GLES , attrId )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: platform + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "platform" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domProfile_GLES , attrPlatform )); + ma->setContainer( meta ); + ma->setDefaultString( "PC"); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_GLES)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_GLES::domTechnique::create(DAE& dae) +{ + domProfile_GLES::domTechniqueRef ref = new domProfile_GLES::domTechnique(dae); + return ref; +} + + +daeMetaElement * +domProfile_GLES::domTechnique::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "technique" ); + meta->registerClass(domProfile_GLES::domTechnique::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domProfile_GLES::domTechnique,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 0, -1 ); + mea->setName( "annotate" ); + mea->setOffset( daeOffsetOf(domProfile_GLES::domTechnique,elemAnnotate_array) ); + mea->setElementType( domFx_annotate_common::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 2, 0, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "image" ); + mea->setOffset( daeOffsetOf(domProfile_GLES::domTechnique,elemImage_array) ); + mea->setElementType( domImage::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "newparam" ); + mea->setOffset( daeOffsetOf(domProfile_GLES::domTechnique,elemNewparam_array) ); + mea->setElementType( domGles_newparam::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "setparam" ); + mea->setOffset( daeOffsetOf(domProfile_GLES::domTechnique,elemSetparam_array) ); + mea->setElementType( domProfile_GLES::domTechnique::domSetparam::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3003, 1, -1 ); + mea->setName( "pass" ); + mea->setOffset( daeOffsetOf(domProfile_GLES::domTechnique,elemPass_array) ); + mea->setElementType( domProfile_GLES::domTechnique::domPass::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3004, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domProfile_GLES::domTechnique,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3004 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domProfile_GLES::domTechnique,_contents)); + meta->addContentsOrder(daeOffsetOf(domProfile_GLES::domTechnique,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domProfile_GLES::domTechnique,_CMData), 1); + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domProfile_GLES::domTechnique , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domProfile_GLES::domTechnique , attrSid )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_GLES::domTechnique)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_GLES::domTechnique::domSetparam::create(DAE& dae) +{ + domProfile_GLES::domTechnique::domSetparamRef ref = new domProfile_GLES::domTechnique::domSetparam(dae); + return ref; +} + + +daeMetaElement * +domProfile_GLES::domTechnique::domSetparam::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "setparam" ); + meta->registerClass(domProfile_GLES::domTechnique::domSetparam::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "annotate" ); + mea->setOffset( daeOffsetOf(domProfile_GLES::domTechnique::domSetparam,elemAnnotate_array) ); + mea->setElementType( domFx_annotate_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "gles_basic_type_common" ); + mea->setOffset( daeOffsetOf(domProfile_GLES::domTechnique::domSetparam,elemGles_basic_type_common) ); + mea->setElementType( domGles_basic_type_common::registerElement(dae) ); + cm->appendChild( new daeMetaGroup( mea, meta, cm, 1, 1, 1 ) ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + // Add attribute: ref + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "ref" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domProfile_GLES::domTechnique::domSetparam , attrRef )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_GLES::domTechnique::domSetparam)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_GLES::domTechnique::domPass::create(DAE& dae) +{ + domProfile_GLES::domTechnique::domPassRef ref = new domProfile_GLES::domTechnique::domPass(dae); + return ref; +} + + +daeMetaElement * +domProfile_GLES::domTechnique::domPass::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "pass" ); + meta->registerClass(domProfile_GLES::domTechnique::domPass::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "annotate" ); + mea->setOffset( daeOffsetOf(domProfile_GLES::domTechnique::domPass,elemAnnotate_array) ); + mea->setElementType( domFx_annotate_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "color_target" ); + mea->setOffset( daeOffsetOf(domProfile_GLES::domTechnique::domPass,elemColor_target) ); + mea->setElementType( domProfile_GLES::domTechnique::domPass::domColor_target::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "depth_target" ); + mea->setOffset( daeOffsetOf(domProfile_GLES::domTechnique::domPass,elemDepth_target) ); + mea->setElementType( domProfile_GLES::domTechnique::domPass::domDepth_target::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "stencil_target" ); + mea->setOffset( daeOffsetOf(domProfile_GLES::domTechnique::domPass,elemStencil_target) ); + mea->setElementType( domProfile_GLES::domTechnique::domPass::domStencil_target::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 4, 0, 1 ); + mea->setName( "color_clear" ); + mea->setOffset( daeOffsetOf(domProfile_GLES::domTechnique::domPass,elemColor_clear) ); + mea->setElementType( domProfile_GLES::domTechnique::domPass::domColor_clear::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 5, 0, 1 ); + mea->setName( "depth_clear" ); + mea->setOffset( daeOffsetOf(domProfile_GLES::domTechnique::domPass,elemDepth_clear) ); + mea->setElementType( domProfile_GLES::domTechnique::domPass::domDepth_clear::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 6, 0, 1 ); + mea->setName( "stencil_clear" ); + mea->setOffset( daeOffsetOf(domProfile_GLES::domTechnique::domPass,elemStencil_clear) ); + mea->setElementType( domProfile_GLES::domTechnique::domPass::domStencil_clear::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 7, 0, 1 ); + mea->setName( "draw" ); + mea->setOffset( daeOffsetOf(domProfile_GLES::domTechnique::domPass,elemDraw) ); + mea->setElementType( domProfile_GLES::domTechnique::domPass::domDraw::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 8, 0, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "gles_pipeline_settings" ); + mea->setOffset( daeOffsetOf(domProfile_GLES::domTechnique::domPass,elemGles_pipeline_settings_array) ); + mea->setElementType( domGles_pipeline_settings::registerElement(dae) ); + cm->appendChild( new daeMetaGroup( mea, meta, cm, 0, 1, 1 ) ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3009, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domProfile_GLES::domTechnique::domPass,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3009 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domProfile_GLES::domTechnique::domPass,_contents)); + meta->addContentsOrder(daeOffsetOf(domProfile_GLES::domTechnique::domPass,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domProfile_GLES::domTechnique::domPass,_CMData), 1); + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domProfile_GLES::domTechnique::domPass , attrSid )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_GLES::domTechnique::domPass)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_GLES::domTechnique::domPass::domColor_target::create(DAE& dae) +{ + domProfile_GLES::domTechnique::domPass::domColor_targetRef ref = new domProfile_GLES::domTechnique::domPass::domColor_target(dae); + return ref; +} + + +daeMetaElement * +domProfile_GLES::domTechnique::domPass::domColor_target::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "color_target" ); + meta->registerClass(domProfile_GLES::domTechnique::domPass::domColor_target::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Gles_rendertarget_common")); + ma->setOffset( daeOffsetOf( domProfile_GLES::domTechnique::domPass::domColor_target , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_GLES::domTechnique::domPass::domColor_target)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_GLES::domTechnique::domPass::domDepth_target::create(DAE& dae) +{ + domProfile_GLES::domTechnique::domPass::domDepth_targetRef ref = new domProfile_GLES::domTechnique::domPass::domDepth_target(dae); + return ref; +} + + +daeMetaElement * +domProfile_GLES::domTechnique::domPass::domDepth_target::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "depth_target" ); + meta->registerClass(domProfile_GLES::domTechnique::domPass::domDepth_target::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Gles_rendertarget_common")); + ma->setOffset( daeOffsetOf( domProfile_GLES::domTechnique::domPass::domDepth_target , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_GLES::domTechnique::domPass::domDepth_target)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_GLES::domTechnique::domPass::domStencil_target::create(DAE& dae) +{ + domProfile_GLES::domTechnique::domPass::domStencil_targetRef ref = new domProfile_GLES::domTechnique::domPass::domStencil_target(dae); + return ref; +} + + +daeMetaElement * +domProfile_GLES::domTechnique::domPass::domStencil_target::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "stencil_target" ); + meta->registerClass(domProfile_GLES::domTechnique::domPass::domStencil_target::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Gles_rendertarget_common")); + ma->setOffset( daeOffsetOf( domProfile_GLES::domTechnique::domPass::domStencil_target , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_GLES::domTechnique::domPass::domStencil_target)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_GLES::domTechnique::domPass::domColor_clear::create(DAE& dae) +{ + domProfile_GLES::domTechnique::domPass::domColor_clearRef ref = new domProfile_GLES::domTechnique::domPass::domColor_clear(dae); + return ref; +} + + +daeMetaElement * +domProfile_GLES::domTechnique::domPass::domColor_clear::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "color_clear" ); + meta->registerClass(domProfile_GLES::domTechnique::domPass::domColor_clear::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_color_common")); + ma->setOffset( daeOffsetOf( domProfile_GLES::domTechnique::domPass::domColor_clear , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_GLES::domTechnique::domPass::domColor_clear)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_GLES::domTechnique::domPass::domDepth_clear::create(DAE& dae) +{ + domProfile_GLES::domTechnique::domPass::domDepth_clearRef ref = new domProfile_GLES::domTechnique::domPass::domDepth_clear(dae); + return ref; +} + + +daeMetaElement * +domProfile_GLES::domTechnique::domPass::domDepth_clear::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "depth_clear" ); + meta->registerClass(domProfile_GLES::domTechnique::domPass::domDepth_clear::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domProfile_GLES::domTechnique::domPass::domDepth_clear , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_GLES::domTechnique::domPass::domDepth_clear)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_GLES::domTechnique::domPass::domStencil_clear::create(DAE& dae) +{ + domProfile_GLES::domTechnique::domPass::domStencil_clearRef ref = new domProfile_GLES::domTechnique::domPass::domStencil_clear(dae); + return ref; +} + + +daeMetaElement * +domProfile_GLES::domTechnique::domPass::domStencil_clear::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "stencil_clear" ); + meta->registerClass(domProfile_GLES::domTechnique::domPass::domStencil_clear::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsByte")); + ma->setOffset( daeOffsetOf( domProfile_GLES::domTechnique::domPass::domStencil_clear , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_GLES::domTechnique::domPass::domStencil_clear)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_GLES::domTechnique::domPass::domDraw::create(DAE& dae) +{ + domProfile_GLES::domTechnique::domPass::domDrawRef ref = new domProfile_GLES::domTechnique::domPass::domDraw(dae); + return ref; +} + + +daeMetaElement * +domProfile_GLES::domTechnique::domPass::domDraw::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "draw" ); + meta->registerClass(domProfile_GLES::domTechnique::domPass::domDraw::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_draw_common")); + ma->setOffset( daeOffsetOf( domProfile_GLES::domTechnique::domPass::domDraw , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_GLES::domTechnique::domPass::domDraw)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domProfile_GLSL.cpp b/Engine/lib/collada/src/1.4/dom/domProfile_GLSL.cpp new file mode 100644 index 000000000..adb2567bb --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domProfile_GLSL.cpp @@ -0,0 +1,702 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domProfile_GLSL::create(DAE& dae) +{ + domProfile_GLSLRef ref = new domProfile_GLSL(dae); + return ref; +} + + +daeMetaElement * +domProfile_GLSL::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "profile_GLSL" ); + meta->registerClass(domProfile_GLSL::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 1, 0, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "code" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL,elemCode_array) ); + mea->setElementType( domFx_code_profile::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "include" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL,elemInclude_array) ); + mea->setElementType( domFx_include_common::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm = new daeMetaChoice( meta, cm, 1, 3002, 0, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "image" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL,elemImage_array) ); + mea->setElementType( domImage::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "newparam" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL,elemNewparam_array) ); + mea->setElementType( domGlsl_newparam::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementArrayAttribute( meta, cm, 6003, 1, -1 ); + mea->setName( "technique" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL,elemTechnique_array) ); + mea->setElementType( domProfile_GLSL::domTechnique::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 6004, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 6004 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domProfile_GLSL,_contents)); + meta->addContentsOrder(daeOffsetOf(domProfile_GLSL,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domProfile_GLSL,_CMData), 2); + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domProfile_GLSL , attrId )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_GLSL)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_GLSL::domTechnique::create(DAE& dae) +{ + domProfile_GLSL::domTechniqueRef ref = new domProfile_GLSL::domTechnique(dae); + return ref; +} + + +daeMetaElement * +domProfile_GLSL::domTechnique::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "technique" ); + meta->registerClass(domProfile_GLSL::domTechnique::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "annotate" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL::domTechnique,elemAnnotate_array) ); + mea->setElementType( domFx_annotate_common::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 1, 0, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "code" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL::domTechnique,elemCode_array) ); + mea->setElementType( domFx_code_profile::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "include" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL::domTechnique,elemInclude_array) ); + mea->setElementType( domFx_include_common::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm = new daeMetaChoice( meta, cm, 1, 3002, 0, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "image" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL::domTechnique,elemImage_array) ); + mea->setElementType( domImage::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "newparam" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL::domTechnique,elemNewparam_array) ); + mea->setElementType( domGlsl_newparam::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "setparam" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL::domTechnique,elemSetparam_array) ); + mea->setElementType( domGlsl_setparam::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementArrayAttribute( meta, cm, 6003, 1, -1 ); + mea->setName( "pass" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL::domTechnique,elemPass_array) ); + mea->setElementType( domProfile_GLSL::domTechnique::domPass::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 6004, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL::domTechnique,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 6004 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domProfile_GLSL::domTechnique,_contents)); + meta->addContentsOrder(daeOffsetOf(domProfile_GLSL::domTechnique,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domProfile_GLSL::domTechnique,_CMData), 2); + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domProfile_GLSL::domTechnique , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domProfile_GLSL::domTechnique , attrSid )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_GLSL::domTechnique)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_GLSL::domTechnique::domPass::create(DAE& dae) +{ + domProfile_GLSL::domTechnique::domPassRef ref = new domProfile_GLSL::domTechnique::domPass(dae); + return ref; +} + + +daeMetaElement * +domProfile_GLSL::domTechnique::domPass::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "pass" ); + meta->registerClass(domProfile_GLSL::domTechnique::domPass::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "annotate" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL::domTechnique::domPass,elemAnnotate_array) ); + mea->setElementType( domFx_annotate_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 0, -1 ); + mea->setName( "color_target" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL::domTechnique::domPass,elemColor_target_array) ); + mea->setElementType( domFx_colortarget_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "depth_target" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL::domTechnique::domPass,elemDepth_target_array) ); + mea->setElementType( domFx_depthtarget_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3, 0, -1 ); + mea->setName( "stencil_target" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL::domTechnique::domPass,elemStencil_target_array) ); + mea->setElementType( domFx_stenciltarget_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 4, 0, -1 ); + mea->setName( "color_clear" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL::domTechnique::domPass,elemColor_clear_array) ); + mea->setElementType( domFx_clearcolor_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 5, 0, -1 ); + mea->setName( "depth_clear" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL::domTechnique::domPass,elemDepth_clear_array) ); + mea->setElementType( domFx_cleardepth_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 6, 0, -1 ); + mea->setName( "stencil_clear" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL::domTechnique::domPass,elemStencil_clear_array) ); + mea->setElementType( domFx_clearstencil_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 7, 0, 1 ); + mea->setName( "draw" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL::domTechnique::domPass,elemDraw) ); + mea->setElementType( domProfile_GLSL::domTechnique::domPass::domDraw::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 8, 1, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "gl_pipeline_settings" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL::domTechnique::domPass,elemGl_pipeline_settings_array) ); + mea->setElementType( domGl_pipeline_settings::registerElement(dae) ); + cm->appendChild( new daeMetaGroup( mea, meta, cm, 0, 1, 1 ) ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "shader" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL::domTechnique::domPass,elemShader_array) ); + mea->setElementType( domProfile_GLSL::domTechnique::domPass::domShader::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3009, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL::domTechnique::domPass,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3009 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domProfile_GLSL::domTechnique::domPass,_contents)); + meta->addContentsOrder(daeOffsetOf(domProfile_GLSL::domTechnique::domPass,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domProfile_GLSL::domTechnique::domPass,_CMData), 1); + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domProfile_GLSL::domTechnique::domPass , attrSid )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_GLSL::domTechnique::domPass)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_GLSL::domTechnique::domPass::domDraw::create(DAE& dae) +{ + domProfile_GLSL::domTechnique::domPass::domDrawRef ref = new domProfile_GLSL::domTechnique::domPass::domDraw(dae); + return ref; +} + + +daeMetaElement * +domProfile_GLSL::domTechnique::domPass::domDraw::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "draw" ); + meta->registerClass(domProfile_GLSL::domTechnique::domPass::domDraw::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Fx_draw_common")); + ma->setOffset( daeOffsetOf( domProfile_GLSL::domTechnique::domPass::domDraw , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_GLSL::domTechnique::domPass::domDraw)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_GLSL::domTechnique::domPass::domShader::create(DAE& dae) +{ + domProfile_GLSL::domTechnique::domPass::domShaderRef ref = new domProfile_GLSL::domTechnique::domPass::domShader(dae); + return ref; +} + + +daeMetaElement * +domProfile_GLSL::domTechnique::domPass::domShader::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "shader" ); + meta->registerClass(domProfile_GLSL::domTechnique::domPass::domShader::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "annotate" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL::domTechnique::domPass::domShader,elemAnnotate_array) ); + mea->setElementType( domFx_annotate_common::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaSequence( meta, cm, 1, 0, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "compiler_target" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL::domTechnique::domPass::domShader,elemCompiler_target) ); + mea->setElementType( domProfile_GLSL::domTechnique::domPass::domShader::domCompiler_target::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "compiler_options" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL::domTechnique::domPass::domShader,elemCompiler_options) ); + mea->setElementType( domProfile_GLSL::domTechnique::domPass::domShader::domCompiler_options::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementAttribute( meta, cm, 3, 1, 1 ); + mea->setName( "name" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL::domTechnique::domPass::domShader,elemName) ); + mea->setElementType( domProfile_GLSL::domTechnique::domPass::domShader::domName::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 4, 0, -1 ); + mea->setName( "bind" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL::domTechnique::domPass::domShader,elemBind_array) ); + mea->setElementType( domProfile_GLSL::domTechnique::domPass::domShader::domBind::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 4 ); + meta->setCMRoot( cm ); + + // Add attribute: stage + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "stage" ); + ma->setType( dae.getAtomicTypes().get("Glsl_pipeline_stage")); + ma->setOffset( daeOffsetOf( domProfile_GLSL::domTechnique::domPass::domShader , attrStage )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_GLSL::domTechnique::domPass::domShader)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_GLSL::domTechnique::domPass::domShader::domCompiler_target::create(DAE& dae) +{ + domProfile_GLSL::domTechnique::domPass::domShader::domCompiler_targetRef ref = new domProfile_GLSL::domTechnique::domPass::domShader::domCompiler_target(dae); + return ref; +} + + +daeMetaElement * +domProfile_GLSL::domTechnique::domPass::domShader::domCompiler_target::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "compiler_target" ); + meta->registerClass(domProfile_GLSL::domTechnique::domPass::domShader::domCompiler_target::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNMTOKEN")); + ma->setOffset( daeOffsetOf( domProfile_GLSL::domTechnique::domPass::domShader::domCompiler_target , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_GLSL::domTechnique::domPass::domShader::domCompiler_target)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_GLSL::domTechnique::domPass::domShader::domCompiler_options::create(DAE& dae) +{ + domProfile_GLSL::domTechnique::domPass::domShader::domCompiler_optionsRef ref = new domProfile_GLSL::domTechnique::domPass::domShader::domCompiler_options(dae); + return ref; +} + + +daeMetaElement * +domProfile_GLSL::domTechnique::domPass::domShader::domCompiler_options::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "compiler_options" ); + meta->registerClass(domProfile_GLSL::domTechnique::domPass::domShader::domCompiler_options::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsString")); + ma->setOffset( daeOffsetOf( domProfile_GLSL::domTechnique::domPass::domShader::domCompiler_options , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_GLSL::domTechnique::domPass::domShader::domCompiler_options)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_GLSL::domTechnique::domPass::domShader::domName::create(DAE& dae) +{ + domProfile_GLSL::domTechnique::domPass::domShader::domNameRef ref = new domProfile_GLSL::domTechnique::domPass::domShader::domName(dae); + return ref; +} + + +daeMetaElement * +domProfile_GLSL::domTechnique::domPass::domShader::domName::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "name" ); + meta->registerClass(domProfile_GLSL::domTechnique::domPass::domShader::domName::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domProfile_GLSL::domTechnique::domPass::domShader::domName , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: source + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "source" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domProfile_GLSL::domTechnique::domPass::domShader::domName , attrSource )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_GLSL::domTechnique::domPass::domShader::domName)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_GLSL::domTechnique::domPass::domShader::domBind::create(DAE& dae) +{ + domProfile_GLSL::domTechnique::domPass::domShader::domBindRef ref = new domProfile_GLSL::domTechnique::domPass::domShader::domBind(dae); + return ref; +} + + +daeMetaElement * +domProfile_GLSL::domTechnique::domPass::domShader::domBind::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bind" ); + meta->registerClass(domProfile_GLSL::domTechnique::domPass::domShader::domBind::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "glsl_param_type" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL::domTechnique::domPass::domShader::domBind,elemGlsl_param_type) ); + mea->setElementType( domGlsl_param_type::registerElement(dae) ); + cm->appendChild( new daeMetaGroup( mea, meta, cm, 0, 1, 1 ) ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "param" ); + mea->setOffset( daeOffsetOf(domProfile_GLSL::domTechnique::domPass::domShader::domBind,elemParam) ); + mea->setElementType( domProfile_GLSL::domTechnique::domPass::domShader::domBind::domParam::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domProfile_GLSL::domTechnique::domPass::domShader::domBind,_contents)); + meta->addContentsOrder(daeOffsetOf(domProfile_GLSL::domTechnique::domPass::domShader::domBind,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domProfile_GLSL::domTechnique::domPass::domShader::domBind,_CMData), 1); + // Add attribute: symbol + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "symbol" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domProfile_GLSL::domTechnique::domPass::domShader::domBind , attrSymbol )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_GLSL::domTechnique::domPass::domShader::domBind)); + meta->validate(); + + return meta; +} + +daeElementRef +domProfile_GLSL::domTechnique::domPass::domShader::domBind::domParam::create(DAE& dae) +{ + domProfile_GLSL::domTechnique::domPass::domShader::domBind::domParamRef ref = new domProfile_GLSL::domTechnique::domPass::domShader::domBind::domParam(dae); + return ref; +} + + +daeMetaElement * +domProfile_GLSL::domTechnique::domPass::domShader::domBind::domParam::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "param" ); + meta->registerClass(domProfile_GLSL::domTechnique::domPass::domShader::domBind::domParam::create); + + meta->setIsInnerClass( true ); + + // Add attribute: ref + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "ref" ); + ma->setType( dae.getAtomicTypes().get("xsString")); + ma->setOffset( daeOffsetOf( domProfile_GLSL::domTechnique::domPass::domShader::domBind::domParam , attrRef )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domProfile_GLSL::domTechnique::domPass::domShader::domBind::domParam)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domRigid_body.cpp b/Engine/lib/collada/src/1.4/dom/domRigid_body.cpp new file mode 100644 index 000000000..15f2ab519 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domRigid_body.cpp @@ -0,0 +1,475 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domRigid_body::create(DAE& dae) +{ + domRigid_bodyRef ref = new domRigid_body(dae); + return ref; +} + + +daeMetaElement * +domRigid_body::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "rigid_body" ); + meta->registerClass(domRigid_body::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "technique_common" ); + mea->setOffset( daeOffsetOf(domRigid_body,elemTechnique_common) ); + mea->setElementType( domRigid_body::domTechnique_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 0, -1 ); + mea->setName( "technique" ); + mea->setOffset( daeOffsetOf(domRigid_body,elemTechnique_array) ); + mea->setElementType( domTechnique::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domRigid_body,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domRigid_body , attrSid )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domRigid_body , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domRigid_body)); + meta->validate(); + + return meta; +} + +daeElementRef +domRigid_body::domTechnique_common::create(DAE& dae) +{ + domRigid_body::domTechnique_commonRef ref = new domRigid_body::domTechnique_common(dae); + return ref; +} + + +daeMetaElement * +domRigid_body::domTechnique_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "technique_common" ); + meta->registerClass(domRigid_body::domTechnique_common::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "dynamic" ); + mea->setOffset( daeOffsetOf(domRigid_body::domTechnique_common,elemDynamic) ); + mea->setElementType( domRigid_body::domTechnique_common::domDynamic::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "mass" ); + mea->setOffset( daeOffsetOf(domRigid_body::domTechnique_common,elemMass) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "mass_frame" ); + mea->setOffset( daeOffsetOf(domRigid_body::domTechnique_common,elemMass_frame) ); + mea->setElementType( domRigid_body::domTechnique_common::domMass_frame::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "inertia" ); + mea->setOffset( daeOffsetOf(domRigid_body::domTechnique_common,elemInertia) ); + mea->setElementType( domTargetableFloat3::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 4, 0, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "instance_physics_material" ); + mea->setOffset( daeOffsetOf(domRigid_body::domTechnique_common,elemInstance_physics_material) ); + mea->setElementType( domInstance_physics_material::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "physics_material" ); + mea->setOffset( daeOffsetOf(domRigid_body::domTechnique_common,elemPhysics_material) ); + mea->setElementType( domPhysics_material::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementArrayAttribute( meta, cm, 5, 1, -1 ); + mea->setName( "shape" ); + mea->setOffset( daeOffsetOf(domRigid_body::domTechnique_common,elemShape_array) ); + mea->setElementType( domRigid_body::domTechnique_common::domShape::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 5 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domRigid_body::domTechnique_common,_contents)); + meta->addContentsOrder(daeOffsetOf(domRigid_body::domTechnique_common,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domRigid_body::domTechnique_common,_CMData), 1); + meta->setElementSize(sizeof(domRigid_body::domTechnique_common)); + meta->validate(); + + return meta; +} + +daeElementRef +domRigid_body::domTechnique_common::domDynamic::create(DAE& dae) +{ + domRigid_body::domTechnique_common::domDynamicRef ref = new domRigid_body::domTechnique_common::domDynamic(dae); + return ref; +} + + +daeMetaElement * +domRigid_body::domTechnique_common::domDynamic::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "dynamic" ); + meta->registerClass(domRigid_body::domTechnique_common::domDynamic::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domRigid_body::domTechnique_common::domDynamic , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domRigid_body::domTechnique_common::domDynamic , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domRigid_body::domTechnique_common::domDynamic)); + meta->validate(); + + return meta; +} + +daeElementRef +domRigid_body::domTechnique_common::domMass_frame::create(DAE& dae) +{ + domRigid_body::domTechnique_common::domMass_frameRef ref = new domRigid_body::domTechnique_common::domMass_frame(dae); + return ref; +} + + +daeMetaElement * +domRigid_body::domTechnique_common::domMass_frame::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "mass_frame" ); + meta->registerClass(domRigid_body::domTechnique_common::domMass_frame::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 1, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "translate" ); + mea->setOffset( daeOffsetOf(domRigid_body::domTechnique_common::domMass_frame,elemTranslate_array) ); + mea->setElementType( domTranslate::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "rotate" ); + mea->setOffset( daeOffsetOf(domRigid_body::domTechnique_common::domMass_frame,elemRotate_array) ); + mea->setElementType( domRotate::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3000 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domRigid_body::domTechnique_common::domMass_frame,_contents)); + meta->addContentsOrder(daeOffsetOf(domRigid_body::domTechnique_common::domMass_frame,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domRigid_body::domTechnique_common::domMass_frame,_CMData), 1); + meta->setElementSize(sizeof(domRigid_body::domTechnique_common::domMass_frame)); + meta->validate(); + + return meta; +} + +daeElementRef +domRigid_body::domTechnique_common::domShape::create(DAE& dae) +{ + domRigid_body::domTechnique_common::domShapeRef ref = new domRigid_body::domTechnique_common::domShape(dae); + return ref; +} + + +daeMetaElement * +domRigid_body::domTechnique_common::domShape::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "shape" ); + meta->registerClass(domRigid_body::domTechnique_common::domShape::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "hollow" ); + mea->setOffset( daeOffsetOf(domRigid_body::domTechnique_common::domShape,elemHollow) ); + mea->setElementType( domRigid_body::domTechnique_common::domShape::domHollow::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "mass" ); + mea->setOffset( daeOffsetOf(domRigid_body::domTechnique_common::domShape,elemMass) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "density" ); + mea->setOffset( daeOffsetOf(domRigid_body::domTechnique_common::domShape,elemDensity) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 3, 0, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "instance_physics_material" ); + mea->setOffset( daeOffsetOf(domRigid_body::domTechnique_common::domShape,elemInstance_physics_material) ); + mea->setElementType( domInstance_physics_material::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "physics_material" ); + mea->setOffset( daeOffsetOf(domRigid_body::domTechnique_common::domShape,elemPhysics_material) ); + mea->setElementType( domPhysics_material::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm = new daeMetaChoice( meta, cm, 1, 4, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "instance_geometry" ); + mea->setOffset( daeOffsetOf(domRigid_body::domTechnique_common::domShape,elemInstance_geometry) ); + mea->setElementType( domInstance_geometry::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "plane" ); + mea->setOffset( daeOffsetOf(domRigid_body::domTechnique_common::domShape,elemPlane) ); + mea->setElementType( domPlane::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "box" ); + mea->setOffset( daeOffsetOf(domRigid_body::domTechnique_common::domShape,elemBox) ); + mea->setElementType( domBox::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "sphere" ); + mea->setOffset( daeOffsetOf(domRigid_body::domTechnique_common::domShape,elemSphere) ); + mea->setElementType( domSphere::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "cylinder" ); + mea->setOffset( daeOffsetOf(domRigid_body::domTechnique_common::domShape,elemCylinder) ); + mea->setElementType( domCylinder::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "tapered_cylinder" ); + mea->setOffset( daeOffsetOf(domRigid_body::domTechnique_common::domShape,elemTapered_cylinder) ); + mea->setElementType( domTapered_cylinder::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "capsule" ); + mea->setOffset( daeOffsetOf(domRigid_body::domTechnique_common::domShape,elemCapsule) ); + mea->setElementType( domCapsule::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "tapered_capsule" ); + mea->setOffset( daeOffsetOf(domRigid_body::domTechnique_common::domShape,elemTapered_capsule) ); + mea->setElementType( domTapered_capsule::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm = new daeMetaChoice( meta, cm, 2, 5, 0, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "translate" ); + mea->setOffset( daeOffsetOf(domRigid_body::domTechnique_common::domShape,elemTranslate_array) ); + mea->setElementType( domTranslate::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "rotate" ); + mea->setOffset( daeOffsetOf(domRigid_body::domTechnique_common::domShape,elemRotate_array) ); + mea->setElementType( domRotate::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3006, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domRigid_body::domTechnique_common::domShape,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3006 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domRigid_body::domTechnique_common::domShape,_contents)); + meta->addContentsOrder(daeOffsetOf(domRigid_body::domTechnique_common::domShape,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domRigid_body::domTechnique_common::domShape,_CMData), 3); + meta->setElementSize(sizeof(domRigid_body::domTechnique_common::domShape)); + meta->validate(); + + return meta; +} + +daeElementRef +domRigid_body::domTechnique_common::domShape::domHollow::create(DAE& dae) +{ + domRigid_body::domTechnique_common::domShape::domHollowRef ref = new domRigid_body::domTechnique_common::domShape::domHollow(dae); + return ref; +} + + +daeMetaElement * +domRigid_body::domTechnique_common::domShape::domHollow::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "hollow" ); + meta->registerClass(domRigid_body::domTechnique_common::domShape::domHollow::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domRigid_body::domTechnique_common::domShape::domHollow , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domRigid_body::domTechnique_common::domShape::domHollow , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domRigid_body::domTechnique_common::domShape::domHollow)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domRigid_constraint.cpp b/Engine/lib/collada/src/1.4/dom/domRigid_constraint.cpp new file mode 100644 index 000000000..3db47816d --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domRigid_constraint.cpp @@ -0,0 +1,673 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domRigid_constraint::create(DAE& dae) +{ + domRigid_constraintRef ref = new domRigid_constraint(dae); + return ref; +} + + +daeMetaElement * +domRigid_constraint::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "rigid_constraint" ); + meta->registerClass(domRigid_constraint::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "ref_attachment" ); + mea->setOffset( daeOffsetOf(domRigid_constraint,elemRef_attachment) ); + mea->setElementType( domRigid_constraint::domRef_attachment::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "attachment" ); + mea->setOffset( daeOffsetOf(domRigid_constraint,elemAttachment) ); + mea->setElementType( domRigid_constraint::domAttachment::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 1, 1 ); + mea->setName( "technique_common" ); + mea->setOffset( daeOffsetOf(domRigid_constraint,elemTechnique_common) ); + mea->setElementType( domRigid_constraint::domTechnique_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3, 0, -1 ); + mea->setName( "technique" ); + mea->setOffset( daeOffsetOf(domRigid_constraint,elemTechnique_array) ); + mea->setElementType( domTechnique::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 4, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domRigid_constraint,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 4 ); + meta->setCMRoot( cm ); + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domRigid_constraint , attrSid )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domRigid_constraint , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domRigid_constraint)); + meta->validate(); + + return meta; +} + +daeElementRef +domRigid_constraint::domRef_attachment::create(DAE& dae) +{ + domRigid_constraint::domRef_attachmentRef ref = new domRigid_constraint::domRef_attachment(dae); + return ref; +} + + +daeMetaElement * +domRigid_constraint::domRef_attachment::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "ref_attachment" ); + meta->registerClass(domRigid_constraint::domRef_attachment::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 0, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "translate" ); + mea->setOffset( daeOffsetOf(domRigid_constraint::domRef_attachment,elemTranslate_array) ); + mea->setElementType( domTranslate::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "rotate" ); + mea->setOffset( daeOffsetOf(domRigid_constraint::domRef_attachment,elemRotate_array) ); + mea->setElementType( domRotate::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domRigid_constraint::domRef_attachment,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3000 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domRigid_constraint::domRef_attachment,_contents)); + meta->addContentsOrder(daeOffsetOf(domRigid_constraint::domRef_attachment,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domRigid_constraint::domRef_attachment,_CMData), 1); + // Add attribute: rigid_body + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "rigid_body" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domRigid_constraint::domRef_attachment , attrRigid_body )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domRigid_constraint::domRef_attachment)); + meta->validate(); + + return meta; +} + +daeElementRef +domRigid_constraint::domAttachment::create(DAE& dae) +{ + domRigid_constraint::domAttachmentRef ref = new domRigid_constraint::domAttachment(dae); + return ref; +} + + +daeMetaElement * +domRigid_constraint::domAttachment::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "attachment" ); + meta->registerClass(domRigid_constraint::domAttachment::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaChoice( meta, cm, 0, 0, 0, -1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "translate" ); + mea->setOffset( daeOffsetOf(domRigid_constraint::domAttachment,elemTranslate_array) ); + mea->setElementType( domTranslate::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "rotate" ); + mea->setOffset( daeOffsetOf(domRigid_constraint::domAttachment,elemRotate_array) ); + mea->setElementType( domRotate::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domRigid_constraint::domAttachment,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3000 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domRigid_constraint::domAttachment,_contents)); + meta->addContentsOrder(daeOffsetOf(domRigid_constraint::domAttachment,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domRigid_constraint::domAttachment,_CMData), 1); + // Add attribute: rigid_body + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "rigid_body" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domRigid_constraint::domAttachment , attrRigid_body )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domRigid_constraint::domAttachment)); + meta->validate(); + + return meta; +} + +daeElementRef +domRigid_constraint::domTechnique_common::create(DAE& dae) +{ + domRigid_constraint::domTechnique_commonRef ref = new domRigid_constraint::domTechnique_common(dae); + return ref; +} + + +daeMetaElement * +domRigid_constraint::domTechnique_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "technique_common" ); + meta->registerClass(domRigid_constraint::domTechnique_common::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "enabled" ); + mea->setOffset( daeOffsetOf(domRigid_constraint::domTechnique_common,elemEnabled) ); + mea->setElementType( domRigid_constraint::domTechnique_common::domEnabled::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "interpenetrate" ); + mea->setOffset( daeOffsetOf(domRigid_constraint::domTechnique_common,elemInterpenetrate) ); + mea->setElementType( domRigid_constraint::domTechnique_common::domInterpenetrate::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "limits" ); + mea->setOffset( daeOffsetOf(domRigid_constraint::domTechnique_common,elemLimits) ); + mea->setElementType( domRigid_constraint::domTechnique_common::domLimits::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 0, 1 ); + mea->setName( "spring" ); + mea->setOffset( daeOffsetOf(domRigid_constraint::domTechnique_common,elemSpring) ); + mea->setElementType( domRigid_constraint::domTechnique_common::domSpring::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domRigid_constraint::domTechnique_common)); + meta->validate(); + + return meta; +} + +daeElementRef +domRigid_constraint::domTechnique_common::domEnabled::create(DAE& dae) +{ + domRigid_constraint::domTechnique_common::domEnabledRef ref = new domRigid_constraint::domTechnique_common::domEnabled(dae); + return ref; +} + + +daeMetaElement * +domRigid_constraint::domTechnique_common::domEnabled::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "enabled" ); + meta->registerClass(domRigid_constraint::domTechnique_common::domEnabled::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domRigid_constraint::domTechnique_common::domEnabled , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domRigid_constraint::domTechnique_common::domEnabled , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domRigid_constraint::domTechnique_common::domEnabled)); + meta->validate(); + + return meta; +} + +daeElementRef +domRigid_constraint::domTechnique_common::domInterpenetrate::create(DAE& dae) +{ + domRigid_constraint::domTechnique_common::domInterpenetrateRef ref = new domRigid_constraint::domTechnique_common::domInterpenetrate(dae); + return ref; +} + + +daeMetaElement * +domRigid_constraint::domTechnique_common::domInterpenetrate::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "interpenetrate" ); + meta->registerClass(domRigid_constraint::domTechnique_common::domInterpenetrate::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domRigid_constraint::domTechnique_common::domInterpenetrate , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domRigid_constraint::domTechnique_common::domInterpenetrate , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domRigid_constraint::domTechnique_common::domInterpenetrate)); + meta->validate(); + + return meta; +} + +daeElementRef +domRigid_constraint::domTechnique_common::domLimits::create(DAE& dae) +{ + domRigid_constraint::domTechnique_common::domLimitsRef ref = new domRigid_constraint::domTechnique_common::domLimits(dae); + return ref; +} + + +daeMetaElement * +domRigid_constraint::domTechnique_common::domLimits::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "limits" ); + meta->registerClass(domRigid_constraint::domTechnique_common::domLimits::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "swing_cone_and_twist" ); + mea->setOffset( daeOffsetOf(domRigid_constraint::domTechnique_common::domLimits,elemSwing_cone_and_twist) ); + mea->setElementType( domRigid_constraint::domTechnique_common::domLimits::domSwing_cone_and_twist::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "linear" ); + mea->setOffset( daeOffsetOf(domRigid_constraint::domTechnique_common::domLimits,elemLinear) ); + mea->setElementType( domRigid_constraint::domTechnique_common::domLimits::domLinear::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domRigid_constraint::domTechnique_common::domLimits)); + meta->validate(); + + return meta; +} + +daeElementRef +domRigid_constraint::domTechnique_common::domLimits::domSwing_cone_and_twist::create(DAE& dae) +{ + domRigid_constraint::domTechnique_common::domLimits::domSwing_cone_and_twistRef ref = new domRigid_constraint::domTechnique_common::domLimits::domSwing_cone_and_twist(dae); + return ref; +} + + +daeMetaElement * +domRigid_constraint::domTechnique_common::domLimits::domSwing_cone_and_twist::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "swing_cone_and_twist" ); + meta->registerClass(domRigid_constraint::domTechnique_common::domLimits::domSwing_cone_and_twist::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "min" ); + mea->setOffset( daeOffsetOf(domRigid_constraint::domTechnique_common::domLimits::domSwing_cone_and_twist,elemMin) ); + mea->setElementType( domTargetableFloat3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "max" ); + mea->setOffset( daeOffsetOf(domRigid_constraint::domTechnique_common::domLimits::domSwing_cone_and_twist,elemMax) ); + mea->setElementType( domTargetableFloat3::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domRigid_constraint::domTechnique_common::domLimits::domSwing_cone_and_twist)); + meta->validate(); + + return meta; +} + +daeElementRef +domRigid_constraint::domTechnique_common::domLimits::domLinear::create(DAE& dae) +{ + domRigid_constraint::domTechnique_common::domLimits::domLinearRef ref = new domRigid_constraint::domTechnique_common::domLimits::domLinear(dae); + return ref; +} + + +daeMetaElement * +domRigid_constraint::domTechnique_common::domLimits::domLinear::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "linear" ); + meta->registerClass(domRigid_constraint::domTechnique_common::domLimits::domLinear::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "min" ); + mea->setOffset( daeOffsetOf(domRigid_constraint::domTechnique_common::domLimits::domLinear,elemMin) ); + mea->setElementType( domTargetableFloat3::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "max" ); + mea->setOffset( daeOffsetOf(domRigid_constraint::domTechnique_common::domLimits::domLinear,elemMax) ); + mea->setElementType( domTargetableFloat3::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domRigid_constraint::domTechnique_common::domLimits::domLinear)); + meta->validate(); + + return meta; +} + +daeElementRef +domRigid_constraint::domTechnique_common::domSpring::create(DAE& dae) +{ + domRigid_constraint::domTechnique_common::domSpringRef ref = new domRigid_constraint::domTechnique_common::domSpring(dae); + return ref; +} + + +daeMetaElement * +domRigid_constraint::domTechnique_common::domSpring::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "spring" ); + meta->registerClass(domRigid_constraint::domTechnique_common::domSpring::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "angular" ); + mea->setOffset( daeOffsetOf(domRigid_constraint::domTechnique_common::domSpring,elemAngular) ); + mea->setElementType( domRigid_constraint::domTechnique_common::domSpring::domAngular::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "linear" ); + mea->setOffset( daeOffsetOf(domRigid_constraint::domTechnique_common::domSpring,elemLinear) ); + mea->setElementType( domRigid_constraint::domTechnique_common::domSpring::domLinear::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domRigid_constraint::domTechnique_common::domSpring)); + meta->validate(); + + return meta; +} + +daeElementRef +domRigid_constraint::domTechnique_common::domSpring::domAngular::create(DAE& dae) +{ + domRigid_constraint::domTechnique_common::domSpring::domAngularRef ref = new domRigid_constraint::domTechnique_common::domSpring::domAngular(dae); + return ref; +} + + +daeMetaElement * +domRigid_constraint::domTechnique_common::domSpring::domAngular::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "angular" ); + meta->registerClass(domRigid_constraint::domTechnique_common::domSpring::domAngular::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "stiffness" ); + mea->setOffset( daeOffsetOf(domRigid_constraint::domTechnique_common::domSpring::domAngular,elemStiffness) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "damping" ); + mea->setOffset( daeOffsetOf(domRigid_constraint::domTechnique_common::domSpring::domAngular,elemDamping) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "target_value" ); + mea->setOffset( daeOffsetOf(domRigid_constraint::domTechnique_common::domSpring::domAngular,elemTarget_value) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domRigid_constraint::domTechnique_common::domSpring::domAngular)); + meta->validate(); + + return meta; +} + +daeElementRef +domRigid_constraint::domTechnique_common::domSpring::domLinear::create(DAE& dae) +{ + domRigid_constraint::domTechnique_common::domSpring::domLinearRef ref = new domRigid_constraint::domTechnique_common::domSpring::domLinear(dae); + return ref; +} + + +daeMetaElement * +domRigid_constraint::domTechnique_common::domSpring::domLinear::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "linear" ); + meta->registerClass(domRigid_constraint::domTechnique_common::domSpring::domLinear::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "stiffness" ); + mea->setOffset( daeOffsetOf(domRigid_constraint::domTechnique_common::domSpring::domLinear,elemStiffness) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "damping" ); + mea->setOffset( daeOffsetOf(domRigid_constraint::domTechnique_common::domSpring::domLinear,elemDamping) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "target_value" ); + mea->setOffset( daeOffsetOf(domRigid_constraint::domTechnique_common::domSpring::domLinear,elemTarget_value) ); + mea->setElementType( domTargetableFloat::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domRigid_constraint::domTechnique_common::domSpring::domLinear)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domRotate.cpp b/Engine/lib/collada/src/1.4/dom/domRotate.cpp new file mode 100644 index 000000000..5d99de836 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domRotate.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domRotate::create(DAE& dae) +{ + domRotateRef ref = new domRotate(dae); + return ref; +} + + +daeMetaElement * +domRotate::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "rotate" ); + meta->registerClass(domRotate::create); + + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float4")); + ma->setOffset( daeOffsetOf( domRotate , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domRotate , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domRotate)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domSampler.cpp b/Engine/lib/collada/src/1.4/dom/domSampler.cpp new file mode 100644 index 000000000..5bbefcbb3 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domSampler.cpp @@ -0,0 +1,72 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domSampler::create(DAE& dae) +{ + domSamplerRef ref = new domSampler(dae); + return ref; +} + + +daeMetaElement * +domSampler::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "sampler" ); + meta->registerClass(domSampler::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, -1 ); + mea->setName( "input" ); + mea->setOffset( daeOffsetOf(domSampler,elemInput_array) ); + mea->setElementType( domInputLocal::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domSampler , attrId )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domSampler)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domScale.cpp b/Engine/lib/collada/src/1.4/dom/domScale.cpp new file mode 100644 index 000000000..029823c05 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domScale.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domScale::create(DAE& dae) +{ + domScaleRef ref = new domScale(dae); + return ref; +} + + +daeMetaElement * +domScale::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "scale" ); + meta->registerClass(domScale::create); + + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float3")); + ma->setOffset( daeOffsetOf( domScale , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domScale , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domScale)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domSkew.cpp b/Engine/lib/collada/src/1.4/dom/domSkew.cpp new file mode 100644 index 000000000..ea400a7ee --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domSkew.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domSkew::create(DAE& dae) +{ + domSkewRef ref = new domSkew(dae); + return ref; +} + + +daeMetaElement * +domSkew::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "skew" ); + meta->registerClass(domSkew::create); + + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float7")); + ma->setOffset( daeOffsetOf( domSkew , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domSkew , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domSkew)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domSkin.cpp b/Engine/lib/collada/src/1.4/dom/domSkin.cpp new file mode 100644 index 000000000..9e23085b4 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domSkin.cpp @@ -0,0 +1,319 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domSkin::create(DAE& dae) +{ + domSkinRef ref = new domSkin(dae); + return ref; +} + + +daeMetaElement * +domSkin::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "skin" ); + meta->registerClass(domSkin::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "bind_shape_matrix" ); + mea->setOffset( daeOffsetOf(domSkin,elemBind_shape_matrix) ); + mea->setElementType( domSkin::domBind_shape_matrix::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 3, -1 ); + mea->setName( "source" ); + mea->setOffset( daeOffsetOf(domSkin,elemSource_array) ); + mea->setElementType( domSource::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 1, 1 ); + mea->setName( "joints" ); + mea->setOffset( daeOffsetOf(domSkin,elemJoints) ); + mea->setElementType( domSkin::domJoints::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 3, 1, 1 ); + mea->setName( "vertex_weights" ); + mea->setOffset( daeOffsetOf(domSkin,elemVertex_weights) ); + mea->setElementType( domSkin::domVertex_weights::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 4, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domSkin,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 4 ); + meta->setCMRoot( cm ); + + // Add attribute: source + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "source" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domSkin , attrSource )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domSkin)); + meta->validate(); + + return meta; +} + +daeElementRef +domSkin::domBind_shape_matrix::create(DAE& dae) +{ + domSkin::domBind_shape_matrixRef ref = new domSkin::domBind_shape_matrix(dae); + return ref; +} + + +daeMetaElement * +domSkin::domBind_shape_matrix::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "bind_shape_matrix" ); + meta->registerClass(domSkin::domBind_shape_matrix::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float4x4")); + ma->setOffset( daeOffsetOf( domSkin::domBind_shape_matrix , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domSkin::domBind_shape_matrix)); + meta->validate(); + + return meta; +} + +daeElementRef +domSkin::domJoints::create(DAE& dae) +{ + domSkin::domJointsRef ref = new domSkin::domJoints(dae); + return ref; +} + + +daeMetaElement * +domSkin::domJoints::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "joints" ); + meta->registerClass(domSkin::domJoints::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 2, -1 ); + mea->setName( "input" ); + mea->setOffset( daeOffsetOf(domSkin::domJoints,elemInput_array) ); + mea->setElementType( domInputLocal::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domSkin::domJoints,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domSkin::domJoints)); + meta->validate(); + + return meta; +} + +daeElementRef +domSkin::domVertex_weights::create(DAE& dae) +{ + domSkin::domVertex_weightsRef ref = new domSkin::domVertex_weights(dae); + return ref; +} + + +daeMetaElement * +domSkin::domVertex_weights::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "vertex_weights" ); + meta->registerClass(domSkin::domVertex_weights::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 2, -1 ); + mea->setName( "input" ); + mea->setOffset( daeOffsetOf(domSkin::domVertex_weights,elemInput_array) ); + mea->setElementType( domInputLocalOffset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "vcount" ); + mea->setOffset( daeOffsetOf(domSkin::domVertex_weights,elemVcount) ); + mea->setElementType( domSkin::domVertex_weights::domVcount::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "v" ); + mea->setOffset( daeOffsetOf(domSkin::domVertex_weights,elemV) ); + mea->setElementType( domSkin::domVertex_weights::domV::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domSkin::domVertex_weights,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3 ); + meta->setCMRoot( cm ); + + // Add attribute: count + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "count" ); + ma->setType( dae.getAtomicTypes().get("Uint")); + ma->setOffset( daeOffsetOf( domSkin::domVertex_weights , attrCount )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domSkin::domVertex_weights)); + meta->validate(); + + return meta; +} + +daeElementRef +domSkin::domVertex_weights::domVcount::create(DAE& dae) +{ + domSkin::domVertex_weights::domVcountRef ref = new domSkin::domVertex_weights::domVcount(dae); + return ref; +} + + +daeMetaElement * +domSkin::domVertex_weights::domVcount::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "vcount" ); + meta->registerClass(domSkin::domVertex_weights::domVcount::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("ListOfUInts")); + ma->setOffset( daeOffsetOf( domSkin::domVertex_weights::domVcount , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domSkin::domVertex_weights::domVcount)); + meta->validate(); + + return meta; +} + +daeElementRef +domSkin::domVertex_weights::domV::create(DAE& dae) +{ + domSkin::domVertex_weights::domVRef ref = new domSkin::domVertex_weights::domV(dae); + return ref; +} + + +daeMetaElement * +domSkin::domVertex_weights::domV::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "v" ); + meta->registerClass(domSkin::domVertex_weights::domV::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("ListOfInts")); + ma->setOffset( daeOffsetOf( domSkin::domVertex_weights::domV , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domSkin::domVertex_weights::domV)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domSource.cpp b/Engine/lib/collada/src/1.4/dom/domSource.cpp new file mode 100644 index 000000000..0a29eb387 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domSource.cpp @@ -0,0 +1,175 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domSource::create(DAE& dae) +{ + domSourceRef ref = new domSource(dae); + return ref; +} + + +daeMetaElement * +domSource::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "source" ); + meta->registerClass(domSource::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domSource,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + cm = new daeMetaChoice( meta, cm, 0, 1, 0, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "IDREF_array" ); + mea->setOffset( daeOffsetOf(domSource,elemIDREF_array) ); + mea->setElementType( domIDREF_array::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "Name_array" ); + mea->setOffset( daeOffsetOf(domSource,elemName_array) ); + mea->setElementType( domName_array::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "bool_array" ); + mea->setOffset( daeOffsetOf(domSource,elemBool_array) ); + mea->setElementType( domBool_array::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "float_array" ); + mea->setOffset( daeOffsetOf(domSource,elemFloat_array) ); + mea->setElementType( domFloat_array::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "int_array" ); + mea->setOffset( daeOffsetOf(domSource,elemInt_array) ); + mea->setElementType( domInt_array::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + mea = new daeMetaElementAttribute( meta, cm, 2, 0, 1 ); + mea->setName( "technique_common" ); + mea->setOffset( daeOffsetOf(domSource,elemTechnique_common) ); + mea->setElementType( domSource::domTechnique_common::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3, 0, -1 ); + mea->setName( "technique" ); + mea->setOffset( daeOffsetOf(domSource,elemTechnique_array) ); + mea->setElementType( domTechnique::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3 ); + meta->setCMRoot( cm ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domSource,_contents)); + meta->addContentsOrder(daeOffsetOf(domSource,_contentsOrder)); + + meta->addCMDataArray(daeOffsetOf(domSource,_CMData), 1); + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domSource , attrId )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domSource , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domSource)); + meta->validate(); + + return meta; +} + +daeElementRef +domSource::domTechnique_common::create(DAE& dae) +{ + domSource::domTechnique_commonRef ref = new domSource::domTechnique_common(dae); + return ref; +} + + +daeMetaElement * +domSource::domTechnique_common::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "technique_common" ); + meta->registerClass(domSource::domTechnique_common::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "accessor" ); + mea->setOffset( daeOffsetOf(domSource::domTechnique_common,elemAccessor) ); + mea->setElementType( domAccessor::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domSource::domTechnique_common)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domSphere.cpp b/Engine/lib/collada/src/1.4/dom/domSphere.cpp new file mode 100644 index 000000000..dda679021 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domSphere.cpp @@ -0,0 +1,103 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domSphere::create(DAE& dae) +{ + domSphereRef ref = new domSphere(dae); + return ref; +} + + +daeMetaElement * +domSphere::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "sphere" ); + meta->registerClass(domSphere::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "radius" ); + mea->setOffset( daeOffsetOf(domSphere,elemRadius) ); + mea->setElementType( domSphere::domRadius::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domSphere,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domSphere)); + meta->validate(); + + return meta; +} + +daeElementRef +domSphere::domRadius::create(DAE& dae) +{ + domSphere::domRadiusRef ref = new domSphere::domRadius(dae); + return ref; +} + + +daeMetaElement * +domSphere::domRadius::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "radius" ); + meta->registerClass(domSphere::domRadius::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domSphere::domRadius , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domSphere::domRadius)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domSpline.cpp b/Engine/lib/collada/src/1.4/dom/domSpline.cpp new file mode 100644 index 000000000..e32b64a9f --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domSpline.cpp @@ -0,0 +1,130 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domSpline::create(DAE& dae) +{ + domSplineRef ref = new domSpline(dae); + return ref; +} + + +daeMetaElement * +domSpline::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "spline" ); + meta->registerClass(domSpline::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, -1 ); + mea->setName( "source" ); + mea->setOffset( daeOffsetOf(domSpline,elemSource_array) ); + mea->setElementType( domSource::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "control_vertices" ); + mea->setOffset( daeOffsetOf(domSpline,elemControl_vertices) ); + mea->setElementType( domSpline::domControl_vertices::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domSpline,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: closed + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "closed" ); + ma->setType( dae.getAtomicTypes().get("Bool")); + ma->setOffset( daeOffsetOf( domSpline , attrClosed )); + ma->setContainer( meta ); + ma->setDefaultString( "false"); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domSpline)); + meta->validate(); + + return meta; +} + +daeElementRef +domSpline::domControl_vertices::create(DAE& dae) +{ + domSpline::domControl_verticesRef ref = new domSpline::domControl_vertices(dae); + return ref; +} + + +daeMetaElement * +domSpline::domControl_vertices::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "control_vertices" ); + meta->registerClass(domSpline::domControl_vertices::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, -1 ); + mea->setName( "input" ); + mea->setOffset( daeOffsetOf(domSpline::domControl_vertices,elemInput_array) ); + mea->setElementType( domInputLocal::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domSpline::domControl_vertices,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domSpline::domControl_vertices)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domTapered_capsule.cpp b/Engine/lib/collada/src/1.4/dom/domTapered_capsule.cpp new file mode 100644 index 000000000..d495f2f52 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domTapered_capsule.cpp @@ -0,0 +1,187 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domTapered_capsule::create(DAE& dae) +{ + domTapered_capsuleRef ref = new domTapered_capsule(dae); + return ref; +} + + +daeMetaElement * +domTapered_capsule::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "tapered_capsule" ); + meta->registerClass(domTapered_capsule::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "height" ); + mea->setOffset( daeOffsetOf(domTapered_capsule,elemHeight) ); + mea->setElementType( domTapered_capsule::domHeight::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "radius1" ); + mea->setOffset( daeOffsetOf(domTapered_capsule,elemRadius1) ); + mea->setElementType( domTapered_capsule::domRadius1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 1, 1 ); + mea->setName( "radius2" ); + mea->setOffset( daeOffsetOf(domTapered_capsule,elemRadius2) ); + mea->setElementType( domTapered_capsule::domRadius2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domTapered_capsule,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domTapered_capsule)); + meta->validate(); + + return meta; +} + +daeElementRef +domTapered_capsule::domHeight::create(DAE& dae) +{ + domTapered_capsule::domHeightRef ref = new domTapered_capsule::domHeight(dae); + return ref; +} + + +daeMetaElement * +domTapered_capsule::domHeight::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "height" ); + meta->registerClass(domTapered_capsule::domHeight::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domTapered_capsule::domHeight , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domTapered_capsule::domHeight)); + meta->validate(); + + return meta; +} + +daeElementRef +domTapered_capsule::domRadius1::create(DAE& dae) +{ + domTapered_capsule::domRadius1Ref ref = new domTapered_capsule::domRadius1(dae); + return ref; +} + + +daeMetaElement * +domTapered_capsule::domRadius1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "radius1" ); + meta->registerClass(domTapered_capsule::domRadius1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float2")); + ma->setOffset( daeOffsetOf( domTapered_capsule::domRadius1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domTapered_capsule::domRadius1)); + meta->validate(); + + return meta; +} + +daeElementRef +domTapered_capsule::domRadius2::create(DAE& dae) +{ + domTapered_capsule::domRadius2Ref ref = new domTapered_capsule::domRadius2(dae); + return ref; +} + + +daeMetaElement * +domTapered_capsule::domRadius2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "radius2" ); + meta->registerClass(domTapered_capsule::domRadius2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float2")); + ma->setOffset( daeOffsetOf( domTapered_capsule::domRadius2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domTapered_capsule::domRadius2)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domTapered_cylinder.cpp b/Engine/lib/collada/src/1.4/dom/domTapered_cylinder.cpp new file mode 100644 index 000000000..8d3cfde5f --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domTapered_cylinder.cpp @@ -0,0 +1,187 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domTapered_cylinder::create(DAE& dae) +{ + domTapered_cylinderRef ref = new domTapered_cylinder(dae); + return ref; +} + + +daeMetaElement * +domTapered_cylinder::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "tapered_cylinder" ); + meta->registerClass(domTapered_cylinder::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 1, 1 ); + mea->setName( "height" ); + mea->setOffset( daeOffsetOf(domTapered_cylinder,elemHeight) ); + mea->setElementType( domTapered_cylinder::domHeight::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 1, 1 ); + mea->setName( "radius1" ); + mea->setOffset( daeOffsetOf(domTapered_cylinder,elemRadius1) ); + mea->setElementType( domTapered_cylinder::domRadius1::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 2, 1, 1 ); + mea->setName( "radius2" ); + mea->setOffset( daeOffsetOf(domTapered_cylinder,elemRadius2) ); + mea->setElementType( domTapered_cylinder::domRadius2::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domTapered_cylinder,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3 ); + meta->setCMRoot( cm ); + + meta->setElementSize(sizeof(domTapered_cylinder)); + meta->validate(); + + return meta; +} + +daeElementRef +domTapered_cylinder::domHeight::create(DAE& dae) +{ + domTapered_cylinder::domHeightRef ref = new domTapered_cylinder::domHeight(dae); + return ref; +} + + +daeMetaElement * +domTapered_cylinder::domHeight::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "height" ); + meta->registerClass(domTapered_cylinder::domHeight::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domTapered_cylinder::domHeight , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domTapered_cylinder::domHeight)); + meta->validate(); + + return meta; +} + +daeElementRef +domTapered_cylinder::domRadius1::create(DAE& dae) +{ + domTapered_cylinder::domRadius1Ref ref = new domTapered_cylinder::domRadius1(dae); + return ref; +} + + +daeMetaElement * +domTapered_cylinder::domRadius1::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "radius1" ); + meta->registerClass(domTapered_cylinder::domRadius1::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float2")); + ma->setOffset( daeOffsetOf( domTapered_cylinder::domRadius1 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domTapered_cylinder::domRadius1)); + meta->validate(); + + return meta; +} + +daeElementRef +domTapered_cylinder::domRadius2::create(DAE& dae) +{ + domTapered_cylinder::domRadius2Ref ref = new domTapered_cylinder::domRadius2(dae); + return ref; +} + + +daeMetaElement * +domTapered_cylinder::domRadius2::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "radius2" ); + meta->registerClass(domTapered_cylinder::domRadius2::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float2")); + ma->setOffset( daeOffsetOf( domTapered_cylinder::domRadius2 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domTapered_cylinder::domRadius2)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domTargetableFloat.cpp b/Engine/lib/collada/src/1.4/dom/domTargetableFloat.cpp new file mode 100644 index 000000000..f4388cda5 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domTargetableFloat.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domTargetableFloat::create(DAE& dae) +{ + domTargetableFloatRef ref = new domTargetableFloat(dae); + return ref; +} + + +daeMetaElement * +domTargetableFloat::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "TargetableFloat" ); + meta->registerClass(domTargetableFloat::create); + + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float")); + ma->setOffset( daeOffsetOf( domTargetableFloat , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domTargetableFloat , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domTargetableFloat)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domTargetableFloat3.cpp b/Engine/lib/collada/src/1.4/dom/domTargetableFloat3.cpp new file mode 100644 index 000000000..1bf8ce8f3 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domTargetableFloat3.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domTargetableFloat3::create(DAE& dae) +{ + domTargetableFloat3Ref ref = new domTargetableFloat3(dae); + return ref; +} + + +daeMetaElement * +domTargetableFloat3::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "TargetableFloat3" ); + meta->registerClass(domTargetableFloat3::create); + + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float3")); + ma->setOffset( daeOffsetOf( domTargetableFloat3 , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domTargetableFloat3 , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domTargetableFloat3)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domTechnique.cpp b/Engine/lib/collada/src/1.4/dom/domTechnique.cpp new file mode 100644 index 000000000..07f052464 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domTechnique.cpp @@ -0,0 +1,87 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domTechnique::create(DAE& dae) +{ + domTechniqueRef ref = new domTechnique(dae); + return ref; +} + + +daeMetaElement * +domTechnique::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "technique" ); + meta->registerClass(domTechnique::create); + + daeMetaCMPolicy *cm = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + cm = new daeMetaAny( meta, cm, 0, 0, -1 ); + + cm->setMaxOrdinal( 0 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + meta->setAllowsAny( true ); + // Ordered list of sub-elements + meta->addContents(daeOffsetOf(domTechnique,_contents)); + meta->addContentsOrder(daeOffsetOf(domTechnique,_contentsOrder)); + + // Add attribute: xmlns + { + daeMetaAttribute* ma = new daeMetaAttribute; + ma->setName( "xmlns" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domTechnique , attrXmlns )); + ma->setContainer( meta ); + //ma->setIsRequired( true ); + meta->appendAttribute(ma); + } + + // Add attribute: profile + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "profile" ); + ma->setType( dae.getAtomicTypes().get("xsNMTOKEN")); + ma->setOffset( daeOffsetOf( domTechnique , attrProfile )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domTechnique)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domTranslate.cpp b/Engine/lib/collada/src/1.4/dom/domTranslate.cpp new file mode 100644 index 000000000..57fcccd70 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domTranslate.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domTranslate::create(DAE& dae) +{ + domTranslateRef ref = new domTranslate(dae); + return ref; +} + + +daeMetaElement * +domTranslate::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "translate" ); + meta->registerClass(domTranslate::create); + + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaArrayAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("Float3")); + ma->setOffset( daeOffsetOf( domTranslate , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + // Add attribute: sid + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "sid" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domTranslate , attrSid )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domTranslate)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domTriangles.cpp b/Engine/lib/collada/src/1.4/dom/domTriangles.cpp new file mode 100644 index 000000000..9ee0bf227 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domTriangles.cpp @@ -0,0 +1,107 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domTriangles::create(DAE& dae) +{ + domTrianglesRef ref = new domTriangles(dae); + return ref; +} + + +daeMetaElement * +domTriangles::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "triangles" ); + meta->registerClass(domTriangles::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "input" ); + mea->setOffset( daeOffsetOf(domTriangles,elemInput_array) ); + mea->setElementType( domInputLocalOffset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "p" ); + mea->setOffset( daeOffsetOf(domTriangles,elemP) ); + mea->setElementType( domP::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domTriangles,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domTriangles , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: count + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "count" ); + ma->setType( dae.getAtomicTypes().get("Uint")); + ma->setOffset( daeOffsetOf( domTriangles , attrCount )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: material + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "material" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domTriangles , attrMaterial )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domTriangles)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domTrifans.cpp b/Engine/lib/collada/src/1.4/dom/domTrifans.cpp new file mode 100644 index 000000000..b05bec27f --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domTrifans.cpp @@ -0,0 +1,107 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domTrifans::create(DAE& dae) +{ + domTrifansRef ref = new domTrifans(dae); + return ref; +} + + +daeMetaElement * +domTrifans::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "trifans" ); + meta->registerClass(domTrifans::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "input" ); + mea->setOffset( daeOffsetOf(domTrifans,elemInput_array) ); + mea->setElementType( domInputLocalOffset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 0, -1 ); + mea->setName( "p" ); + mea->setOffset( daeOffsetOf(domTrifans,elemP_array) ); + mea->setElementType( domP::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domTrifans,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domTrifans , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: count + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "count" ); + ma->setType( dae.getAtomicTypes().get("Uint")); + ma->setOffset( daeOffsetOf( domTrifans , attrCount )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: material + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "material" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domTrifans , attrMaterial )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domTrifans)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domTristrips.cpp b/Engine/lib/collada/src/1.4/dom/domTristrips.cpp new file mode 100644 index 000000000..1ee52fa73 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domTristrips.cpp @@ -0,0 +1,107 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domTristrips::create(DAE& dae) +{ + domTristripsRef ref = new domTristrips(dae); + return ref; +} + + +daeMetaElement * +domTristrips::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "tristrips" ); + meta->registerClass(domTristrips::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "input" ); + mea->setOffset( daeOffsetOf(domTristrips,elemInput_array) ); + mea->setElementType( domInputLocalOffset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 0, -1 ); + mea->setName( "p" ); + mea->setOffset( daeOffsetOf(domTristrips,elemP_array) ); + mea->setElementType( domP::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domTristrips,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 2 ); + meta->setCMRoot( cm ); + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domTristrips , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + // Add attribute: count + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "count" ); + ma->setType( dae.getAtomicTypes().get("Uint")); + ma->setOffset( daeOffsetOf( domTristrips , attrCount )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: material + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "material" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domTristrips , attrMaterial )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domTristrips)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domTypes.cpp b/Engine/lib/collada/src/1.4/dom/domTypes.cpp new file mode 100644 index 000000000..0a8bfd8f3 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domTypes.cpp @@ -0,0 +1,2942 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include + + +void registerDomTypes(DAE& dae) +{ + daeAtomicType* type = NULL; + daeAtomicTypeList& atomicTypes = dae.getAtomicTypes(); + + // TYPEDEF: Bool //check if this type has an existing base + type = atomicTypes.get("xsBoolean"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Bool"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Bool"); + } + + // TYPEDEF: DateTime //check if this type has an existing base + type = atomicTypes.get("xsDateTime"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("DateTime"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("DateTime"); + } + + // TYPEDEF: Float //check if this type has an existing base + type = atomicTypes.get("xsDouble"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Float"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Float"); + } + + // TYPEDEF: Int //check if this type has an existing base + type = atomicTypes.get("xsLong"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Int"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Int"); + } + + // TYPEDEF: Name //check if this type has an existing base + type = atomicTypes.get("xsName"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Name"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Name"); + } + + // TYPEDEF: String //check if this type has an existing base + type = atomicTypes.get("xsString"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("String"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("String"); + } + + // TYPEDEF: Token //check if this type has an existing base + type = atomicTypes.get("xsToken"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Token"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Token"); + } + + // TYPEDEF: Uint //check if this type has an existing base + type = atomicTypes.get("xsUnsignedLong"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Uint"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Uint"); + } + + // TYPEDEF: ListOfBools //check if this type has an existing base + type = atomicTypes.get("Bool"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("ListOfBools"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("ListOfBools"); + } + + // TYPEDEF: ListOfFloats //check if this type has an existing base + type = atomicTypes.get("Float"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("ListOfFloats"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("ListOfFloats"); + } + + // TYPEDEF: ListOfHexBinary //check if this type has an existing base + type = atomicTypes.get("xsHexBinary"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("ListOfHexBinary"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("ListOfHexBinary"); + } + + // TYPEDEF: ListOfInts //check if this type has an existing base + type = atomicTypes.get("Int"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("ListOfInts"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("ListOfInts"); + } + + // TYPEDEF: ListOfNames //check if this type has an existing base + type = atomicTypes.get("Name"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("ListOfNames"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("ListOfNames"); + } + + // TYPEDEF: ListOfTokens //check if this type has an existing base + type = atomicTypes.get("Token"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("ListOfTokens"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("ListOfTokens"); + } + + // TYPEDEF: ListOfUInts //check if this type has an existing base + type = atomicTypes.get("Uint"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("ListOfUInts"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("ListOfUInts"); + } + + // TYPEDEF: Bool2 //check if this type has an existing base + type = atomicTypes.get("ListOfBools"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Bool2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Bool2"); + } + + // TYPEDEF: Bool3 //check if this type has an existing base + type = atomicTypes.get("ListOfBools"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Bool3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Bool3"); + } + + // TYPEDEF: Bool4 //check if this type has an existing base + type = atomicTypes.get("ListOfBools"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Bool4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Bool4"); + } + + // TYPEDEF: Float2 //check if this type has an existing base + type = atomicTypes.get("ListOfFloats"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Float2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Float2"); + } + + // TYPEDEF: Float3 //check if this type has an existing base + type = atomicTypes.get("ListOfFloats"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Float3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Float3"); + } + + // TYPEDEF: Float4 //check if this type has an existing base + type = atomicTypes.get("ListOfFloats"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Float4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Float4"); + } + + // TYPEDEF: Float7 //check if this type has an existing base + type = atomicTypes.get("ListOfFloats"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Float7"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Float7"); + } + + // TYPEDEF: Float2x2 //check if this type has an existing base + type = atomicTypes.get("ListOfFloats"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Float2x2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Float2x2"); + } + + // TYPEDEF: Float3x3 //check if this type has an existing base + type = atomicTypes.get("ListOfFloats"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Float3x3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Float3x3"); + } + + // TYPEDEF: Float4x4 //check if this type has an existing base + type = atomicTypes.get("ListOfFloats"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Float4x4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Float4x4"); + } + + // TYPEDEF: Float2x3 //check if this type has an existing base + type = atomicTypes.get("ListOfFloats"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Float2x3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Float2x3"); + } + + // TYPEDEF: Float2x4 //check if this type has an existing base + type = atomicTypes.get("ListOfFloats"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Float2x4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Float2x4"); + } + + // TYPEDEF: Float3x2 //check if this type has an existing base + type = atomicTypes.get("ListOfFloats"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Float3x2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Float3x2"); + } + + // TYPEDEF: Float3x4 //check if this type has an existing base + type = atomicTypes.get("ListOfFloats"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Float3x4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Float3x4"); + } + + // TYPEDEF: Float4x2 //check if this type has an existing base + type = atomicTypes.get("ListOfFloats"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Float4x2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Float4x2"); + } + + // TYPEDEF: Float4x3 //check if this type has an existing base + type = atomicTypes.get("ListOfFloats"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Float4x3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Float4x3"); + } + + // TYPEDEF: Int2 //check if this type has an existing base + type = atomicTypes.get("ListOfInts"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Int2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Int2"); + } + + // TYPEDEF: Int3 //check if this type has an existing base + type = atomicTypes.get("ListOfInts"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Int3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Int3"); + } + + // TYPEDEF: Int4 //check if this type has an existing base + type = atomicTypes.get("ListOfInts"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Int4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Int4"); + } + + // TYPEDEF: Int2x2 //check if this type has an existing base + type = atomicTypes.get("ListOfInts"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Int2x2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Int2x2"); + } + + // TYPEDEF: Int3x3 //check if this type has an existing base + type = atomicTypes.get("ListOfInts"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Int3x3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Int3x3"); + } + + // TYPEDEF: Int4x4 //check if this type has an existing base + type = atomicTypes.get("ListOfInts"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Int4x4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Int4x4"); + } + + // ENUM: MorphMethodType + type = new daeEnumType(dae); + type->_nameBindings.append("MorphMethodType"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("NORMALIZED"); + ((daeEnumType*)type)->_values->append(MORPHMETHODTYPE_NORMALIZED); + ((daeEnumType*)type)->_strings->append("RELATIVE"); + ((daeEnumType*)type)->_values->append(MORPHMETHODTYPE_RELATIVE); + atomicTypes.append( type ); + + // ENUM: NodeType + type = new daeEnumType(dae); + type->_nameBindings.append("NodeType"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("JOINT"); + ((daeEnumType*)type)->_values->append(NODETYPE_JOINT); + ((daeEnumType*)type)->_strings->append("NODE"); + ((daeEnumType*)type)->_values->append(NODETYPE_NODE); + atomicTypes.append( type ); + + // TYPEDEF: URIFragmentType //check if this type has an existing base + type = atomicTypes.get("xsAnyURI"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("URIFragmentType"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("URIFragmentType"); + } + + // ENUM: UpAxisType + type = new daeEnumType(dae); + type->_nameBindings.append("UpAxisType"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("X_UP"); + ((daeEnumType*)type)->_values->append(UPAXISTYPE_X_UP); + ((daeEnumType*)type)->_strings->append("Y_UP"); + ((daeEnumType*)type)->_values->append(UPAXISTYPE_Y_UP); + ((daeEnumType*)type)->_strings->append("Z_UP"); + ((daeEnumType*)type)->_values->append(UPAXISTYPE_Z_UP); + atomicTypes.append( type ); + + // ENUM: VersionType + type = new daeEnumType(dae); + type->_nameBindings.append("VersionType"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("1.4.0"); + ((daeEnumType*)type)->_values->append(VERSIONTYPE_1_4_0); + ((daeEnumType*)type)->_strings->append("1.4.1"); + ((daeEnumType*)type)->_values->append(VERSIONTYPE_1_4_1); + atomicTypes.append( type ); + + // TYPEDEF: Fx_color_common //check if this type has an existing base + type = atomicTypes.get("Float4"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Fx_color_common"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Fx_color_common"); + } + + // ENUM: Fx_opaque_enum + type = new daeEnumType(dae); + type->_nameBindings.append("Fx_opaque_enum"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("A_ONE"); + ((daeEnumType*)type)->_values->append(FX_OPAQUE_ENUM_A_ONE); + ((daeEnumType*)type)->_strings->append("RGB_ZERO"); + ((daeEnumType*)type)->_values->append(FX_OPAQUE_ENUM_RGB_ZERO); + atomicTypes.append( type ); + + // ENUM: Fx_surface_type_enum + type = new daeEnumType(dae); + type->_nameBindings.append("Fx_surface_type_enum"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("UNTYPED"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_TYPE_ENUM_UNTYPED); + ((daeEnumType*)type)->_strings->append("1D"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_TYPE_ENUM_1D); + ((daeEnumType*)type)->_strings->append("2D"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_TYPE_ENUM_2D); + ((daeEnumType*)type)->_strings->append("3D"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_TYPE_ENUM_3D); + ((daeEnumType*)type)->_strings->append("RECT"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_TYPE_ENUM_RECT); + ((daeEnumType*)type)->_strings->append("CUBE"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_TYPE_ENUM_CUBE); + ((daeEnumType*)type)->_strings->append("DEPTH"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_TYPE_ENUM_DEPTH); + atomicTypes.append( type ); + + // ENUM: Fx_surface_face_enum + type = new daeEnumType(dae); + type->_nameBindings.append("Fx_surface_face_enum"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("POSITIVE_X"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_FACE_ENUM_POSITIVE_X); + ((daeEnumType*)type)->_strings->append("NEGATIVE_X"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_FACE_ENUM_NEGATIVE_X); + ((daeEnumType*)type)->_strings->append("POSITIVE_Y"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_FACE_ENUM_POSITIVE_Y); + ((daeEnumType*)type)->_strings->append("NEGATIVE_Y"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_FACE_ENUM_NEGATIVE_Y); + ((daeEnumType*)type)->_strings->append("POSITIVE_Z"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_FACE_ENUM_POSITIVE_Z); + ((daeEnumType*)type)->_strings->append("NEGATIVE_Z"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_FACE_ENUM_NEGATIVE_Z); + atomicTypes.append( type ); + + // ENUM: Fx_surface_format_hint_channels_enum + type = new daeEnumType(dae); + type->_nameBindings.append("Fx_surface_format_hint_channels_enum"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("RGB"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_FORMAT_HINT_CHANNELS_ENUM_RGB); + ((daeEnumType*)type)->_strings->append("RGBA"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_FORMAT_HINT_CHANNELS_ENUM_RGBA); + ((daeEnumType*)type)->_strings->append("L"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_FORMAT_HINT_CHANNELS_ENUM_L); + ((daeEnumType*)type)->_strings->append("LA"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_FORMAT_HINT_CHANNELS_ENUM_LA); + ((daeEnumType*)type)->_strings->append("D"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_FORMAT_HINT_CHANNELS_ENUM_D); + ((daeEnumType*)type)->_strings->append("XYZ"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_FORMAT_HINT_CHANNELS_ENUM_XYZ); + ((daeEnumType*)type)->_strings->append("XYZW"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_FORMAT_HINT_CHANNELS_ENUM_XYZW); + atomicTypes.append( type ); + + // ENUM: Fx_surface_format_hint_precision_enum + type = new daeEnumType(dae); + type->_nameBindings.append("Fx_surface_format_hint_precision_enum"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("LOW"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_FORMAT_HINT_PRECISION_ENUM_LOW); + ((daeEnumType*)type)->_strings->append("MID"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_FORMAT_HINT_PRECISION_ENUM_MID); + ((daeEnumType*)type)->_strings->append("HIGH"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_FORMAT_HINT_PRECISION_ENUM_HIGH); + atomicTypes.append( type ); + + // ENUM: Fx_surface_format_hint_range_enum + type = new daeEnumType(dae); + type->_nameBindings.append("Fx_surface_format_hint_range_enum"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("SNORM"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_FORMAT_HINT_RANGE_ENUM_SNORM); + ((daeEnumType*)type)->_strings->append("UNORM"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_FORMAT_HINT_RANGE_ENUM_UNORM); + ((daeEnumType*)type)->_strings->append("SINT"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_FORMAT_HINT_RANGE_ENUM_SINT); + ((daeEnumType*)type)->_strings->append("UINT"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_FORMAT_HINT_RANGE_ENUM_UINT); + ((daeEnumType*)type)->_strings->append("FLOAT"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_FORMAT_HINT_RANGE_ENUM_FLOAT); + atomicTypes.append( type ); + + // ENUM: Fx_surface_format_hint_option_enum + type = new daeEnumType(dae); + type->_nameBindings.append("Fx_surface_format_hint_option_enum"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("SRGB_GAMMA"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_FORMAT_HINT_OPTION_ENUM_SRGB_GAMMA); + ((daeEnumType*)type)->_strings->append("NORMALIZED3"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_FORMAT_HINT_OPTION_ENUM_NORMALIZED3); + ((daeEnumType*)type)->_strings->append("NORMALIZED4"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_FORMAT_HINT_OPTION_ENUM_NORMALIZED4); + ((daeEnumType*)type)->_strings->append("COMPRESSABLE"); + ((daeEnumType*)type)->_values->append(FX_SURFACE_FORMAT_HINT_OPTION_ENUM_COMPRESSABLE); + atomicTypes.append( type ); + + // ENUM: Fx_sampler_wrap_common + type = new daeEnumType(dae); + type->_nameBindings.append("Fx_sampler_wrap_common"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("NONE"); + ((daeEnumType*)type)->_values->append(FX_SAMPLER_WRAP_COMMON_NONE); + ((daeEnumType*)type)->_strings->append("WRAP"); + ((daeEnumType*)type)->_values->append(FX_SAMPLER_WRAP_COMMON_WRAP); + ((daeEnumType*)type)->_strings->append("MIRROR"); + ((daeEnumType*)type)->_values->append(FX_SAMPLER_WRAP_COMMON_MIRROR); + ((daeEnumType*)type)->_strings->append("CLAMP"); + ((daeEnumType*)type)->_values->append(FX_SAMPLER_WRAP_COMMON_CLAMP); + ((daeEnumType*)type)->_strings->append("BORDER"); + ((daeEnumType*)type)->_values->append(FX_SAMPLER_WRAP_COMMON_BORDER); + atomicTypes.append( type ); + + // ENUM: Fx_sampler_filter_common + type = new daeEnumType(dae); + type->_nameBindings.append("Fx_sampler_filter_common"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("NONE"); + ((daeEnumType*)type)->_values->append(FX_SAMPLER_FILTER_COMMON_NONE); + ((daeEnumType*)type)->_strings->append("NEAREST"); + ((daeEnumType*)type)->_values->append(FX_SAMPLER_FILTER_COMMON_NEAREST); + ((daeEnumType*)type)->_strings->append("LINEAR"); + ((daeEnumType*)type)->_values->append(FX_SAMPLER_FILTER_COMMON_LINEAR); + ((daeEnumType*)type)->_strings->append("NEAREST_MIPMAP_NEAREST"); + ((daeEnumType*)type)->_values->append(FX_SAMPLER_FILTER_COMMON_NEAREST_MIPMAP_NEAREST); + ((daeEnumType*)type)->_strings->append("LINEAR_MIPMAP_NEAREST"); + ((daeEnumType*)type)->_values->append(FX_SAMPLER_FILTER_COMMON_LINEAR_MIPMAP_NEAREST); + ((daeEnumType*)type)->_strings->append("NEAREST_MIPMAP_LINEAR"); + ((daeEnumType*)type)->_values->append(FX_SAMPLER_FILTER_COMMON_NEAREST_MIPMAP_LINEAR); + ((daeEnumType*)type)->_strings->append("LINEAR_MIPMAP_LINEAR"); + ((daeEnumType*)type)->_values->append(FX_SAMPLER_FILTER_COMMON_LINEAR_MIPMAP_LINEAR); + atomicTypes.append( type ); + + // ENUM: Fx_modifier_enum_common + type = new daeEnumType(dae); + type->_nameBindings.append("Fx_modifier_enum_common"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("CONST"); + ((daeEnumType*)type)->_values->append(FX_MODIFIER_ENUM_COMMON_CONST); + ((daeEnumType*)type)->_strings->append("UNIFORM"); + ((daeEnumType*)type)->_values->append(FX_MODIFIER_ENUM_COMMON_UNIFORM); + ((daeEnumType*)type)->_strings->append("VARYING"); + ((daeEnumType*)type)->_values->append(FX_MODIFIER_ENUM_COMMON_VARYING); + ((daeEnumType*)type)->_strings->append("STATIC"); + ((daeEnumType*)type)->_values->append(FX_MODIFIER_ENUM_COMMON_STATIC); + ((daeEnumType*)type)->_strings->append("VOLATILE"); + ((daeEnumType*)type)->_values->append(FX_MODIFIER_ENUM_COMMON_VOLATILE); + ((daeEnumType*)type)->_strings->append("EXTERN"); + ((daeEnumType*)type)->_values->append(FX_MODIFIER_ENUM_COMMON_EXTERN); + ((daeEnumType*)type)->_strings->append("SHARED"); + ((daeEnumType*)type)->_values->append(FX_MODIFIER_ENUM_COMMON_SHARED); + atomicTypes.append( type ); + + // TYPEDEF: Fx_draw_common //check if this type has an existing base + type = atomicTypes.get("xsString"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Fx_draw_common"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Fx_draw_common"); + } + + // ENUM: Fx_pipeline_stage_common + type = new daeEnumType(dae); + type->_nameBindings.append("Fx_pipeline_stage_common"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("VERTEXPROGRAM"); + ((daeEnumType*)type)->_values->append(FX_PIPELINE_STAGE_COMMON_VERTEXPROGRAM); + ((daeEnumType*)type)->_strings->append("FRAGMENTPROGRAM"); + ((daeEnumType*)type)->_values->append(FX_PIPELINE_STAGE_COMMON_FRAGMENTPROGRAM); + ((daeEnumType*)type)->_strings->append("VERTEXSHADER"); + ((daeEnumType*)type)->_values->append(FX_PIPELINE_STAGE_COMMON_VERTEXSHADER); + ((daeEnumType*)type)->_strings->append("PIXELSHADER"); + ((daeEnumType*)type)->_values->append(FX_PIPELINE_STAGE_COMMON_PIXELSHADER); + atomicTypes.append( type ); + + // TYPEDEF: GL_MAX_LIGHTS_index //check if this type has an existing base + type = atomicTypes.get("xsNonNegativeInteger"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("GL_MAX_LIGHTS_index"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("GL_MAX_LIGHTS_index"); + } + + // TYPEDEF: GL_MAX_CLIP_PLANES_index //check if this type has an existing base + type = atomicTypes.get("xsNonNegativeInteger"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("GL_MAX_CLIP_PLANES_index"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("GL_MAX_CLIP_PLANES_index"); + } + + // TYPEDEF: GL_MAX_TEXTURE_IMAGE_UNITS_index //check if this type has an existing base + type = atomicTypes.get("xsNonNegativeInteger"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("GL_MAX_TEXTURE_IMAGE_UNITS_index"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("GL_MAX_TEXTURE_IMAGE_UNITS_index"); + } + + // ENUM: Gl_blend_type + type = new daeEnumType(dae); + type->_nameBindings.append("Gl_blend_type"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("ZERO"); + ((daeEnumType*)type)->_values->append(GL_BLEND_TYPE_ZERO); + ((daeEnumType*)type)->_strings->append("ONE"); + ((daeEnumType*)type)->_values->append(GL_BLEND_TYPE_ONE); + ((daeEnumType*)type)->_strings->append("SRC_COLOR"); + ((daeEnumType*)type)->_values->append(GL_BLEND_TYPE_SRC_COLOR); + ((daeEnumType*)type)->_strings->append("ONE_MINUS_SRC_COLOR"); + ((daeEnumType*)type)->_values->append(GL_BLEND_TYPE_ONE_MINUS_SRC_COLOR); + ((daeEnumType*)type)->_strings->append("DEST_COLOR"); + ((daeEnumType*)type)->_values->append(GL_BLEND_TYPE_DEST_COLOR); + ((daeEnumType*)type)->_strings->append("ONE_MINUS_DEST_COLOR"); + ((daeEnumType*)type)->_values->append(GL_BLEND_TYPE_ONE_MINUS_DEST_COLOR); + ((daeEnumType*)type)->_strings->append("SRC_ALPHA"); + ((daeEnumType*)type)->_values->append(GL_BLEND_TYPE_SRC_ALPHA); + ((daeEnumType*)type)->_strings->append("ONE_MINUS_SRC_ALPHA"); + ((daeEnumType*)type)->_values->append(GL_BLEND_TYPE_ONE_MINUS_SRC_ALPHA); + ((daeEnumType*)type)->_strings->append("DST_ALPHA"); + ((daeEnumType*)type)->_values->append(GL_BLEND_TYPE_DST_ALPHA); + ((daeEnumType*)type)->_strings->append("ONE_MINUS_DST_ALPHA"); + ((daeEnumType*)type)->_values->append(GL_BLEND_TYPE_ONE_MINUS_DST_ALPHA); + ((daeEnumType*)type)->_strings->append("CONSTANT_COLOR"); + ((daeEnumType*)type)->_values->append(GL_BLEND_TYPE_CONSTANT_COLOR); + ((daeEnumType*)type)->_strings->append("ONE_MINUS_CONSTANT_COLOR"); + ((daeEnumType*)type)->_values->append(GL_BLEND_TYPE_ONE_MINUS_CONSTANT_COLOR); + ((daeEnumType*)type)->_strings->append("CONSTANT_ALPHA"); + ((daeEnumType*)type)->_values->append(GL_BLEND_TYPE_CONSTANT_ALPHA); + ((daeEnumType*)type)->_strings->append("ONE_MINUS_CONSTANT_ALPHA"); + ((daeEnumType*)type)->_values->append(GL_BLEND_TYPE_ONE_MINUS_CONSTANT_ALPHA); + ((daeEnumType*)type)->_strings->append("SRC_ALPHA_SATURATE"); + ((daeEnumType*)type)->_values->append(GL_BLEND_TYPE_SRC_ALPHA_SATURATE); + atomicTypes.append( type ); + + // ENUM: Gl_face_type + type = new daeEnumType(dae); + type->_nameBindings.append("Gl_face_type"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("FRONT"); + ((daeEnumType*)type)->_values->append(GL_FACE_TYPE_FRONT); + ((daeEnumType*)type)->_strings->append("BACK"); + ((daeEnumType*)type)->_values->append(GL_FACE_TYPE_BACK); + ((daeEnumType*)type)->_strings->append("FRONT_AND_BACK"); + ((daeEnumType*)type)->_values->append(GL_FACE_TYPE_FRONT_AND_BACK); + atomicTypes.append( type ); + + // ENUM: Gl_blend_equation_type + type = new daeEnumType(dae); + type->_nameBindings.append("Gl_blend_equation_type"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("FUNC_ADD"); + ((daeEnumType*)type)->_values->append(GL_BLEND_EQUATION_TYPE_FUNC_ADD); + ((daeEnumType*)type)->_strings->append("FUNC_SUBTRACT"); + ((daeEnumType*)type)->_values->append(GL_BLEND_EQUATION_TYPE_FUNC_SUBTRACT); + ((daeEnumType*)type)->_strings->append("FUNC_REVERSE_SUBTRACT"); + ((daeEnumType*)type)->_values->append(GL_BLEND_EQUATION_TYPE_FUNC_REVERSE_SUBTRACT); + ((daeEnumType*)type)->_strings->append("MIN"); + ((daeEnumType*)type)->_values->append(GL_BLEND_EQUATION_TYPE_MIN); + ((daeEnumType*)type)->_strings->append("MAX"); + ((daeEnumType*)type)->_values->append(GL_BLEND_EQUATION_TYPE_MAX); + atomicTypes.append( type ); + + // ENUM: Gl_func_type + type = new daeEnumType(dae); + type->_nameBindings.append("Gl_func_type"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("NEVER"); + ((daeEnumType*)type)->_values->append(GL_FUNC_TYPE_NEVER); + ((daeEnumType*)type)->_strings->append("LESS"); + ((daeEnumType*)type)->_values->append(GL_FUNC_TYPE_LESS); + ((daeEnumType*)type)->_strings->append("LEQUAL"); + ((daeEnumType*)type)->_values->append(GL_FUNC_TYPE_LEQUAL); + ((daeEnumType*)type)->_strings->append("EQUAL"); + ((daeEnumType*)type)->_values->append(GL_FUNC_TYPE_EQUAL); + ((daeEnumType*)type)->_strings->append("GREATER"); + ((daeEnumType*)type)->_values->append(GL_FUNC_TYPE_GREATER); + ((daeEnumType*)type)->_strings->append("NOTEQUAL"); + ((daeEnumType*)type)->_values->append(GL_FUNC_TYPE_NOTEQUAL); + ((daeEnumType*)type)->_strings->append("GEQUAL"); + ((daeEnumType*)type)->_values->append(GL_FUNC_TYPE_GEQUAL); + ((daeEnumType*)type)->_strings->append("ALWAYS"); + ((daeEnumType*)type)->_values->append(GL_FUNC_TYPE_ALWAYS); + atomicTypes.append( type ); + + // ENUM: Gl_stencil_op_type + type = new daeEnumType(dae); + type->_nameBindings.append("Gl_stencil_op_type"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("KEEP"); + ((daeEnumType*)type)->_values->append(GL_STENCIL_OP_TYPE_KEEP); + ((daeEnumType*)type)->_strings->append("ZERO"); + ((daeEnumType*)type)->_values->append(GL_STENCIL_OP_TYPE_ZERO); + ((daeEnumType*)type)->_strings->append("REPLACE"); + ((daeEnumType*)type)->_values->append(GL_STENCIL_OP_TYPE_REPLACE); + ((daeEnumType*)type)->_strings->append("INCR"); + ((daeEnumType*)type)->_values->append(GL_STENCIL_OP_TYPE_INCR); + ((daeEnumType*)type)->_strings->append("DECR"); + ((daeEnumType*)type)->_values->append(GL_STENCIL_OP_TYPE_DECR); + ((daeEnumType*)type)->_strings->append("INVERT"); + ((daeEnumType*)type)->_values->append(GL_STENCIL_OP_TYPE_INVERT); + ((daeEnumType*)type)->_strings->append("INCR_WRAP"); + ((daeEnumType*)type)->_values->append(GL_STENCIL_OP_TYPE_INCR_WRAP); + ((daeEnumType*)type)->_strings->append("DECR_WRAP"); + ((daeEnumType*)type)->_values->append(GL_STENCIL_OP_TYPE_DECR_WRAP); + atomicTypes.append( type ); + + // ENUM: Gl_material_type + type = new daeEnumType(dae); + type->_nameBindings.append("Gl_material_type"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("EMISSION"); + ((daeEnumType*)type)->_values->append(GL_MATERIAL_TYPE_EMISSION); + ((daeEnumType*)type)->_strings->append("AMBIENT"); + ((daeEnumType*)type)->_values->append(GL_MATERIAL_TYPE_AMBIENT); + ((daeEnumType*)type)->_strings->append("DIFFUSE"); + ((daeEnumType*)type)->_values->append(GL_MATERIAL_TYPE_DIFFUSE); + ((daeEnumType*)type)->_strings->append("SPECULAR"); + ((daeEnumType*)type)->_values->append(GL_MATERIAL_TYPE_SPECULAR); + ((daeEnumType*)type)->_strings->append("AMBIENT_AND_DIFFUSE"); + ((daeEnumType*)type)->_values->append(GL_MATERIAL_TYPE_AMBIENT_AND_DIFFUSE); + atomicTypes.append( type ); + + // ENUM: Gl_fog_type + type = new daeEnumType(dae); + type->_nameBindings.append("Gl_fog_type"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("LINEAR"); + ((daeEnumType*)type)->_values->append(GL_FOG_TYPE_LINEAR); + ((daeEnumType*)type)->_strings->append("EXP"); + ((daeEnumType*)type)->_values->append(GL_FOG_TYPE_EXP); + ((daeEnumType*)type)->_strings->append("EXP2"); + ((daeEnumType*)type)->_values->append(GL_FOG_TYPE_EXP2); + atomicTypes.append( type ); + + // ENUM: Gl_fog_coord_src_type + type = new daeEnumType(dae); + type->_nameBindings.append("Gl_fog_coord_src_type"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("FOG_COORDINATE"); + ((daeEnumType*)type)->_values->append(GL_FOG_COORD_SRC_TYPE_FOG_COORDINATE); + ((daeEnumType*)type)->_strings->append("FRAGMENT_DEPTH"); + ((daeEnumType*)type)->_values->append(GL_FOG_COORD_SRC_TYPE_FRAGMENT_DEPTH); + atomicTypes.append( type ); + + // ENUM: Gl_front_face_type + type = new daeEnumType(dae); + type->_nameBindings.append("Gl_front_face_type"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("CW"); + ((daeEnumType*)type)->_values->append(GL_FRONT_FACE_TYPE_CW); + ((daeEnumType*)type)->_strings->append("CCW"); + ((daeEnumType*)type)->_values->append(GL_FRONT_FACE_TYPE_CCW); + atomicTypes.append( type ); + + // ENUM: Gl_light_model_color_control_type + type = new daeEnumType(dae); + type->_nameBindings.append("Gl_light_model_color_control_type"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("SINGLE_COLOR"); + ((daeEnumType*)type)->_values->append(GL_LIGHT_MODEL_COLOR_CONTROL_TYPE_SINGLE_COLOR); + ((daeEnumType*)type)->_strings->append("SEPARATE_SPECULAR_COLOR"); + ((daeEnumType*)type)->_values->append(GL_LIGHT_MODEL_COLOR_CONTROL_TYPE_SEPARATE_SPECULAR_COLOR); + atomicTypes.append( type ); + + // ENUM: Gl_logic_op_type + type = new daeEnumType(dae); + type->_nameBindings.append("Gl_logic_op_type"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("CLEAR"); + ((daeEnumType*)type)->_values->append(GL_LOGIC_OP_TYPE_CLEAR); + ((daeEnumType*)type)->_strings->append("AND"); + ((daeEnumType*)type)->_values->append(GL_LOGIC_OP_TYPE_AND); + ((daeEnumType*)type)->_strings->append("AND_REVERSE"); + ((daeEnumType*)type)->_values->append(GL_LOGIC_OP_TYPE_AND_REVERSE); + ((daeEnumType*)type)->_strings->append("COPY"); + ((daeEnumType*)type)->_values->append(GL_LOGIC_OP_TYPE_COPY); + ((daeEnumType*)type)->_strings->append("AND_INVERTED"); + ((daeEnumType*)type)->_values->append(GL_LOGIC_OP_TYPE_AND_INVERTED); + ((daeEnumType*)type)->_strings->append("NOOP"); + ((daeEnumType*)type)->_values->append(GL_LOGIC_OP_TYPE_NOOP); + ((daeEnumType*)type)->_strings->append("XOR"); + ((daeEnumType*)type)->_values->append(GL_LOGIC_OP_TYPE_XOR); + ((daeEnumType*)type)->_strings->append("OR"); + ((daeEnumType*)type)->_values->append(GL_LOGIC_OP_TYPE_OR); + ((daeEnumType*)type)->_strings->append("NOR"); + ((daeEnumType*)type)->_values->append(GL_LOGIC_OP_TYPE_NOR); + ((daeEnumType*)type)->_strings->append("EQUIV"); + ((daeEnumType*)type)->_values->append(GL_LOGIC_OP_TYPE_EQUIV); + ((daeEnumType*)type)->_strings->append("INVERT"); + ((daeEnumType*)type)->_values->append(GL_LOGIC_OP_TYPE_INVERT); + ((daeEnumType*)type)->_strings->append("OR_REVERSE"); + ((daeEnumType*)type)->_values->append(GL_LOGIC_OP_TYPE_OR_REVERSE); + ((daeEnumType*)type)->_strings->append("COPY_INVERTED"); + ((daeEnumType*)type)->_values->append(GL_LOGIC_OP_TYPE_COPY_INVERTED); + ((daeEnumType*)type)->_strings->append("NAND"); + ((daeEnumType*)type)->_values->append(GL_LOGIC_OP_TYPE_NAND); + ((daeEnumType*)type)->_strings->append("SET"); + ((daeEnumType*)type)->_values->append(GL_LOGIC_OP_TYPE_SET); + atomicTypes.append( type ); + + // ENUM: Gl_polygon_mode_type + type = new daeEnumType(dae); + type->_nameBindings.append("Gl_polygon_mode_type"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("POINT"); + ((daeEnumType*)type)->_values->append(GL_POLYGON_MODE_TYPE_POINT); + ((daeEnumType*)type)->_strings->append("LINE"); + ((daeEnumType*)type)->_values->append(GL_POLYGON_MODE_TYPE_LINE); + ((daeEnumType*)type)->_strings->append("FILL"); + ((daeEnumType*)type)->_values->append(GL_POLYGON_MODE_TYPE_FILL); + atomicTypes.append( type ); + + // ENUM: Gl_shade_model_type + type = new daeEnumType(dae); + type->_nameBindings.append("Gl_shade_model_type"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("FLAT"); + ((daeEnumType*)type)->_values->append(GL_SHADE_MODEL_TYPE_FLAT); + ((daeEnumType*)type)->_strings->append("SMOOTH"); + ((daeEnumType*)type)->_values->append(GL_SHADE_MODEL_TYPE_SMOOTH); + atomicTypes.append( type ); + + // TYPEDEF: Gl_alpha_value_type //check if this type has an existing base + type = atomicTypes.get("xsFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Gl_alpha_value_type"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Gl_alpha_value_type"); + } + + // ENUM: Gl_enumeration + type = new daeEnumType(dae); + type->_nameBindings.append("Gl_enumeration"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("ZERO"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_ZERO); + ((daeEnumType*)type)->_strings->append("ONE"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_ONE); + ((daeEnumType*)type)->_strings->append("SRC_COLOR"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_SRC_COLOR); + ((daeEnumType*)type)->_strings->append("ONE_MINUS_SRC_COLOR"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_ONE_MINUS_SRC_COLOR); + ((daeEnumType*)type)->_strings->append("DEST_COLOR"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_DEST_COLOR); + ((daeEnumType*)type)->_strings->append("ONE_MINUS_DEST_COLOR"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_ONE_MINUS_DEST_COLOR); + ((daeEnumType*)type)->_strings->append("SRC_ALPHA"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_SRC_ALPHA); + ((daeEnumType*)type)->_strings->append("ONE_MINUS_SRC_ALPHA"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_ONE_MINUS_SRC_ALPHA); + ((daeEnumType*)type)->_strings->append("DST_ALPHA"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_DST_ALPHA); + ((daeEnumType*)type)->_strings->append("ONE_MINUS_DST_ALPHA"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_ONE_MINUS_DST_ALPHA); + ((daeEnumType*)type)->_strings->append("CONSTANT_COLOR"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_CONSTANT_COLOR); + ((daeEnumType*)type)->_strings->append("ONE_MINUS_CONSTANT_COLOR"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_ONE_MINUS_CONSTANT_COLOR); + ((daeEnumType*)type)->_strings->append("CONSTANT_ALPHA"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_CONSTANT_ALPHA); + ((daeEnumType*)type)->_strings->append("ONE_MINUS_CONSTANT_ALPHA"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_ONE_MINUS_CONSTANT_ALPHA); + ((daeEnumType*)type)->_strings->append("SRC_ALPHA_SATURATE"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_SRC_ALPHA_SATURATE); + ((daeEnumType*)type)->_strings->append("FRONT"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_FRONT); + ((daeEnumType*)type)->_strings->append("BACK"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_BACK); + ((daeEnumType*)type)->_strings->append("FRONT_AND_BACK"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_FRONT_AND_BACK); + ((daeEnumType*)type)->_strings->append("FUNC_ADD"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_FUNC_ADD); + ((daeEnumType*)type)->_strings->append("FUNC_SUBTRACT"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_FUNC_SUBTRACT); + ((daeEnumType*)type)->_strings->append("FUNC_REVERSE_SUBTRACT"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_FUNC_REVERSE_SUBTRACT); + ((daeEnumType*)type)->_strings->append("MIN"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_MIN); + ((daeEnumType*)type)->_strings->append("MAX"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_MAX); + ((daeEnumType*)type)->_strings->append("NEVER"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_NEVER); + ((daeEnumType*)type)->_strings->append("LESS"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_LESS); + ((daeEnumType*)type)->_strings->append("LEQUAL"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_LEQUAL); + ((daeEnumType*)type)->_strings->append("EQUAL"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_EQUAL); + ((daeEnumType*)type)->_strings->append("GREATER"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_GREATER); + ((daeEnumType*)type)->_strings->append("NOTEQUAL"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_NOTEQUAL); + ((daeEnumType*)type)->_strings->append("GEQUAL"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_GEQUAL); + ((daeEnumType*)type)->_strings->append("ALWAYS"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_ALWAYS); + ((daeEnumType*)type)->_strings->append("KEEP"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_KEEP); + ((daeEnumType*)type)->_strings->append("REPLACE"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_REPLACE); + ((daeEnumType*)type)->_strings->append("INCR"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_INCR); + ((daeEnumType*)type)->_strings->append("DECR"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_DECR); + ((daeEnumType*)type)->_strings->append("INVERT"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_INVERT); + ((daeEnumType*)type)->_strings->append("INCR_WRAP"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_INCR_WRAP); + ((daeEnumType*)type)->_strings->append("DECR_WRAP"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_DECR_WRAP); + ((daeEnumType*)type)->_strings->append("EMISSION"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_EMISSION); + ((daeEnumType*)type)->_strings->append("AMBIENT"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_AMBIENT); + ((daeEnumType*)type)->_strings->append("DIFFUSE"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_DIFFUSE); + ((daeEnumType*)type)->_strings->append("SPECULAR"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_SPECULAR); + ((daeEnumType*)type)->_strings->append("AMBIENT_AND_DIFFUSE"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_AMBIENT_AND_DIFFUSE); + ((daeEnumType*)type)->_strings->append("LINEAR"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_LINEAR); + ((daeEnumType*)type)->_strings->append("EXP"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_EXP); + ((daeEnumType*)type)->_strings->append("EXP2"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_EXP2); + ((daeEnumType*)type)->_strings->append("FOG_COORDINATE"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_FOG_COORDINATE); + ((daeEnumType*)type)->_strings->append("FRAGMENT_DEPTH"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_FRAGMENT_DEPTH); + ((daeEnumType*)type)->_strings->append("CW"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_CW); + ((daeEnumType*)type)->_strings->append("CCW"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_CCW); + ((daeEnumType*)type)->_strings->append("SINGLE_COLOR"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_SINGLE_COLOR); + ((daeEnumType*)type)->_strings->append("SEPARATE_SPECULAR_COLOR"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_SEPARATE_SPECULAR_COLOR); + ((daeEnumType*)type)->_strings->append("CLEAR"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_CLEAR); + ((daeEnumType*)type)->_strings->append("AND"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_AND); + ((daeEnumType*)type)->_strings->append("AND_REVERSE"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_AND_REVERSE); + ((daeEnumType*)type)->_strings->append("COPY"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_COPY); + ((daeEnumType*)type)->_strings->append("AND_INVERTED"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_AND_INVERTED); + ((daeEnumType*)type)->_strings->append("NOOP"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_NOOP); + ((daeEnumType*)type)->_strings->append("XOR"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_XOR); + ((daeEnumType*)type)->_strings->append("OR"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_OR); + ((daeEnumType*)type)->_strings->append("NOR"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_NOR); + ((daeEnumType*)type)->_strings->append("EQUIV"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_EQUIV); + ((daeEnumType*)type)->_strings->append("OR_REVERSE"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_OR_REVERSE); + ((daeEnumType*)type)->_strings->append("COPY_INVERTED"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_COPY_INVERTED); + ((daeEnumType*)type)->_strings->append("NAND"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_NAND); + ((daeEnumType*)type)->_strings->append("SET"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_SET); + ((daeEnumType*)type)->_strings->append("POINT"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_POINT); + ((daeEnumType*)type)->_strings->append("LINE"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_LINE); + ((daeEnumType*)type)->_strings->append("FILL"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_FILL); + ((daeEnumType*)type)->_strings->append("FLAT"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_FLAT); + ((daeEnumType*)type)->_strings->append("SMOOTH"); + ((daeEnumType*)type)->_values->append(GL_ENUMERATION_SMOOTH); + atomicTypes.append( type ); + + // TYPEDEF: Glsl_float //check if this type has an existing base + type = atomicTypes.get("xsFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Glsl_float"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Glsl_float"); + } + + // TYPEDEF: Glsl_int //check if this type has an existing base + type = atomicTypes.get("xsInt"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Glsl_int"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Glsl_int"); + } + + // TYPEDEF: Glsl_bool //check if this type has an existing base + type = atomicTypes.get("xsBoolean"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Glsl_bool"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Glsl_bool"); + } + + // TYPEDEF: Glsl_ListOfBool //check if this type has an existing base + type = atomicTypes.get("Glsl_bool"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Glsl_ListOfBool"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Glsl_ListOfBool"); + } + + // TYPEDEF: Glsl_ListOfFloat //check if this type has an existing base + type = atomicTypes.get("Glsl_float"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Glsl_ListOfFloat"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Glsl_ListOfFloat"); + } + + // TYPEDEF: Glsl_ListOfInt //check if this type has an existing base + type = atomicTypes.get("Glsl_int"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Glsl_ListOfInt"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Glsl_ListOfInt"); + } + + // TYPEDEF: Glsl_bool2 //check if this type has an existing base + type = atomicTypes.get("Glsl_ListOfBool"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Glsl_bool2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Glsl_bool2"); + } + + // TYPEDEF: Glsl_bool3 //check if this type has an existing base + type = atomicTypes.get("Glsl_ListOfBool"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Glsl_bool3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Glsl_bool3"); + } + + // TYPEDEF: Glsl_bool4 //check if this type has an existing base + type = atomicTypes.get("Glsl_ListOfBool"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Glsl_bool4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Glsl_bool4"); + } + + // TYPEDEF: Glsl_float2 //check if this type has an existing base + type = atomicTypes.get("Glsl_ListOfFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Glsl_float2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Glsl_float2"); + } + + // TYPEDEF: Glsl_float3 //check if this type has an existing base + type = atomicTypes.get("Glsl_ListOfFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Glsl_float3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Glsl_float3"); + } + + // TYPEDEF: Glsl_float4 //check if this type has an existing base + type = atomicTypes.get("Glsl_ListOfFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Glsl_float4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Glsl_float4"); + } + + // TYPEDEF: Glsl_float2x2 //check if this type has an existing base + type = atomicTypes.get("Glsl_ListOfFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Glsl_float2x2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Glsl_float2x2"); + } + + // TYPEDEF: Glsl_float3x3 //check if this type has an existing base + type = atomicTypes.get("Glsl_ListOfFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Glsl_float3x3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Glsl_float3x3"); + } + + // TYPEDEF: Glsl_float4x4 //check if this type has an existing base + type = atomicTypes.get("Glsl_ListOfFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Glsl_float4x4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Glsl_float4x4"); + } + + // TYPEDEF: Glsl_int2 //check if this type has an existing base + type = atomicTypes.get("Glsl_ListOfInt"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Glsl_int2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Glsl_int2"); + } + + // TYPEDEF: Glsl_int3 //check if this type has an existing base + type = atomicTypes.get("Glsl_ListOfInt"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Glsl_int3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Glsl_int3"); + } + + // TYPEDEF: Glsl_int4 //check if this type has an existing base + type = atomicTypes.get("Glsl_ListOfInt"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Glsl_int4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Glsl_int4"); + } + + // ENUM: Glsl_pipeline_stage + type = new daeEnumType(dae); + type->_nameBindings.append("Glsl_pipeline_stage"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("VERTEXPROGRAM"); + ((daeEnumType*)type)->_values->append(GLSL_PIPELINE_STAGE_VERTEXPROGRAM); + ((daeEnumType*)type)->_strings->append("FRAGMENTPROGRAM"); + ((daeEnumType*)type)->_values->append(GLSL_PIPELINE_STAGE_FRAGMENTPROGRAM); + atomicTypes.append( type ); + + // TYPEDEF: Glsl_identifier //check if this type has an existing base + type = atomicTypes.get("xsToken"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Glsl_identifier"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Glsl_identifier"); + } + + // TYPEDEF: Cg_bool //check if this type has an existing base + type = atomicTypes.get("xsBoolean"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_bool"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_bool"); + } + + // TYPEDEF: Cg_float //check if this type has an existing base + type = atomicTypes.get("xsFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_float"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_float"); + } + + // TYPEDEF: Cg_int //check if this type has an existing base + type = atomicTypes.get("xsInt"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_int"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_int"); + } + + // TYPEDEF: Cg_half //check if this type has an existing base + type = atomicTypes.get("xsFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_half"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_half"); + } + + // TYPEDEF: Cg_fixed //check if this type has an existing base + type = atomicTypes.get("xsFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_fixed"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_fixed"); + } + + // TYPEDEF: Cg_bool1 //check if this type has an existing base + type = atomicTypes.get("xsBoolean"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_bool1"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_bool1"); + } + + // TYPEDEF: Cg_float1 //check if this type has an existing base + type = atomicTypes.get("xsFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_float1"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_float1"); + } + + // TYPEDEF: Cg_int1 //check if this type has an existing base + type = atomicTypes.get("xsInt"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_int1"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_int1"); + } + + // TYPEDEF: Cg_half1 //check if this type has an existing base + type = atomicTypes.get("xsFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_half1"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_half1"); + } + + // TYPEDEF: Cg_fixed1 //check if this type has an existing base + type = atomicTypes.get("xsFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_fixed1"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_fixed1"); + } + + // TYPEDEF: Cg_ListOfBool //check if this type has an existing base + type = atomicTypes.get("Cg_bool"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_ListOfBool"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_ListOfBool"); + } + + // TYPEDEF: Cg_ListOfFloat //check if this type has an existing base + type = atomicTypes.get("Cg_float"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_ListOfFloat"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_ListOfFloat"); + } + + // TYPEDEF: Cg_ListOfInt //check if this type has an existing base + type = atomicTypes.get("Cg_int"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_ListOfInt"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_ListOfInt"); + } + + // TYPEDEF: Cg_ListOfHalf //check if this type has an existing base + type = atomicTypes.get("Cg_half"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_ListOfHalf"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_ListOfHalf"); + } + + // TYPEDEF: Cg_ListOfFixed //check if this type has an existing base + type = atomicTypes.get("Cg_fixed"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_ListOfFixed"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_ListOfFixed"); + } + + // TYPEDEF: Cg_bool2 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfBool"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_bool2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_bool2"); + } + + // TYPEDEF: Cg_bool3 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfBool"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_bool3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_bool3"); + } + + // TYPEDEF: Cg_bool4 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfBool"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_bool4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_bool4"); + } + + // TYPEDEF: Cg_bool1x1 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfBool"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_bool1x1"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_bool1x1"); + } + + // TYPEDEF: Cg_bool1x2 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfBool"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_bool1x2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_bool1x2"); + } + + // TYPEDEF: Cg_bool1x3 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfBool"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_bool1x3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_bool1x3"); + } + + // TYPEDEF: Cg_bool1x4 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfBool"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_bool1x4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_bool1x4"); + } + + // TYPEDEF: Cg_bool2x1 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfBool"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_bool2x1"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_bool2x1"); + } + + // TYPEDEF: Cg_bool2x2 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfBool"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_bool2x2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_bool2x2"); + } + + // TYPEDEF: Cg_bool2x3 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfBool"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_bool2x3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_bool2x3"); + } + + // TYPEDEF: Cg_bool2x4 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfBool"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_bool2x4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_bool2x4"); + } + + // TYPEDEF: Cg_bool3x1 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfBool"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_bool3x1"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_bool3x1"); + } + + // TYPEDEF: Cg_bool3x2 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfBool"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_bool3x2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_bool3x2"); + } + + // TYPEDEF: Cg_bool3x3 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfBool"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_bool3x3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_bool3x3"); + } + + // TYPEDEF: Cg_bool3x4 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfBool"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_bool3x4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_bool3x4"); + } + + // TYPEDEF: Cg_bool4x1 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfBool"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_bool4x1"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_bool4x1"); + } + + // TYPEDEF: Cg_bool4x2 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfBool"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_bool4x2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_bool4x2"); + } + + // TYPEDEF: Cg_bool4x3 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfBool"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_bool4x3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_bool4x3"); + } + + // TYPEDEF: Cg_bool4x4 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfBool"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_bool4x4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_bool4x4"); + } + + // TYPEDEF: Cg_float2 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_float2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_float2"); + } + + // TYPEDEF: Cg_float3 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_float3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_float3"); + } + + // TYPEDEF: Cg_float4 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_float4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_float4"); + } + + // TYPEDEF: Cg_float1x1 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_float1x1"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_float1x1"); + } + + // TYPEDEF: Cg_float1x2 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_float1x2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_float1x2"); + } + + // TYPEDEF: Cg_float1x3 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_float1x3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_float1x3"); + } + + // TYPEDEF: Cg_float1x4 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_float1x4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_float1x4"); + } + + // TYPEDEF: Cg_float2x1 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_float2x1"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_float2x1"); + } + + // TYPEDEF: Cg_float2x2 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_float2x2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_float2x2"); + } + + // TYPEDEF: Cg_float2x3 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_float2x3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_float2x3"); + } + + // TYPEDEF: Cg_float2x4 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_float2x4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_float2x4"); + } + + // TYPEDEF: Cg_float3x1 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_float3x1"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_float3x1"); + } + + // TYPEDEF: Cg_float3x2 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_float3x2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_float3x2"); + } + + // TYPEDEF: Cg_float3x3 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_float3x3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_float3x3"); + } + + // TYPEDEF: Cg_float3x4 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_float3x4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_float3x4"); + } + + // TYPEDEF: Cg_float4x1 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_float4x1"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_float4x1"); + } + + // TYPEDEF: Cg_float4x2 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_float4x2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_float4x2"); + } + + // TYPEDEF: Cg_float4x3 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_float4x3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_float4x3"); + } + + // TYPEDEF: Cg_float4x4 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFloat"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_float4x4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_float4x4"); + } + + // TYPEDEF: Cg_int2 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfInt"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_int2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_int2"); + } + + // TYPEDEF: Cg_int3 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfInt"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_int3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_int3"); + } + + // TYPEDEF: Cg_int4 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfInt"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_int4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_int4"); + } + + // TYPEDEF: Cg_int1x1 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfInt"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_int1x1"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_int1x1"); + } + + // TYPEDEF: Cg_int1x2 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfInt"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_int1x2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_int1x2"); + } + + // TYPEDEF: Cg_int1x3 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfInt"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_int1x3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_int1x3"); + } + + // TYPEDEF: Cg_int1x4 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfInt"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_int1x4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_int1x4"); + } + + // TYPEDEF: Cg_int2x1 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfInt"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_int2x1"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_int2x1"); + } + + // TYPEDEF: Cg_int2x2 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfInt"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_int2x2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_int2x2"); + } + + // TYPEDEF: Cg_int2x3 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfInt"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_int2x3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_int2x3"); + } + + // TYPEDEF: Cg_int2x4 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfInt"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_int2x4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_int2x4"); + } + + // TYPEDEF: Cg_int3x1 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfInt"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_int3x1"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_int3x1"); + } + + // TYPEDEF: Cg_int3x2 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfInt"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_int3x2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_int3x2"); + } + + // TYPEDEF: Cg_int3x3 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfInt"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_int3x3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_int3x3"); + } + + // TYPEDEF: Cg_int3x4 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfInt"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_int3x4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_int3x4"); + } + + // TYPEDEF: Cg_int4x1 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfInt"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_int4x1"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_int4x1"); + } + + // TYPEDEF: Cg_int4x2 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfInt"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_int4x2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_int4x2"); + } + + // TYPEDEF: Cg_int4x3 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfInt"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_int4x3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_int4x3"); + } + + // TYPEDEF: Cg_int4x4 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfInt"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_int4x4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_int4x4"); + } + + // TYPEDEF: Cg_half2 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfHalf"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_half2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_half2"); + } + + // TYPEDEF: Cg_half3 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfHalf"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_half3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_half3"); + } + + // TYPEDEF: Cg_half4 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfHalf"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_half4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_half4"); + } + + // TYPEDEF: Cg_half1x1 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfHalf"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_half1x1"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_half1x1"); + } + + // TYPEDEF: Cg_half1x2 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfHalf"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_half1x2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_half1x2"); + } + + // TYPEDEF: Cg_half1x3 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfHalf"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_half1x3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_half1x3"); + } + + // TYPEDEF: Cg_half1x4 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfHalf"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_half1x4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_half1x4"); + } + + // TYPEDEF: Cg_half2x1 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfHalf"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_half2x1"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_half2x1"); + } + + // TYPEDEF: Cg_half2x2 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfHalf"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_half2x2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_half2x2"); + } + + // TYPEDEF: Cg_half2x3 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfHalf"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_half2x3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_half2x3"); + } + + // TYPEDEF: Cg_half2x4 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfHalf"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_half2x4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_half2x4"); + } + + // TYPEDEF: Cg_half3x1 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfHalf"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_half3x1"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_half3x1"); + } + + // TYPEDEF: Cg_half3x2 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfHalf"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_half3x2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_half3x2"); + } + + // TYPEDEF: Cg_half3x3 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfHalf"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_half3x3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_half3x3"); + } + + // TYPEDEF: Cg_half3x4 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfHalf"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_half3x4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_half3x4"); + } + + // TYPEDEF: Cg_half4x1 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfHalf"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_half4x1"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_half4x1"); + } + + // TYPEDEF: Cg_half4x2 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfHalf"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_half4x2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_half4x2"); + } + + // TYPEDEF: Cg_half4x3 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfHalf"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_half4x3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_half4x3"); + } + + // TYPEDEF: Cg_half4x4 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfHalf"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_half4x4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_half4x4"); + } + + // TYPEDEF: Cg_fixed2 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFixed"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_fixed2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_fixed2"); + } + + // TYPEDEF: Cg_fixed3 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFixed"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_fixed3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_fixed3"); + } + + // TYPEDEF: Cg_fixed4 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFixed"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_fixed4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_fixed4"); + } + + // TYPEDEF: Cg_fixed1x1 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFixed"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_fixed1x1"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_fixed1x1"); + } + + // TYPEDEF: Cg_fixed1x2 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFixed"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_fixed1x2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_fixed1x2"); + } + + // TYPEDEF: Cg_fixed1x3 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFixed"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_fixed1x3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_fixed1x3"); + } + + // TYPEDEF: Cg_fixed1x4 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFixed"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_fixed1x4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_fixed1x4"); + } + + // TYPEDEF: Cg_fixed2x1 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFixed"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_fixed2x1"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_fixed2x1"); + } + + // TYPEDEF: Cg_fixed2x2 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFixed"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_fixed2x2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_fixed2x2"); + } + + // TYPEDEF: Cg_fixed2x3 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFixed"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_fixed2x3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_fixed2x3"); + } + + // TYPEDEF: Cg_fixed2x4 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFixed"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_fixed2x4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_fixed2x4"); + } + + // TYPEDEF: Cg_fixed3x1 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFixed"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_fixed3x1"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_fixed3x1"); + } + + // TYPEDEF: Cg_fixed3x2 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFixed"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_fixed3x2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_fixed3x2"); + } + + // TYPEDEF: Cg_fixed3x3 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFixed"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_fixed3x3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_fixed3x3"); + } + + // TYPEDEF: Cg_fixed3x4 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFixed"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_fixed3x4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_fixed3x4"); + } + + // TYPEDEF: Cg_fixed4x1 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFixed"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_fixed4x1"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_fixed4x1"); + } + + // TYPEDEF: Cg_fixed4x2 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFixed"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_fixed4x2"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_fixed4x2"); + } + + // TYPEDEF: Cg_fixed4x3 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFixed"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_fixed4x3"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_fixed4x3"); + } + + // TYPEDEF: Cg_fixed4x4 //check if this type has an existing base + type = atomicTypes.get("Cg_ListOfFixed"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_fixed4x4"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_fixed4x4"); + } + + // ENUM: Cg_pipeline_stage + type = new daeEnumType(dae); + type->_nameBindings.append("Cg_pipeline_stage"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("VERTEX"); + ((daeEnumType*)type)->_values->append(CG_PIPELINE_STAGE_VERTEX); + ((daeEnumType*)type)->_strings->append("FRAGMENT"); + ((daeEnumType*)type)->_values->append(CG_PIPELINE_STAGE_FRAGMENT); + atomicTypes.append( type ); + + // TYPEDEF: Cg_identifier //check if this type has an existing base + type = atomicTypes.get("xsToken"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Cg_identifier"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Cg_identifier"); + } + + // TYPEDEF: GLES_MAX_LIGHTS_index //check if this type has an existing base + type = atomicTypes.get("xsNonNegativeInteger"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("GLES_MAX_LIGHTS_index"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("GLES_MAX_LIGHTS_index"); + } + + // TYPEDEF: GLES_MAX_CLIP_PLANES_index //check if this type has an existing base + type = atomicTypes.get("xsNonNegativeInteger"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("GLES_MAX_CLIP_PLANES_index"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("GLES_MAX_CLIP_PLANES_index"); + } + + // TYPEDEF: GLES_MAX_TEXTURE_COORDS_index //check if this type has an existing base + type = atomicTypes.get("xsNonNegativeInteger"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("GLES_MAX_TEXTURE_COORDS_index"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("GLES_MAX_TEXTURE_COORDS_index"); + } + + // TYPEDEF: GLES_MAX_TEXTURE_IMAGE_UNITS_index //check if this type has an existing base + type = atomicTypes.get("xsNonNegativeInteger"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("GLES_MAX_TEXTURE_IMAGE_UNITS_index"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("GLES_MAX_TEXTURE_IMAGE_UNITS_index"); + } + + // ENUM: Gles_texenv_mode_enums + type = new daeEnumType(dae); + type->_nameBindings.append("Gles_texenv_mode_enums"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("REPLACE"); + ((daeEnumType*)type)->_values->append(GLES_TEXENV_MODE_ENUMS_REPLACE); + ((daeEnumType*)type)->_strings->append("MODULATE"); + ((daeEnumType*)type)->_values->append(GLES_TEXENV_MODE_ENUMS_MODULATE); + ((daeEnumType*)type)->_strings->append("DECAL"); + ((daeEnumType*)type)->_values->append(GLES_TEXENV_MODE_ENUMS_DECAL); + ((daeEnumType*)type)->_strings->append("BLEND"); + ((daeEnumType*)type)->_values->append(GLES_TEXENV_MODE_ENUMS_BLEND); + ((daeEnumType*)type)->_strings->append("ADD"); + ((daeEnumType*)type)->_values->append(GLES_TEXENV_MODE_ENUMS_ADD); + atomicTypes.append( type ); + + // ENUM: Gles_texcombiner_operatorRGB_enums + type = new daeEnumType(dae); + type->_nameBindings.append("Gles_texcombiner_operatorRGB_enums"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("REPLACE"); + ((daeEnumType*)type)->_values->append(GLES_TEXCOMBINER_OPERATORRGB_ENUMS_REPLACE); + ((daeEnumType*)type)->_strings->append("MODULATE"); + ((daeEnumType*)type)->_values->append(GLES_TEXCOMBINER_OPERATORRGB_ENUMS_MODULATE); + ((daeEnumType*)type)->_strings->append("ADD"); + ((daeEnumType*)type)->_values->append(GLES_TEXCOMBINER_OPERATORRGB_ENUMS_ADD); + ((daeEnumType*)type)->_strings->append("ADD_SIGNED"); + ((daeEnumType*)type)->_values->append(GLES_TEXCOMBINER_OPERATORRGB_ENUMS_ADD_SIGNED); + ((daeEnumType*)type)->_strings->append("INTERPOLATE"); + ((daeEnumType*)type)->_values->append(GLES_TEXCOMBINER_OPERATORRGB_ENUMS_INTERPOLATE); + ((daeEnumType*)type)->_strings->append("SUBTRACT"); + ((daeEnumType*)type)->_values->append(GLES_TEXCOMBINER_OPERATORRGB_ENUMS_SUBTRACT); + ((daeEnumType*)type)->_strings->append("DOT3_RGB"); + ((daeEnumType*)type)->_values->append(GLES_TEXCOMBINER_OPERATORRGB_ENUMS_DOT3_RGB); + ((daeEnumType*)type)->_strings->append("DOT3_RGBA"); + ((daeEnumType*)type)->_values->append(GLES_TEXCOMBINER_OPERATORRGB_ENUMS_DOT3_RGBA); + atomicTypes.append( type ); + + // ENUM: Gles_texcombiner_operatorAlpha_enums + type = new daeEnumType(dae); + type->_nameBindings.append("Gles_texcombiner_operatorAlpha_enums"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("REPLACE"); + ((daeEnumType*)type)->_values->append(GLES_TEXCOMBINER_OPERATORALPHA_ENUMS_REPLACE); + ((daeEnumType*)type)->_strings->append("MODULATE"); + ((daeEnumType*)type)->_values->append(GLES_TEXCOMBINER_OPERATORALPHA_ENUMS_MODULATE); + ((daeEnumType*)type)->_strings->append("ADD"); + ((daeEnumType*)type)->_values->append(GLES_TEXCOMBINER_OPERATORALPHA_ENUMS_ADD); + ((daeEnumType*)type)->_strings->append("ADD_SIGNED"); + ((daeEnumType*)type)->_values->append(GLES_TEXCOMBINER_OPERATORALPHA_ENUMS_ADD_SIGNED); + ((daeEnumType*)type)->_strings->append("INTERPOLATE"); + ((daeEnumType*)type)->_values->append(GLES_TEXCOMBINER_OPERATORALPHA_ENUMS_INTERPOLATE); + ((daeEnumType*)type)->_strings->append("SUBTRACT"); + ((daeEnumType*)type)->_values->append(GLES_TEXCOMBINER_OPERATORALPHA_ENUMS_SUBTRACT); + atomicTypes.append( type ); + + // ENUM: Gles_texcombiner_source_enums + type = new daeEnumType(dae); + type->_nameBindings.append("Gles_texcombiner_source_enums"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("TEXTURE"); + ((daeEnumType*)type)->_values->append(GLES_TEXCOMBINER_SOURCE_ENUMS_TEXTURE); + ((daeEnumType*)type)->_strings->append("CONSTANT"); + ((daeEnumType*)type)->_values->append(GLES_TEXCOMBINER_SOURCE_ENUMS_CONSTANT); + ((daeEnumType*)type)->_strings->append("PRIMARY"); + ((daeEnumType*)type)->_values->append(GLES_TEXCOMBINER_SOURCE_ENUMS_PRIMARY); + ((daeEnumType*)type)->_strings->append("PREVIOUS"); + ((daeEnumType*)type)->_values->append(GLES_TEXCOMBINER_SOURCE_ENUMS_PREVIOUS); + atomicTypes.append( type ); + + // ENUM: Gles_texcombiner_operandRGB_enums + type = new daeEnumType(dae); + type->_nameBindings.append("Gles_texcombiner_operandRGB_enums"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("SRC_COLOR"); + ((daeEnumType*)type)->_values->append(GLES_TEXCOMBINER_OPERANDRGB_ENUMS_SRC_COLOR); + ((daeEnumType*)type)->_strings->append("ONE_MINUS_SRC_COLOR"); + ((daeEnumType*)type)->_values->append(GLES_TEXCOMBINER_OPERANDRGB_ENUMS_ONE_MINUS_SRC_COLOR); + ((daeEnumType*)type)->_strings->append("SRC_ALPHA"); + ((daeEnumType*)type)->_values->append(GLES_TEXCOMBINER_OPERANDRGB_ENUMS_SRC_ALPHA); + ((daeEnumType*)type)->_strings->append("ONE_MINUS_SRC_ALPHA"); + ((daeEnumType*)type)->_values->append(GLES_TEXCOMBINER_OPERANDRGB_ENUMS_ONE_MINUS_SRC_ALPHA); + atomicTypes.append( type ); + + // ENUM: Gles_texcombiner_operandAlpha_enums + type = new daeEnumType(dae); + type->_nameBindings.append("Gles_texcombiner_operandAlpha_enums"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("SRC_ALPHA"); + ((daeEnumType*)type)->_values->append(GLES_TEXCOMBINER_OPERANDALPHA_ENUMS_SRC_ALPHA); + ((daeEnumType*)type)->_strings->append("ONE_MINUS_SRC_ALPHA"); + ((daeEnumType*)type)->_values->append(GLES_TEXCOMBINER_OPERANDALPHA_ENUMS_ONE_MINUS_SRC_ALPHA); + atomicTypes.append( type ); + + // TYPEDEF: Gles_texcombiner_argument_index_type //check if this type has an existing base + type = atomicTypes.get("xsNonNegativeInteger"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Gles_texcombiner_argument_index_type"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Gles_texcombiner_argument_index_type"); + } + + // ENUM: Gles_sampler_wrap + type = new daeEnumType(dae); + type->_nameBindings.append("Gles_sampler_wrap"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("REPEAT"); + ((daeEnumType*)type)->_values->append(GLES_SAMPLER_WRAP_REPEAT); + ((daeEnumType*)type)->_strings->append("CLAMP"); + ((daeEnumType*)type)->_values->append(GLES_SAMPLER_WRAP_CLAMP); + ((daeEnumType*)type)->_strings->append("CLAMP_TO_EDGE"); + ((daeEnumType*)type)->_values->append(GLES_SAMPLER_WRAP_CLAMP_TO_EDGE); + ((daeEnumType*)type)->_strings->append("MIRRORED_REPEAT"); + ((daeEnumType*)type)->_values->append(GLES_SAMPLER_WRAP_MIRRORED_REPEAT); + atomicTypes.append( type ); + + // ENUM: Gles_stencil_op_type + type = new daeEnumType(dae); + type->_nameBindings.append("Gles_stencil_op_type"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("KEEP"); + ((daeEnumType*)type)->_values->append(GLES_STENCIL_OP_TYPE_KEEP); + ((daeEnumType*)type)->_strings->append("ZERO"); + ((daeEnumType*)type)->_values->append(GLES_STENCIL_OP_TYPE_ZERO); + ((daeEnumType*)type)->_strings->append("REPLACE"); + ((daeEnumType*)type)->_values->append(GLES_STENCIL_OP_TYPE_REPLACE); + ((daeEnumType*)type)->_strings->append("INCR"); + ((daeEnumType*)type)->_values->append(GLES_STENCIL_OP_TYPE_INCR); + ((daeEnumType*)type)->_strings->append("DECR"); + ((daeEnumType*)type)->_values->append(GLES_STENCIL_OP_TYPE_DECR); + ((daeEnumType*)type)->_strings->append("INVERT"); + ((daeEnumType*)type)->_values->append(GLES_STENCIL_OP_TYPE_INVERT); + atomicTypes.append( type ); + + // ENUM: Gles_enumeration + type = new daeEnumType(dae); + type->_nameBindings.append("Gles_enumeration"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("ZERO"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_ZERO); + ((daeEnumType*)type)->_strings->append("ONE"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_ONE); + ((daeEnumType*)type)->_strings->append("SRC_COLOR"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_SRC_COLOR); + ((daeEnumType*)type)->_strings->append("ONE_MINUS_SRC_COLOR"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_ONE_MINUS_SRC_COLOR); + ((daeEnumType*)type)->_strings->append("DEST_COLOR"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_DEST_COLOR); + ((daeEnumType*)type)->_strings->append("ONE_MINUS_DEST_COLOR"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_ONE_MINUS_DEST_COLOR); + ((daeEnumType*)type)->_strings->append("SRC_ALPHA"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_SRC_ALPHA); + ((daeEnumType*)type)->_strings->append("ONE_MINUS_SRC_ALPHA"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_ONE_MINUS_SRC_ALPHA); + ((daeEnumType*)type)->_strings->append("DST_ALPHA"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_DST_ALPHA); + ((daeEnumType*)type)->_strings->append("ONE_MINUS_DST_ALPHA"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_ONE_MINUS_DST_ALPHA); + ((daeEnumType*)type)->_strings->append("CONSTANT_COLOR"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_CONSTANT_COLOR); + ((daeEnumType*)type)->_strings->append("ONE_MINUS_CONSTANT_COLOR"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_ONE_MINUS_CONSTANT_COLOR); + ((daeEnumType*)type)->_strings->append("CONSTANT_ALPHA"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_CONSTANT_ALPHA); + ((daeEnumType*)type)->_strings->append("ONE_MINUS_CONSTANT_ALPHA"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_ONE_MINUS_CONSTANT_ALPHA); + ((daeEnumType*)type)->_strings->append("SRC_ALPHA_SATURATE"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_SRC_ALPHA_SATURATE); + ((daeEnumType*)type)->_strings->append("FRONT"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_FRONT); + ((daeEnumType*)type)->_strings->append("BACK"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_BACK); + ((daeEnumType*)type)->_strings->append("FRONT_AND_BACK"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_FRONT_AND_BACK); + ((daeEnumType*)type)->_strings->append("NEVER"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_NEVER); + ((daeEnumType*)type)->_strings->append("LESS"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_LESS); + ((daeEnumType*)type)->_strings->append("LEQUAL"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_LEQUAL); + ((daeEnumType*)type)->_strings->append("EQUAL"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_EQUAL); + ((daeEnumType*)type)->_strings->append("GREATER"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_GREATER); + ((daeEnumType*)type)->_strings->append("NOTEQUAL"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_NOTEQUAL); + ((daeEnumType*)type)->_strings->append("GEQUAL"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_GEQUAL); + ((daeEnumType*)type)->_strings->append("ALWAYS"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_ALWAYS); + ((daeEnumType*)type)->_strings->append("KEEP"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_KEEP); + ((daeEnumType*)type)->_strings->append("REPLACE"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_REPLACE); + ((daeEnumType*)type)->_strings->append("INCR"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_INCR); + ((daeEnumType*)type)->_strings->append("DECR"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_DECR); + ((daeEnumType*)type)->_strings->append("INVERT"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_INVERT); + ((daeEnumType*)type)->_strings->append("INCR_WRAP"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_INCR_WRAP); + ((daeEnumType*)type)->_strings->append("DECR_WRAP"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_DECR_WRAP); + ((daeEnumType*)type)->_strings->append("EMISSION"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_EMISSION); + ((daeEnumType*)type)->_strings->append("AMBIENT"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_AMBIENT); + ((daeEnumType*)type)->_strings->append("DIFFUSE"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_DIFFUSE); + ((daeEnumType*)type)->_strings->append("SPECULAR"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_SPECULAR); + ((daeEnumType*)type)->_strings->append("AMBIENT_AND_DIFFUSE"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_AMBIENT_AND_DIFFUSE); + ((daeEnumType*)type)->_strings->append("LINEAR"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_LINEAR); + ((daeEnumType*)type)->_strings->append("EXP"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_EXP); + ((daeEnumType*)type)->_strings->append("EXP2"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_EXP2); + ((daeEnumType*)type)->_strings->append("CW"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_CW); + ((daeEnumType*)type)->_strings->append("CCW"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_CCW); + ((daeEnumType*)type)->_strings->append("SINGLE_COLOR"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_SINGLE_COLOR); + ((daeEnumType*)type)->_strings->append("SEPARATE_SPECULAR_COLOR"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_SEPARATE_SPECULAR_COLOR); + ((daeEnumType*)type)->_strings->append("CLEAR"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_CLEAR); + ((daeEnumType*)type)->_strings->append("AND"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_AND); + ((daeEnumType*)type)->_strings->append("AND_REVERSE"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_AND_REVERSE); + ((daeEnumType*)type)->_strings->append("COPY"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_COPY); + ((daeEnumType*)type)->_strings->append("AND_INVERTED"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_AND_INVERTED); + ((daeEnumType*)type)->_strings->append("NOOP"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_NOOP); + ((daeEnumType*)type)->_strings->append("XOR"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_XOR); + ((daeEnumType*)type)->_strings->append("OR"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_OR); + ((daeEnumType*)type)->_strings->append("NOR"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_NOR); + ((daeEnumType*)type)->_strings->append("EQUIV"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_EQUIV); + ((daeEnumType*)type)->_strings->append("OR_REVERSE"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_OR_REVERSE); + ((daeEnumType*)type)->_strings->append("COPY_INVERTED"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_COPY_INVERTED); + ((daeEnumType*)type)->_strings->append("NAND"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_NAND); + ((daeEnumType*)type)->_strings->append("SET"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_SET); + ((daeEnumType*)type)->_strings->append("POINT"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_POINT); + ((daeEnumType*)type)->_strings->append("LINE"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_LINE); + ((daeEnumType*)type)->_strings->append("FILL"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_FILL); + ((daeEnumType*)type)->_strings->append("FLAT"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_FLAT); + ((daeEnumType*)type)->_strings->append("SMOOTH"); + ((daeEnumType*)type)->_values->append(GLES_ENUMERATION_SMOOTH); + atomicTypes.append( type ); + + // TYPEDEF: Gles_rendertarget_common //check if this type has an existing base + type = atomicTypes.get("xsNCName"); + if ( type == NULL ) { //register as a raw type + type = new daeRawRefType(dae); + type->_nameBindings.append("Gles_rendertarget_common"); + atomicTypes.append( type ); + } + else { //add binding to existing type + type->_nameBindings.append("Gles_rendertarget_common"); + } + + // ENUM: SpringType + type = new daeEnumType(dae); + type->_nameBindings.append("SpringType"); + ((daeEnumType*)type)->_strings = new daeStringRefArray; + ((daeEnumType*)type)->_values = new daeEnumArray; + ((daeEnumType*)type)->_strings->append("LINEAR"); + ((daeEnumType*)type)->_values->append(SPRINGTYPE_LINEAR); + ((daeEnumType*)type)->_strings->append("ANGULAR"); + ((daeEnumType*)type)->_values->append(SPRINGTYPE_ANGULAR); + atomicTypes.append( type ); + +} + +daeMetaElement* registerDomElements(DAE& dae) +{ + daeMetaElement* meta = domCOLLADA::registerElement(dae); + // Enable tracking of top level object by default + meta->setIsTrackableForQueries(true); + return meta; +} + +daeInt DLLSPEC colladaTypeCount() { + return 815; +} diff --git a/Engine/lib/collada/src/1.4/dom/domVertices.cpp b/Engine/lib/collada/src/1.4/dom/domVertices.cpp new file mode 100644 index 000000000..a7eb04d7b --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domVertices.cpp @@ -0,0 +1,90 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domVertices::create(DAE& dae) +{ + domVerticesRef ref = new domVertices(dae); + return ref; +} + + +daeMetaElement * +domVertices::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "vertices" ); + meta->registerClass(domVertices::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, -1 ); + mea->setName( "input" ); + mea->setOffset( daeOffsetOf(domVertices,elemInput_array) ); + mea->setElementType( domInputLocal::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domVertices,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domVertices , attrId )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domVertices , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domVertices)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/1.4/dom/domVisual_scene.cpp b/Engine/lib/collada/src/1.4/dom/domVisual_scene.cpp new file mode 100644 index 000000000..0144634e0 --- /dev/null +++ b/Engine/lib/collada/src/1.4/dom/domVisual_scene.cpp @@ -0,0 +1,245 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domVisual_scene::create(DAE& dae) +{ + domVisual_sceneRef ref = new domVisual_scene(dae); + return ref; +} + + +daeMetaElement * +domVisual_scene::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "visual_scene" ); + meta->registerClass(domVisual_scene::create); + + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementAttribute( meta, cm, 0, 0, 1 ); + mea->setName( "asset" ); + mea->setOffset( daeOffsetOf(domVisual_scene,elemAsset) ); + mea->setElementType( domAsset::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 1, 1, -1 ); + mea->setName( "node" ); + mea->setOffset( daeOffsetOf(domVisual_scene,elemNode_array) ); + mea->setElementType( domNode::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 2, 0, -1 ); + mea->setName( "evaluate_scene" ); + mea->setOffset( daeOffsetOf(domVisual_scene,elemEvaluate_scene_array) ); + mea->setElementType( domVisual_scene::domEvaluate_scene::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 3, 0, -1 ); + mea->setName( "extra" ); + mea->setOffset( daeOffsetOf(domVisual_scene,elemExtra_array) ); + mea->setElementType( domExtra::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 3 ); + meta->setCMRoot( cm ); + + // Add attribute: id + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "id" ); + ma->setType( dae.getAtomicTypes().get("xsID")); + ma->setOffset( daeOffsetOf( domVisual_scene , attrId )); + ma->setContainer( meta ); + ma->setIsRequired( false ); + + meta->appendAttribute(ma); + } + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domVisual_scene , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domVisual_scene)); + meta->validate(); + + return meta; +} + +daeElementRef +domVisual_scene::domEvaluate_scene::create(DAE& dae) +{ + domVisual_scene::domEvaluate_sceneRef ref = new domVisual_scene::domEvaluate_scene(dae); + return ref; +} + + +daeMetaElement * +domVisual_scene::domEvaluate_scene::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "evaluate_scene" ); + meta->registerClass(domVisual_scene::domEvaluate_scene::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 1, -1 ); + mea->setName( "render" ); + mea->setOffset( daeOffsetOf(domVisual_scene::domEvaluate_scene,elemRender_array) ); + mea->setElementType( domVisual_scene::domEvaluate_scene::domRender::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 0 ); + meta->setCMRoot( cm ); + + // Add attribute: name + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "name" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domVisual_scene::domEvaluate_scene , attrName )); + ma->setContainer( meta ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domVisual_scene::domEvaluate_scene)); + meta->validate(); + + return meta; +} + +daeElementRef +domVisual_scene::domEvaluate_scene::domRender::create(DAE& dae) +{ + domVisual_scene::domEvaluate_scene::domRenderRef ref = new domVisual_scene::domEvaluate_scene::domRender(dae); + return ref; +} + + +daeMetaElement * +domVisual_scene::domEvaluate_scene::domRender::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "render" ); + meta->registerClass(domVisual_scene::domEvaluate_scene::domRender::create); + + meta->setIsInnerClass( true ); + daeMetaCMPolicy *cm = NULL; + daeMetaElementAttribute *mea = NULL; + cm = new daeMetaSequence( meta, cm, 0, 1, 1 ); + + mea = new daeMetaElementArrayAttribute( meta, cm, 0, 0, -1 ); + mea->setName( "layer" ); + mea->setOffset( daeOffsetOf(domVisual_scene::domEvaluate_scene::domRender,elemLayer_array) ); + mea->setElementType( domVisual_scene::domEvaluate_scene::domRender::domLayer::registerElement(dae) ); + cm->appendChild( mea ); + + mea = new daeMetaElementAttribute( meta, cm, 1, 0, 1 ); + mea->setName( "instance_effect" ); + mea->setOffset( daeOffsetOf(domVisual_scene::domEvaluate_scene::domRender,elemInstance_effect) ); + mea->setElementType( domInstance_effect::registerElement(dae) ); + cm->appendChild( mea ); + + cm->setMaxOrdinal( 1 ); + meta->setCMRoot( cm ); + + // Add attribute: camera_node + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "camera_node" ); + ma->setType( dae.getAtomicTypes().get("xsAnyURI")); + ma->setOffset( daeOffsetOf( domVisual_scene::domEvaluate_scene::domRender , attrCamera_node )); + ma->setContainer( meta ); + ma->setIsRequired( true ); + + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domVisual_scene::domEvaluate_scene::domRender)); + meta->validate(); + + return meta; +} + +daeElementRef +domVisual_scene::domEvaluate_scene::domRender::domLayer::create(DAE& dae) +{ + domVisual_scene::domEvaluate_scene::domRender::domLayerRef ref = new domVisual_scene::domEvaluate_scene::domRender::domLayer(dae); + return ref; +} + + +daeMetaElement * +domVisual_scene::domEvaluate_scene::domRender::domLayer::registerElement(DAE& dae) +{ + daeMetaElement* meta = dae.getMeta(ID()); + if ( meta != NULL ) return meta; + + meta = new daeMetaElement(dae); + dae.setMeta(ID(), *meta); + meta->setName( "layer" ); + meta->registerClass(domVisual_scene::domEvaluate_scene::domRender::domLayer::create); + + meta->setIsInnerClass( true ); + // Add attribute: _value + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsNCName")); + ma->setOffset( daeOffsetOf( domVisual_scene::domEvaluate_scene::domRender::domLayer , _value )); + ma->setContainer( meta ); + meta->appendAttribute(ma); + } + + meta->setElementSize(sizeof(domVisual_scene::domEvaluate_scene::domRender::domLayer)); + meta->validate(); + + return meta; +} + diff --git a/Engine/lib/collada/src/dae/dae.cpp b/Engine/lib/collada/src/dae/dae.cpp new file mode 100644 index 000000000..68d819a39 --- /dev/null +++ b/Engine/lib/collada/src/dae/dae.cpp @@ -0,0 +1,362 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DOM_INCLUDE_LIBXML +#include +#endif + +#ifdef DOM_INCLUDE_TINYXML +#include +#endif + +using namespace std; + +// Don't include domConstants.h because it varies depending on the dom version, +// just extern the one thing we need (COLLADA_VERSION) which all versions of +// domConstants.h/.cpp are required to define. + +extern daeString COLLADA_VERSION; + +daeInt DAEInstanceCount = 0; +DAE::charEncoding DAE::globalCharEncoding = DAE::Utf8; + +void +DAE::cleanup() +{ + //Contributed by Nus - Wed, 08 Nov 2006 + daeStringRef::releaseStringTable(); + //---------------------- +} + +void DAE::init(daeDatabase* database_, daeIOPlugin* ioPlugin) { + database = NULL; + plugin = NULL; + defaultDatabase = false; + defaultPlugin = false; + metas.setCount(colladaTypeCount()); + + initializeDomMeta(*this); + DAEInstanceCount++; + + // The order of the URI resolvers is significant, so be careful + uriResolvers.list().append(new daeRawResolver(*this)); + uriResolvers.list().append(new daeStandardURIResolver(*this)); + + idRefResolvers.addResolver(new daeDefaultIDRefResolver(*this)); + + setDatabase(database_); + setIOPlugin(ioPlugin); +} + +DAE::~DAE() +{ + if (defaultDatabase) + delete database; + if (defaultPlugin) + delete plugin; + if ( --DAEInstanceCount <= 0 ) + cleanup(); +} + +// Database setup +daeDatabase* DAE::getDatabase() +{ + return database; +} + +daeInt DAE::setDatabase(daeDatabase* _database) +{ + if (defaultDatabase) + delete database; + if (_database) + { + defaultDatabase = false; + database = _database; + } + else + { + //create default database + database = new daeSTLDatabase(*this); + defaultDatabase = true; + } + database->setMeta(getMeta(domCOLLADA::ID())); + return DAE_OK; +} + +// IO Plugin setup +daeIOPlugin* DAE::getIOPlugin() +{ + return plugin; +} + +daeInt DAE::setIOPlugin(daeIOPlugin* _plugin) +{ + if (defaultPlugin) + delete plugin; + if (_plugin) { + defaultPlugin = false; + plugin = _plugin; + } + else { + plugin = NULL; + defaultPlugin = true; + + //create default plugin +#ifdef DOM_INCLUDE_LIBXML + plugin = new daeLIBXMLPlugin(*this); +#else +#ifdef DOM_INCLUDE_TINYXML + plugin = new daeTinyXMLPlugin; +#endif +#endif + + if (!plugin) { + daeErrorHandler::get()->handleWarning("No IOPlugin Set"); + plugin = new daeIOEmpty; + return DAE_ERROR; + } + } + + int res = plugin->setMeta(getMeta(domCOLLADA::ID())); + if (res != DAE_OK) { + if (defaultPlugin) { + defaultPlugin = false; + delete plugin; + } + plugin = NULL; + } + return res; +} + + +// Take a path (either a URI ref or a file system path) and return a full URI, +// using the current working directory as the base URI if a relative URI +// reference is given. +string DAE::makeFullUri(const string& path) { + daeURI uri(*this, cdom::nativePathToUri(path)); + return uri.str(); +} + + +domCOLLADA* DAE::add(const string& path) { + close(path); + string uri = makeFullUri(path); + database->insertDocument(uri.c_str()); + return getRoot(uri); +} + +domCOLLADA* DAE::openCommon(const string& path, daeString buffer) { + close(path); + string uri = makeFullUri(path); + plugin->setDatabase(database); + if (plugin->read(daeURI(*this, uri.c_str()), buffer) != DAE_OK) + return NULL; + return getRoot(uri); +} + +domCOLLADA* DAE::open(const string& path) { + return openCommon(path, NULL); +} + +domCOLLADA* DAE::openFromMemory(const string& path, daeString buffer) { + return openCommon(path, buffer); +} + +bool DAE::writeCommon(const string& docPath, const string& pathToWriteTo, bool replace) { + string docUri = makeFullUri(docPath), + uriToWriteTo = makeFullUri(pathToWriteTo); + plugin->setDatabase(database); + if (daeDocument* doc = getDoc(docUri)) + return plugin->write(daeURI(*this, uriToWriteTo.c_str()), doc, replace) == DAE_OK; + return false; +} + +bool DAE::write(const string& path) { + return writeCommon(path, path, true); +} + +bool DAE::writeTo(const string& docPath, const string& pathToWriteTo) { + return writeCommon(docPath, pathToWriteTo, true); +} + +bool DAE::writeAll() { + for (int i = 0; i < getDocCount(); i++) + if (save((daeUInt)i, true) != DAE_OK) + return false; + return true; +} + +void DAE::close(const string& path) { + database->removeDocument(getDoc(makeFullUri(path).c_str())); +} + +daeInt DAE::clear() { + database->clear(); + rawRefCache.clear(); + sidRefCache.clear(); + return DAE_OK; +} + + +// Deprecated methods +daeInt DAE::load(daeString uri, daeString docBuffer) { + return openCommon(uri, docBuffer) ? DAE_OK : DAE_ERROR; +} + +daeInt DAE::save(daeString uri, daeBool replace) { + return writeCommon(uri, uri, replace) ? DAE_OK : DAE_ERROR; +} + +daeInt DAE::save(daeUInt documentIndex, daeBool replace) { + if ((int)documentIndex >= getDocCount()) + return DAE_ERROR; + + // Save it out to the URI it was loaded from + daeString uri = getDoc((int)documentIndex)->getDocumentURI()->getURI(); + return writeCommon(uri, uri, replace) ? DAE_OK : DAE_ERROR; +} + +daeInt DAE::saveAs(daeString uriToSaveTo, daeString docUri, daeBool replace) { + return writeCommon(docUri, uriToSaveTo, replace) ? DAE_OK : DAE_ERROR; +} + +daeInt DAE::saveAs(daeString uriToSaveTo, daeUInt documentIndex, daeBool replace) { + if ((int)documentIndex >= getDocCount()) + return DAE_ERROR; + + daeString docUri = getDoc((int)documentIndex)->getDocumentURI()->getURI(); + return writeCommon(docUri, uriToSaveTo, replace); +} + +daeInt DAE::unload(daeString uri) { + close(uri); + return DAE_OK; +} + + +int DAE::getDocCount() { + return (int)database->getDocumentCount(); +} + +daeDocument* DAE::getDoc(int i) { + return database->getDocument(i); +} + +daeDocument* DAE::getDoc(const string& path) { + return database->getDocument(makeFullUri(path).c_str(), true); +} + +domCOLLADA* DAE::getRoot(const string& path) { + if (daeDocument* doc = getDoc(path)) + return (domCOLLADA*)doc->getDomRoot(); + return NULL; +} + +bool DAE::setRoot(const string& path, domCOLLADA* root) { + if (daeDocument* doc = getDoc(path)) + doc->setDomRoot(root); + else + database->insertDocument(makeFullUri(path).c_str(), root); + return getRoot(path) != NULL; +} + +domCOLLADA* DAE::getDom(daeString uri) { + return getRoot(uri); +} + +daeInt DAE::setDom(daeString uri, domCOLLADA* dom) { + return setRoot(uri, dom); +} + +daeString DAE::getDomVersion() +{ + return(COLLADA_VERSION); +} + +daeAtomicTypeList& DAE::getAtomicTypes() { + return atomicTypes; +} + +daeMetaElement* DAE::getMeta(daeInt typeID) { + if (typeID < 0 || typeID >= daeInt(metas.getCount())) + return NULL; + return metas[typeID]; +} + +daeMetaElementRefArray& DAE::getAllMetas() { + return metas; +} + +void DAE::setMeta(daeInt typeID, daeMetaElement& meta) { + if (typeID < 0 || typeID >= daeInt(metas.getCount())) + return; + metas[typeID] = &meta; +} + +daeURIResolverList& DAE::getURIResolvers() { + return uriResolvers; +} + +daeURI& DAE::getBaseURI() { + return baseUri; +} + +void DAE::setBaseURI(const daeURI& uri) { + baseUri = uri; +} + +void DAE::setBaseURI(const string& uri) { + baseUri = uri.c_str(); +} + +daeIDRefResolverList& DAE::getIDRefResolvers() { + return idRefResolvers; +} + +daeRawRefCache& DAE::getRawRefCache() { + return rawRefCache; +} + +daeSidRefCache& DAE::getSidRefCache() { + return sidRefCache; +} + +void DAE::dummyFunction1() { } + +DAE::charEncoding DAE::getGlobalCharEncoding() { + return globalCharEncoding; +} + +void DAE::setGlobalCharEncoding(charEncoding encoding) { + globalCharEncoding = encoding; +} + +DAE::charEncoding DAE::getCharEncoding() { + return localCharEncoding.get() ? *localCharEncoding : getGlobalCharEncoding(); +} + +void DAE::setCharEncoding(charEncoding encoding) { + localCharEncoding.reset(new charEncoding(encoding)); +} diff --git a/Engine/lib/collada/src/dae/daeArray.cpp b/Engine/lib/collada/src/dae/daeArray.cpp new file mode 100644 index 000000000..2e7bffae8 --- /dev/null +++ b/Engine/lib/collada/src/dae/daeArray.cpp @@ -0,0 +1,28 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include + +daeArray::daeArray():_count(0),_capacity(0),_data(NULL),_elementSize(4),_type(NULL) +{ +} + +daeArray::~daeArray() +{ +} + +void daeArray::setElementSize(size_t elementSize) { + clear(); + _elementSize = elementSize; +} diff --git a/Engine/lib/collada/src/dae/daeAtomicType.cpp b/Engine/lib/collada/src/dae/daeAtomicType.cpp new file mode 100644 index 000000000..a603326af --- /dev/null +++ b/Engine/lib/collada/src/dae/daeAtomicType.cpp @@ -0,0 +1,943 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + // Skip leading whitespace + daeChar* skipWhitespace(daeChar* s) { + if (s) { + // !!!GAC NEEDS TO BE CHANGED to use XML standard whitespace parsing + while(*s == ' ' || *s == '\r' || *s == '\n' || *s == '\t') s++; + } + + return s; + } + + // Move forward past this token + daeChar* skipToken(daeChar* s) { + while(*s != ' ' && *s != '\r' && *s != '\n' && *s != '\t' && *s != 0) s++; + return s; + } + + // Given a string of whitespace-separated tokens, this function returns a null-terminated string + // containing the next token. If the next token is already null-terminated, no memory is allocated + // and the function returns the pointer that was passed in. Note that this function assumes that + // the string passed in starts with the next token and not a whitespace. + // If returnValue != s, the client should free the returnValue with delete[]. + daeChar* extractToken(daeChar* s) { + if (!s) + return 0; + + daeChar* tmp = skipToken(s); + if (*tmp != 0) { + daeChar* scopy = new daeChar[tmp-s+1]; + strncpy(scopy, s, tmp-s); + scopy[tmp-s] = 0; + return scopy; + } + + return s; + } +} + + +daeAtomicTypeList::daeAtomicTypeList(DAE& dae) { + types.append(new daeUIntType(dae)); + types.append(new daeIntType(dae)); + types.append(new daeLongType(dae)); + types.append(new daeShortType(dae)); + types.append(new daeULongType(dae)); + types.append(new daeFloatType(dae)); + types.append(new daeDoubleType(dae)); + types.append(new daeStringRefType(dae)); + types.append(new daeElementRefType(dae)); + types.append(new daeEnumType(dae)); + types.append(new daeRawRefType(dae)); + types.append(new daeResolverType(dae)); + types.append(new daeIDResolverType(dae)); + types.append(new daeBoolType(dae)); + types.append(new daeTokenType(dae)); +} + +daeAtomicTypeList::~daeAtomicTypeList() { + for (size_t i = 0; i < types.getCount(); i++) + delete types[i]; +} + +daeInt daeAtomicTypeList::append(daeAtomicType* t) { + return (daeInt)types.append(t); +} + +const daeAtomicType* daeAtomicTypeList::getByIndex(daeInt index) { + return types[index]; +} + +daeInt daeAtomicTypeList::getCount() { + return (daeInt)types.getCount(); +} + +daeAtomicType* daeAtomicTypeList::get(daeStringRef typeString) { + for (size_t i = 0; i < types.getCount(); i++) { + daeStringRefArray& nameBindings = types[i]->getNameBindings(); + for (size_t j = 0; j < nameBindings.getCount(); j++) { + if (strcmp(typeString, nameBindings[j]) == 0) + return types[i]; + } + } + + return NULL; +} + +daeAtomicType* daeAtomicTypeList::get(daeEnum typeEnum) { + for (size_t i = 0; i < types.getCount(); i++) + if (typeEnum == types[i]->getTypeEnum()) + return types[i]; + return NULL; +} + + +daeAtomicType::daeAtomicType(DAE& dae) +{ + _dae = &dae; + _size = -1; + _alignment = -1; + _typeEnum = -1; + _typeString = "notype"; + _printFormat = "badtype"; + _scanFormat = ""; + _maxStringLength = -1; +} + +daeBool +daeAtomicType::stringToMemory(daeChar *src, daeChar* dstMemory) +{ + sscanf(src, _scanFormat, dstMemory); + return true; +} + +void daeAtomicType::arrayToString(daeArray& array, std::ostringstream& buffer) { + if (array.getCount() > 0) + memoryToString(array.getRaw(0), buffer); + for (size_t i = 1; i < array.getCount(); i++) { + buffer << ' '; + memoryToString(array.getRaw(i), buffer); + } +} + +daeBool +daeAtomicType::stringToArray(daeChar* src, daeArray& array) { + array.clear(); + array.setElementSize(_size); + + if (src == 0) + return false; + + // We're about to insert null terminators into the string so that scanf doesn't take forever + // doing strlens. Since the memory might not be writable, I need to duplicate the string and + // write into the duplicate, or else I might get access violations. + // This sucks... surely we can do better than this. + daeChar* srcDup = new daeChar[strlen(src)+1]; + strcpy(srcDup, src); + src = srcDup; + + while (*src != 0) + { + src = skipWhitespace(src); + if(*src != 0) + { + daeChar* token = src; + src = skipToken(src); + daeChar temp = *src; + *src = 0; + + array.setCount(array.getCount()+1); + if (!stringToMemory(token, array.getRaw(array.getCount()-1))) { + delete[] srcDup; + return false; + } + + *src = temp; + } + } + + delete[] srcDup; + return true; +} + +daeInt daeAtomicType::compareArray(daeArray& value1, daeArray& value2) { + if (value1.getCount() != value2.getCount()) + return value1.getCount() > value2.getCount() ? 1 : -1; + + for (size_t i = 0; i < value1.getCount(); i++) { + daeInt result = compare(value1.getRaw(i), value2.getRaw(i)); + if (result != 0) + return result; + } + + return 0; +} + +void daeAtomicType::copyArray(daeArray& src, daeArray& dst) { + dst.setCount(src.getCount()); + for (size_t i = 0; i < src.getCount(); i++) + copy(src.getRaw(i), dst.getRaw(i)); +} + +daeInt +daeAtomicType::compare(daeChar* value1, daeChar* value2) { + return memcmp(value1, value2, _size); +} + +daeEnumType::daeEnumType(DAE& dae) : daeAtomicType(dae) +{ + _size = sizeof(daeEnum); + _alignment = sizeof(daeEnum); + _typeEnum = EnumType; + _nameBindings.append("enum"); + _printFormat = "%s";//"%%.%ds"; + _scanFormat = "%s"; + _strings = NULL; + _values = NULL; + _typeString = "enum"; +} + +daeEnumType::~daeEnumType() { + if ( _strings ) { + delete _strings; + _strings = NULL; + } + if ( _values ) { + delete _values; + _values = NULL; + } +} + +daeBoolType::daeBoolType(DAE& dae) : daeAtomicType(dae) +{ + _size = sizeof(daeBool); + _alignment = sizeof(daeBool); + _typeEnum = BoolType; + _printFormat = "%d"; + _scanFormat = "%d"; + _typeString = "bool"; + _maxStringLength = (daeInt)strlen("false")+1; + _nameBindings.append("bool"); + //_nameBindings.append("xsBool"); + _nameBindings.append("xsBoolean"); +} + +daeIntType::daeIntType(DAE& dae) : daeAtomicType(dae) +{ + _size = sizeof(daeInt); + _alignment = sizeof(daeInt); + _typeEnum = IntType; + _maxStringLength = 16; + _nameBindings.append("int"); + _nameBindings.append("xsInteger"); + _nameBindings.append("xsHexBinary"); + _nameBindings.append("xsIntegerArray"); + _nameBindings.append("xsHexBinaryArray"); + _nameBindings.append("xsByte"); + _nameBindings.append("xsInt"); + _printFormat = "%d"; + _scanFormat = "%d"; + _typeString = "int"; +} +daeLongType::daeLongType(DAE& dae) : daeAtomicType(dae) +{ + _size = sizeof(daeLong); + _alignment = sizeof(daeLong); + _typeEnum = LongType; + _maxStringLength = 32; + _nameBindings.append("xsLong"); + _nameBindings.append("xsLongArray"); +#if defined(_MSC_VER) || defined(__MINGW32__) + _printFormat = "%I64d"; + _scanFormat = "%I64d"; +#else + _printFormat = "%lld"; + _scanFormat = "%lld"; +#endif + _typeString = "long"; +} +daeShortType::daeShortType(DAE& dae) : daeAtomicType(dae) +{ + _maxStringLength = 8; + _size = sizeof(daeShort); + _alignment = sizeof(daeShort); + _typeEnum = ShortType; + _nameBindings.append("short"); + _nameBindings.append("xsShort"); + _printFormat = "%hd"; + _scanFormat = "%hd"; + _typeString = "short"; +} +daeUIntType::daeUIntType(DAE& dae) : daeAtomicType(dae) +{ + _maxStringLength = 16; + _size = sizeof(daeUInt); + _alignment = sizeof(daeUInt); + _typeEnum = UIntType; + _nameBindings.append("uint"); + _nameBindings.append("xsNonNegativeInteger"); + _nameBindings.append("xsUnsignedByte"); + _nameBindings.append("xsUnsignedInt"); + _nameBindings.append("xsPositiveInteger"); + _printFormat = "%u"; + _scanFormat = "%u"; + _typeString = "uint"; +} +daeULongType::daeULongType(DAE& dae) : daeAtomicType(dae) +{ + _size = sizeof(daeULong); + _alignment = sizeof(daeULong); + _typeEnum = ULongType; + _maxStringLength = 32; + _nameBindings.append("ulong"); + _nameBindings.append("xsUnsignedLong"); +#if defined(_MSC_VER) || defined(__MINGW32__) + _printFormat = "%I64u"; + _scanFormat = "%I64u"; +#else + _printFormat = "%llu"; + _scanFormat = "%llu"; +#endif + _typeString = "ulong"; +} +daeFloatType::daeFloatType(DAE& dae) : daeAtomicType(dae) +{ + _maxStringLength = 64; + _size = sizeof(daeFloat); + _alignment = sizeof(daeFloat); + _typeEnum = FloatType; + _nameBindings.append("float"); + _nameBindings.append("xsFloat"); + _printFormat = "%g"; + _scanFormat = "%g"; + _typeString = "float"; +} +daeDoubleType::daeDoubleType(DAE& dae) : daeAtomicType(dae) +{ + _size = sizeof(daeDouble); + _alignment = sizeof(daeDouble); + _typeEnum = DoubleType; + _nameBindings.append("double"); + _nameBindings.append("xsDouble"); + _nameBindings.append("xsDecimal"); + _printFormat = "%lg"; + _scanFormat = "%lg"; + _typeString = "double"; + _maxStringLength = 64; +} + +daeStringRefType::daeStringRefType(DAE& dae) : daeAtomicType(dae) +{ + _size = sizeof(daeStringRef); + _alignment = sizeof(daeStringRef); + _typeEnum = StringRefType; + _nameBindings.append("string"); + _nameBindings.append("xsString"); + _nameBindings.append("xsDateTime"); + _printFormat = "%s"; + _scanFormat = "%s"; + _typeString = "string"; +} + +daeTokenType::daeTokenType(DAE& dae) : daeStringRefType(dae) +{ + _size = sizeof(daeStringRef); + _alignment = sizeof(daeStringRef); + _typeEnum = TokenType; + _nameBindings.append("token"); + _nameBindings.append("xsID"); + _nameBindings.append("xsNCName"); + _nameBindings.append("xsNMTOKEN"); + _nameBindings.append("xsName"); + _nameBindings.append("xsToken"); + _nameBindings.append("xsNameArray"); + _nameBindings.append("xsTokenArray"); + _nameBindings.append("xsNCNameArray"); + _printFormat = "%s"; + _scanFormat = "%s"; + _typeString = "token"; +} + +daeElementRefType::daeElementRefType(DAE& dae) : daeAtomicType(dae) +{ + _size = sizeof(daeElementRef); + _alignment = sizeof(daeElementRef); + _typeEnum = ElementRefType; + _nameBindings.append("element"); + _nameBindings.append("Element"); + _nameBindings.append("TrackedElement"); + _printFormat = "%p"; + _scanFormat = "%p"; + _typeString = "element"; + _maxStringLength = 64; +} + +daeRawRefType::daeRawRefType(DAE& dae) : daeAtomicType(dae) +{ + _size = sizeof(daeRawRef); + _alignment = sizeof(daeRawRef); + _typeEnum = RawRefType; + _nameBindings.append("raw"); + _printFormat = "%p"; + _scanFormat = "%p"; + _typeString = "raw"; + _maxStringLength = 64; +} + +daeResolverType::daeResolverType(DAE& dae) : daeAtomicType(dae) +{ + _size = sizeof(daeURI); + _alignment = sizeof(daeURI); + _typeEnum = ResolverType; + _nameBindings.append("resolver"); + _nameBindings.append("xsAnyURI"); + _printFormat = "%s"; + _scanFormat = "%s"; + _typeString = "resolver"; +} +daeIDResolverType::daeIDResolverType(DAE& dae) : daeAtomicType(dae) +{ + _size = sizeof(daeIDRef); + _alignment = sizeof(daeIDRef); + _typeEnum = IDResolverType; + _nameBindings.append("xsIDREF"); + _nameBindings.append("xsIDREFS"); + _printFormat = "%s"; + _scanFormat = "%s"; + _typeString = "idref_resolver"; +} + +daeBool daeIntType::memoryToString(daeChar* src, std::ostringstream& dst) { + dst << *(daeInt*)src; + return true; +} + +daeBool daeLongType::memoryToString(daeChar* src, std::ostringstream& dst) { + dst << *(daeLong*)src; + return true; +} + +daeBool daeShortType::memoryToString(daeChar* src, std::ostringstream& dst) { + dst << *(daeShort*)src; + return true; +} + +daeBool daeUIntType::memoryToString(daeChar* src, std::ostringstream& dst) { + dst << *(daeUInt*)src; + return true; +} + +daeBool daeULongType::memoryToString(daeChar* src, std::ostringstream& dst) { +#ifdef _MSC_VER + // Microsoft's stringstream implementation has weird performance issues + static char buffer[64]; + _snprintf(buffer, 64, _printFormat, *((daeULong*)src)); + dst << buffer; +#else + dst << *(daeULong*)src; +#endif + return true; +} + +daeBool daeFloatType::memoryToString(daeChar* src, std::ostringstream& dst) { + if ( *(daeFloat*)src != *(daeFloat*)src ) // NAN + dst << "NaN"; + else if ( *(daeUInt*)src == 0x7f800000 ) // +INF + dst << "INF"; + else if ( *(daeUInt*)src == 0xff800000 ) // -INF + dst << "-INF"; + else + dst << *(daeFloat*)src; + return true; +} + +daeBool +daeFloatType::stringToMemory(daeChar *src, daeChar* dstMemory) +{ + src = skipWhitespace(src); + + if ( strncmp(src, "NaN", 3) == 0 ) { + daeErrorHandler::get()->handleWarning("NaN encountered while setting an attribute or value\n"); + *(daeInt*)(dstMemory) = 0x7f800002; + } + else if ( strncmp(src, "INF", 3) == 0 ) { + daeErrorHandler::get()->handleWarning( "INF encountered while setting an attribute or value\n" ); + *(daeInt*)(dstMemory) = 0x7f800000; + } + else if ( strncmp(src, "-INF", 4) == 0 ) { + daeErrorHandler::get()->handleWarning( "-INF encountered while setting an attribute or value\n" ); + *(daeInt*)(dstMemory) = 0xff800000; + } + else + { + sscanf(src, _scanFormat, dstMemory); + } + return true; +} + +daeBool daeDoubleType::memoryToString(daeChar* src, std::ostringstream& dst) { + if ( *(daeDouble*)src != *(daeDouble*)src ) // NAN + dst << "NaN"; + else if ( *(daeULong*)src == 0x7ff0000000000000LL ) // +INF + dst << "INF"; + else if ( *(daeULong*)src == 0xfff0000000000000LL ) // -INF + dst << "-INF"; + else { +#ifdef _MSC_VER + // Microsoft's stringstream implementation has weird performance issues + static char buffer[64]; + _snprintf(buffer, 64, _printFormat, *((daeDouble*)src)); + dst << buffer; +#else + dst << *(daeDouble*)src; +#endif + } + return true; +} + +daeBool +daeDoubleType::stringToMemory(daeChar *src, daeChar* dstMemory) +{ + src = skipWhitespace(src); + + if ( strncmp(src, "NaN", 3) == 0 ) { + daeErrorHandler::get()->handleWarning( "NaN encountered while setting an attribute or value\n" ); + *(daeLong*)(dstMemory) = 0x7ff0000000000002LL; + } + else if ( strncmp(src, "INF", 3) == 0 ) { + daeErrorHandler::get()->handleWarning( "INF encountered while setting an attribute or value\n" ); + *(daeLong*)(dstMemory) = 0x7ff0000000000000LL; + } + else if ( strncmp(src, "-INF", 4) == 0 ) { + daeErrorHandler::get()->handleWarning( "-INF encountered while setting an attribute or value\n" ); + *(daeLong*)(dstMemory) = 0xfff0000000000000LL; + } + else + { + sscanf(src, _scanFormat, dstMemory); + } + return true; +} + +daeBool daeRawRefType::memoryToString(daeChar* src, std::ostringstream& dst) { + dst << (void *)(*((daeRawRef*)src)); + return true; +} + +daeBool daeStringRefType::memoryToString(daeChar* src, std::ostringstream& dst) { + daeString s = *((daeStringRef *)src); + if (s) + dst << s; + return true; +} + +daeBool daeResolverType::memoryToString(daeChar* src, std::ostringstream& dst) { + // Get the URI we are trying to write + daeURI *thisURI = ((daeURI *)src); + // Encode spaces with %20 + dst << cdom::replace(thisURI->originalStr(), " ", "%20"); + return true; +} + +daeBool daeIDResolverType::memoryToString(daeChar* src, std::ostringstream& dst) { + dst << ((daeIDRef *)src)->getID(); + return true; +} + +daeBool +daeResolverType::stringToMemory(daeChar* src, daeChar* dstMemory) +{ + ((daeURI*)dstMemory)->set(cdom::replace(src, " ", "%20")); + return true; +} + +daeBool +daeIDResolverType::stringToMemory(daeChar* src, daeChar* dstMemory) +{ + src = skipWhitespace(src); + daeChar* id = extractToken(src); + ((daeIDRef*)dstMemory)->setID(id); + if (id != src) + delete[] id; + return true; +} + +daeBool +daeStringRefType::stringToMemory(daeChar* srcChars, daeChar* dstMemory) +{ + *((daeStringRef*)dstMemory) = srcChars; + return true; +} + +daeBool +daeTokenType::stringToMemory(daeChar* src, daeChar* dst) +{ + src = skipWhitespace(src); + daeChar* srcTmp = extractToken(src); + *((daeStringRef*)dst) = srcTmp; + if (srcTmp != src) + delete[] srcTmp; + return true; +} + +daeBool +daeEnumType::stringToMemory(daeChar* src, daeChar* dst ) +{ + src = skipWhitespace(src); + daeChar* srcTmp = extractToken(src); + + size_t index(0); + bool result = _strings->find(srcTmp, index) != DAE_ERR_QUERY_NO_MATCH; + if (result) { + daeEnum val = _values->get( index ); + *((daeEnum*)dst) = val; + } + + if (srcTmp != src) + delete[] srcTmp; + + return result; +} + +daeBool daeEnumType::memoryToString(daeChar* src, std::ostringstream& dst) { + daeStringRef s = "unknown"; + if (_strings != NULL) { + size_t index; + if (_values->find(*((daeEnum*)src), index) == DAE_OK) + s = _strings->get(index); + } + dst << (const char*)s; + return true; +} + +daeBool +daeBoolType::stringToMemory(daeChar* srcChars, daeChar* dstMemory) +{ + if (strncmp(srcChars,"true",4)==0 || strncmp(srcChars,"1",1)==0) + *((daeBool*)dstMemory) = true; + else + *((daeBool*)dstMemory) = false; + return true; +} + +daeBool daeBoolType::memoryToString(daeChar* src, std::ostringstream& dst) { + if (*((daeBool*)src)) + dst << "true"; + else + dst << "false"; + return true; +} +//!!!ACL added for 1.4 complex types and groups + +// Unimplemented +daeBool daeElementRefType::memoryToString(daeChar* src, std::ostringstream& dst) { + (void)src; + (void)dst; + return false; +} + +daeMemoryRef daeBoolType::create() { + return (daeMemoryRef)new daeBool; +} + +daeMemoryRef daeIntType::create() { + return (daeMemoryRef)new daeInt; +} + +daeMemoryRef daeLongType::create() { + return (daeMemoryRef)new daeLong; +} + +daeMemoryRef daeUIntType::create() { + return (daeMemoryRef)new daeUInt; +} + +daeMemoryRef daeULongType::create() { + return (daeMemoryRef)new daeULong; +} + +daeMemoryRef daeShortType::create() { + return (daeMemoryRef)new daeShort; +} + +daeMemoryRef daeFloatType::create() { + return (daeMemoryRef)new daeFloat; +} + +daeMemoryRef daeDoubleType::create() { + return (daeMemoryRef)new daeDouble; +} + +daeMemoryRef daeStringRefType::create() { + return (daeMemoryRef)new daeStringRef; +} + +daeMemoryRef daeTokenType::create() { + return (daeMemoryRef)new daeStringRef; +} + +daeMemoryRef daeElementRefType::create() { + return (daeMemoryRef)new daeElementRef; +} + +daeMemoryRef daeEnumType::create() { + return (daeMemoryRef)new daeEnum; +} + +daeMemoryRef daeRawRefType::create() { + return (daeMemoryRef)new daeRawRef; +} + +daeMemoryRef daeResolverType::create() { + return (daeMemoryRef)new daeURI(*_dae); +} + +daeMemoryRef daeIDResolverType::create() { + return (daeMemoryRef)new daeIDRef; +} + + +void daeBoolType::destroy(daeMemoryRef obj) { + delete (daeBool*)obj; +} + +void daeIntType::destroy(daeMemoryRef obj) { + delete (daeInt*)obj; +} + +void daeLongType::destroy(daeMemoryRef obj) { + delete (daeLong*)obj; +} + +void daeUIntType::destroy(daeMemoryRef obj) { + delete (daeUInt*)obj; +} + +void daeULongType::destroy(daeMemoryRef obj) { + delete (daeULong*)obj; +} + +void daeShortType::destroy(daeMemoryRef obj) { + delete (daeShort*)obj; +} + +void daeFloatType::destroy(daeMemoryRef obj) { + delete (daeFloat*)obj; +} + +void daeDoubleType::destroy(daeMemoryRef obj) { + delete (daeDouble*)obj; +} + +void daeStringRefType::destroy(daeMemoryRef obj) { + delete (daeStringRef*)obj; +} + +void daeTokenType::destroy(daeMemoryRef obj) { + delete (daeStringRef*)obj; +} + +void daeElementRefType::destroy(daeMemoryRef obj) { + delete (daeElementRef*)obj; +} + +void daeEnumType::destroy(daeMemoryRef obj) { + delete (daeEnum*)obj; +} + +void daeRawRefType::destroy(daeMemoryRef obj) { + delete (daeRawRef*)obj; +} + +void daeResolverType::destroy(daeMemoryRef obj) { + delete (daeURI*)obj; +} + +void daeIDResolverType::destroy(daeMemoryRef obj) { + delete (daeIDRef*)obj; +} + + +daeInt daeStringRefType::compare(daeChar* value1, daeChar* value2) { + daeString s1 = *((daeStringRef *)value1); + daeString s2 = *((daeStringRef *)value2); + // For string types, the empty string and null are considered equivalent + if (!s1) + s1 = ""; + if (!s2) + s2 = ""; + return strcmp(s1, s2); +} + +daeInt daeResolverType::compare(daeChar* value1, daeChar* value2) { + return strcmp(((daeURI*)value1)->str().c_str(), ((daeURI*)value2)->str().c_str()); +} + +daeInt daeIDResolverType::compare(daeChar* value1, daeChar* value2) { + return (daeIDRef&)*value1 == (daeIDRef&)*value2; +} + + +daeArray* daeBoolType::createArray() { + return new daeTArray; +} + +daeArray* daeIntType::createArray() { + return new daeTArray; +} + +daeArray* daeLongType::createArray() { + return new daeTArray; +} + +daeArray* daeUIntType::createArray() { + return new daeTArray; +} + +daeArray* daeULongType::createArray() { + return new daeTArray; +} + +daeArray* daeShortType::createArray() { + return new daeTArray; +} + +daeArray* daeFloatType::createArray() { + return new daeTArray; +} + +daeArray* daeDoubleType::createArray() { + return new daeTArray; +} + +daeArray* daeStringRefType::createArray() { + return new daeTArray; +} + +daeArray* daeTokenType::createArray() { + return new daeTArray; +} + +daeArray* daeElementRefType::createArray() { + return new daeTArray; +} + +daeArray* daeEnumType::createArray() { + return new daeTArray; +} + +daeArray* daeRawRefType::createArray() { + return new daeTArray; +} + +daeArray* daeResolverType::createArray() { + // !!!steveT + // The daeURI object no longer has a constructor that takes no arguments, so + // it's not compatible with daeTArray. Therefore this method currently can't be used, + // and asserts if you try to use it. The DOM doesn't ever call this code now, + // so the situation is sort of alright, but we might need to fix this in the future. + assert(false); + return NULL; +} + +daeArray* daeIDResolverType::createArray() { + return new daeTArray; +} + + +void daeBoolType::copy(daeChar* src, daeChar* dst) { + (daeBool&)*dst = (daeBool&)*src; +} + +void daeIntType::copy(daeChar* src, daeChar* dst) { + (daeInt&)*dst = (daeInt&)*src; +} + +void daeLongType::copy(daeChar* src, daeChar* dst) { + (daeLong&)*dst = (daeLong&)*src; +} + +void daeUIntType::copy(daeChar* src, daeChar* dst) { + (daeUInt&)*dst = (daeUInt&)*src; +} + +void daeULongType::copy(daeChar* src, daeChar* dst) { + (daeULong&)*dst = (daeULong&)*src; +} + +void daeShortType::copy(daeChar* src, daeChar* dst) { + (daeShort&)*dst = (daeShort&)*src; +} + +void daeFloatType::copy(daeChar* src, daeChar* dst) { + (daeFloat&)*dst = (daeFloat&)*src; +} + +void daeDoubleType::copy(daeChar* src, daeChar* dst) { + (daeDouble&)*dst = (daeDouble&)*src; +} + +void daeStringRefType::copy(daeChar* src, daeChar* dst) { + (daeStringRef&)*dst = (daeStringRef&)*src; +} + +void daeTokenType::copy(daeChar* src, daeChar* dst) { + (daeStringRef&)*dst = (daeStringRef&)*src; +} + +void daeElementRefType::copy(daeChar* src, daeChar* dst) { + (daeElementRef&)*dst = (daeElementRef&)*src; +} + +void daeEnumType::copy(daeChar* src, daeChar* dst) { + (daeEnum&)*dst = (daeEnum&)*src; +} + +void daeRawRefType::copy(daeChar* src, daeChar* dst) { + (daeRawRef&)*dst = (daeRawRef&)*src; +} + +void daeResolverType::copy(daeChar* src, daeChar* dst) { + (daeURI&)*dst = (daeURI&)*src; +} + +void daeIDResolverType::copy(daeChar* src, daeChar* dst) { + (daeIDRef&)*dst = (daeIDRef&)*src; +} + +void daeResolverType::setDocument(daeChar* value, daeDocument* doc) { + daeURI* uri = (daeURI*)value; + uri->setContainer(uri->getContainer()); +} + +void daeResolverType::setDocument(daeArray& array, daeDocument* doc) { + // !!!steveT + // The daeURI object no longer has a constructor that takes no arguments, so + // it's not compatible with daeTArray. Therefore this method currently can't be used, + // and asserts if you try to use it. The DOM doesn't ever call this code now, + // so the situation is sort of alright, but we might need to fix this in the future. + assert(false); +} diff --git a/Engine/lib/collada/src/dae/daeDatabase.cpp b/Engine/lib/collada/src/dae/daeDatabase.cpp new file mode 100644 index 000000000..6217a7185 --- /dev/null +++ b/Engine/lib/collada/src/dae/daeDatabase.cpp @@ -0,0 +1,45 @@ +/* + * Copyright 2007 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include "dae/daeDatabase.h" +using namespace std; + +daeDatabase::daeDatabase(DAE& dae) : dae(dae) { } + +DAE* daeDatabase::getDAE() { + return &dae; +} + +daeDocument* daeDatabase::getDoc(daeUInt index) { + return getDocument(index); +} + +daeElement* daeDatabase::idLookup(const string& id, daeDocument* doc) { + vector elts = idLookup(id); + for (size_t i = 0; i < elts.size(); i++) + if (elts[i]->getDocument() == doc) + return elts[i]; + return NULL; +} + +vector daeDatabase::typeLookup(daeInt typeID, daeDocument* doc) { + vector result; + typeLookup(typeID, result); + return result; +} + +vector daeDatabase::sidLookup(const string& sid, daeDocument* doc) { + vector result; + sidLookup(sid, result, doc); + return result; +} diff --git a/Engine/lib/collada/src/dae/daeDocument.cpp b/Engine/lib/collada/src/dae/daeDocument.cpp new file mode 100644 index 000000000..3d093c4ed --- /dev/null +++ b/Engine/lib/collada/src/dae/daeDocument.cpp @@ -0,0 +1,54 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include + + +daeDocument::daeDocument(DAE& dae) : dae(&dae), uri(dae) { } + +daeDocument::~daeDocument() { +} + +void daeDocument::insertElement( daeElementRef element ) { + dae->getDatabase()->insertElement( this, element.cast() ); +} + +void daeDocument::removeElement( daeElementRef element ) { + dae->getDatabase()->removeElement( this, element.cast() ); +} + +void daeDocument::changeElementID( daeElementRef element, daeString newID ) { + dae->getDatabase()->changeElementID( element.cast(), newID ); +} + +void daeDocument::changeElementSID( daeElementRef element, daeString newSID ) { + dae->getDatabase()->changeElementSID( element.cast(), newSID ); +} + +void daeDocument::addExternalReference( daeURI &uri ) { + if ( uri.getContainer() == NULL || uri.getContainer()->getDocument() != this ) { + return; + } + daeURI tempURI( *dae, uri.getURI(), true ); // Remove fragment + referencedDocuments.appendUnique( tempURI.getURI() ); +} + +DAE* daeDocument::getDAE() { + return dae; +} + +daeDatabase* daeDocument::getDatabase() { + return dae->getDatabase(); +} diff --git a/Engine/lib/collada/src/dae/daeDom.cpp b/Engine/lib/collada/src/dae/daeDom.cpp new file mode 100644 index 000000000..d89ed63be --- /dev/null +++ b/Engine/lib/collada/src/dae/daeDom.cpp @@ -0,0 +1,22 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ +#include +#include +#include +#include + +daeMetaElement* initializeDomMeta(DAE& dae) +{ + registerDomTypes(dae); + return registerDomElements(dae); +} diff --git a/Engine/lib/collada/src/dae/daeElement.cpp b/Engine/lib/collada/src/dae/daeElement.cpp new file mode 100644 index 000000000..a2c127c3d --- /dev/null +++ b/Engine/lib/collada/src/dae/daeElement.cpp @@ -0,0 +1,758 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +daeElement* daeElement::simpleAdd(daeString name, int index) { + if (daeElementRef elt = _meta->create(name)) + return add(elt, index); + return NULL; +} + +daeElement* daeElement::add(daeString names_, int index) { + list names; + cdom::tokenize(names_, " ", names); + cdom::tokenIter iter = names.begin(); + daeElement* root = simpleAdd(iter->c_str(), index); + if (!root) + return NULL; + + iter++; + daeElement* elt = root; + for (; iter != names.end(); iter++) { + elt = elt->simpleAdd(iter->c_str()); + if (!elt) { + removeChildElement(root); + return NULL; + } + } + + return elt; +} + +daeElement* daeElement::add(daeElement* elt, int index) { + if (!elt) + return NULL; + if (elt == this) + return this; + bool result = (index == -1 ? _meta->place(this, elt) : _meta->placeAt(index, this, elt)); + return result ? elt : NULL; +} + +daeElement* daeElement::addBefore(daeElement* elt, daeElement* index) { + if (!index || !elt || index->getParent() != this) + return NULL; + return _meta->placeBefore(index, this, elt) ? elt : NULL; +} + +daeElement* daeElement::addAfter(daeElement* elt, daeElement* index) { + if (!index || !elt || index->getParent() != this) + return NULL; + return _meta->placeAfter(index, this, elt) ? elt : NULL; +} + +daeElementRef +daeElement::createElement(daeString className) +{ + daeElementRef elem = _meta->create(className); + // Bug #225 work around +// if ( elem != NULL) +// elem->ref(); // change premature delete into memory leak. + return elem; +} + +daeElement* daeElement::createAndPlace(daeString className) { + return add(className); +} + +daeElement* daeElement::createAndPlaceAt(daeInt index, daeString className) { + return add(className, index); +} + +daeBool daeElement::placeElement(daeElement* e) { + return add(e) != NULL; +} + +daeBool daeElement::placeElementAt(daeInt index, daeElement* e) { + return add(e, index) != NULL; +} + +daeBool daeElement::placeElementBefore( daeElement *marker, daeElement *element ) { + return addBefore(element, marker) != NULL; +} + +daeBool daeElement::placeElementAfter( daeElement *marker, daeElement *element ) { + return addAfter(element, marker) != NULL; +} + +daeInt daeElement::findLastIndexOf( daeString elementName ) { + if ( _meta->getContents() != NULL ) { + daeElementRefArray* contents = + (daeElementRefArray*)_meta->getContents()->getWritableMemory(this); + for ( int i = (int)contents->getCount()-1; i >= 0; --i ) { + if ( strcmp( contents->get(i)->getElementName(), elementName ) == 0 ) { + return i; + } + } + } + return -1; +} + +daeBool +daeElement::removeChildElement(daeElement* element) +{ + // error traps + if(element==NULL) + return false; + if(element->_parent != this) + return false; + + return _meta->remove( this, element ); +} + +void daeElement::setDocument( daeDocument *c, bool notifyDocument ) { + if( _document == c ) + return; + + // Notify our parent document if necessary. + if ( _document != NULL && notifyDocument ) + _document->removeElement(this); + _document = c; + if ( _document != NULL && notifyDocument ) + _document->insertElement(this); + + // Notify our attributes + daeMetaAttributeRefArray& metaAttrs = getMeta()->getMetaAttributes(); + for (size_t i = 0; i < metaAttrs.getCount(); i++) + metaAttrs[i]->setDocument(this, c); + + // Notify our char data object + if (getCharDataObject()) + getCharDataObject()->setDocument(this, c); + + // Notify our children + daeElementRefArray ea; + getChildren( ea ); + for ( size_t x = 0; x < ea.getCount(); x++ ) { + // Since inserting and removing elements works recursively in the database, + // we don't need to notify it about inserts/removals as we process the + // children of this element. + ea[x]->setDocument( c, false ); + } +} + +void daeElement::deleteCMDataArray(daeTArray& cmData) { + for (unsigned int i = 0; i < cmData.getCount(); i++) + delete cmData.get(i); + cmData.clear(); +} + +size_t daeElement::getAttributeCount() { + return getMeta()->getMetaAttributes().getCount(); +} + +namespace { + // A helper function to get the index of an attribute given the attribute name. + size_t getAttributeIndex(daeElement& el, daeString name) { + if (el.getMeta()) { + daeMetaAttributeRefArray& metaAttrs = el.getMeta()->getMetaAttributes(); + for (size_t i = 0; i < metaAttrs.getCount(); i++) + if (metaAttrs[i]->getName() && strcmp(metaAttrs[i]->getName(), name) == 0) + return i; + } + return (size_t)-1; + } +} + +daeMetaAttribute* daeElement::getAttributeObject(size_t i) { + daeMetaAttributeRefArray& attrs = getMeta()->getMetaAttributes(); + if (i >= attrs.getCount()) + return NULL; + return attrs[i]; +} + +daeMetaAttribute* daeElement::getAttributeObject(daeString name) { + return getAttributeObject(getAttributeIndex(*this, name)); +} + +std::string daeElement::getAttributeName(size_t i) { + if (daeMetaAttribute* attr = getAttributeObject(i)) + return (daeString)attr->getName(); + return ""; +} + +daeBool daeElement::hasAttribute(daeString name) { + return getAttributeObject(name) != 0; +} + +daeBool daeElement::isAttributeSet(daeString name) { + size_t i = getAttributeIndex(*this, name); + if (i != (size_t)-1) + return _validAttributeArray[i]; + return false; +} + +std::string daeElement::getAttribute(size_t i) { + std::string value; + getAttribute(i, value); + return value; +} + +void daeElement::getAttribute(size_t i, std::string& value) { + value = ""; + if (daeMetaAttribute* attr = getAttributeObject(i)) { + std::ostringstream buffer; + attr->memoryToString(this, buffer); + value = buffer.str(); + } +} + +std::string daeElement::getAttribute(daeString name) { + std::string value; + getAttribute(name, value); + return value; +} + +void daeElement::getAttribute(daeString name, std::string& value) { + getAttribute(getAttributeIndex(*this, name), value); +} + +daeElement::attr::attr() { } +daeElement::attr::attr(const std::string& name, const std::string& value) + : name(name), value(value) { } + +daeTArray daeElement::getAttributes() { + daeTArray attrs; + getAttributes(attrs); + return attrs; +} + +void daeElement::getAttributes(daeTArray& attrs) { + attrs.clear(); + for (size_t i = 0; i < getAttributeCount(); i++) { + std::string value; + getAttribute(i, value); + attrs.append(attr(getAttributeName(i), value)); + } +} + +daeBool daeElement::setAttribute(size_t i, daeString value) { + if (daeMetaAttribute* attr = getAttributeObject(i)) { + if (attr->getType()) { + attr->stringToMemory(this, value); + _validAttributeArray.set(i, true); + return true; + } + } + return false; +} + +daeBool daeElement::setAttribute(daeString name, daeString value) { + return setAttribute(getAttributeIndex(*this, name), value); +} + +// Deprecated +daeMemoryRef daeElement::getAttributeValue(daeString name) { + if (daeMetaAttribute* attr = getAttributeObject(name)) + return attr->get(this); + return NULL; +} + +daeMetaAttribute* daeElement::getCharDataObject() { + if (_meta) + return _meta->getValueAttribute(); + return NULL; +} + +daeBool daeElement::hasCharData() { + return getCharDataObject() != NULL; +} + +std::string daeElement::getCharData() { + std::string result; + getCharData(result); + return result; +} + +void daeElement::getCharData(std::string& data) { + data = ""; + if (daeMetaAttribute* charDataAttr = getCharDataObject()) { + std::ostringstream buffer; + charDataAttr->memoryToString(this, buffer); + data = buffer.str(); + } +} + +daeBool daeElement::setCharData(const std::string& data) { + if (daeMetaAttribute* charDataAttr = getCharDataObject()) { + charDataAttr->stringToMemory(this, data.c_str()); + return true; + } + return false; +} + +daeBool daeElement::hasValue() { + return hasCharData(); +} + +daeMemoryRef daeElement::getValuePointer() { + if (daeMetaAttribute* charDataAttr = getCharDataObject()) + return charDataAttr->get(this); + return NULL; +} + +void +daeElement::setup(daeMetaElement* meta) +{ + if (_meta) + return; + _meta = meta; + daeMetaAttributeRefArray& attrs = meta->getMetaAttributes(); + int macnt = (int)attrs.getCount(); + + _validAttributeArray.setCount(macnt, false); + + for (int i = 0; i < macnt; i++) { + if (attrs[i]->getDefaultValue() != NULL) + attrs[i]->copyDefault(this); + } + + //set up the _CMData array if there is one + if ( _meta->getMetaCMData() != NULL ) + { + daeTArray< daeCharArray *> *CMData = (daeTArray< daeCharArray *>*)_meta->getMetaCMData()->getWritableMemory(this); + CMData->setCount( _meta->getNumChoices() ); + for ( unsigned int i = 0; i < _meta->getNumChoices(); i++ ) + { + CMData->set( i, new daeCharArray() ); + } + } +} + +void daeElement::init() { + _parent = NULL; + _document = NULL; + _meta = NULL; + _elementName = NULL; + _userData = NULL; +} + +daeElement::daeElement() { + init(); +} + +daeElement::daeElement(DAE& dae) { + init(); +} + +daeElement::~daeElement() +{ + if (_elementName) { + delete[] _elementName; + _elementName = NULL; + } +} + +//function used until we clarify what's a type and what's a name for an element +daeString daeElement::getTypeName() const +{ + return _meta->getName(); +} +daeString daeElement::getElementName() const +{ + return _elementName ? _elementName : (daeString)_meta->getName(); +} +void daeElement::setElementName( daeString nm ) { + if ( nm == NULL ) { + if ( _elementName ) delete[] _elementName; + _elementName = NULL; + return; + } + if ( !_elementName ) _elementName = new daeChar[128]; + strcpy( (char*)_elementName, nm ); +} + +daeString daeElement::getID() const { + daeElement* this_ = const_cast(this); + if (_meta) + if (daeMetaAttribute* idAttr = this_->getAttributeObject("id")) + return *(daeStringRef*)idAttr->get(this_); + return NULL; +} + +daeElementRefArray daeElement::getChildren() { + daeElementRefArray array; + getChildren(array); + return array; +} + +void daeElement::getChildren( daeElementRefArray &array ) { + _meta->getChildren( this, array ); +} + +daeSmartRef daeElement::clone(daeString idSuffix, daeString nameSuffix) { + // Use the meta object system to create a new instance of this element. We need to + // create a new meta if we're cloning a domAny object because domAnys never share meta objects. + // Ideally we'd be able to clone the _meta for domAny objects. Then we wouldn't need + // any additional special case code for cloning domAny. Unfortunately, we don't have a + // daeMetaElement::clone method. + bool any = typeID() == domAny::ID(); + daeElementRef ret = any ? domAny::registerElement(*getDAE())->create() : _meta->create(); + ret->setElementName( _elementName ); + + // Copy the attributes and character data. Requires special care for domAny. + if (any) { + domAny* thisAny = (domAny*)this; + domAny* retAny = (domAny*)ret.cast(); + for (daeUInt i = 0; i < (daeUInt)thisAny->getAttributeCount(); i++) + retAny->setAttribute(thisAny->getAttributeName(i), thisAny->getAttributeValue(i)); + retAny->setValue(thisAny->getValue()); + } else { + // Use the meta system to copy attributes + daeMetaAttributeRefArray &attrs = _meta->getMetaAttributes(); + for (unsigned int i = 0; i < attrs.getCount(); i++) { + attrs[i]->copy( ret, this ); + ret->_validAttributeArray[i] = _validAttributeArray[i]; + } + if (daeMetaAttribute* valueAttr = getCharDataObject()) + valueAttr->copy( ret, this ); + } + + daeElementRefArray children; + _meta->getChildren( this, children ); + for ( size_t x = 0; x < children.getCount(); x++ ) { + ret->placeElement( children.get(x)->clone( idSuffix, nameSuffix ) ); + } + + // Mangle the id + if (idSuffix) { + std::string id = ret->getAttribute("id"); + if (!id.empty()) + ret->setAttribute("id", (id + idSuffix).c_str()); + } + // Mangle the name + if (nameSuffix) { + std::string name = ret->getAttribute("name"); + if (!name.empty()) + ret->setAttribute("name", (name + nameSuffix).c_str()); + } + return ret; +} + + +// Element comparison + +namespace { // Utility functions + int getNecessaryColumnWidth(const vector& tokens) { + int result = 0; + for (size_t i = 0; i < tokens.size(); i++) { + int tokenLength = int(tokens[i].length() > 0 ? tokens[i].length()+2 : 0); + result = max(tokenLength, result); + } + return result; + } + + string formatToken(const string& token) { + if (token.length() <= 50) + return token; + return token.substr(0, 47) + "..."; + } +} // namespace { + +daeElement::compareResult::compareResult() + : compareValue(0), + elt1(NULL), + elt2(NULL), + nameMismatch(false), + attrMismatch(""), + charDataMismatch(false), + childCountMismatch(false) { +} + +string daeElement::compareResult::format() { + if (!elt1 || !elt2) + return ""; + + // Gather the data we'll be printing + string name1 = formatToken(elt1->getElementName()), + name2 = formatToken(elt2->getElementName()), + type1 = formatToken(elt1->getTypeName()), + type2 = formatToken(elt2->getTypeName()), + id1 = formatToken(elt1->getAttribute("id")), + id2 = formatToken(elt2->getAttribute("id")), + attrName1 = formatToken(attrMismatch), + attrName2 = formatToken(attrMismatch), + attrValue1 = formatToken(elt1->getAttribute(attrMismatch.c_str())), + attrValue2 = formatToken(elt2->getAttribute(attrMismatch.c_str())), + charData1 = formatToken(elt1->getCharData()), + charData2 = formatToken(elt2->getCharData()), + childCount1 = formatToken(cdom::toString(elt1->getChildren().getCount())), + childCount2 = formatToken(cdom::toString(elt2->getChildren().getCount())); + + // Compute formatting information + vector col1Tokens = cdom::makeStringArray("Name", "Type", "ID", + "Attr name", "Attr value", "Char data", "Child count", 0); + vector col2Tokens = cdom::makeStringArray("Element 1", name1.c_str(), + type1.c_str(), id1.c_str(), attrName1.c_str(), attrValue1.c_str(), + charData1.c_str(), childCount1.c_str(), 0); + + int c1w = getNecessaryColumnWidth(col1Tokens), + c2w = getNecessaryColumnWidth(col2Tokens); + ostringstream msg; + msg << setw(c1w) << left << "" << setw(c2w) << left << "Element 1" << "Element 2\n" + << setw(c1w) << left << "" << setw(c2w) << left << "---------" << "---------\n" + << setw(c1w) << left << "Name" << setw(c2w) << left << name1 << name2 << endl + << setw(c1w) << left << "Type" << setw(c2w) << left << type1 << type2 << endl + << setw(c1w) << left << "ID" << setw(c2w) << left << id1 << id2 << endl + << setw(c1w) << left << "Attr name" << setw(c2w) << left << attrName1 << attrName2 << endl + << setw(c1w) << left << "Attr value" << setw(c2w) << left << attrValue1 << attrValue2 << endl + << setw(c1w) << left << "Char data" << setw(c2w) << left << charData1 << charData2 << endl + << setw(c1w) << left << "Child count" << setw(c2w) << left << childCount1 << childCount2; + + return msg.str(); +} + +namespace { + daeElement::compareResult compareMatch() { + daeElement::compareResult result; + result.compareValue = 0; + return result; + } + + daeElement::compareResult nameMismatch(daeElement& elt1, daeElement& elt2) { + daeElement::compareResult result; + result.elt1 = &elt1; + result.elt2 = &elt2; + result.compareValue = strcmp(elt1.getElementName(), elt2.getElementName()); + result.nameMismatch = true; + return result; + } + + daeElement::compareResult attrMismatch(daeElement& elt1, daeElement& elt2, const string& attr) { + daeElement::compareResult result; + result.elt1 = &elt1; + result.elt2 = &elt2; + result.compareValue = strcmp(elt1.getAttribute(attr.c_str()).c_str(), + elt2.getAttribute(attr.c_str()).c_str()); + result.attrMismatch = attr; + return result; + } + + daeElement::compareResult charDataMismatch(daeElement& elt1, daeElement& elt2) { + daeElement::compareResult result; + result.elt1 = &elt1; + result.elt2 = &elt2; + result.compareValue = strcmp(elt1.getCharData().c_str(), + elt2.getCharData().c_str()); + result.charDataMismatch = true; + return result; + } + + daeElement::compareResult childCountMismatch(daeElement& elt1, daeElement& elt2) { + daeElement::compareResult result; + result.elt1 = &elt1; + result.elt2 = &elt2; + daeElementRefArray children1 = elt1.getChildren(), + children2 = elt2.getChildren(); + result.compareValue = int(children1.getCount()) - int(children2.getCount()); + result.childCountMismatch = true; + return result; + } + + daeElement::compareResult compareElementsSameType(daeElement& elt1, daeElement& elt2) { + // Compare attributes + for (size_t i = 0; i < elt1.getAttributeCount(); i++) + if (elt1.getAttributeObject(i)->compare(&elt1, &elt2) != 0) + return attrMismatch(elt1, elt2, elt1.getAttributeName(i)); + + // Compare character data + if (elt1.getCharDataObject()) + if (elt1.getCharDataObject()->compare(&elt1, &elt2) != 0) + return charDataMismatch(elt1, elt2); + + // Compare children + daeElementRefArray children1 = elt1.getChildren(), + children2 = elt2.getChildren(); + if (children1.getCount() != children2.getCount()) + return childCountMismatch(elt1, elt2); + for (size_t i = 0; i < children1.getCount(); i++) { + daeElement::compareResult result = daeElement::compareWithFullResult(*children1[i], *children2[i]); + if (result.compareValue != 0) + return result; + } + + return compareMatch(); + } + + daeElement::compareResult compareElementsDifferentTypes(daeElement& elt1, daeElement& elt2) { + string value1, value2; + + // Compare attributes. Be careful because each element could have a + // different number of attributes. + if (elt1.getAttributeCount() > elt2.getAttributeCount()) + return attrMismatch(elt1, elt2, elt1.getAttributeName(elt2.getAttributeCount())); + if (elt2.getAttributeCount() > elt1.getAttributeCount()) + return attrMismatch(elt1, elt2, elt2.getAttributeName(elt1.getAttributeCount())); + for (size_t i = 0; i < elt1.getAttributeCount(); i++) { + elt1.getAttribute(i, value1); + elt2.getAttribute(elt1.getAttributeName(i).c_str(), value2); + if (value1 != value2) + return attrMismatch(elt1, elt2, elt1.getAttributeName(i)); + } + + // Compare character data + elt1.getCharData(value1); + elt2.getCharData(value2); + if (value1 != value2) + return charDataMismatch(elt1, elt2); + + // Compare children + daeElementRefArray children1 = elt1.getChildren(), + children2 = elt2.getChildren(); + if (children1.getCount() != children2.getCount()) + return childCountMismatch(elt1, elt2); + for (size_t i = 0; i < children1.getCount(); i++) { + daeElement::compareResult result = daeElement::compareWithFullResult(*children1[i], *children2[i]); + if (result.compareValue != 0) + return result; + } + + return compareMatch(); + } +} // namespace { + +int daeElement::compare(daeElement& elt1, daeElement& elt2) { + return compareWithFullResult(elt1, elt2).compareValue; +} + +daeElement::compareResult daeElement::compareWithFullResult(daeElement& elt1, daeElement& elt2) { + // Check the element name + if (strcmp(elt1.getElementName(), elt2.getElementName()) != 0) + return nameMismatch(elt1, elt2); + + // Dispatch to a specific function based on whether or not the types are the same + if ((elt1.typeID() != elt2.typeID()) || elt1.typeID() == domAny::ID()) + return compareElementsDifferentTypes(elt1, elt2); + else + return compareElementsSameType(elt1, elt2); +} + + +daeURI *daeElement::getDocumentURI() const { + if ( _document == NULL ) { + return NULL; + } + return _document->getDocumentURI(); +} + + +daeElement::matchName::matchName(daeString name) : name(name) { } + +bool daeElement::matchName::operator()(daeElement* elt) const { + return strcmp(elt->getElementName(), name.c_str()) == 0; +} + +daeElement::matchType::matchType(daeInt typeID) : typeID(typeID) { } + +bool daeElement::matchType::operator()(daeElement* elt) const { + return elt->typeID() == typeID; +} + +daeElement* daeElement::getChild(const matchElement& matcher) { + daeElementRefArray children; + getChildren(children); + for (size_t i = 0; i < children.getCount(); i++) + if (matcher(children[i])) + return children[i]; + + return NULL; +} + +daeElement* daeElement::getDescendant(const matchElement& matcher) { + daeElementRefArray elts; + getChildren(elts); + + for (size_t i = 0; i < elts.getCount(); i++) { + // Check the current element for a match + if (matcher(elts[i])) + return elts[i]; + + // Append the element's children to the queue + daeElementRefArray children; + elts[i]->getChildren(children); + size_t oldCount = elts.getCount(); + elts.setCount(elts.getCount() + children.getCount()); + for (size_t j = 0; j < children.getCount(); j++) + elts[oldCount + j] = children[j]; + } + + return NULL; +} + +daeElement* daeElement::getAncestor(const matchElement& matcher) { + daeElement* elt = getParent(); + while (elt) { + if (matcher(elt)) + return elt; + elt = elt->getParent(); + } + + return NULL; +} + +daeElement* daeElement::getParent() { + return _parent; +} + +daeElement* daeElement::getChild(daeString eltName) { + if (!eltName) + return NULL; + matchName test(eltName); + return getChild(matchName(eltName)); +} + +daeElement* daeElement::getDescendant(daeString eltName) { + if (!eltName) + return NULL; + return getDescendant(matchName(eltName)); +} + +daeElement* daeElement::getAncestor(daeString eltName) { + if (!eltName) + return NULL; + return getAncestor(matchName(eltName)); +} + +DAE* daeElement::getDAE() { + return _meta->getDAE(); +} + +void daeElement::setUserData(void* data) { + _userData = data; +} + +void* daeElement::getUserData() { + return _userData; +} diff --git a/Engine/lib/collada/src/dae/daeError.cpp b/Engine/lib/collada/src/dae/daeError.cpp new file mode 100644 index 000000000..cca0a6037 --- /dev/null +++ b/Engine/lib/collada/src/dae/daeError.cpp @@ -0,0 +1,46 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include + +typedef struct +{ + int errCode; + const char *errString; +} DAEERROR; + +static DAEERROR errorsArray[] = +{ + { DAE_OK, "Success" }, + { DAE_ERROR, "Generic error" }, + { DAE_ERR_INVALID_CALL, "Invalid function call" }, + { DAE_ERR_FATAL, "Fatal" }, + { DAE_ERR_BACKEND_IO, "Backend IO" }, + { DAE_ERR_BACKEND_VALIDATION, "Backend validation" }, + { DAE_ERR_QUERY_SYNTAX, "Query syntax" }, + { DAE_ERR_QUERY_NO_MATCH, "Query no match" }, + { DAE_ERR_COLLECTION_ALREADY_EXISTS, "A document with the same name exists already" }, + { DAE_ERR_COLLECTION_DOES_NOT_EXIST, "No document is loaded with that name or index" }, + { DAE_ERR_NOT_IMPLEMENTED, "This function is not implemented in this reference implementation" }, +}; + +const char *daeErrorString(int errorCode) +{ + int iErrorCount = (int)(sizeof(errorsArray)/sizeof(DAEERROR)); + for (int i=0;i +#include + +daeErrorHandler *daeErrorHandler::_instance = NULL; +std::auto_ptr daeErrorHandler::_defaultInstance(new stdErrPlugin); + +daeErrorHandler::daeErrorHandler() { +} + +daeErrorHandler::~daeErrorHandler() { +} + +void daeErrorHandler::setErrorHandler( daeErrorHandler *eh ) { + _instance = eh; +} + +daeErrorHandler *daeErrorHandler::get() { + if ( _instance == NULL ) { + return _defaultInstance.get(); + } + return _instance; +} diff --git a/Engine/lib/collada/src/dae/daeIDRef.cpp b/Engine/lib/collada/src/dae/daeIDRef.cpp new file mode 100644 index 000000000..3fbd32a69 --- /dev/null +++ b/Engine/lib/collada/src/dae/daeIDRef.cpp @@ -0,0 +1,177 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include + +using namespace std; + +void +daeIDRef::initialize() +{ + id = ""; + container = NULL; +} + +daeIDRef::daeIDRef() +{ + initialize(); +} + +daeIDRef::daeIDRef(daeString IDRefString) +{ + initialize(); + setID(IDRefString); +} + +daeIDRef::daeIDRef(const daeIDRef& copyFrom_) +{ + initialize(); + copyFrom(copyFrom_); +} + +daeIDRef::daeIDRef(daeElement& container) { + initialize(); + setContainer(&container); +} + + +void +daeIDRef::reset() +{ + setID(""); +} + +bool daeIDRef::operator==(const daeIDRef& other) const { + return (!strcmp(other.getID(), getID())); +} + +daeIDRef &daeIDRef::operator=( const daeIDRef& other) { + if (!container) + container = other.container; + id = other.getID(); + return *this; +} + +daeString +daeIDRef::getID() const +{ + return id.c_str(); +} + +void +daeIDRef::setID(daeString _IDString) +{ + id = _IDString ? _IDString : ""; +} + +daeElement* daeIDRef::getElement() const { + if (container) + return container->getDAE()->getIDRefResolvers().resolveElement(id, container->getDocument()); + return NULL; +} + +daeElement* daeIDRef::getContainer() const { + return(container); +} + +void daeIDRef::setContainer(daeElement* cont) { + container = cont; +} + +void +daeIDRef::print() +{ + fprintf(stderr,"id = %s\n",id.c_str()); + fflush(stderr); +} + +// These methods are provided for backward compatibility only. +void daeIDRef::validate() { } + +void daeIDRef::resolveElement( daeString ) { } + +void daeIDRef::resolveID() { } + +daeIDRef &daeIDRef::get( daeUInt idx ) { + (void)idx; + return *this; +} + +size_t daeIDRef::getCount() const { + return 1; +} + +daeIDRef& daeIDRef::operator[](size_t index) { + (void)index; + return *this; +} + +void +daeIDRef::copyFrom(const daeIDRef& copyFrom) { + *this = copyFrom; +} + +daeIDRef::ResolveState daeIDRef::getState() const { + if (id.empty()) + return id_empty; + if (getElement()) + return id_success; + return id_failed_id_not_found; +} + + +daeIDRefResolver::daeIDRefResolver(DAE& dae) : dae(&dae) { } + +daeIDRefResolver::~daeIDRefResolver() { } + + +daeDefaultIDRefResolver::daeDefaultIDRefResolver(DAE& dae) : daeIDRefResolver(dae) { } + +daeDefaultIDRefResolver::~daeDefaultIDRefResolver() { } + +daeString +daeDefaultIDRefResolver::getName() +{ + return "DefaultIDRefResolver"; +} + +daeElement* daeDefaultIDRefResolver::resolveElement(const string& id, daeDocument* doc) { + return doc ? dae->getDatabase()->idLookup(id, doc) : NULL; +} + + +daeIDRefResolverList::daeIDRefResolverList() { } + +daeIDRefResolverList::~daeIDRefResolverList() { + for (size_t i = 0; i < resolvers.getCount(); i++) + delete resolvers[i]; +} + +void daeIDRefResolverList::addResolver(daeIDRefResolver* resolver) { + resolvers.append(resolver); +} + +void daeIDRefResolverList::removeResolver(daeIDRefResolver* resolver) { + resolvers.remove(resolver); +} + +daeElement* daeIDRefResolverList::resolveElement(const string& id, daeDocument* doc) { + for(size_t i = 0; i < resolvers.getCount(); i++) + if (daeElement* el = resolvers[i]->resolveElement(id, doc)) + return el; + return NULL; +} diff --git a/Engine/lib/collada/src/dae/daeIOPluginCommon.cpp b/Engine/lib/collada/src/dae/daeIOPluginCommon.cpp new file mode 100644 index 000000000..8b5b23b7a --- /dev/null +++ b/Engine/lib/collada/src/dae/daeIOPluginCommon.cpp @@ -0,0 +1,139 @@ +/* + * Copyright 2007 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +daeIOPluginCommon::daeIOPluginCommon() + : database(NULL), + topMeta(NULL) +{ +} + +daeIOPluginCommon::~daeIOPluginCommon() +{ +} + +daeInt daeIOPluginCommon::setMeta(daeMetaElement *_topMeta) +{ + topMeta = _topMeta; + return DAE_OK; +} + +void daeIOPluginCommon::setDatabase(daeDatabase* _database) +{ + database = _database; +} + +// This function needs to be re-entrant, it can be called recursively from inside of resolveAll +// to load files that the first file depends on. +daeInt daeIOPluginCommon::read(const daeURI& uri, daeString docBuffer) +{ + // Make sure topMeta has been set before proceeding + if (topMeta == NULL) + { + return DAE_ERR_BACKEND_IO; + } + + // Generate a version of the URI with the fragment removed + daeURI fileURI(*uri.getDAE(), uri.str(), true); + + //check if document already exists + if ( database->isDocumentLoaded( fileURI.getURI() ) ) + { + return DAE_ERR_COLLECTION_ALREADY_EXISTS; + } + + daeElementRef domObject = docBuffer ? + readFromMemory(docBuffer, fileURI) : + readFromFile(fileURI); // Load from URI + + if (!domObject) { + string msg = docBuffer ? + "Failed to load XML document from memory\n" : + string("Failed to load ") + fileURI.str() + "\n"; + daeErrorHandler::get()->handleError(msg.c_str()); + return DAE_ERR_BACKEND_IO; + } + + // Insert the document into the database, the Database will keep a ref on the main dom, so it won't get deleted + // until we clear the database + + daeDocument *document = NULL; + + int res = database->insertDocument(fileURI.getURI(),domObject,&document); + if (res!= DAE_OK) + return res; + + return DAE_OK; +} + +daeElementRef daeIOPluginCommon::beginReadElement(daeElement* parentElement, + daeString elementName, + const vector& attributes, + daeInt lineNumber) { + daeMetaElement* parentMeta = parentElement ? parentElement->getMeta() : topMeta; + daeElementRef element = parentMeta->create(elementName); + + if(!element) + { + ostringstream msg; + msg << "The DOM was unable to create an element named " << elementName << " at line " + << lineNumber << ". Probably a schema violation.\n"; + daeErrorHandler::get()->handleWarning( msg.str().c_str() ); + return NULL; + } + + // Process the attributes + for (size_t i = 0; i < attributes.size(); i++) { + daeString name = attributes[i].first, + value = attributes[i].second; + if (!element->setAttribute(name, value)) { + ostringstream msg; + msg << "The DOM was unable to create an attribute " << name << " = " << value + << " at line " << lineNumber << ".\nProbably a schema violation.\n"; + daeErrorHandler::get()->handleWarning(msg.str().c_str()); + } + } + + if (parentElement == NULL) { + // This is the root element. Check the COLLADA version. + daeURI *xmlns = (daeURI*)(element->getMeta()->getMetaAttribute( "xmlns" )->getWritableMemory( element )); + if ( strcmp( xmlns->getURI(), COLLADA_NAMESPACE ) != 0 ) { + // Invalid COLLADA version + daeErrorHandler::get()->handleError("Trying to load an invalid COLLADA version for this DOM build!"); + return NULL; + } + } + + return element; +} + +bool daeIOPluginCommon::readElementText(daeElement* element, daeString text, daeInt elementLineNumber) { + if (element->setCharData(text)) + return true; + + ostringstream msg; + msg << "The DOM was unable to set a value for element of type " << element->getTypeName() + << " at line " << elementLineNumber << ".\nProbably a schema violation.\n"; + daeErrorHandler::get()->handleWarning(msg.str().c_str()); + return false; +} diff --git a/Engine/lib/collada/src/dae/daeMemorySystem.cpp b/Engine/lib/collada/src/dae/daeMemorySystem.cpp new file mode 100644 index 000000000..b67fdec1e --- /dev/null +++ b/Engine/lib/collada/src/dae/daeMemorySystem.cpp @@ -0,0 +1,34 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +//#include + +daeRawRef +daeMemorySystem::alloc(daeString pool, size_t n) +{ + (void)pool; + void *mem = malloc(n); +// memset(mem,0,n); +// printf("alloc[%s] - %d = 0x%x\n",pool,n,mem); + return (daeRawRef)mem; +} + +void +daeMemorySystem::dealloc(daeString pool, daeRawRef mem) +{ + (void)pool; +// printf("free[%s] - 0x%x\n",pool,mem); + free(mem); +} + diff --git a/Engine/lib/collada/src/dae/daeMetaAny.cpp b/Engine/lib/collada/src/dae/daeMetaAny.cpp new file mode 100644 index 000000000..bceee8d71 --- /dev/null +++ b/Engine/lib/collada/src/dae/daeMetaAny.cpp @@ -0,0 +1,62 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include + +daeMetaAny::daeMetaAny( daeMetaElement *container, daeMetaCMPolicy *parent, daeUInt ordinal, + daeInt minO, daeInt maxO) : daeMetaCMPolicy( container, parent, ordinal, minO, maxO ) +{} + +daeMetaAny::~daeMetaAny() +{} + +daeElement *daeMetaAny::placeElement( daeElement *parent, daeElement *child, daeUInt &ordinal, daeInt offset, daeElement* before, daeElement *after ) { + //remove element from praent + (void)offset; + (void)before; + (void)after; + daeElement::removeFromParent( child ); + child->setParentElement( parent ); + //************************************************************************* + ordinal = 0; + return child; +} + +daeBool daeMetaAny::removeElement( daeElement *parent, daeElement *child ) { + (void)parent; + (void)child; + return true; +} + +daeMetaElement * daeMetaAny::findChild( daeString elementName ) { + if ( elementName != NULL ) { + const daeMetaElementRefArray &metas = _container->getDAE()->getAllMetas(); + size_t cnt = metas.getCount(); + for ( size_t x = 0; x < cnt; x++ ) { + if ( metas[x] && !metas[x]->getIsInnerClass() && strcmp( elementName, metas[x]->getName() ) == 0 ) { + return metas[x]; + } + } + } + return domAny::registerElement(*_container->getDAE()); +} + +void daeMetaAny::getChildren( daeElement *parent, daeElementRefArray &array ) { + (void)parent; + (void)array; + //this is taken care of by the _contents in metaElement +} + diff --git a/Engine/lib/collada/src/dae/daeMetaAttribute.cpp b/Engine/lib/collada/src/dae/daeMetaAttribute.cpp new file mode 100644 index 000000000..3d7c5a488 --- /dev/null +++ b/Engine/lib/collada/src/dae/daeMetaAttribute.cpp @@ -0,0 +1,178 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include + +void daeMetaAttribute::set(daeElement* e, daeString s) { + stringToMemory(e, s); +} + +void daeMetaAttribute::copy(daeElement* to, daeElement *from) { + _type->copy(get(from), get(to)); +} + +void daeMetaArrayAttribute::copy(daeElement* to, daeElement *from) { + daeArray& fromArray = (daeArray&)*get(from); + daeArray& toArray = (daeArray&)*get(to); + _type->copyArray(fromArray, toArray); +} + +void daeMetaAttribute::copyDefault(daeElement* element) { + if (_defaultValue) + _type->copy(_defaultValue, get(element)); +} + +void daeMetaArrayAttribute::copyDefault(daeElement* element) { + if (_defaultValue) + _type->copyArray((daeArray&)*_defaultValue, (daeArray&)*get(element)); +} + +daeInt daeMetaAttribute::compare(daeElement* elt1, daeElement* elt2) { + return _type->compare(get(elt1), get(elt2)); +} + +daeInt daeMetaArrayAttribute::compare(daeElement* elt1, daeElement* elt2) { + daeArray& value1 = (daeArray&)*get(elt1); + daeArray& value2 = (daeArray&)*get(elt2); + return _type->compareArray(value1, value2); +} + +daeInt daeMetaAttribute::compareToDefault(daeElement* e) { + if (!_defaultValue) + return 1; + return _type->compare(get(e), _defaultValue); +} + +daeInt daeMetaArrayAttribute::compareToDefault(daeElement* e) { + if (!_defaultValue) + return 1; + daeArray& value1 = (daeArray&)*get(e); + daeArray& value2 = (daeArray&)*_defaultValue; + return _type->compareArray(value1, value2); +} + +daeMetaAttribute::daeMetaAttribute() +{ + _name = "noname"; + _offset = -1; + _type = NULL; + _container = NULL; + _defaultString = ""; + _defaultValue = NULL; + _isRequired = false; +} + +daeMetaAttribute::~daeMetaAttribute() { + if (_defaultValue) + _type->destroy(_defaultValue); + _defaultValue = NULL; +} + +daeMetaArrayAttribute::~daeMetaArrayAttribute() { + delete (daeArray*)_defaultValue; + _defaultValue = NULL; +} + +daeInt +daeMetaAttribute::getSize() +{ + return _type->getSize(); +} +daeInt +daeMetaAttribute::getAlignment() +{ + return _type->getAlignment(); +} + +void daeMetaAttribute::memoryToString(daeElement* e, std::ostringstream& buffer) { + _type->memoryToString(get(e), buffer); +} + +void daeMetaAttribute::stringToMemory(daeElement* e, daeString s) { + if (!strcmp(_name, "id") && e->getDocument()) + e->getDocument()->changeElementID(e, s); + else if (!strcmp(_name, "sid") && e->getDocument()) + e->getDocument()->changeElementSID(e, s); + + _type->stringToMemory((daeChar*)s, get(e)); +} + +daeChar* daeMetaAttribute::getWritableMemory(daeElement* e) { + return (daeChar*)e + _offset; +} + +daeMemoryRef daeMetaAttribute::get(daeElement* e) { + return getWritableMemory(e); +} + +void daeMetaAttribute::setDefaultString(daeString defaultVal) { + _defaultString = defaultVal; + if (!_defaultValue) + _defaultValue = _type->create(); + _type->stringToMemory((daeChar*)_defaultString.c_str(), _defaultValue); +} + +void daeMetaAttribute::setDefaultValue(daeMemoryRef defaultVal) { + if (!_defaultValue) + _defaultValue = _type->create(); + _type->copy(defaultVal, _defaultValue); + std::ostringstream buffer; + _type->memoryToString(_defaultValue, buffer); + _defaultString = buffer.str(); +} + +void daeMetaArrayAttribute::memoryToString(daeElement* e, std::ostringstream& buffer) { + if (e) + _type->arrayToString(*(daeArray*)get(e), buffer); +} + +void daeMetaArrayAttribute::stringToMemory(daeElement* e, daeString s) { + if (e) + _type->stringToArray((daeChar*)s, *(daeArray*)get(e)); +} + +void daeMetaArrayAttribute::setDefaultString(daeString defaultVal) { + _defaultString = defaultVal; + if (!_defaultValue) + _defaultValue = (daeMemoryRef)_type->createArray(); + _type->stringToArray((daeChar*)_defaultString.c_str(), (daeArray&)*_defaultValue); +} + +void daeMetaArrayAttribute::setDefaultValue(daeMemoryRef defaultVal) { + if (!_defaultValue) + _defaultValue = (daeMemoryRef)_type->createArray(); + _type->copyArray((daeArray&)*defaultVal, (daeArray&)*_defaultValue); + std::ostringstream buffer; + _type->arrayToString((daeArray&)*_defaultValue, buffer); + _defaultString = buffer.str(); +} + +daeString daeMetaAttribute::getDefaultString() { + return _defaultString.c_str(); +} + +daeMemoryRef daeMetaAttribute::getDefaultValue() { + return _defaultValue; +} + +void daeMetaAttribute::setDocument(daeElement* e, daeDocument* doc) { + _type->setDocument(get(e), doc); +} + +void daeMetaArrayAttribute::setDocument(daeElement* e, daeDocument* doc) { + _type->setDocument(*(daeArray*)get(e), doc); +} diff --git a/Engine/lib/collada/src/dae/daeMetaCMPolicy.cpp b/Engine/lib/collada/src/dae/daeMetaCMPolicy.cpp new file mode 100644 index 000000000..04e4e52e2 --- /dev/null +++ b/Engine/lib/collada/src/dae/daeMetaCMPolicy.cpp @@ -0,0 +1,22 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include + +daeMetaCMPolicy::~daeMetaCMPolicy() +{ + for( size_t i = 0; i < _children.getCount(); i++ ) { + delete _children[i]; + } +} + diff --git a/Engine/lib/collada/src/dae/daeMetaChoice.cpp b/Engine/lib/collada/src/dae/daeMetaChoice.cpp new file mode 100644 index 000000000..66ad406e2 --- /dev/null +++ b/Engine/lib/collada/src/dae/daeMetaChoice.cpp @@ -0,0 +1,153 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include + +daeMetaChoice::daeMetaChoice( daeMetaElement *container, daeMetaCMPolicy *parent, daeUInt choiceNum, daeUInt ordinal, + daeInt minO, daeInt maxO) : daeMetaCMPolicy( container, parent, ordinal, minO, maxO ), _choiceNum(choiceNum) +{} + +daeMetaChoice::~daeMetaChoice() +{} + +daeElement *daeMetaChoice::placeElement( daeElement *parent, daeElement *child, daeUInt &ordinal, daeInt offset, daeElement* before, daeElement *after ) { + (void)offset; + if ( _maxOccurs == -1 ) { + //Needed to prevent infinate loops. If unbounded check to see if you have the child before just trying to place + if ( findChild( child->getElementName() ) == NULL ) { + return NULL; + } + } + + daeElement *retVal = NULL; + size_t cnt = _children.getCount(); + + daeTArray< daeCharArray *> *CMData = (daeTArray< daeCharArray *>*)_container->getMetaCMData()->getWritableMemory(parent); + daeCharArray *myData = CMData->get( _choiceNum ); + + for ( daeInt i = 0; ( i < _maxOccurs || _maxOccurs == -1 ); i++ ) + { + if ( (daeInt)myData->getCount() > i && myData->get(i) != -1 ) //choice has already been made + { + if ( _children[ myData->get(i) ]->placeElement( parent, child, ordinal, i, before, after ) != NULL ) + { + retVal = child; + ordinal = ordinal + _ordinalOffset; + break; + } + //else //try to see if everything can be in a different choice + //{ + // daeElementRefArray childsInChoice; + // _children[ myData->get(i) ]->getChildren( parent, childsInChoice ); + // for ( size_t x = myData->get(i) +1; x < cnt; x++ ) + // { + // daeElementRefArray childsInNext; + // _children[ x ]->getChildren( parent, childsInNext ); //If you get children in another choice then + // //both choices can have the same type of children. + // if ( childsInNext.getCount() == childsInChoice.getCount() ) + // { + // //if there are the same ammount of children then all present children can belong to both + // //choices. Try to place the new child in this next choice. + // if ( _children[x]->placeElement( parent, child, ordinal, i, before, after ) != NULL ) + // { + // retVal = child; + // ordinal = ordinal + _ordinalOffset; + + // myData->set( i, (daeChar)x ); //change the choice to this new one + // break; + // } + // } + // } + // if ( retVal != NULL ) break; + //} + } + else //no choice has been made yet + { + for ( size_t x = 0; x < cnt; x++ ) + { + if ( _children[x]->placeElement( parent, child, ordinal, i, before, after ) != NULL ) + { + retVal = child; + ordinal = ordinal + _ordinalOffset; + + myData->append( (daeChar)x ); //you always place in the next available choice up to maxOccurs + break; + } + } + if ( retVal != NULL ) break; + } + } + if ( retVal == NULL ) + { + if ( findChild( child->getElementName() ) == NULL ) { + return NULL; + } + for ( daeInt i = 0; ( i < _maxOccurs || _maxOccurs == -1 ); i++ ) + { + daeElementRefArray childsInChoice; + _children[ myData->get(i) ]->getChildren( parent, childsInChoice ); + for ( size_t x = myData->get(i) +1; x < cnt; x++ ) + { + daeElementRefArray childsInNext; + _children[ x ]->getChildren( parent, childsInNext ); //If you get children in another choice then + //both choices can have the same type of children. + if ( childsInNext.getCount() == childsInChoice.getCount() ) + { + //if there are the same ammount of children then all present children can belong to both + //choices. Try to place the new child in this next choice. + if ( _children[x]->placeElement( parent, child, ordinal, i, before, after ) != NULL ) + { + retVal = child; + ordinal = ordinal + _ordinalOffset; + + myData->set( i, (daeChar)x ); //change the choice to this new one + break; + } + } + } + if ( retVal != NULL ) break; + } + } + return retVal; +} + +daeBool daeMetaChoice::removeElement( daeElement *parent, daeElement *child ) { + size_t cnt = _children.getCount(); + for ( size_t x = 0; x < cnt; x++ ) { + if ( _children[x]->removeElement( parent, child ) ) { + return true; + } + } + return false; +} + +daeMetaElement * daeMetaChoice::findChild( daeString elementName ) { + daeMetaElement *me = NULL; + size_t cnt = _children.getCount(); + for ( size_t x = 0; x < cnt; x++ ) { + me = _children[x]->findChild( elementName ); + if ( me != NULL ) { + return me; + } + } + return NULL; +} + +void daeMetaChoice::getChildren( daeElement *parent, daeElementRefArray &array ) { + size_t cnt = _children.getCount(); + for ( size_t x = 0; x < cnt; x++ ) { + _children[x]->getChildren( parent, array ); + } +} + diff --git a/Engine/lib/collada/src/dae/daeMetaElement.cpp b/Engine/lib/collada/src/dae/daeMetaElement.cpp new file mode 100644 index 000000000..0127e12a4 --- /dev/null +++ b/Engine/lib/collada/src/dae/daeMetaElement.cpp @@ -0,0 +1,477 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +daeMetaElement::create() +{ + daeElementRef ret = (*_createFunc)(dae); + ret->setup(this); + + return ret; +} + +daeElementRef +daeMetaElement::create(daeString s) +{ + daeMetaElement* me = NULL; + if ( strcmp( s, _name ) == 0 ) { + //looking for this meta + me = this; + } + else if ( _contentModel != NULL ) { + me = _contentModel->findChild(s); + } + if (me != NULL) { + daeElementRef ret = me->create(); + if ( strcmp(s, me->getName() ) != 0 ) { + ret->setElementName(s); + } + return ret; + } + if ( getAllowsAny() ) { + daeElementRef ret = domAny::registerElement(dae)->create(); + ret->setElementName(s); + return ret; + } + return NULL; +} + +daeMetaElement::daeMetaElement(DAE& dae) : dae(dae) +{ + _name = "noname"; + _createFunc = NULL; + _elementSize = sizeof(daeElement); + _metaValue = NULL; + _metaContents = NULL; + _metaContentsOrder = NULL; // sthomas + _metaID = NULL; + _isTrackableForQueries = true; + _usesStringContents = false; + _isTransparent = false; + _isAbstract = false; + _allowsAny = false; + _innerClass = false; + _contentModel = NULL; + _metaCMData = NULL; + _numMetaChoices = 0; +} + +daeMetaElement::~daeMetaElement() +{ + delete _metaContents; + delete _contentModel; + delete _metaContentsOrder; + delete _metaCMData; +} + +DAE* daeMetaElement::getDAE() { + return &dae; +} + +void daeMetaElement::setCMRoot( daeMetaCMPolicy *cm ) +{ + if (_contentModel) + delete _contentModel; + _contentModel = cm; +} + +void +daeMetaElement::addContents(daeInt offset) +{ + daeMetaElementArrayAttribute* meaa = new daeMetaElementArrayAttribute( this, NULL, 0, 1, -1 ); + meaa->setType(dae.getAtomicTypes().get("element")); + meaa->setName("contents"); + meaa->setOffset(offset); + meaa->setContainer( this); + _metaContents = meaa; +} +void +daeMetaElement::addContentsOrder(daeInt offset) +{ + daeMetaArrayAttribute* meaa = new daeMetaArrayAttribute(); + meaa->setType(dae.getAtomicTypes().get("uint")); + meaa->setName("contentsOrder"); + meaa->setOffset(offset); + meaa->setContainer( this); + + if (_metaContentsOrder) + delete _metaContentsOrder; + + _metaContentsOrder = meaa; +} + +void daeMetaElement::addCMDataArray(daeInt offset, daeUInt numChoices) +{ + daeMetaArrayAttribute* meaa = new daeMetaArrayAttribute(); + meaa->setType(dae.getAtomicTypes().get("int")); + meaa->setName("CMData"); + meaa->setOffset(offset); + meaa->setContainer( this); + + if (_metaCMData) + delete _metaCMData; + + _metaCMData = meaa; + + _numMetaChoices = numChoices; +} + + +/*void +daeMetaElement::appendArrayElement(daeMetaElement* element, daeInt offset, daeString name) +{ + daeMetaElementArrayAttribute* meaa = new daeMetaElementArrayAttribute; + meaa->setType(daeAtomicType::get("element")); + if ( name ) { + meaa->setName(name); + } + else { + meaa->setName(element->getName()); + } + meaa->setOffset(offset); + meaa->setContainer(this); + meaa->setElementType( element); + _metaElements.append(meaa); +} +void +daeMetaElement::appendElement(daeMetaElement* element, daeInt offset, daeString name) +{ + daeMetaElementAttribute* meaa = new daeMetaElementAttribute; + meaa->setType(daeAtomicType::get("element")); + if ( name ) { + meaa->setName(name); + } + else { + meaa->setName(element->getName()); + } + meaa->setOffset( offset); + meaa->setContainer( this ); + meaa->setElementType( element ); + _metaElements.append(meaa); +}*/ + +void +daeMetaElement::appendAttribute(daeMetaAttribute* attr) +{ + if (attr == NULL) + return; + + if (strcmp(attr->getName(),"_value") == 0) { + _metaValue = attr; + } + else + _metaAttributes.append(attr); + + if ((attr->getName() != NULL) && + (strcmp(attr->getName(),"id") == 0)) { + _metaID = attr; + _isTrackableForQueries = true; + } +} + +void +daeMetaElement::validate() +{ + if (_elementSize == 0) + { + daeInt place=0; + unsigned int i; + for(i=0;i<_metaAttributes.getCount();i++) { + place += _metaAttributes[i]->getSize(); + int align = _metaAttributes[i]->getAlignment(); + place += align; + place &= (~(align-1)); + } + _elementSize = place; + } +} + +daeMetaAttribute* +daeMetaElement::getMetaAttribute(daeString s) +{ + int cnt = (int)_metaAttributes.getCount(); + int i; + for(i=0;igetName(),s) == 0) + return _metaAttributes[i]; + return NULL; +} + + +// void daeMetaElement::releaseMetas() +// { +// _metas().clear(); +// size_t count = _classMetaPointers().getCount(); +// for ( size_t i = 0; i < count; i++ ) +// { +// *(_classMetaPointers()[i]) = NULL; +// } +// _classMetaPointers().clear(); +// if (mera) +// { +// delete mera; +// mera = NULL; +// } +// if (mes) +// { +// delete mes; +// mes = NULL; +// } +// } + +daeBool daeMetaElement::place(daeElement *parent, daeElement *child, daeUInt *ordinal ) +{ + if (child->getMeta()->getIsAbstract() || parent->getMeta() != this ) { + return false; + } + daeUInt ord; + daeElement *retVal = _contentModel->placeElement( parent, child, ord ); + if ( retVal != NULL ) { + //update document pointer + child->setDocument( parent->getDocument() ); + retVal->setDocument( parent->getDocument() ); + //add to _contents array + if (_metaContents != NULL) { + daeElementRefArray* contents = + (daeElementRefArray*)_metaContents->getWritableMemory(parent); + daeUIntArray* contentsOrder = + (daeUIntArray*)_metaContentsOrder->getWritableMemory(parent); + daeBool needsAppend = true; + size_t cnt = contentsOrder->getCount(); + for ( size_t x = 0; x < cnt; x++ ) { + if ( contentsOrder->get(x) > ord ) { + contents->insertAt( x, retVal ); + contentsOrder->insertAt( x, ord ); + needsAppend = false; + break; + } + } + if ( needsAppend ) { + contents->append(retVal); + contentsOrder->append( ord ); + } + } + if ( ordinal != NULL ) { + *ordinal = ord; + } + } + return retVal!=NULL; +} + +daeBool daeMetaElement::placeAt( daeInt index, daeElement *parent, daeElement *child ) +{ + if (child->getMeta()->getIsAbstract() || parent->getMeta() != this || index < 0 ) { + return false; + } + daeUInt ord; + daeElement *retVal = _contentModel->placeElement( parent, child, ord ); + if ( retVal != NULL ) { + //add to _contents array + if (_metaContents != NULL) { + daeElementRefArray* contents = + (daeElementRefArray*)_metaContents->getWritableMemory(parent); + daeUIntArray* contentsOrder = + (daeUIntArray*)_metaContentsOrder->getWritableMemory(parent); + daeBool validLoc; + if ( index > 0 ) { + validLoc = contentsOrder->get(index) >= ord && contentsOrder->get(index) <= ord; + } + else { + if ( contentsOrder->getCount() == 0 ) { + validLoc = true; + } + else { + validLoc = contentsOrder->get(index) >= ord; + } + } + if ( validLoc ) { + contents->insertAt( index, retVal ); + contentsOrder->insertAt( index, ord ); + } + else { + _contentModel->removeElement( parent, retVal ); + retVal = NULL; + } + } + } + if ( retVal != NULL ) { + //update document pointer + child->setDocument( parent->getDocument() ); + retVal->setDocument( parent->getDocument() ); + } + return retVal!=NULL; +} + +daeBool daeMetaElement::placeBefore( daeElement *marker, daeElement *parent, daeElement *child, daeUInt *ordinal ) +{ + if (child->getMeta()->getIsAbstract() || parent->getMeta() != this ) { + return false; + } + daeUInt ord; + daeElement *retVal = _contentModel->placeElement( parent, child, ord, 0, marker, NULL ); + if ( retVal != NULL ) { + //add to _contents array + if (_metaContents != NULL) { + daeElementRefArray* contents = + (daeElementRefArray*)_metaContents->getWritableMemory(parent); + daeUIntArray* contentsOrder = + (daeUIntArray*)_metaContentsOrder->getWritableMemory(parent); + size_t index(0); + daeBool validLoc = false; + if ( contents->find( marker, index ) == DAE_OK ) { + if ( index > 0 ) { + daeUInt gt = contentsOrder->get(index-1); + daeUInt lt = contentsOrder->get(index); + validLoc = gt <= ord && lt >= ord; + } + else { + validLoc = contentsOrder->get(index) >= ord; + } + } + if ( validLoc ) { + contents->insertAt( index, retVal ); + contentsOrder->insertAt( index, ord ); + if ( ordinal != NULL ) { + *ordinal = ord; + } + } + else { + _contentModel->removeElement( parent, retVal ); + retVal = NULL; + } + } + } + if ( retVal != NULL ) { + //update document pointer + child->setDocument( parent->getDocument() ); + retVal->setDocument( parent->getDocument() ); + } + return retVal!=NULL; +} + +daeBool daeMetaElement::placeAfter( daeElement *marker, daeElement *parent, daeElement *child, daeUInt *ordinal ) +{ + if (child->getMeta()->getIsAbstract() || parent->getMeta() != this ) { + return false; + } + daeUInt ord; + daeElement *retVal = _contentModel->placeElement( parent, child, ord, 0, NULL, marker ); + if ( retVal != NULL ) { + //add to _contents array + if (_metaContents != NULL) { + daeElementRefArray* contents = + (daeElementRefArray*)_metaContents->getWritableMemory(parent); + daeUIntArray* contentsOrder = + (daeUIntArray*)_metaContentsOrder->getWritableMemory(parent); + size_t index(0); + daeBool validLoc = false; + if ( contents->find( marker, index ) == DAE_OK ) { + if ( index < contentsOrder->getCount()-1 ) { + validLoc = contentsOrder->get(index) <= ord && contentsOrder->get(index+1) >= ord; + } + else { + validLoc = contentsOrder->get(index) <= ord; + } + } + if ( validLoc ) { + contents->insertAt( index+1, retVal ); + contentsOrder->insertAt( index+1, ord ); + if ( ordinal != NULL ) { + *ordinal = ord; + } + } + else { + _contentModel->removeElement( parent, retVal ); + retVal = NULL; + } + } + } + if ( retVal != NULL ) { + //update document pointer + child->setDocument( parent->getDocument() ); + retVal->setDocument( parent->getDocument() ); + } + return retVal!=NULL; +} + +daeBool daeMetaElement::remove(daeElement *parent, daeElement *child) +{ + if ( parent->getMeta() != this ) { + return false; + } + //prevent child from being deleted + daeElementRef el( child ); + if ( _contentModel->removeElement( parent, child ) ) { + if ( _metaContents != NULL) + { + daeElementRefArray* contents = (daeElementRefArray*)_metaContents->getWritableMemory(parent); + daeUIntArray* contentsOrder = (daeUIntArray*)_metaContentsOrder->getWritableMemory(parent); + size_t idx(0); + if ( contents->remove(child, &idx) == DAE_OK ) { + contentsOrder->removeIndex( idx ); + } + } + if ( child->getDocument() ) { + child->getDocument()->removeElement( child ); + } + + // Clear the child's parent pointer + child->setParentElement( NULL ); + + return true; + } + return false; +} + +void daeMetaElement::getChildren( daeElement* parent, daeElementRefArray &array ) +{ + if ( parent->getMeta() != this ) { + return; + } + if ( _metaContents != NULL ) { + daeElementRefArray* contents = (daeElementRefArray*)_metaContents->getWritableMemory(parent); + for ( size_t x = 0; x < contents->getCount(); x++ ) { + array.append( contents->get(x) ); + } + } + else if ( _contentModel != NULL ) { + _contentModel->getChildren( parent, array ); + } +} + +// daeMetaElementRefArray &daeMetaElement::_metas() +// { +// if (!mera) +// { +// mera = new daeMetaElementRefArray(); +// } +// return *mera; +// } + +// daeTArray< daeMetaElement** > &daeMetaElement::_classMetaPointers() +// { +// if (!mes) +// { +// mes = new daeTArray< daeMetaElement** >(); +// } +// return *mes; +// } + diff --git a/Engine/lib/collada/src/dae/daeMetaElementAttribute.cpp b/Engine/lib/collada/src/dae/daeMetaElementAttribute.cpp new file mode 100644 index 000000000..3c0d35761 --- /dev/null +++ b/Engine/lib/collada/src/dae/daeMetaElementAttribute.cpp @@ -0,0 +1,232 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include + +daeMetaElementAttribute::daeMetaElementAttribute( daeMetaElement *container, daeMetaCMPolicy *parent, daeUInt ordinal, + daeInt minO, daeInt maxO) : daeMetaCMPolicy( container, parent, ordinal, minO, maxO ) +{ + _elementType = NULL; +} + +daeMetaElementAttribute::~daeMetaElementAttribute() +{} + +daeMetaElementArrayAttribute::daeMetaElementArrayAttribute( daeMetaElement *container, daeMetaCMPolicy *parent, daeUInt ordinal, + daeInt minO, daeInt maxO) : daeMetaElementAttribute( container, parent, ordinal, minO, maxO ) +{ +} + +daeMetaElementArrayAttribute::~daeMetaElementArrayAttribute() +{} + + +void daeMetaElementAttribute::set(daeElement* e, daeString s) +{ + //_type->stringToMemory((char*)s, getWritableMemory(e)); + daeElementRef *ref = (daeElementRef*)(getWritableMemory(e)); + if ((*ref) == NULL) { + (*ref) = _elementType->create(); + } + (*ref)->getMeta()->getValueAttribute()->stringToMemory((*ref), s); +} + +void daeMetaElementAttribute::copy(daeElement* to, daeElement *from) { + daeElement *cpy = (*(daeElementRef*)(getWritableMemory(from)))->clone(); + (*(daeElementRef*)(getWritableMemory(to))) = cpy; +} + +void daeMetaElementArrayAttribute::copy(daeElement* to, daeElement *from) { + (void)to; + (void)from; +} + +void +daeMetaElementAttribute::setDocument( daeElement * parent, daeDocument* c ) +{ + daeElementRef* er = (daeElementRef*)getWritableMemory( parent ); + if ( ((daeElement*)(*er)) != NULL ) { + (*er)->setDocument( c ); + } +} + +void +daeMetaElementArrayAttribute::setDocument( daeElement * parent, daeDocument* c ) +{ + daeElementRefArray* era = (daeElementRefArray*)getWritableMemory( parent ); + for ( unsigned int i = 0; i < era->getCount(); i++ ) { + era->get(i)->setDocument( c ); + } +} + +daeInt +daeMetaElementAttribute::getCount(daeElement* e) +{ + if (e == NULL) + return 0; + return ((*((daeElementRef*)getWritableMemory(e))) != NULL); +} + +daeMemoryRef +daeMetaElementAttribute::get(daeElement *e, daeInt index) +{ + (void)index; + return getWritableMemory(e); +} + +daeInt +daeMetaElementArrayAttribute::getCount(daeElement *e) +{ + if (e == NULL) + return 0; + daeElementRefArray* era = (daeElementRefArray*)getWritableMemory(e); + if (era == NULL) + return 0; + return (daeInt)era->getCount(); +} + +daeMemoryRef +daeMetaElementArrayAttribute::get(daeElement* e, daeInt index) +{ + if (e == NULL) + return NULL; + daeElementRefArray* era = (daeElementRefArray*)getWritableMemory(e); + if (era == NULL || index >= (daeInt)era->getCount() ) + return NULL; + return (daeMemoryRef)&(era->get(index)); +} + +daeElement * +daeMetaElementAttribute::placeElement(daeElement* parent, daeElement* child, daeUInt &ordinal, daeInt offset, daeElement* before, daeElement *after ) +{ + (void)offset; + (void)before; + (void)after; + if ((parent == NULL)||(child == NULL)) + return NULL; + if ( child->getMeta() != _elementType || strcmp( child->getElementName(), _name ) != 0 ) { + return NULL; + } + if (child->getParentElement() == parent) { + //I Don't know why this gets called when the child already has this as parent. + return child; + } + daeElementRef* er = (daeElementRef*)getWritableMemory(parent); + + if ( *er != NULL ) + { + return NULL; + } + + daeElement::removeFromParent( child ); + child->setParentElement( parent ); + + *er = child; + ordinal = _ordinalOffset; + + return child; +} + +daeElement * +daeMetaElementArrayAttribute::placeElement(daeElement* parent, daeElement* child, daeUInt &ordinal, daeInt offset, daeElement* before, daeElement *after ) +{ + if ((parent == NULL)||(child == NULL)) + return NULL; + if ( child->getMeta() != _elementType || strcmp( child->getElementName(), _name ) != 0 ) { + return NULL; + } + daeElement *p = child->getParentElement(); + daeElementRefArray* era = (daeElementRefArray*)getWritableMemory(parent); + if ( _maxOccurs != -1 && (daeInt)era->getCount()-offset >= _maxOccurs ) { + return NULL; + } + removeElement( p, child ); + child->setParentElement( parent ); + + if ( before != NULL && before->getMeta() == _elementType ) { + size_t idx(0); + if ( era->find( before, idx ) == DAE_OK ) { + era->insertAt( idx, child ); + } + } + else if ( after != NULL && after->getMeta() == _elementType ) { + size_t idx(0); + if ( era->find( after, idx ) == DAE_OK ) { + era->insertAt( idx+1, child ); + } + } + else { + era->append(child); + } + ordinal = _ordinalOffset; + + return child; +} + +// These are the opposite of the placeElement functions above +daeBool +daeMetaElementAttribute::removeElement(daeElement* parent, daeElement* child) +{ + (void)child; // silence unused variable warning + + if ((parent == NULL)||(child == NULL )) + return false; + + daeElementRef* er = (daeElementRef*)getWritableMemory(parent); + if ( *er != child ) { + return false; + } + *er = NULL; + return true; +} + +daeBool +daeMetaElementArrayAttribute::removeElement(daeElement* parent, + daeElement* child) +{ + if ((parent == NULL)||(child == NULL)) + return false ; + + daeElementRefArray* era = (daeElementRefArray*)getWritableMemory(parent); +/* if ( (daeInt)era->getCount() <= _minOccurs ) { + return false; + }*/ + daeInt error = era->remove(child); + if ( error != DAE_OK ) { + return false; + } + return true; +} + +daeMetaElement *daeMetaElementAttribute::findChild( daeString elementName ) { + if ( strcmp( elementName, _name ) == 0 ) { + return _elementType; + } + return NULL; +} + +void daeMetaElementAttribute::getChildren( daeElement *parent, daeElementRefArray &array ) { + daeElementRef* er = (daeElementRef*)getWritableMemory(parent); + if ( *er != NULL ) { + array.appendUnique( *er ); + } +} + +void daeMetaElementArrayAttribute::getChildren( daeElement *parent, daeElementRefArray &array ) { + daeElementRefArray* era = (daeElementRefArray*)getWritableMemory(parent); + size_t cnt = era->getCount(); + for ( size_t x = 0; x < cnt; x++ ) { + array.appendUnique( era->get(x) ); + } +} diff --git a/Engine/lib/collada/src/dae/daeMetaGroup.cpp b/Engine/lib/collada/src/dae/daeMetaGroup.cpp new file mode 100644 index 000000000..0beb67151 --- /dev/null +++ b/Engine/lib/collada/src/dae/daeMetaGroup.cpp @@ -0,0 +1,149 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include + +daeMetaGroup::daeMetaGroup( daeMetaElementAttribute *econ, daeMetaElement *container, + daeMetaCMPolicy *parent, daeUInt ordinal, daeInt minO, daeInt maxO) : + daeMetaCMPolicy( container, parent, ordinal, minO, maxO ), _elementContainer( econ ) +{} + +daeMetaGroup::~daeMetaGroup() +{ + if ( _elementContainer != NULL ) { + delete _elementContainer; + } +} + +daeElement *daeMetaGroup::placeElement( daeElement *parent, daeElement *child, daeUInt &ordinal, daeInt offset, daeElement* before, daeElement *after ) { + (void)offset; + daeString nm = child->getElementName(); + if ( findChild( nm ) == NULL ) { + return false; + } + daeElementRef el; + + //check if the element trying to be placed is a group element. If so Just add it don't create a new one. + if ( strcmp( nm, _elementContainer->getName() ) == 0 ) { + if ( _elementContainer->placeElement(parent, child, ordinal, offset ) != NULL ) { + return child; + } + } + +#if 1 + daeInt elCnt = _elementContainer->getCount(parent); + //check existing groups + //This doesn't work properly. Because the choice can't check if you make two decisions you cannot fail + //here when you are supposed to. Luckily the current schema just has groups with single choices so + //every element needs a new group container. Wasteful but thats how the schema is and its how it works. + for ( daeInt x = 0; x < elCnt; x++ ) { + daeMemoryRef mem = _elementContainer->get(parent, x ); + if ( mem != NULL ) { + el = *(daeElementRef*)mem; + } + if ( el == NULL ) { + continue; + } + if ( before != NULL ) { + if ( _elementContainer->_elementType->placeBefore( before, el, child, &ordinal ) ) { + ordinal = ordinal + _ordinalOffset; + return el; + } + } + else if ( after != NULL ) { + if ( _elementContainer->_elementType->placeAfter( after, el, child, &ordinal ) ) { + ordinal = ordinal + _ordinalOffset; + return el; + } + } + else { + if ( _elementContainer->_elementType->place( el, child, &ordinal ) ) { + ordinal = ordinal + _ordinalOffset; + return el; + } + } + } +#endif + //if you couldn't place in existing groups make a new one if you can + el = _elementContainer->placeElement(parent, _elementContainer->_elementType->create(), ordinal, offset ); + if ( el != NULL ) { + //el = *(daeElementRef*)_elementContainer->get(parent, elCnt ); + if ( before != NULL ) { + if ( _elementContainer->_elementType->placeBefore( before, el, child, &ordinal ) ) { + ordinal = ordinal + _ordinalOffset; + return el; + } + } + else if ( after != NULL ) { + if ( _elementContainer->_elementType->placeAfter( after, el, child, &ordinal ) ) { + ordinal = ordinal + _ordinalOffset; + return el; + } + } + else { + if ( _elementContainer->_elementType->place( el, child, &ordinal ) ) { + ordinal = ordinal + _ordinalOffset; + return el; + } + } + } + return NULL; +} + +daeBool daeMetaGroup::removeElement( daeElement *parent, daeElement *child ) { + daeElementRef el; + daeInt elCnt = _elementContainer->getCount(parent); + for ( daeInt x = 0; x < elCnt; x++ ) { + daeMemoryRef mem = _elementContainer->get(parent, x ); + if ( mem != NULL ) { + el = *(daeElementRef*)mem; + } + if ( el == NULL ) { + continue; + } + if ( el->removeChildElement( child ) ) { + //check if there are any more children in this group. If not remove the group container element too. + daeElementRefArray array; + getChildren( parent, array ); + if ( array.getCount() == 0 ) + { + _elementContainer->removeElement( parent, el ); + } + return true; + } + } + return false; +} + +daeMetaElement * daeMetaGroup::findChild( daeString elementName ) { + if ( strcmp( _elementContainer->getName(), elementName ) == 0 ) { + return _elementContainer->getElementType(); + } + return _elementContainer->_elementType->getCMRoot()->findChild( elementName ); +} + +void daeMetaGroup::getChildren( daeElement *parent, daeElementRefArray &array ) { + size_t cnt = _elementContainer->getCount( parent ); + for ( size_t x = 0; x < cnt; x++ ) { + (*((daeElementRef*)_elementContainer->get(parent, (daeInt)x )))->getChildren( array ); + /*daeElementRef el = (*((daeElementRef*)_elementContainer->get(parent, (daeInt)x ))); + size_t cnt2 = _children.getCount(); + for ( size_t i = 0; i < cnt2; i++ ) { + _children[i]->getChildren( el, array ); + }*/ + } + //_elementContainer->_elementType->getChildren( parent, array ); +} + diff --git a/Engine/lib/collada/src/dae/daeMetaSequence.cpp b/Engine/lib/collada/src/dae/daeMetaSequence.cpp new file mode 100644 index 000000000..f338b2118 --- /dev/null +++ b/Engine/lib/collada/src/dae/daeMetaSequence.cpp @@ -0,0 +1,72 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include + +daeMetaSequence::daeMetaSequence( daeMetaElement *container, daeMetaCMPolicy *parent, daeUInt ordinal, + daeInt minO, daeInt maxO) : + daeMetaCMPolicy( container, parent, ordinal, minO, maxO ) +{} + +daeMetaSequence::~daeMetaSequence() +{} + +daeElement *daeMetaSequence::placeElement( daeElement *parent, daeElement *child, daeUInt &ordinal, daeInt offset, daeElement* before, daeElement *after ) { + (void)offset; + if ( _maxOccurs == -1 ) { + //Needed to prevent infinate loops. If unbounded check to see if you have the child before just trying to place + if ( findChild( child->getElementName() ) == NULL ) { + return NULL; + } + } + + size_t cnt = _children.getCount(); + for ( daeInt i = 0; ( i < _maxOccurs || _maxOccurs == -1 ); i++ ) { + for ( size_t x = 0; x < cnt; x++ ) { + if ( _children[x]->placeElement( parent, child, ordinal, i, before, after ) != NULL ) { + ordinal = ordinal + (i * ( _maxOrdinal + 1 )) + _ordinalOffset; + return child; + } + } + } + return NULL; +} + +daeBool daeMetaSequence::removeElement( daeElement *parent, daeElement *child ) { + size_t cnt = _children.getCount(); + for ( size_t x = 0; x < cnt; x++ ) { + if ( _children[x]->removeElement( parent, child ) ) { + return true; + } + } + return false; +} + +daeMetaElement * daeMetaSequence::findChild( daeString elementName ) { + daeMetaElement *me = NULL; + size_t cnt = _children.getCount(); + for ( size_t x = 0; x < cnt; x++ ) { + me = _children[x]->findChild( elementName ); + if ( me != NULL ) { + return me; + } + } + return NULL; +} + +void daeMetaSequence::getChildren( daeElement *parent, daeElementRefArray &array ) { + size_t cnt = _children.getCount(); + for ( size_t x = 0; x < cnt; x++ ) { + _children[x]->getChildren( parent, array ); + } +} diff --git a/Engine/lib/collada/src/dae/daeRawResolver.cpp b/Engine/lib/collada/src/dae/daeRawResolver.cpp new file mode 100644 index 000000000..f297a36f4 --- /dev/null +++ b/Engine/lib/collada/src/dae/daeRawResolver.cpp @@ -0,0 +1,129 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include + +using namespace std; + +daeRawResolver::daeRawResolver(DAE& dae) : daeURIResolver(dae) +{ +} + +daeRawResolver::~daeRawResolver() +{ +} + +daeString +daeRawResolver::getName() +{ + return "RawResolver"; +} + +daeElement* daeRawResolver::resolveElement(const daeURI& uri) { + if (cdom::tolower(uri.pathExt()).find(".raw") == string::npos) + return NULL; + + daeRawRefCache& cache = dae->getRawRefCache(); + if (daeElement* elt = cache.lookup(uri)) + return elt; + + string fileName = cdom::uriToNativePath(uri.str()); + if (fileName.empty()) + { + daeErrorHandler::get()->handleError( "daeRawResolver::resolveElement() - Can't get path from URI\n" ); + return NULL; + } + FILE *rawFile = fopen(fileName.c_str(), "rb"); + if (rawFile == NULL ) + return NULL; + long byteOffset = atoi( uri.getID() ); //get the fragment + + daeElement *src; + daeElement *array; + daeElement *accessor; + + accessor = uri.getContainer(); + if ( accessor == NULL ) + return NULL; + src = accessor->getParentElement()->getParentElement(); + daeElementRefArray children; + accessor->getChildren( children ); + bool hasInts = children[0]->getAttribute("type") == "int"; + + if ( hasInts ) + { + array = src->createAndPlace( "int_array" ); + } + else + { + array = src->createAndPlace( "float_array" ); + } + + daeULong *countPtr = (daeULong*)accessor->getAttributeValue( "count" ); + daeULong count = countPtr != NULL ? *countPtr : 0; + + daeULong *stridePtr = (daeULong*)accessor->getAttributeValue( "stride" ); + daeULong stride = stridePtr != NULL ? *stridePtr : 1; + + *(daeULong*)(array->getAttributeValue("count")) = count*stride; + array->setAttribute( "id", (src->getAttribute("id") + "-array").c_str() ); + + daeArray *valArray = (daeArray*)array->getValuePointer(); + valArray->setCount( (size_t)(count*stride) ); + + fseek( rawFile, byteOffset, SEEK_SET ); + if ( hasInts ) + { + daeInt val; + for ( unsigned int i = 0; i < count*stride; i++ ) + { + fread( &val, sizeof(daeInt), 1, rawFile ); + *(daeLong*)(valArray->getRaw(i)) = (daeLong)val; + } + } + else + { + daeFloat val; + for ( unsigned int i = 0; i < count*stride; i++ ) + { + fread( &val, sizeof(daeFloat), 1, rawFile ); + *(daeDouble*)(valArray->getRaw(i)) = (daeDouble)val; + } + } + + fclose(rawFile); + cache.add(uri, array); + return array; +} + + +daeElement* daeRawRefCache::lookup(const daeURI& uri) { + map::iterator iter = lookupTable.find(uri.str()); + return iter == lookupTable.end() ? NULL : iter->second; +} + +void daeRawRefCache::add(const daeURI& uri, daeElement* elt) { + lookupTable[uri.str()] = elt; +} + +void daeRawRefCache::remove(const daeURI& uri) { + lookupTable.erase(uri.str()); +} + +void daeRawRefCache::clear() { + lookupTable.clear(); +} diff --git a/Engine/lib/collada/src/dae/daeRefCountedObj.cpp b/Engine/lib/collada/src/dae/daeRefCountedObj.cpp new file mode 100644 index 000000000..45a9898bb --- /dev/null +++ b/Engine/lib/collada/src/dae/daeRefCountedObj.cpp @@ -0,0 +1,37 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include + +daeRefCountedObj::daeRefCountedObj() : _refCount(0) { } + +daeRefCountedObj::~daeRefCountedObj() { } + +void daeRefCountedObj::release() const { + if (--_refCount <= 0) + delete this; +} + +void daeRefCountedObj::ref() const { + _refCount++; +} + +void checkedRelease(const daeRefCountedObj* obj) { + if (obj) + obj->release(); +} + +void checkedRef(const daeRefCountedObj* obj) { + if (obj) + obj->ref(); +} diff --git a/Engine/lib/collada/src/dae/daeSIDResolver.cpp b/Engine/lib/collada/src/dae/daeSIDResolver.cpp new file mode 100644 index 000000000..036ba22af --- /dev/null +++ b/Engine/lib/collada/src/dae/daeSIDResolver.cpp @@ -0,0 +1,511 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + + +namespace { + template + T nextIter(const T& iter) { + T next = iter; + return ++next; + } + + template + T moveIter(const T& iter, int n) { + T result = iter; + advance(result, n); + return result; + } + + // Implements a breadth-first sid search by starting at the container element and + // traversing downward through the element tree. + daeElement* findSidTopDown(daeElement* container, const string& sid, const string& profile) { + if (!container) + return NULL; + + vector elts, matchingElts; + elts.push_back(container); + + for (size_t i = 0; i < elts.size(); i++) { + daeElement* elt = elts[i]; + + // Bail if we're looking for an element in a different profile + if (!profile.empty()) { + if (strcmp(elt->getElementName(), COLLADA_ELEMENT_TECHNIQUE_COMMON) == 0) + continue; + if (strcmp(elt->getElementName(), COLLADA_ELEMENT_TECHNIQUE) == 0 && + profile != elt->getAttribute("profile")) + continue; + } + + // See if this is a matching element + if (elt->getAttribute("sid") == sid) + return elt; + else { + // Add the children to the list of elements to check + daeElementRefArray children; + elt->getChildren(children); + for (size_t j = 0; j < children.getCount(); j++) + elts.push_back(children[j]); + } + } + + return NULL; + } + + // Returns the distance between an element and an ancestor of the element. If 'container + // isn't an ancestor of 'elt', or if 'elt' is in a profile that doesn't match 'profile' + // UINT_MAX is returned. + unsigned int computeDistance(daeElement* container, daeElement* elt, const string& profile) { + if (!container || !elt) + return UINT_MAX; + + unsigned int distance = 0; + do { + // Bail if we're looking for an element in a different profile + if (!profile.empty()) { + if (strcmp(elt->getElementName(), COLLADA_ELEMENT_TECHNIQUE_COMMON) == 0) + return UINT_MAX; + if (strcmp(elt->getElementName(), COLLADA_ELEMENT_TECHNIQUE) == 0 && + profile != elt->getAttribute("profile")) + return UINT_MAX; + } + + if (elt == container) + return distance; + distance++; + } while ((elt = elt->getParentElement()) != NULL); + + return UINT_MAX; + } + + // Implements a breadth-first sid search by using the database to find all elements + // matching 'sid', then finding the element closest to 'container'. + daeElement* findSidBottomUp(daeElement* container, const string& sid, const string& profile) { + if (!container || !container->getDocument()) + return NULL; + + // Get the elements with a matching sid + vector elts; + container->getDocument()->getDAE()->getDatabase()->sidLookup(sid, elts, container->getDocument()); + + // Compute the distance from each matching element to the container element + unsigned int minDistance = UINT_MAX; + daeElement* closestElt = NULL; + for (size_t i = 0; i < elts.size(); i++) { + unsigned int distance = computeDistance(container, elts[i], profile); + if (distance < minDistance) { + minDistance = distance; + closestElt = elts[i]; + } + } + + return closestElt; + } + + daeElement* findID(daeElement* elt, const string& id, const string& profile) { + return elt ? elt->getDAE()->getDatabase()->idLookup(id, elt->getDocument()) : NULL; + } + + void buildString(const list::iterator& begin, + const list::iterator& end, + string& result) { + ostringstream stream; + for (list::iterator iter = begin; iter != end; iter++) + stream << *iter; + result = stream.str(); + } + + // Finds an element with a matching ID or sid (depending on the 'finder' function) + // passed in. First it tries to resolve the whole ID/sid, then it tries to resolve + // successively smaller parts. For example, consider this sid ref: "my.sid.ref". + // First this function will try to resolve "my.sid.ref" entirely, then if that + // fails it'll try to resolve "my.sid.", "my.sid", "my.", and "my", in that order. + // The part that wasn't matched is returned in the 'remainingPart' parameter. + daeElement* findWithDots(daeElement* container, + const string& s, + const string& profile, + daeElement* (*finder)(daeElement*, const string&, const string&), + list& remainingPart) { + remainingPart.clear(); + + // First see if the whole thing resolves correctly + if (daeElement* result = finder(container, s, profile)) + return result; + + // It didn't resolve. Let's tokenize it by '.'s and see if we can resolve a + // portion of it. + cdom::tokenize(s, ".", remainingPart, true); + if (remainingPart.size() == 1) + return NULL; // There were no '.'s, so the result won't be any different + + list::iterator iter = moveIter(remainingPart.end(), -1); + for (int i = int(remainingPart.size())-1; i >= 1; i--, iter--) { + string substr; + buildString(remainingPart.begin(), iter, substr); + if (daeElement* result = finder(container, substr, profile)) { + // Remove the part we matched against from the list + remainingPart.erase(remainingPart.begin(), iter); + return result; + } + } + + remainingPart.clear(); + return NULL; + } + + daeSidRef::resolveData resolveImpl(const daeSidRef& sidRef) { + if (sidRef.sidRef.empty() || !sidRef.refElt) + return daeSidRef::resolveData(); + + daeSidRef::resolveData result; + string separators = "/()"; + list tokens; + cdom::tokenize(sidRef.sidRef, separators, /* out */ tokens, true); + + list::iterator tok = tokens.begin(); + + // The first token should be either an ID or a '.' to indicate + // that we should start the search from the container element. + if (tok == tokens.end()) + return daeSidRef::resolveData(); + + list remainingPart; + if (*tok == ".") { + result.elt = sidRef.refElt; + tok++; + } else { + // Try to resolve it as an ID + result.elt = findWithDots(sidRef.refElt, *tok, sidRef.profile, findID, remainingPart); + if (result.elt) { + if (!remainingPart.empty()) { + // Insert the "remaining part" from the ID resolve into our list of tokens + tokens.erase(tokens.begin()); + tokens.splice(tokens.begin(), remainingPart); + tok = tokens.begin(); + } else + tok++; + } + } + + if (!result.elt) + return daeSidRef::resolveData(); + + // Next we have an optional list of SIDs, each one separated by "/". Once we hit one of "()", + // we know we're done with the SID section. + for (; tok != tokens.end() && *tok == "/"; tok++) { + tok++; // Read the '/' + if (tok == tokens.end()) + return daeSidRef::resolveData(); + + // Find the element matching the SID + result.elt = findWithDots(result.elt, *tok, sidRef.profile, findSidTopDown, remainingPart); + if (!result.elt) + return daeSidRef::resolveData(); + + if (!remainingPart.empty()) { + list::iterator tmp = tok; + tok--; + tokens.splice(tmp, remainingPart); + tokens.erase(tmp); + } + } + + // Now we want to parse the member selection tokens. It can either be + // (a) '.' followed by a string representing the member to access + // (b) '(x)' where x is a number, optionally followed by another '(x)' + // Anything else is an error. + string member; + bool haveArrayIndex1 = false, haveArrayIndex2 = false; + int arrayIndex1 = -1, arrayIndex2 = -1; + if (tok != tokens.end()) { + if (*tok == ".") { + tok++; + if (tok == tokens.end()) + return daeSidRef::resolveData(); + member = *tok; + tok++; + } + else if (*tok == "(") { + tok++; + if (tok == tokens.end()) + return daeSidRef::resolveData(); + + istringstream stream(*tok); + stream >> arrayIndex1; + haveArrayIndex1 = true; + if (!stream.good() && !stream.eof()) + return daeSidRef::resolveData(); + tok++; + if (tok == tokens.end() || *tok != ")") + return daeSidRef::resolveData(); + tok++; + + if (tok != tokens.end() && *tok == "(") { + tok++; + if (tok == tokens.end()) + return daeSidRef::resolveData(); + + stream.clear(); + stream.str(*tok); + stream >> arrayIndex2; + haveArrayIndex2 = true; + if (!stream.good() && !stream.eof()) + return daeSidRef::resolveData(); + tok++; + if (tok == tokens.end() || *tok != ")") + return daeSidRef::resolveData(); + tok++; + } + } + } + + // We shouldn't have any tokens left. If we do it's an error. + if (tok != tokens.end()) + return daeSidRef::resolveData(); + + // At this point we've parsed a correctly formatted SID reference. The only thing left is to resolve + // the member selection portion of the SID ref. First, see if the resolved element has a float array we + // can use. + if (result.elt->typeID() == domSource::ID()) { + if (domFloat_array* floatArray = ((domSource*)result.elt)->getFloat_array()) + result.array = (daeDoubleArray*)floatArray->getCharDataObject()->get(floatArray); + } + else + { + daeMetaAttribute *ma = result.elt->getCharDataObject(); + if ( ma != NULL ) { + if ( ma->isArrayAttribute() && ma->getType()->getTypeEnum() == daeAtomicType::DoubleType ) { + result.array = (daeDoubleArray*)ma->get( result.elt ); + } + } + } + + if( result.array ) { + // We have an array to use for indexing. Let's see if the SID ref uses member selection. + if (!member.empty()) { + // Do member lookup based on the constants defined in the COMMON profile + if (member == "ANGLE") { + result.scalar = &(result.array->get(3)); + } else if (member.length() == 1) { + switch(member[0]) { + case 'X': + case 'R': + case 'U': + case 'S': + result.scalar = &(result.array->get(0)); + break; + case 'Y': + case 'G': + case 'V': + case 'T': + result.scalar = &(result.array->get(1)); + break; + case 'Z': + case 'B': + case 'P': + result.scalar = &(result.array->get(2)); + break; + case 'W': + case 'A': + case 'Q': + result.scalar = &(result.array->get(3)); + break; + }; + } + } else if (haveArrayIndex1) { + // Use the indices to lookup a value in the array + if (haveArrayIndex2 && result.array->getCount() == 16) { + // We're doing a matrix lookup. Make sure the index is valid. + int i = arrayIndex1*4 + arrayIndex2; + if (i >= 0 && i < int(result.array->getCount())) + result.scalar = &(result.array->get(i)); + } else { + // Vector lookup. Make sure the index is valid. + if (arrayIndex1 >= 0 && arrayIndex1 < int(result.array->getCount())) + result.scalar = &(result.array->get(arrayIndex1)); + } + } + } + + // If we tried to do member selection but we couldn't resolve it to a doublePtr, fail. + if ((!member.empty() || haveArrayIndex1) && result.scalar == NULL) + return daeSidRef::resolveData(); + + // SID resolution was successful. + return result; + } +} // namespace { + + +daeSidRef::resolveData::resolveData() : elt(NULL), array(NULL), scalar(NULL) { } + +daeSidRef::resolveData::resolveData(daeElement* elt, daeDoubleArray* array, daeDouble* scalar) + : elt(elt), + array(array), + scalar(scalar) { } + + +daeSidRef::daeSidRef() : refElt(NULL) { } + +daeSidRef::daeSidRef(const string& sidRef, daeElement* referenceElt, const string& profile) + : sidRef(sidRef), + refElt(referenceElt), + profile(profile) { } + +bool daeSidRef::operator<(const daeSidRef& other) const { + if (refElt != other.refElt) + return refElt < other.refElt; + if (sidRef != other.sidRef) + return sidRef < other.sidRef; + return profile < other.profile; +} + +daeSidRef::resolveData daeSidRef::resolve() { + if (!refElt) + return daeSidRef::resolveData(); + + // First check the cache + daeSidRef::resolveData result = refElt->getDAE()->getSidRefCache().lookup(*this); + if (result.elt) + return result; + + // Try to resolve as an effect-style sid ref by prepending "./" to the sid ref. + // If that fails, try resolving as an animation-style sid ref, where the first part is an ID. + result = resolveImpl(daeSidRef(string("./") + sidRef, refElt, profile)); + if (!result.elt) + result = resolveImpl(*this); + + if (result.elt) // Add the result to the cache + refElt->getDAE()->getSidRefCache().add(*this, result); + + return result; +} + + +daeSIDResolver::daeSIDResolver( daeElement *container, daeString target, daeString profile ) + : container(NULL) +{ + setContainer(container); + setTarget(target); + setProfile(profile); +} + +daeString daeSIDResolver::getTarget() const { + return target.empty() ? NULL : target.c_str(); +} + +void daeSIDResolver::setTarget( daeString t ) +{ + target = t ? t : ""; +} + +daeString daeSIDResolver::getProfile() const { + return profile.empty() ? NULL : profile.c_str(); +} + +void daeSIDResolver::setProfile( daeString p ) +{ + profile = p ? p : ""; +} + +daeElement* daeSIDResolver::getContainer() const { + return container; +} + +void daeSIDResolver::setContainer(daeElement* element) +{ + container = element; +} + +daeSIDResolver::ResolveState daeSIDResolver::getState() const { + if (target.empty()) + return target_empty; + + daeSidRef::resolveData result = daeSidRef(target, container, profile).resolve(); + if (!result.elt) + return sid_failed_not_found; + if (result.scalar) + return sid_success_double; + if (result.array) + return sid_success_array; + + return sid_success_element; +} + +daeElement* daeSIDResolver::getElement() +{ + return daeSidRef(target, container, profile).resolve().elt; +} + +daeDoubleArray *daeSIDResolver::getDoubleArray() +{ + return daeSidRef(target, container, profile).resolve().array; +} + +daeDouble *daeSIDResolver::getDouble() +{ + return daeSidRef(target, container, profile).resolve().scalar; +} + + +daeSidRefCache::daeSidRefCache() : hitCount(0), missCount(0) { } + +daeSidRef::resolveData daeSidRefCache::lookup(const daeSidRef& sidRef) { + map::iterator iter = lookupTable.find(sidRef); + if (iter != lookupTable.end()) { + hitCount++; + return iter->second; + } + missCount++; + return daeSidRef::resolveData(); +} + +void daeSidRefCache::add(const daeSidRef& sidRef, const daeSidRef::resolveData& result) { + lookupTable[sidRef] = result; +} + +void daeSidRefCache::clear() { + lookupTable.clear(); + hitCount = missCount = 0; +} + +bool daeSidRefCache::empty() { + return lookupTable.empty(); +} + +int daeSidRefCache::misses() { + return missCount; +} + +int daeSidRefCache::hits() { + return hitCount; +} diff --git a/Engine/lib/collada/src/dae/daeStandardURIResolver.cpp b/Engine/lib/collada/src/dae/daeStandardURIResolver.cpp new file mode 100644 index 000000000..356e890ea --- /dev/null +++ b/Engine/lib/collada/src/dae/daeStandardURIResolver.cpp @@ -0,0 +1,59 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +daeStandardURIResolver::daeStandardURIResolver(DAE& dae) + : daeURIResolver(dae) { } + +daeStandardURIResolver::~daeStandardURIResolver() { } + +daeString +daeStandardURIResolver::getName() +{ + return "XMLResolver"; +} + +namespace { + void printErrorMsg(const daeURI& uri) { + ostringstream msg; + msg << "daeStandardURIResolver::resolveElement() - Failed to resolve " << uri.str() << endl; + daeErrorHandler::get()->handleError(msg.str().c_str()); + } +} + +daeElement* daeStandardURIResolver::resolveElement(const daeURI& uri) { + daeDocument* doc = uri.getReferencedDocument(); + if (!doc) { + dae->open(uri.str()); + doc = uri.getReferencedDocument(); + if (!doc) { + printErrorMsg(uri); + return NULL; + } + } + + daeElement* elt = dae->getDatabase()->idLookup(uri.id(), doc); + if (!elt) + printErrorMsg(uri); + + return elt; +} diff --git a/Engine/lib/collada/src/dae/daeStringRef.cpp b/Engine/lib/collada/src/dae/daeStringRef.cpp new file mode 100644 index 000000000..bcb1d8a78 --- /dev/null +++ b/Engine/lib/collada/src/dae/daeStringRef.cpp @@ -0,0 +1,58 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include + +//Contributed by Nus - Wed, 08 Nov 2006 +// Nus: Use global pointer instead of local static. +static daeStringTable *pST = NULL; +//--------------------------- + +daeStringTable &daeStringRef::_stringTable() +{ +//Contributed by Nus - Wed, 08 Nov 2006 + // static daeStringTable *st = new daeStringTable(); + // return *st; + if(!pST) + pST = new daeStringTable(); + return *pST; +} + +void daeStringRef::releaseStringTable(void) +{ + if(pST) { + delete pST; + pST = NULL; + } +} +//-------------------------------- + +daeStringRef::daeStringRef(daeString string) +{ + daeStringTable &st = _stringTable(); + _string = st.allocString(string); +} + +const daeStringRef& +daeStringRef::set(daeString string) +{ + daeStringTable &st = _stringTable(); + _string = st.allocString(string); + return *this; +} + +const daeStringRef& +daeStringRef::operator= (daeString string) +{ + return set(string); +} diff --git a/Engine/lib/collada/src/dae/daeStringTable.cpp b/Engine/lib/collada/src/dae/daeStringTable.cpp new file mode 100644 index 000000000..fe7c53d12 --- /dev/null +++ b/Engine/lib/collada/src/dae/daeStringTable.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include + +daeStringTable::daeStringTable(int stringBufferSize):_stringBufferSize(stringBufferSize), _empty( "" ) +{ + _stringBufferIndex = _stringBufferSize; + //allocate initial buffer + //allocateBuffer(); +} + +daeString daeStringTable::allocateBuffer() +{ + daeString buf = new daeChar[_stringBufferSize]; + _stringBuffersList.append(buf); + _stringBufferIndex = 0; + return buf; +} + +daeString daeStringTable::allocString(daeString string) +{ + if ( string == NULL ) return _empty; + size_t stringSize = strlen(string) + 1; + size_t sizeLeft = _stringBufferSize - _stringBufferIndex; + daeString buf; + if (sizeLeft < stringSize) + { + if (stringSize > _stringBufferSize) + _stringBufferSize = ((stringSize / _stringBufferSize) + 1) * _stringBufferSize ; + buf = allocateBuffer(); + } + else + { + buf = _stringBuffersList.get((daeInt)_stringBuffersList.getCount()-1); + } + daeChar *str = (char*)buf + _stringBufferIndex; + memcpy(str,string,stringSize); + _stringBufferIndex += stringSize; + + int align = sizeof(void*); + _stringBufferIndex = (_stringBufferIndex+(align-1)) & (~(align-1)); + + return str; +} + +void daeStringTable::clear() +{ + unsigned int i; + for (i=0;i<_stringBuffersList.getCount();i++) +#if _MSC_VER <= 1200 + delete [] (char *) _stringBuffersList[i]; +#else + delete [] _stringBuffersList[i]; +#endif + + _stringBuffersList.clear(); + _stringBufferIndex = _stringBufferSize; +} diff --git a/Engine/lib/collada/src/dae/daeTinyXMLPlugin.cpp b/Engine/lib/collada/src/dae/daeTinyXMLPlugin.cpp new file mode 100644 index 000000000..c8bd37a8b --- /dev/null +++ b/Engine/lib/collada/src/dae/daeTinyXMLPlugin.cpp @@ -0,0 +1,230 @@ +/* + * Copyright 2007 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +// The user can choose whether or not to include TinyXML support in the DOM. Supporting TinyXML will +// require linking against it. By default TinyXML support isn't included. +#if defined(DOM_INCLUDE_TINYXML) + +#if defined(DOM_DYNAMIC) && defined(_MSC_VER) +#pragma comment(lib, "tinyxml.lib") +#endif + +#if defined(_MSC_VER) +#pragma warning(disable: 4100) // warning C4100: 'element' : unreferenced formal parameter +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +namespace { + daeInt getCurrentLineNumber(TiXmlElement* element) { + return -1; + } +} + +daeTinyXMLPlugin::daeTinyXMLPlugin() +{ + m_doc = NULL; + supportedProtocols.push_back("*"); +} + +daeTinyXMLPlugin::~daeTinyXMLPlugin() +{ +} + +daeInt daeTinyXMLPlugin::setOption( daeString option, daeString value ) +{ + return DAE_ERR_INVALID_CALL; +} + +daeString daeTinyXMLPlugin::getOption( daeString option ) +{ + return NULL; +} + +daeElementRef daeTinyXMLPlugin::readFromFile(const daeURI& uri) { + string file = cdom::uriToNativePath(uri.str()); + if (file.empty()) + return NULL; + TiXmlDocument doc; + doc.LoadFile(file.c_str()); + if (!doc.RootElement()) { + daeErrorHandler::get()->handleError((std::string("Failed to open ") + uri.str() + + " in daeTinyXMLPlugin::readFromFile\n").c_str()); + return NULL; + } + return readElement(doc.RootElement(), NULL); +} + +daeElementRef daeTinyXMLPlugin::readFromMemory(daeString buffer, const daeURI& baseUri) { + TiXmlDocument doc; + doc.Parse(buffer); + if (!doc.RootElement()) { + daeErrorHandler::get()->handleError("Failed to open XML document from memory buffer in " + "daeTinyXMLPlugin::readFromMemory\n"); + return NULL; + } + return readElement(doc.RootElement(), NULL); +} + +daeElementRef daeTinyXMLPlugin::readElement(TiXmlElement* tinyXmlElement, daeElement* parentElement) { + std::vector attributes; + for (TiXmlAttribute* attrib = tinyXmlElement->FirstAttribute(); attrib != NULL; attrib = attrib->Next()) + attributes.push_back(attrPair(attrib->Name(), attrib->Value())); + + daeElementRef element = beginReadElement(parentElement, tinyXmlElement->Value(), + attributes, getCurrentLineNumber(tinyXmlElement)); + if (!element) { + // We couldn't create the element. beginReadElement already printed an error message. + return NULL; + } + + if (tinyXmlElement->GetText() != NULL) + readElementText(element, tinyXmlElement->GetText(), getCurrentLineNumber(tinyXmlElement)); + + // Recurse children + for (TiXmlElement* child = tinyXmlElement->FirstChildElement(); child != NULL; child = child->NextSiblingElement()) + element->placeElement(readElement(child, element)); + + return element; +} + +daeInt daeTinyXMLPlugin::write(const daeURI& name, daeDocument *document, daeBool replace) +{ + // Make sure database and document are both set + if (!database) + return DAE_ERR_INVALID_CALL; + if(!document) + return DAE_ERR_COLLECTION_DOES_NOT_EXIST; + + string fileName = cdom::uriToNativePath(name.str()); + if (fileName.empty()) + { + daeErrorHandler::get()->handleError( "can't get path in write\n" ); + return DAE_ERR_BACKEND_IO; + } + // If replace=false, don't replace existing files + if(!replace) + { + // Using "stat" would be better, but it's not available on all platforms + FILE *tempfd = fopen(fileName.c_str(), "r"); + if(tempfd != NULL) + { + // File exists, return error + fclose(tempfd); + return DAE_ERR_BACKEND_FILE_EXISTS; + } + fclose(tempfd); + } + + m_doc = new TiXmlDocument(name.getURI()); + if (m_doc) + { + m_doc->SetTabSize(4); + + TiXmlDeclaration* decl = new TiXmlDeclaration( "1.0", "", "" ); + m_doc->LinkEndChild( decl ); + + writeElement(document->getDomRoot()); + + m_doc->SaveFile(fileName.c_str()); + delete m_doc; + m_doc = NULL; + } + return DAE_OK; +} + +void daeTinyXMLPlugin::writeElement( daeElement* element ) +{ + daeMetaElement* _meta = element->getMeta(); + if (!_meta->getIsTransparent() ) + { + TiXmlElement* tiElm = new TiXmlElement( element->getElementName() ); + + if (m_elements.empty() == true) { + m_doc->LinkEndChild(tiElm); + } else { + TiXmlElement* first = m_elements.front(); + first->LinkEndChild(tiElm); + } + m_elements.push_front(tiElm); + + daeMetaAttributeRefArray& attrs = _meta->getMetaAttributes(); + + int acnt = (int)attrs.getCount(); + + for(int i=0;igetChildren( children ); + for ( size_t x = 0; x < children.getCount(); x++ ) + { + writeElement( children.get(x) ); + } + + if (!_meta->getIsTransparent() ) + { + m_elements.pop_front(); + } +} + + +void daeTinyXMLPlugin::writeValue( daeElement* element ) +{ + if (daeMetaAttribute* attr = element->getMeta()->getValueAttribute()) { + std::ostringstream buffer; + attr->memoryToString(element, buffer); + std::string s = buffer.str(); + if (!s.empty()) + m_elements.front()->LinkEndChild( new TiXmlText(buffer.str().c_str()) ); + } +} + +void daeTinyXMLPlugin::writeAttribute( daeMetaAttribute* attr, daeElement* element ) +{ + //don't write if !required and is set && is default + if ( !attr->getIsRequired() ) { + //not required + if ( !element->isAttributeSet( attr->getName() ) ) { + //early out if !value && !required && !set + return; + } + + //is set + //check for default suppression + if (attr->compareToDefault(element) == 0) { + // We match the default value, so exit early + return; + } + } + + std::ostringstream buffer; + attr->memoryToString(element, buffer); + m_elements.front()->SetAttribute(attr->getName(), buffer.str().c_str()); +} + +#endif // DOM_INCLUDE_TINYXML diff --git a/Engine/lib/collada/src/dae/daeURI.cpp b/Engine/lib/collada/src/dae/daeURI.cpp new file mode 100644 index 000000000..780a41967 --- /dev/null +++ b/Engine/lib/collada/src/dae/daeURI.cpp @@ -0,0 +1,825 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace cdom; + +void daeURI::initialize() { + reset(); + container = NULL; +} + +daeURI::~daeURI() { } + +daeURI::daeURI(DAE& dae) : dae(&dae) { + initialize(); +} + +daeURI::daeURI(DAE& dae, const string& uriStr, daeBool nofrag) : dae(&dae) { + initialize(); + + if (nofrag) { + size_t pos = uriStr.find_last_of('#'); + if (pos != string::npos) { + set(uriStr.substr(0, pos)); + return; + } + } + + set(uriStr); +} + +daeURI::daeURI(const daeURI& baseURI, const string& uriStr) : dae(baseURI.getDAE()) +{ + initialize(); + set(uriStr, &baseURI); +} + +daeURI::daeURI(const daeURI& copyFrom_) : dae(copyFrom_.getDAE()), container(NULL) +{ + initialize(); + copyFrom(copyFrom_); +} + +daeURI::daeURI(daeElement& container_, const std::string& uriStr) + : dae(container_.getDAE()) +{ + initialize(); + container = &container_; + set(uriStr); +} + +daeURI::daeURI(DAE& dae, daeElement& container_, const string& uriStr) + : dae(&dae) +{ + initialize(); + container = &container_; + set(uriStr); +} + +void +daeURI::copyFrom(const daeURI& copyFrom) +{ + if (!container) + container = copyFrom.container; + set(copyFrom.originalStr()); +} + +daeURI& daeURI::operator=(const daeURI& other) { + copyFrom(other); + return *this; +} + +daeURI& daeURI::operator=(const string& uriStr) { + set(uriStr); + return *this; +} + +void daeURI::reset() { + // Clear everything except the container, which doesn't change for the lifetime of the daeURI + uriString = ""; + originalURIString = ""; + _scheme = ""; + _authority = ""; + _path = ""; + _query = ""; + _fragment = ""; +} + +DAE* daeURI::getDAE() const { + return dae; +} + + +const string& daeURI::str() const { + return uriString; +} + +const string& daeURI::originalStr() const { + return originalURIString; +} + +daeString daeURI::getURI() const { + return str().c_str(); +} + +daeString daeURI::getOriginalURI() const { + return originalStr().c_str(); +} + + +namespace { + void parsePath(const string& path, + /* out */ string& dir, + /* out */ string& baseName, + /* out */ string& extension) { + // !!!steveT Currently, if we have a file name that begins with a '.', as in + // ".emacs", that will be treated as having no base name with an extension + // of ".emacs". We might want to change this behavior, so that the base name + // is considered ".emacs" and the extension is empty. I think this is more + // in line with what path parsers in other libraries/languages do, and it + // more accurately reflects the intended structure of the file name. + static pcrecpp::RE re("(.*/)?([^.]*)?(\\..*)?"); + dir = baseName = extension = ""; + re.FullMatch(path, &dir, &baseName, &extension); + } +} + +void daeURI::set(const string& uriStr_, const daeURI* baseURI) { + // We make a copy of the uriStr so that set(originalURIString, ...) works properly. + string uriStr = uriStr_; + reset(); + originalURIString = uriStr; + + if (!parseUriRef(uriStr, _scheme, _authority, _path, _query, _fragment)) { + reset(); + return; + } + + validate(baseURI); +} + +void daeURI::set(const string& scheme_, + const string& authority_, + const string& path_, + const string& query_, + const string& fragment_, + const daeURI* baseURI) +{ + set(assembleUri(scheme_, authority_, path_, query_, fragment_), baseURI); +} + +void daeURI::setURI(daeString _URIString, const daeURI* baseURI) { + string uriStr = _URIString ? _URIString : ""; + set(uriStr, baseURI); +} + + +const string& daeURI::scheme() const { return _scheme; } +const string& daeURI::authority() const { return _authority; } +const string& daeURI::path() const { return _path; } +const string& daeURI::query() const { return _query; } +const string& daeURI::fragment() const { return _fragment; } +const string& daeURI::id() const { return fragment(); } + + +namespace { + string addSlashToEnd(const string& s) { + return (!s.empty() && s[s.length()-1] != '/') ? s + '/' : s; + } +} + +void daeURI::pathComponents(string& dir, string& baseName, string& ext) const { + parsePath(_path, dir, baseName, ext); +} + +string daeURI::pathDir() const { + string dir, base, ext; + parsePath(_path, dir, base, ext); + return dir; +} + +string daeURI::pathFileBase() const { + string dir, base, ext; + parsePath(_path, dir, base, ext); + return base; +} + +string daeURI::pathExt() const { + string dir, base, ext; + parsePath(_path, dir, base, ext); + return ext; +} + +string daeURI::pathFile() const { + string dir, base, ext; + parsePath(_path, dir, base, ext); + return base + ext; +} + +void daeURI::path(const string& dir, const string& baseName, const string& ext) { + path(addSlashToEnd(dir) + baseName + ext); +} + +void daeURI::pathDir(const string& dir) { + string tmp, base, ext; + parsePath(_path, tmp, base, ext); + path(addSlashToEnd(dir), base, ext); +} + +void daeURI::pathFileBase(const string& baseName) { + string dir, tmp, ext; + parsePath(_path, dir, tmp, ext); + path(dir, baseName, ext); +} + +void daeURI::pathExt(const string& ext) { + string dir, base, tmp; + parsePath(_path, dir, base, tmp); + path(dir, base, ext); +} + +void daeURI::pathFile(const string& file) { + string dir, base, ext; + parsePath(_path, dir, base, ext); + path(dir, file, ""); +} + + +daeString daeURI::getScheme() const { return _scheme.c_str(); } +daeString daeURI::getProtocol() const { return getScheme(); } +daeString daeURI::getAuthority() const { return _authority.c_str(); } +daeString daeURI::getPath() const { return _path.c_str(); } +daeString daeURI::getQuery() const { return _query.c_str(); } +daeString daeURI::getFragment() const { return _fragment.c_str(); } +daeString daeURI::getID() const { return getFragment(); } +daeBool daeURI::getPath(daeChar *dest, daeInt size) const { + if (int(_path.length()) < size) { + strcpy(dest, _path.c_str()); + return true; + } + return false; +} + + +void daeURI::scheme(const string& scheme_) { set(scheme_, _authority, _path, _query, _fragment); }; +void daeURI::authority(const string& authority_) { set(_scheme, authority_, _path, _query, _fragment); } +void daeURI::path(const string& path_) { set(_scheme, _authority, path_, _query, _fragment); } +void daeURI::query(const string& query_) { set(_scheme, _authority, _path, query_, _fragment); } +void daeURI::fragment(const string& fragment_) { set(_scheme, _authority, _path, _query, fragment_); } +void daeURI::id(const string& id) { fragment(id); } + +void +daeURI::print() +{ + fprintf(stderr,"URI(%s)\n",uriString.c_str()); + fprintf(stderr,"scheme = %s\n",_scheme.c_str()); + fprintf(stderr,"authority = %s\n",_authority.c_str()); + fprintf(stderr,"path = %s\n",_path.c_str()); + fprintf(stderr,"query = %s\n",_query.c_str()); + fprintf(stderr,"fragment = %s\n",_fragment.c_str()); + fprintf(stderr,"URI without base = %s\n",originalURIString.c_str()); + fflush(stderr); +} + +namespace { + void normalize(string& path) { + daeURI::normalizeURIPath(const_cast(path.c_str())); + path = path.substr(0, strlen(path.c_str())); + } +} + +void +daeURI::validate(const daeURI* baseURI) +{ + // If no base URI was supplied, use the container's document URI. If there's + // no container or the container doesn't have a doc URI, use the application + // base URI. + if (!baseURI) { + if (!container || !(baseURI = container->getDocumentURI())) + baseURI = &dae->getBaseURI(); + if (this == baseURI) + return; + } + + // This is rewritten according to the updated rfc 3986 + if (!_scheme.empty()) // if defined(R.scheme) then + { + // Everything stays the same except path which we normalize + // T.scheme = R.scheme; + // T.authority = R.authority; + // T.path = remove_dot_segments(R.path); + // T.query = R.query; + normalize(_path); + } + else + { + if (!_authority.empty()) // if defined(R.authority) then + { + // Authority and query stay the same, path is normalized + // T.authority = R.authority; + // T.path = remove_dot_segments(R.path); + // T.query = R.query; + normalize(_path); + } + else + { + if (_path.empty()) // if (R.path == "") then + { + // T.path = Base.path; + _path = baseURI->_path; + + //if defined(R.query) then + // T.query = R.query; + //else + // T.query = Base.query; + //endif; + if (_query.empty()) + _query = baseURI->_query; + } + else + { + if (_path[0] == '/') // if (R.path starts-with "/") then + { + // T.path = remove_dot_segments(R.path); + normalize(_path); + } + else + { + // T.path = merge(Base.path, R.path); + if (!baseURI->_authority.empty() && baseURI->_path.empty()) // authority defined, path empty + _path.insert(0, "/"); + else { + string dir, baseName, ext; + parsePath(baseURI->_path, dir, baseName, ext); + _path = dir + _path; + } + // T.path = remove_dot_segments(T.path); + normalize(_path); + } + // T.query = R.query; + } + // T.authority = Base.authority; + _authority = baseURI->_authority; + } + // T.scheme = Base.scheme; + _scheme = baseURI->_scheme; + } + // T.fragment = R.fragment; + + // Reassemble all this into a string version of the URI + uriString = assembleUri(_scheme, _authority, _path, _query, _fragment); + + // Collect external references + if (isExternalReference()) + container->getDocument()->addExternalReference( *this ); +} + +daeElementRef daeURI::getElement() const { + return internalResolveElement(); +} + +daeElement* daeURI::internalResolveElement() const { + if (uriString.empty()) + return NULL; + + return dae->getURIResolvers().resolveElement(*this); +} + +void daeURI::resolveElement() { } + +void daeURI::setContainer(daeElement* cont) { + container = cont; + // Since we have a new container element, the base URI may have changed. Re-resolve. + set(originalURIString); +} + +daeBool daeURI::isExternalReference() const { + if (uriString.empty()) + return false; + + if (container && container->getDocumentURI()) { + daeURI* docURI = container->getDocumentURI(); + if (_path != docURI->_path || + _scheme != docURI->_scheme || + _authority != docURI->_authority) { + return true; + } + } + + return false; +} + + +daeDocument* daeURI::getReferencedDocument() const { + string doc = assembleUri(_scheme, _authority, _path, "", ""); + return dae->getDatabase()->getDocument(doc.c_str(), true); +} + +daeURI::ResolveState daeURI::getState() const { + return uriString.empty() ? uri_empty : uri_loaded; +} + +void daeURI::setState(ResolveState newState) { } + + +// This code is loosely based on the RFC 2396 normalization code from +// libXML. Specifically it does the RFC steps 6.c->6.g from section 5.2 +// The path is modified in place, there is no error return. +void daeURI::normalizeURIPath(char* path) +{ + char *cur, // location we are currently processing + *out; // Everything from this back we are done with + + // Return if the path pointer is null + + if (path == NULL) return; + + // Skip any initial / characters to get us to the start of the first segment + + for(cur=path; *cur == '/'; cur++); + + // Return if we hit the end of the string + + if (*cur == 0) return; + + // Keep everything we've seen so far. + + out = cur; + + // Analyze each segment in sequence for cases (c) and (d). + + while (*cur != 0) + { + // (c) All occurrences of "./", where "." is a complete path segment, are removed from the buffer string. + + if ((*cur == '.') && (*(cur+1) == '/')) + { + cur += 2; + // If there were multiple slashes, skip them too + while (*cur == '/') cur++; + continue; + } + + // (d) If the buffer string ends with "." as a complete path segment, that "." is removed. + + if ((*cur == '.') && (*(cur+1) == 0)) + break; + + // If we passed the above tests copy the segment to the output side + + while (*cur != '/' && *cur != 0) + { + *(out++) = *(cur++); + } + + if(*cur != 0) + { + // Skip any occurrances of // at the end of the segment + + while ((*cur == '/') && (*(cur+1) == '/')) cur++; + + // Bring the last character in the segment (/ or a null terminator) into the output + + *(out++) = *(cur++); + } + } + + *out = 0; + + // Restart at the beginning of the first segment for the next part + + for(cur=path; *cur == '/'; cur++); + if (*cur == 0) return; + + // Analyze each segment in sequence for cases (e) and (f). + // + // e) All occurrences of "/../", where is a + // complete path segment not equal to "..", are removed from the + // buffer string. Removal of these path segments is performed + // iteratively, removing the leftmost matching pattern on each + // iteration, until no matching pattern remains. + // + // f) If the buffer string ends with "/..", where + // is a complete path segment not equal to "..", that + // "/.." is removed. + // + // To satisfy the "iterative" clause in (e), we need to collapse the + // string every time we find something that needs to be removed. Thus, + // we don't need to keep two pointers into the string: we only need a + // "current position" pointer. + // + while (true) + { + char *segp, *tmp; + + // At the beginning of each iteration of this loop, "cur" points to + // the first character of the segment we want to examine. + + // Find the end of the current segment. + + for(segp = cur;(*segp != '/') && (*segp != 0); ++segp); + + // If this is the last segment, we're done (we need at least two + // segments to meet the criteria for the (e) and (f) cases). + + if (*segp == 0) + break; + + // If the first segment is "..", or if the next segment _isn't_ "..", + // keep this segment and try the next one. + + ++segp; + if (((*cur == '.') && (cur[1] == '.') && (segp == cur+3)) + || ((*segp != '.') || (segp[1] != '.') + || ((segp[2] != '/') && (segp[2] != 0)))) + { + cur = segp; + continue; + } + + // If we get here, remove this segment and the next one and back up + // to the previous segment (if there is one), to implement the + // "iteratively" clause. It's pretty much impossible to back up + // while maintaining two pointers into the buffer, so just compact + // the whole buffer now. + + // If this is the end of the buffer, we're done. + + if (segp[2] == 0) + { + *cur = 0; + break; + } + + // Strings overlap during this copy, but not in a bad way, just avoid using strcpy + + tmp = cur; + segp += 3; + while ((*(tmp++) = *(segp++)) != 0); + + // If there are no previous segments, then keep going from here. + + segp = cur; + while ((segp > path) && (*(--segp) == '/')); + + if (segp == path) + continue; + + // "segp" is pointing to the end of a previous segment; find it's + // start. We need to back up to the previous segment and start + // over with that to handle things like "foo/bar/../..". If we + // don't do this, then on the first pass we'll remove the "bar/..", + // but be pointing at the second ".." so we won't realize we can also + // remove the "foo/..". + + for(cur = segp;(cur > path) && (*(cur-1) != '/'); cur--); + } + + *out = 0; + + // g) If the resulting buffer string still begins with one or more + // complete path segments of "..", then the reference is + // considered to be in error. Implementations may handle this + // error by retaining these components in the resolved path (i.e., + // treating them as part of the final URI), by removing them from + // the resolved path (i.e., discarding relative levels above the + // root), or by avoiding traversal of the reference. + // + // We discard them from the final path. + + if (*path == '/') + { + for(cur=path; (*cur == '/') && (cur[1] == '.') && (cur[2] == '.') && ((cur[3] == '/') || (cur[3] == 0)); cur += 3); + + if (cur != path) + { + for(out=path; *cur != 0; *(out++) = *(cur++)); + + *out = 0; + } + } + return; +} + +// This function will take a resolved URI and create a version of it that is relative to +// another existing URI. The new URI is stored in the "originalURI" +int daeURI::makeRelativeTo(const daeURI* relativeToURI) +{ + // Can only do this function if both URIs have the same scheme and authority + if (_scheme != relativeToURI->_scheme || _authority != relativeToURI->_authority) + return DAE_ERR_INVALID_CALL; + + // advance till we find a segment that doesn't match + const char *this_path = getPath(); + const char *relativeTo_path = relativeToURI->getPath(); + const char *this_slash = this_path; + const char *relativeTo_slash = relativeTo_path; + + while((*this_path == *relativeTo_path) && *this_path) + { + if(*this_path == '/') + { + this_slash = this_path; + relativeTo_slash = relativeTo_path; + } + this_path++; + relativeTo_path++; + } + + // Decide how many ../ segments are needed (Filepath should always end in a /) + int segment_count = 0; + relativeTo_slash++; + while(*relativeTo_slash != 0) + { + if(*relativeTo_slash == '/') + segment_count ++; + relativeTo_slash++; + } + this_slash++; + + string newPath; + for (int i = 0; i < segment_count; i++) + newPath += "../"; + newPath += this_slash; + + set("", "", newPath, _query, _fragment, relativeToURI); + return(DAE_OK); +} + + +daeBool daeURIResolver::_loadExternalDocuments = true; + +daeURIResolver::daeURIResolver(DAE& dae) : dae(&dae) { } + +daeURIResolver::~daeURIResolver() { } + +void daeURIResolver::setAutoLoadExternalDocuments( daeBool load ) +{ + _loadExternalDocuments = load; +} + +daeBool daeURIResolver::getAutoLoadExternalDocuments() +{ + return _loadExternalDocuments; +} + + +daeURIResolverList::daeURIResolverList() { } + +daeURIResolverList::~daeURIResolverList() { + for (size_t i = 0; i < resolvers.getCount(); i++) + delete resolvers[i]; +} + +daeTArray& daeURIResolverList::list() { + return resolvers; +} + +daeElement* daeURIResolverList::resolveElement(const daeURI& uri) { + for (size_t i = 0; i < resolvers.getCount(); i++) + if (daeElement* elt = resolvers[i]->resolveElement(uri)) + return elt; + return NULL; +} + + +// Returns true if parsing succeeded, false otherwise. Parsing can fail if the uri +// reference isn't properly formed. +bool cdom::parseUriRef(const string& uriRef, + string& scheme, + string& authority, + string& path, + string& query, + string& fragment) { + // This regular expression for parsing URI references comes from the URI spec: + // http://tools.ietf.org/html/rfc3986#appendix-B + static pcrecpp::RE re("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?"); + string s1, s3, s6, s8; + if (re.FullMatch(uriRef, &s1, &scheme, &s3, &authority, &path, &s6, &query, &s8, &fragment)) + return true; + + return false; +} + +namespace { + string safeSubstr(const string& s, size_t offset, size_t length) { + string result = s.substr(offset, min(length, s.length() - offset)); + result.resize(length, '\0'); + return result; + } +} + +string cdom::assembleUri(const string& scheme, + const string& authority, + const string& path, + const string& query, + const string& fragment, + bool forceLibxmlCompatible) { + string p = safeSubstr(path, 0, 3); + bool libxmlHack = forceLibxmlCompatible && scheme == "file"; + bool uncPath = false; + string uri; + + if (!scheme.empty()) + uri += scheme + ":"; + + if (!authority.empty() || libxmlHack || (p[0] == '/' && p[1] == '/')) + uri += "//"; + if (!authority.empty()) { + if (libxmlHack) { + // We have a UNC path URI of the form file://otherMachine/file.dae. + // Convert it to file://///otherMachine/file.dae, which is how libxml + // does UNC paths. + uri += "///" + authority; + uncPath = true; + } + else { + uri += authority; + } + } + + if (!uncPath && libxmlHack && getSystemType() == Windows) { + // We have to be delicate in how we pass absolute path URIs to libxml on Windows. + // If the path is an absolute path with no drive letter, add an extra slash to + // appease libxml. + if (p[0] == '/' && p[1] != '/' && p[2] != ':') { + uri += "/"; + } + } + uri += path; + + if (!query.empty()) + uri += "?" + query; + if (!fragment.empty()) + uri += "#" + fragment; + + return uri; +} + +string cdom::fixUriForLibxml(const string& uriRef) { + string scheme, authority, path, query, fragment; + cdom::parseUriRef(uriRef, scheme, authority, path, query, fragment); + return assembleUri(scheme, authority, path, query, fragment, true); +} + + +string cdom::nativePathToUri(const string& nativePath, systemType type) { + string uri = nativePath; + + if (type == Windows) { + // Convert "c:\" to "/c:/" + if (uri.length() >= 2 && isalpha(uri[0]) && uri[1] == ':') + uri.insert(0, "/"); + // Convert backslashes to forward slashes + uri = replace(uri, "\\", "/"); + } + + // Convert spaces to %20 + uri = replace(uri, " ", "%20"); + + return uri; +} + +string cdom::filePathToUri(const string& filePath) { + return nativePathToUri(filePath); +} + +string cdom::uriToNativePath(const string& uriRef, systemType type) { + string scheme, authority, path, query, fragment; + parseUriRef(uriRef, scheme, authority, path, query, fragment); + + // Make sure we have a file scheme URI, or that it doesn't have a scheme + if (!scheme.empty() && scheme != "file") + return ""; + + string filePath; + + if (type == Windows) { + if (!authority.empty()) + filePath += string("\\\\") + authority; // UNC path + + // Replace two leading slashes with one leading slash, so that + // ///otherComputer/file.dae becomes //otherComputer/file.dae and + // //folder/file.dae becomes /folder/file.dae + if (path.length() >= 2 && path[0] == '/' && path[1] == '/') + path.erase(0, 1); + + // Convert "/C:/" to "C:/" + if (path.length() >= 3 && path[0] == '/' && path[2] == ':') + path.erase(0, 1); + + // Convert forward slashes to back slashes + path = replace(path, "/", "\\"); + } + + filePath += path; + + // Replace %20 with space + filePath = replace(filePath, "%20", " "); + + return filePath; +} + +string cdom::uriToFilePath(const string& uriRef) { + return uriToNativePath(uriRef); +} diff --git a/Engine/lib/collada/src/dae/daeUtils.cpp b/Engine/lib/collada/src/dae/daeUtils.cpp new file mode 100644 index 000000000..2ceba1234 --- /dev/null +++ b/Engine/lib/collada/src/dae/daeUtils.cpp @@ -0,0 +1,126 @@ +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include // for getcwd (windows) +#else +#include // for getcwd (linux) +#endif + +using namespace std; + +cdom::systemType cdom::getSystemType() { +#ifdef WIN32 + return Windows; +#else + return Posix; +#endif +} + +string cdom::replace(const string& s, const string& replace, const string& replaceWith) { + if (replace.empty()) + return s; + + string result; + size_t pos1 = 0, pos2 = s.find(replace); + while (pos2 != string::npos) { + result += s.substr(pos1, pos2-pos1); + result += replaceWith; + pos1 = pos2 + replace.length(); + pos2 = s.find(replace, pos1); + } + + result += s.substr(pos1, s.length()-pos1); + return result; +} + +void cdom::tokenize(const string& s, + const string& separators, + /* out */ list& tokens, + bool separatorsInResult) { + size_t currentIndex = 0, nextTokenIndex = 0; + while (currentIndex < s.length() && + (nextTokenIndex = s.find_first_of(separators, currentIndex)) != string::npos) { + if ((nextTokenIndex - currentIndex) > 0) + tokens.push_back(s.substr(currentIndex, nextTokenIndex-currentIndex)); + if (separatorsInResult) + tokens.push_back(string(1, s[nextTokenIndex])); + currentIndex = nextTokenIndex+1; + } + + if (currentIndex < s.length()) + tokens.push_back(s.substr(currentIndex, s.length()-currentIndex)); +} + +list cdom::tokenize(const string& s, + const string& separators, + bool separatorsInResult) { + list result; + tokenize(s, separators, result, separatorsInResult); + return result; +} + +vector cdom::makeStringArray(const char* s, ...) { + va_list args; + va_start(args, s); + vector result; + while (s) { + result.push_back(s); + s = va_arg(args, const char*); + } + va_end(args); + return result; +} + +list cdom::makeStringList(const char* s, ...) { + va_list args; + va_start(args, s); + list result; + while (s) { + result.push_back(s); + s = va_arg(args, const char*); + } + va_end(args); + return result; +} + +string cdom::getCurrentDir() { +#if defined(__CELLOS_LV2__) || defined(_XBOX_VER) + // The PS3 has no getcwd call. + // !!!steveT Should we return app_home instead? + return "/"; +#else + char buffer[1024]; +#ifdef _WIN32 + _getcwd(buffer, 1024); +#else + getcwd(buffer, 1024); +#endif + return buffer; +#endif +} + +string cdom::getCurrentDirAsUri() { + string result = string("file://") + cdom::nativePathToUri(getCurrentDir()); + // Make sure the last char is a / + if (!result.empty() && result[result.length()-1] != '/') + result += "/"; + return result; +} + +int cdom::strcasecmp(const char* str1, const char* str2) { +#ifdef _MSC_VER + return _stricmp(str1, str2); +#else + return ::strcasecmp(str1, str2); +#endif +} + +string cdom::tolower(const string& s) { + string result; + transform(s.begin(), s.end(), back_inserter(result), ::tolower); + return result; +} diff --git a/Engine/lib/collada/src/dae/domAny.cpp b/Engine/lib/collada/src/dae/domAny.cpp new file mode 100644 index 000000000..793c5220b --- /dev/null +++ b/Engine/lib/collada/src/dae/domAny.cpp @@ -0,0 +1,111 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +daeElementRef +domAny::create(DAE& dae) +{ + domAnyRef ref = new domAny; + return ref; +} + + +daeMetaElement * +domAny::registerElement(DAE& dae) +{ + daeMetaElement *_Meta = new daeMetaElement(dae); + _Meta->setName( "any" ); + _Meta->registerClass(domAny::create); + _Meta->setIsInnerClass( true ); + + daeMetaCMPolicy *cm = NULL; + cm = new daeMetaSequence( _Meta, cm, 0, 1, 1 ); + + cm = new daeMetaAny( _Meta, cm, 0, 0, -1 ); + cm->getParent()->appendChild( cm ); + cm = cm->getParent(); + + cm->setMaxOrdinal( 0 ); + _Meta->setCMRoot( cm ); + _Meta->setAllowsAny( true ); + + _Meta->addContents(daeOffsetOf(domAny,_contents)); + _Meta->addContentsOrder(daeOffsetOf(domAny,_contentsOrder)); + + //VALUE + { + daeMetaAttribute *ma = new daeMetaAttribute; + ma->setName( "_value" ); + ma->setType( dae.getAtomicTypes().get("xsString")); + ma->setOffset( daeOffsetOf( domAny , _value )); + ma->setContainer( _Meta ); + _Meta->appendAttribute(ma); + } + + _Meta->setElementSize(sizeof(domAny)); + _Meta->validate(); + + return _Meta; +} + +domAny::~domAny() { + // domAny objects own their corresponding daeMetaElement + delete _meta; +} + +// Implementation of daeMetaAttribute that understands how domAny works +class domAnyAttribute : public daeMetaAttribute { +public: + virtual daeChar* getWritableMemory(daeElement* e) { + return (daeChar*)&((domAny*)e)->attrs[_offset]; + } +}; + +daeBool domAny::setAttribute(daeString attrName, daeString attrValue) { + if (_meta == NULL) + return false; + + //if the attribute already exists set it. + if (daeElement::setAttribute(attrName, attrValue)) + return true; + + //else register it and then set it. + attrs.append(""); + daeMetaAttribute *ma = new domAnyAttribute; + ma->setName( attrName ); + ma->setType( getDAE()->getAtomicTypes().get("xsString")); + ma->setOffset((daeInt)attrs.getCount()-1); + ma->setContainer( _meta ); + if (ma->getType()) { + _meta->appendAttribute(ma); + _validAttributeArray.append( true ); + ma->stringToMemory(this, attrValue); + return true; + } + + delete ma; + return false; +} + + diff --git a/Engine/lib/collada/src/modules/LIBXMLPlugin/daeLIBXMLPlugin.cpp b/Engine/lib/collada/src/modules/LIBXMLPlugin/daeLIBXMLPlugin.cpp new file mode 100644 index 000000000..99b1b8b65 --- /dev/null +++ b/Engine/lib/collada/src/modules/LIBXMLPlugin/daeLIBXMLPlugin.cpp @@ -0,0 +1,530 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +// The user can choose whether or not to include libxml support in the DOM. Supporting libxml will +// require linking against it. By default libxml support is included. +#if defined(DOM_INCLUDE_LIBXML) + +// This is a rework of the XML plugin that contains a complete interface to libxml2 "readXML" +// This is intended to be a seperate plugin but I'm starting out by rewriting it in daeLIBXMLPlugin +// because I'm not sure if all the plugin handling stuff has been tested. Once I get a working +// plugin I'll look into renaming it so the old daeLIBXMLPlugin can coexist with it. +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + + +// Some helper functions for working with libxml +namespace { + daeInt getCurrentLineNumber(xmlTextReaderPtr reader) { +#if LIBXML_VERSION >= 20620 + return xmlTextReaderGetParserLineNumber(reader); +#else + return -1; +#endif + } + + // Return value should be freed by caller with delete[]. Passed in value should not + // be null. + xmlChar* utf8ToLatin1(const xmlChar* utf8) { + int inLen = xmlStrlen(utf8); + int outLen = (inLen+1) * 2; + xmlChar* latin1 = new xmlChar[outLen]; + int numBytes = UTF8Toisolat1(latin1, &outLen, utf8, &inLen); + if (numBytes < 0) + // Failed. Return an empty string instead. + numBytes = 0; + + latin1[numBytes] = '\0'; + return latin1; + } + + // Return value should be freed by caller with delete[]. + xmlChar* latin1ToUtf8(const string& latin1) { + int inLen = (int)latin1.length(); + int outLen = (inLen+1) * 2; + xmlChar* utf8 = new xmlChar[outLen]; + int numBytes = isolat1ToUTF8(utf8, &outLen, (xmlChar*)latin1.c_str(), &inLen); + if (numBytes < 0) + // Failed. Return an empty string instead. + numBytes = 0; + + utf8[numBytes] = '\0'; + return utf8; + } + + typedef pair stringPair; + + // The attributes vector passed in should be empty. If 'encoding' is anything + // other than utf8 the caller should free the returned attribute value + // strings. The 'freeAttrValues' function is provided for that purpose. + void packageCurrentAttributes(xmlTextReaderPtr reader, + DAE::charEncoding encoding, + /* out */ vector& attributes) { + int numAttributes = xmlTextReaderAttributeCount(reader); + if (numAttributes == -1 || numAttributes == 0) + return; + attributes.reserve(numAttributes); + + while (xmlTextReaderMoveToNextAttribute(reader) == 1) { + const xmlChar* xmlName = xmlTextReaderConstName(reader); + const xmlChar* xmlValue = xmlTextReaderConstValue(reader); + if (encoding == DAE::Latin1) + attributes.push_back(stringPair((daeString)xmlName, (daeString)utf8ToLatin1(xmlValue))); + else + attributes.push_back(stringPair((daeString)xmlName, (daeString)xmlValue)); + } + } + + void freeAttrValues(vector& pairs) { + for(size_t i=0, size=pairs.size(); ihandleError((string("Failed to open ") + uri.str() + + " in daeLIBXMLPlugin::readFromFile\n").c_str()); + return NULL; + } + return read(readerHelper.reader); +} + +daeElementRef daeLIBXMLPlugin::readFromMemory(daeString buffer, const daeURI& baseUri) { + xmlTextReaderHelper readerHelper(buffer, baseUri); + if (!readerHelper.reader) { + daeErrorHandler::get()->handleError("Failed to open XML document from memory buffer in " + "daeLIBXMLPlugin::readFromMemory\n"); + return NULL; + } + return read(readerHelper.reader); +} + +daeElementRef daeLIBXMLPlugin::read(_xmlTextReader* reader) { + // Drop everything up to the first element. In the future, we should try to store header comments somewhere. + while(xmlTextReaderNodeType(reader) != XML_READER_TYPE_ELEMENT) + { + if (xmlTextReaderRead(reader) != 1) { + daeErrorHandler::get()->handleError("Error parsing XML in daeLIBXMLPlugin::read\n"); + return NULL; + } + } + + return readElement(reader, NULL); +} + +daeElementRef daeLIBXMLPlugin::readElement(_xmlTextReader* reader, daeElement* parentElement) { + assert(xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT); + daeString elementName = (daeString)xmlTextReaderConstName(reader); + bool empty = xmlTextReaderIsEmptyElement(reader) != 0; + + vector attributes; + packageCurrentAttributes(reader, dae.getCharEncoding(), /* out */ attributes); + + daeElementRef element = beginReadElement(parentElement, elementName, attributes, getCurrentLineNumber(reader)); + if (dae.getCharEncoding() != DAE::Utf8) + freeAttrValues(attributes); + + if (!element) { + // We couldn't create the element. beginReadElement already printed an error message. Just make sure + // to skip ahead past the bad element. + xmlTextReaderNext(reader); + return NULL; + } + + if (xmlTextReaderRead(reader) != 1 || empty) + return element; + + int nodeType = xmlTextReaderNodeType(reader); + while (nodeType != -1 && nodeType != XML_READER_TYPE_END_ELEMENT) { + if (nodeType == XML_READER_TYPE_ELEMENT) { + element->placeElement(readElement(reader, element)); + } + else if (nodeType == XML_READER_TYPE_TEXT) { + const xmlChar* xmlText = xmlTextReaderConstValue(reader); + if (dae.getCharEncoding() == DAE::Latin1) + xmlText = utf8ToLatin1(xmlText); + readElementText(element, (daeString)xmlText, getCurrentLineNumber(reader)); + if (dae.getCharEncoding() == DAE::Latin1) + delete[] xmlText; + + if (xmlTextReaderRead(reader) != 1) + return NULL; + } + else { + if (xmlTextReaderRead(reader) != 1) + return NULL; + } + + nodeType = xmlTextReaderNodeType(reader); + } + + if (nodeType == XML_READER_TYPE_END_ELEMENT) + xmlTextReaderRead(reader); + + return element; +} + +daeInt daeLIBXMLPlugin::write(const daeURI& name, daeDocument *document, daeBool replace) +{ + // Make sure database and document are both set + if (!database) + return DAE_ERR_INVALID_CALL; + if(!document) + return DAE_ERR_COLLECTION_DOES_NOT_EXIST; + + // Convert the URI to a file path, to see if we're about to overwrite a file + string file = cdom::uriToNativePath(name.str()); + if (file.empty() && saveRawFile) + { + daeErrorHandler::get()->handleError( "can't get path in write\n" ); + return DAE_ERR_BACKEND_IO; + } + + // If replace=false, don't replace existing files + if(!replace) + { + // Using "stat" would be better, but it's not available on all platforms + FILE *tempfd = fopen(file.c_str(), "r"); + if(tempfd != NULL) + { + // File exists, return error + fclose(tempfd); + return DAE_ERR_BACKEND_FILE_EXISTS; + } + fclose(tempfd); + } + if ( saveRawFile ) + { + string rawFilePath = file + ".raw"; + if ( !replace ) + { + rawFile = fopen(rawFilePath.c_str(), "rb" ); + if ( rawFile != NULL ) + { + fclose(rawFile); + return DAE_ERR_BACKEND_FILE_EXISTS; + } + fclose(rawFile); + } + rawFile = fopen(rawFilePath.c_str(), "wb"); + if ( rawFile == NULL ) + { + return DAE_ERR_BACKEND_IO; + } + rawRelPath.set(cdom::nativePathToUri(rawFilePath)); + rawRelPath.makeRelativeTo( &name ); + } + + // Open the file we will write to + writer = xmlNewTextWriterFilename(cdom::fixUriForLibxml(name.str()).c_str(), 0); + if ( !writer ) { + ostringstream msg; + msg << "daeLIBXMLPlugin::write(" << name.str() << ") failed\n"; + daeErrorHandler::get()->handleError(msg.str().c_str()); + return DAE_ERR_BACKEND_IO; + } + xmlTextWriterSetIndentString( writer, (const xmlChar*)"\t" ); // Don't change this to spaces + xmlTextWriterSetIndent( writer, 1 ); // Turns indentation on + xmlTextWriterStartDocument( writer, "1.0", "UTF-8", NULL ); + + writeElement( document->getDomRoot() ); + + xmlTextWriterEndDocument( writer ); + xmlTextWriterFlush( writer ); + xmlFreeTextWriter( writer ); + + if ( saveRawFile && rawFile != NULL ) + { + fclose( rawFile ); + } + + return DAE_OK; +} + +void daeLIBXMLPlugin::writeElement( daeElement* element ) +{ + daeMetaElement* _meta = element->getMeta(); + + //intercept elements for special handling + if ( saveRawFile ) + { + if ( strcmp( element->getTypeName(), "source" ) == 0 ) + { + daeElementRefArray children; + element->getChildren( children ); + bool validArray = false, teqCommon = false; + for ( unsigned int i = 0; i < children.getCount(); i++ ) + { + if ( strcmp( children[i]->getTypeName(), "float_array" ) == 0 || + strcmp( children[i]->getTypeName(), "int_array" ) == 0 ) + { + validArray = true; + } + else if ( strcmp( children[i]->getTypeName(), "technique_common" ) == 0 ) + { + teqCommon = true; + } + } + if ( validArray && teqCommon ) + { + writeRawSource( element ); + return; + } + } + } + + if (!_meta->getIsTransparent() ) { + xmlTextWriterStartElement(writer, (xmlChar*)element->getElementName()); + daeMetaAttributeRefArray& attrs = _meta->getMetaAttributes(); + + int acnt = (int)attrs.getCount(); + + for(int i=0;igetChildren( children ); + for ( size_t x = 0; x < children.getCount(); x++ ) { + writeElement( children.get(x) ); + } + + /*if (_meta->getContents() != NULL) { + daeElementRefArray* era = (daeElementRefArray*)_meta->getContents()->getWritableMemory(element); + int elemCnt = (int)era->getCount(); + for(int i = 0; i < elemCnt; i++) { + daeElementRef elem = (daeElementRef)era->get(i); + if (elem != NULL) { + writeElement( elem ); + } + } + } + else + { + daeMetaElementAttributeArray& children = _meta->getMetaElements(); + int cnt = (int)children.getCount(); + for(int i=0;igetElementType(); + if ( !type->getIsAbstract() ) { + for (int c = 0; c < children[i]->getCount(element); c++ ) { + writeElement( *(daeElementRef*)children[i]->get(element,c) ); + } + } + } + }*/ + if (!_meta->getIsTransparent() ) { + xmlTextWriterEndElement(writer); + } +} + +void daeLIBXMLPlugin::writeAttribute( daeMetaAttribute* attr, daeElement* element) +{ + //don't write if !required and is set && is default + if ( !attr->getIsRequired() ) { + //not required + if ( !element->isAttributeSet( attr->getName() ) ) { + //early out if !value && !required && !set + return; + } + + //is set + //check for default suppression + if (attr->compareToDefault(element) == 0) { + // We match the default value, so exit early + return; + } + } + + xmlTextWriterStartAttribute(writer, (xmlChar*)(daeString)attr->getName()); + ostringstream buffer; + attr->memoryToString(element, buffer); + string str = buffer.str(); + + xmlChar* utf8 = (xmlChar*)str.c_str(); + if (dae.getCharEncoding() == DAE::Latin1) + utf8 = latin1ToUtf8(str); + xmlTextWriterWriteString(writer, utf8); + if (dae.getCharEncoding() == DAE::Latin1) + delete[] utf8; + + xmlTextWriterEndAttribute(writer); +} + +void daeLIBXMLPlugin::writeValue(daeElement* element) { + if (daeMetaAttribute* attr = element->getMeta()->getValueAttribute()) { + ostringstream buffer; + attr->memoryToString(element, buffer); + string s = buffer.str(); + if (!s.empty()) { + xmlChar* str = (xmlChar*)s.c_str(); + if (dae.getCharEncoding() == DAE::Latin1) + str = latin1ToUtf8(s); + xmlTextWriterWriteString(writer, (xmlChar*)s.c_str()); + if (dae.getCharEncoding() == DAE::Latin1) + delete[] str; + } + } +} + +void daeLIBXMLPlugin::writeRawSource( daeElement *src ) +{ + daeElementRef newSrc = src->clone(); + daeElementRef array = NULL; + daeElement *accessor = NULL; + daeElementRefArray children; + newSrc->getChildren( children ); + bool isInt = false; + for ( int i = 0; i < (int)children.getCount(); i++ ) + { + if ( strcmp( children[i]->getTypeName(), "float_array" ) == 0 ) + { + array = children[i]; + newSrc->removeChildElement( array ); + } + else if ( strcmp( children[i]->getTypeName(), "int_array" ) == 0 ) + { + array = children[i]; + isInt = true; + newSrc->removeChildElement( array ); + } + else if ( strcmp( children[i]->getTypeName(), "technique_common" ) == 0 ) + { + children[i]->getChildren( children ); + } + else if ( strcmp( children[i]->getTypeName(), "accessor" ) == 0 ) + { + accessor = children[i]; + } + } + + daeULong *countPtr = (daeULong*)array->getAttributeValue( "count" ); + daeULong count = countPtr != NULL ? *countPtr : 0; + + daeULong *stridePtr = (daeULong*)accessor->getAttributeValue( "stride" ); + daeULong stride = stridePtr != NULL ? *stridePtr : 1; + + children.clear(); + accessor->getChildren( children ); + if ( children.getCount() > stride ) { + *stridePtr = children.getCount(); + } + + daeFixedName newURI; + sprintf( newURI, "%s#%ld", rawRelPath.getOriginalURI(), rawByteCount ); + accessor->setAttribute( "source", newURI ); + + daeArray *valArray = (daeArray*)array->getValuePointer(); + + //TODO: pay attention to precision for the array. + if ( isInt ) + { + for( size_t i = 0; i < count; i++ ) + { + daeInt tmp = (daeInt)*(daeLong*)(valArray->getRaw(i)); + rawByteCount += (unsigned long)(fwrite( &tmp, sizeof(daeInt), 1, rawFile ) * sizeof(daeInt)); + } + } + else + { + for( size_t i = 0; i < count; i++ ) + { + daeFloat tmp = (daeFloat)*(daeDouble*)(valArray->getRaw(i)); + rawByteCount += (unsigned long)(fwrite( &tmp, sizeof(daeFloat), 1, rawFile ) * sizeof(daeFloat)); + } + } + + writeElement( newSrc ); +} + +#endif // DOM_INCLUDE_LIBXML diff --git a/Engine/lib/collada/src/modules/STLDatabase/daeSTLDatabase.cpp b/Engine/lib/collada/src/modules/STLDatabase/daeSTLDatabase.cpp new file mode 100644 index 000000000..f5fb9bc06 --- /dev/null +++ b/Engine/lib/collada/src/modules/STLDatabase/daeSTLDatabase.cpp @@ -0,0 +1,685 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include + +using namespace std; + +daeSTLDatabase::daeSTLDatabase(DAE& dae) : daeDatabase(dae) +{ } + +daeSTLDatabase::~daeSTLDatabase() +{ + clear(); +} + +daeInt daeSTLDatabase::setMeta(daeMetaElement *_topMeta) +{ + topMeta = _topMeta; + return DAE_OK; +} + +daeBool +daeSTLDatabase::isDocumentLoaded(daeString name) +{ + daeDocument* document = getDocument(name); + if(document) + return(true); + else + return(false); +} + +// Element Types of all Elements +daeUInt daeSTLDatabase::getTypeCount() +{ + return (daeUInt)elements.size(); +} + + +daeString daeSTLDatabase::getTypeName(daeUInt index) +{ + daeUInt count = 0; + + map >::iterator iter = elements.begin(); + map >::iterator end = elements.end(); + while ( iter != end ) + { + if ( count == index ) + { + return (*iter).first.c_str(); + } + ++count; + ++iter; + } + + return NULL; +} + +// Documents +daeInt daeSTLDatabase::insertDocument(const char *name, daeElement* dom, daeDocument** document) +{ + return createDocument( name, dom, document ); +} +daeInt daeSTLDatabase::createDocument(const char *name, daeElement* dom, daeDocument** document) +{ + // If a document already exists with the same name, error + if(isDocumentLoaded(name)) + { + if (document) + *document = NULL; + return DAE_ERR_COLLECTION_ALREADY_EXISTS; + } + + // Make a new document + daeDocument *newDocument = new daeDocument(dae); + newDocument->getDocumentURI()->setURI(name); + newDocument->setDomRoot(dom); + // Push the connection into the database + documents.push_back(newDocument); + + if (document) + *document = newDocument; + + return DAE_OK; +} +// !!!GAC revised version of insertDocument, creates a domCollada and fills it in for you. +daeInt daeSTLDatabase::insertDocument(const char *name, daeDocument** document) +{ + return createDocument( name, document ); +} +daeInt daeSTLDatabase::createDocument(const char *name, daeDocument** document) +{ + + // If a document already exists with the same name, error + if(isDocumentLoaded(name)) + { + if (document) + *document = NULL; + return DAE_ERR_COLLECTION_ALREADY_EXISTS; + } + // Make the new document + daeDocument *newDocument = new daeDocument(dae); + // Make a domCOLLADA to be the root of this new document (this makes a reference so the domCOLLADA won't delete itself + daeElementRef myCOLLADA = topMeta->create(); + myCOLLADA->setDocument(newDocument); + newDocument->getDocumentURI()->setURI(name); + newDocument->setDomRoot(myCOLLADA); + + // Add this document to the list. + documents.push_back(newDocument); + // If the user gave us a place to put the document, send it back to them. + if (document) + *document = newDocument; + + return DAE_OK; +} + +daeInt daeSTLDatabase::insertDocument( daeDocument *c ) { + documents.push_back(c); + insertElement( c, c->getDomRoot() ); + return DAE_OK; +} + +daeInt daeSTLDatabase::removeDocument(daeDocument *document) +{ + vector< daeDocument* >::iterator iter = documents.begin(); + while ( iter != documents.end() ) { + if ( (*iter) == document ) { + //delete all of its children + removeElement( *iter, (*iter)->getDomRoot() ); + delete *iter; // sthomas (see bug 1466019) + iter = documents.erase(iter); + } + else { + iter++; + } + } + return DAE_OK; +} + +daeUInt daeSTLDatabase::getDocumentCount() +{ + return (daeUInt)documents.size(); +} + +daeDocument* daeSTLDatabase::getDocument(daeUInt index) +{ + if (indexgetDocumentURI()->str() == name) + return(document); + } + return(NULL); +} + +daeString daeSTLDatabase::getDocumentName(daeUInt index) +{ + if (indexgetDocumentURI()->getURI(); + else + return NULL; +} + +// Elements +daeInt daeSTLDatabase::insertElement(daeDocument* document,daeElement* element) +{ + insertChildren( document, element ); + + map >::iterator iter = elements.find( string( element->getTypeName() ) ); + if ( iter != elements.end() ) + { + (*iter).second.push_back( element ); + } + else + { + vector< daeElement* > vec; + vec.push_back( element ); + elements.insert( make_pair( string( element->getTypeName() ), vec ) ); + } + + // Insert into the type ID map + typeMap.insert(make_pair(element->typeID(), element)); + + //insert into IDMap if element has an ID. IDMap is used to speed up URI resolution + if ( element->getID() != NULL ) { + elementsIDMap.insert( make_pair( string( element->getID() ), element ) ); + } + + // Insert into sid map if the element has a sid +// string sid = element->getAttribute("sid"); +// if (!sid.empty()) +// sidMap.insert(sidMapPair(sid, element)); + + dae.getSidRefCache().clear(); + + return DAE_OK; +} + +daeInt daeSTLDatabase::insertChildren( daeDocument *c, daeElement *element ) +{ + daeElementRefArray era; + element->getChildren( era ); + for ( unsigned int i = 0; i < era.getCount(); i++ ) { + insertElement( c, era[i] ); + } + return DAE_OK; +} + +daeInt daeSTLDatabase::removeElement(daeDocument* document,daeElement* element) +{ + if ( !element ) { + return DAE_ERR_INVALID_CALL; + } + removeChildren( document, element ); + + map >::iterator iter = elements.find( string( element->getTypeName() ) ); + if ( iter != elements.end() ) + { + vector< daeElement* > &vec = (*iter).second; + vector< daeElement* >::iterator i = vec.begin(); + vector< daeElement* >::iterator end = vec.end(); + while( i != end ) + { + if ( (*i) == element ) + { + vec.erase( i ); + break; + } + ++i; + } + } + + typeMapRange range = typeMap.equal_range(element->typeID()); + for (typeMapIter iter = range.first; iter != range.second; iter++) { + if (iter->second == element) { + typeMap.erase(iter); + break; + } + } + + if ( element->getID() != NULL ) { + idMapRange range = elementsIDMap.equal_range( string( element->getID() ) ); + multimap::iterator iter = range.first; + while( iter != range.second ) { + if ( (*iter).second == element ) { + elementsIDMap.erase( iter ); + break; + } + ++iter; + } + } + +// string sid = element->getAttribute("sid"); +// if (!sid.empty()) { +// pair range = sidMap.equal_range(sid); +// for (sidMapIter iter = range.first; iter != range.second; iter++) { +// if (iter->second == element) { +// sidMap.erase(iter); +// break; +// } +// } +// } + + dae.getSidRefCache().clear(); + + return DAE_OK; +} + +daeInt daeSTLDatabase::removeChildren( daeDocument *c, daeElement *element ) +{ + daeElementRefArray era; + element->getChildren( era ); + for ( unsigned int i = 0; i < era.getCount(); i++ ) { + removeElement( c, era[i] ); + } + return DAE_OK; +} + +daeInt daeSTLDatabase::changeElementID( daeElement* element, daeString newID ) +{ + if ( !element ) { + return DAE_ERR_INVALID_CALL; + } + + // Remove the current entry in the ID map if the element has an ID + if ( element->getID() != NULL ) { + pair< multimap::iterator, multimap::iterator> range; + range = elementsIDMap.equal_range( string( element->getID() ) ); + multimap::iterator iter = range.first; + while( iter != range.second ) { + if ( (*iter).second == element ) { + elementsIDMap.erase( iter ); + break; + } + ++iter; + } + } + + // Add an entry to the ID map if the element will have an ID + if ( newID != NULL ) { + elementsIDMap.insert( make_pair( string( newID ), element ) ); + } + + dae.getSidRefCache().clear(); + + return DAE_OK; +} + +daeInt daeSTLDatabase::changeElementSID(daeElement* element, daeString newSID) { + if (!element) + return DAE_ERR_INVALID_CALL; + +// // Remove the current entry in the sid map if the element has a sid +// string sid = element->getAttribute("sid"); +// if (!sid.empty()) { +// pair range = sidMap.equal_range(sid); +// for (sidMapIter iter = range.first; iter != range.second; iter++) { +// if (iter->second == element) { +// sidMap.erase(iter); +// break; +// } +// } +// } + +// // Add an entry to the sid map if the element will have a sid +// if ( newSID != NULL ) +// sidMap.insert(sidMapPair(newSID, element)); + + dae.getSidRefCache().clear(); + + return DAE_OK; +} + +daeInt daeSTLDatabase::clear() +{ + elements.clear(); + typeMap.clear(); + elementsIDMap.clear(); + sidMap.clear(); + int i; + for (i=0;i<(int)documents.size();i++) + delete documents[i]; + documents.clear(); //this will free the daeElement + dae.getRawRefCache().clear(); + dae.getSidRefCache().clear(); + return DAE_OK; +} + +daeUInt daeSTLDatabase::getElementCount(daeString name,daeString type,daeString file) +{ + // If none of the search keys was specified, return the total element count in the database + if ( !name && !type && !file ) + { + daeUInt count = 0; + map< string, vector< daeElement*> >::iterator iter = elements.begin(); + map< string, vector< daeElement*> >::iterator end = elements.end(); + while( iter != end ) + { + count += (daeUInt)(*iter).second.size(); + ++iter; + } + return count; + } + + if ( name ) + { + // name specified + int count = 0; + if ( file ) + { + // If a document URI was a search key (in file) resolve it to a text URI with no fragment + daeURI tempURI(dae, file,true); + daeDocument *col = getDocument( tempURI.getURI() ); + if ( col == NULL ) { + return 0; + } + // a document was specified + pair< multimap< string, daeElement* >::iterator, multimap< string, daeElement* >::iterator > range; + range = elementsIDMap.equal_range( string( name ) ); + multimap< string, daeElement* >::iterator i = range.first; + while ( i != range.second ) + { + if ( col == (*i).second->getDocument() ) + { + count++; + } + ++i; + } + return count; + } + else + { + //no file specified - just name + return (daeUInt)elementsIDMap.count( string( name ) ); + } + } + + if ( type ) + { + // type specified + map< string, vector< daeElement*> >::iterator iter = elements.find( string( type ) ); + if ( iter == elements.end() ) + { + return 0; + } + + int count = 0; + if ( file ) + { + // If a document URI was a search key (in file) resolve it to a text URI with no fragment + daeURI tempURI(dae, file,true); + daeDocument *col = getDocument( tempURI.getURI() ); + if ( col == NULL ) { + return 0; + } + // a document was specified + vector< daeElement* > &vec = (*iter).second; + vector< daeElement* >::iterator i = vec.begin(); + vector< daeElement* >::iterator end = vec.end(); + while( i != end ) + { + if ( col == (*i)->getDocument() ) + { + ++count; + } + ++i; + } + return count; + } + else + { + //no file specified - just type + return (daeUInt)(*iter).second.size(); + } + } + + //if you get here only a file was specified + daeURI tempURI(dae, file,true); + daeDocument *col = getDocument( tempURI.getURI() ); + if ( col == NULL ) { + return 0; + } + //a document was specified + int count = 0; + map< string, vector< daeElement*> >::iterator iter = elements.begin(); + map< string, vector< daeElement*> >::iterator end = elements.end(); + while( iter != end ) + { + vector< daeElement* > &vec = (*iter).second; + vector< daeElement* >::iterator i = vec.begin(); + vector< daeElement* >::iterator end2 = vec.end(); + while( i != end2 ) + { + if( col == (*i)->getDocument() ) + { + ++count; + } + ++i; + } + ++iter; + } + return count; + +} + +daeInt daeSTLDatabase::getElement(daeElement** pElement,daeInt index,daeString name,daeString type,daeString file) +{ + // If the index is out of range, there can be no match + if ( index < 0 ) + { + return DAE_ERR_QUERY_NO_MATCH; + } + + // If no name, type or file was specified we return the element at "index" - SLOW + if ( !name && !type && !file ) + { + daeUInt count = 0; + map< string, vector< daeElement*> >::iterator iter = elements.begin(); + map< string, vector< daeElement*> >::iterator end = elements.end(); + while( iter != end ) + { + count += (daeUInt)(*iter).second.size(); + if ( (daeInt)count > index ) + { + *pElement = (*iter).second[index - (count - (*iter).second.size())] ; + return DAE_OK; + } + ++iter; + } + return DAE_ERR_QUERY_NO_MATCH; + } + + if ( name ) + { + //name specified + int count = 0; + if ( file ) + { + // If a document URI was a search key (in file) resolve it to a text URI with no fragment + daeURI tempURI(dae, file, true); + daeDocument *col = getDocument( tempURI.getURI() ); + if ( col == NULL ) { + *pElement = NULL; + return DAE_ERR_QUERY_NO_MATCH; + } + //a document was specified + pair< multimap< string, daeElement* >::iterator, multimap< string, daeElement* >::iterator> range; + range = elementsIDMap.equal_range( string( name ) ); + multimap< string, daeElement* >::iterator i = range.first; + while ( i != range.second ) + { + if ( col == (*i).second->getDocument() ) + { + if ( count == index ) + { + *pElement = (*i).second; + return DAE_OK; + } + count++; + } + ++i; + } + *pElement = NULL; + return DAE_ERR_QUERY_NO_MATCH; + } + else + { + //no document specified + multimap< string, daeElement* >::iterator i = elementsIDMap.find( string( name ) ); + if ( index > (daeInt)elementsIDMap.count( string( name ) ) || i == elementsIDMap.end() ) + { + *pElement = NULL; + return DAE_ERR_QUERY_NO_MATCH; + } + for ( int x = 0; x < index; x++ ) + { + ++i; + } + *pElement = i->second; + return DAE_OK; + } + } + + if ( type ) + { + map< string, vector< daeElement*> >::iterator iter = elements.find( string( type ) ); + if ( iter == elements.end() ) + { + *pElement = NULL; + return DAE_ERR_QUERY_NO_MATCH; + } + //type specified + int count = 0; + if ( file ) + { + // If a document URI was a search key (in file) resolve it to a text URI with no fragment + daeURI tempURI(dae, file, true); + daeDocument *col = getDocument( tempURI.getURI() ); + if ( col == NULL ) { + return DAE_ERR_QUERY_NO_MATCH; + } + //a document was specified + // a document was specified + vector< daeElement* > &vec = (*iter).second; + vector< daeElement* >::iterator i = vec.begin(); + vector< daeElement* >::iterator end = vec.end(); + while( i != end ) + { + if ( col == (*i)->getDocument() ) + { + if ( count == index ) + { + *pElement = (*i); + return DAE_OK; + } + ++count; + } + ++i; + } + return DAE_ERR_QUERY_NO_MATCH; + } + else + { + //no document specified + if ( index >= (daeInt)(*iter).second.size() ) + { + *pElement = NULL; + return DAE_ERR_QUERY_NO_MATCH; + } + *pElement = (*iter).second[index]; + return DAE_OK; + } + } + + //if you get here only the file was specified - SLOW + daeURI tempURI(dae, file, true); + daeDocument *col = getDocument( tempURI.getURI() ); + if ( col == NULL ) { + return DAE_ERR_QUERY_NO_MATCH; + } + //a document was specified + int count = 0; + map< string, vector< daeElement*> >::iterator iter = elements.begin(); + map< string, vector< daeElement*> >::iterator end = elements.end(); + while( iter != end ) + { + vector< daeElement* > &vec = (*iter).second; + vector< daeElement* >::iterator i = vec.begin(); + vector< daeElement* >::iterator end2 = vec.end(); + while( i != end2 ) + { + if( col == (*i)->getDocument() ) + { + if( count == index ) + { + *pElement = (*i); + return DAE_OK; + } + ++count; + } + ++i; + } + ++iter; + } + return DAE_ERR_QUERY_NO_MATCH; + +} + +vector daeSTLDatabase::idLookup(const string& id) { + vector matchingElements; + idMapRange range = elementsIDMap.equal_range(id); + for (idMapIter iter = range.first; iter != range.second; iter++) + matchingElements.push_back(iter->second); + return matchingElements; +} + +void daeSTLDatabase::typeLookup(daeInt typeID, + vector& matchingElements, + daeDocument* doc) { + matchingElements.clear(); + typeMapRange range = typeMap.equal_range(typeID); + for (typeMapIter iter = range.first; iter != range.second; iter++) + if (!doc || doc == iter->second->getDocument()) + matchingElements.push_back(iter->second); +} + +void daeSTLDatabase::sidLookup(const string& sid, + vector& matchingElements, + daeDocument* doc) { + matchingElements.clear(); + if (!sid.empty()) { + sidMapRange range = sidMap.equal_range(sid); + for (sidMapIter iter = range.first; iter != range.second; iter++) + if (!doc || doc == iter->second->getDocument()) + matchingElements.push_back(iter->second); + } +} diff --git a/Engine/lib/collada/src/modules/stdErrPlugin/stdErrPlugin.cpp b/Engine/lib/collada/src/modules/stdErrPlugin/stdErrPlugin.cpp new file mode 100644 index 000000000..ee21f16ae --- /dev/null +++ b/Engine/lib/collada/src/modules/stdErrPlugin/stdErrPlugin.cpp @@ -0,0 +1,35 @@ +/* + * Copyright 2006 Sony Computer Entertainment Inc. + * + * Licensed under the SCEA Shared Source License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * http://research.scea.com/scea_shared_source_license.html + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing permissions and limitations under the + * License. + */ + +#include +#include + +quietErrorHandler quietErrorHandler::theInstance; + +stdErrPlugin::stdErrPlugin() { +} + +stdErrPlugin::~stdErrPlugin() { +} + +void stdErrPlugin::handleError( daeString msg ) { + //fprintf( stderr, "Error: %s\n", msg ); + //fflush( stderr ); + printf( "Error: %s\n", msg ); +} + +void stdErrPlugin::handleWarning( daeString msg ) { + //fprintf( stderr, "Warning: %s\n", msg ); + //fflush( stderr ); + printf( "Warning: %s\n", msg ); +} diff --git a/Engine/lib/convexDecomp/NvConcavityVolume.cpp b/Engine/lib/convexDecomp/NvConcavityVolume.cpp new file mode 100644 index 000000000..4cda8811f --- /dev/null +++ b/Engine/lib/convexDecomp/NvConcavityVolume.cpp @@ -0,0 +1,252 @@ +/* + +NvConcavityVolume.cpp : This is a code snippet that computes the volume of concavity of a traingle mesh. + +*/ +/*! +** +** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com +** +** Portions of this source has been released with the PhysXViewer application, as well as +** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. +** +** If you find this code useful or you are feeling particularily generous I would +** ask that you please go to http://www.amillionpixels.us and make a donation +** to Troy DeMolay. +** +** DeMolay is a youth group for young men between the ages of 12 and 21. +** It teaches strong moral principles, as well as leadership skills and +** public speaking. The donations page uses the 'pay for pixels' paradigm +** where, in this case, a pixel is only a single penny. Donations can be +** made for as small as $4 or as high as a $100 block. Each person who donates +** will get a link to their own site as well as acknowledgement on the +** donations blog located here http://www.amillionpixels.blogspot.com/ +** +** If you wish to contact me you can use the following methods: +** +** Skype ID: jratcliff63367 +** Yahoo: jratcliff63367 +** AOL: jratcliff1961 +** email: jratcliffscarab@gmail.com +** +** +** The MIT license: +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is furnished +** to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. + +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#define SHOW_DEBUG 0 +#if SHOW_DEBUG +#include "RenderDebug.h" +#endif +#include "NvConcavityVolume.h" +#include "NvFloatMath.h" +#include "NvRayCast.h" +#include + +#pragma warning(disable:4100 4189 4505 4127 4101) + +namespace CONVEX_DECOMPOSITION +{ + +bool raycast(const NxF32 *p1,const NxF32 *normal,NxF32 *dest,iRayCast *cast_hull,iRayCast *cast_mesh) +{ + bool ret = true; + + NxF32 hit_hull[3]; + NxF32 hit_hullNormal[3]; + + NxF32 hit_mesh[3]; + NxF32 hit_meshNormal[3]; + + bool hitHull = cast_hull->castRay(p1,normal,hit_hull,hit_hullNormal); + bool hitMesh = cast_mesh->castRay(p1,normal,hit_mesh,hit_meshNormal); + + if ( hitMesh ) + { + float dot = fm_dot(normal,hit_meshNormal); + if ( dot < 0 ) // skip if we hit an internal face of the mesh when projection out towards the convex hull. + { + ret = false; + } + else + { + NxF32 d1 = fm_distanceSquared(p1,hit_mesh); + NxF32 d2 = fm_distanceSquared(p1,hit_hull); + if ( d1 < d2 ) + { + dest[0] = hit_mesh[0]; + dest[1] = hit_mesh[1]; + dest[2] = hit_mesh[2]; + } + else + { + dest[0] = hit_hull[0]; + dest[1] = hit_hull[1]; + dest[2] = hit_hull[2]; + } + } + } + else if ( hitHull ) + { + dest[0] = hit_hull[0]; + dest[1] = hit_hull[1]; + dest[2] = hit_hull[2]; + } + else + { + ret = false; + } + + + return ret; +} + +void addTri(NxU32 *indices,NxU32 i1,NxU32 i2,NxU32 i3,NxU32 &tcount) +{ + indices[tcount*3+0] = i1; + indices[tcount*3+1] = i2; + indices[tcount*3+2] = i3; + tcount++; +} + +NxF32 computeConcavityVolume(NxU32 vcount_hull, + const NxF32 *vertices_hull, + NxU32 tcount_hull, + const NxU32 *indices_hull, + NxU32 vcount_mesh, + const NxF32 *vertices_mesh, + NxU32 tcount_mesh, + const NxU32 *indices_mesh) +{ + NxF32 total_volume = 0; + +#if SHOW_DEBUG + NVSHARE::gRenderDebug->pushRenderState(); + NVSHARE::gRenderDebug->setCurrentDisplayTime(150.0f); +#endif + + iRayCast *cast_hull = createRayCast(vertices_hull,tcount_hull,indices_hull); + iRayCast *cast_mesh = createRayCast(vertices_mesh,tcount_mesh,indices_mesh); + + + const NxU32 *indices = indices_mesh; +#if 0 + static NxU32 index = 0; + NxU32 i = index++; + indices = &indices[i*3]; +#else + for (NxU32 i=0; isetCurrentColor(0x0000FF,0xFFFFFF); + NVSHARE::gRenderDebug->addToCurrentState(NVSHARE::DebugRenderState::SolidWireShaded); + + for (NxU32 i=0; iDebugTri(p1,p2,p3); + } +#endif + } + indices+=3; + } +#if SHOW_DEBUG + NVSHARE::gRenderDebug->popRenderState(); +#endif + + releaseRayCast(cast_hull); + releaseRayCast(cast_mesh); + + return total_volume; +} + +}; // end of namespace diff --git a/Engine/lib/convexDecomp/NvConcavityVolume.h b/Engine/lib/convexDecomp/NvConcavityVolume.h new file mode 100644 index 000000000..18bbed073 --- /dev/null +++ b/Engine/lib/convexDecomp/NvConcavityVolume.h @@ -0,0 +1,78 @@ +#ifndef NV_CONCAVITY_H + +#define NV_CONCAVITY_H + +/* + +NvConcavityVolume.h : This is a code snippet that computes the volume of concavity of a traingle mesh. + +*/ + + +/*! +** +** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com +** +** Portions of this source has been released with the PhysXViewer application, as well as +** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. +** +** If you find this code useful or you are feeling particularily generous I would +** ask that you please go to http://www.amillionpixels.us and make a donation +** to Troy DeMolay. +** +** DeMolay is a youth group for young men between the ages of 12 and 21. +** It teaches strong moral principles, as well as leadership skills and +** public speaking. The donations page uses the 'pay for pixels' paradigm +** where, in this case, a pixel is only a single penny. Donations can be +** made for as small as $4 or as high as a $100 block. Each person who donates +** will get a link to their own site as well as acknowledgement on the +** donations blog located here http://www.amillionpixels.blogspot.com/ +** +** If you wish to contact me you can use the following methods: +** +** Skype ID: jratcliff63367 +** Yahoo: jratcliff63367 +** AOL: jratcliff1961 +** email: jratcliffscarab@gmail.com +** +** +** The MIT license: +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is furnished +** to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. + +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include "NvUserMemAlloc.h" + +namespace CONVEX_DECOMPOSITION +{ + +// computes the 'volume of concavity' of a triangle mesh projected against its surrounding convex hull. + +NxF32 computeConcavityVolume(NxU32 vcount_hull, + const NxF32 *vertices_hull, + NxU32 tcount_hull, + const NxU32 *indices_hull, + NxU32 vcount_mesh, + const NxF32 *vertices_mesh, + NxU32 tcount_mesh, + const NxU32 *indices_mesh); + +}; // end of namespace + +#endif diff --git a/Engine/lib/convexDecomp/NvConvexDecomposition.cpp b/Engine/lib/convexDecomp/NvConvexDecomposition.cpp new file mode 100644 index 000000000..386ac497c --- /dev/null +++ b/Engine/lib/convexDecomp/NvConvexDecomposition.cpp @@ -0,0 +1,788 @@ + +/* + +NvConvexDecomposition.cpp : The main interface to the convex decomposition library. + +*/ + +/*! +** +** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com +** +** Portions of this source has been released with the PhysXViewer application, as well as +** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. +** +** If you find this code useful or you are feeling particularily generous I would +** ask that you please go to http://www.amillionpixels.us and make a donation +** to Troy DeMolay. +** +** DeMolay is a youth group for young men between the ages of 12 and 21. +** It teaches strong moral principles, as well as leadership skills and +** public speaking. The donations page uses the 'pay for pixels' paradigm +** where, in this case, a pixel is only a single penny. Donations can be +** made for as small as $4 or as high as a $100 block. Each person who donates +** will get a link to their own site as well as acknowledgement on the +** donations blog located here http://www.amillionpixels.blogspot.com/ +** +** If you wish to contact me you can use the following methods: +** +** Skype ID: jratcliff63367 +** Yahoo: jratcliff63367 +** AOL: jratcliff1961 +** email: jratcliffscarab@gmail.com +** +** +** The MIT license: +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is furnished +** to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. + +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ +#include +#include +#include +#include +#include + +#include "NvConvexDecomposition.h" +#include "NvHashMap.h" +#include "NvFloatMath.h" +#include "NvRemoveTjunctions.h" +#include "NvMeshIslandGeneration.h" +#include "NvStanHull.h" +#include "NvConcavityVolume.h" +#include "NvSplitMesh.h" +#include "NvThreadConfig.h" + + +#pragma warning(disable:4996 4100 4189) + +namespace CONVEX_DECOMPOSITION +{ + + +#define GRANULARITY 0.0000000001f + +typedef CONVEX_DECOMPOSITION::Array< NxU32 > NxU32Array; + +class ConvexHull : public Memalloc +{ +public: + ConvexHull(NxU32 vcount,const NxF32 *vertices,NxU32 tcount,const NxU32 *indices) + { + mTested = false; + mVcount = vcount; + mTcount = tcount; + mVertices = 0; + mIndices = 0; + mHullVolume = 0; + if ( vcount ) + { + mVertices = (NxF32 *)MEMALLOC_MALLOC(sizeof(NxF32)*3*vcount); + memcpy(mVertices,vertices,sizeof(NxF32)*3*vcount); + } + if ( tcount ) + { + mIndices = (NxU32 *)MEMALLOC_MALLOC(sizeof(NxU32)*3*tcount); + memcpy(mIndices,indices,sizeof(NxU32)*3*tcount); + } + if ( mVcount && mTcount ) + { + mHullVolume = fm_computeMeshVolume( mVertices, mTcount, mIndices); + } + } + + ~ConvexHull(void) + { + reset(); + } + + void reset(void) + { + MEMALLOC_FREE(mVertices); + MEMALLOC_FREE(mIndices); + mVertices = 0; + mIndices = 0; + mVcount = 0; + mTcount = 0; + mHullVolume = 0; + } + + // return true if merging this hull with the 'mergeHull' produces a new convex hull which is no greater in volume than the + // mergeThresholdPercentage + bool canMerge(ConvexHull *mergeHull,NxF32 mergeThresholdPercent,NxU32 maxVertices,NxF32 skinWidth,NxF32 &percent) + { + bool ret = false; + + if ( mHullVolume > 0 && mergeHull->mHullVolume > 0 ) + { + NxU32 combineVcount = mVcount + mergeHull->mVcount; + NxF32 *vertices = (NxF32 *)MEMALLOC_MALLOC(sizeof(NxF32)*combineVcount*3); + NxF32 *dest = vertices; + const NxF32 *source = mVertices; + + for (NxU32 i=0; imVertices; + for (NxU32 i=0; imVcount; i++) + { + dest[0] = source[0]; + dest[1] = source[1]; + dest[2] = source[2]; + dest+=3; + source+=3; + } + + // create the combined convex hull. + HullDesc hd; + hd.mVcount = combineVcount; + hd.mVertices = vertices; + hd.mVertexStride = sizeof(NxF32)*3; + hd.mMaxVertices = maxVertices; + hd.mSkinWidth = skinWidth; + HullLibrary hl; + HullResult result; + hl.CreateConvexHull(hd,result); + + NxF32 combinedVolume = fm_computeMeshVolume(result.mOutputVertices, result.mNumFaces, result.mIndices ); + NxF32 seperateVolume = mHullVolume+mergeHull->mHullVolume; + + NxF32 percentMerge = 100 - (seperateVolume*100 / combinedVolume ); + + if ( percentMerge <= mergeThresholdPercent ) + { + percent = percentMerge; + ret = true; + } + MEMALLOC_FREE(vertices); + hl.ReleaseResult(result); + } + return ret; + } + + void merge(ConvexHull *mergeHull,NxU32 maxVertices,NxF32 skinWidth) + { + NxU32 combineVcount = mVcount + mergeHull->mVcount; + NxF32 *vertices = (NxF32 *)MEMALLOC_MALLOC(sizeof(NxF32)*combineVcount*3); + NxF32 *dest = vertices; + const NxF32 *source = mVertices; + + for (NxU32 i=0; imVertices; + for (NxU32 i=0; imVcount; i++) + { + dest[0] = source[0]; + dest[1] = source[1]; + dest[2] = source[2]; + dest+=3; + source+=3; + } + + // create the combined convex hull. + HullDesc hd; + hd.mVcount = combineVcount; + hd.mVertices = vertices; + hd.mVertexStride = sizeof(NxF32)*3; + hd.mMaxVertices = maxVertices; + hd.mSkinWidth = skinWidth; + HullLibrary hl; + HullResult result; + hl.CreateConvexHull(hd,result); + + reset(); + mergeHull->reset(); + mergeHull->mTested = true; // it's been tested. + mVcount = result.mNumOutputVertices; + mVertices = (NxF32 *)MEMALLOC_MALLOC(sizeof(NxF32)*3*mVcount); + memcpy(mVertices,result.mOutputVertices,sizeof(NxF32)*3*mVcount); + mTcount = result.mNumFaces; + mIndices = (NxU32 *)MEMALLOC_MALLOC(sizeof(NxU32)*mTcount*3); + memcpy(mIndices, result.mIndices, sizeof(NxU32)*mTcount*3); + + MEMALLOC_FREE(vertices); + hl.ReleaseResult(result); + } + + void setTested(bool state) + { + mTested = state; + } + + bool beenTested(void) const { return mTested; }; + + bool mTested; + NxF32 mHullVolume; + NxU32 mVcount; + NxF32 *mVertices; + NxU32 mTcount; + NxU32 *mIndices; +}; + +typedef Array< ConvexHull *> ConvexHullVector; + +class ConvexDecomposition : public iConvexDecomposition, public CONVEX_DECOMPOSITION::Memalloc, public ThreadInterface +{ +public: + ConvexDecomposition(void) + { + mVertexIndex = 0; + mComplete = false; + mCancel = false; + mThread = 0; + } + + ~ConvexDecomposition(void) + { + wait(); + reset(); + if ( mThread ) + { + tc_releaseThread(mThread); + } + } + + void wait(void) const + { + while ( mThread && !mComplete ); + } + + virtual void reset(void) // reset the input mesh data. + { + wait(); + if ( mVertexIndex ) + { + fm_releaseVertexIndex(mVertexIndex); + mVertexIndex = 0; + } + mIndices.clear(); + ConvexHullVector::Iterator i; + for (i=mHulls.begin(); i!=mHulls.end(); ++i) + { + ConvexHull *ch = (*i); + delete ch; + } + mHulls.clear(); + } + + virtual bool addTriangle(const NxF32 *p1,const NxF32 *p2,const NxF32 *p3) + { + bool ret = true; + wait(); + if ( mVertexIndex == 0 ) + { + mVertexIndex = fm_createVertexIndex(GRANULARITY,false); + } + + bool newPos; + NxU32 i1 = mVertexIndex->getIndex(p1,newPos); + NxU32 i2 = mVertexIndex->getIndex(p2,newPos); + NxU32 i3 = mVertexIndex->getIndex(p3,newPos); + + if ( i1 == i2 || i1 == i3 || i2 == i3 ) + { + ret = false; // triangle is degenerate + } + else + { + mIndices.pushBack(i1); + mIndices.pushBack(i2); + mIndices.pushBack(i3); + } + return ret; + } + + ConvexHull * getNonTested(void) const + { + ConvexHull *ret = 0; + for (NxU32 i=0; ibeenTested() ) + { + ret = ch; + break; + } + } + return ret; + } + + virtual NxU32 computeConvexDecomposition(NxF32 skinWidth, + NxU32 decompositionDepth, + NxU32 maxHullVertices, + NxF32 concavityThresholdPercent, + NxF32 mergeThresholdPercent, + NxF32 volumeSplitThresholdPercent, + bool useInitialIslandGeneration, + bool useIslandGeneration, + bool useThreads) + { + NxU32 ret = 0; + + if ( mThread ) + return 0; + + if ( mVertexIndex ) + { + + mSkinWidth = skinWidth; + mDecompositionDepth = decompositionDepth; + mMaxHullVertices = maxHullVertices; + mConcavityThresholdPercent = concavityThresholdPercent; + mMergeThresholdPercent = mergeThresholdPercent; + mVolumeSplitThresholdPercent = volumeSplitThresholdPercent; + mUseInitialIslandGeneration = useInitialIslandGeneration; + mUseIslandGeneration = false; // Not currently supported. useIslandGeneration; + mComplete = false; + mCancel = false; + + if ( useThreads ) + { + mThread = tc_createThread(this); + } + else + { + threadMain(); + ret = getHullCount(); + } + } + return ret; + } + + void performConvexDecomposition(NxU32 vcount, + const NxF32 *vertices, + NxU32 tcount, + const NxU32 *indices, + NxF32 skinWidth, + NxU32 decompositionDepth, + NxU32 maxHullVertices, + NxF32 concavityThresholdPercent, + NxF32 mergeThresholdPercent, + NxF32 volumeSplitThresholdPercent, + bool useInitialIslandGeneration, + bool useIslandGeneration, + NxU32 depth) + { + if ( mCancel ) return; + if ( depth >= decompositionDepth ) return; + + RemoveTjunctionsDesc desc; + desc.mVcount = vcount; + desc.mVertices = vertices; + desc.mTcount = tcount; + desc.mIndices = indices; + +#if 0 + RemoveTjunctions *rt = createRemoveTjunctions(); + rt->removeTjunctions(desc); +#else + + desc.mTcountOut = desc.mTcount; + desc.mIndicesOut = desc.mIndices; + +#endif + // ok..we now have a clean mesh without any tjunctions. + bool island = (depth == 0 ) ? useInitialIslandGeneration : useIslandGeneration; + if ( island ) + { + MeshIslandGeneration *mi = createMeshIslandGeneration(); + NxU32 icount = mi->islandGenerate(desc.mTcountOut,desc.mIndicesOut,desc.mVertices); + for (NxU32 i=0; igetIsland(i,tcount); + + baseConvexDecomposition(desc.mVcount,desc.mVertices, + tcount,indices, + skinWidth, + decompositionDepth, + maxHullVertices, + concavityThresholdPercent, + mergeThresholdPercent, + volumeSplitThresholdPercent, + useInitialIslandGeneration, + useIslandGeneration,depth); + } + releaseMeshIslandGeneration(mi); + } + else + { + baseConvexDecomposition(desc.mVcount,desc.mVertices,desc.mTcountOut, + desc.mIndicesOut, + skinWidth, + decompositionDepth, + maxHullVertices, + concavityThresholdPercent, + mergeThresholdPercent, + volumeSplitThresholdPercent, + useInitialIslandGeneration, + useIslandGeneration,depth); + } +#if 0 + releaseRemoveTjunctions(rt); +#endif + } + + virtual void baseConvexDecomposition(NxU32 vcount, + const NxF32 *vertices, + NxU32 tcount, + const NxU32 *indices, + NxF32 skinWidth, + NxU32 decompositionDepth, + NxU32 maxHullVertices, + NxF32 concavityThresholdPercent, + NxF32 mergeThresholdPercent, + NxF32 volumeSplitThresholdPercent, + bool useInitialIslandGeneration, + bool useIslandGeneration, + NxU32 depth) + { + + if ( mCancel ) return; + + bool split = false; // by default we do not split + + + NxU32 *out_indices = (NxU32 *)MEMALLOC_MALLOC( sizeof(NxU32)*tcount*3 ); + NxF32 *out_vertices = (NxF32 *)MEMALLOC_MALLOC( sizeof(NxF32)*3*vcount ); + + NxU32 out_vcount = fm_copyUniqueVertices( vcount, vertices, out_vertices, tcount, indices, out_indices ); + // get a copy of only the unique vertices which are actually being used. + + HullDesc hd; + hd.mVcount = out_vcount; + hd.mVertices = out_vertices; + hd.mVertexStride = sizeof(NxF32)*3; + hd.mMaxVertices = maxHullVertices; + hd.mSkinWidth = skinWidth; + HullLibrary hl; + HullResult result; + hl.CreateConvexHull(hd,result); + + NxF32 meshVolume = fm_computeMeshVolume(result.mOutputVertices, result.mNumFaces, result.mIndices ); + + if ( (depth+1) < decompositionDepth ) + { + // compute the volume of this mesh... + NxF32 percentVolume = (meshVolume*100)/mOverallMeshVolume; // what percentage of the overall mesh volume are we? + if ( percentVolume > volumeSplitThresholdPercent ) // this piece must be greater thant he volume split threshold percent + { + // ok..now we will compute the concavity... + NxF32 concave_volume = computeConcavityVolume(result.mNumOutputVertices, result.mOutputVertices, result.mNumFaces, result.mIndices, out_vcount, out_vertices, tcount, out_indices ); + NxF32 concave_percent = (concave_volume*100) / meshVolume; + if ( concave_percent >= concavityThresholdPercent ) + { + // ready to do split here.. + split = true; + } + } + } + + if ( !split ) + { + saveConvexHull(result.mNumOutputVertices,result.mOutputVertices,result.mNumFaces,result.mIndices); + } + + // Compute the best fit plane relative to the computed convex hull. + NxF32 plane[4]; + bool ok = fm_computeSplitPlane(result.mNumOutputVertices,result.mOutputVertices,result.mNumFaces,result.mIndices,plane); + assert(ok); + + hl.ReleaseResult(result); + MEMALLOC_FREE(out_indices); + MEMALLOC_FREE(out_vertices); + + if ( split ) + { + iSplitMesh *sm = createSplitMesh(); + + NvSplitMesh n; + n.mVcount = vcount; + n.mVertices = vertices; + n.mTcount = tcount; + n.mIndices = indices; + if ( ok ) + { + NvSplitMesh leftMesh; + NvSplitMesh rightMesh; + + sm->splitMesh(n,leftMesh,rightMesh,plane,GRANULARITY); + + if ( leftMesh.mTcount ) + { + performConvexDecomposition(leftMesh.mVcount, + leftMesh.mVertices, + leftMesh.mTcount, + leftMesh.mIndices, + skinWidth, + decompositionDepth, + maxHullVertices, + concavityThresholdPercent, + mergeThresholdPercent, + volumeSplitThresholdPercent, + useInitialIslandGeneration, + useIslandGeneration, + depth+1); + + } + if ( rightMesh.mTcount ) + { + performConvexDecomposition(rightMesh.mVcount, + rightMesh.mVertices, + rightMesh.mTcount, + rightMesh.mIndices, + skinWidth, + decompositionDepth, + maxHullVertices, + concavityThresholdPercent, + mergeThresholdPercent, + volumeSplitThresholdPercent, + useInitialIslandGeneration, + useIslandGeneration, + depth+1); + } + } + releaseSplitMesh(sm); + } + } + + // Copies only the vertices which are actually used. + // Then computes the convex hull around these used vertices. + // Next computes the volume of this convex hull. + // Frees up scratch memory and returns the volume of the convex hull around the source triangle mesh. + NxF32 computeHullMeshVolume(NxU32 vcount,const NxF32 *vertices,NxU32 tcount,const NxU32 *indices,NxU32 maxVertices,NxF32 skinWidth) + { + if ( mCancel ) return 0; + // first thing we should do is compute the overall mesh volume. + NxU32 *out_indices = (NxU32 *)MEMALLOC_MALLOC( sizeof(NxU32)*tcount*3 ); + NxF32 *out_vertices = (NxF32 *)MEMALLOC_MALLOC( sizeof(NxF32)*3*vcount ); + + NxU32 out_vcount = fm_copyUniqueVertices( vcount, vertices, out_vertices, tcount, indices, out_indices ); + // get a copy of only the unique vertices which are actually being used. + + HullDesc hd; + hd.mVcount = out_vcount; + hd.mVertices = out_vertices; + hd.mVertexStride = sizeof(NxF32)*3; + hd.mMaxVertices = maxVertices; + hd.mSkinWidth = skinWidth; + HullLibrary hl; + HullResult result; + hl.CreateConvexHull(hd,result); + + NxF32 volume = fm_computeMeshVolume(result.mOutputVertices, result.mNumFaces, result.mIndices ); + + hl.ReleaseResult(result); + MEMALLOC_FREE(out_indices); + MEMALLOC_FREE(out_vertices); + + return volume; + } + + + virtual bool isComputeComplete(void) // if building the convex hulls in a background thread, this returns true if it is complete. + { + bool ret = true; + + if ( mThread ) + { + ret = mComplete; + if ( ret ) + { + tc_releaseThread(mThread); + mThread = 0; + } + } + + return ret; + } + + + virtual NxU32 getHullCount(void) + { + NxU32 hullCount = 0; + wait(); + if ( mCancel ) + { + reset(); + } + for (NxU32 i=0; imTcount ) + { + hullCount++; + } + } + return hullCount; + } + + virtual bool getConvexHullResult(NxU32 hullIndex,ConvexHullResult &result) + { + bool ret = false; + + wait(); + NxU32 index = 0; + for (NxU32 i=0; imTcount ) + { + if ( hullIndex == index ) + { + ret = true; + result.mVcount = ch->mVcount; + result.mTcount = ch->mTcount; + result.mVertices = ch->mVertices; + result.mIndices = ch->mIndices; + break; + } + index++; + } + } + + return ret; + } + + void saveConvexHull(NxU32 vcount,const NxF32 *vertices,NxU32 tcount,const NxU32 *indices) + { + ConvexHull *ch = MEMALLOC_NEW(ConvexHull)(vcount,vertices,tcount,indices); + mHulls.pushBack(ch); + } + + virtual void threadMain(void) + { + mOverallMeshVolume = computeHullMeshVolume( mVertexIndex->getVcount(), + mVertexIndex->getVerticesFloat(), + mIndices.size()/3, + &mIndices[0], + mMaxHullVertices, mSkinWidth ); + + performConvexDecomposition(mVertexIndex->getVcount(),mVertexIndex->getVerticesFloat(), + mIndices.size()/3,&mIndices[0], + mSkinWidth, + mDecompositionDepth, + mMaxHullVertices, + mConcavityThresholdPercent, + mMergeThresholdPercent, + mVolumeSplitThresholdPercent, + mUseInitialIslandGeneration, + mUseIslandGeneration,0); + + if ( mHulls.size() && !mCancel ) + { + // While convex hulls can be merged... + ConvexHull *ch = getNonTested(); + while ( ch && !mCancel ) + { + // Sort all convex hulls by volume, largest to smallest. + NxU32 hullCount = mHulls.size(); + ConvexHull *bestHull = 0; + NxF32 bestPercent = 100; + + for (NxU32 i=0; ibeenTested() && mergeHull != ch ) + { + NxF32 percent; + if ( ch->canMerge(mergeHull,mMergeThresholdPercent,mMaxHullVertices,mSkinWidth,percent) ) + { + if ( percent < bestPercent ) + { + bestHull = mergeHull; + bestPercent = percent; + } + } + } + } + + if ( bestHull ) + { + ch->merge(bestHull,mMaxHullVertices,mSkinWidth); + } + else + { + ch->setTested(true); + } + + ch = getNonTested(); + } + } + mComplete = true; + } + + virtual bool cancelCompute(void) // cause background thread computation to abort early. Will return no results. Use 'isComputeComplete' to confirm the thread is done. + { + bool ret = false; + + if ( mThread && !mComplete ) + { + mCancel = true; + ret = true; + } + + return ret; + } + +private: + bool mComplete; + bool mCancel; + fm_VertexIndex *mVertexIndex; + NxU32Array mIndices; + NxF32 mOverallMeshVolume; + ConvexHullVector mHulls; + Thread *mThread; + + NxF32 mSkinWidth; + NxU32 mDecompositionDepth; + NxU32 mMaxHullVertices; + NxF32 mConcavityThresholdPercent; + NxF32 mMergeThresholdPercent; + NxF32 mVolumeSplitThresholdPercent; + bool mUseInitialIslandGeneration; + bool mUseIslandGeneration; + +}; + + +iConvexDecomposition * createConvexDecomposition(void) +{ + ConvexDecomposition *cd = MEMALLOC_NEW(ConvexDecomposition); + return static_cast< iConvexDecomposition *>(cd); + +} + +void releaseConvexDecomposition(iConvexDecomposition *ic) +{ + ConvexDecomposition *cd = static_cast< ConvexDecomposition *>(ic); + delete cd; +} + +}; // end of namespace diff --git a/Engine/lib/convexDecomp/NvConvexDecomposition.h b/Engine/lib/convexDecomp/NvConvexDecomposition.h new file mode 100644 index 000000000..bf5ded4b1 --- /dev/null +++ b/Engine/lib/convexDecomp/NvConvexDecomposition.h @@ -0,0 +1,111 @@ +#ifndef CONVEX_DECOMPOSITION_H + +#define CONVEX_DECOMPOSITION_H + +/* + +NvConvexDecomposition.h : The main interface to the convex decomposition library. + +*/ + + +/*! +** +** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com +** +** Portions of this source has been released with the PhysXViewer application, as well as +** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. +** +** If you find this code useful or you are feeling particularily generous I would +** ask that you please go to http://www.amillionpixels.us and make a donation +** to Troy DeMolay. +** +** DeMolay is a youth group for young men between the ages of 12 and 21. +** It teaches strong moral principles, as well as leadership skills and +** public speaking. The donations page uses the 'pay for pixels' paradigm +** where, in this case, a pixel is only a single penny. Donations can be +** made for as small as $4 or as high as a $100 block. Each person who donates +** will get a link to their own site as well as acknowledgement on the +** donations blog located here http://www.amillionpixels.blogspot.com/ +** +** If you wish to contact me you can use the following methods: +** +** Skype ID: jratcliff63367 +** Yahoo: jratcliff63367 +** AOL: jratcliff1961 +** email: jratcliffscarab@gmail.com +** +** +** The MIT license: +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is furnished +** to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. + +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include "NvSimpleTypes.h" + +namespace CONVEX_DECOMPOSITION +{ + +struct ConvexHullResult +{ + NxU32 mVcount; // number of vertices. + NxF32 *mVertices; // vertex positions. + NxU32 mTcount; // number of triangles. + NxU32 *mIndices; // indexed triangle list. +}; + +class iConvexDecomposition +{ +public: + virtual void reset(void) = 0; // reset the input mesh data. + + virtual bool addTriangle(const NxF32 *p1,const NxF32 *p2,const NxF32 *p3) = 0; // add the input mesh one triangle at a time. + + virtual NxU32 computeConvexDecomposition(NxF32 skinWidth=0, // Skin width on the convex hulls generated + NxU32 decompositionDepth=8, // recursion depth for convex decomposition. + NxU32 maxHullVertices=64, // maximum number of vertices in output convex hulls. + NxF32 concavityThresholdPercent=0.1f, // The percentage of concavity allowed without causing a split to occur. + NxF32 mergeThresholdPercent=30.0f, // The percentage of volume difference allowed to merge two convex hulls. + NxF32 volumeSplitThresholdPercent=0.1f, // The percentage of the total volume of the object above which splits will still occur. + bool useInitialIslandGeneration=true, // whether or not to perform initial island generation on the input mesh. + bool useIslandGeneration=false, // Whether or not to perform island generation at each split. Currently disabled due to bug in RemoveTjunctions + bool useBackgroundThread=true) = 0; // Whether or not to compute the convex decomposition in a background thread, the default is true. + + virtual bool isComputeComplete(void) = 0; // if building the convex hulls in a background thread, this returns true if it is complete. + + virtual bool cancelCompute(void) = 0; // cause background thread computation to abort early. Will return no results. Use 'isComputeComplete' to confirm the thread is done. + + + virtual NxU32 getHullCount(void) = 0; // returns the number of convex hulls produced. + virtual bool getConvexHullResult(NxU32 hullIndex,ConvexHullResult &result) = 0; // returns each convex hull result. + +protected: + virtual ~iConvexDecomposition(void) + { + } + +}; + + +iConvexDecomposition * createConvexDecomposition(void); +void releaseConvexDecomposition(iConvexDecomposition *ic); + +}; // end of namespace + +#endif diff --git a/Engine/lib/convexDecomp/NvFloatMath.cpp b/Engine/lib/convexDecomp/NvFloatMath.cpp new file mode 100644 index 000000000..91979b1ca --- /dev/null +++ b/Engine/lib/convexDecomp/NvFloatMath.cpp @@ -0,0 +1,74 @@ +// a set of routines that let you do common 3d math +// operations without any vector, matrix, or quaternion +// classes or templates. +// +// a vector (or point) is a 'NxF32 *' to 3 floating point numbers. +// a matrix is a 'NxF32 *' to an array of 16 floating point numbers representing a 4x4 transformation matrix compatible with D3D or OGL +// a quaternion is a 'NxF32 *' to 4 floats representing a quaternion x,y,z,w +// +// + +/*! +** +** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com +** +** Portions of this source has been released with the PhysXViewer application, as well as +** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. +** +** If you find this code useful or you are feeling particularily generous I would +** ask that you please go to http://www.amillionpixels.us and make a donation +** to Troy DeMolay. +** +** DeMolay is a youth group for young men between the ages of 12 and 21. +** It teaches strong moral principles, as well as leadership skills and +** public speaking. The donations page uses the 'pay for pixels' paradigm +** where, in this case, a pixel is only a single penny. Donations can be +** made for as small as $4 or as high as a $100 block. Each person who donates +** will get a link to their own site as well as acknowledgement on the +** donations blog located here http://www.amillionpixels.blogspot.com/ +** +** If you wish to contact me you can use the following methods: +** +** Skype ID: jratcliff63367 +** Yahoo: jratcliff63367 +** AOL: jratcliff1961 +** email: jratcliffscarab@gmail.com +** +** +** The MIT license: +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is furnished +** to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. + +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ +#include +#include +#include +#include +#include +#include + +#include "NvFloatMath.h" + +#define REAL NxF32 + +#include "NvFloatMath.inl" + +#undef REAL +#define REAL NxF64 + +#include "NvFloatMath.inl" diff --git a/Engine/lib/convexDecomp/NvFloatMath.h b/Engine/lib/convexDecomp/NvFloatMath.h new file mode 100644 index 000000000..3b4a885e6 --- /dev/null +++ b/Engine/lib/convexDecomp/NvFloatMath.h @@ -0,0 +1,586 @@ +#ifndef NV_FLOAT_MATH_H + +#define NV_FLOAT_MATH_H + +#include "NvUserMemAlloc.h" + +// a set of routines that let you do common 3d math +// operations without any vector, matrix, or quaternion +// classes or templates. +// +// a vector (or point) is a 'NxF32 *' to 3 floating point numbers. +// a matrix is a 'NxF32 *' to an array of 16 floating point numbers representing a 4x4 transformation matrix compatible with D3D or OGL +// a quaternion is a 'NxF32 *' to 4 floats representing a quaternion x,y,z,w +// +// + +/*! +** +** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com +** +** Portions of this source has been released with the PhysXViewer application, as well as +** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. +** +** If you find this code useful or you are feeling particularily generous I would +** ask that you please go to http://www.amillionpixels.us and make a donation +** to Troy DeMolay. +** +** DeMolay is a youth group for young men between the ages of 12 and 21. +** It teaches strong moral principles, as well as leadership skills and +** public speaking. The donations page uses the 'pay for pixels' paradigm +** where, in this case, a pixel is only a single penny. Donations can be +** made for as small as $4 or as high as a $100 block. Each person who donates +** will get a link to their own site as well as acknowledgement on the +** donations blog located here http://www.amillionpixels.blogspot.com/ +** +** If you wish to contact me you can use the following methods: +** +** Skype ID: jratcliff63367 +** Yahoo: jratcliff63367 +** AOL: jratcliff1961 +** email: jratcliffscarab@gmail.com +** +** +** The MIT license: +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is furnished +** to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. + +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include + +namespace CONVEX_DECOMPOSITION +{ + +enum FM_ClipState +{ + FMCS_XMIN = (1<<0), + FMCS_XMAX = (1<<1), + FMCS_YMIN = (1<<2), + FMCS_YMAX = (1<<3), + FMCS_ZMIN = (1<<4), + FMCS_ZMAX = (1<<5), +}; + +enum FM_Axis +{ + FM_XAXIS = (1<<0), + FM_YAXIS = (1<<1), + FM_ZAXIS = (1<<2) +}; + +enum LineSegmentType +{ + LS_START, + LS_MIDDLE, + LS_END +}; + + +const NxF32 FM_PI = 3.1415926535897932384626433832795028841971693993751f; +const NxF32 FM_DEG_TO_RAD = ((2.0f * FM_PI) / 360.0f); +const NxF32 FM_RAD_TO_DEG = (360.0f / (2.0f * FM_PI)); + +//***************** Float versions +//*** +//*** vectors are assumed to be 3 floats or 3 doubles representing X, Y, Z +//*** quaternions are assumed to be 4 floats or 4 doubles representing X,Y,Z,W +//*** matrices are assumed to be 16 floats or 16 doubles representing a standard D3D or OpenGL style 4x4 matrix +//*** bounding volumes are expressed as two sets of 3 floats/NxF64 representing bmin(x,y,z) and bmax(x,y,z) +//*** Plane equations are assumed to be 4 floats or 4 doubles representing Ax,By,Cz,D + +FM_Axis fm_getDominantAxis(const NxF32 normal[3]); +FM_Axis fm_getDominantAxis(const NxF64 normal[3]); + +void fm_decomposeTransform(const NxF32 local_transform[16],NxF32 trans[3],NxF32 rot[4],NxF32 scale[3]); +void fm_decomposeTransform(const NxF64 local_transform[16],NxF64 trans[3],NxF64 rot[4],NxF64 scale[3]); + +void fm_multiplyTransform(const NxF32 *pA,const NxF32 *pB,NxF32 *pM); +void fm_multiplyTransform(const NxF64 *pA,const NxF64 *pB,NxF64 *pM); + +void fm_inverseTransform(const NxF32 matrix[16],NxF32 inverse_matrix[16]); +void fm_inverseTransform(const NxF64 matrix[16],NxF64 inverse_matrix[16]); + +void fm_identity(NxF32 matrix[16]); // set 4x4 matrix to identity. +void fm_identity(NxF64 matrix[16]); // set 4x4 matrix to identity. + +void fm_inverseRT(const NxF32 matrix[16], const NxF32 pos[3], NxF32 t[3]); // inverse rotate translate the point. +void fm_inverseRT(const NxF64 matrix[16],const NxF64 pos[3],NxF64 t[3]); // inverse rotate translate the point. + +void fm_transform(const NxF32 matrix[16], const NxF32 pos[3], NxF32 t[3]); // rotate and translate this point. +void fm_transform(const NxF64 matrix[16],const NxF64 pos[3],NxF64 t[3]); // rotate and translate this point. + +NxF32 fm_getDeterminant(const NxF32 matrix[16]); +NxF64 fm_getDeterminant(const NxF64 matrix[16]); + +void fm_getSubMatrix(NxI32 ki,NxI32 kj,NxF32 pDst[16],const NxF32 matrix[16]); +void fm_getSubMatrix(NxI32 ki,NxI32 kj,NxF64 pDst[16],const NxF32 matrix[16]); + +void fm_rotate(const NxF32 matrix[16],const NxF32 pos[3],NxF32 t[3]); // only rotate the point by a 4x4 matrix, don't translate. +void fm_rotate(const NxF64 matri[16],const NxF64 pos[3],NxF64 t[3]); // only rotate the point by a 4x4 matrix, don't translate. + +void fm_eulerToMatrix(NxF32 ax,NxF32 ay,NxF32 az,NxF32 matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero) +void fm_eulerToMatrix(NxF64 ax,NxF64 ay,NxF64 az,NxF64 matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero) + +void fm_getAABB(NxU32 vcount,const NxF32 *points,NxU32 pstride,NxF32 bmin[3],NxF32 bmax[3]); +void fm_getAABB(NxU32 vcount,const NxF64 *points,NxU32 pstride,NxF64 bmin[3],NxF64 bmax[3]); + +void fm_getAABBCenter(const NxF32 bmin[3],const NxF32 bmax[3],NxF32 center[3]); +void fm_getAABBCenter(const NxF64 bmin[3],const NxF64 bmax[3],NxF64 center[3]); + +void fm_eulerToQuat(NxF32 x,NxF32 y,NxF32 z,NxF32 quat[4]); // convert euler angles to quaternion. +void fm_eulerToQuat(NxF64 x,NxF64 y,NxF64 z,NxF64 quat[4]); // convert euler angles to quaternion. + +void fm_quatToEuler(const NxF32 quat[4],NxF32 &ax,NxF32 &ay,NxF32 &az); +void fm_quatToEuler(const NxF64 quat[4],NxF64 &ax,NxF64 &ay,NxF64 &az); + +void fm_eulerToQuat(const NxF32 euler[3],NxF32 quat[4]); // convert euler angles to quaternion. Angles must be radians not degrees! +void fm_eulerToQuat(const NxF64 euler[3],NxF64 quat[4]); // convert euler angles to quaternion. + +void fm_scale(NxF32 x,NxF32 y,NxF32 z,NxF32 matrix[16]); // apply scale to the matrix. +void fm_scale(NxF64 x,NxF64 y,NxF64 z,NxF64 matrix[16]); // apply scale to the matrix. + +void fm_eulerToQuatDX(NxF32 x,NxF32 y,NxF32 z,NxF32 quat[4]); // convert euler angles to quaternion using the fucked up DirectX method +void fm_eulerToQuatDX(NxF64 x,NxF64 y,NxF64 z,NxF64 quat[4]); // convert euler angles to quaternion using the fucked up DirectX method + +void fm_eulerToMatrixDX(NxF32 x,NxF32 y,NxF32 z,NxF32 matrix[16]); // convert euler angles to quaternion using the fucked up DirectX method. +void fm_eulerToMatrixDX(NxF64 x,NxF64 y,NxF64 z,NxF64 matrix[16]); // convert euler angles to quaternion using the fucked up DirectX method. + +void fm_quatToMatrix(const NxF32 quat[4],NxF32 matrix[16]); // convert quaterinion rotation to matrix, translation set to zero. +void fm_quatToMatrix(const NxF64 quat[4],NxF64 matrix[16]); // convert quaterinion rotation to matrix, translation set to zero. + +void fm_quatRotate(const NxF32 quat[4],const NxF32 v[3],NxF32 r[3]); // rotate a vector directly by a quaternion. +void fm_quatRotate(const NxF64 quat[4],const NxF64 v[3],NxF64 r[3]); // rotate a vector directly by a quaternion. + +void fm_getTranslation(const NxF32 matrix[16],NxF32 t[3]); +void fm_getTranslation(const NxF64 matrix[16],NxF64 t[3]); + +void fm_setTranslation(const NxF32 *translation,NxF32 matrix[16]); +void fm_setTranslation(const NxF64 *translation,NxF64 matrix[16]); + +void fm_multiplyQuat(const NxF32 *qa,const NxF32 *qb,NxF32 *quat); +void fm_multiplyQuat(const NxF64 *qa,const NxF64 *qb,NxF64 *quat); + +void fm_matrixToQuat(const NxF32 matrix[16],NxF32 quat[4]); // convert the 3x3 portion of a 4x4 matrix into a quaterion as x,y,z,w +void fm_matrixToQuat(const NxF64 matrix[16],NxF64 quat[4]); // convert the 3x3 portion of a 4x4 matrix into a quaterion as x,y,z,w + +NxF32 fm_sphereVolume(NxF32 radius); // return's the volume of a sphere of this radius (4/3 PI * R cubed ) +NxF64 fm_sphereVolume(NxF64 radius); // return's the volume of a sphere of this radius (4/3 PI * R cubed ) + +NxF32 fm_cylinderVolume(NxF32 radius,NxF32 h); +NxF64 fm_cylinderVolume(NxF64 radius,NxF64 h); + +NxF32 fm_capsuleVolume(NxF32 radius,NxF32 h); +NxF64 fm_capsuleVolume(NxF64 radius,NxF64 h); + +NxF32 fm_distance(const NxF32 p1[3],const NxF32 p2[3]); +NxF64 fm_distance(const NxF64 p1[3],const NxF64 p2[3]); + +NxF32 fm_distanceSquared(const NxF32 p1[3],const NxF32 p2[3]); +NxF64 fm_distanceSquared(const NxF64 p1[3],const NxF64 p2[3]); + +NxF32 fm_distanceSquaredXZ(const NxF32 p1[3],const NxF32 p2[3]); +NxF64 fm_distanceSquaredXZ(const NxF64 p1[3],const NxF64 p2[3]); + +NxF32 fm_computePlane(const NxF32 p1[3],const NxF32 p2[3],const NxF32 p3[3],NxF32 *n); // return D +NxF64 fm_computePlane(const NxF64 p1[3],const NxF64 p2[3],const NxF64 p3[3],NxF64 *n); // return D + +NxF32 fm_distToPlane(const NxF32 plane[4],const NxF32 pos[3]); // computes the distance of this point from the plane. +NxF64 fm_distToPlane(const NxF64 plane[4],const NxF64 pos[3]); // computes the distance of this point from the plane. + +NxF32 fm_dot(const NxF32 p1[3],const NxF32 p2[3]); +NxF64 fm_dot(const NxF64 p1[3],const NxF64 p2[3]); + +void fm_cross(NxF32 cross[3],const NxF32 a[3],const NxF32 b[3]); +void fm_cross(NxF64 cross[3],const NxF64 a[3],const NxF64 b[3]); + +void fm_computeNormalVector(NxF32 n[3],const NxF32 p1[3],const NxF32 p2[3]); // as P2-P1 normalized. +void fm_computeNormalVector(NxF64 n[3],const NxF64 p1[3],const NxF64 p2[3]); // as P2-P1 normalized. + +bool fm_computeWindingOrder(const NxF32 p1[3],const NxF32 p2[3],const NxF32 p3[3]); // returns true if the triangle is clockwise. +bool fm_computeWindingOrder(const NxF64 p1[3],const NxF64 p2[3],const NxF64 p3[3]); // returns true if the triangle is clockwise. + +NxF32 fm_normalize(NxF32 n[3]); // normalize this vector and return the distance +NxF64 fm_normalize(NxF64 n[3]); // normalize this vector and return the distance + +void fm_matrixMultiply(const NxF32 A[16],const NxF32 B[16],NxF32 dest[16]); +void fm_matrixMultiply(const NxF64 A[16],const NxF64 B[16],NxF64 dest[16]); + +void fm_composeTransform(const NxF32 position[3],const NxF32 quat[4],const NxF32 scale[3],NxF32 matrix[16]); +void fm_composeTransform(const NxF64 position[3],const NxF64 quat[4],const NxF64 scale[3],NxF64 matrix[16]); + +NxF32 fm_computeArea(const NxF32 p1[3],const NxF32 p2[3],const NxF32 p3[3]); +NxF64 fm_computeArea(const NxF64 p1[3],const NxF64 p2[3],const NxF64 p3[3]); + +void fm_lerp(const NxF32 p1[3],const NxF32 p2[3],NxF32 dest[3],NxF32 lerpValue); +void fm_lerp(const NxF64 p1[3],const NxF64 p2[3],NxF64 dest[3],NxF64 lerpValue); + +bool fm_insideTriangleXZ(const NxF32 test[3],const NxF32 p1[3],const NxF32 p2[3],const NxF32 p3[3]); +bool fm_insideTriangleXZ(const NxF64 test[3],const NxF64 p1[3],const NxF64 p2[3],const NxF64 p3[3]); + +bool fm_insideAABB(const NxF32 pos[3],const NxF32 bmin[3],const NxF32 bmax[3]); +bool fm_insideAABB(const NxF64 pos[3],const NxF64 bmin[3],const NxF64 bmax[3]); + +bool fm_insideAABB(const NxF32 obmin[3],const NxF32 obmax[3],const NxF32 tbmin[3],const NxF32 tbmax[3]); // test if bounding box tbmin/tmbax is fully inside obmin/obmax +bool fm_insideAABB(const NxF64 obmin[3],const NxF64 obmax[3],const NxF64 tbmin[3],const NxF64 tbmax[3]); // test if bounding box tbmin/tmbax is fully inside obmin/obmax + +NxU32 fm_clipTestPoint(const NxF32 bmin[3],const NxF32 bmax[3],const NxF32 pos[3]); +NxU32 fm_clipTestPoint(const NxF64 bmin[3],const NxF64 bmax[3],const NxF64 pos[3]); + +NxU32 fm_clipTestPointXZ(const NxF32 bmin[3],const NxF32 bmax[3],const NxF32 pos[3]); // only tests X and Z, not Y +NxU32 fm_clipTestPointXZ(const NxF64 bmin[3],const NxF64 bmax[3],const NxF64 pos[3]); // only tests X and Z, not Y + + +NxU32 fm_clipTestAABB(const NxF32 bmin[3],const NxF32 bmax[3],const NxF32 p1[3],const NxF32 p2[3],const NxF32 p3[3],NxU32 &andCode); +NxU32 fm_clipTestAABB(const NxF64 bmin[3],const NxF64 bmax[3],const NxF64 p1[3],const NxF64 p2[3],const NxF64 p3[3],NxU32 &andCode); + + +bool fm_lineTestAABBXZ(const NxF32 p1[3],const NxF32 p2[3],const NxF32 bmin[3],const NxF32 bmax[3],NxF32 &time); +bool fm_lineTestAABBXZ(const NxF64 p1[3],const NxF64 p2[3],const NxF64 bmin[3],const NxF64 bmax[3],NxF64 &time); + +bool fm_lineTestAABB(const NxF32 p1[3],const NxF32 p2[3],const NxF32 bmin[3],const NxF32 bmax[3],NxF32 &time); +bool fm_lineTestAABB(const NxF64 p1[3],const NxF64 p2[3],const NxF64 bmin[3],const NxF64 bmax[3],NxF64 &time); + + +void fm_initMinMax(const NxF32 p[3],NxF32 bmin[3],NxF32 bmax[3]); +void fm_initMinMax(const NxF64 p[3],NxF64 bmin[3],NxF64 bmax[3]); + +void fm_initMinMax(NxF32 bmin[3],NxF32 bmax[3]); +void fm_initMinMax(NxF64 bmin[3],NxF64 bmax[3]); + +void fm_minmax(const NxF32 p[3],NxF32 bmin[3],NxF32 bmax[3]); // accmulate to a min-max value +void fm_minmax(const NxF64 p[3],NxF64 bmin[3],NxF64 bmax[3]); // accmulate to a min-max value + + +NxF32 fm_solveX(const NxF32 plane[4],NxF32 y,NxF32 z); // solve for X given this plane equation and the other two components. +NxF64 fm_solveX(const NxF64 plane[4],NxF64 y,NxF64 z); // solve for X given this plane equation and the other two components. + +NxF32 fm_solveY(const NxF32 plane[4],NxF32 x,NxF32 z); // solve for Y given this plane equation and the other two components. +NxF64 fm_solveY(const NxF64 plane[4],NxF64 x,NxF64 z); // solve for Y given this plane equation and the other two components. + +NxF32 fm_solveZ(const NxF32 plane[4],NxF32 x,NxF32 y); // solve for Z given this plane equation and the other two components. +NxF64 fm_solveZ(const NxF64 plane[4],NxF64 x,NxF64 y); // solve for Z given this plane equation and the other two components. + +bool fm_computeBestFitPlane(NxU32 vcount, // number of input data points + const NxF32 *points, // starting address of points array. + NxU32 vstride, // stride between input points. + const NxF32 *weights, // *optional point weighting values. + NxU32 wstride, // weight stride for each vertex. + NxF32 plane[4]); + +bool fm_computeBestFitPlane(NxU32 vcount, // number of input data points + const NxF64 *points, // starting address of points array. + NxU32 vstride, // stride between input points. + const NxF64 *weights, // *optional point weighting values. + NxU32 wstride, // weight stride for each vertex. + NxF64 plane[4]); + + +NxF32 fm_computeBestFitAABB(NxU32 vcount,const NxF32 *points,NxU32 pstride,NxF32 bmin[3],NxF32 bmax[3]); // returns the diagonal distance +NxF64 fm_computeBestFitAABB(NxU32 vcount,const NxF64 *points,NxU32 pstride,NxF64 bmin[3],NxF64 bmax[3]); // returns the diagonal distance + +NxF32 fm_computeBestFitSphere(NxU32 vcount,const NxF32 *points,NxU32 pstride,NxF32 center[3]); +NxF64 fm_computeBestFitSphere(NxU32 vcount,const NxF64 *points,NxU32 pstride,NxF64 center[3]); + +bool fm_lineSphereIntersect(const NxF32 center[3],NxF32 radius,const NxF32 p1[3],const NxF32 p2[3],NxF32 intersect[3]); +bool fm_lineSphereIntersect(const NxF64 center[3],NxF64 radius,const NxF64 p1[3],const NxF64 p2[3],NxF64 intersect[3]); + +bool fm_intersectRayAABB(const NxF32 bmin[3],const NxF32 bmax[3],const NxF32 pos[3],const NxF32 dir[3],NxF32 intersect[3]); +bool fm_intersectLineSegmentAABB(const NxF32 bmin[3],const NxF32 bmax[3],const NxF32 p1[3],const NxF32 p2[3],NxF32 intersect[3]); + +bool fm_lineIntersectsTriangle(const NxF32 rayStart[3],const NxF32 rayEnd[3],const NxF32 p1[3],const NxF32 p2[3],const NxF32 p3[3],NxF32 sect[3]); +bool fm_lineIntersectsTriangle(const NxF64 rayStart[3],const NxF64 rayEnd[3],const NxF64 p1[3],const NxF64 p2[3],const NxF64 p3[3],NxF64 sect[3]); + +bool fm_rayIntersectsTriangle(const NxF32 origin[3],const NxF32 dir[3],const NxF32 v0[3],const NxF32 v1[3],const NxF32 v2[3],NxF32 &t); +bool fm_rayIntersectsTriangle(const NxF64 origin[3],const NxF64 dir[3],const NxF64 v0[3],const NxF64 v1[3],const NxF64 v2[3],NxF64 &t); + +bool fm_raySphereIntersect(const NxF32 center[3],NxF32 radius,const NxF32 pos[3],const NxF32 dir[3],NxF32 distance,NxF32 intersect[3]); +bool fm_raySphereIntersect(const NxF64 center[3],NxF64 radius,const NxF64 pos[3],const NxF64 dir[3],NxF64 distance,NxF64 intersect[3]); + +void fm_catmullRom(NxF32 out_vector[3],const NxF32 p1[3],const NxF32 p2[3],const NxF32 p3[3],const NxF32 *p4, const NxF32 s); +void fm_catmullRom(NxF64 out_vector[3],const NxF64 p1[3],const NxF64 p2[3],const NxF64 p3[3],const NxF64 *p4, const NxF64 s); + +bool fm_intersectAABB(const NxF32 bmin1[3],const NxF32 bmax1[3],const NxF32 bmin2[3],const NxF32 bmax2[3]); +bool fm_intersectAABB(const NxF64 bmin1[3],const NxF64 bmax1[3],const NxF64 bmin2[3],const NxF64 bmax2[3]); + + +// computes the rotation quaternion to go from unit-vector v0 to unit-vector v1 +void fm_rotationArc(const NxF32 v0[3],const NxF32 v1[3],NxF32 quat[4]); +void fm_rotationArc(const NxF64 v0[3],const NxF64 v1[3],NxF64 quat[4]); + +NxF32 fm_distancePointLineSegment(const NxF32 Point[3],const NxF32 LineStart[3],const NxF32 LineEnd[3],NxF32 intersection[3],LineSegmentType &type,NxF32 epsilon); +NxF64 fm_distancePointLineSegment(const NxF64 Point[3],const NxF64 LineStart[3],const NxF64 LineEnd[3],NxF64 intersection[3],LineSegmentType &type,NxF64 epsilon); + + +bool fm_colinear(const NxF64 p1[3],const NxF64 p2[3],const NxF64 p3[3],NxF64 epsilon=0.999); // true if these three points in a row are co-linear +bool fm_colinear(const NxF32 p1[3],const NxF32 p2[3],const NxF32 p3[3],NxF32 epsilon=0.999f); + +bool fm_colinear(const NxF32 a1[3],const NxF32 a2[3],const NxF32 b1[3],const NxF32 b2[3],NxF32 epsilon=0.999f); // true if these two line segments are co-linear. +bool fm_colinear(const NxF64 a1[3],const NxF64 a2[3],const NxF64 b1[3],const NxF64 b2[3],NxF64 epsilon=0.999); // true if these two line segments are co-linear. + +enum IntersectResult +{ + IR_DONT_INTERSECT, + IR_DO_INTERSECT, + IR_COINCIDENT, + IR_PARALLEL, +}; + +IntersectResult fm_intersectLineSegments2d(const NxF32 a1[3], const NxF32 a2[3], const NxF32 b1[3], const NxF32 b2[3], NxF32 intersectionPoint[3]); +IntersectResult fm_intersectLineSegments2d(const NxF64 a1[3],const NxF64 a2[3],const NxF64 b1[3],const NxF64 b2[3],NxF64 intersectionPoint[3]); + +IntersectResult fm_intersectLineSegments2dTime(const NxF32 a1[3], const NxF32 a2[3], const NxF32 b1[3], const NxF32 b2[3],NxF32 &t1,NxF32 &t2); +IntersectResult fm_intersectLineSegments2dTime(const NxF64 a1[3],const NxF64 a2[3],const NxF64 b1[3],const NxF64 b2[3],NxF64 &t1,NxF64 &t2); + +// Plane-Triangle splitting + +enum PlaneTriResult +{ + PTR_ON_PLANE, + PTR_FRONT, + PTR_BACK, + PTR_SPLIT, +}; + +PlaneTriResult fm_planeTriIntersection(const NxF32 plane[4], // the plane equation in Ax+By+Cz+D format + const NxF32 *triangle, // the source triangle. + NxU32 tstride, // stride in bytes of the input and output *vertices* + NxF32 epsilon, // the co-planer epsilon value. + NxF32 *front, // the triangle in front of the + NxU32 &fcount, // number of vertices in the 'front' triangle + NxF32 *back, // the triangle in back of the plane + NxU32 &bcount); // the number of vertices in the 'back' triangle. + + +PlaneTriResult fm_planeTriIntersection(const NxF64 plane[4], // the plane equation in Ax+By+Cz+D format + const NxF64 *triangle, // the source triangle. + NxU32 tstride, // stride in bytes of the input and output *vertices* + NxF64 epsilon, // the co-planer epsilon value. + NxF64 *front, // the triangle in front of the + NxU32 &fcount, // number of vertices in the 'front' triangle + NxF64 *back, // the triangle in back of the plane + NxU32 &bcount); // the number of vertices in the 'back' triangle. + + +void fm_intersectPointPlane(const NxF32 p1[3],const NxF32 p2[3],NxF32 *split,const NxF32 plane[4]); +void fm_intersectPointPlane(const NxF64 p1[3],const NxF64 p2[3],NxF64 *split,const NxF64 plane[4]); + +PlaneTriResult fm_getSidePlane(const NxF32 p[3],const NxF32 plane[4],NxF32 epsilon); +PlaneTriResult fm_getSidePlane(const NxF64 p[3],const NxF64 plane[4],NxF64 epsilon); + + +void fm_computeBestFitOBB(NxU32 vcount,const NxF32 *points,NxU32 pstride,NxF32 *sides,NxF32 matrix[16],bool bruteForce=true); +void fm_computeBestFitOBB(NxU32 vcount,const NxF64 *points,NxU32 pstride,NxF64 *sides,NxF64 matrix[16],bool bruteForce=true); + +void fm_computeBestFitOBB(NxU32 vcount,const NxF32 *points,NxU32 pstride,NxF32 *sides,NxF32 pos[3],NxF32 quat[4],bool bruteForce=true); +void fm_computeBestFitOBB(NxU32 vcount,const NxF64 *points,NxU32 pstride,NxF64 *sides,NxF64 pos[3],NxF64 quat[4],bool bruteForce=true); + +void fm_computeBestFitABB(NxU32 vcount,const NxF32 *points,NxU32 pstride,NxF32 *sides,NxF32 pos[3]); +void fm_computeBestFitABB(NxU32 vcount,const NxF64 *points,NxU32 pstride,NxF64 *sides,NxF64 pos[3]); + + +//** Note, if the returned capsule height is less than zero, then you must represent it is a sphere of size radius. +void fm_computeBestFitCapsule(NxU32 vcount,const NxF32 *points,NxU32 pstride,NxF32 &radius,NxF32 &height,NxF32 matrix[16],bool bruteForce=true); +void fm_computeBestFitCapsule(NxU32 vcount,const NxF64 *points,NxU32 pstride,NxF32 &radius,NxF32 &height,NxF64 matrix[16],bool bruteForce=true); + + +void fm_planeToMatrix(const NxF32 plane[4],NxF32 matrix[16]); // convert a plane equation to a 4x4 rotation matrix. Reference vector is 0,1,0 +void fm_planeToQuat(const NxF32 plane[4],NxF32 quat[4],NxF32 pos[3]); // convert a plane equation to a quaternion and translation + +void fm_planeToMatrix(const NxF64 plane[4],NxF64 matrix[16]); // convert a plane equation to a 4x4 rotation matrix +void fm_planeToQuat(const NxF64 plane[4],NxF64 quat[4],NxF64 pos[3]); // convert a plane equation to a quaternion and translation + +inline void fm_doubleToFloat3(const NxF64 p[3],NxF32 t[3]) { t[0] = (NxF32) p[0]; t[1] = (NxF32)p[1]; t[2] = (NxF32)p[2]; }; +inline void fm_floatToDouble3(const NxF32 p[3],NxF64 t[3]) { t[0] = (NxF64)p[0]; t[1] = (NxF64)p[1]; t[2] = (NxF64)p[2]; }; + + +void fm_eulerMatrix(NxF32 ax,NxF32 ay,NxF32 az,NxF32 matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero) +void fm_eulerMatrix(NxF64 ax,NxF64 ay,NxF64 az,NxF64 matrix[16]); // convert euler (in radians) to a dest 4x4 matrix (translation set to zero) + + +NxF32 fm_computeMeshVolume(const NxF32 *vertices,NxU32 tcount,const NxU32 *indices); +NxF64 fm_computeMeshVolume(const NxF64 *vertices,NxU32 tcount,const NxU32 *indices); + + +#define FM_DEFAULT_GRANULARITY 0.001f // 1 millimeter is the default granularity + +class fm_VertexIndex +{ +public: + virtual NxU32 getIndex(const NxF32 pos[3],bool &newPos) = 0; // get welded index for this NxF32 vector[3] + virtual NxU32 getIndex(const NxF64 pos[3],bool &newPos) = 0; // get welded index for this NxF64 vector[3] + virtual const NxF32 * getVerticesFloat(void) const = 0; + virtual const NxF64 * getVerticesDouble(void) const = 0; + virtual const NxF32 * getVertexFloat(NxU32 index) const = 0; + virtual const NxF64 * getVertexDouble(NxU32 index) const = 0; + virtual NxU32 getVcount(void) const = 0; + virtual bool isDouble(void) const = 0; + virtual bool saveAsObj(const char *fname,NxU32 tcount,NxU32 *indices) = 0; +}; + +fm_VertexIndex * fm_createVertexIndex(NxF64 granularity,bool snapToGrid); // create an indexed vertex system for doubles +fm_VertexIndex * fm_createVertexIndex(NxF32 granularity,bool snapToGrid); // create an indexed vertext system for floats +void fm_releaseVertexIndex(fm_VertexIndex *vindex); + + + +#if 0 // currently disabled + +class fm_LineSegment +{ +public: + fm_LineSegment(void) + { + mE1 = mE2 = 0; + } + + fm_LineSegment(NxU32 e1,NxU32 e2) + { + mE1 = e1; + mE2 = e2; + } + + NxU32 mE1; + NxU32 mE2; +}; + + +// LineSweep *only* supports doublees. As a geometric operation it needs as much precision as possible. +class fm_LineSweep +{ +public: + + virtual fm_LineSegment * performLineSweep(const fm_LineSegment *segments, + NxU32 icount, + const NxF64 *planeEquation, + fm_VertexIndex *pool, + NxU32 &scount) = 0; + + +}; + +fm_LineSweep * fm_createLineSweep(void); +void fm_releaseLineSweep(fm_LineSweep *sweep); + +#endif + +class fm_Triangulate +{ +public: + virtual const NxF64 * triangulate3d(NxU32 pcount, + const NxF64 *points, + NxU32 vstride, + NxU32 &tcount, + bool consolidate, + NxF64 epsilon) = 0; + + virtual const NxF32 * triangulate3d(NxU32 pcount, + const NxF32 *points, + NxU32 vstride, + NxU32 &tcount, + bool consolidate, + NxF32 epsilon) = 0; +}; + +fm_Triangulate * fm_createTriangulate(void); +void fm_releaseTriangulate(fm_Triangulate *t); + + +const NxF32 * fm_getPoint(const NxF32 *points,NxU32 pstride,NxU32 index); +const NxF64 * fm_getPoint(const NxF64 *points,NxU32 pstride,NxU32 index); + +bool fm_insideTriangle(NxF32 Ax, NxF32 Ay,NxF32 Bx, NxF32 By,NxF32 Cx, NxF32 Cy,NxF32 Px, NxF32 Py); +bool fm_insideTriangle(NxF64 Ax, NxF64 Ay,NxF64 Bx, NxF64 By,NxF64 Cx, NxF64 Cy,NxF64 Px, NxF64 Py); +NxF32 fm_areaPolygon2d(NxU32 pcount,const NxF32 *points,NxU32 pstride); +NxF64 fm_areaPolygon2d(NxU32 pcount,const NxF64 *points,NxU32 pstride); + +bool fm_pointInsidePolygon2d(NxU32 pcount,const NxF32 *points,NxU32 pstride,const NxF32 *point,NxU32 xindex=0,NxU32 yindex=1); +bool fm_pointInsidePolygon2d(NxU32 pcount,const NxF64 *points,NxU32 pstride,const NxF64 *point,NxU32 xindex=0,NxU32 yindex=1); + +NxU32 fm_consolidatePolygon(NxU32 pcount,const NxF32 *points,NxU32 pstride,NxF32 *dest,NxF32 epsilon=0.999999f); // collapses co-linear edges. +NxU32 fm_consolidatePolygon(NxU32 pcount,const NxF64 *points,NxU32 pstride,NxF64 *dest,NxF64 epsilon=0.999999); // collapses co-linear edges. + + +bool fm_computeSplitPlane(NxU32 vcount,const NxF64 *vertices,NxU32 tcount,const NxU32 *indices,NxF64 *plane); +bool fm_computeSplitPlane(NxU32 vcount,const NxF32 *vertices,NxU32 tcount,const NxU32 *indices,NxF32 *plane); + +void fm_nearestPointInTriangle(const NxF32 *pos,const NxF32 *p1,const NxF32 *p2,const NxF32 *p3,NxF32 *nearest); +void fm_nearestPointInTriangle(const NxF64 *pos,const NxF64 *p1,const NxF64 *p2,const NxF64 *p3,NxF64 *nearest); + +NxF32 fm_areaTriangle(const NxF32 *p1,const NxF32 *p2,const NxF32 *p3); +NxF64 fm_areaTriangle(const NxF64 *p1,const NxF64 *p2,const NxF64 *p3); + +void fm_subtract(const NxF32 *A,const NxF32 *B,NxF32 *diff); // compute A-B and store the result in 'diff' +void fm_subtract(const NxF64 *A,const NxF64 *B,NxF64 *diff); // compute A-B and store the result in 'diff' + +void fm_multiply(NxF32 *A,NxF32 scaler); +void fm_multiply(NxF64 *A,NxF64 scaler); + +void fm_add(const NxF32 *A,const NxF32 *B,NxF32 *sum); +void fm_add(const NxF64 *A,const NxF64 *B,NxF64 *sum); + +void fm_copy3(const NxF32 *source,NxF32 *dest); +void fm_copy3(const NxF64 *source,NxF64 *dest); + +// re-indexes an indexed triangle mesh but drops unused vertices. The output_indices can be the same pointer as the input indices. +// the output_vertices can point to the input vertices if you desire. The output_vertices buffer should be at least the same size +// is the input buffer. The routine returns the new vertex count after re-indexing. +NxU32 fm_copyUniqueVertices(NxU32 vcount,const NxF32 *input_vertices,NxF32 *output_vertices,NxU32 tcount,const NxU32 *input_indices,NxU32 *output_indices); +NxU32 fm_copyUniqueVertices(NxU32 vcount,const NxF64 *input_vertices,NxF64 *output_vertices,NxU32 tcount,const NxU32 *input_indices,NxU32 *output_indices); + +bool fm_isMeshCoplanar(NxU32 tcount,const NxU32 *indices,const NxF32 *vertices,bool doubleSided); // returns true if this collection of indexed triangles are co-planar! +bool fm_isMeshCoplanar(NxU32 tcount,const NxU32 *indices,const NxF64 *vertices,bool doubleSided); // returns true if this collection of indexed triangles are co-planar! + +bool fm_samePlane(const NxF32 p1[4],const NxF32 p2[4],NxF32 normalEpsilon=0.01f,NxF32 dEpsilon=0.001f,bool doubleSided=false); // returns true if these two plane equations are identical within an epsilon +bool fm_samePlane(const NxF64 p1[4],const NxF64 p2[4],NxF64 normalEpsilon=0.01,NxF64 dEpsilon=0.001,bool doubleSided=false); + +void fm_OBBtoAABB(const NxF32 obmin[3],const NxF32 obmax[3],const NxF32 matrix[16],NxF32 abmin[3],NxF32 abmax[3]); + +// a utility class that will tesseleate a mesh. +class fm_Tesselate +{ +public: + virtual const NxU32 * tesselate(fm_VertexIndex *vindex,NxU32 tcount,const NxU32 *indices,NxF32 longEdge,NxU32 maxDepth,NxU32 &outcount) = 0; +}; + +fm_Tesselate * fm_createTesselate(void); +void fm_releaseTesselate(fm_Tesselate *t); + +void fm_computeMeanNormals(NxU32 vcount, // the number of vertices + const NxF32 *vertices, // the base address of the vertex position data. + NxU32 vstride, // the stride between position data. + NxF32 *normals, // the base address of the destination for mean vector normals + NxU32 nstride, // the stride between normals + NxU32 tcount, // the number of triangles + const NxU32 *indices); // the triangle indices + +void fm_computeMeanNormals(NxU32 vcount, // the number of vertices + const NxF64 *vertices, // the base address of the vertex position data. + NxU32 vstride, // the stride between position data. + NxF64 *normals, // the base address of the destination for mean vector normals + NxU32 nstride, // the stride between normals + NxU32 tcount, // the number of triangles + const NxU32 *indices); // the triangle indices + + +bool fm_isValidTriangle(const NxF32 *p1,const NxF32 *p2,const NxF32 *p3,NxF32 epsilon=0.00001f); +bool fm_isValidTriangle(const NxF64 *p1,const NxF64 *p2,const NxF64 *p3,NxF64 epsilon=0.00001f); + +}; // end of namespace + +#endif diff --git a/Engine/lib/convexDecomp/NvFloatMath.inl b/Engine/lib/convexDecomp/NvFloatMath.inl new file mode 100644 index 000000000..b8731013a --- /dev/null +++ b/Engine/lib/convexDecomp/NvFloatMath.inl @@ -0,0 +1,5607 @@ +// a set of routines that let you do common 3d math +// operations without any vector, matrix, or quaternion +// classes or templates. +// +// a vector (or point) is a 'NxF32 *' to 3 floating point numbers. +// a matrix is a 'NxF32 *' to an array of 16 floating point numbers representing a 4x4 transformation matrix compatible with D3D or OGL +// a quaternion is a 'NxF32 *' to 4 floats representing a quaternion x,y,z,w +// +// +/*! +** +** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com +** +** Portions of this source has been released with the PhysXViewer application, as well as +** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. +** +** If you find this code useful or you are feeling particularily generous I would +** ask that you please go to http://www.amillionpixels.us and make a donation +** to Troy DeMolay. +** +** DeMolay is a youth group for young men between the ages of 12 and 21. +** It teaches strong moral principles, as well as leadership skills and +** public speaking. The donations page uses the 'pay for pixels' paradigm +** where, in this case, a pixel is only a single penny. Donations can be +** made for as small as $4 or as high as a $100 block. Each person who donates +** will get a link to their own site as well as acknowledgement on the +** donations blog located here http://www.amillionpixels.blogspot.com/ +** +** If you wish to contact me you can use the following methods: +** +** Skype ID: jratcliff63367 +** Yahoo: jratcliff63367 +** AOL: jratcliff1961 +** email: jratcliffscarab@gmail.com +** +** +** The MIT license: +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is furnished +** to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. + +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ +#pragma warning(disable:4996) + +#include "NvUserMemAlloc.h" +#include "NvHashMap.h" + +namespace CONVEX_DECOMPOSITION +{ + +void fm_inverseRT(const REAL matrix[16],const REAL pos[3],REAL t[3]) // inverse rotate translate the point. +{ + + REAL _x = pos[0] - matrix[3*4+0]; + REAL _y = pos[1] - matrix[3*4+1]; + REAL _z = pos[2] - matrix[3*4+2]; + + // Multiply inverse-translated source vector by inverted rotation transform + + t[0] = (matrix[0*4+0] * _x) + (matrix[0*4+1] * _y) + (matrix[0*4+2] * _z); + t[1] = (matrix[1*4+0] * _x) + (matrix[1*4+1] * _y) + (matrix[1*4+2] * _z); + t[2] = (matrix[2*4+0] * _x) + (matrix[2*4+1] * _y) + (matrix[2*4+2] * _z); + +} + +REAL fm_getDeterminant(const REAL matrix[16]) +{ + REAL tempv[3]; + REAL p0[3]; + REAL p1[3]; + REAL p2[3]; + + + p0[0] = matrix[0*4+0]; + p0[1] = matrix[0*4+1]; + p0[2] = matrix[0*4+2]; + + p1[0] = matrix[1*4+0]; + p1[1] = matrix[1*4+1]; + p1[2] = matrix[1*4+2]; + + p2[0] = matrix[2*4+0]; + p2[1] = matrix[2*4+1]; + p2[2] = matrix[2*4+2]; + + fm_cross(tempv,p1,p2); + + return fm_dot(p0,tempv); + +} + +REAL fm_squared(REAL x) { return x*x; }; + +void fm_decomposeTransform(const REAL local_transform[16],REAL trans[3],REAL rot[4],REAL scale[3]) +{ + + trans[0] = local_transform[12]; + trans[1] = local_transform[13]; + trans[2] = local_transform[14]; + + scale[0] = sqrt(fm_squared(local_transform[0*4+0]) + fm_squared(local_transform[0*4+1]) + fm_squared(local_transform[0*4+2])); + scale[1] = sqrt(fm_squared(local_transform[1*4+0]) + fm_squared(local_transform[1*4+1]) + fm_squared(local_transform[1*4+2])); + scale[2] = sqrt(fm_squared(local_transform[2*4+0]) + fm_squared(local_transform[2*4+1]) + fm_squared(local_transform[2*4+2])); + + REAL m[16]; + memcpy(m,local_transform,sizeof(REAL)*16); + + REAL sx = 1.0f / scale[0]; + REAL sy = 1.0f / scale[1]; + REAL sz = 1.0f / scale[2]; + + m[0*4+0]*=sx; + m[0*4+1]*=sx; + m[0*4+2]*=sx; + + m[1*4+0]*=sy; + m[1*4+1]*=sy; + m[1*4+2]*=sy; + + m[2*4+0]*=sz; + m[2*4+1]*=sz; + m[2*4+2]*=sz; + + fm_matrixToQuat(m,rot); + +} + +void fm_getSubMatrix(NxI32 ki,NxI32 kj,REAL pDst[16],const REAL matrix[16]) +{ + NxI32 row, col; + NxI32 dstCol = 0, dstRow = 0; + + for ( col = 0; col < 4; col++ ) + { + if ( col == kj ) + { + continue; + } + for ( dstRow = 0, row = 0; row < 4; row++ ) + { + if ( row == ki ) + { + continue; + } + pDst[dstCol*4+dstRow] = matrix[col*4+row]; + dstRow++; + } + dstCol++; + } +} + +void fm_inverseTransform(const REAL matrix[16],REAL inverse_matrix[16]) +{ + REAL determinant = fm_getDeterminant(matrix); + determinant = 1.0f / determinant; + for (NxI32 i = 0; i < 4; i++ ) + { + for (NxI32 j = 0; j < 4; j++ ) + { + NxI32 sign = 1 - ( ( i + j ) % 2 ) * 2; + REAL subMat[16]; + fm_identity(subMat); + fm_getSubMatrix( i, j, subMat, matrix ); + REAL subDeterminant = fm_getDeterminant(subMat); + inverse_matrix[i*4+j] = ( subDeterminant * sign ) * determinant; + } + } +} + +void fm_identity(REAL matrix[16]) // set 4x4 matrix to identity. +{ + matrix[0*4+0] = 1; + matrix[1*4+1] = 1; + matrix[2*4+2] = 1; + matrix[3*4+3] = 1; + + matrix[1*4+0] = 0; + matrix[2*4+0] = 0; + matrix[3*4+0] = 0; + + matrix[0*4+1] = 0; + matrix[2*4+1] = 0; + matrix[3*4+1] = 0; + + matrix[0*4+2] = 0; + matrix[1*4+2] = 0; + matrix[3*4+2] = 0; + + matrix[0*4+3] = 0; + matrix[1*4+3] = 0; + matrix[2*4+3] = 0; + +} + +void fm_quatToEuler(const REAL quat[4],REAL &ax,REAL &ay,REAL &az) +{ + REAL x = quat[0]; + REAL y = quat[1]; + REAL z = quat[2]; + REAL w = quat[3]; + + REAL sint = (2.0f * w * y) - (2.0f * x * z); + REAL cost_temp = 1.0f - (sint * sint); + REAL cost = 0; + + if ( (REAL)fabs(cost_temp) > 0.001f ) + { + cost = sqrt( cost_temp ); + } + + REAL sinv, cosv, sinf, cosf; + if ( (REAL)fabs(cost) > 0.001f ) + { + cost = 1.0f / cost; + sinv = ((2.0f * y * z) + (2.0f * w * x)) * cost; + cosv = (1.0f - (2.0f * x * x) - (2.0f * y * y)) * cost; + sinf = ((2.0f * x * y) + (2.0f * w * z)) * cost; + cosf = (1.0f - (2.0f * y * y) - (2.0f * z * z)) * cost; + } + else + { + sinv = (2.0f * w * x) - (2.0f * y * z); + cosv = 1.0f - (2.0f * x * x) - (2.0f * z * z); + sinf = 0; + cosf = 1.0f; + } + + // compute output rotations + ax = atan2( sinv, cosv ); + ay = atan2( sint, cost ); + az = atan2( sinf, cosf ); + +} + +void fm_eulerToMatrix(REAL ax,REAL ay,REAL az,REAL *matrix) // convert euler (in radians) to a dest 4x4 matrix (translation set to zero) +{ + REAL quat[4]; + fm_eulerToQuat(ax,ay,az,quat); + fm_quatToMatrix(quat,matrix); +} + +void fm_getAABB(NxU32 vcount,const REAL *points,NxU32 pstride,REAL *bmin,REAL *bmax) +{ + + const NxU8 *source = (const NxU8 *) points; + + bmin[0] = points[0]; + bmin[1] = points[1]; + bmin[2] = points[2]; + + bmax[0] = points[0]; + bmax[1] = points[1]; + bmax[2] = points[2]; + + + for (NxU32 i=1; i bmax[0] ) bmax[0] = p[0]; + if ( p[1] > bmax[1] ) bmax[1] = p[1]; + if ( p[2] > bmax[2] ) bmax[2] = p[2]; + + } +} + +void fm_eulerToQuat(const REAL *euler,REAL *quat) // convert euler angles to quaternion. +{ + fm_eulerToQuat(euler[0],euler[1],euler[2],quat); +} + +void fm_eulerToQuat(REAL roll,REAL pitch,REAL yaw,REAL *quat) // convert euler angles to quaternion. +{ + roll *= 0.5f; + pitch *= 0.5f; + yaw *= 0.5f; + + REAL cr = cos(roll); + REAL cp = cos(pitch); + REAL cy = cos(yaw); + + REAL sr = sin(roll); + REAL sp = sin(pitch); + REAL sy = sin(yaw); + + REAL cpcy = cp * cy; + REAL spsy = sp * sy; + REAL spcy = sp * cy; + REAL cpsy = cp * sy; + + quat[0] = ( sr * cpcy - cr * spsy); + quat[1] = ( cr * spcy + sr * cpsy); + quat[2] = ( cr * cpsy - sr * spcy); + quat[3] = cr * cpcy + sr * spsy; +} + +void fm_quatToMatrix(const REAL *quat,REAL *matrix) // convert quaterinion rotation to matrix, zeros out the translation component. +{ + + REAL xx = quat[0]*quat[0]; + REAL yy = quat[1]*quat[1]; + REAL zz = quat[2]*quat[2]; + REAL xy = quat[0]*quat[1]; + REAL xz = quat[0]*quat[2]; + REAL yz = quat[1]*quat[2]; + REAL wx = quat[3]*quat[0]; + REAL wy = quat[3]*quat[1]; + REAL wz = quat[3]*quat[2]; + + matrix[0*4+0] = 1 - 2 * ( yy + zz ); + matrix[1*4+0] = 2 * ( xy - wz ); + matrix[2*4+0] = 2 * ( xz + wy ); + + matrix[0*4+1] = 2 * ( xy + wz ); + matrix[1*4+1] = 1 - 2 * ( xx + zz ); + matrix[2*4+1] = 2 * ( yz - wx ); + + matrix[0*4+2] = 2 * ( xz - wy ); + matrix[1*4+2] = 2 * ( yz + wx ); + matrix[2*4+2] = 1 - 2 * ( xx + yy ); + + matrix[3*4+0] = matrix[3*4+1] = matrix[3*4+2] = (REAL) 0.0f; + matrix[0*4+3] = matrix[1*4+3] = matrix[2*4+3] = (REAL) 0.0f; + matrix[3*4+3] =(REAL) 1.0f; + +} + + +void fm_quatRotate(const REAL *quat,const REAL *v,REAL *r) // rotate a vector directly by a quaternion. +{ + REAL left[4]; + + left[0] = quat[3]*v[0] + quat[1]*v[2] - v[1]*quat[2]; + left[1] = quat[3]*v[1] + quat[2]*v[0] - v[2]*quat[0]; + left[2] = quat[3]*v[2] + quat[0]*v[1] - v[0]*quat[1]; + left[3] = - quat[0]*v[0] - quat[1]*v[1] - quat[2]*v[2]; + + r[0] = (left[3]*-quat[0]) + (quat[3]*left[0]) + (left[1]*-quat[2]) - (-quat[1]*left[2]); + r[1] = (left[3]*-quat[1]) + (quat[3]*left[1]) + (left[2]*-quat[0]) - (-quat[2]*left[0]); + r[2] = (left[3]*-quat[2]) + (quat[3]*left[2]) + (left[0]*-quat[1]) - (-quat[0]*left[1]); + +} + + +void fm_getTranslation(const REAL *matrix,REAL *t) +{ + t[0] = matrix[3*4+0]; + t[1] = matrix[3*4+1]; + t[2] = matrix[3*4+2]; +} + +void fm_matrixToQuat(const REAL *matrix,REAL *quat) // convert the 3x3 portion of a 4x4 matrix into a quaterion as x,y,z,w +{ + + REAL tr = matrix[0*4+0] + matrix[1*4+1] + matrix[2*4+2]; + + // check the diagonal + + if (tr > 0.0f ) + { + REAL s = (REAL) sqrt ( (NxF64) (tr + 1.0f) ); + quat[3] = s * 0.5f; + s = 0.5f / s; + quat[0] = (matrix[1*4+2] - matrix[2*4+1]) * s; + quat[1] = (matrix[2*4+0] - matrix[0*4+2]) * s; + quat[2] = (matrix[0*4+1] - matrix[1*4+0]) * s; + + } + else + { + // diagonal is negative + NxI32 nxt[3] = {1, 2, 0}; + REAL qa[4]; + + NxI32 i = 0; + + if (matrix[1*4+1] > matrix[0*4+0]) i = 1; + if (matrix[2*4+2] > matrix[i*4+i]) i = 2; + + NxI32 j = nxt[i]; + NxI32 k = nxt[j]; + + REAL s = sqrt ( ((matrix[i*4+i] - (matrix[j*4+j] + matrix[k*4+k])) + 1.0f) ); + + qa[i] = s * 0.5f; + + if (s != 0.0f ) s = 0.5f / s; + + qa[3] = (matrix[j*4+k] - matrix[k*4+j]) * s; + qa[j] = (matrix[i*4+j] + matrix[j*4+i]) * s; + qa[k] = (matrix[i*4+k] + matrix[k*4+i]) * s; + + quat[0] = qa[0]; + quat[1] = qa[1]; + quat[2] = qa[2]; + quat[3] = qa[3]; + } + + +} + + +REAL fm_sphereVolume(REAL radius) // return's the volume of a sphere of this radius (4/3 PI * R cubed ) +{ + return (4.0f / 3.0f ) * FM_PI * radius * radius * radius; +} + + +REAL fm_cylinderVolume(REAL radius,REAL h) +{ + return FM_PI * radius * radius *h; +} + +REAL fm_capsuleVolume(REAL radius,REAL h) +{ + REAL volume = fm_sphereVolume(radius); // volume of the sphere portion. + REAL ch = h-radius*2; // this is the cylinder length + if ( ch > 0 ) + { + volume+=fm_cylinderVolume(radius,ch); + } + return volume; +} + +void fm_transform(const REAL matrix[16],const REAL v[3],REAL t[3]) // rotate and translate this point +{ + if ( matrix ) + { + REAL tx = (matrix[0*4+0] * v[0]) + (matrix[1*4+0] * v[1]) + (matrix[2*4+0] * v[2]) + matrix[3*4+0]; + REAL ty = (matrix[0*4+1] * v[0]) + (matrix[1*4+1] * v[1]) + (matrix[2*4+1] * v[2]) + matrix[3*4+1]; + REAL tz = (matrix[0*4+2] * v[0]) + (matrix[1*4+2] * v[1]) + (matrix[2*4+2] * v[2]) + matrix[3*4+2]; + t[0] = tx; + t[1] = ty; + t[2] = tz; + } + else + { + t[0] = v[0]; + t[1] = v[1]; + t[2] = v[2]; + } +} + +void fm_rotate(const REAL matrix[16],const REAL v[3],REAL t[3]) // rotate and translate this point +{ + if ( matrix ) + { + REAL tx = (matrix[0*4+0] * v[0]) + (matrix[1*4+0] * v[1]) + (matrix[2*4+0] * v[2]); + REAL ty = (matrix[0*4+1] * v[0]) + (matrix[1*4+1] * v[1]) + (matrix[2*4+1] * v[2]); + REAL tz = (matrix[0*4+2] * v[0]) + (matrix[1*4+2] * v[1]) + (matrix[2*4+2] * v[2]); + t[0] = tx; + t[1] = ty; + t[2] = tz; + } + else + { + t[0] = v[0]; + t[1] = v[1]; + t[2] = v[2]; + } +} + + +REAL fm_distance(const REAL *p1,const REAL *p2) +{ + REAL dx = p1[0] - p2[0]; + REAL dy = p1[1] - p2[1]; + REAL dz = p1[2] - p2[2]; + + return sqrt( dx*dx + dy*dy + dz *dz ); +} + +REAL fm_distanceSquared(const REAL *p1,const REAL *p2) +{ + REAL dx = p1[0] - p2[0]; + REAL dy = p1[1] - p2[1]; + REAL dz = p1[2] - p2[2]; + + return dx*dx + dy*dy + dz *dz; +} + + +REAL fm_distanceSquaredXZ(const REAL *p1,const REAL *p2) +{ + REAL dx = p1[0] - p2[0]; + REAL dz = p1[2] - p2[2]; + + return dx*dx + dz *dz; +} + + +REAL fm_computePlane(const REAL *A,const REAL *B,const REAL *C,REAL *n) // returns D +{ + REAL vx = (B[0] - C[0]); + REAL vy = (B[1] - C[1]); + REAL vz = (B[2] - C[2]); + + REAL wx = (A[0] - B[0]); + REAL wy = (A[1] - B[1]); + REAL wz = (A[2] - B[2]); + + REAL vw_x = vy * wz - vz * wy; + REAL vw_y = vz * wx - vx * wz; + REAL vw_z = vx * wy - vy * wx; + + REAL mag = sqrt((vw_x * vw_x) + (vw_y * vw_y) + (vw_z * vw_z)); + + if ( mag < 0.000001f ) + { + mag = 0; + } + else + { + mag = 1.0f/mag; + } + + REAL x = vw_x * mag; + REAL y = vw_y * mag; + REAL z = vw_z * mag; + + + REAL D = 0.0f - ((x*A[0])+(y*A[1])+(z*A[2])); + + n[0] = x; + n[1] = y; + n[2] = z; + + return D; +} + +REAL fm_distToPlane(const REAL *plane,const REAL *p) // computes the distance of this point from the plane. +{ + return p[0]*plane[0]+p[1]*plane[1]+p[2]*plane[2]+plane[3]; +} + +REAL fm_dot(const REAL *p1,const REAL *p2) +{ + return p1[0]*p2[0]+p1[1]*p2[1]+p1[2]*p2[2]; +} + +void fm_cross(REAL *cross,const REAL *a,const REAL *b) +{ + cross[0] = a[1]*b[2] - a[2]*b[1]; + cross[1] = a[2]*b[0] - a[0]*b[2]; + cross[2] = a[0]*b[1] - a[1]*b[0]; +} + +void fm_computeNormalVector(REAL *n,const REAL *p1,const REAL *p2) +{ + n[0] = p2[0] - p1[0]; + n[1] = p2[1] - p1[1]; + n[2] = p2[2] - p1[2]; + fm_normalize(n); +} + +bool fm_computeWindingOrder(const REAL *p1,const REAL *p2,const REAL *p3) // returns true if the triangle is clockwise. +{ + bool ret = false; + + REAL v1[3]; + REAL v2[3]; + + fm_computeNormalVector(v1,p1,p2); // p2-p1 (as vector) and then normalized + fm_computeNormalVector(v2,p1,p3); // p3-p1 (as vector) and then normalized + + REAL cross[3]; + + fm_cross(cross, v1, v2 ); + REAL ref[3] = { 1, 0, 0 }; + + REAL d = fm_dot( cross, ref ); + + + if ( d <= 0 ) + ret = false; + else + ret = true; + + return ret; +} + +REAL fm_normalize(REAL *n) // normalize this vector +{ + REAL dist = (REAL)sqrt(n[0]*n[0] + n[1]*n[1] + n[2]*n[2]); + if ( dist > 0.0000001f ) + { + REAL mag = 1.0f / dist; + n[0]*=mag; + n[1]*=mag; + n[2]*=mag; + } + else + { + n[0] = 1; + n[1] = 0; + n[2] = 0; + } + + return dist; +} + + +void fm_matrixMultiply(const REAL *pA,const REAL *pB,REAL *pM) +{ +#if 1 + + REAL a = pA[0*4+0] * pB[0*4+0] + pA[0*4+1] * pB[1*4+0] + pA[0*4+2] * pB[2*4+0] + pA[0*4+3] * pB[3*4+0]; + REAL b = pA[0*4+0] * pB[0*4+1] + pA[0*4+1] * pB[1*4+1] + pA[0*4+2] * pB[2*4+1] + pA[0*4+3] * pB[3*4+1]; + REAL c = pA[0*4+0] * pB[0*4+2] + pA[0*4+1] * pB[1*4+2] + pA[0*4+2] * pB[2*4+2] + pA[0*4+3] * pB[3*4+2]; + REAL d = pA[0*4+0] * pB[0*4+3] + pA[0*4+1] * pB[1*4+3] + pA[0*4+2] * pB[2*4+3] + pA[0*4+3] * pB[3*4+3]; + + REAL e = pA[1*4+0] * pB[0*4+0] + pA[1*4+1] * pB[1*4+0] + pA[1*4+2] * pB[2*4+0] + pA[1*4+3] * pB[3*4+0]; + REAL f = pA[1*4+0] * pB[0*4+1] + pA[1*4+1] * pB[1*4+1] + pA[1*4+2] * pB[2*4+1] + pA[1*4+3] * pB[3*4+1]; + REAL g = pA[1*4+0] * pB[0*4+2] + pA[1*4+1] * pB[1*4+2] + pA[1*4+2] * pB[2*4+2] + pA[1*4+3] * pB[3*4+2]; + REAL h = pA[1*4+0] * pB[0*4+3] + pA[1*4+1] * pB[1*4+3] + pA[1*4+2] * pB[2*4+3] + pA[1*4+3] * pB[3*4+3]; + + REAL i = pA[2*4+0] * pB[0*4+0] + pA[2*4+1] * pB[1*4+0] + pA[2*4+2] * pB[2*4+0] + pA[2*4+3] * pB[3*4+0]; + REAL j = pA[2*4+0] * pB[0*4+1] + pA[2*4+1] * pB[1*4+1] + pA[2*4+2] * pB[2*4+1] + pA[2*4+3] * pB[3*4+1]; + REAL k = pA[2*4+0] * pB[0*4+2] + pA[2*4+1] * pB[1*4+2] + pA[2*4+2] * pB[2*4+2] + pA[2*4+3] * pB[3*4+2]; + REAL l = pA[2*4+0] * pB[0*4+3] + pA[2*4+1] * pB[1*4+3] + pA[2*4+2] * pB[2*4+3] + pA[2*4+3] * pB[3*4+3]; + + REAL m = pA[3*4+0] * pB[0*4+0] + pA[3*4+1] * pB[1*4+0] + pA[3*4+2] * pB[2*4+0] + pA[3*4+3] * pB[3*4+0]; + REAL n = pA[3*4+0] * pB[0*4+1] + pA[3*4+1] * pB[1*4+1] + pA[3*4+2] * pB[2*4+1] + pA[3*4+3] * pB[3*4+1]; + REAL o = pA[3*4+0] * pB[0*4+2] + pA[3*4+1] * pB[1*4+2] + pA[3*4+2] * pB[2*4+2] + pA[3*4+3] * pB[3*4+2]; + REAL p = pA[3*4+0] * pB[0*4+3] + pA[3*4+1] * pB[1*4+3] + pA[3*4+2] * pB[2*4+3] + pA[3*4+3] * pB[3*4+3]; + + pM[0] = a; + pM[1] = b; + pM[2] = c; + pM[3] = d; + + pM[4] = e; + pM[5] = f; + pM[6] = g; + pM[7] = h; + + pM[8] = i; + pM[9] = j; + pM[10] = k; + pM[11] = l; + + pM[12] = m; + pM[13] = n; + pM[14] = o; + pM[15] = p; + + +#else + memset(pM, 0, sizeof(REAL)*16); + for(NxI32 i=0; i<4; i++ ) + for(NxI32 j=0; j<4; j++ ) + for(NxI32 k=0; k<4; k++ ) + pM[4*i+j] += pA[4*i+k] * pB[4*k+j]; +#endif +} + + +void fm_eulerToQuatDX(REAL x,REAL y,REAL z,REAL *quat) // convert euler angles to quaternion using the fucked up DirectX method +{ + REAL matrix[16]; + fm_eulerToMatrix(x,y,z,matrix); + fm_matrixToQuat(matrix,quat); +} + +// implementation copied from: http://blogs.msdn.com/mikepelton/archive/2004/10/29/249501.aspx +void fm_eulerToMatrixDX(REAL x,REAL y,REAL z,REAL *matrix) // convert euler angles to quaternion using the fucked up DirectX method. +{ + fm_identity(matrix); + matrix[0*4+0] = cos(z)*cos(y) + sin(z)*sin(x)*sin(y); + matrix[0*4+1] = sin(z)*cos(x); + matrix[0*4+2] = cos(z)*-sin(y) + sin(z)*sin(x)*cos(y); + + matrix[1*4+0] = -sin(z)*cos(y)+cos(z)*sin(x)*sin(y); + matrix[1*4+1] = cos(z)*cos(x); + matrix[1*4+2] = sin(z)*sin(y) +cos(z)*sin(x)*cos(y); + + matrix[2*4+0] = cos(x)*sin(y); + matrix[2*4+1] = -sin(x); + matrix[2*4+2] = cos(x)*cos(y); +} + + +void fm_scale(REAL x,REAL y,REAL z,REAL *fscale) // apply scale to the matrix. +{ + fscale[0*4+0] = x; + fscale[1*4+1] = y; + fscale[2*4+2] = z; +} + + +void fm_composeTransform(const REAL *position,const REAL *quat,const REAL *scale,REAL *matrix) +{ + fm_identity(matrix); + fm_quatToMatrix(quat,matrix); + + if ( scale && ( scale[0] != 1 || scale[1] != 1 || scale[2] != 1 ) ) + { + REAL work[16]; + memcpy(work,matrix,sizeof(REAL)*16); + REAL mscale[16]; + fm_identity(mscale); + fm_scale(scale[0],scale[1],scale[2],mscale); + fm_matrixMultiply(work,mscale,matrix); + } + + matrix[12] = position[0]; + matrix[13] = position[1]; + matrix[14] = position[2]; +} + + +void fm_setTranslation(const REAL *translation,REAL *matrix) +{ + matrix[12] = translation[0]; + matrix[13] = translation[1]; + matrix[14] = translation[2]; +} + +static REAL enorm0_3d ( REAL x0, REAL y0, REAL z0, REAL x1, REAL y1, REAL z1 ) + +/**********************************************************************/ + +/* +Purpose: + +ENORM0_3D computes the Euclidean norm of (P1-P0) in 3D. + +Modified: + +18 April 1999 + +Author: + +John Burkardt + +Parameters: + +Input, REAL X0, Y0, Z0, X1, Y1, Z1, the coordinates of the points +P0 and P1. + +Output, REAL ENORM0_3D, the Euclidean norm of (P1-P0). +*/ +{ + REAL value; + + value = sqrt ( + ( x1 - x0 ) * ( x1 - x0 ) + + ( y1 - y0 ) * ( y1 - y0 ) + + ( z1 - z0 ) * ( z1 - z0 ) ); + + return value; +} + + +static REAL triangle_area_3d ( REAL x1, REAL y1, REAL z1, REAL x2,REAL y2, REAL z2, REAL x3, REAL y3, REAL z3 ) + + /**********************************************************************/ + + /* + Purpose: + + TRIANGLE_AREA_3D computes the area of a triangle in 3D. + + Modified: + + 22 April 1999 + + Author: + + John Burkardt + + Parameters: + + Input, REAL X1, Y1, Z1, X2, Y2, Z2, X3, Y3, Z3, the (X,Y,Z) + coordinates of the corners of the triangle. + + Output, REAL TRIANGLE_AREA_3D, the area of the triangle. + */ +{ + REAL a; + REAL alpha; + REAL area; + REAL b; + REAL base; + REAL c; + REAL dot; + REAL height; + /* + Find the projection of (P3-P1) onto (P2-P1). + */ + dot = + ( x2 - x1 ) * ( x3 - x1 ) + + ( y2 - y1 ) * ( y3 - y1 ) + + ( z2 - z1 ) * ( z3 - z1 ); + + base = enorm0_3d ( x1, y1, z1, x2, y2, z2 ); + /* + The height of the triangle is the length of (P3-P1) after its + projection onto (P2-P1) has been subtracted. + */ + if ( base == 0.0 ) { + + height = 0.0; + + } + else { + + alpha = dot / ( base * base ); + + a = x3 - x1 - alpha * ( x2 - x1 ); + b = y3 - y1 - alpha * ( y2 - y1 ); + c = z3 - z1 - alpha * ( z2 - z1 ); + + height = sqrt ( a * a + b * b + c * c ); + + } + + area = 0.5f * base * height; + + return area; +} + + +REAL fm_computeArea(const REAL *p1,const REAL *p2,const REAL *p3) +{ + REAL ret = 0; + + ret = triangle_area_3d(p1[0],p1[1],p1[2],p2[0],p2[1],p2[2],p3[0],p3[1],p3[2]); + + return ret; +} + + +void fm_lerp(const REAL *p1,const REAL *p2,REAL *dest,REAL lerpValue) +{ + dest[0] = ((p2[0] - p1[0])*lerpValue) + p1[0]; + dest[1] = ((p2[1] - p1[1])*lerpValue) + p1[1]; + dest[2] = ((p2[2] - p1[2])*lerpValue) + p1[2]; +} + +bool fm_pointTestXZ(const REAL *p,const REAL *i,const REAL *j) +{ + bool ret = false; + + if (((( i[2] <= p[2] ) && ( p[2] < j[2] )) || (( j[2] <= p[2] ) && ( p[2] < i[2] ))) && ( p[0] < (j[0] - i[0]) * (p[2] - i[2]) / (j[2] - i[2]) + i[0])) + ret = true; + + return ret; +}; + + +bool fm_insideTriangleXZ(const REAL *p,const REAL *p1,const REAL *p2,const REAL *p3) +{ + bool ret = false; + + NxI32 c = 0; + if ( fm_pointTestXZ(p,p1,p2) ) c = !c; + if ( fm_pointTestXZ(p,p2,p3) ) c = !c; + if ( fm_pointTestXZ(p,p3,p1) ) c = !c; + if ( c ) ret = true; + + return ret; +} + +bool fm_insideAABB(const REAL *pos,const REAL *bmin,const REAL *bmax) +{ + bool ret = false; + + if ( pos[0] >= bmin[0] && pos[0] <= bmax[0] && + pos[1] >= bmin[1] && pos[1] <= bmax[1] && + pos[2] >= bmin[2] && pos[2] <= bmax[2] ) + ret = true; + + return ret; +} + + +NxU32 fm_clipTestPoint(const REAL *bmin,const REAL *bmax,const REAL *pos) +{ + NxU32 ret = 0; + + if ( pos[0] < bmin[0] ) + ret|=FMCS_XMIN; + else if ( pos[0] > bmax[0] ) + ret|=FMCS_XMAX; + + if ( pos[1] < bmin[1] ) + ret|=FMCS_YMIN; + else if ( pos[1] > bmax[1] ) + ret|=FMCS_YMAX; + + if ( pos[2] < bmin[2] ) + ret|=FMCS_ZMIN; + else if ( pos[2] > bmax[2] ) + ret|=FMCS_ZMAX; + + return ret; +} + +NxU32 fm_clipTestPointXZ(const REAL *bmin,const REAL *bmax,const REAL *pos) // only tests X and Z, not Y +{ + NxU32 ret = 0; + + if ( pos[0] < bmin[0] ) + ret|=FMCS_XMIN; + else if ( pos[0] > bmax[0] ) + ret|=FMCS_XMAX; + + if ( pos[2] < bmin[2] ) + ret|=FMCS_ZMIN; + else if ( pos[2] > bmax[2] ) + ret|=FMCS_ZMAX; + + return ret; +} + +NxU32 fm_clipTestAABB(const REAL *bmin,const REAL *bmax,const REAL *p1,const REAL *p2,const REAL *p3,NxU32 &andCode) +{ + NxU32 orCode = 0; + + andCode = FMCS_XMIN | FMCS_XMAX | FMCS_YMIN | FMCS_YMAX | FMCS_ZMIN | FMCS_ZMAX; + + NxU32 c = fm_clipTestPoint(bmin,bmax,p1); + orCode|=c; + andCode&=c; + + c = fm_clipTestPoint(bmin,bmax,p2); + orCode|=c; + andCode&=c; + + c = fm_clipTestPoint(bmin,bmax,p3); + orCode|=c; + andCode&=c; + + return orCode; +} + +bool intersect(const REAL *si,const REAL *ei,const REAL *bmin,const REAL *bmax,REAL *time) +{ + REAL st,et,fst = 0,fet = 1; + + for (NxI32 i = 0; i < 3; i++) + { + if (*si < *ei) + { + if (*si > *bmax || *ei < *bmin) + return false; + REAL di = *ei - *si; + st = (*si < *bmin)? (*bmin - *si) / di: 0; + et = (*ei > *bmax)? (*bmax - *si) / di: 1; + } + else + { + if (*ei > *bmax || *si < *bmin) + return false; + REAL di = *ei - *si; + st = (*si > *bmax)? (*bmax - *si) / di: 0; + et = (*ei < *bmin)? (*bmin - *si) / di: 1; + } + + if (st > fst) fst = st; + if (et < fet) fet = et; + if (fet < fst) + return false; + bmin++; bmax++; + si++; ei++; + } + + *time = fst; + return true; +} + + + +bool fm_lineTestAABB(const REAL *p1,const REAL *p2,const REAL *bmin,const REAL *bmax,REAL &time) +{ + bool sect = intersect(p1,p2,bmin,bmax,&time); + return sect; +} + + +bool fm_lineTestAABBXZ(const REAL *p1,const REAL *p2,const REAL *bmin,const REAL *bmax,REAL &time) +{ + REAL _bmin[3]; + REAL _bmax[3]; + + _bmin[0] = bmin[0]; + _bmin[1] = -1e9; + _bmin[2] = bmin[2]; + + _bmax[0] = bmax[0]; + _bmax[1] = 1e9; + _bmax[2] = bmax[2]; + + bool sect = intersect(p1,p2,_bmin,_bmax,&time); + + return sect; +} + +void fm_minmax(const REAL *p,REAL *bmin,REAL *bmax) // accmulate to a min-max value +{ + + if ( p[0] < bmin[0] ) bmin[0] = p[0]; + if ( p[1] < bmin[1] ) bmin[1] = p[1]; + if ( p[2] < bmin[2] ) bmin[2] = p[2]; + + if ( p[0] > bmax[0] ) bmax[0] = p[0]; + if ( p[1] > bmax[1] ) bmax[1] = p[1]; + if ( p[2] > bmax[2] ) bmax[2] = p[2]; + +} + +REAL fm_solveX(const REAL *plane,REAL y,REAL z) // solve for X given this plane equation and the other two components. +{ + REAL x = (y*plane[1]+z*plane[2]+plane[3]) / -plane[0]; + return x; +} + +REAL fm_solveY(const REAL *plane,REAL x,REAL z) // solve for Y given this plane equation and the other two components. +{ + REAL y = (x*plane[0]+z*plane[2]+plane[3]) / -plane[1]; + return y; +} + + +REAL fm_solveZ(const REAL *plane,REAL x,REAL y) // solve for Y given this plane equation and the other two components. +{ + REAL z = (x*plane[0]+y*plane[1]+plane[3]) / -plane[2]; + return z; +} + + +void fm_getAABBCenter(const REAL *bmin,const REAL *bmax,REAL *center) +{ + center[0] = (bmax[0]-bmin[0])*0.5f+bmin[0]; + center[1] = (bmax[1]-bmin[1])*0.5f+bmin[1]; + center[2] = (bmax[2]-bmin[2])*0.5f+bmin[2]; +} + +FM_Axis fm_getDominantAxis(const REAL normal[3]) +{ + FM_Axis ret = FM_XAXIS; + + REAL x = fabs(normal[0]); + REAL y = fabs(normal[1]); + REAL z = fabs(normal[2]); + + if ( y > x && y > z ) + ret = FM_YAXIS; + else if ( z > x && z > y ) + ret = FM_ZAXIS; + + return ret; +} + + +bool fm_lineSphereIntersect(const REAL *center,REAL radius,const REAL *p1,const REAL *p2,REAL *intersect) +{ + bool ret = false; + + REAL dir[3]; + + dir[0] = p2[0]-p1[0]; + dir[1] = p2[1]-p1[1]; + dir[2] = p2[2]-p1[2]; + + REAL distance = sqrt( dir[0]*dir[0]+dir[1]*dir[1]+dir[2]*dir[2]); + + if ( distance > 0 ) + { + REAL recip = 1.0f / distance; + dir[0]*=recip; + dir[1]*=recip; + dir[2]*=recip; + ret = fm_raySphereIntersect(center,radius,p1,dir,distance,intersect); + } + else + { + dir[0] = center[0]-p1[0]; + dir[1] = center[1]-p1[1]; + dir[2] = center[2]-p1[2]; + REAL d2 = dir[0]*dir[0]+dir[1]*dir[1]+dir[2]*dir[2]; + REAL r2 = radius*radius; + if ( d2 < r2 ) + { + ret = true; + if ( intersect ) + { + intersect[0] = p1[0]; + intersect[1] = p1[1]; + intersect[2] = p1[2]; + } + } + } + return ret; +} + +#define DOT(p1,p2) (p1[0]*p2[0]+p1[1]*p2[1]+p1[2]*p2[2]) + +bool fm_raySphereIntersect(const REAL *center,REAL radius,const REAL *pos,const REAL *dir,REAL distance,REAL *intersect) +{ + bool ret = false; + + REAL E0[3]; + + E0[0] = center[0] - pos[0]; + E0[1] = center[1] - pos[1]; + E0[2] = center[2] - pos[2]; + + REAL V[3]; + + V[0] = dir[0]; + V[1] = dir[1]; + V[2] = dir[2]; + + + REAL dist2 = E0[0]*E0[0] + E0[1]*E0[1] + E0[2] * E0[2]; + REAL radius2 = radius*radius; // radius squared.. + + // Bug Fix For Gem, if origin is *inside* the sphere, invert the + // direction vector so that we get a valid intersection location. + if ( dist2 < radius2 ) + { + V[0]*=-1; + V[1]*=-1; + V[2]*=-1; + } + + + REAL v = DOT(E0,V); + + REAL disc = radius2 - (dist2 - v*v); + + if (disc > 0.0f) + { + if ( intersect ) + { + REAL d = sqrt(disc); + REAL diff = v-d; + if ( diff < distance ) + { + intersect[0] = pos[0]+V[0]*diff; + intersect[1] = pos[1]+V[1]*diff; + intersect[2] = pos[2]+V[2]*diff; + ret = true; + } + } + } + + return ret; +} + + +void fm_catmullRom(REAL *out_vector,const REAL *p1,const REAL *p2,const REAL *p3,const REAL *p4, const REAL s) +{ + REAL s_squared = s * s; + REAL s_cubed = s_squared * s; + + REAL coefficient_p1 = -s_cubed + 2*s_squared - s; + REAL coefficient_p2 = 3 * s_cubed - 5 * s_squared + 2; + REAL coefficient_p3 = -3 * s_cubed +4 * s_squared + s; + REAL coefficient_p4 = s_cubed - s_squared; + + out_vector[0] = (coefficient_p1 * p1[0] + coefficient_p2 * p2[0] + coefficient_p3 * p3[0] + coefficient_p4 * p4[0])*0.5f; + out_vector[1] = (coefficient_p1 * p1[1] + coefficient_p2 * p2[1] + coefficient_p3 * p3[1] + coefficient_p4 * p4[1])*0.5f; + out_vector[2] = (coefficient_p1 * p1[2] + coefficient_p2 * p2[2] + coefficient_p3 * p3[2] + coefficient_p4 * p4[2])*0.5f; +} + +bool fm_intersectAABB(const REAL *bmin1,const REAL *bmax1,const REAL *bmin2,const REAL *bmax2) +{ + if ((bmin1[0] > bmax2[0]) || (bmin2[0] > bmax1[0])) return false; + if ((bmin1[1] > bmax2[1]) || (bmin2[1] > bmax1[1])) return false; + if ((bmin1[2] > bmax2[2]) || (bmin2[2] > bmax1[2])) return false; + return true; + +} + +bool fm_insideAABB(const REAL *obmin,const REAL *obmax,const REAL *tbmin,const REAL *tbmax) // test if bounding box tbmin/tmbax is fully inside obmin/obmax +{ + bool ret = false; + + if ( tbmax[0] <= obmax[0] && + tbmax[1] <= obmax[1] && + tbmax[2] <= obmax[2] && + tbmin[0] >= obmin[0] && + tbmin[1] >= obmin[1] && + tbmin[2] >= obmin[2] ) ret = true; + + return ret; +} + + +// Reference, from Stan Melax in Game Gems I +// Quaternion q; +// vector3 c = CrossProduct(v0,v1); +// REAL d = DotProduct(v0,v1); +// REAL s = (REAL)sqrt((1+d)*2); +// q.x = c.x / s; +// q.y = c.y / s; +// q.z = c.z / s; +// q.w = s /2.0f; +// return q; +void fm_rotationArc(const REAL *v0,const REAL *v1,REAL *quat) +{ + REAL cross[3]; + + fm_cross(cross,v0,v1); + REAL d = fm_dot(v0,v1); + REAL s = sqrt((1+d)*2); + REAL recip = 1.0f / s; + + quat[0] = cross[0] * recip; + quat[1] = cross[1] * recip; + quat[2] = cross[2] * recip; + quat[3] = s * 0.5f; + +} + + +REAL fm_distancePointLineSegment(const REAL *Point,const REAL *LineStart,const REAL *LineEnd,REAL *intersection,LineSegmentType &type,REAL epsilon) +{ + REAL ret; + + REAL LineMag = fm_distance( LineEnd, LineStart ); + + if ( LineMag > 0 ) + { + REAL U = ( ( ( Point[0] - LineStart[0] ) * ( LineEnd[0] - LineStart[0] ) ) + ( ( Point[1] - LineStart[1] ) * ( LineEnd[1] - LineStart[1] ) ) + ( ( Point[2] - LineStart[2] ) * ( LineEnd[2] - LineStart[2] ) ) ) / ( LineMag * LineMag ); + if( U < 0.0f || U > 1.0f ) + { + REAL d1 = fm_distanceSquared(Point,LineStart); + REAL d2 = fm_distanceSquared(Point,LineEnd); + if ( d1 <= d2 ) + { + ret = sqrt(d1); + intersection[0] = LineStart[0]; + intersection[1] = LineStart[1]; + intersection[2] = LineStart[2]; + type = LS_START; + } + else + { + ret = sqrt(d2); + intersection[0] = LineEnd[0]; + intersection[1] = LineEnd[1]; + intersection[2] = LineEnd[2]; + type = LS_END; + } + } + else + { + intersection[0] = LineStart[0] + U * ( LineEnd[0] - LineStart[0] ); + intersection[1] = LineStart[1] + U * ( LineEnd[1] - LineStart[1] ); + intersection[2] = LineStart[2] + U * ( LineEnd[2] - LineStart[2] ); + + ret = fm_distance(Point,intersection); + + REAL d1 = fm_distanceSquared(intersection,LineStart); + REAL d2 = fm_distanceSquared(intersection,LineEnd); + REAL mag = (epsilon*2)*(epsilon*2); + + if ( d1 < mag ) // if less than 1/100th the total distance, treat is as the 'start' + { + type = LS_START; + } + else if ( d2 < mag ) + { + type = LS_END; + } + else + { + type = LS_MIDDLE; + } + + } + } + else + { + ret = LineMag; + intersection[0] = LineEnd[0]; + intersection[1] = LineEnd[1]; + intersection[2] = LineEnd[2]; + type = LS_END; + } + + return ret; +} + + +#ifndef BEST_FIT_PLANE_H + +#define BEST_FIT_PLANE_H + +template class Eigen +{ +public: + + + void DecrSortEigenStuff(void) + { + Tridiagonal(); //diagonalize the matrix. + QLAlgorithm(); // + DecreasingSort(); + GuaranteeRotation(); + } + + void Tridiagonal(void) + { + Type fM00 = mElement[0][0]; + Type fM01 = mElement[0][1]; + Type fM02 = mElement[0][2]; + Type fM11 = mElement[1][1]; + Type fM12 = mElement[1][2]; + Type fM22 = mElement[2][2]; + + m_afDiag[0] = fM00; + m_afSubd[2] = 0; + if (fM02 != (Type)0.0) + { + Type fLength = sqrt(fM01*fM01+fM02*fM02); + Type fInvLength = ((Type)1.0)/fLength; + fM01 *= fInvLength; + fM02 *= fInvLength; + Type fQ = ((Type)2.0)*fM01*fM12+fM02*(fM22-fM11); + m_afDiag[1] = fM11+fM02*fQ; + m_afDiag[2] = fM22-fM02*fQ; + m_afSubd[0] = fLength; + m_afSubd[1] = fM12-fM01*fQ; + mElement[0][0] = (Type)1.0; + mElement[0][1] = (Type)0.0; + mElement[0][2] = (Type)0.0; + mElement[1][0] = (Type)0.0; + mElement[1][1] = fM01; + mElement[1][2] = fM02; + mElement[2][0] = (Type)0.0; + mElement[2][1] = fM02; + mElement[2][2] = -fM01; + m_bIsRotation = false; + } + else + { + m_afDiag[1] = fM11; + m_afDiag[2] = fM22; + m_afSubd[0] = fM01; + m_afSubd[1] = fM12; + mElement[0][0] = (Type)1.0; + mElement[0][1] = (Type)0.0; + mElement[0][2] = (Type)0.0; + mElement[1][0] = (Type)0.0; + mElement[1][1] = (Type)1.0; + mElement[1][2] = (Type)0.0; + mElement[2][0] = (Type)0.0; + mElement[2][1] = (Type)0.0; + mElement[2][2] = (Type)1.0; + m_bIsRotation = true; + } + } + + bool QLAlgorithm(void) + { + const NxI32 iMaxIter = 32; + + for (NxI32 i0 = 0; i0 <3; i0++) + { + NxI32 i1; + for (i1 = 0; i1 < iMaxIter; i1++) + { + NxI32 i2; + for (i2 = i0; i2 <= (3-2); i2++) + { + Type fTmp = fabs(m_afDiag[i2]) + fabs(m_afDiag[i2+1]); + if ( fabs(m_afSubd[i2]) + fTmp == fTmp ) + break; + } + if (i2 == i0) + { + break; + } + + Type fG = (m_afDiag[i0+1] - m_afDiag[i0])/(((Type)2.0) * m_afSubd[i0]); + Type fR = sqrt(fG*fG+(Type)1.0); + if (fG < (Type)0.0) + { + fG = m_afDiag[i2]-m_afDiag[i0]+m_afSubd[i0]/(fG-fR); + } + else + { + fG = m_afDiag[i2]-m_afDiag[i0]+m_afSubd[i0]/(fG+fR); + } + Type fSin = (Type)1.0, fCos = (Type)1.0, fP = (Type)0.0; + for (NxI32 i3 = i2-1; i3 >= i0; i3--) + { + Type fF = fSin*m_afSubd[i3]; + Type fB = fCos*m_afSubd[i3]; + if (fabs(fF) >= fabs(fG)) + { + fCos = fG/fF; + fR = sqrt(fCos*fCos+(Type)1.0); + m_afSubd[i3+1] = fF*fR; + fSin = ((Type)1.0)/fR; + fCos *= fSin; + } + else + { + fSin = fF/fG; + fR = sqrt(fSin*fSin+(Type)1.0); + m_afSubd[i3+1] = fG*fR; + fCos = ((Type)1.0)/fR; + fSin *= fCos; + } + fG = m_afDiag[i3+1]-fP; + fR = (m_afDiag[i3]-fG)*fSin+((Type)2.0)*fB*fCos; + fP = fSin*fR; + m_afDiag[i3+1] = fG+fP; + fG = fCos*fR-fB; + for (NxI32 i4 = 0; i4 < 3; i4++) + { + fF = mElement[i4][i3+1]; + mElement[i4][i3+1] = fSin*mElement[i4][i3]+fCos*fF; + mElement[i4][i3] = fCos*mElement[i4][i3]-fSin*fF; + } + } + m_afDiag[i0] -= fP; + m_afSubd[i0] = fG; + m_afSubd[i2] = (Type)0.0; + } + if (i1 == iMaxIter) + { + return false; + } + } + return true; + } + + void DecreasingSort(void) + { + //sort eigenvalues in decreasing order, e[0] >= ... >= e[iSize-1] + for (NxI32 i0 = 0, i1; i0 <= 3-2; i0++) + { + // locate maximum eigenvalue + i1 = i0; + Type fMax = m_afDiag[i1]; + NxI32 i2; + for (i2 = i0+1; i2 < 3; i2++) + { + if (m_afDiag[i2] > fMax) + { + i1 = i2; + fMax = m_afDiag[i1]; + } + } + + if (i1 != i0) + { + // swap eigenvalues + m_afDiag[i1] = m_afDiag[i0]; + m_afDiag[i0] = fMax; + // swap eigenvectors + for (i2 = 0; i2 < 3; i2++) + { + Type fTmp = mElement[i2][i0]; + mElement[i2][i0] = mElement[i2][i1]; + mElement[i2][i1] = fTmp; + m_bIsRotation = !m_bIsRotation; + } + } + } + } + + + void GuaranteeRotation(void) + { + if (!m_bIsRotation) + { + // change sign on the first column + for (NxI32 iRow = 0; iRow <3; iRow++) + { + mElement[iRow][0] = -mElement[iRow][0]; + } + } + } + + Type mElement[3][3]; + Type m_afDiag[3]; + Type m_afSubd[3]; + bool m_bIsRotation; +}; + +#endif + +bool fm_computeBestFitPlane(NxU32 vcount, + const REAL *points, + NxU32 vstride, + const REAL *weights, + NxU32 wstride, + REAL *plane) +{ + bool ret = false; + + REAL kOrigin[3] = { 0, 0, 0 }; + + REAL wtotal = 0; + + { + const char *source = (const char *) points; + const char *wsource = (const char *) weights; + + for (NxU32 i=0; i kES; + + kES.mElement[0][0] = fSumXX; + kES.mElement[0][1] = fSumXY; + kES.mElement[0][2] = fSumXZ; + + kES.mElement[1][0] = fSumXY; + kES.mElement[1][1] = fSumYY; + kES.mElement[1][2] = fSumYZ; + + kES.mElement[2][0] = fSumXZ; + kES.mElement[2][1] = fSumYZ; + kES.mElement[2][2] = fSumZZ; + + // compute eigenstuff, smallest eigenvalue is in last position + kES.DecrSortEigenStuff(); + + REAL kNormal[3]; + + kNormal[0] = kES.mElement[0][2]; + kNormal[1] = kES.mElement[1][2]; + kNormal[2] = kES.mElement[2][2]; + + // the minimum energy + plane[0] = kNormal[0]; + plane[1] = kNormal[1]; + plane[2] = kNormal[2]; + + plane[3] = 0 - fm_dot(kNormal,kOrigin); + + ret = true; + + return ret; +} + + +bool fm_colinear(const REAL a1[3],const REAL a2[3],const REAL b1[3],const REAL b2[3],REAL epsilon) // true if these two line segments are co-linear. +{ + bool ret = false; + + REAL dir1[3]; + REAL dir2[3]; + + dir1[0] = (a2[0] - a1[0]); + dir1[1] = (a2[1] - a1[1]); + dir1[2] = (a2[2] - a1[2]); + + dir2[0] = (b2[0]-a1[0]) - (b1[0]-a1[0]); + dir2[1] = (b2[1]-a1[1]) - (b1[1]-a1[1]); + dir2[2] = (b2[2]-a2[2]) - (b1[2]-a2[2]); + + fm_normalize(dir1); + fm_normalize(dir2); + + REAL dot = fm_dot(dir1,dir2); + + if ( dot >= epsilon ) + { + ret = true; + } + + + return ret; +} + +bool fm_colinear(const REAL *p1,const REAL *p2,const REAL *p3,REAL epsilon) +{ + bool ret = false; + + REAL dir1[3]; + REAL dir2[3]; + + dir1[0] = p2[0] - p1[0]; + dir1[1] = p2[1] - p1[1]; + dir1[2] = p2[2] - p1[2]; + + dir2[0] = p3[0] - p2[0]; + dir2[1] = p3[1] - p2[1]; + dir2[2] = p3[2] - p2[2]; + + fm_normalize(dir1); + fm_normalize(dir2); + + REAL dot = fm_dot(dir1,dir2); + + if ( dot >= epsilon ) + { + ret = true; + } + + + return ret; +} + +void fm_initMinMax(const REAL *p,REAL *bmin,REAL *bmax) +{ + bmax[0] = bmin[0] = p[0]; + bmax[1] = bmin[1] = p[1]; + bmax[2] = bmin[2] = p[2]; +} + +IntersectResult fm_intersectLineSegments2d(const REAL *a1,const REAL *a2,const REAL *b1,const REAL *b2,REAL *intersection) +{ + IntersectResult ret; + + REAL denom = ((b2[1] - b1[1])*(a2[0] - a1[0])) - ((b2[0] - b1[0])*(a2[1] - a1[1])); + REAL nume_a = ((b2[0] - b1[0])*(a1[1] - b1[1])) - ((b2[1] - b1[1])*(a1[0] - b1[0])); + REAL nume_b = ((a2[0] - a1[0])*(a1[1] - b1[1])) - ((a2[1] - a1[1])*(a1[0] - b1[0])); + if (denom == 0 ) + { + if(nume_a == 0 && nume_b == 0) + { + ret = IR_COINCIDENT; + } + else + { + ret = IR_PARALLEL; + } + } + else + { + + REAL recip = 1 / denom; + REAL ua = nume_a * recip; + REAL ub = nume_b * recip; + + if(ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1 ) + { + // Get the intersection point. + intersection[0] = a1[0] + ua*(a2[0] - a1[0]); + intersection[1] = a1[1] + ua*(a2[1] - a1[1]); + ret = IR_DO_INTERSECT; + } + else + { + ret = IR_DONT_INTERSECT; + } + } + return ret; +} + +IntersectResult fm_intersectLineSegments2dTime(const REAL *a1,const REAL *a2,const REAL *b1,const REAL *b2,REAL &t1,REAL &t2) +{ + IntersectResult ret; + + REAL denom = ((b2[1] - b1[1])*(a2[0] - a1[0])) - ((b2[0] - b1[0])*(a2[1] - a1[1])); + REAL nume_a = ((b2[0] - b1[0])*(a1[1] - b1[1])) - ((b2[1] - b1[1])*(a1[0] - b1[0])); + REAL nume_b = ((a2[0] - a1[0])*(a1[1] - b1[1])) - ((a2[1] - a1[1])*(a1[0] - b1[0])); + if (denom == 0 ) + { + if(nume_a == 0 && nume_b == 0) + { + ret = IR_COINCIDENT; + } + else + { + ret = IR_PARALLEL; + } + } + else + { + + REAL recip = 1 / denom; + REAL ua = nume_a * recip; + REAL ub = nume_b * recip; + + if(ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1 ) + { + t1 = ua; + t2 = ub; + ret = IR_DO_INTERSECT; + } + else + { + ret = IR_DONT_INTERSECT; + } + } + return ret; +} + +//**** Plane Triangle Intersection + + + + + +// assumes that the points are on opposite sides of the plane! +void fm_intersectPointPlane(const REAL *p1,const REAL *p2,REAL *split,const REAL *plane) +{ + + REAL dp1 = fm_distToPlane(plane,p1); + + REAL dir[3]; + + dir[0] = p2[0] - p1[0]; + dir[1] = p2[1] - p1[1]; + dir[2] = p2[2] - p1[2]; + + REAL dot1 = dir[0]*plane[0] + dir[1]*plane[1] + dir[2]*plane[2]; + REAL dot2 = dp1 - plane[3]; + + REAL t = -(plane[3] + dot2 ) / dot1; + + split[0] = (dir[0]*t)+p1[0]; + split[1] = (dir[1]*t)+p1[1]; + split[2] = (dir[2]*t)+p1[2]; + +} + +PlaneTriResult fm_getSidePlane(const REAL *p,const REAL *plane,REAL epsilon) +{ + PlaneTriResult ret = PTR_ON_PLANE; + + REAL d = fm_distToPlane(plane,p); + + if ( d < -epsilon || d > epsilon ) + { + if ( d > 0 ) + ret = PTR_FRONT; // it is 'in front' within the provided epsilon value. + else + ret = PTR_BACK; + } + + return ret; +} + + + +#ifndef PLANE_TRIANGLE_INTERSECTION_H + +#define PLANE_TRIANGLE_INTERSECTION_H + +#define MAXPTS 256 + +template class point +{ +public: + + void set(const Type *p) + { + x = p[0]; + y = p[1]; + z = p[2]; + } + + Type x; + Type y; + Type z; +}; + +template class plane +{ +public: + plane(const Type *p) + { + normal.x = p[0]; + normal.y = p[1]; + normal.z = p[2]; + D = p[3]; + } + + Type Classify_Point(const point &p) + { + return p.x*normal.x + p.y*normal.y + p.z*normal.z + D; + } + + point normal; + Type D; +}; + +template class polygon +{ +public: + polygon(void) + { + mVcount = 0; + } + + polygon(const Type *p1,const Type *p2,const Type *p3) + { + mVcount = 3; + mVertices[0].set(p1); + mVertices[1].set(p2); + mVertices[2].set(p3); + } + + + NxI32 NumVertices(void) const { return mVcount; }; + + const point& Vertex(NxI32 index) + { + if ( index < 0 ) index+=mVcount; + return mVertices[index]; + }; + + + void set(const point *pts,NxI32 count) + { + for (NxI32 i=0; i *poly,plane *part, polygon &front, polygon &back) + { + NxI32 count = poly->NumVertices (); + NxI32 out_c = 0, in_c = 0; + point ptA, ptB,outpts[MAXPTS],inpts[MAXPTS]; + Type sideA, sideB; + ptA = poly->Vertex (count - 1); + sideA = part->Classify_Point (ptA); + for (NxI32 i = -1; ++i < count;) + { + ptB = poly->Vertex(i); + sideB = part->Classify_Point(ptB); + if (sideB > 0) + { + if (sideA < 0) + { + point v; + fm_intersectPointPlane(&ptB.x, &ptA.x, &v.x, &part->normal.x ); + outpts[out_c++] = inpts[in_c++] = v; + } + outpts[out_c++] = ptB; + } + else if (sideB < 0) + { + if (sideA > 0) + { + point v; + fm_intersectPointPlane(&ptB.x, &ptA.x, &v.x, &part->normal.x ); + outpts[out_c++] = inpts[in_c++] = v; + } + inpts[in_c++] = ptB; + } + else + outpts[out_c++] = inpts[in_c++] = ptB; + ptA = ptB; + sideA = sideB; + } + + front.set(&outpts[0], out_c); + back.set(&inpts[0], in_c); + } + + NxI32 mVcount; + point mVertices[MAXPTS]; +}; + + + +#endif + +static inline void add(const REAL *p,REAL *dest,NxU32 tstride,NxU32 &pcount) +{ + char *d = (char *) dest; + d = d + pcount*tstride; + dest = (REAL *) d; + dest[0] = p[0]; + dest[1] = p[1]; + dest[2] = p[2]; + pcount++; + assert( pcount <= 4 ); +} + + +PlaneTriResult fm_planeTriIntersection(const REAL *_plane, // the plane equation in Ax+By+Cz+D format + const REAL *triangle, // the source triangle. + NxU32 tstride, // stride in bytes of the input and output *vertices* + REAL epsilon, // the co-planar epsilon value. + REAL *front, // the triangle in front of the + NxU32 &fcount, // number of vertices in the 'front' triangle + REAL *back, // the triangle in back of the plane + NxU32 &bcount) // the number of vertices in the 'back' triangle. +{ + + fcount = 0; + bcount = 0; + + const char *tsource = (const char *) triangle; + + // get the three vertices of the triangle. + const REAL *p1 = (const REAL *) (tsource); + const REAL *p2 = (const REAL *) (tsource+tstride); + const REAL *p3 = (const REAL *) (tsource+tstride*2); + + + PlaneTriResult r1 = fm_getSidePlane(p1,_plane,epsilon); // compute the side of the plane each vertex is on + PlaneTriResult r2 = fm_getSidePlane(p2,_plane,epsilon); + PlaneTriResult r3 = fm_getSidePlane(p3,_plane,epsilon); + + // If any of the points lay right *on* the plane.... + if ( r1 == PTR_ON_PLANE || r2 == PTR_ON_PLANE || r3 == PTR_ON_PLANE ) + { + // If the triangle is completely co-planar, then just treat it as 'front' and return! + if ( r1 == PTR_ON_PLANE && r2 == PTR_ON_PLANE && r3 == PTR_ON_PLANE ) + { + add(p1,front,tstride,fcount); + add(p2,front,tstride,fcount); + add(p3,front,tstride,fcount); + return PTR_FRONT; + } + // Decide to place the co-planar points on the same side as the co-planar point. + PlaneTriResult r= PTR_ON_PLANE; + if ( r1 != PTR_ON_PLANE ) + r = r1; + else if ( r2 != PTR_ON_PLANE ) + r = r2; + else if ( r3 != PTR_ON_PLANE ) + r = r3; + + if ( r1 == PTR_ON_PLANE ) r1 = r; + if ( r2 == PTR_ON_PLANE ) r2 = r; + if ( r3 == PTR_ON_PLANE ) r3 = r; + + } + + if ( r1 == r2 && r1 == r3 ) // if all three vertices are on the same side of the plane. + { + if ( r1 == PTR_FRONT ) // if all three are in front of the plane, then copy to the 'front' output triangle. + { + add(p1,front,tstride,fcount); + add(p2,front,tstride,fcount); + add(p3,front,tstride,fcount); + } + else + { + add(p1,back,tstride,bcount); // if all three are in 'back' then copy to the 'back' output triangle. + add(p2,back,tstride,bcount); + add(p3,back,tstride,bcount); + } + return r1; // if all three points are on the same side of the plane return result + } + + + polygon pi(p1,p2,p3); + polygon pfront,pback; + + plane part(_plane); + + pi.Split_Polygon(&pi,&part,pfront,pback); + + for (NxI32 i=0; i bmax[0] ) bmax[0] = t[0]; + if ( t[1] > bmax[1] ) bmax[1] = t[1]; + if ( t[2] > bmax[2] ) bmax[2] = t[2]; + + src+=pstride; + } + + REAL center[3]; + + sides[0] = bmax[0]-bmin[0]; + sides[1] = bmax[1]-bmin[1]; + sides[2] = bmax[2]-bmin[2]; + + center[0] = sides[0]*0.5f+bmin[0]; + center[1] = sides[1]*0.5f+bmin[1]; + center[2] = sides[2]*0.5f+bmin[2]; + + REAL ocenter[3]; + + fm_rotate(matrix,center,ocenter); + + matrix[12]+=ocenter[0]; + matrix[13]+=ocenter[1]; + matrix[14]+=ocenter[2]; + +} + +void fm_computeBestFitOBB(NxU32 vcount,const REAL *points,NxU32 pstride,REAL *sides,REAL *matrix,bool bruteForce) +{ + REAL plane[4]; + fm_computeBestFitPlane(vcount,points,pstride,0,0,plane); + fm_planeToMatrix(plane,matrix); + computeOBB( vcount, points, pstride, sides, matrix ); + + REAL refmatrix[16]; + memcpy(refmatrix,matrix,16*sizeof(REAL)); + + REAL volume = sides[0]*sides[1]*sides[2]; + if ( bruteForce ) + { + for (REAL a=10; a<180; a+=10) + { + REAL quat[4]; + fm_eulerToQuat(0,a*FM_DEG_TO_RAD,0,quat); + REAL temp[16]; + REAL pmatrix[16]; + fm_quatToMatrix(quat,temp); + fm_matrixMultiply(temp,refmatrix,pmatrix); + REAL psides[3]; + computeOBB( vcount, points, pstride, psides, pmatrix ); + REAL v = psides[0]*psides[1]*psides[2]; + if ( v < volume ) + { + volume = v; + memcpy(matrix,pmatrix,sizeof(REAL)*16); + sides[0] = psides[0]; + sides[1] = psides[1]; + sides[2] = psides[2]; + } + } + } +} + +void fm_computeBestFitOBB(NxU32 vcount,const REAL *points,NxU32 pstride,REAL *sides,REAL *pos,REAL *quat,bool bruteForce) +{ + REAL matrix[16]; + fm_computeBestFitOBB(vcount,points,pstride,sides,matrix,bruteForce); + fm_getTranslation(matrix,pos); + fm_matrixToQuat(matrix,quat); +} + +void fm_computeBestFitABB(NxU32 vcount,const REAL *points,NxU32 pstride,REAL *sides,REAL *pos) +{ + REAL bmin[3]; + REAL bmax[3]; + + bmin[0] = points[0]; + bmin[1] = points[1]; + bmin[2] = points[2]; + + bmax[0] = points[0]; + bmax[1] = points[1]; + bmax[2] = points[2]; + + const char *cp = (const char *) points; + for (NxU32 i=0; i bmax[0] ) bmax[0] = p[0]; + if ( p[1] > bmax[1] ) bmax[1] = p[1]; + if ( p[2] > bmax[2] ) bmax[2] = p[2]; + + cp+=pstride; + } + + + sides[0] = bmax[0] - bmin[0]; + sides[1] = bmax[1] - bmin[1]; + sides[2] = bmax[2] - bmin[2]; + + pos[0] = bmin[0]+sides[0]*0.5f; + pos[1] = bmin[1]+sides[1]*0.5f; + pos[2] = bmin[2]+sides[2]*0.5f; + +} + + +void fm_planeToMatrix(const REAL *plane,REAL *matrix) // convert a plane equation to a 4x4 rotation matrix +{ + REAL ref[3] = { 0, 1, 0 }; + REAL quat[4]; + fm_rotationArc(ref,plane,quat); + fm_quatToMatrix(quat,matrix); + REAL origin[3] = { 0, -plane[3], 0 }; + REAL center[3]; + fm_transform(matrix,origin,center); + fm_setTranslation(center,matrix); +} + +void fm_planeToQuat(const REAL *plane,REAL *quat,REAL *pos) // convert a plane equation to a quaternion and translation +{ + REAL ref[3] = { 0, 1, 0 }; + REAL matrix[16]; + fm_rotationArc(ref,plane,quat); + fm_quatToMatrix(quat,matrix); + REAL origin[3] = { 0, plane[3], 0 }; + fm_transform(matrix,origin,pos); +} + +void fm_eulerMatrix(REAL ax,REAL ay,REAL az,REAL *matrix) // convert euler (in radians) to a dest 4x4 matrix (translation set to zero) +{ + REAL quat[4]; + fm_eulerToQuat(ax,ay,az,quat); + fm_quatToMatrix(quat,matrix); +} + + +//********************************************************** +//********************************************************** +//**** Vertex Welding +//********************************************************** +//********************************************************** + +#ifndef VERTEX_INDEX_H + +#define VERTEX_INDEX_H + +namespace VERTEX_INDEX +{ + +class KdTreeNode; + +typedef CONVEX_DECOMPOSITION::Array< KdTreeNode * > KdTreeNodeVector; + +enum Axes +{ + X_AXIS = 0, + Y_AXIS = 1, + Z_AXIS = 2 +}; + +class KdTreeFindNode +{ +public: + KdTreeFindNode(void) + { + mNode = 0; + mDistance = 0; + } + KdTreeNode *mNode; + NxF64 mDistance; +}; + +class KdTreeInterface +{ +public: + virtual const NxF64 * getPositionDouble(NxU32 index) const = 0; + virtual const NxF32 * getPositionFloat(NxU32 index) const = 0; +}; + +class KdTreeNode +{ +public: + KdTreeNode(void) + { + mIndex = 0; + mLeft = 0; + mRight = 0; + } + + KdTreeNode(NxU32 index) + { + mIndex = index; + mLeft = 0; + mRight = 0; + }; + + ~KdTreeNode(void) + { + } + + + void addDouble(KdTreeNode *node,Axes dim,const KdTreeInterface *iface) + { + const NxF64 *nodePosition = iface->getPositionDouble( node->mIndex ); + const NxF64 *position = iface->getPositionDouble( mIndex ); + switch ( dim ) + { + case X_AXIS: + if ( nodePosition[0] <= position[0] ) + { + if ( mLeft ) + mLeft->addDouble(node,Y_AXIS,iface); + else + mLeft = node; + } + else + { + if ( mRight ) + mRight->addDouble(node,Y_AXIS,iface); + else + mRight = node; + } + break; + case Y_AXIS: + if ( nodePosition[1] <= position[1] ) + { + if ( mLeft ) + mLeft->addDouble(node,Z_AXIS,iface); + else + mLeft = node; + } + else + { + if ( mRight ) + mRight->addDouble(node,Z_AXIS,iface); + else + mRight = node; + } + break; + case Z_AXIS: + if ( nodePosition[2] <= position[2] ) + { + if ( mLeft ) + mLeft->addDouble(node,X_AXIS,iface); + else + mLeft = node; + } + else + { + if ( mRight ) + mRight->addDouble(node,X_AXIS,iface); + else + mRight = node; + } + break; + } + + } + + + void addFloat(KdTreeNode *node,Axes dim,const KdTreeInterface *iface) + { + const NxF32 *nodePosition = iface->getPositionFloat( node->mIndex ); + const NxF32 *position = iface->getPositionFloat( mIndex ); + switch ( dim ) + { + case X_AXIS: + if ( nodePosition[0] <= position[0] ) + { + if ( mLeft ) + mLeft->addFloat(node,Y_AXIS,iface); + else + mLeft = node; + } + else + { + if ( mRight ) + mRight->addFloat(node,Y_AXIS,iface); + else + mRight = node; + } + break; + case Y_AXIS: + if ( nodePosition[1] <= position[1] ) + { + if ( mLeft ) + mLeft->addFloat(node,Z_AXIS,iface); + else + mLeft = node; + } + else + { + if ( mRight ) + mRight->addFloat(node,Z_AXIS,iface); + else + mRight = node; + } + break; + case Z_AXIS: + if ( nodePosition[2] <= position[2] ) + { + if ( mLeft ) + mLeft->addFloat(node,X_AXIS,iface); + else + mLeft = node; + } + else + { + if ( mRight ) + mRight->addFloat(node,X_AXIS,iface); + else + mRight = node; + } + break; + } + + } + + + NxU32 getIndex(void) const { return mIndex; }; + + void search(Axes axis,const NxF64 *pos,NxF64 radius,NxU32 &count,NxU32 maxObjects,KdTreeFindNode *found,const KdTreeInterface *iface) + { + + const NxF64 *position = iface->getPositionDouble(mIndex); + + NxF64 dx = pos[0] - position[0]; + NxF64 dy = pos[1] - position[1]; + NxF64 dz = pos[2] - position[2]; + + KdTreeNode *search1 = 0; + KdTreeNode *search2 = 0; + + switch ( axis ) + { + case X_AXIS: + if ( dx <= 0 ) // JWR if we are to the left + { + search1 = mLeft; // JWR then search to the left + if ( -dx < radius ) // JWR if distance to the right is less than our search radius, continue on the right as well. + search2 = mRight; + } + else + { + search1 = mRight; // JWR ok, we go down the left tree + if ( dx < radius ) // JWR if the distance from the right is less than our search radius + search2 = mLeft; + } + axis = Y_AXIS; + break; + case Y_AXIS: + if ( dy <= 0 ) + { + search1 = mLeft; + if ( -dy < radius ) + search2 = mRight; + } + else + { + search1 = mRight; + if ( dy < radius ) + search2 = mLeft; + } + axis = Z_AXIS; + break; + case Z_AXIS: + if ( dz <= 0 ) + { + search1 = mLeft; + if ( -dz < radius ) + search2 = mRight; + } + else + { + search1 = mRight; + if ( dz < radius ) + search2 = mLeft; + } + axis = X_AXIS; + break; + } + + NxF64 r2 = radius*radius; + NxF64 m = dx*dx+dy*dy+dz*dz; + + if ( m < r2 ) + { + switch ( count ) + { + case 0: + found[count].mNode = this; + found[count].mDistance = m; + break; + case 1: + if ( m < found[0].mDistance ) + { + if ( maxObjects == 1 ) + { + found[0].mNode = this; + found[0].mDistance = m; + } + else + { + found[1] = found[0]; + found[0].mNode = this; + found[0].mDistance = m; + } + } + else if ( maxObjects > 1) + { + found[1].mNode = this; + found[1].mDistance = m; + } + break; + default: + { + bool inserted = false; + + for (NxU32 i=0; i= maxObjects ) scan=maxObjects-1; + for (NxU32 j=scan; j>i; j--) + { + found[j] = found[j-1]; + } + found[i].mNode = this; + found[i].mDistance = m; + inserted = true; + break; + } + } + + if ( !inserted && count < maxObjects ) + { + found[count].mNode = this; + found[count].mDistance = m; + } + } + break; + } + count++; + if ( count > maxObjects ) + { + count = maxObjects; + } + } + + + if ( search1 ) + search1->search( axis, pos,radius, count, maxObjects, found, iface); + + if ( search2 ) + search2->search( axis, pos,radius, count, maxObjects, found, iface); + + } + + void search(Axes axis,const NxF32 *pos,NxF32 radius,NxU32 &count,NxU32 maxObjects,KdTreeFindNode *found,const KdTreeInterface *iface) + { + + const NxF32 *position = iface->getPositionFloat(mIndex); + + NxF32 dx = pos[0] - position[0]; + NxF32 dy = pos[1] - position[1]; + NxF32 dz = pos[2] - position[2]; + + KdTreeNode *search1 = 0; + KdTreeNode *search2 = 0; + + switch ( axis ) + { + case X_AXIS: + if ( dx <= 0 ) // JWR if we are to the left + { + search1 = mLeft; // JWR then search to the left + if ( -dx < radius ) // JWR if distance to the right is less than our search radius, continue on the right as well. + search2 = mRight; + } + else + { + search1 = mRight; // JWR ok, we go down the left tree + if ( dx < radius ) // JWR if the distance from the right is less than our search radius + search2 = mLeft; + } + axis = Y_AXIS; + break; + case Y_AXIS: + if ( dy <= 0 ) + { + search1 = mLeft; + if ( -dy < radius ) + search2 = mRight; + } + else + { + search1 = mRight; + if ( dy < radius ) + search2 = mLeft; + } + axis = Z_AXIS; + break; + case Z_AXIS: + if ( dz <= 0 ) + { + search1 = mLeft; + if ( -dz < radius ) + search2 = mRight; + } + else + { + search1 = mRight; + if ( dz < radius ) + search2 = mLeft; + } + axis = X_AXIS; + break; + } + + NxF32 r2 = radius*radius; + NxF32 m = dx*dx+dy*dy+dz*dz; + + if ( m < r2 ) + { + switch ( count ) + { + case 0: + found[count].mNode = this; + found[count].mDistance = m; + break; + case 1: + if ( m < found[0].mDistance ) + { + if ( maxObjects == 1 ) + { + found[0].mNode = this; + found[0].mDistance = m; + } + else + { + found[1] = found[0]; + found[0].mNode = this; + found[0].mDistance = m; + } + } + else if ( maxObjects > 1) + { + found[1].mNode = this; + found[1].mDistance = m; + } + break; + default: + { + bool inserted = false; + + for (NxU32 i=0; i= maxObjects ) scan=maxObjects-1; + for (NxU32 j=scan; j>i; j--) + { + found[j] = found[j-1]; + } + found[i].mNode = this; + found[i].mDistance = m; + inserted = true; + break; + } + } + + if ( !inserted && count < maxObjects ) + { + found[count].mNode = this; + found[count].mDistance = m; + } + } + break; + } + count++; + if ( count > maxObjects ) + { + count = maxObjects; + } + } + + + if ( search1 ) + search1->search( axis, pos,radius, count, maxObjects, found, iface); + + if ( search2 ) + search2->search( axis, pos,radius, count, maxObjects, found, iface); + + } + +private: + + void setLeft(KdTreeNode *left) { mLeft = left; }; + void setRight(KdTreeNode *right) { mRight = right; }; + + KdTreeNode *getLeft(void) { return mLeft; } + KdTreeNode *getRight(void) { return mRight; } + + NxU32 mIndex; + KdTreeNode *mLeft; + KdTreeNode *mRight; +}; + + +#define MAX_BUNDLE_SIZE 1024 // 1024 nodes at a time, to minimize memory allocation and guarentee that pointers are persistent. + +class KdTreeNodeBundle : public Memalloc +{ +public: + + KdTreeNodeBundle(void) + { + mNext = 0; + mIndex = 0; + } + + bool isFull(void) const + { + return (bool)( mIndex == MAX_BUNDLE_SIZE ); + } + + KdTreeNode * getNextNode(void) + { + assert(mIndex DoubleVector; +typedef CONVEX_DECOMPOSITION::Array< NxF32 > FloatVector; + +class KdTree : public KdTreeInterface, public Memalloc +{ +public: + KdTree(void) + { + mRoot = 0; + mBundle = 0; + mVcount = 0; + mUseDouble = false; + } + + virtual ~KdTree(void) + { + reset(); + } + + const NxF64 * getPositionDouble(NxU32 index) const + { + assert( mUseDouble ); + assert ( index < mVcount ); + return &mVerticesDouble[index*3]; + } + + const NxF32 * getPositionFloat(NxU32 index) const + { + assert( !mUseDouble ); + assert ( index < mVcount ); + return &mVerticesFloat[index*3]; + } + + NxU32 search(const NxF64 *pos,NxF64 radius,NxU32 maxObjects,KdTreeFindNode *found) const + { + assert( mUseDouble ); + if ( !mRoot ) return 0; + NxU32 count = 0; + mRoot->search(X_AXIS,pos,radius,count,maxObjects,found,this); + return count; + } + + NxU32 search(const NxF32 *pos,NxF32 radius,NxU32 maxObjects,KdTreeFindNode *found) const + { + assert( !mUseDouble ); + if ( !mRoot ) return 0; + NxU32 count = 0; + mRoot->search(X_AXIS,pos,radius,count,maxObjects,found,this); + return count; + } + + void reset(void) + { + mRoot = 0; + mVerticesDouble.clear(); + mVerticesFloat.clear(); + KdTreeNodeBundle *bundle = mBundle; + while ( bundle ) + { + KdTreeNodeBundle *next = bundle->mNext; + delete bundle; + bundle = next; + } + mBundle = 0; + mVcount = 0; + } + + NxU32 add(NxF64 x,NxF64 y,NxF64 z) + { + assert(mUseDouble); + NxU32 ret = mVcount; + mVerticesDouble.pushBack(x); + mVerticesDouble.pushBack(y); + mVerticesDouble.pushBack(z); + mVcount++; + KdTreeNode *node = getNewNode(ret); + if ( mRoot ) + { + mRoot->addDouble(node,X_AXIS,this); + } + else + { + mRoot = node; + } + return ret; + } + + NxU32 add(NxF32 x,NxF32 y,NxF32 z) + { + assert(!mUseDouble); + NxU32 ret = mVcount; + mVerticesFloat.pushBack(x); + mVerticesFloat.pushBack(y); + mVerticesFloat.pushBack(z); + mVcount++; + KdTreeNode *node = getNewNode(ret); + if ( mRoot ) + { + mRoot->addFloat(node,X_AXIS,this); + } + else + { + mRoot = node; + } + return ret; + } + + KdTreeNode * getNewNode(NxU32 index) + { + if ( mBundle == 0 ) + { + mBundle = MEMALLOC_NEW(KdTreeNodeBundle); + } + if ( mBundle->isFull() ) + { + KdTreeNodeBundle *bundle = MEMALLOC_NEW(KdTreeNodeBundle); + mBundle->mNext = bundle; + mBundle = bundle; + } + KdTreeNode *node = mBundle->getNextNode(); + new ( node ) KdTreeNode(index); + return node; + } + + NxU32 getNearest(const NxF64 *pos,NxF64 radius,bool &_found) const // returns the nearest possible neighbor's index. + { + assert( mUseDouble ); + NxU32 ret = 0; + + _found = false; + KdTreeFindNode found[1]; + NxU32 count = search(pos,radius,1,found); + if ( count ) + { + KdTreeNode *node = found[0].mNode; + ret = node->getIndex(); + _found = true; + } + return ret; + } + + NxU32 getNearest(const NxF32 *pos,NxF32 radius,bool &_found) const // returns the nearest possible neighbor's index. + { + assert( !mUseDouble ); + NxU32 ret = 0; + + _found = false; + KdTreeFindNode found[1]; + NxU32 count = search(pos,radius,1,found); + if ( count ) + { + KdTreeNode *node = found[0].mNode; + ret = node->getIndex(); + _found = true; + } + return ret; + } + + const NxF64 * getVerticesDouble(void) const + { + assert( mUseDouble ); + const NxF64 *ret = 0; + if ( !mVerticesDouble.empty() ) + { + ret = &mVerticesDouble[0]; + } + return ret; + } + + const NxF32 * getVerticesFloat(void) const + { + assert( !mUseDouble ); + const NxF32 * ret = 0; + if ( !mVerticesFloat.empty() ) + { + ret = &mVerticesFloat[0]; + } + return ret; + } + + NxU32 getVcount(void) const { return mVcount; }; + + void setUseDouble(bool useDouble) + { + mUseDouble = useDouble; + } + +private: + bool mUseDouble; + KdTreeNode *mRoot; + KdTreeNodeBundle *mBundle; + NxU32 mVcount; + DoubleVector mVerticesDouble; + FloatVector mVerticesFloat; +}; + +}; // end of namespace VERTEX_INDEX + +class MyVertexIndex : public fm_VertexIndex, public Memalloc +{ +public: + MyVertexIndex(NxF64 granularity,bool snapToGrid) + { + mDoubleGranularity = granularity; + mFloatGranularity = (NxF32)granularity; + mSnapToGrid = snapToGrid; + mUseDouble = true; + mKdTree.setUseDouble(true); + } + + MyVertexIndex(NxF32 granularity,bool snapToGrid) + { + mDoubleGranularity = granularity; + mFloatGranularity = (NxF32)granularity; + mSnapToGrid = snapToGrid; + mUseDouble = false; + mKdTree.setUseDouble(false); + } + + virtual ~MyVertexIndex(void) + { + + } + + + NxF64 snapToGrid(NxF64 p) + { + NxF64 m = fmod(p,mDoubleGranularity); + p-=m; + return p; + } + + NxF32 snapToGrid(NxF32 p) + { + NxF32 m = fmodf(p,mFloatGranularity); + p-=m; + return p; + } + + NxU32 getIndex(const NxF32 *_p,bool &newPos) // get index for a vector NxF32 + { + NxU32 ret; + + if ( mUseDouble ) + { + NxF64 p[3]; + p[0] = _p[0]; + p[1] = _p[1]; + p[2] = _p[2]; + return getIndex(p,newPos); + } + + newPos = false; + + NxF32 p[3]; + + if ( mSnapToGrid ) + { + p[0] = snapToGrid(_p[0]); + p[1] = snapToGrid(_p[1]); + p[2] = snapToGrid(_p[2]); + } + else + { + p[0] = _p[0]; + p[1] = _p[1]; + p[2] = _p[2]; + } + + bool found; + ret = mKdTree.getNearest(p,mFloatGranularity,found); + if ( !found ) + { + newPos = true; + ret = mKdTree.add(p[0],p[1],p[2]); + } + + + return ret; + } + + NxU32 getIndex(const NxF64 *_p,bool &newPos) // get index for a vector NxF64 + { + NxU32 ret; + + if ( !mUseDouble ) + { + NxF32 p[3]; + p[0] = (NxF32)_p[0]; + p[1] = (NxF32)_p[1]; + p[2] = (NxF32)_p[2]; + return getIndex(p,newPos); + } + + newPos = false; + + NxF64 p[3]; + + if ( mSnapToGrid ) + { + p[0] = snapToGrid(_p[0]); + p[1] = snapToGrid(_p[1]); + p[2] = snapToGrid(_p[2]); + } + else + { + p[0] = _p[0]; + p[1] = _p[1]; + p[2] = _p[2]; + } + + bool found; + ret = mKdTree.getNearest(p,mDoubleGranularity,found); + if ( !found ) + { + newPos = true; + ret = mKdTree.add(p[0],p[1],p[2]); + } + + + return ret; + } + + const NxF32 * getVerticesFloat(void) const + { + const NxF32 * ret = 0; + + assert( !mUseDouble ); + + ret = mKdTree.getVerticesFloat(); + + return ret; + } + + const NxF64 * getVerticesDouble(void) const + { + const NxF64 * ret = 0; + + assert( mUseDouble ); + + ret = mKdTree.getVerticesDouble(); + + return ret; + } + + const NxF32 * getVertexFloat(NxU32 index) const + { + const NxF32 * ret = 0; + assert( !mUseDouble ); +#ifdef _DEBUG + NxU32 vcount = mKdTree.getVcount(); + assert( index < vcount ); +#endif + ret = mKdTree.getVerticesFloat(); + ret = &ret[index*3]; + return ret; + } + + const NxF64 * getVertexDouble(NxU32 index) const + { + const NxF64 * ret = 0; + assert( mUseDouble ); +#ifdef _DEBUG + NxU32 vcount = mKdTree.getVcount(); + assert( index < vcount ); +#endif + ret = mKdTree.getVerticesDouble(); + ret = &ret[index*3]; + + return ret; + } + + NxU32 getVcount(void) const + { + return mKdTree.getVcount(); + } + + bool isDouble(void) const + { + return mUseDouble; + } + + + bool saveAsObj(const char *fname,NxU32 tcount,NxU32 *indices) + { + bool ret = false; + + + FILE *fph = fopen(fname,"wb"); + if ( fph ) + { + ret = true; + + NxU32 vcount = getVcount(); + if ( mUseDouble ) + { + const NxF64 *v = getVerticesDouble(); + for (NxU32 i=0; i(ret); +} + +fm_VertexIndex * fm_createVertexIndex(NxF32 granularity,bool snapToGrid) // create an indexed vertext system for floats +{ + MyVertexIndex *ret = MEMALLOC_NEW(MyVertexIndex)(granularity,snapToGrid); + return static_cast< fm_VertexIndex *>(ret); +} + +void fm_releaseVertexIndex(fm_VertexIndex *vindex) +{ + MyVertexIndex *m = static_cast< MyVertexIndex *>(vindex); + delete m; +} + +#endif // END OF VERTEX WELDING CODE + + +//********************************************************** +//********************************************************** +//**** LineSweep Line-Segment Intersection Code +//********************************************************** +//********************************************************** + +//#ifndef LINE_SWEEP_H +#if 0 + +#define LINE_SWEEP_H + +class fm_quickSort +{ +public: + void qsort(void **base,NxI32 num); // perform the qsort. +protected: + // -1 less, 0 equal, +1 greater. + virtual NxI32 compare(void **p1,void **p2) = 0; +private: + void inline swap(char **a,char **b); +}; + + +void fm_quickSort::swap(char **a,char **b) +{ + char *tmp; + + if ( a != b ) + { + tmp = *a; + *a++ = *b; + *b++ = tmp; + } +} + + +void fm_quickSort::qsort(void **b,NxI32 num) +{ + char *lo,*hi; + char *mid; + char *bottom, *top; + NxI32 size; + char *lostk[30], *histk[30]; + NxI32 stkptr; + char **base = (char **)b; + + if (num < 2 ) return; + + stkptr = 0; + + lo = (char *)base; + hi = (char *)base + sizeof(char **) * (num-1); + +nextone: + + size = (NxI32)(hi - lo) / sizeof(char**) + 1; + + mid = lo + (size / 2) * sizeof(char **); + swap((char **)mid,(char **)lo); + bottom = lo; + top = hi + sizeof(char **); + + for (;;) + { + do + { + bottom += sizeof(char **); + } while (bottom <= hi && compare((void **)bottom,(void **)lo) <= 0); + + do + { + top -= sizeof(char **); + } while (top > lo && compare((void **)top,(void **)lo) >= 0); + + if (top < bottom) break; + + swap((char **)bottom,(char **)top); + + } + + swap((char **)lo,(char **)top); + + if ( top - 1 - lo >= hi - bottom ) + { + if (lo + sizeof(char **) < top) + { + lostk[stkptr] = lo; + histk[stkptr] = top - sizeof(char **); + stkptr++; + } + if (bottom < hi) + { + lo = bottom; + goto nextone; + } + } + else + { + if ( bottom < hi ) + { + lostk[stkptr] = bottom; + histk[stkptr] = hi; + stkptr++; + } + if (lo + sizeof(char **) < top) + { + hi = top - sizeof(char **); + goto nextone; /* do small recursion */ + } + } + + stkptr--; + + if (stkptr >= 0) + { + lo = lostk[stkptr]; + hi = histk[stkptr]; + goto nextone; + } + return; +} + + +typedef CONVEX_DECOMPOSITION::Array< fm_LineSegment > LineSegmentVector; + +static inline void setMinMax(NxF64 &vmin,NxF64 &vmax,NxF64 v1,NxF64 v2) +{ + if ( v1 <= v2 ) + { + vmin = v1; + vmax = v2; + } + else + { + vmin = v2; + vmax = v1; + } +} + + +class Intersection +{ +public: + Intersection(void) + { + mIndex = 0; + mTime = 0; + } + Intersection(NxF64 time,const NxF64 *from,const NxF64 *to,fm_VertexIndex *vpool) + { + mTime = time; + NxF64 pos[3]; + pos[0] = (to[0]-from[0])*time+from[0]; + pos[1] = (to[1]-from[1])*time+from[1]; + pos[2] = (to[2]-from[2])*time+from[2]; + bool newPos; + mIndex = vpool->getIndex(pos,newPos); + } + + NxU32 mIndex; + NxF64 mTime; +}; + + +typedef CONVEX_DECOMPOSITION::Array< Intersection > IntersectionList; + +class MyLineSegment : public fm_LineSegment, public Memalloc +{ +public: + + void init(const fm_LineSegment &s,fm_VertexIndex *vpool,NxU32 x) + { + fm_LineSegment *dest = static_cast< fm_LineSegment *>(this); + *dest = s; + + mFlipped = false; + + const NxF64 *p1 = vpool->getVertexDouble(mE1); + const NxF64 *p2 = vpool->getVertexDouble(mE2); + + setMinMax(mMin[0],mMax[0],p1[0],p2[0]); + setMinMax(mMin[1],mMax[1],p1[1],p2[1]); + setMinMax(mMin[2],mMax[2],p1[2],p2[2]); + + if ( p1[x] <= p2[x] ) + { + mFrom[0] = p1[0]; + mFrom[1] = p1[1]; + mFrom[2] = p1[2]; + + mTo[0] = p2[0]; + mTo[1] = p2[1]; + mTo[2] = p2[2]; + } + else + { + mFrom[0] = p2[0]; + mFrom[1] = p2[1]; + mFrom[2] = p2[2]; + + mTo[0] = p1[0]; + mTo[1] = p1[1]; + mTo[2] = p1[2]; + + mFlipped = true; + + swap(mE1,mE2); + } + + } + + // we already know that the x-extent overlaps or we wouldn't be in this routine.. + void intersect(MyLineSegment *segment,NxU32 x,NxU32 y,NxU32 /* z */,fm_VertexIndex *vpool) + { + NxU32 count = 0; + + // if the two segments share any start/end points then they cannot intersect at all! + + if ( segment->mE1 == mE1 || segment->mE1 == mE2 ) count++; + if ( segment->mE2 == mE1 || segment->mE2 == mE2 ) count++; + + if ( count == 0 ) + { + if ( mMax[y] < segment->mMin[y] ) // no intersection... + { + + } + else if ( mMin[y] > segment->mMax[y] ) // no intersection + { + + } + else + { + + NxF64 a1[2]; + NxF64 a2[2]; + NxF64 b1[2]; + NxF64 b2[2]; + + a1[0] = mFrom[x]; + a1[1] = mFrom[y]; + + a2[0] = mTo[x]; + a2[1] = mTo[y]; + + b1[0] = segment->mFrom[x]; + b1[1] = segment->mFrom[y]; + + b2[0] = segment->mTo[x]; + b2[1] = segment->mTo[y]; + + + NxF64 t1,t2; + IntersectResult result = fm_intersectLineSegments2dTime(a1,a2,b1,b2,t1,t2); + + if ( result == IR_DO_INTERSECT ) + { + addIntersect(t1,vpool); + segment->addIntersect(t2,vpool); + } + + + } + } + } + + void addIntersect(NxF64 time,fm_VertexIndex *vpool) + { + Intersection intersect(time,mFrom,mTo,vpool); + + if ( mE1 == intersect.mIndex || mE2 == intersect.mIndex ) + { + //printf("Split too close to the beginning or the end of the line segment.\r\n"); + } + else + { + if ( mIntersections.empty() ) + { + mIntersections.pushBack(intersect); + } + else + { + IntersectionList::Iterator i; + for (i=mIntersections.begin(); i!=mIntersections.end(); ++i) + { + Intersection &it = (*i); + if ( it.mIndex == intersect.mIndex ) + { + //printf("Duplicate Intersection, throwing it away.\r\n"); + break; + } + else + { + if ( it.mTime > time ) + { +//*** TODO TODO TODO mIntersections.insert(i,intersect); + break; + } + } + } + if ( i==mIntersections.end() ) + { + mIntersections.pushBack(intersect); + } + } + } + } + + void getResults(LineSegmentVector &results) + { + if ( mIntersections.empty() ) + { + fm_LineSegment seg(mE1,mE2); + if ( mFlipped ) + { + swap(seg.mE1,seg.mE2); + } + results.pushBack(seg); + } + else + { + NxU32 prev = mE1; + IntersectionList::Iterator i; + for (i=mIntersections.begin(); i!=mIntersections.end(); ++i) + { + Intersection &it = (*i); + fm_LineSegment seg(prev,it.mIndex); + if ( mFlipped ) + { + swap(seg.mE1,seg.mE2); + } + results.pushBack(seg); + prev = it.mIndex; + } + fm_LineSegment seg(prev,mE2); + if ( mFlipped ) + { + swap(seg.mE1,seg.mE2); + } + results.pushBack(seg); + } + } + + void swap(NxU32 &a,NxU32 &b) + { + NxU32 temp = a; + a = b; + b = temp; + } + + bool mFlipped; + NxF64 mFrom[3]; + NxF64 mTo[3]; + NxF64 mMin[3]; + NxF64 mMax[3]; + IntersectionList mIntersections; +}; + +typedef CONVEX_DECOMPOSITION::Array< MyLineSegment > MyLineSegmentVector; + +class MyLineSweep : public fm_LineSweep, public fm_quickSort, public Memalloc +{ +public: + virtual ~MyLineSweep(void) + { + + } + fm_LineSegment * performLineSweep(const fm_LineSegment *segments,NxU32 icount,const NxF64 *planeEquation,fm_VertexIndex *pool,NxU32 &scount) + { + fm_LineSegment *ret = 0; + + FM_Axis axis = fm_getDominantAxis(planeEquation); + switch ( axis ) + { + case FM_XAXIS: + mX = 1; + mY = 2; + mZ = 0; + break; + case FM_YAXIS: + mX = 0; + mY = 2; + mZ = 1; + break; + case FM_ZAXIS: + mX = 0; + mY = 1; + mZ = 2; + break; + } + + + mResults.clear(); + scount = 0; + + MyLineSegment *mls = MEMALLOC_NEW(MyLineSegment)[icount]; + MyLineSegment **mptr = (MyLineSegment **)MEMALLOC_MALLOC(sizeof(MyLineSegment *)*icount); + + for (NxU32 i=0; imTo[mX]; + for (NxU32 j=i+1; jmFrom[mX] >= esegment ) + { + break; + } + else + { + test->intersect(segment,mX,mY,mZ,pool); + } + } + } + + for (NxU32 i=0; igetResults(mResults); + } + + + delete []mls; + MEMALLOC_FREE(mptr); + + if ( !mResults.empty() ) + { + scount = (NxU32)mResults.size(); + ret = &mResults[0]; + } + + return ret; + } + + NxI32 compare(void **p1,void **p2) + { + NxI32 ret = 0; + + MyLineSegment **m1 = (MyLineSegment **) p1; + MyLineSegment **m2 = (MyLineSegment **) p2; + + MyLineSegment *s1 = *m1; + MyLineSegment *s2 = *m2; + + if ( s1->mFrom[mX] < s2->mFrom[mX] ) + ret = -1; + else if ( s1->mFrom[mX] > s2->mFrom[mX] ) + ret = 1; + else if ( s1->mFrom[mY] < s2->mFrom[mY] ) + ret = -1; + else if ( s1->mFrom[mY] > s2->mFrom[mY] ) + ret = 1; + + return ret; + } + + NxU32 mX; // index for the x-axis + NxU32 mY; // index for the y-axis + NxU32 mZ; + fm_VertexIndex *mfm_VertexIndex; + LineSegmentVector mResults; +}; + + +fm_LineSweep * fm_createLineSweep(void) +{ + MyLineSweep *mls = MEMALLOC_NEW(MyLineSweep); + return static_cast< fm_LineSweep *>(mls); +} + +void fm_releaseLineSweep(fm_LineSweep *sweep) +{ + MyLineSweep *mls = static_cast< MyLineSweep *>(sweep); + delete mls; +} + + + +#endif // End of LineSweep code + + + + +REAL fm_computeBestFitAABB(NxU32 vcount,const REAL *points,NxU32 pstride,REAL *bmin,REAL *bmax) // returns the diagonal distance +{ + + const NxU8 *source = (const NxU8 *) points; + + bmin[0] = points[0]; + bmin[1] = points[1]; + bmin[2] = points[2]; + + bmax[0] = points[0]; + bmax[1] = points[1]; + bmax[2] = points[2]; + + + for (NxU32 i=1; i bmax[0] ) bmax[0] = p[0]; + if ( p[1] > bmax[1] ) bmax[1] = p[1]; + if ( p[2] > bmax[2] ) bmax[2] = p[2]; + + } + + REAL dx = bmax[0] - bmin[0]; + REAL dy = bmax[1] - bmin[1]; + REAL dz = bmax[2] - bmin[2]; + + return (REAL) sqrt( dx*dx + dy*dy + dz*dz ); + +} + + + +/* a = b - c */ +#define vector(a,b,c) \ + (a)[0] = (b)[0] - (c)[0]; \ + (a)[1] = (b)[1] - (c)[1]; \ + (a)[2] = (b)[2] - (c)[2]; + + + +#define innerProduct(v,q) \ + ((v)[0] * (q)[0] + \ + (v)[1] * (q)[1] + \ + (v)[2] * (q)[2]) + +#define crossProduct(a,b,c) \ + (a)[0] = (b)[1] * (c)[2] - (c)[1] * (b)[2]; \ + (a)[1] = (b)[2] * (c)[0] - (c)[2] * (b)[0]; \ + (a)[2] = (b)[0] * (c)[1] - (c)[0] * (b)[1]; + + +bool fm_lineIntersectsTriangle(const REAL *rayStart,const REAL *rayEnd,const REAL *p1,const REAL *p2,const REAL *p3,REAL *sect) +{ + REAL dir[3]; + + dir[0] = rayEnd[0] - rayStart[0]; + dir[1] = rayEnd[1] - rayStart[1]; + dir[2] = rayEnd[2] - rayStart[2]; + + REAL d = (REAL)sqrt(dir[0]*dir[0] + dir[1]*dir[1] + dir[2]*dir[2]); + REAL r = 1.0f / d; + + dir[0]*=r; + dir[1]*=r; + dir[2]*=r; + + + REAL t; + + bool ret = fm_rayIntersectsTriangle(rayStart, dir, p1, p2, p3, t ); + + if ( ret ) + { + if ( t > d ) + { + sect[0] = rayStart[0] + dir[0]*t; + sect[1] = rayStart[1] + dir[1]*t; + sect[2] = rayStart[2] + dir[2]*t; + } + else + { + ret = false; + } + } + + return ret; +} + + + +bool fm_rayIntersectsTriangle(const REAL *p,const REAL *d,const REAL *v0,const REAL *v1,const REAL *v2,REAL &t) +{ + REAL e1[3],e2[3],h[3],s[3],q[3]; + REAL a,f,u,v; + + vector(e1,v1,v0); + vector(e2,v2,v0); + crossProduct(h,d,e2); + a = innerProduct(e1,h); + + if (a > -0.00001 && a < 0.00001) + return(false); + + f = 1/a; + vector(s,p,v0); + u = f * (innerProduct(s,h)); + + if (u < 0.0 || u > 1.0) + return(false); + + crossProduct(q,s,e1); + v = f * innerProduct(d,q); + if (v < 0.0 || u + v > 1.0) + return(false); + // at this stage we can compute t to find out where + // the intersection point is on the line + t = f * innerProduct(e2,q); + if (t > 0) // ray intersection + return(true); + else // this means that there is a line intersection + // but not a ray intersection + return (false); +} + + +inline REAL det(const REAL *p1,const REAL *p2,const REAL *p3) +{ + return p1[0]*p2[1]*p3[2] + p2[0]*p3[1]*p1[2] + p3[0]*p1[1]*p2[2] -p1[0]*p3[1]*p2[2] - p2[0]*p1[1]*p3[2] - p3[0]*p2[1]*p1[2]; +} + + +REAL fm_computeMeshVolume(const REAL *vertices,NxU32 tcount,const NxU32 *indices) +{ + REAL volume = 0; + + for (NxU32 i=0; i= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f)); +} + + +REAL fm_areaPolygon2d(NxU32 pcount,const REAL *points,NxU32 pstride) +{ + NxI32 n = (NxI32)pcount; + + REAL A=0.0f; + for(NxI32 p=n-1,q=0; q= y || y2 < y && y1 >= y ) + { + if (x1+(y-y1)/(y2-y1)*(x2-x1)= 3 ) + { + const REAL *prev = fm_getPoint(points,pstride,pcount-1); + const REAL *current = points; + const REAL *next = fm_getPoint(points,pstride,1); + REAL *dest = _dest; + + for (NxU32 i=0; i class Rect3d +{ +public: + Rect3d(void) { }; + + Rect3d(const T *bmin,const T *bmax) + { + + mMin[0] = bmin[0]; + mMin[1] = bmin[1]; + mMin[2] = bmin[2]; + + mMax[0] = bmax[0]; + mMax[1] = bmax[1]; + mMax[2] = bmax[2]; + + } + + void SetMin(const T *bmin) + { + mMin[0] = bmin[0]; + mMin[1] = bmin[1]; + mMin[2] = bmin[2]; + } + + void SetMax(const T *bmax) + { + mMax[0] = bmax[0]; + mMax[1] = bmax[1]; + mMax[2] = bmax[2]; + } + + void SetMin(T x,T y,T z) + { + mMin[0] = x; + mMin[1] = y; + mMin[2] = z; + } + + void SetMax(T x,T y,T z) + { + mMax[0] = x; + mMax[1] = y; + mMax[2] = z; + } + + T mMin[3]; + T mMax[3]; +}; + +#endif + +void splitRect(NxU32 axis, + const Rect3d &source, + Rect3d &b1, + Rect3d &b2, + const REAL *midpoint) +{ + switch ( axis ) + { + case 0: + b1.SetMin(source.mMin); + b1.SetMax( midpoint[0], source.mMax[1], source.mMax[2] ); + + b2.SetMin( midpoint[0], source.mMin[1], source.mMin[2] ); + b2.SetMax(source.mMax); + + break; + case 1: + b1.SetMin(source.mMin); + b1.SetMax( source.mMax[0], midpoint[1], source.mMax[2] ); + + b2.SetMin( source.mMin[0], midpoint[1], source.mMin[2] ); + b2.SetMax(source.mMax); + + break; + case 2: + b1.SetMin(source.mMin); + b1.SetMax( source.mMax[0], source.mMax[1], midpoint[2] ); + + b2.SetMin( source.mMin[0], source.mMin[1], midpoint[2] ); + b2.SetMax(source.mMax); + + break; + } +} + +bool fm_computeSplitPlane(NxU32 vcount, + const REAL *vertices, + NxU32 /* tcount */, + const NxU32 * /* indices */, + REAL *plane) +{ + + REAL sides[3]; + REAL matrix[16]; + + fm_computeBestFitOBB( vcount, vertices, sizeof(REAL)*3, sides, matrix ); + + REAL bmax[3]; + REAL bmin[3]; + + bmax[0] = sides[0]*0.5f; + bmax[1] = sides[1]*0.5f; + bmax[2] = sides[2]*0.5f; + + bmin[0] = -bmax[0]; + bmin[1] = -bmax[1]; + bmin[2] = -bmax[2]; + + + REAL dx = sides[0]; + REAL dy = sides[1]; + REAL dz = sides[2]; + + + REAL laxis = dx; + + NxU32 axis = 0; + + if ( dy > dx ) + { + axis = 1; + laxis = dy; + } + + if ( dz > dx && dz > dy ) + { + axis = 2; + laxis = dz; + } + + REAL p1[3]; + REAL p2[3]; + REAL p3[3]; + + p3[0] = p2[0] = p1[0] = bmin[0] + dx*0.5f; + p3[1] = p2[1] = p1[1] = bmin[1] + dy*0.5f; + p3[2] = p2[2] = p1[2] = bmin[2] + dz*0.5f; + + Rect3d b(bmin,bmax); + + Rect3d b1,b2; + + splitRect(axis,b,b1,b2,p1); + + + switch ( axis ) + { + case 0: + p2[1] = bmin[1]; + p2[2] = bmin[2]; + + if ( dz > dy ) + { + p3[1] = bmax[1]; + p3[2] = bmin[2]; + } + else + { + p3[1] = bmin[1]; + p3[2] = bmax[2]; + } + + break; + case 1: + p2[0] = bmin[0]; + p2[2] = bmin[2]; + + if ( dx > dz ) + { + p3[0] = bmax[0]; + p3[2] = bmin[2]; + } + else + { + p3[0] = bmin[0]; + p3[2] = bmax[2]; + } + + break; + case 2: + p2[0] = bmin[0]; + p2[1] = bmin[1]; + + if ( dx > dy ) + { + p3[0] = bmax[0]; + p3[1] = bmin[1]; + } + else + { + p3[0] = bmin[0]; + p3[1] = bmax[1]; + } + + break; + } + + REAL tp1[3]; + REAL tp2[3]; + REAL tp3[3]; + + fm_transform(matrix,p1,tp1); + fm_transform(matrix,p2,tp2); + fm_transform(matrix,p3,tp3); + + plane[3] = fm_computePlane(tp1,tp2,tp3,plane); + + return true; + +} + +#pragma warning(disable:4100) + +void fm_nearestPointInTriangle(const REAL *nearestPoint,const REAL *p1,const REAL *p2,const REAL *p3,REAL *nearest) +{ + +} + +static REAL Partial(const REAL *a,const REAL *p) +{ + return (a[0]*p[1]) - (p[0]*a[1]); +} + +REAL fm_areaTriangle(const REAL *p0,const REAL *p1,const REAL *p2) +{ + REAL A = Partial(p0,p1); + A+= Partial(p1,p2); + A+= Partial(p2,p0); + return A*0.5f; +} + +void fm_subtract(const REAL *A,const REAL *B,REAL *diff) // compute A-B and store the result in 'diff' +{ + diff[0] = A[0]-B[0]; + diff[1] = A[1]-B[1]; + diff[2] = A[2]-B[2]; +} + + +void fm_multiplyTransform(const REAL *pA,const REAL *pB,REAL *pM) +{ + + REAL a = pA[0*4+0] * pB[0*4+0] + pA[0*4+1] * pB[1*4+0] + pA[0*4+2] * pB[2*4+0] + pA[0*4+3] * pB[3*4+0]; + REAL b = pA[0*4+0] * pB[0*4+1] + pA[0*4+1] * pB[1*4+1] + pA[0*4+2] * pB[2*4+1] + pA[0*4+3] * pB[3*4+1]; + REAL c = pA[0*4+0] * pB[0*4+2] + pA[0*4+1] * pB[1*4+2] + pA[0*4+2] * pB[2*4+2] + pA[0*4+3] * pB[3*4+2]; + REAL d = pA[0*4+0] * pB[0*4+3] + pA[0*4+1] * pB[1*4+3] + pA[0*4+2] * pB[2*4+3] + pA[0*4+3] * pB[3*4+3]; + + REAL e = pA[1*4+0] * pB[0*4+0] + pA[1*4+1] * pB[1*4+0] + pA[1*4+2] * pB[2*4+0] + pA[1*4+3] * pB[3*4+0]; + REAL f = pA[1*4+0] * pB[0*4+1] + pA[1*4+1] * pB[1*4+1] + pA[1*4+2] * pB[2*4+1] + pA[1*4+3] * pB[3*4+1]; + REAL g = pA[1*4+0] * pB[0*4+2] + pA[1*4+1] * pB[1*4+2] + pA[1*4+2] * pB[2*4+2] + pA[1*4+3] * pB[3*4+2]; + REAL h = pA[1*4+0] * pB[0*4+3] + pA[1*4+1] * pB[1*4+3] + pA[1*4+2] * pB[2*4+3] + pA[1*4+3] * pB[3*4+3]; + + REAL i = pA[2*4+0] * pB[0*4+0] + pA[2*4+1] * pB[1*4+0] + pA[2*4+2] * pB[2*4+0] + pA[2*4+3] * pB[3*4+0]; + REAL j = pA[2*4+0] * pB[0*4+1] + pA[2*4+1] * pB[1*4+1] + pA[2*4+2] * pB[2*4+1] + pA[2*4+3] * pB[3*4+1]; + REAL k = pA[2*4+0] * pB[0*4+2] + pA[2*4+1] * pB[1*4+2] + pA[2*4+2] * pB[2*4+2] + pA[2*4+3] * pB[3*4+2]; + REAL l = pA[2*4+0] * pB[0*4+3] + pA[2*4+1] * pB[1*4+3] + pA[2*4+2] * pB[2*4+3] + pA[2*4+3] * pB[3*4+3]; + + REAL m = pA[3*4+0] * pB[0*4+0] + pA[3*4+1] * pB[1*4+0] + pA[3*4+2] * pB[2*4+0] + pA[3*4+3] * pB[3*4+0]; + REAL n = pA[3*4+0] * pB[0*4+1] + pA[3*4+1] * pB[1*4+1] + pA[3*4+2] * pB[2*4+1] + pA[3*4+3] * pB[3*4+1]; + REAL o = pA[3*4+0] * pB[0*4+2] + pA[3*4+1] * pB[1*4+2] + pA[3*4+2] * pB[2*4+2] + pA[3*4+3] * pB[3*4+2]; + REAL p = pA[3*4+0] * pB[0*4+3] + pA[3*4+1] * pB[1*4+3] + pA[3*4+2] * pB[2*4+3] + pA[3*4+3] * pB[3*4+3]; + + pM[0] = a; pM[1] = b; pM[2] = c; pM[3] = d; + + pM[4] = e; pM[5] = f; pM[6] = g; pM[7] = h; + + pM[8] = i; pM[9] = j; pM[10] = k; pM[11] = l; + + pM[12] = m; pM[13] = n; pM[14] = o; pM[15] = p; +} + +void fm_multiply(REAL *A,REAL scaler) +{ + A[0]*=scaler; + A[1]*=scaler; + A[2]*=scaler; +} + +void fm_add(const REAL *A,const REAL *B,REAL *sum) +{ + sum[0] = A[0]+B[0]; + sum[1] = A[1]+B[1]; + sum[2] = A[2]+B[2]; +} + +void fm_copy3(const REAL *source,REAL *dest) +{ + dest[0] = source[0]; + dest[1] = source[1]; + dest[2] = source[2]; +} + + +NxU32 fm_copyUniqueVertices(NxU32 vcount,const REAL *input_vertices,REAL *output_vertices,NxU32 tcount,const NxU32 *input_indices,NxU32 *output_indices) +{ + NxU32 ret = 0; + + REAL *vertices = (REAL *)MEMALLOC_MALLOC(sizeof(REAL)*vcount*3); + memcpy(vertices,input_vertices,sizeof(REAL)*vcount*3); + REAL *dest = output_vertices; + + NxU32 *reindex = (NxU32 *)MEMALLOC_MALLOC(sizeof(NxU32)*vcount); + memset(reindex,0xFF,sizeof(NxU32)*vcount); + + NxU32 icount = tcount*3; + + for (NxU32 i=0; i 0 ) + { + NxU32 i1 = indices[0]; + NxU32 i2 = indices[1]; + NxU32 i3 = indices[2]; + const REAL *p1 = &vertices[i1*3]; + const REAL *p2 = &vertices[i2*3]; + const REAL *p3 = &vertices[i3*3]; + REAL plane[4]; + plane[3] = fm_computePlane(p1,p2,p3,plane); + const NxU32 *scan = &indices[3]; + for (NxU32 i=1; i= dmin && dot <= dmax ) + { + ret = true; // then the plane equation is for practical purposes identical. + } + } + + return ret; +} + + +void fm_initMinMax(REAL bmin[3],REAL bmax[3]) +{ + bmin[0] = FLT_MAX; + bmin[1] = FLT_MAX; + bmin[2] = FLT_MAX; + bmax[0] = FLT_MIN; + bmax[1] = FLT_MIN; + bmax[2] = FLT_MIN; +} + + +#ifndef TESSELATE_H + +#define TESSELATE_H + +typedef CONVEX_DECOMPOSITION::Array< NxU32 > UintVector; + +class Myfm_Tesselate : public fm_Tesselate, public Memalloc +{ +public: + virtual ~Myfm_Tesselate(void) + { + + } + + const NxU32 * tesselate(fm_VertexIndex *vindex,NxU32 tcount,const NxU32 *indices,NxF32 longEdge,NxU32 maxDepth,NxU32 &outcount) + { + const NxU32 *ret = 0; + + mMaxDepth = maxDepth; + mLongEdge = longEdge*longEdge; + mLongEdgeD = mLongEdge; + mVertices = vindex; + + if ( mVertices->isDouble() ) + { + NxU32 vcount = mVertices->getVcount(); + NxF64 *vertices = (NxF64 *)MEMALLOC_MALLOC(sizeof(NxF64)*vcount*3); + memcpy(vertices,mVertices->getVerticesDouble(),sizeof(NxF64)*vcount*3); + + for (NxU32 i=0; igetVcount(); + NxF32 *vertices = (NxF32 *)MEMALLOC_MALLOC(sizeof(NxF32)*vcount*3); + memcpy(vertices,mVertices->getVerticesFloat(),sizeof(NxF32)*vcount*3); + + + for (NxU32 i=0; i mLongEdge || l2 > mLongEdge || l3 > mLongEdge ) + split = true; + + } + + if ( split ) + { + NxU32 edge; + + if ( l1 >= l2 && l1 >= l3 ) + edge = 0; + else if ( l2 >= l1 && l2 >= l3 ) + edge = 1; + else + edge = 2; + + NxF32 split[3]; + + switch ( edge ) + { + case 0: + { + fm_lerp(p1,p2,split,0.5f); + tesselate(p1,split,p3, recurse+1 ); + tesselate(split,p2,p3, recurse+1 ); + } + break; + case 1: + { + fm_lerp(p2,p3,split,0.5f); + tesselate(p1,p2,split, recurse+1 ); + tesselate(p1,split,p3, recurse+1 ); + } + break; + case 2: + { + fm_lerp(p3,p1,split,0.5f); + tesselate(p1,p2,split, recurse+1 ); + tesselate(split,p2,p3, recurse+1 ); + } + break; + } + } + else + { + bool newp; + + NxU32 i1 = mVertices->getIndex(p1,newp); + NxU32 i2 = mVertices->getIndex(p2,newp); + NxU32 i3 = mVertices->getIndex(p3,newp); + + mIndices.pushBack(i1); + mIndices.pushBack(i2); + mIndices.pushBack(i3); + } + + } + + void tesselate(const NxF64 *p1,const NxF64 *p2,const NxF64 *p3,NxU32 recurse) + { + bool split = false; + NxF64 l1,l2,l3; + + l1 = l2 = l3 = 0; + + if ( recurse < mMaxDepth ) + { + l1 = fm_distanceSquared(p1,p2); + l2 = fm_distanceSquared(p2,p3); + l3 = fm_distanceSquared(p3,p1); + + if ( l1 > mLongEdgeD || l2 > mLongEdgeD || l3 > mLongEdgeD ) + split = true; + + } + + if ( split ) + { + NxU32 edge; + + if ( l1 >= l2 && l1 >= l3 ) + edge = 0; + else if ( l2 >= l1 && l2 >= l3 ) + edge = 1; + else + edge = 2; + + NxF64 split[3]; + + switch ( edge ) + { + case 0: + { + fm_lerp(p1,p2,split,0.5); + tesselate(p1,split,p3, recurse+1 ); + tesselate(split,p2,p3, recurse+1 ); + } + break; + case 1: + { + fm_lerp(p2,p3,split,0.5); + tesselate(p1,p2,split, recurse+1 ); + tesselate(p1,split,p3, recurse+1 ); + } + break; + case 2: + { + fm_lerp(p3,p1,split,0.5); + tesselate(p1,p2,split, recurse+1 ); + tesselate(split,p2,p3, recurse+1 ); + } + break; + } + } + else + { + bool newp; + + NxU32 i1 = mVertices->getIndex(p1,newp); + NxU32 i2 = mVertices->getIndex(p2,newp); + NxU32 i3 = mVertices->getIndex(p3,newp); + + mIndices.pushBack(i1); + mIndices.pushBack(i2); + mIndices.pushBack(i3); + } + + } + +private: + NxF32 mLongEdge; + NxF64 mLongEdgeD; + fm_VertexIndex *mVertices; + UintVector mIndices; + NxU32 mMaxDepth; +}; + +fm_Tesselate * fm_createTesselate(void) +{ + Myfm_Tesselate *m = MEMALLOC_NEW(Myfm_Tesselate); + return static_cast< fm_Tesselate * >(m); +} + +void fm_releaseTesselate(fm_Tesselate *t) +{ + Myfm_Tesselate *m = static_cast< Myfm_Tesselate *>(t); + delete m; +} + +#endif + + +#ifndef RAY_ABB_INTERSECT + +#define RAY_ABB_INTERSECT + +//! Integer representation of a floating-point value. +#define IR(x) ((NxU32&)x) + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** +* A method to compute a ray-AABB intersection. +* Original code by Andrew Woo, from "Graphics Gems", Academic Press, 1990 +* Optimized code by Pierre Terdiman, 2000 (~20-30% faster on my Celeron 500) +* Epsilon value added by Klaus Hartmann. (discarding it saves a few cycles only) +* +* Hence this version is faster as well as more robust than the original one. +* +* Should work provided: +* 1) the integer representation of 0.0f is 0x00000000 +* 2) the sign bit of the NxF32 is the most significant one +* +* Report bugs: p.terdiman@codercorner.com +* +* \param aabb [in] the axis-aligned bounding box +* \param origin [in] ray origin +* \param dir [in] ray direction +* \param coord [out] impact coordinates +* \return true if ray intersects AABB +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#define RAYAABB_EPSILON 0.00001f +bool fm_intersectRayAABB(const NxF32 MinB[3],const NxF32 MaxB[3],const NxF32 origin[3],const NxF32 dir[3],NxF32 coord[3]) +{ + bool Inside = true; + NxF32 MaxT[3]; + MaxT[0]=MaxT[1]=MaxT[2]=-1.0f; + + // Find candidate planes. + for(NxU32 i=0;i<3;i++) + { + if(origin[i] < MinB[i]) + { + coord[i] = MinB[i]; + Inside = false; + + // Calculate T distances to candidate planes + if(IR(dir[i])) MaxT[i] = (MinB[i] - origin[i]) / dir[i]; + } + else if(origin[i] > MaxB[i]) + { + coord[i] = MaxB[i]; + Inside = false; + + // Calculate T distances to candidate planes + if(IR(dir[i])) MaxT[i] = (MaxB[i] - origin[i]) / dir[i]; + } + } + + // Ray origin inside bounding box + if(Inside) + { + coord[0] = origin[0]; + coord[1] = origin[1]; + coord[2] = origin[2]; + return true; + } + + // Get largest of the maxT's for final choice of intersection + NxU32 WhichPlane = 0; + if(MaxT[1] > MaxT[WhichPlane]) WhichPlane = 1; + if(MaxT[2] > MaxT[WhichPlane]) WhichPlane = 2; + + // Check final candidate actually inside box + if(IR(MaxT[WhichPlane])&0x80000000) return false; + + for(NxU32 i=0;i<3;i++) + { + if(i!=WhichPlane) + { + coord[i] = origin[i] + MaxT[WhichPlane] * dir[i]; +#ifdef RAYAABB_EPSILON + if(coord[i] < MinB[i] - RAYAABB_EPSILON || coord[i] > MaxB[i] + RAYAABB_EPSILON) return false; +#else + if(coord[i] < MinB[i] || coord[i] > MaxB[i]) return false; +#endif + } + } + return true; // ray hits box +} + +bool fm_intersectLineSegmentAABB(const NxF32 bmin[3],const NxF32 bmax[3],const NxF32 p1[3],const NxF32 p2[3],NxF32 intersect[3]) +{ + bool ret = false; + + NxF32 dir[3]; + dir[0] = p2[0] - p1[0]; + dir[1] = p2[1] - p1[1]; + dir[2] = p2[2] - p1[2]; + NxF32 dist = fm_normalize(dir); + if ( dist > RAYAABB_EPSILON ) + { + ret = fm_intersectRayAABB(bmin,bmax,p1,dir,intersect); + if ( ret ) + { + NxF32 d = fm_distanceSquared(p1,intersect); + if ( d > (dist*dist) ) + { + ret = false; + } + } + } + return ret; +} + +#endif + +#ifndef OBB_TO_AABB + +#define OBB_TO_AABB + +#pragma warning(disable:4100) +void fm_OBBtoAABB(const NxF32 obmin[3],const NxF32 obmax[3],const NxF32 matrix[16],NxF32 abmin[3],NxF32 abmax[3]) +{ + assert(0); // not yet implemented. +} + + +const REAL * computePos(NxU32 index,const REAL *vertices,NxU32 vstride) +{ + const char *tmp = (const char *)vertices; + tmp+=(index*vstride); + return (const REAL*)tmp; +} + +void computeNormal(NxU32 index,REAL *normals,NxU32 nstride,const REAL *normal) +{ + char *tmp = (char *)normals; + tmp+=(index*nstride); + REAL *dest = (REAL *)tmp; + dest[0]+=normal[0]; + dest[1]+=normal[1]; + dest[2]+=normal[2]; +} + +void fm_computeMeanNormals(NxU32 vcount, // the number of vertices + const REAL *vertices, // the base address of the vertex position data. + NxU32 vstride, // the stride between position data. + REAL *normals, // the base address of the destination for mean vector normals + NxU32 nstride, // the stride between normals + NxU32 tcount, // the number of triangles + const NxU32 *indices) // the triangle indices +{ + + // Step #1 : Zero out the vertex normals + char *dest = (char *)normals; + for (NxU32 i=0; ixmax[0]) + Copy(xmax,caller_p); + if (caller_p[1]ymax[1]) + Copy(ymax,caller_p); + if (caller_p[2]zmax[2]) + Copy(zmax,caller_p); + + scan+=pstride; + } + + /* Set xspan = distance between the 2 points xmin & xmax (squared) */ + REAL dx = xmax[0] - xmin[0]; + REAL dy = xmax[1] - xmin[1]; + REAL dz = xmax[2] - xmin[2]; + REAL xspan = dx*dx + dy*dy + dz*dz; + + /* Same for y & z spans */ + dx = ymax[0] - ymin[0]; + dy = ymax[1] - ymin[1]; + dz = ymax[2] - ymin[2]; + REAL yspan = dx*dx + dy*dy + dz*dz; + + dx = zmax[0] - zmin[0]; + dy = zmax[1] - zmin[1]; + dz = zmax[2] - zmin[2]; + REAL zspan = dx*dx + dy*dy + dz*dz; + + /* Set points dia1 & dia2 to the maximally separated pair */ + Copy(dia1,xmin); + Copy(dia2,xmax); /* assume xspan biggest */ + REAL maxspan = xspan; + + if (yspan>maxspan) + { + maxspan = yspan; + Copy(dia1,ymin); + Copy(dia2,ymax); + } + + if (zspan>maxspan) + { + Copy(dia1,zmin); + Copy(dia2,zmax); + } + + + /* dia1,dia2 is a diameter of initial sphere */ + /* calc initial center */ + center[0] = (dia1[0]+dia2[0])*0.5f; + center[1] = (dia1[1]+dia2[1])*0.5f; + center[2] = (dia1[2]+dia2[2])*0.5f; + + /* calculate initial radius**2 and radius */ + + dx = dia2[0]-center[0]; /* x component of radius vector */ + dy = dia2[1]-center[1]; /* y component of radius vector */ + dz = dia2[2]-center[2]; /* z component of radius vector */ + + radius2 = dx*dx + dy*dy + dz*dz; + radius = REAL(sqrt(radius2)); + + /* SECOND PASS: increment current sphere */ + { + const char *scan = (const char *)points; + for (NxU32 i=0; i radius2) /* do r**2 test first */ + { /* this point is outside of current sphere */ + REAL old_to_p = REAL(sqrt(old_to_p_sq)); + /* calc radius of new sphere */ + radius = (radius + old_to_p) * 0.5f; + radius2 = radius*radius; /* for next r**2 compare */ + REAL old_to_new = old_to_p - radius; + + /* calc center of new sphere */ + + REAL recip = 1.0f /old_to_p; + + REAL cx = (radius*center[0] + old_to_new*caller_p[0]) * recip; + REAL cy = (radius*center[1] + old_to_new*caller_p[1]) * recip; + REAL cz = (radius*center[2] + old_to_new*caller_p[2]) * recip; + + Set(center,cx,cy,cz); + + scan+=pstride; + } + } + } + + return radius; +} + + +void fm_computeBestFitCapsule(NxU32 vcount,const REAL *points,NxU32 pstride,REAL &radius,REAL &height,REAL matrix[16],bool bruteForce) +{ + REAL sides[3]; + REAL omatrix[16]; + fm_computeBestFitOBB(vcount,points,pstride,sides,omatrix,bruteForce); + + NxI32 axis = 0; + if ( sides[0] > sides[1] && sides[0] > sides[2] ) + axis = 0; + else if ( sides[1] > sides[0] && sides[1] > sides[2] ) + axis = 1; + else + axis = 2; + + REAL localTransform[16]; + + REAL maxDist = 0; + REAL maxLen = 0; + + switch ( axis ) + { + case 0: + { + fm_eulerMatrix(0,0,FM_PI/2,localTransform); + fm_matrixMultiply(localTransform,omatrix,matrix); + + const NxU8 *scan = (const NxU8 *)points; + for (NxU32 i=0; i maxDist ) + { + maxDist = dist; + } + REAL l = (REAL) fabs(t[0]); + if ( l > maxLen ) + { + maxLen = l; + } + scan+=pstride; + } + } + height = sides[0]; + break; + case 1: + { + fm_eulerMatrix(0,FM_PI/2,0,localTransform); + fm_matrixMultiply(localTransform,omatrix,matrix); + + const NxU8 *scan = (const NxU8 *)points; + for (NxU32 i=0; i maxDist ) + { + maxDist = dist; + } + REAL l = (REAL) fabs(t[1]); + if ( l > maxLen ) + { + maxLen = l; + } + scan+=pstride; + } + } + height = sides[1]; + break; + case 2: + { + fm_eulerMatrix(FM_PI/2,0,0,localTransform); + fm_matrixMultiply(localTransform,omatrix,matrix); + + const NxU8 *scan = (const NxU8 *)points; + for (NxU32 i=0; i maxDist ) + { + maxDist = dist; + } + REAL l = (REAL) fabs(t[2]); + if ( l > maxLen ) + { + maxLen = l; + } + scan+=pstride; + } + } + height = sides[2]; + break; + } + radius = (REAL)sqrt(maxDist); + height = (maxLen*2)-(radius*2); +} + + +//************* Triangulation + +#ifndef TRIANGULATE_H + +#define TRIANGULATE_H + +typedef NxU32 TU32; + +class TVec +{ +public: + TVec(NxF64 _x,NxF64 _y,NxF64 _z) { x = _x; y = _y; z = _z; }; + TVec(void) { }; + + NxF64 x; + NxF64 y; + NxF64 z; +}; + +typedef CONVEX_DECOMPOSITION::Array< TVec > TVecVector; +typedef CONVEX_DECOMPOSITION::Array< TU32 > TU32Vector; + +class CTriangulator +{ +public: + /// Default constructor + CTriangulator(); + + /// Default destructor + virtual ~CTriangulator(); + + /// Triangulates the contour + void triangulate(TU32Vector &indices); + + /// Returns the given point in the triangulator array + inline TVec get(const TU32 id) { return mPoints[id]; } + + virtual void reset(void) + { + mInputPoints.clear(); + mPoints.clear(); + mIndices.clear(); + } + + virtual void addPoint(NxF64 x,NxF64 y,NxF64 z) + { + TVec v(x,y,z); + // update bounding box... + if ( mInputPoints.empty() ) + { + mMin = v; + mMax = v; + } + else + { + if ( x < mMin.x ) mMin.x = x; + if ( y < mMin.y ) mMin.y = y; + if ( z < mMin.z ) mMin.z = z; + + if ( x > mMax.x ) mMax.x = x; + if ( y > mMax.y ) mMax.y = y; + if ( z > mMax.z ) mMax.z = z; + } + mInputPoints.pushBack(v); + } + + // Triangulation happens in 2d. We could inverse transform the polygon around the normal direction, or we just use the two most signficant axes + // Here we find the two longest axes and use them to triangulate. Inverse transforming them would introduce more doubleing point error and isn't worth it. + virtual NxU32 * triangulate(NxU32 &tcount,NxF64 epsilon) + { + NxU32 *ret = 0; + tcount = 0; + mEpsilon = epsilon; + + if ( !mInputPoints.empty() ) + { + mPoints.clear(); + + NxF64 dx = mMax.x - mMin.x; // locate the first, second and third longest edges and store them in i1, i2, i3 + NxF64 dy = mMax.y - mMin.y; + NxF64 dz = mMax.z - mMin.z; + + NxU32 i1,i2,i3; + + if ( dx > dy && dx > dz ) + { + i1 = 0; + if ( dy > dz ) + { + i2 = 1; + i3 = 2; + } + else + { + i2 = 2; + i3 = 1; + } + } + else if ( dy > dx && dy > dz ) + { + i1 = 1; + if ( dx > dz ) + { + i2 = 0; + i3 = 2; + } + else + { + i2 = 2; + i3 = 0; + } + } + else + { + i1 = 2; + if ( dx > dy ) + { + i2 = 0; + i3 = 1; + } + else + { + i2 = 1; + i3 = 0; + } + } + + NxU32 pcount = (NxU32)mInputPoints.size(); + const NxF64 *points = &mInputPoints[0].x; + for (NxU32 i=0; i 2;) + { + if (0 >= (count--)) + return; + + NxI32 u = v; + if (nv <= u) + u = 0; + v = u + 1; + if (nv <= v) + v = 0; + NxI32 w = v + 1; + if (nv <= w) + w = 0; + + if (_snip(u, v, w, nv, V)) + { + NxI32 a, b, c, s, t; + a = V[u]; + b = V[v]; + c = V[w]; + if ( flipped ) + { + indices.pushBack(a); + indices.pushBack(b); + indices.pushBack(c); + } + else + { + indices.pushBack(c); + indices.pushBack(b); + indices.pushBack(a); + } + m++; + for (s = v, t = v + 1; t < nv; s++, t++) + V[s] = V[t]; + nv--; + count = 2 * nv; + } + } + + MEMALLOC_FREE(V); +} + +/// Returns the area of the contour +NxF64 CTriangulator::_area() +{ + NxI32 n = (NxU32)mPoints.size(); + NxF64 A = 0.0f; + for (NxI32 p = n - 1, q = 0; q < n; p = q++) + { + const TVec &pval = mPoints[p]; + const TVec &qval = mPoints[q]; + A += pval.x * qval.y - qval.x * pval.y; + } + A*=0.5f; + return A; +} + +bool CTriangulator::_snip(NxI32 u, NxI32 v, NxI32 w, NxI32 n, NxI32 *V) +{ + NxI32 p; + + const TVec &A = mPoints[ V[u] ]; + const TVec &B = mPoints[ V[v] ]; + const TVec &C = mPoints[ V[w] ]; + + if (mEpsilon > (((B.x - A.x) * (C.y - A.y)) - ((B.y - A.y) * (C.x - A.x))) ) + return false; + + for (p = 0; p < n; p++) + { + if ((p == u) || (p == v) || (p == w)) + continue; + const TVec &P = mPoints[ V[p] ]; + if (_insideTriangle(A, B, C, P)) + return false; + } + return true; +} + +/// Tests if a point is inside the given triangle +bool CTriangulator::_insideTriangle(const TVec& A, const TVec& B, const TVec& C,const TVec& P) +{ + NxF64 ax, ay, bx, by, cx, cy, apx, apy, bpx, bpy, cpx, cpy; + NxF64 cCROSSap, bCROSScp, aCROSSbp; + + ax = C.x - B.x; ay = C.y - B.y; + bx = A.x - C.x; by = A.y - C.y; + cx = B.x - A.x; cy = B.y - A.y; + apx = P.x - A.x; apy = P.y - A.y; + bpx = P.x - B.x; bpy = P.y - B.y; + cpx = P.x - C.x; cpy = P.y - C.y; + + aCROSSbp = ax * bpy - ay * bpx; + cCROSSap = cx * apy - cy * apx; + bCROSScp = bx * cpy - by * cpx; + + return ((aCROSSbp >= 0.0f) && (bCROSScp >= 0.0f) && (cCROSSap >= 0.0f)); +} + +class Triangulate : public fm_Triangulate, public Memalloc +{ +public: + Triangulate(void) + { + mPointsFloat = 0; + mPointsDouble = 0; + } + + virtual ~Triangulate(void) + { + reset(); + } + void reset(void) + { + MEMALLOC_FREE(mPointsFloat); + MEMALLOC_FREE(mPointsDouble); + mPointsFloat = 0; + mPointsDouble = 0; + } + + virtual const NxF64 * triangulate3d(NxU32 pcount, + const NxF64 *_points, + NxU32 vstride, + NxU32 &tcount, + bool consolidate, + NxF64 epsilon) + { + reset(); + + NxF64 *points = (NxF64 *)MEMALLOC_MALLOC(sizeof(NxF64)*pcount*3); + if ( consolidate ) + { + pcount = fm_consolidatePolygon(pcount,_points,vstride,points,1-epsilon); + } + else + { + NxF64 *dest = points; + for (NxU32 i=0; i= 3 ) + { + CTriangulator ct; + for (NxU32 i=0; i(t); +} + +void fm_releaseTriangulate(fm_Triangulate *t) +{ + Triangulate *tt = static_cast< Triangulate *>(t); + delete tt; +} + +#endif + +bool validDistance(const REAL *p1,const REAL *p2,REAL epsilon) +{ + bool ret = true; + + REAL dx = p1[0] - p2[0]; + REAL dy = p1[1] - p2[1]; + REAL dz = p1[2] - p2[2]; + REAL dist = dx*dx+dy*dy+dz*dz; + if ( dist < (epsilon*epsilon) ) + { + ret = false; + } + return ret; +} + +bool fm_isValidTriangle(const REAL *p1,const REAL *p2,const REAL *p3,REAL epsilon) +{ + bool ret = false; + + if ( validDistance(p1,p2,epsilon) && + validDistance(p1,p3,epsilon) && + validDistance(p2,p3,epsilon) ) + { + + REAL area = fm_computeArea(p1,p2,p3); + if ( area > epsilon ) + { + REAL _vertices[3*3],vertices[64*3]; + + _vertices[0] = p1[0]; + _vertices[1] = p1[1]; + _vertices[2] = p1[2]; + + _vertices[3] = p2[0]; + _vertices[4] = p2[1]; + _vertices[5] = p2[2]; + + _vertices[6] = p3[0]; + _vertices[7] = p3[1]; + _vertices[8] = p3[2]; + + NxU32 pcount = fm_consolidatePolygon(3,_vertices,sizeof(REAL)*3,vertices,1-epsilon); + if ( pcount == 3 ) + { + ret = true; + } + } + } + return ret; +} + + +void fm_multiplyQuat(const REAL *left,const REAL *right,REAL *quat) +{ + REAL a,b,c,d; + + a = left[3]*right[3] - left[0]*right[0] - left[1]*right[1] - left[2]*right[2]; + b = left[3]*right[0] + right[3]*left[0] + left[1]*right[2] - right[1]*left[2]; + c = left[3]*right[1] + right[3]*left[1] + left[2]*right[0] - right[2]*left[0]; + d = left[3]*right[2] + right[3]*left[2] + left[0]*right[1] - right[0]*left[1]; + + quat[3] = a; + quat[0] = b; + quat[1] = c; + quat[2] = d; +} + +}; // end of namespace diff --git a/Engine/lib/convexDecomp/NvHashMap.h b/Engine/lib/convexDecomp/NvHashMap.h new file mode 100644 index 000000000..3d57da0df --- /dev/null +++ b/Engine/lib/convexDecomp/NvHashMap.h @@ -0,0 +1,1908 @@ +/* + +NvHashMap.h : A simple hash map and array template class to avoid introducing dependencies on the STL for containers. + +*/ + + +// This code contains NVIDIA Confidential Information and is disclosed +// under the Mutual Non-Disclosure Agreement. +// +// Notice +// ALL NVIDIA DESIGN SPECIFICATIONS, CODE ARE PROVIDED "AS IS.". NVIDIA MAKES +// NO WARRANTIES, EXPRESSED, IMPLIED, STATUTORY, OR OTHERWISE WITH RESPECT TO +// THE MATERIALS, AND EXPRESSLY DISCLAIMS ALL IMPLIED WARRANTIES OF NONINFRINGEMENT, +// MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Information and code furnished is believed to be accurate and reliable. +// However, NVIDIA Corporation assumes no responsibility for the consequences of use of such +// information or for any infringement of patents or other rights of third parties that may +// result from its use. No license is granted by implication or otherwise under any patent +// or patent rights of NVIDIA Corporation. Details are subject to change without notice. +// This code supersedes and replaces all information previously supplied. +// NVIDIA Corporation products are not authorized for use as critical +// components in life support devices or systems without express written approval of +// NVIDIA Corporation. +// +// Copyright ďż˝ 2009 NVIDIA Corporation. All rights reserved. +// Copyright ďż˝ 2002-2008 AGEIA Technologies, Inc. All rights reserved. +// Copyright ďż˝ 2001-2006 NovodeX. All rights reserved. + +#ifndef NV_HASH_MAP_H +#define NV_HASH_MAP_H + +#include "NvUserMemAlloc.h" + +#if (defined(NX_WINDOWS) | defined(NX_X360)) +#include +#endif + +#include +#include +#include +#include +//****************************************************** +//****************************************************** +//****************************************************** + + +#ifndef NV_FOUNDATION_BASIC_TEMPLATES_H +#define NV_FOUNDATION_BASIC_TEMPLATES_H + +#pragma warning(push) +#pragma warning(disable:4512) // suppress the 'assignment operator could not be generated' warning message. + +namespace CONVEX_DECOMPOSITION +{ + template + struct Equal + { + bool operator()(const A& a, const A& b) const { return a==b; } + }; + + template + struct Less + { + bool operator()(const A& a, const A& b) const { return a + struct Greater + { + bool operator()(const A& a, const A& b) const { return a>b; } + }; + + + template + class Pair + { + public: + F first; + S second; + Pair(): first(F()), second(S()) {} + Pair(const F &f, const S &s): first(f), second(s) {} + Pair(const Pair &p): first(p.first), second(p.second) {} + }; + + template struct LogTwo { static const unsigned int value = LogTwo<(A>>1)>::value + 1; }; + template<> struct LogTwo<1>{ static const unsigned int value = 0; }; + + template struct UnConst { typedef T Type; }; + template struct UnConst { typedef T Type; }; +} + +#pragma warning(pop) + +#endif + +#ifndef NV_FOUNDATION_ALLOCATOR +#define NV_FOUNDATION_ALLOCATOR + +#pragma warning(push) +#pragma warning(disable:4100) + +namespace CONVEX_DECOMPOSITION +{ + + +/** +\brief The return value is the greater of the two specified values. +*/ +template +NX_INLINE N NxMax(N a, N b) { return a +NX_INLINE NxF32 NxMax(NxF32 a, NxF32 b) { return a > b ? a : b; } + +/** +\brief The return value is the lesser of the two specified values. +*/ +template +NX_INLINE N NxMin(N a, N b) { return a +NX_INLINE NxF32 NxMin(NxF32 a, NxF32 b) { return a < b ? a : b; } + + + + /** + Allocator used to access the global NxUserAllocator instance without providing additional information. + */ + class Allocator + { + public: + Allocator(const char* dummy = 0) + { + } + void* allocate(size_t size, const char* file, int line) + { + return MEMALLOC_MALLOC(size); + } + void deallocate(void* ptr) + { + MEMALLOC_FREE(ptr); + } + }; + + /** + Allocator used to access the global NxUserAllocator instance using a dynamic name. + */ + class NamedAllocator + { + public: + NamedAllocator(const char* name = 0) + + { + + } + void* allocate(size_t size, const char* filename, int line) + { + return MEMALLOC_MALLOC(size); + } + void deallocate(void* ptr) + { + MEMALLOC_FREE(ptr); + } + private: + }; + + /** + Allocator used to access the global NxUserAllocator instance using a static name derived from T. + */ + template + class ReflectionAllocator + { + static const char* getName() + { +#if defined NX_GNUC + return __PRETTY_FUNCTION__; +#else + return typeid(T).name(); +#endif + } + public: + ReflectionAllocator(const char* dummy=0) + { + } + void* allocate(size_t size, const char* filename, int line) + { + return MEMALLOC_MALLOC(size); + } + void deallocate(void* ptr) + { + MEMALLOC_FREE(ptr); + } + }; + + // if you get a build error here, you are trying to NX_NEW a class + // that is neither plain-old-type nor derived from CONVEX_DECOMPOSITION::UserAllocated + template + union EnableIfPod + { + int i; T t; + typedef X Type; + }; + +} + +// Global placement new for ReflectionAllocator templated by plain-old-type. Allows using NX_NEW for pointers and built-in-types. +// ATTENTION: You need to use NX_DELETE_POD or NX_FREE to deallocate memory, not NX_DELETE. NX_DELETE_POD redirects to NX_FREE. +// Rationale: NX_DELETE uses global operator delete(void*), which we dont' want to overload. +// Any other definition of NX_DELETE couldn't support array syntax 'NX_DELETE([]a);'. +// NX_DELETE_POD was preferred over NX_DELETE_ARRAY because it is used less often and applies to both single instances and arrays. +template +NX_INLINE void* operator new(size_t size, CONVEX_DECOMPOSITION::ReflectionAllocator alloc, const char* fileName, typename CONVEX_DECOMPOSITION::EnableIfPod::Type line) +{ + return alloc.allocate(size, fileName, line); +} + +template +NX_INLINE void* operator new[](size_t size, CONVEX_DECOMPOSITION::ReflectionAllocator alloc, const char* fileName, typename CONVEX_DECOMPOSITION::EnableIfPod::Type line) +{ + return alloc.allocate(size, fileName, line); +} + +// If construction after placement new throws, this placement delete is being called. +template +NX_INLINE void operator delete(void* ptr, CONVEX_DECOMPOSITION::ReflectionAllocator alloc, const char* fileName, typename CONVEX_DECOMPOSITION::EnableIfPod::Type line) +{ + alloc.deallocate(ptr); +} + +// If construction after placement new throws, this placement delete is being called. +template +NX_INLINE void operator delete[](void* ptr, CONVEX_DECOMPOSITION::ReflectionAllocator alloc, const char* fileName, typename CONVEX_DECOMPOSITION::EnableIfPod::Type line) +{ + alloc.deallocate(ptr); +} + +#pragma warning(pop) + +#endif + + +#ifndef NV_FOUNDATION_USERALLOCATED +#define NV_FOUNDATION_USERALLOCATED + +// an expression that should expand to nothing in _DEBUG builds. We currently +// use this only for tagging the purpose of containers for memory use tracking. +#if defined(_DEBUG) +#define NV_DEBUG_EXP(x) (x) +#define NV_DEBUG_EXP_C(x) x, +#else +#define NV_DEBUG_EXP(x) +#define NV_DEBUG_EXP_C(x) +#endif + +#if defined (NX_X360) | defined (NX_WINDOWS) | defined (NX_CELL) | defined (NXLINUX) | defined(NX_WII) +// Stack allocation with alloc fallback for large allocations (>50% of default stack size for platform) +# define NX_ALLOCA(var, type, number) \ + bool alloced_##var = false; \ + if (sizeof(type)*number*2 > (CONVEX_DECOMPOSITION::gSystemServices ? gSystemServices->getAllocaThreshold() : 8192) ) \ + { \ + var = (type *)MEMALLOC_MALLOC(sizeof(type)*number); \ + alloced_##var = true; \ + } else { \ + var = (type *)MEMALLOC_ALLOCA(sizeof(type)*number); \ + } +# define NX_FREEA(var) if (alloced_##var) MEMALLOC_FREE(var); +#else +# define NX_ALLOCA(var, type, number) var = (type *)NxAlloca(sizeof(type)*number); +# define NX_FREEA(var) 0; +#endif + +namespace CONVEX_DECOMPOSITION +{ + /** + Provides new and delete using a UserAllocator. + Guarantees that 'delete x;' uses the UserAllocator too. + */ + class UserAllocated + { + public: + + template + NX_INLINE void* operator new(size_t size, Alloc alloc, const char* fileName, int line) + { + return MEMALLOC_MALLOC(size); + } + template + NX_INLINE void* operator new[](size_t size, Alloc alloc, const char* fileName, int line) + { + return MEMALLOC_MALLOC(size); + } + + NX_INLINE void operator delete(void* ptr) + { + MEMALLOC_FREE(ptr); + } + NX_INLINE void operator delete[](void* ptr) + { + MEMALLOC_FREE(ptr); + } + }; +}; + +#endif + + +#ifndef NV_FOUNDATION_ALIGNEDMALLOC_H +#define NV_FOUNDATION_ALIGNEDMALLOC_H + +/*! +Allocate aligned memory. +Alignment must be a power of 2! +-- should be templated by a base allocator +*/ + +namespace CONVEX_DECOMPOSITION +{ + /** + Allocator, which is used to access the global NxUserAllocator instance + (used for dynamic data types template instantiation), which can align memory + */ + + // SCS: AlignedMalloc with 3 params not found, seems not used on PC either + // disabled for now to avoid GCC error + + template + class AlignedAllocator : public BaseAllocator + { + public: + AlignedAllocator(const BaseAllocator& base = BaseAllocator()) + : BaseAllocator(base) {} + + void* allocate(size_t size, const char* file, int line) + { + size_t pad = N - 1 + sizeof(size_t); // store offset for delete. + NxU8* base = (NxU8*)BaseAllocator::allocate(size+pad, file, line); + + NxU8* ptr = (NxU8*)(size_t(base + pad) & ~(N - 1)); // aligned pointer + ((size_t*)ptr)[-1] = ptr - base; // store offset + + return ptr; + } + void deallocate(void* ptr) + { + if(ptr == NULL) + return; + + NxU8* base = ((NxU8*)ptr) - ((size_t*)ptr)[-1]; + BaseAllocator::deallocate(base); + } + }; +} + +#endif + + +#ifndef NV_FOUNDATION_INLINE_ALLOCATOR_H +#define NV_FOUNDATION_INLINE_ALLOCATOR_H + +namespace CONVEX_DECOMPOSITION +{ + // this is used by the array class to allocate some space for a small number + // of objects along with the metadata + template + class InlineAllocator : private BaseAllocator + { + public: + + InlineAllocator(const BaseAllocator& alloc = BaseAllocator()) + : BaseAllocator(alloc) + {} + + void* allocate(size_t size, const char* filename, int line) + { + return size <= N ? mBuffer : BaseAllocator::allocate(size, filename, line); + } + + void deallocate(void* ptr) + { + if(ptr != mBuffer) + BaseAllocator::deallocate(ptr); + } + + private: + NxU8 mBuffer[N]; + }; +} + +#endif + + +#ifndef NV_FOUNDATION_NXSTRIDEDDATA +#define NV_FOUNDATION_NXSTRIDEDDATA +/** \addtogroup foundation + @{ +*/ + +template +class NvStrideIterator +{ + template + struct StripConst + { + typedef X Type; + }; + + template + struct StripConst + { + typedef X Type; + }; + +public: + explicit NX_INLINE NvStrideIterator(T* ptr = NULL, NxU32 stride = sizeof(T)) : + mPtr(ptr), mStride(stride) + { + NX_ASSERT(mStride == 0 || sizeof(T) <= mStride); + } + + NX_INLINE NvStrideIterator(const NvStrideIterator::Type>& strideIterator) : + mPtr(strideIterator.ptr()), mStride(strideIterator.stride()) + { + NX_ASSERT(mStride == 0 || sizeof(T) <= mStride); + } + + NX_INLINE T* ptr() const + { + return mPtr; + } + + NX_INLINE NxU32 stride() const + { + return mStride; + } + + NX_INLINE T& operator*() const + { + return *mPtr; + } + + NX_INLINE T* operator->() const + { + return mPtr; + } + + NX_INLINE T& operator[](int i) const + { + return *byteAdd(mPtr, i * stride()); + } + + // preincrement + NX_INLINE NvStrideIterator& operator++() + { + mPtr = byteAdd(mPtr, stride()); + return *this; + } + + // postincrement + NX_INLINE NvStrideIterator operator++(int) + { + NvStrideIterator tmp = *this; + mPtr = byteAdd(mPtr, stride()); + return tmp; + } + + // predecrement + NX_INLINE NvStrideIterator& operator--() + { + mPtr = byteSub(mPtr, stride()); + return *this; + } + + // postdecrement + NX_INLINE NvStrideIterator operator--(int) + { + NvStrideIterator tmp = *this; + mPtr = byteSub(mPtr, stride()); + return tmp; + } + + NX_INLINE NvStrideIterator& operator+=(int i) + { + mPtr = byteAdd(mPtr, i * stride()); + return *this; + } + + NX_INLINE NvStrideIterator operator+(int i) const + { + return NvStrideIterator(byteAdd(mPtr, i * stride()), stride()); + } + + NX_INLINE NvStrideIterator& operator-=(int i) + { + mPtr = byteSub(mPtr, i * stride()); + return *this; + } + + NX_INLINE NvStrideIterator operator-(int i) const + { + return NvStrideIterator(byteSub(mPtr, i * stride()), stride()); + } + + // iterator difference + NX_INLINE int operator-(const NvStrideIterator& other) const + { + NX_ASSERT(isCompatible(other)); + int byteDiff = static_cast(reinterpret_cast(mPtr) - reinterpret_cast(other.mPtr)); + return byteDiff / static_cast(stride()); + } + + NX_INLINE bool operator==(const NvStrideIterator& other) const + { + NX_ASSERT(isCompatible(other)); + return mPtr == other.mPtr; + } + + NX_INLINE bool operator!=(const NvStrideIterator& other) const + { + NX_ASSERT(isCompatible(other)); + return mPtr != other.mPtr; + } + + NX_INLINE bool operator<(const NvStrideIterator& other) const + { + NX_ASSERT(isCompatible(other)); + return mPtr < other.mPtr; + } + + NX_INLINE bool operator>(const NvStrideIterator& other) const + { + NX_ASSERT(isCompatible(other)); + return mPtr > other.mPtr; + } + + NX_INLINE bool operator<=(const NvStrideIterator& other) const + { + NX_ASSERT(isCompatible(other)); + return mPtr <= other.mPtr; + } + + NX_INLINE bool operator>=(const NvStrideIterator& other) const + { + NX_ASSERT(isCompatible(other)); + return mPtr >= other.mPtr; + } + +private: + NX_INLINE static T* byteAdd(T* ptr, NxU32 bytes) + { + return const_cast(reinterpret_cast(reinterpret_cast(ptr) + bytes)); + } + + NX_INLINE static T* byteSub(T* ptr, NxU32 bytes) + { + return const_cast(reinterpret_cast(reinterpret_cast(ptr) - bytes)); + } + + NX_INLINE bool isCompatible(const NvStrideIterator& other) const + { + int byteDiff = static_cast(reinterpret_cast(mPtr) - reinterpret_cast(other.mPtr)); + return (stride() == other.stride()) && (abs(byteDiff) % stride() == 0); + } + + T* mPtr; + NxU32 mStride; +}; + + +template +NX_INLINE NvStrideIterator operator+(int i, NvStrideIterator it) +{ + it += i; + return it; +} + + /** @} */ +#endif + +#ifndef NV_FOUNDATION_ARRAY +#define NV_FOUNDATION_ARRAY + +namespace CONVEX_DECOMPOSITION +{ + namespace Internal + { + template + struct ArrayMetaData + { + T* mData; + NxU32 mCapacity; + NxU32 mSize; + ArrayMetaData(): mSize(0), mCapacity(0), mData(0) {} + }; + + template + struct AllocatorTraits + { +#if defined _DEBUG + typedef NamedAllocator Type; +#else + typedef ReflectionAllocator Type; +#endif + }; + } + + /*! + An array is a sequential container. + + Implementation note + * entries between 0 and size are valid objects + * we use inheritance to build this because the array is included inline in a lot + of objects and we want the allocator to take no space if it's not stateful, which + aggregation doesn't allow. Also, we want the metadata at the front for the inline + case where the allocator contains some inline storage space + */ + template::Type > + class Array : private Internal::ArrayMetaData, private Alloc + { + typedef Internal::ArrayMetaData MetaData; + + using MetaData::mCapacity; + using MetaData::mData; + using MetaData::mSize; + + public: + + typedef T* Iterator; + typedef const T* ConstIterator; + + /*! + Default array constructor. Initialize an empty array + */ + NX_INLINE Array(const Alloc& alloc = Alloc()) : Alloc(alloc) {} + + /*! + Initialize array with given length + */ + NX_INLINE explicit Array(NxU32 capacity, const Alloc& alloc = Alloc()) + : Alloc(alloc) + { + if(mCapacity>0) + allocate(mCapacity); + } + + /*! + Copy-constructor. Copy all entries from other array + */ + template + NX_INLINE Array(const Array& other, const Alloc& alloc = Alloc()) + { + if(other.mSize > 0) + { + mData = allocate(mSize = mCapacity = other.mSize); + copy(mData, other.mData, mSize); + } + } + + /*! + Default destructor + */ + NX_INLINE ~Array() + { + destroy(0, mSize); + if(mCapacity) + deallocate(mData); + } + + /*! + Assignment operator. Copy content (deep-copy) + */ + template + NX_INLINE const Array& operator= (const Array& t) + { + if(&t == this) + return *this; + + if(mCapacity < t.mSize) + { + destroy(0,mSize); + deallocate(mData); + + mData = allocate(t.mCapacity); + mCapacity = t.mCapacity; + + copy(mData,t.mData,t.mSize); + mSize = t.mSize; + + return; + } + else + { + NxU32 m = NxMin(t.mSize,mSize); + copy(mData,t.mData,m); + for(NxU32 i = m; i < mSize;i++) + mData[i].~T(); + for(NxU32 i = m; i < t.mSize; i++) + new(mData+i)T(t.mData[i]); + } + + mSize = t.mSize; + return *this; + } + + /*! + Array indexing operator. + \param i + The index of the element that will be returned. + \return + The element i in the array. + */ + NX_INLINE const T& operator[] (NxU32 i) const + { + return mData[i]; + } + + /*! + Array indexing operator. + \param i + The index of the element that will be returned. + \return + The element i in the array. + */ + NX_INLINE T& operator[] (NxU32 i) + { + return mData[i]; + } + + /*! + Returns a pointer to the initial element of the array. + \return + a pointer to the initial element of the array. + */ + NX_INLINE ConstIterator begin() const + { + return mData; + } + + NX_INLINE Iterator begin() + { + return mData; + } + + /*! + Returns an iterator beyond the last element of the array. Do not dereference. + \return + a pointer to the element beyond the last element of the array. + */ + + NX_INLINE ConstIterator end() const + { + return mData+mSize; + } + + NX_INLINE Iterator end() + { + return mData+mSize; + } + + /*! + Returns a reference to the first element of the array. Undefined if the array is empty. + \return a reference to the first element of the array + */ + + NX_INLINE const T& front() const + { + NX_ASSERT(mSize); + return mData[0]; + } + + NX_INLINE T& front() + { + NX_ASSERT(mSize); + return mData[0]; + } + + /*! + Returns a reference to the last element of the array. Undefined if the array is empty + \return a reference to the last element of the array + */ + + NX_INLINE const T& back() const + { + NX_ASSERT(mSize); + return mData[mSize-1]; + } + + NX_INLINE T& back() + { + NX_ASSERT(mSize); + return mData[mSize-1]; + } + + + /*! + Returns the number of entries in the array. This can, and probably will, + differ from the array capacity. + \return + The number of of entries in the array. + */ + NX_INLINE NxU32 size() const + { + return mSize; + } + + /*! + Clears the array. + */ + NX_INLINE void clear() + { + destroy(0,mSize); + mSize = 0; + } + + /*! + Returns whether the array is empty (i.e. whether its size is 0). + \return + true if the array is empty + */ + NX_INLINE bool empty() const + { + return mSize==0; + } + + /*! + Finds the first occurrence of an element in the array. + \param a + The element that will be removed. + */ + + + NX_INLINE Iterator find(const T&a) + { + NxU32 index; + for(index=0;index(i-mData)); + } + + ///////////////////////////////////////////////////////////////////////// + /*! + Replaces the first occurrence of the element a with the last element + Operation is O(n) + \param i + The position of the element that will be subtracted from this array. + \return Returns true if the element has been removed. + */ + ///////////////////////////////////////////////////////////////////////// + + NX_INLINE bool findAndReplaceWithLast(const T& a) + { + NxU32 index; + for(index=0;index= mSize) + return false; + replaceWithLast(index); + return true; + } + + ///////////////////////////////////////////////////////////////////////// + /*! + Subtracts the element on position i from the array. Shift the entire + array one step. + Operation is O(n) + \param i + The position of the element that will be subtracted from this array. + \return + The element that was removed. + */ + ///////////////////////////////////////////////////////////////////////// + NX_INLINE void remove(NxU32 i) + { + NX_ASSERT(i mCapacity) + { + grow(size); + } + else if (compaction && (size != mCapacity)) + { + recreate(size, NxMin(mSize, size)); + } + + for(NxU32 i = mSize; i < size; i++) + ::new(mData+i)T(a); + + if (!compaction) // With compaction, these elements have been deleted already + { + for(NxU32 i = size; i < mSize; i++) + mData[i].~T(); + } + + mSize = size; + } + + + ////////////////////////////////////////////////////////////////////////// + /*! + Resize array such that only as much memory is allocated to hold the + existing elements + */ + ////////////////////////////////////////////////////////////////////////// + NX_INLINE void shrink() + { + resize(mSize, true); + } + + + ////////////////////////////////////////////////////////////////////////// + /*! + Deletes all array elements and frees memory. + */ + ////////////////////////////////////////////////////////////////////////// + NX_INLINE void reset() + { + resize(0, true); + } + + + ////////////////////////////////////////////////////////////////////////// + /*! + Ensure that the array has at least size capacity. + */ + ////////////////////////////////////////////////////////////////////////// + NX_INLINE void reserve(const NxU32 size) + { + if(size > mCapacity) + grow(size); + } + + ////////////////////////////////////////////////////////////////////////// + /*! + Query the capacity(allocated mem) for the array. + */ + ////////////////////////////////////////////////////////////////////////// + NX_INLINE NxU32 capacity() const + { + return mCapacity; + } + + + private: + + NX_INLINE T* allocate(size_t capacity) + { + return (T*)Alloc::allocate(sizeof(T) * capacity, __FILE__, __LINE__); + } + + NX_INLINE void deallocate(void *mem) + { + Alloc::deallocate(mem); + } + + NX_INLINE void copy(T* dst, const T* src, size_t count) + { + for(size_t i=0;i= copyCount); + NX_ASSERT(mSize >= copyCount); + T* newData = allocate(capacity); + NX_ASSERT( ((newData != NULL) && (capacity > 0)) || + ((newData == NULL) && (capacity == 0)) ); + + if(mCapacity) + { + copy(newData,mData,copyCount); + destroy(0,mSize); + deallocate(mData); + } + + mData = newData; + mCapacity = capacity; + } + + /*! + Resizes the available memory for the array. + + \param capacity + The number of entries that the set should be able to hold. + */ + NX_INLINE void grow(NxU32 capacity) + { + NX_ASSERT(mCapacity < capacity); + recreate(capacity, mSize); + } + }; + + // array that pre-allocates for N elements + template ::Type> + class InlineArray : public Array > + { + typedef InlineAllocator Allocator; + public: + NX_INLINE InlineArray(const Alloc& alloc = Alloc()) + : Array(alloc) + {} + }; +} + +template +NX_INLINE NvStrideIterator getStrideIterator(CONVEX_DECOMPOSITION::Array& array) +{ + return NvStrideIterator(array.begin(), sizeof(T)); +} + +template +NX_INLINE NvStrideIterator getConstStrideIterator(CONVEX_DECOMPOSITION::Array& array) +{ + return NvStrideIterator(array.begin(), sizeof(T)); +} + + +#endif + +#ifndef NV_FOUNDATION_BITUTILS_H +#define NV_FOUNDATION_BITUTILS_H + +namespace CONVEX_DECOMPOSITION +{ + NX_INLINE NxU32 bitCount32(NxU32 v) + { + // from http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel + NxU32 const w = v - ((v >> 1) & 0x55555555); + NxU32 const x = (w & 0x33333333) + ((w >> 2) & 0x33333333); + return ((x + (x >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; + } + + /*! + Return the index of the highest set bit. Or 0 if no bits are set. + */ + NX_INLINE NxU32 highestSetBit32(NxU32 v) + { + for(NxU32 j = 32; j-- > 0;) + { + if(v&(1<> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + return x+1; + } + + // Helper function to approximate log2 of an integer value (assumes that the input is actually power of two) + NX_INLINE NxU32 ilog2(NxU32 num) + { + for (NxU32 i=0; i<32; i++) + { + num >>= 1; + if (num == 0) return i; + } + + NX_ASSERT(0); + return (NxU32)-1; + } + + NX_INLINE int intChop(const NxF32& f) + { + NxI32 a = *reinterpret_cast(&f); // take bit pattern of float into a register + NxI32 sign = (a>>31); // sign = 0xFFFFFFFF if original value is negative, 0 if positive + NxI32 mantissa = (a&((1<<23)-1))|(1<<23); // extract mantissa and add the hidden bit + NxI32 exponent = ((a&0x7fffffff)>>23)-127; // extract the exponent + NxI32 r = ((NxU32)(mantissa)<<8)>>(31-exponent); // ((1<>24 -- (we know that mantissa > (1<<24)) + return ((r ^ (sign)) - sign ) &~ (exponent>>31); // add original sign. If exponent was negative, make return value 0. + } + + NX_INLINE int intFloor(const NxF32& f) + { + NxI32 a = *reinterpret_cast(&f); // take bit pattern of float into a register + NxI32 sign = (a>>31); // sign = 0xFFFFFFFF if original value is negative, 0 if positive + a&=0x7fffffff; // we don't need the sign any more + NxI32 exponent = (a>>23)-127; // extract the exponent + NxI32 expsign = ~(exponent>>31); // 0xFFFFFFFF if exponent is positive, 0 otherwise + NxI32 imask = ( (1<<(31-(exponent))))-1; // mask for true integer values + NxI32 mantissa = (a&((1<<23)-1)); // extract mantissa (without the hidden bit) + NxI32 r = ((NxU32)(mantissa|(1<<23))<<8)>>(31-exponent); // ((1<>24 -- (we know that mantissa > (1<<24)) + r = ((r & expsign) ^ (sign)) + ((!((mantissa<<8)&imask)&(expsign^((a-1)>>31)))&sign); // if (fabs(value)<1.0) value = 0; copy sign; if (value < 0 && value==(int)(value)) value++; + return r; + } + + NX_INLINE int intCeil(const NxF32& f) + { + NxI32 a = *reinterpret_cast(&f) ^ 0x80000000; // take bit pattern of float into a register + NxI32 sign = (a>>31); // sign = 0xFFFFFFFF if original value is negative, 0 if positive + a&=0x7fffffff; // we don't need the sign any more + NxI32 exponent = (a>>23)-127; // extract the exponent + NxI32 expsign = ~(exponent>>31); // 0xFFFFFFFF if exponent is positive, 0 otherwise + NxI32 imask = ( (1<<(31-(exponent))))-1; // mask for true integer values + NxI32 mantissa = (a&((1<<23)-1)); // extract mantissa (without the hidden bit) + NxI32 r = ((NxU32)(mantissa|(1<<23))<<8)>>(31-exponent); // ((1<>24 -- (we know that mantissa > (1<<24)) + r = ((r & expsign) ^ (sign)) + ((!((mantissa<<8)&imask)&(expsign^((a-1)>>31)))&sign); // if (fabs(value)<1.0) value = 0; copy sign; if (value < 0 && value==(int)(value)) value++; + return -r; + } + +} + +#endif + +#ifndef NV_FOUNDATION_HASHFUNCTION_H +#define NV_FOUNDATION_HASHFUNCTION_H + +/*! +Central definition of hash functions +*/ + +namespace CONVEX_DECOMPOSITION +{ + // Hash functions + template + NxU32 hash(const T& key) + { + return (NxU32)key; + } + + // Thomas Wang's 32 bit mix + // http://www.cris.com/~Ttwang/tech/inthash.htm + template<> + NX_INLINE NxU32 hash(const NxU32& key) + { + NxU32 k = key; + k += ~(k << 15); + k ^= (k >> 10); + k += (k << 3); + k ^= (k >> 6); + k += ~(k << 11); + k ^= (k >> 16); + return (NxU32)k; + } + + template<> + NX_INLINE NxU32 hash(const NxI32& key) + { + return hash((NxU32)key); + } + + // Thomas Wang's 64 bit mix + // http://www.cris.com/~Ttwang/tech/inthash.htm + template<> + NX_INLINE NxU32 hash(const NxU64& key) + { + NxU64 k = key; + k += ~(k << 32); + k ^= (k >> 22); + k += ~(k << 13); + k ^= (k >> 8); + k += (k << 3); + k ^= (k >> 15); + k += ~(k << 27); + k ^= (k >> 31); + return (NxU32)k; + } + + // Helper for pointer hashing + template + NxU32 PointerHash(const void* ptr); + + template<> + NX_INLINE NxU32 PointerHash<4>(const void* ptr) + { + return hash(static_cast(reinterpret_cast(ptr))); + } + + + template<> + NX_INLINE NxU32 PointerHash<8>(const void* ptr) + { + return hash(reinterpret_cast(ptr)); + } + + // Hash function for pointers + template + NX_INLINE NxU32 hash(T* key) + { + return PointerHash(key); + } + + // Hash function object for pointers + template + struct PointerHashFunctor + { + NxU32 operator()(const T* t) const + { + return PointerHash(t); + } + bool operator()(const T* t0, const T* t1) const + { + return t0 == t1; + } + }; + + /* + -------------------------------------------------------------------- + lookup2.c, by Bob Jenkins, December 1996, Public Domain. + -------------------------------------------------------------------- + -------------------------------------------------------------------- + mix -- mix 3 32-bit values reversibly. + For every delta with one or two bit set, and the deltas of all three + high bits or all three low bits, whether the original value of a,b,c + is almost all zero or is uniformly distributed, + * If mix() is run forward or backward, at least 32 bits in a,b,c + have at least 1/4 probability of changing. + * If mix() is run forward, every bit of c will change between 1/3 and + 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.) + mix() was built out of 36 single-cycle latency instructions in a + structure that could supported 2x parallelism, like so: + a -= b; + a -= c; x = (c>>13); + b -= c; a ^= x; + b -= a; x = (a<<8); + c -= a; b ^= x; + c -= b; x = (b>>13); + ... + Unfortunately, superscalar Pentiums and Sparcs can't take advantage + of that parallelism. They've also turned some of those single-cycle + latency instructions into multi-cycle latency instructions. Still, + this is the fastest good hash I could find. There were about 2^^68 + to choose from. I only looked at a billion or so. + -------------------------------------------------------------------- + */ + NX_INLINE NxU32 hashMix(NxU32 &a, NxU32 &b, NxU32 &c) + { + a -= b; a -= c; a ^= (c>>13); + b -= c; b -= a; b ^= (a<<8); + c -= a; c -= b; c ^= (b>>13); + a -= b; a -= c; a ^= (c>>12); + b -= c; b -= a; b ^= (a<<16); + c -= a; c -= b; c ^= (b>>5); + a -= b; a -= c; a ^= (c>>3); + b -= c; b -= a; b ^= (a<<10); + c -= a; c -= b; c ^= (b>>15); + } + + NX_INLINE NxU32 hash(const NxU32 *k, NxU32 length) + { + NxU32 a,b,c,len; + + /* Set up the internal state */ + len = length; + a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */ + c = 0; /* the previous hash value */ + + /*---------------------------------------- handle most of the key */ + while (len >= 3) + { + a += k[0]; + b += k[1]; + c += k[2]; + hashMix(a,b,c); + k += 3; + len -= 3; + } + + /*-------------------------------------- handle the last 2 ub4's */ + c += length; + switch(len) /* all the case statements fall through */ + { + /* c is reserved for the length */ + case 2 : b+=k[1]; + case 1 : a+=k[0]; + /* case 0: nothing left to add */ + } + hashMix(a,b,c); + /*-------------------------------------------- report the result */ + return c; + } + + template + class Hash + { + public: + NxU32 operator()(const Key &k) const { return hash(k); } + bool operator()(const Key &k0, const Key &k1) const { return k0 == k1; } + }; + + class NvStringHash + { + public: + NxU32 operator()(const char *string) const + { + // "DJB" string hash + NxU32 h = 5381; + for(const char *ptr = string; *ptr; ptr++) + h = ((h<<5)+h)^*ptr; + return h; + } + bool operator()(const char* string0, const char* string1) const + { + return !strcmp(string0, string1); + } + }; +} + +#endif + + +#ifndef NV_FOUNDATION_HASHINTERNALS +#define NV_FOUNDATION_HASHINTERNALS + + +#pragma warning(push) +#pragma warning(disable:4127 4512) // disable the 'conditoinal expression is constant' warning message + +namespace CONVEX_DECOMPOSITION +{ + namespace Internal + { + template + class HashBase + { + public: + typedef Entry EntryType; + + HashBase(NxU32 initialTableSize = 64, float loadFactor = 0.75f): + mLoadFactor(loadFactor), + mFreeList((NxU32)EOL), + mTimestamp(0), + mSize(0), + mEntries(Allocator(NV_DEBUG_EXP("hashBaseEntries"))), + mNext(Allocator(NV_DEBUG_EXP("hashBaseNext"))), + mHash(Allocator(NV_DEBUG_EXP("hashBaseHash"))) + { + if(initialTableSize) + reserveInternal(initialTableSize); + } + + ~HashBase() + { + for(NxU32 i = 0;imHash.size()) + reserveInternal(size); + } + + NX_INLINE const Entry *getEntries() const + { + return &mEntries[0]; + } + + private: + + // free list management - if we're coalescing, then we use mFreeList to hold + // the top of the free list and it should always be equal to size(). Otherwise, + // we build a free list in the next() pointers. + + NX_INLINE void freeListAdd(NxU32 index) + { + if(compacting) + { + mFreeList--; + NX_ASSERT(mFreeList == mSize); + } + else + { + mNext[index] = mFreeList; + mFreeList = index; + } + } + + NX_INLINE void freeListAdd(NxU32 start, NxU32 end) + { + if(!compacting) + { + for(NxU32 i = start; i mEntries; + Array mNext; + Array mHash; + float mLoadFactor; + NxU32 mFreeList; + NxU32 mTimestamp; + NxU32 mSize; + + friend class Iter; + + public: + class Iter + { + public: + NX_INLINE Iter(HashBase &b): mBase(b), mTimestamp(b.mTimestamp), mBucket(0), mEntry((NxU32)b.EOL) + { + if(mBase.mEntries.size()>0) + { + mEntry = mBase.mHash[0]; + skip(); + } + } + + NX_INLINE void check() { NX_ASSERT(mTimestamp == mBase.mTimestamp); } + NX_INLINE Entry operator*() { check(); return mBase.mEntries[mEntry]; } + NX_INLINE Entry *operator->() { check(); return &mBase.mEntries[mEntry]; } + NX_INLINE Iter operator++() { check(); advance(); return *this; } + NX_INLINE Iter operator++(int) { check(); Iter i = *this; advance(); return i; } + NX_INLINE bool done() { check(); return mEntry == mBase.EOL; } + + private: + NX_INLINE void advance() { mEntry = mBase.mNext[mEntry]; skip(); } + NX_INLINE void skip() + { + while(mEntry==mBase.EOL) + { + if(++mBucket == mBase.mHash.size()) + break; + mEntry = mBase.mHash[mBucket]; + } + } + + NxU32 mBucket; + NxU32 mEntry; + NxU32 mTimestamp; + HashBase &mBase; + }; + }; + + template + class HashSetBase + { + public: + struct GetKey { NX_INLINE const Key &operator()(const Key &e) { return e; } }; + + typedef HashBase BaseMap; + typedef typename BaseMap::Iter Iterator; + + HashSetBase(NxU32 initialTableSize = 64, + float loadFactor = 0.75f): mBase(initialTableSize,loadFactor) {} + + bool insert(const Key &k) + { + bool exists; + Key *e = mBase.create(k,exists); + if(!exists) + new(e)Key(k); + return !exists; + } + + NX_INLINE bool contains(const Key &k) const { return mBase.find(k)!=0; } + NX_INLINE bool erase(const Key &k) { return mBase.erase(k); } + NX_INLINE NxU32 size() const { return mBase.size(); } + NX_INLINE void reserve(NxU32 size) { mBase.reserve(size); } + NX_INLINE void clear() { mBase.clear(); } + protected: + BaseMap mBase; + + }; + + template + + class HashMapBase + { + public: + typedef Pair Entry; + struct GetKey { NX_INLINE const Key &operator()(const Entry &e) { return e.first; } }; + typedef HashBase, Key, HashFn, GetKey, Allocator, true> BaseMap; + typedef typename BaseMap::Iter Iterator; + + HashMapBase(NxU32 initialTableSize = 64, float loadFactor = 0.75f): mBase(initialTableSize,loadFactor) {} + + bool insert(const Key &k, const Value &v) + { + bool exists; + Entry *e = mBase.create(k,exists); + if(!exists) + new(e)Entry(k,v); + return !exists; + } + + Value &operator [](const Key &k) + { + bool exists; + Entry *e = mBase.create(k, exists); + if(!exists) + new(e)Entry(k,Value()); + + return e->second; + } + + NX_INLINE const Entry * find(const Key &k) const { return mBase.find(k); } + NX_INLINE bool erase(const Key &k) { return mBase.erase(k); } + NX_INLINE NxU32 size() const { return mBase.size(); } + NX_INLINE Iterator getIterator() { return Iterator(mBase); } + NX_INLINE void reserve(NxU32 size) { mBase.reserve(size); } + NX_INLINE void clear() { mBase.clear(); } + + protected: + BaseMap mBase; + }; + + } +} + +#pragma warning(pop) + +#endif + +#ifndef NV_FOUNDATION_HASHMAP +#define NV_FOUNDATION_HASHMAP + + +// TODO: make this doxy-format +// +// This header defines two hash maps. Hash maps +// * support custom initial table sizes (rounded up internally to power-of-2) +// * support custom static allocator objects +// * auto-resize, based on a load factor (i.e. a 64-entry .75 load factor hash will resize +// when the 49th element is inserted) +// * are based on open hashing +// * have O(1) contains, erase +// +// Maps have STL-like copying semantics, and properly initialize and destruct copies of objects +// +// There are two forms of map: coalesced and uncoalesced. Coalesced maps keep the entries in the +// initial segment of an array, so are fast to iterate over; however deletion is approximately +// twice as expensive. +// +// HashMap: +// bool insert(const Key &k, const Value &v) O(1) amortized (exponential resize policy) +// Value & operator[](const Key &k) O(1) for existing objects, else O(1) amortized +// const Entry * find(const Key &k); O(1) +// bool erase(const T &k); O(1) +// NxU32 size(); constant +// void reserve(NxU32 size); O(MAX(currentOccupancy,size)) +// void clear(); O(currentOccupancy) (with zero constant for objects without destructors) +// Iterator getIterator(); +// +// operator[] creates an entry if one does not exist, initializing with the default constructor. +// CoalescedHashMap does not support getInterator, but instead supports +// const Key *getEntries(); +// +// Use of iterators: +// +// for(HashMap::Iterator iter = test.getIterator(); !iter.done(); ++iter) +// myFunction(iter->first, iter->second); + +namespace CONVEX_DECOMPOSITION +{ + template , + class Allocator = Allocator > + class HashMap: public Internal::HashMapBase + { + public: + + typedef Internal::HashMapBase HashMapBase; + typedef typename HashMapBase::Iterator Iterator; + + HashMap(NxU32 initialTableSize = 64, float loadFactor = 0.75f): HashMapBase(initialTableSize,loadFactor) {} + Iterator getIterator() { return Iterator(HashMapBase::mBase); } + }; + + template , + class Allocator = Allocator > + class CoalescedHashMap: public Internal::HashMapBase + { + typedef Internal::HashMapBase HashMapBase; + + CoalescedHashMap(NxU32 initialTableSize = 64, float loadFactor = 0.75f): HashMapBase(initialTableSize,loadFactor){} + const Key *getEntries() const { return HashMapBase::mBase.getEntries(); } + }; + +} +#endif + +#endif diff --git a/Engine/lib/convexDecomp/NvMeshIslandGeneration.cpp b/Engine/lib/convexDecomp/NvMeshIslandGeneration.cpp new file mode 100644 index 000000000..35157200b --- /dev/null +++ b/Engine/lib/convexDecomp/NvMeshIslandGeneration.cpp @@ -0,0 +1,783 @@ +/* + +NvMeshIslandGeneration.cpp : This code snippet walks the toplogy of a triangle mesh and detects the set of unique connected 'mesh islands' + +*/ + +/*! +** +** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com +** +** Portions of this source has been released with the PhysXViewer application, as well as +** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. +** +** If you find this code useful or you are feeling particularily generous I would +** ask that you please go to http://www.amillionpixels.us and make a donation +** to Troy DeMolay. +** +** DeMolay is a youth group for young men between the ages of 12 and 21. +** It teaches strong moral principles, as well as leadership skills and +** public speaking. The donations page uses the 'pay for pixels' paradigm +** where, in this case, a pixel is only a single penny. Donations can be +** made for as small as $4 or as high as a $100 block. Each person who donates +** will get a link to their own site as well as acknowledgement on the +** donations blog located here http://www.amillionpixels.blogspot.com/ +** +** If you wish to contact me you can use the following methods: +** +** Skype ID: jratcliff63367 +** Yahoo: jratcliff63367 +** AOL: jratcliff1961 +** email: jratcliffscarab@gmail.com +** +** +** The MIT license: +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is furnished +** to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. + +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ +#include +#include +#include +#include + +#pragma warning(disable:4100 4288) +#include "NvMeshIslandGeneration.h" +#include "NvFloatMath.h" +#include "NvHashMap.h" + +namespace CONVEX_DECOMPOSITION +{ + +typedef CONVEX_DECOMPOSITION::Array< NxU32 > NxU32Vector; + +class Edge; +class Island; + +class AABB +{ +public: + NxF32 mMin[3]; + NxF32 mMax[3]; +}; + +class Triangle +{ +public: + Triangle(void) + { + mConsumed = false; + mIsland = 0; + mHandle = 0; + mId = 0; + } + + void minmax(const NxF32 *p,AABB &box) + { + if ( p[0] < box.mMin[0] ) box.mMin[0] = p[0]; + if ( p[1] < box.mMin[1] ) box.mMin[1] = p[1]; + if ( p[2] < box.mMin[2] ) box.mMin[2] = p[2]; + + if ( p[0] > box.mMax[0] ) box.mMax[0] = p[0]; + if ( p[1] > box.mMax[1] ) box.mMax[1] = p[1]; + if ( p[2] > box.mMax[2] ) box.mMax[2] = p[2]; + } + + void minmax(const NxF64 *p,AABB &box) + { + if ( (NxF32)p[0] < box.mMin[0] ) box.mMin[0] = (NxF32)p[0]; + if ( (NxF32)p[1] < box.mMin[1] ) box.mMin[1] = (NxF32)p[1]; + if ( (NxF32)p[2] < box.mMin[2] ) box.mMin[2] = (NxF32)p[2]; + if ( (NxF32)p[0] > box.mMax[0] ) box.mMax[0] = (NxF32)p[0]; + if ( (NxF32)p[1] > box.mMax[1] ) box.mMax[1] = (NxF32)p[1]; + if ( (NxF32)p[2] > box.mMax[2] ) box.mMax[2] = (NxF32)p[2]; + } + + void buildBox(const NxF32 *vertices_f,const NxF64 *vertices_d,NxU32 id); + + void render(NxU32 color) + { +// gRenderDebug->DebugBound(&mBox.mMin[0],&mBox.mMax[0],color,60.0f); + } + + void getTriangle(NxF32 *tri,const NxF32 *vertices_f,const NxF64 *vertices_d); + + NxU32 mHandle; + bool mConsumed; + Edge *mEdges[3]; + Island *mIsland; // identifies which island it is a member of + unsigned short mId; + AABB mBox; +}; + + +class Edge +{ +public: + Edge(void) + { + mI1 = 0; + mI2 = 0; + mHash = 0; + mNext = 0; + mPrevious = 0; + mParent = 0; + mNextTriangleEdge = 0; + } + + void init(NxU32 i1,NxU32 i2,Triangle *parent) + { + assert( i1 < 65536 ); + assert( i2 < 65536 ); + + mI1 = i1; + mI2 = i2; + mHash = (i2<<16)|i1; + mReverseHash = (i1<<16)|i2; + mNext = 0; + mPrevious = 0; + mParent = parent; + } + + NxU32 mI1; + NxU32 mI2; + NxU32 mHash; + NxU32 mReverseHash; + + Edge *mNext; + Edge *mPrevious; + Edge *mNextTriangleEdge; + Triangle *mParent; +}; + +typedef CONVEX_DECOMPOSITION::HashMap< NxU32, Edge * > EdgeHashMap; +typedef CONVEX_DECOMPOSITION::Array< Triangle * > TriangleVector; + +class EdgeCheck +{ +public: + EdgeCheck(Triangle *t,Edge *e) + { + mTriangle = t; + mEdge = e; + } + + Triangle *mTriangle; + Edge *mEdge; +}; + +typedef CONVEX_DECOMPOSITION::Array< EdgeCheck > EdgeCheckQueue; + +class Island +{ +public: + Island(Triangle *t,Triangle *root) + { + mVerticesFloat = 0; + mVerticesDouble = 0; + t->mIsland = this; + mTriangles.pushBack(t); + mCoplanar = false; + fm_initMinMax(mMin,mMax); + } + + void add(Triangle *t,Triangle *root) + { + t->mIsland = this; + mTriangles.pushBack(t); + } + + void merge(Island &isl) + { + TriangleVector::Iterator i; + for (i=isl.mTriangles.begin(); i!=isl.mTriangles.end(); ++i) + { + Triangle *t = (*i); + mTriangles.pushBack(t); + } + isl.mTriangles.clear(); + } + + bool isTouching(Island *isl,const NxF32 *vertices_f,const NxF64 *vertices_d) + { + bool ret = false; + + mVerticesFloat = vertices_f; + mVerticesDouble = vertices_d; + + if ( fm_intersectAABB(mMin,mMax,isl->mMin,isl->mMax) ) // if the two islands has an intersecting AABB + { + // todo.. + } + + + return ret; + } + + + void SAP_DeletePair(const void* object0, const void* object1, void* user_data, void* pair_user_data) + { + } + + void render(NxU32 color) + { +// gRenderDebug->DebugBound(mMin,mMax,color,60.0f); + TriangleVector::Iterator i; + for (i=mTriangles.begin(); i!=mTriangles.end(); ++i) + { + Triangle *t = (*i); + t->render(color); + } + } + + + const NxF64 *mVerticesDouble; + const NxF32 *mVerticesFloat; + + NxF32 mMin[3]; + NxF32 mMax[3]; + bool mCoplanar; // marked as co-planar.. + TriangleVector mTriangles; +}; + + +void Triangle::getTriangle(NxF32 *tri,const NxF32 *vertices_f,const NxF64 *vertices_d) +{ + NxU32 i1 = mEdges[0]->mI1; + NxU32 i2 = mEdges[1]->mI1; + NxU32 i3 = mEdges[2]->mI1; + if ( vertices_f ) + { + const NxF32 *p1 = &vertices_f[i1*3]; + const NxF32 *p2 = &vertices_f[i2*3]; + const NxF32 *p3 = &vertices_f[i3*3]; + fm_copy3(p1,tri); + fm_copy3(p2,tri+3); + fm_copy3(p3,tri+6); + } + else + { + const NxF64 *p1 = &vertices_d[i1*3]; + const NxF64 *p2 = &vertices_d[i2*3]; + const NxF64 *p3 = &vertices_d[i3*3]; + fm_doubleToFloat3(p1,tri); + fm_doubleToFloat3(p2,tri+3); + fm_doubleToFloat3(p3,tri+6); + } +} + +void Triangle::buildBox(const NxF32 *vertices_f,const NxF64 *vertices_d,NxU32 id) +{ + mId = (unsigned short)id; + NxU32 i1 = mEdges[0]->mI1; + NxU32 i2 = mEdges[1]->mI1; + NxU32 i3 = mEdges[2]->mI1; + + if ( vertices_f ) + { + const NxF32 *p1 = &vertices_f[i1*3]; + const NxF32 *p2 = &vertices_f[i2*3]; + const NxF32 *p3 = &vertices_f[i3*3]; + mBox.mMin[0] = p1[0]; + mBox.mMin[1] = p1[1]; + mBox.mMin[2] = p1[2]; + mBox.mMax[0] = p1[0]; + mBox.mMax[1] = p1[1]; + mBox.mMax[2] = p1[2]; + minmax(p2,mBox); + minmax(p3,mBox); + } + else + { + const NxF64 *p1 = &vertices_d[i1*3]; + const NxF64 *p2 = &vertices_d[i2*3]; + const NxF64 *p3 = &vertices_d[i3*3]; + mBox.mMin[0] = (NxF32)p1[0]; + mBox.mMin[1] = (NxF32)p1[1]; + mBox.mMin[2] = (NxF32)p1[2]; + mBox.mMax[0] = (NxF32)p1[0]; + mBox.mMax[1] = (NxF32)p1[1]; + mBox.mMax[2] = (NxF32)p1[2]; + minmax(p2,mBox); + minmax(p3,mBox); + } + + assert(mIsland); + if ( mIsland ) + { + if ( mBox.mMin[0] < mIsland->mMin[0] ) mIsland->mMin[0] = mBox.mMin[0]; + if ( mBox.mMin[1] < mIsland->mMin[1] ) mIsland->mMin[1] = mBox.mMin[1]; + if ( mBox.mMin[2] < mIsland->mMin[2] ) mIsland->mMin[2] = mBox.mMin[2]; + + if ( mBox.mMax[0] > mIsland->mMax[0] ) mIsland->mMax[0] = mBox.mMax[0]; + if ( mBox.mMax[1] > mIsland->mMax[1] ) mIsland->mMax[1] = mBox.mMax[1]; + if ( mBox.mMax[2] > mIsland->mMax[2] ) mIsland->mMax[2] = mBox.mMax[2]; + } + +} + + +typedef CONVEX_DECOMPOSITION::Array< Island * > IslandVector; + +class MyMeshIslandGeneration : public MeshIslandGeneration +{ +public: + MyMeshIslandGeneration(void) + { + mTriangles = 0; + mEdges = 0; + mVerticesDouble = 0; + mVerticesFloat = 0; + } + + ~MyMeshIslandGeneration(void) + { + reset(); + } + + void reset(void) + { + delete []mTriangles; + delete []mEdges; + mTriangles = 0; + mEdges = 0; + mTriangleEdges.clear(); + IslandVector::Iterator i; + for (i=mIslands.begin(); i!=mIslands.end(); ++i) + { + Island *_i = (*i); + delete _i; + } + mIslands.clear(); + } + + NxU32 islandGenerate(NxU32 tcount,const NxU32 *indices,const NxF64 *vertices) + { + mVerticesDouble = vertices; + mVerticesFloat = 0; + return islandGenerate(tcount,indices); + } + + NxU32 islandGenerate(NxU32 tcount,const NxU32 *indices,const NxF32 *vertices) + { + mVerticesDouble = 0; + mVerticesFloat = vertices; + return islandGenerate(tcount,indices); + } + + NxU32 islandGenerate(NxU32 tcount,const NxU32 *indices) + { + NxU32 ret = 0; + + reset(); + + mTcount = tcount; + mTriangles = new Triangle[tcount]; + mEdges = new Edge[tcount*3]; + Edge *e = mEdges; + + for (NxU32 i=0; isecond->mParent; + + Island *i = new Island(t,mTriangles); // the initial triangle... + removeTriangle(t); // remove this triangle from the triangle-edges hashmap + + mIslands.pushBack(i); + + // now keep adding to this island until we can no longer walk any shared edges.. + addEdgeCheck(t,t->mEdges[0]); + addEdgeCheck(t,t->mEdges[1]); + addEdgeCheck(t,t->mEdges[2]); + + while ( !mEdgeCheckQueue.empty() ) + { + + EdgeCheck e = mEdgeCheckQueue.popBack(); + + // Process all triangles which share this edge + Edge *edge = locateSharedEdge(e.mEdge); + + while ( edge ) + { + Triangle *t = edge->mParent; + assert(!t->mConsumed); + i->add(t,mTriangles); + removeTriangle(t); // remove this triangle from the triangle-edges hashmap + + // now keep adding to this island until we can no longer walk any shared edges.. + + if ( edge != t->mEdges[0] ) + { + addEdgeCheck(t,t->mEdges[0]); + } + + if ( edge != t->mEdges[1] ) + { + addEdgeCheck(t,t->mEdges[1]); + } + + if ( edge != t->mEdges[2] ) + { + addEdgeCheck(t,t->mEdges[2]); + } + + edge = locateSharedEdge(e.mEdge); // keep going until all shared edges have been processed! + } + + } + } + + ret = (NxU32)mIslands.size(); + + return ret; + } + + NxU32 * getIsland(NxU32 index,NxU32 &otcount) + { + NxU32 *ret = 0; + + mIndices.clear(); + if ( index < mIslands.size() ) + { + Island *i = mIslands[index]; + otcount = (NxU32)i->mTriangles.size(); + TriangleVector::Iterator j; + for (j=i->mTriangles.begin(); j!=i->mTriangles.end(); ++j) + { + Triangle *t = (*j); + mIndices.pushBack(t->mEdges[0]->mI1); + mIndices.pushBack(t->mEdges[1]->mI1); + mIndices.pushBack(t->mEdges[2]->mI1); + } + ret = &mIndices[0]; + } + + return ret; + } + +private: + + void removeTriangle(Triangle *t) + { + t->mConsumed = true; + + removeEdge(t->mEdges[0]); + removeEdge(t->mEdges[1]); + removeEdge(t->mEdges[2]); + + } + + + Edge * locateSharedEdge(Edge *e) + { + Edge *ret = 0; + + const EdgeHashMap::Entry *found = mTriangleEdges.find( e->mReverseHash ); + if ( found != NULL ) + { + ret = (*found).second; + assert( ret->mHash == e->mReverseHash ); + } + return ret; + } + + void removeEdge(Edge *e) + { + const EdgeHashMap::Entry *found = mTriangleEdges.find( e->mHash ); + + if ( found != NULL ) + { + Edge *prev = 0; + Edge *scan = (*found).second; + while ( scan && scan != e ) + { + prev = scan; + scan = scan->mNextTriangleEdge; + } + + if ( scan ) + { + if ( prev == 0 ) + { + if ( scan->mNextTriangleEdge ) + { + mTriangleEdges.erase(e->mHash); + mTriangleEdges[e->mHash] = scan->mNextTriangleEdge; + } + else + { + mTriangleEdges.erase(e->mHash); // no more polygons have an edge here + } + } + else + { + prev->mNextTriangleEdge = scan->mNextTriangleEdge; + } + } + else + { + assert(0); + } + } + else + { + assert(0); // impossible! + } + } + + + Edge * addEdge(Edge *e,Triangle *t,NxU32 i1,NxU32 i2) + { + + e->init(i1,i2,t); + + const EdgeHashMap::Entry *found = mTriangleEdges.find(e->mHash); + if ( found == NULL ) + { + mTriangleEdges[ e->mHash ] = e; + } + else + { + Edge *pn = (*found).second; + e->mNextTriangleEdge = pn; + mTriangleEdges.erase(e->mHash); + mTriangleEdges[e->mHash] = e; + } + + e++; + + return e; + } + + void addEdgeCheck(Triangle *t,Edge *e) + { + EdgeCheck ec(t,e); + mEdgeCheckQueue.pushBack(ec); + } + + NxU32 mergeCoplanarIslands(const NxF32 *vertices) + { + mVerticesFloat = vertices; + mVerticesDouble = 0; + return mergeCoplanarIslands(); + } + + NxU32 mergeCoplanarIslands(const NxF64 *vertices) + { + mVerticesDouble = vertices; + mVerticesFloat = 0; + return mergeCoplanarIslands(); + } + + // this island needs to be merged + void mergeTouching(Island *isl) + { + Island *touching = 0; + + IslandVector::Iterator i; + for (i=mIslands.begin(); i!=mIslands.end(); ++i) + { + Island *_i = (*i); + if ( !_i->mCoplanar ) // can't merge with coplanar islands! + { + if ( _i->isTouching(isl,mVerticesFloat,mVerticesDouble) ) + { + touching = _i; + } + } + } + } + + NxU32 mergeCoplanarIslands(void) + { + NxU32 ret = 0; + + if ( !mIslands.empty() ) + { + + + NxU32 cp_count = 0; + NxU32 npc_count = 0; + + NxU32 count = (NxU32)mIslands.size(); + + for (NxU32 i=0; imCoplanar = true; + cp_count++; + } + else + { + npc_count++; + } + } + else + { + assert(0); + } + } + + if ( cp_count ) + { + if ( npc_count == 0 ) // all islands are co-planar! + { + IslandVector temp = mIslands; + mIslands.clear(); + Island *isl = mIslands[0]; + mIslands.pushBack(isl); + for (NxU32 i=1; imerge(*_i); + delete _i; + } + } + else + { + + + Triangle *t = mTriangles; + for (NxU32 i=0; ibuildBox(mVerticesFloat,mVerticesDouble,i); + t++; + } + + IslandVector::Iterator i; + for (i=mIslands.begin(); i!=mIslands.end(); ++i) + { + Island *isl = (*i); + + NxU32 color = 0x00FF00; + + if ( isl->mCoplanar ) + { + color = 0xFFFF00; + } + + mergeTouching(isl); + + } + + IslandVector temp = mIslands; + mIslands.clear(); + for (i=temp.begin(); i!=temp.end(); i++) + { + Island *isl = (*i); + if ( isl->mCoplanar ) + { + delete isl; // kill it + } + else + { + mIslands.pushBack(isl); + } + } + ret = (NxU32)mIslands.size(); + } + } + else + { + ret = npc_count; + } + } + + + return ret; + } + + NxU32 mergeTouchingIslands(const NxF32 *vertices) + { + NxU32 ret = 0; + + return ret; + } + + NxU32 mergeTouchingIslands(const NxF64 *vertices) + { + NxU32 ret = 0; + + return ret; + } + + NxU32 mTcount; + Triangle *mTriangles; + Edge *mEdges; + EdgeHashMap mTriangleEdges; + IslandVector mIslands; + EdgeCheckQueue mEdgeCheckQueue; + const NxF64 *mVerticesDouble; + const NxF32 *mVerticesFloat; + NxU32Vector mIndices; +}; + + +MeshIslandGeneration * createMeshIslandGeneration(void) +{ + MyMeshIslandGeneration *mig = new MyMeshIslandGeneration; + return static_cast< MeshIslandGeneration *>(mig); +} + +void releaseMeshIslandGeneration(MeshIslandGeneration *cm) +{ + MyMeshIslandGeneration *mig = static_cast< MyMeshIslandGeneration *>(cm); + delete mig; +} + +}; // end of namespace + diff --git a/Engine/lib/convexDecomp/NvMeshIslandGeneration.h b/Engine/lib/convexDecomp/NvMeshIslandGeneration.h new file mode 100644 index 000000000..5c9347613 --- /dev/null +++ b/Engine/lib/convexDecomp/NvMeshIslandGeneration.h @@ -0,0 +1,91 @@ +#ifndef MESH_ISLAND_GENERATION_H + +#define MESH_ISLAND_GENERATION_H + +/* + +NvMeshIslandGeneration.h : This code snippet walks the toplogy of a triangle mesh and detects the set of unique connected 'mesh islands' + +*/ + + +#include "NvUserMemAlloc.h" + +/*! +** +** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com +** +** Portions of this source has been released with the PhysXViewer application, as well as +** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. +** +** If you find this code useful or you are feeling particularily generous I would +** ask that you please go to http://www.amillionpixels.us and make a donation +** to Troy DeMolay. +** +** DeMolay is a youth group for young men between the ages of 12 and 21. +** It teaches strong moral principles, as well as leadership skills and +** public speaking. The donations page uses the 'pay for pixels' paradigm +** where, in this case, a pixel is only a single penny. Donations can be +** made for as small as $4 or as high as a $100 block. Each person who donates +** will get a link to their own site as well as acknowledgement on the +** donations blog located here http://www.amillionpixels.blogspot.com/ +** +** If you wish to contact me you can use the following methods: +** +** Skype ID: jratcliff63367 +** Yahoo: jratcliff63367 +** AOL: jratcliff1961 +** email: jratcliffscarab@gmail.com +** +** +** The MIT license: +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is furnished +** to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. + +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +namespace CONVEX_DECOMPOSITION +{ + +class MeshIslandGeneration +{ +public: + + virtual NxU32 islandGenerate(NxU32 tcount,const NxU32 *indices,const NxF32 *vertices) = 0; + virtual NxU32 islandGenerate(NxU32 tcount,const NxU32 *indices,const NxF64 *vertices) = 0; + + // sometimes island generation can produce co-planar islands. Slivers if you will. If you are passing these islands into a geometric system + // that wants to turn them into physical objects, they may not be acceptable. In this case it may be preferable to merge the co-planar islands with + // other islands that it 'touches'. + virtual NxU32 mergeCoplanarIslands(const NxF32 *vertices) = 0; + virtual NxU32 mergeCoplanarIslands(const NxF64 *vertices) = 0; + + virtual NxU32 mergeTouchingIslands(const NxF32 *vertices) = 0; + virtual NxU32 mergeTouchingIslands(const NxF64 *vertices) = 0; + + virtual NxU32 * getIsland(NxU32 index,NxU32 &tcount) = 0; + + +}; + +MeshIslandGeneration * createMeshIslandGeneration(void); +void releaseMeshIslandGeneration(MeshIslandGeneration *cm); + +}; // end of namespace + +#endif diff --git a/Engine/lib/convexDecomp/NvRayCast.cpp b/Engine/lib/convexDecomp/NvRayCast.cpp new file mode 100644 index 000000000..6a9f02fe7 --- /dev/null +++ b/Engine/lib/convexDecomp/NvRayCast.cpp @@ -0,0 +1,153 @@ +/* + +NvRayCast.cpp : A code snippet to cast a ray against a triangle mesh. This implementation does not use any acceleration data structures. That is a 'to do' item. + +*/ +/*! +** +** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com +** +** Portions of this source has been released with the PhysXViewer application, as well as +** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. +** +** If you find this code useful or you are feeling particularily generous I would +** ask that you please go to http://www.amillionpixels.us and make a donation +** to Troy DeMolay. +** +** DeMolay is a youth group for young men between the ages of 12 and 21. +** It teaches strong moral principles, as well as leadership skills and +** public speaking. The donations page uses the 'pay for pixels' paradigm +** where, in this case, a pixel is only a single penny. Donations can be +** made for as small as $4 or as high as a $100 block. Each person who donates +** will get a link to their own site as well as acknowledgement on the +** donations blog located here http://www.amillionpixels.blogspot.com/ +** +** If you wish to contact me you can use the following methods: +** +** Skype ID: jratcliff63367 +** Yahoo: jratcliff63367 +** AOL: jratcliff1961 +** email: jratcliffscarab@gmail.com +** +** +** The MIT license: +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is furnished +** to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. + +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ +#include "NvRayCast.h" +#include "NvUserMemAlloc.h" +#include "NvFloatMath.h" + +#pragma warning(disable:4100) + +namespace CONVEX_DECOMPOSITION +{ + +class RayCast : public iRayCast, public Memalloc +{ +public: + RayCast(const NxF32 *vertices,NxU32 tcount,const NxU32 *indices) + { + mVertices = vertices; + mTcount = tcount; + mIndices = indices; + } + + ~RayCast(void) + { + } + + virtual bool castRay(const NxF32 *orig,const NxF32 *dir,NxF32 *dest,NxF32 *hitNormal) + { + bool ret = false; + + NxF32 p2[3]; + + const NxF32 RAY_DIST=50; + + dest[0] = p2[0] = orig[0]+ dir[0]*RAY_DIST; + dest[1] = p2[1] = orig[1]+ dir[1]*RAY_DIST; + dest[2] = p2[2] = orig[2]+ dir[2]*RAY_DIST; + + NxF32 nearest = 1e9; + NxU32 near_face=0; + + + for (NxU32 i=0; i(rc); +} + +void releaseRayCast(iRayCast *rc) +{ + RayCast *r = static_cast< RayCast *>(rc); + delete r; +} + +}; + diff --git a/Engine/lib/convexDecomp/NvRayCast.h b/Engine/lib/convexDecomp/NvRayCast.h new file mode 100644 index 000000000..4f4cf1fc5 --- /dev/null +++ b/Engine/lib/convexDecomp/NvRayCast.h @@ -0,0 +1,79 @@ +#ifndef NV_RAYCAST_H + +#define NV_RAYCAST_H + +/* + +NvRayCast.h : A code snippet to cast a ray against a triangle mesh. This implementation does not use any acceleration data structures. That is a 'to do' item. + +*/ + + +#include "NvSimpleTypes.h" + +/*! +** +** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com +** +** Portions of this source has been released with the PhysXViewer application, as well as +** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. +** +** If you find this code useful or you are feeling particularily generous I would +** ask that you please go to http://www.amillionpixels.us and make a donation +** to Troy DeMolay. +** +** DeMolay is a youth group for young men between the ages of 12 and 21. +** It teaches strong moral principles, as well as leadership skills and +** public speaking. The donations page uses the 'pay for pixels' paradigm +** where, in this case, a pixel is only a single penny. Donations can be +** made for as small as $4 or as high as a $100 block. Each person who donates +** will get a link to their own site as well as acknowledgement on the +** donations blog located here http://www.amillionpixels.blogspot.com/ +** +** If you wish to contact me you can use the following methods: +** +** Skype ID: jratcliff63367 +** Yahoo: jratcliff63367 +** AOL: jratcliff1961 +** email: jratcliffscarab@gmail.com +** +** +** The MIT license: +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is furnished +** to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. + +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +namespace CONVEX_DECOMPOSITION +{ + +class iRayCast +{ +public: + virtual bool castRay(const NxF32 *orig,const NxF32 *dir,NxF32 *hitPoint,NxF32 *hitNormal) = 0; +protected: + virtual ~iRayCast(void) { }; +}; + + +iRayCast *createRayCast(const NxF32 *vertices,NxU32 tcount,const NxU32 *indices); +void releaseRayCast(iRayCast *rc); + +}; + +#endif diff --git a/Engine/lib/convexDecomp/NvRemoveTjunctions.cpp b/Engine/lib/convexDecomp/NvRemoveTjunctions.cpp new file mode 100644 index 000000000..67ecd287c --- /dev/null +++ b/Engine/lib/convexDecomp/NvRemoveTjunctions.cpp @@ -0,0 +1,706 @@ +/* + +NvRemoveTjunctions.cpp : A code snippet to remove tjunctions from a triangle mesh. This version is currently disabled as it appears to have a bug. + +*/ + +/*! +** +** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com +** +** Portions of this source has been released with the PhysXViewer application, as well as +** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. +** +** If you find this code useful or you are feeling particularily generous I would +** ask that you please go to http://www.amillionpixels.us and make a donation +** to Troy DeMolay. +** +** DeMolay is a youth group for young men between the ages of 12 and 21. +** It teaches strong moral principles, as well as leadership skills and +** public speaking. The donations page uses the 'pay for pixels' paradigm +** where, in this case, a pixel is only a single penny. Donations can be +** made for as small as $4 or as high as a $100 block. Each person who donates +** will get a link to their own site as well as acknowledgement on the +** donations blog located here http://www.amillionpixels.blogspot.com/ +** +** If you wish to contact me you can use the following methods: +** +** Skype ID: jratcliff63367 +** Yahoo: jratcliff63367 +** AOL: jratcliff1961 +** email: jratcliffscarab@gmail.com +** +** +** The MIT license: +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is furnished +** to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. + +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ +#include +#include +#include +#include +#pragma warning(disable:4702) +#pragma warning(disable:4127) //conditional expression is constant (because _HAS_EXCEPTIONS=0) +#include +#ifdef __APPLE__ + #include +#else + #include +#endif +#include "NvUserMemAlloc.h" +#include "NvHashMap.h" +#include "NvRemoveTjunctions.h" +#include "NvFloatMath.h" + +#pragma warning(disable:4189) + +using namespace CONVEX_DECOMPOSITION; + +namespace CONVEX_DECOMPOSITION +{ + +class AABB +{ +public: + NxF32 mMin[3]; + NxF32 mMax[3]; +}; + +bool gDebug=false; +NxU32 gCount=0; + +typedef CONVEX_DECOMPOSITION::Array< NxU32 > NxU32Vector; + +class Triangle +{ +public: + Triangle(void) + { + mPending = false; + mSplit = false; + mI1 = mI2 = mI3 = 0xFFFFFFFF; + mId = 0; + } + + Triangle(NxU32 i1,NxU32 i2,NxU32 i3,const float *vertices,NxU32 id) + { + mPending = false; + init(i1,i2,i3,vertices,id); + mSplit = false; + } + + void init(NxU32 i1,NxU32 i2,NxU32 i3,const float *vertices,NxU32 id) + { + mSplit = false; + mI1 = i1; + mI2 = i2; + mI3 = i3; + mId = id; + + const float *p1 = &vertices[mI1*3]; + const float *p2 = &vertices[mI2*3]; + const float *p3 = &vertices[mI3*3]; + + initMinMax(p1,p2,p3); + } + + void initMinMax(const float *p1,const float *p2,const float *p3) + { + fm_copy3(p1,mBmin); + fm_copy3(p1,mBmax); + fm_minmax(p2,mBmin,mBmax); + fm_minmax(p3,mBmin,mBmax); + } + + void init(const NxU32 *idx,const float *vertices,NxU32 id) + { + mSplit = false; + mI1 = idx[0]; + mI2 = idx[1]; + mI3 = idx[2]; + mId = id; + + const float *p1 = &vertices[mI1*3]; + const float *p2 = &vertices[mI2*3]; + const float *p3 = &vertices[mI3*3]; + + initMinMax(p1,p2,p3); + + } + + bool intersects(const float *pos,const float *p1,const float *p2,float epsilon) const + { + bool ret = false; + + float sect[3]; + LineSegmentType type; + + float dist = fm_distancePointLineSegment(pos,p1,p2,sect,type,epsilon); + + if ( type == LS_MIDDLE && dist < epsilon ) + { + ret = true; + } + + return ret; + } + + bool intersects(NxU32 i,const float *vertices,NxU32 &edge,float epsilon) const + { + bool ret = true; + + const float *pos = &vertices[i*3]; + const float *p1 = &vertices[mI1*3]; + const float *p2 = &vertices[mI2*3]; + const float *p3 = &vertices[mI3*3]; + if ( intersects(pos,p1,p2,epsilon) ) + { + edge = 0; + } + else if ( intersects(pos,p2,p3,epsilon) ) + { + edge = 1; + } + else if ( intersects(pos,p3,p1,epsilon) ) + { + edge = 2; + } + else + { + ret = false; + } + return ret; + } + + bool intersects(const Triangle *t,const float *vertices,NxU32 &intersection_index,NxU32 &edge,float epsilon) + { + bool ret = false; + + if ( fm_intersectAABB(mBmin,mBmax,t->mBmin,t->mBmax) ) // only if the AABB's of the two triangles intersect... + { + + if ( t->intersects(mI1,vertices,edge,epsilon) ) + { + intersection_index = mI1; + ret = true; + } + + if ( t->intersects(mI2,vertices,edge,epsilon) ) + { + intersection_index = mI2; + ret = true; + } + + if ( t->intersects(mI3,vertices,edge,epsilon) ) + { + intersection_index = mI3; + ret = true; + } + + } + + return ret; + } + + bool mSplit:1; + bool mPending:1; + NxU32 mI1; + NxU32 mI2; + NxU32 mI3; + NxU32 mId; + float mBmin[3]; + float mBmax[3]; +}; + +class RtEdge +{ +public: + RtEdge(void) + { + mNextEdge = 0; + mTriangle = 0; + mHash = 0; + } + + NxU32 init(Triangle *t,NxU32 i1,NxU32 i2) + { + mTriangle = t; + mNextEdge = 0; + NX_ASSERT( i1 < 65536 ); + NX_ASSERT( i2 < 65536 ); + if ( i1 < i2 ) + { + mHash = (i1<<16)|i2; + } + else + { + mHash = (i2<<16)|i1; + } + return mHash; + } + RtEdge *mNextEdge; + Triangle *mTriangle; + NxU32 mHash; +}; + + +typedef CONVEX_DECOMPOSITION::Array< Triangle * > TriangleVector; +typedef CONVEX_DECOMPOSITION::HashMap< NxU32, RtEdge * > EdgeMap; + +class MyRemoveTjunctions : public RemoveTjunctions +{ +public: + MyRemoveTjunctions(void) + { + mInputTriangles = 0; + mEdges = 0; + mVcount = 0; + mVertices = 0; + mEdgeCount = 0; + } + ~MyRemoveTjunctions(void) + { + release(); + } + + virtual NxU32 removeTjunctions(RemoveTjunctionsDesc &desc) + { + NxU32 ret = 0; + + mEpsilon = desc.mEpsilon; + + size_t TcountOut; + + desc.mIndicesOut = removeTjunctions(desc.mVcount, desc.mVertices, desc.mTcount, desc.mIndices, TcountOut, desc.mIds); + +#ifdef WIN32 +# pragma warning(push) +# pragma warning(disable:4267) +#endif + + NX_ASSERT( TcountOut < UINT_MAX ); + desc.mTcountOut = TcountOut; + +#ifdef WIN32 +# pragma warning(pop) +#endif + + if ( !mIds.empty() ) + { + desc.mIdsOut = &mIds[0]; + } + + ret = desc.mTcountOut; + + bool check = ret != desc.mTcount; +#if 0 + while ( check ) + { + NxU32 tcount = ret; + NxU32 *indices = new NxU32[tcount*3]; + NxU32 *ids = new NxU32[tcount]; + memcpy(indices,desc.mIndicesOut,sizeof(NxU32)*ret*3); + memcpy(ids,desc.mIdsOut,sizeof(NxU32)*ret); + desc.mIndicesOut = removeTjunctions(desc.mVcount, desc.mVertices, tcount, indices, desc.mTcountOut, ids ); + if ( !mIds.empty() ) + { + desc.mIdsOut = &mIds[0]; + } + ret = desc.mTcountOut; + delete []indices; + delete []ids; + check = ret != tcount; + } +#endif + return ret; + } + + RtEdge * addEdge(Triangle *t,RtEdge *e,NxU32 i1,NxU32 i2) + { + NxU32 hash = e->init(t,i1,i2); + const EdgeMap::Entry *found = mEdgeMap.find(hash); + if ( found == NULL ) + { + mEdgeMap[hash] = e; + } + else + { + RtEdge *old_edge = (*found).second; + e->mNextEdge = old_edge; + mEdgeMap.erase(hash); + mEdgeMap[hash] = e; + } + e++; + mEdgeCount++; + return e; + } + + RtEdge * init(Triangle *t,const NxU32 *indices,const float *vertices,RtEdge *e,NxU32 id) + { + t->init(indices,vertices,id); + e = addEdge(t,e,t->mI1,t->mI2); + e = addEdge(t,e,t->mI2,t->mI3); + e = addEdge(t,e,t->mI3,t->mI1); + return e; + } + + void release(void) + { + mIds.clear(); + mEdgeMap.clear(); + mIndices.clear(); + mSplit.clear(); + delete []mInputTriangles; + delete []mEdges; + mInputTriangles = 0; + mEdges = 0; + mVcount = 0; + mVertices = 0; + mEdgeCount = 0; + + } + + virtual NxU32 * removeTjunctions(NxU32 vcount, + const float *vertices, + size_t tcount, + const NxU32 *indices, + size_t &tcount_out, + const NxU32 * ids) + { + NxU32 *ret = 0; + + release(); + + mVcount = vcount; + mVertices = vertices; + mTcount = (NxU32)tcount; + tcount_out = 0; + + mTcount = (NxU32)tcount; + mMaxTcount = (NxU32)tcount*2; + mInputTriangles = new Triangle[mMaxTcount]; + Triangle *t = mInputTriangles; + + mEdges = new RtEdge[mMaxTcount*3]; + mEdgeCount = 0; + + NxU32 id = 0; + + RtEdge *e = mEdges; + for (NxU32 i=0; imNextEdge == 0 ) // open edge! + { + Triangle *t = e->mTriangle; + if ( !t->mPending ) + { + test.pushBack(t); + t->mPending = true; + } + } + } + + if ( !test.empty() ) + { + TriangleVector::Iterator i; + for (i=test.begin(); i!=test.end(); ++i) + { + Triangle *t = (*i); + locateIntersection(t); + } + } + + } + + while ( !mSplit.empty() ) + { + TriangleVector scan = mSplit; + mSplit.clear(); + TriangleVector::Iterator i; + for (i=scan.begin(); i!=scan.end(); ++i) + { + Triangle *t = (*i); + locateIntersection(t); + } + } + + + mIndices.clear(); + mIds.clear(); + + t = mInputTriangles; + for (NxU32 i=0; imI1); + mIndices.pushBack(t->mI2); + mIndices.pushBack(t->mI3); + mIds.pushBack(t->mId); + t++; + } + + + mEdgeMap.clear(); + + delete []mEdges; + mEdges = 0; + delete []mInputTriangles; + mInputTriangles = 0; + tcount_out = mIndices.size()/3; + ret = tcount_out ? &mIndices[0] : 0; +#ifdef _DEBUG + if ( ret ) + { + const NxU32 *scan = ret; + for (NxU32 i=0; imI1 < mVcount ); + NX_ASSERT( scan->mI2 < mVcount ); + NX_ASSERT( scan->mI3 < mVcount ); + + NX_ASSERT( t->mI1 < mVcount ); + NX_ASSERT( t->mI2 < mVcount ); + NX_ASSERT( t->mI3 < mVcount ); + + + NxU32 intersection_index; + NxU32 edge; + + if ( scan != t && scan->intersects(t,mVertices,intersection_index,edge,mEpsilon) ) + { + + if ( t->mI1 == intersection_index || t->mI2 == intersection_index || t->mI3 == intersection_index ) + { + } + else + { + // here is where it intersects! + NxU32 i1,i2,i3; + NxU32 j1,j2,j3; + NxU32 id = t->mId; + + switch ( edge ) + { + case 0: + i1 = t->mI1; + i2 = intersection_index; + i3 = t->mI3; + j1 = intersection_index; + j2 = t->mI2; + j3 = t->mI3; + break; + case 1: + i1 = t->mI2; + i2 = intersection_index; + i3 = t->mI1; + j1 = intersection_index; + j2 = t->mI3; + j3 = t->mI1; + break; + case 2: + i1 = t->mI3; + i2 = intersection_index; + i3 = t->mI2; + j1 = intersection_index; + j2 = t->mI1; + j3 = t->mI2; + break; + default: + NX_ASSERT(0); + i1 = i2 = i3 = 0; + j1 = j2 = j3 = 0; + break; + } + + if ( mTcount < mMaxTcount ) + { + t->init(i1,i2,i3,mVertices,id); + Triangle *newt = &mInputTriangles[mTcount]; + newt->init(j1,j2,j3,mVertices,id); + mTcount++; + t->mSplit = true; + newt->mSplit = true; + + mSplit.pushBack(t); + mSplit.pushBack(newt); + ret = scan; + } + } + } + return ret; + } + + Triangle * testIntersection(Triangle *scan,Triangle *t) + { + Triangle *ret = 0; + + NxU32 t1 = (NxU32)(scan-mInputTriangles); + NxU32 t2 = (NxU32)(t-mInputTriangles); + + NX_ASSERT( t1 < mTcount ); + NX_ASSERT( t2 < mTcount ); + + NX_ASSERT( scan->mI1 < mVcount ); + NX_ASSERT( scan->mI2 < mVcount ); + NX_ASSERT( scan->mI3 < mVcount ); + + NX_ASSERT( t->mI1 < mVcount ); + NX_ASSERT( t->mI2 < mVcount ); + NX_ASSERT( t->mI3 < mVcount ); + + + NxU32 intersection_index; + NxU32 edge; + + assert( scan != t ); + + if ( scan->intersects(t,mVertices,intersection_index,edge,mEpsilon) ) + { + // here is where it intersects! + NxU32 i1,i2,i3; + NxU32 j1,j2,j3; + NxU32 id = t->mId; + + switch ( edge ) + { + case 0: + i1 = t->mI1; + i2 = intersection_index; + i3 = t->mI3; + j1 = intersection_index; + j2 = t->mI2; + j3 = t->mI3; + break; + case 1: + i1 = t->mI2; + i2 = intersection_index; + i3 = t->mI1; + j1 = intersection_index; + j2 = t->mI3; + j3 = t->mI1; + break; + case 2: + i1 = t->mI3; + i2 = intersection_index; + i3 = t->mI2; + j1 = intersection_index; + j2 = t->mI1; + j3 = t->mI2; + break; + default: + NX_ASSERT(0); + i1 = i2 = i3 = 0; + j1 = j2 = j3 = 0; + break; + } + + if ( mTcount < mMaxTcount ) + { + t->init(i1,i2,i3,mVertices,id); + Triangle *newt = &mInputTriangles[mTcount]; + newt->init(j1,j2,j3,mVertices,id); + mTcount++; + t->mSplit = true; + newt->mSplit = true; + + mSplit.pushBack(t); + mSplit.pushBack(newt); + ret = scan; + } + } + return ret; + } + + Triangle * locateIntersection(Triangle *t) + { + Triangle *ret = 0; + + Triangle *scan = mInputTriangles; + + for (NxU32 i=0; i(m); +} + +void releaseRemoveTjunctions(RemoveTjunctions *tj) +{ + MyRemoveTjunctions *m = static_cast< MyRemoveTjunctions *>(tj); + delete m; +} + + +}; // end of namespace + diff --git a/Engine/lib/convexDecomp/NvRemoveTjunctions.h b/Engine/lib/convexDecomp/NvRemoveTjunctions.h new file mode 100644 index 000000000..b6a674722 --- /dev/null +++ b/Engine/lib/convexDecomp/NvRemoveTjunctions.h @@ -0,0 +1,110 @@ +#ifndef REMOVE_TJUNCTIONS_H + +#define REMOVE_TJUNCTIONS_H + +/* + +NvRemoveTjunctions.h : A code snippet to remove tjunctions from a triangle mesh. This version is currently disabled as it appears to have a bug. + +*/ + + +#include "NvUserMemAlloc.h" + +/*! +** +** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com +** +** Portions of this source has been released with the PhysXViewer application, as well as +** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. +** +** If you find this code useful or you are feeling particularily generous I would +** ask that you please go to http://www.amillionpixels.us and make a donation +** to Troy DeMolay. +** +** DeMolay is a youth group for young men between the ages of 12 and 21. +** It teaches strong moral principles, as well as leadership skills and +** public speaking. The donations page uses the 'pay for pixels' paradigm +** where, in this case, a pixel is only a single penny. Donations can be +** made for as small as $4 or as high as a $100 block. Each person who donates +** will get a link to their own site as well as acknowledgement on the +** donations blog located here http://www.amillionpixels.blogspot.com/ +** +** If you wish to contact me you can use the following methods: +** +** Skype ID: jratcliff63367 +** Yahoo: jratcliff63367 +** AOL: jratcliff1961 +** email: jratcliffscarab@gmail.com +** +** +** The MIT license: +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is furnished +** to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. + +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +namespace CONVEX_DECOMPOSITION +{ + +class RemoveTjunctionsDesc +{ +public: + RemoveTjunctionsDesc(void) + { + mVcount = 0; + mVertices = 0; + mTcount = 0; + mIndices = 0; + mIds = 0; + mTcountOut = 0; + mIndicesOut = 0; + mIdsOut = 0; + mEpsilon = 0.00000001f; + } + +// input + NxF32 mEpsilon; + NxF32 mDistanceEpsilon; + NxU32 mVcount; // input vertice count. + const NxF32 *mVertices; // input vertices as NxF32s or... + NxU32 mTcount; // number of input triangles. + const NxU32 *mIndices; // triangle indices. + const NxU32 *mIds; // optional triangle Id numbers. +// output.. + NxU32 mTcountOut; // number of output triangles. + const NxU32 *mIndicesOut; // output triangle indices + const NxU32 *mIdsOut; // output retained id numbers. +}; + +// Removes t-junctions from an input mesh. Does not generate any new data points, but may possible produce additional triangles and new indices. +class RemoveTjunctions +{ +public: + + virtual NxU32 removeTjunctions(RemoveTjunctionsDesc &desc) =0; // returns number of triangles output and the descriptor is filled with the appropriate results. + + +}; + +RemoveTjunctions * createRemoveTjunctions(void); +void releaseRemoveTjunctions(RemoveTjunctions *tj); + +}; // end of namespace + +#endif diff --git a/Engine/lib/convexDecomp/NvSimpleTypes.h b/Engine/lib/convexDecomp/NvSimpleTypes.h new file mode 100644 index 000000000..cf838b149 --- /dev/null +++ b/Engine/lib/convexDecomp/NvSimpleTypes.h @@ -0,0 +1,170 @@ +#ifndef NV_SIMPLE_TYPES_H + +#define NV_SIMPLE_TYPES_H + +/* + +NvSimpleTypes.h : Defines basic data types for integers and floats. + +*/ + + +/*! +** +** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com +** +** Portions of this source has been released with the PhysXViewer application, as well as +** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. +** +** If you find this code useful or you are feeling particularily generous I would +** ask that you please go to http://www.amillionpixels.us and make a donation +** to Troy DeMolay. +** +** DeMolay is a youth group for young men between the ages of 12 and 21. +** It teaches strong moral principles, as well as leadership skills and +** public speaking. The donations page uses the 'pay for pixels' paradigm +** where, in this case, a pixel is only a single penny. Donations can be +** made for as small as $4 or as high as a $100 block. Each person who donates +** will get a link to their own site as well as acknowledgement on the +** donations blog located here http://www.amillionpixels.blogspot.com/ +** +** If you wish to contact me you can use the following methods: +** +** Skype ID: jratcliff63367 +** Yahoo: jratcliff63367 +** AOL: jratcliff1961 +** email: jratcliffscarab@gmail.com +** +** +** The MIT license: +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is furnished +** to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. + +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifdef __APPLE__ + #include +#else + #include +#endif +#include + +#if defined(__APPLE__) || defined(__CELLOS_LV2__) || defined(LINUX) + +#ifndef stricmp +#define stricmp(a, b) strcasecmp((a), (b)) +#define _stricmp(a, b) strcasecmp((a), (b)) +#endif + +#endif + +#ifdef WIN32 + typedef __int64 NxI64; + typedef signed int NxI32; + typedef signed short NxI16; + typedef signed char NxI8; + + typedef unsigned __int64 NxU64; + typedef unsigned int NxU32; + typedef unsigned short NxU16; + typedef unsigned char NxU8; + + typedef float NxF32; + typedef double NxF64; + +#elif LINUX + typedef long long NxI64; + typedef signed int NxI32; + typedef signed short NxI16; + typedef signed char NxI8; + + typedef unsigned long long NxU64; + typedef unsigned int NxU32; + typedef unsigned short NxU16; + typedef unsigned char NxU8; + + typedef float NxF32; + typedef double NxF64; + +#elif __APPLE__ + typedef long long NxI64; + typedef signed int NxI32; + typedef signed short NxI16; + typedef signed char NxI8; + + typedef unsigned long long NxU64; + typedef unsigned int NxU32; + typedef unsigned short NxU16; + typedef unsigned char NxU8; + + typedef float NxF32; + typedef double NxF64; + +#elif __CELLOS_LV2__ + typedef long long NxI64; + typedef signed int NxI32; + typedef signed short NxI16; + typedef signed char NxI8; + + typedef unsigned long long NxU64; + typedef unsigned int NxU32; + typedef unsigned short NxU16; + typedef unsigned char NxU8; + + typedef float NxF32; + typedef double NxF64; + +#elif _XBOX + typedef __int64 NxI64; + typedef signed int NxI32; + typedef signed short NxI16; + typedef signed char NxI8; + + typedef unsigned __int64 NxU64; + typedef unsigned int NxU32; + typedef unsigned short NxU16; + typedef unsigned char NxU8; + + typedef float NxF32; + typedef double NxF64; + +#elif defined(__PPCGEKKO__) + typedef long long NxI64; + typedef signed int NxI32; + typedef signed short NxI16; + typedef signed char NxI8; + + typedef unsigned long long NxU64; + typedef unsigned int NxU32; + typedef unsigned short NxU16; + typedef unsigned char NxU8; + + typedef float NxF32; + typedef double NxF64; + +#else + #error Unknown platform! +#endif + +#ifndef NX_INLINE +#define NX_INLINE inline +#define NX_ASSERT assert +#endif + + +#endif diff --git a/Engine/lib/convexDecomp/NvSplitMesh.cpp b/Engine/lib/convexDecomp/NvSplitMesh.cpp new file mode 100644 index 000000000..49905f91e --- /dev/null +++ b/Engine/lib/convexDecomp/NvSplitMesh.cpp @@ -0,0 +1,224 @@ +/* + +NvSplitMesh.cpp : A code snippet to split a mesh into two seperate meshes based on a slicing plane. + +*/ + +/*! +** +** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com +** +** Portions of this source has been released with the PhysXViewer application, as well as +** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. +** +** If you find this code useful or you are feeling particularily generous I would +** ask that you please go to http://www.amillionpixels.us and make a donation +** to Troy DeMolay. +** +** DeMolay is a youth group for young men between the ages of 12 and 21. +** It teaches strong moral principles, as well as leadership skills and +** public speaking. The donations page uses the 'pay for pixels' paradigm +** where, in this case, a pixel is only a single penny. Donations can be +** made for as small as $4 or as high as a $100 block. Each person who donates +** will get a link to their own site as well as acknowledgement on the +** donations blog located here http://www.amillionpixels.blogspot.com/ +** +** If you wish to contact me you can use the following methods: +** +** Skype ID: jratcliff63367 +** Yahoo: jratcliff63367 +** AOL: jratcliff1961 +** email: jratcliffscarab@gmail.com +** +** +** The MIT license: +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is furnished +** to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. + +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ +#define SHOW_DEBUG 0 +#if SHOW_DEBUG +#include "RenderDebug.h" +#endif + +#include "NvSplitMesh.h" +#include "NvFloatMath.h" +#include "NvHashMap.h" + +#pragma warning(disable:4100) + +namespace CONVEX_DECOMPOSITION +{ + + +typedef Array< NxU32 > NxU32Array; + +class SplitMesh : public iSplitMesh, public Memalloc +{ +public: + SplitMesh(void) + { + mLeftVertices = 0; + mRightVertices = 0; + } + + ~SplitMesh(void) + { + reset(); + } + + void reset(void) + { + if ( mLeftVertices ) + { + fm_releaseVertexIndex(mLeftVertices); + mLeftVertices = 0; + } + if ( mRightVertices ) + { + fm_releaseVertexIndex(mRightVertices); + mRightVertices = 0; + } + mLeftIndices.clear(); + mRightIndices.clear(); + } + + + virtual void splitMesh(const NvSplitMesh &source,NvSplitMesh &leftMesh,NvSplitMesh &rightMesh,const NxF32 *planeEquation,NxF32 precision) + { + reset(); + + mLeftVertices = fm_createVertexIndex(precision,false); + mRightVertices = fm_createVertexIndex(precision,false); + + for (NxU32 i=0; igetIndex( &front_tri[0],newPos ); + i2 = mLeftVertices->getIndex( &front_tri[3],newPos ); + i3 = mLeftVertices->getIndex( &front_tri[6],newPos ); + mLeftIndices.pushBack(i1); + mLeftIndices.pushBack(i2); + mLeftIndices.pushBack(i3); + #if SHOW_DEBUG + NVSHARE::gRenderDebug->setCurrentColor(0xFFFFFF); + NVSHARE::gRenderDebug->DebugTri(&front_tri[0],&front_tri[3],&front_tri[6]); + #endif + if ( fcount == 4 ) + { + i4 = mLeftVertices->getIndex( &front_tri[9],newPos ); + mLeftIndices.pushBack(i1); + mLeftIndices.pushBack(i3); + mLeftIndices.pushBack(i4); + #if SHOW_DEBUG + NVSHARE::gRenderDebug->setCurrentColor(0xFFFF00); + NVSHARE::gRenderDebug->DebugTri(&front_tri[0],&front_tri[6],&front_tri[9]); + #endif + } + } + if ( bcount ) + { + NxU32 i1,i2,i3,i4; + i1 = mRightVertices->getIndex( &back_tri[0],newPos ); + i2 = mRightVertices->getIndex( &back_tri[3],newPos ); + i3 = mRightVertices->getIndex( &back_tri[6],newPos ); + mRightIndices.pushBack(i1); + mRightIndices.pushBack(i2); + mRightIndices.pushBack(i3); + #if SHOW_DEBUG + NVSHARE::gRenderDebug->setCurrentColor(0xFF8080); + NVSHARE::gRenderDebug->DebugTri(&back_tri[0],&back_tri[3],&back_tri[6]); + #endif + if ( bcount == 4 ) + { + i4 = mRightVertices->getIndex( &back_tri[9],newPos ); + mRightIndices.pushBack(i1); + mRightIndices.pushBack(i3); + mRightIndices.pushBack(i4); + #if SHOW_DEBUG + NVSHARE::gRenderDebug->setCurrentColor(0x00FF00); + NVSHARE::gRenderDebug->DebugTri(&back_tri[0],&back_tri[6],&back_tri[9]); + #endif + } + } + } + + leftMesh.mVcount = mLeftVertices->getVcount(); + leftMesh.mVertices = mLeftVertices->getVerticesFloat(); + leftMesh.mTcount = mLeftIndices.size()/3; + leftMesh.mIndices = &mLeftIndices[0]; + + rightMesh.mVcount = mRightVertices->getVcount(); + rightMesh.mVertices = mRightVertices->getVerticesFloat(); + rightMesh.mTcount = mRightIndices.size()/3; + rightMesh.mIndices = &mRightIndices[0]; + + } + + + fm_VertexIndex *mLeftVertices; + fm_VertexIndex *mRightVertices; + NxU32Array mLeftIndices; + NxU32Array mRightIndices; +}; + +iSplitMesh *createSplitMesh(void) +{ + SplitMesh *sm = MEMALLOC_NEW(SplitMesh); + return static_cast< iSplitMesh *>(sm); +} + +void releaseSplitMesh(iSplitMesh *splitMesh) +{ + SplitMesh *sm = static_cast< SplitMesh *>(splitMesh); + delete sm; +} + +}; // end of namespace diff --git a/Engine/lib/convexDecomp/NvSplitMesh.h b/Engine/lib/convexDecomp/NvSplitMesh.h new file mode 100644 index 000000000..d9d623b54 --- /dev/null +++ b/Engine/lib/convexDecomp/NvSplitMesh.h @@ -0,0 +1,88 @@ +#ifndef NV_SPLIT_MESH_H + +#define NV_SPLIT_MESH_H + +/* + +NvSplitMesh.h : A code snippet to split a mesh into two seperate meshes based on a slicing plane. + +*/ + + +#include "NvUserMemAlloc.h" + +/*! +** +** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com +** +** Portions of this source has been released with the PhysXViewer application, as well as +** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. +** +** If you find this code useful or you are feeling particularily generous I would +** ask that you please go to http://www.amillionpixels.us and make a donation +** to Troy DeMolay. +** +** DeMolay is a youth group for young men between the ages of 12 and 21. +** It teaches strong moral principles, as well as leadership skills and +** public speaking. The donations page uses the 'pay for pixels' paradigm +** where, in this case, a pixel is only a single penny. Donations can be +** made for as small as $4 or as high as a $100 block. Each person who donates +** will get a link to their own site as well as acknowledgement on the +** donations blog located here http://www.amillionpixels.blogspot.com/ +** +** If you wish to contact me you can use the following methods: +** +** Skype ID: jratcliff63367 +** Yahoo: jratcliff63367 +** AOL: jratcliff1961 +** email: jratcliffscarab@gmail.com +** +** +** The MIT license: +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is furnished +** to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. + +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +namespace CONVEX_DECOMPOSITION +{ + +struct NvSplitMesh +{ + NxU32 mVcount; + const NxF32 *mVertices; + NxU32 mTcount; + const NxU32 *mIndices; +}; + + +class iSplitMesh +{ +public: + virtual void splitMesh(const NvSplitMesh &source,NvSplitMesh &leftMesh,NvSplitMesh &rightMesh,const NxF32 *planeEquation,NxF32 precision) = 0; +protected: + virtual ~iSplitMesh(void) { }; +}; + +iSplitMesh *createSplitMesh(void); +void releaseSplitMesh(iSplitMesh *splitMesh); + + +}; // end of namespace + +#endif diff --git a/Engine/lib/convexDecomp/NvStanHull.cpp b/Engine/lib/convexDecomp/NvStanHull.cpp new file mode 100644 index 000000000..4c14fa1f8 --- /dev/null +++ b/Engine/lib/convexDecomp/NvStanHull.cpp @@ -0,0 +1,3464 @@ + +/* + +NvStanHull.cpp : A convex hull generator written by Stan Melax + +*/ +/*! +** +** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com +** +** Portions of this source has been released with the PhysXViewer application, as well as +** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. +** +** If you find this code useful or you are feeling particularily generous I would +** ask that you please go to http://www.amillionpixels.us and make a donation +** to Troy DeMolay. +** +** DeMolay is a youth group for young men between the ages of 12 and 21. +** It teaches strong moral principles, as well as leadership skills and +** public speaking. The donations page uses the 'pay for pixels' paradigm +** where, in this case, a pixel is only a single penny. Donations can be +** made for as small as $4 or as high as a $100 block. Each person who donates +** will get a link to their own site as well as acknowledgement on the +** donations blog located here http://www.amillionpixels.blogspot.com/ +** +** If you wish to contact me you can use the following methods: +** +** Skype ID: jratcliff63367 +** Yahoo: jratcliff63367 +** AOL: jratcliff1961 +** email: jratcliffscarab@gmail.com +** +** +** The MIT license: +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is furnished +** to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. + +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ +#include +#include +#include +#include +#include +#include + + +#include +#include + +#include "NvStanHull.h" + +namespace CONVEX_DECOMPOSITION +{ + +//***************************************************** +//*** DARRAY.H +//***************************************************** + +template class ArrayRet; +template class Array +{ + public: + Array(NxI32 s=0); + Array(Array &array); + Array(ArrayRet &array); + ~Array(); + void allocate(NxI32 s); + void SetSize(NxI32 s); + void Pack(); + Type& Add(Type); + void AddUnique(Type); + NxI32 Contains(Type); + void Insert(Type,NxI32); + NxI32 IndexOf(Type); + void Remove(Type); + void DelIndex(NxI32 i); + Type * element; + NxI32 count; + NxI32 array_size; + const Type &operator[](NxI32 i) const { assert(i>=0 && i=0 && i &operator=(Array &array); + Array &operator=(ArrayRet &array); + // operator ArrayRet &() { return *(ArrayRet *)this;} // this worked but i suspect could be dangerous +}; + +template class ArrayRet:public Array +{ +}; + +template Array::Array(NxI32 s) +{ + count=0; + array_size = 0; + element = NULL; + if(s) + { + allocate(s); + } +} + + +template Array::Array(Array &array) +{ + count=0; + array_size = 0; + element = NULL; + for(NxI32 i=0;i Array::Array(ArrayRet &array) +{ + *this = array; +} +template Array &Array::operator=(ArrayRet &array) +{ + count=array.count; + array_size = array.array_size; + element = array.element; + array.element=NULL; + array.count=0; + array.array_size=0; + return *this; +} + + +template Array &Array::operator=(Array &array) +{ + count=0; + for(NxI32 i=0;i Array::~Array() +{ + if (element != NULL) + { + MEMALLOC_FREE(element); + } + count=0;array_size=0;element=NULL; +} + +template void Array::allocate(NxI32 s) +{ + assert(s>0); + assert(s>=count); + Type *old = element; + array_size =s; + element = (Type *) MEMALLOC_MALLOC( sizeof(Type)*array_size ); + assert(element); + for(NxI32 i=0;i void Array::SetSize(NxI32 s) +{ + if(s==0) + { + if(element) + { + MEMALLOC_FREE(element); + element = NULL; + } + array_size = s; + } + else + { + allocate(s); + } + count=s; +} + +template void Array::Pack() +{ + allocate(count); +} + +template Type& Array::Add(Type t) +{ + assert(count<=array_size); + if(count==array_size) + { + allocate((array_size)?array_size *2:16); + } + element[count++] = t; + return element[count-1]; +} + +template NxI32 Array::Contains(Type t) +{ + NxI32 i; + NxI32 found=0; + for(i=0;i void Array::AddUnique(Type t) +{ + if(!Contains(t)) Add(t); +} + + +template void Array::DelIndex(NxI32 i) +{ + assert(i void Array::Remove(Type t) +{ + NxI32 i; + for(i=0;i void Array::Insert(Type t,NxI32 k) +{ + NxI32 i=count; + Add(t); // to allocate space + while(i>k) + { + element[i]=element[i-1]; + i--; + } + assert(i==k); + element[k]=t; +} + + +template NxI32 Array::IndexOf(Type t) +{ + NxI32 i; + for(i=0;i Member )))- ((char*)NULL)) + + + +NxI32 argmin(NxF32 a[],NxI32 n); +NxF32 sqr(NxF32 a); +NxF32 clampf(NxF32 a) ; +NxF32 Round(NxF32 a,NxF32 precision); +NxF32 Interpolate(const NxF32 &f0,const NxF32 &f1,NxF32 alpha) ; + +template +void Swap(T &a,T &b) +{ + T tmp = a; + a=b; + b=tmp; +} + + + +template +T Max(const T &a,const T &b) +{ + return (a>b)?a:b; +} + +template +T Min(const T &a,const T &b) +{ + return (a=0&&i<2);return ((NxF32*)this)[i];} + const NxF32& operator[](NxI32 i) const {assert(i>=0&&i<2);return ((NxF32*)this)[i];} +}; +inline float2 operator-( const float2& a, const float2& b ){return float2(a.x-b.x,a.y-b.y);} +inline float2 operator+( const float2& a, const float2& b ){return float2(a.x+b.x,a.y+b.y);} + +//--------- 3D --------- + +class float3 : public Memalloc // 3D +{ + public: + NxF32 x,y,z; + float3(){x=0;y=0;z=0;}; + float3(NxF32 _x,NxF32 _y,NxF32 _z){x=_x;y=_y;z=_z;}; + //operator NxF32 *() { return &x;}; + NxF32& operator[](NxI32 i) {assert(i>=0&&i<3);return ((NxF32*)this)[i];} + const NxF32& operator[](NxI32 i) const {assert(i>=0&&i<3);return ((NxF32*)this)[i];} +}; + + +float3& operator+=( float3 &a, const float3& b ); +float3& operator-=( float3 &a ,const float3& b ); +float3& operator*=( float3 &v ,const NxF32 s ); +float3& operator/=( float3 &v, const NxF32 s ); + +NxF32 magnitude( const float3& v ); +float3 normalize( const float3& v ); +float3 safenormalize(const float3 &v); +float3 vabs(const float3 &v); +float3 operator+( const float3& a, const float3& b ); +float3 operator-( const float3& a, const float3& b ); +float3 operator-( const float3& v ); +float3 operator*( const float3& v, const NxF32 s ); +float3 operator*( const NxF32 s, const float3& v ); +float3 operator/( const float3& v, const NxF32 s ); +inline NxI32 operator==( const float3 &a, const float3 &b ) { return (a.x==b.x && a.y==b.y && a.z==b.z); } +inline NxI32 operator!=( const float3 &a, const float3 &b ) { return (a.x!=b.x || a.y!=b.y || a.z!=b.z); } +// due to ambiguity and inconsistent standards ther are no overloaded operators for mult such as va*vb. +NxF32 dot( const float3& a, const float3& b ); +float3 cmul( const float3 &a, const float3 &b); +float3 cross( const float3& a, const float3& b ); +float3 Interpolate(const float3 &v0,const float3 &v1,NxF32 alpha); +float3 Round(const float3& a,NxF32 precision); +float3 VectorMax(const float3 &a, const float3 &b); +float3 VectorMin(const float3 &a, const float3 &b); + + + +class float3x3 : public Memalloc +{ + public: + float3 x,y,z; // the 3 rows of the Matrix + float3x3(){} + float3x3(NxF32 xx,NxF32 xy,NxF32 xz,NxF32 yx,NxF32 yy,NxF32 yz,NxF32 zx,NxF32 zy,NxF32 zz):x(xx,xy,xz),y(yx,yy,yz),z(zx,zy,zz){} + float3x3(float3 _x,float3 _y,float3 _z):x(_x),y(_y),z(_z){} + float3& operator[](NxI32 i) {assert(i>=0&&i<3);return (&x)[i];} + const float3& operator[](NxI32 i) const {assert(i>=0&&i<3);return (&x)[i];} + NxF32& operator()(NxI32 r, NxI32 c) {assert(r>=0&&r<3&&c>=0&&c<3);return ((&x)[r])[c];} + const NxF32& operator()(NxI32 r, NxI32 c) const {assert(r>=0&&r<3&&c>=0&&c<3);return ((&x)[r])[c];} +}; +float3x3 Transpose( const float3x3& m ); +float3 operator*( const float3& v , const float3x3& m ); +float3 operator*( const float3x3& m , const float3& v ); +float3x3 operator*( const float3x3& m , const NxF32& s ); +float3x3 operator*( const float3x3& ma, const float3x3& mb ); +float3x3 operator/( const float3x3& a, const NxF32& s ) ; +float3x3 operator+( const float3x3& a, const float3x3& b ); +float3x3 operator-( const float3x3& a, const float3x3& b ); +float3x3 &operator+=( float3x3& a, const float3x3& b ); +float3x3 &operator-=( float3x3& a, const float3x3& b ); +float3x3 &operator*=( float3x3& a, const NxF32& s ); +NxF32 Determinant(const float3x3& m ); +float3x3 Inverse(const float3x3& a); // its just 3x3 so we simply do that cofactor method + + +//-------- 4D Math -------- + +class float4 : public Memalloc +{ +public: + NxF32 x,y,z,w; + float4(){x=0;y=0;z=0;w=0;}; + float4(NxF32 _x,NxF32 _y,NxF32 _z,NxF32 _w){x=_x;y=_y;z=_z;w=_w;} + float4(const float3 &v,NxF32 _w){x=v.x;y=v.y;z=v.z;w=_w;} + //operator NxF32 *() { return &x;}; + NxF32& operator[](NxI32 i) {assert(i>=0&&i<4);return ((NxF32*)this)[i];} + const NxF32& operator[](NxI32 i) const {assert(i>=0&&i<4);return ((NxF32*)this)[i];} + const float3& xyz() const { return *((float3*)this);} + float3& xyz() { return *((float3*)this);} +}; + + +struct D3DXMATRIX; + +class float4x4 : public Memalloc +{ + public: + float4 x,y,z,w; // the 4 rows + float4x4(){} + float4x4(const float4 &_x, const float4 &_y, const float4 &_z, const float4 &_w):x(_x),y(_y),z(_z),w(_w){} + float4x4(NxF32 m00, NxF32 m01, NxF32 m02, NxF32 m03, + NxF32 m10, NxF32 m11, NxF32 m12, NxF32 m13, + NxF32 m20, NxF32 m21, NxF32 m22, NxF32 m23, + NxF32 m30, NxF32 m31, NxF32 m32, NxF32 m33 ) + :x(m00,m01,m02,m03),y(m10,m11,m12,m13),z(m20,m21,m22,m23),w(m30,m31,m32,m33){} + NxF32& operator()(NxI32 r, NxI32 c) {assert(r>=0&&r<4&&c>=0&&c<4);return ((&x)[r])[c];} + const NxF32& operator()(NxI32 r, NxI32 c) const {assert(r>=0&&r<4&&c>=0&&c<4);return ((&x)[r])[c];} + operator NxF32* () {return &x.x;} + operator const NxF32* () const {return &x.x;} + operator struct D3DXMATRIX* () { return (struct D3DXMATRIX*) this;} + operator const struct D3DXMATRIX* () const { return (struct D3DXMATRIX*) this;} +}; + + +NxI32 operator==( const float4 &a, const float4 &b ); +float4 Homogenize(const float3 &v3,const NxF32 &w=1.0f); // Turns a 3D float3 4D vector4 by appending w +float4 cmul( const float4 &a, const float4 &b); +float4 operator*( const float4 &v, NxF32 s); +float4 operator*( NxF32 s, const float4 &v); +float4 operator+( const float4 &a, const float4 &b); +float4 operator-( const float4 &a, const float4 &b); +float4x4 operator*( const float4x4& a, const float4x4& b ); +float4 operator*( const float4& v, const float4x4& m ); +float4x4 Inverse(const float4x4 &m); +float4x4 MatrixRigidInverse(const float4x4 &m); +float4x4 MatrixTranspose(const float4x4 &m); +float4x4 MatrixPerspectiveFov(NxF32 fovy, NxF32 Aspect, NxF32 zn, NxF32 zf ); +float4x4 MatrixTranslation(const float3 &t); +float4x4 MatrixRotationZ(const NxF32 angle_radians); +float4x4 MatrixLookAt(const float3& eye, const float3& at, const float3& up); +NxI32 operator==( const float4x4 &a, const float4x4 &b ); + + +//-------- Quaternion ------------ + +class Quaternion :public float4 +{ + public: + Quaternion() { x = y = z = 0.0f; w = 1.0f; } + Quaternion( float3 v, NxF32 t ) { v = normalize(v); w = cosf(t/2.0f); v = v*sinf(t/2.0f); x = v.x; y = v.y; z = v.z; } + Quaternion(NxF32 _x, NxF32 _y, NxF32 _z, NxF32 _w){x=_x;y=_y;z=_z;w=_w;} + NxF32 angle() const { return acosf(w)*2.0f; } + float3 axis() const { float3 a(x,y,z); if(fabsf(angle())<0.0000001f) return float3(1,0,0); return a*(1/sinf(angle()/2.0f)); } + float3 xdir() const { return float3( 1-2*(y*y+z*z), 2*(x*y+w*z), 2*(x*z-w*y) ); } + float3 ydir() const { return float3( 2*(x*y-w*z),1-2*(x*x+z*z), 2*(y*z+w*x) ); } + float3 zdir() const { return float3( 2*(x*z+w*y), 2*(y*z-w*x),1-2*(x*x+y*y) ); } + float3x3 getmatrix() const { return float3x3( xdir(), ydir(), zdir() ); } + operator float3x3() { return getmatrix(); } + void Normalize(); +}; + +Quaternion& operator*=(Quaternion& a, NxF32 s ); +Quaternion operator*( const Quaternion& a, NxF32 s ); +Quaternion operator*( const Quaternion& a, const Quaternion& b); +Quaternion operator+( const Quaternion& a, const Quaternion& b ); +Quaternion normalize( Quaternion a ); +NxF32 dot( const Quaternion &a, const Quaternion &b ); +float3 operator*( const Quaternion& q, const float3& v ); +float3 operator*( const float3& v, const Quaternion& q ); +Quaternion slerp( Quaternion a, const Quaternion& b, NxF32 interp ); +Quaternion Interpolate(const Quaternion &q0,const Quaternion &q1,NxF32 alpha); +Quaternion RotationArc(float3 v0, float3 v1 ); // returns quat q where q*v0=v1 +Quaternion Inverse(const Quaternion &q); +float4x4 MatrixFromQuatVec(const Quaternion &q, const float3 &v); + + +//------ Euler Angle ----- + +Quaternion YawPitchRoll( NxF32 yaw, NxF32 pitch, NxF32 roll ); +NxF32 Yaw( const Quaternion& q ); +NxF32 Pitch( const Quaternion& q ); +NxF32 Roll( Quaternion q ); +NxF32 Yaw( const float3& v ); +NxF32 Pitch( const float3& v ); + + +//------- Plane ---------- + +class Plane +{ + public: + float3 normal; + NxF32 dist; // distance below origin - the D from plane equasion Ax+By+Cz+D=0 + Plane(const float3 &n,NxF32 d):normal(n),dist(d){} + Plane():normal(),dist(0){} + void Transform(const float3 &position, const Quaternion &orientation); +}; + +inline Plane PlaneFlip(const Plane &plane){return Plane(-plane.normal,-plane.dist);} +inline NxI32 operator==( const Plane &a, const Plane &b ) { return (a.normal==b.normal && a.dist==b.dist); } +inline NxI32 coplanar( const Plane &a, const Plane &b ) { return (a==b || a==PlaneFlip(b)); } + + +//--------- Utility Functions ------ + +float3 PlaneLineIntersection(const Plane &plane, const float3 &p0, const float3 &p1); +float3 PlaneProject(const Plane &plane, const float3 &point); +float3 LineProject(const float3 &p0, const float3 &p1, const float3 &a); // projects a onto infinite line p0p1 +NxF32 LineProjectTime(const float3 &p0, const float3 &p1, const float3 &a); +float3 ThreePlaneIntersection(const Plane &p0,const Plane &p1, const Plane &p2); +NxI32 PolyHit(const float3 *vert,const NxI32 n,const float3 &v0, const float3 &v1, float3 *impact=NULL, float3 *normal=NULL); +NxI32 BoxInside(const float3 &p,const float3 &bmin, const float3 &bmax) ; +NxI32 BoxIntersect(const float3 &v0, const float3 &v1, const float3 &bmin, const float3 &bmax, float3 *impact); +NxF32 DistanceBetweenLines(const float3 &ustart, const float3 &udir, const float3 &vstart, const float3 &vdir, float3 *upoint=NULL, float3 *vpoint=NULL); +float3 TriNormal(const float3 &v0, const float3 &v1, const float3 &v2); +float3 NormalOf(const float3 *vert, const NxI32 n); +Quaternion VirtualTrackBall(const float3 &cop, const float3 &cor, const float3 &dir0, const float3 &dir1); + + + + +//***************************************************** +// ** VECMATH.CPP +//***************************************************** + + +NxF32 sqr(NxF32 a) {return a*a;} +NxF32 clampf(NxF32 a) {return Min(1.0f,Max(0.0f,a));} + + +NxF32 Round(NxF32 a,NxF32 precision) +{ + return floorf(0.5f+a/precision)*precision; +} + + +NxF32 Interpolate(const NxF32 &f0,const NxF32 &f1,NxF32 alpha) +{ + return f0*(1-alpha) + f1*alpha; +} + + +NxI32 argmin(NxF32 a[],NxI32 n) +{ + NxI32 r=0; + for(NxI32 i=1;i=1.0) { + return a; + } + NxF32 theta = acosf(d); + if(theta==0.0f) { return(a);} + return a*(sinf(theta-interp*theta)/sinf(theta)) + b*(sinf(interp*theta)/sinf(theta)); +} + + +Quaternion Interpolate(const Quaternion &q0,const Quaternion &q1,NxF32 alpha) { + return slerp(q0,q1,alpha); +} + + +Quaternion YawPitchRoll( NxF32 yaw, NxF32 pitch, NxF32 roll ) +{ + roll *= DEG2RAD; + yaw *= DEG2RAD; + pitch *= DEG2RAD; + return Quaternion(float3(0.0f,0.0f,1.0f),yaw)*Quaternion(float3(1.0f,0.0f,0.0f),pitch)*Quaternion(float3(0.0f,1.0f,0.0f),roll); +} + +NxF32 Yaw( const Quaternion& q ) +{ + static float3 v; + v=q.ydir(); + return (v.y==0.0&&v.x==0.0) ? 0.0f: atan2f(-v.x,v.y)*RAD2DEG; +} + +NxF32 Pitch( const Quaternion& q ) +{ + static float3 v; + v=q.ydir(); + return atan2f(v.z,sqrtf(sqr(v.x)+sqr(v.y)))*RAD2DEG; +} + +NxF32 Roll( Quaternion q ) +{ + q = Quaternion(float3(0.0f,0.0f,1.0f),-Yaw(q)*DEG2RAD) *q; + q = Quaternion(float3(1.0f,0.0f,0.0f),-Pitch(q)*DEG2RAD) *q; + return atan2f(-q.xdir().z,q.xdir().x)*RAD2DEG; +} + +NxF32 Yaw( const float3& v ) +{ + return (v.y==0.0&&v.x==0.0) ? 0.0f: atan2f(-v.x,v.y)*RAD2DEG; +} + +NxF32 Pitch( const float3& v ) +{ + return atan2f(v.z,sqrtf(sqr(v.x)+sqr(v.y)))*RAD2DEG; +} + + +//------------- Plane -------------- + + +void Plane::Transform(const float3 &position, const Quaternion &orientation) { + // Transforms the plane to the space defined by the + // given position/orientation. + static float3 newnormal; + static float3 origin; + + newnormal = Inverse(orientation)*normal; + origin = Inverse(orientation)*(-normal*dist - position); + + normal = newnormal; + dist = -dot(newnormal, origin); +} + + + + +//--------- utility functions ------------- + +// RotationArc() +// Given two vectors v0 and v1 this function +// returns quaternion q where q*v0==v1. +// Routine taken from game programming gems. +Quaternion RotationArc(float3 v0,float3 v1){ + static Quaternion q; + v0 = normalize(v0); // Comment these two lines out if you know its not needed. + v1 = normalize(v1); // If vector is already unit length then why do it again? + float3 c = cross(v0,v1); + NxF32 d = dot(v0,v1); + if(d<=-1.0f) { return Quaternion(1,0,0,0);} // 180 about x axis + NxF32 s = sqrtf((1+d)*2); + q.x = c.x / s; + q.y = c.y / s; + q.z = c.z / s; + q.w = s /2.0f; + return q; +} + + +float4x4 MatrixFromQuatVec(const Quaternion &q, const float3 &v) +{ + // builds a 4x4 transformation matrix based on orientation q and translation v + NxF32 qx2 = q.x*q.x; + NxF32 qy2 = q.y*q.y; + NxF32 qz2 = q.z*q.z; + + NxF32 qxqy = q.x*q.y; + NxF32 qxqz = q.x*q.z; + NxF32 qxqw = q.x*q.w; + NxF32 qyqz = q.y*q.z; + NxF32 qyqw = q.y*q.w; + NxF32 qzqw = q.z*q.w; + + return float4x4( + 1-2*(qy2+qz2), + 2*(qxqy+qzqw), + 2*(qxqz-qyqw), + 0 , + 2*(qxqy-qzqw), + 1-2*(qx2+qz2), + 2*(qyqz+qxqw), + 0 , + 2*(qxqz+qyqw), + 2*(qyqz-qxqw), + 1-2*(qx2+qy2), + 0 , + v.x , + v.y , + v.z , + 1.0f ); +} + + +float3 PlaneLineIntersection(const Plane &plane, const float3 &p0, const float3 &p1) +{ + // returns the point where the line p0-p1 intersects the plane n&d + static float3 dif; + dif = p1-p0; + NxF32 dn= dot(plane.normal,dif); + NxF32 t = -(plane.dist+dot(plane.normal,p0) )/dn; + return p0 + (dif*t); +} + +float3 PlaneProject(const Plane &plane, const float3 &point) +{ + return point - plane.normal * (dot(point,plane.normal)+plane.dist); +} + +float3 LineProject(const float3 &p0, const float3 &p1, const float3 &a) +{ + float3 w; + w = p1-p0; + NxF32 t= dot(w,(a-p0)) / (sqr(w.x)+sqr(w.y)+sqr(w.z)); + return p0+ w*t; +} + + +NxF32 LineProjectTime(const float3 &p0, const float3 &p1, const float3 &a) +{ + float3 w; + w = p1-p0; + NxF32 t= dot(w,(a-p0)) / (sqr(w.x)+sqr(w.y)+sqr(w.z)); + return t; +} + + + +float3 TriNormal(const float3 &v0, const float3 &v1, const float3 &v2) +{ + // return the normal of the triangle + // inscribed by v0, v1, and v2 + float3 cp=cross(v1-v0,v2-v1); + NxF32 m=magnitude(cp); + if(m==0) return float3(1,0,0); + return cp*(1.0f/m); +} + + + +NxI32 BoxInside(const float3 &p, const float3 &bmin, const float3 &bmax) +{ + return (p.x >= bmin.x && p.x <=bmax.x && + p.y >= bmin.y && p.y <=bmax.y && + p.z >= bmin.z && p.z <=bmax.z ); +} + + +NxI32 BoxIntersect(const float3 &v0, const float3 &v1, const float3 &bmin, const float3 &bmax,float3 *impact) +{ + if(BoxInside(v0,bmin,bmax)) + { + *impact=v0; + return 1; + } + if(v0.x<=bmin.x && v1.x>=bmin.x) + { + NxF32 a = (bmin.x-v0.x)/(v1.x-v0.x); + //v.x = bmin.x; + NxF32 vy = (1-a) *v0.y + a*v1.y; + NxF32 vz = (1-a) *v0.z + a*v1.z; + if(vy>=bmin.y && vy<=bmax.y && vz>=bmin.z && vz<=bmax.z) + { + impact->x = bmin.x; + impact->y = vy; + impact->z = vz; + return 1; + } + } + else if(v0.x >= bmax.x && v1.x <= bmax.x) + { + NxF32 a = (bmax.x-v0.x)/(v1.x-v0.x); + //v.x = bmax.x; + NxF32 vy = (1-a) *v0.y + a*v1.y; + NxF32 vz = (1-a) *v0.z + a*v1.z; + if(vy>=bmin.y && vy<=bmax.y && vz>=bmin.z && vz<=bmax.z) + { + impact->x = bmax.x; + impact->y = vy; + impact->z = vz; + return 1; + } + } + if(v0.y<=bmin.y && v1.y>=bmin.y) + { + NxF32 a = (bmin.y-v0.y)/(v1.y-v0.y); + NxF32 vx = (1-a) *v0.x + a*v1.x; + //v.y = bmin.y; + NxF32 vz = (1-a) *v0.z + a*v1.z; + if(vx>=bmin.x && vx<=bmax.x && vz>=bmin.z && vz<=bmax.z) + { + impact->x = vx; + impact->y = bmin.y; + impact->z = vz; + return 1; + } + } + else if(v0.y >= bmax.y && v1.y <= bmax.y) + { + NxF32 a = (bmax.y-v0.y)/(v1.y-v0.y); + NxF32 vx = (1-a) *v0.x + a*v1.x; + // vy = bmax.y; + NxF32 vz = (1-a) *v0.z + a*v1.z; + if(vx>=bmin.x && vx<=bmax.x && vz>=bmin.z && vz<=bmax.z) + { + impact->x = vx; + impact->y = bmax.y; + impact->z = vz; + return 1; + } + } + if(v0.z<=bmin.z && v1.z>=bmin.z) + { + NxF32 a = (bmin.z-v0.z)/(v1.z-v0.z); + NxF32 vx = (1-a) *v0.x + a*v1.x; + NxF32 vy = (1-a) *v0.y + a*v1.y; + // v.z = bmin.z; + if(vy>=bmin.y && vy<=bmax.y && vx>=bmin.x && vx<=bmax.x) + { + impact->x = vx; + impact->y = vy; + impact->z = bmin.z; + return 1; + } + } + else if(v0.z >= bmax.z && v1.z <= bmax.z) + { + NxF32 a = (bmax.z-v0.z)/(v1.z-v0.z); + NxF32 vx = (1-a) *v0.x + a*v1.x; + NxF32 vy = (1-a) *v0.y + a*v1.y; + // v.z = bmax.z; + if(vy>=bmin.y && vy<=bmax.y && vx>=bmin.x && vx<=bmax.x) + { + impact->x = vx; + impact->y = vy; + impact->z = bmax.z; + return 1; + } + } + return 0; +} + + +NxF32 DistanceBetweenLines(const float3 &ustart, const float3 &udir, const float3 &vstart, const float3 &vdir, float3 *upoint, float3 *vpoint) +{ + static float3 cp; + cp = normalize(cross(udir,vdir)); + + NxF32 distu = -dot(cp,ustart); + NxF32 distv = -dot(cp,vstart); + NxF32 dist = (NxF32)fabs(distu-distv); + if(upoint) + { + Plane plane; + plane.normal = normalize(cross(vdir,cp)); + plane.dist = -dot(plane.normal,vstart); + *upoint = PlaneLineIntersection(plane,ustart,ustart+udir); + } + if(vpoint) + { + Plane plane; + plane.normal = normalize(cross(udir,cp)); + plane.dist = -dot(plane.normal,ustart); + *vpoint = PlaneLineIntersection(plane,vstart,vstart+vdir); + } + return dist; +} + + +Quaternion VirtualTrackBall(const float3 &cop, const float3 &cor, const float3 &dir1, const float3 &dir2) +{ + // routine taken from game programming gems. + // Implement track ball functionality to spin stuf on the screen + // cop center of projection + // cor center of rotation + // dir1 old mouse direction + // dir2 new mouse direction + // pretend there is a sphere around cor. Then find the points + // where dir1 and dir2 intersect that sphere. Find the + // rotation that takes the first point to the second. + NxF32 m; + // compute plane + float3 nrml = cor - cop; + NxF32 fudgefactor = 1.0f/(magnitude(nrml) * 0.25f); // since trackball proportional to distance from cop + nrml = normalize(nrml); + NxF32 dist = -dot(nrml,cor); + float3 u= PlaneLineIntersection(Plane(nrml,dist),cop,cop+dir1); + u=u-cor; + u=u*fudgefactor; + m= magnitude(u); + if(m>1) + { + u/=m; + } + else + { + u=u - (nrml * sqrtf(1-m*m)); + } + float3 v= PlaneLineIntersection(Plane(nrml,dist),cop,cop+dir2); + v=v-cor; + v=v*fudgefactor; + m= magnitude(v); + if(m>1) + { + v/=m; + } + else + { + v=v - (nrml * sqrtf(1-m*m)); + } + return RotationArc(u,v); +} + + +NxI32 countpolyhit=0; +NxI32 PolyHit(const float3 *vert, const NxI32 n, const float3 &v0, const float3 &v1, float3 *impact, float3 *normal) +{ + countpolyhit++; + NxI32 i; + float3 nrml(0,0,0); + for(i=0;i0) + { + return 0; + } + + static float3 the_point; + // By using the cached plane distances d0 and d1 + // we can optimize the following: + // the_point = planelineintersection(nrml,dist,v0,v1); + NxF32 a = d0/(d0-d1); + the_point = v0*(1-a) + v1*a; + + + NxI32 inside=1; + for(NxI32 j=0;inside && j= 0.0); + } + if(inside) + { + if(normal){*normal=nrml;} + if(impact){*impact=the_point;} + } + return inside; +} + +//**************************************************** +// HULL.H source code goes here +//**************************************************** +class PHullResult +{ +public: + + PHullResult(void) + { + mVcount = 0; + mIndexCount = 0; + mFaceCount = 0; + mVertices = 0; + mIndices = 0; + } + + NxU32 mVcount; + NxU32 mIndexCount; + NxU32 mFaceCount; + NxF32 *mVertices; + NxU32 *mIndices; +}; + +bool ComputeHull(NxU32 vcount,const NxF32 *vertices,PHullResult &result,NxU32 maxverts,NxF32 inflate); +void ReleaseHull(PHullResult &result); + +//***************************************************** +// HULL.cpp source code goes here +//***************************************************** + + +#define REAL3 float3 +#define REAL NxF32 + +#define COPLANAR (0) +#define UNDER (1) +#define OVER (2) +#define SPLIT (OVER|UNDER) +#define PAPERWIDTH (0.001f) +#define VOLUME_EPSILON (1e-20f) + +NxF32 planetestepsilon = PAPERWIDTH; + +class ConvexH : public Memalloc +{ + public: + class HalfEdge + { + public: + short ea; // the other half of the edge (index into edges list) + NxU8 v; // the vertex at the start of this edge (index into vertices list) + NxU8 p; // the facet on which this edge lies (index into facets list) + HalfEdge(){} + HalfEdge(short _ea,NxU8 _v, NxU8 _p):ea(_ea),v(_v),p(_p){} + }; + Array vertices; + Array edges; + Array facets; + ConvexH(NxI32 vertices_size,NxI32 edges_size,NxI32 facets_size); +}; + +typedef ConvexH::HalfEdge HalfEdge; + +ConvexH::ConvexH(NxI32 vertices_size,NxI32 edges_size,NxI32 facets_size) + :vertices(vertices_size) + ,edges(edges_size) + ,facets(facets_size) +{ + vertices.count=vertices_size; + edges.count = edges_size; + facets.count = facets_size; +} + +ConvexH *ConvexHDup(ConvexH *src) +{ + ConvexH *dst = MEMALLOC_NEW(ConvexH)(src->vertices.count,src->edges.count,src->facets.count); + + memcpy(dst->vertices.element,src->vertices.element,sizeof(float3)*src->vertices.count); + memcpy(dst->edges.element,src->edges.element,sizeof(HalfEdge)*src->edges.count); + memcpy(dst->facets.element,src->facets.element,sizeof(Plane)*src->facets.count); + return dst; +} + + +NxI32 PlaneTest(const Plane &p, const REAL3 &v) { + REAL a = dot(v,p.normal)+p.dist; + NxI32 flag = (a>planetestepsilon)?OVER:((a<-planetestepsilon)?UNDER:COPLANAR); + return flag; +} + +NxI32 SplitTest(ConvexH &convex,const Plane &plane) { + NxI32 flag=0; + for(NxI32 i=0;i= convex.edges.count || convex.edges[inext].p != convex.edges[i].p) { + inext = estart; + } + assert(convex.edges[inext].p == convex.edges[i].p); + NxI32 nb = convex.edges[i].ea; + assert(nb!=255); + if(nb==255 || nb==-1) return 0; + assert(nb!=-1); + assert(i== convex.edges[nb].ea); + } + for(i=0;i= convex.edges.count || convex.edges[i1].p != convex.edges[i].p) { + i1 = estart; + } + NxI32 i2 = i1+1; + if(i2>= convex.edges.count || convex.edges[i2].p != convex.edges[i].p) { + i2 = estart; + } + if(i==i2) continue; // i sliced tangent to an edge and created 2 meaningless edges + REAL3 localnormal = TriNormal(convex.vertices[convex.edges[i ].v], + convex.vertices[convex.edges[i1].v], + convex.vertices[convex.edges[i2].v]); + //assert(dot(localnormal,convex.facets[convex.edges[i].p].normal)>0);//Commented out on Stan Melax' advice + if(dot(localnormal,convex.facets[convex.edges[i].p].normal)<=0)return 0; + } + return 1; +} + +ConvexH *ConvexHCrop(ConvexH &convex,const Plane &slice) +{ + NxI32 i; + NxI32 vertcountunder=0; + NxI32 vertcountover =0; + static Array vertscoplanar; // existing vertex members of convex that are coplanar + vertscoplanar.count=0; + static Array edgesplit; // existing edges that members of convex that cross the splitplane + edgesplit.count=0; + + assert(convex.edges.count<480); + + EdgeFlag edgeflag[512]; + VertFlag vertflag[256]; + PlaneFlag planeflag[128]; + HalfEdge tmpunderedges[512]; + Plane tmpunderplanes[128]; + Coplanar coplanaredges[512]; + NxI32 coplanaredges_num=0; + + Array createdverts; + // do the side-of-plane tests + for(i=0;i= convex.edges.count || convex.edges[e1].p!=currentplane) { + enextface = e1; + e1=estart; + } + HalfEdge &edge0 = convex.edges[e0]; + HalfEdge &edge1 = convex.edges[e1]; + HalfEdge &edgea = convex.edges[edge0.ea]; + + + planeside |= vertflag[edge0.v].planetest; + //if((vertflag[edge0.v].planetest & vertflag[edge1.v].planetest) == COPLANAR) { + // assert(ecop==-1); + // ecop=e; + //} + + + if(vertflag[edge0.v].planetest == OVER && vertflag[edge1.v].planetest == OVER){ + // both endpoints over plane + edgeflag[e0].undermap = -1; + } + else if((vertflag[edge0.v].planetest | vertflag[edge1.v].planetest) == UNDER) { + // at least one endpoint under, the other coplanar or under + + edgeflag[e0].undermap = (short)under_edge_count; + tmpunderedges[under_edge_count].v = (NxU8)vertflag[edge0.v].undermap; + tmpunderedges[under_edge_count].p = (NxU8)underplanescount; + if(edge0.ea < e0) { + // connect the neighbors + assert(edgeflag[edge0.ea].undermap !=-1); + tmpunderedges[under_edge_count].ea = edgeflag[edge0.ea].undermap; + tmpunderedges[edgeflag[edge0.ea].undermap].ea = (short)under_edge_count; + } + under_edge_count++; + } + else if((vertflag[edge0.v].planetest | vertflag[edge1.v].planetest) == COPLANAR) { + // both endpoints coplanar + // must check a 3rd point to see if UNDER + NxI32 e2 = e1+1; + if(e2>=convex.edges.count || convex.edges[e2].p!=currentplane) { + e2 = estart; + } + assert(convex.edges[e2].p==currentplane); + HalfEdge &edge2 = convex.edges[e2]; + if(vertflag[edge2.v].planetest==UNDER) { + + edgeflag[e0].undermap = (short)under_edge_count; + tmpunderedges[under_edge_count].v = (NxU8)vertflag[edge0.v].undermap; + tmpunderedges[under_edge_count].p = (NxU8)underplanescount; + tmpunderedges[under_edge_count].ea = -1; + // make sure this edge is added to the "coplanar" list + coplanaredge = under_edge_count; + vout = vertflag[edge0.v].undermap; + vin = vertflag[edge1.v].undermap; + under_edge_count++; + } + else { + edgeflag[e0].undermap = -1; + } + } + else if(vertflag[edge0.v].planetest == UNDER && vertflag[edge1.v].planetest == OVER) { + // first is under 2nd is over + + edgeflag[e0].undermap = (short) under_edge_count; + tmpunderedges[under_edge_count].v = (NxU8)vertflag[edge0.v].undermap; + tmpunderedges[under_edge_count].p = (NxU8)underplanescount; + if(edge0.ea < e0) { + assert(edgeflag[edge0.ea].undermap !=-1); + // connect the neighbors + tmpunderedges[under_edge_count].ea = edgeflag[edge0.ea].undermap; + tmpunderedges[edgeflag[edge0.ea].undermap].ea = (short)under_edge_count; + vout = tmpunderedges[edgeflag[edge0.ea].undermap].v; + } + else { + Plane &p0 = convex.facets[edge0.p]; + Plane &pa = convex.facets[edgea.p]; + createdverts.Add(ThreePlaneIntersection(p0,pa,slice)); + //createdverts.Add(PlaneProject(slice,PlaneLineIntersection(slice,convex.vertices[edge0.v],convex.vertices[edgea.v]))); + //createdverts.Add(PlaneLineIntersection(slice,convex.vertices[edge0.v],convex.vertices[edgea.v])); + vout = vertcountunder++; + } + under_edge_count++; + /// hmmm something to think about: i might be able to output this edge regarless of + // wheter or not we know v-in yet. ok i;ll try this now: + tmpunderedges[under_edge_count].v = (NxU8)vout; + tmpunderedges[under_edge_count].p = (NxU8)underplanescount; + tmpunderedges[under_edge_count].ea = -1; + coplanaredge = under_edge_count; + under_edge_count++; + + if(vin!=-1) { + // we previously processed an edge where we came under + // now we know about vout as well + + // ADD THIS EDGE TO THE LIST OF EDGES THAT NEED NEIGHBOR ON PARTITION PLANE!! + } + + } + else if(vertflag[edge0.v].planetest == COPLANAR && vertflag[edge1.v].planetest == OVER) { + // first is coplanar 2nd is over + + edgeflag[e0].undermap = -1; + vout = vertflag[edge0.v].undermap; + // I hate this but i have to make sure part of this face is UNDER before ouputting this vert + NxI32 k=estart; + assert(edge0.p == currentplane); + while(!(planeside&UNDER) && kedge0.ea) { + assert(edgeflag[edge0.ea].undermap !=-1); + // connect the neighbors + tmpunderedges[under_edge_count].ea = edgeflag[edge0.ea].undermap; + tmpunderedges[edgeflag[edge0.ea].undermap].ea = (short)under_edge_count; + } + assert(edgeflag[e0].undermap == under_edge_count); + under_edge_count++; + } + else if(vertflag[edge0.v].planetest == OVER && vertflag[edge1.v].planetest == COPLANAR) { + // first is over next is coplanar + + edgeflag[e0].undermap = -1; + vin = vertflag[edge1.v].undermap; + if (vin==-1) return NULL; + if(vout!=-1) { + // we previously processed an edge where we came under + // now we know both endpoints + // ADD THIS EDGE TO THE LIST OF EDGES THAT NEED NEIGHBOR ON PARTITION PLANE!! + } + + } + else { + assert(0); + } + + + e0=e1; + e1++; // do the modulo at the beginning of the loop + + } while(e0!=estart) ; + e0 = enextface; + if(planeside&UNDER) { + planeflag[currentplane].undermap = (NxU8)underplanescount; + tmpunderplanes[underplanescount] = convex.facets[currentplane]; + underplanescount++; + } + else { + planeflag[currentplane].undermap = 0; + } + if(vout>=0 && (planeside&UNDER)) { + assert(vin>=0); + assert(coplanaredge>=0); + assert(coplanaredge!=511); + coplanaredges[coplanaredges_num].ea = (short)coplanaredge; + coplanaredges[coplanaredges_num].v0 = (NxU8)vin; + coplanaredges[coplanaredges_num].v1 = (NxU8)vout; + coplanaredges_num++; + } + } + + // add the new plane to the mix: + if(coplanaredges_num>0) { + tmpunderplanes[underplanescount++]=slice; + } + for(i=0;i=coplanaredges_num) + { + // assert(jvertices.count;j++) + { + dmax = Max(dmax,dot(convex->vertices[j],planes[i].normal)+planes[i].dist); + dmin = Min(dmin,dot(convex->vertices[j],planes[i].normal)+planes[i].dist); + } + NxF32 dr = dmax-dmin; + if(drfacets.count;j++) + { + if(planes[i]==convex->facets[j]) + { + d=0;continue; + } + if(dot(planes[i].normal,convex->facets[j].normal)>maxdot_minang) + { + for(NxI32 k=0;kedges.count;k++) + { + if(convex->edges[k].p!=j) continue; + if(dot(convex->vertices[convex->edges[k].v],planes[i].normal)+planes[i].dist<0) + { + d=0; // so this plane wont get selected. + break; + } + } + } + } + if(d>md) + { + p=i; + md=d; + } + } + return (md>epsilon)?p:-1; +} + + + +template +inline NxI32 maxdir(const T *p,NxI32 count,const T &dir) +{ + assert(count); + NxI32 m=0; + for(NxI32 i=1;idot(p[m],dir)) m=i; + } + return m; +} + + +template +NxI32 maxdirfiltered(const T *p,NxI32 count,const T &dir,Array &allow) +{ + assert(count); + NxI32 m=-1; + for(NxI32 i=0;idot(p[m],dir)) m=i; + } + assert(m!=-1); + return m; +} + +float3 orth(const float3 &v) +{ + float3 a=cross(v,float3(0,0,1)); + float3 b=cross(v,float3(0,1,0)); + return normalize((magnitude(a)>magnitude(b))?a:b); +} + + +template +NxI32 maxdirsterid(const T *p,NxI32 count,const T &dir,Array &allow) +{ + NxI32 m=-1; + while(m==-1) + { + m = maxdirfiltered(p,count,dir,allow); + if(allow[m]==3) return m; + T u = orth(dir); + T v = cross(u,dir); + NxI32 ma=-1; + for(NxF32 x = 0.0f ; x<= 360.0f ; x+= 45.0f) + { + NxF32 s = sinf(DEG2RAD*(x)); + NxF32 c = cosf(DEG2RAD*(x)); + NxI32 mb = maxdirfiltered(p,count,dir+(u*s+v*c)*0.025f,allow); + if(ma==m && mb==m) + { + allow[m]=3; + return m; + } + if(ma!=-1 && ma!=mb) // Yuck - this is really ugly + { + NxI32 mc = ma; + for(NxF32 xx = x-40.0f ; xx <= x ; xx+= 5.0f) + { + NxF32 s = sinf(DEG2RAD*(xx)); + NxF32 c = cosf(DEG2RAD*(xx)); + NxI32 md = maxdirfiltered(p,count,dir+(u*s+v*c)*0.025f,allow); + if(mc==m && md==m) + { + allow[m]=3; + return m; + } + mc=md; + } + } + ma=mb; + } + allow[m]=0; + m=-1; + } + assert(0); + return m; +} + + + + +NxI32 operator ==(const int3 &a,const int3 &b) +{ + for(NxI32 i=0;i<3;i++) + { + if(a[i]!=b[i]) return 0; + } + return 1; +} + +int3 roll3(int3 a) +{ + NxI32 tmp=a[0]; + a[0]=a[1]; + a[1]=a[2]; + a[2]=tmp; + return a; +} +NxI32 isa(const int3 &a,const int3 &b) +{ + return ( a==b || roll3(a)==b || a==roll3(b) ); +} +NxI32 b2b(const int3 &a,const int3 &b) +{ + return isa(a,int3(b[2],b[1],b[0])); +} +NxI32 above(float3* vertices,const int3& t, const float3 &p, NxF32 epsilon) +{ + float3 n=TriNormal(vertices[t[0]],vertices[t[1]],vertices[t[2]]); + return (dot(n,p-vertices[t[0]]) > epsilon); // EPSILON??? +} +NxI32 hasedge(const int3 &t, NxI32 a,NxI32 b) +{ + for(NxI32 i=0;i<3;i++) + { + NxI32 i1= (i+1)%3; + if(t[i]==a && t[i1]==b) return 1; + } + return 0; +} +NxI32 hasvert(const int3 &t, NxI32 v) +{ + return (t[0]==v || t[1]==v || t[2]==v) ; +} +NxI32 shareedge(const int3 &a,const int3 &b) +{ + NxI32 i; + for(i=0;i<3;i++) + { + NxI32 i1= (i+1)%3; + if(hasedge(a,b[i1],b[i])) return 1; + } + return 0; +} + +class Tri; + +static Array tris; // djs: For heaven's sake!!!! + +class Tri : public int3 +{ +public: + int3 n; + NxI32 id; + NxI32 vmax; + NxF32 rise; + Tri(NxI32 a,NxI32 b,NxI32 c):int3(a,b,c),n(-1,-1,-1) + { + id = tris.count; + tris.Add(this); + vmax=-1; + rise = 0.0f; + } + ~Tri() + { + assert(tris[id]==this); + tris[id]=NULL; + } + NxI32 &neib(NxI32 a,NxI32 b); +}; + + +NxI32 &Tri::neib(NxI32 a,NxI32 b) +{ + static NxI32 er=-1; + NxI32 i; + for(i=0;i<3;i++) + { + NxI32 i1=(i+1)%3; + NxI32 i2=(i+2)%3; + if((*this)[i]==a && (*this)[i1]==b) return n[i2]; + if((*this)[i]==b && (*this)[i1]==a) return n[i2]; + } + assert(0); + return er; +} +void b2bfix(Tri* s,Tri*t) +{ + NxI32 i; + for(i=0;i<3;i++) + { + NxI32 i1=(i+1)%3; + NxI32 i2=(i+2)%3; + NxI32 a = (*s)[i1]; + NxI32 b = (*s)[i2]; + assert(tris[s->neib(a,b)]->neib(b,a) == s->id); + assert(tris[t->neib(a,b)]->neib(b,a) == t->id); + tris[s->neib(a,b)]->neib(b,a) = t->neib(b,a); + tris[t->neib(b,a)]->neib(a,b) = s->neib(a,b); + } +} + +void removeb2b(Tri* s,Tri*t) +{ + b2bfix(s,t); + delete s; + delete t; +} + +void extrude(Tri *t0,NxI32 v) +{ + int3 t= *t0; + NxI32 n = tris.count; + Tri* ta = MEMALLOC_NEW(Tri)(v,t[1],t[2]); + ta->n = int3(t0->n[0],n+1,n+2); + tris[t0->n[0]]->neib(t[1],t[2]) = n+0; + Tri* tb = MEMALLOC_NEW(Tri)(v,t[2],t[0]); + tb->n = int3(t0->n[1],n+2,n+0); + tris[t0->n[1]]->neib(t[2],t[0]) = n+1; + Tri* tc = MEMALLOC_NEW(Tri)(v,t[0],t[1]); + tc->n = int3(t0->n[2],n+0,n+1); + tris[t0->n[2]]->neib(t[0],t[1]) = n+2; + if(hasvert(*tris[ta->n[0]],v)) removeb2b(ta,tris[ta->n[0]]); + if(hasvert(*tris[tb->n[0]],v)) removeb2b(tb,tris[tb->n[0]]); + if(hasvert(*tris[tc->n[0]],v)) removeb2b(tc,tris[tc->n[0]]); + delete t0; + +} + +Tri *extrudable(NxF32 epsilon) +{ + NxI32 i; + Tri *t=NULL; + for(i=0;iriserise)) + { + t = tris[i]; + } + } + return (t->rise >epsilon)?t:NULL ; +} + +class int4 +{ +public: + NxI32 x,y,z,w; + int4(){}; + int4(NxI32 _x,NxI32 _y, NxI32 _z,NxI32 _w){x=_x;y=_y;z=_z;w=_w;} + const NxI32& operator[](NxI32 i) const {return (&x)[i];} + NxI32& operator[](NxI32 i) {return (&x)[i];} +}; + + + +bool hasVolume(float3 *verts, NxI32 p0, NxI32 p1, NxI32 p2, NxI32 p3) +{ + float3 result3 = cross(verts[p1]-verts[p0], verts[p2]-verts[p0]); + if (magnitude(result3) < VOLUME_EPSILON && magnitude(result3) > -VOLUME_EPSILON) // Almost collinear or otherwise very close to each other + return false; + NxF32 result = dot(normalize(result3), verts[p3]-verts[p0]); + return (result > VOLUME_EPSILON || result < -VOLUME_EPSILON); // Returns true iff volume is significantly non-zero +} + +int4 FindSimplex(float3 *verts,NxI32 verts_count,Array &allow) +{ + float3 basis[3]; + basis[0] = float3( 0.01f, 0.02f, 1.0f ); + NxI32 p0 = maxdirsterid(verts,verts_count, basis[0],allow); + NxI32 p1 = maxdirsterid(verts,verts_count,-basis[0],allow); + basis[0] = verts[p0]-verts[p1]; + if(p0==p1 || basis[0]==float3(0,0,0)) + return int4(-1,-1,-1,-1); + basis[1] = cross(float3( 1, 0.02f, 0),basis[0]); + basis[2] = cross(float3(-0.02f, 1, 0),basis[0]); + basis[1] = normalize( (magnitude(basis[1])>magnitude(basis[2])) ? basis[1]:basis[2]); + NxI32 p2 = maxdirsterid(verts,verts_count,basis[1],allow); + if(p2 == p0 || p2 == p1) + { + p2 = maxdirsterid(verts,verts_count,-basis[1],allow); + } + if(p2 == p0 || p2 == p1) + return int4(-1,-1,-1,-1); + basis[1] = verts[p2] - verts[p0]; + basis[2] = normalize(cross(basis[1],basis[0])); + NxI32 p3 = maxdirsterid(verts,verts_count,basis[2],allow); + if(p3==p0||p3==p1||p3==p2||!hasVolume(verts, p0, p1, p2, p3)) p3 = maxdirsterid(verts,verts_count,-basis[2],allow); + if(p3==p0||p3==p1||p3==p2) + return int4(-1,-1,-1,-1); + assert(!(p0==p1||p0==p2||p0==p3||p1==p2||p1==p3||p2==p3)); + if(dot(verts[p3]-verts[p0],cross(verts[p1]-verts[p0],verts[p2]-verts[p0])) <0) {Swap(p2,p3);} + return int4(p0,p1,p2,p3); +} +#pragma warning(push) +#pragma warning(disable:4706) +NxI32 calchullgen(float3 *verts,NxI32 verts_count, NxI32 vlimit) +{ + if(verts_count <4) return 0; + if(vlimit==0) vlimit=1000000000; + NxI32 j; + float3 bmin(*verts),bmax(*verts); + Array isextreme(verts_count); + Array allow(verts_count); + for(j=0;jn=int3(2,3,1); + Tri *t1 = MEMALLOC_NEW(Tri)(p[3],p[2],p[0]); t1->n=int3(3,2,0); + Tri *t2 = MEMALLOC_NEW(Tri)(p[0],p[1],p[3]); t2->n=int3(0,1,3); + Tri *t3 = MEMALLOC_NEW(Tri)(p[1],p[0],p[2]); t3->n=int3(1,0,2); + isextreme[p[0]]=isextreme[p[1]]=isextreme[p[2]]=isextreme[p[3]]=1; + + for(j=0;jvmax<0); + float3 n=TriNormal(verts[(*t)[0]],verts[(*t)[1]],verts[(*t)[2]]); + t->vmax = maxdirsterid(verts,verts_count,n,allow); + t->rise = dot(n,verts[t->vmax]-verts[(*t)[0]]); + } + Tri *te; + vlimit-=4; + while(vlimit >0 && (te=extrudable(epsilon))) + { + int3 ti=*te; + NxI32 v=te->vmax; + assert(!isextreme[v]); // wtf we've already done this vertex + isextreme[v]=1; + //if(v==p0 || v==p1 || v==p2 || v==p3) continue; // done these already + j=tris.count; + while(j--) { + if(!tris[j]) continue; + int3 t=*tris[j]; + if(above(verts,t,verts[v],0.01f*epsilon)) + { + extrude(tris[j],v); + } + } + // now check for those degenerate cases where we have a flipped triangle or a really skinny triangle + j=tris.count; + while(j--) + { + if(!tris[j]) continue; + if(!hasvert(*tris[j],v)) break; + int3 nt=*tris[j]; + if(above(verts,nt,center,0.01f*epsilon) || magnitude(cross(verts[nt[1]]-verts[nt[0]],verts[nt[2]]-verts[nt[1]]))< epsilon*epsilon*0.1f ) + { + Tri *nb = tris[tris[j]->n[0]]; + assert(nb);assert(!hasvert(*nb,v));assert(nb->idvmax>=0) break; + float3 n=TriNormal(verts[(*t)[0]],verts[(*t)[1]],verts[(*t)[2]]); + t->vmax = maxdirsterid(verts,verts_count,n,allow); + if(isextreme[t->vmax]) + { + t->vmax=-1; // already done that vertex - algorithm needs to be able to terminate. + } + else + { + t->rise = dot(n,verts[t->vmax]-verts[(*t)[0]]); + } + } + vlimit --; + } + return 1; +} +#pragma warning(pop) + +NxI32 calchull(float3 *verts,NxI32 verts_count, NxI32 *&tris_out, NxI32 &tris_count,NxI32 vlimit) +{ + NxI32 rc=calchullgen(verts,verts_count, vlimit) ; + if(!rc) return 0; + Array ts; + for(NxI32 i=0;i &planes,NxF32 bevangle) +{ + NxI32 i,j; + Array bplanes; + planes.count=0; + NxI32 rc = calchullgen(verts,verts_count,vlimit); + if(!rc) return 0; + extern NxF32 minadjangle; // default is 3.0f; // in degrees - result wont have two adjacent facets within this angle of each other. + NxF32 maxdot_minang = cosf(DEG2RAD*minadjangle); + for(i=0;in[j]id) continue; + Tri *s = tris[t->n[j]]; + REAL3 snormal = TriNormal(verts[(*s)[0]],verts[(*s)[1]],verts[(*s)[2]]); + if(dot(snormal,p.normal)>=cos(bevangle*DEG2RAD)) continue; + REAL3 e = verts[(*t)[(j+2)%3]] - verts[(*t)[(j+1)%3]]; + REAL3 n = (e!=REAL3(0,0,0))? cross(snormal,e)+cross(e,p.normal) : snormal+p.normal; + assert(n!=REAL3(0,0,0)); + if(n==REAL3(0,0,0)) return 0; + n=normalize(n); + bplanes.Add(Plane(n,-dot(n,verts[maxdir(verts,verts_count,n)]))); + } + } + for(i=0;imaxdot_minang) + { + // somebody has to die, keep the biggest triangle + if( area2(verts[(*ti)[0]],verts[(*ti)[1]],verts[(*ti)[2]]) < area2(verts[(*tj)[0]],verts[(*tj)[1]],verts[(*tj)[2]])) + { + delete tris[i]; + } + else + { + delete tris[j]; + } + } + } + for(i=0;imaxdot_minang) break; + } + if(j==planes.count) + { + planes.Add(bplanes[i]); + } + } + for(i=0;ivertices[0] = REAL3(0,0,0); + convex->vertices[1] = REAL3(0,0,1); + convex->vertices[2] = REAL3(0,1,0); + convex->vertices[3] = REAL3(0,1,1); + convex->vertices[4] = REAL3(1,0,0); + convex->vertices[5] = REAL3(1,0,1); + convex->vertices[6] = REAL3(1,1,0); + convex->vertices[7] = REAL3(1,1,1); + + convex->facets[0] = Plane(REAL3(-1,0,0),0); + convex->facets[1] = Plane(REAL3(1,0,0),-1); + convex->facets[2] = Plane(REAL3(0,-1,0),0); + convex->facets[3] = Plane(REAL3(0,1,0),-1); + convex->facets[4] = Plane(REAL3(0,0,-1),0); + convex->facets[5] = Plane(REAL3(0,0,1),-1); + + convex->edges[0 ] = HalfEdge(11,0,0); + convex->edges[1 ] = HalfEdge(23,1,0); + convex->edges[2 ] = HalfEdge(15,3,0); + convex->edges[3 ] = HalfEdge(16,2,0); + + convex->edges[4 ] = HalfEdge(13,6,1); + convex->edges[5 ] = HalfEdge(21,7,1); + convex->edges[6 ] = HalfEdge( 9,5,1); + convex->edges[7 ] = HalfEdge(18,4,1); + + convex->edges[8 ] = HalfEdge(19,0,2); + convex->edges[9 ] = HalfEdge( 6,4,2); + convex->edges[10] = HalfEdge(20,5,2); + convex->edges[11] = HalfEdge( 0,1,2); + + convex->edges[12] = HalfEdge(22,3,3); + convex->edges[13] = HalfEdge( 4,7,3); + convex->edges[14] = HalfEdge(17,6,3); + convex->edges[15] = HalfEdge( 2,2,3); + + convex->edges[16] = HalfEdge( 3,0,4); + convex->edges[17] = HalfEdge(14,2,4); + convex->edges[18] = HalfEdge( 7,6,4); + convex->edges[19] = HalfEdge( 8,4,4); + + convex->edges[20] = HalfEdge(10,1,5); + convex->edges[21] = HalfEdge( 5,5,5); + convex->edges[22] = HalfEdge(12,7,5); + convex->edges[23] = HalfEdge( 1,3,5); + + + return convex; +} + +ConvexH *ConvexHMakeCube(const REAL3 &bmin, const REAL3 &bmax) +{ + ConvexH *convex = test_cube(); + convex->vertices[0] = REAL3(bmin.x,bmin.y,bmin.z); + convex->vertices[1] = REAL3(bmin.x,bmin.y,bmax.z); + convex->vertices[2] = REAL3(bmin.x,bmax.y,bmin.z); + convex->vertices[3] = REAL3(bmin.x,bmax.y,bmax.z); + convex->vertices[4] = REAL3(bmax.x,bmin.y,bmin.z); + convex->vertices[5] = REAL3(bmax.x,bmin.y,bmax.z); + convex->vertices[6] = REAL3(bmax.x,bmax.y,bmin.z); + convex->vertices[7] = REAL3(bmax.x,bmax.y,bmax.z); + + convex->facets[0] = Plane(REAL3(-1,0,0), bmin.x); + convex->facets[1] = Plane(REAL3(1,0,0), -bmax.x); + convex->facets[2] = Plane(REAL3(0,-1,0), bmin.y); + convex->facets[3] = Plane(REAL3(0,1,0), -bmax.y); + convex->facets[4] = Plane(REAL3(0,0,-1), bmin.z); + convex->facets[5] = Plane(REAL3(0,0,1), -bmax.z); + return convex; +} + + +static NxI32 overhull(Plane *planes,NxI32 planes_count,float3 *verts, NxI32 verts_count,NxI32 maxplanes, + float3 *&verts_out, NxI32 &verts_count_out, NxI32 *&faces_out, NxI32 &faces_count_out ,NxF32 inflate) +{ + NxI32 i,j; + if(verts_count <4) return NULL; + maxplanes = Min(maxplanes,planes_count); + float3 bmin(verts[0]),bmax(verts[0]); + for(i=0;i maxdot_minang) + { + (*((j%2)?&bmax:&bmin)) += n * (diameter*0.5f); + break; + } + } + } + ConvexH *c = ConvexHMakeCube(REAL3(bmin),REAL3(bmax)); + NxI32 k; + while(maxplanes-- && (k=candidateplane(planes,planes_count,c,epsilon))>=0) + { + ConvexH *tmp = c; + c = ConvexHCrop(*tmp,planes[k]); + if(c==NULL) {c=tmp; break;} // might want to debug this case better!!! + if(!AssertIntact(*c)) {c=tmp; break;} // might want to debug this case better too!!! + delete tmp; + } + + assert(AssertIntact(*c)); + //return c; + faces_out = (NxI32*)MEMALLOC_MALLOC(sizeof(NxI32)*(1+c->facets.count+c->edges.count)); // new NxI32[1+c->facets.count+c->edges.count]; + faces_count_out=0; + i=0; + faces_out[faces_count_out++]=-1; + k=0; + while(iedges.count) + { + j=1; + while(j+iedges.count && c->edges[i].p==c->edges[i+j].p) { j++; } + faces_out[faces_count_out++]=j; + while(j--) + { + faces_out[faces_count_out++] = c->edges[i].v; + i++; + } + k++; + } + faces_out[0]=k; // number of faces. + assert(k==c->facets.count); + assert(faces_count_out == 1+c->facets.count+c->edges.count); + verts_out = c->vertices.element; // new float3[c->vertices.count]; + verts_count_out = c->vertices.count; + for(i=0;ivertices.count;i++) + { + verts_out[i] = float3(c->vertices[i]); + } + c->vertices.count=c->vertices.array_size=0; c->vertices.element=NULL; + delete c; + return 1; +} + +static NxI32 overhullv(float3 *verts, NxI32 verts_count,NxI32 maxplanes, + float3 *&verts_out, NxI32 &verts_count_out, NxI32 *&faces_out, NxI32 &faces_count_out ,NxF32 inflate,NxF32 bevangle,NxI32 vlimit) +{ + if(!verts_count) return 0; + extern NxI32 calchullpbev(float3 *verts,NxI32 verts_count,NxI32 vlimit, Array &planes,NxF32 bevangle) ; + Array planes; + NxI32 rc=calchullpbev(verts,verts_count,vlimit,planes,bevangle) ; + if(!rc) return 0; + return overhull(planes.element,planes.count,verts,verts_count,maxplanes,verts_out,verts_count_out,faces_out,faces_count_out,inflate); +} + + +//***************************************************** +//***************************************************** + + +bool ComputeHull(NxU32 vcount,const NxF32 *vertices,PHullResult &result,NxU32 vlimit,NxF32 inflate) +{ + + NxI32 index_count; + NxI32 *faces; + float3 *verts_out; + NxI32 verts_count_out; + + if(inflate==0.0f) + { + NxI32 *tris_out; + NxI32 tris_count; + NxI32 ret = calchull( (float3 *) vertices, (NxI32) vcount, tris_out, tris_count, vlimit ); + if(!ret) return false; + result.mIndexCount = (NxU32) (tris_count*3); + result.mFaceCount = (NxU32) tris_count; + result.mVertices = (NxF32*) vertices; + result.mVcount = (NxU32) vcount; + result.mIndices = (NxU32 *) tris_out; + return true; + } + + NxI32 ret = overhullv((float3*)vertices,vcount,35,verts_out,verts_count_out,faces,index_count,inflate,120.0f,vlimit); + if(!ret) { + tris.SetSize(0); //have to set the size to 0 in order to protect from a "pure virtual function call" problem + return false; + } + + Array tris; + NxI32 n=faces[0]; + NxI32 k=1; + for(NxI32 i=0;i bmax[j] ) bmax[j] = p[j]; + } + } + } + + NxF32 dx = bmax[0] - bmin[0]; + NxF32 dy = bmax[1] - bmin[1]; + NxF32 dz = bmax[2] - bmin[2]; + + NxF32 center[3]; + + center[0] = dx*0.5f + bmin[0]; + center[1] = dy*0.5f + bmin[1]; + center[2] = dz*0.5f + bmin[2]; + + if ( dx < EPSILON || dy < EPSILON || dz < EPSILON || svcount < 3 ) + { + + NxF32 len = FLT_MAX; + + if ( dx > EPSILON && dx < len ) len = dx; + if ( dy > EPSILON && dy < len ) len = dy; + if ( dz > EPSILON && dz < len ) len = dz; + + if ( len == FLT_MAX ) + { + dx = dy = dz = 0.01f; // one centimeter + } + else + { + if ( dx < EPSILON ) dx = len * 0.05f; // 1/5th the shortest non-zero edge. + if ( dy < EPSILON ) dy = len * 0.05f; + if ( dz < EPSILON ) dz = len * 0.05f; + } + + NxF32 x1 = center[0] - dx; + NxF32 x2 = center[0] + dx; + + NxF32 y1 = center[1] - dy; + NxF32 y2 = center[1] + dy; + + NxF32 z1 = center[2] - dz; + NxF32 z2 = center[2] + dz; + + AddPoint(vcount,vertices,x1,y1,z1); + AddPoint(vcount,vertices,x2,y1,z1); + AddPoint(vcount,vertices,x2,y2,z1); + AddPoint(vcount,vertices,x1,y2,z1); + AddPoint(vcount,vertices,x1,y1,z2); + AddPoint(vcount,vertices,x2,y1,z2); + AddPoint(vcount,vertices,x2,y2,z2); + AddPoint(vcount,vertices,x1,y2,z2); + + return true; // return cube + + + } + else + { + if ( scale ) + { + scale[0] = dx; + scale[1] = dy; + scale[2] = dz; + + recip[0] = 1 / dx; + recip[1] = 1 / dy; + recip[2] = 1 / dz; + + center[0]*=recip[0]; + center[1]*=recip[1]; + center[2]*=recip[2]; + + } + + } + + + + vtx = (const char *) svertices; + + for (NxU32 i=0; i dist2 ) + { + v[0] = px; + v[1] = py; + v[2] = pz; + } + + break; + } + } + + if ( j == vcount ) + { + NxF32 *dest = &vertices[vcount*3]; + dest[0] = px; + dest[1] = py; + dest[2] = pz; + vcount++; + } + } + } + + // ok..now make sure we didn't prune so many vertices it is now invalid. + { + NxF32 bmin[3] = { FLT_MAX, FLT_MAX, FLT_MAX }; + NxF32 bmax[3] = { -FLT_MAX, -FLT_MAX, -FLT_MAX }; + + for (NxU32 i=0; i bmax[j] ) bmax[j] = p[j]; + } + } + + NxF32 dx = bmax[0] - bmin[0]; + NxF32 dy = bmax[1] - bmin[1]; + NxF32 dz = bmax[2] - bmin[2]; + + if ( dx < EPSILON || dy < EPSILON || dz < EPSILON || vcount < 3) + { + NxF32 cx = dx*0.5f + bmin[0]; + NxF32 cy = dy*0.5f + bmin[1]; + NxF32 cz = dz*0.5f + bmin[2]; + + NxF32 len = FLT_MAX; + + if ( dx >= EPSILON && dx < len ) len = dx; + if ( dy >= EPSILON && dy < len ) len = dy; + if ( dz >= EPSILON && dz < len ) len = dz; + + if ( len == FLT_MAX ) + { + dx = dy = dz = 0.01f; // one centimeter + } + else + { + if ( dx < EPSILON ) dx = len * 0.05f; // 1/5th the shortest non-zero edge. + if ( dy < EPSILON ) dy = len * 0.05f; + if ( dz < EPSILON ) dz = len * 0.05f; + } + + NxF32 x1 = cx - dx; + NxF32 x2 = cx + dx; + + NxF32 y1 = cy - dy; + NxF32 y2 = cy + dy; + + NxF32 z1 = cz - dz; + NxF32 z2 = cz + dz; + + vcount = 0; // add box + + AddPoint(vcount,vertices,x1,y1,z1); + AddPoint(vcount,vertices,x2,y1,z1); + AddPoint(vcount,vertices,x2,y2,z1); + AddPoint(vcount,vertices,x1,y2,z1); + AddPoint(vcount,vertices,x1,y1,z2); + AddPoint(vcount,vertices,x2,y1,z2); + AddPoint(vcount,vertices,x2,y2,z2); + AddPoint(vcount,vertices,x1,y2,z2); + + return true; + } + } + + return true; +} + +void HullLibrary::BringOutYourDead(const NxF32 *verts,NxU32 vcount, NxF32 *overts,NxU32 &ocount,NxU32 *indices,NxU32 indexcount) +{ + NxU32 *used = (NxU32 *)MEMALLOC_MALLOC(sizeof(NxU32)*vcount); + memset(used,0,sizeof(NxU32)*vcount); + + ocount = 0; + + for (NxU32 i=0; iConvexHullTriangle(v3,v2,v1); +} + +//================================================================================== +NxF32 HullLibrary::ComputeNormal(NxF32 *n,const NxF32 *A,const NxF32 *B,const NxF32 *C) +{ + NxF32 vx,vy,vz,wx,wy,wz,vw_x,vw_y,vw_z,mag; + + vx = (B[0] - C[0]); + vy = (B[1] - C[1]); + vz = (B[2] - C[2]); + + wx = (A[0] - B[0]); + wy = (A[1] - B[1]); + wz = (A[2] - B[2]); + + vw_x = vy * wz - vz * wy; + vw_y = vz * wx - vx * wz; + vw_z = vx * wy - vy * wx; + + mag = sqrtf((vw_x * vw_x) + (vw_y * vw_y) + (vw_z * vw_z)); + + if ( mag < 0.000001f ) + { + mag = 0; + } + else + { + mag = 1.0f/mag; + } + + n[0] = vw_x * mag; + n[1] = vw_y * mag; + n[2] = vw_z * mag; + + return mag; +} + +}; // End of namespace diff --git a/Engine/lib/convexDecomp/NvStanHull.h b/Engine/lib/convexDecomp/NvStanHull.h new file mode 100644 index 000000000..7890d05bf --- /dev/null +++ b/Engine/lib/convexDecomp/NvStanHull.h @@ -0,0 +1,201 @@ +#ifndef NV_STAN_HULL_H + +#define NV_STAN_HULL_H + +/* + +NvStanHull.h : A convex hull generator written by Stan Melax + +*/ + + +/*! +** +** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com +** +** Portions of this source has been released with the PhysXViewer application, as well as +** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. +** +** If you find this code useful or you are feeling particularily generous I would +** ask that you please go to http://www.amillionpixels.us and make a donation +** to Troy DeMolay. +** +** DeMolay is a youth group for young men between the ages of 12 and 21. +** It teaches strong moral principles, as well as leadership skills and +** public speaking. The donations page uses the 'pay for pixels' paradigm +** where, in this case, a pixel is only a single penny. Donations can be +** made for as small as $4 or as high as a $100 block. Each person who donates +** will get a link to their own site as well as acknowledgement on the +** donations blog located here http://www.amillionpixels.blogspot.com/ +** +** If you wish to contact me you can use the following methods: +** +** Skype ID: jratcliff63367 +** Yahoo: jratcliff63367 +** AOL: jratcliff1961 +** email: jratcliffscarab@gmail.com +** +** +** The MIT license: +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is furnished +** to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. + +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include "NvUserMemAlloc.h" + +namespace CONVEX_DECOMPOSITION +{ + +class HullResult +{ +public: + HullResult(void) + { + mPolygons = true; + mNumOutputVertices = 0; + mOutputVertices = 0; + mNumFaces = 0; + mNumIndices = 0; + mIndices = 0; + } + bool mPolygons; // true if indices represents polygons, false indices are triangles + NxU32 mNumOutputVertices; // number of vertices in the output hull + NxF32 *mOutputVertices; // array of vertices, 3 floats each x,y,z + NxU32 mNumFaces; // the number of faces produced + NxU32 mNumIndices; // the total number of indices + NxU32 *mIndices; // pointer to indices. + +// If triangles, then indices are array indexes into the vertex list. +// If polygons, indices are in the form (number of points in face) (p1, p2, p3, ..) etc.. +}; + +enum HullFlag +{ + QF_TRIANGLES = (1<<0), // report results as triangles, not polygons. + QF_REVERSE_ORDER = (1<<1), // reverse order of the triangle indices. + QF_SKIN_WIDTH = (1<<2), // extrude hull based on this skin width + QF_DEFAULT = (QF_TRIANGLES | QF_SKIN_WIDTH) +}; + + +class HullDesc +{ +public: + HullDesc(void) + { + mFlags = QF_DEFAULT; + mVcount = 0; + mVertices = 0; + mVertexStride = 0; + mNormalEpsilon = 0.001f; + mMaxVertices = 4096; // maximum number of points to be considered for a convex hull. + mSkinWidth = 0.01f; // default is one centimeter + }; + + HullDesc(HullFlag flag, + NxU32 vcount, + const NxF32 *vertices, + NxU32 stride) + { + mFlags = flag; + mVcount = vcount; + mVertices = vertices; + mVertexStride = stride; + mNormalEpsilon = 0.001f; + mMaxVertices = 4096; + mSkinWidth = 0.01f; // default is one centimeter + } + + bool HasHullFlag(HullFlag flag) const + { + if ( mFlags & flag ) return true; + return false; + } + + void SetHullFlag(HullFlag flag) + { + mFlags|=flag; + } + + void ClearHullFlag(HullFlag flag) + { + mFlags&=~flag; + } + + NxU32 mFlags; // flags to use when generating the convex hull. + NxU32 mVcount; // number of vertices in the input point cloud + const NxF32 *mVertices; // the array of vertices. + NxU32 mVertexStride; // the stride of each vertex, in bytes. + NxF32 mNormalEpsilon; // the epsilon for removing duplicates. This is a normalized value, if normalized bit is on. + NxF32 mSkinWidth; + NxU32 mMaxVertices; // maximum number of vertices to be considered for the hull! +}; + +enum HullError +{ + QE_OK, // success! + QE_FAIL, // failed. + QE_NOT_READY, +}; + +// This class is used when converting a convex hull into a triangle mesh. +class ConvexHullVertex +{ +public: + NxF32 mPos[3]; + NxF32 mNormal[3]; + NxF32 mTexel[2]; +}; + +// A virtual interface to receive the triangles from the convex hull. +class ConvexHullTriangleInterface +{ +public: + virtual void ConvexHullTriangle(const ConvexHullVertex &v1,const ConvexHullVertex &v2,const ConvexHullVertex &v3) = 0; +}; + + +class HullLibrary +{ +public: + + HullError CreateConvexHull(const HullDesc &desc, // describes the input request + HullResult &result); // contains the resulst + + HullError ReleaseResult(HullResult &result); // release memory allocated for this result, we are done with it. + + HullError CreateTriangleMesh(HullResult &answer,ConvexHullTriangleInterface *iface); +private: + NxF32 ComputeNormal(NxF32 *n,const NxF32 *A,const NxF32 *B,const NxF32 *C); + void AddConvexTriangle(ConvexHullTriangleInterface *callback,const NxF32 *p1,const NxF32 *p2,const NxF32 *p3); + + void BringOutYourDead(const NxF32 *verts,NxU32 vcount, NxF32 *overts,NxU32 &ocount,NxU32 *indices,NxU32 indexcount); + + bool CleanupVertices(NxU32 svcount, + const NxF32 *svertices, + NxU32 stride, + NxU32 &vcount, // output number of vertices + NxF32 *vertices, // location to store the results. + NxF32 normalepsilon, + NxF32 *scale); +}; + +}; // end of namespace + +#endif diff --git a/Engine/lib/convexDecomp/NvThreadConfig.cpp b/Engine/lib/convexDecomp/NvThreadConfig.cpp new file mode 100644 index 000000000..af6f2bdda --- /dev/null +++ b/Engine/lib/convexDecomp/NvThreadConfig.cpp @@ -0,0 +1,481 @@ +/* + +NvThreadConfig.cpp : A simple wrapper class to define threading and mutex locks. + +*/ +/*! +** +** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com +** +** Portions of this source has been released with the PhysXViewer application, as well as +** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. +** +** If you find this code useful or you are feeling particularily generous I would +** ask that you please go to http://www.amillionpixels.us and make a donation +** to Troy DeMolay. +** +** DeMolay is a youth group for young men between the ages of 12 and 21. +** It teaches strong moral principles, as well as leadership skills and +** public speaking. The donations page uses the 'pay for pixels' paradigm +** where, in this case, a pixel is only a single penny. Donations can be +** made for as small as $4 or as high as a $100 block. Each person who donates +** will get a link to their own site as well as acknowledgement on the +** donations blog located here http://www.amillionpixels.blogspot.com/ +** +** If you wish to contact me you can use the following methods: +** +** Skype ID: jratcliff63367 +** Yahoo: jratcliff63367 +** AOL: jratcliff1961 +** email: jratcliffscarab@gmail.com +** +** +** The MIT license: +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is furnished +** to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. + +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ +#include +#include "NvThreadConfig.h" + +#if defined(WIN32) + +#define _WIN32_WINNT 0x400 +#include + +#pragma comment(lib,"winmm.lib") + +// #ifndef _WIN32_WINNT + +// #endif +// #include +//#include +#endif + +#if defined(_XBOX) + #include +#endif + +#if defined(__linux__) || defined( __APPLE__ ) + //#include + #include + #include + #include + #define __stdcall +#endif + +#if defined( __APPLE__ ) + #include +#endif + +#if defined(__APPLE__) || defined(__linux__) + #include +#endif + +#if defined( __APPLE__ ) + #define PTHREAD_MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE +#endif + + +#ifdef NDEBUG +#define VERIFY( x ) (x) +#else +#define VERIFY( x ) assert((x)) +#endif + +namespace CONVEX_DECOMPOSITION +{ + +NxU32 tc_timeGetTime(void) +{ + #if defined(__linux__) + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return ts.tv_sec * 1000 + ts.tv_nsec / 1000000; + #elif defined( __APPLE__ ) + struct timeval tp; + gettimeofday(&tp, (struct timezone *)0); + return tp.tv_sec * 1000 + tp.tv_usec / 1000; + #elif defined( _XBOX ) + return GetTickCount(); + #else + return timeGetTime(); + #endif +} + +void tc_sleep(NxU32 ms) +{ + #if defined(__linux__) || defined( __APPLE__ ) + usleep(ms * 1000); + #else + Sleep(ms); + #endif +} + +void tc_spinloop() +{ + #ifdef __linux__ + asm ( "pause" ); + #elif defined( _XBOX ) + // Pause would do nothing on the Xbox. Threads are not scheduled. + #else + __asm { pause }; + #endif +} + +void tc_interlockedExchange(void *dest, const int64_t exchange) +{ + #if defined( __linux__ ) || defined( __APPLE__ ) + // not working + assert(false); + //__sync_lock_test_and_set((int64_t*)dest, exchange); + #elif defined( _XBOX ) + InterlockedExchange((volatile LONG *)dest, exchange); + #else + __asm + { + mov ebx, dword ptr [exchange] + mov ecx, dword ptr [exchange + 4] + mov edi, dest + mov eax, dword ptr [edi] + mov edx, dword ptr [edi + 4] + jmp start + retry: + pause + start: + lock cmpxchg8b [edi] + jnz retry + }; + #endif +} + +NxI32 tc_interlockedCompareExchange(void *dest, NxI32 exchange, NxI32 compare) +{ + #if defined( __linux__ ) || defined( __APPLE__ ) + // not working + assert(false); + return 0; + //return __sync_val_compare_and_swap((uintptr_t*)dest, exchange, compare); + //return __sync_bool_compare_and_swap((uintptr_t*)dest, exchange, compare); + #elif defined( _XBOX ) + return InterlockedCompareExchange((volatile LONG *)dest, exchange, compare); + #else + char _ret; + // + __asm + { + mov edx, [dest] + mov eax, [compare] + mov ecx, [exchange] + + lock cmpxchg [edx], ecx + + setz al + mov byte ptr [_ret], al + } + // + return _ret; + #endif +} + +NxI32 tc_interlockedCompareExchange(void *dest, const NxI32 exchange1, const NxI32 exchange2, const NxI32 compare1, const NxI32 compare2) +{ + #if defined( __linux__ ) || defined( __APPLE__ ) + // not working + assert(false); + return 0; + //uint64_t exchange = ((uint64_t)exchange1 << 32) | (uint64_t)exchange2; + //uint64_t compare = ((uint64_t)compare1 << 32) | (uint64_t)compare2; + //return __sync_bool_compare_and_swap((int64_t*)dest, exchange, compare); + #elif defined( _XBOX ) + assert(false); + return 0; + #else + char _ret; + // + __asm + { + mov ebx, [exchange1] + mov ecx, [exchange2] + mov edi, [dest] + mov eax, [compare1] + mov edx, [compare2] + lock cmpxchg8b [edi] + setz al + mov byte ptr [_ret], al + } + // + return _ret; + #endif +} + +class MyThreadMutex : public ThreadMutex +{ +public: + MyThreadMutex(void) + { + #if defined(WIN32) || defined(_XBOX) + InitializeCriticalSection(&m_Mutex); + #elif defined(__APPLE__) || defined(__linux__) + pthread_mutexattr_t mutexAttr; // Mutex Attribute + VERIFY( pthread_mutexattr_init(&mutexAttr) == 0 ); + VERIFY( pthread_mutexattr_settype(&mutexAttr, PTHREAD_MUTEX_RECURSIVE_NP) == 0 ); + VERIFY( pthread_mutex_init(&m_Mutex, &mutexAttr) == 0 ); + VERIFY( pthread_mutexattr_destroy(&mutexAttr) == 0 ); + #endif + } + + ~MyThreadMutex(void) + { + #if defined(WIN32) || defined(_XBOX) + DeleteCriticalSection(&m_Mutex); + #elif defined(__APPLE__) || defined(__linux__) + VERIFY( pthread_mutex_destroy(&m_Mutex) == 0 ); + #endif + } + + void lock(void) + { + #if defined(WIN32) || defined(_XBOX) + EnterCriticalSection(&m_Mutex); + #elif defined(__APPLE__) || defined(__linux__) + VERIFY( pthread_mutex_lock(&m_Mutex) == 0 ); + #endif + } + + bool tryLock(void) + { + #if defined(WIN32) || defined(_XBOX) + bool bRet = false; + //assert(("TryEnterCriticalSection seems to not work on XP???", 0)); + bRet = TryEnterCriticalSection(&m_Mutex) ? true : false; + return bRet; + #elif defined(__APPLE__) || defined(__linux__) + NxI32 result = pthread_mutex_trylock(&m_Mutex); + return (result == 0); + #endif + } + + void unlock(void) + { + #if defined(WIN32) || defined(_XBOX) + LeaveCriticalSection(&m_Mutex); + #elif defined(__APPLE__) || defined(__linux__) + VERIFY( pthread_mutex_unlock(&m_Mutex) == 0 ); + #endif + } + +private: + #if defined(WIN32) || defined(_XBOX) + CRITICAL_SECTION m_Mutex; + #elif defined(__APPLE__) || defined(__linux__) + pthread_mutex_t m_Mutex; + #endif +}; + +ThreadMutex * tc_createThreadMutex(void) +{ + MyThreadMutex *m = new MyThreadMutex; + return static_cast< ThreadMutex *>(m); +} + +void tc_releaseThreadMutex(ThreadMutex *tm) +{ + MyThreadMutex *m = static_cast< MyThreadMutex *>(tm); + delete m; +} + +#if defined(WIN32) || defined(_XBOX) +static unsigned long __stdcall _ThreadWorkerFunc(LPVOID arg); +#elif defined(__APPLE__) || defined(__linux__) +static void* _ThreadWorkerFunc(void* arg); +#endif + +class MyThread : public Thread +{ +public: + MyThread(ThreadInterface *iface) + { + mInterface = iface; + #if defined(WIN32) || defined(_XBOX) + mThread = CreateThread(0, 0, _ThreadWorkerFunc, this, 0, 0); + #elif defined(__APPLE__) || defined(__linux__) + VERIFY( pthread_create(&mThread, NULL, _ThreadWorkerFunc, this) == 0 ); + #endif + } + + ~MyThread(void) + { + #if defined(WIN32) || defined(_XBOX) + if ( mThread ) + { + CloseHandle(mThread); + mThread = 0; + } + #endif + } + + void onJobExecute(void) + { + mInterface->threadMain(); + } + +private: + ThreadInterface *mInterface; + #if defined(WIN32) || defined(_XBOX) + HANDLE mThread; + #elif defined(__APPLE__) || defined(__linux__) + pthread_t mThread; + #endif +}; + + +Thread * tc_createThread(ThreadInterface *tinterface) +{ + MyThread *m = new MyThread(tinterface); + return static_cast< Thread *>(m); +} + +void tc_releaseThread(Thread *t) +{ + MyThread *m = static_cast(t); + delete m; +} + +#if defined(WIN32) || defined(_XBOX) +static unsigned long __stdcall _ThreadWorkerFunc(LPVOID arg) +#elif defined(__APPLE__) || defined(__linux__) +static void* _ThreadWorkerFunc(void* arg) +#endif +{ + MyThread *worker = (MyThread *) arg; + worker->onJobExecute(); + return 0; +} + + +class MyThreadEvent : public ThreadEvent +{ +public: + MyThreadEvent(void) + { + #if defined(WIN32) || defined(_XBOX) + mEvent = ::CreateEventA(NULL,TRUE,TRUE,"ThreadEvent"); + #elif defined(__APPLE__) || defined(__linux__) + pthread_mutexattr_t mutexAttr; // Mutex Attribute + VERIFY( pthread_mutexattr_init(&mutexAttr) == 0 ); + VERIFY( pthread_mutexattr_settype(&mutexAttr, PTHREAD_MUTEX_RECURSIVE_NP) == 0 ); + VERIFY( pthread_mutex_init(&mEventMutex, &mutexAttr) == 0 ); + VERIFY( pthread_mutexattr_destroy(&mutexAttr) == 0 ); + VERIFY( pthread_cond_init(&mEvent, NULL) == 0 ); + #endif + } + + ~MyThreadEvent(void) + { + #if defined(WIN32) || defined(_XBOX) + if ( mEvent ) + { + ::CloseHandle(mEvent); + } + #elif defined(__APPLE__) || defined(__linux__) + VERIFY( pthread_cond_destroy(&mEvent) == 0 ); + VERIFY( pthread_mutex_destroy(&mEventMutex) == 0 ); + #endif + } + + virtual void setEvent(void) // signal the event + { + #if defined(WIN32) || defined(_XBOX) + if ( mEvent ) + { + ::SetEvent(mEvent); + } + #elif defined(__APPLE__) || defined(__linux__) + VERIFY( pthread_mutex_lock(&mEventMutex) == 0 ); + VERIFY( pthread_cond_signal(&mEvent) == 0 ); + VERIFY( pthread_mutex_unlock(&mEventMutex) == 0 ); + #endif + } + + void resetEvent(void) + { + #if defined(WIN32) || defined(_XBOX) + if ( mEvent ) + { + ::ResetEvent(mEvent); + } + #endif + } + + virtual void waitForSingleObject(NxU32 ms) + { + #if defined(WIN32) || defined(_XBOX) + if ( mEvent ) + { + ::WaitForSingleObject(mEvent,ms); + } + #elif defined(__APPLE__) || defined(__linux__) + VERIFY( pthread_mutex_lock(&mEventMutex) == 0 ); + if (ms == 0xffffffff) + { + VERIFY( pthread_cond_wait(&mEvent, &mEventMutex) == 0 ); + } + else + { + struct timespec ts; + #ifdef __APPLE__ + struct timeval tp; + gettimeofday(&tp, (struct timezone *)0); + ts.tv_nsec = tp.tv_usec * 1000; + ts.tv_sec = tp.tv_sec; + #else + clock_gettime(CLOCK_REALTIME, &ts); + #endif + ts.tv_nsec += ms * 1000000; + ts.tv_sec += ts.tv_nsec / 1000000000; + ts.tv_nsec %= 1000000000; + NxI32 result = pthread_cond_timedwait(&mEvent, &mEventMutex, &ts); + assert(result == 0 || result == ETIMEDOUT); + } + VERIFY( pthread_mutex_unlock(&mEventMutex) == 0 ); + #endif + } + +private: + #if defined(WIN32) || defined(_XBOX) + HANDLE mEvent; + #elif defined(__APPLE__) || defined(__linux__) + pthread_mutex_t mEventMutex; + pthread_cond_t mEvent; + #endif +}; + +ThreadEvent * tc_createThreadEvent(void) +{ + MyThreadEvent *m = new MyThreadEvent; + return static_cast(m); +} + +void tc_releaseThreadEvent(ThreadEvent *t) +{ + MyThreadEvent *m = static_cast< MyThreadEvent *>(t); + delete m; +} + +}; // end of namespace diff --git a/Engine/lib/convexDecomp/NvThreadConfig.h b/Engine/lib/convexDecomp/NvThreadConfig.h new file mode 100644 index 000000000..1074c045c --- /dev/null +++ b/Engine/lib/convexDecomp/NvThreadConfig.h @@ -0,0 +1,119 @@ +#ifndef NV_THREAD_CONFIG_H + +#define NV_THREAD_CONFIG_H + +#include "NvUserMemAlloc.h" + +/* + +NvThreadConfig.h : A simple wrapper class to define threading and mutex locks. + +*/ + + +/*! +** +** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com +** +** Portions of this source has been released with the PhysXViewer application, as well as +** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. +** +** If you find this code useful or you are feeling particularily generous I would +** ask that you please go to http://www.amillionpixels.us and make a donation +** to Troy DeMolay. +** +** DeMolay is a youth group for young men between the ages of 12 and 21. +** It teaches strong moral principles, as well as leadership skills and +** public speaking. The donations page uses the 'pay for pixels' paradigm +** where, in this case, a pixel is only a single penny. Donations can be +** made for as small as $4 or as high as a $100 block. Each person who donates +** will get a link to their own site as well as acknowledgement on the +** donations blog located here http://www.amillionpixels.blogspot.com/ +** +** If you wish to contact me you can use the following methods: +** +** Skype ID: jratcliff63367 +** Yahoo: jratcliff63367 +** AOL: jratcliff1961 +** email: jratcliffscarab@gmail.com +** +** +** The MIT license: +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is furnished +** to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. + +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifdef _MSC_VER +typedef __int64 int64_t; +#else +#include +#endif + +namespace CONVEX_DECOMPOSITION +{ + +NxU32 tc_timeGetTime(void); +void tc_sleep(NxU32 ms); + +void tc_spinloop(); +void tc_interlockedExchange(void *dest, const int64_t exchange); +NxI32 tc_interlockedCompareExchange(void *dest, NxI32 exchange, NxI32 compare); +NxI32 tc_interlockedCompareExchange(void *dest, const NxI32 exchange1, const NxI32 exchange2, const NxI32 compare1, const NxI32 compare2); + +class ThreadMutex +{ +public: + virtual void lock(void) = 0; + virtual void unlock(void) = 0; + virtual bool tryLock(void) = 0; +}; + + +ThreadMutex * tc_createThreadMutex(void); +void tc_releaseThreadMutex(ThreadMutex *tm); + +class ThreadInterface +{ +public: + virtual void threadMain(void) = 0; +}; + +class Thread +{ +public: +}; + +Thread * tc_createThread(ThreadInterface *tinterface); +void tc_releaseThread(Thread *t); + +class ThreadEvent +{ +public: + virtual void setEvent(void) = 0; // signal the event + virtual void resetEvent(void) = 0; + virtual void waitForSingleObject(NxU32 ms) = 0; +}; + +ThreadEvent * tc_createThreadEvent(void); +void tc_releaseThreadEvent(ThreadEvent *t); + +}; // end of namespace + + +#endif diff --git a/Engine/lib/convexDecomp/NvUserMemAlloc.h b/Engine/lib/convexDecomp/NvUserMemAlloc.h new file mode 100644 index 000000000..14ae899d9 --- /dev/null +++ b/Engine/lib/convexDecomp/NvUserMemAlloc.h @@ -0,0 +1,81 @@ +#ifndef NV_USER_MEMALLOC_H + +#define NV_USER_MEMALLOC_H + +#include "NvSimpleTypes.h" + +/* + +NvUserMemAlloc.h : Modify these macros to change the default memory allocation behavior of the convex decomposition code. + +*/ + + +/*! +** +** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com +** +** Portions of this source has been released with the PhysXViewer application, as well as +** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. +** +** If you find this code useful or you are feeling particularily generous I would +** ask that you please go to http://www.amillionpixels.us and make a donation +** to Troy DeMolay. +** +** DeMolay is a youth group for young men between the ages of 12 and 21. +** It teaches strong moral principles, as well as leadership skills and +** public speaking. The donations page uses the 'pay for pixels' paradigm +** where, in this case, a pixel is only a single penny. Donations can be +** made for as small as $4 or as high as a $100 block. Each person who donates +** will get a link to their own site as well as acknowledgement on the +** donations blog located here http://www.amillionpixels.blogspot.com/ +** +** If you wish to contact me you can use the following methods: +** +** Skype ID: jratcliff63367 +** Yahoo: jratcliff63367 +** AOL: jratcliff1961 +** email: jratcliffscarab@gmail.com +** +** +** The MIT license: +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is furnished +** to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. + +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#ifndef MEMALLOC_NEW +#define MEMALLOC_NEW(x) new x +#define MEMALLOC_MALLOC(x) ::malloc(x) +#define MEMALLOC_FREE(x) ::free(x) +#define MEMALLOC_REALLOC(x,y) ::realloc(x,y) +#endif + +namespace CONVEX_DECOMPOSITION +{ + +class Memalloc +{ +public: +}; + +}; // end of namespace + + + +#endif diff --git a/Engine/lib/convexDecomp/readme.txt b/Engine/lib/convexDecomp/readme.txt new file mode 100644 index 000000000..679396aa3 --- /dev/null +++ b/Engine/lib/convexDecomp/readme.txt @@ -0,0 +1,38 @@ +The ConvexDecomposition library was written by John W. Ratcliff mailto:jratcliffscarab@gmail.com + +What is Convex Decomposition? + +Convex Decomposition is when you take an arbitrarily complex triangle mesh and sub-divide it into +a collection of discrete compound pieces (each represented as a convex hull) to approximate +the original shape of the objet. + +This is required since few physics engines can treat aribtrary triangle mesh objects as dynamic +objects. Even those engines which can handle this use case incurr a huge performance and memory +penalty to do so. + +By breaking a complex triangle mesh up into a discrete number of convex components you can greatly +improve performance for dynamic simulations. + +-------------------------------------------------------------------------------- + +This code is released under the MIT license. + +The code is functional but could use the following improvements: + +(1) The convex hull generator, originally written by Stan Melax, could use some major code cleanup. + +(2) The code to remove T-junctions appears to have a bug in it. This code was working fine before, + but I haven't had time to debug why it stopped working. + +(3) Island generation once the mesh has been split is currently disabled due to the fact that the + Remove Tjunctions functionality has a bug in it. + +(4) The code to perform a raycast against a triangle mesh does not currently use any acceleration + data structures. + +(5) When a split is performed, the surface that got split is not 'capped'. This causes a problem + if you use a high recursion depth on your convex decomposition. It will cause the object to + be modelled as if it had a hollow interior. A lot of work was done to solve this problem, but + it hasn't been integrated into this code drop yet. + + diff --git a/Engine/lib/convexDecomp/wavefront.cpp b/Engine/lib/convexDecomp/wavefront.cpp new file mode 100644 index 000000000..0d4b979b1 --- /dev/null +++ b/Engine/lib/convexDecomp/wavefront.cpp @@ -0,0 +1,852 @@ +/* + +wavefront.cpp : A very small code snippet to read a Wavefront OBJ file into memory. + +*/ + + +/*! +** +** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com +** +** Portions of this source has been released with the PhysXViewer application, as well as +** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. +** +** If you find this code useful or you are feeling particularily generous I would +** ask that you please go to http://www.amillionpixels.us and make a donation +** to Troy DeMolay. +** +** DeMolay is a youth group for young men between the ages of 12 and 21. +** It teaches strong moral principles, as well as leadership skills and +** public speaking. The donations page uses the 'pay for pixels' paradigm +** where, in this case, a pixel is only a single penny. Donations can be +** made for as small as $4 or as high as a $100 block. Each person who donates +** will get a link to their own site as well as acknowledgement on the +** donations blog located here http://www.amillionpixels.blogspot.com/ +** +** If you wish to contact me you can use the following methods: +** +** Skype ID: jratcliff63367 +** Yahoo: jratcliff63367 +** AOL: jratcliff1961 +** email: jratcliffscarab@gmail.com +** +** +** The MIT license: +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is furnished +** to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. + +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ +#ifndef __PPCGEKKO__ + +#include +#include +#include +#include +#include + +#include "wavefront.h" + +#include + +typedef std::vector< NxI32 > IntVector; +typedef std::vector< NxF32 > FloatVector; + +#pragma warning(disable:4996) + +namespace WAVEFRONT +{ + + +/*******************************************************************/ +/******************** InParser.h ********************************/ +/*******************************************************************/ +class InPlaceParserInterface +{ +public: + virtual NxI32 ParseLine(NxI32 lineno,NxI32 argc,const char **argv) =0; // return TRUE to continue parsing, return FALSE to abort parsing process +}; + +enum SeparatorType +{ + ST_DATA, // is data + ST_HARD, // is a hard separator + ST_SOFT, // is a soft separator + ST_EOS // is a comment symbol, and everything past this character should be ignored +}; + +class InPlaceParser +{ +public: + InPlaceParser(void) + { + Init(); + } + + InPlaceParser(char *data,NxI32 len) + { + Init(); + SetSourceData(data,len); + } + + InPlaceParser(const char *fname) + { + Init(); + SetFile(fname); + } + + ~InPlaceParser(void); + + void Init(void) + { + mQuoteChar = 34; + mData = 0; + mLen = 0; + mMyAlloc = false; + for (NxI32 i=0; i<256; i++) + { + mHard[i] = ST_DATA; + mHardString[i*2] = i; + mHardString[i*2+1] = 0; + } + mHard[0] = ST_EOS; + mHard[32] = ST_SOFT; + mHard[9] = ST_SOFT; + mHard[13] = ST_SOFT; + mHard[10] = ST_SOFT; + } + + void SetFile(const char *fname); // use this file as source data to parse. + + void SetSourceData(char *data,NxI32 len) + { + mData = data; + mLen = len; + mMyAlloc = false; + }; + + NxI32 Parse(InPlaceParserInterface *callback); // returns true if entire file was parsed, false if it aborted for some reason + + NxI32 ProcessLine(NxI32 lineno,char *line,InPlaceParserInterface *callback); + + const char ** GetArglist(char *source,NxI32 &count); // convert source string into an arg list, this is a destructive parse. + + void SetHardSeparator(char c) // add a hard separator + { + mHard[c] = ST_HARD; + } + + void SetHard(char c) // add a hard separator + { + mHard[c] = ST_HARD; + } + + + void SetCommentSymbol(char c) // comment character, treated as 'end of string' + { + mHard[c] = ST_EOS; + } + + void ClearHardSeparator(char c) + { + mHard[c] = ST_DATA; + } + + + void DefaultSymbols(void); // set up default symbols for hard seperator and comment symbol of the '#' character. + + bool EOS(char c) + { + if ( mHard[c] == ST_EOS ) + { + return true; + } + return false; + } + + void SetQuoteChar(char c) + { + mQuoteChar = c; + } + +private: + + + inline char * AddHard(NxI32 &argc,const char **argv,char *foo); + inline bool IsHard(char c); + inline char * SkipSpaces(char *foo); + inline bool IsWhiteSpace(char c); + inline bool IsNonSeparator(char c); // non seperator,neither hard nor soft + + bool mMyAlloc; // whether or not *I* allocated the buffer and am responsible for deleting it. + char *mData; // ascii data to parse. + NxI32 mLen; // length of data + SeparatorType mHard[256]; + char mHardString[256*2]; + char mQuoteChar; +}; + +/*******************************************************************/ +/******************** InParser.cpp ********************************/ +/*******************************************************************/ +void InPlaceParser::SetFile(const char *fname) +{ + if ( mMyAlloc ) + { + free(mData); + } + mData = 0; + mLen = 0; + mMyAlloc = false; + + FILE *fph = fopen(fname,"rb"); + if ( fph ) + { + fseek(fph,0L,SEEK_END); + mLen = ftell(fph); + fseek(fph,0L,SEEK_SET); + if ( mLen ) + { + mData = (char *) malloc(sizeof(char)*(mLen+1)); + size_t ok = fread(mData, mLen, 1, fph); + if ( !ok ) + { + free(mData); + mData = 0; + } + else + { + mData[mLen] = 0; // zero byte terminate end of file marker. + mMyAlloc = true; + } + } + fclose(fph); + } + +} + +InPlaceParser::~InPlaceParser(void) +{ + if ( mMyAlloc ) + { + free(mData); + } +} + +#define MAXARGS 512 + +bool InPlaceParser::IsHard(char c) +{ + return mHard[c] == ST_HARD; +} + +char * InPlaceParser::AddHard(NxI32 &argc,const char **argv,char *foo) +{ + while ( IsHard(*foo) ) + { + const char *hard = &mHardString[*foo*2]; + if ( argc < MAXARGS ) + { + argv[argc++] = hard; + } + foo++; + } + return foo; +} + +bool InPlaceParser::IsWhiteSpace(char c) +{ + return mHard[c] == ST_SOFT; +} + +char * InPlaceParser::SkipSpaces(char *foo) +{ + while ( !EOS(*foo) && IsWhiteSpace(*foo) ) foo++; + return foo; +} + +bool InPlaceParser::IsNonSeparator(char c) +{ + if ( !IsHard(c) && !IsWhiteSpace(c) && c != 0 ) return true; + return false; +} + + +NxI32 InPlaceParser::ProcessLine(NxI32 lineno,char *line,InPlaceParserInterface *callback) +{ + NxI32 ret = 0; + + const char *argv[MAXARGS]; + NxI32 argc = 0; + + char *foo = line; + + while ( !EOS(*foo) && argc < MAXARGS ) + { + + foo = SkipSpaces(foo); // skip any leading spaces + + if ( EOS(*foo) ) break; + + if ( *foo == mQuoteChar ) // if it is an open quote + { + foo++; + if ( argc < MAXARGS ) + { + argv[argc++] = foo; + } + while ( !EOS(*foo) && *foo != mQuoteChar ) foo++; + if ( !EOS(*foo) ) + { + *foo = 0; // replace close quote with zero byte EOS + foo++; + } + } + else + { + + foo = AddHard(argc,argv,foo); // add any hard separators, skip any spaces + + if ( IsNonSeparator(*foo) ) // add non-hard argument. + { + bool quote = false; + if ( *foo == mQuoteChar ) + { + foo++; + quote = true; + } + + if ( argc < MAXARGS ) + { + argv[argc++] = foo; + } + + if ( quote ) + { + while (*foo && *foo != mQuoteChar ) foo++; + if ( *foo ) *foo = 32; + } + + // continue..until we hit an eos .. + while ( !EOS(*foo) ) // until we hit EOS + { + if ( IsWhiteSpace(*foo) ) // if we hit a space, stomp a zero byte, and exit + { + *foo = 0; + foo++; + break; + } + else if ( IsHard(*foo) ) // if we hit a hard separator, stomp a zero byte and store the hard separator argument + { + const char *hard = &mHardString[*foo*2]; + *foo = 0; + if ( argc < MAXARGS ) + { + argv[argc++] = hard; + } + foo++; + break; + } + foo++; + } // end of while loop... + } + } + } + + if ( argc ) + { + ret = callback->ParseLine(lineno, argc, argv ); + } + + return ret; +} + +NxI32 InPlaceParser::Parse(InPlaceParserInterface *callback) // returns true if entire file was parsed, false if it aborted for some reason +{ + assert( callback ); + if ( !mData ) return 0; + + NxI32 ret = 0; + + NxI32 lineno = 0; + + char *foo = mData; + char *begin = foo; + + + while ( *foo ) + { + if ( *foo == 10 || *foo == 13 ) + { + lineno++; + *foo = 0; + + if ( *begin ) // if there is any data to parse at all... + { + NxI32 v = ProcessLine(lineno,begin,callback); + if ( v ) ret = v; + } + + foo++; + if ( *foo == 10 ) foo++; // skip line feed, if it is in the carraige-return line-feed format... + begin = foo; + } + else + { + foo++; + } + } + + lineno++; // lasst line. + + NxI32 v = ProcessLine(lineno,begin,callback); + if ( v ) ret = v; + return ret; +} + + +void InPlaceParser::DefaultSymbols(void) +{ + SetHardSeparator(','); + SetHardSeparator('('); + SetHardSeparator(')'); + SetHardSeparator('='); + SetHardSeparator('['); + SetHardSeparator(']'); + SetHardSeparator('{'); + SetHardSeparator('}'); + SetCommentSymbol('#'); +} + + +const char ** InPlaceParser::GetArglist(char *line,NxI32 &count) // convert source string into an arg list, this is a destructive parse. +{ + const char **ret = 0; + + static const char *argv[MAXARGS]; + NxI32 argc = 0; + + char *foo = line; + + while ( !EOS(*foo) && argc < MAXARGS ) + { + + foo = SkipSpaces(foo); // skip any leading spaces + + if ( EOS(*foo) ) break; + + if ( *foo == mQuoteChar ) // if it is an open quote + { + foo++; + if ( argc < MAXARGS ) + { + argv[argc++] = foo; + } + while ( !EOS(*foo) && *foo != mQuoteChar ) foo++; + if ( !EOS(*foo) ) + { + *foo = 0; // replace close quote with zero byte EOS + foo++; + } + } + else + { + + foo = AddHard(argc,argv,foo); // add any hard separators, skip any spaces + + if ( IsNonSeparator(*foo) ) // add non-hard argument. + { + bool quote = false; + if ( *foo == mQuoteChar ) + { + foo++; + quote = true; + } + + if ( argc < MAXARGS ) + { + argv[argc++] = foo; + } + + if ( quote ) + { + while (*foo && *foo != mQuoteChar ) foo++; + if ( *foo ) *foo = 32; + } + + // continue..until we hit an eos .. + while ( !EOS(*foo) ) // until we hit EOS + { + if ( IsWhiteSpace(*foo) ) // if we hit a space, stomp a zero byte, and exit + { + *foo = 0; + foo++; + break; + } + else if ( IsHard(*foo) ) // if we hit a hard separator, stomp a zero byte and store the hard separator argument + { + const char *hard = &mHardString[*foo*2]; + *foo = 0; + if ( argc < MAXARGS ) + { + argv[argc++] = hard; + } + foo++; + break; + } + foo++; + } // end of while loop... + } + } + } + + count = argc; + if ( argc ) + { + ret = argv; + } + + return ret; +} + +/*******************************************************************/ +/******************** Geometry.h ********************************/ +/*******************************************************************/ + +class GeometryVertex +{ +public: + NxF32 mPos[3]; + NxF32 mNormal[3]; + NxF32 mTexel[2]; +}; + + +class GeometryInterface +{ +public: + + virtual void NodeTriangle(const GeometryVertex *v1,const GeometryVertex *v2,const GeometryVertex *v3, bool textured) + { + } + +}; + + +/*******************************************************************/ +/******************** Obj.h ********************************/ +/*******************************************************************/ + + +class OBJ : public InPlaceParserInterface +{ +public: + NxI32 LoadMesh(const char *fname,GeometryInterface *callback, bool textured); + NxI32 ParseLine(NxI32 lineno,NxI32 argc,const char **argv); // return TRUE to continue parsing, return FALSE to abort parsing process +private: + + void GetVertex(GeometryVertex &v,const char *face) const; + + FloatVector mVerts; + FloatVector mTexels; + FloatVector mNormals; + + bool mTextured; + + GeometryInterface *mCallback; +}; + + +/*******************************************************************/ +/******************** Obj.cpp ********************************/ +/*******************************************************************/ + +NxI32 OBJ::LoadMesh(const char *fname,GeometryInterface *iface, bool textured) +{ + mTextured = textured; + NxI32 ret = 0; + + mVerts.clear(); + mTexels.clear(); + mNormals.clear(); + + mCallback = iface; + + InPlaceParser ipp(fname); + + ipp.Parse(this); + +return ret; +} + +static const char * GetArg(const char **argv,NxI32 i,NxI32 argc) +{ + const char * ret = 0; + if ( i < argc ) ret = argv[i]; + return ret; +} + +void OBJ::GetVertex(GeometryVertex &v,const char *face) const +{ + v.mPos[0] = 0; + v.mPos[1] = 0; + v.mPos[2] = 0; + + v.mTexel[0] = 0; + v.mTexel[1] = 0; + + v.mNormal[0] = 0; + v.mNormal[1] = 1; + v.mNormal[2] = 0; + + NxI32 index = atoi( face )-1; + + const char *texel = strstr(face,"/"); + + if ( texel ) + { + NxI32 tindex = atoi( texel+1) - 1; + + if ( tindex >=0 && tindex < (NxI32)(mTexels.size()/2) ) + { + const NxF32 *t = &mTexels[tindex*2]; + + v.mTexel[0] = t[0]; + v.mTexel[1] = t[1]; + + } + + const char *normal = strstr(texel+1,"/"); + if ( normal ) + { + NxI32 nindex = atoi( normal+1 ) - 1; + + if (nindex >= 0 && nindex < (NxI32)(mNormals.size()/3) ) + { + const NxF32 *n = &mNormals[nindex*3]; + + v.mNormal[0] = n[0]; + v.mNormal[1] = n[1]; + v.mNormal[2] = n[2]; + } + } + } + + if ( index >= 0 && index < (NxI32)(mVerts.size()/3) ) + { + + const NxF32 *p = &mVerts[index*3]; + + v.mPos[0] = p[0]; + v.mPos[1] = p[1]; + v.mPos[2] = p[2]; + } + +} + +NxI32 OBJ::ParseLine(NxI32 lineno,NxI32 argc,const char **argv) // return TRUE to continue parsing, return FALSE to abort parsing process +{ + NxI32 ret = 0; + + if ( argc >= 1 ) + { + const char *foo = argv[0]; + if ( *foo != '#' ) + { + if ( _stricmp(argv[0],"v") == 0 && argc == 4 ) + { + NxF32 vx = (NxF32) atof( argv[1] ); + NxF32 vy = (NxF32) atof( argv[2] ); + NxF32 vz = (NxF32) atof( argv[3] ); + mVerts.push_back(vx); + mVerts.push_back(vy); + mVerts.push_back(vz); + } + else if ( _stricmp(argv[0],"vt") == 0 && (argc == 3 || argc == 4)) + { + // ignore 4rd component if present + NxF32 tx = (NxF32) atof( argv[1] ); + NxF32 ty = (NxF32) atof( argv[2] ); + mTexels.push_back(tx); + mTexels.push_back(ty); + } + else if ( _stricmp(argv[0],"vn") == 0 && argc == 4 ) + { + NxF32 normalx = (NxF32) atof(argv[1]); + NxF32 normaly = (NxF32) atof(argv[2]); + NxF32 normalz = (NxF32) atof(argv[3]); + mNormals.push_back(normalx); + mNormals.push_back(normaly); + mNormals.push_back(normalz); + } + else if ( _stricmp(argv[0],"f") == 0 && argc >= 4 ) + { + GeometryVertex v[32]; + + NxI32 vcount = argc-1; + + for (NxI32 i=1; iNodeTriangle(&v[0],&v[1],&v[2], mTextured); + + if ( vcount >=3 ) // do the fan + { + for (NxI32 i=2; i<(vcount-1); i++) + { + mCallback->NodeTriangle(&v[0],&v[i],&v[i+1], mTextured); + } + } + + } + } + } + + return ret; +} + + + + +class BuildMesh : public GeometryInterface +{ +public: + + NxI32 GetIndex(const NxF32 *p, const NxF32 *texCoord) + { + + NxI32 vcount = (NxI32)mVertices.size()/3; + + if(vcount>0) + { + //New MS STL library checks indices in debug build, so zero causes an assert if it is empty. + const NxF32 *v = &mVertices[0]; + const NxF32 *t = texCoord != NULL ? &mTexCoords[0] : NULL; + + for (NxI32 i=0; imPos, textured ? v1->mTexel : NULL) ); + mIndices.push_back( GetIndex(v2->mPos, textured ? v2->mTexel : NULL) ); + mIndices.push_back( GetIndex(v3->mPos, textured ? v3->mTexel : NULL) ); + } + + const FloatVector& GetVertices(void) const { return mVertices; }; + const FloatVector& GetTexCoords(void) const { return mTexCoords; }; + const IntVector& GetIndices(void) const { return mIndices; }; + +private: + FloatVector mVertices; + FloatVector mTexCoords; + IntVector mIndices; +}; + +}; + +using namespace WAVEFRONT; + +WavefrontObj::WavefrontObj(void) +{ + mVertexCount = 0; + mTriCount = 0; + mIndices = 0; + mVertices = NULL; + mTexCoords = NULL; +} + +WavefrontObj::~WavefrontObj(void) +{ + delete mIndices; + delete mVertices; +} + +NxU32 WavefrontObj::loadObj(const char *fname, bool textured) // load a wavefront obj returns number of triangles that were loaded. Data is persists until the class is destructed. +{ + + NxU32 ret = 0; + + delete mVertices; + mVertices = 0; + delete mIndices; + mIndices = 0; + mVertexCount = 0; + mTriCount = 0; + + + BuildMesh bm; + + OBJ obj; + + obj.LoadMesh(fname,&bm, textured); + + + const FloatVector &vlist = bm.GetVertices(); + const IntVector &indices = bm.GetIndices(); + if ( vlist.size() ) + { + mVertexCount = (NxI32)vlist.size()/3; + mVertices = new NxF32[mVertexCount*3]; + memcpy( mVertices, &vlist[0], sizeof(NxF32)*mVertexCount*3 ); + + if (textured) + { + mTexCoords = new NxF32[mVertexCount * 2]; + const FloatVector& tList = bm.GetTexCoords(); + memcpy( mTexCoords, &tList[0], sizeof(NxF32) * mVertexCount * 2); + } + + mTriCount = (NxI32)indices.size()/3; + mIndices = new NxU32[mTriCount*3*sizeof(NxU32)]; + memcpy(mIndices, &indices[0], sizeof(NxU32)*mTriCount*3); + ret = mTriCount; + } + + + return ret; +} + +#endif diff --git a/Engine/lib/convexDecomp/wavefront.h b/Engine/lib/convexDecomp/wavefront.h new file mode 100644 index 000000000..601d28885 --- /dev/null +++ b/Engine/lib/convexDecomp/wavefront.h @@ -0,0 +1,77 @@ +#ifndef WAVEFRONT_OBJ_H + +#define WAVEFRONT_OBJ_H + +/* + +wavefront.h : A very small code snippet to read a Wavefront OBJ file into memory. + +*/ + +/*! +** +** Copyright (c) 2009 by John W. Ratcliff mailto:jratcliffscarab@gmail.com +** +** Portions of this source has been released with the PhysXViewer application, as well as +** Rocket, CreateDynamics, ODF, and as a number of sample code snippets. +** +** If you find this code useful or you are feeling particularily generous I would +** ask that you please go to http://www.amillionpixels.us and make a donation +** to Troy DeMolay. +** +** DeMolay is a youth group for young men between the ages of 12 and 21. +** It teaches strong moral principles, as well as leadership skills and +** public speaking. The donations page uses the 'pay for pixels' paradigm +** where, in this case, a pixel is only a single penny. Donations can be +** made for as small as $4 or as high as a $100 block. Each person who donates +** will get a link to their own site as well as acknowledgement on the +** donations blog located here http://www.amillionpixels.blogspot.com/ +** +** If you wish to contact me you can use the following methods: +** +** Skype ID: jratcliff63367 +** Yahoo: jratcliff63367 +** AOL: jratcliff1961 +** email: jratcliffscarab@gmail.com +** +** +** The MIT license: +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is furnished +** to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. + +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +** WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +** CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +#include "NvUserMemAlloc.h" + +class WavefrontObj +{ +public: + + WavefrontObj(void); + ~WavefrontObj(void); + + NxU32 loadObj(const char *fname, bool textured); // load a wavefront obj returns number of triangles that were loaded. Data is persists until the class is destructed. + + NxU32 mVertexCount; + NxU32 mTriCount; + NxU32 *mIndices; + NxF32 *mVertices; + NxF32 *mTexCoords; +}; + +#endif diff --git a/Engine/lib/generateProjects.bat b/Engine/lib/generateProjects.bat new file mode 100644 index 000000000..b064262ec --- /dev/null +++ b/Engine/lib/generateProjects.bat @@ -0,0 +1,13 @@ +@echo off + +SET TORQUEDIR=%2 + +IF NOT DEFINED TORQUEDIR SET TORQUEDIR=..\.. + +@%TORQUEDIR%\engine\bin\php\php %TORQUEDIR%\Tools\projectGenerator\projectGenerator.php buildFiles/config/libs.conf %TORQUEDIR% +@echo ... +@echo ... +@echo ... +@echo REMEMBER: Restart Visual Studio if you are running it to be sure the new +@echo project file(s) are loaded! See docs for more info! +@IF X%1 == X pause \ No newline at end of file diff --git a/Engine/lib/libogg/CHANGES b/Engine/lib/libogg/CHANGES new file mode 100644 index 000000000..cab24c3fa --- /dev/null +++ b/Engine/lib/libogg/CHANGES @@ -0,0 +1,33 @@ +Version 1.1.3 (2005 November 27) + + * Correct a bug in the granulepos field of pages where no packet ends + * New VS2003 and XCode builds, minor fixes to other builds + * documentation fixes and cleanup + +Version 1.1.2 (2004 September 23) + + * fix a bug with multipage packet assembly after seek + +Version 1.1.1 (2004 September 12) + + * various bugfixes + * important bugfix for 64-bit platforms + * various portability fixes + * autotools cleanup from Thomas Vander Stichele + * Symbian OS build support from Colin Ward at CSIRO + * new multiplexed Ogg stream documentation + +Version 1.1 (2003 November 17) + + * big-endian bitpacker routines for Theora + * various portability fixes + * improved API documenation + * RFC 3533 documentation of the format by Silvia Pfeiffer at CSIRO + * RFC 3534 documentation of the application/ogg mime-type by Linus Walleij + +Version 1.0 (2002 July 19) + + * First stable release + * little-endian bitpacker routines for Vorbis + * basic Ogg bitstream sync and coding support + diff --git a/Engine/lib/libogg/include/ogg/ogg.h b/Engine/lib/libogg/include/ogg/ogg.h new file mode 100644 index 000000000..9082679d9 --- /dev/null +++ b/Engine/lib/libogg/include/ogg/ogg.h @@ -0,0 +1,202 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: toplevel libogg include + last mod: $Id: ogg.h 7188 2004-07-20 07:26:04Z xiphmont $ + + ********************************************************************/ +#ifndef _OGG_H +#define _OGG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct { + long endbyte; + int endbit; + + unsigned char *buffer; + unsigned char *ptr; + long storage; +} oggpack_buffer; + +/* ogg_page is used to encapsulate the data in one Ogg bitstream page *****/ + +typedef struct { + unsigned char *header; + long header_len; + unsigned char *body; + long body_len; +} ogg_page; + +/* ogg_stream_state contains the current encode/decode state of a logical + Ogg bitstream **********************************************************/ + +typedef struct { + unsigned char *body_data; /* bytes from packet bodies */ + long body_storage; /* storage elements allocated */ + long body_fill; /* elements stored; fill mark */ + long body_returned; /* elements of fill returned */ + + + int *lacing_vals; /* The values that will go to the segment table */ + ogg_int64_t *granule_vals; /* granulepos values for headers. Not compact + this way, but it is simple coupled to the + lacing fifo */ + long lacing_storage; + long lacing_fill; + long lacing_packet; + long lacing_returned; + + unsigned char header[282]; /* working space for header encode */ + int header_fill; + + int e_o_s; /* set when we have buffered the last packet in the + logical bitstream */ + int b_o_s; /* set after we've written the initial page + of a logical bitstream */ + long serialno; + long pageno; + ogg_int64_t packetno; /* sequence number for decode; the framing + knows where there's a hole in the data, + but we need coupling so that the codec + (which is in a seperate abstraction + layer) also knows about the gap */ + ogg_int64_t granulepos; + +} ogg_stream_state; + +/* ogg_packet is used to encapsulate the data and metadata belonging + to a single raw Ogg/Vorbis packet *************************************/ + +typedef struct { + unsigned char *packet; + long bytes; + long b_o_s; + long e_o_s; + + ogg_int64_t granulepos; + + ogg_int64_t packetno; /* sequence number for decode; the framing + knows where there's a hole in the data, + but we need coupling so that the codec + (which is in a seperate abstraction + layer) also knows about the gap */ +} ogg_packet; + +typedef struct { + unsigned char *data; + int storage; + int fill; + int returned; + + int unsynced; + int headerbytes; + int bodybytes; +} ogg_sync_state; + +/* Ogg BITSTREAM PRIMITIVES: bitstream ************************/ + +extern void oggpack_writeinit(oggpack_buffer *b); +extern void oggpack_writetrunc(oggpack_buffer *b,long bits); +extern void oggpack_writealign(oggpack_buffer *b); +extern void oggpack_writecopy(oggpack_buffer *b,void *source,long bits); +extern void oggpack_reset(oggpack_buffer *b); +extern void oggpack_writeclear(oggpack_buffer *b); +extern void oggpack_readinit(oggpack_buffer *b,unsigned char *buf,int bytes); +extern void oggpack_write(oggpack_buffer *b,unsigned long value,int bits); +extern long oggpack_look(oggpack_buffer *b,int bits); +extern long oggpack_look1(oggpack_buffer *b); +extern void oggpack_adv(oggpack_buffer *b,int bits); +extern void oggpack_adv1(oggpack_buffer *b); +extern long oggpack_read(oggpack_buffer *b,int bits); +extern long oggpack_read1(oggpack_buffer *b); +extern long oggpack_bytes(oggpack_buffer *b); +extern long oggpack_bits(oggpack_buffer *b); +extern unsigned char *oggpack_get_buffer(oggpack_buffer *b); + +extern void oggpackB_writeinit(oggpack_buffer *b); +extern void oggpackB_writetrunc(oggpack_buffer *b,long bits); +extern void oggpackB_writealign(oggpack_buffer *b); +extern void oggpackB_writecopy(oggpack_buffer *b,void *source,long bits); +extern void oggpackB_reset(oggpack_buffer *b); +extern void oggpackB_writeclear(oggpack_buffer *b); +extern void oggpackB_readinit(oggpack_buffer *b,unsigned char *buf,int bytes); +extern void oggpackB_write(oggpack_buffer *b,unsigned long value,int bits); +extern long oggpackB_look(oggpack_buffer *b,int bits); +extern long oggpackB_look1(oggpack_buffer *b); +extern void oggpackB_adv(oggpack_buffer *b,int bits); +extern void oggpackB_adv1(oggpack_buffer *b); +extern long oggpackB_read(oggpack_buffer *b,int bits); +extern long oggpackB_read1(oggpack_buffer *b); +extern long oggpackB_bytes(oggpack_buffer *b); +extern long oggpackB_bits(oggpack_buffer *b); +extern unsigned char *oggpackB_get_buffer(oggpack_buffer *b); + +/* Ogg BITSTREAM PRIMITIVES: encoding **************************/ + +extern int ogg_stream_packetin(ogg_stream_state *os, ogg_packet *op); +extern int ogg_stream_pageout(ogg_stream_state *os, ogg_page *og); +extern int ogg_stream_flush(ogg_stream_state *os, ogg_page *og); + +/* Ogg BITSTREAM PRIMITIVES: decoding **************************/ + +extern int ogg_sync_init(ogg_sync_state *oy); +extern int ogg_sync_clear(ogg_sync_state *oy); +extern int ogg_sync_reset(ogg_sync_state *oy); +extern int ogg_sync_destroy(ogg_sync_state *oy); + +extern char *ogg_sync_buffer(ogg_sync_state *oy, long size); +extern int ogg_sync_wrote(ogg_sync_state *oy, long bytes); +extern long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og); +extern int ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og); +extern int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og); +extern int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op); +extern int ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op); + +/* Ogg BITSTREAM PRIMITIVES: general ***************************/ + +extern int ogg_stream_init(ogg_stream_state *os,int serialno); +extern int ogg_stream_clear(ogg_stream_state *os); +extern int ogg_stream_reset(ogg_stream_state *os); +extern int ogg_stream_reset_serialno(ogg_stream_state *os,int serialno); +extern int ogg_stream_destroy(ogg_stream_state *os); +extern int ogg_stream_eos(ogg_stream_state *os); + +extern void ogg_page_checksum_set(ogg_page *og); + +extern int ogg_page_version(ogg_page *og); +extern int ogg_page_continued(ogg_page *og); +extern int ogg_page_bos(ogg_page *og); +extern int ogg_page_eos(ogg_page *og); +extern ogg_int64_t ogg_page_granulepos(ogg_page *og); +extern int ogg_page_serialno(ogg_page *og); +extern long ogg_page_pageno(ogg_page *og); +extern int ogg_page_packets(ogg_page *og); + +extern void ogg_packet_clear(ogg_packet *op); + + +#ifdef __cplusplus +} +#endif + +#endif /* _OGG_H */ + + + + + + diff --git a/Engine/lib/libogg/include/ogg/os_types.h b/Engine/lib/libogg/include/ogg/os_types.h new file mode 100644 index 000000000..32dcb8bff --- /dev/null +++ b/Engine/lib/libogg/include/ogg/os_types.h @@ -0,0 +1,127 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: #ifdef jail to whip a few platforms into the UNIX ideal. + last mod: $Id: os_types.h 7524 2004-08-11 04:20:36Z conrad $ + + ********************************************************************/ +#ifndef _OS_TYPES_H +#define _OS_TYPES_H + +/* make it easy on the folks that want to compile the libs with a + different malloc than stdlib */ +#define _ogg_malloc malloc +#define _ogg_calloc calloc +#define _ogg_realloc realloc +#define _ogg_free free + +#if defined(_WIN32) + +# if defined(__CYGWIN__) +# include <_G_config.h> + typedef _G_int64_t ogg_int64_t; + typedef _G_int32_t ogg_int32_t; + typedef _G_uint32_t ogg_uint32_t; + typedef _G_int16_t ogg_int16_t; + typedef _G_uint16_t ogg_uint16_t; +# elif defined(__MINGW32__) + typedef short ogg_int16_t; + typedef unsigned short ogg_uint16_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long ogg_int64_t; + typedef unsigned long long ogg_uint64_t; +# elif defined(__MWERKS__) + typedef long long ogg_int64_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef short ogg_int16_t; + typedef unsigned short ogg_uint16_t; +# else + /* MSVC/Borland */ + typedef __int64 ogg_int64_t; + typedef __int32 ogg_int32_t; + typedef unsigned __int32 ogg_uint32_t; + typedef __int16 ogg_int16_t; + typedef unsigned __int16 ogg_uint16_t; +# endif + +#elif defined(__MACOS__) + +# include + typedef SInt16 ogg_int16_t; + typedef UInt16 ogg_uint16_t; + typedef SInt32 ogg_int32_t; + typedef UInt32 ogg_uint32_t; + typedef SInt64 ogg_int64_t; + +#elif defined(__MACOSX__) /* MacOS X Framework build */ + +# include + typedef int16_t ogg_int16_t; + typedef u_int16_t ogg_uint16_t; + typedef int32_t ogg_int32_t; + typedef u_int32_t ogg_uint32_t; + typedef int64_t ogg_int64_t; + +#elif defined(__BEOS__) + + /* Be */ +# include + typedef int16_t ogg_int16_t; + typedef u_int16_t ogg_uint16_t; + typedef int32_t ogg_int32_t; + typedef u_int32_t ogg_uint32_t; + typedef int64_t ogg_int64_t; + +#elif defined (__EMX__) + + /* OS/2 GCC */ + typedef short ogg_int16_t; + typedef unsigned short ogg_uint16_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long ogg_int64_t; + +#elif defined (DJGPP) + + /* DJGPP */ + typedef short ogg_int16_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long ogg_int64_t; + +#elif defined(R5900) + + /* PS2 EE */ + typedef long ogg_int64_t; + typedef int ogg_int32_t; + typedef unsigned ogg_uint32_t; + typedef short ogg_int16_t; + +#elif defined(__SYMBIAN32__) + + /* Symbian GCC */ + typedef signed short ogg_int16_t; + typedef unsigned short ogg_uint16_t; + typedef signed int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long int ogg_int64_t; + +#else + +# include +# include + +#endif + +#endif /* _OS_TYPES_H */ diff --git a/Engine/lib/libogg/src/bitwise.c b/Engine/lib/libogg/src/bitwise.c new file mode 100644 index 000000000..15246db09 --- /dev/null +++ b/Engine/lib/libogg/src/bitwise.c @@ -0,0 +1,784 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: packing variable sized words into an octet stream + last mod: $Id: bitwise.c 7675 2004-09-01 00:34:39Z xiphmont $ + + ********************************************************************/ + +/* We're 'LSb' endian; if we write a word but read individual bits, + then we'll read the lsb first */ + +#include +#include +#include + +#define BUFFER_INCREMENT 256 + +static const unsigned long mask[]= +{0x00000000,0x00000001,0x00000003,0x00000007,0x0000000f, + 0x0000001f,0x0000003f,0x0000007f,0x000000ff,0x000001ff, + 0x000003ff,0x000007ff,0x00000fff,0x00001fff,0x00003fff, + 0x00007fff,0x0000ffff,0x0001ffff,0x0003ffff,0x0007ffff, + 0x000fffff,0x001fffff,0x003fffff,0x007fffff,0x00ffffff, + 0x01ffffff,0x03ffffff,0x07ffffff,0x0fffffff,0x1fffffff, + 0x3fffffff,0x7fffffff,0xffffffff }; + +static const unsigned int mask8B[]= +{0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff}; + +void oggpack_writeinit(oggpack_buffer *b){ + memset(b,0,sizeof(*b)); + b->ptr=b->buffer=_ogg_malloc(BUFFER_INCREMENT); + b->buffer[0]='\0'; + b->storage=BUFFER_INCREMENT; +} + +void oggpackB_writeinit(oggpack_buffer *b){ + oggpack_writeinit(b); +} + +void oggpack_writetrunc(oggpack_buffer *b,long bits){ + long bytes=bits>>3; + bits-=bytes*8; + b->ptr=b->buffer+bytes; + b->endbit=bits; + b->endbyte=bytes; + *b->ptr&=mask[bits]; +} + +void oggpackB_writetrunc(oggpack_buffer *b,long bits){ + long bytes=bits>>3; + bits-=bytes*8; + b->ptr=b->buffer+bytes; + b->endbit=bits; + b->endbyte=bytes; + *b->ptr&=mask8B[bits]; +} + +/* Takes only up to 32 bits. */ +void oggpack_write(oggpack_buffer *b,unsigned long value,int bits){ + if(b->endbyte+4>=b->storage){ + b->buffer=_ogg_realloc(b->buffer,b->storage+BUFFER_INCREMENT); + b->storage+=BUFFER_INCREMENT; + b->ptr=b->buffer+b->endbyte; + } + + value&=mask[bits]; + bits+=b->endbit; + + b->ptr[0]|=value<endbit; + + if(bits>=8){ + b->ptr[1]=(unsigned char)(value>>(8-b->endbit)); + if(bits>=16){ + b->ptr[2]=(unsigned char)(value>>(16-b->endbit)); + if(bits>=24){ + b->ptr[3]=(unsigned char)(value>>(24-b->endbit)); + if(bits>=32){ + if(b->endbit) + b->ptr[4]=(unsigned char)(value>>(32-b->endbit)); + else + b->ptr[4]=0; + } + } + } + } + + b->endbyte+=bits/8; + b->ptr+=bits/8; + b->endbit=bits&7; +} + +/* Takes only up to 32 bits. */ +void oggpackB_write(oggpack_buffer *b,unsigned long value,int bits){ + if(b->endbyte+4>=b->storage){ + b->buffer=_ogg_realloc(b->buffer,b->storage+BUFFER_INCREMENT); + b->storage+=BUFFER_INCREMENT; + b->ptr=b->buffer+b->endbyte; + } + + value=(value&mask[bits])<<(32-bits); + bits+=b->endbit; + + b->ptr[0]|=value>>(24+b->endbit); + + if(bits>=8){ + b->ptr[1]=(unsigned char)(value>>(16+b->endbit)); + if(bits>=16){ + b->ptr[2]=(unsigned char)(value>>(8+b->endbit)); + if(bits>=24){ + b->ptr[3]=(unsigned char)(value>>(b->endbit)); + if(bits>=32){ + if(b->endbit) + b->ptr[4]=(unsigned char)(value<<(8-b->endbit)); + else + b->ptr[4]=0; + } + } + } + } + + b->endbyte+=bits/8; + b->ptr+=bits/8; + b->endbit=bits&7; +} + +void oggpack_writealign(oggpack_buffer *b){ + int bits=8-b->endbit; + if(bits<8) + oggpack_write(b,0,bits); +} + +void oggpackB_writealign(oggpack_buffer *b){ + int bits=8-b->endbit; + if(bits<8) + oggpackB_write(b,0,bits); +} + +static void oggpack_writecopy_helper(oggpack_buffer *b, + void *source, + long bits, + void (*w)(oggpack_buffer *, + unsigned long, + int), + int msb){ + unsigned char *ptr=(unsigned char *)source; + + long bytes=bits/8; + bits-=bytes*8; + + if(b->endbit){ + int i; + /* unaligned copy. Do it the hard way. */ + for(i=0;iendbyte+bytes+1>=b->storage){ + b->storage=b->endbyte+bytes+BUFFER_INCREMENT; + b->buffer=_ogg_realloc(b->buffer,b->storage); + b->ptr=b->buffer+b->endbyte; + } + + memmove(b->ptr,source,bytes); + b->ptr+=bytes; + b->endbyte+=bytes; + *b->ptr=0; + + } + if(bits){ + if(msb) + w(b,(unsigned long)(ptr[bytes]>>(8-bits)),bits); + else + w(b,(unsigned long)(ptr[bytes]),bits); + } +} + +void oggpack_writecopy(oggpack_buffer *b,void *source,long bits){ + oggpack_writecopy_helper(b,source,bits,oggpack_write,0); +} + +void oggpackB_writecopy(oggpack_buffer *b,void *source,long bits){ + oggpack_writecopy_helper(b,source,bits,oggpackB_write,1); +} + +void oggpack_reset(oggpack_buffer *b){ + b->ptr=b->buffer; + b->buffer[0]=0; + b->endbit=b->endbyte=0; +} + +void oggpackB_reset(oggpack_buffer *b){ + oggpack_reset(b); +} + +void oggpack_writeclear(oggpack_buffer *b){ + _ogg_free(b->buffer); + memset(b,0,sizeof(*b)); +} + +void oggpackB_writeclear(oggpack_buffer *b){ + oggpack_writeclear(b); +} + +void oggpack_readinit(oggpack_buffer *b,unsigned char *buf,int bytes){ + memset(b,0,sizeof(*b)); + b->buffer=b->ptr=buf; + b->storage=bytes; +} + +void oggpackB_readinit(oggpack_buffer *b,unsigned char *buf,int bytes){ + oggpack_readinit(b,buf,bytes); +} + +/* Read in bits without advancing the bitptr; bits <= 32 */ +long oggpack_look(oggpack_buffer *b,int bits){ + unsigned long ret; + unsigned long m=mask[bits]; + + bits+=b->endbit; + + if(b->endbyte+4>=b->storage){ + /* not the main path */ + if(b->endbyte*8+bits>b->storage*8)return(-1); + } + + ret=b->ptr[0]>>b->endbit; + if(bits>8){ + ret|=b->ptr[1]<<(8-b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(16-b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(24-b->endbit); + if(bits>32 && b->endbit) + ret|=b->ptr[4]<<(32-b->endbit); + } + } + } + return(m&ret); +} + +/* Read in bits without advancing the bitptr; bits <= 32 */ +long oggpackB_look(oggpack_buffer *b,int bits){ + unsigned long ret; + int m=32-bits; + + bits+=b->endbit; + + if(b->endbyte+4>=b->storage){ + /* not the main path */ + if(b->endbyte*8+bits>b->storage*8)return(-1); + } + + ret=b->ptr[0]<<(24+b->endbit); + if(bits>8){ + ret|=b->ptr[1]<<(16+b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(8+b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(b->endbit); + if(bits>32 && b->endbit) + ret|=b->ptr[4]>>(8-b->endbit); + } + } + } + return ((ret&0xffffffff)>>(m>>1))>>((m+1)>>1); +} + +long oggpack_look1(oggpack_buffer *b){ + if(b->endbyte>=b->storage)return(-1); + return((b->ptr[0]>>b->endbit)&1); +} + +long oggpackB_look1(oggpack_buffer *b){ + if(b->endbyte>=b->storage)return(-1); + return((b->ptr[0]>>(7-b->endbit))&1); +} + +void oggpack_adv(oggpack_buffer *b,int bits){ + bits+=b->endbit; + b->ptr+=bits/8; + b->endbyte+=bits/8; + b->endbit=bits&7; +} + +void oggpackB_adv(oggpack_buffer *b,int bits){ + oggpack_adv(b,bits); +} + +void oggpack_adv1(oggpack_buffer *b){ + if(++(b->endbit)>7){ + b->endbit=0; + b->ptr++; + b->endbyte++; + } +} + +void oggpackB_adv1(oggpack_buffer *b){ + oggpack_adv1(b); +} + +/* bits <= 32 */ +long oggpack_read(oggpack_buffer *b,int bits){ + long ret; + unsigned long m=mask[bits]; + + bits+=b->endbit; + + if(b->endbyte+4>=b->storage){ + /* not the main path */ + ret=-1L; + if(b->endbyte*8+bits>b->storage*8)goto overflow; + } + + ret=b->ptr[0]>>b->endbit; + if(bits>8){ + ret|=b->ptr[1]<<(8-b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(16-b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(24-b->endbit); + if(bits>32 && b->endbit){ + ret|=b->ptr[4]<<(32-b->endbit); + } + } + } + } + ret&=m; + + overflow: + + b->ptr+=bits/8; + b->endbyte+=bits/8; + b->endbit=bits&7; + return(ret); +} + +/* bits <= 32 */ +long oggpackB_read(oggpack_buffer *b,int bits){ + long ret; + long m=32-bits; + + bits+=b->endbit; + + if(b->endbyte+4>=b->storage){ + /* not the main path */ + ret=-1L; + if(b->endbyte*8+bits>b->storage*8)goto overflow; + } + + ret=b->ptr[0]<<(24+b->endbit); + if(bits>8){ + ret|=b->ptr[1]<<(16+b->endbit); + if(bits>16){ + ret|=b->ptr[2]<<(8+b->endbit); + if(bits>24){ + ret|=b->ptr[3]<<(b->endbit); + if(bits>32 && b->endbit) + ret|=b->ptr[4]>>(8-b->endbit); + } + } + } + ret=((ret&0xffffffffUL)>>(m>>1))>>((m+1)>>1); + + overflow: + + b->ptr+=bits/8; + b->endbyte+=bits/8; + b->endbit=bits&7; + return(ret); +} + +long oggpack_read1(oggpack_buffer *b){ + long ret; + + if(b->endbyte>=b->storage){ + /* not the main path */ + ret=-1L; + goto overflow; + } + + ret=(b->ptr[0]>>b->endbit)&1; + + overflow: + + b->endbit++; + if(b->endbit>7){ + b->endbit=0; + b->ptr++; + b->endbyte++; + } + return(ret); +} + +long oggpackB_read1(oggpack_buffer *b){ + long ret; + + if(b->endbyte>=b->storage){ + /* not the main path */ + ret=-1L; + goto overflow; + } + + ret=(b->ptr[0]>>(7-b->endbit))&1; + + overflow: + + b->endbit++; + if(b->endbit>7){ + b->endbit=0; + b->ptr++; + b->endbyte++; + } + return(ret); +} + +long oggpack_bytes(oggpack_buffer *b){ + return(b->endbyte+(b->endbit+7)/8); +} + +long oggpack_bits(oggpack_buffer *b){ + return(b->endbyte*8+b->endbit); +} + +long oggpackB_bytes(oggpack_buffer *b){ + return oggpack_bytes(b); +} + +long oggpackB_bits(oggpack_buffer *b){ + return oggpack_bits(b); +} + +unsigned char *oggpack_get_buffer(oggpack_buffer *b){ + return(b->buffer); +} + +unsigned char *oggpackB_get_buffer(oggpack_buffer *b){ + return oggpack_get_buffer(b); +} + +/* Self test of the bitwise routines; everything else is based on + them, so they damned well better be solid. */ + +#ifdef _V_SELFTEST +#include + +static int ilog(unsigned int v){ + int ret=0; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +oggpack_buffer o; +oggpack_buffer r; + +void report(char *in){ + fprintf(stderr,"%s",in); + exit(1); +} + +void cliptest(unsigned long *b,int vals,int bits,int *comp,int compsize){ + long bytes,i; + unsigned char *buffer; + + oggpack_reset(&o); + for(i=0;i +#include +#include + +/* A complete description of Ogg framing exists in docs/framing.html */ + +int ogg_page_version(ogg_page *og){ + return((int)(og->header[4])); +} + +int ogg_page_continued(ogg_page *og){ + return((int)(og->header[5]&0x01)); +} + +int ogg_page_bos(ogg_page *og){ + return((int)(og->header[5]&0x02)); +} + +int ogg_page_eos(ogg_page *og){ + return((int)(og->header[5]&0x04)); +} + +ogg_int64_t ogg_page_granulepos(ogg_page *og){ + unsigned char *page=og->header; + ogg_int64_t granulepos=page[13]&(0xff); + granulepos= (granulepos<<8)|(page[12]&0xff); + granulepos= (granulepos<<8)|(page[11]&0xff); + granulepos= (granulepos<<8)|(page[10]&0xff); + granulepos= (granulepos<<8)|(page[9]&0xff); + granulepos= (granulepos<<8)|(page[8]&0xff); + granulepos= (granulepos<<8)|(page[7]&0xff); + granulepos= (granulepos<<8)|(page[6]&0xff); + return(granulepos); +} + +int ogg_page_serialno(ogg_page *og){ + return(og->header[14] | + (og->header[15]<<8) | + (og->header[16]<<16) | + (og->header[17]<<24)); +} + +long ogg_page_pageno(ogg_page *og){ + return(og->header[18] | + (og->header[19]<<8) | + (og->header[20]<<16) | + (og->header[21]<<24)); +} + + + +/* returns the number of packets that are completed on this page (if + the leading packet is begun on a previous page, but ends on this + page, it's counted */ + +/* NOTE: +If a page consists of a packet begun on a previous page, and a new +packet begun (but not completed) on this page, the return will be: + ogg_page_packets(page) ==1, + ogg_page_continued(page) !=0 + +If a page happens to be a single packet that was begun on a +previous page, and spans to the next page (in the case of a three or +more page packet), the return will be: + ogg_page_packets(page) ==0, + ogg_page_continued(page) !=0 +*/ + +int ogg_page_packets(ogg_page *og){ + int i,n=og->header[26],count=0; + for(i=0;iheader[27+i]<255)count++; + return(count); +} + + +#if 0 +/* helper to initialize lookup for direct-table CRC (illustrative; we + use the static init below) */ + +static ogg_uint32_t _ogg_crc_entry(unsigned long index){ + int i; + unsigned long r; + + r = index << 24; + for (i=0; i<8; i++) + if (r & 0x80000000UL) + r = (r << 1) ^ 0x04c11db7; /* The same as the ethernet generator + polynomial, although we use an + unreflected alg and an init/final + of 0, not 0xffffffff */ + else + r<<=1; + return (r & 0xffffffffUL); +} +#endif + +static const ogg_uint32_t crc_lookup[256]={ + 0x00000000,0x04c11db7,0x09823b6e,0x0d4326d9, + 0x130476dc,0x17c56b6b,0x1a864db2,0x1e475005, + 0x2608edb8,0x22c9f00f,0x2f8ad6d6,0x2b4bcb61, + 0x350c9b64,0x31cd86d3,0x3c8ea00a,0x384fbdbd, + 0x4c11db70,0x48d0c6c7,0x4593e01e,0x4152fda9, + 0x5f15adac,0x5bd4b01b,0x569796c2,0x52568b75, + 0x6a1936c8,0x6ed82b7f,0x639b0da6,0x675a1011, + 0x791d4014,0x7ddc5da3,0x709f7b7a,0x745e66cd, + 0x9823b6e0,0x9ce2ab57,0x91a18d8e,0x95609039, + 0x8b27c03c,0x8fe6dd8b,0x82a5fb52,0x8664e6e5, + 0xbe2b5b58,0xbaea46ef,0xb7a96036,0xb3687d81, + 0xad2f2d84,0xa9ee3033,0xa4ad16ea,0xa06c0b5d, + 0xd4326d90,0xd0f37027,0xddb056fe,0xd9714b49, + 0xc7361b4c,0xc3f706fb,0xceb42022,0xca753d95, + 0xf23a8028,0xf6fb9d9f,0xfbb8bb46,0xff79a6f1, + 0xe13ef6f4,0xe5ffeb43,0xe8bccd9a,0xec7dd02d, + 0x34867077,0x30476dc0,0x3d044b19,0x39c556ae, + 0x278206ab,0x23431b1c,0x2e003dc5,0x2ac12072, + 0x128e9dcf,0x164f8078,0x1b0ca6a1,0x1fcdbb16, + 0x018aeb13,0x054bf6a4,0x0808d07d,0x0cc9cdca, + 0x7897ab07,0x7c56b6b0,0x71159069,0x75d48dde, + 0x6b93dddb,0x6f52c06c,0x6211e6b5,0x66d0fb02, + 0x5e9f46bf,0x5a5e5b08,0x571d7dd1,0x53dc6066, + 0x4d9b3063,0x495a2dd4,0x44190b0d,0x40d816ba, + 0xaca5c697,0xa864db20,0xa527fdf9,0xa1e6e04e, + 0xbfa1b04b,0xbb60adfc,0xb6238b25,0xb2e29692, + 0x8aad2b2f,0x8e6c3698,0x832f1041,0x87ee0df6, + 0x99a95df3,0x9d684044,0x902b669d,0x94ea7b2a, + 0xe0b41de7,0xe4750050,0xe9362689,0xedf73b3e, + 0xf3b06b3b,0xf771768c,0xfa325055,0xfef34de2, + 0xc6bcf05f,0xc27dede8,0xcf3ecb31,0xcbffd686, + 0xd5b88683,0xd1799b34,0xdc3abded,0xd8fba05a, + 0x690ce0ee,0x6dcdfd59,0x608edb80,0x644fc637, + 0x7a089632,0x7ec98b85,0x738aad5c,0x774bb0eb, + 0x4f040d56,0x4bc510e1,0x46863638,0x42472b8f, + 0x5c007b8a,0x58c1663d,0x558240e4,0x51435d53, + 0x251d3b9e,0x21dc2629,0x2c9f00f0,0x285e1d47, + 0x36194d42,0x32d850f5,0x3f9b762c,0x3b5a6b9b, + 0x0315d626,0x07d4cb91,0x0a97ed48,0x0e56f0ff, + 0x1011a0fa,0x14d0bd4d,0x19939b94,0x1d528623, + 0xf12f560e,0xf5ee4bb9,0xf8ad6d60,0xfc6c70d7, + 0xe22b20d2,0xe6ea3d65,0xeba91bbc,0xef68060b, + 0xd727bbb6,0xd3e6a601,0xdea580d8,0xda649d6f, + 0xc423cd6a,0xc0e2d0dd,0xcda1f604,0xc960ebb3, + 0xbd3e8d7e,0xb9ff90c9,0xb4bcb610,0xb07daba7, + 0xae3afba2,0xaafbe615,0xa7b8c0cc,0xa379dd7b, + 0x9b3660c6,0x9ff77d71,0x92b45ba8,0x9675461f, + 0x8832161a,0x8cf30bad,0x81b02d74,0x857130c3, + 0x5d8a9099,0x594b8d2e,0x5408abf7,0x50c9b640, + 0x4e8ee645,0x4a4ffbf2,0x470cdd2b,0x43cdc09c, + 0x7b827d21,0x7f436096,0x7200464f,0x76c15bf8, + 0x68860bfd,0x6c47164a,0x61043093,0x65c52d24, + 0x119b4be9,0x155a565e,0x18197087,0x1cd86d30, + 0x029f3d35,0x065e2082,0x0b1d065b,0x0fdc1bec, + 0x3793a651,0x3352bbe6,0x3e119d3f,0x3ad08088, + 0x2497d08d,0x2056cd3a,0x2d15ebe3,0x29d4f654, + 0xc5a92679,0xc1683bce,0xcc2b1d17,0xc8ea00a0, + 0xd6ad50a5,0xd26c4d12,0xdf2f6bcb,0xdbee767c, + 0xe3a1cbc1,0xe760d676,0xea23f0af,0xeee2ed18, + 0xf0a5bd1d,0xf464a0aa,0xf9278673,0xfde69bc4, + 0x89b8fd09,0x8d79e0be,0x803ac667,0x84fbdbd0, + 0x9abc8bd5,0x9e7d9662,0x933eb0bb,0x97ffad0c, + 0xafb010b1,0xab710d06,0xa6322bdf,0xa2f33668, + 0xbcb4666d,0xb8757bda,0xb5365d03,0xb1f740b4}; + +/* init the encode/decode logical stream state */ + +int ogg_stream_init(ogg_stream_state *os,int serialno){ + if(os){ + memset(os,0,sizeof(*os)); + os->body_storage=16*1024; + os->body_data=_ogg_malloc(os->body_storage*sizeof(*os->body_data)); + + os->lacing_storage=1024; + os->lacing_vals=_ogg_malloc(os->lacing_storage*sizeof(*os->lacing_vals)); + os->granule_vals=_ogg_malloc(os->lacing_storage*sizeof(*os->granule_vals)); + + os->serialno=serialno; + + return(0); + } + return(-1); +} + +/* _clear does not free os, only the non-flat storage within */ +int ogg_stream_clear(ogg_stream_state *os){ + if(os){ + if(os->body_data)_ogg_free(os->body_data); + if(os->lacing_vals)_ogg_free(os->lacing_vals); + if(os->granule_vals)_ogg_free(os->granule_vals); + + memset(os,0,sizeof(*os)); + } + return(0); +} + +int ogg_stream_destroy(ogg_stream_state *os){ + if(os){ + ogg_stream_clear(os); + _ogg_free(os); + } + return(0); +} + +/* Helpers for ogg_stream_encode; this keeps the structure and + what's happening fairly clear */ + +static void _os_body_expand(ogg_stream_state *os,int needed){ + if(os->body_storage<=os->body_fill+needed){ + os->body_storage+=(needed+1024); + os->body_data=_ogg_realloc(os->body_data,os->body_storage*sizeof(*os->body_data)); + } +} + +static void _os_lacing_expand(ogg_stream_state *os,int needed){ + if(os->lacing_storage<=os->lacing_fill+needed){ + os->lacing_storage+=(needed+32); + os->lacing_vals=_ogg_realloc(os->lacing_vals,os->lacing_storage*sizeof(*os->lacing_vals)); + os->granule_vals=_ogg_realloc(os->granule_vals,os->lacing_storage*sizeof(*os->granule_vals)); + } +} + +/* checksum the page */ +/* Direct table CRC; note that this will be faster in the future if we + perform the checksum silmultaneously with other copies */ + +void ogg_page_checksum_set(ogg_page *og){ + if(og){ + ogg_uint32_t crc_reg=0; + int i; + + /* safety; needed for API behavior, but not framing code */ + og->header[22]=0; + og->header[23]=0; + og->header[24]=0; + og->header[25]=0; + + for(i=0;iheader_len;i++) + crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^og->header[i]]; + for(i=0;ibody_len;i++) + crc_reg=(crc_reg<<8)^crc_lookup[((crc_reg >> 24)&0xff)^og->body[i]]; + + og->header[22]=(unsigned char)(crc_reg&0xff); + og->header[23]=(unsigned char)((crc_reg>>8)&0xff); + og->header[24]=(unsigned char)((crc_reg>>16)&0xff); + og->header[25]=(unsigned char)((crc_reg>>24)&0xff); + } +} + +/* submit data to the internal buffer of the framing engine */ +int ogg_stream_packetin(ogg_stream_state *os,ogg_packet *op){ + int lacing_vals=op->bytes/255+1,i; + + if(os->body_returned){ + /* advance packet data according to the body_returned pointer. We + had to keep it around to return a pointer into the buffer last + call */ + + os->body_fill-=os->body_returned; + if(os->body_fill) + memmove(os->body_data,os->body_data+os->body_returned, + os->body_fill); + os->body_returned=0; + } + + /* make sure we have the buffer storage */ + _os_body_expand(os,op->bytes); + _os_lacing_expand(os,lacing_vals); + + /* Copy in the submitted packet. Yes, the copy is a waste; this is + the liability of overly clean abstraction for the time being. It + will actually be fairly easy to eliminate the extra copy in the + future */ + + memcpy(os->body_data+os->body_fill,op->packet,op->bytes); + os->body_fill+=op->bytes; + + /* Store lacing vals for this packet */ + for(i=0;ilacing_vals[os->lacing_fill+i]=255; + os->granule_vals[os->lacing_fill+i]=os->granulepos; + } + os->lacing_vals[os->lacing_fill+i]=(op->bytes)%255; + os->granulepos=os->granule_vals[os->lacing_fill+i]=op->granulepos; + + /* flag the first segment as the beginning of the packet */ + os->lacing_vals[os->lacing_fill]|= 0x100; + + os->lacing_fill+=lacing_vals; + + /* for the sake of completeness */ + os->packetno++; + + if(op->e_o_s)os->e_o_s=1; + + return(0); +} + +/* This will flush remaining packets into a page (returning nonzero), + even if there is not enough data to trigger a flush normally + (undersized page). If there are no packets or partial packets to + flush, ogg_stream_flush returns 0. Note that ogg_stream_flush will + try to flush a normal sized page like ogg_stream_pageout; a call to + ogg_stream_flush does not guarantee that all packets have flushed. + Only a return value of 0 from ogg_stream_flush indicates all packet + data is flushed into pages. + + since ogg_stream_flush will flush the last page in a stream even if + it's undersized, you almost certainly want to use ogg_stream_pageout + (and *not* ogg_stream_flush) unless you specifically need to flush + an page regardless of size in the middle of a stream. */ + +int ogg_stream_flush(ogg_stream_state *os,ogg_page *og){ + int i; + int vals=0; + int maxvals=(os->lacing_fill>255?255:os->lacing_fill); + int bytes=0; + long acc=0; + ogg_int64_t granule_pos=-1; + + if(maxvals==0)return(0); + + /* construct a page */ + /* decide how many segments to include */ + + /* If this is the initial header case, the first page must only include + the initial header packet */ + if(os->b_o_s==0){ /* 'initial header page' case */ + granule_pos=0; + for(vals=0;valslacing_vals[vals]&0x0ff)<255){ + vals++; + break; + } + } + }else{ + for(vals=0;vals4096)break; + acc+=os->lacing_vals[vals]&0x0ff; + if((os->lacing_vals[vals]&0xff)<255) + granule_pos=os->granule_vals[vals]; + } + } + + /* construct the header in temp storage */ + memcpy(os->header,"OggS",4); + + /* stream structure version */ + os->header[4]=0x00; + + /* continued packet flag? */ + os->header[5]=0x00; + if((os->lacing_vals[0]&0x100)==0)os->header[5]|=0x01; + /* first page flag? */ + if(os->b_o_s==0)os->header[5]|=0x02; + /* last page flag? */ + if(os->e_o_s && os->lacing_fill==vals)os->header[5]|=0x04; + os->b_o_s=1; + + /* 64 bits of PCM position */ + for(i=6;i<14;i++){ + os->header[i]=(unsigned char)(granule_pos&0xff); + granule_pos>>=8; + } + + /* 32 bits of stream serial number */ + { + long serialno=os->serialno; + for(i=14;i<18;i++){ + os->header[i]=(unsigned char)(serialno&0xff); + serialno>>=8; + } + } + + /* 32 bits of page counter (we have both counter and page header + because this val can roll over) */ + if(os->pageno==-1)os->pageno=0; /* because someone called + stream_reset; this would be a + strange thing to do in an + encode stream, but it has + plausible uses */ + { + long pageno=os->pageno++; + for(i=18;i<22;i++){ + os->header[i]=(unsigned char)(pageno&0xff); + pageno>>=8; + } + } + + /* zero for computation; filled in later */ + os->header[22]=0; + os->header[23]=0; + os->header[24]=0; + os->header[25]=0; + + /* segment table */ + os->header[26]=(unsigned char)(vals&0xff); + for(i=0;iheader[i+27]=(unsigned char)(os->lacing_vals[i]&0xff); + + /* set pointers in the ogg_page struct */ + og->header=os->header; + og->header_len=os->header_fill=vals+27; + og->body=os->body_data+os->body_returned; + og->body_len=bytes; + + /* advance the lacing data and set the body_returned pointer */ + + os->lacing_fill-=vals; + memmove(os->lacing_vals,os->lacing_vals+vals,os->lacing_fill*sizeof(*os->lacing_vals)); + memmove(os->granule_vals,os->granule_vals+vals,os->lacing_fill*sizeof(*os->granule_vals)); + os->body_returned+=bytes; + + /* calculate the checksum */ + + ogg_page_checksum_set(og); + + /* done */ + return(1); +} + + +/* This constructs pages from buffered packet segments. The pointers +returned are to static buffers; do not free. The returned buffers are +good only until the next call (using the same ogg_stream_state) */ + +int ogg_stream_pageout(ogg_stream_state *os, ogg_page *og){ + + if((os->e_o_s&&os->lacing_fill) || /* 'were done, now flush' case */ + os->body_fill-os->body_returned > 4096 ||/* 'page nominal size' case */ + os->lacing_fill>=255 || /* 'segment table full' case */ + (os->lacing_fill&&!os->b_o_s)){ /* 'initial header page' case */ + + return(ogg_stream_flush(os,og)); + } + + /* not enough data to construct a page and not end of stream */ + return(0); +} + +int ogg_stream_eos(ogg_stream_state *os){ + return os->e_o_s; +} + +/* DECODING PRIMITIVES: packet streaming layer **********************/ + +/* This has two layers to place more of the multi-serialno and paging + control in the application's hands. First, we expose a data buffer + using ogg_sync_buffer(). The app either copies into the + buffer, or passes it directly to read(), etc. We then call + ogg_sync_wrote() to tell how many bytes we just added. + + Pages are returned (pointers into the buffer in ogg_sync_state) + by ogg_sync_pageout(). The page is then submitted to + ogg_stream_pagein() along with the appropriate + ogg_stream_state* (ie, matching serialno). We then get raw + packets out calling ogg_stream_packetout() with a + ogg_stream_state. */ + +/* initialize the struct to a known state */ +int ogg_sync_init(ogg_sync_state *oy){ + if(oy){ + memset(oy,0,sizeof(*oy)); + } + return(0); +} + +/* clear non-flat storage within */ +int ogg_sync_clear(ogg_sync_state *oy){ + if(oy){ + if(oy->data)_ogg_free(oy->data); + ogg_sync_init(oy); + } + return(0); +} + +int ogg_sync_destroy(ogg_sync_state *oy){ + if(oy){ + ogg_sync_clear(oy); + _ogg_free(oy); + } + return(0); +} + +char *ogg_sync_buffer(ogg_sync_state *oy, long size){ + + /* first, clear out any space that has been previously returned */ + if(oy->returned){ + oy->fill-=oy->returned; + if(oy->fill>0) + memmove(oy->data,oy->data+oy->returned,oy->fill); + oy->returned=0; + } + + if(size>oy->storage-oy->fill){ + /* We need to extend the internal buffer */ + long newsize=size+oy->fill+4096; /* an extra page to be nice */ + + if(oy->data) + oy->data=_ogg_realloc(oy->data,newsize); + else + oy->data=_ogg_malloc(newsize); + oy->storage=newsize; + } + + /* expose a segment at least as large as requested at the fill mark */ + return((char *)oy->data+oy->fill); +} + +int ogg_sync_wrote(ogg_sync_state *oy, long bytes){ + if(oy->fill+bytes>oy->storage)return(-1); + oy->fill+=bytes; + return(0); +} + +/* sync the stream. This is meant to be useful for finding page + boundaries. + + return values for this: + -n) skipped n bytes + 0) page not ready; more data (no bytes skipped) + n) page synced at current location; page length n bytes + +*/ + +long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og){ + unsigned char *page=oy->data+oy->returned; + unsigned char *next; + long bytes=oy->fill-oy->returned; + + if(oy->headerbytes==0){ + int headerbytes,i; + if(bytes<27)return(0); /* not enough for a header */ + + /* verify capture pattern */ + if(memcmp(page,"OggS",4))goto sync_fail; + + headerbytes=page[26]+27; + if(bytesbodybytes+=page[27+i]; + oy->headerbytes=headerbytes; + } + + if(oy->bodybytes+oy->headerbytes>bytes)return(0); + + /* The whole test page is buffered. Verify the checksum */ + { + /* Grab the checksum bytes, set the header field to zero */ + char chksum[4]; + ogg_page log; + + memcpy(chksum,page+22,4); + memset(page+22,0,4); + + /* set up a temp page struct and recompute the checksum */ + log.header=page; + log.header_len=oy->headerbytes; + log.body=page+oy->headerbytes; + log.body_len=oy->bodybytes; + ogg_page_checksum_set(&log); + + /* Compare */ + if(memcmp(chksum,page+22,4)){ + /* D'oh. Mismatch! Corrupt page (or miscapture and not a page + at all) */ + /* replace the computed checksum with the one actually read in */ + memcpy(page+22,chksum,4); + + /* Bad checksum. Lose sync */ + goto sync_fail; + } + } + + /* yes, have a whole page all ready to go */ + { + unsigned char *page=oy->data+oy->returned; + long bytes; + + if(og){ + og->header=page; + og->header_len=oy->headerbytes; + og->body=page+oy->headerbytes; + og->body_len=oy->bodybytes; + } + + oy->unsynced=0; + oy->returned+=(bytes=oy->headerbytes+oy->bodybytes); + oy->headerbytes=0; + oy->bodybytes=0; + return(bytes); + } + + sync_fail: + + oy->headerbytes=0; + oy->bodybytes=0; + + /* search for possible capture */ + next=memchr(page+1,'O',bytes-1); + if(!next) + next=oy->data+oy->fill; + + oy->returned=next-oy->data; + return(-(next-page)); +} + +/* sync the stream and get a page. Keep trying until we find a page. + Supress 'sync errors' after reporting the first. + + return values: + -1) recapture (hole in data) + 0) need more data + 1) page returned + + Returns pointers into buffered data; invalidated by next call to + _stream, _clear, _init, or _buffer */ + +int ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og){ + + /* all we need to do is verify a page at the head of the stream + buffer. If it doesn't verify, we look for the next potential + frame */ + + for(;;){ + long ret=ogg_sync_pageseek(oy,og); + if(ret>0){ + /* have a page */ + return(1); + } + if(ret==0){ + /* need more data */ + return(0); + } + + /* head did not start a synced page... skipped some bytes */ + if(!oy->unsynced){ + oy->unsynced=1; + return(-1); + } + + /* loop. keep looking */ + + } +} + +/* add the incoming page to the stream state; we decompose the page + into packet segments here as well. */ + +int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og){ + unsigned char *header=og->header; + unsigned char *body=og->body; + long bodysize=og->body_len; + int segptr=0; + + int version=ogg_page_version(og); + int continued=ogg_page_continued(og); + int bos=ogg_page_bos(og); + int eos=ogg_page_eos(og); + ogg_int64_t granulepos=ogg_page_granulepos(og); + int serialno=ogg_page_serialno(og); + long pageno=ogg_page_pageno(og); + int segments=header[26]; + + /* clean up 'returned data' */ + { + long lr=os->lacing_returned; + long br=os->body_returned; + + /* body data */ + if(br){ + os->body_fill-=br; + if(os->body_fill) + memmove(os->body_data,os->body_data+br,os->body_fill); + os->body_returned=0; + } + + if(lr){ + /* segment table */ + if(os->lacing_fill-lr){ + memmove(os->lacing_vals,os->lacing_vals+lr, + (os->lacing_fill-lr)*sizeof(*os->lacing_vals)); + memmove(os->granule_vals,os->granule_vals+lr, + (os->lacing_fill-lr)*sizeof(*os->granule_vals)); + } + os->lacing_fill-=lr; + os->lacing_packet-=lr; + os->lacing_returned=0; + } + } + + /* check the serial number */ + if(serialno!=os->serialno)return(-1); + if(version>0)return(-1); + + _os_lacing_expand(os,segments+1); + + /* are we in sequence? */ + if(pageno!=os->pageno){ + int i; + + /* unroll previous partial packet (if any) */ + for(i=os->lacing_packet;ilacing_fill;i++) + os->body_fill-=os->lacing_vals[i]&0xff; + os->lacing_fill=os->lacing_packet; + + /* make a note of dropped data in segment table */ + if(os->pageno!=-1){ + os->lacing_vals[os->lacing_fill++]=0x400; + os->lacing_packet++; + } + } + + /* are we a 'continued packet' page? If so, we may need to skip + some segments */ + if(continued){ + if(os->lacing_fill<1 || + os->lacing_vals[os->lacing_fill-1]==0x400){ + bos=0; + for(;segptrbody_data+os->body_fill,body,bodysize); + os->body_fill+=bodysize; + } + + { + int saved=-1; + while(segptrlacing_vals[os->lacing_fill]=val; + os->granule_vals[os->lacing_fill]=-1; + + if(bos){ + os->lacing_vals[os->lacing_fill]|=0x100; + bos=0; + } + + if(val<255)saved=os->lacing_fill; + + os->lacing_fill++; + segptr++; + + if(val<255)os->lacing_packet=os->lacing_fill; + } + + /* set the granulepos on the last granuleval of the last full packet */ + if(saved!=-1){ + os->granule_vals[saved]=granulepos; + } + + } + + if(eos){ + os->e_o_s=1; + if(os->lacing_fill>0) + os->lacing_vals[os->lacing_fill-1]|=0x200; + } + + os->pageno=pageno+1; + + return(0); +} + +/* clear things to an initial state. Good to call, eg, before seeking */ +int ogg_sync_reset(ogg_sync_state *oy){ + oy->fill=0; + oy->returned=0; + oy->unsynced=0; + oy->headerbytes=0; + oy->bodybytes=0; + return(0); +} + +int ogg_stream_reset(ogg_stream_state *os){ + os->body_fill=0; + os->body_returned=0; + + os->lacing_fill=0; + os->lacing_packet=0; + os->lacing_returned=0; + + os->header_fill=0; + + os->e_o_s=0; + os->b_o_s=0; + os->pageno=-1; + os->packetno=0; + os->granulepos=0; + + return(0); +} + +int ogg_stream_reset_serialno(ogg_stream_state *os,int serialno){ + ogg_stream_reset(os); + os->serialno=serialno; + return(0); +} + +static int _packetout(ogg_stream_state *os,ogg_packet *op,int adv){ + + /* The last part of decode. We have the stream broken into packet + segments. Now we need to group them into packets (or return the + out of sync markers) */ + + int ptr=os->lacing_returned; + + if(os->lacing_packet<=ptr)return(0); + + if(os->lacing_vals[ptr]&0x400){ + /* we need to tell the codec there's a gap; it might need to + handle previous packet dependencies. */ + os->lacing_returned++; + os->packetno++; + return(-1); + } + + if(!op && !adv)return(1); /* just using peek as an inexpensive way + to ask if there's a whole packet + waiting */ + + /* Gather the whole packet. We'll have no holes or a partial packet */ + { + int size=os->lacing_vals[ptr]&0xff; + int bytes=size; + int eos=os->lacing_vals[ptr]&0x200; /* last packet of the stream? */ + int bos=os->lacing_vals[ptr]&0x100; /* first packet of the stream? */ + + while(size==255){ + int val=os->lacing_vals[++ptr]; + size=val&0xff; + if(val&0x200)eos=0x200; + bytes+=size; + } + + if(op){ + op->e_o_s=eos; + op->b_o_s=bos; + op->packet=os->body_data+os->body_returned; + op->packetno=os->packetno; + op->granulepos=os->granule_vals[ptr]; + op->bytes=bytes; + } + + if(adv){ + os->body_returned+=bytes; + os->lacing_returned=ptr+1; + os->packetno++; + } + } + return(1); +} + +int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op){ + return _packetout(os,op,1); +} + +int ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op){ + return _packetout(os,op,0); +} + +void ogg_packet_clear(ogg_packet *op) { + _ogg_free(op->packet); + memset(op, 0, sizeof(*op)); +} + +#ifdef _V_SELFTEST +#include + +ogg_stream_state os_en, os_de; +ogg_sync_state oy; + +void checkpacket(ogg_packet *op,int len, int no, int pos){ + long j; + static int sequence=0; + static int lastno=0; + + if(op->bytes!=len){ + fprintf(stderr,"incorrect packet length!\n"); + exit(1); + } + if(op->granulepos!=pos){ + fprintf(stderr,"incorrect packet position!\n"); + exit(1); + } + + /* packet number just follows sequence/gap; adjust the input number + for that */ + if(no==0){ + sequence=0; + }else{ + sequence++; + if(no>lastno+1) + sequence++; + } + lastno=no; + if(op->packetno!=sequence){ + fprintf(stderr,"incorrect packet sequence %ld != %d\n", + (long)(op->packetno),sequence); + exit(1); + } + + /* Test data */ + for(j=0;jbytes;j++) + if(op->packet[j]!=((j+no)&0xff)){ + fprintf(stderr,"body data mismatch (1) at pos %ld: %x!=%lx!\n\n", + j,op->packet[j],(j+no)&0xff); + exit(1); + } +} + +void check_page(unsigned char *data,const int *header,ogg_page *og){ + long j; + /* Test data */ + for(j=0;jbody_len;j++) + if(og->body[j]!=data[j]){ + fprintf(stderr,"body data mismatch (2) at pos %ld: %x!=%x!\n\n", + j,data[j],og->body[j]); + exit(1); + } + + /* Test header */ + for(j=0;jheader_len;j++){ + if(og->header[j]!=header[j]){ + fprintf(stderr,"header content mismatch at pos %ld:\n",j); + for(j=0;jheader[j]); + fprintf(stderr,"\n"); + exit(1); + } + } + if(og->header_len!=header[26]+27){ + fprintf(stderr,"header length incorrect! (%ld!=%d)\n", + og->header_len,header[26]+27); + exit(1); + } +} + +void print_header(ogg_page *og){ + int j; + fprintf(stderr,"\nHEADER:\n"); + fprintf(stderr," capture: %c %c %c %c version: %d flags: %x\n", + og->header[0],og->header[1],og->header[2],og->header[3], + (int)og->header[4],(int)og->header[5]); + + fprintf(stderr," granulepos: %d serialno: %d pageno: %ld\n", + (og->header[9]<<24)|(og->header[8]<<16)| + (og->header[7]<<8)|og->header[6], + (og->header[17]<<24)|(og->header[16]<<16)| + (og->header[15]<<8)|og->header[14], + ((long)(og->header[21])<<24)|(og->header[20]<<16)| + (og->header[19]<<8)|og->header[18]); + + fprintf(stderr," checksum: %02x:%02x:%02x:%02x\n segments: %d (", + (int)og->header[22],(int)og->header[23], + (int)og->header[24],(int)og->header[25], + (int)og->header[26]); + + for(j=27;jheader_len;j++) + fprintf(stderr,"%d ",(int)og->header[j]); + fprintf(stderr,")\n\n"); +} + +void copy_page(ogg_page *og){ + unsigned char *temp=_ogg_malloc(og->header_len); + memcpy(temp,og->header,og->header_len); + og->header=temp; + + temp=_ogg_malloc(og->body_len); + memcpy(temp,og->body,og->body_len); + og->body=temp; +} + +void free_page(ogg_page *og){ + _ogg_free (og->header); + _ogg_free (og->body); +} + +void error(void){ + fprintf(stderr,"error!\n"); + exit(1); +} + +/* 17 only */ +const int head1_0[] = {0x4f,0x67,0x67,0x53,0,0x06, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0x15,0xed,0xec,0x91, + 1, + 17}; + +/* 17, 254, 255, 256, 500, 510, 600 byte, pad */ +const int head1_1[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0x59,0x10,0x6c,0x2c, + 1, + 17}; +const int head2_1[] = {0x4f,0x67,0x67,0x53,0,0x04, + 0x07,0x18,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x89,0x33,0x85,0xce, + 13, + 254,255,0,255,1,255,245,255,255,0, + 255,255,90}; + +/* nil packets; beginning,middle,end */ +const int head1_2[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; +const int head2_2[] = {0x4f,0x67,0x67,0x53,0,0x04, + 0x07,0x28,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x5c,0x3f,0x66,0xcb, + 17, + 17,254,255,0,0,255,1,0,255,245,255,255,0, + 255,255,90,0}; + +/* large initial packet */ +const int head1_3[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0x01,0x27,0x31,0xaa, + 18, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,10}; + +const int head2_3[] = {0x4f,0x67,0x67,0x53,0,0x04, + 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x7f,0x4e,0x8a,0xd2, + 4, + 255,4,255,0}; + + +/* continuing packet test */ +const int head1_4[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; + +const int head2_4[] = {0x4f,0x67,0x67,0x53,0,0x00, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x54,0x05,0x51,0xc8, + 17, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255}; + +const int head3_4[] = {0x4f,0x67,0x67,0x53,0,0x05, + 0x07,0x0c,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0xc8,0xc3,0xcb,0xed, + 5, + 10,255,4,255,0}; + + +/* page with the 255 segment limit */ +const int head1_5[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; + +const int head2_5[] = {0x4f,0x67,0x67,0x53,0,0x00, + 0x07,0xfc,0x03,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0xed,0x2a,0x2e,0xa7, + 255, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10}; + +const int head3_5[] = {0x4f,0x67,0x67,0x53,0,0x04, + 0x07,0x00,0x04,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0x6c,0x3b,0x82,0x3d, + 1, + 50}; + + +/* packet that overspans over an entire page */ +const int head1_6[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; + +const int head2_6[] = {0x4f,0x67,0x67,0x53,0,0x00, + 0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x3c,0xd9,0x4d,0x3f, + 17, + 100,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255}; + +const int head3_6[] = {0x4f,0x67,0x67,0x53,0,0x01, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0x01,0xd2,0xe5,0xe5, + 17, + 255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255}; + +const int head4_6[] = {0x4f,0x67,0x67,0x53,0,0x05, + 0x07,0x10,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,3,0,0,0, + 0xef,0xdd,0x88,0xde, + 7, + 255,255,75,255,4,255,0}; + +/* packet that overspans over an entire page */ +const int head1_7[] = {0x4f,0x67,0x67,0x53,0,0x02, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,0,0,0,0, + 0xff,0x7b,0x23,0x17, + 1, + 0}; + +const int head2_7[] = {0x4f,0x67,0x67,0x53,0,0x00, + 0x07,0x04,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,1,0,0,0, + 0x3c,0xd9,0x4d,0x3f, + 17, + 100,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255}; + +const int head3_7[] = {0x4f,0x67,0x67,0x53,0,0x05, + 0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x02,0x03,0x04,2,0,0,0, + 0xd4,0xe0,0x60,0xe5, + 1,0}; + +void test_pack(const int *pl, const int **headers, int byteskip, + int pageskip, int packetskip){ + unsigned char *data=_ogg_malloc(1024*1024); /* for scripted test cases only */ + long inptr=0; + long outptr=0; + long deptr=0; + long depacket=0; + long granule_pos=7,pageno=0; + int i,j,packets,pageout=pageskip; + int eosflag=0; + int bosflag=0; + + int byteskipcount=0; + + ogg_stream_reset(&os_en); + ogg_stream_reset(&os_de); + ogg_sync_reset(&oy); + + for(packets=0;packetsbyteskip){ + memcpy(next,og.header,byteskipcount-byteskip); + next+=byteskipcount-byteskip; + byteskipcount=byteskip; + } + + byteskipcount+=og.body_len; + if(byteskipcount>byteskip){ + memcpy(next,og.body,byteskipcount-byteskip); + next+=byteskipcount-byteskip; + byteskipcount=byteskip; + } + + ogg_sync_wrote(&oy,next-buf); + + while(1){ + int ret=ogg_sync_pageout(&oy,&og_de); + if(ret==0)break; + if(ret<0)continue; + /* got a page. Happy happy. Verify that it's good. */ + + fprintf(stderr,"(%ld), ",pageout); + + check_page(data+deptr,headers[pageout],&og_de); + deptr+=og_de.body_len; + pageout++; + + /* submit it to deconstitution */ + ogg_stream_pagein(&os_de,&og_de); + + /* packets out? */ + while(ogg_stream_packetpeek(&os_de,&op_de2)>0){ + ogg_stream_packetpeek(&os_de,NULL); + ogg_stream_packetout(&os_de,&op_de); /* just catching them all */ + + /* verify peek and out match */ + if(memcmp(&op_de,&op_de2,sizeof(op_de))){ + fprintf(stderr,"packetout != packetpeek! pos=%ld\n", + depacket); + exit(1); + } + + /* verify the packet! */ + /* check data */ + if(memcmp(data+depacket,op_de.packet,op_de.bytes)){ + fprintf(stderr,"packet data mismatch in decode! pos=%ld\n", + depacket); + exit(1); + } + /* check bos flag */ + if(bosflag==0 && op_de.b_o_s==0){ + fprintf(stderr,"b_o_s flag not set on packet!\n"); + exit(1); + } + if(bosflag && op_de.b_o_s){ + fprintf(stderr,"b_o_s flag incorrectly set on packet!\n"); + exit(1); + } + bosflag=1; + depacket+=op_de.bytes; + + /* check eos flag */ + if(eosflag){ + fprintf(stderr,"Multiple decoded packets with eos flag!\n"); + exit(1); + } + + if(op_de.e_o_s)eosflag=1; + + /* check granulepos flag */ + if(op_de.granulepos!=-1){ + fprintf(stderr," granule:%ld ",(long)op_de.granulepos); + } + } + } + } + } + } + } + _ogg_free(data); + if(headers[pageno]!=NULL){ + fprintf(stderr,"did not write last page!\n"); + exit(1); + } + if(headers[pageout]!=NULL){ + fprintf(stderr,"did not decode last page!\n"); + exit(1); + } + if(inptr!=outptr){ + fprintf(stderr,"encoded page data incomplete!\n"); + exit(1); + } + if(inptr!=deptr){ + fprintf(stderr,"decoded page data incomplete!\n"); + exit(1); + } + if(inptr!=depacket){ + fprintf(stderr,"decoded packet data incomplete!\n"); + exit(1); + } + if(!eosflag){ + fprintf(stderr,"Never got a packet with EOS set!\n"); + exit(1); + } + fprintf(stderr,"ok.\n"); +} + +int main(void){ + + ogg_stream_init(&os_en,0x04030201); + ogg_stream_init(&os_de,0x04030201); + ogg_sync_init(&oy); + + /* Exercise each code path in the framing code. Also verify that + the checksums are working. */ + + { + /* 17 only */ + const int packets[]={17, -1}; + const int *headret[]={head1_0,NULL}; + + fprintf(stderr,"testing single page encoding... "); + test_pack(packets,headret,0,0,0); + } + + { + /* 17, 254, 255, 256, 500, 510, 600 byte, pad */ + const int packets[]={17, 254, 255, 256, 500, 510, 600, -1}; + const int *headret[]={head1_1,head2_1,NULL}; + + fprintf(stderr,"testing basic page encoding... "); + test_pack(packets,headret,0,0,0); + } + + { + /* nil packets; beginning,middle,end */ + const int packets[]={0,17, 254, 255, 0, 256, 0, 500, 510, 600, 0, -1}; + const int *headret[]={head1_2,head2_2,NULL}; + + fprintf(stderr,"testing basic nil packets... "); + test_pack(packets,headret,0,0,0); + } + + { + /* large initial packet */ + const int packets[]={4345,259,255,-1}; + const int *headret[]={head1_3,head2_3,NULL}; + + fprintf(stderr,"testing initial-packet lacing > 4k... "); + test_pack(packets,headret,0,0,0); + } + + { + /* continuing packet test */ + const int packets[]={0,4345,259,255,-1}; + const int *headret[]={head1_4,head2_4,head3_4,NULL}; + + fprintf(stderr,"testing single packet page span... "); + test_pack(packets,headret,0,0,0); + } + + /* page with the 255 segment limit */ + { + + const int packets[]={0,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,50,-1}; + const int *headret[]={head1_5,head2_5,head3_5,NULL}; + + fprintf(stderr,"testing max packet segments... "); + test_pack(packets,headret,0,0,0); + } + + { + /* packet that overspans over an entire page */ + const int packets[]={0,100,9000,259,255,-1}; + const int *headret[]={head1_6,head2_6,head3_6,head4_6,NULL}; + + fprintf(stderr,"testing very large packets... "); + test_pack(packets,headret,0,0,0); + } + + { + /* test for the libogg 1.1.1 resync in large continuation bug + found by Josh Coalson) */ + const int packets[]={0,100,9000,259,255,-1}; + const int *headret[]={head1_6,head2_6,head3_6,head4_6,NULL}; + + fprintf(stderr,"testing continuation resync in very large packets... "); + test_pack(packets,headret,100,2,3); + } + + { + /* term only page. why not? */ + const int packets[]={0,100,4080,-1}; + const int *headret[]={head1_7,head2_7,head3_7,NULL}; + + fprintf(stderr,"testing zero data page (1 nil packet)... "); + test_pack(packets,headret,0,0,0); + } + + + + { + /* build a bunch of pages for testing */ + unsigned char *data=_ogg_malloc(1024*1024); + int pl[]={0,100,4079,2956,2057,76,34,912,0,234,1000,1000,1000,300,-1}; + int inptr=0,i,j; + ogg_page og[5]; + + ogg_stream_reset(&os_en); + + for(i=0;pl[i]!=-1;i++){ + ogg_packet op; + int len=pl[i]; + + op.packet=data+inptr; + op.bytes=len; + op.e_o_s=(pl[i+1]<0?1:0); + op.granulepos=(i+1)*1000; + + for(j=0;j0)error(); + + /* Test fractional page inputs: incomplete fixed header */ + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+3, + 20); + ogg_sync_wrote(&oy,20); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + /* Test fractional page inputs: incomplete header */ + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+23, + 5); + ogg_sync_wrote(&oy,5); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + /* Test fractional page inputs: incomplete body */ + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+28, + og[1].header_len-28); + ogg_sync_wrote(&oy,og[1].header_len-28); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body,1000); + ogg_sync_wrote(&oy,1000); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body+1000, + og[1].body_len-1000); + ogg_sync_wrote(&oy,og[1].body_len-1000); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + + fprintf(stderr,"ok.\n"); + } + + /* Test fractional page inputs: page + incomplete capture */ + { + ogg_page og_de; + fprintf(stderr,"Testing sync on 1+partial inputs... "); + ogg_sync_reset(&oy); + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, + og[1].header_len); + ogg_sync_wrote(&oy,og[1].header_len); + + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, + og[1].body_len); + ogg_sync_wrote(&oy,og[1].body_len); + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, + 20); + ogg_sync_wrote(&oy,20); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header+20, + og[1].header_len-20); + ogg_sync_wrote(&oy,og[1].header_len-20); + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, + og[1].body_len); + ogg_sync_wrote(&oy,og[1].body_len); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + + fprintf(stderr,"ok.\n"); + } + + /* Test recapture: garbage + page */ + { + ogg_page og_de; + fprintf(stderr,"Testing search for capture... "); + ogg_sync_reset(&oy); + + /* 'garbage' */ + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, + og[1].body_len); + ogg_sync_wrote(&oy,og[1].body_len); + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, + og[1].header_len); + ogg_sync_wrote(&oy,og[1].header_len); + + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, + og[1].body_len); + ogg_sync_wrote(&oy,og[1].body_len); + + memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header, + 20); + ogg_sync_wrote(&oy,20); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + + memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header+20, + og[2].header_len-20); + ogg_sync_wrote(&oy,og[2].header_len-20); + memcpy(ogg_sync_buffer(&oy,og[2].body_len),og[2].body, + og[2].body_len); + ogg_sync_wrote(&oy,og[2].body_len); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + + fprintf(stderr,"ok.\n"); + } + + /* Test recapture: page + garbage + page */ + { + ogg_page og_de; + fprintf(stderr,"Testing recapture... "); + ogg_sync_reset(&oy); + + memcpy(ogg_sync_buffer(&oy,og[1].header_len),og[1].header, + og[1].header_len); + ogg_sync_wrote(&oy,og[1].header_len); + + memcpy(ogg_sync_buffer(&oy,og[1].body_len),og[1].body, + og[1].body_len); + ogg_sync_wrote(&oy,og[1].body_len); + + memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header, + og[2].header_len); + ogg_sync_wrote(&oy,og[2].header_len); + + memcpy(ogg_sync_buffer(&oy,og[2].header_len),og[2].header, + og[2].header_len); + ogg_sync_wrote(&oy,og[2].header_len); + + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + + memcpy(ogg_sync_buffer(&oy,og[2].body_len),og[2].body, + og[2].body_len-5); + ogg_sync_wrote(&oy,og[2].body_len-5); + + memcpy(ogg_sync_buffer(&oy,og[3].header_len),og[3].header, + og[3].header_len); + ogg_sync_wrote(&oy,og[3].header_len); + + memcpy(ogg_sync_buffer(&oy,og[3].body_len),og[3].body, + og[3].body_len); + ogg_sync_wrote(&oy,og[3].body_len); + + if(ogg_sync_pageout(&oy,&og_de)>0)error(); + if(ogg_sync_pageout(&oy,&og_de)<=0)error(); + + fprintf(stderr,"ok.\n"); + } + + /* Free page data that was previously copied */ + { + for(i=0;i<5;i++){ + free_page(&og[i]); + } + } + } + + return(0); +} + +#endif + + + + diff --git a/Engine/lib/libtheora/CHANGES b/Engine/lib/libtheora/CHANGES new file mode 100644 index 000000000..74183d91b --- /dev/null +++ b/Engine/lib/libtheora/CHANGES @@ -0,0 +1,169 @@ +libtheora 1.0 (2008 November 3) + + - Merge x86 assembly for forward DCT from Thusnelda branch. + - Update 32 bit MMX with loop filter fix. + - Check for an uninitialized state before dereferencing in propagating + decode calls. + - Remove all TH_DEBUG statements. + - Rename the bitpacker source files copied from libogg to avoid + confusing simple build systems using both libraries. + - Declare bitfield entries to be explicitly signed for Solaris cc. + - Set quantization parameters to default values when an empty buffer is + passed with TH_ENCCTL_SET_QUANT_PARAMS. + - Split encoder and decoder tests depending on configure settings. + - Return lstylex.sty to the distribution. + - Disable inline assembly on gcc versions prior to 3.1. + - Remove extern references for OC_*_QUANT_MIN. + - Make various data tables static const so they can be read-only. + - Remove ENCCTL codes from the old encoder API. + - Implement TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE ctl. + - Fix segfault when exactly one of the width or height is not a multiple + of 16, but the other is. + - Compute the correct vertical offset for chroma. + - cpuid assembly fix for MSVC. + - Add VS2008 project files. + - Build updates for 64-bit platforms, Mingw32, VS and XCode. + - Do not clobber the cropping rectangle. + - Declare ourselves 1.0final to pkg-config to sort after beta releases. + - Fix the scons build to include asm in libtheoradec/enc. + +libtheora 1.0beta3 (2008 April 16) + + - Build new libtheoradec and libtheoraenc libraries + supporting the new API from theora-exp. This API should + not be considered stable yet. + - Change granule_frame() to return an index as documented. + This is a change of behaviour from 1.0beta1. + - Document that granule_time() returns the end of the + presentation interval. + - Use a custom copy of the libogg bitpacker in the decoder + to avoid function call overhead. + - MMX code improved and ported to MSVC. + - Fix a problem with the MMX code on SELinux. + - Fix a problem with decoder quantizer initialization. + - Fix a page queue problem with png2theora. + - Improved robustness. + - Updated VS2005 project files. + - Dropped build support for Microsoft VS2003. + - Dropped build support for the unreleased libogg2. + - Added the specification to the autotools build. + - Specification corrections. + +libtheora 1.0beta2 (2007 October 12) + + - Fix a crash bug on char-is-unsigned architectures (PowerPC) + - Fix a buffer sizing issue that caused rare encoder crashes + - Fix a buffer alignment issue + - Build fixes for MingW32, MSVC + - Improved format documentation. + +libtheora 1.0beta1 (2007 September 22) + + - Granulepos scheme modified to match other codecs. This bumps + the bitstream revision to 3.2.1. Bitstreams marked 3.2.0 are + handled correctly by this decoder. Older decoders will show + a one frame sync error in the less noticable direction. + +libtheora 1.0alpha8 (2007 September 18) + + - Switch to new spec compliant decoder from theora-exp branch. + Written by Dr. Timothy Terriberry. + - Add support to the encoder for using quantization settings + provided by the application. + - more assembly optimizations + +libtheora 1.0alpha7 (2006 June 20) + + - Enable mmx assembly by default + - Avoid some relocations that caused problems on SELinux + - Other build fixes + - time testing mode (-f) for the dump_video example + +libtheora 1.0alpha6 (2006 May 30) + + * Merge theora-mmx simd acceleration (x86_32 and x86_64) + * Major RTP payload specification update + * Minor format specification updates + * Fix some spurious calls to free() instead of _ogg_free() + * Fix invalid array indexing in PixelLineSearch() + * Improve robustness against invalid input + * General warning cleanup + * The offset_y member now means what every application thought it meant + (offset from the top). This will mean some old files (those with a + non-centered image created with a buggy encoder) will display differently. + +libtheora 1.0alpha5 (2005 August 20) + + * Fixed bitrate management bugs that caused popping and encode + errors + * Fixed a crash problem with the theora_state internals not + being intialized properly. + * new utility function: + - theora_granule_shift() + * dump_video example now makes YUV4MPEG files by default, so + the results can be fed back to encoder_example and similar + tools. The old behavior is restored through the '-r' switch. + * ./configure now prints a summary + * simple unit test of the comment api under 'make check' + * misc code cleanup, warning and leak fixes + +libtheora 1.0alpha4 (2004 December 15) + + * first draft of the Theora I Format Specification + * API documentation generated from theora.h with Doxygen + * fix a double-update bug in the motion analysis + * apply the loop filter before filling motion vector border + in the reference frame + * new utility functions: + - theora_packet_isheader(), + - theora_packet_iskeyframe() + - theora_granule_frame() + * optional support for building without floating point + * optional support for building without encode support + * various build and packaging fixes + * pkg-config support + * SymbianOS build support + +libtheora 1.0alpha3 (2004 March 20) + + UPDATE: on 2004 July 1 the Theora I bitstream format was frozen. Files + produced by the libtheora 1.0alpha3 reference encoder will always be + decodable by the Theora I spec. + + * Bitstream info header FORMAT CHANGES: + - move the granulepos shift field to maintain byte alignment longer. + - reserve 5 additional bits for subsampling and interlace flags. + * Bitstream setup header FORMAT CHANGES: + - support for a range of interpolated quant matricies. + - include the in-loop block filter coeff. + * Bitsteam data packet FORMAT CHANGES: + - Reserve a bit for per-block Q index selection. + - Flip the coded image orientation for compatibility with VP3. + This allows lossless transcoding of VP3 content, but files + encoded with earlier theora releases would play upside down. + * example VP3 lossless transcoder + * optional support for libogg2 + * timing improvements in the example player + * packaging and build system updates and fixes + +libtheora 1.0alpha2 (2003 June 9) + + * bitstream FORMAT CHANGES: + - store the quant tables in a third setup header for + future encoder flexibility + - store the huffman tables in the third setup header + - add a field for marking the colorspace to the info header + - add crop parameters for non-multiple-of-16 frame sizes + - add a second vorbiscomment-style metadata header + * API changes to handle multiple headers with a single + theora_decode_header() call, like libvorbis + * code cleanup and minor fixes + * new dump_video code example/utility + * experimental win32 code examples + +libtheora 1.0alpha1 (2002 September 25) + + * First release of the theora reference implementation + * Port of the newly opened VP3 code to the Ogg container + * Rewrite of the code for portability and to use the libogg bitpacker + diff --git a/Engine/lib/libtheora/COPYING b/Engine/lib/libtheora/COPYING new file mode 100644 index 000000000..5a711972d --- /dev/null +++ b/Engine/lib/libtheora/COPYING @@ -0,0 +1,28 @@ +Copyright (C) 2002-2008 Xiph.Org Foundation and contributors. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- Neither the name of the Xiph.Org Foundation nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Engine/lib/libtheora/include/theora/codec.h b/Engine/lib/libtheora/include/theora/codec.h new file mode 100644 index 000000000..afdc1b0fa --- /dev/null +++ b/Engine/lib/libtheora/include/theora/codec.h @@ -0,0 +1,594 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: theora.h,v 1.8 2004/03/15 22:17:32 derf Exp $ + + ********************************************************************/ + +/**\mainpage + * + * \section intro Introduction + * + * This is the documentation for libtheora C API. + * The current reference + * implementation for Theora, a free, + * patent-unencumbered video codec. + * Theora is derived from On2's VP3 codec with additional features and + * integration for Ogg multimedia formats by + * the Xiph.Org Foundation. + * Complete documentation of the format itself is available in + * the Theora + * specification. + * + * \subsection Organization + * + * The functions documented here are actually subdivided into three + * separate libraries: + * - libtheoraenc contains the encoder interface, + * described in \ref encfuncs. + * - libtheoradec contains the decoder interface and + * routines shared with the encoder. + * You must also link to this if you link to libtheoraenc. + * The routines in this library are described in \ref decfuncs and + * \ref basefuncs. + * - libtheora contains the \ref oldfuncs. + * + * New code should link to libtheoradec and, if using encoder + * features, libtheoraenc. Together these two export both + * the standard and the legacy API, so this is all that is needed by + * any code. The older libtheora library is provided just for + * compatibility with older build configurations. + * + * In general the recommended 1.x API symbols can be distinguished + * by their th_ or TH_ namespace prefix. + * The older, legacy API uses theora_ or OC_ + * prefixes instead. + */ + +/**\file + * The shared libtheoradec and libtheoraenc C API. + * You don't need to include this directly.*/ + +#if !defined(_O_THEORA_CODEC_H_) +# define _O_THEORA_CODEC_H_ (1) +# include + +#if defined(__cplusplus) +extern "C" { +#endif + + + +/**\name Return codes*/ +/*@{*/ +/**An invalid pointer was provided.*/ +#define TH_EFAULT (-1) +/**An invalid argument was provided.*/ +#define TH_EINVAL (-10) +/**The contents of the header were incomplete, invalid, or unexpected.*/ +#define TH_EBADHEADER (-20) +/**The header does not belong to a Theora stream.*/ +#define TH_ENOTFORMAT (-21) +/**The bitstream version is too high.*/ +#define TH_EVERSION (-22) +/**The specified function is not implemented.*/ +#define TH_EIMPL (-23) +/**There were errors in the video data packet.*/ +#define TH_EBADPACKET (-24) +/**The decoded packet represented a dropped frame. + The player can continue to display the current frame, as the contents of the + decoded frame buffer have not changed.*/ +#define TH_DUPFRAME (1) +/*@}*/ + +/**The currently defined color space tags. + * See the Theora + * specification, Chapter 4, for exact details on the meaning of each of + * these color spaces.*/ +typedef enum{ + /**The color space was not specified at the encoder. + It may be conveyed by an external means.*/ + TH_CS_UNSPECIFIED, + /**A color space designed for NTSC content.*/ + TH_CS_ITU_REC_470M, + /**A color space designed for PAL/SECAM content.*/ + TH_CS_ITU_REC_470BG, + /**The total number of currently defined color spaces.*/ + TH_CS_NSPACES +}th_colorspace; + +/**The currently defined pixel format tags. + * See the Theora + * specification, Section 4.4, for details on the precise sample + * locations.*/ +typedef enum{ + /**Chroma decimation by 2 in both the X and Y directions (4:2:0). + The Cb and Cr chroma planes are half the width and half the height of the + luma plane.*/ + TH_PF_420, + /**Currently reserved.*/ + TH_PF_RSVD, + /**Chroma decimation by 2 in the X direction (4:2:2). + The Cb and Cr chroma planes are half the width of the luma plane, but full + height.*/ + TH_PF_422, + /**No chroma decimation (4:4:4). + The Cb and Cr chroma planes are full width and full height.*/ + TH_PF_444, + /**The total number of currently defined pixel formats.*/ + TH_PF_NFORMATS +}th_pixel_fmt; + + + +/**A buffer for a single color plane in an uncompressed image. + * This contains the image data in a left-to-right, top-down format. + * Each row of pixels is stored contiguously in memory, but successive rows + * need not be. + * Use \a stride to compute the offset of the next row. + * The encoder accepts both positive \a stride values (top-down in memory) and + * negative (bottom-up in memory). + * The decoder currently always generates images with positive strides.*/ +typedef struct{ + /**The width of this plane.*/ + int width; + /**The height of this plane.*/ + int height; + /**The offset in bytes between successive rows.*/ + int stride; + /**A pointer to the beginning of the first row.*/ + unsigned char *data; +}th_img_plane; + +/**A complete image buffer for an uncompressed frame. + * The chroma planes may be decimated by a factor of two in either direction, + * as indicated by th_info#pixel_fmt. + * The width and height of the Y' plane must be multiples of 16. + * They may need to be cropped for display, using the rectangle specified by + * th_info#pic_x, th_info#pic_y, th_info#pic_width, and + * th_info#pic_height. + * All samples are 8 bits. + * \note The term YUV often used to describe a colorspace is ambiguous. + * The exact parameters of the RGB to YUV conversion process aside, in many + * contexts the U and V channels actually have opposite meanings. + * To avoid this confusion, we are explicit: the name of the color channels are + * Y'CbCr, and they appear in that order, always. + * The prime symbol denotes that the Y channel is non-linear. + * Cb and Cr stand for "Chroma blue" and "Chroma red", respectively.*/ +typedef th_img_plane th_ycbcr_buffer[3]; + +/**Theora bitstream information. + * This contains the basic playback parameters for a stream, and corresponds to + * the initial 'info' header packet. + * To initialize an encoder, the application fills in this structure and + * passes it to th_encode_alloc(). + * A default encoding mode is chosen based on the values of the #quality and + * #target_bitrate fields. + * On decode, it is filled in by th_decode_headerin(), and then passed to + * th_decode_alloc(). + * + * Encoded Theora frames must be a multiple of 16 in size; + * this is what the #frame_width and #frame_height members represent. + * To handle arbitrary picture sizes, a crop rectangle is specified in the + * #pic_x, #pic_y, #pic_width and #pic_height members. + * + * All frame buffers contain pointers to the full, padded frame. + * However, the current encoder will not reference pixels outside of + * the cropped picture region, and the application does not need to fill them + * in. + * The decoder will allocate storage for a full frame, but the + * application should not rely on the padding containing sensible + * data. + * + * It is also generally recommended that the offsets and sizes should still be + * multiples of 2 to avoid chroma sampling shifts when chroma is sub-sampled. + * See the Theora + * specification, Section 4.4, for more details. + * + * Frame rate, in frames per second, is stored as a rational fraction, as is + * the pixel aspect ratio. + * Note that this refers to the aspect ratio of the individual pixels, not of + * the overall frame itself. + * The frame aspect ratio can be computed from pixel aspect ratio using the + * image dimensions.*/ +typedef struct{ + /**\name Theora version + * Bitstream version information.*/ + /*@{*/ + unsigned char version_major; + unsigned char version_minor; + unsigned char version_subminor; + /*@}*/ + /**The encoded frame width. + * This must be a multiple of 16, and less than 1048576.*/ + ogg_uint32_t frame_width; + /**The encoded frame height. + * This must be a multiple of 16, and less than 1048576.*/ + ogg_uint32_t frame_height; + /**The displayed picture width. + * This must be no larger than width.*/ + ogg_uint32_t pic_width; + /**The displayed picture height. + * This must be no larger than height.*/ + ogg_uint32_t pic_height; + /**The X offset of the displayed picture. + * This must be no larger than #frame_width-#pic_width or 255, whichever is + * smaller.*/ + ogg_uint32_t pic_x; + /**The Y offset of the displayed picture. + * This must be no larger than #frame_height-#pic_height, and + * #frame_height-#pic_height-#pic_y must be no larger than 255. + * This slightly funny restriction is due to the fact that the offset is + * specified from the top of the image for consistency with the standard + * graphics left-handed coordinate system used throughout this API, while it + * is stored in the encoded stream as an offset from the bottom.*/ + ogg_uint32_t pic_y; + /**\name Frame rate + * The frame rate, as a fraction. + * If either is 0, the frame rate is undefined.*/ + /*@{*/ + ogg_uint32_t fps_numerator; + ogg_uint32_t fps_denominator; + /*@}*/ + /**\name Aspect ratio + * The aspect ratio of the pixels. + * If either value is zero, the aspect ratio is undefined. + * If not specified by any external means, 1:1 should be assumed. + * The aspect ratio of the full picture can be computed as + * \code + * aspect_numerator*pic_width/(aspect_denominator*pic_height). + * \endcode */ + /*@{*/ + ogg_uint32_t aspect_numerator; + ogg_uint32_t aspect_denominator; + /*@}*/ + /**The color space.*/ + th_colorspace colorspace; + /**The pixel format.*/ + th_pixel_fmt pixel_fmt; + /**The target bit-rate in bits per second. + If initializing an encoder with this struct, set this field to a non-zero + value to activate CBR encoding by default.*/ + /*TODO: Current encoder does not support CBR mode, or anything like it. + We also don't really know what nominal rate each quality level + corresponds to yet.*/ + int target_bitrate; + /**The target quality level. + Valid values range from 0 to 63, inclusive, with higher values giving + higher quality. + If initializing an encoder with this struct, and #target_bitrate is set + to zero, VBR encoding at this quality will be activated by default.*/ + /*Currently this is set so that a qi of 0 corresponds to distortions of 24 + times the JND, and each increase by 16 halves that value. + This gives us fine discrimination at low qualities, yet effective rate + control at high qualities. + The qi value 63 is special, however. + For this, the highest quality, we use one half of a JND for our threshold. + Due to the lower bounds placed on allowable quantizers in Theora, we will + not actually be able to achieve quality this good, but this should + provide as close to visually lossless quality as Theora is capable of. + We could lift the quantizer restrictions without breaking VP3.1 + compatibility, but this would result in quantized coefficients that are + too large for the current bitstream to be able to store. + We'd have to redesign the token syntax to store these large coefficients, + which would make transcoding complex.*/ + int quality; + /**The amount to shift to extract the last keyframe number from the granule + * position. + * This can be at most 31. + * th_info_init() will set this to a default value (currently 6, + * which is good for streaming applications), but you can set it to 0 to + * make every frame a keyframe. + * The maximum distance between key frames is + * 1<<#keyframe_granule_shift. + * The keyframe frequency can be more finely controlled with + * #TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE, which can also be adjusted + * during encoding (for example, to force the next frame to be a keyframe), + * but it cannot be set larger than the amount permitted by this field after + * the headers have been output.*/ + int keyframe_granule_shift; +}th_info; + +/**The comment information. + * + * This structure holds the in-stream metadata corresponding to + * the 'comment' header packet. + * The comment header is meant to be used much like someone jotting a quick + * note on the label of a video. + * It should be a short, to the point text note that can be more than a couple + * words, but not more than a short paragraph. + * + * The metadata is stored as a series of (tag, value) pairs, in + * length-encoded string vectors. + * The first occurrence of the '=' character delimits the tag and value. + * A particular tag may occur more than once, and order is significant. + * The character set encoding for the strings is always UTF-8, but the tag + * names are limited to ASCII, and treated as case-insensitive. + * See the Theora + * specification, Section 6.3.3 for details. + * + * In filling in this structure, th_decode_headerin() will null-terminate + * the user_comment strings for safety. + * However, the bitstream format itself treats them as 8-bit clean vectors, + * possibly containing null characters, and so the length array should be + * treated as their authoritative length. + */ +typedef struct th_comment{ + /**The array of comment string vectors.*/ + char **user_comments; + /**An array of the corresponding length of each vector, in bytes.*/ + int *comment_lengths; + /**The total number of comment strings.*/ + int comments; + /**The null-terminated vendor string. + This identifies the software used to encode the stream.*/ + char *vendor; +}th_comment; + + + +/**A single base matrix.*/ +typedef unsigned char th_quant_base[64]; + +/**A set of \a qi ranges.*/ +typedef struct{ + /**The number of ranges in the set.*/ + int nranges; + /**The size of each of the #nranges ranges. + These must sum to 63.*/ + const int *sizes; + /**#nranges +1 base matrices. + Matrices \a i and i+1 form the endpoints of range \a i.*/ + const th_quant_base *base_matrices; +}th_quant_ranges; + +/**A complete set of quantization parameters. + The quantizer for each coefficient is calculated as: + \code + Q=MAX(MIN(qmin[qti][ci!=0],scale[ci!=0][qi]*base[qti][pli][qi][ci]/100), + 1024). + \endcode + + \a qti is the quantization type index: 0 for intra, 1 for inter. + ci!=0 is 0 for the DC coefficient and 1 for AC coefficients. + \a qi is the quality index, ranging between 0 (low quality) and 63 (high + quality). + \a pli is the color plane index: 0 for Y', 1 for Cb, 2 for Cr. + \a ci is the DCT coefficient index. + Coefficient indices correspond to the normal 2D DCT block + ordering--row-major with low frequencies first--\em not zig-zag order. + + Minimum quantizers are constant, and are given by: + \code + qmin[2][2]={{4,2},{8,4}}. + \endcode + + Parameters that can be stored in the bitstream are as follows: + - The two scale matrices ac_scale and dc_scale. + \code + scale[2][64]={dc_scale,ac_scale}. + \endcode + - The base matrices for each \a qi, \a qti and \a pli (up to 384 in all). + In order to avoid storing a full 384 base matrices, only a sparse set of + matrices are stored, and the rest are linearly interpolated. + This is done as follows. + For each \a qti and \a pli, a series of \a n \a qi ranges is defined. + The size of each \a qi range can vary arbitrarily, but they must sum to + 63. + Then, n+1 matrices are specified, one for each endpoint of the + ranges. + For interpolation purposes, each range's endpoints are the first \a qi + value it contains and one past the last \a qi value it contains. + Fractional values are rounded to the nearest integer, with ties rounded + away from zero. + + Base matrices are stored by reference, so if the same matrices are used + multiple times, they will only appear once in the bitstream. + The bitstream is also capable of omitting an entire set of ranges and + its associated matrices if they are the same as either the previous + set (indexed in row-major order) or if the inter set is the same as the + intra set. + + - Loop filter limit values. + The same limits are used for the loop filter in all color planes, despite + potentially differing levels of quantization in each. + + For the current encoder, scale[ci!=0][qi] must be no greater + than scale[ci!=0][qi-1] and base[qti][pli][qi][ci] must + be no greater than base[qti][pli][qi-1][ci]. + These two conditions ensure that the actual quantizer for a given \a qti, + \a pli, and \a ci does not increase as \a qi increases. + This is not required by the decoder.*/ +typedef struct{ + /**The DC scaling factors.*/ + ogg_uint16_t dc_scale[64]; + /**The AC scaling factors.*/ + ogg_uint16_t ac_scale[64]; + /**The loop filter limit values.*/ + unsigned char loop_filter_limits[64]; + /**The \a qi ranges for each \a ci and \a pli.*/ + th_quant_ranges qi_ranges[2][3]; +}th_quant_info; + + + +/**The number of Huffman tables used by Theora.*/ +#define TH_NHUFFMAN_TABLES (80) +/**The number of DCT token values in each table.*/ +#define TH_NDCT_TOKENS (32) + +/**A Huffman code for a Theora DCT token. + * Each set of Huffman codes in a given table must form a complete, prefix-free + * code. + * There is no requirement that all the tokens in a table have a valid code, + * but the current encoder is not optimized to take advantage of this. + * If each of the five grouops of 16 tables does not contain at least one table + * with a code for every token, then the encoder may fail to encode certain + * frames. + * The complete table in the first group of 16 does not have to be in the same + * place as the complete table in the other groups, but the complete tables in + * the remaining four groups must all be in the same place.*/ +typedef struct{ + /**The bit pattern for the code, with the LSbit of the pattern aligned in + * the LSbit of the word.*/ + ogg_uint32_t pattern; + /**The number of bits in the code. + * This must be between 0 and 32, inclusive.*/ + int nbits; +}th_huff_code; + + + +/**\defgroup basefuncs Functions Shared by Encode and Decode*/ +/*@{*/ +/**\name Basic shared functions*/ +/*@{*/ +/**Retrieves a human-readable string to identify the library vendor and + * version. + * \return the version string.*/ +extern const char *th_version_string(void); +/**Retrieves the library version number. + * This is the highest bitstream version that the encoder library will produce, + * or that the decoder library can decode. + * This number is composed of a 16-bit major version, 8-bit minor version + * and 8 bit sub-version, composed as follows: + * \code + * (VERSION_MAJOR<<16)+(VERSION_MINOR<<8)+(VERSION_SUBMINOR) + * \endcode + * \return the version number.*/ +extern ogg_uint32_t th_version_number(void); +/**Converts a granule position to an absolute frame index, starting at + * 0. + * The granule position is interpreted in the context of a given + * #th_enc_ctx or #th_dec_ctx handle (either will suffice). + * \param _encdec A previously allocated #th_enc_ctx or #th_dec_ctx + * handle. + * \param _granpos The granule position to convert. + * \returns The absolute frame index corresponding to \a _granpos. + * \retval -1 The given granule position was invalid (i.e. negative).*/ +extern ogg_int64_t th_granule_frame(void *_encdec,ogg_int64_t _granpos); +/**Converts a granule position to an absolute time in seconds. + * The granule position is interpreted in the context of a given + * #th_enc_ctx or #th_dec_ctx handle (either will suffice). + * \param _encdec A previously allocated #th_enc_ctx or #th_dec_ctx + * handle. + * \param _granpos The granule position to convert. + * \return The absolute time in seconds corresponding to \a _granpos. + * This is the "end time" for the frame, or the latest time it should + * be displayed. + * It is not the presentation time. + * \retval -1 The given granule position was invalid (i.e. negative).*/ +extern double th_granule_time(void *_encdec,ogg_int64_t _granpos); +/**Determines whether a Theora packet is a header or not. + * This function does no verification beyond checking the packet type bit, so + * it should not be used for bitstream identification; use + * th_decode_headerin() for that. + * As per the Theora specification, an empty (0-byte) packet is treated as a + * data packet (a delta frame with no coded blocks). + * \param _op An ogg_packet containing encoded Theora data. + * \retval 1 The packet is a header packet + * \retval 0 The packet is a video data packet.*/ +extern int th_packet_isheader(ogg_packet *_op); +/**Determines whether a theora packet is a key frame or not. + * This function does no verification beyond checking the packet type and + * key frame bits, so it should not be used for bitstream identification; use + * th_decode_headerin() for that. + * As per the Theora specification, an empty (0-byte) packet is treated as a + * delta frame (with no coded blocks). + * \param _op An ogg_packet containing encoded Theora data. + * \retval 1 The packet contains a key frame. + * \retval 0 The packet contains a delta frame. + * \retval -1 The packet is not a video data packet.*/ +extern int th_packet_iskeyframe(ogg_packet *_op); +/*@}*/ + + +/**\name Functions for manipulating header data*/ +/*@{*/ +/**Initializes a th_info structure. + * This should be called on a freshly allocated #th_info structure before + * attempting to use it. + * \param _info The #th_info struct to initialize.*/ +extern void th_info_init(th_info *_info); +/**Clears a #th_info structure. + * This should be called on a #th_info structure after it is no longer + * needed. + * \param _info The #th_info struct to clear.*/ +extern void th_info_clear(th_info *_info); + +/**Initialize a #th_comment structure. + * This should be called on a freshly allocated #th_comment structure + * before attempting to use it. + * \param _tc The #th_comment struct to initialize.*/ +extern void th_comment_init(th_comment *_tc); +/**Add a comment to an initialized #th_comment structure. + * \note Neither th_comment_add() nor th_comment_add_tag() support + * comments containing null values, although the bitstream format does + * support them. + * To add such comments you will need to manipulate the #th_comment + * structure directly. + * \param _tc The #th_comment struct to add the comment to. + * \param _comment Must be a null-terminated UTF-8 string containing the + * comment in "TAG=the value" form.*/ +extern void th_comment_add(th_comment *_tc, char *_comment); +/**Add a comment to an initialized #th_comment structure. + * \note Neither th_comment_add() nor th_comment_add_tag() support + * comments containing null values, although the bitstream format does + * support them. + * To add such comments you will need to manipulate the #th_comment + * structure directly. + * \param _tc The #th_comment struct to add the comment to. + * \param _tag A null-terminated string containing the tag associated with + * the comment. + * \param _val The corresponding value as a null-terminated string.*/ +extern void th_comment_add_tag(th_comment *_tc,char *_tag,char *_val); +/**Look up a comment value by its tag. + * \param _tc An initialized #th_comment structure. + * \param _tag The tag to look up. + * \param _count The instance of the tag. + * The same tag can appear multiple times, each with a distinct + * value, so an index is required to retrieve them all. + * The order in which these values appear is significant and + * should be preserved. + * Use th_comment_query_count() to get the legal range for + * the \a _count parameter. + * \return A pointer to the queried tag's value. + * This points directly to data in the #th_comment structure. + * It should not be modified or freed by the application, and + * modifications to the structure may invalidate the pointer. + * \retval NULL If no matching tag is found.*/ +extern char *th_comment_query(th_comment *_tc,char *_tag,int _count); +/**Look up the number of instances of a tag. + * Call this first when querying for a specific tag and then iterate over the + * number of instances with separate calls to th_comment_query() to + * retrieve all the values for that tag in order. + * \param _tc An initialized #th_comment structure. + * \param _tag The tag to look up. + * \return The number on instances of this particular tag.*/ +extern int th_comment_query_count(th_comment *_tc,char *_tag); +/**Clears a #th_comment structure. + * This should be called on a #th_comment structure after it is no longer + * needed. + * It will free all memory used by the structure members. + * \param _tc The #th_comment struct to clear.*/ +extern void th_comment_clear(th_comment *_tc); +/*@}*/ +/*@}*/ + + + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/Engine/lib/libtheora/include/theora/theora.h b/Engine/lib/libtheora/include/theora/theora.h new file mode 100644 index 000000000..dbef71675 --- /dev/null +++ b/Engine/lib/libtheora/include/theora/theora.h @@ -0,0 +1,794 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: theora.h,v 1.17 2003/12/06 18:06:19 arc Exp $ + + ********************************************************************/ + +#ifndef _O_THEORA_H_ +#define _O_THEORA_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#include /* for size_t */ + +#include + +/** \defgroup oldfuncs Legacy pre-1.0 C API */ +/* @{ */ + +/** \mainpage + * + * \section intro Introduction + * + * This is the documentation for the libtheora legacy C API, declared in + * the theora.h header, which describes the old interface used before + * the 1.0 release. This API was widely deployed for several years and + * remains supported, but for new code we recommend the cleaner API + * declared in theoradec.h and theoraenc.h. + * + * libtheora is the reference implementation for + * Theora, a free video codec. + * Theora is derived from On2's VP3 codec with improved integration for + * Ogg multimedia formats by Xiph.Org. + * + * \section overview Overview + * + * This library will both decode and encode theora packets to/from raw YUV + * frames. In either case, the packets will most likely either come from or + * need to be embedded in an Ogg stream. Use + * libogg or + * liboggz + * to extract/package these packets. + * + * \section decoding Decoding Process + * + * Decoding can be separated into the following steps: + * -# initialise theora_info and theora_comment structures using + * theora_info_init() and theora_comment_init(): + \verbatim + theora_info info; + theora_comment comment; + + theora_info_init(&info); + theora_comment_init(&comment); + \endverbatim + * -# retrieve header packets from Ogg stream (there should be 3) and decode + * into theora_info and theora_comment structures using + * theora_decode_header(). See \ref identification for more information on + * identifying which packets are theora packets. + \verbatim + int i; + for (i = 0; i < 3; i++) + { + (get a theora packet "op" from the Ogg stream) + theora_decode_header(&info, &comment, op); + } + \endverbatim + * -# initialise the decoder based on the information retrieved into the + * theora_info struct by theora_decode_header(). You will need a + * theora_state struct. + \verbatim + theora_state state; + + theora_decode_init(&state, &info); + \endverbatim + * -# pass in packets and retrieve decoded frames! See the yuv_buffer + * documentation for information on how to retrieve raw YUV data. + \verbatim + yuf_buffer buffer; + while (last packet was not e_o_s) { + (get a theora packet "op" from the Ogg stream) + theora_decode_packetin(&state, op); + theora_decode_YUVout(&state, &buffer); + } + \endverbatim + * + * + * \subsection identification Identifying Theora Packets + * + * All streams inside an Ogg file have a unique serial_no attached to the + * stream. Typically, you will want to + * - retrieve the serial_no for each b_o_s (beginning of stream) page + * encountered within the Ogg file; + * - test the first (only) packet on that page to determine if it is a theora + * packet; + * - once you have found a theora b_o_s page then use the retrieved serial_no + * to identify future packets belonging to the same theora stream. + * + * Note that you \e cannot use theora_packet_isheader() to determine if a + * packet is a theora packet or not, as this function does not perform any + * checking beyond whether a header bit is present. Instead, use the + * theora_decode_header() function and check the return value; or examine the + * header bytes at the beginning of the Ogg page. + * + * \subsection example Example Decoder + * + * See + * examples/dump_video.c for a simple decoder implementation. + * + * \section encoding Encoding Process + * + * See + * examples/encoder_example.c for a simple encoder implementation. + */ + +/** \file + * The libtheora pre-1.0 legacy C API. + */ + +/** + * A YUV buffer for passing uncompressed frames to and from the codec. + * This holds a Y'CbCr frame in planar format. The CbCr planes can be + * subsampled and have their own separate dimensions and row stride + * offsets. Note that the strides may be negative in some + * configurations. For theora the width and height of the largest plane + * must be a multiple of 16. The actual meaningful picture size and + * offset are stored in the theora_info structure; frames returned by + * the decoder may need to be cropped for display. + * + * All samples are 8 bits. Within each plane samples are ordered by + * row from the top of the frame to the bottom. Within each row samples + * are ordered from left to right. + * + * During decode, the yuv_buffer struct is allocated by the user, but all + * fields (including luma and chroma pointers) are filled by the library. + * These pointers address library-internal memory and their contents should + * not be modified. + * + * Conversely, during encode the user allocates the struct and fills out all + * fields. The user also manages the data addressed by the luma and chroma + * pointers. See the encoder_example.c and dump_video.c example files in + * theora/examples/ for more information. + */ +typedef struct { + int y_width; /**< Width of the Y' luminance plane */ + int y_height; /**< Height of the luminance plane */ + int y_stride; /**< Offset in bytes between successive rows */ + + int uv_width; /**< Width of the Cb and Cr chroma planes */ + int uv_height; /**< Height of the chroma planes */ + int uv_stride; /**< Offset between successive chroma rows */ + unsigned char *y; /**< Pointer to start of luminance data */ + unsigned char *u; /**< Pointer to start of Cb data */ + unsigned char *v; /**< Pointer to start of Cr data */ + +} yuv_buffer; + +/** + * A Colorspace. + */ +typedef enum { + OC_CS_UNSPECIFIED, /**< The colorspace is unknown or unspecified */ + OC_CS_ITU_REC_470M, /**< This is the best option for 'NTSC' content */ + OC_CS_ITU_REC_470BG, /**< This is the best option for 'PAL' content */ + OC_CS_NSPACES /**< This marks the end of the defined colorspaces */ +} theora_colorspace; + +/** + * A Chroma subsampling + * + * These enumerate the available chroma subsampling options supported + * by the theora format. See Section 4.4 of the specification for + * exact definitions. + */ +typedef enum { + OC_PF_420, /**< Chroma subsampling by 2 in each direction (4:2:0) */ + OC_PF_RSVD, /**< Reserved value */ + OC_PF_422, /**< Horizonatal chroma subsampling by 2 (4:2:2) */ + OC_PF_444, /**< No chroma subsampling at all (4:4:4) */ +} theora_pixelformat; + +/** + * Theora bitstream info. + * Contains the basic playback parameters for a stream, + * corresponding to the initial 'info' header packet. + * + * Encoded theora frames must be a multiple of 16 in width and height. + * To handle other frame sizes, a crop rectangle is specified in + * frame_height and frame_width, offset_x and * offset_y. The offset + * and size should still be a multiple of 2 to avoid chroma sampling + * shifts. Offset values in this structure are measured from the + * upper left of the image. + * + * Frame rate, in frames per second, is stored as a rational + * fraction. Aspect ratio is also stored as a rational fraction, and + * refers to the aspect ratio of the frame pixels, not of the + * overall frame itself. + * + * See + * examples/encoder_example.c for usage examples of the + * other paramters and good default settings for the encoder parameters. + */ +typedef struct { + ogg_uint32_t width; /**< encoded frame width */ + ogg_uint32_t height; /**< encoded frame height */ + ogg_uint32_t frame_width; /**< display frame width */ + ogg_uint32_t frame_height; /**< display frame height */ + ogg_uint32_t offset_x; /**< horizontal offset of the displayed frame */ + ogg_uint32_t offset_y; /**< vertical offset of the displayed frame */ + ogg_uint32_t fps_numerator; /**< frame rate numerator **/ + ogg_uint32_t fps_denominator; /**< frame rate denominator **/ + ogg_uint32_t aspect_numerator; /**< pixel aspect ratio numerator */ + ogg_uint32_t aspect_denominator; /**< pixel aspect ratio denominator */ + theora_colorspace colorspace; /**< colorspace */ + int target_bitrate; /**< nominal bitrate in bits per second */ + int quality; /**< Nominal quality setting, 0-63 */ + int quick_p; /**< Quick encode/decode */ + + /* decode only */ + unsigned char version_major; + unsigned char version_minor; + unsigned char version_subminor; + + void *codec_setup; + + /* encode only */ + int dropframes_p; + int keyframe_auto_p; + ogg_uint32_t keyframe_frequency; + ogg_uint32_t keyframe_frequency_force; /* also used for decode init to + get granpos shift correct */ + ogg_uint32_t keyframe_data_target_bitrate; + ogg_int32_t keyframe_auto_threshold; + ogg_uint32_t keyframe_mindistance; + ogg_int32_t noise_sensitivity; + ogg_int32_t sharpness; + + theora_pixelformat pixelformat; /**< chroma subsampling mode to expect */ + +} theora_info; + +/** Codec internal state and context. + */ +typedef struct{ + theora_info *i; + ogg_int64_t granulepos; + + void *internal_encode; + void *internal_decode; + +} theora_state; + +/** + * Comment header metadata. + * + * This structure holds the in-stream metadata corresponding to + * the 'comment' header packet. + * + * Meta data is stored as a series of (tag, value) pairs, in + * length-encoded string vectors. The first occurence of the + * '=' character delimits the tag and value. A particular tag + * may occur more than once. The character set encoding for + * the strings is always UTF-8, but the tag names are limited + * to case-insensitive ASCII. See the spec for details. + * + * In filling in this structure, theora_decode_header() will + * null-terminate the user_comment strings for safety. However, + * the bitstream format itself treats them as 8-bit clean, + * and so the length array should be treated as authoritative + * for their length. + */ +typedef struct theora_comment{ + char **user_comments; /**< An array of comment string vectors */ + int *comment_lengths; /**< An array of corresponding string vector lengths in bytes */ + int comments; /**< The total number of comment string vectors */ + char *vendor; /**< The vendor string identifying the encoder, null terminated */ + +} theora_comment; + + +/**\name theora_control() codes */ + +/**\anchor decctlcodes + * These are the available request codes for theora_control() + * when called with a decoder instance. + * By convention, these are odd, to distinguish them from the + * \ref encctlcodes "encoder control codes". + * Keep any experimental or vendor-specific values above \c 0x8000.*/ + +/**Get the maximum post-processing level. + * The decoder supports a post-processing filter that can improve + * the appearance of the decoded images. This returns the highest + * level setting for this post-processor, corresponding to maximum + * improvement and computational expense. + */ +#define TH_DECCTL_GET_PPLEVEL_MAX (1) + +/**Set the post-processing level. + * Sets the level of post-processing to use when decoding the + * compressed stream. This must be a value between zero (off) + * and the maximum returned by TH_DECCTL_GET_PPLEVEL_MAX. + */ +#define TH_DECCTL_SET_PPLEVEL (3) + +/**Sets the maximum distance between key frames. + * This can be changed during an encode, but will be bounded by + * 1<. + * If it is set before encoding begins, th_info#keyframe_granule_shift will + * be enlarged appropriately. + * + * \param[in] buf ogg_uint32_t: The maximum distance between key + * frames. + * \param[out] buf ogg_uint32_t: The actual maximum distance set. + * \retval TH_FAULT \a theora_state or \a buf is NULL. + * \retval TH_EINVAL \a buf_sz is not sizeof(ogg_uint32_t). + * \retval TH_IMPL Not supported by this implementation.*/ +#define TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE (4) + +/**Set the granule position. + * Call this after a seek, to update the internal granulepos + * in the decoder, to insure that subsequent frames are marked + * properly. If you track timestamps yourself and do not use + * the granule postion returned by the decoder, then you do + * not need to use this control. + */ +#define TH_DECCTL_SET_GRANPOS (5) + + +/**\anchor encctlcodes + * These are the available request codes for theora_control() + * when called with an encoder instance. + * By convention, these are even, to distinguish them from the + * \ref decctlcodes "decoder control codes". + * Keep any experimental or vendor-specific values above \c 0x8000.*/ +/*@{*/ +/**Sets the quantization parameters to use. + * The parameters are copied, not stored by reference, so they can be freed + * after this call. + * NULL may be specified to revert to the default parameters. + * For the current encoder, scale[ci!=0][qi] must be no greater than + * scale[ci!=0][qi-1] and base[qti][pli][qi][ci] must be no + * greater than base[qti][pli][qi-1][ci]. + * These two conditions ensure that the actual quantizer for a given \a qti, + * \a pli, and \a ci does not increase as \a qi increases. + * + * \param[in] buf #th_quant_info + * \retval TH_FAULT \a theora_state is NULL. + * \retval TH_EINVAL Encoding has already begun, the quantization parameters + * do not meet one of the above stated conditions, \a buf + * is NULL and \a buf_sz is not zero, or \a buf + * is non-NULL and \a buf_sz is not + * sizeof(#th_quant_info). + * \retval TH_IMPL Not supported by this implementation.*/ +#define TH_ENCCTL_SET_QUANT_PARAMS (2) +/**Disables any encoder features that would prevent lossless transcoding back + * to VP3. + * This primarily means disabling block-level QI values and not using 4MV mode + * when any of the luma blocks in a macro block are not coded. + * It also includes using the VP3 quantization tables and Huffman codes; if you + * set them explicitly after calling this function, the resulting stream will + * not be VP3-compatible. + * If you enable VP3-compatibility when encoding 4:2:2 or 4:4:4 source + * material, or when using a picture region smaller than the full frame (e.g. + * a non-multiple-of-16 width or height), then non-VP3 bitstream features will + * still be disabled, but the stream will still not be VP3-compatible, as VP3 + * was not capable of encoding such formats. + * If you call this after encoding has already begun, then the quantization + * tables and codebooks cannot be changed, but the frame-level features will + * be enabled or disabled as requested. + * + * \param[in] buf int: a non-zero value to enable VP3 compatibility, + * or 0 to disable it (the default). + * \param[out] buf int: 1 if all bitstream features required for + * VP3-compatibility could be set, and 0 otherwise. + * The latter will be returned if the pixel format is not + * 4:2:0, the picture region is smaller than the full frame, + * or if encoding has begun, preventing the quantization + * tables and codebooks from being set. + * \retval TH_FAULT \a theora_state or \a buf is NULL. + * \retval TH_EINVAL \a buf_sz is not sizeof(int). + * \retval TH_IMPL Not supported by this implementation.*/ +#define TH_ENCCTL_SET_VP3_COMPATIBLE (10) +/**Gets the maximum speed level. + * Higher speed levels favor quicker encoding over better quality per bit. + * Depending on the encoding mode, and the internal algorithms used, quality + * may actually improve, but in this case bitrate will also likely increase. + * In any case, overall rate/distortion performance will probably decrease. + * The maximum value, and the meaning of each value, may change depending on + * the current encoding mode (VBR vs. CQI, etc.). + * + * \param[out] buf int: The maximum encoding speed level. + * \retval TH_FAULT \a theora_state or \a buf is NULL. + * \retval TH_EINVAL \a buf_sz is not sizeof(int). + * \retval TH_IMPL Not supported by this implementation in the current + * encoding mode.*/ +#define TH_ENCCTL_GET_SPLEVEL_MAX (12) +/**Sets the speed level. + * By default a speed value of 1 is used. + * + * \param[in] buf int: The new encoding speed level. + * 0 is slowest, larger values use less CPU. + * \retval TH_FAULT \a theora_state or \a buf is NULL. + * \retval TH_EINVAL \a buf_sz is not sizeof(int), or the + * encoding speed level is out of bounds. + * The maximum encoding speed level may be + * implementation- and encoding mode-specific, and can be + * obtained via #TH_ENCCTL_GET_SPLEVEL_MAX. + * \retval TH_IMPL Not supported by this implementation in the current + * encoding mode.*/ +#define TH_ENCCTL_SET_SPLEVEL (14) +/*@}*/ + +#define OC_FAULT -1 /**< General failure */ +#define OC_EINVAL -10 /**< Library encountered invalid internal data */ +#define OC_DISABLED -11 /**< Requested action is disabled */ +#define OC_BADHEADER -20 /**< Header packet was corrupt/invalid */ +#define OC_NOTFORMAT -21 /**< Packet is not a theora packet */ +#define OC_VERSION -22 /**< Bitstream version is not handled */ +#define OC_IMPL -23 /**< Feature or action not implemented */ +#define OC_BADPACKET -24 /**< Packet is corrupt */ +#define OC_NEWPACKET -25 /**< Packet is an (ignorable) unhandled extension */ +#define OC_DUPFRAME 1 /**< Packet is a dropped frame */ + +/** + * Retrieve a human-readable string to identify the encoder vendor and version. + * \returns A version string. + */ +extern const char *theora_version_string(void); + +/** + * Retrieve a 32-bit version number. + * This number is composed of a 16-bit major version, 8-bit minor version + * and 8 bit sub-version, composed as follows: +
    +   (VERSION_MAJOR<<16) + (VERSION_MINOR<<8) + (VERSION_SUB)
    +
    +* \returns The version number. +*/ +extern ogg_uint32_t theora_version_number(void); + +/** + * Initialize the theora encoder. + * \param th The theora_state handle to initialize for encoding. + * \param ti A theora_info struct filled with the desired encoding parameters. + * \retval 0 Success + */ +extern int theora_encode_init(theora_state *th, theora_info *ti); + +/** + * Submit a YUV buffer to the theora encoder. + * \param t A theora_state handle previously initialized for encoding. + * \param yuv A buffer of YUV data to encode. Note that both the yuv_buffer + * struct and the luma/chroma buffers within should be allocated by + * the user. + * \retval OC_EINVAL Encoder is not ready, or is finished. + * \retval -1 The size of the given frame differs from those previously input + * \retval 0 Success + */ +extern int theora_encode_YUVin(theora_state *t, yuv_buffer *yuv); + +/** + * Request the next packet of encoded video. + * The encoded data is placed in a user-provided ogg_packet structure. + * \param t A theora_state handle previously initialized for encoding. + * \param last_p whether this is the last packet the encoder should produce. + * \param op An ogg_packet structure to fill. libtheora will set all + * elements of this structure, including a pointer to encoded + * data. The memory for the encoded data is owned by libtheora. + * \retval 0 No internal storage exists OR no packet is ready + * \retval -1 The encoding process has completed + * \retval 1 Success + */ +extern int theora_encode_packetout( theora_state *t, int last_p, + ogg_packet *op); + +/** + * Request a packet containing the initial header. + * A pointer to the header data is placed in a user-provided ogg_packet + * structure. + * \param t A theora_state handle previously initialized for encoding. + * \param op An ogg_packet structure to fill. libtheora will set all + * elements of this structure, including a pointer to the header + * data. The memory for the header data is owned by libtheora. + * \retval 0 Success + */ +extern int theora_encode_header(theora_state *t, ogg_packet *op); + +/** + * Request a comment header packet from provided metadata. + * A pointer to the comment data is placed in a user-provided ogg_packet + * structure. + * \param tc A theora_comment structure filled with the desired metadata + * \param op An ogg_packet structure to fill. libtheora will set all + * elements of this structure, including a pointer to the encoded + * comment data. The memory for the comment data is owned by + * libtheora. + * \retval 0 Success + */ +extern int theora_encode_comment(theora_comment *tc, ogg_packet *op); + +/** + * Request a packet containing the codebook tables for the stream. + * A pointer to the codebook data is placed in a user-provided ogg_packet + * structure. + * \param t A theora_state handle previously initialized for encoding. + * \param op An ogg_packet structure to fill. libtheora will set all + * elements of this structure, including a pointer to the codebook + * data. The memory for the header data is owned by libtheora. + * \retval 0 Success + */ +extern int theora_encode_tables(theora_state *t, ogg_packet *op); + +/** + * Decode an Ogg packet, with the expectation that the packet contains + * an initial header, comment data or codebook tables. + * + * \param ci A theora_info structure to fill. This must have been previously + * initialized with theora_info_init(). If \a op contains an initial + * header, theora_decode_header() will fill \a ci with the + * parsed header values. If \a op contains codebook tables, + * theora_decode_header() will parse these and attach an internal + * representation to \a ci->codec_setup. + * \param cc A theora_comment structure to fill. If \a op contains comment + * data, theora_decode_header() will fill \a cc with the parsed + * comments. + * \param op An ogg_packet structure which you expect contains an initial + * header, comment data or codebook tables. + * + * \retval OC_BADHEADER \a op is NULL; OR the first byte of \a op->packet + * has the signature of an initial packet, but op is + * not a b_o_s packet; OR this packet has the signature + * of an initial header packet, but an initial header + * packet has already been seen; OR this packet has the + * signature of a comment packet, but the initial header + * has not yet been seen; OR this packet has the signature + * of a comment packet, but contains invalid data; OR + * this packet has the signature of codebook tables, + * but the initial header or comments have not yet + * been seen; OR this packet has the signature of codebook + * tables, but contains invalid data; + * OR the stream being decoded has a compatible version + * but this packet does not have the signature of a + * theora initial header, comments, or codebook packet + * \retval OC_VERSION The packet data of \a op is an initial header with + * a version which is incompatible with this version of + * libtheora. + * \retval OC_NEWPACKET the stream being decoded has an incompatible (future) + * version and contains an unknown signature. + * \retval 0 Success + * + * \note The normal usage is that theora_decode_header() be called on the + * first three packets of a theora logical bitstream in succession. + */ +extern int theora_decode_header(theora_info *ci, theora_comment *cc, + ogg_packet *op); + +/** + * Initialize a theora_state handle for decoding. + * \param th The theora_state handle to initialize. + * \param c A theora_info struct filled with the desired decoding parameters. + * This is of course usually obtained from a previous call to + * theora_decode_header(). + * \retval 0 Success + */ +extern int theora_decode_init(theora_state *th, theora_info *c); + +/** + * Input a packet containing encoded data into the theora decoder. + * \param th A theora_state handle previously initialized for decoding. + * \param op An ogg_packet containing encoded theora data. + * \retval 0 Success + * \retval OC_BADPACKET \a op does not contain encoded video data + */ +extern int theora_decode_packetin(theora_state *th,ogg_packet *op); + +/** + * Output the next available frame of decoded YUV data. + * \param th A theora_state handle previously initialized for decoding. + * \param yuv A yuv_buffer in which libtheora should place the decoded data. + * Note that the buffer struct itself is allocated by the user, but + * that the luma and chroma pointers will be filled in by the + * library. Also note that these luma and chroma regions should be + * considered read-only by the user. + * \retval 0 Success + */ +extern int theora_decode_YUVout(theora_state *th,yuv_buffer *yuv); + +/** + * Report whether a theora packet is a header or not + * This function does no verification beyond checking the header + * flag bit so it should not be used for bitstream identification; + * use theora_decode_header() for that. + * + * \param op An ogg_packet containing encoded theora data. + * \retval 1 The packet is a header packet + * \retval 0 The packet is not a header packet (and so contains frame data) + * + * Thus function was added in the 1.0alpha4 release. + */ +extern int theora_packet_isheader(ogg_packet *op); + +/** + * Report whether a theora packet is a keyframe or not + * + * \param op An ogg_packet containing encoded theora data. + * \retval 1 The packet contains a keyframe image + * \retval 0 The packet is contains an interframe delta + * \retval -1 The packet is not an image data packet at all + * + * Thus function was added in the 1.0alpha4 release. + */ +extern int theora_packet_iskeyframe(ogg_packet *op); + +/** + * Report the granulepos shift radix + * + * When embedded in Ogg, Theora uses a two-part granulepos, + * splitting the 64-bit field into two pieces. The more-significant + * section represents the frame count at the last keyframe, + * and the less-significant section represents the count of + * frames since the last keyframe. In this way the overall + * field is still non-decreasing with time, but usefully encodes + * a pointer to the last keyframe, which is necessary for + * correctly restarting decode after a seek. + * + * This function reports the number of bits used to represent + * the distance to the last keyframe, and thus how the granulepos + * field must be shifted or masked to obtain the two parts. + * + * Since libtheora returns compressed data in an ogg_packet + * structure, this may be generally useful even if the Theora + * packets are not being used in an Ogg container. + * + * \param ti A previously initialized theora_info struct + * \returns The bit shift dividing the two granulepos fields + * + * This function was added in the 1.0alpha5 release. + */ +int theora_granule_shift(theora_info *ti); + +/** + * Convert a granulepos to an absolute frame index, starting at 0. + * The granulepos is interpreted in the context of a given theora_state handle. + * + * Note that while the granulepos encodes the frame count (i.e. starting + * from 1) this call returns the frame index, starting from zero. Thus + * One can calculate the presentation time by multiplying the index by + * the rate. + * + * \param th A previously initialized theora_state handle (encode or decode) + * \param granulepos The granulepos to convert. + * \returns The frame index corresponding to \a granulepos. + * \retval -1 The given granulepos is undefined (i.e. negative) + * + * Thus function was added in the 1.0alpha4 release. + */ +extern ogg_int64_t theora_granule_frame(theora_state *th,ogg_int64_t granulepos); + +/** + * Convert a granulepos to absolute time in seconds. The granulepos is + * interpreted in the context of a given theora_state handle, and gives + * the end time of a frame's presentation as used in Ogg mux ordering. + * + * \param th A previously initialized theora_state handle (encode or decode) + * \param granulepos The granulepos to convert. + * \returns The absolute time in seconds corresponding to \a granulepos. + * This is the "end time" for the frame, or the latest time it should + * be displayed. + * It is not the presentation time. + * \retval -1. The given granulepos is undefined (i.e. negative), or + * \retval -1. The function has been disabled because floating + * point support is not available. + */ +extern double theora_granule_time(theora_state *th,ogg_int64_t granulepos); + +/** + * Initialize a theora_info structure. All values within the given theora_info + * structure are initialized, and space is allocated within libtheora for + * internal codec setup data. + * \param c A theora_info struct to initialize. + */ +extern void theora_info_init(theora_info *c); + +/** + * Clear a theora_info structure. All values within the given theora_info + * structure are cleared, and associated internal codec setup data is freed. + * \param c A theora_info struct to initialize. + */ +extern void theora_info_clear(theora_info *c); + +/** + * Free all internal data associated with a theora_state handle. + * \param t A theora_state handle. + */ +extern void theora_clear(theora_state *t); + +/** + * Initialize an allocated theora_comment structure + * \param tc An allocated theora_comment structure + **/ +extern void theora_comment_init(theora_comment *tc); + +/** + * Add a comment to an initialized theora_comment structure + * \param tc A previously initialized theora comment structure + * \param comment A null-terminated string encoding the comment in the form + * "TAG=the value" + * + * Neither theora_comment_add() nor theora_comment_add_tag() support + * comments containing null values, although the bitstream format + * supports this. To add such comments you will need to manipulate + * the theora_comment structure directly. + **/ + +extern void theora_comment_add(theora_comment *tc, char *comment); + +/** + * Add a comment to an initialized theora_comment structure. + * \param tc A previously initialized theora comment structure + * \param tag A null-terminated string containing the tag + * associated with the comment. + * \param value The corresponding value as a null-terminated string + * + * Neither theora_comment_add() nor theora_comment_add_tag() support + * comments containing null values, although the bitstream format + * supports this. To add such comments you will need to manipulate + * the theora_comment structure directly. + **/ +extern void theora_comment_add_tag(theora_comment *tc, + char *tag, char *value); + +/** + * Look up a comment value by tag. + * \param tc Tn initialized theora_comment structure + * \param tag The tag to look up + * \param count The instance of the tag. The same tag can appear multiple + * times, each with a distinct and ordered value, so an index + * is required to retrieve them all. + * \returns A pointer to the queried tag's value + * \retval NULL No matching tag is found + * + * \note Use theora_comment_query_count() to get the legal range for the + * count parameter. + **/ + +extern char *theora_comment_query(theora_comment *tc, char *tag, int count); + +/** Look up the number of instances of a tag. + * \param tc An initialized theora_comment structure + * \param tag The tag to look up + * \returns The number on instances of a particular tag. + * + * Call this first when querying for a specific tag and then interate + * over the number of instances with separate calls to + * theora_comment_query() to retrieve all instances in order. + **/ +extern int theora_comment_query_count(theora_comment *tc, char *tag); + +/** + * Clear an allocated theora_comment struct so that it can be freed. + * \param tc An allocated theora_comment structure. + **/ +extern void theora_comment_clear(theora_comment *tc); + +/**Encoder control function. + * This is used to provide advanced control the encoding process. + * \param th A #theora_state handle. + * \param req The control code to process. + * See \ref encctlcodes "the list of available control codes" + * for details. + * \param buf The parameters for this control code. + * \param buf_sz The size of the parameter buffer.*/ +extern int theora_control(theora_state *th,int req,void *buf,size_t buf_sz); + +/* @} */ /* end oldfuncs doxygen group */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _O_THEORA_H_ */ diff --git a/Engine/lib/libtheora/include/theora/theoradec.h b/Engine/lib/libtheora/include/theora/theoradec.h new file mode 100644 index 000000000..7c08caadf --- /dev/null +++ b/Engine/lib/libtheora/include/theora/theoradec.h @@ -0,0 +1,307 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: theora.h,v 1.8 2004/03/15 22:17:32 derf Exp $ + + ********************************************************************/ + +/**\file + * The libtheoradec C decoding API.*/ + +#if !defined(_O_THEORA_THEORADEC_H_) +# define _O_THEORA_THEORADEC_H_ (1) +# include +# include +# include "codec.h" + +#if defined(__cplusplus) +extern "C" { +#endif + + + +/**\name th_decode_ctl() codes + * \anchor decctlcodes + * These are the available request codes for th_decode_ctl(). + * By convention, these are odd, to distinguish them from the + * \ref encctlcodes "encoder control codes". + * Keep any experimental or vendor-specific values above \c 0x8000.*/ +/*@{*/ +/**Gets the maximum post-processing level. + * + * \param[out] _buf int: The maximum post-processing level. + * \retval TH_EFAULT \a _dec_ctx or \a _buf is NULL. + * \retval TH_EINVAL \a _buf_sz is not sizeof(int). + * \retval TH_EIMPL Not supported by this implementation.*/ +#define TH_DECCTL_GET_PPLEVEL_MAX (1) +/**Sets the post-processing level. + * By default, post-processing is disabled. + * + * \param[in] _buf int: The new post-processing level. + * 0 to disable; larger values use more CPU. + * \retval TH_EFAULT \a _dec_ctx or \a _buf is NULL. + * \retval TH_EINVAL \a _buf_sz is not sizeof(int), or the + * post-processing level is out of bounds. + * The maximum post-processing level may be + * implementation-specific, and can be obtained via + * #TH_DECCTL_GET_PPLEVEL_MAX. + * \retval TH_EIMPL Not supported by this implementation.*/ +#define TH_DECCTL_SET_PPLEVEL (3) +/**Sets the granule position. + * Call this after a seek, before decoding the first frame, to ensure that the + * proper granule position is returned for all subsequent frames. + * If you track timestamps yourself and do not use the granule position + * returned by the decoder, then you need not call this function. + * + * \param[in] _buf ogg_int64_t: The granule position of the next + * frame. + * \retval TH_EFAULT \a _dec_ctx or \a _buf is NULL. + * \retval TH_EINVAL \a _buf_sz is not sizeof(ogg_int64_t), or the + * granule position is negative.*/ +#define TH_DECCTL_SET_GRANPOS (5) +/**Sets the striped decode callback function. + * If set, this function will be called as each piece of a frame is fully + * decoded in th_decode_packetin(). + * You can pass in a #th_stripe_callback with + * th_stripe_callback#stripe_decoded set to NULL to disable the + * callbacks at any point. + * Enabling striped decode does not prevent you from calling + * th_decode_ycbcr_out() after the frame is fully decoded. + * + * \param[in] _buf #th_stripe_callback: The callback parameters. + * \retval TH_EFAULT \a _dec_ctx or \a _buf is NULL. + * \retval TH_EINVAL \a _buf_sz is not + * sizeof(th_stripe_callback).*/ +#define TH_DECCTL_SET_STRIPE_CB (7) +/*@}*/ + + + +/**A callback function for striped decode. + * This is a function pointer to an application-provided function that will be + * called each time a section of the image is fully decoded in + * th_decode_packetin(). + * This allows the application to process the section immediately, while it is + * still in cache. + * Note that the frame is decoded bottom to top, so \a _yfrag0 will steadily + * decrease with each call until it reaches 0, at which point the full frame + * is decoded. + * The number of fragment rows made available in each call depends on the pixel + * format and the number of post-processing filters enabled, and may not even + * be constant for the entire frame. + * If a non-NULL \a _granpos pointer is passed to + * th_decode_packetin(), the granule position for the frame will be stored + * in it before the first callback is made. + * If an entire frame is dropped (a 0-byte packet), then no callbacks will be + * made at all for that frame. + * \param _ctx An application-provided context pointer. + * \param _buf The image buffer for the decoded frame. + * \param _yfrag0 The Y coordinate of the first row of 8x8 fragments + * decoded. + * Multiply this by 8 to obtain the pixel row number in the + * luma plane. + * If the chroma planes are subsampled in the Y direction, + * this will always be divisible by two. + * \param _yfrag_end The Y coordinate of the first row of 8x8 fragments past + * the newly decoded section. + * If the chroma planes are subsampled in the Y direction, + * this will always be divisible by two. + * I.e., this section contains fragment rows + * \a _yfrag0 ...\a _yfrag_end -1.*/ +typedef void (*th_stripe_decoded_func)(void *_ctx,th_ycbcr_buffer _buf, + int _yfrag0,int _yfrag_end); + +/**The striped decode callback data to pass to #TH_DECCTL_SET_STRIPE_CB.*/ +typedef struct{ + /**An application-provided context pointer. + * This will be passed back verbatim to the application.*/ + void *ctx; + /**The callback function pointer.*/ + th_stripe_decoded_func stripe_decoded; +}th_stripe_callback; + + + +/**\name Decoder state + The following data structures are opaque, and their contents are not + publicly defined by this API. + Referring to their internals directly is unsupported, and may break without + warning.*/ +/*@{*/ +/**The decoder context.*/ +typedef struct th_dec_ctx th_dec_ctx; +/**Setup information. + This contains auxiliary information (Huffman tables and quantization + parameters) decoded from the setup header by th_decode_headerin() to be + passed to th_decode_alloc(). + It can be re-used to initialize any number of decoders, and can be freed + via th_setup_free() at any time.*/ +typedef struct th_setup_info th_setup_info; +/*@}*/ + + + +/**\defgroup decfuncs Functions for Decoding*/ +/*@{*/ +/**\name Functions for decoding + * You must link to libtheoradec if you use any of the + * functions in this section. + * + * The functions are listed in the order they are used in a typical decode. + * The basic steps are: + * - Parse the header packets by repeatedly calling th_decode_headerin(). + * - Allocate a #th_dec_ctx handle with th_decode_alloc(). + * - Call th_setup_free() to free any memory used for codec setup + * information. + * - Perform any additional decoder configuration with th_decode_ctl(). + * - For each video data packet: + * - Submit the packet to the decoder via th_decode_packetin(). + * - Retrieve the uncompressed video data via th_decode_ycbcr_out(). + * - Call th_decode_free() to release all decoder memory.*/ +/*@{*/ +/**Decodes the header packets of a Theora stream. + * This should be called on the initial packets of the stream, in succession, + * until it returns 0, indicating that all headers have been + * processed, or an error is encountered. + * At least three header packets are required, and additional optional header + * packets may follow. + * This can be used on the first packet of any logical stream to determine if + * that stream is a Theora stream. + * \param _info A #th_info structure to fill in. + * This must have been previously initialized with + * th_info_init(). + * The application may immediately begin using the contents of + * this structure after the first header is decoded, though it + * must continue to be passed in on all subsequent calls. + * \param _tc A #th_comment structure to fill in. + * The application may immediately begin using the contents of + * this structure after the second header is decoded, though it + * must continue to be passed in on all subsequent calls. + * \param _setup Returns a pointer to additional, private setup information + * needed by the decoder. + * The contents of this pointer must be initialized to + * NULL on the first call, and the returned value must + * continue to be passed in on all subsequent calls. + * \param _op An ogg_packet structure which contains one of the + * initial packets of an Ogg logical stream. + * \return A positive value indicates that a Theora header was successfully + * processed. + * \retval 0 The first video data packet was encountered after all + * required header packets were parsed. + * The packet just passed in on this call should be saved + * and fed to th_decode_packetin() to begin decoding + * video data. + * \retval TH_EFAULT One of \a _info, \a _tc, or \a _setup was + * NULL. + * \retval TH_EBADHEADER \a _op was NULL, the packet was not the next + * header packet in the expected sequence, or the format + * of the header data was invalid. + * \retval TH_EVERSION The packet data was a Theora info header, but for a + * bitstream version not decodable with this version of + * libtheoradec. + * \retval TH_ENOTFORMAT The packet was not a Theora header. + */ +extern int th_decode_headerin(th_info *_info,th_comment *_tc, + th_setup_info **_setup,ogg_packet *_op); +/**Allocates a decoder instance. + * + * Security Warning: The Theora format supports very large frame sizes, + * potentially even larger than the address space of a 32-bit machine, and + * creating a decoder context allocates the space for several frames of data. + * If the allocation fails here, your program will crash, possibly at some + * future point because the OS kernel returned a valid memory range and will + * only fail when it tries to map the pages in it the first time they are + * used. + * Even if it succeeds, you may experience a denial of service if the frame + * size is large enough to cause excessive paging. + * If you are integrating libtheora in a larger application where such things + * are undesirable, it is highly recommended that you check the frame size in + * \a _info before calling this function and refuse to decode streams where it + * is larger than some reasonable maximum. + * libtheora will not check this for you, because there may be machines that + * can handle such streams and applications that wish to. + * \param _info A #th_info struct filled via th_decode_headerin(). + * \param _setup A #th_setup_info handle returned via + * th_decode_headerin(). + * \return The initialized #th_dec_ctx handle. + * \retval NULL If the decoding parameters were invalid.*/ +extern th_dec_ctx *th_decode_alloc(const th_info *_info, + const th_setup_info *_setup); +/**Releases all storage used for the decoder setup information. + * This should be called after you no longer want to create any decoders for + * a stream whose headers you have parsed with th_decode_headerin(). + * \param _setup The setup information to free. + * This can safely be NULL.*/ +extern void th_setup_free(th_setup_info *_setup); +/**Decoder control function. + * This is used to provide advanced control of the decoding process. + * \param _dec A #th_dec_ctx handle. + * \param _req The control code to process. + * See \ref decctlcodes "the list of available control codes" + * for details. + * \param _buf The parameters for this control code. + * \param _buf_sz The size of the parameter buffer.*/ +extern int th_decode_ctl(th_dec_ctx *_dec,int _req,void *_buf, + size_t _buf_sz); +/**Submits a packet containing encoded video data to the decoder. + * \param _dec A #th_dec_ctx handle. + * \param _op An ogg_packet containing encoded video data. + * \param _granpos Returns the granule position of the decoded packet. + * If non-NULL, the granule position for this specific + * packet is stored in this location. + * This is computed incrementally from previously decoded + * packets. + * After a seek, the correct granule position must be set via + * #TH_DECCTL_SET_GRANPOS for this to work properly. + * \retval 0 Success. + * A new decoded frame can be retrieved by calling + * th_decode_ycbcr_out(). + * \retval TH_DUPFRAME The packet represented a dropped (0-byte) frame. + * The player can skip the call to th_decode_ycbcr_out(), + * as the contents of the decoded frame buffer have not + * changed. + * \retval TH_EFAULT \a _dec or \a _op was NULL. + * \retval TH_EBADPACKET \a _op does not contain encoded video data. + * \retval TH_EIMPL The video data uses bitstream features which this + * library does not support.*/ +extern int th_decode_packetin(th_dec_ctx *_dec,const ogg_packet *_op, + ogg_int64_t *_granpos); +/**Outputs the next available frame of decoded Y'CbCr data. + * If a striped decode callback has been set with #TH_DECCTL_SET_STRIPE_CB, + * then the application does not need to call this function. + * \param _dec A #th_dec_ctx handle. + * \param _ycbcr A video buffer structure to fill in. + * libtheoradec will fill in all the members of this + * structure, including the pointers to the uncompressed video + * data. + * The memory for this video data is owned by + * libtheoradec. + * It may be freed or overwritten without notification when + * subsequent frames are decoded. + * \retval 0 Success + */ +extern int th_decode_ycbcr_out(th_dec_ctx *_dec, + th_ycbcr_buffer _ycbcr); +/**Frees an allocated decoder instance. + * \param _dec A #th_dec_ctx handle.*/ +extern void th_decode_free(th_dec_ctx *_dec); +/*@}*/ +/*@}*/ + + + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/Engine/lib/libtheora/include/theora/theoraenc.h b/Engine/lib/libtheora/include/theora/theoraenc.h new file mode 100644 index 000000000..b98285862 --- /dev/null +++ b/Engine/lib/libtheora/include/theora/theoraenc.h @@ -0,0 +1,266 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2003 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: theora.h,v 1.8 2004/03/15 22:17:32 derf Exp $ + + ********************************************************************/ + +/**\file + * The libtheoraenc C encoding API.*/ + +#if !defined(_O_THEORA_THEORAENC_H_) +# define _O_THEORA_THEORAENC_H_ (1) +# include +# include +# include "codec.h" + +#if defined(__cplusplus) +extern "C" { +#endif + + + +/**\name th_encode_ctl() codes + * \anchor encctlcodes + * These are the available request codes for th_encode_ctl(). + * By convention, these are even, to distinguish them from the + * \ref decctlcodes "decoder control codes". + * Keep any experimental or vendor-specific values above \c 0x8000.*/ +/*@{*/ +/**Sets the Huffman tables to use. + * The tables are copied, not stored by reference, so they can be freed after + * this call. + * NULL may be specified to revert to the default tables. + * + * \param[in] _buf #th_huff_code[#TH_NHUFFMAN_TABLES][#TH_NDCT_TOKENS] + * \retval TH_EFAULT \a _enc_ctx is NULL. + * \retval TH_EINVAL Encoding has already begun or one or more of the given + * tables is not full or prefix-free, \a _buf is + * NULL and \a _buf_sz is not zero, or \a _buf is + * non-NULL and \a _buf_sz is not + * sizeof(#th_huff_code)*#TH_NHUFFMAN_TABLES*#TH_NDCT_TOKENS. + * \retval TH_IMPL Not supported by this implementation.*/ +#define TH_ENCCTL_SET_HUFFMAN_CODES (0) +/**Sets the quantization parameters to use. + * The parameters are copied, not stored by reference, so they can be freed + * after this call. + * NULL may be specified to revert to the default parameters. + * For the current encoder, scale[ci!=0][qi] must be no greater than + * scale[ci!=0][qi-1] and base[qti][pli][qi][ci] must be no + * greater than base[qti][pli][qi-1][ci]. + * These two conditions ensure that the actual quantizer for a given \a qti, + * \a pli, and \a ci does not increase as \a qi increases. + * + * \param[in] _buf #th_quant_info + * \retval TH_EFAULT \a _enc_ctx is NULL. + * \retval TH_EINVAL Encoding has already begun, the quantization parameters + * do not meet one of the above stated conditions, \a _buf + * is NULL and \a _buf_sz is not zero, or \a _buf + * is non-NULL and \a _buf_sz is not + * sizeof(#th_quant_info). + * \retval TH_IMPL Not supported by this implementation.*/ +#define TH_ENCCTL_SET_QUANT_PARAMS (2) +/**Sets the maximum distance between key frames. + * This can be changed during an encode, but will be bounded by + * 1<. + * If it is set before encoding begins, th_info#keyframe_granule_shift will + * be enlarged appropriately. + * + * \param[in] _buf ogg_uint32_t: The maximum distance between key + * frames. + * \param[out] _buf ogg_uint32_t: The actual maximum distance set. + * \retval TH_EFAULT \a _enc_ctx or \a _buf is NULL. + * \retval TH_EINVAL \a _buf_sz is not sizeof(ogg_uint32_t). + * \retval TH_IMPL Not supported by this implementation.*/ +#define TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE (4) +/**Disables any encoder features that would prevent lossless transcoding back + * to VP3. + * This primarily means disabling block-level QI values and not using 4MV mode + * when any of the luma blocks in a macro block are not coded. + * It also includes using the VP3 quantization tables and Huffman codes; if you + * set them explicitly after calling this function, the resulting stream will + * not be VP3-compatible. + * If you enable VP3-compatibility when encoding 4:2:2 or 4:4:4 source + * material, or when using a picture region smaller than the full frame (e.g. + * a non-multiple-of-16 width or height), then non-VP3 bitstream features will + * still be disabled, but the stream will still not be VP3-compatible, as VP3 + * was not capable of encoding such formats. + * If you call this after encoding has already begun, then the quantization + * tables and codebooks cannot be changed, but the frame-level features will + * be enabled or disabled as requested. + * + * \param[in] _buf int: a non-zero value to enable VP3 compatibility, + * or 0 to disable it (the default). + * \param[out] _buf int: 1 if all bitstream features required for + * VP3-compatibility could be set, and 0 otherwise. + * The latter will be returned if the pixel format is not + * 4:2:0, the picture region is smaller than the full frame, + * or if encoding has begun, preventing the quantization + * tables and codebooks from being set. + * \retval TH_EFAULT \a _enc_ctx or \a _buf is NULL. + * \retval TH_EINVAL \a _buf_sz is not sizeof(int). + * \retval TH_IMPL Not supported by this implementation.*/ +#define TH_ENCCTL_SET_VP3_COMPATIBLE (10) +/**Gets the maximum speed level. + * Higher speed levels favor quicker encoding over better quality per bit. + * Depending on the encoding mode, and the internal algorithms used, quality + * may actually improve, but in this case bitrate will also likely increase. + * In any case, overall rate/distortion performance will probably decrease. + * The maximum value, and the meaning of each value, may change depending on + * the current encoding mode (VBR vs. CQI, etc.). + * + * \param[out] _buf int: The maximum encoding speed level. + * \retval TH_EFAULT \a _enc_ctx or \a _buf is NULL. + * \retval TH_EINVAL \a _buf_sz is not sizeof(int). + * \retval TH_IMPL Not supported by this implementation in the current + * encoding mode.*/ +#define TH_ENCCTL_GET_SPLEVEL_MAX (12) +/**Sets the speed level. + * By default, the slowest speed (0) is used. + * + * \param[in] _buf int: The new encoding speed level. + * 0 is slowest, larger values use less CPU. + * \retval TH_EFAULT \a _enc_ctx or \a _buf is NULL. + * \retval TH_EINVAL \a _buf_sz is not sizeof(int), or the + * encoding speed level is out of bounds. + * The maximum encoding speed level may be + * implementation- and encoding mode-specific, and can be + * obtained via #TH_ENCCTL_GET_SPLEVEL_MAX. + * \retval TH_IMPL Not supported by this implementation in the current + * encoding mode.*/ +#define TH_ENCCTL_SET_SPLEVEL (14) +/*@}*/ + + + +/**The quantization parameters used by VP3.*/ +extern const th_quant_info TH_VP31_QUANT_INFO; + +/**The Huffman tables used by VP3.*/ +extern const th_huff_code + TH_VP31_HUFF_CODES[TH_NHUFFMAN_TABLES][TH_NDCT_TOKENS]; + + + +/**\name Encoder state + The following data structure is opaque, and its contents are not publicly + defined by this API. + Referring to its internals directly is unsupported, and may break without + warning.*/ +/*@{*/ +/**The encoder context.*/ +typedef struct th_enc_ctx th_enc_ctx; +/*@}*/ + + + +/**\defgroup encfuncs Functions for Encoding*/ +/*@{*/ +/**\name Functions for encoding + * You must link to libtheoraenc and libtheoradec + * if you use any of the functions in this section. + * + * The functions are listed in the order they are used in a typical encode. + * The basic steps are: + * - Fill in a #th_info structure with details on the format of the video you + * wish to encode. + * - Allocate a #th_enc_ctx handle with th_encode_alloc(). + * - Perform any additional encoder configuration required with + * th_encode_ctl(). + * - Repeatedly call th_encode_flushheader() to retrieve all the header + * packets. + * - For each uncompressed frame: + * - Submit the uncompressed frame via th_encode_ycbcr_in() + * - Repeatedly call th_encode_packetout() to retrieve any video data packets + * that are ready. + * - Call th_encode_free() to release all encoder memory.*/ +/*@{*/ +/**Allocates an encoder instance. + * \param _info A #th_info struct filled with the desired encoding parameters. + * \return The initialized #th_enc_ctx handle. + * \retval NULL If the encoding parameters were invalid.*/ +extern th_enc_ctx *th_encode_alloc(const th_info *_info); +/**Encoder control function. + * This is used to provide advanced control the encoding process. + * \param _enc A #th_enc_ctx handle. + * \param _req The control code to process. + * See \ref encctlcodes "the list of available control codes" + * for details. + * \param _buf The parameters for this control code. + * \param _buf_sz The size of the parameter buffer.*/ +extern int th_encode_ctl(th_enc_ctx *_enc,int _req,void *_buf,size_t _buf_sz); +/**Outputs the next header packet. + * This should be called repeatedly after encoder initialization until it + * returns 0 in order to get all of the header packets, in order, before + * encoding actual video data. + * \param _enc A #th_enc_ctx handle. + * \param _comments The metadata to place in the comment header, when it is + * encoded. + * \param _op An ogg_packet structure to fill. + * All of the elements of this structure will be set, + * including a pointer to the header data. + * The memory for the header data is owned by + * libtheoraenc, and may be invalidated when the + * next encoder function is called. + * \return A positive value indicates that a header packet was successfully + * produced. + * \retval 0 No packet was produced, and no more header packets remain. + * \retval TH_EFAULT \a _enc, \a _comments, or \a _op was NULL.*/ +extern int th_encode_flushheader(th_enc_ctx *_enc, + th_comment *_comments,ogg_packet *_op); +/**Submits an uncompressed frame to the encoder. + * \param _enc A #th_enc_ctx handle. + * \param _ycbcr A buffer of Y'CbCr data to encode. + * \retval 0 Success. + * \retval TH_EFAULT \a _enc or \a _ycbcr is NULL. + * \retval TH_EINVAL The buffer size does not match the frame size the encoder + * was initialized with, or encoding has already + * completed.*/ +extern int th_encode_ycbcr_in(th_enc_ctx *_enc,th_ycbcr_buffer _ycbcr); +/**Retrieves encoded video data packets. + * This should be called repeatedly after each frame is submitted to flush any + * encoded packets, until it returns 0. + * The encoder will not buffer these packets as subsequent frames are + * compressed, so a failure to do so will result in lost video data. + * \note Currently the encoder operates in a one-frame-in, one-packet-out + * manner. + * However, this may be changed in the future. + * \param _enc A #th_enc_ctx handle. + * \param _last Set this flag to a non-zero value if no more uncompressed + * frames will be submitted. + * This ensures that a proper EOS flag is set on the last packet. + * \param _op An ogg_packet structure to fill. + * All of the elements of this structure will be set, including a + * pointer to the video data. + * The memory for the video data is owned by + * libtheoraenc, and may be invalidated when the next + * encoder function is called. + * \return A positive value indicates that a video data packet was successfully + * produced. + * \retval 0 No packet was produced, and no more encoded video data + * remains. + * \retval TH_EFAULT \a _enc or \a _op was NULL.*/ +extern int th_encode_packetout(th_enc_ctx *_enc,int _last,ogg_packet *_op); +/**Frees an allocated encoder instance. + * \param _enc A #th_enc_ctx handle.*/ +extern void th_encode_free(th_enc_ctx *_enc); +/*@}*/ +/*@}*/ + + + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/Engine/lib/libtheora/lib/cpu.c b/Engine/lib/libtheora/lib/cpu.c new file mode 100644 index 000000000..8da50d070 --- /dev/null +++ b/Engine/lib/libtheora/lib/cpu.c @@ -0,0 +1,227 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2008 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + CPU capability detection for x86 processors. + Originally written by Rudolf Marek. + + function: + last mod: $Id: cpu.c 15427 2008-10-21 02:36:19Z xiphmont $ + + ********************************************************************/ + +#include "cpu.h" + +#if !defined(USE_ASM) +static ogg_uint32_t oc_cpu_flags_get(void){ + return 0; +} +#else +# if !defined(_MSC_VER) +# if defined(__amd64__)||defined(__x86_64__) +/*On x86-64, gcc seems to be able to figure out how to save %rbx for us when + compiling with -fPIC.*/ +# define cpuid(_op,_eax,_ebx,_ecx,_edx) \ + __asm__ __volatile__( \ + "cpuid\n\t" \ + :[eax]"=a"(_eax),[ebx]"=b"(_ebx),[ecx]"=c"(_ecx),[edx]"=d"(_edx) \ + :"a"(_op) \ + :"cc" \ + ) +# else +/*On x86-32, not so much.*/ +# define cpuid(_op,_eax,_ebx,_ecx,_edx) \ + __asm__ __volatile__( \ + "xchgl %%ebx,%[ebx]\n\t" \ + "cpuid\n\t" \ + "xchgl %%ebx,%[ebx]\n\t" \ + :[eax]"=a"(_eax),[ebx]"=r"(_ebx),[ecx]"=c"(_ecx),[edx]"=d"(_edx) \ + :"a"(_op) \ + :"cc" \ + ) +# endif +# else +/*Why does MSVC need this complicated rigamarole? + At this point I honestly do not care.*/ + +/*Visual C cpuid helper function. + For VS2005 we could as well use the _cpuid builtin, but that wouldn't work + for VS2003 users, so we do it in inline assembler.*/ +static void oc_cpuid_helper(ogg_uint32_t _cpu_info[4],ogg_uint32_t _op){ + _asm{ + mov eax,[_op] + mov esi,_cpu_info + cpuid + mov [esi+0],eax + mov [esi+4],ebx + mov [esi+8],ecx + mov [esi+12],edx + } +} + +# define cpuid(_op,_eax,_ebx,_ecx,_edx) \ + do{ \ + ogg_uint32_t cpu_info[4]; \ + oc_cpuid_helper(cpu_info,_op); \ + (_eax)=cpu_info[0]; \ + (_ebx)=cpu_info[1]; \ + (_ecx)=cpu_info[2]; \ + (_edx)=cpu_info[3]; \ + }while(0) + +static void oc_detect_cpuid_helper(ogg_uint32_t *_eax,ogg_uint32_t *_ebx){ + _asm{ + pushfd + pushfd + pop eax + mov ebx,eax + xor eax,200000h + push eax + popfd + pushfd + pop eax + popfd + mov ecx,_eax + mov [ecx],eax + mov ecx,_ebx + mov [ecx],ebx + } +} +# endif + +static ogg_uint32_t oc_parse_intel_flags(ogg_uint32_t _edx,ogg_uint32_t _ecx){ + ogg_uint32_t flags; + /*If there isn't even MMX, give up.*/ + if(!(_edx&0x00800000))return 0; + flags=OC_CPU_X86_MMX; + if(_edx&0x02000000)flags|=OC_CPU_X86_MMXEXT|OC_CPU_X86_SSE; + if(_edx&0x04000000)flags|=OC_CPU_X86_SSE2; + if(_ecx&0x00000001)flags|=OC_CPU_X86_PNI; + if(_ecx&0x00000100)flags|=OC_CPU_X86_SSSE3; + if(_ecx&0x00080000)flags|=OC_CPU_X86_SSE4_1; + if(_ecx&0x00100000)flags|=OC_CPU_X86_SSE4_2; + return flags; +} + +static ogg_uint32_t oc_parse_amd_flags(ogg_uint32_t _edx,ogg_uint32_t _ecx){ + ogg_uint32_t flags; + /*If there isn't even MMX, give up.*/ + if(!(_edx&0x00800000))return 0; + flags=OC_CPU_X86_MMX; + if(_edx&0x00400000)flags|=OC_CPU_X86_MMXEXT; + if(_edx&0x80000000)flags|=OC_CPU_X86_3DNOW; + if(_edx&0x40000000)flags|=OC_CPU_X86_3DNOWEXT; + if(_ecx&0x00000040)flags|=OC_CPU_X86_SSE4A; + if(_ecx&0x00000800)flags|=OC_CPU_X86_SSE5; + return flags; +} + +static ogg_uint32_t oc_cpu_flags_get(void){ + ogg_uint32_t flags; + ogg_uint32_t eax; + ogg_uint32_t ebx; + ogg_uint32_t ecx; + ogg_uint32_t edx; +# if !defined(__amd64__)&&!defined(__x86_64__) + /*Not all x86-32 chips support cpuid, so we have to check.*/ +# if !defined(_MSC_VER) + __asm__ __volatile__( + "pushfl\n\t" + "pushfl\n\t" + "popl %[a]\n\t" + "movl %[a],%[b]\n\t" + "xorl $0x200000,%[a]\n\t" + "pushl %[a]\n\t" + "popfl\n\t" + "pushfl\n\t" + "popl %[a]\n\t" + "popfl\n\t" + :[a]"=r"(eax),[b]"=r"(ebx) + : + :"cc" + ); +# else + oc_detect_cpuid_helper(&eax,&ebx); +# endif + /*No cpuid.*/ + if(eax==ebx)return 0; +# endif + cpuid(0,eax,ebx,ecx,edx); + /* l e t n I e n i u n e G*/ + if(ecx==0x6C65746E&&edx==0x49656E69&&ebx==0x756E6547|| + /* 6 8 x M T e n i u n e G*/ + ecx==0x3638784D&&edx==0x54656E69&&ebx==0x756E6547){ + /*Intel, Transmeta (tested with Crusoe TM5800):*/ + cpuid(1,eax,ebx,ecx,edx); + flags=oc_parse_intel_flags(edx,ecx); + } + /* D M A c i t n e h t u A*/ + else if(ecx==0x444D4163&&edx==0x69746E65&&ebx==0x68747541|| + /* C S N y b e d o e G*/ + ecx==0x43534E20&&edx==0x79622065&&ebx==0x646F6547){ + /*AMD, Geode:*/ + cpuid(0x80000000,eax,ebx,ecx,edx); + if(eax<0x80000001)flags=0; + else{ + cpuid(0x80000001,eax,ebx,ecx,edx); + flags=oc_parse_amd_flags(edx,ecx); + } + /*Also check for SSE.*/ + cpuid(1,eax,ebx,ecx,edx); + flags|=oc_parse_intel_flags(edx,ecx); + } + /*Technically some VIA chips can be configured in the BIOS to return any + string here the user wants. + There is a special detection method that can be used to identify such + processors, but in my opinion, if the user really wants to change it, they + deserve what they get.*/ + /* s l u a H r u a t n e C*/ + else if(ecx==0x736C7561&&edx==0x48727561&&ebx==0x746E6543){ + /*VIA:*/ + /*I only have documentation for the C7 (Esther) and Isaiah (forthcoming) + chips (thanks to the engineers from Centaur Technology who provided it). + These chips support Intel-like cpuid info. + The C3-2 (Nehemiah) cores appear to, as well.*/ + cpuid(1,eax,ebx,ecx,edx); + flags=oc_parse_intel_flags(edx,ecx); + cpuid(0x80000000,eax,ebx,ecx,edx); + if(eax>=0x80000001){ + /*The (non-Nehemiah) C3 processors support AMD-like cpuid info. + We need to check this even if the Intel test succeeds to pick up 3DNow! + support on these processors. + Unlike actual AMD processors, we cannot _rely_ on this info, since + some cores (e.g., the 693 stepping of the Nehemiah) claim to support + this function, yet return edx=0, despite the Intel test indicating + MMX support. + Therefore the features detected here are strictly added to those + detected by the Intel test.*/ + /*TODO: How about earlier chips?*/ + cpuid(0x80000001,eax,ebx,ecx,edx); + /*Note: As of the C7, this function returns Intel-style extended feature + flags, not AMD-style. + Currently, this only defines bits 11, 20, and 29 (0x20100800), which + do not conflict with any of the AMD flags we inspect. + For the remaining bits, Intel tells us, "Do not count on their value", + but VIA assures us that they will all be zero (at least on the C7 and + Isaiah chips). + In the (unlikely) event a future processor uses bits 18, 19, 30, or 31 + (0xC0C00000) for something else, we will have to add code to detect + the model to decide when it is appropriate to inspect them.*/ + flags|=oc_parse_amd_flags(edx,ecx); + } + } + else{ + /*Implement me.*/ + flags=0; + } + return flags; +} +#endif diff --git a/Engine/lib/libtheora/lib/cpu.h b/Engine/lib/libtheora/lib/cpu.h new file mode 100644 index 000000000..efe5e9f52 --- /dev/null +++ b/Engine/lib/libtheora/lib/cpu.h @@ -0,0 +1,34 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + function: + last mod: $Id: cpu.h 15430 2008-10-21 05:03:55Z giles $ + + ********************************************************************/ + +#if !defined(_x86_cpu_H) +# define _x86_cpu_H (1) +#include "internal.h" + +#define OC_CPU_X86_MMX (1<<0) +#define OC_CPU_X86_3DNOW (1<<1) +#define OC_CPU_X86_3DNOWEXT (1<<2) +#define OC_CPU_X86_MMXEXT (1<<3) +#define OC_CPU_X86_SSE (1<<4) +#define OC_CPU_X86_SSE2 (1<<5) +#define OC_CPU_X86_PNI (1<<6) +#define OC_CPU_X86_SSSE3 (1<<7) +#define OC_CPU_X86_SSE4_1 (1<<8) +#define OC_CPU_X86_SSE4_2 (1<<9) +#define OC_CPU_X86_SSE4A (1<<10) +#define OC_CPU_X86_SSE5 (1<<11) + +#endif diff --git a/Engine/lib/libtheora/lib/dec/apiwrapper.c b/Engine/lib/libtheora/lib/dec/apiwrapper.c new file mode 100644 index 000000000..aa4416d7f --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/apiwrapper.c @@ -0,0 +1,166 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: apiwrapper.c 15400 2008-10-15 12:10:58Z tterribe $ + + ********************************************************************/ + +#include +#include +#include +#include "apiwrapper.h" + + + +const char *theora_version_string(void){ + return th_version_string(); +} + +ogg_uint32_t theora_version_number(void){ + return th_version_number(); +} + +void theora_info_init(theora_info *_ci){ + memset(_ci,0,sizeof(*_ci)); +} + +void theora_info_clear(theora_info *_ci){ + th_api_wrapper *api; + api=(th_api_wrapper *)_ci->codec_setup; + memset(_ci,0,sizeof(*_ci)); + if(api!=NULL){ + if(api->clear!=NULL)(*api->clear)(api); + _ogg_free(api); + } +} + +void theora_clear(theora_state *_th){ + /*Provide compatibility with mixed encoder and decoder shared lib versions.*/ + if(_th->internal_decode!=NULL){ + (*((oc_state_dispatch_vtbl *)_th->internal_decode)->clear)(_th); + } + if(_th->internal_encode!=NULL){ + (*((oc_state_dispatch_vtbl *)_th->internal_encode)->clear)(_th); + } + if(_th->i!=NULL)theora_info_clear(_th->i); + memset(_th,0,sizeof(*_th)); +} + +int theora_control(theora_state *_th,int _req,void *_buf,size_t _buf_sz){ + /*Provide compatibility with mixed encoder and decoder shared lib versions.*/ + if(_th->internal_decode!=NULL){ + return (*((oc_state_dispatch_vtbl *)_th->internal_decode)->control)(_th, + _req,_buf,_buf_sz); + } + else if(_th->internal_encode!=NULL){ + return (*((oc_state_dispatch_vtbl *)_th->internal_encode)->control)(_th, + _req,_buf,_buf_sz); + } + else return TH_EINVAL; +} + +ogg_int64_t theora_granule_frame(theora_state *_th,ogg_int64_t _gp){ + /*Provide compatibility with mixed encoder and decoder shared lib versions.*/ + if(_th->internal_decode!=NULL){ + return (*((oc_state_dispatch_vtbl *)_th->internal_decode)->granule_frame)( + _th,_gp); + } + else if(_th->internal_encode!=NULL){ + return (*((oc_state_dispatch_vtbl *)_th->internal_encode)->granule_frame)( + _th,_gp); + } + else return -1; +} + +double theora_granule_time(theora_state *_th, ogg_int64_t _gp){ + /*Provide compatibility with mixed encoder and decoder shared lib versions.*/ + if(_th->internal_decode!=NULL){ + return (*((oc_state_dispatch_vtbl *)_th->internal_decode)->granule_time)( + _th,_gp); + } + else if(_th->internal_encode!=NULL){ + return (*((oc_state_dispatch_vtbl *)_th->internal_encode)->granule_time)( + _th,_gp); + } + else return -1; +} + +void oc_theora_info2th_info(th_info *_info,const theora_info *_ci){ + _info->version_major=_ci->version_major; + _info->version_minor=_ci->version_minor; + _info->version_subminor=_ci->version_subminor; + _info->frame_width=_ci->width; + _info->frame_height=_ci->height; + _info->pic_width=_ci->frame_width; + _info->pic_height=_ci->frame_height; + _info->pic_x=_ci->offset_x; + _info->pic_y=_ci->offset_y; + _info->fps_numerator=_ci->fps_numerator; + _info->fps_denominator=_ci->fps_denominator; + _info->aspect_numerator=_ci->aspect_numerator; + _info->aspect_denominator=_ci->aspect_denominator; + switch(_ci->colorspace){ + case OC_CS_ITU_REC_470M:_info->colorspace=TH_CS_ITU_REC_470M;break; + case OC_CS_ITU_REC_470BG:_info->colorspace=TH_CS_ITU_REC_470BG;break; + default:_info->colorspace=TH_CS_UNSPECIFIED;break; + } + switch(_ci->pixelformat){ + case OC_PF_420:_info->pixel_fmt=TH_PF_420;break; + case OC_PF_422:_info->pixel_fmt=TH_PF_422;break; + case OC_PF_444:_info->pixel_fmt=TH_PF_444;break; + default:_info->pixel_fmt=TH_PF_RSVD; + } + _info->target_bitrate=_ci->target_bitrate; + _info->quality=_ci->quality; + _info->keyframe_granule_shift=_ci->keyframe_frequency_force>0? + OC_MINI(31,oc_ilog(_ci->keyframe_frequency_force-1)):0; +} + +int theora_packet_isheader(ogg_packet *_op){ + return th_packet_isheader(_op); +} + +int theora_packet_iskeyframe(ogg_packet *_op){ + return th_packet_iskeyframe(_op); +} + +int theora_granule_shift(theora_info *_ci){ + /*This breaks when keyframe_frequency_force is not positive or is larger than + 2**31 (if your int is more than 32 bits), but that's what the original + function does.*/ + return oc_ilog(_ci->keyframe_frequency_force-1); +} + +void theora_comment_init(theora_comment *_tc){ + th_comment_init((th_comment *)_tc); +} + +char *theora_comment_query(theora_comment *_tc,char *_tag,int _count){ + return th_comment_query((th_comment *)_tc,_tag,_count); +} + +int theora_comment_query_count(theora_comment *_tc,char *_tag){ + return th_comment_query_count((th_comment *)_tc,_tag); +} + +void theora_comment_clear(theora_comment *_tc){ + th_comment_clear((th_comment *)_tc); +} + +void theora_comment_add(theora_comment *_tc,char *_comment){ + th_comment_add((th_comment *)_tc,_comment); +} + +void theora_comment_add_tag(theora_comment *_tc, char *_tag, char *_value){ + th_comment_add_tag((th_comment *)_tc,_tag,_value); +} diff --git a/Engine/lib/libtheora/lib/dec/apiwrapper.h b/Engine/lib/libtheora/lib/dec/apiwrapper.h new file mode 100644 index 000000000..211021fc0 --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/apiwrapper.h @@ -0,0 +1,55 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: apiwrapper.h 13596 2007-08-23 20:05:38Z tterribe $ + + ********************************************************************/ + +#if !defined(_apiwrapper_H) +# define _apiwrapper_H (1) +# include +# include +# include "theora/theoradec.h" +/*# include "theora/theoraenc.h"*/ +typedef struct th_enc_ctx th_enc_ctx; +# include "../internal.h" + +typedef struct th_api_wrapper th_api_wrapper; +typedef struct th_api_info th_api_info; + +/*Provide an entry point for the codec setup to clear itself in case we ever + want to break pieces off into a common base library shared by encoder and + decoder. + In addition, this makes several other pieces of the API wrapper cleaner.*/ +typedef void (*oc_setup_clear_func)(void *_ts); + +/*Generally only one of these pointers will be non-NULL in any given instance. + Technically we do not even really need this struct, since we should be able + to figure out which one from "context", but doing it this way makes sure we + don't flub it up.*/ +struct th_api_wrapper{ + oc_setup_clear_func clear; + th_setup_info *setup; + th_dec_ctx *decode; + th_enc_ctx *encode; +}; + +struct th_api_info{ + th_api_wrapper api; + theora_info info; +}; + + +void oc_theora_info2th_info(th_info *_info,const theora_info *_ci); + +#endif diff --git a/Engine/lib/libtheora/lib/dec/bitpack.c b/Engine/lib/libtheora/lib/dec/bitpack.c new file mode 100644 index 000000000..3836150c2 --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/bitpack.c @@ -0,0 +1,121 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggTheora SOURCE CODE IS (C) COPYRIGHT 1994-2008 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: packing variable sized words into an octet stream + last mod: $Id: bitpack.c 15400 2008-10-15 12:10:58Z tterribe $ + + ********************************************************************/ + +/*We're 'MSb' endian; if we write a word but read individual bits, + then we'll read the MSb first.*/ + +#include +#include +#include "bitpack.h" + +void theorapackB_readinit(oggpack_buffer *_b,unsigned char *_buf,int _bytes){ + memset(_b,0,sizeof(*_b)); + _b->buffer=_b->ptr=_buf; + _b->storage=_bytes; +} + +int theorapackB_look1(oggpack_buffer *_b,long *_ret){ + if(_b->endbyte>=_b->storage){ + *_ret=0L; + return -1; + } + *_ret=(_b->ptr[0]>>7-_b->endbit)&1; + return 0; +} + +void theorapackB_adv1(oggpack_buffer *_b){ + if(++(_b->endbit)>7){ + _b->endbit=0; + _b->ptr++; + _b->endbyte++; + } +} + +/*Here we assume that 0<=_bits&&_bits<=32.*/ +int theorapackB_read(oggpack_buffer *_b,int _bits,long *_ret){ + long ret; + long m; + long d; + int fail; + m=32-_bits; + _bits+=_b->endbit; + d=_b->storage-_b->endbyte; + if(d<=4){ + /*Not the main path.*/ + if(d*8<_bits){ + *_ret=0L; + fail=-1; + goto overflow; + } + /*Special case to avoid reading _b->ptr[0], which might be past the end of + the buffer; also skips some useless accounting.*/ + else if(!_bits){ + *_ret=0L; + return 0; + } + } + ret=_b->ptr[0]<<24+_b->endbit; + if(_bits>8){ + ret|=_b->ptr[1]<<16+_b->endbit; + if(_bits>16){ + ret|=_b->ptr[2]<<8+_b->endbit; + if(_bits>24){ + ret|=_b->ptr[3]<<_b->endbit; + if(_bits>32)ret|=_b->ptr[4]>>8-_b->endbit; + } + } + } + *_ret=((ret&0xFFFFFFFFUL)>>(m>>1))>>(m+1>>1); + fail=0; +overflow: + _b->ptr+=_bits>>3; + _b->endbyte+=_bits>>3; + _b->endbit=_bits&7; + return fail; +} + +int theorapackB_read1(oggpack_buffer *_b,long *_ret){ + int fail; + if(_b->endbyte>=_b->storage){ + /*Not the main path.*/ + *_ret=0L; + fail=-1; + } + else{ + *_ret=(_b->ptr[0]>>7-_b->endbit)&1; + fail=0; + } + _b->endbit++; + if(_b->endbit>7){ + _b->endbit=0; + _b->ptr++; + _b->endbyte++; + } + return fail; +} + +long theorapackB_bytes(oggpack_buffer *_b){ + return _b->endbyte+(_b->endbit+7>>3); +} + +long theorapackB_bits(oggpack_buffer *_b){ + return _b->endbyte*8+_b->endbit; +} + +unsigned char *theorapackB_get_buffer(oggpack_buffer *_b){ + return _b->buffer; +} diff --git a/Engine/lib/libtheora/lib/dec/bitpack.h b/Engine/lib/libtheora/lib/dec/bitpack.h new file mode 100644 index 000000000..1bff3fa50 --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/bitpack.h @@ -0,0 +1,38 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggTheora SOURCE CODE IS (C) COPYRIGHT 1994-2008 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: packing variable sized words into an octet stream + last mod: $Id: bitwise.c 7675 2004-09-01 00:34:39Z xiphmont $ + + ********************************************************************/ +#if !defined(_bitpack_H) +# define _bitpack_H (1) +# include + +void theorapackB_readinit(oggpack_buffer *_b,unsigned char *_buf,int _bytes); +int theorapackB_look1(oggpack_buffer *_b,long *_ret); +void theorapackB_adv1(oggpack_buffer *_b); +/*Here we assume 0<=_bits&&_bits<=32.*/ +int theorapackB_read(oggpack_buffer *_b,int _bits,long *_ret); +int theorapackB_read1(oggpack_buffer *_b,long *_ret); +long theorapackB_bytes(oggpack_buffer *_b); +long theorapackB_bits(oggpack_buffer *_b); +unsigned char *theorapackB_get_buffer(oggpack_buffer *_b); + +/*These two functions are implemented locally in huffdec.c*/ +/*Read in bits without advancing the bitptr. + Here we assume 0<=_bits&&_bits<=32.*/ +/*static int theorapackB_look(oggpack_buffer *_b,int _bits,long *_ret);*/ +/*static void theorapackB_adv(oggpack_buffer *_b,int _bits);*/ + + +#endif diff --git a/Engine/lib/libtheora/lib/dec/dct.h b/Engine/lib/libtheora/lib/dec/dct.h new file mode 100644 index 000000000..09043dc51 --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/dct.h @@ -0,0 +1,31 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: dct.h 15400 2008-10-15 12:10:58Z tterribe $ + + ********************************************************************/ + +/*Definitions shared by the forward and inverse DCT transforms.*/ +#if !defined(_dct_H) +# define _dct_H (1) + +/*cos(n*pi/16) (resp. sin(m*pi/16)) scaled by 65536.*/ +#define OC_C1S7 ((ogg_int32_t)64277) +#define OC_C2S6 ((ogg_int32_t)60547) +#define OC_C3S5 ((ogg_int32_t)54491) +#define OC_C4S4 ((ogg_int32_t)46341) +#define OC_C5S3 ((ogg_int32_t)36410) +#define OC_C6S2 ((ogg_int32_t)25080) +#define OC_C7S1 ((ogg_int32_t)12785) + +#endif diff --git a/Engine/lib/libtheora/lib/dec/decapiwrapper.c b/Engine/lib/libtheora/lib/dec/decapiwrapper.c new file mode 100644 index 000000000..bceec6c26 --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/decapiwrapper.c @@ -0,0 +1,188 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: decapiwrapper.c 13596 2007-08-23 20:05:38Z tterribe $ + + ********************************************************************/ + +#include +#include +#include +#include "apiwrapper.h" +#include "theora/theoradec.h" + +static void th_dec_api_clear(th_api_wrapper *_api){ + if(_api->setup)th_setup_free(_api->setup); + if(_api->decode)th_decode_free(_api->decode); + memset(_api,0,sizeof(*_api)); +} + +static void theora_decode_clear(theora_state *_td){ + if(_td->i!=NULL)theora_info_clear(_td->i); + memset(_td,0,sizeof(*_td)); +} + +static int theora_decode_control(theora_state *_td,int _req, + void *_buf,size_t _buf_sz){ + return th_decode_ctl(((th_api_wrapper *)_td->i->codec_setup)->decode, + _req,_buf,_buf_sz); +} + +static ogg_int64_t theora_decode_granule_frame(theora_state *_td, + ogg_int64_t _gp){ + return th_granule_frame(((th_api_wrapper *)_td->i->codec_setup)->decode,_gp); +} + +static double theora_decode_granule_time(theora_state *_td,ogg_int64_t _gp){ + return th_granule_time(((th_api_wrapper *)_td->i->codec_setup)->decode,_gp); +} + +static const oc_state_dispatch_vtbl OC_DEC_DISPATCH_VTBL={ + (oc_state_clear_func)theora_decode_clear, + (oc_state_control_func)theora_decode_control, + (oc_state_granule_frame_func)theora_decode_granule_frame, + (oc_state_granule_time_func)theora_decode_granule_time, +}; + +static void th_info2theora_info(theora_info *_ci,const th_info *_info){ + _ci->version_major=_info->version_major; + _ci->version_minor=_info->version_minor; + _ci->version_subminor=_info->version_subminor; + _ci->width=_info->frame_width; + _ci->height=_info->frame_height; + _ci->frame_width=_info->pic_width; + _ci->frame_height=_info->pic_height; + _ci->offset_x=_info->pic_x; + _ci->offset_y=_info->pic_y; + _ci->fps_numerator=_info->fps_numerator; + _ci->fps_denominator=_info->fps_denominator; + _ci->aspect_numerator=_info->aspect_numerator; + _ci->aspect_denominator=_info->aspect_denominator; + switch(_info->colorspace){ + case TH_CS_ITU_REC_470M:_ci->colorspace=OC_CS_ITU_REC_470M;break; + case TH_CS_ITU_REC_470BG:_ci->colorspace=OC_CS_ITU_REC_470BG;break; + default:_ci->colorspace=OC_CS_UNSPECIFIED;break; + } + switch(_info->pixel_fmt){ + case TH_PF_420:_ci->pixelformat=OC_PF_420;break; + case TH_PF_422:_ci->pixelformat=OC_PF_422;break; + case TH_PF_444:_ci->pixelformat=OC_PF_444;break; + default:_ci->pixelformat=OC_PF_RSVD; + } + _ci->target_bitrate=_info->target_bitrate; + _ci->quality=_info->quality; + _ci->keyframe_frequency_force=1<<_info->keyframe_granule_shift; +} + +int theora_decode_init(theora_state *_td,theora_info *_ci){ + th_api_info *apiinfo; + th_api_wrapper *api; + th_info info; + api=(th_api_wrapper *)_ci->codec_setup; + /*Allocate our own combined API wrapper/theora_info struct. + We put them both in one malloc'd block so that when the API wrapper is + freed, the info struct goes with it. + This avoids having to figure out whether or not we need to free the info + struct in either theora_info_clear() or theora_clear().*/ + apiinfo=(th_api_info *)_ogg_calloc(1,sizeof(*apiinfo)); + /*Make our own copy of the info struct, since its lifetime should be + independent of the one we were passed in.*/ + *&apiinfo->info=*_ci; + /*Convert the info struct now instead of saving the the one we decoded with + theora_decode_header(), since the user might have modified values (i.e., + color space, aspect ratio, etc. can be specified from a higher level). + The user also might be doing something "clever" with the header packets if + they are not using an Ogg encapsulation.*/ + oc_theora_info2th_info(&info,_ci); + /*Don't bother to copy the setup info; th_decode_alloc() makes its own copy + of the stuff it needs.*/ + apiinfo->api.decode=th_decode_alloc(&info,api->setup); + if(apiinfo->api.decode==NULL){ + _ogg_free(apiinfo); + return OC_EINVAL; + } + apiinfo->api.clear=(oc_setup_clear_func)th_dec_api_clear; + _td->internal_encode=NULL; + /*Provide entry points for ABI compatibility with old decoder shared libs.*/ + _td->internal_decode=(void *)&OC_DEC_DISPATCH_VTBL; + _td->granulepos=0; + _td->i=&apiinfo->info; + _td->i->codec_setup=&apiinfo->api; + return 0; +} + +int theora_decode_header(theora_info *_ci,theora_comment *_cc,ogg_packet *_op){ + th_api_wrapper *api; + th_info info; + int ret; + api=(th_api_wrapper *)_ci->codec_setup; + /*Allocate an API wrapper struct on demand, since it will not also include a + theora_info struct like the ones that are used in a theora_state struct.*/ + if(api==NULL){ + _ci->codec_setup=_ogg_calloc(1,sizeof(*api)); + api=(th_api_wrapper *)_ci->codec_setup; + api->clear=(oc_setup_clear_func)th_dec_api_clear; + } + /*Convert from the theora_info struct instead of saving our own th_info + struct between calls. + The user might be doing something "clever" with the header packets if they + are not using an Ogg encapsulation, and we don't want to break this.*/ + oc_theora_info2th_info(&info,_ci); + /*We rely on the fact that theora_comment and th_comment structures are + actually identical. + Take care not to change this fact unless you change the code here as + well!*/ + ret=th_decode_headerin(&info,(th_comment *)_cc,&api->setup,_op); + /*We also rely on the fact that the error return code values are the same, + and that the implementations of these two functions return the same set of + them. + Note that theora_decode_header() really can return OC_NOTFORMAT, even + though it is not currently documented to do so.*/ + if(ret<0)return ret; + th_info2theora_info(_ci,&info); + return 0; +} + +int theora_decode_packetin(theora_state *_td,ogg_packet *_op){ + th_api_wrapper *api; + ogg_int64_t gp; + int ret; + if(!_td||!_td->i||!_td->i->codec_setup)return OC_FAULT; + api=(th_api_wrapper *)_td->i->codec_setup; + ret=th_decode_packetin(api->decode,_op,&gp); + if(ret<0)return OC_BADPACKET; + _td->granulepos=gp; + return 0; +} + +int theora_decode_YUVout(theora_state *_td,yuv_buffer *_yuv){ + th_api_wrapper *api; + th_ycbcr_buffer buf; + int ret; + if(!_td||!_td->i||!_td->i->codec_setup)return OC_FAULT; + api=(th_api_wrapper *)_td->i->codec_setup; + if(!api->decode)return OC_FAULT; + ret=th_decode_ycbcr_out(api->decode,buf); + if(ret>=0){ + _yuv->y_width=buf[0].width; + _yuv->y_height=buf[0].height; + _yuv->y_stride=buf[0].stride; + _yuv->uv_width=buf[1].width; + _yuv->uv_height=buf[1].height; + _yuv->uv_stride=buf[1].stride; + _yuv->y=buf[0].data; + _yuv->u=buf[1].data; + _yuv->v=buf[2].data; + } + return ret; +} diff --git a/Engine/lib/libtheora/lib/dec/decinfo.c b/Engine/lib/libtheora/lib/dec/decinfo.c new file mode 100644 index 000000000..3c4ba868a --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/decinfo.c @@ -0,0 +1,239 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: decinfo.c 15400 2008-10-15 12:10:58Z tterribe $ + + ********************************************************************/ + +#include +#include +#include +#include "decint.h" + + + +/*Unpacks a series of octets from a given byte array into the pack buffer. + No checking is done to ensure the buffer contains enough data. + _opb: The pack buffer to read the octets from. + _buf: The byte array to store the unpacked bytes in. + _len: The number of octets to unpack.*/ +static void oc_unpack_octets(oggpack_buffer *_opb,char *_buf,size_t _len){ + while(_len-->0){ + long val; + theorapackB_read(_opb,8,&val); + *_buf++=(char)val; + } +} + +/*Unpacks a 32-bit integer encoded by octets in little-endian form.*/ +static long oc_unpack_length(oggpack_buffer *_opb){ + long ret[4]; + int i; + for(i=0;i<4;i++)theorapackB_read(_opb,8,ret+i); + return ret[0]|ret[1]<<8|ret[2]<<16|ret[3]<<24; +} + +static int oc_info_unpack(oggpack_buffer *_opb,th_info *_info){ + long val; + /*Check the codec bitstream version.*/ + theorapackB_read(_opb,8,&val); + _info->version_major=(unsigned char)val; + theorapackB_read(_opb,8,&val); + _info->version_minor=(unsigned char)val; + theorapackB_read(_opb,8,&val); + _info->version_subminor=(unsigned char)val; + /*verify we can parse this bitstream version. + We accept earlier minors and all subminors, by spec*/ + if(_info->version_major>TH_VERSION_MAJOR|| + _info->version_major==TH_VERSION_MAJOR&& + _info->version_minor>TH_VERSION_MINOR){ + return TH_EVERSION; + } + /*Read the encoded frame description.*/ + theorapackB_read(_opb,16,&val); + _info->frame_width=(ogg_uint32_t)val<<4; + theorapackB_read(_opb,16,&val); + _info->frame_height=(ogg_uint32_t)val<<4; + theorapackB_read(_opb,24,&val); + _info->pic_width=(ogg_uint32_t)val; + theorapackB_read(_opb,24,&val); + _info->pic_height=(ogg_uint32_t)val; + theorapackB_read(_opb,8,&val); + _info->pic_x=(ogg_uint32_t)val; + /*Note: The sense of pic_y is inverted in what we pass back to the + application compared to how it is stored in the bitstream. + This is because the bitstream uses a right-handed coordinate system, while + applications expect a left-handed one.*/ + theorapackB_read(_opb,8,&val); + _info->pic_y=_info->frame_height-_info->pic_height-(ogg_uint32_t)val; + theorapackB_read(_opb,32,&val); + _info->fps_numerator=(ogg_uint32_t)val; + theorapackB_read(_opb,32,&val); + _info->fps_denominator=(ogg_uint32_t)val; + if(_info->frame_width==0||_info->frame_height==0|| + _info->pic_width+_info->pic_x>_info->frame_width|| + _info->pic_height+_info->pic_y>_info->frame_height|| + _info->fps_numerator==0||_info->fps_denominator==0){ + return TH_EBADHEADER; + } + theorapackB_read(_opb,24,&val); + _info->aspect_numerator=(ogg_uint32_t)val; + theorapackB_read(_opb,24,&val); + _info->aspect_denominator=(ogg_uint32_t)val; + theorapackB_read(_opb,8,&val); + _info->colorspace=(th_colorspace)val; + theorapackB_read(_opb,24,&val); + _info->target_bitrate=(int)val; + theorapackB_read(_opb,6,&val); + _info->quality=(int)val; + theorapackB_read(_opb,5,&val); + _info->keyframe_granule_shift=(int)val; + theorapackB_read(_opb,2,&val); + _info->pixel_fmt=(th_pixel_fmt)val; + if(_info->pixel_fmt==TH_PF_RSVD)return TH_EBADHEADER; + if(theorapackB_read(_opb,3,&val)<0||val!=0)return TH_EBADHEADER; + return 0; +} + +static int oc_comment_unpack(oggpack_buffer *_opb,th_comment *_tc){ + long len; + int i; + /*Read the vendor string.*/ + len=oc_unpack_length(_opb); + if(len<0||theorapackB_bytes(_opb)+len>_opb->storage)return TH_EBADHEADER; + _tc->vendor=_ogg_malloc((size_t)len+1); + oc_unpack_octets(_opb,_tc->vendor,len); + _tc->vendor[len]='\0'; + /*Read the user comments.*/ + _tc->comments=(int)oc_unpack_length(_opb); + if(_tc->comments<0||_tc->comments>(LONG_MAX>>2)|| + theorapackB_bytes(_opb)+((long)_tc->comments<<2)>_opb->storage){ + return TH_EBADHEADER; + } + _tc->comment_lengths=(int *)_ogg_malloc( + _tc->comments*sizeof(_tc->comment_lengths[0])); + _tc->user_comments=(char **)_ogg_malloc( + _tc->comments*sizeof(_tc->user_comments[0])); + for(i=0;i<_tc->comments;i++){ + len=oc_unpack_length(_opb); + if(len<0||theorapackB_bytes(_opb)+len>_opb->storage){ + _tc->comments=i; + return TH_EBADHEADER; + } + _tc->comment_lengths[i]=len; + _tc->user_comments[i]=_ogg_malloc((size_t)len+1); + oc_unpack_octets(_opb,_tc->user_comments[i],len); + _tc->user_comments[i][len]='\0'; + } + return theorapackB_read(_opb,0,&len)<0?TH_EBADHEADER:0; +} + +static int oc_setup_unpack(oggpack_buffer *_opb,th_setup_info *_setup){ + int ret; + /*Read the quantizer tables.*/ + ret=oc_quant_params_unpack(_opb,&_setup->qinfo); + if(ret<0)return ret; + /*Read the Huffman trees.*/ + return oc_huff_trees_unpack(_opb,_setup->huff_tables); +} + +static void oc_setup_clear(th_setup_info *_setup){ + oc_quant_params_clear(&_setup->qinfo); + oc_huff_trees_clear(_setup->huff_tables); +} + +static int oc_dec_headerin(oggpack_buffer *_opb,th_info *_info, + th_comment *_tc,th_setup_info **_setup,ogg_packet *_op){ + char buffer[6]; + long val; + int packtype; + int ret; + theorapackB_read(_opb,8,&val); + packtype=(int)val; + /*If we're at a data packet and we have received all three headers, we're + done.*/ + if(!(packtype&0x80)&&_info->frame_width>0&&_tc->vendor!=NULL&&*_setup!=NULL){ + return 0; + } + /*Check the codec string.*/ + oc_unpack_octets(_opb,buffer,6); + if(memcmp(buffer,"theora",6)!=0)return TH_ENOTFORMAT; + switch(packtype){ + /*Codec info header.*/ + case 0x80:{ + /*This should be the first packet, and we should not already be + initialized.*/ + if(!_op->b_o_s||_info->frame_width>0)return TH_EBADHEADER; + ret=oc_info_unpack(_opb,_info); + if(ret<0)th_info_clear(_info); + else ret=3; + }break; + /*Comment header.*/ + case 0x81:{ + if(_tc==NULL)return TH_EFAULT; + /*We shoud have already decoded the info header, and should not yet have + decoded the comment header.*/ + if(_info->frame_width==0||_tc->vendor!=NULL)return TH_EBADHEADER; + ret=oc_comment_unpack(_opb,_tc); + if(ret<0)th_comment_clear(_tc); + else ret=2; + }break; + /*Codec setup header.*/ + case 0x82:{ + oc_setup_info *setup; + if(_tc==NULL||_setup==NULL)return TH_EFAULT; + /*We should have already decoded the info header and the comment header, + and should not yet have decoded the setup header.*/ + if(_info->frame_width==0||_tc->vendor==NULL||*_setup!=NULL){ + return TH_EBADHEADER; + } + setup=(oc_setup_info *)_ogg_calloc(1,sizeof(*setup)); + ret=oc_setup_unpack(_opb,setup); + if(ret<0){ + oc_setup_clear(setup); + _ogg_free(setup); + } + else{ + *_setup=setup; + ret=1; + } + }break; + default:{ + /*We don't know what this header is.*/ + return TH_EBADHEADER; + }break; + } + return ret; +} + + +/*Decodes one header packet. + This should be called repeatedly with the packets at the beginning of the + stream until it returns 0.*/ +int th_decode_headerin(th_info *_info,th_comment *_tc, + th_setup_info **_setup,ogg_packet *_op){ + oggpack_buffer opb; + int ret; + if(_op==NULL)return TH_EBADHEADER; + if(_info==NULL)return TH_EFAULT; + theorapackB_readinit(&opb,_op->packet,_op->bytes); + ret=oc_dec_headerin(&opb,_info,_tc,_setup,_op); + return ret; +} + +void th_setup_free(th_setup_info *_setup){ + if(_setup!=NULL){ + oc_setup_clear(_setup); + _ogg_free(_setup); + } +} diff --git a/Engine/lib/libtheora/lib/dec/decint.h b/Engine/lib/libtheora/lib/dec/decint.h new file mode 100644 index 000000000..7924c0e0c --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/decint.h @@ -0,0 +1,95 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: decint.h 15400 2008-10-15 12:10:58Z tterribe $ + + ********************************************************************/ + +#include +#if !defined(_decint_H) +# define _decint_H (1) +# include "theora/theoradec.h" +# include "../internal.h" +# include "bitpack.h" + +typedef struct th_setup_info oc_setup_info; +typedef struct th_dec_ctx oc_dec_ctx; + +# include "idct.h" +# include "huffdec.h" +# include "dequant.h" + +/*Constants for the packet-in state machine specific to the decoder.*/ + +/*Next packet to read: Data packet.*/ +#define OC_PACKET_DATA (0) + + + +struct th_setup_info{ + /*The Huffman codes.*/ + oc_huff_node *huff_tables[TH_NHUFFMAN_TABLES]; + /*The quantization parameters.*/ + th_quant_info qinfo; +}; + + + +struct th_dec_ctx{ + /*Shared encoder/decoder state.*/ + oc_theora_state state; + /*Whether or not packets are ready to be emitted. + This takes on negative values while there are remaining header packets to + be emitted, reaches 0 when the codec is ready for input, and goes to 1 + when a frame has been processed and a data packet is ready.*/ + int packet_state; + /*Buffer in which to assemble packets.*/ + oggpack_buffer opb; + /*Huffman decode trees.*/ + oc_huff_node *huff_tables[TH_NHUFFMAN_TABLES]; + /*The index of one past the last token in each plane for each coefficient. + The final entries are the total number of tokens for each coefficient.*/ + int ti0[3][64]; + /*The index of one past the last extra bits entry in each plane for each + coefficient. + The final entries are the total number of extra bits entries for each + coefficient.*/ + int ebi0[3][64]; + /*The number of outstanding EOB runs at the start of each coefficient in each + plane.*/ + int eob_runs[3][64]; + /*The DCT token lists.*/ + unsigned char **dct_tokens; + /*The extra bits associated with DCT tokens.*/ + ogg_uint16_t **extra_bits; + /*The out-of-loop post-processing level.*/ + int pp_level; + /*The DC scale used for out-of-loop deblocking.*/ + int pp_dc_scale[64]; + /*The sharpen modifier used for out-of-loop deringing.*/ + int pp_sharp_mod[64]; + /*The DC quantization index of each block.*/ + unsigned char *dc_qis; + /*The variance of each block.*/ + int *variances; + /*The storage for the post-processed frame buffer.*/ + unsigned char *pp_frame_data; + /*Whether or not the post-processsed frame buffer has space for chroma.*/ + int pp_frame_has_chroma; + /*The buffer used for the post-processed frame.*/ + th_ycbcr_buffer pp_frame_buf; + /*The striped decode callback function.*/ + th_stripe_callback stripe_cb; +}; + +#endif diff --git a/Engine/lib/libtheora/lib/dec/decode.c b/Engine/lib/libtheora/lib/dec/decode.c new file mode 100644 index 000000000..5804cf709 --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/decode.c @@ -0,0 +1,2057 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: decode.c 15403 2008-10-16 12:44:05Z tterribe $ + + ********************************************************************/ + +#include +#include +#include +#include "decint.h" +#if defined(OC_DUMP_IMAGES) +# include +# include "png.h" +#endif + +/*No post-processing.*/ +#define OC_PP_LEVEL_DISABLED (0) +/*Keep track of DC qi for each block only.*/ +#define OC_PP_LEVEL_TRACKDCQI (1) +/*Deblock the luma plane.*/ +#define OC_PP_LEVEL_DEBLOCKY (2) +/*Dering the luma plane.*/ +#define OC_PP_LEVEL_DERINGY (3) +/*Stronger luma plane deringing.*/ +#define OC_PP_LEVEL_SDERINGY (4) +/*Deblock the chroma planes.*/ +#define OC_PP_LEVEL_DEBLOCKC (5) +/*Dering the chroma planes.*/ +#define OC_PP_LEVEL_DERINGC (6) +/*Stronger chroma plane deringing.*/ +#define OC_PP_LEVEL_SDERINGC (7) +/*Maximum valid post-processing level.*/ +#define OC_PP_LEVEL_MAX (7) + + + +/*The mode alphabets for the various mode coding schemes. + Scheme 0 uses a custom alphabet, which is not stored in this table.*/ +static const int OC_MODE_ALPHABETS[7][OC_NMODES]={ + /*Last MV dominates */ + { + OC_MODE_INTER_MV_LAST,OC_MODE_INTER_MV_LAST2,OC_MODE_INTER_MV, + OC_MODE_INTER_NOMV,OC_MODE_INTRA,OC_MODE_GOLDEN_NOMV,OC_MODE_GOLDEN_MV, + OC_MODE_INTER_MV_FOUR + }, + { + OC_MODE_INTER_MV_LAST,OC_MODE_INTER_MV_LAST2,OC_MODE_INTER_NOMV, + OC_MODE_INTER_MV,OC_MODE_INTRA,OC_MODE_GOLDEN_NOMV,OC_MODE_GOLDEN_MV, + OC_MODE_INTER_MV_FOUR + }, + { + OC_MODE_INTER_MV_LAST,OC_MODE_INTER_MV,OC_MODE_INTER_MV_LAST2, + OC_MODE_INTER_NOMV,OC_MODE_INTRA,OC_MODE_GOLDEN_NOMV,OC_MODE_GOLDEN_MV, + OC_MODE_INTER_MV_FOUR + }, + { + OC_MODE_INTER_MV_LAST,OC_MODE_INTER_MV,OC_MODE_INTER_NOMV, + OC_MODE_INTER_MV_LAST2,OC_MODE_INTRA,OC_MODE_GOLDEN_NOMV, + OC_MODE_GOLDEN_MV,OC_MODE_INTER_MV_FOUR + }, + /*No MV dominates.*/ + { + OC_MODE_INTER_NOMV,OC_MODE_INTER_MV_LAST,OC_MODE_INTER_MV_LAST2, + OC_MODE_INTER_MV,OC_MODE_INTRA,OC_MODE_GOLDEN_NOMV,OC_MODE_GOLDEN_MV, + OC_MODE_INTER_MV_FOUR + }, + { + OC_MODE_INTER_NOMV,OC_MODE_GOLDEN_NOMV,OC_MODE_INTER_MV_LAST, + OC_MODE_INTER_MV_LAST2,OC_MODE_INTER_MV,OC_MODE_INTRA,OC_MODE_GOLDEN_MV, + OC_MODE_INTER_MV_FOUR + }, + /*Default ordering.*/ + { + OC_MODE_INTER_NOMV,OC_MODE_INTRA,OC_MODE_INTER_MV,OC_MODE_INTER_MV_LAST, + OC_MODE_INTER_MV_LAST2,OC_MODE_GOLDEN_NOMV,OC_MODE_GOLDEN_MV, + OC_MODE_INTER_MV_FOUR + } +}; + + +static int oc_sb_run_unpack(oggpack_buffer *_opb){ + long bits; + int ret; + /*Coding scheme: + Codeword Run Length + 0 1 + 10x 2-3 + 110x 4-5 + 1110xx 6-9 + 11110xxx 10-17 + 111110xxxx 18-33 + 111111xxxxxxxxxxxx 34-4129*/ + theorapackB_read1(_opb,&bits); + if(bits==0)return 1; + theorapackB_read(_opb,2,&bits); + if((bits&2)==0)return 2+(int)bits; + else if((bits&1)==0){ + theorapackB_read1(_opb,&bits); + return 4+(int)bits; + } + theorapackB_read(_opb,3,&bits); + if((bits&4)==0)return 6+(int)bits; + else if((bits&2)==0){ + ret=10+((bits&1)<<2); + theorapackB_read(_opb,2,&bits); + return ret+(int)bits; + } + else if((bits&1)==0){ + theorapackB_read(_opb,4,&bits); + return 18+(int)bits; + } + theorapackB_read(_opb,12,&bits); + return 34+(int)bits; +} + +static int oc_block_run_unpack(oggpack_buffer *_opb){ + long bits; + long bits2; + /*Coding scheme: + Codeword Run Length + 0x 1-2 + 10x 3-4 + 110x 5-6 + 1110xx 7-10 + 11110xx 11-14 + 11111xxxx 15-30*/ + theorapackB_read(_opb,2,&bits); + if((bits&2)==0)return 1+(int)bits; + else if((bits&1)==0){ + theorapackB_read1(_opb,&bits); + return 3+(int)bits; + } + theorapackB_read(_opb,2,&bits); + if((bits&2)==0)return 5+(int)bits; + else if((bits&1)==0){ + theorapackB_read(_opb,2,&bits); + return 7+(int)bits; + } + theorapackB_read(_opb,3,&bits); + if((bits&4)==0)return 11+bits; + theorapackB_read(_opb,2,&bits2); + return 15+((bits&3)<<2)+bits2; +} + + + +static int oc_dec_init(oc_dec_ctx *_dec,const th_info *_info, + const th_setup_info *_setup){ + int qti; + int pli; + int qi; + int ret; + ret=oc_state_init(&_dec->state,_info); + if(ret<0)return ret; + oc_huff_trees_copy(_dec->huff_tables, + (const oc_huff_node *const *)_setup->huff_tables); + for(qti=0;qti<2;qti++)for(pli=0;pli<3;pli++){ + _dec->state.dequant_tables[qti][pli]= + _dec->state.dequant_table_data[qti][pli]; + } + oc_dequant_tables_init(_dec->state.dequant_tables,_dec->pp_dc_scale, + &_setup->qinfo); + for(qi=0;qi<64;qi++){ + int qsum; + qsum=0; + for(qti=0;qti<2;qti++)for(pli=0;pli<3;pli++){ + qsum+=_dec->state.dequant_tables[qti][pli][qi][18]+ + _dec->state.dequant_tables[qti][pli][qi][19]+ + _dec->state.dequant_tables[qti][pli][qi][26]+ + _dec->state.dequant_tables[qti][pli][qi][27]<<(pli==0); + } + _dec->pp_sharp_mod[qi]=-(qsum>>11); + } + _dec->dct_tokens=(unsigned char **)oc_calloc_2d(64, + _dec->state.nfrags,sizeof(_dec->dct_tokens[0][0])); + _dec->extra_bits=(ogg_uint16_t **)oc_calloc_2d(64, + _dec->state.nfrags,sizeof(_dec->extra_bits[0][0])); + memcpy(_dec->state.loop_filter_limits,_setup->qinfo.loop_filter_limits, + sizeof(_dec->state.loop_filter_limits)); + _dec->pp_level=OC_PP_LEVEL_DISABLED; + _dec->dc_qis=NULL; + _dec->variances=NULL; + _dec->pp_frame_data=NULL; + _dec->stripe_cb.ctx=NULL; + _dec->stripe_cb.stripe_decoded=NULL; + return 0; +} + +static void oc_dec_clear(oc_dec_ctx *_dec){ + _ogg_free(_dec->pp_frame_data); + _ogg_free(_dec->variances); + _ogg_free(_dec->dc_qis); + oc_free_2d(_dec->extra_bits); + oc_free_2d(_dec->dct_tokens); + oc_huff_trees_clear(_dec->huff_tables); + oc_state_clear(&_dec->state); +} + + +static int oc_dec_frame_header_unpack(oc_dec_ctx *_dec){ + long val; + /*Check to make sure this is a data packet.*/ + theorapackB_read1(&_dec->opb,&val); + if(val!=0)return TH_EBADPACKET; + /*Read in the frame type (I or P).*/ + theorapackB_read1(&_dec->opb,&val); + _dec->state.frame_type=(int)val; + /*Read in the current qi.*/ + theorapackB_read(&_dec->opb,6,&val); + _dec->state.qis[0]=(int)val; + theorapackB_read1(&_dec->opb,&val); + if(!val)_dec->state.nqis=1; + else{ + theorapackB_read(&_dec->opb,6,&val); + _dec->state.qis[1]=(int)val; + theorapackB_read1(&_dec->opb,&val); + if(!val)_dec->state.nqis=2; + else{ + theorapackB_read(&_dec->opb,6,&val); + _dec->state.qis[2]=(int)val; + _dec->state.nqis=3; + } + } + if(_dec->state.frame_type==OC_INTRA_FRAME){ + /*Keyframes have 3 unused configuration bits, holdovers from VP3 days. + Most of the other unused bits in the VP3 headers were eliminated. + I don't know why these remain.*/ + /* I wanted to eliminate wasted bits, but not all config wiggle room --Monty */ + theorapackB_read(&_dec->opb,3,&val); + if(val!=0)return TH_EIMPL; + } + return 0; +} + +/*Mark all fragments as coded and in OC_MODE_INTRA. + This also builds up the coded fragment list (in coded order), and clears the + uncoded fragment list. + It does not update the coded macro block list, as that is not used when + decoding INTRA frames.*/ +static void oc_dec_mark_all_intra(oc_dec_ctx *_dec){ + oc_sb *sb; + oc_sb *sb_end; + int pli; + int ncoded_fragis; + int prev_ncoded_fragis; + prev_ncoded_fragis=ncoded_fragis=0; + sb=sb_end=_dec->state.sbs; + for(pli=0;pli<3;pli++){ + const oc_fragment_plane *fplane; + fplane=_dec->state.fplanes+pli; + sb_end+=fplane->nsbs; + for(;sbquad_valid&1<map[quadi][bi]; + if(fragi>=0){ + oc_fragment *frag; + frag=_dec->state.frags+fragi; + frag->coded=1; + frag->mbmode=OC_MODE_INTRA; + _dec->state.coded_fragis[ncoded_fragis++]=fragi; + } + } + } + } + _dec->state.ncoded_fragis[pli]=ncoded_fragis-prev_ncoded_fragis; + prev_ncoded_fragis=ncoded_fragis; + _dec->state.nuncoded_fragis[pli]=0; + } +} + +/*Decodes the bit flags for whether or not each super block is partially coded + or not. + Return: The number of partially coded super blocks.*/ +static int oc_dec_partial_sb_flags_unpack(oc_dec_ctx *_dec){ + oc_sb *sb; + oc_sb *sb_end; + long val; + int flag; + int npartial; + int run_count; + theorapackB_read1(&_dec->opb,&val); + flag=(int)val; + sb=_dec->state.sbs; + sb_end=sb+_dec->state.nsbs; + run_count=npartial=0; + while(sbopb); + full_run=run_count>=4129; + do{ + sb->coded_partially=flag; + sb->coded_fully=0; + npartial+=flag; + sb++; + } + while(--run_count>0&&sbopb,&val); + flag=(int)val; + } + else flag=!flag; + } + /*TODO: run_count should be 0 here. + If it's not, we should issue a warning of some kind.*/ + return npartial; +} + +/*Decodes the bit flags for whether or not each non-partially-coded super + block is fully coded or not. + This function should only be called if there is at least one + non-partially-coded super block. + Return: The number of partially coded super blocks.*/ +static void oc_dec_coded_sb_flags_unpack(oc_dec_ctx *_dec){ + oc_sb *sb; + oc_sb *sb_end; + long val; + int flag; + int run_count; + sb=_dec->state.sbs; + sb_end=sb+_dec->state.nsbs; + /*Skip partially coded super blocks.*/ + for(;sb->coded_partially;sb++); + theorapackB_read1(&_dec->opb,&val); + flag=(int)val; + while(sbopb); + full_run=run_count>=4129; + for(;sbcoded_partially)continue; + if(run_count--<=0)break; + sb->coded_fully=flag; + } + if(full_run&&sbopb,&val); + flag=(int)val; + } + else flag=!flag; + } + /*TODO: run_count should be 0 here. + If it's not, we should issue a warning of some kind.*/ +} + +static void oc_dec_coded_flags_unpack(oc_dec_ctx *_dec){ + oc_sb *sb; + oc_sb *sb_end; + long val; + int npartial; + int pli; + int flag; + int run_count; + int ncoded_fragis; + int prev_ncoded_fragis; + int nuncoded_fragis; + int prev_nuncoded_fragis; + npartial=oc_dec_partial_sb_flags_unpack(_dec); + if(npartial<_dec->state.nsbs)oc_dec_coded_sb_flags_unpack(_dec); + if(npartial>0){ + theorapackB_read1(&_dec->opb,&val); + flag=!(int)val; + } + else flag=0; + run_count=0; + prev_ncoded_fragis=ncoded_fragis=prev_nuncoded_fragis=nuncoded_fragis=0; + sb=sb_end=_dec->state.sbs; + for(pli=0;pli<3;pli++){ + const oc_fragment_plane *fplane; + fplane=_dec->state.fplanes+pli; + sb_end+=fplane->nsbs; + for(;sbquad_valid&1<map[quadi][bi]; + if(fragi>=0){ + oc_fragment *frag; + frag=_dec->state.frags+fragi; + if(sb->coded_fully)frag->coded=1; + else if(!sb->coded_partially)frag->coded=0; + else{ + if(run_count<=0){ + run_count=oc_block_run_unpack(&_dec->opb); + flag=!flag; + } + run_count--; + frag->coded=flag; + } + if(frag->coded)_dec->state.coded_fragis[ncoded_fragis++]=fragi; + else *(_dec->state.uncoded_fragis-++nuncoded_fragis)=fragi; + } + } + } + } + _dec->state.ncoded_fragis[pli]=ncoded_fragis-prev_ncoded_fragis; + prev_ncoded_fragis=ncoded_fragis; + _dec->state.nuncoded_fragis[pli]=nuncoded_fragis-prev_nuncoded_fragis; + prev_nuncoded_fragis=nuncoded_fragis; + } + /*TODO: run_count should be 0 here. + If it's not, we should issue a warning of some kind.*/ +} + + + +typedef int (*oc_mode_unpack_func)(oggpack_buffer *_opb); + +static int oc_vlc_mode_unpack(oggpack_buffer *_opb){ + long val; + int i; + for(i=0;i<7;i++){ + theorapackB_read1(_opb,&val); + if(!val)break; + } + return i; +} + +static int oc_clc_mode_unpack(oggpack_buffer *_opb){ + long val; + theorapackB_read(_opb,3,&val); + return (int)val; +} + +/*Unpacks the list of macro block modes for INTER frames.*/ +static void oc_dec_mb_modes_unpack(oc_dec_ctx *_dec){ + oc_mode_unpack_func mode_unpack; + oc_mb *mb; + oc_mb *mb_end; + const int *alphabet; + long val; + int scheme0_alphabet[8]; + int mode_scheme; + theorapackB_read(&_dec->opb,3,&val); + mode_scheme=(int)val; + if(mode_scheme==0){ + int mi; + /*Just in case, initialize the modes to something. + If the bitstream doesn't contain each index exactly once, it's likely + corrupt and the rest of the packet is garbage anyway, but this way we + won't crash, and we'll decode SOMETHING.*/ + /*LOOP VECTORIZES.*/ + for(mi=0;miopb,3,&val); + scheme0_alphabet[val]=OC_MODE_ALPHABETS[6][mi]; + } + alphabet=scheme0_alphabet; + } + else alphabet=OC_MODE_ALPHABETS[mode_scheme-1]; + if(mode_scheme==7)mode_unpack=oc_clc_mode_unpack; + else mode_unpack=oc_vlc_mode_unpack; + mb=_dec->state.mbs; + mb_end=mb+_dec->state.nmbs; + for(;mbmode!=OC_MODE_INVALID){ + int bi; + for(bi=0;bi<4;bi++){ + int fragi; + fragi=mb->map[0][bi]; + if(fragi>=0&&_dec->state.frags[fragi].coded)break; + } + if(bi<4)mb->mode=alphabet[(*mode_unpack)(&_dec->opb)]; + else mb->mode=OC_MODE_INTER_NOMV; + } + } +} + + + +typedef int (*oc_mv_comp_unpack_func)(oggpack_buffer *_opb); + +static int oc_vlc_mv_comp_unpack(oggpack_buffer *_opb){ + long bits; + int mvsigned[2]; + theorapackB_read(_opb,3,&bits); + switch(bits){ + case 0:return 0; + case 1:return 1; + case 2:return -1; + case 3: + case 4:{ + mvsigned[0]=(int)(bits-1); + theorapackB_read1(_opb,&bits); + }break; + /*case 5: + case 6: + case 7:*/ + default:{ + mvsigned[0]=1<>1); + bits&=1; + }break; + } + mvsigned[1]=-mvsigned[0]; + return mvsigned[bits]; +} + +static int oc_clc_mv_comp_unpack(oggpack_buffer *_opb){ + long bits; + int mvsigned[2]; + theorapackB_read(_opb,6,&bits); + mvsigned[0]=bits>>1; + mvsigned[1]=-mvsigned[0]; + return mvsigned[bits&1]; +} + +/*Unpacks the list of motion vectors for INTER frames, and propagtes the macro + block modes and motion vectors to the individual fragments.*/ +static void oc_dec_mv_unpack_and_frag_modes_fill(oc_dec_ctx *_dec){ + oc_set_chroma_mvs_func set_chroma_mvs; + oc_mv_comp_unpack_func mv_comp_unpack; + oc_mb *mb; + oc_mb *mb_end; + const int *map_idxs; + long val; + int map_nidxs; + oc_mv last_mv[2]; + oc_mv cbmvs[4]; + set_chroma_mvs=OC_SET_CHROMA_MVS_TABLE[_dec->state.info.pixel_fmt]; + theorapackB_read1(&_dec->opb,&val); + mv_comp_unpack=val?oc_clc_mv_comp_unpack:oc_vlc_mv_comp_unpack; + map_idxs=OC_MB_MAP_IDXS[_dec->state.info.pixel_fmt]; + map_nidxs=OC_MB_MAP_NIDXS[_dec->state.info.pixel_fmt]; + memset(last_mv,0,sizeof(last_mv)); + mb=_dec->state.mbs; + mb_end=mb+_dec->state.nmbs; + for(;mbmode!=OC_MODE_INVALID){ + oc_fragment *frag; + oc_mv mbmv; + int coded[13]; + int codedi; + int ncoded; + int mapi; + int mapii; + int fragi; + int mb_mode; + /*Search for at least one coded fragment.*/ + ncoded=mapii=0; + do{ + mapi=map_idxs[mapii]; + fragi=mb->map[mapi>>2][mapi&3]; + if(fragi>=0&&_dec->state.frags[fragi].coded)coded[ncoded++]=mapi; + } + while(++mapiimode; + switch(mb_mode){ + case OC_MODE_INTER_MV_FOUR:{ + oc_mv lbmvs[4]; + int bi; + /*Mark the tail of the list, so we don't accidentally go past it.*/ + coded[ncoded]=-1; + for(bi=codedi=0;bi<4;bi++){ + if(coded[codedi]==bi){ + codedi++; + frag=_dec->state.frags+mb->map[0][bi]; + frag->mbmode=mb_mode; + frag->mv[0]=lbmvs[bi][0]=(signed char)(*mv_comp_unpack)(&_dec->opb); + frag->mv[1]=lbmvs[bi][1]=(signed char)(*mv_comp_unpack)(&_dec->opb); + } + else lbmvs[bi][0]=lbmvs[bi][1]=0; + } + if(codedi>0){ + last_mv[1][0]=last_mv[0][0]; + last_mv[1][1]=last_mv[0][1]; + last_mv[0][0]=lbmvs[coded[codedi-1]][0]; + last_mv[0][1]=lbmvs[coded[codedi-1]][1]; + } + if(codedistate.frags+mb->map[mapi>>2][bi]; + frag->mbmode=mb_mode; + frag->mv[0]=cbmvs[bi][0]; + frag->mv[1]=cbmvs[bi][1]; + } + } + }break; + case OC_MODE_INTER_MV:{ + last_mv[1][0]=last_mv[0][0]; + last_mv[1][1]=last_mv[0][1]; + mbmv[0]=last_mv[0][0]=(signed char)(*mv_comp_unpack)(&_dec->opb); + mbmv[1]=last_mv[0][1]=(signed char)(*mv_comp_unpack)(&_dec->opb); + }break; + case OC_MODE_INTER_MV_LAST:{ + mbmv[0]=last_mv[0][0]; + mbmv[1]=last_mv[0][1]; + }break; + case OC_MODE_INTER_MV_LAST2:{ + mbmv[0]=last_mv[1][0]; + mbmv[1]=last_mv[1][1]; + last_mv[1][0]=last_mv[0][0]; + last_mv[1][1]=last_mv[0][1]; + last_mv[0][0]=mbmv[0]; + last_mv[0][1]=mbmv[1]; + }break; + case OC_MODE_GOLDEN_MV:{ + mbmv[0]=(signed char)(*mv_comp_unpack)(&_dec->opb); + mbmv[1]=(signed char)(*mv_comp_unpack)(&_dec->opb); + }break; + default:mbmv[0]=mbmv[1]=0;break; + } + /*4MV mode fills in the fragments itself. + For all other modes we can use this common code.*/ + if(mb_mode!=OC_MODE_INTER_MV_FOUR){ + for(codedi=0;codedimap[mapi>>2][mapi&3]; + frag=_dec->state.frags+fragi; + frag->mbmode=mb_mode; + frag->mv[0]=mbmv[0]; + frag->mv[1]=mbmv[1]; + } + } + } +} + +static void oc_dec_block_qis_unpack(oc_dec_ctx *_dec){ + oc_fragment *frag; + int *coded_fragi; + int *coded_fragi_end; + int ncoded_fragis; + ncoded_fragis=_dec->state.ncoded_fragis[0]+ + _dec->state.ncoded_fragis[1]+_dec->state.ncoded_fragis[2]; + if(ncoded_fragis<=0)return; + coded_fragi=_dec->state.coded_fragis; + coded_fragi_end=coded_fragi+ncoded_fragis; + if(_dec->state.nqis==1){ + /*If this frame has only a single qi value, then just set it in all coded + fragments.*/ + while(coded_fragistate.frags[*coded_fragi++].qi=_dec->state.qis[0]; + } + } + else{ + long val; + int flag; + int nqi1; + int run_count; + /*Otherwise, we decode a qi index for each fragment, using two passes of + the same binary RLE scheme used for super-block coded bits. + The first pass marks each fragment as having a qii of 0 or greater than + 0, and the second pass (if necessary), distinguishes between a qii of + 1 and 2. + At first we just store the qii in the fragment. + After all the qii's are decoded, we make a final pass to replace them + with the corresponding qi's for this frame.*/ + theorapackB_read1(&_dec->opb,&val); + flag=(int)val; + run_count=nqi1=0; + while(coded_fragiopb); + full_run=run_count>=4129; + do{ + _dec->state.frags[*coded_fragi++].qi=flag; + nqi1+=flag; + } + while(--run_count>0&&coded_fragiopb,&val); + flag=(int)val; + } + else flag=!flag; + } + /*TODO: run_count should be 0 here. + If it's not, we should issue a warning of some kind.*/ + /*If we have 3 different qi's for this frame, and there was at least one + fragment with a non-zero qi, make the second pass.*/ + if(_dec->state.nqis==3&&nqi1>0){ + /*Skip qii==0 fragments.*/ + for(coded_fragi=_dec->state.coded_fragis; + _dec->state.frags[*coded_fragi].qi==0;coded_fragi++); + theorapackB_read1(&_dec->opb,&val); + flag=(int)val; + while(coded_fragiopb); + full_run=run_count>=4129; + for(;coded_fragistate.frags+*coded_fragi; + if(frag->qi==0)continue; + if(run_count--<=0)break; + frag->qi+=flag; + } + if(full_run&&coded_fragiopb,&val); + flag=(int)val; + } + else flag=!flag; + } + /*TODO: run_count should be 0 here. + If it's not, we should issue a warning of some kind.*/ + } + /*Finally, translate qii's to qi's.*/ + for(coded_fragi=_dec->state.coded_fragis;coded_fragistate.frags+*coded_fragi; + frag->qi=_dec->state.qis[frag->qi]; + } + } +} + + + +/*Returns the decoded value of the given token. + It CANNOT be called for any of the EOB tokens. + _token: The token value to skip. + _extra_bits: The extra bits attached to this token. + Return: The decoded coefficient value.*/ +typedef int (*oc_token_dec1val_func)(int _token,int _extra_bits); + +/*Handles zero run tokens.*/ +static int oc_token_dec1val_zrl(void){ + return 0; +} + +/*Handles 1, -1, 2 and -2 tokens.*/ +static int oc_token_dec1val_const(int _token){ + static const int CONST_VALS[4]={1,-1,2,-2}; + return CONST_VALS[_token-OC_NDCT_ZRL_TOKEN_MAX]; +} + +/*Handles DCT value tokens category 2.*/ +static int oc_token_dec1val_cat2(int _token,int _extra_bits){ + int valsigned[2]; + valsigned[0]=_token-OC_DCT_VAL_CAT2+3; + valsigned[1]=-valsigned[0]; + return valsigned[_extra_bits]; +} + +/*Handles DCT value tokens categories 3 through 8.*/ +static int oc_token_dec1val_cati(int _token,int _extra_bits){ + static const int VAL_CAT_OFFS[6]={ + OC_NDCT_VAL_CAT2_SIZE+3, + OC_NDCT_VAL_CAT2_SIZE+5, + OC_NDCT_VAL_CAT2_SIZE+9, + OC_NDCT_VAL_CAT2_SIZE+17, + OC_NDCT_VAL_CAT2_SIZE+33, + OC_NDCT_VAL_CAT2_SIZE+65 + }; + static const int VAL_CAT_MASKS[6]={ + 0x001,0x003,0x007,0x00F,0x01F,0x1FF + }; + static const int VAL_CAT_SHIFTS[6]={1,2,3,4,5,9}; + int valsigned[2]; + int cati; + cati=_token-OC_NDCT_VAL_CAT2_MAX; + valsigned[0]=VAL_CAT_OFFS[cati]+(_extra_bits&VAL_CAT_MASKS[cati]); + valsigned[1]=-valsigned[0]; + return valsigned[_extra_bits>>VAL_CAT_SHIFTS[cati]&1]; +} + +/*A jump table for compute the first coefficient value the given token value + represents.*/ +static const oc_token_dec1val_func OC_TOKEN_DEC1VAL_TABLE[TH_NDCT_TOKENS- + OC_NDCT_EOB_TOKEN_MAX]={ + (oc_token_dec1val_func)oc_token_dec1val_zrl, + (oc_token_dec1val_func)oc_token_dec1val_zrl, + (oc_token_dec1val_func)oc_token_dec1val_const, + (oc_token_dec1val_func)oc_token_dec1val_const, + (oc_token_dec1val_func)oc_token_dec1val_const, + (oc_token_dec1val_func)oc_token_dec1val_const, + oc_token_dec1val_cat2, + oc_token_dec1val_cat2, + oc_token_dec1val_cat2, + oc_token_dec1val_cat2, + oc_token_dec1val_cati, + oc_token_dec1val_cati, + oc_token_dec1val_cati, + oc_token_dec1val_cati, + oc_token_dec1val_cati, + oc_token_dec1val_cati, + (oc_token_dec1val_func)oc_token_dec1val_zrl, + (oc_token_dec1val_func)oc_token_dec1val_zrl, + (oc_token_dec1val_func)oc_token_dec1val_zrl, + (oc_token_dec1val_func)oc_token_dec1val_zrl, + (oc_token_dec1val_func)oc_token_dec1val_zrl, + (oc_token_dec1val_func)oc_token_dec1val_zrl, + (oc_token_dec1val_func)oc_token_dec1val_zrl, + (oc_token_dec1val_func)oc_token_dec1val_zrl, + (oc_token_dec1val_func)oc_token_dec1val_zrl +}; + +/*Returns the decoded value of the given token. + It CANNOT be called for any of the EOB tokens. + _token: The token value to skip. + _extra_bits: The extra bits attached to this token. + Return: The decoded coefficient value.*/ +static int oc_dct_token_dec1val(int _token,int _extra_bits){ + return (*OC_TOKEN_DEC1VAL_TABLE[_token-OC_NDCT_EOB_TOKEN_MAX])(_token, + _extra_bits); +} + +/*Unpacks the DC coefficient tokens. + Unlike when unpacking the AC coefficient tokens, we actually need to decode + the DC coefficient values now so that we can do DC prediction. + _huff_idx: The index of the Huffman table to use for each color plane. + _ntoks_left: The number of tokens left to be decoded in each color plane for + each coefficient. + This is updated as EOB tokens and zero run tokens are decoded. + Return: The length of any outstanding EOB run.*/ +static int oc_dec_dc_coeff_unpack(oc_dec_ctx *_dec,int _huff_idxs[3], + int _ntoks_left[3][64]){ + long val; + int *coded_fragi; + int *coded_fragi_end; + int run_counts[64]; + int cfi; + int eobi; + int eobs; + int ti; + int ebi; + int pli; + int rli; + eobs=0; + ti=ebi=0; + coded_fragi_end=coded_fragi=_dec->state.coded_fragis; + for(pli=0;pli<3;pli++){ + coded_fragi_end+=_dec->state.ncoded_fragis[pli]; + memset(run_counts,0,sizeof(run_counts)); + _dec->eob_runs[pli][0]=eobs; + /*Continue any previous EOB run, if there was one.*/ + for(eobi=eobs;eobi-->0&&coded_fragistate.frags[*coded_fragi++].dc=0; + } + cfi=0; + while(eobs<_ntoks_left[pli][0]-cfi){ + int token; + int neb; + int eb; + int skip; + cfi+=eobs; + run_counts[63]+=eobs; + token=oc_huff_token_decode(&_dec->opb, + _dec->huff_tables[_huff_idxs[pli]]); + _dec->dct_tokens[0][ti++]=(unsigned char)token; + neb=OC_DCT_TOKEN_EXTRA_BITS[token]; + if(neb){ + theorapackB_read(&_dec->opb,neb,&val); + eb=(int)val; + _dec->extra_bits[0][ebi++]=(ogg_uint16_t)eb; + } + else eb=0; + skip=oc_dct_token_skip(token,eb); + if(skip<0){ + eobs=eobi=-skip; + while(eobi-->0&&coded_fragistate.frags[*coded_fragi++].dc=0; + } + } + else{ + run_counts[skip-1]++; + cfi++; + eobs=0; + _dec->state.frags[*coded_fragi++].dc=oc_dct_token_dec1val(token,eb); + } + } + _dec->ti0[pli][0]=ti; + _dec->ebi0[pli][0]=ebi; + /*Set the EOB count to the portion of the last EOB run which extends past + this coefficient.*/ + eobs=eobs+cfi-_ntoks_left[pli][0]; + /*Add the portion of the last EOB which was included in this coefficient to + to the longest run length.*/ + run_counts[63]+=_ntoks_left[pli][0]-cfi; + /*And convert the run_counts array to a moment table.*/ + for(rli=63;rli-->0;)run_counts[rli]+=run_counts[rli+1]; + /*Finally, subtract off the number of coefficients that have been + accounted for by runs started in this coefficient.*/ + for(rli=64;rli-->0;)_ntoks_left[pli][rli]-=run_counts[rli]; + } + return eobs; +} + +/*Unpacks the AC coefficient tokens. + This can completely discard coefficient values while unpacking, and so is + somewhat simpler than unpacking the DC coefficient tokens. + _huff_idx: The index of the Huffman table to use for each color plane. + _ntoks_left: The number of tokens left to be decoded in each color plane for + each coefficient. + This is updated as EOB tokens and zero run tokens are decoded. + _eobs: The length of any outstanding EOB run from previous + coefficients. + Return: The length of any outstanding EOB run.*/ +static int oc_dec_ac_coeff_unpack(oc_dec_ctx *_dec,int _zzi,int _huff_idxs[3], + int _ntoks_left[3][64],int _eobs){ + long val; + int run_counts[64]; + int cfi; + int ti; + int ebi; + int pli; + int rli; + ti=ebi=0; + for(pli=0;pli<3;pli++){ + memset(run_counts,0,sizeof(run_counts)); + _dec->eob_runs[pli][_zzi]=_eobs; + cfi=0; + while(_eobs<_ntoks_left[pli][_zzi]-cfi){ + int token; + int neb; + int eb; + int skip; + cfi+=_eobs; + run_counts[63]+=_eobs; + token=oc_huff_token_decode(&_dec->opb, + _dec->huff_tables[_huff_idxs[pli]]); + _dec->dct_tokens[_zzi][ti++]=(unsigned char)token; + neb=OC_DCT_TOKEN_EXTRA_BITS[token]; + if(neb){ + theorapackB_read(&_dec->opb,neb,&val); + eb=(int)val; + _dec->extra_bits[_zzi][ebi++]=(ogg_uint16_t)eb; + } + else eb=0; + skip=oc_dct_token_skip(token,eb); + if(skip<0)_eobs=-skip; + else{ + run_counts[skip-1]++; + cfi++; + _eobs=0; + } + } + _dec->ti0[pli][_zzi]=ti; + _dec->ebi0[pli][_zzi]=ebi; + /*Set the EOB count to the portion of the last EOB run which extends past + this coefficient.*/ + _eobs=_eobs+cfi-_ntoks_left[pli][_zzi]; + /*Add the portion of the last EOB which was included in this coefficient to + to the longest run length.*/ + run_counts[63]+=_ntoks_left[pli][_zzi]-cfi; + /*And convert the run_counts array to a moment table.*/ + for(rli=63;rli-->0;)run_counts[rli]+=run_counts[rli+1]; + /*Finally, subtract off the number of coefficients that have been + accounted for by runs started in this coefficient.*/ + for(rli=64-_zzi;rli-->0;)_ntoks_left[pli][_zzi+rli]-=run_counts[rli]; + } + return _eobs; +} + +/*Tokens describing the DCT coefficients that belong to each fragment are + stored in the bitstream grouped by coefficient, not by fragment. + + This means that we either decode all the tokens in order, building up a + separate coefficient list for each fragment as we go, and then go back and + do the iDCT on each fragment, or we have to create separate lists of tokens + for each coefficient, so that we can pull the next token required off the + head of the appropriate list when decoding a specific fragment. + + The former was VP3's choice, and it meant 2*w*h extra storage for all the + decoded coefficient values. + + We take the second option, which lets us store just one or three bytes per + token (generally far fewer than the number of coefficients, due to EOB + tokens and zero runs), and which requires us to only maintain a counter for + each of the 64 coefficients, instead of a counter for every fragment to + determine where the next token goes. + + Actually, we use 3 counters per coefficient, one for each color plane, so we + can decode all color planes simultaneously. + + This lets color conversion, etc., be done as soon as a full MCU (one or + two super block rows) is decoded, while the image data is still in cache.*/ + +static void oc_dec_residual_tokens_unpack(oc_dec_ctx *_dec){ + static const int OC_HUFF_LIST_MAX[5]={1,6,15,28,64}; + long val; + int ntoks_left[3][64]; + int huff_idxs[3]; + int pli; + int zzi; + int hgi; + int huffi_y; + int huffi_c; + int eobs; + for(pli=0;pli<3;pli++)for(zzi=0;zzi<64;zzi++){ + ntoks_left[pli][zzi]=_dec->state.ncoded_fragis[pli]; + } + theorapackB_read(&_dec->opb,4,&val); + huffi_y=(int)val; + theorapackB_read(&_dec->opb,4,&val); + huffi_c=(int)val; + huff_idxs[0]=huffi_y; + huff_idxs[1]=huff_idxs[2]=huffi_c; + _dec->eob_runs[0][0]=0; + eobs=oc_dec_dc_coeff_unpack(_dec,huff_idxs,ntoks_left); + theorapackB_read(&_dec->opb,4,&val); + huffi_y=(int)val; + theorapackB_read(&_dec->opb,4,&val); + huffi_c=(int)val; + zzi=1; + for(hgi=1;hgi<5;hgi++){ + huff_idxs[0]=huffi_y+(hgi<<4); + huff_idxs[1]=huff_idxs[2]=huffi_c+(hgi<<4); + for(;zzi0); + *_zzi=zzi; +} + +/*Expands a constant, single-value token.*/ +static void oc_token_expand_const(int _token,int _extra_bits, + ogg_int16_t _dct_coeffs[128],int *_zzi){ + _dct_coeffs[(*_zzi)++]=(ogg_int16_t)oc_token_dec1val_const(_token); +} + +/*Expands category 2 single-valued tokens.*/ +static void oc_token_expand_cat2(int _token,int _extra_bits, + ogg_int16_t _dct_coeffs[128],int *_zzi){ + _dct_coeffs[(*_zzi)++]= + (ogg_int16_t)oc_token_dec1val_cat2(_token,_extra_bits); +} + +/*Expands category 3 through 8 single-valued tokens.*/ +static void oc_token_expand_cati(int _token,int _extra_bits, + ogg_int16_t _dct_coeffs[128],int *_zzi){ + _dct_coeffs[(*_zzi)++]= + (ogg_int16_t)oc_token_dec1val_cati(_token,_extra_bits); +} + +/*Expands a category 1a zero run/value combo token.*/ +static void oc_token_expand_run_cat1a(int _token,int _extra_bits, + ogg_int16_t _dct_coeffs[128],int *_zzi){ + int zzi; + int rl; + zzi=*_zzi; + /*LOOP VECTORIZES.*/ + for(rl=_token-OC_DCT_RUN_CAT1A+1;rl-->0;)_dct_coeffs[zzi++]=0; + _dct_coeffs[zzi++]=(ogg_int16_t)(1-(_extra_bits<<1)); + *_zzi=zzi; +} + +/*Expands all other zero run/value combo tokens.*/ +static void oc_token_expand_run(int _token,int _extra_bits, + ogg_int16_t _dct_coeffs[128],int *_zzi){ + static const int NZEROS_ADJUST[OC_NDCT_RUN_MAX-OC_DCT_RUN_CAT1B]={ + 6,10,1,2 + }; + static const int NZEROS_MASK[OC_NDCT_RUN_MAX-OC_DCT_RUN_CAT1B]={ + 3,7,0,1 + }; + static const int VALUE_SHIFT[OC_NDCT_RUN_MAX-OC_DCT_RUN_CAT1B]={ + 0,0,0,1 + }; + static const int VALUE_MASK[OC_NDCT_RUN_MAX-OC_DCT_RUN_CAT1B]={ + 0,0,1,1 + }; + static const int VALUE_ADJUST[OC_NDCT_RUN_MAX-OC_DCT_RUN_CAT1B]={ + 1,1,2,2 + }; + static const int SIGN_SHIFT[OC_NDCT_RUN_MAX-OC_DCT_RUN_CAT1B]={ + 2,3,1,2 + }; + int valsigned[2]; + int zzi; + int rl; + _token-=OC_DCT_RUN_CAT1B; + rl=(_extra_bits&NZEROS_MASK[_token])+NZEROS_ADJUST[_token]; + zzi=*_zzi; + /*LOOP VECTORIZES.*/ + while(rl-->0)_dct_coeffs[zzi++]=0; + valsigned[0]=VALUE_ADJUST[_token]+ + (_extra_bits>>VALUE_SHIFT[_token]&VALUE_MASK[_token]); + valsigned[1]=-valsigned[0]; + _dct_coeffs[zzi++]=(ogg_int16_t)valsigned[ + _extra_bits>>SIGN_SHIFT[_token]]; + *_zzi=zzi; +} + +/*A jump table for expanding token values into coefficient values. + This reduces all the conditional branches, etc., needed to parse these token + values down to one indirect jump.*/ +static const oc_token_expand_func OC_TOKEN_EXPAND_TABLE[TH_NDCT_TOKENS- + OC_NDCT_EOB_TOKEN_MAX]={ + oc_token_expand_zrl, + oc_token_expand_zrl, + oc_token_expand_const, + oc_token_expand_const, + oc_token_expand_const, + oc_token_expand_const, + oc_token_expand_cat2, + oc_token_expand_cat2, + oc_token_expand_cat2, + oc_token_expand_cat2, + oc_token_expand_cati, + oc_token_expand_cati, + oc_token_expand_cati, + oc_token_expand_cati, + oc_token_expand_cati, + oc_token_expand_cati, + oc_token_expand_run_cat1a, + oc_token_expand_run_cat1a, + oc_token_expand_run_cat1a, + oc_token_expand_run_cat1a, + oc_token_expand_run_cat1a, + oc_token_expand_run, + oc_token_expand_run, + oc_token_expand_run, + oc_token_expand_run +}; + +/*Expands a single token into the given coefficient list. + This fills in the zeros for zero runs as well as coefficient values, and + updates the index of the current coefficient. + It CANNOT be called for any of the EOB tokens. + _token: The token value to expand. + _extra_bits: The extra bits associated with the token. + _dct_coeffs: The current list of coefficients, in zig-zag order. + _zzi: A pointer to the zig-zag index of the next coefficient to write + to. + This is updated before the function returns.*/ +static void oc_dct_token_expand(int _token,int _extra_bits, + ogg_int16_t *_dct_coeffs,int *_zzi){ + (*OC_TOKEN_EXPAND_TABLE[_token-OC_NDCT_EOB_TOKEN_MAX])(_token, + _extra_bits,_dct_coeffs,_zzi); +} + + + +static int oc_dec_postprocess_init(oc_dec_ctx *_dec){ + /*pp_level 0: disabled; free any memory used and return*/ + if(_dec->pp_level<=OC_PP_LEVEL_DISABLED){ + if(_dec->dc_qis!=NULL){ + _ogg_free(_dec->dc_qis); + _dec->dc_qis=NULL; + _ogg_free(_dec->variances); + _dec->variances=NULL; + _ogg_free(_dec->pp_frame_data); + _dec->pp_frame_data=NULL; + } + return 1; + } + if(_dec->dc_qis==NULL){ + /*If we haven't been tracking DC quantization indices, there's no point in + starting now.*/ + if(_dec->state.frame_type!=OC_INTRA_FRAME)return 1; + _dec->dc_qis=(unsigned char *)_ogg_malloc( + _dec->state.nfrags*sizeof(_dec->dc_qis[0])); + memset(_dec->dc_qis,_dec->state.qis[0],_dec->state.nfrags); + } + else{ + int *coded_fragi; + int *coded_fragi_end; + unsigned char qi0; + /*Update the DC quantization index of each coded block.*/ + qi0=(unsigned char)_dec->state.qis[0]; + coded_fragi_end=_dec->state.coded_fragis+_dec->state.ncoded_fragis[0]+ + _dec->state.ncoded_fragis[1]+_dec->state.ncoded_fragis[2]; + for(coded_fragi=_dec->state.coded_fragis;coded_fragidc_qis[*coded_fragi]=qi0; + } + } + /*pp_level 1: Stop after updating DC quantization indices.*/ + if(_dec->pp_level<=OC_PP_LEVEL_TRACKDCQI){ + if(_dec->variances!=NULL){ + _ogg_free(_dec->variances); + _dec->variances=NULL; + _ogg_free(_dec->pp_frame_data); + _dec->pp_frame_data=NULL; + } + return 1; + } + if(_dec->variances==NULL|| + _dec->pp_frame_has_chroma!=(_dec->pp_level>=OC_PP_LEVEL_DEBLOCKC)){ + size_t frame_sz; + frame_sz=_dec->state.info.frame_width*_dec->state.info.frame_height; + if(_dec->pp_levelvariances=(int *)_ogg_realloc(_dec->variances, + _dec->state.fplanes[0].nfrags*sizeof(_dec->variances[0])); + _dec->pp_frame_data=(unsigned char *)_ogg_realloc( + _dec->pp_frame_data,frame_sz*sizeof(_dec->pp_frame_data[0])); + _dec->pp_frame_buf[0].width=_dec->state.info.frame_width; + _dec->pp_frame_buf[0].height=_dec->state.info.frame_height; + _dec->pp_frame_buf[0].stride=-_dec->pp_frame_buf[0].width; + _dec->pp_frame_buf[0].data=_dec->pp_frame_data+ + (1-_dec->pp_frame_buf[0].height)*_dec->pp_frame_buf[0].stride; + } + else{ + size_t y_sz; + size_t c_sz; + int c_w; + int c_h; + _dec->variances=(int *)_ogg_realloc(_dec->variances, + _dec->state.nfrags*sizeof(_dec->variances[0])); + y_sz=frame_sz; + c_w=_dec->state.info.frame_width>>!(_dec->state.info.pixel_fmt&1); + c_h=_dec->state.info.frame_height>>!(_dec->state.info.pixel_fmt&2); + c_sz=c_w*c_h; + frame_sz+=c_sz<<1; + _dec->pp_frame_data=(unsigned char *)_ogg_realloc( + _dec->pp_frame_data,frame_sz*sizeof(_dec->pp_frame_data[0])); + _dec->pp_frame_buf[0].width=_dec->state.info.frame_width; + _dec->pp_frame_buf[0].height=_dec->state.info.frame_height; + _dec->pp_frame_buf[0].stride=_dec->pp_frame_buf[0].width; + _dec->pp_frame_buf[0].data=_dec->pp_frame_data; + _dec->pp_frame_buf[1].width=c_w; + _dec->pp_frame_buf[1].height=c_h; + _dec->pp_frame_buf[1].stride=_dec->pp_frame_buf[1].width; + _dec->pp_frame_buf[1].data=_dec->pp_frame_buf[0].data+y_sz; + _dec->pp_frame_buf[2].width=c_w; + _dec->pp_frame_buf[2].height=c_h; + _dec->pp_frame_buf[2].stride=_dec->pp_frame_buf[2].width; + _dec->pp_frame_buf[2].data=_dec->pp_frame_buf[1].data+c_sz; + oc_ycbcr_buffer_flip(_dec->pp_frame_buf,_dec->pp_frame_buf); + } + _dec->pp_frame_has_chroma=(_dec->pp_level>=OC_PP_LEVEL_DEBLOCKC); + } + /*If we're not processing chroma, copy the reference frame's chroma planes.*/ + if(_dec->pp_levelpp_frame_buf+1, + _dec->state.ref_frame_bufs[_dec->state.ref_frame_idx[OC_FRAME_SELF]]+1, + sizeof(_dec->pp_frame_buf[1])*2); + } + return 0; +} + + + +typedef struct{ + int ti[3][64]; + int ebi[3][64]; + int eob_runs[3][64]; + int bounding_values[256]; + int *coded_fragis[3]; + int *uncoded_fragis[3]; + int fragy0[3]; + int fragy_end[3]; + int ncoded_fragis[3]; + int nuncoded_fragis[3]; + int pred_last[3][3]; + int mcu_nvfrags; + int loop_filter; + int pp_level; +}oc_dec_pipeline_state; + + + +/*Initialize the main decoding pipeline.*/ +static void oc_dec_pipeline_init(oc_dec_ctx *_dec, + oc_dec_pipeline_state *_pipe){ + int *coded_fragi_end; + int *uncoded_fragi_end; + int pli; + /*If chroma is sub-sampled in the vertical direction, we have to decode two + super block rows of Y' for each super block row of Cb and Cr.*/ + _pipe->mcu_nvfrags=4<state.info.pixel_fmt&2); + /*Initialize the token and extra bits indices for each plane and + coefficient.*/ + memset(_pipe->ti[0],0,sizeof(_pipe->ti[0])); + memset(_pipe->ebi[0],0,sizeof(_pipe->ebi[0])); + for(pli=1;pli<3;pli++){ + memcpy(_pipe->ti[pli],_dec->ti0[pli-1],sizeof(_pipe->ti[0])); + memcpy(_pipe->ebi[pli],_dec->ebi0[pli-1],sizeof(_pipe->ebi[0])); + } + /*Also copy over the initial the EOB run counts.*/ + memcpy(_pipe->eob_runs,_dec->eob_runs,sizeof(_pipe->eob_runs)); + /*Set up per-plane pointers to the coded and uncoded fragments lists.*/ + coded_fragi_end=_dec->state.coded_fragis; + uncoded_fragi_end=_dec->state.uncoded_fragis; + for(pli=0;pli<3;pli++){ + _pipe->coded_fragis[pli]=coded_fragi_end; + _pipe->uncoded_fragis[pli]=uncoded_fragi_end; + coded_fragi_end+=_dec->state.ncoded_fragis[pli]; + uncoded_fragi_end-=_dec->state.nuncoded_fragis[pli]; + } + /*Set the previous DC predictor to 0 for all color planes and frame types.*/ + memset(_pipe->pred_last,0,sizeof(_pipe->pred_last)); + /*Initialize the bounding value array for the loop filter.*/ + _pipe->loop_filter=!oc_state_loop_filter_init(&_dec->state, + _pipe->bounding_values); + /*Initialize any buffers needed for post-processing. + We also save the current post-processing level, to guard against the user + changing it from a callback.*/ + if(!oc_dec_postprocess_init(_dec))_pipe->pp_level=_dec->pp_level; + /*If we don't have enough information to post-process, disable it, regardless + of the user-requested level.*/ + else{ + _pipe->pp_level=OC_PP_LEVEL_DISABLED; + memcpy(_dec->pp_frame_buf, + _dec->state.ref_frame_bufs[_dec->state.ref_frame_idx[OC_FRAME_SELF]], + sizeof(_dec->pp_frame_buf[0])*3); + } +} + +/*Undo the DC prediction in a single plane of an MCU (one or two super block + rows). + As a side effect, the number of coded and uncoded fragments in this plane of + the MCU is also computed.*/ +static void oc_dec_dc_unpredict_mcu_plane(oc_dec_ctx *_dec, + oc_dec_pipeline_state *_pipe,int _pli){ + /*Undo the DC prediction.*/ + oc_fragment_plane *fplane; + oc_fragment *frag; + int *pred_last; + int ncoded_fragis; + int fragx; + int fragy; + int fragy0; + int fragy_end; + /*Compute the first and last fragment row of the current MCU for this + plane.*/ + fplane=_dec->state.fplanes+_pli; + fragy0=_pipe->fragy0[_pli]; + fragy_end=_pipe->fragy_end[_pli]; + frag=_dec->state.frags+fplane->froffset+(fragy0*fplane->nhfrags); + ncoded_fragis=0; + pred_last=_pipe->pred_last[_pli]; + for(fragy=fragy0;fragynhfrags;fragx++,frag++){ + if(!frag->coded)continue; + pred_last[OC_FRAME_FOR_MODE[frag->mbmode]]=frag->dc+= + oc_frag_pred_dc(frag,fplane,fragx,fragy,pred_last); + ncoded_fragis++; + } + } + _pipe->ncoded_fragis[_pli]=ncoded_fragis; + /*Also save the number of uncoded fragments so we know how many to copy.*/ + _pipe->nuncoded_fragis[_pli]= + (fragy_end-fragy0)*fplane->nhfrags-ncoded_fragis; +} + +/*Reconstructs all coded fragments in a single MCU (one or two super block + rows). + This requires that each coded fragment have a proper macro block mode and + motion vector (if not in INTRA mode), and have it's DC value decoded, with + the DC prediction process reversed, and the number of coded and uncoded + fragments in this plane of the MCU be counted. + The token lists for each color plane and coefficient should also be filled + in, along with initial token offsets, extra bits offsets, and EOB run + counts.*/ +static void oc_dec_frags_recon_mcu_plane(oc_dec_ctx *_dec, + oc_dec_pipeline_state *_pipe,int _pli){ + /*Decode the AC coefficients.*/ + int *ti; + int *ebi; + int *eob_runs; + int *coded_fragi; + int *coded_fragi_end; + ti=_pipe->ti[_pli]; + ebi=_pipe->ebi[_pli]; + eob_runs=_pipe->eob_runs[_pli]; + coded_fragi_end=coded_fragi=_pipe->coded_fragis[_pli]; + coded_fragi_end+=_pipe->ncoded_fragis[_pli]; + for(;coded_fragistate.frags+fragi; + for(zzi=0;zzi<64;){ + int token; + int eb; + last_zzi=zzi; + if(eob_runs[zzi]){ + eob_runs[zzi]--; + break; + } + else{ + int ebflag; + token=_dec->dct_tokens[zzi][ti[zzi]++]; + ebflag=OC_DCT_TOKEN_EXTRA_BITS[token]!=0; + eb=_dec->extra_bits[zzi][ebi[zzi]]&-ebflag; + ebi[zzi]+=ebflag; + if(tokendc; + iquants=_dec->state.dequant_tables[frag->mbmode!=OC_MODE_INTRA][_pli]; + /*last_zzi is always initialized. + If your compiler thinks otherwise, it is dumb.*/ + oc_state_frag_recon(&_dec->state,frag,_pli,dct_coeffs,last_zzi,zzi, + iquants[_dec->state.qis[0]][0],iquants[frag->qi]); + } + _pipe->coded_fragis[_pli]=coded_fragi; + /*Right now the reconstructed MCU has only the coded blocks in it.*/ + /*TODO: We make the decision here to always copy the uncoded blocks into it + from the reference frame. + We could also copy the coded blocks back over the reference frame, if we + wait for an additional MCU to be decoded, which might be faster if only a + small number of blocks are coded. + However, this introduces more latency, creating a larger cache footprint. + It's unknown which decision is better, but this one results in simpler + code, and the hard case (high bitrate, high resolution) is handled + correctly.*/ + /*Copy the uncoded blocks from the previous reference frame.*/ + _pipe->uncoded_fragis[_pli]-=_pipe->nuncoded_fragis[_pli]; + oc_state_frag_copy(&_dec->state,_pipe->uncoded_fragis[_pli], + _pipe->nuncoded_fragis[_pli],OC_FRAME_SELF,OC_FRAME_PREV,_pli); +} + +/*Filter a horizontal block edge.*/ +static void oc_filter_hedge(unsigned char *_dst,int _dst_ystride, + const unsigned char *_src,int _src_ystride,int _qstep,int _flimit, + int *_variance0,int *_variance1){ + unsigned char *rdst; + const unsigned char *rsrc; + unsigned char *cdst; + const unsigned char *csrc; + int r[10]; + int sum0; + int sum1; + int bx; + int by; + rdst=_dst; + rsrc=_src; + for(bx=0;bx<8;bx++){ + cdst=rdst; + csrc=rsrc; + for(by=0;by<10;by++){ + r[by]=*csrc; + csrc+=_src_ystride; + } + sum0=sum1=0; + for(by=0;by<4;by++){ + sum0+=abs(r[by+1]-r[by]); + sum1+=abs(r[by+5]-r[by+6]); + } + *_variance0+=OC_MINI(255,sum0); + *_variance1+=OC_MINI(255,sum1); + if(sum0<_flimit&&sum1<_flimit&&r[5]-r[4]<_qstep&&r[4]-r[5]<_qstep){ + *cdst=(unsigned char)(r[0]*3+r[1]*2+r[2]+r[3]+r[4]+4>>3); + cdst+=_dst_ystride; + *cdst=(unsigned char)(r[0]*2+r[1]+r[2]*2+r[3]+r[4]+r[5]+4>>3); + cdst+=_dst_ystride; + for(by=0;by<4;by++){ + *cdst=(unsigned char)(r[by]+r[by+1]+r[by+2]+r[by+3]*2+ + r[by+4]+r[by+5]+r[by+6]+4>>3); + cdst+=_dst_ystride; + } + *cdst=(unsigned char)(r[4]+r[5]+r[6]+r[7]*2+r[8]+r[9]*2+4>>3); + cdst+=_dst_ystride; + *cdst=(unsigned char)(r[5]+r[6]+r[7]+r[8]*2+r[9]*3+4>>3); + } + else{ + for(by=1;by<=8;by++){ + *cdst=(unsigned char)r[by]; + cdst+=_dst_ystride; + } + } + rdst++; + rsrc++; + } +} + +/*Filter a vertical block edge.*/ +static void oc_filter_vedge(unsigned char *_dst,int _dst_ystride, + int _qstep,int _flimit,int *_variances){ + unsigned char *rdst; + const unsigned char *rsrc; + unsigned char *cdst; + int r[10]; + int sum0; + int sum1; + int bx; + int by; + cdst=_dst; + for(by=0;by<8;by++){ + rsrc=cdst-1; + rdst=cdst; + for(bx=0;bx<10;bx++)r[bx]=*rsrc++; + sum0=sum1=0; + for(bx=0;bx<4;bx++){ + sum0+=abs(r[bx+1]-r[bx]); + sum1+=abs(r[bx+5]-r[bx+6]); + } + _variances[0]+=OC_MINI(255,sum0); + _variances[1]+=OC_MINI(255,sum1); + if(sum0<_flimit&&sum1<_flimit&&r[5]-r[4]<_qstep&&r[4]-r[5]<_qstep){ + *rdst++=(unsigned char)(r[0]*3+r[1]*2+r[2]+r[3]+r[4]+4>>3); + *rdst++=(unsigned char)(r[0]*2+r[1]+r[2]*2+r[3]+r[4]+r[5]+4>>3); + for(bx=0;bx<4;bx++){ + *rdst++=(unsigned char)(r[bx]+r[bx+1]+r[bx+2]+r[bx+3]*2+ + r[bx+4]+r[bx+5]+r[bx+6]+4>>3); + } + *rdst++=(unsigned char)(r[4]+r[5]+r[6]+r[7]*2+r[8]+r[9]*2+4>>3); + *rdst=(unsigned char)(r[5]+r[6]+r[7]+r[8]*2+r[9]*3+4>>3); + } + else for(bx=1;bx<=8;bx++)*rdst++=(unsigned char)r[bx]; + cdst+=_dst_ystride; + } +} + +static void oc_dec_deblock_frag_rows(oc_dec_ctx *_dec, + th_img_plane *_dst,th_img_plane *_src,int _pli,int _fragy0, + int _fragy_end){ + oc_fragment_plane *fplane; + int *variance; + unsigned char *dc_qi; + unsigned char *dst; + const unsigned char *src; + int notstart; + int notdone; + int froffset; + int flimit; + int qstep; + int y_end; + int y; + int x; + _dst+=_pli; + _src+=_pli; + fplane=_dec->state.fplanes+_pli; + froffset=fplane->froffset+_fragy0*fplane->nhfrags; + variance=_dec->variances+froffset; + dc_qi=_dec->dc_qis+froffset; + notstart=_fragy0>0; + notdone=_fragy_endnvfrags; + /*We want to clear an extra row of variances, except at the end.*/ + memset(variance+(fplane->nhfrags&-notstart),0, + (_fragy_end+notdone-_fragy0-notstart)*fplane->nhfrags*sizeof(variance[0])); + /*Except for the first time, we want to point to the middle of the row.*/ + y=(_fragy0<<3)+(notstart<<2); + dst=_dst->data+y*_dst->stride; + src=_src->data+y*_src->stride; + for(;y<4;y++){ + memcpy(dst,src,_dst->width*sizeof(dst[0])); + dst+=_dst->stride; + src+=_src->stride; + } + /*We also want to skip the last row in the frame for this loop.*/ + y_end=_fragy_end-!notdone<<3; + for(;ypp_dc_scale[*dc_qi]; + flimit=(qstep*3)>>2; + oc_filter_hedge(dst,_dst->stride,src-_src->stride,_src->stride, + qstep,flimit,variance,variance+fplane->nhfrags); + variance++; + dc_qi++; + for(x=8;x<_dst->width;x+=8){ + qstep=_dec->pp_dc_scale[*dc_qi]; + flimit=(qstep*3)>>2; + oc_filter_hedge(dst+x,_dst->stride,src+x-_src->stride,_src->stride, + qstep,flimit,variance,variance+fplane->nhfrags); + oc_filter_vedge(dst+x-(_dst->stride<<2)-4,_dst->stride, + qstep,flimit,variance-1); + variance++; + dc_qi++; + } + dst+=_dst->stride<<3; + src+=_src->stride<<3; + } + /*And finally, handle the last row in the frame, if it's in the range.*/ + if(!notdone){ + for(;y<_dst->height;y++){ + memcpy(dst,src,_dst->width*sizeof(dst[0])); + dst+=_dst->stride; + src+=_src->stride; + } + /*Filter the last row of vertical block edges.*/ + dc_qi++; + for(x=8;x<_dst->width;x+=8){ + qstep=_dec->pp_dc_scale[*dc_qi++]; + flimit=(qstep*3)>>2; + oc_filter_vedge(dst+x-(_dst->stride<<3)-4,_dst->stride, + qstep,flimit,variance++); + } + } +} + +static void oc_dering_block(unsigned char *_idata,int _ystride,int _b, + int _dc_scale,int _sharp_mod,int _strong){ + static const int MOD_MAX[2]={24,32}; + static const int MOD_SHIFT[2]={1,0}; + const unsigned char *psrc; + const unsigned char *src; + const unsigned char *nsrc; + unsigned char *dst; + int vmod[72]; + int hmod[72]; + int mod_hi; + int by; + int bx; + mod_hi=OC_MINI(3*_dc_scale,MOD_MAX[_strong]); + dst=_idata; + src=dst; + psrc=src-(_ystride&-!(_b&4)); + for(by=0;by<9;by++){ + for(bx=0;bx<8;bx++){ + int mod; + mod=32+_dc_scale-(abs(src[bx]-psrc[bx])<>7); + for(bx=1;bx<7;bx++){ + a=128; + b=64; + w=hmod[(bx<<3)+by]; + a-=w; + b+=w*src[bx-1]; + w=vmod[(by<<3)+bx]; + a-=w; + b+=w*psrc[bx]; + w=vmod[(by+1<<3)+bx]; + a-=w; + b+=w*nsrc[bx]; + w=hmod[(bx+1<<3)+by]; + a-=w; + b+=w*src[bx+1]; + dst[bx]=OC_CLAMP255(a*src[bx]+b>>7); + } + a=128; + b=64; + w=hmod[(7<<3)+by]; + a-=w; + b+=w*src[6]; + w=vmod[(by<<3)+7]; + a-=w; + b+=w*psrc[7]; + w=vmod[(by+1<<3)+7]; + a-=w; + b+=w*nsrc[7]; + w=hmod[(8<<3)+by]; + a-=w; + b+=w*src[7+!(_b&2)]; + dst[7]=OC_CLAMP255(a*src[7]+b>>7); + dst+=_ystride; + psrc=src; + src=nsrc; + nsrc+=_ystride&-(!(_b&8)|by<6); + } +} + +#define OC_DERING_THRESH1 (384) +#define OC_DERING_THRESH2 (4*OC_DERING_THRESH1) +#define OC_DERING_THRESH3 (5*OC_DERING_THRESH1) +#define OC_DERING_THRESH4 (10*OC_DERING_THRESH1) + +static void oc_dec_dering_frag_rows(oc_dec_ctx *_dec,th_img_plane *_img, + int _pli,int _fragy0,int _fragy_end){ + th_img_plane *iplane; + oc_fragment_plane *fplane; + oc_fragment *frag; + int *variance; + unsigned char *idata; + int sthresh; + int strong; + int froffset; + int y_end; + int y; + int x; + iplane=_img+_pli; + fplane=_dec->state.fplanes+_pli; + froffset=fplane->froffset+_fragy0*fplane->nhfrags; + variance=_dec->variances+froffset; + frag=_dec->state.frags+froffset; + strong=_dec->pp_level>=(_pli?OC_PP_LEVEL_SDERINGC:OC_PP_LEVEL_SDERINGY); + sthresh=_pli?OC_DERING_THRESH4:OC_DERING_THRESH3; + y=_fragy0<<3; + idata=iplane->data+y*iplane->stride; + y_end=_fragy_end<<3; + for(;ywidth;x+=8){ + int b; + int qi; + int var; + qi=frag->qi; + var=*variance; + b=(x<=0)|(x+8>=iplane->width)<<1|(y<=0)<<2|(y+8>=iplane->height)<<3; + if(strong&&var>sthresh){ + oc_dering_block(idata+x,iplane->stride,b, + _dec->pp_dc_scale[qi],_dec->pp_sharp_mod[qi],1); + if(_pli||!(b&1)&&*(variance-1)>OC_DERING_THRESH4|| + !(b&2)&&variance[1]>OC_DERING_THRESH4|| + !(b&4)&&*(variance-fplane->nvfrags)>OC_DERING_THRESH4|| + !(b&8)&&variance[fplane->nvfrags]>OC_DERING_THRESH4){ + oc_dering_block(idata+x,iplane->stride,b, + _dec->pp_dc_scale[qi],_dec->pp_sharp_mod[qi],1); + oc_dering_block(idata+x,iplane->stride,b, + _dec->pp_dc_scale[qi],_dec->pp_sharp_mod[qi],1); + } + } + else if(var>OC_DERING_THRESH2){ + oc_dering_block(idata+x,iplane->stride,b, + _dec->pp_dc_scale[qi],_dec->pp_sharp_mod[qi],1); + } + else if(var>OC_DERING_THRESH1){ + oc_dering_block(idata+x,iplane->stride,b, + _dec->pp_dc_scale[qi],_dec->pp_sharp_mod[qi],0); + } + frag++; + variance++; + } + idata+=iplane->stride<<3; + } +} + + + +th_dec_ctx *th_decode_alloc(const th_info *_info, + const th_setup_info *_setup){ + oc_dec_ctx *dec; + if(_info==NULL||_setup==NULL)return NULL; + dec=_ogg_malloc(sizeof(*dec)); + if(oc_dec_init(dec,_info,_setup)<0){ + _ogg_free(dec); + return NULL; + } + dec->state.curframe_num=0; + return dec; +} + +void th_decode_free(th_dec_ctx *_dec){ + if(_dec!=NULL){ + oc_dec_clear(_dec); + _ogg_free(_dec); + } +} + +int th_decode_ctl(th_dec_ctx *_dec,int _req,void *_buf, + size_t _buf_sz){ + switch(_req){ + case TH_DECCTL_GET_PPLEVEL_MAX:{ + if(_dec==NULL||_buf==NULL)return TH_EFAULT; + if(_buf_sz!=sizeof(int))return TH_EINVAL; + (*(int *)_buf)=OC_PP_LEVEL_MAX; + return 0; + }break; + case TH_DECCTL_SET_PPLEVEL:{ + int pp_level; + if(_dec==NULL||_buf==NULL)return TH_EFAULT; + if(_buf_sz!=sizeof(int))return TH_EINVAL; + pp_level=*(int *)_buf; + if(pp_level<0||pp_level>OC_PP_LEVEL_MAX)return TH_EINVAL; + _dec->pp_level=pp_level; + return 0; + }break; + case TH_DECCTL_SET_GRANPOS:{ + ogg_int64_t granpos; + if(_dec==NULL||_buf==NULL)return TH_EFAULT; + if(_buf_sz!=sizeof(ogg_int64_t))return TH_EINVAL; + granpos=*(ogg_int64_t *)_buf; + if(granpos<0)return TH_EINVAL; + _dec->state.granpos=granpos; + _dec->state.keyframe_num= + granpos>>_dec->state.info.keyframe_granule_shift; + _dec->state.curframe_num=_dec->state.keyframe_num+ + (granpos&(1<<_dec->state.info.keyframe_granule_shift)-1); + return 0; + }break; + case TH_DECCTL_SET_STRIPE_CB:{ + th_stripe_callback *cb; + if(_dec==NULL||_buf==NULL)return TH_EFAULT; + if(_buf_sz!=sizeof(th_stripe_callback))return TH_EINVAL; + cb=(th_stripe_callback *)_buf; + _dec->stripe_cb.ctx=cb->ctx; + _dec->stripe_cb.stripe_decoded=cb->stripe_decoded; + return 0; + }break; + default:return TH_EIMPL; + } +} + +int th_decode_packetin(th_dec_ctx *_dec,const ogg_packet *_op, + ogg_int64_t *_granpos){ + int ret; + if(_dec==NULL||_op==NULL)return TH_EFAULT; + /*A completely empty packet indicates a dropped frame and is treated exactly + like an inter frame with no coded blocks. + Only proceed if we have a non-empty packet.*/ + if(_op->bytes!=0){ + oc_dec_pipeline_state pipe; + th_ycbcr_buffer stripe_buf; + int stripe_fragy; + int refi; + int pli; + int notstart; + int notdone; + theorapackB_readinit(&_dec->opb,_op->packet,_op->bytes); + ret=oc_dec_frame_header_unpack(_dec); + if(ret<0)return ret; + /*Select a free buffer to use for the reconstructed version of this + frame.*/ + if(_dec->state.frame_type!=OC_INTRA_FRAME&& + (_dec->state.ref_frame_idx[OC_FRAME_GOLD]<0|| + _dec->state.ref_frame_idx[OC_FRAME_PREV]<0)){ + th_info *info; + size_t yplane_sz; + size_t cplane_sz; + int yhstride; + int yvstride; + int chstride; + int cvstride; + /*We're decoding an INTER frame, but have no initialized reference + buffers (i.e., decoding did not start on a key frame). + We initialize them to a solid gray here.*/ + _dec->state.ref_frame_idx[OC_FRAME_GOLD]=0; + _dec->state.ref_frame_idx[OC_FRAME_PREV]=0; + _dec->state.ref_frame_idx[OC_FRAME_SELF]=refi=1; + info=&_dec->state.info; + yhstride=info->frame_width+2*OC_UMV_PADDING; + yvstride=info->frame_height+2*OC_UMV_PADDING; + chstride=yhstride>>!(info->pixel_fmt&1); + cvstride=yvstride>>!(info->pixel_fmt&2); + yplane_sz=(size_t)yhstride*yvstride; + cplane_sz=(size_t)chstride*cvstride; + memset(_dec->state.ref_frame_data,0x80,yplane_sz+2*cplane_sz); + } + else{ + for(refi=0;refi==_dec->state.ref_frame_idx[OC_FRAME_GOLD]|| + refi==_dec->state.ref_frame_idx[OC_FRAME_PREV];refi++); + _dec->state.ref_frame_idx[OC_FRAME_SELF]=refi; + } + if(_dec->state.frame_type==OC_INTRA_FRAME){ + oc_dec_mark_all_intra(_dec); + _dec->state.keyframe_num=_dec->state.curframe_num; + }else{ + oc_dec_coded_flags_unpack(_dec); + oc_dec_mb_modes_unpack(_dec); + oc_dec_mv_unpack_and_frag_modes_fill(_dec); + } + oc_dec_block_qis_unpack(_dec); + oc_dec_residual_tokens_unpack(_dec); + /*Update granule position. + This must be done before the striped decode callbacks so that the + application knows what to do with the frame data.*/ + _dec->state.granpos= + (_dec->state.keyframe_num<<_dec->state.info.keyframe_granule_shift)+ + (_dec->state.curframe_num-_dec->state.keyframe_num); + _dec->state.curframe_num++; + if(_granpos!=NULL)*_granpos=_dec->state.granpos; + /*All of the rest of the operations -- DC prediction reversal, + reconstructing coded fragments, copying uncoded fragments, loop + filtering, extending borders, and out-of-loop post-processing -- should + be pipelined. + I.e., DC prediction reversal, reconstruction, and uncoded fragment + copying are done for one or two super block rows, then loop filtering is + run as far as it can, then bordering copying, then post-processing. + For 4:2:0 video a Minimum Codable Unit or MCU contains two luma super + block rows, and one chroma. + Otherwise, an MCU consists of one super block row from each plane. + Inside each MCU, we perform all of the steps on one color plane before + moving on to the next. + After reconstruction, the additional filtering stages introduce a delay + since they need some pixels from the next fragment row. + Thus the actual number of decoded rows available is slightly smaller for + the first MCU, and slightly larger for the last. + + This entire process allows us to operate on the data while it is still in + cache, resulting in big performance improvements. + An application callback allows further application processing (blitting + to video memory, color conversion, etc.) to also use the data while it's + in cache.*/ + oc_dec_pipeline_init(_dec,&pipe); + oc_ycbcr_buffer_flip(stripe_buf,_dec->pp_frame_buf); + notstart=0; + notdone=1; + for(stripe_fragy=notstart=0;notdone;stripe_fragy+=pipe.mcu_nvfrags){ + int avail_fragy0; + int avail_fragy_end; + avail_fragy0=avail_fragy_end=_dec->state.fplanes[0].nvfrags; + notdone=stripe_fragy+pipe.mcu_nvfragsstate.fplanes+pli; + /*Compute the first and last fragment row of the current MCU for this + plane.*/ + frag_shift=pli!=0&&!(_dec->state.info.pixel_fmt&2); + pipe.fragy0[pli]=stripe_fragy>>frag_shift; + pipe.fragy_end[pli]=OC_MINI(fplane->nvfrags, + pipe.fragy0[pli]+(pipe.mcu_nvfrags>>frag_shift)); + oc_dec_dc_unpredict_mcu_plane(_dec,&pipe,pli); + oc_dec_frags_recon_mcu_plane(_dec,&pipe,pli); + sdelay=edelay=0; + if(pipe.loop_filter){ + sdelay+=notstart; + edelay+=notdone; + oc_state_loop_filter_frag_rows(&_dec->state,pipe.bounding_values, + refi,pli,pipe.fragy0[pli]-sdelay,pipe.fragy_end[pli]-edelay); + } + /*To fill the borders, we have an additional two pixel delay, since a + fragment in the next row could filter its top edge, using two pixels + from a fragment in this row. + But there's no reason to delay a full fragment between the two.*/ + oc_state_borders_fill_rows(&_dec->state,refi,pli, + (pipe.fragy0[pli]-sdelay<<3)-(sdelay<<1), + (pipe.fragy_end[pli]-edelay<<3)-(edelay<<1)); + /*Out-of-loop post-processing.*/ + pp_offset=3*(pli!=0); + if(pipe.pp_level>=OC_PP_LEVEL_DEBLOCKY+pp_offset){ + /*Perform de-blocking in one plane.*/ + sdelay+=notstart; + edelay+=notdone; + oc_dec_deblock_frag_rows(_dec,_dec->pp_frame_buf, + _dec->state.ref_frame_bufs[refi],pli, + pipe.fragy0[pli]-sdelay,pipe.fragy_end[pli]-edelay); + if(pipe.pp_level>=OC_PP_LEVEL_DERINGY+pp_offset){ + /*Perform de-ringing in one plane.*/ + sdelay+=notstart; + edelay+=notdone; + oc_dec_dering_frag_rows(_dec,_dec->pp_frame_buf,pli, + pipe.fragy0[pli]-sdelay,pipe.fragy_end[pli]-edelay); + } + } + /*If no post-processing is done, we still need to delay a row for the + loop filter, thanks to the strange filtering order VP3 chose.*/ + else if(pipe.loop_filter){ + sdelay+=notstart; + edelay+=notdone; + } + /*Compute the intersection of the available rows in all planes. + If chroma is sub-sampled, the effect of each of its delays is + doubled, but luma might have more post-processing filters enabled + than chroma, so we don't know up front which one is the limiting + factor.*/ + avail_fragy0=OC_MINI(avail_fragy0,pipe.fragy0[pli]-sdelay<stripe_cb.stripe_decoded!=NULL){ + /*Make the callback, ensuring we flip the sense of the "start" and + "end" of the available region upside down.*/ + (*_dec->stripe_cb.stripe_decoded)(_dec->stripe_cb.ctx,stripe_buf, + _dec->state.fplanes[0].nvfrags-avail_fragy_end, + _dec->state.fplanes[0].nvfrags-avail_fragy0); + } + notstart=1; + } + /*Finish filling in the reference frame borders.*/ + for(pli=0;pli<3;pli++)oc_state_borders_fill_caps(&_dec->state,refi,pli); + /*Update the reference frame indices.*/ + if(_dec->state.frame_type==OC_INTRA_FRAME){ + /*The new frame becomes both the previous and gold reference frames.*/ + _dec->state.ref_frame_idx[OC_FRAME_GOLD]= + _dec->state.ref_frame_idx[OC_FRAME_PREV]= + _dec->state.ref_frame_idx[OC_FRAME_SELF]; + } + else{ + /*Otherwise, just replace the previous reference frame.*/ + _dec->state.ref_frame_idx[OC_FRAME_PREV]= + _dec->state.ref_frame_idx[OC_FRAME_SELF]; + } +#if defined(OC_DUMP_IMAGES) + /*Don't dump images for dropped frames.*/ + oc_state_dump_frame(&_dec->state,OC_FRAME_SELF,"dec"); +#endif + return 0; + } + else{ + /*Just update the granule position and return.*/ + _dec->state.granpos= + (_dec->state.keyframe_num<<_dec->state.info.keyframe_granule_shift)+ + (_dec->state.curframe_num-_dec->state.keyframe_num); + _dec->state.curframe_num++; + if(_granpos!=NULL)*_granpos=_dec->state.granpos; + return TH_DUPFRAME; + } +} + +int th_decode_ycbcr_out(th_dec_ctx *_dec,th_ycbcr_buffer _ycbcr){ + oc_ycbcr_buffer_flip(_ycbcr,_dec->pp_frame_buf); + return 0; +} diff --git a/Engine/lib/libtheora/lib/dec/dequant.c b/Engine/lib/libtheora/lib/dec/dequant.c new file mode 100644 index 000000000..50bba85b8 --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/dequant.c @@ -0,0 +1,170 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: dequant.c 15400 2008-10-15 12:10:58Z tterribe $ + + ********************************************************************/ + +#include +#include +#include +#include "dequant.h" +#include "decint.h" + +int oc_quant_params_unpack(oggpack_buffer *_opb, + th_quant_info *_qinfo){ + th_quant_base *base_mats; + long val; + int nbase_mats; + int sizes[64]; + int indices[64]; + int nbits; + int bmi; + int ci; + int qti; + int pli; + int qri; + int qi; + int i; + theorapackB_read(_opb,3,&val); + nbits=(int)val; + for(qi=0;qi<64;qi++){ + theorapackB_read(_opb,nbits,&val); + _qinfo->loop_filter_limits[qi]=(unsigned char)val; + } + theorapackB_read(_opb,4,&val); + nbits=(int)val+1; + for(qi=0;qi<64;qi++){ + theorapackB_read(_opb,nbits,&val); + _qinfo->ac_scale[qi]=(ogg_uint16_t)val; + } + theorapackB_read(_opb,4,&val); + nbits=(int)val+1; + for(qi=0;qi<64;qi++){ + theorapackB_read(_opb,nbits,&val); + _qinfo->dc_scale[qi]=(ogg_uint16_t)val; + } + theorapackB_read(_opb,9,&val); + nbase_mats=(int)val+1; + base_mats=_ogg_malloc(nbase_mats*sizeof(base_mats[0])); + for(bmi=0;bmiqi_ranges[qti]+pli; + if(i>0){ + theorapackB_read1(_opb,&val); + if(!val){ + int qtj; + int plj; + if(qti>0){ + theorapackB_read1(_opb,&val); + if(val){ + qtj=qti-1; + plj=pli; + } + else{ + qtj=(i-1)/3; + plj=(i-1)%3; + } + } + else{ + qtj=(i-1)/3; + plj=(i-1)%3; + } + *qranges=*(_qinfo->qi_ranges[qtj]+plj); + continue; + } + } + theorapackB_read(_opb,nbits,&val); + indices[0]=(int)val; + for(qi=qri=0;qi<63;){ + theorapackB_read(_opb,oc_ilog(62-qi),&val); + sizes[qri]=(int)val+1; + qi+=(int)val+1; + theorapackB_read(_opb,nbits,&val); + indices[++qri]=(int)val; + } + /*Note: The caller is responsible for cleaning up any partially + constructed qinfo.*/ + if(qi>63){ + _ogg_free(base_mats); + return TH_EBADHEADER; + } + qranges->nranges=qri; + qranges->sizes=qrsizes=(int *)_ogg_malloc(qri*sizeof(qrsizes[0])); + memcpy(qrsizes,sizes,qri*sizeof(qrsizes[0])); + qrbms=(th_quant_base *)_ogg_malloc((qri+1)*sizeof(qrbms[0])); + qranges->base_matrices=(const th_quant_base *)qrbms; + do{ + bmi=indices[qri]; + /*Note: The caller is responsible for cleaning up any partially + constructed qinfo.*/ + if(bmi>=nbase_mats){ + _ogg_free(base_mats); + return TH_EBADHEADER; + } + memcpy(qrbms[qri],base_mats[bmi],sizeof(qrbms[qri])); + } + while(qri-->0); + } + _ogg_free(base_mats); + return 0; +} + +void oc_quant_params_clear(th_quant_info *_qinfo){ + int i; + for(i=6;i-->0;){ + int qti; + int pli; + qti=i/3; + pli=i%3; + /*Clear any duplicate pointer references.*/ + if(i>0){ + int qtj; + int plj; + qtj=(i-1)/3; + plj=(i-1)%3; + if(_qinfo->qi_ranges[qti][pli].sizes== + _qinfo->qi_ranges[qtj][plj].sizes){ + _qinfo->qi_ranges[qti][pli].sizes=NULL; + } + if(_qinfo->qi_ranges[qti][pli].base_matrices== + _qinfo->qi_ranges[qtj][plj].base_matrices){ + _qinfo->qi_ranges[qti][pli].base_matrices=NULL; + } + } + if(qti>0){ + if(_qinfo->qi_ranges[1][pli].sizes== + _qinfo->qi_ranges[0][pli].sizes){ + _qinfo->qi_ranges[1][pli].sizes=NULL; + } + if(_qinfo->qi_ranges[1][pli].base_matrices== + _qinfo->qi_ranges[0][pli].base_matrices){ + _qinfo->qi_ranges[1][pli].base_matrices=NULL; + } + } + /*Now free all the non-duplicate storage.*/ + _ogg_free((void *)_qinfo->qi_ranges[qti][pli].sizes); + _ogg_free((void *)_qinfo->qi_ranges[qti][pli].base_matrices); + } +} diff --git a/Engine/lib/libtheora/lib/dec/dequant.h b/Engine/lib/libtheora/lib/dec/dequant.h new file mode 100644 index 000000000..928b509e5 --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/dequant.h @@ -0,0 +1,26 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: dequant.h 15400 2008-10-15 12:10:58Z tterribe $ + + ********************************************************************/ + +#if !defined(_dequant_H) +# define _dequant_H (1) +# include "quant.h" + +int oc_quant_params_unpack(oggpack_buffer *_opb, + th_quant_info *_qinfo); +void oc_quant_params_clear(th_quant_info *_qinfo); + +#endif diff --git a/Engine/lib/libtheora/lib/dec/fragment.c b/Engine/lib/libtheora/lib/dec/fragment.c new file mode 100644 index 000000000..77f1c7f6b --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/fragment.c @@ -0,0 +1,199 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: fragment.c 15469 2008-10-30 12:49:42Z tterribe $ + + ********************************************************************/ + +#include "../internal.h" + +void oc_frag_recon_intra(const oc_theora_state *_state,unsigned char *_dst, + int _dst_ystride,const ogg_int16_t *_residue){ + _state->opt_vtable.frag_recon_intra(_dst,_dst_ystride,_residue); +} + +void oc_frag_recon_intra_c(unsigned char *_dst,int _dst_ystride, + const ogg_int16_t *_residue){ + int i; + for(i=0;i<8;i++){ + int j; + for(j=0;j<8;j++){ + int res; + res=*_residue++; + _dst[j]=OC_CLAMP255(res+128); + } + _dst+=_dst_ystride; + } +} + +void oc_frag_recon_inter(const oc_theora_state *_state,unsigned char *_dst, + int _dst_ystride,const unsigned char *_src,int _src_ystride, + const ogg_int16_t *_residue){ + _state->opt_vtable.frag_recon_inter(_dst,_dst_ystride,_src,_src_ystride, + _residue); +} + +void oc_frag_recon_inter_c(unsigned char *_dst,int _dst_ystride, + const unsigned char *_src,int _src_ystride,const ogg_int16_t *_residue){ + int i; + for(i=0;i<8;i++){ + int j; + for(j=0;j<8;j++){ + int res; + res=*_residue++; + _dst[j]=OC_CLAMP255(res+_src[j]); + } + _dst+=_dst_ystride; + _src+=_src_ystride; + } +} + +void oc_frag_recon_inter2(const oc_theora_state *_state,unsigned char *_dst, + int _dst_ystride,const unsigned char *_src1,int _src1_ystride, + const unsigned char *_src2,int _src2_ystride,const ogg_int16_t *_residue){ + _state->opt_vtable.frag_recon_inter2(_dst,_dst_ystride,_src1,_src1_ystride, + _src2,_src2_ystride,_residue); +} + +void oc_frag_recon_inter2_c(unsigned char *_dst,int _dst_ystride, + const unsigned char *_src1,int _src1_ystride,const unsigned char *_src2, + int _src2_ystride,const ogg_int16_t *_residue){ + int i; + for(i=0;i<8;i++){ + int j; + for(j=0;j<8;j++){ + int res; + res=*_residue++; + _dst[j]=OC_CLAMP255(res+((int)_src1[j]+_src2[j]>>1)); + } + _dst+=_dst_ystride; + _src1+=_src1_ystride; + _src2+=_src2_ystride; + } +} + +/*Computes the predicted DC value for the given fragment. + This requires that the fully decoded DC values be available for the left, + upper-left, upper, and upper-right fragments (if they exist). + _frag: The fragment to predict the DC value for. + _fplane: The fragment plane the fragment belongs to. + _x: The x-coordinate of the fragment. + _y: The y-coordinate of the fragment. + _pred_last: The last fully-decoded DC value for each predictor frame + (OC_FRAME_GOLD, OC_FRAME_PREV and OC_FRAME_SELF). + This should be initialized to 0's for the first fragment in each + color plane. + Return: The predicted DC value for this fragment.*/ +int oc_frag_pred_dc(const oc_fragment *_frag, + const oc_fragment_plane *_fplane,int _x,int _y,int _pred_last[3]){ + static const int PRED_SCALE[16][4]={ + /*0*/ + {0,0,0,0}, + /*OC_PL*/ + {1,0,0,0}, + /*OC_PUL*/ + {1,0,0,0}, + /*OC_PL|OC_PUL*/ + {1,0,0,0}, + /*OC_PU*/ + {1,0,0,0}, + /*OC_PL|OC_PU*/ + {1,1,0,0}, + /*OC_PUL|OC_PU*/ + {0,1,0,0}, + /*OC_PL|OC_PUL|PC_PU*/ + {29,-26,29,0}, + /*OC_PUR*/ + {1,0,0,0}, + /*OC_PL|OC_PUR*/ + {75,53,0,0}, + /*OC_PUL|OC_PUR*/ + {1,1,0,0}, + /*OC_PL|OC_PUL|OC_PUR*/ + {75,0,53,0}, + /*OC_PU|OC_PUR*/ + {1,0,0,0}, + /*OC_PL|OC_PU|OC_PUR*/ + {75,0,53,0}, + /*OC_PUL|OC_PU|OC_PUR*/ + {3,10,3,0}, + /*OC_PL|OC_PUL|OC_PU|OC_PUR*/ + {29,-26,29,0} + }; + static const int PRED_SHIFT[16]={0,0,0,0,0,1,0,5,0,7,1,7,0,7,4,5}; + static const int PRED_RMASK[16]={0,0,0,0,0,1,0,31,0,127,1,127,0,127,15,31}; + static const int BC_MASK[8]={ + /*No boundary condition.*/ + OC_PL|OC_PUL|OC_PU|OC_PUR, + /*Left column.*/ + OC_PU|OC_PUR, + /*Top row.*/ + OC_PL, + /*Top row, left column.*/ + 0, + /*Right column.*/ + OC_PL|OC_PUL|OC_PU, + /*Right and left column.*/ + OC_PU, + /*Top row, right column.*/ + OC_PL, + /*Top row, right and left column.*/ + 0 + }; + /*Predictor fragments, left, up-left, up, up-right.*/ + const oc_fragment *predfr[4]; + /*The frame used for prediction for this fragment.*/ + int pred_frame; + /*The boundary condition flags.*/ + int bc; + /*DC predictor values: left, up-left, up, up-right, missing values + skipped.*/ + int p[4]; + /*Predictor count.*/ + int np; + /*Which predictor constants to use.*/ + int pflags; + /*The predicted DC value.*/ + int ret; + int i; + pred_frame=OC_FRAME_FOR_MODE[_frag->mbmode]; + bc=(_x==0)+((_y==0)<<1)+((_x+1==_fplane->nhfrags)<<2); + predfr[0]=_frag-1; + predfr[1]=_frag-_fplane->nhfrags-1; + predfr[2]=predfr[1]+1; + predfr[3]=predfr[2]+1; + np=0; + pflags=0; + for(i=0;i<4;i++){ + int pflag; + pflag=1<coded&& + OC_FRAME_FOR_MODE[predfr[i]->mbmode]==pred_frame){ + p[np++]=predfr[i]->dc; + pflags|=pflag; + } + } + if(pflags==0)return _pred_last[pred_frame]; + else{ + ret=PRED_SCALE[pflags][0]*p[0]; + /*LOOP VECTORIZES.*/ + for(i=1;i128)ret=p[2]; + else if(abs(ret-p[0])>128)ret=p[0]; + else if(abs(ret-p[1])>128)ret=p[1]; + } + return ret; +} diff --git a/Engine/lib/libtheora/lib/dec/huffdec.c b/Engine/lib/libtheora/lib/dec/huffdec.c new file mode 100644 index 000000000..86c52b62f --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/huffdec.c @@ -0,0 +1,325 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: huffdec.c 15431 2008-10-21 05:04:02Z giles $ + + ********************************************************************/ + +#include +#include +#include "huffdec.h" +#include "decint.h" + + +/*The ANSI offsetof macro is broken on some platforms (e.g., older DECs).*/ +#define _ogg_offsetof(_type,_field)\ + ((size_t)((char *)&((_type *)0)->_field-(char *)0)) + +/*These two functions are really part of the bitpack.c module, but + they are only used here. Declaring local static versions so they + can be inlined saves considerable function call overhead.*/ + +/*Read in bits without advancing the bitptr. + Here we assume 0<=_bits&&_bits<=32.*/ +static int theorapackB_look(oggpack_buffer *_b,int _bits,long *_ret){ + long ret; + long m; + long d; + m=32-_bits; + _bits+=_b->endbit; + d=_b->storage-_b->endbyte; + if(d<=4){ + /*Not the main path.*/ + if(d<=0){ + *_ret=0L; + return -(_bits>d*8); + } + /*If we have some bits left, but not enough, return the ones we have.*/ + if(d*8<_bits)_bits=d*8; + } + ret=_b->ptr[0]<<24+_b->endbit; + if(_bits>8){ + ret|=_b->ptr[1]<<16+_b->endbit; + if(_bits>16){ + ret|=_b->ptr[2]<<8+_b->endbit; + if(_bits>24){ + ret|=_b->ptr[3]<<_b->endbit; + if(_bits>32)ret|=_b->ptr[4]>>8-_b->endbit; + } + } + } + *_ret=((ret&0xFFFFFFFF)>>(m>>1))>>(m+1>>1); + return 0; +} + +/*advance the bitptr*/ +static void theorapackB_adv(oggpack_buffer *_b,int _bits){ + _bits+=_b->endbit; + _b->ptr+=_bits>>3; + _b->endbyte+=_bits>>3; + _b->endbit=_bits&7; +} + + +/*The log_2 of the size of a lookup table is allowed to grow to relative to + the number of unique nodes it contains. + E.g., if OC_HUFF_SLUSH is 2, then at most 75% of the space in the tree is + wasted (each node will have an amortized cost of at most 20 bytes when using + 4-byte pointers). + Larger numbers can decode tokens with fewer read operations, while smaller + numbers may save more space (requiring as little as 8 bytes amortized per + node, though there will be more nodes). + With a sample file: + 32233473 read calls are required when no tree collapsing is done (100.0%). + 19269269 read calls are required when OC_HUFF_SLUSH is 0 (59.8%). + 11144969 read calls are required when OC_HUFF_SLUSH is 1 (34.6%). + 10538563 read calls are required when OC_HUFF_SLUSH is 2 (32.7%). + 10192578 read calls are required when OC_HUFF_SLUSH is 3 (31.6%). + Since a value of 1 gets us the vast majority of the speed-up with only a + small amount of wasted memory, this is what we use.*/ +#define OC_HUFF_SLUSH (1) + + +/*Allocates a Huffman tree node that represents a subtree of depth _nbits. + _nbits: The depth of the subtree. + If this is 0, the node is a leaf node. + Otherwise 1<<_nbits pointers are allocated for children. + Return: The newly allocated and fully initialized Huffman tree node.*/ +static oc_huff_node *oc_huff_node_alloc(int _nbits){ + oc_huff_node *ret; + size_t size; + size=_ogg_offsetof(oc_huff_node,nodes); + if(_nbits>0)size+=sizeof(oc_huff_node *)*(1<<_nbits); + ret=_ogg_calloc(1,size); + ret->nbits=(unsigned char)_nbits; + return ret; +} + +/*Frees a Huffman tree node allocated with oc_huf_node_alloc. + _node: The node to free. + This may be NULL.*/ +static void oc_huff_node_free(oc_huff_node *_node){ + _ogg_free(_node); +} + +/*Frees the memory used by a Huffman tree. + _node: The Huffman tree to free. + This may be NULL.*/ +static void oc_huff_tree_free(oc_huff_node *_node){ + if(_node==NULL)return; + if(_node->nbits){ + int nchildren; + int i; + int inext; + nchildren=1<<_node->nbits; + for(i=0;inodes[i]!=NULL?1<<_node->nbits-_node->nodes[i]->depth:1); + oc_huff_tree_free(_node->nodes[i]); + } + } + oc_huff_node_free(_node); +} + +/*Unpacks a sub-tree from the given buffer. + _opb: The buffer to unpack from. + _binode: The location to store a pointer to the sub-tree in. + _depth: The current depth of the tree. + This is used to prevent infinite recursion. + Return: 0 on success, or a negative value on error.*/ +static int oc_huff_tree_unpack(oggpack_buffer *_opb, + oc_huff_node **_binode,int _depth){ + oc_huff_node *binode; + long bits; + /*Prevent infinite recursion.*/ + if(++_depth>32)return TH_EBADHEADER; + if(theorapackB_read1(_opb,&bits)<0)return TH_EBADHEADER; + /*Read an internal node:*/ + if(!bits){ + int ret; + binode=oc_huff_node_alloc(1); + binode->depth=(unsigned char)(_depth>1); + ret=oc_huff_tree_unpack(_opb,binode->nodes,_depth); + if(ret>=0)ret=oc_huff_tree_unpack(_opb,binode->nodes+1,_depth); + if(ret<0){ + oc_huff_tree_free(binode); + *_binode=NULL; + return ret; + } + } + /*Read a leaf node:*/ + else{ + if(theorapackB_read(_opb,OC_NDCT_TOKEN_BITS,&bits)<0)return TH_EBADHEADER; + binode=oc_huff_node_alloc(0); + binode->depth=(unsigned char)(_depth>1); + binode->token=(unsigned char)bits; + } + *_binode=binode; + return 0; +} + +/*Finds the depth of shortest branch of the given sub-tree. + The tree must be binary. + _binode: The root of the given sub-tree. + _binode->nbits must be 0 or 1. + Return: The smallest depth of a leaf node in this sub-tree. + 0 indicates this sub-tree is a leaf node.*/ +static int oc_huff_tree_mindepth(oc_huff_node *_binode){ + int depth0; + int depth1; + if(_binode->nbits==0)return 0; + depth0=oc_huff_tree_mindepth(_binode->nodes[0]); + depth1=oc_huff_tree_mindepth(_binode->nodes[1]); + return OC_MINI(depth0,depth1)+1; +} + +/*Finds the number of internal nodes at a given depth, plus the number of + leaves at that depth or shallower. + The tree must be binary. + _binode: The root of the given sub-tree. + _binode->nbits must be 0 or 1. + Return: The number of entries that would be contained in a jump table of the + given depth.*/ +static int oc_huff_tree_occupancy(oc_huff_node *_binode,int _depth){ + if(_binode->nbits==0||_depth<=0)return 1; + else{ + return oc_huff_tree_occupancy(_binode->nodes[0],_depth-1)+ + oc_huff_tree_occupancy(_binode->nodes[1],_depth-1); + } +} + +static oc_huff_node *oc_huff_tree_collapse(oc_huff_node *_binode); + +/*Fills the given nodes table with all the children in the sub-tree at the + given depth. + The nodes in the sub-tree with a depth less than that stored in the table + are freed. + The sub-tree must be binary and complete up until the given depth. + _nodes: The nodes table to fill. + _binode: The root of the sub-tree to fill it with. + _binode->nbits must be 0 or 1. + _level: The current level in the table. + 0 indicates that the current node should be stored, regardless of + whether it is a leaf node or an internal node. + _depth: The depth of the nodes to fill the table with, relative to their + parent.*/ +static void oc_huff_node_fill(oc_huff_node **_nodes, + oc_huff_node *_binode,int _level,int _depth){ + if(_level<=0||_binode->nbits==0){ + int i; + _binode->depth=(unsigned char)(_depth-_level); + _nodes[0]=oc_huff_tree_collapse(_binode); + for(i=1;i<1<<_level;i++)_nodes[i]=_nodes[0]; + } + else{ + _level--; + oc_huff_node_fill(_nodes,_binode->nodes[0],_level,_depth); + oc_huff_node_fill(_nodes+(1<<_level),_binode->nodes[1],_level,_depth); + oc_huff_node_free(_binode); + } +} + +/*Finds the largest complete sub-tree rooted at the current node and collapses + it into a single node. + This procedure is then applied recursively to all the children of that node. + _binode: The root of the sub-tree to collapse. + _binode->nbits must be 0 or 1. + Return: The new root of the collapsed sub-tree.*/ +static oc_huff_node *oc_huff_tree_collapse(oc_huff_node *_binode){ + oc_huff_node *root; + int mindepth; + int depth; + int loccupancy; + int occupancy; + depth=mindepth=oc_huff_tree_mindepth(_binode); + occupancy=1<loccupancy&&occupancy>=1<depth=_binode->depth; + oc_huff_node_fill(root->nodes,_binode,depth,depth); + return root; +} + +/*Makes a copy of the given Huffman tree. + _node: The Huffman tree to copy. + Return: The copy of the Huffman tree.*/ +static oc_huff_node *oc_huff_tree_copy(const oc_huff_node *_node){ + oc_huff_node *ret; + ret=oc_huff_node_alloc(_node->nbits); + ret->depth=_node->depth; + if(_node->nbits){ + int nchildren; + int i; + int inext; + nchildren=1<<_node->nbits; + for(i=0;inodes[i]=oc_huff_tree_copy(_node->nodes[i]); + inext=i+(1<<_node->nbits-ret->nodes[i]->depth); + while(++inodes[i]=ret->nodes[i-1]; + } + } + else ret->token=_node->token; + return ret; +} + +/*Unpacks a set of Huffman trees, and reduces them to a collapsed + representation. + _opb: The buffer to unpack the trees from. + _nodes: The table to fill with the Huffman trees. + Return: 0 on success, or a negative value on error.*/ +int oc_huff_trees_unpack(oggpack_buffer *_opb, + oc_huff_node *_nodes[TH_NHUFFMAN_TABLES]){ + int i; + for(i=0;inbits!=0){ + theorapackB_look(_opb,_node->nbits,&bits); + _node=_node->nodes[bits]; + theorapackB_adv(_opb,_node->depth); + } + return _node->token; +} diff --git a/Engine/lib/libtheora/lib/dec/huffdec.h b/Engine/lib/libtheora/lib/dec/huffdec.h new file mode 100644 index 000000000..cc87b4092 --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/huffdec.h @@ -0,0 +1,91 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: huffdec.h 15400 2008-10-15 12:10:58Z tterribe $ + + ********************************************************************/ + +#if !defined(_huffdec_H) +# define _huffdec_H (1) +# include "huffman.h" + + + +typedef struct oc_huff_node oc_huff_node; + +/*A node in the Huffman tree. + Instead of storing every branching in the tree, subtrees can be collapsed + into one node, with a table of size 1< +#include +#include "dct.h" +#include "idct.h" + +/*Performs an inverse 8 point Type-II DCT transform. + The output is scaled by a factor of 2 relative to the orthonormal version of + the transform. + _y: The buffer to store the result in. + Data will be placed in every 8th entry (e.g., in a column of an 8x8 + block). + _x: The input coefficients. + The first 8 entries are used (e.g., from a row of an 8x8 block).*/ +static void idct8(ogg_int16_t *_y,const ogg_int16_t _x[8]){ + ogg_int32_t t[8]; + ogg_int32_t r; + /*Stage 1:*/ + /*0-1 butterfly.*/ + t[0]=OC_C4S4*(ogg_int16_t)(_x[0]+_x[4])>>16; + t[1]=OC_C4S4*(ogg_int16_t)(_x[0]-_x[4])>>16; + /*2-3 rotation by 6pi/16.*/ + t[2]=(OC_C6S2*_x[2]>>16)-(OC_C2S6*_x[6]>>16); + t[3]=(OC_C2S6*_x[2]>>16)+(OC_C6S2*_x[6]>>16); + /*4-7 rotation by 7pi/16.*/ + t[4]=(OC_C7S1*_x[1]>>16)-(OC_C1S7*_x[7]>>16); + /*5-6 rotation by 3pi/16.*/ + t[5]=(OC_C3S5*_x[5]>>16)-(OC_C5S3*_x[3]>>16); + t[6]=(OC_C5S3*_x[5]>>16)+(OC_C3S5*_x[3]>>16); + t[7]=(OC_C1S7*_x[1]>>16)+(OC_C7S1*_x[7]>>16); + /*Stage 2:*/ + /*4-5 butterfly.*/ + r=t[4]+t[5]; + t[5]=OC_C4S4*(ogg_int16_t)(t[4]-t[5])>>16; + t[4]=r; + /*7-6 butterfly.*/ + r=t[7]+t[6]; + t[6]=OC_C4S4*(ogg_int16_t)(t[7]-t[6])>>16; + t[7]=r; + /*Stage 3:*/ + /*0-3 butterfly.*/ + r=t[0]+t[3]; + t[3]=t[0]-t[3]; + t[0]=r; + /*1-2 butterfly.*/ + r=t[1]+t[2]; + t[2]=t[1]-t[2]; + t[1]=r; + /*6-5 butterfly.*/ + r=t[6]+t[5]; + t[5]=t[6]-t[5]; + t[6]=r; + /*Stage 4:*/ + /*0-7 butterfly.*/ + _y[0<<3]=(ogg_int16_t)(t[0]+t[7]); + /*1-6 butterfly.*/ + _y[1<<3]=(ogg_int16_t)(t[1]+t[6]); + /*2-5 butterfly.*/ + _y[2<<3]=(ogg_int16_t)(t[2]+t[5]); + /*3-4 butterfly.*/ + _y[3<<3]=(ogg_int16_t)(t[3]+t[4]); + _y[4<<3]=(ogg_int16_t)(t[3]-t[4]); + _y[5<<3]=(ogg_int16_t)(t[2]-t[5]); + _y[6<<3]=(ogg_int16_t)(t[1]-t[6]); + _y[7<<3]=(ogg_int16_t)(t[0]-t[7]); +} + +/*Performs an inverse 8 point Type-II DCT transform. + The output is scaled by a factor of 2 relative to the orthonormal version of + the transform. + _y: The buffer to store the result in. + Data will be placed in every 8th entry (e.g., in a column of an 8x8 + block). + _x: The input coefficients. + Only the first 4 entries are used. + The other 4 are assumed to be 0.*/ +static void idct8_4(ogg_int16_t *_y,const ogg_int16_t _x[8]){ + ogg_int32_t t[8]; + ogg_int32_t r; + /*Stage 1:*/ + t[0]=OC_C4S4*_x[0]>>16; + t[2]=OC_C6S2*_x[2]>>16; + t[3]=OC_C2S6*_x[2]>>16; + t[4]=OC_C7S1*_x[1]>>16; + t[5]=-(OC_C5S3*_x[3]>>16); + t[6]=OC_C3S5*_x[3]>>16; + t[7]=OC_C1S7*_x[1]>>16; + /*Stage 2:*/ + r=t[4]+t[5]; + t[5]=OC_C4S4*(ogg_int16_t)(t[4]-t[5])>>16; + t[4]=r; + r=t[7]+t[6]; + t[6]=OC_C4S4*(ogg_int16_t)(t[7]-t[6])>>16; + t[7]=r; + /*Stage 3:*/ + t[1]=t[0]+t[2]; + t[2]=t[0]-t[2]; + r=t[0]+t[3]; + t[3]=t[0]-t[3]; + t[0]=r; + r=t[6]+t[5]; + t[5]=t[6]-t[5]; + t[6]=r; + /*Stage 4:*/ + _y[0<<3]=(ogg_int16_t)(t[0]+t[7]); + _y[1<<3]=(ogg_int16_t)(t[1]+t[6]); + _y[2<<3]=(ogg_int16_t)(t[2]+t[5]); + _y[3<<3]=(ogg_int16_t)(t[3]+t[4]); + _y[4<<3]=(ogg_int16_t)(t[3]-t[4]); + _y[5<<3]=(ogg_int16_t)(t[2]-t[5]); + _y[6<<3]=(ogg_int16_t)(t[1]-t[6]); + _y[7<<3]=(ogg_int16_t)(t[0]-t[7]); +} + +/*Performs an inverse 8 point Type-II DCT transform. + The output is scaled by a factor of 2 relative to the orthonormal version of + the transform. + _y: The buffer to store the result in. + Data will be placed in every 8th entry (e.g., in a column of an 8x8 + block). + _x: The input coefficients. + Only the first 3 entries are used. + The other 5 are assumed to be 0.*/ +static void idct8_3(ogg_int16_t *_y,const ogg_int16_t _x[8]){ + ogg_int32_t t[8]; + ogg_int32_t r; + /*Stage 1:*/ + t[0]=OC_C4S4*_x[0]>>16; + t[2]=OC_C6S2*_x[2]>>16; + t[3]=OC_C2S6*_x[2]>>16; + t[4]=OC_C7S1*_x[1]>>16; + t[7]=OC_C1S7*_x[1]>>16; + /*Stage 2:*/ + t[5]=OC_C4S4*t[4]>>16; + t[6]=OC_C4S4*t[7]>>16; + /*Stage 3:*/ + t[1]=t[0]+t[2]; + t[2]=t[0]-t[2]; + r=t[0]+t[3]; + t[3]=t[0]-t[3]; + t[0]=r; + r=t[6]+t[5]; + t[5]=t[6]-t[5]; + t[6]=r; + /*Stage 4:*/ + _y[0<<3]=(ogg_int16_t)(t[0]+t[7]); + _y[1<<3]=(ogg_int16_t)(t[1]+t[6]); + _y[2<<3]=(ogg_int16_t)(t[2]+t[5]); + _y[3<<3]=(ogg_int16_t)(t[3]+t[4]); + _y[4<<3]=(ogg_int16_t)(t[3]-t[4]); + _y[5<<3]=(ogg_int16_t)(t[2]-t[5]); + _y[6<<3]=(ogg_int16_t)(t[1]-t[6]); + _y[7<<3]=(ogg_int16_t)(t[0]-t[7]); +} + +/*Performs an inverse 8 point Type-II DCT transform. + The output is scaled by a factor of 2 relative to the orthonormal version of + the transform. + _y: The buffer to store the result in. + Data will be placed in every 8th entry (e.g., in a column of an 8x8 + block). + _x: The input coefficients. + Only the first 2 entries are used. + The other 6 are assumed to be 0.*/ +static void idct8_2(ogg_int16_t *_y,const ogg_int16_t _x[8]){ + ogg_int32_t t[8]; + ogg_int32_t r; + /*Stage 1:*/ + t[0]=OC_C4S4*_x[0]>>16; + t[4]=OC_C7S1*_x[1]>>16; + t[7]=OC_C1S7*_x[1]>>16; + /*Stage 2:*/ + t[5]=OC_C4S4*t[4]>>16; + t[6]=OC_C4S4*t[7]>>16; + /*Stage 3:*/ + r=t[6]+t[5]; + t[5]=t[6]-t[5]; + t[6]=r; + /*Stage 4:*/ + _y[0<<3]=(ogg_int16_t)(t[0]+t[7]); + _y[1<<3]=(ogg_int16_t)(t[0]+t[6]); + _y[2<<3]=(ogg_int16_t)(t[0]+t[5]); + _y[3<<3]=(ogg_int16_t)(t[0]+t[4]); + _y[4<<3]=(ogg_int16_t)(t[0]-t[4]); + _y[5<<3]=(ogg_int16_t)(t[0]-t[5]); + _y[6<<3]=(ogg_int16_t)(t[0]-t[6]); + _y[7<<3]=(ogg_int16_t)(t[0]-t[7]); +} + +/*Performs an inverse 8 point Type-II DCT transform. + The output is scaled by a factor of 2 relative to the orthonormal version of + the transform. + _y: The buffer to store the result in. + Data will be placed in every 8th entry (e.g., in a column of an 8x8 + block). + _x: The input coefficients. + Only the first entry is used. + The other 7 are assumed to be 0.*/ +static void idct8_1(ogg_int16_t *_y,const ogg_int16_t _x[1]){ + _y[0<<3]=_y[1<<3]=_y[2<<3]=_y[3<<3]= + _y[4<<3]=_y[5<<3]=_y[6<<3]=_y[7<<3]=(ogg_int16_t)(OC_C4S4*_x[0]>>16); +} + +/*Performs an inverse 8x8 Type-II DCT transform. + The input is assumed to be scaled by a factor of 4 relative to orthonormal + version of the transform. + _y: The buffer to store the result in. + This may be the same as _x. + _x: The input coefficients. */ +void oc_idct8x8_c(ogg_int16_t _y[64],const ogg_int16_t _x[64]){ + const ogg_int16_t *in; + ogg_int16_t *end; + ogg_int16_t *out; + ogg_int16_t w[64]; + /*Transform rows of x into columns of w.*/ + for(in=_x,out=w,end=out+8;out>4); +} + +/*Performs an inverse 8x8 Type-II DCT transform. + The input is assumed to be scaled by a factor of 4 relative to orthonormal + version of the transform. + All coefficients but the first 10 in zig-zag scan order are assumed to be 0: + x x x x 0 0 0 0 + x x x 0 0 0 0 0 + x x 0 0 0 0 0 0 + x 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + _y: The buffer to store the result in. + This may be the same as _x. + _x: The input coefficients. */ +void oc_idct8x8_10_c(ogg_int16_t _y[64],const ogg_int16_t _x[64]){ + const ogg_int16_t *in; + ogg_int16_t *end; + ogg_int16_t *out; + ogg_int16_t w[64]; + /*Transform rows of x into columns of w.*/ + idct8_4(w,_x); + idct8_3(w+1,_x+8); + idct8_2(w+2,_x+16); + idct8_1(w+3,_x+24); + /*Transform rows of w into columns of y.*/ + for(in=w,out=_y,end=out+8;out>4); +} diff --git a/Engine/lib/libtheora/lib/dec/idct.h b/Engine/lib/libtheora/lib/dec/idct.h new file mode 100644 index 000000000..3ee53712e --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/idct.h @@ -0,0 +1,26 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: idct.h 15400 2008-10-15 12:10:58Z tterribe $ + + ********************************************************************/ + +/*Inverse DCT transforms.*/ +#include +#if !defined(_idct_H) +# define _idct_H (1) + +void oc_idct8x8_c(ogg_int16_t _y[64],const ogg_int16_t _x[64]); +void oc_idct8x8_10_c(ogg_int16_t _y[64],const ogg_int16_t _x[64]); + +#endif diff --git a/Engine/lib/libtheora/lib/dec/info.c b/Engine/lib/libtheora/lib/dec/info.c new file mode 100644 index 000000000..26e7f42a9 --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/info.c @@ -0,0 +1,123 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: info.c 15400 2008-10-15 12:10:58Z tterribe $ + + ********************************************************************/ + +#include +#include +#include +#include "../internal.h" + + + +/*This is more or less the same as strncasecmp, but that doesn't exist + everywhere, and this is a fairly trivial function, so we include it. + Note: We take advantage of the fact that we know _n is less than or equal to + the length of at least one of the strings.*/ +static int oc_tagcompare(const char *_s1,const char *_s2,int _n){ + int c; + for(c=0;c<_n;c++){ + if(toupper(_s1[c])!=toupper(_s2[c]))return !0; + } + return _s1[c]!='='; +} + + + +void th_info_init(th_info *_info){ + memset(_info,0,sizeof(*_info)); + _info->version_major=TH_VERSION_MAJOR; + _info->version_minor=TH_VERSION_MINOR; + _info->version_subminor=TH_VERSION_SUB; + _info->keyframe_granule_shift=6; +} + +void th_info_clear(th_info *_info){ + memset(_info,0,sizeof(*_info)); +} + + + +void th_comment_init(th_comment *_tc){ + memset(_tc,0,sizeof(*_tc)); +} + +void th_comment_add(th_comment *_tc,char *_comment){ + int comment_len; + _tc->user_comments=_ogg_realloc(_tc->user_comments, + (_tc->comments+2)*sizeof(*_tc->user_comments)); + _tc->comment_lengths=_ogg_realloc(_tc->comment_lengths, + (_tc->comments+2)*sizeof(*_tc->comment_lengths)); + comment_len=strlen(_comment); + _tc->comment_lengths[_tc->comments]=comment_len; + _tc->user_comments[_tc->comments]=_ogg_malloc(comment_len+1); + memcpy(_tc->user_comments[_tc->comments],_comment,comment_len+1); + _tc->comments++; + _tc->user_comments[_tc->comments]=NULL; +} + +void th_comment_add_tag(th_comment *_tc,char *_tag,char *_val){ + char *comment; + int tag_len; + int val_len; + tag_len=strlen(_tag); + val_len=strlen(_val); + /*+2 for '=' and '\0'.*/ + comment=_ogg_malloc(tag_len+val_len+2); + memcpy(comment,_tag,tag_len); + comment[tag_len]='='; + memcpy(comment+tag_len+1,_val,val_len+1); + th_comment_add(_tc,comment); + _ogg_free(comment); +} + +char *th_comment_query(th_comment *_tc,char *_tag,int _count){ + long i; + int found; + int tag_len; + tag_len=strlen(_tag); + found=0; + for(i=0;i<_tc->comments;i++){ + if(!oc_tagcompare(_tc->user_comments[i],_tag,tag_len)){ + /*We return a pointer to the data, not a copy.*/ + if(_count==found++)return _tc->user_comments[i]+tag_len+1; + } + } + /*Didn't find anything.*/ + return NULL; +} + +int th_comment_query_count(th_comment *_tc,char *_tag){ + long i; + int tag_len; + int count; + tag_len=strlen(_tag); + count=0; + for(i=0;i<_tc->comments;i++){ + if(!oc_tagcompare(_tc->user_comments[i],_tag,tag_len))count++; + } + return count; +} + +void th_comment_clear(th_comment *_tc){ + if(_tc!=NULL){ + long i; + for(i=0;i<_tc->comments;i++)_ogg_free(_tc->user_comments[i]); + _ogg_free(_tc->user_comments); + _ogg_free(_tc->comment_lengths); + _ogg_free(_tc->vendor); + memset(_tc,0,sizeof(*_tc)); + } +} diff --git a/Engine/lib/libtheora/lib/dec/internal.c b/Engine/lib/libtheora/lib/dec/internal.c new file mode 100644 index 000000000..3fe62e55b --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/internal.c @@ -0,0 +1,383 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: internal.c 15400 2008-10-15 12:10:58Z tterribe $ + + ********************************************************************/ + +#include +#include +#include +#include "../internal.h" +#include "idct.h" + + + +/*A map from the index in the zig zag scan to the coefficient number in a + block. + All zig zag indices beyond 63 are sent to coefficient 64, so that zero runs + past the end of a block in bogus streams get mapped to a known location.*/ +const int OC_FZIG_ZAG[128]={ + 0, 1, 8,16, 9, 2, 3,10, + 17,24,32,25,18,11, 4, 5, + 12,19,26,33,40,48,41,34, + 27,20,13, 6, 7,14,21,28, + 35,42,49,56,57,50,43,36, + 29,22,15,23,30,37,44,51, + 58,59,52,45,38,31,39,46, + 53,60,61,54,47,55,62,63, + 64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64 +}; + +/*A map from the coefficient number in a block to its index in the zig zag + scan.*/ +const int OC_IZIG_ZAG[64]={ + 0, 1, 5, 6,14,15,27,28, + 2, 4, 7,13,16,26,29,42, + 3, 8,12,17,25,30,41,43, + 9,11,18,24,31,40,44,53, + 10,19,23,32,39,45,52,54, + 20,22,33,38,46,51,55,60, + 21,34,37,47,50,56,59,61, + 35,36,48,49,57,58,62,63 +}; + +/*The predictor frame to use for each macro block mode.*/ +const int OC_FRAME_FOR_MODE[8]={ + /*OC_MODE_INTER_NOMV*/ + OC_FRAME_PREV, + /*OC_MODE_INTRA*/ + OC_FRAME_SELF, + /*OC_MODE_INTER_MV*/ + OC_FRAME_PREV, + /*OC_MODE_INTER_MV_LAST*/ + OC_FRAME_PREV, + /*OC_MODE_INTER_MV_LAST2*/ + OC_FRAME_PREV, + /*OC_MODE_GOLDEN*/ + OC_FRAME_GOLD, + /*OC_MODE_GOLDEN_MV*/ + OC_FRAME_GOLD, + /*OC_MODE_INTER_MV_FOUR*/ + OC_FRAME_PREV, +}; + +/*A map from physical macro block ordering to bitstream macro block + ordering within a super block.*/ +const int OC_MB_MAP[2][2]={{0,3},{1,2}}; + +/*A list of the indices in the oc_mb.map array that can be valid for each of + the various chroma decimation types.*/ +const int OC_MB_MAP_IDXS[TH_PF_NFORMATS][12]={ + {0,1,2,3,4,8}, + {0,1,2,3,4,5,8,9}, + {0,1,2,3,4,6,8,10}, + {0,1,2,3,4,5,6,7,8,9,10,11} +}; + +/*The number of indices in the oc_mb.map array that can be valid for each of + the various chroma decimation types.*/ +const int OC_MB_MAP_NIDXS[TH_PF_NFORMATS]={6,8,8,12}; + +/*The number of extra bits that are coded with each of the DCT tokens. + Each DCT token has some fixed number of additional bits (possibly 0) stored + after the token itself, containing, for example, coefficient magnitude, + sign bits, etc.*/ +const int OC_DCT_TOKEN_EXTRA_BITS[TH_NDCT_TOKENS]={ + 0,0,0,2,3,4,12,3,6, + 0,0,0,0, + 1,1,1,1,2,3,4,5,6,10, + 1,1,1,1,1,3,4, + 2,3 +}; + + + +int oc_ilog(unsigned _v){ + int ret; + for(ret=0;_v;ret++)_v>>=1; + return ret; +} + + + +/*Determines the number of blocks or coefficients to be skipped for a given + token value. + _token: The token value to skip. + _extra_bits: The extra bits attached to this token. + Return: A positive value indicates that number of coefficients are to be + skipped in the current block. + Otherwise, the negative of the return value indicates that number of + blocks are to be ended.*/ +typedef int (*oc_token_skip_func)(int _token,int _extra_bits); + +/*Handles the simple end of block tokens.*/ +static int oc_token_skip_eob(int _token,int _extra_bits){ + static const int NBLOCKS_ADJUST[OC_NDCT_EOB_TOKEN_MAX]={1,2,3,4,8,16,0}; + return -_extra_bits-NBLOCKS_ADJUST[_token]; +} + +/*The last EOB token has a special case, where an EOB run of size zero ends all + the remaining blocks in the frame.*/ +static int oc_token_skip_eob6(int _token,int _extra_bits){ + if(!_extra_bits)return -INT_MAX; + return -_extra_bits; +} + +/*Handles the pure zero run tokens.*/ +static int oc_token_skip_zrl(int _token,int _extra_bits){ + return _extra_bits+1; +} + +/*Handles a normal coefficient value token.*/ +static int oc_token_skip_val(void){ + return 1; +} + +/*Handles a category 1A zero run/coefficient value combo token.*/ +static int oc_token_skip_run_cat1a(int _token){ + return _token-OC_DCT_RUN_CAT1A+2; +} + +/*Handles category 1b and 2 zero run/coefficient value combo tokens.*/ +static int oc_token_skip_run(int _token,int _extra_bits){ + static const int NCOEFFS_ADJUST[OC_NDCT_RUN_MAX-OC_DCT_RUN_CAT1B]={ + 7,11,2,3 + }; + static const int NCOEFFS_MASK[OC_NDCT_RUN_MAX-OC_DCT_RUN_CAT1B]={ + 3,7,0,1 + }; + _token-=OC_DCT_RUN_CAT1B; + return (_extra_bits&NCOEFFS_MASK[_token])+NCOEFFS_ADJUST[_token]; +} + +/*A jump table for computing the number of coefficients or blocks to skip for + a given token value. + This reduces all the conditional branches, etc., needed to parse these token + values down to one indirect jump.*/ +static const oc_token_skip_func OC_TOKEN_SKIP_TABLE[TH_NDCT_TOKENS]={ + oc_token_skip_eob, + oc_token_skip_eob, + oc_token_skip_eob, + oc_token_skip_eob, + oc_token_skip_eob, + oc_token_skip_eob, + oc_token_skip_eob6, + oc_token_skip_zrl, + oc_token_skip_zrl, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_run_cat1a, + (oc_token_skip_func)oc_token_skip_run_cat1a, + (oc_token_skip_func)oc_token_skip_run_cat1a, + (oc_token_skip_func)oc_token_skip_run_cat1a, + (oc_token_skip_func)oc_token_skip_run_cat1a, + oc_token_skip_run, + oc_token_skip_run, + oc_token_skip_run, + oc_token_skip_run +}; + +/*Determines the number of blocks or coefficients to be skipped for a given + token value. + _token: The token value to skip. + _extra_bits: The extra bits attached to this token. + Return: A positive value indicates that number of coefficients are to be + skipped in the current block. + Otherwise, the negative of the return value indicates that number of + blocks are to be ended. + 0 will never be returned, so that at least one coefficient in one + block will always be decoded for every token.*/ +int oc_dct_token_skip(int _token,int _extra_bits){ + return (*OC_TOKEN_SKIP_TABLE[_token])(_token,_extra_bits); +} + + +/*The function used to fill in the chroma plane motion vectors for a macro + block when 4 different motion vectors are specified in the luma plane. + This version is for use with chroma decimated in the X and Y directions. + _cbmvs: The chroma block-level motion vectors to fill in. + _lbmvs: The luma block-level motion vectors.*/ +static void oc_set_chroma_mvs00(oc_mv _cbmvs[4],const oc_mv _lbmvs[4]){ + int dx; + int dy; + dx=_lbmvs[0][0]+_lbmvs[1][0]+_lbmvs[2][0]+_lbmvs[3][0]; + dy=_lbmvs[0][1]+_lbmvs[1][1]+_lbmvs[2][1]+_lbmvs[3][1]; + _cbmvs[0][0]=(signed char)OC_DIV_ROUND_POW2(dx,2,2); + _cbmvs[0][1]=(signed char)OC_DIV_ROUND_POW2(dy,2,2); +} + +/*The function used to fill in the chroma plane motion vectors for a macro + block when 4 different motion vectors are specified in the luma plane. + This version is for use with chroma decimated in the Y direction. + _cbmvs: The chroma block-level motion vectors to fill in. + _lbmvs: The luma block-level motion vectors.*/ +static void oc_set_chroma_mvs01(oc_mv _cbmvs[4],const oc_mv _lbmvs[4]){ + int dx; + int dy; + dx=_lbmvs[0][0]+_lbmvs[2][0]; + dy=_lbmvs[0][1]+_lbmvs[2][1]; + _cbmvs[0][0]=(signed char)OC_DIV_ROUND_POW2(dx,1,1); + _cbmvs[0][1]=(signed char)OC_DIV_ROUND_POW2(dy,1,1); + dx=_lbmvs[1][0]+_lbmvs[3][0]; + dy=_lbmvs[1][1]+_lbmvs[3][1]; + _cbmvs[1][0]=(signed char)OC_DIV_ROUND_POW2(dx,1,1); + _cbmvs[1][1]=(signed char)OC_DIV_ROUND_POW2(dy,1,1); +} + +/*The function used to fill in the chroma plane motion vectors for a macro + block when 4 different motion vectors are specified in the luma plane. + This version is for use with chroma decimated in the X direction. + _cbmvs: The chroma block-level motion vectors to fill in. + _lbmvs: The luma block-level motion vectors.*/ +static void oc_set_chroma_mvs10(oc_mv _cbmvs[4],const oc_mv _lbmvs[4]){ + int dx; + int dy; + dx=_lbmvs[0][0]+_lbmvs[1][0]; + dy=_lbmvs[0][1]+_lbmvs[1][1]; + _cbmvs[0][0]=(signed char)OC_DIV_ROUND_POW2(dx,1,1); + _cbmvs[0][1]=(signed char)OC_DIV_ROUND_POW2(dy,1,1); + dx=_lbmvs[2][0]+_lbmvs[3][0]; + dy=_lbmvs[2][1]+_lbmvs[3][1]; + _cbmvs[2][0]=(signed char)OC_DIV_ROUND_POW2(dx,1,1); + _cbmvs[2][1]=(signed char)OC_DIV_ROUND_POW2(dy,1,1); +} + +/*The function used to fill in the chroma plane motion vectors for a macro + block when 4 different motion vectors are specified in the luma plane. + This version is for use with no chroma decimation. + _cbmvs: The chroma block-level motion vectors to fill in. + _lmbmv: The luma macro-block level motion vector to fill in for use in + prediction. + _lbmvs: The luma block-level motion vectors.*/ +static void oc_set_chroma_mvs11(oc_mv _cbmvs[4],const oc_mv _lbmvs[4]){ + memcpy(_cbmvs,_lbmvs,4*sizeof(_lbmvs[0])); +} + +/*A table of functions used to fill in the chroma plane motion vectors for a + macro block when 4 different motion vectors are specified in the luma + plane.*/ +const oc_set_chroma_mvs_func OC_SET_CHROMA_MVS_TABLE[TH_PF_NFORMATS]={ + (oc_set_chroma_mvs_func)oc_set_chroma_mvs00, + (oc_set_chroma_mvs_func)oc_set_chroma_mvs01, + (oc_set_chroma_mvs_func)oc_set_chroma_mvs10, + (oc_set_chroma_mvs_func)oc_set_chroma_mvs11 +}; + + + +void **oc_malloc_2d(size_t _height,size_t _width,size_t _sz){ + size_t rowsz; + size_t colsz; + size_t datsz; + char *ret; + colsz=_height*sizeof(void *); + rowsz=_sz*_width; + datsz=rowsz*_height; + /*Alloc array and row pointers.*/ + ret=(char *)_ogg_malloc(datsz+colsz); + /*Initialize the array.*/ + if(ret!=NULL){ + size_t i; + void **p; + char *datptr; + p=(void **)ret; + i=_height; + for(datptr=ret+colsz;i-->0;p++,datptr+=rowsz)*p=(void *)datptr; + } + return (void **)ret; +} + +void **oc_calloc_2d(size_t _height,size_t _width,size_t _sz){ + size_t colsz; + size_t rowsz; + size_t datsz; + char *ret; + colsz=_height*sizeof(void *); + rowsz=_sz*_width; + datsz=rowsz*_height; + /*Alloc array and row pointers.*/ + ret=(char *)_ogg_calloc(datsz+colsz,1); + /*Initialize the array.*/ + if(ret!=NULL){ + size_t i; + void **p; + char *datptr; + p=(void **)ret; + i=_height; + for(datptr=ret+colsz;i-->0;p++,datptr+=rowsz)*p=(void *)datptr; + } + return (void **)ret; +} + +void oc_free_2d(void *_ptr){ + _ogg_free(_ptr); +} + +/*Fills in a Y'CbCr buffer with a pointer to the image data in the first + buffer, but with the opposite vertical orientation. + _dst: The destination buffer. + This can be the same as _src. + _src: The source buffer.*/ +void oc_ycbcr_buffer_flip(th_ycbcr_buffer _dst, + const th_ycbcr_buffer _src){ + int pli; + for(pli=0;pli<3;pli++){ + _dst[pli].width=_src[pli].width; + _dst[pli].height=_src[pli].height; + _dst[pli].stride=-_src[pli].stride; + _dst[pli].data=_src[pli].data+(1-_dst[pli].height)*_dst[pli].stride; + } +} + +const char *th_version_string(void){ + return OC_VENDOR_STRING; +} + +ogg_uint32_t th_version_number(void){ + return (TH_VERSION_MAJOR<<16)+(TH_VERSION_MINOR<<8)+(TH_VERSION_SUB); +} + +/*Determines the packet type. + Note that this correctly interprets a 0-byte packet as a video data packet. + Return: 1 for a header packet, 0 for a data packet.*/ +int th_packet_isheader(ogg_packet *_op){ + return _op->bytes>0?_op->packet[0]>>7:0; +} + +/*Determines the frame type of a video data packet. + Note that this correctly interprets a 0-byte packet as a delta frame. + Return: 1 for a key frame, 0 for a delta frame, and -1 for a header + packet.*/ +int th_packet_iskeyframe(ogg_packet *_op){ + return _op->bytes<=0?0:_op->packet[0]&0x80?-1:!(_op->packet[0]&0x40); +} diff --git a/Engine/lib/libtheora/lib/dec/ocintrin.h b/Engine/lib/libtheora/lib/dec/ocintrin.h new file mode 100644 index 000000000..317f5aeae --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/ocintrin.h @@ -0,0 +1,88 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: ocintrin.h 15400 2008-10-15 12:10:58Z tterribe $ + + ********************************************************************/ + +/*Some common macros for potential platform-specific optimization.*/ +#include +#if !defined(_ocintrin_H) +# define _ocintrin_H (1) + +/*Some specific platforms may have optimized intrinsic or inline assembly + versions of these functions which can substantially improve performance. + We define macros for them to allow easy incorporation of these non-ANSI + features.*/ + +/*Branchless, but not correct for differences larger than INT_MAX. +static int oc_mini(int _a,int _b){ + int ambsign; + ambsign=_a-_b>>sizeof(int)*8-1; + return (_a&~ambsign)+(_b&ambsign); +}*/ + + +#define OC_MAXI(_a,_b) ((_a)<(_b)?(_b):(_a)) +#define OC_MINI(_a,_b) ((_a)>(_b)?(_b):(_a)) +/*Clamps an integer into the given range. + If _a>_c, then the lower bound _a is respected over the upper bound _c (this + behavior is required to meet our documented API behavior). + _a: The lower bound. + _b: The value to clamp. + _c: The upper boud.*/ +#define OC_CLAMPI(_a,_b,_c) (OC_MAXI(_a,OC_MINI(_b,_c))) +#define OC_CLAMP255(_x) ((unsigned char)((((_x)<0)-1)&((_x)|-((_x)>255)))) +/*Divides an integer by a power of two, truncating towards 0. + _dividend: The integer to divide. + _shift: The non-negative power of two to divide by. + _rmask: (1<<_shift)-1*/ +#define OC_DIV_POW2(_dividend,_shift,_rmask)\ + ((_dividend)+(((_dividend)>>sizeof(_dividend)*8-1)&(_rmask))>>(_shift)) +/*Divides _x by 65536, truncating towards 0.*/ +#define OC_DIV2_16(_x) OC_DIV_POW2(_x,16,0xFFFF) +/*Divides _x by 2, truncating towards 0.*/ +#define OC_DIV2(_x) OC_DIV_POW2(_x,1,0x1) +/*Divides _x by 8, truncating towards 0.*/ +#define OC_DIV8(_x) OC_DIV_POW2(_x,3,0x7) +/*Divides _x by 16, truncating towards 0.*/ +#define OC_DIV16(_x) OC_DIV_POW2(_x,4,0xF) +/*Right shifts _dividend by _shift, adding _rval, and subtracting one for + negative dividends first.. + When _rval is (1<<_shift-1), this is equivalent to division with rounding + ties towards positive infinity.*/ +#define OC_DIV_ROUND_POW2(_dividend,_shift,_rval)\ + ((_dividend)+((_dividend)>>sizeof(_dividend)*8-1)+(_rval)>>(_shift)) +/*Swaps two integers _a and _b if _a>_b.*/ +#define OC_SORT2I(_a,_b)\ + if((_a)>(_b)){\ + int t__;\ + t__=(_a);\ + (_a)=(_b);\ + (_b)=t__;\ + } + + + +/*All of these macros should expect floats as arguments.*/ +#define OC_MAXF(_a,_b) ((_a)<(_b)?(_b):(_a)) +#define OC_MINF(_a,_b) ((_a)>(_b)?(_b):(_a)) +#define OC_CLAMPF(_a,_b,_c) (OC_MINF(_a,OC_MAXF(_b,_c))) +#define OC_FABSF(_f) ((float)fabs(_f)) +#define OC_SQRTF(_f) ((float)sqrt(_f)) +#define OC_POWF(_b,_e) ((float)pow(_b,_e)) +#define OC_LOGF(_f) ((float)log(_f)) +#define OC_IFLOORF(_f) ((int)floor(_f)) +#define OC_ICEILF(_f) ((int)ceil(_f)) + +#endif diff --git a/Engine/lib/libtheora/lib/dec/quant.c b/Engine/lib/libtheora/lib/dec/quant.c new file mode 100644 index 000000000..5cb7784db --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/quant.c @@ -0,0 +1,122 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: quant.c 15400 2008-10-15 12:10:58Z tterribe $ + + ********************************************************************/ + +#include +#include +#include +#include "quant.h" +#include "decint.h" + +static const unsigned OC_DC_QUANT_MIN[2]={4<<2,8<<2}; +static const unsigned OC_AC_QUANT_MIN[2]={2<<2,4<<2}; + +/*Initializes the dequantization tables from a set of quantizer info. + Currently the dequantizer (and elsewhere enquantizer) tables are expected to + be initialized as pointing to the storage reserved for them in the + oc_theora_state (resp. oc_enc_ctx) structure. + If some tables are duplicates of others, the pointers will be adjusted to + point to a single copy of the tables, but the storage for them will not be + freed. + If you're concerned about the memory footprint, the obvious thing to do is + to move the storage out of its fixed place in the structures and allocate + it on demand. + However, a much, much better option is to only store the quantization + matrices being used for the current frame, and to recalculate these as the + qi values change between frames (this is what VP3 did).*/ +void oc_dequant_tables_init(oc_quant_table *_dequant[2][3], + int _pp_dc_scale[64],const th_quant_info *_qinfo){ + /*coding mode: intra or inter.*/ + int qti; + /*Y', C_b, C_r*/ + int pli; + for(qti=0;qti<2;qti++){ + for(pli=0;pli<3;pli++){ + oc_quant_tables stage; + /*Quality index.*/ + int qi; + /*Range iterator.*/ + int qri; + for(qi=0,qri=0; qri<=_qinfo->qi_ranges[qti][pli].nranges; qri++){ + th_quant_base base; + ogg_uint32_t q; + int qi_start; + int qi_end; + int ci; + memcpy(base,_qinfo->qi_ranges[qti][pli].base_matrices[qri], + sizeof(base)); + qi_start=qi; + if(qri==_qinfo->qi_ranges[qti][pli].nranges)qi_end=qi+1; + else qi_end=qi+_qinfo->qi_ranges[qti][pli].sizes[qri]; + /*Iterate over quality indicies in this range.*/ + for(;;){ + ogg_uint32_t qfac; + /*In the original VP3.2 code, the rounding offset and the size of the + dead zone around 0 were controlled by a "sharpness" parameter. + The size of our dead zone is now controlled by the per-coefficient + quality thresholds returned by our HVS module. + We round down from a more accurate value when the quality of the + reconstruction does not fall below our threshold and it saves bits. + Hence, all of that VP3.2 code is gone from here, and the remaining + floating point code has been implemented as equivalent integer code + with exact precision.*/ + qfac=(ogg_uint32_t)_qinfo->dc_scale[qi]*base[0]; + /*For postprocessing, not dequantization.*/ + if(_pp_dc_scale!=NULL)_pp_dc_scale[qi]=(int)(qfac/160); + /*Scale DC the coefficient from the proper table.*/ + q=(qfac/100)<<2; + q=OC_CLAMPI(OC_DC_QUANT_MIN[qti],q,OC_QUANT_MAX); + stage[qi][0]=(ogg_uint16_t)q; + /*Now scale AC coefficients from the proper table.*/ + for(ci=1;ci<64;ci++){ + q=((ogg_uint32_t)_qinfo->ac_scale[qi]*base[ci]/100)<<2; + q=OC_CLAMPI(OC_AC_QUANT_MIN[qti],q,OC_QUANT_MAX); + stage[qi][ci]=(ogg_uint16_t)q; + } + if(++qi>=qi_end)break; + /*Interpolate the next base matrix.*/ + for(ci=0;ci<64;ci++){ + base[ci]=(unsigned char)( + (2*((qi_end-qi)*_qinfo->qi_ranges[qti][pli].base_matrices[qri][ci]+ + (qi-qi_start)*_qinfo->qi_ranges[qti][pli].base_matrices[qri+1][ci]) + +_qinfo->qi_ranges[qti][pli].sizes[qri])/ + (2*_qinfo->qi_ranges[qti][pli].sizes[qri])); + } + } + } + /*Staging matrices complete; commit to memory only if this isn't a + duplicate of a preceeding plane. + This simple check helps us improve cache coherency later.*/ + { + int dupe; + int qtj; + int plj; + dupe=0; + for(qtj=0;qtj<=qti;qtj++){ + for(plj=0;plj<(qtj +#include +#include "../internal.h" +#include "idct.h" +#if defined(USE_ASM) +#if defined(_MSC_VER) +# include "x86_vc/x86int.h" +#else +# include "x86/x86int.h" +#endif +#endif +#if defined(OC_DUMP_IMAGES) +# include +# include "png.h" +#endif + +void oc_restore_fpu(const oc_theora_state *_state){ + _state->opt_vtable.restore_fpu(); +} + +void oc_restore_fpu_c(void){} + +/*Returns the fragment index of the top-left block in a macro block. + This can be used to test whether or not the whole macro block is coded. + _sb: The super block. + _quadi: The quadrant number. + Return: The index of the fragment of the upper left block in the macro + block, or -1 if the block lies outside the coded frame.*/ +static int oc_sb_quad_top_left_frag(const oc_sb *_sb,int _quadi){ + /*It so happens that under the Hilbert curve ordering described below, the + upper-left block in each macro block is at index 0, except in macro block + 3, where it is at index 2.*/ + return _sb->map[_quadi][_quadi&_quadi<<1]; +} + +/*Fills in the mapping from block positions to fragment numbers for a single + color plane. + This function also fills in the "valid" flag of each quadrant in a super + block. + _sbs: The array of super blocks for the color plane. + _frag0: The index of the first fragment in the plane. + _hfrags: The number of horizontal fragments in a coded frame. + _vfrags: The number of vertical fragments in a coded frame.*/ +static void oc_sb_create_plane_mapping(oc_sb _sbs[],int _frag0,int _hfrags, + int _vfrags){ + /*Contains the (macro_block,block) indices for a 4x4 grid of + fragments. + The pattern is a 4x4 Hilbert space-filling curve. + A Hilbert curve has the nice property that as the curve grows larger, its + fractal dimension approaches 2. + The intuition is that nearby blocks in the curve are also close spatially, + with the previous element always an immediate neighbor, so that runs of + blocks should be well correlated.*/ + static const int SB_MAP[4][4][2]={ + {{0,0},{0,1},{3,2},{3,3}}, + {{0,3},{0,2},{3,1},{3,0}}, + {{1,0},{1,3},{2,0},{2,3}}, + {{1,1},{1,2},{2,1},{2,2}} + }; + oc_sb *sb; + int yfrag; + int y; + sb=_sbs; + yfrag=_frag0; + for(y=0;;y+=4){ + int imax; + int x; + /*Figure out how many columns of blocks in this super block lie within the + image.*/ + imax=_vfrags-y; + if(imax>4)imax=4; + else if(imax<=0)break; + for(x=0;;x+=4,sb++){ + int xfrag; + int jmax; + int quadi; + int i; + /*Figure out how many rows of blocks in this super block lie within the + image.*/ + jmax=_hfrags-x; + if(jmax>4)jmax=4; + else if(jmax<=0)break; + /*By default, set all fragment indices to -1.*/ + memset(sb->map[0],0xFF,sizeof(sb->map)); + /*Fill in the fragment map for this super block.*/ + xfrag=yfrag+x; + for(i=0;imap[SB_MAP[i][j][0]][SB_MAP[i][j][1]]=xfrag+j; + } + xfrag+=_hfrags; + } + /*Mark which quadrants of this super block lie within the image.*/ + for(quadi=0;quadi<4;quadi++){ + sb->quad_valid|=(oc_sb_quad_top_left_frag(sb,quadi)>=0)<=_fplane->nvfrags)break; + for(j=0;j<2;j++){ + if(_x+j>=_fplane->nhfrags)break; + _mb->map[0][i<<1|j]=(_y+i)*_fplane->nhfrags+_x+j; + } + } +} + +/*Fills in the chroma plane fragment maps for a macro block. + This version is for use with chroma decimated in the X and Y directions. + _mb: The macro block to fill. + _fplanes: The descriptions of the fragment planes. + _x: The X location of the upper-left hand fragment in the Y plane. + _y: The Y location of the upper-left hand fragment in the Y plane.*/ +static void oc_mb_fill_cmapping00(oc_mb *_mb, + const oc_fragment_plane _fplanes[3],int _x,int _y){ + int fragi; + _x>>=1; + _y>>=1; + fragi=_y*_fplanes[1].nhfrags+_x; + _mb->map[1][0]=fragi+_fplanes[1].froffset; + _mb->map[2][0]=fragi+_fplanes[2].froffset; +} + +/*Fills in the chroma plane fragment maps for a macro block. + This version is for use with chroma decimated in the Y direction. + _mb: The macro block to fill. + _fplanes: The descriptions of the fragment planes. + _x: The X location of the upper-left hand fragment in the Y plane. + _y: The Y location of the upper-left hand fragment in the Y plane.*/ +static void oc_mb_fill_cmapping01(oc_mb *_mb, + const oc_fragment_plane _fplanes[3],int _x,int _y){ + int fragi; + int j; + _y>>=1; + fragi=_y*_fplanes[1].nhfrags+_x; + for(j=0;j<2;j++){ + if(_x+j>=_fplanes[1].nhfrags)break; + _mb->map[1][j]=fragi+_fplanes[1].froffset; + _mb->map[2][j]=fragi+_fplanes[2].froffset; + fragi++; + } +} + +/*Fills in the chroma plane fragment maps for a macro block. + This version is for use with chroma decimated in the X direction. + _mb: The macro block to fill. + _fplanes: The descriptions of the fragment planes. + _x: The X location of the upper-left hand fragment in the Y plane. + _y: The Y location of the upper-left hand fragment in the Y plane.*/ +static void oc_mb_fill_cmapping10(oc_mb *_mb, + const oc_fragment_plane _fplanes[3],int _x,int _y){ + int fragi; + int i; + _x>>=1; + fragi=_y*_fplanes[1].nhfrags+_x; + for(i=0;i<2;i++){ + if(_y+i>=_fplanes[1].nvfrags)break; + _mb->map[1][i<<1]=fragi+_fplanes[1].froffset; + _mb->map[2][i<<1]=fragi+_fplanes[2].froffset; + fragi+=_fplanes[1].nhfrags; + } +} + +/*Fills in the chroma plane fragment maps for a macro block. + This version is for use with no chroma decimation. + This uses the already filled-in Y plane values. + _mb: The macro block to fill. + _fplanes: The descriptions of the fragment planes.*/ +static void oc_mb_fill_cmapping11(oc_mb *_mb, + const oc_fragment_plane _fplanes[3]){ + int k; + for(k=0;k<4;k++){ + if(_mb->map[0][k]>=0){ + _mb->map[1][k]=_mb->map[0][k]+_fplanes[1].froffset; + _mb->map[2][k]=_mb->map[0][k]+_fplanes[2].froffset; + } + } +} + +/*The function type used to fill in the chroma plane fragment maps for a + macro block. + _mb: The macro block to fill. + _fplanes: The descriptions of the fragment planes. + _x: The X location of the upper-left hand fragment in the Y plane. + _y: The Y location of the upper-left hand fragment in the Y plane.*/ +typedef void (*oc_mb_fill_cmapping_func)(oc_mb *_mb, + const oc_fragment_plane _fplanes[3],int _xfrag0,int _yfrag0); + +/*A table of functions used to fill in the chroma plane fragment maps for a + macro block for each type of chrominance decimation.*/ +static const oc_mb_fill_cmapping_func OC_MB_FILL_CMAPPING_TABLE[4]={ + oc_mb_fill_cmapping00, + oc_mb_fill_cmapping01, + oc_mb_fill_cmapping10, + (oc_mb_fill_cmapping_func)oc_mb_fill_cmapping11 +}; + +/*Fills in the mapping from macro blocks to their corresponding fragment + numbers in each plane. + _mbs: The array of macro blocks. + _fplanes: The descriptions of the fragment planes. + _ctype: The chroma decimation type.*/ +static void oc_mb_create_mapping(oc_mb _mbs[], + const oc_fragment_plane _fplanes[3],int _ctype){ + oc_mb_fill_cmapping_func mb_fill_cmapping; + oc_mb *mb0; + int y; + mb0=_mbs; + mb_fill_cmapping=OC_MB_FILL_CMAPPING_TABLE[_ctype]; + /*Loop through the Y plane super blocks.*/ + for(y=0;y<_fplanes[0].nvfrags;y+=4){ + int x; + for(x=0;x<_fplanes[0].nhfrags;x+=4,mb0+=4){ + int ymb; + /*Loop through the macro blocks in each super block in display order.*/ + for(ymb=0;ymb<2;ymb++){ + int xmb; + for(xmb=0;xmb<2;xmb++){ + oc_mb *mb; + int mbx; + int mby; + mb=mb0+OC_MB_MAP[ymb][xmb]; + mbx=x|xmb<<1; + mby=y|ymb<<1; + mb->x=mbx<<3; + mb->y=mby<<3; + /*Initialize fragment indexes to -1.*/ + memset(mb->map,0xFF,sizeof(mb->map)); + /*Make sure this macro block is within the encoded region.*/ + if(mbx>=_fplanes[0].nhfrags||mby>=_fplanes[0].nvfrags){ + mb->mode=OC_MODE_INVALID; + continue; + } + /*Fill in the fragment indices for the Y plane.*/ + oc_mb_fill_ymapping(mb,_fplanes,mbx,mby); + /*Fill in the fragment indices for the chroma planes.*/ + (*mb_fill_cmapping)(mb,_fplanes,mbx,mby); + } + } + } + } +} + +/*Marks the fragments which fall all or partially outside the displayable + region of the frame. + _state: The Theora state containing the fragments to be marked.*/ +static void oc_state_border_init(oc_theora_state *_state){ + typedef struct{ + int x0; + int y0; + int xf; + int yf; + }oc_crop_rect; + oc_fragment *frag; + oc_fragment *yfrag_end; + oc_fragment *xfrag_end; + oc_fragment_plane *fplane; + oc_crop_rect *crop; + oc_crop_rect crop_rects[3]; + int pli; + int y; + int x; + /*The method we use here is slow, but the code is dead simple and handles + all the special cases easily. + We only ever need to do it once.*/ + /*Loop through the fragments, marking those completely outside the + displayable region and constructing a border mask for those that straddle + the border.*/ + _state->nborders=0; + yfrag_end=frag=_state->frags; + for(pli=0;pli<3;pli++){ + fplane=_state->fplanes+pli; + crop=crop_rects+pli; + /*Set up the cropping rectangle for this plane.*/ + crop->x0=_state->info.pic_x; + crop->xf=_state->info.pic_x+_state->info.pic_width; + crop->y0=_state->info.pic_y; + crop->yf=_state->info.pic_y+_state->info.pic_height; + if(pli>0){ + if(!(_state->info.pixel_fmt&1)){ + crop->x0=crop->x0>>1; + crop->xf=crop->xf+1>>1; + } + if(!(_state->info.pixel_fmt&2)){ + crop->y0=crop->y0>>1; + crop->yf=crop->yf+1>>1; + } + } + y=0; + for(yfrag_end+=fplane->nfrags;fragnhfrags;fragx0||crop->xf<=x||y+8<=crop->y0||crop->yf<=y|| + crop->x0>=crop->xf||crop->y0>=crop->yf){ + frag->invalid=1; + } + /*Otherwise, check to see if it straddles the border.*/ + else if(xx0&&crop->x0xf&&crop->xfy0&&crop->y0yf&&crop->yf=crop->x0&&x+jxf&&y+i>=crop->y0&&y+iyf){ + mask|=(ogg_int64_t)1<<(i<<3|j); + npixels++; + } + } + } + /*Search the fragment array for border info with the same pattern. + In general, there will be at most 8 different patterns (per + plane).*/ + for(i=0;;i++){ + if(i>=_state->nborders){ + _state->nborders++; + _state->borders[i].mask=mask; + _state->borders[i].npixels=npixels; + } + else if(_state->borders[i].mask!=mask)continue; + frag->border=_state->borders+i; + break; + } + } + } + } + } +} + +static void oc_state_frarray_init(oc_theora_state *_state){ + int yhfrags; + int yvfrags; + int chfrags; + int cvfrags; + int yfrags; + int cfrags; + int nfrags; + int yhsbs; + int yvsbs; + int chsbs; + int cvsbs; + int ysbs; + int csbs; + int nsbs; + int nmbs; + int hdec; + int vdec; + int pli; + /*Figure out the number of fragments in each plane.*/ + /*These parameters have already been validated to be multiples of 16.*/ + yhfrags=_state->info.frame_width>>3; + yvfrags=_state->info.frame_height>>3; + hdec=!(_state->info.pixel_fmt&1); + vdec=!(_state->info.pixel_fmt&2); + chfrags=yhfrags+hdec>>hdec; + cvfrags=yvfrags+vdec>>vdec; + yfrags=yhfrags*yvfrags; + cfrags=chfrags*cvfrags; + nfrags=yfrags+2*cfrags; + /*Figure out the number of super blocks in each plane.*/ + yhsbs=yhfrags+3>>2; + yvsbs=yvfrags+3>>2; + chsbs=chfrags+3>>2; + cvsbs=cvfrags+3>>2; + ysbs=yhsbs*yvsbs; + csbs=chsbs*cvsbs; + nsbs=ysbs+2*csbs; + nmbs=ysbs<<2; + /*Initialize the fragment array.*/ + _state->fplanes[0].nhfrags=yhfrags; + _state->fplanes[0].nvfrags=yvfrags; + _state->fplanes[0].froffset=0; + _state->fplanes[0].nfrags=yfrags; + _state->fplanes[0].nhsbs=yhsbs; + _state->fplanes[0].nvsbs=yvsbs; + _state->fplanes[0].sboffset=0; + _state->fplanes[0].nsbs=ysbs; + _state->fplanes[1].nhfrags=_state->fplanes[2].nhfrags=chfrags; + _state->fplanes[1].nvfrags=_state->fplanes[2].nvfrags=cvfrags; + _state->fplanes[1].froffset=yfrags; + _state->fplanes[2].froffset=yfrags+cfrags; + _state->fplanes[1].nfrags=_state->fplanes[2].nfrags=cfrags; + _state->fplanes[1].nhsbs=_state->fplanes[2].nhsbs=chsbs; + _state->fplanes[1].nvsbs=_state->fplanes[2].nvsbs=cvsbs; + _state->fplanes[1].sboffset=ysbs; + _state->fplanes[2].sboffset=ysbs+csbs; + _state->fplanes[1].nsbs=_state->fplanes[2].nsbs=csbs; + _state->nfrags=nfrags; + _state->frags=_ogg_calloc(nfrags,sizeof(oc_fragment)); + _state->nsbs=nsbs; + _state->sbs=_ogg_calloc(nsbs,sizeof(oc_sb)); + _state->nhmbs=yhsbs<<1; + _state->nvmbs=yvsbs<<1; + _state->nmbs=nmbs; + _state->mbs=_ogg_calloc(nmbs,sizeof(oc_mb)); + _state->coded_fragis=_ogg_malloc(nfrags*sizeof(_state->coded_fragis[0])); + _state->uncoded_fragis=_state->coded_fragis+nfrags; + _state->coded_mbis=_ogg_malloc(nmbs*sizeof(_state->coded_mbis[0])); + /*Create the mapping from super blocks to fragments.*/ + for(pli=0;pli<3;pli++){ + oc_fragment_plane *fplane; + fplane=_state->fplanes+pli; + oc_sb_create_plane_mapping(_state->sbs+fplane->sboffset, + fplane->froffset,fplane->nhfrags,fplane->nvfrags); + } + /*Create the mapping from macro blocks to fragments.*/ + oc_mb_create_mapping(_state->mbs,_state->fplanes,_state->info.pixel_fmt); + /*Initialize the invalid and border fields of each fragment.*/ + oc_state_border_init(_state); +} + +static void oc_state_frarray_clear(oc_theora_state *_state){ + _ogg_free(_state->coded_mbis); + _ogg_free(_state->coded_fragis); + _ogg_free(_state->mbs); + _ogg_free(_state->sbs); + _ogg_free(_state->frags); +} + + +/*Initializes the buffers used for reconstructed frames. + These buffers are padded with 16 extra pixels on each side, to allow + unrestricted motion vectors without special casing the boundary. + If chroma is decimated in either direction, the padding is reduced by a + factor of 2 on the appropriate sides. + _enc: The encoding context to store the buffers in.*/ +static void oc_state_ref_bufs_init(oc_theora_state *_state){ + th_info *info; + unsigned char *ref_frame_data; + size_t yplane_sz; + size_t cplane_sz; + int yhstride; + int yvstride; + int chstride; + int cvstride; + int yoffset; + int coffset; + int rfi; + info=&_state->info; + /*Compute the image buffer parameters for each plane.*/ + yhstride=info->frame_width+2*OC_UMV_PADDING; + yvstride=info->frame_height+2*OC_UMV_PADDING; + chstride=yhstride>>!(info->pixel_fmt&1); + cvstride=yvstride>>!(info->pixel_fmt&2); + yplane_sz=(size_t)yhstride*yvstride; + cplane_sz=(size_t)chstride*cvstride; + yoffset=OC_UMV_PADDING+OC_UMV_PADDING*yhstride; + coffset=(OC_UMV_PADDING>>!(info->pixel_fmt&1))+ + (OC_UMV_PADDING>>!(info->pixel_fmt&2))*chstride; + _state->ref_frame_data=ref_frame_data=_ogg_malloc(3*(yplane_sz+2*cplane_sz)); + /*Set up the width, height and stride for the image buffers.*/ + _state->ref_frame_bufs[0][0].width=info->frame_width; + _state->ref_frame_bufs[0][0].height=info->frame_height; + _state->ref_frame_bufs[0][0].stride=yhstride; + _state->ref_frame_bufs[0][1].width=_state->ref_frame_bufs[0][2].width= + info->frame_width>>!(info->pixel_fmt&1); + _state->ref_frame_bufs[0][1].height=_state->ref_frame_bufs[0][2].height= + info->frame_height>>!(info->pixel_fmt&2); + _state->ref_frame_bufs[0][1].stride=_state->ref_frame_bufs[0][2].stride= + chstride; + memcpy(_state->ref_frame_bufs[1],_state->ref_frame_bufs[0], + sizeof(_state->ref_frame_bufs[0])); + memcpy(_state->ref_frame_bufs[2],_state->ref_frame_bufs[0], + sizeof(_state->ref_frame_bufs[0])); + /*Set up the data pointers for the image buffers.*/ + for(rfi=0;rfi<3;rfi++){ + _state->ref_frame_bufs[rfi][0].data=ref_frame_data+yoffset; + ref_frame_data+=yplane_sz; + _state->ref_frame_bufs[rfi][1].data=ref_frame_data+coffset; + ref_frame_data+=cplane_sz; + _state->ref_frame_bufs[rfi][2].data=ref_frame_data+coffset; + ref_frame_data+=cplane_sz; + /*Flip the buffer upside down.*/ + oc_ycbcr_buffer_flip(_state->ref_frame_bufs[rfi], + _state->ref_frame_bufs[rfi]); + /*Initialize the fragment pointers into this buffer.*/ + oc_state_fill_buffer_ptrs(_state,rfi,_state->ref_frame_bufs[rfi]); + } + /*Initialize the reference frame indexes.*/ + _state->ref_frame_idx[OC_FRAME_GOLD]= + _state->ref_frame_idx[OC_FRAME_PREV]= + _state->ref_frame_idx[OC_FRAME_SELF]=-1; +} + +static void oc_state_ref_bufs_clear(oc_theora_state *_state){ + _ogg_free(_state->ref_frame_data); +} + + +void oc_state_vtable_init_c(oc_theora_state *_state){ + _state->opt_vtable.frag_recon_intra=oc_frag_recon_intra_c; + _state->opt_vtable.frag_recon_inter=oc_frag_recon_inter_c; + _state->opt_vtable.frag_recon_inter2=oc_frag_recon_inter2_c; + _state->opt_vtable.state_frag_copy=oc_state_frag_copy_c; + _state->opt_vtable.state_frag_recon=oc_state_frag_recon_c; + _state->opt_vtable.state_loop_filter_frag_rows= + oc_state_loop_filter_frag_rows_c; + _state->opt_vtable.restore_fpu=oc_restore_fpu_c; +} + +/*Initialize the accelerated function pointers.*/ +void oc_state_vtable_init(oc_theora_state *_state){ +#if defined(USE_ASM) + oc_state_vtable_init_x86(_state); +#else + oc_state_vtable_init_c(_state); +#endif +} + + +int oc_state_init(oc_theora_state *_state,const th_info *_info){ + int old_granpos; + /*First validate the parameters.*/ + if(_info==NULL)return TH_EFAULT; + /*The width and height of the encoded frame must be multiples of 16. + They must also, when divided by 16, fit into a 16-bit unsigned integer. + The displayable frame offset coordinates must fit into an 8-bit unsigned + integer. + Note that the offset Y in the API is specified on the opposite side from + how it is specified in the bitstream, because the Y axis is flipped in + the bitstream. + The displayable frame must fit inside the encoded frame. + The color space must be one known by the encoder.*/ + if((_info->frame_width&0xF)||(_info->frame_height&0xF)|| + _info->frame_width>=0x100000||_info->frame_height>=0x100000|| + _info->pic_x+_info->pic_width>_info->frame_width|| + _info->pic_y+_info->pic_height>_info->frame_height|| + _info->pic_x>255|| + _info->frame_height-_info->pic_height-_info->pic_y>255|| + _info->colorspace<0||_info->colorspace>=TH_CS_NSPACES|| + _info->pixel_fmt<0||_info->pixel_fmt>=TH_PF_NFORMATS){ + return TH_EINVAL; + } + memset(_state,0,sizeof(*_state)); + memcpy(&_state->info,_info,sizeof(*_info)); + /*Invert the sense of pic_y to match Theora's right-handed coordinate + system.*/ + _state->info.pic_y=_info->frame_height-_info->pic_height-_info->pic_y; + _state->frame_type=OC_UNKWN_FRAME; + oc_state_vtable_init(_state); + oc_state_frarray_init(_state); + oc_state_ref_bufs_init(_state); + /*If the keyframe_granule_shift is out of range, use the maximum allowable + value.*/ + if(_info->keyframe_granule_shift<0||_info->keyframe_granule_shift>31){ + _state->info.keyframe_granule_shift=31; + } + _state->keyframe_num=1; + _state->curframe_num=0; + /*3.2.0 streams mark the frame index instead of the frame count. + This was changed with stream version 3.2.1 to conform to other Ogg + codecs. + We subtract an extra one from the frame number for old streams.*/ + old_granpos=!TH_VERSION_CHECK(_info,3,2,1); + _state->curframe_num-=old_granpos; + _state->keyframe_num-=old_granpos; + return 0; +} + +void oc_state_clear(oc_theora_state *_state){ + oc_state_ref_bufs_clear(_state); + oc_state_frarray_clear(_state); +} + + +/*Duplicates the pixels on the border of the image plane out into the + surrounding padding for use by unrestricted motion vectors. + This function only adds the left and right borders, and only for the fragment + rows specified. + _refi: The index of the reference buffer to pad. + _pli: The color plane. + _y0: The Y coordinate of the first row to pad. + _yend: The Y coordinate of the row to stop padding at.*/ +void oc_state_borders_fill_rows(oc_theora_state *_state,int _refi,int _pli, + int _y0,int _yend){ + th_img_plane *iplane; + unsigned char *apix; + unsigned char *bpix; + unsigned char *epix; + int hpadding; + hpadding=OC_UMV_PADDING>>(_pli!=0&&!(_state->info.pixel_fmt&1)); + iplane=_state->ref_frame_bufs[_refi]+_pli; + apix=iplane->data+_y0*iplane->stride; + bpix=apix+iplane->width-1; + epix=iplane->data+_yend*iplane->stride; + /*Note the use of != instead of <, which allows ystride to be negative.*/ + while(apix!=epix){ + memset(apix-hpadding,apix[0],hpadding); + memset(bpix+1,bpix[0],hpadding); + apix+=iplane->stride; + bpix+=iplane->stride; + } +} + +/*Duplicates the pixels on the border of the image plane out into the + surrounding padding for use by unrestricted motion vectors. + This function only adds the top and bottom borders, and must be called after + the left and right borders are added. + _refi: The index of the reference buffer to pad. + _pli: The color plane.*/ +void oc_state_borders_fill_caps(oc_theora_state *_state,int _refi,int _pli){ + th_img_plane *iplane; + unsigned char *apix; + unsigned char *bpix; + unsigned char *epix; + int hpadding; + int vpadding; + int fullw; + hpadding=OC_UMV_PADDING>>(_pli!=0&&!(_state->info.pixel_fmt&1)); + vpadding=OC_UMV_PADDING>>(_pli!=0&&!(_state->info.pixel_fmt&2)); + iplane=_state->ref_frame_bufs[_refi]+_pli; + fullw=iplane->width+(hpadding<<1); + apix=iplane->data-hpadding; + bpix=iplane->data+(iplane->height-1)*iplane->stride-hpadding; + epix=apix-iplane->stride*vpadding; + while(apix!=epix){ + memcpy(apix-iplane->stride,apix,fullw); + memcpy(bpix+iplane->stride,bpix,fullw); + apix-=iplane->stride; + bpix+=iplane->stride; + } +} + +/*Duplicates the pixels on the border of the given reference image out into + the surrounding padding for use by unrestricted motion vectors. + _state: The context containing the reference buffers. + _refi: The index of the reference buffer to pad.*/ +void oc_state_borders_fill(oc_theora_state *_state,int _refi){ + int pli; + for(pli=0;pli<3;pli++){ + oc_state_borders_fill_rows(_state,_refi,pli,0, + _state->ref_frame_bufs[_refi][pli].height); + oc_state_borders_fill_caps(_state,_refi,pli); + } +} + +/*Sets the buffer pointer in each fragment to point to the portion of the + image buffer which it corresponds to. + _state: The Theora state to fill. + _buf_idx: The index of the buffer pointer to fill. + The first three correspond to our reconstructed frame buffers, + while the last corresponds to the input image. + _img: The image buffer to fill the fragments with.*/ +void oc_state_fill_buffer_ptrs(oc_theora_state *_state,int _buf_idx, + th_ycbcr_buffer _img){ + int pli; + /*Special handling for the input image to give us the opportunity to skip + some updates. + The other buffers do not change throughout the encoding process.*/ + if(_buf_idx==OC_FRAME_IO){ + if(memcmp(_state->input,_img,sizeof(th_ycbcr_buffer))==0)return; + memcpy(_state->input,_img,sizeof(th_ycbcr_buffer)); + } + for(pli=0;pli<3;pli++){ + th_img_plane *iplane; + oc_fragment_plane *fplane; + oc_fragment *frag; + oc_fragment *vfrag_end; + unsigned char *vpix; + iplane=&_img[pli]; + fplane=&_state->fplanes[pli]; + vpix=iplane->data; + frag=_state->frags+fplane->froffset; + vfrag_end=frag+fplane->nfrags; + while(fragnhfrags;fragbuffer[_buf_idx]=hpix; + hpix+=8; + } + vpix+=iplane->stride<<3; + } + } +} + +/*Returns the macro block index of the macro block in the given position. + _state: The Theora state the macro block is contained in. + _mbx: The X coordinate of the macro block (in macro blocks, not pixels). + _mby: The Y coordinate of the macro block (in macro blocks, not pixels). + Return: The index of the macro block in the given position.*/ +int oc_state_mbi_for_pos(oc_theora_state *_state,int _mbx,int _mby){ + return ((_mbx&~1)<<1)+(_mby&~1)*_state->nhmbs+OC_MB_MAP[_mby&1][_mbx&1]; +} + +/*Determines the offsets in an image buffer to use for motion compensation. + _state: The Theora state the offsets are to be computed with. + _offsets: Returns the offset for the buffer(s). + _offsets[0] is always set. + _offsets[1] is set if the motion vector has non-zero fractional + components. + _dx: The X component of the motion vector. + _dy: The Y component of the motion vector. + _ystride: The Y stride in the buffer the motion vector points into. + _pli: The color plane index. + Return: The number of offsets returned: 1 or 2.*/ +int oc_state_get_mv_offsets(oc_theora_state *_state,int _offsets[2], + int _dx,int _dy,int _ystride,int _pli){ + int xprec; + int yprec; + int xfrac; + int yfrac; + /*Here is a brief description of how Theora handles motion vectors: + Motion vector components are specified to half-pixel accuracy in + undecimated directions of each plane, and quarter-pixel accuracy in + decimated directions. + Integer parts are extracted by dividing (not shifting) by the + appropriate amount, with truncation towards zero. + These integer values are used to calculate the first offset. + + If either of the fractional parts are non-zero, then a second offset is + computed. + No third or fourth offsets are computed, even if both components have + non-zero fractional parts. + The second offset is computed by dividing (not shifting) by the + appropriate amount, always truncating _away_ from zero.*/ + /*These two variables decide whether we are in half- or quarter-pixel + precision in each component.*/ + xprec=1+(!(_state->info.pixel_fmt&1)&&_pli); + yprec=1+(!(_state->info.pixel_fmt&2)&&_pli); + /*These two variables are either 0 if all the fractional bits are 0 or 1 if + any of them are non-zero.*/ + xfrac=!!(_dx&(1<>xprec)+(_dy>>yprec)*_ystride; + if(xfrac||yfrac){ + /*This branchless code is equivalent to: + if(_dx<0)_offests[0]=-(-_dx>>xprec); + else _offsets[0]=(_dx>>xprec); + if(_dy<0)_offsets[0]-=(-_dy>>yprec)*_ystride; + else _offsets[0]+=(_dy>>yprec)*_ystride; + _offsets[1]=_offsets[0]; + if(xfrac){ + if(_dx<0)_offsets[1]++; + else _offsets[1]--; + } + if(yfrac){ + if(_dy<0)_offsets[1]+=_ystride; + else _offsets[1]-=_ystride; + }*/ + _offsets[1]=_offsets[0]; + _offsets[_dx>=0]+=xfrac; + _offsets[_dy>=0]+=_ystride&-yfrac; + return 2; + } + else return 1; +} + +void oc_state_frag_recon(oc_theora_state *_state,oc_fragment *_frag, + int _pli,ogg_int16_t _dct_coeffs[128],int _last_zzi,int _ncoefs, + ogg_uint16_t _dc_iquant,const ogg_uint16_t _ac_iquant[64]){ + _state->opt_vtable.state_frag_recon(_state,_frag,_pli,_dct_coeffs, + _last_zzi,_ncoefs,_dc_iquant,_ac_iquant); +} + +void oc_state_frag_recon_c(oc_theora_state *_state,oc_fragment *_frag, + int _pli,ogg_int16_t _dct_coeffs[128],int _last_zzi,int _ncoefs, + ogg_uint16_t _dc_iquant, const ogg_uint16_t _ac_iquant[64]){ + ogg_int16_t dct_buf[64]; + ogg_int16_t res_buf[64]; + int dst_framei; + int dst_ystride; + int zzi; + int ci; + /*_last_zzi is subtly different from an actual count of the number of + coefficients we decoded for this block. + It contains the value of zzi BEFORE the final token in the block was + decoded. + In most cases this is an EOB token (the continuation of an EOB run from a + previous block counts), and so this is the same as the coefficient count. + However, in the case that the last token was NOT an EOB token, but filled + the block up with exactly 64 coefficients, _last_zzi will be less than 64. + Provided the last token was not a pure zero run, the minimum value it can + be is 46, and so that doesn't affect any of the cases in this routine. + However, if the last token WAS a pure zero run of length 63, then _last_zzi + will be 1 while the number of coefficients decoded is 64. + Thus, we will trigger the following special case, where the real + coefficient count would not. + Note also that a zero run of length 64 will give _last_zzi a value of 0, + but we still process the DC coefficient, which might have a non-zero value + due to DC prediction. + Although convoluted, this is arguably the correct behavior: it allows us to + dequantize fewer coefficients and use a smaller transform when the block + ends with a long zero run instead of a normal EOB token. + It could be smarter... multiple separate zero runs at the end of a block + will fool it, but an encoder that generates these really deserves what it + gets. + Needless to say we inherited this approach from VP3.*/ + /*Special case only having a DC component.*/ + if(_last_zzi<2){ + ogg_int16_t p; + /*Why is the iquant product rounded in this case and no others? + Who knows.*/ + p=(ogg_int16_t)((ogg_int32_t)_frag->dc*_dc_iquant+15>>5); + /*LOOP VECTORIZES.*/ + for(ci=0;ci<64;ci++)res_buf[ci]=p; + } + else{ + /*First, dequantize the coefficients.*/ + dct_buf[0]=(ogg_int16_t)((ogg_int32_t)_frag->dc*_dc_iquant); + for(zzi=1;zzi<_ncoefs;zzi++){ + int ci; + ci=OC_FZIG_ZAG[zzi]; + dct_buf[ci]=(ogg_int16_t)((ogg_int32_t)_dct_coeffs[zzi]*_ac_iquant[ci]); + } + /*Then, fill in the remainder of the coefficients with 0's, and perform + the iDCT.*/ + if(_last_zzi<10){ + for(;zzi<10;zzi++)dct_buf[OC_FZIG_ZAG[zzi]]=0; + oc_idct8x8_10_c(res_buf,dct_buf); + } + else{ + for(;zzi<64;zzi++)dct_buf[OC_FZIG_ZAG[zzi]]=0; + oc_idct8x8_c(res_buf,dct_buf); + } + } + /*Fill in the target buffer.*/ + dst_framei=_state->ref_frame_idx[OC_FRAME_SELF]; + dst_ystride=_state->ref_frame_bufs[dst_framei][_pli].stride; + /*For now ystride values in all ref frames assumed to be equal.*/ + if(_frag->mbmode==OC_MODE_INTRA){ + oc_frag_recon_intra(_state,_frag->buffer[dst_framei],dst_ystride,res_buf); + } + else{ + int ref_framei; + int ref_ystride; + int mvoffsets[2]; + ref_framei=_state->ref_frame_idx[OC_FRAME_FOR_MODE[_frag->mbmode]]; + ref_ystride=_state->ref_frame_bufs[ref_framei][_pli].stride; + if(oc_state_get_mv_offsets(_state,mvoffsets,_frag->mv[0],_frag->mv[1], + ref_ystride,_pli)>1){ + oc_frag_recon_inter2(_state,_frag->buffer[dst_framei],dst_ystride, + _frag->buffer[ref_framei]+mvoffsets[0],ref_ystride, + _frag->buffer[ref_framei]+mvoffsets[1],ref_ystride,res_buf); + } + else{ + oc_frag_recon_inter(_state,_frag->buffer[dst_framei],dst_ystride, + _frag->buffer[ref_framei]+mvoffsets[0],ref_ystride,res_buf); + } + } + oc_restore_fpu(_state); +} + +/*Copies the fragments specified by the lists of fragment indices from one + frame to another. + _fragis: A pointer to a list of fragment indices. + _nfragis: The number of fragment indices to copy. + _dst_frame: The reference frame to copy to. + _src_frame: The reference frame to copy from. + _pli: The color plane the fragments lie in.*/ +void oc_state_frag_copy(const oc_theora_state *_state,const int *_fragis, + int _nfragis,int _dst_frame,int _src_frame,int _pli){ + _state->opt_vtable.state_frag_copy(_state,_fragis,_nfragis,_dst_frame, + _src_frame,_pli); +} + +void oc_state_frag_copy_c(const oc_theora_state *_state,const int *_fragis, + int _nfragis,int _dst_frame,int _src_frame,int _pli){ + const int *fragi; + const int *fragi_end; + int dst_framei; + int dst_ystride; + int src_framei; + int src_ystride; + dst_framei=_state->ref_frame_idx[_dst_frame]; + src_framei=_state->ref_frame_idx[_src_frame]; + dst_ystride=_state->ref_frame_bufs[dst_framei][_pli].stride; + src_ystride=_state->ref_frame_bufs[src_framei][_pli].stride; + fragi_end=_fragis+_nfragis; + for(fragi=_fragis;fragifrags+*fragi; + dst=frag->buffer[dst_framei]; + src=frag->buffer[src_framei]; + for(j=0;j<8;j++){ + memcpy(dst,src,sizeof(dst[0])*8); + dst+=dst_ystride; + src+=src_ystride; + } + } +} + +static void loop_filter_h(unsigned char *_pix,int _ystride,int *_bv){ + int y; + _pix-=2; + for(y=0;y<8;y++){ + int f; + f=_pix[0]-_pix[3]+3*(_pix[2]-_pix[1]); + /*The _bv array is used to compute the function + f=OC_CLAMPI(OC_MINI(-_2flimit-f,0),f,OC_MAXI(_2flimit-f,0)); + where _2flimit=_state->loop_filter_limits[_state->qis[0]]<<1;*/ + f=*(_bv+(f+4>>3)); + _pix[1]=OC_CLAMP255(_pix[1]+f); + _pix[2]=OC_CLAMP255(_pix[2]-f); + _pix+=_ystride; + } +} + +static void loop_filter_v(unsigned char *_pix,int _ystride,int *_bv){ + int y; + _pix-=_ystride*2; + for(y=0;y<8;y++){ + int f; + f=_pix[0]-_pix[_ystride*3]+3*(_pix[_ystride*2]-_pix[_ystride]); + /*The _bv array is used to compute the function + f=OC_CLAMPI(OC_MINI(-_2flimit-f,0),f,OC_MAXI(_2flimit-f,0)); + where _2flimit=_state->loop_filter_limits[_state->qis[0]]<<1;*/ + f=*(_bv+(f+4>>3)); + _pix[_ystride]=OC_CLAMP255(_pix[_ystride]+f); + _pix[_ystride*2]=OC_CLAMP255(_pix[_ystride*2]-f); + _pix++; + } +} + +/*Initialize the bounding values array used by the loop filter. + _bv: Storage for the array. + Return: 0 on success, or a non-zero value if no filtering need be applied.*/ +int oc_state_loop_filter_init(oc_theora_state *_state,int *_bv){ + int flimit; + int i; + flimit=_state->loop_filter_limits[_state->qis[0]]; + if(flimit==0)return 1; + memset(_bv,0,sizeof(_bv[0])*256); + for(i=0;i=0)_bv[127-i-flimit]=i-flimit; + _bv[127-i]=-i; + _bv[127+i]=i; + if(127+i+flimit<256)_bv[127+i+flimit]=flimit-i; + } + return 0; +} + +/*Apply the loop filter to a given set of fragment rows in the given plane. + The filter may be run on the bottom edge, affecting pixels in the next row of + fragments, so this row also needs to be available. + _bv: The bounding values array. + _refi: The index of the frame buffer to filter. + _pli: The color plane to filter. + _fragy0: The Y coordinate of the first fragment row to filter. + _fragy_end: The Y coordinate of the fragment row to stop filtering at.*/ +void oc_state_loop_filter_frag_rows(oc_theora_state *_state,int *_bv, + int _refi,int _pli,int _fragy0,int _fragy_end){ + _state->opt_vtable.state_loop_filter_frag_rows(_state,_bv,_refi,_pli, + _fragy0,_fragy_end); +} + +void oc_state_loop_filter_frag_rows_c(oc_theora_state *_state,int *_bv, + int _refi,int _pli,int _fragy0,int _fragy_end){ + th_img_plane *iplane; + oc_fragment_plane *fplane; + oc_fragment *frag_top; + oc_fragment *frag0; + oc_fragment *frag; + oc_fragment *frag_end; + oc_fragment *frag0_end; + oc_fragment *frag_bot; + _bv+=127; + iplane=_state->ref_frame_bufs[_refi]+_pli; + fplane=_state->fplanes+_pli; + /*The following loops are constructed somewhat non-intuitively on purpose. + The main idea is: if a block boundary has at least one coded fragment on + it, the filter is applied to it. + However, the order that the filters are applied in matters, and VP3 chose + the somewhat strange ordering used below.*/ + frag_top=_state->frags+fplane->froffset; + frag0=frag_top+_fragy0*fplane->nhfrags; + frag0_end=frag0+(_fragy_end-_fragy0)*fplane->nhfrags; + frag_bot=_state->frags+fplane->froffset+fplane->nfrags; + while(frag0nhfrags; + while(fragcoded){ + if(frag>frag0){ + loop_filter_h(frag->buffer[_refi],iplane->stride,_bv); + } + if(frag0>frag_top){ + loop_filter_v(frag->buffer[_refi],iplane->stride,_bv); + } + if(frag+1coded){ + loop_filter_h(frag->buffer[_refi]+8,iplane->stride,_bv); + } + if(frag+fplane->nhfragsnhfrags)->coded){ + loop_filter_v((frag+fplane->nhfrags)->buffer[_refi], + iplane->stride,_bv); + } + } + frag++; + } + frag0+=fplane->nhfrags; + } +} + +#if defined(OC_DUMP_IMAGES) +int oc_state_dump_frame(const oc_theora_state *_state,int _frame, + const char *_suf){ + /*Dump a PNG of the reconstructed image.*/ + png_structp png; + png_infop info; + png_bytep *image; + FILE *fp; + char fname[16]; + unsigned char *y_row; + unsigned char *u_row; + unsigned char *v_row; + unsigned char *y; + unsigned char *u; + unsigned char *v; + ogg_int64_t iframe; + ogg_int64_t pframe; + int y_stride; + int u_stride; + int v_stride; + int framei; + int width; + int height; + int imgi; + int imgj; + width=_state->info.frame_width; + height=_state->info.frame_height; + iframe=_state->granpos>>_state->info.keyframe_granule_shift; + pframe=_state->granpos-(iframe<<_state->info.keyframe_granule_shift); + sprintf(fname,"%08i%s.png",(int)(iframe+pframe),_suf); + fp=fopen(fname,"wb"); + if(fp==NULL)return TH_EFAULT; + image=(png_bytep *)oc_malloc_2d(height,6*width,sizeof(image[0][0])); + png=png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL); + if(png==NULL){ + oc_free_2d(image); + fclose(fp); + return TH_EFAULT; + } + info=png_create_info_struct(png); + if(info==NULL){ + png_destroy_write_struct(&png,NULL); + oc_free_2d(image); + fclose(fp); + return TH_EFAULT; + } + if(setjmp(png_jmpbuf(png))){ + png_destroy_write_struct(&png,&info); + oc_free_2d(image); + fclose(fp); + return TH_EFAULT; + } + framei=_state->ref_frame_idx[_frame]; + y_row=_state->ref_frame_bufs[framei][0].data; + u_row=_state->ref_frame_bufs[framei][1].data; + v_row=_state->ref_frame_bufs[framei][2].data; + y_stride=_state->ref_frame_bufs[framei][0].stride; + u_stride=_state->ref_frame_bufs[framei][1].stride; + v_stride=_state->ref_frame_bufs[framei][2].stride; + /*Chroma up-sampling is just done with a box filter. + This is very likely what will actually be used in practice on a real + display, and also removes one more layer to search in for the source of + artifacts. + As an added bonus, it's dead simple.*/ + for(imgi=height;imgi-->0;){ + int dc; + y=y_row; + u=u_row; + v=v_row; + for(imgj=0;imgj<6*width;){ + float yval; + float uval; + float vval; + unsigned rval; + unsigned gval; + unsigned bval; + /*This is intentionally slow and very accurate.*/ + yval=(*y-16)*(1.0F/219); + uval=(*u-128)*(2*(1-0.114F)/224); + vval=(*v-128)*(2*(1-0.299F)/224); + rval=OC_CLAMPI(0,(int)(65535*(yval+vval)+0.5F),65535); + gval=OC_CLAMPI(0,(int)(65535*( + yval-uval*(0.114F/0.587F)-vval*(0.299F/0.587F))+0.5F),65535); + bval=OC_CLAMPI(0,(int)(65535*(yval+uval)+0.5F),65535); + image[imgi][imgj++]=(unsigned char)(rval>>8); + image[imgi][imgj++]=(unsigned char)(rval&0xFF); + image[imgi][imgj++]=(unsigned char)(gval>>8); + image[imgi][imgj++]=(unsigned char)(gval&0xFF); + image[imgi][imgj++]=(unsigned char)(bval>>8); + image[imgi][imgj++]=(unsigned char)(bval&0xFF); + dc=(y-y_row&1)|(_state->info.pixel_fmt&1); + y++; + u+=dc; + v+=dc; + } + dc=-((height-1-imgi&1)|_state->info.pixel_fmt>>1); + y_row+=y_stride; + u_row+=dc&u_stride; + v_row+=dc&v_stride; + } + png_init_io(png,fp); + png_set_compression_level(png,Z_BEST_COMPRESSION); + png_set_IHDR(png,info,width,height,16,PNG_COLOR_TYPE_RGB, + PNG_INTERLACE_NONE,PNG_COMPRESSION_TYPE_DEFAULT,PNG_FILTER_TYPE_DEFAULT); + switch(_state->info.colorspace){ + case TH_CS_ITU_REC_470M:{ + png_set_gAMA(png,info,2.2); + png_set_cHRM_fixed(png,info,31006,31616, + 67000,32000,21000,71000,14000,8000); + }break; + case TH_CS_ITU_REC_470BG:{ + png_set_gAMA(png,info,2.67); + png_set_cHRM_fixed(png,info,31271,32902, + 64000,33000,29000,60000,15000,6000); + }break; + } + png_set_pHYs(png,info,_state->info.aspect_numerator, + _state->info.aspect_denominator,0); + png_set_rows(png,info,image); + png_write_png(png,info,PNG_TRANSFORM_IDENTITY,NULL); + png_write_end(png,info); + png_destroy_write_struct(&png,&info); + oc_free_2d(image); + fclose(fp); + return 0; +} +#endif + + + +ogg_int64_t th_granule_frame(void *_encdec,ogg_int64_t _granpos){ + oc_theora_state *state; + state=(oc_theora_state *)_encdec; + if(_granpos>=0){ + ogg_int64_t iframe; + ogg_int64_t pframe; + iframe=_granpos>>state->info.keyframe_granule_shift; + pframe=_granpos-(iframe<info.keyframe_granule_shift); + /*3.2.0 streams store the frame index in the granule position. + 3.2.1 and later store the frame count. + We return the index, so adjust the value if we have a 3.2.1 or later + stream.*/ + return iframe+pframe-TH_VERSION_CHECK(&state->info,3,2,1); + } + return -1; +} + +double th_granule_time(void *_encdec,ogg_int64_t _granpos){ + oc_theora_state *state; + state=(oc_theora_state *)_encdec; + if(_granpos>=0){ + return (th_granule_frame(_encdec, _granpos)+1)*( + (double)state->info.fps_denominator/state->info.fps_numerator); + } + return -1; +} diff --git a/Engine/lib/libtheora/lib/dec/x86/mmxfrag.c b/Engine/lib/libtheora/lib/dec/x86/mmxfrag.c new file mode 100644 index 000000000..b4f8167a6 --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/x86/mmxfrag.c @@ -0,0 +1,291 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2003 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: mmxfrag.c 15400 2008-10-15 12:10:58Z tterribe $ + + ********************************************************************/ + +/*MMX acceleration of fragment reconstruction for motion compensation. + Originally written by Rudolf Marek. + Additional optimization by Nils Pipenbrinck. + Note: Loops are unrolled for best performance. + The iteration each instruction belongs to is marked in the comments as #i.*/ +#include "x86int.h" +#include + +#if defined(USE_ASM) + +void oc_frag_recon_intra_mmx(unsigned char *_dst,int _dst_ystride, + const ogg_int16_t *_residue){ + __asm__ __volatile__( + /*Set mm0 to 0xFFFFFFFFFFFFFFFF.*/ + "pcmpeqw %%mm0,%%mm0\n\t" + /*#0 Load low residue.*/ + "movq 0*8(%[residue]),%%mm1\n\t" + /*#0 Load high residue.*/ + "movq 1*8(%[residue]),%%mm2\n\t" + /*Set mm0 to 0x8000800080008000.*/ + "psllw $15,%%mm0\n\t" + /*#1 Load low residue.*/ + "movq 2*8(%[residue]),%%mm3\n\t" + /*#1 Load high residue.*/ + "movq 3*8(%[residue]),%%mm4\n\t" + /*Set mm0 to 0x0080008000800080.*/ + "psrlw $8,%%mm0\n\t" + /*#2 Load low residue.*/ + "movq 4*8(%[residue]),%%mm5\n\t" + /*#2 Load high residue.*/ + "movq 5*8(%[residue]),%%mm6\n\t" + /*#0 Bias low residue.*/ + "paddsw %%mm0,%%mm1\n\t" + /*#0 Bias high residue.*/ + "paddsw %%mm0,%%mm2\n\t" + /*#0 Pack to byte.*/ + "packuswb %%mm2,%%mm1\n\t" + /*#1 Bias low residue.*/ + "paddsw %%mm0,%%mm3\n\t" + /*#1 Bias high residue.*/ + "paddsw %%mm0,%%mm4\n\t" + /*#1 Pack to byte.*/ + "packuswb %%mm4,%%mm3\n\t" + /*#2 Bias low residue.*/ + "paddsw %%mm0,%%mm5\n\t" + /*#2 Bias high residue.*/ + "paddsw %%mm0,%%mm6\n\t" + /*#2 Pack to byte.*/ + "packuswb %%mm6,%%mm5\n\t" + /*#0 Write row.*/ + "movq %%mm1,(%[dst])\n\t" + /*#1 Write row.*/ + "movq %%mm3,(%[dst],%[dst_ystride])\n\t" + /*#2 Write row.*/ + "movq %%mm5,(%[dst],%[dst_ystride],2)\n\t" + /*#3 Load low residue.*/ + "movq 6*8(%[residue]),%%mm1\n\t" + /*#3 Load high residue.*/ + "movq 7*8(%[residue]),%%mm2\n\t" + /*#4 Load high residue.*/ + "movq 8*8(%[residue]),%%mm3\n\t" + /*#4 Load high residue.*/ + "movq 9*8(%[residue]),%%mm4\n\t" + /*#5 Load high residue.*/ + "movq 10*8(%[residue]),%%mm5\n\t" + /*#5 Load high residue.*/ + "movq 11*8(%[residue]),%%mm6\n\t" + /*#3 Bias low residue.*/ + "paddsw %%mm0,%%mm1\n\t" + /*#3 Bias high residue.*/ + "paddsw %%mm0,%%mm2\n\t" + /*#3 Pack to byte.*/ + "packuswb %%mm2,%%mm1\n\t" + /*#4 Bias low residue.*/ + "paddsw %%mm0,%%mm3\n\t" + /*#4 Bias high residue.*/ + "paddsw %%mm0,%%mm4\n\t" + /*#4 Pack to byte.*/ + "packuswb %%mm4,%%mm3\n\t" + /*#5 Bias low residue.*/ + "paddsw %%mm0,%%mm5\n\t" + /*#5 Bias high residue.*/ + "paddsw %%mm0,%%mm6\n\t" + /*#5 Pack to byte.*/ + "packuswb %%mm6,%%mm5\n\t" + /*#3 Write row.*/ + "movq %%mm1,(%[dst],%[dst_ystride3])\n\t" + /*#4 Write row.*/ + "movq %%mm3,(%[dst4])\n\t" + /*#5 Write row.*/ + "movq %%mm5,(%[dst4],%[dst_ystride])\n\t" + /*#6 Load low residue.*/ + "movq 12*8(%[residue]),%%mm1\n\t" + /*#6 Load high residue.*/ + "movq 13*8(%[residue]),%%mm2\n\t" + /*#7 Load low residue.*/ + "movq 14*8(%[residue]),%%mm3\n\t" + /*#7 Load high residue.*/ + "movq 15*8(%[residue]),%%mm4\n\t" + /*#6 Bias low residue.*/ + "paddsw %%mm0,%%mm1\n\t" + /*#6 Bias high residue.*/ + "paddsw %%mm0,%%mm2\n\t" + /*#6 Pack to byte.*/ + "packuswb %%mm2,%%mm1\n\t" + /*#7 Bias low residue.*/ + "paddsw %%mm0,%%mm3\n\t" + /*#7 Bias high residue.*/ + "paddsw %%mm0,%%mm4\n\t" + /*#7 Pack to byte.*/ + "packuswb %%mm4,%%mm3\n\t" + /*#6 Write row.*/ + "movq %%mm1,(%[dst4],%[dst_ystride],2)\n\t" + /*#7 Write row.*/ + "movq %%mm3,(%[dst4],%[dst_ystride3])\n\t" + : + :[residue]"r"(_residue), + [dst]"r"(_dst), + [dst4]"r"(_dst+(_dst_ystride<<2)), + [dst_ystride]"r"((ptrdiff_t)_dst_ystride), + [dst_ystride3]"r"((ptrdiff_t)_dst_ystride*3) + :"memory" + ); +} + +void oc_frag_recon_inter_mmx(unsigned char *_dst,int _dst_ystride, + const unsigned char *_src,int _src_ystride,const ogg_int16_t *_residue){ + int i; + /*Zero mm0.*/ + __asm__ __volatile__("pxor %%mm0,%%mm0\n\t"::); + for(i=4;i-->0;){ + __asm__ __volatile__( + /*#0 Load source.*/ + "movq (%[src]),%%mm3\n\t" + /*#1 Load source.*/ + "movq (%[src],%[src_ystride]),%%mm7\n\t" + /*#0 Get copy of src.*/ + "movq %%mm3,%%mm4\n\t" + /*#0 Expand high source.*/ + "punpckhbw %%mm0,%%mm4\n\t" + /*#0 Expand low source.*/ + "punpcklbw %%mm0,%%mm3\n\t" + /*#0 Add residue high.*/ + "paddsw 8(%[residue]),%%mm4\n\t" + /*#1 Get copy of src.*/ + "movq %%mm7,%%mm2\n\t" + /*#0 Add residue low.*/ + "paddsw (%[residue]), %%mm3\n\t" + /*#1 Expand high source.*/ + "punpckhbw %%mm0,%%mm2\n\t" + /*#0 Pack final row pixels.*/ + "packuswb %%mm4,%%mm3\n\t" + /*#1 Expand low source.*/ + "punpcklbw %%mm0,%%mm7\n\t" + /*#1 Add residue low.*/ + "paddsw 16(%[residue]),%%mm7\n\t" + /*#1 Add residue high.*/ + "paddsw 24(%[residue]),%%mm2\n\t" + /*Advance residue.*/ + "lea 32(%[residue]),%[residue]\n\t" + /*#1 Pack final row pixels.*/ + "packuswb %%mm2,%%mm7\n\t" + /*Advance src.*/ + "lea (%[src],%[src_ystride],2),%[src]\n\t" + /*#0 Write row.*/ + "movq %%mm3,(%[dst])\n\t" + /*#1 Write row.*/ + "movq %%mm7,(%[dst],%[dst_ystride])\n\t" + /*Advance dst.*/ + "lea (%[dst],%[dst_ystride],2),%[dst]\n\t" + :[residue]"+r"(_residue),[dst]"+r"(_dst),[src]"+r"(_src) + :[dst_ystride]"r"((ptrdiff_t)_dst_ystride), + [src_ystride]"r"((ptrdiff_t)_src_ystride) + :"memory" + ); + } +} + +void oc_frag_recon_inter2_mmx(unsigned char *_dst,int _dst_ystride, + const unsigned char *_src1,int _src1_ystride,const unsigned char *_src2, + int _src2_ystride,const ogg_int16_t *_residue){ + int i; + /*NOTE: This assumes that + _dst_ystride==_src1_ystride&&_dst_ystride==_src2_ystride. + This is currently always the case, but a slower fallback version will need + to be written if it ever is not.*/ + /*Zero mm7.*/ + __asm__ __volatile__("pxor %%mm7,%%mm7\n\t"::); + for(i=4;i-->0;){ + __asm__ __volatile__( + /*#0 Load src1.*/ + "movq (%[src1]),%%mm0\n\t" + /*#0 Load src2.*/ + "movq (%[src2]),%%mm2\n\t" + /*#0 Copy src1.*/ + "movq %%mm0,%%mm1\n\t" + /*#0 Copy src2.*/ + "movq %%mm2,%%mm3\n\t" + /*#1 Load src1.*/ + "movq (%[src1],%[ystride]),%%mm4\n\t" + /*#0 Unpack lower src1.*/ + "punpcklbw %%mm7,%%mm0\n\t" + /*#1 Load src2.*/ + "movq (%[src2],%[ystride]),%%mm5\n\t" + /*#0 Unpack higher src1.*/ + "punpckhbw %%mm7,%%mm1\n\t" + /*#0 Unpack lower src2.*/ + "punpcklbw %%mm7,%%mm2\n\t" + /*#0 Unpack higher src2.*/ + "punpckhbw %%mm7,%%mm3\n\t" + /*Advance src1 ptr.*/ + "lea (%[src1],%[ystride],2),%[src1]\n\t" + /*Advance src2 ptr.*/ + "lea (%[src2],%[ystride],2),%[src2]\n\t" + /*#0 Lower src1+src2.*/ + "paddsw %%mm2,%%mm0\n\t" + /*#0 Higher src1+src2.*/ + "paddsw %%mm3,%%mm1\n\t" + /*#1 Copy src1.*/ + "movq %%mm4,%%mm2\n\t" + /*#0 Build lo average.*/ + "psraw $1,%%mm0\n\t" + /*#1 Copy src2.*/ + "movq %%mm5,%%mm3\n\t" + /*#1 Unpack lower src1.*/ + "punpcklbw %%mm7,%%mm4\n\t" + /*#0 Build hi average.*/ + "psraw $1,%%mm1\n\t" + /*#1 Unpack higher src1.*/ + "punpckhbw %%mm7,%%mm2\n\t" + /*#0 low+=residue.*/ + "paddsw (%[residue]),%%mm0\n\t" + /*#1 Unpack lower src2.*/ + "punpcklbw %%mm7,%%mm5\n\t" + /*#0 high+=residue.*/ + "paddsw 8(%[residue]),%%mm1\n\t" + /*#1 Unpack higher src2.*/ + "punpckhbw %%mm7,%%mm3\n\t" + /*#1 Lower src1+src2.*/ + "paddsw %%mm4,%%mm5\n\t" + /*#0 Pack and saturate.*/ + "packuswb %%mm1,%%mm0\n\t" + /*#1 Higher src1+src2.*/ + "paddsw %%mm2,%%mm3\n\t" + /*#0 Write row.*/ + "movq %%mm0,(%[dst])\n\t" + /*#1 Build lo average.*/ + "psraw $1,%%mm5\n\t" + /*#1 Build hi average.*/ + "psraw $1,%%mm3\n\t" + /*#1 low+=residue.*/ + "paddsw 16(%[residue]),%%mm5\n\t" + /*#1 high+=residue.*/ + "paddsw 24(%[residue]),%%mm3\n\t" + /*#1 Pack and saturate.*/ + "packuswb %%mm3,%%mm5\n\t" + /*#1 Write row ptr.*/ + "movq %%mm5,(%[dst],%[ystride])\n\t" + /*Advance residue ptr.*/ + "add $32,%[residue]\n\t" + /*Advance dest ptr.*/ + "lea (%[dst],%[ystride],2),%[dst]\n\t" + :[dst]"+r"(_dst),[residue]"+r"(_residue), + [src1]"+r"(_src1),[src2]"+r"(_src2) + :[ystride]"r"((ptrdiff_t)_dst_ystride) + :"memory" + ); + } +} + +void oc_restore_fpu_mmx(void){ + __asm__ __volatile__("emms\n\t"); +} +#endif diff --git a/Engine/lib/libtheora/lib/dec/x86/mmxidct.c b/Engine/lib/libtheora/lib/dec/x86/mmxidct.c new file mode 100644 index 000000000..5dbbe201a --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/x86/mmxidct.c @@ -0,0 +1,535 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: mmxidct.c 15400 2008-10-15 12:10:58Z tterribe $ + + ********************************************************************/ + +/*MMX acceleration of Theora's iDCT. + Originally written by Rudolf Marek, based on code from On2's VP3.*/ +#include +#include "../dct.h" +#include "../idct.h" + +#include "x86int.h" + +#if defined(USE_ASM) + +/*These are offsets into the table of constants below.*/ +/*7 rows of cosines, in order: pi/16 * (1 ... 7).*/ +#define OC_COSINE_OFFSET (0) +/*A row of 8's.*/ +#define OC_EIGHT_OFFSET (56) + + + +/*A table of constants used by the MMX routines.*/ +static const ogg_uint16_t __attribute__((aligned(8),used)) + OC_IDCT_CONSTS[(7+1)*4]={ + (ogg_uint16_t)OC_C1S7,(ogg_uint16_t)OC_C1S7, + (ogg_uint16_t)OC_C1S7,(ogg_uint16_t)OC_C1S7, + (ogg_uint16_t)OC_C2S6,(ogg_uint16_t)OC_C2S6, + (ogg_uint16_t)OC_C2S6,(ogg_uint16_t)OC_C2S6, + (ogg_uint16_t)OC_C3S5,(ogg_uint16_t)OC_C3S5, + (ogg_uint16_t)OC_C3S5,(ogg_uint16_t)OC_C3S5, + (ogg_uint16_t)OC_C4S4,(ogg_uint16_t)OC_C4S4, + (ogg_uint16_t)OC_C4S4,(ogg_uint16_t)OC_C4S4, + (ogg_uint16_t)OC_C5S3,(ogg_uint16_t)OC_C5S3, + (ogg_uint16_t)OC_C5S3,(ogg_uint16_t)OC_C5S3, + (ogg_uint16_t)OC_C6S2,(ogg_uint16_t)OC_C6S2, + (ogg_uint16_t)OC_C6S2,(ogg_uint16_t)OC_C6S2, + (ogg_uint16_t)OC_C7S1,(ogg_uint16_t)OC_C7S1, + (ogg_uint16_t)OC_C7S1,(ogg_uint16_t)OC_C7S1, + 8, 8, 8, 8 +}; + +/*Converts the expression in the argument to a string.*/ +#define OC_M2STR(_s) #_s + +/*38 cycles*/ +#define OC_IDCT_BEGIN \ + "#OC_IDCT_BEGIN\n\t" \ + "movq "OC_I(3)",%%mm2\n\t" \ + "movq "OC_C(3)",%%mm6\n\t" \ + "movq %%mm2,%%mm4\n\t" \ + "movq "OC_J(5)",%%mm7\n\t" \ + "pmulhw %%mm6,%%mm4\n\t" \ + "movq "OC_C(5)",%%mm1\n\t" \ + "pmulhw %%mm7,%%mm6\n\t" \ + "movq %%mm1,%%mm5\n\t" \ + "pmulhw %%mm2,%%mm1\n\t" \ + "movq "OC_I(1)",%%mm3\n\t" \ + "pmulhw %%mm7,%%mm5\n\t" \ + "movq "OC_C(1)",%%mm0\n\t" \ + "paddw %%mm2,%%mm4\n\t" \ + "paddw %%mm7,%%mm6\n\t" \ + "paddw %%mm1,%%mm2\n\t" \ + "movq "OC_J(7)",%%mm1\n\t" \ + "paddw %%mm5,%%mm7\n\t" \ + "movq %%mm0,%%mm5\n\t" \ + "pmulhw %%mm3,%%mm0\n\t" \ + "paddw %%mm7,%%mm4\n\t" \ + "pmulhw %%mm1,%%mm5\n\t" \ + "movq "OC_C(7)",%%mm7\n\t" \ + "psubw %%mm2,%%mm6\n\t" \ + "paddw %%mm3,%%mm0\n\t" \ + "pmulhw %%mm7,%%mm3\n\t" \ + "movq "OC_I(2)",%%mm2\n\t" \ + "pmulhw %%mm1,%%mm7\n\t" \ + "paddw %%mm1,%%mm5\n\t" \ + "movq %%mm2,%%mm1\n\t" \ + "pmulhw "OC_C(2)",%%mm2\n\t" \ + "psubw %%mm5,%%mm3\n\t" \ + "movq "OC_J(6)",%%mm5\n\t" \ + "paddw %%mm7,%%mm0\n\t" \ + "movq %%mm5,%%mm7\n\t" \ + "psubw %%mm4,%%mm0\n\t" \ + "pmulhw "OC_C(2)",%%mm5\n\t" \ + "paddw %%mm1,%%mm2\n\t" \ + "pmulhw "OC_C(6)",%%mm1\n\t" \ + "paddw %%mm4,%%mm4\n\t" \ + "paddw %%mm0,%%mm4\n\t" \ + "psubw %%mm6,%%mm3\n\t" \ + "paddw %%mm7,%%mm5\n\t" \ + "paddw %%mm6,%%mm6\n\t" \ + "pmulhw "OC_C(6)",%%mm7\n\t" \ + "paddw %%mm3,%%mm6\n\t" \ + "movq %%mm4,"OC_I(1)"\n\t" \ + "psubw %%mm5,%%mm1\n\t" \ + "movq "OC_C(4)",%%mm4\n\t" \ + "movq %%mm3,%%mm5\n\t" \ + "pmulhw %%mm4,%%mm3\n\t" \ + "paddw %%mm2,%%mm7\n\t" \ + "movq %%mm6,"OC_I(2)"\n\t" \ + "movq %%mm0,%%mm2\n\t" \ + "movq "OC_I(0)",%%mm6\n\t" \ + "pmulhw %%mm4,%%mm0\n\t" \ + "paddw %%mm3,%%mm5\n\t" \ + "movq "OC_J(4)",%%mm3\n\t" \ + "psubw %%mm1,%%mm5\n\t" \ + "paddw %%mm0,%%mm2\n\t" \ + "psubw %%mm3,%%mm6\n\t" \ + "movq %%mm6,%%mm0\n\t" \ + "pmulhw %%mm4,%%mm6\n\t" \ + "paddw %%mm3,%%mm3\n\t" \ + "paddw %%mm1,%%mm1\n\t" \ + "paddw %%mm0,%%mm3\n\t" \ + "paddw %%mm5,%%mm1\n\t" \ + "pmulhw %%mm3,%%mm4\n\t" \ + "paddw %%mm0,%%mm6\n\t" \ + "psubw %%mm2,%%mm6\n\t" \ + "paddw %%mm2,%%mm2\n\t" \ + "movq "OC_I(1)",%%mm0\n\t" \ + "paddw %%mm6,%%mm2\n\t" \ + "paddw %%mm3,%%mm4\n\t" \ + "psubw %%mm1,%%mm2\n\t" \ + "#end OC_IDCT_BEGIN\n\t" \ + +/*38+8=46 cycles.*/ +#define OC_ROW_IDCT \ + "#OC_ROW_IDCT\n" \ + OC_IDCT_BEGIN \ + /*r3=D'*/ \ + "movq "OC_I(2)",%%mm3\n\t" \ + /*r4=E'=E-G*/ \ + "psubw %%mm7,%%mm4\n\t" \ + /*r1=H'+H'*/ \ + "paddw %%mm1,%%mm1\n\t" \ + /*r7=G+G*/ \ + "paddw %%mm7,%%mm7\n\t" \ + /*r1=R1=A''+H'*/ \ + "paddw %%mm2,%%mm1\n\t" \ + /*r7=G'=E+G*/ \ + "paddw %%mm4,%%mm7\n\t" \ + /*r4=R4=E'-D'*/ \ + "psubw %%mm3,%%mm4\n\t" \ + "paddw %%mm3,%%mm3\n\t" \ + /*r6=R6=F'-B''*/ \ + "psubw %%mm5,%%mm6\n\t" \ + "paddw %%mm5,%%mm5\n\t" \ + /*r3=R3=E'+D'*/ \ + "paddw %%mm4,%%mm3\n\t" \ + /*r5=R5=F'+B''*/ \ + "paddw %%mm6,%%mm5\n\t" \ + /*r7=R7=G'-C'*/ \ + "psubw %%mm0,%%mm7\n\t" \ + "paddw %%mm0,%%mm0\n\t" \ + /*Save R1.*/ \ + "movq %%mm1,"OC_I(1)"\n\t" \ + /*r0=R0=G.+C.*/ \ + "paddw %%mm7,%%mm0\n\t" \ + "#end OC_ROW_IDCT\n\t" \ + +/*The following macro does two 4x4 transposes in place. + At entry, we assume: + r0 = a3 a2 a1 a0 + I(1) = b3 b2 b1 b0 + r2 = c3 c2 c1 c0 + r3 = d3 d2 d1 d0 + + r4 = e3 e2 e1 e0 + r5 = f3 f2 f1 f0 + r6 = g3 g2 g1 g0 + r7 = h3 h2 h1 h0 + + At exit, we have: + I(0) = d0 c0 b0 a0 + I(1) = d1 c1 b1 a1 + I(2) = d2 c2 b2 a2 + I(3) = d3 c3 b3 a3 + + J(4) = h0 g0 f0 e0 + J(5) = h1 g1 f1 e1 + J(6) = h2 g2 f2 e2 + J(7) = h3 g3 f3 e3 + + I(0) I(1) I(2) I(3) is the transpose of r0 I(1) r2 r3. + J(4) J(5) J(6) J(7) is the transpose of r4 r5 r6 r7. + + Since r1 is free at entry, we calculate the Js first.*/ +/*19 cycles.*/ +#define OC_TRANSPOSE \ + "#OC_TRANSPOSE\n\t" \ + "movq %%mm4,%%mm1\n\t" \ + "punpcklwd %%mm5,%%mm4\n\t" \ + "movq %%mm0,"OC_I(0)"\n\t" \ + "punpckhwd %%mm5,%%mm1\n\t" \ + "movq %%mm6,%%mm0\n\t" \ + "punpcklwd %%mm7,%%mm6\n\t" \ + "movq %%mm4,%%mm5\n\t" \ + "punpckldq %%mm6,%%mm4\n\t" \ + "punpckhdq %%mm6,%%mm5\n\t" \ + "movq %%mm1,%%mm6\n\t" \ + "movq %%mm4,"OC_J(4)"\n\t" \ + "punpckhwd %%mm7,%%mm0\n\t" \ + "movq %%mm5,"OC_J(5)"\n\t" \ + "punpckhdq %%mm0,%%mm6\n\t" \ + "movq "OC_I(0)",%%mm4\n\t" \ + "punpckldq %%mm0,%%mm1\n\t" \ + "movq "OC_I(1)",%%mm5\n\t" \ + "movq %%mm4,%%mm0\n\t" \ + "movq %%mm6,"OC_J(7)"\n\t" \ + "punpcklwd %%mm5,%%mm0\n\t" \ + "movq %%mm1,"OC_J(6)"\n\t" \ + "punpckhwd %%mm5,%%mm4\n\t" \ + "movq %%mm2,%%mm5\n\t" \ + "punpcklwd %%mm3,%%mm2\n\t" \ + "movq %%mm0,%%mm1\n\t" \ + "punpckldq %%mm2,%%mm0\n\t" \ + "punpckhdq %%mm2,%%mm1\n\t" \ + "movq %%mm4,%%mm2\n\t" \ + "movq %%mm0,"OC_I(0)"\n\t" \ + "punpckhwd %%mm3,%%mm5\n\t" \ + "movq %%mm1,"OC_I(1)"\n\t" \ + "punpckhdq %%mm5,%%mm4\n\t" \ + "punpckldq %%mm5,%%mm2\n\t" \ + "movq %%mm4,"OC_I(3)"\n\t" \ + "movq %%mm2,"OC_I(2)"\n\t" \ + "#end OC_TRANSPOSE\n\t" \ + +/*38+19=57 cycles.*/ +#define OC_COLUMN_IDCT \ + "#OC_COLUMN_IDCT\n" \ + OC_IDCT_BEGIN \ + "paddw "OC_8",%%mm2\n\t" \ + /*r1=H'+H'*/ \ + "paddw %%mm1,%%mm1\n\t" \ + /*r1=R1=A''+H'*/ \ + "paddw %%mm2,%%mm1\n\t" \ + /*r2=NR2*/ \ + "psraw $4,%%mm2\n\t" \ + /*r4=E'=E-G*/ \ + "psubw %%mm7,%%mm4\n\t" \ + /*r1=NR1*/ \ + "psraw $4,%%mm1\n\t" \ + /*r3=D'*/ \ + "movq "OC_I(2)",%%mm3\n\t" \ + /*r7=G+G*/ \ + "paddw %%mm7,%%mm7\n\t" \ + /*Store NR2 at I(2).*/ \ + "movq %%mm2,"OC_I(2)"\n\t" \ + /*r7=G'=E+G*/ \ + "paddw %%mm4,%%mm7\n\t" \ + /*Store NR1 at I(1).*/ \ + "movq %%mm1,"OC_I(1)"\n\t" \ + /*r4=R4=E'-D'*/ \ + "psubw %%mm3,%%mm4\n\t" \ + "paddw "OC_8",%%mm4\n\t" \ + /*r3=D'+D'*/ \ + "paddw %%mm3,%%mm3\n\t" \ + /*r3=R3=E'+D'*/ \ + "paddw %%mm4,%%mm3\n\t" \ + /*r4=NR4*/ \ + "psraw $4,%%mm4\n\t" \ + /*r6=R6=F'-B''*/ \ + "psubw %%mm5,%%mm6\n\t" \ + /*r3=NR3*/ \ + "psraw $4,%%mm3\n\t" \ + "paddw "OC_8",%%mm6\n\t" \ + /*r5=B''+B''*/ \ + "paddw %%mm5,%%mm5\n\t" \ + /*r5=R5=F'+B''*/ \ + "paddw %%mm6,%%mm5\n\t" \ + /*r6=NR6*/ \ + "psraw $4,%%mm6\n\t" \ + /*Store NR4 at J(4).*/ \ + "movq %%mm4,"OC_J(4)"\n\t" \ + /*r5=NR5*/ \ + "psraw $4,%%mm5\n\t" \ + /*Store NR3 at I(3).*/ \ + "movq %%mm3,"OC_I(3)"\n\t" \ + /*r7=R7=G'-C'*/ \ + "psubw %%mm0,%%mm7\n\t" \ + "paddw "OC_8",%%mm7\n\t" \ + /*r0=C'+C'*/ \ + "paddw %%mm0,%%mm0\n\t" \ + /*r0=R0=G'+C'*/ \ + "paddw %%mm7,%%mm0\n\t" \ + /*r7=NR7*/ \ + "psraw $4,%%mm7\n\t" \ + /*Store NR6 at J(6).*/ \ + "movq %%mm6,"OC_J(6)"\n\t" \ + /*r0=NR0*/ \ + "psraw $4,%%mm0\n\t" \ + /*Store NR5 at J(5).*/ \ + "movq %%mm5,"OC_J(5)"\n\t" \ + /*Store NR7 at J(7).*/ \ + "movq %%mm7,"OC_J(7)"\n\t" \ + /*Store NR0 at I(0).*/ \ + "movq %%mm0,"OC_I(0)"\n\t" \ + "#end OC_COLUMN_IDCT\n\t" \ + +#define OC_MID(_m,_i) OC_M2STR(_m+(_i)*8)"(%[c])" +#define OC_C(_i) OC_MID(OC_COSINE_OFFSET,_i-1) +#define OC_8 OC_MID(OC_EIGHT_OFFSET,0) + +void oc_idct8x8_mmx(ogg_int16_t _y[64]){ + /*This routine accepts an 8x8 matrix, but in transposed form. + Every 4x4 submatrix is transposed.*/ + __asm__ __volatile__( +#define OC_I(_k) OC_M2STR((_k*16))"(%[y])" +#define OC_J(_k) OC_M2STR(((_k-4)*16)+8)"(%[y])" + OC_ROW_IDCT + OC_TRANSPOSE +#undef OC_I +#undef OC_J +#define OC_I(_k) OC_M2STR((_k*16)+64)"(%[y])" +#define OC_J(_k) OC_M2STR(((_k-4)*16)+72)"(%[y])" + OC_ROW_IDCT + OC_TRANSPOSE +#undef OC_I +#undef OC_J +#define OC_I(_k) OC_M2STR((_k*16))"(%[y])" +#define OC_J(_k) OC_I(_k) + OC_COLUMN_IDCT +#undef OC_I +#undef OC_J +#define OC_I(_k) OC_M2STR((_k*16)+8)"(%[y])" +#define OC_J(_k) OC_I(_k) + OC_COLUMN_IDCT +#undef OC_I +#undef OC_J + "emms\n\t" + : + :[y]"r"(_y),[c]"r"(OC_IDCT_CONSTS) + ); +} + +/*25 cycles.*/ +#define OC_IDCT_BEGIN_10 \ + "#OC_IDCT_BEGIN_10\n\t" \ + "movq "OC_I(3)",%%mm2\n\t" \ + "nop\n\t" \ + "movq "OC_C(3)",%%mm6\n\t" \ + "movq %%mm2,%%mm4\n\t" \ + "movq "OC_C(5)",%%mm1\n\t" \ + "pmulhw %%mm6,%%mm4\n\t" \ + "movq "OC_I(1)",%%mm3\n\t" \ + "pmulhw %%mm2,%%mm1\n\t" \ + "movq "OC_C(1)",%%mm0\n\t" \ + "paddw %%mm2,%%mm4\n\t" \ + "pxor %%mm6,%%mm6\n\t" \ + "paddw %%mm1,%%mm2\n\t" \ + "movq "OC_I(2)",%%mm5\n\t" \ + "pmulhw %%mm3,%%mm0\n\t" \ + "movq %%mm5,%%mm1\n\t" \ + "paddw %%mm3,%%mm0\n\t" \ + "pmulhw "OC_C(7)",%%mm3\n\t" \ + "psubw %%mm2,%%mm6\n\t" \ + "pmulhw "OC_C(2)",%%mm5\n\t" \ + "psubw %%mm4,%%mm0\n\t" \ + "movq "OC_I(2)",%%mm7\n\t" \ + "paddw %%mm4,%%mm4\n\t" \ + "paddw %%mm5,%%mm7\n\t" \ + "paddw %%mm0,%%mm4\n\t" \ + "pmulhw "OC_C(6)",%%mm1\n\t" \ + "psubw %%mm6,%%mm3\n\t" \ + "movq %%mm4,"OC_I(1)"\n\t" \ + "paddw %%mm6,%%mm6\n\t" \ + "movq "OC_C(4)",%%mm4\n\t" \ + "paddw %%mm3,%%mm6\n\t" \ + "movq %%mm3,%%mm5\n\t" \ + "pmulhw %%mm4,%%mm3\n\t" \ + "movq %%mm6,"OC_I(2)"\n\t" \ + "movq %%mm0,%%mm2\n\t" \ + "movq "OC_I(0)",%%mm6\n\t" \ + "pmulhw %%mm4,%%mm0\n\t" \ + "paddw %%mm3,%%mm5\n\t" \ + "paddw %%mm0,%%mm2\n\t" \ + "psubw %%mm1,%%mm5\n\t" \ + "pmulhw %%mm4,%%mm6\n\t" \ + "paddw "OC_I(0)",%%mm6\n\t" \ + "paddw %%mm1,%%mm1\n\t" \ + "movq %%mm6,%%mm4\n\t" \ + "paddw %%mm5,%%mm1\n\t" \ + "psubw %%mm2,%%mm6\n\t" \ + "paddw %%mm2,%%mm2\n\t" \ + "movq "OC_I(1)",%%mm0\n\t" \ + "paddw %%mm6,%%mm2\n\t" \ + "psubw %%mm1,%%mm2\n\t" \ + "nop\n\t" \ + "#end OC_IDCT_BEGIN_10\n\t" \ + +/*25+8=33 cycles.*/ +#define OC_ROW_IDCT_10 \ + "#OC_ROW_IDCT_10\n\t" \ + OC_IDCT_BEGIN_10 \ + /*r3=D'*/ \ + "movq "OC_I(2)",%%mm3\n\t" \ + /*r4=E'=E-G*/ \ + "psubw %%mm7,%%mm4\n\t" \ + /*r1=H'+H'*/ \ + "paddw %%mm1,%%mm1\n\t" \ + /*r7=G+G*/ \ + "paddw %%mm7,%%mm7\n\t" \ + /*r1=R1=A''+H'*/ \ + "paddw %%mm2,%%mm1\n\t" \ + /*r7=G'=E+G*/ \ + "paddw %%mm4,%%mm7\n\t" \ + /*r4=R4=E'-D'*/ \ + "psubw %%mm3,%%mm4\n\t" \ + "paddw %%mm3,%%mm3\n\t" \ + /*r6=R6=F'-B''*/ \ + "psubw %%mm5,%%mm6\n\t" \ + "paddw %%mm5,%%mm5\n\t" \ + /*r3=R3=E'+D'*/ \ + "paddw %%mm4,%%mm3\n\t" \ + /*r5=R5=F'+B''*/ \ + "paddw %%mm6,%%mm5\n\t" \ + /*r7=R7=G'-C'*/ \ + "psubw %%mm0,%%mm7\n\t" \ + "paddw %%mm0,%%mm0\n\t" \ + /*Save R1.*/ \ + "movq %%mm1,"OC_I(1)"\n\t" \ + /*r0=R0=G'+C'*/ \ + "paddw %%mm7,%%mm0\n\t" \ + "#end OC_ROW_IDCT_10\n\t" \ + +/*25+19=44 cycles'*/ +#define OC_COLUMN_IDCT_10 \ + "#OC_COLUMN_IDCT_10\n\t" \ + OC_IDCT_BEGIN_10 \ + "paddw "OC_8",%%mm2\n\t" \ + /*r1=H'+H'*/ \ + "paddw %%mm1,%%mm1\n\t" \ + /*r1=R1=A''+H'*/ \ + "paddw %%mm2,%%mm1\n\t" \ + /*r2=NR2*/ \ + "psraw $4,%%mm2\n\t" \ + /*r4=E'=E-G*/ \ + "psubw %%mm7,%%mm4\n\t" \ + /*r1=NR1*/ \ + "psraw $4,%%mm1\n\t" \ + /*r3=D'*/ \ + "movq "OC_I(2)",%%mm3\n\t" \ + /*r7=G+G*/ \ + "paddw %%mm7,%%mm7\n\t" \ + /*Store NR2 at I(2).*/ \ + "movq %%mm2,"OC_I(2)"\n\t" \ + /*r7=G'=E+G*/ \ + "paddw %%mm4,%%mm7\n\t" \ + /*Store NR1 at I(1).*/ \ + "movq %%mm1,"OC_I(1)"\n\t" \ + /*r4=R4=E'-D'*/ \ + "psubw %%mm3,%%mm4\n\t" \ + "paddw "OC_8",%%mm4\n\t" \ + /*r3=D'+D'*/ \ + "paddw %%mm3,%%mm3\n\t" \ + /*r3=R3=E'+D'*/ \ + "paddw %%mm4,%%mm3\n\t" \ + /*r4=NR4*/ \ + "psraw $4,%%mm4\n\t" \ + /*r6=R6=F'-B''*/ \ + "psubw %%mm5,%%mm6\n\t" \ + /*r3=NR3*/ \ + "psraw $4,%%mm3\n\t" \ + "paddw "OC_8",%%mm6\n\t" \ + /*r5=B''+B''*/ \ + "paddw %%mm5,%%mm5\n\t" \ + /*r5=R5=F'+B''*/ \ + "paddw %%mm6,%%mm5\n\t" \ + /*r6=NR6*/ \ + "psraw $4,%%mm6\n\t" \ + /*Store NR4 at J(4).*/ \ + "movq %%mm4,"OC_J(4)"\n\t" \ + /*r5=NR5*/ \ + "psraw $4,%%mm5\n\t" \ + /*Store NR3 at I(3).*/ \ + "movq %%mm3,"OC_I(3)"\n\t" \ + /*r7=R7=G'-C'*/ \ + "psubw %%mm0,%%mm7\n\t" \ + "paddw "OC_8",%%mm7\n\t" \ + /*r0=C'+C'*/ \ + "paddw %%mm0,%%mm0\n\t" \ + /*r0=R0=G'+C'*/ \ + "paddw %%mm7,%%mm0\n\t" \ + /*r7=NR7*/ \ + "psraw $4,%%mm7\n\t" \ + /*Store NR6 at J(6).*/ \ + "movq %%mm6,"OC_J(6)"\n\t" \ + /*r0=NR0*/ \ + "psraw $4,%%mm0\n\t" \ + /*Store NR5 at J(5).*/ \ + "movq %%mm5,"OC_J(5)"\n\t" \ + /*Store NR7 at J(7).*/ \ + "movq %%mm7,"OC_J(7)"\n\t" \ + /*Store NR0 at I(0).*/ \ + "movq %%mm0,"OC_I(0)"\n\t" \ + "#end OC_COLUMN_IDCT_10\n\t" \ + +void oc_idct8x8_10_mmx(ogg_int16_t _y[64]){ + __asm__ __volatile__( +#define OC_I(_k) OC_M2STR((_k*16))"(%[y])" +#define OC_J(_k) OC_M2STR(((_k-4)*16)+8)"(%[y])" + /*Done with dequant, descramble, and partial transpose. + Now do the iDCT itself.*/ + OC_ROW_IDCT_10 + OC_TRANSPOSE +#undef OC_I +#undef OC_J +#define OC_I(_k) OC_M2STR((_k*16))"(%[y])" +#define OC_J(_k) OC_I(_k) + OC_COLUMN_IDCT_10 +#undef OC_I +#undef OC_J +#define OC_I(_k) OC_M2STR((_k*16)+8)"(%[y])" +#define OC_J(_k) OC_I(_k) + OC_COLUMN_IDCT_10 +#undef OC_I +#undef OC_J + "emms\n\t" + : + :[y]"r"(_y),[c]"r"(OC_IDCT_CONSTS) + ); +} +#endif diff --git a/Engine/lib/libtheora/lib/dec/x86/mmxstate.c b/Engine/lib/libtheora/lib/dec/x86/mmxstate.c new file mode 100644 index 000000000..175364672 --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/x86/mmxstate.c @@ -0,0 +1,653 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: mmxstate.c 15400 2008-10-15 12:10:58Z tterribe $ + + ********************************************************************/ + +/*MMX acceleration of complete fragment reconstruction algorithm. + Originally written by Rudolf Marek.*/ +#include "x86int.h" +#include "../../internal.h" +#include + +#if defined(USE_ASM) + +static const __attribute__((aligned(8),used)) int OC_FZIG_ZAGMMX[64]={ + 0, 8, 1, 2, 9,16,24,17, + 10, 3,32,11,18,25, 4,12, + 5,26,19,40,33,34,41,48, + 27, 6,13,20,28,21,14, 7, + 56,49,42,35,43,50,57,36, + 15,22,29,30,23,44,37,58, + 51,59,38,45,52,31,60,53, + 46,39,47,54,61,62,55,63 +}; + + + +void oc_state_frag_recon_mmx(oc_theora_state *_state,oc_fragment *_frag, + int _pli,ogg_int16_t _dct_coeffs[128],int _last_zzi,int _ncoefs, + ogg_uint16_t _dc_iquant,const ogg_uint16_t _ac_iquant[64]){ + ogg_int16_t __attribute__((aligned(8))) res_buf[64]; + int dst_framei; + int dst_ystride; + int zzi; + /*_last_zzi is subtly different from an actual count of the number of + coefficients we decoded for this block. + It contains the value of zzi BEFORE the final token in the block was + decoded. + In most cases this is an EOB token (the continuation of an EOB run from a + previous block counts), and so this is the same as the coefficient count. + However, in the case that the last token was NOT an EOB token, but filled + the block up with exactly 64 coefficients, _last_zzi will be less than 64. + Provided the last token was not a pure zero run, the minimum value it can + be is 46, and so that doesn't affect any of the cases in this routine. + However, if the last token WAS a pure zero run of length 63, then _last_zzi + will be 1 while the number of coefficients decoded is 64. + Thus, we will trigger the following special case, where the real + coefficient count would not. + Note also that a zero run of length 64 will give _last_zzi a value of 0, + but we still process the DC coefficient, which might have a non-zero value + due to DC prediction. + Although convoluted, this is arguably the correct behavior: it allows us to + dequantize fewer coefficients and use a smaller transform when the block + ends with a long zero run instead of a normal EOB token. + It could be smarter... multiple separate zero runs at the end of a block + will fool it, but an encoder that generates these really deserves what it + gets. + Needless to say we inherited this approach from VP3.*/ + /*Special case only having a DC component.*/ + if(_last_zzi<2){ + ogg_uint16_t p; + /*Why is the iquant product rounded in this case and no others? + Who knows.*/ + p=(ogg_int16_t)((ogg_int32_t)_frag->dc*_dc_iquant+15>>5); + /*Fill res_buf with p.*/ + __asm__ __volatile__( + /*mm0=0000 0000 0000 AAAA*/ + "movd %[p],%%mm0\n\t" + /*mm1=0000 0000 0000 AAAA*/ + "movd %[p],%%mm1\n\t" + /*mm0=0000 0000 AAAA 0000*/ + "pslld $16,%%mm0\n\t" + /*mm0=0000 0000 AAAA AAAA*/ + "por %%mm1,%%mm0\n\t" + /*mm0=AAAA AAAA AAAA AAAA*/ + "punpcklwd %%mm0,%%mm0\n\t" + "movq %%mm0,(%[res_buf])\n\t" + "movq %%mm0,8(%[res_buf])\n\t" + "movq %%mm0,16(%[res_buf])\n\t" + "movq %%mm0,24(%[res_buf])\n\t" + "movq %%mm0,32(%[res_buf])\n\t" + "movq %%mm0,40(%[res_buf])\n\t" + "movq %%mm0,48(%[res_buf])\n\t" + "movq %%mm0,56(%[res_buf])\n\t" + "movq %%mm0,64(%[res_buf])\n\t" + "movq %%mm0,72(%[res_buf])\n\t" + "movq %%mm0,80(%[res_buf])\n\t" + "movq %%mm0,88(%[res_buf])\n\t" + "movq %%mm0,96(%[res_buf])\n\t" + "movq %%mm0,104(%[res_buf])\n\t" + "movq %%mm0,112(%[res_buf])\n\t" + "movq %%mm0,120(%[res_buf])\n\t" + : + :[res_buf]"r"(res_buf),[p]"r"((unsigned)p) + :"memory" + ); + } + else{ + /*Then, fill in the remainder of the coefficients with 0's, and perform + the iDCT.*/ + /*First zero the buffer.*/ + /*On K7, etc., this could be replaced with movntq and sfence.*/ + __asm__ __volatile__( + "pxor %%mm0,%%mm0\n\t" + "movq %%mm0,(%[res_buf])\n\t" + "movq %%mm0,8(%[res_buf])\n\t" + "movq %%mm0,16(%[res_buf])\n\t" + "movq %%mm0,24(%[res_buf])\n\t" + "movq %%mm0,32(%[res_buf])\n\t" + "movq %%mm0,40(%[res_buf])\n\t" + "movq %%mm0,48(%[res_buf])\n\t" + "movq %%mm0,56(%[res_buf])\n\t" + "movq %%mm0,64(%[res_buf])\n\t" + "movq %%mm0,72(%[res_buf])\n\t" + "movq %%mm0,80(%[res_buf])\n\t" + "movq %%mm0,88(%[res_buf])\n\t" + "movq %%mm0,96(%[res_buf])\n\t" + "movq %%mm0,104(%[res_buf])\n\t" + "movq %%mm0,112(%[res_buf])\n\t" + "movq %%mm0,120(%[res_buf])\n\t" + : + :[res_buf]"r"(res_buf) + :"memory" + ); + res_buf[0]=(ogg_int16_t)((ogg_int32_t)_frag->dc*_dc_iquant); + /*This is planned to be rewritten in MMX.*/ + for(zzi=1;zzi<_ncoefs;zzi++){ + int ci; + ci=OC_FZIG_ZAG[zzi]; + res_buf[OC_FZIG_ZAGMMX[zzi]]=(ogg_int16_t)((ogg_int32_t)_dct_coeffs[zzi]* + _ac_iquant[ci]); + } + if(_last_zzi<10)oc_idct8x8_10_mmx(res_buf); + else oc_idct8x8_mmx(res_buf); + } + /*Fill in the target buffer.*/ + dst_framei=_state->ref_frame_idx[OC_FRAME_SELF]; + dst_ystride=_state->ref_frame_bufs[dst_framei][_pli].stride; + /*For now ystride values in all ref frames assumed to be equal.*/ + if(_frag->mbmode==OC_MODE_INTRA){ + oc_frag_recon_intra_mmx(_frag->buffer[dst_framei],dst_ystride,res_buf); + } + else{ + int ref_framei; + int ref_ystride; + int mvoffsets[2]; + ref_framei=_state->ref_frame_idx[OC_FRAME_FOR_MODE[_frag->mbmode]]; + ref_ystride=_state->ref_frame_bufs[ref_framei][_pli].stride; + if(oc_state_get_mv_offsets(_state,mvoffsets,_frag->mv[0],_frag->mv[1], + ref_ystride,_pli)>1){ + oc_frag_recon_inter2_mmx(_frag->buffer[dst_framei],dst_ystride, + _frag->buffer[ref_framei]+mvoffsets[0],ref_ystride, + _frag->buffer[ref_framei]+mvoffsets[1],ref_ystride,res_buf); + } + else{ + oc_frag_recon_inter_mmx(_frag->buffer[dst_framei],dst_ystride, + _frag->buffer[ref_framei]+mvoffsets[0],ref_ystride,res_buf); + } + } + oc_restore_fpu(_state); +} + +/*Copies the fragments specified by the lists of fragment indices from one + frame to another. + _fragis: A pointer to a list of fragment indices. + _nfragis: The number of fragment indices to copy. + _dst_frame: The reference frame to copy to. + _src_frame: The reference frame to copy from. + _pli: The color plane the fragments lie in.*/ +void oc_state_frag_copy_mmx(const oc_theora_state *_state,const int *_fragis, + int _nfragis,int _dst_frame,int _src_frame,int _pli){ + const int *fragi; + const int *fragi_end; + int dst_framei; + ptrdiff_t dst_ystride; + int src_framei; + ptrdiff_t src_ystride; + dst_framei=_state->ref_frame_idx[_dst_frame]; + src_framei=_state->ref_frame_idx[_src_frame]; + dst_ystride=_state->ref_frame_bufs[dst_framei][_pli].stride; + src_ystride=_state->ref_frame_bufs[src_framei][_pli].stride; + fragi_end=_fragis+_nfragis; + for(fragi=_fragis;fragifrags+*fragi; + dst=frag->buffer[dst_framei]; + src=frag->buffer[src_framei]; + __asm__ __volatile__( + /*src+0*src_ystride*/ + "movq (%[src]),%%mm0\n\t" + /*s=src_ystride*3*/ + "lea (%[src_ystride],%[src_ystride],2),%[s]\n\t" + /*src+1*src_ystride*/ + "movq (%[src],%[src_ystride]),%%mm1\n\t" + /*src+2*src_ystride*/ + "movq (%[src],%[src_ystride],2),%%mm2\n\t" + /*src+3*src_ystride*/ + "movq (%[src],%[s]),%%mm3\n\t" + /*dst+0*dst_ystride*/ + "movq %%mm0,(%[dst])\n\t" + /*s=dst_ystride*3*/ + "lea (%[dst_ystride],%[dst_ystride],2),%[s]\n\t" + /*dst+1*dst_ystride*/ + "movq %%mm1,(%[dst],%[dst_ystride])\n\t" + /*Pointer to next 4.*/ + "lea (%[src],%[src_ystride],4),%[src]\n\t" + /*dst+2*dst_ystride*/ + "movq %%mm2,(%[dst],%[dst_ystride],2)\n\t" + /*dst+3*dst_ystride*/ + "movq %%mm3,(%[dst],%[s])\n\t" + /*Pointer to next 4.*/ + "lea (%[dst],%[dst_ystride],4),%[dst]\n\t" + /*src+0*src_ystride*/ + "movq (%[src]),%%mm0\n\t" + /*s=src_ystride*3*/ + "lea (%[src_ystride],%[src_ystride],2),%[s]\n\t" + /*src+1*src_ystride*/ + "movq (%[src],%[src_ystride]),%%mm1\n\t" + /*src+2*src_ystride*/ + "movq (%[src],%[src_ystride],2),%%mm2\n\t" + /*src+3*src_ystride*/ + "movq (%[src],%[s]),%%mm3\n\t" + /*dst+0*dst_ystride*/ + "movq %%mm0,(%[dst])\n\t" + /*s=dst_ystride*3*/ + "lea (%[dst_ystride],%[dst_ystride],2),%[s]\n\t" + /*dst+1*dst_ystride*/ + "movq %%mm1,(%[dst],%[dst_ystride])\n\t" + /*dst+2*dst_ystride*/ + "movq %%mm2,(%[dst],%[dst_ystride],2)\n\t" + /*dst+3*dst_ystride*/ + "movq %%mm3,(%[dst],%[s])\n\t" + :[s]"=&r"(s) + :[dst]"r"(dst),[src]"r"(src),[dst_ystride]"r"(dst_ystride), + [src_ystride]"r"(src_ystride) + :"memory" + ); + } + /*This needs to be removed when decode specific functions are implemented:*/ + __asm__ __volatile__("emms\n\t"); +} + +static void loop_filter_v(unsigned char *_pix,int _ystride, + const ogg_int16_t *_ll){ + ptrdiff_t s; + _pix-=_ystride*2; + __asm__ __volatile__( + /*mm0=0*/ + "pxor %%mm0,%%mm0\n\t" + /*s=_ystride*3*/ + "lea (%[ystride],%[ystride],2),%[s]\n\t" + /*mm7=_pix[0...8]*/ + "movq (%[pix]),%%mm7\n\t" + /*mm4=_pix[0...8+_ystride*3]*/ + "movq (%[pix],%[s]),%%mm4\n\t" + /*mm6=_pix[0...8]*/ + "movq %%mm7,%%mm6\n\t" + /*Expand unsigned _pix[0...3] to 16 bits.*/ + "punpcklbw %%mm0,%%mm6\n\t" + "movq %%mm4,%%mm5\n\t" + /*Expand unsigned _pix[4...8] to 16 bits.*/ + "punpckhbw %%mm0,%%mm7\n\t" + /*Expand other arrays too.*/ + "punpcklbw %%mm0,%%mm4\n\t" + "punpckhbw %%mm0,%%mm5\n\t" + /*mm7:mm6=_p[0...8]-_p[0...8+_ystride*3]:*/ + "psubw %%mm4,%%mm6\n\t" + "psubw %%mm5,%%mm7\n\t" + /*mm5=mm4=_pix[0...8+_ystride]*/ + "movq (%[pix],%[ystride]),%%mm4\n\t" + /*mm1=mm3=mm2=_pix[0..8]+_ystride*2]*/ + "movq (%[pix],%[ystride],2),%%mm2\n\t" + "movq %%mm4,%%mm5\n\t" + "movq %%mm2,%%mm3\n\t" + "movq %%mm2,%%mm1\n\t" + /*Expand these arrays.*/ + "punpckhbw %%mm0,%%mm5\n\t" + "punpcklbw %%mm0,%%mm4\n\t" + "punpckhbw %%mm0,%%mm3\n\t" + "punpcklbw %%mm0,%%mm2\n\t" + /*mm0=3 3 3 3 + mm3:mm2=_pix[0...8+_ystride*2]-_pix[0...8+_ystride]*/ + "pcmpeqw %%mm0,%%mm0\n\t" + "psubw %%mm5,%%mm3\n\t" + "psrlw $14,%%mm0\n\t" + "psubw %%mm4,%%mm2\n\t" + /*Scale by 3.*/ + "pmullw %%mm0,%%mm3\n\t" + "pmullw %%mm0,%%mm2\n\t" + /*mm0=4 4 4 4 + f=mm3:mm2==_pix[0...8]-_pix[0...8+_ystride*3]+ + 3*(_pix[0...8+_ystride*2]-_pix[0...8+_ystride])*/ + "psrlw $1,%%mm0\n\t" + "paddw %%mm7,%%mm3\n\t" + "psllw $2,%%mm0\n\t" + "paddw %%mm6,%%mm2\n\t" + /*Add 4.*/ + "paddw %%mm0,%%mm3\n\t" + "paddw %%mm0,%%mm2\n\t" + /*"Divide" by 8.*/ + "psraw $3,%%mm3\n\t" + "psraw $3,%%mm2\n\t" + /*Now compute lflim of mm3:mm2 cf. Section 7.10 of the sepc.*/ + /*Free up mm5.*/ + "packuswb %%mm5,%%mm4\n\t" + /*mm0=L L L L*/ + "movq (%[ll]),%%mm0\n\t" + /*if(R_i<-2L||R_i>2L)R_i=0:*/ + "movq %%mm2,%%mm5\n\t" + "pxor %%mm6,%%mm6\n\t" + "movq %%mm0,%%mm7\n\t" + "psubw %%mm0,%%mm6\n\t" + "psllw $1,%%mm7\n\t" + "psllw $1,%%mm6\n\t" + /*mm2==R_3 R_2 R_1 R_0*/ + /*mm5==R_3 R_2 R_1 R_0*/ + /*mm6==-2L -2L -2L -2L*/ + /*mm7==2L 2L 2L 2L*/ + "pcmpgtw %%mm2,%%mm7\n\t" + "pcmpgtw %%mm6,%%mm5\n\t" + "pand %%mm7,%%mm2\n\t" + "movq %%mm0,%%mm7\n\t" + "pand %%mm5,%%mm2\n\t" + "psllw $1,%%mm7\n\t" + "movq %%mm3,%%mm5\n\t" + /*mm3==R_7 R_6 R_5 R_4*/ + /*mm5==R_7 R_6 R_5 R_4*/ + /*mm6==-2L -2L -2L -2L*/ + /*mm7==2L 2L 2L 2L*/ + "pcmpgtw %%mm3,%%mm7\n\t" + "pcmpgtw %%mm6,%%mm5\n\t" + "pand %%mm7,%%mm3\n\t" + "movq %%mm0,%%mm7\n\t" + "pand %%mm5,%%mm3\n\t" + /*if(R_i<-L)R_i'=R_i+2L; + if(R_i>L)R_i'=R_i-2L; + if(R_i<-L||R_i>L)R_i=-R_i':*/ + "psraw $1,%%mm6\n\t" + "movq %%mm2,%%mm5\n\t" + "psllw $1,%%mm7\n\t" + /*mm2==R_3 R_2 R_1 R_0*/ + /*mm5==R_3 R_2 R_1 R_0*/ + /*mm6==-L -L -L -L*/ + /*mm0==L L L L*/ + /*mm5=R_i>L?FF:00*/ + "pcmpgtw %%mm0,%%mm5\n\t" + /*mm6=-L>R_i?FF:00*/ + "pcmpgtw %%mm2,%%mm6\n\t" + /*mm7=R_i>L?2L:0*/ + "pand %%mm5,%%mm7\n\t" + /*mm2=R_i>L?R_i-2L:R_i*/ + "psubw %%mm7,%%mm2\n\t" + "movq %%mm0,%%mm7\n\t" + /*mm5=-L>R_i||R_i>L*/ + "por %%mm6,%%mm5\n\t" + "psllw $1,%%mm7\n\t" + /*mm7=-L>R_i?2L:0*/ + "pand %%mm6,%%mm7\n\t" + "pxor %%mm6,%%mm6\n\t" + /*mm2=-L>R_i?R_i+2L:R_i*/ + "paddw %%mm7,%%mm2\n\t" + "psubw %%mm0,%%mm6\n\t" + /*mm5=-L>R_i||R_i>L?-R_i':0*/ + "pand %%mm2,%%mm5\n\t" + "movq %%mm0,%%mm7\n\t" + /*mm2=-L>R_i||R_i>L?0:R_i*/ + "psubw %%mm5,%%mm2\n\t" + "psllw $1,%%mm7\n\t" + /*mm2=-L>R_i||R_i>L?-R_i':R_i*/ + "psubw %%mm5,%%mm2\n\t" + "movq %%mm3,%%mm5\n\t" + /*mm3==R_7 R_6 R_5 R_4*/ + /*mm5==R_7 R_6 R_5 R_4*/ + /*mm6==-L -L -L -L*/ + /*mm0==L L L L*/ + /*mm6=-L>R_i?FF:00*/ + "pcmpgtw %%mm3,%%mm6\n\t" + /*mm5=R_i>L?FF:00*/ + "pcmpgtw %%mm0,%%mm5\n\t" + /*mm7=R_i>L?2L:0*/ + "pand %%mm5,%%mm7\n\t" + /*mm2=R_i>L?R_i-2L:R_i*/ + "psubw %%mm7,%%mm3\n\t" + "psllw $1,%%mm0\n\t" + /*mm5=-L>R_i||R_i>L*/ + "por %%mm6,%%mm5\n\t" + /*mm0=-L>R_i?2L:0*/ + "pand %%mm6,%%mm0\n\t" + /*mm3=-L>R_i?R_i+2L:R_i*/ + "paddw %%mm0,%%mm3\n\t" + /*mm5=-L>R_i||R_i>L?-R_i':0*/ + "pand %%mm3,%%mm5\n\t" + /*mm2=-L>R_i||R_i>L?0:R_i*/ + "psubw %%mm5,%%mm3\n\t" + /*mm2=-L>R_i||R_i>L?-R_i':R_i*/ + "psubw %%mm5,%%mm3\n\t" + /*Unfortunately, there's no unsigned byte+signed byte with unsigned + saturation op code, so we have to promote things back 16 bits.*/ + "pxor %%mm0,%%mm0\n\t" + "movq %%mm4,%%mm5\n\t" + "punpcklbw %%mm0,%%mm4\n\t" + "punpckhbw %%mm0,%%mm5\n\t" + "movq %%mm1,%%mm6\n\t" + "punpcklbw %%mm0,%%mm1\n\t" + "punpckhbw %%mm0,%%mm6\n\t" + /*_pix[0...8+_ystride]+=R_i*/ + "paddw %%mm2,%%mm4\n\t" + "paddw %%mm3,%%mm5\n\t" + /*_pix[0...8+_ystride*2]-=R_i*/ + "psubw %%mm2,%%mm1\n\t" + "psubw %%mm3,%%mm6\n\t" + "packuswb %%mm5,%%mm4\n\t" + "packuswb %%mm6,%%mm1\n\t" + /*Write it back out.*/ + "movq %%mm4,(%[pix],%[ystride])\n\t" + "movq %%mm1,(%[pix],%[ystride],2)\n\t" + :[s]"=&r"(s) + :[pix]"r"(_pix),[ystride]"r"((ptrdiff_t)_ystride),[ll]"r"(_ll) + :"memory" + ); +} + +/*This code implements the bulk of loop_filter_h(). + Data are striped p0 p1 p2 p3 ... p0 p1 p2 p3 ..., so in order to load all + four p0's to one register we must transpose the values in four mmx regs. + When half is done we repeat this for the rest.*/ +static void loop_filter_h4(unsigned char *_pix,ptrdiff_t _ystride, + const ogg_int16_t *_ll){ + ptrdiff_t s; + /*d doesn't technically need to be 64-bit on x86-64, but making it so will + help avoid partial register stalls.*/ + ptrdiff_t d; + __asm__ __volatile__( + /*x x x x 3 2 1 0*/ + "movd (%[pix]),%%mm0\n\t" + /*s=_ystride*3*/ + "lea (%[ystride],%[ystride],2),%[s]\n\t" + /*x x x x 7 6 5 4*/ + "movd (%[pix],%[ystride]),%%mm1\n\t" + /*x x x x B A 9 8*/ + "movd (%[pix],%[ystride],2),%%mm2\n\t" + /*x x x x F E D C*/ + "movd (%[pix],%[s]),%%mm3\n\t" + /*mm0=7 3 6 2 5 1 4 0*/ + "punpcklbw %%mm1,%%mm0\n\t" + /*mm2=F B E A D 9 C 8*/ + "punpcklbw %%mm3,%%mm2\n\t" + /*mm1=7 3 6 2 5 1 4 0*/ + "movq %%mm0,%%mm1\n\t" + /*mm0=F B 7 3 E A 6 2*/ + "punpckhwd %%mm2,%%mm0\n\t" + /*mm1=D 9 5 1 C 8 4 0*/ + "punpcklwd %%mm2,%%mm1\n\t" + "pxor %%mm7,%%mm7\n\t" + /*mm5=D 9 5 1 C 8 4 0*/ + "movq %%mm1,%%mm5\n\t" + /*mm1=x C x 8 x 4 x 0==pix[0]*/ + "punpcklbw %%mm7,%%mm1\n\t" + /*mm5=x D x 9 x 5 x 1==pix[1]*/ + "punpckhbw %%mm7,%%mm5\n\t" + /*mm3=F B 7 3 E A 6 2*/ + "movq %%mm0,%%mm3\n\t" + /*mm0=x E x A x 6 x 2==pix[2]*/ + "punpcklbw %%mm7,%%mm0\n\t" + /*mm3=x F x B x 7 x 3==pix[3]*/ + "punpckhbw %%mm7,%%mm3\n\t" + /*mm1=mm1-mm3==pix[0]-pix[3]*/ + "psubw %%mm3,%%mm1\n\t" + /*Save a copy of pix[2] for later.*/ + "movq %%mm0,%%mm4\n\t" + /*mm2=3 3 3 3 + mm0=mm0-mm5==pix[2]-pix[1]*/ + "pcmpeqw %%mm2,%%mm2\n\t" + "psubw %%mm5,%%mm0\n\t" + "psrlw $14,%%mm2\n\t" + /*Scale by 3.*/ + "pmullw %%mm2,%%mm0\n\t" + /*mm2=4 4 4 4 + f=mm1==_pix[0]-_pix[3]+ 3*(_pix[2]-_pix[1])*/ + "psrlw $1,%%mm2\n\t" + "paddw %%mm1,%%mm0\n\t" + "psllw $2,%%mm2\n\t" + /*Add 4.*/ + "paddw %%mm2,%%mm0\n\t" + /*"Divide" by 8, producing the residuals R_i.*/ + "psraw $3,%%mm0\n\t" + /*Now compute lflim of mm0 cf. Section 7.10 of the sepc.*/ + /*mm6=L L L L*/ + "movq (%[ll]),%%mm6\n\t" + /*if(R_i<-2L||R_i>2L)R_i=0:*/ + "movq %%mm0,%%mm1\n\t" + "pxor %%mm2,%%mm2\n\t" + "movq %%mm6,%%mm3\n\t" + "psubw %%mm6,%%mm2\n\t" + "psllw $1,%%mm3\n\t" + "psllw $1,%%mm2\n\t" + /*mm0==R_3 R_2 R_1 R_0*/ + /*mm1==R_3 R_2 R_1 R_0*/ + /*mm2==-2L -2L -2L -2L*/ + /*mm3==2L 2L 2L 2L*/ + "pcmpgtw %%mm0,%%mm3\n\t" + "pcmpgtw %%mm2,%%mm1\n\t" + "pand %%mm3,%%mm0\n\t" + "pand %%mm1,%%mm0\n\t" + /*if(R_i<-L)R_i'=R_i+2L; + if(R_i>L)R_i'=R_i-2L; + if(R_i<-L||R_i>L)R_i=-R_i':*/ + "psraw $1,%%mm2\n\t" + "movq %%mm0,%%mm1\n\t" + "movq %%mm6,%%mm3\n\t" + /*mm0==R_3 R_2 R_1 R_0*/ + /*mm1==R_3 R_2 R_1 R_0*/ + /*mm2==-L -L -L -L*/ + /*mm6==L L L L*/ + /*mm2=-L>R_i?FF:00*/ + "pcmpgtw %%mm0,%%mm2\n\t" + /*mm1=R_i>L?FF:00*/ + "pcmpgtw %%mm6,%%mm1\n\t" + /*mm3=2L 2L 2L 2L*/ + "psllw $1,%%mm3\n\t" + /*mm6=2L 2L 2L 2L*/ + "psllw $1,%%mm6\n\t" + /*mm3=R_i>L?2L:0*/ + "pand %%mm1,%%mm3\n\t" + /*mm6=-L>R_i?2L:0*/ + "pand %%mm2,%%mm6\n\t" + /*mm0=R_i>L?R_i-2L:R_i*/ + "psubw %%mm3,%%mm0\n\t" + /*mm1=-L>R_i||R_i>L*/ + "por %%mm2,%%mm1\n\t" + /*mm0=-L>R_i?R_i+2L:R_i*/ + "paddw %%mm6,%%mm0\n\t" + /*mm1=-L>R_i||R_i>L?R_i':0*/ + "pand %%mm0,%%mm1\n\t" + /*mm0=-L>R_i||R_i>L?0:R_i*/ + "psubw %%mm1,%%mm0\n\t" + /*mm0=-L>R_i||R_i>L?-R_i':R_i*/ + "psubw %%mm1,%%mm0\n\t" + /*_pix[1]+=R_i;*/ + "paddw %%mm0,%%mm5\n\t" + /*_pix[2]-=R_i;*/ + "psubw %%mm0,%%mm4\n\t" + /*mm5=x x x x D 9 5 1*/ + "packuswb %%mm7,%%mm5\n\t" + /*mm4=x x x x E A 6 2*/ + "packuswb %%mm7,%%mm4\n\t" + /*mm5=E D A 9 6 5 2 1*/ + "punpcklbw %%mm4,%%mm5\n\t" + /*d=6 5 2 1*/ + "movd %%mm5,%[d]\n\t" + "movw %w[d],1(%[pix])\n\t" + /*Why is there such a big stall here?*/ + "psrlq $32,%%mm5\n\t" + "shr $16,%[d]\n\t" + "movw %w[d],1(%[pix],%[ystride])\n\t" + /*d=E D A 9*/ + "movd %%mm5,%[d]\n\t" + "movw %w[d],1(%[pix],%[ystride],2)\n\t" + "shr $16,%[d]\n\t" + "movw %w[d],1(%[pix],%[s])\n\t" + :[s]"=&r"(s),[d]"=&r"(d), + [pix]"+r"(_pix),[ystride]"+r"(_ystride),[ll]"+r"(_ll) + : + :"memory" + ); +} + +static void loop_filter_h(unsigned char *_pix,int _ystride, + const ogg_int16_t *_ll){ + _pix-=2; + loop_filter_h4(_pix,_ystride,_ll); + loop_filter_h4(_pix+(_ystride<<2),_ystride,_ll); +} + +/*We copy the whole function because the MMX routines will be inlined 4 times, + and we can do just a single emms call at the end this way. + We also do not use the _bv lookup table, instead computing the values that + would lie in it on the fly.*/ + +/*Apply the loop filter to a given set of fragment rows in the given plane. + The filter may be run on the bottom edge, affecting pixels in the next row of + fragments, so this row also needs to be available. + _bv: The bounding values array. + _refi: The index of the frame buffer to filter. + _pli: The color plane to filter. + _fragy0: The Y coordinate of the first fragment row to filter. + _fragy_end: The Y coordinate of the fragment row to stop filtering at.*/ +void oc_state_loop_filter_frag_rows_mmx(oc_theora_state *_state,int *_bv, + int _refi,int _pli,int _fragy0,int _fragy_end){ + ogg_int16_t __attribute__((aligned(8))) ll[4]; + th_img_plane *iplane; + oc_fragment_plane *fplane; + oc_fragment *frag_top; + oc_fragment *frag0; + oc_fragment *frag; + oc_fragment *frag_end; + oc_fragment *frag0_end; + oc_fragment *frag_bot; + ll[0]=ll[1]=ll[2]=ll[3]= + (ogg_int16_t)_state->loop_filter_limits[_state->qis[0]]; + iplane=_state->ref_frame_bufs[_refi]+_pli; + fplane=_state->fplanes+_pli; + /*The following loops are constructed somewhat non-intuitively on purpose. + The main idea is: if a block boundary has at least one coded fragment on + it, the filter is applied to it. + However, the order that the filters are applied in matters, and VP3 chose + the somewhat strange ordering used below.*/ + frag_top=_state->frags+fplane->froffset; + frag0=frag_top+_fragy0*fplane->nhfrags; + frag0_end=frag0+(_fragy_end-_fragy0)*fplane->nhfrags; + frag_bot=_state->frags+fplane->froffset+fplane->nfrags; + while(frag0nhfrags; + while(fragcoded){ + if(frag>frag0){ + loop_filter_h(frag->buffer[_refi],iplane->stride,ll); + } + if(frag0>frag_top){ + loop_filter_v(frag->buffer[_refi],iplane->stride,ll); + } + if(frag+1coded){ + loop_filter_h(frag->buffer[_refi]+8,iplane->stride,ll); + } + if(frag+fplane->nhfragsnhfrags)->coded){ + loop_filter_v((frag+fplane->nhfrags)->buffer[_refi], + iplane->stride,ll); + } + } + frag++; + } + frag0+=fplane->nhfrags; + } + /*This needs to be removed when decode specific functions are implemented:*/ + __asm__ __volatile__("emms\n\t"); +} + +#endif diff --git a/Engine/lib/libtheora/lib/dec/x86/x86int.h b/Engine/lib/libtheora/lib/dec/x86/x86int.h new file mode 100644 index 000000000..05f9c57c1 --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/x86/x86int.h @@ -0,0 +1,42 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: x86int.h 15400 2008-10-15 12:10:58Z tterribe $ + + ********************************************************************/ + +#if !defined(_x86_x86int_H) +# define _x86_x86int_H (1) +# include "../../internal.h" + +void oc_state_vtable_init_x86(oc_theora_state *_state); + +void oc_frag_recon_intra_mmx(unsigned char *_dst,int _dst_ystride, + const ogg_int16_t *_residue); +void oc_frag_recon_inter_mmx(unsigned char *_dst,int _dst_ystride, + const unsigned char *_src,int _src_ystride,const ogg_int16_t *_residue); +void oc_frag_recon_inter2_mmx(unsigned char *_dst,int _dst_ystride, + const unsigned char *_src1,int _src1_ystride,const unsigned char *_src2, + int _src2_ystride,const ogg_int16_t *_residue); +void oc_state_frag_copy_mmx(const oc_theora_state *_state,const int *_fragis, + int _nfragis,int _dst_frame,int _src_frame,int _pli); +void oc_state_frag_recon_mmx(oc_theora_state *_state,oc_fragment *_frag, + int _pli,ogg_int16_t _dct_coeffs[128],int _last_zzi,int _ncoefs, + ogg_uint16_t _dc_iquant,const ogg_uint16_t _ac_iquant[64]); +void oc_restore_fpu_mmx(void); +void oc_idct8x8_mmx(ogg_int16_t _y[64]); +void oc_idct8x8_10_mmx(ogg_int16_t _y[64]); +void oc_fill_idct_constants_mmx(void); +void oc_state_loop_filter_frag_rows_mmx(oc_theora_state *_state,int *_bv, + int _refi,int _pli,int _fragy0,int _fragy_end); +#endif diff --git a/Engine/lib/libtheora/lib/dec/x86/x86state.c b/Engine/lib/libtheora/lib/dec/x86/x86state.c new file mode 100644 index 000000000..28a559ba4 --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/x86/x86state.c @@ -0,0 +1,38 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: x86state.c 15427 2008-10-21 02:36:19Z xiphmont $ + + ********************************************************************/ + +#include "x86int.h" + +#if defined(USE_ASM) + +#include "../../cpu.c" + +void oc_state_vtable_init_x86(oc_theora_state *_state){ + _state->cpu_flags=oc_cpu_flags_get(); + if(_state->cpu_flags&OC_CPU_X86_MMX){ + _state->opt_vtable.frag_recon_intra=oc_frag_recon_intra_mmx; + _state->opt_vtable.frag_recon_inter=oc_frag_recon_inter_mmx; + _state->opt_vtable.frag_recon_inter2=oc_frag_recon_inter2_mmx; + _state->opt_vtable.state_frag_copy=oc_state_frag_copy_mmx; + _state->opt_vtable.state_frag_recon=oc_state_frag_recon_mmx; + _state->opt_vtable.state_loop_filter_frag_rows= + oc_state_loop_filter_frag_rows_mmx; + _state->opt_vtable.restore_fpu=oc_restore_fpu_mmx; + } + else oc_state_vtable_init_c(_state); +} +#endif diff --git a/Engine/lib/libtheora/lib/dec/x86_vc/mmxfrag.c b/Engine/lib/libtheora/lib/dec/x86_vc/mmxfrag.c new file mode 100644 index 000000000..e87e0640d --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/x86_vc/mmxfrag.c @@ -0,0 +1,214 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: + + ********************************************************************/ +#include "../../internal.h" + +/* ------------------------------------------------------------------------ + MMX reconstruction fragment routines for Visual Studio. + Tested with VS2005. Should compile for VS2003 and VC6 as well. + + Initial implementation 2007 by Nils Pipenbrinck. + ---------------------------------------------------------------------*/ + +#if defined(USE_ASM) + +void oc_frag_recon_intra_mmx(unsigned char *_dst,int _dst_ystride, + const ogg_int16_t *_residue){ + /* --------------------------------------------------------------------- + This function does the inter reconstruction step with 8 iterations + unrolled. The iteration for each instruction is noted by the #id in the + comments (in case you want to reconstruct it) + --------------------------------------------------------------------- */ + _asm{ + mov edi, [_residue] /* load residue ptr */ + mov eax, 0x00800080 /* generate constant */ + mov ebx, [_dst_ystride] /* load dst-stride */ + mov edx, [_dst] /* load dest pointer */ + + /* unrolled loop begins here */ + + movd mm0, eax /* load constant */ + movq mm1, [edi+ 8*0] /* #1 load low residue */ + movq mm2, [edi+ 8*1] /* #1 load high residue */ + punpckldq mm0, mm0 /* build constant */ + movq mm3, [edi+ 8*2] /* #2 load low residue */ + movq mm4, [edi+ 8*3] /* #2 load high residue */ + movq mm5, [edi+ 8*4] /* #3 load low residue */ + movq mm6, [edi+ 8*5] /* #3 load high residue */ + paddsw mm1, mm0 /* #1 bias low residue */ + paddsw mm2, mm0 /* #1 bias high residue */ + packuswb mm1, mm2 /* #1 pack to byte */ + paddsw mm3, mm0 /* #2 bias low residue */ + paddsw mm4, mm0 /* #2 bias high residue */ + packuswb mm3, mm4 /* #2 pack to byte */ + paddsw mm5, mm0 /* #3 bias low residue */ + paddsw mm6, mm0 /* #3 bias high residue */ + packuswb mm5, mm6 /* #3 pack to byte */ + movq [edx], mm1 /* #1 write row */ + movq [edx + ebx], mm3 /* #2 write row */ + movq [edx + ebx*2], mm5 /* #3 write row */ + movq mm1, [edi+ 8*6] /* #4 load low residue */ + lea ecx, [ebx + ebx*2] /* make dst_ystride * 3 */ + movq mm2, [edi+ 8*7] /* #4 load high residue */ + movq mm3, [edi+ 8*8] /* #5 load low residue */ + lea esi, [ebx*4 + ebx] /* make dst_ystride * 5 */ + movq mm4, [edi+ 8*9] /* #5 load high residue */ + movq mm5, [edi+ 8*10] /* #6 load low residue */ + lea eax, [ecx*2 + ebx] /* make dst_ystride * 7 */ + movq mm6, [edi+ 8*11] /* #6 load high residue */ + paddsw mm1, mm0 /* #4 bias low residue */ + paddsw mm2, mm0 /* #4 bias high residue */ + packuswb mm1, mm2 /* #4 pack to byte */ + paddsw mm3, mm0 /* #5 bias low residue */ + paddsw mm4, mm0 /* #5 bias high residue */ + packuswb mm3, mm4 /* #5 pack to byte */ + paddsw mm5, mm0 /* #6 bias low residue */ + paddsw mm6, mm0 /* #6 bias high residue */ + packuswb mm5, mm6 /* #6 pack to byte */ + movq [edx + ecx], mm1 /* #4 write row */ + movq [edx + ebx*4], mm3 /* #5 write row */ + movq [edx + esi], mm5 /* #6 write row */ + movq mm1, [edi+ 8*12] /* #7 load low residue */ + movq mm2, [edi+ 8*13] /* #7 load high residue */ + movq mm3, [edi+ 8*14] /* #8 load low residue */ + movq mm4, [edi+ 8*15] /* #8 load high residue */ + paddsw mm1, mm0 /* #7 bias low residue */ + paddsw mm2, mm0 /* #7 bias high residue */ + packuswb mm1, mm2 /* #7 pack to byte */ + paddsw mm3, mm0 /* #8 bias low residue */ + paddsw mm4, mm0 /* #8 bias high residue */ + packuswb mm3, mm4 /* #8 pack to byte */ + movq [edx + ecx*2], mm1 /* #7 write row */ + movq [edx + eax], mm3 /* #8 write row */ + } +} + + + +void oc_frag_recon_inter_mmx (unsigned char *_dst, int _dst_ystride, + const unsigned char *_src, int _src_ystride, const ogg_int16_t *_residue){ + /* --------------------------------------------------------------------- + This function does the inter reconstruction step with two iterations + running in parallel to hide some load-latencies and break the dependency + chains. The iteration for each instruction is noted by the #id in the + comments (in case you want to reconstruct it) + --------------------------------------------------------------------- */ + _asm{ + pxor mm0, mm0 /* generate constant 0 */ + mov esi, [_src] + mov edi, [_residue] + mov eax, [_src_ystride] + mov edx, [_dst] + mov ebx, [_dst_ystride] + mov ecx, 4 + + align 16 + +nextchunk: + movq mm3, [esi] /* #1 load source */ + movq mm1, [edi+0] /* #1 load residium low */ + movq mm2, [edi+8] /* #1 load residium high */ + movq mm7, [esi+eax] /* #2 load source */ + movq mm4, mm3 /* #1 get copy of src */ + movq mm5, [edi+16] /* #2 load residium low */ + punpckhbw mm4, mm0 /* #1 expand high source */ + movq mm6, [edi+24] /* #2 load residium high */ + punpcklbw mm3, mm0 /* #1 expand low source */ + paddsw mm4, mm2 /* #1 add residium high */ + movq mm2, mm7 /* #2 get copy of src */ + paddsw mm3, mm1 /* #1 add residium low */ + punpckhbw mm2, mm0 /* #2 expand high source */ + packuswb mm3, mm4 /* #1 final row pixels */ + punpcklbw mm7, mm0 /* #2 expand low source */ + movq [edx], mm3 /* #1 write row */ + paddsw mm2, mm6 /* #2 add residium high */ + add edi, 32 /* residue += 4 */ + paddsw mm7, mm5 /* #2 add residium low */ + sub ecx, 1 /* update loop counter */ + packuswb mm7, mm2 /* #2 final row */ + lea esi, [esi+eax*2] /* src += stride * 2 */ + movq [edx + ebx], mm7 /* #2 write row */ + lea edx, [edx+ebx*2] /* dst += stride * 2 */ + jne nextchunk + } +} + + +void oc_frag_recon_inter2_mmx(unsigned char *_dst, int _dst_ystride, + const unsigned char *_src1, int _src1_ystride, const unsigned char *_src2, + int _src2_ystride,const ogg_int16_t *_residue){ + /* --------------------------------------------------------------------- + This function does the inter2 reconstruction step.The building of the + average is done with a bit-twiddeling trick to avoid excessive register + copy work during byte to word conversion. + + average = (a & b) + (((a ^ b) & 0xfe) >> 1); + + (shown for a single byte; it's done with 8 of them at a time) + + Slightly faster than the obvious method using add and shift, but not + earthshaking improvement either. + + If anyone comes up with a way that produces bit-identical outputs + using the pavgb instruction let me know and I'll do the 3dnow codepath. + --------------------------------------------------------------------- */ + _asm{ + mov eax, 0xfefefefe + mov esi, [_src1] + mov edi, [_src2] + movd mm1, eax + mov ebx, [_residue] + mov edx, [_dst] + mov eax, [_dst_ystride] + punpckldq mm1, mm1 /* replicate lsb32 */ + mov ecx, 8 /* init loop counter */ + pxor mm0, mm0 /* constant zero */ + sub edx, eax /* dst -= dst_stride */ + + align 16 + +nextrow: + movq mm2, [esi] /* load source1 */ + movq mm3, [edi] /* load source2 */ + movq mm5, [ebx + 0] /* load lower residue */ + movq mm6, [ebx + 8] /* load higer residue */ + add esi, _src1_ystride /* src1 += src1_stride */ + add edi, _src2_ystride /* src2 += src1_stride */ + movq mm4, mm2 /* get copy of source1 */ + pand mm2, mm3 /* s1 & s2 (avg part) */ + pxor mm3, mm4 /* s1 ^ s2 (avg part) */ + add ebx, 16 /* residue++ */ + pand mm3, mm1 /* mask out low bits */ + psrlq mm3, 1 /* shift xor avg-part */ + paddd mm3, mm2 /* build final average */ + add edx, eax /* dst += dst_stride */ + movq mm2, mm3 /* get copy of average */ + punpckhbw mm3, mm0 /* average high */ + punpcklbw mm2, mm0 /* average low */ + paddsw mm3, mm6 /* high + residue */ + paddsw mm2, mm5 /* low + residue */ + sub ecx, 1 /* update loop counter */ + packuswb mm2, mm3 /* pack and saturate */ + movq [edx], mm2 /* write row */ + jne nextrow + } +} + +void oc_restore_fpu_mmx(void){ + _asm { emms } +} + +#endif diff --git a/Engine/lib/libtheora/lib/dec/x86_vc/mmxidct.c b/Engine/lib/libtheora/lib/dec/x86_vc/mmxidct.c new file mode 100644 index 000000000..2c171594f --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/x86_vc/mmxidct.c @@ -0,0 +1,1006 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: + + ********************************************************************/ + +/* ------------------------------------------------------------------- + MMX based IDCT for the theora codec. + + Originally written by Rudolf Marek, based on code from On2's VP3. + Converted to Visual Studio inline assembly by Nils Pipenbrinck. + + ---------------------------------------------------------------------*/ +#if defined(USE_ASM) + +#include +#include "../dct.h" +#include "../idct.h" +#include "x86int.h" + +/*A table of constants used by the MMX routines.*/ +static const __declspec(align(16)) ogg_uint16_t + OC_IDCT_CONSTS[(7+1)*4]={ + (ogg_uint16_t)OC_C1S7,(ogg_uint16_t)OC_C1S7, + (ogg_uint16_t)OC_C1S7,(ogg_uint16_t)OC_C1S7, + (ogg_uint16_t)OC_C2S6,(ogg_uint16_t)OC_C2S6, + (ogg_uint16_t)OC_C2S6,(ogg_uint16_t)OC_C2S6, + (ogg_uint16_t)OC_C3S5,(ogg_uint16_t)OC_C3S5, + (ogg_uint16_t)OC_C3S5,(ogg_uint16_t)OC_C3S5, + (ogg_uint16_t)OC_C4S4,(ogg_uint16_t)OC_C4S4, + (ogg_uint16_t)OC_C4S4,(ogg_uint16_t)OC_C4S4, + (ogg_uint16_t)OC_C5S3,(ogg_uint16_t)OC_C5S3, + (ogg_uint16_t)OC_C5S3,(ogg_uint16_t)OC_C5S3, + (ogg_uint16_t)OC_C6S2,(ogg_uint16_t)OC_C6S2, + (ogg_uint16_t)OC_C6S2,(ogg_uint16_t)OC_C6S2, + (ogg_uint16_t)OC_C7S1,(ogg_uint16_t)OC_C7S1, + (ogg_uint16_t)OC_C7S1,(ogg_uint16_t)OC_C7S1, + 8, 8, 8, 8 +}; + + +void oc_idct8x8_10_mmx(ogg_int16_t _y[64]){ + _asm { + mov edx, [_y] + mov eax, offset OC_IDCT_CONSTS + movq mm2, [edx + 30H] + movq mm6, [eax + 10H] + movq mm4, mm2 + movq mm7, [edx + 18H] + pmulhw mm4, mm6 + movq mm1, [eax + 20H] + pmulhw mm6, mm7 + movq mm5, mm1 + pmulhw mm1, mm2 + movq mm3, [edx + 10H] + pmulhw mm5, mm7 + movq mm0, [eax] + paddw mm4, mm2 + paddw mm6, mm7 + paddw mm2, mm1 + movq mm1, [edx + 38H] + paddw mm7, mm5 + movq mm5, mm0 + pmulhw mm0, mm3 + paddw mm4, mm7 + pmulhw mm5, mm1 + movq mm7, [eax + 30H] + psubw mm6, mm2 + paddw mm0, mm3 + pmulhw mm3, mm7 + movq mm2, [edx + 20H] + pmulhw mm7, mm1 + paddw mm5, mm1 + movq mm1, mm2 + pmulhw mm2, [eax + 08H] + psubw mm3, mm5 + movq mm5, [edx + 28H] + paddw mm0, mm7 + movq mm7, mm5 + psubw mm0, mm4 + pmulhw mm5, [eax + 08H] + paddw mm2, mm1 + pmulhw mm1, [eax + 28H] + paddw mm4, mm4 + paddw mm4, mm0 + psubw mm3, mm6 + paddw mm5, mm7 + paddw mm6, mm6 + pmulhw mm7, [eax + 28H] + paddw mm6, mm3 + movq [edx + 10H], mm4 + psubw mm1, mm5 + movq mm4, [eax + 18H] + movq mm5, mm3 + pmulhw mm3, mm4 + paddw mm7, mm2 + movq [edx + 20H], mm6 + movq mm2, mm0 + movq mm6, [edx] + pmulhw mm0, mm4 + paddw mm5, mm3 + movq mm3, [edx + 08H] + psubw mm5, mm1 + paddw mm2, mm0 + psubw mm6, mm3 + movq mm0, mm6 + pmulhw mm6, mm4 + paddw mm3, mm3 + paddw mm1, mm1 + paddw mm3, mm0 + paddw mm1, mm5 + pmulhw mm4, mm3 + paddw mm6, mm0 + psubw mm6, mm2 + paddw mm2, mm2 + movq mm0, [edx + 10H] + paddw mm2, mm6 + paddw mm4, mm3 + psubw mm2, mm1 + movq mm3, [edx + 20H] + psubw mm4, mm7 + paddw mm1, mm1 + paddw mm7, mm7 + paddw mm1, mm2 + paddw mm7, mm4 + psubw mm4, mm3 + paddw mm3, mm3 + psubw mm6, mm5 + paddw mm5, mm5 + paddw mm3, mm4 + paddw mm5, mm6 + psubw mm7, mm0 + paddw mm0, mm0 + movq [edx + 10H], mm1 + paddw mm0, mm7 + movq mm1, mm4 + punpcklwd mm4, mm5 + movq [edx], mm0 + punpckhwd mm1, mm5 + movq mm0, mm6 + punpcklwd mm6, mm7 + movq mm5, mm4 + punpckldq mm4, mm6 + punpckhdq mm5, mm6 + movq mm6, mm1 + movq [edx + 08H], mm4 + punpckhwd mm0, mm7 + movq [edx + 18H], mm5 + punpckhdq mm6, mm0 + movq mm4, [edx] + punpckldq mm1, mm0 + movq mm5, [edx + 10H] + movq mm0, mm4 + movq [edx + 38H], mm6 + punpcklwd mm0, mm5 + movq [edx + 28H], mm1 + punpckhwd mm4, mm5 + movq mm5, mm2 + punpcklwd mm2, mm3 + movq mm1, mm0 + punpckldq mm0, mm2 + punpckhdq mm1, mm2 + movq mm2, mm4 + movq [edx], mm0 + punpckhwd mm5, mm3 + movq [edx + 10H], mm1 + punpckhdq mm4, mm5 + punpckldq mm2, mm5 + movq [edx + 30H], mm4 + movq [edx + 20H], mm2 + movq mm2, [edx + 70H] + movq mm6, [eax + 10H] + movq mm4, mm2 + movq mm7, [edx + 58H] + pmulhw mm4, mm6 + movq mm1, [eax + 20H] + pmulhw mm6, mm7 + movq mm5, mm1 + pmulhw mm1, mm2 + movq mm3, [edx + 50H] + pmulhw mm5, mm7 + movq mm0, [eax] + paddw mm4, mm2 + paddw mm6, mm7 + paddw mm2, mm1 + movq mm1, [edx + 78H] + paddw mm7, mm5 + movq mm5, mm0 + pmulhw mm0, mm3 + paddw mm4, mm7 + pmulhw mm5, mm1 + movq mm7, [eax + 30H] + psubw mm6, mm2 + paddw mm0, mm3 + pmulhw mm3, mm7 + movq mm2, [edx + 60H] + pmulhw mm7, mm1 + paddw mm5, mm1 + movq mm1, mm2 + pmulhw mm2, [eax + 08H] + psubw mm3, mm5 + movq mm5, [edx + 68H] + paddw mm0, mm7 + movq mm7, mm5 + psubw mm0, mm4 + pmulhw mm5, [eax + 08H] + paddw mm2, mm1 + pmulhw mm1, [eax + 28H] + paddw mm4, mm4 + paddw mm4, mm0 + psubw mm3, mm6 + paddw mm5, mm7 + paddw mm6, mm6 + pmulhw mm7, [eax + 28H] + paddw mm6, mm3 + movq [edx + 50H], mm4 + psubw mm1, mm5 + movq mm4, [eax + 18H] + movq mm5, mm3 + pmulhw mm3, mm4 + paddw mm7, mm2 + movq [edx + 60H], mm6 + movq mm2, mm0 + movq mm6, [edx + 40H] + pmulhw mm0, mm4 + paddw mm5, mm3 + movq mm3, [edx + 48H] + psubw mm5, mm1 + paddw mm2, mm0 + psubw mm6, mm3 + movq mm0, mm6 + pmulhw mm6, mm4 + paddw mm3, mm3 + paddw mm1, mm1 + paddw mm3, mm0 + paddw mm1, mm5 + pmulhw mm4, mm3 + paddw mm6, mm0 + psubw mm6, mm2 + paddw mm2, mm2 + movq mm0, [edx + 50H] + paddw mm2, mm6 + paddw mm4, mm3 + psubw mm2, mm1 + movq mm3, [edx + 60H] + psubw mm4, mm7 + paddw mm1, mm1 + paddw mm7, mm7 + paddw mm1, mm2 + paddw mm7, mm4 + psubw mm4, mm3 + paddw mm3, mm3 + psubw mm6, mm5 + paddw mm5, mm5 + paddw mm3, mm4 + paddw mm5, mm6 + psubw mm7, mm0 + paddw mm0, mm0 + movq [edx + 50H], mm1 + paddw mm0, mm7 + movq mm1, mm4 + punpcklwd mm4, mm5 + movq [edx + 40H], mm0 + punpckhwd mm1, mm5 + movq mm0, mm6 + punpcklwd mm6, mm7 + movq mm5, mm4 + punpckldq mm4, mm6 + punpckhdq mm5, mm6 + movq mm6, mm1 + movq [edx + 48H], mm4 + punpckhwd mm0, mm7 + movq [edx + 58H], mm5 + punpckhdq mm6, mm0 + movq mm4, [edx + 40H] + punpckldq mm1, mm0 + movq mm5, [edx + 50H] + movq mm0, mm4 + movq [edx + 78H], mm6 + punpcklwd mm0, mm5 + movq [edx + 68H], mm1 + punpckhwd mm4, mm5 + movq mm5, mm2 + punpcklwd mm2, mm3 + movq mm1, mm0 + punpckldq mm0, mm2 + punpckhdq mm1, mm2 + movq mm2, mm4 + movq [edx + 40H], mm0 + punpckhwd mm5, mm3 + movq [edx + 50H], mm1 + punpckhdq mm4, mm5 + punpckldq mm2, mm5 + movq [edx + 70H], mm4 + movq [edx + 60H], mm2 + movq mm2, [edx + 30H] + movq mm6, [eax + 10H] + movq mm4, mm2 + movq mm7, [edx + 50H] + pmulhw mm4, mm6 + movq mm1, [eax + 20H] + pmulhw mm6, mm7 + movq mm5, mm1 + pmulhw mm1, mm2 + movq mm3, [edx + 10H] + pmulhw mm5, mm7 + movq mm0, [eax] + paddw mm4, mm2 + paddw mm6, mm7 + paddw mm2, mm1 + movq mm1, [edx + 70H] + paddw mm7, mm5 + movq mm5, mm0 + pmulhw mm0, mm3 + paddw mm4, mm7 + pmulhw mm5, mm1 + movq mm7, [eax + 30H] + psubw mm6, mm2 + paddw mm0, mm3 + pmulhw mm3, mm7 + movq mm2, [edx + 20H] + pmulhw mm7, mm1 + paddw mm5, mm1 + movq mm1, mm2 + pmulhw mm2, [eax + 08H] + psubw mm3, mm5 + movq mm5, [edx + 60H] + paddw mm0, mm7 + movq mm7, mm5 + psubw mm0, mm4 + pmulhw mm5, [eax + 08H] + paddw mm2, mm1 + pmulhw mm1, [eax + 28H] + paddw mm4, mm4 + paddw mm4, mm0 + psubw mm3, mm6 + paddw mm5, mm7 + paddw mm6, mm6 + pmulhw mm7, [eax + 28H] + paddw mm6, mm3 + movq [edx + 10H], mm4 + psubw mm1, mm5 + movq mm4, [eax + 18H] + movq mm5, mm3 + pmulhw mm3, mm4 + paddw mm7, mm2 + movq [edx + 20H], mm6 + movq mm2, mm0 + movq mm6, [edx] + pmulhw mm0, mm4 + paddw mm5, mm3 + movq mm3, [edx + 40H] + psubw mm5, mm1 + paddw mm2, mm0 + psubw mm6, mm3 + movq mm0, mm6 + pmulhw mm6, mm4 + paddw mm3, mm3 + paddw mm1, mm1 + paddw mm3, mm0 + paddw mm1, mm5 + pmulhw mm4, mm3 + paddw mm6, mm0 + psubw mm6, mm2 + paddw mm2, mm2 + movq mm0, [edx + 10H] + paddw mm2, mm6 + paddw mm4, mm3 + psubw mm2, mm1 + paddw mm2, [eax + 38H] + paddw mm1, mm1 + paddw mm1, mm2 + psraw mm2, 4 + psubw mm4, mm7 + psraw mm1, 4 + movq mm3, [edx + 20H] + paddw mm7, mm7 + movq [edx + 20H], mm2 + paddw mm7, mm4 + movq [edx + 10H], mm1 + psubw mm4, mm3 + paddw mm4, [eax + 38H] + paddw mm3, mm3 + paddw mm3, mm4 + psraw mm4, 4 + psubw mm6, mm5 + psraw mm3, 4 + paddw mm6, [eax + 38H] + paddw mm5, mm5 + paddw mm5, mm6 + psraw mm6, 4 + movq [edx + 40H], mm4 + psraw mm5, 4 + movq [edx + 30H], mm3 + psubw mm7, mm0 + paddw mm7, [eax + 38H] + paddw mm0, mm0 + paddw mm0, mm7 + psraw mm7, 4 + movq [edx + 60H], mm6 + psraw mm0, 4 + movq [edx + 50H], mm5 + movq [edx + 70H], mm7 + movq [edx], mm0 + movq mm2, [edx + 38H] + movq mm6, [eax + 10H] + movq mm4, mm2 + movq mm7, [edx + 58H] + pmulhw mm4, mm6 + movq mm1, [eax + 20H] + pmulhw mm6, mm7 + movq mm5, mm1 + pmulhw mm1, mm2 + movq mm3, [edx + 18H] + pmulhw mm5, mm7 + movq mm0, [eax] + paddw mm4, mm2 + paddw mm6, mm7 + paddw mm2, mm1 + movq mm1, [edx + 78H] + paddw mm7, mm5 + movq mm5, mm0 + pmulhw mm0, mm3 + paddw mm4, mm7 + pmulhw mm5, mm1 + movq mm7, [eax + 30H] + psubw mm6, mm2 + paddw mm0, mm3 + pmulhw mm3, mm7 + movq mm2, [edx + 28H] + pmulhw mm7, mm1 + paddw mm5, mm1 + movq mm1, mm2 + pmulhw mm2, [eax + 08H] + psubw mm3, mm5 + movq mm5, [edx + 68H] + paddw mm0, mm7 + movq mm7, mm5 + psubw mm0, mm4 + pmulhw mm5, [eax + 08H] + paddw mm2, mm1 + pmulhw mm1, [eax + 28H] + paddw mm4, mm4 + paddw mm4, mm0 + psubw mm3, mm6 + paddw mm5, mm7 + paddw mm6, mm6 + pmulhw mm7, [eax + 28H] + paddw mm6, mm3 + movq [edx + 18H], mm4 + psubw mm1, mm5 + movq mm4, [eax + 18H] + movq mm5, mm3 + pmulhw mm3, mm4 + paddw mm7, mm2 + movq [edx + 28H], mm6 + movq mm2, mm0 + movq mm6, [edx + 08H] + pmulhw mm0, mm4 + paddw mm5, mm3 + movq mm3, [edx + 48H] + psubw mm5, mm1 + paddw mm2, mm0 + psubw mm6, mm3 + movq mm0, mm6 + pmulhw mm6, mm4 + paddw mm3, mm3 + paddw mm1, mm1 + paddw mm3, mm0 + paddw mm1, mm5 + pmulhw mm4, mm3 + paddw mm6, mm0 + psubw mm6, mm2 + paddw mm2, mm2 + movq mm0, [edx + 18H] + paddw mm2, mm6 + paddw mm4, mm3 + psubw mm2, mm1 + paddw mm2, [eax + 38H] + paddw mm1, mm1 + paddw mm1, mm2 + psraw mm2, 4 + psubw mm4, mm7 + psraw mm1, 4 + movq mm3, [edx + 28H] + paddw mm7, mm7 + movq [edx + 28H], mm2 + paddw mm7, mm4 + movq [edx + 18H], mm1 + psubw mm4, mm3 + paddw mm4, [eax + 38H] + paddw mm3, mm3 + paddw mm3, mm4 + psraw mm4, 4 + psubw mm6, mm5 + psraw mm3, 4 + paddw mm6, [eax + 38H] + paddw mm5, mm5 + paddw mm5, mm6 + psraw mm6, 4 + movq [edx + 48H], mm4 + psraw mm5, 4 + movq [edx + 38H], mm3 + psubw mm7, mm0 + paddw mm7, [eax + 38H] + paddw mm0, mm0 + paddw mm0, mm7 + psraw mm7, 4 + movq [edx + 68H], mm6 + psraw mm0, 4 + movq [edx + 58H], mm5 + movq [edx + 78H], mm7 + movq [edx + 08H], mm0 + /* emms */ + } +} + + +void oc_idct8x8_mmx(ogg_int16_t _y[64]){ + _asm { + mov edx, [_y] + mov eax, offset OC_IDCT_CONSTS + movq mm2, [edx + 30H] + movq mm6, [eax + 10H] + movq mm4, mm2 + movq mm7, [edx + 18H] + pmulhw mm4, mm6 + movq mm1, [eax + 20H] + pmulhw mm6, mm7 + movq mm5, mm1 + pmulhw mm1, mm2 + movq mm3, [edx + 10H] + pmulhw mm5, mm7 + movq mm0, [eax] + paddw mm4, mm2 + paddw mm6, mm7 + paddw mm2, mm1 + movq mm1, [edx + 38H] + paddw mm7, mm5 + movq mm5, mm0 + pmulhw mm0, mm3 + paddw mm4, mm7 + pmulhw mm5, mm1 + movq mm7, [eax + 30H] + psubw mm6, mm2 + paddw mm0, mm3 + pmulhw mm3, mm7 + movq mm2, [edx + 20H] + pmulhw mm7, mm1 + paddw mm5, mm1 + movq mm1, mm2 + pmulhw mm2, [eax + 08H] + psubw mm3, mm5 + movq mm5, [edx + 28H] + paddw mm0, mm7 + movq mm7, mm5 + psubw mm0, mm4 + pmulhw mm5, [eax + 08H] + paddw mm2, mm1 + pmulhw mm1, [eax + 28H] + paddw mm4, mm4 + paddw mm4, mm0 + psubw mm3, mm6 + paddw mm5, mm7 + paddw mm6, mm6 + pmulhw mm7, [eax + 28H] + paddw mm6, mm3 + movq [edx + 10H], mm4 + psubw mm1, mm5 + movq mm4, [eax + 18H] + movq mm5, mm3 + pmulhw mm3, mm4 + paddw mm7, mm2 + movq [edx + 20H], mm6 + movq mm2, mm0 + movq mm6, [edx] + pmulhw mm0, mm4 + paddw mm5, mm3 + movq mm3, [edx + 08H] + psubw mm5, mm1 + paddw mm2, mm0 + psubw mm6, mm3 + movq mm0, mm6 + pmulhw mm6, mm4 + paddw mm3, mm3 + paddw mm1, mm1 + paddw mm3, mm0 + paddw mm1, mm5 + pmulhw mm4, mm3 + paddw mm6, mm0 + psubw mm6, mm2 + paddw mm2, mm2 + movq mm0, [edx + 10H] + paddw mm2, mm6 + paddw mm4, mm3 + psubw mm2, mm1 + movq mm3, [edx + 20H] + psubw mm4, mm7 + paddw mm1, mm1 + paddw mm7, mm7 + paddw mm1, mm2 + paddw mm7, mm4 + psubw mm4, mm3 + paddw mm3, mm3 + psubw mm6, mm5 + paddw mm5, mm5 + paddw mm3, mm4 + paddw mm5, mm6 + psubw mm7, mm0 + paddw mm0, mm0 + movq [edx + 10H], mm1 + paddw mm0, mm7 + movq mm1, mm4 + punpcklwd mm4, mm5 + movq [edx], mm0 + punpckhwd mm1, mm5 + movq mm0, mm6 + punpcklwd mm6, mm7 + movq mm5, mm4 + punpckldq mm4, mm6 + punpckhdq mm5, mm6 + movq mm6, mm1 + movq [edx + 08H], mm4 + punpckhwd mm0, mm7 + movq [edx + 18H], mm5 + punpckhdq mm6, mm0 + movq mm4, [edx] + punpckldq mm1, mm0 + movq mm5, [edx + 10H] + movq mm0, mm4 + movq [edx + 38H], mm6 + punpcklwd mm0, mm5 + movq [edx + 28H], mm1 + punpckhwd mm4, mm5 + movq mm5, mm2 + punpcklwd mm2, mm3 + movq mm1, mm0 + punpckldq mm0, mm2 + punpckhdq mm1, mm2 + movq mm2, mm4 + movq [edx], mm0 + punpckhwd mm5, mm3 + movq [edx + 10H], mm1 + punpckhdq mm4, mm5 + punpckldq mm2, mm5 + movq [edx + 30H], mm4 + movq [edx + 20H], mm2 + movq mm2, [edx + 70H] + movq mm6, [eax + 10H] + movq mm4, mm2 + movq mm7, [edx + 58H] + pmulhw mm4, mm6 + movq mm1, [eax + 20H] + pmulhw mm6, mm7 + movq mm5, mm1 + pmulhw mm1, mm2 + movq mm3, [edx + 50H] + pmulhw mm5, mm7 + movq mm0, [eax] + paddw mm4, mm2 + paddw mm6, mm7 + paddw mm2, mm1 + movq mm1, [edx + 78H] + paddw mm7, mm5 + movq mm5, mm0 + pmulhw mm0, mm3 + paddw mm4, mm7 + pmulhw mm5, mm1 + movq mm7, [eax + 30H] + psubw mm6, mm2 + paddw mm0, mm3 + pmulhw mm3, mm7 + movq mm2, [edx + 60H] + pmulhw mm7, mm1 + paddw mm5, mm1 + movq mm1, mm2 + pmulhw mm2, [eax + 08H] + psubw mm3, mm5 + movq mm5, [edx + 68H] + paddw mm0, mm7 + movq mm7, mm5 + psubw mm0, mm4 + pmulhw mm5, [eax + 08H] + paddw mm2, mm1 + pmulhw mm1, [eax + 28H] + paddw mm4, mm4 + paddw mm4, mm0 + psubw mm3, mm6 + paddw mm5, mm7 + paddw mm6, mm6 + pmulhw mm7, [eax + 28H] + paddw mm6, mm3 + movq [edx + 50H], mm4 + psubw mm1, mm5 + movq mm4, [eax + 18H] + movq mm5, mm3 + pmulhw mm3, mm4 + paddw mm7, mm2 + movq [edx + 60H], mm6 + movq mm2, mm0 + movq mm6, [edx + 40H] + pmulhw mm0, mm4 + paddw mm5, mm3 + movq mm3, [edx + 48H] + psubw mm5, mm1 + paddw mm2, mm0 + psubw mm6, mm3 + movq mm0, mm6 + pmulhw mm6, mm4 + paddw mm3, mm3 + paddw mm1, mm1 + paddw mm3, mm0 + paddw mm1, mm5 + pmulhw mm4, mm3 + paddw mm6, mm0 + psubw mm6, mm2 + paddw mm2, mm2 + movq mm0, [edx + 50H] + paddw mm2, mm6 + paddw mm4, mm3 + psubw mm2, mm1 + movq mm3, [edx + 60H] + psubw mm4, mm7 + paddw mm1, mm1 + paddw mm7, mm7 + paddw mm1, mm2 + paddw mm7, mm4 + psubw mm4, mm3 + paddw mm3, mm3 + psubw mm6, mm5 + paddw mm5, mm5 + paddw mm3, mm4 + paddw mm5, mm6 + psubw mm7, mm0 + paddw mm0, mm0 + movq [edx + 50H], mm1 + paddw mm0, mm7 + movq mm1, mm4 + punpcklwd mm4, mm5 + movq [edx + 40H], mm0 + punpckhwd mm1, mm5 + movq mm0, mm6 + punpcklwd mm6, mm7 + movq mm5, mm4 + punpckldq mm4, mm6 + punpckhdq mm5, mm6 + movq mm6, mm1 + movq [edx + 48H], mm4 + punpckhwd mm0, mm7 + movq [edx + 58H], mm5 + punpckhdq mm6, mm0 + movq mm4, [edx + 40H] + punpckldq mm1, mm0 + movq mm5, [edx + 50H] + movq mm0, mm4 + movq [edx + 78H], mm6 + punpcklwd mm0, mm5 + movq [edx + 68H], mm1 + punpckhwd mm4, mm5 + movq mm5, mm2 + punpcklwd mm2, mm3 + movq mm1, mm0 + punpckldq mm0, mm2 + punpckhdq mm1, mm2 + movq mm2, mm4 + movq [edx + 40H], mm0 + punpckhwd mm5, mm3 + movq [edx + 50H], mm1 + punpckhdq mm4, mm5 + punpckldq mm2, mm5 + movq [edx + 70H], mm4 + movq [edx + 60H], mm2 + movq mm2, [edx + 30H] + movq mm6, [eax + 10H] + movq mm4, mm2 + movq mm7, [edx + 50H] + pmulhw mm4, mm6 + movq mm1, [eax + 20H] + pmulhw mm6, mm7 + movq mm5, mm1 + pmulhw mm1, mm2 + movq mm3, [edx + 10H] + pmulhw mm5, mm7 + movq mm0, [eax] + paddw mm4, mm2 + paddw mm6, mm7 + paddw mm2, mm1 + movq mm1, [edx + 70H] + paddw mm7, mm5 + movq mm5, mm0 + pmulhw mm0, mm3 + paddw mm4, mm7 + pmulhw mm5, mm1 + movq mm7, [eax + 30H] + psubw mm6, mm2 + paddw mm0, mm3 + pmulhw mm3, mm7 + movq mm2, [edx + 20H] + pmulhw mm7, mm1 + paddw mm5, mm1 + movq mm1, mm2 + pmulhw mm2, [eax + 08H] + psubw mm3, mm5 + movq mm5, [edx + 60H] + paddw mm0, mm7 + movq mm7, mm5 + psubw mm0, mm4 + pmulhw mm5, [eax + 08H] + paddw mm2, mm1 + pmulhw mm1, [eax + 28H] + paddw mm4, mm4 + paddw mm4, mm0 + psubw mm3, mm6 + paddw mm5, mm7 + paddw mm6, mm6 + pmulhw mm7, [eax + 28H] + paddw mm6, mm3 + movq [edx + 10H], mm4 + psubw mm1, mm5 + movq mm4, [eax + 18H] + movq mm5, mm3 + pmulhw mm3, mm4 + paddw mm7, mm2 + movq [edx + 20H], mm6 + movq mm2, mm0 + movq mm6, [edx] + pmulhw mm0, mm4 + paddw mm5, mm3 + movq mm3, [edx + 40H] + psubw mm5, mm1 + paddw mm2, mm0 + psubw mm6, mm3 + movq mm0, mm6 + pmulhw mm6, mm4 + paddw mm3, mm3 + paddw mm1, mm1 + paddw mm3, mm0 + paddw mm1, mm5 + pmulhw mm4, mm3 + paddw mm6, mm0 + psubw mm6, mm2 + paddw mm2, mm2 + movq mm0, [edx + 10H] + paddw mm2, mm6 + paddw mm4, mm3 + psubw mm2, mm1 + paddw mm2, [eax + 38H] + paddw mm1, mm1 + paddw mm1, mm2 + psraw mm2, 4 + psubw mm4, mm7 + psraw mm1, 4 + movq mm3, [edx + 20H] + paddw mm7, mm7 + movq [edx + 20H], mm2 + paddw mm7, mm4 + movq [edx + 10H], mm1 + psubw mm4, mm3 + paddw mm4, [eax + 38H] + paddw mm3, mm3 + paddw mm3, mm4 + psraw mm4, 4 + psubw mm6, mm5 + psraw mm3, 4 + paddw mm6, [eax + 38H] + paddw mm5, mm5 + paddw mm5, mm6 + psraw mm6, 4 + movq [edx + 40H], mm4 + psraw mm5, 4 + movq [edx + 30H], mm3 + psubw mm7, mm0 + paddw mm7, [eax + 38H] + paddw mm0, mm0 + paddw mm0, mm7 + psraw mm7, 4 + movq [edx + 60H], mm6 + psraw mm0, 4 + movq [edx + 50H], mm5 + movq [edx + 70H], mm7 + movq [edx], mm0 + movq mm2, [edx + 38H] + movq mm6, [eax + 10H] + movq mm4, mm2 + movq mm7, [edx + 58H] + pmulhw mm4, mm6 + movq mm1, [eax + 20H] + pmulhw mm6, mm7 + movq mm5, mm1 + pmulhw mm1, mm2 + movq mm3, [edx + 18H] + pmulhw mm5, mm7 + movq mm0, [eax] + paddw mm4, mm2 + paddw mm6, mm7 + paddw mm2, mm1 + movq mm1, [edx + 78H] + paddw mm7, mm5 + movq mm5, mm0 + pmulhw mm0, mm3 + paddw mm4, mm7 + pmulhw mm5, mm1 + movq mm7, [eax + 30H] + psubw mm6, mm2 + paddw mm0, mm3 + pmulhw mm3, mm7 + movq mm2, [edx + 28H] + pmulhw mm7, mm1 + paddw mm5, mm1 + movq mm1, mm2 + pmulhw mm2, [eax + 08H] + psubw mm3, mm5 + movq mm5, [edx + 68H] + paddw mm0, mm7 + movq mm7, mm5 + psubw mm0, mm4 + pmulhw mm5, [eax + 08H] + paddw mm2, mm1 + pmulhw mm1, [eax + 28H] + paddw mm4, mm4 + paddw mm4, mm0 + psubw mm3, mm6 + paddw mm5, mm7 + paddw mm6, mm6 + pmulhw mm7, [eax + 28H] + paddw mm6, mm3 + movq [edx + 18H], mm4 + psubw mm1, mm5 + movq mm4, [eax + 18H] + movq mm5, mm3 + pmulhw mm3, mm4 + paddw mm7, mm2 + movq [edx + 28H], mm6 + movq mm2, mm0 + movq mm6, [edx + 08H] + pmulhw mm0, mm4 + paddw mm5, mm3 + movq mm3, [edx + 48H] + psubw mm5, mm1 + paddw mm2, mm0 + psubw mm6, mm3 + movq mm0, mm6 + pmulhw mm6, mm4 + paddw mm3, mm3 + paddw mm1, mm1 + paddw mm3, mm0 + paddw mm1, mm5 + pmulhw mm4, mm3 + paddw mm6, mm0 + psubw mm6, mm2 + paddw mm2, mm2 + movq mm0, [edx + 18H] + paddw mm2, mm6 + paddw mm4, mm3 + psubw mm2, mm1 + paddw mm2, [eax + 38H] + paddw mm1, mm1 + paddw mm1, mm2 + psraw mm2, 4 + psubw mm4, mm7 + psraw mm1, 4 + movq mm3, [edx + 28H] + paddw mm7, mm7 + movq [edx + 28H], mm2 + paddw mm7, mm4 + movq [edx + 18H], mm1 + psubw mm4, mm3 + paddw mm4, [eax + 38H] + paddw mm3, mm3 + paddw mm3, mm4 + psraw mm4, 4 + psubw mm6, mm5 + psraw mm3, 4 + paddw mm6, [eax + 38H] + paddw mm5, mm5 + paddw mm5, mm6 + psraw mm6, 4 + movq [edx + 48H], mm4 + psraw mm5, 4 + movq [edx + 38H], mm3 + psubw mm7, mm0 + paddw mm7, [eax + 38H] + paddw mm0, mm0 + paddw mm0, mm7 + psraw mm7, 4 + movq [edx + 68H], mm6 + psraw mm0, 4 + movq [edx + 58H], mm5 + movq [edx + 78H], mm7 + movq [edx + 08H], mm0 + /* emms */ + } +} + +#endif diff --git a/Engine/lib/libtheora/lib/dec/x86_vc/mmxloopfilter.c b/Engine/lib/libtheora/lib/dec/x86_vc/mmxloopfilter.c new file mode 100644 index 000000000..62d06dc89 --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/x86_vc/mmxloopfilter.c @@ -0,0 +1,377 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: + + ********************************************************************/ + +/* ------------------------------------------------------------------- + MMX based loop filter for the theora codec. + + Originally written by Rudolf Marek, based on code from On2's VP3. + Converted to Visual Studio inline assembly by Nils Pipenbrinck. + + Note: I can't test these since my example files never get into the + loop filters, but the code has been converted semi-automatic from + the GCC sources, so it ought to work. + ---------------------------------------------------------------------*/ +#include "../../internal.h" +#include "x86int.h" +#include + +#if defined(USE_ASM) + + + +static void loop_filter_v(unsigned char *_pix,int _ystride, + const ogg_int16_t *_ll){ + _asm { + mov eax, [_pix] + mov edx, [_ystride] + mov ebx, [_ll] + + /* _pix -= ystride */ + sub eax, edx + /* mm0=0 */ + pxor mm0, mm0 + /* _pix -= ystride */ + sub eax, edx + /* esi=_ystride*3 */ + lea esi, [edx + edx*2] + + /* mm7=_pix[0...8]*/ + movq mm7, [eax] + /* mm4=_pix[0...8+_ystride*3]*/ + movq mm4, [eax + esi] + /* mm6=_pix[0...8]*/ + movq mm6, mm7 + /* Expand unsigned _pix[0...3] to 16 bits.*/ + punpcklbw mm6, mm0 + movq mm5, mm4 + /* Expand unsigned _pix[4...7] to 16 bits.*/ + punpckhbw mm7, mm0 + punpcklbw mm4, mm0 + /* Expand other arrays too.*/ + punpckhbw mm5, mm0 + /*mm7:mm6=_p[0...7]-_p[0...7+_ystride*3]:*/ + psubw mm6, mm4 + psubw mm7, mm5 + /*mm5=mm4=_pix[0...7+_ystride]*/ + movq mm4, [eax + edx] + /*mm1=mm3=mm2=_pix[0..7]+_ystride*2]*/ + movq mm2, [eax + edx*2] + movq mm5, mm4 + movq mm3, mm2 + movq mm1, mm2 + /*Expand these arrays.*/ + punpckhbw mm5, mm0 + punpcklbw mm4, mm0 + punpckhbw mm3, mm0 + punpcklbw mm2, mm0 + pcmpeqw mm0, mm0 + /*mm0=3 3 3 3 + mm3:mm2=_pix[0...8+_ystride*2]-_pix[0...8+_ystride]*/ + psubw mm3, mm5 + psrlw mm0, 14 + psubw mm2, mm4 + /*Scale by 3.*/ + pmullw mm3, mm0 + pmullw mm2, mm0 + /*mm0=4 4 4 4 + f=mm3:mm2==_pix[0...8]-_pix[0...8+_ystride*3]+ + 3*(_pix[0...8+_ystride*2]-_pix[0...8+_ystride])*/ + psrlw mm0, 1 + paddw mm3, mm7 + psllw mm0, 2 + paddw mm2, mm6 + /*Add 4.*/ + paddw mm3, mm0 + paddw mm2, mm0 + /*"Divide" by 8.*/ + psraw mm3, 3 + psraw mm2, 3 + /*Now compute lflim of mm3:mm2 cf. Section 7.10 of the sepc.*/ + /*Free up mm5.*/ + packuswb mm4, mm5 + /*mm0=L L L L*/ + movq mm0, [ebx] + /*if(R_i<-2L||R_i>2L)R_i=0:*/ + movq mm5, mm2 + pxor mm6, mm6 + movq mm7, mm0 + psubw mm6, mm0 + psllw mm7, 1 + psllw mm6, 1 + /*mm2==R_3 R_2 R_1 R_0*/ + /*mm5==R_3 R_2 R_1 R_0*/ + /*mm6==-2L -2L -2L -2L*/ + /*mm7==2L 2L 2L 2L*/ + pcmpgtw mm7, mm2 + pcmpgtw mm5, mm6 + pand mm2, mm7 + movq mm7, mm0 + pand mm2, mm5 + psllw mm7, 1 + movq mm5, mm3 + /*mm3==R_7 R_6 R_5 R_4*/ + /*mm5==R_7 R_6 R_5 R_4*/ + /*mm6==-2L -2L -2L -2L*/ + /*mm7==2L 2L 2L 2L*/ + pcmpgtw mm7, mm3 + pcmpgtw mm5, mm6 + pand mm3, mm7 + movq mm7, mm0 + pand mm3, mm5 + /*if(R_i<-L)R_i'=R_i+2L; + if(R_i>L)R_i'=R_i-2L; + if(R_i<-L||R_i>L)R_i=-R_i':*/ + psraw mm6, 1 + movq mm5, mm2 + psllw mm7, 1 + /*mm2==R_3 R_2 R_1 R_0*/ + /*mm5==R_3 R_2 R_1 R_0*/ + /*mm6==-L -L -L -L*/ + /*mm0==L L L L*/ + /*mm5=R_i>L?FF:00*/ + pcmpgtw mm5, mm0 + /*mm6=-L>R_i?FF:00*/ + pcmpgtw mm6, mm2 + /*mm7=R_i>L?2L:0*/ + pand mm7, mm5 + /*mm2=R_i>L?R_i-2L:R_i*/ + psubw mm2, mm7 + movq mm7, mm0 + /*mm5=-L>R_i||R_i>L*/ + por mm5, mm6 + psllw mm7, 1 + /*mm7=-L>R_i?2L:0*/ + pand mm7, mm6 + pxor mm6, mm6 + /*mm2=-L>R_i?R_i+2L:R_i*/ + paddw mm2, mm7 + psubw mm6, mm0 + /*mm5=-L>R_i||R_i>L?-R_i':0*/ + pand mm5, mm2 + movq mm7, mm0 + /*mm2=-L>R_i||R_i>L?0:R_i*/ + psubw mm2, mm5 + psllw mm7, 1 + /*mm2=-L>R_i||R_i>L?-R_i':R_i*/ + psubw mm2, mm5 + movq mm5, mm3 + /*mm3==R_7 R_6 R_5 R_4*/ + /*mm5==R_7 R_6 R_5 R_4*/ + /*mm6==-L -L -L -L*/ + /*mm0==L L L L*/ + /*mm6=-L>R_i?FF:00*/ + pcmpgtw mm6, mm3 + /*mm5=R_i>L?FF:00*/ + pcmpgtw mm5, mm0 + /*mm7=R_i>L?2L:0*/ + pand mm7, mm5 + /*mm2=R_i>L?R_i-2L:R_i*/ + psubw mm3, mm7 + psllw mm0, 1 + /*mm5=-L>R_i||R_i>L*/ + por mm5, mm6 + /*mm0=-L>R_i?2L:0*/ + pand mm0, mm6 + /*mm3=-L>R_i?R_i+2L:R_i*/ + paddw mm3, mm0 + /*mm5=-L>R_i||R_i>L?-R_i':0*/ + pand mm5, mm3 + /*mm2=-L>R_i||R_i>L?0:R_i*/ + psubw mm3, mm5 + /*mm3=-L>R_i||R_i>L?-R_i':R_i*/ + psubw mm3, mm5 + /*Unfortunately, there's no unsigned byte+signed byte with unsigned + saturation op code, so we have to promote things back 16 bits.*/ + pxor mm0, mm0 + movq mm5, mm4 + punpcklbw mm4, mm0 + punpckhbw mm5, mm0 + movq mm6, mm1 + punpcklbw mm1, mm0 + punpckhbw mm6, mm0 + /*_pix[0...8+_ystride]+=R_i*/ + paddw mm4, mm2 + paddw mm5, mm3 + /*_pix[0...8+_ystride*2]-=R_i*/ + psubw mm1, mm2 + psubw mm6, mm3 + packuswb mm4, mm5 + packuswb mm1, mm6 + /*Write it back out.*/ + movq [eax + edx], mm4 + movq [eax + edx*2], mm1 + } +} + +/*This code implements the bulk of loop_filter_h(). + Data are striped p0 p1 p2 p3 ... p0 p1 p2 p3 ..., so in order to load all + four p0's to one register we must transpose the values in four mmx regs. + When half is done we repeat this for the rest.*/ +static void loop_filter_h4(unsigned char *_pix,long _ystride, + const ogg_int16_t *_ll){ + /* todo: merge the comments from the GCC sources */ + _asm { + mov ecx, [_pix] + mov edx, [_ystride] + mov eax, [_ll] + /*esi=_ystride*3*/ + lea esi, [edx + edx*2] + + movd mm0, dword ptr [ecx] + movd mm1, dword ptr [ecx + edx] + movd mm2, dword ptr [ecx + edx*2] + movd mm3, dword ptr [ecx + esi] + punpcklbw mm0, mm1 + punpcklbw mm2, mm3 + movq mm1, mm0 + punpckhwd mm0, mm2 + punpcklwd mm1, mm2 + pxor mm7, mm7 + movq mm5, mm1 + punpcklbw mm1, mm7 + punpckhbw mm5, mm7 + movq mm3, mm0 + punpcklbw mm0, mm7 + punpckhbw mm3, mm7 + psubw mm1, mm3 + movq mm4, mm0 + pcmpeqw mm2, mm2 + psubw mm0, mm5 + psrlw mm2, 14 + pmullw mm0, mm2 + psrlw mm2, 1 + paddw mm0, mm1 + psllw mm2, 2 + paddw mm0, mm2 + psraw mm0, 3 + movq mm6, qword ptr [eax] + movq mm1, mm0 + pxor mm2, mm2 + movq mm3, mm6 + psubw mm2, mm6 + psllw mm3, 1 + psllw mm2, 1 + pcmpgtw mm3, mm0 + pcmpgtw mm1, mm2 + pand mm0, mm3 + pand mm0, mm1 + psraw mm2, 1 + movq mm1, mm0 + movq mm3, mm6 + pcmpgtw mm2, mm0 + pcmpgtw mm1, mm6 + psllw mm3, 1 + psllw mm6, 1 + pand mm3, mm1 + pand mm6, mm2 + psubw mm0, mm3 + por mm1, mm2 + paddw mm0, mm6 + pand mm1, mm0 + psubw mm0, mm1 + psubw mm0, mm1 + paddw mm5, mm0 + psubw mm4, mm0 + packuswb mm5, mm7 + packuswb mm4, mm7 + punpcklbw mm5, mm4 + movd edi, mm5 + mov word ptr [ecx + 01H], di + psrlq mm5, 32 + shr edi, 16 + mov word ptr [ecx + edx + 01H], di + movd edi, mm5 + mov word ptr [ecx + edx*2 + 01H], di + shr edi, 16 + mov word ptr [ecx + esi + 01H], di + } +} + +static void loop_filter_h(unsigned char *_pix,int _ystride, + const ogg_int16_t *_ll){ + _pix-=2; + loop_filter_h4(_pix,_ystride,_ll); + loop_filter_h4(_pix+(_ystride<<2),_ystride,_ll); +} + + +/*We copy the whole function because the MMX routines will be inlined 4 times, + and we can do just a single emms call at the end this way. + We also do not use the _bv lookup table, instead computing the values that + would lie in it on the fly.*/ + +/*Apply the loop filter to a given set of fragment rows in the given plane. + The filter may be run on the bottom edge, affecting pixels in the next row of + fragments, so this row also needs to be available. + _bv: The bounding values array. + _refi: The index of the frame buffer to filter. + _pli: The color plane to filter. + _fragy0: The Y coordinate of the first fragment row to filter. + _fragy_end: The Y coordinate of the fragment row to stop filtering at.*/ +void oc_state_loop_filter_frag_rows_mmx(oc_theora_state *_state,int *_bv, + int _refi,int _pli,int _fragy0,int _fragy_end){ + ogg_int16_t __declspec(align(8)) ll[4]; + th_img_plane *iplane; + oc_fragment_plane *fplane; + oc_fragment *frag_top; + oc_fragment *frag0; + oc_fragment *frag; + oc_fragment *frag_end; + oc_fragment *frag0_end; + oc_fragment *frag_bot; + ll[0]=ll[1]=ll[2]=ll[3]= + (ogg_int16_t)_state->loop_filter_limits[_state->qis[0]]; + iplane=_state->ref_frame_bufs[_refi]+_pli; + fplane=_state->fplanes+_pli; + /*The following loops are constructed somewhat non-intuitively on purpose. + The main idea is: if a block boundary has at least one coded fragment on + it, the filter is applied to it. + However, the order that the filters are applied in matters, and VP3 chose + the somewhat strange ordering used below.*/ + frag_top=_state->frags+fplane->froffset; + frag0=frag_top+_fragy0*fplane->nhfrags; + frag0_end=frag0+(_fragy_end-_fragy0)*fplane->nhfrags; + frag_bot=_state->frags+fplane->froffset+fplane->nfrags; + while(frag0nhfrags; + while(fragcoded){ + if(frag>frag0){ + loop_filter_h(frag->buffer[_refi],iplane->stride,ll); + } + if(frag0>frag_top){ + loop_filter_v(frag->buffer[_refi],iplane->stride,ll); + } + if(frag+1coded){ + loop_filter_h(frag->buffer[_refi]+8,iplane->stride,ll); + } + if(frag+fplane->nhfragsnhfrags)->coded){ + loop_filter_v((frag+fplane->nhfrags)->buffer[_refi], + iplane->stride,ll); + } + } + frag++; + } + frag0+=fplane->nhfrags; + } + + /*This needs to be removed when decode specific functions are implemented:*/ + _mm_empty(); +} + +#endif diff --git a/Engine/lib/libtheora/lib/dec/x86_vc/mmxstate.c b/Engine/lib/libtheora/lib/dec/x86_vc/mmxstate.c new file mode 100644 index 000000000..526ef53f3 --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/x86_vc/mmxstate.c @@ -0,0 +1,189 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2008 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: mmxstate.c 15400 2008-10-15 12:10:58Z tterribe $ + + ********************************************************************/ + +/* ------------------------------------------------------------------------ + MMX acceleration of complete fragment reconstruction algorithm. + Originally written by Rudolf Marek. + + Conversion to MSC intrinsics by Nils Pipenbrinck. + ---------------------------------------------------------------------*/ +#if defined(USE_ASM) + +#include "../../internal.h" +#include "../idct.h" +#include "x86int.h" +#include + +static const unsigned char OC_FZIG_ZAGMMX[64]= +{ + 0, 8, 1, 2, 9,16,24,17, + 10, 3,32,11,18,25, 4,12, + 5,26,19,40,33,34,41,48, + 27, 6,13,20,28,21,14, 7, + 56,49,42,35,43,50,57,36, + 15,22,29,30,23,44,37,58, + 51,59,38,45,52,31,60,53, + 46,39,47,54,61,62,55,63 +}; + +/* Fill a block with value */ +static __inline void loc_fill_mmx_value (__m64 * _dst, __m64 _value){ + __m64 t = _value; + _dst[0] = t; _dst[1] = t; _dst[2] = t; _dst[3] = t; + _dst[4] = t; _dst[5] = t; _dst[6] = t; _dst[7] = t; + _dst[8] = t; _dst[9] = t; _dst[10] = t; _dst[11] = t; + _dst[12] = t; _dst[13] = t; _dst[14] = t; _dst[15] = t; +} + +/* copy a block of 8 byte elements using different strides */ +static __inline void loc_blockcopy_mmx (unsigned char * _dst, int _dst_ystride, + unsigned char * _src, int _src_ystride){ + __m64 a,b,c,d,e,f,g,h; + a = *(__m64*)(_src + 0 * _src_ystride); + b = *(__m64*)(_src + 1 * _src_ystride); + c = *(__m64*)(_src + 2 * _src_ystride); + d = *(__m64*)(_src + 3 * _src_ystride); + e = *(__m64*)(_src + 4 * _src_ystride); + f = *(__m64*)(_src + 5 * _src_ystride); + g = *(__m64*)(_src + 6 * _src_ystride); + h = *(__m64*)(_src + 7 * _src_ystride); + *(__m64*)(_dst + 0 * _dst_ystride) = a; + *(__m64*)(_dst + 1 * _dst_ystride) = b; + *(__m64*)(_dst + 2 * _dst_ystride) = c; + *(__m64*)(_dst + 3 * _dst_ystride) = d; + *(__m64*)(_dst + 4 * _dst_ystride) = e; + *(__m64*)(_dst + 5 * _dst_ystride) = f; + *(__m64*)(_dst + 6 * _dst_ystride) = g; + *(__m64*)(_dst + 7 * _dst_ystride) = h; +} + +void oc_state_frag_recon_mmx(oc_theora_state *_state,const oc_fragment *_frag, + int _pli,ogg_int16_t _dct_coeffs[128],int _last_zzi,int _ncoefs, + ogg_uint16_t _dc_iquant,const ogg_uint16_t _ac_iquant[64]){ + ogg_int16_t __declspec(align(16)) res_buf[64]; + int dst_framei; + int dst_ystride; + int zzi; + /*_last_zzi is subtly different from an actual count of the number of + coefficients we decoded for this block. + It contains the value of zzi BEFORE the final token in the block was + decoded. + In most cases this is an EOB token (the continuation of an EOB run from a + previous block counts), and so this is the same as the coefficient count. + However, in the case that the last token was NOT an EOB token, but filled + the block up with exactly 64 coefficients, _last_zzi will be less than 64. + Provided the last token was not a pure zero run, the minimum value it can + be is 46, and so that doesn't affect any of the cases in this routine. + However, if the last token WAS a pure zero run of length 63, then _last_zzi + will be 1 while the number of coefficients decoded is 64. + Thus, we will trigger the following special case, where the real + coefficient count would not. + Note also that a zero run of length 64 will give _last_zzi a value of 0, + but we still process the DC coefficient, which might have a non-zero value + due to DC prediction. + Although convoluted, this is arguably the correct behavior: it allows us to + dequantize fewer coefficients and use a smaller transform when the block + ends with a long zero run instead of a normal EOB token. + It could be smarter... multiple separate zero runs at the end of a block + will fool it, but an encoder that generates these really deserves what it + gets. + Needless to say we inherited this approach from VP3.*/ + /*Special case only having a DC component.*/ + if(_last_zzi<2){ + __m64 p; + /*Why is the iquant product rounded in this case and no others? Who knows.*/ + p = _m_from_int((ogg_int32_t)_frag->dc*_dc_iquant+15>>5); + /* broadcast 16 bits into all 4 mmx subregisters */ + p = _m_punpcklwd (p,p); + p = _m_punpckldq (p,p); + loc_fill_mmx_value ((__m64 *)res_buf, p); + } + else{ + /*Then, fill in the remainder of the coefficients with 0's, and perform + the iDCT.*/ + /*First zero the buffer.*/ + /*On K7, etc., this could be replaced with movntq and sfence.*/ + loc_fill_mmx_value ((__m64 *)res_buf, _mm_setzero_si64()); + + res_buf[0]=(ogg_int16_t)((ogg_int32_t)_frag->dc*_dc_iquant); + /*This is planned to be rewritten in MMX.*/ + for(zzi=1;zzi<_ncoefs;zzi++) + { + int ci; + ci=OC_FZIG_ZAG[zzi]; + res_buf[OC_FZIG_ZAGMMX[zzi]]=(ogg_int16_t)((ogg_int32_t)_dct_coeffs[zzi]* + _ac_iquant[ci]); + } + + if(_last_zzi<10){ + oc_idct8x8_10_mmx(res_buf); + } + else { + oc_idct8x8_mmx(res_buf); + } + } + /*Fill in the target buffer.*/ + dst_framei=_state->ref_frame_idx[OC_FRAME_SELF]; + dst_ystride=_state->ref_frame_bufs[dst_framei][_pli].stride; + /*For now ystride values in all ref frames assumed to be equal.*/ + if(_frag->mbmode==OC_MODE_INTRA){ + oc_frag_recon_intra_mmx(_frag->buffer[dst_framei],dst_ystride,res_buf); + } + else{ + int ref_framei; + int ref_ystride; + int mvoffsets[2]; + ref_framei=_state->ref_frame_idx[OC_FRAME_FOR_MODE[_frag->mbmode]]; + ref_ystride=_state->ref_frame_bufs[ref_framei][_pli].stride; + if(oc_state_get_mv_offsets(_state,mvoffsets,_frag->mv[0], + _frag->mv[1],ref_ystride,_pli)>1){ + oc_frag_recon_inter2_mmx(_frag->buffer[dst_framei],dst_ystride, + _frag->buffer[ref_framei]+mvoffsets[0],ref_ystride, + _frag->buffer[ref_framei]+mvoffsets[1],ref_ystride,res_buf); + } + else{ + oc_frag_recon_inter_mmx(_frag->buffer[dst_framei],dst_ystride, + _frag->buffer[ref_framei]+mvoffsets[0],ref_ystride,res_buf); + } + } + + _mm_empty(); +} + + +void oc_state_frag_copy_mmx(const oc_theora_state *_state,const int *_fragis, + int _nfragis,int _dst_frame,int _src_frame,int _pli){ + const int *fragi; + const int *fragi_end; + int dst_framei; + int dst_ystride; + int src_framei; + int src_ystride; + dst_framei=_state->ref_frame_idx[_dst_frame]; + src_framei=_state->ref_frame_idx[_src_frame]; + dst_ystride=_state->ref_frame_bufs[dst_framei][_pli].stride; + src_ystride=_state->ref_frame_bufs[src_framei][_pli].stride; + fragi_end=_fragis+_nfragis; + for(fragi=_fragis;fragifrags+*fragi; + loc_blockcopy_mmx (frag->buffer[dst_framei], dst_ystride, + frag->buffer[src_framei], src_ystride); + } + _m_empty(); +} + +#endif diff --git a/Engine/lib/libtheora/lib/dec/x86_vc/x86int.h b/Engine/lib/libtheora/lib/dec/x86_vc/x86int.h new file mode 100644 index 000000000..be5016100 --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/x86_vc/x86int.h @@ -0,0 +1,49 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: x86int.h 15400 2008-10-15 12:10:58Z tterribe $ + + ********************************************************************/ + +#if !defined(_x86_x86int_vc_H) +# define _x86_x86int_vc_H (1) +# include "../../internal.h" + +void oc_state_vtable_init_x86(oc_theora_state *_state); + +void oc_frag_recon_intra_mmx(unsigned char *_dst,int _dst_ystride, + const ogg_int16_t *_residue); + +void oc_frag_recon_inter_mmx(unsigned char *_dst,int _dst_ystride, + const unsigned char *_src,int _src_ystride,const ogg_int16_t *_residue); + +void oc_frag_recon_inter2_mmx(unsigned char *_dst,int _dst_ystride, + const unsigned char *_src1,int _src1_ystride,const unsigned char *_src2, + int _src2_ystride,const ogg_int16_t *_residue); + +void oc_state_frag_copy_mmx(const oc_theora_state *_state,const int *_fragis, + int _nfragis,int _dst_frame,int _src_frame,int _pli); + +void oc_restore_fpu_mmx(void); + +void oc_state_frag_recon_mmx(oc_theora_state *_state,const oc_fragment *_frag, + int _pli,ogg_int16_t _dct_coeffs[128],int _last_zzi,int _ncoefs, + ogg_uint16_t _dc_iquant,const ogg_uint16_t _ac_iquant[64]); + +void oc_idct8x8_mmx(ogg_int16_t _y[64]); +void oc_idct8x8_10_mmx(ogg_int16_t _y[64]); + +void oc_state_loop_filter_frag_rows_mmx(oc_theora_state *_state,int *_bv, + int _refi,int _pli,int _fragy0,int _fragy_end); + +#endif diff --git a/Engine/lib/libtheora/lib/dec/x86_vc/x86state.c b/Engine/lib/libtheora/lib/dec/x86_vc/x86state.c new file mode 100644 index 000000000..735390823 --- /dev/null +++ b/Engine/lib/libtheora/lib/dec/x86_vc/x86state.c @@ -0,0 +1,41 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2008 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: x86state.c 15427 2008-10-21 02:36:19Z xiphmont $ + + ********************************************************************/ + +#if defined(USE_ASM) + +#include "x86int.h" +#include "../../cpu.c" + +void oc_state_vtable_init_x86(oc_theora_state *_state){ + _state->cpu_flags=oc_cpu_flags_get(); + + /* fill with defaults */ + oc_state_vtable_init_c(_state); + + /* patch MMX functions */ + if(_state->cpu_flags&OC_CPU_X86_MMX){ + _state->opt_vtable.frag_recon_intra=oc_frag_recon_intra_mmx; + _state->opt_vtable.frag_recon_inter=oc_frag_recon_inter_mmx; + _state->opt_vtable.frag_recon_inter2=oc_frag_recon_inter2_mmx; + _state->opt_vtable.restore_fpu=oc_restore_fpu_mmx; + _state->opt_vtable.state_frag_copy=oc_state_frag_copy_mmx; + _state->opt_vtable.state_frag_recon=oc_state_frag_recon_mmx; + _state->opt_vtable.state_loop_filter_frag_rows=oc_state_loop_filter_frag_rows_mmx; + } +} + +#endif diff --git a/Engine/lib/libtheora/lib/enc/block_inline.h b/Engine/lib/libtheora/lib/enc/block_inline.h new file mode 100644 index 000000000..008977095 --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/block_inline.h @@ -0,0 +1,37 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: block_inline.h 14059 2007-10-28 23:43:27Z xiphmont $ + + ********************************************************************/ + +#include "codec_internal.h" + +static const ogg_int32_t MBOrderMap[4] = { 0, 2, 3, 1 }; +static const ogg_int32_t BlockOrderMap1[4][4] = { + { 0, 1, 3, 2 }, + { 0, 2, 3, 1 }, + { 0, 2, 3, 1 }, + { 3, 2, 0, 1 } +}; + +static ogg_int32_t QuadMapToIndex1( ogg_int32_t (*BlockMap)[4][4], + ogg_uint32_t SB, ogg_uint32_t MB, + ogg_uint32_t B ){ + return BlockMap[SB][MBOrderMap[MB]][BlockOrderMap1[MB][B]]; +} + +static ogg_int32_t QuadMapToMBTopLeft( ogg_int32_t (*BlockMap)[4][4], + ogg_uint32_t SB, ogg_uint32_t MB ){ + return BlockMap[SB][MBOrderMap[MB]][0]; +} diff --git a/Engine/lib/libtheora/lib/enc/blockmap.c b/Engine/lib/libtheora/lib/enc/blockmap.c new file mode 100644 index 000000000..5f3478fc2 --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/blockmap.c @@ -0,0 +1,99 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: blockmap.c 14059 2007-10-28 23:43:27Z xiphmont $ + + ********************************************************************/ + +#include "codec_internal.h" + +static void CreateMapping ( ogg_int32_t (*BlockMap)[4][4], + ogg_uint32_t FirstSB, + ogg_uint32_t FirstFrag, ogg_uint32_t HFrags, + ogg_uint32_t VFrags ){ + ogg_uint32_t i, j = 0; + ogg_uint32_t xpos; + ogg_uint32_t ypos; + ogg_uint32_t SBrow, SBcol; + ogg_uint32_t SBRows, SBCols; + ogg_uint32_t MB, B; + + ogg_uint32_t SB=FirstSB; + ogg_uint32_t FragIndex=FirstFrag; + + /* Set Super-Block dimensions */ + SBRows = VFrags/4 + ( VFrags%4 ? 1 : 0 ); + SBCols = HFrags/4 + ( HFrags%4 ? 1 : 0 ); + + /* Map each Super-Block */ + for ( SBrow=0; SBrow + FragIndex */ + + /* Coded flag arrays and counters for them */ + unsigned char *SBCodedFlags; + unsigned char *SBFullyFlags; + unsigned char *MBCodedFlags; + unsigned char *MBFullyFlags; + + /**********************************************************************/ + ogg_uint32_t EOB_Run; + + COORDINATE *FragCoordinates; + MOTION_VECTOR MVector; + ogg_int32_t ReconPtr2Offset; /* Offset for second reconstruction + in half pixel MC */ + Q_LIST_ENTRY *quantized_list; + ogg_int16_t *ReconDataBuffer; + Q_LIST_ENTRY InvLastIntraDC; + Q_LIST_ENTRY InvLastInterDC; + Q_LIST_ENTRY LastIntraDC; + Q_LIST_ENTRY LastInterDC; + + ogg_uint32_t BlocksToDecode; /* Blocks to be decoded this frame */ + ogg_uint32_t DcHuffChoice; /* Huffman table selection variables */ + unsigned char ACHuffChoice; + ogg_uint32_t QuadMBListIndex; + + ogg_int32_t ByteCount; + + ogg_uint32_t bit_pattern; + unsigned char bits_so_far; + unsigned char NextBit; + ogg_int32_t BitsLeft; + + ogg_int16_t *DequantBuffer; + + ogg_int32_t fp_quant_InterUV_coeffs[64]; + ogg_int32_t fp_quant_InterUV_round[64]; + ogg_int32_t fp_ZeroBinSize_InterUV[64]; + + ogg_int16_t *TmpReconBuffer; + ogg_int16_t *TmpDataBuffer; + + /* Loop filter bounding values */ + ogg_int16_t FiltBoundingValue[256]; + + /* Naming convention for all quant matrices and related data structures: + * Fields containing "Inter" in their name are for Inter frames, the + * rest is Intra. */ + + /* Dequantiser and rounding tables */ + ogg_uint16_t *QThreshTable; + Q_LIST_ENTRY dequant_Y_coeffs[64]; + Q_LIST_ENTRY dequant_U_coeffs[64]; + Q_LIST_ENTRY dequant_V_coeffs[64]; + Q_LIST_ENTRY dequant_InterY_coeffs[64]; + Q_LIST_ENTRY dequant_InterU_coeffs[64]; + Q_LIST_ENTRY dequant_InterV_coeffs[64]; + + Q_LIST_ENTRY *dequant_coeffs; /* currently active quantizer */ + unsigned int zigzag_index[64]; + + HUFF_ENTRY *HuffRoot_VP3x[NUM_HUFF_TABLES]; + ogg_uint32_t *HuffCodeArray_VP3x[NUM_HUFF_TABLES]; + unsigned char *HuffCodeLengthArray_VP3x[NUM_HUFF_TABLES]; + const unsigned char *ExtraBitLengths_VP3x; + + th_quant_info quant_info; + oc_quant_tables quant_tables[2][3]; + + /* Quantiser and rounding tables */ + /* this is scheduled to be replaced a new mechanism + that will simply reuse the dequantizer information. */ + ogg_int32_t fp_quant_Y_coeffs[64]; /* used in reiniting quantizers */ + ogg_int32_t fp_quant_U_coeffs[64]; + ogg_int32_t fp_quant_V_coeffs[64]; + ogg_int32_t fp_quant_Inter_Y_coeffs[64]; + ogg_int32_t fp_quant_Inter_U_coeffs[64]; + ogg_int32_t fp_quant_Inter_V_coeffs[64]; + + ogg_int32_t fp_quant_Y_round[64]; + ogg_int32_t fp_quant_U_round[64]; + ogg_int32_t fp_quant_V_round[64]; + ogg_int32_t fp_quant_Inter_Y_round[64]; + ogg_int32_t fp_quant_Inter_U_round[64]; + ogg_int32_t fp_quant_Inter_V_round[64]; + + ogg_int32_t fp_ZeroBinSize_Y[64]; + ogg_int32_t fp_ZeroBinSize_U[64]; + ogg_int32_t fp_ZeroBinSize_V[64]; + ogg_int32_t fp_ZeroBinSize_Inter_Y[64]; + ogg_int32_t fp_ZeroBinSize_Inter_U[64]; + ogg_int32_t fp_ZeroBinSize_Inter_V[64]; + + ogg_int32_t *fquant_coeffs; + ogg_int32_t *fquant_round; + ogg_int32_t *fquant_ZbSize; + + /* Predictor used in choosing entropy table for decoding block patterns. */ + unsigned char BlockPatternPredictor; + + short Modifier[4][512]; + short *ModifierPointer[4]; + + unsigned char *DataOutputInPtr; + + DspFunctions dsp; /* Selected functions for this platform */ + +}; + +/* Encoder (Compressor) instance -- installed in a theora_state */ +typedef struct CP_INSTANCE { + /*This structure must be first. + It contains entry points accessed by the decoder library's API wrapper, and + is the only assumption that library makes about our internal format.*/ + oc_state_dispatch_vtbl dispatch_vtbl; + + /* Compressor Configuration */ + SCAN_CONFIG_DATA ScanConfig; + CONFIG_TYPE2 Configuration; + int GoldenFrameEnabled; + int InterPrediction; + int MotionCompensation; + + ogg_uint32_t LastKeyFrame ; + ogg_int32_t DropCount ; + ogg_int32_t MaxConsDroppedFrames ; + ogg_int32_t DropFrameTriggerBytes; + int DropFrameCandidate; + + /* Compressor Statistics */ + double TotErrScore; + ogg_int64_t KeyFrameCount; /* Count of key frames. */ + ogg_int64_t TotKeyFrameBytes; + ogg_uint32_t LastKeyFrameSize; + ogg_uint32_t PriorKeyFrameSize[KEY_FRAME_CONTEXT]; + ogg_uint32_t PriorKeyFrameDistance[KEY_FRAME_CONTEXT]; + ogg_int32_t FrameQuality[6]; + int DecoderErrorCode; /* Decoder error flag. */ + ogg_int32_t ThreshMapThreshold; + ogg_int32_t TotalMotionScore; + ogg_int64_t TotalByteCount; + ogg_int32_t FixedQ; + + /* Frame Statistics */ + signed char InterCodeCount; + ogg_int64_t CurrentFrame; + ogg_int64_t CarryOver ; + ogg_uint32_t LastFrameSize; + ogg_uint32_t FrameBitCount; + int ThisIsFirstFrame; + int ThisIsKeyFrame; + + ogg_int32_t MotionScore; + ogg_uint32_t RegulationBlocks; + ogg_int32_t RecoveryMotionScore; + int RecoveryBlocksAdded ; + double ProportionRecBlocks; + double MaxRecFactor ; + + /* Rate Targeting variables. */ + ogg_uint32_t ThisFrameTargetBytes; + double BpbCorrectionFactor; + + /* Up regulation variables */ + ogg_uint32_t FinalPassLastPos; /* Used to regulate a final + unrestricted high quality + pass. */ + ogg_uint32_t LastEndSB; /* Where we were in the loop + last time. */ + ogg_uint32_t ResidueLastEndSB; /* Where we were in the residue + update loop last time. */ + + /* Controlling Block Selection */ + ogg_uint32_t MVChangeFactor; + ogg_uint32_t FourMvChangeFactor; + ogg_uint32_t MinImprovementForNewMV; + ogg_uint32_t ExhaustiveSearchThresh; + ogg_uint32_t MinImprovementForFourMV; + ogg_uint32_t FourMVThreshold; + + /* Module shared data structures. */ + ogg_int32_t frame_target_rate; + ogg_int32_t BaseLineFrameTargetRate; + ogg_int32_t min_blocks_per_frame; + ogg_uint32_t tot_bytes_old; + + /*********************************************************************/ + /* Frames Used in the selecetive convolution filtering of the Y plane. */ + unsigned char *ConvDestBuffer; + YUV_BUFFER_ENTRY *yuv0ptr; + YUV_BUFFER_ENTRY *yuv1ptr; + /*********************************************************************/ + + /*********************************************************************/ + /* Token Buffers */ + ogg_uint32_t *OptimisedTokenListEb; /* Optimised token list extra bits */ + unsigned char *OptimisedTokenList; /* Optimised token list. */ + unsigned char *OptimisedTokenListHi; /* Optimised token list huffman + table index */ + + unsigned char *OptimisedTokenListPl; /* Plane to which the token + belongs Y = 0 or UV = 1 */ + ogg_int32_t OptimisedTokenCount; /* Count of Optimized tokens */ + ogg_uint32_t RunHuffIndex; /* Huffman table in force at + the start of a run */ + ogg_uint32_t RunPlaneIndex; /* The plane (Y=0 UV=1) to + which the first token in + an EOB run belonged. */ + + + ogg_uint32_t TotTokenCount; + ogg_int32_t TokensToBeCoded; + ogg_int32_t TokensCoded; + /********************************************************************/ + + /* SuperBlock, MacroBLock and Fragment Information */ + /* Coded flag arrays and counters for them */ + unsigned char *PartiallyCodedFlags; + unsigned char *PartiallyCodedMbPatterns; + unsigned char *UncodedMbFlags; + + unsigned char *extra_fragments; /* extra updates not + recommended by pre-processor */ + ogg_int16_t *OriginalDC; + + ogg_uint32_t *FragmentLastQ; /* Array used to keep track of + quality at which each + fragment was last + updated. */ + unsigned char *FragTokens; + ogg_uint32_t *FragTokenCounts; /* Number of tokens per fragment */ + + ogg_uint32_t *RunHuffIndices; + ogg_uint32_t *LastCodedErrorScore; + ogg_uint32_t *ModeList; + MOTION_VECTOR *MVList; + + unsigned char *BlockCodedFlags; + + ogg_uint32_t MvListCount; + ogg_uint32_t ModeListCount; + + + unsigned char *DataOutputBuffer; + /*********************************************************************/ + + ogg_uint32_t RunLength; + ogg_uint32_t MaxBitTarget; /* Cut off target for rate capping */ + double BitRateCapFactor; /* Factor relating delta frame target + to cut off target. */ + + unsigned char MBCodingMode; /* Coding mode flags */ + + ogg_int32_t MVPixelOffsetY[MAX_SEARCH_SITES]; + ogg_uint32_t InterTripOutThresh; + unsigned char MVEnabled; + ogg_uint32_t MotionVectorSearchCount; + ogg_uint32_t FrameMVSearcOunt; + ogg_int32_t MVSearchSteps; + ogg_int32_t MVOffsetX[MAX_SEARCH_SITES]; + ogg_int32_t MVOffsetY[MAX_SEARCH_SITES]; + ogg_int32_t HalfPixelRef2Offset[9]; /* Offsets for half pixel + compensation */ + signed char HalfPixelXOffset[9]; /* Half pixel MV offsets for X */ + signed char HalfPixelYOffset[9]; /* Half pixel MV offsets for Y */ + + ogg_uint32_t bit_pattern ; + unsigned char bits_so_far ; + ogg_uint32_t lastval ; + ogg_uint32_t lastrun ; + + Q_LIST_ENTRY *quantized_list; + + MOTION_VECTOR MVector; + ogg_uint32_t TempBitCount; + ogg_int16_t *DCT_codes; /* Buffer that stores the result of + Forward DCT */ + ogg_int16_t *DCTDataBuffer; /* Input data buffer for Forward DCT */ + + /* Motion compensation related variables */ + ogg_uint32_t MvMaxExtent; + + double QTargetModifier[Q_TABLE_SIZE]; + + /* instances (used for reconstructing buffers and to hold tokens etc.) */ + PP_INSTANCE pp; /* preprocessor */ + PB_INSTANCE pb; /* playback */ + + /* ogg bitpacker for use in packet coding, other API state */ + oggpack_buffer *oggbuffer; + int readyflag; + int packetflag; + int doneflag; + + DspFunctions dsp; /* Selected functions for this platform */ + +} CP_INSTANCE; + +#define clamp255(x) ((unsigned char)((((x)<0)-1) & ((x) | -((x)>255)))) + +extern void ConfigurePP( PP_INSTANCE *ppi, int Level ) ; +extern ogg_uint32_t YUVAnalyseFrame( PP_INSTANCE *ppi, + ogg_uint32_t * KFIndicator ); + +extern void ClearPPInstance(PP_INSTANCE *ppi); +extern void InitPPInstance(PP_INSTANCE *ppi, DspFunctions *funcs); +extern void InitPBInstance(PB_INSTANCE *pbi); +extern void ClearPBInstance(PB_INSTANCE *pbi); + +extern void IDct1( Q_LIST_ENTRY * InputData, + ogg_int16_t *QuantMatrix, + ogg_int16_t * OutputData ); + +extern void ReconIntra( PB_INSTANCE *pbi, unsigned char * ReconPtr, + ogg_int16_t * ChangePtr, ogg_uint32_t LineStep ); + +extern void ReconInter( PB_INSTANCE *pbi, unsigned char * ReconPtr, + unsigned char * RefPtr, ogg_int16_t * ChangePtr, + ogg_uint32_t LineStep ) ; + +extern void ReconInterHalfPixel2( PB_INSTANCE *pbi, unsigned char * ReconPtr, + unsigned char * RefPtr1, + unsigned char * RefPtr2, + ogg_int16_t * ChangePtr, + ogg_uint32_t LineStep ) ; + +extern void SetupLoopFilter(PB_INSTANCE *pbi); +extern void CopyBlock(unsigned char *src, + unsigned char *dest, + unsigned int srcstride); +extern void LoopFilter(PB_INSTANCE *pbi); +extern void ReconRefFrames (PB_INSTANCE *pbi); +extern void ExpandToken( Q_LIST_ENTRY * ExpandedBlock, + unsigned char * CoeffIndex, ogg_uint32_t Token, + ogg_int32_t ExtraBits ); +extern void ClearDownQFragData(PB_INSTANCE *pbi); + +extern void select_quantiser (PB_INSTANCE *pbi, int type); + +extern void quantize( PB_INSTANCE *pbi, + ogg_int16_t * DCT_block, + Q_LIST_ENTRY * quantized_list); +extern void UpdateQ( PB_INSTANCE *pbi, int NewQIndex ); +extern void UpdateQC( CP_INSTANCE *cpi, ogg_uint32_t NewQ ); +extern void fdct_short ( ogg_int16_t * InputData, ogg_int16_t * OutputData ); +extern ogg_uint32_t DPCMTokenizeBlock (CP_INSTANCE *cpi, + ogg_int32_t FragIndex); +extern void TransformQuantizeBlock (CP_INSTANCE *cpi, ogg_int32_t FragIndex, + ogg_uint32_t PixelsPerLine ) ; +extern void ClearFragmentInfo(PB_INSTANCE * pbi); +extern void InitFragmentInfo(PB_INSTANCE * pbi); +extern void ClearFrameInfo(PB_INSTANCE * pbi); +extern void InitFrameInfo(PB_INSTANCE * pbi, unsigned int FrameSize); +extern void InitializeFragCoordinates(PB_INSTANCE *pbi); +extern void InitFrameDetails(PB_INSTANCE *pbi); +extern void WriteQTables(PB_INSTANCE *pbi,oggpack_buffer *opb); +extern void InitQTables( PB_INSTANCE *pbi ); +extern void quant_tables_init( PB_INSTANCE *pbi, const th_quant_info *qinfo); +extern void InitHuffmanSet( PB_INSTANCE *pbi ); +extern void ClearHuffmanSet( PB_INSTANCE *pbi ); +extern int ReadHuffmanTrees(codec_setup_info *ci, oggpack_buffer *opb); +extern void WriteHuffmanTrees(HUFF_ENTRY *HuffRoot[NUM_HUFF_TABLES], + oggpack_buffer *opb); +extern void InitHuffmanTrees(PB_INSTANCE *pbi, const codec_setup_info *ci); +extern void ClearHuffmanTrees(HUFF_ENTRY *HuffRoot[NUM_HUFF_TABLES]); +extern int ReadFilterTables(codec_setup_info *ci, oggpack_buffer *opb); +extern void QuadDecodeDisplayFragments ( PB_INSTANCE *pbi ); +extern void PackAndWriteDFArray( CP_INSTANCE *cpi ); +extern void UpdateFragQIndex(PB_INSTANCE *pbi); +extern void PostProcess(PB_INSTANCE *pbi); +extern void InitMotionCompensation ( CP_INSTANCE *cpi ); +extern ogg_uint32_t GetMBIntraError (CP_INSTANCE *cpi, ogg_uint32_t FragIndex, + ogg_uint32_t PixelsPerLine ) ; +extern ogg_uint32_t GetMBInterError (CP_INSTANCE *cpi, + unsigned char * SrcPtr, + unsigned char * RefPtr, + ogg_uint32_t FragIndex, + ogg_int32_t LastXMV, + ogg_int32_t LastYMV, + ogg_uint32_t PixelsPerLine ) ; +extern void WriteFrameHeader( CP_INSTANCE *cpi) ; +extern ogg_uint32_t GetMBMVInterError (CP_INSTANCE *cpi, + unsigned char * RefFramePtr, + ogg_uint32_t FragIndex, + ogg_uint32_t PixelsPerLine, + ogg_int32_t *MVPixelOffset, + MOTION_VECTOR *MV ); +extern ogg_uint32_t GetMBMVExhaustiveSearch (CP_INSTANCE *cpi, + unsigned char * RefFramePtr, + ogg_uint32_t FragIndex, + ogg_uint32_t PixelsPerLine, + MOTION_VECTOR *MV ); +extern ogg_uint32_t GetFOURMVExhaustiveSearch (CP_INSTANCE *cpi, + unsigned char * RefFramePtr, + ogg_uint32_t FragIndex, + ogg_uint32_t PixelsPerLine, + MOTION_VECTOR *MV ) ; +extern ogg_uint32_t EncodeData(CP_INSTANCE *cpi); +extern ogg_uint32_t PickIntra( CP_INSTANCE *cpi, + ogg_uint32_t SBRows, + ogg_uint32_t SBCols); +extern ogg_uint32_t PickModes(CP_INSTANCE *cpi, + ogg_uint32_t SBRows, + ogg_uint32_t SBCols, + ogg_uint32_t PixelsPerLine, + ogg_uint32_t *InterError, + ogg_uint32_t *IntraError); + +extern CODING_MODE FrArrayUnpackMode(PB_INSTANCE *pbi); +extern void CreateBlockMapping ( ogg_int32_t (*BlockMap)[4][4], + ogg_uint32_t YSuperBlocks, + ogg_uint32_t UVSuperBlocks, + ogg_uint32_t HFrags, ogg_uint32_t VFrags ); +extern void UpRegulateDataStream (CP_INSTANCE *cpi, ogg_uint32_t RegulationQ, + ogg_int32_t RecoveryBlocks ) ; +extern void RegulateQ( CP_INSTANCE *cpi, ogg_int32_t UpdateScore ); +extern void CopyBackExtraFrags(CP_INSTANCE *cpi); + +extern void UpdateUMVBorder( PB_INSTANCE *pbi, + unsigned char * DestReconPtr ); +extern void PInitFrameInfo(PP_INSTANCE * ppi); + +extern double GetEstimatedBpb( CP_INSTANCE *cpi, ogg_uint32_t TargetQ ); +extern void ClearTmpBuffers(PB_INSTANCE * pbi); +extern void InitTmpBuffers(PB_INSTANCE * pbi); +extern void ScanYUVInit( PP_INSTANCE * ppi, + SCAN_CONFIG_DATA * ScanConfigPtr); + +#endif /* ENCODER_INTERNAL_H */ diff --git a/Engine/lib/libtheora/lib/enc/dct.c b/Engine/lib/libtheora/lib/enc/dct.c new file mode 100644 index 000000000..29bf8f269 --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/dct.c @@ -0,0 +1,268 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: dct.c 13884 2007-09-22 08:38:10Z giles $ + + ********************************************************************/ + +#include "codec_internal.h" +#include "dsp.h" +#include "../cpu.h" + +static ogg_int32_t xC1S7 = 64277; +static ogg_int32_t xC2S6 = 60547; +static ogg_int32_t xC3S5 = 54491; +static ogg_int32_t xC4S4 = 46341; +static ogg_int32_t xC5S3 = 36410; +static ogg_int32_t xC6S2 = 25080; +static ogg_int32_t xC7S1 = 12785; + +#define SIGNBITDUPPED(X) ((signed )(((X) & 0x80000000)) >> 31) +#define DOROUND(X) ( (SIGNBITDUPPED(X) & (0xffff)) + (X) ) + +static void fdct_short__c ( ogg_int16_t * InputData, ogg_int16_t * OutputData ){ + int loop; + + ogg_int32_t is07, is12, is34, is56; + ogg_int32_t is0734, is1256; + ogg_int32_t id07, id12, id34, id56; + + ogg_int32_t irot_input_x, irot_input_y; + ogg_int32_t icommon_product1; /* Re-used product (c4s4 * (s12 - s56)). */ + ogg_int32_t icommon_product2; /* Re-used product (c4s4 * (d12 + d56)). */ + + ogg_int32_t temp1, temp2; /* intermediate variable for computation */ + + ogg_int32_t InterData[64]; + ogg_int32_t *ip = InterData; + ogg_int16_t * op = OutputData; + for (loop = 0; loop < 8; loop++){ + /* Pre calculate some common sums and differences. */ + is07 = InputData[0] + InputData[7]; + is12 = InputData[1] + InputData[2]; + is34 = InputData[3] + InputData[4]; + is56 = InputData[5] + InputData[6]; + + id07 = InputData[0] - InputData[7]; + id12 = InputData[1] - InputData[2]; + id34 = InputData[3] - InputData[4]; + id56 = InputData[5] - InputData[6]; + + is0734 = is07 + is34; + is1256 = is12 + is56; + + /* Pre-Calculate some common product terms. */ + icommon_product1 = xC4S4*(is12 - is56); + icommon_product1 = DOROUND(icommon_product1); + icommon_product1>>=16; + + icommon_product2 = xC4S4*(id12 + id56); + icommon_product2 = DOROUND(icommon_product2); + icommon_product2>>=16; + + + ip[0] = (xC4S4*(is0734 + is1256)); + ip[0] = DOROUND(ip[0]); + ip[0] >>= 16; + + ip[4] = (xC4S4*(is0734 - is1256)); + ip[4] = DOROUND(ip[4]); + ip[4] >>= 16; + + /* Define inputs to rotation for outputs 2 and 6 */ + irot_input_x = id12 - id56; + irot_input_y = is07 - is34; + + /* Apply rotation for outputs 2 and 6. */ + temp1=xC6S2*irot_input_x; + temp1=DOROUND(temp1); + temp1>>=16; + temp2=xC2S6*irot_input_y; + temp2=DOROUND(temp2); + temp2>>=16; + ip[2] = temp1 + temp2; + + temp1=xC6S2*irot_input_y; + temp1=DOROUND(temp1); + temp1>>=16; + temp2=xC2S6*irot_input_x ; + temp2=DOROUND(temp2); + temp2>>=16; + ip[6] = temp1 -temp2 ; + + /* Define inputs to rotation for outputs 1 and 7 */ + irot_input_x = icommon_product1 + id07; + irot_input_y = -( id34 + icommon_product2 ); + + /* Apply rotation for outputs 1 and 7. */ + + temp1=xC1S7*irot_input_x; + temp1=DOROUND(temp1); + temp1>>=16; + temp2=xC7S1*irot_input_y; + temp2=DOROUND(temp2); + temp2>>=16; + ip[1] = temp1 - temp2; + + temp1=xC7S1*irot_input_x; + temp1=DOROUND(temp1); + temp1>>=16; + temp2=xC1S7*irot_input_y ; + temp2=DOROUND(temp2); + temp2>>=16; + ip[7] = temp1 + temp2 ; + + /* Define inputs to rotation for outputs 3 and 5 */ + irot_input_x = id07 - icommon_product1; + irot_input_y = id34 - icommon_product2; + + /* Apply rotation for outputs 3 and 5. */ + temp1=xC3S5*irot_input_x; + temp1=DOROUND(temp1); + temp1>>=16; + temp2=xC5S3*irot_input_y ; + temp2=DOROUND(temp2); + temp2>>=16; + ip[3] = temp1 - temp2 ; + + temp1=xC5S3*irot_input_x; + temp1=DOROUND(temp1); + temp1>>=16; + temp2=xC3S5*irot_input_y; + temp2=DOROUND(temp2); + temp2>>=16; + ip[5] = temp1 + temp2; + + /* Increment data pointer for next row. */ + InputData += 8 ; + ip += 8; /* advance pointer to next row */ + + } + + + /* Performed DCT on rows, now transform the columns */ + ip = InterData; + for (loop = 0; loop < 8; loop++){ + /* Pre calculate some common sums and differences. */ + is07 = ip[0 * 8] + ip[7 * 8]; + is12 = ip[1 * 8] + ip[2 * 8]; + is34 = ip[3 * 8] + ip[4 * 8]; + is56 = ip[5 * 8] + ip[6 * 8]; + + id07 = ip[0 * 8] - ip[7 * 8]; + id12 = ip[1 * 8] - ip[2 * 8]; + id34 = ip[3 * 8] - ip[4 * 8]; + id56 = ip[5 * 8] - ip[6 * 8]; + + is0734 = is07 + is34; + is1256 = is12 + is56; + + /* Pre-Calculate some common product terms. */ + icommon_product1 = xC4S4*(is12 - is56) ; + icommon_product2 = xC4S4*(id12 + id56) ; + icommon_product1 = DOROUND(icommon_product1); + icommon_product2 = DOROUND(icommon_product2); + icommon_product1>>=16; + icommon_product2>>=16; + + + temp1 = xC4S4*(is0734 + is1256) ; + temp2 = xC4S4*(is0734 - is1256) ; + temp1 = DOROUND(temp1); + temp2 = DOROUND(temp2); + temp1>>=16; + temp2>>=16; + op[0*8] = (ogg_int16_t) temp1; + op[4*8] = (ogg_int16_t) temp2; + + /* Define inputs to rotation for outputs 2 and 6 */ + irot_input_x = id12 - id56; + irot_input_y = is07 - is34; + + /* Apply rotation for outputs 2 and 6. */ + temp1=xC6S2*irot_input_x; + temp1=DOROUND(temp1); + temp1>>=16; + temp2=xC2S6*irot_input_y; + temp2=DOROUND(temp2); + temp2>>=16; + op[2*8] = (ogg_int16_t) (temp1 + temp2); + + temp1=xC6S2*irot_input_y; + temp1=DOROUND(temp1); + temp1>>=16; + temp2=xC2S6*irot_input_x ; + temp2=DOROUND(temp2); + temp2>>=16; + op[6*8] = (ogg_int16_t) (temp1 -temp2) ; + + /* Define inputs to rotation for outputs 1 and 7 */ + irot_input_x = icommon_product1 + id07; + irot_input_y = -( id34 + icommon_product2 ); + + /* Apply rotation for outputs 1 and 7. */ + temp1=xC1S7*irot_input_x; + temp1=DOROUND(temp1); + temp1>>=16; + temp2=xC7S1*irot_input_y; + temp2=DOROUND(temp2); + temp2>>=16; + op[1*8] = (ogg_int16_t) (temp1 - temp2); + + temp1=xC7S1*irot_input_x; + temp1=DOROUND(temp1); + temp1>>=16; + temp2=xC1S7*irot_input_y ; + temp2=DOROUND(temp2); + temp2>>=16; + op[7*8] = (ogg_int16_t) (temp1 + temp2); + + /* Define inputs to rotation for outputs 3 and 5 */ + irot_input_x = id07 - icommon_product1; + irot_input_y = id34 - icommon_product2; + + /* Apply rotation for outputs 3 and 5. */ + temp1=xC3S5*irot_input_x; + temp1=DOROUND(temp1); + temp1>>=16; + temp2=xC5S3*irot_input_y ; + temp2=DOROUND(temp2); + temp2>>=16; + op[3*8] = (ogg_int16_t) (temp1 - temp2) ; + + temp1=xC5S3*irot_input_x; + temp1=DOROUND(temp1); + temp1>>=16; + temp2=xC3S5*irot_input_y; + temp2=DOROUND(temp2); + temp2>>=16; + op[5*8] = (ogg_int16_t) (temp1 + temp2); + + /* Increment data pointer for next column. */ + ip ++; + op ++; + } +} + +void dsp_dct_init (DspFunctions *funcs, ogg_uint32_t cpu_flags) +{ + funcs->fdct_short = fdct_short__c; + dsp_dct_decode_init(funcs, cpu_flags); + dsp_idct_init(funcs, cpu_flags); +#if defined(USE_ASM) + if (cpu_flags & OC_CPU_X86_MMX) { + dsp_mmx_fdct_init(funcs); + } +#endif +} + diff --git a/Engine/lib/libtheora/lib/enc/dct_decode.c b/Engine/lib/libtheora/lib/enc/dct_decode.c new file mode 100644 index 000000000..e27611610 --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/dct_decode.c @@ -0,0 +1,941 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: dct_decode.c 15400 2008-10-15 12:10:58Z tterribe $ + + ********************************************************************/ + +#include +#include +#include "codec_internal.h" +#include "quant_lookup.h" + + +#define GOLDEN_FRAME_THRESH_Q 50 +#define PUR 8 +#define PU 4 +#define PUL 2 +#define PL 1 +#define HIGHBITDUPPED(X) (((signed short) X) >> 15) + + +static const int ModeUsesMC[MAX_MODES] = { 0, 0, 1, 1, 1, 0, 1, 1 }; + +static void SetupBoundingValueArray_Generic(ogg_int16_t *BoundingValuePtr, + ogg_int32_t FLimit){ + + ogg_int32_t i; + + /* Set up the bounding value array. */ + memset ( BoundingValuePtr, 0, (256*sizeof(*BoundingValuePtr)) ); + for ( i = 0; i < FLimit; i++ ){ + BoundingValuePtr[127-i-FLimit] = (-FLimit+i); + BoundingValuePtr[127-i] = -i; + BoundingValuePtr[127+i] = i; + BoundingValuePtr[127+i+FLimit] = FLimit-i; + } +} + +static void ExpandKFBlock ( PB_INSTANCE *pbi, ogg_int32_t FragmentNumber ){ + ogg_uint32_t ReconPixelsPerLine; + ogg_int32_t ReconPixelIndex; + + /* Select the appropriate inverse Q matrix and line stride */ + if ( FragmentNumber<(ogg_int32_t)pbi->YPlaneFragments ){ + ReconPixelsPerLine = pbi->YStride; + pbi->dequant_coeffs = pbi->dequant_Y_coeffs; + }else if ( FragmentNumber<(ogg_int32_t)(pbi->YPlaneFragments + pbi->UVPlaneFragments) ){ + ReconPixelsPerLine = pbi->UVStride; + pbi->dequant_coeffs = pbi->dequant_U_coeffs; + }else{ + ReconPixelsPerLine = pbi->UVStride; + pbi->dequant_coeffs = pbi->dequant_V_coeffs; + } + + /* Set up pointer into the quantisation buffer. */ + pbi->quantized_list = &pbi->QFragData[FragmentNumber][0]; + + /* Invert quantisation and DCT to get pixel data. */ + switch(pbi->FragCoefEOB[FragmentNumber]){ + case 0:case 1: + IDct1( pbi->quantized_list, pbi->dequant_coeffs, pbi->ReconDataBuffer ); + break; + case 2: case 3: + dsp_IDct3(pbi->dsp, pbi->quantized_list, pbi->dequant_coeffs, pbi->ReconDataBuffer ); + break; + case 4:case 5:case 6:case 7:case 8: case 9:case 10: + dsp_IDct10(pbi->dsp, pbi->quantized_list, pbi->dequant_coeffs, pbi->ReconDataBuffer ); + break; + default: + dsp_IDctSlow(pbi->dsp, pbi->quantized_list, pbi->dequant_coeffs, pbi->ReconDataBuffer ); + } + + /* Convert fragment number to a pixel offset in a reconstruction buffer. */ + ReconPixelIndex = pbi->recon_pixel_index_table[FragmentNumber]; + + /* Get the pixel index for the first pixel in the fragment. */ + dsp_recon_intra8x8 (pbi->dsp, (unsigned char *)(&pbi->ThisFrameRecon[ReconPixelIndex]), + (ogg_int16_t *)pbi->ReconDataBuffer, ReconPixelsPerLine); +} + +static void ExpandBlock ( PB_INSTANCE *pbi, ogg_int32_t FragmentNumber){ + unsigned char *LastFrameRecPtr; /* Pointer into previous frame + reconstruction. */ + unsigned char *LastFrameRecPtr2; /* Pointer into previous frame + reconstruction for 1/2 pixel MC. */ + + ogg_uint32_t ReconPixelsPerLine; /* Pixels per line */ + ogg_int32_t ReconPixelIndex; /* Offset for block into a + reconstruction buffer */ + ogg_int32_t ReconPtr2Offset; /* Offset for second + reconstruction in half pixel + MC */ + ogg_int32_t MVOffset; /* Baseline motion vector offset */ + ogg_int32_t MvShift ; /* Shift to correct to 1/2 or 1/4 pixel */ + ogg_int32_t MvModMask; /* Mask to determine whether 1/2 + pixel is used */ + + /* Get coding mode for this block */ + if ( pbi->FrameType == KEY_FRAME ){ + pbi->CodingMode = CODE_INTRA; + }else{ + /* Get Motion vector and mode for this block. */ + pbi->CodingMode = pbi->FragCodingMethod[FragmentNumber]; + } + + /* Select the appropriate inverse Q matrix and line stride */ + if ( FragmentNumber<(ogg_int32_t)pbi->YPlaneFragments ) { + ReconPixelsPerLine = pbi->YStride; + MvShift = 1; + MvModMask = 0x00000001; + + /* Select appropriate dequantiser matrix. */ + if ( pbi->CodingMode == CODE_INTRA ) + pbi->dequant_coeffs = pbi->dequant_Y_coeffs; + else + pbi->dequant_coeffs = pbi->dequant_InterY_coeffs; + }else{ + ReconPixelsPerLine = pbi->UVStride; + MvShift = 2; + MvModMask = 0x00000003; + + /* Select appropriate dequantiser matrix. */ + if ( pbi->CodingMode == CODE_INTRA ) + if ( FragmentNumber < + (ogg_int32_t)(pbi->YPlaneFragments + pbi->UVPlaneFragments) ) + pbi->dequant_coeffs = pbi->dequant_U_coeffs; + else + pbi->dequant_coeffs = pbi->dequant_V_coeffs; + else + if ( FragmentNumber < + (ogg_int32_t)(pbi->YPlaneFragments + pbi->UVPlaneFragments) ) + pbi->dequant_coeffs = pbi->dequant_InterU_coeffs; + else + pbi->dequant_coeffs = pbi->dequant_InterV_coeffs; + } + + /* Set up pointer into the quantisation buffer. */ + pbi->quantized_list = &pbi->QFragData[FragmentNumber][0]; + + /* Invert quantisation and DCT to get pixel data. */ + switch(pbi->FragCoefEOB[FragmentNumber]){ + case 0:case 1: + IDct1( pbi->quantized_list, pbi->dequant_coeffs, pbi->ReconDataBuffer ); + break; + case 2: case 3: + dsp_IDct3(pbi->dsp, pbi->quantized_list, pbi->dequant_coeffs, pbi->ReconDataBuffer ); + break; + case 4:case 5:case 6:case 7:case 8: case 9:case 10: + dsp_IDct10(pbi->dsp, pbi->quantized_list, pbi->dequant_coeffs, pbi->ReconDataBuffer ); + break; + default: + dsp_IDctSlow(pbi->dsp, pbi->quantized_list, pbi->dequant_coeffs, pbi->ReconDataBuffer ); + } + + /* Convert fragment number to a pixel offset in a reconstruction buffer. */ + ReconPixelIndex = pbi->recon_pixel_index_table[FragmentNumber]; + + /* Action depends on decode mode. */ + if ( pbi->CodingMode == CODE_INTER_NO_MV ){ + /* Inter with no motion vector */ + /* Reconstruct the pixel data using the last frame reconstruction + and change data when the motion vector is (0,0), the recon is + based on the lastframe without loop filtering---- for testing */ + dsp_recon_inter8x8 (pbi->dsp, &pbi->ThisFrameRecon[ReconPixelIndex], + &pbi->LastFrameRecon[ReconPixelIndex], + pbi->ReconDataBuffer, ReconPixelsPerLine); + }else if ( ModeUsesMC[pbi->CodingMode] ) { + /* The mode uses a motion vector. */ + /* Get vector from list */ + pbi->MVector.x = pbi->FragMVect[FragmentNumber].x; + pbi->MVector.y = pbi->FragMVect[FragmentNumber].y; + + /* Work out the base motion vector offset and the 1/2 pixel offset + if any. For the U and V planes the MV specifies 1/4 pixel + accuracy. This is adjusted to 1/2 pixel as follows ( 0->0, + 1/4->1/2, 1/2->1/2, 3/4->1/2 ). */ + MVOffset = 0; + ReconPtr2Offset = 0; + if ( pbi->MVector.x > 0 ){ + MVOffset = pbi->MVector.x >> MvShift; + if ( pbi->MVector.x & MvModMask ) + ReconPtr2Offset += 1; + } else if ( pbi->MVector.x < 0 ) { + MVOffset -= (-pbi->MVector.x) >> MvShift; + if ( (-pbi->MVector.x) & MvModMask ) + ReconPtr2Offset -= 1; + } + + if ( pbi->MVector.y > 0 ){ + MVOffset += (pbi->MVector.y >> MvShift) * ReconPixelsPerLine; + if ( pbi->MVector.y & MvModMask ) + ReconPtr2Offset += ReconPixelsPerLine; + } else if ( pbi->MVector.y < 0 ){ + MVOffset -= ((-pbi->MVector.y) >> MvShift) * ReconPixelsPerLine; + if ( (-pbi->MVector.y) & MvModMask ) + ReconPtr2Offset -= ReconPixelsPerLine; + } + + /* Set up the first of the two reconstruction buffer pointers. */ + if ( pbi->CodingMode==CODE_GOLDEN_MV ) { + LastFrameRecPtr = &pbi->GoldenFrame[ReconPixelIndex] + MVOffset; + }else{ + LastFrameRecPtr = &pbi->LastFrameRecon[ReconPixelIndex] + MVOffset; + } + + /* Set up the second of the two reconstruction pointers. */ + LastFrameRecPtr2 = LastFrameRecPtr + ReconPtr2Offset; + + /* Select the appropriate reconstruction function */ + if ( (int)(LastFrameRecPtr - LastFrameRecPtr2) == 0 ) { + /* Reconstruct the pixel dats from the reference frame and change data + (no half pixel in this case as the two references were the same. */ + dsp_recon_inter8x8 (pbi->dsp, + &pbi->ThisFrameRecon[ReconPixelIndex], + LastFrameRecPtr, pbi->ReconDataBuffer, + ReconPixelsPerLine); + }else{ + /* Fractional pixel reconstruction. */ + /* Note that we only use two pixels per reconstruction even for + the diagonal. */ + dsp_recon_inter8x8_half(pbi->dsp, &pbi->ThisFrameRecon[ReconPixelIndex], + LastFrameRecPtr, LastFrameRecPtr2, + pbi->ReconDataBuffer, ReconPixelsPerLine); + } + } else if ( pbi->CodingMode == CODE_USING_GOLDEN ){ + /* Golden frame with motion vector */ + /* Reconstruct the pixel data using the golden frame + reconstruction and change data */ + dsp_recon_inter8x8 (pbi->dsp, &pbi->ThisFrameRecon[ReconPixelIndex], + &pbi->GoldenFrame[ ReconPixelIndex ], + pbi->ReconDataBuffer, ReconPixelsPerLine); + } else { + /* Simple Intra coding */ + /* Get the pixel index for the first pixel in the fragment. */ + dsp_recon_intra8x8 (pbi->dsp, &pbi->ThisFrameRecon[ReconPixelIndex], + pbi->ReconDataBuffer, ReconPixelsPerLine); + } +} + +static void UpdateUMV_HBorders( PB_INSTANCE *pbi, + unsigned char * DestReconPtr, + ogg_uint32_t PlaneFragOffset ) { + ogg_uint32_t i; + ogg_uint32_t PixelIndex; + + ogg_uint32_t PlaneStride; + ogg_uint32_t BlockVStep; + ogg_uint32_t PlaneFragments; + ogg_uint32_t LineFragments; + ogg_uint32_t PlaneBorderWidth; + + unsigned char *SrcPtr1; + unsigned char *SrcPtr2; + unsigned char *DestPtr1; + unsigned char *DestPtr2; + + /* Work out various plane specific values */ + if ( PlaneFragOffset == 0 ) { + /* Y Plane */ + BlockVStep = (pbi->YStride * + (VFRAGPIXELS - 1)); + PlaneStride = pbi->YStride; + PlaneBorderWidth = UMV_BORDER; + PlaneFragments = pbi->YPlaneFragments; + LineFragments = pbi->HFragments; + }else{ + /* U or V plane. */ + BlockVStep = (pbi->UVStride * + (VFRAGPIXELS - 1)); + PlaneStride = pbi->UVStride; + PlaneBorderWidth = UMV_BORDER / 2; + PlaneFragments = pbi->UVPlaneFragments; + LineFragments = pbi->HFragments / 2; + } + + /* Setup the source and destination pointers for the top and bottom + borders */ + PixelIndex = pbi->recon_pixel_index_table[PlaneFragOffset]; + SrcPtr1 = &DestReconPtr[ PixelIndex - PlaneBorderWidth ]; + DestPtr1 = SrcPtr1 - (PlaneBorderWidth * PlaneStride); + + PixelIndex = pbi->recon_pixel_index_table[PlaneFragOffset + + PlaneFragments - LineFragments] + + BlockVStep; + SrcPtr2 = &DestReconPtr[ PixelIndex - PlaneBorderWidth]; + DestPtr2 = SrcPtr2 + PlaneStride; + + /* Now copy the top and bottom source lines into each line of the + respective borders */ + for ( i = 0; i < PlaneBorderWidth; i++ ) { + memcpy( DestPtr1, SrcPtr1, PlaneStride ); + memcpy( DestPtr2, SrcPtr2, PlaneStride ); + DestPtr1 += PlaneStride; + DestPtr2 += PlaneStride; + } +} + +static void UpdateUMV_VBorders( PB_INSTANCE *pbi, + unsigned char * DestReconPtr, + ogg_uint32_t PlaneFragOffset ){ + ogg_uint32_t i; + ogg_uint32_t PixelIndex; + + ogg_uint32_t PlaneStride; + ogg_uint32_t LineFragments; + ogg_uint32_t PlaneBorderWidth; + ogg_uint32_t PlaneHeight; + + unsigned char *SrcPtr1; + unsigned char *SrcPtr2; + unsigned char *DestPtr1; + unsigned char *DestPtr2; + + /* Work out various plane specific values */ + if ( PlaneFragOffset == 0 ) { + /* Y Plane */ + PlaneStride = pbi->YStride; + PlaneBorderWidth = UMV_BORDER; + LineFragments = pbi->HFragments; + PlaneHeight = pbi->info.height; + }else{ + /* U or V plane. */ + PlaneStride = pbi->UVStride; + PlaneBorderWidth = UMV_BORDER / 2; + LineFragments = pbi->HFragments / 2; + PlaneHeight = pbi->info.height / 2; + } + + /* Setup the source data values and destination pointers for the + left and right edge borders */ + PixelIndex = pbi->recon_pixel_index_table[PlaneFragOffset]; + SrcPtr1 = &DestReconPtr[ PixelIndex ]; + DestPtr1 = &DestReconPtr[ PixelIndex - PlaneBorderWidth ]; + + PixelIndex = pbi->recon_pixel_index_table[PlaneFragOffset + + LineFragments - 1] + + (HFRAGPIXELS - 1); + SrcPtr2 = &DestReconPtr[ PixelIndex ]; + DestPtr2 = &DestReconPtr[ PixelIndex + 1 ]; + + /* Now copy the top and bottom source lines into each line of the + respective borders */ + for ( i = 0; i < PlaneHeight; i++ ) { + memset( DestPtr1, SrcPtr1[0], PlaneBorderWidth ); + memset( DestPtr2, SrcPtr2[0], PlaneBorderWidth ); + SrcPtr1 += PlaneStride; + SrcPtr2 += PlaneStride; + DestPtr1 += PlaneStride; + DestPtr2 += PlaneStride; + } +} + +void UpdateUMVBorder( PB_INSTANCE *pbi, + unsigned char * DestReconPtr ) { + ogg_uint32_t PlaneFragOffset; + + /* Y plane */ + PlaneFragOffset = 0; + UpdateUMV_VBorders( pbi, DestReconPtr, PlaneFragOffset ); + UpdateUMV_HBorders( pbi, DestReconPtr, PlaneFragOffset ); + + /* Then the U and V Planes */ + PlaneFragOffset = pbi->YPlaneFragments; + UpdateUMV_VBorders( pbi, DestReconPtr, PlaneFragOffset ); + UpdateUMV_HBorders( pbi, DestReconPtr, PlaneFragOffset ); + + PlaneFragOffset = pbi->YPlaneFragments + pbi->UVPlaneFragments; + UpdateUMV_VBorders( pbi, DestReconPtr, PlaneFragOffset ); + UpdateUMV_HBorders( pbi, DestReconPtr, PlaneFragOffset ); +} + +static void CopyRecon( PB_INSTANCE *pbi, unsigned char * DestReconPtr, + unsigned char * SrcReconPtr ) { + ogg_uint32_t i; + ogg_uint32_t PlaneLineStep; /* Pixels per line */ + ogg_uint32_t PixelIndex; + + unsigned char *SrcPtr; /* Pointer to line of source image data */ + unsigned char *DestPtr; /* Pointer to line of destination image data */ + + /* Copy over only updated blocks.*/ + + /* First Y plane */ + PlaneLineStep = pbi->YStride; + for ( i = 0; i < pbi->YPlaneFragments; i++ ) { + if ( pbi->display_fragments[i] ) { + PixelIndex = pbi->recon_pixel_index_table[i]; + SrcPtr = &SrcReconPtr[ PixelIndex ]; + DestPtr = &DestReconPtr[ PixelIndex ]; + + dsp_copy8x8 (pbi->dsp, SrcPtr, DestPtr, PlaneLineStep); + } + } + + /* Then U and V */ + PlaneLineStep = pbi->UVStride; + for ( i = pbi->YPlaneFragments; i < pbi->UnitFragments; i++ ) { + if ( pbi->display_fragments[i] ) { + PixelIndex = pbi->recon_pixel_index_table[i]; + SrcPtr = &SrcReconPtr[ PixelIndex ]; + DestPtr = &DestReconPtr[ PixelIndex ]; + + dsp_copy8x8 (pbi->dsp, SrcPtr, DestPtr, PlaneLineStep); + + } + } +} + +static void CopyNotRecon( PB_INSTANCE *pbi, unsigned char * DestReconPtr, + unsigned char * SrcReconPtr ) { + ogg_uint32_t i; + ogg_uint32_t PlaneLineStep; /* Pixels per line */ + ogg_uint32_t PixelIndex; + + unsigned char *SrcPtr; /* Pointer to line of source image data */ + unsigned char *DestPtr; /* Pointer to line of destination image data*/ + + /* Copy over only updated blocks. */ + + /* First Y plane */ + PlaneLineStep = pbi->YStride; + for ( i = 0; i < pbi->YPlaneFragments; i++ ) { + if ( !pbi->display_fragments[i] ) { + PixelIndex = pbi->recon_pixel_index_table[i]; + SrcPtr = &SrcReconPtr[ PixelIndex ]; + DestPtr = &DestReconPtr[ PixelIndex ]; + + dsp_copy8x8 (pbi->dsp, SrcPtr, DestPtr, PlaneLineStep); + } + } + + /* Then U and V */ + PlaneLineStep = pbi->UVStride; + for ( i = pbi->YPlaneFragments; i < pbi->UnitFragments; i++ ) { + if ( !pbi->display_fragments[i] ) { + PixelIndex = pbi->recon_pixel_index_table[i]; + SrcPtr = &SrcReconPtr[ PixelIndex ]; + DestPtr = &DestReconPtr[ PixelIndex ]; + + dsp_copy8x8 (pbi->dsp, SrcPtr, DestPtr, PlaneLineStep); + + } + } +} + +void ExpandToken( Q_LIST_ENTRY * ExpandedBlock, + unsigned char * CoeffIndex, ogg_uint32_t Token, + ogg_int32_t ExtraBits ){ + /* Is the token is a combination run and value token. */ + if ( Token >= DCT_RUN_CATEGORY1 ){ + /* Expand the token and additional bits to a zero run length and + data value. */ + if ( Token < DCT_RUN_CATEGORY2 ) { + /* Decoding method depends on token */ + if ( Token < DCT_RUN_CATEGORY1B ) { + /* Step on by the zero run length */ + *CoeffIndex += (unsigned char)((Token - DCT_RUN_CATEGORY1) + 1); + + /* The extra bit determines the sign. */ + if ( ExtraBits & 0x01 ) + ExpandedBlock[*CoeffIndex] = -1; + else + ExpandedBlock[*CoeffIndex] = 1; + } else if ( Token == DCT_RUN_CATEGORY1B ) { + /* Bits 0-1 determines the zero run length */ + *CoeffIndex += (6 + (ExtraBits & 0x03)); + + /* Bit 2 determines the sign */ + if ( ExtraBits & 0x04 ) + ExpandedBlock[*CoeffIndex] = -1; + else + ExpandedBlock[*CoeffIndex] = 1; + }else{ + /* Bits 0-2 determines the zero run length */ + *CoeffIndex += (10 + (ExtraBits & 0x07)); + + /* Bit 3 determines the sign */ + if ( ExtraBits & 0x08 ) + ExpandedBlock[*CoeffIndex] = -1; + else + ExpandedBlock[*CoeffIndex] = 1; + } + }else{ + /* If token == DCT_RUN_CATEGORY2 we have a single 0 followed by + a value */ + if ( Token == DCT_RUN_CATEGORY2 ){ + /* Step on by the zero run length */ + *CoeffIndex += 1; + + /* Bit 1 determines sign, bit 0 the value */ + if ( ExtraBits & 0x02 ) + ExpandedBlock[*CoeffIndex] = -(2 + (ExtraBits & 0x01)); + else + ExpandedBlock[*CoeffIndex] = 2 + (ExtraBits & 0x01); + }else{ + /* else we have 2->3 zeros followed by a value */ + /* Bit 0 determines the zero run length */ + *CoeffIndex += 2 + (ExtraBits & 0x01); + + /* Bit 2 determines the sign, bit 1 the value */ + if ( ExtraBits & 0x04 ) + ExpandedBlock[*CoeffIndex] = -(2 + ((ExtraBits & 0x02) >> 1)); + else + ExpandedBlock[*CoeffIndex] = 2 + ((ExtraBits & 0x02) >> 1); + } + } + + /* Step on over value */ + *CoeffIndex += 1; + + } else if ( Token == DCT_SHORT_ZRL_TOKEN ) { + /* Token is a ZRL token so step on by the appropriate number of zeros */ + *CoeffIndex += ExtraBits + 1; + } else if ( Token == DCT_ZRL_TOKEN ) { + /* Token is a ZRL token so step on by the appropriate number of zeros */ + *CoeffIndex += ExtraBits + 1; + } else if ( Token < LOW_VAL_TOKENS ) { + /* Token is a small single value token. */ + switch ( Token ) { + case ONE_TOKEN: + ExpandedBlock[*CoeffIndex] = 1; + break; + case MINUS_ONE_TOKEN: + ExpandedBlock[*CoeffIndex] = -1; + break; + case TWO_TOKEN: + ExpandedBlock[*CoeffIndex] = 2; + break; + case MINUS_TWO_TOKEN: + ExpandedBlock[*CoeffIndex] = -2; + break; + } + + /* Step on the coefficient index. */ + *CoeffIndex += 1; + }else{ + /* Token is a larger single value token */ + /* Expand the token and additional bits to a data value. */ + if ( Token < DCT_VAL_CATEGORY3 ) { + /* Offset from LOW_VAL_TOKENS determines value */ + Token = Token - LOW_VAL_TOKENS; + + /* Extra bit determines sign */ + if ( ExtraBits ) + ExpandedBlock[*CoeffIndex] = + -((Q_LIST_ENTRY)(Token + DCT_VAL_CAT2_MIN)); + else + ExpandedBlock[*CoeffIndex] = + (Q_LIST_ENTRY)(Token + DCT_VAL_CAT2_MIN); + } else if ( Token == DCT_VAL_CATEGORY3 ) { + /* Bit 1 determines sign, Bit 0 the value */ + if ( ExtraBits & 0x02 ) + ExpandedBlock[*CoeffIndex] = -(DCT_VAL_CAT3_MIN + (ExtraBits & 0x01)); + else + ExpandedBlock[*CoeffIndex] = DCT_VAL_CAT3_MIN + (ExtraBits & 0x01); + } else if ( Token == DCT_VAL_CATEGORY4 ) { + /* Bit 2 determines sign, Bit 0-1 the value */ + if ( ExtraBits & 0x04 ) + ExpandedBlock[*CoeffIndex] = -(DCT_VAL_CAT4_MIN + (ExtraBits & 0x03)); + else + ExpandedBlock[*CoeffIndex] = DCT_VAL_CAT4_MIN + (ExtraBits & 0x03); + } else if ( Token == DCT_VAL_CATEGORY5 ) { + /* Bit 3 determines sign, Bit 0-2 the value */ + if ( ExtraBits & 0x08 ) + ExpandedBlock[*CoeffIndex] = -(DCT_VAL_CAT5_MIN + (ExtraBits & 0x07)); + else + ExpandedBlock[*CoeffIndex] = DCT_VAL_CAT5_MIN + (ExtraBits & 0x07); + } else if ( Token == DCT_VAL_CATEGORY6 ) { + /* Bit 4 determines sign, Bit 0-3 the value */ + if ( ExtraBits & 0x10 ) + ExpandedBlock[*CoeffIndex] = -(DCT_VAL_CAT6_MIN + (ExtraBits & 0x0F)); + else + ExpandedBlock[*CoeffIndex] = DCT_VAL_CAT6_MIN + (ExtraBits & 0x0F); + } else if ( Token == DCT_VAL_CATEGORY7 ) { + /* Bit 5 determines sign, Bit 0-4 the value */ + if ( ExtraBits & 0x20 ) + ExpandedBlock[*CoeffIndex] = -(DCT_VAL_CAT7_MIN + (ExtraBits & 0x1F)); + else + ExpandedBlock[*CoeffIndex] = DCT_VAL_CAT7_MIN + (ExtraBits & 0x1F); + } else if ( Token == DCT_VAL_CATEGORY8 ) { + /* Bit 9 determines sign, Bit 0-8 the value */ + if ( ExtraBits & 0x200 ) + ExpandedBlock[*CoeffIndex] = -(DCT_VAL_CAT8_MIN + (ExtraBits & 0x1FF)); + else + ExpandedBlock[*CoeffIndex] = DCT_VAL_CAT8_MIN + (ExtraBits & 0x1FF); + } + + /* Step on the coefficient index. */ + *CoeffIndex += 1; + } +} + +void ClearDownQFragData(PB_INSTANCE *pbi){ + ogg_int32_t i; + Q_LIST_ENTRY * QFragPtr; + + for ( i = 0; i < pbi->CodedBlockIndex; i++ ) { + /* Get the linear index for the current fragment. */ + QFragPtr = pbi->QFragData[pbi->CodedBlockList[i]]; + memset(QFragPtr, 0, 64*sizeof(Q_LIST_ENTRY)); + } +} + +static void loop_filter_h(unsigned char * PixelPtr, + ogg_int32_t LineLength, + ogg_int16_t *BoundingValuePtr){ + ogg_int32_t j; + ogg_int32_t FiltVal; + PixelPtr-=2; + + for ( j = 0; j < 8; j++ ){ + FiltVal = + ( PixelPtr[0] ) - + ( PixelPtr[1] * 3 ) + + ( PixelPtr[2] * 3 ) - + ( PixelPtr[3] ); + + FiltVal = *(BoundingValuePtr+((FiltVal + 4) >> 3)); + + PixelPtr[1] = clamp255(PixelPtr[1] + FiltVal); + PixelPtr[2] = clamp255(PixelPtr[2] - FiltVal); + + PixelPtr += LineLength; + } +} + +static void loop_filter_v(unsigned char * PixelPtr, + ogg_int32_t LineLength, + ogg_int16_t *BoundingValuePtr){ + ogg_int32_t j; + ogg_int32_t FiltVal; + PixelPtr -= 2*LineLength; + + for ( j = 0; j < 8; j++ ) { + FiltVal = ( (ogg_int32_t)PixelPtr[0] ) - + ( (ogg_int32_t)PixelPtr[LineLength] * 3 ) + + ( (ogg_int32_t)PixelPtr[2 * LineLength] * 3 ) - + ( (ogg_int32_t)PixelPtr[3 * LineLength] ); + + FiltVal = *(BoundingValuePtr+((FiltVal + 4) >> 3)); + + PixelPtr[LineLength] = clamp255(PixelPtr[LineLength] + FiltVal); + PixelPtr[2 * LineLength] = clamp255(PixelPtr[2*LineLength] - FiltVal); + + PixelPtr ++; + } +} + +static void LoopFilter__c(PB_INSTANCE *pbi, int FLimit){ + + int j; + ogg_int16_t BoundingValues[256]; + ogg_int16_t *bvp = BoundingValues+127; + unsigned char *cp = pbi->display_fragments; + ogg_uint32_t *bp = pbi->recon_pixel_index_table; + + if ( FLimit == 0 ) return; + SetupBoundingValueArray_Generic(BoundingValues, FLimit); + + for ( j = 0; j < 3 ; j++){ + ogg_uint32_t *bp_begin = bp; + ogg_uint32_t *bp_end; + int stride; + int h; + + switch(j) { + case 0: /* y */ + bp_end = bp + pbi->YPlaneFragments; + h = pbi->HFragments; + stride = pbi->YStride; + break; + default: /* u,v, 4:20 specific */ + bp_end = bp + pbi->UVPlaneFragments; + h = pbi->HFragments >> 1; + stride = pbi->UVStride; + break; + } + + while(bpbp_left) + loop_filter_h(&pbi->LastFrameRecon[bp[0]],stride,bvp); + if(bp_left>bp_begin) + loop_filter_v(&pbi->LastFrameRecon[bp[0]],stride,bvp); + if(bp+1LastFrameRecon[bp[0]]+8,stride,bvp); + if(bp+hLastFrameRecon[bp[h]],stride,bvp); + } + bp++; + cp++; + } + } + } +} + +void ReconRefFrames (PB_INSTANCE *pbi){ + ogg_int32_t i; + unsigned char *SwapReconBuffersTemp; + + /* predictor multiplier up-left, up, up-right,left, shift + Entries are packed in the order L, UL, U, UR, with missing entries + moved to the end (before the shift parameters). */ + static const ogg_int16_t pc[16][6]={ + {0,0,0,0,0,0}, + {1,0,0,0,0,0}, /* PL */ + {1,0,0,0,0,0}, /* PUL */ + {1,0,0,0,0,0}, /* PUL|PL */ + {1,0,0,0,0,0}, /* PU */ + {1,1,0,0,1,1}, /* PU|PL */ + {0,1,0,0,0,0}, /* PU|PUL */ + {29,-26,29,0,5,31}, /* PU|PUL|PL */ + {1,0,0,0,0,0}, /* PUR */ + {75,53,0,0,7,127}, /* PUR|PL */ + {1,1,0,0,1,1}, /* PUR|PUL */ + {75,0,53,0,7,127}, /* PUR|PUL|PL */ + {1,0,0,0,0,0}, /* PUR|PU */ + {75,0,53,0,7,127}, /* PUR|PU|PL */ + {3,10,3,0,4,15}, /* PUR|PU|PUL */ + {29,-26,29,0,5,31} /* PUR|PU|PUL|PL */ + }; + + /* boundary case bit masks. */ + static const int bc_mask[8]={ + /* normal case no boundary condition */ + PUR|PU|PUL|PL, + /* left column */ + PUR|PU, + /* top row */ + PL, + /* top row, left column */ + 0, + /* right column */ + PU|PUL|PL, + /* right and left column */ + PU, + /* top row, right column */ + PL, + /* top row, right and left column */ + 0 + }; + + /* value left value up-left, value up, value up-right, missing + values skipped. */ + int v[4]; + + /* fragment number left, up-left, up, up-right */ + int fn[4]; + + /* predictor count. */ + int pcount; + + short wpc; + static const short Mode2Frame[] = { + 1, /* CODE_INTER_NO_MV 0 => Encoded diff from same MB last frame */ + 0, /* CODE_INTRA 1 => DCT Encoded Block */ + 1, /* CODE_INTER_PLUS_MV 2 => Encoded diff from included MV MB last frame */ + 1, /* CODE_INTER_LAST_MV 3 => Encoded diff from MRU MV MB last frame */ + 1, /* CODE_INTER_PRIOR_MV 4 => Encoded diff from included 4 separate MV blocks */ + 2, /* CODE_USING_GOLDEN 5 => Encoded diff from same MB golden frame */ + 2, /* CODE_GOLDEN_MV 6 => Encoded diff from included MV MB golden frame */ + 1 /* CODE_INTER_FOUR_MV 7 => Encoded diff from included 4 separate MV blocks */ + }; + short Last[3]; + short PredictedDC; + int FragsAcross=pbi->HFragments; + int FromFragment,ToFragment; + int FragsDown = pbi->VFragments; + + int WhichFrame; + int WhichCase; + int j,k,m,n; + + void (*ExpandBlockA) ( PB_INSTANCE *pbi, ogg_int32_t FragmentNumber ); + + if ( pbi->FrameType == KEY_FRAME ) + ExpandBlockA=ExpandKFBlock; + else + ExpandBlockA=ExpandBlock; + + /* for y,u,v */ + for ( j = 0; j < 3 ; j++) { + /* pick which fragments based on Y, U, V */ + switch(j){ + case 0: /* y */ + FromFragment = 0; + ToFragment = pbi->YPlaneFragments; + FragsAcross = pbi->HFragments; + FragsDown = pbi->VFragments; + break; + case 1: /* u */ + FromFragment = pbi->YPlaneFragments; + ToFragment = pbi->YPlaneFragments + pbi->UVPlaneFragments ; + FragsAcross = pbi->HFragments >> 1; + FragsDown = pbi->VFragments >> 1; + break; + /*case 2: v */ + default: + FromFragment = pbi->YPlaneFragments + pbi->UVPlaneFragments; + ToFragment = pbi->YPlaneFragments + (2 * pbi->UVPlaneFragments) ; + FragsAcross = pbi->HFragments >> 1; + FragsDown = pbi->VFragments >> 1; + break; + } + + /* initialize our array of last used DC Components */ + for(k=0;k<3;k++) + Last[k]=0; + + i=FromFragment; + + /* do prediction on all of Y, U or V */ + for ( m = 0 ; m < FragsDown ; m++) { + for ( n = 0 ; n < FragsAcross ; n++, i++){ + + /* only do 2 prediction if fragment coded and on non intra or + if all fragments are intra */ + if( pbi->display_fragments[i] || (pbi->FrameType == KEY_FRAME) ){ + /* Type of Fragment */ + WhichFrame = Mode2Frame[pbi->FragCodingMethod[i]]; + + /* Check Borderline Cases */ + WhichCase = (n==0) + ((m==0) << 1) + ((n+1 == FragsAcross) << 2); + + fn[0]=i-1; + fn[1]=i-FragsAcross-1; + fn[2]=i-FragsAcross; + fn[3]=i-FragsAcross+1; + + /* fragment valid for prediction use if coded and it comes + from same frame as the one we are predicting */ + for(k=pcount=wpc=0; k<4; k++) { + int pflag; + pflag=1<display_fragments[fn[k]] && + (Mode2Frame[pbi->FragCodingMethod[fn[k]]] == WhichFrame)){ + v[pcount]=pbi->QFragData[fn[k]][0]; + wpc|=pflag; + pcount++; + } + } + + if(wpc==0){ + /* fall back to the last coded fragment */ + pbi->QFragData[i][0] += Last[WhichFrame]; + + }else{ + + /* don't do divide if divisor is 1 or 0 */ + PredictedDC = pc[wpc][0]*v[0]; + for(k=1; k>= pc[wpc][4]; + } + + /* check for outranging on the two predictors that can outrange */ + if((wpc&(PU|PUL|PL)) == (PU|PUL|PL)){ + if( abs(PredictedDC - v[2]) > 128) { + PredictedDC = v[2]; + } else if( abs(PredictedDC - v[0]) > 128) { + PredictedDC = v[0]; + } else if( abs(PredictedDC - v[1]) > 128) { + PredictedDC = v[1]; + } + } + + pbi->QFragData[i][0] += PredictedDC; + + } + + /* Save the last fragment coded for whatever frame we are + predicting from */ + Last[WhichFrame] = pbi->QFragData[i][0]; + + /* Inverse DCT and reconstitute buffer in thisframe */ + ExpandBlockA( pbi, i ); + } + } + } + } + + /* Copy the current reconstruction back to the last frame recon buffer. */ + if(pbi->CodedBlockIndex > (ogg_int32_t) (pbi->UnitFragments >> 1)){ + SwapReconBuffersTemp = pbi->ThisFrameRecon; + pbi->ThisFrameRecon = pbi->LastFrameRecon; + pbi->LastFrameRecon = SwapReconBuffersTemp; + CopyNotRecon( pbi, pbi->LastFrameRecon, pbi->ThisFrameRecon ); + }else{ + CopyRecon( pbi, pbi->LastFrameRecon, pbi->ThisFrameRecon ); + } + + /* Apply a loop filter to edge pixels of updated blocks */ + dsp_LoopFilter(pbi->dsp, pbi, pbi->quant_info.loop_filter_limits[pbi->FrameQIndex]); + + /* We may need to update the UMV border */ + UpdateUMVBorder(pbi, pbi->LastFrameRecon); + + /* Reconstruct the golden frame if necessary. + For VFW codec only on key frames */ + if ( pbi->FrameType == KEY_FRAME ){ + CopyRecon( pbi, pbi->GoldenFrame, pbi->LastFrameRecon ); + /* We may need to update the UMV border */ + UpdateUMVBorder(pbi, pbi->GoldenFrame); + } +} + +void dsp_dct_decode_init (DspFunctions *funcs, ogg_uint32_t cpu_flags) +{ + funcs->LoopFilter = LoopFilter__c; +#if defined(USE_ASM) + // Todo: Port the dct for MSC one day. +#if !defined (_MSC_VER) + if (cpu_flags & OC_CPU_X86_MMX) { + dsp_mmx_dct_decode_init(funcs); + } +#endif +#endif +} diff --git a/Engine/lib/libtheora/lib/enc/dct_encode.c b/Engine/lib/libtheora/lib/enc/dct_encode.c new file mode 100644 index 000000000..3a3c47778 --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/dct_encode.c @@ -0,0 +1,469 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: dct_encode.c 15153 2008-08-04 18:37:55Z tterribe $ + + ********************************************************************/ + +#include +#include "codec_internal.h" +#include "dsp.h" +#include "quant_lookup.h" + + +static int ModeUsesMC[MAX_MODES] = { 0, 0, 1, 1, 1, 0, 1, 1 }; + +static unsigned char TokenizeDctValue (ogg_int16_t DataValue, + ogg_uint32_t * TokenListPtr ){ + unsigned char tokens_added = 0; + ogg_uint32_t AbsDataVal = abs( (ogg_int32_t)DataValue ); + + /* Values are tokenised as category value and a number of additional + bits that define the position within the category. */ + + if ( DataValue == 0 ) return 0; + + if ( AbsDataVal == 1 ){ + if ( DataValue == 1 ) + TokenListPtr[0] = ONE_TOKEN; + else + TokenListPtr[0] = MINUS_ONE_TOKEN; + tokens_added = 1; + } else if ( AbsDataVal == 2 ) { + if ( DataValue == 2 ) + TokenListPtr[0] = TWO_TOKEN; + else + TokenListPtr[0] = MINUS_TWO_TOKEN; + tokens_added = 1; + } else if ( AbsDataVal <= MAX_SINGLE_TOKEN_VALUE ) { + TokenListPtr[0] = LOW_VAL_TOKENS + (AbsDataVal - DCT_VAL_CAT2_MIN); + if ( DataValue > 0 ) + TokenListPtr[1] = 0; + else + TokenListPtr[1] = 1; + tokens_added = 2; + } else if ( AbsDataVal <= 8 ) { + /* Bit 1 determines sign, Bit 0 the value */ + TokenListPtr[0] = DCT_VAL_CATEGORY3; + if ( DataValue > 0 ) + TokenListPtr[1] = (AbsDataVal - DCT_VAL_CAT3_MIN); + else + TokenListPtr[1] = (0x02) + (AbsDataVal - DCT_VAL_CAT3_MIN); + tokens_added = 2; + } else if ( AbsDataVal <= 12 ) { + /* Bit 2 determines sign, Bit 0-2 the value */ + TokenListPtr[0] = DCT_VAL_CATEGORY4; + if ( DataValue > 0 ) + TokenListPtr[1] = (AbsDataVal - DCT_VAL_CAT4_MIN); + else + TokenListPtr[1] = (0x04) + (AbsDataVal - DCT_VAL_CAT4_MIN); + tokens_added = 2; + } else if ( AbsDataVal <= 20 ) { + /* Bit 3 determines sign, Bit 0-2 the value */ + TokenListPtr[0] = DCT_VAL_CATEGORY5; + if ( DataValue > 0 ) + TokenListPtr[1] = (AbsDataVal - DCT_VAL_CAT5_MIN); + else + TokenListPtr[1] = (0x08) + (AbsDataVal - DCT_VAL_CAT5_MIN); + tokens_added = 2; + } else if ( AbsDataVal <= 36 ) { + /* Bit 4 determines sign, Bit 0-3 the value */ + TokenListPtr[0] = DCT_VAL_CATEGORY6; + if ( DataValue > 0 ) + TokenListPtr[1] = (AbsDataVal - DCT_VAL_CAT6_MIN); + else + TokenListPtr[1] = (0x010) + (AbsDataVal - DCT_VAL_CAT6_MIN); + tokens_added = 2; + } else if ( AbsDataVal <= 68 ) { + /* Bit 5 determines sign, Bit 0-4 the value */ + TokenListPtr[0] = DCT_VAL_CATEGORY7; + if ( DataValue > 0 ) + TokenListPtr[1] = (AbsDataVal - DCT_VAL_CAT7_MIN); + else + TokenListPtr[1] = (0x20) + (AbsDataVal - DCT_VAL_CAT7_MIN); + tokens_added = 2; + } else if ( AbsDataVal <= 511 ) { + /* Bit 9 determines sign, Bit 0-8 the value */ + TokenListPtr[0] = DCT_VAL_CATEGORY8; + if ( DataValue > 0 ) + TokenListPtr[1] = (AbsDataVal - DCT_VAL_CAT8_MIN); + else + TokenListPtr[1] = (0x200) + (AbsDataVal - DCT_VAL_CAT8_MIN); + tokens_added = 2; + } else { + TokenListPtr[0] = DCT_VAL_CATEGORY8; + if ( DataValue > 0 ) + TokenListPtr[1] = (511 - DCT_VAL_CAT8_MIN); + else + TokenListPtr[1] = (0x200) + (511 - DCT_VAL_CAT8_MIN); + tokens_added = 2; + } + + /* Return the total number of tokens added */ + return tokens_added; +} + +static unsigned char TokenizeDctRunValue (unsigned char RunLength, + ogg_int16_t DataValue, + ogg_uint32_t * TokenListPtr ){ + unsigned char tokens_added = 0; + ogg_uint32_t AbsDataVal = abs( (ogg_int32_t)DataValue ); + + /* Values are tokenised as category value and a number of additional + bits that define the category. */ + if ( DataValue == 0 ) return 0; + if ( AbsDataVal == 1 ) { + /* Zero runs of 1-5 */ + if ( RunLength <= 5 ) { + TokenListPtr[0] = DCT_RUN_CATEGORY1 + (RunLength - 1); + if ( DataValue > 0 ) + TokenListPtr[1] = 0; + else + TokenListPtr[1] = 1; + } else if ( RunLength <= 9 ) { + /* Zero runs of 6-9 */ + TokenListPtr[0] = DCT_RUN_CATEGORY1B; + if ( DataValue > 0 ) + TokenListPtr[1] = (RunLength - 6); + else + TokenListPtr[1] = 0x04 + (RunLength - 6); + } else { + /* Zero runs of 10-17 */ + TokenListPtr[0] = DCT_RUN_CATEGORY1C; + if ( DataValue > 0 ) + TokenListPtr[1] = (RunLength - 10); + else + TokenListPtr[1] = 0x08 + (RunLength - 10); + } + tokens_added = 2; + } else if ( AbsDataVal <= 3 ) { + if ( RunLength == 1 ) { + TokenListPtr[0] = DCT_RUN_CATEGORY2; + + /* Extra bits token bit 1 indicates sign, bit 0 indicates value */ + if ( DataValue > 0 ) + TokenListPtr[1] = (AbsDataVal - 2); + else + TokenListPtr[1] = (0x02) + (AbsDataVal - 2); + tokens_added = 2; + }else{ + TokenListPtr[0] = DCT_RUN_CATEGORY2 + 1; + + /* Extra bits token. */ + /* bit 2 indicates sign, bit 1 indicates value, bit 0 indicates + run length */ + if ( DataValue > 0 ) + TokenListPtr[1] = ((AbsDataVal - 2) << 1) + (RunLength - 2); + else + TokenListPtr[1] = (0x04) + ((AbsDataVal - 2) << 1) + (RunLength - 2); + tokens_added = 2; + } + } else { + tokens_added = 2; /* ERROR */ + /*IssueWarning( "Bad Input to TokenizeDctRunValue" );*/ + } + + /* Return the total number of tokens added */ + return tokens_added; +} + +static unsigned char TokenizeDctBlock (ogg_int16_t * RawData, + ogg_uint32_t * TokenListPtr ) { + ogg_uint32_t i; + unsigned char run_count; + unsigned char token_count = 0; /* Number of tokens crated. */ + ogg_uint32_t AbsData; + + + /* Tokenize the block */ + for( i = 0; i < BLOCK_SIZE; i++ ){ + run_count = 0; + + /* Look for a zero run. */ + /* NOTE the use of & instead of && which is faster (and + equivalent) in this instance. */ + /* NO, NO IT ISN'T --Monty */ + while( (i < BLOCK_SIZE) && (!RawData[i]) ){ + run_count++; + i++; + } + + /* If we have reached the end of the block then code EOB */ + if ( i == BLOCK_SIZE ){ + TokenListPtr[token_count] = DCT_EOB_TOKEN; + token_count++; + }else{ + /* If we have a short zero run followed by a low data value code + the two as a composite token. */ + if ( run_count ){ + AbsData = abs(RawData[i]); + + if ( ((AbsData == 1) && (run_count <= 17)) || + ((AbsData <= 3) && (run_count <= 3)) ) { + /* Tokenise the run and subsequent value combination value */ + token_count += TokenizeDctRunValue( run_count, + RawData[i], + &TokenListPtr[token_count] ); + }else{ + + /* Else if we have a long non-EOB run or a run followed by a + value token > MAX_RUN_VAL then code the run and token + seperately */ + if ( run_count <= 8 ) + TokenListPtr[token_count] = DCT_SHORT_ZRL_TOKEN; + else + TokenListPtr[token_count] = DCT_ZRL_TOKEN; + + token_count++; + TokenListPtr[token_count] = run_count - 1; + token_count++; + + /* Now tokenize the value */ + token_count += TokenizeDctValue( RawData[i], + &TokenListPtr[token_count] ); + } + }else{ + /* Else there was NO zero run. */ + /* Tokenise the value */ + token_count += TokenizeDctValue( RawData[i], + &TokenListPtr[token_count] ); + } + } + } + + /* Return the total number of tokens (including additional bits + tokens) used. */ + return token_count; +} + +ogg_uint32_t DPCMTokenizeBlock (CP_INSTANCE *cpi, + ogg_int32_t FragIndex){ + ogg_uint32_t token_count; + + if ( cpi->pb.FrameType == KEY_FRAME ){ + /* Key frame so code block in INTRA mode. */ + cpi->pb.CodingMode = CODE_INTRA; + }else{ + /* Get Motion vector and mode for this block. */ + cpi->pb.CodingMode = cpi->pb.FragCodingMethod[FragIndex]; + } + + /* Tokenise the dct data. */ + token_count = TokenizeDctBlock( cpi->pb.QFragData[FragIndex], + cpi->pb.TokenList[FragIndex] ); + + cpi->FragTokenCounts[FragIndex] = token_count; + cpi->TotTokenCount += token_count; + + /* Return number of pixels coded (i.e. 8x8). */ + return BLOCK_SIZE; +} + +static int AllZeroDctData( Q_LIST_ENTRY * QuantList ){ + ogg_uint32_t i; + + for ( i = 0; i < 64; i ++ ) + if ( QuantList[i] != 0 ) + return 0; + + return 1; +} + +static void MotionBlockDifference (CP_INSTANCE * cpi, unsigned char * FiltPtr, + ogg_int16_t *DctInputPtr, ogg_int32_t MvDevisor, + unsigned char* old_ptr1, unsigned char* new_ptr1, + ogg_uint32_t FragIndex,ogg_uint32_t PixelsPerLine, + ogg_uint32_t ReconPixelsPerLine) { + + ogg_int32_t MvShift; + ogg_int32_t MvModMask; + ogg_int32_t AbsRefOffset; + ogg_int32_t AbsXOffset; + ogg_int32_t AbsYOffset; + ogg_int32_t MVOffset; /* Baseline motion vector offset */ + ogg_int32_t ReconPtr2Offset; /* Offset for second reconstruction in + half pixel MC */ + unsigned char *ReconPtr1; /* DCT reconstructed image pointers */ + unsigned char *ReconPtr2; /* Pointer used in half pixel MC */ + + switch(MvDevisor) { + case 2: + MvShift = 1; + MvModMask = 1; + break; + case 4: + MvShift = 2; + MvModMask = 3; + break; + default: + break; + } + + cpi->MVector.x = cpi->pb.FragMVect[FragIndex].x; + cpi->MVector.y = cpi->pb.FragMVect[FragIndex].y; + + /* Set up the baseline offset for the motion vector. */ + MVOffset = ((cpi->MVector.y / MvDevisor) * ReconPixelsPerLine) + + (cpi->MVector.x / MvDevisor); + + /* Work out the offset of the second reference position for 1/2 + pixel interpolation. For the U and V planes the MV specifies 1/4 + pixel accuracy. This is adjusted to 1/2 pixel as follows ( 0->0, + 1/4->1/2, 1/2->1/2, 3/4->1/2 ). */ + ReconPtr2Offset = 0; + AbsXOffset = cpi->MVector.x % MvDevisor; + AbsYOffset = cpi->MVector.y % MvDevisor; + + if ( AbsXOffset ) { + if ( cpi->MVector.x > 0 ) + ReconPtr2Offset += 1; + else + ReconPtr2Offset -= 1; + } + + if ( AbsYOffset ) { + if ( cpi->MVector.y > 0 ) + ReconPtr2Offset += ReconPixelsPerLine; + else + ReconPtr2Offset -= ReconPixelsPerLine; + } + + if ( cpi->pb.CodingMode==CODE_GOLDEN_MV ) { + ReconPtr1 = &cpi-> + pb.GoldenFrame[cpi->pb.recon_pixel_index_table[FragIndex]]; + } else { + ReconPtr1 = &cpi-> + pb.LastFrameRecon[cpi->pb.recon_pixel_index_table[FragIndex]]; + } + + ReconPtr1 += MVOffset; + ReconPtr2 = ReconPtr1 + ReconPtr2Offset; + + AbsRefOffset = abs((int)(ReconPtr1 - ReconPtr2)); + + /* Is the MV offset exactly pixel alligned */ + if ( AbsRefOffset == 0 ){ + dsp_sub8x8(cpi->dsp, FiltPtr, ReconPtr1, DctInputPtr, + PixelsPerLine, ReconPixelsPerLine); + dsp_copy8x8 (cpi->dsp, new_ptr1, old_ptr1, PixelsPerLine); + } else { + /* Fractional pixel MVs. */ + /* Note that we only use two pixel values even for the diagonal */ + dsp_sub8x8avg2(cpi->dsp, FiltPtr, ReconPtr1,ReconPtr2,DctInputPtr, + PixelsPerLine, ReconPixelsPerLine); + dsp_copy8x8 (cpi->dsp, new_ptr1, old_ptr1, PixelsPerLine); + } +} + +void TransformQuantizeBlock (CP_INSTANCE *cpi, ogg_int32_t FragIndex, + ogg_uint32_t PixelsPerLine) { + unsigned char *new_ptr1; /* Pointers into current frame */ + unsigned char *old_ptr1; /* Pointers into old frame */ + unsigned char *FiltPtr; /* Pointers to srf filtered pixels */ + ogg_int16_t *DctInputPtr; /* Pointer into buffer containing input to DCT */ + int LeftEdge; /* Flag if block at left edge of component */ + ogg_uint32_t ReconPixelsPerLine; /* Line length for recon buffers. */ + + unsigned char *ReconPtr1; /* DCT reconstructed image pointers */ + ogg_int32_t MvDevisor; /* Defines MV resolution (2 = 1/2 + pixel for Y or 4 = 1/4 for UV) */ + + new_ptr1 = &cpi->yuv1ptr[cpi->pb.pixel_index_table[FragIndex]]; + old_ptr1 = &cpi->yuv0ptr[cpi->pb.pixel_index_table[FragIndex]]; + DctInputPtr = cpi->DCTDataBuffer; + + /* Set plane specific values */ + if (FragIndex < (ogg_int32_t)cpi->pb.YPlaneFragments){ + ReconPixelsPerLine = cpi->pb.YStride; + MvDevisor = 2; /* 1/2 pixel accuracy in Y */ + }else{ + ReconPixelsPerLine = cpi->pb.UVStride; + MvDevisor = 4; /* UV planes at 1/2 resolution of Y */ + } + + /* adjusted / filtered pointers */ + FiltPtr = &cpi->ConvDestBuffer[cpi->pb.pixel_index_table[FragIndex]]; + + if ( cpi->pb.FrameType == KEY_FRAME ) { + /* Key frame so code block in INTRA mode. */ + cpi->pb.CodingMode = CODE_INTRA; + }else{ + /* Get Motion vector and mode for this block. */ + cpi->pb.CodingMode = cpi->pb.FragCodingMethod[FragIndex]; + } + + /* Selection of Quantiser matrix and set other plane related values. */ + if ( FragIndex < (ogg_int32_t)cpi->pb.YPlaneFragments ){ + LeftEdge = !(FragIndex%cpi->pb.HFragments); + + /* Select the appropriate Y quantiser matrix */ + if ( cpi->pb.CodingMode == CODE_INTRA ) + select_quantiser(&cpi->pb, BLOCK_Y); + else + select_quantiser(&cpi->pb, BLOCK_INTER_Y); + } else { + LeftEdge = !((FragIndex-cpi->pb.YPlaneFragments)%(cpi->pb.HFragments>>1)); + + if(FragIndex < (ogg_int32_t)cpi->pb.YPlaneFragments + (ogg_int32_t)cpi->pb.UVPlaneFragments) { + /* U plane */ + if ( cpi->pb.CodingMode == CODE_INTRA ) + select_quantiser(&cpi->pb, BLOCK_U); + else + select_quantiser(&cpi->pb, BLOCK_INTER_U); + } else { + /* V plane */ + if ( cpi->pb.CodingMode == CODE_INTRA ) + select_quantiser(&cpi->pb, BLOCK_V); + else + select_quantiser(&cpi->pb, BLOCK_INTER_V); + } + } + + if ( ModeUsesMC[cpi->pb.CodingMode] ){ + + MotionBlockDifference(cpi, FiltPtr, DctInputPtr, MvDevisor, + old_ptr1, new_ptr1, FragIndex, PixelsPerLine, + ReconPixelsPerLine); + + } else if ( (cpi->pb.CodingMode==CODE_INTER_NO_MV ) || + ( cpi->pb.CodingMode==CODE_USING_GOLDEN ) ) { + if ( cpi->pb.CodingMode==CODE_INTER_NO_MV ) { + ReconPtr1 = &cpi-> + pb.LastFrameRecon[cpi->pb.recon_pixel_index_table[FragIndex]]; + } else { + ReconPtr1 = &cpi-> + pb.GoldenFrame[cpi->pb.recon_pixel_index_table[FragIndex]]; + } + + dsp_sub8x8(cpi->dsp, FiltPtr, ReconPtr1, DctInputPtr, + PixelsPerLine, ReconPixelsPerLine); + dsp_copy8x8 (cpi->dsp, new_ptr1, old_ptr1, PixelsPerLine); + } else if ( cpi->pb.CodingMode==CODE_INTRA ) { + dsp_sub8x8_128(cpi->dsp, FiltPtr, DctInputPtr, PixelsPerLine); + dsp_copy8x8 (cpi->dsp, new_ptr1, old_ptr1, PixelsPerLine); + } + + /* Proceed to encode the data into the encode buffer if the encoder + is enabled. */ + /* Perform a 2D DCT transform on the data. */ + dsp_fdct_short(cpi->dsp, cpi->DCTDataBuffer, cpi->DCT_codes ); + + /* Quantize that transform data. */ + quantize ( &cpi->pb, cpi->DCT_codes, cpi->pb.QFragData[FragIndex] ); + + if ( (cpi->pb.CodingMode == CODE_INTER_NO_MV) && + ( AllZeroDctData(cpi->pb.QFragData[FragIndex]) ) ) { + cpi->pb.display_fragments[FragIndex] = 0; + } + +} diff --git a/Engine/lib/libtheora/lib/enc/dsp.c b/Engine/lib/libtheora/lib/enc/dsp.c new file mode 100644 index 000000000..9fe402d4e --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/dsp.c @@ -0,0 +1,422 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: dsp.c 15427 2008-10-21 02:36:19Z xiphmont $ + + ********************************************************************/ + +#include +#include "codec_internal.h" +#include "../cpu.c" + +#define DSP_OP_AVG(a,b) ((((int)(a)) + ((int)(b)))/2) +#define DSP_OP_DIFF(a,b) (((int)(a)) - ((int)(b))) +#define DSP_OP_ABS_DIFF(a,b) abs((((int)(a)) - ((int)(b)))) + +static void sub8x8__c (unsigned char *FiltPtr, unsigned char *ReconPtr, + ogg_int16_t *DctInputPtr, ogg_uint32_t PixelsPerLine, + ogg_uint32_t ReconPixelsPerLine) { + int i; + + /* For each block row */ + for (i=8; i; i--) { + DctInputPtr[0] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[0], ReconPtr[0]); + DctInputPtr[1] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[1], ReconPtr[1]); + DctInputPtr[2] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[2], ReconPtr[2]); + DctInputPtr[3] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[3], ReconPtr[3]); + DctInputPtr[4] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[4], ReconPtr[4]); + DctInputPtr[5] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[5], ReconPtr[5]); + DctInputPtr[6] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[6], ReconPtr[6]); + DctInputPtr[7] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[7], ReconPtr[7]); + + /* Start next row */ + FiltPtr += PixelsPerLine; + ReconPtr += ReconPixelsPerLine; + DctInputPtr += 8; + } +} + +static void sub8x8_128__c (unsigned char *FiltPtr, ogg_int16_t *DctInputPtr, + ogg_uint32_t PixelsPerLine) { + int i; + /* For each block row */ + for (i=8; i; i--) { + /* INTRA mode so code raw image data */ + /* We convert the data to 8 bit signed (by subtracting 128) as + this reduces the internal precision requirments in the DCT + transform. */ + DctInputPtr[0] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[0], 128); + DctInputPtr[1] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[1], 128); + DctInputPtr[2] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[2], 128); + DctInputPtr[3] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[3], 128); + DctInputPtr[4] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[4], 128); + DctInputPtr[5] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[5], 128); + DctInputPtr[6] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[6], 128); + DctInputPtr[7] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[7], 128); + + /* Start next row */ + FiltPtr += PixelsPerLine; + DctInputPtr += 8; + } +} + +static void sub8x8avg2__c (unsigned char *FiltPtr, unsigned char *ReconPtr1, + unsigned char *ReconPtr2, ogg_int16_t *DctInputPtr, + ogg_uint32_t PixelsPerLine, + ogg_uint32_t ReconPixelsPerLine) +{ + int i; + + /* For each block row */ + for (i=8; i; i--) { + DctInputPtr[0] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[0], DSP_OP_AVG (ReconPtr1[0], ReconPtr2[0])); + DctInputPtr[1] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[1], DSP_OP_AVG (ReconPtr1[1], ReconPtr2[1])); + DctInputPtr[2] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[2], DSP_OP_AVG (ReconPtr1[2], ReconPtr2[2])); + DctInputPtr[3] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[3], DSP_OP_AVG (ReconPtr1[3], ReconPtr2[3])); + DctInputPtr[4] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[4], DSP_OP_AVG (ReconPtr1[4], ReconPtr2[4])); + DctInputPtr[5] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[5], DSP_OP_AVG (ReconPtr1[5], ReconPtr2[5])); + DctInputPtr[6] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[6], DSP_OP_AVG (ReconPtr1[6], ReconPtr2[6])); + DctInputPtr[7] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[7], DSP_OP_AVG (ReconPtr1[7], ReconPtr2[7])); + + /* Start next row */ + FiltPtr += PixelsPerLine; + ReconPtr1 += ReconPixelsPerLine; + ReconPtr2 += ReconPixelsPerLine; + DctInputPtr += 8; + } +} + +static ogg_uint32_t row_sad8__c (unsigned char *Src1, unsigned char *Src2) +{ + ogg_uint32_t SadValue; + ogg_uint32_t SadValue1; + + SadValue = DSP_OP_ABS_DIFF (Src1[0], Src2[0]) + + DSP_OP_ABS_DIFF (Src1[1], Src2[1]) + + DSP_OP_ABS_DIFF (Src1[2], Src2[2]) + + DSP_OP_ABS_DIFF (Src1[3], Src2[3]); + + SadValue1 = DSP_OP_ABS_DIFF (Src1[4], Src2[4]) + + DSP_OP_ABS_DIFF (Src1[5], Src2[5]) + + DSP_OP_ABS_DIFF (Src1[6], Src2[6]) + + DSP_OP_ABS_DIFF (Src1[7], Src2[7]); + + SadValue = ( SadValue > SadValue1 ) ? SadValue : SadValue1; + + return SadValue; +} + +static ogg_uint32_t col_sad8x8__c (unsigned char *Src1, unsigned char *Src2, + ogg_uint32_t stride) +{ + ogg_uint32_t SadValue[8] = {0,0,0,0,0,0,0,0}; + ogg_uint32_t SadValue2[8] = {0,0,0,0,0,0,0,0}; + ogg_uint32_t MaxSad = 0; + ogg_uint32_t i; + + for ( i = 0; i < 4; i++ ){ + SadValue[0] += abs(Src1[0] - Src2[0]); + SadValue[1] += abs(Src1[1] - Src2[1]); + SadValue[2] += abs(Src1[2] - Src2[2]); + SadValue[3] += abs(Src1[3] - Src2[3]); + SadValue[4] += abs(Src1[4] - Src2[4]); + SadValue[5] += abs(Src1[5] - Src2[5]); + SadValue[6] += abs(Src1[6] - Src2[6]); + SadValue[7] += abs(Src1[7] - Src2[7]); + + Src1 += stride; + Src2 += stride; + } + + for ( i = 0; i < 4; i++ ){ + SadValue2[0] += abs(Src1[0] - Src2[0]); + SadValue2[1] += abs(Src1[1] - Src2[1]); + SadValue2[2] += abs(Src1[2] - Src2[2]); + SadValue2[3] += abs(Src1[3] - Src2[3]); + SadValue2[4] += abs(Src1[4] - Src2[4]); + SadValue2[5] += abs(Src1[5] - Src2[5]); + SadValue2[6] += abs(Src1[6] - Src2[6]); + SadValue2[7] += abs(Src1[7] - Src2[7]); + + Src1 += stride; + Src2 += stride; + } + + for ( i = 0; i < 8; i++ ){ + if ( SadValue[i] > MaxSad ) + MaxSad = SadValue[i]; + if ( SadValue2[i] > MaxSad ) + MaxSad = SadValue2[i]; + } + + return MaxSad; +} + +static ogg_uint32_t sad8x8__c (unsigned char *ptr1, ogg_uint32_t stride1, + unsigned char *ptr2, ogg_uint32_t stride2) +{ + ogg_uint32_t i; + ogg_uint32_t sad = 0; + + for (i=8; i; i--) { + sad += DSP_OP_ABS_DIFF(ptr1[0], ptr2[0]); + sad += DSP_OP_ABS_DIFF(ptr1[1], ptr2[1]); + sad += DSP_OP_ABS_DIFF(ptr1[2], ptr2[2]); + sad += DSP_OP_ABS_DIFF(ptr1[3], ptr2[3]); + sad += DSP_OP_ABS_DIFF(ptr1[4], ptr2[4]); + sad += DSP_OP_ABS_DIFF(ptr1[5], ptr2[5]); + sad += DSP_OP_ABS_DIFF(ptr1[6], ptr2[6]); + sad += DSP_OP_ABS_DIFF(ptr1[7], ptr2[7]); + + /* Step to next row of block. */ + ptr1 += stride1; + ptr2 += stride2; + } + + return sad; +} + +static ogg_uint32_t sad8x8_thres__c (unsigned char *ptr1, ogg_uint32_t stride1, + unsigned char *ptr2, ogg_uint32_t stride2, + ogg_uint32_t thres) +{ + ogg_uint32_t i; + ogg_uint32_t sad = 0; + + for (i=8; i; i--) { + sad += DSP_OP_ABS_DIFF(ptr1[0], ptr2[0]); + sad += DSP_OP_ABS_DIFF(ptr1[1], ptr2[1]); + sad += DSP_OP_ABS_DIFF(ptr1[2], ptr2[2]); + sad += DSP_OP_ABS_DIFF(ptr1[3], ptr2[3]); + sad += DSP_OP_ABS_DIFF(ptr1[4], ptr2[4]); + sad += DSP_OP_ABS_DIFF(ptr1[5], ptr2[5]); + sad += DSP_OP_ABS_DIFF(ptr1[6], ptr2[6]); + sad += DSP_OP_ABS_DIFF(ptr1[7], ptr2[7]); + + if (sad > thres ) + break; + + /* Step to next row of block. */ + ptr1 += stride1; + ptr2 += stride2; + } + + return sad; +} + +static ogg_uint32_t sad8x8_xy2_thres__c (unsigned char *SrcData, ogg_uint32_t SrcStride, + unsigned char *RefDataPtr1, + unsigned char *RefDataPtr2, ogg_uint32_t RefStride, + ogg_uint32_t thres) +{ + ogg_uint32_t i; + ogg_uint32_t sad = 0; + + for (i=8; i; i--) { + sad += DSP_OP_ABS_DIFF(SrcData[0], DSP_OP_AVG (RefDataPtr1[0], RefDataPtr2[0])); + sad += DSP_OP_ABS_DIFF(SrcData[1], DSP_OP_AVG (RefDataPtr1[1], RefDataPtr2[1])); + sad += DSP_OP_ABS_DIFF(SrcData[2], DSP_OP_AVG (RefDataPtr1[2], RefDataPtr2[2])); + sad += DSP_OP_ABS_DIFF(SrcData[3], DSP_OP_AVG (RefDataPtr1[3], RefDataPtr2[3])); + sad += DSP_OP_ABS_DIFF(SrcData[4], DSP_OP_AVG (RefDataPtr1[4], RefDataPtr2[4])); + sad += DSP_OP_ABS_DIFF(SrcData[5], DSP_OP_AVG (RefDataPtr1[5], RefDataPtr2[5])); + sad += DSP_OP_ABS_DIFF(SrcData[6], DSP_OP_AVG (RefDataPtr1[6], RefDataPtr2[6])); + sad += DSP_OP_ABS_DIFF(SrcData[7], DSP_OP_AVG (RefDataPtr1[7], RefDataPtr2[7])); + + if ( sad > thres ) + break; + + /* Step to next row of block. */ + SrcData += SrcStride; + RefDataPtr1 += RefStride; + RefDataPtr2 += RefStride; + } + + return sad; +} + +static ogg_uint32_t intra8x8_err__c (unsigned char *DataPtr, ogg_uint32_t Stride) +{ + ogg_uint32_t i; + ogg_uint32_t XSum=0; + ogg_uint32_t XXSum=0; + + for (i=8; i; i--) { + /* Examine alternate pixel locations. */ + XSum += DataPtr[0]; + XXSum += DataPtr[0]*DataPtr[0]; + XSum += DataPtr[1]; + XXSum += DataPtr[1]*DataPtr[1]; + XSum += DataPtr[2]; + XXSum += DataPtr[2]*DataPtr[2]; + XSum += DataPtr[3]; + XXSum += DataPtr[3]*DataPtr[3]; + XSum += DataPtr[4]; + XXSum += DataPtr[4]*DataPtr[4]; + XSum += DataPtr[5]; + XXSum += DataPtr[5]*DataPtr[5]; + XSum += DataPtr[6]; + XXSum += DataPtr[6]*DataPtr[6]; + XSum += DataPtr[7]; + XXSum += DataPtr[7]*DataPtr[7]; + + /* Step to next row of block. */ + DataPtr += Stride; + } + + /* Compute population variance as mis-match metric. */ + return (( (XXSum<<6) - XSum*XSum ) ); +} + +static ogg_uint32_t inter8x8_err__c (unsigned char *SrcData, ogg_uint32_t SrcStride, + unsigned char *RefDataPtr, ogg_uint32_t RefStride) +{ + ogg_uint32_t i; + ogg_uint32_t XSum=0; + ogg_uint32_t XXSum=0; + ogg_int32_t DiffVal; + + for (i=8; i; i--) { + DiffVal = DSP_OP_DIFF (SrcData[0], RefDataPtr[0]); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF (SrcData[1], RefDataPtr[1]); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF (SrcData[2], RefDataPtr[2]); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF (SrcData[3], RefDataPtr[3]); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF (SrcData[4], RefDataPtr[4]); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF (SrcData[5], RefDataPtr[5]); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF (SrcData[6], RefDataPtr[6]); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF (SrcData[7], RefDataPtr[7]); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + /* Step to next row of block. */ + SrcData += SrcStride; + RefDataPtr += RefStride; + } + + /* Compute and return population variance as mis-match metric. */ + return (( (XXSum<<6) - XSum*XSum )); +} + +static ogg_uint32_t inter8x8_err_xy2__c (unsigned char *SrcData, ogg_uint32_t SrcStride, + unsigned char *RefDataPtr1, + unsigned char *RefDataPtr2, ogg_uint32_t RefStride) +{ + ogg_uint32_t i; + ogg_uint32_t XSum=0; + ogg_uint32_t XXSum=0; + ogg_int32_t DiffVal; + + for (i=8; i; i--) { + DiffVal = DSP_OP_DIFF(SrcData[0], DSP_OP_AVG (RefDataPtr1[0], RefDataPtr2[0])); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF(SrcData[1], DSP_OP_AVG (RefDataPtr1[1], RefDataPtr2[1])); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF(SrcData[2], DSP_OP_AVG (RefDataPtr1[2], RefDataPtr2[2])); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF(SrcData[3], DSP_OP_AVG (RefDataPtr1[3], RefDataPtr2[3])); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF(SrcData[4], DSP_OP_AVG (RefDataPtr1[4], RefDataPtr2[4])); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF(SrcData[5], DSP_OP_AVG (RefDataPtr1[5], RefDataPtr2[5])); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF(SrcData[6], DSP_OP_AVG (RefDataPtr1[6], RefDataPtr2[6])); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF(SrcData[7], DSP_OP_AVG (RefDataPtr1[7], RefDataPtr2[7])); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + /* Step to next row of block. */ + SrcData += SrcStride; + RefDataPtr1 += RefStride; + RefDataPtr2 += RefStride; + } + + /* Compute and return population variance as mis-match metric. */ + return (( (XXSum<<6) - XSum*XSum )); +} + +static void nop (void) { /* NOP */ } + +void dsp_init(DspFunctions *funcs) +{ + funcs->save_fpu = nop; + funcs->restore_fpu = nop; + funcs->sub8x8 = sub8x8__c; + funcs->sub8x8_128 = sub8x8_128__c; + funcs->sub8x8avg2 = sub8x8avg2__c; + funcs->row_sad8 = row_sad8__c; + funcs->col_sad8x8 = col_sad8x8__c; + funcs->sad8x8 = sad8x8__c; + funcs->sad8x8_thres = sad8x8_thres__c; + funcs->sad8x8_xy2_thres = sad8x8_xy2_thres__c; + funcs->intra8x8_err = intra8x8_err__c; + funcs->inter8x8_err = inter8x8_err__c; + funcs->inter8x8_err_xy2 = inter8x8_err_xy2__c; +} + +void dsp_static_init(DspFunctions *funcs) +{ + ogg_uint32_t cpuflags; + + cpuflags = oc_cpu_flags_get (); + dsp_init (funcs); + + dsp_recon_init (funcs, cpuflags); + dsp_dct_init (funcs, cpuflags); +#if defined(USE_ASM) + if (cpuflags & OC_CPU_X86_MMX) { + dsp_mmx_init(funcs); + } +# ifndef WIN32 + /* This is implemented for win32 yet */ + if (cpuflags & OC_CPU_X86_MMXEXT) { + dsp_mmxext_init(funcs); + } +# endif +#endif +} + diff --git a/Engine/lib/libtheora/lib/enc/dsp.h b/Engine/lib/libtheora/lib/enc/dsp.h new file mode 100644 index 000000000..7f96f7f84 --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/dsp.h @@ -0,0 +1,166 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: dsp.h 15153 2008-08-04 18:37:55Z tterribe $ + + ********************************************************************/ + +#ifndef DSP_H +#define DSP_H + +#include "theora/theora.h" +#include "../cpu.h" + +typedef struct +{ + void (*save_fpu) (void); + void (*restore_fpu) (void); + + void (*sub8x8) (unsigned char *FiltPtr, unsigned char *ReconPtr, + ogg_int16_t *DctInputPtr, ogg_uint32_t PixelsPerLine, + ogg_uint32_t ReconPixelsPerLine); + + void (*sub8x8_128) (unsigned char *FiltPtr, ogg_int16_t *DctInputPtr, + ogg_uint32_t PixelsPerLine); + + void (*sub8x8avg2) (unsigned char *FiltPtr, unsigned char *ReconPtr1, + unsigned char *ReconPtr2, ogg_int16_t *DctInputPtr, + ogg_uint32_t PixelsPerLine, + ogg_uint32_t ReconPixelsPerLine); + + void (*copy8x8) (unsigned char *src, unsigned char *dest, + ogg_uint32_t stride); + + void (*recon_intra8x8) (unsigned char *ReconPtr, ogg_int16_t *ChangePtr, + ogg_uint32_t LineStep); + + void (*recon_inter8x8) (unsigned char *ReconPtr, unsigned char *RefPtr, + ogg_int16_t *ChangePtr, ogg_uint32_t LineStep); + + void (*recon_inter8x8_half) (unsigned char *ReconPtr, unsigned char *RefPtr1, + unsigned char *RefPtr2, ogg_int16_t *ChangePtr, + ogg_uint32_t LineStep); + + void (*fdct_short) (ogg_int16_t *InputData, ogg_int16_t *OutputData); + + ogg_uint32_t (*row_sad8) (unsigned char *Src1, unsigned char *Src2); + + ogg_uint32_t (*col_sad8x8) (unsigned char *Src1, unsigned char *Src2, + ogg_uint32_t stride); + + ogg_uint32_t (*sad8x8) (unsigned char *ptr1, ogg_uint32_t stride1, + unsigned char *ptr2, ogg_uint32_t stride2); + + ogg_uint32_t (*sad8x8_thres) (unsigned char *ptr1, ogg_uint32_t stride1, + unsigned char *ptr2, ogg_uint32_t stride2, + ogg_uint32_t thres); + + ogg_uint32_t (*sad8x8_xy2_thres)(unsigned char *SrcData, ogg_uint32_t SrcStride, + unsigned char *RefDataPtr1, + unsigned char *RefDataPtr2, ogg_uint32_t RefStride, + ogg_uint32_t thres); + + ogg_uint32_t (*intra8x8_err) (unsigned char *DataPtr, ogg_uint32_t Stride); + + ogg_uint32_t (*inter8x8_err) (unsigned char *SrcData, ogg_uint32_t SrcStride, + unsigned char *RefDataPtr, ogg_uint32_t RefStride); + + ogg_uint32_t (*inter8x8_err_xy2)(unsigned char *SrcData, ogg_uint32_t SrcStride, + unsigned char *RefDataPtr1, + unsigned char *RefDataPtr2, ogg_uint32_t RefStride); + + void (*LoopFilter) (PB_INSTANCE *pbi, int FLimit); + + void (*FilterVert) (unsigned char * PixelPtr, + ogg_int32_t LineLength, ogg_int16_t *BoundingValuePtr); + + void (*IDctSlow) (ogg_int16_t *InputData, + ogg_int16_t *QuantMatrix, ogg_int16_t *OutputData); + + void (*IDct3) (ogg_int16_t *InputData, + ogg_int16_t *QuantMatrix, ogg_int16_t *OutputData); + + void (*IDct10) (ogg_int16_t *InputData, + ogg_int16_t *QuantMatrix, ogg_int16_t *OutputData); +} DspFunctions; + +extern void dsp_dct_init(DspFunctions *funcs, ogg_uint32_t cpu_flags); +extern void dsp_recon_init (DspFunctions *funcs, ogg_uint32_t cpu_flags); +extern void dsp_dct_decode_init(DspFunctions *funcs, ogg_uint32_t cpu_flags); +extern void dsp_idct_init(DspFunctions *funcs, ogg_uint32_t cpu_flags); + +void dsp_init(DspFunctions *funcs); +void dsp_static_init(DspFunctions *funcs); +#if defined(USE_ASM) && (defined(__i386__) || defined(__x86_64__) || defined(WIN32)) +extern void dsp_mmx_init(DspFunctions *funcs); +extern void dsp_mmxext_init(DspFunctions *funcs); +extern void dsp_mmx_fdct_init(DspFunctions *funcs); +extern void dsp_mmx_recon_init(DspFunctions *funcs); +extern void dsp_mmx_dct_decode_init(DspFunctions *funcs); +extern void dsp_mmx_idct_init(DspFunctions *funcs); +#endif + +#define dsp_save_fpu(funcs) (funcs.save_fpu ()) + +#define dsp_restore_fpu(funcs) (funcs.restore_fpu ()) + +#define dsp_sub8x8(funcs,a1,a2,a3,a4,a5) (funcs.sub8x8 (a1,a2,a3,a4,a5)) + +#define dsp_sub8x8_128(funcs,a1,a2,a3) (funcs.sub8x8_128 (a1,a2,a3)) + +#define dsp_sub8x8avg2(funcs,a1,a2,a3,a4,a5,a6) (funcs.sub8x8avg2 (a1,a2,a3,a4,a5,a6)) + +#define dsp_copy8x8(funcs,ptr1,ptr2,str1) (funcs.copy8x8 (ptr1,ptr2,str1)) + +#define dsp_recon_intra8x8(funcs,ptr1,ptr2,str1) (funcs.recon_intra8x8 (ptr1,ptr2,str1)) + +#define dsp_recon_inter8x8(funcs,ptr1,ptr2,ptr3,str1) \ + (funcs.recon_inter8x8 (ptr1,ptr2,ptr3,str1)) + +#define dsp_recon_inter8x8_half(funcs,ptr1,ptr2,ptr3,ptr4,str1) \ + (funcs.recon_inter8x8_half (ptr1,ptr2,ptr3,ptr4,str1)) + +#define dsp_fdct_short(funcs,in,out) (funcs.fdct_short (in,out)) + +#define dsp_row_sad8(funcs,ptr1,ptr2) (funcs.row_sad8 (ptr1,ptr2)) + +#define dsp_col_sad8x8(funcs,ptr1,ptr2,str1) (funcs.col_sad8x8 (ptr1,ptr2,str1)) + +#define dsp_sad8x8(funcs,ptr1,str1,ptr2,str2) (funcs.sad8x8 (ptr1,str1,ptr2,str2)) + +#define dsp_sad8x8_thres(funcs,ptr1,str1,ptr2,str2,t) (funcs.sad8x8_thres (ptr1,str1,ptr2,str2,t)) + +#define dsp_sad8x8_xy2_thres(funcs,ptr1,str1,ptr2,ptr3,str2,t) \ + (funcs.sad8x8_xy2_thres (ptr1,str1,ptr2,ptr3,str2,t)) + +#define dsp_intra8x8_err(funcs,ptr1,str1) (funcs.intra8x8_err (ptr1,str1)) + +#define dsp_inter8x8_err(funcs,ptr1,str1,ptr2,str2) \ + (funcs.inter8x8_err (ptr1,str1,ptr2,str2)) + +#define dsp_inter8x8_err_xy2(funcs,ptr1,str1,ptr2,ptr3,str2) \ + (funcs.inter8x8_err_xy2 (ptr1,str1,ptr2,ptr3,str2)) + +#define dsp_LoopFilter(funcs, ptr1, i) \ + (funcs.LoopFilter(ptr1, i)) + +#define dsp_IDctSlow(funcs, ptr1, ptr2, ptr3) \ + (funcs.IDctSlow(ptr1, ptr2, ptr3)) + +#define dsp_IDct3(funcs, ptr1, ptr2, ptr3) \ + (funcs.IDctSlow(ptr1, ptr2, ptr3)) + +#define dsp_IDct10(funcs, ptr1, ptr2, ptr3) \ + (funcs.IDctSlow(ptr1, ptr2, ptr3)) + +#endif /* DSP_H */ diff --git a/Engine/lib/libtheora/lib/enc/encapiwrapper.c b/Engine/lib/libtheora/lib/enc/encapiwrapper.c new file mode 100644 index 000000000..f1c46c39b --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/encapiwrapper.c @@ -0,0 +1,1141 @@ +#include +#include "theora/theoraenc.h" +#include "theora/theora.h" +#include "codec_internal.h" +#include "../dec/ocintrin.h" + +/*Wrapper to translate the new API into the old API. + Eventually we need to convert the old functions to support the new API + natively and do the translation the other way. + theora-exp already the necessary code to do so.*/ + + + +/*The default Huffman codes used for VP3.1. + It's kind of useless to include this, as TH_ENCCTL_SET_HUFFMAN_CODES is not + actually implemented in the old encoder, but it's part of the public API.*/ +const th_huff_code TH_VP31_HUFF_CODES[TH_NHUFFMAN_TABLES][TH_NDCT_TOKENS]={ + { + {0x002D, 6},{0x0026, 7},{0x0166, 9},{0x004E, 8}, + {0x02CE,10},{0x059E,11},{0x027D,11},{0x0008, 5}, + {0x04F9,12},{0x000F, 4},{0x000E, 4},{0x001B, 5}, + {0x0006, 4},{0x0008, 4},{0x0005, 4},{0x001A, 5}, + {0x0015, 5},{0x0007, 4},{0x000C, 4},{0x0001, 3}, + {0x0000, 3},{0x0009, 4},{0x0017, 5},{0x0029, 6}, + {0x0028, 6},{0x00B2, 8},{0x04F8,12},{0x059F,11}, + {0x009E, 9},{0x013F,10},{0x0012, 6},{0x0058, 7} + }, + { + {0x0010, 5},{0x0047, 7},{0x01FF, 9},{0x008C, 8}, + {0x03FC,10},{0x046A,11},{0x0469,11},{0x0022, 6}, + {0x11A1,13},{0x000E, 4},{0x000D, 4},{0x0004, 4}, + {0x0005, 4},{0x0009, 4},{0x0006, 4},{0x001E, 5}, + {0x0016, 5},{0x0007, 4},{0x000C, 4},{0x0001, 3}, + {0x0000, 3},{0x000A, 4},{0x0017, 5},{0x007D, 7}, + {0x007E, 7},{0x011B, 9},{0x08D1,12},{0x03FD,10}, + {0x046B,11},{0x11A0,13},{0x007C, 7},{0x00FE, 8} + }, + { + {0x0016, 5},{0x0020, 6},{0x0086, 8},{0x0087, 8}, + {0x0367,10},{0x06CC,11},{0x06CB,11},{0x006E, 7}, + {0x366D,14},{0x000F, 4},{0x000E, 4},{0x0004, 4}, + {0x0005, 4},{0x000A, 4},{0x0006, 4},{0x001A, 5}, + {0x0011, 5},{0x0007, 4},{0x000C, 4},{0x0001, 3}, + {0x0000, 3},{0x0009, 4},{0x0017, 5},{0x006F, 7}, + {0x006D, 7},{0x0364,10},{0x0D9A,12},{0x06CA,11}, + {0x1B37,13},{0x366C,14},{0x0042, 7},{0x00D8, 8} + }, + { + {0x0000, 4},{0x002D, 6},{0x00F7, 8},{0x0058, 7}, + {0x0167, 9},{0x02CB,10},{0x02CA,10},{0x000E, 6}, + {0x1661,13},{0x0003, 3},{0x0002, 3},{0x0008, 4}, + {0x0009, 4},{0x000D, 4},{0x0002, 4},{0x001F, 5}, + {0x0017, 5},{0x0001, 4},{0x000C, 4},{0x000E, 4}, + {0x000A, 4},{0x0006, 5},{0x0078, 7},{0x000F, 6}, + {0x007A, 7},{0x0164, 9},{0x0599,11},{0x02CD,10}, + {0x0B31,12},{0x1660,13},{0x0079, 7},{0x00F6, 8} + }, + { + {0x0003, 4},{0x003C, 6},{0x000F, 7},{0x007A, 7}, + {0x001D, 8},{0x0020, 9},{0x0072,10},{0x0006, 6}, + {0x0399,13},{0x0004, 3},{0x0005, 3},{0x0005, 4}, + {0x0006, 4},{0x000E, 4},{0x0004, 4},{0x0000, 4}, + {0x0019, 5},{0x0002, 4},{0x000D, 4},{0x0007, 4}, + {0x001F, 5},{0x0030, 6},{0x0011, 8},{0x0031, 6}, + {0x0005, 6},{0x0021, 9},{0x00E7,11},{0x0038, 9}, + {0x01CD,12},{0x0398,13},{0x007B, 7},{0x0009, 7} + }, + { + {0x0009, 4},{0x0002, 5},{0x0074, 7},{0x0007, 6}, + {0x00EC, 8},{0x00D1, 9},{0x01A6,10},{0x0006, 6}, + {0x0D21,13},{0x0005, 3},{0x0006, 3},{0x0008, 4}, + {0x0007, 4},{0x000F, 4},{0x0004, 4},{0x0000, 4}, + {0x001C, 5},{0x0002, 4},{0x0005, 4},{0x0003, 4}, + {0x000C, 5},{0x0035, 7},{0x01A7,10},{0x001B, 6}, + {0x0077, 7},{0x01A5,10},{0x0349,11},{0x00D0, 9}, + {0x0691,12},{0x0D20,13},{0x0075, 7},{0x00ED, 8} + }, + { + {0x000A, 4},{0x000C, 5},{0x0012, 6},{0x001B, 6}, + {0x00B7, 8},{0x016C, 9},{0x0099, 9},{0x005A, 7}, + {0x16D8,13},{0x0007, 3},{0x0006, 3},{0x0009, 4}, + {0x0008, 4},{0x0000, 3},{0x0005, 4},{0x0017, 5}, + {0x000E, 5},{0x0002, 4},{0x0003, 4},{0x000F, 5}, + {0x001A, 6},{0x004D, 8},{0x2DB3,14},{0x002C, 6}, + {0x0011, 6},{0x02DA,10},{0x05B7,11},{0x0098, 9}, + {0x0B6D,12},{0x2DB2,14},{0x0010, 6},{0x0027, 7} + }, + { + {0x000D, 4},{0x000F, 5},{0x001D, 6},{0x0008, 5}, + {0x0051, 7},{0x0056, 8},{0x00AF, 9},{0x002A, 7}, + {0x148A,13},{0x0007, 3},{0x0000, 2},{0x0008, 4}, + {0x0009, 4},{0x000C, 4},{0x0006, 4},{0x0017, 5}, + {0x000B, 5},{0x0016, 5},{0x0015, 5},{0x0009, 5}, + {0x0050, 7},{0x00AE, 9},{0x2917,14},{0x001C, 6}, + {0x0014, 6},{0x0290,10},{0x0523,11},{0x0149, 9}, + {0x0A44,12},{0x2916,14},{0x0053, 7},{0x00A5, 8} + }, + { + {0x0001, 4},{0x001D, 6},{0x00F5, 8},{0x00F4, 8}, + {0x024D,10},{0x0499,11},{0x0498,11},{0x0001, 5}, + {0x0021, 6},{0x0006, 3},{0x0005, 3},{0x0006, 4}, + {0x0005, 4},{0x0002, 4},{0x0007, 5},{0x0025, 6}, + {0x007B, 7},{0x001C, 6},{0x0020, 6},{0x000D, 6}, + {0x0048, 7},{0x0092, 8},{0x0127, 9},{0x000E, 4}, + {0x0004, 4},{0x0011, 5},{0x000C, 6},{0x003C, 6}, + {0x000F, 5},{0x0000, 5},{0x001F, 5},{0x0013, 5} + }, + { + {0x0005, 4},{0x003C, 6},{0x0040, 7},{0x000D, 7}, + {0x0031, 9},{0x0061,10},{0x0060,10},{0x0002, 5}, + {0x00F5, 8},{0x0006, 3},{0x0005, 3},{0x0007, 4}, + {0x0006, 4},{0x0002, 4},{0x0009, 5},{0x0025, 6}, + {0x0007, 6},{0x0021, 6},{0x0024, 6},{0x0010, 6}, + {0x0041, 7},{0x00F4, 8},{0x0019, 8},{0x000E, 4}, + {0x0003, 4},{0x0011, 5},{0x0011, 6},{0x003F, 6}, + {0x003E, 6},{0x007B, 7},{0x0000, 4},{0x0013, 5} + }, + { + {0x000A, 4},{0x0007, 5},{0x0001, 6},{0x0009, 6}, + {0x0131, 9},{0x0261,10},{0x0260,10},{0x0015, 6}, + {0x0001, 7},{0x0007, 3},{0x0006, 3},{0x0008, 4}, + {0x0007, 4},{0x0006, 4},{0x0012, 5},{0x002F, 6}, + {0x0014, 6},{0x0027, 6},{0x002D, 6},{0x0016, 6}, + {0x004D, 7},{0x0099, 8},{0x0000, 7},{0x0004, 4}, + {0x0001, 4},{0x0005, 5},{0x0017, 6},{0x002E, 6}, + {0x002C, 6},{0x0008, 6},{0x0006, 5},{0x0001, 5} + }, + { + {0x0000, 3},{0x000E, 5},{0x0017, 6},{0x002A, 6}, + {0x0010, 7},{0x00F9,10},{0x00F8,10},{0x001E, 7}, + {0x003F, 8},{0x0007, 3},{0x0006, 3},{0x0009, 4}, + {0x0008, 4},{0x0006, 4},{0x000F, 5},{0x0005, 5}, + {0x0016, 6},{0x0029, 6},{0x002B, 6},{0x0015, 6}, + {0x0050, 7},{0x0011, 7},{0x007D, 9},{0x0004, 4}, + {0x0017, 5},{0x0006, 5},{0x0014, 6},{0x002C, 6}, + {0x002D, 6},{0x000E, 6},{0x0009, 6},{0x0051, 7} + }, + { + {0x0002, 3},{0x0018, 5},{0x002F, 6},{0x000D, 5}, + {0x0053, 7},{0x0295,10},{0x0294,10},{0x00A4, 8}, + {0x007C, 8},{0x0000, 2},{0x0007, 3},{0x0009, 4}, + {0x0008, 4},{0x001B, 5},{0x000C, 5},{0x0028, 6}, + {0x006A, 7},{0x001E, 6},{0x001D, 6},{0x0069, 7}, + {0x00D7, 8},{0x007D, 8},{0x014B, 9},{0x0019, 5}, + {0x0016, 5},{0x002E, 6},{0x001C, 6},{0x002B, 6}, + {0x002A, 6},{0x0068, 7},{0x003F, 7},{0x00D6, 8} + }, + { + {0x0002, 3},{0x001B, 5},{0x000C, 5},{0x0018, 5}, + {0x0029, 6},{0x007F, 8},{0x02F0,10},{0x0198, 9}, + {0x0179, 9},{0x0000, 2},{0x0007, 3},{0x0009, 4}, + {0x0008, 4},{0x001A, 5},{0x000D, 5},{0x002A, 6}, + {0x0064, 7},{0x001E, 6},{0x0067, 7},{0x005F, 7}, + {0x00CD, 8},{0x007E, 8},{0x02F1,10},{0x0016, 5}, + {0x000E, 5},{0x002E, 6},{0x0065, 7},{0x002B, 6}, + {0x0028, 6},{0x003E, 7},{0x00BD, 8},{0x0199, 9} + }, + { + {0x0002, 3},{0x0007, 4},{0x0016, 5},{0x0006, 4}, + {0x0036, 6},{0x005C, 7},{0x015D, 9},{0x015C, 9}, + {0x02BF,10},{0x0000, 2},{0x0007, 3},{0x0009, 4}, + {0x0008, 4},{0x0018, 5},{0x0034, 6},{0x002A, 6}, + {0x005E, 7},{0x006A, 7},{0x0064, 7},{0x005D, 7}, + {0x00CB, 8},{0x00AD, 8},{0x02BE,10},{0x0014, 5}, + {0x0033, 6},{0x006E, 7},{0x005F, 7},{0x006F, 7}, + {0x006B, 7},{0x00CA, 8},{0x00AC, 8},{0x015E, 9} + }, + { + {0x000F, 4},{0x001D, 5},{0x0018, 5},{0x000B, 4}, + {0x0019, 5},{0x0029, 6},{0x00D6, 8},{0x0551,11}, + {0x0AA1,12},{0x0001, 2},{0x0000, 2},{0x0009, 4}, + {0x0008, 4},{0x001B, 5},{0x0038, 6},{0x0028, 6}, + {0x0057, 7},{0x006A, 7},{0x0068, 7},{0x0056, 7}, + {0x00E5, 8},{0x0155, 9},{0x0AA0,12},{0x0073, 7}, + {0x0069, 7},{0x00D7, 8},{0x00AB, 8},{0x00E4, 8}, + {0x00A9, 8},{0x0151, 9},{0x0150, 9},{0x02A9,10} + }, + { + {0x0008, 5},{0x0025, 7},{0x017A, 9},{0x02F7,10}, + {0x0BDB,12},{0x17B4,13},{0x2F6B,14},{0x001D, 5}, + {0x2F6A,14},{0x0008, 4},{0x0007, 4},{0x0001, 4}, + {0x0002, 4},{0x000A, 4},{0x0006, 4},{0x0000, 4}, + {0x001C, 5},{0x0009, 4},{0x000D, 4},{0x000F, 4}, + {0x000C, 4},{0x0003, 4},{0x000A, 5},{0x0016, 5}, + {0x0013, 6},{0x005D, 7},{0x0024, 7},{0x00BC, 8}, + {0x005C, 7},{0x05EC,11},{0x000B, 5},{0x005F, 7} + }, + { + {0x000F, 5},{0x0010, 6},{0x004B, 8},{0x00C6, 8}, + {0x031D,10},{0x0C71,12},{0x0C70,12},{0x0001, 4}, + {0x0C73,12},{0x0008, 4},{0x0009, 4},{0x0002, 4}, + {0x0003, 4},{0x000B, 4},{0x0006, 4},{0x0000, 4}, + {0x001C, 5},{0x0005, 4},{0x000D, 4},{0x000F, 4}, + {0x000A, 4},{0x0019, 5},{0x0013, 6},{0x001D, 5}, + {0x0030, 6},{0x0062, 7},{0x0024, 7},{0x004A, 8}, + {0x018F, 9},{0x0C72,12},{0x000E, 5},{0x0011, 6} + }, + { + {0x001B, 5},{0x0003, 6},{0x008D, 8},{0x0040, 7}, + {0x0239,10},{0x0471,11},{0x08E0,12},{0x0003, 4}, + {0x11C3,13},{0x000A, 4},{0x0009, 4},{0x0004, 4}, + {0x0005, 4},{0x000E, 4},{0x0007, 4},{0x0001, 4}, + {0x001E, 5},{0x0006, 4},{0x000C, 4},{0x000B, 4}, + {0x0002, 4},{0x0000, 5},{0x0041, 7},{0x001F, 5}, + {0x0022, 6},{0x0002, 6},{0x008F, 8},{0x008C, 8}, + {0x011D, 9},{0x11C2,13},{0x001A, 5},{0x0021, 6} + }, + { + {0x001F, 5},{0x0003, 6},{0x0003, 7},{0x0043, 7}, + {0x000B, 9},{0x0015,10},{0x0051,12},{0x0003, 4}, + {0x0050,12},{0x000D, 4},{0x000C, 4},{0x0004, 4}, + {0x0006, 4},{0x000E, 4},{0x000A, 4},{0x0001, 4}, + {0x001E, 5},{0x0005, 4},{0x0009, 4},{0x0007, 4}, + {0x0011, 5},{0x0002, 6},{0x0004, 8},{0x0002, 4}, + {0x002D, 6},{0x0020, 6},{0x0042, 7},{0x0001, 7}, + {0x0000, 7},{0x0029,11},{0x0017, 5},{0x002C, 6} + }, + { + {0x0003, 4},{0x001F, 6},{0x003A, 7},{0x005D, 7}, + {0x0173, 9},{0x02E4,10},{0x172D,13},{0x0004, 4}, + {0x172C,13},{0x000F, 4},{0x000E, 4},{0x0009, 4}, + {0x0008, 4},{0x000C, 4},{0x000A, 4},{0x0001, 4}, + {0x0016, 5},{0x0002, 4},{0x0005, 4},{0x001A, 5}, + {0x002F, 6},{0x0038, 7},{0x05CA,11},{0x0006, 4}, + {0x0037, 6},{0x001E, 6},{0x003B, 7},{0x0039, 7}, + {0x00B8, 8},{0x0B97,12},{0x0000, 4},{0x0036, 6} + }, + { + {0x0006, 4},{0x0037, 6},{0x005D, 7},{0x000C, 6}, + {0x00B9, 8},{0x02E3,10},{0x05C4,11},{0x0004, 4}, + {0x1715,13},{0x0000, 3},{0x000F, 4},{0x0008, 4}, + {0x0007, 4},{0x000C, 4},{0x0009, 4},{0x001D, 5}, + {0x0016, 5},{0x001C, 5},{0x001A, 5},{0x000B, 5}, + {0x005E, 7},{0x0170, 9},{0x1714,13},{0x000A, 4}, + {0x000A, 5},{0x0036, 6},{0x005F, 7},{0x001B, 7}, + {0x001A, 7},{0x0B8B,12},{0x0002, 4},{0x0007, 5} + }, + { + {0x000C, 4},{0x000B, 5},{0x0079, 7},{0x0022, 6}, + {0x00F0, 8},{0x0119, 9},{0x0230,10},{0x001D, 5}, + {0x08C4,12},{0x0001, 3},{0x0000, 3},{0x000A, 4}, + {0x0009, 4},{0x000B, 4},{0x0007, 4},{0x001C, 5}, + {0x003D, 6},{0x000D, 5},{0x0008, 5},{0x0015, 6}, + {0x008D, 8},{0x118B,13},{0x118A,13},{0x000D, 4}, + {0x0010, 5},{0x0009, 5},{0x0014, 6},{0x0047, 7}, + {0x00F1, 8},{0x0463,11},{0x001F, 5},{0x000C, 5} + }, + { + {0x0000, 3},{0x001A, 5},{0x0033, 6},{0x000C, 5}, + {0x0046, 7},{0x01E3, 9},{0x03C5,10},{0x0017, 5}, + {0x1E21,13},{0x0002, 3},{0x0001, 3},{0x0009, 4}, + {0x000A, 4},{0x0007, 4},{0x001B, 5},{0x003D, 6}, + {0x001B, 6},{0x0022, 6},{0x0079, 7},{0x00F0, 8}, + {0x1E20,13},{0x1E23,13},{0x1E22,13},{0x000E, 4}, + {0x0016, 5},{0x0018, 5},{0x0032, 6},{0x001A, 6}, + {0x0047, 7},{0x0789,11},{0x001F, 5},{0x0010, 5} + }, + { + {0x001D, 5},{0x0061, 7},{0x004E, 8},{0x009E, 9}, + {0x027C,11},{0x09F5,13},{0x09F4,13},{0x0003, 4}, + {0x0060, 7},{0x0000, 3},{0x000F, 4},{0x000B, 4}, + {0x000A, 4},{0x0009, 4},{0x0005, 4},{0x000D, 5}, + {0x0031, 6},{0x0008, 5},{0x0038, 6},{0x0012, 6}, + {0x0026, 7},{0x013F,10},{0x04FB,12},{0x000D, 4}, + {0x0002, 4},{0x000C, 5},{0x0039, 6},{0x001C, 6}, + {0x000F, 5},{0x001D, 6},{0x0008, 4},{0x0019, 5} + }, + { + {0x0007, 4},{0x0019, 6},{0x00AB, 8},{0x00AA, 8}, + {0x0119,10},{0x0461,12},{0x0460,12},{0x001B, 5}, + {0x0047, 8},{0x0001, 3},{0x0000, 3},{0x000C, 4}, + {0x000B, 4},{0x0009, 4},{0x0005, 4},{0x000D, 5}, + {0x0035, 6},{0x003D, 6},{0x003C, 6},{0x0018, 6}, + {0x0022, 7},{0x008D, 9},{0x0231,11},{0x000E, 4}, + {0x001F, 5},{0x0009, 5},{0x002B, 6},{0x0010, 6}, + {0x0034, 6},{0x0054, 7},{0x0008, 4},{0x0014, 5} + }, + { + {0x000C, 4},{0x0005, 5},{0x0008, 6},{0x005B, 7}, + {0x004D, 9},{0x0131,11},{0x0261,12},{0x001A, 5}, + {0x0012, 7},{0x0000, 3},{0x000F, 4},{0x000A, 4}, + {0x0009, 4},{0x0006, 4},{0x001B, 5},{0x0006, 5}, + {0x001C, 6},{0x002C, 6},{0x0015, 6},{0x005A, 7}, + {0x0027, 8},{0x0099,10},{0x0260,12},{0x000E, 4}, + {0x0004, 4},{0x000F, 5},{0x0007, 5},{0x001D, 6}, + {0x000B, 5},{0x0014, 6},{0x0008, 4},{0x0017, 5} + }, + { + {0x000F, 4},{0x0013, 5},{0x0075, 7},{0x0024, 6}, + {0x0095, 8},{0x0251,10},{0x04A0,11},{0x0010, 5}, + {0x00C8, 8},{0x0002, 3},{0x0001, 3},{0x0001, 4}, + {0x0000, 4},{0x001A, 5},{0x0011, 5},{0x002C, 6}, + {0x0065, 7},{0x0074, 7},{0x004B, 7},{0x00C9, 8}, + {0x0129, 9},{0x0943,12},{0x0942,12},{0x0003, 3}, + {0x000A, 4},{0x001C, 5},{0x0018, 5},{0x0033, 6}, + {0x0017, 5},{0x002D, 6},{0x001B, 5},{0x003B, 6} + }, + { + {0x0003, 3},{0x001A, 5},{0x002D, 6},{0x0038, 6}, + {0x0028, 7},{0x0395,10},{0x0E51,12},{0x0037, 6}, + {0x00E4, 8},{0x0001, 3},{0x0000, 3},{0x001F, 5}, + {0x001E, 5},{0x0017, 5},{0x003A, 6},{0x0073, 7}, + {0x002A, 7},{0x002B, 7},{0x0029, 7},{0x01CB, 9}, + {0x0729,11},{0x1CA1,13},{0x1CA0,13},{0x0004, 3}, + {0x000A, 4},{0x0004, 4},{0x0018, 5},{0x0036, 6}, + {0x000B, 5},{0x002C, 6},{0x0019, 5},{0x003B, 6} + }, + { + {0x0004, 3},{0x0004, 4},{0x003F, 6},{0x0017, 5}, + {0x0075, 7},{0x01F5, 9},{0x07D1,11},{0x0017, 6}, + {0x01F6, 9},{0x0001, 3},{0x0000, 3},{0x001B, 5}, + {0x001A, 5},{0x000A, 5},{0x0032, 6},{0x0074, 7}, + {0x00F8, 8},{0x00F9, 8},{0x01F7, 9},{0x03E9,10}, + {0x0FA0,12},{0x1F43,13},{0x1F42,13},{0x0003, 3}, + {0x000A, 4},{0x001E, 5},{0x001C, 5},{0x003B, 6}, + {0x0018, 5},{0x0016, 6},{0x0016, 5},{0x0033, 6} + }, + { + {0x0004, 3},{0x0007, 4},{0x0018, 5},{0x001E, 5}, + {0x0036, 6},{0x0031, 7},{0x0177, 9},{0x0077, 7}, + {0x0176, 9},{0x0001, 3},{0x0000, 3},{0x001A, 5}, + {0x0019, 5},{0x003A, 6},{0x0019, 6},{0x005C, 7}, + {0x00BA, 8},{0x0061, 8},{0x00C1, 9},{0x0180,10}, + {0x0302,11},{0x0607,12},{0x0606,12},{0x0002, 3}, + {0x000A, 4},{0x001F, 5},{0x001C, 5},{0x0037, 6}, + {0x0016, 5},{0x0076, 7},{0x000D, 5},{0x002F, 6} + }, + { + {0x0000, 3},{0x000A, 4},{0x001A, 5},{0x000C, 4}, + {0x001D, 5},{0x0039, 6},{0x0078, 7},{0x005E, 7}, + {0x0393,11},{0x0002, 3},{0x0001, 3},{0x0016, 5}, + {0x000F, 5},{0x002E, 6},{0x005F, 7},{0x0073, 8}, + {0x00E5, 9},{0x01C8,10},{0x0E4A,13},{0x1C97,14}, + {0x1C96,14},{0x0E49,13},{0x0E48,13},{0x0004, 3}, + {0x0006, 4},{0x001F, 5},{0x001B, 5},{0x001D, 6}, + {0x0038, 6},{0x0038, 7},{0x003D, 6},{0x0079, 7} + }, + { + {0x000B, 5},{0x002B, 7},{0x0054, 8},{0x01B7, 9}, + {0x06D9,11},{0x0DB1,12},{0x0DB0,12},{0x0002, 4}, + {0x00AB, 9},{0x0009, 4},{0x000A, 4},{0x0007, 4}, + {0x0008, 4},{0x000F, 4},{0x000C, 4},{0x0003, 4}, + {0x001D, 5},{0x0004, 4},{0x000B, 4},{0x0006, 4}, + {0x001A, 5},{0x0003, 6},{0x00AA, 9},{0x0001, 4}, + {0x0000, 5},{0x0014, 6},{0x006C, 7},{0x00DA, 8}, + {0x0002, 6},{0x036D,10},{0x001C, 5},{0x0037, 6} + }, + { + {0x001D, 5},{0x0004, 6},{0x00B6, 8},{0x006A, 8}, + {0x05B9,11},{0x16E1,13},{0x16E0,13},{0x0007, 4}, + {0x016F, 9},{0x000C, 4},{0x000D, 4},{0x0009, 4}, + {0x0008, 4},{0x000F, 4},{0x000A, 4},{0x0003, 4}, + {0x0017, 5},{0x0002, 4},{0x0004, 4},{0x001C, 5}, + {0x002C, 6},{0x006B, 8},{0x0B71,12},{0x0005, 4}, + {0x0003, 5},{0x001B, 6},{0x005A, 7},{0x0034, 7}, + {0x0005, 6},{0x02DD,10},{0x0000, 4},{0x000C, 5} + }, + { + {0x0003, 4},{0x007F, 7},{0x00A1, 8},{0x00A0, 8}, + {0x020C,10},{0x0834,12},{0x106B,13},{0x0007, 4}, + {0x0082, 8},{0x000E, 4},{0x000D, 4},{0x000B, 4}, + {0x000C, 4},{0x0000, 3},{0x0009, 4},{0x0002, 4}, + {0x0011, 5},{0x001E, 5},{0x0015, 5},{0x003E, 6}, + {0x0040, 7},{0x041B,11},{0x106A,13},{0x0006, 4}, + {0x000A, 5},{0x0029, 6},{0x007E, 7},{0x0051, 7}, + {0x0021, 6},{0x0107, 9},{0x0004, 4},{0x000B, 5} + }, + { + {0x0007, 4},{0x001B, 6},{0x00F6, 8},{0x00E9, 8}, + {0x03A1,10},{0x0740,11},{0x0E82,12},{0x001F, 5}, + {0x01EF, 9},{0x0001, 3},{0x0002, 3},{0x000B, 4}, + {0x000C, 4},{0x000D, 4},{0x0008, 4},{0x001C, 5}, + {0x0003, 5},{0x0012, 5},{0x0002, 5},{0x0075, 7}, + {0x01D1, 9},{0x1D07,13},{0x1D06,13},{0x000A, 4}, + {0x0013, 5},{0x003B, 6},{0x001A, 6},{0x007A, 7}, + {0x003C, 6},{0x01EE, 9},{0x0000, 4},{0x000C, 5} + }, + { + {0x000D, 4},{0x003D, 6},{0x0042, 7},{0x0037, 7}, + {0x00D9, 9},{0x0362,11},{0x06C6,12},{0x001F, 5}, + {0x0086, 8},{0x0001, 3},{0x0002, 3},{0x000C, 4}, + {0x000B, 4},{0x000A, 4},{0x0001, 4},{0x000F, 5}, + {0x0025, 6},{0x003C, 6},{0x001A, 6},{0x0087, 8}, + {0x01B0,10},{0x0D8F,13},{0x0D8E,13},{0x000E, 4}, + {0x0013, 5},{0x000C, 5},{0x0024, 6},{0x0020, 6}, + {0x0011, 5},{0x006D, 8},{0x0000, 4},{0x000E, 5} + }, + { + {0x0000, 3},{0x0012, 5},{0x0076, 7},{0x0077, 7}, + {0x014D, 9},{0x0533,11},{0x14C9,13},{0x0013, 5}, + {0x00A5, 8},{0x0002, 3},{0x0003, 3},{0x000B, 4}, + {0x000C, 4},{0x0008, 4},{0x001A, 5},{0x002B, 6}, + {0x0075, 7},{0x0074, 7},{0x00A7, 8},{0x0298,10}, + {0x14C8,13},{0x14CB,13},{0x14CA,13},{0x000F, 4}, + {0x001C, 5},{0x0007, 5},{0x002A, 6},{0x0028, 6}, + {0x001B, 5},{0x00A4, 8},{0x0002, 4},{0x0006, 5} + }, + { + {0x0002, 3},{0x001A, 5},{0x002B, 6},{0x003A, 6}, + {0x00ED, 8},{0x0283,10},{0x0A0A,12},{0x0004, 5}, + {0x00A1, 8},{0x0004, 3},{0x0003, 3},{0x000B, 4}, + {0x000C, 4},{0x001F, 5},{0x0006, 5},{0x0077, 7}, + {0x00A3, 8},{0x00A2, 8},{0x0140, 9},{0x1417,13}, + {0x1416,13},{0x0A09,12},{0x0A08,12},{0x0000, 3}, + {0x001E, 5},{0x0007, 5},{0x002A, 6},{0x0029, 6}, + {0x001C, 5},{0x00EC, 8},{0x001B, 5},{0x0005, 5} + }, + { + {0x0002, 3},{0x0002, 4},{0x0018, 5},{0x001D, 5}, + {0x0035, 6},{0x00E4, 8},{0x01CF,11},{0x001D, 7}, + {0x0072, 9},{0x0004, 3},{0x0005, 3},{0x0006, 4}, + {0x0007, 4},{0x0006, 5},{0x0073, 7},{0x0038, 8}, + {0x01CE,11},{0x039B,12},{0x0398,12},{0x0733,13}, + {0x0732,13},{0x0735,13},{0x0734,13},{0x0000, 3}, + {0x001F, 5},{0x001B, 5},{0x0034, 6},{0x000F, 6}, + {0x001E, 5},{0x00E5, 8},{0x0019, 5},{0x0038, 6} + }, + { + {0x0016, 5},{0x0050, 7},{0x0172, 9},{0x02E7,10}, + {0x1732,13},{0x2E67,14},{0x2E66,14},{0x0006, 4}, + {0x0051, 7},{0x0001, 3},{0x0000, 3},{0x000D, 4}, + {0x000C, 4},{0x0009, 4},{0x001C, 5},{0x0009, 5}, + {0x001C, 6},{0x001D, 6},{0x005D, 7},{0x00B8, 8}, + {0x05CD,11},{0x1731,13},{0x1730,13},{0x000F, 4}, + {0x0005, 4},{0x000F, 5},{0x0008, 5},{0x0029, 6}, + {0x001D, 5},{0x002F, 6},{0x0008, 4},{0x0015, 5} + }, + { + {0x0009, 4},{0x0021, 6},{0x0040, 7},{0x00AD, 8}, + {0x02B0,10},{0x1589,13},{0x1588,13},{0x001C, 5}, + {0x005F, 7},{0x0000, 3},{0x000F, 4},{0x000D, 4}, + {0x000C, 4},{0x0006, 4},{0x0011, 5},{0x002A, 6}, + {0x0057, 7},{0x005E, 7},{0x0041, 7},{0x0159, 9}, + {0x0563,11},{0x158B,13},{0x158A,13},{0x0001, 3}, + {0x0005, 4},{0x0014, 5},{0x003B, 6},{0x002E, 6}, + {0x0004, 4},{0x003A, 6},{0x0007, 4},{0x0016, 5} + }, + { + {0x000E, 4},{0x0007, 5},{0x0046, 7},{0x0045, 7}, + {0x0064, 9},{0x032A,12},{0x0657,13},{0x0018, 5}, + {0x000D, 6},{0x0000, 3},{0x000F, 4},{0x000A, 4}, + {0x000B, 4},{0x001A, 5},{0x0036, 6},{0x0047, 7}, + {0x0044, 7},{0x0018, 7},{0x0033, 8},{0x00CB,10}, + {0x0656,13},{0x0329,12},{0x0328,12},{0x0002, 3}, + {0x0006, 4},{0x0019, 5},{0x000E, 5},{0x0037, 6}, + {0x0009, 4},{0x000F, 5},{0x0002, 4},{0x0010, 5} + }, + { + {0x0003, 3},{0x0018, 5},{0x0023, 6},{0x0077, 7}, + {0x0194, 9},{0x1956,13},{0x32AF,14},{0x003A, 6}, + {0x0076, 7},{0x0002, 3},{0x0001, 3},{0x001F, 5}, + {0x001E, 5},{0x0014, 5},{0x0022, 6},{0x0064, 7}, + {0x0197, 9},{0x0196, 9},{0x032B,10},{0x0654,11}, + {0x32AE,14},{0x1955,13},{0x1954,13},{0x0000, 3}, + {0x0009, 4},{0x001C, 5},{0x0015, 5},{0x0010, 5}, + {0x000D, 4},{0x0017, 5},{0x0016, 5},{0x0033, 6} + }, + { + {0x0005, 3},{0x0006, 4},{0x003E, 6},{0x0010, 5}, + {0x0048, 7},{0x093F,12},{0x24FA,14},{0x0032, 6}, + {0x0067, 7},{0x0002, 3},{0x0001, 3},{0x001B, 5}, + {0x001E, 5},{0x0034, 6},{0x0066, 7},{0x0092, 8}, + {0x0126, 9},{0x024E,10},{0x049E,11},{0x49F7,15}, + {0x49F6,15},{0x24F9,14},{0x24F8,14},{0x0000, 3}, + {0x0007, 4},{0x0018, 5},{0x0011, 5},{0x003F, 6}, + {0x000E, 4},{0x0013, 5},{0x0035, 6},{0x0025, 6} + }, + { + {0x0005, 3},{0x0008, 4},{0x0012, 5},{0x001C, 5}, + {0x001C, 6},{0x00EA, 9},{0x1D75,14},{0x001E, 6}, + {0x0066, 7},{0x0001, 3},{0x0002, 3},{0x001B, 5}, + {0x001A, 5},{0x001F, 6},{0x003B, 7},{0x0074, 8}, + {0x01D6,10},{0x03AF,11},{0x1D74,14},{0x1D77,14}, + {0x1D76,14},{0x0EB9,13},{0x0EB8,13},{0x000F, 4}, + {0x0006, 4},{0x0013, 5},{0x003B, 6},{0x003A, 6}, + {0x0000, 3},{0x0018, 5},{0x0032, 6},{0x0067, 7} + }, + { + {0x0004, 3},{0x000A, 4},{0x001B, 5},{0x000C, 4}, + {0x000D, 5},{0x00E6, 8},{0x0684,11},{0x0072, 7}, + {0x00E7, 8},{0x0002, 3},{0x0001, 3},{0x0017, 5}, + {0x0016, 5},{0x0018, 6},{0x00D1, 8},{0x01A0, 9}, + {0x0686,11},{0x0D0F,12},{0x0D0A,12},{0x1A17,13}, + {0x1A16,13},{0x1A1D,13},{0x1A1C,13},{0x000F, 4}, + {0x001D, 5},{0x000E, 5},{0x0035, 6},{0x0038, 6}, + {0x0000, 3},{0x000F, 5},{0x0019, 6},{0x0069, 7} + }, + { + {0x0003, 3},{0x000C, 4},{0x001B, 5},{0x0000, 3}, + {0x0003, 4},{0x002E, 6},{0x0051, 9},{0x00BC, 8}, + {0x0053, 9},{0x0004, 3},{0x0002, 3},{0x0016, 5}, + {0x0015, 5},{0x0015, 7},{0x0050, 9},{0x00A4,10}, + {0x0294,12},{0x052B,13},{0x052A,13},{0x052D,13}, + {0x052C,13},{0x052F,13},{0x052E,13},{0x000E, 4}, + {0x001A, 5},{0x0004, 5},{0x0028, 6},{0x0029, 6}, + {0x000F, 4},{0x000B, 6},{0x005F, 7},{0x00BD, 8} + }, + { + {0x0003, 4},{0x0009, 6},{0x00D0, 8},{0x01A3, 9}, + {0x0344,10},{0x0D14,12},{0x1A2B,13},{0x0004, 4}, + {0x0015, 7},{0x0000, 3},{0x000F, 4},{0x000B, 4}, + {0x000C, 4},{0x000E, 4},{0x0009, 4},{0x001B, 5}, + {0x000A, 5},{0x0014, 5},{0x000D, 5},{0x002A, 6}, + {0x0014, 7},{0x068B,11},{0x1A2A,13},{0x0008, 4}, + {0x000B, 5},{0x002B, 6},{0x000B, 6},{0x0069, 7}, + {0x0035, 6},{0x0008, 6},{0x0007, 4},{0x000C, 5} + }, + { + {0x000A, 4},{0x003C, 6},{0x0032, 7},{0x0030, 7}, + {0x00C5, 9},{0x0621,12},{0x0620,12},{0x001F, 5}, + {0x0033, 7},{0x0001, 3},{0x0000, 3},{0x000E, 4}, + {0x000D, 4},{0x000C, 4},{0x0004, 4},{0x000D, 5}, + {0x0026, 6},{0x0027, 6},{0x0014, 6},{0x0063, 8}, + {0x0189,10},{0x0623,12},{0x0622,12},{0x000B, 4}, + {0x0012, 5},{0x003D, 6},{0x0022, 6},{0x0015, 6}, + {0x000B, 5},{0x0023, 6},{0x0007, 4},{0x0010, 5} + }, + { + {0x000F, 4},{0x000C, 5},{0x0043, 7},{0x0010, 6}, + {0x0044, 8},{0x0114,10},{0x0455,12},{0x0018, 5}, + {0x0023, 7},{0x0001, 3},{0x0000, 3},{0x000E, 4}, + {0x000D, 4},{0x0009, 4},{0x0019, 5},{0x0009, 5}, + {0x0017, 6},{0x0016, 6},{0x0042, 7},{0x008B, 9}, + {0x0454,12},{0x0457,12},{0x0456,12},{0x000B, 4}, + {0x0015, 5},{0x000A, 5},{0x0029, 6},{0x0020, 6}, + {0x000D, 5},{0x0028, 6},{0x0007, 4},{0x0011, 5} + }, + { + {0x0001, 3},{0x001A, 5},{0x0029, 6},{0x002A, 6}, + {0x00A0, 8},{0x0285,10},{0x1425,13},{0x0002, 5}, + {0x0000, 7},{0x0002, 3},{0x0003, 3},{0x000C, 4}, + {0x000B, 4},{0x0008, 4},{0x0012, 5},{0x0001, 6}, + {0x0051, 7},{0x0001, 7},{0x0143, 9},{0x0508,11}, + {0x1424,13},{0x1427,13},{0x1426,13},{0x000F, 4}, + {0x001C, 5},{0x0003, 5},{0x0037, 6},{0x002B, 6}, + {0x0013, 5},{0x0036, 6},{0x001D, 5},{0x0001, 5} + }, + { + {0x0004, 3},{0x001F, 5},{0x003D, 6},{0x0006, 5}, + {0x0016, 7},{0x0053, 9},{0x014A,11},{0x0034, 6}, + {0x002A, 8},{0x0002, 3},{0x0003, 3},{0x000B, 4}, + {0x000C, 4},{0x001C, 5},{0x0037, 6},{0x0017, 7}, + {0x002B, 8},{0x0028, 8},{0x00A4,10},{0x052D,13}, + {0x052C,13},{0x052F,13},{0x052E,13},{0x0000, 3}, + {0x001D, 5},{0x0007, 5},{0x0004, 5},{0x0035, 6}, + {0x0014, 5},{0x0036, 6},{0x0015, 5},{0x003C, 6} + }, + { + {0x0004, 3},{0x000A, 4},{0x0007, 5},{0x001D, 5}, + {0x0009, 6},{0x01F3, 9},{0x07C7,11},{0x0008, 6}, + {0x01F0, 9},{0x0003, 3},{0x0002, 3},{0x000D, 4}, + {0x000C, 4},{0x0017, 5},{0x007D, 7},{0x01F2, 9}, + {0x07C6,11},{0x07C5,11},{0x1F12,13},{0x3E27,14}, + {0x3E26,14},{0x1F11,13},{0x1F10,13},{0x0000, 3}, + {0x001E, 5},{0x0006, 5},{0x0039, 6},{0x0038, 6}, + {0x003F, 6},{0x002C, 6},{0x0005, 5},{0x002D, 6} + }, + { + {0x0002, 3},{0x0007, 4},{0x0018, 5},{0x0003, 4}, + {0x0005, 5},{0x0035, 7},{0x004F, 9},{0x0012, 7}, + {0x04E5,13},{0x0005, 3},{0x0004, 3},{0x000D, 4}, + {0x000E, 4},{0x0033, 6},{0x0026, 8},{0x009D,10}, + {0x04E4,13},{0x04E7,13},{0x04E6,13},{0x04E1,13}, + {0x04E0,13},{0x04E3,13},{0x04E2,13},{0x0000, 3}, + {0x001F, 5},{0x000C, 5},{0x003D, 6},{0x003C, 6}, + {0x0032, 6},{0x0034, 7},{0x001B, 6},{0x0008, 6} + }, + { + {0x0000, 3},{0x0004, 4},{0x001C, 5},{0x000F, 4}, + {0x0002, 4},{0x0007, 5},{0x0075, 7},{0x00E8, 8}, + {0x1D2A,13},{0x0005, 3},{0x0004, 3},{0x000D, 4}, + {0x000C, 4},{0x0077, 7},{0x0E96,12},{0x3A57,14}, + {0x3A56,14},{0x3A5D,14},{0x3A5C,14},{0x3A5F,14}, + {0x3A5E,14},{0x1D29,13},{0x1D28,13},{0x0003, 3}, + {0x0006, 5},{0x000A, 5},{0x002C, 7},{0x0017, 6}, + {0x0076, 7},{0x01D3, 9},{0x03A4,10},{0x002D, 7} + }, + { + {0x000A, 4},{0x0024, 6},{0x00BF, 8},{0x0085, 8}, + {0x0211,10},{0x0842,12},{0x1087,13},{0x0018, 5}, + {0x0020, 6},{0x0001, 3},{0x0002, 3},{0x000E, 4}, + {0x000D, 4},{0x0007, 4},{0x0013, 5},{0x0025, 6}, + {0x005E, 7},{0x0043, 7},{0x00BE, 8},{0x0109, 9}, + {0x1086,13},{0x0841,12},{0x0840,12},{0x000F, 4}, + {0x0001, 4},{0x0011, 5},{0x0000, 5},{0x002E, 6}, + {0x0019, 5},{0x0001, 5},{0x0006, 4},{0x0016, 5} + }, + { + {0x0002, 3},{0x000F, 5},{0x006F, 7},{0x0061, 7}, + {0x0374,10},{0x1BA8,13},{0x3753,14},{0x0012, 5}, + {0x0036, 6},{0x0000, 3},{0x0001, 3},{0x000A, 4}, + {0x000B, 4},{0x001A, 5},{0x0031, 6},{0x0060, 7}, + {0x00DC, 8},{0x01BB, 9},{0x06EB,11},{0x1BAB,13}, + {0x3752,14},{0x3755,14},{0x3754,14},{0x000E, 4}, + {0x0006, 4},{0x0013, 5},{0x000E, 5},{0x003E, 6}, + {0x0008, 4},{0x001E, 5},{0x0019, 5},{0x003F, 6} + }, + { + {0x0003, 3},{0x001C, 5},{0x0025, 6},{0x0024, 6}, + {0x01DA, 9},{0x1DBD,13},{0x3B7C,14},{0x003C, 6}, + {0x003D, 6},{0x0000, 3},{0x0001, 3},{0x000B, 4}, + {0x000A, 4},{0x000B, 5},{0x0077, 7},{0x00EC, 8}, + {0x03B6,10},{0x076E,11},{0x1DBF,13},{0x76FB,15}, + {0x76FA,15},{0x3B79,14},{0x3B78,14},{0x000D, 4}, + {0x001F, 5},{0x0013, 5},{0x000A, 5},{0x0008, 5}, + {0x000C, 4},{0x0008, 4},{0x0009, 5},{0x003A, 6} + }, + { + {0x0005, 3},{0x0003, 4},{0x0004, 5},{0x0010, 5}, + {0x008F, 8},{0x0475,11},{0x11D1,13},{0x0079, 7}, + {0x0027, 6},{0x0002, 3},{0x0003, 3},{0x0001, 4}, + {0x0000, 4},{0x0026, 6},{0x0046, 7},{0x011C, 9}, + {0x0477,11},{0x08ED,12},{0x11D0,13},{0x11D3,13}, + {0x11D2,13},{0x11D9,13},{0x11D8,13},{0x000D, 4}, + {0x001F, 5},{0x0012, 5},{0x0005, 5},{0x003D, 6}, + {0x000C, 4},{0x000E, 4},{0x0022, 6},{0x0078, 7} + }, + { + {0x0005, 3},{0x000C, 4},{0x001B, 5},{0x0000, 4}, + {0x0006, 6},{0x03E2,10},{0x3E3D,14},{0x000F, 7}, + {0x0034, 6},{0x0003, 3},{0x0002, 3},{0x001E, 5}, + {0x001D, 5},{0x007D, 7},{0x01F0, 9},{0x07C6,11}, + {0x3E3C,14},{0x3E3F,14},{0x3E3E,14},{0x3E39,14}, + {0x3E38,14},{0x3E3B,14},{0x3E3A,14},{0x0008, 4}, + {0x001C, 5},{0x0002, 5},{0x003F, 6},{0x0035, 6}, + {0x0009, 4},{0x0001, 3},{0x000E, 7},{0x00F9, 8} + }, + { + {0x0004, 3},{0x000B, 4},{0x0001, 4},{0x000A, 4}, + {0x001E, 6},{0x00E0, 9},{0x0E1E,13},{0x0071, 8}, + {0x0039, 7},{0x0007, 3},{0x0006, 3},{0x000D, 5}, + {0x000C, 5},{0x0020, 7},{0x01C2,10},{0x1C3F,14}, + {0x1C3E,14},{0x0E19,13},{0x0E18,13},{0x0E1B,13}, + {0x0E1A,13},{0x0E1D,13},{0x0E1C,13},{0x0000, 4}, + {0x0009, 5},{0x001D, 6},{0x001F, 6},{0x0011, 6}, + {0x0005, 4},{0x0001, 3},{0x0043, 8},{0x0042, 8} + }, + { + {0x0004, 3},{0x000D, 4},{0x0007, 4},{0x0002, 3}, + {0x0014, 5},{0x016C, 9},{0x16D1,13},{0x02DF,10}, + {0x016E, 9},{0x0000, 2},{0x0007, 3},{0x002C, 6}, + {0x002B, 6},{0x02DE,10},{0x16D0,13},{0x16D3,13}, + {0x16D2,13},{0x2DB5,14},{0x2DB4,14},{0x2DB7,14}, + {0x2DB6,14},{0x16D9,13},{0x16D8,13},{0x000C, 5}, + {0x002A, 6},{0x005A, 7},{0x001B, 6},{0x001A, 6}, + {0x0017, 5},{0x000C, 4},{0x05B7,11},{0x05B5,11} + }, + { + {0x0002, 2},{0x000F, 4},{0x001C, 5},{0x000C, 4}, + {0x003B, 6},{0x01AC, 9},{0x1AD8,13},{0x35B3,14}, + {0x35B2,14},{0x0001, 2},{0x0000, 2},{0x0069, 7}, + {0x0068, 7},{0x35BD,14},{0x35BC,14},{0x35BF,14}, + {0x35BE,14},{0x35B9,14},{0x35B8,14},{0x35BB,14}, + {0x35BA,14},{0x35B5,14},{0x35B4,14},{0x01A9, 9}, + {0x01A8, 9},{0x035A,10},{0x00D7, 8},{0x00D5, 8}, + {0x003A, 6},{0x001B, 5},{0x35B7,14},{0x35B6,14} + }, + { + {0x0000, 3},{0x0010, 5},{0x0072, 7},{0x0071, 7}, + {0x0154, 9},{0x0AAB,12},{0x0AA8,12},{0x0014, 5}, + {0x0070, 7},{0x0002, 3},{0x0003, 3},{0x000C, 4}, + {0x000B, 4},{0x0003, 4},{0x0011, 5},{0x0073, 7}, + {0x0054, 7},{0x00AB, 8},{0x02AB,10},{0x1553,13}, + {0x1552,13},{0x1555,13},{0x1554,13},{0x000D, 4}, + {0x001E, 5},{0x0012, 5},{0x003E, 6},{0x002B, 6}, + {0x0002, 4},{0x003F, 6},{0x001D, 5},{0x0013, 5} + }, + { + {0x0003, 3},{0x001F, 5},{0x0029, 6},{0x003D, 6}, + {0x000C, 7},{0x0069,10},{0x0345,13},{0x0002, 5}, + {0x0028, 6},{0x0002, 3},{0x0001, 3},{0x000E, 4}, + {0x000C, 4},{0x0015, 5},{0x0007, 6},{0x001B, 8}, + {0x006B,10},{0x006A,10},{0x0344,13},{0x0347,13}, + {0x0346,13},{0x01A1,12},{0x01A0,12},{0x000B, 4}, + {0x001A, 5},{0x0012, 5},{0x0000, 5},{0x003C, 6}, + {0x0008, 4},{0x001B, 5},{0x0013, 5},{0x0001, 5} + }, + { + {0x0004, 3},{0x0004, 4},{0x003F, 6},{0x0014, 5}, + {0x0056, 7},{0x015C, 9},{0x15D5,13},{0x003C, 6}, + {0x002A, 6},{0x0000, 3},{0x0001, 3},{0x000E, 4}, + {0x000D, 4},{0x000C, 5},{0x00AF, 8},{0x02BB,10}, + {0x15D4,13},{0x15D7,13},{0x15D6,13},{0x15D1,13}, + {0x15D0,13},{0x15D3,13},{0x15D2,13},{0x000B, 4}, + {0x0019, 5},{0x000D, 5},{0x003E, 6},{0x0031, 6}, + {0x0007, 4},{0x0005, 4},{0x003D, 6},{0x0030, 6} + }, + { + {0x0005, 3},{0x0008, 4},{0x001A, 5},{0x0000, 4}, + {0x0036, 6},{0x0011, 8},{0x0106,12},{0x000A, 7}, + {0x006E, 7},{0x0002, 3},{0x0003, 3},{0x0003, 4}, + {0x0002, 4},{0x006F, 7},{0x0021, 9},{0x020F,13}, + {0x020E,13},{0x0101,12},{0x0100,12},{0x0103,12}, + {0x0102,12},{0x0105,12},{0x0104,12},{0x000C, 4}, + {0x001E, 5},{0x0003, 5},{0x003E, 6},{0x003F, 6}, + {0x0009, 4},{0x000E, 4},{0x000B, 7},{0x0009, 7} + }, + { + {0x0002, 3},{0x000E, 4},{0x001E, 5},{0x000C, 4}, + {0x001F, 5},{0x006E, 7},{0x00AD,10},{0x00AF,10}, + {0x0014, 7},{0x0004, 3},{0x0003, 3},{0x001A, 5}, + {0x0017, 5},{0x002A, 8},{0x0576,13},{0x0AEF,14}, + {0x0AEE,14},{0x0571,13},{0x0570,13},{0x0573,13}, + {0x0572,13},{0x0575,13},{0x0574,13},{0x0003, 4}, + {0x0016, 5},{0x0004, 5},{0x0036, 6},{0x000B, 6}, + {0x000A, 4},{0x0000, 3},{0x006F, 7},{0x00AC,10} + }, + { + {0x0004, 3},{0x0005, 4},{0x0003, 3},{0x0001, 3}, + {0x0004, 4},{0x002F, 6},{0x0526,11},{0x1495,13}, + {0x00A6, 8},{0x0007, 3},{0x0006, 3},{0x002D, 6}, + {0x002C, 6},{0x1494,13},{0x1497,13},{0x1496,13}, + {0x1491,13},{0x1490,13},{0x1493,13},{0x1492,13}, + {0x293D,14},{0x293C,14},{0x293F,14},{0x0000, 3}, + {0x0028, 6},{0x00A5, 8},{0x0148, 9},{0x00A7, 8}, + {0x002E, 6},{0x0015, 5},{0x0A4E,12},{0x293E,14} + }, + { + {0x0004, 3},{0x0005, 4},{0x0003, 3},{0x0001, 3}, + {0x0004, 4},{0x002F, 6},{0x0526,11},{0x1495,13}, + {0x00A6, 8},{0x0007, 3},{0x0006, 3},{0x002D, 6}, + {0x002C, 6},{0x1494,13},{0x1497,13},{0x1496,13}, + {0x1491,13},{0x1490,13},{0x1493,13},{0x1492,13}, + {0x293D,14},{0x293C,14},{0x293F,14},{0x0000, 3}, + {0x0028, 6},{0x00A5, 8},{0x0148, 9},{0x00A7, 8}, + {0x002E, 6},{0x0015, 5},{0x0A4E,12},{0x293E,14} + }, + { + {0x0004, 3},{0x0005, 4},{0x0003, 3},{0x0001, 3}, + {0x0004, 4},{0x002F, 6},{0x0526,11},{0x1495,13}, + {0x00A6, 8},{0x0007, 3},{0x0006, 3},{0x002D, 6}, + {0x002C, 6},{0x1494,13},{0x1497,13},{0x1496,13}, + {0x1491,13},{0x1490,13},{0x1493,13},{0x1492,13}, + {0x293D,14},{0x293C,14},{0x293F,14},{0x0000, 3}, + {0x0028, 6},{0x00A5, 8},{0x0148, 9},{0x00A7, 8}, + {0x002E, 6},{0x0015, 5},{0x0A4E,12},{0x293E,14} + }, + { + {0x0003, 3},{0x0011, 5},{0x0020, 6},{0x0074, 7}, + {0x010D, 9},{0x0863,12},{0x0860,12},{0x000A, 5}, + {0x0075, 7},{0x0001, 3},{0x0000, 3},{0x000B, 4}, + {0x000A, 4},{0x0018, 5},{0x0038, 6},{0x0042, 7}, + {0x010F, 9},{0x010E, 9},{0x0219,10},{0x10C3,13}, + {0x10C2,13},{0x10C5,13},{0x10C4,13},{0x000F, 4}, + {0x0004, 4},{0x0019, 5},{0x000B, 5},{0x0039, 6}, + {0x0009, 4},{0x001B, 5},{0x001A, 5},{0x003B, 6} + }, + { + {0x0005, 3},{0x0001, 4},{0x003E, 6},{0x0001, 5}, + {0x00E2, 8},{0x1C6F,13},{0x38D9,14},{0x0039, 6}, + {0x001F, 6},{0x0002, 3},{0x0001, 3},{0x0009, 4}, + {0x0008, 4},{0x0000, 5},{0x0070, 7},{0x01C7, 9}, + {0x038C,10},{0x071A,11},{0x38D8,14},{0x38DB,14}, + {0x38DA,14},{0x38DD,14},{0x38DC,14},{0x000D, 4}, + {0x001D, 5},{0x000E, 5},{0x003F, 6},{0x003C, 6}, + {0x000C, 4},{0x0006, 4},{0x003D, 6},{0x001E, 6} + }, + { + {0x0006, 3},{0x000B, 4},{0x0011, 5},{0x001E, 5}, + {0x0074, 7},{0x03AA,10},{0x1D5C,13},{0x0001, 6}, + {0x0021, 6},{0x0001, 3},{0x0002, 3},{0x0007, 4}, + {0x0006, 4},{0x003E, 6},{0x00EB, 8},{0x01D4, 9}, + {0x0EAF,12},{0x3ABB,14},{0x3ABA,14},{0x1D59,13}, + {0x1D58,13},{0x1D5B,13},{0x1D5A,13},{0x000A, 4}, + {0x001C, 5},{0x0001, 5},{0x003F, 6},{0x003B, 6}, + {0x0001, 4},{0x0009, 4},{0x0020, 6},{0x0000, 6} + }, + { + {0x0004, 3},{0x000A, 4},{0x0017, 5},{0x0004, 4}, + {0x0016, 6},{0x016A, 9},{0x16B1,13},{0x0017, 7}, + {0x005B, 7},{0x0006, 3},{0x0007, 3},{0x0001, 4}, + {0x0000, 4},{0x000A, 6},{0x02D7,10},{0x0B5A,12}, + {0x16B0,13},{0x16B3,13},{0x16B2,13},{0x2D6D,14}, + {0x2D6C,14},{0x2D6F,14},{0x2D6E,14},{0x0006, 4}, + {0x000A, 5},{0x0004, 5},{0x002C, 6},{0x0017, 6}, + {0x0003, 4},{0x0007, 4},{0x0016, 7},{0x00B4, 8} + }, + { + {0x0005, 3},{0x000D, 4},{0x0005, 4},{0x0009, 4}, + {0x0033, 6},{0x0193, 9},{0x192C,13},{0x0061, 8}, + {0x0031, 7},{0x0000, 2},{0x0007, 3},{0x0010, 5}, + {0x0011, 5},{0x00C8, 8},{0x192F,13},{0x325B,14}, + {0x325A,14},{0x1929,13},{0x1928,13},{0x192B,13}, + {0x192A,13},{0x325D,14},{0x325C,14},{0x0018, 5}, + {0x001A, 6},{0x001B, 6},{0x0065, 7},{0x0019, 6}, + {0x0004, 4},{0x0007, 4},{0x0060, 8},{0x0324,10} + }, + { + {0x0006, 3},{0x0000, 3},{0x0002, 4},{0x000F, 4}, + {0x0039, 6},{0x01D9, 9},{0x1D82,13},{0x0761,11}, + {0x03BE,10},{0x0001, 2},{0x0002, 2},{0x000F, 6}, + {0x000E, 6},{0x0762,11},{0x3B07,14},{0x3B06,14}, + {0x3B1D,14},{0x3B1C,14},{0x3B1F,14},{0x3B1E,14}, + {0x3B19,14},{0x3B18,14},{0x3B1B,14},{0x0038, 6}, + {0x01DE, 9},{0x00ED, 8},{0x03BF,10},{0x00EE, 8}, + {0x003A, 6},{0x0006, 5},{0x0EC0,12},{0x3B1A,14} + }, + { + {0x0000, 2},{0x0002, 3},{0x000F, 5},{0x0006, 4}, + {0x001C, 6},{0x01D0,10},{0x0E8C,13},{0x1D1B,14}, + {0x1D1A,14},{0x0003, 2},{0x0002, 2},{0x00EA, 9}, + {0x00E9, 9},{0x0E89,13},{0x0E88,13},{0x0E8B,13}, + {0x0E8A,13},{0x1D65,14},{0x1D64,14},{0x1D67,14}, + {0x1D66,14},{0x1D61,14},{0x1D60,14},{0x03AD,11}, + {0x1D63,14},{0x1D62,14},{0x1D1D,14},{0x1D1C,14}, + {0x003B, 7},{0x01D7,10},{0x1D1F,14},{0x1D1E,14} + }, + { + {0x0002, 2},{0x000F, 4},{0x001C, 5},{0x000C, 4}, + {0x003B, 6},{0x01AC, 9},{0x1AD8,13},{0x35B3,14}, + {0x35B2,14},{0x0001, 2},{0x0000, 2},{0x0069, 7}, + {0x0068, 7},{0x35BD,14},{0x35BC,14},{0x35BF,14}, + {0x35BE,14},{0x35B9,14},{0x35B8,14},{0x35BB,14}, + {0x35BA,14},{0x35B5,14},{0x35B4,14},{0x01A9, 9}, + {0x01A8, 9},{0x035A,10},{0x00D7, 8},{0x00D5, 8}, + {0x003A, 6},{0x001B, 5},{0x35B7,14},{0x35B6,14} + } +}; + + + +static void th_info2theora_info(theora_info *_ci,const th_info *_info){ + _ci->version_major=_info->version_major; + _ci->version_minor=_info->version_minor; + _ci->version_subminor=_info->version_subminor; + _ci->width=_info->frame_width; + _ci->height=_info->frame_height; + _ci->frame_width=_info->pic_width; + _ci->frame_height=_info->pic_height; + _ci->offset_x=_info->pic_x; + _ci->offset_y=_info->pic_y; + _ci->fps_numerator=_info->fps_numerator; + _ci->fps_denominator=_info->fps_denominator; + _ci->aspect_numerator=_info->aspect_numerator; + _ci->aspect_denominator=_info->aspect_denominator; + switch(_info->colorspace){ + case TH_CS_ITU_REC_470M:_ci->colorspace=OC_CS_ITU_REC_470M;break; + case TH_CS_ITU_REC_470BG:_ci->colorspace=OC_CS_ITU_REC_470BG;break; + default:_ci->colorspace=OC_CS_UNSPECIFIED;break; + } + switch(_info->pixel_fmt){ + case TH_PF_420:_ci->pixelformat=OC_PF_420;break; + case TH_PF_422:_ci->pixelformat=OC_PF_422;break; + case TH_PF_444:_ci->pixelformat=OC_PF_444;break; + default:_ci->pixelformat=OC_PF_RSVD; + } + _ci->target_bitrate=_info->target_bitrate; + _ci->quality=_info->quality; + _ci->codec_setup=NULL; + /*Defaults from old encoder_example... eventually most of these should go + away when we make the encoder no longer use them.*/ + _ci->dropframes_p=0; + _ci->keyframe_auto_p=1; + _ci->keyframe_frequency=1<<_info->keyframe_granule_shift; + _ci->keyframe_frequency_force=1<<_info->keyframe_granule_shift; + _ci->keyframe_data_target_bitrate= + _info->target_bitrate+(_info->target_bitrate>>1); + _ci->keyframe_auto_threshold=80; + _ci->keyframe_mindistance=8; + _ci->noise_sensitivity=1; + _ci->sharpness=0; + _ci->quick_p=1; +} + +static int _ilog(unsigned _v){ + int ret; + for(ret=0;_v;ret++)_v>>=1; + return ret; +} + + + +struct th_enc_ctx{ + /*This is required at the start of the struct for the common functions to + work.*/ + th_info info; + /*The actual encoder.*/ + theora_state state; + /*A temporary buffer for input frames. + This is needed if the U and V strides differ, or padding is required.*/ + unsigned char *buf; +}; + + +th_enc_ctx *th_encode_alloc(const th_info *_info){ + theora_info ci; + th_enc_ctx *enc; + th_info2theora_info(&ci,_info); + /*Do a bunch of checks the new API does, but the old one didn't.*/ + if((_info->frame_width&0xF)||(_info->frame_height&0xF)|| + _info->frame_width>=0x100000||_info->frame_height>=0x100000|| + _info->pic_x+_info->pic_width>_info->frame_width|| + _info->pic_y+_info->pic_height>_info->frame_height|| + _info->pic_x>255|| + _info->frame_height-_info->pic_height-_info->pic_y>255|| + _info->colorspace<0||_info->colorspace>=TH_CS_NSPACES|| + _info->pixel_fmt<0||_info->pixel_fmt>=TH_PF_NFORMATS){ + enc=NULL; + } + else{ + enc=(th_enc_ctx *)_ogg_malloc(sizeof(*enc)); + if(theora_encode_init(&enc->state,&ci)<0){ + _ogg_free(enc); + enc=NULL; + } + else{ + if(_info->frame_width>_info->pic_width|| + _info->frame_height>_info->pic_height){ + enc->buf=_ogg_malloc((_info->frame_width*_info->frame_height+ + ((_info->frame_width>>!(_info->pixel_fmt&1))* + (_info->frame_height>>!(_info->pixel_fmt&2))<<1))*sizeof(*enc->buf)); + } + else enc->buf=NULL; + memcpy(&enc->info,_info,sizeof(enc->info)); + /*Overwrite values theora_encode_init() can change; don't trust the user.*/ + enc->info.version_major=ci.version_major; + enc->info.version_minor=ci.version_minor; + enc->info.version_subminor=ci.version_subminor; + enc->info.quality=ci.quality; + enc->info.target_bitrate=ci.target_bitrate; + enc->info.fps_numerator=ci.fps_numerator; + enc->info.fps_denominator=ci.fps_denominator; + enc->info.keyframe_granule_shift=_ilog(ci.keyframe_frequency_force-1); + } + } + return enc; +} + +int th_encode_ctl(th_enc_ctx *_enc,int _req,void *_buf,size_t _buf_sz){ + return theora_control(&_enc->state,_req,_buf,_buf_sz); +} + +int th_encode_flushheader(th_enc_ctx *_enc,th_comment *_comments, + ogg_packet *_op){ + theora_state *te; + CP_INSTANCE *cpi; + if(_enc==NULL||_op==NULL)return OC_FAULT; + te=&_enc->state; + cpi=(CP_INSTANCE *)te->internal_encode; + switch(cpi->doneflag){ + case -3:{ + theora_encode_header(te,_op); + return -cpi->doneflag++; + }break; + case -2:{ + if(_comments==NULL)return OC_FAULT; + theora_encode_comment((theora_comment *)_comments,_op); + /*The old API does not require a theora_state struct when writing the + comment header, so it can't use its internal buffer and relies on the + application to free it. + The old documentation is wrong on this subject, and this breaks on + Windows when linking against multiple versions of libc (which is + almost always done when, e.g., using DLLs built with mingw32). + The new API _does_ require a th_enc_ctx, and states that libtheora owns + the memory. + Thus we move the contents of this packet into our internal + oggpack_buffer so it can be properly reclaimed.*/ + oggpackB_reset(cpi->oggbuffer); + oggpackB_writecopy(cpi->oggbuffer,_op->packet,_op->bytes*8); + _ogg_free(_op->packet); + _op->packet=oggpackB_get_buffer(cpi->oggbuffer); + return -cpi->doneflag++; + }break; + case -1:{ + theora_encode_tables(te,_op); + return -cpi->doneflag++; + }break; + case 0:return 0; + default:return OC_EINVAL; + } +} + +/*Copies the picture region of the _src image plane into _dst and pads the rest + of _dst using a diffusion extension method. + We could do much better (e.g., the DCT-based low frequency extension method + in theora-exp's fdct.c) if we were to pad after motion compensation, but + that would require significant changes to the encoder.*/ +static unsigned char *th_encode_copy_pad_plane(th_img_plane *_dst, + unsigned char *_buf,th_img_plane *_src, + ogg_uint32_t _pic_x,ogg_uint32_t _pic_y, + ogg_uint32_t _pic_width,ogg_uint32_t _pic_height){ + size_t buf_sz; + _dst->width=_src->width; + _dst->height=_src->height; + _dst->stride=_src->width; + _dst->data=_buf; + buf_sz=_dst->width*_dst->height*sizeof(*_dst->data); + /*If we have _no_ data, just encode a dull green.*/ + if(_pic_width==0||_pic_height==0)memset(_dst->data,0,buf_sz); + else{ + unsigned char *dst; + unsigned char *src; + ogg_uint32_t x; + ogg_uint32_t y; + int dstride; + int sstride; + /*Step 1: Copy the data we do have.*/ + dstride=_dst->stride; + sstride=_src->stride; + dst=_dst->data+_pic_y*dstride+_pic_x; + src=_src->data+_pic_y*sstride+_pic_x; + for(y=0;y<_pic_height;y++){ + memcpy(dst,src,_pic_width); + dst+=dstride; + src+=sstride; + } + /*Step 2: Copy the border into any blocks that are 100% padding. + There's probably smarter things we could do than this.*/ + /*Left side.*/ + for(x=_pic_x;x-->0;){ + dst=_dst->data+_pic_y*dstride+x; + for(y=0;y<_pic_height;y++){ + dst[0]=(dst[1]<<1)+(dst-(dstride&-(y>0)))[1]+ + (dst+(dstride&-(y+1<_pic_height)))[1]+2>>2; + dst+=dstride; + } + } + /*Right side.*/ + for(x=_pic_x+_pic_width;x<_dst->width;x++){ + dst=_dst->data+_pic_y*dstride+x-1; + for(y=0;y<_pic_height;y++){ + dst[1]=(dst[0]<<1)+(dst-(dstride&-(y>0)))[0]+ + (dst+(dstride&-(y+1<_pic_height)))[0]+2>>2; + dst+=dstride; + } + } + /*Top.*/ + dst=_dst->data+_pic_y*dstride; + for(y=_pic_y;y-->0;){ + for(x=0;x<_dst->width;x++){ + (dst-dstride)[x]=(dst[x]<<1)+dst[x-(x>0)]+dst[x+(x+1<_dst->width)]+2>>2; + } + dst-=dstride; + } + /*Bottom.*/ + dst=_dst->data+(_pic_y+_pic_height)*dstride; + for(y=_pic_y+_pic_height;y<_dst->height;y++){ + for(x=0;x<_dst->width;x++){ + dst[x]=((dst-dstride)[x]<<1)+(dst-dstride)[x-(x>0)]+ + (dst-dstride)[x+(x+1<_dst->width)]+2>>2; + } + dst+=dstride; + } + } + _buf+=buf_sz; + return _buf; +} + +int th_encode_ycbcr_in(th_enc_ctx *_enc,th_ycbcr_buffer _ycbcr){ + CP_INSTANCE *cpi; + theora_state *te; + th_img_plane *pycbcr; + th_ycbcr_buffer ycbcr; + yuv_buffer yuv; + ogg_uint32_t pic_width; + ogg_uint32_t pic_height; + int hdec; + int vdec; + int ret; + if(_enc==NULL||_ycbcr==NULL)return OC_FAULT; + te=&_enc->state; + /*theora_encode_YUVin() does not bother to check uv_width and uv_height, and + then uses them. + This is arguably okay (it will most likely lead to a crash if they're + wrong, which will make the developer who passed them fix the problem), but + our API promises to return an error code instead.*/ + cpi=(CP_INSTANCE *)te->internal_encode; + hdec=!(cpi->pb.info.pixelformat&1); + vdec=!(cpi->pb.info.pixelformat&2); + if(_ycbcr[0].width!=cpi->pb.info.width|| + _ycbcr[0].height!=cpi->pb.info.height|| + _ycbcr[1].width!=_ycbcr[0].width>>hdec|| + _ycbcr[1].height!=_ycbcr[0].height>>vdec|| + _ycbcr[2].width!=_ycbcr[1].width||_ycbcr[2].height!=_ycbcr[1].height){ + return OC_EINVAL; + } + pic_width=cpi->pb.info.frame_width; + pic_height=cpi->pb.info.frame_height; + /*We can only directly use the input buffer if no padding is required (since + the new API is documented not to use values outside the picture region) + and if the strides for the Cb and Cr planes are the same, since the old + API had no way to specify different ones.*/ + if(_ycbcr[0].width==pic_width&&_ycbcr[0].height==pic_height&& + _ycbcr[1].stride==_ycbcr[2].stride){ + pycbcr=_ycbcr; + } + else{ + unsigned char *buf; + int pic_x; + int pic_y; + int pli; + pic_x=cpi->pb.info.offset_x; + pic_y=cpi->pb.info.offset_y; + if(_ycbcr[0].width>pic_width||_ycbcr[0].height>pic_height){ + buf=th_encode_copy_pad_plane(ycbcr+0,_enc->buf,_ycbcr+0, + pic_x,pic_y,pic_width,pic_height); + } + else{ + /*If only the strides differ, we can still avoid copying the luma plane.*/ + memcpy(ycbcr+0,_ycbcr+0,sizeof(ycbcr[0])); + if(_enc->buf==NULL){ + _enc->buf=(unsigned char *)_ogg_malloc( + (_ycbcr[1].width*_ycbcr[1].height<<1)*sizeof(*_enc->buf)); + } + buf=_enc->buf; + } + for(pli=1;pli<3;pli++){ + int x0; + int y0; + x0=pic_x>>hdec; + y0=pic_y>>vdec; + buf=th_encode_copy_pad_plane(ycbcr+pli,buf,_ycbcr+pli, + x0,y0,(pic_x+pic_width+hdec>>hdec)-x0,(pic_y+pic_height+vdec>>vdec)-y0); + } + pycbcr=ycbcr; + } + yuv.y_width=pycbcr[0].width; + yuv.y_height=pycbcr[0].height; + yuv.uv_width=pycbcr[1].width; + yuv.uv_height=pycbcr[1].height; + yuv.y_stride=pycbcr[0].stride; + yuv.y=pycbcr[0].data; + yuv.uv_stride=pycbcr[1].stride; + yuv.u=pycbcr[1].data; + yuv.v=pycbcr[2].data; + ret=theora_encode_YUVin(te,&yuv); + return ret; +} + +int th_encode_packetout(th_enc_ctx *_enc,int _last,ogg_packet *_op){ + if(_enc==NULL)return OC_FAULT; + return theora_encode_packetout(&_enc->state,_last,_op); +} + +void th_encode_free(th_enc_ctx *_enc){ + if(_enc!=NULL){ + theora_clear(&_enc->state); + _ogg_free(_enc->buf); + _ogg_free(_enc); + } +} diff --git a/Engine/lib/libtheora/lib/enc/encode.c b/Engine/lib/libtheora/lib/enc/encode.c new file mode 100644 index 000000000..5dc89f2af --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/encode.c @@ -0,0 +1,1479 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: encode.c 15383 2008-10-10 14:33:46Z xiphmont $ + + ********************************************************************/ + +#include +#include +#include "codec_internal.h" +#include "encoder_lookup.h" +#include "block_inline.h" + +#define PUR 8 +#define PU 4 +#define PUL 2 +#define PL 1 +#define HIGHBITDUPPED(X) (((ogg_int16_t) X) >> 15) + +static ogg_uint32_t QuadCodeComponent ( CP_INSTANCE *cpi, + ogg_uint32_t FirstSB, + ogg_uint32_t SBRows, + ogg_uint32_t SBCols, + ogg_uint32_t PixelsPerLine){ + + ogg_int32_t FragIndex; /* Fragment number */ + ogg_uint32_t MB, B; /* Macro-Block, Block indices */ + ogg_uint32_t SBrow; /* Super-Block row number */ + ogg_uint32_t SBcol; /* Super-Block row number */ + ogg_uint32_t SB=FirstSB; /* Super-Block index, initialised to first + of this component */ + ogg_uint32_t coded_pixels=0; /* Number of pixels coded */ + int MBCodedFlag; + + /* actually transform and quantize the image now that we've decided + on the modes Parse in quad-tree ordering */ + + for ( SBrow=0; SBrowpb.BlockMap,SB,MB) >= 0 ) { + + MBCodedFlag = 0; + + /* Now actually code the blocks */ + for ( B=0; B<4; B++ ) { + FragIndex = QuadMapToIndex1( cpi->pb.BlockMap, SB, MB, B ); + + /* Does Block lie in frame: */ + if ( FragIndex >= 0 ) { + + /* In Frame: Is it coded: */ + if ( cpi->pb.display_fragments[FragIndex] ) { + + /* transform and quantize block */ + TransformQuantizeBlock( cpi, FragIndex, PixelsPerLine ); + + /* Has the block got struck off (no MV and no data + generated after DCT) If not then mark it and the + assosciated MB as coded. */ + if ( cpi->pb.display_fragments[FragIndex] ) { + /* Create linear list of coded block indices */ + cpi->pb.CodedBlockList[cpi->pb.CodedBlockIndex] = FragIndex; + cpi->pb.CodedBlockIndex++; + + /* MB is still coded */ + MBCodedFlag = 1; + cpi->MBCodingMode = cpi->pb.FragCodingMethod[FragIndex]; + + } + } + } + } + /* If the MB is marked as coded and we are in the Y plane then */ + /* the mode list needs to be updated. */ + if ( MBCodedFlag && (FirstSB == 0) ){ + /* Make a note of the selected mode in the mode list */ + cpi->ModeList[cpi->ModeListCount] = cpi->MBCodingMode; + cpi->ModeListCount++; + } + } + } + + SB++; + + } + } + + /* Return number of pixels coded */ + return coded_pixels; +} + +static void EncodeDcTokenList (CP_INSTANCE *cpi) { + ogg_int32_t i,j; + ogg_uint32_t Token; + ogg_uint32_t ExtraBitsToken; + ogg_uint32_t HuffIndex; + + ogg_uint32_t BestDcBits; + ogg_uint32_t DcHuffChoice[2]; + ogg_uint32_t EntropyTableBits[2][DC_HUFF_CHOICES]; + + oggpack_buffer *opb=cpi->oggbuffer; + + /* Clear table data structure */ + memset ( EntropyTableBits, 0, sizeof(ogg_uint32_t)*DC_HUFF_CHOICES*2 ); + + /* Analyse token list to see which is the best entropy table to use */ + for ( i = 0; i < cpi->OptimisedTokenCount; i++ ) { + /* Count number of bits for each table option */ + Token = (ogg_uint32_t)cpi->OptimisedTokenList[i]; + for ( j = 0; j < DC_HUFF_CHOICES; j++ ){ + EntropyTableBits[cpi->OptimisedTokenListPl[i]][j] += + cpi->pb.HuffCodeLengthArray_VP3x[DC_HUFF_OFFSET + j][Token]; + } + } + + /* Work out which table option is best for Y */ + BestDcBits = EntropyTableBits[0][0]; + DcHuffChoice[0] = 0; + for ( j = 1; j < DC_HUFF_CHOICES; j++ ) { + if ( EntropyTableBits[0][j] < BestDcBits ) { + BestDcBits = EntropyTableBits[0][j]; + DcHuffChoice[0] = j; + } + } + + /* Add the DC huffman table choice to the bitstream */ + oggpackB_write( opb, DcHuffChoice[0], DC_HUFF_CHOICE_BITS ); + + /* Work out which table option is best for UV */ + BestDcBits = EntropyTableBits[1][0]; + DcHuffChoice[1] = 0; + for ( j = 1; j < DC_HUFF_CHOICES; j++ ) { + if ( EntropyTableBits[1][j] < BestDcBits ) { + BestDcBits = EntropyTableBits[1][j]; + DcHuffChoice[1] = j; + } + } + + /* Add the DC huffman table choice to the bitstream */ + oggpackB_write( opb, DcHuffChoice[1], DC_HUFF_CHOICE_BITS ); + + /* Encode the token list */ + for ( i = 0; i < cpi->OptimisedTokenCount; i++ ) { + + /* Get the token and extra bits */ + Token = (ogg_uint32_t)cpi->OptimisedTokenList[i]; + ExtraBitsToken = (ogg_uint32_t)cpi->OptimisedTokenListEb[i]; + + /* Select the huffman table */ + if ( cpi->OptimisedTokenListPl[i] == 0) + HuffIndex = (ogg_uint32_t)DC_HUFF_OFFSET + (ogg_uint32_t)DcHuffChoice[0]; + else + HuffIndex = (ogg_uint32_t)DC_HUFF_OFFSET + (ogg_uint32_t)DcHuffChoice[1]; + + /* Add the bits to the encode holding buffer. */ + cpi->FrameBitCount += cpi->pb.HuffCodeLengthArray_VP3x[HuffIndex][Token]; + oggpackB_write( opb, cpi->pb.HuffCodeArray_VP3x[HuffIndex][Token], + (ogg_uint32_t)cpi-> + pb.HuffCodeLengthArray_VP3x[HuffIndex][Token] ); + + /* If the token is followed by an extra bits token then code it */ + if ( cpi->pb.ExtraBitLengths_VP3x[Token] > 0 ) { + /* Add the bits to the encode holding buffer. */ + cpi->FrameBitCount += cpi->pb.ExtraBitLengths_VP3x[Token]; + oggpackB_write( opb, ExtraBitsToken, + (ogg_uint32_t)cpi->pb.ExtraBitLengths_VP3x[Token] ); + } + + } + + /* Reset the count of second order optimised tokens */ + cpi->OptimisedTokenCount = 0; +} + +static void EncodeAcTokenList (CP_INSTANCE *cpi) { + ogg_int32_t i,j; + ogg_uint32_t Token; + ogg_uint32_t ExtraBitsToken; + ogg_uint32_t HuffIndex; + + ogg_uint32_t BestAcBits; + ogg_uint32_t AcHuffChoice[2]; + ogg_uint32_t EntropyTableBits[2][AC_HUFF_CHOICES]; + + oggpack_buffer *opb=cpi->oggbuffer; + + memset ( EntropyTableBits, 0, sizeof(ogg_uint32_t)*AC_HUFF_CHOICES*2 ); + + /* Analyse token list to see which is the best entropy table to use */ + for ( i = 0; i < cpi->OptimisedTokenCount; i++ ) { + /* Count number of bits for each table option */ + Token = (ogg_uint32_t)cpi->OptimisedTokenList[i]; + HuffIndex = cpi->OptimisedTokenListHi[i]; + for ( j = 0; j < AC_HUFF_CHOICES; j++ ) { + EntropyTableBits[cpi->OptimisedTokenListPl[i]][j] += + cpi->pb.HuffCodeLengthArray_VP3x[HuffIndex + j][Token]; + } + } + + /* Select the best set of AC tables for Y */ + BestAcBits = EntropyTableBits[0][0]; + AcHuffChoice[0] = 0; + for ( j = 1; j < AC_HUFF_CHOICES; j++ ) { + if ( EntropyTableBits[0][j] < BestAcBits ) { + BestAcBits = EntropyTableBits[0][j]; + AcHuffChoice[0] = j; + } + } + + /* Add the AC-Y huffman table choice to the bitstream */ + oggpackB_write( opb, AcHuffChoice[0], AC_HUFF_CHOICE_BITS ); + + /* Select the best set of AC tables for UV */ + BestAcBits = EntropyTableBits[1][0]; + AcHuffChoice[1] = 0; + for ( j = 1; j < AC_HUFF_CHOICES; j++ ) { + if ( EntropyTableBits[1][j] < BestAcBits ) { + BestAcBits = EntropyTableBits[1][j]; + AcHuffChoice[1] = j; + } + } + + /* Add the AC-UV huffman table choice to the bitstream */ + oggpackB_write( opb, AcHuffChoice[1], AC_HUFF_CHOICE_BITS ); + + /* Encode the token list */ + for ( i = 0; i < cpi->OptimisedTokenCount; i++ ) { + /* Get the token and extra bits */ + Token = (ogg_uint32_t)cpi->OptimisedTokenList[i]; + ExtraBitsToken = (ogg_uint32_t)cpi->OptimisedTokenListEb[i]; + + /* Select the huffman table */ + HuffIndex = (ogg_uint32_t)cpi->OptimisedTokenListHi[i] + + AcHuffChoice[cpi->OptimisedTokenListPl[i]]; + + /* Add the bits to the encode holding buffer. */ + cpi->FrameBitCount += cpi->pb.HuffCodeLengthArray_VP3x[HuffIndex][Token]; + oggpackB_write( opb, cpi->pb.HuffCodeArray_VP3x[HuffIndex][Token], + (ogg_uint32_t)cpi-> + pb.HuffCodeLengthArray_VP3x[HuffIndex][Token] ); + + /* If the token is followed by an extra bits token then code it */ + if ( cpi->pb.ExtraBitLengths_VP3x[Token] > 0 ) { + /* Add the bits to the encode holding buffer. */ + cpi->FrameBitCount += cpi->pb.ExtraBitLengths_VP3x[Token]; + oggpackB_write( opb, ExtraBitsToken, + (ogg_uint32_t)cpi->pb.ExtraBitLengths_VP3x[Token] ); + } + } + + /* Reset the count of second order optimised tokens */ + cpi->OptimisedTokenCount = 0; +} + +static void PackModes (CP_INSTANCE *cpi) { + ogg_uint32_t i,j; + unsigned char ModeIndex; + const unsigned char *SchemeList; + + unsigned char BestModeSchemes[MAX_MODES]; + ogg_int32_t ModeCount[MAX_MODES]; + ogg_int32_t TmpFreq = -1; + ogg_int32_t TmpIndex = -1; + + ogg_uint32_t BestScheme; + ogg_uint32_t BestSchemeScore; + ogg_uint32_t SchemeScore; + + oggpack_buffer *opb=cpi->oggbuffer; + + /* Build a frequency map for the modes in this frame */ + memset( ModeCount, 0, MAX_MODES*sizeof(ogg_int32_t) ); + for ( i = 0; i < cpi->ModeListCount; i++ ) + ModeCount[cpi->ModeList[i]] ++; + + /* Order the modes from most to least frequent. Store result as + scheme 0 */ + for ( j = 0; j < MAX_MODES; j++ ) { + TmpFreq = -1; /* need to re-initialize for each loop */ + /* Find the most frequent */ + for ( i = 0; i < MAX_MODES; i++ ) { + /* Is this the best scheme so far ??? */ + if ( ModeCount[i] > TmpFreq ) { + TmpFreq = ModeCount[i]; + TmpIndex = i; + } + } + /* I don't know if the above loop ever fails to match, but it's + better safe than sorry. Plus this takes care of gcc warning */ + if ( TmpIndex != -1 ) { + ModeCount[TmpIndex] = -1; + BestModeSchemes[TmpIndex] = (unsigned char)j; + } + } + + /* Default/ fallback scheme uses MODE_BITS bits per mode entry */ + BestScheme = (MODE_METHODS - 1); + BestSchemeScore = cpi->ModeListCount * 3; + /* Get a bit score for the available schemes. */ + for ( j = 0; j < (MODE_METHODS - 1); j++ ) { + + /* Reset the scheme score */ + if ( j == 0 ){ + /* Scheme 0 additional cost of sending frequency order */ + SchemeScore = 24; + SchemeList = BestModeSchemes; + } else { + SchemeScore = 0; + SchemeList = ModeSchemes[j-1]; + } + + /* Find the total bits to code using each avaialable scheme */ + for ( i = 0; i < cpi->ModeListCount; i++ ) + SchemeScore += ModeBitLengths[SchemeList[cpi->ModeList[i]]]; + + /* Is this the best scheme so far ??? */ + if ( SchemeScore < BestSchemeScore ) { + BestSchemeScore = SchemeScore; + BestScheme = j; + } + } + + /* Encode the best scheme. */ + oggpackB_write( opb, BestScheme, (ogg_uint32_t)MODE_METHOD_BITS ); + + /* If the chosen schems is scheme 0 send details of the mode + frequency order */ + if ( BestScheme == 0 ) { + for ( j = 0; j < MAX_MODES; j++ ){ + /* Note that the last two entries are implicit */ + oggpackB_write( opb, BestModeSchemes[j], (ogg_uint32_t)MODE_BITS ); + } + SchemeList = BestModeSchemes; + } + else { + SchemeList = ModeSchemes[BestScheme-1]; + } + + /* Are we using one of the alphabet based schemes or the fallback scheme */ + if ( BestScheme < (MODE_METHODS - 1)) { + /* Pack and encode the Mode list */ + for ( i = 0; i < cpi->ModeListCount; i++) { + /* Add the appropriate mode entropy token. */ + ModeIndex = SchemeList[cpi->ModeList[i]]; + oggpackB_write( opb, ModeBitPatterns[ModeIndex], + (ogg_uint32_t)ModeBitLengths[ModeIndex] ); + } + }else{ + /* Fall back to MODE_BITS per entry */ + for ( i = 0; i < cpi->ModeListCount; i++) + /* Add the appropriate mode entropy token. */ + oggpackB_write( opb, cpi->ModeList[i], MODE_BITS ); + } + +} + +static void PackMotionVectors (CP_INSTANCE *cpi) { + ogg_int32_t i; + ogg_uint32_t MethodBits[2] = {0,0}; + const ogg_uint32_t * MvBitsPtr; + const ogg_uint32_t * MvPatternPtr; + + oggpack_buffer *opb=cpi->oggbuffer; + + /* Choose the coding method */ + MvBitsPtr = &MvBits[MAX_MV_EXTENT]; + for ( i = 0; i < (ogg_int32_t)cpi->MvListCount; i++ ) { + MethodBits[0] += MvBitsPtr[cpi->MVList[i].x]; + MethodBits[0] += MvBitsPtr[cpi->MVList[i].y]; + MethodBits[1] += 12; /* Simple six bits per mv component fallback + mechanism */ + } + + /* Select entropy table */ + if ( MethodBits[0] < MethodBits[1] ) { + oggpackB_write( opb, 0, 1 ); + MvBitsPtr = &MvBits[MAX_MV_EXTENT]; + MvPatternPtr = &MvPattern[MAX_MV_EXTENT]; + }else{ + oggpackB_write( opb, 1, 1 ); + MvBitsPtr = &MvBits2[MAX_MV_EXTENT]; + MvPatternPtr = &MvPattern2[MAX_MV_EXTENT]; + } + + /* Pack and encode the motion vectors */ + for ( i = 0; i < (ogg_int32_t)cpi->MvListCount; i++ ) { + oggpackB_write( opb, MvPatternPtr[cpi->MVList[i].x], + (ogg_uint32_t)MvBitsPtr[cpi->MVList[i].x] ); + oggpackB_write( opb, MvPatternPtr[cpi->MVList[i].y], + (ogg_uint32_t)MvBitsPtr[cpi->MVList[i].y] ); + } + +} + +static void PackEOBRun( CP_INSTANCE *cpi) { + if(cpi->RunLength == 0) + return; + + /* Note the appropriate EOB or EOB run token and any extra bits in + the optimised token list. Use the huffman index assosciated with + the first token in the run */ + + /* Mark out which plane the block belonged to */ + cpi->OptimisedTokenListPl[cpi->OptimisedTokenCount] = + (unsigned char)cpi->RunPlaneIndex; + + /* Note the huffman index to be used */ + cpi->OptimisedTokenListHi[cpi->OptimisedTokenCount] = + (unsigned char)cpi->RunHuffIndex; + + if ( cpi->RunLength <= 3 ) { + if ( cpi->RunLength == 1 ) { + cpi->OptimisedTokenList[cpi->OptimisedTokenCount] = DCT_EOB_TOKEN; + } else if ( cpi->RunLength == 2 ) { + cpi->OptimisedTokenList[cpi->OptimisedTokenCount] = DCT_EOB_PAIR_TOKEN; + } else { + cpi->OptimisedTokenList[cpi->OptimisedTokenCount] = DCT_EOB_TRIPLE_TOKEN; + } + + cpi->RunLength = 0; + + } else { + + /* Choose a token appropriate to the run length. */ + if ( cpi->RunLength < 8 ) { + cpi->OptimisedTokenList[cpi->OptimisedTokenCount] = + DCT_REPEAT_RUN_TOKEN; + cpi->OptimisedTokenListEb[cpi->OptimisedTokenCount] = + cpi->RunLength - 4; + cpi->RunLength = 0; + } else if ( cpi->RunLength < 16 ) { + cpi->OptimisedTokenList[cpi->OptimisedTokenCount] = + DCT_REPEAT_RUN2_TOKEN; + cpi->OptimisedTokenListEb[cpi->OptimisedTokenCount] = + cpi->RunLength - 8; + cpi->RunLength = 0; + } else if ( cpi->RunLength < 32 ) { + cpi->OptimisedTokenList[cpi->OptimisedTokenCount] = + DCT_REPEAT_RUN3_TOKEN; + cpi->OptimisedTokenListEb[cpi->OptimisedTokenCount] = + cpi->RunLength - 16; + cpi->RunLength = 0; + } else if ( cpi->RunLength < 4096) { + cpi->OptimisedTokenList[cpi->OptimisedTokenCount] = + DCT_REPEAT_RUN4_TOKEN; + cpi->OptimisedTokenListEb[cpi->OptimisedTokenCount] = + cpi->RunLength; + cpi->RunLength = 0; + } + + } + + cpi->OptimisedTokenCount++; + /* Reset run EOB length */ + cpi->RunLength = 0; +} + +static void PackToken ( CP_INSTANCE *cpi, ogg_int32_t FragmentNumber, + ogg_uint32_t HuffIndex ) { + ogg_uint32_t Token = + cpi->pb.TokenList[FragmentNumber][cpi->FragTokens[FragmentNumber]]; + ogg_uint32_t ExtraBitsToken = + cpi->pb.TokenList[FragmentNumber][cpi->FragTokens[FragmentNumber] + 1]; + ogg_uint32_t OneOrTwo; + ogg_uint32_t OneOrZero; + + /* Update the record of what coefficient we have got up to for this + block and unpack the encoded token back into the quantised data + array. */ + if ( Token == DCT_EOB_TOKEN ) + cpi->pb.FragCoeffs[FragmentNumber] = BLOCK_SIZE; + else + ExpandToken( cpi->pb.QFragData[FragmentNumber], + &cpi->pb.FragCoeffs[FragmentNumber], + Token, ExtraBitsToken ); + + /* Update record of tokens coded and where we are in this fragment. */ + /* Is there an extra bits token */ + OneOrTwo= 1 + ( cpi->pb.ExtraBitLengths_VP3x[Token] > 0 ); + /* Advance to the next real token. */ + cpi->FragTokens[FragmentNumber] += (unsigned char)OneOrTwo; + + /* Update the counts of tokens coded */ + cpi->TokensCoded += OneOrTwo; + cpi->TokensToBeCoded -= OneOrTwo; + + OneOrZero = ( FragmentNumber < (ogg_int32_t)cpi->pb.YPlaneFragments ); + + if ( Token == DCT_EOB_TOKEN ) { + if ( cpi->RunLength == 0 ) { + cpi->RunHuffIndex = HuffIndex; + cpi->RunPlaneIndex = 1 - OneOrZero; + } + cpi->RunLength++; + + /* we have exceeded our longest run length xmit an eob run token; */ + if ( cpi->RunLength == 4095 ) PackEOBRun(cpi); + + }else{ + + /* If we have an EOB run then code it up first */ + if ( cpi->RunLength > 0 ) PackEOBRun( cpi); + + /* Mark out which plane the block belonged to */ + cpi->OptimisedTokenListPl[cpi->OptimisedTokenCount] = + (unsigned char)(1 - OneOrZero); + + /* Note the token, extra bits and hufman table in the optimised + token list */ + cpi->OptimisedTokenList[cpi->OptimisedTokenCount] = + (unsigned char)Token; + cpi->OptimisedTokenListEb[cpi->OptimisedTokenCount] = + ExtraBitsToken; + cpi->OptimisedTokenListHi[cpi->OptimisedTokenCount] = + (unsigned char)HuffIndex; + + cpi->OptimisedTokenCount++; + } +} + +static ogg_uint32_t GetBlockReconErrorSlow( CP_INSTANCE *cpi, + ogg_int32_t BlockIndex ) { + ogg_uint32_t ErrorVal; + + unsigned char * SrcDataPtr = + &cpi->ConvDestBuffer[cpi->pb.pixel_index_table[BlockIndex]]; + unsigned char * RecDataPtr = + &cpi->pb.LastFrameRecon[cpi->pb.recon_pixel_index_table[BlockIndex]]; + ogg_int32_t SrcStride; + ogg_int32_t RecStride; + + /* Is the block a Y block or a UV block. */ + if ( BlockIndex < (ogg_int32_t)cpi->pb.YPlaneFragments ) { + SrcStride = cpi->pb.info.width; + RecStride = cpi->pb.YStride; + }else{ + SrcStride = cpi->pb.info.width >> 1; + RecStride = cpi->pb.UVStride; + } + + ErrorVal = dsp_sad8x8 (cpi->dsp, SrcDataPtr, SrcStride, RecDataPtr, RecStride); + + return ErrorVal; +} + +static void PackCodedVideo (CP_INSTANCE *cpi) { + ogg_int32_t i; + ogg_int32_t EncodedCoeffs = 1; + ogg_int32_t FragIndex; + ogg_uint32_t HuffIndex; /* Index to group of tables used to code a token */ + + /* Reset the count of second order optimised tokens */ + cpi->OptimisedTokenCount = 0; + + cpi->TokensToBeCoded = cpi->TotTokenCount; + cpi->TokensCoded = 0; + + /* Calculate the bit rate at which this frame should be capped. */ + cpi->MaxBitTarget = (ogg_uint32_t)((double)(cpi->ThisFrameTargetBytes * 8) * + cpi->BitRateCapFactor); + + /* Blank the various fragment data structures before we start. */ + memset(cpi->pb.FragCoeffs, 0, cpi->pb.UnitFragments); + memset(cpi->FragTokens, 0, cpi->pb.UnitFragments); + + /* Clear down the QFragData structure for all coded blocks. */ + ClearDownQFragData(&cpi->pb); + + /* The tree is not needed (implicit) for key frames */ + if ( cpi->pb.FrameType != KEY_FRAME ){ + /* Pack the quad tree fragment mapping. */ + PackAndWriteDFArray( cpi ); + } + + /* Note the number of bits used to code the tree itself. */ + cpi->FrameBitCount = oggpackB_bytes(cpi->oggbuffer) << 3; + + /* Mode and MV data not needed for key frames. */ + if ( cpi->pb.FrameType != KEY_FRAME ){ + /* Pack and code the mode list. */ + PackModes(cpi); + /* Pack the motion vectors */ + PackMotionVectors (cpi); + } + + cpi->FrameBitCount = oggpackB_bytes(cpi->oggbuffer) << 3; + + /* Optimise the DC tokens */ + for ( i = 0; i < cpi->pb.CodedBlockIndex; i++ ) { + /* Get the linear index for the current fragment. */ + FragIndex = cpi->pb.CodedBlockList[i]; + + cpi->pb.FragCoefEOB[FragIndex]=(unsigned char)EncodedCoeffs; + PackToken(cpi, FragIndex, DC_HUFF_OFFSET ); + + } + + /* Pack any outstanding EOB tokens */ + PackEOBRun(cpi); + + /* Now output the optimised DC token list using the appropriate + entropy tables. */ + EncodeDcTokenList(cpi); + + /* Work out the number of DC bits coded */ + + /* Optimise the AC tokens */ + while ( EncodedCoeffs < 64 ) { + /* Huffman table adjustment based upon coefficient number. */ + if ( EncodedCoeffs <= AC_TABLE_2_THRESH ) + HuffIndex = AC_HUFF_OFFSET; + else if ( EncodedCoeffs <= AC_TABLE_3_THRESH ) + HuffIndex = AC_HUFF_OFFSET + AC_HUFF_CHOICES; + else if ( EncodedCoeffs <= AC_TABLE_4_THRESH ) + HuffIndex = AC_HUFF_OFFSET + (AC_HUFF_CHOICES * 2); + else + HuffIndex = AC_HUFF_OFFSET + (AC_HUFF_CHOICES * 3); + + /* Repeatedly scan through the list of blocks. */ + for ( i = 0; i < cpi->pb.CodedBlockIndex; i++ ) { + /* Get the linear index for the current fragment. */ + FragIndex = cpi->pb.CodedBlockList[i]; + + /* Should we code a token for this block on this pass. */ + if ( cpi->FragTokens[FragIndex] < cpi->FragTokenCounts[FragIndex] + && cpi->pb.FragCoeffs[FragIndex] <= EncodedCoeffs ) { + /* Bit pack and a token for this block */ + cpi->pb.FragCoefEOB[FragIndex]=(unsigned char)EncodedCoeffs; + PackToken( cpi, FragIndex, HuffIndex ); + } + } + + EncodedCoeffs ++; + } + + /* Pack any outstanding EOB tokens */ + PackEOBRun(cpi); + + /* Now output the optimised AC token list using the appropriate + entropy tables. */ + EncodeAcTokenList(cpi); + +} + +static ogg_uint32_t QuadCodeDisplayFragments (CP_INSTANCE *cpi) { + ogg_int32_t i,j; + ogg_uint32_t coded_pixels=0; + int QIndex; + int k,m,n; + + /* predictor multiplier up-left, up, up-right,left, shift + Entries are packed in the order L, UL, U, UR, with missing entries + moved to the end (before the shift parameters). */ + static const ogg_int16_t pc[16][6]={ + {0,0,0,0,0,0}, + {1,0,0,0,0,0}, /* PL */ + {1,0,0,0,0,0}, /* PUL */ + {1,0,0,0,0,0}, /* PUL|PL */ + {1,0,0,0,0,0}, /* PU */ + {1,1,0,0,1,1}, /* PU|PL */ + {0,1,0,0,0,0}, /* PU|PUL */ + {29,-26,29,0,5,31}, /* PU|PUL|PL */ + {1,0,0,0,0,0}, /* PUR */ + {75,53,0,0,7,127}, /* PUR|PL */ + {1,1,0,0,1,1}, /* PUR|PUL */ + {75,0,53,0,7,127}, /* PUR|PUL|PL */ + {1,0,0,0,0,0}, /* PUR|PU */ + {75,0,53,0,7,127}, /* PUR|PU|PL */ + {3,10,3,0,4,15}, /* PUR|PU|PUL */ + {29,-26,29,0,5,31} /* PUR|PU|PUL|PL */ + }; + + /* boundary case bit masks. */ + static const int bc_mask[8]={ + /* normal case no boundary condition */ + PUR|PU|PUL|PL, + /* left column */ + PUR|PU, + /* top row */ + PL, + /* top row, left column */ + 0, + /* right column */ + PU|PUL|PL, + /* right and left column */ + PU, + /* top row, right column */ + PL, + /* top row, right and left column */ + 0 + }; + + /* value left value up-left, value up, value up-right, missing + values skipped. */ + int v[4]; + + /* fragment number left, up-left, up, up-right */ + int fn[4]; + + /* predictor count. */ + int pcount; + + /*which predictor constants to use */ + ogg_int16_t wpc; + + /* last used inter predictor (Raster Order) */ + ogg_int16_t Last[3]; /* last value used for given frame */ + + int FragsAcross=cpi->pb.HFragments; + int FragsDown = cpi->pb.VFragments; + int FromFragment,ToFragment; + ogg_int32_t FragIndex; + int WhichFrame; + int WhichCase; + + static const ogg_int16_t Mode2Frame[] = { + 1, /* CODE_INTER_NO_MV 0 => Encoded diff from same MB last frame */ + 0, /* CODE_INTRA 1 => DCT Encoded Block */ + 1, /* CODE_INTER_PLUS_MV 2 => Encoded diff from included MV MB last frame */ + 1, /* CODE_INTER_LAST_MV 3 => Encoded diff from MRU MV MB last frame */ + 1, /* CODE_INTER_PRIOR_MV 4 => Encoded diff from included 4 separate MV blocks */ + 2, /* CODE_USING_GOLDEN 5 => Encoded diff from same MB golden frame */ + 2, /* CODE_GOLDEN_MV 6 => Encoded diff from included MV MB golden frame */ + 1 /* CODE_INTER_FOUR_MV 7 => Encoded diff from included 4 separate MV blocks */ + }; + + ogg_int16_t PredictedDC; + + /* Initialise the coded block indices variables. These allow + subsequent linear access to the quad tree ordered list of coded + blocks */ + cpi->pb.CodedBlockIndex = 0; + + /* Set the inter/intra descision control variables. */ + QIndex = Q_TABLE_SIZE - 1; + while ( QIndex >= 0 ) { + if ( (QIndex == 0) || + ( cpi->pb.QThreshTable[QIndex] >= cpi->pb.ThisFrameQualityValue) ) + break; + QIndex --; + } + + + /* Encode and tokenise the Y, U and V components */ + coded_pixels = QuadCodeComponent(cpi, 0, cpi->pb.YSBRows, cpi->pb.YSBCols, + cpi->pb.info.width ); + coded_pixels += QuadCodeComponent(cpi, cpi->pb.YSuperBlocks, + cpi->pb.UVSBRows, + cpi->pb.UVSBCols, + cpi->pb.info.width>>1 ); + coded_pixels += QuadCodeComponent(cpi, + cpi->pb.YSuperBlocks+cpi->pb.UVSuperBlocks, + cpi->pb.UVSBRows, cpi->pb.UVSBCols, + cpi->pb.info.width>>1 ); + + /* for y,u,v */ + for ( j = 0; j < 3 ; j++) { + /* pick which fragments based on Y, U, V */ + switch(j){ + case 0: /* y */ + FromFragment = 0; + ToFragment = cpi->pb.YPlaneFragments; + FragsAcross = cpi->pb.HFragments; + FragsDown = cpi->pb.VFragments; + break; + case 1: /* u */ + FromFragment = cpi->pb.YPlaneFragments; + ToFragment = cpi->pb.YPlaneFragments + cpi->pb.UVPlaneFragments ; + FragsAcross = cpi->pb.HFragments >> 1; + FragsDown = cpi->pb.VFragments >> 1; + break; + /*case 2: v */ + default: + FromFragment = cpi->pb.YPlaneFragments + cpi->pb.UVPlaneFragments; + ToFragment = cpi->pb.YPlaneFragments + (2 * cpi->pb.UVPlaneFragments) ; + FragsAcross = cpi->pb.HFragments >> 1; + FragsDown = cpi->pb.VFragments >> 1; + break; + } + + /* initialize our array of last used DC Components */ + for(k=0;k<3;k++)Last[k]=0; + i=FromFragment; + + /* do prediction on all of Y, U or V */ + for ( m = 0 ; m < FragsDown ; m++) { + for ( n = 0 ; n < FragsAcross ; n++, i++) { + cpi->OriginalDC[i] = cpi->pb.QFragData[i][0]; + + /* only do 2 prediction if fragment coded and on non intra or + if all fragments are intra */ + if( cpi->pb.display_fragments[i] || + (cpi->pb.FrameType == KEY_FRAME) ) { + /* Type of Fragment */ + + WhichFrame = Mode2Frame[cpi->pb.FragCodingMethod[i]]; + + /* Check Borderline Cases */ + WhichCase = (n==0) + ((m==0) << 1) + ((n+1 == FragsAcross) << 2); + + fn[0]=i-1; + fn[1]=i-FragsAcross-1; + fn[2]=i-FragsAcross; + fn[3]=i-FragsAcross+1; + + /* fragment valid for prediction use if coded and it comes + from same frame as the one we are predicting */ + for(k=pcount=wpc=0; k<4; k++) { + int pflag; + pflag=1<pb.display_fragments[fn[k]] && + (Mode2Frame[cpi->pb.FragCodingMethod[fn[k]]] == WhichFrame)){ + v[pcount]=cpi->OriginalDC[fn[k]]; + wpc|=pflag; + pcount++; + } + } + + if(wpc==0) { + + /* fall back to the last coded fragment */ + cpi->pb.QFragData[i][0] -= Last[WhichFrame]; + + } else { + + /* don't do divide if divisor is 1 or 0 */ + PredictedDC = pc[wpc][0]*v[0]; + for(k=1; k>= pc[wpc][4]; + + } + + /* check for outranging on the two predictors that can outrange */ + if((wpc&(PU|PUL|PL)) == (PU|PUL|PL)){ + if( abs(PredictedDC - v[2]) > 128) { + PredictedDC = v[2]; + } else if( abs(PredictedDC - v[0]) > 128) { + PredictedDC = v[0]; + } else if( abs(PredictedDC - v[1]) > 128) { + PredictedDC = v[1]; + } + } + + cpi->pb.QFragData[i][0] -= PredictedDC; + } + + /* Save the last fragment coded for whatever frame we are + predicting from */ + + Last[WhichFrame] = cpi->OriginalDC[i]; + + } + } + } + } + + /* Pack DC tokens and adjust the ones we couldn't predict 2d */ + for ( i = 0; i < cpi->pb.CodedBlockIndex; i++ ) { + /* Get the linear index for the current coded fragment. */ + FragIndex = cpi->pb.CodedBlockList[i]; + coded_pixels += DPCMTokenizeBlock ( cpi, FragIndex); + + } + + /* Bit pack the video data data */ + PackCodedVideo(cpi); + + /* End the bit packing run. */ + /* EndAddBitsToBuffer(cpi); */ + + /* Reconstruct the reference frames */ + ReconRefFrames(&cpi->pb); + + UpdateFragQIndex(&cpi->pb); + + /* Measure the inter reconstruction error for all the blocks that + were coded */ + /* for use as part of the recovery monitoring process in subsequent frames. */ + for ( i = 0; i < cpi->pb.CodedBlockIndex; i++ ) { + cpi->LastCodedErrorScore[ cpi->pb.CodedBlockList[i] ] = + GetBlockReconErrorSlow( cpi, cpi->pb.CodedBlockList[i] ); + + } + + /* Return total number of coded pixels */ + return coded_pixels; +} + +ogg_uint32_t EncodeData(CP_INSTANCE *cpi){ + ogg_uint32_t coded_pixels = 0; + + /* Zero the count of tokens so far this frame. */ + cpi->TotTokenCount = 0; + + /* Zero the mode and MV list indices. */ + cpi->ModeListCount = 0; + + /* Zero Decoder EOB run count */ + cpi->pb.EOB_Run = 0; + + dsp_save_fpu (cpi->dsp); + + /* Encode any fragments coded using DCT. */ + coded_pixels += QuadCodeDisplayFragments (cpi); + + dsp_restore_fpu (cpi->dsp); + + return coded_pixels; + +} + +ogg_uint32_t PickIntra( CP_INSTANCE *cpi, + ogg_uint32_t SBRows, + ogg_uint32_t SBCols){ + + ogg_int32_t FragIndex; /* Fragment number */ + ogg_uint32_t MB, B; /* Macro-Block, Block indices */ + ogg_uint32_t SBrow; /* Super-Block row number */ + ogg_uint32_t SBcol; /* Super-Block row number */ + ogg_uint32_t SB=0; /* Super-Block index, initialised to first of + this component */ + ogg_uint32_t UVRow; + ogg_uint32_t UVColumn; + ogg_uint32_t UVFragOffset; + + /* decide what block type and motion vectors to use on all of the frames */ + for ( SBrow=0; SBrowpb.BlockMap,SB,MB) >= 0 ) { + + cpi->MBCodingMode = CODE_INTRA; + + /* Now actually code the blocks. */ + for ( B=0; B<4; B++ ) { + FragIndex = QuadMapToIndex1( cpi->pb.BlockMap, SB, MB, B ); + cpi->pb.FragCodingMethod[FragIndex] = cpi->MBCodingMode; + } + + /* Matching fragments in the U and V planes */ + UVRow = (FragIndex / (cpi->pb.HFragments * 2)); + UVColumn = (FragIndex % cpi->pb.HFragments) / 2; + UVFragOffset = (UVRow * (cpi->pb.HFragments / 2)) + UVColumn; + + cpi->pb.FragCodingMethod[cpi->pb.YPlaneFragments + UVFragOffset] = + cpi->MBCodingMode; + cpi->pb.FragCodingMethod[cpi->pb.YPlaneFragments + + cpi->pb.UVPlaneFragments + UVFragOffset] = + cpi->MBCodingMode; + } + } + + /* Next Super-Block */ + SB++; + } + } + return 0; +} + +static void AddMotionVector(CP_INSTANCE *cpi, + MOTION_VECTOR *ThisMotionVector) { + cpi->MVList[cpi->MvListCount].x = ThisMotionVector->x; + cpi->MVList[cpi->MvListCount].y = ThisMotionVector->y; + cpi->MvListCount++; +} + +static void SetFragMotionVectorAndMode(CP_INSTANCE *cpi, + ogg_int32_t FragIndex, + MOTION_VECTOR *ThisMotionVector){ + /* Note the coding mode and vector for each block */ + cpi->pb.FragMVect[FragIndex].x = ThisMotionVector->x; + cpi->pb.FragMVect[FragIndex].y = ThisMotionVector->y; + cpi->pb.FragCodingMethod[FragIndex] = cpi->MBCodingMode; +} + +static void SetMBMotionVectorsAndMode(CP_INSTANCE *cpi, + ogg_int32_t YFragIndex, + ogg_int32_t UFragIndex, + ogg_int32_t VFragIndex, + MOTION_VECTOR *ThisMotionVector){ + SetFragMotionVectorAndMode(cpi, YFragIndex, ThisMotionVector); + SetFragMotionVectorAndMode(cpi, YFragIndex + 1, ThisMotionVector); + SetFragMotionVectorAndMode(cpi, YFragIndex + cpi->pb.HFragments, + ThisMotionVector); + SetFragMotionVectorAndMode(cpi, YFragIndex + cpi->pb.HFragments + 1, + ThisMotionVector); + SetFragMotionVectorAndMode(cpi, UFragIndex, ThisMotionVector); + SetFragMotionVectorAndMode(cpi, VFragIndex, ThisMotionVector); +} + +ogg_uint32_t PickModes(CP_INSTANCE *cpi, + ogg_uint32_t SBRows, ogg_uint32_t SBCols, + ogg_uint32_t PixelsPerLine, + ogg_uint32_t *InterError, ogg_uint32_t *IntraError) { + ogg_int32_t YFragIndex; + ogg_int32_t UFragIndex; + ogg_int32_t VFragIndex; + ogg_uint32_t MB, B; /* Macro-Block, Block indices */ + ogg_uint32_t SBrow; /* Super-Block row number */ + ogg_uint32_t SBcol; /* Super-Block row number */ + ogg_uint32_t SB=0; /* Super-Block index, initialised to first + of this component */ + + ogg_uint32_t MBIntraError; /* Intra error for macro block */ + ogg_uint32_t MBGFError; /* Golden frame macro block error */ + ogg_uint32_t MBGF_MVError; /* Golden frame plus MV error */ + ogg_uint32_t LastMBGF_MVError; /* Golden frame error with + last used GF motion + vector. */ + ogg_uint32_t MBInterError; /* Inter no MV macro block error */ + ogg_uint32_t MBLastInterError; /* Inter with last used MV */ + ogg_uint32_t MBPriorLastInterError; /* Inter with prior last MV */ + ogg_uint32_t MBInterMVError; /* Inter MV macro block error */ + ogg_uint32_t MBInterMVExError; /* Inter MV (exhaustive + search) macro block error */ + ogg_uint32_t MBInterFOURMVError; /* Inter MV error when using 4 + motion vectors per macro + block */ + ogg_uint32_t BestError; /* Best error so far. */ + + MOTION_VECTOR FourMVect[6]; /* storage for last used vectors (one + entry for each block in MB) */ + MOTION_VECTOR LastInterMVect; /* storage for last used Inter frame + MB motion vector */ + MOTION_VECTOR PriorLastInterMVect; /* storage for prior last used + Inter frame MB motion vector */ + MOTION_VECTOR TmpMVect; /* Temporary MV storage */ + MOTION_VECTOR LastGFMVect; /* storage for last used Golden + Frame MB motion vector */ + MOTION_VECTOR InterMVect; /* storage for motion vector */ + MOTION_VECTOR InterMVectEx; /* storage for motion vector result + from exhaustive search */ + MOTION_VECTOR GFMVect; /* storage for motion vector */ + MOTION_VECTOR ZeroVect; + + ogg_uint32_t UVRow; + ogg_uint32_t UVColumn; + ogg_uint32_t UVFragOffset; + + int MBCodedFlag; + unsigned char QIndex; + + /* initialize error scores */ + *InterError = 0; + *IntraError = 0; + + /* clear down the default motion vector. */ + cpi->MvListCount = 0; + FourMVect[0].x = 0; + FourMVect[0].y = 0; + FourMVect[1].x = 0; + FourMVect[1].y = 0; + FourMVect[2].x = 0; + FourMVect[2].y = 0; + FourMVect[3].x = 0; + FourMVect[3].y = 0; + FourMVect[4].x = 0; + FourMVect[4].y = 0; + FourMVect[5].x = 0; + FourMVect[5].y = 0; + LastInterMVect.x = 0; + LastInterMVect.y = 0; + PriorLastInterMVect.x = 0; + PriorLastInterMVect.y = 0; + LastGFMVect.x = 0; + LastGFMVect.y = 0; + InterMVect.x = 0; + InterMVect.y = 0; + GFMVect.x = 0; + GFMVect.y = 0; + + ZeroVect.x = 0; + ZeroVect.y = 0; + + QIndex = (unsigned char)cpi->pb.FrameQIndex; + + + /* change the quatization matrix to the one at best Q to compute the + new error score */ + cpi->MinImprovementForNewMV = (MvThreshTable[QIndex] << 12); + cpi->InterTripOutThresh = (5000<<12); + cpi->MVChangeFactor = MVChangeFactorTable[QIndex]; /* 0.9 */ + + if ( cpi->pb.info.quick_p ) { + cpi->ExhaustiveSearchThresh = (1000<<12); + cpi->FourMVThreshold = (2500<<12); + } else { + cpi->ExhaustiveSearchThresh = (250<<12); + cpi->FourMVThreshold = (500<<12); + } + cpi->MinImprovementForFourMV = cpi->MinImprovementForNewMV * 4; + + if(cpi->MinImprovementForFourMV < (40<<12)) + cpi->MinImprovementForFourMV = (40<<12); + + cpi->FourMvChangeFactor = 8; /* cpi->MVChangeFactor - 0.05; */ + + /* decide what block type and motion vectors to use on all of the frames */ + for ( SBrow=0; SBrowpb.BlockMap,SB,MB) < 0 ) continue; + + /* Is the current macro block coded (in part or in whole) */ + MBCodedFlag = 0; + for ( B=0; B<4; B++ ) { + YFragIndex = QuadMapToIndex1( cpi->pb.BlockMap, SB, MB, B ); + + /* Does Block lie in frame: */ + if ( YFragIndex >= 0 ) { + /* In Frame: Is it coded: */ + if ( cpi->pb.display_fragments[YFragIndex] ) { + MBCodedFlag = 1; + break; + } + } else + MBCodedFlag = 0; + } + + /* This one isn't coded go to the next one */ + if(!MBCodedFlag) continue; + + /* Calculate U and V FragIndex from YFragIndex */ + YFragIndex = QuadMapToMBTopLeft(cpi->pb.BlockMap, SB,MB); + UVRow = (YFragIndex / (cpi->pb.HFragments * 2)); + UVColumn = (YFragIndex % cpi->pb.HFragments) / 2; + UVFragOffset = (UVRow * (cpi->pb.HFragments / 2)) + UVColumn; + UFragIndex = cpi->pb.YPlaneFragments + UVFragOffset; + VFragIndex = cpi->pb.YPlaneFragments + cpi->pb.UVPlaneFragments + + UVFragOffset; + + + /************************************************************** + Find the block choice with the lowest error + + NOTE THAT if U or V is coded but no Y from a macro block then + the mode will be CODE_INTER_NO_MV as this is the default + state to which the mode data structure is initialised in + encoder and decoder at the start of each frame. */ + + BestError = HUGE_ERROR; + + + /* Look at the intra coding error. */ + MBIntraError = GetMBIntraError( cpi, YFragIndex, PixelsPerLine ); + BestError = (BestError > MBIntraError) ? MBIntraError : BestError; + + /* Get the golden frame error */ + MBGFError = GetMBInterError( cpi, cpi->ConvDestBuffer, + cpi->pb.GoldenFrame, YFragIndex, + 0, 0, PixelsPerLine ); + BestError = (BestError > MBGFError) ? MBGFError : BestError; + + /* Calculate the 0,0 case. */ + MBInterError = GetMBInterError( cpi, cpi->ConvDestBuffer, + cpi->pb.LastFrameRecon, + YFragIndex, 0, 0, PixelsPerLine ); + BestError = (BestError > MBInterError) ? MBInterError : BestError; + + /* Measure error for last MV */ + MBLastInterError = GetMBInterError( cpi, cpi->ConvDestBuffer, + cpi->pb.LastFrameRecon, + YFragIndex, LastInterMVect.x, + LastInterMVect.y, PixelsPerLine ); + BestError = (BestError > MBLastInterError) ? + MBLastInterError : BestError; + + /* Measure error for prior last MV */ + MBPriorLastInterError = GetMBInterError( cpi, cpi->ConvDestBuffer, + cpi->pb.LastFrameRecon, + YFragIndex, + PriorLastInterMVect.x, + PriorLastInterMVect.y, + PixelsPerLine ); + BestError = (BestError > MBPriorLastInterError) ? + MBPriorLastInterError : BestError; + + /* Temporarily force usage of no motionvector blocks */ + MBInterMVError = HUGE_ERROR; + InterMVect.x = 0; /* Set 0,0 motion vector */ + InterMVect.y = 0; + + /* If the best error is above the required threshold search + for a new inter MV */ + if ( BestError > cpi->MinImprovementForNewMV && cpi->MotionCompensation) { + /* Use a mix of heirachical and exhaustive searches for + quick mode. */ + if ( cpi->pb.info.quick_p ) { + MBInterMVError = GetMBMVInterError( cpi, cpi->pb.LastFrameRecon, + YFragIndex, PixelsPerLine, + cpi->MVPixelOffsetY, + &InterMVect ); + + /* If we still do not have a good match try an exhaustive + MBMV search */ + if ( (MBInterMVError > cpi->ExhaustiveSearchThresh) && + (BestError > cpi->ExhaustiveSearchThresh) ) { + + MBInterMVExError = + GetMBMVExhaustiveSearch( cpi, cpi->pb.LastFrameRecon, + YFragIndex, PixelsPerLine, + &InterMVectEx ); + + /* Is the Variance measure for the EX search + better... If so then use it. */ + if ( MBInterMVExError < MBInterMVError ) { + MBInterMVError = MBInterMVExError; + InterMVect.x = InterMVectEx.x; + InterMVect.y = InterMVectEx.y; + } + } + }else{ + /* Use an exhaustive search */ + MBInterMVError = + GetMBMVExhaustiveSearch( cpi, cpi->pb.LastFrameRecon, + YFragIndex, PixelsPerLine, + &InterMVect ); + } + + + /* Is the improvement, if any, good enough to justify a new MV */ + if ( (16 * MBInterMVError < (BestError * cpi->MVChangeFactor)) && + ((MBInterMVError + cpi->MinImprovementForNewMV) < BestError) ){ + BestError = MBInterMVError; + } + + } + + /* If the best error is still above the required threshold + search for a golden frame MV */ + MBGF_MVError = HUGE_ERROR; + GFMVect.x = 0; /* Set 0,0 motion vector */ + GFMVect.y = 0; + if ( BestError > cpi->MinImprovementForNewMV && cpi->MotionCompensation) { + /* Do an MV search in the golden reference frame */ + MBGF_MVError = GetMBMVInterError( cpi, cpi->pb.GoldenFrame, + YFragIndex, PixelsPerLine, + cpi->MVPixelOffsetY, &GFMVect ); + + /* Measure error for last GFMV */ + LastMBGF_MVError = GetMBInterError( cpi, cpi->ConvDestBuffer, + cpi->pb.GoldenFrame, + YFragIndex, LastGFMVect.x, + LastGFMVect.y, PixelsPerLine ); + + /* Check against last GF motion vector and reset if the + search has thrown a worse result. */ + if ( LastMBGF_MVError < MBGF_MVError ) { + GFMVect.x = LastGFMVect.x; + GFMVect.y = LastGFMVect.y; + MBGF_MVError = LastMBGF_MVError; + }else{ + LastGFMVect.x = GFMVect.x; + LastGFMVect.y = GFMVect.y; + } + + /* Is the improvement, if any, good enough to justify a new MV */ + if ( (16 * MBGF_MVError < (BestError * cpi->MVChangeFactor)) && + ((MBGF_MVError + cpi->MinImprovementForNewMV) < BestError) ) { + BestError = MBGF_MVError; + } + } + + /* Finally... If the best error is still to high then consider + the 4MV mode */ + MBInterFOURMVError = HUGE_ERROR; + if ( BestError > cpi->FourMVThreshold && cpi->MotionCompensation) { + /* Get the 4MV error. */ + MBInterFOURMVError = + GetFOURMVExhaustiveSearch( cpi, cpi->pb.LastFrameRecon, + YFragIndex, PixelsPerLine, FourMVect ); + + /* If the improvement is great enough then use the four MV mode */ + if ( ((MBInterFOURMVError + cpi->MinImprovementForFourMV) < + BestError) && (16 * MBInterFOURMVError < + (BestError * cpi->FourMvChangeFactor))) { + BestError = MBInterFOURMVError; + } + } + + /******************************************************** + end finding the best error + ******************************************************* + + Figure out what to do with the block we chose + + Over-ride and force intra if error high and Intra error similar + Now choose a mode based on lowest error (with bias towards no MV) */ + + if ( (BestError > cpi->InterTripOutThresh) && + (10 * BestError > MBIntraError * 7 ) ) { + cpi->MBCodingMode = CODE_INTRA; + SetMBMotionVectorsAndMode(cpi,YFragIndex,UFragIndex, + VFragIndex,&ZeroVect); + } else if ( BestError == MBInterError ) { + cpi->MBCodingMode = CODE_INTER_NO_MV; + SetMBMotionVectorsAndMode(cpi,YFragIndex,UFragIndex, + VFragIndex,&ZeroVect); + } else if ( BestError == MBGFError ) { + cpi->MBCodingMode = CODE_USING_GOLDEN; + SetMBMotionVectorsAndMode(cpi,YFragIndex,UFragIndex, + VFragIndex,&ZeroVect); + } else if ( BestError == MBLastInterError ) { + cpi->MBCodingMode = CODE_INTER_LAST_MV; + SetMBMotionVectorsAndMode(cpi,YFragIndex,UFragIndex, + VFragIndex,&LastInterMVect); + } else if ( BestError == MBPriorLastInterError ) { + cpi->MBCodingMode = CODE_INTER_PRIOR_LAST; + SetMBMotionVectorsAndMode(cpi,YFragIndex,UFragIndex, + VFragIndex,&PriorLastInterMVect); + + /* Swap the prior and last MV cases over */ + TmpMVect.x = PriorLastInterMVect.x; + TmpMVect.y = PriorLastInterMVect.y; + PriorLastInterMVect.x = LastInterMVect.x; + PriorLastInterMVect.y = LastInterMVect.y; + LastInterMVect.x = TmpMVect.x; + LastInterMVect.y = TmpMVect.y; + + } else if ( BestError == MBInterMVError ) { + + cpi->MBCodingMode = CODE_INTER_PLUS_MV; + SetMBMotionVectorsAndMode(cpi,YFragIndex,UFragIndex, + VFragIndex,&InterMVect); + + /* Update Prior last mv with last mv */ + PriorLastInterMVect.x = LastInterMVect.x; + PriorLastInterMVect.y = LastInterMVect.y; + + /* Note last inter MV for future use */ + LastInterMVect.x = InterMVect.x; + LastInterMVect.y = InterMVect.y; + + AddMotionVector( cpi, &InterMVect); + + } else if ( BestError == MBGF_MVError ) { + + cpi->MBCodingMode = CODE_GOLDEN_MV; + SetMBMotionVectorsAndMode(cpi,YFragIndex,UFragIndex, + VFragIndex,&GFMVect); + + /* Note last inter GF MV for future use */ + LastGFMVect.x = GFMVect.x; + LastGFMVect.y = GFMVect.y; + + AddMotionVector( cpi, &GFMVect); + } else if ( BestError == MBInterFOURMVError ) { + cpi->MBCodingMode = CODE_INTER_FOURMV; + + /* Calculate the UV vectors as the average of the Y plane ones. */ + /* First .x component */ + FourMVect[4].x = FourMVect[0].x + FourMVect[1].x + + FourMVect[2].x + FourMVect[3].x; + if ( FourMVect[4].x >= 0 ) + FourMVect[4].x = (FourMVect[4].x + 2) / 4; + else + FourMVect[4].x = (FourMVect[4].x - 2) / 4; + FourMVect[5].x = FourMVect[4].x; + + /* Then .y component */ + FourMVect[4].y = FourMVect[0].y + FourMVect[1].y + + FourMVect[2].y + FourMVect[3].y; + if ( FourMVect[4].y >= 0 ) + FourMVect[4].y = (FourMVect[4].y + 2) / 4; + else + FourMVect[4].y = (FourMVect[4].y - 2) / 4; + FourMVect[5].y = FourMVect[4].y; + + SetFragMotionVectorAndMode(cpi, YFragIndex, &FourMVect[0]); + SetFragMotionVectorAndMode(cpi, YFragIndex + 1, &FourMVect[1]); + SetFragMotionVectorAndMode(cpi, YFragIndex + cpi->pb.HFragments, + &FourMVect[2]); + SetFragMotionVectorAndMode(cpi, YFragIndex + cpi->pb.HFragments + 1, + &FourMVect[3]); + SetFragMotionVectorAndMode(cpi, UFragIndex, &FourMVect[4]); + SetFragMotionVectorAndMode(cpi, VFragIndex, &FourMVect[5]); + + /* Note the four MVs values for current macro-block. */ + AddMotionVector( cpi, &FourMVect[0]); + AddMotionVector( cpi, &FourMVect[1]); + AddMotionVector( cpi, &FourMVect[2]); + AddMotionVector( cpi, &FourMVect[3]); + + /* Update Prior last mv with last mv */ + PriorLastInterMVect.x = LastInterMVect.x; + PriorLastInterMVect.y = LastInterMVect.y; + + /* Note last inter MV for future use */ + LastInterMVect.x = FourMVect[3].x; + LastInterMVect.y = FourMVect[3].y; + + } else { + + cpi->MBCodingMode = CODE_INTRA; + SetMBMotionVectorsAndMode(cpi,YFragIndex,UFragIndex, + VFragIndex,&ZeroVect); + } + + + /* setting up mode specific block types + *******************************************************/ + + *InterError += (BestError>>8); + *IntraError += (MBIntraError>>8); + + + } + SB++; + + } + } + + /* Return number of pixels coded */ + return 0; +} + +void WriteFrameHeader( CP_INSTANCE *cpi) { + ogg_uint32_t i; + oggpack_buffer *opb=cpi->oggbuffer; + /* Output the frame type (base/key frame or inter frame) */ + oggpackB_write( opb, cpi->pb.FrameType, 1 ); + /* Write out details of the current value of Q... variable resolution. */ + for ( i = 0; i < Q_TABLE_SIZE; i++ ) { + if ( cpi->pb.ThisFrameQualityValue == cpi->pb.QThreshTable[i] ) { + oggpackB_write( opb, i, 6 ); + break; + } + } + + if ( i == Q_TABLE_SIZE ) { + /* An invalid DCT value was specified. */ + /*IssueWarning( "Invalid Q Multiplier" );*/ + oggpackB_write( opb, 31, 6 ); + } + + /* we only support one Q index per frame */ + oggpackB_write( opb, 0, 1 ); + + /* If the frame was a base frame then write out the frame dimensions. */ + if ( cpi->pb.FrameType == KEY_FRAME ) { + /* all bits reserved! */ + oggpackB_write( opb, 0, 3 ); + } +} + diff --git a/Engine/lib/libtheora/lib/enc/encoder_huffman.c b/Engine/lib/libtheora/lib/enc/encoder_huffman.c new file mode 100644 index 000000000..191ada75c --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/encoder_huffman.c @@ -0,0 +1,310 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: encoder_huffman.c 13884 2007-09-22 08:38:10Z giles $ + + ********************************************************************/ + +#include +#include +#include "codec_internal.h" +#include "hufftables.h" + +static void CreateHuffmanList(HUFF_ENTRY ** HuffRoot, + ogg_uint32_t HIndex, + const ogg_uint32_t *FreqList ) { + int i; + HUFF_ENTRY *entry_ptr; + HUFF_ENTRY *search_ptr; + + /* Create a HUFF entry for token zero. */ + HuffRoot[HIndex] = (HUFF_ENTRY *)_ogg_calloc(1,sizeof(*HuffRoot[HIndex])); + + HuffRoot[HIndex]->Previous = NULL; + HuffRoot[HIndex]->Next = NULL; + HuffRoot[HIndex]->ZeroChild = NULL; + HuffRoot[HIndex]->OneChild = NULL; + HuffRoot[HIndex]->Value = 0; + HuffRoot[HIndex]->Frequency = FreqList[0]; + + if ( HuffRoot[HIndex]->Frequency == 0 ) + HuffRoot[HIndex]->Frequency = 1; + + /* Now add entries for all the other possible tokens. */ + for ( i = 1; i < MAX_ENTROPY_TOKENS; i++ ) { + entry_ptr = (HUFF_ENTRY *)_ogg_calloc(1,sizeof(*entry_ptr)); + + entry_ptr->Value = i; + entry_ptr->Frequency = FreqList[i]; + entry_ptr->ZeroChild = NULL; + entry_ptr->OneChild = NULL; + + /* Force min value of 1. This prevents the tree getting too deep. */ + if ( entry_ptr->Frequency == 0 ) + entry_ptr->Frequency = 1; + + if ( entry_ptr->Frequency <= HuffRoot[HIndex]->Frequency ){ + entry_ptr->Next = HuffRoot[HIndex]; + HuffRoot[HIndex]->Previous = entry_ptr; + entry_ptr->Previous = NULL; + HuffRoot[HIndex] = entry_ptr; + }else{ + search_ptr = HuffRoot[HIndex]; + while ( (search_ptr->Next != NULL) && + (search_ptr->Frequency < entry_ptr->Frequency) ){ + search_ptr = (HUFF_ENTRY *)search_ptr->Next; + } + + if ( search_ptr->Frequency < entry_ptr->Frequency ){ + entry_ptr->Next = NULL; + entry_ptr->Previous = search_ptr; + search_ptr->Next = entry_ptr; + }else{ + entry_ptr->Next = search_ptr; + entry_ptr->Previous = search_ptr->Previous; + search_ptr->Previous->Next = entry_ptr; + search_ptr->Previous = entry_ptr; + } + } + } +} + +static void CreateCodeArray( HUFF_ENTRY * HuffRoot, + ogg_uint32_t *HuffCodeArray, + unsigned char *HuffCodeLengthArray, + ogg_uint32_t CodeValue, + unsigned char CodeLength ) { + + /* If we are at a leaf then fill in a code array entry. */ + if ( ( HuffRoot->ZeroChild == NULL ) && ( HuffRoot->OneChild == NULL ) ){ + HuffCodeArray[HuffRoot->Value] = CodeValue; + HuffCodeLengthArray[HuffRoot->Value] = CodeLength; + }else{ + /* Recursive calls to scan down the tree. */ + CodeLength++; + CreateCodeArray(HuffRoot->ZeroChild, HuffCodeArray, HuffCodeLengthArray, + ((CodeValue << 1) + 0), CodeLength); + CreateCodeArray(HuffRoot->OneChild, HuffCodeArray, HuffCodeLengthArray, + ((CodeValue << 1) + 1), CodeLength); + } +} + +static void BuildHuffmanTree( HUFF_ENTRY **HuffRoot, + ogg_uint32_t *HuffCodeArray, + unsigned char *HuffCodeLengthArray, + ogg_uint32_t HIndex, + const ogg_uint32_t *FreqList ){ + + HUFF_ENTRY *entry_ptr; + HUFF_ENTRY *search_ptr; + + /* First create a sorted linked list representing the frequencies of + each token. */ + CreateHuffmanList( HuffRoot, HIndex, FreqList ); + + /* Now build the tree from the list. */ + + /* While there are at least two items left in the list. */ + while ( HuffRoot[HIndex]->Next != NULL ){ + /* Create the new node as the parent of the first two in the list. */ + entry_ptr = (HUFF_ENTRY *)_ogg_calloc(1,sizeof(*entry_ptr)); + entry_ptr->Value = -1; + entry_ptr->Frequency = HuffRoot[HIndex]->Frequency + + HuffRoot[HIndex]->Next->Frequency ; + entry_ptr->ZeroChild = HuffRoot[HIndex]; + entry_ptr->OneChild = HuffRoot[HIndex]->Next; + + /* If there are still more items in the list then insert the new + node into the list. */ + if (entry_ptr->OneChild->Next != NULL ){ + /* Set up the provisional 'new root' */ + HuffRoot[HIndex] = entry_ptr->OneChild->Next; + HuffRoot[HIndex]->Previous = NULL; + + /* Now scan through the remaining list to insert the new entry + at the appropriate point. */ + if ( entry_ptr->Frequency <= HuffRoot[HIndex]->Frequency ){ + entry_ptr->Next = HuffRoot[HIndex]; + HuffRoot[HIndex]->Previous = entry_ptr; + entry_ptr->Previous = NULL; + HuffRoot[HIndex] = entry_ptr; + }else{ + search_ptr = HuffRoot[HIndex]; + while ( (search_ptr->Next != NULL) && + (search_ptr->Frequency < entry_ptr->Frequency) ){ + search_ptr = search_ptr->Next; + } + + if ( search_ptr->Frequency < entry_ptr->Frequency ){ + entry_ptr->Next = NULL; + entry_ptr->Previous = search_ptr; + search_ptr->Next = entry_ptr; + }else{ + entry_ptr->Next = search_ptr; + entry_ptr->Previous = search_ptr->Previous; + search_ptr->Previous->Next = entry_ptr; + search_ptr->Previous = entry_ptr; + } + } + }else{ + /* Build has finished. */ + entry_ptr->Next = NULL; + entry_ptr->Previous = NULL; + HuffRoot[HIndex] = entry_ptr; + } + + /* Delete the Next/Previous properties of the children (PROB NOT NEC). */ + entry_ptr->ZeroChild->Next = NULL; + entry_ptr->ZeroChild->Previous = NULL; + entry_ptr->OneChild->Next = NULL; + entry_ptr->OneChild->Previous = NULL; + + } + + /* Now build a code array from the tree. */ + CreateCodeArray( HuffRoot[HIndex], HuffCodeArray, + HuffCodeLengthArray, 0, 0); +} + +static void DestroyHuffTree(HUFF_ENTRY *root_ptr){ + if (root_ptr){ + if ( root_ptr->ZeroChild ) + DestroyHuffTree(root_ptr->ZeroChild); + + if ( root_ptr->OneChild ) + DestroyHuffTree(root_ptr->OneChild); + + _ogg_free(root_ptr); + } +} + +void ClearHuffmanSet( PB_INSTANCE *pbi ){ + int i; + + ClearHuffmanTrees(pbi->HuffRoot_VP3x); + + for ( i = 0; i < NUM_HUFF_TABLES; i++ ) + if (pbi->HuffCodeArray_VP3x[i]) + _ogg_free (pbi->HuffCodeArray_VP3x[i]); + + for ( i = 0; i < NUM_HUFF_TABLES; i++ ) + if (pbi->HuffCodeLengthArray_VP3x[i]) + _ogg_free (pbi->HuffCodeLengthArray_VP3x[i]); +} + +void InitHuffmanSet( PB_INSTANCE *pbi ){ + int i; + + ClearHuffmanSet(pbi); + + pbi->ExtraBitLengths_VP3x = ExtraBitLengths_VP31; + + for ( i = 0; i < NUM_HUFF_TABLES; i++ ){ + pbi->HuffCodeArray_VP3x[i] = + _ogg_calloc(MAX_ENTROPY_TOKENS, + sizeof(*pbi->HuffCodeArray_VP3x[i])); + pbi->HuffCodeLengthArray_VP3x[i] = + _ogg_calloc(MAX_ENTROPY_TOKENS, + sizeof(*pbi->HuffCodeLengthArray_VP3x[i])); + BuildHuffmanTree( pbi->HuffRoot_VP3x, + pbi->HuffCodeArray_VP3x[i], + pbi->HuffCodeLengthArray_VP3x[i], + i, FrequencyCounts_VP3[i]); + } +} + +static int ReadHuffTree(HUFF_ENTRY * HuffRoot, int depth, + oggpack_buffer *opb) { + long bit; + long ret; + theora_read(opb,1,&bit); + if(bit < 0) return OC_BADHEADER; + else if(!bit) { + int ret; + if (++depth > 32) return OC_BADHEADER; + HuffRoot->ZeroChild = (HUFF_ENTRY *)_ogg_calloc(1, sizeof(HUFF_ENTRY)); + ret = ReadHuffTree(HuffRoot->ZeroChild, depth, opb); + if (ret < 0) return ret; + HuffRoot->OneChild = (HUFF_ENTRY *)_ogg_calloc(1, sizeof(HUFF_ENTRY)); + ret = ReadHuffTree(HuffRoot->OneChild, depth, opb); + if (ret < 0) return ret; + HuffRoot->Value = -1; + } else { + HuffRoot->ZeroChild = NULL; + HuffRoot->OneChild = NULL; + theora_read(opb,5,&ret); + HuffRoot->Value=ret;; + if (HuffRoot->Value < 0) return OC_BADHEADER; + } + return 0; +} + +int ReadHuffmanTrees(codec_setup_info *ci, oggpack_buffer *opb) { + int i; + for (i=0; iHuffRoot[i] = (HUFF_ENTRY *)_ogg_calloc(1, sizeof(HUFF_ENTRY)); + ret = ReadHuffTree(ci->HuffRoot[i], 0, opb); + if (ret) return ret; + } + return 0; +} + +static void WriteHuffTree(HUFF_ENTRY *HuffRoot, oggpack_buffer *opb) { + if (HuffRoot->Value >= 0) { + oggpackB_write(opb, 1, 1); + oggpackB_write(opb, HuffRoot->Value, 5); + } else { + oggpackB_write(opb, 0, 1); + WriteHuffTree(HuffRoot->ZeroChild, opb); + WriteHuffTree(HuffRoot->OneChild, opb); + } +} + +void WriteHuffmanTrees(HUFF_ENTRY *HuffRoot[NUM_HUFF_TABLES], + oggpack_buffer *opb) { + int i; + for(i=0; iValue = HuffSrc->Value; + if (HuffSrc->Value < 0) { + HuffDst->ZeroChild = CopyHuffTree(HuffSrc->ZeroChild); + HuffDst->OneChild = CopyHuffTree(HuffSrc->OneChild); + } + return HuffDst; + } + return NULL; +} + +void InitHuffmanTrees(PB_INSTANCE *pbi, const codec_setup_info *ci) { + int i; + pbi->ExtraBitLengths_VP3x = ExtraBitLengths_VP31; + for(i=0; iHuffRoot_VP3x[i] = CopyHuffTree(ci->HuffRoot[i]); + } +} + +void ClearHuffmanTrees(HUFF_ENTRY *HuffRoot[NUM_HUFF_TABLES]){ + int i; + for(i=0; i +#include "codec_internal.h" + +#include "quant_lookup.h" + +#define IdctAdjustBeforeShift 8 +/* cos(n*pi/16) or sin(8-n)*pi/16) */ +#define xC1S7 64277 +#define xC2S6 60547 +#define xC3S5 54491 +#define xC4S4 46341 +#define xC5S3 36410 +#define xC6S2 25080 +#define xC7S1 12785 + +/* compute the 16 bit signed 1D inverse DCT - spec version */ +/* +static void idct_short__c ( ogg_int16_t * InputData, ogg_int16_t * OutputData ) { + ogg_int32_t t[8], r; + ogg_int16_t *y = InputData; + ogg_int16_t *x = OutputData; + + t[0] = y[0] + y[4]; + t[0] &= 0xffff; + t[0] = (xC4S4 * t[0]) >> 16; + + t[1] = y[0] - y[4]; + t[1] &= 0xffff; + t[1] = (xC4S4 * t[1]) >> 16; + + t[2] = ((xC6S2 * y[2]) >> 16) - ((xC2S6 * y[6]) >> 16); + t[3] = ((xC2S6 * y[2]) >> 16) + ((xC6S2 * y[6]) >> 16); + t[4] = ((xC7S1 * y[1]) >> 16) - ((xC1S7 * y[7]) >> 16); + t[5] = ((xC3S5 * y[5]) >> 16) - ((xC5S3 * y[3]) >> 16); + t[6] = ((xC5S3 * y[5]) >> 16) + ((xC3S5 * y[3]) >> 16); + t[7] = ((xC1S7 * y[1]) >> 16) + ((xC7S1 * y[7]) >> 16); + + r = t[4] + t[5]; + t[5] = t[4] - t[5]; + t[5] &= 0xffff; + t[5] = (xC4S4 * (-t[5])) >> 16; + t[4] = r; + + r = t[7] + t[6]; + t[6] = t[7] - t[6]; + t[6] &= 0xffff; + t[6] = (xC4S4 * t[6]) >> 16; + t[7] = r; + + r = t[0] + t[3]; + t[3] = t[0] - t[3]; + t[0] = r; + + r = t[1] + t[2]; + t[2] = t[1] - t[2]; + t[1] = r; + + r = t[6] + t[5]; + t[5] = t[6] - t[5]; + t[6] = r; + + r = t[0] + t[7]; + r &= 0xffff; + x[0] = r; + + r = t[1] + t[6]; + r &= 0xffff; + x[1] = r; + + r = t[2] + t[5]; + r &= 0xffff; + x[2] = r; + + r = t[3] + t[4]; + r &= 0xffff; + x[3] = r; + + r = t[3] - t[4]; + r &= 0xffff; + x[4] = r; + + r = t[2] - t[5]; + r &= 0xffff; + x[5] = r; + + r = t[1] - t[6]; + r &= 0xffff; + x[6] = r; + + r = t[0] - t[7]; + r &= 0xffff; + x[7] = r; + +} +*/ + +static void dequant_slow( ogg_int16_t * dequant_coeffs, + ogg_int16_t * quantized_list, + ogg_int32_t * DCT_block) { + int i; + for(i=0;i<64;i++) + DCT_block[dezigzag_index[i]] = quantized_list[i] * dequant_coeffs[i]; +} + + + +void IDctSlow__c( Q_LIST_ENTRY * InputData, + ogg_int16_t *QuantMatrix, + ogg_int16_t * OutputData ) { + ogg_int32_t IntermediateData[64]; + ogg_int32_t * ip = IntermediateData; + ogg_int16_t * op = OutputData; + + ogg_int32_t _A, _B, _C, _D, _Ad, _Bd, _Cd, _Dd, _E, _F, _G, _H; + ogg_int32_t _Ed, _Gd, _Add, _Bdd, _Fd, _Hd; + ogg_int32_t t1, t2; + + int loop; + + dequant_slow( QuantMatrix, InputData, IntermediateData); + + /* Inverse DCT on the rows now */ + for ( loop = 0; loop < 8; loop++){ + /* Check for non-zero values */ + if ( ip[0] | ip[1] | ip[2] | ip[3] | ip[4] | ip[5] | ip[6] | ip[7] ) { + t1 = (xC1S7 * ip[1]); + t2 = (xC7S1 * ip[7]); + t1 >>= 16; + t2 >>= 16; + _A = t1 + t2; + + t1 = (xC7S1 * ip[1]); + t2 = (xC1S7 * ip[7]); + t1 >>= 16; + t2 >>= 16; + _B = t1 - t2; + + t1 = (xC3S5 * ip[3]); + t2 = (xC5S3 * ip[5]); + t1 >>= 16; + t2 >>= 16; + _C = t1 + t2; + + t1 = (xC3S5 * ip[5]); + t2 = (xC5S3 * ip[3]); + t1 >>= 16; + t2 >>= 16; + _D = t1 - t2; + + t1 = (xC4S4 * (ogg_int16_t)(_A - _C)); + t1 >>= 16; + _Ad = t1; + + t1 = (xC4S4 * (ogg_int16_t)(_B - _D)); + t1 >>= 16; + _Bd = t1; + + + _Cd = _A + _C; + _Dd = _B + _D; + + t1 = (xC4S4 * (ogg_int16_t)(ip[0] + ip[4])); + t1 >>= 16; + _E = t1; + + t1 = (xC4S4 * (ogg_int16_t)(ip[0] - ip[4])); + t1 >>= 16; + _F = t1; + + t1 = (xC2S6 * ip[2]); + t2 = (xC6S2 * ip[6]); + t1 >>= 16; + t2 >>= 16; + _G = t1 + t2; + + t1 = (xC6S2 * ip[2]); + t2 = (xC2S6 * ip[6]); + t1 >>= 16; + t2 >>= 16; + _H = t1 - t2; + + + _Ed = _E - _G; + _Gd = _E + _G; + + _Add = _F + _Ad; + _Bdd = _Bd - _H; + + _Fd = _F - _Ad; + _Hd = _Bd + _H; + + /* Final sequence of operations over-write original inputs. */ + ip[0] = (ogg_int16_t)((_Gd + _Cd ) >> 0); + ip[7] = (ogg_int16_t)((_Gd - _Cd ) >> 0); + + ip[1] = (ogg_int16_t)((_Add + _Hd ) >> 0); + ip[2] = (ogg_int16_t)((_Add - _Hd ) >> 0); + + ip[3] = (ogg_int16_t)((_Ed + _Dd ) >> 0); + ip[4] = (ogg_int16_t)((_Ed - _Dd ) >> 0); + + ip[5] = (ogg_int16_t)((_Fd + _Bdd ) >> 0); + ip[6] = (ogg_int16_t)((_Fd - _Bdd ) >> 0); + + } + + ip += 8; /* next row */ + } + + ip = IntermediateData; + + for ( loop = 0; loop < 8; loop++){ + /* Check for non-zero values (bitwise or faster than ||) */ + if ( ip[0 * 8] | ip[1 * 8] | ip[2 * 8] | ip[3 * 8] | + ip[4 * 8] | ip[5 * 8] | ip[6 * 8] | ip[7 * 8] ) { + + t1 = (xC1S7 * ip[1*8]); + t2 = (xC7S1 * ip[7*8]); + t1 >>= 16; + t2 >>= 16; + _A = t1 + t2; + + t1 = (xC7S1 * ip[1*8]); + t2 = (xC1S7 * ip[7*8]); + t1 >>= 16; + t2 >>= 16; + _B = t1 - t2; + + t1 = (xC3S5 * ip[3*8]); + t2 = (xC5S3 * ip[5*8]); + t1 >>= 16; + t2 >>= 16; + _C = t1 + t2; + + t1 = (xC3S5 * ip[5*8]); + t2 = (xC5S3 * ip[3*8]); + t1 >>= 16; + t2 >>= 16; + _D = t1 - t2; + + t1 = (xC4S4 * (ogg_int16_t)(_A - _C)); + t1 >>= 16; + _Ad = t1; + + t1 = (xC4S4 * (ogg_int16_t)(_B - _D)); + t1 >>= 16; + _Bd = t1; + + + _Cd = _A + _C; + _Dd = _B + _D; + + t1 = (xC4S4 * (ogg_int16_t)(ip[0*8] + ip[4*8])); + t1 >>= 16; + _E = t1; + + t1 = (xC4S4 * (ogg_int16_t)(ip[0*8] - ip[4*8])); + t1 >>= 16; + _F = t1; + + t1 = (xC2S6 * ip[2*8]); + t2 = (xC6S2 * ip[6*8]); + t1 >>= 16; + t2 >>= 16; + _G = t1 + t2; + + t1 = (xC6S2 * ip[2*8]); + t2 = (xC2S6 * ip[6*8]); + t1 >>= 16; + t2 >>= 16; + _H = t1 - t2; + + _Ed = _E - _G; + _Gd = _E + _G; + + _Add = _F + _Ad; + _Bdd = _Bd - _H; + + _Fd = _F - _Ad; + _Hd = _Bd + _H; + + _Gd += IdctAdjustBeforeShift; + _Add += IdctAdjustBeforeShift; + _Ed += IdctAdjustBeforeShift; + _Fd += IdctAdjustBeforeShift; + + /* Final sequence of operations over-write original inputs. */ + op[0*8] = (ogg_int16_t)((_Gd + _Cd ) >> 4); + op[7*8] = (ogg_int16_t)((_Gd - _Cd ) >> 4); + + op[1*8] = (ogg_int16_t)((_Add + _Hd ) >> 4); + op[2*8] = (ogg_int16_t)((_Add - _Hd ) >> 4); + + op[3*8] = (ogg_int16_t)((_Ed + _Dd ) >> 4); + op[4*8] = (ogg_int16_t)((_Ed - _Dd ) >> 4); + + op[5*8] = (ogg_int16_t)((_Fd + _Bdd ) >> 4); + op[6*8] = (ogg_int16_t)((_Fd - _Bdd ) >> 4); + }else{ + op[0*8] = 0; + op[7*8] = 0; + op[1*8] = 0; + op[2*8] = 0; + op[3*8] = 0; + op[4*8] = 0; + op[5*8] = 0; + op[6*8] = 0; + } + + ip++; /* next column */ + op++; + } +} + +/************************ + x x x x 0 0 0 0 + x x x 0 0 0 0 0 + x x 0 0 0 0 0 0 + x 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 +*************************/ + +static void dequant_slow10( ogg_int16_t * dequant_coeffs, + ogg_int16_t * quantized_list, + ogg_int32_t * DCT_block){ + int i; + memset(DCT_block,0, 128); + for(i=0;i<10;i++) + DCT_block[dezigzag_index[i]] = quantized_list[i] * dequant_coeffs[i]; + +} + +void IDct10__c( Q_LIST_ENTRY * InputData, + ogg_int16_t *QuantMatrix, + ogg_int16_t * OutputData ){ + ogg_int32_t IntermediateData[64]; + ogg_int32_t * ip = IntermediateData; + ogg_int16_t * op = OutputData; + + ogg_int32_t _A, _B, _C, _D, _Ad, _Bd, _Cd, _Dd, _E, _F, _G, _H; + ogg_int32_t _Ed, _Gd, _Add, _Bdd, _Fd, _Hd; + ogg_int32_t t1, t2; + + int loop; + + dequant_slow10( QuantMatrix, InputData, IntermediateData); + + /* Inverse DCT on the rows now */ + for ( loop = 0; loop < 4; loop++){ + /* Check for non-zero values */ + if ( ip[0] | ip[1] | ip[2] | ip[3] ){ + t1 = (xC1S7 * ip[1]); + t1 >>= 16; + _A = t1; + + t1 = (xC7S1 * ip[1]); + t1 >>= 16; + _B = t1 ; + + t1 = (xC3S5 * ip[3]); + t1 >>= 16; + _C = t1; + + t2 = (xC5S3 * ip[3]); + t2 >>= 16; + _D = -t2; + + + t1 = (xC4S4 * (ogg_int16_t)(_A - _C)); + t1 >>= 16; + _Ad = t1; + + t1 = (xC4S4 * (ogg_int16_t)(_B - _D)); + t1 >>= 16; + _Bd = t1; + + + _Cd = _A + _C; + _Dd = _B + _D; + + t1 = (xC4S4 * ip[0] ); + t1 >>= 16; + _E = t1; + + _F = t1; + + t1 = (xC2S6 * ip[2]); + t1 >>= 16; + _G = t1; + + t1 = (xC6S2 * ip[2]); + t1 >>= 16; + _H = t1 ; + + + _Ed = _E - _G; + _Gd = _E + _G; + + _Add = _F + _Ad; + _Bdd = _Bd - _H; + + _Fd = _F - _Ad; + _Hd = _Bd + _H; + + /* Final sequence of operations over-write original inputs. */ + ip[0] = (ogg_int16_t)((_Gd + _Cd ) >> 0); + ip[7] = (ogg_int16_t)((_Gd - _Cd ) >> 0); + + ip[1] = (ogg_int16_t)((_Add + _Hd ) >> 0); + ip[2] = (ogg_int16_t)((_Add - _Hd ) >> 0); + + ip[3] = (ogg_int16_t)((_Ed + _Dd ) >> 0); + ip[4] = (ogg_int16_t)((_Ed - _Dd ) >> 0); + + ip[5] = (ogg_int16_t)((_Fd + _Bdd ) >> 0); + ip[6] = (ogg_int16_t)((_Fd - _Bdd ) >> 0); + + } + + ip += 8; /* next row */ + } + + ip = IntermediateData; + + for ( loop = 0; loop < 8; loop++) { + /* Check for non-zero values (bitwise or faster than ||) */ + if ( ip[0 * 8] | ip[1 * 8] | ip[2 * 8] | ip[3 * 8] ) { + + t1 = (xC1S7 * ip[1*8]); + t1 >>= 16; + _A = t1 ; + + t1 = (xC7S1 * ip[1*8]); + t1 >>= 16; + _B = t1 ; + + t1 = (xC3S5 * ip[3*8]); + t1 >>= 16; + _C = t1 ; + + t2 = (xC5S3 * ip[3*8]); + t2 >>= 16; + _D = - t2; + + + t1 = (xC4S4 * (ogg_int16_t)(_A - _C)); + t1 >>= 16; + _Ad = t1; + + t1 = (xC4S4 * (ogg_int16_t)(_B - _D)); + t1 >>= 16; + _Bd = t1; + + + _Cd = _A + _C; + _Dd = _B + _D; + + t1 = (xC4S4 * ip[0*8]); + t1 >>= 16; + _E = t1; + _F = t1; + + t1 = (xC2S6 * ip[2*8]); + t1 >>= 16; + _G = t1; + + t1 = (xC6S2 * ip[2*8]); + t1 >>= 16; + _H = t1; + + + _Ed = _E - _G; + _Gd = _E + _G; + + _Add = _F + _Ad; + _Bdd = _Bd - _H; + + _Fd = _F - _Ad; + _Hd = _Bd + _H; + + _Gd += IdctAdjustBeforeShift; + _Add += IdctAdjustBeforeShift; + _Ed += IdctAdjustBeforeShift; + _Fd += IdctAdjustBeforeShift; + + /* Final sequence of operations over-write original inputs. */ + op[0*8] = (ogg_int16_t)((_Gd + _Cd ) >> 4); + op[7*8] = (ogg_int16_t)((_Gd - _Cd ) >> 4); + + op[1*8] = (ogg_int16_t)((_Add + _Hd ) >> 4); + op[2*8] = (ogg_int16_t)((_Add - _Hd ) >> 4); + + op[3*8] = (ogg_int16_t)((_Ed + _Dd ) >> 4); + op[4*8] = (ogg_int16_t)((_Ed - _Dd ) >> 4); + + op[5*8] = (ogg_int16_t)((_Fd + _Bdd ) >> 4); + op[6*8] = (ogg_int16_t)((_Fd - _Bdd ) >> 4); + }else{ + op[0*8] = 0; + op[7*8] = 0; + op[1*8] = 0; + op[2*8] = 0; + op[3*8] = 0; + op[4*8] = 0; + op[5*8] = 0; + op[6*8] = 0; + } + + ip++; /* next column */ + op++; + } +} + +/*************************** + x 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 +**************************/ + +void IDct1( Q_LIST_ENTRY * InputData, + ogg_int16_t *QuantMatrix, + ogg_int16_t * OutputData ){ + int loop; + + ogg_int16_t OutD; + + OutD=(ogg_int16_t) ((ogg_int32_t)(InputData[0]*QuantMatrix[0]+15)>>5); + + for(loop=0;loop<64;loop++) + OutputData[loop]=OutD; + +} + +void dsp_idct_init (DspFunctions *funcs, ogg_uint32_t cpu_flags) +{ + funcs->IDctSlow = IDctSlow__c; + funcs->IDct10 = IDct10__c; + funcs->IDct3 = IDct10__c; +#if defined(USE_ASM) + // todo: make mmx encoder idct for MSC one day... +#if !defined (_MSC_VER) + if (cpu_flags & OC_CPU_X86_MMX) { + dsp_mmx_idct_init(funcs); + } +#endif +#endif +} diff --git a/Engine/lib/libtheora/lib/enc/encoder_lookup.h b/Engine/lib/libtheora/lib/enc/encoder_lookup.h new file mode 100644 index 000000000..c5759869a --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/encoder_lookup.h @@ -0,0 +1,120 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: simple static lookups for VP3 frame encoder + last mod: $Id: encoder_lookup.h 15323 2008-09-19 19:43:59Z giles $ + + ********************************************************************/ + +#include "codec_internal.h" + +static const ogg_uint32_t MvPattern[(MAX_MV_EXTENT * 2) + 1] = { + 0x000000ff, 0x000000fd, 0x000000fb, 0x000000f9, + 0x000000f7, 0x000000f5, 0x000000f3, 0x000000f1, + 0x000000ef, 0x000000ed, 0x000000eb, 0x000000e9, + 0x000000e7, 0x000000e5, 0x000000e3, 0x000000e1, + 0x0000006f, 0x0000006d, 0x0000006b, 0x00000069, + 0x00000067, 0x00000065, 0x00000063, 0x00000061, + 0x0000002f, 0x0000002d, 0x0000002b, 0x00000029, + 0x00000009, 0x00000007, 0x00000002, 0x00000000, + 0x00000001, 0x00000006, 0x00000008, 0x00000028, + 0x0000002a, 0x0000002c, 0x0000002e, 0x00000060, + 0x00000062, 0x00000064, 0x00000066, 0x00000068, + 0x0000006a, 0x0000006c, 0x0000006e, 0x000000e0, + 0x000000e2, 0x000000e4, 0x000000e6, 0x000000e8, + 0x000000ea, 0x000000ec, 0x000000ee, 0x000000f0, + 0x000000f2, 0x000000f4, 0x000000f6, 0x000000f8, + 0x000000fa, 0x000000fc, 0x000000fe, +}; + +static const ogg_uint32_t MvBits[(MAX_MV_EXTENT * 2) + 1] = { + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 7, 7, 7, 7, 7, 7, 7, 7, + 6, 6, 6, 6, 4, 4, 3, 3, + 3, 4, 4, 6, 6, 6, 6, 7, + 7, 7, 7, 7, 7, 7, 7, 8, + 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, +}; + +static const ogg_uint32_t MvPattern2[(MAX_MV_EXTENT * 2) + 1] = { + 0x0000003f, 0x0000003d, 0x0000003b, 0x00000039, + 0x00000037, 0x00000035, 0x00000033, 0x00000031, + 0x0000002f, 0x0000002d, 0x0000002b, 0x00000029, + 0x00000027, 0x00000025, 0x00000023, 0x00000021, + 0x0000001f, 0x0000001d, 0x0000001b, 0x00000019, + 0x00000017, 0x00000015, 0x00000013, 0x00000011, + 0x0000000f, 0x0000000d, 0x0000000b, 0x00000009, + 0x00000007, 0x00000005, 0x00000003, 0x00000000, + 0x00000002, 0x00000004, 0x00000006, 0x00000008, + 0x0000000a, 0x0000000c, 0x0000000e, 0x00000010, + 0x00000012, 0x00000014, 0x00000016, 0x00000018, + 0x0000001a, 0x0000001c, 0x0000001e, 0x00000020, + 0x00000022, 0x00000024, 0x00000026, 0x00000028, + 0x0000002a, 0x0000002c, 0x0000002e, 0x00000030, + 0x00000032, 0x00000034, 0x00000036, 0x00000038, + 0x0000003a, 0x0000003c, 0x0000003e, +}; + +static const ogg_uint32_t MvBits2[(MAX_MV_EXTENT * 2) + 1] = { + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, +}; + +static const ogg_uint32_t ModeBitPatterns[MAX_MODES] = { + 0x00, 0x02, 0x06, 0x0E, 0x1E, 0x3E, 0x7E, 0x7F }; + +static const ogg_int32_t ModeBitLengths[MAX_MODES] = { + 1, 2, 3, 4, 5, 6, 7, 7 }; + +static const unsigned char ModeSchemes[MODE_METHODS-2][MAX_MODES] = { + /* Last Mv dominates */ + { 3, 4, 2, 0, 1, 5, 6, 7 }, /* L P M N I G GM 4 */ + { 2, 4, 3, 0, 1, 5, 6, 7 }, /* L P N M I G GM 4 */ + { 3, 4, 1, 0, 2, 5, 6, 7 }, /* L M P N I G GM 4 */ + { 2, 4, 1, 0, 3, 5, 6, 7 }, /* L M N P I G GM 4 */ + + /* No MV dominates */ + { 0, 4, 3, 1, 2, 5, 6, 7 }, /* N L P M I G GM 4 */ + { 0, 5, 4, 2, 3, 1, 6, 7 }, /* N G L P M I GM 4 */ + +}; + + +static const ogg_uint32_t MvThreshTable[Q_TABLE_SIZE] = { + 65, 65, 65, 65, 50, 50, 50, 50, + 40, 40, 40, 40, 40, 40, 40, 40, + 30, 30, 30, 30, 30, 30, 30, 30, + 20, 20, 20, 20, 20, 20, 20, 20, + 15, 15, 15, 15, 15, 15, 15, 15, + 10, 10, 10, 10, 10, 10, 10, 10, + 5, 5, 5, 5, 5, 5, 5, 5, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const ogg_uint32_t MVChangeFactorTable[Q_TABLE_SIZE] = { + 11, 11, 11, 11, 12, 12, 12, 12, + 13, 13, 13, 13, 13, 13, 13, 13, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15 +}; diff --git a/Engine/lib/libtheora/lib/enc/encoder_quant.c b/Engine/lib/libtheora/lib/enc/encoder_quant.c new file mode 100644 index 000000000..a5639a233 --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/encoder_quant.c @@ -0,0 +1,558 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2005 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: encoder_quant.c 15153 2008-08-04 18:37:55Z tterribe $ + + ********************************************************************/ + +#include +#include +#include "codec_internal.h" +#include "quant_lookup.h" + +#define OC_QUANT_MAX (1024<<2) +static const unsigned DC_QUANT_MIN[2]={4<<2,8<<2}; +static const unsigned AC_QUANT_MIN[2]={2<<2,4<<2}; +#define OC_MAXI(_a,_b) ((_a)<(_b)?(_b):(_a)) +#define OC_MINI(_a,_b) ((_a)>(_b)?(_b):(_a)) +#define OC_CLAMPI(_a,_b,_c) (OC_MAXI(_a,OC_MINI(_b,_c))) + +static int ilog(unsigned _v){ + int ret; + for(ret=0;_v;ret++)_v>>=1; + return ret; +} + + +void WriteQTables(PB_INSTANCE *pbi,oggpack_buffer* _opb) { + + th_quant_info *_qinfo = &pbi->quant_info; + + const th_quant_ranges *qranges; + const th_quant_base *base_mats[2*3*64]; + int indices[2][3][64]; + int nbase_mats; + int nbits; + int ci; + int qi; + int qri; + int qti; + int pli; + int qtj; + int plj; + int bmi; + int i; + + /*Unlike the scale tables, we can't assume the maximum value will be in + index 0, so search for it here.*/ + i=_qinfo->loop_filter_limits[0]; + for(qi=1;qi<64;qi++)i=OC_MAXI(i,_qinfo->loop_filter_limits[qi]); + nbits=ilog(i); + oggpackB_write(_opb,nbits,3); + for(qi=0;qi<64;qi++){ + oggpackB_write(_opb,_qinfo->loop_filter_limits[qi],nbits); + } + /* 580 bits for VP3.*/ + nbits=OC_MAXI(ilog(_qinfo->ac_scale[0]),1); + oggpackB_write(_opb,nbits-1,4); + for(qi=0;qi<64;qi++)oggpackB_write(_opb,_qinfo->ac_scale[qi],nbits); + /* 516 bits for VP3.*/ + nbits=OC_MAXI(ilog(_qinfo->dc_scale[0]),1); + oggpackB_write(_opb,nbits-1,4); + for(qi=0;qi<64;qi++)oggpackB_write(_opb,_qinfo->dc_scale[qi],nbits); + /*Consolidate any duplicate base matrices.*/ + nbase_mats=0; + for(qti=0;qti<2;qti++)for(pli=0;pli<3;pli++){ + qranges=_qinfo->qi_ranges[qti]+pli; + for(qri=0;qri<=qranges->nranges;qri++){ + for(bmi=0;;bmi++){ + if(bmi>=nbase_mats){ + base_mats[bmi]=qranges->base_matrices+qri; + indices[qti][pli][qri]=nbase_mats++; + break; + } + else if(memcmp(base_mats[bmi][0],qranges->base_matrices[qri], + sizeof(base_mats[bmi][0]))==0){ + indices[qti][pli][qri]=bmi; + break; + } + } + } + } + /*Write out the list of unique base matrices. + 1545 bits for VP3 matrices.*/ + oggpackB_write(_opb,nbase_mats-1,9); + for(bmi=0;bmiqi_ranges[qti]+pli; + if(i>0){ + if(qti>0){ + if(qranges->nranges==_qinfo->qi_ranges[qti-1][pli].nranges&& + memcmp(qranges->sizes,_qinfo->qi_ranges[qti-1][pli].sizes, + qranges->nranges*sizeof(qranges->sizes[0]))==0&& + memcmp(indices[qti][pli],indices[qti-1][pli], + (qranges->nranges+1)*sizeof(indices[qti][pli][0]))==0){ + oggpackB_write(_opb,1,2); + continue; + } + } + qtj=(i-1)/3; + plj=(i-1)%3; + if(qranges->nranges==_qinfo->qi_ranges[qtj][plj].nranges&& + memcmp(qranges->sizes,_qinfo->qi_ranges[qtj][plj].sizes, + qranges->nranges*sizeof(qranges->sizes[0]))==0&& + memcmp(indices[qti][pli],indices[qtj][plj], + (qranges->nranges+1)*sizeof(indices[qti][pli][0]))==0){ + oggpackB_write(_opb,0,1+(qti>0)); + continue; + } + oggpackB_write(_opb,1,1); + } + oggpackB_write(_opb,indices[qti][pli][0],nbits); + for(qi=qri=0;qi<63;qri++){ + oggpackB_write(_opb,qranges->sizes[qri]-1,ilog(62-qi)); + qi+=qranges->sizes[qri]; + oggpackB_write(_opb,indices[qti][pli][qri+1],nbits); + } + } +} + +/* a copied/reconciled version of derf's theora-exp code; redundancy + should be eliminated at some point */ +void InitQTables( PB_INSTANCE *pbi ){ + int qti; /* coding mode: intra or inter */ + int pli; /* Y U V */ + th_quant_info *qinfo = &pbi->quant_info; + + pbi->QThreshTable = pbi->quant_info.ac_scale; + + for(qti=0;qti<2;qti++){ + for(pli=0;pli<3;pli++){ + int qi; /* quality index */ + int qri; /* range iterator */ + + for(qi=0,qri=0; qri<=qinfo->qi_ranges[qti][pli].nranges; qri++){ + th_quant_base base; + + ogg_uint32_t q; + int qi_start; + int qi_end; + int ci; + memcpy(base,qinfo->qi_ranges[qti][pli].base_matrices[qri], + sizeof(base)); + + qi_start=qi; + if(qri==qinfo->qi_ranges[qti][pli].nranges) + qi_end=qi+1; + else + qi_end=qi+qinfo->qi_ranges[qti][pli].sizes[qri]; + + /* Iterate over quality indicies in this range */ + for(;;){ + + /*Scale DC the coefficient from the proper table.*/ + q=((ogg_uint32_t)qinfo->dc_scale[qi]*base[0]/100)<<2; + q=OC_CLAMPI(DC_QUANT_MIN[qti],q,OC_QUANT_MAX); + pbi->quant_tables[qti][pli][qi][0]=(ogg_uint16_t)q; + + /*Now scale AC coefficients from the proper table.*/ + for(ci=1;ci<64;ci++){ + q=((ogg_uint32_t)qinfo->ac_scale[qi]*base[ci]/100)<<2; + q=OC_CLAMPI(AC_QUANT_MIN[qti],q,OC_QUANT_MAX); + pbi->quant_tables[qti][pli][qi][ci]=(ogg_uint16_t)q; + } + + if(++qi>=qi_end)break; + + /*Interpolate the next base matrix.*/ + for(ci=0;ci<64;ci++){ + base[ci]=(unsigned char) + ((2*((qi_end-qi)*qinfo->qi_ranges[qti][pli].base_matrices[qri][ci]+ + (qi-qi_start)*qinfo->qi_ranges[qti][pli].base_matrices[qri+1][ci]) + +qinfo->qi_ranges[qti][pli].sizes[qri])/ + (2*qinfo->qi_ranges[qti][pli].sizes[qri])); + } + } + } + } + } +} + +static void BuildZigZagIndex(PB_INSTANCE *pbi){ + ogg_int32_t i,j; + + /* invert the row to zigzag coeffient order lookup table */ + for ( i = 0; i < BLOCK_SIZE; i++ ){ + j = dezigzag_index[i]; + pbi->zigzag_index[j] = i; + } +} + +static void init_quantizer ( CP_INSTANCE *cpi, + unsigned char QIndex ){ + int i; + double ZBinFactor; + double RoundingFactor; + + double temp_fp_quant_coeffs; + double temp_fp_quant_round; + double temp_fp_ZeroBinSize; + PB_INSTANCE *pbi = &cpi->pb; + + + const ogg_uint16_t * temp_Y_coeffs; + const ogg_uint16_t * temp_U_coeffs; + const ogg_uint16_t * temp_V_coeffs; + const ogg_uint16_t * temp_Inter_Y_coeffs; + const ogg_uint16_t * temp_Inter_U_coeffs; + const ogg_uint16_t * temp_Inter_V_coeffs; + ogg_uint16_t scale_factor = cpi->pb.quant_info.ac_scale[QIndex]; + + /* Notes on setup of quantisers. The initial multiplication by + the scale factor is done in the ogg_int32_t domain to insure that the + precision in the quantiser is the same as in the inverse + quantiser where all calculations are integer. The "<< 2" is a + normalisation factor for the forward DCT transform. */ + + temp_Y_coeffs = pbi->quant_tables[0][0][QIndex]; + temp_U_coeffs = pbi->quant_tables[0][1][QIndex]; + temp_V_coeffs = pbi->quant_tables[0][2][QIndex]; + temp_Inter_Y_coeffs = pbi->quant_tables[1][0][QIndex]; + temp_Inter_U_coeffs = pbi->quant_tables[1][1][QIndex]; + temp_Inter_V_coeffs = pbi->quant_tables[1][2][QIndex]; + + ZBinFactor = 0.9; + + switch(cpi->pb.info.sharpness){ + case 0: + ZBinFactor = 0.65; + if ( scale_factor <= 50 ) + RoundingFactor = 0.499; + else + RoundingFactor = 0.46; + break; + case 1: + ZBinFactor = 0.75; + if ( scale_factor <= 50 ) + RoundingFactor = 0.476; + else + RoundingFactor = 0.400; + break; + + default: + ZBinFactor = 0.9; + if ( scale_factor <= 50 ) + RoundingFactor = 0.476; + else + RoundingFactor = 0.333; + break; + } + + /* Use fixed multiplier for intra Y DC */ + temp_fp_quant_coeffs = temp_Y_coeffs[0]; + temp_fp_quant_round = temp_fp_quant_coeffs * RoundingFactor; + pbi->fp_quant_Y_round[0] = (ogg_int32_t) (0.5 + temp_fp_quant_round); + temp_fp_ZeroBinSize = temp_fp_quant_coeffs * ZBinFactor; + pbi->fp_ZeroBinSize_Y[0] = (ogg_int32_t) (0.5 + temp_fp_ZeroBinSize); + temp_fp_quant_coeffs = 1.0 / temp_fp_quant_coeffs; + pbi->fp_quant_Y_coeffs[0] = (0.5 + SHIFT16 * temp_fp_quant_coeffs); + + /* Intra U */ + temp_fp_quant_coeffs = temp_U_coeffs[0]; + temp_fp_quant_round = temp_fp_quant_coeffs * RoundingFactor; + pbi->fp_quant_U_round[0] = (0.5 + temp_fp_quant_round); + temp_fp_ZeroBinSize = temp_fp_quant_coeffs * ZBinFactor; + pbi->fp_ZeroBinSize_U[0] = (0.5 + temp_fp_ZeroBinSize); + temp_fp_quant_coeffs = 1.0 / temp_fp_quant_coeffs; + pbi->fp_quant_U_coeffs[0]= (0.5 + SHIFT16 * temp_fp_quant_coeffs); + + /* Intra V */ + temp_fp_quant_coeffs = temp_V_coeffs[0]; + temp_fp_quant_round = temp_fp_quant_coeffs * RoundingFactor; + pbi->fp_quant_V_round[0] = (0.5 + temp_fp_quant_round); + temp_fp_ZeroBinSize = temp_fp_quant_coeffs * ZBinFactor; + pbi->fp_ZeroBinSize_V[0] = (0.5 + temp_fp_ZeroBinSize); + temp_fp_quant_coeffs = 1.0 / temp_fp_quant_coeffs; + pbi->fp_quant_V_coeffs[0]= (0.5 + SHIFT16 * temp_fp_quant_coeffs); + + + /* Inter Y */ + temp_fp_quant_coeffs = temp_Inter_Y_coeffs[0]; + temp_fp_quant_round = temp_fp_quant_coeffs * RoundingFactor; + pbi->fp_quant_Inter_Y_round[0]= (0.5 + temp_fp_quant_round); + temp_fp_ZeroBinSize = temp_fp_quant_coeffs * ZBinFactor; + pbi->fp_ZeroBinSize_Inter_Y[0]= (0.5 + temp_fp_ZeroBinSize); + temp_fp_quant_coeffs= 1.0 / temp_fp_quant_coeffs; + pbi->fp_quant_Inter_Y_coeffs[0]= (0.5 + SHIFT16 * temp_fp_quant_coeffs); + + /* Inter U */ + temp_fp_quant_coeffs = temp_Inter_U_coeffs[0]; + temp_fp_quant_round = temp_fp_quant_coeffs * RoundingFactor; + pbi->fp_quant_Inter_U_round[0]= (0.5 + temp_fp_quant_round); + temp_fp_ZeroBinSize = temp_fp_quant_coeffs * ZBinFactor; + pbi->fp_ZeroBinSize_Inter_U[0]= (0.5 + temp_fp_ZeroBinSize); + temp_fp_quant_coeffs= 1.0 / temp_fp_quant_coeffs; + pbi->fp_quant_Inter_U_coeffs[0]= (0.5 + SHIFT16 * temp_fp_quant_coeffs); + + /* Inter V */ + temp_fp_quant_coeffs = temp_Inter_V_coeffs[0]; + temp_fp_quant_round = temp_fp_quant_coeffs * RoundingFactor; + pbi->fp_quant_Inter_V_round[0]= (0.5 + temp_fp_quant_round); + temp_fp_ZeroBinSize = temp_fp_quant_coeffs * ZBinFactor; + pbi->fp_ZeroBinSize_Inter_V[0]= (0.5 + temp_fp_ZeroBinSize); + temp_fp_quant_coeffs= 1.0 / temp_fp_quant_coeffs; + pbi->fp_quant_Inter_V_coeffs[0]= (0.5 + SHIFT16 * temp_fp_quant_coeffs); + + + for ( i = 1; i < 64; i++ ){ + /* Intra Y */ + temp_fp_quant_coeffs = temp_Y_coeffs[i]; + temp_fp_quant_round = temp_fp_quant_coeffs * RoundingFactor; + pbi->fp_quant_Y_round[i] = (0.5 + temp_fp_quant_round); + temp_fp_ZeroBinSize = temp_fp_quant_coeffs * ZBinFactor; + pbi->fp_ZeroBinSize_Y[i] = (0.5 + temp_fp_ZeroBinSize); + temp_fp_quant_coeffs = 1.0 / temp_fp_quant_coeffs; + pbi->fp_quant_Y_coeffs[i] = (0.5 + SHIFT16 * temp_fp_quant_coeffs); + + /* Intra U */ + temp_fp_quant_coeffs = temp_U_coeffs[i]; + temp_fp_quant_round = temp_fp_quant_coeffs * RoundingFactor; + pbi->fp_quant_U_round[i] = (0.5 + temp_fp_quant_round); + temp_fp_ZeroBinSize = temp_fp_quant_coeffs * ZBinFactor; + pbi->fp_ZeroBinSize_U[i] = (0.5 + temp_fp_ZeroBinSize); + temp_fp_quant_coeffs = 1.0 / temp_fp_quant_coeffs; + pbi->fp_quant_U_coeffs[i]= (0.5 + SHIFT16 * temp_fp_quant_coeffs); + + /* Intra V */ + temp_fp_quant_coeffs = temp_V_coeffs[i]; + temp_fp_quant_round = temp_fp_quant_coeffs * RoundingFactor; + pbi->fp_quant_V_round[i] = (0.5 + temp_fp_quant_round); + temp_fp_ZeroBinSize = temp_fp_quant_coeffs * ZBinFactor; + pbi->fp_ZeroBinSize_V[i] = (0.5 + temp_fp_ZeroBinSize); + temp_fp_quant_coeffs = 1.0 / temp_fp_quant_coeffs; + pbi->fp_quant_V_coeffs[i]= (0.5 + SHIFT16 * temp_fp_quant_coeffs); + + /* Inter Y */ + temp_fp_quant_coeffs = temp_Inter_Y_coeffs[i]; + temp_fp_quant_round = temp_fp_quant_coeffs * RoundingFactor; + pbi->fp_quant_Inter_Y_round[i]= (0.5 + temp_fp_quant_round); + temp_fp_ZeroBinSize = temp_fp_quant_coeffs * ZBinFactor; + pbi->fp_ZeroBinSize_Inter_Y[i]= (0.5 + temp_fp_ZeroBinSize); + temp_fp_quant_coeffs = 1.0 / temp_fp_quant_coeffs; + pbi->fp_quant_Inter_Y_coeffs[i]= (0.5 + SHIFT16 * temp_fp_quant_coeffs); + + /* Inter U */ + temp_fp_quant_coeffs = temp_Inter_U_coeffs[i]; + temp_fp_quant_round = temp_fp_quant_coeffs * RoundingFactor; + pbi->fp_quant_Inter_U_round[i]= (0.5 + temp_fp_quant_round); + temp_fp_ZeroBinSize = temp_fp_quant_coeffs * ZBinFactor; + pbi->fp_ZeroBinSize_Inter_U[i]= (0.5 + temp_fp_ZeroBinSize); + temp_fp_quant_coeffs = 1.0 / temp_fp_quant_coeffs; + pbi->fp_quant_Inter_U_coeffs[i]= (0.5 + SHIFT16 * temp_fp_quant_coeffs); + + /* Inter V */ + temp_fp_quant_coeffs = temp_Inter_V_coeffs[i]; + temp_fp_quant_round = temp_fp_quant_coeffs * RoundingFactor; + pbi->fp_quant_Inter_V_round[i]= (0.5 + temp_fp_quant_round); + temp_fp_ZeroBinSize = temp_fp_quant_coeffs * ZBinFactor; + pbi->fp_ZeroBinSize_Inter_V[i]= (0.5 + temp_fp_ZeroBinSize); + temp_fp_quant_coeffs = 1.0 / temp_fp_quant_coeffs; + pbi->fp_quant_Inter_V_coeffs[i]= (0.5 + SHIFT16 * temp_fp_quant_coeffs); + + + } + + pbi->fquant_coeffs = pbi->fp_quant_Y_coeffs; + +} + +void select_quantiser(PB_INSTANCE *pbi, int type) { + /* select a quantiser according to what plane has to be coded in what + * mode. Could be extended to a more sophisticated scheme. */ + + switch(type) { + case BLOCK_Y: + pbi->fquant_coeffs = pbi->fp_quant_Y_coeffs; + pbi->fquant_round = pbi->fp_quant_Y_round; + pbi->fquant_ZbSize = pbi->fp_ZeroBinSize_Y; + break; + case BLOCK_U: + pbi->fquant_coeffs = pbi->fp_quant_U_coeffs; + pbi->fquant_round = pbi->fp_quant_U_round; + pbi->fquant_ZbSize = pbi->fp_ZeroBinSize_U; + break; + case BLOCK_V: + pbi->fquant_coeffs = pbi->fp_quant_V_coeffs; + pbi->fquant_round = pbi->fp_quant_V_round; + pbi->fquant_ZbSize = pbi->fp_ZeroBinSize_V; + break; + case BLOCK_INTER_Y: + pbi->fquant_coeffs = pbi->fp_quant_Inter_Y_coeffs; + pbi->fquant_round = pbi->fp_quant_Inter_Y_round; + pbi->fquant_ZbSize = pbi->fp_ZeroBinSize_Inter_Y; + break; + case BLOCK_INTER_U: + pbi->fquant_coeffs = pbi->fp_quant_Inter_U_coeffs; + pbi->fquant_round = pbi->fp_quant_Inter_U_round; + pbi->fquant_ZbSize = pbi->fp_ZeroBinSize_Inter_U; + break; + case BLOCK_INTER_V: + pbi->fquant_coeffs = pbi->fp_quant_Inter_V_coeffs; + pbi->fquant_round = pbi->fp_quant_Inter_V_round; + pbi->fquant_ZbSize = pbi->fp_ZeroBinSize_Inter_V; + break; + } +} + + +void quantize( PB_INSTANCE *pbi, + ogg_int16_t * DCT_block, + Q_LIST_ENTRY * quantized_list){ + ogg_uint32_t i; /* Row index */ + Q_LIST_ENTRY val; /* Quantised value. */ + + ogg_int32_t * FquantRoundPtr = pbi->fquant_round; + ogg_int32_t * FquantCoeffsPtr = pbi->fquant_coeffs; + ogg_int32_t * FquantZBinSizePtr = pbi->fquant_ZbSize; + ogg_int16_t * DCT_blockPtr = DCT_block; + ogg_uint32_t * ZigZagPtr = (ogg_uint32_t *)pbi->zigzag_index; + ogg_int32_t temp; + + /* Set the quantized_list to default to 0 */ + memset( quantized_list, 0, 64 * sizeof(Q_LIST_ENTRY) ); + + /* Note that we add half divisor to effect rounding on positive number */ + for( i = 0; i < VFRAGPIXELS; i++) { + + int col; + /* Iterate through columns */ + for( col = 0; col < 8; col++) { + if ( DCT_blockPtr[col] >= FquantZBinSizePtr[col] ) { + temp = FquantCoeffsPtr[col] * ( DCT_blockPtr[col] + FquantRoundPtr[col] ) ; + val = (Q_LIST_ENTRY) (temp>>16); + quantized_list[ZigZagPtr[col]] = ( val > 511 ) ? 511 : val; + } else if ( DCT_blockPtr[col] <= -FquantZBinSizePtr[col] ) { + temp = FquantCoeffsPtr[col] * + ( DCT_blockPtr[col] - FquantRoundPtr[col] ) + MIN16; + val = (Q_LIST_ENTRY) (temp>>16); + quantized_list[ZigZagPtr[col]] = ( val < -511 ) ? -511 : val; + } + } + + FquantRoundPtr += 8; + FquantCoeffsPtr += 8; + FquantZBinSizePtr += 8; + DCT_blockPtr += 8; + ZigZagPtr += 8; + } +} + +static void init_dequantizer ( PB_INSTANCE *pbi, + unsigned char QIndex ){ + int i, j; + + ogg_uint16_t * InterY_coeffs; + ogg_uint16_t * InterU_coeffs; + ogg_uint16_t * InterV_coeffs; + ogg_uint16_t * Y_coeffs; + ogg_uint16_t * U_coeffs; + ogg_uint16_t * V_coeffs; + + Y_coeffs = pbi->quant_tables[0][0][QIndex]; + U_coeffs = pbi->quant_tables[0][1][QIndex]; + V_coeffs = pbi->quant_tables[0][2][QIndex]; + InterY_coeffs = pbi->quant_tables[1][0][QIndex]; + InterU_coeffs = pbi->quant_tables[1][1][QIndex]; + InterV_coeffs = pbi->quant_tables[1][2][QIndex]; + + /* invert the dequant index into the quant index + the dxer has a different order than the cxer. */ + BuildZigZagIndex(pbi); + + /* Reorder dequantisation coefficients into dct zigzag order. */ + for ( i = 0; i < BLOCK_SIZE; i++ ) { + j = pbi->zigzag_index[i]; + pbi->dequant_Y_coeffs[j] = Y_coeffs[i]; + } + for ( i = 0; i < BLOCK_SIZE; i++ ) { + j = pbi->zigzag_index[i]; + pbi->dequant_U_coeffs[j] = U_coeffs[i]; + } + for ( i = 0; i < BLOCK_SIZE; i++ ) { + j = pbi->zigzag_index[i]; + pbi->dequant_V_coeffs[j] = V_coeffs[i]; + } + for ( i = 0; i < BLOCK_SIZE; i++ ){ + j = pbi->zigzag_index[i]; + pbi->dequant_InterY_coeffs[j] = InterY_coeffs[i]; + } + for ( i = 0; i < BLOCK_SIZE; i++ ){ + j = pbi->zigzag_index[i]; + pbi->dequant_InterU_coeffs[j] = InterU_coeffs[i]; + } + for ( i = 0; i < BLOCK_SIZE; i++ ){ + j = pbi->zigzag_index[i]; + pbi->dequant_InterV_coeffs[j] = InterV_coeffs[i]; + } + + pbi->dequant_coeffs = pbi->dequant_Y_coeffs; +} + +void UpdateQ( PB_INSTANCE *pbi, int NewQIndex ){ + ogg_uint32_t qscale; + + /* clamp to legal bounds */ + if (NewQIndex >= Q_TABLE_SIZE) NewQIndex = Q_TABLE_SIZE - 1; + else if (NewQIndex < 0) NewQIndex = 0; + + pbi->FrameQIndex = NewQIndex; + + qscale = pbi->quant_info.ac_scale[NewQIndex]; + pbi->ThisFrameQualityValue = qscale; + + /* Re-initialise the Q tables for forward and reverse transforms. */ + init_dequantizer ( pbi, (unsigned char) pbi->FrameQIndex ); +} + +void UpdateQC( CP_INSTANCE *cpi, ogg_uint32_t NewQ ){ + ogg_uint32_t qscale; + PB_INSTANCE *pbi = &cpi->pb; + + /* Do bounds checking and convert to a float. */ + qscale = NewQ; + if ( qscale < pbi->quant_info.ac_scale[Q_TABLE_SIZE-1] ) + qscale = pbi->quant_info.ac_scale[Q_TABLE_SIZE-1]; + else if ( qscale > pbi->quant_info.ac_scale[0] ) + qscale = pbi->quant_info.ac_scale[0]; + + /* Set the inter/intra descision control variables. */ + pbi->FrameQIndex = Q_TABLE_SIZE - 1; + while ((ogg_int32_t) pbi->FrameQIndex >= 0 ) { + if ( (pbi->FrameQIndex == 0) || + ( pbi->quant_info.ac_scale[pbi->FrameQIndex] >= NewQ) ) + break; + pbi->FrameQIndex --; + } + + /* Re-initialise the Q tables for forward and reverse transforms. */ + init_quantizer ( cpi, pbi->FrameQIndex ); + init_dequantizer ( pbi, pbi->FrameQIndex ); +} diff --git a/Engine/lib/libtheora/lib/enc/encoder_toplevel.c b/Engine/lib/libtheora/lib/enc/encoder_toplevel.c new file mode 100644 index 000000000..9356bba23 --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/encoder_toplevel.c @@ -0,0 +1,1447 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: encoder_toplevel.c 15383 2008-10-10 14:33:46Z xiphmont $ + + ********************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include "toplevel_lookup.h" +#include "../internal.h" +#include "dsp.h" +#include "codec_internal.h" + +#define A_TABLE_SIZE 29 +#define DF_CANDIDATE_WINDOW 5 + +/* + * th_quant_info for VP3 + */ + +/*The default quantization parameters used by VP3.1.*/ +static const int OC_VP31_RANGE_SIZES[1]={63}; +static const th_quant_base OC_VP31_BASES_INTRA_Y[2]={ + { + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 58, 68, 109,103, 77, + 24, 35, 55, 64, 81, 104,113, 92, + 49, 64, 78, 87,103, 121,120,101, + 72, 92, 95, 98,112, 100,103, 99 + }, + { + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 58, 68, 109,103, 77, + 24, 35, 55, 64, 81, 104,113, 92, + 49, 64, 78, 87,103, 121,120,101, + 72, 92, 95, 98,112, 100,103, 99 + } +}; +static const th_quant_base OC_VP31_BASES_INTRA_C[2]={ + { + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 + }, + { + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 + } +}; +static const th_quant_base OC_VP31_BASES_INTER[2]={ + { + 16, 16, 16, 20, 24, 28, 32, 40, + 16, 16, 20, 24, 28, 32, 40, 48, + 16, 20, 24, 28, 32, 40, 48, 64, + 20, 24, 28, 32, 40, 48, 64, 64, + 24, 28, 32, 40, 48, 64, 64, 64, + 28, 32, 40, 48, 64, 64, 64, 96, + 32, 40, 48, 64, 64, 64, 96,128, + 40, 48, 64, 64, 64, 96,128,128 + }, + { + 16, 16, 16, 20, 24, 28, 32, 40, + 16, 16, 20, 24, 28, 32, 40, 48, + 16, 20, 24, 28, 32, 40, 48, 64, + 20, 24, 28, 32, 40, 48, 64, 64, + 24, 28, 32, 40, 48, 64, 64, 64, + 28, 32, 40, 48, 64, 64, 64, 96, + 32, 40, 48, 64, 64, 64, 96,128, + 40, 48, 64, 64, 64, 96,128,128 + } +}; + +const th_quant_info TH_VP31_QUANT_INFO={ + { + 220,200,190,180,170,170,160,160, + 150,150,140,140,130,130,120,120, + 110,110,100,100, 90, 90, 90, 80, + 80, 80, 70, 70, 70, 60, 60, 60, + 60, 50, 50, 50, 50, 40, 40, 40, + 40, 40, 30, 30, 30, 30, 30, 30, + 30, 20, 20, 20, 20, 20, 20, 20, + 20, 10, 10, 10, 10, 10, 10, 10 + }, + { + 500,450,400,370,340,310,285,265, + 245,225,210,195,185,180,170,160, + 150,145,135,130,125,115,110,107, + 100, 96, 93, 89, 85, 82, 75, 74, + 70, 68, 64, 60, 57, 56, 52, 50, + 49, 45, 44, 43, 40, 38, 37, 35, + 33, 32, 30, 29, 28, 25, 24, 22, + 21, 19, 18, 17, 15, 13, 12, 10 + }, + { + 30,25,20,20,15,15,14,14, + 13,13,12,12,11,11,10,10, + 9, 9, 8, 8, 7, 7, 7, 7, + 6, 6, 6, 6, 5, 5, 5, 5, + 4, 4, 4, 4, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + { + {1,OC_VP31_RANGE_SIZES,OC_VP31_BASES_INTRA_Y}, + {1,OC_VP31_RANGE_SIZES,OC_VP31_BASES_INTRA_C}, + {1,OC_VP31_RANGE_SIZES,OC_VP31_BASES_INTRA_C} + }, + { + {1,OC_VP31_RANGE_SIZES,OC_VP31_BASES_INTER}, + {1,OC_VP31_RANGE_SIZES,OC_VP31_BASES_INTER}, + {1,OC_VP31_RANGE_SIZES,OC_VP31_BASES_INTER} + } + } +}; + + +static void EClearFragmentInfo(CP_INSTANCE * cpi){ + if(cpi->extra_fragments) + _ogg_free(cpi->extra_fragments); + if(cpi->FragmentLastQ) + _ogg_free(cpi->FragmentLastQ); + if(cpi->FragTokens) + _ogg_free(cpi->FragTokens); + if(cpi->FragTokenCounts) + _ogg_free(cpi->FragTokenCounts); + if(cpi->RunHuffIndices) + _ogg_free(cpi->RunHuffIndices); + if(cpi->LastCodedErrorScore) + _ogg_free(cpi->LastCodedErrorScore); + if(cpi->ModeList) + _ogg_free(cpi->ModeList); + if(cpi->MVList) + _ogg_free(cpi->MVList); + if(cpi->DCT_codes ) + _ogg_free( cpi->DCT_codes ); + if(cpi->DCTDataBuffer ) + _ogg_free( cpi->DCTDataBuffer); + if(cpi->quantized_list) + _ogg_free( cpi->quantized_list); + if(cpi->OriginalDC) + _ogg_free( cpi->OriginalDC); + if(cpi->PartiallyCodedFlags) + _ogg_free(cpi->PartiallyCodedFlags); + if(cpi->PartiallyCodedMbPatterns) + _ogg_free(cpi->PartiallyCodedMbPatterns); + if(cpi->UncodedMbFlags) + _ogg_free(cpi->UncodedMbFlags); + + if(cpi->BlockCodedFlags) + _ogg_free(cpi->BlockCodedFlags); + + cpi->extra_fragments = 0; + cpi->FragmentLastQ = 0; + cpi->FragTokens = 0; + cpi->FragTokenCounts = 0; + cpi->RunHuffIndices = 0; + cpi->LastCodedErrorScore = 0; + cpi->ModeList = 0; + cpi->MVList = 0; + cpi->DCT_codes = 0; + cpi->DCTDataBuffer = 0; + cpi->quantized_list = 0; + cpi->OriginalDC = 0; + cpi->BlockCodedFlags = 0; +} + +static void EInitFragmentInfo(CP_INSTANCE * cpi){ + + /* clear any existing info */ + EClearFragmentInfo(cpi); + + /* Perform Fragment Allocations */ + cpi->extra_fragments = + _ogg_malloc(cpi->pb.UnitFragments*sizeof(unsigned char)); + + /* A note to people reading and wondering why malloc returns aren't + checked: + + lines like the following that implement a general strategy of + 'check the return of malloc; a zero pointer means we're out of + memory!'...: + + if(!cpi->extra_fragments) { EDeleteFragmentInfo(cpi); return FALSE; } + + ...are not useful. It's true that many platforms follow this + malloc behavior, but many do not. The more modern malloc + strategy is only to allocate virtual pages, which are not mapped + until the memory on that page is touched. At *that* point, if + the machine is out of heap, the page fails to be mapped and a + SEGV is generated. + + That means that if we want to deal with out of memory conditions, + we *must* be prepared to process a SEGV. If we implement the + SEGV handler, there's no reason to to check malloc return; it is + a waste of code. */ + + cpi->FragmentLastQ = + _ogg_malloc(cpi->pb.UnitFragments* + sizeof(*cpi->FragmentLastQ)); + cpi->FragTokens = + _ogg_malloc(cpi->pb.UnitFragments* + sizeof(*cpi->FragTokens)); + cpi->OriginalDC = + _ogg_malloc(cpi->pb.UnitFragments* + sizeof(*cpi->OriginalDC)); + cpi->FragTokenCounts = + _ogg_malloc(cpi->pb.UnitFragments* + sizeof(*cpi->FragTokenCounts)); + cpi->RunHuffIndices = + _ogg_malloc(cpi->pb.UnitFragments* + sizeof(*cpi->RunHuffIndices)); + cpi->LastCodedErrorScore = + _ogg_malloc(cpi->pb.UnitFragments* + sizeof(*cpi->LastCodedErrorScore)); + cpi->BlockCodedFlags = + _ogg_malloc(cpi->pb.UnitFragments* + sizeof(*cpi->BlockCodedFlags)); + cpi->ModeList = + _ogg_malloc(cpi->pb.UnitFragments* + sizeof(*cpi->ModeList)); + cpi->MVList = + _ogg_malloc(cpi->pb.UnitFragments* + sizeof(*cpi->MVList)); + cpi->DCT_codes = + _ogg_malloc(64* + sizeof(*cpi->DCT_codes)); + cpi->DCTDataBuffer = + _ogg_malloc(64* + sizeof(*cpi->DCTDataBuffer)); + cpi->quantized_list = + _ogg_malloc(64* + sizeof(*cpi->quantized_list)); + cpi->PartiallyCodedFlags = + _ogg_malloc(cpi->pb.MacroBlocks* + sizeof(*cpi->PartiallyCodedFlags)); + cpi->PartiallyCodedMbPatterns = + _ogg_malloc(cpi->pb.MacroBlocks* + sizeof(*cpi->PartiallyCodedMbPatterns)); + cpi->UncodedMbFlags = + _ogg_malloc(cpi->pb.MacroBlocks* + sizeof(*cpi->UncodedMbFlags)); + +} + +static void EClearFrameInfo(CP_INSTANCE * cpi) { + if(cpi->ConvDestBuffer ) + _ogg_free(cpi->ConvDestBuffer ); + cpi->ConvDestBuffer = 0; + + if(cpi->yuv0ptr) + _ogg_free(cpi->yuv0ptr); + cpi->yuv0ptr = 0; + + if(cpi->yuv1ptr) + _ogg_free(cpi->yuv1ptr); + cpi->yuv1ptr = 0; + + if(cpi->OptimisedTokenListEb ) + _ogg_free(cpi->OptimisedTokenListEb); + cpi->OptimisedTokenListEb = 0; + + if(cpi->OptimisedTokenList ) + _ogg_free(cpi->OptimisedTokenList); + cpi->OptimisedTokenList = 0; + + if(cpi->OptimisedTokenListHi ) + _ogg_free(cpi->OptimisedTokenListHi); + cpi->OptimisedTokenListHi = 0; + + if(cpi->OptimisedTokenListPl ) + _ogg_free(cpi->OptimisedTokenListPl); + cpi->OptimisedTokenListPl = 0; + +} + +static void EInitFrameInfo(CP_INSTANCE * cpi){ + int FrameSize = cpi->pb.ReconYPlaneSize + 2 * cpi->pb.ReconUVPlaneSize; + + /* clear any existing info */ + EClearFrameInfo(cpi); + + /* allocate frames */ + cpi->ConvDestBuffer = + _ogg_malloc(FrameSize* + sizeof(*cpi->ConvDestBuffer)); + cpi->yuv0ptr = + _ogg_malloc(FrameSize* + sizeof(*cpi->yuv0ptr)); + cpi->yuv1ptr = + _ogg_malloc(FrameSize* + sizeof(*cpi->yuv1ptr)); + cpi->OptimisedTokenListEb = + _ogg_malloc(FrameSize* + sizeof(*cpi->OptimisedTokenListEb)); + cpi->OptimisedTokenList = + _ogg_malloc(FrameSize* + sizeof(*cpi->OptimisedTokenList)); + cpi->OptimisedTokenListHi = + _ogg_malloc(FrameSize* + sizeof(*cpi->OptimisedTokenListHi)); + cpi->OptimisedTokenListPl = + _ogg_malloc(FrameSize* + sizeof(*cpi->OptimisedTokenListPl)); +} + +static void SetupKeyFrame(CP_INSTANCE *cpi) { + /* Make sure the "last frame" buffer contains the first frame data + as well. */ + memcpy ( cpi->yuv0ptr, cpi->yuv1ptr, + cpi->pb.ReconYPlaneSize + 2 * cpi->pb.ReconUVPlaneSize ); + + /* Initialise the cpi->pb.display_fragments and other fragment + structures for the first frame. */ + memset( cpi->pb.display_fragments, 1, cpi->pb.UnitFragments ); + memset( cpi->extra_fragments, 1, cpi->pb.UnitFragments ); + + /* Set up for a KEY FRAME */ + cpi->pb.FrameType = KEY_FRAME; +} + +static void AdjustKeyFrameContext(CP_INSTANCE *cpi) { + ogg_uint32_t i; + ogg_uint32_t AvKeyFrameFrequency = + (ogg_uint32_t) (cpi->CurrentFrame / cpi->KeyFrameCount); + ogg_uint32_t AvKeyFrameBytes = + (ogg_uint32_t) (cpi->TotKeyFrameBytes / cpi->KeyFrameCount); + ogg_uint32_t TotalWeight=0; + ogg_int32_t AvKeyFramesPerSecond; + ogg_int32_t MinFrameTargetRate; + + /* Update the frame carry over. */ + cpi->TotKeyFrameBytes += oggpackB_bytes(cpi->oggbuffer); + + /* reset keyframe context and calculate weighted average of last + KEY_FRAME_CONTEXT keyframes */ + for( i = 0 ; i < KEY_FRAME_CONTEXT ; i ++ ) { + if ( i < KEY_FRAME_CONTEXT -1) { + cpi->PriorKeyFrameSize[i] = cpi->PriorKeyFrameSize[i+1]; + cpi->PriorKeyFrameDistance[i] = cpi->PriorKeyFrameDistance[i+1]; + } else { + cpi->PriorKeyFrameSize[KEY_FRAME_CONTEXT - 1] = + oggpackB_bytes(cpi->oggbuffer); + cpi->PriorKeyFrameDistance[KEY_FRAME_CONTEXT - 1] = + cpi->LastKeyFrame; + } + + AvKeyFrameBytes += PriorKeyFrameWeight[i] * + cpi->PriorKeyFrameSize[i]; + AvKeyFrameFrequency += PriorKeyFrameWeight[i] * + cpi->PriorKeyFrameDistance[i]; + TotalWeight += PriorKeyFrameWeight[i]; + } + AvKeyFrameBytes /= TotalWeight; + AvKeyFrameFrequency /= TotalWeight; + AvKeyFramesPerSecond = 100 * cpi->Configuration.OutputFrameRate / + AvKeyFrameFrequency ; + + /* Calculate a new target rate per frame allowing for average key + frame frequency over newest frames . */ + if ( 100 * cpi->Configuration.TargetBandwidth > + AvKeyFrameBytes * AvKeyFramesPerSecond && + (100 * cpi->Configuration.OutputFrameRate - AvKeyFramesPerSecond )){ + cpi->frame_target_rate = + (ogg_int32_t)(100* cpi->Configuration.TargetBandwidth - + AvKeyFrameBytes * AvKeyFramesPerSecond ) / + ( (100 * cpi->Configuration.OutputFrameRate - AvKeyFramesPerSecond ) ); + } else { + /* don't let this number get too small!!! */ + cpi->frame_target_rate = 1; + } + + /* minimum allowable frame_target_rate */ + MinFrameTargetRate = (cpi->Configuration.TargetBandwidth / + cpi->Configuration.OutputFrameRate) / 3; + + if(cpi->frame_target_rate < MinFrameTargetRate ) { + cpi->frame_target_rate = MinFrameTargetRate; + } + + cpi->LastKeyFrame = 1; + cpi->LastKeyFrameSize=oggpackB_bytes(cpi->oggbuffer); + +} + +static void UpdateFrame(CP_INSTANCE *cpi){ + + double CorrectionFactor; + + /* Reset the DC predictors. */ + cpi->pb.LastIntraDC = 0; + cpi->pb.InvLastIntraDC = 0; + cpi->pb.LastInterDC = 0; + cpi->pb.InvLastInterDC = 0; + + /* Initialise bit packing mechanism. */ + oggpackB_reset(cpi->oggbuffer); + + /* mark as video frame */ + oggpackB_write(cpi->oggbuffer,0,1); + + /* Write out the frame header information including size. */ + WriteFrameHeader(cpi); + + /* Copy back any extra frags that are to be updated by the codec + as part of the background cleanup task */ + CopyBackExtraFrags(cpi); + + /* Encode the data. */ + EncodeData(cpi); + + /* Adjust drop frame trigger. */ + if ( cpi->pb.FrameType != KEY_FRAME ) { + /* Apply decay factor then add in the last frame size. */ + cpi->DropFrameTriggerBytes = + ((cpi->DropFrameTriggerBytes * (DF_CANDIDATE_WINDOW-1)) / + DF_CANDIDATE_WINDOW) + oggpackB_bytes(cpi->oggbuffer); + }else{ + /* Increase cpi->DropFrameTriggerBytes a little. Just after a key + frame may actually be a good time to drop a frame. */ + cpi->DropFrameTriggerBytes = + (cpi->DropFrameTriggerBytes * DF_CANDIDATE_WINDOW) / + (DF_CANDIDATE_WINDOW-1); + } + + /* Test for overshoot which may require a dropped frame next time + around. If we are already in a drop frame condition but the + previous frame was not dropped then the threshold for continuing + to allow dropped frames is reduced. */ + if ( cpi->DropFrameCandidate ) { + if ( cpi->DropFrameTriggerBytes > + (cpi->frame_target_rate * (DF_CANDIDATE_WINDOW+1)) ) + cpi->DropFrameCandidate = 1; + else + cpi->DropFrameCandidate = 0; + } else { + if ( cpi->DropFrameTriggerBytes > + (cpi->frame_target_rate * ((DF_CANDIDATE_WINDOW*2)-2)) ) + cpi->DropFrameCandidate = 1; + else + cpi->DropFrameCandidate = 0; + } + + /* Update the BpbCorrectionFactor variable according to whether or + not we were close enough with our selection of DCT quantiser. */ + if ( cpi->pb.FrameType != KEY_FRAME ) { + /* Work out a size correction factor. */ + CorrectionFactor = (double)oggpackB_bytes(cpi->oggbuffer) / + (double)cpi->ThisFrameTargetBytes; + + if ( (CorrectionFactor > 1.05) && + (cpi->pb.ThisFrameQualityValue < + cpi->pb.QThreshTable[cpi->Configuration.ActiveMaxQ]) ) { + CorrectionFactor = 1.0 + ((CorrectionFactor - 1.0)/2); + if ( CorrectionFactor > 1.5 ) + cpi->BpbCorrectionFactor *= 1.5; + else + cpi->BpbCorrectionFactor *= CorrectionFactor; + + /* Keep BpbCorrectionFactor within limits */ + if ( cpi->BpbCorrectionFactor > MAX_BPB_FACTOR ) + cpi->BpbCorrectionFactor = MAX_BPB_FACTOR; + } else if ( (CorrectionFactor < 0.95) && + (cpi->pb.ThisFrameQualityValue > VERY_BEST_Q) ){ + CorrectionFactor = 1.0 - ((1.0 - CorrectionFactor)/2); + if ( CorrectionFactor < 0.75 ) + cpi->BpbCorrectionFactor *= 0.75; + else + cpi->BpbCorrectionFactor *= CorrectionFactor; + + /* Keep BpbCorrectionFactor within limits */ + if ( cpi->BpbCorrectionFactor < MIN_BPB_FACTOR ) + cpi->BpbCorrectionFactor = MIN_BPB_FACTOR; + } + } + + /* Adjust carry over and or key frame context. */ + if ( cpi->pb.FrameType == KEY_FRAME ) { + /* Adjust the key frame context unless the key frame was very small */ + AdjustKeyFrameContext(cpi); + } else { + /* Update the frame carry over */ + cpi->CarryOver += ((ogg_int32_t)cpi->frame_target_rate - + (ogg_int32_t)oggpackB_bytes(cpi->oggbuffer)); + } + cpi->TotalByteCount += oggpackB_bytes(cpi->oggbuffer); +} + +static void CompressFirstFrame(CP_INSTANCE *cpi) { + ogg_uint32_t i; + + /* set up context of key frame sizes and distances for more local + datarate control */ + for( i = 0 ; i < KEY_FRAME_CONTEXT ; i ++ ) { + cpi->PriorKeyFrameSize[i] = cpi->Configuration.KeyFrameDataTarget; + cpi->PriorKeyFrameDistance[i] = cpi->pb.info.keyframe_frequency_force; + } + + /* Keep track of the total number of Key Frames Coded. */ + cpi->KeyFrameCount = 1; + cpi->LastKeyFrame = 1; + cpi->TotKeyFrameBytes = 0; + + /* A key frame is not a dropped frame there for reset the count of + consequative dropped frames. */ + cpi->DropCount = 0; + + SetupKeyFrame(cpi); + + /* Calculate a new target rate per frame allowing for average key + frame frequency and size thus far. */ + if ( cpi->Configuration.TargetBandwidth > + ((cpi->Configuration.KeyFrameDataTarget * + cpi->Configuration.OutputFrameRate)/ + cpi->pb.info.keyframe_frequency) ) { + + cpi->frame_target_rate = + (ogg_int32_t)((cpi->Configuration.TargetBandwidth - + ((cpi->Configuration.KeyFrameDataTarget * + cpi->Configuration.OutputFrameRate)/ + cpi->pb.info.keyframe_frequency)) / + cpi->Configuration.OutputFrameRate); + }else + cpi->frame_target_rate = 1; + + /* Set baseline frame target rate. */ + cpi->BaseLineFrameTargetRate = cpi->frame_target_rate; + + /* A key frame is not a dropped frame there for reset the count of + consequative dropped frames. */ + cpi->DropCount = 0; + + /* Initialise drop frame trigger to 5 frames worth of data. */ + cpi->DropFrameTriggerBytes = cpi->frame_target_rate * DF_CANDIDATE_WINDOW; + + /* Set a target size for this key frame based upon the baseline + target and frequency */ + cpi->ThisFrameTargetBytes = cpi->Configuration.KeyFrameDataTarget; + + /* Get a DCT quantizer level for the key frame. */ + cpi->MotionScore = cpi->pb.UnitFragments; + + RegulateQ(cpi, cpi->pb.UnitFragments); + + cpi->pb.LastFrameQualityValue = cpi->pb.ThisFrameQualityValue; + + /* Initialise quantizer. */ + UpdateQC(cpi, cpi->pb.ThisFrameQualityValue ); + + /* Initialise the cpi->pb.display_fragments and other fragment + structures for the first frame. */ + for ( i = 0; i < cpi->pb.UnitFragments; i ++ ) + cpi->FragmentLastQ[i] = cpi->pb.ThisFrameQualityValue; + + /* Compress and output the frist frame. */ + PickIntra( cpi, + cpi->pb.YSBRows, cpi->pb.YSBCols); + UpdateFrame(cpi); + + /* Initialise the carry over rate targeting variables. */ + cpi->CarryOver = 0; + +} + +static void CompressKeyFrame(CP_INSTANCE *cpi){ + ogg_uint32_t i; + + /* Before we compress reset the carry over to the actual frame carry over */ + cpi->CarryOver = cpi->Configuration.TargetBandwidth * cpi->CurrentFrame / + cpi->Configuration.OutputFrameRate - cpi->TotalByteCount; + + /* Keep track of the total number of Key Frames Coded */ + cpi->KeyFrameCount += 1; + + /* A key frame is not a dropped frame there for reset the count of + consequative dropped frames. */ + cpi->DropCount = 0; + + SetupKeyFrame(cpi); + + /* set a target size for this frame */ + cpi->ThisFrameTargetBytes = (ogg_int32_t) cpi->frame_target_rate + + ( (cpi->Configuration.KeyFrameDataTarget - cpi->frame_target_rate) * + cpi->LastKeyFrame / cpi->pb.info.keyframe_frequency_force ); + + if ( cpi->ThisFrameTargetBytes > cpi->Configuration.KeyFrameDataTarget ) + cpi->ThisFrameTargetBytes = cpi->Configuration.KeyFrameDataTarget; + + /* Get a DCT quantizer level for the key frame. */ + cpi->MotionScore = cpi->pb.UnitFragments; + + RegulateQ(cpi, cpi->pb.UnitFragments); + + cpi->pb.LastFrameQualityValue = cpi->pb.ThisFrameQualityValue; + + /* Initialise DCT tables. */ + UpdateQC(cpi, cpi->pb.ThisFrameQualityValue ); + + /* Initialise the cpi->pb.display_fragments and other fragment + structures for the first frame. */ + for ( i = 0; i < cpi->pb.UnitFragments; i ++ ) + cpi->FragmentLastQ[i] = cpi->pb.ThisFrameQualityValue; + + + /* Compress and output the frist frame. */ + PickIntra( cpi, + cpi->pb.YSBRows, cpi->pb.YSBCols); + UpdateFrame(cpi); + +} + +static void CompressFrame( CP_INSTANCE *cpi) { + ogg_int32_t min_blocks_per_frame; + ogg_uint32_t i; + int DropFrame = 0; + ogg_uint32_t ResidueBlocksAdded=0; + ogg_uint32_t KFIndicator = 0; + + double QModStep; + double QModifier = 1.0; + + /* Clear down the macro block level mode and MV arrays. */ + for ( i = 0; i < cpi->pb.UnitFragments; i++ ) { + cpi->pb.FragCodingMethod[i] = CODE_INTER_NO_MV; /* Default coding mode */ + cpi->pb.FragMVect[i].x = 0; + cpi->pb.FragMVect[i].y = 0; + } + + /* Default to delta frames. */ + cpi->pb.FrameType = DELTA_FRAME; + + /* Clear down the difference arrays for the current frame. */ + memset( cpi->pb.display_fragments, 0, cpi->pb.UnitFragments ); + memset( cpi->extra_fragments, 0, cpi->pb.UnitFragments ); + + /* Calculate the target bytes for this frame. */ + cpi->ThisFrameTargetBytes = cpi->frame_target_rate; + + /* Correct target to try and compensate for any overall rate error + that is developing */ + + /* Set the max allowed Q for this frame based upon carry over + history. First set baseline worst Q for this frame */ + cpi->Configuration.ActiveMaxQ = cpi->Configuration.MaxQ + 10; + if ( cpi->Configuration.ActiveMaxQ >= Q_TABLE_SIZE ) + cpi->Configuration.ActiveMaxQ = Q_TABLE_SIZE - 1; + + /* Make a further adjustment based upon the carry over and recent + history.. cpi->Configuration.ActiveMaxQ reduced by 1 for each 1/2 + seconds worth of -ve carry over up to a limit of 6. Also + cpi->Configuration.ActiveMaxQ reduced if frame is a + "DropFrameCandidate". Remember that if we are behind the bit + target carry over is -ve. */ + if ( cpi->CarryOver < 0 ) { + if ( cpi->DropFrameCandidate ) { + cpi->Configuration.ActiveMaxQ -= 4; + } + + if ( cpi->CarryOver < + -((ogg_int32_t)cpi->Configuration.TargetBandwidth*3) ) + cpi->Configuration.ActiveMaxQ -= 6; + else + cpi->Configuration.ActiveMaxQ += + (ogg_int32_t) ((cpi->CarryOver*2) / + (ogg_int32_t)cpi->Configuration.TargetBandwidth); + + /* Check that we have not dropped quality too far */ + if ( cpi->Configuration.ActiveMaxQ < cpi->Configuration.MaxQ ) + cpi->Configuration.ActiveMaxQ = cpi->Configuration.MaxQ; + } + + /* Calculate the Q Modifier step size required to cause a step down + from full target bandwidth to 40% of target between max Q and + best Q */ + QModStep = 0.5 / (double)((Q_TABLE_SIZE - 1) - + cpi->Configuration.ActiveMaxQ); + + /* Set up the cpi->QTargetModifier[] table. */ + for ( i = 0; i < cpi->Configuration.ActiveMaxQ; i++ ) { + cpi->QTargetModifier[i] = QModifier; + } + for ( i = cpi->Configuration.ActiveMaxQ; i < Q_TABLE_SIZE; i++ ) { + cpi->QTargetModifier[i] = QModifier; + QModifier -= QModStep; + } + + /* if we are allowed to drop frames and are falling behind (eg more + than x frames worth of bandwidth) */ + if ( cpi->pb.info.dropframes_p && + ( cpi->DropCount < cpi->MaxConsDroppedFrames) && + ( cpi->CarryOver < + -((ogg_int32_t)cpi->Configuration.TargetBandwidth)) && + ( cpi->DropFrameCandidate) ) { + /* (we didn't do this frame so we should have some left over for + the next frame) */ + cpi->CarryOver += cpi->frame_target_rate; + DropFrame = 1; + cpi->DropCount ++; + + /* Adjust DropFrameTriggerBytes to account for the saving achieved. */ + cpi->DropFrameTriggerBytes = + (cpi->DropFrameTriggerBytes * + (DF_CANDIDATE_WINDOW-1))/DF_CANDIDATE_WINDOW; + + /* Even if we drop a frame we should account for it when + considering key frame seperation. */ + cpi->LastKeyFrame++; + } else if ( cpi->CarryOver < + -((ogg_int32_t)cpi->Configuration.TargetBandwidth * 2) ) { + /* Reduce frame bit target by 1.75% for each 1/10th of a seconds + worth of -ve carry over down to a minimum of 65% of its + un-modified value. */ + + cpi->ThisFrameTargetBytes = + (ogg_uint32_t)(cpi->ThisFrameTargetBytes * 0.65); + } else if ( cpi->CarryOver < 0 ) { + /* Note that cpi->CarryOver is a -ve here hence 1.0 "+" ... */ + cpi->ThisFrameTargetBytes = + (ogg_uint32_t)(cpi->ThisFrameTargetBytes * + (1.0 + ( ((cpi->CarryOver * 10)/ + ((ogg_int32_t)cpi-> + Configuration.TargetBandwidth)) * 0.0175) )); + } + + if ( !DropFrame ) { + /* pick all the macroblock modes and motion vectors */ + ogg_uint32_t InterError; + ogg_uint32_t IntraError; + + + /* Set Baseline filter level. */ + ConfigurePP( &cpi->pp, cpi->pb.info.noise_sensitivity); + + /* Score / analyses the fragments. */ + cpi->MotionScore = YUVAnalyseFrame(&cpi->pp, &KFIndicator ); + + /* Get the baseline Q value */ + RegulateQ( cpi, cpi->MotionScore ); + + /* Recode blocks if the error score in last frame was high. */ + ResidueBlocksAdded = 0; + for ( i = 0; i < cpi->pb.UnitFragments; i++ ){ + if ( !cpi->pb.display_fragments[i] ){ + if ( cpi->LastCodedErrorScore[i] >= + ResidueErrorThresh[cpi->pb.FrameQIndex] ) { + cpi->pb.display_fragments[i] = 1; /* Force block update */ + cpi->extra_fragments[i] = 1; /* Insures up to date + pixel data is used. */ + ResidueBlocksAdded ++; + } + } + } + + /* Adjust the motion score to allow for residue blocks + added. These are assumed to have below average impact on + bitrate (Hence ResidueBlockFactor). */ + cpi->MotionScore = cpi->MotionScore + + (ResidueBlocksAdded / ResidueBlockFactor[cpi->pb.FrameQIndex]); + + /* Estimate the min number of blocks at best Q */ + min_blocks_per_frame = + (ogg_int32_t)(cpi->ThisFrameTargetBytes / + GetEstimatedBpb( cpi, VERY_BEST_Q )); + if ( min_blocks_per_frame == 0 ) + min_blocks_per_frame = 1; + + /* If we have less than this number then consider adding in some + extra blocks */ + if ( cpi->MotionScore < min_blocks_per_frame ) { + min_blocks_per_frame = + cpi->MotionScore + + (ogg_int32_t)(((min_blocks_per_frame - cpi->MotionScore) * 4) / 3 ); + UpRegulateDataStream( cpi, VERY_BEST_Q, min_blocks_per_frame ); + }else{ + /* Reset control variable for best quality final pass. */ + cpi->FinalPassLastPos = 0; + } + + /* Get the modified Q prediction taking into account extra blocks added. */ + RegulateQ( cpi, cpi->MotionScore ); + + /* Unless we are already well ahead (4 seconds of data) of the + projected bitrate */ + if ( cpi->CarryOver < + (ogg_int32_t)(cpi->Configuration.TargetBandwidth * 4) ){ + /* Look at the predicted Q (pbi->FrameQIndex). Adjust the + target bits for this frame based upon projected Q and + re-calculate. The idea is that if the Q is better than a + given (good enough) level then we will try and save some bits + for use in more difficult segments. */ + cpi->ThisFrameTargetBytes = + (ogg_int32_t) (cpi->ThisFrameTargetBytes * + cpi->QTargetModifier[cpi->pb.FrameQIndex]); + + /* Recalculate Q again */ + RegulateQ( cpi, cpi->MotionScore ); + } + + + /* Select modes and motion vectors for each of the blocks : return + an error score for inter and intra */ + PickModes( cpi, cpi->pb.YSBRows, cpi->pb.YSBCols, + cpi->pb.info.width, + &InterError, &IntraError ); + + /* decide whether we really should have made this frame a key frame */ + /* forcing out a keyframe if the max interval is up is done at a higher level */ + if( cpi->pb.info.keyframe_auto_p){ + if( ( 2* IntraError < 5 * InterError ) + && ( KFIndicator >= (ogg_uint32_t) + cpi->pb.info.keyframe_auto_threshold) + && ( cpi->LastKeyFrame > cpi->pb.info.keyframe_mindistance) + ){ + CompressKeyFrame(cpi); /* Code a key frame */ + return; + } + + } + + /* Increment the frames since last key frame count */ + cpi->LastKeyFrame++; + + /* Proceed with the frame update. */ + UpdateFrame(cpi); + cpi->DropCount = 0; + + if ( cpi->MotionScore > 0 ){ + /* Note the Quantizer used for each block coded. */ + for ( i = 0; i < cpi->pb.UnitFragments; i++ ){ + if ( cpi->pb.display_fragments[i] ){ + cpi->FragmentLastQ[i] = cpi->pb.ThisFrameQualityValue; + } + } + + } + }else{ + /* even if we 'drop' a frame, a placeholder must be written as we + currently assume fixed frame rate timebase as Ogg mapping + invariant */ + UpdateFrame(cpi); + } +} + +/********************** The toplevel: encode ***********************/ + +static int _ilog(unsigned int v){ + int ret=0; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +static void theora_encode_dispatch_init(CP_INSTANCE *cpi); + +int theora_encode_init(theora_state *th, theora_info *c){ + int i; + + CP_INSTANCE *cpi; + + memset(th, 0, sizeof(*th)); + /*Currently only the 4:2:0 format is supported.*/ + if(c->pixelformat!=OC_PF_420)return OC_IMPL; + th->internal_encode=cpi=_ogg_calloc(1,sizeof(*cpi)); + theora_encode_dispatch_init(cpi); + + dsp_static_init (&cpi->dsp); + memcpy (&cpi->pb.dsp, &cpi->dsp, sizeof(DspFunctions)); + + c->version_major=TH_VERSION_MAJOR; + c->version_minor=TH_VERSION_MINOR; + c->version_subminor=TH_VERSION_SUB; + + InitTmpBuffers(&cpi->pb); + InitPPInstance(&cpi->pp, &cpi->dsp); + + /* Initialise Configuration structure to legal values */ + if(c->quality>63)c->quality=63; + if(c->quality<0)c->quality=32; + if(c->target_bitrate<0)c->target_bitrate=0; + /* we clamp target_bitrate to 24 bits after setting up the encoder */ + + cpi->Configuration.BaseQ = c->quality; + cpi->Configuration.FirstFrameQ = c->quality; + cpi->Configuration.MaxQ = c->quality; + cpi->Configuration.ActiveMaxQ = c->quality; + + cpi->MVChangeFactor = 14; + cpi->FourMvChangeFactor = 8; + cpi->MinImprovementForNewMV = 25; + cpi->ExhaustiveSearchThresh = 2500; + cpi->MinImprovementForFourMV = 100; + cpi->FourMVThreshold = 10000; + cpi->BitRateCapFactor = 1.5; + cpi->InterTripOutThresh = 5000; + cpi->MVEnabled = 1; + cpi->InterCodeCount = 127; + cpi->BpbCorrectionFactor = 1.0; + cpi->GoldenFrameEnabled = 1; + cpi->InterPrediction = 1; + cpi->MotionCompensation = 1; + cpi->ThreshMapThreshold = 5; + cpi->MaxConsDroppedFrames = 1; + + /* Set encoder flags. */ + /* if not AutoKeyframing cpi->ForceKeyFrameEvery = is frequency */ + if(!c->keyframe_auto_p) + c->keyframe_frequency_force = c->keyframe_frequency; + + /* Set the frame rate variables. */ + if ( c->fps_numerator < 1 ) + c->fps_numerator = 1; + if ( c->fps_denominator < 1 ) + c->fps_denominator = 1; + + /* don't go too nuts on keyframe spacing; impose a high limit to + make certain the granulepos encoding strategy works */ + if(c->keyframe_frequency_force>32768)c->keyframe_frequency_force=32768; + if(c->keyframe_mindistance>32768)c->keyframe_mindistance=32768; + if(c->keyframe_mindistance>c->keyframe_frequency_force) + c->keyframe_mindistance=c->keyframe_frequency_force; + cpi->pb.keyframe_granule_shift=_ilog(c->keyframe_frequency_force-1); + + /* clamp the target_bitrate to a maximum of 24 bits so we get a + more meaningful value when we write this out in the header. */ + if(c->target_bitrate>(1<<24)-1)c->target_bitrate=(1<<24)-1; + + /* copy in config */ + memcpy(&cpi->pb.info,c,sizeof(*c)); + th->i=&cpi->pb.info; + th->granulepos=-1; + + /* Set up default values for QTargetModifier[Q_TABLE_SIZE] table */ + for ( i = 0; i < Q_TABLE_SIZE; i++ ) + cpi->QTargetModifier[i] = 1.0; + + /* Set up an encode buffer */ + cpi->oggbuffer = _ogg_malloc(sizeof(oggpack_buffer)); + oggpackB_writeinit(cpi->oggbuffer); + + /* Set data rate related variables. */ + cpi->Configuration.TargetBandwidth = (c->target_bitrate) / 8; + + cpi->Configuration.OutputFrameRate = + (double)( c->fps_numerator / + c->fps_denominator ); + + cpi->frame_target_rate = cpi->Configuration.TargetBandwidth / + cpi->Configuration.OutputFrameRate; + + /* Set key frame data rate target; this is nominal keyframe size */ + cpi->Configuration.KeyFrameDataTarget = (c->keyframe_data_target_bitrate * + c->fps_denominator / + c->fps_numerator ) / 8; + + /* Note the height and width in the pre-processor control structure. */ + cpi->ScanConfig.VideoFrameHeight = cpi->pb.info.height; + cpi->ScanConfig.VideoFrameWidth = cpi->pb.info.width; + + InitFrameDetails(&cpi->pb); + EInitFragmentInfo(cpi); + EInitFrameInfo(cpi); + + /* Set up pre-processor config pointers. */ + cpi->ScanConfig.Yuv0ptr = cpi->yuv0ptr; + cpi->ScanConfig.Yuv1ptr = cpi->yuv1ptr; + cpi->ScanConfig.SrfWorkSpcPtr = cpi->ConvDestBuffer; + cpi->ScanConfig.disp_fragments = cpi->pb.display_fragments; + cpi->ScanConfig.RegionIndex = cpi->pb.pixel_index_table; + + /* Initialise the pre-processor module. */ + ScanYUVInit(&cpi->pp, &(cpi->ScanConfig)); + + /* Initialise Motion compensation */ + InitMotionCompensation(cpi); + + /* Initialise the compression process. */ + /* We always start at frame 1 */ + cpi->CurrentFrame = 1; + + /* Reset the rate targeting correction factor. */ + cpi->BpbCorrectionFactor = 1.0; + + cpi->TotalByteCount = 0; + cpi->TotalMotionScore = 0; + + /* Up regulation variables. */ + cpi->FinalPassLastPos = 0; /* Used to regulate a final unrestricted pass. */ + cpi->LastEndSB = 0; /* Where we were in the loop last time. */ + cpi->ResidueLastEndSB = 0; /* Where we were in the residue update + loop last time. */ + + InitHuffmanSet(&cpi->pb); + + /* This makes sure encoder version specific tables are initialised */ + memcpy(&cpi->pb.quant_info, &TH_VP31_QUANT_INFO, sizeof(th_quant_info)); + InitQTables(&cpi->pb); + + /* Indicate that the next frame to be compressed is the first in the + current clip. */ + cpi->ThisIsFirstFrame = 1; + cpi->readyflag = 1; + + cpi->pb.HeadersWritten = 0; + /*We overload this flag to track header output.*/ + cpi->doneflag=-3; + + return 0; +} + +int theora_encode_YUVin(theora_state *t, + yuv_buffer *yuv){ + ogg_int32_t i; + unsigned char *LocalDataPtr; + unsigned char *InputDataPtr; + CP_INSTANCE *cpi=(CP_INSTANCE *)(t->internal_encode); + + if(!cpi->readyflag)return OC_EINVAL; + if(cpi->doneflag>0)return OC_EINVAL; + + /* If frame size has changed, abort out for now */ + if (yuv->y_height != (int)cpi->pb.info.height || + yuv->y_width != (int)cpi->pb.info.width ) + return(-1); + + + /* Copy over input YUV to internal YUV buffers. */ + /* we invert the image for backward compatibility with VP3 */ + /* First copy over the Y data */ + LocalDataPtr = cpi->yuv1ptr + yuv->y_width*(yuv->y_height - 1); + InputDataPtr = yuv->y; + for ( i = 0; i < yuv->y_height; i++ ){ + memcpy( LocalDataPtr, InputDataPtr, yuv->y_width ); + LocalDataPtr -= yuv->y_width; + InputDataPtr += yuv->y_stride; + } + + /* Now copy over the U data */ + LocalDataPtr = &cpi->yuv1ptr[(yuv->y_height * yuv->y_width)]; + LocalDataPtr += yuv->uv_width*(yuv->uv_height - 1); + InputDataPtr = yuv->u; + for ( i = 0; i < yuv->uv_height; i++ ){ + memcpy( LocalDataPtr, InputDataPtr, yuv->uv_width ); + LocalDataPtr -= yuv->uv_width; + InputDataPtr += yuv->uv_stride; + } + + /* Now copy over the V data */ + LocalDataPtr = + &cpi->yuv1ptr[((yuv->y_height*yuv->y_width)*5)/4]; + LocalDataPtr += yuv->uv_width*(yuv->uv_height - 1); + InputDataPtr = yuv->v; + for ( i = 0; i < yuv->uv_height; i++ ){ + memcpy( LocalDataPtr, InputDataPtr, yuv->uv_width ); + LocalDataPtr -= yuv->uv_width; + InputDataPtr += yuv->uv_stride; + } + + /* Special case for first frame */ + if ( cpi->ThisIsFirstFrame ){ + CompressFirstFrame(cpi); + cpi->ThisIsFirstFrame = 0; + cpi->ThisIsKeyFrame = 0; + } else { + + /* don't allow generating invalid files that overflow the p-frame + shift, even if keyframe_auto_p is turned off */ + if(cpi->LastKeyFrame >= (ogg_uint32_t) + cpi->pb.info.keyframe_frequency_force) + cpi->ThisIsKeyFrame = 1; + + if ( cpi->ThisIsKeyFrame ) { + CompressKeyFrame(cpi); + cpi->ThisIsKeyFrame = 0; + } else { + /* Compress the frame. */ + CompressFrame( cpi ); + } + + } + + /* Update stats variables. */ + cpi->LastFrameSize = oggpackB_bytes(cpi->oggbuffer); + cpi->CurrentFrame++; + cpi->packetflag=1; + + t->granulepos= + ((cpi->CurrentFrame - cpi->LastKeyFrame)<pb.keyframe_granule_shift)+ + cpi->LastKeyFrame - 1; + + return 0; +} + +int theora_encode_packetout( theora_state *t, int last_p, ogg_packet *op){ + CP_INSTANCE *cpi=(CP_INSTANCE *)(t->internal_encode); + long bytes=oggpackB_bytes(cpi->oggbuffer); + + if(!bytes)return(0); + if(!cpi->packetflag)return(0); + if(cpi->doneflag>0)return(-1); + + op->packet=oggpackB_get_buffer(cpi->oggbuffer); + op->bytes=bytes; + op->b_o_s=0; + op->e_o_s=last_p; + + op->packetno=cpi->CurrentFrame; + op->granulepos=t->granulepos; + + cpi->packetflag=0; + if(last_p)cpi->doneflag=1; + + return 1; +} + +static void _tp_writebuffer(oggpack_buffer *opb, const char *buf, const long len) +{ + long i; + + for (i = 0; i < len; i++) + oggpackB_write(opb, *buf++, 8); +} + +static void _tp_writelsbint(oggpack_buffer *opb, long value) +{ + oggpackB_write(opb, value&0xFF, 8); + oggpackB_write(opb, value>>8&0xFF, 8); + oggpackB_write(opb, value>>16&0xFF, 8); + oggpackB_write(opb, value>>24&0xFF, 8); +} + +/* build the initial short header for stream recognition and format */ +int theora_encode_header(theora_state *t, ogg_packet *op){ + CP_INSTANCE *cpi=(CP_INSTANCE *)(t->internal_encode); + int offset_y; + + oggpackB_reset(cpi->oggbuffer); + oggpackB_write(cpi->oggbuffer,0x80,8); + _tp_writebuffer(cpi->oggbuffer, "theora", 6); + + oggpackB_write(cpi->oggbuffer,TH_VERSION_MAJOR,8); + oggpackB_write(cpi->oggbuffer,TH_VERSION_MINOR,8); + oggpackB_write(cpi->oggbuffer,TH_VERSION_SUB,8); + + oggpackB_write(cpi->oggbuffer,cpi->pb.info.width>>4,16); + oggpackB_write(cpi->oggbuffer,cpi->pb.info.height>>4,16); + oggpackB_write(cpi->oggbuffer,cpi->pb.info.frame_width,24); + oggpackB_write(cpi->oggbuffer,cpi->pb.info.frame_height,24); + oggpackB_write(cpi->oggbuffer,cpi->pb.info.offset_x,8); + /* Applications use offset_y to mean offset from the top of the image; the + * meaning in the bitstream is the opposite (from the bottom). Transform. + */ + offset_y = cpi->pb.info.height - cpi->pb.info.frame_height - + cpi->pb.info.offset_y; + oggpackB_write(cpi->oggbuffer,offset_y,8); + + oggpackB_write(cpi->oggbuffer,cpi->pb.info.fps_numerator,32); + oggpackB_write(cpi->oggbuffer,cpi->pb.info.fps_denominator,32); + oggpackB_write(cpi->oggbuffer,cpi->pb.info.aspect_numerator,24); + oggpackB_write(cpi->oggbuffer,cpi->pb.info.aspect_denominator,24); + + oggpackB_write(cpi->oggbuffer,cpi->pb.info.colorspace,8); + oggpackB_write(cpi->oggbuffer,cpi->pb.info.target_bitrate,24); + oggpackB_write(cpi->oggbuffer,cpi->pb.info.quality,6); + + oggpackB_write(cpi->oggbuffer,cpi->pb.keyframe_granule_shift,5); + + oggpackB_write(cpi->oggbuffer,cpi->pb.info.pixelformat,2); + + oggpackB_write(cpi->oggbuffer,0,3); /* spare config bits */ + + op->packet=oggpackB_get_buffer(cpi->oggbuffer); + op->bytes=oggpackB_bytes(cpi->oggbuffer); + + op->b_o_s=1; + op->e_o_s=0; + + op->packetno=0; + + op->granulepos=0; + cpi->packetflag=0; + + return(0); +} + +/* build the comment header packet from the passed metadata */ +int theora_encode_comment(theora_comment *tc, ogg_packet *op) +{ + const char *vendor = theora_version_string(); + const int vendor_length = strlen(vendor); + oggpack_buffer *opb; + + opb = _ogg_malloc(sizeof(oggpack_buffer)); + oggpackB_writeinit(opb); + oggpackB_write(opb, 0x81, 8); + _tp_writebuffer(opb, "theora", 6); + + _tp_writelsbint(opb, vendor_length); + _tp_writebuffer(opb, vendor, vendor_length); + + _tp_writelsbint(opb, tc->comments); + if(tc->comments){ + int i; + for(i=0;icomments;i++){ + if(tc->user_comments[i]){ + _tp_writelsbint(opb,tc->comment_lengths[i]); + _tp_writebuffer(opb,tc->user_comments[i],tc->comment_lengths[i]); + }else{ + oggpackB_write(opb,0,32); + } + } + } + op->bytes=oggpack_bytes(opb); + + /* So we're expecting the application will free this? */ + op->packet=_ogg_malloc(oggpack_bytes(opb)); + memcpy(op->packet, oggpack_get_buffer(opb), oggpack_bytes(opb)); + oggpack_writeclear(opb); + + _ogg_free(opb); + + op->b_o_s=0; + op->e_o_s=0; + + op->packetno=0; + op->granulepos=0; + + return (0); +} + +/* build the final header packet with the tables required + for decode */ +int theora_encode_tables(theora_state *t, ogg_packet *op){ + CP_INSTANCE *cpi=(CP_INSTANCE *)(t->internal_encode); + + oggpackB_reset(cpi->oggbuffer); + oggpackB_write(cpi->oggbuffer,0x82,8); + _tp_writebuffer(cpi->oggbuffer,"theora",6); + + WriteQTables(&cpi->pb,cpi->oggbuffer); + WriteHuffmanTrees(cpi->pb.HuffRoot_VP3x,cpi->oggbuffer); + + op->packet=oggpackB_get_buffer(cpi->oggbuffer); + op->bytes=oggpackB_bytes(cpi->oggbuffer); + + op->b_o_s=0; + op->e_o_s=0; + + op->packetno=0; + + op->granulepos=0; + cpi->packetflag=0; + + cpi->pb.HeadersWritten = 1; + + return(0); +} + +static void theora_encode_clear (theora_state *th){ + CP_INSTANCE *cpi; + cpi=(CP_INSTANCE *)th->internal_encode; + if(cpi){ + + ClearHuffmanSet(&cpi->pb); + ClearFragmentInfo(&cpi->pb); + ClearFrameInfo(&cpi->pb); + EClearFragmentInfo(cpi); + EClearFrameInfo(cpi); + ClearTmpBuffers(&cpi->pb); + ClearPPInstance(&cpi->pp); + + oggpackB_writeclear(cpi->oggbuffer); + _ogg_free(cpi->oggbuffer); + _ogg_free(cpi); + } + + memset(th,0,sizeof(*th)); +} + + +/* returns, in seconds, absolute time of current packet in given + logical stream */ +static double theora_encode_granule_time(theora_state *th, + ogg_int64_t granulepos){ +#ifndef THEORA_DISABLE_FLOAT + CP_INSTANCE *cpi=(CP_INSTANCE *)(th->internal_encode); + PB_INSTANCE *pbi=(PB_INSTANCE *)(th->internal_decode); + + if(cpi)pbi=&cpi->pb; + + if(granulepos>=0){ + ogg_int64_t iframe=granulepos>>pbi->keyframe_granule_shift; + ogg_int64_t pframe=granulepos-(iframe<keyframe_granule_shift); + + return (iframe+pframe)* + ((double)pbi->info.fps_denominator/pbi->info.fps_numerator); + + } +#endif + + return(-1); /* negative granulepos or float calculations disabled */ +} + +/* returns frame number of current packet in given logical stream */ +static ogg_int64_t theora_encode_granule_frame(theora_state *th, + ogg_int64_t granulepos){ + CP_INSTANCE *cpi=(CP_INSTANCE *)(th->internal_encode); + PB_INSTANCE *pbi=(PB_INSTANCE *)(th->internal_decode); + + if(cpi)pbi=&cpi->pb; + + if(granulepos>=0){ + ogg_int64_t iframe=granulepos>>pbi->keyframe_granule_shift; + ogg_int64_t pframe=granulepos-(iframe<keyframe_granule_shift); + + return (iframe+pframe-1); + } + + return(-1); +} + + +static int theora_encode_control(theora_state *th,int req, + void *buf,size_t buf_sz) { + CP_INSTANCE *cpi; + PB_INSTANCE *pbi; + int value; + + if(th == NULL) + return TH_EFAULT; + + cpi = th->internal_encode; + pbi = &cpi->pb; + + switch(req) { + case TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE: + { + ogg_uint32_t keyframe_frequency_force; + if( (buf==NULL) || (buf_sz!=sizeof(ogg_uint32_t))) return TH_EINVAL; + keyframe_frequency_force=*(ogg_uint32_t *)buf; + + keyframe_frequency_force= + OC_MINI(keyframe_frequency_force, + 1U<pb.keyframe_granule_shift); + cpi->pb.info.keyframe_frequency_force= + OC_MAXI(1,keyframe_frequency_force); + *(ogg_uint32_t *)buf=cpi->pb.info.keyframe_frequency_force; + return 0; + } + case TH_ENCCTL_SET_QUANT_PARAMS: + if( ( buf==NULL&&buf_sz!=0 ) + || ( buf!=NULL&&buf_sz!=sizeof(th_quant_info) ) + || cpi->pb.HeadersWritten ){ + return TH_EINVAL; + } + + if(buf==NULL) + memcpy(&pbi->quant_info, &TH_VP31_QUANT_INFO, sizeof(th_quant_info)); + else + memcpy(&pbi->quant_info, buf, sizeof(th_quant_info)); + InitQTables(pbi); + + return 0; + case TH_ENCCTL_SET_VP3_COMPATIBLE: + if(cpi->pb.HeadersWritten) + return TH_EINVAL; + + memcpy(&pbi->quant_info, &TH_VP31_QUANT_INFO, sizeof(th_quant_info)); + InitQTables(pbi); + + return 0; + case TH_ENCCTL_SET_SPLEVEL: + if(buf == NULL || buf_sz != sizeof(int)) + return TH_EINVAL; + + memcpy(&value, buf, sizeof(int)); + + switch(value) { + case 0: + cpi->MotionCompensation = 1; + pbi->info.quick_p = 0; + break; + + case 1: + cpi->MotionCompensation = 1; + pbi->info.quick_p = 1; + break; + + case 2: + cpi->MotionCompensation = 0; + pbi->info.quick_p = 1; + break; + + default: + return TH_EINVAL; + } + + return 0; + case TH_ENCCTL_GET_SPLEVEL_MAX: + value = 2; + memcpy(buf, &value, sizeof(int)); + return 0; + default: + return TH_EIMPL; + } +} + +static void theora_encode_dispatch_init(CP_INSTANCE *cpi){ + cpi->dispatch_vtbl.clear=theora_encode_clear; + cpi->dispatch_vtbl.control=theora_encode_control; + cpi->dispatch_vtbl.granule_frame=theora_encode_granule_frame; + cpi->dispatch_vtbl.granule_time=theora_encode_granule_time; +} diff --git a/Engine/lib/libtheora/lib/enc/frarray.c b/Engine/lib/libtheora/lib/enc/frarray.c new file mode 100644 index 000000000..51b327206 --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/frarray.c @@ -0,0 +1,243 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: frarray.c 15153 2008-08-04 18:37:55Z tterribe $ + + ********************************************************************/ + +#include +#include "codec_internal.h" +#include "block_inline.h" + +/* Long run bit string coding */ +static ogg_uint32_t FrArrayCodeSBRun( CP_INSTANCE *cpi, ogg_uint32_t value){ + ogg_uint32_t CodedVal = 0; + ogg_uint32_t CodedBits = 0; + + /* Coding scheme: + Codeword RunLength + 0 1 + 10x 2-3 + 110x 4-5 + 1110xx 6-9 + 11110xxx 10-17 + 111110xxxx 18-33 + 111111xxxxxxxxxxxx 34-4129 */ + + if ( value == 1 ){ + CodedVal = 0; + CodedBits = 1; + } else if ( value <= 3 ) { + CodedVal = 0x0004 + (value - 2); + CodedBits = 3; + } else if ( value <= 5 ) { + CodedVal = 0x000C + (value - 4); + CodedBits = 4; + } else if ( value <= 9 ) { + CodedVal = 0x0038 + (value - 6); + CodedBits = 6; + } else if ( value <= 17 ) { + CodedVal = 0x00F0 + (value - 10); + CodedBits = 8; + } else if ( value <= 33 ) { + CodedVal = 0x03E0 + (value - 18); + CodedBits = 10; + } else { + CodedVal = 0x3F000 + (value - 34); + CodedBits = 18; + } + + /* Add the bits to the encode holding buffer. */ + oggpackB_write( cpi->oggbuffer, CodedVal, CodedBits ); + + return CodedBits; +} + +/* Short run bit string coding */ +static ogg_uint32_t FrArrayCodeBlockRun( CP_INSTANCE *cpi, + ogg_uint32_t value ) { + ogg_uint32_t CodedVal = 0; + ogg_uint32_t CodedBits = 0; + + /* Coding scheme: + Codeword RunLength + 0x 1-2 + 10x 3-4 + 110x 5-6 + 1110xx 7-10 + 11110xx 11-14 + 11111xxxx 15-30 */ + + if ( value <= 2 ) { + CodedVal = value - 1; + CodedBits = 2; + } else if ( value <= 4 ) { + CodedVal = 0x0004 + (value - 3); + CodedBits = 3; + + } else if ( value <= 6 ) { + CodedVal = 0x000C + (value - 5); + CodedBits = 4; + + } else if ( value <= 10 ) { + CodedVal = 0x0038 + (value - 7); + CodedBits = 6; + + } else if ( value <= 14 ) { + CodedVal = 0x0078 + (value - 11); + CodedBits = 7; + } else { + CodedVal = 0x01F0 + (value - 15); + CodedBits = 9; + } + + /* Add the bits to the encode holding buffer. */ + oggpackB_write( cpi->oggbuffer, CodedVal, CodedBits ); + + return CodedBits; +} + +void PackAndWriteDFArray( CP_INSTANCE *cpi ){ + ogg_uint32_t i; + unsigned char val; + ogg_uint32_t run_count; + + ogg_uint32_t SB, MB, B; /* Block, MB and SB loop variables */ + ogg_uint32_t BListIndex = 0; + ogg_uint32_t LastSbBIndex = 0; + ogg_int32_t DfBlockIndex; /* Block index in display_fragments */ + + /* Initialise workspaces */ + memset( cpi->pb.SBFullyFlags, 1, cpi->pb.SuperBlocks); + memset( cpi->pb.SBCodedFlags, 0, cpi->pb.SuperBlocks ); + memset( cpi->PartiallyCodedFlags, 0, cpi->pb.SuperBlocks ); + memset( cpi->BlockCodedFlags, 0, cpi->pb.UnitFragments); + + for( SB = 0; SB < cpi->pb.SuperBlocks; SB++ ) { + /* Check for coded blocks and macro-blocks */ + for ( MB=0; MB<4; MB++ ) { + /* If MB in frame */ + if ( QuadMapToMBTopLeft(cpi->pb.BlockMap,SB,MB) >= 0 ) { + for ( B=0; B<4; B++ ) { + DfBlockIndex = QuadMapToIndex1( cpi->pb.BlockMap,SB, MB, B ); + + /* Does Block lie in frame: */ + if ( DfBlockIndex >= 0 ) { + /* In Frame: If it is not coded then this SB is only + partly coded.: */ + if ( cpi->pb.display_fragments[DfBlockIndex] ) { + cpi->pb.SBCodedFlags[SB] = 1; /* SB at least partly coded */ + cpi->BlockCodedFlags[BListIndex] = 1; /* Block is coded */ + + }else{ + cpi->pb.SBFullyFlags[SB] = 0; /* SB not fully coded */ + cpi->BlockCodedFlags[BListIndex] = 0; /* Block is not coded */ + } + + BListIndex++; + } + } + } + } + + /* Is the SB fully coded or uncoded. + If so then backup BListIndex and MBListIndex */ + if ( cpi->pb.SBFullyFlags[SB] || !cpi->pb.SBCodedFlags[SB] ) { + BListIndex = LastSbBIndex; /* Reset to values from previous SB */ + }else{ + cpi->PartiallyCodedFlags[SB] = 1; /* Set up list of partially + coded SBs */ + LastSbBIndex = BListIndex; + } + } + + /* Code list of partially coded Super-Block. */ + val = cpi->PartiallyCodedFlags[0]; + oggpackB_write( cpi->oggbuffer, (ogg_uint32_t)val, 1); + + i = 0; + while ( i < cpi->pb.SuperBlocks ) { + run_count = 0; + while ( (ipb.SuperBlocks) && + (cpi->PartiallyCodedFlags[i]==val) && + run_count<4129 ) { + i++; + run_count++; + } + + /* Code the run */ + FrArrayCodeSBRun( cpi, run_count); + + if(run_count >= 4129 && i < cpi->pb.SuperBlocks ){ + val = cpi->PartiallyCodedFlags[i]; + oggpackB_write( cpi->oggbuffer, (ogg_uint32_t)val, 1); + + }else + val = ( val == 0 ) ? 1 : 0; + } + + /* RLC Super-Block fully/not coded. */ + i = 0; + + /* Skip partially coded blocks */ + while( (i < cpi->pb.SuperBlocks) && cpi->PartiallyCodedFlags[i] ) + i++; + + if ( i < cpi->pb.SuperBlocks ) { + val = cpi->pb.SBFullyFlags[i]; + oggpackB_write( cpi->oggbuffer, (ogg_uint32_t)val, 1); + + while ( i < cpi->pb.SuperBlocks ) { + run_count = 0; + while ( (i < cpi->pb.SuperBlocks) && + (cpi->pb.SBFullyFlags[i] == val) && + run_count < 4129) { + i++; + /* Skip partially coded blocks */ + while( (i < cpi->pb.SuperBlocks) && cpi->PartiallyCodedFlags[i] ) + i++; + run_count++; + } + + /* Code the run */ + FrArrayCodeSBRun( cpi, run_count ); + + if(run_count >= 4129 && i < cpi->pb.SuperBlocks ){ + val = cpi->PartiallyCodedFlags[i]; + oggpackB_write( cpi->oggbuffer, (ogg_uint32_t)val, 1); + }else + val = ( val == 0 ) ? 1 : 0; + } + } + + + /* Now code the block flags */ + if ( BListIndex > 0 ) { + /* Code the block flags start value */ + val = cpi->BlockCodedFlags[0]; + oggpackB_write( cpi->oggbuffer, (ogg_uint32_t)val, 1); + + /* Now code the block flags. */ + for ( i = 0; i < BListIndex; ) { + run_count = 0; + while ( (i < BListIndex) && (cpi->BlockCodedFlags[i] == val) ) { + i++; + run_count++; + } + + FrArrayCodeBlockRun( cpi, run_count ); + + val = ( val == 0 ) ? 1 : 0; + } + } +} diff --git a/Engine/lib/libtheora/lib/enc/frinit.c b/Engine/lib/libtheora/lib/enc/frinit.c new file mode 100644 index 000000000..ae6bbd64f --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/frinit.c @@ -0,0 +1,392 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: frinit.c 15153 2008-08-04 18:37:55Z tterribe $ + + ********************************************************************/ + +#include +#include "codec_internal.h" + + +void InitializeFragCoordinates(PB_INSTANCE *pbi){ + + ogg_uint32_t i, j; + + ogg_uint32_t HorizFrags = pbi->HFragments; + ogg_uint32_t VertFrags = pbi->VFragments; + ogg_uint32_t StartFrag = 0; + + /* Y */ + + for(i = 0; i< VertFrags; i++){ + for(j = 0; j< HorizFrags; j++){ + + ogg_uint32_t ThisFrag = i * HorizFrags + j; + pbi->FragCoordinates[ ThisFrag ].x=j * BLOCK_HEIGHT_WIDTH; + pbi->FragCoordinates[ ThisFrag ].y=i * BLOCK_HEIGHT_WIDTH; + + } + } + + /* U */ + HorizFrags >>= 1; + VertFrags >>= 1; + StartFrag = pbi->YPlaneFragments; + + for(i = 0; i< VertFrags; i++) { + for(j = 0; j< HorizFrags; j++) { + ogg_uint32_t ThisFrag = StartFrag + i * HorizFrags + j; + pbi->FragCoordinates[ ThisFrag ].x=j * BLOCK_HEIGHT_WIDTH; + pbi->FragCoordinates[ ThisFrag ].y=i * BLOCK_HEIGHT_WIDTH; + + } + } + + /* V */ + StartFrag = pbi->YPlaneFragments + pbi->UVPlaneFragments; + for(i = 0; i< VertFrags; i++) { + for(j = 0; j< HorizFrags; j++) { + ogg_uint32_t ThisFrag = StartFrag + i * HorizFrags + j; + pbi->FragCoordinates[ ThisFrag ].x=j * BLOCK_HEIGHT_WIDTH; + pbi->FragCoordinates[ ThisFrag ].y=i * BLOCK_HEIGHT_WIDTH; + + } + } +} + +static void CalcPixelIndexTable( PB_INSTANCE *pbi){ + ogg_uint32_t i; + ogg_uint32_t * PixelIndexTablePtr; + + /* Calculate the pixel index table for normal image buffers */ + PixelIndexTablePtr = pbi->pixel_index_table; + for ( i = 0; i < pbi->YPlaneFragments; i++ ) { + PixelIndexTablePtr[ i ] = + ((i / pbi->HFragments) * VFRAGPIXELS * + pbi->info.width); + PixelIndexTablePtr[ i ] += + ((i % pbi->HFragments) * HFRAGPIXELS); + } + + PixelIndexTablePtr = &pbi->pixel_index_table[pbi->YPlaneFragments]; + for ( i = 0; i < ((pbi->HFragments >> 1) * pbi->VFragments); i++ ) { + PixelIndexTablePtr[ i ] = + ((i / (pbi->HFragments / 2) ) * + (VFRAGPIXELS * + (pbi->info.width / 2)) ); + PixelIndexTablePtr[ i ] += + ((i % (pbi->HFragments / 2) ) * + HFRAGPIXELS) + pbi->YPlaneSize; + } + + /************************************************************************/ + /* Now calculate the pixel index table for image reconstruction buffers */ + PixelIndexTablePtr = pbi->recon_pixel_index_table; + for ( i = 0; i < pbi->YPlaneFragments; i++ ){ + PixelIndexTablePtr[ i ] = + ((i / pbi->HFragments) * VFRAGPIXELS * + pbi->YStride); + PixelIndexTablePtr[ i ] += + ((i % pbi->HFragments) * HFRAGPIXELS) + + pbi->ReconYDataOffset; + } + + /* U blocks */ + PixelIndexTablePtr = &pbi->recon_pixel_index_table[pbi->YPlaneFragments]; + for ( i = 0; i < pbi->UVPlaneFragments; i++ ) { + PixelIndexTablePtr[ i ] = + ((i / (pbi->HFragments / 2) ) * + (VFRAGPIXELS * (pbi->UVStride)) ); + PixelIndexTablePtr[ i ] += + ((i % (pbi->HFragments / 2) ) * + HFRAGPIXELS) + pbi->ReconUDataOffset; + } + + /* V blocks */ + PixelIndexTablePtr = + &pbi->recon_pixel_index_table[pbi->YPlaneFragments + + pbi->UVPlaneFragments]; + + for ( i = 0; i < pbi->UVPlaneFragments; i++ ) { + PixelIndexTablePtr[ i ] = + ((i / (pbi->HFragments / 2) ) * + (VFRAGPIXELS * (pbi->UVStride)) ); + PixelIndexTablePtr[ i ] += + ((i % (pbi->HFragments / 2) ) * HFRAGPIXELS) + + pbi->ReconVDataOffset; + } +} + +void ClearFragmentInfo(PB_INSTANCE * pbi){ + + /* free prior allocs if present */ + if(pbi->display_fragments) _ogg_free(pbi->display_fragments); + if(pbi->pixel_index_table) _ogg_free(pbi->pixel_index_table); + if(pbi->recon_pixel_index_table) _ogg_free(pbi->recon_pixel_index_table); + if(pbi->FragTokenCounts) _ogg_free(pbi->FragTokenCounts); + if(pbi->CodedBlockList) _ogg_free(pbi->CodedBlockList); + if(pbi->FragMVect) _ogg_free(pbi->FragMVect); + if(pbi->FragCoeffs) _ogg_free(pbi->FragCoeffs); + if(pbi->FragCoefEOB) _ogg_free(pbi->FragCoefEOB); + if(pbi->skipped_display_fragments) _ogg_free(pbi->skipped_display_fragments); + if(pbi->QFragData) _ogg_free(pbi->QFragData); + if(pbi->TokenList) _ogg_free(pbi->TokenList); + if(pbi->FragCodingMethod) _ogg_free(pbi->FragCodingMethod); + if(pbi->FragCoordinates) _ogg_free(pbi->FragCoordinates); + + if(pbi->FragQIndex) _ogg_free(pbi->FragQIndex); + if(pbi->PPCoefBuffer) _ogg_free(pbi->PPCoefBuffer); + if(pbi->FragmentVariances) _ogg_free(pbi->FragmentVariances); + + if(pbi->BlockMap) _ogg_free(pbi->BlockMap); + + if(pbi->SBCodedFlags) _ogg_free(pbi->SBCodedFlags); + if(pbi->SBFullyFlags) _ogg_free(pbi->SBFullyFlags); + if(pbi->MBFullyFlags) _ogg_free(pbi->MBFullyFlags); + if(pbi->MBCodedFlags) _ogg_free(pbi->MBCodedFlags); + + if(pbi->_Nodes) _ogg_free(pbi->_Nodes); + pbi->_Nodes = 0; + + pbi->QFragData = 0; + pbi->TokenList = 0; + pbi->skipped_display_fragments = 0; + pbi->FragCoeffs = 0; + pbi->FragCoefEOB = 0; + pbi->display_fragments = 0; + pbi->pixel_index_table = 0; + pbi->recon_pixel_index_table = 0; + pbi->FragTokenCounts = 0; + pbi->CodedBlockList = 0; + pbi->FragCodingMethod = 0; + pbi->FragMVect = 0; + pbi->MBCodedFlags = 0; + pbi->MBFullyFlags = 0; + pbi->BlockMap = 0; + + pbi->SBCodedFlags = 0; + pbi->SBFullyFlags = 0; + pbi->QFragData = 0; + pbi->TokenList = 0; + pbi->skipped_display_fragments = 0; + pbi->FragCoeffs = 0; + pbi->FragCoefEOB = 0; + pbi->display_fragments = 0; + pbi->pixel_index_table = 0; + pbi->recon_pixel_index_table = 0; + pbi->FragTokenCounts = 0; + pbi->CodedBlockList = 0; + pbi->FragCodingMethod = 0; + pbi->FragCoordinates = 0; + pbi->FragMVect = 0; + + pbi->PPCoefBuffer=0; + pbi->PPCoefBuffer=0; + pbi->FragQIndex = 0; + pbi->FragQIndex = 0; + pbi->FragmentVariances= 0; + pbi->FragmentVariances = 0 ; +} + +void InitFragmentInfo(PB_INSTANCE * pbi){ + + /* clear any existing info */ + ClearFragmentInfo(pbi); + + /* Perform Fragment Allocations */ + pbi->display_fragments = + _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->display_fragments)); + + pbi->pixel_index_table = + _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->pixel_index_table)); + + pbi->recon_pixel_index_table = + _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->recon_pixel_index_table)); + + pbi->FragTokenCounts = + _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->FragTokenCounts)); + + pbi->CodedBlockList = + _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->CodedBlockList)); + + pbi->FragMVect = + _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->FragMVect)); + + pbi->FragCoeffs = + _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->FragCoeffs)); + + pbi->FragCoefEOB = + _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->FragCoefEOB)); + + pbi->skipped_display_fragments = + _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->skipped_display_fragments)); + + pbi->QFragData = + _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->QFragData)); + + pbi->TokenList = + _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->TokenList)); + + pbi->FragCodingMethod = + _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->FragCodingMethod)); + + pbi->FragCoordinates = + _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->FragCoordinates)); + + pbi->FragQIndex = + _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->FragQIndex)); + + pbi->PPCoefBuffer = + _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->PPCoefBuffer)); + + pbi->FragmentVariances = + _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->FragmentVariances)); + + pbi->_Nodes = + _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->_Nodes)); + + /* Super Block Initialization */ + pbi->SBCodedFlags = + _ogg_malloc(pbi->SuperBlocks * sizeof(*pbi->SBCodedFlags)); + + pbi->SBFullyFlags = + _ogg_malloc(pbi->SuperBlocks * sizeof(*pbi->SBFullyFlags)); + + /* Macro Block Initialization */ + pbi->MBCodedFlags = + _ogg_malloc(pbi->MacroBlocks * sizeof(*pbi->MBCodedFlags)); + + pbi->MBFullyFlags = + _ogg_malloc(pbi->MacroBlocks * sizeof(*pbi->MBFullyFlags)); + + pbi->BlockMap = + _ogg_malloc(pbi->SuperBlocks * sizeof(*pbi->BlockMap)); + +} + +void ClearFrameInfo(PB_INSTANCE * pbi){ + if(pbi->ThisFrameRecon ) + _ogg_free(pbi->ThisFrameRecon ); + if(pbi->GoldenFrame) + _ogg_free(pbi->GoldenFrame); + if(pbi->LastFrameRecon) + _ogg_free(pbi->LastFrameRecon); + if(pbi->PostProcessBuffer) + _ogg_free(pbi->PostProcessBuffer); + + + pbi->ThisFrameRecon = 0; + pbi->GoldenFrame = 0; + pbi->LastFrameRecon = 0; + pbi->PostProcessBuffer = 0; + + + pbi->ThisFrameRecon = 0; + pbi->GoldenFrame = 0; + pbi->LastFrameRecon = 0; + pbi->PostProcessBuffer = 0; + +} + +void InitFrameInfo(PB_INSTANCE * pbi, unsigned int FrameSize){ + + /* clear any existing info */ + ClearFrameInfo(pbi); + + /* allocate frames */ + pbi->ThisFrameRecon = + _ogg_malloc(FrameSize*sizeof(*pbi->ThisFrameRecon)); + + pbi->GoldenFrame = + _ogg_malloc(FrameSize*sizeof(*pbi->GoldenFrame)); + + pbi->LastFrameRecon = + _ogg_malloc(FrameSize*sizeof(*pbi->LastFrameRecon)); + + pbi->PostProcessBuffer = + _ogg_malloc(FrameSize*sizeof(*pbi->PostProcessBuffer)); + +} + +void InitFrameDetails(PB_INSTANCE *pbi){ + int FrameSize; + + /*pbi->PostProcessingLevel = 0; + pbi->PostProcessingLevel = 4; + pbi->PostProcessingLevel = 5; + pbi->PostProcessingLevel = 6;*/ + + pbi->PostProcessingLevel = 0; + + + /* Set the frame size etc. */ + + pbi->YPlaneSize = pbi->info.width * + pbi->info.height; + pbi->UVPlaneSize = pbi->YPlaneSize / 4; + pbi->HFragments = pbi->info.width / HFRAGPIXELS; + pbi->VFragments = pbi->info.height / VFRAGPIXELS; + pbi->UnitFragments = ((pbi->VFragments * pbi->HFragments)*3)/2; + pbi->YPlaneFragments = pbi->HFragments * pbi->VFragments; + pbi->UVPlaneFragments = pbi->YPlaneFragments / 4; + + pbi->YStride = (pbi->info.width + STRIDE_EXTRA); + pbi->UVStride = pbi->YStride / 2; + pbi->ReconYPlaneSize = pbi->YStride * + (pbi->info.height + STRIDE_EXTRA); + pbi->ReconUVPlaneSize = pbi->ReconYPlaneSize / 4; + FrameSize = pbi->ReconYPlaneSize + 2 * pbi->ReconUVPlaneSize; + + pbi->YDataOffset = 0; + pbi->UDataOffset = pbi->YPlaneSize; + pbi->VDataOffset = pbi->YPlaneSize + pbi->UVPlaneSize; + pbi->ReconYDataOffset = + (pbi->YStride * UMV_BORDER) + UMV_BORDER; + pbi->ReconUDataOffset = pbi->ReconYPlaneSize + + (pbi->UVStride * (UMV_BORDER/2)) + (UMV_BORDER/2); + pbi->ReconVDataOffset = pbi->ReconYPlaneSize + pbi->ReconUVPlaneSize + + (pbi->UVStride * (UMV_BORDER/2)) + (UMV_BORDER/2); + + /* Image dimensions in Super-Blocks */ + pbi->YSBRows = (pbi->info.height/32) + + ( pbi->info.height%32 ? 1 : 0 ); + pbi->YSBCols = (pbi->info.width/32) + + ( pbi->info.width%32 ? 1 : 0 ); + pbi->UVSBRows = ((pbi->info.height/2)/32) + + ( (pbi->info.height/2)%32 ? 1 : 0 ); + pbi->UVSBCols = ((pbi->info.width/2)/32) + + ( (pbi->info.width/2)%32 ? 1 : 0 ); + + /* Super-Blocks per component */ + pbi->YSuperBlocks = pbi->YSBRows * pbi->YSBCols; + pbi->UVSuperBlocks = pbi->UVSBRows * pbi->UVSBCols; + pbi->SuperBlocks = pbi->YSuperBlocks+2*pbi->UVSuperBlocks; + + /* Useful externals */ + pbi->MacroBlocks = ((pbi->VFragments+1)/2)*((pbi->HFragments+1)/2); + + InitFragmentInfo(pbi); + InitFrameInfo(pbi, FrameSize); + InitializeFragCoordinates(pbi); + + /* Configure mapping between quad-tree and fragments */ + CreateBlockMapping ( pbi->BlockMap, pbi->YSuperBlocks, + pbi->UVSuperBlocks, pbi->HFragments, pbi->VFragments); + + /* Re-initialise the pixel index table. */ + + CalcPixelIndexTable( pbi ); + +} + diff --git a/Engine/lib/libtheora/lib/enc/hufftables.h b/Engine/lib/libtheora/lib/enc/hufftables.h new file mode 100644 index 000000000..eb4be22c2 --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/hufftables.h @@ -0,0 +1,1034 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: hufftables.h 13884 2007-09-22 08:38:10Z giles $ + + ********************************************************************/ + +#include "../dec/huffman.h" +#include "codec_internal.h" + +const unsigned char ExtraBitLengths_VP31[MAX_ENTROPY_TOKENS] = { + 0, 0, 0, 2, 3, 4, 12,3, 6, /* EOB and Zero-run tokens. */ + 0, 0, 0, 0, /* Very low value tokens. */ + 1, 1, 1, 1, 2, 3, 4, 5, 6, 10, /* Other value tokens */ + 1, 1, 1, 1, 1, 3, 4, /* Category 1 runs. */ + 2, 3, /* Category 2 runs. */ +}; + +#define NEW_FREQS 0 /* dbm - test new frequency tables */ + +#if NEW_FREQS +/* New baseline frequency tables for encoder version >= 2 */ +const ogg_uint32_t FrequencyCounts_VP3[NUM_HUFF_TABLES][MAX_ENTROPY_TOKENS] = { + /* DC Intra bias */ + { 272, 84, 31, 36, 10, 2, 1, 92, 1, + 701, 872, 410, 478, + 630, 502, 417, 356, 582, 824, 985, 965, 697, 606, + 125, 119, 40, 3, 9, 15, 10, + 73, 37, + }, + { 311, 107, 41, 51, 18, 4, 2, 120, 1, + 824, 1037, 468, 541, + 714, 555, 451, 374, 595, 819, 929, 817, 474, 220, + 172, 142, 27, 4, 9, 10, 2, + 98, 48, + }, + { 353, 125, 49, 66, 24, 6, 2, 124, 1, + 926, 1172, 512, 594, + 766, 581, 458, 379, 590, 789, 849, 665, 306, 80, + 204, 147, 25, 5, 12, 9, 2, + 108, 54, + }, + { 392, 141, 57, 75, 31, 7, 4, 138, 1, + 1050, 1321, 559, 649, + 806, 594, 460, 372, 568, 727, 710, 475, 155, 19, + 251, 174, 27, 7, 16, 8, 2, + 126, 62, + }, + { 455, 168, 66, 87, 39, 10, 6, 124, 2, + 1143, 1455, 592, 692, + 824, 596, 453, 361, 542, 657, 592, 329, 78, 5, + 269, 184, 27, 9, 19, 7, 2, + 127, 66, + }, + { 544, 201, 80, 102, 45, 11, 6, 99, 1, + 1236, 1587, 610, 720, + 833, 590, 444, 348, 506, 588, 487, 226, 39, 2, + 253, 178, 27, 10, 20, 7, 2, + 118, 65, + }, + { 649, 241, 98, 121, 54, 14, 8, 84, 1, + 1349, 1719, 634, 763, + 847, 583, 428, 323, 456, 492, 349, 120, 13, 1, + 231, 170, 24, 8, 19, 7, 1, + 109, 67, + }, + { 824, 304, 129, 158, 66, 19, 10, 44, 2, + 1476, 1925, 644, 794, + 838, 559, 396, 289, 392, 384, 223, 53, 3, 1, + 159, 121, 17, 6, 16, 6, 2, + 69, 53, + }, + + /* DC Inter Bias */ + { 534, 174, 71, 68, 10, 1, 1, 68, 119, + 1674, 1526, 560, 536, + 539, 331, 229, 168, 233, 262, 231, 149, 71, 51, + 629, 530, 284, 126, 182, 208, 184, + 148, 87, + }, + { 594, 195, 77, 71, 9, 1, 1, 47, 89, + 1723, 1592, 595, 570, + 574, 351, 241, 176, 243, 271, 234, 144, 65, 37, + 534, 449, 240, 117, 167, 277, 153, + 96, 54, + }, + { 642, 213, 88, 83, 12, 1, 1, 40, 80, + 1751, 1630, 621, 600, + 598, 367, 250, 183, 251, 276, 235, 143, 62, 28, + 485, 397, 212, 110, 161, 193, 141, + 84, 48, + }, + { 693, 258, 114, 131, 27, 3, 1, 44, 79, + 1794, 1644, 550, 533, + 518, 314, 213, 154, 209, 223, 174, 97, 40, 14, + 584, 463, 236, 138, 196, 249, 143, + 94, 54, + }, + { 758, 303, 144, 189, 53, 8, 1, 37, 69, + 1842, 1732, 513, 504, + 478, 287, 191, 137, 182, 186, 137, 72, 31, 6, + 589, 469, 199, 128, 177, 264, 161, + 89, 49, + }, + { 817, 344, 170, 243, 84, 18, 2, 30, 65, + 1836, 1733, 518, 511, + 477, 281, 185, 130, 169, 166, 117, 59, 25, 3, + 572, 450, 185, 121, 173, 232, 146, + 80, 43, + }, + { 865, 389, 204, 322, 139, 42, 9, 26, 51, + 1848, 1766, 531, 522, + 477, 275, 177, 122, 153, 144, 97, 50, 16, 1, + 485, 378, 167, 115, 164, 203, 128, + 74, 42, + }, + { 961, 447, 243, 407, 196, 74, 26, 12, 34, + 2003, 1942, 571, 565, + 494, 278, 173, 116, 141, 129, 85, 44, 8, 1, + 285, 223, 101, 66, 104, 120, 74, + 35, 22, + }, + + /* AC INTRA Tables */ + /* AC Intra bias group 1 tables */ + { 245, 68, 25, 28, 5, 1, 1, 359, 4, + 910, 904, 570, 571, + 766, 620, 478, 375, 554, 684, 652, 441, 182, 30, + 535, 206, 118, 77, 69, 90, 16, + 299, 100, + }, + { 302, 86, 32, 36, 8, 1, 1, 362, 3, + 974, 968, 599, 599, + 774, 635, 469, 365, 528, 628, 557, 337, 118, 14, + 577, 219, 136, 82, 69, 65, 13, + 317, 112, + }, + { 348, 102, 39, 44, 9, 2, 1, 363, 3, + 1062, 1055, 607, 609, + 787, 626, 457, 348, 494, 550, 452, 233, 60, 2, + 636, 244, 159, 92, 74, 68, 12, + 327, 119, + }, + { 400, 121, 47, 51, 11, 2, 1, 366, 3, + 1109, 1102, 620, 622, + 786, 624, 450, 331, 459, 490, 366, 163, 29, 1, + 673, 257, 175, 98, 77, 63, 14, + 344, 131, + }, + { 470, 151, 59, 67, 15, 3, 1, 354, 4, + 1198, 1189, 640, 643, + 769, 603, 410, 294, 386, 381, 240, 78, 5, 1, + 746, 282, 205, 113, 87, 64, 15, + 368, 145, + }, + { 553, 189, 77, 94, 24, 6, 1, 347, 4, + 1244, 1232, 650, 653, + 739, 551, 360, 249, 303, 261, 129, 24, 1, 1, + 828, 313, 245, 135, 108, 77, 17, + 403, 169, + }, + { 701, 253, 109, 140, 42, 12, 2, 350, 6, + 1210, 1197, 652, 647, + 673, 495, 299, 189, 211, 151, 50, 2, 1, 1, + 892, 336, 284, 162, 134, 101, 25, + 455, 205, + }, + { 924, 390, 180, 248, 85, 31, 13, 286, 14, + 1242, 1206, 601, 577, + 519, 342, 175, 100, 85, 36, 1, 1, 1, 1, + 1031, 348, 346, 204, 166, 131, 34, + 473, 197, + }, + /* AC Inter bias group 1 tables */ + { 459, 128, 50, 48, 8, 1, 1, 224, 69, + 1285, 1227, 587, 565, + 573, 406, 261, 180, 228, 213, 130, 47, 11, 3, + 1069, 540, 309, 231, 147, 279, 157, + 383, 165, + }, + { 524, 155, 62, 64, 14, 2, 1, 209, 63, + 1345, 1288, 523, 507, + 515, 358, 225, 153, 183, 160, 87, 29, 7, 2, + 1151, 591, 365, 282, 179, 308, 133, + 344, 157, + }, + { 588, 181, 75, 81, 19, 3, 1, 204, 68, + 1344, 1288, 517, 503, + 505, 346, 216, 141, 169, 139, 71, 21, 5, 1, + 1146, 584, 366, 286, 170, 298, 153, + 342, 157, + }, + { 634, 196, 82, 89, 22, 4, 1, 194, 60, + 1356, 1312, 515, 502, + 489, 331, 199, 127, 145, 111, 51, 14, 3, 1, + 1156, 589, 393, 300, 182, 285, 144, + 340, 159, + }, + { 715, 231, 98, 113, 31, 7, 1, 181, 57, + 1345, 1303, 498, 490, + 448, 291, 166, 101, 106, 75, 30, 9, 1, 1, + 1175, 584, 416, 321, 209, 333, 164, + 330, 159, + }, + { 825, 283, 125, 149, 44, 11, 2, 160, 59, + 1343, 1308, 476, 469, + 405, 247, 131, 75, 76, 47, 18, 5, 1, 1, + 1192, 579, 432, 332, 217, 327, 176, + 320, 154, + }, + { 961, 361, 170, 215, 70, 20, 5, 161, 55, + 1250, 1218, 463, 460, + 354, 204, 101, 52, 48, 28, 11, 1, 1, 1, + 1172, 570, 449, 350, 222, 332, 169, + 338, 174, + }, + { 1139, 506, 266, 387, 156, 57, 26, 114, 48, + 1192, 1170, 366, 366, + 226, 113, 47, 22, 22, 12, 1, 1, 1, 1, + 1222, 551, 462, 391, 220, 322, 156, + 290, 136, + }, + + /* AC Intra bias group 2 tables */ + { 245, 49, 15, 11, 1, 1, 1, 332, 38, + 1163, 1162, 685, 683, + 813, 623, 437, 318, 421, 424, 288, 109, 14, 1, + 729, 303, 179, 112, 87, 199, 46, + 364, 135, + }, + { 305, 67, 22, 17, 2, 1, 1, 329, 39, + 1250, 1245, 706, 705, + 801, 584, 385, 267, 330, 296, 165, 40, 3, 1, + 798, 340, 206, 131, 108, 258, 52, + 382, 154, + }, + { 356, 82, 28, 23, 3, 1, 1, 312, 42, + 1340, 1334, 701, 703, + 770, 545, 346, 227, 269, 223, 100, 17, 1, 1, + 846, 359, 222, 142, 120, 284, 55, + 379, 157, + }, + { 402, 95, 33, 30, 4, 1, 1, 300, 43, + 1379, 1371, 710, 714, + 724, 486, 289, 182, 202, 144, 47, 5, 1, 1, + 908, 394, 250, 161, 141, 350, 60, + 391, 171, + }, + { 499, 122, 44, 42, 7, 1, 1, 267, 45, + 1439, 1436, 690, 694, + 628, 385, 213, 122, 117, 62, 14, 1, 1, 1, + 992, 441, 288, 187, 167, 446, 82, + 378, 176, + }, + { 641, 168, 62, 60, 12, 1, 1, 247, 49, + 1435, 1436, 662, 669, + 527, 298, 142, 71, 55, 22, 3, 1, 1, 1, + 1036, 470, 319, 208, 193, 548, 106, + 362, 184, + }, + { 860, 274, 111, 113, 23, 4, 1, 229, 59, + 1331, 1323, 629, 645, + 419, 192, 72, 30, 19, 6, 1, 1, 1, 1, + 1022, 478, 339, 225, 213, 690, 142, + 342, 198, + }, + { 1059, 437, 218, 285, 84, 17, 2, 152, 44, + 1284, 1313, 530, 561, + 212, 66, 17, 6, 3, 1, 1, 1, 1, 1, + 1034, 485, 346, 226, 207, 819, 185, + 248, 145, + }, + /* AC Inter bias group 2 tables */ + { 407, 93, 31, 24, 2, 1, 1, 232, 108, + 1365, 1349, 581, 578, + 498, 305, 170, 100, 103, 67, 24, 5, 1, 1, + 1175, 604, 393, 268, 209, 506, 217, + 379, 193, + }, + { 521, 129, 46, 39, 4, 1, 1, 199, 116, + 1419, 1403, 543, 540, + 446, 263, 138, 78, 75, 44, 13, 2, 1, 1, + 1201, 605, 392, 267, 214, 533, 252, + 334, 167, + }, + { 575, 144, 52, 46, 6, 1, 1, 193, 124, + 1394, 1384, 528, 528, + 406, 227, 112, 59, 54, 28, 7, 1, 1, 1, + 1210, 621, 412, 284, 235, 604, 265, + 320, 167, + }, + { 673, 174, 64, 59, 9, 1, 1, 177, 128, + 1392, 1385, 499, 499, + 352, 183, 85, 42, 35, 16, 3, 1, 1, 1, + 1210, 626, 418, 289, 246, 675, 297, + 292, 158, + }, + { 804, 225, 85, 77, 12, 1, 1, 150, 129, + 1387, 1384, 455, 455, + 277, 129, 53, 23, 17, 7, 1, 1, 1, 1, + 1212, 635, 433, 306, 268, 760, 313, + 249, 137, + }, + { 975, 305, 123, 117, 20, 2, 1, 135, 140, + 1312, 1310, 401, 399, + 201, 80, 28, 11, 8, 2, 1, 1, 1, 1, + 1162, 623, 439, 314, 283, 906, 368, + 203, 121, + }, + { 1205, 452, 208, 231, 50, 6, 1, 123, 149, + 1161, 1164, 370, 370, + 137, 45, 14, 4, 2, 1, 1, 1, 1, 1, + 1047, 562, 413, 300, 277, 1020, 404, + 168, 105, + }, + { 1297, 662, 389, 574, 200, 39, 4, 55, 120, + 1069, 1076, 273, 265, + 66, 14, 2, 1, 1, 1, 1, 1, 1, 1, + 930, 475, 345, 249, 236, 1124, 376, + 91, 56, + }, + + /* AC Intra bias group 3 tables */ + { 278, 55, 17, 12, 1, 1, 1, 288, 71, + 1315, 1304, 725, 724, + 733, 506, 307, 195, 225, 175, 77, 12, 1, 1, + 904, 414, 246, 170, 126, 290, 205, + 423, 185, + }, + { 382, 80, 26, 21, 2, 1, 1, 239, 64, + 1442, 1429, 706, 701, + 664, 420, 239, 146, 152, 105, 34, 2, 1, 1, + 975, 440, 263, 185, 140, 332, 229, + 397, 169, + }, + { 451, 97, 32, 27, 4, 1, 1, 223, 75, + 1462, 1454, 682, 680, + 574, 343, 179, 101, 98, 54, 9, 1, 1, 1, + 1031, 482, 293, 210, 163, 400, 297, + 384, 181, + }, + { 551, 128, 43, 37, 5, 1, 1, 201, 78, + 1497, 1487, 642, 651, + 493, 269, 133, 70, 60, 24, 2, 1, 1, 1, + 1065, 504, 312, 228, 178, 451, 352, + 351, 174, + }, + { 693, 179, 63, 54, 8, 1, 1, 169, 78, + 1502, 1497, 580, 591, + 375, 186, 77, 35, 21, 4, 1, 1, 1, 1, + 1099, 533, 341, 253, 206, 542, 432, + 306, 164, + }, + { 867, 263, 105, 96, 16, 2, 1, 152, 81, + 1435, 1439, 521, 525, + 270, 107, 32, 8, 3, 1, 1, 1, 1, 1, + 1085, 537, 361, 277, 223, 616, 549, + 258, 156, + }, + { 1022, 385, 182, 207, 46, 7, 1, 158, 88, + 1290, 1318, 501, 502, + 184, 38, 6, 1, 1, 1, 1, 1, 1, 1, + 1023, 480, 345, 301, 232, 665, 661, + 210, 133, + }, + { 1184, 555, 307, 457, 185, 44, 6, 115, 41, + 1236, 1253, 329, 340, + 32, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1017, 385, 316, 370, 246, 672, 788, + 85, 23, + }, + /* AC Inter bias group 3 tables */ + { 502, 106, 33, 22, 1, 1, 1, 151, 132, + 1446, 1451, 502, 499, + 343, 181, 84, 42, 36, 16, 3, 1, 1, 1, + 1211, 661, 429, 312, 242, 637, 498, + 288, 156, + }, + { 651, 147, 48, 35, 3, 1, 1, 145, 140, + 1419, 1420, 469, 466, + 281, 132, 56, 25, 18, 6, 1, 1, 1, 1, + 1175, 656, 435, 328, 260, 715, 556, + 252, 147, + }, + { 749, 179, 59, 43, 4, 1, 1, 123, 135, + 1423, 1431, 413, 409, + 221, 95, 36, 15, 9, 2, 1, 1, 1, 1, + 1159, 658, 444, 340, 272, 782, 656, + 205, 124, + }, + { 902, 243, 86, 67, 7, 1, 1, 114, 141, + 1385, 1385, 387, 383, + 178, 67, 22, 7, 4, 1, 1, 1, 1, 1, + 1096, 632, 434, 339, 277, 813, 735, + 171, 109, + }, + { 1081, 337, 133, 112, 15, 1, 1, 92, 137, + 1350, 1349, 311, 309, + 115, 34, 8, 2, 1, 1, 1, 1, 1, 1, + 1016, 595, 418, 342, 283, 870, 883, + 114, 78, + }, + { 1253, 467, 210, 205, 34, 3, 1, 80, 130, + 1318, 1313, 258, 260, + 68, 12, 2, 1, 1, 1, 1, 1, 1, 1, + 874, 516, 378, 330, 273, 877, 1000, + 72, 53, + }, + { 1362, 626, 333, 423, 100, 10, 1, 73, 106, + 1311, 1313, 241, 231, + 31, 3, 1, 1, 1, 1, 1, 1, 1, 1, + 620, 368, 286, 302, 245, 814, 1127, + 34, 28, + }, + { 1203, 743, 460, 774, 284, 36, 1, 13, 25, + 1956, 1961, 103, 106, + 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 248, 131, 149, 272, 165, 535, 813, + 3, 3, + }, + + /* AC Intra bias group 4 tables */ + { 599, 150, 55, 50, 9, 1, 1, 181, 19, + 1487, 1487, 625, 625, + 473, 271, 138, 74, 71, 42, 11, 1, 1, 1, + 1187, 591, 356, 239, 170, 351, 137, + 395, 194, + }, + { 758, 209, 79, 74, 15, 2, 1, 147, 25, + 1514, 1514, 521, 520, + 334, 165, 74, 36, 30, 11, 1, 1, 1, 1, + 1252, 644, 409, 279, 211, 472, 203, + 318, 171, + }, + { 852, 252, 100, 98, 20, 3, 1, 130, 26, + 1493, 1498, 481, 473, + 268, 123, 51, 23, 15, 3, 1, 1, 1, 1, + 1256, 652, 426, 294, 231, 543, 242, + 278, 156, + }, + { 971, 309, 130, 136, 30, 5, 1, 113, 28, + 1458, 1467, 443, 435, + 215, 90, 31, 12, 5, 1, 1, 1, 1, 1, + 1232, 643, 426, 303, 243, 590, 300, + 235, 136, + }, + { 1100, 399, 180, 206, 53, 9, 1, 101, 29, + 1419, 1425, 375, 374, + 158, 47, 10, 1, 1, 1, 1, 1, 1, 1, + 1193, 609, 426, 319, 256, 643, 383, + 166, 103, + }, + { 1195, 505, 249, 326, 98, 20, 3, 102, 25, + 1370, 1356, 355, 347, + 104, 11, 1, 1, 1, 1, 1, 1, 1, 1, + 1100, 568, 381, 330, 261, 642, 466, + 105, 69, + }, + { 1176, 608, 345, 559, 244, 57, 6, 110, 9, + 1370, 1332, 372, 367, + 29, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 859, 427, 269, 359, 375, 608, 451, + 35, 20, + }, + { 1140, 613, 391, 797, 458, 180, 37, 2, 1, + 2037, 1697, 95, 31, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 360, 49, 23, 198, 1001, 719, 160, + 1, 1, + }, + /* AC Inter bias group 4 tables */ + { 931, 272, 105, 96, 16, 1, 1, 91, 52, + 1481, 1489, 347, 349, + 174, 74, 28, 12, 8, 3, 1, 1, 1, 1, + 1247, 719, 490, 356, 279, 706, 363, + 187, 110, + }, + { 1095, 358, 148, 143, 25, 3, 1, 74, 61, + 1439, 1457, 304, 302, + 127, 46, 15, 5, 3, 1, 1, 1, 1, 1, + 1138, 664, 469, 347, 282, 768, 487, + 139, 87, + }, + { 1192, 423, 188, 189, 36, 4, 1, 64, 61, + 1457, 1475, 284, 282, + 106, 35, 10, 3, 1, 1, 1, 1, 1, 1, + 1078, 624, 440, 329, 264, 744, 507, + 117, 73, + }, + { 1275, 496, 231, 258, 52, 6, 1, 53, 55, + 1458, 1470, 248, 245, + 77, 20, 5, 1, 1, 1, 1, 1, 1, 1, + 984, 576, 414, 323, 260, 771, 569, + 84, 54, + }, + { 1377, 603, 302, 367, 87, 11, 1, 37, 52, + 1522, 1532, 207, 204, + 47, 8, 1, 1, 1, 1, 1, 1, 1, 1, + 840, 493, 366, 291, 231, 690, 636, + 52, 32, + }, + { 1409, 708, 385, 529, 148, 24, 1, 23, 37, + 1672, 1670, 163, 162, + 22, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 647, 364, 291, 262, 210, 574, 643, + 26, 14, + }, + { 1348, 778, 481, 755, 245, 53, 4, 13, 19, + 2114, 2089, 141, 139, + 7, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 302, 183, 162, 181, 182, 344, 437, + 8, 3, + }, + { 1560, 769, 410, 664, 243, 58, 1, 1, 1, + 3017, 2788, 17, 24, + 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 34, 16, 8, 55, 134, 105, 86, + 1, 1, + }, +}; + +#else /* Frequency tables for encoder version < 2 */ + +const ogg_uint32_t FrequencyCounts_VP3[NUM_HUFF_TABLES][MAX_ENTROPY_TOKENS] = { + /* DC Intra bias */ + { 198, 62, 22, 31, 14, 6, 6, 205, 3, + 843, 843, 415, 516, + 660, 509, 412, 347, 560, 779, 941, 930, 661, 377, + 170, 155, 39, 2, 9, 15, 11, + 128, 86, + }, + { 299, 92, 34, 39, 15, 6, 6, 132, 1, + 851, 851, 484, 485, + 666, 514, 416, 351, 567, 788, 953, 943, 670, 383, + 117, 119, 26, 4, 17, 7, 1, + 93, 56, + }, + { 367, 115, 42, 47, 16, 6, 6, 105, 1, + 896, 896, 492, 493, + 667, 510, 408, 342, 547, 760, 932, 927, 656, 379, + 114, 103, 10, 3, 6, 2, 1, + 88, 49, + }, + { 462, 158, 63, 76, 28, 9, 8, 145, 1, + 1140, 1140, 573, 574, + 754, 562, 435, 357, 555, 742, 793, 588, 274, 81, + 154, 117, 13, 6, 12, 2, 1, + 104, 62, + }, + { 558, 196, 81, 99, 36, 11, 9, 135, 1, + 1300, 1301, 606, 607, + 779, 560, 429, 349, 536, 680, 644, 405, 153, 30, + 171, 120, 12, 5, 14, 3, 1, + 104, 53, + }, + { 635, 233, 100, 122, 46, 14, 12, 113, 1, + 1414, 1415, 631, 631, + 785, 555, 432, 335, 513, 611, 521, 284, 89, 13, + 170, 113, 10, 5, 14, 3, 1, + 102, 62, + }, + { 720, 276, 119, 154, 62, 20, 16, 101, 1, + 1583, 1583, 661, 661, + 794, 556, 407, 318, 447, 472, 343, 153, 35, 1, + 172, 115, 11, 7, 14, 3, 1, + 112, 70, + }, + { 853, 326, 144, 184, 80, 27, 19, 52, 1, + 1739, 1740, 684, 685, + 800, 540, 381, 277, 364, 352, 218, 78, 13, 1, + 139, 109, 9, 6, 20, 2, 1, + 94, 50, + }, + + /* DC Inter Bias */ + { 490, 154, 57, 53, 10, 2, 1, 238, 160, + 1391, 1390, 579, 578, + 491, 273, 172, 118, 152, 156, 127, 79, 41, 39, + 712, 547, 316, 125, 183, 306, 237, + 451, 358, + }, + { 566, 184, 70, 65, 11, 2, 1, 235, 51, + 1414, 1414, 599, 598, + 510, 285, 180, 124, 157, 161, 131, 82, 42, 40, + 738, 551, 322, 138, 195, 188, 93, + 473, 365, + }, + { 711, 261, 111, 126, 27, 4, 1, 137, 52, + 1506, 1505, 645, 645, + 567, 316, 199, 136, 172, 175, 142, 88, 45, 48, + 548, 449, 255, 145, 184, 174, 121, + 260, 227, + }, + { 823, 319, 144, 175, 43, 7, 1, 53, 42, + 1648, 1648, 653, 652, + 583, 329, 205, 139, 175, 176, 139, 84, 44, 34, + 467, 389, 211, 137, 181, 186, 107, + 106, 85, + }, + { 948, 411, 201, 276, 85, 16, 2, 39, 33, + 1778, 1777, 584, 583, + 489, 265, 162, 111, 140, 140, 108, 64, 38, 23, + 428, 356, 201, 139, 186, 165, 94, + 78, 63, + }, + { 1002, 470, 248, 386, 153, 39, 6, 23, 23, + 1866, 1866, 573, 573, + 467, 249, 155, 103, 130, 128, 94, 60, 38, 14, + 323, 263, 159, 111, 156, 153, 74, + 46, 34, + }, + { 1020, 518, 291, 504, 242, 78, 18, 14, 14, + 1980, 1979, 527, 526, + 408, 219, 132, 87, 110, 104, 79, 55, 31, 7, + 265, 213, 129, 91, 131, 111, 50, + 31, 20, + }, + { 1018, 544, 320, 591, 338, 139, 47, 5, 2, + 2123, 2123, 548, 547, + 414, 212, 126, 83, 101, 96, 79, 60, 23, 1, + 120, 97, 55, 39, 60, 38, 15, + 11, 8, + }, + + /* AC INTRA Tables */ + /* AC Intra bias group 1 tables */ + { 242, 62, 22, 20, 4, 1, 1, 438, 1, + 593, 593, 489, 490, + 657, 580, 471, 374, 599, 783, 869, 770, 491, 279, + 358, 144, 82, 54, 49, 70, 5, + 289, 107, + }, + { 317, 95, 38, 41, 8, 1, 1, 479, 1, + 653, 654, 500, 501, + 682, 611, 473, 376, 582, 762, 806, 656, 358, 155, + 419, 162, 86, 58, 36, 34, 1, + 315, 126, + }, + { 382, 121, 49, 59, 15, 3, 1, 496, 1, + 674, 674, 553, 554, + 755, 636, 487, 391, 576, 718, 701, 488, 221, 72, + 448, 161, 107, 56, 37, 29, 1, + 362, 156, + }, + { 415, 138, 57, 73, 21, 5, 1, 528, 1, + 742, 741, 562, 563, + 753, 669, 492, 388, 563, 664, 589, 340, 129, 26, + 496, 184, 139, 71, 48, 33, 2, + 387, 166, + }, + { 496, 170, 73, 94, 31, 8, 2, 513, 1, + 855, 855, 604, 604, + 769, 662, 477, 356, 486, 526, 381, 183, 51, 5, + 590, 214, 160, 85, 60, 39, 3, + 427, 203, + }, + { 589, 207, 89, 116, 40, 13, 3, 491, 1, + 919, 919, 631, 631, + 769, 633, 432, 308, 408, 378, 247, 94, 17, 1, + 659, 247, 201, 105, 73, 51, 3, + 466, 242, + }, + { 727, 266, 115, 151, 49, 17, 6, 439, 1, + 977, 977, 642, 642, + 718, 572, 379, 243, 285, 251, 133, 40, 1, 1, + 756, 287, 253, 126, 94, 66, 4, + 492, 280, + }, + { 940, 392, 180, 247, 82, 30, 14, 343, 1, + 1064, 1064, 615, 616, + 596, 414, 235, 146, 149, 108, 41, 1, 1, 1, + 882, 314, 346, 172, 125, 83, 6, + 489, 291, + }, + /* AC Inter bias group 1 tables */ + { 440, 102, 33, 23, 2, 1, 1, 465, 85, + 852, 852, 744, 743, + 701, 496, 297, 193, 225, 200, 129, 58, 18, 2, + 798, 450, 269, 202, 145, 308, 154, + 646, 389, + }, + { 592, 151, 53, 43, 6, 1, 1, 409, 34, + 875, 875, 748, 747, + 723, 510, 305, 196, 229, 201, 130, 59, 18, 2, + 800, 436, 253, 185, 115, 194, 88, + 642, 368, + }, + { 759, 222, 86, 85, 17, 2, 1, 376, 46, + 888, 888, 689, 688, + 578, 408, 228, 143, 165, 141, 84, 35, 7, 1, + 878, 488, 321, 244, 147, 266, 124, + 612, 367, + }, + { 912, 298, 122, 133, 34, 7, 1, 261, 44, + 1092, 1091, 496, 496, + 409, 269, 150, 95, 106, 87, 49, 16, 1, 1, + 1102, 602, 428, 335, 193, 323, 157, + 423, 253, + }, + { 1072, 400, 180, 210, 60, 16, 3, 210, 40, + 1063, 1063, 451, 451, + 345, 221, 121, 73, 79, 64, 31, 6, 1, 1, + 1105, 608, 462, 358, 202, 330, 155, + 377, 228, + }, + { 1164, 503, 254, 330, 109, 34, 9, 167, 35, + 1038, 1037, 390, 390, + 278, 170, 89, 54, 56, 40, 13, 1, 1, 1, + 1110, 607, 492, 401, 218, 343, 141, + 323, 192, + }, + { 1173, 583, 321, 486, 196, 68, 23, 124, 23, + 1037, 1037, 347, 346, + 232, 139, 69, 40, 37, 20, 2, 1, 1, 1, + 1128, 584, 506, 410, 199, 301, 113, + 283, 159, + }, + { 1023, 591, 366, 699, 441, 228, 113, 79, 5, + 1056, 1056, 291, 291, + 173, 96, 38, 19, 8, 1, 1, 1, 1, 1, + 1187, 527, 498, 409, 147, 210, 56, + 263, 117, + }, + + /* AC Intra bias group 2 tables */ + { 311, 74, 27, 27, 5, 1, 1, 470, 24, + 665, 667, 637, 638, + 806, 687, 524, 402, 585, 679, 609, 364, 127, 20, + 448, 210, 131, 76, 52, 111, 19, + 393, 195, + }, + { 416, 104, 39, 38, 8, 1, 1, 545, 33, + 730, 731, 692, 692, + 866, 705, 501, 365, 495, 512, 387, 168, 39, 2, + 517, 240, 154, 86, 64, 127, 19, + 461, 247, + }, + { 474, 117, 43, 42, 9, 1, 1, 560, 40, + 783, 783, 759, 760, + 883, 698, 466, 318, 404, 377, 215, 66, 7, 1, + 559, 259, 176, 110, 87, 170, 22, + 520, 278, + }, + { 582, 149, 53, 53, 12, 2, 1, 473, 39, + 992, 993, 712, 713, + 792, 593, 373, 257, 299, 237, 114, 25, 1, 1, + 710, 329, 221, 143, 116, 226, 26, + 490, 259, + }, + { 744, 210, 78, 77, 16, 2, 1, 417, 37, + 1034, 1035, 728, 728, + 718, 509, 296, 175, 184, 122, 42, 3, 1, 1, + 791, 363, 255, 168, 145, 311, 35, + 492, 272, + }, + { 913, 291, 121, 128, 28, 4, 1, 334, 40, + 1083, 1084, 711, 712, + 624, 378, 191, 107, 95, 50, 7, 1, 1, 1, + 876, 414, 288, 180, 164, 382, 39, + 469, 275, + }, + { 1065, 405, 184, 216, 53, 8, 1, 236, 36, + 1134, 1134, 685, 686, + 465, 253, 113, 48, 41, 9, 1, 1, 1, 1, + 965, 451, 309, 179, 166, 429, 53, + 414, 249, + }, + { 1148, 548, 301, 438, 160, 42, 6, 84, 17, + 1222, 1223, 574, 575, + 272, 111, 23, 6, 2, 1, 1, 1, 1, 1, + 1060, 502, 328, 159, 144, 501, 54, + 302, 183, + }, + /* AC Inter bias group 2 tables */ + { 403, 80, 24, 17, 1, 1, 1, 480, 90, + 899, 899, 820, 819, + 667, 413, 228, 133, 139, 98, 42, 10, 1, 1, + 865, 470, 316, 222, 171, 419, 213, + 645, 400, + }, + { 698, 169, 59, 49, 6, 1, 1, 414, 101, + 894, 893, 761, 761, + 561, 338, 171, 96, 97, 64, 26, 6, 1, 1, + 896, 494, 343, 239, 192, 493, 215, + 583, 366, + }, + { 914, 255, 94, 80, 10, 1, 1, 345, 128, + 935, 935, 670, 671, + 415, 222, 105, 55, 51, 30, 10, 1, 1, 1, + 954, 530, 377, 274, 232, 641, 295, + 456, 298, + }, + { 1103, 359, 146, 135, 20, 1, 1, 235, 119, + 1042, 1042, 508, 507, + 293, 146, 65, 33, 30, 16, 4, 1, 1, 1, + 1031, 561, 407, 296, 265, 813, 317, + 301, 192, + }, + { 1255, 504, 238, 265, 51, 5, 1, 185, 113, + 1013, 1013, 437, 438, + 212, 92, 41, 18, 15, 6, 1, 1, 1, 1, + 976, 530, 386, 276, 260, 927, 357, + 224, 148, + }, + { 1292, 610, 332, 460, 127, 16, 1, 136, 99, + 1014, 1015, 384, 384, + 153, 65, 25, 11, 6, 1, 1, 1, 1, 1, + 942, 487, 343, 241, 238, 970, 358, + 174, 103, + }, + { 1219, 655, 407, 700, 280, 55, 2, 100, 60, + 1029, 1029, 337, 336, + 119, 43, 11, 3, 2, 1, 1, 1, 1, 1, + 894, 448, 305, 199, 213, 1005, 320, + 136, 77, + }, + { 1099, 675, 435, 971, 581, 168, 12, 37, 16, + 1181, 1081, 319, 318, + 66, 11, 6, 1, 1, 1, 1, 1, 1, 1, + 914, 370, 235, 138, 145, 949, 128, + 94, 41, + }, + + /* AC Intra bias group 3 tables */ + { 486, 112, 39, 34, 6, 1, 1, 541, 67, + 819, 818, 762, 763, + 813, 643, 403, 280, 332, 295, 164, 53, 6, 1, + 632, 294, 180, 131, 105, 208, 109, + 594, 295, + }, + { 723, 191, 69, 65, 12, 1, 1, 445, 79, + 865, 865, 816, 816, + 750, 515, 290, 172, 184, 122, 46, 5, 1, 1, + 740, 340, 213, 165, 129, 270, 168, + 603, 326, + }, + { 884, 264, 102, 103, 21, 3, 1, 382, 68, + 897, 897, 836, 836, + 684, 427, 227, 119, 119, 70, 16, 1, 1, 1, + 771, 367, 234, 184, 143, 272, 178, + 555, 326, + }, + { 1028, 347, 153, 161, 36, 8, 1, 251, 44, + 1083, 1084, 735, 735, + 541, 289, 144, 77, 57, 23, 3, 1, 1, 1, + 926, 422, 270, 215, 176, 301, 183, + 443, 248, + }, + { 1155, 465, 224, 264, 71, 14, 3, 174, 27, + 1110, 1111, 730, 731, + 429, 206, 79, 30, 19, 4, 1, 1, 1, 1, + 929, 443, 279, 225, 194, 298, 196, + 354, 223, + }, + { 1191, 576, 296, 415, 144, 36, 8, 114, 16, + 1162, 1162, 749, 749, + 338, 108, 29, 8, 5, 1, 1, 1, 1, 1, + 947, 458, 273, 207, 194, 248, 145, + 258, 152, + }, + { 1169, 619, 366, 603, 247, 92, 23, 46, 1, + 1236, 1236, 774, 775, + 191, 35, 14, 1, 1, 1, 1, 1, 1, 1, + 913, 449, 260, 214, 194, 180, 82, + 174, 98, + }, + { 1006, 537, 381, 897, 504, 266, 101, 39, 1, + 1307, 1307, 668, 667, + 116, 3, 1, 1, 1, 1, 1, 1, 1, 1, + 1175, 261, 295, 70, 164, 107, 31, + 10, 76, + }, + /* AC Inter bias group 3 tables */ + { 652, 156, 53, 43, 5, 1, 1, 368, 128, + 983, 984, 825, 825, + 583, 331, 163, 88, 84, 48, 15, 1, 1, 1, + 870, 480, 316, 228, 179, 421, 244, + 562, 349, + }, + { 988, 280, 104, 87, 12, 1, 1, 282, 194, + 980, 981, 738, 739, + 395, 189, 80, 37, 31, 12, 2, 1, 1, 1, + 862, 489, 333, 262, 214, 600, 446, + 390, 260, + }, + { 1176, 399, 165, 154, 24, 2, 1, 218, 224, + 1017, 1018, 651, 651, + 280, 111, 42, 16, 9, 3, 1, 1, 1, 1, + 787, 469, 324, 269, 229, 686, 603, + 267, 194, + }, + { 1319, 530, 255, 268, 47, 4, 1, 113, 183, + 1149, 1150, 461, 461, + 173, 58, 17, 5, 3, 1, 1, 1, 1, 1, + 768, 450, 305, 261, 221, 716, 835, + 136, 97, + }, + { 1362, 669, 355, 465, 104, 9, 1, 76, 153, + 1253, 1253, 398, 397, + 102, 21, 5, 1, 1, 1, 1, 1, 1, 1, + 596, 371, 238, 228, 196, 660, 954, + 68, 53, + }, + { 1354, 741, 446, 702, 174, 15, 1, 38, 87, + 1498, 1498, 294, 294, + 43, 7, 1, 1, 1, 1, 1, 1, 1, 1, + 381, 283, 165, 181, 155, 544, 1039, + 25, 21, + }, + { 1262, 885, 546, 947, 263, 18, 1, 18, 27, + 1908, 1908, 163, 162, + 14, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 195, 152, 83, 125, 109, 361, 827, + 7, 5, + }, + { 2539, 951, 369, 554, 212, 18, 1, 1, 1, + 2290, 2289, 64, 64, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 18, 18, 9, 55, 36, 184, 323, + 1, 1, + }, + + + /* AC Intra bias group 4 tables */ + { 921, 264, 101, 100, 19, 2, 1, 331, 98, + 1015, 1016, 799, 799, + 512, 269, 119, 60, 50, 17, 1, 1, 1, 1, + 841, 442, 307, 222, 182, 493, 256, + 438, 310, + }, + { 1147, 412, 184, 206, 50, 6, 1, 242, 141, + 977, 976, 808, 807, + 377, 135, 40, 10, 7, 1, 1, 1, 1, 1, + 788, 402, 308, 223, 205, 584, 406, + 316, 227, + }, + { 1243, 504, 238, 310, 79, 11, 1, 184, 150, + 983, 984, 814, 813, + 285, 56, 10, 1, 1, 1, 1, 1, 1, 1, + 713, 377, 287, 217, 180, 615, 558, + 208, 164, + }, + { 1266, 606, 329, 484, 161, 27, 1, 79, 92, + 1187, 1188, 589, 588, + 103, 10, 1, 1, 1, 1, 1, 1, 1, 1, + 680, 371, 278, 221, 244, 614, 728, + 80, 62, + }, + { 1126, 828, 435, 705, 443, 90, 8, 10, 55, + 1220, 1219, 350, 350, + 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 602, 330, 222, 168, 158, 612, 919, + 104, 5, + }, + { 1210, 506, 1014, 926, 474, 240, 4, 1, 44, + 1801, 1801, 171, 171, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 900, 132, 36, 11, 47, 191, 316, + 2, 1, + }, + { 1210, 506, 1014, 926, 474, 240, 4, 1, 44, + 1801, 1801, 171, 171, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 900, 132, 36, 11, 47, 191, 316, + 2, 1, + }, + { 1210, 506, 1014, 926, 474, 240, 4, 1, 44, + 1801, 1801, 171, 171, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 900, 132, 36, 11, 47, 191, 316, + 2, 1, + }, + /* AC Inter bias group 4 tables */ + { 1064, 325, 129, 117, 20, 2, 1, 266, 121, + 1000, 1000, 706, 706, + 348, 162, 67, 32, 25, 11, 1, 1, 1, 1, + 876, 513, 363, 274, 225, 627, 384, + 370, 251, + }, + { 1311, 517, 238, 254, 45, 3, 1, 188, 160, + 1070, 1070, 635, 635, + 239, 85, 30, 11, 6, 1, 1, 1, 1, 1, + 744, 420, 313, 239, 206, 649, 541, + 221, 155, + }, + { 1394, 632, 322, 385, 78, 7, 1, 134, 152, + 1163, 1164, 607, 607, + 185, 51, 12, 3, 1, 1, 1, 1, 1, 1, + 631, 331, 275, 203, 182, 604, 620, + 146, 98, + }, + { 1410, 727, 407, 546, 146, 19, 1, 67, 88, + 1485, 1486, 419, 418, + 103, 18, 3, 1, 1, 1, 1, 1, 1, 1, + 555, 261, 234, 164, 148, 522, 654, + 67, 39, + }, + { 1423, 822, 492, 719, 216, 22, 1, 28, 59, + 1793, 1793, 323, 324, + 37, 2, 1, 1, 1, 1, 1, 1, 1, 1, + 376, 138, 158, 102, 119, 400, 604, + 28, 9, + }, + { 1585, 923, 563, 918, 207, 25, 1, 5, 20, + 2229, 2230, 172, 172, + 7, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 191, 40, 56, 22, 65, 243, 312, + 2, 1, + }, + { 2225, 1100, 408, 608, 133, 8, 1, 1, 1, + 2658, 2658, 25, 24, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 8, 1, 1, 1, 1, 125, 16, + 1, 1, + }, + { 2539, 951, 369, 554, 212, 18, 1, 1, 1, + 2290, 2289, 64, 64, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 18, 18, 9, 55, 36, 184, 323, + 1, 1, + }, +}; + +#endif /* NEW_FREQS */ diff --git a/Engine/lib/libtheora/lib/enc/mcomp.c b/Engine/lib/libtheora/lib/enc/mcomp.c new file mode 100644 index 000000000..3b6b4ac28 --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/mcomp.c @@ -0,0 +1,767 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: mcomp.c 15153 2008-08-04 18:37:55Z tterribe $ + + ********************************************************************/ + +#include +#include +#include "codec_internal.h" + +/* Initialises motion compentsation. */ +void InitMotionCompensation ( CP_INSTANCE *cpi ){ + int i; + int SearchSite=0; + int Len; + int LineStepY = (ogg_int32_t)cpi->pb.YStride; + + Len=((MAX_MV_EXTENT/2)+1)/2; + + + /* How many search stages are there. */ + cpi->MVSearchSteps = 0; + + /* Set up offsets arrays used in half pixel correction. */ + cpi->HalfPixelRef2Offset[0] = -LineStepY - 1; + cpi->HalfPixelRef2Offset[1] = -LineStepY; + cpi->HalfPixelRef2Offset[2] = -LineStepY + 1; + cpi->HalfPixelRef2Offset[3] = - 1; + cpi->HalfPixelRef2Offset[4] = 0; + cpi->HalfPixelRef2Offset[5] = 1; + cpi->HalfPixelRef2Offset[6] = LineStepY - 1; + cpi->HalfPixelRef2Offset[7] = LineStepY; + cpi->HalfPixelRef2Offset[8] = LineStepY + 1; + + cpi->HalfPixelXOffset[0] = -1; + cpi->HalfPixelXOffset[1] = 0; + cpi->HalfPixelXOffset[2] = 1; + cpi->HalfPixelXOffset[3] = -1; + cpi->HalfPixelXOffset[4] = 0; + cpi->HalfPixelXOffset[5] = 1; + cpi->HalfPixelXOffset[6] = -1; + cpi->HalfPixelXOffset[7] = 0; + cpi->HalfPixelXOffset[8] = 1; + + cpi->HalfPixelYOffset[0] = -1; + cpi->HalfPixelYOffset[1] = -1; + cpi->HalfPixelYOffset[2] = -1; + cpi->HalfPixelYOffset[3] = 0; + cpi->HalfPixelYOffset[4] = 0; + cpi->HalfPixelYOffset[5] = 0; + cpi->HalfPixelYOffset[6] = 1; + cpi->HalfPixelYOffset[7] = 1; + cpi->HalfPixelYOffset[8] = 1; + + + /* Generate offsets for 8 search sites per step. */ + while ( Len>0 ) { + /* Another step. */ + cpi->MVSearchSteps += 1; + + /* Compute offsets for search sites. */ + cpi->MVOffsetX[SearchSite] = -Len; + cpi->MVOffsetY[SearchSite++] = -Len; + cpi->MVOffsetX[SearchSite] = 0; + cpi->MVOffsetY[SearchSite++] = -Len; + cpi->MVOffsetX[SearchSite] = Len; + cpi->MVOffsetY[SearchSite++] = -Len; + cpi->MVOffsetX[SearchSite] = -Len; + cpi->MVOffsetY[SearchSite++] = 0; + cpi->MVOffsetX[SearchSite] = Len; + cpi->MVOffsetY[SearchSite++] = 0; + cpi->MVOffsetX[SearchSite] = -Len; + cpi->MVOffsetY[SearchSite++] = Len; + cpi->MVOffsetX[SearchSite] = 0; + cpi->MVOffsetY[SearchSite++] = Len; + cpi->MVOffsetX[SearchSite] = Len; + cpi->MVOffsetY[SearchSite++] = Len; + + /* Contract. */ + Len /= 2; + } + + /* Compute pixel index offsets. */ + for ( i=SearchSite-1; i>=0; i-- ) + cpi->MVPixelOffsetY[i] = (cpi->MVOffsetY[i]*LineStepY) + cpi->MVOffsetX[i]; +} + +static ogg_uint32_t GetInterErr (CP_INSTANCE *cpi, unsigned char * NewDataPtr, + unsigned char * RefDataPtr1, + unsigned char * RefDataPtr2, + ogg_uint32_t PixelsPerLine ) { + ogg_int32_t DiffVal; + ogg_int32_t RefOffset = (int)(RefDataPtr1 - RefDataPtr2); + ogg_uint32_t RefPixelsPerLine = PixelsPerLine + STRIDE_EXTRA; + + /* Mode of interpolation chosen based upon on the offset of the + second reference pointer */ + if ( RefOffset == 0 ) { + DiffVal = dsp_inter8x8_err (cpi->dsp, NewDataPtr, PixelsPerLine, + RefDataPtr1, RefPixelsPerLine); + }else{ + DiffVal = dsp_inter8x8_err_xy2 (cpi->dsp, NewDataPtr, PixelsPerLine, + RefDataPtr1, + RefDataPtr2, RefPixelsPerLine); + } + + /* Compute and return population variance as mis-match metric. */ + return DiffVal; +} + +static ogg_uint32_t GetHalfPixelSumAbsDiffs (CP_INSTANCE *cpi, + unsigned char * SrcData, + unsigned char * RefDataPtr1, + unsigned char * RefDataPtr2, + ogg_uint32_t PixelsPerLine, + ogg_uint32_t ErrorSoFar, + ogg_uint32_t BestSoFar ) { + + ogg_uint32_t DiffVal = ErrorSoFar; + ogg_int32_t RefOffset = (int)(RefDataPtr1 - RefDataPtr2); + ogg_uint32_t RefPixelsPerLine = PixelsPerLine + STRIDE_EXTRA; + + if ( RefOffset == 0 ) { + /* Simple case as for non 0.5 pixel */ + DiffVal += dsp_sad8x8 (cpi->dsp, SrcData, PixelsPerLine, + RefDataPtr1, RefPixelsPerLine); + } else { + DiffVal += dsp_sad8x8_xy2_thres (cpi->dsp, SrcData, PixelsPerLine, + RefDataPtr1, + RefDataPtr2, RefPixelsPerLine, BestSoFar); + } + + return DiffVal; +} + +ogg_uint32_t GetMBIntraError (CP_INSTANCE *cpi, ogg_uint32_t FragIndex, + ogg_uint32_t PixelsPerLine ) { + ogg_uint32_t LocalFragIndex = FragIndex; + ogg_uint32_t IntraError = 0; + + dsp_save_fpu (cpi->dsp); + + /* Add together the intra errors for those blocks in the macro block + that are coded (Y only) */ + if ( cpi->pb.display_fragments[LocalFragIndex] ) + IntraError += + dsp_intra8x8_err (cpi->dsp, &cpi-> + ConvDestBuffer[cpi->pb.pixel_index_table[LocalFragIndex]], + PixelsPerLine); + + LocalFragIndex++; + if ( cpi->pb.display_fragments[LocalFragIndex] ) + IntraError += + dsp_intra8x8_err (cpi->dsp, &cpi-> + ConvDestBuffer[cpi->pb.pixel_index_table[LocalFragIndex]], + PixelsPerLine); + + LocalFragIndex = FragIndex + cpi->pb.HFragments; + if ( cpi->pb.display_fragments[LocalFragIndex] ) + IntraError += + dsp_intra8x8_err (cpi->dsp, &cpi-> + ConvDestBuffer[cpi->pb.pixel_index_table[LocalFragIndex]], + PixelsPerLine); + + LocalFragIndex++; + if ( cpi->pb.display_fragments[LocalFragIndex] ) + IntraError += + dsp_intra8x8_err (cpi->dsp, &cpi-> + ConvDestBuffer[cpi->pb.pixel_index_table[LocalFragIndex]], + PixelsPerLine); + + dsp_restore_fpu (cpi->dsp); + + return IntraError; +} + +ogg_uint32_t GetMBInterError (CP_INSTANCE *cpi, + unsigned char * SrcPtr, + unsigned char * RefPtr, + ogg_uint32_t FragIndex, + ogg_int32_t LastXMV, + ogg_int32_t LastYMV, + ogg_uint32_t PixelsPerLine ) { + ogg_uint32_t RefPixelsPerLine = cpi->pb.YStride; + ogg_uint32_t LocalFragIndex = FragIndex; + ogg_int32_t PixelIndex; + ogg_int32_t RefPixelIndex; + ogg_int32_t RefPixelOffset; + ogg_int32_t RefPtr2Offset; + + ogg_uint32_t InterError = 0; + + unsigned char * SrcPtr1; + unsigned char * RefPtr1; + + dsp_save_fpu (cpi->dsp); + + /* Work out pixel offset into source buffer. */ + PixelIndex = cpi->pb.pixel_index_table[LocalFragIndex]; + + /* Work out the pixel offset in reference buffer for the default + motion vector */ + RefPixelIndex = cpi->pb.recon_pixel_index_table[LocalFragIndex]; + RefPixelOffset = ((LastYMV/2) * RefPixelsPerLine) + (LastXMV/2); + + /* Work out the second reference pointer offset. */ + RefPtr2Offset = 0; + if ( LastXMV % 2 ) { + if ( LastXMV > 0 ) + RefPtr2Offset += 1; + else + RefPtr2Offset -= 1; + } + if ( LastYMV % 2 ) { + if ( LastYMV > 0 ) + RefPtr2Offset += RefPixelsPerLine; + else + RefPtr2Offset -= RefPixelsPerLine; + } + + /* Add together the errors for those blocks in the macro block that + are coded (Y only) */ + if ( cpi->pb.display_fragments[LocalFragIndex] ) { + SrcPtr1 = &SrcPtr[PixelIndex]; + RefPtr1 = &RefPtr[RefPixelIndex + RefPixelOffset]; + InterError += GetInterErr(cpi, SrcPtr1, RefPtr1, + &RefPtr1[RefPtr2Offset], PixelsPerLine ); + } + + LocalFragIndex++; + if ( cpi->pb.display_fragments[LocalFragIndex] ) { + PixelIndex = cpi->pb.pixel_index_table[LocalFragIndex]; + RefPixelIndex = cpi->pb.recon_pixel_index_table[LocalFragIndex]; + SrcPtr1 = &SrcPtr[PixelIndex]; + RefPtr1 = &RefPtr[RefPixelIndex + RefPixelOffset]; + InterError += GetInterErr(cpi, SrcPtr1, RefPtr1, + &RefPtr1[RefPtr2Offset], PixelsPerLine ); + + } + + LocalFragIndex = FragIndex + cpi->pb.HFragments; + if ( cpi->pb.display_fragments[LocalFragIndex] ) { + PixelIndex = cpi->pb.pixel_index_table[LocalFragIndex]; + RefPixelIndex = cpi->pb.recon_pixel_index_table[LocalFragIndex]; + SrcPtr1 = &SrcPtr[PixelIndex]; + RefPtr1 = &RefPtr[RefPixelIndex + RefPixelOffset]; + InterError += GetInterErr(cpi, SrcPtr1, RefPtr1, + &RefPtr1[RefPtr2Offset], PixelsPerLine ); + } + + LocalFragIndex++; + if ( cpi->pb.display_fragments[LocalFragIndex] ) { + PixelIndex = cpi->pb.pixel_index_table[LocalFragIndex]; + RefPixelIndex = cpi->pb.recon_pixel_index_table[LocalFragIndex]; + SrcPtr1 = &SrcPtr[PixelIndex]; + RefPtr1 = &RefPtr[RefPixelIndex + RefPixelOffset]; + InterError += GetInterErr(cpi, SrcPtr1, RefPtr1, + &RefPtr1[RefPtr2Offset], PixelsPerLine ); + } + + dsp_restore_fpu (cpi->dsp); + + return InterError; +} + +ogg_uint32_t GetMBMVInterError (CP_INSTANCE *cpi, + unsigned char * RefFramePtr, + ogg_uint32_t FragIndex, + ogg_uint32_t PixelsPerLine, + ogg_int32_t *MVPixelOffset, + MOTION_VECTOR *MV ) { + ogg_uint32_t Error = 0; + ogg_uint32_t MinError; + ogg_uint32_t InterMVError = 0; + + ogg_int32_t i; + ogg_int32_t x=0, y=0; + ogg_int32_t step; + ogg_int32_t SearchSite=0; + + unsigned char *SrcPtr[4] = {NULL,NULL,NULL,NULL}; + unsigned char *RefPtr=NULL; + unsigned char *CandidateBlockPtr=NULL; + unsigned char *BestBlockPtr=NULL; + + ogg_uint32_t RefRow2Offset = cpi->pb.YStride * 8; + + int MBlockDispFrags[4]; + + /* Half pixel variables */ + ogg_int32_t HalfPixelError; + ogg_int32_t BestHalfPixelError; + unsigned char BestHalfOffset; + unsigned char * RefDataPtr1; + unsigned char * RefDataPtr2; + + dsp_save_fpu (cpi->dsp); + + /* Note which of the four blocks in the macro block are to be + included in the search. */ + MBlockDispFrags[0] = + cpi->pb.display_fragments[FragIndex]; + MBlockDispFrags[1] = + cpi->pb.display_fragments[FragIndex + 1]; + MBlockDispFrags[2] = + cpi->pb.display_fragments[FragIndex + cpi->pb.HFragments]; + MBlockDispFrags[3] = + cpi->pb.display_fragments[FragIndex + cpi->pb.HFragments + 1]; + + /* Set up the source pointers for the four source blocks. */ + SrcPtr[0] = &cpi->ConvDestBuffer[cpi->pb.pixel_index_table[FragIndex]]; + SrcPtr[1] = SrcPtr[0] + 8; + SrcPtr[2] = SrcPtr[0] + (PixelsPerLine * 8); + SrcPtr[3] = SrcPtr[2] + 8; + + /* Set starting reference point for search. */ + RefPtr = &RefFramePtr[cpi->pb.recon_pixel_index_table[FragIndex]]; + + /* Check the 0,0 candidate. */ + if ( MBlockDispFrags[0] ) { + Error += dsp_sad8x8 (cpi->dsp, SrcPtr[0], PixelsPerLine, RefPtr, + PixelsPerLine + STRIDE_EXTRA); + } + if ( MBlockDispFrags[1] ) { + Error += dsp_sad8x8 (cpi->dsp, SrcPtr[1], PixelsPerLine, RefPtr + 8, + PixelsPerLine + STRIDE_EXTRA); + } + if ( MBlockDispFrags[2] ) { + Error += dsp_sad8x8 (cpi->dsp, SrcPtr[2], PixelsPerLine, RefPtr + RefRow2Offset, + PixelsPerLine + STRIDE_EXTRA); + } + if ( MBlockDispFrags[3] ) { + Error += dsp_sad8x8 (cpi->dsp, SrcPtr[3], PixelsPerLine, RefPtr + RefRow2Offset + 8, + PixelsPerLine + STRIDE_EXTRA); + } + + /* Set starting values to results of 0, 0 vector. */ + MinError = Error; + BestBlockPtr = RefPtr; + x = 0; + y = 0; + MV->x = 0; + MV->y = 0; + + /* Proceed through N-steps. */ + for ( step=0; stepMVSearchSteps; step++ ) { + /* Search the 8-neighbours at distance pertinent to current step.*/ + for ( i=0; i<8; i++ ) { + /* Set pointer to next candidate matching block. */ + CandidateBlockPtr = RefPtr + MVPixelOffset[SearchSite]; + + /* Reset error */ + Error = 0; + + /* Get the score for the current offset */ + if ( MBlockDispFrags[0] ) { + Error += dsp_sad8x8 (cpi->dsp, SrcPtr[0], PixelsPerLine, CandidateBlockPtr, + PixelsPerLine + STRIDE_EXTRA); + } + + if ( MBlockDispFrags[1] && (Error < MinError) ) { + Error += dsp_sad8x8_thres (cpi->dsp, SrcPtr[1], PixelsPerLine, CandidateBlockPtr + 8, + PixelsPerLine + STRIDE_EXTRA, MinError); + } + + if ( MBlockDispFrags[2] && (Error < MinError) ) { + Error += dsp_sad8x8_thres (cpi->dsp, SrcPtr[2], PixelsPerLine, CandidateBlockPtr + RefRow2Offset, + PixelsPerLine + STRIDE_EXTRA, MinError); + } + + if ( MBlockDispFrags[3] && (Error < MinError) ) { + Error += dsp_sad8x8_thres (cpi->dsp, SrcPtr[3], PixelsPerLine, CandidateBlockPtr + RefRow2Offset + 8, + PixelsPerLine + STRIDE_EXTRA, MinError); + } + + if ( Error < MinError ) { + /* Remember best match. */ + MinError = Error; + BestBlockPtr = CandidateBlockPtr; + + /* Where is it. */ + x = MV->x + cpi->MVOffsetX[SearchSite]; + y = MV->y + cpi->MVOffsetY[SearchSite]; + } + + /* Move to next search location. */ + SearchSite += 1; + } + + /* Move to best location this step. */ + RefPtr = BestBlockPtr; + MV->x = x; + MV->y = y; + } + + /* Factor vectors to 1/2 pixel resoultion. */ + MV->x = (MV->x * 2); + MV->y = (MV->y * 2); + + /* Now do the half pixel pass */ + BestHalfOffset = 4; /* Default to the no offset case. */ + BestHalfPixelError = MinError; + + /* Get the half pixel error for each half pixel offset */ + for ( i=0; i < 9; i++ ) { + HalfPixelError = 0; + + if ( MBlockDispFrags[0] ) { + RefDataPtr1 = BestBlockPtr; + RefDataPtr2 = RefDataPtr1 + cpi->HalfPixelRef2Offset[i]; + HalfPixelError = + GetHalfPixelSumAbsDiffs(cpi, SrcPtr[0], RefDataPtr1, RefDataPtr2, + PixelsPerLine, HalfPixelError, BestHalfPixelError ); + } + + if ( MBlockDispFrags[1] && (HalfPixelError < BestHalfPixelError) ) { + RefDataPtr1 = BestBlockPtr + 8; + RefDataPtr2 = RefDataPtr1 + cpi->HalfPixelRef2Offset[i]; + HalfPixelError = + GetHalfPixelSumAbsDiffs(cpi, SrcPtr[1], RefDataPtr1, RefDataPtr2, + PixelsPerLine, HalfPixelError, BestHalfPixelError ); + } + + if ( MBlockDispFrags[2] && (HalfPixelError < BestHalfPixelError) ) { + RefDataPtr1 = BestBlockPtr + RefRow2Offset; + RefDataPtr2 = RefDataPtr1 + cpi->HalfPixelRef2Offset[i]; + HalfPixelError = + GetHalfPixelSumAbsDiffs(cpi, SrcPtr[2], RefDataPtr1, RefDataPtr2, + PixelsPerLine, HalfPixelError, BestHalfPixelError ); + } + + if ( MBlockDispFrags[3] && (HalfPixelError < BestHalfPixelError) ) { + RefDataPtr1 = BestBlockPtr + RefRow2Offset + 8; + RefDataPtr2 = RefDataPtr1 + cpi->HalfPixelRef2Offset[i]; + HalfPixelError = + GetHalfPixelSumAbsDiffs(cpi, SrcPtr[3], RefDataPtr1, RefDataPtr2, + PixelsPerLine, HalfPixelError, BestHalfPixelError ); + } + + if ( HalfPixelError < BestHalfPixelError ) { + BestHalfOffset = (unsigned char)i; + BestHalfPixelError = HalfPixelError; + } + } + + /* Half pixel adjust the MV */ + MV->x += cpi->HalfPixelXOffset[BestHalfOffset]; + MV->y += cpi->HalfPixelYOffset[BestHalfOffset]; + + /* Get the error score for the chosen 1/2 pixel offset as a variance. */ + InterMVError = GetMBInterError( cpi, cpi->ConvDestBuffer, RefFramePtr, + FragIndex, MV->x, MV->y, PixelsPerLine ); + + dsp_restore_fpu (cpi->dsp); + + /* Return score of best matching block. */ + return InterMVError; +} + +ogg_uint32_t GetMBMVExhaustiveSearch (CP_INSTANCE *cpi, + unsigned char * RefFramePtr, + ogg_uint32_t FragIndex, + ogg_uint32_t PixelsPerLine, + MOTION_VECTOR *MV ) { + ogg_uint32_t Error = 0; + ogg_uint32_t MinError = HUGE_ERROR; + ogg_uint32_t InterMVError = 0; + + ogg_int32_t i, j; + ogg_int32_t x=0, y=0; + + unsigned char *SrcPtr[4] = {NULL,NULL,NULL,NULL}; + unsigned char *RefPtr; + unsigned char *CandidateBlockPtr=NULL; + unsigned char *BestBlockPtr=NULL; + + ogg_uint32_t RefRow2Offset = cpi->pb.YStride * 8; + + int MBlockDispFrags[4]; + + /* Half pixel variables */ + ogg_int32_t HalfPixelError; + ogg_int32_t BestHalfPixelError; + unsigned char BestHalfOffset; + unsigned char * RefDataPtr1; + unsigned char * RefDataPtr2; + + dsp_save_fpu (cpi->dsp); + + /* Note which of the four blocks in the macro block are to be + included in the search. */ + MBlockDispFrags[0] = cpi-> + pb.display_fragments[FragIndex]; + MBlockDispFrags[1] = cpi-> + pb.display_fragments[FragIndex + 1]; + MBlockDispFrags[2] = cpi-> + pb.display_fragments[FragIndex + cpi->pb.HFragments]; + MBlockDispFrags[3] = cpi-> + pb.display_fragments[FragIndex + cpi->pb.HFragments + 1]; + + /* Set up the source pointers for the four source blocks. */ + SrcPtr[0] = &cpi-> + ConvDestBuffer[cpi->pb.pixel_index_table[FragIndex]]; + SrcPtr[1] = SrcPtr[0] + 8; + SrcPtr[2] = SrcPtr[0] + (PixelsPerLine * 8); + SrcPtr[3] = SrcPtr[2] + 8; + + RefPtr = &RefFramePtr[cpi->pb.recon_pixel_index_table[FragIndex]]; + RefPtr = RefPtr - ((MAX_MV_EXTENT/2) * cpi-> + pb.YStride) - (MAX_MV_EXTENT/2); + + /* Search each pixel alligned site */ + for ( i = 0; i < (ogg_int32_t)MAX_MV_EXTENT; i ++ ) { + /* Starting position in row */ + CandidateBlockPtr = RefPtr; + + for ( j = 0; j < (ogg_int32_t)MAX_MV_EXTENT; j++ ) { + /* Reset error */ + Error = 0; + + /* Summ errors for each block. */ + if ( MBlockDispFrags[0] ) { + Error += dsp_sad8x8 (cpi->dsp, SrcPtr[0], PixelsPerLine, CandidateBlockPtr, + PixelsPerLine + STRIDE_EXTRA); + } + if ( MBlockDispFrags[1] ){ + Error += dsp_sad8x8 (cpi->dsp, SrcPtr[1], PixelsPerLine, CandidateBlockPtr + 8, + PixelsPerLine + STRIDE_EXTRA); + } + if ( MBlockDispFrags[2] ){ + Error += dsp_sad8x8 (cpi->dsp, SrcPtr[2], PixelsPerLine, CandidateBlockPtr + RefRow2Offset, + PixelsPerLine + STRIDE_EXTRA); + } + if ( MBlockDispFrags[3] ){ + Error += dsp_sad8x8 (cpi->dsp, SrcPtr[3], PixelsPerLine, CandidateBlockPtr + RefRow2Offset + 8, + PixelsPerLine + STRIDE_EXTRA); + } + + /* Was this the best so far */ + if ( Error < MinError ) { + MinError = Error; + BestBlockPtr = CandidateBlockPtr; + x = 16 + j - MAX_MV_EXTENT; + y = 16 + i - MAX_MV_EXTENT; + } + + /* Move the the next site */ + CandidateBlockPtr ++; + } + + /* Move on to the next row. */ + RefPtr += cpi->pb.YStride; + + } + + /* Factor vectors to 1/2 pixel resoultion. */ + MV->x = (x * 2); + MV->y = (y * 2); + + /* Now do the half pixel pass */ + BestHalfOffset = 4; /* Default to the no offset case. */ + BestHalfPixelError = MinError; + + /* Get the half pixel error for each half pixel offset */ + for ( i=0; i < 9; i++ ) { + HalfPixelError = 0; + + if ( MBlockDispFrags[0] ) { + RefDataPtr1 = BestBlockPtr; + RefDataPtr2 = RefDataPtr1 + cpi->HalfPixelRef2Offset[i]; + HalfPixelError = + GetHalfPixelSumAbsDiffs(cpi, SrcPtr[0], RefDataPtr1, RefDataPtr2, + PixelsPerLine, HalfPixelError, BestHalfPixelError ); + } + + if ( MBlockDispFrags[1] && (HalfPixelError < BestHalfPixelError) ) { + RefDataPtr1 = BestBlockPtr + 8; + RefDataPtr2 = RefDataPtr1 + cpi->HalfPixelRef2Offset[i]; + HalfPixelError = + GetHalfPixelSumAbsDiffs(cpi, SrcPtr[1], RefDataPtr1, RefDataPtr2, + PixelsPerLine, HalfPixelError, BestHalfPixelError ); + } + + if ( MBlockDispFrags[2] && (HalfPixelError < BestHalfPixelError) ) { + RefDataPtr1 = BestBlockPtr + RefRow2Offset; + RefDataPtr2 = RefDataPtr1 + cpi->HalfPixelRef2Offset[i]; + HalfPixelError = + GetHalfPixelSumAbsDiffs(cpi, SrcPtr[2], RefDataPtr1, RefDataPtr2, + PixelsPerLine, HalfPixelError, BestHalfPixelError ); + } + + if ( MBlockDispFrags[3] && (HalfPixelError < BestHalfPixelError) ) { + RefDataPtr1 = BestBlockPtr + RefRow2Offset + 8; + RefDataPtr2 = RefDataPtr1 + cpi->HalfPixelRef2Offset[i]; + HalfPixelError = + GetHalfPixelSumAbsDiffs(cpi, SrcPtr[3], RefDataPtr1, RefDataPtr2, + PixelsPerLine, HalfPixelError, BestHalfPixelError ); + } + + if ( HalfPixelError < BestHalfPixelError ){ + BestHalfOffset = (unsigned char)i; + BestHalfPixelError = HalfPixelError; + } + } + + /* Half pixel adjust the MV */ + MV->x += cpi->HalfPixelXOffset[BestHalfOffset]; + MV->y += cpi->HalfPixelYOffset[BestHalfOffset]; + + /* Get the error score for the chosen 1/2 pixel offset as a variance. */ + InterMVError = GetMBInterError( cpi, cpi->ConvDestBuffer, RefFramePtr, + FragIndex, MV->x, MV->y, PixelsPerLine ); + + dsp_restore_fpu (cpi->dsp); + + /* Return score of best matching block. */ + return InterMVError; +} + +static ogg_uint32_t GetBMVExhaustiveSearch (CP_INSTANCE *cpi, + unsigned char * RefFramePtr, + ogg_uint32_t FragIndex, + ogg_uint32_t PixelsPerLine, + MOTION_VECTOR *MV ) { + ogg_uint32_t Error = 0; + ogg_uint32_t MinError = HUGE_ERROR; + ogg_uint32_t InterMVError = 0; + + ogg_int32_t i, j; + ogg_int32_t x=0, y=0; + + unsigned char *SrcPtr = NULL; + unsigned char *RefPtr; + unsigned char *CandidateBlockPtr=NULL; + unsigned char *BestBlockPtr=NULL; + + /* Half pixel variables */ + ogg_int32_t HalfPixelError; + ogg_int32_t BestHalfPixelError; + unsigned char BestHalfOffset; + unsigned char * RefDataPtr2; + + /* Set up the source pointer for the block. */ + SrcPtr = &cpi-> + ConvDestBuffer[cpi->pb.pixel_index_table[FragIndex]]; + + RefPtr = &RefFramePtr[cpi->pb.recon_pixel_index_table[FragIndex]]; + RefPtr = RefPtr - ((MAX_MV_EXTENT/2) * + cpi->pb.YStride) - (MAX_MV_EXTENT/2); + + /* Search each pixel alligned site */ + for ( i = 0; i < (ogg_int32_t)MAX_MV_EXTENT; i ++ ) { + /* Starting position in row */ + CandidateBlockPtr = RefPtr; + + for ( j = 0; j < (ogg_int32_t)MAX_MV_EXTENT; j++ ){ + /* Get the block error score. */ + Error = dsp_sad8x8 (cpi->dsp, SrcPtr, PixelsPerLine, CandidateBlockPtr, + PixelsPerLine + STRIDE_EXTRA); + + /* Was this the best so far */ + if ( Error < MinError ) { + MinError = Error; + BestBlockPtr = CandidateBlockPtr; + x = 16 + j - MAX_MV_EXTENT; + y = 16 + i - MAX_MV_EXTENT; + } + + /* Move the the next site */ + CandidateBlockPtr ++; + } + + /* Move on to the next row. */ + RefPtr += cpi->pb.YStride; + } + + /* Factor vectors to 1/2 pixel resoultion. */ + MV->x = (x * 2); + MV->y = (y * 2); + + /* Now do the half pixel pass */ + BestHalfOffset = 4; /* Default to the no offset case. */ + BestHalfPixelError = MinError; + + /* Get the half pixel error for each half pixel offset */ + for ( i=0; i < 9; i++ ) { + RefDataPtr2 = BestBlockPtr + cpi->HalfPixelRef2Offset[i]; + HalfPixelError = + GetHalfPixelSumAbsDiffs(cpi, SrcPtr, BestBlockPtr, RefDataPtr2, + PixelsPerLine, 0, BestHalfPixelError ); + + if ( HalfPixelError < BestHalfPixelError ){ + BestHalfOffset = (unsigned char)i; + BestHalfPixelError = HalfPixelError; + } + } + + /* Half pixel adjust the MV */ + MV->x += cpi->HalfPixelXOffset[BestHalfOffset]; + MV->y += cpi->HalfPixelYOffset[BestHalfOffset]; + + /* Get the variance score at the chosen offset */ + RefDataPtr2 = BestBlockPtr + cpi->HalfPixelRef2Offset[BestHalfOffset]; + + InterMVError = + GetInterErr(cpi, SrcPtr, BestBlockPtr, RefDataPtr2, PixelsPerLine ); + + /* Return score of best matching block. */ + return InterMVError; +} + +ogg_uint32_t GetFOURMVExhaustiveSearch (CP_INSTANCE *cpi, + unsigned char * RefFramePtr, + ogg_uint32_t FragIndex, + ogg_uint32_t PixelsPerLine, + MOTION_VECTOR *MV ) { + ogg_uint32_t InterMVError; + + dsp_save_fpu (cpi->dsp); + + /* For the moment the 4MV mode is only deemed to be valid + if all four Y blocks are to be updated */ + /* This may be adapted later. */ + if ( cpi->pb.display_fragments[FragIndex] && + cpi->pb.display_fragments[FragIndex + 1] && + cpi->pb.display_fragments[FragIndex + cpi->pb.HFragments] && + cpi->pb.display_fragments[FragIndex + cpi->pb.HFragments + 1] ) { + + /* Reset the error score. */ + InterMVError = 0; + + /* Get the error component from each coded block */ + InterMVError += + GetBMVExhaustiveSearch(cpi, RefFramePtr, FragIndex, + PixelsPerLine, &(MV[0]) ); + InterMVError += + GetBMVExhaustiveSearch(cpi, RefFramePtr, (FragIndex + 1), + PixelsPerLine, &(MV[1]) ); + InterMVError += + GetBMVExhaustiveSearch(cpi, RefFramePtr, + (FragIndex + cpi->pb.HFragments), + PixelsPerLine, &(MV[2]) ); + InterMVError += + GetBMVExhaustiveSearch(cpi, RefFramePtr, + (FragIndex + cpi->pb.HFragments + 1), + PixelsPerLine, &(MV[3]) ); + }else{ + InterMVError = HUGE_ERROR; + } + + dsp_restore_fpu (cpi->dsp); + + /* Return score of best matching block. */ + return InterMVError; +} + diff --git a/Engine/lib/libtheora/lib/enc/misc_common.c b/Engine/lib/libtheora/lib/enc/misc_common.c new file mode 100644 index 000000000..1536a494a --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/misc_common.c @@ -0,0 +1,339 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: misc_common.c 15323 2008-09-19 19:43:59Z giles $ + + ********************************************************************/ + +#include +#include "codec_internal.h" +#include "block_inline.h" + +#define FIXED_Q 150 +#define MAX_UP_REG_LOOPS 2 + +/* Gives the initial bytes per block estimate for each Q value */ +static const double BpbTable[Q_TABLE_SIZE] = { + 0.42, 0.45, 0.46, 0.49, 0.51, 0.53, 0.56, 0.58, + 0.61, 0.64, 0.68, 0.71, 0.74, 0.77, 0.80, 0.84, + 0.89, 0.92, 0.98, 1.01, 1.04, 1.13, 1.17, 1.23, + 1.28, 1.34, 1.41, 1.45, 1.51, 1.59, 1.69, 1.80, + 1.84, 1.94, 2.02, 2.15, 2.23, 2.34, 2.44, 2.50, + 2.69, 2.80, 2.87, 3.04, 3.16, 3.29, 3.59, 3.66, + 3.86, 3.94, 4.22, 4.50, 4.64, 4.70, 5.24, 5.34, + 5.61, 5.87, 6.11, 6.41, 6.71, 6.99, 7.36, 7.69 +}; + +static const double KfBpbTable[Q_TABLE_SIZE] = { + 0.74, 0.81, 0.88, 0.94, 1.00, 1.06, 1.14, 1.19, + 1.27, 1.34, 1.42, 1.49, 1.54, 1.59, 1.66, 1.73, + 1.80, 1.87, 1.97, 2.01, 2.08, 2.21, 2.25, 2.36, + 2.39, 2.50, 2.55, 2.65, 2.71, 2.82, 2.95, 3.01, + 3.11, 3.19, 3.31, 3.42, 3.58, 3.66, 3.78, 3.89, + 4.11, 4.26, 4.36, 4.39, 4.63, 4.76, 4.85, 5.04, + 5.26, 5.29, 5.47, 5.64, 5.76, 6.05, 6.35, 6.67, + 6.91, 7.17, 7.40, 7.56, 8.02, 8.45, 8.86, 9.38 +}; + +double GetEstimatedBpb( CP_INSTANCE *cpi, ogg_uint32_t TargetQ ){ + ogg_uint32_t i; + ogg_int32_t ThreshTableIndex = Q_TABLE_SIZE - 1; + double BytesPerBlock; + + /* Search for the Q table index that matches the given Q. */ + for ( i = 0; i < Q_TABLE_SIZE; i++ ) { + if ( TargetQ >= cpi->pb.QThreshTable[i] ) { + ThreshTableIndex = i; + break; + } + } + + /* Adjust according to Q shift and type of frame */ + if ( cpi->pb.FrameType == KEY_FRAME ) { + /* Get primary prediction */ + BytesPerBlock = KfBpbTable[ThreshTableIndex]; + } else { + /* Get primary prediction */ + BytesPerBlock = BpbTable[ThreshTableIndex]; + BytesPerBlock = BytesPerBlock * cpi->BpbCorrectionFactor; + } + + return BytesPerBlock; +} + +static void UpRegulateMB( CP_INSTANCE *cpi, ogg_uint32_t RegulationQ, + ogg_uint32_t SB, ogg_uint32_t MB, int NoCheck ) { + ogg_int32_t FragIndex; + ogg_uint32_t B; + + /* Variables used in calculating corresponding row,col and index in + UV planes */ + ogg_uint32_t UVRow; + ogg_uint32_t UVColumn; + ogg_uint32_t UVFragOffset; + + /* There may be MB's lying out of frame which must be ignored. For + these MB's Top left block will have a negative Fragment Index. */ + if ( QuadMapToMBTopLeft(cpi->pb.BlockMap, SB, MB ) >= 0 ) { + /* Up regulate the component blocks Y then UV. */ + for ( B=0; B<4; B++ ){ + FragIndex = QuadMapToIndex1( cpi->pb.BlockMap, SB, MB, B ); + + if ( ( !cpi->pb.display_fragments[FragIndex] ) && + ( (NoCheck) || (cpi->FragmentLastQ[FragIndex] > RegulationQ) ) ){ + cpi->pb.display_fragments[FragIndex] = 1; + cpi->extra_fragments[FragIndex] = 1; + cpi->FragmentLastQ[FragIndex] = RegulationQ; + cpi->MotionScore++; + } + } + + /* Check the two UV blocks */ + FragIndex = QuadMapToMBTopLeft(cpi->pb.BlockMap, SB, MB ); + + UVRow = (FragIndex / (cpi->pb.HFragments * 2)); + UVColumn = (FragIndex % cpi->pb.HFragments) / 2; + UVFragOffset = (UVRow * (cpi->pb.HFragments / 2)) + UVColumn; + + FragIndex = cpi->pb.YPlaneFragments + UVFragOffset; + if ( ( !cpi->pb.display_fragments[FragIndex] ) && + ( (NoCheck) || (cpi->FragmentLastQ[FragIndex] > RegulationQ) ) ) { + cpi->pb.display_fragments[FragIndex] = 1; + cpi->extra_fragments[FragIndex] = 1; + cpi->FragmentLastQ[FragIndex] = RegulationQ; + cpi->MotionScore++; + } + + FragIndex += cpi->pb.UVPlaneFragments; + if ( ( !cpi->pb.display_fragments[FragIndex] ) && + ( (NoCheck) || (cpi->FragmentLastQ[FragIndex] > RegulationQ) ) ) { + cpi->pb.display_fragments[FragIndex] = 1; + cpi->extra_fragments[FragIndex] = 1; + cpi->FragmentLastQ[FragIndex] = RegulationQ; + cpi->MotionScore++; + } + } +} + +static void UpRegulateBlocks (CP_INSTANCE *cpi, ogg_uint32_t RegulationQ, + ogg_int32_t RecoveryBlocks, + ogg_uint32_t * LastSB, ogg_uint32_t * LastMB ) { + + ogg_uint32_t LoopTimesRound = 0; + ogg_uint32_t MaxSB = cpi->pb.YSBRows * + cpi->pb.YSBCols; /* Tot super blocks in image */ + ogg_uint32_t SB, MB; /* Super-Block and macro block indices. */ + + /* First scan for blocks for which a residue update is outstanding. */ + while ( (cpi->MotionScore < RecoveryBlocks) && + (LoopTimesRound < MAX_UP_REG_LOOPS) ) { + LoopTimesRound++; + + for ( SB = (*LastSB); SB < MaxSB; SB++ ) { + /* Check its four Macro-Blocks */ + for ( MB=(*LastMB); MB<4; MB++ ) { + /* Mark relevant blocks for update */ + UpRegulateMB( cpi, RegulationQ, SB, MB, 0 ); + + /* Keep track of the last refresh MB. */ + (*LastMB) += 1; + if ( (*LastMB) == 4 ) + (*LastMB) = 0; + + /* Termination clause */ + if (cpi->MotionScore >= RecoveryBlocks) { + /* Make sure we don't stall at SB level */ + if ( *LastMB == 0 ) + SB++; + break; + } + } + + /* Termination clause */ + if (cpi->MotionScore >= RecoveryBlocks) + break; + } + + /* Update super block start index */ + if ( SB >= MaxSB){ + (*LastSB) = 0; + }else{ + (*LastSB) = SB; + } + } +} + +void UpRegulateDataStream (CP_INSTANCE *cpi, ogg_uint32_t RegulationQ, + ogg_int32_t RecoveryBlocks ) { + ogg_uint32_t LastPassMBPos = 0; + ogg_uint32_t StdLastMBPos = 0; + + ogg_uint32_t MaxSB = cpi->pb.YSBRows * + cpi->pb.YSBCols; /* Tot super blocks in image */ + + ogg_uint32_t SB=0; /* Super-Block index */ + ogg_uint32_t MB; /* Macro-Block index */ + + /* Decduct the number of blocks in an MB / 2 from the recover block count. + This will compensate for the fact that once we start checking an MB + we test every block in that macro block */ + if ( RecoveryBlocks > 3 ) + RecoveryBlocks -= 3; + + /* Up regulate blocks last coded at higher Q */ + UpRegulateBlocks( cpi, RegulationQ, RecoveryBlocks, + &cpi->LastEndSB, &StdLastMBPos ); + + /* If we have still not used up the minimum number of blocks and are + at the minimum Q then run through a final pass of the data to + insure that each block gets a final refresh. */ + if ( (RegulationQ == VERY_BEST_Q) && + (cpi->MotionScore < RecoveryBlocks) ) { + if ( cpi->FinalPassLastPos < MaxSB ) { + for ( SB = cpi->FinalPassLastPos; SB < MaxSB; SB++ ) { + /* Check its four Macro-Blocks */ + for ( MB=LastPassMBPos; MB<4; MB++ ) { + /* Mark relevant blocks for update */ + UpRegulateMB( cpi, RegulationQ, SB, MB, 1 ); + + /* Keep track of the last refresh MB. */ + LastPassMBPos += 1; + if ( LastPassMBPos == 4 ) { + LastPassMBPos = 0; + + /* Increment SB index */ + cpi->FinalPassLastPos += 1; + } + + /* Termination clause */ + if (cpi->MotionScore >= RecoveryBlocks) + break; + } + + /* Termination clause */ + if (cpi->MotionScore >= RecoveryBlocks) + break; + + } + } + } +} + +void RegulateQ( CP_INSTANCE *cpi, ogg_int32_t UpdateScore ) { + double PredUnitScoreBytes; + ogg_uint32_t QIndex = Q_TABLE_SIZE - 1; + ogg_uint32_t i; + + if ( UpdateScore > 0 ) { + double TargetUnitScoreBytes = (double)cpi->ThisFrameTargetBytes / + (double)UpdateScore; + double LastBitError = 10000.0; /* Silly high number */ + /* Search for the best Q for the target bitrate. */ + for ( i = 0; i < Q_TABLE_SIZE; i++ ) { + PredUnitScoreBytes = GetEstimatedBpb( cpi, cpi->pb.QThreshTable[i] ); + if ( PredUnitScoreBytes > TargetUnitScoreBytes ) { + if ( (PredUnitScoreBytes - TargetUnitScoreBytes) <= LastBitError ) { + QIndex = i; + } else { + QIndex = i - 1; + } + break; + } else { + LastBitError = TargetUnitScoreBytes - PredUnitScoreBytes; + } + } + } + + /* QIndex should now indicate the optimal Q. */ + cpi->pb.ThisFrameQualityValue = cpi->pb.QThreshTable[QIndex]; + + /* Apply range restrictions for key frames. */ + if ( cpi->pb.FrameType == KEY_FRAME ) { + if ( cpi->pb.ThisFrameQualityValue > cpi->pb.QThreshTable[20] ) + cpi->pb.ThisFrameQualityValue = cpi->pb.QThreshTable[20]; + else if ( cpi->pb.ThisFrameQualityValue < cpi->pb.QThreshTable[50] ) + cpi->pb.ThisFrameQualityValue = cpi->pb.QThreshTable[50]; + } + + /* Limit the Q value to the maximum available value */ + if (cpi->pb.ThisFrameQualityValue > + cpi->pb.QThreshTable[cpi->Configuration.ActiveMaxQ]) { + cpi->pb.ThisFrameQualityValue = + (ogg_uint32_t)cpi->pb.QThreshTable[cpi->Configuration.ActiveMaxQ]; + } + + if(cpi->FixedQ) { + if ( cpi->pb.FrameType == KEY_FRAME ) { + cpi->pb.ThisFrameQualityValue = cpi->pb.QThreshTable[43]; + cpi->pb.ThisFrameQualityValue = cpi->FixedQ; + } else { + cpi->pb.ThisFrameQualityValue = cpi->FixedQ; + } + } + + /* If the quantizer value has changed then re-initialise it */ + if ( cpi->pb.ThisFrameQualityValue != cpi->pb.LastFrameQualityValue ) { + /* Initialise quality tables. */ + UpdateQC( cpi, cpi->pb.ThisFrameQualityValue ); + cpi->pb.LastFrameQualityValue = cpi->pb.ThisFrameQualityValue; + } +} + +void CopyBackExtraFrags(CP_INSTANCE *cpi){ + ogg_uint32_t i,j; + unsigned char * SrcPtr; + unsigned char * DestPtr; + ogg_uint32_t PlaneLineStep; + ogg_uint32_t PixelIndex; + + /* Copy back for Y plane. */ + PlaneLineStep = cpi->pb.info.width; + for ( i = 0; i < cpi->pb.YPlaneFragments; i++ ) { + /* We are only interested in updated fragments. */ + if ( cpi->extra_fragments[i] ) { + /* Get the start index for the fragment. */ + PixelIndex = cpi->pb.pixel_index_table[i]; + SrcPtr = &cpi->yuv1ptr[PixelIndex]; + DestPtr = &cpi->ConvDestBuffer[PixelIndex]; + + for ( j = 0; j < VFRAGPIXELS; j++ ) { + memcpy( DestPtr, SrcPtr, HFRAGPIXELS); + + SrcPtr += PlaneLineStep; + DestPtr += PlaneLineStep; + } + } + } + + /* Now the U and V planes */ + PlaneLineStep = cpi->pb.info.width / 2; + for ( i = cpi->pb.YPlaneFragments; + i < (cpi->pb.YPlaneFragments + (2 * cpi->pb.UVPlaneFragments)) ; + i++ ) { + + /* We are only interested in updated fragments. */ + if ( cpi->extra_fragments[i] ) { + /* Get the start index for the fragment. */ + PixelIndex = cpi->pb.pixel_index_table[i]; + SrcPtr = &cpi->yuv1ptr[PixelIndex]; + DestPtr = &cpi->ConvDestBuffer[PixelIndex]; + + for ( j = 0; j < VFRAGPIXELS; j++ ) { + memcpy( DestPtr, SrcPtr, HFRAGPIXELS); + SrcPtr += PlaneLineStep; + DestPtr += PlaneLineStep; + } + } + } +} + diff --git a/Engine/lib/libtheora/lib/enc/pb.c b/Engine/lib/libtheora/lib/enc/pb.c new file mode 100644 index 000000000..42047249a --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/pb.c @@ -0,0 +1,89 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: pb.c 14372 2008-01-05 23:52:28Z giles $ + + ********************************************************************/ + +#include +#include +#include "codec_internal.h" + +void ClearTmpBuffers(PB_INSTANCE * pbi){ + + if(pbi->ReconDataBuffer) + _ogg_free(pbi->ReconDataBuffer); + if(pbi->DequantBuffer) + _ogg_free(pbi->DequantBuffer); + if(pbi->TmpDataBuffer) + _ogg_free(pbi->TmpDataBuffer); + if(pbi->TmpReconBuffer) + _ogg_free(pbi->TmpReconBuffer); + + + pbi->ReconDataBuffer=0; + pbi->DequantBuffer = 0; + pbi->TmpDataBuffer = 0; + pbi->TmpReconBuffer = 0; + +} + +void InitTmpBuffers(PB_INSTANCE * pbi){ + + /* clear any existing info */ + ClearTmpBuffers(pbi); + + /* Adjust the position of all of our temporary */ + pbi->ReconDataBuffer = + _ogg_malloc(64*sizeof(*pbi->ReconDataBuffer)); + + pbi->DequantBuffer = + _ogg_malloc(64 * sizeof(*pbi->DequantBuffer)); + + pbi->TmpDataBuffer = + _ogg_malloc(64 * sizeof(*pbi->TmpDataBuffer)); + + pbi->TmpReconBuffer = + _ogg_malloc(64 * sizeof(*pbi->TmpReconBuffer)); + +} + +void ClearPBInstance(PB_INSTANCE *pbi){ + if(pbi){ + ClearTmpBuffers(pbi); + if (pbi->opb) { + _ogg_free(pbi->opb); + } + } +} + +void InitPBInstance(PB_INSTANCE *pbi){ + /* initialize whole structure to 0 */ + memset(pbi, 0, sizeof(*pbi)); + + InitTmpBuffers(pbi); + + /* allocate memory for the oggpack_buffer */ + pbi->opb = _ogg_malloc(sizeof(oggpack_buffer)); + + /* variables needing initialization (not being set to 0) */ + + pbi->ModifierPointer[0] = &pbi->Modifier[0][255]; + pbi->ModifierPointer[1] = &pbi->Modifier[1][255]; + pbi->ModifierPointer[2] = &pbi->Modifier[2][255]; + pbi->ModifierPointer[3] = &pbi->Modifier[3][255]; + + pbi->DecoderErrorCode = 0; + pbi->KeyFrameType = DCT_KEY_FRAME; + pbi->FramesHaveBeenSkipped = 0; +} diff --git a/Engine/lib/libtheora/lib/enc/pp.c b/Engine/lib/libtheora/lib/enc/pp.c new file mode 100644 index 000000000..c45289703 --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/pp.c @@ -0,0 +1,951 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: pp.c 15057 2008-06-22 21:07:32Z xiphmont $ + + ********************************************************************/ + +#include +#include +#include "codec_internal.h" +#include "pp.h" +#include "dsp.h" + +#define MAX(a, b) ((a>b)?a:b) +#define MIN(a, b) ((aScanPixelIndexTable) _ogg_free(ppi->ScanPixelIndexTable); + ppi->ScanPixelIndexTable=0; + + if(ppi->ScanDisplayFragments) _ogg_free(ppi->ScanDisplayFragments); + ppi->ScanDisplayFragments=0; + + for(i = 0 ; i < MAX_PREV_FRAMES ; i ++) + if(ppi->PrevFragments[i]){ + _ogg_free(ppi->PrevFragments[i]); + ppi->PrevFragments[i]=0; + } + + if(ppi->FragScores) _ogg_free(ppi->FragScores); + ppi->FragScores=0; + + if(ppi->SameGreyDirPixels) _ogg_free(ppi->SameGreyDirPixels); + ppi->SameGreyDirPixels=0; + + if(ppi->FragDiffPixels) _ogg_free(ppi->FragDiffPixels); + ppi->FragDiffPixels=0; + + if(ppi->BarBlockMap) _ogg_free(ppi->BarBlockMap); + ppi->BarBlockMap=0; + + if(ppi->TmpCodedMap) _ogg_free(ppi->TmpCodedMap); + ppi->TmpCodedMap=0; + + if(ppi->RowChangedPixels) _ogg_free(ppi->RowChangedPixels); + ppi->RowChangedPixels=0; + + if(ppi->PixelScores) _ogg_free(ppi->PixelScores); + ppi->PixelScores=0; + + if(ppi->PixelChangedMap) _ogg_free(ppi->PixelChangedMap); + ppi->PixelChangedMap=0; + + if(ppi->ChLocals) _ogg_free(ppi->ChLocals); + ppi->ChLocals=0; + + if(ppi->yuv_differences) _ogg_free(ppi->yuv_differences); + ppi->yuv_differences=0; + +} + +void PInitFrameInfo(PP_INSTANCE * ppi){ + int i; + PClearFrameInfo(ppi); + + ppi->ScanPixelIndexTable = + _ogg_malloc(ppi->ScanFrameFragments*sizeof(*ppi->ScanPixelIndexTable)); + + ppi->ScanDisplayFragments = + _ogg_malloc(ppi->ScanFrameFragments*sizeof(*ppi->ScanDisplayFragments)); + + for(i = 0 ; i < MAX_PREV_FRAMES ; i ++) + ppi->PrevFragments[i] = + _ogg_malloc(ppi->ScanFrameFragments*sizeof(*ppi->PrevFragments)); + + ppi->FragScores = + _ogg_malloc(ppi->ScanFrameFragments*sizeof(*ppi->FragScores)); + + ppi->SameGreyDirPixels = + _ogg_malloc(ppi->ScanFrameFragments*sizeof(*ppi->SameGreyDirPixels)); + + ppi->FragDiffPixels = + _ogg_malloc(ppi->ScanFrameFragments*sizeof(*ppi->FragScores)); + + ppi->BarBlockMap= + _ogg_malloc(3 * ppi->ScanHFragments*sizeof(*ppi->BarBlockMap)); + + ppi->TmpCodedMap = + _ogg_malloc(ppi->ScanHFragments*sizeof(*ppi->TmpCodedMap)); + + ppi->RowChangedPixels = + _ogg_malloc(3 * ppi->ScanConfig.VideoFrameHeight* + sizeof(*ppi->RowChangedPixels)); + + ppi->PixelScores = + _ogg_malloc(ppi->ScanConfig.VideoFrameWidth* + sizeof(*ppi->PixelScores) * PSCORE_CB_ROWS); + + ppi->PixelChangedMap = + _ogg_malloc(ppi->ScanConfig.VideoFrameWidth* + sizeof(*ppi->PixelChangedMap) * PMAP_CB_ROWS); + + ppi->ChLocals = + _ogg_malloc(ppi->ScanConfig.VideoFrameWidth* + sizeof(*ppi->ChLocals) * CHLOCALS_CB_ROWS); + + ppi->yuv_differences = + _ogg_malloc(ppi->ScanConfig.VideoFrameWidth* + sizeof(*ppi->yuv_differences) * YDIFF_CB_ROWS); +} + +void ClearPPInstance(PP_INSTANCE *ppi){ + PClearFrameInfo(ppi); +} + + +void InitPPInstance(PP_INSTANCE *ppi, DspFunctions *funcs){ + + memset(ppi,0,sizeof(*ppi)); + + memcpy(&ppi->dsp, funcs, sizeof(DspFunctions)); + + /* Initializations */ + ppi->PrevFrameLimit = 3; /* Must not exceed MAX_PREV_FRAMES (Note + that this number includes the current + frame so "1 = no effect") */ + + /* Scan control variables. */ + ppi->HFragPixels = 8; + ppi->VFragPixels = 8; + + ppi->SRFGreyThresh = 4; + ppi->SRFColThresh = 5; + ppi->NoiseSupLevel = 3; + ppi->SgcLevelThresh = 3; + ppi->SuvcLevelThresh = 4; + + /* Variables controlling S.A.D. breakouts. */ + ppi->GrpLowSadThresh = 10; + ppi->GrpHighSadThresh = 64; + ppi->PrimaryBlockThreshold = 5; + ppi->SgcThresh = 16; /* (Default values for 8x8 blocks). */ + + ppi->UVBlockThreshCorrection = 1.25; + ppi->UVSgcCorrection = 1.5; + + ppi->MaxLineSearchLen = MAX_SEARCH_LINE_LEN; +} + +static void DeringBlockStrong(unsigned char *SrcPtr, + unsigned char *DstPtr, + ogg_int32_t Pitch, + ogg_uint32_t FragQIndex, + const ogg_uint32_t *QuantScale){ + + ogg_int16_t UDMod[72]; + ogg_int16_t LRMod[72]; + unsigned int j,k,l; + const unsigned char * Src; + unsigned int QValue = QuantScale[FragQIndex]; + + unsigned char p; + unsigned char pl; + unsigned char pr; + unsigned char pu; + unsigned char pd; + + int al; + int ar; + int au; + int ad; + + int atot; + int B; + int newVal; + + const unsigned char *curRow = SrcPtr - 1; /* avoid negative array indexes */ + unsigned char *dstRow = DstPtr; + const unsigned char *lastRow = SrcPtr-Pitch; + const unsigned char *nextRow = SrcPtr+Pitch; + + unsigned int rowOffset = 0; + unsigned int round = (1<<6); + + int High; + int Low; + int TmpMod; + + int Sharpen = SharpenModifier[FragQIndex]; + High = 3 * QValue; + if(High>32)High=32; + Low = 0; + + + /* Initialize the Mod Data */ + Src = SrcPtr-Pitch; + for(k=0;k<9;k++){ + for(j=0;j<8;j++){ + + TmpMod = 32 + QValue - (abs(Src[j+Pitch]-Src[j])); + + if(TmpMod< -64) + TmpMod = Sharpen; + + else if(TmpModHigh) + TmpMod = High; + + UDMod[k*8+j] = (ogg_int16_t)TmpMod; + } + Src +=Pitch; + } + + Src = SrcPtr-1; + + for(k=0;k<8;k++){ + for(j=0;j<9;j++){ + TmpMod = 32 + QValue - (abs(Src[j+1]-Src[j])); + + if(TmpMod< -64 ) + TmpMod = Sharpen; + + else if(TmpMod<0) + TmpMod = Low; + + else if(TmpMod>High) + TmpMod = High; + + LRMod[k*9+j] = (ogg_int16_t)TmpMod; + } + Src+=Pitch; + } + + for(k=0;k<8;k++){ + /* In the case that this function called with same buffer for + source and destination, To keep the c and the mmx version to have + consistant results, intermediate buffer is used to store the + eight pixel value before writing them to destination + (i.e. Overwriting souce for the speical case) */ + for(l=0;l<8;l++){ + + atot = 128; + B = round; + p = curRow[ rowOffset +l +1]; + + pl = curRow[ rowOffset +l]; + al = LRMod[k*9+l]; + atot -= al; + B += al * pl; + + pu = lastRow[ rowOffset +l]; + au = UDMod[k*8+l]; + atot -= au; + B += au * pu; + + pd = nextRow[ rowOffset +l]; + ad = UDMod[(k+1)*8+l]; + atot -= ad; + B += ad * pd; + + pr = curRow[ rowOffset +l+2]; + ar = LRMod[k*9+l+1]; + atot -= ar; + B += ar * pr; + + newVal = ( atot * p + B) >> 7; + + dstRow[ rowOffset +l]= clamp255( newVal ); + } + rowOffset += Pitch; + } +} + +static void DeringBlockWeak(unsigned char *SrcPtr, + unsigned char *DstPtr, + ogg_int32_t Pitch, + ogg_uint32_t FragQIndex, + const ogg_uint32_t *QuantScale){ + + ogg_int16_t UDMod[72]; + ogg_int16_t LRMod[72]; + unsigned int j,k; + const unsigned char * Src; + unsigned int QValue = QuantScale[FragQIndex]; + + unsigned char p; + unsigned char pl; + unsigned char pr; + unsigned char pu; + unsigned char pd; + + int al; + int ar; + int au; + int ad; + + int atot; + int B; + int newVal; + + const unsigned char *curRow = SrcPtr-1; + unsigned char *dstRow = DstPtr; + const unsigned char *lastRow = SrcPtr-Pitch; + const unsigned char *nextRow = SrcPtr+Pitch; + + unsigned int rowOffset = 0; + unsigned int round = (1<<6); + + int High; + int Low; + int TmpMod; + int Sharpen = SharpenModifier[FragQIndex]; + + High = 3 * QValue; + if(High>24) + High=24; + Low = 0 ; + + /* Initialize the Mod Data */ + Src=SrcPtr-Pitch; + for(k=0;k<9;k++) { + for(j=0;j<8;j++) { + + TmpMod = 32 + QValue - 2*(abs(Src[j+Pitch]-Src[j])); + + if(TmpMod< -64) + TmpMod = Sharpen; + + else if(TmpModHigh) + TmpMod = High; + + UDMod[k*8+j] = (ogg_int16_t)TmpMod; + } + Src +=Pitch; + } + + Src = SrcPtr-1; + + for(k=0;k<8;k++){ + for(j=0;j<9;j++){ + TmpMod = 32 + QValue - 2*(abs(Src[j+1]-Src[j])); + + if(TmpMod< -64 ) + TmpMod = Sharpen; + + else if(TmpModHigh) + TmpMod = High; + + LRMod[k*9+j] = (ogg_int16_t)TmpMod; + } + Src+=Pitch; + } + + for(k=0;k<8;k++) { + for(j=0;j<8;j++){ + atot = 128; + B = round; + p = curRow[ rowOffset +j+1]; + + pl = curRow[ rowOffset +j]; + al = LRMod[k*9+j]; + atot -= al; + B += al * pl; + + pu = lastRow[ rowOffset +j]; + au = UDMod[k*8+j]; + atot -= au; + B += au * pu; + + pd = nextRow[ rowOffset +j]; + ad = UDMod[(k+1)*8+j]; + atot -= ad; + B += ad * pd; + + pr = curRow[ rowOffset +j+2]; + ar = LRMod[k*9+j+1]; + atot -= ar; + B += ar * pr; + + newVal = ( atot * p + B) >> 7; + + dstRow[ rowOffset +j] = clamp255( newVal ); + } + + rowOffset += Pitch; + } +} + +static void DeringFrame(PB_INSTANCE *pbi, + unsigned char *Src, unsigned char *Dst){ + ogg_uint32_t col,row; + unsigned char *SrcPtr; + unsigned char *DestPtr; + ogg_uint32_t BlocksAcross,BlocksDown; + const ogg_uint32_t *QuantScale; + ogg_uint32_t Block; + ogg_uint32_t LineLength; + + ogg_int32_t Thresh1,Thresh2,Thresh3,Thresh4; + + Thresh1 = 384; + Thresh2 = 4 * Thresh1; + Thresh3 = 5 * Thresh2/4; + Thresh4 = 5 * Thresh2/2; + + QuantScale = DeringModifierV1; + + BlocksAcross = pbi->HFragments; + BlocksDown = pbi->VFragments; + + SrcPtr = Src + pbi->ReconYDataOffset; + DestPtr = Dst + pbi->ReconYDataOffset; + LineLength = pbi->YStride; + + Block = 0; + + for ( row = 0 ; row < BlocksDown; row ++){ + for (col = 0; col < BlocksAcross; col ++){ + ogg_uint32_t Quality = pbi->FragQIndex[Block]; + ogg_int32_t Variance = pbi->FragmentVariances[Block]; + + if( pbi->PostProcessingLevel >5 && Variance > Thresh3 ){ + DeringBlockStrong(SrcPtr + 8 * col, DestPtr + 8 * col, + LineLength,Quality,QuantScale); + + if( (col > 0 && + pbi->FragmentVariances[Block-1] > Thresh4 ) || + (col + 1 < BlocksAcross && + pbi->FragmentVariances[Block+1] > Thresh4 ) || + (row + 1 < BlocksDown && + pbi->FragmentVariances[Block+BlocksAcross] > Thresh4) || + (row > 0 && + pbi->FragmentVariances[Block-BlocksAcross] > Thresh4) ){ + + DeringBlockStrong(SrcPtr + 8 * col, DestPtr + 8 * col, + LineLength,Quality,QuantScale); + DeringBlockStrong(SrcPtr + 8 * col, DestPtr + 8 * col, + LineLength,Quality,QuantScale); + } + } else if(Variance > Thresh2 ) { + + DeringBlockStrong(SrcPtr + 8 * col, DestPtr + 8 * col, + LineLength,Quality,QuantScale); + } else if(Variance > Thresh1 ) { + + DeringBlockWeak(SrcPtr + 8 * col, DestPtr + 8 * col, + LineLength,Quality,QuantScale); + + } else { + + dsp_copy8x8(pbi->dsp, SrcPtr + 8 * col, DestPtr + 8 * col, LineLength); + + } + + ++Block; + + } + SrcPtr += 8 * LineLength; + DestPtr += 8 * LineLength; + } + + /* Then U */ + + BlocksAcross /= 2; + BlocksDown /= 2; + LineLength /= 2; + + SrcPtr = Src + pbi->ReconUDataOffset; + DestPtr = Dst + pbi->ReconUDataOffset; + for ( row = 0 ; row < BlocksDown; row ++) { + for (col = 0; col < BlocksAcross; col ++) { + ogg_uint32_t Quality = pbi->FragQIndex[Block]; + ogg_int32_t Variance = pbi->FragmentVariances[Block]; + + if( pbi->PostProcessingLevel >5 && Variance > Thresh4 ) { + DeringBlockStrong(SrcPtr + 8 * col, DestPtr + 8 * col, + LineLength,Quality,QuantScale); + DeringBlockStrong(SrcPtr + 8 * col, DestPtr + 8 * col, + LineLength,Quality,QuantScale); + DeringBlockStrong(SrcPtr + 8 * col, DestPtr + 8 * col, + LineLength,Quality,QuantScale); + + }else if(Variance > Thresh2 ){ + DeringBlockStrong(SrcPtr + 8 * col, DestPtr + 8 * col, + LineLength,Quality,QuantScale); + }else if(Variance > Thresh1 ){ + DeringBlockWeak(SrcPtr + 8 * col, DestPtr + 8 * col, + LineLength,Quality,QuantScale); + }else{ + dsp_copy8x8(pbi->dsp, SrcPtr + 8 * col, DestPtr + 8 * col, LineLength); + } + + ++Block; + + } + SrcPtr += 8 * LineLength; + DestPtr += 8 * LineLength; + } + + /* Then V */ + SrcPtr = Src + pbi->ReconVDataOffset; + DestPtr = Dst + pbi->ReconVDataOffset; + + for ( row = 0 ; row < BlocksDown; row ++){ + for (col = 0; col < BlocksAcross; col ++){ + + ogg_uint32_t Quality = pbi->FragQIndex[Block]; + ogg_int32_t Variance = pbi->FragmentVariances[Block]; + + + if( pbi->PostProcessingLevel >5 && Variance > Thresh4 ) { + DeringBlockStrong(SrcPtr + 8 * col, DestPtr + 8 * col, + LineLength,Quality,QuantScale); + DeringBlockStrong(SrcPtr + 8 * col, DestPtr + 8 * col, + LineLength,Quality,QuantScale); + DeringBlockStrong(SrcPtr + 8 * col, DestPtr + 8 * col, + LineLength,Quality,QuantScale); + + }else if(Variance > Thresh2 ){ + DeringBlockStrong(SrcPtr + 8 * col, DestPtr + 8 * col, + LineLength,Quality,QuantScale); + }else if(Variance > Thresh1 ){ + DeringBlockWeak(SrcPtr + 8 * col, DestPtr + 8 * col, + LineLength,Quality,QuantScale); + }else{ + dsp_copy8x8(pbi->dsp, SrcPtr + 8 * col, DestPtr + 8 * col, LineLength); + } + + ++Block; + + } + SrcPtr += 8 * LineLength; + DestPtr += 8 * LineLength; + + } + +} + +void UpdateFragQIndex(PB_INSTANCE *pbi){ + + ogg_uint32_t ThisFrameQIndex; + ogg_uint32_t i; + + /* Check this frame quality index */ + ThisFrameQIndex = pbi->FrameQIndex; + + + /* It is not a key frame, so only reset those are coded */ + for( i = 0; i < pbi->UnitFragments; i++ ) + if( pbi->display_fragments[i]) + pbi->FragQIndex[i] = ThisFrameQIndex; + +} + +static void DeblockLoopFilteredBand(PB_INSTANCE *pbi, + unsigned char *SrcPtr, + unsigned char *DesPtr, + ogg_uint32_t PlaneLineStep, + ogg_uint32_t FragsAcross, + ogg_uint32_t StartFrag, + const ogg_uint32_t *QuantScale){ + ogg_uint32_t j,k; + ogg_uint32_t CurrentFrag=StartFrag; + ogg_int32_t QStep; + ogg_int32_t FLimit; + unsigned char *Src, *Des; + ogg_int32_t x[10]; + ogg_int32_t Sum1, Sum2; + + while(CurrentFrag < StartFrag + FragsAcross){ + + Src=SrcPtr+8*(CurrentFrag-StartFrag)-PlaneLineStep*5; + Des=DesPtr+8*(CurrentFrag-StartFrag)-PlaneLineStep*4; + + QStep = QuantScale[pbi->FragQIndex[CurrentFrag+FragsAcross]]; + FLimit = ( QStep * 3 ) >> 2; + + for( j=0; j<8 ; j++){ + x[0] = Src[0]; + x[1] = Src[PlaneLineStep]; + x[2] = Src[PlaneLineStep*2]; + x[3] = Src[PlaneLineStep*3]; + x[4] = Src[PlaneLineStep*4]; + x[5] = Src[PlaneLineStep*5]; + x[6] = Src[PlaneLineStep*6]; + x[7] = Src[PlaneLineStep*7]; + x[8] = Src[PlaneLineStep*8]; + x[9] = Src[PlaneLineStep*9]; + + Sum1=Sum2=0; + + for(k=1;k<=4;k++){ + Sum1 += abs(x[k]-x[k-1]); + Sum2 += abs(x[k+4]-x[k+5]); + } + + pbi->FragmentVariances[CurrentFrag] +=((Sum1>255)?255:Sum1); + pbi->FragmentVariances[CurrentFrag + FragsAcross] += ((Sum2>255)?255:Sum2); + + if( Sum1 < FLimit && + Sum2 < FLimit && + (x[5] - x[4]) < QStep && + (x[4] - x[5]) < QStep ){ + + /* low pass filtering (LPF7: 1 1 1 2 1 1 1) */ + Des[0 ] = (x[0] + x[0] +x[0] + x[1] * 2 + + x[2] + x[3] +x[4] + 4) >> 3; + Des[PlaneLineStep ] = (x[0] + x[0] +x[1] + x[2] * 2 + + x[3] + x[4] +x[5] + 4) >> 3; + Des[PlaneLineStep*2] = (x[0] + x[1] +x[2] + x[3] * 2 + + x[4] + x[5] +x[6] + 4) >> 3; + Des[PlaneLineStep*3] = (x[1] + x[2] +x[3] + x[4] * 2 + + x[5] + x[6] +x[7] + 4) >> 3; + Des[PlaneLineStep*4] = (x[2] + x[3] +x[4] + x[5] * 2 + + x[6] + x[7] +x[8] + 4) >> 3; + Des[PlaneLineStep*5] = (x[3] + x[4] +x[5] + x[6] * 2 + + x[7] + x[8] +x[9] + 4) >> 3; + Des[PlaneLineStep*6] = (x[4] + x[5] +x[6] + x[7] * 2 + + x[8] + x[9] +x[9] + 4) >> 3; + Des[PlaneLineStep*7] = (x[5] + x[6] +x[7] + x[8] * 2 + + x[9] + x[9] +x[9] + 4) >> 3; + + }else { + /* copy the pixels to destination */ + Des[0 ]= (unsigned char)x[1]; + Des[PlaneLineStep ]= (unsigned char)x[2]; + Des[PlaneLineStep*2]= (unsigned char)x[3]; + Des[PlaneLineStep*3]= (unsigned char)x[4]; + Des[PlaneLineStep*4]= (unsigned char)x[5]; + Des[PlaneLineStep*5]= (unsigned char)x[6]; + Des[PlaneLineStep*6]= (unsigned char)x[7]; + Des[PlaneLineStep*7]= (unsigned char)x[8]; + } + Src ++; + Des ++; + } + + + /* done with filtering the horizontal edge, now let's do the + vertical one */ + /* skip the first one */ + if(CurrentFrag==StartFrag) + CurrentFrag++; + else{ + Des=DesPtr-8*PlaneLineStep+8*(CurrentFrag-StartFrag); + Src=Des-5; + Des-=4; + + QStep = QuantScale[pbi->FragQIndex[CurrentFrag]]; + FLimit = ( QStep * 3 ) >> 2; + + for( j=0; j<8 ; j++){ + x[0] = Src[0]; + x[1] = Src[1]; + x[2] = Src[2]; + x[3] = Src[3]; + x[4] = Src[4]; + x[5] = Src[5]; + x[6] = Src[6]; + x[7] = Src[7]; + x[8] = Src[8]; + x[9] = Src[9]; + + Sum1=Sum2=0; + + for(k=1;k<=4;k++){ + Sum1 += abs(x[k]-x[k-1]); + Sum2 += abs(x[k+4]-x[k+5]); + } + + pbi->FragmentVariances[CurrentFrag-1] += ((Sum1>255)?255:Sum1); + pbi->FragmentVariances[CurrentFrag] += ((Sum2>255)?255:Sum2); + + if( Sum1 < FLimit && + Sum2 < FLimit && + (x[5] - x[4]) < QStep && + (x[4] - x[5]) < QStep ){ + + /* low pass filtering (LPF7: 1 1 1 2 1 1 1) */ + Des[0] = (x[0] + x[0] +x[0] + x[1] * 2 + x[2] + x[3] +x[4] + 4) >> 3; + Des[1] = (x[0] + x[0] +x[1] + x[2] * 2 + x[3] + x[4] +x[5] + 4) >> 3; + Des[2] = (x[0] + x[1] +x[2] + x[3] * 2 + x[4] + x[5] +x[6] + 4) >> 3; + Des[3] = (x[1] + x[2] +x[3] + x[4] * 2 + x[5] + x[6] +x[7] + 4) >> 3; + Des[4] = (x[2] + x[3] +x[4] + x[5] * 2 + x[6] + x[7] +x[8] + 4) >> 3; + Des[5] = (x[3] + x[4] +x[5] + x[6] * 2 + x[7] + x[8] +x[9] + 4) >> 3; + Des[6] = (x[4] + x[5] +x[6] + x[7] * 2 + x[8] + x[9] +x[9] + 4) >> 3; + Des[7] = (x[5] + x[6] +x[7] + x[8] * 2 + x[9] + x[9] +x[9] + 4) >> 3; + } + + Src += PlaneLineStep; + Des += PlaneLineStep; + } + CurrentFrag ++; + } + } +} + +static void DeblockVerticalEdgesInLoopFilteredBand(PB_INSTANCE *pbi, + unsigned char *SrcPtr, + unsigned char *DesPtr, + ogg_uint32_t PlaneLineStep, + ogg_uint32_t FragsAcross, + ogg_uint32_t StartFrag, + const ogg_uint32_t *QuantScale){ + ogg_uint32_t j,k; + ogg_uint32_t CurrentFrag=StartFrag; + ogg_int32_t QStep; + ogg_int32_t FLimit; + unsigned char *Src, *Des; + ogg_int32_t x[10]; + ogg_int32_t Sum1, Sum2; + + while(CurrentFrag < StartFrag + FragsAcross-1) { + + Src=SrcPtr+8*(CurrentFrag-StartFrag+1)-5; + Des=DesPtr+8*(CurrentFrag-StartFrag+1)-4; + + QStep = QuantScale[pbi->FragQIndex[CurrentFrag+1]]; + FLimit = ( QStep * 3)>>2 ; + + for( j=0; j<8 ; j++){ + x[0] = Src[0]; + x[1] = Src[1]; + x[2] = Src[2]; + x[3] = Src[3]; + x[4] = Src[4]; + x[5] = Src[5]; + x[6] = Src[6]; + x[7] = Src[7]; + x[8] = Src[8]; + x[9] = Src[9]; + + Sum1=Sum2=0; + + for(k=1;k<=4;k++){ + Sum1 += abs(x[k]-x[k-1]); + Sum2 += abs(x[k+4]-x[k+5]); + } + + pbi->FragmentVariances[CurrentFrag] += ((Sum1>255)?255:Sum1); + pbi->FragmentVariances[CurrentFrag+1] += ((Sum2>255)?255:Sum2); + + + if( Sum1 < FLimit && + Sum2 < FLimit && + (x[5] - x[4]) < QStep && + (x[4] - x[5]) < QStep ){ + + /* low pass filtering (LPF7: 1 1 1 2 1 1 1) */ + Des[0] = (x[0] + x[0] +x[0] + x[1] * 2 + x[2] + x[3] +x[4] + 4) >> 3; + Des[1] = (x[0] + x[0] +x[1] + x[2] * 2 + x[3] + x[4] +x[5] + 4) >> 3; + Des[2] = (x[0] + x[1] +x[2] + x[3] * 2 + x[4] + x[5] +x[6] + 4) >> 3; + Des[3] = (x[1] + x[2] +x[3] + x[4] * 2 + x[5] + x[6] +x[7] + 4) >> 3; + Des[4] = (x[2] + x[3] +x[4] + x[5] * 2 + x[6] + x[7] +x[8] + 4) >> 3; + Des[5] = (x[3] + x[4] +x[5] + x[6] * 2 + x[7] + x[8] +x[9] + 4) >> 3; + Des[6] = (x[4] + x[5] +x[6] + x[7] * 2 + x[8] + x[9] +x[9] + 4) >> 3; + Des[7] = (x[5] + x[6] +x[7] + x[8] * 2 + x[9] + x[9] +x[9] + 4) >> 3; + } + Src +=PlaneLineStep; + Des +=PlaneLineStep; + + } + CurrentFrag ++; + } +} + +static void DeblockPlane(PB_INSTANCE *pbi, + unsigned char *SourceBuffer, + unsigned char *DestinationBuffer, + ogg_uint32_t Channel ){ + + ogg_uint32_t i,k; + ogg_uint32_t PlaneLineStep=0; + ogg_uint32_t StartFrag =0; + ogg_uint32_t PixelIndex=0; + unsigned char * SrcPtr=0, * DesPtr=0; + ogg_uint32_t FragsAcross=0; + ogg_uint32_t FragsDown=0; + const ogg_uint32_t *QuantScale=0; + + switch( Channel ){ + case 0: + /* Get the parameters */ + PlaneLineStep = pbi->YStride; + FragsAcross = pbi->HFragments; + FragsDown = pbi->VFragments; + StartFrag = 0; + PixelIndex = pbi->ReconYDataOffset; + SrcPtr = & SourceBuffer[PixelIndex]; + DesPtr = & DestinationBuffer[PixelIndex]; + break; + + case 1: + /* Get the parameters */ + PlaneLineStep = pbi->UVStride; + FragsAcross = pbi->HFragments / 2; + FragsDown = pbi->VFragments / 2; + StartFrag = pbi->YPlaneFragments; + + PixelIndex = pbi->ReconUDataOffset; + SrcPtr = & SourceBuffer[PixelIndex]; + DesPtr = & DestinationBuffer[PixelIndex]; + break; + + default: + /* Get the parameters */ + PlaneLineStep = pbi->UVStride; + FragsAcross = pbi->HFragments / 2; + FragsDown = pbi->VFragments / 2; + StartFrag = pbi->YPlaneFragments + pbi->UVPlaneFragments; + + PixelIndex = pbi->ReconVDataOffset; + SrcPtr = & SourceBuffer[PixelIndex]; + DesPtr = & DestinationBuffer[PixelIndex]; + break; + } + + QuantScale = DcQuantScaleV1; + + for(i=0;i<4;i++) + memcpy(DesPtr+i*PlaneLineStep, SrcPtr+i*PlaneLineStep, PlaneLineStep); + + k = 1; + + while( k < FragsDown ){ + + SrcPtr += 8*PlaneLineStep; + DesPtr += 8*PlaneLineStep; + + /* Filter both the horizontal and vertical block edges inside the band */ + DeblockLoopFilteredBand(pbi, SrcPtr, DesPtr, PlaneLineStep, + FragsAcross, StartFrag, QuantScale); + + /* Move Pointers */ + StartFrag += FragsAcross; + + k ++; + } + + /* The Last band */ + for(i=0;i<4;i++) + memcpy(DesPtr+(i+4)*PlaneLineStep, + SrcPtr+(i+4)*PlaneLineStep, + PlaneLineStep); + + DeblockVerticalEdgesInLoopFilteredBand(pbi,SrcPtr,DesPtr,PlaneLineStep, + FragsAcross,StartFrag,QuantScale); + +} + +static void DeblockFrame(PB_INSTANCE *pbi, unsigned char *SourceBuffer, + unsigned char *DestinationBuffer){ + + memset(pbi->FragmentVariances, 0 , sizeof(ogg_int32_t) * pbi->UnitFragments); + + + UpdateFragQIndex(pbi); + + /* Y */ + DeblockPlane( pbi, SourceBuffer, DestinationBuffer, 0); + + /* U */ + DeblockPlane( pbi, SourceBuffer, DestinationBuffer, 1); + + /* V */ + DeblockPlane( pbi, SourceBuffer, DestinationBuffer, 2); + +} + +void PostProcess(PB_INSTANCE *pbi){ + + switch (pbi->PostProcessingLevel){ + case 8: + /* on a slow machine, use a simpler and faster deblocking filter */ + DeblockFrame(pbi, pbi->LastFrameRecon,pbi->PostProcessBuffer); + break; + + case 6: + DeblockFrame(pbi, pbi->LastFrameRecon,pbi->PostProcessBuffer); + UpdateUMVBorder(pbi, pbi->PostProcessBuffer ); + DeringFrame(pbi, pbi->PostProcessBuffer, pbi->PostProcessBuffer); + break; + + case 5: + DeblockFrame(pbi, pbi->LastFrameRecon,pbi->PostProcessBuffer); + UpdateUMVBorder(pbi, pbi->PostProcessBuffer ); + DeringFrame(pbi, pbi->PostProcessBuffer, pbi->PostProcessBuffer); + break; + case 4: + DeblockFrame(pbi, pbi->LastFrameRecon, pbi->PostProcessBuffer); + break; + case 1: + UpdateFragQIndex(pbi); + break; + + case 0: + break; + + default: + DeblockFrame(pbi, pbi->LastFrameRecon, pbi->PostProcessBuffer); + UpdateUMVBorder(pbi, pbi->PostProcessBuffer ); + DeringFrame(pbi, pbi->PostProcessBuffer, pbi->PostProcessBuffer); + break; + } +} + diff --git a/Engine/lib/libtheora/lib/enc/pp.h b/Engine/lib/libtheora/lib/enc/pp.h new file mode 100644 index 000000000..6eb3a7604 --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/pp.h @@ -0,0 +1,48 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: pp.h 13884 2007-09-22 08:38:10Z giles $ + + ********************************************************************/ + +/* Constants. */ +#define INTERNAL_BLOCK_HEIGHT 8 +#define INTERNAL_BLOCK_WIDTH 8 + + +/* NEW Line search values. */ +#define UP 0 +#define DOWN 1 +#define LEFT 2 +#define RIGHT 3 + +#define FIRST_ROW 0 +#define NOT_EDGE_ROW 1 +#define LAST_ROW 2 + +#define YDIFF_CB_ROWS (INTERNAL_BLOCK_HEIGHT * 3) +#define CHLOCALS_CB_ROWS (INTERNAL_BLOCK_HEIGHT * 3) +#define PMAP_CB_ROWS (INTERNAL_BLOCK_HEIGHT * 3) +#define PSCORE_CB_ROWS (INTERNAL_BLOCK_HEIGHT * 4) + +/* Status values in block coding map */ +#define CANDIDATE_BLOCK_LOW -2 +#define CANDIDATE_BLOCK -1 +#define BLOCK_NOT_CODED 0 +#define BLOCK_CODED_BAR 3 +#define BLOCK_CODED_SGC 4 +#define BLOCK_CODED_LOW 4 +#define BLOCK_CODED 5 + +#define MAX_PREV_FRAMES 16 +#define MAX_SEARCH_LINE_LEN 7 diff --git a/Engine/lib/libtheora/lib/enc/quant_lookup.h b/Engine/lib/libtheora/lib/enc/quant_lookup.h new file mode 100644 index 000000000..04bbce910 --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/quant_lookup.h @@ -0,0 +1,43 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: quant_lookup.h 13884 2007-09-22 08:38:10Z giles $ + + ********************************************************************/ + +#include "codec_internal.h" + +#define MIN16 ((1<<16)-1) +#define SHIFT16 (1<<16) + +#define MIN_LEGAL_QUANT_ENTRY 8 +#define MIN_DEQUANT_VAL 2 +#define IDCT_SCALE_FACTOR 2 /* Shift left bits to improve IDCT precision */ +#define OLD_SCHEME 1 + + +/****************************** + * lookup table for DCT coefficient zig-zag ordering + * ****************************/ + +static const ogg_uint32_t dezigzag_index[64] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; + diff --git a/Engine/lib/libtheora/lib/enc/reconstruct.c b/Engine/lib/libtheora/lib/enc/reconstruct.c new file mode 100644 index 000000000..5602884af --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/reconstruct.c @@ -0,0 +1,110 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: reconstruct.c 13884 2007-09-22 08:38:10Z giles $ + + ********************************************************************/ + +#include "codec_internal.h" + +static void copy8x8__c (unsigned char *src, + unsigned char *dest, + unsigned int stride) +{ + int j; + for ( j = 0; j < 8; j++ ){ + ((ogg_uint32_t*)dest)[0] = ((ogg_uint32_t*)src)[0]; + ((ogg_uint32_t*)dest)[1] = ((ogg_uint32_t*)src)[1]; + src+=stride; + dest+=stride; + } +} + +static void recon_intra8x8__c (unsigned char *ReconPtr, ogg_int16_t *ChangePtr, + ogg_uint32_t LineStep) +{ + ogg_uint32_t i; + + for (i = 8; i; i--){ + /* Convert the data back to 8 bit unsigned */ + /* Saturate the output to unsigend 8 bit values */ + ReconPtr[0] = clamp255( ChangePtr[0] + 128 ); + ReconPtr[1] = clamp255( ChangePtr[1] + 128 ); + ReconPtr[2] = clamp255( ChangePtr[2] + 128 ); + ReconPtr[3] = clamp255( ChangePtr[3] + 128 ); + ReconPtr[4] = clamp255( ChangePtr[4] + 128 ); + ReconPtr[5] = clamp255( ChangePtr[5] + 128 ); + ReconPtr[6] = clamp255( ChangePtr[6] + 128 ); + ReconPtr[7] = clamp255( ChangePtr[7] + 128 ); + + ReconPtr += LineStep; + ChangePtr += 8; + } +} + +static void recon_inter8x8__c (unsigned char *ReconPtr, unsigned char *RefPtr, + ogg_int16_t *ChangePtr, ogg_uint32_t LineStep) +{ + ogg_uint32_t i; + + for (i = 8; i; i--){ + ReconPtr[0] = clamp255(RefPtr[0] + ChangePtr[0]); + ReconPtr[1] = clamp255(RefPtr[1] + ChangePtr[1]); + ReconPtr[2] = clamp255(RefPtr[2] + ChangePtr[2]); + ReconPtr[3] = clamp255(RefPtr[3] + ChangePtr[3]); + ReconPtr[4] = clamp255(RefPtr[4] + ChangePtr[4]); + ReconPtr[5] = clamp255(RefPtr[5] + ChangePtr[5]); + ReconPtr[6] = clamp255(RefPtr[6] + ChangePtr[6]); + ReconPtr[7] = clamp255(RefPtr[7] + ChangePtr[7]); + + ChangePtr += 8; + ReconPtr += LineStep; + RefPtr += LineStep; + } +} + +static void recon_inter8x8_half__c (unsigned char *ReconPtr, unsigned char *RefPtr1, + unsigned char *RefPtr2, ogg_int16_t *ChangePtr, + ogg_uint32_t LineStep) +{ + ogg_uint32_t i; + + for (i = 8; i; i--){ + ReconPtr[0] = clamp255((((int)RefPtr1[0] + (int)RefPtr2[0]) >> 1) + ChangePtr[0] ); + ReconPtr[1] = clamp255((((int)RefPtr1[1] + (int)RefPtr2[1]) >> 1) + ChangePtr[1] ); + ReconPtr[2] = clamp255((((int)RefPtr1[2] + (int)RefPtr2[2]) >> 1) + ChangePtr[2] ); + ReconPtr[3] = clamp255((((int)RefPtr1[3] + (int)RefPtr2[3]) >> 1) + ChangePtr[3] ); + ReconPtr[4] = clamp255((((int)RefPtr1[4] + (int)RefPtr2[4]) >> 1) + ChangePtr[4] ); + ReconPtr[5] = clamp255((((int)RefPtr1[5] + (int)RefPtr2[5]) >> 1) + ChangePtr[5] ); + ReconPtr[6] = clamp255((((int)RefPtr1[6] + (int)RefPtr2[6]) >> 1) + ChangePtr[6] ); + ReconPtr[7] = clamp255((((int)RefPtr1[7] + (int)RefPtr2[7]) >> 1) + ChangePtr[7] ); + + ChangePtr += 8; + ReconPtr += LineStep; + RefPtr1 += LineStep; + RefPtr2 += LineStep; + } +} + +void dsp_recon_init (DspFunctions *funcs, ogg_uint32_t cpu_flags) +{ + funcs->copy8x8 = copy8x8__c; + funcs->recon_intra8x8 = recon_intra8x8__c; + funcs->recon_inter8x8 = recon_inter8x8__c; + funcs->recon_inter8x8_half = recon_inter8x8_half__c; +#if defined(USE_ASM) + if (cpu_flags & OC_CPU_X86_MMX) { + dsp_mmx_recon_init(funcs); + } +#endif +} diff --git a/Engine/lib/libtheora/lib/enc/scan.c b/Engine/lib/libtheora/lib/enc/scan.c new file mode 100644 index 000000000..5466ca438 --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/scan.c @@ -0,0 +1,2301 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: scan.c 13884 2007-09-22 08:38:10Z giles $ + + ********************************************************************/ + +#include +#include +#include +#include "codec_internal.h" +#include "dsp.h" + +#define MAX_SEARCH_LINE_LEN 7 + +#define SET8_0(ptr) \ + ((ogg_uint32_t *)ptr)[0] = 0x00000000; \ + ((ogg_uint32_t *)ptr)[1] = 0x00000000; +#define SET8_1(ptr) \ + ((ogg_uint32_t *)ptr)[0] = 0x01010101; \ + ((ogg_uint32_t *)ptr)[1] = 0x01010101; +#define SET8_8(ptr) \ + ((ogg_uint32_t *)ptr)[0] = 0x08080808; \ + ((ogg_uint32_t *)ptr)[1] = 0x08080808; + +static ogg_uint32_t LineLengthScores[ MAX_SEARCH_LINE_LEN + 1 ] = { + 0, 0, 0, 0, 2, 4, 12, 24 +}; + +static ogg_uint32_t BodyNeighbourScore = 8; +static double DiffDevisor = 0.0625; +#define HISTORY_BLOCK_FACTOR 2 +#define MIN_STEP_THRESH 6 +#define SCORE_MULT_LOW 0.5 +#define SCORE_MULT_HIGH 4 + +#define UP 0 +#define DOWN 1 +#define LEFT 2 +#define RIGHT 3 + +#define INTERNAL_BLOCK_HEIGHT 8 +#define INTERNAL_BLOCK_WIDTH 8 + +#define BLOCK_NOT_CODED 0 +#define BLOCK_CODED_BAR 3 +#define BLOCK_CODED_SGC 4 +#define BLOCK_CODED_LOW 4 +#define BLOCK_CODED 5 + +#define CANDIDATE_BLOCK_LOW -2 +#define CANDIDATE_BLOCK -1 + +#define FIRST_ROW 0 +#define NOT_EDGE_ROW 1 +#define LAST_ROW 2 + +#define YDIFF_CB_ROWS (INTERNAL_BLOCK_HEIGHT * 3) +#define CHLOCALS_CB_ROWS (INTERNAL_BLOCK_HEIGHT * 3) +#define PMAP_CB_ROWS (INTERNAL_BLOCK_HEIGHT * 3) + +void ConfigurePP( PP_INSTANCE *ppi, int Level ) { + switch ( Level ){ + case 0: + ppi->SRFGreyThresh = 1; + ppi->SRFColThresh = 1; + ppi->NoiseSupLevel = 2; + ppi->SgcLevelThresh = 1; + ppi->SuvcLevelThresh = 1; + ppi->GrpLowSadThresh = 6; + ppi->GrpHighSadThresh = 24; + ppi->PrimaryBlockThreshold = 2; + ppi->SgcThresh = 10; + + ppi->PAKEnabled = 0; + break; + + case 1: + ppi->SRFGreyThresh = 2; + ppi->SRFColThresh = 2; + ppi->NoiseSupLevel = 2; + ppi->SgcLevelThresh = 2; + ppi->SuvcLevelThresh = 2; + ppi->GrpLowSadThresh = 8; + ppi->GrpHighSadThresh = 32; + ppi->PrimaryBlockThreshold = 5; + ppi->SgcThresh = 12; + + ppi->PAKEnabled = 1; + break; + + case 2: /* Default VP3 settings */ + ppi->SRFGreyThresh = 3; + ppi->SRFColThresh = 3; + ppi->NoiseSupLevel = 2; + ppi->SgcLevelThresh = 2; + ppi->SuvcLevelThresh = 2; + ppi->GrpLowSadThresh = 8; + ppi->GrpHighSadThresh = 32; + ppi->PrimaryBlockThreshold = 5; + ppi->SgcThresh = 16; + + ppi->PAKEnabled = 1; + break; + + case 3: + ppi->SRFGreyThresh = 4; + ppi->SRFColThresh = 4; + ppi->NoiseSupLevel = 3; + ppi->SgcLevelThresh = 3; + ppi->SuvcLevelThresh = 3; + ppi->GrpLowSadThresh = 10; + ppi->GrpHighSadThresh = 48; + ppi->PrimaryBlockThreshold = 5; + ppi->SgcThresh = 18; + + ppi->PAKEnabled = 1; + break; + + case 4: + ppi->SRFGreyThresh = 5; + ppi->SRFColThresh = 5; + ppi->NoiseSupLevel = 3; + ppi->SgcLevelThresh = 4; + ppi->SuvcLevelThresh = 4; + ppi->GrpLowSadThresh = 12; + ppi->GrpHighSadThresh = 48; + ppi->PrimaryBlockThreshold = 5; + ppi->SgcThresh = 20; + + ppi->PAKEnabled = 1; + break; + + case 5: + ppi->SRFGreyThresh = 6; + ppi->SRFColThresh = 6; + ppi->NoiseSupLevel = 3; + ppi->SgcLevelThresh = 4; + ppi->SuvcLevelThresh = 4; + ppi->GrpLowSadThresh = 12; + ppi->GrpHighSadThresh = 64; + ppi->PrimaryBlockThreshold = 10; + ppi->SgcThresh = 24; + + ppi->PAKEnabled = 1; + break; + + case 6: + ppi->SRFGreyThresh = 6; + ppi->SRFColThresh = 7; + ppi->NoiseSupLevel = 3; + ppi->SgcLevelThresh = 4; + ppi->SuvcLevelThresh = 4; + ppi->GrpLowSadThresh = 12; + ppi->GrpHighSadThresh = 64; + ppi->PrimaryBlockThreshold = 10; + ppi->SgcThresh = 24; + + ppi->PAKEnabled = 1; + break; + + default: + ppi->SRFGreyThresh = 3; + ppi->SRFColThresh = 3; + ppi->NoiseSupLevel = 2; + ppi->SgcLevelThresh = 2; + ppi->SuvcLevelThresh = 2; + ppi->GrpLowSadThresh = 10; + ppi->GrpHighSadThresh = 32; + ppi->PrimaryBlockThreshold = 5; + ppi->SgcThresh = 16; + ppi->PAKEnabled = 1; + break; + } +} + +static void ScanCalcPixelIndexTable(PP_INSTANCE *ppi){ + ogg_uint32_t i; + ogg_uint32_t * PixelIndexTablePtr = ppi->ScanPixelIndexTable; + + /* If appropriate add on extra inices for U and V planes. */ + for ( i = 0; i < (ppi->ScanYPlaneFragments); i++ ) { + PixelIndexTablePtr[ i ] = + ((i / ppi->ScanHFragments) * + VFRAGPIXELS * ppi->ScanConfig.VideoFrameWidth); + PixelIndexTablePtr[ i ] += + ((i % ppi->ScanHFragments) * HFRAGPIXELS); + } + + PixelIndexTablePtr = &ppi->ScanPixelIndexTable[ppi->ScanYPlaneFragments]; + + for ( i = 0; i < (ppi->ScanUVPlaneFragments * 2); i++ ){ + PixelIndexTablePtr[ i ] = + ((i / (ppi->ScanHFragments >> 1) ) * + (VFRAGPIXELS * (ppi->ScanConfig.VideoFrameWidth >> 1)) ); + PixelIndexTablePtr[ i ] += + ((i % (ppi->ScanHFragments >> 1) ) * + HFRAGPIXELS) + ppi->YFramePixels; + } +} + +static void InitScanMapArrays(PP_INSTANCE *ppi){ + int i; + unsigned char StepThresh; + + /* Clear down the fragment level map arrays for the current frame. */ + memset( ppi->FragScores, 0, + ppi->ScanFrameFragments * sizeof(*ppi->FragScores) ); + memset( ppi->SameGreyDirPixels, 0, + ppi->ScanFrameFragments ); + memset( ppi->FragDiffPixels, 0, + ppi->ScanFrameFragments ); + memset( ppi->RowChangedPixels, 0, + 3* ppi->ScanConfig.VideoFrameHeight*sizeof(*ppi->RowChangedPixels)); + + memset( ppi->ScanDisplayFragments, BLOCK_NOT_CODED, ppi->ScanFrameFragments); + + /* Threshold used in setting up ppi->NoiseScoreBoostTable[] */ + StepThresh = (unsigned int)(ppi->SRFGreyThresh >> 1); + if ( StepThresh < MIN_STEP_THRESH ) + StepThresh = MIN_STEP_THRESH; + ppi->SrfThresh = (int)ppi->SRFGreyThresh; + + /* Set up various tables used to tweak pixel score values and + scoring rules based upon absolute value of a pixel change */ + for ( i = 0; i < 256; i++ ){ + /* Score multiplier table indexed by absolute difference. */ + ppi->AbsDiff_ScoreMultiplierTable[i] = (double)i * DiffDevisor; + if ( ppi->AbsDiff_ScoreMultiplierTable[i] < SCORE_MULT_LOW ) + ppi->AbsDiff_ScoreMultiplierTable[i] = SCORE_MULT_LOW; + else if ( ppi->AbsDiff_ScoreMultiplierTable[i] > SCORE_MULT_HIGH) + ppi->AbsDiff_ScoreMultiplierTable[i] = SCORE_MULT_HIGH; + + /* Table that facilitates a relaxation of the changed locals rules + in NoiseScoreRow() for pixels that have changed by a large + amount. */ + if ( i < (ppi->SrfThresh + StepThresh) ) + ppi->NoiseScoreBoostTable[i] = 0; + else if ( i < (ppi->SrfThresh + (StepThresh * 4)) ) + ppi->NoiseScoreBoostTable[i] = 1; + else if ( i < (ppi->SrfThresh + (StepThresh * 6)) ) + ppi->NoiseScoreBoostTable[i] = 2; + else + ppi->NoiseScoreBoostTable[i] = 3; + + } + + /* Set various other threshold parameters. */ + + /* Set variables that control access to the line search algorithms. */ + ppi->LineSearchTripTresh = 16; + if ( ppi->LineSearchTripTresh > ppi->PrimaryBlockThreshold ) + ppi->LineSearchTripTresh = (unsigned int)(ppi->PrimaryBlockThreshold + 1); + + /* Adjust line search length if block threshold low */ + ppi->MaxLineSearchLen = MAX_SEARCH_LINE_LEN; + while ( (ppi->MaxLineSearchLen > 0) && + (LineLengthScores[ppi->MaxLineSearchLen-1] > + ppi->PrimaryBlockThreshold) ) + ppi->MaxLineSearchLen -= 1; + +} + +void ScanYUVInit( PP_INSTANCE * ppi, SCAN_CONFIG_DATA * ScanConfigPtr){ + int i; + + /* Set up the various imported data structure pointers. */ + ppi->ScanConfig.Yuv0ptr = ScanConfigPtr->Yuv0ptr; + ppi->ScanConfig.Yuv1ptr = ScanConfigPtr->Yuv1ptr; + ppi->ScanConfig.SrfWorkSpcPtr = ScanConfigPtr->SrfWorkSpcPtr; + ppi->ScanConfig.disp_fragments = ScanConfigPtr->disp_fragments; + + ppi->ScanConfig.RegionIndex = ScanConfigPtr->RegionIndex; + + ppi->ScanConfig.VideoFrameWidth = ScanConfigPtr->VideoFrameWidth; + ppi->ScanConfig.VideoFrameHeight = ScanConfigPtr->VideoFrameHeight; + + /* UV plane sizes. */ + ppi->VideoUVPlaneWidth = ScanConfigPtr->VideoFrameWidth / 2; + ppi->VideoUVPlaneHeight = ScanConfigPtr->VideoFrameHeight / 2; + + /* Note the size of each plane in pixels. */ + ppi->YFramePixels = ppi->ScanConfig.VideoFrameWidth * + ppi->ScanConfig.VideoFrameHeight; + ppi->UVFramePixels = ppi->VideoUVPlaneWidth * ppi->VideoUVPlaneHeight; + + /* Work out various fragment related values. */ + ppi->ScanYPlaneFragments = ppi->YFramePixels / + (HFRAGPIXELS * VFRAGPIXELS); + ppi->ScanUVPlaneFragments = ppi->UVFramePixels / + (HFRAGPIXELS * VFRAGPIXELS);; + ppi->ScanHFragments = ppi->ScanConfig.VideoFrameWidth / HFRAGPIXELS; + ppi->ScanVFragments = ppi->ScanConfig.VideoFrameHeight / VFRAGPIXELS; + ppi->ScanFrameFragments = ppi->ScanYPlaneFragments + + (2 * ppi->ScanUVPlaneFragments); + + PInitFrameInfo(ppi); + + /* Set up the scan pixel index table. */ + ScanCalcPixelIndexTable(ppi); + + /* Initialise the previous frame block history lists */ + for ( i = 0; i < MAX_PREV_FRAMES; i++ ) + memset( ppi->PrevFragments[i], BLOCK_NOT_CODED, ppi->ScanFrameFragments); + + /* YUVAnalyseFrame() is not called for the first frame in a sequence + (a key frame obviously). This memset insures that for the second + frame all blocks are marked for coding in line with the behaviour + for other key frames. */ + memset( ppi->PrevFragments[ppi->PrevFrameLimit-1], + BLOCK_CODED, ppi->ScanFrameFragments ); + + /* Initialise scan arrays */ + InitScanMapArrays(ppi); +} + +static void SetFromPrevious(PP_INSTANCE *ppi) { + unsigned int i,j; + + /* We buld up the list of previously updated blocks in the zero + index list of PrevFragments[] so we must start by reseting its + contents */ + memset( ppi->PrevFragments[0], BLOCK_NOT_CODED, ppi->ScanFrameFragments ); + + if ( ppi->PrevFrameLimit > 1 ){ + /* Now build up PrevFragments[0] from PrevFragments[1 to PrevFrameLimit] */ + for ( i = 0; i < ppi->ScanFrameFragments; i++ ){ + for ( j = 1; j < ppi->PrevFrameLimit; j++ ){ + if ( ppi->PrevFragments[j][i] > BLOCK_CODED_BAR ){ + ppi->PrevFragments[0][i] = BLOCK_CODED; + break; + } + } + } + } +} + +static void UpdatePreviousBlockLists(PP_INSTANCE *ppi) { + int i; + + /* Shift previous frame block lists along. */ + for ( i = ppi->PrevFrameLimit; i > 1; i-- ){ + memcpy( ppi->PrevFragments[i], ppi->PrevFragments[i-1], + ppi->ScanFrameFragments ); + } + + /* Now copy in this frames block list */ + memcpy( ppi->PrevFragments[1], ppi->ScanDisplayFragments, + ppi->ScanFrameFragments ); +} + +static void CreateOutputDisplayMap( PP_INSTANCE *ppi, + signed char *InternalFragmentsPtr, + signed char *RecentHistoryPtr, + unsigned char *ExternalFragmentsPtr ) { + ogg_uint32_t i; + ogg_uint32_t HistoryBlocksAdded = 0; + ogg_uint32_t YBand = (ppi->ScanYPlaneFragments/8); /* 1/8th of Y image. */ + + ppi->OutputBlocksUpdated = 0; + for ( i = 0; i < ppi->ScanFrameFragments; i++ ) { + if ( InternalFragmentsPtr[i] > BLOCK_NOT_CODED ) { + ppi->OutputBlocksUpdated ++; + ExternalFragmentsPtr[i] = 1; + }else if ( RecentHistoryPtr[i] == BLOCK_CODED ){ + HistoryBlocksAdded ++; + ExternalFragmentsPtr[i] = 1; + }else{ + ExternalFragmentsPtr[i] = 0; + } + } + + /* Add in a weighting for the history blocks that have been added */ + ppi->OutputBlocksUpdated += (HistoryBlocksAdded / HISTORY_BLOCK_FACTOR); + + /* Now calculate a key frame candidate indicator. This is based + upon Y data only and ignores the top and bottom 1/8 of the + image. Also ignore history blocks and BAR blocks. */ + ppi->KFIndicator = 0; + for ( i = YBand; i < (ppi->ScanYPlaneFragments - YBand); i++ ) + if ( InternalFragmentsPtr[i] > BLOCK_CODED_BAR ) + ppi->KFIndicator ++; + + /* Convert the KF score to a range 0-100 */ + ppi->KFIndicator = ((ppi->KFIndicator*100)/((ppi->ScanYPlaneFragments*3)/4)); +} + +static int RowSadScan( PP_INSTANCE *ppi, + unsigned char * YuvPtr1, + unsigned char * YuvPtr2, + signed char * DispFragPtr){ + ogg_int32_t i, j; + ogg_uint32_t GrpSad; + ogg_uint32_t LocalGrpLowSadThresh = ppi->ModifiedGrpLowSadThresh; + ogg_uint32_t LocalGrpHighSadThresh = ppi->ModifiedGrpHighSadThresh; + signed char *LocalDispFragPtr; + unsigned char *LocalYuvPtr1; + unsigned char *LocalYuvPtr2; + + int InterestingBlocksInRow = 0; + + /* For each row of pixels in the row of blocks */ + for ( j = 0; j < VFRAGPIXELS; j++ ){ + /* Set local block map pointer. */ + LocalDispFragPtr = DispFragPtr; + + /* Set the local pixel data pointers for this row.*/ + LocalYuvPtr1 = YuvPtr1; + LocalYuvPtr2 = YuvPtr2; + + /* Scan along the row of pixels If the block to which a group of + pixels belongs is already marked for update then do nothing. */ + for ( i = 0; i < ppi->PlaneHFragments; i ++ ){ + if ( *LocalDispFragPtr <= BLOCK_NOT_CODED ){ + /* Calculate the SAD score for the block row */ + GrpSad = dsp_row_sad8(ppi->dsp, LocalYuvPtr1,LocalYuvPtr2); + + /* Now test the group SAD score */ + if ( GrpSad > LocalGrpLowSadThresh ){ + /* If SAD very high we must update else we have candidate block */ + if ( GrpSad > LocalGrpHighSadThresh ){ + /* Force update */ + *LocalDispFragPtr = BLOCK_CODED; + }else{ + /* Possible Update required */ + *LocalDispFragPtr = CANDIDATE_BLOCK; + } + InterestingBlocksInRow = 1; + } + } + LocalDispFragPtr++; + + LocalYuvPtr1 += 8; + LocalYuvPtr2 += 8; + } + + /* Increment the base data pointers to the start of the next line. */ + YuvPtr1 += ppi->PlaneStride; + YuvPtr2 += ppi->PlaneStride; + } + + return InterestingBlocksInRow; + +} + +static int ColSadScan( PP_INSTANCE *ppi, + unsigned char * YuvPtr1, + unsigned char * YuvPtr2, + signed char * DispFragPtr ){ + ogg_int32_t i; + ogg_uint32_t MaxSad; + ogg_uint32_t LocalGrpLowSadThresh = ppi->ModifiedGrpLowSadThresh; + ogg_uint32_t LocalGrpHighSadThresh = ppi->ModifiedGrpHighSadThresh; + signed char * LocalDispFragPtr; + + unsigned char * LocalYuvPtr1; + unsigned char * LocalYuvPtr2; + + int InterestingBlocksInRow = 0; + + /* Set the local pixel data pointers for this row. */ + LocalYuvPtr1 = YuvPtr1; + LocalYuvPtr2 = YuvPtr2; + + /* Set local block map pointer. */ + LocalDispFragPtr = DispFragPtr; + + /* Scan along the row of blocks */ + for ( i = 0; i < ppi->PlaneHFragments; i ++ ){ + /* Skip if block already marked to be coded. */ + if ( *LocalDispFragPtr <= BLOCK_NOT_CODED ){ + /* Calculate the SAD score for the block column */ + MaxSad = dsp_col_sad8x8(ppi->dsp, LocalYuvPtr1, LocalYuvPtr2, ppi->PlaneStride ); + + /* Now test the group SAD score */ + if ( MaxSad > LocalGrpLowSadThresh ){ + /* If SAD very high we must update else we have candidate block */ + if ( MaxSad > LocalGrpHighSadThresh ){ + /* Force update */ + *LocalDispFragPtr = BLOCK_CODED; + }else{ + /* Possible Update required */ + *LocalDispFragPtr = CANDIDATE_BLOCK; + } + InterestingBlocksInRow = 1; + } + } + + /* Increment the block map pointer. */ + LocalDispFragPtr++; + + /* Step data pointers on ready for next block */ + LocalYuvPtr1 += HFRAGPIXELS; + LocalYuvPtr2 += HFRAGPIXELS; + } + + return InterestingBlocksInRow; +} + +static void SadPass2( PP_INSTANCE *ppi, + ogg_int32_t RowNumber, + signed char * DispFragPtr ){ + ogg_int32_t i; + + /* First row */ + if ( RowNumber == 0 ) { + /* First block in row. */ + if ( DispFragPtr[0] == CANDIDATE_BLOCK ){ + if ( (DispFragPtr[1] == BLOCK_CODED) || + (DispFragPtr[ppi->PlaneHFragments] == BLOCK_CODED) || + (DispFragPtr[ppi->PlaneHFragments+1] == BLOCK_CODED) ){ + ppi->TmpCodedMap[0] = BLOCK_CODED_LOW; + }else{ + ppi->TmpCodedMap[0] = DispFragPtr[0]; + } + }else{ + ppi->TmpCodedMap[0] = DispFragPtr[0]; + } + + /* All but first and last in row */ + for ( i = 1; (i < ppi->PlaneHFragments-1); i++ ){ + if ( DispFragPtr[i] == CANDIDATE_BLOCK ){ + if ( (DispFragPtr[i-1] == BLOCK_CODED) || + (DispFragPtr[i+1] == BLOCK_CODED) || + (DispFragPtr[i+ppi->PlaneHFragments] == BLOCK_CODED) || + (DispFragPtr[i+ppi->PlaneHFragments-1] == BLOCK_CODED) || + (DispFragPtr[i+ppi->PlaneHFragments+1] == BLOCK_CODED) ){ + ppi->TmpCodedMap[i] = BLOCK_CODED_LOW; + }else{ + ppi->TmpCodedMap[i] = DispFragPtr[i]; + } + }else{ + ppi->TmpCodedMap[i] = DispFragPtr[i]; + } + } + + /* Last block in row. */ + i = ppi->PlaneHFragments-1; + if ( DispFragPtr[i] == CANDIDATE_BLOCK ){ + if ( (DispFragPtr[i-1] == BLOCK_CODED) || + (DispFragPtr[i+ppi->PlaneHFragments] == BLOCK_CODED) || + (DispFragPtr[i+ppi->PlaneHFragments-1] == BLOCK_CODED) ){ + ppi->TmpCodedMap[i] = BLOCK_CODED_LOW; + }else{ + ppi->TmpCodedMap[i] = DispFragPtr[i]; + } + }else{ + ppi->TmpCodedMap[i] = DispFragPtr[i]; + } + }else if ( RowNumber < (ppi->PlaneVFragments - 1) ){ + /* General case */ + /* First block in row. */ + if ( DispFragPtr[0] == CANDIDATE_BLOCK ){ + if ( (DispFragPtr[1] == BLOCK_CODED) || + (DispFragPtr[(-ppi->PlaneHFragments)] == BLOCK_CODED) || + (DispFragPtr[(-ppi->PlaneHFragments)+1] == BLOCK_CODED) || + (DispFragPtr[ppi->PlaneHFragments] == BLOCK_CODED) || + (DispFragPtr[ppi->PlaneHFragments+1] == BLOCK_CODED) ){ + ppi->TmpCodedMap[0] = BLOCK_CODED_LOW; + }else{ + ppi->TmpCodedMap[0] = DispFragPtr[0]; + } + }else{ + ppi->TmpCodedMap[0] = DispFragPtr[0]; + } + + /* All but first and last in row */ + for ( i = 1; (i < ppi->PlaneHFragments-1); i++ ){ + if ( DispFragPtr[i] == CANDIDATE_BLOCK ){ + if ( (DispFragPtr[i-1] == BLOCK_CODED) || + (DispFragPtr[i+1] == BLOCK_CODED) || + (DispFragPtr[i-ppi->PlaneHFragments] == BLOCK_CODED) || + (DispFragPtr[i-ppi->PlaneHFragments-1] == BLOCK_CODED) || + (DispFragPtr[i-ppi->PlaneHFragments+1] == BLOCK_CODED) || + (DispFragPtr[i+ppi->PlaneHFragments] == BLOCK_CODED) || + (DispFragPtr[i+ppi->PlaneHFragments-1] == BLOCK_CODED) || + (DispFragPtr[i+ppi->PlaneHFragments+1] == BLOCK_CODED) ){ + ppi->TmpCodedMap[i] = BLOCK_CODED_LOW; + }else{ + ppi->TmpCodedMap[i] = DispFragPtr[i]; + } + }else{ + ppi->TmpCodedMap[i] = DispFragPtr[i]; + } + } + + /* Last block in row. */ + i = ppi->PlaneHFragments-1; + if ( DispFragPtr[i] == CANDIDATE_BLOCK ){ + if ( (DispFragPtr[i-1] == BLOCK_CODED) || + (DispFragPtr[i-ppi->PlaneHFragments] == BLOCK_CODED) || + (DispFragPtr[i-ppi->PlaneHFragments-1] == BLOCK_CODED) || + (DispFragPtr[i+ppi->PlaneHFragments] == BLOCK_CODED) || + (DispFragPtr[i+ppi->PlaneHFragments-1] == BLOCK_CODED) ){ + ppi->TmpCodedMap[i] = BLOCK_CODED_LOW; + }else{ + ppi->TmpCodedMap[i] = DispFragPtr[i]; + } + }else{ + ppi->TmpCodedMap[i] = DispFragPtr[i]; + } + }else{ + /* Last row */ + /* First block in row. */ + if ( DispFragPtr[0] == CANDIDATE_BLOCK ){ + if ( (DispFragPtr[1] == BLOCK_CODED) || + (DispFragPtr[(-ppi->PlaneHFragments)] == BLOCK_CODED) || + (DispFragPtr[(-ppi->PlaneHFragments)+1] == BLOCK_CODED)){ + ppi->TmpCodedMap[0] = BLOCK_CODED_LOW; + }else{ + ppi->TmpCodedMap[0] = DispFragPtr[0]; + } + }else{ + ppi->TmpCodedMap[0] = DispFragPtr[0]; + } + + /* All but first and last in row */ + for ( i = 1; (i < ppi->PlaneHFragments-1); i++ ){ + if ( DispFragPtr[i] == CANDIDATE_BLOCK ){ + if ( (DispFragPtr[i-1] == BLOCK_CODED) || + (DispFragPtr[i+1] == BLOCK_CODED) || + (DispFragPtr[i-ppi->PlaneHFragments] == BLOCK_CODED) || + (DispFragPtr[i-ppi->PlaneHFragments-1] == BLOCK_CODED) || + (DispFragPtr[i-ppi->PlaneHFragments+1] == BLOCK_CODED) ){ + ppi->TmpCodedMap[i] = BLOCK_CODED_LOW; + }else{ + ppi->TmpCodedMap[i] = DispFragPtr[i]; + } + }else{ + ppi->TmpCodedMap[i] = DispFragPtr[i]; + } + } + + /* Last block in row. */ + i = ppi->PlaneHFragments-1; + if ( DispFragPtr[i] == CANDIDATE_BLOCK ){ + if ( (DispFragPtr[i-1] == BLOCK_CODED) || + (DispFragPtr[i-ppi->PlaneHFragments] == BLOCK_CODED) || + (DispFragPtr[i-ppi->PlaneHFragments-1] == BLOCK_CODED) ){ + ppi->TmpCodedMap[i] = BLOCK_CODED_LOW; + }else{ + ppi->TmpCodedMap[i] = DispFragPtr[i]; + } + }else{ + ppi->TmpCodedMap[i] = DispFragPtr[i]; + } + } + + /* Now copy back the modified Fragment data */ + memcpy( &DispFragPtr[0], &ppi->TmpCodedMap[0], (ppi->PlaneHFragments) ); +} + +static unsigned char ApplyPakLowPass( PP_INSTANCE *ppi, + unsigned char * SrcPtr ){ + unsigned char * SrcPtr1 = SrcPtr - 1; + unsigned char * SrcPtr0 = SrcPtr1 - ppi->PlaneStride; /* Note the + use of + stride not + width. */ + unsigned char * SrcPtr2 = SrcPtr1 + ppi->PlaneStride; + + return (unsigned char)( ( (ogg_uint32_t)SrcPtr0[0] + + (ogg_uint32_t)SrcPtr0[1] + + (ogg_uint32_t)SrcPtr0[2] + + (ogg_uint32_t)SrcPtr1[0] + + (ogg_uint32_t)SrcPtr1[2] + + (ogg_uint32_t)SrcPtr2[0] + + (ogg_uint32_t)SrcPtr2[1] + + (ogg_uint32_t)SrcPtr2[2] ) >> 3 ); + +} + +static void RowDiffScan( PP_INSTANCE *ppi, + unsigned char * YuvPtr1, + unsigned char * YuvPtr2, + ogg_int16_t * YUVDiffsPtr, + unsigned char * bits_map_ptr, + signed char * SgcPtr, + signed char * DispFragPtr, + unsigned char * FDiffPixels, + ogg_int32_t * RowDiffsPtr, + unsigned char * ChLocalsPtr, int EdgeRow ){ + + ogg_int32_t i,j; + ogg_int32_t FragChangedPixels; + + ogg_int16_t Diff; /* Temp local workspace. */ + + /* Cannot use kernel if at edge or if PAK disabled */ + if ( (!ppi->PAKEnabled) || EdgeRow ){ + for ( i = 0; i < ppi->PlaneWidth; i += HFRAGPIXELS ){ + /* Reset count of pixels changed for the current fragment. */ + FragChangedPixels = 0; + + /* Test for break out conditions to save time. */ + if (*DispFragPtr == CANDIDATE_BLOCK){ + + /* Clear down entries in changed locals array */ + SET8_0(ChLocalsPtr); + + for ( j = 0; j < HFRAGPIXELS; j++ ){ + /* Take a local copy of the measured difference. */ + Diff = (int)YuvPtr1[j] - (int)YuvPtr2[j]; + + /* Store the actual difference value */ + YUVDiffsPtr[j] = Diff; + + /* Test against the Level thresholds and record the results */ + SgcPtr[0] += ppi->SgcThreshTable[Diff+255]; + + /* Test against the SRF thresholds */ + bits_map_ptr[j] = ppi->SrfThreshTable[Diff+255]; + FragChangedPixels += ppi->SrfThreshTable[Diff+255]; + } + }else{ + /* If we are breaking out here mark all pixels as changed. */ + if ( *DispFragPtr > BLOCK_NOT_CODED ){ + SET8_1(bits_map_ptr); + SET8_8(ChLocalsPtr); + }else{ + SET8_0(ChLocalsPtr); + } + } + + *RowDiffsPtr += FragChangedPixels; + *FDiffPixels += (unsigned char)FragChangedPixels; + + YuvPtr1 += HFRAGPIXELS; + YuvPtr2 += HFRAGPIXELS; + bits_map_ptr += HFRAGPIXELS; + ChLocalsPtr += HFRAGPIXELS; + YUVDiffsPtr += HFRAGPIXELS; + SgcPtr ++; + FDiffPixels ++; + + /* If we have a lot of changed pixels for this fragment on this + row then the fragment is almost sure to be picked (e.g. through + the line search) so we can mark it as selected and then ignore + it. */ + if (FragChangedPixels >= 7){ + *DispFragPtr = BLOCK_CODED_LOW; + } + DispFragPtr++; + } + }else{ + + /*************************************************************/ + /* First fragment of row !! */ + + i = 0; + /* Reset count of pixels changed for the current fragment. */ + FragChangedPixels = 0; + + /* Test for break out conditions to save time. */ + if (*DispFragPtr == CANDIDATE_BLOCK){ + /* Clear down entries in changed locals array */ + SET8_0(ChLocalsPtr); + + for ( j = 0; j < HFRAGPIXELS; j++ ){ + /* Take a local copy of the measured difference. */ + Diff = (int)YuvPtr1[j] - (int)YuvPtr2[j]; + + /* Store the actual difference value */ + YUVDiffsPtr[j] = Diff; + + /* Test against the Level thresholds and record the results */ + SgcPtr[0] += ppi->SgcThreshTable[Diff+255]; + + if (j>0 && ppi->SrfPakThreshTable[Diff+255] ) + Diff = (int)ApplyPakLowPass( ppi, &YuvPtr1[j] ) - + (int)ApplyPakLowPass( ppi, &YuvPtr2[j] ); + + /* Test against the SRF thresholds */ + bits_map_ptr[j] = ppi->SrfThreshTable[Diff+255]; + FragChangedPixels += ppi->SrfThreshTable[Diff+255]; + } + }else{ + /* If we are breaking out here mark all pixels as changed. */ + if ( *DispFragPtr > BLOCK_NOT_CODED ){ + SET8_1(bits_map_ptr); + SET8_8(ChLocalsPtr); + }else{ + SET8_0(ChLocalsPtr); + } + } + + *RowDiffsPtr += FragChangedPixels; + *FDiffPixels += (unsigned char)FragChangedPixels; + + YuvPtr1 += HFRAGPIXELS; + YuvPtr2 += HFRAGPIXELS; + bits_map_ptr += HFRAGPIXELS; + ChLocalsPtr += HFRAGPIXELS; + YUVDiffsPtr += HFRAGPIXELS; + SgcPtr ++; + FDiffPixels ++; + + /* If we have a lot of changed pixels for this fragment on this + row then the fragment is almost sure to be picked + (e.g. through the line search) so we can mark it as selected + and then ignore it. */ + if (FragChangedPixels >= 7){ + *DispFragPtr = BLOCK_CODED_LOW; + } + DispFragPtr++; + /*************************************************************/ + /* Fragment in between!! */ + + for ( i = HFRAGPIXELS ; i < ppi->PlaneWidth-HFRAGPIXELS; + i += HFRAGPIXELS ){ + /* Reset count of pixels changed for the current fragment. */ + FragChangedPixels = 0; + + /* Test for break out conditions to save time. */ + if (*DispFragPtr == CANDIDATE_BLOCK){ + /* Clear down entries in changed locals array */ + SET8_0(ChLocalsPtr); + for ( j = 0; j < HFRAGPIXELS; j++ ){ + /* Take a local copy of the measured difference. */ + Diff = (int)YuvPtr1[j] - (int)YuvPtr2[j]; + + /* Store the actual difference value */ + YUVDiffsPtr[j] = Diff; + + /* Test against the Level thresholds and record the results */ + SgcPtr[0] += ppi->SgcThreshTable[Diff+255]; + + if (ppi->SrfPakThreshTable[Diff+255] ) + Diff = (int)ApplyPakLowPass( ppi, &YuvPtr1[j] ) - + (int)ApplyPakLowPass( ppi, &YuvPtr2[j] ); + + + /* Test against the SRF thresholds */ + bits_map_ptr[j] = ppi->SrfThreshTable[Diff+255]; + FragChangedPixels += ppi->SrfThreshTable[Diff+255]; + } + }else{ + /* If we are breaking out here mark all pixels as changed. */ + if ( *DispFragPtr > BLOCK_NOT_CODED ){ + SET8_1(bits_map_ptr); + SET8_8(ChLocalsPtr); + }else{ + SET8_0(ChLocalsPtr); + } + } + + *RowDiffsPtr += FragChangedPixels; + *FDiffPixels += (unsigned char)FragChangedPixels; + + YuvPtr1 += HFRAGPIXELS; + YuvPtr2 += HFRAGPIXELS; + bits_map_ptr += HFRAGPIXELS; + ChLocalsPtr += HFRAGPIXELS; + YUVDiffsPtr += HFRAGPIXELS; + SgcPtr ++; + FDiffPixels ++; + + /* If we have a lot of changed pixels for this fragment on this + row then the fragment is almost sure to be picked + (e.g. through the line search) so we can mark it as selected + and then ignore it. */ + if (FragChangedPixels >= 7){ + *DispFragPtr = BLOCK_CODED_LOW; + } + DispFragPtr++; + } + /*************************************************************/ + /* Last fragment of row !! */ + + /* Reset count of pixels changed for the current fragment. */ + FragChangedPixels = 0; + + /* Test for break out conditions to save time. */ + if (*DispFragPtr == CANDIDATE_BLOCK){ + /* Clear down entries in changed locals array */ + SET8_0(ChLocalsPtr); + + for ( j = 0; j < HFRAGPIXELS; j++ ){ + /* Take a local copy of the measured difference. */ + Diff = (int)YuvPtr1[j] - (int)YuvPtr2[j]; + + /* Store the actual difference value */ + YUVDiffsPtr[j] = Diff; + + /* Test against the Level thresholds and record the results */ + SgcPtr[0] += ppi->SgcThreshTable[Diff+255]; + + if (j<7 && ppi->SrfPakThreshTable[Diff+255] ) + Diff = (int)ApplyPakLowPass( ppi, &YuvPtr1[j] ) - + (int)ApplyPakLowPass( ppi, &YuvPtr2[j] ); + + + /* Test against the SRF thresholds */ + bits_map_ptr[j] = ppi->SrfThreshTable[Diff+255]; + FragChangedPixels += ppi->SrfThreshTable[Diff+255]; + } + }else{ + /* If we are breaking out here mark all pixels as changed.*/ + if ( *DispFragPtr > BLOCK_NOT_CODED ) { + SET8_1(bits_map_ptr); + SET8_8(ChLocalsPtr); + }else{ + SET8_0(ChLocalsPtr); + } + } + /* If we have a lot of changed pixels for this fragment on this + row then the fragment is almost sure to be picked (e.g. through + the line search) so we can mark it as selected and then ignore + it. */ + *RowDiffsPtr += FragChangedPixels; + *FDiffPixels += (unsigned char)FragChangedPixels; + + /* If we have a lot of changed pixels for this fragment on this + row then the fragment is almost sure to be picked (e.g. through + the line search) so we can mark it as selected and then ignore + it. */ + if (FragChangedPixels >= 7){ + *DispFragPtr = BLOCK_CODED_LOW; + } + DispFragPtr++; + + } +} + +static void ConsolidateDiffScanResults( PP_INSTANCE *ppi, + unsigned char * FDiffPixels, + signed char * SgcScoresPtr, + signed char * DispFragPtr ){ + ogg_int32_t i; + + for ( i = 0; i < ppi->PlaneHFragments; i ++ ){ + /* Consider only those blocks that were candidates in the + difference scan. Ignore definite YES and NO cases. */ + if ( DispFragPtr[i] == CANDIDATE_BLOCK ){ + if ( ((ogg_uint32_t)abs(SgcScoresPtr[i]) > ppi->BlockSgcThresh) ){ + /* Block marked for update due to Sgc change */ + DispFragPtr[i] = BLOCK_CODED_SGC; + }else if ( FDiffPixels[i] == 0 ){ + /* Block is no longer a candidate for the main tests but will + still be considered a candidate in RowBarEnhBlockMap() */ + DispFragPtr[i] = CANDIDATE_BLOCK_LOW; + } + } + } +} + +static void RowChangedLocalsScan( PP_INSTANCE *ppi, + unsigned char * PixelMapPtr, + unsigned char * ChLocalsPtr, + signed char * DispFragPtr, + unsigned char RowType ){ + + unsigned char changed_locals = 0; + unsigned char * PixelsChangedPtr0; + unsigned char * PixelsChangedPtr1; + unsigned char * PixelsChangedPtr2; + ogg_int32_t i, j; + ogg_int32_t LastRowIndex = ppi->PlaneWidth - 1; + + /* Set up the line based pointers into the bits changed map. */ + PixelsChangedPtr0 = PixelMapPtr - ppi->PlaneWidth; + if ( PixelsChangedPtr0 < ppi->PixelChangedMap ) + PixelsChangedPtr0 += ppi->PixelMapCircularBufferSize; + PixelsChangedPtr0 -= 1; + + PixelsChangedPtr1 = PixelMapPtr - 1; + + PixelsChangedPtr2 = PixelMapPtr + ppi->PlaneWidth; + if ( PixelsChangedPtr2 >= + (ppi->PixelChangedMap + ppi->PixelMapCircularBufferSize) ) + PixelsChangedPtr2 -= ppi->PixelMapCircularBufferSize; + PixelsChangedPtr2 -= 1; + + if ( RowType == NOT_EDGE_ROW ){ + /* Scan through the row of pixels and calculate changed locals. */ + for ( i = 0; i < ppi->PlaneWidth; i += HFRAGPIXELS ){ + /* Skip a group of 8 pixels if the assosciated fragment has no + pixels of interest. */ + if ( *DispFragPtr == CANDIDATE_BLOCK ){ + for ( j = 0; j < HFRAGPIXELS; j++ ){ + changed_locals = 0; + + /* If the pixel itself has changed */ + if ( PixelsChangedPtr1[1] ){ + if ( (i > 0) || (j > 0) ){ + changed_locals += PixelsChangedPtr0[0]; + changed_locals += PixelsChangedPtr1[0]; + changed_locals += PixelsChangedPtr2[0]; + } + + changed_locals += PixelsChangedPtr0[1]; + changed_locals += PixelsChangedPtr2[1]; + + if ( (i + j) < LastRowIndex ){ + changed_locals += PixelsChangedPtr0[2]; + changed_locals += PixelsChangedPtr1[2]; + changed_locals += PixelsChangedPtr2[2]; + } + + /* Store the number of changed locals */ + *ChLocalsPtr |= changed_locals; + } + + /* Increment to next pixel in the row */ + ChLocalsPtr++; + PixelsChangedPtr0++; + PixelsChangedPtr1++; + PixelsChangedPtr2++; + } + }else{ + if ( *DispFragPtr > BLOCK_NOT_CODED ) + SET8_0(ChLocalsPtr); + + /* Step pointers */ + ChLocalsPtr += HFRAGPIXELS; + PixelsChangedPtr0 += HFRAGPIXELS; + PixelsChangedPtr1 += HFRAGPIXELS; + PixelsChangedPtr2 += HFRAGPIXELS; + } + + /* Move on to next fragment. */ + DispFragPtr++; + + } + }else{ + /* Scan through the row of pixels and calculate changed locals. */ + for ( i = 0; i < ppi->PlaneWidth; i += HFRAGPIXELS ){ + /* Skip a group of 8 pixels if the assosciated fragment has no + pixels of interest */ + if ( *DispFragPtr == CANDIDATE_BLOCK ){ + for ( j = 0; j < HFRAGPIXELS; j++ ){ + changed_locals = 0; + + /* If the pixel itself has changed */ + if ( PixelsChangedPtr1[1] ){ + if ( RowType == FIRST_ROW ){ + if ( (i > 0) || (j > 0) ){ + changed_locals += PixelsChangedPtr1[0]; + changed_locals += PixelsChangedPtr2[0]; + } + + changed_locals += PixelsChangedPtr2[1]; + + if ( (i + j) < LastRowIndex ){ + changed_locals += PixelsChangedPtr1[2]; + changed_locals += PixelsChangedPtr2[2]; + } + }else{ + if ( (i > 0) || (j > 0 ) ){ + changed_locals += PixelsChangedPtr0[0]; + changed_locals += PixelsChangedPtr1[0]; + } + + changed_locals += PixelsChangedPtr0[1]; + + if ( (i + j) < LastRowIndex ){ + changed_locals += PixelsChangedPtr0[2]; + changed_locals += PixelsChangedPtr1[2]; + } + } + + /* Store the number of changed locals */ + *ChLocalsPtr |= changed_locals; + } + + /* Increment to next pixel in the row */ + ChLocalsPtr++; + PixelsChangedPtr0++; + PixelsChangedPtr1++; + PixelsChangedPtr2++; + } + }else{ + if ( *DispFragPtr > BLOCK_NOT_CODED ) + SET8_0(ChLocalsPtr); + + /* Step pointers */ + ChLocalsPtr += HFRAGPIXELS; + PixelsChangedPtr0 += HFRAGPIXELS; + PixelsChangedPtr1 += HFRAGPIXELS; + PixelsChangedPtr2 += HFRAGPIXELS; + } + + /* Move on to next fragment. */ + DispFragPtr++; + } + } +} + +static void NoiseScoreRow( PP_INSTANCE *ppi, + unsigned char * PixelMapPtr, + unsigned char * ChLocalsPtr, + ogg_int16_t * YUVDiffsPtr, + unsigned char * PixelNoiseScorePtr, + ogg_uint32_t * FragScorePtr, + signed char * DispFragPtr, + ogg_int32_t * RowDiffsPtr ){ + ogg_int32_t i,j; + unsigned char changed_locals = 0; + ogg_int32_t Score; + ogg_uint32_t FragScore; + ogg_int32_t AbsDiff; + + /* For each pixel in the row */ + for ( i = 0; i < ppi->PlaneWidth; i += HFRAGPIXELS ){ + /* Skip a group of 8 pixels if the assosciated fragment has no + pixels of interest. */ + if ( *DispFragPtr == CANDIDATE_BLOCK ){ + /* Reset the cumulative fragment score. */ + FragScore = 0; + + /* Pixels grouped along the row into fragments */ + for ( j = 0; j < HFRAGPIXELS; j++ ){ + if ( PixelMapPtr[j] ){ + AbsDiff = (ogg_int32_t)( abs(YUVDiffsPtr[j]) ); + changed_locals = ChLocalsPtr[j]; + + /* Give this pixel a score based on changed locals and level + of its own change. */ + Score = (1 + ((ogg_int32_t)(changed_locals + + ppi->NoiseScoreBoostTable[AbsDiff]) - + ppi->NoiseSupLevel)); + + /* For no zero scores adjust by a level based score multiplier. */ + if ( Score > 0 ){ + Score = ((double)Score * + ppi->AbsDiff_ScoreMultiplierTable[AbsDiff] ); + if ( Score < 1 ) + Score = 1; + }else{ + /* Set -ve values to 0 */ + Score = 0; + + /* If there are no changed locals then clear the pixel + changed flag and decrement the pixels changed in + fragment count to speed later stages. */ + if ( changed_locals == 0 ){ + PixelMapPtr[j] = 0; + *RowDiffsPtr -= 1; + } + } + + /* Update the pixel scores etc. */ + PixelNoiseScorePtr[j] = (unsigned char)Score; + FragScore += (ogg_uint32_t)Score; + } + } + + /* Add fragment score (with plane correction factor) into main + data structure */ + *FragScorePtr += (ogg_int32_t)(FragScore * + ppi->YUVPlaneCorrectionFactor); + + /* If score is greater than trip threshold then mark blcok for update. */ + if ( *FragScorePtr > ppi->BlockThreshold ){ + *DispFragPtr = BLOCK_CODED_LOW; + } + } + + /* Increment the various pointers */ + FragScorePtr++; + DispFragPtr++; + PixelNoiseScorePtr += HFRAGPIXELS; + PixelMapPtr += HFRAGPIXELS; + ChLocalsPtr += HFRAGPIXELS; + YUVDiffsPtr += HFRAGPIXELS; + } +} + +static void PrimaryEdgeScoreRow( PP_INSTANCE *ppi, + unsigned char * ChangedLocalsPtr, + ogg_int16_t * YUVDiffsPtr, + unsigned char * PixelNoiseScorePtr, + ogg_uint32_t * FragScorePtr, + signed char * DispFragPtr, + unsigned char RowType ){ + ogg_uint32_t BodyNeighbours; + ogg_uint32_t AbsDiff; + unsigned char changed_locals = 0; + ogg_int32_t Score; + ogg_uint32_t FragScore; + unsigned char * CHLocalsPtr0; + unsigned char * CHLocalsPtr1; + unsigned char * CHLocalsPtr2; + ogg_int32_t i,j; + ogg_int32_t LastRowIndex = ppi->PlaneWidth - 1; + + /* Set up pointers into the current previous and next row of the + changed locals data structure. */ + CHLocalsPtr0 = ChangedLocalsPtr - ppi->PlaneWidth; + if ( CHLocalsPtr0 < ppi->ChLocals ) + CHLocalsPtr0 += ppi->ChLocalsCircularBufferSize; + CHLocalsPtr0 -= 1; + + CHLocalsPtr1 = ChangedLocalsPtr - 1; + + CHLocalsPtr2 = ChangedLocalsPtr + ppi->PlaneWidth; + if ( CHLocalsPtr2 >= (ppi->ChLocals + ppi->ChLocalsCircularBufferSize) ) + CHLocalsPtr2 -= ppi->ChLocalsCircularBufferSize; + CHLocalsPtr2 -= 1; + + + /* The defining rule used here is as follows. */ + /* An edge pixels has 3-5 changed locals. */ + /* And one or more of these changed locals has itself got 7-8 + changed locals. */ + + if ( RowType == NOT_EDGE_ROW ){ + /* Loop for all pixels in the row. */ + for ( i = 0; i < ppi->PlaneWidth; i += HFRAGPIXELS ){ + /* Does the fragment contain anything interesting to work with. */ + if ( *DispFragPtr == CANDIDATE_BLOCK ){ + /* Reset the cumulative fragment score. */ + FragScore = 0; + + /* Pixels grouped along the row into fragments */ + for ( j = 0; j < HFRAGPIXELS; j++ ){ + /* How many changed locals has the current pixel got. */ + changed_locals = ChangedLocalsPtr[j]; + + /* Is the pixel a suitable candidate */ + if ( (changed_locals > 2) && (changed_locals < 6) ){ + /* The pixel may qualify... have a closer look. */ + BodyNeighbours = 0; + + /* Count the number of "BodyNeighbours" .. Pixels that + have 7 or more changed neighbours. */ + if ( (i > 0) || (j > 0 ) ){ + if ( CHLocalsPtr0[0] >= 7 ) + BodyNeighbours++; + if ( CHLocalsPtr1[0] >= 7 ) + BodyNeighbours++; + if ( CHLocalsPtr2[0] >= 7 ) + BodyNeighbours++; + } + + if ( CHLocalsPtr0[1] >= 7 ) + BodyNeighbours++; + if ( CHLocalsPtr2[1] >= 7 ) + BodyNeighbours++; + + if ( (i + j) < LastRowIndex ){ + if ( CHLocalsPtr0[2] >= 7 ) + BodyNeighbours++; + if ( CHLocalsPtr1[2] >= 7 ) + BodyNeighbours++; + if ( CHLocalsPtr2[2] >= 7 ) + BodyNeighbours++; + } + + if ( BodyNeighbours > 0 ){ + AbsDiff = abs( YUVDiffsPtr[j] ); + Score = (ogg_int32_t) + ( (double)(BodyNeighbours * + BodyNeighbourScore) * + ppi->AbsDiff_ScoreMultiplierTable[AbsDiff] ); + if ( Score < 1 ) + Score = 1; + + /* Increment the score by a value determined by the + number of body neighbours. */ + PixelNoiseScorePtr[j] += (unsigned char)Score; + FragScore += (ogg_uint32_t)Score; + } + } + + /* Increment pointers into changed locals buffer */ + CHLocalsPtr0 ++; + CHLocalsPtr1 ++; + CHLocalsPtr2 ++; + } + + /* Add fragment score (with plane correction factor) into main + data structure */ + *FragScorePtr += (ogg_int32_t)(FragScore * + ppi->YUVPlaneCorrectionFactor); + + /* If score is greater than trip threshold then mark blcok for + update. */ + if ( *FragScorePtr > ppi->BlockThreshold ){ + *DispFragPtr = BLOCK_CODED_LOW; + } + + }else{ + /* Nothing to do for this fragment group */ + /* Advance pointers into changed locals buffer */ + CHLocalsPtr0 += HFRAGPIXELS; + CHLocalsPtr1 += HFRAGPIXELS; + CHLocalsPtr2 += HFRAGPIXELS; + } + + /* Increment the various pointers */ + FragScorePtr++; + DispFragPtr++; + PixelNoiseScorePtr += HFRAGPIXELS; + ChangedLocalsPtr += HFRAGPIXELS; + YUVDiffsPtr += HFRAGPIXELS; + } + }else{ + /* This is either the top or bottom row of pixels in a plane. */ + /* Loop for all pixels in the row. */ + for ( i = 0; i < ppi->PlaneWidth; i += HFRAGPIXELS ){ + /* Does the fragment contain anything interesting to work with. */ + if ( *DispFragPtr == CANDIDATE_BLOCK ){ + /* Reset the cumulative fragment score. */ + FragScore = 0; + + /* Pixels grouped along the row into fragments */ + for ( j = 0; j < HFRAGPIXELS; j++ ){ + /* How many changed locals has the current pixel got. */ + changed_locals = ChangedLocalsPtr[j]; + + /* Is the pixel a suitable candidate */ + if ( (changed_locals > 2) && (changed_locals < 6) ){ + /* The pixel may qualify... have a closer look. */ + BodyNeighbours = 0; + + /* Count the number of "BodyNeighbours" .. Pixels + that have 7 or more changed neighbours. */ + if ( RowType == LAST_ROW ){ + /* Test for cases where it could be the first pixel on + the line */ + if ( (i > 0) || (j > 0) ){ + if ( CHLocalsPtr0[0] >= 7 ) + BodyNeighbours++; + if ( CHLocalsPtr1[0] >= 7 ) + BodyNeighbours++; + } + + if ( CHLocalsPtr0[1] >= 7 ) + BodyNeighbours++; + + /* Test for the end of line case */ + if ( (i + j) < LastRowIndex ){ + if ( CHLocalsPtr0[2] >= 7 ) + BodyNeighbours++; + + if ( CHLocalsPtr1[2] >= 7 ) + BodyNeighbours++; + } + }else{ + /* First Row */ + /* Test for cases where it could be the first pixel on + the line */ + if ( (i > 0) || (j > 0) ){ + if ( CHLocalsPtr1[0] >= 7 ) + BodyNeighbours++; + if ( CHLocalsPtr2[0] >= 7 ) + BodyNeighbours++; + } + + /* Test for the end of line case */ + if ( CHLocalsPtr2[1] >= 7 ) + BodyNeighbours++; + + if ( (i + j) < LastRowIndex ){ + if ( CHLocalsPtr1[2] >= 7 ) + BodyNeighbours++; + if ( CHLocalsPtr2[2] >= 7 ) + BodyNeighbours++; + } + } + + /* Allocate a score according to the number of Body neighbours. */ + if ( BodyNeighbours > 0 ){ + AbsDiff = abs( YUVDiffsPtr[j] ); + Score = (ogg_int32_t) + ( (double)(BodyNeighbours * BodyNeighbourScore) * + ppi->AbsDiff_ScoreMultiplierTable[AbsDiff] ); + if ( Score < 1 ) + Score = 1; + + PixelNoiseScorePtr[j] += (unsigned char)Score; + FragScore += (ogg_uint32_t)Score; + } + } + + /* Increment pointers into changed locals buffer */ + CHLocalsPtr0 ++; + CHLocalsPtr1 ++; + CHLocalsPtr2 ++; + } + + /* Add fragment score (with plane correction factor) into main + data structure */ + *FragScorePtr += + (ogg_int32_t)(FragScore * ppi->YUVPlaneCorrectionFactor); + + /* If score is greater than trip threshold then mark blcok for + update. */ + if ( *FragScorePtr > ppi->BlockThreshold ){ + *DispFragPtr = BLOCK_CODED_LOW; + } + + }else{ + /* Nothing to do for this fragment group */ + /* Advance pointers into changed locals buffer */ + CHLocalsPtr0 += HFRAGPIXELS; + CHLocalsPtr1 += HFRAGPIXELS; + CHLocalsPtr2 += HFRAGPIXELS; + } + + /* Increment the various pointers */ + FragScorePtr++; + DispFragPtr++; + PixelNoiseScorePtr += HFRAGPIXELS; + ChangedLocalsPtr += HFRAGPIXELS; + YUVDiffsPtr += HFRAGPIXELS; + } + } +} + +static void PixelLineSearch( PP_INSTANCE *ppi, + unsigned char * ChangedLocalsPtr, + ogg_int32_t RowNumber, + ogg_int32_t ColNumber, + unsigned char direction, + ogg_uint32_t * line_length ){ + /* Exit if the pixel does not qualify or we have fallen off the edge + of either the image plane or the row. */ + if ( (RowNumber < 0) || + (RowNumber >= ppi->PlaneHeight) || + (ColNumber < 0) || + (ColNumber >= ppi->PlaneWidth) || + ((*ChangedLocalsPtr) <= 1) || + ((*ChangedLocalsPtr) >= 6) ){ + /* If not then it isn't part of any line. */ + return; + } + + if (*line_length < ppi->MaxLineSearchLen){ + ogg_uint32_t TmpLineLength; + ogg_uint32_t BestLineLength; + unsigned char * search_ptr; + + /* Increment the line length to include this pixel. */ + *line_length += 1; + BestLineLength = *line_length; + + /* Continue search */ + /* up */ + if ( direction == UP ){ + TmpLineLength = *line_length; + + search_ptr = ChangedLocalsPtr - ppi->PlaneWidth; + if ( search_ptr < ppi->ChLocals ) + search_ptr += ppi->ChLocalsCircularBufferSize; + + PixelLineSearch( ppi, search_ptr, RowNumber - 1, ColNumber, + direction, &TmpLineLength ); + + if ( TmpLineLength > BestLineLength ) + BestLineLength = TmpLineLength; + } + + /* up and left */ + if ( (BestLineLength < ppi->MaxLineSearchLen) && + ((direction == UP) || (direction == LEFT)) ){ + TmpLineLength = *line_length; + + search_ptr = ChangedLocalsPtr - ppi->PlaneWidth; + if ( search_ptr < ppi->ChLocals ) + search_ptr += ppi->ChLocalsCircularBufferSize; + search_ptr -= 1; + + PixelLineSearch( ppi, search_ptr, RowNumber - 1, ColNumber - 1, + direction, &TmpLineLength ); + + if ( TmpLineLength > BestLineLength ) + BestLineLength = TmpLineLength; + } + + /* up and right */ + if ( (BestLineLength < ppi->MaxLineSearchLen) && + ((direction == UP) || (direction == RIGHT)) ){ + TmpLineLength = *line_length; + + search_ptr = ChangedLocalsPtr - ppi->PlaneWidth; + if ( search_ptr < ppi->ChLocals ) + search_ptr += ppi->ChLocalsCircularBufferSize; + search_ptr += 1; + + PixelLineSearch( ppi, search_ptr, RowNumber - 1, ColNumber + 1, + direction, &TmpLineLength ); + + if ( TmpLineLength > BestLineLength ) + BestLineLength = TmpLineLength; + } + + /* left */ + if ( (BestLineLength < ppi->MaxLineSearchLen) && ( direction == LEFT ) ){ + TmpLineLength = *line_length; + PixelLineSearch( ppi, ChangedLocalsPtr - 1, RowNumber, ColNumber - 1, + direction, &TmpLineLength ); + + if ( TmpLineLength > BestLineLength ) + BestLineLength = TmpLineLength; + } + + /* right */ + if ( (BestLineLength < ppi->MaxLineSearchLen) && ( direction == RIGHT ) ){ + TmpLineLength = *line_length; + PixelLineSearch( ppi, ChangedLocalsPtr + 1, RowNumber, ColNumber + 1, + direction, &TmpLineLength ); + + if ( TmpLineLength > BestLineLength ) + BestLineLength = TmpLineLength; + } + + /* Down */ + if ( BestLineLength < ppi->MaxLineSearchLen ){ + TmpLineLength = *line_length; + if ( direction == DOWN ){ + search_ptr = ChangedLocalsPtr + ppi->PlaneWidth; + if ( search_ptr >= (ppi->ChLocals + ppi->ChLocalsCircularBufferSize) ) + search_ptr -= ppi->ChLocalsCircularBufferSize; + + PixelLineSearch( ppi, search_ptr, RowNumber + 1, ColNumber, direction, + &TmpLineLength ); + + if ( TmpLineLength > BestLineLength ) + BestLineLength = TmpLineLength; + } + + + /* down and left */ + if ( (BestLineLength < ppi->MaxLineSearchLen) && + ((direction == DOWN) || (direction == LEFT)) ){ + TmpLineLength = *line_length; + + search_ptr = ChangedLocalsPtr + ppi->PlaneWidth; + if ( search_ptr >= (ppi->ChLocals + ppi->ChLocalsCircularBufferSize) ) + search_ptr -= ppi->ChLocalsCircularBufferSize; + search_ptr -= 1; + + PixelLineSearch( ppi, search_ptr, RowNumber + 1, ColNumber - 1, + direction, &TmpLineLength ); + + if ( TmpLineLength > BestLineLength ) + BestLineLength = TmpLineLength; + } + + /* down and right */ + if ( (BestLineLength < ppi->MaxLineSearchLen) && + ((direction == DOWN) || (direction == RIGHT)) ){ + TmpLineLength = *line_length; + + search_ptr = ChangedLocalsPtr + ppi->PlaneWidth; + if ( search_ptr >= (ppi->ChLocals + ppi->ChLocalsCircularBufferSize) ) + search_ptr -= ppi->ChLocalsCircularBufferSize; + search_ptr += 1; + + PixelLineSearch( ppi, search_ptr, RowNumber + 1, ColNumber + 1, + direction, &TmpLineLength ); + + if ( TmpLineLength > BestLineLength ) + BestLineLength = TmpLineLength; + } + } + + /* Note the search value for this pixel. */ + *line_length = BestLineLength; + } +} + +static unsigned char LineSearchScorePixel( PP_INSTANCE *ppi, + unsigned char * ChangedLocalsPtr, + ogg_int32_t RowNumber, + ogg_int32_t ColNumber ){ + ogg_uint32_t line_length = 0; + ogg_uint32_t line_length2 = 0; + ogg_uint32_t line_length_score = 0; + ogg_uint32_t tmp_line_length = 0; + ogg_uint32_t tmp_line_length2 = 0; + + /* Look UP and Down */ + PixelLineSearch( ppi, ChangedLocalsPtr, RowNumber, + ColNumber, UP, &tmp_line_length ); + + if (tmp_line_length < ppi->MaxLineSearchLen) { + /* Look DOWN */ + PixelLineSearch( ppi, ChangedLocalsPtr, RowNumber, + ColNumber, DOWN, &tmp_line_length2 ); + line_length = tmp_line_length + tmp_line_length2 - 1; + + if ( line_length > ppi->MaxLineSearchLen ) + line_length = ppi->MaxLineSearchLen; + }else + line_length = tmp_line_length; + + /* If no max length line found then look left and right */ + if ( line_length < ppi->MaxLineSearchLen ){ + tmp_line_length = 0; + tmp_line_length2 = 0; + + PixelLineSearch( ppi, ChangedLocalsPtr, RowNumber, + ColNumber, LEFT, &tmp_line_length ); + if (tmp_line_length < ppi->MaxLineSearchLen){ + PixelLineSearch( ppi, ChangedLocalsPtr, RowNumber, + ColNumber, RIGHT, &tmp_line_length2 ); + line_length2 = tmp_line_length + tmp_line_length2 - 1; + + if ( line_length2 > ppi->MaxLineSearchLen ) + line_length2 = ppi->MaxLineSearchLen; + }else + line_length2 = tmp_line_length; + + } + + /* Take the largest line length */ + if ( line_length2 > line_length ) + line_length = line_length2; + + /* Create line length score */ + line_length_score = LineLengthScores[line_length]; + + return (unsigned char)line_length_score; +} + +static void LineSearchScoreRow( PP_INSTANCE *ppi, + unsigned char * ChangedLocalsPtr, + ogg_int16_t * YUVDiffsPtr, + unsigned char * PixelNoiseScorePtr, + ogg_uint32_t * FragScorePtr, + signed char * DispFragPtr, + ogg_int32_t RowNumber ){ + ogg_uint32_t AbsDiff; + unsigned char changed_locals = 0; + ogg_int32_t Score; + ogg_uint32_t FragScore; + ogg_int32_t i,j; + + /* The defining rule used here is as follows. */ + /* An edge pixels has 2-5 changed locals. */ + /* And one or more of these changed locals has itself got 7-8 + changed locals. */ + + /* Loop for all pixels in the row. */ + for ( i = 0; i < ppi->PlaneWidth; i += HFRAGPIXELS ){ + /* Does the fragment contain anything interesting to work with. */ + if ( *DispFragPtr == CANDIDATE_BLOCK ){ + /* Reset the cumulative fragment score. */ + FragScore = 0; + + /* Pixels grouped along the row into fragments */ + for ( j = 0; j < HFRAGPIXELS; j++ ){ + /* How many changed locals has the current pixel got. */ + changed_locals = ChangedLocalsPtr[j]; + + /* Is the pixel a suitable candidate for edge enhancement */ + if ( (changed_locals > 1) && (changed_locals < 6) && + (PixelNoiseScorePtr[j] < ppi->LineSearchTripTresh) ) { + Score = (ogg_int32_t) + LineSearchScorePixel( ppi, &ChangedLocalsPtr[j], RowNumber, i+j ); + + if ( Score ){ + AbsDiff = abs( YUVDiffsPtr[j] ); + Score = (ogg_int32_t) + ( (double)Score * ppi->AbsDiff_ScoreMultiplierTable[AbsDiff] ); + if ( Score < 1 ) + Score = 1; + + PixelNoiseScorePtr[j] += (unsigned char)Score; + FragScore += (ogg_uint32_t)Score; + } + } + } + + /* Add fragment score (with plane correction factor) into main + data structure */ + *FragScorePtr += + (ogg_int32_t)(FragScore * ppi->YUVPlaneCorrectionFactor); + + /* If score is greater than trip threshold then mark blcok for update. */ + if ( *FragScorePtr > ppi->BlockThreshold ){ + *DispFragPtr = BLOCK_CODED_LOW; + } + } + + /* Increment the various pointers */ + FragScorePtr++; + DispFragPtr++; + PixelNoiseScorePtr += HFRAGPIXELS; + ChangedLocalsPtr += HFRAGPIXELS; + YUVDiffsPtr += HFRAGPIXELS; + + } +} + +static void RowCopy( PP_INSTANCE *ppi, ogg_uint32_t BlockMapIndex ){ + + ogg_uint32_t i,j; + + ogg_uint32_t PixelIndex = ppi->ScanPixelIndexTable[BlockMapIndex]; + signed char * BlockMapPtr = &ppi->ScanDisplayFragments[BlockMapIndex]; + signed char * PrevFragmentsPtr = &ppi->PrevFragments[0][BlockMapIndex]; + + unsigned char * SourcePtr; + unsigned char * DestPtr; + + /* Copy pixels from changed blocks back to reference frame. */ + for ( i = 0; i < (ogg_uint32_t)ppi->PlaneHFragments; i ++ ){ + /* If the fragement is marked for update or was recently marked + for update (PrevFragmentsPtr[i]) */ + if ( (BlockMapPtr[i] > BLOCK_NOT_CODED) || + (PrevFragmentsPtr[i] == BLOCK_CODED) ){ + /* Set up the various pointers required. */ + SourcePtr = &ppi->ScanConfig.Yuv1ptr[PixelIndex]; + DestPtr = &ppi->ScanConfig.SrfWorkSpcPtr[PixelIndex]; + + /* For each row of the block */ + for ( j = 0; j < VFRAGPIXELS; j++ ){ + /* Copy the data unaltered from source to destination */ + memcpy(DestPtr,SourcePtr,8); + + /* Increment pointers for next line in the block */ + SourcePtr += ppi->PlaneWidth; + DestPtr += ppi->PlaneWidth; + } + } + + /* Increment pixel index for next block. */ + PixelIndex += HFRAGPIXELS; + } +} + +static void RowBarEnhBlockMap( PP_INSTANCE *ppi, + signed char * UpdatedBlockMapPtr, + signed char * BarBlockMapPtr, + ogg_uint32_t RowNumber ){ + int i; + + /* Start by blanking the row in the bar block map structure. */ + memset( BarBlockMapPtr, BLOCK_NOT_CODED, ppi->PlaneHFragments ); + + /* First row */ + if ( RowNumber == 0 ){ + + /* For each fragment in the row. */ + for ( i = 0; i < ppi->PlaneHFragments; i ++ ){ + /* Test for CANDIDATE_BLOCK or CANDIDATE_BLOCK_LOW. Uncoded or + coded blocks will be ignored. */ + if ( UpdatedBlockMapPtr[i] <= CANDIDATE_BLOCK ){ + /* Is one of the immediate neighbours updated in the main map. */ + /* Note special cases for blocks at the start and end of rows. */ + if ( i == 0 ){ + + if ((UpdatedBlockMapPtr[i+1] > BLOCK_NOT_CODED ) || + (UpdatedBlockMapPtr[i+ppi->PlaneHFragments]>BLOCK_NOT_CODED ) || + (UpdatedBlockMapPtr[i+ppi->PlaneHFragments+1]>BLOCK_NOT_CODED ) ) + BarBlockMapPtr[i] = BLOCK_CODED_BAR; + + + }else if ( i == (ppi->PlaneHFragments - 1) ){ + + if ((UpdatedBlockMapPtr[i-1] > BLOCK_NOT_CODED ) || + (UpdatedBlockMapPtr[i+ppi->PlaneHFragments-1]>BLOCK_NOT_CODED) || + (UpdatedBlockMapPtr[i+ppi->PlaneHFragments]>BLOCK_NOT_CODED) ) + BarBlockMapPtr[i] = BLOCK_CODED_BAR; + + }else{ + if((UpdatedBlockMapPtr[i-1] > BLOCK_NOT_CODED ) || + (UpdatedBlockMapPtr[i+1] > BLOCK_NOT_CODED ) || + (UpdatedBlockMapPtr[i+ppi->PlaneHFragments-1] > BLOCK_NOT_CODED)|| + (UpdatedBlockMapPtr[i+ppi->PlaneHFragments] > BLOCK_NOT_CODED ) || + (UpdatedBlockMapPtr[i+ppi->PlaneHFragments+1] > BLOCK_NOT_CODED) ) + BarBlockMapPtr[i] = BLOCK_CODED_BAR; + } + } + } + + } else if ( RowNumber == (ogg_uint32_t)(ppi->PlaneVFragments-1)) { + + /* Last row */ + /* Used to read PlaneHFragments */ + + /* For each fragment in the row. */ + for ( i = 0; i < ppi->PlaneHFragments; i ++ ){ + /* Test for CANDIDATE_BLOCK or CANDIDATE_BLOCK_LOW + Uncoded or coded blocks will be ignored. */ + if ( UpdatedBlockMapPtr[i] <= CANDIDATE_BLOCK ){ + /* Is one of the immediate neighbours updated in the main map. */ + /* Note special cases for blocks at the start and end of rows. */ + if ( i == 0 ){ + if((UpdatedBlockMapPtr[i+1] > BLOCK_NOT_CODED ) || + (UpdatedBlockMapPtr[i-ppi->PlaneHFragments] > BLOCK_NOT_CODED ) || + (UpdatedBlockMapPtr[i-ppi->PlaneHFragments+1] > BLOCK_NOT_CODED )) + BarBlockMapPtr[i] = BLOCK_CODED_BAR; + + }else if ( i == (ppi->PlaneHFragments - 1) ){ + if((UpdatedBlockMapPtr[i-1] > BLOCK_NOT_CODED ) || + (UpdatedBlockMapPtr[i-ppi->PlaneHFragments-1] > BLOCK_NOT_CODED)|| + (UpdatedBlockMapPtr[i-ppi->PlaneHFragments] > BLOCK_NOT_CODED ) ) + BarBlockMapPtr[i] = BLOCK_CODED_BAR; + }else{ + if((UpdatedBlockMapPtr[i-1] > BLOCK_NOT_CODED ) || + (UpdatedBlockMapPtr[i+1] > BLOCK_NOT_CODED ) || + (UpdatedBlockMapPtr[i-ppi->PlaneHFragments-1] > BLOCK_NOT_CODED)|| + (UpdatedBlockMapPtr[i-ppi->PlaneHFragments] > BLOCK_NOT_CODED ) || + (UpdatedBlockMapPtr[i-ppi->PlaneHFragments+1] > BLOCK_NOT_CODED) ) + BarBlockMapPtr[i] = BLOCK_CODED_BAR; + } + } + } + + }else{ + /* All other rows */ + /* For each fragment in the row. */ + for ( i = 0; i < ppi->PlaneHFragments; i ++ ){ + /* Test for CANDIDATE_BLOCK or CANDIDATE_BLOCK_LOW */ + /* Uncoded or coded blocks will be ignored. */ + if ( UpdatedBlockMapPtr[i] <= CANDIDATE_BLOCK ){ + /* Is one of the immediate neighbours updated in the main map. */ + /* Note special cases for blocks at the start and end of rows. */ + if ( i == 0 ){ + + if((UpdatedBlockMapPtr[i+1] > BLOCK_NOT_CODED ) || + (UpdatedBlockMapPtr[i-ppi->PlaneHFragments] > BLOCK_NOT_CODED ) || + (UpdatedBlockMapPtr[i-ppi->PlaneHFragments+1] > BLOCK_NOT_CODED)|| + (UpdatedBlockMapPtr[i+ppi->PlaneHFragments] > BLOCK_NOT_CODED ) || + (UpdatedBlockMapPtr[i+ppi->PlaneHFragments+1] > BLOCK_NOT_CODED) ) + BarBlockMapPtr[i] = BLOCK_CODED_BAR; + + }else if ( i == (ppi->PlaneHFragments - 1) ){ + + if((UpdatedBlockMapPtr[i-1] > BLOCK_NOT_CODED ) || + (UpdatedBlockMapPtr[i-ppi->PlaneHFragments-1] > BLOCK_NOT_CODED)|| + (UpdatedBlockMapPtr[i-ppi->PlaneHFragments] > BLOCK_NOT_CODED ) || + (UpdatedBlockMapPtr[i+ppi->PlaneHFragments-1] > BLOCK_NOT_CODED)|| + (UpdatedBlockMapPtr[i+ppi->PlaneHFragments] > BLOCK_NOT_CODED ) ) + BarBlockMapPtr[i] = BLOCK_CODED_BAR; + + }else{ + if((UpdatedBlockMapPtr[i-1] > BLOCK_NOT_CODED ) || + (UpdatedBlockMapPtr[i+1] > BLOCK_NOT_CODED ) || + (UpdatedBlockMapPtr[i-ppi->PlaneHFragments-1] > BLOCK_NOT_CODED)|| + (UpdatedBlockMapPtr[i-ppi->PlaneHFragments] > BLOCK_NOT_CODED ) || + (UpdatedBlockMapPtr[i-ppi->PlaneHFragments+1] > BLOCK_NOT_CODED)|| + (UpdatedBlockMapPtr[i+ppi->PlaneHFragments-1] > BLOCK_NOT_CODED)|| + (UpdatedBlockMapPtr[i+ppi->PlaneHFragments] > BLOCK_NOT_CODED ) || + (UpdatedBlockMapPtr[i+ppi->PlaneHFragments+1] > BLOCK_NOT_CODED )) + BarBlockMapPtr[i] = BLOCK_CODED_BAR; + } + } + } + } +} + +static void BarCopyBack( PP_INSTANCE *ppi, + signed char * UpdatedBlockMapPtr, + signed char * BarBlockMapPtr ){ + ogg_int32_t i; + + /* For each fragment in the row. */ + for ( i = 0; i < ppi->PlaneHFragments; i ++ ){ + if ( BarBlockMapPtr[i] > BLOCK_NOT_CODED ){ + UpdatedBlockMapPtr[i] = BarBlockMapPtr[i]; + } + } +} + +static void AnalysePlane( PP_INSTANCE *ppi, + unsigned char * PlanePtr0, + unsigned char * PlanePtr1, + ogg_uint32_t FragArrayOffset, + ogg_uint32_t PWidth, + ogg_uint32_t PHeight, + ogg_uint32_t PStride ) { + unsigned char * RawPlanePtr0; + unsigned char * RawPlanePtr1; + + ogg_int16_t * YUVDiffsPtr; + ogg_int16_t * YUVDiffsPtr1; + ogg_int16_t * YUVDiffsPtr2; + + ogg_uint32_t FragIndex; + ogg_uint32_t ScoreFragIndex1; + ogg_uint32_t ScoreFragIndex2; + ogg_uint32_t ScoreFragIndex3; + ogg_uint32_t ScoreFragIndex4; + + int UpdatedOrCandidateBlocks = 0; + + unsigned char * ChLocalsPtr0; + unsigned char * ChLocalsPtr1; + unsigned char * ChLocalsPtr2; + + unsigned char * PixelsChangedPtr0; + unsigned char * PixelsChangedPtr1; + + unsigned char * PixelScoresPtr1; + unsigned char * PixelScoresPtr2; + + signed char * DispFragPtr0; + signed char * DispFragPtr1; + signed char * DispFragPtr2; + + ogg_uint32_t * FragScoresPtr1; + ogg_uint32_t * FragScoresPtr2; + + ogg_int32_t * RowDiffsPtr; + ogg_int32_t * RowDiffsPtr1; + ogg_int32_t * RowDiffsPtr2; + + ogg_int32_t i,j; + + ogg_int32_t RowNumber1; + ogg_int32_t RowNumber2; + ogg_int32_t RowNumber3; + ogg_int32_t RowNumber4; + + int EdgeRow; + ogg_int32_t LineSearchRowNumber = 0; + + /* Variables used as temporary stores for frequently used values. */ + ogg_int32_t Row0Mod3; + ogg_int32_t Row1Mod3; + ogg_int32_t Row2Mod3; + ogg_int32_t BlockRowPixels; + + /* Set pixel difference threshold */ + if ( FragArrayOffset == 0 ){ + /* Luminance */ + ppi->LevelThresh = (int)ppi->SgcLevelThresh; + ppi->NegLevelThresh = -ppi->LevelThresh; + + ppi->SrfThresh = (int)ppi->SRFGreyThresh; + ppi->NegSrfThresh = -ppi->SrfThresh; + + /* Scores correction for Y pixels. */ + ppi->YUVPlaneCorrectionFactor = 1.0; + + ppi->BlockThreshold = ppi->PrimaryBlockThreshold; + ppi->BlockSgcThresh = ppi->SgcThresh; + }else{ + /* Chrominance */ + ppi->LevelThresh = (int)ppi->SuvcLevelThresh; + ppi->NegLevelThresh = -ppi->LevelThresh; + + ppi->SrfThresh = (int)ppi->SRFColThresh; + ppi->NegSrfThresh = -ppi->SrfThresh; + + /* Scores correction for UV pixels. */ + ppi->YUVPlaneCorrectionFactor = 1.5; + + /* Block threholds different for subsampled U and V blocks */ + ppi->BlockThreshold = + (ppi->PrimaryBlockThreshold / ppi->UVBlockThreshCorrection); + ppi->BlockSgcThresh = + (ppi->SgcThresh / ppi->UVSgcCorrection); + } + + /* Initialise the SRF thresh table and pointer. */ + memset( ppi->SrfThreshTable, 1, 512 ); + for ( i = ppi->NegSrfThresh; i <= ppi->SrfThresh; i++ ) + ppi->SrfThreshTable[i+255] = 0; + + /* Initialise the PAK thresh table. */ + for ( i = -255; i <= 255; i++ ) + if ( ppi->SrfThreshTable[i+255] && + (i <= ppi->HighChange) && + (i >= ppi->NegHighChange) ) + ppi->SrfPakThreshTable[i+255] = 1; + else + ppi->SrfPakThreshTable[i+255] = 0; + + /* Initialise the SGc lookup table */ + for ( i = -255; i <= 255; i++ ){ + if ( i <= ppi->NegLevelThresh ) + ppi->SgcThreshTable[i+255] = (unsigned char) -1; + else if ( i >= ppi->LevelThresh ) + ppi->SgcThreshTable[i+255] = 1; + else + ppi->SgcThreshTable[i+255] = 0; + } + + /* Set up plane dimension variables */ + ppi->PlaneHFragments = PWidth / HFRAGPIXELS; + ppi->PlaneVFragments = PHeight / VFRAGPIXELS; + ppi->PlaneWidth = PWidth; + ppi->PlaneHeight = PHeight; + ppi->PlaneStride = PStride; + + /* Set up local pointers into the raw image data. */ + RawPlanePtr0 = PlanePtr0; + RawPlanePtr1 = PlanePtr1; + + /* Note size and endo points for circular buffers. */ + ppi->YuvDiffsCircularBufferSize = YDIFF_CB_ROWS * ppi->PlaneWidth; + ppi->ChLocalsCircularBufferSize = CHLOCALS_CB_ROWS * ppi->PlaneWidth; + ppi->PixelMapCircularBufferSize = PMAP_CB_ROWS * ppi->PlaneWidth; + + /* Set high change thresh where PAK not needed */ + ppi->HighChange = ppi->SrfThresh * 4; + ppi->NegHighChange = -ppi->HighChange; + + /* Set up row difference pointers. */ + RowDiffsPtr = ppi->RowChangedPixels; + RowDiffsPtr1 = ppi->RowChangedPixels; + RowDiffsPtr2 = ppi->RowChangedPixels; + + BlockRowPixels = ppi->PlaneWidth * VFRAGPIXELS; + + for ( i = 0; i < (ppi->PlaneVFragments + 4); i++ ){ + RowNumber1 = (i - 1); + RowNumber2 = (i - 2); + RowNumber3 = (i - 3); + RowNumber4 = (i - 4); + + /* Pre calculate some frequently used values */ + Row0Mod3 = i % 3; + Row1Mod3 = RowNumber1 % 3; + Row2Mod3 = RowNumber2 % 3; + + /* For row diff scan last two iterations are invalid */ + if ( i < ppi->PlaneVFragments ){ + FragIndex = (i * ppi->PlaneHFragments) + FragArrayOffset; + YUVDiffsPtr = &ppi->yuv_differences[Row0Mod3 * BlockRowPixels]; + + PixelsChangedPtr0 = (&ppi->PixelChangedMap[Row0Mod3 * BlockRowPixels]); + DispFragPtr0 = &ppi->ScanDisplayFragments[FragIndex]; + + ChLocalsPtr0 = (&ppi->ChLocals[Row0Mod3 * BlockRowPixels]); + + } + + /* Set up the changed locals pointer to trail behind by one row of + fragments. */ + if ( i > 0 ){ + /* For last iteration the ch locals and noise scans are invalid */ + if ( RowNumber1 < ppi->PlaneVFragments ){ + ScoreFragIndex1 = (RowNumber1 * ppi->PlaneHFragments) + + FragArrayOffset; + + ChLocalsPtr1 = &ppi->ChLocals[Row1Mod3 * BlockRowPixels]; + PixelsChangedPtr1 = + &ppi->PixelChangedMap[(Row1Mod3) * BlockRowPixels]; + + PixelScoresPtr1 = &ppi->PixelScores[(RowNumber1 % 4) * BlockRowPixels]; + + YUVDiffsPtr1 = &ppi->yuv_differences[Row1Mod3 * BlockRowPixels]; + FragScoresPtr1 = &ppi->FragScores[ScoreFragIndex1]; + DispFragPtr1 = &ppi->ScanDisplayFragments[ScoreFragIndex1]; + + } + + if ( RowNumber2 >= 0 ){ + ScoreFragIndex2 = (RowNumber2 * ppi->PlaneHFragments) + + FragArrayOffset; + ChLocalsPtr2 = (&ppi->ChLocals[Row2Mod3 * BlockRowPixels]); + YUVDiffsPtr2 = &ppi->yuv_differences[Row2Mod3 * BlockRowPixels]; + + PixelScoresPtr2 = &ppi->PixelScores[(RowNumber2 % 4) * BlockRowPixels]; + + FragScoresPtr2 = &ppi->FragScores[ScoreFragIndex2]; + DispFragPtr2 = &ppi->ScanDisplayFragments[ScoreFragIndex2]; + }else{ + ChLocalsPtr2 = NULL; + } + }else{ + ChLocalsPtr1 = NULL; + ChLocalsPtr2 = NULL; + } + + /* Fast break out test for obvious yes and no cases in this row of + blocks */ + if ( i < ppi->PlaneVFragments ){ + dsp_save_fpu (ppi->dsp); + UpdatedOrCandidateBlocks = + RowSadScan( ppi, RawPlanePtr0, RawPlanePtr1, DispFragPtr0 ); + UpdatedOrCandidateBlocks |= + ColSadScan( ppi, RawPlanePtr0, RawPlanePtr1, DispFragPtr0 ); + dsp_restore_fpu (ppi->dsp); + }else{ + /* Make sure we still call other functions if RowSadScan() disabled */ + UpdatedOrCandidateBlocks = 1; + } + + /* Consolidation and fast break ot tests at Row 1 level */ + if ( (i > 0) && (RowNumber1 < ppi->PlaneVFragments) ){ + /* Mark as coded any candidate block that lies adjacent to a + coded block. */ + SadPass2( ppi, RowNumber1, DispFragPtr1 ); + + /* Check results of diff scan in last set of blocks. */ + /* Eliminate NO cases and add in +SGC cases */ + ConsolidateDiffScanResults( ppi, &ppi->FragDiffPixels[ScoreFragIndex1], + &ppi->SameGreyDirPixels[ScoreFragIndex1], + DispFragPtr1 + ); + } + + for ( j = 0; j < VFRAGPIXELS; j++ ){ + /* Last two iterations do not apply */ + if ( i < ppi->PlaneVFragments ){ + /* Is the current fragment at an edge. */ + EdgeRow = ( ( (i == 0) && (j == 0) ) || + ( (i == (ppi->PlaneVFragments - 1)) && + (j == (VFRAGPIXELS - 1)) ) ); + + /* Clear the arrays that will be used for the changed pixels maps */ + memset( PixelsChangedPtr0, 0, ppi->PlaneWidth ); + + /* Difference scan and map each row */ + if ( UpdatedOrCandidateBlocks ){ + /* Scan the row for interesting differences */ + /* Also clear the array that will be used for changed locals map */ + RowDiffScan( ppi, RawPlanePtr0, RawPlanePtr1, + YUVDiffsPtr, PixelsChangedPtr0, + &ppi->SameGreyDirPixels[FragIndex], + DispFragPtr0, &ppi->FragDiffPixels[FragIndex], + RowDiffsPtr, ChLocalsPtr0, EdgeRow); + }else{ + /* Clear the array that will be used for changed locals map */ + memset( ChLocalsPtr0, 0, ppi->PlaneWidth ); + } + + /* The actual image plane pointers must be incremented by + stride as this may be different (more) than the plane + width. Our own internal buffers use ppi->PlaneWidth. */ + RawPlanePtr0 += ppi->PlaneStride; + RawPlanePtr1 += ppi->PlaneStride; + PixelsChangedPtr0 += ppi->PlaneWidth; + ChLocalsPtr0 += ppi->PlaneWidth; + YUVDiffsPtr += ppi->PlaneWidth; + RowDiffsPtr++; + } + + /* Run behind calculating the changed locals data and noise scores. */ + if ( ChLocalsPtr1 != NULL ){ + /* Last few iterations do not apply */ + if ( RowNumber1 < ppi->PlaneVFragments ){ + /* Blank the next row in the pixel scores data structure. */ + memset( PixelScoresPtr1, 0, ppi->PlaneWidth ); + + /* Don't bother doing anything if there are no changed + pixels in this row */ + if ( *RowDiffsPtr1 ){ + /* Last valid row is a special case */ + if ( i < ppi->PlaneVFragments ) + RowChangedLocalsScan( ppi, PixelsChangedPtr1, ChLocalsPtr1, + DispFragPtr1, + ( (((i-1)==0) && (j==0)) ? + FIRST_ROW : NOT_EDGE_ROW) ); + else + RowChangedLocalsScan( ppi, PixelsChangedPtr1, ChLocalsPtr1, + DispFragPtr1, + ((j==(VFRAGPIXELS-1)) ? + LAST_ROW : NOT_EDGE_ROW) ); + + NoiseScoreRow( ppi, PixelsChangedPtr1, ChLocalsPtr1, YUVDiffsPtr1, + PixelScoresPtr1, FragScoresPtr1, DispFragPtr1, + RowDiffsPtr1 ); + } + + ChLocalsPtr1 += ppi->PlaneWidth; + PixelsChangedPtr1 += ppi->PlaneWidth; + YUVDiffsPtr1 += ppi->PlaneWidth; + PixelScoresPtr1 += ppi->PlaneWidth; + RowDiffsPtr1 ++; + } + + /* Run edge enhancement algorithms */ + if ( RowNumber2 < ppi->PlaneVFragments ){ + if ( ChLocalsPtr2 != NULL ){ + /* Don't bother doing anything if there are no changed + pixels in this row */ + if ( *RowDiffsPtr2 ){ + if ( RowNumber1 < ppi->PlaneVFragments ){ + PrimaryEdgeScoreRow( ppi, ChLocalsPtr2, YUVDiffsPtr2, + PixelScoresPtr2, FragScoresPtr2, + DispFragPtr2, + ( (((i-2)==0) && (j==0)) ? + FIRST_ROW : NOT_EDGE_ROW) ); + }else{ + /* Edge enhancement */ + PrimaryEdgeScoreRow( ppi, ChLocalsPtr2, YUVDiffsPtr2, + PixelScoresPtr2, FragScoresPtr2, + DispFragPtr2, + ((j==(VFRAGPIXELS-1)) ? + LAST_ROW : NOT_EDGE_ROW) ); + } + + /* Recursive line search */ + LineSearchScoreRow( ppi, ChLocalsPtr2, YUVDiffsPtr2, + PixelScoresPtr2, FragScoresPtr2, + DispFragPtr2, + LineSearchRowNumber ); + } + + ChLocalsPtr2 += ppi->PlaneWidth; + YUVDiffsPtr2 += ppi->PlaneWidth; + PixelScoresPtr2 += ppi->PlaneWidth; + LineSearchRowNumber += 1; + RowDiffsPtr2 ++; + } + } + } + } + + /* BAR algorithm */ + if ( (RowNumber3 >= 0) && (RowNumber3 < ppi->PlaneVFragments) ){ + ScoreFragIndex3 = (RowNumber3 * ppi->PlaneHFragments) + FragArrayOffset; + RowBarEnhBlockMap(ppi, + &ppi->ScanDisplayFragments[ScoreFragIndex3], + &ppi->BarBlockMap[(RowNumber3 % 3) * + ppi->PlaneHFragments], + RowNumber3 ); + } + + /* BAR copy back and "ppi->SRF filtering" or "pixel copy back" */ + if ( (RowNumber4 >= 0) && (RowNumber4 < ppi->PlaneVFragments) ){ + /* BAR copy back stage must lag by one more row to avoid BAR blocks + being used in BAR descisions. */ + ScoreFragIndex4 = (RowNumber4 * ppi->PlaneHFragments) + FragArrayOffset; + + BarCopyBack(ppi, &ppi->ScanDisplayFragments[ScoreFragIndex4], + &ppi->BarBlockMap[(RowNumber4 % 3) * ppi->PlaneHFragments]); + + /* Copy over the data from any blocks marked for update into the + output buffer. */ + RowCopy(ppi, ScoreFragIndex4); + } + } +} + +ogg_uint32_t YUVAnalyseFrame( PP_INSTANCE *ppi, ogg_uint32_t * KFIndicator ){ + + /* Initialise the map arrays. */ + InitScanMapArrays(ppi); + + /* If the motion level in the previous frame was high then adjust + the high and low SAD thresholds to speed things up. */ + ppi->ModifiedGrpLowSadThresh = ppi->GrpLowSadThresh; + ppi->ModifiedGrpHighSadThresh = ppi->GrpHighSadThresh; + + + /* Set up the internal plane height and width variables. */ + ppi->VideoYPlaneWidth = ppi->ScanConfig.VideoFrameWidth; + ppi->VideoYPlaneHeight = ppi->ScanConfig.VideoFrameHeight; + ppi->VideoUVPlaneWidth = ppi->ScanConfig.VideoFrameWidth / 2; + ppi->VideoUVPlaneHeight = ppi->ScanConfig.VideoFrameHeight / 2; + + /* To start with the strides will be set from the widths */ + ppi->VideoYPlaneStride = ppi->VideoYPlaneWidth; + ppi->VideoUPlaneStride = ppi->VideoUVPlaneWidth; + ppi->VideoVPlaneStride = ppi->VideoUVPlaneWidth; + + /* Set up the plane pointers */ + ppi->YPlanePtr0 = ppi->ScanConfig.Yuv0ptr; + ppi->YPlanePtr1 = ppi->ScanConfig.Yuv1ptr; + ppi->UPlanePtr0 = (ppi->ScanConfig.Yuv0ptr + ppi->YFramePixels); + ppi->UPlanePtr1 = (ppi->ScanConfig.Yuv1ptr + ppi->YFramePixels); + ppi->VPlanePtr0 = (ppi->ScanConfig.Yuv0ptr + ppi->YFramePixels + + ppi->UVFramePixels); + ppi->VPlanePtr1 = (ppi->ScanConfig.Yuv1ptr + ppi->YFramePixels + + ppi->UVFramePixels); + + /* Check previous frame lists and if necessary mark extra blocks for + update. */ + SetFromPrevious(ppi); + + /* Ananlyse the U and V palnes. */ + AnalysePlane( ppi, ppi->UPlanePtr0, ppi->UPlanePtr1, + ppi->ScanYPlaneFragments, ppi->VideoUVPlaneWidth, + ppi->VideoUVPlaneHeight, ppi->VideoUPlaneStride ); + AnalysePlane( ppi, ppi->VPlanePtr0, ppi->VPlanePtr1, + (ppi->ScanYPlaneFragments + ppi->ScanUVPlaneFragments), + ppi->VideoUVPlaneWidth, ppi->VideoUVPlaneHeight, + ppi->VideoVPlaneStride ); + + /* Now analyse the Y plane. */ + AnalysePlane( ppi, ppi->YPlanePtr0, ppi->YPlanePtr1, 0, + ppi->VideoYPlaneWidth, ppi->VideoYPlaneHeight, + ppi->VideoYPlaneStride ); + + /* Update the list of previous frame block updates. */ + UpdatePreviousBlockLists(ppi); + + /* Create an output block map for the calling process. */ + CreateOutputDisplayMap( ppi, ppi->ScanDisplayFragments, + ppi->PrevFragments[0], + ppi->ScanConfig.disp_fragments ); + + /* Set the candidate key frame indicator (0-100) */ + *KFIndicator = ppi->KFIndicator; + + /* Return the normalised block count (this is actually a motion + level weighting not a true block count). */ + return ppi->OutputBlocksUpdated; +} + diff --git a/Engine/lib/libtheora/lib/enc/toplevel_lookup.h b/Engine/lib/libtheora/lib/enc/toplevel_lookup.h new file mode 100644 index 000000000..bf83a15b6 --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/toplevel_lookup.h @@ -0,0 +1,40 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: toplevel_lookup.h 13884 2007-09-22 08:38:10Z giles $ + + ********************************************************************/ + +#include "codec_internal.h" + +const ogg_uint32_t PriorKeyFrameWeight[KEY_FRAME_CONTEXT] = { 1,2,3,4,5 }; + +/* Data structures controlling addition of residue blocks */ +const ogg_uint32_t ResidueErrorThresh[Q_TABLE_SIZE] = { + 750, 700, 650, 600, 590, 580, 570, 560, + 550, 540, 530, 520, 510, 500, 490, 480, + 470, 460, 450, 440, 430, 420, 410, 400, + 390, 380, 370, 360, 350, 340, 330, 320, + 310, 300, 290, 280, 270, 260, 250, 245, + 240, 235, 230, 225, 220, 215, 210, 205, + 200, 195, 190, 185, 180, 175, 170, 165, + 160, 155, 150, 145, 140, 135, 130, 130 }; +const ogg_uint32_t ResidueBlockFactor[Q_TABLE_SIZE] = { + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2 }; diff --git a/Engine/lib/libtheora/lib/enc/x86_32/dct_decode_mmx.c b/Engine/lib/libtheora/lib/enc/x86_32/dct_decode_mmx.c new file mode 100644 index 000000000..547e974e3 --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/x86_32/dct_decode_mmx.c @@ -0,0 +1,409 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2008 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: dct_decode_mmx.c 15400 2008-10-15 12:10:58Z tterribe $ + + ********************************************************************/ + +#include + +#include "../codec_internal.h" + +#if defined(USE_ASM) + +static const __attribute__((aligned(8),used)) ogg_int64_t OC_V3= + 0x0003000300030003LL; +static const __attribute__((aligned(8),used)) ogg_int64_t OC_V4= + 0x0004000400040004LL; + +static void loop_filter_v(unsigned char *_pix,int _ystride, + const ogg_int16_t *_ll){ + long esi; + _pix-=_ystride*2; + __asm__ __volatile__( + /*mm0=0*/ + "pxor %%mm0,%%mm0\n\t" + /*esi=_ystride*3*/ + "lea (%[ystride],%[ystride],2),%[s]\n\t" + /*mm7=_pix[0...8]*/ + "movq (%[pix]),%%mm7\n\t" + /*mm4=_pix[0...8+_ystride*3]*/ + "movq (%[pix],%[s]),%%mm4\n\t" + /*mm6=_pix[0...8]*/ + "movq %%mm7,%%mm6\n\t" + /*Expand unsigned _pix[0...3] to 16 bits.*/ + "punpcklbw %%mm0,%%mm6\n\t" + "movq %%mm4,%%mm5\n\t" + /*Expand unsigned _pix[4...8] to 16 bits.*/ + "punpckhbw %%mm0,%%mm7\n\t" + /*Expand other arrays too.*/ + "punpcklbw %%mm0,%%mm4\n\t" + "punpckhbw %%mm0,%%mm5\n\t" + /*mm7:mm6=_p[0...8]-_p[0...8+_ystride*3]:*/ + "psubw %%mm4,%%mm6\n\t" + "psubw %%mm5,%%mm7\n\t" + /*mm5=mm4=_pix[0...8+_ystride]*/ + "movq (%[pix],%[ystride]),%%mm4\n\t" + /*mm1=mm3=mm2=_pix[0..8]+_ystride*2]*/ + "movq (%[pix],%[ystride],2),%%mm2\n\t" + "movq %%mm4,%%mm5\n\t" + "movq %%mm2,%%mm3\n\t" + "movq %%mm2,%%mm1\n\t" + /*Expand these arrays.*/ + "punpckhbw %%mm0,%%mm5\n\t" + "punpcklbw %%mm0,%%mm4\n\t" + "punpckhbw %%mm0,%%mm3\n\t" + "punpcklbw %%mm0,%%mm2\n\t" + /*Preload...*/ + "movq %[OC_V3],%%mm0\n\t" + /*mm3:mm2=_pix[0...8+_ystride*2]-_pix[0...8+_ystride]*/ + "psubw %%mm5,%%mm3\n\t" + "psubw %%mm4,%%mm2\n\t" + /*Scale by 3.*/ + "pmullw %%mm0,%%mm3\n\t" + "pmullw %%mm0,%%mm2\n\t" + /*Preload...*/ + "movq %[OC_V4],%%mm0\n\t" + /*f=mm3:mm2==_pix[0...8]-_pix[0...8+_ystride*3]+ + 3*(_pix[0...8+_ystride*2]-_pix[0...8+_ystride])*/ + "paddw %%mm7,%%mm3\n\t" + "paddw %%mm6,%%mm2\n\t" + /*Add 4.*/ + "paddw %%mm0,%%mm3\n\t" + "paddw %%mm0,%%mm2\n\t" + /*"Divide" by 8.*/ + "psraw $3,%%mm3\n\t" + "psraw $3,%%mm2\n\t" + /*Now compute lflim of mm3:mm2 cf. Section 7.10 of the sepc.*/ + /*Free up mm5.*/ + "packuswb %%mm5,%%mm4\n\t" + /*mm0=L L L L*/ + "movq (%[ll]),%%mm0\n\t" + /*if(R_i<-2L||R_i>2L)R_i=0:*/ + "movq %%mm2,%%mm5\n\t" + "pxor %%mm6,%%mm6\n\t" + "movq %%mm0,%%mm7\n\t" + "psubw %%mm0,%%mm6\n\t" + "psllw $1,%%mm7\n\t" + "psllw $1,%%mm6\n\t" + /*mm2==R_3 R_2 R_1 R_0*/ + /*mm5==R_3 R_2 R_1 R_0*/ + /*mm6==-2L -2L -2L -2L*/ + /*mm7==2L 2L 2L 2L*/ + "pcmpgtw %%mm2,%%mm7\n\t" + "pcmpgtw %%mm6,%%mm5\n\t" + "pand %%mm7,%%mm2\n\t" + "movq %%mm0,%%mm7\n\t" + "pand %%mm5,%%mm2\n\t" + "psllw $1,%%mm7\n\t" + "movq %%mm3,%%mm5\n\t" + /*mm3==R_7 R_6 R_5 R_4*/ + /*mm5==R_7 R_6 R_5 R_4*/ + /*mm6==-2L -2L -2L -2L*/ + /*mm7==2L 2L 2L 2L*/ + "pcmpgtw %%mm3,%%mm7\n\t" + "pcmpgtw %%mm6,%%mm5\n\t" + "pand %%mm7,%%mm3\n\t" + "movq %%mm0,%%mm7\n\t" + "pand %%mm5,%%mm3\n\t" + /*if(R_i<-L)R_i'=R_i+2L; + if(R_i>L)R_i'=R_i-2L; + if(R_i<-L||R_i>L)R_i=-R_i':*/ + "psraw $1,%%mm6\n\t" + "movq %%mm2,%%mm5\n\t" + "psllw $1,%%mm7\n\t" + /*mm2==R_3 R_2 R_1 R_0*/ + /*mm5==R_3 R_2 R_1 R_0*/ + /*mm6==-L -L -L -L*/ + /*mm0==L L L L*/ + /*mm5=R_i>L?FF:00*/ + "pcmpgtw %%mm0,%%mm5\n\t" + /*mm6=-L>R_i?FF:00*/ + "pcmpgtw %%mm2,%%mm6\n\t" + /*mm7=R_i>L?2L:0*/ + "pand %%mm5,%%mm7\n\t" + /*mm2=R_i>L?R_i-2L:R_i*/ + "psubw %%mm7,%%mm2\n\t" + "movq %%mm0,%%mm7\n\t" + /*mm5=-L>R_i||R_i>L*/ + "por %%mm6,%%mm5\n\t" + "psllw $1,%%mm7\n\t" + /*mm7=-L>R_i?2L:0*/ + "pand %%mm6,%%mm7\n\t" + "pxor %%mm6,%%mm6\n\t" + /*mm2=-L>R_i?R_i+2L:R_i*/ + "paddw %%mm7,%%mm2\n\t" + "psubw %%mm0,%%mm6\n\t" + /*mm5=-L>R_i||R_i>L?-R_i':0*/ + "pand %%mm2,%%mm5\n\t" + "movq %%mm0,%%mm7\n\t" + /*mm2=-L>R_i||R_i>L?0:R_i*/ + "psubw %%mm5,%%mm2\n\t" + "psllw $1,%%mm7\n\t" + /*mm2=-L>R_i||R_i>L?-R_i':R_i*/ + "psubw %%mm5,%%mm2\n\t" + "movq %%mm3,%%mm5\n\t" + /*mm3==R_7 R_6 R_5 R_4*/ + /*mm5==R_7 R_6 R_5 R_4*/ + /*mm6==-L -L -L -L*/ + /*mm0==L L L L*/ + /*mm6=-L>R_i?FF:00*/ + "pcmpgtw %%mm3,%%mm6\n\t" + /*mm5=R_i>L?FF:00*/ + "pcmpgtw %%mm0,%%mm5\n\t" + /*mm7=R_i>L?2L:0*/ + "pand %%mm5,%%mm7\n\t" + /*mm2=R_i>L?R_i-2L:R_i*/ + "psubw %%mm7,%%mm3\n\t" + "psllw $1,%%mm0\n\t" + /*mm5=-L>R_i||R_i>L*/ + "por %%mm6,%%mm5\n\t" + /*mm0=-L>R_i?2L:0*/ + "pand %%mm6,%%mm0\n\t" + /*mm3=-L>R_i?R_i+2L:R_i*/ + "paddw %%mm0,%%mm3\n\t" + /*mm5=-L>R_i||R_i>L?-R_i':0*/ + "pand %%mm3,%%mm5\n\t" + /*mm2=-L>R_i||R_i>L?0:R_i*/ + "psubw %%mm5,%%mm3\n\t" + /*mm2=-L>R_i||R_i>L?-R_i':R_i*/ + "psubw %%mm5,%%mm3\n\t" + /*Unfortunately, there's no unsigned byte+signed byte with unsigned + saturation op code, so we have to promote things back 16 bits.*/ + "pxor %%mm0,%%mm0\n\t" + "movq %%mm4,%%mm5\n\t" + "punpcklbw %%mm0,%%mm4\n\t" + "punpckhbw %%mm0,%%mm5\n\t" + "movq %%mm1,%%mm6\n\t" + "punpcklbw %%mm0,%%mm1\n\t" + "punpckhbw %%mm0,%%mm6\n\t" + /*_pix[0...8+_ystride]+=R_i*/ + "paddw %%mm2,%%mm4\n\t" + "paddw %%mm3,%%mm5\n\t" + /*_pix[0...8+_ystride*2]-=R_i*/ + "psubw %%mm2,%%mm1\n\t" + "psubw %%mm3,%%mm6\n\t" + "packuswb %%mm5,%%mm4\n\t" + "packuswb %%mm6,%%mm1\n\t" + /*Write it back out.*/ + "movq %%mm4,(%[pix],%[ystride])\n\t" + "movq %%mm1,(%[pix],%[ystride],2)\n\t" + :[s]"=&S"(esi) + :[pix]"r"(_pix),[ystride]"r"((long)_ystride),[ll]"r"(_ll), + [OC_V3]"m"(OC_V3),[OC_V4]"m"(OC_V4) + :"memory" + ); +} + +/*This code implements the bulk of loop_filter_h(). + Data are striped p0 p1 p2 p3 ... p0 p1 p2 p3 ..., so in order to load all + four p0's to one register we must transpose the values in four mmx regs. + When half is done we repeat this for the rest.*/ +static void loop_filter_h4(unsigned char *_pix,long _ystride, + const ogg_int16_t *_ll){ + long esi; + long edi; + __asm__ __volatile__( + /*x x x x 3 2 1 0*/ + "movd (%[pix]),%%mm0\n\t" + /*esi=_ystride*3*/ + "lea (%[ystride],%[ystride],2),%[s]\n\t" + /*x x x x 7 6 5 4*/ + "movd (%[pix],%[ystride]),%%mm1\n\t" + /*x x x x B A 9 8*/ + "movd (%[pix],%[ystride],2),%%mm2\n\t" + /*x x x x F E D C*/ + "movd (%[pix],%[s]),%%mm3\n\t" + /*mm0=7 3 6 2 5 1 4 0*/ + "punpcklbw %%mm1,%%mm0\n\t" + /*mm2=F B E A D 9 C 8*/ + "punpcklbw %%mm3,%%mm2\n\t" + /*mm1=7 3 6 2 5 1 4 0*/ + "movq %%mm0,%%mm1\n\t" + /*mm0=F B 7 3 E A 6 2*/ + "punpckhwd %%mm2,%%mm0\n\t" + /*mm1=D 9 5 1 C 8 4 0*/ + "punpcklwd %%mm2,%%mm1\n\t" + "pxor %%mm7,%%mm7\n\t" + /*mm5=D 9 5 1 C 8 4 0*/ + "movq %%mm1,%%mm5\n\t" + /*mm1=x C x 8 x 4 x 0==pix[0]*/ + "punpcklbw %%mm7,%%mm1\n\t" + /*mm5=x D x 9 x 5 x 1==pix[1]*/ + "punpckhbw %%mm7,%%mm5\n\t" + /*mm3=F B 7 3 E A 6 2*/ + "movq %%mm0,%%mm3\n\t" + /*mm0=x E x A x 6 x 2==pix[2]*/ + "punpcklbw %%mm7,%%mm0\n\t" + /*mm3=x F x B x 7 x 3==pix[3]*/ + "punpckhbw %%mm7,%%mm3\n\t" + /*mm1=mm1-mm3==pix[0]-pix[3]*/ + "psubw %%mm3,%%mm1\n\t" + /*Save a copy of pix[2] for later.*/ + "movq %%mm0,%%mm4\n\t" + /*mm0=mm0-mm5==pix[2]-pix[1]*/ + "psubw %%mm5,%%mm0\n\t" + /*Scale by 3.*/ + "pmullw %[OC_V3],%%mm0\n\t" + /*f=mm1==_pix[0]-_pix[3]+ 3*(_pix[2]-_pix[1])*/ + "paddw %%mm1,%%mm0\n\t" + /*Add 4.*/ + "paddw %[OC_V4],%%mm0\n\t" + /*"Divide" by 8, producing the residuals R_i.*/ + "psraw $3,%%mm0\n\t" + /*Now compute lflim of mm0 cf. Section 7.10 of the sepc.*/ + /*mm6=L L L L*/ + "movq (%[ll]),%%mm6\n\t" + /*if(R_i<-2L||R_i>2L)R_i=0:*/ + "movq %%mm0,%%mm1\n\t" + "pxor %%mm2,%%mm2\n\t" + "movq %%mm6,%%mm3\n\t" + "psubw %%mm6,%%mm2\n\t" + "psllw $1,%%mm3\n\t" + "psllw $1,%%mm2\n\t" + /*mm0==R_3 R_2 R_1 R_0*/ + /*mm1==R_3 R_2 R_1 R_0*/ + /*mm2==-2L -2L -2L -2L*/ + /*mm3==2L 2L 2L 2L*/ + "pcmpgtw %%mm0,%%mm3\n\t" + "pcmpgtw %%mm2,%%mm1\n\t" + "pand %%mm3,%%mm0\n\t" + "pand %%mm1,%%mm0\n\t" + /*if(R_i<-L)R_i'=R_i+2L; + if(R_i>L)R_i'=R_i-2L; + if(R_i<-L||R_i>L)R_i=-R_i':*/ + "psraw $1,%%mm2\n\t" + "movq %%mm0,%%mm1\n\t" + "movq %%mm6,%%mm3\n\t" + /*mm0==R_3 R_2 R_1 R_0*/ + /*mm1==R_3 R_2 R_1 R_0*/ + /*mm2==-L -L -L -L*/ + /*mm6==L L L L*/ + /*mm2=-L>R_i?FF:00*/ + "pcmpgtw %%mm0,%%mm2\n\t" + /*mm1=R_i>L?FF:00*/ + "pcmpgtw %%mm6,%%mm1\n\t" + /*mm3=2L 2L 2L 2L*/ + "psllw $1,%%mm3\n\t" + /*mm6=2L 2L 2L 2L*/ + "psllw $1,%%mm6\n\t" + /*mm3=R_i>L?2L:0*/ + "pand %%mm1,%%mm3\n\t" + /*mm6=-L>R_i?2L:0*/ + "pand %%mm2,%%mm6\n\t" + /*mm0=R_i>L?R_i-2L:R_i*/ + "psubw %%mm3,%%mm0\n\t" + /*mm1=-L>R_i||R_i>L*/ + "por %%mm2,%%mm1\n\t" + /*mm0=-L>R_i?R_i+2L:R_i*/ + "paddw %%mm6,%%mm0\n\t" + /*mm1=-L>R_i||R_i>L?R_i':0*/ + "pand %%mm0,%%mm1\n\t" + /*mm0=-L>R_i||R_i>L?0:R_i*/ + "psubw %%mm1,%%mm0\n\t" + /*mm0=-L>R_i||R_i>L?-R_i':R_i*/ + "psubw %%mm1,%%mm0\n\t" + /*_pix[1]+=R_i;*/ + "paddw %%mm0,%%mm5\n\t" + /*_pix[2]-=R_i;*/ + "psubw %%mm0,%%mm4\n\t" + /*mm5=x x x x D 9 5 1*/ + "packuswb %%mm7,%%mm5\n\t" + /*mm4=x x x x E A 6 2*/ + "packuswb %%mm7,%%mm4\n\t" + /*mm5=E D A 9 6 5 2 1*/ + "punpcklbw %%mm4,%%mm5\n\t" + /*edi=6 5 2 1*/ + "movd %%mm5,%%edi\n\t" + "movw %%di,1(%[pix])\n\t" + /*Why is there such a big stall here?*/ + "psrlq $32,%%mm5\n\t" + "shrl $16,%%edi\n\t" + "movw %%di,1(%[pix],%[ystride])\n\t" + /*edi=E D A 9*/ + "movd %%mm5,%%edi\n\t" + "movw %%di,1(%[pix],%[ystride],2)\n\t" + "shrl $16,%%edi\n\t" + "movw %%di,1(%[pix],%[s])\n\t" + :[s]"=&S"(esi),[d]"=&D"(edi), + [pix]"+r"(_pix),[ystride]"+r"(_ystride),[ll]"+r"(_ll) + :[OC_V3]"m"(OC_V3),[OC_V4]"m"(OC_V4) + :"memory" + ); +} + +static void loop_filter_h(unsigned char *_pix,int _ystride, + const ogg_int16_t *_ll){ + _pix-=2; + loop_filter_h4(_pix,_ystride,_ll); + loop_filter_h4(_pix+(_ystride<<2),_ystride,_ll); +} + +static void loop_filter_mmx(PB_INSTANCE *pbi, int FLimit){ + int j; + ogg_int16_t __attribute__((aligned(8))) ll[4]; + unsigned char *cp = pbi->display_fragments; + ogg_uint32_t *bp = pbi->recon_pixel_index_table; + + if ( FLimit == 0 ) return; + ll[0]=ll[1]=ll[2]=ll[3]=FLimit; + + for ( j = 0; j < 3 ; j++){ + ogg_uint32_t *bp_begin = bp; + ogg_uint32_t *bp_end; + int stride; + int h; + + switch(j) { + case 0: /* y */ + bp_end = bp + pbi->YPlaneFragments; + h = pbi->HFragments; + stride = pbi->YStride; + break; + default: /* u,v, 4:20 specific */ + bp_end = bp + pbi->UVPlaneFragments; + h = pbi->HFragments >> 1; + stride = pbi->UVStride; + break; + } + + while(bpbp_left) + loop_filter_h(&pbi->LastFrameRecon[bp[0]],stride,ll); + if(bp_left>bp_begin) + loop_filter_v(&pbi->LastFrameRecon[bp[0]],stride,ll); + if(bp+1LastFrameRecon[bp[0]]+8,stride,ll); + if(bp+hLastFrameRecon[bp[h]],stride,ll); + } + bp++; + cp++; + } + } + } + + __asm__ __volatile__("emms\n\t"); +} + +/* install our implementation in the function table */ +void dsp_mmx_dct_decode_init(DspFunctions *funcs) +{ + funcs->LoopFilter = loop_filter_mmx; +} + +#endif /* USE_ASM */ diff --git a/Engine/lib/libtheora/lib/enc/x86_32/dsp_mmx.c b/Engine/lib/libtheora/lib/enc/x86_32/dsp_mmx.c new file mode 100644 index 000000000..3c8a46e6a --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/x86_32/dsp_mmx.c @@ -0,0 +1,666 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: dsp_mmx.c 15153 2008-08-04 18:37:55Z tterribe $ + + ********************************************************************/ + +#include + +#include "../codec_internal.h" +#include "../dsp.h" + +#if defined(USE_ASM) + +static const __attribute__ ((aligned(8),used)) ogg_int64_t V128 = 0x0080008000800080LL; + +#define DSP_OP_AVG(a,b) ((((int)(a)) + ((int)(b)))/2) +#define DSP_OP_DIFF(a,b) (((int)(a)) - ((int)(b))) +#define DSP_OP_ABS_DIFF(a,b) abs((((int)(a)) - ((int)(b)))) + +#define SUB_LOOP \ + " movq (%0), %%mm0 \n\t" /* mm0 = FiltPtr */ \ + " movq (%1), %%mm1 \n\t" /* mm1 = ReconPtr */ \ + " movq %%mm0, %%mm2 \n\t" /* dup to prepare for up conversion */\ + " movq %%mm1, %%mm3 \n\t" /* dup to prepare for up conversion */\ + /* convert from UINT8 to INT16 */ \ + " punpcklbw %%mm7, %%mm0 \n\t" /* mm0 = INT16(FiltPtr) */ \ + " punpcklbw %%mm7, %%mm1 \n\t" /* mm1 = INT16(ReconPtr) */ \ + " punpckhbw %%mm7, %%mm2 \n\t" /* mm2 = INT16(FiltPtr) */ \ + " punpckhbw %%mm7, %%mm3 \n\t" /* mm3 = INT16(ReconPtr) */ \ + /* start calculation */ \ + " psubw %%mm1, %%mm0 \n\t" /* mm0 = FiltPtr - ReconPtr */ \ + " psubw %%mm3, %%mm2 \n\t" /* mm2 = FiltPtr - ReconPtr */ \ + " movq %%mm0, (%2) \n\t" /* write answer out */ \ + " movq %%mm2, 8(%2) \n\t" /* write answer out */ \ + /* Increment pointers */ \ + " add $16, %2 \n\t" \ + " add %3, %0 \n\t" \ + " add %4, %1 \n\t" + +static void sub8x8__mmx (unsigned char *FiltPtr, unsigned char *ReconPtr, + ogg_int16_t *DctInputPtr, ogg_uint32_t PixelsPerLine, + ogg_uint32_t ReconPixelsPerLine) +{ + __asm__ __volatile__ ( + " .p2align 4 \n\t" + + " pxor %%mm7, %%mm7 \n\t" + SUB_LOOP + SUB_LOOP + SUB_LOOP + SUB_LOOP + SUB_LOOP + SUB_LOOP + SUB_LOOP + SUB_LOOP + : "+r" (FiltPtr), + "+r" (ReconPtr), + "+r" (DctInputPtr) + : "m" (PixelsPerLine), + "m" (ReconPixelsPerLine) + : "memory" + ); +} + +#define SUB_128_LOOP \ + " movq (%0), %%mm0 \n\t" /* mm0 = FiltPtr */ \ + " movq %%mm0, %%mm2 \n\t" /* dup to prepare for up conversion */\ + /* convert from UINT8 to INT16 */ \ + " punpcklbw %%mm7, %%mm0 \n\t" /* mm0 = INT16(FiltPtr) */ \ + " punpckhbw %%mm7, %%mm2 \n\t" /* mm2 = INT16(FiltPtr) */ \ + /* start calculation */ \ + " psubw %%mm1, %%mm0 \n\t" /* mm0 = FiltPtr - 128 */ \ + " psubw %%mm1, %%mm2 \n\t" /* mm2 = FiltPtr - 128 */ \ + " movq %%mm0, (%1) \n\t" /* write answer out */ \ + " movq %%mm2, 8(%1) \n\t" /* write answer out */ \ + /* Increment pointers */ \ + " add $16, %1 \n\t" \ + " add %2, %0 \n\t" + + +static void sub8x8_128__mmx (unsigned char *FiltPtr, ogg_int16_t *DctInputPtr, + ogg_uint32_t PixelsPerLine) +{ + __asm__ __volatile__ ( + " .p2align 4 \n\t" + + " pxor %%mm7, %%mm7 \n\t" + " movq %[V128], %%mm1 \n\t" + SUB_128_LOOP + SUB_128_LOOP + SUB_128_LOOP + SUB_128_LOOP + SUB_128_LOOP + SUB_128_LOOP + SUB_128_LOOP + SUB_128_LOOP + : "+r" (FiltPtr), + "+r" (DctInputPtr) + : "m" (PixelsPerLine), + [V128] "m" (V128) + : "memory" + ); +} + +#define SUB_AVG2_LOOP \ + " movq (%0), %%mm0 \n\t" /* mm0 = FiltPtr */ \ + " movq (%1), %%mm1 \n\t" /* mm1 = ReconPtr1 */ \ + " movq (%2), %%mm4 \n\t" /* mm1 = ReconPtr2 */ \ + " movq %%mm0, %%mm2 \n\t" /* dup to prepare for up conversion */\ + " movq %%mm1, %%mm3 \n\t" /* dup to prepare for up conversion */\ + " movq %%mm4, %%mm5 \n\t" /* dup to prepare for up conversion */\ + /* convert from UINT8 to INT16 */ \ + " punpcklbw %%mm7, %%mm0 \n\t" /* mm0 = INT16(FiltPtr) */ \ + " punpcklbw %%mm7, %%mm1 \n\t" /* mm1 = INT16(ReconPtr1) */ \ + " punpcklbw %%mm7, %%mm4 \n\t" /* mm1 = INT16(ReconPtr2) */ \ + " punpckhbw %%mm7, %%mm2 \n\t" /* mm2 = INT16(FiltPtr) */ \ + " punpckhbw %%mm7, %%mm3 \n\t" /* mm3 = INT16(ReconPtr1) */ \ + " punpckhbw %%mm7, %%mm5 \n\t" /* mm3 = INT16(ReconPtr2) */ \ + /* average ReconPtr1 and ReconPtr2 */ \ + " paddw %%mm4, %%mm1 \n\t" /* mm1 = ReconPtr1 + ReconPtr2 */ \ + " paddw %%mm5, %%mm3 \n\t" /* mm3 = ReconPtr1 + ReconPtr2 */ \ + " psrlw $1, %%mm1 \n\t" /* mm1 = (ReconPtr1 + ReconPtr2) / 2 */ \ + " psrlw $1, %%mm3 \n\t" /* mm3 = (ReconPtr1 + ReconPtr2) / 2 */ \ + " psubw %%mm1, %%mm0 \n\t" /* mm0 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ \ + " psubw %%mm3, %%mm2 \n\t" /* mm2 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ \ + " movq %%mm0, (%3) \n\t" /* write answer out */ \ + " movq %%mm2, 8(%3) \n\t" /* write answer out */ \ + /* Increment pointers */ \ + " add $16, %3 \n\t" \ + " add %4, %0 \n\t" \ + " add %5, %1 \n\t" \ + " add %5, %2 \n\t" + + +static void sub8x8avg2__mmx (unsigned char *FiltPtr, unsigned char *ReconPtr1, + unsigned char *ReconPtr2, ogg_int16_t *DctInputPtr, + ogg_uint32_t PixelsPerLine, + ogg_uint32_t ReconPixelsPerLine) +{ + __asm__ __volatile__ ( + " .p2align 4 \n\t" + + " pxor %%mm7, %%mm7 \n\t" + SUB_AVG2_LOOP + SUB_AVG2_LOOP + SUB_AVG2_LOOP + SUB_AVG2_LOOP + SUB_AVG2_LOOP + SUB_AVG2_LOOP + SUB_AVG2_LOOP + SUB_AVG2_LOOP + : "+r" (FiltPtr), + "+r" (ReconPtr1), + "+r" (ReconPtr2), + "+r" (DctInputPtr) + : "m" (PixelsPerLine), + "m" (ReconPixelsPerLine) + : "memory" + ); +} + +static ogg_uint32_t row_sad8__mmx (unsigned char *Src1, unsigned char *Src2) +{ + ogg_uint32_t MaxSad; + + __asm__ __volatile__ ( + " .p2align 4 \n\t" + + " pxor %%mm6, %%mm6 \n\t" /* zero out mm6 for unpack */ + " pxor %%mm7, %%mm7 \n\t" /* zero out mm7 for unpack */ + " movq (%1), %%mm0 \n\t" /* take 8 bytes */ + " movq (%2), %%mm1 \n\t" + + " movq %%mm0, %%mm2 \n\t" + " psubusb %%mm1, %%mm0 \n\t" /* A - B */ + " psubusb %%mm2, %%mm1 \n\t" /* B - A */ + " por %%mm1, %%mm0 \n\t" /* and or gives abs difference */ + + " movq %%mm0, %%mm1 \n\t" + + " punpcklbw %%mm6, %%mm0 \n\t" /* ; unpack low four bytes to higher precision */ + " punpckhbw %%mm7, %%mm1 \n\t" /* ; unpack high four bytes to higher precision */ + + " movq %%mm0, %%mm2 \n\t" + " movq %%mm1, %%mm3 \n\t" + " psrlq $32, %%mm2 \n\t" /* fold and add */ + " psrlq $32, %%mm3 \n\t" + " paddw %%mm2, %%mm0 \n\t" + " paddw %%mm3, %%mm1 \n\t" + " movq %%mm0, %%mm2 \n\t" + " movq %%mm1, %%mm3 \n\t" + " psrlq $16, %%mm2 \n\t" + " psrlq $16, %%mm3 \n\t" + " paddw %%mm2, %%mm0 \n\t" + " paddw %%mm3, %%mm1 \n\t" + + " psubusw %%mm0, %%mm1 \n\t" + " paddw %%mm0, %%mm1 \n\t" /* mm1 = max(mm1, mm0) */ + " movd %%mm1, %0 \n\t" + " andl $0xffff, %0 \n\t" + + : "=m" (MaxSad), + "+r" (Src1), + "+r" (Src2) + : + : "memory" + ); + return MaxSad; +} + +static ogg_uint32_t col_sad8x8__mmx (unsigned char *Src1, unsigned char *Src2, + ogg_uint32_t stride) +{ + ogg_uint32_t MaxSad; + + __asm__ __volatile__ ( + " .p2align 4 \n\t" + + " pxor %%mm3, %%mm3 \n\t" /* zero out mm3 for unpack */ + " pxor %%mm4, %%mm4 \n\t" /* mm4 low sum */ + " pxor %%mm5, %%mm5 \n\t" /* mm5 high sum */ + " pxor %%mm6, %%mm6 \n\t" /* mm6 low sum */ + " pxor %%mm7, %%mm7 \n\t" /* mm7 high sum */ + " mov $4, %%edi \n\t" /* 4 rows */ + "1: \n\t" + " movq (%1), %%mm0 \n\t" /* take 8 bytes */ + " movq (%2), %%mm1 \n\t" /* take 8 bytes */ + + " movq %%mm0, %%mm2 \n\t" + " psubusb %%mm1, %%mm0 \n\t" /* A - B */ + " psubusb %%mm2, %%mm1 \n\t" /* B - A */ + " por %%mm1, %%mm0 \n\t" /* and or gives abs difference */ + " movq %%mm0, %%mm1 \n\t" + + " punpcklbw %%mm3, %%mm0 \n\t" /* unpack to higher precision for accumulation */ + " paddw %%mm0, %%mm4 \n\t" /* accumulate difference... */ + " punpckhbw %%mm3, %%mm1 \n\t" /* unpack high four bytes to higher precision */ + " paddw %%mm1, %%mm5 \n\t" /* accumulate difference... */ + " add %3, %1 \n\t" /* Inc pointer into the new data */ + " add %3, %2 \n\t" /* Inc pointer into the new data */ + + " dec %%edi \n\t" + " jnz 1b \n\t" + + " mov $4, %%edi \n\t" /* 4 rows */ + "2: \n\t" + " movq (%1), %%mm0 \n\t" /* take 8 bytes */ + " movq (%2), %%mm1 \n\t" /* take 8 bytes */ + + " movq %%mm0, %%mm2 \n\t" + " psubusb %%mm1, %%mm0 \n\t" /* A - B */ + " psubusb %%mm2, %%mm1 \n\t" /* B - A */ + " por %%mm1, %%mm0 \n\t" /* and or gives abs difference */ + " movq %%mm0, %%mm1 \n\t" + + " punpcklbw %%mm3, %%mm0 \n\t" /* unpack to higher precision for accumulation */ + " paddw %%mm0, %%mm6 \n\t" /* accumulate difference... */ + " punpckhbw %%mm3, %%mm1 \n\t" /* unpack high four bytes to higher precision */ + " paddw %%mm1, %%mm7 \n\t" /* accumulate difference... */ + " add %3, %1 \n\t" /* Inc pointer into the new data */ + " add %3, %2 \n\t" /* Inc pointer into the new data */ + + " dec %%edi \n\t" + " jnz 2b \n\t" + + " psubusw %%mm6, %%mm7 \n\t" + " paddw %%mm6, %%mm7 \n\t" /* mm7 = max(mm7, mm6) */ + " psubusw %%mm4, %%mm5 \n\t" + " paddw %%mm4, %%mm5 \n\t" /* mm5 = max(mm5, mm4) */ + " psubusw %%mm5, %%mm7 \n\t" + " paddw %%mm5, %%mm7 \n\t" /* mm7 = max(mm5, mm7) */ + " movq %%mm7, %%mm6 \n\t" + " psrlq $32, %%mm6 \n\t" + " psubusw %%mm6, %%mm7 \n\t" + " paddw %%mm6, %%mm7 \n\t" /* mm7 = max(mm5, mm7) */ + " movq %%mm7, %%mm6 \n\t" + " psrlq $16, %%mm6 \n\t" + " psubusw %%mm6, %%mm7 \n\t" + " paddw %%mm6, %%mm7 \n\t" /* mm7 = max(mm5, mm7) */ + " movd %%mm7, %0 \n\t" + " andl $0xffff, %0 \n\t" + + : "=r" (MaxSad), + "+r" (Src1), + "+r" (Src2) + : "r" (stride) + : "memory", "edi" + ); + + return MaxSad; +} + +#define SAD_LOOP \ + " movq (%1), %%mm0 \n\t" /* take 8 bytes */ \ + " movq (%2), %%mm1 \n\t" \ + " movq %%mm0, %%mm2 \n\t" \ + " psubusb %%mm1, %%mm0 \n\t" /* A - B */ \ + " psubusb %%mm2, %%mm1 \n\t" /* B - A */ \ + " por %%mm1, %%mm0 \n\t" /* and or gives abs difference */ \ + " movq %%mm0, %%mm1 \n\t" \ + " punpcklbw %%mm6, %%mm0 \n\t" /* unpack to higher precision for accumulation */ \ + " paddw %%mm0, %%mm7 \n\t" /* accumulate difference... */ \ + " punpckhbw %%mm6, %%mm1 \n\t" /* unpack high four bytes to higher precision */ \ + " add %3, %1 \n\t" /* Inc pointer into the new data */ \ + " paddw %%mm1, %%mm7 \n\t" /* accumulate difference... */ \ + " add %4, %2 \n\t" /* Inc pointer into ref data */ + +static ogg_uint32_t sad8x8__mmx (unsigned char *ptr1, ogg_uint32_t stride1, + unsigned char *ptr2, ogg_uint32_t stride2) +{ + ogg_uint32_t DiffVal; + + __asm__ __volatile__ ( + " .p2align 4 \n\t" + " pxor %%mm6, %%mm6 \n\t" /* zero out mm6 for unpack */ + " pxor %%mm7, %%mm7 \n\t" /* mm7 contains the result */ + SAD_LOOP + SAD_LOOP + SAD_LOOP + SAD_LOOP + SAD_LOOP + SAD_LOOP + SAD_LOOP + SAD_LOOP + " movq %%mm7, %%mm0 \n\t" + " psrlq $32, %%mm7 \n\t" + " paddw %%mm0, %%mm7 \n\t" + " movq %%mm7, %%mm0 \n\t" + " psrlq $16, %%mm7 \n\t" + " paddw %%mm0, %%mm7 \n\t" + " movd %%mm7, %0 \n\t" + " andl $0xffff, %0 \n\t" + + : "=m" (DiffVal), + "+r" (ptr1), + "+r" (ptr2) + : "r" (stride1), + "r" (stride2) + : "memory" + ); + + return DiffVal; +} + +static ogg_uint32_t sad8x8_thres__mmx (unsigned char *ptr1, ogg_uint32_t stride1, + unsigned char *ptr2, ogg_uint32_t stride2, + ogg_uint32_t thres) +{ + return sad8x8__mmx (ptr1, stride1, ptr2, stride2); +} + +static ogg_uint32_t sad8x8_xy2_thres__mmx (unsigned char *SrcData, ogg_uint32_t SrcStride, + unsigned char *RefDataPtr1, + unsigned char *RefDataPtr2, ogg_uint32_t RefStride, + ogg_uint32_t thres) +{ + ogg_uint32_t DiffVal; + + __asm__ __volatile__ ( + " .p2align 4 \n\t" + + " pcmpeqd %%mm5, %%mm5 \n\t" /* fefefefefefefefe in mm5 */ + " paddb %%mm5, %%mm5 \n\t" + + " pxor %%mm6, %%mm6 \n\t" /* zero out mm6 for unpack */ + " pxor %%mm7, %%mm7 \n\t" /* mm7 contains the result */ + " mov $8, %%edi \n\t" /* 8 rows */ + "1: \n\t" + " movq (%1), %%mm0 \n\t" /* take 8 bytes */ + + " movq (%2), %%mm2 \n\t" + " movq (%3), %%mm3 \n\t" /* take average of mm2 and mm3 */ + " movq %%mm2, %%mm1 \n\t" + " pand %%mm3, %%mm1 \n\t" + " pxor %%mm2, %%mm3 \n\t" + " pand %%mm5, %%mm3 \n\t" + " psrlq $1, %%mm3 \n\t" + " paddb %%mm3, %%mm1 \n\t" + + " movq %%mm0, %%mm2 \n\t" + + " psubusb %%mm1, %%mm0 \n\t" /* A - B */ + " psubusb %%mm2, %%mm1 \n\t" /* B - A */ + " por %%mm1, %%mm0 \n\t" /* and or gives abs difference */ + " movq %%mm0, %%mm1 \n\t" + + " punpcklbw %%mm6, %%mm0 \n\t" /* unpack to higher precision for accumulation */ + " paddw %%mm0, %%mm7 \n\t" /* accumulate difference... */ + " punpckhbw %%mm6, %%mm1 \n\t" /* unpack high four bytes to higher precision */ + " add %4, %1 \n\t" /* Inc pointer into the new data */ + " paddw %%mm1, %%mm7 \n\t" /* accumulate difference... */ + " add %5, %2 \n\t" /* Inc pointer into ref data */ + " add %5, %3 \n\t" /* Inc pointer into ref data */ + + " dec %%edi \n\t" + " jnz 1b \n\t" + + " movq %%mm7, %%mm0 \n\t" + " psrlq $32, %%mm7 \n\t" + " paddw %%mm0, %%mm7 \n\t" + " movq %%mm7, %%mm0 \n\t" + " psrlq $16, %%mm7 \n\t" + " paddw %%mm0, %%mm7 \n\t" + " movd %%mm7, %0 \n\t" + " andl $0xffff, %0 \n\t" + + : "=m" (DiffVal), + "+r" (SrcData), + "+r" (RefDataPtr1), + "+r" (RefDataPtr2) + : "m" (SrcStride), + "m" (RefStride) + : "edi", "memory" + ); + + return DiffVal; +} + +static ogg_uint32_t intra8x8_err__mmx (unsigned char *DataPtr, ogg_uint32_t Stride) +{ + ogg_uint32_t XSum; + ogg_uint32_t XXSum; + + __asm__ __volatile__ ( + " .p2align 4 \n\t" + + " pxor %%mm5, %%mm5 \n\t" + " pxor %%mm6, %%mm6 \n\t" + " pxor %%mm7, %%mm7 \n\t" + " mov $8, %%edi \n\t" + "1: \n\t" + " movq (%2), %%mm0 \n\t" /* take 8 bytes */ + " movq %%mm0, %%mm2 \n\t" + + " punpcklbw %%mm6, %%mm0 \n\t" + " punpckhbw %%mm6, %%mm2 \n\t" + + " paddw %%mm0, %%mm5 \n\t" + " paddw %%mm2, %%mm5 \n\t" + + " pmaddwd %%mm0, %%mm0 \n\t" + " pmaddwd %%mm2, %%mm2 \n\t" + + " paddd %%mm0, %%mm7 \n\t" + " paddd %%mm2, %%mm7 \n\t" + + " add %3, %2 \n\t" /* Inc pointer into src data */ + + " dec %%edi \n\t" + " jnz 1b \n\t" + + " movq %%mm5, %%mm0 \n\t" + " psrlq $32, %%mm5 \n\t" + " paddw %%mm0, %%mm5 \n\t" + " movq %%mm5, %%mm0 \n\t" + " psrlq $16, %%mm5 \n\t" + " paddw %%mm0, %%mm5 \n\t" + " movd %%mm5, %%edi \n\t" + " movsx %%di, %%edi \n\t" + " movl %%edi, %0 \n\t" + + " movq %%mm7, %%mm0 \n\t" + " psrlq $32, %%mm7 \n\t" + " paddd %%mm0, %%mm7 \n\t" + " movd %%mm7, %1 \n\t" + + : "=r" (XSum), + "=r" (XXSum), + "+r" (DataPtr) + : "r" (Stride) + : "edi", "memory" + ); + + /* Compute population variance as mis-match metric. */ + return (( (XXSum<<6) - XSum*XSum ) ); +} + +static ogg_uint32_t inter8x8_err__mmx (unsigned char *SrcData, ogg_uint32_t SrcStride, + unsigned char *RefDataPtr, ogg_uint32_t RefStride) +{ + ogg_uint32_t XSum; + ogg_uint32_t XXSum; + + __asm__ __volatile__ ( + " .p2align 4 \n\t" + + " pxor %%mm5, %%mm5 \n\t" + " pxor %%mm6, %%mm6 \n\t" + " pxor %%mm7, %%mm7 \n\t" + " mov $8, %%edi \n\t" + "1: \n\t" + " movq (%2), %%mm0 \n\t" /* take 8 bytes */ + " movq (%3), %%mm1 \n\t" + " movq %%mm0, %%mm2 \n\t" + " movq %%mm1, %%mm3 \n\t" + + " punpcklbw %%mm6, %%mm0 \n\t" + " punpcklbw %%mm6, %%mm1 \n\t" + " punpckhbw %%mm6, %%mm2 \n\t" + " punpckhbw %%mm6, %%mm3 \n\t" + + " psubsw %%mm1, %%mm0 \n\t" + " psubsw %%mm3, %%mm2 \n\t" + + " paddw %%mm0, %%mm5 \n\t" + " paddw %%mm2, %%mm5 \n\t" + + " pmaddwd %%mm0, %%mm0 \n\t" + " pmaddwd %%mm2, %%mm2 \n\t" + + " paddd %%mm0, %%mm7 \n\t" + " paddd %%mm2, %%mm7 \n\t" + + " add %4, %2 \n\t" /* Inc pointer into src data */ + " add %5, %3 \n\t" /* Inc pointer into ref data */ + + " dec %%edi \n\t" + " jnz 1b \n\t" + + " movq %%mm5, %%mm0 \n\t" + " psrlq $32, %%mm5 \n\t" + " paddw %%mm0, %%mm5 \n\t" + " movq %%mm5, %%mm0 \n\t" + " psrlq $16, %%mm5 \n\t" + " paddw %%mm0, %%mm5 \n\t" + " movd %%mm5, %%edi \n\t" + " movsx %%di, %%edi \n\t" + " movl %%edi, %0 \n\t" + + " movq %%mm7, %%mm0 \n\t" + " psrlq $32, %%mm7 \n\t" + " paddd %%mm0, %%mm7 \n\t" + " movd %%mm7, %1 \n\t" + + : "=m" (XSum), + "=m" (XXSum), + "+r" (SrcData), + "+r" (RefDataPtr) + : "m" (SrcStride), + "m" (RefStride) + : "edi", "memory" + ); + + /* Compute and return population variance as mis-match metric. */ + return (( (XXSum<<6) - XSum*XSum )); +} + +static ogg_uint32_t inter8x8_err_xy2__mmx (unsigned char *SrcData, ogg_uint32_t SrcStride, + unsigned char *RefDataPtr1, + unsigned char *RefDataPtr2, ogg_uint32_t RefStride) +{ + ogg_uint32_t XSum; + ogg_uint32_t XXSum; + + __asm__ __volatile__ ( + " .p2align 4 \n\t" + + " pcmpeqd %%mm4, %%mm4 \n\t" /* fefefefefefefefe in mm4 */ + " paddb %%mm4, %%mm4 \n\t" + " pxor %%mm5, %%mm5 \n\t" + " pxor %%mm6, %%mm6 \n\t" + " pxor %%mm7, %%mm7 \n\t" + " mov $8, %%edi \n\t" + "1: \n\t" + " movq (%2), %%mm0 \n\t" /* take 8 bytes */ + + " movq (%3), %%mm2 \n\t" + " movq (%4), %%mm3 \n\t" /* take average of mm2 and mm3 */ + " movq %%mm2, %%mm1 \n\t" + " pand %%mm3, %%mm1 \n\t" + " pxor %%mm2, %%mm3 \n\t" + " pand %%mm4, %%mm3 \n\t" + " psrlq $1, %%mm3 \n\t" + " paddb %%mm3, %%mm1 \n\t" + + " movq %%mm0, %%mm2 \n\t" + " movq %%mm1, %%mm3 \n\t" + + " punpcklbw %%mm6, %%mm0 \n\t" + " punpcklbw %%mm6, %%mm1 \n\t" + " punpckhbw %%mm6, %%mm2 \n\t" + " punpckhbw %%mm6, %%mm3 \n\t" + + " psubsw %%mm1, %%mm0 \n\t" + " psubsw %%mm3, %%mm2 \n\t" + + " paddw %%mm0, %%mm5 \n\t" + " paddw %%mm2, %%mm5 \n\t" + + " pmaddwd %%mm0, %%mm0 \n\t" + " pmaddwd %%mm2, %%mm2 \n\t" + + " paddd %%mm0, %%mm7 \n\t" + " paddd %%mm2, %%mm7 \n\t" + + " add %5, %2 \n\t" /* Inc pointer into src data */ + " add %6, %3 \n\t" /* Inc pointer into ref data */ + " add %6, %4 \n\t" /* Inc pointer into ref data */ + + " dec %%edi \n\t" + " jnz 1b \n\t" + + " movq %%mm5, %%mm0 \n\t" + " psrlq $32, %%mm5 \n\t" + " paddw %%mm0, %%mm5 \n\t" + " movq %%mm5, %%mm0 \n\t" + " psrlq $16, %%mm5 \n\t" + " paddw %%mm0, %%mm5 \n\t" + " movd %%mm5, %%edi \n\t" + " movsx %%di, %%edi \n\t" + " movl %%edi, %0 \n\t" + + " movq %%mm7, %%mm0 \n\t" + " psrlq $32, %%mm7 \n\t" + " paddd %%mm0, %%mm7 \n\t" + " movd %%mm7, %1 \n\t" + + : "=m" (XSum), + "=m" (XXSum), + "+r" (SrcData), + "+r" (RefDataPtr1), + "+r" (RefDataPtr2) + : "m" (SrcStride), + "m" (RefStride) + : "edi", "memory" + ); + + /* Compute and return population variance as mis-match metric. */ + return (( (XXSum<<6) - XSum*XSum )); +} + +static void restore_fpu (void) +{ + __asm__ __volatile__ ( + " emms \n\t" + ); +} + +void dsp_mmx_init(DspFunctions *funcs) +{ + funcs->restore_fpu = restore_fpu; + funcs->sub8x8 = sub8x8__mmx; + funcs->sub8x8_128 = sub8x8_128__mmx; + funcs->sub8x8avg2 = sub8x8avg2__mmx; + funcs->row_sad8 = row_sad8__mmx; + funcs->col_sad8x8 = col_sad8x8__mmx; + funcs->sad8x8 = sad8x8__mmx; + funcs->sad8x8_thres = sad8x8_thres__mmx; + funcs->sad8x8_xy2_thres = sad8x8_xy2_thres__mmx; + funcs->intra8x8_err = intra8x8_err__mmx; + funcs->inter8x8_err = inter8x8_err__mmx; + funcs->inter8x8_err_xy2 = inter8x8_err_xy2__mmx; +} + +#endif /* USE_ASM */ diff --git a/Engine/lib/libtheora/lib/enc/x86_32/dsp_mmxext.c b/Engine/lib/libtheora/lib/enc/x86_32/dsp_mmxext.c new file mode 100644 index 000000000..297c3213a --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/x86_32/dsp_mmxext.c @@ -0,0 +1,347 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: dsp_mmxext.c 15153 2008-08-04 18:37:55Z tterribe $ + + ********************************************************************/ + +#include + +#include "../codec_internal.h" +#include "../dsp.h" + +#if defined(USE_ASM) + +#define SAD_MMXEXT_LOOP \ + " movq (%1), %%mm0 \n\t" /* take 8 bytes */ \ + " movq (%2), %%mm1 \n\t" \ + " psadbw %%mm1, %%mm0 \n\t" \ + " add %3, %1 \n\t" /* Inc pointer into the new data */ \ + " paddw %%mm0, %%mm7 \n\t" /* accumulate difference... */ \ + " add %4, %2 \n\t" /* Inc pointer into ref data */ + + +static ogg_uint32_t sad8x8__mmxext (unsigned char *ptr1, ogg_uint32_t stride1, + unsigned char *ptr2, ogg_uint32_t stride2) +{ + ogg_uint32_t DiffVal; + + __asm__ __volatile__ ( + " .p2align 4 \n\t" + " pxor %%mm7, %%mm7 \n\t" /* mm7 contains the result */ + + SAD_MMXEXT_LOOP + SAD_MMXEXT_LOOP + SAD_MMXEXT_LOOP + SAD_MMXEXT_LOOP + SAD_MMXEXT_LOOP + SAD_MMXEXT_LOOP + SAD_MMXEXT_LOOP + + " movq (%1), %%mm0 \n\t" /* take 8 bytes */ + " movq (%2), %%mm1 \n\t" + " psadbw %%mm1, %%mm0 \n\t" + " paddw %%mm0, %%mm7 \n\t" /* accumulate difference... */ + " movd %%mm7, %0 \n\t" + + : "=r" (DiffVal), + "+r" (ptr1), + "+r" (ptr2) + : "r" (stride1), + "r" (stride2) + : "memory" + ); + + return DiffVal; +} + +#define SAD_TRES_LOOP \ + " movq (%1), %%mm0 \n\t" /* take 8 bytes */ \ + " movq (%2), %%mm1 \n\t" \ + " psadbw %%mm1, %%mm0 \n\t" \ + " add %3, %1 \n\t" /* Inc pointer into the new data */ \ + " paddw %%mm0, %%mm7 \n\t" /* accumulate difference... */ \ + " add %4, %2 \n\t" /* Inc pointer into ref data */ + + +static ogg_uint32_t sad8x8_thres__mmxext (unsigned char *ptr1, ogg_uint32_t stride1, + unsigned char *ptr2, ogg_uint32_t stride2, + ogg_uint32_t thres) +{ + ogg_uint32_t DiffVal; + + __asm__ __volatile__ ( + " .p2align 4 \n\t" + " pxor %%mm7, %%mm7 \n\t" /* mm7 contains the result */ + + SAD_TRES_LOOP + SAD_TRES_LOOP + SAD_TRES_LOOP + SAD_TRES_LOOP + SAD_TRES_LOOP + SAD_TRES_LOOP + SAD_TRES_LOOP + SAD_TRES_LOOP + + " movd %%mm7, %0 \n\t" + + : "=r" (DiffVal), + "+r" (ptr1), + "+r" (ptr2) + : "r" (stride1), + "r" (stride2) + : "memory" + ); + + return DiffVal; +} + +#define SAD_XY2_TRES \ + " movq (%1), %%mm0 \n\t" /* take 8 bytes */ \ + " movq (%2), %%mm1 \n\t" \ + " movq (%3), %%mm2 \n\t" \ + " pavgb %%mm2, %%mm1 \n\t" \ + " psadbw %%mm1, %%mm0 \n\t" \ + \ + " add %4, %1 \n\t" /* Inc pointer into the new data */ \ + " paddw %%mm0, %%mm7 \n\t" /* accumulate difference... */ \ + " add %5, %2 \n\t" /* Inc pointer into ref data */ \ + " add %5, %3 \n\t" /* Inc pointer into ref data */ + + +static ogg_uint32_t sad8x8_xy2_thres__mmxext (unsigned char *SrcData, ogg_uint32_t SrcStride, + unsigned char *RefDataPtr1, + unsigned char *RefDataPtr2, ogg_uint32_t RefStride, + ogg_uint32_t thres) +{ + ogg_uint32_t DiffVal; + + __asm__ __volatile__ ( + " .p2align 4 \n\t" + " pxor %%mm7, %%mm7 \n\t" /* mm7 contains the result */ + SAD_XY2_TRES + SAD_XY2_TRES + SAD_XY2_TRES + SAD_XY2_TRES + SAD_XY2_TRES + SAD_XY2_TRES + SAD_XY2_TRES + SAD_XY2_TRES + + " movd %%mm7, %0 \n\t" + : "=m" (DiffVal), + "+r" (SrcData), + "+r" (RefDataPtr1), + "+r" (RefDataPtr2) + : "m" (SrcStride), + "m" (RefStride) + : "memory" + ); + + return DiffVal; +} + +static ogg_uint32_t row_sad8__mmxext (unsigned char *Src1, unsigned char *Src2) +{ + ogg_uint32_t MaxSad; + + __asm__ __volatile__ ( + " .p2align 4 \n\t" + + " movd (%1), %%mm0 \n\t" + " movd (%2), %%mm1 \n\t" + " psadbw %%mm0, %%mm1 \n\t" + " movd 4(%1), %%mm2 \n\t" + " movd 4(%2), %%mm3 \n\t" + " psadbw %%mm2, %%mm3 \n\t" + + " pmaxsw %%mm1, %%mm3 \n\t" + " movd %%mm3, %0 \n\t" + " andl $0xffff, %0 \n\t" + + : "=m" (MaxSad), + "+r" (Src1), + "+r" (Src2) + : + : "memory" + ); + + return MaxSad; +} + +static ogg_uint32_t col_sad8x8__mmxext (unsigned char *Src1, unsigned char *Src2, + ogg_uint32_t stride) +{ + ogg_uint32_t MaxSad; + + __asm__ __volatile__ ( + " .p2align 4 \n\t" + + " pxor %%mm3, %%mm3 \n\t" /* zero out mm3 for unpack */ + " pxor %%mm4, %%mm4 \n\t" /* mm4 low sum */ + " pxor %%mm5, %%mm5 \n\t" /* mm5 high sum */ + " pxor %%mm6, %%mm6 \n\t" /* mm6 low sum */ + " pxor %%mm7, %%mm7 \n\t" /* mm7 high sum */ + " mov $4, %%edi \n\t" /* 4 rows */ + "1: \n\t" + " movq (%1), %%mm0 \n\t" /* take 8 bytes */ + " movq (%2), %%mm1 \n\t" /* take 8 bytes */ + + " movq %%mm0, %%mm2 \n\t" + " psubusb %%mm1, %%mm0 \n\t" /* A - B */ + " psubusb %%mm2, %%mm1 \n\t" /* B - A */ + " por %%mm1, %%mm0 \n\t" /* and or gives abs difference */ + " movq %%mm0, %%mm1 \n\t" + + " punpcklbw %%mm3, %%mm0 \n\t" /* unpack to higher precision for accumulation */ + " paddw %%mm0, %%mm4 \n\t" /* accumulate difference... */ + " punpckhbw %%mm3, %%mm1 \n\t" /* unpack high four bytes to higher precision */ + " paddw %%mm1, %%mm5 \n\t" /* accumulate difference... */ + " add %3, %1 \n\t" /* Inc pointer into the new data */ + " add %3, %2 \n\t" /* Inc pointer into the new data */ + + " dec %%edi \n\t" + " jnz 1b \n\t" + + " mov $4, %%edi \n\t" /* 4 rows */ + "2: \n\t" + " movq (%1), %%mm0 \n\t" /* take 8 bytes */ + " movq (%2), %%mm1 \n\t" /* take 8 bytes */ + + " movq %%mm0, %%mm2 \n\t" + " psubusb %%mm1, %%mm0 \n\t" /* A - B */ + " psubusb %%mm2, %%mm1 \n\t" /* B - A */ + " por %%mm1, %%mm0 \n\t" /* and or gives abs difference */ + " movq %%mm0, %%mm1 \n\t" + + " punpcklbw %%mm3, %%mm0 \n\t" /* unpack to higher precision for accumulation */ + " paddw %%mm0, %%mm6 \n\t" /* accumulate difference... */ + " punpckhbw %%mm3, %%mm1 \n\t" /* unpack high four bytes to higher precision */ + " paddw %%mm1, %%mm7 \n\t" /* accumulate difference... */ + " add %3, %1 \n\t" /* Inc pointer into the new data */ + " add %3, %2 \n\t" /* Inc pointer into the new data */ + + " dec %%edi \n\t" + " jnz 2b \n\t" + + " pmaxsw %%mm6, %%mm7 \n\t" + " pmaxsw %%mm4, %%mm5 \n\t" + " pmaxsw %%mm5, %%mm7 \n\t" + " movq %%mm7, %%mm6 \n\t" + " psrlq $32, %%mm6 \n\t" + " pmaxsw %%mm6, %%mm7 \n\t" + " movq %%mm7, %%mm6 \n\t" + " psrlq $16, %%mm6 \n\t" + " pmaxsw %%mm6, %%mm7 \n\t" + " movd %%mm7, %0 \n\t" + " andl $0xffff, %0 \n\t" + + : "=r" (MaxSad), + "+r" (Src1), + "+r" (Src2) + : "r" (stride) + : "memory", "edi" + ); + + return MaxSad; +} + +static ogg_uint32_t inter8x8_err_xy2__mmxext (unsigned char *SrcData, ogg_uint32_t SrcStride, + unsigned char *RefDataPtr1, + unsigned char *RefDataPtr2, ogg_uint32_t RefStride) +{ + ogg_uint32_t XSum; + ogg_uint32_t XXSum; + + __asm__ __volatile__ ( + " .p2align 4 \n\t" + + " pxor %%mm4, %%mm4 \n\t" + " pxor %%mm5, %%mm5 \n\t" + " pxor %%mm6, %%mm6 \n\t" + " pxor %%mm7, %%mm7 \n\t" + " mov $8, %%edi \n\t" + "1: \n\t" + " movq (%2), %%mm0 \n\t" /* take 8 bytes */ + + " movq (%3), %%mm2 \n\t" + " movq (%4), %%mm1 \n\t" /* take average of mm2 and mm1 */ + " pavgb %%mm2, %%mm1 \n\t" + + " movq %%mm0, %%mm2 \n\t" + " movq %%mm1, %%mm3 \n\t" + + " punpcklbw %%mm6, %%mm0 \n\t" + " punpcklbw %%mm4, %%mm1 \n\t" + " punpckhbw %%mm6, %%mm2 \n\t" + " punpckhbw %%mm4, %%mm3 \n\t" + + " psubsw %%mm1, %%mm0 \n\t" + " psubsw %%mm3, %%mm2 \n\t" + + " paddw %%mm0, %%mm5 \n\t" + " paddw %%mm2, %%mm5 \n\t" + + " pmaddwd %%mm0, %%mm0 \n\t" + " pmaddwd %%mm2, %%mm2 \n\t" + + " paddd %%mm0, %%mm7 \n\t" + " paddd %%mm2, %%mm7 \n\t" + + " add %5, %2 \n\t" /* Inc pointer into src data */ + " add %6, %3 \n\t" /* Inc pointer into ref data */ + " add %6, %4 \n\t" /* Inc pointer into ref data */ + + " dec %%edi \n\t" + " jnz 1b \n\t" + + " movq %%mm5, %%mm0 \n\t" + " psrlq $32, %%mm5 \n\t" + " paddw %%mm0, %%mm5 \n\t" + " movq %%mm5, %%mm0 \n\t" + " psrlq $16, %%mm5 \n\t" + " paddw %%mm0, %%mm5 \n\t" + " movd %%mm5, %%edi \n\t" + " movsx %%di, %%edi \n\t" + " movl %%edi, %0 \n\t" + + " movq %%mm7, %%mm0 \n\t" + " psrlq $32, %%mm7 \n\t" + " paddd %%mm0, %%mm7 \n\t" + " movd %%mm7, %1 \n\t" + + : "=m" (XSum), + "=m" (XXSum), + "+r" (SrcData), + "+r" (RefDataPtr1), + "+r" (RefDataPtr2) + : "m" (SrcStride), + "m" (RefStride) + : "edi", "memory" + ); + + /* Compute and return population variance as mis-match metric. */ + return (( (XXSum<<6) - XSum*XSum )); +} + +void dsp_mmxext_init(DspFunctions *funcs) +{ + funcs->row_sad8 = row_sad8__mmxext; + funcs->col_sad8x8 = col_sad8x8__mmxext; + funcs->sad8x8 = sad8x8__mmxext; + funcs->sad8x8_thres = sad8x8_thres__mmxext; + funcs->sad8x8_xy2_thres = sad8x8_xy2_thres__mmxext; + funcs->inter8x8_err_xy2 = inter8x8_err_xy2__mmxext; +} + +#endif /* USE_ASM */ diff --git a/Engine/lib/libtheora/lib/enc/x86_32/fdct_mmx.c b/Engine/lib/libtheora/lib/enc/x86_32/fdct_mmx.c new file mode 100644 index 000000000..8de691f81 --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/x86_32/fdct_mmx.c @@ -0,0 +1,339 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: fdct_mmx.c 15153 2008-08-04 18:37:55Z tterribe $ + + ********************************************************************/ + +/* mmx fdct implementation */ + +#include "theora/theora.h" +#include "../codec_internal.h" +#include "../dsp.h" + +#if defined(USE_ASM) + +static const __attribute__ ((aligned(8),used)) ogg_int64_t xC1S7 = 0x0fb15fb15fb15fb15LL; +static const __attribute__ ((aligned(8),used)) ogg_int64_t xC2S6 = 0x0ec83ec83ec83ec83LL; +static const __attribute__ ((aligned(8),used)) ogg_int64_t xC3S5 = 0x0d4dbd4dbd4dbd4dbLL; +static const __attribute__ ((aligned(8),used)) ogg_int64_t xC4S4 = 0x0b505b505b505b505LL; +static const __attribute__ ((aligned(8),used)) ogg_int64_t xC5S3 = 0x08e3a8e3a8e3a8e3aLL; +static const __attribute__ ((aligned(8),used)) ogg_int64_t xC6S2 = 0x061f861f861f861f8LL; +static const __attribute__ ((aligned(8),used)) ogg_int64_t xC7S1 = 0x031f131f131f131f1LL; + +/* execute stage 1 of forward DCT */ +#define Fdct_mmx(ip0,ip1,ip2,ip3,ip4,ip5,ip6,ip7,temp) \ + " movq " #ip0 ", %%mm0 \n\t" \ + " movq " #ip1 ", %%mm1 \n\t" \ + " movq " #ip3 ", %%mm2 \n\t" \ + " movq " #ip5 ", %%mm3 \n\t" \ + " movq %%mm0, %%mm4 \n\t" \ + " movq %%mm1, %%mm5 \n\t" \ + " movq %%mm2, %%mm6 \n\t" \ + " movq %%mm3, %%mm7 \n\t" \ + \ + " paddsw " #ip7 ", %%mm0 \n\t" /* mm0 = ip0 + ip7 = is07 */ \ + " paddsw " #ip2 ", %%mm1 \n\t" /* mm1 = ip1 + ip2 = is12 */ \ + " paddsw " #ip4 ", %%mm2 \n\t" /* mm2 = ip3 + ip4 = is34 */ \ + " paddsw " #ip6 ", %%mm3 \n\t" /* mm3 = ip5 + ip6 = is56 */ \ + " psubsw " #ip7 ", %%mm4 \n\t" /* mm4 = ip0 - ip7 = id07 */ \ + " psubsw " #ip2 ", %%mm5 \n\t" /* mm5 = ip1 - ip2 = id12 */ \ + \ + " psubsw %%mm2, %%mm0 \n\t" /* mm0 = is07 - is34 */ \ + \ + " paddsw %%mm2, %%mm2 \n\t" \ + \ + " psubsw " #ip4 ", %%mm6 \n\t" /* mm6 = ip3 - ip4 = id34 */ \ + \ + " paddsw %%mm0, %%mm2 \n\t" /* mm2 = is07 + is34 = is0734 */ \ + " psubsw %%mm3, %%mm1 \n\t" /* mm1 = is12 - is56 */ \ + " movq %%mm0," #temp " \n\t" /* Save is07 - is34 to free mm0; */ \ + " paddsw %%mm3, %%mm3 \n\t" \ + " paddsw %%mm1, %%mm3 \n\t" /* mm3 = is12 + 1s56 = is1256 */ \ + \ + " psubsw " #ip6 ", %%mm7 \n\t" /* mm7 = ip5 - ip6 = id56 */ \ + /* ------------------------------------------------------------------- */ \ + " psubsw %%mm7, %%mm5 \n\t" /* mm5 = id12 - id56 */ \ + " paddsw %%mm7, %%mm7 \n\t" \ + " paddsw %%mm5, %%mm7 \n\t" /* mm7 = id12 + id56 */ \ + /* ------------------------------------------------------------------- */ \ + " psubsw %%mm3, %%mm2 \n\t" /* mm2 = is0734 - is1256 */ \ + " paddsw %%mm3, %%mm3 \n\t" \ + \ + " movq %%mm2, %%mm0 \n\t" /* make a copy */ \ + " paddsw %%mm2, %%mm3 \n\t" /* mm3 = is0734 + is1256 */ \ + \ + " pmulhw %[xC4S4], %%mm0 \n\t" /* mm0 = xC4S4 * ( is0734 - is1256 ) - ( is0734 - is1256 ) */ \ + " paddw %%mm2, %%mm0 \n\t" /* mm0 = xC4S4 * ( is0734 - is1256 ) */ \ + " psrlw $15, %%mm2 \n\t" \ + " paddw %%mm2, %%mm0 \n\t" /* Truncate mm0, now it is op[4] */ \ + \ + " movq %%mm3, %%mm2 \n\t" \ + " movq %%mm0," #ip4 " \n\t" /* save ip4, now mm0,mm2 are free */ \ + \ + " movq %%mm3, %%mm0 \n\t" \ + " pmulhw %[xC4S4], %%mm3 \n\t" /* mm3 = xC4S4 * ( is0734 +is1256 ) - ( is0734 +is1256 ) */ \ + \ + " psrlw $15, %%mm2 \n\t" \ + " paddw %%mm0, %%mm3 \n\t" /* mm3 = xC4S4 * ( is0734 +is1256 ) */ \ + " paddw %%mm2, %%mm3 \n\t" /* Truncate mm3, now it is op[0] */ \ + \ + " movq %%mm3," #ip0 " \n\t" \ + /* ------------------------------------------------------------------- */ \ + " movq " #temp ", %%mm3 \n\t" /* mm3 = irot_input_y */ \ + " pmulhw %[xC2S6], %%mm3 \n\t" /* mm3 = xC2S6 * irot_input_y - irot_input_y */ \ + \ + " movq " #temp ", %%mm2 \n\t" \ + " movq %%mm2, %%mm0 \n\t" \ + \ + " psrlw $15, %%mm2 \n\t" /* mm3 = xC2S6 * irot_input_y */ \ + " paddw %%mm0, %%mm3 \n\t" \ + \ + " paddw %%mm2, %%mm3 \n\t" /* Truncated */ \ + " movq %%mm5, %%mm0 \n\t" \ + \ + " movq %%mm5, %%mm2 \n\t" \ + " pmulhw %[xC6S2], %%mm0 \n\t" /* mm0 = xC6S2 * irot_input_x */ \ + \ + " psrlw $15, %%mm2 \n\t" \ + " paddw %%mm2, %%mm0 \n\t" /* Truncated */ \ + \ + " paddsw %%mm0, %%mm3 \n\t" /* ip[2] */ \ + " movq %%mm3," #ip2 " \n\t" /* Save ip2 */ \ + \ + " movq %%mm5, %%mm0 \n\t" \ + " movq %%mm5, %%mm2 \n\t" \ + \ + " pmulhw %[xC2S6], %%mm5 \n\t" /* mm5 = xC2S6 * irot_input_x - irot_input_x */ \ + " psrlw $15, %%mm2 \n\t" \ + \ + " movq " #temp ", %%mm3 \n\t" \ + " paddw %%mm0, %%mm5 \n\t" /* mm5 = xC2S6 * irot_input_x */ \ + \ + " paddw %%mm2, %%mm5 \n\t" /* Truncated */ \ + " movq %%mm3, %%mm2 \n\t" \ + \ + " pmulhw %[xC6S2], %%mm3 \n\t" /* mm3 = xC6S2 * irot_input_y */ \ + " psrlw $15, %%mm2 \n\t" \ + \ + " paddw %%mm2, %%mm3 \n\t" /* Truncated */ \ + " psubsw %%mm5, %%mm3 \n\t" \ + \ + " movq %%mm3," #ip6 " \n\t" \ + /* ------------------------------------------------------------------- */ \ + " movq %[xC4S4], %%mm0 \n\t" \ + " movq %%mm1, %%mm2 \n\t" \ + " movq %%mm1, %%mm3 \n\t" \ + \ + " pmulhw %%mm0, %%mm1 \n\t" /* mm0 = xC4S4 * ( is12 - is56 ) - ( is12 - is56 ) */ \ + " psrlw $15, %%mm2 \n\t" \ + \ + " paddw %%mm3, %%mm1 \n\t" /* mm0 = xC4S4 * ( is12 - is56 ) */ \ + " paddw %%mm2, %%mm1 \n\t" /* Truncate mm1, now it is icommon_product1 */ \ + \ + " movq %%mm7, %%mm2 \n\t" \ + " movq %%mm7, %%mm3 \n\t" \ + \ + " pmulhw %%mm0, %%mm7 \n\t" /* mm7 = xC4S4 * ( id12 + id56 ) - ( id12 + id56 ) */ \ + " psrlw $15, %%mm2 \n\t" \ + \ + " paddw %%mm3, %%mm7 \n\t" /* mm7 = xC4S4 * ( id12 + id56 ) */ \ + " paddw %%mm2, %%mm7 \n\t" /* Truncate mm7, now it is icommon_product2 */ \ + /* ------------------------------------------------------------------- */ \ + " pxor %%mm0, %%mm0 \n\t" /* Clear mm0 */ \ + " psubsw %%mm6, %%mm0 \n\t" /* mm0 = - id34 */ \ + \ + " psubsw %%mm7, %%mm0 \n\t" /* mm0 = - ( id34 + idcommon_product2 ) */ \ + " paddsw %%mm6, %%mm6 \n\t" \ + " paddsw %%mm0, %%mm6 \n\t" /* mm6 = id34 - icommon_product2 */ \ + \ + " psubsw %%mm1, %%mm4 \n\t" /* mm4 = id07 - icommon_product1 */ \ + " paddsw %%mm1, %%mm1 \n\t" \ + " paddsw %%mm4, %%mm1 \n\t" /* mm1 = id07 + icommon_product1 */ \ + /* ------------------------------------------------------------------- */ \ + " movq %[xC1S7], %%mm7 \n\t" \ + " movq %%mm1, %%mm2 \n\t" \ + \ + " movq %%mm1, %%mm3 \n\t" \ + " pmulhw %%mm7, %%mm1 \n\t" /* mm1 = xC1S7 * irot_input_x - irot_input_x */ \ + \ + " movq %[xC7S1], %%mm7 \n\t" \ + " psrlw $15, %%mm2 \n\t" \ + \ + " paddw %%mm3, %%mm1 \n\t" /* mm1 = xC1S7 * irot_input_x */ \ + " paddw %%mm2, %%mm1 \n\t" /* Trucated */ \ + \ + " pmulhw %%mm7, %%mm3 \n\t" /* mm3 = xC7S1 * irot_input_x */ \ + " paddw %%mm2, %%mm3 \n\t" /* Truncated */ \ + \ + " movq %%mm0, %%mm5 \n\t" \ + " movq %%mm0, %%mm2 \n\t" \ + \ + " movq %[xC1S7], %%mm7 \n\t" \ + " pmulhw %%mm7, %%mm0 \n\t" /* mm0 = xC1S7 * irot_input_y - irot_input_y */ \ + \ + " movq %[xC7S1], %%mm7 \n\t" \ + " psrlw $15, %%mm2 \n\t" \ + \ + " paddw %%mm5, %%mm0 \n\t" /* mm0 = xC1S7 * irot_input_y */ \ + " paddw %%mm2, %%mm0 \n\t" /* Truncated */ \ + \ + " pmulhw %%mm7, %%mm5 \n\t" /* mm5 = xC7S1 * irot_input_y */ \ + " paddw %%mm2, %%mm5 \n\t" /* Truncated */ \ + \ + " psubsw %%mm5, %%mm1 \n\t" /* mm1 = xC1S7 * irot_input_x - xC7S1 * irot_input_y = ip1 */ \ + " paddsw %%mm0, %%mm3 \n\t" /* mm3 = xC7S1 * irot_input_x - xC1S7 * irot_input_y = ip7 */ \ + \ + " movq %%mm1," #ip1 " \n\t" \ + " movq %%mm3," #ip7 " \n\t" \ + /* ------------------------------------------------------------------- */ \ + " movq %[xC3S5], %%mm0 \n\t" \ + " movq %[xC5S3], %%mm1 \n\t" \ + \ + " movq %%mm6, %%mm5 \n\t" \ + " movq %%mm6, %%mm7 \n\t" \ + \ + " movq %%mm4, %%mm2 \n\t" \ + " movq %%mm4, %%mm3 \n\t" \ + \ + " pmulhw %%mm0, %%mm4 \n\t" /* mm4 = xC3S5 * irot_input_x - irot_input_x */ \ + " pmulhw %%mm1, %%mm6 \n\t" /* mm6 = xC5S3 * irot_input_y - irot_input_y */ \ + \ + " psrlw $15, %%mm2 \n\t" \ + " psrlw $15, %%mm5 \n\t" \ + \ + " paddw %%mm3, %%mm4 \n\t" /* mm4 = xC3S5 * irot_input_x */ \ + " paddw %%mm7, %%mm6 \n\t" /* mm6 = xC5S3 * irot_input_y */ \ + \ + " paddw %%mm2, %%mm4 \n\t" /* Truncated */ \ + " paddw %%mm5, %%mm6 \n\t" /* Truncated */ \ + \ + " psubsw %%mm6, %%mm4 \n\t" /* ip3 */ \ + " movq %%mm4," #ip3 " \n\t" \ + \ + " movq %%mm3, %%mm4 \n\t" \ + " movq %%mm7, %%mm6 \n\t" \ + \ + " pmulhw %%mm1, %%mm3 \n\t" /* mm3 = xC5S3 * irot_input_x - irot_input_x */ \ + " pmulhw %%mm0, %%mm7 \n\t" /* mm7 = xC3S5 * irot_input_y - irot_input_y */ \ + \ + " paddw %%mm2, %%mm4 \n\t" \ + " paddw %%mm5, %%mm6 \n\t" \ + \ + " paddw %%mm4, %%mm3 \n\t" /* mm3 = xC5S3 * irot_input_x */ \ + " paddw %%mm6, %%mm7 \n\t" /* mm7 = xC3S5 * irot_input_y */ \ + \ + " paddw %%mm7, %%mm3 \n\t" /* ip5 */ \ + " movq %%mm3," #ip5 " \n\t" + +#define Transpose_mmx(ip0,ip1,ip2,ip3,ip4,ip5,ip6,ip7, \ + op0,op1,op2,op3,op4,op5,op6,op7) \ + " movq " #ip0 ", %%mm0 \n\t" /* mm0 = a0 a1 a2 a3 */ \ + " movq " #ip4 ", %%mm4 \n\t" /* mm4 = e4 e5 e6 e7 */ \ + " movq " #ip1 ", %%mm1 \n\t" /* mm1 = b0 b1 b2 b3 */ \ + " movq " #ip5 ", %%mm5 \n\t" /* mm5 = f4 f5 f6 f7 */ \ + " movq " #ip2 ", %%mm2 \n\t" /* mm2 = c0 c1 c2 c3 */ \ + " movq " #ip6 ", %%mm6 \n\t" /* mm6 = g4 g5 g6 g7 */ \ + " movq " #ip3 ", %%mm3 \n\t" /* mm3 = d0 d1 d2 d3 */ \ + " movq %%mm1," #op1 " \n\t" /* save b0 b1 b2 b3 */ \ + " movq " #ip7 ", %%mm7 \n\t" /* mm7 = h0 h1 h2 h3 */ \ + /* Transpose 2x8 block */ \ + " movq %%mm4, %%mm1 \n\t" /* mm1 = e3 e2 e1 e0 */ \ + " punpcklwd %%mm5, %%mm4 \n\t" /* mm4 = f1 e1 f0 e0 */ \ + " movq %%mm0," #op0 " \n\t" /* save a3 a2 a1 a0 */ \ + " punpckhwd %%mm5, %%mm1 \n\t" /* mm1 = f3 e3 f2 e2 */ \ + " movq %%mm6, %%mm0 \n\t" /* mm0 = g3 g2 g1 g0 */ \ + " punpcklwd %%mm7, %%mm6 \n\t" /* mm6 = h1 g1 h0 g0 */ \ + " movq %%mm4, %%mm5 \n\t" /* mm5 = f1 e1 f0 e0 */ \ + " punpckldq %%mm6, %%mm4 \n\t" /* mm4 = h0 g0 f0 e0 = MM4 */ \ + " punpckhdq %%mm6, %%mm5 \n\t" /* mm5 = h1 g1 f1 e1 = MM5 */ \ + " movq %%mm1, %%mm6 \n\t" /* mm6 = f3 e3 f2 e2 */ \ + " movq %%mm4," #op4 " \n\t" \ + " punpckhwd %%mm7, %%mm0 \n\t" /* mm0 = h3 g3 h2 g2 */ \ + " movq %%mm5," #op5 " \n\t" \ + " punpckhdq %%mm0, %%mm6 \n\t" /* mm6 = h3 g3 f3 e3 = MM7 */ \ + " movq " #op0 ", %%mm4 \n\t" /* mm4 = a3 a2 a1 a0 */ \ + " punpckldq %%mm0, %%mm1 \n\t" /* mm1 = h2 g2 f2 e2 = MM6 */ \ + " movq " #op1 ", %%mm5 \n\t" /* mm5 = b3 b2 b1 b0 */ \ + " movq %%mm4, %%mm0 \n\t" /* mm0 = a3 a2 a1 a0 */ \ + " movq %%mm6," #op7 " \n\t" \ + " punpcklwd %%mm5, %%mm0 \n\t" /* mm0 = b1 a1 b0 a0 */ \ + " movq %%mm1," #op6 " \n\t" \ + " punpckhwd %%mm5, %%mm4 \n\t" /* mm4 = b3 a3 b2 a2 */ \ + " movq %%mm2, %%mm5 \n\t" /* mm5 = c3 c2 c1 c0 */ \ + " punpcklwd %%mm3, %%mm2 \n\t" /* mm2 = d1 c1 d0 c0 */ \ + " movq %%mm0, %%mm1 \n\t" /* mm1 = b1 a1 b0 a0 */ \ + " punpckldq %%mm2, %%mm0 \n\t" /* mm0 = d0 c0 b0 a0 = MM0 */ \ + " punpckhdq %%mm2, %%mm1 \n\t" /* mm1 = d1 c1 b1 a1 = MM1 */ \ + " movq %%mm4, %%mm2 \n\t" /* mm2 = b3 a3 b2 a2 */ \ + " movq %%mm0," #op0 " \n\t" \ + " punpckhwd %%mm3, %%mm5 \n\t" /* mm5 = d3 c3 d2 c2 */ \ + " movq %%mm1," #op1 " \n\t" \ + " punpckhdq %%mm5, %%mm4 \n\t" /* mm4 = d3 c3 b3 a3 = MM3 */ \ + " punpckldq %%mm5, %%mm2 \n\t" /* mm2 = d2 c2 b2 a2 = MM2 */ \ + " movq %%mm4," #op3 " \n\t" \ + " movq %%mm2," #op2 " \n\t" + + +/* This performs a 2D Forward DCT on an 8x8 block with short + coefficients. We try to do the truncation to match the C + version. */ +static void fdct_short__mmx ( ogg_int16_t *InputData, ogg_int16_t *OutputData) +{ + ogg_int16_t __attribute__((aligned(8))) temp[8*8]; + + __asm__ __volatile__ ( + " .p2align 4 \n\t" + /* + * Input data is an 8x8 block. To make processing of the data more efficent + * we will transpose the block of data to two 4x8 blocks??? + */ + Transpose_mmx ( (%0), 16(%0), 32(%0), 48(%0), 8(%0), 24(%0), 40(%0), 56(%0), + (%1), 16(%1), 32(%1), 48(%1), 8(%1), 24(%1), 40(%1), 56(%1)) + Fdct_mmx ( (%1), 16(%1), 32(%1), 48(%1), 8(%1), 24(%1), 40(%1), 56(%1), (%2)) + + Transpose_mmx (64(%0), 80(%0), 96(%0),112(%0), 72(%0), 88(%0),104(%0),120(%0), + 64(%1), 80(%1), 96(%1),112(%1), 72(%1), 88(%1),104(%1),120(%1)) + Fdct_mmx (64(%1), 80(%1), 96(%1),112(%1), 72(%1), 88(%1),104(%1),120(%1), (%2)) + + Transpose_mmx ( 0(%1), 16(%1), 32(%1), 48(%1), 64(%1), 80(%1), 96(%1),112(%1), + 0(%1), 16(%1), 32(%1), 48(%1), 64(%1), 80(%1), 96(%1),112(%1)) + Fdct_mmx ( 0(%1), 16(%1), 32(%1), 48(%1), 64(%1), 80(%1), 96(%1),112(%1), (%2)) + + Transpose_mmx ( 8(%1), 24(%1), 40(%1), 56(%1), 72(%1), 88(%1),104(%1),120(%1), + 8(%1), 24(%1), 40(%1), 56(%1), 72(%1), 88(%1),104(%1),120(%1)) + Fdct_mmx ( 8(%1), 24(%1), 40(%1), 56(%1), 72(%1), 88(%1),104(%1),120(%1), (%2)) + + " emms \n\t" + + : "+r" (InputData), + "+r" (OutputData) + : "r" (temp), + [xC1S7] "m" (xC1S7), /* gcc 3.1+ allows named asm parameters */ + [xC2S6] "m" (xC2S6), + [xC3S5] "m" (xC3S5), + [xC4S4] "m" (xC4S4), + [xC5S3] "m" (xC5S3), + [xC6S2] "m" (xC6S2), + [xC7S1] "m" (xC7S1) + : "memory" + ); +} + +/* install our implementation in the function table */ +void dsp_mmx_fdct_init(DspFunctions *funcs) +{ + funcs->fdct_short = fdct_short__mmx; +} + +#endif /* USE_ASM */ diff --git a/Engine/lib/libtheora/lib/enc/x86_32/idct_mmx.c b/Engine/lib/libtheora/lib/enc/x86_32/idct_mmx.c new file mode 100644 index 000000000..5fc6a1f66 --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/x86_32/idct_mmx.c @@ -0,0 +1,1452 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: idct_mmx.c 15153 2008-08-04 18:37:55Z tterribe $ + + ********************************************************************/ + +#include "../codec_internal.h" + +#if defined(USE_ASM) + +#define ASM asm + +/**************************************************************************** +* +* Description : IDCT with multiple versions based on # of non 0 coeffs +* +***************************************************************************** +*/ + +// Dequantization + inverse discrete cosine transform. + +// Constants used in MMX implementation of dequantization and idct. +// All the MMX stuff works with 4 16-bit quantities at a time and +// we create 11 constants of size 4 x 16 bits. +// The first 4 are used to mask the individual 16-bit words within a group +// and are used in the address-shuffling part of the dequantization. +// The last 7 are fixed-point approximations to the cosines of angles +// occurring in the DCT; each of these contains 4 copies of the same value. + +// There is only one (statically initialized) instance of this object +// wrapped in an allocator object that forces its starting address +// to be evenly divisible by 32. Hence the actual object occupies 2.75 +// cache lines on a Pentium processor. + +// Offsets in bytes used by the assembler code below +// must of course agree with the idctConstants constructor. + +#define MaskOffset 0 // 4 masks come in order low word to high +#define CosineOffset 32 // 7 cosines come in order pi/16 * (1 ... 7) +#define EightOffset 88 +#define IdctAdjustBeforeShift 8 + +/* +UINT16 idctcosTbl[ 7] = +{ + 64277, 60547, 54491, 46341, 36410, 25080, 12785 +}; + +void fillidctconstants(void) +{ + int j = 16; + UINT16 * p; + do + { + idctconstants[ --j] = 0; + } + while( j); + + idctconstants[0] = idctconstants[5] = idctconstants[10] = idctconstants[15] = 65535; + + j = 1; + do + { + p = idctconstants + ( (j+3) << 2); + p[0] = p[1] = p[2] = p[3] = idctcosTbl[ j - 1]; + } + while( ++j <= 7); + + idctconstants[44] = idctconstants[45] = idctconstants[46] = idctconstants[47] = IdctAdjustBeforeShift; +} +*/ + +ogg_uint16_t idctconstants[(4+7+1) * 4] = { + 65535, 0, 0, 0, 0, 65535, 0, 0, + 0, 0, 65535, 0, 0, 0, 0, 65535, + 64277, 64277, 64277, 64277, 60547, 60547, 60547, 60547, + 54491, 54491, 54491, 54491, 46341, 46341, 46341, 46341, + 36410, 36410, 36410, 36410, 25080, 25080, 25080, 25080, + 12785, 12785, 12785, 12785, 8, 8, 8, 8, +}; + +/* Dequantization + inverse DCT. + + Dequantization multiplies user's 16-bit signed indices (range -512 to +511) + by unsigned 16-bit quantization table entries. + These table entries are upscaled by 4, max is 30 * 128 * 4 < 2^14. + Result is scaled signed DCT coefficients (abs value < 2^15). + + In the data stream, the coefficients are sent in order of increasing + total (horizontal + vertical) frequency. The exact picture is as follows: + + 00 01 05 06 16 17 33 34 + 02 04 07 15 20 32 35 52 + 03 10 14 21 31 36 51 53 + 11 13 22 30 37 50 54 65 + + 12 23 27 40 47 55 64 66 + 24 26 41 46 56 63 67 74 + 25 42 45 57 62 70 73 75 + 43 44 60 61 71 72 76 77 + + Here the position in the matrix corresponds to the (horiz,vert) + freqency indices and the octal entry in the matrix is the position + of the coefficient in the data stream. Thus the coefficients are sent + in sort of a diagonal "snake". + + The dequantization stage "uncurls the snake" and stores the expanded + coefficients in more convenient positions. These are not exactly the + natural positions given above but take into account our implementation + of the idct, which basically requires two one-dimensional idcts and + two transposes. + + We fold the first transpose into the storage of the expanded coefficients. + We don't actually do a full transpose because this would require doubling + the size of the idct buffer; rather, we just transpose each of the 4x4 + subblocks. Using slightly varying addressing schemes in each of the + four 4x8 idcts then allows these transforms to be done in place. + + Transposing the 4x4 subblocks in the matrix above gives + + 00 02 03 11 16 20 31 37 + 01 04 10 13 17 32 36 50 + 05 07 14 22 33 35 51 54 + 06 15 21 30 34 52 53 65 + + 12 24 25 43 47 56 62 71 + 23 26 42 44 55 63 70 72 + 27 41 45 60 64 67 73 76 + 40 46 57 61 66 74 75 77 + + Finally, we reverse the words in each 4 word group to clarify + direction of shifts. + + 11 03 02 00 37 31 20 16 + 13 10 04 01 50 36 32 17 + 22 14 07 05 54 51 35 33 + 30 21 15 06 65 53 52 34 + + 43 25 24 12 71 62 56 47 + 44 42 26 23 72 70 63 55 + 60 45 41 27 76 73 67 64 + 61 57 46 40 77 75 74 66 + + This matrix then shows the 16 4x16 destination words in terms of + the 16 4x16 input words. + + We implement this algorithm by manipulation of mmx registers, + which seems to be the fastest way to proceed. It is completely + hand-written; there does not seem to be enough recurrence to + reasonably compartmentalize any of it. Hence the resulting + program is ugly and bloated. Furthermore, due to the absence of + register pressure, it is boring and artless. I hate it. + + The idct itself is more interesting. Since the two-dimensional dct + basis functions are products of the one-dimesional dct basis functions, + we can compute an inverse (or forward) dct via two 1-D transforms, + on rows then on columns. To exploit MMX parallelism, we actually do + both operations on columns, interposing a (partial) transpose between + the two 1-D transforms, the first transpose being done by the expansion + described above. + + The 8-sample one-dimensional DCT is a standard orthogonal expansion using + the (unnormalized) basis functions + + b[k]( i) = cos( pi * k * (2i + 1) / 16); + + here k = 0 ... 7 is the frequency and i = 0 ... 7 is the spatial coordinate. + To normalize, b[0] should be multiplied by 1/sqrt( 8) and the other b[k] + should be multiplied by 1/2. + + The 8x8 two-dimensional DCT is just the product of one-dimensional DCTs + in each direction. The (unnormalized) basis functions are + + B[k,l]( i, j) = b[k]( i) * b[l]( j); + + this time k and l are the horizontal and vertical frequencies, + i and j are the horizontal and vertical spatial coordinates; + all indices vary from 0 ... 7 (as above) + and there are now 4 cases of normalization. + + Our 1-D idct expansion uses constants C1 ... C7 given by + + (*) Ck = C(-k) = cos( pi * k/16) = S(8-k) = -S(k-8) = sin( pi * (8-k)/16) + + and the following 1-D algorithm transforming I0 ... I7 to R0 ... R7 : + + A = (C1 * I1) + (C7 * I7) B = (C7 * I1) - (C1 * I7) + C = (C3 * I3) + (C5 * I5) D = (C3 * I5) - (C5 * I3) + A. = C4 * (A - C) B. = C4 * (B - D) + C. = A + C D. = B + D + + E = C4 * (I0 + I4) F = C4 * (I0 - I4) + G = (C2 * I2) + (C6 * I6) H = (C6 * I2) - (C2 * I6) + E. = E - G + G. = E + G + + A.. = F + A. B.. = B. - H + F. = F - A. H. = B. + H + + R0 = G. + C. R1 = A.. + H. R3 = E. + D. R5 = F. + B.. + R7 = G. - C. R2 = A.. - H. R4 = E. - D. R6 = F. - B.. + + It is due to Vetterli and Lightenberg and may be found in the JPEG + reference book by Pennebaker and Mitchell. + + Correctness of the algorithm follows from (*) together with the + addition formulas for sine and cosine: + + cos( A + B) = cos( A) * cos( B) - sin( A) * sin( B) + sin( A + B) = sin( A) * cos( B) + cos( A) * sin( B) + + Note that this implementation absorbs the difference in normalization + between the 0th and higher frequencies, although the results produced + are actually twice as big as they should be. Since we do this for each + dimension, the 2-D idct results are 4x the desired results. Finally, + taking into account that the dequantization multiplies by 4 as well, + our actual results are 16x too big. We fix this by shifting the final + results right by 4 bits. + + High precision version approximates C1 ... C7 to 16 bits. + Since MMX only provides a signed multiply, C1 ... C5 appear to be + negative and multiplies involving them must be adjusted to compensate + for this. C6 and C7 do not require this adjustment since + they are < 1/2 and are correctly treated as positive numbers. + + Following macro does four 8-sample one-dimensional idcts in parallel. + This is actually not such a difficult program to write once you + make a couple of observations (I of course was unable to make these + observations until I'd half-written a couple of other versions). + + 1. Everything is easy once you are done with the multiplies. + This is because, given X and Y in registers, one may easily + calculate X+Y and X-Y using just those 2 registers. + + 2. You always need at least 2 extra registers to calculate products, + so storing 2 temporaries is inevitable. C. and D. seem to be + the best candidates. + + 3. The products should be calculated in decreasing order of complexity + (which translates into register pressure). Since C1 ... C5 require + adjustment (and C6, C7 do not), we begin by calculating C and D. +*/ + +/************************************************************************************** + * + * Routine: BeginIDCT + * + * Description: The Macro does IDct on 4 1-D Dcts + * + * Input: None + * + * Output: None + * + * Return: None + * + * Special Note: None + * + * Error: None + * + *************************************************************************************** + */ + +#define MtoSTR(s) #s + +#define Dump "call MMX_dump\n" + +#define BeginIDCT "#BeginIDCT\n" \ + \ + " movq " I(3)","r2"\n" \ + \ + " movq " C(3)","r6"\n" \ + " movq " r2","r4"\n" \ + " movq " J(5)","r7"\n" \ + " pmulhw " r6","r4"\n" \ + " movq " C(5)","r1"\n" \ + " pmulhw " r7","r6"\n" \ + " movq " r1","r5"\n" \ + " pmulhw " r2","r1"\n" \ + " movq " I(1)","r3"\n" \ + " pmulhw " r7","r5"\n" \ + " movq " C(1)","r0"\n" \ + " paddw " r2","r4"\n" \ + " paddw " r7","r6"\n" \ + " paddw " r1","r2"\n" \ + " movq " J(7)","r1"\n" \ + " paddw " r5","r7"\n" \ + " movq " r0","r5"\n" \ + " pmulhw " r3","r0"\n" \ + " paddsw " r7","r4"\n" \ + " pmulhw " r1","r5"\n" \ + " movq " C(7)","r7"\n" \ + " psubsw " r2","r6"\n" \ + " paddw " r3","r0"\n" \ + " pmulhw " r7","r3"\n" \ + " movq " I(2)","r2"\n" \ + " pmulhw " r1","r7"\n" \ + " paddw " r1","r5"\n" \ + " movq " r2","r1"\n" \ + " pmulhw " C(2)","r2"\n" \ + " psubsw " r5","r3"\n" \ + " movq " J(6)","r5"\n" \ + " paddsw " r7","r0"\n" \ + " movq " r5","r7"\n" \ + " psubsw " r4","r0"\n" \ + " pmulhw " C(2)","r5"\n" \ + " paddw " r1","r2"\n" \ + " pmulhw " C(6)","r1"\n" \ + " paddsw " r4","r4"\n" \ + " paddsw " r0","r4"\n" \ + " psubsw " r6","r3"\n" \ + " paddw " r7","r5"\n" \ + " paddsw " r6","r6"\n" \ + " pmulhw " C(6)","r7"\n" \ + " paddsw " r3","r6"\n" \ + " movq " r4","I(1)"\n" \ + " psubsw " r5","r1"\n" \ + " movq " C(4)","r4"\n" \ + " movq " r3","r5"\n" \ + " pmulhw " r4","r3"\n" \ + " paddsw " r2","r7"\n" \ + " movq " r6","I(2)"\n" \ + " movq " r0","r2"\n" \ + " movq " I(0)","r6"\n" \ + " pmulhw " r4","r0"\n" \ + " paddw " r3","r5"\n" \ + "\n" \ + " movq " J(4)","r3"\n" \ + " psubsw " r1","r5"\n" \ + " paddw " r0","r2"\n" \ + " psubsw " r3","r6"\n" \ + " movq " r6","r0"\n" \ + " pmulhw " r4","r6"\n" \ + " paddsw " r3","r3"\n" \ + " paddsw " r1","r1"\n" \ + " paddsw " r0","r3"\n" \ + " paddsw " r5","r1"\n" \ + " pmulhw " r3","r4"\n" \ + " paddsw " r0","r6"\n" \ + " psubsw " r2","r6"\n" \ + " paddsw " r2","r2"\n" \ + " movq " I(1)","r0"\n" \ + " paddsw " r6","r2"\n" \ + " paddw " r3","r4"\n" \ + " psubsw " r1","r2"\n" \ + "#end BeginIDCT\n" +// end BeginIDCT macro (38 cycles). + + +// Two versions of the end of the idct depending on whether we're feeding +// into a transpose or dividing the final results by 16 and storing them. + +/************************************************************************************** + * + * Routine: RowIDCT + * + * Description: The Macro does 1-D IDct on 4 Rows + * + * Input: None + * + * Output: None + * + * Return: None + * + * Special Note: None + * + * Error: None + * + *************************************************************************************** + */ + +// RowIDCT gets ready to transpose. + +#define RowIDCT ASM("\n"\ + "#RowIDCT\n" \ + BeginIDCT \ + "\n" \ + " movq "I(2)","r3"\n" /* r3 = D. */ \ + " psubsw "r7","r4"\n" /* r4 = E. = E - G */ \ + " paddsw "r1","r1"\n" /* r1 = H. + H. */ \ + " paddsw "r7","r7"\n" /* r7 = G + G */ \ + " paddsw "r2","r1"\n" /* r1 = R1 = A.. + H. */\ + " paddsw "r4","r7"\n" /* r7 = G. = E + G */ \ + " psubsw "r3","r4"\n" /* r4 = R4 = E. - D. */ \ + " paddsw "r3","r3"\n" \ + " psubsw "r5","r6"\n" /* r6 = R6 = F. - B.. */\ + " paddsw "r5","r5"\n" \ + " paddsw "r4","r3"\n" /* r3 = R3 = E. + D. */ \ + " paddsw "r6","r5"\n" /* r5 = R5 = F. + B.. */\ + " psubsw "r0","r7"\n" /* r7 = R7 = G. - C. */ \ + " paddsw "r0","r0"\n" \ + " movq "r1","I(1)"\n" /* save R1 */ \ + " paddsw "r7","r0"\n" /* r0 = R0 = G. + C. */ \ + "#end RowIDCT" \ +); +// end RowIDCT macro (8 + 38 = 46 cycles) + + +/************************************************************************************** + * + * Routine: ColumnIDCT + * + * Description: The Macro does 1-D IDct on 4 columns + * + * Input: None + * + * Output: None + * + * Return: None + * + * Special Note: None + * + * Error: None + * + *************************************************************************************** + */ +// Column IDCT normalizes and stores final results. + +#define ColumnIDCT ASM("\n" \ + "#ColumnIDCT\n" \ + BeginIDCT \ + "\n" \ + " paddsw "Eight","r2"\n" \ + " paddsw "r1","r1"\n" /* r1 = H. + H. */ \ + " paddsw "r2","r1"\n" /* r1 = R1 = A.. + H. */\ + " psraw ""$4"","r2"\n" /* r2 = NR2 */ \ + " psubsw "r7","r4"\n" /* r4 = E. = E - G */ \ + " psraw ""$4"","r1"\n" /* r1 = NR1 */ \ + " movq "I(2)","r3"\n" /* r3 = D. */ \ + " paddsw "r7","r7"\n" /* r7 = G + G */ \ + " movq "r2","I(2)"\n" /* store NR2 at I2 */ \ + " paddsw "r4","r7"\n" /* r7 = G. = E + G */ \ + " movq "r1","I(1)"\n" /* store NR1 at I1 */ \ + " psubsw "r3","r4"\n" /* r4 = R4 = E. - D. */ \ + " paddsw "Eight","r4"\n" \ + " paddsw "r3","r3"\n" /* r3 = D. + D. */ \ + " paddsw "r4","r3"\n" /* r3 = R3 = E. + D. */ \ + " psraw ""$4"","r4"\n" /* r4 = NR4 */ \ + " psubsw "r5","r6"\n" /* r6 = R6 = F. - B.. */\ + " psraw ""$4"","r3"\n" /* r3 = NR3 */ \ + " paddsw "Eight","r6"\n" \ + " paddsw "r5","r5"\n" /* r5 = B.. + B.. */ \ + " paddsw "r6","r5"\n" /* r5 = R5 = F. + B.. */\ + " psraw ""$4"","r6"\n" /* r6 = NR6 */ \ + " movq "r4","J(4)"\n" /* store NR4 at J4 */ \ + " psraw ""$4"","r5"\n" /* r5 = NR5 */ \ + " movq "r3","I(3)"\n" /* store NR3 at I3 */ \ + " psubsw "r0","r7"\n" /* r7 = R7 = G. - C. */ \ + " paddsw "Eight","r7"\n" \ + " paddsw "r0","r0"\n" /* r0 = C. + C. */ \ + " paddsw "r7","r0"\n" /* r0 = R0 = G. + C. */ \ + " psraw ""$4"","r7"\n" /* r7 = NR7 */ \ + " movq "r6","J(6)"\n" /* store NR6 at J6 */ \ + " psraw ""$4"","r0"\n" /* r0 = NR0 */ \ + " movq "r5","J(5)"\n" /* store NR5 at J5 */ \ + " movq "r7","J(7)"\n" /* store NR7 at J7 */ \ + " movq "r0","I(0)"\n" /* store NR0 at I0 */ \ + "#end ColumnIDCT\n" \ +); +// end ColumnIDCT macro (38 + 19 = 57 cycles) + +/************************************************************************************** + * + * Routine: Transpose + * + * Description: The Macro does two 4x4 transposes in place. + * + * Input: None + * + * Output: None + * + * Return: None + * + * Special Note: None + * + * Error: None + * + *************************************************************************************** + */ + +/* Following macro does two 4x4 transposes in place. + + At entry (we assume): + + r0 = a3 a2 a1 a0 + I(1) = b3 b2 b1 b0 + r2 = c3 c2 c1 c0 + r3 = d3 d2 d1 d0 + + r4 = e3 e2 e1 e0 + r5 = f3 f2 f1 f0 + r6 = g3 g2 g1 g0 + r7 = h3 h2 h1 h0 + + At exit, we have: + + I(0) = d0 c0 b0 a0 + I(1) = d1 c1 b1 a1 + I(2) = d2 c2 b2 a2 + I(3) = d3 c3 b3 a3 + + J(4) = h0 g0 f0 e0 + J(5) = h1 g1 f1 e1 + J(6) = h2 g2 f2 e2 + J(7) = h3 g3 f3 e3 + + I(0) I(1) I(2) I(3) is the transpose of r0 I(1) r2 r3. + J(4) J(5) J(6) J(7) is the transpose of r4 r5 r6 r7. + + Since r1 is free at entry, we calculate the Js first. */ + + +#define Transpose ASM("\n#Transpose\n" \ + \ + " movq "r4","r1"\n" \ + " punpcklwd "r5","r4"\n" \ + " movq "r0","I(0)"\n" \ + " punpckhwd "r5","r1"\n" \ + " movq "r6","r0"\n" \ + " punpcklwd "r7","r6"\n" \ + " movq "r4","r5"\n" \ + " punpckldq "r6","r4"\n" \ + " punpckhdq "r6","r5"\n" \ + " movq "r1","r6"\n" \ + " movq "r4","J(4)"\n" \ + " punpckhwd "r7","r0"\n" \ + " movq "r5","J(5)"\n" \ + " punpckhdq "r0","r6"\n" \ + " movq "I(0)","r4"\n" \ + " punpckldq "r0","r1"\n" \ + " movq "I(1)","r5"\n" \ + " movq "r4","r0"\n" \ + " movq "r6","J(7)"\n" \ + " punpcklwd "r5","r0"\n" \ + " movq "r1","J(6)"\n" \ + " punpckhwd "r5","r4"\n" \ + " movq "r2","r5"\n" \ + " punpcklwd "r3","r2"\n" \ + " movq "r0","r1"\n" \ + " punpckldq "r2","r0"\n" \ + " punpckhdq "r2","r1"\n" \ + " movq "r4","r2"\n" \ + " movq "r0","I(0)"\n" \ + " punpckhwd "r3","r5"\n" \ + " movq "r1","I(1)"\n" \ + " punpckhdq "r5","r4"\n" \ + " punpckldq "r5","r2"\n" \ + \ + " movq "r4","I(3)"\n" \ + \ + " movq "r2","I(2)"\n" \ + "#end Transpose\n" \ +); +// end Transpose macro (19 cycles). + +/* +static void MMX_dump() +{ + ASM + ("\ + movq %mm0,(%edi)\n\ + movq %mm1,8(%edi)\n\ + movq %mm2,16(%edi)\n\ + movq %mm3,24(%edi)\n\ + movq %mm4,32(%edi)\n\ + movq %mm5,40(%edi)\n\ + movq %mm6,48(%edi)\n\ + movq %mm7,56(%edi)\n\ + ret" + ); +} +*/ + +/************************************************************************************** + * + * Routine: MMX_idct + * + * Description: Perform IDCT on a 8x8 block + * + * Input: Pointer to input and output buffer + * + * Output: None + * + * Return: None + * + * Special Note: The input coefficients are in ZigZag order + * + * Error: None + * + *************************************************************************************** + */ +void IDctSlow__mmx( Q_LIST_ENTRY * InputData, + ogg_int16_t *QuantMatrix, + ogg_int16_t * OutputData ) { + +# define MIDM(M,I) MtoSTR(M+I*8(%ecx)) +# define M(I) MIDM( MaskOffset , I ) +# define MIDC(M,I) MtoSTR(M+(I-1)*8(%ecx)) +# define C(I) MIDC( CosineOffset , I ) +# define MIDEight(M) MtoSTR(M(%ecx)) +# define Eight MIDEight(EightOffset) + +# define r0 "%mm0" +# define r1 "%mm1" +# define r2 "%mm2" +# define r3 "%mm3" +# define r4 "%mm4" +# define r5 "%mm5" +# define r6 "%mm6" +# define r7 "%mm7" + + __asm__ __volatile__ ( + /* eax = quantized input */ + /* esi = quantization table */ + /* edx = destination (= idct buffer) */ + /* ecx = idctconstants */ + "" + : + :"a"(InputData), "S"(QuantMatrix), "d"(OutputData), "c"(idctconstants) + ); + + ASM( + "movq (%eax), "r0"\n" + "pmullw (%esi), "r0"\n" /* r0 = 03 02 01 00 */ + "movq 16(%eax), "r1"\n" + "pmullw 16(%esi), "r1"\n" /* r1 = 13 12 11 10 */ + "movq "M(0)", "r2"\n" /* r2 = __ __ __ FF */ + "movq "r0", "r3"\n" /* r3 = 03 02 01 00 */ + "movq 8(%eax), "r4"\n" + "psrlq $16, "r0"\n" /* r0 = __ 03 02 01 */ + "pmullw 8(%esi), "r4"\n" /* r4 = 07 06 05 04 */ + "pand "r2", "r3"\n" /* r3 = __ __ __ 00 */ + "movq "r0", "r5"\n" /* r5 = __ 03 02 01 */ + "movq "r1", "r6"\n" /* r6 = 13 12 11 10 */ + "pand "r2", "r5"\n" /* r5 = __ __ __ 01 */ + "psllq $32, "r6"\n" /* r6 = 11 10 __ __ */ + "movq "M(3)", "r7"\n" /* r7 = FF __ __ __ */ + "pxor "r5", "r0"\n" /* r0 = __ 03 02 __ */ + "pand "r6", "r7"\n" /* r7 = 11 __ __ __ */ + "por "r3", "r0"\n" /* r0 = __ 03 02 00 */ + "pxor "r7", "r6"\n" /* r6 = __ 10 __ __ */ + "por "r7", "r0"\n" /* r0 = 11 03 02 00 = R0 */ + "movq "M(3)", "r7"\n" /* r7 = FF __ __ __ */ + "movq "r4", "r3"\n" /* r3 = 07 06 05 04 */ + "movq "r0", (%edx)\n" /* write R0 = r0 */ + "pand "r2", "r3"\n" /* r3 = __ __ __ 04 */ + "movq 32(%eax), "r0"\n" + "psllq $16, "r3"\n" /* r3 = __ __ 04 __ */ + "pmullw 32(%esi), "r0"\n" /* r0 = 23 22 21 20 */ + "pand "r1", "r7"\n" /* r7 = 13 __ __ __ */ + "por "r3", "r5"\n" /* r5 = __ __ 04 01 */ + "por "r6", "r7"\n" /* r7 = 13 10 __ __ */ + "movq 24(%eax), "r3"\n" + "por "r5", "r7"\n" /* r7 = 13 10 04 01 = R1 */ + "pmullw 24(%esi), "r3"\n" /* r3 = 17 16 15 14 */ + "psrlq $16, "r4"\n" /* r4 = __ 07 06 05 */ + "movq "r7", 16(%edx)\n" /* write R1 = r7 */ + "movq "r4", "r5"\n" /* r5 = __ 07 06 05 */ + "movq "r0", "r7"\n" /* r7 = 23 22 21 20 */ + "psrlq $16, "r4"\n" /* r4 = __ __ 07 06 */ + "psrlq $48, "r7"\n" /* r7 = __ __ __ 23 */ + "movq "r2", "r6"\n" /* r6 = __ __ __ FF */ + "pand "r2", "r5"\n" /* r5 = __ __ __ 05 */ + "pand "r4", "r6"\n" /* r6 = __ __ __ 06 */ + "movq "r7", 80(%edx)\n" /* partial R9 = __ __ __ 23 */ + "pxor "r6", "r4"\n" /* r4 = __ __ 07 __ */ + "psrlq $32, "r1"\n" /* r1 = __ __ 13 12 */ + "por "r5", "r4"\n" /* r4 = __ __ 07 05 */ + "movq "M(3)", "r7"\n" /* r7 = FF __ __ __ */ + "pand "r2", "r1"\n" /* r1 = __ __ __ 12 */ + "movq 48(%eax), "r5"\n" + "psllq $16, "r0"\n" /* r0 = 22 21 20 __ */ + "pmullw 48(%esi), "r5"\n" /* r5 = 33 32 31 30 */ + "pand "r0", "r7"\n" /* r7 = 22 __ __ __ */ + "movq "r1", 64(%edx)\n" /* partial R8 = __ __ __ 12 */ + "por "r4", "r7"\n" /* r7 = 22 __ 07 05 */ + "movq "r3", "r4"\n" /* r4 = 17 16 15 14 */ + "pand "r2", "r3"\n" /* r3 = __ __ __ 14 */ + "movq "M(2)", "r1"\n" /* r1 = __ FF __ __ */ + "psllq $32, "r3"\n" /* r3 = __ 14 __ __ */ + "por "r3", "r7"\n" /* r7 = 22 14 07 05 = R2 */ + "movq "r5", "r3"\n" /* r3 = 33 32 31 30 */ + "psllq $48, "r3"\n" /* r3 = 30 __ __ __ */ + "pand "r0", "r1"\n" /* r1 = __ 21 __ __ */ + "movq "r7", 32(%edx)\n" /* write R2 = r7 */ + "por "r3", "r6"\n" /* r6 = 30 __ __ 06 */ + "movq "M(1)", "r7"\n" /* r7 = __ __ FF __ */ + "por "r1", "r6"\n" /* r6 = 30 21 __ 06 */ + "movq 56(%eax), "r1"\n" + "pand "r4", "r7"\n" /* r7 = __ __ 15 __ */ + "pmullw 56(%esi), "r1"\n" /* r1 = 37 36 35 34 */ + "por "r6", "r7"\n" /* r7 = 30 21 15 06 = R3 */ + "pand "M(1)", "r0"\n" /* r0 = __ __ 20 __ */ + "psrlq $32, "r4"\n" /* r4 = __ __ 17 16 */ + "movq "r7", 48(%edx)\n" /* write R3 = r7 */ + "movq "r4", "r6"\n" /* r6 = __ __ 17 16 */ + "movq "M(3)", "r7"\n" /* r7 = FF __ __ __ */ + "pand "r2", "r4"\n" /* r4 = __ __ __ 16 */ + "movq "M(1)", "r3"\n" /* r3 = __ __ FF __ */ + "pand "r1", "r7"\n" /* r7 = 37 __ __ __ */ + "pand "r5", "r3"\n" /* r3 = __ __ 31 __ */ + "por "r4", "r0"\n" /* r0 = __ __ 20 16 */ + "psllq $16, "r3"\n" /* r3 = __ 31 __ __ */ + "por "r0", "r7"\n" /* r7 = 37 __ 20 16 */ + "movq "M(2)", "r4"\n" /* r4 = __ FF __ __ */ + "por "r3", "r7"\n" /* r7 = 37 31 20 16 = R4 */ + "movq 80(%eax), "r0"\n" + "movq "r4", "r3"\n" /* r3 = __ __ FF __ */ + "pmullw 80(%esi), "r0"\n" /* r0 = 53 52 51 50 */ + "pand "r5", "r4"\n" /* r4 = __ 32 __ __ */ + "movq "r7", 8(%edx)\n" /* write R4 = r7 */ + "por "r4", "r6"\n" /* r6 = __ 32 17 16 */ + "movq "r3", "r4"\n" /* r4 = __ FF __ __ */ + "psrlq $16, "r6"\n" /* r6 = __ __ 32 17 */ + "movq "r0", "r7"\n" /* r7 = 53 52 51 50 */ + "pand "r1", "r4"\n" /* r4 = __ 36 __ __ */ + "psllq $48, "r7"\n" /* r7 = 50 __ __ __ */ + "por "r4", "r6"\n" /* r6 = __ 36 32 17 */ + "movq 88(%eax), "r4"\n" + "por "r6", "r7"\n" /* r7 = 50 36 32 17 = R5 */ + "pmullw 88(%esi), "r4"\n" /* r4 = 57 56 55 54 */ + "psrlq $16, "r3"\n" /* r3 = __ __ FF __ */ + "movq "r7", 24(%edx)\n" /* write R5 = r7 */ + "pand "r1", "r3"\n" /* r3 = __ __ 35 __ */ + "psrlq $48, "r5"\n" /* r5 = __ __ __ 33 */ + "pand "r2", "r1"\n" /* r1 = __ __ __ 34 */ + "movq 104(%eax), "r6"\n" + "por "r3", "r5"\n" /* r5 = __ __ 35 33 */ + "pmullw 104(%esi), "r6"\n" /* r6 = 67 66 65 64 */ + "psrlq $16, "r0"\n" /* r0 = __ 53 52 51 */ + "movq "r4", "r7"\n" /* r7 = 57 56 55 54 */ + "movq "r2", "r3"\n" /* r3 = __ __ __ FF */ + "psllq $48, "r7"\n" /* r7 = 54 __ __ __ */ + "pand "r0", "r3"\n" /* r3 = __ __ __ 51 */ + "pxor "r3", "r0"\n" /* r0 = __ 53 52 __ */ + "psllq $32, "r3"\n" /* r3 = __ 51 __ __ */ + "por "r5", "r7"\n" /* r7 = 54 __ 35 33 */ + "movq "r6", "r5"\n" /* r5 = 67 66 65 64 */ + "pand "M(1)", "r6"\n" /* r6 = __ __ 65 __ */ + "por "r3", "r7"\n" /* r7 = 54 51 35 33 = R6 */ + "psllq $32, "r6"\n" /* r6 = 65 __ __ __ */ + "por "r1", "r0"\n" /* r0 = __ 53 52 34 */ + "movq "r7", 40(%edx)\n" /* write R6 = r7 */ + "por "r6", "r0"\n" /* r0 = 65 53 52 34 = R7 */ + "movq 120(%eax), "r7"\n" + "movq "r5", "r6"\n" /* r6 = 67 66 65 64 */ + "pmullw 120(%esi), "r7"\n" /* r7 = 77 76 75 74 */ + "psrlq $32, "r5"\n" /* r5 = __ __ 67 66 */ + "pand "r2", "r6"\n" /* r6 = __ __ __ 64 */ + "movq "r5", "r1"\n" /* r1 = __ __ 67 66 */ + "movq "r0", 56(%edx)\n" /* write R7 = r0 */ + "pand "r2", "r1"\n" /* r1 = __ __ __ 66 */ + "movq 112(%eax), "r0"\n" + "movq "r7", "r3"\n" /* r3 = 77 76 75 74 */ + "pmullw 112(%esi), "r0"\n" /* r0 = 73 72 71 70 */ + "psllq $16, "r3"\n" /* r3 = 76 75 74 __ */ + "pand "M(3)", "r7"\n" /* r7 = 77 __ __ __ */ + "pxor "r1", "r5"\n" /* r5 = __ __ 67 __ */ + "por "r5", "r6"\n" /* r6 = __ __ 67 64 */ + "movq "r3", "r5"\n" /* r5 = 76 75 74 __ */ + "pand "M(3)", "r5"\n" /* r5 = 76 __ __ __ */ + "por "r1", "r7"\n" /* r7 = 77 __ __ 66 */ + "movq 96(%eax), "r1"\n" + "pxor "r5", "r3"\n" /* r3 = __ 75 74 __ */ + "pmullw 96(%esi), "r1"\n" /* r1 = 63 62 61 60 */ + "por "r3", "r7"\n" /* r7 = 77 75 74 66 = R15 */ + "por "r5", "r6"\n" /* r6 = 76 __ 67 64 */ + "movq "r0", "r5"\n" /* r5 = 73 72 71 70 */ + "movq "r7", 120(%edx)\n" /* store R15 = r7 */ + "psrlq $16, "r5"\n" /* r5 = __ 73 72 71 */ + "pand "M(2)", "r5"\n" /* r5 = __ 73 __ __ */ + "movq "r0", "r7"\n" /* r7 = 73 72 71 70 */ + "por "r5", "r6"\n" /* r6 = 76 73 67 64 = R14 */ + "pand "r2", "r0"\n" /* r0 = __ __ __ 70 */ + "pxor "r0", "r7"\n" /* r7 = 73 72 71 __ */ + "psllq $32, "r0"\n" /* r0 = __ 70 __ __ */ + "movq "r6", 104(%edx)\n" /* write R14 = r6 */ + "psrlq $16, "r4"\n" /* r4 = __ 57 56 55 */ + "movq 72(%eax), "r5"\n" + "psllq $16, "r7"\n" /* r7 = 72 71 __ __ */ + "pmullw 72(%esi), "r5"\n" /* r5 = 47 46 45 44 */ + "movq "r7", "r6"\n" /* r6 = 72 71 __ __ */ + "movq "M(2)", "r3"\n" /* r3 = __ FF __ __ */ + "psllq $16, "r6"\n" /* r6 = 71 __ __ __ */ + "pand "M(3)", "r7"\n" /* r7 = 72 __ __ __ */ + "pand "r1", "r3"\n" /* r3 = __ 62 __ __ */ + "por "r0", "r7"\n" /* r7 = 72 70 __ __ */ + "movq "r1", "r0"\n" /* r0 = 63 62 61 60 */ + "pand "M(3)", "r1"\n" /* r1 = 63 __ __ __ */ + "por "r3", "r6"\n" /* r6 = 71 62 __ __ */ + "movq "r4", "r3"\n" /* r3 = __ 57 56 55 */ + "psrlq $32, "r1"\n" /* r1 = __ __ 63 __ */ + "pand "r2", "r3"\n" /* r3 = __ __ __ 55 */ + "por "r1", "r7"\n" /* r7 = 72 70 63 __ */ + "por "r3", "r7"\n" /* r7 = 72 70 63 55 = R13 */ + "movq "r4", "r3"\n" /* r3 = __ 57 56 55 */ + "pand "M(1)", "r3"\n" /* r3 = __ __ 56 __ */ + "movq "r5", "r1"\n" /* r1 = 47 46 45 44 */ + "movq "r7", 88(%edx)\n" /* write R13 = r7 */ + "psrlq $48, "r5"\n" /* r5 = __ __ __ 47 */ + "movq 64(%eax), "r7"\n" + "por "r3", "r6"\n" /* r6 = 71 62 56 __ */ + "pmullw 64(%esi), "r7"\n" /* r7 = 43 42 41 40 */ + "por "r5", "r6"\n" /* r6 = 71 62 56 47 = R12 */ + "pand "M(2)", "r4"\n" /* r4 = __ 57 __ __ */ + "psllq $32, "r0"\n" /* r0 = 61 60 __ __ */ + "movq "r6", 72(%edx)\n" /* write R12 = r6 */ + "movq "r0", "r6"\n" /* r6 = 61 60 __ __ */ + "pand "M(3)", "r0"\n" /* r0 = 61 __ __ __ */ + "psllq $16, "r6"\n" /* r6 = 60 __ __ __ */ + "movq 40(%eax), "r5"\n" + "movq "r1", "r3"\n" /* r3 = 47 46 45 44 */ + "pmullw 40(%esi), "r5"\n" /* r5 = 27 26 25 24 */ + "psrlq $16, "r1"\n" /* r1 = __ 47 46 45 */ + "pand "M(1)", "r1"\n" /* r1 = __ __ 46 __ */ + "por "r4", "r0"\n" /* r0 = 61 57 __ __ */ + "pand "r7", "r2"\n" /* r2 = __ __ __ 40 */ + "por "r1", "r0"\n" /* r0 = 61 57 46 __ */ + "por "r2", "r0"\n" /* r0 = 61 57 46 40 = R11 */ + "psllq $16, "r3"\n" /* r3 = 46 45 44 __ */ + "movq "r3", "r4"\n" /* r4 = 46 45 44 __ */ + "movq "r5", "r2"\n" /* r2 = 27 26 25 24 */ + "movq "r0", 112(%edx)\n" /* write R11 = r0 */ + "psrlq $48, "r2"\n" /* r2 = __ __ __ 27 */ + "pand "M(2)", "r4"\n" /* r4 = __ 45 __ __ */ + "por "r2", "r6"\n" /* r6 = 60 __ __ 27 */ + "movq "M(1)", "r2"\n" /* r2 = __ __ FF __ */ + "por "r4", "r6"\n" /* r6 = 60 45 __ 27 */ + "pand "r7", "r2"\n" /* r2 = __ __ 41 __ */ + "psllq $32, "r3"\n" /* r3 = 44 __ __ __ */ + "por 80(%edx), "r3"\n" /* r3 = 44 __ __ 23 */ + "por "r2", "r6"\n" /* r6 = 60 45 41 27 = R10 */ + "movq "M(3)", "r2"\n" /* r2 = FF __ __ __ */ + "psllq $16, "r5"\n" /* r5 = 26 25 24 __ */ + "movq "r6", 96(%edx)\n" /* store R10 = r6 */ + "pand "r5", "r2"\n" /* r2 = 26 __ __ __ */ + "movq "M(2)", "r6"\n" /* r6 = __ FF __ __ */ + "pxor "r2", "r5"\n" /* r5 = __ 25 24 __ */ + "pand "r7", "r6"\n" /* r6 = __ 42 __ __ */ + "psrlq $32, "r2"\n" /* r2 = __ __ 26 __ */ + "pand "M(3)", "r7"\n" /* r7 = 43 __ __ __ */ + "por "r2", "r3"\n" /* r3 = 44 __ 26 23 */ + "por 64(%edx), "r7"\n" /* r7 = 43 __ __ 12 */ + "por "r3", "r6"\n" /* r6 = 44 42 26 23 = R9 */ + "por "r5", "r7"\n" /* r7 = 43 25 24 12 = R8 */ + "movq "r6", 80(%edx)\n" /* store R9 = r6 */ + "movq "r7", 64(%edx)\n" /* store R8 = r7 */ + ); + /* 123c ( / 64 coeffs < 2c / coeff) */ +# undef M + +/* Done w/dequant + descramble + partial transpose; now do the idct itself. */ + +# define I( K) MtoSTR(K*16(%edx)) +# define J( K) MtoSTR(((K - 4)*16)+8(%edx)) + + RowIDCT /* 46 c */ + Transpose /* 19 c */ + +# undef I +# undef J +# define I( K) MtoSTR((K*16)+64(%edx)) +# define J( K) MtoSTR(((K-4)*16)+72(%edx)) + + RowIDCT /* 46 c */ + Transpose /* 19 c */ + +# undef I +# undef J +# define I( K) MtoSTR((K * 16)(%edx)) +# define J( K) I( K) + + ColumnIDCT /* 57 c */ + +# undef I +# undef J +# define I( K) MtoSTR((K*16)+8(%edx)) +# define J( K) I( K) + + ColumnIDCT /* 57 c */ + +# undef I +# undef J + /* 368 cycles ( / 64 coeff < 6 c / coeff) */ + + ASM("emms\n"); +} + +/************************************************************************************** + * + * Routine: MMX_idct10 + * + * Description: Perform IDCT on a 8x8 block with at most 10 nonzero coefficients + * + * Input: Pointer to input and output buffer + * + * Output: None + * + * Return: None + * + * Special Note: The input coefficients are in transposed ZigZag order + * + * Error: None + * + *************************************************************************************** + */ +/* --------------------------------------------------------------- */ +// This macro does four 4-sample one-dimensional idcts in parallel. Inputs +// 4 thru 7 are assumed to be zero. +#define BeginIDCT_10 "#BeginIDCT_10\n" \ + " movq "I(3)","r2"\n" \ + \ + " movq "C(3)","r6"\n" \ + " movq "r2","r4"\n" \ + \ + " movq "C(5)","r1"\n" \ + " pmulhw "r6","r4"\n" \ + \ + " movq "I(1)","r3"\n" \ + " pmulhw "r2","r1"\n" \ + \ + " movq "C(1)","r0"\n" \ + " paddw "r2","r4"\n" \ + \ + " pxor "r6","r6"\n" \ + " paddw "r1","r2"\n" \ + \ + " movq "I(2)","r5"\n" \ + " pmulhw "r3","r0"\n" \ + \ + " movq "r5","r1"\n" \ + " paddw "r3","r0"\n" \ + \ + " pmulhw "C(7)","r3"\n" \ + " psubsw "r2","r6"\n" \ + \ + " pmulhw "C(2)","r5"\n" \ + " psubsw "r4","r0"\n" \ + \ + " movq "I(2)","r7"\n" \ + " paddsw "r4","r4"\n" \ + \ + " paddw "r5","r7"\n" \ + " paddsw "r0","r4"\n" \ + \ + " pmulhw "C(6)","r1"\n" \ + " psubsw "r6","r3"\n" \ + \ + " movq "r4","I(1)"\n" \ + " paddsw "r6","r6"\n" \ + \ + " movq "C(4)","r4"\n" \ + " paddsw "r3","r6"\n" \ + \ + " movq "r3","r5"\n" \ + " pmulhw "r4","r3"\n" \ + \ + " movq "r6","I(2)"\n" \ + " movq "r0","r2"\n" \ + \ + " movq "I(0)","r6"\n" \ + " pmulhw "r4","r0"\n" \ + \ + " paddw "r3","r5"\n" \ + " paddw "r0","r2"\n" \ + \ + " psubsw "r1","r5"\n" \ + " pmulhw "r4","r6"\n" \ + \ + " paddw "I(0)","r6"\n" \ + " paddsw "r1","r1"\n" \ + \ + " movq "r6","r4"\n" \ + " paddsw "r5","r1"\n" \ + \ + " psubsw "r2","r6"\n" \ + " paddsw "r2","r2"\n" \ + \ + " movq "I(1)","r0"\n" \ + " paddsw "r6","r2"\n" \ + \ + " psubsw "r1","r2"\n" \ + "#end BeginIDCT_10\n" +// end BeginIDCT_10 macro (25 cycles). + +#define RowIDCT_10 ASM("\n" \ + "#RowIDCT_10\n" \ + BeginIDCT_10 \ + "\n" \ + " movq "I(2)","r3"\n" /* r3 = D. */ \ + " psubsw "r7","r4"\n" /* r4 = E. = E - G */ \ + " paddsw "r1","r1"\n" /* r1 = H. + H. */ \ + " paddsw "r7","r7"\n" /* r7 = G + G */ \ + " paddsw "r2","r1"\n" /* r1 = R1 = A.. + H. */\ + " paddsw "r4","r7"\n" /* r7 = G. = E + G */ \ + " psubsw "r3","r4"\n" /* r4 = R4 = E. - D. */ \ + " paddsw "r3","r3"\n" \ + " psubsw "r5","r6"\n" /* r6 = R6 = F. - B.. */\ + " paddsw "r5","r5"\n" \ + " paddsw "r4","r3"\n" /* r3 = R3 = E. + D. */ \ + " paddsw "r6","r5"\n" /* r5 = R5 = F. + B.. */\ + " psubsw "r0","r7"\n" /* r7 = R7 = G. - C. */ \ + " paddsw "r0","r0"\n" \ + " movq "r1","I(1)"\n" /* save R1 */ \ + " paddsw "r7","r0"\n" /* r0 = R0 = G. + C. */ \ + "#end RowIDCT_10\n" \ +); +// end RowIDCT macro (8 + 38 = 46 cycles) + +// Column IDCT normalizes and stores final results. + +#define ColumnIDCT_10 ASM("\n" \ + "#ColumnIDCT_10\n" \ + BeginIDCT_10 \ + "\n" \ + " paddsw "Eight","r2"\n" \ + " paddsw "r1","r1"\n" /* r1 = H. + H. */ \ + " paddsw "r2","r1"\n" /* r1 = R1 = A.. + H. */\ + " psraw ""$4"","r2"\n" /* r2 = NR2 */ \ + " psubsw "r7","r4"\n" /* r4 = E. = E - G */ \ + " psraw ""$4"","r1"\n" /* r1 = NR1 */ \ + " movq "I(2)","r3"\n" /* r3 = D. */ \ + " paddsw "r7","r7"\n" /* r7 = G + G */ \ + " movq "r2","I(2)"\n" /* store NR2 at I2 */ \ + " paddsw "r4","r7"\n" /* r7 = G. = E + G */ \ + " movq "r1","I(1)"\n" /* store NR1 at I1 */ \ + " psubsw "r3","r4"\n" /* r4 = R4 = E. - D. */ \ + " paddsw "Eight","r4"\n" \ + " paddsw "r3","r3"\n" /* r3 = D. + D. */ \ + " paddsw "r4","r3"\n" /* r3 = R3 = E. + D. */ \ + " psraw ""$4"","r4"\n" /* r4 = NR4 */ \ + " psubsw "r5","r6"\n" /* r6 = R6 = F. - B.. */\ + " psraw ""$4"","r3"\n" /* r3 = NR3 */ \ + " paddsw "Eight","r6"\n" \ + " paddsw "r5","r5"\n" /* r5 = B.. + B.. */ \ + " paddsw "r6","r5"\n" /* r5 = R5 = F. + B.. */\ + " psraw ""$4"","r6"\n" /* r6 = NR6 */ \ + " movq "r4","J(4)"\n" /* store NR4 at J4 */ \ + " psraw ""$4"","r5"\n" /* r5 = NR5 */ \ + " movq "r3","I(3)"\n" /* store NR3 at I3 */ \ + " psubsw "r0","r7"\n" /* r7 = R7 = G. - C. */ \ + " paddsw "Eight","r7"\n" \ + " paddsw "r0","r0"\n" /* r0 = C. + C. */ \ + " paddsw "r7","r0"\n" /* r0 = R0 = G. + C. */ \ + " psraw ""$4"","r7"\n" /* r7 = NR7 */ \ + " movq "r6","J(6)"\n" /* store NR6 at J6 */ \ + " psraw ""$4"","r0"\n" /* r0 = NR0 */ \ + " movq "r5","J(5)"\n" /* store NR5 at J5 */ \ + \ + " movq "r7","J(7)"\n" /* store NR7 at J7 */ \ + \ + " movq "r0","I(0)"\n" /* store NR0 at I0 */ \ + "#end ColumnIDCT_10\n" \ +); +// end ColumnIDCT macro (38 + 19 = 57 cycles) +/* --------------------------------------------------------------- */ + + +/* --------------------------------------------------------------- */ +/* IDCT 10 */ +void IDct10__mmx( Q_LIST_ENTRY * InputData, + ogg_int16_t *QuantMatrix, + ogg_int16_t * OutputData ) { + +# define MIDM(M,I) MtoSTR(M+I*8(%ecx)) +# define M(I) MIDM( MaskOffset , I ) +# define MIDC(M,I) MtoSTR(M+(I-1)*8(%ecx)) +# define C(I) MIDC( CosineOffset , I ) +# define MIDEight(M) MtoSTR(M(%ecx)) +# define Eight MIDEight(EightOffset) + +# define r0 "%mm0" +# define r1 "%mm1" +# define r2 "%mm2" +# define r3 "%mm3" +# define r4 "%mm4" +# define r5 "%mm5" +# define r6 "%mm6" +# define r7 "%mm7" + + __asm__ __volatile__ ( + /* eax = quantized input */ + /* esi = quantization table */ + /* edx = destination (= idct buffer) */ + /* ecx = idctconstants */ + "" + : + :"a"(InputData), "S"(QuantMatrix), "d"(OutputData), "c"(idctconstants) + ); + + ASM( + "movq (%eax), "r0"\n" + "pmullw (%esi), "r0"\n" /* r0 = 03 02 01 00 */ + "movq 16(%eax), "r1"\n" + "pmullw 16(%esi), "r1"\n" /* r1 = 13 12 11 10 */ + "movq "M(0)", "r2"\n" /* r2 = __ __ __ FF */ + "movq "r0", "r3"\n" /* r3 = 03 02 01 00 */ + "movq 8(%eax), "r4"\n" + "psrlq $16, "r0"\n" /* r0 = __ 03 02 01 */ + "pmullw 8(%esi), "r4"\n" /* r4 = 07 06 05 04 */ + "pand "r2", "r3"\n" /* r3 = __ __ __ 00 */ + "movq "r0", "r5"\n" /* r5 = __ 03 02 01 */ + "pand "r2", "r5"\n" /* r5 = __ __ __ 01 */ + "psllq $32, "r1"\n" /* r1 = 11 10 __ __ */ + "movq "M(3)", "r7"\n" /* r7 = FF __ __ __ */ + "pxor "r5", "r0"\n" /* r0 = __ 03 02 __ */ + "pand "r1", "r7"\n" /* r7 = 11 __ __ __ */ + "por "r3", "r0"\n" /* r0 = __ 03 02 00 */ + "pxor "r7", "r1"\n" /* r1 = __ 10 __ __ */ + "por "r7", "r0"\n" /* r0 = 11 03 02 00 = R0 */ + "movq "r4", "r3"\n" /* r3 = 07 06 05 04 */ + "movq "r0", (%edx)\n" /* write R0 = r0 */ + "pand "r2", "r3"\n" /* r3 = __ __ __ 04 */ + "psllq $16, "r3"\n" /* r3 = __ __ 04 __ */ + "por "r3", "r5"\n" /* r5 = __ __ 04 01 */ + "por "r5", "r1"\n" /* r1 = __ 10 04 01 = R1 */ + "psrlq $16, "r4"\n" /* r4 = __ 07 06 05 */ + "movq "r1", 16(%edx)\n" /* write R1 = r1 */ + "movq "r4", "r5"\n" /* r5 = __ 07 06 05 */ + "psrlq $16, "r4"\n" /* r4 = __ __ 07 06 */ + "movq "r2", "r6"\n" /* r6 = __ __ __ FF */ + "pand "r2", "r5"\n" /* r5 = __ __ __ 05 */ + "pand "r4", "r6"\n" /* r6 = __ __ __ 06 */ + "pxor "r6", "r4"\n" /* r4 = __ __ 07 __ */ + "por "r5", "r4"\n" /* r4 = __ __ 07 05 */ + "movq "r4", 32(%edx)\n" /* write R2 = r4 */ + "movq "r6", 48(%edx)\n" /* write R3 = r6 */ + ); +# undef M + +/* Done w/dequant + descramble + partial transpose; now do the idct itself. */ + +# define I( K) MtoSTR((K*16)(%edx)) +# define J( K) MtoSTR(((K - 4) * 16)+8(%edx)) + + RowIDCT_10 /* 33 c */ + Transpose /* 19 c */ + +# undef I +# undef J +//# define I( K) [edx + ( K * 16) + 64] +//# define J( K) [edx + ( (K - 4) * 16) + 72] + +// RowIDCT ; 46 c +// Transpose ; 19 c + +//# undef I +//# undef J +# define I( K) MtoSTR((K * 16)(%edx)) +# define J( K) I( K) + + ColumnIDCT_10 /* 44 c */ + +# undef I +# undef J +# define I( K) MtoSTR((K * 16)+8(%edx)) +# define J( K) I( K) + + ColumnIDCT_10 /* 44 c */ + +# undef I +# undef J + + ASM("emms\n"); +} + +/************************************************************************************** + * + * Routine: MMX_idct3 + * + * Description: Perform IDCT on a 8x8 block with at most 3 nonzero coefficients + * + * Input: Pointer to input and output buffer + * + * Output: None + * + * Return: None + * + * Special Note: Only works for three nonzero coefficients. + * + * Error: None + * + *************************************************************************************** + */ +/*************************************************************************************** + In IDCT 3, we are dealing with only three Non-Zero coefficients in the 8x8 block. + In the case that we work in the fashion RowIDCT -> ColumnIDCT, we only have to + do 1-D row idcts on the first two rows, the rest six rows remain zero anyway. + After row IDCTs, since every column could have nonzero coefficients, we need do + eight 1-D column IDCT. However, for each column, there are at most two nonzero + coefficients, coefficient 0 and coefficient 1. Same for the coefficents for the + two 1-d row idcts. For this reason, the process of a 1-D IDCT is simplified + + from a full version: + + A = (C1 * I1) + (C7 * I7) B = (C7 * I1) - (C1 * I7) + C = (C3 * I3) + (C5 * I5) D = (C3 * I5) - (C5 * I3) + A. = C4 * (A - C) B. = C4 * (B - D) + C. = A + C D. = B + D + + E = C4 * (I0 + I4) F = C4 * (I0 - I4) + G = (C2 * I2) + (C6 * I6) H = (C6 * I2) - (C2 * I6) + E. = E - G + G. = E + G + + A.. = F + A. B.. = B. - H + F. = F - A. H. = B. + H + + R0 = G. + C. R1 = A.. + H. R3 = E. + D. R5 = F. + B.. + R7 = G. - C. R2 = A.. - H. R4 = E. - D. R6 = F. - B.. + + To: + + + A = (C1 * I1) B = (C7 * I1) + C = 0 D = 0 + A. = C4 * A B. = C4 * B + C. = A D. = B + + E = C4 * I0 F = E + G = 0 H = 0 + E. = E + G. = E + + A.. = E + A. B.. = B. + F. = E - A. H. = B. + + R0 = E + A R1 = E + A. + B. R3 = E + B R5 = E - A. + B. + R7 = E - A R2 = E + A. - B. R4 = E - B R6 = F - A. - B. + +******************************************************************************************/ + +#define RowIDCT_3 ASM("\n"\ + "#RowIDCT_3\n"\ + " movq "I(1)","r7"\n" /* r7 = I1 */ \ + " movq "C(1)","r0"\n" /* r0 = C1 */ \ + " movq "C(7)","r3"\n" /* r3 = C7 */ \ + " pmulhw "r7","r0"\n" /* r0 = C1 * I1 - I1 */ \ + " pmulhw "r7","r3"\n" /* r3 = C7 * I1 = B, D. */ \ + " movq "I(0)","r6"\n" /* r6 = I0 */ \ + " movq "C(4)","r4"\n" /* r4 = C4 */ \ + " paddw "r7","r0"\n" /* r0 = C1 * I1 = A, C. */ \ + " movq "r6","r1"\n" /* make a copy of I0 */ \ + " pmulhw "r4","r6"\n" /* r2 = C4 * I0 - I0 */ \ + " movq "r0","r2"\n" /* make a copy of A */ \ + " movq "r3","r5"\n" /* make a copy of B */ \ + " pmulhw "r4","r2"\n" /* r2 = C4 * A - A */ \ + " pmulhw "r4","r5"\n" /* r5 = C4 * B - B */ \ + " paddw "r1","r6"\n" /* r2 = C4 * I0 = E, F */ \ + " movq "r6","r4"\n" /* r4 = E */ \ + " paddw "r0","r2"\n" /* r2 = A. */ \ + " paddw "r3","r5"\n" /* r5 = B. */ \ + " movq "r6","r7"\n" /* r7 = E */ \ + " movq "r5","r1"\n" /* r1 = B. */ \ + /* r0 = A */ \ + /* r3 = B */ \ + /* r2 = A. */ \ + /* r5 = B. */ \ + /* r6 = E */ \ + /* r4 = E */ \ + /* r7 = E */ \ + /* r1 = B. */ \ + " psubw "r2","r6"\n" /* r6 = E - A. */ \ + " psubw "r3","r4"\n" /* r4 = E - B ----R4 */ \ + " psubw "r0","r7"\n" /* r7 = E - A ----R7 */ \ + " paddw "r2","r2"\n" /* r2 = A. + A. */ \ + " paddw "r3","r3"\n" /* r3 = B + B */ \ + " paddw "r0","r0"\n" /* r0 = A + A */ \ + " paddw "r6","r2"\n" /* r2 = E + A. */ \ + " paddw "r4","r3"\n" /* r3 = E + B ----R3 */ \ + " psubw "r1","r2"\n" /* r2 = E + A. - B. ----R2 */ \ + " psubw "r5","r6"\n" /* r6 = E - A. - B. ----R6 */ \ + " paddw "r1","r1"\n" /* r1 = B. + B. */ \ + " paddw "r5","r5"\n" /* r5 = B. + B. */ \ + " paddw "r7","r0"\n" /* r0 = E + A ----R0 */ \ + " paddw "r2","r1"\n" /* r1 = E + A. + B. -----R1 */ \ + " movq "r1","I(1)"\n" /* save r1 */ \ + " paddw "r6","r5"\n" /* r5 = E - A. + B. -----R5 */ \ + "#end RowIDCT_3\n"\ +); +//End of RowIDCT_3 + +#define ColumnIDCT_3 ASM("\n"\ + "#ColumnIDCT_3\n"\ + " movq "I(1)","r7"\n" /* r7 = I1 */ \ + " movq "C(1)","r0"\n" /* r0 = C1 */ \ + " movq "C(7)","r3"\n" /* r3 = C7 */ \ + " pmulhw "r7","r0"\n" /* r0 = C1 * I1 - I1 */ \ + " pmulhw "r7","r3"\n" /* r3 = C7 * I1 = B, D. */ \ + " movq "I(0)","r6"\n" /* r6 = I0 */ \ + " movq "C(4)","r4"\n" /* r4 = C4 */ \ + " paddw "r7","r0"\n" /* r0 = C1 * I1 = A, C. */ \ + " movq "r6","r1"\n" /* make a copy of I0 */ \ + " pmulhw "r4","r6"\n" /* r2 = C4 * I0 - I0 */ \ + " movq "r0","r2"\n" /* make a copy of A */ \ + " movq "r3","r5"\n" /* make a copy of B */ \ + " pmulhw "r4","r2"\n" /* r2 = C4 * A - A */ \ + " pmulhw "r4","r5"\n" /* r5 = C4 * B - B */ \ + " paddw "r1","r6"\n" /* r2 = C4 * I0 = E, F */ \ + " movq "r6","r4"\n" /* r4 = E */ \ + " paddw "Eight","r6"\n" /* +8 for shift */ \ + " paddw "Eight","r4"\n" /* +8 for shift */ \ + " paddw "r0","r2"\n" /* r2 = A. */ \ + " paddw "r3","r5"\n" /* r5 = B. */ \ + " movq "r6","r7"\n" /* r7 = E */ \ + " movq "r5","r1"\n" /* r1 = B. */ \ +/* r0 = A */ \ +/* r3 = B */ \ +/* r2 = A. */ \ +/* r5 = B. */ \ +/* r6 = E */ \ +/* r4 = E */ \ +/* r7 = E */ \ +/* r1 = B. */ \ + " psubw "r2","r6"\n" /* r6 = E - A. */ \ + " psubw "r3","r4"\n" /* r4 = E - B ----R4 */ \ + " psubw "r0","r7"\n" /* r7 = E - A ----R7 */ \ + " paddw "r2","r2"\n" /* r2 = A. + A. */ \ + " paddw "r3","r3"\n" /* r3 = B + B */ \ + " paddw "r0","r0"\n" /* r0 = A + A */ \ + " paddw "r6","r2"\n" /* r2 = E + A. */ \ + " paddw "r4","r3"\n" /* r3 = E + B ----R3 */ \ + " psraw $4,"r4"\n" /* shift */ \ + " movq "r4","J(4)"\n" /* store R4 at J4 */ \ + " psraw $4,"r3"\n" /* shift */ \ + " movq "r3","I(3)"\n" /* store R3 at I3 */ \ + " psubw "r1","r2"\n" /* r2 = E + A. - B. ----R2 */ \ + " psubw "r5","r6"\n" /* r6 = E - A. - B. ----R6 */ \ + " paddw "r1","r1"\n" /* r1 = B. + B. */ \ + " paddw "r5","r5"\n" /* r5 = B. + B. */ \ + " paddw "r7","r0"\n" /* r0 = E + A ----R0 */ \ + " paddw "r2","r1"\n" /* r1 = E + A. + B. -----R1 */ \ + " psraw $4,"r7"\n" /* shift */ \ + " psraw $4,"r2"\n" /* shift */ \ + " psraw $4,"r0"\n" /* shift */ \ + " psraw $4,"r1"\n" /* shift */ \ + " movq "r7","J(7)"\n" /* store R7 to J7 */ \ + " movq "r0","I(0)"\n" /* store R0 to I0 */ \ + " movq "r1","I(1)"\n" /* store R1 to I1 */ \ + " movq "r2","I(2)"\n" /* store R2 to I2 */ \ + " movq "r1","I(1)"\n" /* save r1 */ \ + " paddw "r6","r5"\n" /* r5 = E - A. + B. -----R5 */ \ + " psraw $4,"r5"\n" /* shift */ \ + " movq "r5","J(5)"\n" /* store R5 at J5 */ \ + " psraw $4,"r6"\n" /* shift */ \ + " movq "r6","J(6)"\n" /* store R6 at J6 */ \ + "#end ColumnIDCT_3\n"\ +); +//End of ColumnIDCT_3 + +void IDct3__mmx( Q_LIST_ENTRY * InputData, + ogg_int16_t *QuantMatrix, + ogg_int16_t * OutputData ) { + +# define MIDM(M,I) MtoSTR(M+I*8(%ecx)) +# define M(I) MIDM( MaskOffset , I ) +# define MIDC(M,I) MtoSTR(M+(I-1)*8(%ecx)) +# define C(I) MIDC( CosineOffset , I ) +# define MIDEight(M) MtoSTR(M(%ecx)) +# define Eight MIDEight(EightOffset) + +# define r0 "%mm0" +# define r1 "%mm1" +# define r2 "%mm2" +# define r3 "%mm3" +# define r4 "%mm4" +# define r5 "%mm5" +# define r6 "%mm6" +# define r7 "%mm7" + + __asm__ __volatile__ ( + /* eax = quantized input */ + /* esi = quantization table */ + /* edx = destination (= idct buffer) */ + /* ecx = idctconstants */ + "" + : + :"a"(InputData), "S"(QuantMatrix), "d"(OutputData), "c"(idctconstants) + ); + + ASM( + "movq (%eax), "r0"\n" + "pmullw (%esi), "r0"\n" /* r0 = 03 02 01 00 */ + "movq "M(0)", "r2"\n" /* r2 = __ __ __ FF */ + "movq "r0", "r3"\n" /* r3 = 03 02 01 00 */ + "psrlq $16, "r0"\n" /* r0 = __ 03 02 01 */ + "pand "r2", "r3"\n" /* r3 = __ __ __ 00 */ + "movq "r0", "r5"\n" /* r5 = __ 03 02 01 */ + "pand "r2", "r5"\n" /* r5 = __ __ __ 01 */ + "pxor "r5", "r0"\n" /* r0 = __ 03 02 __ */ + "por "r3", "r0"\n" /* r0 = __ 03 02 00 */ + "movq "r0", (%edx)\n" /* write R0 = r0 */ + "movq "r5", 16(%edx)\n" /* write R1 = r5 */ + ); +# undef M + +/* Done partial transpose; now do the idct itself. */ + +# define I( K) MtoSTR(K*16(%edx)) +# define J( K) MtoSTR(((K - 4)*16)+8(%edx)) + + RowIDCT_3 /* 33 c */ + Transpose /* 19 c */ + +# undef I +# undef J +//# define I( K) [edx + ( K * 16) + 64] +//# define J( K) [edx + ( (K - 4) * 16) + 72] + +// RowIDCT ; 46 c +// Transpose ; 19 c + +//# undef I +//# undef J +# define I( K) MtoSTR((K * 16)(%edx)) +# define J( K) I( K) + + ColumnIDCT_3 /* 44 c */ + +# undef I +# undef J +# define I( K) MtoSTR((K*16)+8(%edx)) +# define J( K) I( K) + + ColumnIDCT_3 /* 44 c */ + +# undef I +# undef J + + ASM("emms\n"); +} + + +/* install our implementation in the function table */ +void dsp_mmx_idct_init(DspFunctions *funcs) +{ + funcs->IDctSlow = IDctSlow__mmx; + funcs->IDct10 = IDct10__mmx; + funcs->IDct3 = IDct3__mmx; +} + +#endif /* USE_ASM */ diff --git a/Engine/lib/libtheora/lib/enc/x86_32/recon_mmx.c b/Engine/lib/libtheora/lib/enc/x86_32/recon_mmx.c new file mode 100644 index 000000000..7a931afe4 --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/x86_32/recon_mmx.c @@ -0,0 +1,182 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: recon_mmx.c 15153 2008-08-04 18:37:55Z tterribe $ + + ********************************************************************/ + +#include "../codec_internal.h" + +#if defined(USE_ASM) + +static const __attribute__ ((aligned(8),used)) ogg_int64_t V128 = 0x8080808080808080LL; + +static void copy8x8__mmx (unsigned char *src, + unsigned char *dest, + unsigned int stride) +{ + __asm__ __volatile__ ( + " .p2align 4 \n\t" + + " lea (%2, %2, 2), %%edi \n\t" + + " movq (%1), %%mm0 \n\t" + " movq (%1, %2), %%mm1 \n\t" + " movq (%1, %2, 2), %%mm2 \n\t" + " movq (%1, %%edi), %%mm3 \n\t" + + " lea (%1, %2, 4), %1 \n\t" + + " movq %%mm0, (%0) \n\t" + " movq %%mm1, (%0, %2) \n\t" + " movq %%mm2, (%0, %2, 2) \n\t" + " movq %%mm3, (%0, %%edi) \n\t" + + " lea (%0, %2, 4), %0 \n\t" + + " movq (%1), %%mm0 \n\t" + " movq (%1, %2), %%mm1 \n\t" + " movq (%1, %2, 2), %%mm2 \n\t" + " movq (%1, %%edi), %%mm3 \n\t" + + " movq %%mm0, (%0) \n\t" + " movq %%mm1, (%0, %2) \n\t" + " movq %%mm2, (%0, %2, 2) \n\t" + " movq %%mm3, (%0, %%edi) \n\t" + : "+a" (dest) + : "c" (src), + "d" (stride) + : "memory", "edi" + ); +} + +static void recon_intra8x8__mmx (unsigned char *ReconPtr, ogg_int16_t *ChangePtr, + ogg_uint32_t LineStep) +{ + __asm__ __volatile__ ( + " .p2align 4 \n\t" + + " movq %[V128], %%mm0 \n\t" /* Set mm0 to 0x8080808080808080 */ + + " lea 128(%1), %%edi \n\t" /* Endpoint in input buffer */ + "1: \n\t" + " movq (%1), %%mm2 \n\t" /* First four input values */ + + " packsswb 8(%1), %%mm2 \n\t" /* pack with next(high) four values */ + " por %%mm0, %%mm0 \n\t" + " pxor %%mm0, %%mm2 \n\t" /* Convert result to unsigned (same as add 128) */ + " lea 16(%1), %1 \n\t" /* Step source buffer */ + " cmp %%edi, %1 \n\t" /* are we done */ + + " movq %%mm2, (%0) \n\t" /* store results */ + + " lea (%0, %2), %0 \n\t" /* Step output buffer */ + " jc 1b \n\t" /* Loop back if we are not done */ + : "+r" (ReconPtr) + : "r" (ChangePtr), + "r" (LineStep), + [V128] "m" (V128) + : "memory", "edi" + ); +} + +static void recon_inter8x8__mmx (unsigned char *ReconPtr, unsigned char *RefPtr, + ogg_int16_t *ChangePtr, ogg_uint32_t LineStep) +{ + __asm__ __volatile__ ( + " .p2align 4 \n\t" + + " pxor %%mm0, %%mm0 \n\t" + " lea 128(%1), %%edi \n\t" + + "1: \n\t" + " movq (%2), %%mm2 \n\t" /* (+3 misaligned) 8 reference pixels */ + + " movq (%1), %%mm4 \n\t" /* first 4 changes */ + " movq %%mm2, %%mm3 \n\t" + " movq 8(%1), %%mm5 \n\t" /* last 4 changes */ + " punpcklbw %%mm0, %%mm2 \n\t" /* turn first 4 refs into positive 16-bit #s */ + " paddsw %%mm4, %%mm2 \n\t" /* add in first 4 changes */ + " punpckhbw %%mm0, %%mm3 \n\t" /* turn last 4 refs into positive 16-bit #s */ + " paddsw %%mm5, %%mm3 \n\t" /* add in last 4 changes */ + " add %3, %2 \n\t" /* next row of reference pixels */ + " packuswb %%mm3, %%mm2 \n\t" /* pack result to unsigned 8-bit values */ + " lea 16(%1), %1 \n\t" /* next row of changes */ + " cmp %%edi, %1 \n\t" /* are we done? */ + + " movq %%mm2, (%0) \n\t" /* store result */ + + " lea (%0, %3), %0 \n\t" /* next row of output */ + " jc 1b \n\t" + : "+r" (ReconPtr) + : "r" (ChangePtr), + "r" (RefPtr), + "r" (LineStep) + : "memory", "edi" + ); +} + +static void recon_inter8x8_half__mmx (unsigned char *ReconPtr, unsigned char *RefPtr1, + unsigned char *RefPtr2, ogg_int16_t *ChangePtr, + ogg_uint32_t LineStep) +{ + __asm__ __volatile__ ( + " .p2align 4 \n\t" + + " pxor %%mm0, %%mm0 \n\t" + " lea 128(%1), %%edi \n\t" + + "1: \n\t" + " movq (%2), %%mm2 \n\t" /* (+3 misaligned) 8 reference pixels */ + " movq (%3), %%mm4 \n\t" /* (+3 misaligned) 8 reference pixels */ + + " movq %%mm2, %%mm3 \n\t" + " punpcklbw %%mm0, %%mm2 \n\t" /* mm2 = start ref1 as positive 16-bit #s */ + " movq %%mm4, %%mm5 \n\t" + " movq (%1), %%mm6 \n\t" /* first 4 changes */ + " punpckhbw %%mm0, %%mm3 \n\t" /* mm3 = end ref1 as positive 16-bit #s */ + " movq 8(%1), %%mm7 \n\t" /* last 4 changes */ + " punpcklbw %%mm0, %%mm4 \n\t" /* mm4 = start ref2 as positive 16-bit #s */ + " punpckhbw %%mm0, %%mm5 \n\t" /* mm5 = end ref2 as positive 16-bit #s */ + " paddw %%mm4, %%mm2 \n\t" /* mm2 = start (ref1 + ref2) */ + " paddw %%mm5, %%mm3 \n\t" /* mm3 = end (ref1 + ref2) */ + " psrlw $1, %%mm2 \n\t" /* mm2 = start (ref1 + ref2)/2 */ + " psrlw $1, %%mm3 \n\t" /* mm3 = end (ref1 + ref2)/2 */ + " paddw %%mm6, %%mm2 \n\t" /* add changes to start */ + " paddw %%mm7, %%mm3 \n\t" /* add changes to end */ + " lea 16(%1), %1 \n\t" /* next row of changes */ + " packuswb %%mm3, %%mm2 \n\t" /* pack start|end to unsigned 8-bit */ + " add %4, %2 \n\t" /* next row of reference pixels */ + " add %4, %3 \n\t" /* next row of reference pixels */ + " movq %%mm2, (%0) \n\t" /* store result */ + " add %4, %0 \n\t" /* next row of output */ + " cmp %%edi, %1 \n\t" /* are we done? */ + " jc 1b \n\t" + : "+r" (ReconPtr) + : "r" (ChangePtr), + "r" (RefPtr1), + "r" (RefPtr2), + "m" (LineStep) + : "memory", "edi" + ); +} + +void dsp_mmx_recon_init(DspFunctions *funcs) +{ + funcs->copy8x8 = copy8x8__mmx; + funcs->recon_intra8x8 = recon_intra8x8__mmx; + funcs->recon_inter8x8 = recon_inter8x8__mmx; + funcs->recon_inter8x8_half = recon_inter8x8_half__mmx; +} + +#endif /* USE_ASM */ diff --git a/Engine/lib/libtheora/lib/enc/x86_32_vs/dsp_mmx.c b/Engine/lib/libtheora/lib/enc/x86_32_vs/dsp_mmx.c new file mode 100644 index 000000000..cecc0eb76 --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/x86_32_vs/dsp_mmx.c @@ -0,0 +1,1605 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: mcomp.c,v 1.8 2003/12/03 08:59:41 arc Exp $ + + ********************************************************************/ + +#include + +#include "../codec_internal.h" +#include "../dsp.h" + +#if 0 +//These are to let me selectively enable the C versions, these are needed +#define DSP_OP_AVG(a,b) ((((int)(a)) + ((int)(b)))/2) +#define DSP_OP_DIFF(a,b) (((int)(a)) - ((int)(b))) +#define DSP_OP_ABS_DIFF(a,b) abs((((int)(a)) - ((int)(b)))) +#endif + + +static const ogg_int64_t V128 = 0x0080008000800080; + +static void sub8x8__mmx (unsigned char *FiltPtr, unsigned char *ReconPtr, + ogg_int16_t *DctInputPtr, ogg_uint32_t PixelsPerLine, + ogg_uint32_t ReconPixelsPerLine) +{ + + //Make non-zero to use the C-version +#if 0 + int i; + + /* For each block row */ + for (i=8; i; i--) { + DctInputPtr[0] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[0], ReconPtr[0]); + DctInputPtr[1] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[1], ReconPtr[1]); + DctInputPtr[2] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[2], ReconPtr[2]); + DctInputPtr[3] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[3], ReconPtr[3]); + DctInputPtr[4] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[4], ReconPtr[4]); + DctInputPtr[5] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[5], ReconPtr[5]); + DctInputPtr[6] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[6], ReconPtr[6]); + DctInputPtr[7] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[7], ReconPtr[7]); + + /* Start next row */ + FiltPtr += PixelsPerLine; + ReconPtr += ReconPixelsPerLine; + DctInputPtr += 8; + } +#else + __asm { + align 16 + + pxor mm7, mm7 + + mov eax, FiltPtr + mov ebx, ReconPtr + mov edx, DctInputPtr + + /* You can't use rept in inline masm and macro parsing seems screwed with inline asm*/ + + /* ITERATION 1 */ + movq mm0, [eax] /* mm0 = FiltPtr */ + movq mm1, [ebx] /* mm1 = ReconPtr */ + movq mm2, mm0 /* dup to prepare for up conversion */ + movq mm3, mm1 /* dup to prepare for up conversion */ + /* convert from UINT8 to INT16 */ + punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ + punpcklbw mm1, mm7 /* mm1 = INT16(ReconPtr) */ + punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ + punpckhbw mm3, mm7 /* mm3 = INT16(ReconPtr) */ + /* start calculation */ + psubw mm0, mm1 /* mm0 = FiltPtr - ReconPtr */ + psubw mm2, mm3 /* mm2 = FiltPtr - ReconPtr */ + movq [edx], mm0 /* write answer out */ + movq [8 + edx], mm2 /* write answer out */ + /* Increment pointers */ + add edx, 16 + add eax, PixelsPerLine + add ebx, ReconPixelsPerLine + + + /* ITERATION 2 */ + movq mm0, [eax] /* mm0 = FiltPtr */ + movq mm1, [ebx] /* mm1 = ReconPtr */ + movq mm2, mm0 /* dup to prepare for up conversion */ + movq mm3, mm1 /* dup to prepare for up conversion */ + /* convert from UINT8 to INT16 */ + punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ + punpcklbw mm1, mm7 /* mm1 = INT16(ReconPtr) */ + punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ + punpckhbw mm3, mm7 /* mm3 = INT16(ReconPtr) */ + /* start calculation */ + psubw mm0, mm1 /* mm0 = FiltPtr - ReconPtr */ + psubw mm2, mm3 /* mm2 = FiltPtr - ReconPtr */ + movq [edx], mm0 /* write answer out */ + movq [8 + edx], mm2 /* write answer out */ + /* Increment pointers */ + add edx, 16 + add eax, PixelsPerLine + add ebx, ReconPixelsPerLine + + + /* ITERATION 3 */ + movq mm0, [eax] /* mm0 = FiltPtr */ + movq mm1, [ebx] /* mm1 = ReconPtr */ + movq mm2, mm0 /* dup to prepare for up conversion */ + movq mm3, mm1 /* dup to prepare for up conversion */ + /* convert from UINT8 to INT16 */ + punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ + punpcklbw mm1, mm7 /* mm1 = INT16(ReconPtr) */ + punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ + punpckhbw mm3, mm7 /* mm3 = INT16(ReconPtr) */ + /* start calculation */ + psubw mm0, mm1 /* mm0 = FiltPtr - ReconPtr */ + psubw mm2, mm3 /* mm2 = FiltPtr - ReconPtr */ + movq [edx], mm0 /* write answer out */ + movq [8 + edx], mm2 /* write answer out */ + /* Increment pointers */ + add edx, 16 + add eax, PixelsPerLine + add ebx, ReconPixelsPerLine + + + /* ITERATION 4 */ + movq mm0, [eax] /* mm0 = FiltPtr */ + movq mm1, [ebx] /* mm1 = ReconPtr */ + movq mm2, mm0 /* dup to prepare for up conversion */ + movq mm3, mm1 /* dup to prepare for up conversion */ + /* convert from UINT8 to INT16 */ + punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ + punpcklbw mm1, mm7 /* mm1 = INT16(ReconPtr) */ + punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ + punpckhbw mm3, mm7 /* mm3 = INT16(ReconPtr) */ + /* start calculation */ + psubw mm0, mm1 /* mm0 = FiltPtr - ReconPtr */ + psubw mm2, mm3 /* mm2 = FiltPtr - ReconPtr */ + movq [edx], mm0 /* write answer out */ + movq [8 + edx], mm2 /* write answer out */ + /* Increment pointers */ + add edx, 16 + add eax, PixelsPerLine + add ebx, ReconPixelsPerLine + + + /* ITERATION 5 */ + movq mm0, [eax] /* mm0 = FiltPtr */ + movq mm1, [ebx] /* mm1 = ReconPtr */ + movq mm2, mm0 /* dup to prepare for up conversion */ + movq mm3, mm1 /* dup to prepare for up conversion */ + /* convert from UINT8 to INT16 */ + punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ + punpcklbw mm1, mm7 /* mm1 = INT16(ReconPtr) */ + punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ + punpckhbw mm3, mm7 /* mm3 = INT16(ReconPtr) */ + /* start calculation */ + psubw mm0, mm1 /* mm0 = FiltPtr - ReconPtr */ + psubw mm2, mm3 /* mm2 = FiltPtr - ReconPtr */ + movq [edx], mm0 /* write answer out */ + movq [8 + edx], mm2 /* write answer out */ + /* Increment pointers */ + add edx, 16 + add eax, PixelsPerLine + add ebx, ReconPixelsPerLine + + + /* ITERATION 6 */ + movq mm0, [eax] /* mm0 = FiltPtr */ + movq mm1, [ebx] /* mm1 = ReconPtr */ + movq mm2, mm0 /* dup to prepare for up conversion */ + movq mm3, mm1 /* dup to prepare for up conversion */ + /* convert from UINT8 to INT16 */ + punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ + punpcklbw mm1, mm7 /* mm1 = INT16(ReconPtr) */ + punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ + punpckhbw mm3, mm7 /* mm3 = INT16(ReconPtr) */ + /* start calculation */ + psubw mm0, mm1 /* mm0 = FiltPtr - ReconPtr */ + psubw mm2, mm3 /* mm2 = FiltPtr - ReconPtr */ + movq [edx], mm0 /* write answer out */ + movq [8 + edx], mm2 /* write answer out */ + /* Increment pointers */ + add edx, 16 + add eax, PixelsPerLine + add ebx, ReconPixelsPerLine + + + /* ITERATION 7 */ + movq mm0, [eax] /* mm0 = FiltPtr */ + movq mm1, [ebx] /* mm1 = ReconPtr */ + movq mm2, mm0 /* dup to prepare for up conversion */ + movq mm3, mm1 /* dup to prepare for up conversion */ + /* convert from UINT8 to INT16 */ + punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ + punpcklbw mm1, mm7 /* mm1 = INT16(ReconPtr) */ + punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ + punpckhbw mm3, mm7 /* mm3 = INT16(ReconPtr) */ + /* start calculation */ + psubw mm0, mm1 /* mm0 = FiltPtr - ReconPtr */ + psubw mm2, mm3 /* mm2 = FiltPtr - ReconPtr */ + movq [edx], mm0 /* write answer out */ + movq [8 + edx], mm2 /* write answer out */ + /* Increment pointers */ + add edx, 16 + add eax, PixelsPerLine + add ebx, ReconPixelsPerLine + + + /* ITERATION 8 */ + movq mm0, [eax] /* mm0 = FiltPtr */ + movq mm1, [ebx] /* mm1 = ReconPtr */ + movq mm2, mm0 /* dup to prepare for up conversion */ + movq mm3, mm1 /* dup to prepare for up conversion */ + /* convert from UINT8 to INT16 */ + punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ + punpcklbw mm1, mm7 /* mm1 = INT16(ReconPtr) */ + punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ + punpckhbw mm3, mm7 /* mm3 = INT16(ReconPtr) */ + /* start calculation */ + psubw mm0, mm1 /* mm0 = FiltPtr - ReconPtr */ + psubw mm2, mm3 /* mm2 = FiltPtr - ReconPtr */ + movq [edx], mm0 /* write answer out */ + movq [8 + edx], mm2 /* write answer out */ + /* Increment pointers */ + add edx, 16 + add eax, PixelsPerLine + add ebx, ReconPixelsPerLine + + + + + + }; + +#endif +} + +static void sub8x8_128__mmx (unsigned char *FiltPtr, ogg_int16_t *DctInputPtr, + ogg_uint32_t PixelsPerLine) +{ + +#if 0 + int i; + /* For each block row */ + for (i=8; i; i--) { + /* INTRA mode so code raw image data */ + /* We convert the data to 8 bit signed (by subtracting 128) as + this reduces the internal precision requirments in the DCT + transform. */ + DctInputPtr[0] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[0], 128); + DctInputPtr[1] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[1], 128); + DctInputPtr[2] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[2], 128); + DctInputPtr[3] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[3], 128); + DctInputPtr[4] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[4], 128); + DctInputPtr[5] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[5], 128); + DctInputPtr[6] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[6], 128); + DctInputPtr[7] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[7], 128); + + /* Start next row */ + FiltPtr += PixelsPerLine; + DctInputPtr += 8; + } + +#else + __asm { + align 16 + + pxor mm7, mm7 + + mov eax, FiltPtr + mov ebx, DctInputPtr + + movq mm1, V128 + + /* ITERATION 1 */ + movq mm0, [eax] /* mm0 = FiltPtr */ + movq mm2, mm0 /* dup to prepare for up conversion */ + /* convert from UINT8 to INT16 */ + punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ + punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ + /* start calculation */ + psubw mm0, mm1 /* mm0 = FiltPtr - 128 */ + psubw mm2, mm1 /* mm2 = FiltPtr - 128 */ + movq [ebx], mm0 /* write answer out */ + movq [8 + ebx], mm2 /* write answer out */ + /* Increment pointers */ + add ebx, 16 + add eax, PixelsPerLine + + + /* ITERATION 2 */ + movq mm0, [eax] /* mm0 = FiltPtr */ + movq mm2, mm0 /* dup to prepare for up conversion */ + /* convert from UINT8 to INT16 */ + punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ + punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ + /* start calculation */ + psubw mm0, mm1 /* mm0 = FiltPtr - 128 */ + psubw mm2, mm1 /* mm2 = FiltPtr - 128 */ + movq [ebx], mm0 /* write answer out */ + movq [8 + ebx], mm2 /* write answer out */ + /* Increment pointers */ + add ebx, 16 + add eax, PixelsPerLine + + + /* ITERATION 3 */ + movq mm0, [eax] /* mm0 = FiltPtr */ + movq mm2, mm0 /* dup to prepare for up conversion */ + /* convert from UINT8 to INT16 */ + punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ + punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ + /* start calculation */ + psubw mm0, mm1 /* mm0 = FiltPtr - 128 */ + psubw mm2, mm1 /* mm2 = FiltPtr - 128 */ + movq [ebx], mm0 /* write answer out */ + movq [8 + ebx], mm2 /* write answer out */ + /* Increment pointers */ + add ebx, 16 + add eax, PixelsPerLine + + + /* ITERATION 4 */ + movq mm0, [eax] /* mm0 = FiltPtr */ + movq mm2, mm0 /* dup to prepare for up conversion */ + /* convert from UINT8 to INT16 */ + punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ + punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ + /* start calculation */ + psubw mm0, mm1 /* mm0 = FiltPtr - 128 */ + psubw mm2, mm1 /* mm2 = FiltPtr - 128 */ + movq [ebx], mm0 /* write answer out */ + movq [8 + ebx], mm2 /* write answer out */ + /* Increment pointers */ + add ebx, 16 + add eax, PixelsPerLine + + + /* ITERATION 5 */ + movq mm0, [eax] /* mm0 = FiltPtr */ + movq mm2, mm0 /* dup to prepare for up conversion */ + /* convert from UINT8 to INT16 */ + punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ + punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ + /* start calculation */ + psubw mm0, mm1 /* mm0 = FiltPtr - 128 */ + psubw mm2, mm1 /* mm2 = FiltPtr - 128 */ + movq [ebx], mm0 /* write answer out */ + movq [8 + ebx], mm2 /* write answer out */ + /* Increment pointers */ + add ebx, 16 + add eax, PixelsPerLine + + + /* ITERATION 6 */ + movq mm0, [eax] /* mm0 = FiltPtr */ + movq mm2, mm0 /* dup to prepare for up conversion */ + /* convert from UINT8 to INT16 */ + punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ + punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ + /* start calculation */ + psubw mm0, mm1 /* mm0 = FiltPtr - 128 */ + psubw mm2, mm1 /* mm2 = FiltPtr - 128 */ + movq [ebx], mm0 /* write answer out */ + movq [8 + ebx], mm2 /* write answer out */ + /* Increment pointers */ + add ebx, 16 + add eax, PixelsPerLine + + + /* ITERATION 7 */ + movq mm0, [eax] /* mm0 = FiltPtr */ + movq mm2, mm0 /* dup to prepare for up conversion */ + /* convert from UINT8 to INT16 */ + punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ + punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ + /* start calculation */ + psubw mm0, mm1 /* mm0 = FiltPtr - 128 */ + psubw mm2, mm1 /* mm2 = FiltPtr - 128 */ + movq [ebx], mm0 /* write answer out */ + movq [8 + ebx], mm2 /* write answer out */ + /* Increment pointers */ + add ebx, 16 + add eax, PixelsPerLine + + + /* ITERATION 8 */ + movq mm0, [eax] /* mm0 = FiltPtr */ + movq mm2, mm0 /* dup to prepare for up conversion */ + /* convert from UINT8 to INT16 */ + punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ + punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ + /* start calculation */ + psubw mm0, mm1 /* mm0 = FiltPtr - 128 */ + psubw mm2, mm1 /* mm2 = FiltPtr - 128 */ + movq [ebx], mm0 /* write answer out */ + movq [8 + ebx], mm2 /* write answer out */ + /* Increment pointers */ + add ebx, 16 + add eax, PixelsPerLine + + }; + +#endif +} + + + + +static void sub8x8avg2__mmx (unsigned char *FiltPtr, unsigned char *ReconPtr1, + unsigned char *ReconPtr2, ogg_int16_t *DctInputPtr, + ogg_uint32_t PixelsPerLine, + ogg_uint32_t ReconPixelsPerLine) +{ + +#if 0 + int i; + + /* For each block row */ + for (i=8; i; i--) { + DctInputPtr[0] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[0], DSP_OP_AVG (ReconPtr1[0], ReconPtr2[0])); + DctInputPtr[1] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[1], DSP_OP_AVG (ReconPtr1[1], ReconPtr2[1])); + DctInputPtr[2] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[2], DSP_OP_AVG (ReconPtr1[2], ReconPtr2[2])); + DctInputPtr[3] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[3], DSP_OP_AVG (ReconPtr1[3], ReconPtr2[3])); + DctInputPtr[4] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[4], DSP_OP_AVG (ReconPtr1[4], ReconPtr2[4])); + DctInputPtr[5] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[5], DSP_OP_AVG (ReconPtr1[5], ReconPtr2[5])); + DctInputPtr[6] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[6], DSP_OP_AVG (ReconPtr1[6], ReconPtr2[6])); + DctInputPtr[7] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[7], DSP_OP_AVG (ReconPtr1[7], ReconPtr2[7])); + + /* Start next row */ + FiltPtr += PixelsPerLine; + ReconPtr1 += ReconPixelsPerLine; + ReconPtr2 += ReconPixelsPerLine; + DctInputPtr += 8; + } +#else + + __asm { + align 16 + + pxor mm7, mm7 + + mov eax, FiltPtr + mov ebx, ReconPtr1 + mov ecx, ReconPtr2 + mov edx, DctInputPtr + + /* ITERATION 1 */ + movq mm0, [eax] ; /* mm0 = FiltPtr */ + movq mm1, [ebx] ; /* mm1 = ReconPtr1 */ + movq mm4, [ecx] ; /* mm1 = ReconPtr2 */ + movq mm2, mm0 ; /* dup to prepare for up conversion */ + movq mm3, mm1 ; /* dup to prepare for up conversion */ + movq mm5, mm4 ; /* dup to prepare for up conversion */ + ; /* convert from UINT8 to INT16 */ + punpcklbw mm0, mm7 ; /* mm0 = INT16(FiltPtr) */ + punpcklbw mm1, mm7 ; /* mm1 = INT16(ReconPtr1) */ + punpcklbw mm4, mm7 ; /* mm1 = INT16(ReconPtr2) */ + punpckhbw mm2, mm7 ; /* mm2 = INT16(FiltPtr) */ + punpckhbw mm3, mm7 ; /* mm3 = INT16(ReconPtr1) */ + punpckhbw mm5, mm7 ; /* mm3 = INT16(ReconPtr2) */ + ; /* average ReconPtr1 and ReconPtr2 */ + paddw mm1, mm4 ; /* mm1 = ReconPtr1 + ReconPtr2 */ + paddw mm3, mm5 ; /* mm3 = ReconPtr1 + ReconPtr2 */ + psrlw mm1, 1 ; /* mm1 = (ReconPtr1 + ReconPtr2) / 2 */ + psrlw mm3, 1 ; /* mm3 = (ReconPtr1 + ReconPtr2) / 2 */ + psubw mm0, mm1 ; /* mm0 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ + psubw mm2, mm3 ; /* mm2 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ + movq [edx], mm0 ; /* write answer out */ + movq [8 + edx], mm2 ; /* write answer out */ + ; /* Increment pointers */ + add edx, 16 ; + add eax, PixelsPerLine ; + add ebx, ReconPixelsPerLine ; + add ecx, ReconPixelsPerLine ; + + + /* ITERATION 2 */ + movq mm0, [eax] ; /* mm0 = FiltPtr */ + movq mm1, [ebx] ; /* mm1 = ReconPtr1 */ + movq mm4, [ecx] ; /* mm1 = ReconPtr2 */ + movq mm2, mm0 ; /* dup to prepare for up conversion */ + movq mm3, mm1 ; /* dup to prepare for up conversion */ + movq mm5, mm4 ; /* dup to prepare for up conversion */ + ; /* convert from UINT8 to INT16 */ + punpcklbw mm0, mm7 ; /* mm0 = INT16(FiltPtr) */ + punpcklbw mm1, mm7 ; /* mm1 = INT16(ReconPtr1) */ + punpcklbw mm4, mm7 ; /* mm1 = INT16(ReconPtr2) */ + punpckhbw mm2, mm7 ; /* mm2 = INT16(FiltPtr) */ + punpckhbw mm3, mm7 ; /* mm3 = INT16(ReconPtr1) */ + punpckhbw mm5, mm7 ; /* mm3 = INT16(ReconPtr2) */ + ; /* average ReconPtr1 and ReconPtr2 */ + paddw mm1, mm4 ; /* mm1 = ReconPtr1 + ReconPtr2 */ + paddw mm3, mm5 ; /* mm3 = ReconPtr1 + ReconPtr2 */ + psrlw mm1, 1 ; /* mm1 = (ReconPtr1 + ReconPtr2) / 2 */ + psrlw mm3, 1 ; /* mm3 = (ReconPtr1 + ReconPtr2) / 2 */ + psubw mm0, mm1 ; /* mm0 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ + psubw mm2, mm3 ; /* mm2 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ + movq [edx], mm0 ; /* write answer out */ + movq [8 + edx], mm2 ; /* write answer out */ + ; /* Increment pointers */ + add edx, 16 ; + add eax, PixelsPerLine ; + add ebx, ReconPixelsPerLine ; + add ecx, ReconPixelsPerLine ; + + + /* ITERATION 3 */ + movq mm0, [eax] ; /* mm0 = FiltPtr */ + movq mm1, [ebx] ; /* mm1 = ReconPtr1 */ + movq mm4, [ecx] ; /* mm1 = ReconPtr2 */ + movq mm2, mm0 ; /* dup to prepare for up conversion */ + movq mm3, mm1 ; /* dup to prepare for up conversion */ + movq mm5, mm4 ; /* dup to prepare for up conversion */ + ; /* convert from UINT8 to INT16 */ + punpcklbw mm0, mm7 ; /* mm0 = INT16(FiltPtr) */ + punpcklbw mm1, mm7 ; /* mm1 = INT16(ReconPtr1) */ + punpcklbw mm4, mm7 ; /* mm1 = INT16(ReconPtr2) */ + punpckhbw mm2, mm7 ; /* mm2 = INT16(FiltPtr) */ + punpckhbw mm3, mm7 ; /* mm3 = INT16(ReconPtr1) */ + punpckhbw mm5, mm7 ; /* mm3 = INT16(ReconPtr2) */ + ; /* average ReconPtr1 and ReconPtr2 */ + paddw mm1, mm4 ; /* mm1 = ReconPtr1 + ReconPtr2 */ + paddw mm3, mm5 ; /* mm3 = ReconPtr1 + ReconPtr2 */ + psrlw mm1, 1 ; /* mm1 = (ReconPtr1 + ReconPtr2) / 2 */ + psrlw mm3, 1 ; /* mm3 = (ReconPtr1 + ReconPtr2) / 2 */ + psubw mm0, mm1 ; /* mm0 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ + psubw mm2, mm3 ; /* mm2 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ + movq [edx], mm0 ; /* write answer out */ + movq [8 + edx], mm2 ; /* write answer out */ + ; /* Increment pointers */ + add edx, 16 ; + add eax, PixelsPerLine ; + add ebx, ReconPixelsPerLine ; + add ecx, ReconPixelsPerLine ; + + + /* ITERATION 4 */ + movq mm0, [eax] ; /* mm0 = FiltPtr */ + movq mm1, [ebx] ; /* mm1 = ReconPtr1 */ + movq mm4, [ecx] ; /* mm1 = ReconPtr2 */ + movq mm2, mm0 ; /* dup to prepare for up conversion */ + movq mm3, mm1 ; /* dup to prepare for up conversion */ + movq mm5, mm4 ; /* dup to prepare for up conversion */ + ; /* convert from UINT8 to INT16 */ + punpcklbw mm0, mm7 ; /* mm0 = INT16(FiltPtr) */ + punpcklbw mm1, mm7 ; /* mm1 = INT16(ReconPtr1) */ + punpcklbw mm4, mm7 ; /* mm1 = INT16(ReconPtr2) */ + punpckhbw mm2, mm7 ; /* mm2 = INT16(FiltPtr) */ + punpckhbw mm3, mm7 ; /* mm3 = INT16(ReconPtr1) */ + punpckhbw mm5, mm7 ; /* mm3 = INT16(ReconPtr2) */ + ; /* average ReconPtr1 and ReconPtr2 */ + paddw mm1, mm4 ; /* mm1 = ReconPtr1 + ReconPtr2 */ + paddw mm3, mm5 ; /* mm3 = ReconPtr1 + ReconPtr2 */ + psrlw mm1, 1 ; /* mm1 = (ReconPtr1 + ReconPtr2) / 2 */ + psrlw mm3, 1 ; /* mm3 = (ReconPtr1 + ReconPtr2) / 2 */ + psubw mm0, mm1 ; /* mm0 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ + psubw mm2, mm3 ; /* mm2 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ + movq [edx], mm0 ; /* write answer out */ + movq [8 + edx], mm2 ; /* write answer out */ + ; /* Increment pointers */ + add edx, 16 ; + add eax, PixelsPerLine ; + add ebx, ReconPixelsPerLine ; + add ecx, ReconPixelsPerLine ; + + + /* ITERATION 5 */ + movq mm0, [eax] ; /* mm0 = FiltPtr */ + movq mm1, [ebx] ; /* mm1 = ReconPtr1 */ + movq mm4, [ecx] ; /* mm1 = ReconPtr2 */ + movq mm2, mm0 ; /* dup to prepare for up conversion */ + movq mm3, mm1 ; /* dup to prepare for up conversion */ + movq mm5, mm4 ; /* dup to prepare for up conversion */ + ; /* convert from UINT8 to INT16 */ + punpcklbw mm0, mm7 ; /* mm0 = INT16(FiltPtr) */ + punpcklbw mm1, mm7 ; /* mm1 = INT16(ReconPtr1) */ + punpcklbw mm4, mm7 ; /* mm1 = INT16(ReconPtr2) */ + punpckhbw mm2, mm7 ; /* mm2 = INT16(FiltPtr) */ + punpckhbw mm3, mm7 ; /* mm3 = INT16(ReconPtr1) */ + punpckhbw mm5, mm7 ; /* mm3 = INT16(ReconPtr2) */ + ; /* average ReconPtr1 and ReconPtr2 */ + paddw mm1, mm4 ; /* mm1 = ReconPtr1 + ReconPtr2 */ + paddw mm3, mm5 ; /* mm3 = ReconPtr1 + ReconPtr2 */ + psrlw mm1, 1 ; /* mm1 = (ReconPtr1 + ReconPtr2) / 2 */ + psrlw mm3, 1 ; /* mm3 = (ReconPtr1 + ReconPtr2) / 2 */ + psubw mm0, mm1 ; /* mm0 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ + psubw mm2, mm3 ; /* mm2 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ + movq [edx], mm0 ; /* write answer out */ + movq [8 + edx], mm2 ; /* write answer out */ + ; /* Increment pointers */ + add edx, 16 ; + add eax, PixelsPerLine ; + add ebx, ReconPixelsPerLine ; + add ecx, ReconPixelsPerLine ; + + + /* ITERATION 6 */ + movq mm0, [eax] ; /* mm0 = FiltPtr */ + movq mm1, [ebx] ; /* mm1 = ReconPtr1 */ + movq mm4, [ecx] ; /* mm1 = ReconPtr2 */ + movq mm2, mm0 ; /* dup to prepare for up conversion */ + movq mm3, mm1 ; /* dup to prepare for up conversion */ + movq mm5, mm4 ; /* dup to prepare for up conversion */ + ; /* convert from UINT8 to INT16 */ + punpcklbw mm0, mm7 ; /* mm0 = INT16(FiltPtr) */ + punpcklbw mm1, mm7 ; /* mm1 = INT16(ReconPtr1) */ + punpcklbw mm4, mm7 ; /* mm1 = INT16(ReconPtr2) */ + punpckhbw mm2, mm7 ; /* mm2 = INT16(FiltPtr) */ + punpckhbw mm3, mm7 ; /* mm3 = INT16(ReconPtr1) */ + punpckhbw mm5, mm7 ; /* mm3 = INT16(ReconPtr2) */ + ; /* average ReconPtr1 and ReconPtr2 */ + paddw mm1, mm4 ; /* mm1 = ReconPtr1 + ReconPtr2 */ + paddw mm3, mm5 ; /* mm3 = ReconPtr1 + ReconPtr2 */ + psrlw mm1, 1 ; /* mm1 = (ReconPtr1 + ReconPtr2) / 2 */ + psrlw mm3, 1 ; /* mm3 = (ReconPtr1 + ReconPtr2) / 2 */ + psubw mm0, mm1 ; /* mm0 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ + psubw mm2, mm3 ; /* mm2 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ + movq [edx], mm0 ; /* write answer out */ + movq [8 + edx], mm2 ; /* write answer out */ + ; /* Increment pointers */ + add edx, 16 ; + add eax, PixelsPerLine ; + add ebx, ReconPixelsPerLine ; + add ecx, ReconPixelsPerLine ; + + + /* ITERATION 7 */ + movq mm0, [eax] ; /* mm0 = FiltPtr */ + movq mm1, [ebx] ; /* mm1 = ReconPtr1 */ + movq mm4, [ecx] ; /* mm1 = ReconPtr2 */ + movq mm2, mm0 ; /* dup to prepare for up conversion */ + movq mm3, mm1 ; /* dup to prepare for up conversion */ + movq mm5, mm4 ; /* dup to prepare for up conversion */ + ; /* convert from UINT8 to INT16 */ + punpcklbw mm0, mm7 ; /* mm0 = INT16(FiltPtr) */ + punpcklbw mm1, mm7 ; /* mm1 = INT16(ReconPtr1) */ + punpcklbw mm4, mm7 ; /* mm1 = INT16(ReconPtr2) */ + punpckhbw mm2, mm7 ; /* mm2 = INT16(FiltPtr) */ + punpckhbw mm3, mm7 ; /* mm3 = INT16(ReconPtr1) */ + punpckhbw mm5, mm7 ; /* mm3 = INT16(ReconPtr2) */ + ; /* average ReconPtr1 and ReconPtr2 */ + paddw mm1, mm4 ; /* mm1 = ReconPtr1 + ReconPtr2 */ + paddw mm3, mm5 ; /* mm3 = ReconPtr1 + ReconPtr2 */ + psrlw mm1, 1 ; /* mm1 = (ReconPtr1 + ReconPtr2) / 2 */ + psrlw mm3, 1 ; /* mm3 = (ReconPtr1 + ReconPtr2) / 2 */ + psubw mm0, mm1 ; /* mm0 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ + psubw mm2, mm3 ; /* mm2 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ + movq [edx], mm0 ; /* write answer out */ + movq [8 + edx], mm2 ; /* write answer out */ + ; /* Increment pointers */ + add edx, 16 ; + add eax, PixelsPerLine ; + add ebx, ReconPixelsPerLine ; + add ecx, ReconPixelsPerLine ; + + + /* ITERATION 8 */ + movq mm0, [eax] ; /* mm0 = FiltPtr */ + movq mm1, [ebx] ; /* mm1 = ReconPtr1 */ + movq mm4, [ecx] ; /* mm1 = ReconPtr2 */ + movq mm2, mm0 ; /* dup to prepare for up conversion */ + movq mm3, mm1 ; /* dup to prepare for up conversion */ + movq mm5, mm4 ; /* dup to prepare for up conversion */ + ; /* convert from UINT8 to INT16 */ + punpcklbw mm0, mm7 ; /* mm0 = INT16(FiltPtr) */ + punpcklbw mm1, mm7 ; /* mm1 = INT16(ReconPtr1) */ + punpcklbw mm4, mm7 ; /* mm1 = INT16(ReconPtr2) */ + punpckhbw mm2, mm7 ; /* mm2 = INT16(FiltPtr) */ + punpckhbw mm3, mm7 ; /* mm3 = INT16(ReconPtr1) */ + punpckhbw mm5, mm7 ; /* mm3 = INT16(ReconPtr2) */ + ; /* average ReconPtr1 and ReconPtr2 */ + paddw mm1, mm4 ; /* mm1 = ReconPtr1 + ReconPtr2 */ + paddw mm3, mm5 ; /* mm3 = ReconPtr1 + ReconPtr2 */ + psrlw mm1, 1 ; /* mm1 = (ReconPtr1 + ReconPtr2) / 2 */ + psrlw mm3, 1 ; /* mm3 = (ReconPtr1 + ReconPtr2) / 2 */ + psubw mm0, mm1 ; /* mm0 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ + psubw mm2, mm3 ; /* mm2 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ + movq [edx], mm0 ; /* write answer out */ + movq [8 + edx], mm2 ; /* write answer out */ + ; /* Increment pointers */ + add edx, 16 ; + add eax, PixelsPerLine ; + add ebx, ReconPixelsPerLine ; + add ecx, ReconPixelsPerLine ; + + }; + + + + + +#endif +} + +static ogg_uint32_t row_sad8__mmx (unsigned char *Src1, unsigned char *Src2) +{ + +#if 0 + ogg_uint32_t SadValue; + ogg_uint32_t SadValue1; + + SadValue = DSP_OP_ABS_DIFF (Src1[0], Src2[0]) + + DSP_OP_ABS_DIFF (Src1[1], Src2[1]) + + DSP_OP_ABS_DIFF (Src1[2], Src2[2]) + + DSP_OP_ABS_DIFF (Src1[3], Src2[3]); + + SadValue1 = DSP_OP_ABS_DIFF (Src1[4], Src2[4]) + + DSP_OP_ABS_DIFF (Src1[5], Src2[5]) + + DSP_OP_ABS_DIFF (Src1[6], Src2[6]) + + DSP_OP_ABS_DIFF (Src1[7], Src2[7]); + + SadValue = ( SadValue > SadValue1 ) ? SadValue : SadValue1; + + return SadValue; + +#else + ogg_uint32_t MaxSad; + + + __asm { + align 16 + mov ebx, Src1 + mov ecx, Src2 + + + pxor mm6, mm6 ; /* zero out mm6 for unpack */ + pxor mm7, mm7 ; /* zero out mm7 for unpack */ + movq mm0, [ebx] ; /* take 8 bytes */ + movq mm1, [ecx] ; + + movq mm2, mm0 ; + psubusb mm0, mm1 ; /* A - B */ + psubusb mm1, mm2 ; /* B - A */ + por mm0, mm1 ; /* and or gives abs difference */ + + movq mm1, mm0 ; + + punpcklbw mm0, mm6 ; /* ; unpack low four bytes to higher precision */ + punpckhbw mm1, mm7 ; /* ; unpack high four bytes to higher precision */ + + movq mm2, mm0 ; + movq mm3, mm1 ; + psrlq mm2, 32 ; /* fold and add */ + psrlq mm3, 32 ; + paddw mm0, mm2 ; + paddw mm1, mm3 ; + movq mm2, mm0 ; + movq mm3, mm1 ; + psrlq mm2, 16 ; + psrlq mm3, 16 ; + paddw mm0, mm2 ; + paddw mm1, mm3 ; + + psubusw mm1, mm0 ; + paddw mm1, mm0 ; /* mm1 = max(mm1, mm0) */ + movd eax, mm1 ; + + and eax, 0xffff + mov MaxSad, eax + }; + return MaxSad; + + + + + +#endif +} + + + + +static ogg_uint32_t col_sad8x8__mmx (unsigned char *Src1, unsigned char *Src2, + ogg_uint32_t stride) +{ + +#if 0 + ogg_uint32_t SadValue[8] = {0,0,0,0,0,0,0,0}; + ogg_uint32_t SadValue2[8] = {0,0,0,0,0,0,0,0}; + ogg_uint32_t MaxSad = 0; + ogg_uint32_t i; + + for ( i = 0; i < 4; i++ ){ + SadValue[0] += abs(Src1[0] - Src2[0]); + SadValue[1] += abs(Src1[1] - Src2[1]); + SadValue[2] += abs(Src1[2] - Src2[2]); + SadValue[3] += abs(Src1[3] - Src2[3]); + SadValue[4] += abs(Src1[4] - Src2[4]); + SadValue[5] += abs(Src1[5] - Src2[5]); + SadValue[6] += abs(Src1[6] - Src2[6]); + SadValue[7] += abs(Src1[7] - Src2[7]); + + Src1 += stride; + Src2 += stride; + } + + for ( i = 0; i < 4; i++ ){ + SadValue2[0] += abs(Src1[0] - Src2[0]); + SadValue2[1] += abs(Src1[1] - Src2[1]); + SadValue2[2] += abs(Src1[2] - Src2[2]); + SadValue2[3] += abs(Src1[3] - Src2[3]); + SadValue2[4] += abs(Src1[4] - Src2[4]); + SadValue2[5] += abs(Src1[5] - Src2[5]); + SadValue2[6] += abs(Src1[6] - Src2[6]); + SadValue2[7] += abs(Src1[7] - Src2[7]); + + Src1 += stride; + Src2 += stride; + } + + for ( i = 0; i < 8; i++ ){ + if ( SadValue[i] > MaxSad ) + MaxSad = SadValue[i]; + if ( SadValue2[i] > MaxSad ) + MaxSad = SadValue2[i]; + } + + return MaxSad; +#else + ogg_uint32_t MaxSad; + + + __asm { + align 16 + mov ebx, Src1 + mov ecx, Src2 + + pxor mm3, mm3 ; /* zero out mm3 for unpack */ + pxor mm4, mm4 ; /* mm4 low sum */ + pxor mm5, mm5 ; /* mm5 high sum */ + pxor mm6, mm6 ; /* mm6 low sum */ + pxor mm7, mm7 ; /* mm7 high sum */ + mov edi, 4 ; /* 4 rows */ + label_1: ; + movq mm0, [ebx] ; /* take 8 bytes */ + movq mm1, [ecx] ; /* take 8 bytes */ + + movq mm2, mm0 ; + psubusb mm0, mm1 ; /* A - B */ + psubusb mm1, mm2 ; /* B - A */ + por mm0, mm1 ; /* and or gives abs difference */ + movq mm1, mm0 ; + + punpcklbw mm0, mm3 ; /* unpack to higher precision for accumulation */ + paddw mm4, mm0 ; /* accumulate difference... */ + punpckhbw mm1, mm3 ; /* unpack high four bytes to higher precision */ + paddw mm5, mm1 ; /* accumulate difference... */ + add ebx, stride ; /* Inc pointer into the new data */ + add ecx, stride ; /* Inc pointer into the new data */ + + dec edi ; + jnz label_1 ; + + mov edi, 4 ; /* 4 rows */ + label_2: ; + movq mm0, [ebx] ; /* take 8 bytes */ + movq mm1, [ecx] ; /* take 8 bytes */ + + movq mm2, mm0 ; + psubusb mm0, mm1 ; /* A - B */ + psubusb mm1, mm2 ; /* B - A */ + por mm0, mm1 ; /* and or gives abs difference */ + movq mm1, mm0 ; + + punpcklbw mm0, mm3 ; /* unpack to higher precision for accumulation */ + paddw mm6, mm0 ; /* accumulate difference... */ + punpckhbw mm1, mm3 ; /* unpack high four bytes to higher precision */ + paddw mm7, mm1 ; /* accumulate difference... */ + add ebx, stride ; /* Inc pointer into the new data */ + add ecx, stride ; /* Inc pointer into the new data */ + + dec edi ; + jnz label_2 ; + + psubusw mm7, mm6 ; + paddw mm7, mm6 ; /* mm7 = max(mm7, mm6) */ + psubusw mm5, mm4 ; + paddw mm5, mm4 ; /* mm5 = max(mm5, mm4) */ + psubusw mm7, mm5 ; + paddw mm7, mm5 ; /* mm7 = max(mm5, mm7) */ + movq mm6, mm7 ; + psrlq mm6, 32 ; + psubusw mm7, mm6 ; + paddw mm7, mm6 ; /* mm7 = max(mm5, mm7) */ + movq mm6, mm7 ; + psrlq mm6, 16 ; + psubusw mm7, mm6 ; + paddw mm7, mm6 ; /* mm7 = max(mm5, mm7) */ + movd eax, mm7 ; + and eax, 0xffff ; + + mov MaxSad, eax + }; + + return MaxSad; + + +#endif +} + +static ogg_uint32_t sad8x8__mmx (unsigned char *ptr1, ogg_uint32_t stride1, + unsigned char *ptr2, ogg_uint32_t stride2) +{ + +#if 0 + ogg_uint32_t i; + ogg_uint32_t sad = 0; + + for (i=8; i; i--) { + sad += DSP_OP_ABS_DIFF(ptr1[0], ptr2[0]); + sad += DSP_OP_ABS_DIFF(ptr1[1], ptr2[1]); + sad += DSP_OP_ABS_DIFF(ptr1[2], ptr2[2]); + sad += DSP_OP_ABS_DIFF(ptr1[3], ptr2[3]); + sad += DSP_OP_ABS_DIFF(ptr1[4], ptr2[4]); + sad += DSP_OP_ABS_DIFF(ptr1[5], ptr2[5]); + sad += DSP_OP_ABS_DIFF(ptr1[6], ptr2[6]); + sad += DSP_OP_ABS_DIFF(ptr1[7], ptr2[7]); + + /* Step to next row of block. */ + ptr1 += stride1; + ptr2 += stride2; + } + + return sad; +#else + ogg_uint32_t DiffVal; + + __asm { + align 16 + + mov ebx, ptr1 + mov edx, ptr2 + + pxor mm6, mm6 ; /* zero out mm6 for unpack */ + pxor mm7, mm7 ; /* mm7 contains the result */ + + ; /* ITERATION 1 */ + movq mm0, [ebx] ; /* take 8 bytes */ + movq mm1, [edx] ; + movq mm2, mm0 ; + + psubusb mm0, mm1 ; /* A - B */ + psubusb mm1, mm2 ; /* B - A */ + por mm0, mm1 ; /* and or gives abs difference */ + movq mm1, mm0 ; + + punpcklbw mm0, mm6 ; /* unpack to higher precision for accumulation */ + paddw mm7, mm0 ; /* accumulate difference... */ + punpckhbw mm1, mm6 ; /* unpack high four bytes to higher precision */ + add ebx, stride1 ; /* Inc pointer into the new data */ + paddw mm7, mm1 ; /* accumulate difference... */ + add edx, stride2 ; /* Inc pointer into ref data */ + + ; /* ITERATION 2 */ + movq mm0, [ebx] ; /* take 8 bytes */ + movq mm1, [edx] ; + movq mm2, mm0 ; + + psubusb mm0, mm1 ; /* A - B */ + psubusb mm1, mm2 ; /* B - A */ + por mm0, mm1 ; /* and or gives abs difference */ + movq mm1, mm0 ; + + punpcklbw mm0, mm6 ; /* unpack to higher precision for accumulation */ + paddw mm7, mm0 ; /* accumulate difference... */ + punpckhbw mm1, mm6 ; /* unpack high four bytes to higher precision */ + add ebx, stride1 ; /* Inc pointer into the new data */ + paddw mm7, mm1 ; /* accumulate difference... */ + add edx, stride2 ; /* Inc pointer into ref data */ + + + ; /* ITERATION 3 */ + movq mm0, [ebx] ; /* take 8 bytes */ + movq mm1, [edx] ; + movq mm2, mm0 ; + + psubusb mm0, mm1 ; /* A - B */ + psubusb mm1, mm2 ; /* B - A */ + por mm0, mm1 ; /* and or gives abs difference */ + movq mm1, mm0 ; + + punpcklbw mm0, mm6 ; /* unpack to higher precision for accumulation */ + paddw mm7, mm0 ; /* accumulate difference... */ + punpckhbw mm1, mm6 ; /* unpack high four bytes to higher precision */ + add ebx, stride1 ; /* Inc pointer into the new data */ + paddw mm7, mm1 ; /* accumulate difference... */ + add edx, stride2 ; /* Inc pointer into ref data */ + + ; /* ITERATION 4 */ + movq mm0, [ebx] ; /* take 8 bytes */ + movq mm1, [edx] ; + movq mm2, mm0 ; + + psubusb mm0, mm1 ; /* A - B */ + psubusb mm1, mm2 ; /* B - A */ + por mm0, mm1 ; /* and or gives abs difference */ + movq mm1, mm0 ; + + punpcklbw mm0, mm6 ; /* unpack to higher precision for accumulation */ + paddw mm7, mm0 ; /* accumulate difference... */ + punpckhbw mm1, mm6 ; /* unpack high four bytes to higher precision */ + add ebx, stride1 ; /* Inc pointer into the new data */ + paddw mm7, mm1 ; /* accumulate difference... */ + add edx, stride2 ; /* Inc pointer into ref data */ + + + ; /* ITERATION 5 */ + movq mm0, [ebx] ; /* take 8 bytes */ + movq mm1, [edx] ; + movq mm2, mm0 ; + + psubusb mm0, mm1 ; /* A - B */ + psubusb mm1, mm2 ; /* B - A */ + por mm0, mm1 ; /* and or gives abs difference */ + movq mm1, mm0 ; + + punpcklbw mm0, mm6 ; /* unpack to higher precision for accumulation */ + paddw mm7, mm0 ; /* accumulate difference... */ + punpckhbw mm1, mm6 ; /* unpack high four bytes to higher precision */ + add ebx, stride1 ; /* Inc pointer into the new data */ + paddw mm7, mm1 ; /* accumulate difference... */ + add edx, stride2 ; /* Inc pointer into ref data */ + + + ; /* ITERATION 6 */ + movq mm0, [ebx] ; /* take 8 bytes */ + movq mm1, [edx] ; + movq mm2, mm0 ; + + psubusb mm0, mm1 ; /* A - B */ + psubusb mm1, mm2 ; /* B - A */ + por mm0, mm1 ; /* and or gives abs difference */ + movq mm1, mm0 ; + + punpcklbw mm0, mm6 ; /* unpack to higher precision for accumulation */ + paddw mm7, mm0 ; /* accumulate difference... */ + punpckhbw mm1, mm6 ; /* unpack high four bytes to higher precision */ + add ebx, stride1 ; /* Inc pointer into the new data */ + paddw mm7, mm1 ; /* accumulate difference... */ + add edx, stride2 ; /* Inc pointer into ref data */ + + + ; /* ITERATION 7 */ + movq mm0, [ebx] ; /* take 8 bytes */ + movq mm1, [edx] ; + movq mm2, mm0 ; + + psubusb mm0, mm1 ; /* A - B */ + psubusb mm1, mm2 ; /* B - A */ + por mm0, mm1 ; /* and or gives abs difference */ + movq mm1, mm0 ; + + punpcklbw mm0, mm6 ; /* unpack to higher precision for accumulation */ + paddw mm7, mm0 ; /* accumulate difference... */ + punpckhbw mm1, mm6 ; /* unpack high four bytes to higher precision */ + add ebx, stride1 ; /* Inc pointer into the new data */ + paddw mm7, mm1 ; /* accumulate difference... */ + add edx, stride2 ; /* Inc pointer into ref data */ + + + + ; /* ITERATION 8 */ + movq mm0, [ebx] ; /* take 8 bytes */ + movq mm1, [edx] ; + movq mm2, mm0 ; + + psubusb mm0, mm1 ; /* A - B */ + psubusb mm1, mm2 ; /* B - A */ + por mm0, mm1 ; /* and or gives abs difference */ + movq mm1, mm0 ; + + punpcklbw mm0, mm6 ; /* unpack to higher precision for accumulation */ + paddw mm7, mm0 ; /* accumulate difference... */ + punpckhbw mm1, mm6 ; /* unpack high four bytes to higher precision */ + add ebx, stride1 ; /* Inc pointer into the new data */ + paddw mm7, mm1 ; /* accumulate difference... */ + add edx, stride2 ; /* Inc pointer into ref data */ + + + + ; /* ------ */ + + movq mm0, mm7 ; + psrlq mm7, 32 ; + paddw mm7, mm0 ; + movq mm0, mm7 ; + psrlq mm7, 16 ; + paddw mm7, mm0 ; + movd eax, mm7 ; + and eax, 0xffff ; + + mov DiffVal, eax + }; + + return DiffVal; + + + +#endif +} + +static ogg_uint32_t sad8x8_thres__mmx (unsigned char *ptr1, ogg_uint32_t stride1, + unsigned char *ptr2, ogg_uint32_t stride2, + ogg_uint32_t thres) +{ +#if 0 + ogg_uint32_t i; + ogg_uint32_t sad = 0; + + for (i=8; i; i--) { + sad += DSP_OP_ABS_DIFF(ptr1[0], ptr2[0]); + sad += DSP_OP_ABS_DIFF(ptr1[1], ptr2[1]); + sad += DSP_OP_ABS_DIFF(ptr1[2], ptr2[2]); + sad += DSP_OP_ABS_DIFF(ptr1[3], ptr2[3]); + sad += DSP_OP_ABS_DIFF(ptr1[4], ptr2[4]); + sad += DSP_OP_ABS_DIFF(ptr1[5], ptr2[5]); + sad += DSP_OP_ABS_DIFF(ptr1[6], ptr2[6]); + sad += DSP_OP_ABS_DIFF(ptr1[7], ptr2[7]); + + if (sad > thres ) + break; + + /* Step to next row of block. */ + ptr1 += stride1; + ptr2 += stride2; + } + + return sad; +#else + return sad8x8__mmx (ptr1, stride1, ptr2, stride2); +#endif +} + + +static ogg_uint32_t sad8x8_xy2_thres__mmx (unsigned char *SrcData, ogg_uint32_t SrcStride, + unsigned char *RefDataPtr1, + unsigned char *RefDataPtr2, ogg_uint32_t RefStride, + ogg_uint32_t thres) +{ +#if 0 + ogg_uint32_t i; + ogg_uint32_t sad = 0; + + for (i=8; i; i--) { + sad += DSP_OP_ABS_DIFF(SrcData[0], DSP_OP_AVG (RefDataPtr1[0], RefDataPtr2[0])); + sad += DSP_OP_ABS_DIFF(SrcData[1], DSP_OP_AVG (RefDataPtr1[1], RefDataPtr2[1])); + sad += DSP_OP_ABS_DIFF(SrcData[2], DSP_OP_AVG (RefDataPtr1[2], RefDataPtr2[2])); + sad += DSP_OP_ABS_DIFF(SrcData[3], DSP_OP_AVG (RefDataPtr1[3], RefDataPtr2[3])); + sad += DSP_OP_ABS_DIFF(SrcData[4], DSP_OP_AVG (RefDataPtr1[4], RefDataPtr2[4])); + sad += DSP_OP_ABS_DIFF(SrcData[5], DSP_OP_AVG (RefDataPtr1[5], RefDataPtr2[5])); + sad += DSP_OP_ABS_DIFF(SrcData[6], DSP_OP_AVG (RefDataPtr1[6], RefDataPtr2[6])); + sad += DSP_OP_ABS_DIFF(SrcData[7], DSP_OP_AVG (RefDataPtr1[7], RefDataPtr2[7])); + + if ( sad > thres ) + break; + + /* Step to next row of block. */ + SrcData += SrcStride; + RefDataPtr1 += RefStride; + RefDataPtr2 += RefStride; + } + + return sad; +#else + ogg_uint32_t DiffVal; + + __asm { + align 16 + + mov ebx, SrcData + mov ecx, RefDataPtr1 + mov edx, RefDataPtr2 + + + pcmpeqd mm5, mm5 ; /* fefefefefefefefe in mm5 */ + paddb mm5, mm5 ; + ; + pxor mm6, mm6 ; /* zero out mm6 for unpack */ + pxor mm7, mm7 ; /* mm7 contains the result */ + mov edi, 8 ; /* 8 rows */ + loop_start: ; + movq mm0, [ebx] ; /* take 8 bytes */ + + movq mm2, [ecx] ; + movq mm3, [edx] ; /* take average of mm2 and mm3 */ + movq mm1, mm2 ; + pand mm1, mm3 ; + pxor mm3, mm2 ; + pand mm3, mm5 ; + psrlq mm3, 1 ; + paddb mm1, mm3 ; + + movq mm2, mm0 ; + + psubusb mm0, mm1 ; /* A - B */ + psubusb mm1, mm2 ; /* B - A */ + por mm0, mm1 ; /* and or gives abs difference */ + movq mm1, mm0 ; + + punpcklbw mm0, mm6 ; /* unpack to higher precision for accumulation */ + paddw mm7, mm0 ; /* accumulate difference... */ + punpckhbw mm1, mm6 ; /* unpack high four bytes to higher precision */ + add ebx, SrcStride ; /* Inc pointer into the new data */ + paddw mm7, mm1 ; /* accumulate difference... */ + add ecx, RefStride ; /* Inc pointer into ref data */ + add edx, RefStride ; /* Inc pointer into ref data */ + + dec edi ; + jnz loop_start ; + + movq mm0, mm7 ; + psrlq mm7, 32 ; + paddw mm7, mm0 ; + movq mm0, mm7 ; + psrlq mm7, 16 ; + paddw mm7, mm0 ; + movd eax, mm7 ; + and eax, 0xffff ; + + mov DiffVal, eax + }; + + return DiffVal; + + + +#endif +} + +static ogg_uint32_t intra8x8_err__mmx (unsigned char *DataPtr, ogg_uint32_t Stride) +{ +#if 0 + ogg_uint32_t i; + ogg_uint32_t XSum=0; + ogg_uint32_t XXSum=0; + + for (i=8; i; i--) { + /* Examine alternate pixel locations. */ + XSum += DataPtr[0]; + XXSum += DataPtr[0]*DataPtr[0]; + XSum += DataPtr[1]; + XXSum += DataPtr[1]*DataPtr[1]; + XSum += DataPtr[2]; + XXSum += DataPtr[2]*DataPtr[2]; + XSum += DataPtr[3]; + XXSum += DataPtr[3]*DataPtr[3]; + XSum += DataPtr[4]; + XXSum += DataPtr[4]*DataPtr[4]; + XSum += DataPtr[5]; + XXSum += DataPtr[5]*DataPtr[5]; + XSum += DataPtr[6]; + XXSum += DataPtr[6]*DataPtr[6]; + XSum += DataPtr[7]; + XXSum += DataPtr[7]*DataPtr[7]; + + /* Step to next row of block. */ + DataPtr += Stride; + } + + /* Compute population variance as mis-match metric. */ + return (( (XXSum<<6) - XSum*XSum ) ); +#else + ogg_uint32_t XSum; + ogg_uint32_t XXSum; + + __asm { + align 16 + + mov ecx, DataPtr + + pxor mm5, mm5 ; + pxor mm6, mm6 ; + pxor mm7, mm7 ; + mov edi, 8 ; + loop_start: + movq mm0, [ecx] ; /* take 8 bytes */ + movq mm2, mm0 ; + + punpcklbw mm0, mm6 ; + punpckhbw mm2, mm6 ; + + paddw mm5, mm0 ; + paddw mm5, mm2 ; + + pmaddwd mm0, mm0 ; + pmaddwd mm2, mm2 ; + ; + paddd mm7, mm0 ; + paddd mm7, mm2 ; + + add ecx, Stride ; /* Inc pointer into src data */ + + dec edi ; + jnz loop_start ; + + movq mm0, mm5 ; + psrlq mm5, 32 ; + paddw mm5, mm0 ; + movq mm0, mm5 ; + psrlq mm5, 16 ; + paddw mm5, mm0 ; + movd edi, mm5 ; + movsx edi, di ; + mov eax, edi ; + + movq mm0, mm7 ; + psrlq mm7, 32 ; + paddd mm7, mm0 ; + movd ebx, mm7 ; + + mov XSum, eax + mov XXSum, ebx; + + }; + /* Compute population variance as mis-match metric. */ + return (( (XXSum<<6) - XSum*XSum ) ); + + + +#endif +} + +static ogg_uint32_t inter8x8_err__mmx (unsigned char *SrcData, ogg_uint32_t SrcStride, + unsigned char *RefDataPtr, ogg_uint32_t RefStride) +{ + +#if 0 + ogg_uint32_t i; + ogg_uint32_t XSum=0; + ogg_uint32_t XXSum=0; + ogg_int32_t DiffVal; + + for (i=8; i; i--) { + DiffVal = DSP_OP_DIFF (SrcData[0], RefDataPtr[0]); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF (SrcData[1], RefDataPtr[1]); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF (SrcData[2], RefDataPtr[2]); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF (SrcData[3], RefDataPtr[3]); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF (SrcData[4], RefDataPtr[4]); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF (SrcData[5], RefDataPtr[5]); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF (SrcData[6], RefDataPtr[6]); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF (SrcData[7], RefDataPtr[7]); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + /* Step to next row of block. */ + SrcData += SrcStride; + RefDataPtr += RefStride; + } + + /* Compute and return population variance as mis-match metric. */ + return (( (XXSum<<6) - XSum*XSum )); +#else + ogg_uint32_t XSum; + ogg_uint32_t XXSum; + + + __asm { + align 16 + + mov ecx, SrcData + mov edx, RefDataPtr + + pxor mm5, mm5 ; + pxor mm6, mm6 ; + pxor mm7, mm7 ; + mov edi, 8 ; + loop_start: ; + movq mm0, [ecx] ; /* take 8 bytes */ + movq mm1, [edx] ; + movq mm2, mm0 ; + movq mm3, mm1 ; + + punpcklbw mm0, mm6 ; + punpcklbw mm1, mm6 ; + punpckhbw mm2, mm6 ; + punpckhbw mm3, mm6 ; + + psubsw mm0, mm1 ; + psubsw mm2, mm3 ; + + paddw mm5, mm0 ; + paddw mm5, mm2 ; + + pmaddwd mm0, mm0 ; + pmaddwd mm2, mm2 ; + ; + paddd mm7, mm0 ; + paddd mm7, mm2 ; + + add ecx, SrcStride ; /* Inc pointer into src data */ + add edx, RefStride ; /* Inc pointer into ref data */ + + dec edi ; + jnz loop_start ; + + movq mm0, mm5 ; + psrlq mm5, 32 ; + paddw mm5, mm0 ; + movq mm0, mm5 ; + psrlq mm5, 16 ; + paddw mm5, mm0 ; + movd edi, mm5 ; + movsx edi, di ; + mov eax, edi ; + + movq mm0, mm7 ; + psrlq mm7, 32 ; + paddd mm7, mm0 ; + movd ebx, mm7 ; + + mov XSum, eax + mov XXSum, ebx + + }; + + /* Compute and return population variance as mis-match metric. */ + return (( (XXSum<<6) - XSum*XSum )); + + +#endif +} + +static ogg_uint32_t inter8x8_err_xy2__mmx (unsigned char *SrcData, ogg_uint32_t SrcStride, + unsigned char *RefDataPtr1, + unsigned char *RefDataPtr2, ogg_uint32_t RefStride) +{ +#if 0 + ogg_uint32_t i; + ogg_uint32_t XSum=0; + ogg_uint32_t XXSum=0; + ogg_int32_t DiffVal; + + for (i=8; i; i--) { + DiffVal = DSP_OP_DIFF(SrcData[0], DSP_OP_AVG (RefDataPtr1[0], RefDataPtr2[0])); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF(SrcData[1], DSP_OP_AVG (RefDataPtr1[1], RefDataPtr2[1])); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF(SrcData[2], DSP_OP_AVG (RefDataPtr1[2], RefDataPtr2[2])); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF(SrcData[3], DSP_OP_AVG (RefDataPtr1[3], RefDataPtr2[3])); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF(SrcData[4], DSP_OP_AVG (RefDataPtr1[4], RefDataPtr2[4])); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF(SrcData[5], DSP_OP_AVG (RefDataPtr1[5], RefDataPtr2[5])); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF(SrcData[6], DSP_OP_AVG (RefDataPtr1[6], RefDataPtr2[6])); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + DiffVal = DSP_OP_DIFF(SrcData[7], DSP_OP_AVG (RefDataPtr1[7], RefDataPtr2[7])); + XSum += DiffVal; + XXSum += DiffVal*DiffVal; + + /* Step to next row of block. */ + SrcData += SrcStride; + RefDataPtr1 += RefStride; + RefDataPtr2 += RefStride; + } + + /* Compute and return population variance as mis-match metric. */ + return (( (XXSum<<6) - XSum*XSum )); +#else + ogg_uint32_t XSum; + ogg_uint32_t XXSum; + + __asm { + align 16 + + mov ebx, SrcData + mov ecx, RefDataPtr1 + mov edx, RefDataPtr2 + + pcmpeqd mm4, mm4 ; /* fefefefefefefefe in mm4 */ + paddb mm4, mm4 ; + pxor mm5, mm5 ; + pxor mm6, mm6 ; + pxor mm7, mm7 ; + mov edi, 8 ; + loop_start: ; + movq mm0, [ebx] ; /* take 8 bytes */ + + movq mm2, [ecx] ; + movq mm3, [edx] ; /* take average of mm2 and mm3 */ + movq mm1, mm2 ; + pand mm1, mm3 ; + pxor mm3, mm2 ; + pand mm3, mm4 ; + psrlq mm3, 1 ; + paddb mm1, mm3 ; + + movq mm2, mm0 ; + movq mm3, mm1 ; + + punpcklbw mm0, mm6 ; + punpcklbw mm1, mm6 ; + punpckhbw mm2, mm6 ; + punpckhbw mm3, mm6 ; + + psubsw mm0, mm1 ; + psubsw mm2, mm3 ; + + paddw mm5, mm0 ; + paddw mm5, mm2 ; + + pmaddwd mm0, mm0 ; + pmaddwd mm2, mm2 ; + ; + paddd mm7, mm0 ; + paddd mm7, mm2 ; + + add ebx, SrcStride ; /* Inc pointer into src data */ + add ecx, RefStride ; /* Inc pointer into ref data */ + add edx, RefStride ; /* Inc pointer into ref data */ + + dec edi ; + jnz loop_start ; + + movq mm0, mm5 ; + psrlq mm5, 32 ; + paddw mm5, mm0 ; + movq mm0, mm5 ; + psrlq mm5, 16 ; + paddw mm5, mm0 ; + movd edi, mm5 ; + movsx edi, di ; + mov XSum, edi ; /* movl eax, edi ; Modified for vc to resuse eax*/ + + movq mm0, mm7 ; + psrlq mm7, 32 ; + paddd mm7, mm0 ; + movd XXSum, mm7 ; /*movd eax, mm7 ; Modified for vc to reuse eax */ + }; + + return (( (XXSum<<6) - XSum*XSum )); + +#endif +} + +static void restore_fpu (void) +{ + + __asm { + emms + } + +} + +void dsp_mmx_init(DspFunctions *funcs) +{ + funcs->restore_fpu = restore_fpu; + funcs->sub8x8 = sub8x8__mmx; + funcs->sub8x8_128 = sub8x8_128__mmx; + funcs->sub8x8avg2 = sub8x8avg2__mmx; + funcs->row_sad8 = row_sad8__mmx; + funcs->col_sad8x8 = col_sad8x8__mmx; + funcs->sad8x8 = sad8x8__mmx; + funcs->sad8x8_thres = sad8x8_thres__mmx; + funcs->sad8x8_xy2_thres = sad8x8_xy2_thres__mmx; + funcs->intra8x8_err = intra8x8_err__mmx; + funcs->inter8x8_err = inter8x8_err__mmx; + funcs->inter8x8_err_xy2 = inter8x8_err_xy2__mmx; +} + diff --git a/Engine/lib/libtheora/lib/enc/x86_32_vs/fdct_mmx.c b/Engine/lib/libtheora/lib/enc/x86_32_vs/fdct_mmx.c new file mode 100644 index 000000000..65cd9c367 --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/x86_32_vs/fdct_mmx.c @@ -0,0 +1,333 @@ +;//========================================================================== +;// +;// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY +;// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +;// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR +;// PURPOSE. +;// +;// Copyright (c) 1999 - 2001 On2 Technologies Inc. All Rights Reserved. +;// +;//-------------------------------------------------------------------------- + +#include "theora/theora.h" +#include "../codec_internal.h" +#include "../dsp.h" + + +static const ogg_int64_t xC1S7 = 0x0fb15fb15fb15fb15; +static const ogg_int64_t xC2S6 = 0x0ec83ec83ec83ec83; +static const ogg_int64_t xC3S5 = 0x0d4dbd4dbd4dbd4db; +static const ogg_int64_t xC4S4 = 0x0b505b505b505b505; +static const ogg_int64_t xC5S3 = 0x08e3a8e3a8e3a8e3a; +static const ogg_int64_t xC6S2 = 0x061f861f861f861f8; +static const ogg_int64_t xC7S1 = 0x031f131f131f131f1; + + +static __inline void Transpose_mmx( ogg_int16_t *InputData1, ogg_int16_t *OutputData1, + ogg_int16_t *InputData2, ogg_int16_t *OutputData2) +{ + + __asm { + align 16 + mov eax, InputData1 + mov ebx, InputData2 + mov ecx, OutputData1 + mov edx, OutputData2 + + + movq mm0, [eax] ; /* mm0 = a0 a1 a2 a3 */ + movq mm4, [ebx] ; /* mm4 = e4 e5 e6 e7 */ + movq mm1, [16 + eax] ; /* mm1 = b0 b1 b2 b3 */ + movq mm5, [16 + ebx] ; /* mm5 = f4 f5 f6 f7 */ + movq mm2, [32 + eax] ; /* mm2 = c0 c1 c2 c3 */ + movq mm6, [32 + ebx] ; /* mm6 = g4 g5 g6 g7 */ + movq mm3, [48 + eax] ; /* mm3 = d0 d1 d2 d3 */ + movq [16 + ecx], mm1 ; /* save b0 b1 b2 b3 */ + movq mm7, [48 + ebx] ; /* mm7 = h0 h1 h2 h3 */ + ; /* Transpose 2x8 block */ + movq mm1, mm4 ; /* mm1 = e3 e2 e1 e0 */ + punpcklwd mm4, mm5 ; /* mm4 = f1 e1 f0 e0 */ + movq [ecx], mm0 ; /* save a3 a2 a1 a0 */ + punpckhwd mm1, mm5 ; /* mm1 = f3 e3 f2 e2 */ + movq mm0, mm6 ; /* mm0 = g3 g2 g1 g0 */ + punpcklwd mm6, mm7 ; /* mm6 = h1 g1 h0 g0 */ + movq mm5, mm4 ; /* mm5 = f1 e1 f0 e0 */ + punpckldq mm4, mm6 ; /* mm4 = h0 g0 f0 e0 = MM4 */ + punpckhdq mm5, mm6 ; /* mm5 = h1 g1 f1 e1 = MM5 */ + movq mm6, mm1 ; /* mm6 = f3 e3 f2 e2 */ + movq [edx], mm4 ; + punpckhwd mm0, mm7 ; /* mm0 = h3 g3 h2 g2 */ + movq [16 + edx], mm5 ; + punpckhdq mm6, mm0 ; /* mm6 = h3 g3 f3 e3 = MM7 */ + movq mm4, [ecx] ; /* mm4 = a3 a2 a1 a0 */ + punpckldq mm1, mm0 ; /* mm1 = h2 g2 f2 e2 = MM6 */ + movq mm5, [16 + ecx] ; /* mm5 = b3 b2 b1 b0 */ + movq mm0, mm4 ; /* mm0 = a3 a2 a1 a0 */ + movq [48 + edx], mm6 ; + punpcklwd mm0, mm5 ; /* mm0 = b1 a1 b0 a0 */ + movq [32 + edx], mm1 ; + punpckhwd mm4, mm5 ; /* mm4 = b3 a3 b2 a2 */ + movq mm5, mm2 ; /* mm5 = c3 c2 c1 c0 */ + punpcklwd mm2, mm3 ; /* mm2 = d1 c1 d0 c0 */ + movq mm1, mm0 ; /* mm1 = b1 a1 b0 a0 */ + punpckldq mm0, mm2 ; /* mm0 = d0 c0 b0 a0 = MM0 */ + punpckhdq mm1, mm2 ; /* mm1 = d1 c1 b1 a1 = MM1 */ + movq mm2, mm4 ; /* mm2 = b3 a3 b2 a2 */ + movq [ecx], mm0 ; + punpckhwd mm5, mm3 ; /* mm5 = d3 c3 d2 c2 */ + movq [16 + ecx], mm1 ; + punpckhdq mm4, mm5 ; /* mm4 = d3 c3 b3 a3 = MM3 */ + punpckldq mm2, mm5 ; /* mm2 = d2 c2 b2 a2 = MM2 */ + movq [48 + ecx], mm4 ; + movq [32 + ecx], mm2 ; + + }; + + +} + +static __inline void Fdct_mmx( ogg_int16_t *InputData1, ogg_int16_t *InputData2, ogg_int16_t *temp) +{ + + __asm { + align 16 + + + mov eax, InputData1 + mov ebx, InputData2 + mov ecx, temp + movq mm0, [eax] ; + movq mm1, [16 + eax] ; + movq mm2, [48 + eax] ; + movq mm3, [16 + ebx] ; + movq mm4, mm0 ; + movq mm5, mm1 ; + movq mm6, mm2 ; + movq mm7, mm3 ; + ; + paddsw mm0, [48 + ebx] ; /* mm0 = ip0 + ip7 = is07 */ + paddsw mm1, [32 + eax] ; /* mm1 = ip1 + ip2 = is12 */ + paddsw mm2, [ebx] ; /* mm2 = ip3 + ip4 = is34 */ + paddsw mm3, [32 + ebx] ; /* mm3 = ip5 + ip6 = is56 */ + psubsw mm4, [48 + ebx] ; /* mm4 = ip0 - ip7 = id07 */ + psubsw mm5, [32 + eax] ; /* mm5 = ip1 - ip2 = id12 */ + ; + psubsw mm0, mm2 ; /* mm0 = is07 - is34 */ + ; + paddsw mm2, mm2 ; + ; + psubsw mm6, [ebx] ; /* mm6 = ip3 - ip4 = id34 */ + ; + paddsw mm2, mm0 ; /* mm2 = is07 + is34 = is0734 */ + psubsw mm1, mm3 ; /* mm1 = is12 - is56 */ + movq [ecx], mm0 ; /* Save is07 - is34 to free mm0; */ + paddsw mm3, mm3 ; + paddsw mm3, mm1 ; /* mm3 = is12 + 1s56 = is1256 */ + ; + psubsw mm7, [32 + ebx] ; /* mm7 = ip5 - ip6 = id56 */ + ; /* ------------------------------------------------------------------- */ + psubsw mm5, mm7 ; /* mm5 = id12 - id56 */ + paddsw mm7, mm7 ; + paddsw mm7, mm5 ; /* mm7 = id12 + id56 */ + ; /* ------------------------------------------------------------------- */ + psubsw mm2, mm3 ; /* mm2 = is0734 - is1256 */ + paddsw mm3, mm3 ; + ; + movq mm0, mm2 ; /* make a copy */ + paddsw mm3, mm2 ; /* mm3 = is0734 + is1256 */ + ; + pmulhw mm0, xC4S4 ; /* mm0 = xC4S4 * ( is0734 - is1256 ) - ( is0734 - is1256 ) */ + paddw mm0, mm2 ; /* mm0 = xC4S4 * ( is0734 - is1256 ) */ + psrlw mm2, 15 ; + paddw mm0, mm2 ; /* Truncate mm0, now it is op[4] */ + ; + movq mm2, mm3 ; + movq [ebx], mm0 ; /* save ip4, now mm0,mm2 are free */ + ; + movq mm0, mm3 ; + pmulhw mm3, xC4S4 ; /* mm3 = xC4S4 * ( is0734 +is1256 ) - ( is0734 +is1256 ) */ + ; + psrlw mm2, 15 ; + paddw mm3, mm0 ; /* mm3 = xC4S4 * ( is0734 +is1256 ) */ + paddw mm3, mm2 ; /* Truncate mm3, now it is op[0] */ + ; + movq [eax], mm3 ; + ; /* ------------------------------------------------------------------- */ + movq mm3, [ecx] ; /* mm3 = irot_input_y */ + pmulhw mm3, xC2S6 ; /* mm3 = xC2S6 * irot_input_y - irot_input_y */ + ; + movq mm2, [ecx] ; + movq mm0, mm2 ; + ; + psrlw mm2, 15 ; /* mm3 = xC2S6 * irot_input_y */ + paddw mm3, mm0 ; + ; + paddw mm3, mm2 ; /* Truncated */ + movq mm0, mm5 ; + ; + movq mm2, mm5 ; + pmulhw mm0, xC6S2 ; /* mm0 = xC6S2 * irot_input_x */ + ; + psrlw mm2, 15 ; + paddw mm0, mm2 ; /* Truncated */ + ; + paddsw mm3, mm0 ; /* ip[2] */ + movq [32 + eax], mm3 ; /* Save ip2 */ + ; + movq mm0, mm5 ; + movq mm2, mm5 ; + ; + pmulhw mm5, xC2S6 ; /* mm5 = xC2S6 * irot_input_x - irot_input_x */ + psrlw mm2, 15 ; + ; + movq mm3, [ecx] ; + paddw mm5, mm0 ; /* mm5 = xC2S6 * irot_input_x */ + ; + paddw mm5, mm2 ; /* Truncated */ + movq mm2, mm3 ; + ; + pmulhw mm3, xC6S2 ; /* mm3 = xC6S2 * irot_input_y */ + psrlw mm2, 15 ; + ; + paddw mm3, mm2 ; /* Truncated */ + psubsw mm3, mm5 ; + ; + movq [32 + ebx], mm3 ; + ; /* ------------------------------------------------------------------- */ + movq mm0, xC4S4 ; + movq mm2, mm1 ; + movq mm3, mm1 ; + ; + pmulhw mm1, mm0 ; /* mm0 = xC4S4 * ( is12 - is56 ) - ( is12 - is56 ) */ + psrlw mm2, 15 ; + ; + paddw mm1, mm3 ; /* mm0 = xC4S4 * ( is12 - is56 ) */ + paddw mm1, mm2 ; /* Truncate mm1, now it is icommon_product1 */ + ; + movq mm2, mm7 ; + movq mm3, mm7 ; + ; + pmulhw mm7, mm0 ; /* mm7 = xC4S4 * ( id12 + id56 ) - ( id12 + id56 ) */ + psrlw mm2, 15 ; + ; + paddw mm7, mm3 ; /* mm7 = xC4S4 * ( id12 + id56 ) */ + paddw mm7, mm2 ; /* Truncate mm7, now it is icommon_product2 */ + ; /* ------------------------------------------------------------------- */ + pxor mm0, mm0 ; /* Clear mm0 */ + psubsw mm0, mm6 ; /* mm0 = - id34 */ + ; + psubsw mm0, mm7 ; /* mm0 = - ( id34 + idcommon_product2 ) */ + paddsw mm6, mm6 ; + paddsw mm6, mm0 ; /* mm6 = id34 - icommon_product2 */ + ; + psubsw mm4, mm1 ; /* mm4 = id07 - icommon_product1 */ + paddsw mm1, mm1 ; + paddsw mm1, mm4 ; /* mm1 = id07 + icommon_product1 */ + ; /* ------------------------------------------------------------------- */ + movq mm7, xC1S7 ; + movq mm2, mm1 ; + ; + movq mm3, mm1 ; + pmulhw mm1, mm7 ; /* mm1 = xC1S7 * irot_input_x - irot_input_x */ + ; + movq mm7, xC7S1 ; + psrlw mm2, 15 ; + ; + paddw mm1, mm3 ; /* mm1 = xC1S7 * irot_input_x */ + paddw mm1, mm2 ; /* Trucated */ + ; + pmulhw mm3, mm7 ; /* mm3 = xC7S1 * irot_input_x */ + paddw mm3, mm2 ; /* Truncated */ + ; + movq mm5, mm0 ; + movq mm2, mm0 ; + ; + movq mm7, xC1S7 ; + pmulhw mm0, mm7 ; /* mm0 = xC1S7 * irot_input_y - irot_input_y */ + ; + movq mm7, xC7S1 ; + psrlw mm2, 15 ; + ; + paddw mm0, mm5 ; /* mm0 = xC1S7 * irot_input_y */ + paddw mm0, mm2 ; /* Truncated */ + ; + pmulhw mm5, mm7 ; /* mm5 = xC7S1 * irot_input_y */ + paddw mm5, mm2 ; /* Truncated */ + ; + psubsw mm1, mm5 ; /* mm1 = xC1S7 * irot_input_x - xC7S1 * irot_input_y = ip1 */ + paddsw mm3, mm0 ; /* mm3 = xC7S1 * irot_input_x - xC1S7 * irot_input_y = ip7 */ + ; + movq [16 + eax], mm1 ; + movq [48 + ebx], mm3 ; + ; /* ------------------------------------------------------------------- */ + movq mm0, xC3S5 ; + movq mm1, xC5S3 ; + ; + movq mm5, mm6 ; + movq mm7, mm6 ; + ; + movq mm2, mm4 ; + movq mm3, mm4 ; + ; + pmulhw mm4, mm0 ; /* mm4 = xC3S5 * irot_input_x - irot_input_x */ + pmulhw mm6, mm1 ; /* mm6 = xC5S3 * irot_input_y - irot_input_y */ + ; + psrlw mm2, 15 ; + psrlw mm5, 15 ; + ; + paddw mm4, mm3 ; /* mm4 = xC3S5 * irot_input_x */ + paddw mm6, mm7 ; /* mm6 = xC5S3 * irot_input_y */ + ; + paddw mm4, mm2 ; /* Truncated */ + paddw mm6, mm5 ; /* Truncated */ + ; + psubsw mm4, mm6 ; /* ip3 */ + movq [48 + eax], mm4 ; + ; + movq mm4, mm3 ; + movq mm6, mm7 ; + ; + pmulhw mm3, mm1 ; /* mm3 = xC5S3 * irot_input_x - irot_input_x */ + pmulhw mm7, mm0 ; /* mm7 = xC3S5 * irot_input_y - irot_input_y */ + ; + paddw mm4, mm2 ; + paddw mm6, mm5 ; + ; + paddw mm3, mm4 ; /* mm3 = xC5S3 * irot_input_x */ + paddw mm7, mm6 ; /* mm7 = xC3S5 * irot_input_y */ + ; + paddw mm3, mm7 ; /* ip5 */ + movq [16 + ebx], mm3 ; + +}; + +} + + +static void fdct_short__mmx ( ogg_int16_t *InputData, ogg_int16_t *OutputData) +{ + + static ogg_int16_t tmp[32]; + ogg_int16_t* align_tmp = (ogg_int16_t*)((unsigned char*)tmp + (16 - ((int)tmp)&15)); + + + Transpose_mmx(InputData, OutputData, InputData + 4, OutputData + 4); + Fdct_mmx(OutputData, OutputData + 4, align_tmp); + + Transpose_mmx(InputData + 32, OutputData + 32, InputData + 36, OutputData + 36); + Fdct_mmx(OutputData+32, OutputData + 36, align_tmp); + + Transpose_mmx(OutputData, OutputData, OutputData + 32, OutputData + 32); + Fdct_mmx(OutputData, OutputData + 32, align_tmp); + + Transpose_mmx(OutputData + 4, OutputData + 4, OutputData + 36, OutputData + 36); + Fdct_mmx(OutputData + 4, OutputData + 36, align_tmp); + + __asm emms + +} + +void dsp_mmx_fdct_init(DspFunctions *funcs) +{ + funcs->fdct_short = fdct_short__mmx; +} diff --git a/Engine/lib/libtheora/lib/enc/x86_32_vs/recon_mmx.c b/Engine/lib/libtheora/lib/enc/x86_32_vs/recon_mmx.c new file mode 100644 index 000000000..1e0f1f095 --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/x86_32_vs/recon_mmx.c @@ -0,0 +1,197 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: reconstruct.c,v 1.6 2003/12/03 08:59:41 arc Exp $ + + ********************************************************************/ + +#include "../codec_internal.h" + + +static const unsigned __int64 V128 = 0x8080808080808080; + +static void copy8x8__mmx (unsigned char *src, + unsigned char *dest, + unsigned int stride) +{ + + //Is this even the fastest way to do this? + __asm { + align 16 + + mov eax, src + mov ebx, dest + mov ecx, stride + + lea edi, [ecx + ecx * 2] + movq mm0, [eax] + movq mm1, [eax + ecx] + movq mm2, [eax + ecx * 2] + movq mm3, [eax + edi] + lea eax, [eax + ecx * 4] + movq [ebx], mm0 + movq [ebx + ecx], mm1 + movq [ebx + ecx * 2], mm2 + movq [ebx + edi], mm3 + lea ebx, [ebx + ecx * 4] + movq mm0, [eax] + movq mm1, [eax + ecx] + movq mm2, [eax + ecx * 2] + movq mm3, [eax + edi] + movq [ebx], mm0 + movq [ebx + ecx], mm1 + movq [ebx + ecx * 2], mm2 + movq [ebx + edi], mm3 + + }; + +} + +static void recon_intra8x8__mmx (unsigned char *ReconPtr, ogg_int16_t *ChangePtr, + ogg_uint32_t LineStep) +{ + + __asm { + align 16 + + mov eax, ReconPtr + mov ebx, ChangePtr + mov ecx, LineStep + + movq mm0, V128 + + lea edi, [128 + ebx] + loop_start: + movq mm2, [ebx] + + packsswb mm2, [8 + ebx] + por mm0, mm0 + pxor mm2, mm0 + lea ebx, [16 + ebx] + cmp ebx, edi + + movq [eax], mm2 + + + + lea eax, [eax + ecx] + jc loop_start + + + }; + +} + + + + + +static void recon_inter8x8__mmx (unsigned char *ReconPtr, unsigned char *RefPtr, + ogg_int16_t *ChangePtr, ogg_uint32_t LineStep) +{ + + __asm { + + align 16 + + mov eax, ReconPtr + mov ebx, ChangePtr + mov ecx, LineStep + mov edx, RefPtr + + pxor mm0, mm0 + lea edi, [128 + ebx] + + loop_start: + movq mm2, [edx] + + movq mm4, [ebx] + movq mm3, mm2 + movq mm5, [8 + ebx] + punpcklbw mm2, mm0 + paddsw mm2, mm4 + punpckhbw mm3, mm0 + paddsw mm3, mm5 + add edx, ecx + packuswb mm2, mm3 + lea ebx, [16 + ebx] + cmp ebx, edi + + movq [eax], mm2 + + lea eax, [eax + ecx] + jc loop_start + + }; +} + + + + +static void recon_inter8x8_half__mmx (unsigned char *ReconPtr, unsigned char *RefPtr1, + unsigned char *RefPtr2, ogg_int16_t *ChangePtr, + ogg_uint32_t LineStep) +{ + __asm { + align 16 + + mov eax, ReconPtr + mov ebx, ChangePtr + mov ecx, RefPtr1 + mov edx, RefPtr2 + + pxor mm0, mm0 + lea edi, [128 + ebx] + + loop_start: + movq mm2, [ecx] + movq mm4, [edx] + + movq mm3, mm2 + punpcklbw mm2, mm0 + movq mm5, mm4 + movq mm6, [ebx] + punpckhbw mm3, mm0 + movq mm7, [8 + ebx] + punpcklbw mm4, mm0 + punpckhbw mm5, mm0 + paddw mm2, mm4 + paddw mm3, mm5 + psrlw mm2, 1 + psrlw mm3, 1 + paddw mm2, mm6 + paddw mm3, mm7 + lea ebx, [16 + ebx] + packuswb mm2, mm3 + add ecx, LineStep + add edx, LineStep + movq [eax], mm2 + add eax, LineStep + cmp ebx, edi + jc loop_start + + }; + +} + + + + +void dsp_mmx_recon_init(DspFunctions *funcs) +{ + funcs->copy8x8 = copy8x8__mmx; + funcs->recon_intra8x8 = recon_intra8x8__mmx; + funcs->recon_inter8x8 = recon_inter8x8__mmx; + funcs->recon_inter8x8_half = recon_inter8x8_half__mmx; +} + diff --git a/Engine/lib/libtheora/lib/enc/x86_64/dct_decode_mmx.c b/Engine/lib/libtheora/lib/enc/x86_64/dct_decode_mmx.c new file mode 100644 index 000000000..547e974e3 --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/x86_64/dct_decode_mmx.c @@ -0,0 +1,409 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2008 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: dct_decode_mmx.c 15400 2008-10-15 12:10:58Z tterribe $ + + ********************************************************************/ + +#include + +#include "../codec_internal.h" + +#if defined(USE_ASM) + +static const __attribute__((aligned(8),used)) ogg_int64_t OC_V3= + 0x0003000300030003LL; +static const __attribute__((aligned(8),used)) ogg_int64_t OC_V4= + 0x0004000400040004LL; + +static void loop_filter_v(unsigned char *_pix,int _ystride, + const ogg_int16_t *_ll){ + long esi; + _pix-=_ystride*2; + __asm__ __volatile__( + /*mm0=0*/ + "pxor %%mm0,%%mm0\n\t" + /*esi=_ystride*3*/ + "lea (%[ystride],%[ystride],2),%[s]\n\t" + /*mm7=_pix[0...8]*/ + "movq (%[pix]),%%mm7\n\t" + /*mm4=_pix[0...8+_ystride*3]*/ + "movq (%[pix],%[s]),%%mm4\n\t" + /*mm6=_pix[0...8]*/ + "movq %%mm7,%%mm6\n\t" + /*Expand unsigned _pix[0...3] to 16 bits.*/ + "punpcklbw %%mm0,%%mm6\n\t" + "movq %%mm4,%%mm5\n\t" + /*Expand unsigned _pix[4...8] to 16 bits.*/ + "punpckhbw %%mm0,%%mm7\n\t" + /*Expand other arrays too.*/ + "punpcklbw %%mm0,%%mm4\n\t" + "punpckhbw %%mm0,%%mm5\n\t" + /*mm7:mm6=_p[0...8]-_p[0...8+_ystride*3]:*/ + "psubw %%mm4,%%mm6\n\t" + "psubw %%mm5,%%mm7\n\t" + /*mm5=mm4=_pix[0...8+_ystride]*/ + "movq (%[pix],%[ystride]),%%mm4\n\t" + /*mm1=mm3=mm2=_pix[0..8]+_ystride*2]*/ + "movq (%[pix],%[ystride],2),%%mm2\n\t" + "movq %%mm4,%%mm5\n\t" + "movq %%mm2,%%mm3\n\t" + "movq %%mm2,%%mm1\n\t" + /*Expand these arrays.*/ + "punpckhbw %%mm0,%%mm5\n\t" + "punpcklbw %%mm0,%%mm4\n\t" + "punpckhbw %%mm0,%%mm3\n\t" + "punpcklbw %%mm0,%%mm2\n\t" + /*Preload...*/ + "movq %[OC_V3],%%mm0\n\t" + /*mm3:mm2=_pix[0...8+_ystride*2]-_pix[0...8+_ystride]*/ + "psubw %%mm5,%%mm3\n\t" + "psubw %%mm4,%%mm2\n\t" + /*Scale by 3.*/ + "pmullw %%mm0,%%mm3\n\t" + "pmullw %%mm0,%%mm2\n\t" + /*Preload...*/ + "movq %[OC_V4],%%mm0\n\t" + /*f=mm3:mm2==_pix[0...8]-_pix[0...8+_ystride*3]+ + 3*(_pix[0...8+_ystride*2]-_pix[0...8+_ystride])*/ + "paddw %%mm7,%%mm3\n\t" + "paddw %%mm6,%%mm2\n\t" + /*Add 4.*/ + "paddw %%mm0,%%mm3\n\t" + "paddw %%mm0,%%mm2\n\t" + /*"Divide" by 8.*/ + "psraw $3,%%mm3\n\t" + "psraw $3,%%mm2\n\t" + /*Now compute lflim of mm3:mm2 cf. Section 7.10 of the sepc.*/ + /*Free up mm5.*/ + "packuswb %%mm5,%%mm4\n\t" + /*mm0=L L L L*/ + "movq (%[ll]),%%mm0\n\t" + /*if(R_i<-2L||R_i>2L)R_i=0:*/ + "movq %%mm2,%%mm5\n\t" + "pxor %%mm6,%%mm6\n\t" + "movq %%mm0,%%mm7\n\t" + "psubw %%mm0,%%mm6\n\t" + "psllw $1,%%mm7\n\t" + "psllw $1,%%mm6\n\t" + /*mm2==R_3 R_2 R_1 R_0*/ + /*mm5==R_3 R_2 R_1 R_0*/ + /*mm6==-2L -2L -2L -2L*/ + /*mm7==2L 2L 2L 2L*/ + "pcmpgtw %%mm2,%%mm7\n\t" + "pcmpgtw %%mm6,%%mm5\n\t" + "pand %%mm7,%%mm2\n\t" + "movq %%mm0,%%mm7\n\t" + "pand %%mm5,%%mm2\n\t" + "psllw $1,%%mm7\n\t" + "movq %%mm3,%%mm5\n\t" + /*mm3==R_7 R_6 R_5 R_4*/ + /*mm5==R_7 R_6 R_5 R_4*/ + /*mm6==-2L -2L -2L -2L*/ + /*mm7==2L 2L 2L 2L*/ + "pcmpgtw %%mm3,%%mm7\n\t" + "pcmpgtw %%mm6,%%mm5\n\t" + "pand %%mm7,%%mm3\n\t" + "movq %%mm0,%%mm7\n\t" + "pand %%mm5,%%mm3\n\t" + /*if(R_i<-L)R_i'=R_i+2L; + if(R_i>L)R_i'=R_i-2L; + if(R_i<-L||R_i>L)R_i=-R_i':*/ + "psraw $1,%%mm6\n\t" + "movq %%mm2,%%mm5\n\t" + "psllw $1,%%mm7\n\t" + /*mm2==R_3 R_2 R_1 R_0*/ + /*mm5==R_3 R_2 R_1 R_0*/ + /*mm6==-L -L -L -L*/ + /*mm0==L L L L*/ + /*mm5=R_i>L?FF:00*/ + "pcmpgtw %%mm0,%%mm5\n\t" + /*mm6=-L>R_i?FF:00*/ + "pcmpgtw %%mm2,%%mm6\n\t" + /*mm7=R_i>L?2L:0*/ + "pand %%mm5,%%mm7\n\t" + /*mm2=R_i>L?R_i-2L:R_i*/ + "psubw %%mm7,%%mm2\n\t" + "movq %%mm0,%%mm7\n\t" + /*mm5=-L>R_i||R_i>L*/ + "por %%mm6,%%mm5\n\t" + "psllw $1,%%mm7\n\t" + /*mm7=-L>R_i?2L:0*/ + "pand %%mm6,%%mm7\n\t" + "pxor %%mm6,%%mm6\n\t" + /*mm2=-L>R_i?R_i+2L:R_i*/ + "paddw %%mm7,%%mm2\n\t" + "psubw %%mm0,%%mm6\n\t" + /*mm5=-L>R_i||R_i>L?-R_i':0*/ + "pand %%mm2,%%mm5\n\t" + "movq %%mm0,%%mm7\n\t" + /*mm2=-L>R_i||R_i>L?0:R_i*/ + "psubw %%mm5,%%mm2\n\t" + "psllw $1,%%mm7\n\t" + /*mm2=-L>R_i||R_i>L?-R_i':R_i*/ + "psubw %%mm5,%%mm2\n\t" + "movq %%mm3,%%mm5\n\t" + /*mm3==R_7 R_6 R_5 R_4*/ + /*mm5==R_7 R_6 R_5 R_4*/ + /*mm6==-L -L -L -L*/ + /*mm0==L L L L*/ + /*mm6=-L>R_i?FF:00*/ + "pcmpgtw %%mm3,%%mm6\n\t" + /*mm5=R_i>L?FF:00*/ + "pcmpgtw %%mm0,%%mm5\n\t" + /*mm7=R_i>L?2L:0*/ + "pand %%mm5,%%mm7\n\t" + /*mm2=R_i>L?R_i-2L:R_i*/ + "psubw %%mm7,%%mm3\n\t" + "psllw $1,%%mm0\n\t" + /*mm5=-L>R_i||R_i>L*/ + "por %%mm6,%%mm5\n\t" + /*mm0=-L>R_i?2L:0*/ + "pand %%mm6,%%mm0\n\t" + /*mm3=-L>R_i?R_i+2L:R_i*/ + "paddw %%mm0,%%mm3\n\t" + /*mm5=-L>R_i||R_i>L?-R_i':0*/ + "pand %%mm3,%%mm5\n\t" + /*mm2=-L>R_i||R_i>L?0:R_i*/ + "psubw %%mm5,%%mm3\n\t" + /*mm2=-L>R_i||R_i>L?-R_i':R_i*/ + "psubw %%mm5,%%mm3\n\t" + /*Unfortunately, there's no unsigned byte+signed byte with unsigned + saturation op code, so we have to promote things back 16 bits.*/ + "pxor %%mm0,%%mm0\n\t" + "movq %%mm4,%%mm5\n\t" + "punpcklbw %%mm0,%%mm4\n\t" + "punpckhbw %%mm0,%%mm5\n\t" + "movq %%mm1,%%mm6\n\t" + "punpcklbw %%mm0,%%mm1\n\t" + "punpckhbw %%mm0,%%mm6\n\t" + /*_pix[0...8+_ystride]+=R_i*/ + "paddw %%mm2,%%mm4\n\t" + "paddw %%mm3,%%mm5\n\t" + /*_pix[0...8+_ystride*2]-=R_i*/ + "psubw %%mm2,%%mm1\n\t" + "psubw %%mm3,%%mm6\n\t" + "packuswb %%mm5,%%mm4\n\t" + "packuswb %%mm6,%%mm1\n\t" + /*Write it back out.*/ + "movq %%mm4,(%[pix],%[ystride])\n\t" + "movq %%mm1,(%[pix],%[ystride],2)\n\t" + :[s]"=&S"(esi) + :[pix]"r"(_pix),[ystride]"r"((long)_ystride),[ll]"r"(_ll), + [OC_V3]"m"(OC_V3),[OC_V4]"m"(OC_V4) + :"memory" + ); +} + +/*This code implements the bulk of loop_filter_h(). + Data are striped p0 p1 p2 p3 ... p0 p1 p2 p3 ..., so in order to load all + four p0's to one register we must transpose the values in four mmx regs. + When half is done we repeat this for the rest.*/ +static void loop_filter_h4(unsigned char *_pix,long _ystride, + const ogg_int16_t *_ll){ + long esi; + long edi; + __asm__ __volatile__( + /*x x x x 3 2 1 0*/ + "movd (%[pix]),%%mm0\n\t" + /*esi=_ystride*3*/ + "lea (%[ystride],%[ystride],2),%[s]\n\t" + /*x x x x 7 6 5 4*/ + "movd (%[pix],%[ystride]),%%mm1\n\t" + /*x x x x B A 9 8*/ + "movd (%[pix],%[ystride],2),%%mm2\n\t" + /*x x x x F E D C*/ + "movd (%[pix],%[s]),%%mm3\n\t" + /*mm0=7 3 6 2 5 1 4 0*/ + "punpcklbw %%mm1,%%mm0\n\t" + /*mm2=F B E A D 9 C 8*/ + "punpcklbw %%mm3,%%mm2\n\t" + /*mm1=7 3 6 2 5 1 4 0*/ + "movq %%mm0,%%mm1\n\t" + /*mm0=F B 7 3 E A 6 2*/ + "punpckhwd %%mm2,%%mm0\n\t" + /*mm1=D 9 5 1 C 8 4 0*/ + "punpcklwd %%mm2,%%mm1\n\t" + "pxor %%mm7,%%mm7\n\t" + /*mm5=D 9 5 1 C 8 4 0*/ + "movq %%mm1,%%mm5\n\t" + /*mm1=x C x 8 x 4 x 0==pix[0]*/ + "punpcklbw %%mm7,%%mm1\n\t" + /*mm5=x D x 9 x 5 x 1==pix[1]*/ + "punpckhbw %%mm7,%%mm5\n\t" + /*mm3=F B 7 3 E A 6 2*/ + "movq %%mm0,%%mm3\n\t" + /*mm0=x E x A x 6 x 2==pix[2]*/ + "punpcklbw %%mm7,%%mm0\n\t" + /*mm3=x F x B x 7 x 3==pix[3]*/ + "punpckhbw %%mm7,%%mm3\n\t" + /*mm1=mm1-mm3==pix[0]-pix[3]*/ + "psubw %%mm3,%%mm1\n\t" + /*Save a copy of pix[2] for later.*/ + "movq %%mm0,%%mm4\n\t" + /*mm0=mm0-mm5==pix[2]-pix[1]*/ + "psubw %%mm5,%%mm0\n\t" + /*Scale by 3.*/ + "pmullw %[OC_V3],%%mm0\n\t" + /*f=mm1==_pix[0]-_pix[3]+ 3*(_pix[2]-_pix[1])*/ + "paddw %%mm1,%%mm0\n\t" + /*Add 4.*/ + "paddw %[OC_V4],%%mm0\n\t" + /*"Divide" by 8, producing the residuals R_i.*/ + "psraw $3,%%mm0\n\t" + /*Now compute lflim of mm0 cf. Section 7.10 of the sepc.*/ + /*mm6=L L L L*/ + "movq (%[ll]),%%mm6\n\t" + /*if(R_i<-2L||R_i>2L)R_i=0:*/ + "movq %%mm0,%%mm1\n\t" + "pxor %%mm2,%%mm2\n\t" + "movq %%mm6,%%mm3\n\t" + "psubw %%mm6,%%mm2\n\t" + "psllw $1,%%mm3\n\t" + "psllw $1,%%mm2\n\t" + /*mm0==R_3 R_2 R_1 R_0*/ + /*mm1==R_3 R_2 R_1 R_0*/ + /*mm2==-2L -2L -2L -2L*/ + /*mm3==2L 2L 2L 2L*/ + "pcmpgtw %%mm0,%%mm3\n\t" + "pcmpgtw %%mm2,%%mm1\n\t" + "pand %%mm3,%%mm0\n\t" + "pand %%mm1,%%mm0\n\t" + /*if(R_i<-L)R_i'=R_i+2L; + if(R_i>L)R_i'=R_i-2L; + if(R_i<-L||R_i>L)R_i=-R_i':*/ + "psraw $1,%%mm2\n\t" + "movq %%mm0,%%mm1\n\t" + "movq %%mm6,%%mm3\n\t" + /*mm0==R_3 R_2 R_1 R_0*/ + /*mm1==R_3 R_2 R_1 R_0*/ + /*mm2==-L -L -L -L*/ + /*mm6==L L L L*/ + /*mm2=-L>R_i?FF:00*/ + "pcmpgtw %%mm0,%%mm2\n\t" + /*mm1=R_i>L?FF:00*/ + "pcmpgtw %%mm6,%%mm1\n\t" + /*mm3=2L 2L 2L 2L*/ + "psllw $1,%%mm3\n\t" + /*mm6=2L 2L 2L 2L*/ + "psllw $1,%%mm6\n\t" + /*mm3=R_i>L?2L:0*/ + "pand %%mm1,%%mm3\n\t" + /*mm6=-L>R_i?2L:0*/ + "pand %%mm2,%%mm6\n\t" + /*mm0=R_i>L?R_i-2L:R_i*/ + "psubw %%mm3,%%mm0\n\t" + /*mm1=-L>R_i||R_i>L*/ + "por %%mm2,%%mm1\n\t" + /*mm0=-L>R_i?R_i+2L:R_i*/ + "paddw %%mm6,%%mm0\n\t" + /*mm1=-L>R_i||R_i>L?R_i':0*/ + "pand %%mm0,%%mm1\n\t" + /*mm0=-L>R_i||R_i>L?0:R_i*/ + "psubw %%mm1,%%mm0\n\t" + /*mm0=-L>R_i||R_i>L?-R_i':R_i*/ + "psubw %%mm1,%%mm0\n\t" + /*_pix[1]+=R_i;*/ + "paddw %%mm0,%%mm5\n\t" + /*_pix[2]-=R_i;*/ + "psubw %%mm0,%%mm4\n\t" + /*mm5=x x x x D 9 5 1*/ + "packuswb %%mm7,%%mm5\n\t" + /*mm4=x x x x E A 6 2*/ + "packuswb %%mm7,%%mm4\n\t" + /*mm5=E D A 9 6 5 2 1*/ + "punpcklbw %%mm4,%%mm5\n\t" + /*edi=6 5 2 1*/ + "movd %%mm5,%%edi\n\t" + "movw %%di,1(%[pix])\n\t" + /*Why is there such a big stall here?*/ + "psrlq $32,%%mm5\n\t" + "shrl $16,%%edi\n\t" + "movw %%di,1(%[pix],%[ystride])\n\t" + /*edi=E D A 9*/ + "movd %%mm5,%%edi\n\t" + "movw %%di,1(%[pix],%[ystride],2)\n\t" + "shrl $16,%%edi\n\t" + "movw %%di,1(%[pix],%[s])\n\t" + :[s]"=&S"(esi),[d]"=&D"(edi), + [pix]"+r"(_pix),[ystride]"+r"(_ystride),[ll]"+r"(_ll) + :[OC_V3]"m"(OC_V3),[OC_V4]"m"(OC_V4) + :"memory" + ); +} + +static void loop_filter_h(unsigned char *_pix,int _ystride, + const ogg_int16_t *_ll){ + _pix-=2; + loop_filter_h4(_pix,_ystride,_ll); + loop_filter_h4(_pix+(_ystride<<2),_ystride,_ll); +} + +static void loop_filter_mmx(PB_INSTANCE *pbi, int FLimit){ + int j; + ogg_int16_t __attribute__((aligned(8))) ll[4]; + unsigned char *cp = pbi->display_fragments; + ogg_uint32_t *bp = pbi->recon_pixel_index_table; + + if ( FLimit == 0 ) return; + ll[0]=ll[1]=ll[2]=ll[3]=FLimit; + + for ( j = 0; j < 3 ; j++){ + ogg_uint32_t *bp_begin = bp; + ogg_uint32_t *bp_end; + int stride; + int h; + + switch(j) { + case 0: /* y */ + bp_end = bp + pbi->YPlaneFragments; + h = pbi->HFragments; + stride = pbi->YStride; + break; + default: /* u,v, 4:20 specific */ + bp_end = bp + pbi->UVPlaneFragments; + h = pbi->HFragments >> 1; + stride = pbi->UVStride; + break; + } + + while(bpbp_left) + loop_filter_h(&pbi->LastFrameRecon[bp[0]],stride,ll); + if(bp_left>bp_begin) + loop_filter_v(&pbi->LastFrameRecon[bp[0]],stride,ll); + if(bp+1LastFrameRecon[bp[0]]+8,stride,ll); + if(bp+hLastFrameRecon[bp[h]],stride,ll); + } + bp++; + cp++; + } + } + } + + __asm__ __volatile__("emms\n\t"); +} + +/* install our implementation in the function table */ +void dsp_mmx_dct_decode_init(DspFunctions *funcs) +{ + funcs->LoopFilter = loop_filter_mmx; +} + +#endif /* USE_ASM */ diff --git a/Engine/lib/libtheora/lib/enc/x86_64/dsp_mmx.c b/Engine/lib/libtheora/lib/enc/x86_64/dsp_mmx.c new file mode 100644 index 000000000..6c2689e63 --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/x86_64/dsp_mmx.c @@ -0,0 +1,303 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: dsp_mmx.c 15397 2008-10-14 02:06:24Z tterribe $ + + ********************************************************************/ + +#include + +#include "../codec_internal.h" +#include "../dsp.h" + +#if defined(USE_ASM) + +typedef unsigned long long ogg_uint64_t; + +static const __attribute__ ((aligned(8),used)) ogg_int64_t V128 = 0x0080008000800080LL; + +#define DSP_OP_AVG(a,b) ((((int)(a)) + ((int)(b)))/2) +#define DSP_OP_DIFF(a,b) (((int)(a)) - ((int)(b))) +#define DSP_OP_ABS_DIFF(a,b) abs((((int)(a)) - ((int)(b)))) + +static void sub8x8__mmx (unsigned char *FiltPtr, unsigned char *ReconPtr, + ogg_int16_t *DctInputPtr, ogg_uint32_t PixelsPerLine, + ogg_uint32_t ReconPixelsPerLine) +{ + __asm__ __volatile__ ( + " .balign 16 \n\t" + + " pxor %%mm7, %%mm7 \n\t" + + ".rept 8 \n\t" + " movq (%0), %%mm0 \n\t" /* mm0 = FiltPtr */ + " movq (%1), %%mm1 \n\t" /* mm1 = ReconPtr */ + " movq %%mm0, %%mm2 \n\t" /* dup to prepare for up conversion */ + " movq %%mm1, %%mm3 \n\t" /* dup to prepare for up conversion */ + /* convert from UINT8 to INT16 */ + " punpcklbw %%mm7, %%mm0 \n\t" /* mm0 = INT16(FiltPtr) */ + " punpcklbw %%mm7, %%mm1 \n\t" /* mm1 = INT16(ReconPtr) */ + " punpckhbw %%mm7, %%mm2 \n\t" /* mm2 = INT16(FiltPtr) */ + " punpckhbw %%mm7, %%mm3 \n\t" /* mm3 = INT16(ReconPtr) */ + /* start calculation */ + " psubw %%mm1, %%mm0 \n\t" /* mm0 = FiltPtr - ReconPtr */ + " psubw %%mm3, %%mm2 \n\t" /* mm2 = FiltPtr - ReconPtr */ + " movq %%mm0, (%2) \n\t" /* write answer out */ + " movq %%mm2, 8(%2) \n\t" /* write answer out */ + /* Increment pointers */ + " add $16, %2 \n\t" + " add %3, %0 \n\t" + " add %4, %1 \n\t" + ".endr \n\t" + + : "+r" (FiltPtr), + "+r" (ReconPtr), + "+r" (DctInputPtr) + : "r" ((ogg_uint64_t)PixelsPerLine), + "r" ((ogg_uint64_t)ReconPixelsPerLine) + : "memory" + ); +} + +static void sub8x8_128__mmx (unsigned char *FiltPtr, ogg_int16_t *DctInputPtr, + ogg_uint32_t PixelsPerLine) +{ + ogg_uint64_t ppl = PixelsPerLine; + + __asm__ __volatile__ ( + " .balign 16 \n\t" + + " pxor %%mm7, %%mm7 \n\t" + " movq %[V128], %%mm1 \n\t" + + ".rept 8 \n\t" + " movq (%0), %%mm0 \n\t" /* mm0 = FiltPtr */ + " movq %%mm0, %%mm2 \n\t" /* dup to prepare for up conversion */ + /* convert from UINT8 to INT16 */ + " punpcklbw %%mm7, %%mm0 \n\t" /* mm0 = INT16(FiltPtr) */ + " punpckhbw %%mm7, %%mm2 \n\t" /* mm2 = INT16(FiltPtr) */ + /* start calculation */ + " psubw %%mm1, %%mm0 \n\t" /* mm0 = FiltPtr - 128 */ + " psubw %%mm1, %%mm2 \n\t" /* mm2 = FiltPtr - 128 */ + " movq %%mm0, (%1) \n\t" /* write answer out */ + " movq %%mm2, 8(%1) \n\t" /* write answer out */ + /* Increment pointers */ + " add $16, %1 \n\t" + " add %2, %0 \n\t" + ".endr \n\t" + + : "+r" (FiltPtr), + "+r" (DctInputPtr) + : "r" (ppl), /* gcc bug? a cast won't work here, e.g. (ogg_uint64_t)PixelsPerLine */ + [V128] "m" (V128) + : "memory" + ); +} + +static void sub8x8avg2__mmx (unsigned char *FiltPtr, unsigned char *ReconPtr1, + unsigned char *ReconPtr2, ogg_int16_t *DctInputPtr, + ogg_uint32_t PixelsPerLine, + ogg_uint32_t ReconPixelsPerLine) +{ + __asm__ __volatile__ ( + " .balign 16 \n\t" + + " pxor %%mm7, %%mm7 \n\t" + + ".rept 8 \n\t" + " movq (%0), %%mm0 \n\t" /* mm0 = FiltPtr */ + " movq (%1), %%mm1 \n\t" /* mm1 = ReconPtr1 */ + " movq (%2), %%mm4 \n\t" /* mm1 = ReconPtr2 */ + " movq %%mm0, %%mm2 \n\t" /* dup to prepare for up conversion */ + " movq %%mm1, %%mm3 \n\t" /* dup to prepare for up conversion */ + " movq %%mm4, %%mm5 \n\t" /* dup to prepare for up conversion */ + /* convert from UINT8 to INT16 */ + " punpcklbw %%mm7, %%mm0 \n\t" /* mm0 = INT16(FiltPtr) */ + " punpcklbw %%mm7, %%mm1 \n\t" /* mm1 = INT16(ReconPtr1) */ + " punpcklbw %%mm7, %%mm4 \n\t" /* mm1 = INT16(ReconPtr2) */ + " punpckhbw %%mm7, %%mm2 \n\t" /* mm2 = INT16(FiltPtr) */ + " punpckhbw %%mm7, %%mm3 \n\t" /* mm3 = INT16(ReconPtr1) */ + " punpckhbw %%mm7, %%mm5 \n\t" /* mm3 = INT16(ReconPtr2) */ + /* average ReconPtr1 and ReconPtr2 */ + " paddw %%mm4, %%mm1 \n\t" /* mm1 = ReconPtr1 + ReconPtr2 */ + " paddw %%mm5, %%mm3 \n\t" /* mm3 = ReconPtr1 + ReconPtr2 */ + " psrlw $1, %%mm1 \n\t" /* mm1 = (ReconPtr1 + ReconPtr2) / 2 */ + " psrlw $1, %%mm3 \n\t" /* mm3 = (ReconPtr1 + ReconPtr2) / 2 */ + " psubw %%mm1, %%mm0 \n\t" /* mm0 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ + " psubw %%mm3, %%mm2 \n\t" /* mm2 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ + " movq %%mm0, (%3) \n\t" /* write answer out */ + " movq %%mm2, 8(%3) \n\t" /* write answer out */ + /* Increment pointers */ + " add $16, %3 \n\t" + " add %4, %0 \n\t" + " add %5, %1 \n\t" + " add %5, %2 \n\t" + ".endr \n\t" + + : "+r" (FiltPtr), + "+r" (ReconPtr1), + "+r" (ReconPtr2), + "+r" (DctInputPtr) + : "r" ((ogg_uint64_t)PixelsPerLine), + "r" ((ogg_uint64_t)ReconPixelsPerLine) + : "memory" + ); +} + +static ogg_uint32_t intra8x8_err__mmx (unsigned char *DataPtr, ogg_uint32_t Stride) +{ + ogg_uint64_t XSum; + ogg_uint64_t XXSum; + + __asm__ __volatile__ ( + " .balign 16 \n\t" + + " pxor %%mm5, %%mm5 \n\t" + " pxor %%mm6, %%mm6 \n\t" + " pxor %%mm7, %%mm7 \n\t" + " mov $8, %%rdi \n\t" + "1: \n\t" + " movq (%2), %%mm0 \n\t" /* take 8 bytes */ + " movq %%mm0, %%mm2 \n\t" + + " punpcklbw %%mm6, %%mm0 \n\t" + " punpckhbw %%mm6, %%mm2 \n\t" + + " paddw %%mm0, %%mm5 \n\t" + " paddw %%mm2, %%mm5 \n\t" + + " pmaddwd %%mm0, %%mm0 \n\t" + " pmaddwd %%mm2, %%mm2 \n\t" + + " paddd %%mm0, %%mm7 \n\t" + " paddd %%mm2, %%mm7 \n\t" + + " add %3, %2 \n\t" /* Inc pointer into src data */ + + " dec %%rdi \n\t" + " jnz 1b \n\t" + + " movq %%mm5, %%mm0 \n\t" + " psrlq $32, %%mm5 \n\t" + " paddw %%mm0, %%mm5 \n\t" + " movq %%mm5, %%mm0 \n\t" + " psrlq $16, %%mm5 \n\t" + " paddw %%mm0, %%mm5 \n\t" + " movd %%mm5, %%rdi \n\t" + " movsx %%di, %%rdi \n\t" + " mov %%rdi, %0 \n\t" + + " movq %%mm7, %%mm0 \n\t" + " psrlq $32, %%mm7 \n\t" + " paddd %%mm0, %%mm7 \n\t" + " movd %%mm7, %1 \n\t" + + : "=r" (XSum), + "=r" (XXSum), + "+r" (DataPtr) + : "r" ((ogg_uint64_t)Stride) + : "rdi", "memory" + ); + + /* Compute population variance as mis-match metric. */ + return (( (XXSum<<6) - XSum*XSum ) ); +} + +static ogg_uint32_t inter8x8_err__mmx (unsigned char *SrcData, ogg_uint32_t SrcStride, + unsigned char *RefDataPtr, ogg_uint32_t RefStride) +{ + ogg_uint64_t XSum; + ogg_uint64_t XXSum; + + __asm__ __volatile__ ( + " .balign 16 \n\t" + + " pxor %%mm5, %%mm5 \n\t" + " pxor %%mm6, %%mm6 \n\t" + " pxor %%mm7, %%mm7 \n\t" + " mov $8, %%rdi \n\t" + "1: \n\t" + " movq (%2), %%mm0 \n\t" /* take 8 bytes */ + " movq (%3), %%mm1 \n\t" + " movq %%mm0, %%mm2 \n\t" + " movq %%mm1, %%mm3 \n\t" + + " punpcklbw %%mm6, %%mm0 \n\t" + " punpcklbw %%mm6, %%mm1 \n\t" + " punpckhbw %%mm6, %%mm2 \n\t" + " punpckhbw %%mm6, %%mm3 \n\t" + + " psubsw %%mm1, %%mm0 \n\t" + " psubsw %%mm3, %%mm2 \n\t" + + " paddw %%mm0, %%mm5 \n\t" + " paddw %%mm2, %%mm5 \n\t" + + " pmaddwd %%mm0, %%mm0 \n\t" + " pmaddwd %%mm2, %%mm2 \n\t" + + " paddd %%mm0, %%mm7 \n\t" + " paddd %%mm2, %%mm7 \n\t" + + " add %4, %2 \n\t" /* Inc pointer into src data */ + " add %5, %3 \n\t" /* Inc pointer into ref data */ + + " dec %%rdi \n\t" + " jnz 1b \n\t" + + " movq %%mm5, %%mm0 \n\t" + " psrlq $32, %%mm5 \n\t" + " paddw %%mm0, %%mm5 \n\t" + " movq %%mm5, %%mm0 \n\t" + " psrlq $16, %%mm5 \n\t" + " paddw %%mm0, %%mm5 \n\t" + " movd %%mm5, %%rdi \n\t" + " movsx %%di, %%rdi \n\t" + " mov %%rdi, %0 \n\t" + + " movq %%mm7, %%mm0 \n\t" + " psrlq $32, %%mm7 \n\t" + " paddd %%mm0, %%mm7 \n\t" + " movd %%mm7, %1 \n\t" + + : "=m" (XSum), + "=m" (XXSum), + "+r" (SrcData), + "+r" (RefDataPtr) + : "r" ((ogg_uint64_t)SrcStride), + "r" ((ogg_uint64_t)RefStride) + : "rdi", "memory" + ); + + /* Compute and return population variance as mis-match metric. */ + return (( (XXSum<<6) - XSum*XSum )); +} + +static void restore_fpu (void) +{ + __asm__ __volatile__ ( + " emms \n\t" + ); +} + +void dsp_mmx_init(DspFunctions *funcs) +{ + funcs->restore_fpu = restore_fpu; + funcs->sub8x8 = sub8x8__mmx; + funcs->sub8x8_128 = sub8x8_128__mmx; + funcs->sub8x8avg2 = sub8x8avg2__mmx; + funcs->intra8x8_err = intra8x8_err__mmx; + funcs->inter8x8_err = inter8x8_err__mmx; +} + +#endif /* USE_ASM */ diff --git a/Engine/lib/libtheora/lib/enc/x86_64/dsp_mmxext.c b/Engine/lib/libtheora/lib/enc/x86_64/dsp_mmxext.c new file mode 100644 index 000000000..f0aeed96e --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/x86_64/dsp_mmxext.c @@ -0,0 +1,323 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: dsp_mmxext.c 15397 2008-10-14 02:06:24Z tterribe $ + + ********************************************************************/ + +#include + +#include "../codec_internal.h" +#include "../dsp.h" + +#if defined(USE_ASM) + +typedef unsigned long long ogg_uint64_t; + +static ogg_uint32_t sad8x8__mmxext (unsigned char *ptr1, ogg_uint32_t stride1, + unsigned char *ptr2, ogg_uint32_t stride2) +{ + ogg_uint32_t DiffVal; + + __asm__ __volatile__ ( + " .balign 16 \n\t" + " pxor %%mm7, %%mm7 \n\t" /* mm7 contains the result */ + + ".rept 7 \n\t" + " movq (%1), %%mm0 \n\t" /* take 8 bytes */ + " movq (%2), %%mm1 \n\t" + " psadbw %%mm1, %%mm0 \n\t" + " add %3, %1 \n\t" /* Inc pointer into the new data */ + " paddw %%mm0, %%mm7 \n\t" /* accumulate difference... */ + " add %4, %2 \n\t" /* Inc pointer into ref data */ + ".endr \n\t" + + " movq (%1), %%mm0 \n\t" /* take 8 bytes */ + " movq (%2), %%mm1 \n\t" + " psadbw %%mm1, %%mm0 \n\t" + " paddw %%mm0, %%mm7 \n\t" /* accumulate difference... */ + " movd %%mm7, %0 \n\t" + + : "=r" (DiffVal), + "+r" (ptr1), + "+r" (ptr2) + : "r" ((ogg_uint64_t)stride1), + "r" ((ogg_uint64_t)stride2) + : "memory" + ); + + return DiffVal; +} + +static ogg_uint32_t sad8x8_thres__mmxext (unsigned char *ptr1, ogg_uint32_t stride1, + unsigned char *ptr2, ogg_uint32_t stride2, + ogg_uint32_t thres) +{ + ogg_uint32_t DiffVal; + + __asm__ __volatile__ ( + " .balign 16 \n\t" + " pxor %%mm7, %%mm7 \n\t" /* mm7 contains the result */ + + ".rept 8 \n\t" + " movq (%1), %%mm0 \n\t" /* take 8 bytes */ + " movq (%2), %%mm1 \n\t" + " psadbw %%mm1, %%mm0 \n\t" + " add %3, %1 \n\t" /* Inc pointer into the new data */ + " paddw %%mm0, %%mm7 \n\t" /* accumulate difference... */ + " add %4, %2 \n\t" /* Inc pointer into ref data */ + ".endr \n\t" + + " movd %%mm7, %0 \n\t" + + : "=r" (DiffVal), + "+r" (ptr1), + "+r" (ptr2) + : "r" ((ogg_uint64_t)stride1), + "r" ((ogg_uint64_t)stride2) + : "memory" + ); + + return DiffVal; +} + +static ogg_uint32_t sad8x8_xy2_thres__mmxext (unsigned char *SrcData, ogg_uint32_t SrcStride, + unsigned char *RefDataPtr1, + unsigned char *RefDataPtr2, ogg_uint32_t RefStride, + ogg_uint32_t thres) +{ + ogg_uint32_t DiffVal; + + __asm__ __volatile__ ( + " .balign 16 \n\t" + " pxor %%mm7, %%mm7 \n\t" /* mm7 contains the result */ + ".rept 8 \n\t" + " movq (%1), %%mm0 \n\t" /* take 8 bytes */ + " movq (%2), %%mm1 \n\t" + " movq (%3), %%mm2 \n\t" + " pavgb %%mm2, %%mm1 \n\t" + " psadbw %%mm1, %%mm0 \n\t" + + " add %4, %1 \n\t" /* Inc pointer into the new data */ + " paddw %%mm0, %%mm7 \n\t" /* accumulate difference... */ + " add %5, %2 \n\t" /* Inc pointer into ref data */ + " add %5, %3 \n\t" /* Inc pointer into ref data */ + ".endr \n\t" + + " movd %%mm7, %0 \n\t" + : "=m" (DiffVal), + "+r" (SrcData), + "+r" (RefDataPtr1), + "+r" (RefDataPtr2) + : "r" ((ogg_uint64_t)SrcStride), + "r" ((ogg_uint64_t)RefStride) + : "memory" + ); + + return DiffVal; +} + +static ogg_uint32_t row_sad8__mmxext (unsigned char *Src1, unsigned char *Src2) +{ + ogg_uint32_t MaxSad; + + __asm__ __volatile__ ( + " .balign 16 \n\t" + + " movd (%1), %%mm0 \n\t" + " movd (%2), %%mm1 \n\t" + " psadbw %%mm0, %%mm1 \n\t" + " movd 4(%1), %%mm2 \n\t" + " movd 4(%2), %%mm3 \n\t" + " psadbw %%mm2, %%mm3 \n\t" + + " pmaxsw %%mm1, %%mm3 \n\t" + " movd %%mm3, %0 \n\t" + " andl $0xffff, %0 \n\t" + + : "=m" (MaxSad), + "+r" (Src1), + "+r" (Src2) + : + : "memory" + ); + + return MaxSad; +} + +static ogg_uint32_t col_sad8x8__mmxext (unsigned char *Src1, unsigned char *Src2, + ogg_uint32_t stride) +{ + ogg_uint32_t MaxSad; + + __asm__ __volatile__ ( + " .balign 16 \n\t" + + " pxor %%mm3, %%mm3 \n\t" /* zero out mm3 for unpack */ + " pxor %%mm4, %%mm4 \n\t" /* mm4 low sum */ + " pxor %%mm5, %%mm5 \n\t" /* mm5 high sum */ + " pxor %%mm6, %%mm6 \n\t" /* mm6 low sum */ + " pxor %%mm7, %%mm7 \n\t" /* mm7 high sum */ + " mov $4, %%rdi \n\t" /* 4 rows */ + "1: \n\t" + " movq (%1), %%mm0 \n\t" /* take 8 bytes */ + " movq (%2), %%mm1 \n\t" /* take 8 bytes */ + + " movq %%mm0, %%mm2 \n\t" + " psubusb %%mm1, %%mm0 \n\t" /* A - B */ + " psubusb %%mm2, %%mm1 \n\t" /* B - A */ + " por %%mm1, %%mm0 \n\t" /* and or gives abs difference */ + " movq %%mm0, %%mm1 \n\t" + + " punpcklbw %%mm3, %%mm0 \n\t" /* unpack to higher precision for accumulation */ + " paddw %%mm0, %%mm4 \n\t" /* accumulate difference... */ + " punpckhbw %%mm3, %%mm1 \n\t" /* unpack high four bytes to higher precision */ + " paddw %%mm1, %%mm5 \n\t" /* accumulate difference... */ + " add %3, %1 \n\t" /* Inc pointer into the new data */ + " add %3, %2 \n\t" /* Inc pointer into the new data */ + + " dec %%rdi \n\t" + " jnz 1b \n\t" + + " mov $4, %%rdi \n\t" /* 4 rows */ + "2: \n\t" + " movq (%1), %%mm0 \n\t" /* take 8 bytes */ + " movq (%2), %%mm1 \n\t" /* take 8 bytes */ + + " movq %%mm0, %%mm2 \n\t" + " psubusb %%mm1, %%mm0 \n\t" /* A - B */ + " psubusb %%mm2, %%mm1 \n\t" /* B - A */ + " por %%mm1, %%mm0 \n\t" /* and or gives abs difference */ + " movq %%mm0, %%mm1 \n\t" + + " punpcklbw %%mm3, %%mm0 \n\t" /* unpack to higher precision for accumulation */ + " paddw %%mm0, %%mm6 \n\t" /* accumulate difference... */ + " punpckhbw %%mm3, %%mm1 \n\t" /* unpack high four bytes to higher precision */ + " paddw %%mm1, %%mm7 \n\t" /* accumulate difference... */ + " add %3, %1 \n\t" /* Inc pointer into the new data */ + " add %3, %2 \n\t" /* Inc pointer into the new data */ + + " dec %%rdi \n\t" + " jnz 2b \n\t" + + " pmaxsw %%mm6, %%mm7 \n\t" + " pmaxsw %%mm4, %%mm5 \n\t" + " pmaxsw %%mm5, %%mm7 \n\t" + " movq %%mm7, %%mm6 \n\t" + " psrlq $32, %%mm6 \n\t" + " pmaxsw %%mm6, %%mm7 \n\t" + " movq %%mm7, %%mm6 \n\t" + " psrlq $16, %%mm6 \n\t" + " pmaxsw %%mm6, %%mm7 \n\t" + " movd %%mm7, %0 \n\t" + " andl $0xffff, %0 \n\t" + + : "=r" (MaxSad), + "+r" (Src1), + "+r" (Src2) + : "r" ((ogg_uint64_t)stride) + : "memory", "rdi" + ); + + return MaxSad; +} + +static ogg_uint32_t inter8x8_err_xy2__mmxext (unsigned char *SrcData, ogg_uint32_t SrcStride, + unsigned char *RefDataPtr1, + unsigned char *RefDataPtr2, ogg_uint32_t RefStride) +{ + ogg_uint64_t XSum; + ogg_uint64_t XXSum; + + __asm__ __volatile__ ( + " .balign 16 \n\t" + + " pxor %%mm4, %%mm4 \n\t" + " pxor %%mm5, %%mm5 \n\t" + " pxor %%mm6, %%mm6 \n\t" + " pxor %%mm7, %%mm7 \n\t" + " mov $8, %%rdi \n\t" + "1: \n\t" + " movq (%2), %%mm0 \n\t" /* take 8 bytes */ + + " movq (%3), %%mm2 \n\t" + " movq (%4), %%mm1 \n\t" /* take average of mm2 and mm1 */ + " pavgb %%mm2, %%mm1 \n\t" + + " movq %%mm0, %%mm2 \n\t" + " movq %%mm1, %%mm3 \n\t" + + " punpcklbw %%mm6, %%mm0 \n\t" + " punpcklbw %%mm4, %%mm1 \n\t" + " punpckhbw %%mm6, %%mm2 \n\t" + " punpckhbw %%mm4, %%mm3 \n\t" + + " psubsw %%mm1, %%mm0 \n\t" + " psubsw %%mm3, %%mm2 \n\t" + + " paddw %%mm0, %%mm5 \n\t" + " paddw %%mm2, %%mm5 \n\t" + + " pmaddwd %%mm0, %%mm0 \n\t" + " pmaddwd %%mm2, %%mm2 \n\t" + + " paddd %%mm0, %%mm7 \n\t" + " paddd %%mm2, %%mm7 \n\t" + + " add %5, %2 \n\t" /* Inc pointer into src data */ + " add %6, %3 \n\t" /* Inc pointer into ref data */ + " add %6, %4 \n\t" /* Inc pointer into ref data */ + + " dec %%rdi \n\t" + " jnz 1b \n\t" + + " movq %%mm5, %%mm0 \n\t" + " psrlq $32, %%mm5 \n\t" + " paddw %%mm0, %%mm5 \n\t" + " movq %%mm5, %%mm0 \n\t" + " psrlq $16, %%mm5 \n\t" + " paddw %%mm0, %%mm5 \n\t" + " movd %%mm5, %%edi \n\t" + " movsx %%di, %%edi \n\t" + " movl %%edi, %0 \n\t" + + " movq %%mm7, %%mm0 \n\t" + " psrlq $32, %%mm7 \n\t" + " paddd %%mm0, %%mm7 \n\t" + " movd %%mm7, %1 \n\t" + + : "=m" (XSum), + "=m" (XXSum), + "+r" (SrcData), + "+r" (RefDataPtr1), + "+r" (RefDataPtr2) + : "r" ((ogg_uint64_t)SrcStride), + "r" ((ogg_uint64_t)RefStride) + : "rdi", "memory" + ); + + /* Compute and return population variance as mis-match metric. */ + return (( (XXSum<<6) - XSum*XSum )); +} + +void dsp_mmxext_init(DspFunctions *funcs) +{ + funcs->row_sad8 = row_sad8__mmxext; + funcs->col_sad8x8 = col_sad8x8__mmxext; + funcs->sad8x8 = sad8x8__mmxext; + funcs->sad8x8_thres = sad8x8_thres__mmxext; + funcs->sad8x8_xy2_thres = sad8x8_xy2_thres__mmxext; + funcs->inter8x8_err_xy2 = inter8x8_err_xy2__mmxext; +} + +#endif /* USE_ASM */ diff --git a/Engine/lib/libtheora/lib/enc/x86_64/fdct_mmx.c b/Engine/lib/libtheora/lib/enc/x86_64/fdct_mmx.c new file mode 100644 index 000000000..3765561cf --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/x86_64/fdct_mmx.c @@ -0,0 +1,342 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 1999-2006 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ********************************************************************/ + +/* mmx fdct implementation for x86_64 */ +/* $Id: fdct_mmx.c 15397 2008-10-14 02:06:24Z tterribe $ */ + +#include "theora/theora.h" +#include "../codec_internal.h" +#include "../dsp.h" + +#if defined(USE_ASM) + +static const __attribute__ ((aligned(8),used)) ogg_int64_t xC1S7 = 0x0fb15fb15fb15fb15LL; +static const __attribute__ ((aligned(8),used)) ogg_int64_t xC2S6 = 0x0ec83ec83ec83ec83LL; +static const __attribute__ ((aligned(8),used)) ogg_int64_t xC3S5 = 0x0d4dbd4dbd4dbd4dbLL; +static const __attribute__ ((aligned(8),used)) ogg_int64_t xC4S4 = 0x0b505b505b505b505LL; +static const __attribute__ ((aligned(8),used)) ogg_int64_t xC5S3 = 0x08e3a8e3a8e3a8e3aLL; +static const __attribute__ ((aligned(8),used)) ogg_int64_t xC6S2 = 0x061f861f861f861f8LL; +static const __attribute__ ((aligned(8),used)) ogg_int64_t xC7S1 = 0x031f131f131f131f1LL; + +#if defined(__MINGW32__) || defined(__CYGWIN__) || \ + defined(__OS2__) || (defined (__OpenBSD__) && !defined(__ELF__)) +# define M(a) "_" #a +#else +# define M(a) #a +#endif + +/* execute stage 1 of forward DCT */ +#define Fdct_mmx(ip0,ip1,ip2,ip3,ip4,ip5,ip6,ip7,temp) \ + " movq " #ip0 ", %%mm0 \n\t" \ + " movq " #ip1 ", %%mm1 \n\t" \ + " movq " #ip3 ", %%mm2 \n\t" \ + " movq " #ip5 ", %%mm3 \n\t" \ + " movq %%mm0, %%mm4 \n\t" \ + " movq %%mm1, %%mm5 \n\t" \ + " movq %%mm2, %%mm6 \n\t" \ + " movq %%mm3, %%mm7 \n\t" \ + \ + " paddsw " #ip7 ", %%mm0 \n\t" /* mm0 = ip0 + ip7 = is07 */ \ + " paddsw " #ip2 ", %%mm1 \n\t" /* mm1 = ip1 + ip2 = is12 */ \ + " paddsw " #ip4 ", %%mm2 \n\t" /* mm2 = ip3 + ip4 = is34 */ \ + " paddsw " #ip6 ", %%mm3 \n\t" /* mm3 = ip5 + ip6 = is56 */ \ + " psubsw " #ip7 ", %%mm4 \n\t" /* mm4 = ip0 - ip7 = id07 */ \ + " psubsw " #ip2 ", %%mm5 \n\t" /* mm5 = ip1 - ip2 = id12 */ \ + \ + " psubsw %%mm2, %%mm0 \n\t" /* mm0 = is07 - is34 */ \ + \ + " paddsw %%mm2, %%mm2 \n\t" \ + \ + " psubsw " #ip4 ", %%mm6 \n\t" /* mm6 = ip3 - ip4 = id34 */ \ + \ + " paddsw %%mm0, %%mm2 \n\t" /* mm2 = is07 + is34 = is0734 */ \ + " psubsw %%mm3, %%mm1 \n\t" /* mm1 = is12 - is56 */ \ + " movq %%mm0," #temp " \n\t" /* Save is07 - is34 to free mm0; */ \ + " paddsw %%mm3, %%mm3 \n\t" \ + " paddsw %%mm1, %%mm3 \n\t" /* mm3 = is12 + 1s56 = is1256 */ \ + \ + " psubsw " #ip6 ", %%mm7 \n\t" /* mm7 = ip5 - ip6 = id56 */ \ + /* ------------------------------------------------------------------- */ \ + " psubsw %%mm7, %%mm5 \n\t" /* mm5 = id12 - id56 */ \ + " paddsw %%mm7, %%mm7 \n\t" \ + " paddsw %%mm5, %%mm7 \n\t" /* mm7 = id12 + id56 */ \ + /* ------------------------------------------------------------------- */ \ + " psubsw %%mm3, %%mm2 \n\t" /* mm2 = is0734 - is1256 */ \ + " paddsw %%mm3, %%mm3 \n\t" \ + \ + " movq %%mm2, %%mm0 \n\t" /* make a copy */ \ + " paddsw %%mm2, %%mm3 \n\t" /* mm3 = is0734 + is1256 */ \ + \ + " pmulhw %[xC4S4], %%mm0 \n\t" /* mm0 = xC4S4 * ( is0734 - is1256 ) - ( is0734 - is1256 ) */ \ + " paddw %%mm2, %%mm0 \n\t" /* mm0 = xC4S4 * ( is0734 - is1256 ) */ \ + " psrlw $15, %%mm2 \n\t" \ + " paddw %%mm2, %%mm0 \n\t" /* Truncate mm0, now it is op[4] */ \ + \ + " movq %%mm3, %%mm2 \n\t" \ + " movq %%mm0," #ip4 " \n\t" /* save ip4, now mm0,mm2 are free */ \ + \ + " movq %%mm3, %%mm0 \n\t" \ + " pmulhw %[xC4S4], %%mm3 \n\t" /* mm3 = xC4S4 * ( is0734 +is1256 ) - ( is0734 +is1256 ) */ \ + \ + " psrlw $15, %%mm2 \n\t" \ + " paddw %%mm0, %%mm3 \n\t" /* mm3 = xC4S4 * ( is0734 +is1256 ) */ \ + " paddw %%mm2, %%mm3 \n\t" /* Truncate mm3, now it is op[0] */ \ + \ + " movq %%mm3," #ip0 " \n\t" \ + /* ------------------------------------------------------------------- */ \ + " movq " #temp ", %%mm3 \n\t" /* mm3 = irot_input_y */ \ + " pmulhw %[xC2S6], %%mm3 \n\t" /* mm3 = xC2S6 * irot_input_y - irot_input_y */ \ + \ + " movq " #temp ", %%mm2 \n\t" \ + " movq %%mm2, %%mm0 \n\t" \ + \ + " psrlw $15, %%mm2 \n\t" /* mm3 = xC2S6 * irot_input_y */ \ + " paddw %%mm0, %%mm3 \n\t" \ + \ + " paddw %%mm2, %%mm3 \n\t" /* Truncated */ \ + " movq %%mm5, %%mm0 \n\t" \ + \ + " movq %%mm5, %%mm2 \n\t" \ + " pmulhw %[xC6S2], %%mm0 \n\t" /* mm0 = xC6S2 * irot_input_x */ \ + \ + " psrlw $15, %%mm2 \n\t" \ + " paddw %%mm2, %%mm0 \n\t" /* Truncated */ \ + \ + " paddsw %%mm0, %%mm3 \n\t" /* ip[2] */ \ + " movq %%mm3," #ip2 " \n\t" /* Save ip2 */ \ + \ + " movq %%mm5, %%mm0 \n\t" \ + " movq %%mm5, %%mm2 \n\t" \ + \ + " pmulhw %[xC2S6], %%mm5 \n\t" /* mm5 = xC2S6 * irot_input_x - irot_input_x */ \ + " psrlw $15, %%mm2 \n\t" \ + \ + " movq " #temp ", %%mm3 \n\t" \ + " paddw %%mm0, %%mm5 \n\t" /* mm5 = xC2S6 * irot_input_x */ \ + \ + " paddw %%mm2, %%mm5 \n\t" /* Truncated */ \ + " movq %%mm3, %%mm2 \n\t" \ + \ + " pmulhw %[xC6S2], %%mm3 \n\t" /* mm3 = xC6S2 * irot_input_y */ \ + " psrlw $15, %%mm2 \n\t" \ + \ + " paddw %%mm2, %%mm3 \n\t" /* Truncated */ \ + " psubsw %%mm5, %%mm3 \n\t" \ + \ + " movq %%mm3," #ip6 " \n\t" \ + /* ------------------------------------------------------------------- */ \ + " movq %[xC4S4], %%mm0 \n\t" \ + " movq %%mm1, %%mm2 \n\t" \ + " movq %%mm1, %%mm3 \n\t" \ + \ + " pmulhw %%mm0, %%mm1 \n\t" /* mm0 = xC4S4 * ( is12 - is56 ) - ( is12 - is56 ) */ \ + " psrlw $15, %%mm2 \n\t" \ + \ + " paddw %%mm3, %%mm1 \n\t" /* mm0 = xC4S4 * ( is12 - is56 ) */ \ + " paddw %%mm2, %%mm1 \n\t" /* Truncate mm1, now it is icommon_product1 */ \ + \ + " movq %%mm7, %%mm2 \n\t" \ + " movq %%mm7, %%mm3 \n\t" \ + \ + " pmulhw %%mm0, %%mm7 \n\t" /* mm7 = xC4S4 * ( id12 + id56 ) - ( id12 + id56 ) */ \ + " psrlw $15, %%mm2 \n\t" \ + \ + " paddw %%mm3, %%mm7 \n\t" /* mm7 = xC4S4 * ( id12 + id56 ) */ \ + " paddw %%mm2, %%mm7 \n\t" /* Truncate mm7, now it is icommon_product2 */ \ + /* ------------------------------------------------------------------- */ \ + " pxor %%mm0, %%mm0 \n\t" /* Clear mm0 */ \ + " psubsw %%mm6, %%mm0 \n\t" /* mm0 = - id34 */ \ + \ + " psubsw %%mm7, %%mm0 \n\t" /* mm0 = - ( id34 + idcommon_product2 ) */ \ + " paddsw %%mm6, %%mm6 \n\t" \ + " paddsw %%mm0, %%mm6 \n\t" /* mm6 = id34 - icommon_product2 */ \ + \ + " psubsw %%mm1, %%mm4 \n\t" /* mm4 = id07 - icommon_product1 */ \ + " paddsw %%mm1, %%mm1 \n\t" \ + " paddsw %%mm4, %%mm1 \n\t" /* mm1 = id07 + icommon_product1 */ \ + /* ------------------------------------------------------------------- */ \ + " movq %[xC1S7], %%mm7 \n\t" \ + " movq %%mm1, %%mm2 \n\t" \ + \ + " movq %%mm1, %%mm3 \n\t" \ + " pmulhw %%mm7, %%mm1 \n\t" /* mm1 = xC1S7 * irot_input_x - irot_input_x */ \ + \ + " movq %[xC7S1], %%mm7 \n\t" \ + " psrlw $15, %%mm2 \n\t" \ + \ + " paddw %%mm3, %%mm1 \n\t" /* mm1 = xC1S7 * irot_input_x */ \ + " paddw %%mm2, %%mm1 \n\t" /* Trucated */ \ + \ + " pmulhw %%mm7, %%mm3 \n\t" /* mm3 = xC7S1 * irot_input_x */ \ + " paddw %%mm2, %%mm3 \n\t" /* Truncated */ \ + \ + " movq %%mm0, %%mm5 \n\t" \ + " movq %%mm0, %%mm2 \n\t" \ + \ + " movq %[xC1S7], %%mm7 \n\t" \ + " pmulhw %%mm7, %%mm0 \n\t" /* mm0 = xC1S7 * irot_input_y - irot_input_y */ \ + \ + " movq %[xC7S1], %%mm7 \n\t" \ + " psrlw $15, %%mm2 \n\t" \ + \ + " paddw %%mm5, %%mm0 \n\t" /* mm0 = xC1S7 * irot_input_y */ \ + " paddw %%mm2, %%mm0 \n\t" /* Truncated */ \ + \ + " pmulhw %%mm7, %%mm5 \n\t" /* mm5 = xC7S1 * irot_input_y */ \ + " paddw %%mm2, %%mm5 \n\t" /* Truncated */ \ + \ + " psubsw %%mm5, %%mm1 \n\t" /* mm1 = xC1S7 * irot_input_x - xC7S1 * irot_input_y = ip1 */ \ + " paddsw %%mm0, %%mm3 \n\t" /* mm3 = xC7S1 * irot_input_x - xC1S7 * irot_input_y = ip7 */ \ + \ + " movq %%mm1," #ip1 " \n\t" \ + " movq %%mm3," #ip7 " \n\t" \ + /* ------------------------------------------------------------------- */ \ + " movq %[xC3S5], %%mm0 \n\t" \ + " movq %[xC5S3], %%mm1 \n\t" \ + \ + " movq %%mm6, %%mm5 \n\t" \ + " movq %%mm6, %%mm7 \n\t" \ + \ + " movq %%mm4, %%mm2 \n\t" \ + " movq %%mm4, %%mm3 \n\t" \ + \ + " pmulhw %%mm0, %%mm4 \n\t" /* mm4 = xC3S5 * irot_input_x - irot_input_x */ \ + " pmulhw %%mm1, %%mm6 \n\t" /* mm6 = xC5S3 * irot_input_y - irot_input_y */ \ + \ + " psrlw $15, %%mm2 \n\t" \ + " psrlw $15, %%mm5 \n\t" \ + \ + " paddw %%mm3, %%mm4 \n\t" /* mm4 = xC3S5 * irot_input_x */ \ + " paddw %%mm7, %%mm6 \n\t" /* mm6 = xC5S3 * irot_input_y */ \ + \ + " paddw %%mm2, %%mm4 \n\t" /* Truncated */ \ + " paddw %%mm5, %%mm6 \n\t" /* Truncated */ \ + \ + " psubsw %%mm6, %%mm4 \n\t" /* ip3 */ \ + " movq %%mm4," #ip3 " \n\t" \ + \ + " movq %%mm3, %%mm4 \n\t" \ + " movq %%mm7, %%mm6 \n\t" \ + \ + " pmulhw %%mm1, %%mm3 \n\t" /* mm3 = xC5S3 * irot_input_x - irot_input_x */ \ + " pmulhw %%mm0, %%mm7 \n\t" /* mm7 = xC3S5 * irot_input_y - irot_input_y */ \ + \ + " paddw %%mm2, %%mm4 \n\t" \ + " paddw %%mm5, %%mm6 \n\t" \ + \ + " paddw %%mm4, %%mm3 \n\t" /* mm3 = xC5S3 * irot_input_x */ \ + " paddw %%mm6, %%mm7 \n\t" /* mm7 = xC3S5 * irot_input_y */ \ + \ + " paddw %%mm7, %%mm3 \n\t" /* ip5 */ \ + " movq %%mm3," #ip5 " \n\t" + +#define Transpose_mmx(ip0,ip1,ip2,ip3,ip4,ip5,ip6,ip7, \ + op0,op1,op2,op3,op4,op5,op6,op7) \ + " movq " #ip0 ", %%mm0 \n\t" /* mm0 = a0 a1 a2 a3 */ \ + " movq " #ip4 ", %%mm4 \n\t" /* mm4 = e4 e5 e6 e7 */ \ + " movq " #ip1 ", %%mm1 \n\t" /* mm1 = b0 b1 b2 b3 */ \ + " movq " #ip5 ", %%mm5 \n\t" /* mm5 = f4 f5 f6 f7 */ \ + " movq " #ip2 ", %%mm2 \n\t" /* mm2 = c0 c1 c2 c3 */ \ + " movq " #ip6 ", %%mm6 \n\t" /* mm6 = g4 g5 g6 g7 */ \ + " movq " #ip3 ", %%mm3 \n\t" /* mm3 = d0 d1 d2 d3 */ \ + " movq %%mm1," #op1 " \n\t" /* save b0 b1 b2 b3 */ \ + " movq " #ip7 ", %%mm7 \n\t" /* mm7 = h0 h1 h2 h3 */ \ + /* Transpose 2x8 block */ \ + " movq %%mm4, %%mm1 \n\t" /* mm1 = e3 e2 e1 e0 */ \ + " punpcklwd %%mm5, %%mm4 \n\t" /* mm4 = f1 e1 f0 e0 */ \ + " movq %%mm0," #op0 " \n\t" /* save a3 a2 a1 a0 */ \ + " punpckhwd %%mm5, %%mm1 \n\t" /* mm1 = f3 e3 f2 e2 */ \ + " movq %%mm6, %%mm0 \n\t" /* mm0 = g3 g2 g1 g0 */ \ + " punpcklwd %%mm7, %%mm6 \n\t" /* mm6 = h1 g1 h0 g0 */ \ + " movq %%mm4, %%mm5 \n\t" /* mm5 = f1 e1 f0 e0 */ \ + " punpckldq %%mm6, %%mm4 \n\t" /* mm4 = h0 g0 f0 e0 = MM4 */ \ + " punpckhdq %%mm6, %%mm5 \n\t" /* mm5 = h1 g1 f1 e1 = MM5 */ \ + " movq %%mm1, %%mm6 \n\t" /* mm6 = f3 e3 f2 e2 */ \ + " movq %%mm4," #op4 " \n\t" \ + " punpckhwd %%mm7, %%mm0 \n\t" /* mm0 = h3 g3 h2 g2 */ \ + " movq %%mm5," #op5 " \n\t" \ + " punpckhdq %%mm0, %%mm6 \n\t" /* mm6 = h3 g3 f3 e3 = MM7 */ \ + " movq " #op0 ", %%mm4 \n\t" /* mm4 = a3 a2 a1 a0 */ \ + " punpckldq %%mm0, %%mm1 \n\t" /* mm1 = h2 g2 f2 e2 = MM6 */ \ + " movq " #op1 ", %%mm5 \n\t" /* mm5 = b3 b2 b1 b0 */ \ + " movq %%mm4, %%mm0 \n\t" /* mm0 = a3 a2 a1 a0 */ \ + " movq %%mm6," #op7 " \n\t" \ + " punpcklwd %%mm5, %%mm0 \n\t" /* mm0 = b1 a1 b0 a0 */ \ + " movq %%mm1," #op6 " \n\t" \ + " punpckhwd %%mm5, %%mm4 \n\t" /* mm4 = b3 a3 b2 a2 */ \ + " movq %%mm2, %%mm5 \n\t" /* mm5 = c3 c2 c1 c0 */ \ + " punpcklwd %%mm3, %%mm2 \n\t" /* mm2 = d1 c1 d0 c0 */ \ + " movq %%mm0, %%mm1 \n\t" /* mm1 = b1 a1 b0 a0 */ \ + " punpckldq %%mm2, %%mm0 \n\t" /* mm0 = d0 c0 b0 a0 = MM0 */ \ + " punpckhdq %%mm2, %%mm1 \n\t" /* mm1 = d1 c1 b1 a1 = MM1 */ \ + " movq %%mm4, %%mm2 \n\t" /* mm2 = b3 a3 b2 a2 */ \ + " movq %%mm0," #op0 " \n\t" \ + " punpckhwd %%mm3, %%mm5 \n\t" /* mm5 = d3 c3 d2 c2 */ \ + " movq %%mm1," #op1 " \n\t" \ + " punpckhdq %%mm5, %%mm4 \n\t" /* mm4 = d3 c3 b3 a3 = MM3 */ \ + " punpckldq %%mm5, %%mm2 \n\t" /* mm2 = d2 c2 b2 a2 = MM2 */ \ + " movq %%mm4," #op3 " \n\t" \ + " movq %%mm2," #op2 " \n\t" + + +/* This performs a 2D Forward DCT on an 8x8 block with short + coefficients. We try to do the truncation to match the C + version. */ +static void fdct_short__mmx ( ogg_int16_t *InputData, ogg_int16_t *OutputData) +{ + ogg_int16_t __attribute__((aligned(8))) temp[8*8]; + + __asm__ __volatile__ ( + " .balign 16 \n\t" + /* + * Input data is an 8x8 block. To make processing of the data more efficent + * we will transpose the block of data to two 4x8 blocks??? + */ + Transpose_mmx ( (%0), 16(%0), 32(%0), 48(%0), 8(%0), 24(%0), 40(%0), 56(%0), + (%1), 16(%1), 32(%1), 48(%1), 8(%1), 24(%1), 40(%1), 56(%1)) + Fdct_mmx ( (%1), 16(%1), 32(%1), 48(%1), 8(%1), 24(%1), 40(%1), 56(%1), (%2)) + + Transpose_mmx (64(%0), 80(%0), 96(%0),112(%0), 72(%0), 88(%0),104(%0),120(%0), + 64(%1), 80(%1), 96(%1),112(%1), 72(%1), 88(%1),104(%1),120(%1)) + Fdct_mmx (64(%1), 80(%1), 96(%1),112(%1), 72(%1), 88(%1),104(%1),120(%1), (%2)) + + Transpose_mmx ( 0(%1), 16(%1), 32(%1), 48(%1), 64(%1), 80(%1), 96(%1),112(%1), + 0(%1), 16(%1), 32(%1), 48(%1), 64(%1), 80(%1), 96(%1),112(%1)) + Fdct_mmx ( 0(%1), 16(%1), 32(%1), 48(%1), 64(%1), 80(%1), 96(%1),112(%1), (%2)) + + Transpose_mmx ( 8(%1), 24(%1), 40(%1), 56(%1), 72(%1), 88(%1),104(%1),120(%1), + 8(%1), 24(%1), 40(%1), 56(%1), 72(%1), 88(%1),104(%1),120(%1)) + Fdct_mmx ( 8(%1), 24(%1), 40(%1), 56(%1), 72(%1), 88(%1),104(%1),120(%1), (%2)) + + " emms \n\t" + + : "+r" (InputData), + "+r" (OutputData) + : "r" (temp), + [xC1S7] "m" (xC1S7), /* gcc 3.1+ allows named asm parameters */ + [xC2S6] "m" (xC2S6), + [xC3S5] "m" (xC3S5), + [xC4S4] "m" (xC4S4), + [xC5S3] "m" (xC5S3), + [xC6S2] "m" (xC6S2), + [xC7S1] "m" (xC7S1) + : "memory" + ); +} + +/* install our implementation in the function table */ +void dsp_mmx_fdct_init(DspFunctions *funcs) +{ + funcs->fdct_short = fdct_short__mmx; +} + +#endif /* USE_ASM */ diff --git a/Engine/lib/libtheora/lib/enc/x86_64/idct_mmx.c b/Engine/lib/libtheora/lib/enc/x86_64/idct_mmx.c new file mode 100644 index 000000000..b87db6085 --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/x86_64/idct_mmx.c @@ -0,0 +1,27 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: idct_mmx.c 15397 2008-10-14 02:06:24Z tterribe $ + + ********************************************************************/ + +#include "../codec_internal.h" + +#if defined(USE_ASM) + +/* nothing implemented right now */ +void dsp_mmx_idct_init(DspFunctions *funcs) +{ +} + +#endif /* USE_ASM */ diff --git a/Engine/lib/libtheora/lib/enc/x86_64/recon_mmx.c b/Engine/lib/libtheora/lib/enc/x86_64/recon_mmx.c new file mode 100644 index 000000000..b9b86e982 --- /dev/null +++ b/Engine/lib/libtheora/lib/enc/x86_64/recon_mmx.c @@ -0,0 +1,184 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: recon_mmx.c 15397 2008-10-14 02:06:24Z tterribe $ + + ********************************************************************/ + +#include "../codec_internal.h" + +#if defined(USE_ASM) + +typedef unsigned long long ogg_uint64_t; + +static const __attribute__ ((aligned(8),used)) ogg_int64_t V128 = 0x8080808080808080LL; + +static void copy8x8__mmx (unsigned char *src, + unsigned char *dest, + ogg_uint32_t stride) +{ + __asm__ __volatile__ ( + " .balign 16 \n\t" + + " lea (%2, %2, 2), %%rdi \n\t" + + " movq (%1), %%mm0 \n\t" + " movq (%1, %2), %%mm1 \n\t" + " movq (%1, %2, 2), %%mm2 \n\t" + " movq (%1, %%rdi), %%mm3 \n\t" + + " lea (%1, %2, 4), %1 \n\t" + + " movq %%mm0, (%0) \n\t" + " movq %%mm1, (%0, %2) \n\t" + " movq %%mm2, (%0, %2, 2) \n\t" + " movq %%mm3, (%0, %%rdi) \n\t" + + " lea (%0, %2, 4), %0 \n\t" + + " movq (%1), %%mm0 \n\t" + " movq (%1, %2), %%mm1 \n\t" + " movq (%1, %2, 2), %%mm2 \n\t" + " movq (%1, %%rdi), %%mm3 \n\t" + + " movq %%mm0, (%0) \n\t" + " movq %%mm1, (%0, %2) \n\t" + " movq %%mm2, (%0, %2, 2) \n\t" + " movq %%mm3, (%0, %%rdi) \n\t" + : "+a" (dest) + : "c" (src), + "d" ((ogg_uint64_t)stride) + : "memory", "rdi" + ); +} + +static void recon_intra8x8__mmx (unsigned char *ReconPtr, ogg_int16_t *ChangePtr, + ogg_uint32_t LineStep) +{ + __asm__ __volatile__ ( + " .balign 16 \n\t" + + " movq %[V128], %%mm0 \n\t" /* Set mm0 to 0x8080808080808080 */ + + " lea 128(%1), %%rdi \n\t" /* Endpoint in input buffer */ + "1: \n\t" + " movq (%1), %%mm2 \n\t" /* First four input values */ + + " packsswb 8(%1), %%mm2 \n\t" /* pack with next(high) four values */ + " por %%mm0, %%mm0 \n\t" + " pxor %%mm0, %%mm2 \n\t" /* Convert result to unsigned (same as add 128) */ + " lea 16(%1), %1 \n\t" /* Step source buffer */ + " cmp %%rdi, %1 \n\t" /* are we done */ + + " movq %%mm2, (%0) \n\t" /* store results */ + + " lea (%0, %2), %0 \n\t" /* Step output buffer */ + " jc 1b \n\t" /* Loop back if we are not done */ + : "+r" (ReconPtr) + : "r" (ChangePtr), + "r" ((ogg_uint64_t)LineStep), + [V128] "m" (V128) + : "memory", "rdi" + ); +} + +static void recon_inter8x8__mmx (unsigned char *ReconPtr, unsigned char *RefPtr, + ogg_int16_t *ChangePtr, ogg_uint32_t LineStep) +{ + __asm__ __volatile__ ( + " .balign 16 \n\t" + + " pxor %%mm0, %%mm0 \n\t" + " lea 128(%1), %%rdi \n\t" + + "1: \n\t" + " movq (%2), %%mm2 \n\t" /* (+3 misaligned) 8 reference pixels */ + + " movq (%1), %%mm4 \n\t" /* first 4 changes */ + " movq %%mm2, %%mm3 \n\t" + " movq 8(%1), %%mm5 \n\t" /* last 4 changes */ + " punpcklbw %%mm0, %%mm2 \n\t" /* turn first 4 refs into positive 16-bit #s */ + " paddsw %%mm4, %%mm2 \n\t" /* add in first 4 changes */ + " punpckhbw %%mm0, %%mm3 \n\t" /* turn last 4 refs into positive 16-bit #s */ + " paddsw %%mm5, %%mm3 \n\t" /* add in last 4 changes */ + " add %3, %2 \n\t" /* next row of reference pixels */ + " packuswb %%mm3, %%mm2 \n\t" /* pack result to unsigned 8-bit values */ + " lea 16(%1), %1 \n\t" /* next row of changes */ + " cmp %%rdi, %1 \n\t" /* are we done? */ + + " movq %%mm2, (%0) \n\t" /* store result */ + + " lea (%0, %3), %0 \n\t" /* next row of output */ + " jc 1b \n\t" + : "+r" (ReconPtr) + : "r" (ChangePtr), + "r" (RefPtr), + "r" ((ogg_uint64_t)LineStep) + : "memory", "rdi" + ); +} + +static void recon_inter8x8_half__mmx (unsigned char *ReconPtr, unsigned char *RefPtr1, + unsigned char *RefPtr2, ogg_int16_t *ChangePtr, + ogg_uint32_t LineStep) +{ + __asm__ __volatile__ ( + " .balign 16 \n\t" + + " pxor %%mm0, %%mm0 \n\t" + " lea 128(%1), %%rdi \n\t" + + "1: \n\t" + " movq (%2), %%mm2 \n\t" /* (+3 misaligned) 8 reference pixels */ + " movq (%3), %%mm4 \n\t" /* (+3 misaligned) 8 reference pixels */ + + " movq %%mm2, %%mm3 \n\t" + " punpcklbw %%mm0, %%mm2 \n\t" /* mm2 = start ref1 as positive 16-bit #s */ + " movq %%mm4, %%mm5 \n\t" + " movq (%1), %%mm6 \n\t" /* first 4 changes */ + " punpckhbw %%mm0, %%mm3 \n\t" /* mm3 = end ref1 as positive 16-bit #s */ + " movq 8(%1), %%mm7 \n\t" /* last 4 changes */ + " punpcklbw %%mm0, %%mm4 \n\t" /* mm4 = start ref2 as positive 16-bit #s */ + " punpckhbw %%mm0, %%mm5 \n\t" /* mm5 = end ref2 as positive 16-bit #s */ + " paddw %%mm4, %%mm2 \n\t" /* mm2 = start (ref1 + ref2) */ + " paddw %%mm5, %%mm3 \n\t" /* mm3 = end (ref1 + ref2) */ + " psrlw $1, %%mm2 \n\t" /* mm2 = start (ref1 + ref2)/2 */ + " psrlw $1, %%mm3 \n\t" /* mm3 = end (ref1 + ref2)/2 */ + " paddw %%mm6, %%mm2 \n\t" /* add changes to start */ + " paddw %%mm7, %%mm3 \n\t" /* add changes to end */ + " lea 16(%1), %1 \n\t" /* next row of changes */ + " packuswb %%mm3, %%mm2 \n\t" /* pack start|end to unsigned 8-bit */ + " add %4, %2 \n\t" /* next row of reference pixels */ + " add %4, %3 \n\t" /* next row of reference pixels */ + " movq %%mm2, (%0) \n\t" /* store result */ + " add %4, %0 \n\t" /* next row of output */ + " cmp %%rdi, %1 \n\t" /* are we done? */ + " jc 1b \n\t" + : "+r" (ReconPtr) + : "r" (ChangePtr), + "r" (RefPtr1), + "r" (RefPtr2), + "r" ((ogg_uint64_t)LineStep) + : "memory", "rdi" + ); +} + +void dsp_mmx_recon_init(DspFunctions *funcs) +{ + funcs->copy8x8 = copy8x8__mmx; + funcs->recon_intra8x8 = recon_intra8x8__mmx; + funcs->recon_inter8x8 = recon_inter8x8__mmx; + funcs->recon_inter8x8_half = recon_inter8x8_half__mmx; +} + +#endif /* USE_ASM */ diff --git a/Engine/lib/libtheora/lib/internal.h b/Engine/lib/libtheora/lib/internal.h new file mode 100644 index 000000000..0413a355a --- /dev/null +++ b/Engine/lib/libtheora/lib/internal.h @@ -0,0 +1,491 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: internal.h 15469 2008-10-30 12:49:42Z tterribe $ + + ********************************************************************/ + +#if !defined(_internal_H) +# define _internal_H (1) +# include +# if defined(HAVE_CONFIG_H) +# include +# endif +# include "theora/codec.h" +# include "theora/theora.h" +# include "dec/ocintrin.h" +# include "dec/huffman.h" +# include "dec/quant.h" + +/*Thank you Microsoft, I know the order of operations.*/ +# if defined(_MSC_VER) +# pragma warning(disable:4554) /* order of operations */ +# pragma warning(disable:4799) /* disable missing EMMS warnings */ +# endif + +/*This library's version.*/ +# define OC_VENDOR_STRING "Xiph.Org libTheora I 20081020 3 2 1" + +/*Theora bitstream version.*/ +# define TH_VERSION_MAJOR (3) +# define TH_VERSION_MINOR (2) +# define TH_VERSION_SUB (1) +# define TH_VERSION_CHECK(_info,_maj,_min,_sub) \ + ((_info)->version_major>(_maj)||(_info)->version_major==(_maj)&& \ + ((_info)->version_minor>(_min)||(_info)->version_minor==(_min)&& \ + (_info)->version_subminor>=(_sub))) + +/*A keyframe.*/ +#define OC_INTRA_FRAME (0) +/*A predicted frame.*/ +#define OC_INTER_FRAME (1) +/*A frame of unknown type (frame type decision has not yet been made).*/ +#define OC_UNKWN_FRAME (-1) + +/*The amount of padding to add to the reconstructed frame buffers on all + sides. + This is used to allow unrestricted motion vectors without special casing. + This must be a multiple of 2.*/ +#define OC_UMV_PADDING (16) + +/*Frame classification indices.*/ +/*The previous golden frame.*/ +#define OC_FRAME_GOLD (0) +/*The previous frame.*/ +#define OC_FRAME_PREV (1) +/*The current frame.*/ +#define OC_FRAME_SELF (2) + +/*The input or output buffer.*/ +#define OC_FRAME_IO (3) + +/*Macroblock modes.*/ +/*Macro block is invalid: It is never coded.*/ +#define OC_MODE_INVALID (-1) +/*Encoded difference from the same macro block in the previous frame.*/ +#define OC_MODE_INTER_NOMV (0) +/*Encoded with no motion compensated prediction.*/ +#define OC_MODE_INTRA (1) +/*Encoded difference from the previous frame offset by the given motion + vector.*/ +#define OC_MODE_INTER_MV (2) +/*Encoded difference from the previous frame offset by the last coded motion + vector.*/ +#define OC_MODE_INTER_MV_LAST (3) +/*Encoded difference from the previous frame offset by the second to last + coded motion vector.*/ +#define OC_MODE_INTER_MV_LAST2 (4) +/*Encoded difference from the same macro block in the previous golden + frame.*/ +#define OC_MODE_GOLDEN_NOMV (5) +/*Encoded difference from the previous golden frame offset by the given motion + vector.*/ +#define OC_MODE_GOLDEN_MV (6) +/*Encoded difference from the previous frame offset by the individual motion + vectors given for each block.*/ +#define OC_MODE_INTER_MV_FOUR (7) +/*The number of (coded) modes.*/ +#define OC_NMODES (8) + +/*Macro block is not coded.*/ +#define OC_MODE_NOT_CODED (8) + +/*Predictor bit flags.*/ +/*Left.*/ +#define OC_PL (1) +/*Upper-left.*/ +#define OC_PUL (2) +/*Up.*/ +#define OC_PU (4) +/*Upper-right.*/ +#define OC_PUR (8) + +/*Constants for the packet state machine common between encoder and decoder.*/ + +/*Next packet to emit/read: Codec info header.*/ +#define OC_PACKET_INFO_HDR (-3) +/*Next packet to emit/read: Comment header.*/ +#define OC_PACKET_COMMENT_HDR (-2) +/*Next packet to emit/read: Codec setup header.*/ +#define OC_PACKET_SETUP_HDR (-1) +/*No more packets to emit/read.*/ +#define OC_PACKET_DONE (INT_MAX) + + + +typedef struct oc_theora_state oc_theora_state; + + + +/*A map from a super block to fragment numbers.*/ +typedef int oc_sb_map[4][4]; +/*A map from a macro block to fragment numbers.*/ +typedef int oc_mb_map[3][4]; +/*A motion vector.*/ +typedef signed char oc_mv[2]; + + + +/*Super block information. + Super blocks are 32x32 segments of pixels in a single color plane indexed + in image order. + Internally, super blocks are broken up into four quadrants, each of which + contains a 2x2 pattern of blocks, each of which is an 8x8 block of pixels. + Quadrants, and the blocks within them, are indexed in a special order called + a "Hilbert curve" within the super block. + + In order to differentiate between the Hilbert-curve indexing strategy and + the regular image order indexing strategy, blocks indexed in image order + are called "fragments". + Fragments are indexed in image order, left to right, then bottom to top, + from Y plane to Cb plane to Cr plane.*/ +typedef struct{ + unsigned coded_fully:1; + unsigned coded_partially:1; + unsigned quad_valid:4; + oc_sb_map map; +}oc_sb; + + + +/*Macro block information. + The co-located fragments in all image planes corresponding to the location of + a single luma plane super block quadrant forms a macro block. + Thus there is only a single set of macro blocks for all planes, which + contains between 6 and 12 fragments, depending on the pixel format. + Therefore macro block information is kept in a separate array from super + blocks, to avoid unused space in the other planes.*/ +typedef struct{ + /*The current macro block mode. + A negative number indicates the macro block lies entirely outside the + coded frame.*/ + int mode; + /*The X location of the macro block's upper-left hand pixel.*/ + int x; + /*The Y location of the macro block's upper-right hand pixel.*/ + int y; + /*The fragments that belong to this macro block in each color plane. + Fragments are stored in image order (left to right then top to bottom). + When chroma components are decimated, the extra fragments have an index of + -1.*/ + oc_mb_map map; +}oc_mb; + + + +/*Information about a fragment which intersects the border of the displayable + region. + This marks which pixels belong to the displayable region, and is used to + ensure that pixels outside of this region are never referenced. + This allows applications to pass in buffers that are really the size of the + displayable region without causing a seg fault.*/ +typedef struct{ + /*A bit mask marking which pixels are in the displayable region. + Pixel (x,y) corresponds to bit (y<<3|x).*/ + ogg_int64_t mask; + /*The number of pixels in the displayable region. + This is always positive, and always less than 64.*/ + int npixels; +}oc_border_info; + + + +/*Fragment information.*/ +typedef struct{ + /*A flag indicating whether or not this fragment is coded.*/ + unsigned coded:1; + /*A flag indicating that all of this fragment lies outside the displayable + region of the frame. + Note the contrast with an invalid macro block, which is outside the coded + frame, not just the displayable one.*/ + unsigned invalid:1; + /*The quality index used for this fragment's AC coefficients.*/ + unsigned qi:6; + /*The mode of the macroblock this fragment belongs to. + Note that the C standard requires an explicit signed keyword for bitfield + types, since some compilers may treat them as unsigned without it.*/ + signed int mbmode:8; + /*The prediction-corrected DC component. + Note that the C standard requires an explicit signed keyword for bitfield + types, since some compilers may treat them as unsigned without it.*/ + signed int dc:16; + /*A pointer to the portion of an image covered by this fragment in several + images. + The first three are reconstructed frame buffers, while the last is the + input image buffer. + The appropriate stride value is determined by the color plane the fragment + belongs in.*/ + unsigned char *buffer[4]; + /*Information for fragments which lie partially outside the displayable + region. + For fragments completely inside or outside this region, this is NULL.*/ + oc_border_info *border; + /*The motion vector used for this fragment.*/ + oc_mv mv; +}oc_fragment; + + + +/*A description of each fragment plane.*/ +typedef struct{ + /*The number of fragments in the horizontal direction.*/ + int nhfrags; + /*The number of fragments in the vertical direction.*/ + int nvfrags; + /*The offset of the first fragment in the plane.*/ + int froffset; + /*The total number of fragments in the plane.*/ + int nfrags; + /*The number of super blocks in the horizontal direction.*/ + int nhsbs; + /*The number of super blocks in the vertical direction.*/ + int nvsbs; + /*The offset of the first super block in the plane.*/ + int sboffset; + /*The total number of super blocks in the plane.*/ + int nsbs; +}oc_fragment_plane; + + + +/*The shared (encoder and decoder) functions that have accelerated variants.*/ +typedef struct{ + void (*frag_recon_intra)(unsigned char *_dst,int _dst_ystride, + const ogg_int16_t *_residue); + void (*frag_recon_inter)(unsigned char *_dst,int _dst_ystride, + const unsigned char *_src,int _src_ystride,const ogg_int16_t *_residue); + void (*frag_recon_inter2)(unsigned char *_dst,int _dst_ystride, + const unsigned char *_src1,int _src1_ystride,const unsigned char *_src2, + int _src2_ystride,const ogg_int16_t *_residue); + void (*state_frag_copy)(const oc_theora_state *_state, + const int *_fragis,int _nfragis,int _dst_frame,int _src_frame,int _pli); + void (*state_frag_recon)(oc_theora_state *_state,oc_fragment *_frag, + int _pli,ogg_int16_t _dct_coeffs[128],int _last_zzi,int _ncoefs, + ogg_uint16_t _dc_iquant,const ogg_uint16_t _ac_iquant[64]); + void (*restore_fpu)(void); + void (*state_loop_filter_frag_rows)(oc_theora_state *_state,int *_bv, + int _refi,int _pli,int _fragy0,int _fragy_end); +}oc_base_opt_vtable; + + + +/*Common state information between the encoder and decoder.*/ +struct oc_theora_state{ + /*The stream information.*/ + th_info info; + /*Table for shared accelerated functions.*/ + oc_base_opt_vtable opt_vtable; + /*CPU flags to detect the presence of extended instruction sets.*/ + ogg_uint32_t cpu_flags; + /*The fragment plane descriptions.*/ + oc_fragment_plane fplanes[3]; + /*The total number of fragments in a single frame.*/ + int nfrags; + /*The list of fragments, indexed in image order.*/ + oc_fragment *frags; + /*The total number of super blocks in a single frame.*/ + int nsbs; + /*The list of super blocks, indexed in image order.*/ + oc_sb *sbs; + /*The number of macro blocks in the X direction.*/ + int nhmbs; + /*The number of macro blocks in the Y direction.*/ + int nvmbs; + /*The total number of macro blocks.*/ + int nmbs; + /*The list of macro blocks, indexed in super block order. + That is, the macro block corresponding to the macro block mbi in (luma + plane) super block sbi is (sbi<<2|mbi).*/ + oc_mb *mbs; + /*The list of coded fragments, in coded order.*/ + int *coded_fragis; + /*The number of coded fragments in each plane.*/ + int ncoded_fragis[3]; + /*The list of uncoded fragments. + This just past the end of the list, which is in reverse order, and + uses the same block of allocated storage as the coded_fragis list.*/ + int *uncoded_fragis; + /*The number of uncoded fragments in each plane.*/ + int nuncoded_fragis[3]; + /*The list of coded macro blocks in the Y plane, in coded order.*/ + int *coded_mbis; + /*The number of coded macro blocks in the Y plane.*/ + int ncoded_mbis; + /*A copy of the image data used to fill the input pointers in each fragment. + If the data pointers or strides change, these input pointers must be + re-populated.*/ + th_ycbcr_buffer input; + /*The number of unique border patterns.*/ + int nborders; + /*The storage for the border info for all border fragments. + This data is pointed to from the appropriate fragments.*/ + oc_border_info borders[16]; + /*The index of the buffers being used for each OC_FRAME_* reference frame.*/ + int ref_frame_idx[3]; + /*The actual buffers used for the previously decoded frames.*/ + th_ycbcr_buffer ref_frame_bufs[3]; + /*The storage for the reference frame buffers.*/ + unsigned char *ref_frame_data; + /*The frame number of the last keyframe.*/ + ogg_int64_t keyframe_num; + /*The frame number of the current frame.*/ + ogg_int64_t curframe_num; + /*The granpos of the current frame.*/ + ogg_int64_t granpos; + /*The type of the current frame.*/ + int frame_type; + /*The quality indices of the current frame.*/ + int qis[3]; + /*The number of quality indices used in the current frame.*/ + int nqis; + /*The dequantization tables.*/ + oc_quant_table *dequant_tables[2][3]; + oc_quant_tables dequant_table_data[2][3]; + /*Loop filter strength parameters.*/ + unsigned char loop_filter_limits[64]; +}; + + + +/*The function type used to fill in the chroma plane motion vectors for a + macro block when 4 different motion vectors are specified in the luma + plane. + _cbmvs: The chroma block-level motion vectors to fill in. + _lmbmv: The luma macro-block level motion vector to fill in for use in + prediction. + _lbmvs: The luma block-level motion vectors.*/ +typedef void (*oc_set_chroma_mvs_func)(oc_mv _cbmvs[4],const oc_mv _lbmvs[4]); + + + +/*A map from the index in the zig zag scan to the coefficient number in a + block. + The extra 64 entries send out of bounds indexes to index 64. + This is used to safely ignore invalid zero runs when decoding + coefficients.*/ +extern const int OC_FZIG_ZAG[128]; +/*A map from the coefficient number in a block to its index in the zig zag + scan.*/ +extern const int OC_IZIG_ZAG[64]; +/*The predictor frame to use for each macro block mode.*/ +extern const int OC_FRAME_FOR_MODE[OC_NMODES]; +/*A map from physical macro block ordering to bitstream macro block + ordering within a super block.*/ +extern const int OC_MB_MAP[2][2]; +/*A list of the indices in the oc_mb.map array that can be valid for each of + the various chroma decimation types.*/ +extern const int OC_MB_MAP_IDXS[TH_PF_NFORMATS][12]; +/*The number of indices in the oc_mb.map array that can be valid for each of + the various chroma decimation types.*/ +extern const int OC_MB_MAP_NIDXS[TH_PF_NFORMATS]; +/*A table of functions used to fill in the Cb,Cr plane motion vectors for a + macro block when 4 different motion vectors are specified in the luma + plane.*/ +extern const oc_set_chroma_mvs_func OC_SET_CHROMA_MVS_TABLE[TH_PF_NFORMATS]; + + + +int oc_ilog(unsigned _v); +void **oc_malloc_2d(size_t _height,size_t _width,size_t _sz); +void **oc_calloc_2d(size_t _height,size_t _width,size_t _sz); +void oc_free_2d(void *_ptr); + +void oc_ycbcr_buffer_flip(th_ycbcr_buffer _dst, + const th_ycbcr_buffer _src); + +int oc_dct_token_skip(int _token,int _extra_bits); + +int oc_frag_pred_dc(const oc_fragment *_frag, + const oc_fragment_plane *_fplane,int _x,int _y,int _pred_last[3]); + +int oc_state_init(oc_theora_state *_state,const th_info *_info); +void oc_state_clear(oc_theora_state *_state); +void oc_state_vtable_init_c(oc_theora_state *_state); +void oc_state_borders_fill_rows(oc_theora_state *_state,int _refi,int _pli, + int _y0,int _yend); +void oc_state_borders_fill_caps(oc_theora_state *_state,int _refi,int _pli); +void oc_state_borders_fill(oc_theora_state *_state,int _refi); +void oc_state_fill_buffer_ptrs(oc_theora_state *_state,int _buf_idx, + th_ycbcr_buffer _img); +int oc_state_mbi_for_pos(oc_theora_state *_state,int _mbx,int _mby); +int oc_state_get_mv_offsets(oc_theora_state *_state,int *_offsets, + int _dx,int _dy,int _ystride,int _pli); + +int oc_state_loop_filter_init(oc_theora_state *_state,int *_bv); +void oc_state_loop_filter(oc_theora_state *_state,int _frame); +#if defined(OC_DUMP_IMAGES) +int oc_state_dump_frame(const oc_theora_state *_state,int _frame, + const char *_suf); +#endif + +/*Shared accelerated functions.*/ +void oc_frag_recon_intra(const oc_theora_state *_state, + unsigned char *_dst,int _dst_ystride,const ogg_int16_t *_residue); +void oc_frag_recon_inter(const oc_theora_state *_state, + unsigned char *_dst,int _dst_ystride, + const unsigned char *_src,int _src_ystride,const ogg_int16_t *_residue); +void oc_frag_recon_inter2(const oc_theora_state *_state, + unsigned char *_dst,int _dst_ystride, + const unsigned char *_src1,int _src1_ystride,const unsigned char *_src2, + int _src2_ystride,const ogg_int16_t *_residue); +void oc_state_frag_copy(const oc_theora_state *_state,const int *_fragis, + int _nfragis,int _dst_frame,int _src_frame,int _pli); +void oc_state_frag_recon(oc_theora_state *_state,oc_fragment *_frag, + int _pli,ogg_int16_t _dct_coeffs[128],int _last_zzi,int _ncoefs, + ogg_uint16_t _dc_iquant,const ogg_uint16_t _ac_iquant[64]); +void oc_state_loop_filter_frag_rows(oc_theora_state *_state,int *_bv, + int _refi,int _pli,int _fragy0,int _fragy_end); +void oc_restore_fpu(const oc_theora_state *_state); + +/*Default pure-C implementations.*/ +void oc_frag_recon_intra_c(unsigned char *_dst,int _dst_ystride, + const ogg_int16_t *_residue); +void oc_frag_recon_inter_c(unsigned char *_dst,int _dst_ystride, + const unsigned char *_src,int _src_ystride,const ogg_int16_t *_residue); +void oc_frag_recon_inter2_c(unsigned char *_dst,int _dst_ystride, + const unsigned char *_src1,int _src1_ystride,const unsigned char *_src2, + int _src2_ystride,const ogg_int16_t *_residue); +void oc_state_frag_copy_c(const oc_theora_state *_state,const int *_fragis, + int _nfragis,int _dst_frame,int _src_frame,int _pli); +void oc_state_frag_recon_c(oc_theora_state *_state,oc_fragment *_frag, + int _pli,ogg_int16_t _dct_coeffs[128],int _last_zzi,int _ncoefs, + ogg_uint16_t _dc_iquant,const ogg_uint16_t _ac_iquant[64]); +void oc_state_loop_filter_frag_rows_c(oc_theora_state *_state,int *_bv, + int _refi,int _pli,int _fragy0,int _fragy_end); +void oc_restore_fpu_c(void); + +/*We need a way to call a few encoder functions without introducing a link-time + dependency into the decoder, while still allowing the old alpha API which + does not distinguish between encoder and decoder objects to be used. + We do this by placing a function table at the start of the encoder object + which can dispatch into the encoder library. + We do a similar thing for the decoder in case we ever decide to split off a + common base library.*/ +typedef void (*oc_state_clear_func)(theora_state *_th); +typedef int (*oc_state_control_func)(theora_state *th,int req, + void *buf,size_t buf_sz); +typedef ogg_int64_t (*oc_state_granule_frame_func)(theora_state *_th, + ogg_int64_t _granulepos); +typedef double (*oc_state_granule_time_func)(theora_state *_th, + ogg_int64_t _granulepos); + +typedef struct oc_state_dispatch_vtbl oc_state_dispatch_vtbl; + +struct oc_state_dispatch_vtbl{ + oc_state_clear_func clear; + oc_state_control_func control; + oc_state_granule_frame_func granule_frame; + oc_state_granule_time_func granule_time; +}; + +#endif diff --git a/Engine/lib/libvorbis/CHANGES b/Engine/lib/libvorbis/CHANGES new file mode 100644 index 000000000..e01cff7ef --- /dev/null +++ b/Engine/lib/libvorbis/CHANGES @@ -0,0 +1,48 @@ +libvorbis 1.2.0 (2007-07-25) -- "Xiph.Org libVorbis I 20070622" + + * new ov_fopen() convenience call that avoids the common + stdio conflicts with ov_open() and MSVC runtimes. + * libvorbisfile now handles multiplexed streams + * improve robustness to corrupt input streams + * fix a minor encoder bug + * updated RTP draft + * build system updates + * minor corrections to the specification + +libvorbis 1.1.2 (2005-11-27) -- "Xiph.Org libVorbis I 20050304" + + * fix a serious encoder bug with gcc 4 optimized builds + * documentation and spec fixes + * updated VS2003 and XCode builds + * new draft RTP encapsulation spec + +libvorbis 1.1.1 (2005-06-27) -- "Xiph.Org libVorbis I 20050304" + + * bug fix to the bitrate management encoder interface + * bug fix to properly set packetno field in the encoder + * new draft RTP encapsulation spec + * library API documentation improvements + +libvorbis 1.1.0 (2004-09-22) -- "Xiph.Org libVorbis I 20040629" + + * merges tuning improvements from Aoyumi's aoTuV with fixups + * new managed bitrate (CBR) mode support + * new vorbis_encoder_ctl() interface + * extensive documentation updates + * application/ogg mimetype is now official + * autotools cleanup from Thomas Vander Stichele + * SymbianOS build support from Colin Ward at CSIRO + * various bugfixes + * various packaging improvements + +libvorbis 1.0.1 (2003-11-17) -- "Xiph.Org libVorbis I 20030909" + + * numerous bug fixes + * specification corrections + * new crosslap and halfrate APIs for game use + * packaging and build updates + +libvorbis 1.0.0 (2002-07-19) -- "Xiph.Org libVorbis I 20020717" + + * first stable release + diff --git a/Engine/lib/libvorbis/include/vorbis/codec.h b/Engine/lib/libvorbis/include/vorbis/codec.h new file mode 100644 index 000000000..b23fe0af7 --- /dev/null +++ b/Engine/lib/libvorbis/include/vorbis/codec.h @@ -0,0 +1,241 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + + ******************************************************************** + + function: libvorbis codec headers + last mod: $Id: codec.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#ifndef _vorbis_codec_h_ +#define _vorbis_codec_h_ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#include + +typedef struct vorbis_info{ + int version; + int channels; + long rate; + + /* The below bitrate declarations are *hints*. + Combinations of the three values carry the following implications: + + all three set to the same value: + implies a fixed rate bitstream + only nominal set: + implies a VBR stream that averages the nominal bitrate. No hard + upper/lower limit + upper and or lower set: + implies a VBR bitstream that obeys the bitrate limits. nominal + may also be set to give a nominal rate. + none set: + the coder does not care to speculate. + */ + + long bitrate_upper; + long bitrate_nominal; + long bitrate_lower; + long bitrate_window; + + void *codec_setup; +} vorbis_info; + +/* vorbis_dsp_state buffers the current vorbis audio + analysis/synthesis state. The DSP state belongs to a specific + logical bitstream ****************************************************/ +typedef struct vorbis_dsp_state{ + int analysisp; + vorbis_info *vi; + + float **pcm; + float **pcmret; + int pcm_storage; + int pcm_current; + int pcm_returned; + + int preextrapolate; + int eofflag; + + long lW; + long W; + long nW; + long centerW; + + ogg_int64_t granulepos; + ogg_int64_t sequence; + + ogg_int64_t glue_bits; + ogg_int64_t time_bits; + ogg_int64_t floor_bits; + ogg_int64_t res_bits; + + void *backend_state; +} vorbis_dsp_state; + +typedef struct vorbis_block{ + /* necessary stream state for linking to the framing abstraction */ + float **pcm; /* this is a pointer into local storage */ + oggpack_buffer opb; + + long lW; + long W; + long nW; + int pcmend; + int mode; + + int eofflag; + ogg_int64_t granulepos; + ogg_int64_t sequence; + vorbis_dsp_state *vd; /* For read-only access of configuration */ + + /* local storage to avoid remallocing; it's up to the mapping to + structure it */ + void *localstore; + long localtop; + long localalloc; + long totaluse; + struct alloc_chain *reap; + + /* bitmetrics for the frame */ + long glue_bits; + long time_bits; + long floor_bits; + long res_bits; + + void *internal; + +} vorbis_block; + +/* vorbis_block is a single block of data to be processed as part of +the analysis/synthesis stream; it belongs to a specific logical +bitstream, but is independant from other vorbis_blocks belonging to +that logical bitstream. *************************************************/ + +struct alloc_chain{ + void *ptr; + struct alloc_chain *next; +}; + +/* vorbis_info contains all the setup information specific to the + specific compression/decompression mode in progress (eg, + psychoacoustic settings, channel setup, options, codebook + etc). vorbis_info and substructures are in backends.h. +*********************************************************************/ + +/* the comments are not part of vorbis_info so that vorbis_info can be + static storage */ +typedef struct vorbis_comment{ + /* unlimited user comment fields. libvorbis writes 'libvorbis' + whatever vendor is set to in encode */ + char **user_comments; + int *comment_lengths; + int comments; + char *vendor; + +} vorbis_comment; + + +/* libvorbis encodes in two abstraction layers; first we perform DSP + and produce a packet (see docs/analysis.txt). The packet is then + coded into a framed OggSquish bitstream by the second layer (see + docs/framing.txt). Decode is the reverse process; we sync/frame + the bitstream and extract individual packets, then decode the + packet back into PCM audio. + + The extra framing/packetizing is used in streaming formats, such as + files. Over the net (such as with UDP), the framing and + packetization aren't necessary as they're provided by the transport + and the streaming layer is not used */ + +/* Vorbis PRIMITIVES: general ***************************************/ + +extern void vorbis_info_init(vorbis_info *vi); +extern void vorbis_info_clear(vorbis_info *vi); +extern int vorbis_info_blocksize(vorbis_info *vi,int zo); +extern void vorbis_comment_init(vorbis_comment *vc); +extern void vorbis_comment_add(vorbis_comment *vc, char *comment); +extern void vorbis_comment_add_tag(vorbis_comment *vc, + char *tag, char *contents); +extern char *vorbis_comment_query(vorbis_comment *vc, char *tag, int count); +extern int vorbis_comment_query_count(vorbis_comment *vc, char *tag); +extern void vorbis_comment_clear(vorbis_comment *vc); + +extern int vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb); +extern int vorbis_block_clear(vorbis_block *vb); +extern void vorbis_dsp_clear(vorbis_dsp_state *v); +extern double vorbis_granule_time(vorbis_dsp_state *v, + ogg_int64_t granulepos); + +/* Vorbis PRIMITIVES: analysis/DSP layer ****************************/ + +extern int vorbis_analysis_init(vorbis_dsp_state *v,vorbis_info *vi); +extern int vorbis_commentheader_out(vorbis_comment *vc, ogg_packet *op); +extern int vorbis_analysis_headerout(vorbis_dsp_state *v, + vorbis_comment *vc, + ogg_packet *op, + ogg_packet *op_comm, + ogg_packet *op_code); +extern float **vorbis_analysis_buffer(vorbis_dsp_state *v,int vals); +extern int vorbis_analysis_wrote(vorbis_dsp_state *v,int vals); +extern int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb); +extern int vorbis_analysis(vorbis_block *vb,ogg_packet *op); + +extern int vorbis_bitrate_addblock(vorbis_block *vb); +extern int vorbis_bitrate_flushpacket(vorbis_dsp_state *vd, + ogg_packet *op); + +/* Vorbis PRIMITIVES: synthesis layer *******************************/ +extern int vorbis_synthesis_idheader(ogg_packet *op); +extern int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc, + ogg_packet *op); + +extern int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi); +extern int vorbis_synthesis_restart(vorbis_dsp_state *v); +extern int vorbis_synthesis(vorbis_block *vb,ogg_packet *op); +extern int vorbis_synthesis_trackonly(vorbis_block *vb,ogg_packet *op); +extern int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb); +extern int vorbis_synthesis_pcmout(vorbis_dsp_state *v,float ***pcm); +extern int vorbis_synthesis_lapout(vorbis_dsp_state *v,float ***pcm); +extern int vorbis_synthesis_read(vorbis_dsp_state *v,int samples); +extern long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op); + +extern int vorbis_synthesis_halfrate(vorbis_info *v,int flag); +extern int vorbis_synthesis_halfrate_p(vorbis_info *v); + +/* Vorbis ERRORS and return codes ***********************************/ + +#define OV_FALSE -1 +#define OV_EOF -2 +#define OV_HOLE -3 + +#define OV_EREAD -128 +#define OV_EFAULT -129 +#define OV_EIMPL -130 +#define OV_EINVAL -131 +#define OV_ENOTVORBIS -132 +#define OV_EBADHEADER -133 +#define OV_EVERSION -134 +#define OV_ENOTAUDIO -135 +#define OV_EBADPACKET -136 +#define OV_EBADLINK -137 +#define OV_ENOSEEK -138 + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif + diff --git a/Engine/lib/libvorbis/include/vorbis/vorbisenc.h b/Engine/lib/libvorbis/include/vorbis/vorbisenc.h new file mode 100644 index 000000000..82797a496 --- /dev/null +++ b/Engine/lib/libvorbis/include/vorbis/vorbisenc.h @@ -0,0 +1,112 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: vorbis encode-engine setup + last mod: $Id: vorbisenc.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#ifndef _OV_ENC_H_ +#define _OV_ENC_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#include "codec.h" + +extern int vorbis_encode_init(vorbis_info *vi, + long channels, + long rate, + + long max_bitrate, + long nominal_bitrate, + long min_bitrate); + +extern int vorbis_encode_setup_managed(vorbis_info *vi, + long channels, + long rate, + + long max_bitrate, + long nominal_bitrate, + long min_bitrate); + +extern int vorbis_encode_setup_vbr(vorbis_info *vi, + long channels, + long rate, + + float quality /* quality level from 0. (lo) to 1. (hi) */ + ); + +extern int vorbis_encode_init_vbr(vorbis_info *vi, + long channels, + long rate, + + float base_quality /* quality level from 0. (lo) to 1. (hi) */ + ); + +extern int vorbis_encode_setup_init(vorbis_info *vi); + +extern int vorbis_encode_ctl(vorbis_info *vi,int number,void *arg); + + /* deprecated rate management supported only for compatability */ +#define OV_ECTL_RATEMANAGE_GET 0x10 +#define OV_ECTL_RATEMANAGE_SET 0x11 +#define OV_ECTL_RATEMANAGE_AVG 0x12 +#define OV_ECTL_RATEMANAGE_HARD 0x13 + +struct ovectl_ratemanage_arg { + int management_active; + + long bitrate_hard_min; + long bitrate_hard_max; + double bitrate_hard_window; + + long bitrate_av_lo; + long bitrate_av_hi; + double bitrate_av_window; + double bitrate_av_window_center; +}; + + + /* new rate setup */ +#define OV_ECTL_RATEMANAGE2_GET 0x14 +#define OV_ECTL_RATEMANAGE2_SET 0x15 + +struct ovectl_ratemanage2_arg { + int management_active; + + long bitrate_limit_min_kbps; + long bitrate_limit_max_kbps; + long bitrate_limit_reservoir_bits; + double bitrate_limit_reservoir_bias; + + long bitrate_average_kbps; + double bitrate_average_damping; +}; + + + +#define OV_ECTL_LOWPASS_GET 0x20 +#define OV_ECTL_LOWPASS_SET 0x21 + +#define OV_ECTL_IBLOCK_GET 0x30 +#define OV_ECTL_IBLOCK_SET 0x31 + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif + + diff --git a/Engine/lib/libvorbis/include/vorbis/vorbisfile.h b/Engine/lib/libvorbis/include/vorbis/vorbisfile.h new file mode 100644 index 000000000..af4e22f96 --- /dev/null +++ b/Engine/lib/libvorbis/include/vorbis/vorbisfile.h @@ -0,0 +1,145 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: stdio-based convenience library for opening/seeking/decoding + last mod: $Id: vorbisfile.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#ifndef _OV_FILE_H_ +#define _OV_FILE_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#include +#include "codec.h" + +/* The function prototypes for the callbacks are basically the same as for + * the stdio functions fread, fseek, fclose, ftell. + * The one difference is that the FILE * arguments have been replaced with + * a void * - this is to be used as a pointer to whatever internal data these + * functions might need. In the stdio case, it's just a FILE * cast to a void * + * + * If you use other functions, check the docs for these functions and return + * the right values. For seek_func(), you *MUST* return -1 if the stream is + * unseekable + */ +typedef struct { + size_t (*read_func) (void *ptr, size_t size, size_t nmemb, void *datasource); + int (*seek_func) (void *datasource, ogg_int64_t offset, int whence); + int (*close_func) (void *datasource); + long (*tell_func) (void *datasource); +} ov_callbacks; + +#define NOTOPEN 0 +#define PARTOPEN 1 +#define OPENED 2 +#define STREAMSET 3 +#define INITSET 4 + +typedef struct OggVorbis_File { + void *datasource; /* Pointer to a FILE *, etc. */ + int seekable; + ogg_int64_t offset; + ogg_int64_t end; + ogg_sync_state oy; + + /* If the FILE handle isn't seekable (eg, a pipe), only the current + stream appears */ + int links; + ogg_int64_t *offsets; + ogg_int64_t *dataoffsets; + long *serialnos; + ogg_int64_t *pcmlengths; /* overloaded to maintain binary + compatability; x2 size, stores both + beginning and end values */ + vorbis_info *vi; + vorbis_comment *vc; + + /* Decoding working state local storage */ + ogg_int64_t pcm_offset; + int ready_state; + long current_serialno; + int current_link; + + double bittrack; + double samptrack; + + ogg_stream_state os; /* take physical pages, weld into a logical + stream of packets */ + vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */ + vorbis_block vb; /* local working space for packet->PCM decode */ + + ov_callbacks callbacks; + +} OggVorbis_File; + + +extern int ov_clear(OggVorbis_File *vf); +extern int ov_fopen(char *path,OggVorbis_File *vf); +extern int ov_open(FILE *f,OggVorbis_File *vf,char *initial,long ibytes); +extern int ov_open_callbacks(void *datasource, OggVorbis_File *vf, + char *initial, long ibytes, ov_callbacks callbacks); + +extern int ov_test(FILE *f,OggVorbis_File *vf,char *initial,long ibytes); +extern int ov_test_callbacks(void *datasource, OggVorbis_File *vf, + char *initial, long ibytes, ov_callbacks callbacks); +extern int ov_test_open(OggVorbis_File *vf); + +extern long ov_bitrate(OggVorbis_File *vf,int i); +extern long ov_bitrate_instant(OggVorbis_File *vf); +extern long ov_streams(OggVorbis_File *vf); +extern long ov_seekable(OggVorbis_File *vf); +extern long ov_serialnumber(OggVorbis_File *vf,int i); + +extern ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i); +extern ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i); +extern double ov_time_total(OggVorbis_File *vf,int i); + +extern int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_time_seek(OggVorbis_File *vf,double pos); +extern int ov_time_seek_page(OggVorbis_File *vf,double pos); + +extern int ov_raw_seek_lap(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_pcm_seek_lap(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_pcm_seek_page_lap(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_time_seek_lap(OggVorbis_File *vf,double pos); +extern int ov_time_seek_page_lap(OggVorbis_File *vf,double pos); + +extern ogg_int64_t ov_raw_tell(OggVorbis_File *vf); +extern ogg_int64_t ov_pcm_tell(OggVorbis_File *vf); +extern double ov_time_tell(OggVorbis_File *vf); + +extern vorbis_info *ov_info(OggVorbis_File *vf,int link); +extern vorbis_comment *ov_comment(OggVorbis_File *vf,int link); + +extern long ov_read_float(OggVorbis_File *vf,float ***pcm_channels,int samples, + int *bitstream); +extern long ov_read(OggVorbis_File *vf,char *buffer,int length, + int bigendianp,int word,int sgned,int *bitstream); +extern int ov_crosslap(OggVorbis_File *vf1,OggVorbis_File *vf2); + +extern int ov_halfrate(OggVorbis_File *vf,int flag); +extern int ov_halfrate_p(OggVorbis_File *vf); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif + + diff --git a/Engine/lib/libvorbis/lib/analysis.c b/Engine/lib/libvorbis/lib/analysis.c new file mode 100644 index 000000000..316e1dcca --- /dev/null +++ b/Engine/lib/libvorbis/lib/analysis.c @@ -0,0 +1,121 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: single-block PCM analysis mode dispatch + last mod: $Id: analysis.c 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#include +#include +#include +#include +#include "vorbis/codec.h" +#include "codec_internal.h" +#include "registry.h" +#include "scales.h" +#include "os.h" +#include "misc.h" + +int analysis_noisy=1; + +/* decides between modes, dispatches to the appropriate mapping. */ +int vorbis_analysis(vorbis_block *vb, ogg_packet *op){ + int ret,i; + vorbis_block_internal *vbi=vb->internal; + + vb->glue_bits=0; + vb->time_bits=0; + vb->floor_bits=0; + vb->res_bits=0; + + /* first things first. Make sure encode is ready */ + for(i=0;ipacketblob[i]); + + /* we only have one mapping type (0), and we let the mapping code + itself figure out what soft mode to use. This allows easier + bitrate management */ + + if((ret=_mapping_P[0]->forward(vb))) + return(ret); + + if(op){ + if(vorbis_bitrate_managed(vb)) + /* The app is using a bitmanaged mode... but not using the + bitrate management interface. */ + return(OV_EINVAL); + + op->packet=oggpack_get_buffer(&vb->opb); + op->bytes=oggpack_bytes(&vb->opb); + op->b_o_s=0; + op->e_o_s=vb->eofflag; + op->granulepos=vb->granulepos; + op->packetno=vb->sequence; /* for sake of completeness */ + } + return(0); +} + +/* there was no great place to put this.... */ +void _analysis_output_always(char *base,int i,float *v,int n,int bark,int dB,ogg_int64_t off){ + int j; + FILE *of; + char buffer[80]; + + /* if(i==5870){*/ + sprintf(buffer,"%s_%d.m",base,i); + of=fopen(buffer,"w"); + + if(!of)perror("failed to open data dump file"); + + for(j=0;j +#include +#include +#include +#include "vorbis/codec.h" +#include "codec_internal.h" +#include "os.h" +#include "misc.h" +#include "bitrate.h" + +/* compute bitrate tracking setup */ +void vorbis_bitrate_init(vorbis_info *vi,bitrate_manager_state *bm){ + codec_setup_info *ci=vi->codec_setup; + bitrate_manager_info *bi=&ci->bi; + + memset(bm,0,sizeof(*bm)); + + if(bi && (bi->reservoir_bits>0)){ + long ratesamples=vi->rate; + int halfsamples=ci->blocksizes[0]>>1; + + bm->short_per_long=ci->blocksizes[1]/ci->blocksizes[0]; + bm->managed=1; + + bm->avg_bitsper= rint(1.*bi->avg_rate*halfsamples/ratesamples); + bm->min_bitsper= rint(1.*bi->min_rate*halfsamples/ratesamples); + bm->max_bitsper= rint(1.*bi->max_rate*halfsamples/ratesamples); + + bm->avgfloat=PACKETBLOBS/2; + + /* not a necessary fix, but one that leads to a more balanced + typical initialization */ + { + long desired_fill=bi->reservoir_bits*bi->reservoir_bias; + bm->minmax_reservoir=desired_fill; + bm->avg_reservoir=desired_fill; + } + + } +} + +void vorbis_bitrate_clear(bitrate_manager_state *bm){ + memset(bm,0,sizeof(*bm)); + return; +} + +int vorbis_bitrate_managed(vorbis_block *vb){ + vorbis_dsp_state *vd=vb->vd; + private_state *b=vd->backend_state; + bitrate_manager_state *bm=&b->bms; + + if(bm && bm->managed)return(1); + return(0); +} + +/* finish taking in the block we just processed */ +int vorbis_bitrate_addblock(vorbis_block *vb){ + vorbis_block_internal *vbi=vb->internal; + vorbis_dsp_state *vd=vb->vd; + private_state *b=vd->backend_state; + bitrate_manager_state *bm=&b->bms; + vorbis_info *vi=vd->vi; + codec_setup_info *ci=vi->codec_setup; + bitrate_manager_info *bi=&ci->bi; + + int choice=rint(bm->avgfloat); + long this_bits=oggpack_bytes(vbi->packetblob[choice])*8; + long min_target_bits=(vb->W?bm->min_bitsper*bm->short_per_long:bm->min_bitsper); + long max_target_bits=(vb->W?bm->max_bitsper*bm->short_per_long:bm->max_bitsper); + int samples=ci->blocksizes[vb->W]>>1; + long desired_fill=bi->reservoir_bits*bi->reservoir_bias; + if(!bm->managed){ + /* not a bitrate managed stream, but for API simplicity, we'll + buffer the packet to keep the code path clean */ + + if(bm->vb)return(-1); /* one has been submitted without + being claimed */ + bm->vb=vb; + return(0); + } + + bm->vb=vb; + + /* look ahead for avg floater */ + if(bm->avg_bitsper>0){ + double slew=0.; + long avg_target_bits=(vb->W?bm->avg_bitsper*bm->short_per_long:bm->avg_bitsper); + double slewlimit= 15./bi->slew_damp; + + /* choosing a new floater: + if we're over target, we slew down + if we're under target, we slew up + + choose slew as follows: look through packetblobs of this frame + and set slew as the first in the appropriate direction that + gives us the slew we want. This may mean no slew if delta is + already favorable. + + Then limit slew to slew max */ + + if(bm->avg_reservoir+(this_bits-avg_target_bits)>desired_fill){ + while(choice>0 && this_bits>avg_target_bits && + bm->avg_reservoir+(this_bits-avg_target_bits)>desired_fill){ + choice--; + this_bits=oggpack_bytes(vbi->packetblob[choice])*8; + } + }else if(bm->avg_reservoir+(this_bits-avg_target_bits)avg_reservoir+(this_bits-avg_target_bits)packetblob[choice])*8; + } + } + + slew=rint(choice-bm->avgfloat)/samples*vi->rate; + if(slew<-slewlimit)slew=-slewlimit; + if(slew>slewlimit)slew=slewlimit; + choice=rint(bm->avgfloat+= slew/vi->rate*samples); + this_bits=oggpack_bytes(vbi->packetblob[choice])*8; + } + + + + /* enforce min(if used) on the current floater (if used) */ + if(bm->min_bitsper>0){ + /* do we need to force the bitrate up? */ + if(this_bitsminmax_reservoir-(min_target_bits-this_bits)<0){ + choice++; + if(choice>=PACKETBLOBS)break; + this_bits=oggpack_bytes(vbi->packetblob[choice])*8; + } + } + } + + /* enforce max (if used) on the current floater (if used) */ + if(bm->max_bitsper>0){ + /* do we need to force the bitrate down? */ + if(this_bits>max_target_bits){ + while(bm->minmax_reservoir+(this_bits-max_target_bits)>bi->reservoir_bits){ + choice--; + if(choice<0)break; + this_bits=oggpack_bytes(vbi->packetblob[choice])*8; + } + } + } + + /* Choice of packetblobs now made based on floater, and min/max + requirements. Now boundary check extreme choices */ + + if(choice<0){ + /* choosing a smaller packetblob is insufficient to trim bitrate. + frame will need to be truncated */ + long maxsize=(max_target_bits+(bi->reservoir_bits-bm->minmax_reservoir))/8; + bm->choice=choice=0; + + if(oggpack_bytes(vbi->packetblob[choice])>maxsize){ + + oggpack_writetrunc(vbi->packetblob[choice],maxsize*8); + this_bits=oggpack_bytes(vbi->packetblob[choice])*8; + } + }else{ + long minsize=(min_target_bits-bm->minmax_reservoir+7)/8; + if(choice>=PACKETBLOBS) + choice=PACKETBLOBS-1; + + bm->choice=choice; + + /* prop up bitrate according to demand. pad this frame out with zeroes */ + minsize-=oggpack_bytes(vbi->packetblob[choice]); + while(minsize-->0)oggpack_write(vbi->packetblob[choice],0,8); + this_bits=oggpack_bytes(vbi->packetblob[choice])*8; + + } + + /* now we have the final packet and the final packet size. Update statistics */ + /* min and max reservoir */ + if(bm->min_bitsper>0 || bm->max_bitsper>0){ + + if(max_target_bits>0 && this_bits>max_target_bits){ + bm->minmax_reservoir+=(this_bits-max_target_bits); + }else if(min_target_bits>0 && this_bitsminmax_reservoir+=(this_bits-min_target_bits); + }else{ + /* inbetween; we want to take reservoir toward but not past desired_fill */ + if(bm->minmax_reservoir>desired_fill){ + if(max_target_bits>0){ /* logical bulletproofing against initialization state */ + bm->minmax_reservoir+=(this_bits-max_target_bits); + if(bm->minmax_reservoirminmax_reservoir=desired_fill; + }else{ + bm->minmax_reservoir=desired_fill; + } + }else{ + if(min_target_bits>0){ /* logical bulletproofing against initialization state */ + bm->minmax_reservoir+=(this_bits-min_target_bits); + if(bm->minmax_reservoir>desired_fill)bm->minmax_reservoir=desired_fill; + }else{ + bm->minmax_reservoir=desired_fill; + } + } + } + } + + /* avg reservoir */ + if(bm->avg_bitsper>0){ + long avg_target_bits=(vb->W?bm->avg_bitsper*bm->short_per_long:bm->avg_bitsper); + bm->avg_reservoir+=this_bits-avg_target_bits; + } + + return(0); +} + +int vorbis_bitrate_flushpacket(vorbis_dsp_state *vd,ogg_packet *op){ + private_state *b=vd->backend_state; + bitrate_manager_state *bm=&b->bms; + vorbis_block *vb=bm->vb; + int choice=PACKETBLOBS/2; + if(!vb)return 0; + + if(op){ + vorbis_block_internal *vbi=vb->internal; + + if(vorbis_bitrate_managed(vb)) + choice=bm->choice; + + op->packet=oggpack_get_buffer(vbi->packetblob[choice]); + op->bytes=oggpack_bytes(vbi->packetblob[choice]); + op->b_o_s=0; + op->e_o_s=vb->eofflag; + op->granulepos=vb->granulepos; + op->packetno=vb->sequence; /* for sake of completeness */ + } + + bm->vb=0; + return(1); +} diff --git a/Engine/lib/libvorbis/lib/bitrate.h b/Engine/lib/libvorbis/lib/bitrate.h new file mode 100644 index 000000000..db48fcb64 --- /dev/null +++ b/Engine/lib/libvorbis/lib/bitrate.h @@ -0,0 +1,59 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: bitrate tracking and management + last mod: $Id: bitrate.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#ifndef _V_BITRATE_H_ +#define _V_BITRATE_H_ + +#include "vorbis/codec.h" +#include "codec_internal.h" +#include "os.h" + +/* encode side bitrate tracking */ +typedef struct bitrate_manager_state { + int managed; + + long avg_reservoir; + long minmax_reservoir; + long avg_bitsper; + long min_bitsper; + long max_bitsper; + + long short_per_long; + double avgfloat; + + vorbis_block *vb; + int choice; +} bitrate_manager_state; + +typedef struct bitrate_manager_info{ + long avg_rate; + long min_rate; + long max_rate; + long reservoir_bits; + double reservoir_bias; + + double slew_damp; + +} bitrate_manager_info; + +extern void vorbis_bitrate_init(vorbis_info *vi,bitrate_manager_state *bs); +extern void vorbis_bitrate_clear(bitrate_manager_state *bs); +extern int vorbis_bitrate_managed(vorbis_block *vb); +extern int vorbis_bitrate_addblock(vorbis_block *vb); +extern int vorbis_bitrate_flushpacket(vorbis_dsp_state *vd, ogg_packet *op); + +#endif diff --git a/Engine/lib/libvorbis/lib/block.c b/Engine/lib/libvorbis/lib/block.c new file mode 100644 index 000000000..3b6f456dd --- /dev/null +++ b/Engine/lib/libvorbis/lib/block.c @@ -0,0 +1,993 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: PCM data vector blocking, windowing and dis/reassembly + last mod: $Id: block.c 13293 2007-07-24 00:09:47Z xiphmont $ + + Handle windowing, overlap-add, etc of the PCM vectors. This is made + more amusing by Vorbis' current two allowed block sizes. + + ********************************************************************/ + +#include +#include +#include +#include +#include "vorbis/codec.h" +#include "codec_internal.h" + +#include "window.h" +#include "mdct.h" +#include "lpc.h" +#include "registry.h" +#include "misc.h" + +static int ilog2(unsigned int v){ + int ret=0; + if(v)--v; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +/* pcm accumulator examples (not exhaustive): + + <-------------- lW ----------------> + <--------------- W ----------------> +: .....|..... _______________ | +: .''' | '''_--- | |\ | +:.....''' |_____--- '''......| | \_______| +:.................|__________________|_______|__|______| + |<------ Sl ------>| > Sr < |endW + |beginSl |endSl | |endSr + |beginW |endlW |beginSr + + + |< lW >| + <--------------- W ----------------> + | | .. ______________ | + | | ' `/ | ---_ | + |___.'___/`. | ---_____| + |_______|__|_______|_________________| + | >|Sl|< |<------ Sr ----->|endW + | | |endSl |beginSr |endSr + |beginW | |endlW + mult[0] |beginSl mult[n] + + <-------------- lW -----------------> + |<--W-->| +: .............. ___ | | +: .''' |`/ \ | | +:.....''' |/`....\|...| +:.........................|___|___|___| + |Sl |Sr |endW + | | |endSr + | |beginSr + | |endSl + |beginSl + |beginW +*/ + +/* block abstraction setup *********************************************/ + +#ifndef WORD_ALIGN +#define WORD_ALIGN 8 +#endif + +int vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb){ + int i; + memset(vb,0,sizeof(*vb)); + vb->vd=v; + vb->localalloc=0; + vb->localstore=NULL; + if(v->analysisp){ + vorbis_block_internal *vbi= + vb->internal=_ogg_calloc(1,sizeof(vorbis_block_internal)); + vbi->ampmax=-9999; + + for(i=0;ipacketblob[i]=&vb->opb; + }else{ + vbi->packetblob[i]= + _ogg_calloc(1,sizeof(oggpack_buffer)); + } + oggpack_writeinit(vbi->packetblob[i]); + } + } + + return(0); +} + +void *_vorbis_block_alloc(vorbis_block *vb,long bytes){ + bytes=(bytes+(WORD_ALIGN-1)) & ~(WORD_ALIGN-1); + if(bytes+vb->localtop>vb->localalloc){ + /* can't just _ogg_realloc... there are outstanding pointers */ + if(vb->localstore){ + struct alloc_chain *link=_ogg_malloc(sizeof(*link)); + vb->totaluse+=vb->localtop; + link->next=vb->reap; + link->ptr=vb->localstore; + vb->reap=link; + } + /* highly conservative */ + vb->localalloc=bytes; + vb->localstore=_ogg_malloc(vb->localalloc); + vb->localtop=0; + } + { + void *ret=(void *)(((char *)vb->localstore)+vb->localtop); + vb->localtop+=bytes; + return ret; + } +} + +/* reap the chain, pull the ripcord */ +void _vorbis_block_ripcord(vorbis_block *vb){ + /* reap the chain */ + struct alloc_chain *reap=vb->reap; + while(reap){ + struct alloc_chain *next=reap->next; + _ogg_free(reap->ptr); + memset(reap,0,sizeof(*reap)); + _ogg_free(reap); + reap=next; + } + /* consolidate storage */ + if(vb->totaluse){ + vb->localstore=_ogg_realloc(vb->localstore,vb->totaluse+vb->localalloc); + vb->localalloc+=vb->totaluse; + vb->totaluse=0; + } + + /* pull the ripcord */ + vb->localtop=0; + vb->reap=NULL; +} + +int vorbis_block_clear(vorbis_block *vb){ + int i; + vorbis_block_internal *vbi=vb->internal; + + _vorbis_block_ripcord(vb); + if(vb->localstore)_ogg_free(vb->localstore); + + if(vbi){ + for(i=0;ipacketblob[i]); + if(i!=PACKETBLOBS/2)_ogg_free(vbi->packetblob[i]); + } + _ogg_free(vbi); + } + memset(vb,0,sizeof(*vb)); + return(0); +} + +/* Analysis side code, but directly related to blocking. Thus it's + here and not in analysis.c (which is for analysis transforms only). + The init is here because some of it is shared */ + +static int _vds_shared_init(vorbis_dsp_state *v,vorbis_info *vi,int encp){ + int i; + codec_setup_info *ci=vi->codec_setup; + private_state *b=NULL; + int hs; + + if(ci==NULL) return 1; + hs=ci->halfrate_flag; + + memset(v,0,sizeof(*v)); + b=v->backend_state=_ogg_calloc(1,sizeof(*b)); + + v->vi=vi; + b->modebits=ilog2(ci->modes); + + b->transform[0]=_ogg_calloc(VI_TRANSFORMB,sizeof(*b->transform[0])); + b->transform[1]=_ogg_calloc(VI_TRANSFORMB,sizeof(*b->transform[1])); + + /* MDCT is tranform 0 */ + + b->transform[0][0]=_ogg_calloc(1,sizeof(mdct_lookup)); + b->transform[1][0]=_ogg_calloc(1,sizeof(mdct_lookup)); + mdct_init(b->transform[0][0],ci->blocksizes[0]>>hs); + mdct_init(b->transform[1][0],ci->blocksizes[1]>>hs); + + /* Vorbis I uses only window type 0 */ + b->window[0]=ilog2(ci->blocksizes[0])-6; + b->window[1]=ilog2(ci->blocksizes[1])-6; + + if(encp){ /* encode/decode differ here */ + + /* analysis always needs an fft */ + drft_init(&b->fft_look[0],ci->blocksizes[0]); + drft_init(&b->fft_look[1],ci->blocksizes[1]); + + /* finish the codebooks */ + if(!ci->fullbooks){ + ci->fullbooks=_ogg_calloc(ci->books,sizeof(*ci->fullbooks)); + for(i=0;ibooks;i++) + vorbis_book_init_encode(ci->fullbooks+i,ci->book_param[i]); + } + + b->psy=_ogg_calloc(ci->psys,sizeof(*b->psy)); + for(i=0;ipsys;i++){ + _vp_psy_init(b->psy+i, + ci->psy_param[i], + &ci->psy_g_param, + ci->blocksizes[ci->psy_param[i]->blockflag]/2, + vi->rate); + } + + v->analysisp=1; + }else{ + /* finish the codebooks */ + if(!ci->fullbooks){ + ci->fullbooks=_ogg_calloc(ci->books,sizeof(*ci->fullbooks)); + for(i=0;ibooks;i++){ + vorbis_book_init_decode(ci->fullbooks+i,ci->book_param[i]); + /* decode codebooks are now standalone after init */ + vorbis_staticbook_destroy(ci->book_param[i]); + ci->book_param[i]=NULL; + } + } + } + + /* initialize the storage vectors. blocksize[1] is small for encode, + but the correct size for decode */ + v->pcm_storage=ci->blocksizes[1]; + v->pcm=_ogg_malloc(vi->channels*sizeof(*v->pcm)); + v->pcmret=_ogg_malloc(vi->channels*sizeof(*v->pcmret)); + { + int i; + for(i=0;ichannels;i++) + v->pcm[i]=_ogg_calloc(v->pcm_storage,sizeof(*v->pcm[i])); + } + + /* all 1 (large block) or 0 (small block) */ + /* explicitly set for the sake of clarity */ + v->lW=0; /* previous window size */ + v->W=0; /* current window size */ + + /* all vector indexes */ + v->centerW=ci->blocksizes[1]/2; + + v->pcm_current=v->centerW; + + /* initialize all the backend lookups */ + b->flr=_ogg_calloc(ci->floors,sizeof(*b->flr)); + b->residue=_ogg_calloc(ci->residues,sizeof(*b->residue)); + + for(i=0;ifloors;i++) + b->flr[i]=_floor_P[ci->floor_type[i]]-> + look(v,ci->floor_param[i]); + + for(i=0;iresidues;i++) + b->residue[i]=_residue_P[ci->residue_type[i]]-> + look(v,ci->residue_param[i]); + + return 0; +} + +/* arbitrary settings and spec-mandated numbers get filled in here */ +int vorbis_analysis_init(vorbis_dsp_state *v,vorbis_info *vi){ + private_state *b=NULL; + + if(_vds_shared_init(v,vi,1))return 1; + b=v->backend_state; + b->psy_g_look=_vp_global_look(vi); + + /* Initialize the envelope state storage */ + b->ve=_ogg_calloc(1,sizeof(*b->ve)); + _ve_envelope_init(b->ve,vi); + + vorbis_bitrate_init(vi,&b->bms); + + /* compressed audio packets start after the headers + with sequence number 3 */ + v->sequence=3; + + return(0); +} + +void vorbis_dsp_clear(vorbis_dsp_state *v){ + int i; + if(v){ + vorbis_info *vi=v->vi; + codec_setup_info *ci=(vi?vi->codec_setup:NULL); + private_state *b=v->backend_state; + + if(b){ + + if(b->ve){ + _ve_envelope_clear(b->ve); + _ogg_free(b->ve); + } + + if(b->transform[0]){ + mdct_clear(b->transform[0][0]); + _ogg_free(b->transform[0][0]); + _ogg_free(b->transform[0]); + } + if(b->transform[1]){ + mdct_clear(b->transform[1][0]); + _ogg_free(b->transform[1][0]); + _ogg_free(b->transform[1]); + } + + if(b->flr){ + if(ci) + for(i=0;ifloors;i++) + _floor_P[ci->floor_type[i]]-> + free_look(b->flr[i]); + _ogg_free(b->flr); + } + if(b->residue){ + if(ci) + for(i=0;iresidues;i++) + _residue_P[ci->residue_type[i]]-> + free_look(b->residue[i]); + _ogg_free(b->residue); + } + if(b->psy){ + if(ci) + for(i=0;ipsys;i++) + _vp_psy_clear(b->psy+i); + _ogg_free(b->psy); + } + + if(b->psy_g_look)_vp_global_free(b->psy_g_look); + vorbis_bitrate_clear(&b->bms); + + drft_clear(&b->fft_look[0]); + drft_clear(&b->fft_look[1]); + + } + + if(v->pcm){ + if(vi) + for(i=0;ichannels;i++) + if(v->pcm[i])_ogg_free(v->pcm[i]); + _ogg_free(v->pcm); + if(v->pcmret)_ogg_free(v->pcmret); + } + + if(b){ + /* free header, header1, header2 */ + if(b->header)_ogg_free(b->header); + if(b->header1)_ogg_free(b->header1); + if(b->header2)_ogg_free(b->header2); + _ogg_free(b); + } + + memset(v,0,sizeof(*v)); + } +} + +float **vorbis_analysis_buffer(vorbis_dsp_state *v, int vals){ + int i; + vorbis_info *vi=v->vi; + private_state *b=v->backend_state; + + /* free header, header1, header2 */ + if(b->header)_ogg_free(b->header);b->header=NULL; + if(b->header1)_ogg_free(b->header1);b->header1=NULL; + if(b->header2)_ogg_free(b->header2);b->header2=NULL; + + /* Do we have enough storage space for the requested buffer? If not, + expand the PCM (and envelope) storage */ + + if(v->pcm_current+vals>=v->pcm_storage){ + v->pcm_storage=v->pcm_current+vals*2; + + for(i=0;ichannels;i++){ + v->pcm[i]=_ogg_realloc(v->pcm[i],v->pcm_storage*sizeof(*v->pcm[i])); + } + } + + for(i=0;ichannels;i++) + v->pcmret[i]=v->pcm[i]+v->pcm_current; + + return(v->pcmret); +} + +static void _preextrapolate_helper(vorbis_dsp_state *v){ + int i; + int order=32; + float *lpc=alloca(order*sizeof(*lpc)); + float *work=alloca(v->pcm_current*sizeof(*work)); + long j; + v->preextrapolate=1; + + if(v->pcm_current-v->centerW>order*2){ /* safety */ + for(i=0;ivi->channels;i++){ + /* need to run the extrapolation in reverse! */ + for(j=0;jpcm_current;j++) + work[j]=v->pcm[i][v->pcm_current-j-1]; + + /* prime as above */ + vorbis_lpc_from_data(work,lpc,v->pcm_current-v->centerW,order); + + /* run the predictor filter */ + vorbis_lpc_predict(lpc,work+v->pcm_current-v->centerW-order, + order, + work+v->pcm_current-v->centerW, + v->centerW); + + for(j=0;jpcm_current;j++) + v->pcm[i][v->pcm_current-j-1]=work[j]; + + } + } +} + + +/* call with val<=0 to set eof */ + +int vorbis_analysis_wrote(vorbis_dsp_state *v, int vals){ + vorbis_info *vi=v->vi; + codec_setup_info *ci=vi->codec_setup; + + if(vals<=0){ + int order=32; + int i; + float *lpc=alloca(order*sizeof(*lpc)); + + /* if it wasn't done earlier (very short sample) */ + if(!v->preextrapolate) + _preextrapolate_helper(v); + + /* We're encoding the end of the stream. Just make sure we have + [at least] a few full blocks of zeroes at the end. */ + /* actually, we don't want zeroes; that could drop a large + amplitude off a cliff, creating spread spectrum noise that will + suck to encode. Extrapolate for the sake of cleanliness. */ + + vorbis_analysis_buffer(v,ci->blocksizes[1]*3); + v->eofflag=v->pcm_current; + v->pcm_current+=ci->blocksizes[1]*3; + + for(i=0;ichannels;i++){ + if(v->eofflag>order*2){ + /* extrapolate with LPC to fill in */ + long n; + + /* make a predictor filter */ + n=v->eofflag; + if(n>ci->blocksizes[1])n=ci->blocksizes[1]; + vorbis_lpc_from_data(v->pcm[i]+v->eofflag-n,lpc,n,order); + + /* run the predictor filter */ + vorbis_lpc_predict(lpc,v->pcm[i]+v->eofflag-order,order, + v->pcm[i]+v->eofflag,v->pcm_current-v->eofflag); + }else{ + /* not enough data to extrapolate (unlikely to happen due to + guarding the overlap, but bulletproof in case that + assumtion goes away). zeroes will do. */ + memset(v->pcm[i]+v->eofflag,0, + (v->pcm_current-v->eofflag)*sizeof(*v->pcm[i])); + + } + } + }else{ + + if(v->pcm_current+vals>v->pcm_storage) + return(OV_EINVAL); + + v->pcm_current+=vals; + + /* we may want to reverse extrapolate the beginning of a stream + too... in case we're beginning on a cliff! */ + /* clumsy, but simple. It only runs once, so simple is good. */ + if(!v->preextrapolate && v->pcm_current-v->centerW>ci->blocksizes[1]) + _preextrapolate_helper(v); + + } + return(0); +} + +/* do the deltas, envelope shaping, pre-echo and determine the size of + the next block on which to continue analysis */ +int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb){ + int i; + vorbis_info *vi=v->vi; + codec_setup_info *ci=vi->codec_setup; + private_state *b=v->backend_state; + vorbis_look_psy_global *g=b->psy_g_look; + long beginW=v->centerW-ci->blocksizes[v->W]/2,centerNext; + vorbis_block_internal *vbi=(vorbis_block_internal *)vb->internal; + + /* check to see if we're started... */ + if(!v->preextrapolate)return(0); + + /* check to see if we're done... */ + if(v->eofflag==-1)return(0); + + /* By our invariant, we have lW, W and centerW set. Search for + the next boundary so we can determine nW (the next window size) + which lets us compute the shape of the current block's window */ + + /* we do an envelope search even on a single blocksize; we may still + be throwing more bits at impulses, and envelope search handles + marking impulses too. */ + { + long bp=_ve_envelope_search(v); + if(bp==-1){ + + if(v->eofflag==0)return(0); /* not enough data currently to search for a + full long block */ + v->nW=0; + }else{ + + if(ci->blocksizes[0]==ci->blocksizes[1]) + v->nW=0; + else + v->nW=bp; + } + } + + centerNext=v->centerW+ci->blocksizes[v->W]/4+ci->blocksizes[v->nW]/4; + + { + /* center of next block + next block maximum right side. */ + + long blockbound=centerNext+ci->blocksizes[v->nW]/2; + if(v->pcm_currentlW=v->lW; + vb->W=v->W; + vb->nW=v->nW; + + if(v->W){ + if(!v->lW || !v->nW){ + vbi->blocktype=BLOCKTYPE_TRANSITION; + /*fprintf(stderr,"-");*/ + }else{ + vbi->blocktype=BLOCKTYPE_LONG; + /*fprintf(stderr,"_");*/ + } + }else{ + if(_ve_envelope_mark(v)){ + vbi->blocktype=BLOCKTYPE_IMPULSE; + /*fprintf(stderr,"|");*/ + + }else{ + vbi->blocktype=BLOCKTYPE_PADDING; + /*fprintf(stderr,".");*/ + + } + } + + vb->vd=v; + vb->sequence=v->sequence++; + vb->granulepos=v->granulepos; + vb->pcmend=ci->blocksizes[v->W]; + + /* copy the vectors; this uses the local storage in vb */ + + /* this tracks 'strongest peak' for later psychoacoustics */ + /* moved to the global psy state; clean this mess up */ + if(vbi->ampmax>g->ampmax)g->ampmax=vbi->ampmax; + g->ampmax=_vp_ampmax_decay(g->ampmax,v); + vbi->ampmax=g->ampmax; + + vb->pcm=_vorbis_block_alloc(vb,sizeof(*vb->pcm)*vi->channels); + vbi->pcmdelay=_vorbis_block_alloc(vb,sizeof(*vbi->pcmdelay)*vi->channels); + for(i=0;ichannels;i++){ + vbi->pcmdelay[i]= + _vorbis_block_alloc(vb,(vb->pcmend+beginW)*sizeof(*vbi->pcmdelay[i])); + memcpy(vbi->pcmdelay[i],v->pcm[i],(vb->pcmend+beginW)*sizeof(*vbi->pcmdelay[i])); + vb->pcm[i]=vbi->pcmdelay[i]+beginW; + + /* before we added the delay + vb->pcm[i]=_vorbis_block_alloc(vb,vb->pcmend*sizeof(*vb->pcm[i])); + memcpy(vb->pcm[i],v->pcm[i]+beginW,ci->blocksizes[v->W]*sizeof(*vb->pcm[i])); + */ + + } + + /* handle eof detection: eof==0 means that we've not yet received EOF + eof>0 marks the last 'real' sample in pcm[] + eof<0 'no more to do'; doesn't get here */ + + if(v->eofflag){ + if(v->centerW>=v->eofflag){ + v->eofflag=-1; + vb->eofflag=1; + return(1); + } + } + + /* advance storage vectors and clean up */ + { + int new_centerNext=ci->blocksizes[1]/2; + int movementW=centerNext-new_centerNext; + + if(movementW>0){ + + _ve_envelope_shift(b->ve,movementW); + v->pcm_current-=movementW; + + for(i=0;ichannels;i++) + memmove(v->pcm[i],v->pcm[i]+movementW, + v->pcm_current*sizeof(*v->pcm[i])); + + + v->lW=v->W; + v->W=v->nW; + v->centerW=new_centerNext; + + if(v->eofflag){ + v->eofflag-=movementW; + if(v->eofflag<=0)v->eofflag=-1; + /* do not add padding to end of stream! */ + if(v->centerW>=v->eofflag){ + v->granulepos+=movementW-(v->centerW-v->eofflag); + }else{ + v->granulepos+=movementW; + } + }else{ + v->granulepos+=movementW; + } + } + } + + /* done */ + return(1); +} + +int vorbis_synthesis_restart(vorbis_dsp_state *v){ + vorbis_info *vi=v->vi; + codec_setup_info *ci; + int hs; + + if(!v->backend_state)return -1; + if(!vi)return -1; + ci=vi->codec_setup; + if(!ci)return -1; + hs=ci->halfrate_flag; + + v->centerW=ci->blocksizes[1]>>(hs+1); + v->pcm_current=v->centerW>>hs; + + v->pcm_returned=-1; + v->granulepos=-1; + v->sequence=-1; + v->eofflag=0; + ((private_state *)(v->backend_state))->sample_count=-1; + + return(0); +} + +int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi){ + if(_vds_shared_init(v,vi,0)) return 1; + vorbis_synthesis_restart(v); + + return 0; +} + +/* Unlike in analysis, the window is only partially applied for each + block. The time domain envelope is not yet handled at the point of + calling (as it relies on the previous block). */ + +int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb){ + vorbis_info *vi=v->vi; + codec_setup_info *ci=vi->codec_setup; + private_state *b=v->backend_state; + int hs=ci->halfrate_flag; + int i,j; + + if(!vb)return(OV_EINVAL); + if(v->pcm_current>v->pcm_returned && v->pcm_returned!=-1)return(OV_EINVAL); + + v->lW=v->W; + v->W=vb->W; + v->nW=-1; + + if((v->sequence==-1)|| + (v->sequence+1 != vb->sequence)){ + v->granulepos=-1; /* out of sequence; lose count */ + b->sample_count=-1; + } + + v->sequence=vb->sequence; + + if(vb->pcm){ /* no pcm to process if vorbis_synthesis_trackonly + was called on block */ + int n=ci->blocksizes[v->W]>>(hs+1); + int n0=ci->blocksizes[0]>>(hs+1); + int n1=ci->blocksizes[1]>>(hs+1); + + int thisCenter; + int prevCenter; + + v->glue_bits+=vb->glue_bits; + v->time_bits+=vb->time_bits; + v->floor_bits+=vb->floor_bits; + v->res_bits+=vb->res_bits; + + if(v->centerW){ + thisCenter=n1; + prevCenter=0; + }else{ + thisCenter=0; + prevCenter=n1; + } + + /* v->pcm is now used like a two-stage double buffer. We don't want + to have to constantly shift *or* adjust memory usage. Don't + accept a new block until the old is shifted out */ + + for(j=0;jchannels;j++){ + /* the overlap/add section */ + if(v->lW){ + if(v->W){ + /* large/large */ + float *w=_vorbis_window_get(b->window[1]-hs); + float *pcm=v->pcm[j]+prevCenter; + float *p=vb->pcm[j]; + for(i=0;iwindow[0]-hs); + float *pcm=v->pcm[j]+prevCenter+n1/2-n0/2; + float *p=vb->pcm[j]; + for(i=0;iW){ + /* small/large */ + float *w=_vorbis_window_get(b->window[0]-hs); + float *pcm=v->pcm[j]+prevCenter; + float *p=vb->pcm[j]+n1/2-n0/2; + for(i=0;iwindow[0]-hs); + float *pcm=v->pcm[j]+prevCenter; + float *p=vb->pcm[j]; + for(i=0;ipcm[j]+thisCenter; + float *p=vb->pcm[j]+n; + for(i=0;icenterW) + v->centerW=0; + else + v->centerW=n1; + + /* deal with initial packet state; we do this using the explicit + pcm_returned==-1 flag otherwise we're sensitive to first block + being short or long */ + + if(v->pcm_returned==-1){ + v->pcm_returned=thisCenter; + v->pcm_current=thisCenter; + }else{ + v->pcm_returned=prevCenter; + v->pcm_current=prevCenter+ + ((ci->blocksizes[v->lW]/4+ + ci->blocksizes[v->W]/4)>>hs); + } + + } + + /* track the frame number... This is for convenience, but also + making sure our last packet doesn't end with added padding. If + the last packet is partial, the number of samples we'll have to + return will be past the vb->granulepos. + + This is not foolproof! It will be confused if we begin + decoding at the last page after a seek or hole. In that case, + we don't have a starting point to judge where the last frame + is. For this reason, vorbisfile will always try to make sure + it reads the last two marked pages in proper sequence */ + + if(b->sample_count==-1){ + b->sample_count=0; + }else{ + b->sample_count+=ci->blocksizes[v->lW]/4+ci->blocksizes[v->W]/4; + } + + if(v->granulepos==-1){ + if(vb->granulepos!=-1){ /* only set if we have a position to set to */ + + v->granulepos=vb->granulepos; + + /* is this a short page? */ + if(b->sample_count>v->granulepos){ + /* corner case; if this is both the first and last audio page, + then spec says the end is cut, not beginning */ + if(vb->eofflag){ + /* trim the end */ + /* no preceeding granulepos; assume we started at zero (we'd + have to in a short single-page stream) */ + /* granulepos could be -1 due to a seek, but that would result + in a long count, not short count */ + + v->pcm_current-=(b->sample_count-v->granulepos)>>hs; + }else{ + /* trim the beginning */ + v->pcm_returned+=(b->sample_count-v->granulepos)>>hs; + if(v->pcm_returned>v->pcm_current) + v->pcm_returned=v->pcm_current; + } + + } + + } + }else{ + v->granulepos+=ci->blocksizes[v->lW]/4+ci->blocksizes[v->W]/4; + if(vb->granulepos!=-1 && v->granulepos!=vb->granulepos){ + + if(v->granulepos>vb->granulepos){ + long extra=v->granulepos-vb->granulepos; + + if(extra) + if(vb->eofflag){ + /* partial last frame. Strip the extra samples off */ + v->pcm_current-=extra>>hs; + } /* else {Shouldn't happen *unless* the bitstream is out of + spec. Either way, believe the bitstream } */ + } /* else {Shouldn't happen *unless* the bitstream is out of + spec. Either way, believe the bitstream } */ + v->granulepos=vb->granulepos; + } + } + + /* Update, cleanup */ + + if(vb->eofflag)v->eofflag=1; + return(0); + +} + +/* pcm==NULL indicates we just want the pending samples, no more */ +int vorbis_synthesis_pcmout(vorbis_dsp_state *v,float ***pcm){ + vorbis_info *vi=v->vi; + + if(v->pcm_returned>-1 && v->pcm_returnedpcm_current){ + if(pcm){ + int i; + for(i=0;ichannels;i++) + v->pcmret[i]=v->pcm[i]+v->pcm_returned; + *pcm=v->pcmret; + } + return(v->pcm_current-v->pcm_returned); + } + return(0); +} + +int vorbis_synthesis_read(vorbis_dsp_state *v,int n){ + if(n && v->pcm_returned+n>v->pcm_current)return(OV_EINVAL); + v->pcm_returned+=n; + return(0); +} + +/* intended for use with a specific vorbisfile feature; we want access + to the [usually synthetic/postextrapolated] buffer and lapping at + the end of a decode cycle, specifically, a half-short-block worth. + This funtion works like pcmout above, except it will also expose + this implicit buffer data not normally decoded. */ +int vorbis_synthesis_lapout(vorbis_dsp_state *v,float ***pcm){ + vorbis_info *vi=v->vi; + codec_setup_info *ci=vi->codec_setup; + int hs=ci->halfrate_flag; + + int n=ci->blocksizes[v->W]>>(hs+1); + int n0=ci->blocksizes[0]>>(hs+1); + int n1=ci->blocksizes[1]>>(hs+1); + int i,j; + + if(v->pcm_returned<0)return 0; + + /* our returned data ends at pcm_returned; because the synthesis pcm + buffer is a two-fragment ring, that means our data block may be + fragmented by buffering, wrapping or a short block not filling + out a buffer. To simplify things, we unfragment if it's at all + possibly needed. Otherwise, we'd need to call lapout more than + once as well as hold additional dsp state. Opt for + simplicity. */ + + /* centerW was advanced by blockin; it would be the center of the + *next* block */ + if(v->centerW==n1){ + /* the data buffer wraps; swap the halves */ + /* slow, sure, small */ + for(j=0;jchannels;j++){ + float *p=v->pcm[j]; + for(i=0;ipcm_current-=n1; + v->pcm_returned-=n1; + v->centerW=0; + } + + /* solidify buffer into contiguous space */ + if((v->lW^v->W)==1){ + /* long/short or short/long */ + for(j=0;jchannels;j++){ + float *s=v->pcm[j]; + float *d=v->pcm[j]+(n1-n0)/2; + for(i=(n1+n0)/2-1;i>=0;--i) + d[i]=s[i]; + } + v->pcm_returned+=(n1-n0)/2; + v->pcm_current+=(n1-n0)/2; + }else{ + if(v->lW==0){ + /* short/short */ + for(j=0;jchannels;j++){ + float *s=v->pcm[j]; + float *d=v->pcm[j]+n1-n0; + for(i=n0-1;i>=0;--i) + d[i]=s[i]; + } + v->pcm_returned+=n1-n0; + v->pcm_current+=n1-n0; + } + } + + if(pcm){ + int i; + for(i=0;ichannels;i++) + v->pcmret[i]=v->pcm[i]+v->pcm_returned; + *pcm=v->pcmret; + } + + return(n1+n-v->pcm_returned); + +} + +float *vorbis_window(vorbis_dsp_state *v,int W){ + vorbis_info *vi=v->vi; + codec_setup_info *ci=vi->codec_setup; + int hs=ci->halfrate_flag; + private_state *b=v->backend_state; + + if(b->window[W]-1<0)return NULL; + return _vorbis_window_get(b->window[W]-hs); +} + diff --git a/Engine/lib/libvorbis/lib/books/coupled/res_books_stereo.h b/Engine/lib/libvorbis/lib/books/coupled/res_books_stereo.h new file mode 100644 index 000000000..e20e96a2b --- /dev/null +++ b/Engine/lib/libvorbis/lib/books/coupled/res_books_stereo.h @@ -0,0 +1,20681 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: static codebooks autogenerated by huff/huffbuld + last modified: $Id: res_books_stereo.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#include "codebook.h" +static long _vq_quantlist__16c0_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16c0_s_p1_0[] = { + 1, 4, 4, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 8, 8, 0, 0, 0, 0, 0, 0, 8, 9,10, 0, 0, 0, + 0, 0, 0, 7, 9,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 8, 8, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 8, 8, 0, 0, 0, 0, + 0, 0, 8,10,10, 0, 0, 0, 0, 0, 0, 8,10,10, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,10,10, 0, 0, 0, + 0, 0, 0, 9, 9,12, 0, 0, 0, 0, 0, 0,10,12,11, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,10,10, 0, 0, + 0, 0, 0, 0, 9,12,10, 0, 0, 0, 0, 0, 0,10,11,12, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 8, 8, 0, 0, 0, 0, 0, 0, 8,10,10, 0, 0, + 0, 0, 0, 0, 8,10,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7,10,10, 0, 0, 0, 0, 0, 0,10,12,11, 0, + 0, 0, 0, 0, 0, 9,10,12, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7,10,10, 0, 0, 0, 0, 0, 0,10,11,12, + 0, 0, 0, 0, 0, 0, 9,12, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__16c0_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__16c0_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16c0_s_p1_0 = { + _vq_quantthresh__16c0_s_p1_0, + _vq_quantmap__16c0_s_p1_0, + 3, + 3 +}; + +static static_codebook _16c0_s_p1_0 = { + 8, 6561, + _vq_lengthlist__16c0_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__16c0_s_p1_0, + NULL, + &_vq_auxt__16c0_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c0_s_p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16c0_s_p3_0[] = { + 1, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 6, 6, 7, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 6, 6, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__16c0_s_p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16c0_s_p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16c0_s_p3_0 = { + _vq_quantthresh__16c0_s_p3_0, + _vq_quantmap__16c0_s_p3_0, + 5, + 5 +}; + +static static_codebook _16c0_s_p3_0 = { + 4, 625, + _vq_lengthlist__16c0_s_p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16c0_s_p3_0, + NULL, + &_vq_auxt__16c0_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c0_s_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__16c0_s_p4_0[] = { + 1, 3, 2, 7, 8, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, + 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 7, 7, + 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__16c0_s_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__16c0_s_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__16c0_s_p4_0 = { + _vq_quantthresh__16c0_s_p4_0, + _vq_quantmap__16c0_s_p4_0, + 9, + 9 +}; + +static static_codebook _16c0_s_p4_0 = { + 2, 81, + _vq_lengthlist__16c0_s_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__16c0_s_p4_0, + NULL, + &_vq_auxt__16c0_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c0_s_p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__16c0_s_p5_0[] = { + 1, 3, 3, 6, 6, 6, 6, 8, 8, 0, 0, 0, 7, 7, 7, 7, + 8, 8, 0, 0, 0, 7, 7, 7, 7, 8, 8, 0, 0, 0, 7, 7, + 8, 8, 9, 9, 0, 0, 0, 7, 7, 8, 8, 9, 9, 0, 0, 0, + 8, 9, 8, 8,10,10, 0, 0, 0, 8, 8, 8, 8,10,10, 0, + 0, 0,10,10, 9, 9,10,10, 0, 0, 0, 0, 0, 9, 9,10, + 10, +}; + +static float _vq_quantthresh__16c0_s_p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__16c0_s_p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__16c0_s_p5_0 = { + _vq_quantthresh__16c0_s_p5_0, + _vq_quantmap__16c0_s_p5_0, + 9, + 9 +}; + +static static_codebook _16c0_s_p5_0 = { + 2, 81, + _vq_lengthlist__16c0_s_p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__16c0_s_p5_0, + NULL, + &_vq_auxt__16c0_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c0_s_p6_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__16c0_s_p6_0[] = { + 1, 3, 4, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10,11, + 11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10,11, + 11,11, 0, 0, 0, 6, 6, 8, 8, 9, 9, 9, 9,10,10,11, + 11,11,11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10, + 11,11,12,12, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10, + 10,11,11,12,12, 0, 0, 0, 8, 8, 9, 9,10,10,10,10, + 11,11,12,12,12,12, 0, 0, 0, 8, 8, 9, 9,10,10,10, + 10,11,11,12,12,12,13, 0, 0, 0, 9, 9, 9, 9,10,10, + 10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0,10,10,10, + 10,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, 9, + 10,10,11,11,12,12,13,13,13,13, 0, 0, 0, 0, 0, 9, + 9,10,10,11,11,12,12,13,13,13,14, 0, 0, 0, 0, 0, + 10,10,10,11,11,11,12,12,13,13,13,14, 0, 0, 0, 0, + 0, 0, 0,10,10,11,11,12,12,13,13,14,14, 0, 0, 0, + 0, 0, 0, 0,11,11,12,12,13,13,13,13,14,14, 0, 0, + 0, 0, 0, 0, 0,11,11,12,12,12,13,13,14,15,14, 0, + 0, 0, 0, 0, 0, 0,12,12,12,12,13,13,13,14,14,15, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,13,13,14,13,14, + 14, +}; + +static float _vq_quantthresh__16c0_s_p6_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__16c0_s_p6_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__16c0_s_p6_0 = { + _vq_quantthresh__16c0_s_p6_0, + _vq_quantmap__16c0_s_p6_0, + 17, + 17 +}; + +static static_codebook _16c0_s_p6_0 = { + 2, 289, + _vq_lengthlist__16c0_s_p6_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__16c0_s_p6_0, + NULL, + &_vq_auxt__16c0_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c0_s_p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16c0_s_p7_0[] = { + 1, 4, 4, 6, 6, 6, 7, 6, 6, 4, 7, 7,11,10,10,11, + 11,10, 4, 7, 7,10,10,10,11,10,10, 6,10,10,11,11, + 11,11,11,10, 6, 9, 9,11,12,12,11, 9, 9, 6, 9,10, + 11,12,12,11, 9,10, 7,11,11,11,11,11,12,13,12, 6, + 9,10,11,10,10,12,13,13, 6,10, 9,11,10,10,11,12, + 13, +}; + +static float _vq_quantthresh__16c0_s_p7_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__16c0_s_p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16c0_s_p7_0 = { + _vq_quantthresh__16c0_s_p7_0, + _vq_quantmap__16c0_s_p7_0, + 3, + 3 +}; + +static static_codebook _16c0_s_p7_0 = { + 4, 81, + _vq_lengthlist__16c0_s_p7_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__16c0_s_p7_0, + NULL, + &_vq_auxt__16c0_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c0_s_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__16c0_s_p7_1[] = { + 1, 3, 4, 6, 6, 7, 7, 8, 8, 8, 8,10,10,10, 7, 7, + 8, 8, 8, 9, 9, 9,10,10,10, 6, 7, 8, 8, 8, 8, 9, + 8,10,10,10, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10, 7, + 7, 8, 8, 9, 9, 8, 9,10,10,10, 8, 8, 9, 9, 9, 9, + 9, 9,11,11,11, 8, 8, 9, 9, 9, 9, 9,10,10,11,11, + 9, 9, 9, 9, 9, 9, 9,10,11,11,11,10,11, 9, 9, 9, + 9,10, 9,11,11,11,10,11,10,10, 9, 9,10,10,11,11, + 11,11,11, 9, 9, 9, 9,10,10, +}; + +static float _vq_quantthresh__16c0_s_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__16c0_s_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__16c0_s_p7_1 = { + _vq_quantthresh__16c0_s_p7_1, + _vq_quantmap__16c0_s_p7_1, + 11, + 11 +}; + +static static_codebook _16c0_s_p7_1 = { + 2, 121, + _vq_lengthlist__16c0_s_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__16c0_s_p7_1, + NULL, + &_vq_auxt__16c0_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__16c0_s_p8_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__16c0_s_p8_0[] = { + 1, 4, 4, 7, 7, 7, 7, 7, 6, 8, 8,10,10, 6, 5, 6, + 8, 8, 8, 8, 8, 8, 8, 9,10,10, 7, 6, 6, 8, 8, 8, + 8, 8, 8, 8, 8,10,10, 0, 8, 8, 8, 8, 9, 8, 9, 9, + 9,10,10,10, 0, 9, 8, 8, 8, 9, 9, 8, 8, 9, 9,10, + 10, 0,12,11, 8, 8, 9, 9, 9, 9,10,10,11,10, 0,12, + 13, 8, 8, 9,10, 9, 9,11,11,11,12, 0, 0, 0, 8, 8, + 8, 8,10, 9,12,13,12,14, 0, 0, 0, 8, 8, 8, 9,10, + 10,12,12,13,14, 0, 0, 0,13,13, 9, 9,11,11, 0, 0, + 14, 0, 0, 0, 0,14,14,10,10,12,11,12,14,14,14, 0, + 0, 0, 0, 0,11,11,13,13,14,13,14,14, 0, 0, 0, 0, + 0,12,13,13,12,13,14,14,14, +}; + +static float _vq_quantthresh__16c0_s_p8_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__16c0_s_p8_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__16c0_s_p8_0 = { + _vq_quantthresh__16c0_s_p8_0, + _vq_quantmap__16c0_s_p8_0, + 13, + 13 +}; + +static static_codebook _16c0_s_p8_0 = { + 2, 169, + _vq_lengthlist__16c0_s_p8_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__16c0_s_p8_0, + NULL, + &_vq_auxt__16c0_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c0_s_p8_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16c0_s_p8_1[] = { + 1, 4, 3, 5, 5, 7, 7, 7, 6, 6, 7, 7, 7, 5, 5, 7, + 7, 7, 6, 6, 7, 7, 7, 6, 6, +}; + +static float _vq_quantthresh__16c0_s_p8_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16c0_s_p8_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16c0_s_p8_1 = { + _vq_quantthresh__16c0_s_p8_1, + _vq_quantmap__16c0_s_p8_1, + 5, + 5 +}; + +static static_codebook _16c0_s_p8_1 = { + 2, 25, + _vq_lengthlist__16c0_s_p8_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16c0_s_p8_1, + NULL, + &_vq_auxt__16c0_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__16c0_s_p9_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16c0_s_p9_0[] = { + 1, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, +}; + +static float _vq_quantthresh__16c0_s_p9_0[] = { + -157.5, 157.5, +}; + +static long _vq_quantmap__16c0_s_p9_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16c0_s_p9_0 = { + _vq_quantthresh__16c0_s_p9_0, + _vq_quantmap__16c0_s_p9_0, + 3, + 3 +}; + +static static_codebook _16c0_s_p9_0 = { + 4, 81, + _vq_lengthlist__16c0_s_p9_0, + 1, -518803456, 1628680192, 2, 0, + _vq_quantlist__16c0_s_p9_0, + NULL, + &_vq_auxt__16c0_s_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c0_s_p9_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__16c0_s_p9_1[] = { + 1, 5, 5, 5, 5, 9,11,11,10,10,10,10,10,10,10, 7, + 6, 6, 6, 6,10,10,10,10,10,10,10,10,10,10, 7, 6, + 6, 6, 6,10, 9,10,10,10,10,10,10,10,10,10, 7, 7, + 8, 9,10,10,10,10,10,10,10,10,10,10,10, 8, 7,10, + 10,10, 9,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10, +}; + +static float _vq_quantthresh__16c0_s_p9_1[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__16c0_s_p9_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__16c0_s_p9_1 = { + _vq_quantthresh__16c0_s_p9_1, + _vq_quantmap__16c0_s_p9_1, + 15, + 15 +}; + +static static_codebook _16c0_s_p9_1 = { + 2, 225, + _vq_lengthlist__16c0_s_p9_1, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__16c0_s_p9_1, + NULL, + &_vq_auxt__16c0_s_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__16c0_s_p9_2[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__16c0_s_p9_2[] = { + 1, 5, 5, 7, 8, 8, 7, 9, 9, 9,12,12,11,12,12,10, + 10,11,12,12,12,11,12,12, 8, 9, 8, 7, 9,10,10,11, + 11,10,11,12,10,12,10,12,12,12,11,12,11, 9, 8, 8, + 9,10, 9, 8, 9,10,12,12,11,11,12,11,10,11,12,11, + 12,12, 8, 9, 9, 9,10,11,12,11,12,11,11,11,11,12, + 12,11,11,12,12,11,11, 9, 9, 8, 9, 9,11, 9, 9,10, + 9,11,11,11,11,12,11,11,10,12,12,12, 9,12,11,10, + 11,11,11,11,12,12,12,11,11,11,12,10,12,12,12,10, + 10, 9,10, 9,10,10, 9, 9, 9,10,10,12,10,11,11, 9, + 11,11,10,11,11,11,10,10,10, 9, 9,10,10, 9, 9,10, + 11,11,10,11,10,11,10,11,11,10,11,11,11,10, 9,10, + 10, 9,10, 9, 9,11, 9, 9,11,10,10,11,11,10,10,11, + 10,11, 8, 9,11,11,10, 9,10,11,11,10,11,11,10,10, + 10,11,10, 9,10,10,11, 9,10,10, 9,11,10,10,10,10, + 11,10,11,11, 9,11,10,11,10,10,11,11,10,10,10, 9, + 10,10,11,11,11, 9,10,10,10,10,10,11,10,10,10, 9, + 10,10,11,10,10,10,10,10, 9,10,11,10,10,10,10,11, + 11,11,10,10,10,10,10,11,10,11,10,11,10,10,10, 9, + 11,11,10,10,10,11,11,10,10,10,10,10,10,10,10,11, + 11, 9,10,10,10,11,10,11,10,10,10,11, 9,10,11,10, + 11,10,10, 9,10,10,10,11,10,11,10,10,10,10,10,11, + 11,10,11,11,10,10,11,11,10, 9, 9,10,10,10,10,10, + 9,11, 9,10,10,10,11,11,10,10,10,10,11,11,11,10, + 9, 9,10,10,11,10,10,10,10,10,11,11,11,10,10,10, + 11,11,11, 9,10,10,10,10, 9,10, 9,10,11,10,11,10, + 10,11,11,10,11,11,11,11,11,10,11,10,10,10, 9,11, + 11,10,11,11,11,11,11,11,11,11,11,10,11,10,10,10, + 10,11,10,10,11, 9,10,10,10, +}; + +static float _vq_quantthresh__16c0_s_p9_2[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__16c0_s_p9_2[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__16c0_s_p9_2 = { + _vq_quantthresh__16c0_s_p9_2, + _vq_quantmap__16c0_s_p9_2, + 21, + 21 +}; + +static static_codebook _16c0_s_p9_2 = { + 2, 441, + _vq_lengthlist__16c0_s_p9_2, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__16c0_s_p9_2, + NULL, + &_vq_auxt__16c0_s_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__16c0_s_single[] = { + 3, 4,19, 7, 9, 7, 8,11, 9,12, 4, 1,19, 6, 7, 7, + 8,10,11,13,18,18,18,18,18,18,18,18,18,18, 8, 6, + 18, 8, 9, 9,11,12,14,18, 9, 6,18, 9, 7, 8, 9,11, + 12,18, 7, 6,18, 8, 7, 7, 7, 9,11,17, 8, 8,18, 9, + 7, 6, 6, 8,11,17,10,10,18,12, 9, 8, 7, 9,12,18, + 13,15,18,15,13,11,10,11,15,18,14,18,18,18,18,18, + 16,16,18,18, +}; + +static static_codebook _huff_book__16c0_s_single = { + 2, 100, + _huff_lengthlist__16c0_s_single, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__16c1_s_long[] = { + 2, 5,20, 7,10, 7, 8,10,11,11, 4, 2,20, 5, 8, 6, + 7, 9,10,10,20,20,20,20,19,19,19,19,19,19, 7, 5, + 19, 6,10, 7, 9,11,13,17,11, 8,19,10, 7, 7, 8,10, + 11,15, 7, 5,19, 7, 7, 5, 6, 9,11,16, 7, 6,19, 8, + 7, 6, 6, 7, 9,13, 9, 9,19,11, 9, 8, 6, 7, 8,13, + 12,14,19,16,13,10, 9, 8, 9,13,14,17,19,18,18,17, + 12,11,11,13, +}; + +static static_codebook _huff_book__16c1_s_long = { + 2, 100, + _huff_lengthlist__16c1_s_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__16c1_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16c1_s_p1_0[] = { + 1, 5, 5, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 8, 7, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, + 0, 0, 0, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 7, 8, 0, 0, 0, 0, 0, 0, 7, 9, 8, 0, 0, + 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 8, 7, 0, 0, 0, 0, + 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, + 0, 0, 0, 9, 9,11, 0, 0, 0, 0, 0, 0, 9,11,10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 8,11, 9, 0, 0, 0, 0, 0, 0, 9,10,11, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 7, 8, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 9,11,10, 0, + 0, 0, 0, 0, 0, 8, 9,11, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 9,10,11, + 0, 0, 0, 0, 0, 0, 9,11, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__16c1_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__16c1_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16c1_s_p1_0 = { + _vq_quantthresh__16c1_s_p1_0, + _vq_quantmap__16c1_s_p1_0, + 3, + 3 +}; + +static static_codebook _16c1_s_p1_0 = { + 8, 6561, + _vq_lengthlist__16c1_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__16c1_s_p1_0, + NULL, + &_vq_auxt__16c1_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c1_s_p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16c1_s_p3_0[] = { + 1, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 7, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 5, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 7, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 7, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__16c1_s_p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16c1_s_p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16c1_s_p3_0 = { + _vq_quantthresh__16c1_s_p3_0, + _vq_quantmap__16c1_s_p3_0, + 5, + 5 +}; + +static static_codebook _16c1_s_p3_0 = { + 4, 625, + _vq_lengthlist__16c1_s_p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16c1_s_p3_0, + NULL, + &_vq_auxt__16c1_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c1_s_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__16c1_s_p4_0[] = { + 1, 2, 3, 7, 7, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, + 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 7, 7, + 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 0, 0, 0, + 0, 0,10,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__16c1_s_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__16c1_s_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__16c1_s_p4_0 = { + _vq_quantthresh__16c1_s_p4_0, + _vq_quantmap__16c1_s_p4_0, + 9, + 9 +}; + +static static_codebook _16c1_s_p4_0 = { + 2, 81, + _vq_lengthlist__16c1_s_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__16c1_s_p4_0, + NULL, + &_vq_auxt__16c1_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c1_s_p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__16c1_s_p5_0[] = { + 1, 3, 3, 5, 5, 6, 6, 8, 8, 0, 0, 0, 7, 7, 7, 7, + 9, 9, 0, 0, 0, 7, 7, 7, 7, 9, 9, 0, 0, 0, 8, 8, + 8, 8, 9, 9, 0, 0, 0, 8, 8, 8, 8,10,10, 0, 0, 0, + 9, 9, 8, 8,10,10, 0, 0, 0, 9, 9, 8, 8,10,10, 0, + 0, 0,10,10, 9, 9,10,10, 0, 0, 0, 0, 0, 9, 9,10, + 10, +}; + +static float _vq_quantthresh__16c1_s_p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__16c1_s_p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__16c1_s_p5_0 = { + _vq_quantthresh__16c1_s_p5_0, + _vq_quantmap__16c1_s_p5_0, + 9, + 9 +}; + +static static_codebook _16c1_s_p5_0 = { + 2, 81, + _vq_lengthlist__16c1_s_p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__16c1_s_p5_0, + NULL, + &_vq_auxt__16c1_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c1_s_p6_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__16c1_s_p6_0[] = { + 1, 3, 3, 6, 6, 8, 8, 9, 9, 9, 9,10,10,11,11,12, + 12, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11,11, + 12,12, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11, + 11,12,12, 0, 0, 0, 8, 8, 8, 9,10, 9,10,10,10,10, + 11,11,12,12, 0, 0, 0, 8, 8, 9, 9,10,10,10,10,11, + 11,11,12,12,12, 0, 0, 0, 8, 8, 9, 9,10,10,10,10, + 11,11,12,12,12,12, 0, 0, 0, 8, 8, 9, 9,10,10,10, + 10,11,11,12,12,13,13, 0, 0, 0, 9, 9, 9, 9,10,10, + 10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, 9,10, + 10,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, 9, + 10,10,11,11,12,12,12,12,13,13, 0, 0, 0, 0, 0, 9, + 9,10,10,11,11,12,12,12,12,13,13, 0, 0, 0, 0, 0, + 10,10,11,10,11,11,12,12,13,13,13,13, 0, 0, 0, 0, + 0, 0, 0,10,10,11,11,12,12,13,13,13,13, 0, 0, 0, + 0, 0, 0, 0,11,11,12,12,12,12,13,13,14,14, 0, 0, + 0, 0, 0, 0, 0,11,11,12,12,12,12,13,13,14,14, 0, + 0, 0, 0, 0, 0, 0,12,12,12,12,13,13,13,13,14,14, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,13,13,13,13,14, + 14, +}; + +static float _vq_quantthresh__16c1_s_p6_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__16c1_s_p6_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__16c1_s_p6_0 = { + _vq_quantthresh__16c1_s_p6_0, + _vq_quantmap__16c1_s_p6_0, + 17, + 17 +}; + +static static_codebook _16c1_s_p6_0 = { + 2, 289, + _vq_lengthlist__16c1_s_p6_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__16c1_s_p6_0, + NULL, + &_vq_auxt__16c1_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c1_s_p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16c1_s_p7_0[] = { + 1, 4, 4, 6, 6, 6, 7, 6, 6, 4, 7, 7,10, 9,10,10, + 10, 9, 4, 7, 7,10,10,10,11,10,10, 6,10,10,11,11, + 11,11,10,10, 6,10, 9,11,11,11,11,10,10, 6,10,10, + 11,11,11,11,10,10, 7,11,11,11,11,11,12,12,11, 6, + 10,10,11,10,10,11,11,11, 6,10,10,10,11,10,11,11, + 11, +}; + +static float _vq_quantthresh__16c1_s_p7_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__16c1_s_p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16c1_s_p7_0 = { + _vq_quantthresh__16c1_s_p7_0, + _vq_quantmap__16c1_s_p7_0, + 3, + 3 +}; + +static static_codebook _16c1_s_p7_0 = { + 4, 81, + _vq_lengthlist__16c1_s_p7_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__16c1_s_p7_0, + NULL, + &_vq_auxt__16c1_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c1_s_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__16c1_s_p7_1[] = { + 2, 3, 3, 5, 6, 7, 7, 7, 7, 8, 8,10,10,10, 6, 6, + 7, 7, 8, 8, 8, 8,10,10,10, 6, 6, 7, 7, 8, 8, 8, + 8,10,10,10, 7, 7, 7, 7, 8, 8, 8, 8,10,10,10, 7, + 7, 7, 7, 8, 8, 8, 8,10,10,10, 7, 7, 8, 8, 8, 8, + 8, 8,10,10,10, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, + 8, 8, 8, 8, 8, 8, 9, 9,10,10,10,10,10, 8, 8, 8, + 8, 9, 9,10,10,10,10,10, 9, 9, 8, 8, 9, 9,10,10, + 10,10,10, 8, 8, 8, 8, 9, 9, +}; + +static float _vq_quantthresh__16c1_s_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__16c1_s_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__16c1_s_p7_1 = { + _vq_quantthresh__16c1_s_p7_1, + _vq_quantmap__16c1_s_p7_1, + 11, + 11 +}; + +static static_codebook _16c1_s_p7_1 = { + 2, 121, + _vq_lengthlist__16c1_s_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__16c1_s_p7_1, + NULL, + &_vq_auxt__16c1_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__16c1_s_p8_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__16c1_s_p8_0[] = { + 1, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 6, 5, 5, + 7, 8, 8, 9, 8, 8, 9, 9,10,11, 6, 5, 5, 8, 8, 9, + 9, 8, 8, 9,10,10,11, 0, 8, 8, 8, 9, 9, 9, 9, 9, + 10,10,11,11, 0, 9, 9, 9, 8, 9, 9, 9, 9,10,10,11, + 11, 0,13,13, 9, 9,10,10,10,10,11,11,12,12, 0,14, + 13, 9, 9,10,10,10,10,11,11,12,12, 0, 0, 0,10,10, + 9, 9,11,11,12,12,13,12, 0, 0, 0,10,10, 9, 9,10, + 10,12,12,13,13, 0, 0, 0,13,14,11,10,11,11,12,12, + 13,14, 0, 0, 0,14,14,10,10,11,11,12,12,13,13, 0, + 0, 0, 0, 0,12,12,12,12,13,13,14,15, 0, 0, 0, 0, + 0,12,12,12,12,13,13,14,15, +}; + +static float _vq_quantthresh__16c1_s_p8_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__16c1_s_p8_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__16c1_s_p8_0 = { + _vq_quantthresh__16c1_s_p8_0, + _vq_quantmap__16c1_s_p8_0, + 13, + 13 +}; + +static static_codebook _16c1_s_p8_0 = { + 2, 169, + _vq_lengthlist__16c1_s_p8_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__16c1_s_p8_0, + NULL, + &_vq_auxt__16c1_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c1_s_p8_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16c1_s_p8_1[] = { + 2, 3, 3, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, 6, + 6, 6, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__16c1_s_p8_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16c1_s_p8_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16c1_s_p8_1 = { + _vq_quantthresh__16c1_s_p8_1, + _vq_quantmap__16c1_s_p8_1, + 5, + 5 +}; + +static static_codebook _16c1_s_p8_1 = { + 2, 25, + _vq_lengthlist__16c1_s_p8_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16c1_s_p8_1, + NULL, + &_vq_auxt__16c1_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__16c1_s_p9_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__16c1_s_p9_0[] = { + 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__16c1_s_p9_0[] = { + -1732.5, -1417.5, -1102.5, -787.5, -472.5, -157.5, 157.5, 472.5, + 787.5, 1102.5, 1417.5, 1732.5, +}; + +static long _vq_quantmap__16c1_s_p9_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__16c1_s_p9_0 = { + _vq_quantthresh__16c1_s_p9_0, + _vq_quantmap__16c1_s_p9_0, + 13, + 13 +}; + +static static_codebook _16c1_s_p9_0 = { + 2, 169, + _vq_lengthlist__16c1_s_p9_0, + 1, -513964032, 1628680192, 4, 0, + _vq_quantlist__16c1_s_p9_0, + NULL, + &_vq_auxt__16c1_s_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c1_s_p9_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__16c1_s_p9_1[] = { + 1, 4, 4, 4, 4, 8, 8,12,13,14,14,14,14,14,14, 6, + 6, 6, 6, 6,10, 9,14,14,14,14,14,14,14,14, 7, 6, + 5, 6, 6,10, 9,12,13,13,13,13,13,13,13,13, 7, 7, + 9, 9,11,11,12,13,13,13,13,13,13,13,13, 7, 7, 8, + 8,11,12,13,13,13,13,13,13,13,13,13,12,12,10,10, + 13,12,13,13,13,13,13,13,13,13,13,12,12,10,10,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,12,13,12, + 13,13,13,13,13,13,13,13,13,13,13,13,12,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,12,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,12,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13, +}; + +static float _vq_quantthresh__16c1_s_p9_1[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__16c1_s_p9_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__16c1_s_p9_1 = { + _vq_quantthresh__16c1_s_p9_1, + _vq_quantmap__16c1_s_p9_1, + 15, + 15 +}; + +static static_codebook _16c1_s_p9_1 = { + 2, 225, + _vq_lengthlist__16c1_s_p9_1, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__16c1_s_p9_1, + NULL, + &_vq_auxt__16c1_s_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__16c1_s_p9_2[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__16c1_s_p9_2[] = { + 1, 4, 4, 6, 6, 7, 7, 8, 7, 8, 8, 9, 9, 9, 9,10, + 10,10, 9,10,10,11,12,12, 8, 8, 8, 8, 9, 9, 9, 9, + 10,10,10,10,10,11,11,10,12,11,11,13,11, 7, 7, 8, + 8, 8, 8, 9, 9, 9,10,10,10,10, 9,10,10,11,11,12, + 11,11, 8, 8, 8, 8, 9, 9,10,10,10,10,11,11,11,11, + 11,11,11,12,11,12,12, 8, 8, 9, 9, 9, 9, 9,10,10, + 10,10,10,10,11,11,11,11,11,11,12,11, 9, 9, 9, 9, + 10,10,10,10,11,10,11,11,11,11,11,11,12,12,12,12, + 11, 9, 9, 9, 9,10,10,10,10,11,11,11,11,11,11,11, + 11,11,12,12,12,13, 9,10,10, 9,11,10,10,10,10,11, + 11,11,11,11,10,11,12,11,12,12,11,12,11,10, 9,10, + 10,11,10,11,11,11,11,11,11,11,11,11,12,12,11,12, + 12,12,10,10,10,11,10,11,11,11,11,11,11,11,11,11, + 11,11,12,13,12,12,11, 9,10,10,11,11,10,11,11,11, + 12,11,11,11,11,11,12,12,13,13,12,13,10,10,12,10, + 11,11,11,11,11,11,11,11,11,12,12,11,13,12,12,12, + 12,13,12,11,11,11,11,11,11,12,11,12,11,11,11,11, + 12,12,13,12,11,12,12,11,11,11,11,11,12,11,11,11, + 11,12,11,11,12,11,12,13,13,12,12,12,12,11,11,11, + 11,11,12,11,11,12,11,12,11,11,11,11,13,12,12,12, + 12,13,11,11,11,12,12,11,11,11,12,11,12,12,12,11, + 12,13,12,11,11,12,12,11,12,11,11,11,12,12,11,12, + 11,11,11,12,12,12,12,13,12,13,12,12,12,12,11,11, + 12,11,11,11,11,11,11,12,12,12,13,12,11,13,13,12, + 12,11,12,10,11,11,11,11,12,11,12,12,11,12,12,13, + 12,12,13,12,12,12,12,12,11,12,12,12,11,12,11,11, + 11,12,13,12,13,13,13,13,13,12,13,13,12,12,13,11, + 11,11,11,11,12,11,11,12,11, +}; + +static float _vq_quantthresh__16c1_s_p9_2[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__16c1_s_p9_2[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__16c1_s_p9_2 = { + _vq_quantthresh__16c1_s_p9_2, + _vq_quantmap__16c1_s_p9_2, + 21, + 21 +}; + +static static_codebook _16c1_s_p9_2 = { + 2, 441, + _vq_lengthlist__16c1_s_p9_2, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__16c1_s_p9_2, + NULL, + &_vq_auxt__16c1_s_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__16c1_s_short[] = { + 5, 6,17, 8,12, 9,10,10,12,13, 5, 2,17, 4, 9, 5, + 7, 8,11,13,16,16,16,16,16,16,16,16,16,16, 6, 4, + 16, 5,10, 5, 7,10,14,16,13, 9,16,11, 8, 7, 8, 9, + 13,16, 7, 4,16, 5, 7, 4, 6, 8,11,13, 8, 6,16, 7, + 8, 5, 5, 7, 9,13, 9, 8,16, 9, 8, 6, 6, 7, 9,13, + 11,11,16,10,10, 7, 7, 7, 9,13,13,13,16,13,13, 9, + 9, 9,10,13, +}; + +static static_codebook _huff_book__16c1_s_short = { + 2, 100, + _huff_lengthlist__16c1_s_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__16c2_s_long[] = { + 4, 7, 9, 9, 9, 8, 9,10,15,19, 5, 4, 5, 6, 7, 7, + 8, 9,14,16, 6, 5, 4, 5, 6, 7, 8,10,12,19, 7, 6, + 5, 4, 5, 6, 7, 9,11,18, 8, 7, 6, 5, 5, 5, 7, 9, + 10,17, 8, 7, 7, 5, 5, 5, 6, 7,12,18, 8, 8, 8, 7, + 7, 5, 5, 7,12,18, 8, 9,10, 9, 9, 7, 6, 7,12,17, + 14,18,16,16,15,12,11,10,12,18,15,17,18,18,18,15, + 14,14,16,18, +}; + +static static_codebook _huff_book__16c2_s_long = { + 2, 100, + _huff_lengthlist__16c2_s_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16c2_s_p1_0[] = { + 1, 3, 3, 0, 0, 0, 0, 0, 0, 4, 5, 5, 0, 0, 0, 0, + 0, 0, 4, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__16c2_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__16c2_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p1_0 = { + _vq_quantthresh__16c2_s_p1_0, + _vq_quantmap__16c2_s_p1_0, + 3, + 3 +}; + +static static_codebook _16c2_s_p1_0 = { + 4, 81, + _vq_lengthlist__16c2_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__16c2_s_p1_0, + NULL, + &_vq_auxt__16c2_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16c2_s_p2_0[] = { + 2, 4, 3, 7, 7, 0, 0, 0, 7, 8, 0, 0, 0, 8, 8, 0, + 0, 0, 8, 8, 0, 0, 0, 8, 8, 4, 5, 4, 8, 8, 0, 0, + 0, 8, 8, 0, 0, 0, 8, 8, 0, 0, 0, 9, 9, 0, 0, 0, + 9, 9, 4, 4, 5, 8, 8, 0, 0, 0, 8, 8, 0, 0, 0, 8, + 8, 0, 0, 0, 9, 9, 0, 0, 0, 9, 9, 7, 8, 8,10,10, + 0, 0, 0,12,11, 0, 0, 0,11,11, 0, 0, 0,14,13, 0, + 0, 0,14,13, 7, 8, 8, 9,10, 0, 0, 0,11,12, 0, 0, + 0,11,11, 0, 0, 0,14,14, 0, 0, 0,13,14, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8,11,11, 0, 0, 0, + 11,11, 0, 0, 0,12,11, 0, 0, 0,12,12, 0, 0, 0,13, + 13, 8, 8, 8,11,11, 0, 0, 0,11,11, 0, 0, 0,11,12, + 0, 0, 0,12,13, 0, 0, 0,13,13, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 8, 8, 8,12,11, 0, 0, 0,12,11, 0, + 0, 0,11,11, 0, 0, 0,13,13, 0, 0, 0,13,12, 8, 8, + 8,11,12, 0, 0, 0,11,12, 0, 0, 0,11,11, 0, 0, 0, + 13,13, 0, 0, 0,12,13, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 8, 9, 9,14,13, 0, 0, 0,13,12, 0, 0, 0,13, + 13, 0, 0, 0,13,12, 0, 0, 0,13,13, 8, 9, 9,13,14, + 0, 0, 0,12,13, 0, 0, 0,13,13, 0, 0, 0,12,13, 0, + 0, 0,13,13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, + 9, 9,14,13, 0, 0, 0,13,13, 0, 0, 0,13,12, 0, 0, + 0,13,13, 0, 0, 0,13,12, 8, 9, 9,14,14, 0, 0, 0, + 13,13, 0, 0, 0,12,13, 0, 0, 0,13,13, 0, 0, 0,12, + 13, +}; + +static float _vq_quantthresh__16c2_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16c2_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p2_0 = { + _vq_quantthresh__16c2_s_p2_0, + _vq_quantmap__16c2_s_p2_0, + 5, + 5 +}; + +static static_codebook _16c2_s_p2_0 = { + 4, 625, + _vq_lengthlist__16c2_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16c2_s_p2_0, + NULL, + &_vq_auxt__16c2_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__16c2_s_p3_0[] = { + 1, 3, 3, 6, 6, 7, 7, 8, 8, 0, 0, 0, 6, 6, 7, 7, + 9, 9, 0, 0, 0, 6, 6, 7, 7, 9, 9, 0, 0, 0, 7, 7, + 8, 8,10,10, 0, 0, 0, 7, 7, 8, 8,10,10, 0, 0, 0, + 7, 7, 9, 9,10,10, 0, 0, 0, 7, 7, 9, 9,10,10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__16c2_s_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__16c2_s_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p3_0 = { + _vq_quantthresh__16c2_s_p3_0, + _vq_quantmap__16c2_s_p3_0, + 9, + 9 +}; + +static static_codebook _16c2_s_p3_0 = { + 2, 81, + _vq_lengthlist__16c2_s_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__16c2_s_p3_0, + NULL, + &_vq_auxt__16c2_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p4_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__16c2_s_p4_0[] = { + 2, 3, 3, 5, 5, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9,10, + 10, 0, 0, 0, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10, + 11,11, 0, 0, 0, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10, + 10,10,11, 0, 0, 0, 6, 6, 8, 8, 8, 8, 9, 9,10,10, + 10,11,11,11, 0, 0, 0, 6, 6, 8, 8, 9, 9, 9, 9,10, + 10,11,11,11,11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9, + 10,10,11,11,12,12, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, + 9,10,10,11,11,12,12, 0, 0, 0, 7, 7, 8, 8, 9, 9, + 10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 8, 8, 9, + 9,10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__16c2_s_p4_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__16c2_s_p4_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p4_0 = { + _vq_quantthresh__16c2_s_p4_0, + _vq_quantmap__16c2_s_p4_0, + 17, + 17 +}; + +static static_codebook _16c2_s_p4_0 = { + 2, 289, + _vq_lengthlist__16c2_s_p4_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__16c2_s_p4_0, + NULL, + &_vq_auxt__16c2_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p5_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16c2_s_p5_0[] = { + 1, 4, 4, 5, 7, 7, 6, 7, 7, 4, 6, 6,10,10,10,10, + 10,10, 4, 7, 6,10,10,10,10,10,10, 5, 9, 9, 9,12, + 11,10,11,12, 7,10,10,12,12,12,12,12,12, 7,10,10, + 11,12,12,12,12,13, 6,10,10,10,12,12,10,12,12, 7, + 10,10,11,13,12,12,12,12, 7,10,10,11,12,12,12,12, + 12, +}; + +static float _vq_quantthresh__16c2_s_p5_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__16c2_s_p5_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p5_0 = { + _vq_quantthresh__16c2_s_p5_0, + _vq_quantmap__16c2_s_p5_0, + 3, + 3 +}; + +static static_codebook _16c2_s_p5_0 = { + 4, 81, + _vq_lengthlist__16c2_s_p5_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__16c2_s_p5_0, + NULL, + &_vq_auxt__16c2_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p5_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__16c2_s_p5_1[] = { + 2, 3, 3, 6, 6, 7, 7, 7, 7, 8, 8,11,11,11, 6, 6, + 7, 7, 8, 8, 8, 8,11,11,11, 6, 6, 7, 7, 8, 8, 8, + 8,11,11,11, 6, 6, 8, 8, 8, 8, 9, 9,11,11,11, 6, + 6, 8, 8, 8, 8, 9, 9,11,11,11, 7, 7, 8, 8, 8, 8, + 8, 8,11,11,11, 7, 7, 8, 8, 8, 8, 8, 9,11,11,11, + 8, 8, 8, 8, 8, 8, 8, 8,11,11,11,11,11, 8, 8, 8, + 8, 8, 8,11,11,11,11,11, 8, 8, 8, 8, 8, 8,11,11, + 11,11,11, 7, 7, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__16c2_s_p5_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__16c2_s_p5_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p5_1 = { + _vq_quantthresh__16c2_s_p5_1, + _vq_quantmap__16c2_s_p5_1, + 11, + 11 +}; + +static static_codebook _16c2_s_p5_1 = { + 2, 121, + _vq_lengthlist__16c2_s_p5_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__16c2_s_p5_1, + NULL, + &_vq_auxt__16c2_s_p5_1, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__16c2_s_p6_0[] = { + 1, 4, 4, 7, 6, 8, 8, 9, 9,10,10,11,11, 5, 5, 5, + 7, 7, 9, 9, 9, 9,11,11,12,12, 6, 5, 5, 7, 7, 9, + 9,10,10,11,11,12,12, 0, 6, 6, 7, 7, 9, 9,10,10, + 11,11,12,12, 0, 7, 7, 7, 7, 9, 9,10,10,11,12,12, + 12, 0,11,11, 8, 8,10,10,11,11,12,12,13,13, 0,11, + 12, 8, 8,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static float _vq_quantthresh__16c2_s_p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__16c2_s_p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p6_0 = { + _vq_quantthresh__16c2_s_p6_0, + _vq_quantmap__16c2_s_p6_0, + 13, + 13 +}; + +static static_codebook _16c2_s_p6_0 = { + 2, 169, + _vq_lengthlist__16c2_s_p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__16c2_s_p6_0, + NULL, + &_vq_auxt__16c2_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16c2_s_p6_1[] = { + 2, 3, 3, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, 6, + 6, 6, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__16c2_s_p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16c2_s_p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p6_1 = { + _vq_quantthresh__16c2_s_p6_1, + _vq_quantmap__16c2_s_p6_1, + 5, + 5 +}; + +static static_codebook _16c2_s_p6_1 = { + 2, 25, + _vq_lengthlist__16c2_s_p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16c2_s_p6_1, + NULL, + &_vq_auxt__16c2_s_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__16c2_s_p7_0[] = { + 1, 4, 4, 7, 7, 8, 8, 9, 9,10,10,11,11, 5, 5, 5, + 8, 8, 9, 9,10,10,11,11,12,12, 6, 5, 5, 8, 8, 9, + 9,10,10,11,11,12,13,18, 6, 6, 7, 7, 9, 9,10,10, + 12,12,13,13,18, 6, 6, 7, 7, 9, 9,10,10,12,12,13, + 13,18,11,10, 8, 8,10,10,11,11,12,12,13,13,18,11, + 11, 8, 8,10,10,11,11,12,13,13,13,18,18,18,10,11, + 11,11,12,12,13,13,14,14,18,18,18,11,11,11,11,12, + 12,13,13,14,14,18,18,18,14,14,12,12,12,12,14,14, + 15,14,18,18,18,15,15,11,12,12,12,13,13,15,15,18, + 18,18,18,18,13,13,13,13,13,14,17,16,18,18,18,18, + 18,13,14,13,13,14,13,15,14, +}; + +static float _vq_quantthresh__16c2_s_p7_0[] = { + -60.5, -49.5, -38.5, -27.5, -16.5, -5.5, 5.5, 16.5, + 27.5, 38.5, 49.5, 60.5, +}; + +static long _vq_quantmap__16c2_s_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p7_0 = { + _vq_quantthresh__16c2_s_p7_0, + _vq_quantmap__16c2_s_p7_0, + 13, + 13 +}; + +static static_codebook _16c2_s_p7_0 = { + 2, 169, + _vq_lengthlist__16c2_s_p7_0, + 1, -523206656, 1618345984, 4, 0, + _vq_quantlist__16c2_s_p7_0, + NULL, + &_vq_auxt__16c2_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__16c2_s_p7_1[] = { + 2, 4, 4, 6, 6, 7, 7, 7, 7, 7, 7, 9, 9, 9, 6, 6, + 7, 7, 8, 8, 8, 8, 9, 9, 9, 6, 6, 7, 7, 8, 8, 8, + 8, 9, 9, 9, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 7, + 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 7, 7, 7, 7, 8, 8, + 8, 8, 9, 9, 9, 7, 7, 7, 7, 7, 7, 8, 8, 9, 9, 9, + 7, 7, 8, 8, 7, 7, 8, 8, 9, 9, 9, 9, 9, 7, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 8, 8, 7, 7, 8, 8, 9, 9, + 9, 9, 9, 7, 7, 7, 7, 8, 8, +}; + +static float _vq_quantthresh__16c2_s_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__16c2_s_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p7_1 = { + _vq_quantthresh__16c2_s_p7_1, + _vq_quantmap__16c2_s_p7_1, + 11, + 11 +}; + +static static_codebook _16c2_s_p7_1 = { + 2, 121, + _vq_lengthlist__16c2_s_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__16c2_s_p7_1, + NULL, + &_vq_auxt__16c2_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p8_0[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__16c2_s_p8_0[] = { + 1, 4, 4, 7, 6, 7, 7, 6, 6, 8, 8, 9, 9,10,10, 6, + 6, 6, 8, 8, 9, 8, 8, 8, 9, 9,11,10,11,11, 7, 6, + 6, 8, 8, 9, 8, 7, 7, 9, 9,10,10,12,11,14, 8, 8, + 8, 9, 9, 9, 9, 9,10, 9,10,10,11,13,14, 8, 8, 8, + 8, 9, 9, 8, 8, 9, 9,10,10,11,12,14,13,11, 9, 9, + 9, 9, 9, 9, 9,10,11,10,13,12,14,11,13, 8, 9, 9, + 9, 9, 9,10,10,11,10,13,12,14,14,14, 8, 9, 9, 9, + 11,11,11,11,11,12,13,13,14,14,14, 9, 8, 9, 9,10, + 10,12,10,11,12,12,14,14,14,14,11,12,10,10,12,12, + 12,12,13,14,12,12,14,14,14,12,12, 9,10,11,11,12, + 14,12,14,14,14,14,14,14,14,14,11,11,12,11,12,14, + 14,14,14,14,14,14,14,14,14,12,11,11,11,11,14,14, + 14,14,14,14,14,14,14,14,14,14,13,12,14,14,14,14, + 14,14,14,14,14,14,14,14,14,12,12,12,13,14,14,13, + 13, +}; + +static float _vq_quantthresh__16c2_s_p8_0[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__16c2_s_p8_0[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p8_0 = { + _vq_quantthresh__16c2_s_p8_0, + _vq_quantmap__16c2_s_p8_0, + 15, + 15 +}; + +static static_codebook _16c2_s_p8_0 = { + 2, 225, + _vq_lengthlist__16c2_s_p8_0, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__16c2_s_p8_0, + NULL, + &_vq_auxt__16c2_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p8_1[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__16c2_s_p8_1[] = { + 2, 4, 4, 6, 6, 7, 7, 7, 7, 8, 7, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8,11,12,11, 7, 7, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 9, 9,10, 9, 9,11,11,10, 7, 7, 8, + 8, 8, 8, 9, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,11, + 11,11, 8, 7, 8, 8, 9, 9, 9, 9, 9, 9,10,10, 9,10, + 10, 9,10,10,11,11,12, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9,10, 9,10,10,10,10,11,11,11, 8, 8, 9, 9, + 9, 9, 9, 9, 9,10,10,10,10,10,10,10,10,10,11,11, + 11, 8, 8, 9, 8, 9, 9, 9, 9,10, 9, 9, 9,10,10,10, + 10, 9,10,11,11,11, 9, 9, 9, 9,10, 9, 9, 9,10,10, + 9,10, 9,10,10,10,10,10,11,12,11,11,11, 9, 9, 9, + 9, 9,10,10, 9,10,10,10,10,10,10,10,10,12,11,13, + 13,11, 9, 9, 9, 9,10,10, 9,10,10,10,10,11,10,10, + 10,10,11,12,11,12,11, 9, 9, 9,10,10, 9,10,10,10, + 10,10,10,10,10,10,10,11,11,11,12,11, 9,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,11,12,12,12, + 11,11,11,10, 9,10,10,10,10,10,10,10,10,11,10,10, + 10,11,11,11,11,11,11,11,10,10,10,11,10,10,10,10, + 10,10,10,10,10,10,11,11,11,11,12,12,11,10,10,10, + 10,10,10,10,10,11,10,10,10,11,10,12,11,11,12,11, + 11,11,10,10,10,10,10,11,10,10,10,10,10,11,10,10, + 11,11,11,12,11,12,11,11,12,10,10,10,10,10,10,10, + 11,10,10,11,10,12,11,11,11,12,11,11,11,11,10,10, + 10,10,10,10,10,11,11,11,10,11,12,11,11,11,12,11, + 12,11,12,10,11,10,10,10,10,11,10,10,10,10,10,10, + 12,11,11,11,11,11,12,12,10,10,10,10,10,11,10,10, + 11,10,11,11,11,11,11,11,11,11,11,11,11,11,12,11, + 10,11,10,10,10,10,10,10,10, +}; + +static float _vq_quantthresh__16c2_s_p8_1[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__16c2_s_p8_1[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p8_1 = { + _vq_quantthresh__16c2_s_p8_1, + _vq_quantmap__16c2_s_p8_1, + 21, + 21 +}; + +static static_codebook _16c2_s_p8_1 = { + 2, 441, + _vq_lengthlist__16c2_s_p8_1, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__16c2_s_p8_1, + NULL, + &_vq_auxt__16c2_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p9_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__16c2_s_p9_0[] = { + 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__16c2_s_p9_0[] = { + -5120.5, -4189.5, -3258.5, -2327.5, -1396.5, -465.5, 465.5, 1396.5, + 2327.5, 3258.5, 4189.5, 5120.5, +}; + +static long _vq_quantmap__16c2_s_p9_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p9_0 = { + _vq_quantthresh__16c2_s_p9_0, + _vq_quantmap__16c2_s_p9_0, + 13, + 13 +}; + +static static_codebook _16c2_s_p9_0 = { + 2, 169, + _vq_lengthlist__16c2_s_p9_0, + 1, -510275072, 1631393792, 4, 0, + _vq_quantlist__16c2_s_p9_0, + NULL, + &_vq_auxt__16c2_s_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p9_1[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__16c2_s_p9_1[] = { + 1, 5, 5, 9, 8, 7, 7, 7, 6,10,11,11,11,11,11,11, + 11, 8, 7, 6, 8, 8,10, 9,10,10,10, 9,11,10,10,10, + 10,10, 8, 6, 6, 8, 8, 9, 8, 9, 8, 9,10,10,10,10, + 10,10,10,10, 8,10, 9, 9, 9, 9,10,10,10,10,10,10, + 10,10,10,10,10, 8, 9, 9, 9,10,10, 9,10,10,10,10, + 10,10,10,10,10,10,10,10, 9, 8, 9, 9,10,10,10,10, + 10,10,10,10,10,10,10,10, 9, 8, 8, 9, 9,10,10,10, + 10,10,10,10,10,10,10,10,10,10, 9,10, 9, 9,10,10, + 10,10,10,10,10,10,10,10,10,10,10, 9, 8, 9, 9,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, 9, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 8,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10, 9,10, 9,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10, 9,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10, +}; + +static float _vq_quantthresh__16c2_s_p9_1[] = { + -367.5, -318.5, -269.5, -220.5, -171.5, -122.5, -73.5, -24.5, + 24.5, 73.5, 122.5, 171.5, 220.5, 269.5, 318.5, 367.5, +}; + +static long _vq_quantmap__16c2_s_p9_1[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p9_1 = { + _vq_quantthresh__16c2_s_p9_1, + _vq_quantmap__16c2_s_p9_1, + 17, + 17 +}; + +static static_codebook _16c2_s_p9_1 = { + 2, 289, + _vq_lengthlist__16c2_s_p9_1, + 1, -518488064, 1622704128, 5, 0, + _vq_quantlist__16c2_s_p9_1, + NULL, + &_vq_auxt__16c2_s_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__16c2_s_p9_2[] = { + 13, + 12, + 14, + 11, + 15, + 10, + 16, + 9, + 17, + 8, + 18, + 7, + 19, + 6, + 20, + 5, + 21, + 4, + 22, + 3, + 23, + 2, + 24, + 1, + 25, + 0, + 26, +}; + +static long _vq_lengthlist__16c2_s_p9_2[] = { + 1, 4, 4, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 8, 7, 8, 7, 7, 4, 4, +}; + +static float _vq_quantthresh__16c2_s_p9_2[] = { + -12.5, -11.5, -10.5, -9.5, -8.5, -7.5, -6.5, -5.5, + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, 10.5, + 11.5, 12.5, +}; + +static long _vq_quantmap__16c2_s_p9_2[] = { + 25, 23, 21, 19, 17, 15, 13, 11, + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, 12, 14, 16, 18, 20, + 22, 24, 26, +}; + +static encode_aux_threshmatch _vq_auxt__16c2_s_p9_2 = { + _vq_quantthresh__16c2_s_p9_2, + _vq_quantmap__16c2_s_p9_2, + 27, + 27 +}; + +static static_codebook _16c2_s_p9_2 = { + 1, 27, + _vq_lengthlist__16c2_s_p9_2, + 1, -528875520, 1611661312, 5, 0, + _vq_quantlist__16c2_s_p9_2, + NULL, + &_vq_auxt__16c2_s_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__16c2_s_short[] = { + 7,10,11,11,11,14,15,15,17,14, 8, 6, 7, 7, 8, 9, + 11,11,14,17, 9, 6, 6, 6, 7, 7,10,11,15,16, 9, 6, + 6, 4, 4, 5, 8, 9,12,16,10, 6, 6, 4, 4, 4, 6, 9, + 13,16,10, 7, 6, 5, 4, 3, 5, 7,13,16,11, 9, 8, 7, + 6, 5, 5, 6,12,15,10,10,10, 9, 7, 6, 6, 7,11,15, + 13,13,13,13,11,10,10, 9,12,16,16,16,16,14,16,15, + 15,12,14,14, +}; + +static static_codebook _huff_book__16c2_s_short = { + 2, 100, + _huff_lengthlist__16c2_s_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__8c0_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__8c0_s_p1_0[] = { + 1, 5, 4, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 8, 8, 0, 0, 0, 0, 0, 0, 7, 8, 9, 0, 0, 0, + 0, 0, 0, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 8, 8, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 7, 9, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 8, 8, 0, 0, 0, 0, + 0, 0, 8,10,10, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,10, 9, 0, 0, 0, + 0, 0, 0, 8, 9,11, 0, 0, 0, 0, 0, 0, 9,11,11, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9,10, 0, 0, + 0, 0, 0, 0, 9,11,10, 0, 0, 0, 0, 0, 0, 9,11,11, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 8, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, + 0, 0, 0, 0, 8, 9,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 9,11,11, 0, + 0, 0, 0, 0, 0, 9,10,11, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 9,10, 0, 0, 0, 0, 0, 0, 9,11,11, + 0, 0, 0, 0, 0, 0, 8,11, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__8c0_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__8c0_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__8c0_s_p1_0 = { + _vq_quantthresh__8c0_s_p1_0, + _vq_quantmap__8c0_s_p1_0, + 3, + 3 +}; + +static static_codebook _8c0_s_p1_0 = { + 8, 6561, + _vq_lengthlist__8c0_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__8c0_s_p1_0, + NULL, + &_vq_auxt__8c0_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c0_s_p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__8c0_s_p3_0[] = { + 1, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 5, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 7, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 7, 7, 8, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__8c0_s_p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__8c0_s_p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__8c0_s_p3_0 = { + _vq_quantthresh__8c0_s_p3_0, + _vq_quantmap__8c0_s_p3_0, + 5, + 5 +}; + +static static_codebook _8c0_s_p3_0 = { + 4, 625, + _vq_lengthlist__8c0_s_p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__8c0_s_p3_0, + NULL, + &_vq_auxt__8c0_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c0_s_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__8c0_s_p4_0[] = { + 1, 2, 3, 7, 7, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, + 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 7, 7, + 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 0, 0, 0, 0, 0, 0, 0, 9, 8, 0, 0, 0, 0, 0, + 0, 0,10,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__8c0_s_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__8c0_s_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__8c0_s_p4_0 = { + _vq_quantthresh__8c0_s_p4_0, + _vq_quantmap__8c0_s_p4_0, + 9, + 9 +}; + +static static_codebook _8c0_s_p4_0 = { + 2, 81, + _vq_lengthlist__8c0_s_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__8c0_s_p4_0, + NULL, + &_vq_auxt__8c0_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c0_s_p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__8c0_s_p5_0[] = { + 1, 3, 3, 5, 5, 7, 6, 8, 8, 0, 0, 0, 7, 7, 7, 7, + 8, 8, 0, 0, 0, 7, 7, 7, 7, 8, 9, 0, 0, 0, 8, 8, + 8, 8, 9, 9, 0, 0, 0, 8, 8, 8, 8, 9, 9, 0, 0, 0, + 9, 9, 8, 8,10,10, 0, 0, 0, 9, 9, 8, 8,10,10, 0, + 0, 0,10,10, 9, 9,10,10, 0, 0, 0, 0, 0, 9, 9,10, + 10, +}; + +static float _vq_quantthresh__8c0_s_p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__8c0_s_p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__8c0_s_p5_0 = { + _vq_quantthresh__8c0_s_p5_0, + _vq_quantmap__8c0_s_p5_0, + 9, + 9 +}; + +static static_codebook _8c0_s_p5_0 = { + 2, 81, + _vq_lengthlist__8c0_s_p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__8c0_s_p5_0, + NULL, + &_vq_auxt__8c0_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c0_s_p6_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__8c0_s_p6_0[] = { + 1, 3, 3, 6, 6, 8, 8, 9, 9, 8, 8,10, 9,10,10,11, + 11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11,11, + 11,12, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11, + 11,12,11, 0, 0, 0, 8, 8, 9, 9,10,10, 9, 9,10,10, + 11,11,12,12, 0, 0, 0, 8, 8, 9, 9,10,10, 9, 9,11, + 10,11,11,12,12, 0, 0, 0, 9, 9, 9, 9,10,10,10,10, + 11,11,11,12,12,12, 0, 0, 0, 9, 9, 9, 9,10,10,10, + 10,11,11,12,12,13,13, 0, 0, 0,10,10,10,10,11,11, + 10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0,10, 9,10, + 11,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, 9, + 10, 9,10,11,12,12,13,13,14,13, 0, 0, 0, 0, 0, 9, + 9, 9,10,10,10,11,11,13,12,13,13, 0, 0, 0, 0, 0, + 10,10,10,10,11,11,12,12,13,13,14,14, 0, 0, 0, 0, + 0, 0, 0,10,10,11,11,12,12,13,13,13,14, 0, 0, 0, + 0, 0, 0, 0,11,11,11,11,12,12,13,14,14,14, 0, 0, + 0, 0, 0, 0, 0,11,11,11,11,12,12,13,13,14,13, 0, + 0, 0, 0, 0, 0, 0,11,11,12,12,13,13,14,14,14,14, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,12,12,13,13,14, + 14, +}; + +static float _vq_quantthresh__8c0_s_p6_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__8c0_s_p6_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__8c0_s_p6_0 = { + _vq_quantthresh__8c0_s_p6_0, + _vq_quantmap__8c0_s_p6_0, + 17, + 17 +}; + +static static_codebook _8c0_s_p6_0 = { + 2, 289, + _vq_lengthlist__8c0_s_p6_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__8c0_s_p6_0, + NULL, + &_vq_auxt__8c0_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c0_s_p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__8c0_s_p7_0[] = { + 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 7,11, 9,10,12, + 9,10, 4, 7, 7,10,10,10,11, 9, 9, 6,11,10,11,11, + 12,11,11,11, 6,10,10,11,11,12,11,10,10, 6, 9,10, + 11,11,11,11,10,10, 7,10,11,12,11,11,12,11,12, 6, + 9, 9,10, 9, 9,11,10,10, 6, 9, 9,10,10,10,11,10, + 10, +}; + +static float _vq_quantthresh__8c0_s_p7_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__8c0_s_p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__8c0_s_p7_0 = { + _vq_quantthresh__8c0_s_p7_0, + _vq_quantmap__8c0_s_p7_0, + 3, + 3 +}; + +static static_codebook _8c0_s_p7_0 = { + 4, 81, + _vq_lengthlist__8c0_s_p7_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__8c0_s_p7_0, + NULL, + &_vq_auxt__8c0_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c0_s_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__8c0_s_p7_1[] = { + 1, 3, 3, 6, 6, 8, 8, 9, 9, 9, 9,10,10,10, 7, 7, + 8, 8, 9, 9, 9, 9,10,10, 9, 7, 7, 8, 8, 9, 9, 9, + 9,10,10,10, 8, 8, 9, 9, 9, 9, 9, 9,10,10,10, 8, + 8, 9, 9, 9, 9, 8, 9,10,10,10, 8, 8, 9, 9, 9,10, + 10,10,10,10,10, 9, 9, 9, 9, 9, 9,10,10,11,10,11, + 9, 9, 9, 9,10,10,10,10,11,11,11,10,10, 9, 9,10, + 10,10, 9,11,10,10,10,10,10,10, 9, 9,10,10,11,11, + 10,10,10, 9, 9, 9,10,10,10, +}; + +static float _vq_quantthresh__8c0_s_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__8c0_s_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__8c0_s_p7_1 = { + _vq_quantthresh__8c0_s_p7_1, + _vq_quantmap__8c0_s_p7_1, + 11, + 11 +}; + +static static_codebook _8c0_s_p7_1 = { + 2, 121, + _vq_lengthlist__8c0_s_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__8c0_s_p7_1, + NULL, + &_vq_auxt__8c0_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__8c0_s_p8_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__8c0_s_p8_0[] = { + 1, 4, 4, 7, 6, 7, 7, 7, 7, 8, 8, 9, 9, 7, 6, 6, + 7, 7, 8, 8, 7, 7, 8, 9,10,10, 7, 6, 6, 7, 7, 8, + 7, 7, 7, 9, 9,10,12, 0, 8, 8, 8, 8, 8, 9, 8, 8, + 9, 9,10,10, 0, 8, 8, 8, 8, 8, 9, 8, 9, 9, 9,11, + 10, 0, 0,13, 9, 8, 9, 9, 9, 9,10,10,11,11, 0,13, + 0, 9, 9, 9, 9, 9, 9,11,10,11,11, 0, 0, 0, 8, 9, + 10, 9,10,10,13,11,12,12, 0, 0, 0, 8, 9, 9, 9,10, + 10,13,12,12,13, 0, 0, 0,12, 0,10,10,12,11,10,11, + 12,12, 0, 0, 0,13,13,10,10,10,11,12, 0,13, 0, 0, + 0, 0, 0, 0,13,11, 0,12,12,12,13,12, 0, 0, 0, 0, + 0, 0,13,13,11,13,13,11,12, +}; + +static float _vq_quantthresh__8c0_s_p8_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__8c0_s_p8_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__8c0_s_p8_0 = { + _vq_quantthresh__8c0_s_p8_0, + _vq_quantmap__8c0_s_p8_0, + 13, + 13 +}; + +static static_codebook _8c0_s_p8_0 = { + 2, 169, + _vq_lengthlist__8c0_s_p8_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__8c0_s_p8_0, + NULL, + &_vq_auxt__8c0_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c0_s_p8_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__8c0_s_p8_1[] = { + 1, 3, 4, 5, 5, 7, 6, 6, 6, 5, 7, 7, 7, 6, 6, 7, + 7, 7, 6, 6, 7, 7, 7, 6, 6, +}; + +static float _vq_quantthresh__8c0_s_p8_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__8c0_s_p8_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__8c0_s_p8_1 = { + _vq_quantthresh__8c0_s_p8_1, + _vq_quantmap__8c0_s_p8_1, + 5, + 5 +}; + +static static_codebook _8c0_s_p8_1 = { + 2, 25, + _vq_lengthlist__8c0_s_p8_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__8c0_s_p8_1, + NULL, + &_vq_auxt__8c0_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__8c0_s_p9_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__8c0_s_p9_0[] = { + 1, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, +}; + +static float _vq_quantthresh__8c0_s_p9_0[] = { + -157.5, 157.5, +}; + +static long _vq_quantmap__8c0_s_p9_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__8c0_s_p9_0 = { + _vq_quantthresh__8c0_s_p9_0, + _vq_quantmap__8c0_s_p9_0, + 3, + 3 +}; + +static static_codebook _8c0_s_p9_0 = { + 4, 81, + _vq_lengthlist__8c0_s_p9_0, + 1, -518803456, 1628680192, 2, 0, + _vq_quantlist__8c0_s_p9_0, + NULL, + &_vq_auxt__8c0_s_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c0_s_p9_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__8c0_s_p9_1[] = { + 1, 4, 4, 5, 5,10, 8,11,11,11,11,11,11,11,11, 6, + 6, 6, 7, 6,11,10,11,11,11,11,11,11,11,11, 7, 5, + 6, 6, 6, 8, 7,11,11,11,11,11,11,11,11,11, 7, 8, + 8, 8, 9, 9,11,11,11,11,11,11,11,11,11, 9, 8, 7, + 8, 9,11,11,11,11,11,11,11,11,11,11,11,10,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,10,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11, +}; + +static float _vq_quantthresh__8c0_s_p9_1[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__8c0_s_p9_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__8c0_s_p9_1 = { + _vq_quantthresh__8c0_s_p9_1, + _vq_quantmap__8c0_s_p9_1, + 15, + 15 +}; + +static static_codebook _8c0_s_p9_1 = { + 2, 225, + _vq_lengthlist__8c0_s_p9_1, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__8c0_s_p9_1, + NULL, + &_vq_auxt__8c0_s_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__8c0_s_p9_2[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__8c0_s_p9_2[] = { + 1, 5, 5, 7, 7, 8, 7, 8, 8,10,10, 9, 9,10,10,10, + 11,11,10,12,11,12,12,12, 9, 8, 8, 8, 8, 8, 9,10, + 10,10,10,11,11,11,10,11,11,12,12,11,12, 8, 8, 7, + 7, 8, 9,10,10,10, 9,10,10, 9,10,10,11,11,11,11, + 11,11, 9, 9, 9, 9, 8, 9,10,10,11,10,10,11,11,12, + 10,10,12,12,11,11,10, 9, 9,10, 8, 9,10,10,10, 9, + 10,10,11,11,10,11,10,10,10,12,12,12, 9,10, 9,10, + 9, 9,10,10,11,11,11,11,10,10,10,11,12,11,12,11, + 12,10,11,10,11, 9,10, 9,10, 9,10,10, 9,10,10,11, + 10,11,11,11,11,12,11, 9,10,10,10,10,11,11,11,11, + 11,10,11,11,11,11,10,12,10,12,12,11,12,10,10,11, + 10, 9,11,10,11, 9,10,11,10,10,10,11,11,11,11,12, + 12,10, 9, 9,11,10, 9,12,11,10,12,12,11,11,11,11, + 10,11,11,12,11,10,12, 9,11,10,11,10,10,11,10,11, + 9,10,10,10,11,12,11,11,12,11,10,10,11,11, 9,10, + 10,12,10,11,10,10,10, 9,10,10,10,10, 9,10,10,11, + 11,11,11,12,11,10,10,10,10,11,11,10,11,11, 9,11, + 10,12,10,12,11,10,11,10,10,10,11,10,10,11,11,10, + 11,10,10,10,10,11,11,12,10,10,10,11,10,11,12,11, + 10,11,10,10,11,11,10,12,10, 9,10,10,11,11,11,10, + 12,10,10,11,11,11,10,10,11,10,10,10,11,10,11,10, + 12,11,11,10,10,10,12,10,10,11, 9,10,11,11,11,10, + 10,11,10,10, 9,11,11,12,12,11,12,11,11,11,11,11, + 11, 9,10,11,10,12,10,10,10,10,11,10,10,11,10,10, + 12,10,10,10,10,10, 9,12,10,10,10,10,12, 9,11,10, + 10,11,10,12,12,10,12,12,12,10,10,10,10, 9,10,11, + 10,10,12,10,10,12,11,10,11,10,10,12,11,10,12,10, + 10,11, 9,11,10, 9,10, 9,10, +}; + +static float _vq_quantthresh__8c0_s_p9_2[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__8c0_s_p9_2[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__8c0_s_p9_2 = { + _vq_quantthresh__8c0_s_p9_2, + _vq_quantmap__8c0_s_p9_2, + 21, + 21 +}; + +static static_codebook _8c0_s_p9_2 = { + 2, 441, + _vq_lengthlist__8c0_s_p9_2, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__8c0_s_p9_2, + NULL, + &_vq_auxt__8c0_s_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__8c0_s_single[] = { + 4, 5,18, 7,10, 6, 7, 8, 9,10, 5, 2,18, 5, 7, 5, + 6, 7, 8,11,17,17,17,17,17,17,17,17,17,17, 7, 4, + 17, 6, 9, 6, 8,10,12,15,11, 7,17, 9, 6, 6, 7, 9, + 11,15, 6, 4,17, 6, 6, 4, 5, 8,11,16, 6, 6,17, 8, + 6, 5, 6, 9,13,16, 8, 9,17,11, 9, 8, 8,11,13,17, + 9,12,17,15,14,13,12,13,14,17,12,15,17,17,17,17, + 17,16,17,17, +}; + +static static_codebook _huff_book__8c0_s_single = { + 2, 100, + _huff_lengthlist__8c0_s_single, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__8c1_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__8c1_s_p1_0[] = { + 1, 5, 5, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 8, 7, 0, 0, 0, 0, 0, 0, 7, 8, 9, 0, 0, 0, + 0, 0, 0, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 7, 8, 0, 0, 0, 0, 0, 0, 7, 9, 8, 0, 0, + 0, 0, 0, 0, 7, 9, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 8, 8, 0, 0, 0, 0, + 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, + 0, 0, 0, 8, 8,10, 0, 0, 0, 0, 0, 0, 9,10,10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 8,10, 9, 0, 0, 0, 0, 0, 0, 9,10,10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 8, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, + 0, 0, 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 9,10,10, 0, + 0, 0, 0, 0, 0, 8, 9,10, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 9,10,10, + 0, 0, 0, 0, 0, 0, 8,10, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__8c1_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__8c1_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__8c1_s_p1_0 = { + _vq_quantthresh__8c1_s_p1_0, + _vq_quantmap__8c1_s_p1_0, + 3, + 3 +}; + +static static_codebook _8c1_s_p1_0 = { + 8, 6561, + _vq_lengthlist__8c1_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__8c1_s_p1_0, + NULL, + &_vq_auxt__8c1_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c1_s_p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__8c1_s_p3_0[] = { + 2, 4, 4, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 6, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 7, 7, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 6, 6, 7, 7, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__8c1_s_p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__8c1_s_p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__8c1_s_p3_0 = { + _vq_quantthresh__8c1_s_p3_0, + _vq_quantmap__8c1_s_p3_0, + 5, + 5 +}; + +static static_codebook _8c1_s_p3_0 = { + 4, 625, + _vq_lengthlist__8c1_s_p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__8c1_s_p3_0, + NULL, + &_vq_auxt__8c1_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c1_s_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__8c1_s_p4_0[] = { + 1, 2, 3, 7, 7, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, + 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 7, 7, + 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 0, 0, 0, 0, 0, 0, 0, 9, 8, 0, 0, 0, 0, 0, + 0, 0,10,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__8c1_s_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__8c1_s_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__8c1_s_p4_0 = { + _vq_quantthresh__8c1_s_p4_0, + _vq_quantmap__8c1_s_p4_0, + 9, + 9 +}; + +static static_codebook _8c1_s_p4_0 = { + 2, 81, + _vq_lengthlist__8c1_s_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__8c1_s_p4_0, + NULL, + &_vq_auxt__8c1_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c1_s_p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__8c1_s_p5_0[] = { + 1, 3, 3, 4, 5, 6, 6, 8, 8, 0, 0, 0, 8, 8, 7, 7, + 9, 9, 0, 0, 0, 8, 8, 7, 7, 9, 9, 0, 0, 0, 9,10, + 8, 8, 9, 9, 0, 0, 0,10,10, 8, 8, 9, 9, 0, 0, 0, + 11,10, 8, 8,10,10, 0, 0, 0,11,11, 8, 8,10,10, 0, + 0, 0,12,12, 9, 9,10,10, 0, 0, 0, 0, 0, 9, 9,10, + 10, +}; + +static float _vq_quantthresh__8c1_s_p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__8c1_s_p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__8c1_s_p5_0 = { + _vq_quantthresh__8c1_s_p5_0, + _vq_quantmap__8c1_s_p5_0, + 9, + 9 +}; + +static static_codebook _8c1_s_p5_0 = { + 2, 81, + _vq_lengthlist__8c1_s_p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__8c1_s_p5_0, + NULL, + &_vq_auxt__8c1_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c1_s_p6_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__8c1_s_p6_0[] = { + 1, 3, 3, 5, 5, 8, 8, 8, 8, 9, 9,10,10,11,11,11, + 11, 0, 0, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11,11, + 12,12, 0, 0, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11, + 11,12,12, 0, 0, 0, 9, 9, 8, 8,10,10,10,10,11,11, + 12,12,12,12, 0, 0, 0, 9, 9, 8, 8,10,10,10,10,11, + 11,12,12,12,12, 0, 0, 0,10,10, 9, 9,10,10,10,10, + 11,11,12,12,13,13, 0, 0, 0,10,10, 9, 9,10,10,10, + 10,11,11,12,12,13,13, 0, 0, 0,11,11, 9, 9,10,10, + 10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, 9,10, + 10,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, 9, + 10,10,11,11,12,12,12,12,13,13, 0, 0, 0, 0, 0, 9, + 9,10,10,11,11,12,11,12,12,13,13, 0, 0, 0, 0, 0, + 10,10,11,11,11,11,12,12,13,12,13,13, 0, 0, 0, 0, + 0, 0, 0,11,10,11,11,12,12,13,13,13,13, 0, 0, 0, + 0, 0, 0, 0,11,11,12,12,12,12,13,13,13,14, 0, 0, + 0, 0, 0, 0, 0,11,11,12,12,12,12,13,13,14,13, 0, + 0, 0, 0, 0, 0, 0,12,12,12,12,13,13,13,13,14,14, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,13,13,13,13,14, + 14, +}; + +static float _vq_quantthresh__8c1_s_p6_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__8c1_s_p6_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__8c1_s_p6_0 = { + _vq_quantthresh__8c1_s_p6_0, + _vq_quantmap__8c1_s_p6_0, + 17, + 17 +}; + +static static_codebook _8c1_s_p6_0 = { + 2, 289, + _vq_lengthlist__8c1_s_p6_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__8c1_s_p6_0, + NULL, + &_vq_auxt__8c1_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c1_s_p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__8c1_s_p7_0[] = { + 1, 4, 4, 6, 6, 6, 7, 6, 6, 4, 7, 7,10, 9, 9,10, + 9, 9, 5, 7, 7,10, 9, 9,10, 9, 9, 6,10,10,10,10, + 10,11,10,10, 6, 9, 9,10, 9,10,11,10,10, 6, 9, 9, + 10, 9, 9,11, 9,10, 7,10,10,11,11,11,11,10,10, 6, + 9, 9,10,10,10,11, 9, 9, 6, 9, 9,10,10,10,10, 9, + 9, +}; + +static float _vq_quantthresh__8c1_s_p7_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__8c1_s_p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__8c1_s_p7_0 = { + _vq_quantthresh__8c1_s_p7_0, + _vq_quantmap__8c1_s_p7_0, + 3, + 3 +}; + +static static_codebook _8c1_s_p7_0 = { + 4, 81, + _vq_lengthlist__8c1_s_p7_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__8c1_s_p7_0, + NULL, + &_vq_auxt__8c1_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c1_s_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__8c1_s_p7_1[] = { + 2, 3, 3, 5, 5, 7, 7, 7, 7, 7, 7,10,10, 9, 7, 7, + 7, 7, 8, 8, 8, 8, 9, 9, 9, 7, 7, 7, 7, 8, 8, 8, + 8,10,10,10, 7, 7, 7, 7, 8, 8, 8, 8,10,10,10, 7, + 7, 7, 7, 8, 8, 8, 8,10,10,10, 8, 8, 8, 8, 8, 8, + 8, 8,10,10,10, 8, 8, 8, 8, 8, 8, 8, 8,10,10,10, + 8, 8, 8, 8, 8, 8, 8, 8,10,10,10,10,10, 8, 8, 8, + 8, 8, 8,10,10,10,10,10, 8, 8, 8, 8, 8, 8,10,10, + 10,10,10, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__8c1_s_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__8c1_s_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__8c1_s_p7_1 = { + _vq_quantthresh__8c1_s_p7_1, + _vq_quantmap__8c1_s_p7_1, + 11, + 11 +}; + +static static_codebook _8c1_s_p7_1 = { + 2, 121, + _vq_lengthlist__8c1_s_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__8c1_s_p7_1, + NULL, + &_vq_auxt__8c1_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__8c1_s_p8_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__8c1_s_p8_0[] = { + 1, 4, 4, 6, 6, 8, 8, 8, 8, 9, 9,10,10, 7, 5, 5, + 7, 7, 8, 8, 8, 8, 9,10,11,11, 7, 5, 5, 7, 7, 8, + 8, 9, 9,10,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9, + 9,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11, + 11, 0,12,12, 9, 9, 9, 9,10, 9,10,11,11,11, 0,13, + 12, 9, 8, 9, 9,10,10,11,11,12,11, 0, 0, 0, 9, 9, + 9, 9,10,10,11,11,12,12, 0, 0, 0,10,10, 9, 9,10, + 10,11,11,12,12, 0, 0, 0,13,13,10,10,11,11,12,11, + 13,12, 0, 0, 0,14,14,10,10,11,10,11,11,12,12, 0, + 0, 0, 0, 0,12,12,11,11,12,12,13,13, 0, 0, 0, 0, + 0,12,12,11,10,12,11,13,12, +}; + +static float _vq_quantthresh__8c1_s_p8_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__8c1_s_p8_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__8c1_s_p8_0 = { + _vq_quantthresh__8c1_s_p8_0, + _vq_quantmap__8c1_s_p8_0, + 13, + 13 +}; + +static static_codebook _8c1_s_p8_0 = { + 2, 169, + _vq_lengthlist__8c1_s_p8_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__8c1_s_p8_0, + NULL, + &_vq_auxt__8c1_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c1_s_p8_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__8c1_s_p8_1[] = { + 2, 3, 3, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, 6, + 6, 6, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__8c1_s_p8_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__8c1_s_p8_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__8c1_s_p8_1 = { + _vq_quantthresh__8c1_s_p8_1, + _vq_quantmap__8c1_s_p8_1, + 5, + 5 +}; + +static static_codebook _8c1_s_p8_1 = { + 2, 25, + _vq_lengthlist__8c1_s_p8_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__8c1_s_p8_1, + NULL, + &_vq_auxt__8c1_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__8c1_s_p9_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__8c1_s_p9_0[] = { + 1, 3, 3,10,10,10,10,10,10,10,10,10,10, 5, 6, 6, + 10,10,10,10,10,10,10,10,10,10, 6, 7, 8,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10, 9, 9, 9, 9, +}; + +static float _vq_quantthresh__8c1_s_p9_0[] = { + -1732.5, -1417.5, -1102.5, -787.5, -472.5, -157.5, 157.5, 472.5, + 787.5, 1102.5, 1417.5, 1732.5, +}; + +static long _vq_quantmap__8c1_s_p9_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__8c1_s_p9_0 = { + _vq_quantthresh__8c1_s_p9_0, + _vq_quantmap__8c1_s_p9_0, + 13, + 13 +}; + +static static_codebook _8c1_s_p9_0 = { + 2, 169, + _vq_lengthlist__8c1_s_p9_0, + 1, -513964032, 1628680192, 4, 0, + _vq_quantlist__8c1_s_p9_0, + NULL, + &_vq_auxt__8c1_s_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__8c1_s_p9_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__8c1_s_p9_1[] = { + 1, 4, 4, 5, 5, 7, 7, 9, 9,11,11,12,12,13,13, 6, + 5, 5, 6, 6, 9, 9,10,10,12,12,12,13,15,14, 6, 5, + 5, 7, 7, 9, 9,10,10,12,12,12,13,14,13,17, 7, 7, + 8, 8,10,10,11,11,12,13,13,13,13,13,17, 7, 7, 8, + 8,10,10,11,11,13,13,13,13,14,14,17,11,11, 9, 9, + 11,11,12,12,12,13,13,14,15,13,17,12,12, 9, 9,11, + 11,12,12,13,13,13,13,14,16,17,17,17,11,12,12,12, + 13,13,13,14,15,14,15,15,17,17,17,12,12,11,11,13, + 13,14,14,15,14,15,15,17,17,17,15,15,13,13,14,14, + 15,14,15,15,16,15,17,17,17,15,15,13,13,13,14,14, + 15,15,15,15,16,17,17,17,17,16,14,15,14,14,15,14, + 14,15,15,15,17,17,17,17,17,14,14,16,14,15,15,15, + 15,15,15,17,17,17,17,17,17,16,16,15,17,15,15,14, + 17,15,17,16,17,17,17,17,16,15,14,15,15,15,15,15, + 15, +}; + +static float _vq_quantthresh__8c1_s_p9_1[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__8c1_s_p9_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__8c1_s_p9_1 = { + _vq_quantthresh__8c1_s_p9_1, + _vq_quantmap__8c1_s_p9_1, + 15, + 15 +}; + +static static_codebook _8c1_s_p9_1 = { + 2, 225, + _vq_lengthlist__8c1_s_p9_1, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__8c1_s_p9_1, + NULL, + &_vq_auxt__8c1_s_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__8c1_s_p9_2[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__8c1_s_p9_2[] = { + 2, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8, 9, 8, 9, 9, 9, + 9, 9, 9, 9, 9,11,11,12, 7, 7, 7, 7, 8, 8, 9, 9, + 9, 9,10,10,10,10,10,10,10,10,11,11,11, 7, 7, 7, + 7, 8, 8, 9, 8, 9, 9, 9, 9, 9, 9,10,10,10,10,11, + 11,12, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9,10,10,10,10, + 10,10,10,10,11,11,11, 7, 7, 8, 8, 8, 8, 9, 9, 9, + 9,10,10,10,10,10,10,10,10,11,11,11, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9,10,10,10,10,10,10,10,10,11,11, + 11, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9,10,10,10,10,10, + 10,10,10,11,12,11, 9, 9, 8, 9, 9, 9, 9, 9,10,10, + 10,10,10,10,10,10,10,10,11,11,11,11,11, 8, 8, 9, + 9, 9, 9,10,10,10,10,10,10,10,10,10,10,11,12,11, + 12,11, 9, 9, 9, 9, 9,10,10,10,10,10,10,10,10,10, + 10,10,11,11,11,11,11, 9, 9, 9, 9,10,10,10,10,10, + 10,10,10,10,10,10,10,12,11,12,11,11, 9, 9, 9,10, + 10,10,10,10,10,10,10,10,10,10,10,10,12,11,11,11, + 11,11,11,10,10,10,10,10,10,10,10,10,10,10,10,10, + 11,11,11,12,11,11,12,11,10,10,10,10,10,10,10,10, + 10,10,10,10,11,10,11,11,11,11,11,11,11,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,11,11,12,11,12, + 11,11,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 11,11,12,11,12,11,11,11,11,10,10,10,10,10,10,10, + 10,10,10,10,10,11,11,12,11,11,12,11,11,12,10,10, + 11,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11, + 11,11,11,10,10,10,10,10,10,10,10,10,10,10,10,12, + 12,11,12,11,11,12,12,12,11,11,10,10,10,10,10,10, + 10,10,10,11,12,12,11,12,12,11,12,11,11,11,11,10, + 10,10,10,10,10,10,10,10,10, +}; + +static float _vq_quantthresh__8c1_s_p9_2[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__8c1_s_p9_2[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__8c1_s_p9_2 = { + _vq_quantthresh__8c1_s_p9_2, + _vq_quantmap__8c1_s_p9_2, + 21, + 21 +}; + +static static_codebook _8c1_s_p9_2 = { + 2, 441, + _vq_lengthlist__8c1_s_p9_2, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__8c1_s_p9_2, + NULL, + &_vq_auxt__8c1_s_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__8c1_s_single[] = { + 4, 6,18, 8,11, 8, 8, 9, 9,10, 4, 4,18, 5, 9, 5, + 6, 7, 8,10,18,18,18,18,17,17,17,17,17,17, 7, 5, + 17, 6,11, 6, 7, 8, 9,12,12, 9,17,12, 8, 8, 9,10, + 10,13, 7, 5,17, 6, 8, 4, 5, 6, 8,10, 6, 5,17, 6, + 8, 5, 4, 5, 7, 9, 7, 7,17, 8, 9, 6, 5, 5, 6, 8, + 8, 8,17, 9,11, 8, 6, 6, 6, 7, 9,10,17,12,12,10, + 9, 7, 7, 8, +}; + +static static_codebook _huff_book__8c1_s_single = { + 2, 100, + _huff_lengthlist__8c1_s_single, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44c2_s_long[] = { + 6, 6,12,10,10,10, 9,10,12,12, 6, 1,10, 5, 6, 6, + 7, 9,11,14,12, 9, 8,11, 7, 8, 9,11,13,15,10, 5, + 12, 7, 8, 7, 9,12,14,15,10, 6, 7, 8, 5, 6, 7, 9, + 12,14, 9, 6, 8, 7, 6, 6, 7, 9,12,12, 9, 7, 9, 9, + 7, 6, 6, 7,10,10,10, 9,10,11, 8, 7, 6, 6, 8,10, + 12,11,13,13,11,10, 8, 8, 8,10,11,13,15,15,14,13, + 10, 8, 8, 9, +}; + +static static_codebook _huff_book__44c2_s_long = { + 2, 100, + _huff_lengthlist__44c2_s_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44c2_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c2_s_p1_0[] = { + 2, 4, 4, 0, 0, 0, 0, 0, 0, 5, 6, 6, 0, 0, 0, 0, + 0, 0, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, + 0, 0, 0, 6, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 6, 8, 7, 0, 0, + 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, + 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, + 0, 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 8, 8, 0, 0, + 0, 0, 0, 0, 8, 9, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 7, 7, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, + 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 6, 8, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, + 0, 0, 0, 0, 0, 8, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, + 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c2_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44c2_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c2_s_p1_0 = { + _vq_quantthresh__44c2_s_p1_0, + _vq_quantmap__44c2_s_p1_0, + 3, + 3 +}; + +static static_codebook _44c2_s_p1_0 = { + 8, 6561, + _vq_lengthlist__44c2_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44c2_s_p1_0, + NULL, + &_vq_auxt__44c2_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c2_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c2_s_p2_0[] = { + 1, 4, 4, 0, 0, 0, 7, 7, 0, 0, 0, 7, 7, 0, 0, 0, + 8, 8, 0, 0, 0, 0, 0, 0, 0, 4, 6, 6, 0, 0, 0, 8, + 8, 0, 0, 0, 8, 8, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, + 0, 0, 4, 6, 6, 0, 0, 0, 8, 8, 0, 0, 0, 8, 8, 0, + 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, 0,11,11, 0, 0, + 0,11,11, 0, 0, 0,12,11, 0, 0, 0, 0, 0, 0, 0, 7, + 8, 8, 0, 0, 0,10,11, 0, 0, 0,11,11, 0, 0, 0,11, + 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 6, 8, 8, 0, 0, 0,11,11, 0, 0, 0,11,11, + 0, 0, 0,12,12, 0, 0, 0, 0, 0, 0, 0, 6, 8, 8, 0, + 0, 0,10,11, 0, 0, 0,10,11, 0, 0, 0,11,11, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 9, 9, 0, 0, 0,11,12, 0, 0, 0,11,12, 0, 0, 0, + 12,11, 0, 0, 0, 0, 0, 0, 0, 8,10, 9, 0, 0, 0,12, + 11, 0, 0, 0,12,11, 0, 0, 0,11,12, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c2_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c2_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c2_s_p2_0 = { + _vq_quantthresh__44c2_s_p2_0, + _vq_quantmap__44c2_s_p2_0, + 5, + 5 +}; + +static static_codebook _44c2_s_p2_0 = { + 4, 625, + _vq_lengthlist__44c2_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c2_s_p2_0, + NULL, + &_vq_auxt__44c2_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c2_s_p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c2_s_p3_0[] = { + 2, 4, 3, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 6, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 6, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c2_s_p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c2_s_p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c2_s_p3_0 = { + _vq_quantthresh__44c2_s_p3_0, + _vq_quantmap__44c2_s_p3_0, + 5, + 5 +}; + +static static_codebook _44c2_s_p3_0 = { + 4, 625, + _vq_lengthlist__44c2_s_p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c2_s_p3_0, + NULL, + &_vq_auxt__44c2_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c2_s_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c2_s_p4_0[] = { + 1, 3, 3, 6, 6, 0, 0, 0, 0, 0, 6, 6, 6, 6, 0, 0, + 0, 0, 0, 6, 6, 6, 6, 0, 0, 0, 0, 0, 7, 7, 6, 6, + 0, 0, 0, 0, 0, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 0, + 7, 8, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c2_s_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c2_s_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c2_s_p4_0 = { + _vq_quantthresh__44c2_s_p4_0, + _vq_quantmap__44c2_s_p4_0, + 9, + 9 +}; + +static static_codebook _44c2_s_p4_0 = { + 2, 81, + _vq_lengthlist__44c2_s_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c2_s_p4_0, + NULL, + &_vq_auxt__44c2_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c2_s_p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c2_s_p5_0[] = { + 1, 3, 3, 6, 6, 7, 7, 9, 9, 0, 7, 7, 7, 7, 7, 7, + 9, 9, 0, 7, 7, 7, 7, 7, 7, 9, 9, 0, 8, 8, 7, 7, + 8, 8,10,10, 0, 0, 0, 7, 7, 8, 8,10,10, 0, 0, 0, + 9, 9, 8, 8,10,10, 0, 0, 0, 9, 9, 8, 8,10,10, 0, + 0, 0,10,10, 9, 9,11,11, 0, 0, 0, 0, 0, 9, 9,11, + 11, +}; + +static float _vq_quantthresh__44c2_s_p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c2_s_p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c2_s_p5_0 = { + _vq_quantthresh__44c2_s_p5_0, + _vq_quantmap__44c2_s_p5_0, + 9, + 9 +}; + +static static_codebook _44c2_s_p5_0 = { + 2, 81, + _vq_lengthlist__44c2_s_p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c2_s_p5_0, + NULL, + &_vq_auxt__44c2_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c2_s_p6_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c2_s_p6_0[] = { + 1, 4, 3, 6, 6, 8, 8, 9, 9, 9, 9, 9, 9,10,10,11, + 11, 0, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11,11, + 12,11, 0, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11, + 11,11,12, 0, 8, 8, 7, 7, 9, 9,10,10, 9, 9,10,10, + 11,11,12,12, 0, 0, 0, 7, 7, 9, 9,10,10,10, 9,10, + 10,11,11,12,12, 0, 0, 0, 8, 8, 9, 9,10,10,10,10, + 11,11,11,11,12,12, 0, 0, 0, 8, 8, 9, 9,10,10,10, + 10,11,11,12,12,12,12, 0, 0, 0, 9, 9, 9, 9,10,10, + 10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 9, 9,10, + 10,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, 9, + 10,10,11,11,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, + 9,10,10,11,11,11,11,12,12,13,13, 0, 0, 0, 0, 0, + 10,10,10,10,11,11,12,12,13,12,13,13, 0, 0, 0, 0, + 0, 0, 0,10,10,11,11,12,12,13,13,13,13, 0, 0, 0, + 0, 0, 0, 0,11,11,12,12,12,12,13,13,13,14, 0, 0, + 0, 0, 0, 0, 0,11,11,12,12,12,12,13,13,13,14, 0, + 0, 0, 0, 0, 0, 0,12,12,12,12,13,13,13,13,14,14, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,13,13,13,13,14, + 14, +}; + +static float _vq_quantthresh__44c2_s_p6_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c2_s_p6_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c2_s_p6_0 = { + _vq_quantthresh__44c2_s_p6_0, + _vq_quantmap__44c2_s_p6_0, + 17, + 17 +}; + +static static_codebook _44c2_s_p6_0 = { + 2, 289, + _vq_lengthlist__44c2_s_p6_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c2_s_p6_0, + NULL, + &_vq_auxt__44c2_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c2_s_p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c2_s_p7_0[] = { + 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 7,10, 9, 9,11, + 9, 9, 4, 7, 7,10, 9, 9,10, 9, 9, 7,10,10,11,10, + 11,11,10,11, 6, 9, 9,11,10,10,11,10,10, 6, 9, 9, + 11,10,11,11,10,10, 7,11,10,11,11,11,12,11,11, 6, + 9, 9,11,10,10,11,11,10, 6, 9, 9,11,10,10,12,10, + 11, +}; + +static float _vq_quantthresh__44c2_s_p7_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44c2_s_p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c2_s_p7_0 = { + _vq_quantthresh__44c2_s_p7_0, + _vq_quantmap__44c2_s_p7_0, + 3, + 3 +}; + +static static_codebook _44c2_s_p7_0 = { + 4, 81, + _vq_lengthlist__44c2_s_p7_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44c2_s_p7_0, + NULL, + &_vq_auxt__44c2_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c2_s_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c2_s_p7_1[] = { + 2, 3, 4, 6, 6, 7, 7, 7, 7, 7, 7, 9, 7, 7, 6, 6, + 7, 7, 8, 8, 8, 8, 9, 6, 6, 6, 6, 7, 7, 8, 8, 8, + 8,10, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8,10,10,10, 7, + 7, 7, 7, 8, 8, 8, 8,10,10,10, 7, 7, 8, 8, 8, 8, + 8, 8,10,10,10, 7, 8, 8, 8, 8, 8, 8, 8,10,10,10, + 8, 8, 8, 8, 8, 8, 8, 8,10,10,10,10,10, 8, 8, 8, + 8, 8, 8,10,10,10,10,10, 9, 9, 8, 8, 8, 8,10,10, + 10,10,10, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44c2_s_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c2_s_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c2_s_p7_1 = { + _vq_quantthresh__44c2_s_p7_1, + _vq_quantmap__44c2_s_p7_1, + 11, + 11 +}; + +static static_codebook _44c2_s_p7_1 = { + 2, 121, + _vq_lengthlist__44c2_s_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c2_s_p7_1, + NULL, + &_vq_auxt__44c2_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c2_s_p8_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c2_s_p8_0[] = { + 1, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 6, 5, 5, + 7, 7, 8, 8, 8, 8, 9, 9,10,10, 7, 6, 5, 7, 7, 8, + 8, 8, 8, 9, 9,10,10, 0, 8, 8, 8, 8, 9, 9, 9, 9, + 10,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11, + 11, 0,12,12, 9, 9,10,10,10,10,11,11,11,11, 0,13, + 13, 9, 9,10,10,10,10,11,11,12,12, 0, 0, 0,10,10, + 10,10,11,11,12,12,12,13, 0, 0, 0,10,10,10,10,11, + 11,12,12,12,12, 0, 0, 0,14,14,10,11,11,11,12,12, + 13,13, 0, 0, 0,14,14,11,10,11,11,13,12,13,13, 0, + 0, 0, 0, 0,12,12,11,12,13,12,14,14, 0, 0, 0, 0, + 0,12,12,12,12,13,12,14,14, +}; + +static float _vq_quantthresh__44c2_s_p8_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44c2_s_p8_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c2_s_p8_0 = { + _vq_quantthresh__44c2_s_p8_0, + _vq_quantmap__44c2_s_p8_0, + 13, + 13 +}; + +static static_codebook _44c2_s_p8_0 = { + 2, 169, + _vq_lengthlist__44c2_s_p8_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44c2_s_p8_0, + NULL, + &_vq_auxt__44c2_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c2_s_p8_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c2_s_p8_1[] = { + 2, 4, 4, 5, 4, 6, 5, 5, 5, 5, 6, 5, 5, 5, 5, 6, + 5, 5, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__44c2_s_p8_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c2_s_p8_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c2_s_p8_1 = { + _vq_quantthresh__44c2_s_p8_1, + _vq_quantmap__44c2_s_p8_1, + 5, + 5 +}; + +static static_codebook _44c2_s_p8_1 = { + 2, 25, + _vq_lengthlist__44c2_s_p8_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c2_s_p8_1, + NULL, + &_vq_auxt__44c2_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c2_s_p9_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c2_s_p9_0[] = { + 1, 5, 4,12,12,12,12,12,12,12,12,12,12, 4, 9, 8, + 11,11,11,11,11,11,11,11,11,11, 2, 8, 7,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,10,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11, +}; + +static float _vq_quantthresh__44c2_s_p9_0[] = { + -1215.5, -994.5, -773.5, -552.5, -331.5, -110.5, 110.5, 331.5, + 552.5, 773.5, 994.5, 1215.5, +}; + +static long _vq_quantmap__44c2_s_p9_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c2_s_p9_0 = { + _vq_quantthresh__44c2_s_p9_0, + _vq_quantmap__44c2_s_p9_0, + 13, + 13 +}; + +static static_codebook _44c2_s_p9_0 = { + 2, 169, + _vq_lengthlist__44c2_s_p9_0, + 1, -514541568, 1627103232, 4, 0, + _vq_quantlist__44c2_s_p9_0, + NULL, + &_vq_auxt__44c2_s_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c2_s_p9_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c2_s_p9_1[] = { + 1, 4, 4, 6, 6, 7, 6, 8, 8,10, 9,10,10, 6, 5, 5, + 7, 7, 8, 7,10, 9,11,11,12,13, 6, 5, 5, 7, 7, 8, + 8,10,10,11,11,13,13,18, 8, 8, 8, 8, 9, 9,10,10, + 12,12,12,13,18, 8, 8, 8, 8, 9, 9,10,10,12,12,13, + 13,18,11,11, 8, 8,10,10,11,11,12,11,13,12,18,11, + 11, 9, 7,10,10,11,11,11,12,12,13,17,17,17,10,10, + 11,11,12,12,12,10,12,12,17,17,17,11,10,11,10,13, + 12,11,12,12,12,17,17,17,15,14,11,11,12,11,13,10, + 13,12,17,17,17,14,14,12,10,11,11,13,13,13,13,17, + 17,16,17,16,13,13,12,10,13,10,14,13,17,16,17,16, + 17,13,12,12,10,13,11,14,14, +}; + +static float _vq_quantthresh__44c2_s_p9_1[] = { + -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, 25.5, + 42.5, 59.5, 76.5, 93.5, +}; + +static long _vq_quantmap__44c2_s_p9_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c2_s_p9_1 = { + _vq_quantthresh__44c2_s_p9_1, + _vq_quantmap__44c2_s_p9_1, + 13, + 13 +}; + +static static_codebook _44c2_s_p9_1 = { + 2, 169, + _vq_lengthlist__44c2_s_p9_1, + 1, -522616832, 1620115456, 4, 0, + _vq_quantlist__44c2_s_p9_1, + NULL, + &_vq_auxt__44c2_s_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c2_s_p9_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c2_s_p9_2[] = { + 2, 4, 4, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, + 8,10, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, + 9, 9,10, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9,10, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, + 9, 9, 9, 9,10,10,10, 8, 7, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 9, 9,10,11,11, 8, 8, 8, 8, 9, 9, 9, 9, + 9, 9,10, 9, 9, 9,10,11,10, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 9,10,10,10,10,11,10, 8, 8, 9, 9, 9, 9, + 9, 9,10, 9, 9,10, 9,10,11,10,11,11,11, 8, 8, 9, + 9, 9, 9, 9, 9, 9, 9,10,10,11,11,11,11,11, 9, 9, + 9, 9, 9, 9,10, 9, 9, 9,10,10,11,11,11,11,11, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10, 9,10,11,11,11,11,11, + 9, 9, 9, 9,10,10, 9, 9, 9,10,10,10,11,11,11,11, + 11,11,11, 9, 9, 9,10, 9, 9,10,10,10,10,11,11,10, + 11,11,11,11,10, 9,10,10, 9, 9, 9, 9,10,10,11,10, + 11,11,11,11,11, 9, 9, 9, 9,10, 9,10,10,10,10,11, + 10,11,11,11,11,11,10,10, 9, 9,10, 9,10,10,10,10, + 10,10,10,11,11,11,11,11,11, 9, 9,10, 9,10, 9,10, + 10, +}; + +static float _vq_quantthresh__44c2_s_p9_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c2_s_p9_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c2_s_p9_2 = { + _vq_quantthresh__44c2_s_p9_2, + _vq_quantmap__44c2_s_p9_2, + 17, + 17 +}; + +static static_codebook _44c2_s_p9_2 = { + 2, 289, + _vq_lengthlist__44c2_s_p9_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c2_s_p9_2, + NULL, + &_vq_auxt__44c2_s_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44c2_s_short[] = { + 11, 9,13,12,12,11,12,12,13,15, 8, 2,11, 4, 8, 5, + 7,10,12,15,13, 7,10, 9, 8, 8,10,13,17,17,11, 4, + 12, 5, 9, 5, 8,11,14,16,12, 6, 8, 7, 6, 6, 8,11, + 13,16,11, 4, 9, 5, 6, 4, 6,10,13,16,11, 6,11, 7, + 7, 6, 7,10,13,15,13, 9,12, 9, 8, 6, 8,10,12,14, + 14,10,10, 8, 6, 5, 6, 9,11,13,15,11,11, 9, 6, 5, + 6, 8, 9,12, +}; + +static static_codebook _huff_book__44c2_s_short = { + 2, 100, + _huff_lengthlist__44c2_s_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44c3_s_long[] = { + 5, 6,11,11,11,11,10,10,12,11, 5, 2,11, 5, 6, 6, + 7, 9,11,13,13,10, 7,11, 6, 7, 8, 9,10,12,11, 5, + 11, 6, 8, 7, 9,11,14,15,11, 6, 6, 8, 4, 5, 7, 8, + 10,13,10, 5, 7, 7, 5, 5, 6, 8,10,11,10, 7, 7, 8, + 6, 5, 5, 7, 9, 9,11, 8, 8,11, 8, 7, 6, 6, 7, 9, + 12,11,10,13, 9, 9, 7, 7, 7, 9,11,13,12,15,12,11, + 9, 8, 8, 8, +}; + +static static_codebook _huff_book__44c3_s_long = { + 2, 100, + _huff_lengthlist__44c3_s_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44c3_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c3_s_p1_0[] = { + 2, 4, 4, 0, 0, 0, 0, 0, 0, 5, 6, 6, 0, 0, 0, 0, + 0, 0, 5, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, + 0, 0, 0, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 6, 8, 7, 0, 0, + 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, + 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, + 0, 0, 0, 8, 8, 9, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 8, 8, 0, 0, + 0, 0, 0, 0, 7, 9, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, + 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 6, 8, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, + 0, 0, 0, 0, 0, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 8, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, + 0, 0, 0, 0, 0, 0, 8, 9, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c3_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44c3_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c3_s_p1_0 = { + _vq_quantthresh__44c3_s_p1_0, + _vq_quantmap__44c3_s_p1_0, + 3, + 3 +}; + +static static_codebook _44c3_s_p1_0 = { + 8, 6561, + _vq_lengthlist__44c3_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44c3_s_p1_0, + NULL, + &_vq_auxt__44c3_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c3_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c3_s_p2_0[] = { + 2, 5, 5, 0, 0, 0, 5, 5, 0, 0, 0, 5, 5, 0, 0, 0, + 7, 8, 0, 0, 0, 0, 0, 0, 0, 5, 6, 6, 0, 0, 0, 7, + 7, 0, 0, 0, 7, 7, 0, 0, 0,10,10, 0, 0, 0, 0, 0, + 0, 0, 5, 6, 6, 0, 0, 0, 7, 7, 0, 0, 0, 7, 7, 0, + 0, 0,10,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 7, 7, 0, 0, + 0, 7, 7, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 5, + 7, 7, 0, 0, 0, 7, 7, 0, 0, 0, 7, 7, 0, 0, 0, 9, + 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 7, 7, 0, 0, 0, 7, 7, 0, 0, 0, 7, 7, + 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, + 0, 0, 7, 7, 0, 0, 0, 7, 7, 0, 0, 0, 9, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8,10,10, 0, 0, 0, 9, 9, 0, 0, 0, 9, 9, 0, 0, 0, + 10,10, 0, 0, 0, 0, 0, 0, 0, 8,10,10, 0, 0, 0, 9, + 9, 0, 0, 0, 9, 9, 0, 0, 0,10,10, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c3_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c3_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c3_s_p2_0 = { + _vq_quantthresh__44c3_s_p2_0, + _vq_quantmap__44c3_s_p2_0, + 5, + 5 +}; + +static static_codebook _44c3_s_p2_0 = { + 4, 625, + _vq_lengthlist__44c3_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c3_s_p2_0, + NULL, + &_vq_auxt__44c3_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c3_s_p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c3_s_p3_0[] = { + 2, 4, 3, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 6, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 6, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c3_s_p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c3_s_p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c3_s_p3_0 = { + _vq_quantthresh__44c3_s_p3_0, + _vq_quantmap__44c3_s_p3_0, + 5, + 5 +}; + +static static_codebook _44c3_s_p3_0 = { + 4, 625, + _vq_lengthlist__44c3_s_p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c3_s_p3_0, + NULL, + &_vq_auxt__44c3_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c3_s_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c3_s_p4_0[] = { + 2, 3, 3, 6, 6, 0, 0, 0, 0, 0, 4, 4, 6, 6, 0, 0, + 0, 0, 0, 4, 4, 6, 6, 0, 0, 0, 0, 0, 5, 5, 6, 6, + 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, + 7, 8, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c3_s_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c3_s_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c3_s_p4_0 = { + _vq_quantthresh__44c3_s_p4_0, + _vq_quantmap__44c3_s_p4_0, + 9, + 9 +}; + +static static_codebook _44c3_s_p4_0 = { + 2, 81, + _vq_lengthlist__44c3_s_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c3_s_p4_0, + NULL, + &_vq_auxt__44c3_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c3_s_p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c3_s_p5_0[] = { + 1, 3, 4, 6, 6, 7, 7, 9, 9, 0, 5, 5, 7, 7, 7, 8, + 9, 9, 0, 5, 5, 7, 7, 8, 8, 9, 9, 0, 7, 7, 8, 8, + 8, 8,10,10, 0, 0, 0, 8, 8, 8, 8,10,10, 0, 0, 0, + 9, 9, 9, 9,10,10, 0, 0, 0, 9, 9, 9, 9,10,10, 0, + 0, 0,10,10,10,10,11,11, 0, 0, 0, 0, 0,10,10,11, + 11, +}; + +static float _vq_quantthresh__44c3_s_p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c3_s_p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c3_s_p5_0 = { + _vq_quantthresh__44c3_s_p5_0, + _vq_quantmap__44c3_s_p5_0, + 9, + 9 +}; + +static static_codebook _44c3_s_p5_0 = { + 2, 81, + _vq_lengthlist__44c3_s_p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c3_s_p5_0, + NULL, + &_vq_auxt__44c3_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c3_s_p6_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c3_s_p6_0[] = { + 2, 3, 3, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10,11, + 10, 0, 5, 5, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10,10, + 11,11, 0, 5, 5, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10, + 10,11,11, 0, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9,10,10, + 11,11,11,11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10, + 10,11,11,11,12, 0, 0, 0, 8, 8, 8, 8, 9, 9, 9, 9, + 10,10,11,11,12,12, 0, 0, 0, 8, 8, 8, 8, 9, 9, 9, + 9,10,10,11,11,12,12, 0, 0, 0, 9, 9, 9, 9,10,10, + 10,10,11,10,11,11,12,12, 0, 0, 0, 0, 0, 9, 9,10, + 10,10,10,11,11,11,11,12,12, 0, 0, 0, 0, 0, 9, 8, + 9, 9,10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 8, + 8, 9, 9,10,10,11,11,12,11,12,12, 0, 0, 0, 0, 0, + 9,10,10,10,11,11,11,11,12,12,13,13, 0, 0, 0, 0, + 0, 0, 0,10,10,10,10,11,11,12,12,13,13, 0, 0, 0, + 0, 0, 0, 0,11,11,11,11,12,12,12,12,13,13, 0, 0, + 0, 0, 0, 0, 0,11,11,11,11,12,12,12,12,13,13, 0, + 0, 0, 0, 0, 0, 0,11,11,12,12,12,12,13,13,13,13, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,12,12,13,13,13, + 13, +}; + +static float _vq_quantthresh__44c3_s_p6_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c3_s_p6_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c3_s_p6_0 = { + _vq_quantthresh__44c3_s_p6_0, + _vq_quantmap__44c3_s_p6_0, + 17, + 17 +}; + +static static_codebook _44c3_s_p6_0 = { + 2, 289, + _vq_lengthlist__44c3_s_p6_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c3_s_p6_0, + NULL, + &_vq_auxt__44c3_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c3_s_p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c3_s_p7_0[] = { + 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 7,10, 9, 9,11, + 9, 9, 4, 7, 7,10, 9, 9,11, 9, 9, 7,10,10,11,11, + 10,12,11,11, 6, 9, 9,11,10,10,11,10,10, 6, 9, 9, + 11,10,10,11,10,10, 7,11,11,11,11,11,12,11,11, 6, + 9, 9,11,10,10,11,10,10, 6, 9, 9,11,10,10,11,10, + 10, +}; + +static float _vq_quantthresh__44c3_s_p7_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44c3_s_p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c3_s_p7_0 = { + _vq_quantthresh__44c3_s_p7_0, + _vq_quantmap__44c3_s_p7_0, + 3, + 3 +}; + +static static_codebook _44c3_s_p7_0 = { + 4, 81, + _vq_lengthlist__44c3_s_p7_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44c3_s_p7_0, + NULL, + &_vq_auxt__44c3_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c3_s_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c3_s_p7_1[] = { + 2, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8,10, 5, 5, 6, 6, + 7, 7, 8, 8, 8, 8,10, 5, 5, 6, 6, 7, 7, 8, 8, 8, + 8,10, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, 7, + 7, 8, 7, 8, 8, 8, 8,10,10,10, 8, 8, 8, 8, 8, 8, + 8, 8,10,10,10, 7, 8, 8, 8, 8, 8, 8, 8,10,10,10, + 8, 8, 8, 8, 8, 8, 8, 8,10,10,10,10,10, 8, 8, 8, + 8, 8, 8,10,10,10,10,10, 9, 9, 8, 8, 9, 8,10,10, + 10,10,10, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44c3_s_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c3_s_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c3_s_p7_1 = { + _vq_quantthresh__44c3_s_p7_1, + _vq_quantmap__44c3_s_p7_1, + 11, + 11 +}; + +static static_codebook _44c3_s_p7_1 = { + 2, 121, + _vq_lengthlist__44c3_s_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c3_s_p7_1, + NULL, + &_vq_auxt__44c3_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c3_s_p8_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c3_s_p8_0[] = { + 1, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10, 6, 5, 5, + 7, 7, 8, 8, 8, 8, 9, 9,10,10, 7, 5, 5, 7, 7, 8, + 8, 8, 8, 9, 9,11,10, 0, 8, 8, 8, 8, 9, 9, 9, 9, + 10,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11, + 11, 0,12,12, 9, 9,10,10,10,10,11,11,11,12, 0,13, + 13, 9, 9,10,10,10,10,11,11,12,12, 0, 0, 0,10,10, + 10,10,11,11,12,12,12,12, 0, 0, 0,10,10,10,10,11, + 11,12,12,12,12, 0, 0, 0,14,14,11,11,11,11,12,12, + 13,13, 0, 0, 0,14,14,11,11,11,11,12,12,13,13, 0, + 0, 0, 0, 0,12,12,12,12,13,13,14,13, 0, 0, 0, 0, + 0,13,13,12,12,13,12,14,13, +}; + +static float _vq_quantthresh__44c3_s_p8_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44c3_s_p8_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c3_s_p8_0 = { + _vq_quantthresh__44c3_s_p8_0, + _vq_quantmap__44c3_s_p8_0, + 13, + 13 +}; + +static static_codebook _44c3_s_p8_0 = { + 2, 169, + _vq_lengthlist__44c3_s_p8_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44c3_s_p8_0, + NULL, + &_vq_auxt__44c3_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c3_s_p8_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c3_s_p8_1[] = { + 2, 4, 4, 5, 5, 6, 5, 5, 5, 5, 6, 4, 5, 5, 5, 6, + 5, 5, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__44c3_s_p8_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c3_s_p8_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c3_s_p8_1 = { + _vq_quantthresh__44c3_s_p8_1, + _vq_quantmap__44c3_s_p8_1, + 5, + 5 +}; + +static static_codebook _44c3_s_p8_1 = { + 2, 25, + _vq_lengthlist__44c3_s_p8_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c3_s_p8_1, + NULL, + &_vq_auxt__44c3_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c3_s_p9_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c3_s_p9_0[] = { + 1, 4, 4,12,12,12,12,12,12,12,12,12,12, 4, 9, 8, + 12,12,12,12,12,12,12,12,12,12, 2, 9, 7,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,11,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11, +}; + +static float _vq_quantthresh__44c3_s_p9_0[] = { + -1402.5, -1147.5, -892.5, -637.5, -382.5, -127.5, 127.5, 382.5, + 637.5, 892.5, 1147.5, 1402.5, +}; + +static long _vq_quantmap__44c3_s_p9_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c3_s_p9_0 = { + _vq_quantthresh__44c3_s_p9_0, + _vq_quantmap__44c3_s_p9_0, + 13, + 13 +}; + +static static_codebook _44c3_s_p9_0 = { + 2, 169, + _vq_lengthlist__44c3_s_p9_0, + 1, -514332672, 1627381760, 4, 0, + _vq_quantlist__44c3_s_p9_0, + NULL, + &_vq_auxt__44c3_s_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c3_s_p9_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44c3_s_p9_1[] = { + 1, 4, 4, 6, 6, 7, 7, 8, 7, 9, 9,10,10,10,10, 6, + 5, 5, 7, 7, 8, 8,10, 8,11,10,12,12,13,13, 6, 5, + 5, 7, 7, 8, 8,10, 9,11,11,12,12,13,12,18, 8, 8, + 8, 8, 9, 9,10, 9,11,10,12,12,13,13,18, 8, 8, 8, + 8, 9, 9,10,10,11,11,13,12,14,13,18,11,11, 9, 9, + 10,10,11,11,11,12,13,12,13,14,18,11,11, 9, 8,11, + 10,11,11,11,11,12,12,14,13,18,18,18,10,11,10,11, + 12,12,12,12,13,12,14,13,18,18,18,10,11,11, 9,12, + 11,12,12,12,13,13,13,18,18,17,14,14,11,11,12,12, + 13,12,14,12,14,13,18,18,18,14,14,11,10,12, 9,12, + 13,13,13,13,13,18,18,17,16,18,13,13,12,12,13,11, + 14,12,14,14,17,18,18,17,18,13,12,13,10,12,11,14, + 14,14,14,17,18,18,18,18,15,16,12,12,13,10,14,12, + 14,15,18,18,18,16,17,16,14,12,11,13,10,13,13,14, + 15, +}; + +static float _vq_quantthresh__44c3_s_p9_1[] = { + -110.5, -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, + 25.5, 42.5, 59.5, 76.5, 93.5, 110.5, +}; + +static long _vq_quantmap__44c3_s_p9_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44c3_s_p9_1 = { + _vq_quantthresh__44c3_s_p9_1, + _vq_quantmap__44c3_s_p9_1, + 15, + 15 +}; + +static static_codebook _44c3_s_p9_1 = { + 2, 225, + _vq_lengthlist__44c3_s_p9_1, + 1, -522338304, 1620115456, 4, 0, + _vq_quantlist__44c3_s_p9_1, + NULL, + &_vq_auxt__44c3_s_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c3_s_p9_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c3_s_p9_2[] = { + 2, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, + 8,10, 6, 6, 7, 7, 8, 7, 8, 8, 8, 8, 8, 9, 9, 9, + 9, 9,10, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9,10, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, + 9, 9, 9, 9,10,10,10, 7, 7, 8, 8, 8, 9, 9, 9, 9, + 9, 9, 9, 9, 9,11,11,11, 8, 8, 8, 8, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9,10,10,10, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9,10,10,10, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9,10, 9,10,10,10,11,11, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9,11,10,11,11,11, 9, 9, + 9, 9, 9, 9,10,10, 9, 9,10, 9,11,10,11,11,11, 9, + 9, 9, 9, 9, 9, 9, 9,10,10,10, 9,11,11,11,11,11, + 9, 9, 9, 9,10,10, 9, 9, 9, 9,10, 9,11,11,11,11, + 11,11,11, 9, 9, 9, 9, 9, 9,10,10,10,10,11,11,11, + 11,11,11,11,10, 9,10,10, 9,10, 9, 9,10, 9,11,10, + 10,11,11,11,11, 9,10, 9, 9, 9, 9,10,10,10,10,11, + 11,11,11,11,11,10,10,10, 9, 9,10, 9,10, 9,10,10, + 10,10,11,11,11,11,11,11,11, 9, 9, 9, 9, 9,10,10, + 10, +}; + +static float _vq_quantthresh__44c3_s_p9_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c3_s_p9_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c3_s_p9_2 = { + _vq_quantthresh__44c3_s_p9_2, + _vq_quantmap__44c3_s_p9_2, + 17, + 17 +}; + +static static_codebook _44c3_s_p9_2 = { + 2, 289, + _vq_lengthlist__44c3_s_p9_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c3_s_p9_2, + NULL, + &_vq_auxt__44c3_s_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44c3_s_short[] = { + 10, 9,13,11,14,10,12,13,13,14, 7, 2,12, 5,10, 5, + 7,10,12,14,12, 6, 9, 8, 7, 7, 9,11,13,16,10, 4, + 12, 5,10, 6, 8,12,14,16,12, 6, 8, 7, 6, 5, 7,11, + 12,16,10, 4, 8, 5, 6, 4, 6, 9,13,16,10, 6,10, 7, + 7, 6, 7, 9,13,15,12, 9,11, 9, 8, 6, 7,10,12,14, + 14,11,10, 9, 6, 5, 6, 9,11,13,15,13,11,10, 6, 5, + 6, 8, 9,11, +}; + +static static_codebook _huff_book__44c3_s_short = { + 2, 100, + _huff_lengthlist__44c3_s_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44c4_s_long[] = { + 4, 7,11,11,11,11,10,11,12,11, 5, 2,11, 5, 6, 6, + 7, 9,11,12,11, 9, 6,10, 6, 7, 8, 9,10,11,11, 5, + 11, 7, 8, 8, 9,11,13,14,11, 6, 5, 8, 4, 5, 7, 8, + 10,11,10, 6, 7, 7, 5, 5, 6, 8, 9,11,10, 7, 8, 9, + 6, 6, 6, 7, 8, 9,11, 9, 9,11, 7, 7, 6, 6, 7, 9, + 12,12,10,13, 9, 8, 7, 7, 7, 8,11,13,11,14,11,10, + 9, 8, 7, 7, +}; + +static static_codebook _huff_book__44c4_s_long = { + 2, 100, + _huff_lengthlist__44c4_s_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44c4_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c4_s_p1_0[] = { + 2, 4, 4, 0, 0, 0, 0, 0, 0, 5, 6, 6, 0, 0, 0, 0, + 0, 0, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, + 0, 0, 0, 6, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 6, 8, 7, 0, 0, + 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, + 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, + 0, 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 8, 8, 0, 0, + 0, 0, 0, 0, 8, 9, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 7, 7, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, + 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 6, 8, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, + 0, 0, 0, 0, 0, 8, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, + 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c4_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44c4_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c4_s_p1_0 = { + _vq_quantthresh__44c4_s_p1_0, + _vq_quantmap__44c4_s_p1_0, + 3, + 3 +}; + +static static_codebook _44c4_s_p1_0 = { + 8, 6561, + _vq_lengthlist__44c4_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44c4_s_p1_0, + NULL, + &_vq_auxt__44c4_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c4_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c4_s_p2_0[] = { + 2, 5, 5, 0, 0, 0, 5, 5, 0, 0, 0, 5, 5, 0, 0, 0, + 7, 7, 0, 0, 0, 0, 0, 0, 0, 5, 6, 6, 0, 0, 0, 7, + 7, 0, 0, 0, 7, 7, 0, 0, 0,10,10, 0, 0, 0, 0, 0, + 0, 0, 5, 6, 6, 0, 0, 0, 7, 7, 0, 0, 0, 7, 7, 0, + 0, 0,10,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 5, 8, 7, 0, 0, 0, 7, 7, 0, 0, + 0, 7, 7, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 5, + 7, 8, 0, 0, 0, 7, 7, 0, 0, 0, 7, 7, 0, 0, 0, 9, + 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 7, 7, 0, 0, 0, 7, 7, 0, 0, 0, 7, 7, + 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, + 0, 0, 7, 7, 0, 0, 0, 7, 7, 0, 0, 0, 9, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7,10,10, 0, 0, 0, 9, 9, 0, 0, 0, 9, 9, 0, 0, 0, + 10,10, 0, 0, 0, 0, 0, 0, 0, 8,10,10, 0, 0, 0, 9, + 9, 0, 0, 0, 9, 9, 0, 0, 0,10,10, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c4_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c4_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c4_s_p2_0 = { + _vq_quantthresh__44c4_s_p2_0, + _vq_quantmap__44c4_s_p2_0, + 5, + 5 +}; + +static static_codebook _44c4_s_p2_0 = { + 4, 625, + _vq_lengthlist__44c4_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c4_s_p2_0, + NULL, + &_vq_auxt__44c4_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c4_s_p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c4_s_p3_0[] = { + 2, 3, 3, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 4, 6, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 4, 5, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 6, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c4_s_p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c4_s_p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c4_s_p3_0 = { + _vq_quantthresh__44c4_s_p3_0, + _vq_quantmap__44c4_s_p3_0, + 5, + 5 +}; + +static static_codebook _44c4_s_p3_0 = { + 4, 625, + _vq_lengthlist__44c4_s_p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c4_s_p3_0, + NULL, + &_vq_auxt__44c4_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c4_s_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c4_s_p4_0[] = { + 2, 3, 3, 6, 6, 0, 0, 0, 0, 0, 4, 4, 6, 6, 0, 0, + 0, 0, 0, 4, 4, 6, 6, 0, 0, 0, 0, 0, 5, 5, 6, 6, + 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, + 7, 8, 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c4_s_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c4_s_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c4_s_p4_0 = { + _vq_quantthresh__44c4_s_p4_0, + _vq_quantmap__44c4_s_p4_0, + 9, + 9 +}; + +static static_codebook _44c4_s_p4_0 = { + 2, 81, + _vq_lengthlist__44c4_s_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c4_s_p4_0, + NULL, + &_vq_auxt__44c4_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c4_s_p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c4_s_p5_0[] = { + 2, 3, 3, 6, 6, 7, 7, 9, 9, 0, 4, 4, 6, 6, 7, 7, + 9, 9, 0, 4, 5, 6, 6, 7, 7, 9, 9, 0, 6, 6, 7, 7, + 8, 8,10,10, 0, 0, 0, 7, 7, 8, 8,10, 9, 0, 0, 0, + 9, 8, 8, 8,10,10, 0, 0, 0, 8, 8, 8, 8,10,10, 0, + 0, 0,10,10, 9, 9,11,11, 0, 0, 0, 0, 0, 9, 9,10, + 10, +}; + +static float _vq_quantthresh__44c4_s_p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c4_s_p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c4_s_p5_0 = { + _vq_quantthresh__44c4_s_p5_0, + _vq_quantmap__44c4_s_p5_0, + 9, + 9 +}; + +static static_codebook _44c4_s_p5_0 = { + 2, 81, + _vq_lengthlist__44c4_s_p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c4_s_p5_0, + NULL, + &_vq_auxt__44c4_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c4_s_p6_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c4_s_p6_0[] = { + 2, 4, 4, 6, 6, 8, 8, 9, 9, 8, 8, 9, 9,10,10,11, + 11, 0, 4, 4, 6, 6, 8, 8, 9, 9, 9, 9,10,10,11,11, + 11,11, 0, 4, 4, 7, 6, 8, 8, 9, 9, 9, 9,10,10,11, + 11,11,11, 0, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9,10,10, + 11,11,11,12, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10, + 10,11,11,12,12, 0, 0, 0, 8, 8, 8, 8, 9, 9, 9, 9, + 10,10,11,11,12,12, 0, 0, 0, 8, 8, 8, 8, 9, 9, 9, + 9,10,10,11,11,12,12, 0, 0, 0, 9, 9, 9, 9,10,10, + 10,10,11,11,11,11,12,12, 0, 0, 0, 0, 0, 9, 9,10, + 10,10,10,11,11,11,11,12,12, 0, 0, 0, 0, 0, 9, 9, + 9,10,10,10,11,11,11,11,12,12, 0, 0, 0, 0, 0, 9, + 9, 9, 9,10,10,11,11,11,12,12,12, 0, 0, 0, 0, 0, + 10,10,10,10,11,11,11,11,12,12,13,12, 0, 0, 0, 0, + 0, 0, 0,10,10,11,11,11,11,12,12,12,12, 0, 0, 0, + 0, 0, 0, 0,11,11,11,11,12,12,12,12,13,13, 0, 0, + 0, 0, 0, 0, 0,11,11,11,11,12,12,12,12,13,13, 0, + 0, 0, 0, 0, 0, 0,12,12,12,12,12,12,13,13,13,13, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,12,12,12,13,13, + 13, +}; + +static float _vq_quantthresh__44c4_s_p6_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c4_s_p6_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c4_s_p6_0 = { + _vq_quantthresh__44c4_s_p6_0, + _vq_quantmap__44c4_s_p6_0, + 17, + 17 +}; + +static static_codebook _44c4_s_p6_0 = { + 2, 289, + _vq_lengthlist__44c4_s_p6_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c4_s_p6_0, + NULL, + &_vq_auxt__44c4_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c4_s_p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c4_s_p7_0[] = { + 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 7,10, 9, 9,11, + 9, 9, 4, 7, 7,10, 9, 9,11, 9, 9, 7,10,10,11,11, + 10,11,11,11, 6, 9, 9,11,10,10,11,10,10, 6, 9, 9, + 11,10,10,11,10,10, 7,11,11,12,11,11,12,11,11, 6, + 9, 9,11,10,10,11,10,10, 6, 9, 9,11,10,10,11,10, + 10, +}; + +static float _vq_quantthresh__44c4_s_p7_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44c4_s_p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c4_s_p7_0 = { + _vq_quantthresh__44c4_s_p7_0, + _vq_quantmap__44c4_s_p7_0, + 3, + 3 +}; + +static static_codebook _44c4_s_p7_0 = { + 4, 81, + _vq_lengthlist__44c4_s_p7_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44c4_s_p7_0, + NULL, + &_vq_auxt__44c4_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c4_s_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c4_s_p7_1[] = { + 2, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8,10, 5, 5, 6, 6, + 7, 7, 8, 8, 8, 8,10, 5, 5, 6, 6, 7, 7, 8, 8, 8, + 8,10, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, 7, + 7, 8, 8, 8, 8, 8, 8,10,10,10, 8, 7, 8, 8, 8, 8, + 8, 8,10,10,10, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, + 8, 8, 8, 8, 8, 8, 8, 8,10,10,10,10,10, 8, 8, 8, + 8, 8, 8,10,10,10,10,10, 9, 9, 8, 8, 9, 8,10,10, + 10,10,10, 8, 8, 8, 8, 9, 9, +}; + +static float _vq_quantthresh__44c4_s_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c4_s_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c4_s_p7_1 = { + _vq_quantthresh__44c4_s_p7_1, + _vq_quantmap__44c4_s_p7_1, + 11, + 11 +}; + +static static_codebook _44c4_s_p7_1 = { + 2, 121, + _vq_lengthlist__44c4_s_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c4_s_p7_1, + NULL, + &_vq_auxt__44c4_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c4_s_p8_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c4_s_p8_0[] = { + 1, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10, 6, 5, 5, + 7, 7, 8, 8, 8, 8, 9,10,11,11, 7, 5, 5, 7, 7, 8, + 8, 9, 9,10,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9, + 10,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11, + 11, 0,12,12, 9, 9, 9, 9,10,10,10,10,11,11, 0,13, + 13, 9, 9,10, 9,10,10,11,11,11,12, 0, 0, 0,10,10, + 10,10,10,10,11,11,12,12, 0, 0, 0,10,10,10,10,10, + 10,11,11,12,12, 0, 0, 0,14,14,11,11,11,11,12,12, + 12,12, 0, 0, 0,14,14,11,11,11,11,12,12,12,13, 0, + 0, 0, 0, 0,12,12,12,12,12,12,13,13, 0, 0, 0, 0, + 0,13,12,12,12,12,12,13,13, +}; + +static float _vq_quantthresh__44c4_s_p8_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44c4_s_p8_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c4_s_p8_0 = { + _vq_quantthresh__44c4_s_p8_0, + _vq_quantmap__44c4_s_p8_0, + 13, + 13 +}; + +static static_codebook _44c4_s_p8_0 = { + 2, 169, + _vq_lengthlist__44c4_s_p8_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44c4_s_p8_0, + NULL, + &_vq_auxt__44c4_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c4_s_p8_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c4_s_p8_1[] = { + 2, 4, 4, 5, 5, 6, 5, 5, 5, 5, 6, 5, 4, 5, 5, 6, + 5, 5, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__44c4_s_p8_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c4_s_p8_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c4_s_p8_1 = { + _vq_quantthresh__44c4_s_p8_1, + _vq_quantmap__44c4_s_p8_1, + 5, + 5 +}; + +static static_codebook _44c4_s_p8_1 = { + 2, 25, + _vq_lengthlist__44c4_s_p8_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c4_s_p8_1, + NULL, + &_vq_auxt__44c4_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c4_s_p9_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c4_s_p9_0[] = { + 1, 3, 3,12,12,12,12,12,12,12,12,12,12, 4, 7, 7, + 12,12,12,12,12,12,12,12,12,12, 3, 8, 8,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12, +}; + +static float _vq_quantthresh__44c4_s_p9_0[] = { + -1732.5, -1417.5, -1102.5, -787.5, -472.5, -157.5, 157.5, 472.5, + 787.5, 1102.5, 1417.5, 1732.5, +}; + +static long _vq_quantmap__44c4_s_p9_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c4_s_p9_0 = { + _vq_quantthresh__44c4_s_p9_0, + _vq_quantmap__44c4_s_p9_0, + 13, + 13 +}; + +static static_codebook _44c4_s_p9_0 = { + 2, 169, + _vq_lengthlist__44c4_s_p9_0, + 1, -513964032, 1628680192, 4, 0, + _vq_quantlist__44c4_s_p9_0, + NULL, + &_vq_auxt__44c4_s_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c4_s_p9_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44c4_s_p9_1[] = { + 1, 4, 4, 5, 5, 7, 7, 9, 8,10, 9,10,10,10,10, 6, + 5, 5, 7, 7, 9, 8,10, 9,11,10,12,12,13,13, 6, 5, + 5, 7, 7, 9, 9,10,10,11,11,12,12,12,13,19, 8, 8, + 8, 8, 9, 9,10,10,12,11,12,12,13,13,19, 8, 8, 8, + 8, 9, 9,11,11,12,12,13,13,13,13,19,12,12, 9, 9, + 11,11,11,11,12,11,13,12,13,13,18,12,12, 9, 9,11, + 10,11,11,12,12,12,13,13,14,19,18,18,11,11,11,11, + 12,12,13,12,13,13,14,14,16,18,18,11,11,11,10,12, + 11,13,13,13,13,13,14,17,18,18,14,15,11,12,12,13, + 13,13,13,14,14,14,18,18,18,15,15,12,10,13,10,13, + 13,13,13,13,14,18,17,18,17,18,12,13,12,13,13,13, + 14,14,16,14,18,17,18,18,17,13,12,13,10,12,12,14, + 14,14,14,17,18,18,18,18,14,15,12,12,13,12,14,14, + 15,15,18,18,18,17,18,15,14,12,11,12,12,14,14,14, + 15, +}; + +static float _vq_quantthresh__44c4_s_p9_1[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__44c4_s_p9_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44c4_s_p9_1 = { + _vq_quantthresh__44c4_s_p9_1, + _vq_quantmap__44c4_s_p9_1, + 15, + 15 +}; + +static static_codebook _44c4_s_p9_1 = { + 2, 225, + _vq_lengthlist__44c4_s_p9_1, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__44c4_s_p9_1, + NULL, + &_vq_auxt__44c4_s_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c4_s_p9_2[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__44c4_s_p9_2[] = { + 2, 5, 5, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, + 8, 9, 9, 9, 9,11, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 9,10,10,10,10,11, 6, 6, 7, 7, 8, + 8, 8, 8, 9, 9, 9, 9, 9, 9,10, 9,10,10,10,10,11, + 7, 7, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9, 9,10,10,10, + 10,10,10,10,12,11,11, 7, 7, 8, 8, 9, 9, 9, 9, 9, + 9,10,10,10,10,10,10,10,10,12,11,12, 8, 8, 8, 8, + 9, 9, 9, 9, 9,10,10,10,10,10,10,10,10,10,11,11, + 11, 8, 8, 8, 8, 9, 9, 9, 9,10,10,10,10,10,10,10, + 10,10,10,11,11,12, 9, 9, 9, 9, 9, 9,10, 9,10,10, + 10,10,10,10,10,10,10,10,11,11,11,11,11, 9, 9, 9, + 9,10,10,10,10,10,10,10,10,10,10,10,10,11,12,11, + 11,11, 9, 9, 9,10,10,10,10,10,10,10,10,10,10,10, + 10,10,11,11,11,11,11, 9, 9, 9, 9,10,10,10,10,10, + 10,10,10,10,10,10,10,11,11,11,12,12,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,11,12,11,12, + 11,11,11, 9,10,10,10,10,10,10,10,10,10,10,10,10, + 10,11,12,11,11,11,11,11,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,11,11,11,12,11,11,11,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,12,11,11,12,11, + 11,11,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 11,11,11,11,11,11,11,11,11,10,10,10,10,10,10,10, + 10,10,10,10,10,11,11,11,11,12,12,11,11,11,11,11, + 11,11,10,10,10,10,10,10,10,10,12,12,12,11,11,11, + 12,11,11,11,10,10,10,10,10,10,10,10,10,10,10,12, + 11,12,12,12,12,12,11,12,11,11,10,10,10,10,10,10, + 10,10,10,10,12,12,12,12,11,11,11,11,11,11,11,10, + 10,10,10,10,10,10,10,10,10, +}; + +static float _vq_quantthresh__44c4_s_p9_2[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__44c4_s_p9_2[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__44c4_s_p9_2 = { + _vq_quantthresh__44c4_s_p9_2, + _vq_quantmap__44c4_s_p9_2, + 21, + 21 +}; + +static static_codebook _44c4_s_p9_2 = { + 2, 441, + _vq_lengthlist__44c4_s_p9_2, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__44c4_s_p9_2, + NULL, + &_vq_auxt__44c4_s_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44c4_s_short[] = { + 4, 7,14,10,15,10,12,15,16,15, 4, 2,11, 5,10, 6, + 8,11,14,14,14,10, 7,11, 6, 8,10,11,13,15, 9, 4, + 11, 5, 9, 6, 9,12,14,15,14, 9, 6, 9, 4, 5, 7,10, + 12,13, 9, 5, 7, 6, 5, 5, 7,10,13,13,10, 8, 9, 8, + 7, 6, 8,10,14,14,13,11,10,10, 7, 7, 8,11,14,15, + 13,12, 9, 9, 6, 5, 7,10,14,17,15,13,11,10, 6, 6, + 7, 9,12,17, +}; + +static static_codebook _huff_book__44c4_s_short = { + 2, 100, + _huff_lengthlist__44c4_s_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44c5_s_long[] = { + 3, 8, 9,13,10,12,12,12,12,12, 6, 4, 6, 8, 6, 8, + 10,10,11,12, 8, 5, 4,10, 4, 7, 8, 9,10,11,13, 8, + 10, 8, 9, 9,11,12,13,14,10, 6, 4, 9, 3, 5, 6, 8, + 10,11,11, 8, 6, 9, 5, 5, 6, 7, 9,11,12, 9, 7,11, + 6, 6, 6, 7, 8,10,12,11, 9,12, 7, 7, 6, 6, 7, 9, + 13,12,10,13, 9, 8, 7, 7, 7, 8,11,15,11,15,11,10, + 9, 8, 7, 7, +}; + +static static_codebook _huff_book__44c5_s_long = { + 2, 100, + _huff_lengthlist__44c5_s_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44c5_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c5_s_p1_0[] = { + 2, 4, 4, 0, 0, 0, 0, 0, 0, 4, 7, 7, 0, 0, 0, 0, + 0, 0, 4, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, + 0, 0, 0, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 7, 7, 0, 0, 0, 0, 0, 0, 7, 9, 8, 0, 0, + 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 7, 7, 0, 0, 0, 0, + 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, + 0, 0, 0, 9,10,11, 0, 0, 0, 0, 0, 0, 9,10,10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 8,10, 9, 0, 0, 0, 0, 0, 0, 9,10,11, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 7, 7, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 9,11,10, 0, + 0, 0, 0, 0, 0, 8, 9,10, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 9,10,10, + 0, 0, 0, 0, 0, 0, 9,11,10, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c5_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44c5_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c5_s_p1_0 = { + _vq_quantthresh__44c5_s_p1_0, + _vq_quantmap__44c5_s_p1_0, + 3, + 3 +}; + +static static_codebook _44c5_s_p1_0 = { + 8, 6561, + _vq_lengthlist__44c5_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44c5_s_p1_0, + NULL, + &_vq_auxt__44c5_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c5_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c5_s_p2_0[] = { + 2, 4, 4, 0, 0, 0, 5, 5, 0, 0, 0, 5, 5, 0, 0, 0, + 8, 7, 0, 0, 0, 0, 0, 0, 0, 4, 6, 6, 0, 0, 0, 8, + 8, 0, 0, 0, 8, 7, 0, 0, 0,10,10, 0, 0, 0, 0, 0, + 0, 0, 4, 6, 6, 0, 0, 0, 8, 8, 0, 0, 0, 7, 8, 0, + 0, 0,10,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 5, 8, 7, 0, 0, 0, 8, 8, 0, 0, + 0, 8, 8, 0, 0, 0,10,10, 0, 0, 0, 0, 0, 0, 0, 5, + 7, 8, 0, 0, 0, 8, 8, 0, 0, 0, 8, 8, 0, 0, 0,10, + 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 8, 8, 0, 0, 0, 8, 8, 0, 0, 0, 8, 8, + 0, 0, 0,10,10, 0, 0, 0, 0, 0, 0, 0, 5, 8, 8, 0, + 0, 0, 8, 8, 0, 0, 0, 8, 8, 0, 0, 0,10,10, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8,10,10, 0, 0, 0,10,10, 0, 0, 0, 9,10, 0, 0, 0, + 11,10, 0, 0, 0, 0, 0, 0, 0, 8,10,10, 0, 0, 0,10, + 10, 0, 0, 0,10,10, 0, 0, 0,10,11, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c5_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c5_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c5_s_p2_0 = { + _vq_quantthresh__44c5_s_p2_0, + _vq_quantmap__44c5_s_p2_0, + 5, + 5 +}; + +static static_codebook _44c5_s_p2_0 = { + 4, 625, + _vq_lengthlist__44c5_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c5_s_p2_0, + NULL, + &_vq_auxt__44c5_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c5_s_p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c5_s_p3_0[] = { + 2, 4, 3, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 6, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 5, 5, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 6, 6, 8, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c5_s_p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c5_s_p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c5_s_p3_0 = { + _vq_quantthresh__44c5_s_p3_0, + _vq_quantmap__44c5_s_p3_0, + 5, + 5 +}; + +static static_codebook _44c5_s_p3_0 = { + 4, 625, + _vq_lengthlist__44c5_s_p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c5_s_p3_0, + NULL, + &_vq_auxt__44c5_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c5_s_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c5_s_p4_0[] = { + 2, 3, 3, 6, 6, 0, 0, 0, 0, 0, 4, 4, 6, 6, 0, 0, + 0, 0, 0, 4, 4, 6, 6, 0, 0, 0, 0, 0, 5, 5, 6, 6, + 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, + 7, 7, 0, 0, 0, 0, 0, 0, 0, 8, 7, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c5_s_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c5_s_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c5_s_p4_0 = { + _vq_quantthresh__44c5_s_p4_0, + _vq_quantmap__44c5_s_p4_0, + 9, + 9 +}; + +static static_codebook _44c5_s_p4_0 = { + 2, 81, + _vq_lengthlist__44c5_s_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c5_s_p4_0, + NULL, + &_vq_auxt__44c5_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c5_s_p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c5_s_p5_0[] = { + 2, 4, 3, 6, 6, 7, 7, 9, 9, 0, 4, 4, 6, 6, 7, 7, + 9, 9, 0, 4, 4, 6, 6, 7, 7, 9, 9, 0, 6, 6, 7, 7, + 7, 7, 9, 9, 0, 0, 0, 7, 6, 7, 7, 9, 9, 0, 0, 0, + 8, 8, 8, 8,10,10, 0, 0, 0, 8, 8, 8, 8,10,10, 0, + 0, 0, 9, 9, 9, 9,10,10, 0, 0, 0, 0, 0, 9, 9,10, + 10, +}; + +static float _vq_quantthresh__44c5_s_p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c5_s_p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c5_s_p5_0 = { + _vq_quantthresh__44c5_s_p5_0, + _vq_quantmap__44c5_s_p5_0, + 9, + 9 +}; + +static static_codebook _44c5_s_p5_0 = { + 2, 81, + _vq_lengthlist__44c5_s_p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c5_s_p5_0, + NULL, + &_vq_auxt__44c5_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c5_s_p6_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c5_s_p6_0[] = { + 2, 4, 4, 6, 6, 8, 8, 9, 9, 9, 9,10,10,10,10,11, + 11, 0, 4, 4, 6, 6, 8, 8, 9, 9, 9, 9,10,10,11,11, + 12,12, 0, 4, 4, 6, 6, 8, 8, 9, 9, 9, 9,10,10,11, + 11,12,12, 0, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9,10,10, + 11,11,12,12, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10, + 10,11,11,12,12, 0, 0, 0, 7, 7, 9, 9,10,10,10,10, + 11,11,11,11,12,12, 0, 0, 0, 7, 7, 8, 9,10,10,10, + 10,11,11,11,11,12,12, 0, 0, 0, 8, 8, 9, 9,10,10, + 10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 9, 9,10, + 10,10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 9, 9, + 10,10,10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 9, + 9, 9,10,10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, + 10,10,10,10,11,11,11,12,12,12,13,13, 0, 0, 0, 0, + 0, 0, 0,10,10,11,11,11,11,12,12,13,13, 0, 0, 0, + 0, 0, 0, 0,11,11,11,11,12,12,12,13,13,13, 0, 0, + 0, 0, 0, 0, 0,11,11,11,11,12,12,12,12,13,13, 0, + 0, 0, 0, 0, 0, 0,12,12,12,12,13,12,13,13,13,13, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,12,12,13,13,13, + 13, +}; + +static float _vq_quantthresh__44c5_s_p6_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c5_s_p6_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c5_s_p6_0 = { + _vq_quantthresh__44c5_s_p6_0, + _vq_quantmap__44c5_s_p6_0, + 17, + 17 +}; + +static static_codebook _44c5_s_p6_0 = { + 2, 289, + _vq_lengthlist__44c5_s_p6_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c5_s_p6_0, + NULL, + &_vq_auxt__44c5_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c5_s_p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c5_s_p7_0[] = { + 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 7,10, 9, 9,11, + 9, 9, 4, 7, 7,10, 9, 9,11, 9, 9, 7,10,10,11,11, + 10,11,11,11, 6, 9, 9,11,10,10,11,10,10, 6, 9, 9, + 11,10,10,11,10,10, 7,11,11,12,11,11,12,11,11, 6, + 9, 9,11,10,10,11,10,10, 6, 9, 9,11,10,10,11,10, + 10, +}; + +static float _vq_quantthresh__44c5_s_p7_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44c5_s_p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c5_s_p7_0 = { + _vq_quantthresh__44c5_s_p7_0, + _vq_quantmap__44c5_s_p7_0, + 3, + 3 +}; + +static static_codebook _44c5_s_p7_0 = { + 4, 81, + _vq_lengthlist__44c5_s_p7_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44c5_s_p7_0, + NULL, + &_vq_auxt__44c5_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c5_s_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c5_s_p7_1[] = { + 2, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8,10, 5, 5, 6, 6, + 7, 7, 8, 8, 8, 8,10, 5, 5, 6, 6, 7, 7, 8, 8, 8, + 8,10, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, 7, + 7, 8, 8, 8, 8, 8, 8,10,10,10, 7, 7, 8, 8, 8, 8, + 8, 8,10,10,10, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, + 8, 8, 8, 8, 8, 8, 8, 9,10,10,10,10,10, 8, 8, 8, + 8, 8, 8,10,10,10,10,10, 9, 9, 8, 8, 8, 8,10,10, + 10,10,10, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44c5_s_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c5_s_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c5_s_p7_1 = { + _vq_quantthresh__44c5_s_p7_1, + _vq_quantmap__44c5_s_p7_1, + 11, + 11 +}; + +static static_codebook _44c5_s_p7_1 = { + 2, 121, + _vq_lengthlist__44c5_s_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c5_s_p7_1, + NULL, + &_vq_auxt__44c5_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c5_s_p8_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c5_s_p8_0[] = { + 1, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10, 6, 5, 5, + 7, 7, 8, 8, 8, 9,10,10,10,10, 7, 5, 5, 7, 7, 8, + 8, 9, 9,10,10,10,10, 0, 8, 8, 8, 8, 9, 9, 9, 9, + 10,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11, + 11, 0,12,12, 9, 9, 9,10,10,10,10,10,11,11, 0,13, + 13, 9, 9, 9, 9,10,10,11,11,11,11, 0, 0, 0,10,10, + 10,10,10,10,11,11,11,11, 0, 0, 0,10,10,10,10,10, + 10,11,11,12,12, 0, 0, 0,14,14,11,11,11,11,12,12, + 12,12, 0, 0, 0,14,14,11,11,11,11,12,12,12,12, 0, + 0, 0, 0, 0,12,12,12,12,12,12,13,13, 0, 0, 0, 0, + 0,12,12,12,12,12,12,13,13, +}; + +static float _vq_quantthresh__44c5_s_p8_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44c5_s_p8_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c5_s_p8_0 = { + _vq_quantthresh__44c5_s_p8_0, + _vq_quantmap__44c5_s_p8_0, + 13, + 13 +}; + +static static_codebook _44c5_s_p8_0 = { + 2, 169, + _vq_lengthlist__44c5_s_p8_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44c5_s_p8_0, + NULL, + &_vq_auxt__44c5_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c5_s_p8_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c5_s_p8_1[] = { + 2, 4, 4, 5, 5, 6, 5, 5, 5, 5, 6, 4, 5, 5, 5, 6, + 5, 5, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__44c5_s_p8_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c5_s_p8_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c5_s_p8_1 = { + _vq_quantthresh__44c5_s_p8_1, + _vq_quantmap__44c5_s_p8_1, + 5, + 5 +}; + +static static_codebook _44c5_s_p8_1 = { + 2, 25, + _vq_lengthlist__44c5_s_p8_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c5_s_p8_1, + NULL, + &_vq_auxt__44c5_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c5_s_p9_0[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44c5_s_p9_0[] = { + 1, 3, 3,13,13,13,13,13,13,13,13,13,13,13,13, 4, + 7, 7,13,13,13,13,13,13,13,13,13,13,13,13, 3, 8, + 6,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,12,12,12,12,12,12,12, + 12, +}; + +static float _vq_quantthresh__44c5_s_p9_0[] = { + -2320.5, -1963.5, -1606.5, -1249.5, -892.5, -535.5, -178.5, 178.5, + 535.5, 892.5, 1249.5, 1606.5, 1963.5, 2320.5, +}; + +static long _vq_quantmap__44c5_s_p9_0[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44c5_s_p9_0 = { + _vq_quantthresh__44c5_s_p9_0, + _vq_quantmap__44c5_s_p9_0, + 15, + 15 +}; + +static static_codebook _44c5_s_p9_0 = { + 2, 225, + _vq_lengthlist__44c5_s_p9_0, + 1, -512522752, 1628852224, 4, 0, + _vq_quantlist__44c5_s_p9_0, + NULL, + &_vq_auxt__44c5_s_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c5_s_p9_1[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c5_s_p9_1[] = { + 1, 4, 4, 5, 5, 7, 7, 9, 8,10, 9,10,10,11,10,11, + 11, 6, 5, 5, 7, 7, 8, 9,10,10,11,10,12,11,12,11, + 13,12, 6, 5, 5, 7, 7, 9, 9,10,10,11,11,12,12,13, + 12,13,13,18, 8, 8, 8, 8, 9, 9,10,11,11,11,12,11, + 13,11,13,12,18, 8, 8, 8, 8,10,10,11,11,12,12,13, + 13,13,13,13,14,18,12,12, 9, 9,11,11,11,11,12,12, + 13,12,13,12,13,13,20,13,12, 9, 9,11,11,11,11,12, + 12,13,13,13,14,14,13,20,18,19,11,12,11,11,12,12, + 13,13,13,13,13,13,14,13,18,19,19,12,11,11,11,12, + 12,13,12,13,13,13,14,14,13,18,17,19,14,15,12,12, + 12,13,13,13,14,14,14,14,14,14,19,19,19,16,15,12, + 11,13,12,14,14,14,13,13,14,14,14,19,18,19,18,19, + 13,13,13,13,14,14,14,13,14,14,14,14,18,17,19,19, + 19,13,13,13,11,13,11,13,14,14,14,14,14,19,17,17, + 18,18,16,16,13,13,13,13,14,13,15,15,14,14,19,19, + 17,17,18,16,16,13,11,14,10,13,12,14,14,14,14,19, + 19,19,19,19,18,17,13,14,13,11,14,13,14,14,15,15, + 19,19,19,17,19,18,18,14,13,12,11,14,11,15,15,15, + 15, +}; + +static float _vq_quantthresh__44c5_s_p9_1[] = { + -157.5, -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, + 10.5, 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, 157.5, +}; + +static long _vq_quantmap__44c5_s_p9_1[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c5_s_p9_1 = { + _vq_quantthresh__44c5_s_p9_1, + _vq_quantmap__44c5_s_p9_1, + 17, + 17 +}; + +static static_codebook _44c5_s_p9_1 = { + 2, 289, + _vq_lengthlist__44c5_s_p9_1, + 1, -520814592, 1620377600, 5, 0, + _vq_quantlist__44c5_s_p9_1, + NULL, + &_vq_auxt__44c5_s_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c5_s_p9_2[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__44c5_s_p9_2[] = { + 3, 5, 5, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 9,11, 5, 6, 7, 7, 8, 7, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,11, 5, 5, 7, 7, 7, + 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,11, + 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, + 9,10, 9,10,11,11,11, 7, 7, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9,10,10,10,10,10,10,11,11,11, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9,10,10,10,10,10,10,10,11,11, + 11, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9,10,10,10,10,10, + 10,10,10,11,11,11, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 10,10,10,10,10,10,10,10,11,11,11,11,11, 9, 9, 9, + 9, 9, 9,10, 9,10,10,10,10,10,10,10,10,11,11,11, + 11,11, 9, 9, 9, 9, 9, 9,10,10,10,10,10,10,10,10, + 10,10,11,11,11,11,11, 9, 9, 9, 9, 9, 9,10,10,10, + 10,10,10,10,10,10,10,11,11,11,11,11, 9, 9,10, 9, + 10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11, + 11,11,11, 9, 9,10,10,10,10,10,10,10,10,10,10,10, + 10,11,11,11,11,11,11,11,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,11,11,11,11,11,11,11,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11, + 11,11,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 11,11,11,11,11,11,11,11,11,10,10,10,10,10,10,10, + 10,10,10,10,10,11,11,11,11,11,11,11,11,11,10,10, + 10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11, + 11,11,11,10,10,10,10,10,10,10,10,10,10,10,10,11, + 11,11,11,11,11,11,11,11,10,10,10,10,10,10,10,10, + 10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,10, + 10,10,10,10,10,10,10,10,10, +}; + +static float _vq_quantthresh__44c5_s_p9_2[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__44c5_s_p9_2[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__44c5_s_p9_2 = { + _vq_quantthresh__44c5_s_p9_2, + _vq_quantmap__44c5_s_p9_2, + 21, + 21 +}; + +static static_codebook _44c5_s_p9_2 = { + 2, 441, + _vq_lengthlist__44c5_s_p9_2, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__44c5_s_p9_2, + NULL, + &_vq_auxt__44c5_s_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44c5_s_short[] = { + 5, 8,10,14,11,11,12,16,15,17, 5, 5, 7, 9, 7, 8, + 10,13,17,17, 7, 5, 5,10, 5, 7, 8,11,13,15,10, 8, + 10, 8, 8, 8,11,15,18,18, 8, 5, 5, 8, 3, 4, 6,10, + 14,16, 9, 7, 6, 7, 4, 3, 5, 9,14,18,10, 9, 8,10, + 6, 5, 6, 9,14,18,12,12,11,12, 8, 7, 8,11,14,18, + 14,13,12,10, 7, 5, 6, 9,14,18,14,14,13,10, 6, 5, + 6, 8,11,16, +}; + +static static_codebook _huff_book__44c5_s_short = { + 2, 100, + _huff_lengthlist__44c5_s_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44c6_s_long[] = { + 3, 8,11,13,14,14,13,13,16,14, 6, 3, 4, 7, 9, 9, + 10,11,14,13,10, 4, 3, 5, 7, 7, 9,10,13,15,12, 7, + 4, 4, 6, 6, 8,10,13,15,12, 8, 6, 6, 6, 6, 8,10, + 13,14,11, 9, 7, 6, 6, 6, 7, 8,12,11,13,10, 9, 8, + 7, 6, 6, 7,11,11,13,11,10, 9, 9, 7, 7, 6,10,11, + 13,13,13,13,13,11, 9, 8,10,12,12,15,15,16,15,12, + 11,10,10,12, +}; + +static static_codebook _huff_book__44c6_s_long = { + 2, 100, + _huff_lengthlist__44c6_s_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c6_s_p1_0[] = { + 1, 5, 5, 0, 5, 5, 0, 5, 5, 5, 8, 7, 0, 9, 9, 0, + 9, 8, 5, 7, 8, 0, 9, 9, 0, 8, 9, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 9, 8, 0, 8, 8, 0, 8, 8, 5, 8, 9, + 0, 8, 8, 0, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, + 9, 9, 0, 8, 8, 0, 8, 8, 5, 9, 9, 0, 8, 8, 0, 8, + 8, +}; + +static float _vq_quantthresh__44c6_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44c6_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p1_0 = { + _vq_quantthresh__44c6_s_p1_0, + _vq_quantmap__44c6_s_p1_0, + 3, + 3 +}; + +static static_codebook _44c6_s_p1_0 = { + 4, 81, + _vq_lengthlist__44c6_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44c6_s_p1_0, + NULL, + &_vq_auxt__44c6_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c6_s_p2_0[] = { + 3, 5, 5, 8, 8, 0, 5, 5, 8, 8, 0, 5, 5, 8, 8, 0, + 7, 7, 9, 9, 0, 0, 0, 9, 9, 5, 7, 7, 9, 9, 0, 8, + 8,10,10, 0, 8, 7,10, 9, 0,10,10,11,11, 0, 0, 0, + 11,11, 5, 7, 7, 9, 9, 0, 8, 8,10,10, 0, 7, 8, 9, + 10, 0,10,10,11,11, 0, 0, 0,11,11, 8, 9, 9,11,11, + 0,11,11,12,12, 0,11,10,12,12, 0,13,14,14,14, 0, + 0, 0,14,13, 8, 9, 9,11,11, 0,11,11,12,12, 0,10, + 11,12,12, 0,14,13,14,14, 0, 0, 0,13,14, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 5, 8, 7,11,10, 0, 7, 7,10,10, + 0, 7, 7,10,10, 0, 9, 9,11,10, 0, 0, 0,11,11, 5, + 7, 8,10,11, 0, 7, 7,10,10, 0, 7, 7,10,10, 0, 9, + 9,10,11, 0, 0, 0,11,11, 8,10, 9,12,12, 0,10,10, + 12,12, 0,10,10,12,12, 0,12,12,13,13, 0, 0, 0,13, + 13, 8, 9,10,12,12, 0,10,10,11,12, 0,10,10,12,12, + 0,12,12,13,13, 0, 0, 0,13,13, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 8, 8,11,11, 0, 7, 7,10,10, 0, 7, 7, + 10,10, 0, 9, 9,10,11, 0, 0, 0,11,10, 5, 8, 8,11, + 11, 0, 7, 7,10,10, 0, 7, 7,10,10, 0, 9, 9,11,11, + 0, 0, 0,10,11, 8,10,10,12,12, 0,10,10,12,12, 0, + 10,10,12,12, 0,12,13,13,13, 0, 0, 0,14,13, 8,10, + 10,12,12, 0,10,10,12,12, 0,10,10,12,12, 0,13,12, + 13,13, 0, 0, 0,13,13, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7,10,10,14,13, 0, 9, 9,13,12, 0, 9, 9,12,12, 0, + 10,10,12,12, 0, 0, 0,12,12, 7,10,10,13,14, 0, 9, + 9,12,13, 0, 9, 9,12,12, 0,10,10,12,12, 0, 0, 0, + 12,12, 9,11,11,14,13, 0,11,10,14,13, 0,11,11,13, + 13, 0,12,12,13,13, 0, 0, 0,13,13, 9,11,11,13,14, + 0,10,11,13,14, 0,11,11,13,13, 0,12,12,13,13, 0, + 0, 0,13,13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, + 11,11,14,14, 0,11,11,13,13, 0,11,10,13,13, 0,12, + 12,13,13, 0, 0, 0,13,13, 9,11,11,14,14, 0,11,11, + 13,13, 0,10,11,13,13, 0,12,12,14,13, 0, 0, 0,13, + 13, +}; + +static float _vq_quantthresh__44c6_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c6_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p2_0 = { + _vq_quantthresh__44c6_s_p2_0, + _vq_quantmap__44c6_s_p2_0, + 5, + 5 +}; + +static static_codebook _44c6_s_p2_0 = { + 4, 625, + _vq_lengthlist__44c6_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c6_s_p2_0, + NULL, + &_vq_auxt__44c6_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c6_s_p3_0[] = { + 2, 3, 4, 6, 6, 7, 7, 9, 9, 0, 4, 4, 6, 6, 7, 7, + 9,10, 0, 4, 4, 6, 6, 7, 7,10, 9, 0, 5, 5, 7, 7, + 8, 8,10,10, 0, 0, 0, 7, 6, 8, 8,10,10, 0, 0, 0, + 7, 7, 9, 9,11,11, 0, 0, 0, 7, 7, 9, 9,11,11, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c6_s_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c6_s_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p3_0 = { + _vq_quantthresh__44c6_s_p3_0, + _vq_quantmap__44c6_s_p3_0, + 9, + 9 +}; + +static static_codebook _44c6_s_p3_0 = { + 2, 81, + _vq_lengthlist__44c6_s_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c6_s_p3_0, + NULL, + &_vq_auxt__44c6_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p4_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c6_s_p4_0[] = { + 2, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9,10,10, + 10, 0, 4, 4, 6, 6, 8, 8, 9, 9, 9, 9,10,10,10,10, + 11,11, 0, 4, 4, 6, 6, 8, 8, 9, 9, 9, 9,10,10,10, + 10,11,11, 0, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9,10,10, + 11,11,11,11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10, + 10,11,11,11,11, 0, 0, 0, 7, 7, 9, 9,10,10,10,10, + 11,11,11,11,12,12, 0, 0, 0, 7, 7, 9, 9,10,10,10, + 10,11,11,11,11,12,12, 0, 0, 0, 7, 7, 8, 8, 9, 9, + 10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 8, 8, 9, + 9,10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c6_s_p4_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c6_s_p4_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p4_0 = { + _vq_quantthresh__44c6_s_p4_0, + _vq_quantmap__44c6_s_p4_0, + 17, + 17 +}; + +static static_codebook _44c6_s_p4_0 = { + 2, 289, + _vq_lengthlist__44c6_s_p4_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c6_s_p4_0, + NULL, + &_vq_auxt__44c6_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p5_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c6_s_p5_0[] = { + 1, 4, 4, 5, 7, 7, 6, 7, 7, 4, 6, 6, 9, 9,10,10, + 10, 9, 4, 6, 6, 9,10, 9,10, 9,10, 6, 9, 9,10,12, + 11,10,11,11, 7,10, 9,11,12,12,12,12,12, 7,10,10, + 11,12,12,12,12,12, 6,10,10,10,12,12,11,12,12, 7, + 9,10,11,12,12,12,12,12, 7,10, 9,12,12,12,12,12, + 12, +}; + +static float _vq_quantthresh__44c6_s_p5_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44c6_s_p5_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p5_0 = { + _vq_quantthresh__44c6_s_p5_0, + _vq_quantmap__44c6_s_p5_0, + 3, + 3 +}; + +static static_codebook _44c6_s_p5_0 = { + 4, 81, + _vq_lengthlist__44c6_s_p5_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44c6_s_p5_0, + NULL, + &_vq_auxt__44c6_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p5_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c6_s_p5_1[] = { + 3, 5, 4, 6, 6, 7, 7, 8, 8, 8, 8,11, 4, 4, 6, 6, + 7, 7, 8, 8, 8, 8,11, 4, 4, 6, 6, 7, 7, 8, 8, 8, + 8,11, 6, 6, 6, 6, 8, 8, 8, 8, 9, 9,11,11,11, 6, + 6, 7, 8, 8, 8, 8, 9,11,11,11, 7, 7, 8, 8, 8, 8, + 8, 8,11,11,11, 7, 7, 8, 8, 8, 8, 8, 8,11,11,11, + 8, 8, 8, 8, 8, 8, 8, 8,11,11,11,10,10, 8, 8, 8, + 8, 8, 8,11,11,11,10,10, 8, 8, 8, 8, 8, 8,11,11, + 11,10,10, 7, 7, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44c6_s_p5_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c6_s_p5_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p5_1 = { + _vq_quantthresh__44c6_s_p5_1, + _vq_quantmap__44c6_s_p5_1, + 11, + 11 +}; + +static static_codebook _44c6_s_p5_1 = { + 2, 121, + _vq_lengthlist__44c6_s_p5_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c6_s_p5_1, + NULL, + &_vq_auxt__44c6_s_p5_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c6_s_p6_0[] = { + 1, 4, 4, 6, 6, 8, 8, 8, 8,10, 9,10,10, 6, 5, 5, + 7, 7, 9, 9, 9, 9,10,10,11,11, 6, 5, 5, 7, 7, 9, + 9,10, 9,11,10,11,11, 0, 6, 6, 7, 7, 9, 9,10,10, + 11,11,12,12, 0, 7, 7, 7, 7, 9, 9,10,10,11,11,12, + 12, 0,11,11, 8, 8,10,10,11,11,12,12,12,12, 0,11, + 12, 9, 8,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static float _vq_quantthresh__44c6_s_p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44c6_s_p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p6_0 = { + _vq_quantthresh__44c6_s_p6_0, + _vq_quantmap__44c6_s_p6_0, + 13, + 13 +}; + +static static_codebook _44c6_s_p6_0 = { + 2, 169, + _vq_lengthlist__44c6_s_p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44c6_s_p6_0, + NULL, + &_vq_auxt__44c6_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c6_s_p6_1[] = { + 3, 4, 4, 5, 5, 5, 4, 4, 5, 5, 5, 4, 4, 5, 5, 6, + 5, 5, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__44c6_s_p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c6_s_p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p6_1 = { + _vq_quantthresh__44c6_s_p6_1, + _vq_quantmap__44c6_s_p6_1, + 5, + 5 +}; + +static static_codebook _44c6_s_p6_1 = { + 2, 25, + _vq_lengthlist__44c6_s_p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c6_s_p6_1, + NULL, + &_vq_auxt__44c6_s_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c6_s_p7_0[] = { + 1, 4, 4, 6, 6, 8, 8, 8, 8,10,10,11,10, 6, 5, 5, + 7, 7, 8, 8, 9, 9,10,10,12,11, 6, 5, 5, 7, 7, 8, + 8, 9, 9,10,10,12,11,21, 7, 7, 7, 7, 9, 9,10,10, + 11,11,12,12,21, 7, 7, 7, 7, 9, 9,10,10,11,11,12, + 12,21,12,12, 9, 9,10,10,11,11,11,11,12,12,21,12, + 12, 9, 9,10,10,11,11,12,12,12,12,21,21,21,11,11, + 10,10,11,12,12,12,13,13,21,21,21,11,11,10,10,12, + 12,12,12,13,13,21,21,21,15,15,11,11,12,12,13,13, + 13,13,21,21,21,15,16,11,11,12,12,13,13,14,14,21, + 21,21,21,20,13,13,13,13,13,13,14,14,20,20,20,20, + 20,13,13,13,13,13,13,14,14, +}; + +static float _vq_quantthresh__44c6_s_p7_0[] = { + -60.5, -49.5, -38.5, -27.5, -16.5, -5.5, 5.5, 16.5, + 27.5, 38.5, 49.5, 60.5, +}; + +static long _vq_quantmap__44c6_s_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p7_0 = { + _vq_quantthresh__44c6_s_p7_0, + _vq_quantmap__44c6_s_p7_0, + 13, + 13 +}; + +static static_codebook _44c6_s_p7_0 = { + 2, 169, + _vq_lengthlist__44c6_s_p7_0, + 1, -523206656, 1618345984, 4, 0, + _vq_quantlist__44c6_s_p7_0, + NULL, + &_vq_auxt__44c6_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c6_s_p7_1[] = { + 3, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 9, 5, 5, 6, 6, + 7, 7, 7, 7, 8, 7, 8, 5, 5, 6, 6, 7, 7, 7, 7, 7, + 7, 9, 6, 6, 7, 7, 7, 7, 8, 7, 7, 8, 9, 9, 9, 7, + 7, 7, 7, 7, 7, 7, 8, 9, 9, 9, 7, 7, 7, 7, 8, 8, + 8, 8, 9, 9, 9, 7, 7, 7, 7, 7, 7, 8, 8, 9, 9, 9, + 8, 8, 8, 8, 7, 7, 8, 8, 9, 9, 9, 9, 8, 8, 8, 7, + 7, 8, 8, 9, 9, 9, 8, 8, 8, 8, 7, 7, 8, 8, 9, 9, + 9, 8, 8, 7, 7, 7, 7, 8, 8, +}; + +static float _vq_quantthresh__44c6_s_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c6_s_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p7_1 = { + _vq_quantthresh__44c6_s_p7_1, + _vq_quantmap__44c6_s_p7_1, + 11, + 11 +}; + +static static_codebook _44c6_s_p7_1 = { + 2, 121, + _vq_lengthlist__44c6_s_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c6_s_p7_1, + NULL, + &_vq_auxt__44c6_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p8_0[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44c6_s_p8_0[] = { + 1, 4, 4, 7, 7, 8, 8, 7, 7, 8, 7, 9, 8,10, 9, 6, + 5, 5, 8, 8, 9, 9, 8, 8, 9, 9,11,10,11,10, 6, 5, + 5, 8, 8, 9, 9, 8, 8, 9, 9,10,10,11,11,18, 8, 8, + 9, 8,10,10, 9, 9,10,10,10,10,11,10,18, 8, 8, 9, + 9,10,10, 9, 9,10,10,11,11,12,12,18,12,13, 9,10, + 10,10, 9,10,10,10,11,11,12,11,18,13,13, 9, 9,10, + 10,10,10,10,10,11,11,12,12,18,18,18,10,10, 9, 9, + 11,11,11,11,11,12,12,12,18,18,18,10, 9,10, 9,11, + 10,11,11,11,11,13,12,18,18,18,14,13,10,10,11,11, + 12,12,12,12,12,12,18,18,18,14,13,10,10,11,10,12, + 12,12,12,12,12,18,18,18,18,18,12,12,11,11,12,12, + 13,13,13,14,18,18,18,18,18,12,12,11,11,12,11,13, + 13,14,13,18,18,18,18,18,16,16,11,12,12,13,13,13, + 14,13,18,18,18,18,18,16,15,12,11,12,11,13,11,15, + 14, +}; + +static float _vq_quantthresh__44c6_s_p8_0[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__44c6_s_p8_0[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p8_0 = { + _vq_quantthresh__44c6_s_p8_0, + _vq_quantmap__44c6_s_p8_0, + 15, + 15 +}; + +static static_codebook _44c6_s_p8_0 = { + 2, 225, + _vq_lengthlist__44c6_s_p8_0, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__44c6_s_p8_0, + NULL, + &_vq_auxt__44c6_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p8_1[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__44c6_s_p8_1[] = { + 3, 5, 5, 6, 6, 7, 7, 7, 7, 8, 7, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8,10, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 6, 6, 7, 7, 8, + 8, 8, 8, 8, 8, 9, 8, 9, 9, 9, 9, 9, 9, 9, 9,10, + 7, 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9,10,11,11, 8, 7, 8, 8, 8, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9,11,11,11, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,11,11, + 11, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9,11,11,11, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9,11,11,11,11,11, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10, 9,11,11,11, + 11,11, 9, 9, 9, 9, 9, 9,10, 9, 9,10, 9,10, 9, 9, + 10, 9,11,11,11,11,11, 9, 9, 9, 9, 9, 9, 9,10,10, + 10,10, 9,10,10, 9,10,11,11,11,11,11, 9, 9, 9, 9, + 10,10,10, 9,10,10,10,10, 9,10,10, 9,11,11,11,11, + 11,11,11, 9, 9, 9, 9,10,10,10,10, 9,10,10,10,10, + 10,11,11,11,11,11,11,11,10, 9,10,10,10,10,10,10, + 10, 9,10, 9,10,10,11,11,11,11,11,11,11,10, 9,10, + 9,10,10, 9,10,10,10,10,10,10,10,11,11,11,11,11, + 11,11,10,10,10,10,10,10,10, 9,10,10,10,10,10, 9, + 11,11,11,11,11,11,11,11,11,10,10,10,10,10,10,10, + 10,10,10,10,10,11,11,11,11,11,11,11,11,11,10,10, + 10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11, + 11,11,11,10,10,10,10,10,10,10,10,10, 9,10,10,11, + 11,11,11,11,11,11,11,11,10,10,10, 9,10,10,10,10, + 10,10,10,10,10,11,11,11,11,11,11,11,11,10,11, 9, + 10,10,10,10,10,10,10,10,10, +}; + +static float _vq_quantthresh__44c6_s_p8_1[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__44c6_s_p8_1[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p8_1 = { + _vq_quantthresh__44c6_s_p8_1, + _vq_quantmap__44c6_s_p8_1, + 21, + 21 +}; + +static static_codebook _44c6_s_p8_1 = { + 2, 441, + _vq_lengthlist__44c6_s_p8_1, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__44c6_s_p8_1, + NULL, + &_vq_auxt__44c6_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p9_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c6_s_p9_0[] = { + 1, 3, 3,11,11,11,11,11,11,11,11,11,11, 4, 7, 7, + 11,11,11,11,11,11,11,11,11,11, 5, 8, 9,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10, +}; + +static float _vq_quantthresh__44c6_s_p9_0[] = { + -3503.5, -2866.5, -2229.5, -1592.5, -955.5, -318.5, 318.5, 955.5, + 1592.5, 2229.5, 2866.5, 3503.5, +}; + +static long _vq_quantmap__44c6_s_p9_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p9_0 = { + _vq_quantthresh__44c6_s_p9_0, + _vq_quantmap__44c6_s_p9_0, + 13, + 13 +}; + +static static_codebook _44c6_s_p9_0 = { + 2, 169, + _vq_lengthlist__44c6_s_p9_0, + 1, -511845376, 1630791680, 4, 0, + _vq_quantlist__44c6_s_p9_0, + NULL, + &_vq_auxt__44c6_s_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p9_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c6_s_p9_1[] = { + 1, 4, 4, 7, 7, 7, 7, 7, 6, 8, 8, 8, 8, 6, 6, 6, + 8, 8, 8, 8, 8, 7, 9, 8,10,10, 5, 6, 6, 8, 8, 9, + 9, 8, 8,10,10,10,10,16, 9, 9, 9, 9, 9, 9, 9, 8, + 10, 9,11,11,16, 8, 9, 9, 9, 9, 9, 9, 9,10,10,11, + 11,16,13,13, 9, 9,10, 9, 9,10,11,11,11,12,16,13, + 14, 9, 8,10, 8, 9, 9,10,10,12,11,16,14,16, 9, 9, + 9, 9,11,11,12,11,12,11,16,16,16, 9, 7, 9, 6,11, + 11,11,10,11,11,16,16,16,11,12, 9,10,11,11,12,11, + 13,13,16,16,16,12,11,10, 7,12,10,12,12,12,12,16, + 16,15,16,16,10,11,10,11,13,13,14,12,16,16,16,15, + 15,12,10,11,11,13,11,12,13, +}; + +static float _vq_quantthresh__44c6_s_p9_1[] = { + -269.5, -220.5, -171.5, -122.5, -73.5, -24.5, 24.5, 73.5, + 122.5, 171.5, 220.5, 269.5, +}; + +static long _vq_quantmap__44c6_s_p9_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p9_1 = { + _vq_quantthresh__44c6_s_p9_1, + _vq_quantmap__44c6_s_p9_1, + 13, + 13 +}; + +static static_codebook _44c6_s_p9_1 = { + 2, 169, + _vq_lengthlist__44c6_s_p9_1, + 1, -518889472, 1622704128, 4, 0, + _vq_quantlist__44c6_s_p9_1, + NULL, + &_vq_auxt__44c6_s_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c6_s_p9_2[] = { + 24, + 23, + 25, + 22, + 26, + 21, + 27, + 20, + 28, + 19, + 29, + 18, + 30, + 17, + 31, + 16, + 32, + 15, + 33, + 14, + 34, + 13, + 35, + 12, + 36, + 11, + 37, + 10, + 38, + 9, + 39, + 8, + 40, + 7, + 41, + 6, + 42, + 5, + 43, + 4, + 44, + 3, + 45, + 2, + 46, + 1, + 47, + 0, + 48, +}; + +static long _vq_lengthlist__44c6_s_p9_2[] = { + 2, 4, 3, 4, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, +}; + +static float _vq_quantthresh__44c6_s_p9_2[] = { + -23.5, -22.5, -21.5, -20.5, -19.5, -18.5, -17.5, -16.5, + -15.5, -14.5, -13.5, -12.5, -11.5, -10.5, -9.5, -8.5, + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, + 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, + 16.5, 17.5, 18.5, 19.5, 20.5, 21.5, 22.5, 23.5, +}; + +static long _vq_quantmap__44c6_s_p9_2[] = { + 47, 45, 43, 41, 39, 37, 35, 33, + 31, 29, 27, 25, 23, 21, 19, 17, + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, + 48, +}; + +static encode_aux_threshmatch _vq_auxt__44c6_s_p9_2 = { + _vq_quantthresh__44c6_s_p9_2, + _vq_quantmap__44c6_s_p9_2, + 49, + 49 +}; + +static static_codebook _44c6_s_p9_2 = { + 1, 49, + _vq_lengthlist__44c6_s_p9_2, + 1, -526909440, 1611661312, 6, 0, + _vq_quantlist__44c6_s_p9_2, + NULL, + &_vq_auxt__44c6_s_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44c6_s_short[] = { + 3, 9,11,11,13,14,19,17,17,19, 5, 4, 5, 8,10,10, + 13,16,18,19, 7, 4, 4, 5, 8, 9,12,14,17,19, 8, 6, + 5, 5, 7, 7,10,13,16,18,10, 8, 7, 6, 5, 5, 8,11, + 17,19,11, 9, 7, 7, 5, 4, 5, 8,17,19,13,11, 8, 7, + 7, 5, 5, 7,16,18,14,13, 8, 6, 6, 5, 5, 7,16,18, + 18,16,10, 8, 8, 7, 7, 9,16,18,18,18,12,10,10, 9, + 9,10,17,18, +}; + +static static_codebook _huff_book__44c6_s_short = { + 2, 100, + _huff_lengthlist__44c6_s_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44c7_s_long[] = { + 3, 8,11,13,15,14,14,13,15,14, 6, 4, 5, 7, 9,10, + 11,11,14,13,10, 4, 3, 5, 7, 8, 9,10,13,13,12, 7, + 4, 4, 5, 6, 8, 9,12,14,13, 9, 6, 5, 5, 6, 8, 9, + 12,14,12, 9, 7, 6, 5, 5, 6, 8,11,11,12,11, 9, 8, + 7, 6, 6, 7,10,11,13,11,10, 9, 8, 7, 6, 6, 9,11, + 13,13,12,12,12,10, 9, 8, 9,11,12,14,15,15,14,12, + 11,10,10,12, +}; + +static static_codebook _huff_book__44c7_s_long = { + 2, 100, + _huff_lengthlist__44c7_s_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c7_s_p1_0[] = { + 1, 5, 5, 0, 5, 5, 0, 5, 5, 5, 8, 7, 0, 9, 9, 0, + 9, 8, 5, 7, 8, 0, 9, 9, 0, 8, 9, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 9, 9, 0, 8, 8, 0, 8, 8, 5, 8, 9, + 0, 8, 8, 0, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, + 9, 9, 0, 8, 8, 0, 8, 8, 5, 8, 9, 0, 8, 8, 0, 8, + 8, +}; + +static float _vq_quantthresh__44c7_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44c7_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p1_0 = { + _vq_quantthresh__44c7_s_p1_0, + _vq_quantmap__44c7_s_p1_0, + 3, + 3 +}; + +static static_codebook _44c7_s_p1_0 = { + 4, 81, + _vq_lengthlist__44c7_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44c7_s_p1_0, + NULL, + &_vq_auxt__44c7_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c7_s_p2_0[] = { + 3, 5, 5, 8, 8, 0, 5, 5, 8, 8, 0, 5, 5, 8, 8, 0, + 7, 7, 9, 9, 0, 0, 0, 9, 9, 5, 7, 7, 9, 9, 0, 8, + 8,10,10, 0, 8, 7,10, 9, 0,10,10,11,11, 0, 0, 0, + 11,11, 5, 7, 7, 9, 9, 0, 8, 8,10,10, 0, 7, 8, 9, + 10, 0,10,10,11,11, 0, 0, 0,11,11, 8, 9, 9,11,10, + 0,11,11,12,12, 0,11,10,12,12, 0,13,14,14,14, 0, + 0, 0,14,13, 8, 9, 9,10,11, 0,11,11,12,12, 0,10, + 11,12,12, 0,13,13,14,14, 0, 0, 0,13,14, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 5, 8, 7,11,10, 0, 7, 7,10,10, + 0, 7, 7,10,10, 0, 9, 9,11,10, 0, 0, 0,11,11, 5, + 7, 8,10,11, 0, 7, 7,10,10, 0, 7, 7,10,10, 0, 9, + 9,10,11, 0, 0, 0,11,11, 8,10, 9,12,12, 0,10,10, + 12,12, 0,10,10,12,12, 0,12,12,13,13, 0, 0, 0,13, + 13, 8, 9,10,12,12, 0,10,10,12,12, 0,10,10,11,12, + 0,12,12,13,13, 0, 0, 0,13,13, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 8, 8,11,11, 0, 7, 7,10,10, 0, 7, 7, + 10,10, 0, 9, 9,10,11, 0, 0, 0,11,10, 5, 8, 8,10, + 11, 0, 7, 7,10,10, 0, 7, 7,10,10, 0, 9, 9,11,10, + 0, 0, 0,10,11, 9,10,10,12,12, 0,10,10,12,12, 0, + 10,10,12,12, 0,12,13,13,13, 0, 0, 0,13,12, 9,10, + 10,12,12, 0,10,10,12,12, 0,10,10,12,12, 0,13,12, + 13,13, 0, 0, 0,12,13, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7,10,10,14,13, 0, 9, 9,12,12, 0, 9, 9,12,12, 0, + 10,10,12,12, 0, 0, 0,12,12, 7,10,10,13,14, 0, 9, + 9,12,13, 0, 9, 9,12,12, 0,10,10,12,12, 0, 0, 0, + 12,12, 9,11,11,14,13, 0,11,10,13,12, 0,11,11,13, + 13, 0,12,12,13,13, 0, 0, 0,13,13, 9,11,11,13,14, + 0,10,11,12,13, 0,11,11,13,13, 0,12,12,13,13, 0, + 0, 0,13,13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, + 11,11,14,14, 0,10,11,13,13, 0,11,10,13,13, 0,12, + 12,13,13, 0, 0, 0,13,12, 9,11,11,14,14, 0,11,10, + 13,13, 0,10,11,13,13, 0,12,12,14,13, 0, 0, 0,13, + 13, +}; + +static float _vq_quantthresh__44c7_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c7_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p2_0 = { + _vq_quantthresh__44c7_s_p2_0, + _vq_quantmap__44c7_s_p2_0, + 5, + 5 +}; + +static static_codebook _44c7_s_p2_0 = { + 4, 625, + _vq_lengthlist__44c7_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c7_s_p2_0, + NULL, + &_vq_auxt__44c7_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c7_s_p3_0[] = { + 2, 4, 4, 5, 5, 7, 7, 9, 9, 0, 4, 4, 6, 6, 7, 7, + 9, 9, 0, 4, 4, 6, 6, 7, 7, 9, 9, 0, 5, 5, 6, 6, + 8, 8,10,10, 0, 0, 0, 6, 6, 8, 8,10,10, 0, 0, 0, + 7, 7, 9, 9,10,10, 0, 0, 0, 7, 7, 8, 8,10,10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c7_s_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c7_s_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p3_0 = { + _vq_quantthresh__44c7_s_p3_0, + _vq_quantmap__44c7_s_p3_0, + 9, + 9 +}; + +static static_codebook _44c7_s_p3_0 = { + 2, 81, + _vq_lengthlist__44c7_s_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c7_s_p3_0, + NULL, + &_vq_auxt__44c7_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p4_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c7_s_p4_0[] = { + 3, 4, 4, 5, 5, 7, 7, 8, 8, 8, 8, 9, 9,10,10,11, + 11, 0, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10,11,11, + 12,12, 0, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10,11, + 11,12,12, 0, 5, 5, 6, 6, 8, 8, 9, 9, 9, 9,10,10, + 11,12,12,12, 0, 0, 0, 6, 6, 8, 7, 9, 9, 9, 9,10, + 10,11,11,12,12, 0, 0, 0, 7, 7, 8, 8, 9, 9,10,10, + 11,11,12,12,13,12, 0, 0, 0, 7, 7, 8, 8, 9, 9,10, + 10,11,11,12,12,12,13, 0, 0, 0, 7, 7, 8, 8, 9, 9, + 10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 8, 8, 9, + 9,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c7_s_p4_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c7_s_p4_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p4_0 = { + _vq_quantthresh__44c7_s_p4_0, + _vq_quantmap__44c7_s_p4_0, + 17, + 17 +}; + +static static_codebook _44c7_s_p4_0 = { + 2, 289, + _vq_lengthlist__44c7_s_p4_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c7_s_p4_0, + NULL, + &_vq_auxt__44c7_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p5_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c7_s_p5_0[] = { + 1, 4, 4, 5, 7, 7, 6, 7, 7, 4, 6, 7,10,10,10,10, + 10, 9, 4, 6, 6,10,10,10,10, 9,10, 5,10,10, 9,11, + 12,10,11,12, 7,10,10,11,12,12,12,12,12, 7,10,10, + 11,12,12,12,12,12, 6,10,10,10,12,12,11,12,12, 7, + 10,10,12,12,12,12,11,12, 7,10,10,11,12,12,12,12, + 12, +}; + +static float _vq_quantthresh__44c7_s_p5_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44c7_s_p5_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p5_0 = { + _vq_quantthresh__44c7_s_p5_0, + _vq_quantmap__44c7_s_p5_0, + 3, + 3 +}; + +static static_codebook _44c7_s_p5_0 = { + 4, 81, + _vq_lengthlist__44c7_s_p5_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44c7_s_p5_0, + NULL, + &_vq_auxt__44c7_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p5_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c7_s_p5_1[] = { + 3, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8,11, 4, 4, 6, 6, + 7, 7, 8, 8, 9, 9,11, 4, 4, 6, 6, 7, 7, 8, 8, 9, + 9,12, 5, 5, 6, 6, 7, 7, 9, 9, 9, 9,12,12,12, 6, + 6, 7, 7, 9, 9, 9, 9,11,11,11, 7, 7, 7, 7, 8, 8, + 9, 9,11,11,11, 7, 7, 7, 7, 8, 8, 9, 9,11,11,11, + 7, 7, 8, 8, 8, 8, 9, 9,11,11,11,11,11, 8, 8, 8, + 8, 8, 9,11,11,11,11,11, 8, 8, 8, 8, 8, 8,11,11, + 11,11,11, 7, 7, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44c7_s_p5_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c7_s_p5_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p5_1 = { + _vq_quantthresh__44c7_s_p5_1, + _vq_quantmap__44c7_s_p5_1, + 11, + 11 +}; + +static static_codebook _44c7_s_p5_1 = { + 2, 121, + _vq_lengthlist__44c7_s_p5_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c7_s_p5_1, + NULL, + &_vq_auxt__44c7_s_p5_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c7_s_p6_0[] = { + 1, 4, 4, 6, 6, 7, 7, 8, 7, 9, 8,10,10, 6, 5, 5, + 7, 7, 8, 8, 9, 9, 9,10,11,11, 7, 5, 5, 7, 7, 8, + 8, 9, 9,10,10,11,11, 0, 7, 7, 7, 7, 9, 8, 9, 9, + 10,10,11,11, 0, 8, 8, 7, 7, 8, 9, 9, 9,10,10,11, + 11, 0,11,11, 9, 9,10,10,11,10,11,11,12,12, 0,12, + 12, 9, 9,10,10,11,11,11,11,12,12, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static float _vq_quantthresh__44c7_s_p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44c7_s_p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p6_0 = { + _vq_quantthresh__44c7_s_p6_0, + _vq_quantmap__44c7_s_p6_0, + 13, + 13 +}; + +static static_codebook _44c7_s_p6_0 = { + 2, 169, + _vq_lengthlist__44c7_s_p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44c7_s_p6_0, + NULL, + &_vq_auxt__44c7_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c7_s_p6_1[] = { + 3, 4, 4, 5, 5, 5, 4, 4, 5, 5, 5, 4, 4, 5, 5, 6, + 5, 5, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__44c7_s_p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c7_s_p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p6_1 = { + _vq_quantthresh__44c7_s_p6_1, + _vq_quantmap__44c7_s_p6_1, + 5, + 5 +}; + +static static_codebook _44c7_s_p6_1 = { + 2, 25, + _vq_lengthlist__44c7_s_p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c7_s_p6_1, + NULL, + &_vq_auxt__44c7_s_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c7_s_p7_0[] = { + 1, 4, 4, 6, 6, 7, 8, 9, 9,10,10,12,11, 6, 5, 5, + 7, 7, 8, 8, 9,10,11,11,12,12, 7, 5, 5, 7, 7, 8, + 8,10,10,11,11,12,12,20, 7, 7, 7, 7, 8, 9,10,10, + 11,11,12,13,20, 7, 7, 7, 7, 9, 9,10,10,11,12,13, + 13,20,11,11, 8, 8, 9, 9,11,11,12,12,13,13,20,11, + 11, 8, 8, 9, 9,11,11,12,12,13,13,20,20,20,10,10, + 10,10,12,12,13,13,13,13,20,20,20,10,10,10,10,12, + 12,13,13,13,14,20,20,20,14,14,11,11,12,12,13,13, + 14,14,20,20,20,14,14,11,11,12,12,13,13,14,14,20, + 20,20,20,19,13,13,13,13,14,14,15,14,19,19,19,19, + 19,13,13,13,13,14,14,15,15, +}; + +static float _vq_quantthresh__44c7_s_p7_0[] = { + -60.5, -49.5, -38.5, -27.5, -16.5, -5.5, 5.5, 16.5, + 27.5, 38.5, 49.5, 60.5, +}; + +static long _vq_quantmap__44c7_s_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p7_0 = { + _vq_quantthresh__44c7_s_p7_0, + _vq_quantmap__44c7_s_p7_0, + 13, + 13 +}; + +static static_codebook _44c7_s_p7_0 = { + 2, 169, + _vq_lengthlist__44c7_s_p7_0, + 1, -523206656, 1618345984, 4, 0, + _vq_quantlist__44c7_s_p7_0, + NULL, + &_vq_auxt__44c7_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c7_s_p7_1[] = { + 4, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 8, 6, 6, 7, 7, + 7, 7, 7, 7, 7, 7, 8, 6, 6, 6, 7, 7, 7, 7, 7, 7, + 7, 8, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 7, + 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, + 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 7, 7, 7, + 7, 7, 7, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 8, 8, + 8, 8, 8, 7, 7, 7, 7, 7, 7, +}; + +static float _vq_quantthresh__44c7_s_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c7_s_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p7_1 = { + _vq_quantthresh__44c7_s_p7_1, + _vq_quantmap__44c7_s_p7_1, + 11, + 11 +}; + +static static_codebook _44c7_s_p7_1 = { + 2, 121, + _vq_lengthlist__44c7_s_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c7_s_p7_1, + NULL, + &_vq_auxt__44c7_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p8_0[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44c7_s_p8_0[] = { + 1, 4, 4, 7, 7, 8, 8, 8, 7, 9, 8, 9, 9,10,10, 6, + 5, 5, 7, 7, 9, 9, 8, 8,10, 9,11,10,12,11, 6, 5, + 5, 8, 7, 9, 9, 8, 8,10,10,11,11,12,11,19, 8, 8, + 8, 8,10,10, 9, 9,10,10,11,11,12,11,19, 8, 8, 8, + 8,10,10, 9, 9,10,10,11,11,12,12,19,12,12, 9, 9, + 10,10, 9,10,10,10,11,11,12,12,19,12,12, 9, 9,10, + 10,10,10,10,10,12,12,12,12,19,19,19, 9, 9, 9, 9, + 11,10,11,11,12,11,13,13,19,19,19, 9, 9, 9, 9,11, + 10,11,11,11,12,13,13,19,19,19,13,13,10,10,11,11, + 12,12,12,12,13,12,19,19,19,14,13,10,10,11,11,12, + 12,12,13,13,13,19,19,19,19,19,12,12,12,11,12,13, + 14,13,13,13,19,19,19,19,19,12,12,12,11,12,12,13, + 14,13,14,19,19,19,19,19,16,16,12,13,12,13,13,14, + 15,14,19,18,18,18,18,16,15,12,11,12,11,14,12,14, + 14, +}; + +static float _vq_quantthresh__44c7_s_p8_0[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__44c7_s_p8_0[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p8_0 = { + _vq_quantthresh__44c7_s_p8_0, + _vq_quantmap__44c7_s_p8_0, + 15, + 15 +}; + +static static_codebook _44c7_s_p8_0 = { + 2, 225, + _vq_lengthlist__44c7_s_p8_0, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__44c7_s_p8_0, + NULL, + &_vq_auxt__44c7_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p8_1[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__44c7_s_p8_1[] = { + 3, 5, 5, 7, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8,10, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 6, 6, 7, 7, 8, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, + 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9,10,10,10, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10,10, 8, 8, 8, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10, + 10, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9,10,10,10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10,11,10,10,10, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9,10, 9, 9,10, 9, 9,10,11,10, + 11,10, 9, 9, 9, 9, 9, 9, 9,10,10,10, 9,10, 9, 9, + 9, 9,11,10,11,10,10, 9, 9, 9, 9, 9, 9,10, 9, 9, + 10, 9, 9,10, 9, 9,10,11,10,10,11,10, 9, 9, 9, 9, + 9,10,10, 9,10,10,10,10, 9,10,10,10,10,10,10,11, + 11,11,10, 9, 9, 9,10,10,10,10,10,10,10,10,10,10, + 10,10,10,11,11,10,10,10,10,10,10,10,10,10,10,10, + 10, 9,10,10, 9,10,11,11,10,11,10,11,10, 9,10,10, + 9,10,10,10,10,10,10,10,10,10,10,11,11,11,11,10, + 11,11,10,10,10,10,10,10, 9,10, 9,10,10, 9,10, 9, + 10,10,10,11,10,11,10,11,11,10,10,10,10,10,10, 9, + 10,10,10,10,10,10,10,11,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,11,10,11, + 11,10,10,10,10, 9, 9,10,10, 9, 9,10, 9,10,10,10, + 10,11,11,10,10,10,10,10,10,10, 9, 9,10,10,10, 9, + 9,10,10,10,10,10,11,10,11,10,10,10,10,10,10, 9, + 10,10,10,10,10,10,10,10,10, +}; + +static float _vq_quantthresh__44c7_s_p8_1[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__44c7_s_p8_1[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p8_1 = { + _vq_quantthresh__44c7_s_p8_1, + _vq_quantmap__44c7_s_p8_1, + 21, + 21 +}; + +static static_codebook _44c7_s_p8_1 = { + 2, 441, + _vq_lengthlist__44c7_s_p8_1, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__44c7_s_p8_1, + NULL, + &_vq_auxt__44c7_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p9_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c7_s_p9_0[] = { + 1, 3, 3,11,11,11,11,11,11,11,11,11,11, 4, 6, 6, + 11,11,11,11,11,11,11,11,11,11, 4, 7, 7,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11, +}; + +static float _vq_quantthresh__44c7_s_p9_0[] = { + -3503.5, -2866.5, -2229.5, -1592.5, -955.5, -318.5, 318.5, 955.5, + 1592.5, 2229.5, 2866.5, 3503.5, +}; + +static long _vq_quantmap__44c7_s_p9_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p9_0 = { + _vq_quantthresh__44c7_s_p9_0, + _vq_quantmap__44c7_s_p9_0, + 13, + 13 +}; + +static static_codebook _44c7_s_p9_0 = { + 2, 169, + _vq_lengthlist__44c7_s_p9_0, + 1, -511845376, 1630791680, 4, 0, + _vq_quantlist__44c7_s_p9_0, + NULL, + &_vq_auxt__44c7_s_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p9_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c7_s_p9_1[] = { + 1, 4, 4, 7, 7, 7, 7, 7, 6, 8, 8, 8, 8, 6, 6, 6, + 8, 8, 9, 8, 8, 7, 9, 8,11,10, 5, 6, 6, 8, 8, 9, + 8, 8, 8,10, 9,11,11,16, 8, 8, 9, 8, 9, 9, 9, 8, + 10, 9,11,10,16, 8, 8, 9, 9,10,10, 9, 9,10,10,11, + 11,16,13,13, 9, 9,10,10, 9,10,11,11,12,11,16,13, + 13, 9, 8,10, 9,10,10,10,10,11,11,16,14,16, 8, 9, + 9, 9,11,10,11,11,12,11,16,16,16, 9, 7,10, 7,11, + 10,11,11,12,11,16,16,16,12,12, 9,10,11,11,12,11, + 12,12,16,16,16,12,10,10, 7,11, 8,12,11,12,12,16, + 16,15,16,16,11,12,10,10,12,11,12,12,16,16,16,15, + 15,11,11,10,10,12,12,12,12, +}; + +static float _vq_quantthresh__44c7_s_p9_1[] = { + -269.5, -220.5, -171.5, -122.5, -73.5, -24.5, 24.5, 73.5, + 122.5, 171.5, 220.5, 269.5, +}; + +static long _vq_quantmap__44c7_s_p9_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p9_1 = { + _vq_quantthresh__44c7_s_p9_1, + _vq_quantmap__44c7_s_p9_1, + 13, + 13 +}; + +static static_codebook _44c7_s_p9_1 = { + 2, 169, + _vq_lengthlist__44c7_s_p9_1, + 1, -518889472, 1622704128, 4, 0, + _vq_quantlist__44c7_s_p9_1, + NULL, + &_vq_auxt__44c7_s_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c7_s_p9_2[] = { + 24, + 23, + 25, + 22, + 26, + 21, + 27, + 20, + 28, + 19, + 29, + 18, + 30, + 17, + 31, + 16, + 32, + 15, + 33, + 14, + 34, + 13, + 35, + 12, + 36, + 11, + 37, + 10, + 38, + 9, + 39, + 8, + 40, + 7, + 41, + 6, + 42, + 5, + 43, + 4, + 44, + 3, + 45, + 2, + 46, + 1, + 47, + 0, + 48, +}; + +static long _vq_lengthlist__44c7_s_p9_2[] = { + 2, 4, 3, 4, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, +}; + +static float _vq_quantthresh__44c7_s_p9_2[] = { + -23.5, -22.5, -21.5, -20.5, -19.5, -18.5, -17.5, -16.5, + -15.5, -14.5, -13.5, -12.5, -11.5, -10.5, -9.5, -8.5, + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, + 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, + 16.5, 17.5, 18.5, 19.5, 20.5, 21.5, 22.5, 23.5, +}; + +static long _vq_quantmap__44c7_s_p9_2[] = { + 47, 45, 43, 41, 39, 37, 35, 33, + 31, 29, 27, 25, 23, 21, 19, 17, + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, + 48, +}; + +static encode_aux_threshmatch _vq_auxt__44c7_s_p9_2 = { + _vq_quantthresh__44c7_s_p9_2, + _vq_quantmap__44c7_s_p9_2, + 49, + 49 +}; + +static static_codebook _44c7_s_p9_2 = { + 1, 49, + _vq_lengthlist__44c7_s_p9_2, + 1, -526909440, 1611661312, 6, 0, + _vq_quantlist__44c7_s_p9_2, + NULL, + &_vq_auxt__44c7_s_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44c7_s_short[] = { + 4,11,12,14,15,15,17,17,18,18, 5, 6, 6, 8, 9,10, + 13,17,18,19, 7, 5, 4, 6, 8, 9,11,15,19,19, 8, 6, + 5, 5, 6, 7,11,14,16,17, 9, 7, 7, 6, 7, 7,10,13, + 15,19,10, 8, 7, 6, 7, 6, 7, 9,14,16,12,10, 9, 7, + 7, 6, 4, 5,10,15,14,13,11, 7, 6, 6, 4, 2, 7,13, + 16,16,15, 9, 8, 8, 8, 6, 9,13,19,19,17,12,11,10, + 10, 9,11,14, +}; + +static static_codebook _huff_book__44c7_s_short = { + 2, 100, + _huff_lengthlist__44c7_s_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44c8_s_long[] = { + 3, 8,12,13,14,14,14,13,14,14, 6, 4, 5, 8,10,10, + 11,11,14,13, 9, 5, 4, 5, 7, 8, 9,10,13,13,12, 7, + 5, 4, 5, 6, 8, 9,12,13,13, 9, 6, 5, 5, 5, 7, 9, + 11,14,12,10, 7, 6, 5, 4, 6, 7,10,11,12,11, 9, 8, + 7, 5, 5, 6,10,10,13,12,10, 9, 8, 6, 6, 5, 8,10, + 14,13,12,12,11,10, 9, 7, 8,10,12,13,14,14,13,12, + 11, 9, 9,10, +}; + +static static_codebook _huff_book__44c8_s_long = { + 2, 100, + _huff_lengthlist__44c8_s_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c8_s_p1_0[] = { + 1, 5, 5, 0, 5, 5, 0, 5, 5, 5, 7, 7, 0, 9, 8, 0, + 9, 8, 6, 7, 7, 0, 8, 9, 0, 8, 9, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 9, 8, 0, 8, 8, 0, 8, 8, 5, 8, 9, + 0, 8, 8, 0, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, + 9, 8, 0, 8, 8, 0, 8, 8, 5, 8, 9, 0, 8, 8, 0, 8, + 8, +}; + +static float _vq_quantthresh__44c8_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44c8_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p1_0 = { + _vq_quantthresh__44c8_s_p1_0, + _vq_quantmap__44c8_s_p1_0, + 3, + 3 +}; + +static static_codebook _44c8_s_p1_0 = { + 4, 81, + _vq_lengthlist__44c8_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44c8_s_p1_0, + NULL, + &_vq_auxt__44c8_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c8_s_p2_0[] = { + 3, 5, 5, 8, 8, 0, 5, 5, 8, 8, 0, 5, 5, 8, 8, 0, + 7, 7, 9, 9, 0, 0, 0, 9, 9, 5, 7, 7, 9, 9, 0, 8, + 7,10, 9, 0, 8, 7,10, 9, 0,10,10,11,11, 0, 0, 0, + 11,11, 5, 7, 7, 9, 9, 0, 7, 8, 9,10, 0, 7, 8, 9, + 10, 0,10,10,11,11, 0, 0, 0,11,11, 8, 9, 9,11,10, + 0,11,10,12,11, 0,11,10,12,12, 0,13,13,14,14, 0, + 0, 0,14,13, 8, 9, 9,10,11, 0,10,11,12,12, 0,10, + 11,12,12, 0,13,13,14,14, 0, 0, 0,13,14, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 5, 8, 7,11,10, 0, 7, 7,10,10, + 0, 7, 7,10,10, 0, 9, 9,10,10, 0, 0, 0,11,10, 5, + 7, 8,10,11, 0, 7, 7,10,10, 0, 7, 7,10,10, 0, 9, + 9,10,10, 0, 0, 0,10,10, 8,10, 9,12,12, 0,10,10, + 12,11, 0,10,10,12,12, 0,12,12,13,12, 0, 0, 0,13, + 12, 8, 9,10,12,12, 0,10,10,11,12, 0,10,10,11,12, + 0,12,12,13,13, 0, 0, 0,12,13, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 6, 8, 7,11,10, 0, 7, 7,10,10, 0, 7, 7, + 10,10, 0, 9, 9,10,11, 0, 0, 0,10,10, 6, 7, 8,10, + 11, 0, 7, 7,10,10, 0, 7, 7,10,10, 0, 9, 9,10,10, + 0, 0, 0,10,10, 9,10, 9,12,12, 0,10,10,12,12, 0, + 10,10,12,11, 0,12,12,13,13, 0, 0, 0,13,12, 8, 9, + 10,12,12, 0,10,10,12,12, 0,10,10,11,12, 0,12,12, + 13,13, 0, 0, 0,12,13, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7,10,10,13,13, 0, 9, 9,12,12, 0, 9, 9,12,12, 0, + 10,10,12,12, 0, 0, 0,12,12, 7,10,10,13,13, 0, 9, + 9,12,12, 0, 9, 9,12,12, 0,10,10,12,12, 0, 0, 0, + 12,12, 9,11,11,14,13, 0,10,10,13,12, 0,11,10,13, + 12, 0,12,12,13,12, 0, 0, 0,13,13, 9,11,11,13,14, + 0,10,11,12,13, 0,10,11,13,13, 0,12,12,12,13, 0, + 0, 0,13,13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, + 11,11,14,14, 0,10,11,13,13, 0,11,10,13,13, 0,11, + 12,13,13, 0, 0, 0,13,12, 9,11,11,14,14, 0,11,10, + 13,13, 0,10,11,13,13, 0,12,12,13,13, 0, 0, 0,12, + 13, +}; + +static float _vq_quantthresh__44c8_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c8_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p2_0 = { + _vq_quantthresh__44c8_s_p2_0, + _vq_quantmap__44c8_s_p2_0, + 5, + 5 +}; + +static static_codebook _44c8_s_p2_0 = { + 4, 625, + _vq_lengthlist__44c8_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c8_s_p2_0, + NULL, + &_vq_auxt__44c8_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c8_s_p3_0[] = { + 2, 4, 4, 5, 5, 7, 7, 9, 9, 0, 4, 4, 6, 6, 7, 7, + 9, 9, 0, 4, 4, 6, 6, 7, 7, 9, 9, 0, 5, 5, 6, 6, + 8, 8,10,10, 0, 0, 0, 6, 6, 8, 8,10,10, 0, 0, 0, + 7, 7, 9, 9,10,10, 0, 0, 0, 7, 7, 8, 8,10,10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c8_s_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c8_s_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p3_0 = { + _vq_quantthresh__44c8_s_p3_0, + _vq_quantmap__44c8_s_p3_0, + 9, + 9 +}; + +static static_codebook _44c8_s_p3_0 = { + 2, 81, + _vq_lengthlist__44c8_s_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c8_s_p3_0, + NULL, + &_vq_auxt__44c8_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p4_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c8_s_p4_0[] = { + 3, 4, 4, 5, 5, 7, 7, 8, 8, 8, 8, 9, 9,10,10,11, + 11, 0, 4, 4, 6, 6, 7, 7, 8, 8, 9, 8,10,10,11,11, + 11,11, 0, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10,11, + 11,11,11, 0, 6, 5, 6, 6, 7, 7, 9, 9, 9, 9,10,10, + 11,11,12,12, 0, 0, 0, 6, 6, 7, 7, 9, 9, 9, 9,10, + 10,11,11,12,12, 0, 0, 0, 7, 7, 8, 8, 9, 9,10,10, + 11,11,11,12,12,12, 0, 0, 0, 7, 7, 8, 8, 9, 9,10, + 10,11,11,11,12,12,12, 0, 0, 0, 7, 7, 8, 8, 9, 9, + 10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 8, 8, 9, + 9,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c8_s_p4_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c8_s_p4_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p4_0 = { + _vq_quantthresh__44c8_s_p4_0, + _vq_quantmap__44c8_s_p4_0, + 17, + 17 +}; + +static static_codebook _44c8_s_p4_0 = { + 2, 289, + _vq_lengthlist__44c8_s_p4_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c8_s_p4_0, + NULL, + &_vq_auxt__44c8_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p5_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c8_s_p5_0[] = { + 1, 4, 4, 5, 7, 7, 6, 7, 7, 4, 7, 6,10,10,10,10, + 10,10, 4, 6, 6,10,10,10,10, 9,10, 5,10,10, 9,11, + 11,10,11,11, 7,10,10,11,12,12,12,12,12, 7,10,10, + 11,12,12,12,12,12, 6,10,10,10,12,12,10,12,12, 7, + 10,10,11,12,12,12,12,12, 7,10,10,11,12,12,12,12, + 12, +}; + +static float _vq_quantthresh__44c8_s_p5_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44c8_s_p5_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p5_0 = { + _vq_quantthresh__44c8_s_p5_0, + _vq_quantmap__44c8_s_p5_0, + 3, + 3 +}; + +static static_codebook _44c8_s_p5_0 = { + 4, 81, + _vq_lengthlist__44c8_s_p5_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44c8_s_p5_0, + NULL, + &_vq_auxt__44c8_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p5_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c8_s_p5_1[] = { + 3, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8,11, 4, 5, 6, 6, + 7, 7, 8, 8, 8, 8,11, 5, 5, 6, 6, 7, 7, 8, 8, 8, + 9,12, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,12,12,12, 6, + 6, 7, 7, 8, 8, 9, 9,11,11,11, 6, 6, 7, 7, 8, 8, + 8, 8,11,11,11, 6, 6, 7, 7, 8, 8, 8, 8,11,11,11, + 7, 7, 7, 8, 8, 8, 8, 8,11,11,11,11,11, 7, 7, 8, + 8, 8, 8,11,11,11,11,11, 7, 7, 7, 7, 8, 8,11,11, + 11,11,11, 7, 7, 7, 7, 8, 8, +}; + +static float _vq_quantthresh__44c8_s_p5_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c8_s_p5_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p5_1 = { + _vq_quantthresh__44c8_s_p5_1, + _vq_quantmap__44c8_s_p5_1, + 11, + 11 +}; + +static static_codebook _44c8_s_p5_1 = { + 2, 121, + _vq_lengthlist__44c8_s_p5_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c8_s_p5_1, + NULL, + &_vq_auxt__44c8_s_p5_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c8_s_p6_0[] = { + 1, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10, 6, 5, 5, + 7, 7, 8, 8, 9, 9,10,10,11,11, 6, 5, 5, 7, 7, 8, + 8, 9, 9,10,10,11,11, 0, 7, 7, 7, 7, 9, 9,10,10, + 10,10,11,11, 0, 7, 7, 7, 7, 9, 9,10,10,10,10,11, + 11, 0,11,11, 9, 9,10,10,11,11,11,11,12,12, 0,12, + 12, 9, 9,10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static float _vq_quantthresh__44c8_s_p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44c8_s_p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p6_0 = { + _vq_quantthresh__44c8_s_p6_0, + _vq_quantmap__44c8_s_p6_0, + 13, + 13 +}; + +static static_codebook _44c8_s_p6_0 = { + 2, 169, + _vq_lengthlist__44c8_s_p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44c8_s_p6_0, + NULL, + &_vq_auxt__44c8_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c8_s_p6_1[] = { + 3, 4, 4, 5, 5, 5, 4, 4, 5, 5, 5, 4, 4, 5, 5, 6, + 5, 5, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__44c8_s_p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c8_s_p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p6_1 = { + _vq_quantthresh__44c8_s_p6_1, + _vq_quantmap__44c8_s_p6_1, + 5, + 5 +}; + +static static_codebook _44c8_s_p6_1 = { + 2, 25, + _vq_lengthlist__44c8_s_p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c8_s_p6_1, + NULL, + &_vq_auxt__44c8_s_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c8_s_p7_0[] = { + 1, 4, 4, 6, 6, 8, 7, 9, 9,10,10,12,12, 6, 5, 5, + 7, 7, 8, 8,10,10,11,11,12,12, 7, 5, 5, 7, 7, 8, + 8,10,10,11,11,12,12,21, 7, 7, 7, 7, 8, 9,10,10, + 11,11,12,12,21, 7, 7, 7, 7, 9, 9,10,10,12,12,13, + 13,21,11,11, 8, 8, 9, 9,11,11,12,12,13,13,21,11, + 11, 8, 8, 9, 9,11,11,12,12,13,13,21,21,21,10,10, + 10,10,11,11,12,13,13,13,21,21,21,10,10,10,10,11, + 11,13,13,14,13,21,21,21,13,13,11,11,12,12,13,13, + 14,14,21,21,21,14,14,11,11,12,12,13,13,14,14,21, + 21,21,21,20,13,13,13,12,14,14,16,15,20,20,20,20, + 20,13,13,13,13,14,13,15,15, +}; + +static float _vq_quantthresh__44c8_s_p7_0[] = { + -60.5, -49.5, -38.5, -27.5, -16.5, -5.5, 5.5, 16.5, + 27.5, 38.5, 49.5, 60.5, +}; + +static long _vq_quantmap__44c8_s_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p7_0 = { + _vq_quantthresh__44c8_s_p7_0, + _vq_quantmap__44c8_s_p7_0, + 13, + 13 +}; + +static static_codebook _44c8_s_p7_0 = { + 2, 169, + _vq_lengthlist__44c8_s_p7_0, + 1, -523206656, 1618345984, 4, 0, + _vq_quantlist__44c8_s_p7_0, + NULL, + &_vq_auxt__44c8_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c8_s_p7_1[] = { + 4, 5, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 6, 6, 6, 7, + 7, 7, 7, 7, 7, 7, 8, 6, 6, 6, 6, 7, 7, 7, 7, 7, + 7, 8, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 7, + 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, + 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 7, 7, 7, + 7, 7, 7, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 8, 8, + 8, 8, 8, 7, 7, 7, 7, 7, 7, +}; + +static float _vq_quantthresh__44c8_s_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c8_s_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p7_1 = { + _vq_quantthresh__44c8_s_p7_1, + _vq_quantmap__44c8_s_p7_1, + 11, + 11 +}; + +static static_codebook _44c8_s_p7_1 = { + 2, 121, + _vq_lengthlist__44c8_s_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c8_s_p7_1, + NULL, + &_vq_auxt__44c8_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p8_0[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44c8_s_p8_0[] = { + 1, 4, 4, 7, 6, 8, 8, 8, 7, 9, 8,10,10,11,10, 6, + 5, 5, 7, 7, 9, 9, 8, 8,10,10,11,11,12,11, 6, 5, + 5, 7, 7, 9, 9, 9, 9,10,10,11,11,12,12,20, 8, 8, + 8, 8, 9, 9, 9, 9,10,10,11,11,12,12,20, 8, 8, 8, + 8,10, 9, 9, 9,10,10,11,11,12,12,20,12,12, 9, 9, + 10,10,10,10,10,11,12,12,12,12,20,12,12, 9, 9,10, + 10,10,10,11,11,12,12,13,13,20,20,20, 9, 9, 9, 9, + 11,10,11,11,12,12,12,13,20,19,19, 9, 9, 9, 9,11, + 11,11,12,12,12,13,13,19,19,19,13,13,10,10,11,11, + 12,12,13,13,13,13,19,19,19,14,13,11,10,11,11,12, + 12,12,13,13,13,19,19,19,19,19,12,12,12,12,13,13, + 13,13,14,13,19,19,19,19,19,12,12,12,11,12,12,13, + 14,14,14,19,19,19,19,19,16,15,13,12,13,13,13,14, + 14,14,19,19,19,19,19,17,17,13,12,13,11,14,13,15, + 15, +}; + +static float _vq_quantthresh__44c8_s_p8_0[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__44c8_s_p8_0[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p8_0 = { + _vq_quantthresh__44c8_s_p8_0, + _vq_quantmap__44c8_s_p8_0, + 15, + 15 +}; + +static static_codebook _44c8_s_p8_0 = { + 2, 225, + _vq_lengthlist__44c8_s_p8_0, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__44c8_s_p8_0, + NULL, + &_vq_auxt__44c8_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p8_1[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__44c8_s_p8_1[] = { + 4, 5, 5, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8,10, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 6, 6, 7, 7, 8, + 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, + 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9,10,10,10, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10,10, 8, 8, 8, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10, + 10, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9,10,10,10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10,10,10,10,10, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10,10, + 10,10, 9, 9, 9, 9, 9, 9, 9, 9,10, 9, 9, 9, 9, 9, + 9, 9,10,10,10,10,10, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9,10,10,10,10,10, 9, 9, 9, 9, + 9, 9, 9, 9,10,10,10, 9, 9, 9, 9, 9,10,10,10,10, + 10,10,10, 9, 9, 9, 9, 9,10,10,10, 9, 9, 9, 9, 9, + 9,10,10,10,10,10,10,10, 9,10,10, 9,10,10,10,10, + 9,10, 9,10,10, 9,10,10,10,10,10,10,10, 9,10,10, + 10,10,10,10, 9, 9,10,10, 9,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, 9, 9, 9,10, 9, 9, 9, 9, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10, 9, 9, + 10, 9,10, 9,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10, 9, 9,10, 9, 9, 9,10,10,10,10,10,10, + 10,10,10,10,10, 9, 9, 9, 9, 9, 9,10, 9, 9,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10, 9,10, 9, + 9,10, 9, 9,10,10,10,10,10,10,10,10,10,10,10,10, + 10, 9, 9,10,10, 9,10, 9, 9, +}; + +static float _vq_quantthresh__44c8_s_p8_1[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__44c8_s_p8_1[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p8_1 = { + _vq_quantthresh__44c8_s_p8_1, + _vq_quantmap__44c8_s_p8_1, + 21, + 21 +}; + +static static_codebook _44c8_s_p8_1 = { + 2, 441, + _vq_lengthlist__44c8_s_p8_1, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__44c8_s_p8_1, + NULL, + &_vq_auxt__44c8_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p9_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c8_s_p9_0[] = { + 1, 4, 3,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11, 4, 7, 7,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11, 4, 8,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10, +}; + +static float _vq_quantthresh__44c8_s_p9_0[] = { + -6982.5, -6051.5, -5120.5, -4189.5, -3258.5, -2327.5, -1396.5, -465.5, + 465.5, 1396.5, 2327.5, 3258.5, 4189.5, 5120.5, 6051.5, 6982.5, +}; + +static long _vq_quantmap__44c8_s_p9_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p9_0 = { + _vq_quantthresh__44c8_s_p9_0, + _vq_quantmap__44c8_s_p9_0, + 17, + 17 +}; + +static static_codebook _44c8_s_p9_0 = { + 2, 289, + _vq_lengthlist__44c8_s_p9_0, + 1, -509798400, 1631393792, 5, 0, + _vq_quantlist__44c8_s_p9_0, + NULL, + &_vq_auxt__44c8_s_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p9_1[] = { + 9, + 8, + 10, + 7, + 11, + 6, + 12, + 5, + 13, + 4, + 14, + 3, + 15, + 2, + 16, + 1, + 17, + 0, + 18, +}; + +static long _vq_lengthlist__44c8_s_p9_1[] = { + 1, 4, 4, 7, 6, 7, 7, 7, 7, 8, 8, 9, 9,10,10,10, + 10,11,11, 6, 6, 6, 8, 8, 9, 8, 8, 7,10, 8,11,10, + 12,11,12,12,13,13, 5, 5, 6, 8, 8, 9, 9, 8, 8,10, + 9,11,11,12,12,13,13,13,13,17, 8, 8, 9, 9, 9, 9, + 9, 9,10, 9,12,10,12,12,13,12,13,13,17, 9, 8, 9, + 9, 9, 9, 9, 9,10,10,12,12,12,12,13,13,13,13,17, + 13,13, 9, 9,10,10,10,10,11,11,12,11,13,12,13,13, + 14,15,17,13,13, 9, 8,10, 9,10,10,11,11,12,12,14, + 13,15,13,14,15,17,17,17, 9,10, 9,10,11,11,12,12, + 12,12,13,13,14,14,15,15,17,17,17, 9, 8, 9, 8,11, + 11,12,12,12,12,14,13,14,14,14,15,17,17,17,12,14, + 9,10,11,11,12,12,14,13,13,14,15,13,15,15,17,17, + 17,13,11,10, 8,11, 9,13,12,13,13,13,13,13,14,14, + 14,17,17,17,17,17,11,12,11,11,13,13,14,13,15,14, + 13,15,16,15,17,17,17,17,17,11,11,12, 8,13,12,14, + 13,17,14,15,14,15,14,17,17,17,17,17,15,15,12,12, + 12,12,13,14,14,14,15,14,17,14,17,17,17,17,17,16, + 17,12,12,13,12,13,13,14,14,14,14,14,14,17,17,17, + 17,17,17,17,14,14,13,12,13,13,15,15,14,13,15,17, + 17,17,17,17,17,17,17,13,14,13,13,13,13,14,15,15, + 15,14,15,17,17,17,17,17,17,17,16,15,13,14,13,13, + 14,14,15,14,14,16,17,17,17,17,17,17,17,16,16,13, + 14,13,13,14,14,15,14,15,14, +}; + +static float _vq_quantthresh__44c8_s_p9_1[] = { + -416.5, -367.5, -318.5, -269.5, -220.5, -171.5, -122.5, -73.5, + -24.5, 24.5, 73.5, 122.5, 171.5, 220.5, 269.5, 318.5, + 367.5, 416.5, +}; + +static long _vq_quantmap__44c8_s_p9_1[] = { + 17, 15, 13, 11, 9, 7, 5, 3, + 1, 0, 2, 4, 6, 8, 10, 12, + 14, 16, 18, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p9_1 = { + _vq_quantthresh__44c8_s_p9_1, + _vq_quantmap__44c8_s_p9_1, + 19, + 19 +}; + +static static_codebook _44c8_s_p9_1 = { + 2, 361, + _vq_lengthlist__44c8_s_p9_1, + 1, -518287360, 1622704128, 5, 0, + _vq_quantlist__44c8_s_p9_1, + NULL, + &_vq_auxt__44c8_s_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c8_s_p9_2[] = { + 24, + 23, + 25, + 22, + 26, + 21, + 27, + 20, + 28, + 19, + 29, + 18, + 30, + 17, + 31, + 16, + 32, + 15, + 33, + 14, + 34, + 13, + 35, + 12, + 36, + 11, + 37, + 10, + 38, + 9, + 39, + 8, + 40, + 7, + 41, + 6, + 42, + 5, + 43, + 4, + 44, + 3, + 45, + 2, + 46, + 1, + 47, + 0, + 48, +}; + +static long _vq_lengthlist__44c8_s_p9_2[] = { + 2, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, +}; + +static float _vq_quantthresh__44c8_s_p9_2[] = { + -23.5, -22.5, -21.5, -20.5, -19.5, -18.5, -17.5, -16.5, + -15.5, -14.5, -13.5, -12.5, -11.5, -10.5, -9.5, -8.5, + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, + 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, + 16.5, 17.5, 18.5, 19.5, 20.5, 21.5, 22.5, 23.5, +}; + +static long _vq_quantmap__44c8_s_p9_2[] = { + 47, 45, 43, 41, 39, 37, 35, 33, + 31, 29, 27, 25, 23, 21, 19, 17, + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, + 48, +}; + +static encode_aux_threshmatch _vq_auxt__44c8_s_p9_2 = { + _vq_quantthresh__44c8_s_p9_2, + _vq_quantmap__44c8_s_p9_2, + 49, + 49 +}; + +static static_codebook _44c8_s_p9_2 = { + 1, 49, + _vq_lengthlist__44c8_s_p9_2, + 1, -526909440, 1611661312, 6, 0, + _vq_quantlist__44c8_s_p9_2, + NULL, + &_vq_auxt__44c8_s_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44c8_s_short[] = { + 4,11,13,14,15,15,18,17,19,17, 5, 6, 8, 9,10,10, + 12,15,19,19, 6, 6, 6, 6, 8, 8,11,14,18,19, 8, 6, + 5, 4, 6, 7,10,13,16,17, 9, 7, 6, 5, 6, 7, 9,12, + 15,19,10, 8, 7, 6, 6, 6, 7, 9,13,15,12,10, 9, 8, + 7, 6, 4, 5,10,15,13,13,11, 8, 6, 6, 4, 2, 7,12, + 17,15,16,10, 8, 8, 7, 6, 9,12,19,18,17,13,11,10, + 10, 9,11,14, +}; + +static static_codebook _huff_book__44c8_s_short = { + 2, 100, + _huff_lengthlist__44c8_s_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44c9_s_long[] = { + 3, 8,12,14,15,15,15,13,15,15, 6, 5, 8,10,12,12, + 13,12,14,13,10, 6, 5, 6, 8, 9,11,11,13,13,13, 8, + 5, 4, 5, 6, 8,10,11,13,14,10, 7, 5, 4, 5, 7, 9, + 11,12,13,11, 8, 6, 5, 4, 5, 7, 9,11,12,11,10, 8, + 7, 5, 4, 5, 9,10,13,13,11,10, 8, 6, 5, 4, 7, 9, + 15,14,13,12,10, 9, 8, 7, 8, 9,12,12,14,13,12,11, + 10, 9, 8, 9, +}; + +static static_codebook _huff_book__44c9_s_long = { + 2, 100, + _huff_lengthlist__44c9_s_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c9_s_p1_0[] = { + 1, 5, 5, 0, 5, 5, 0, 5, 5, 6, 8, 8, 0, 9, 8, 0, + 9, 8, 6, 8, 8, 0, 8, 9, 0, 8, 9, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 8, 8, 0, 7, 7, 0, 8, 8, 5, 8, 8, + 0, 7, 8, 0, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, + 9, 8, 0, 8, 8, 0, 7, 7, 5, 8, 9, 0, 8, 8, 0, 7, + 7, +}; + +static float _vq_quantthresh__44c9_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44c9_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p1_0 = { + _vq_quantthresh__44c9_s_p1_0, + _vq_quantmap__44c9_s_p1_0, + 3, + 3 +}; + +static static_codebook _44c9_s_p1_0 = { + 4, 81, + _vq_lengthlist__44c9_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44c9_s_p1_0, + NULL, + &_vq_auxt__44c9_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c9_s_p2_0[] = { + 3, 5, 5, 8, 8, 0, 5, 5, 8, 8, 0, 5, 5, 8, 8, 0, + 7, 7, 9, 9, 0, 0, 0, 9, 9, 6, 7, 7, 9, 8, 0, 8, + 8, 9, 9, 0, 8, 7, 9, 9, 0, 9,10,10,10, 0, 0, 0, + 11,10, 6, 7, 7, 8, 9, 0, 8, 8, 9, 9, 0, 7, 8, 9, + 9, 0,10, 9,11,10, 0, 0, 0,10,10, 8, 9, 8,10,10, + 0,10,10,12,11, 0,10,10,11,11, 0,12,13,13,13, 0, + 0, 0,13,12, 8, 8, 9,10,10, 0,10,10,11,12, 0,10, + 10,11,11, 0,13,12,13,13, 0, 0, 0,13,13, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 6, 8, 7,10,10, 0, 7, 7,10, 9, + 0, 7, 7,10,10, 0, 9, 9,10,10, 0, 0, 0,10,10, 6, + 7, 8,10,10, 0, 7, 7, 9,10, 0, 7, 7,10,10, 0, 9, + 9,10,10, 0, 0, 0,10,10, 8, 9, 9,11,11, 0,10,10, + 11,11, 0,10,10,11,11, 0,12,12,12,12, 0, 0, 0,12, + 12, 8, 9,10,11,11, 0, 9,10,11,11, 0,10,10,11,11, + 0,12,12,12,12, 0, 0, 0,12,12, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 8, 7,10,10, 0, 7, 7,10,10, 0, 7, 7, + 10, 9, 0, 9, 9,10,10, 0, 0, 0,10,10, 6, 7, 8,10, + 10, 0, 7, 7,10,10, 0, 7, 7, 9,10, 0, 9, 9,10,10, + 0, 0, 0,10,10, 8,10, 9,12,11, 0,10,10,12,11, 0, + 10, 9,11,11, 0,11,12,12,12, 0, 0, 0,12,12, 8, 9, + 10,11,12, 0,10,10,11,11, 0, 9,10,11,11, 0,12,11, + 12,12, 0, 0, 0,12,12, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7,10, 9,12,12, 0, 9, 9,12,11, 0, 9, 9,11,11, 0, + 10,10,12,11, 0, 0, 0,11,12, 7, 9,10,12,12, 0, 9, + 9,11,12, 0, 9, 9,11,11, 0,10,10,11,12, 0, 0, 0, + 11,11, 9,11,10,13,12, 0,10,10,12,12, 0,10,10,12, + 12, 0,11,11,12,12, 0, 0, 0,13,12, 9,10,11,12,13, + 0,10,10,12,12, 0,10,10,12,12, 0,11,12,12,12, 0, + 0, 0,12,13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, + 11,10,13,13, 0,10,10,12,12, 0,10,10,12,12, 0,11, + 12,12,12, 0, 0, 0,12,12, 9,10,11,13,13, 0,10,10, + 12,12, 0,10,10,12,12, 0,12,11,13,12, 0, 0, 0,12, + 12, +}; + +static float _vq_quantthresh__44c9_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c9_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p2_0 = { + _vq_quantthresh__44c9_s_p2_0, + _vq_quantmap__44c9_s_p2_0, + 5, + 5 +}; + +static static_codebook _44c9_s_p2_0 = { + 4, 625, + _vq_lengthlist__44c9_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c9_s_p2_0, + NULL, + &_vq_auxt__44c9_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c9_s_p3_0[] = { + 3, 4, 4, 5, 5, 6, 6, 8, 8, 0, 4, 4, 5, 5, 6, 7, + 8, 8, 0, 4, 4, 5, 5, 7, 7, 8, 8, 0, 5, 5, 6, 6, + 7, 7, 9, 9, 0, 0, 0, 6, 6, 7, 7, 9, 9, 0, 0, 0, + 7, 7, 8, 8, 9, 9, 0, 0, 0, 7, 7, 8, 8, 9, 9, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c9_s_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c9_s_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p3_0 = { + _vq_quantthresh__44c9_s_p3_0, + _vq_quantmap__44c9_s_p3_0, + 9, + 9 +}; + +static static_codebook _44c9_s_p3_0 = { + 2, 81, + _vq_lengthlist__44c9_s_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c9_s_p3_0, + NULL, + &_vq_auxt__44c9_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p4_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c9_s_p4_0[] = { + 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,10,10,10, + 10, 0, 5, 4, 5, 5, 7, 7, 8, 8, 8, 8, 9, 9,10,10, + 11,11, 0, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10, + 10,11,11, 0, 6, 5, 6, 6, 7, 7, 8, 8, 9, 9,10,10, + 11,11,11,12, 0, 0, 0, 6, 6, 7, 7, 8, 8, 9, 9,10, + 10,11,11,12,12, 0, 0, 0, 7, 7, 7, 7, 9, 9, 9, 9, + 10,10,11,11,12,12, 0, 0, 0, 7, 7, 7, 8, 9, 9, 9, + 9,10,10,11,11,12,12, 0, 0, 0, 7, 7, 8, 8, 9, 9, + 10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 8, 8, 9, + 9,10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c9_s_p4_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c9_s_p4_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p4_0 = { + _vq_quantthresh__44c9_s_p4_0, + _vq_quantmap__44c9_s_p4_0, + 17, + 17 +}; + +static static_codebook _44c9_s_p4_0 = { + 2, 289, + _vq_lengthlist__44c9_s_p4_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c9_s_p4_0, + NULL, + &_vq_auxt__44c9_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p5_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c9_s_p5_0[] = { + 1, 4, 4, 5, 7, 7, 6, 7, 7, 4, 7, 6, 9,10,10,10, + 10, 9, 4, 6, 7, 9,10,10,10, 9,10, 5, 9, 9, 9,11, + 11,10,11,11, 7,10, 9,11,12,11,12,12,12, 7, 9,10, + 11,11,12,12,12,12, 6,10,10,10,12,12,10,12,11, 7, + 10,10,11,12,12,11,12,12, 7,10,10,11,12,12,12,12, + 12, +}; + +static float _vq_quantthresh__44c9_s_p5_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44c9_s_p5_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p5_0 = { + _vq_quantthresh__44c9_s_p5_0, + _vq_quantmap__44c9_s_p5_0, + 3, + 3 +}; + +static static_codebook _44c9_s_p5_0 = { + 4, 81, + _vq_lengthlist__44c9_s_p5_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44c9_s_p5_0, + NULL, + &_vq_auxt__44c9_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p5_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c9_s_p5_1[] = { + 4, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7,11, 5, 5, 6, 6, + 7, 7, 7, 7, 8, 8,11, 5, 5, 6, 6, 7, 7, 7, 7, 8, + 8,11, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8,11,11,11, 6, + 6, 7, 7, 7, 8, 8, 8,11,11,11, 6, 6, 7, 7, 7, 8, + 8, 8,11,11,11, 6, 6, 7, 7, 7, 7, 8, 8,11,11,11, + 7, 7, 7, 7, 7, 7, 8, 8,11,11,11,10,10, 7, 7, 7, + 7, 8, 8,11,11,11,11,11, 7, 7, 7, 7, 7, 7,11,11, + 11,11,11, 7, 7, 7, 7, 7, 7, +}; + +static float _vq_quantthresh__44c9_s_p5_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c9_s_p5_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p5_1 = { + _vq_quantthresh__44c9_s_p5_1, + _vq_quantmap__44c9_s_p5_1, + 11, + 11 +}; + +static static_codebook _44c9_s_p5_1 = { + 2, 121, + _vq_lengthlist__44c9_s_p5_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c9_s_p5_1, + NULL, + &_vq_auxt__44c9_s_p5_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c9_s_p6_0[] = { + 2, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 5, 4, 4, + 6, 6, 8, 8, 9, 9, 9, 9,10,10, 6, 4, 4, 6, 6, 8, + 8, 9, 9, 9, 9,10,10, 0, 6, 6, 7, 7, 8, 8, 9, 9, + 10,10,11,11, 0, 6, 6, 7, 7, 8, 8, 9, 9,10,10,11, + 11, 0,10,10, 8, 8, 9, 9,10,10,11,11,12,12, 0,11, + 11, 8, 8, 9, 9,10,10,11,11,12,12, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static float _vq_quantthresh__44c9_s_p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44c9_s_p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p6_0 = { + _vq_quantthresh__44c9_s_p6_0, + _vq_quantmap__44c9_s_p6_0, + 13, + 13 +}; + +static static_codebook _44c9_s_p6_0 = { + 2, 169, + _vq_lengthlist__44c9_s_p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44c9_s_p6_0, + NULL, + &_vq_auxt__44c9_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c9_s_p6_1[] = { + 4, 4, 4, 5, 5, 5, 4, 4, 5, 5, 5, 4, 4, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, +}; + +static float _vq_quantthresh__44c9_s_p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c9_s_p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p6_1 = { + _vq_quantthresh__44c9_s_p6_1, + _vq_quantmap__44c9_s_p6_1, + 5, + 5 +}; + +static static_codebook _44c9_s_p6_1 = { + 2, 25, + _vq_lengthlist__44c9_s_p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c9_s_p6_1, + NULL, + &_vq_auxt__44c9_s_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c9_s_p7_0[] = { + 2, 4, 4, 6, 6, 7, 7, 8, 8,10,10,11,11, 6, 4, 4, + 6, 6, 8, 8, 9, 9,10,10,12,12, 6, 4, 5, 6, 6, 8, + 8, 9, 9,10,10,12,12,20, 6, 6, 6, 6, 8, 8, 9,10, + 11,11,12,12,20, 6, 6, 6, 6, 8, 8,10,10,11,11,12, + 12,20,10,10, 7, 7, 9, 9,10,10,11,11,12,12,20,11, + 11, 7, 7, 9, 9,10,10,11,11,12,12,20,20,20, 9, 9, + 9, 9,11,11,12,12,13,13,20,20,20, 9, 9, 9, 9,11, + 11,12,12,13,13,20,20,20,13,13,10,10,11,11,12,13, + 13,13,20,20,20,13,13,10,10,11,11,12,13,13,13,20, + 20,20,20,19,12,12,12,12,13,13,14,15,19,19,19,19, + 19,12,12,12,12,13,13,14,14, +}; + +static float _vq_quantthresh__44c9_s_p7_0[] = { + -60.5, -49.5, -38.5, -27.5, -16.5, -5.5, 5.5, 16.5, + 27.5, 38.5, 49.5, 60.5, +}; + +static long _vq_quantmap__44c9_s_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p7_0 = { + _vq_quantthresh__44c9_s_p7_0, + _vq_quantmap__44c9_s_p7_0, + 13, + 13 +}; + +static static_codebook _44c9_s_p7_0 = { + 2, 169, + _vq_lengthlist__44c9_s_p7_0, + 1, -523206656, 1618345984, 4, 0, + _vq_quantlist__44c9_s_p7_0, + NULL, + &_vq_auxt__44c9_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c9_s_p7_1[] = { + 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 7, 7, 7, 7, 7, + 7, 8, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 6, + 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 7, 7, 7, 7, 7, 7, + 7, 7, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, + 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 7, 7, 7, + 7, 7, 7, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 8, 8, + 8, 8, 8, 7, 7, 7, 7, 7, 7, +}; + +static float _vq_quantthresh__44c9_s_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c9_s_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p7_1 = { + _vq_quantthresh__44c9_s_p7_1, + _vq_quantmap__44c9_s_p7_1, + 11, + 11 +}; + +static static_codebook _44c9_s_p7_1 = { + 2, 121, + _vq_lengthlist__44c9_s_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c9_s_p7_1, + NULL, + &_vq_auxt__44c9_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p8_0[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44c9_s_p8_0[] = { + 1, 4, 4, 7, 6, 8, 8, 8, 8, 9, 9,10,10,11,10, 6, + 5, 5, 7, 7, 9, 9, 8, 9,10,10,11,11,12,12, 6, 5, + 5, 7, 7, 9, 9, 9, 9,10,10,11,11,12,12,21, 7, 8, + 8, 8, 9, 9, 9, 9,10,10,11,11,12,12,21, 8, 8, 8, + 8, 9, 9, 9, 9,10,10,11,11,12,12,21,11,12, 9, 9, + 10,10,10,10,10,11,11,12,12,12,21,12,12, 9, 8,10, + 10,10,10,11,11,12,12,13,13,21,21,21, 9, 9, 9, 9, + 11,11,11,11,12,12,12,13,21,20,20, 9, 9, 9, 9,10, + 11,11,11,12,12,13,13,20,20,20,13,13,10,10,11,11, + 12,12,13,13,13,13,20,20,20,13,13,10,10,11,11,12, + 12,13,13,13,13,20,20,20,20,20,12,12,12,12,12,12, + 13,13,14,14,20,20,20,20,20,12,12,12,11,13,12,13, + 13,14,14,20,20,20,20,20,15,16,13,12,13,13,14,13, + 14,14,20,20,20,20,20,16,15,12,12,13,12,14,13,14, + 14, +}; + +static float _vq_quantthresh__44c9_s_p8_0[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__44c9_s_p8_0[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p8_0 = { + _vq_quantthresh__44c9_s_p8_0, + _vq_quantmap__44c9_s_p8_0, + 15, + 15 +}; + +static static_codebook _44c9_s_p8_0 = { + 2, 225, + _vq_lengthlist__44c9_s_p8_0, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__44c9_s_p8_0, + NULL, + &_vq_auxt__44c9_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p8_1[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__44c9_s_p8_1[] = { + 4, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8,10, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 6, 6, 7, 7, 8, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, + 7, 7, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9,10,10,10, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10,10, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10, + 10, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9,10,10,10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10,10,10,10,10, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10,10, + 10,10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9,10,10,10,10,10, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9,10,10,10,10,10, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10, 9, 9, 9,10,10,10,10, + 10,10,10, 9, 9, 9, 9, 9, 9,10, 9, 9, 9, 9, 9, 9, + 9,10,10,10,10,10,10,10, 9, 9, 9,10,10,10,10,10, + 9, 9, 9, 9, 9, 9,10,10,10,10,10,10,10, 9, 9,10, + 9,10, 9, 9, 9, 9, 9, 9, 9, 9,10,10,10,10,10,10, + 10,10,10,10, 9, 9,10,10, 9, 9, 9, 9, 9, 9, 9, 9, + 10,10,10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, + 9, 9, 9, 9, 9,10,10,10,10,10,10,10,10,10,10,10, + 10,10, 9, 9,10, 9, 9, 9, 9, 9,10,10,10,10,10,10, + 10,10,10,10,10, 9, 9,10,10, 9, 9,10, 9, 9, 9,10, + 10,10,10,10,10,10,10,10,10,10, 9, 9,10, 9, 9, 9, + 9, 9, 9, 9,10,10,10,10,10,10,10,10,10,10,10, 9, + 9, 9, 9,10, 9, 9, 9, 9, 9, +}; + +static float _vq_quantthresh__44c9_s_p8_1[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__44c9_s_p8_1[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p8_1 = { + _vq_quantthresh__44c9_s_p8_1, + _vq_quantmap__44c9_s_p8_1, + 21, + 21 +}; + +static static_codebook _44c9_s_p8_1 = { + 2, 441, + _vq_lengthlist__44c9_s_p8_1, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__44c9_s_p8_1, + NULL, + &_vq_auxt__44c9_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p9_0[] = { + 9, + 8, + 10, + 7, + 11, + 6, + 12, + 5, + 13, + 4, + 14, + 3, + 15, + 2, + 16, + 1, + 17, + 0, + 18, +}; + +static long _vq_lengthlist__44c9_s_p9_0[] = { + 1, 4, 3,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12, 4, 5, 6,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12, 4, 6, 6,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,11,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11, +}; + +static float _vq_quantthresh__44c9_s_p9_0[] = { + -7913.5, -6982.5, -6051.5, -5120.5, -4189.5, -3258.5, -2327.5, -1396.5, + -465.5, 465.5, 1396.5, 2327.5, 3258.5, 4189.5, 5120.5, 6051.5, + 6982.5, 7913.5, +}; + +static long _vq_quantmap__44c9_s_p9_0[] = { + 17, 15, 13, 11, 9, 7, 5, 3, + 1, 0, 2, 4, 6, 8, 10, 12, + 14, 16, 18, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p9_0 = { + _vq_quantthresh__44c9_s_p9_0, + _vq_quantmap__44c9_s_p9_0, + 19, + 19 +}; + +static static_codebook _44c9_s_p9_0 = { + 2, 361, + _vq_lengthlist__44c9_s_p9_0, + 1, -508535424, 1631393792, 5, 0, + _vq_quantlist__44c9_s_p9_0, + NULL, + &_vq_auxt__44c9_s_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p9_1[] = { + 9, + 8, + 10, + 7, + 11, + 6, + 12, + 5, + 13, + 4, + 14, + 3, + 15, + 2, + 16, + 1, + 17, + 0, + 18, +}; + +static long _vq_lengthlist__44c9_s_p9_1[] = { + 1, 4, 4, 7, 7, 7, 7, 8, 7, 9, 8, 9, 9,10,10,11, + 11,11,11, 6, 5, 5, 8, 8, 9, 9, 9, 8,10, 9,11,10, + 12,12,13,12,13,13, 5, 5, 5, 8, 8, 9, 9, 9, 9,10, + 10,11,11,12,12,13,12,13,13,17, 8, 8, 9, 9, 9, 9, + 9, 9,10,10,12,11,13,12,13,13,13,13,18, 8, 8, 9, + 9, 9, 9, 9, 9,11,11,12,12,13,13,13,13,13,13,17, + 13,12, 9, 9,10,10,10,10,11,11,12,12,12,13,13,13, + 14,14,18,13,12, 9, 9,10,10,10,10,11,11,12,12,13, + 13,13,14,14,14,17,18,18,10,10,10,10,11,11,11,12, + 12,12,14,13,14,13,13,14,18,18,18,10, 9,10, 9,11, + 11,12,12,12,12,13,13,15,14,14,14,18,18,16,13,14, + 10,11,11,11,12,13,13,13,13,14,13,13,14,14,18,18, + 18,14,12,11, 9,11,10,13,12,13,13,13,14,14,14,13, + 14,18,18,17,18,18,11,12,12,12,13,13,14,13,14,14, + 13,14,14,14,18,18,18,18,17,12,10,12, 9,13,11,13, + 14,14,14,14,14,15,14,18,18,17,17,18,14,15,12,13, + 13,13,14,13,14,14,15,14,15,14,18,17,18,18,18,15, + 15,12,10,14,10,14,14,13,13,14,14,14,14,18,16,18, + 18,18,18,17,14,14,13,14,14,13,13,14,14,14,15,15, + 18,18,18,18,17,17,17,14,14,14,12,14,13,14,14,15, + 14,15,14,18,18,18,18,18,18,18,17,16,13,13,13,14, + 14,14,14,15,16,15,18,18,18,18,18,18,18,17,17,13, + 13,13,13,14,13,14,15,15,15, +}; + +static float _vq_quantthresh__44c9_s_p9_1[] = { + -416.5, -367.5, -318.5, -269.5, -220.5, -171.5, -122.5, -73.5, + -24.5, 24.5, 73.5, 122.5, 171.5, 220.5, 269.5, 318.5, + 367.5, 416.5, +}; + +static long _vq_quantmap__44c9_s_p9_1[] = { + 17, 15, 13, 11, 9, 7, 5, 3, + 1, 0, 2, 4, 6, 8, 10, 12, + 14, 16, 18, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p9_1 = { + _vq_quantthresh__44c9_s_p9_1, + _vq_quantmap__44c9_s_p9_1, + 19, + 19 +}; + +static static_codebook _44c9_s_p9_1 = { + 2, 361, + _vq_lengthlist__44c9_s_p9_1, + 1, -518287360, 1622704128, 5, 0, + _vq_quantlist__44c9_s_p9_1, + NULL, + &_vq_auxt__44c9_s_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c9_s_p9_2[] = { + 24, + 23, + 25, + 22, + 26, + 21, + 27, + 20, + 28, + 19, + 29, + 18, + 30, + 17, + 31, + 16, + 32, + 15, + 33, + 14, + 34, + 13, + 35, + 12, + 36, + 11, + 37, + 10, + 38, + 9, + 39, + 8, + 40, + 7, + 41, + 6, + 42, + 5, + 43, + 4, + 44, + 3, + 45, + 2, + 46, + 1, + 47, + 0, + 48, +}; + +static long _vq_lengthlist__44c9_s_p9_2[] = { + 2, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, +}; + +static float _vq_quantthresh__44c9_s_p9_2[] = { + -23.5, -22.5, -21.5, -20.5, -19.5, -18.5, -17.5, -16.5, + -15.5, -14.5, -13.5, -12.5, -11.5, -10.5, -9.5, -8.5, + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, + 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, + 16.5, 17.5, 18.5, 19.5, 20.5, 21.5, 22.5, 23.5, +}; + +static long _vq_quantmap__44c9_s_p9_2[] = { + 47, 45, 43, 41, 39, 37, 35, 33, + 31, 29, 27, 25, 23, 21, 19, 17, + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, + 48, +}; + +static encode_aux_threshmatch _vq_auxt__44c9_s_p9_2 = { + _vq_quantthresh__44c9_s_p9_2, + _vq_quantmap__44c9_s_p9_2, + 49, + 49 +}; + +static static_codebook _44c9_s_p9_2 = { + 1, 49, + _vq_lengthlist__44c9_s_p9_2, + 1, -526909440, 1611661312, 6, 0, + _vq_quantlist__44c9_s_p9_2, + NULL, + &_vq_auxt__44c9_s_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44c9_s_short[] = { + 5,13,18,16,17,17,19,18,19,19, 5, 7,10,11,12,12, + 13,16,17,18, 6, 6, 7, 7, 9, 9,10,14,17,19, 8, 7, + 6, 5, 6, 7, 9,12,19,17, 8, 7, 7, 6, 5, 6, 8,11, + 15,19, 9, 8, 7, 6, 5, 5, 6, 8,13,15,11,10, 8, 8, + 7, 5, 4, 4,10,14,12,13,11, 9, 7, 6, 4, 2, 6,12, + 18,16,16,13, 8, 7, 7, 5, 8,13,16,17,18,15,11, 9, + 9, 8,10,13, +}; + +static static_codebook _huff_book__44c9_s_short = { + 2, 100, + _huff_lengthlist__44c9_s_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44c0_s_long[] = { + 5, 4, 8, 9, 8, 9,10,12,15, 4, 1, 5, 5, 6, 8,11, + 12,12, 8, 5, 8, 9, 9,11,13,12,12, 9, 5, 8, 5, 7, + 9,12,13,13, 8, 6, 8, 7, 7, 9,11,11,11, 9, 7, 9, + 7, 7, 7, 7,10,12,10,10,11, 9, 8, 7, 7, 9,11,11, + 12,13,12,11, 9, 8, 9,11,13,16,16,15,15,12,10,11, + 12, +}; + +static static_codebook _huff_book__44c0_s_long = { + 2, 81, + _huff_lengthlist__44c0_s_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c0_s_p1_0[] = { + 1, 5, 5, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 8, 7, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, + 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, + 0, 0, 8,10, 9, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, + 0, 0, 0, 9,10,11, 0, 0, 0, 0, 0, 0, 9,11,10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 9,11, 9, 0, 0, 0, 0, 0, 0, 9,10,11, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 9,11,10, 0, + 0, 0, 0, 0, 0, 9, 9,11, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 9,10, 0, 0, 0, 0, 0, 0, 9,10,11, + 0, 0, 0, 0, 0, 0, 9,11,10, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c0_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44c0_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_s_p1_0 = { + _vq_quantthresh__44c0_s_p1_0, + _vq_quantmap__44c0_s_p1_0, + 3, + 3 +}; + +static static_codebook _44c0_s_p1_0 = { + 8, 6561, + _vq_lengthlist__44c0_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44c0_s_p1_0, + NULL, + &_vq_auxt__44c0_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c0_s_p2_0[] = { + 1, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 7, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 5, 6, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 7, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 7, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c0_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c0_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_s_p2_0 = { + _vq_quantthresh__44c0_s_p2_0, + _vq_quantmap__44c0_s_p2_0, + 5, + 5 +}; + +static static_codebook _44c0_s_p2_0 = { + 4, 625, + _vq_lengthlist__44c0_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c0_s_p2_0, + NULL, + &_vq_auxt__44c0_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_s_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c0_s_p3_0[] = { + 1, 3, 2, 8, 7, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, + 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 7, 7, + 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, + 0, 0, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c0_s_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c0_s_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_s_p3_0 = { + _vq_quantthresh__44c0_s_p3_0, + _vq_quantmap__44c0_s_p3_0, + 9, + 9 +}; + +static static_codebook _44c0_s_p3_0 = { + 2, 81, + _vq_lengthlist__44c0_s_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c0_s_p3_0, + NULL, + &_vq_auxt__44c0_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_s_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c0_s_p4_0[] = { + 1, 3, 3, 6, 6, 6, 6, 8, 8, 0, 0, 0, 7, 7, 7, 7, + 9, 9, 0, 0, 0, 7, 7, 7, 7, 9, 9, 0, 0, 0, 7, 7, + 7, 8, 9, 9, 0, 0, 0, 7, 7, 7, 7, 9, 9, 0, 0, 0, + 9, 9, 8, 8,10,10, 0, 0, 0, 8, 9, 8, 8,10,10, 0, + 0, 0,10,10, 9, 9,10,10, 0, 0, 0, 0, 0, 9, 9,10, + 10, +}; + +static float _vq_quantthresh__44c0_s_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c0_s_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_s_p4_0 = { + _vq_quantthresh__44c0_s_p4_0, + _vq_quantmap__44c0_s_p4_0, + 9, + 9 +}; + +static static_codebook _44c0_s_p4_0 = { + 2, 81, + _vq_lengthlist__44c0_s_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c0_s_p4_0, + NULL, + &_vq_auxt__44c0_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_s_p5_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c0_s_p5_0[] = { + 1, 4, 3, 6, 6, 8, 7, 8, 8, 8, 8, 9, 9,10,10,11, + 11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9, 9,10,10,10, + 11,11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10, + 10,11,11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10, + 11,11,11,11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10, + 10,11,11,11,11, 0, 0, 0, 8, 8, 9, 9, 9, 9,10,10, + 10,10,11,11,12,12, 0, 0, 0, 8, 8, 9, 9, 9, 9,10, + 10,10,10,11,11,12,12, 0, 0, 0, 9, 9, 9, 9,10,10, + 10,10,11,11,11,12,12,12, 0, 0, 0, 0, 0, 9, 9,10, + 10,10,10,11,11,11,11,12,12, 0, 0, 0, 0, 0, 9, 9, + 10,10,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, + 9,10,10,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, + 10,10,11,11,11,11,11,12,12,12,13,13, 0, 0, 0, 0, + 0, 0, 0,11,10,11,11,11,11,12,12,13,13, 0, 0, 0, + 0, 0, 0, 0,11,11,12,11,12,12,12,12,13,13, 0, 0, + 0, 0, 0, 0, 0,11,11,11,12,12,12,12,13,13,13, 0, + 0, 0, 0, 0, 0, 0,12,12,12,12,12,13,13,13,14,14, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,12,12,13,13,14, + 14, +}; + +static float _vq_quantthresh__44c0_s_p5_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c0_s_p5_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_s_p5_0 = { + _vq_quantthresh__44c0_s_p5_0, + _vq_quantmap__44c0_s_p5_0, + 17, + 17 +}; + +static static_codebook _44c0_s_p5_0 = { + 2, 289, + _vq_lengthlist__44c0_s_p5_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c0_s_p5_0, + NULL, + &_vq_auxt__44c0_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_s_p6_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c0_s_p6_0[] = { + 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 7,10, 9, 9,10, + 9, 9, 4, 6, 7,10, 9, 9,11, 9, 9, 7,10,10,11,11, + 11,12,10,11, 6, 9, 9,11,10,11,11,10,10, 6, 9, 9, + 11,10,11,11,10,10, 7,11,10,12,11,11,11,11,11, 7, + 9, 9,10,10,10,11,11,10, 6, 9, 9,11,10,10,11,10, + 10, +}; + +static float _vq_quantthresh__44c0_s_p6_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44c0_s_p6_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_s_p6_0 = { + _vq_quantthresh__44c0_s_p6_0, + _vq_quantmap__44c0_s_p6_0, + 3, + 3 +}; + +static static_codebook _44c0_s_p6_0 = { + 4, 81, + _vq_lengthlist__44c0_s_p6_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44c0_s_p6_0, + NULL, + &_vq_auxt__44c0_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_s_p6_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c0_s_p6_1[] = { + 2, 3, 3, 6, 6, 7, 7, 7, 7, 7, 8,10,10,10, 6, 6, + 7, 7, 8, 8, 8, 8,10,10,10, 6, 6, 7, 7, 8, 8, 8, + 8,10,10,10, 7, 7, 7, 7, 8, 8, 8, 8,10,10,10, 7, + 7, 7, 7, 8, 8, 8, 8,10,10,10, 8, 7, 8, 8, 8, 8, + 8, 8,10,10,10, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, + 8, 8, 8, 8, 8, 8, 8, 8,10,10,10,10,10, 8, 8, 8, + 8, 8, 8,10,10,10,10,10, 9, 9, 8, 8, 8, 8,10,10, + 10,10,10, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44c0_s_p6_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c0_s_p6_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_s_p6_1 = { + _vq_quantthresh__44c0_s_p6_1, + _vq_quantmap__44c0_s_p6_1, + 11, + 11 +}; + +static static_codebook _44c0_s_p6_1 = { + 2, 121, + _vq_lengthlist__44c0_s_p6_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c0_s_p6_1, + NULL, + &_vq_auxt__44c0_s_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_s_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c0_s_p7_0[] = { + 1, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 7, 5, 5, + 7, 7, 8, 8, 8, 8, 9, 9,10,10, 7, 5, 6, 7, 7, 8, + 8, 8, 8, 9, 9,10,10, 0, 8, 8, 8, 8, 9, 9, 9, 9, + 10,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11, + 11, 0,12,12, 9, 9,10,10,10,10,11,11,11,11, 0,13, + 13, 9, 9, 9, 9,10,10,11,11,11,12, 0, 0, 0,10,10, + 10,10,11,11,11,11,12,12, 0, 0, 0,10,10, 9, 9,11, + 11,11,12,12,12, 0, 0, 0,13,13,10,10,11,11,12,12, + 13,13, 0, 0, 0,14,14,10,10,11,11,12,12,13,13, 0, + 0, 0, 0, 0,11,11,11,11,13,12,13,13, 0, 0, 0, 0, + 0,12,12,11,11,12,12,13,13, +}; + +static float _vq_quantthresh__44c0_s_p7_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44c0_s_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_s_p7_0 = { + _vq_quantthresh__44c0_s_p7_0, + _vq_quantmap__44c0_s_p7_0, + 13, + 13 +}; + +static static_codebook _44c0_s_p7_0 = { + 2, 169, + _vq_lengthlist__44c0_s_p7_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44c0_s_p7_0, + NULL, + &_vq_auxt__44c0_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_s_p7_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c0_s_p7_1[] = { + 2, 3, 3, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, 6, + 6, 6, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__44c0_s_p7_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c0_s_p7_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_s_p7_1 = { + _vq_quantthresh__44c0_s_p7_1, + _vq_quantmap__44c0_s_p7_1, + 5, + 5 +}; + +static static_codebook _44c0_s_p7_1 = { + 2, 25, + _vq_lengthlist__44c0_s_p7_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c0_s_p7_1, + NULL, + &_vq_auxt__44c0_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_s_p8_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c0_s_p8_0[] = { + 1, 5, 5,10,10, 6, 9, 8,10,10, 6,10, 9,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10, 8,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,10,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11, +}; + +static float _vq_quantthresh__44c0_s_p8_0[] = { + -331.5, -110.5, 110.5, 331.5, +}; + +static long _vq_quantmap__44c0_s_p8_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_s_p8_0 = { + _vq_quantthresh__44c0_s_p8_0, + _vq_quantmap__44c0_s_p8_0, + 5, + 5 +}; + +static static_codebook _44c0_s_p8_0 = { + 4, 625, + _vq_lengthlist__44c0_s_p8_0, + 1, -518283264, 1627103232, 3, 0, + _vq_quantlist__44c0_s_p8_0, + NULL, + &_vq_auxt__44c0_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_s_p8_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c0_s_p8_1[] = { + 1, 4, 4, 6, 6, 7, 7, 9, 9,11,12,13,12, 6, 5, 5, + 7, 7, 8, 8,10, 9,12,12,12,12, 6, 5, 5, 7, 7, 8, + 8,10, 9,12,11,11,13,16, 7, 7, 8, 8, 9, 9,10,10, + 12,12,13,12,16, 7, 7, 8, 7, 9, 9,10,10,11,12,12, + 13,16,10,10, 8, 8,10,10,11,12,12,12,13,13,16,11, + 10, 8, 7,11,10,11,11,12,11,13,13,16,16,16,10,10, + 10,10,11,11,13,12,13,13,16,16,16,11, 9,11, 9,15, + 13,12,13,13,13,16,16,16,15,13,11,11,12,13,12,12, + 14,13,16,16,16,14,13,11,11,13,12,14,13,13,13,16, + 16,16,16,16,13,13,13,12,14,13,14,14,16,16,16,16, + 16,13,13,12,12,14,14,15,13, +}; + +static float _vq_quantthresh__44c0_s_p8_1[] = { + -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, 25.5, + 42.5, 59.5, 76.5, 93.5, +}; + +static long _vq_quantmap__44c0_s_p8_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_s_p8_1 = { + _vq_quantthresh__44c0_s_p8_1, + _vq_quantmap__44c0_s_p8_1, + 13, + 13 +}; + +static static_codebook _44c0_s_p8_1 = { + 2, 169, + _vq_lengthlist__44c0_s_p8_1, + 1, -522616832, 1620115456, 4, 0, + _vq_quantlist__44c0_s_p8_1, + NULL, + &_vq_auxt__44c0_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_s_p8_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c0_s_p8_2[] = { + 2, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, + 8,10,10,10, 7, 7, 7, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9,10,10,10, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9,10,10,10, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, + 9,10, 9, 9,10,10,10, 7, 7, 8, 8, 9, 8, 9, 9, 9, + 9,10, 9, 9,10,10,10,10, 8, 8, 8, 8, 9, 8, 9, 9, + 9, 9, 9,10, 9,10,10,10,10, 7, 7, 8, 8, 9, 9, 9, + 9, 9, 9,10, 9,10,10,10,10,10, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 9,10,10,10, 9,11,10,10,10,10, 8, 8, 9, + 9, 9, 9, 9,10, 9, 9, 9,10,10,10,10,11,11, 9, 9, + 9, 9, 9, 9, 9, 9,10, 9, 9,10,11,10,10,11,11, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 9,11,11,10,11,11, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 9,11,10,10,11, + 11,11,11, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10,10, + 11,11,11,11, 9,10, 9,10, 9, 9, 9, 9,10, 9,10,11, + 10,11,10,10,10,10,10, 9, 9, 9,10, 9, 9, 9,10,11, + 11,10,11,11,10,11,10,10,10, 9, 9, 9, 9,10, 9, 9, + 10,11,10,11,11,11,11,10,11,10,10, 9,10, 9, 9, 9, + 10, +}; + +static float _vq_quantthresh__44c0_s_p8_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c0_s_p8_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_s_p8_2 = { + _vq_quantthresh__44c0_s_p8_2, + _vq_quantmap__44c0_s_p8_2, + 17, + 17 +}; + +static static_codebook _44c0_s_p8_2 = { + 2, 289, + _vq_lengthlist__44c0_s_p8_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c0_s_p8_2, + NULL, + &_vq_auxt__44c0_s_p8_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44c0_s_short[] = { + 9, 8,12,11,12,13,14,14,16, 6, 1, 5, 6, 6, 9,12, + 14,17, 9, 4, 5, 9, 7, 9,13,15,16, 8, 5, 8, 6, 8, + 10,13,17,17, 9, 6, 7, 7, 8, 9,13,15,17,11, 8, 9, + 9, 9,10,12,16,16,13, 7, 8, 7, 7, 9,12,14,15,13, + 6, 7, 5, 5, 7,10,13,13,14, 7, 8, 5, 6, 7, 9,10, + 12, +}; + +static static_codebook _huff_book__44c0_s_short = { + 2, 81, + _huff_lengthlist__44c0_s_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44c0_sm_long[] = { + 5, 4, 9,10, 9,10,11,12,13, 4, 1, 5, 7, 7, 9,11, + 12,14, 8, 5, 7, 9, 8,10,13,13,13,10, 7, 9, 4, 6, + 7,10,12,14, 9, 6, 7, 6, 6, 7,10,12,12, 9, 8, 9, + 7, 6, 7, 8,11,12,11,11,11, 9, 8, 7, 8,10,12,12, + 13,14,12,11, 9, 9, 9,12,12,17,17,15,16,12,10,11, + 13, +}; + +static static_codebook _huff_book__44c0_sm_long = { + 2, 81, + _huff_lengthlist__44c0_sm_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_sm_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c0_sm_p1_0[] = { + 1, 5, 5, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 8, 7, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, + 0, 0, 0, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 7, 9, 8, 0, 0, + 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 8, 7, 0, 0, 0, 0, + 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, + 0, 0, 0, 9,10,10, 0, 0, 0, 0, 0, 0, 9,10,10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 8,10, 9, 0, 0, 0, 0, 0, 0, 9,10,10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 7, 8, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 9,10,10, 0, + 0, 0, 0, 0, 0, 9, 9,10, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 9,10,10, + 0, 0, 0, 0, 0, 0, 9,10,10, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c0_sm_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44c0_sm_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_sm_p1_0 = { + _vq_quantthresh__44c0_sm_p1_0, + _vq_quantmap__44c0_sm_p1_0, + 3, + 3 +}; + +static static_codebook _44c0_sm_p1_0 = { + 8, 6561, + _vq_lengthlist__44c0_sm_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44c0_sm_p1_0, + NULL, + &_vq_auxt__44c0_sm_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_sm_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c0_sm_p2_0[] = { + 1, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 7, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 5, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 7, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 7, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c0_sm_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c0_sm_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_sm_p2_0 = { + _vq_quantthresh__44c0_sm_p2_0, + _vq_quantmap__44c0_sm_p2_0, + 5, + 5 +}; + +static static_codebook _44c0_sm_p2_0 = { + 4, 625, + _vq_lengthlist__44c0_sm_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c0_sm_p2_0, + NULL, + &_vq_auxt__44c0_sm_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_sm_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c0_sm_p3_0[] = { + 1, 3, 3, 7, 7, 0, 0, 0, 0, 0, 5, 4, 7, 7, 0, 0, + 0, 0, 0, 5, 5, 7, 7, 0, 0, 0, 0, 0, 6, 7, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0, 0, + 9,10, 0, 0, 0, 0, 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, + 0, 0,11,11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c0_sm_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c0_sm_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_sm_p3_0 = { + _vq_quantthresh__44c0_sm_p3_0, + _vq_quantmap__44c0_sm_p3_0, + 9, + 9 +}; + +static static_codebook _44c0_sm_p3_0 = { + 2, 81, + _vq_lengthlist__44c0_sm_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c0_sm_p3_0, + NULL, + &_vq_auxt__44c0_sm_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_sm_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c0_sm_p4_0[] = { + 1, 4, 3, 6, 6, 7, 7, 9, 9, 0, 5, 5, 7, 7, 8, 7, + 9, 9, 0, 5, 5, 7, 7, 8, 8, 9, 9, 0, 7, 7, 8, 8, + 8, 8,10,10, 0, 0, 0, 8, 8, 8, 8,10,10, 0, 0, 0, + 9, 9, 9, 9,11,11, 0, 0, 0, 9, 9, 9, 9,11,11, 0, + 0, 0,10,10,10,10,11,11, 0, 0, 0, 0, 0, 9, 9,11, + 11, +}; + +static float _vq_quantthresh__44c0_sm_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c0_sm_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_sm_p4_0 = { + _vq_quantthresh__44c0_sm_p4_0, + _vq_quantmap__44c0_sm_p4_0, + 9, + 9 +}; + +static static_codebook _44c0_sm_p4_0 = { + 2, 81, + _vq_lengthlist__44c0_sm_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c0_sm_p4_0, + NULL, + &_vq_auxt__44c0_sm_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_sm_p5_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c0_sm_p5_0[] = { + 1, 4, 4, 6, 6, 8, 8, 8, 8, 8, 8, 9, 9,10,10,11, + 11, 0, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10,11, + 11,11, 0, 5, 6, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10, + 11,11,11, 0, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9,10,10, + 11,11,12,12, 0, 0, 0, 8, 8, 8, 8, 9, 9, 9, 9,10, + 10,11,11,12,12, 0, 0, 0, 8, 8, 9, 9,10,10,10,10, + 11,11,11,11,12,12, 0, 0, 0, 8, 8, 9, 9,10,10,10, + 10,11,11,11,11,12,12, 0, 0, 0, 9, 9, 9, 9,10,10, + 10,10,11,11,12,12,12,13, 0, 0, 0, 0, 0, 9, 9,10, + 10,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, 9, + 10,10,11,11,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, + 9,10,10,11,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, + 10,10,10,10,11,11,12,12,12,13,13,13, 0, 0, 0, 0, + 0, 0, 0,10,10,11,11,12,12,12,13,13,13, 0, 0, 0, + 0, 0, 0, 0,11,11,12,12,12,12,13,13,14,14, 0, 0, + 0, 0, 0, 0, 0,11,11,12,11,12,12,13,13,13,13, 0, + 0, 0, 0, 0, 0, 0,12,12,12,12,13,13,13,13,14,14, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,12,12,13,13,14, + 14, +}; + +static float _vq_quantthresh__44c0_sm_p5_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c0_sm_p5_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_sm_p5_0 = { + _vq_quantthresh__44c0_sm_p5_0, + _vq_quantmap__44c0_sm_p5_0, + 17, + 17 +}; + +static static_codebook _44c0_sm_p5_0 = { + 2, 289, + _vq_lengthlist__44c0_sm_p5_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c0_sm_p5_0, + NULL, + &_vq_auxt__44c0_sm_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_sm_p6_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c0_sm_p6_0[] = { + 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 7,10, 9, 9,11, + 9, 9, 4, 7, 7,10, 9, 9,11, 9, 9, 7,10,10,10,11, + 11,11,10,10, 6, 9, 9,11,11,10,11,10,10, 6, 9, 9, + 11,10,11,11,10,10, 7,11,10,11,11,11,11,11,11, 6, + 9, 9,11,10,10,11,11,10, 6, 9, 9,11,10,10,11,10, + 11, +}; + +static float _vq_quantthresh__44c0_sm_p6_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44c0_sm_p6_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_sm_p6_0 = { + _vq_quantthresh__44c0_sm_p6_0, + _vq_quantmap__44c0_sm_p6_0, + 3, + 3 +}; + +static static_codebook _44c0_sm_p6_0 = { + 4, 81, + _vq_lengthlist__44c0_sm_p6_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44c0_sm_p6_0, + NULL, + &_vq_auxt__44c0_sm_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_sm_p6_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c0_sm_p6_1[] = { + 2, 4, 4, 6, 6, 7, 7, 7, 7, 7, 8, 9, 5, 5, 6, 6, + 7, 7, 8, 8, 8, 8, 9, 5, 5, 6, 6, 7, 7, 8, 8, 8, + 8,10, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8,10,10,10, 7, + 7, 7, 7, 8, 8, 8, 8,10,10,10, 8, 8, 8, 8, 8, 8, + 8, 8,10,10,10, 8, 8, 8, 8, 8, 8, 8, 8,10,10,10, + 8, 8, 8, 8, 8, 8, 8, 8,10,10,10,10,10, 8, 8, 8, + 8, 8, 8,10,10,10,10,10, 9, 9, 8, 8, 8, 8,10,10, + 10,10,10, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44c0_sm_p6_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c0_sm_p6_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_sm_p6_1 = { + _vq_quantthresh__44c0_sm_p6_1, + _vq_quantmap__44c0_sm_p6_1, + 11, + 11 +}; + +static static_codebook _44c0_sm_p6_1 = { + 2, 121, + _vq_lengthlist__44c0_sm_p6_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c0_sm_p6_1, + NULL, + &_vq_auxt__44c0_sm_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_sm_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c0_sm_p7_0[] = { + 1, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 7, 5, 5, + 7, 7, 8, 8, 8, 8, 9, 9,10,10, 7, 6, 5, 7, 7, 8, + 8, 8, 8, 9, 9,10,10, 0, 8, 8, 8, 8, 9, 9, 9, 9, + 10,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11, + 11, 0,12,12, 9, 9,10,10,10,10,11,11,11,11, 0,13, + 13, 9, 9, 9, 9,10,10,11,11,11,12, 0, 0, 0, 9,10, + 10,10,11,11,12,11,12,12, 0, 0, 0,10,10, 9, 9,11, + 11,12,12,12,12, 0, 0, 0,13,13,10,10,11,11,12,12, + 13,13, 0, 0, 0,14,14,10,10,11,11,12,12,13,13, 0, + 0, 0, 0, 0,11,12,11,11,13,12,13,13, 0, 0, 0, 0, + 0,12,12,11,11,13,12,14,14, +}; + +static float _vq_quantthresh__44c0_sm_p7_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44c0_sm_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_sm_p7_0 = { + _vq_quantthresh__44c0_sm_p7_0, + _vq_quantmap__44c0_sm_p7_0, + 13, + 13 +}; + +static static_codebook _44c0_sm_p7_0 = { + 2, 169, + _vq_lengthlist__44c0_sm_p7_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44c0_sm_p7_0, + NULL, + &_vq_auxt__44c0_sm_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_sm_p7_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c0_sm_p7_1[] = { + 2, 4, 4, 4, 4, 6, 5, 5, 5, 5, 6, 5, 5, 5, 5, 6, + 6, 6, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__44c0_sm_p7_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c0_sm_p7_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_sm_p7_1 = { + _vq_quantthresh__44c0_sm_p7_1, + _vq_quantmap__44c0_sm_p7_1, + 5, + 5 +}; + +static static_codebook _44c0_sm_p7_1 = { + 2, 25, + _vq_lengthlist__44c0_sm_p7_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c0_sm_p7_1, + NULL, + &_vq_auxt__44c0_sm_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_sm_p8_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c0_sm_p8_0[] = { + 1, 3, 3,11,11,11,11,11,11, 3, 7, 6,11,11,11,11, + 11,11, 4, 8, 7,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12, +}; + +static float _vq_quantthresh__44c0_sm_p8_0[] = { + -773.5, -552.5, -331.5, -110.5, 110.5, 331.5, 552.5, 773.5, +}; + +static long _vq_quantmap__44c0_sm_p8_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_sm_p8_0 = { + _vq_quantthresh__44c0_sm_p8_0, + _vq_quantmap__44c0_sm_p8_0, + 9, + 9 +}; + +static static_codebook _44c0_sm_p8_0 = { + 2, 81, + _vq_lengthlist__44c0_sm_p8_0, + 1, -516186112, 1627103232, 4, 0, + _vq_quantlist__44c0_sm_p8_0, + NULL, + &_vq_auxt__44c0_sm_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_sm_p8_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c0_sm_p8_1[] = { + 1, 4, 4, 6, 6, 7, 7, 9, 9,10,11,12,12, 6, 5, 5, + 7, 7, 8, 8,10,10,12,11,12,12, 6, 5, 5, 7, 7, 8, + 8,10,10,12,11,12,12,17, 7, 7, 8, 8, 9, 9,10,10, + 12,12,13,13,18, 7, 7, 8, 7, 9, 9,10,10,12,12,12, + 13,19,10,10, 8, 8,10,10,11,11,12,12,13,14,19,11, + 10, 8, 7,10,10,11,11,12,12,13,12,19,19,19,10,10, + 10,10,11,11,12,12,13,13,19,19,19,11, 9,11, 9,14, + 12,13,12,13,13,19,20,18,13,14,11,11,12,12,13,13, + 14,13,20,20,20,15,13,11,10,13,11,13,13,14,13,20, + 20,20,20,20,13,14,12,12,13,13,13,13,20,20,20,20, + 20,13,13,12,12,16,13,15,13, +}; + +static float _vq_quantthresh__44c0_sm_p8_1[] = { + -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, 25.5, + 42.5, 59.5, 76.5, 93.5, +}; + +static long _vq_quantmap__44c0_sm_p8_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_sm_p8_1 = { + _vq_quantthresh__44c0_sm_p8_1, + _vq_quantmap__44c0_sm_p8_1, + 13, + 13 +}; + +static static_codebook _44c0_sm_p8_1 = { + 2, 169, + _vq_lengthlist__44c0_sm_p8_1, + 1, -522616832, 1620115456, 4, 0, + _vq_quantlist__44c0_sm_p8_1, + NULL, + &_vq_auxt__44c0_sm_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c0_sm_p8_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c0_sm_p8_2[] = { + 2, 5, 5, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, + 8,10, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9,10, 6, 6, 7, 7, 8, 7, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9,10, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9,10,10,10, 7, 7, 8, 8, 9, 8, 9, 9, 9, + 9,10, 9, 9,10,10,10,11, 8, 8, 8, 8, 9, 9, 9, 9, + 9, 9, 9,10, 9,10,10,10,10, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 9, 9,10,10,11,10,10, 8, 8, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9,10,10,10,10,10,11,11, 8, 8, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10,11,11,11,11,11, 9, 9, + 9, 9, 9, 9, 9, 9,10, 9,10, 9,11,11,10,11,11, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 9,11,11,10,11,11, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 9,11,10,11,11, + 11,11,11, 9, 9,10, 9, 9, 9, 9, 9, 9, 9,10,11,10, + 11,11,11,11,10,10,10,10, 9, 9, 9, 9, 9, 9,10,11, + 11,11,11,11,11, 9,10, 9, 9, 9, 9, 9, 9, 9, 9,11, + 11,10,11,11,11,10,10,10, 9, 9, 9, 9, 9, 9, 9, 9, + 10,11,10,11,11,11,11,11,11, 9, 9, 9, 9, 9, 9, 9, + 9, +}; + +static float _vq_quantthresh__44c0_sm_p8_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c0_sm_p8_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c0_sm_p8_2 = { + _vq_quantthresh__44c0_sm_p8_2, + _vq_quantmap__44c0_sm_p8_2, + 17, + 17 +}; + +static static_codebook _44c0_sm_p8_2 = { + 2, 289, + _vq_lengthlist__44c0_sm_p8_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c0_sm_p8_2, + NULL, + &_vq_auxt__44c0_sm_p8_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44c0_sm_short[] = { + 6, 6,12,13,13,14,16,17,17, 4, 2, 5, 8, 7, 9,12, + 15,15, 9, 4, 5, 9, 7, 9,12,16,18,11, 6, 7, 4, 6, + 8,11,14,18,10, 5, 6, 5, 5, 7,10,14,17,10, 5, 7, + 7, 6, 7,10,13,16,11, 5, 7, 7, 7, 8,10,12,15,13, + 6, 7, 5, 5, 7, 9,12,13,16, 8, 9, 6, 6, 7, 9,10, + 12, +}; + +static static_codebook _huff_book__44c0_sm_short = { + 2, 81, + _huff_lengthlist__44c0_sm_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44c1_s_long[] = { + 5, 5, 9,10, 9, 9,10,11,12, 5, 1, 5, 6, 6, 7,10, + 12,14, 9, 5, 6, 8, 8,10,12,14,14,10, 5, 8, 5, 6, + 8,11,13,14, 9, 5, 7, 6, 6, 8,10,12,11, 9, 7, 9, + 7, 6, 6, 7,10,10,10, 9,12, 9, 8, 7, 7,10,12,11, + 11,13,12,10, 9, 8, 9,11,11,14,15,15,13,11, 9, 9, + 11, +}; + +static static_codebook _huff_book__44c1_s_long = { + 2, 81, + _huff_lengthlist__44c1_s_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c1_s_p1_0[] = { + 2, 4, 4, 0, 0, 0, 0, 0, 0, 5, 7, 6, 0, 0, 0, 0, + 0, 0, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, + 0, 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, + 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 7, 7, 0, 0, 0, 0, + 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, + 0, 0, 0, 8, 9,10, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 8, 8, 0, 0, + 0, 0, 0, 0, 8, 9, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 7, 7, 0, 0, 0, 0, 0, 0, 7, 8, 8, 0, 0, + 0, 0, 0, 0, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 6, 8, 8, 0, 0, 0, 0, 0, 0, 8,10, 9, 0, + 0, 0, 0, 0, 0, 8, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 8, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, + 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c1_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44c1_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_s_p1_0 = { + _vq_quantthresh__44c1_s_p1_0, + _vq_quantmap__44c1_s_p1_0, + 3, + 3 +}; + +static static_codebook _44c1_s_p1_0 = { + 8, 6561, + _vq_lengthlist__44c1_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44c1_s_p1_0, + NULL, + &_vq_auxt__44c1_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c1_s_p2_0[] = { + 2, 3, 4, 6, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 6, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 4, 5, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 6, 6, 8, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c1_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c1_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_s_p2_0 = { + _vq_quantthresh__44c1_s_p2_0, + _vq_quantmap__44c1_s_p2_0, + 5, + 5 +}; + +static static_codebook _44c1_s_p2_0 = { + 4, 625, + _vq_lengthlist__44c1_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c1_s_p2_0, + NULL, + &_vq_auxt__44c1_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_s_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c1_s_p3_0[] = { + 1, 3, 2, 7, 7, 0, 0, 0, 0, 0,13,13, 6, 6, 0, 0, + 0, 0, 0,12, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 7, 7, + 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, + 8, 9, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, + 0, 0,11,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c1_s_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c1_s_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_s_p3_0 = { + _vq_quantthresh__44c1_s_p3_0, + _vq_quantmap__44c1_s_p3_0, + 9, + 9 +}; + +static static_codebook _44c1_s_p3_0 = { + 2, 81, + _vq_lengthlist__44c1_s_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c1_s_p3_0, + NULL, + &_vq_auxt__44c1_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_s_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c1_s_p4_0[] = { + 1, 3, 3, 6, 5, 6, 6, 8, 8, 0, 0, 0, 7, 7, 7, 7, + 9, 9, 0, 0, 0, 7, 7, 7, 7, 9, 9, 0, 0, 0, 7, 7, + 8, 8,10,10, 0, 0, 0, 7, 7, 8, 8,10,10, 0, 0, 0, + 9, 9, 8, 8,10,10, 0, 0, 0, 8, 8, 8, 8,10,10, 0, + 0, 0,10,10, 9, 9,11,11, 0, 0, 0, 0, 0, 9, 9,11, + 11, +}; + +static float _vq_quantthresh__44c1_s_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c1_s_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_s_p4_0 = { + _vq_quantthresh__44c1_s_p4_0, + _vq_quantmap__44c1_s_p4_0, + 9, + 9 +}; + +static static_codebook _44c1_s_p4_0 = { + 2, 81, + _vq_lengthlist__44c1_s_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c1_s_p4_0, + NULL, + &_vq_auxt__44c1_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_s_p5_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c1_s_p5_0[] = { + 1, 4, 3, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10,11, + 11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10,10, + 11,11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10, + 10,11,11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10, + 11,11,11,11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10, + 10,11,11,12,11, 0, 0, 0, 8, 8, 9, 9, 9,10,10,10, + 10,10,11,11,12,12, 0, 0, 0, 8, 8, 9, 9,10, 9,10, + 10,10,10,11,11,12,12, 0, 0, 0, 9, 9, 9, 9,10,10, + 10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 9, 9,10, + 10,10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 9, 9, + 10,10,10,11,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, + 9,10,10,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, + 10,10,10,10,11,11,12,12,12,12,13,13, 0, 0, 0, 0, + 0, 0, 0,10,10,11,11,12,12,12,12,13,13, 0, 0, 0, + 0, 0, 0, 0,11,11,12,12,12,12,13,13,13,13, 0, 0, + 0, 0, 0, 0, 0,11,11,11,11,12,12,13,13,13,13, 0, + 0, 0, 0, 0, 0, 0,12,12,12,12,12,12,13,13,14,14, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,12,12,13,13,14, + 14, +}; + +static float _vq_quantthresh__44c1_s_p5_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c1_s_p5_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_s_p5_0 = { + _vq_quantthresh__44c1_s_p5_0, + _vq_quantmap__44c1_s_p5_0, + 17, + 17 +}; + +static static_codebook _44c1_s_p5_0 = { + 2, 289, + _vq_lengthlist__44c1_s_p5_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c1_s_p5_0, + NULL, + &_vq_auxt__44c1_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_s_p6_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c1_s_p6_0[] = { + 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 7,10, 9, 9,11, + 9, 9, 4, 7, 7,10, 9, 9,11, 9, 9, 6,10,10,11,11, + 11,11,10,10, 6, 9, 9,11,10,10,11,10,10, 6, 9, 9, + 11,10,11,11,10,10, 7,11,10,11,11,11,12,11,11, 7, + 9, 9,11,10,10,11,11,10, 6, 9, 9,10,10,10,12,10, + 11, +}; + +static float _vq_quantthresh__44c1_s_p6_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44c1_s_p6_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_s_p6_0 = { + _vq_quantthresh__44c1_s_p6_0, + _vq_quantmap__44c1_s_p6_0, + 3, + 3 +}; + +static static_codebook _44c1_s_p6_0 = { + 4, 81, + _vq_lengthlist__44c1_s_p6_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44c1_s_p6_0, + NULL, + &_vq_auxt__44c1_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_s_p6_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c1_s_p6_1[] = { + 2, 3, 3, 6, 6, 7, 7, 7, 7, 8, 8,10,10,10, 6, 6, + 7, 7, 8, 8, 8, 8,10,10,10, 6, 6, 7, 7, 8, 8, 8, + 8,10,10,10, 7, 7, 7, 7, 8, 8, 8, 8,10,10,10, 7, + 7, 7, 7, 8, 8, 8, 8,10,10,10, 7, 7, 8, 8, 8, 8, + 8, 8,10,10,10, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, + 8, 8, 8, 8, 8, 8, 8, 8,10,10,10,10,10, 8, 8, 8, + 8, 8, 8,10,10,10,10,10, 9, 9, 8, 8, 8, 8,10,10, + 10,10,10, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44c1_s_p6_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c1_s_p6_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_s_p6_1 = { + _vq_quantthresh__44c1_s_p6_1, + _vq_quantmap__44c1_s_p6_1, + 11, + 11 +}; + +static static_codebook _44c1_s_p6_1 = { + 2, 121, + _vq_lengthlist__44c1_s_p6_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c1_s_p6_1, + NULL, + &_vq_auxt__44c1_s_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_s_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c1_s_p7_0[] = { + 1, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8,10, 9, 7, 5, 6, + 7, 7, 8, 8, 8, 8, 9, 9,10,10, 7, 5, 5, 7, 7, 8, + 8, 8, 8, 9, 9,10,10, 0, 8, 8, 8, 8, 9, 9, 9, 9, + 10,10,11,10, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11, + 11, 0,12,12, 9, 9, 9,10,10,10,11,11,11,11, 0,13, + 13, 9, 9, 9, 9,10,10,11,11,11,11, 0, 0, 0,10,10, + 10,10,11,11,12,11,12,12, 0, 0, 0,10,10,10, 9,11, + 11,12,11,13,12, 0, 0, 0,13,13,10,10,11,11,12,12, + 13,13, 0, 0, 0,14,14,10,10,11,11,12,12,13,13, 0, + 0, 0, 0, 0,11,12,11,11,12,12,14,13, 0, 0, 0, 0, + 0,12,11,11,11,13,10,14,13, +}; + +static float _vq_quantthresh__44c1_s_p7_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44c1_s_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_s_p7_0 = { + _vq_quantthresh__44c1_s_p7_0, + _vq_quantmap__44c1_s_p7_0, + 13, + 13 +}; + +static static_codebook _44c1_s_p7_0 = { + 2, 169, + _vq_lengthlist__44c1_s_p7_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44c1_s_p7_0, + NULL, + &_vq_auxt__44c1_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_s_p7_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c1_s_p7_1[] = { + 2, 3, 3, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, 6, + 6, 6, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__44c1_s_p7_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c1_s_p7_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_s_p7_1 = { + _vq_quantthresh__44c1_s_p7_1, + _vq_quantmap__44c1_s_p7_1, + 5, + 5 +}; + +static static_codebook _44c1_s_p7_1 = { + 2, 25, + _vq_lengthlist__44c1_s_p7_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c1_s_p7_1, + NULL, + &_vq_auxt__44c1_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_s_p8_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c1_s_p8_0[] = { + 1, 4, 3,10,10,10,10,10,10,10,10,10,10, 4, 8, 6, + 10,10,10,10,10,10,10,10,10,10, 4, 8, 7,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10, +}; + +static float _vq_quantthresh__44c1_s_p8_0[] = { + -1215.5, -994.5, -773.5, -552.5, -331.5, -110.5, 110.5, 331.5, + 552.5, 773.5, 994.5, 1215.5, +}; + +static long _vq_quantmap__44c1_s_p8_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_s_p8_0 = { + _vq_quantthresh__44c1_s_p8_0, + _vq_quantmap__44c1_s_p8_0, + 13, + 13 +}; + +static static_codebook _44c1_s_p8_0 = { + 2, 169, + _vq_lengthlist__44c1_s_p8_0, + 1, -514541568, 1627103232, 4, 0, + _vq_quantlist__44c1_s_p8_0, + NULL, + &_vq_auxt__44c1_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_s_p8_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c1_s_p8_1[] = { + 1, 4, 4, 6, 5, 7, 7, 9, 9,10,10,12,12, 6, 5, 5, + 7, 7, 8, 8,10,10,12,11,12,12, 6, 5, 5, 7, 7, 8, + 8,10,10,11,11,12,12,15, 7, 7, 8, 8, 9, 9,11,11, + 12,12,13,12,15, 8, 8, 8, 7, 9, 9,10,10,12,12,13, + 13,16,11,10, 8, 8,10,10,11,11,12,12,13,13,16,11, + 11, 9, 8,11,10,11,11,12,12,13,12,16,16,16,10,11, + 10,11,12,12,12,12,13,13,16,16,16,11, 9,11, 9,14, + 12,12,12,13,13,16,16,16,12,14,11,12,12,12,13,13, + 14,13,16,16,16,15,13,12,10,13,10,13,14,13,13,16, + 16,16,16,16,13,14,12,13,13,12,13,13,16,16,16,16, + 16,13,12,12,11,14,12,15,13, +}; + +static float _vq_quantthresh__44c1_s_p8_1[] = { + -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, 25.5, + 42.5, 59.5, 76.5, 93.5, +}; + +static long _vq_quantmap__44c1_s_p8_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_s_p8_1 = { + _vq_quantthresh__44c1_s_p8_1, + _vq_quantmap__44c1_s_p8_1, + 13, + 13 +}; + +static static_codebook _44c1_s_p8_1 = { + 2, 169, + _vq_lengthlist__44c1_s_p8_1, + 1, -522616832, 1620115456, 4, 0, + _vq_quantlist__44c1_s_p8_1, + NULL, + &_vq_auxt__44c1_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_s_p8_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c1_s_p8_2[] = { + 2, 4, 4, 6, 6, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, + 8,10,10,10, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9,10,10,10, 7, 7, 8, 7, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9,10,10,10, 7, 7, 8, 8, 8, 9, 9, 9, 9, 9, + 9,10, 9, 9,10,10,10, 7, 7, 8, 8, 9, 8, 9, 9, 9, + 9,10, 9, 9,10,10,11,11, 8, 8, 8, 8, 9, 9, 9, 9, + 9, 9,10, 9, 9,10,10,10,10, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 9, 9,10,10,11,11,11, 8, 8, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9,10,10,10,10,11,11,11, 8, 8, 9, + 9, 9, 9,10, 9, 9, 9, 9, 9,11,11,11,11,11, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,11,10,10,11,11, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10,10,10,10,11,10,11,11, + 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10, 9,10,10,11,11, + 11,11,11, 9, 9, 9,10, 9, 9, 9, 9, 9, 9,10,11,11, + 11,11,11,11,10,10,10,10, 9, 9, 9, 9, 9, 9,10,11, + 11,11,11,11,11, 9,10, 9, 9, 9, 9,10, 9, 9, 9,11, + 11,11,11,11,11,11,10,10, 9, 9, 9, 9, 9, 9,10, 9, + 11,11,10,11,11,11,11,10,11, 9, 9, 9, 9, 9, 9, 9, + 9, +}; + +static float _vq_quantthresh__44c1_s_p8_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c1_s_p8_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_s_p8_2 = { + _vq_quantthresh__44c1_s_p8_2, + _vq_quantmap__44c1_s_p8_2, + 17, + 17 +}; + +static static_codebook _44c1_s_p8_2 = { + 2, 289, + _vq_lengthlist__44c1_s_p8_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c1_s_p8_2, + NULL, + &_vq_auxt__44c1_s_p8_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44c1_s_short[] = { + 6, 8,13,12,13,14,15,16,16, 4, 2, 4, 7, 6, 8,11, + 13,15,10, 4, 4, 8, 6, 8,11,14,17,11, 5, 6, 5, 6, + 8,12,14,17,11, 5, 5, 6, 5, 7,10,13,16,12, 6, 7, + 8, 7, 8,10,13,15,13, 8, 8, 7, 7, 8,10,12,15,15, + 7, 7, 5, 5, 7, 9,12,14,15, 8, 8, 6, 6, 7, 8,10, + 11, +}; + +static static_codebook _huff_book__44c1_s_short = { + 2, 81, + _huff_lengthlist__44c1_s_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44c1_sm_long[] = { + 5, 4, 8,10, 9, 9,10,11,12, 4, 2, 5, 6, 6, 8,10, + 11,13, 8, 4, 6, 8, 7, 9,12,12,14,10, 6, 8, 4, 5, + 6, 9,11,12, 9, 5, 6, 5, 5, 6, 9,11,11, 9, 7, 9, + 6, 5, 5, 7,10,10,10, 9,11, 8, 7, 6, 7, 9,11,11, + 12,13,10,10, 9, 8, 9,11,11,15,15,12,13,11, 9,10, + 11, +}; + +static static_codebook _huff_book__44c1_sm_long = { + 2, 81, + _huff_lengthlist__44c1_sm_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_sm_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c1_sm_p1_0[] = { + 1, 5, 5, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 8, 7, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, + 0, 0, 0, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 7, 9, 8, 0, 0, + 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 8, 7, 0, 0, 0, 0, + 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, + 0, 0, 0, 9, 9,10, 0, 0, 0, 0, 0, 0, 9,10,10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 8,10, 9, 0, 0, 0, 0, 0, 0, 9,10,10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 7, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, + 0, 0, 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 9,10,10, 0, + 0, 0, 0, 0, 0, 8, 9,10, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 9,10,10, + 0, 0, 0, 0, 0, 0, 9,10, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c1_sm_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44c1_sm_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_sm_p1_0 = { + _vq_quantthresh__44c1_sm_p1_0, + _vq_quantmap__44c1_sm_p1_0, + 3, + 3 +}; + +static static_codebook _44c1_sm_p1_0 = { + 8, 6561, + _vq_lengthlist__44c1_sm_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44c1_sm_p1_0, + NULL, + &_vq_auxt__44c1_sm_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_sm_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c1_sm_p2_0[] = { + 2, 3, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 6, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 6, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c1_sm_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c1_sm_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_sm_p2_0 = { + _vq_quantthresh__44c1_sm_p2_0, + _vq_quantmap__44c1_sm_p2_0, + 5, + 5 +}; + +static static_codebook _44c1_sm_p2_0 = { + 4, 625, + _vq_lengthlist__44c1_sm_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c1_sm_p2_0, + NULL, + &_vq_auxt__44c1_sm_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_sm_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c1_sm_p3_0[] = { + 1, 3, 3, 7, 7, 0, 0, 0, 0, 0, 5, 5, 6, 6, 0, 0, + 0, 0, 0, 5, 5, 7, 7, 0, 0, 0, 0, 0, 7, 7, 7, 7, + 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, + 8, 9, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, + 0, 0,10,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44c1_sm_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c1_sm_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_sm_p3_0 = { + _vq_quantthresh__44c1_sm_p3_0, + _vq_quantmap__44c1_sm_p3_0, + 9, + 9 +}; + +static static_codebook _44c1_sm_p3_0 = { + 2, 81, + _vq_lengthlist__44c1_sm_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c1_sm_p3_0, + NULL, + &_vq_auxt__44c1_sm_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_sm_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44c1_sm_p4_0[] = { + 1, 3, 3, 6, 6, 7, 7, 9, 9, 0, 6, 6, 7, 7, 8, 8, + 9, 9, 0, 6, 6, 7, 7, 8, 8, 9, 9, 0, 7, 7, 8, 8, + 8, 8,10,10, 0, 0, 0, 8, 8, 8, 8,10,10, 0, 0, 0, + 8, 8, 9, 9,11,11, 0, 0, 0, 9, 9, 9, 9,11,11, 0, + 0, 0,10,10,10,10,11,11, 0, 0, 0, 0, 0, 9, 9,11, + 11, +}; + +static float _vq_quantthresh__44c1_sm_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44c1_sm_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_sm_p4_0 = { + _vq_quantthresh__44c1_sm_p4_0, + _vq_quantmap__44c1_sm_p4_0, + 9, + 9 +}; + +static static_codebook _44c1_sm_p4_0 = { + 2, 81, + _vq_lengthlist__44c1_sm_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44c1_sm_p4_0, + NULL, + &_vq_auxt__44c1_sm_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_sm_p5_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c1_sm_p5_0[] = { + 2, 3, 3, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10,11, + 11, 0, 5, 5, 6, 6, 8, 8, 9, 9, 9, 9,10,10,10,10, + 11,11, 0, 5, 5, 6, 6, 8, 8, 9, 9, 9, 9,10,10,10, + 10,11,11, 0, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9,10,10, + 11,11,12,12, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10, + 10,11,11,12,12, 0, 0, 0, 8, 8, 8, 8, 9, 9,10,10, + 10,11,11,11,12,12, 0, 0, 0, 8, 8, 8, 8, 9, 9,10, + 10,10,10,11,11,12,12, 0, 0, 0, 9, 9, 9, 9,10,10, + 10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 9, 9,10, + 10,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, 9, + 9, 9,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, + 9, 9, 9,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, + 9, 9,10,10,11,11,12,12,12,12,13,13, 0, 0, 0, 0, + 0, 0, 0,10,10,11,11,12,12,12,12,13,13, 0, 0, 0, + 0, 0, 0, 0,11,11,11,11,12,12,13,13,13,13, 0, 0, + 0, 0, 0, 0, 0,11,11,11,11,12,12,13,13,13,13, 0, + 0, 0, 0, 0, 0, 0,11,11,12,12,12,12,13,13,14,14, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,12,12,13,13,14, + 14, +}; + +static float _vq_quantthresh__44c1_sm_p5_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c1_sm_p5_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_sm_p5_0 = { + _vq_quantthresh__44c1_sm_p5_0, + _vq_quantmap__44c1_sm_p5_0, + 17, + 17 +}; + +static static_codebook _44c1_sm_p5_0 = { + 2, 289, + _vq_lengthlist__44c1_sm_p5_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c1_sm_p5_0, + NULL, + &_vq_auxt__44c1_sm_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_sm_p6_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44c1_sm_p6_0[] = { + 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 7,10, 9, 9,11, + 9, 9, 4, 7, 7,10, 9, 9,11, 9, 9, 7,10,10,10,11, + 11,11,10,10, 6, 9, 9,11,11,10,11,10,10, 6, 9, 9, + 11,10,11,11,10,10, 7,11,11,11,11,11,11,11,11, 6, + 9, 9,11,10,10,11,11,10, 6, 9, 9,10,10,10,11,10, + 11, +}; + +static float _vq_quantthresh__44c1_sm_p6_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44c1_sm_p6_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_sm_p6_0 = { + _vq_quantthresh__44c1_sm_p6_0, + _vq_quantmap__44c1_sm_p6_0, + 3, + 3 +}; + +static static_codebook _44c1_sm_p6_0 = { + 4, 81, + _vq_lengthlist__44c1_sm_p6_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44c1_sm_p6_0, + NULL, + &_vq_auxt__44c1_sm_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_sm_p6_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44c1_sm_p6_1[] = { + 2, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8,10, 5, 5, 6, 6, + 7, 7, 8, 8, 8, 8,10, 5, 5, 6, 6, 7, 7, 8, 8, 8, + 8,10, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, 7, + 7, 7, 7, 8, 8, 8, 8,10,10,10, 7, 7, 8, 8, 8, 8, + 8, 8,10,10,10, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, + 8, 8, 8, 8, 8, 8, 9, 8,10,10,10,10,10, 8, 8, 8, + 8, 8, 8,10,10,10,10,10, 9, 9, 8, 8, 8, 8,10,10, + 10,10,10, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44c1_sm_p6_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44c1_sm_p6_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_sm_p6_1 = { + _vq_quantthresh__44c1_sm_p6_1, + _vq_quantmap__44c1_sm_p6_1, + 11, + 11 +}; + +static static_codebook _44c1_sm_p6_1 = { + 2, 121, + _vq_lengthlist__44c1_sm_p6_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44c1_sm_p6_1, + NULL, + &_vq_auxt__44c1_sm_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_sm_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c1_sm_p7_0[] = { + 1, 4, 4, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 7, 5, 5, + 7, 7, 8, 8, 8, 8, 9, 9,10,10, 7, 5, 6, 7, 7, 8, + 8, 8, 8, 9, 9,11,10, 0, 8, 8, 8, 8, 9, 9, 9, 9, + 10,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11, + 11, 0,12,12, 9, 9,10,10,10,10,11,11,11,11, 0,13, + 13, 9, 9, 9, 9,10,10,11,11,12,12, 0, 0, 0, 9,10, + 9,10,11,11,12,11,13,12, 0, 0, 0,10,10, 9, 9,11, + 11,12,12,13,12, 0, 0, 0,13,13,10,10,11,11,12,12, + 13,13, 0, 0, 0,14,14,10,10,11,11,12,12,13,13, 0, + 0, 0, 0, 0,11,12,11,11,12,13,14,13, 0, 0, 0, 0, + 0,12,12,11,11,13,12,14,13, +}; + +static float _vq_quantthresh__44c1_sm_p7_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44c1_sm_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_sm_p7_0 = { + _vq_quantthresh__44c1_sm_p7_0, + _vq_quantmap__44c1_sm_p7_0, + 13, + 13 +}; + +static static_codebook _44c1_sm_p7_0 = { + 2, 169, + _vq_lengthlist__44c1_sm_p7_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44c1_sm_p7_0, + NULL, + &_vq_auxt__44c1_sm_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_sm_p7_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44c1_sm_p7_1[] = { + 2, 4, 4, 4, 5, 6, 5, 5, 5, 5, 6, 5, 5, 5, 5, 6, + 5, 5, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__44c1_sm_p7_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44c1_sm_p7_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_sm_p7_1 = { + _vq_quantthresh__44c1_sm_p7_1, + _vq_quantmap__44c1_sm_p7_1, + 5, + 5 +}; + +static static_codebook _44c1_sm_p7_1 = { + 2, 25, + _vq_lengthlist__44c1_sm_p7_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44c1_sm_p7_1, + NULL, + &_vq_auxt__44c1_sm_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_sm_p8_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c1_sm_p8_0[] = { + 1, 3, 3,13,13,13,13,13,13,13,13,13,13, 3, 6, 6, + 13,13,13,13,13,13,13,13,13,13, 4, 8, 7,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13, +}; + +static float _vq_quantthresh__44c1_sm_p8_0[] = { + -1215.5, -994.5, -773.5, -552.5, -331.5, -110.5, 110.5, 331.5, + 552.5, 773.5, 994.5, 1215.5, +}; + +static long _vq_quantmap__44c1_sm_p8_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_sm_p8_0 = { + _vq_quantthresh__44c1_sm_p8_0, + _vq_quantmap__44c1_sm_p8_0, + 13, + 13 +}; + +static static_codebook _44c1_sm_p8_0 = { + 2, 169, + _vq_lengthlist__44c1_sm_p8_0, + 1, -514541568, 1627103232, 4, 0, + _vq_quantlist__44c1_sm_p8_0, + NULL, + &_vq_auxt__44c1_sm_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_sm_p8_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44c1_sm_p8_1[] = { + 1, 4, 4, 6, 6, 7, 7, 9, 9,10,11,12,12, 6, 5, 5, + 7, 7, 8, 7,10,10,11,11,12,12, 6, 5, 5, 7, 7, 8, + 8,10,10,11,11,12,12,16, 7, 7, 8, 8, 9, 9,11,11, + 12,12,13,13,17, 7, 7, 8, 7, 9, 9,11,10,12,12,13, + 13,19,11,10, 8, 8,10,10,11,11,12,12,13,13,19,11, + 11, 9, 7,11,10,11,11,12,12,13,12,19,19,19,10,10, + 10,10,11,12,12,12,13,14,18,19,19,11, 9,11, 9,13, + 12,12,12,13,13,19,20,19,13,15,11,11,12,12,13,13, + 14,13,18,19,20,15,13,12,10,13,10,13,13,13,14,20, + 20,20,20,20,13,14,12,12,13,12,13,13,20,20,20,20, + 20,13,12,12,12,14,12,14,13, +}; + +static float _vq_quantthresh__44c1_sm_p8_1[] = { + -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, 25.5, + 42.5, 59.5, 76.5, 93.5, +}; + +static long _vq_quantmap__44c1_sm_p8_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_sm_p8_1 = { + _vq_quantthresh__44c1_sm_p8_1, + _vq_quantmap__44c1_sm_p8_1, + 13, + 13 +}; + +static static_codebook _44c1_sm_p8_1 = { + 2, 169, + _vq_lengthlist__44c1_sm_p8_1, + 1, -522616832, 1620115456, 4, 0, + _vq_quantlist__44c1_sm_p8_1, + NULL, + &_vq_auxt__44c1_sm_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44c1_sm_p8_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44c1_sm_p8_2[] = { + 2, 5, 5, 6, 6, 7, 6, 7, 7, 8, 8, 8, 8, 8, 8, 8, + 8,10, 6, 6, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9,10, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9,10, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9,10,10,10, 7, 7, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9,10,11,11, 8, 8, 8, 8, 9, 9, 9, 9, + 9, 9,10,10, 9,10,10,10,10, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 9, 9,10,10,11,10,10, 8, 8, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9,10, 9,10,10,10,11,11, 8, 8, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9,11,11,11,11,11, 9, 9, + 9, 9, 9, 9, 9, 9,10, 9,10, 9,11,11,11,11,11, 9, + 8, 9, 9, 9, 9, 9, 9, 9,10,10, 9,11,11,10,11,11, + 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10, 9,11,11,11,11, + 11,11,11, 9, 9,10, 9, 9, 9, 9,10, 9,10,10,11,10, + 11,11,11,11, 9,10,10,10, 9, 9, 9, 9, 9, 9,10,11, + 11,11,11,11,11, 9, 9, 9, 9, 9, 9, 9, 9,10, 9,11, + 11,10,11,11,11,11,10,10, 9, 9, 9, 9, 9, 9,10, 9, + 10,11,10,11,11,11,11,11,11, 9, 9,10, 9, 9, 9, 9, + 9, +}; + +static float _vq_quantthresh__44c1_sm_p8_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44c1_sm_p8_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44c1_sm_p8_2 = { + _vq_quantthresh__44c1_sm_p8_2, + _vq_quantmap__44c1_sm_p8_2, + 17, + 17 +}; + +static static_codebook _44c1_sm_p8_2 = { + 2, 289, + _vq_lengthlist__44c1_sm_p8_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44c1_sm_p8_2, + NULL, + &_vq_auxt__44c1_sm_p8_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44c1_sm_short[] = { + 4, 7,13,14,14,15,16,18,18, 4, 2, 5, 8, 7, 9,12, + 15,15,10, 4, 5,10, 6, 8,11,15,17,12, 5, 7, 5, 6, + 8,11,14,17,11, 5, 6, 6, 5, 6, 9,13,17,12, 6, 7, + 6, 5, 6, 8,12,14,14, 7, 8, 6, 6, 7, 9,11,14,14, + 8, 9, 6, 5, 6, 9,11,13,16,10,10, 7, 6, 7, 8,10, + 11, +}; + +static static_codebook _huff_book__44c1_sm_short = { + 2, 81, + _huff_lengthlist__44c1_sm_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44cn1_s_long[] = { + 4, 4, 7, 8, 7, 8,10,12,17, 3, 1, 6, 6, 7, 8,10, + 12,15, 7, 6, 9, 9, 9,11,12,14,17, 8, 6, 9, 6, 7, + 9,11,13,17, 7, 6, 9, 7, 7, 8, 9,12,15, 8, 8,10, + 8, 7, 7, 7,10,14, 9,10,12,10, 8, 8, 8,10,14,11, + 13,15,13,12,11,11,12,16,17,18,18,19,20,18,16,16, + 20, +}; + +static static_codebook _huff_book__44cn1_s_long = { + 2, 81, + _huff_lengthlist__44cn1_s_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_s_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44cn1_s_p1_0[] = { + 1, 4, 4, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 8, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, 0, + 0, 0, 0, 7, 9,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 8, 8, 0, 0, 0, 0, 0, 0, 7,10, 9, 0, 0, + 0, 0, 0, 0, 8,10, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 8, 8, 0, 0, 0, 0, + 0, 0, 8,10,10, 0, 0, 0, 0, 0, 0, 8, 9,10, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,10,10, 0, 0, 0, + 0, 0, 0, 9, 9,11, 0, 0, 0, 0, 0, 0,10,11,11, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,10,10, 0, 0, + 0, 0, 0, 0, 9,11, 9, 0, 0, 0, 0, 0, 0,10,11,11, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 8, 8, 0, 0, 0, 0, 0, 0, 8,10,10, 0, 0, + 0, 0, 0, 0, 8,10,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7,10,10, 0, 0, 0, 0, 0, 0,10,11,11, 0, + 0, 0, 0, 0, 0, 9, 9,11, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7,10,10, 0, 0, 0, 0, 0, 0,10,11,11, + 0, 0, 0, 0, 0, 0, 9,11, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44cn1_s_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44cn1_s_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_s_p1_0 = { + _vq_quantthresh__44cn1_s_p1_0, + _vq_quantmap__44cn1_s_p1_0, + 3, + 3 +}; + +static static_codebook _44cn1_s_p1_0 = { + 8, 6561, + _vq_lengthlist__44cn1_s_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44cn1_s_p1_0, + NULL, + &_vq_auxt__44cn1_s_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_s_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44cn1_s_p2_0[] = { + 1, 4, 4, 7, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 7, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 5, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 7, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 7, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44cn1_s_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44cn1_s_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_s_p2_0 = { + _vq_quantthresh__44cn1_s_p2_0, + _vq_quantmap__44cn1_s_p2_0, + 5, + 5 +}; + +static static_codebook _44cn1_s_p2_0 = { + 4, 625, + _vq_lengthlist__44cn1_s_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44cn1_s_p2_0, + NULL, + &_vq_auxt__44cn1_s_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_s_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44cn1_s_p3_0[] = { + 1, 2, 3, 7, 7, 0, 0, 0, 0, 0, 0, 0, 6, 6, 0, 0, + 0, 0, 0, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 7, 7, + 0, 0, 0, 0, 0, 0, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, + 9, 8, 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, + 0, 0,10,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44cn1_s_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44cn1_s_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_s_p3_0 = { + _vq_quantthresh__44cn1_s_p3_0, + _vq_quantmap__44cn1_s_p3_0, + 9, + 9 +}; + +static static_codebook _44cn1_s_p3_0 = { + 2, 81, + _vq_lengthlist__44cn1_s_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44cn1_s_p3_0, + NULL, + &_vq_auxt__44cn1_s_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_s_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44cn1_s_p4_0[] = { + 1, 3, 3, 6, 6, 6, 6, 8, 8, 0, 0, 0, 6, 6, 7, 7, + 9, 9, 0, 0, 0, 6, 6, 7, 7, 9, 9, 0, 0, 0, 7, 7, + 8, 8,10,10, 0, 0, 0, 7, 7, 8, 8,10,10, 0, 0, 0, + 9, 9, 9, 9,10,10, 0, 0, 0, 9, 9, 9, 9,10,10, 0, + 0, 0,10,10,10,10,11,11, 0, 0, 0, 0, 0,10,10,11, + 11, +}; + +static float _vq_quantthresh__44cn1_s_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44cn1_s_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_s_p4_0 = { + _vq_quantthresh__44cn1_s_p4_0, + _vq_quantmap__44cn1_s_p4_0, + 9, + 9 +}; + +static static_codebook _44cn1_s_p4_0 = { + 2, 81, + _vq_lengthlist__44cn1_s_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44cn1_s_p4_0, + NULL, + &_vq_auxt__44cn1_s_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_s_p5_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44cn1_s_p5_0[] = { + 1, 4, 3, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10,10, + 10, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10,10, + 11,11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10,10, + 10,11,11, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10,10, + 11,11,11,12, 0, 0, 0, 7, 7, 8, 8, 9, 9, 9, 9,10, + 10,11,11,11,11, 0, 0, 0, 8, 8, 9, 9, 9, 9,10,10, + 10,10,11,11,12,12, 0, 0, 0, 8, 8, 9, 9, 9, 9,10, + 10,10,11,11,11,12,12, 0, 0, 0, 9, 9,10, 9,10,10, + 10,10,11,11,11,11,12,12, 0, 0, 0, 0, 0, 9, 9,10, + 10,10,10,11,11,12,12,12,12, 0, 0, 0, 0, 0, 9, 9, + 10,10,10,11,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, + 9,10,10,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, + 10,10,11,10,11,11,11,12,13,12,13,13, 0, 0, 0, 0, + 0, 0, 0,11,10,11,11,12,12,12,12,13,13, 0, 0, 0, + 0, 0, 0, 0,11,11,12,12,12,12,13,13,13,14, 0, 0, + 0, 0, 0, 0, 0,11,11,12,12,12,12,13,13,13,14, 0, + 0, 0, 0, 0, 0, 0,12,12,12,13,13,13,13,13,14,14, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,13,12,13,13,14, + 14, +}; + +static float _vq_quantthresh__44cn1_s_p5_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44cn1_s_p5_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_s_p5_0 = { + _vq_quantthresh__44cn1_s_p5_0, + _vq_quantmap__44cn1_s_p5_0, + 17, + 17 +}; + +static static_codebook _44cn1_s_p5_0 = { + 2, 289, + _vq_lengthlist__44cn1_s_p5_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44cn1_s_p5_0, + NULL, + &_vq_auxt__44cn1_s_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_s_p6_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44cn1_s_p6_0[] = { + 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 6, 6,10, 9, 9,11, + 9, 9, 4, 6, 6,10, 9, 9,10, 9, 9, 7,10,10,11,11, + 11,12,11,11, 7, 9, 9,11,11,10,11,10,10, 7, 9, 9, + 11,10,11,11,10,10, 7,10,10,11,11,11,12,11,11, 7, + 9, 9,11,10,10,11,10,10, 7, 9, 9,11,10,10,11,10, + 10, +}; + +static float _vq_quantthresh__44cn1_s_p6_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44cn1_s_p6_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_s_p6_0 = { + _vq_quantthresh__44cn1_s_p6_0, + _vq_quantmap__44cn1_s_p6_0, + 3, + 3 +}; + +static static_codebook _44cn1_s_p6_0 = { + 4, 81, + _vq_lengthlist__44cn1_s_p6_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44cn1_s_p6_0, + NULL, + &_vq_auxt__44cn1_s_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_s_p6_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44cn1_s_p6_1[] = { + 1, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8,10,10,10, 7, 6, + 8, 8, 8, 8, 8, 8,10,10,10, 7, 6, 7, 7, 8, 8, 8, + 8,10,10,10, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, 7, + 7, 8, 8, 8, 8, 8, 8,10,10,10, 8, 8, 8, 8, 9, 9, + 9, 9,10,10,10, 8, 8, 8, 8, 9, 9, 9, 9,10,10,10, + 9, 9, 9, 9, 9, 9, 9, 9,10,10,10,10,10, 9, 9, 9, + 9, 9, 9,10,10,10,10,10, 9, 9, 9, 9, 9, 9,10,10, + 10,10,10, 9, 9, 9, 9, 9, 9, +}; + +static float _vq_quantthresh__44cn1_s_p6_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44cn1_s_p6_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_s_p6_1 = { + _vq_quantthresh__44cn1_s_p6_1, + _vq_quantmap__44cn1_s_p6_1, + 11, + 11 +}; + +static static_codebook _44cn1_s_p6_1 = { + 2, 121, + _vq_lengthlist__44cn1_s_p6_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44cn1_s_p6_1, + NULL, + &_vq_auxt__44cn1_s_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_s_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44cn1_s_p7_0[] = { + 1, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10, 6, 5, 5, + 7, 7, 8, 8, 8, 8, 9, 9,11,11, 7, 5, 5, 7, 7, 8, + 8, 8, 8, 9,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9, + 10,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11, + 11, 0,12,12, 9, 9, 9,10,10,10,11,11,11,12, 0,13, + 13, 9, 9, 9, 9,10,10,11,11,11,12, 0, 0, 0,10,10, + 10,10,11,11,12,12,12,13, 0, 0, 0,10,10,10,10,11, + 11,12,12,13,12, 0, 0, 0,14,14,11,10,11,12,12,13, + 13,14, 0, 0, 0,15,15,11,11,12,11,12,12,14,13, 0, + 0, 0, 0, 0,12,12,12,12,13,13,14,14, 0, 0, 0, 0, + 0,13,13,12,12,13,13,13,14, +}; + +static float _vq_quantthresh__44cn1_s_p7_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44cn1_s_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_s_p7_0 = { + _vq_quantthresh__44cn1_s_p7_0, + _vq_quantmap__44cn1_s_p7_0, + 13, + 13 +}; + +static static_codebook _44cn1_s_p7_0 = { + 2, 169, + _vq_lengthlist__44cn1_s_p7_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44cn1_s_p7_0, + NULL, + &_vq_auxt__44cn1_s_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_s_p7_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44cn1_s_p7_1[] = { + 2, 3, 3, 5, 5, 6, 6, 6, 5, 5, 6, 6, 6, 5, 5, 6, + 6, 6, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__44cn1_s_p7_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44cn1_s_p7_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_s_p7_1 = { + _vq_quantthresh__44cn1_s_p7_1, + _vq_quantmap__44cn1_s_p7_1, + 5, + 5 +}; + +static static_codebook _44cn1_s_p7_1 = { + 2, 25, + _vq_lengthlist__44cn1_s_p7_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44cn1_s_p7_1, + NULL, + &_vq_auxt__44cn1_s_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_s_p8_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44cn1_s_p8_0[] = { + 1, 7, 7,11,11, 8,11,11,11,11, 4,11, 3,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,10,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,10,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11, 7,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,10,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11, 8,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12, +}; + +static float _vq_quantthresh__44cn1_s_p8_0[] = { + -331.5, -110.5, 110.5, 331.5, +}; + +static long _vq_quantmap__44cn1_s_p8_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_s_p8_0 = { + _vq_quantthresh__44cn1_s_p8_0, + _vq_quantmap__44cn1_s_p8_0, + 5, + 5 +}; + +static static_codebook _44cn1_s_p8_0 = { + 4, 625, + _vq_lengthlist__44cn1_s_p8_0, + 1, -518283264, 1627103232, 3, 0, + _vq_quantlist__44cn1_s_p8_0, + NULL, + &_vq_auxt__44cn1_s_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_s_p8_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44cn1_s_p8_1[] = { + 1, 4, 4, 6, 6, 8, 8, 9,10,10,11,11,11, 6, 5, 5, + 7, 7, 8, 8, 9,10, 9,11,11,12, 5, 5, 5, 7, 7, 8, + 9,10,10,12,12,14,13,15, 7, 7, 8, 8, 9,10,11,11, + 10,12,10,11,15, 7, 8, 8, 8, 9, 9,11,11,13,12,12, + 13,15,10,10, 8, 8,10,10,12,12,11,14,10,10,15,11, + 11, 8, 8,10,10,12,13,13,14,15,13,15,15,15,10,10, + 10,10,12,12,13,12,13,10,15,15,15,10,10,11,10,13, + 11,13,13,15,13,15,15,15,13,13,10,11,11,11,12,10, + 14,11,15,15,14,14,13,10,10,12,11,13,13,14,14,15, + 15,15,15,15,11,11,11,11,12,11,15,12,15,15,15,15, + 15,12,12,11,11,14,12,13,14, +}; + +static float _vq_quantthresh__44cn1_s_p8_1[] = { + -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, 25.5, + 42.5, 59.5, 76.5, 93.5, +}; + +static long _vq_quantmap__44cn1_s_p8_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_s_p8_1 = { + _vq_quantthresh__44cn1_s_p8_1, + _vq_quantmap__44cn1_s_p8_1, + 13, + 13 +}; + +static static_codebook _44cn1_s_p8_1 = { + 2, 169, + _vq_lengthlist__44cn1_s_p8_1, + 1, -522616832, 1620115456, 4, 0, + _vq_quantlist__44cn1_s_p8_1, + NULL, + &_vq_auxt__44cn1_s_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_s_p8_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44cn1_s_p8_2[] = { + 3, 4, 3, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, + 9,10,11,11, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9,10,10,10, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9,10,10,10, 7, 7, 7, 8, 8, 8, 9, 9, 9, 9, + 9, 9,10, 9,10,11,10, 7, 6, 7, 7, 8, 8, 9, 9, 9, + 9, 9, 9, 9,10,10,10,11, 7, 7, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 9,10,10,10, 7, 7, 8, 8, 8, 8, 9, + 9, 9, 9, 9, 9, 9,10,11,11,11, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9,11,10,10,11,11, 8, 8, 8, + 9, 9, 9, 9, 9, 9,10, 9,10,10,10,10,11,11, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,11,11,10,11,11, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10,10,10,10,11,10,11,11, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,11,10,10,11, + 11,11,11, 9, 9, 9, 9, 9, 9, 9, 9,10,10,10,11,11, + 10,11,11,11, 9,10,10, 9, 9, 9, 9, 9, 9, 9,10,11, + 11,11,11,11,11, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,11, + 11,11,11,11,11,11,10,10, 9, 9, 9, 9, 9, 9, 9, 9, + 11,11,11,10,11,11,11,11,11, 9, 9, 9,10, 9, 9, 9, + 9, +}; + +static float _vq_quantthresh__44cn1_s_p8_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44cn1_s_p8_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_s_p8_2 = { + _vq_quantthresh__44cn1_s_p8_2, + _vq_quantmap__44cn1_s_p8_2, + 17, + 17 +}; + +static static_codebook _44cn1_s_p8_2 = { + 2, 289, + _vq_lengthlist__44cn1_s_p8_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44cn1_s_p8_2, + NULL, + &_vq_auxt__44cn1_s_p8_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44cn1_s_short[] = { + 10, 9,12,15,12,13,16,14,16, 7, 1, 5,14, 7,10,13, + 16,16, 9, 4, 6,16, 8,11,16,16,16,14, 4, 7,16, 9, + 12,14,16,16,10, 5, 7,14, 9,12,14,15,15,13, 8, 9, + 14,10,12,13,14,15,13, 9, 9, 7, 6, 8,11,12,12,14, + 8, 8, 5, 4, 5, 8,11,12,16,10,10, 6, 5, 6, 8, 9, + 10, +}; + +static static_codebook _huff_book__44cn1_s_short = { + 2, 81, + _huff_lengthlist__44cn1_s_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44cn1_sm_long[] = { + 3, 3, 8, 8, 8, 8,10,12,14, 3, 2, 6, 7, 7, 8,10, + 12,16, 7, 6, 7, 9, 8,10,12,14,16, 8, 6, 8, 4, 5, + 7, 9,11,13, 7, 6, 8, 5, 6, 7, 9,11,14, 8, 8,10, + 7, 7, 6, 8,10,13, 9,11,12, 9, 9, 7, 8,10,12,10, + 13,15,11,11,10, 9,10,13,13,16,17,14,15,14,13,14, + 17, +}; + +static static_codebook _huff_book__44cn1_sm_long = { + 2, 81, + _huff_lengthlist__44cn1_sm_long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_sm_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44cn1_sm_p1_0[] = { + 1, 4, 5, 0, 0, 0, 0, 0, 0, 5, 7, 7, 0, 0, 0, 0, + 0, 0, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 8, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, 0, + 0, 0, 0, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 8, 8, 0, 0, 0, 0, 0, 0, 7, 9, 8, 0, 0, + 0, 0, 0, 0, 8, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 8, 8, 0, 0, 0, 0, + 0, 0, 8,10, 9, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,10, 9, 0, 0, 0, + 0, 0, 0, 9, 9,10, 0, 0, 0, 0, 0, 0, 9,10,10, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 9, 0, 0, + 0, 0, 0, 0, 8,10, 9, 0, 0, 0, 0, 0, 0, 9,10,10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 8, 8, 0, 0, 0, 0, 0, 0, 8, 9, 9, 0, 0, + 0, 0, 0, 0, 8, 9,10, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 7, 9, 9, 0, 0, 0, 0, 0, 0, 9,10,10, 0, + 0, 0, 0, 0, 0, 8, 9,10, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 9,10, 0, 0, 0, 0, 0, 0, 9,10,10, + 0, 0, 0, 0, 0, 0, 9,10, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44cn1_sm_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44cn1_sm_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_sm_p1_0 = { + _vq_quantthresh__44cn1_sm_p1_0, + _vq_quantmap__44cn1_sm_p1_0, + 3, + 3 +}; + +static static_codebook _44cn1_sm_p1_0 = { + 8, 6561, + _vq_lengthlist__44cn1_sm_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44cn1_sm_p1_0, + NULL, + &_vq_auxt__44cn1_sm_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_sm_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44cn1_sm_p2_0[] = { + 1, 4, 4, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 5, 7, 7, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 5, 5, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 7, 9, 9, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 7, 7, 7, 9, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44cn1_sm_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44cn1_sm_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_sm_p2_0 = { + _vq_quantthresh__44cn1_sm_p2_0, + _vq_quantmap__44cn1_sm_p2_0, + 5, + 5 +}; + +static static_codebook _44cn1_sm_p2_0 = { + 4, 625, + _vq_lengthlist__44cn1_sm_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44cn1_sm_p2_0, + NULL, + &_vq_auxt__44cn1_sm_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_sm_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44cn1_sm_p3_0[] = { + 1, 3, 4, 7, 7, 0, 0, 0, 0, 0, 4, 4, 7, 7, 0, 0, + 0, 0, 0, 4, 5, 7, 7, 0, 0, 0, 0, 0, 6, 7, 8, 8, + 0, 0, 0, 0, 0, 0, 0, 8, 8, 0, 0, 0, 0, 0, 0, 0, + 9, 9, 0, 0, 0, 0, 0, 0, 0,10, 9, 0, 0, 0, 0, 0, + 0, 0,11,11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, +}; + +static float _vq_quantthresh__44cn1_sm_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44cn1_sm_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_sm_p3_0 = { + _vq_quantthresh__44cn1_sm_p3_0, + _vq_quantmap__44cn1_sm_p3_0, + 9, + 9 +}; + +static static_codebook _44cn1_sm_p3_0 = { + 2, 81, + _vq_lengthlist__44cn1_sm_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44cn1_sm_p3_0, + NULL, + &_vq_auxt__44cn1_sm_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_sm_p4_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44cn1_sm_p4_0[] = { + 1, 4, 3, 6, 6, 7, 7, 9, 9, 0, 5, 5, 7, 7, 8, 7, + 9, 9, 0, 5, 5, 7, 7, 8, 8, 9, 9, 0, 7, 7, 8, 8, + 8, 8,10,10, 0, 0, 0, 8, 8, 8, 8,10,10, 0, 0, 0, + 9, 9, 9, 9,10,10, 0, 0, 0, 9, 9, 9, 9,10,10, 0, + 0, 0,10,10,10,10,11,11, 0, 0, 0, 0, 0,10,10,11, + 11, +}; + +static float _vq_quantthresh__44cn1_sm_p4_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44cn1_sm_p4_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_sm_p4_0 = { + _vq_quantthresh__44cn1_sm_p4_0, + _vq_quantmap__44cn1_sm_p4_0, + 9, + 9 +}; + +static static_codebook _44cn1_sm_p4_0 = { + 2, 81, + _vq_lengthlist__44cn1_sm_p4_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44cn1_sm_p4_0, + NULL, + &_vq_auxt__44cn1_sm_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_sm_p5_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44cn1_sm_p5_0[] = { + 1, 4, 4, 6, 6, 8, 8, 9, 9, 8, 8, 9, 9,10,10,11, + 11, 0, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11,11, + 12,12, 0, 6, 5, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11, + 11,12,12, 0, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9,10,10, + 11,11,12,12, 0, 0, 0, 7, 7, 8, 8, 9, 9,10,10,11, + 11,11,11,12,12, 0, 0, 0, 8, 8, 9, 9,10,10,10,10, + 11,11,12,12,12,12, 0, 0, 0, 8, 8, 9, 9,10,10,10, + 10,11,11,12,12,12,12, 0, 0, 0, 9, 9, 9, 9,10,10, + 10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, 9,10, + 10,10,10,11,11,12,12,13,13, 0, 0, 0, 0, 0, 9, 9, + 10,10,11,11,12,12,13,13,13,13, 0, 0, 0, 0, 0, 9, + 9,10,10,11,11,12,12,12,13,13,13, 0, 0, 0, 0, 0, + 10,10,11,11,11,11,12,12,13,13,14,14, 0, 0, 0, 0, + 0, 0, 0,11,11,11,11,12,12,13,13,14,14, 0, 0, 0, + 0, 0, 0, 0,11,11,12,12,13,13,13,13,14,14, 0, 0, + 0, 0, 0, 0, 0,11,11,12,12,13,13,13,13,14,14, 0, + 0, 0, 0, 0, 0, 0,12,12,12,13,13,13,14,14,14,14, + 0, 0, 0, 0, 0, 0, 0, 0, 0,12,12,13,13,14,14,14, + 14, +}; + +static float _vq_quantthresh__44cn1_sm_p5_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44cn1_sm_p5_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_sm_p5_0 = { + _vq_quantthresh__44cn1_sm_p5_0, + _vq_quantmap__44cn1_sm_p5_0, + 17, + 17 +}; + +static static_codebook _44cn1_sm_p5_0 = { + 2, 289, + _vq_lengthlist__44cn1_sm_p5_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44cn1_sm_p5_0, + NULL, + &_vq_auxt__44cn1_sm_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_sm_p6_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44cn1_sm_p6_0[] = { + 1, 4, 4, 7, 6, 6, 7, 6, 6, 4, 7, 6,10, 9, 9,11, + 9, 9, 4, 6, 7,10, 9, 9,11, 9, 9, 7,10,10,10,11, + 11,11,11,10, 6, 9, 9,11,10,10,11,10,10, 6, 9, 9, + 11,10,11,11,10,10, 7,11,11,11,11,11,12,11,11, 7, + 9, 9,11,10,10,12,10,10, 7, 9, 9,11,10,10,11,10, + 10, +}; + +static float _vq_quantthresh__44cn1_sm_p6_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44cn1_sm_p6_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_sm_p6_0 = { + _vq_quantthresh__44cn1_sm_p6_0, + _vq_quantmap__44cn1_sm_p6_0, + 3, + 3 +}; + +static static_codebook _44cn1_sm_p6_0 = { + 4, 81, + _vq_lengthlist__44cn1_sm_p6_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44cn1_sm_p6_0, + NULL, + &_vq_auxt__44cn1_sm_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_sm_p6_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44cn1_sm_p6_1[] = { + 2, 4, 4, 5, 5, 7, 7, 7, 7, 8, 8,10, 5, 5, 6, 6, + 7, 7, 8, 8, 8, 8,10, 5, 5, 6, 6, 7, 7, 8, 8, 8, + 8,10, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8,10,10,10, 7, + 7, 7, 7, 8, 8, 8, 8,10,10,10, 8, 8, 8, 8, 8, 8, + 8, 8,10,10,10, 8, 8, 8, 8, 8, 8, 8, 8,10,10,10, + 8, 8, 8, 8, 8, 8, 9, 9,10,10,10,10,10, 8, 8, 8, + 8, 9, 9,10,10,10,10,10, 9, 9, 9, 9, 8, 9,10,10, + 10,10,10, 8, 9, 8, 8, 9, 8, +}; + +static float _vq_quantthresh__44cn1_sm_p6_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44cn1_sm_p6_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_sm_p6_1 = { + _vq_quantthresh__44cn1_sm_p6_1, + _vq_quantmap__44cn1_sm_p6_1, + 11, + 11 +}; + +static static_codebook _44cn1_sm_p6_1 = { + 2, 121, + _vq_lengthlist__44cn1_sm_p6_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44cn1_sm_p6_1, + NULL, + &_vq_auxt__44cn1_sm_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_sm_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44cn1_sm_p7_0[] = { + 1, 4, 4, 6, 6, 7, 7, 7, 7, 9, 9,10,10, 7, 5, 5, + 7, 7, 8, 8, 8, 8,10, 9,11,10, 7, 5, 5, 7, 7, 8, + 8, 8, 8, 9,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9, + 10,10,11,11, 0, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11, + 11, 0,12,12, 9, 9, 9,10,10,10,11,11,12,12, 0,13, + 13, 9, 9, 9, 9,10,10,11,11,12,12, 0, 0, 0,10,10, + 10,10,11,11,12,12,12,13, 0, 0, 0,10,10,10,10,11, + 11,12,12,12,12, 0, 0, 0,14,14,11,11,11,11,12,13, + 13,13, 0, 0, 0,14,14,11,10,11,11,12,12,13,13, 0, + 0, 0, 0, 0,12,12,12,12,13,13,13,14, 0, 0, 0, 0, + 0,13,12,12,12,13,13,13,14, +}; + +static float _vq_quantthresh__44cn1_sm_p7_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44cn1_sm_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_sm_p7_0 = { + _vq_quantthresh__44cn1_sm_p7_0, + _vq_quantmap__44cn1_sm_p7_0, + 13, + 13 +}; + +static static_codebook _44cn1_sm_p7_0 = { + 2, 169, + _vq_lengthlist__44cn1_sm_p7_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44cn1_sm_p7_0, + NULL, + &_vq_auxt__44cn1_sm_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_sm_p7_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44cn1_sm_p7_1[] = { + 2, 4, 4, 4, 5, 6, 5, 5, 5, 5, 6, 5, 5, 5, 5, 6, + 5, 5, 5, 5, 6, 6, 6, 5, 5, +}; + +static float _vq_quantthresh__44cn1_sm_p7_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44cn1_sm_p7_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_sm_p7_1 = { + _vq_quantthresh__44cn1_sm_p7_1, + _vq_quantmap__44cn1_sm_p7_1, + 5, + 5 +}; + +static static_codebook _44cn1_sm_p7_1 = { + 2, 25, + _vq_lengthlist__44cn1_sm_p7_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44cn1_sm_p7_1, + NULL, + &_vq_auxt__44cn1_sm_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_sm_p8_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44cn1_sm_p8_0[] = { + 1, 4, 4,12,11,13,13,14,14, 4, 7, 7,11,13,14,14, + 14,14, 3, 8, 3,14,14,14,14,14,14,14,10,12,14,14, + 14,14,14,14,14,14, 5,14, 8,14,14,14,14,14,12,14, + 13,14,14,14,14,14,14,14,13,14,10,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14, +}; + +static float _vq_quantthresh__44cn1_sm_p8_0[] = { + -773.5, -552.5, -331.5, -110.5, 110.5, 331.5, 552.5, 773.5, +}; + +static long _vq_quantmap__44cn1_sm_p8_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_sm_p8_0 = { + _vq_quantthresh__44cn1_sm_p8_0, + _vq_quantmap__44cn1_sm_p8_0, + 9, + 9 +}; + +static static_codebook _44cn1_sm_p8_0 = { + 2, 81, + _vq_lengthlist__44cn1_sm_p8_0, + 1, -516186112, 1627103232, 4, 0, + _vq_quantlist__44cn1_sm_p8_0, + NULL, + &_vq_auxt__44cn1_sm_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_sm_p8_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44cn1_sm_p8_1[] = { + 1, 4, 4, 6, 6, 8, 8, 9, 9,10,11,11,11, 6, 5, 5, + 7, 7, 8, 8,10,10,10,11,11,11, 6, 5, 5, 7, 7, 8, + 8,10,10,11,12,12,12,14, 7, 7, 7, 8, 9, 9,11,11, + 11,12,11,12,17, 7, 7, 8, 7, 9, 9,11,11,12,12,12, + 12,14,11,11, 8, 8,10,10,11,12,12,13,11,12,14,11, + 11, 8, 8,10,10,11,12,12,13,13,12,14,15,14,10,10, + 10,10,11,12,12,12,12,11,14,13,16,10,10,10, 9,12, + 11,12,12,13,14,14,15,14,14,13,10,10,11,11,12,11, + 13,11,14,12,15,13,14,11,10,12,10,12,12,13,13,13, + 13,14,15,15,12,12,11,11,12,11,13,12,14,14,14,14, + 17,12,12,11,10,13,11,13,13, +}; + +static float _vq_quantthresh__44cn1_sm_p8_1[] = { + -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, 25.5, + 42.5, 59.5, 76.5, 93.5, +}; + +static long _vq_quantmap__44cn1_sm_p8_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_sm_p8_1 = { + _vq_quantthresh__44cn1_sm_p8_1, + _vq_quantmap__44cn1_sm_p8_1, + 13, + 13 +}; + +static static_codebook _44cn1_sm_p8_1 = { + 2, 169, + _vq_lengthlist__44cn1_sm_p8_1, + 1, -522616832, 1620115456, 4, 0, + _vq_quantlist__44cn1_sm_p8_1, + NULL, + &_vq_auxt__44cn1_sm_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44cn1_sm_p8_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44cn1_sm_p8_2[] = { + 3, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9,10, 6, 6, 6, 6, 7, 7, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9,10, 6, 6, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9,10, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, + 9, 9, 9, 9,10,10,10, 7, 7, 7, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 9, 9,10,10,10, 8, 8, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 9,10,10,10, 8, 8, 8, 8, 8, 8, 9, + 9, 9, 9, 9, 9, 9, 9,11,10,11, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9,10,10,10,11,11, 8, 8, 8, + 8, 9, 9, 9, 9, 9, 9, 9, 9,11,10,11,11,11, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,11,10,11,11, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,11,11,10,11,11, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,11,10,11,11, + 11,11,11, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,11,11, + 11,11,11,11, 9,10,10,10, 9, 9, 9, 9, 9, 9,11,10, + 11,11,11,11,11, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,11, + 11,11,11,11,11,11,10,10, 9, 9, 9, 9, 9, 9, 9, 9, + 10,11,11,11,11,11,11,11,11, 9, 9, 9, 9, 9, 9, 9, + 9, +}; + +static float _vq_quantthresh__44cn1_sm_p8_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44cn1_sm_p8_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44cn1_sm_p8_2 = { + _vq_quantthresh__44cn1_sm_p8_2, + _vq_quantmap__44cn1_sm_p8_2, + 17, + 17 +}; + +static static_codebook _44cn1_sm_p8_2 = { + 2, 289, + _vq_lengthlist__44cn1_sm_p8_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44cn1_sm_p8_2, + NULL, + &_vq_auxt__44cn1_sm_p8_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44cn1_sm_short[] = { + 5, 6,12,14,12,14,16,17,18, 4, 2, 5,11, 7,10,12, + 14,15, 9, 4, 5,11, 7,10,13,15,18,15, 6, 7, 5, 6, + 8,11,13,16,11, 5, 6, 5, 5, 6, 9,13,15,12, 5, 7, + 6, 5, 6, 9,12,14,12, 6, 7, 8, 6, 7, 9,12,13,14, + 8, 8, 7, 5, 5, 8,10,12,16, 9, 9, 8, 6, 6, 7, 9, + 9, +}; + +static static_codebook _huff_book__44cn1_sm_short = { + 2, 81, + _huff_lengthlist__44cn1_sm_short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + diff --git a/Engine/lib/libvorbis/lib/books/floor/floor_books.h b/Engine/lib/libvorbis/lib/books/floor/floor_books.h new file mode 100644 index 000000000..e2e433403 --- /dev/null +++ b/Engine/lib/libvorbis/lib/books/floor/floor_books.h @@ -0,0 +1,1838 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: static codebooks autogenerated by huff/huffbuld + last modified: $Id: floor_books.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#include "codebook.h" + +static long _huff_lengthlist_line_256x7_0sub1[] = { + 0, 2, 3, 3, 3, 3, 4, 3, 4, +}; + +static static_codebook _huff_book_line_256x7_0sub1 = { + 1, 9, + _huff_lengthlist_line_256x7_0sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x7_0sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 3, 4, 3, 5, 3, + 6, 3, 6, 4, 6, 4, 7, 5, 7, +}; + +static static_codebook _huff_book_line_256x7_0sub2 = { + 1, 25, + _huff_lengthlist_line_256x7_0sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x7_0sub3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 5, 2, 5, 3, 5, 3, + 6, 3, 6, 4, 7, 6, 7, 8, 7, 9, 8, 9, 9, 9,10, 9, + 11,13,11,13,10,10,13,13,13,13,13,13,12,12,12,12, +}; + +static static_codebook _huff_book_line_256x7_0sub3 = { + 1, 64, + _huff_lengthlist_line_256x7_0sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x7_1sub1[] = { + 0, 3, 3, 3, 3, 2, 4, 3, 4, +}; + +static static_codebook _huff_book_line_256x7_1sub1 = { + 1, 9, + _huff_lengthlist_line_256x7_1sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x7_1sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 3, 4, 3, 4, 4, + 5, 4, 6, 5, 6, 7, 6, 8, 8, +}; + +static static_codebook _huff_book_line_256x7_1sub2 = { + 1, 25, + _huff_lengthlist_line_256x7_1sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x7_1sub3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 2, 4, 3, 6, 3, 7, + 3, 8, 5, 8, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, +}; + +static static_codebook _huff_book_line_256x7_1sub3 = { + 1, 64, + _huff_lengthlist_line_256x7_1sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x7_class0[] = { + 7, 5, 5, 9, 9, 6, 6, 9,12, 8, 7, 8,11, 8, 9,15, + 6, 3, 3, 7, 7, 4, 3, 6, 9, 6, 5, 6, 8, 6, 8,15, + 8, 5, 5, 9, 8, 5, 4, 6,10, 7, 5, 5,11, 8, 7,15, + 14,15,13,13,13,13, 8,11,15,10, 7, 6,11, 9,10,15, +}; + +static static_codebook _huff_book_line_256x7_class0 = { + 1, 64, + _huff_lengthlist_line_256x7_class0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x7_class1[] = { + 5, 6, 8,15, 6, 9,10,15,10,11,12,15,15,15,15,15, + 4, 6, 7,15, 6, 7, 8,15, 9, 8, 9,15,15,15,15,15, + 6, 8, 9,15, 7, 7, 8,15,10, 9,10,15,15,15,15,15, + 15,13,15,15,15,10,11,15,15,13,13,15,15,15,15,15, + 4, 6, 7,15, 6, 8, 9,15,10,10,12,15,15,15,15,15, + 2, 5, 6,15, 5, 6, 7,15, 8, 6, 7,15,15,15,15,15, + 5, 6, 8,15, 5, 6, 7,15, 9, 6, 7,15,15,15,15,15, + 14,12,13,15,12,10,11,15,15,15,15,15,15,15,15,15, + 7, 8, 9,15, 9,10,10,15,15,14,14,15,15,15,15,15, + 5, 6, 7,15, 7, 8, 9,15,12, 9,10,15,15,15,15,15, + 7, 7, 9,15, 7, 7, 8,15,12, 8, 9,15,15,15,15,15, + 13,13,14,15,12,11,12,15,15,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, + 13,13,13,15,15,15,15,15,15,15,15,15,15,15,15,15, + 15,12,13,15,15,12,13,15,15,14,15,15,15,15,15,15, + 15,15,15,15,15,15,13,15,15,15,15,15,15,15,15,15, +}; + +static static_codebook _huff_book_line_256x7_class1 = { + 1, 256, + _huff_lengthlist_line_256x7_class1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_512x17_0sub0[] = { + 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 6, 5, 6, 6, 6, 6, 5, 6, 6, 7, 6, 7, 6, 7, 6, + 7, 6, 8, 7, 8, 7, 8, 7, 8, 7, 8, 7, 9, 7, 9, 7, + 9, 7, 9, 8, 9, 8,10, 8,10, 8,10, 7,10, 6,10, 8, + 10, 8,11, 7,10, 7,11, 8,11,11,12,12,11,11,12,11, + 13,11,13,11,13,12,15,12,13,13,14,14,14,14,14,15, + 15,15,16,14,17,19,19,18,18,18,18,18,18,18,18,18, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +}; + +static static_codebook _huff_book_line_512x17_0sub0 = { + 1, 128, + _huff_lengthlist_line_512x17_0sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_512x17_1sub0[] = { + 2, 4, 5, 4, 5, 4, 5, 4, 5, 5, 5, 5, 5, 5, 6, 5, + 6, 5, 6, 6, 7, 6, 7, 6, 8, 7, 8, 7, 8, 7, 8, 7, +}; + +static static_codebook _huff_book_line_512x17_1sub0 = { + 1, 32, + _huff_lengthlist_line_512x17_1sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_512x17_1sub1[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 3, 5, 3, 5, 4, 5, 4, 5, 4, 5, 5, 5, 5, 6, 5, + 6, 5, 7, 5, 8, 6, 8, 6, 8, 6, 8, 6, 8, 7, 9, 7, + 9, 7,11, 9,11,11,12,11,14,12,14,16,14,16,13,16, + 14,16,12,15,13,16,14,16,13,14,12,15,13,15,13,13, + 13,15,12,14,14,15,13,15,12,15,15,15,15,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, +}; + +static static_codebook _huff_book_line_512x17_1sub1 = { + 1, 128, + _huff_lengthlist_line_512x17_1sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_512x17_2sub1[] = { + 0, 4, 5, 4, 4, 4, 5, 4, 4, 4, 5, 4, 5, 4, 5, 3, + 5, 3, +}; + +static static_codebook _huff_book_line_512x17_2sub1 = { + 1, 18, + _huff_lengthlist_line_512x17_2sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_512x17_2sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 3, 4, 3, 4, 4, 5, 4, 5, 4, 6, 4, 6, 5, + 6, 5, 7, 5, 7, 6, 8, 6, 8, 6, 8, 7, 8, 7, 9, 7, + 9, 8, +}; + +static static_codebook _huff_book_line_512x17_2sub2 = { + 1, 50, + _huff_lengthlist_line_512x17_2sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_512x17_2sub3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 3, 3, 3, 4, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 7, 8, 8,11, 8, 9, 9, 9,10,11,11,11, 9,10,10,11, + 11,11,11,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, +}; + +static static_codebook _huff_book_line_512x17_2sub3 = { + 1, 128, + _huff_lengthlist_line_512x17_2sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_512x17_3sub1[] = { + 0, 4, 4, 4, 4, 4, 4, 3, 4, 4, 4, 4, 4, 5, 4, 5, + 5, 5, +}; + +static static_codebook _huff_book_line_512x17_3sub1 = { + 1, 18, + _huff_lengthlist_line_512x17_3sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_512x17_3sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 3, 3, 4, 3, 5, 4, 6, 4, 6, 5, 7, 6, 7, + 6, 8, 6, 8, 7, 9, 8,10, 8,12, 9,13,10,15,10,15, + 11,14, +}; + +static static_codebook _huff_book_line_512x17_3sub2 = { + 1, 50, + _huff_lengthlist_line_512x17_3sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_512x17_3sub3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 8, 4, 8, 4, 8, 4, 8, 5, 8, 5, 8, 6, 8, + 4, 8, 4, 8, 5, 8, 5, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, +}; + +static static_codebook _huff_book_line_512x17_3sub3 = { + 1, 128, + _huff_lengthlist_line_512x17_3sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_512x17_class1[] = { + 1, 2, 3, 6, 5, 4, 7, 7, +}; + +static static_codebook _huff_book_line_512x17_class1 = { + 1, 8, + _huff_lengthlist_line_512x17_class1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_512x17_class2[] = { + 3, 3, 3,14, 5, 4, 4,11, 8, 6, 6,10,17,12,11,17, + 6, 5, 5,15, 5, 3, 4,11, 8, 5, 5, 8,16, 9,10,14, + 10, 8, 9,17, 8, 6, 6,13,10, 7, 7,10,16,11,13,14, + 17,17,17,17,17,16,16,16,16,15,16,16,16,16,16,16, +}; + +static static_codebook _huff_book_line_512x17_class2 = { + 1, 64, + _huff_lengthlist_line_512x17_class2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_512x17_class3[] = { + 2, 4, 6,17, 4, 5, 7,17, 8, 7,10,17,17,17,17,17, + 3, 4, 6,15, 3, 3, 6,15, 7, 6, 9,17,17,17,17,17, + 6, 8,10,17, 6, 6, 8,16, 9, 8,10,17,17,15,16,17, + 17,17,17,17,12,15,15,16,12,15,15,16,16,16,16,16, +}; + +static static_codebook _huff_book_line_512x17_class3 = { + 1, 64, + _huff_lengthlist_line_512x17_class3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x4_class0[] = { + 7, 7, 7,11, 6, 6, 7,11, 7, 6, 6,10,12,10,10,13, + 7, 7, 8,11, 7, 7, 7,11, 7, 6, 7,10,11,10,10,13, + 10,10, 9,12, 9, 9, 9,11, 8, 8, 8,11,13,11,10,14, + 15,15,14,15,15,14,13,14,15,12,12,17,17,17,17,17, + 7, 7, 6, 9, 6, 6, 6, 9, 7, 6, 6, 8,11,11,10,12, + 7, 7, 7, 9, 7, 6, 6, 9, 7, 6, 6, 9,13,10,10,11, + 10, 9, 8,10, 9, 8, 8,10, 8, 8, 7, 9,13,12,10,11, + 17,14,14,13,15,14,12,13,17,13,12,15,17,17,14,17, + 7, 6, 6, 7, 6, 6, 5, 7, 6, 6, 6, 6,11, 9, 9, 9, + 7, 7, 6, 7, 7, 6, 6, 7, 6, 6, 6, 6,10, 9, 8, 9, + 10, 9, 8, 8, 9, 8, 7, 8, 8, 7, 6, 8,11,10, 9,10, + 17,17,12,15,15,15,12,14,14,14,10,12,15,13,12,13, + 11,10, 8,10,11,10, 8, 8,10, 9, 7, 7,10, 9, 9,11, + 11,11, 9,10,11,10, 8, 9,10, 8, 6, 8,10, 9, 9,11, + 14,13,10,12,12,11,10,10, 8, 7, 8,10,10,11,11,12, + 17,17,15,17,17,17,17,17,17,13,12,17,17,17,14,17, +}; + +static static_codebook _huff_book_line_128x4_class0 = { + 1, 256, + _huff_lengthlist_line_128x4_class0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x4_0sub0[] = { + 2, 2, 2, 2, +}; + +static static_codebook _huff_book_line_128x4_0sub0 = { + 1, 4, + _huff_lengthlist_line_128x4_0sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x4_0sub1[] = { + 0, 0, 0, 0, 3, 2, 3, 2, 3, 3, +}; + +static static_codebook _huff_book_line_128x4_0sub1 = { + 1, 10, + _huff_lengthlist_line_128x4_0sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x4_0sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 4, 3, 4, 3, + 4, 4, 5, 4, 5, 4, 6, 5, 6, +}; + +static static_codebook _huff_book_line_128x4_0sub2 = { + 1, 25, + _huff_lengthlist_line_128x4_0sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x4_0sub3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 3, 5, 3, 5, 3, + 5, 4, 6, 5, 6, 5, 7, 6, 6, 7, 7, 9, 9,11,11,16, + 11,14,10,11,11,13,16,15,15,15,15,15,15,15,15,15, +}; + +static static_codebook _huff_book_line_128x4_0sub3 = { + 1, 64, + _huff_lengthlist_line_128x4_0sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x4_class0[] = { + 6, 7, 7,12, 6, 6, 7,12, 7, 6, 6,10,15,12,11,13, + 7, 7, 8,13, 7, 7, 8,12, 7, 7, 7,11,12,12,11,13, + 10, 9, 9,11, 9, 9, 9,10,10, 8, 8,12,14,12,12,14, + 11,11,12,14,11,12,11,15,15,12,13,15,15,15,15,15, + 6, 6, 7,10, 6, 6, 6,11, 7, 6, 6, 9,14,12,11,13, + 7, 7, 7,10, 6, 6, 7, 9, 7, 7, 6,10,13,12,10,12, + 9, 9, 9,11, 9, 9, 8, 9, 9, 8, 8,10,13,12,10,12, + 12,12,11,13,12,12,11,12,15,13,12,15,15,15,14,14, + 6, 6, 6, 8, 6, 6, 5, 6, 7, 7, 6, 5,11,10, 9, 8, + 7, 6, 6, 7, 6, 6, 5, 6, 7, 7, 6, 6,11,10, 9, 8, + 8, 8, 8, 9, 8, 8, 7, 8, 8, 8, 6, 7,11,10, 9, 9, + 14,11,10,14,14,11,10,15,13,11, 9,11,15,12,12,11, + 11, 9, 8, 8,10, 9, 8, 9,11,10, 9, 8,12,11,12,11, + 13,10, 8, 9,11,10, 8, 9,10, 9, 8, 9,10, 8,12,12, + 15,11,10,10,13,11,10,10, 8, 8, 7,12,10, 9,11,12, + 15,12,11,15,13,11,11,15,12,14,11,13,15,15,13,13, +}; + +static static_codebook _huff_book_line_256x4_class0 = { + 1, 256, + _huff_lengthlist_line_256x4_class0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x4_0sub0[] = { + 2, 2, 2, 2, +}; + +static static_codebook _huff_book_line_256x4_0sub0 = { + 1, 4, + _huff_lengthlist_line_256x4_0sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x4_0sub1[] = { + 0, 0, 0, 0, 2, 2, 3, 3, 3, 3, +}; + +static static_codebook _huff_book_line_256x4_0sub1 = { + 1, 10, + _huff_lengthlist_line_256x4_0sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x4_0sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 3, 4, 3, 4, 3, + 5, 3, 5, 4, 5, 4, 6, 4, 6, +}; + +static static_codebook _huff_book_line_256x4_0sub2 = { + 1, 25, + _huff_lengthlist_line_256x4_0sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x4_0sub3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 3, 5, 3, 5, 3, + 6, 4, 7, 4, 7, 5, 7, 6, 7, 6, 7, 8,10,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,12,12,12,12,12, +}; + +static static_codebook _huff_book_line_256x4_0sub3 = { + 1, 64, + _huff_lengthlist_line_256x4_0sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x7_class0[] = { + 10, 7, 8,13, 9, 6, 7,11,10, 8, 8,12,17,17,17,17, + 7, 5, 5, 9, 6, 4, 4, 8, 8, 5, 5, 8,16,14,13,16, + 7, 5, 5, 7, 6, 3, 3, 5, 8, 5, 4, 7,14,12,12,15, + 10, 7, 8, 9, 7, 5, 5, 6, 9, 6, 5, 5,15,12, 9,10, +}; + +static static_codebook _huff_book_line_128x7_class0 = { + 1, 64, + _huff_lengthlist_line_128x7_class0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x7_class1[] = { + 8,13,17,17, 8,11,17,17,11,13,17,17,17,17,17,17, + 6,10,16,17, 6,10,15,17, 8,10,16,17,17,17,17,17, + 9,13,15,17, 8,11,17,17,10,12,17,17,17,17,17,17, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 6,11,15,17, 7,10,15,17, 8,10,17,17,17,15,17,17, + 4, 8,13,17, 4, 7,13,17, 6, 8,15,17,16,15,17,17, + 6,11,15,17, 6, 9,13,17, 8,10,17,17,15,17,17,17, + 16,17,17,17,12,14,15,17,13,14,15,17,17,17,17,17, + 5,10,14,17, 5, 9,14,17, 7, 9,15,17,15,15,17,17, + 3, 7,12,17, 3, 6,11,17, 5, 7,13,17,12,12,17,17, + 5, 9,14,17, 3, 7,11,17, 5, 8,13,17,13,11,16,17, + 12,17,17,17, 9,14,15,17,10,11,14,17,16,14,17,17, + 8,12,17,17, 8,12,17,17,10,12,17,17,17,17,17,17, + 5,10,17,17, 5, 9,15,17, 7, 9,17,17,13,13,17,17, + 7,11,17,17, 6,10,15,17, 7, 9,15,17,12,11,17,17, + 12,15,17,17,11,14,17,17,11,10,15,17,17,16,17,17, +}; + +static static_codebook _huff_book_line_128x7_class1 = { + 1, 256, + _huff_lengthlist_line_128x7_class1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x7_0sub1[] = { + 0, 3, 3, 3, 3, 3, 3, 3, 3, +}; + +static static_codebook _huff_book_line_128x7_0sub1 = { + 1, 9, + _huff_lengthlist_line_128x7_0sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x7_0sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 4, 4, 4, 4, + 5, 4, 5, 4, 5, 4, 6, 4, 6, +}; + +static static_codebook _huff_book_line_128x7_0sub2 = { + 1, 25, + _huff_lengthlist_line_128x7_0sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x7_0sub3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 5, 3, 5, 3, 5, 4, + 5, 4, 5, 5, 5, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, + 7, 8, 9,11,13,13,13,13,13,13,13,13,13,13,13,13, +}; + +static static_codebook _huff_book_line_128x7_0sub3 = { + 1, 64, + _huff_lengthlist_line_128x7_0sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x7_1sub1[] = { + 0, 3, 3, 2, 3, 3, 4, 3, 4, +}; + +static static_codebook _huff_book_line_128x7_1sub1 = { + 1, 9, + _huff_lengthlist_line_128x7_1sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x7_1sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 3, 6, 3, 6, 3, + 6, 3, 7, 3, 8, 4, 9, 4, 9, +}; + +static static_codebook _huff_book_line_128x7_1sub2 = { + 1, 25, + _huff_lengthlist_line_128x7_1sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x7_1sub3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 7, 2, 7, 3, 8, 4, + 9, 5, 9, 8,10,11,11,12,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,13,13,13,13, +}; + +static static_codebook _huff_book_line_128x7_1sub3 = { + 1, 64, + _huff_lengthlist_line_128x7_1sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x11_class1[] = { + 1, 6, 3, 7, 2, 4, 5, 7, +}; + +static static_codebook _huff_book_line_128x11_class1 = { + 1, 8, + _huff_lengthlist_line_128x11_class1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x11_class2[] = { + 1, 6,12,16, 4,12,15,16, 9,15,16,16,16,16,16,16, + 2, 5,11,16, 5,11,13,16, 9,13,16,16,16,16,16,16, + 4, 8,12,16, 5, 9,12,16, 9,13,15,16,16,16,16,16, + 15,16,16,16,11,14,13,16,12,15,16,16,16,16,16,15, +}; + +static static_codebook _huff_book_line_128x11_class2 = { + 1, 64, + _huff_lengthlist_line_128x11_class2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x11_class3[] = { + 7, 6, 9,17, 7, 6, 8,17,12, 9,11,16,16,16,16,16, + 5, 4, 7,16, 5, 3, 6,14, 9, 6, 8,15,16,16,16,16, + 5, 4, 6,13, 3, 2, 4,11, 7, 4, 6,13,16,11,10,14, + 12,12,12,16, 9, 7,10,15,12, 9,11,16,16,15,15,16, +}; + +static static_codebook _huff_book_line_128x11_class3 = { + 1, 64, + _huff_lengthlist_line_128x11_class3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x11_0sub0[] = { + 5, 5, 5, 5, 5, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, + 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 6, 6, 6, 7, 6, + 7, 6, 7, 6, 7, 6, 7, 6, 7, 6, 8, 6, 8, 6, 8, 7, + 8, 7, 8, 7, 8, 7, 9, 7, 9, 8, 9, 8, 9, 8,10, 8, + 10, 9,10, 9,10, 9,11, 9,11, 9,10,10,11,10,11,10, + 11,11,11,11,11,11,12,13,14,14,14,15,15,16,16,16, + 17,15,16,15,16,16,17,17,16,17,17,17,17,17,17,17, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +}; + +static static_codebook _huff_book_line_128x11_0sub0 = { + 1, 128, + _huff_lengthlist_line_128x11_0sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x11_1sub0[] = { + 2, 5, 5, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 5, 6, 5, 6, 5, 7, 6, 7, 6, 7, 6, 8, 6, 8, 6, +}; + +static static_codebook _huff_book_line_128x11_1sub0 = { + 1, 32, + _huff_lengthlist_line_128x11_1sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x11_1sub1[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 3, 5, 3, 6, 4, 6, 4, 7, 4, 7, 4, 7, 4, 8, 4, + 8, 4, 9, 5, 9, 5, 9, 5, 9, 6,10, 6,10, 6,11, 7, + 10, 7,10, 8,11, 9,11, 9,11,10,11,11,12,11,11,12, + 15,15,12,14,11,14,12,14,11,14,13,14,12,14,11,14, + 11,14,12,14,11,14,11,14,13,13,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, +}; + +static static_codebook _huff_book_line_128x11_1sub1 = { + 1, 128, + _huff_lengthlist_line_128x11_1sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x11_2sub1[] = { + 0, 4, 5, 4, 5, 4, 5, 3, 5, 3, 5, 3, 5, 4, 4, 4, + 5, 5, +}; + +static static_codebook _huff_book_line_128x11_2sub1 = { + 1, 18, + _huff_lengthlist_line_128x11_2sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x11_2sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 3, 3, 4, 4, 4, 4, 5, 4, 5, 4, 6, 5, 7, + 5, 7, 6, 8, 6, 8, 6, 9, 7, 9, 7,10, 7, 9, 8,11, + 8,11, +}; + +static static_codebook _huff_book_line_128x11_2sub2 = { + 1, 50, + _huff_lengthlist_line_128x11_2sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x11_2sub3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 8, 3, 8, 4, 8, 4, 8, 6, 8, 5, 8, 4, 8, + 4, 8, 6, 8, 7, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, +}; + +static static_codebook _huff_book_line_128x11_2sub3 = { + 1, 128, + _huff_lengthlist_line_128x11_2sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x11_3sub1[] = { + 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 4, + 5, 4, +}; + +static static_codebook _huff_book_line_128x11_3sub1 = { + 1, 18, + _huff_lengthlist_line_128x11_3sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x11_3sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 3, 5, 4, 6, 4, 6, 4, 7, 4, 7, 4, 8, 4, + 8, 4, 9, 4, 9, 4,10, 4,10, 5,10, 5,11, 5,12, 6, + 12, 6, +}; + +static static_codebook _huff_book_line_128x11_3sub2 = { + 1, 50, + _huff_lengthlist_line_128x11_3sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x11_3sub3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 7, 1, 6, 3, 7, 3, 8, 4, 8, 5, 8, 8, 8, 9, + 7, 8, 8, 7, 7, 7, 8, 9,10, 9, 9,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10, 9, 9, +}; + +static static_codebook _huff_book_line_128x11_3sub3 = { + 1, 128, + _huff_lengthlist_line_128x11_3sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x17_class1[] = { + 1, 3, 4, 7, 2, 5, 6, 7, +}; + +static static_codebook _huff_book_line_128x17_class1 = { + 1, 8, + _huff_lengthlist_line_128x17_class1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x17_class2[] = { + 1, 4,10,19, 3, 8,13,19, 7,12,19,19,19,19,19,19, + 2, 6,11,19, 8,13,19,19, 9,11,19,19,19,19,19,19, + 6, 7,13,19, 9,13,19,19,10,13,18,18,18,18,18,18, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, +}; + +static static_codebook _huff_book_line_128x17_class2 = { + 1, 64, + _huff_lengthlist_line_128x17_class2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x17_class3[] = { + 3, 6,10,17, 4, 8,11,20, 8,10,11,20,20,20,20,20, + 2, 4, 8,18, 4, 6, 8,17, 7, 8,10,20,20,17,20,20, + 3, 5, 8,17, 3, 4, 6,17, 8, 8,10,17,17,12,16,20, + 13,13,15,20,10,10,12,20,15,14,15,20,20,20,19,19, +}; + +static static_codebook _huff_book_line_128x17_class3 = { + 1, 64, + _huff_lengthlist_line_128x17_class3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x17_0sub0[] = { + 5, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, + 7, 5, 7, 5, 7, 5, 7, 5, 7, 5, 7, 5, 8, 5, 8, 5, + 8, 5, 8, 5, 8, 6, 8, 6, 8, 6, 9, 6, 9, 6, 9, 6, + 9, 6, 9, 7, 9, 7, 9, 7, 9, 7,10, 7,10, 8,10, 8, + 10, 8,10, 8,10, 8,11, 8,11, 8,11, 8,11, 8,11, 9, + 12, 9,12, 9,12, 9,12, 9,12,10,12,10,13,11,13,11, + 14,12,14,13,15,14,16,14,17,15,18,16,20,20,20,20, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, +}; + +static static_codebook _huff_book_line_128x17_0sub0 = { + 1, 128, + _huff_lengthlist_line_128x17_0sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x17_1sub0[] = { + 2, 5, 5, 4, 5, 4, 5, 4, 5, 5, 5, 5, 5, 5, 6, 5, + 6, 5, 6, 5, 7, 6, 7, 6, 7, 6, 8, 6, 9, 7, 9, 7, +}; + +static static_codebook _huff_book_line_128x17_1sub0 = { + 1, 32, + _huff_lengthlist_line_128x17_1sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x17_1sub1[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 3, 5, 3, 5, 3, 6, 3, 6, 4, 6, 4, 7, 4, 7, 5, + 8, 5, 8, 6, 9, 7, 9, 7, 9, 8,10, 9,10, 9,11,10, + 11,11,11,11,11,11,12,12,12,13,12,13,12,14,12,15, + 12,14,12,16,13,17,13,17,14,17,14,16,13,17,14,17, + 14,17,15,17,15,15,16,17,17,17,17,17,17,17,17,17, + 17,17,17,17,17,17,16,16,16,16,16,16,16,16,16,16, +}; + +static static_codebook _huff_book_line_128x17_1sub1 = { + 1, 128, + _huff_lengthlist_line_128x17_1sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x17_2sub1[] = { + 0, 4, 5, 4, 6, 4, 8, 3, 9, 3, 9, 2, 9, 3, 8, 4, + 9, 4, +}; + +static static_codebook _huff_book_line_128x17_2sub1 = { + 1, 18, + _huff_lengthlist_line_128x17_2sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x17_2sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 1, 5, 3, 5, 3, 5, 4, 7, 5,10, 7,10, 7, + 12,10,14,10,14, 9,14,11,14,14,14,13,13,13,13,13, + 13,13, +}; + +static static_codebook _huff_book_line_128x17_2sub2 = { + 1, 50, + _huff_lengthlist_line_128x17_2sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x17_2sub3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, +}; + +static static_codebook _huff_book_line_128x17_2sub3 = { + 1, 128, + _huff_lengthlist_line_128x17_2sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x17_3sub1[] = { + 0, 4, 4, 4, 4, 4, 4, 4, 5, 3, 5, 3, 5, 4, 6, 4, + 6, 4, +}; + +static static_codebook _huff_book_line_128x17_3sub1 = { + 1, 18, + _huff_lengthlist_line_128x17_3sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x17_3sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 5, 3, 6, 3, 6, 4, 7, 4, 7, 4, 7, 4, 8, 4, + 8, 4, 8, 4, 8, 4, 9, 4, 9, 5,10, 5,10, 7,10, 8, + 10, 8, +}; + +static static_codebook _huff_book_line_128x17_3sub2 = { + 1, 50, + _huff_lengthlist_line_128x17_3sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_128x17_3sub3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 2, 4, 3, 4, 4, 4, 5, 4, 7, 5, 8, 5,11, + 6,10, 6,12, 7,12, 7,12, 8,12, 8,12,10,12,12,12, + 12,12,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, +}; + +static static_codebook _huff_book_line_128x17_3sub3 = { + 1, 128, + _huff_lengthlist_line_128x17_3sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_class1[] = { + 2,10, 8,14, 7,12,11,14, 1, 5, 3, 7, 4, 9, 7,13, +}; + +static static_codebook _huff_book_line_1024x27_class1 = { + 1, 16, + _huff_lengthlist_line_1024x27_class1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_class2[] = { + 1, 4, 2, 6, 3, 7, 5, 7, +}; + +static static_codebook _huff_book_line_1024x27_class2 = { + 1, 8, + _huff_lengthlist_line_1024x27_class2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_class3[] = { + 1, 5, 7,21, 5, 8, 9,21,10, 9,12,20,20,16,20,20, + 4, 8, 9,20, 6, 8, 9,20,11,11,13,20,20,15,17,20, + 9,11,14,20, 8,10,15,20,11,13,15,20,20,20,20,20, + 20,20,20,20,13,20,20,20,18,18,20,20,20,20,20,20, + 3, 6, 8,20, 6, 7, 9,20,10, 9,12,20,20,20,20,20, + 5, 7, 9,20, 6, 6, 9,20,10, 9,12,20,20,20,20,20, + 8,10,13,20, 8, 9,12,20,11,10,12,20,20,20,20,20, + 18,20,20,20,15,17,18,20,18,17,18,20,20,20,20,20, + 7,10,12,20, 8, 9,11,20,14,13,14,20,20,20,20,20, + 6, 9,12,20, 7, 8,11,20,12,11,13,20,20,20,20,20, + 9,11,15,20, 8,10,14,20,12,11,14,20,20,20,20,20, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 11,16,18,20,15,15,17,20,20,17,20,20,20,20,20,20, + 9,14,16,20,12,12,15,20,17,15,18,20,20,20,20,20, + 16,19,18,20,15,16,20,20,17,17,20,20,20,20,20,20, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, +}; + +static static_codebook _huff_book_line_1024x27_class3 = { + 1, 256, + _huff_lengthlist_line_1024x27_class3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_class4[] = { + 2, 3, 7,13, 4, 4, 7,15, 8, 6, 9,17,21,16,15,21, + 2, 5, 7,11, 5, 5, 7,14, 9, 7,10,16,17,15,16,21, + 4, 7,10,17, 7, 7, 9,15,11, 9,11,16,21,18,15,21, + 18,21,21,21,15,17,17,19,21,19,18,20,21,21,21,20, +}; + +static static_codebook _huff_book_line_1024x27_class4 = { + 1, 64, + _huff_lengthlist_line_1024x27_class4, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_0sub0[] = { + 5, 5, 5, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, + 6, 5, 6, 5, 6, 5, 6, 5, 7, 5, 7, 5, 7, 5, 7, 5, + 8, 6, 8, 6, 8, 6, 9, 6, 9, 6,10, 6,10, 6,11, 6, + 11, 7,11, 7,12, 7,12, 7,12, 7,12, 7,12, 7,12, 7, + 12, 7,12, 8,13, 8,12, 8,12, 8,13, 8,13, 9,13, 9, + 13, 9,13, 9,12,10,12,10,13,10,14,11,14,12,14,13, + 14,13,14,14,15,16,15,15,15,14,15,17,21,22,22,21, + 22,22,22,22,22,22,21,21,21,21,21,21,21,21,21,21, +}; + +static static_codebook _huff_book_line_1024x27_0sub0 = { + 1, 128, + _huff_lengthlist_line_1024x27_0sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_1sub0[] = { + 2, 5, 5, 4, 5, 4, 5, 4, 5, 4, 6, 5, 6, 5, 6, 5, + 6, 5, 7, 5, 7, 6, 8, 6, 8, 6, 8, 6, 9, 6, 9, 6, +}; + +static static_codebook _huff_book_line_1024x27_1sub0 = { + 1, 32, + _huff_lengthlist_line_1024x27_1sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_1sub1[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 5, 8, 4, 9, 4, 9, 4, 9, 4, 9, 4, 9, 4, 9, 4, + 9, 4, 9, 4, 9, 4, 8, 4, 8, 4, 9, 5, 9, 5, 9, 5, + 9, 5, 9, 6,10, 6,10, 7,10, 8,11, 9,11,11,12,13, + 12,14,13,15,13,15,14,16,14,17,15,17,15,15,16,16, + 15,16,16,16,15,18,16,15,17,17,19,19,19,19,19,19, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, +}; + +static static_codebook _huff_book_line_1024x27_1sub1 = { + 1, 128, + _huff_lengthlist_line_1024x27_1sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_2sub0[] = { + 1, 5, 5, 5, 5, 5, 5, 5, 6, 5, 6, 5, 6, 5, 6, 5, + 6, 6, 7, 7, 7, 7, 8, 7, 8, 8, 9, 8,10, 9,10, 9, +}; + +static static_codebook _huff_book_line_1024x27_2sub0 = { + 1, 32, + _huff_lengthlist_line_1024x27_2sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_2sub1[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 3, 4, 3, 4, 4, 5, 4, 5, 4, 5, 5, 6, 5, 6, 5, + 7, 5, 7, 6, 7, 6, 8, 7, 8, 7, 8, 7, 9, 8, 9, 9, + 9, 9,10,10,10,11, 9,12, 9,12, 9,15,10,14, 9,13, + 10,13,10,12,10,12,10,13,10,12,11,13,11,14,12,13, + 13,14,14,13,14,15,14,16,13,13,14,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,15,15, +}; + +static static_codebook _huff_book_line_1024x27_2sub1 = { + 1, 128, + _huff_lengthlist_line_1024x27_2sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_3sub1[] = { + 0, 4, 5, 4, 5, 3, 5, 3, 5, 3, 5, 4, 4, 4, 4, 5, + 5, 5, +}; + +static static_codebook _huff_book_line_1024x27_3sub1 = { + 1, 18, + _huff_lengthlist_line_1024x27_3sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_3sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 3, 4, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, + 5, 7, 5, 8, 6, 8, 6, 9, 7,10, 7,10, 8,10, 8,11, + 9,11, +}; + +static static_codebook _huff_book_line_1024x27_3sub2 = { + 1, 50, + _huff_lengthlist_line_1024x27_3sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_3sub3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 7, 3, 8, 3,10, 3, 8, 3, 9, 3, 8, 4, 9, + 4, 9, 5, 9, 6,10, 6, 9, 7,11, 7,12, 9,13,10,13, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +}; + +static static_codebook _huff_book_line_1024x27_3sub3 = { + 1, 128, + _huff_lengthlist_line_1024x27_3sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_4sub1[] = { + 0, 4, 5, 4, 5, 4, 5, 4, 5, 3, 5, 3, 5, 3, 5, 4, + 5, 4, +}; + +static static_codebook _huff_book_line_1024x27_4sub1 = { + 1, 18, + _huff_lengthlist_line_1024x27_4sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_4sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 2, 4, 2, 5, 3, 5, 4, 6, 6, 6, 7, 7, 8, + 7, 8, 7, 8, 7, 9, 8, 9, 8, 9, 8,10, 8,11, 9,12, + 9,12, +}; + +static static_codebook _huff_book_line_1024x27_4sub2 = { + 1, 50, + _huff_lengthlist_line_1024x27_4sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_1024x27_4sub3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 5, 2, 6, 3, 6, 4, 7, 4, 7, 5, 9, 5,11, + 6,11, 6,11, 7,11, 6,11, 6,11, 9,11, 8,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,10,10,10,10,10,10, +}; + +static static_codebook _huff_book_line_1024x27_4sub3 = { + 1, 128, + _huff_lengthlist_line_1024x27_4sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_class1[] = { + 2, 6, 8, 9, 7,11,13,13, 1, 3, 5, 5, 6, 6,12,10, +}; + +static static_codebook _huff_book_line_2048x27_class1 = { + 1, 16, + _huff_lengthlist_line_2048x27_class1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_class2[] = { + 1, 2, 3, 6, 4, 7, 5, 7, +}; + +static static_codebook _huff_book_line_2048x27_class2 = { + 1, 8, + _huff_lengthlist_line_2048x27_class2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_class3[] = { + 3, 3, 6,16, 5, 5, 7,16, 9, 8,11,16,16,16,16,16, + 5, 5, 8,16, 5, 5, 7,16, 8, 7, 9,16,16,16,16,16, + 9, 9,12,16, 6, 8,11,16, 9,10,11,16,16,16,16,16, + 16,16,16,16,13,16,16,16,15,16,16,16,16,16,16,16, + 5, 4, 7,16, 6, 5, 8,16, 9, 8,10,16,16,16,16,16, + 5, 5, 7,15, 5, 4, 6,15, 7, 6, 8,16,16,16,16,16, + 9, 9,11,15, 7, 7, 9,16, 8, 8, 9,16,16,16,16,16, + 16,16,16,16,15,15,15,16,15,15,14,16,16,16,16,16, + 8, 8,11,16, 8, 9,10,16,11,10,14,16,16,16,16,16, + 6, 8,10,16, 6, 7,10,16, 8, 8,11,16,14,16,16,16, + 10,11,14,16, 9, 9,11,16,10,10,11,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,15,16,16,16,16,16,16,16,16,16,16,16, + 12,16,15,16,12,14,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, +}; + +static static_codebook _huff_book_line_2048x27_class3 = { + 1, 256, + _huff_lengthlist_line_2048x27_class3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_class4[] = { + 2, 4, 7,13, 4, 5, 7,15, 8, 7,10,16,16,14,16,16, + 2, 4, 7,16, 3, 4, 7,14, 8, 8,10,16,16,16,15,16, + 6, 8,11,16, 7, 7, 9,16,11, 9,13,16,16,16,15,16, + 16,16,16,16,14,16,16,16,16,16,16,16,16,16,16,16, +}; + +static static_codebook _huff_book_line_2048x27_class4 = { + 1, 64, + _huff_lengthlist_line_2048x27_class4, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_0sub0[] = { + 5, 5, 5, 5, 5, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, + 6, 5, 7, 5, 7, 5, 7, 5, 8, 5, 8, 5, 8, 5, 9, 5, + 9, 6,10, 6,10, 6,11, 6,11, 6,11, 6,11, 6,11, 6, + 11, 6,11, 6,12, 7,11, 7,11, 7,11, 7,11, 7,10, 7, + 11, 7,11, 7,12, 7,11, 8,11, 8,11, 8,11, 8,13, 8, + 12, 9,11, 9,11, 9,11,10,12,10,12, 9,12,10,12,11, + 14,12,16,12,12,11,14,16,17,17,17,17,17,17,17,17, + 17,17,17,17,17,17,17,17,17,17,17,17,16,16,16,16, +}; + +static static_codebook _huff_book_line_2048x27_0sub0 = { + 1, 128, + _huff_lengthlist_line_2048x27_0sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_1sub0[] = { + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 6, 6, 6, 6, 6, 6, 7, 6, 7, 6, 7, 6, 7, 6, +}; + +static static_codebook _huff_book_line_2048x27_1sub0 = { + 1, 32, + _huff_lengthlist_line_2048x27_1sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_1sub1[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 5, 7, 5, 7, 4, 7, 4, 8, 4, 8, 4, 8, 4, 8, 3, + 8, 4, 9, 4, 9, 4, 9, 4, 9, 4, 9, 5, 9, 5, 9, 6, + 9, 7, 9, 8, 9, 9, 9,10, 9,11, 9,14, 9,15,10,15, + 10,15,10,15,10,15,11,15,10,14,12,14,11,14,13,14, + 13,15,15,15,12,15,15,15,13,15,13,15,13,15,15,15, + 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,14, +}; + +static static_codebook _huff_book_line_2048x27_1sub1 = { + 1, 128, + _huff_lengthlist_line_2048x27_1sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_2sub0[] = { + 2, 4, 5, 4, 5, 4, 5, 4, 5, 5, 5, 5, 5, 5, 6, 5, + 6, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, +}; + +static static_codebook _huff_book_line_2048x27_2sub0 = { + 1, 32, + _huff_lengthlist_line_2048x27_2sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_2sub1[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 4, 3, 4, 3, 4, 4, 5, 4, 5, 5, 5, 6, 6, 6, 7, + 6, 8, 6, 8, 6, 9, 7,10, 7,10, 7,10, 7,12, 7,12, + 7,12, 9,12,11,12,10,12,10,12,11,12,12,12,10,12, + 10,12,10,12, 9,12,11,12,12,12,12,12,11,12,11,12, + 12,12,12,12,12,12,12,12,10,10,12,12,12,12,12,10, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +}; + +static static_codebook _huff_book_line_2048x27_2sub1 = { + 1, 128, + _huff_lengthlist_line_2048x27_2sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_3sub1[] = { + 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, +}; + +static static_codebook _huff_book_line_2048x27_3sub1 = { + 1, 18, + _huff_lengthlist_line_2048x27_3sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_3sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, + 6, 7, 6, 7, 6, 8, 6, 9, 7, 9, 7, 9, 9,11, 9,12, + 10,12, +}; + +static static_codebook _huff_book_line_2048x27_3sub2 = { + 1, 50, + _huff_lengthlist_line_2048x27_3sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_3sub3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 6, 3, 7, 3, 7, 5, 7, 7, 7, 7, 7, 6, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, +}; + +static static_codebook _huff_book_line_2048x27_3sub3 = { + 1, 128, + _huff_lengthlist_line_2048x27_3sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_4sub1[] = { + 0, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 4, 5, 4, 5, 4, + 4, 5, +}; + +static static_codebook _huff_book_line_2048x27_4sub1 = { + 1, 18, + _huff_lengthlist_line_2048x27_4sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_4sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 2, 4, 3, 4, 4, 4, 5, 5, 6, 5, 6, 5, 7, + 6, 6, 6, 7, 7, 7, 8, 9, 9, 9,12,10,11,10,10,12, + 10,10, +}; + +static static_codebook _huff_book_line_2048x27_4sub2 = { + 1, 50, + _huff_lengthlist_line_2048x27_4sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_2048x27_4sub3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 6, 5, 7, 5, 7, 7, 7, 7, 7, 5, 7, 5, 7, + 5, 7, 5, 7, 7, 7, 7, 7, 4, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, +}; + +static static_codebook _huff_book_line_2048x27_4sub3 = { + 1, 128, + _huff_lengthlist_line_2048x27_4sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x4low_class0[] = { + 4, 5, 6,11, 5, 5, 6,10, 7, 7, 6, 6,14,13, 9, 9, + 6, 6, 6,10, 6, 6, 6, 9, 8, 7, 7, 9,14,12, 8,11, + 8, 7, 7,11, 8, 8, 7,11, 9, 9, 7, 9,13,11, 9,13, + 19,19,18,19,15,16,16,19,11,11,10,13,10,10, 9,15, + 5, 5, 6,13, 6, 6, 6,11, 8, 7, 6, 7,14,11,10,11, + 6, 6, 6,12, 7, 6, 6,11, 8, 7, 7,11,13,11, 9,11, + 9, 7, 6,12, 8, 7, 6,12, 9, 8, 8,11,13,10, 7,13, + 19,19,17,19,17,14,14,19,12,10, 8,12,13,10, 9,16, + 7, 8, 7,12, 7, 7, 7,11, 8, 7, 7, 8,12,12,11,11, + 8, 8, 7,12, 8, 7, 6,11, 8, 7, 7,10,10,11,10,11, + 9, 8, 8,13, 9, 8, 7,12,10, 9, 7,11, 9, 8, 7,11, + 18,18,15,18,18,16,17,18,15,11,10,18,11, 9, 9,18, + 16,16,13,16,12,11,10,16,12,11, 9, 6,15,12,11,13, + 16,16,14,14,13,11,12,16,12, 9, 9,13,13,10,10,12, + 17,18,17,17,14,15,14,16,14,12,14,15,12,10,11,12, + 18,18,18,18,18,18,18,18,18,12,13,18,16,11, 9,18, +}; + +static static_codebook _huff_book_line_256x4low_class0 = { + 1, 256, + _huff_lengthlist_line_256x4low_class0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x4low_0sub0[] = { + 1, 3, 2, 3, +}; + +static static_codebook _huff_book_line_256x4low_0sub0 = { + 1, 4, + _huff_lengthlist_line_256x4low_0sub0, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x4low_0sub1[] = { + 0, 0, 0, 0, 2, 3, 2, 3, 3, 3, +}; + +static static_codebook _huff_book_line_256x4low_0sub1 = { + 1, 10, + _huff_lengthlist_line_256x4low_0sub1, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x4low_0sub2[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 4, 3, 4, + 4, 4, 4, 4, 5, 5, 5, 6, 6, +}; + +static static_codebook _huff_book_line_256x4low_0sub2 = { + 1, 25, + _huff_lengthlist_line_256x4low_0sub2, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist_line_256x4low_0sub3[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 2, 4, 3, 5, 4, + 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 7, 8, 6, 9, + 7,12,11,16,13,16,12,15,13,15,12,14,12,15,15,15, +}; + +static static_codebook _huff_book_line_256x4low_0sub3 = { + 1, 64, + _huff_lengthlist_line_256x4low_0sub3, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + diff --git a/Engine/lib/libvorbis/lib/books/uncoupled/res_books_uncoupled.h b/Engine/lib/libvorbis/lib/books/uncoupled/res_books_uncoupled.h new file mode 100644 index 000000000..83a001879 --- /dev/null +++ b/Engine/lib/libvorbis/lib/books/uncoupled/res_books_uncoupled.h @@ -0,0 +1,11517 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: static codebooks autogenerated by huff/huffbuld + last modified: $Id: res_books_uncoupled.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#include "codebook.h" +static long _vq_quantlist__16u0__p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16u0__p1_0[] = { + 1, 4, 4, 5, 7, 7, 5, 7, 8, 5, 8, 8, 8,10,10, 8, + 10,11, 5, 8, 8, 8,10,10, 8,10,10, 4, 9, 9, 9,12, + 11, 8,11,11, 8,12,11,10,12,14,10,13,13, 7,11,11, + 10,14,12,11,14,14, 4, 9, 9, 8,11,11, 9,11,12, 7, + 11,11,10,13,14,10,12,14, 8,11,12,10,14,14,10,13, + 12, +}; + +static float _vq_quantthresh__16u0__p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__16u0__p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16u0__p1_0 = { + _vq_quantthresh__16u0__p1_0, + _vq_quantmap__16u0__p1_0, + 3, + 3 +}; + +static static_codebook _16u0__p1_0 = { + 4, 81, + _vq_lengthlist__16u0__p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__16u0__p1_0, + NULL, + &_vq_auxt__16u0__p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u0__p2_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16u0__p2_0[] = { + 2, 4, 4, 5, 6, 6, 5, 6, 6, 5, 7, 7, 7, 8, 9, 7, + 8, 9, 5, 7, 7, 7, 9, 8, 7, 9, 7, 4, 7, 7, 7, 9, + 9, 7, 8, 8, 6, 9, 8, 7, 8,11, 9,11,10, 6, 8, 9, + 8,11, 8, 9,10,11, 4, 7, 7, 7, 8, 8, 7, 9, 9, 6, + 9, 8, 9,11,10, 8, 8,11, 6, 8, 9, 9,10,11, 8,11, + 8, +}; + +static float _vq_quantthresh__16u0__p2_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__16u0__p2_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16u0__p2_0 = { + _vq_quantthresh__16u0__p2_0, + _vq_quantmap__16u0__p2_0, + 3, + 3 +}; + +static static_codebook _16u0__p2_0 = { + 4, 81, + _vq_lengthlist__16u0__p2_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__16u0__p2_0, + NULL, + &_vq_auxt__16u0__p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u0__p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16u0__p3_0[] = { + 1, 5, 5, 7, 7, 6, 7, 7, 8, 8, 6, 7, 8, 8, 8, 8, + 9, 9,11,11, 8, 9, 9,11,11, 6, 9, 8,10,10, 8,10, + 10,11,11, 8,10,10,11,11,10,11,10,13,12, 9,11,10, + 13,13, 6, 8, 9,10,10, 8,10,10,11,11, 8,10,10,11, + 11, 9,10,11,13,12,10,10,11,12,12, 8,11,11,14,13, + 10,12,11,15,13, 9,12,11,15,14,12,14,13,16,14,12, + 13,13,17,14, 8,11,11,13,14, 9,11,12,14,15,10,11, + 12,13,15,11,13,13,14,16,12,13,14,14,16, 5, 9, 9, + 11,11, 9,11,11,12,12, 8,11,11,12,12,11,12,12,15, + 14,10,12,12,15,15, 8,11,11,13,12,10,12,12,13,13, + 10,12,12,14,13,12,12,13,14,15,11,13,13,17,16, 7, + 11,11,13,13,10,12,12,14,13,10,12,12,13,14,12,13, + 12,15,14,11,13,13,15,14, 9,12,12,16,15,11,13,13, + 17,16,10,13,13,16,16,13,14,15,15,16,13,15,14,19, + 17, 9,12,12,14,16,11,13,13,15,16,10,13,13,17,16, + 13,14,13,17,15,12,15,15,16,17, 5, 9, 9,11,11, 8, + 11,11,13,12, 9,11,11,12,12,10,12,12,14,15,11,12, + 12,14,14, 7,11,10,13,12,10,12,12,14,13,10,11,12, + 13,13,11,13,13,15,16,12,12,13,15,15, 7,11,11,13, + 13,10,13,13,14,14,10,12,12,13,13,11,13,13,16,15, + 12,13,13,15,14, 9,12,12,15,15,10,13,13,17,16,11, + 12,13,15,15,12,15,14,18,18,13,14,14,16,17, 9,12, + 12,15,16,10,13,13,15,16,11,13,13,15,16,13,15,15, + 17,17,13,15,14,16,15, 7,11,11,15,16,10,13,12,16, + 17,10,12,13,15,17,15,16,16,18,17,13,15,15,17,18, + 8,12,12,16,16,11,13,14,17,18,11,13,13,18,16,15, + 17,16,17,19,14,15,15,17,16, 8,12,12,16,15,11,14, + 13,18,17,11,13,14,18,17,15,16,16,18,17,13,16,16, + 18,18,11,15,14,18,17,13,14,15,18, 0,12,15,15, 0, + 17,17,16,17,17,18,14,16,18,18, 0,11,14,14,17, 0, + 12,15,14,17,19,12,15,14,18, 0,15,18,16, 0,17,14, + 18,16,18, 0, 7,11,11,16,15,10,12,12,18,16,10,13, + 13,16,15,13,15,14,17,17,14,16,16,19,18, 8,12,12, + 16,16,11,13,13,18,16,11,13,14,17,16,14,15,15,19, + 18,15,16,16, 0,19, 8,12,12,16,17,11,13,13,17,17, + 11,14,13,17,17,13,15,15,17,19,15,17,17,19, 0,11, + 14,15,19,17,12,15,16,18,18,12,14,15,19,17,14,16, + 17, 0,18,16,16,19,17, 0,11,14,14,18,19,12,15,14, + 17,17,13,16,14,17,16,14,17,16,18,18,15,18,15, 0, + 18, +}; + +static float _vq_quantthresh__16u0__p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16u0__p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16u0__p3_0 = { + _vq_quantthresh__16u0__p3_0, + _vq_quantmap__16u0__p3_0, + 5, + 5 +}; + +static static_codebook _16u0__p3_0 = { + 4, 625, + _vq_lengthlist__16u0__p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16u0__p3_0, + NULL, + &_vq_auxt__16u0__p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u0__p4_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16u0__p4_0[] = { + 3, 5, 5, 8, 8, 6, 6, 6, 9, 9, 6, 6, 6, 9, 9, 9, + 10, 9,11,11, 9, 9, 9,11,11, 6, 7, 7,10,10, 7, 7, + 8,10,10, 7, 7, 8,10,10,10,10,10,11,12, 9,10,10, + 11,12, 6, 7, 7,10,10, 7, 8, 7,10,10, 7, 8, 7,10, + 10,10,11,10,12,11,10,10,10,13,10, 9,10,10,12,12, + 10,11,10,14,12, 9,11,11,13,13,11,12,13,13,13,11, + 12,12,15,13, 9,10,10,12,13, 9,11,10,12,13,10,10, + 11,12,13,11,12,12,12,13,11,12,12,13,13, 5, 7, 7, + 10,10, 7, 8, 8,10,10, 7, 8, 8,10,10,10,11,10,12, + 13,10,10,11,12,12, 6, 8, 8,11,10, 7, 8, 9,10,12, + 8, 9, 9,11,11,11,10,11,11,12,10,11,11,13,12, 7, + 8, 8,10,11, 8, 9, 8,11,10, 8, 9, 9,11,11,10,12, + 10,13,11,10,11,11,13,13,10,11,10,14,13,10,10,11, + 13,13,10,12,11,14,13,12,11,13,12,13,13,12,13,14, + 14,10,11,11,13,13,10,11,10,12,13,10,12,12,12,14, + 12,12,12,14,12,12,13,12,17,15, 5, 7, 7,10,10, 7, + 8, 8,10,10, 7, 8, 8,11,10,10,10,11,12,12,10,11, + 11,12,13, 6, 8, 8,11,10, 8, 9, 9,11,11, 7, 8, 9, + 10,11,11,11,11,12,12,10,10,11,12,13, 6, 8, 8,10, + 11, 8, 9, 9,11,11, 7, 9, 7,11,10,10,12,12,13,13, + 11,11,10,13,11, 9,11,10,14,13,11,11,11,15,13,10, + 10,11,13,13,12,13,13,14,14,12,11,12,12,13,10,11, + 11,12,13,10,11,12,13,13,10,11,10,13,12,12,12,13, + 14, 0,12,13,11,13,11, 8,10,10,13,13,10,11,11,14, + 13,10,11,11,13,12,13,14,14,14,15,12,12,12,15,14, + 9,11,10,13,12,10,10,11,13,14,11,11,11,15,12,13, + 12,14,15,16,13,13,13,14,13, 9,11,11,12,12,10,12, + 11,13,13,10,11,11,13,14,13,13,13,15,15,13,13,14, + 17,15,11,12,12,14,14,10,11,12,13,15,12,13,13, 0, + 15,13,11,14,12,16,14,16,14, 0,15,11,12,12,14,16, + 11,13,12,16,15,12,13,13,14,15,12,14,12,15,13,15, + 14,14,16,16, 8,10,10,13,13,10,11,10,13,14,10,11, + 11,13,13,13,13,12,14,14,14,13,13,16,17, 9,10,10, + 12,14,10,12,11,14,13,10,11,12,13,14,12,12,12,15, + 15,13,13,13,14,14, 9,10,10,13,13,10,11,12,12,14, + 10,11,10,13,13,13,13,13,14,16,13,13,13,14,14,11, + 12,13,15,13,12,14,13,14,16,12,12,13,13,14,13,14, + 14,17,15,13,12,17,13,16,11,12,13,14,15,12,13,14, + 14,17,11,12,11,14,14,13,16,14,16, 0,14,15,11,15, + 11, +}; + +static float _vq_quantthresh__16u0__p4_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16u0__p4_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16u0__p4_0 = { + _vq_quantthresh__16u0__p4_0, + _vq_quantmap__16u0__p4_0, + 5, + 5 +}; + +static static_codebook _16u0__p4_0 = { + 4, 625, + _vq_lengthlist__16u0__p4_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16u0__p4_0, + NULL, + &_vq_auxt__16u0__p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u0__p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__16u0__p5_0[] = { + 1, 4, 4, 7, 7, 7, 7, 9, 9, 4, 6, 6, 8, 8, 8, 8, + 9, 9, 4, 6, 6, 8, 8, 8, 8, 9, 9, 7, 8, 8, 9, 9, + 9, 9,11,10, 7, 8, 8, 9, 9, 9, 9,10,11, 7, 8, 8, + 9, 9,10,10,11,11, 7, 8, 8, 9, 9,10,10,11,11, 9, + 9, 9,10,10,11,11,12,12, 9, 9, 9,10,10,11,11,12, + 12, +}; + +static float _vq_quantthresh__16u0__p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__16u0__p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__16u0__p5_0 = { + _vq_quantthresh__16u0__p5_0, + _vq_quantmap__16u0__p5_0, + 9, + 9 +}; + +static static_codebook _16u0__p5_0 = { + 2, 81, + _vq_lengthlist__16u0__p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__16u0__p5_0, + NULL, + &_vq_auxt__16u0__p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u0__p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__16u0__p6_0[] = { + 1, 4, 4, 7, 7,10,10,12,12,13,13,18,17, 3, 6, 6, + 9, 9,11,11,13,13,14,14,18,17, 3, 6, 6, 9, 9,11, + 11,13,13,14,14,17,18, 7, 9, 9,11,11,13,13,14,14, + 15,15, 0, 0, 7, 9, 9,11,11,13,13,14,14,15,16,19, + 18,10,11,11,13,13,14,14,16,15,17,18, 0, 0,10,11, + 11,13,13,14,14,15,15,16,18, 0, 0,11,13,13,14,14, + 15,15,17,17, 0,19, 0, 0,11,13,13,14,14,14,15,16, + 18, 0,19, 0, 0,13,14,14,15,15,18,17,18,18, 0,19, + 0, 0,13,14,14,15,16,16,16,18,18,19, 0, 0, 0,16, + 17,17, 0,17,19,19, 0,19, 0, 0, 0, 0,16,19,16,17, + 18, 0,19, 0, 0, 0, 0, 0, 0, +}; + +static float _vq_quantthresh__16u0__p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__16u0__p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__16u0__p6_0 = { + _vq_quantthresh__16u0__p6_0, + _vq_quantmap__16u0__p6_0, + 13, + 13 +}; + +static static_codebook _16u0__p6_0 = { + 2, 169, + _vq_lengthlist__16u0__p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__16u0__p6_0, + NULL, + &_vq_auxt__16u0__p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u0__p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16u0__p6_1[] = { + 1, 4, 5, 6, 6, 4, 6, 6, 6, 6, 4, 6, 6, 6, 6, 6, + 6, 6, 7, 7, 6, 6, 6, 7, 7, +}; + +static float _vq_quantthresh__16u0__p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16u0__p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16u0__p6_1 = { + _vq_quantthresh__16u0__p6_1, + _vq_quantmap__16u0__p6_1, + 5, + 5 +}; + +static static_codebook _16u0__p6_1 = { + 2, 25, + _vq_lengthlist__16u0__p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16u0__p6_1, + NULL, + &_vq_auxt__16u0__p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__16u0__p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16u0__p7_0[] = { + 1, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, +}; + +static float _vq_quantthresh__16u0__p7_0[] = { + -157.5, 157.5, +}; + +static long _vq_quantmap__16u0__p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16u0__p7_0 = { + _vq_quantthresh__16u0__p7_0, + _vq_quantmap__16u0__p7_0, + 3, + 3 +}; + +static static_codebook _16u0__p7_0 = { + 4, 81, + _vq_lengthlist__16u0__p7_0, + 1, -518803456, 1628680192, 2, 0, + _vq_quantlist__16u0__p7_0, + NULL, + &_vq_auxt__16u0__p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u0__p7_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__16u0__p7_1[] = { + 1, 5, 5, 6, 5, 9,10,11,11,10,10,10,10,10,10, 5, + 8, 8, 8,10,10,10,10,10,10,10,10,10,10,10, 5, 8, + 9, 9, 9,10,10,10,10,10,10,10,10,10,10, 5,10, 8, + 10,10,10,10,10,10,10,10,10,10,10,10, 4, 8, 9,10, + 10,10,10,10,10,10,10,10,10,10,10, 9,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10, 9,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10, +}; + +static float _vq_quantthresh__16u0__p7_1[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__16u0__p7_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__16u0__p7_1 = { + _vq_quantthresh__16u0__p7_1, + _vq_quantmap__16u0__p7_1, + 15, + 15 +}; + +static static_codebook _16u0__p7_1 = { + 2, 225, + _vq_lengthlist__16u0__p7_1, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__16u0__p7_1, + NULL, + &_vq_auxt__16u0__p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__16u0__p7_2[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__16u0__p7_2[] = { + 1, 6, 6, 7, 8, 7, 7,10, 9,10, 9,11,10, 9,11,10, + 9, 9, 9, 9,10, 6, 8, 7, 9, 9, 8, 8,10,10, 9,11, + 11,12,12,10, 9,11, 9,12,10, 9, 6, 9, 8, 9,12, 8, + 8,11, 9,11,11,12,11,12,12,10,11,11,10,10,11, 7, + 10, 9, 9, 9, 9, 9,10, 9,10, 9,10,10,12,10,10,10, + 11,12,10,10, 7, 9, 9, 9,10, 9, 9,10,10, 9, 9, 9, + 11,11,10,10,10,10, 9, 9,12, 7, 9,10, 9,11, 9,10, + 9,10,11,11,11,10,11,12, 9,12,11,10,10,10, 7, 9, + 9, 9, 9,10,12,10, 9,11,12,10,11,12,12,11, 9,10, + 11,10,11, 7, 9,10,10,11,10, 9,10,11,11,11,10,12, + 12,12,11,11,10,11,11,12, 8, 9,10,12,11,10,10,12, + 12,12,12,12,10,11,11, 9,11,10,12,11,11, 8, 9,10, + 10,11,12,11,11,10,10,10,12,12,12, 9,10,12,12,12, + 12,12, 8,10,11,10,10,12, 9,11,12,12,11,12,12,12, + 12,10,12,10,10,10,10, 8,12,11,11,11,10,10,11,12, + 12,12,12,11,12,12,12,11,11,11,12,10, 9,10,10,12, + 10,12,10,12,12,10,10,10,11,12,12,12,11,12,12,12, + 11,10,11,12,12,12,11,12,12,11,12,12,11,12,12,12, + 12,11,12,12,10,10,10,10,11,11,12,11,12,12,12,12, + 12,12,12,11,12,11,10,11,11,12,11,11, 9,10,10,10, + 12,10,10,11, 9,11,12,11,12,11,12,12,10,11,10,12, + 9, 9, 9,12,11,10,11,10,12,10,12,10,12,12,12,11, + 11,11,11,11,10, 9,10,10,11,10,11,11,12,11,10,11, + 12,12,12,11,11, 9,12,10,12, 9,10,12,10,10,11,10, + 11,11,12,11,10,11,10,11,11,11,11,12,11,11,10, 9, + 10,10,10, 9,11,11,10, 9,12,10,11,12,11,12,12,11, + 12,11,12,11,10,11,10,12,11,12,11,12,11,12,10,11, + 10,10,12,11,10,11,11,11,10, +}; + +static float _vq_quantthresh__16u0__p7_2[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__16u0__p7_2[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__16u0__p7_2 = { + _vq_quantthresh__16u0__p7_2, + _vq_quantmap__16u0__p7_2, + 21, + 21 +}; + +static static_codebook _16u0__p7_2 = { + 2, 441, + _vq_lengthlist__16u0__p7_2, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__16u0__p7_2, + NULL, + &_vq_auxt__16u0__p7_2, + NULL, + 0 +}; + +static long _huff_lengthlist__16u0__single[] = { + 3, 5, 8, 7,14, 8, 9,19, 5, 2, 5, 5, 9, 6, 9,19, + 8, 4, 5, 7, 8, 9,13,19, 7, 4, 6, 5, 9, 6, 9,19, + 12, 8, 7, 9,10,11,13,19, 8, 5, 8, 6, 9, 6, 7,19, + 8, 8,10, 7, 7, 4, 5,19,12,17,19,15,18,13,11,18, +}; + +static static_codebook _huff_book__16u0__single = { + 2, 64, + _huff_lengthlist__16u0__single, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__16u1__long[] = { + 3, 6,10, 8,12, 8,14, 8,14,19, 5, 3, 5, 5, 7, 6, + 11, 7,16,19, 7, 5, 6, 7, 7, 9,11,12,19,19, 6, 4, + 7, 5, 7, 6,10, 7,18,18, 8, 6, 7, 7, 7, 7, 8, 9, + 18,18, 7, 5, 8, 5, 7, 5, 8, 6,18,18,12, 9,10, 9, + 9, 9, 8, 9,18,18, 8, 7,10, 6, 8, 5, 6, 4,11,18, + 11,15,16,12,11, 8, 8, 6, 9,18,14,18,18,18,16,16, + 16,13,16,18, +}; + +static static_codebook _huff_book__16u1__long = { + 2, 100, + _huff_lengthlist__16u1__long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__16u1__p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16u1__p1_0[] = { + 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 8, 7, 7,10,10, 7, + 9,10, 5, 7, 8, 7,10, 9, 7,10,10, 5, 8, 8, 8,10, + 10, 8,10,10, 7,10,10,10,11,12,10,12,13, 7,10,10, + 9,13,11,10,12,13, 5, 8, 8, 8,10,10, 8,10,10, 7, + 10,10,10,12,12, 9,11,12, 7,10,11,10,12,12,10,13, + 11, +}; + +static float _vq_quantthresh__16u1__p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__16u1__p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16u1__p1_0 = { + _vq_quantthresh__16u1__p1_0, + _vq_quantmap__16u1__p1_0, + 3, + 3 +}; + +static static_codebook _16u1__p1_0 = { + 4, 81, + _vq_lengthlist__16u1__p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__16u1__p1_0, + NULL, + &_vq_auxt__16u1__p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u1__p2_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16u1__p2_0[] = { + 3, 4, 4, 5, 6, 6, 5, 6, 6, 5, 6, 6, 6, 7, 8, 6, + 7, 8, 5, 6, 6, 6, 8, 7, 6, 8, 7, 5, 6, 6, 6, 8, + 8, 6, 8, 8, 6, 8, 8, 7, 7,10, 8, 9, 9, 6, 8, 8, + 7, 9, 8, 8, 9,10, 5, 6, 6, 6, 8, 8, 7, 8, 8, 6, + 8, 8, 8,10, 9, 7, 8, 9, 6, 8, 8, 8, 9, 9, 7,10, + 8, +}; + +static float _vq_quantthresh__16u1__p2_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__16u1__p2_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16u1__p2_0 = { + _vq_quantthresh__16u1__p2_0, + _vq_quantmap__16u1__p2_0, + 3, + 3 +}; + +static static_codebook _16u1__p2_0 = { + 4, 81, + _vq_lengthlist__16u1__p2_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__16u1__p2_0, + NULL, + &_vq_auxt__16u1__p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u1__p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16u1__p3_0[] = { + 1, 5, 5, 8, 8, 6, 7, 7, 9, 9, 5, 7, 7, 9, 9, 9, + 10, 9,11,11, 9, 9,10,11,11, 6, 8, 8,10,10, 8, 9, + 10,11,11, 8, 9,10,11,11,10,11,11,12,13,10,11,11, + 13,13, 6, 8, 8,10,10, 8,10, 9,11,11, 8,10, 9,11, + 11,10,11,11,13,13,10,11,11,13,12, 9,11,11,14,13, + 10,12,12,15,14,10,12,11,14,13,12,13,13,15,15,12, + 13,13,16,14, 9,11,11,13,14,10,11,12,14,14,10,12, + 12,14,15,12,13,13,14,15,12,13,14,15,16, 5, 8, 8, + 11,11, 8,10,10,12,12, 8,10,10,12,12,11,12,12,14, + 14,11,12,12,14,14, 8,10,10,12,12, 9,11,12,12,13, + 10,12,12,13,13,12,12,13,14,15,11,13,13,15,15, 7, + 10,10,12,12, 9,12,11,13,12,10,11,12,13,13,12,13, + 12,15,14,11,12,13,15,15,10,12,12,15,14,11,13,13, + 16,15,11,13,13,16,15,14,13,14,15,16,13,15,15,17, + 17,10,12,12,14,15,11,12,12,15,15,11,13,13,15,16, + 13,15,13,16,15,13,15,15,16,17, 5, 8, 8,11,11, 8, + 10,10,12,12, 8,10,10,12,12,11,12,12,14,14,11,12, + 12,14,14, 7,10,10,12,12,10,12,12,14,13, 9,11,12, + 12,13,12,13,13,15,15,12,12,13,13,15, 7,10,10,12, + 13,10,11,12,13,13,10,12,11,13,13,11,13,13,15,15, + 12,13,12,15,14, 9,12,12,15,14,11,13,13,15,15,11, + 12,13,15,15,13,14,14,17,19,13,13,14,16,16,10,12, + 12,14,15,11,13,13,15,16,11,13,12,16,15,13,15,15, + 17,18,14,15,13,16,15, 8,11,11,15,14,10,12,12,16, + 15,10,12,12,16,16,14,15,15,18,17,13,14,15,16,18, + 9,12,12,15,15,11,12,14,16,17,11,13,13,16,15,15, + 15,15,17,18,14,15,16,17,17, 9,12,12,15,15,11,14, + 13,16,16,11,13,13,16,16,15,16,15,17,18,14,16,15, + 17,16,12,14,14,17,16,12,14,15,18,17,13,15,15,17, + 17,15,15,18,16,20,15,16,17,18,18,11,14,14,16,17, + 13,15,14,18,17,13,15,15,17,17,15,17,15,18,17,15, + 17,16,19,18, 8,11,11,14,15,10,12,12,15,15,10,12, + 12,16,16,13,14,14,17,16,14,15,15,17,17, 9,12,12, + 15,16,11,13,13,16,16,11,12,13,16,16,14,16,15,20, + 17,14,16,16,17,17, 9,12,12,15,16,11,13,13,16,17, + 11,13,13,17,16,14,15,15,17,18,15,15,15,18,18,11, + 14,14,17,16,13,15,15,17,17,13,14,14,18,17,15,16, + 16,18,19,15,15,17,17,19,11,14,14,16,17,13,15,14, + 17,19,13,15,14,18,17,15,17,16,18,18,15,17,15,18, + 16, +}; + +static float _vq_quantthresh__16u1__p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16u1__p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16u1__p3_0 = { + _vq_quantthresh__16u1__p3_0, + _vq_quantmap__16u1__p3_0, + 5, + 5 +}; + +static static_codebook _16u1__p3_0 = { + 4, 625, + _vq_lengthlist__16u1__p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16u1__p3_0, + NULL, + &_vq_auxt__16u1__p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u1__p4_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16u1__p4_0[] = { + 4, 5, 5, 8, 8, 6, 6, 7, 9, 9, 6, 6, 6, 9, 9, 9, + 10, 9,11,11, 9, 9,10,11,11, 6, 7, 7,10, 9, 7, 7, + 8, 9,10, 7, 7, 8,10,10,10,10,10,10,12, 9, 9,10, + 11,12, 6, 7, 7, 9, 9, 7, 8, 7,10,10, 7, 8, 7,10, + 10, 9,10, 9,12,11,10,10, 9,12,10, 9,10,10,12,11, + 10,10,10,12,12, 9,10,10,12,12,12,11,12,13,13,11, + 11,12,12,13, 9,10,10,11,12, 9,10,10,12,12,10,10, + 10,12,12,11,12,11,14,13,11,12,12,14,13, 5, 7, 7, + 10,10, 7, 8, 8,10,10, 7, 8, 7,10,10,10,10,10,12, + 12,10,10,10,12,12, 6, 8, 7,10,10, 7, 7, 9,10,11, + 8, 9, 9,11,10,10,10,11,11,13,10,10,11,12,13, 6, + 8, 8,10,10, 7, 9, 8,11,10, 8, 9, 9,10,11,10,11, + 10,13,11,10,11,10,12,12,10,11,10,12,11,10,10,10, + 12,13,10,11,11,13,12,11,11,13,11,14,12,12,13,14, + 14, 9,10,10,12,13,10,11,10,13,12,10,11,11,12,13, + 11,12,11,14,12,12,13,13,15,14, 5, 7, 7,10,10, 7, + 7, 8,10,10, 7, 8, 8,10,10,10,10,10,11,12,10,10, + 10,12,12, 7, 8, 8,10,10, 8, 9, 8,11,10, 7, 8, 9, + 10,11,10,11,11,12,12,10,10,11,11,13, 7, 7, 8,10, + 10, 8, 8, 9,10,11, 7, 9, 7,11,10,10,11,11,13,12, + 11,11,10,13,11, 9,10,10,12,12,10,11,11,13,12,10, + 10,11,12,12,12,13,13,14,14,11,11,12,12,14,10,10, + 11,12,12,10,11,11,12,13,10,10,10,13,12,12,13,13, + 15,14,12,13,10,14,11, 8,10,10,12,12,10,11,10,13, + 13, 9,10,10,12,12,12,13,13,15,14,11,12,12,13,13, + 9,10,10,13,12,10,10,11,13,13,10,11,10,13,12,12, + 12,13,14,15,12,13,12,15,13, 9,10,10,12,13,10,11, + 10,13,12,10,10,11,12,13,12,14,12,15,13,12,12,13, + 14,15,11,12,11,14,13,11,11,12,14,15,12,13,12,15, + 14,13,11,15,11,16,13,14,14,16,15,11,12,12,14,14, + 11,12,11,14,13,12,12,13,14,15,13,14,12,16,12,14, + 14,14,15,15, 8,10,10,12,12, 9,10,10,12,12,10,10, + 11,13,13,11,12,12,13,13,12,13,13,14,15, 9,10,10, + 13,12,10,11,11,13,12,10,10,11,13,13,12,13,12,15, + 14,12,12,13,13,16, 9, 9,10,12,13,10,10,11,12,13, + 10,11,10,13,13,12,12,13,13,15,13,13,12,15,13,11, + 12,12,14,14,12,13,12,15,14,11,11,12,13,14,14,14, + 14,16,15,13,12,15,12,16,11,11,12,13,14,12,13,13, + 14,15,10,12,11,14,13,14,15,14,16,16,13,14,11,15, + 11, +}; + +static float _vq_quantthresh__16u1__p4_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16u1__p4_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16u1__p4_0 = { + _vq_quantthresh__16u1__p4_0, + _vq_quantmap__16u1__p4_0, + 5, + 5 +}; + +static static_codebook _16u1__p4_0 = { + 4, 625, + _vq_lengthlist__16u1__p4_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16u1__p4_0, + NULL, + &_vq_auxt__16u1__p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u1__p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__16u1__p5_0[] = { + 1, 4, 4, 7, 7, 7, 7, 9, 9, 4, 6, 6, 8, 8, 8, 8, + 10,10, 4, 5, 6, 8, 8, 8, 8,10,10, 7, 8, 8, 9, 9, + 9, 9,11,11, 7, 8, 8, 9, 9, 9, 9,11,11, 7, 8, 8, + 10, 9,11,11,12,11, 7, 8, 8, 9, 9,11,11,12,12, 9, + 10,10,11,11,12,12,13,12, 9,10,10,11,11,12,12,12, + 13, +}; + +static float _vq_quantthresh__16u1__p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__16u1__p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__16u1__p5_0 = { + _vq_quantthresh__16u1__p5_0, + _vq_quantmap__16u1__p5_0, + 9, + 9 +}; + +static static_codebook _16u1__p5_0 = { + 2, 81, + _vq_lengthlist__16u1__p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__16u1__p5_0, + NULL, + &_vq_auxt__16u1__p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u1__p6_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__16u1__p6_0[] = { + 3, 4, 4, 6, 6, 7, 7, 9, 9, 4, 4, 4, 6, 6, 8, 8, + 9, 9, 4, 4, 4, 6, 6, 7, 7, 9, 9, 6, 6, 6, 7, 7, + 8, 8,10, 9, 6, 6, 6, 7, 7, 8, 8, 9,10, 7, 8, 7, + 8, 8, 9, 9,10,10, 7, 8, 8, 8, 8, 9, 9,10,10, 9, + 9, 9,10,10,10,10,11,11, 9, 9, 9,10,10,10,10,11, + 11, +}; + +static float _vq_quantthresh__16u1__p6_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__16u1__p6_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__16u1__p6_0 = { + _vq_quantthresh__16u1__p6_0, + _vq_quantmap__16u1__p6_0, + 9, + 9 +}; + +static static_codebook _16u1__p6_0 = { + 2, 81, + _vq_lengthlist__16u1__p6_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__16u1__p6_0, + NULL, + &_vq_auxt__16u1__p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u1__p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16u1__p7_0[] = { + 1, 4, 4, 4, 8, 8, 4, 8, 8, 5,11, 9, 8,12,11, 8, + 12,11, 5,10,11, 8,11,12, 8,11,12, 4,11,11,11,14, + 13,10,13,13, 8,14,13,12,14,16,12,16,15, 8,14,14, + 13,16,14,12,15,16, 4,11,11,10,14,13,11,14,14, 8, + 15,14,12,15,15,12,14,16, 8,14,14,11,16,15,12,15, + 13, +}; + +static float _vq_quantthresh__16u1__p7_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__16u1__p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16u1__p7_0 = { + _vq_quantthresh__16u1__p7_0, + _vq_quantmap__16u1__p7_0, + 3, + 3 +}; + +static static_codebook _16u1__p7_0 = { + 4, 81, + _vq_lengthlist__16u1__p7_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__16u1__p7_0, + NULL, + &_vq_auxt__16u1__p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u1__p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__16u1__p7_1[] = { + 2, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8, 4, 6, 5, 7, 7, + 8, 8, 8, 8, 8, 8, 4, 5, 6, 7, 7, 8, 8, 8, 8, 8, + 8, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 6, 7, 7, 8, + 8, 8, 8, 9, 9, 9, 9, 7, 8, 8, 8, 8, 9, 9, 9,10, + 9,10, 7, 8, 8, 8, 8, 9, 9, 9, 9,10, 9, 8, 8, 8, + 9, 9,10,10,10,10,10,10, 8, 8, 8, 9, 9, 9, 9,10, + 10,10,10, 8, 8, 8, 9, 9, 9,10,10,10,10,10, 8, 8, + 8, 9, 9,10,10,10,10,10,10, +}; + +static float _vq_quantthresh__16u1__p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__16u1__p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__16u1__p7_1 = { + _vq_quantthresh__16u1__p7_1, + _vq_quantmap__16u1__p7_1, + 11, + 11 +}; + +static static_codebook _16u1__p7_1 = { + 2, 121, + _vq_lengthlist__16u1__p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__16u1__p7_1, + NULL, + &_vq_auxt__16u1__p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__16u1__p8_0[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__16u1__p8_0[] = { + 1, 4, 4, 5, 5, 8, 8,10,10,12,12, 4, 7, 7, 8, 8, + 9, 9,12,11,14,13, 4, 7, 7, 7, 8, 9,10,11,11,13, + 12, 5, 8, 8, 9, 9,11,11,12,13,15,14, 5, 7, 8, 9, + 9,11,11,13,13,17,15, 8, 9,10,11,11,12,13,17,14, + 17,16, 8,10, 9,11,11,12,12,13,15,15,17,10,11,11, + 12,13,14,15,15,16,16,17, 9,11,11,12,12,14,15,17, + 15,15,16,11,14,12,14,15,16,15,16,16,16,15,11,13, + 13,14,14,15,15,16,16,15,16, +}; + +static float _vq_quantthresh__16u1__p8_0[] = { + -49.5, -38.5, -27.5, -16.5, -5.5, 5.5, 16.5, 27.5, + 38.5, 49.5, +}; + +static long _vq_quantmap__16u1__p8_0[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__16u1__p8_0 = { + _vq_quantthresh__16u1__p8_0, + _vq_quantmap__16u1__p8_0, + 11, + 11 +}; + +static static_codebook _16u1__p8_0 = { + 2, 121, + _vq_lengthlist__16u1__p8_0, + 1, -524582912, 1618345984, 4, 0, + _vq_quantlist__16u1__p8_0, + NULL, + &_vq_auxt__16u1__p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u1__p8_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__16u1__p8_1[] = { + 2, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 4, 6, 6, 7, 7, + 8, 7, 8, 8, 8, 8, 4, 6, 6, 7, 7, 7, 7, 8, 8, 8, + 8, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 9, 6, 7, 7, 7, + 7, 8, 8, 8, 8, 9, 9, 7, 7, 7, 8, 8, 8, 8, 9, 9, + 9, 9, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 8, 8, 8, + 8, 8, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 8, 8, 8, 9, 8, 9, 9, 9, 9, 9, 9, 8, 8, + 8, 9, 9, 9, 9, 9, 9, 9, 9, +}; + +static float _vq_quantthresh__16u1__p8_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__16u1__p8_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__16u1__p8_1 = { + _vq_quantthresh__16u1__p8_1, + _vq_quantmap__16u1__p8_1, + 11, + 11 +}; + +static static_codebook _16u1__p8_1 = { + 2, 121, + _vq_lengthlist__16u1__p8_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__16u1__p8_1, + NULL, + &_vq_auxt__16u1__p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__16u1__p9_0[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__16u1__p9_0[] = { + 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, +}; + +static float _vq_quantthresh__16u1__p9_0[] = { + -1657.5, -1402.5, -1147.5, -892.5, -637.5, -382.5, -127.5, 127.5, + 382.5, 637.5, 892.5, 1147.5, 1402.5, 1657.5, +}; + +static long _vq_quantmap__16u1__p9_0[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__16u1__p9_0 = { + _vq_quantthresh__16u1__p9_0, + _vq_quantmap__16u1__p9_0, + 15, + 15 +}; + +static static_codebook _16u1__p9_0 = { + 2, 225, + _vq_lengthlist__16u1__p9_0, + 1, -514071552, 1627381760, 4, 0, + _vq_quantlist__16u1__p9_0, + NULL, + &_vq_auxt__16u1__p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u1__p9_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__16u1__p9_1[] = { + 1, 6, 5, 9, 9,10,10, 6, 7, 9, 9,10,10,10,10, 5, + 10, 8,10, 8,10,10, 8, 8,10, 9,10,10,10,10, 5, 8, + 9,10,10,10,10, 8,10,10,10,10,10,10,10, 9,10,10, + 10,10,10,10, 9, 9,10,10,10,10,10,10, 9, 9, 8, 9, + 10,10,10, 9,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10, 8,10,10,10,10, + 10,10,10,10,10,10,10,10,10, 6, 8, 8,10,10,10, 8, + 10,10,10,10,10,10,10,10, 5, 8, 8,10,10,10, 9, 9, + 10,10,10,10,10,10,10,10, 9,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, +}; + +static float _vq_quantthresh__16u1__p9_1[] = { + -110.5, -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, + 25.5, 42.5, 59.5, 76.5, 93.5, 110.5, +}; + +static long _vq_quantmap__16u1__p9_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__16u1__p9_1 = { + _vq_quantthresh__16u1__p9_1, + _vq_quantmap__16u1__p9_1, + 15, + 15 +}; + +static static_codebook _16u1__p9_1 = { + 2, 225, + _vq_lengthlist__16u1__p9_1, + 1, -522338304, 1620115456, 4, 0, + _vq_quantlist__16u1__p9_1, + NULL, + &_vq_auxt__16u1__p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__16u1__p9_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__16u1__p9_2[] = { + 1, 6, 6, 7, 8, 8,11,10, 9, 9,11, 9,10, 9,11,11, + 9, 6, 7, 6,11, 8,11, 9,10,10,11, 9,11,10,10,10, + 11, 9, 5, 7, 7, 8, 8,10,11, 8, 8,11, 9, 9,10,11, + 9,10,11, 8, 9, 6, 8, 8, 9, 9,10,10,11,11,11, 9, + 11,10, 9,11, 8, 8, 8, 9, 8, 9,10,11, 9, 9,11,11, + 10, 9, 9,11,10, 8,11, 8, 9, 8,11, 9,10, 9,10,11, + 11,10,10, 9,10,10, 8, 8, 9,10,10,10, 9,11, 9,10, + 11,11,11,11,10, 9,11, 9, 9,11,11,10, 8,11,11,11, + 9,10,10,11,10,11,11, 9,11,10, 9,11,10,10,10,10, + 9,11,10,11,10, 9, 9,10,11, 9, 8,10,11,11,10,10, + 11, 9,11,10,11,11,10,11, 9, 9, 8,10, 8, 9,11, 9, + 8,10,10, 9,11,10,11,10,11, 9,11, 8,10,11,11,11, + 11,10,10,11,11,11,11,10,11,11,10, 9, 8,10,10, 9, + 11,10,11,11,11, 9, 9, 9,11,11,11,10,10, 9, 9,10, + 9,11,11,11,11, 8,10,11,10,11,11,10,11,11, 9, 9, + 9,10, 9,11, 9,11,11,11,11,11,10,11,11,10,11,10, + 11,11, 9,11,10,11,10, 9,10, 9,10,10,11,11,11,11, + 9,10, 9,10,11,11,10,11,11,11,11,11,11,10,11,11, + 10, +}; + +static float _vq_quantthresh__16u1__p9_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__16u1__p9_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__16u1__p9_2 = { + _vq_quantthresh__16u1__p9_2, + _vq_quantmap__16u1__p9_2, + 17, + 17 +}; + +static static_codebook _16u1__p9_2 = { + 2, 289, + _vq_lengthlist__16u1__p9_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__16u1__p9_2, + NULL, + &_vq_auxt__16u1__p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__16u1__short[] = { + 5, 7,10, 9,11,10,15,11,13,16, 6, 4, 6, 6, 7, 7, + 10, 9,12,16,10, 6, 5, 6, 6, 7,10,11,16,16, 9, 6, + 7, 6, 7, 7,10, 8,14,16,11, 6, 5, 4, 5, 6, 8, 9, + 15,16, 9, 6, 6, 5, 6, 6, 9, 8,14,16,12, 7, 6, 6, + 5, 6, 6, 7,13,16, 8, 6, 7, 6, 5, 5, 4, 4,11,16, + 9, 8, 9, 9, 7, 7, 6, 5,13,16,14,14,16,15,16,15, + 16,16,16,16, +}; + +static static_codebook _huff_book__16u1__short = { + 2, 100, + _huff_lengthlist__16u1__short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__16u2__long[] = { + 5, 7,10,10,10,11,11,13,18,19, 6, 5, 5, 6, 7, 8, + 9,12,19,19, 8, 5, 4, 4, 6, 7, 9,13,19,19, 8, 5, + 4, 4, 5, 6, 8,12,17,19, 7, 5, 5, 4, 4, 5, 7,12, + 18,18, 8, 7, 7, 6, 5, 5, 6,10,18,18, 9, 9, 9, 8, + 6, 5, 6, 9,18,18,11,13,13,13, 8, 7, 7, 9,16,18, + 13,17,18,16,11, 9, 9, 9,17,18,15,18,18,18,15,13, + 13,14,18,18, +}; + +static static_codebook _huff_book__16u2__long = { + 2, 100, + _huff_lengthlist__16u2__long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__16u2__short[] = { + 8,11,12,12,14,15,16,16,16,16, 9, 7, 7, 8, 9,11, + 13,14,16,16,13, 7, 6, 6, 7, 9,12,13,15,16,15, 7, + 6, 5, 4, 6,10,11,14,16,12, 8, 7, 4, 2, 4, 7,10, + 14,16,11, 9, 7, 5, 3, 4, 6, 9,14,16,11,10, 9, 7, + 5, 5, 6, 9,16,16,10,10, 9, 8, 6, 6, 7,10,16,16, + 11,11,11,10,10,10,11,14,16,16,16,14,14,13,14,16, + 16,16,16,16, +}; + +static static_codebook _huff_book__16u2__short = { + 2, 100, + _huff_lengthlist__16u2__short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16u2_p1_0[] = { + 1, 5, 5, 5, 7, 7, 5, 7, 7, 5, 7, 7, 7, 9, 9, 7, + 9, 9, 5, 7, 7, 7, 9, 9, 7, 9, 9, 5, 7, 7, 8, 9, + 9, 7, 9, 9, 7, 9, 9, 9,10,10, 9,10,10, 7, 9, 9, + 9,10,10, 9,10,11, 5, 7, 8, 8, 9, 9, 8, 9, 9, 7, + 9, 9, 9,10,10, 9, 9,10, 7, 9, 9, 9,10,10, 9,11, + 10, +}; + +static float _vq_quantthresh__16u2_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__16u2_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p1_0 = { + _vq_quantthresh__16u2_p1_0, + _vq_quantmap__16u2_p1_0, + 3, + 3 +}; + +static static_codebook _16u2_p1_0 = { + 4, 81, + _vq_lengthlist__16u2_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__16u2_p1_0, + NULL, + &_vq_auxt__16u2_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16u2_p2_0[] = { + 3, 5, 5, 8, 8, 5, 7, 7, 9, 9, 5, 7, 7, 9, 9, 9, + 10, 9,11,11, 9, 9, 9,11,11, 5, 7, 7, 9, 9, 7, 8, + 8,10,10, 7, 8, 8,10,10,10,10,10,12,12, 9,10,10, + 11,12, 5, 7, 7, 9, 9, 7, 8, 8,10,10, 7, 8, 8,10, + 10, 9,10,10,12,11,10,10,10,12,12, 9,10,10,12,12, + 10,11,10,13,12, 9,10,10,12,12,12,12,12,14,14,11, + 12,12,13,14, 9,10,10,12,12, 9,10,10,12,12,10,10, + 10,12,12,11,12,12,14,13,12,13,12,14,14, 5, 7, 7, + 9, 9, 7, 8, 8,10,10, 7, 8, 8,10,10,10,11,10,12, + 12,10,10,11,12,12, 7, 8, 8,10,10, 8, 9, 9,11,11, + 8, 9, 9,11,11,11,11,11,12,13,10,11,11,12,13, 7, + 8, 8,10,10, 8, 9, 8,11,10, 8, 9, 9,11,11,10,11, + 10,13,12,10,11,11,13,13, 9,11,10,13,13,10,11,11, + 13,13,10,11,11,13,13,12,12,13,13,15,12,12,13,14, + 15, 9,10,10,12,12,10,11,10,13,12,10,11,11,13,13, + 11,13,11,14,13,12,13,13,15,15, 5, 7, 7, 9, 9, 7, + 8, 8,10,10, 7, 8, 8,10,10,10,10,10,12,12,10,10, + 11,12,12, 7, 8, 8,10,10, 8, 9, 9,11,11, 8, 8, 9, + 10,11,10,11,11,13,13,10,10,11,12,13, 7, 8, 8,10, + 11, 8, 9, 9,11,11, 8, 9, 9,11,11,10,11,11,13,12, + 11,11,11,13,12, 9,10,10,12,12,10,11,11,13,13,10, + 10,11,12,13,12,13,13,15,14,11,11,13,12,14,10,10, + 11,13,13,10,11,11,13,13,10,11,11,13,13,12,13,13, + 14,14,12,13,12,14,13, 8,10, 9,12,12, 9,11,10,13, + 13, 9,10,10,12,13,12,13,13,14,14,12,12,13,14,14, + 9,11,10,13,13,10,11,11,13,13,10,11,11,13,13,12, + 13,13,15,15,13,13,13,14,15, 9,10,10,12,13,10,11, + 10,13,12,10,11,11,13,13,12,13,12,15,14,13,13,13, + 14,15,11,12,12,15,14,12,12,13,15,15,12,13,13,15, + 14,14,13,15,14,16,13,14,15,16,16,11,12,12,14,14, + 11,12,12,15,14,12,13,13,15,15,13,14,13,16,14,14, + 14,14,16,16, 8, 9, 9,12,12, 9,10,10,13,12, 9,10, + 10,13,13,12,12,12,14,14,12,12,13,15,15, 9,10,10, + 13,12,10,11,11,13,13,10,10,11,13,14,12,13,13,15, + 15,12,12,13,14,15, 9,10,10,13,13,10,11,11,13,13, + 10,11,11,13,13,12,13,13,14,14,13,14,13,15,14,11, + 12,12,14,14,12,13,13,15,14,11,12,12,14,15,14,14, + 14,16,15,13,12,14,14,16,11,12,13,14,15,12,13,13, + 14,16,12,13,12,15,14,13,15,14,16,16,14,15,13,16, + 13, +}; + +static float _vq_quantthresh__16u2_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16u2_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p2_0 = { + _vq_quantthresh__16u2_p2_0, + _vq_quantmap__16u2_p2_0, + 5, + 5 +}; + +static static_codebook _16u2_p2_0 = { + 4, 625, + _vq_lengthlist__16u2_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16u2_p2_0, + NULL, + &_vq_auxt__16u2_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__16u2_p3_0[] = { + 2, 4, 4, 6, 6, 7, 7, 9, 9, 4, 5, 5, 6, 6, 8, 7, + 9, 9, 4, 5, 5, 6, 6, 7, 8, 9, 9, 6, 6, 6, 7, 7, + 8, 8,10,10, 6, 6, 6, 7, 7, 8, 8, 9,10, 7, 8, 7, + 8, 8, 9, 9,10,10, 7, 8, 8, 8, 8, 9, 9,10,10, 9, + 9, 9,10, 9,10,10,11,11, 9, 9, 9,10,10,10,10,11, + 11, +}; + +static float _vq_quantthresh__16u2_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__16u2_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p3_0 = { + _vq_quantthresh__16u2_p3_0, + _vq_quantmap__16u2_p3_0, + 9, + 9 +}; + +static static_codebook _16u2_p3_0 = { + 2, 81, + _vq_lengthlist__16u2_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__16u2_p3_0, + NULL, + &_vq_auxt__16u2_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p4_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__16u2_p4_0[] = { + 2, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10,11,11,11, + 11, 5, 5, 5, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11,11, + 12,11, 5, 5, 5, 7, 7, 8, 8, 9, 9, 9, 9,10,10,11, + 11,12,12, 6, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9,10,10, + 11,11,12,12, 6, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9,10, + 10,11,11,12,12, 7, 8, 8, 8, 8, 9, 9, 9, 9,10,10, + 11,11,12,12,12,12, 7, 8, 8, 8, 8, 9, 9, 9, 9,10, + 10,11,11,11,12,12,12, 9, 9, 9, 9, 9, 9,10,10,10, + 10,10,11,11,12,12,13,13, 8, 9, 9, 9, 9,10, 9,10, + 10,10,10,11,11,12,12,13,13, 9, 9, 9, 9, 9,10,10, + 10,10,11,11,11,12,12,12,13,13, 9, 9, 9, 9, 9,10, + 10,10,10,11,11,12,11,12,12,13,13,10,10,10,10,10, + 11,11,11,11,11,12,12,12,12,13,13,14,10,10,10,10, + 10,11,11,11,11,12,11,12,12,13,12,13,13,11,11,11, + 11,11,12,12,12,12,12,12,13,13,13,13,14,14,11,11, + 11,11,11,12,12,12,12,12,12,13,12,13,13,14,14,11, + 12,12,12,12,12,12,13,13,13,13,13,13,14,14,14,14, + 11,12,12,12,12,12,12,13,13,13,13,14,13,14,14,14, + 14, +}; + +static float _vq_quantthresh__16u2_p4_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__16u2_p4_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p4_0 = { + _vq_quantthresh__16u2_p4_0, + _vq_quantmap__16u2_p4_0, + 17, + 17 +}; + +static static_codebook _16u2_p4_0 = { + 2, 289, + _vq_lengthlist__16u2_p4_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__16u2_p4_0, + NULL, + &_vq_auxt__16u2_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p5_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__16u2_p5_0[] = { + 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 9, 8, 7,10, 9, 7, + 10, 9, 5, 8, 9, 7, 9,10, 7, 9,10, 4, 9, 9, 9,11, + 11, 8,11,11, 7,11,11,10,10,13,10,14,13, 7,11,11, + 10,13,11,10,13,14, 5, 9, 9, 8,11,11, 9,11,11, 7, + 11,11,10,14,13,10,12,14, 7,11,11,10,13,13,10,13, + 10, +}; + +static float _vq_quantthresh__16u2_p5_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__16u2_p5_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p5_0 = { + _vq_quantthresh__16u2_p5_0, + _vq_quantmap__16u2_p5_0, + 3, + 3 +}; + +static static_codebook _16u2_p5_0 = { + 4, 81, + _vq_lengthlist__16u2_p5_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__16u2_p5_0, + NULL, + &_vq_auxt__16u2_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p5_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__16u2_p5_1[] = { + 2, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 5, 5, 5, 7, 7, + 7, 7, 8, 8, 8, 8, 5, 5, 6, 7, 7, 7, 7, 8, 8, 8, + 8, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 6, 7, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 7, 7, 7, 8, 8, 8, 8, 9, 9, + 9, 9, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 8, 8, 8, + 8, 8, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 8, 8, + 8, 8, 8, 9, 9, 9, 9, 9, 9, +}; + +static float _vq_quantthresh__16u2_p5_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__16u2_p5_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p5_1 = { + _vq_quantthresh__16u2_p5_1, + _vq_quantmap__16u2_p5_1, + 11, + 11 +}; + +static static_codebook _16u2_p5_1 = { + 2, 121, + _vq_lengthlist__16u2_p5_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__16u2_p5_1, + NULL, + &_vq_auxt__16u2_p5_1, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__16u2_p6_0[] = { + 1, 4, 4, 7, 7, 8, 8, 8, 8, 9, 9,10,10, 4, 6, 6, + 8, 8, 9, 9, 9, 9,10,10,12,11, 4, 6, 6, 8, 8, 9, + 9, 9, 9,10,10,11,12, 7, 8, 8, 9, 9,10,10,10,10, + 12,12,13,12, 7, 8, 8, 9, 9,10,10,10,10,11,12,12, + 12, 8, 9, 9,10,10,11,11,11,11,12,12,13,13, 8, 9, + 9,10,10,11,11,11,11,12,13,13,13, 8, 9, 9,10,10, + 11,11,12,12,13,13,14,14, 8, 9, 9,10,10,11,11,12, + 12,13,13,14,14, 9,10,10,11,12,13,12,13,14,14,14, + 14,14, 9,10,10,11,12,12,13,13,13,14,14,14,14,10, + 11,11,12,12,13,13,14,14,15,15,15,15,10,11,11,12, + 12,13,13,14,14,14,14,15,15, +}; + +static float _vq_quantthresh__16u2_p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__16u2_p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p6_0 = { + _vq_quantthresh__16u2_p6_0, + _vq_quantmap__16u2_p6_0, + 13, + 13 +}; + +static static_codebook _16u2_p6_0 = { + 2, 169, + _vq_lengthlist__16u2_p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__16u2_p6_0, + NULL, + &_vq_auxt__16u2_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__16u2_p6_1[] = { + 2, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 6, 6, 5, 5, 5, 6, 6, +}; + +static float _vq_quantthresh__16u2_p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__16u2_p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p6_1 = { + _vq_quantthresh__16u2_p6_1, + _vq_quantmap__16u2_p6_1, + 5, + 5 +}; + +static static_codebook _16u2_p6_1 = { + 2, 25, + _vq_lengthlist__16u2_p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__16u2_p6_1, + NULL, + &_vq_auxt__16u2_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__16u2_p7_0[] = { + 1, 4, 4, 7, 7, 7, 7, 8, 8, 9, 9,10,10, 4, 6, 6, + 9, 9, 9, 9, 9, 9,10,10,11,11, 4, 6, 6, 8, 9, 9, + 9, 9, 9,10,11,12,11, 7, 8, 9,10,10,10,10,11,10, + 11,12,12,13, 7, 9, 9,10,10,10,10,10,10,11,12,13, + 13, 7, 9, 8,10,10,11,11,11,12,12,13,13,14, 7, 9, + 9,10,10,11,11,11,12,13,13,13,13, 8, 9, 9,10,11, + 11,12,12,12,13,13,13,13, 8, 9, 9,10,11,11,11,12, + 12,13,13,14,14, 9,10,10,12,11,12,13,13,13,14,13, + 13,13, 9,10,10,11,11,12,12,13,14,13,13,14,13,10, + 11,11,12,13,14,14,14,15,14,14,14,14,10,11,11,12, + 12,13,13,13,14,14,14,15,14, +}; + +static float _vq_quantthresh__16u2_p7_0[] = { + -60.5, -49.5, -38.5, -27.5, -16.5, -5.5, 5.5, 16.5, + 27.5, 38.5, 49.5, 60.5, +}; + +static long _vq_quantmap__16u2_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p7_0 = { + _vq_quantthresh__16u2_p7_0, + _vq_quantmap__16u2_p7_0, + 13, + 13 +}; + +static static_codebook _16u2_p7_0 = { + 2, 169, + _vq_lengthlist__16u2_p7_0, + 1, -523206656, 1618345984, 4, 0, + _vq_quantlist__16u2_p7_0, + NULL, + &_vq_auxt__16u2_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__16u2_p7_1[] = { + 3, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 5, 6, 6, 7, 7, + 7, 7, 7, 7, 8, 8, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, + 8, 6, 6, 7, 7, 7, 8, 7, 8, 8, 8, 8, 6, 7, 7, 7, + 7, 7, 7, 8, 8, 8, 8, 7, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 8, 8, 8, 8, 8, + 8, 8, 8, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__16u2_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__16u2_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p7_1 = { + _vq_quantthresh__16u2_p7_1, + _vq_quantmap__16u2_p7_1, + 11, + 11 +}; + +static static_codebook _16u2_p7_1 = { + 2, 121, + _vq_lengthlist__16u2_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__16u2_p7_1, + NULL, + &_vq_auxt__16u2_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p8_0[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__16u2_p8_0[] = { + 1, 5, 5, 7, 7, 8, 8, 7, 7, 8, 8,10, 9,11,11, 4, + 6, 6, 8, 8,10, 9, 9, 8, 9, 9,10,10,12,14, 4, 6, + 7, 8, 9, 9,10, 9, 8, 9, 9,10,12,12,11, 7, 8, 8, + 10,10,10,10, 9, 9,10,10,11,13,13,12, 7, 8, 8, 9, + 11,11,10, 9, 9,11,10,12,11,11,14, 8, 9, 9,11,10, + 11,11,10,10,11,11,13,12,14,12, 8, 9, 9,11,12,11, + 11,10,10,12,11,12,12,12,14, 7, 8, 8, 9, 9,10,10, + 10,11,12,11,13,13,14,12, 7, 8, 9, 9, 9,10,10,11, + 11,11,12,12,14,14,14, 8,10, 9,10,11,11,11,11,14, + 12,12,13,14,14,13, 9, 9, 9,10,11,11,11,12,12,12, + 14,12,14,13,14,10,10,10,12,11,12,11,14,13,14,13, + 14,14,13,14, 9,10,10,11,12,11,13,12,13,13,14,14, + 14,13,14,10,13,13,12,12,11,12,14,13,14,13,14,12, + 14,13,10,11,11,12,11,12,12,14,14,14,13,14,14,14, + 14, +}; + +static float _vq_quantthresh__16u2_p8_0[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__16u2_p8_0[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p8_0 = { + _vq_quantthresh__16u2_p8_0, + _vq_quantmap__16u2_p8_0, + 15, + 15 +}; + +static static_codebook _16u2_p8_0 = { + 2, 225, + _vq_lengthlist__16u2_p8_0, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__16u2_p8_0, + NULL, + &_vq_auxt__16u2_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p8_1[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__16u2_p8_1[] = { + 2, 5, 5, 7, 7, 8, 8, 8, 8, 9, 9,10, 9,10, 9, 9, + 9,10,10,10,10, 5, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9, + 10, 9,10,10,10,10,10,10,11,10, 5, 6, 6, 7, 7, 8, + 8, 8, 9, 9,10,10,10,10,10,10,10,10,10,10,10, 7, + 7, 7, 8, 8, 9, 8, 9, 9,10, 9,10,10,10,10,10,10, + 11,10,11,10, 7, 7, 7, 8, 8, 8, 9, 9, 9,10, 9,10, + 10,10,10,10,10,10,10,10,10, 8, 8, 8, 9, 9, 9, 9, + 10, 9,10,10,10,10,10,10,10,11,10,10,11,10, 8, 8, + 8, 8, 9, 9, 9, 9,10,10,10,10,10,10,10,10,10,11, + 11,10,10, 8, 9, 9, 9, 9, 9, 9,10,10,10,10,10,10, + 11,10,11,10,11,10,11,10, 8, 9, 9, 9, 9, 9,10,10, + 10,10,10,10,10,10,10,10,11,11,10,10,10, 9,10, 9, + 9,10,10,10,11,10,10,10,10,10,10,10,10,11,11,11, + 11,11, 9, 9, 9,10, 9,10,10,10,10,10,10,11,10,11, + 10,11,11,11,11,10,10, 9,10, 9,10,10,10,10,11,10, + 10,10,10,10,11,10,11,10,11,10,10,11, 9,10,10,10, + 10,10,10,10,10,10,11,10,10,11,11,10,11,11,11,11, + 11, 9, 9,10,10,10,10,10,11,10,10,11,10,10,11,10, + 10,11,11,11,11,11, 9,10,10,10,10,10,10,10,11,10, + 11,10,11,10,11,11,11,11,11,10,11,10,10,10,10,10, + 10,10,10,10,11,11,11,11,11,11,11,11,11,10,11,11, + 10,10,10,10,10,11,10,10,10,11,10,11,11,11,11,10, + 12,11,11,11,10,10,10,10,10,10,11,10,10,10,11,11, + 12,11,11,11,11,11,11,11,11,11,10,10,10,11,10,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10, + 10,10,11,10,11,10,10,11,11,11,11,11,11,11,11,11, + 11,11,11,10,10,10,10,10,10,10,11,11,10,11,11,10, + 11,11,10,11,11,11,10,11,11, +}; + +static float _vq_quantthresh__16u2_p8_1[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__16u2_p8_1[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p8_1 = { + _vq_quantthresh__16u2_p8_1, + _vq_quantmap__16u2_p8_1, + 21, + 21 +}; + +static static_codebook _16u2_p8_1 = { + 2, 441, + _vq_lengthlist__16u2_p8_1, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__16u2_p8_1, + NULL, + &_vq_auxt__16u2_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p9_0[] = { + 5586, + 4655, + 6517, + 3724, + 7448, + 2793, + 8379, + 1862, + 9310, + 931, + 10241, + 0, + 11172, + 5521, + 5651, +}; + +static long _vq_lengthlist__16u2_p9_0[] = { + 1,10,10,10,10,10,10,10,10,10,10,10,10, 5, 4,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10, 4,10,10,10,10,10,10,10,10,10,10,10,10, + 6, 6, 5,10,10,10,10, 9, 9, 9, 9, 9, 9, 9, 9, 5, + 5, +}; + +static float _vq_quantthresh__16u2_p9_0[] = { + -5120.5, -4189.5, -3258.5, -2327.5, -1396.5, -498, -32.5, 32.5, + 498, 1396.5, 2327.5, 3258.5, 4189.5, 5120.5, +}; + +static long _vq_quantmap__16u2_p9_0[] = { + 11, 9, 7, 5, 3, 1, 13, 0, + 14, 2, 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p9_0 = { + _vq_quantthresh__16u2_p9_0, + _vq_quantmap__16u2_p9_0, + 15, + 15 +}; + +static static_codebook _16u2_p9_0 = { + 2, 225, + _vq_lengthlist__16u2_p9_0, + 1, -510275072, 1611661312, 14, 0, + _vq_quantlist__16u2_p9_0, + NULL, + &_vq_auxt__16u2_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p9_1[] = { + 392, + 343, + 441, + 294, + 490, + 245, + 539, + 196, + 588, + 147, + 637, + 98, + 686, + 49, + 735, + 0, + 784, + 388, + 396, +}; + +static long _vq_lengthlist__16u2_p9_1[] = { + 1,12,10,12,10,12,10,12,11,12,12,12,12,12,12,12, + 12, 5, 5, 9,10,12,11,11,12,12,12,12,12,12,12,12, + 12,12,12,12,10, 9, 9,11, 9,11,11,12,11,12,12,12, + 12,12,12,12,12,12,12, 8, 8,10,11, 9,12,11,12,12, + 12,12,12,12,12,12,12,12,12,12, 9, 8,10,11,12,11, + 12,11,12,12,12,12,12,12,12,12,12,12,12, 8, 9,11, + 11,10,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 9,10,11,12,11,12,11,12,12,12,12,12,12,12,12,12, + 12,12,12, 9, 9,11,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11, 5, 8, 9, 9, 8,11, 9,11,11,11,11,11,11, + 11,11,11,11, 5, 5, 4, 8, 8, 8, 8,10, 9,10,10,11, + 11,11,11,11,11,11,11, 5, 4, +}; + +static float _vq_quantthresh__16u2_p9_1[] = { + -367.5, -318.5, -269.5, -220.5, -171.5, -122.5, -73.5, -26.5, + -2, 2, 26.5, 73.5, 122.5, 171.5, 220.5, 269.5, + 318.5, 367.5, +}; + +static long _vq_quantmap__16u2_p9_1[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 17, 0, 18, 2, 4, 6, 8, 10, + 12, 14, 16, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p9_1 = { + _vq_quantthresh__16u2_p9_1, + _vq_quantmap__16u2_p9_1, + 19, + 19 +}; + +static static_codebook _16u2_p9_1 = { + 2, 361, + _vq_lengthlist__16u2_p9_1, + 1, -518488064, 1611661312, 10, 0, + _vq_quantlist__16u2_p9_1, + NULL, + &_vq_auxt__16u2_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__16u2_p9_2[] = { + 24, + 23, + 25, + 22, + 26, + 21, + 27, + 20, + 28, + 19, + 29, + 18, + 30, + 17, + 31, + 16, + 32, + 15, + 33, + 14, + 34, + 13, + 35, + 12, + 36, + 11, + 37, + 10, + 38, + 9, + 39, + 8, + 40, + 7, + 41, + 6, + 42, + 5, + 43, + 4, + 44, + 3, + 45, + 2, + 46, + 1, + 47, + 0, + 48, +}; + +static long _vq_lengthlist__16u2_p9_2[] = { + 1, 3, 3, 4, 7, 7, 7, 8, 7, 7, 7, 7, 8, 8, 8, 8, + 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 9, 9, 8, 9, 9, + 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,12,12,10, + 11, +}; + +static float _vq_quantthresh__16u2_p9_2[] = { + -23.5, -22.5, -21.5, -20.5, -19.5, -18.5, -17.5, -16.5, + -15.5, -14.5, -13.5, -12.5, -11.5, -10.5, -9.5, -8.5, + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, + 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, + 16.5, 17.5, 18.5, 19.5, 20.5, 21.5, 22.5, 23.5, +}; + +static long _vq_quantmap__16u2_p9_2[] = { + 47, 45, 43, 41, 39, 37, 35, 33, + 31, 29, 27, 25, 23, 21, 19, 17, + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, + 48, +}; + +static encode_aux_threshmatch _vq_auxt__16u2_p9_2 = { + _vq_quantthresh__16u2_p9_2, + _vq_quantmap__16u2_p9_2, + 49, + 49 +}; + +static static_codebook _16u2_p9_2 = { + 1, 49, + _vq_lengthlist__16u2_p9_2, + 1, -526909440, 1611661312, 6, 0, + _vq_quantlist__16u2_p9_2, + NULL, + &_vq_auxt__16u2_p9_2, + NULL, + 0 +}; + +static long _vq_quantlist__8u0__p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__8u0__p1_0[] = { + 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 8, 8, 8,10,10, 7, + 10,10, 5, 8, 8, 7,10,10, 8,10,10, 4, 9, 8, 8,11, + 11, 8,11,11, 7,11,11,10,11,13,10,13,13, 7,11,11, + 10,13,12,10,13,13, 5, 9, 8, 8,11,11, 8,11,11, 7, + 11,11, 9,13,13,10,12,13, 7,11,11,10,13,13,10,13, + 11, +}; + +static float _vq_quantthresh__8u0__p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__8u0__p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__8u0__p1_0 = { + _vq_quantthresh__8u0__p1_0, + _vq_quantmap__8u0__p1_0, + 3, + 3 +}; + +static static_codebook _8u0__p1_0 = { + 4, 81, + _vq_lengthlist__8u0__p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__8u0__p1_0, + NULL, + &_vq_auxt__8u0__p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u0__p2_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__8u0__p2_0[] = { + 2, 4, 4, 5, 6, 6, 5, 6, 6, 5, 7, 7, 6, 7, 8, 6, + 7, 8, 5, 7, 7, 6, 8, 8, 7, 9, 7, 5, 7, 7, 7, 9, + 9, 7, 8, 8, 6, 9, 8, 7, 7,10, 8,10,10, 6, 8, 8, + 8,10, 8, 8,10,10, 5, 7, 7, 7, 8, 8, 7, 8, 9, 6, + 8, 8, 8,10,10, 8, 8,10, 6, 8, 9, 8,10,10, 7,10, + 8, +}; + +static float _vq_quantthresh__8u0__p2_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__8u0__p2_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__8u0__p2_0 = { + _vq_quantthresh__8u0__p2_0, + _vq_quantmap__8u0__p2_0, + 3, + 3 +}; + +static static_codebook _8u0__p2_0 = { + 4, 81, + _vq_lengthlist__8u0__p2_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__8u0__p2_0, + NULL, + &_vq_auxt__8u0__p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u0__p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__8u0__p3_0[] = { + 1, 5, 5, 7, 7, 6, 7, 7, 9, 9, 6, 7, 7, 9, 9, 8, + 10, 9,11,11, 8, 9, 9,11,11, 6, 8, 8,10,10, 8,10, + 10,11,11, 8,10,10,11,11,10,11,11,12,12,10,11,11, + 12,13, 6, 8, 8,10,10, 8,10,10,11,11, 8,10,10,11, + 11, 9,10,11,12,12,10,11,11,12,12, 8,11,11,14,13, + 10,12,11,15,13,10,12,11,14,14,12,13,12,16,14,12, + 14,12,16,15, 8,11,11,13,14,10,11,12,13,15,10,11, + 12,13,15,11,12,13,14,15,12,12,14,14,16, 5, 8, 8, + 11,11, 9,11,11,12,12, 8,10,11,12,12,11,12,12,15, + 14,11,12,12,14,14, 7,11,10,13,12,10,11,12,13,14, + 10,12,12,14,13,12,13,13,14,15,12,13,13,15,15, 7, + 10,11,12,13,10,12,11,14,13,10,12,13,13,15,12,13, + 12,14,14,11,13,13,15,16, 9,12,12,15,14,11,13,13, + 15,16,11,13,13,16,16,13,14,15,15,15,12,14,15,17, + 16, 9,12,12,14,15,11,13,13,15,16,11,13,13,16,18, + 13,14,14,17,16,13,15,15,17,18, 5, 8, 9,11,11, 8, + 11,11,12,12, 8,10,11,12,12,11,12,12,14,14,11,12, + 12,14,15, 7,11,10,12,13,10,12,12,14,13,10,11,12, + 13,14,11,13,13,15,14,12,13,13,14,15, 7,10,11,13, + 13,10,12,12,13,14,10,12,12,13,13,11,13,13,16,16, + 12,13,13,15,14, 9,12,12,16,15,10,13,13,15,15,11, + 13,13,17,15,12,15,15,18,17,13,14,14,15,16, 9,12, + 12,15,15,11,13,13,15,16,11,13,13,15,15,12,15,15, + 16,16,13,15,14,17,15, 7,11,11,15,15,10,13,13,16, + 15,10,13,13,15,16,14,15,15,17,19,13,15,14,15,18, + 9,12,12,16,16,11,13,14,17,16,11,13,13,17,16,15, + 15,16,17,19,13,15,16, 0,18, 9,12,12,16,15,11,14, + 13,17,17,11,13,14,16,16,15,16,16,19,18,13,15,15, + 17,19,11,14,14,19,16,12,14,15, 0,18,12,16,15,18, + 17,15,15,18,16,19,14,15,17,19,19,11,14,14,18,19, + 13,15,14,19,19,12,16,15,18,17,15,17,15, 0,16,14, + 17,16,19, 0, 7,11,11,14,14,10,12,12,15,15,10,13, + 13,16,15,13,15,15,17, 0,14,15,15,16,19, 9,12,12, + 16,16,11,14,14,16,16,11,13,13,16,16,14,17,16,19, + 0,14,18,17,17,19, 9,12,12,15,16,11,13,13,15,17, + 12,14,13,19,16,13,15,15,17,19,15,17,16,17,19,11, + 14,14,19,16,12,15,15,19,17,13,14,15,17,19,14,16, + 17,19,19,16,15,16,17,19,11,15,14,16,16,12,15,15, + 19, 0,12,14,15,19,19,14,16,16, 0,18,15,19,14,18, + 16, +}; + +static float _vq_quantthresh__8u0__p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__8u0__p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__8u0__p3_0 = { + _vq_quantthresh__8u0__p3_0, + _vq_quantmap__8u0__p3_0, + 5, + 5 +}; + +static static_codebook _8u0__p3_0 = { + 4, 625, + _vq_lengthlist__8u0__p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__8u0__p3_0, + NULL, + &_vq_auxt__8u0__p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u0__p4_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__8u0__p4_0[] = { + 3, 5, 5, 8, 8, 5, 6, 7, 9, 9, 6, 7, 6, 9, 9, 9, + 9, 9,10,11, 9, 9, 9,11,10, 6, 7, 7,10,10, 7, 7, + 8,10,10, 7, 8, 8,10,10,10,10,10,10,11, 9,10,10, + 11,12, 6, 7, 7,10,10, 7, 8, 8,10,10, 7, 8, 7,10, + 10, 9,10,10,12,11,10,10,10,11,10, 9,10,10,12,11, + 10,10,10,13,11, 9,10,10,12,12,11,11,12,12,13,11, + 11,11,12,13, 9,10,10,12,12,10,10,11,12,12,10,10, + 11,12,12,11,11,11,13,13,11,12,12,13,13, 5, 7, 7, + 10,10, 7, 8, 8,10,10, 7, 8, 8,10,10,10,11,11,12, + 12,10,11,10,12,12, 7, 8, 8,11,11, 7, 8, 9,10,11, + 8, 9, 9,11,11,11,10,11,10,12,10,11,11,12,13, 7, + 8, 8,10,11, 8, 9, 8,12,10, 8, 9, 9,11,12,10,11, + 10,13,11,10,11,11,13,12, 9,11,10,13,12,10,10,11, + 12,12,10,11,11,13,13,12,10,13,11,14,11,12,12,15, + 13, 9,11,11,13,13,10,11,11,13,12,10,11,11,12,14, + 12,13,11,14,12,12,12,12,14,14, 5, 7, 7,10,10, 7, + 8, 8,10,10, 7, 8, 8,11,10,10,11,11,12,12,10,11, + 10,12,12, 7, 8, 8,10,11, 8, 9, 9,12,11, 8, 8, 9, + 10,11,10,11,11,12,13,11,10,11,11,13, 6, 8, 8,10, + 11, 8, 9, 9,11,11, 7, 9, 7,11,10,10,11,11,12,12, + 10,11,10,13,10, 9,11,10,13,12,10,12,11,13,13,10, + 10,11,12,13,11,12,13,15,14,11,11,13,12,13, 9,10, + 11,12,13,10,11,11,12,13,10,11,10,13,12,12,13,13, + 13,14,12,12,11,14,11, 8,10,10,12,13,10,11,11,13, + 13,10,11,10,13,13,12,13,14,15,14,12,12,12,14,13, + 9,10,10,13,12,10,10,12,13,13,10,11,11,15,12,12, + 12,13,15,14,12,13,13,15,13, 9,10,11,12,13,10,12, + 10,13,12,10,11,11,12,13,12,14,12,15,13,12,12,12, + 15,14,11,12,11,14,13,11,11,12,14,14,12,13,13,14, + 13,13,11,15,11,15,14,14,14,16,15,11,12,12,13,14, + 11,13,11,14,14,12,12,13,14,15,12,14,12,15,12,13, + 15,14,16,15, 8,10,10,12,12,10,10,10,12,13,10,11, + 11,13,13,12,12,12,13,14,13,13,13,15,15, 9,10,10, + 12,12,10,11,11,13,12,10,10,11,13,13,12,12,12,14, + 14,12,12,13,15,14, 9,10,10,13,12,10,10,12,12,13, + 10,11,10,13,13,12,13,13,14,14,12,13,12,14,13,11, + 12,12,14,13,12,13,12,14,14,10,12,12,14,14,14,14, + 14,16,14,13,12,14,12,15,10,12,12,14,15,12,13,13, + 14,16,11,12,11,15,14,13,14,14,14,15,13,14,11,14, + 12, +}; + +static float _vq_quantthresh__8u0__p4_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__8u0__p4_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__8u0__p4_0 = { + _vq_quantthresh__8u0__p4_0, + _vq_quantmap__8u0__p4_0, + 5, + 5 +}; + +static static_codebook _8u0__p4_0 = { + 4, 625, + _vq_lengthlist__8u0__p4_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__8u0__p4_0, + NULL, + &_vq_auxt__8u0__p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u0__p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__8u0__p5_0[] = { + 1, 4, 4, 7, 7, 7, 7, 9, 9, 4, 6, 6, 8, 7, 8, 8, + 10,10, 4, 6, 6, 8, 8, 8, 8,10,10, 6, 8, 8, 9, 9, + 9, 9,11,11, 7, 8, 8, 9, 9, 9, 9,11,11, 7, 8, 8, + 9, 9,10,10,12,11, 7, 8, 8, 9, 9,10,10,11,11, 9, + 10,10,11,11,11,12,12,12, 9,10,10,11,11,12,12,12, + 12, +}; + +static float _vq_quantthresh__8u0__p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__8u0__p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__8u0__p5_0 = { + _vq_quantthresh__8u0__p5_0, + _vq_quantmap__8u0__p5_0, + 9, + 9 +}; + +static static_codebook _8u0__p5_0 = { + 2, 81, + _vq_lengthlist__8u0__p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__8u0__p5_0, + NULL, + &_vq_auxt__8u0__p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u0__p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__8u0__p6_0[] = { + 1, 4, 4, 7, 7, 9, 9,11,11,12,12,16,16, 3, 6, 6, + 9, 9,11,11,12,12,13,14,18,16, 3, 6, 7, 9, 9,11, + 11,13,12,14,14,17,16, 7, 9, 9,11,11,12,12,14,14, + 14,14,17,16, 7, 9, 9,11,11,13,12,13,13,14,14,17, + 0, 9,11,11,12,13,14,14,14,13,15,14,17,17, 9,11, + 11,12,12,14,14,13,14,14,15, 0, 0,11,12,12,15,14, + 15,14,15,14,15,16,17, 0,11,12,13,13,13,14,14,15, + 14,15,15, 0, 0,12,14,14,15,15,14,16,15,15,17,16, + 0,18,13,14,14,15,14,15,14,15,16,17,16, 0, 0,17, + 17,18, 0,16,18,16, 0, 0, 0,17, 0, 0,16, 0, 0,16, + 16, 0,15, 0,17, 0, 0, 0, 0, +}; + +static float _vq_quantthresh__8u0__p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__8u0__p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__8u0__p6_0 = { + _vq_quantthresh__8u0__p6_0, + _vq_quantmap__8u0__p6_0, + 13, + 13 +}; + +static static_codebook _8u0__p6_0 = { + 2, 169, + _vq_lengthlist__8u0__p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__8u0__p6_0, + NULL, + &_vq_auxt__8u0__p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u0__p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__8u0__p6_1[] = { + 1, 4, 4, 6, 6, 4, 6, 5, 7, 7, 4, 5, 6, 7, 7, 6, + 7, 7, 7, 7, 6, 7, 7, 7, 7, +}; + +static float _vq_quantthresh__8u0__p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__8u0__p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__8u0__p6_1 = { + _vq_quantthresh__8u0__p6_1, + _vq_quantmap__8u0__p6_1, + 5, + 5 +}; + +static static_codebook _8u0__p6_1 = { + 2, 25, + _vq_lengthlist__8u0__p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__8u0__p6_1, + NULL, + &_vq_auxt__8u0__p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__8u0__p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__8u0__p7_0[] = { + 1, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, +}; + +static float _vq_quantthresh__8u0__p7_0[] = { + -157.5, 157.5, +}; + +static long _vq_quantmap__8u0__p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__8u0__p7_0 = { + _vq_quantthresh__8u0__p7_0, + _vq_quantmap__8u0__p7_0, + 3, + 3 +}; + +static static_codebook _8u0__p7_0 = { + 4, 81, + _vq_lengthlist__8u0__p7_0, + 1, -518803456, 1628680192, 2, 0, + _vq_quantlist__8u0__p7_0, + NULL, + &_vq_auxt__8u0__p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u0__p7_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__8u0__p7_1[] = { + 1, 5, 5, 5, 5,10,10,11,11,11,11,11,11,11,11, 5, + 7, 6, 8, 8, 9,10,11,11,11,11,11,11,11,11, 6, 6, + 7, 9, 7,11,10,11,11,11,11,11,11,11,11, 5, 6, 6, + 11, 8,11,11,11,11,11,11,11,11,11,11, 5, 6, 6, 9, + 10,11,10,11,11,11,11,11,11,11,11, 7,10,10,11,11, + 11,11,11,11,11,11,11,11,11,11, 7,11, 8,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10, +}; + +static float _vq_quantthresh__8u0__p7_1[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__8u0__p7_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__8u0__p7_1 = { + _vq_quantthresh__8u0__p7_1, + _vq_quantmap__8u0__p7_1, + 15, + 15 +}; + +static static_codebook _8u0__p7_1 = { + 2, 225, + _vq_lengthlist__8u0__p7_1, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__8u0__p7_1, + NULL, + &_vq_auxt__8u0__p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__8u0__p7_2[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__8u0__p7_2[] = { + 1, 6, 5, 7, 7, 9, 9, 9, 9,10,12,12,10,11,11,10, + 11,11,11,10,11, 6, 8, 8, 9, 9,10,10, 9,10,11,11, + 10,11,11,11,11,10,11,11,11,11, 6, 7, 8, 9, 9, 9, + 10,11,10,11,12,11,10,11,11,11,11,11,11,12,10, 8, + 9, 9,10, 9,10,10, 9,10,10,10,10,10, 9,10,10,10, + 10, 9,10,10, 9, 9, 9, 9,10,10, 9, 9,10,10,11,10, + 9,12,10,11,10, 9,10,10,10, 8, 9, 9,10, 9,10, 9, + 9,10,10, 9,10, 9,11,10,10,10,10,10, 9,10, 8, 8, + 9, 9,10, 9,11, 9, 8, 9, 9,10,11,10,10,10,11,12, + 9, 9,11, 8, 9, 8,11,10,11,10,10, 9,11,10,10,10, + 10,10,10,10,11,11,11,11, 8, 9, 9, 9,10,10,10,11, + 11,12,11,12,11,10,10,10,12,11,11,11,10, 8,10, 9, + 11,10,10,11,12,10,11,12,11,11,12,11,12,12,10,11, + 11,10, 9, 9,10,11,12,10,10,10,11,10,11,11,10,12, + 12,10,11,10,11,12,10, 9,10,10,11,10,11,11,11,11, + 11,12,11,11,11, 9,11,10,11,10,11,10, 9, 9,10,11, + 11,11,10,10,11,12,12,11,12,11,11,11,12,12,12,12, + 11, 9,11,11,12,10,11,11,11,11,11,11,12,11,11,12, + 11,11,11,10,11,11, 9,11,10,11,11,11,10,10,10,11, + 11,11,12,10,11,10,11,11,11,11,12, 9,11,10,11,11, + 10,10,11,11, 9,11,11,12,10,10,10,10,10,11,11,10, + 9,10,11,11,12,11,10,10,12,11,11,12,11,12,11,11, + 10,10,11,11,10,12,11,10,11,10,11,10,10,10,11,11, + 10,10,11,11,11,11,10,10,10,12,11,11,11,11,10, 9, + 10,11,11,11,12,11,11,11,12,10,11,11,11, 9,10,11, + 11,11,11,11,11,10,10,11,11,12,11,10,11,12,11,10, + 10,11, 9,10,11,11,11,11,11,10,11,11,10,12,11,11, + 11,12,11,11,11,10,10,11,11, +}; + +static float _vq_quantthresh__8u0__p7_2[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__8u0__p7_2[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__8u0__p7_2 = { + _vq_quantthresh__8u0__p7_2, + _vq_quantmap__8u0__p7_2, + 21, + 21 +}; + +static static_codebook _8u0__p7_2 = { + 2, 441, + _vq_lengthlist__8u0__p7_2, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__8u0__p7_2, + NULL, + &_vq_auxt__8u0__p7_2, + NULL, + 0 +}; + +static long _huff_lengthlist__8u0__single[] = { + 4, 7,11, 9,12, 8, 7,10, 6, 4, 5, 5, 7, 5, 6,16, + 9, 5, 5, 6, 7, 7, 9,16, 7, 4, 6, 5, 7, 5, 7,17, + 10, 7, 7, 8, 7, 7, 8,18, 7, 5, 6, 4, 5, 4, 5,15, + 7, 6, 7, 5, 6, 4, 5,15,12,13,18,12,17,11, 9,17, +}; + +static static_codebook _huff_book__8u0__single = { + 2, 64, + _huff_lengthlist__8u0__single, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__8u1__p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__8u1__p1_0[] = { + 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 8, 8, 7, 9,10, 7, + 9, 9, 5, 8, 8, 7,10, 9, 7, 9, 9, 5, 8, 8, 8,10, + 10, 8,10,10, 7,10,10, 9,10,12,10,12,12, 7,10,10, + 9,12,11,10,12,12, 5, 8, 8, 8,10,10, 8,10,10, 7, + 10,10,10,12,12, 9,11,12, 7,10,10,10,12,12, 9,12, + 10, +}; + +static float _vq_quantthresh__8u1__p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__8u1__p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__8u1__p1_0 = { + _vq_quantthresh__8u1__p1_0, + _vq_quantmap__8u1__p1_0, + 3, + 3 +}; + +static static_codebook _8u1__p1_0 = { + 4, 81, + _vq_lengthlist__8u1__p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__8u1__p1_0, + NULL, + &_vq_auxt__8u1__p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u1__p2_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__8u1__p2_0[] = { + 3, 4, 5, 5, 6, 6, 5, 6, 6, 5, 7, 6, 6, 7, 8, 6, + 7, 8, 5, 6, 6, 6, 8, 7, 6, 8, 7, 5, 6, 6, 7, 8, + 8, 6, 7, 7, 6, 8, 7, 7, 7, 9, 8, 9, 9, 6, 7, 8, + 7, 9, 7, 8, 9, 9, 5, 6, 6, 6, 7, 7, 7, 8, 8, 6, + 8, 7, 8, 9, 9, 7, 7, 9, 6, 7, 8, 8, 9, 9, 7, 9, + 7, +}; + +static float _vq_quantthresh__8u1__p2_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__8u1__p2_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__8u1__p2_0 = { + _vq_quantthresh__8u1__p2_0, + _vq_quantmap__8u1__p2_0, + 3, + 3 +}; + +static static_codebook _8u1__p2_0 = { + 4, 81, + _vq_lengthlist__8u1__p2_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__8u1__p2_0, + NULL, + &_vq_auxt__8u1__p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u1__p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__8u1__p3_0[] = { + 1, 5, 5, 7, 7, 6, 7, 7, 9, 9, 6, 7, 7, 9, 9, 8, + 10, 9,11,11, 9, 9, 9,11,11, 6, 8, 8,10,10, 8,10, + 10,11,11, 8, 9,10,11,11,10,11,11,12,12,10,11,11, + 12,13, 6, 8, 8,10,10, 8,10, 9,11,11, 8,10, 9,11, + 11,10,11,11,12,12,10,11,11,12,12, 9,11,11,14,13, + 10,12,11,14,14,10,12,11,14,13,12,13,13,15,14,12, + 13,13,15,14, 8,11,11,13,14,10,11,12,13,15,10,11, + 12,14,14,12,13,13,14,15,12,13,13,14,15, 5, 8, 8, + 11,11, 8,10,10,12,12, 8,10,10,12,12,11,12,12,14, + 13,11,12,12,13,14, 8,10,10,12,12, 9,11,12,13,14, + 10,12,12,13,13,12,12,13,14,14,11,13,13,15,15, 7, + 10,10,12,12, 9,12,11,14,12,10,11,12,13,14,12,13, + 12,14,14,12,13,13,15,16,10,12,12,15,14,11,12,13, + 15,15,11,13,13,15,16,14,14,15,15,16,13,14,15,17, + 15, 9,12,12,14,15,11,13,12,15,15,11,13,13,15,15, + 13,14,13,15,14,13,14,14,17, 0, 5, 8, 8,11,11, 8, + 10,10,12,12, 8,10,10,12,12,11,12,12,14,14,11,12, + 12,14,14, 7,10,10,12,12,10,12,12,13,13, 9,11,12, + 12,13,11,12,13,15,15,11,12,13,14,15, 8,10,10,12, + 12,10,12,11,13,13,10,12,11,13,13,11,13,13,15,14, + 12,13,12,15,13, 9,12,12,14,14,11,13,13,16,15,11, + 12,13,16,15,13,14,15,16,16,13,13,15,15,16,10,12, + 12,15,14,11,13,13,14,16,11,13,13,15,16,13,15,15, + 16,17,13,15,14,16,15, 8,11,11,14,15,10,12,12,15, + 15,10,12,12,15,16,14,15,15,16,17,13,14,14,16,16, + 9,12,12,15,15,11,13,14,15,17,11,13,13,15,16,14, + 15,16,19,17,13,15,15, 0,17, 9,12,12,15,15,11,14, + 13,16,15,11,13,13,15,16,15,15,15,18,17,13,15,15, + 17,17,11,15,14,18,16,12,14,15,17,17,12,15,15,18, + 18,15,15,16,15,19,14,16,16, 0, 0,11,14,14,16,17, + 12,15,14,18,17,12,15,15,18,18,15,17,15,18,16,14, + 16,16,18,18, 7,11,11,14,14,10,12,12,15,15,10,12, + 13,15,15,13,14,15,16,16,14,15,15,18,18, 9,12,12, + 15,15,11,13,13,16,15,11,12,13,16,16,14,15,15,17, + 16,15,16,16,17,17, 9,12,12,15,15,11,13,13,15,17, + 11,14,13,16,15,13,15,15,17,17,15,15,15,18,17,11, + 14,14,17,15,12,14,15,17,18,13,13,15,17,17,14,16, + 16,19,18,16,15,17,17, 0,11,14,14,17,17,12,15,15, + 18, 0,12,15,14,18,16,14,17,17,19, 0,16,18,15, 0, + 16, +}; + +static float _vq_quantthresh__8u1__p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__8u1__p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__8u1__p3_0 = { + _vq_quantthresh__8u1__p3_0, + _vq_quantmap__8u1__p3_0, + 5, + 5 +}; + +static static_codebook _8u1__p3_0 = { + 4, 625, + _vq_lengthlist__8u1__p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__8u1__p3_0, + NULL, + &_vq_auxt__8u1__p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u1__p4_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__8u1__p4_0[] = { + 4, 5, 5, 9, 9, 6, 7, 7, 9, 9, 6, 7, 7, 9, 9, 9, + 9, 9,11,11, 9, 9, 9,11,11, 6, 7, 7, 9, 9, 7, 7, + 8, 9,10, 7, 7, 8, 9,10, 9, 9,10,10,11, 9, 9,10, + 10,12, 6, 7, 7, 9, 9, 7, 8, 7,10, 9, 7, 8, 7,10, + 9, 9,10, 9,12,11,10,10, 9,12,10, 9,10,10,12,11, + 9,10,10,12,11, 9,10,10,12,12,11,11,12,12,13,11, + 11,12,12,13, 9, 9,10,12,11, 9,10,10,12,12,10,10, + 10,12,12,11,12,11,13,12,11,12,11,13,12, 6, 7, 7, + 9, 9, 7, 8, 8,10,10, 7, 8, 7,10, 9,10,10,10,12, + 12,10,10,10,12,11, 7, 8, 7,10,10, 7, 7, 9,10,11, + 8, 9, 9,11,10,10,10,11,10,12,10,10,11,12,12, 7, + 8, 8,10,10, 7, 9, 8,11,10, 8, 8, 9,11,11,10,11, + 10,12,11,10,11,11,12,12, 9,10,10,12,12, 9,10,10, + 12,12,10,11,11,13,12,11,10,12,10,14,12,12,12,13, + 14, 9,10,10,12,12, 9,11,10,12,12,10,11,11,12,12, + 11,12,11,14,12,12,12,12,14,14, 5, 7, 7, 9, 9, 7, + 7, 7, 9,10, 7, 8, 8,10,10,10,10,10,11,11,10,10, + 10,12,12, 7, 8, 8,10,10, 8, 9, 8,11,10, 7, 8, 9, + 10,11,10,10,10,11,12,10,10,11,11,13, 6, 7, 8,10, + 10, 8, 9, 9,10,10, 7, 9, 7,11,10,10,11,10,12,12, + 10,11,10,12,10, 9,10,10,12,12,10,11,11,13,12, 9, + 10,10,12,12,12,12,12,14,13,11,11,12,11,14, 9,10, + 10,11,12,10,11,11,12,13, 9,10,10,12,12,12,12,12, + 14,13,11,12,10,14,11, 9, 9,10,11,12, 9,10,10,12, + 12, 9,10,10,12,12,12,12,12,14,14,11,12,12,13,12, + 9,10, 9,12,12, 9,10,11,12,13,10,11,10,13,11,12, + 12,13,13,14,12,12,12,13,13, 9,10,10,12,12,10,11, + 10,13,12,10,10,11,12,13,12,13,12,14,13,12,12,12, + 13,14,11,12,11,14,13,10,10,11,13,13,12,12,12,14, + 13,12,10,14,10,15,13,14,14,14,14,11,11,12,13,14, + 10,12,11,13,13,12,12,12,13,15,12,13,11,15,12,13, + 13,14,14,14, 9,10, 9,12,12, 9,10,10,12,12,10,10, + 10,12,12,11,11,12,12,13,12,12,12,14,14, 9,10,10, + 12,12,10,11,10,13,12,10,10,11,12,13,12,12,12,14, + 13,12,12,13,13,14, 9,10,10,12,13,10,10,11,11,12, + 9,11,10,13,12,12,12,12,13,14,12,13,12,14,13,11, + 12,11,13,13,12,13,12,14,13,10,11,12,13,13,13,13, + 13,14,15,12,11,14,12,14,11,11,12,12,13,12,12,12, + 13,14,10,12,10,14,13,13,13,13,14,15,12,14,11,15, + 10, +}; + +static float _vq_quantthresh__8u1__p4_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__8u1__p4_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__8u1__p4_0 = { + _vq_quantthresh__8u1__p4_0, + _vq_quantmap__8u1__p4_0, + 5, + 5 +}; + +static static_codebook _8u1__p4_0 = { + 4, 625, + _vq_lengthlist__8u1__p4_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__8u1__p4_0, + NULL, + &_vq_auxt__8u1__p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u1__p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__8u1__p5_0[] = { + 1, 4, 4, 7, 7, 7, 7, 9, 9, 4, 6, 5, 8, 7, 8, 8, + 10,10, 4, 6, 6, 8, 8, 8, 8,10,10, 7, 8, 8, 9, 9, + 9, 9,11,11, 7, 8, 8, 9, 9, 9, 9,11,11, 8, 8, 8, + 9, 9,10,10,12,11, 8, 8, 8, 9, 9,10,10,11,11, 9, + 10,10,11,11,11,11,13,12, 9,10,10,11,11,12,12,12, + 13, +}; + +static float _vq_quantthresh__8u1__p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__8u1__p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__8u1__p5_0 = { + _vq_quantthresh__8u1__p5_0, + _vq_quantmap__8u1__p5_0, + 9, + 9 +}; + +static static_codebook _8u1__p5_0 = { + 2, 81, + _vq_lengthlist__8u1__p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__8u1__p5_0, + NULL, + &_vq_auxt__8u1__p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u1__p6_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__8u1__p6_0[] = { + 3, 4, 4, 6, 6, 7, 7, 9, 9, 4, 4, 5, 6, 6, 7, 7, + 9, 9, 4, 4, 4, 6, 6, 7, 7, 9, 9, 6, 6, 6, 7, 7, + 8, 8, 9, 9, 6, 6, 6, 7, 7, 8, 8, 9, 9, 7, 7, 7, + 8, 8, 8, 9,10,10, 7, 7, 7, 8, 8, 9, 8,10,10, 9, + 9, 9, 9, 9,10,10,10,10, 9, 9, 9, 9, 9,10,10,10, + 10, +}; + +static float _vq_quantthresh__8u1__p6_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__8u1__p6_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__8u1__p6_0 = { + _vq_quantthresh__8u1__p6_0, + _vq_quantmap__8u1__p6_0, + 9, + 9 +}; + +static static_codebook _8u1__p6_0 = { + 2, 81, + _vq_lengthlist__8u1__p6_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__8u1__p6_0, + NULL, + &_vq_auxt__8u1__p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u1__p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__8u1__p7_0[] = { + 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 9, 9, 8,10,10, 8, + 10,10, 5, 9, 9, 7,10,10, 8,10,10, 4,10,10, 9,12, + 12, 9,11,11, 7,12,11,10,11,13,10,13,13, 7,12,12, + 10,13,12,10,13,13, 4,10,10, 9,12,12, 9,12,12, 7, + 12,12,10,13,13,10,12,13, 7,11,12,10,13,13,10,13, + 11, +}; + +static float _vq_quantthresh__8u1__p7_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__8u1__p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__8u1__p7_0 = { + _vq_quantthresh__8u1__p7_0, + _vq_quantmap__8u1__p7_0, + 3, + 3 +}; + +static static_codebook _8u1__p7_0 = { + 4, 81, + _vq_lengthlist__8u1__p7_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__8u1__p7_0, + NULL, + &_vq_auxt__8u1__p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u1__p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__8u1__p7_1[] = { + 2, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8, 4, 5, 5, 7, 7, + 8, 8, 9, 9, 9, 9, 4, 5, 5, 7, 7, 8, 8, 9, 9, 9, + 9, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 6, 7, 7, 8, + 8, 8, 8, 9, 9, 9, 9, 8, 8, 8, 8, 8, 9, 9, 9, 9, + 9, 9, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 8, 9, 9, + 9, 9, 9, 9,10,10,10,10, 8, 9, 9, 9, 9, 9, 9,10, + 10,10,10, 8, 9, 9, 9, 9, 9, 9,10,10,10,10, 8, 9, + 9, 9, 9, 9, 9,10,10,10,10, +}; + +static float _vq_quantthresh__8u1__p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__8u1__p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__8u1__p7_1 = { + _vq_quantthresh__8u1__p7_1, + _vq_quantmap__8u1__p7_1, + 11, + 11 +}; + +static static_codebook _8u1__p7_1 = { + 2, 121, + _vq_lengthlist__8u1__p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__8u1__p7_1, + NULL, + &_vq_auxt__8u1__p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__8u1__p8_0[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__8u1__p8_0[] = { + 1, 4, 4, 6, 6, 8, 8,10,10,11,11, 4, 6, 6, 7, 7, + 9, 9,11,11,13,12, 4, 6, 6, 7, 7, 9, 9,11,11,12, + 12, 6, 7, 7, 9, 9,11,11,12,12,13,13, 6, 7, 7, 9, + 9,11,11,12,12,13,13, 8, 9, 9,11,11,12,12,13,13, + 14,14, 8, 9, 9,11,11,12,12,13,13,14,14, 9,11,11, + 12,12,13,13,14,14,15,15, 9,11,11,12,12,13,13,14, + 14,15,14,11,12,12,13,13,14,14,15,15,16,16,11,12, + 12,13,13,14,14,15,15,15,15, +}; + +static float _vq_quantthresh__8u1__p8_0[] = { + -49.5, -38.5, -27.5, -16.5, -5.5, 5.5, 16.5, 27.5, + 38.5, 49.5, +}; + +static long _vq_quantmap__8u1__p8_0[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__8u1__p8_0 = { + _vq_quantthresh__8u1__p8_0, + _vq_quantmap__8u1__p8_0, + 11, + 11 +}; + +static static_codebook _8u1__p8_0 = { + 2, 121, + _vq_lengthlist__8u1__p8_0, + 1, -524582912, 1618345984, 4, 0, + _vq_quantlist__8u1__p8_0, + NULL, + &_vq_auxt__8u1__p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u1__p8_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__8u1__p8_1[] = { + 2, 5, 5, 6, 6, 7, 7, 7, 7, 8, 8, 5, 6, 6, 7, 7, + 7, 7, 8, 8, 8, 8, 5, 6, 6, 7, 7, 7, 7, 8, 8, 8, + 8, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 6, 7, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 7, 7, 7, 8, 8, 8, 8, 8, 8, + 8, 8, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 8, 9, 9, 7, 8, 8, 8, 8, 8, 8, 9, + 8, 9, 9, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 8, 8, + 8, 8, 8, 8, 8, 9, 9, 9, 9, +}; + +static float _vq_quantthresh__8u1__p8_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__8u1__p8_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__8u1__p8_1 = { + _vq_quantthresh__8u1__p8_1, + _vq_quantmap__8u1__p8_1, + 11, + 11 +}; + +static static_codebook _8u1__p8_1 = { + 2, 121, + _vq_lengthlist__8u1__p8_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__8u1__p8_1, + NULL, + &_vq_auxt__8u1__p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__8u1__p9_0[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__8u1__p9_0[] = { + 1, 4, 4,11,11,11,11,11,11,11,11,11,11,11,11, 3, + 11, 8,11,11,11,11,11,11,11,11,11,11,11,11, 3, 9, + 9,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10, +}; + +static float _vq_quantthresh__8u1__p9_0[] = { + -1657.5, -1402.5, -1147.5, -892.5, -637.5, -382.5, -127.5, 127.5, + 382.5, 637.5, 892.5, 1147.5, 1402.5, 1657.5, +}; + +static long _vq_quantmap__8u1__p9_0[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__8u1__p9_0 = { + _vq_quantthresh__8u1__p9_0, + _vq_quantmap__8u1__p9_0, + 15, + 15 +}; + +static static_codebook _8u1__p9_0 = { + 2, 225, + _vq_lengthlist__8u1__p9_0, + 1, -514071552, 1627381760, 4, 0, + _vq_quantlist__8u1__p9_0, + NULL, + &_vq_auxt__8u1__p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__8u1__p9_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__8u1__p9_1[] = { + 1, 4, 4, 7, 7, 9, 9, 7, 7, 8, 8,10,10,11,11, 4, + 7, 7, 9, 9,10,10, 8, 8,10,10,10,11,10,11, 4, 7, + 7, 9, 9,10,10, 8, 8,10, 9,11,11,11,11, 7, 9, 9, + 12,12,11,12,10,10,11,10,12,11,11,11, 7, 9, 9,11, + 11,13,12, 9, 9,11,10,11,11,12,11, 9,10,10,12,12, + 14,14,10,10,11,12,12,11,11,11, 9,10,11,11,13,14, + 13,10,11,11,11,12,11,12,12, 7, 8, 8,10, 9,11,10, + 11,12,12,11,12,14,12,13, 7, 8, 8, 9,10,10,11,12, + 12,12,11,12,12,12,13, 9, 9, 9,11,11,13,12,12,12, + 12,11,12,12,13,12, 8,10,10,11,10,11,12,12,12,12, + 12,12,14,12,12, 9,11,11,11,12,12,12,12,13,13,12, + 12,13,13,12,10,11,11,12,11,12,12,12,11,12,13,12, + 12,12,13,11,11,12,12,12,13,12,12,11,12,13,13,12, + 12,13,12,11,12,12,13,13,12,13,12,13,13,13,13,14, + 13, +}; + +static float _vq_quantthresh__8u1__p9_1[] = { + -110.5, -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, + 25.5, 42.5, 59.5, 76.5, 93.5, 110.5, +}; + +static long _vq_quantmap__8u1__p9_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__8u1__p9_1 = { + _vq_quantthresh__8u1__p9_1, + _vq_quantmap__8u1__p9_1, + 15, + 15 +}; + +static static_codebook _8u1__p9_1 = { + 2, 225, + _vq_lengthlist__8u1__p9_1, + 1, -522338304, 1620115456, 4, 0, + _vq_quantlist__8u1__p9_1, + NULL, + &_vq_auxt__8u1__p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__8u1__p9_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__8u1__p9_2[] = { + 2, 5, 4, 6, 6, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 5, 6, 6, 7, 7, 8, 8, 9, 8, 9, 9, 9, 9, 9, 9, + 9, 9, 5, 6, 6, 7, 7, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 7, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, + 9,10,10, 9, 7, 7, 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9,10,10, 8, 8, 8, 9, 9, 9, 9,10,10,10, 9, + 10,10,10,10,10,10, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, + 10,10,10,10,10,10,10, 9, 9, 9, 9, 9, 9, 9, 9,10, + 10,10,10,10,10,10,10,10, 9, 9, 9, 9, 9,10,10,10, + 10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, 9, 9,10, + 10,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, 9,10, + 10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9,10, + 10,10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, + 9,10,10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10, 9,10, + 9, 9, 9,10,10,10,10,10,10,10,10,10,10,10,10, 9, + 10, 9,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 9, 9,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10, +}; + +static float _vq_quantthresh__8u1__p9_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__8u1__p9_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__8u1__p9_2 = { + _vq_quantthresh__8u1__p9_2, + _vq_quantmap__8u1__p9_2, + 17, + 17 +}; + +static static_codebook _8u1__p9_2 = { + 2, 289, + _vq_lengthlist__8u1__p9_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__8u1__p9_2, + NULL, + &_vq_auxt__8u1__p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__8u1__single[] = { + 4, 7,13, 9,15, 9,16, 8,10,13, 7, 5, 8, 6, 9, 7, + 10, 7,10,11,11, 6, 7, 8, 8, 9, 9, 9,12,16, 8, 5, + 8, 6, 8, 6, 9, 7,10,12,11, 7, 7, 7, 6, 7, 7, 7, + 11,15, 7, 5, 8, 6, 7, 5, 7, 6, 9,13,13, 9, 9, 8, + 6, 6, 5, 5, 9,14, 8, 6, 8, 6, 6, 4, 5, 3, 5,13, + 9, 9,11, 8,10, 7, 8, 4, 5,12,11,16,17,15,17,12, + 13, 8, 8,15, +}; + +static static_codebook _huff_book__8u1__single = { + 2, 100, + _huff_lengthlist__8u1__single, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44u0__long[] = { + 5, 8,13,10,17,11,11,15, 7, 2, 4, 5, 8, 7, 9,16, + 13, 4, 3, 5, 6, 8,11,20,10, 4, 5, 5, 7, 6, 8,18, + 15, 7, 6, 7, 8,10,14,20,10, 6, 7, 6, 9, 7, 8,17, + 9, 8,10, 8,10, 5, 4,11,12,17,19,14,16,10, 7,12, +}; + +static static_codebook _huff_book__44u0__long = { + 2, 64, + _huff_lengthlist__44u0__long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44u0__p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u0__p1_0[] = { + 1, 4, 4, 5, 8, 7, 5, 7, 8, 5, 8, 8, 8,11,11, 8, + 10,10, 5, 8, 8, 8,11,10, 8,11,11, 4, 8, 8, 8,11, + 11, 8,11,11, 8,12,11,11,13,13,11,13,14, 7,11,11, + 10,13,12,11,13,14, 4, 8, 8, 8,11,11, 8,11,12, 8, + 11,11,11,13,13,10,12,13, 8,11,11,11,14,13,11,14, + 13, +}; + +static float _vq_quantthresh__44u0__p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u0__p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u0__p1_0 = { + _vq_quantthresh__44u0__p1_0, + _vq_quantmap__44u0__p1_0, + 3, + 3 +}; + +static static_codebook _44u0__p1_0 = { + 4, 81, + _vq_lengthlist__44u0__p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u0__p1_0, + NULL, + &_vq_auxt__44u0__p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u0__p2_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u0__p2_0[] = { + 2, 4, 4, 5, 6, 6, 5, 6, 6, 5, 7, 7, 7, 8, 8, 6, + 8, 8, 5, 7, 7, 6, 8, 8, 7, 8, 8, 4, 7, 7, 7, 8, + 8, 7, 8, 8, 7, 8, 8, 8, 9,10, 8,10,10, 6, 8, 8, + 8,10, 8, 8,10,10, 5, 7, 7, 7, 8, 8, 7, 8, 8, 6, + 8, 8, 8,10,10, 8, 8,10, 6, 8, 8, 8,10,10, 8,10, + 9, +}; + +static float _vq_quantthresh__44u0__p2_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u0__p2_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u0__p2_0 = { + _vq_quantthresh__44u0__p2_0, + _vq_quantmap__44u0__p2_0, + 3, + 3 +}; + +static static_codebook _44u0__p2_0 = { + 4, 81, + _vq_lengthlist__44u0__p2_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u0__p2_0, + NULL, + &_vq_auxt__44u0__p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u0__p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u0__p3_0[] = { + 1, 5, 5, 8, 8, 5, 8, 7, 9, 9, 5, 7, 8, 9, 9, 9, + 10, 9,12,12, 9, 9,10,12,12, 6, 8, 8,11,10, 8,10, + 10,11,11, 8, 9,10,11,11,10,11,11,14,13,10,11,11, + 13,13, 5, 8, 8,10,10, 8,10,10,11,11, 8,10,10,11, + 11,10,11,11,13,13,10,11,11,13,13, 9,11,11,15,14, + 10,12,12,15,14,10,12,11,15,14,13,14,14,16,16,12, + 14,13,17,15, 9,11,11,14,15,10,11,12,14,16,10,11, + 12,14,16,12,13,14,16,16,13,13,15,15,18, 5, 8, 8, + 11,11, 8,10,10,12,12, 8,10,10,12,13,11,12,12,14, + 14,11,12,12,15,15, 8,10,10,13,13,10,12,12,13,13, + 10,12,12,14,14,12,13,13,15,15,12,13,13,16,16, 7, + 10,10,12,12,10,12,11,13,13,10,12,12,13,14,12,13, + 12,15,14,12,13,13,16,16,10,12,12,17,16,12,13,13, + 16,15,11,13,13,17,17,15,15,15,16,17,14,15,15,19, + 19,10,12,12,15,16,11,13,12,15,18,11,13,13,16,16, + 14,15,15,17,17,14,15,15,17,19, 5, 8, 8,11,11, 8, + 10,10,12,12, 8,10,10,12,12,11,12,12,16,15,11,12, + 12,14,15, 7,10,10,13,13,10,12,12,14,13,10,11,12, + 13,13,12,13,13,16,16,12,12,13,15,15, 8,10,10,13, + 13,10,12,12,14,14,10,12,12,13,13,12,13,13,16,16, + 12,13,13,15,15,10,12,12,16,15,11,13,13,17,16,11, + 12,13,16,15,13,15,15,19,17,14,15,14,17,16,10,12, + 12,16,16,11,13,13,16,17,12,13,13,15,17,14,15,15, + 17,19,14,15,15,17,17, 8,11,11,16,16,10,13,12,17, + 17,10,12,13,16,16,15,17,16,20,19,14,15,17,18,19, + 9,12,12,16,17,11,13,14,17,18,11,13,13,19,18,16, + 17,18,19,19,15,16,16,19,19, 9,12,12,16,17,11,14, + 13,18,17,11,13,13,17,17,16,17,16,20,19,14,16,16, + 18,18,12,15,15,19,17,14,15,16, 0,20,13,15,16,20, + 17,18,16,20, 0, 0,15,16,19,20, 0,12,15,14,18,19, + 13,16,15,20,19,13,16,15,20,18,17,18,17, 0,20,16, + 17,16, 0, 0, 8,11,11,16,15,10,12,12,17,17,10,13, + 13,17,16,14,16,15,18,20,15,16,16,19,19, 9,12,12, + 16,16,11,13,13,17,16,11,13,14,17,18,15,15,16,20, + 20,16,16,17,19,19, 9,13,12,16,17,11,14,13,17,17, + 11,14,14,18,17,14,16,15,18,19,16,17,18,18,19,12, + 14,15,19,18,13,15,16,18, 0,13,14,15, 0, 0,16,16, + 17,20, 0,17,17,20,20, 0,12,15,15,19,20,13,15,15, + 0, 0,14,16,15, 0, 0,15,18,16, 0, 0,17,18,16, 0, + 19, +}; + +static float _vq_quantthresh__44u0__p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u0__p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u0__p3_0 = { + _vq_quantthresh__44u0__p3_0, + _vq_quantmap__44u0__p3_0, + 5, + 5 +}; + +static static_codebook _44u0__p3_0 = { + 4, 625, + _vq_lengthlist__44u0__p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u0__p3_0, + NULL, + &_vq_auxt__44u0__p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u0__p4_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u0__p4_0[] = { + 4, 5, 5, 9, 9, 5, 6, 6, 9, 9, 5, 6, 6, 9, 9, 9, + 10, 9,12,12, 9, 9,10,12,12, 5, 7, 7,10,10, 7, 7, + 8,10,10, 6, 7, 8,10,10,10,10,10,11,13,10, 9,10, + 12,13, 5, 7, 7,10,10, 6, 8, 7,10,10, 7, 8, 7,10, + 10, 9,10,10,12,12,10,10,10,13,11, 9,10,10,13,13, + 10,11,10,13,13,10,10,10,13,13,12,12,13,14,14,12, + 12,13,14,14, 9,10,10,13,13,10,10,10,13,13,10,10, + 10,13,13,12,13,12,15,14,12,13,12,15,15, 5, 7, 6, + 10,10, 7, 8, 8,10,10, 7, 8, 8,10,10,10,11,10,13, + 13,10,10,10,12,12, 7, 8, 8,11,10, 8, 8, 9,10,11, + 8, 9, 9,11,11,11,10,11,11,14,11,11,11,13,13, 6, + 8, 8,10,10, 7, 9, 8,11,10, 8, 9, 9,11,11,10,11, + 10,14,11,10,11,11,13,13,10,11,11,14,13,10,10,11, + 14,13,10,11,11,14,14,12,11,13,12,16,13,14,14,15, + 15,10,10,11,13,14,10,11,10,14,13,10,11,11,14,14, + 12,13,12,15,13,13,13,14,15,16, 5, 7, 7,10,10, 7, + 8, 8,10,10, 7, 8, 8,10,10,10,10,10,13,13,10,10, + 11,12,13, 6, 8, 8,11,10, 8, 9, 9,11,11, 7, 8, 9, + 10,11,10,11,11,13,13,10,10,11,11,13, 6, 8, 8,10, + 11, 8, 9, 9,11,11, 8, 9, 8,12,10,10,11,11,13,13, + 10,11,10,14,11,10,10,10,14,13,10,11,11,14,13,10, + 10,11,13,13,12,14,14,16,16,12,12,13,13,15,10,11, + 11,13,14,10,11,11,14,15,10,11,10,13,13,13,14,13, + 16,16,12,13,11,15,12, 9,10,10,13,13,10,11,11,14, + 13,10,10,11,13,14,13,14,13,16,16,13,13,13,15,16, + 9,10,10,13,13,10,10,11,13,14,10,11,11,15,13,13, + 13,14,14,18,13,13,14,16,15, 9,10,10,13,14,10,11, + 10,14,13,10,11,11,13,14,13,14,13,16,15,13,13,14, + 15,16,12,13,12,16,14,11,11,13,15,15,13,14,13,16, + 15,15,12,16,12,17,14,15,15,17,17,12,13,13,14,16, + 11,13,11,16,15,12,13,14,15,16,14,15,13, 0,14,14, + 16,16, 0, 0, 9,10,10,13,13,10,11,10,14,14,10,11, + 11,13,13,12,13,13,14,16,13,14,14,16,16, 9,10,10, + 14,14,11,11,11,14,13,10,10,11,14,14,13,13,13,16, + 16,13,13,14,14,17, 9,10,10,13,14,10,11,11,13,15, + 10,11,10,14,14,13,13,13,14,17,13,14,13,17,14,12, + 13,13,16,14,13,14,13,16,15,12,12,13,15,16,15,15, + 16,18,16,15,13,15,14, 0,12,12,13,14,16,13,13,14, + 15,16,11,12,11,16,14,15,16,16,17,17,14,15,12,17, + 12, +}; + +static float _vq_quantthresh__44u0__p4_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u0__p4_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u0__p4_0 = { + _vq_quantthresh__44u0__p4_0, + _vq_quantmap__44u0__p4_0, + 5, + 5 +}; + +static static_codebook _44u0__p4_0 = { + 4, 625, + _vq_lengthlist__44u0__p4_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u0__p4_0, + NULL, + &_vq_auxt__44u0__p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u0__p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u0__p5_0[] = { + 1, 4, 4, 7, 7, 7, 7, 9, 9, 4, 6, 6, 8, 8, 8, 8, + 9, 9, 4, 6, 6, 8, 8, 8, 8, 9, 9, 7, 8, 8, 9, 9, + 9, 9,11,10, 7, 8, 8, 9, 9, 9, 9,10,10, 7, 8, 8, + 9, 9,10,10,11,11, 7, 8, 8, 9, 9,10,10,11,11, 9, + 9, 9,10,10,11,11,12,12, 9, 9, 9,10,11,11,11,12, + 12, +}; + +static float _vq_quantthresh__44u0__p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44u0__p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u0__p5_0 = { + _vq_quantthresh__44u0__p5_0, + _vq_quantmap__44u0__p5_0, + 9, + 9 +}; + +static static_codebook _44u0__p5_0 = { + 2, 81, + _vq_lengthlist__44u0__p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44u0__p5_0, + NULL, + &_vq_auxt__44u0__p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u0__p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u0__p6_0[] = { + 1, 4, 4, 6, 6, 8, 8,10, 9,11,10,14,13, 4, 6, 5, + 8, 8, 9, 9,11,10,11,11,14,14, 4, 5, 6, 8, 8, 9, + 9,10,10,11,11,14,14, 6, 8, 8, 9, 9,10,10,11,11, + 12,12,16,15, 7, 8, 8, 9, 9,10,10,11,11,12,12,15, + 15, 9,10,10,10,10,11,11,12,12,12,12,15,15, 9,10, + 9,10,11,11,11,12,12,12,13,15,15,10,10,11,11,11, + 12,12,13,12,13,13,16,15,10,11,11,11,11,12,12,13, + 12,13,13,16,17,11,11,12,12,12,13,13,13,14,14,15, + 17,17,11,11,12,12,12,13,13,13,14,14,14,16,18,14, + 15,15,15,15,16,16,16,16,17,18, 0, 0,14,15,15,15, + 15,17,16,17,18,17,17,18, 0, +}; + +static float _vq_quantthresh__44u0__p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44u0__p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u0__p6_0 = { + _vq_quantthresh__44u0__p6_0, + _vq_quantmap__44u0__p6_0, + 13, + 13 +}; + +static static_codebook _44u0__p6_0 = { + 2, 169, + _vq_lengthlist__44u0__p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44u0__p6_0, + NULL, + &_vq_auxt__44u0__p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u0__p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u0__p6_1[] = { + 2, 4, 4, 5, 5, 4, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 5, 6, 6, 6, 6, +}; + +static float _vq_quantthresh__44u0__p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u0__p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u0__p6_1 = { + _vq_quantthresh__44u0__p6_1, + _vq_quantmap__44u0__p6_1, + 5, + 5 +}; + +static static_codebook _44u0__p6_1 = { + 2, 25, + _vq_lengthlist__44u0__p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u0__p6_1, + NULL, + &_vq_auxt__44u0__p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u0__p7_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u0__p7_0[] = { + 1, 4, 4,11,11, 9,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11, 9,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,10,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10, +}; + +static float _vq_quantthresh__44u0__p7_0[] = { + -253.5, -84.5, 84.5, 253.5, +}; + +static long _vq_quantmap__44u0__p7_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u0__p7_0 = { + _vq_quantthresh__44u0__p7_0, + _vq_quantmap__44u0__p7_0, + 5, + 5 +}; + +static static_codebook _44u0__p7_0 = { + 4, 625, + _vq_lengthlist__44u0__p7_0, + 1, -518709248, 1626677248, 3, 0, + _vq_quantlist__44u0__p7_0, + NULL, + &_vq_auxt__44u0__p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u0__p7_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u0__p7_1[] = { + 1, 4, 4, 6, 6, 6, 6, 7, 7, 8, 8, 9, 9, 5, 7, 7, + 8, 7, 7, 7, 9, 8,10, 9,10,11, 5, 7, 7, 8, 8, 7, + 7, 8, 9,10,10,11,11, 6, 8, 8, 9, 9, 9, 9,11,10, + 12,12,15,12, 6, 8, 8, 9, 9, 9, 9,11,11,12,11,14, + 12, 7, 8, 8,10,10,12,12,13,13,13,15,13,13, 7, 8, + 8,10,10,11,11,13,12,14,15,15,15, 9,10,10,11,12, + 13,13,14,15,14,15,14,15, 8,10,10,12,12,14,14,15, + 14,14,15,15,14,10,12,12,14,14,15,14,15,15,15,14, + 15,15,10,12,12,13,14,15,14,15,15,14,15,15,15,12, + 15,13,15,14,15,15,15,15,15,15,15,15,13,13,15,15, + 15,15,15,15,15,15,15,15,15, +}; + +static float _vq_quantthresh__44u0__p7_1[] = { + -71.5, -58.5, -45.5, -32.5, -19.5, -6.5, 6.5, 19.5, + 32.5, 45.5, 58.5, 71.5, +}; + +static long _vq_quantmap__44u0__p7_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u0__p7_1 = { + _vq_quantthresh__44u0__p7_1, + _vq_quantmap__44u0__p7_1, + 13, + 13 +}; + +static static_codebook _44u0__p7_1 = { + 2, 169, + _vq_lengthlist__44u0__p7_1, + 1, -523010048, 1618608128, 4, 0, + _vq_quantlist__44u0__p7_1, + NULL, + &_vq_auxt__44u0__p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u0__p7_2[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u0__p7_2[] = { + 2, 5, 4, 6, 6, 7, 7, 8, 8, 8, 8, 9, 8, 5, 5, 6, + 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 5, 6, 5, 7, 7, 8, + 8, 8, 8, 9, 9, 9, 9, 6, 7, 7, 8, 8, 8, 8, 9, 8, + 9, 9, 9, 9, 6, 7, 7, 8, 7, 8, 8, 9, 9, 9, 9, 9, + 9, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 7, 8, + 8, 9, 8, 9, 8, 9, 9, 9, 9, 9, 9, 8, 9, 8, 9, 9, + 9, 9, 9, 9, 9, 9,10,10, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9,10, 9,10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10,10,10, 9, 9, 9, 9, 9, + 9, 9, 9,10, 9, 9,10,10, 9, +}; + +static float _vq_quantthresh__44u0__p7_2[] = { + -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, + 2.5, 3.5, 4.5, 5.5, +}; + +static long _vq_quantmap__44u0__p7_2[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u0__p7_2 = { + _vq_quantthresh__44u0__p7_2, + _vq_quantmap__44u0__p7_2, + 13, + 13 +}; + +static static_codebook _44u0__p7_2 = { + 2, 169, + _vq_lengthlist__44u0__p7_2, + 1, -531103744, 1611661312, 4, 0, + _vq_quantlist__44u0__p7_2, + NULL, + &_vq_auxt__44u0__p7_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44u0__short[] = { + 12,13,14,13,17,12,15,17, 5, 5, 6,10,10,11,15,16, + 4, 3, 3, 7, 5, 7,10,16, 7, 7, 7,10, 9,11,12,16, + 6, 5, 5, 9, 5, 6,10,16, 8, 7, 7, 9, 6, 7, 9,16, + 11, 7, 3, 6, 4, 5, 8,16,12, 9, 4, 8, 5, 7, 9,16, +}; + +static static_codebook _huff_book__44u0__short = { + 2, 64, + _huff_lengthlist__44u0__short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44u1__long[] = { + 5, 8,13,10,17,11,11,15, 7, 2, 4, 5, 8, 7, 9,16, + 13, 4, 3, 5, 6, 8,11,20,10, 4, 5, 5, 7, 6, 8,18, + 15, 7, 6, 7, 8,10,14,20,10, 6, 7, 6, 9, 7, 8,17, + 9, 8,10, 8,10, 5, 4,11,12,17,19,14,16,10, 7,12, +}; + +static static_codebook _huff_book__44u1__long = { + 2, 64, + _huff_lengthlist__44u1__long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44u1__p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u1__p1_0[] = { + 1, 4, 4, 5, 8, 7, 5, 7, 8, 5, 8, 8, 8,11,11, 8, + 10,10, 5, 8, 8, 8,11,10, 8,11,11, 4, 8, 8, 8,11, + 11, 8,11,11, 8,12,11,11,13,13,11,13,14, 7,11,11, + 10,13,12,11,13,14, 4, 8, 8, 8,11,11, 8,11,12, 8, + 11,11,11,13,13,10,12,13, 8,11,11,11,14,13,11,14, + 13, +}; + +static float _vq_quantthresh__44u1__p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u1__p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u1__p1_0 = { + _vq_quantthresh__44u1__p1_0, + _vq_quantmap__44u1__p1_0, + 3, + 3 +}; + +static static_codebook _44u1__p1_0 = { + 4, 81, + _vq_lengthlist__44u1__p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u1__p1_0, + NULL, + &_vq_auxt__44u1__p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u1__p2_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u1__p2_0[] = { + 2, 4, 4, 5, 6, 6, 5, 6, 6, 5, 7, 7, 7, 8, 8, 6, + 8, 8, 5, 7, 7, 6, 8, 8, 7, 8, 8, 4, 7, 7, 7, 8, + 8, 7, 8, 8, 7, 8, 8, 8, 9,10, 8,10,10, 6, 8, 8, + 8,10, 8, 8,10,10, 5, 7, 7, 7, 8, 8, 7, 8, 8, 6, + 8, 8, 8,10,10, 8, 8,10, 6, 8, 8, 8,10,10, 8,10, + 9, +}; + +static float _vq_quantthresh__44u1__p2_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u1__p2_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u1__p2_0 = { + _vq_quantthresh__44u1__p2_0, + _vq_quantmap__44u1__p2_0, + 3, + 3 +}; + +static static_codebook _44u1__p2_0 = { + 4, 81, + _vq_lengthlist__44u1__p2_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u1__p2_0, + NULL, + &_vq_auxt__44u1__p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u1__p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u1__p3_0[] = { + 1, 5, 5, 8, 8, 5, 8, 7, 9, 9, 5, 7, 8, 9, 9, 9, + 10, 9,12,12, 9, 9,10,12,12, 6, 8, 8,11,10, 8,10, + 10,11,11, 8, 9,10,11,11,10,11,11,14,13,10,11,11, + 13,13, 5, 8, 8,10,10, 8,10,10,11,11, 8,10,10,11, + 11,10,11,11,13,13,10,11,11,13,13, 9,11,11,15,14, + 10,12,12,15,14,10,12,11,15,14,13,14,14,16,16,12, + 14,13,17,15, 9,11,11,14,15,10,11,12,14,16,10,11, + 12,14,16,12,13,14,16,16,13,13,15,15,18, 5, 8, 8, + 11,11, 8,10,10,12,12, 8,10,10,12,13,11,12,12,14, + 14,11,12,12,15,15, 8,10,10,13,13,10,12,12,13,13, + 10,12,12,14,14,12,13,13,15,15,12,13,13,16,16, 7, + 10,10,12,12,10,12,11,13,13,10,12,12,13,14,12,13, + 12,15,14,12,13,13,16,16,10,12,12,17,16,12,13,13, + 16,15,11,13,13,17,17,15,15,15,16,17,14,15,15,19, + 19,10,12,12,15,16,11,13,12,15,18,11,13,13,16,16, + 14,15,15,17,17,14,15,15,17,19, 5, 8, 8,11,11, 8, + 10,10,12,12, 8,10,10,12,12,11,12,12,16,15,11,12, + 12,14,15, 7,10,10,13,13,10,12,12,14,13,10,11,12, + 13,13,12,13,13,16,16,12,12,13,15,15, 8,10,10,13, + 13,10,12,12,14,14,10,12,12,13,13,12,13,13,16,16, + 12,13,13,15,15,10,12,12,16,15,11,13,13,17,16,11, + 12,13,16,15,13,15,15,19,17,14,15,14,17,16,10,12, + 12,16,16,11,13,13,16,17,12,13,13,15,17,14,15,15, + 17,19,14,15,15,17,17, 8,11,11,16,16,10,13,12,17, + 17,10,12,13,16,16,15,17,16,20,19,14,15,17,18,19, + 9,12,12,16,17,11,13,14,17,18,11,13,13,19,18,16, + 17,18,19,19,15,16,16,19,19, 9,12,12,16,17,11,14, + 13,18,17,11,13,13,17,17,16,17,16,20,19,14,16,16, + 18,18,12,15,15,19,17,14,15,16, 0,20,13,15,16,20, + 17,18,16,20, 0, 0,15,16,19,20, 0,12,15,14,18,19, + 13,16,15,20,19,13,16,15,20,18,17,18,17, 0,20,16, + 17,16, 0, 0, 8,11,11,16,15,10,12,12,17,17,10,13, + 13,17,16,14,16,15,18,20,15,16,16,19,19, 9,12,12, + 16,16,11,13,13,17,16,11,13,14,17,18,15,15,16,20, + 20,16,16,17,19,19, 9,13,12,16,17,11,14,13,17,17, + 11,14,14,18,17,14,16,15,18,19,16,17,18,18,19,12, + 14,15,19,18,13,15,16,18, 0,13,14,15, 0, 0,16,16, + 17,20, 0,17,17,20,20, 0,12,15,15,19,20,13,15,15, + 0, 0,14,16,15, 0, 0,15,18,16, 0, 0,17,18,16, 0, + 19, +}; + +static float _vq_quantthresh__44u1__p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u1__p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u1__p3_0 = { + _vq_quantthresh__44u1__p3_0, + _vq_quantmap__44u1__p3_0, + 5, + 5 +}; + +static static_codebook _44u1__p3_0 = { + 4, 625, + _vq_lengthlist__44u1__p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u1__p3_0, + NULL, + &_vq_auxt__44u1__p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u1__p4_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u1__p4_0[] = { + 4, 5, 5, 9, 9, 5, 6, 6, 9, 9, 5, 6, 6, 9, 9, 9, + 10, 9,12,12, 9, 9,10,12,12, 5, 7, 7,10,10, 7, 7, + 8,10,10, 6, 7, 8,10,10,10,10,10,11,13,10, 9,10, + 12,13, 5, 7, 7,10,10, 6, 8, 7,10,10, 7, 8, 7,10, + 10, 9,10,10,12,12,10,10,10,13,11, 9,10,10,13,13, + 10,11,10,13,13,10,10,10,13,13,12,12,13,14,14,12, + 12,13,14,14, 9,10,10,13,13,10,10,10,13,13,10,10, + 10,13,13,12,13,12,15,14,12,13,12,15,15, 5, 7, 6, + 10,10, 7, 8, 8,10,10, 7, 8, 8,10,10,10,11,10,13, + 13,10,10,10,12,12, 7, 8, 8,11,10, 8, 8, 9,10,11, + 8, 9, 9,11,11,11,10,11,11,14,11,11,11,13,13, 6, + 8, 8,10,10, 7, 9, 8,11,10, 8, 9, 9,11,11,10,11, + 10,14,11,10,11,11,13,13,10,11,11,14,13,10,10,11, + 14,13,10,11,11,14,14,12,11,13,12,16,13,14,14,15, + 15,10,10,11,13,14,10,11,10,14,13,10,11,11,14,14, + 12,13,12,15,13,13,13,14,15,16, 5, 7, 7,10,10, 7, + 8, 8,10,10, 7, 8, 8,10,10,10,10,10,13,13,10,10, + 11,12,13, 6, 8, 8,11,10, 8, 9, 9,11,11, 7, 8, 9, + 10,11,10,11,11,13,13,10,10,11,11,13, 6, 8, 8,10, + 11, 8, 9, 9,11,11, 8, 9, 8,12,10,10,11,11,13,13, + 10,11,10,14,11,10,10,10,14,13,10,11,11,14,13,10, + 10,11,13,13,12,14,14,16,16,12,12,13,13,15,10,11, + 11,13,14,10,11,11,14,15,10,11,10,13,13,13,14,13, + 16,16,12,13,11,15,12, 9,10,10,13,13,10,11,11,14, + 13,10,10,11,13,14,13,14,13,16,16,13,13,13,15,16, + 9,10,10,13,13,10,10,11,13,14,10,11,11,15,13,13, + 13,14,14,18,13,13,14,16,15, 9,10,10,13,14,10,11, + 10,14,13,10,11,11,13,14,13,14,13,16,15,13,13,14, + 15,16,12,13,12,16,14,11,11,13,15,15,13,14,13,16, + 15,15,12,16,12,17,14,15,15,17,17,12,13,13,14,16, + 11,13,11,16,15,12,13,14,15,16,14,15,13, 0,14,14, + 16,16, 0, 0, 9,10,10,13,13,10,11,10,14,14,10,11, + 11,13,13,12,13,13,14,16,13,14,14,16,16, 9,10,10, + 14,14,11,11,11,14,13,10,10,11,14,14,13,13,13,16, + 16,13,13,14,14,17, 9,10,10,13,14,10,11,11,13,15, + 10,11,10,14,14,13,13,13,14,17,13,14,13,17,14,12, + 13,13,16,14,13,14,13,16,15,12,12,13,15,16,15,15, + 16,18,16,15,13,15,14, 0,12,12,13,14,16,13,13,14, + 15,16,11,12,11,16,14,15,16,16,17,17,14,15,12,17, + 12, +}; + +static float _vq_quantthresh__44u1__p4_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u1__p4_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u1__p4_0 = { + _vq_quantthresh__44u1__p4_0, + _vq_quantmap__44u1__p4_0, + 5, + 5 +}; + +static static_codebook _44u1__p4_0 = { + 4, 625, + _vq_lengthlist__44u1__p4_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u1__p4_0, + NULL, + &_vq_auxt__44u1__p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u1__p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u1__p5_0[] = { + 1, 4, 4, 7, 7, 7, 7, 9, 9, 4, 6, 6, 8, 8, 8, 8, + 9, 9, 4, 6, 6, 8, 8, 8, 8, 9, 9, 7, 8, 8, 9, 9, + 9, 9,11,10, 7, 8, 8, 9, 9, 9, 9,10,10, 7, 8, 8, + 9, 9,10,10,11,11, 7, 8, 8, 9, 9,10,10,11,11, 9, + 9, 9,10,10,11,11,12,12, 9, 9, 9,10,11,11,11,12, + 12, +}; + +static float _vq_quantthresh__44u1__p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44u1__p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u1__p5_0 = { + _vq_quantthresh__44u1__p5_0, + _vq_quantmap__44u1__p5_0, + 9, + 9 +}; + +static static_codebook _44u1__p5_0 = { + 2, 81, + _vq_lengthlist__44u1__p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44u1__p5_0, + NULL, + &_vq_auxt__44u1__p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u1__p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u1__p6_0[] = { + 1, 4, 4, 6, 6, 8, 8,10, 9,11,10,14,13, 4, 6, 5, + 8, 8, 9, 9,11,10,11,11,14,14, 4, 5, 6, 8, 8, 9, + 9,10,10,11,11,14,14, 6, 8, 8, 9, 9,10,10,11,11, + 12,12,16,15, 7, 8, 8, 9, 9,10,10,11,11,12,12,15, + 15, 9,10,10,10,10,11,11,12,12,12,12,15,15, 9,10, + 9,10,11,11,11,12,12,12,13,15,15,10,10,11,11,11, + 12,12,13,12,13,13,16,15,10,11,11,11,11,12,12,13, + 12,13,13,16,17,11,11,12,12,12,13,13,13,14,14,15, + 17,17,11,11,12,12,12,13,13,13,14,14,14,16,18,14, + 15,15,15,15,16,16,16,16,17,18, 0, 0,14,15,15,15, + 15,17,16,17,18,17,17,18, 0, +}; + +static float _vq_quantthresh__44u1__p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44u1__p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u1__p6_0 = { + _vq_quantthresh__44u1__p6_0, + _vq_quantmap__44u1__p6_0, + 13, + 13 +}; + +static static_codebook _44u1__p6_0 = { + 2, 169, + _vq_lengthlist__44u1__p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44u1__p6_0, + NULL, + &_vq_auxt__44u1__p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u1__p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u1__p6_1[] = { + 2, 4, 4, 5, 5, 4, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 5, 6, 6, 6, 6, +}; + +static float _vq_quantthresh__44u1__p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u1__p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u1__p6_1 = { + _vq_quantthresh__44u1__p6_1, + _vq_quantmap__44u1__p6_1, + 5, + 5 +}; + +static static_codebook _44u1__p6_1 = { + 2, 25, + _vq_lengthlist__44u1__p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u1__p6_1, + NULL, + &_vq_auxt__44u1__p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u1__p7_0[] = { + 3, + 2, + 4, + 1, + 5, + 0, + 6, +}; + +static long _vq_lengthlist__44u1__p7_0[] = { + 1, 3, 2, 9, 9, 7, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, +}; + +static float _vq_quantthresh__44u1__p7_0[] = { + -422.5, -253.5, -84.5, 84.5, 253.5, 422.5, +}; + +static long _vq_quantmap__44u1__p7_0[] = { + 5, 3, 1, 0, 2, 4, 6, +}; + +static encode_aux_threshmatch _vq_auxt__44u1__p7_0 = { + _vq_quantthresh__44u1__p7_0, + _vq_quantmap__44u1__p7_0, + 7, + 7 +}; + +static static_codebook _44u1__p7_0 = { + 2, 49, + _vq_lengthlist__44u1__p7_0, + 1, -518017024, 1626677248, 3, 0, + _vq_quantlist__44u1__p7_0, + NULL, + &_vq_auxt__44u1__p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u1__p7_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u1__p7_1[] = { + 1, 4, 4, 6, 6, 6, 6, 7, 7, 8, 8, 9, 9, 5, 7, 7, + 8, 7, 7, 7, 9, 8,10, 9,10,11, 5, 7, 7, 8, 8, 7, + 7, 8, 9,10,10,11,11, 6, 8, 8, 9, 9, 9, 9,11,10, + 12,12,15,12, 6, 8, 8, 9, 9, 9, 9,11,11,12,11,14, + 12, 7, 8, 8,10,10,12,12,13,13,13,15,13,13, 7, 8, + 8,10,10,11,11,13,12,14,15,15,15, 9,10,10,11,12, + 13,13,14,15,14,15,14,15, 8,10,10,12,12,14,14,15, + 14,14,15,15,14,10,12,12,14,14,15,14,15,15,15,14, + 15,15,10,12,12,13,14,15,14,15,15,14,15,15,15,12, + 15,13,15,14,15,15,15,15,15,15,15,15,13,13,15,15, + 15,15,15,15,15,15,15,15,15, +}; + +static float _vq_quantthresh__44u1__p7_1[] = { + -71.5, -58.5, -45.5, -32.5, -19.5, -6.5, 6.5, 19.5, + 32.5, 45.5, 58.5, 71.5, +}; + +static long _vq_quantmap__44u1__p7_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u1__p7_1 = { + _vq_quantthresh__44u1__p7_1, + _vq_quantmap__44u1__p7_1, + 13, + 13 +}; + +static static_codebook _44u1__p7_1 = { + 2, 169, + _vq_lengthlist__44u1__p7_1, + 1, -523010048, 1618608128, 4, 0, + _vq_quantlist__44u1__p7_1, + NULL, + &_vq_auxt__44u1__p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u1__p7_2[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u1__p7_2[] = { + 2, 5, 4, 6, 6, 7, 7, 8, 8, 8, 8, 9, 8, 5, 5, 6, + 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 5, 6, 5, 7, 7, 8, + 8, 8, 8, 9, 9, 9, 9, 6, 7, 7, 8, 8, 8, 8, 9, 8, + 9, 9, 9, 9, 6, 7, 7, 8, 7, 8, 8, 9, 9, 9, 9, 9, + 9, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 7, 8, + 8, 9, 8, 9, 8, 9, 9, 9, 9, 9, 9, 8, 9, 8, 9, 9, + 9, 9, 9, 9, 9, 9,10,10, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9,10, 9,10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10,10,10, 9, 9, 9, 9, 9, + 9, 9, 9,10, 9, 9,10,10, 9, +}; + +static float _vq_quantthresh__44u1__p7_2[] = { + -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, + 2.5, 3.5, 4.5, 5.5, +}; + +static long _vq_quantmap__44u1__p7_2[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u1__p7_2 = { + _vq_quantthresh__44u1__p7_2, + _vq_quantmap__44u1__p7_2, + 13, + 13 +}; + +static static_codebook _44u1__p7_2 = { + 2, 169, + _vq_lengthlist__44u1__p7_2, + 1, -531103744, 1611661312, 4, 0, + _vq_quantlist__44u1__p7_2, + NULL, + &_vq_auxt__44u1__p7_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44u1__short[] = { + 12,13,14,13,17,12,15,17, 5, 5, 6,10,10,11,15,16, + 4, 3, 3, 7, 5, 7,10,16, 7, 7, 7,10, 9,11,12,16, + 6, 5, 5, 9, 5, 6,10,16, 8, 7, 7, 9, 6, 7, 9,16, + 11, 7, 3, 6, 4, 5, 8,16,12, 9, 4, 8, 5, 7, 9,16, +}; + +static static_codebook _huff_book__44u1__short = { + 2, 64, + _huff_lengthlist__44u1__short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44u2__long[] = { + 5, 9,14,12,15,13,10,13, 7, 4, 5, 6, 8, 7, 8,12, + 13, 4, 3, 5, 5, 6, 9,15,12, 6, 5, 6, 6, 6, 7,14, + 14, 7, 4, 6, 4, 6, 8,15,12, 6, 6, 5, 5, 5, 6,14, + 9, 7, 8, 6, 7, 5, 4,10,10,13,14,14,15,10, 6, 8, +}; + +static static_codebook _huff_book__44u2__long = { + 2, 64, + _huff_lengthlist__44u2__long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44u2__p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u2__p1_0[] = { + 1, 4, 4, 5, 8, 7, 5, 7, 8, 5, 8, 8, 8,11,11, 8, + 10,11, 5, 8, 8, 8,11,10, 8,11,11, 4, 8, 8, 8,11, + 11, 8,11,11, 8,11,11,11,13,14,11,13,13, 7,11,11, + 10,13,12,11,14,14, 4, 8, 8, 8,11,11, 8,11,11, 8, + 11,11,11,14,13,10,12,13, 8,11,11,11,13,13,11,13, + 13, +}; + +static float _vq_quantthresh__44u2__p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u2__p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u2__p1_0 = { + _vq_quantthresh__44u2__p1_0, + _vq_quantmap__44u2__p1_0, + 3, + 3 +}; + +static static_codebook _44u2__p1_0 = { + 4, 81, + _vq_lengthlist__44u2__p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u2__p1_0, + NULL, + &_vq_auxt__44u2__p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u2__p2_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u2__p2_0[] = { + 2, 5, 5, 5, 6, 6, 5, 6, 6, 5, 6, 6, 7, 8, 8, 6, + 8, 8, 5, 6, 6, 6, 8, 7, 7, 8, 8, 5, 6, 6, 7, 8, + 8, 6, 8, 8, 6, 8, 8, 8, 9,10, 8,10,10, 6, 8, 8, + 7,10, 8, 8,10,10, 5, 6, 6, 6, 8, 8, 7, 8, 8, 6, + 8, 8, 8,10,10, 8, 8,10, 6, 8, 8, 8,10,10, 8,10, + 9, +}; + +static float _vq_quantthresh__44u2__p2_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u2__p2_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u2__p2_0 = { + _vq_quantthresh__44u2__p2_0, + _vq_quantmap__44u2__p2_0, + 3, + 3 +}; + +static static_codebook _44u2__p2_0 = { + 4, 81, + _vq_lengthlist__44u2__p2_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u2__p2_0, + NULL, + &_vq_auxt__44u2__p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u2__p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u2__p3_0[] = { + 2, 4, 4, 7, 8, 5, 7, 7, 9, 9, 5, 7, 7, 9, 9, 8, + 9, 9,12,11, 8, 9, 9,11,12, 5, 7, 7,10,10, 7, 9, + 9,11,11, 7, 9, 9,10,11,10,11,11,13,13, 9,10,11, + 12,13, 5, 7, 7,10,10, 7, 9, 9,11,10, 7, 9, 9,11, + 11, 9,11,10,13,13,10,11,11,13,13, 8,10,10,14,13, + 10,11,11,15,14, 9,11,11,15,14,13,14,13,16,14,12, + 13,13,15,16, 8,10,10,13,14, 9,11,11,14,15,10,11, + 11,14,15,12,13,13,15,15,12,13,14,15,16, 5, 7, 7, + 10,10, 7, 9, 9,11,11, 7, 9, 9,11,12,10,11,11,14, + 13,10,11,11,14,14, 7, 9, 9,12,12, 9,11,11,13,13, + 9,11,11,13,13,12,13,12,14,14,11,12,13,15,15, 7, + 9, 9,12,12, 8,11,10,13,12, 9,11,11,13,13,11,13, + 12,15,13,11,13,13,15,16, 9,12,11,15,15,11,12,12, + 16,15,11,12,13,16,16,13,14,15,16,15,13,15,15,17, + 17, 9,11,11,14,15,10,12,12,15,15,11,13,12,15,16, + 13,15,14,16,16,13,15,15,17,19, 5, 7, 7,10,10, 7, + 9, 9,12,11, 7, 9, 9,11,11,10,11,11,14,14,10,11, + 11,13,14, 7, 9, 9,12,12, 9,11,11,13,13, 9,10,11, + 12,13,11,13,12,16,15,11,12,12,14,15, 7, 9, 9,12, + 12, 9,11,11,13,13, 9,11,11,13,12,11,13,12,15,16, + 12,13,13,15,14, 9,11,11,15,14,11,13,12,16,15,10, + 11,12,15,15,13,14,14,18,17,13,14,14,15,17,10,11, + 11,14,15,11,13,12,15,17,11,13,12,15,16,13,15,14, + 18,17,14,15,15,16,18, 7,10,10,14,14,10,12,12,15, + 15,10,12,12,15,15,14,15,15,18,17,13,15,15,16,16, + 9,11,11,16,15,11,13,13,16,18,11,13,13,16,16,15, + 16,16, 0, 0,14,15,16,18,17, 9,11,11,15,15,10,13, + 12,17,16,11,12,13,16,17,14,15,16,19,19,14,15,15, + 0,20,12,14,14, 0, 0,13,14,16,19,18,13,15,16,20, + 17,16,18, 0, 0, 0,15,16,17,18,19,11,14,14, 0,19, + 12,15,14,17,17,13,15,15, 0, 0,16,17,15,20,19,15, + 17,16,19, 0, 8,10,10,14,15,10,12,11,15,15,10,11, + 12,16,15,13,14,14,19,17,14,15,15, 0, 0, 9,11,11, + 16,15,11,13,13,17,16,10,12,13,16,17,14,15,15,18, + 18,14,15,16,20,19, 9,12,12, 0,15,11,13,13,16,17, + 11,13,13,19,17,14,16,16,18,17,15,16,16,17,19,11, + 14,14,18,18,13,14,15, 0, 0,12,14,15,19,18,15,16, + 19, 0,19,15,16,19,19,17,12,14,14,16,19,13,15,15, + 0,17,13,15,14,18,18,15,16,15, 0,18,16,17,17, 0, + 0, +}; + +static float _vq_quantthresh__44u2__p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u2__p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u2__p3_0 = { + _vq_quantthresh__44u2__p3_0, + _vq_quantmap__44u2__p3_0, + 5, + 5 +}; + +static static_codebook _44u2__p3_0 = { + 4, 625, + _vq_lengthlist__44u2__p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u2__p3_0, + NULL, + &_vq_auxt__44u2__p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u2__p4_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u2__p4_0[] = { + 4, 5, 5, 8, 8, 5, 7, 6, 9, 9, 5, 6, 7, 9, 9, 9, + 9, 9,11,11, 9, 9, 9,11,11, 5, 7, 7, 9, 9, 7, 8, + 8,10,10, 7, 7, 8,10,10,10,10,10,11,12, 9,10,10, + 11,12, 5, 7, 7, 9, 9, 6, 8, 7,10,10, 7, 8, 8,10, + 10, 9,10,10,12,11, 9,10,10,12,11, 9,10,10,12,12, + 10,10,10,13,12, 9,10,10,12,13,12,12,12,14,14,11, + 12,12,13,14, 9,10,10,12,12, 9,10,10,12,13,10,10, + 10,12,13,11,12,12,14,13,12,12,12,14,13, 5, 7, 7, + 10, 9, 7, 8, 8,10,10, 7, 8, 8,10,10,10,10,10,12, + 12,10,10,10,12,12, 7, 8, 8,11,10, 8, 8, 9,11,11, + 8, 9, 9,11,11,10,11,11,12,13,10,11,11,13,13, 6, + 8, 8,10,10, 7, 9, 8,11,10, 8, 9, 9,11,11,10,11, + 10,13,11,10,11,11,13,13, 9,10,10,13,13,10,11,11, + 13,13,10,11,11,14,13,12,11,13,12,15,12,13,13,15, + 15, 9,10,10,12,13,10,11,10,13,13,10,11,11,13,13, + 12,13,11,15,13,12,13,13,15,15, 5, 7, 7, 9,10, 7, + 8, 8,10,10, 7, 8, 8,10,10,10,10,10,12,12,10,10, + 11,12,12, 6, 8, 8,10,10, 8, 9, 9,11,11, 7, 8, 9, + 10,11,10,11,11,13,13,10,10,11,11,13, 7, 8, 8,10, + 11, 8, 9, 9,11,11, 8, 9, 8,11,11,10,11,11,13,13, + 10,11,11,13,12, 9,10,10,13,12,10,11,11,14,13,10, + 10,11,13,13,12,13,13,15,15,12,11,13,12,14, 9,10, + 10,12,13,10,11,11,13,14,10,11,11,13,13,12,13,13, + 15,15,12,13,12,15,12, 8, 9, 9,12,12, 9,11,10,13, + 13, 9,10,10,13,13,12,13,13,15,15,12,12,12,14,14, + 9,10,10,13,13,10,11,11,13,14,10,11,11,14,12,13, + 13,14,14,16,12,13,13,15,14, 9,10,10,13,13,10,11, + 10,14,13,10,11,11,13,14,12,14,13,16,14,13,13,13, + 14,15,11,13,12,15,14,11,12,13,14,15,12,13,13,16, + 15,14,12,15,12,16,14,15,15,17,16,11,12,12,14,15, + 11,13,11,15,14,12,13,13,15,16,13,15,12,17,13,14, + 15,15,16,16, 8, 9, 9,12,12, 9,10,10,13,13, 9,10, + 10,13,13,12,13,12,14,14,12,13,13,15,15, 9,10,10, + 13,13,10,11,11,14,13,10,10,11,13,14,12,13,13,15, + 14,12,12,14,14,16, 9,10,10,13,13,10,11,11,13,14, + 10,11,11,14,13,13,13,13,15,15,13,14,13,16,14,11, + 12,12,14,14,12,13,13,16,15,11,12,13,14,15,14,15, + 15,16,16,14,13,15,13,17,11,12,12,14,15,12,13,13, + 15,16,11,13,12,15,15,14,15,14,16,16,14,15,12,17, + 13, +}; + +static float _vq_quantthresh__44u2__p4_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u2__p4_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u2__p4_0 = { + _vq_quantthresh__44u2__p4_0, + _vq_quantmap__44u2__p4_0, + 5, + 5 +}; + +static static_codebook _44u2__p4_0 = { + 4, 625, + _vq_lengthlist__44u2__p4_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u2__p4_0, + NULL, + &_vq_auxt__44u2__p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u2__p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u2__p5_0[] = { + 1, 4, 4, 7, 7, 8, 8, 9, 9, 4, 6, 5, 8, 8, 8, 8, + 10,10, 4, 5, 6, 8, 8, 8, 8,10,10, 7, 8, 8, 9, 9, + 9, 9,11,11, 7, 8, 8, 9, 9, 9, 9,11,11, 8, 8, 8, + 9, 9,10,11,12,12, 8, 8, 8, 9, 9,10,10,12,12,10, + 10,10,11,11,12,12,13,13,10,10,10,11,11,12,12,13, + 13, +}; + +static float _vq_quantthresh__44u2__p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44u2__p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u2__p5_0 = { + _vq_quantthresh__44u2__p5_0, + _vq_quantmap__44u2__p5_0, + 9, + 9 +}; + +static static_codebook _44u2__p5_0 = { + 2, 81, + _vq_lengthlist__44u2__p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44u2__p5_0, + NULL, + &_vq_auxt__44u2__p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u2__p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u2__p6_0[] = { + 1, 4, 4, 6, 6, 8, 8,10,10,11,11,14,13, 4, 6, 5, + 8, 8, 9, 9,11,10,12,11,15,14, 4, 5, 6, 8, 8, 9, + 9,11,11,11,11,14,14, 6, 8, 8,10, 9,11,11,11,11, + 12,12,15,15, 6, 8, 8, 9, 9,11,11,11,12,12,12,15, + 15, 8,10,10,11,11,11,11,12,12,13,13,15,16, 8,10, + 10,11,11,11,11,12,12,13,13,16,16,10,11,11,12,12, + 12,12,13,13,13,13,17,16,10,11,11,12,12,12,12,13, + 13,13,14,16,17,11,12,12,13,13,13,13,14,14,15,14, + 18,17,11,12,12,13,13,13,13,14,14,14,15,19,18,14, + 15,15,15,15,16,16,18,19,18,18, 0, 0,14,15,15,16, + 15,17,17,16,18,17,18, 0, 0, +}; + +static float _vq_quantthresh__44u2__p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44u2__p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u2__p6_0 = { + _vq_quantthresh__44u2__p6_0, + _vq_quantmap__44u2__p6_0, + 13, + 13 +}; + +static static_codebook _44u2__p6_0 = { + 2, 169, + _vq_lengthlist__44u2__p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44u2__p6_0, + NULL, + &_vq_auxt__44u2__p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u2__p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u2__p6_1[] = { + 2, 4, 4, 5, 5, 4, 5, 5, 6, 5, 4, 5, 5, 5, 6, 5, + 6, 5, 6, 6, 5, 5, 6, 6, 6, +}; + +static float _vq_quantthresh__44u2__p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u2__p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u2__p6_1 = { + _vq_quantthresh__44u2__p6_1, + _vq_quantmap__44u2__p6_1, + 5, + 5 +}; + +static static_codebook _44u2__p6_1 = { + 2, 25, + _vq_lengthlist__44u2__p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u2__p6_1, + NULL, + &_vq_auxt__44u2__p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u2__p7_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u2__p7_0[] = { + 1, 3, 2,12,12,12,12,12,12, 4,12,12,12,12,12,12, + 12,12, 5,12,12,12,12,12,12,12,12,12,12,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11, +}; + +static float _vq_quantthresh__44u2__p7_0[] = { + -591.5, -422.5, -253.5, -84.5, 84.5, 253.5, 422.5, 591.5, +}; + +static long _vq_quantmap__44u2__p7_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u2__p7_0 = { + _vq_quantthresh__44u2__p7_0, + _vq_quantmap__44u2__p7_0, + 9, + 9 +}; + +static static_codebook _44u2__p7_0 = { + 2, 81, + _vq_lengthlist__44u2__p7_0, + 1, -516612096, 1626677248, 4, 0, + _vq_quantlist__44u2__p7_0, + NULL, + &_vq_auxt__44u2__p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u2__p7_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u2__p7_1[] = { + 1, 4, 4, 7, 6, 7, 6, 8, 7, 9, 7, 9, 8, 4, 7, 6, + 8, 8, 9, 8,10, 9,10,10,11,11, 4, 7, 7, 8, 8, 8, + 8, 9,10,11,11,11,11, 6, 8, 8,10,10,10,10,11,11, + 12,12,12,12, 7, 8, 8,10,10,10,10,11,11,12,12,13, + 13, 7, 9, 9,11,10,12,12,13,13,14,13,14,14, 7, 9, + 9,10,11,11,12,13,13,13,13,16,14, 9,10,10,12,12, + 13,13,14,14,15,16,15,16, 9,10,10,12,12,12,13,14, + 14,14,15,16,15,10,12,12,13,13,15,13,16,16,15,17, + 17,17,10,11,11,12,14,14,14,15,15,17,17,15,17,11, + 12,12,14,14,14,15,15,15,17,16,17,17,10,12,12,13, + 14,14,14,17,15,17,17,17,17, +}; + +static float _vq_quantthresh__44u2__p7_1[] = { + -71.5, -58.5, -45.5, -32.5, -19.5, -6.5, 6.5, 19.5, + 32.5, 45.5, 58.5, 71.5, +}; + +static long _vq_quantmap__44u2__p7_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u2__p7_1 = { + _vq_quantthresh__44u2__p7_1, + _vq_quantmap__44u2__p7_1, + 13, + 13 +}; + +static static_codebook _44u2__p7_1 = { + 2, 169, + _vq_lengthlist__44u2__p7_1, + 1, -523010048, 1618608128, 4, 0, + _vq_quantlist__44u2__p7_1, + NULL, + &_vq_auxt__44u2__p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u2__p7_2[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u2__p7_2[] = { + 2, 5, 5, 6, 6, 7, 7, 8, 7, 8, 8, 8, 8, 5, 6, 6, + 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 5, 6, 6, 7, 7, 8, + 7, 8, 8, 8, 8, 8, 8, 6, 7, 7, 7, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 6, 7, 7, 8, 7, 8, 8, 9, 9, 9, 9, 9, + 9, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 7, 8, + 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, +}; + +static float _vq_quantthresh__44u2__p7_2[] = { + -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, + 2.5, 3.5, 4.5, 5.5, +}; + +static long _vq_quantmap__44u2__p7_2[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u2__p7_2 = { + _vq_quantthresh__44u2__p7_2, + _vq_quantmap__44u2__p7_2, + 13, + 13 +}; + +static static_codebook _44u2__p7_2 = { + 2, 169, + _vq_lengthlist__44u2__p7_2, + 1, -531103744, 1611661312, 4, 0, + _vq_quantlist__44u2__p7_2, + NULL, + &_vq_auxt__44u2__p7_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44u2__short[] = { + 13,15,17,17,15,15,12,17,11, 9, 7,10,10, 9,12,17, + 10, 6, 3, 6, 5, 7,10,17,15,10, 6, 9, 8, 9,11,17, + 15, 8, 4, 7, 3, 5, 9,16,16,10, 5, 8, 4, 5, 8,16, + 13,11, 5, 8, 3, 3, 5,14,13,12, 7,10, 5, 5, 7,14, +}; + +static static_codebook _huff_book__44u2__short = { + 2, 64, + _huff_lengthlist__44u2__short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44u3__long[] = { + 6, 9,13,12,14,11,10,13, 8, 4, 5, 7, 8, 7, 8,12, + 11, 4, 3, 5, 5, 7, 9,14,11, 6, 5, 6, 6, 6, 7,13, + 13, 7, 5, 6, 4, 5, 7,14,11, 7, 6, 6, 5, 5, 6,13, + 9, 7, 8, 6, 7, 5, 3, 9, 9,12,13,12,14,10, 6, 7, +}; + +static static_codebook _huff_book__44u3__long = { + 2, 64, + _huff_lengthlist__44u3__long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44u3__p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u3__p1_0[] = { + 1, 4, 4, 5, 8, 7, 5, 7, 8, 5, 8, 8, 8,10,11, 8, + 10,11, 5, 8, 8, 8,11,10, 8,11,11, 4, 8, 8, 8,11, + 11, 8,11,11, 8,11,11,11,13,14,11,14,14, 8,11,11, + 10,14,12,11,14,14, 4, 8, 8, 8,11,11, 8,11,11, 7, + 11,11,11,14,14,10,12,14, 8,11,11,11,14,14,11,14, + 13, +}; + +static float _vq_quantthresh__44u3__p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u3__p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u3__p1_0 = { + _vq_quantthresh__44u3__p1_0, + _vq_quantmap__44u3__p1_0, + 3, + 3 +}; + +static static_codebook _44u3__p1_0 = { + 4, 81, + _vq_lengthlist__44u3__p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u3__p1_0, + NULL, + &_vq_auxt__44u3__p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u3__p2_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u3__p2_0[] = { + 2, 5, 4, 5, 6, 6, 5, 6, 6, 5, 6, 6, 7, 8, 8, 6, + 8, 8, 5, 6, 6, 6, 8, 8, 7, 8, 8, 5, 7, 6, 7, 8, + 8, 6, 8, 8, 7, 8, 8, 8, 9,10, 8,10,10, 6, 8, 8, + 8,10, 8, 8,10,10, 5, 6, 6, 6, 8, 8, 7, 8, 8, 6, + 8, 8, 8,10,10, 8, 8,10, 7, 8, 8, 8,10,10, 8,10, + 9, +}; + +static float _vq_quantthresh__44u3__p2_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u3__p2_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u3__p2_0 = { + _vq_quantthresh__44u3__p2_0, + _vq_quantmap__44u3__p2_0, + 3, + 3 +}; + +static static_codebook _44u3__p2_0 = { + 4, 81, + _vq_lengthlist__44u3__p2_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u3__p2_0, + NULL, + &_vq_auxt__44u3__p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u3__p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u3__p3_0[] = { + 2, 4, 4, 7, 7, 5, 7, 7, 9, 9, 5, 7, 7, 9, 9, 8, + 9, 9,12,12, 8, 9, 9,11,12, 5, 7, 7,10,10, 7, 9, + 9,11,11, 7, 9, 9,10,11,10,11,11,13,13, 9,10,11, + 13,13, 5, 7, 7,10,10, 7, 9, 9,11,10, 7, 9, 9,11, + 11, 9,11,10,13,13,10,11,11,14,13, 8,10,10,14,13, + 10,11,11,15,14, 9,11,11,14,14,13,14,13,16,16,12, + 13,13,15,15, 8,10,10,13,14, 9,11,11,14,14,10,11, + 11,14,15,12,13,13,15,15,13,14,14,15,16, 5, 7, 7, + 10,10, 7, 9, 9,11,11, 7, 9, 9,11,12,10,11,11,14, + 14,10,11,11,14,14, 7, 9, 9,12,12, 9,11,11,13,13, + 9,11,11,13,13,12,12,13,15,15,11,12,13,15,16, 7, + 9, 9,11,11, 8,11,10,13,12, 9,11,11,13,13,11,13, + 12,15,13,11,13,13,15,16, 9,12,11,15,14,11,12,13, + 16,15,11,13,13,15,16,14,14,15,17,16,13,15,16, 0, + 17, 9,11,11,15,15,10,13,12,15,15,11,13,13,15,16, + 13,15,13,16,15,14,16,15, 0,19, 5, 7, 7,10,10, 7, + 9, 9,11,11, 7, 9, 9,11,11,10,12,11,14,14,10,11, + 12,14,14, 7, 9, 9,12,12, 9,11,11,14,13, 9,10,11, + 12,13,11,13,13,16,16,11,12,13,13,16, 7, 9, 9,12, + 12, 9,11,11,13,13, 9,11,11,13,13,11,13,13,15,15, + 12,13,12,15,14, 9,11,11,15,14,11,13,12,16,16,10, + 12,12,15,15,13,15,15,17,19,13,14,15,16,17,10,12, + 12,15,15,11,13,13,16,16,11,13,13,15,16,13,15,15, + 0, 0,14,15,15,16,16, 8,10,10,14,14,10,12,12,15, + 15,10,12,11,15,16,14,15,15,19,20,13,14,14,18,16, + 9,11,11,15,15,11,13,13,17,16,11,13,13,16,16,15, + 17,17,20,20,14,15,16,17,20, 9,11,11,15,15,10,13, + 12,16,15,11,13,13,15,17,14,16,15,18, 0,14,16,15, + 18,20,12,14,14, 0, 0,14,14,16, 0, 0,13,16,15, 0, + 0,17,17,18, 0, 0,16,17,19,19, 0,12,14,14,18, 0, + 12,16,14, 0,17,13,15,15,18, 0,16,18,17, 0,17,16, + 18,17, 0, 0, 7,10,10,14,14,10,12,11,15,15,10,12, + 12,16,15,13,15,15,18, 0,14,15,15,17, 0, 9,11,11, + 15,15,11,13,13,16,16,11,12,13,16,16,14,15,16,17, + 17,14,16,16,16,18, 9,11,12,16,16,11,13,13,17,17, + 11,14,13,20,17,15,16,16,19, 0,15,16,17, 0,19,11, + 13,14,17,16,14,15,15,20,18,13,14,15,17,19,16,18, + 18, 0,20,16,16,19,17, 0,12,15,14,17, 0,14,15,15, + 18,19,13,16,15,19,20,15,18,18, 0,20,17, 0,16, 0, + 0, +}; + +static float _vq_quantthresh__44u3__p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u3__p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u3__p3_0 = { + _vq_quantthresh__44u3__p3_0, + _vq_quantmap__44u3__p3_0, + 5, + 5 +}; + +static static_codebook _44u3__p3_0 = { + 4, 625, + _vq_lengthlist__44u3__p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u3__p3_0, + NULL, + &_vq_auxt__44u3__p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u3__p4_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u3__p4_0[] = { + 4, 5, 5, 8, 8, 5, 7, 6, 9, 9, 5, 6, 7, 9, 9, 9, + 9, 9,11,11, 9, 9, 9,11,11, 5, 7, 7, 9, 9, 7, 8, + 8,10,10, 7, 7, 8,10,10, 9,10,10,11,12, 9,10,10, + 11,12, 5, 7, 7, 9, 9, 7, 8, 7,10,10, 7, 8, 8,10, + 10, 9,10, 9,12,11, 9,10,10,12,11, 9,10, 9,12,12, + 9,10,10,13,12, 9,10,10,12,13,12,12,12,14,14,11, + 12,12,13,14, 9, 9,10,12,12, 9,10,10,12,12, 9,10, + 10,12,13,11,12,11,14,13,12,12,12,14,13, 5, 7, 7, + 9, 9, 7, 8, 8,10,10, 7, 8, 8,10,10,10,10,10,12, + 12, 9,10,10,12,12, 7, 8, 8,11,10, 8, 8, 9,11,11, + 8, 9, 9,11,11,11,11,11,12,13,10,11,11,13,13, 6, + 8, 8,10,10, 7, 9, 8,11,10, 8, 9, 9,11,11,10,11, + 10,13,11,10,11,11,13,13, 9,11,10,13,12,10,11,11, + 13,13,10,11,11,13,13,12,12,13,12,15,12,13,13,15, + 15, 9,10,10,12,13,10,11,10,13,12,10,11,11,13,14, + 12,13,11,15,13,12,13,13,15,15, 5, 7, 7, 9, 9, 7, + 8, 8,10,10, 7, 8, 8,10,10, 9,10,10,12,12,10,10, + 11,12,12, 6, 8, 8,10,10, 8, 9, 9,11,11, 7, 8, 9, + 10,11,10,11,11,13,13,10,10,11,11,13, 7, 8, 8,10, + 10, 8, 9, 9,11,11, 8, 9, 9,11,11,10,11,11,13,13, + 11,11,11,13,12, 9,10,10,13,12,10,11,11,14,13,10, + 10,11,12,13,12,13,13,15,15,12,11,13,13,14, 9,10, + 11,12,13,10,11,11,13,13,10,11,11,13,13,12,13,13, + 15,15,12,13,12,15,12, 8, 9, 9,12,12, 9,11,10,13, + 13, 9,10,10,13,13,12,13,13,15,14,12,12,12,14,13, + 9,10,10,13,12,10,11,11,13,13,10,11,11,14,12,13, + 13,14,14,16,12,13,13,15,15, 9,10,10,13,13,10,11, + 10,14,13,10,11,11,13,14,12,14,13,15,14,13,13,13, + 15,15,11,13,12,15,14,11,12,13,14,15,12,13,13,16, + 14,14,12,15,12,16,14,15,15,17,15,11,12,12,14,14, + 11,13,11,15,14,12,13,13,15,15,13,15,12,17,13,14, + 15,15,16,16, 8, 9, 9,12,12, 9,10,10,12,13, 9,10, + 10,13,13,12,12,12,14,14,12,13,13,15,15, 9,10,10, + 13,12,10,11,11,14,13,10,10,11,13,14,12,13,13,15, + 15,12,12,13,14,16, 9,10,10,13,13,10,11,11,13,14, + 10,11,11,14,13,12,13,13,14,15,13,14,13,16,14,11, + 12,12,14,14,12,13,13,15,14,11,12,13,14,15,14,15, + 15,16,16,13,13,15,13,16,11,12,12,14,15,12,13,13, + 14,15,11,13,12,15,14,14,15,15,16,16,14,15,12,16, + 13, +}; + +static float _vq_quantthresh__44u3__p4_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u3__p4_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u3__p4_0 = { + _vq_quantthresh__44u3__p4_0, + _vq_quantmap__44u3__p4_0, + 5, + 5 +}; + +static static_codebook _44u3__p4_0 = { + 4, 625, + _vq_lengthlist__44u3__p4_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u3__p4_0, + NULL, + &_vq_auxt__44u3__p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u3__p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u3__p5_0[] = { + 2, 3, 3, 6, 6, 7, 7, 9, 9, 4, 5, 5, 7, 7, 8, 8, + 10,10, 4, 5, 5, 7, 7, 8, 8,10,10, 6, 7, 7, 8, 8, + 9, 9,11,10, 6, 7, 7, 8, 8, 9, 9,10,10, 7, 8, 8, + 9, 9,10,10,11,11, 7, 8, 8, 9, 9,10,10,11,11, 9, + 10,10,11,10,11,11,12,12, 9,10,10,10,10,11,11,12, + 12, +}; + +static float _vq_quantthresh__44u3__p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44u3__p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u3__p5_0 = { + _vq_quantthresh__44u3__p5_0, + _vq_quantmap__44u3__p5_0, + 9, + 9 +}; + +static static_codebook _44u3__p5_0 = { + 2, 81, + _vq_lengthlist__44u3__p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44u3__p5_0, + NULL, + &_vq_auxt__44u3__p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u3__p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u3__p6_0[] = { + 1, 4, 4, 6, 6, 8, 8, 9, 9,10,11,13,14, 4, 6, 5, + 8, 8, 9, 9,10,10,11,11,14,14, 4, 6, 6, 8, 8, 9, + 9,10,10,11,11,14,14, 6, 8, 8, 9, 9,10,10,11,11, + 12,12,15,15, 6, 8, 8, 9, 9,10,11,11,11,12,12,15, + 15, 8, 9, 9,11,10,11,11,12,12,13,13,15,16, 8, 9, + 9,10,11,11,11,12,12,13,13,16,16,10,10,11,11,11, + 12,12,13,13,13,14,17,16, 9,10,11,12,11,12,12,13, + 13,13,13,16,18,11,12,11,12,12,13,13,13,14,15,14, + 17,17,11,11,12,12,12,13,13,13,14,14,15,18,17,14, + 15,15,15,15,16,16,17,17,19,18, 0,20,14,15,14,15, + 15,16,16,16,17,18,16,20,18, +}; + +static float _vq_quantthresh__44u3__p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44u3__p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u3__p6_0 = { + _vq_quantthresh__44u3__p6_0, + _vq_quantmap__44u3__p6_0, + 13, + 13 +}; + +static static_codebook _44u3__p6_0 = { + 2, 169, + _vq_lengthlist__44u3__p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44u3__p6_0, + NULL, + &_vq_auxt__44u3__p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u3__p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u3__p6_1[] = { + 2, 4, 4, 5, 5, 4, 5, 5, 6, 5, 4, 5, 5, 5, 6, 5, + 6, 5, 6, 6, 5, 5, 6, 6, 6, +}; + +static float _vq_quantthresh__44u3__p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u3__p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u3__p6_1 = { + _vq_quantthresh__44u3__p6_1, + _vq_quantmap__44u3__p6_1, + 5, + 5 +}; + +static static_codebook _44u3__p6_1 = { + 2, 25, + _vq_lengthlist__44u3__p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u3__p6_1, + NULL, + &_vq_auxt__44u3__p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u3__p7_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u3__p7_0[] = { + 1, 3, 3,10,10,10,10,10,10, 4,10,10,10,10,10,10, + 10,10, 4,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, +}; + +static float _vq_quantthresh__44u3__p7_0[] = { + -892.5, -637.5, -382.5, -127.5, 127.5, 382.5, 637.5, 892.5, +}; + +static long _vq_quantmap__44u3__p7_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u3__p7_0 = { + _vq_quantthresh__44u3__p7_0, + _vq_quantmap__44u3__p7_0, + 9, + 9 +}; + +static static_codebook _44u3__p7_0 = { + 2, 81, + _vq_lengthlist__44u3__p7_0, + 1, -515907584, 1627381760, 4, 0, + _vq_quantlist__44u3__p7_0, + NULL, + &_vq_auxt__44u3__p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u3__p7_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44u3__p7_1[] = { + 1, 4, 4, 6, 6, 7, 6, 8, 7, 9, 8,10, 9,11,11, 4, + 7, 7, 8, 7, 9, 9,10,10,11,11,11,11,12,12, 4, 7, + 7, 7, 7, 9, 9,10,10,11,11,12,12,12,11, 6, 8, 8, + 9, 9,10,10,11,11,12,12,13,12,13,13, 6, 8, 8, 9, + 9,10,11,11,11,12,12,13,14,13,13, 8, 9, 9,11,11, + 12,12,12,13,14,13,14,14,14,15, 8, 9, 9,11,11,11, + 12,13,14,13,14,15,17,14,15, 9,10,10,12,12,13,13, + 13,14,15,15,15,16,16,16, 9,11,11,12,12,13,13,14, + 14,14,15,16,16,16,16,10,12,12,13,13,14,14,15,15, + 15,16,17,17,17,17,10,12,11,13,13,15,14,15,14,16, + 17,16,16,16,16,11,13,12,14,14,14,14,15,16,17,16, + 17,17,17,17,11,13,12,14,14,14,15,17,16,17,17,17, + 17,17,17,12,13,13,15,16,15,16,17,17,16,16,17,17, + 17,17,12,13,13,15,15,15,16,17,17,17,16,17,16,17, + 17, +}; + +static float _vq_quantthresh__44u3__p7_1[] = { + -110.5, -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, + 25.5, 42.5, 59.5, 76.5, 93.5, 110.5, +}; + +static long _vq_quantmap__44u3__p7_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44u3__p7_1 = { + _vq_quantthresh__44u3__p7_1, + _vq_quantmap__44u3__p7_1, + 15, + 15 +}; + +static static_codebook _44u3__p7_1 = { + 2, 225, + _vq_lengthlist__44u3__p7_1, + 1, -522338304, 1620115456, 4, 0, + _vq_quantlist__44u3__p7_1, + NULL, + &_vq_auxt__44u3__p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u3__p7_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44u3__p7_2[] = { + 2, 5, 5, 7, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 5, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 10,10, 5, 6, 6, 7, 7, 8, 8, 8, 8, 9, 8, 9, 9, 9, + 9,10, 9, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 10,10,10,10, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9,10, + 9,10,10,10,10, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 10,10,10,10,10,10, 7, 8, 8, 9, 8, 9, 9, 9, 9,10, + 9,10,10,10,10,10,10, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 9,10,10,10,10,10,10,10, 8, 9, 8, 9, 9, 9, 9,10, + 9,10,10,10,10,10,10,10,10, 9, 9, 9, 9, 9, 9,10, + 9,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, 9,10, + 9,10,10,10,10,10,10,10,10,10,10, 9, 9, 9,10, 9, + 10,10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, + 10,10,10,10,10,10,10,10,10,10,10,10,10,11, 9,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,11, 9, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 9,10,10,10,10,10,10,10,10,10,10,10,11,11,11,10, + 11, +}; + +static float _vq_quantthresh__44u3__p7_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44u3__p7_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44u3__p7_2 = { + _vq_quantthresh__44u3__p7_2, + _vq_quantmap__44u3__p7_2, + 17, + 17 +}; + +static static_codebook _44u3__p7_2 = { + 2, 289, + _vq_lengthlist__44u3__p7_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44u3__p7_2, + NULL, + &_vq_auxt__44u3__p7_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44u3__short[] = { + 14,14,14,15,13,15,12,16,10, 8, 7, 9, 9, 8,12,16, + 10, 5, 4, 6, 5, 6, 9,16,14, 8, 6, 8, 7, 8,10,16, + 14, 7, 4, 6, 3, 5, 8,16,15, 9, 5, 7, 4, 4, 7,16, + 13,10, 6, 7, 4, 3, 4,13,13,12, 7, 9, 5, 5, 6,12, +}; + +static static_codebook _huff_book__44u3__short = { + 2, 64, + _huff_lengthlist__44u3__short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44u4__long[] = { + 3, 8,12,12,13,12,11,13, 5, 4, 6, 7, 8, 8, 9,13, + 9, 5, 4, 5, 5, 7, 9,13, 9, 6, 5, 6, 6, 7, 8,12, + 12, 7, 5, 6, 4, 5, 8,13,11, 7, 6, 6, 5, 5, 6,12, + 10, 8, 8, 7, 7, 5, 3, 8,10,12,13,12,12, 9, 6, 7, +}; + +static static_codebook _huff_book__44u4__long = { + 2, 64, + _huff_lengthlist__44u4__long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44u4__p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u4__p1_0[] = { + 1, 4, 4, 5, 8, 7, 5, 7, 8, 5, 8, 8, 8,10,11, 8, + 10,11, 5, 8, 8, 8,11,10, 8,11,11, 4, 8, 8, 8,11, + 11, 8,11,11, 8,11,11,11,13,14,11,15,14, 8,11,11, + 10,13,12,11,14,14, 4, 8, 8, 8,11,11, 8,11,11, 7, + 11,11,11,15,14,10,12,14, 8,11,11,11,14,14,11,14, + 13, +}; + +static float _vq_quantthresh__44u4__p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u4__p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u4__p1_0 = { + _vq_quantthresh__44u4__p1_0, + _vq_quantmap__44u4__p1_0, + 3, + 3 +}; + +static static_codebook _44u4__p1_0 = { + 4, 81, + _vq_lengthlist__44u4__p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u4__p1_0, + NULL, + &_vq_auxt__44u4__p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u4__p2_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u4__p2_0[] = { + 2, 5, 5, 5, 6, 6, 5, 6, 6, 5, 6, 6, 7, 8, 8, 6, + 8, 8, 5, 6, 6, 6, 8, 8, 7, 8, 8, 5, 7, 6, 6, 8, + 8, 6, 8, 8, 6, 8, 8, 8, 9,10, 8,10,10, 6, 8, 8, + 8,10, 8, 8,10,10, 5, 6, 6, 6, 8, 8, 6, 8, 8, 6, + 8, 8, 8,10,10, 8, 8,10, 6, 8, 8, 8,10,10, 8,10, + 9, +}; + +static float _vq_quantthresh__44u4__p2_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u4__p2_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u4__p2_0 = { + _vq_quantthresh__44u4__p2_0, + _vq_quantmap__44u4__p2_0, + 3, + 3 +}; + +static static_codebook _44u4__p2_0 = { + 4, 81, + _vq_lengthlist__44u4__p2_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u4__p2_0, + NULL, + &_vq_auxt__44u4__p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u4__p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u4__p3_0[] = { + 2, 4, 4, 8, 8, 5, 7, 7, 9, 9, 5, 7, 7, 9, 9, 8, + 10, 9,12,12, 8, 9,10,12,12, 5, 7, 7,10,10, 7, 9, + 9,11,11, 7, 9, 9,11,11,10,12,11,14,14, 9,10,11, + 13,14, 5, 7, 7,10,10, 7, 9, 9,11,11, 7, 9, 9,11, + 11, 9,11,10,14,13,10,11,11,14,14, 8,10,10,14,13, + 10,12,12,15,14, 9,11,11,15,14,13,14,14,17,17,12, + 14,14,16,16, 8,10,10,14,14, 9,11,11,14,15,10,12, + 12,14,15,12,14,13,16,16,13,14,15,15,18, 4, 7, 7, + 10,10, 7, 9, 9,12,11, 7, 9, 9,11,12,10,12,11,15, + 14,10,11,12,14,15, 7, 9, 9,12,12, 9,11,12,13,13, + 9,11,12,13,13,12,13,13,15,16,11,13,13,15,16, 7, + 9, 9,12,12, 9,11,10,13,12, 9,11,12,13,14,11,13, + 12,16,14,12,13,13,15,16,10,12,12,16,15,11,13,13, + 17,16,11,13,13,17,16,14,15,15,17,17,14,16,16,18, + 20, 9,11,11,15,16,11,13,12,16,16,11,13,13,16,17, + 14,15,14,18,16,14,16,16,17,20, 5, 7, 7,10,10, 7, + 9, 9,12,11, 7, 9,10,11,12,10,12,11,15,15,10,12, + 12,14,14, 7, 9, 9,12,12, 9,12,11,14,13, 9,10,11, + 12,13,12,13,14,16,16,11,12,13,14,16, 7, 9, 9,12, + 12, 9,12,11,13,13, 9,12,11,13,13,11,13,13,16,16, + 12,13,13,16,15, 9,11,11,16,14,11,13,13,16,16,11, + 12,13,16,16,14,16,16,17,17,13,14,15,16,17,10,12, + 12,15,15,11,13,13,16,17,11,13,13,16,16,14,16,15, + 19,19,14,15,15,17,18, 8,10,10,14,14,10,12,12,15, + 15,10,12,12,16,16,14,16,15,20,19,13,15,15,17,16, + 9,12,12,16,16,11,13,13,16,18,11,14,13,16,17,16, + 17,16,20, 0,15,16,18,18,20, 9,11,11,15,15,11,14, + 12,17,16,11,13,13,17,17,15,17,15,20,20,14,16,16, + 17, 0,13,15,14,18,16,14,15,16, 0,18,14,16,16, 0, + 0,18,16, 0, 0,20,16,18,18, 0, 0,12,14,14,17,18, + 13,15,14,20,18,14,16,15,19,19,16,20,16, 0,18,16, + 19,17,19, 0, 8,10,10,14,14,10,12,12,16,15,10,12, + 12,16,16,13,15,15,18,17,14,16,16,19, 0, 9,11,11, + 16,15,11,14,13,18,17,11,12,13,17,18,14,17,16,18, + 18,15,16,17,18,18, 9,12,12,16,16,11,13,13,16,18, + 11,14,13,17,17,15,16,16,18,20,16,17,17,20,20,12, + 14,14,18,17,14,16,16, 0,19,13,14,15,18, 0,16, 0, + 0, 0, 0,16,16, 0,19,20,13,15,14, 0, 0,14,16,16, + 18,19,14,16,15, 0,20,16,20,18, 0,20,17,20,17, 0, + 0, +}; + +static float _vq_quantthresh__44u4__p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u4__p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u4__p3_0 = { + _vq_quantthresh__44u4__p3_0, + _vq_quantmap__44u4__p3_0, + 5, + 5 +}; + +static static_codebook _44u4__p3_0 = { + 4, 625, + _vq_lengthlist__44u4__p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u4__p3_0, + NULL, + &_vq_auxt__44u4__p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u4__p4_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u4__p4_0[] = { + 4, 5, 5, 8, 8, 5, 7, 6, 9, 9, 5, 6, 7, 9, 9, 9, + 9, 9,11,11, 8, 9, 9,11,11, 5, 7, 7, 9, 9, 7, 8, + 8,10,10, 7, 7, 8,10,10, 9,10,10,11,12, 9,10,10, + 11,12, 5, 7, 7, 9, 9, 7, 8, 7,10,10, 7, 8, 8,10, + 10, 9,10,10,12,11, 9,10,10,12,11, 9,10, 9,12,12, + 9,10,10,13,12, 9,10,10,12,12,12,12,12,14,14,11, + 12,12,13,14, 9, 9,10,12,12, 9,10,10,13,13, 9,10, + 10,12,13,11,12,12,14,13,11,12,12,14,14, 5, 7, 7, + 9, 9, 7, 8, 8,10,10, 7, 8, 8,10,10,10,10,10,12, + 12, 9,10,10,12,12, 7, 8, 8,11,10, 8, 8, 9,11,11, + 8, 9, 9,11,11,11,11,11,12,13,10,11,11,13,13, 6, + 8, 8,10,10, 7, 9, 8,11,10, 8, 9, 9,11,11,10,11, + 10,13,11,10,11,11,13,13, 9,11,10,13,12,10,11,11, + 13,14,10,11,11,14,13,12,12,13,12,15,12,13,13,15, + 15, 9,10,10,12,13,10,11,10,13,12,10,11,11,13,14, + 12,13,11,15,13,13,13,13,15,15, 5, 7, 7, 9, 9, 7, + 8, 8,10,10, 7, 8, 8,10,10, 9,10,10,12,12,10,10, + 11,12,13, 6, 8, 8,10,10, 8, 9, 9,11,11, 7, 8, 9, + 10,11,10,11,11,13,13,10,10,11,11,13, 7, 8, 8,10, + 11, 8, 9, 9,11,11, 8, 9, 8,11,11,10,11,11,13,13, + 11,12,11,13,12, 9,10,10,13,12,10,11,11,14,13,10, + 10,11,12,13,12,13,13,15,15,12,11,13,13,14, 9,10, + 11,12,13,10,11,11,13,14,10,11,11,13,13,12,13,13, + 15,15,12,13,12,15,12, 8, 9, 9,12,12, 9,11,10,13, + 13, 9,10,10,13,13,12,13,13,15,15,12,12,12,14,14, + 9,10,10,13,13,10,11,11,13,14,10,11,11,14,13,13, + 13,14,14,16,13,13,13,15,15, 9,10,10,13,13,10,11, + 10,14,13,10,11,11,13,14,12,14,13,16,14,12,13,13, + 14,15,11,12,12,15,14,11,12,13,14,15,12,13,13,16, + 15,14,12,15,12,16,14,15,15,16,16,11,12,12,14,14, + 11,13,12,15,14,12,13,13,15,16,13,15,13,17,13,14, + 15,15,16,17, 8, 9, 9,12,12, 9,10,10,12,13, 9,10, + 10,13,13,12,12,12,14,14,12,13,13,15,15, 9,10,10, + 13,12,10,11,11,14,13,10,10,11,13,14,13,13,13,15, + 15,12,13,14,14,16, 9,10,10,13,13,10,11,11,13,14, + 10,11,11,14,14,13,13,13,15,15,13,14,13,16,14,11, + 12,12,15,14,12,13,13,16,15,11,12,13,14,15,14,15, + 15,17,16,13,13,15,13,16,11,12,13,14,15,13,13,13, + 15,16,11,13,12,15,14,14,15,15,16,16,14,15,12,17, + 13, +}; + +static float _vq_quantthresh__44u4__p4_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u4__p4_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u4__p4_0 = { + _vq_quantthresh__44u4__p4_0, + _vq_quantmap__44u4__p4_0, + 5, + 5 +}; + +static static_codebook _44u4__p4_0 = { + 4, 625, + _vq_lengthlist__44u4__p4_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u4__p4_0, + NULL, + &_vq_auxt__44u4__p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u4__p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u4__p5_0[] = { + 2, 3, 3, 6, 6, 7, 7, 9, 9, 4, 5, 5, 7, 7, 8, 8, + 10, 9, 4, 5, 5, 7, 7, 8, 8,10,10, 6, 7, 7, 8, 8, + 9, 9,11,10, 6, 7, 7, 8, 8, 9, 9,10,11, 7, 8, 8, + 9, 9,10,10,11,11, 7, 8, 8, 9, 9,10,10,11,11, 9, + 10,10,11,10,11,11,12,12, 9,10,10,10,11,11,11,12, + 12, +}; + +static float _vq_quantthresh__44u4__p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44u4__p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u4__p5_0 = { + _vq_quantthresh__44u4__p5_0, + _vq_quantmap__44u4__p5_0, + 9, + 9 +}; + +static static_codebook _44u4__p5_0 = { + 2, 81, + _vq_lengthlist__44u4__p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44u4__p5_0, + NULL, + &_vq_auxt__44u4__p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u4__p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u4__p6_0[] = { + 1, 4, 4, 6, 6, 8, 8, 9, 9,11,10,13,13, 4, 6, 5, + 8, 8, 9, 9,10,10,11,11,14,14, 4, 6, 6, 8, 8, 9, + 9,10,10,11,11,14,14, 6, 8, 8, 9, 9,10,10,11,11, + 12,12,15,15, 6, 8, 8, 9, 9,10,11,11,11,12,12,15, + 15, 8, 9, 9,11,10,11,11,12,12,13,13,16,16, 8, 9, + 9,10,10,11,11,12,12,13,13,16,16,10,10,10,12,11, + 12,12,13,13,14,14,16,16,10,10,10,11,12,12,12,13, + 13,13,14,16,17,11,12,11,12,12,13,13,14,14,15,14, + 18,17,11,11,12,12,12,13,13,14,14,14,15,19,18,14, + 15,14,15,15,17,16,17,17,17,17,21, 0,14,15,15,16, + 16,16,16,17,17,18,17,20,21, +}; + +static float _vq_quantthresh__44u4__p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44u4__p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u4__p6_0 = { + _vq_quantthresh__44u4__p6_0, + _vq_quantmap__44u4__p6_0, + 13, + 13 +}; + +static static_codebook _44u4__p6_0 = { + 2, 169, + _vq_lengthlist__44u4__p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44u4__p6_0, + NULL, + &_vq_auxt__44u4__p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u4__p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u4__p6_1[] = { + 2, 4, 4, 5, 5, 4, 5, 5, 6, 5, 4, 5, 5, 5, 6, 5, + 6, 5, 6, 6, 5, 5, 6, 6, 6, +}; + +static float _vq_quantthresh__44u4__p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u4__p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u4__p6_1 = { + _vq_quantthresh__44u4__p6_1, + _vq_quantmap__44u4__p6_1, + 5, + 5 +}; + +static static_codebook _44u4__p6_1 = { + 2, 25, + _vq_lengthlist__44u4__p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u4__p6_1, + NULL, + &_vq_auxt__44u4__p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u4__p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u4__p7_0[] = { + 1, 3, 3,12,12,12,12,12,12,12,12,12,12, 3,12,11, + 12,12,12,12,12,12,12,12,12,12, 4,11,10,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11, +}; + +static float _vq_quantthresh__44u4__p7_0[] = { + -1402.5, -1147.5, -892.5, -637.5, -382.5, -127.5, 127.5, 382.5, + 637.5, 892.5, 1147.5, 1402.5, +}; + +static long _vq_quantmap__44u4__p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u4__p7_0 = { + _vq_quantthresh__44u4__p7_0, + _vq_quantmap__44u4__p7_0, + 13, + 13 +}; + +static static_codebook _44u4__p7_0 = { + 2, 169, + _vq_lengthlist__44u4__p7_0, + 1, -514332672, 1627381760, 4, 0, + _vq_quantlist__44u4__p7_0, + NULL, + &_vq_auxt__44u4__p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u4__p7_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44u4__p7_1[] = { + 1, 4, 4, 6, 6, 7, 7, 9, 8,10, 8,10, 9,11,11, 4, + 7, 6, 8, 7, 9, 9,10,10,11,10,11,10,12,10, 4, 6, + 7, 8, 8, 9, 9,10,10,11,11,11,11,12,12, 6, 8, 8, + 10, 9,11,10,12,11,12,12,12,12,13,13, 6, 8, 8,10, + 10,10,11,11,11,12,12,13,12,13,13, 8, 9, 9,11,11, + 12,11,12,12,13,13,13,13,13,13, 8, 9, 9,11,11,11, + 12,12,12,13,13,13,13,13,13, 9,10,10,12,11,13,13, + 13,13,14,13,13,14,14,14, 9,10,11,11,12,12,13,13, + 13,13,13,14,15,14,14,10,11,11,12,12,13,13,14,14, + 14,14,14,15,16,16,10,11,11,12,13,13,13,13,15,14, + 14,15,16,15,16,10,12,12,13,13,14,14,14,15,15,15, + 15,15,15,16,11,12,12,13,13,14,14,14,15,15,15,16, + 15,17,16,11,12,12,13,13,13,15,15,14,16,16,16,16, + 16,17,11,12,12,13,13,14,14,15,14,15,15,17,17,16, + 16, +}; + +static float _vq_quantthresh__44u4__p7_1[] = { + -110.5, -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, + 25.5, 42.5, 59.5, 76.5, 93.5, 110.5, +}; + +static long _vq_quantmap__44u4__p7_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44u4__p7_1 = { + _vq_quantthresh__44u4__p7_1, + _vq_quantmap__44u4__p7_1, + 15, + 15 +}; + +static static_codebook _44u4__p7_1 = { + 2, 225, + _vq_lengthlist__44u4__p7_1, + 1, -522338304, 1620115456, 4, 0, + _vq_quantlist__44u4__p7_1, + NULL, + &_vq_auxt__44u4__p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u4__p7_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44u4__p7_2[] = { + 2, 5, 5, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 5, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9, 5, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 10,10,10,10, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9,10, + 9,10, 9,10,10, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 10,10,10,10,10,10, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9,10,10,10,10,10,10, 8, 9, 8, 9, 9, 9, 9, 9, 9, + 10,10,10,10,10,10,10,10, 8, 8, 8, 9, 9, 9, 9, 9, + 10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, 9,10,10, + 10,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, 9,10, + 10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9,10, + 10,10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, + 10,10,10,10,10,10,10,10,10,11,10,10,10, 9, 9, 9, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10, 9, 9, + 9,10,10,10,10,10,10,10,10,10,10,10,10,10,10, 9, + 10, 9,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 9,10, 9,10,10,10,10,10,10,10,10,10,10,11,10,10, + 10, +}; + +static float _vq_quantthresh__44u4__p7_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44u4__p7_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44u4__p7_2 = { + _vq_quantthresh__44u4__p7_2, + _vq_quantmap__44u4__p7_2, + 17, + 17 +}; + +static static_codebook _44u4__p7_2 = { + 2, 289, + _vq_lengthlist__44u4__p7_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44u4__p7_2, + NULL, + &_vq_auxt__44u4__p7_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44u4__short[] = { + 14,17,15,17,16,14,13,16,10, 7, 7,10,13,10,15,16, + 9, 4, 4, 6, 5, 7, 9,16,12, 8, 7, 8, 8, 8,11,16, + 14, 7, 4, 6, 3, 5, 8,15,13, 8, 5, 7, 4, 5, 7,16, + 12, 9, 6, 8, 3, 3, 5,16,14,13, 7,10, 5, 5, 7,15, +}; + +static static_codebook _huff_book__44u4__short = { + 2, 64, + _huff_lengthlist__44u4__short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44u5__long[] = { + 3, 8,13,12,14,12,16,11,13,14, 5, 4, 5, 6, 7, 8, + 10, 9,12,15,10, 5, 5, 5, 6, 8, 9, 9,13,15,10, 5, + 5, 6, 6, 7, 8, 8,11,13,12, 7, 5, 6, 4, 6, 7, 7, + 11,14,11, 7, 7, 6, 6, 6, 7, 6,10,14,14, 9, 8, 8, + 6, 7, 7, 7,11,16,11, 8, 8, 7, 6, 6, 7, 4, 7,12, + 10,10,12,10,10, 9,10, 5, 6, 9,10,12,15,13,14,14, + 14, 8, 7, 8, +}; + +static static_codebook _huff_book__44u5__long = { + 2, 100, + _huff_lengthlist__44u5__long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44u5__p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u5__p1_0[] = { + 1, 4, 4, 5, 8, 7, 5, 7, 7, 5, 8, 8, 8,10,10, 7, + 9,10, 5, 8, 8, 7,10, 9, 8,10,10, 5, 8, 8, 8,10, + 10, 8,10,10, 8,10,10,10,12,13,10,13,13, 7,10,10, + 10,13,11,10,13,13, 4, 8, 8, 8,11,10, 8,10,10, 7, + 10,10,10,13,13,10,11,13, 8,10,11,10,13,13,10,13, + 12, +}; + +static float _vq_quantthresh__44u5__p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u5__p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u5__p1_0 = { + _vq_quantthresh__44u5__p1_0, + _vq_quantmap__44u5__p1_0, + 3, + 3 +}; + +static static_codebook _44u5__p1_0 = { + 4, 81, + _vq_lengthlist__44u5__p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u5__p1_0, + NULL, + &_vq_auxt__44u5__p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u5__p2_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u5__p2_0[] = { + 3, 4, 4, 5, 6, 6, 5, 6, 6, 5, 6, 6, 6, 8, 8, 6, + 7, 8, 5, 6, 6, 6, 8, 7, 6, 8, 8, 5, 6, 6, 6, 8, + 8, 6, 8, 8, 6, 8, 8, 8, 9, 9, 8, 9, 9, 6, 8, 7, + 7, 9, 8, 8, 9, 9, 5, 6, 6, 6, 8, 7, 6, 8, 8, 6, + 8, 7, 8, 9, 9, 7, 8, 9, 6, 8, 8, 8, 9, 9, 8, 9, + 9, +}; + +static float _vq_quantthresh__44u5__p2_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u5__p2_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u5__p2_0 = { + _vq_quantthresh__44u5__p2_0, + _vq_quantmap__44u5__p2_0, + 3, + 3 +}; + +static static_codebook _44u5__p2_0 = { + 4, 81, + _vq_lengthlist__44u5__p2_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u5__p2_0, + NULL, + &_vq_auxt__44u5__p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u5__p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u5__p3_0[] = { + 2, 4, 5, 8, 8, 5, 7, 6, 9, 9, 5, 6, 7, 9, 9, 8, + 10, 9,13,12, 8, 9,10,12,12, 5, 7, 7,10,10, 7, 9, + 9,11,11, 6, 8, 9,11,11,10,11,11,14,14, 9,10,11, + 13,14, 5, 7, 7, 9,10, 7, 9, 8,11,11, 7, 9, 9,11, + 11, 9,11,10,14,13,10,11,11,14,14, 8,10,10,13,13, + 10,11,11,15,14, 9,11,11,14,14,13,14,14,17,16,12, + 13,13,15,16, 8,10,10,13,13, 9,11,11,14,15,10,11, + 11,14,15,12,14,13,16,16,13,15,14,15,17, 5, 7, 7, + 10,10, 7, 9, 9,11,11, 7, 9, 9,11,11,10,11,11,14, + 14,10,11,12,14,14, 7, 9, 9,12,11, 9,11,11,13,13, + 9,11,11,13,13,12,13,13,15,16,11,12,13,15,16, 6, + 9, 9,11,11, 8,11,10,13,12, 9,11,11,13,14,11,13, + 12,16,14,11,13,13,16,17,10,12,11,15,15,11,13,13, + 16,16,11,13,13,17,16,14,15,15,17,17,14,16,16,17, + 18, 9,11,11,14,15,10,12,12,15,15,11,13,13,16,17, + 13,15,13,17,15,14,15,16,18, 0, 5, 7, 7,10,10, 7, + 9, 9,11,11, 7, 9, 9,11,11,10,11,11,14,14,10,11, + 12,14,15, 6, 9, 9,12,11, 9,11,11,13,13, 8,10,11, + 12,13,11,13,13,16,15,11,12,13,14,15, 7, 9, 9,11, + 12, 9,11,11,13,13, 9,11,11,13,13,11,13,13,15,16, + 11,13,13,15,14, 9,11,11,15,14,11,13,13,17,15,10, + 12,12,15,15,14,16,16,17,17,13,13,15,15,17,10,11, + 12,15,15,11,13,13,16,16,11,13,13,15,15,14,15,15, + 18,18,14,15,15,17,17, 8,10,10,13,13,10,12,11,15, + 15,10,11,12,15,15,14,15,15,18,18,13,14,14,18,18, + 9,11,11,15,16,11,13,13,17,17,11,13,13,16,16,15, + 15,16,17, 0,14,15,17, 0, 0, 9,11,11,15,15,10,13, + 12,18,16,11,13,13,15,16,14,16,15,20,20,14,15,16, + 17, 0,13,14,14,20,16,14,15,16,19,18,14,15,15,19, + 0,18,16, 0,20,20,16,18,18, 0, 0,12,14,14,18,18, + 13,15,14,18,16,14,15,16,18,20,16,19,16, 0,17,17, + 18,18,19, 0, 8,10,10,14,14,10,11,11,14,15,10,11, + 12,15,15,13,15,14,19,17,13,15,15,17, 0, 9,11,11, + 16,15,11,13,13,16,16,10,12,13,15,17,14,16,16,18, + 18,14,15,15,18, 0, 9,11,11,15,15,11,13,13,16,17, + 11,13,13,18,17,14,18,16,18,18,15,17,17,18, 0,12, + 14,14,18,18,14,15,15,20, 0,13,14,15,17, 0,16,18, + 17, 0, 0,16,16, 0,17,20,12,14,14,18,18,14,16,15, + 0,18,14,16,15,18, 0,16,19,17, 0, 0,17,18,16, 0, + 0, +}; + +static float _vq_quantthresh__44u5__p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u5__p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u5__p3_0 = { + _vq_quantthresh__44u5__p3_0, + _vq_quantmap__44u5__p3_0, + 5, + 5 +}; + +static static_codebook _44u5__p3_0 = { + 4, 625, + _vq_lengthlist__44u5__p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u5__p3_0, + NULL, + &_vq_auxt__44u5__p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u5__p4_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u5__p4_0[] = { + 4, 5, 5, 8, 8, 6, 7, 6, 9, 9, 6, 6, 7, 9, 9, 8, + 9, 9,11,11, 8, 9, 9,11,11, 6, 7, 7, 9, 9, 7, 8, + 8,10,10, 6, 7, 8, 9,10, 9,10,10,11,12, 9, 9,10, + 11,12, 6, 7, 7, 9, 9, 6, 8, 7,10, 9, 7, 8, 8,10, + 10, 9,10, 9,12,11, 9,10,10,12,11, 8, 9, 9,12,11, + 9,10,10,12,12, 9,10,10,12,12,11,12,12,13,14,11, + 11,12,13,14, 8, 9, 9,11,12, 9,10,10,12,12, 9,10, + 10,12,12,11,12,11,14,13,11,12,12,13,13, 5, 7, 7, + 9, 9, 7, 8, 8,10,10, 7, 8, 8,10,10, 9,10,10,12, + 12, 9,10,10,12,12, 7, 8, 8,10,10, 8, 8, 9,10,11, + 8, 9, 9,11,11,10,10,11,11,13,10,11,11,12,13, 6, + 7, 8,10,10, 7, 9, 8,11,10, 8, 9, 9,11,11,10,11, + 10,13,11,10,11,11,12,12, 9,10,10,12,12,10,10,11, + 12,13,10,11,11,13,13,12,11,13,12,15,12,13,13,14, + 15, 9,10,10,12,12, 9,11,10,13,12,10,11,11,13,13, + 11,13,11,14,12,12,13,13,14,15, 5, 7, 7, 9, 9, 7, + 8, 8,10,10, 7, 8, 8,10,10, 9,10,10,12,12, 9,10, + 10,12,12, 6, 8, 7,10,10, 8, 9, 9,11,11, 7, 8, 9, + 10,11,10,11,11,12,12,10,10,11,11,13, 7, 8, 8,10, + 10, 8, 9, 9,11,11, 8, 9, 8,11,10,10,11,11,13,12, + 10,11,10,13,11, 9,10,10,12,12,10,11,11,13,12, 9, + 10,10,12,13,12,13,13,14,15,11,11,13,12,14, 9,10, + 10,12,12,10,11,11,13,13,10,11,10,13,12,12,13,13, + 14,14,12,13,11,14,12, 8, 9, 9,12,12, 9,10,10,12, + 12, 9,10,10,12,12,12,12,12,14,14,11,12,12,14,13, + 9,10,10,12,12,10,11,11,13,13,10,11,11,13,12,12, + 12,13,14,15,12,13,13,15,14, 9,10,10,12,12,10,11, + 10,13,12,10,11,11,12,13,12,13,12,15,13,12,13,13, + 14,15,11,12,12,14,13,11,12,12,14,15,12,13,13,15, + 14,13,12,14,12,16,13,14,14,15,15,11,11,12,14,14, + 11,12,11,14,13,12,13,13,14,15,13,14,12,16,12,14, + 14,15,16,16, 8, 9, 9,11,12, 9,10,10,12,12, 9,10, + 10,12,13,11,12,12,13,13,12,12,13,14,14, 9,10,10, + 12,12,10,11,10,13,12,10,10,11,12,13,12,13,13,15, + 14,12,12,13,13,15, 9,10,10,12,13,10,11,11,12,13, + 10,11,11,13,13,12,13,13,14,15,12,13,12,15,14,11, + 12,11,14,13,12,13,13,15,14,11,11,12,13,14,14,15, + 14,16,15,13,12,14,13,16,11,12,12,13,14,12,13,13, + 14,15,11,12,11,14,14,14,14,14,15,16,13,15,12,16, + 12, +}; + +static float _vq_quantthresh__44u5__p4_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u5__p4_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u5__p4_0 = { + _vq_quantthresh__44u5__p4_0, + _vq_quantmap__44u5__p4_0, + 5, + 5 +}; + +static static_codebook _44u5__p4_0 = { + 4, 625, + _vq_lengthlist__44u5__p4_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u5__p4_0, + NULL, + &_vq_auxt__44u5__p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u5__p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u5__p5_0[] = { + 2, 3, 3, 6, 6, 8, 8,10,10, 4, 5, 5, 8, 7, 8, 8, + 11,10, 3, 5, 5, 7, 8, 8, 8,10,11, 6, 8, 7,10, 9, + 10,10,11,11, 6, 7, 8, 9, 9, 9,10,11,12, 8, 8, 8, + 10,10,11,11,13,12, 8, 8, 9, 9,10,11,11,12,13,10, + 11,10,12,11,13,12,14,14,10,10,11,11,12,12,13,14, + 14, +}; + +static float _vq_quantthresh__44u5__p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44u5__p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u5__p5_0 = { + _vq_quantthresh__44u5__p5_0, + _vq_quantmap__44u5__p5_0, + 9, + 9 +}; + +static static_codebook _44u5__p5_0 = { + 2, 81, + _vq_lengthlist__44u5__p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44u5__p5_0, + NULL, + &_vq_auxt__44u5__p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u5__p6_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u5__p6_0[] = { + 3, 4, 4, 5, 5, 7, 7, 9, 9, 4, 5, 4, 6, 6, 7, 7, + 9, 9, 4, 4, 5, 6, 6, 7, 7, 9, 9, 5, 6, 6, 7, 7, + 8, 8,10,10, 6, 6, 6, 7, 7, 8, 8,10,10, 7, 7, 7, + 8, 8, 9, 9,11,10, 7, 7, 7, 8, 8, 9, 9,10,11, 9, + 9, 9,10,10,11,10,11,11, 9, 9, 9,10,10,11,10,11, + 11, +}; + +static float _vq_quantthresh__44u5__p6_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44u5__p6_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u5__p6_0 = { + _vq_quantthresh__44u5__p6_0, + _vq_quantmap__44u5__p6_0, + 9, + 9 +}; + +static static_codebook _44u5__p6_0 = { + 2, 81, + _vq_lengthlist__44u5__p6_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44u5__p6_0, + NULL, + &_vq_auxt__44u5__p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u5__p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u5__p7_0[] = { + 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 9, 9, 8,11,10, 7, + 11,10, 5, 9, 9, 7,10,10, 8,10,11, 4, 9, 9, 9,12, + 12, 9,12,12, 8,12,12,11,12,12,10,12,13, 7,12,12, + 11,12,12,10,12,13, 4, 9, 9, 9,12,12, 9,12,12, 7, + 12,11,10,13,13,11,12,12, 7,12,12,10,13,13,11,12, + 12, +}; + +static float _vq_quantthresh__44u5__p7_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44u5__p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u5__p7_0 = { + _vq_quantthresh__44u5__p7_0, + _vq_quantmap__44u5__p7_0, + 3, + 3 +}; + +static static_codebook _44u5__p7_0 = { + 4, 81, + _vq_lengthlist__44u5__p7_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44u5__p7_0, + NULL, + &_vq_auxt__44u5__p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u5__p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u5__p7_1[] = { + 2, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8, 4, 5, 5, 7, 7, + 8, 8, 9, 8, 8, 9, 4, 5, 5, 7, 7, 8, 8, 9, 9, 8, + 9, 6, 7, 7, 8, 8, 9, 8, 9, 9, 9, 9, 6, 7, 7, 8, + 8, 9, 9, 9, 9, 9, 9, 7, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9, 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 8, 9, 9, + 9, 9, 9, 9,10,10,10,10, 8, 9, 9, 9, 9, 9, 9,10, + 10,10,10, 8, 9, 9, 9, 9, 9, 9,10,10,10,10, 8, 9, + 9, 9, 9, 9, 9,10,10,10,10, +}; + +static float _vq_quantthresh__44u5__p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44u5__p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u5__p7_1 = { + _vq_quantthresh__44u5__p7_1, + _vq_quantmap__44u5__p7_1, + 11, + 11 +}; + +static static_codebook _44u5__p7_1 = { + 2, 121, + _vq_lengthlist__44u5__p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44u5__p7_1, + NULL, + &_vq_auxt__44u5__p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u5__p8_0[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u5__p8_0[] = { + 1, 4, 4, 6, 6, 8, 8, 9, 9,10,10, 4, 6, 6, 7, 7, + 9, 9,10,10,11,11, 4, 6, 6, 7, 7, 9, 9,10,10,11, + 11, 6, 8, 7, 9, 9,10,10,11,11,13,12, 6, 8, 8, 9, + 9,10,10,11,11,12,13, 8, 9, 9,10,10,12,12,13,12, + 14,13, 8, 9, 9,10,10,12,12,13,13,14,14, 9,11,11, + 12,12,13,13,14,14,15,14, 9,11,11,12,12,13,13,14, + 14,15,14,11,12,12,13,13,14,14,15,14,15,14,11,11, + 12,13,13,14,14,14,14,15,15, +}; + +static float _vq_quantthresh__44u5__p8_0[] = { + -49.5, -38.5, -27.5, -16.5, -5.5, 5.5, 16.5, 27.5, + 38.5, 49.5, +}; + +static long _vq_quantmap__44u5__p8_0[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u5__p8_0 = { + _vq_quantthresh__44u5__p8_0, + _vq_quantmap__44u5__p8_0, + 11, + 11 +}; + +static static_codebook _44u5__p8_0 = { + 2, 121, + _vq_lengthlist__44u5__p8_0, + 1, -524582912, 1618345984, 4, 0, + _vq_quantlist__44u5__p8_0, + NULL, + &_vq_auxt__44u5__p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u5__p8_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u5__p8_1[] = { + 3, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 5, 6, 5, 7, 6, + 7, 7, 8, 8, 8, 8, 5, 5, 5, 6, 6, 7, 7, 8, 8, 8, + 8, 6, 7, 6, 7, 7, 8, 8, 8, 8, 8, 8, 6, 6, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 7, 7, 7, 8, 8, 8, 8, 8, 8, + 8, 8, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 7, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 7, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44u5__p8_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44u5__p8_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u5__p8_1 = { + _vq_quantthresh__44u5__p8_1, + _vq_quantmap__44u5__p8_1, + 11, + 11 +}; + +static static_codebook _44u5__p8_1 = { + 2, 121, + _vq_lengthlist__44u5__p8_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44u5__p8_1, + NULL, + &_vq_auxt__44u5__p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u5__p9_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u5__p9_0[] = { + 1, 3, 2,12,10,13,13,13,13,13,13,13,13, 4, 9, 9, + 13,13,13,13,13,13,13,13,13,13, 5,10, 9,13,13,13, + 13,13,13,13,13,13,13,12,13,13,13,13,13,13,13,13, + 13,13,13,13,11,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, + 13,13,13,13,13,13,13,13,13,13,13,13,13,12,12,12, + 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, + 12,12,12,12,12,12,12,12,12, +}; + +static float _vq_quantthresh__44u5__p9_0[] = { + -1402.5, -1147.5, -892.5, -637.5, -382.5, -127.5, 127.5, 382.5, + 637.5, 892.5, 1147.5, 1402.5, +}; + +static long _vq_quantmap__44u5__p9_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u5__p9_0 = { + _vq_quantthresh__44u5__p9_0, + _vq_quantmap__44u5__p9_0, + 13, + 13 +}; + +static static_codebook _44u5__p9_0 = { + 2, 169, + _vq_lengthlist__44u5__p9_0, + 1, -514332672, 1627381760, 4, 0, + _vq_quantlist__44u5__p9_0, + NULL, + &_vq_auxt__44u5__p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u5__p9_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44u5__p9_1[] = { + 1, 4, 4, 7, 7, 8, 8, 8, 7, 8, 7, 9, 8, 9, 9, 4, + 7, 6, 9, 8,10,10, 9, 8, 9, 9, 9, 9, 9, 8, 5, 6, + 6, 8, 9,10,10, 9, 9, 9,10,10,10,10,11, 7, 8, 8, + 10,10,11,11,10,10,11,11,11,12,11,11, 7, 8, 8,10, + 10,11,11,10,10,11,11,12,11,11,11, 8, 9, 9,11,11, + 12,12,11,11,12,11,12,12,12,12, 8, 9,10,11,11,12, + 12,11,11,12,12,12,12,12,12, 8, 9, 9,10,10,12,11, + 12,12,12,12,12,12,12,13, 8, 9, 9,11,11,11,11,12, + 12,12,12,13,12,13,13, 9,10,10,11,11,12,12,12,13, + 12,13,13,13,14,13, 9,10,10,11,11,12,12,12,13,13, + 12,13,13,14,13, 9,11,10,12,11,13,12,12,13,13,13, + 13,13,13,14, 9,10,10,12,12,12,12,12,13,13,13,13, + 13,14,14,10,11,11,12,12,12,13,13,13,14,14,13,14, + 14,14,10,11,11,12,12,12,12,13,12,13,14,13,14,14, + 14, +}; + +static float _vq_quantthresh__44u5__p9_1[] = { + -110.5, -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, + 25.5, 42.5, 59.5, 76.5, 93.5, 110.5, +}; + +static long _vq_quantmap__44u5__p9_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44u5__p9_1 = { + _vq_quantthresh__44u5__p9_1, + _vq_quantmap__44u5__p9_1, + 15, + 15 +}; + +static static_codebook _44u5__p9_1 = { + 2, 225, + _vq_lengthlist__44u5__p9_1, + 1, -522338304, 1620115456, 4, 0, + _vq_quantlist__44u5__p9_1, + NULL, + &_vq_auxt__44u5__p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u5__p9_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44u5__p9_2[] = { + 2, 5, 5, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 5, 6, 6, 7, 7, 8, 8, 9, 8, 9, 9, 9, 9, 9, 9, + 9, 9, 5, 6, 6, 7, 7, 8, 8, 9, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 7, 7, 7, 8, 8, 9, 8, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 7, 7, 7, 8, 8, 9, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, + 9,10, 9,10,10,10, 8, 8, 8, 9, 8, 9, 9, 9, 9, 9, + 9, 9,10, 9,10, 9,10, 8, 9, 9, 9, 9, 9, 9, 9, 9, + 9,10, 9,10,10,10,10,10, 8, 9, 9, 9, 9, 9, 9,10, + 9,10, 9,10,10,10,10,10,10, 9, 9, 9, 9, 9,10, 9, + 10,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, 9, 9, + 9,10, 9,10, 9,10,10,10,10,10,10, 9, 9, 9, 9, 9, + 10,10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, + 9, 9,10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, + 9,10,10, 9,10,10,10,10,10,10,10,10,10,10, 9, 9, + 9, 9, 9,10,10,10,10,10,10,10,10,10,10,10,10, 9, + 9, 9, 9, 9,10,10,10,10,10,10,10,10,10,10,10,10, + 9, 9, 9,10, 9,10,10,10,10,10,10,10,10,10,10,10, + 10, +}; + +static float _vq_quantthresh__44u5__p9_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44u5__p9_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44u5__p9_2 = { + _vq_quantthresh__44u5__p9_2, + _vq_quantmap__44u5__p9_2, + 17, + 17 +}; + +static static_codebook _44u5__p9_2 = { + 2, 289, + _vq_lengthlist__44u5__p9_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44u5__p9_2, + NULL, + &_vq_auxt__44u5__p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44u5__short[] = { + 4,10,17,13,17,13,17,17,17,17, 3, 6, 8, 9,11, 9, + 15,12,16,17, 6, 5, 5, 7, 7, 8,10,11,17,17, 7, 8, + 7, 9, 9,10,13,13,17,17, 8, 6, 5, 7, 4, 7, 5, 8, + 14,17, 9, 9, 8, 9, 7, 9, 8,10,16,17,12,10, 7, 8, + 4, 7, 4, 7,16,17,12,11, 9,10, 6, 9, 5, 7,14,17, + 14,13,10,15, 4, 8, 3, 5,14,17,17,14,11,15, 6,10, + 6, 8,15,17, +}; + +static static_codebook _huff_book__44u5__short = { + 2, 100, + _huff_lengthlist__44u5__short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44u6__long[] = { + 3, 9,14,13,14,13,16,12,13,14, 5, 4, 6, 6, 8, 9, + 11,10,12,15,10, 5, 5, 6, 6, 8,10,10,13,16,10, 6, + 6, 6, 6, 8, 9, 9,12,14,13, 7, 6, 6, 4, 6, 6, 7, + 11,14,10, 7, 7, 7, 6, 6, 6, 7,10,13,15,10, 9, 8, + 5, 6, 5, 6,10,14,10, 9, 8, 8, 6, 6, 5, 4, 6,11, + 11,11,12,11,10, 9, 9, 5, 5, 9,10,12,15,13,13,13, + 13, 8, 7, 7, +}; + +static static_codebook _huff_book__44u6__long = { + 2, 100, + _huff_lengthlist__44u6__long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44u6__p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u6__p1_0[] = { + 1, 4, 4, 4, 8, 7, 5, 7, 7, 5, 8, 8, 8,10,10, 7, + 9,10, 5, 8, 8, 7,10, 9, 8,10,10, 5, 8, 8, 8,10, + 10, 8,10,10, 8,10,10,10,12,13,10,13,13, 7,10,10, + 10,13,11,10,13,13, 5, 8, 8, 8,11,10, 8,10,10, 7, + 10,10,10,13,13,10,11,13, 8,10,11,10,13,13,10,13, + 12, +}; + +static float _vq_quantthresh__44u6__p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u6__p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u6__p1_0 = { + _vq_quantthresh__44u6__p1_0, + _vq_quantmap__44u6__p1_0, + 3, + 3 +}; + +static static_codebook _44u6__p1_0 = { + 4, 81, + _vq_lengthlist__44u6__p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u6__p1_0, + NULL, + &_vq_auxt__44u6__p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u6__p2_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u6__p2_0[] = { + 3, 4, 4, 5, 6, 6, 5, 6, 6, 5, 6, 6, 6, 8, 8, 6, + 7, 8, 5, 6, 6, 6, 8, 7, 6, 8, 8, 5, 6, 6, 6, 8, + 8, 6, 8, 8, 6, 8, 8, 8, 9, 9, 8, 9, 9, 6, 7, 7, + 7, 9, 8, 8, 9, 9, 5, 6, 6, 6, 8, 7, 6, 8, 8, 6, + 8, 8, 8, 9, 9, 7, 8, 9, 6, 8, 8, 8, 9, 9, 8, 9, + 9, +}; + +static float _vq_quantthresh__44u6__p2_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u6__p2_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u6__p2_0 = { + _vq_quantthresh__44u6__p2_0, + _vq_quantmap__44u6__p2_0, + 3, + 3 +}; + +static static_codebook _44u6__p2_0 = { + 4, 81, + _vq_lengthlist__44u6__p2_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u6__p2_0, + NULL, + &_vq_auxt__44u6__p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u6__p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u6__p3_0[] = { + 2, 5, 4, 8, 8, 5, 7, 6, 9, 9, 5, 6, 7, 9, 9, 8, + 9, 9,13,12, 8, 9,10,12,13, 5, 7, 7,10, 9, 7, 9, + 9,11,11, 7, 8, 9,11,11,10,11,11,14,14, 9,10,11, + 13,14, 5, 7, 7, 9,10, 6, 9, 8,11,11, 7, 9, 9,11, + 11, 9,11,10,14,13,10,11,11,14,13, 8,10,10,13,13, + 10,11,11,15,15, 9,11,11,14,14,13,14,14,17,16,12, + 13,14,16,16, 8,10,10,13,14, 9,11,11,14,15,10,11, + 12,14,15,12,14,13,16,15,13,14,14,15,17, 5, 7, 7, + 10,10, 7, 9, 9,11,11, 7, 9, 9,11,11,10,12,11,14, + 14,10,11,11,14,14, 7, 9, 9,12,11, 9,11,11,13,13, + 9,11,11,13,13,11,13,13,14,15,11,12,13,15,16, 6, + 9, 9,11,12, 8,11,10,13,12, 9,11,11,13,14,11,13, + 12,16,14,11,13,13,15,16,10,12,11,14,15,11,13,13, + 15,17,11,13,13,17,16,15,15,16,17,16,14,15,16,18, + 0, 9,11,11,14,15,10,12,12,16,15,11,13,13,16,16, + 13,15,14,18,15,14,16,16, 0, 0, 5, 7, 7,10,10, 7, + 9, 9,11,11, 7, 9, 9,11,11,10,11,11,14,14,10,11, + 12,14,14, 6, 9, 9,11,11, 9,11,11,13,13, 8,10,11, + 12,13,11,13,13,16,15,11,12,13,14,16, 7, 9, 9,11, + 12, 9,11,11,13,13, 9,11,11,13,13,11,13,13,16,15, + 11,13,12,15,15, 9,11,11,15,14,11,13,13,17,16,10, + 12,13,15,16,14,16,16, 0,18,14,14,15,15,17,10,11, + 12,15,15,11,13,13,16,16,11,13,13,16,16,14,16,16, + 19,17,14,15,15,17,17, 8,10,10,14,14,10,12,11,15, + 15,10,11,12,16,15,14,15,15,18,20,13,14,16,17,18, + 9,11,11,15,16,11,13,13,17,17,11,13,13,17,16,15, + 16,16, 0, 0,15,16,16, 0, 0, 9,11,11,15,15,10,13, + 12,17,15,11,13,13,17,16,15,17,15,20,19,15,16,16, + 19, 0,13,15,14, 0,17,14,15,16, 0,20,15,16,16, 0, + 19,17,18, 0, 0, 0,16,17,18, 0, 0,12,14,14,19,18, + 13,15,14, 0,17,14,15,16,19,19,16,18,16, 0,19,19, + 20,17,20, 0, 8,10,10,13,14,10,11,11,15,15,10,12, + 12,15,16,14,15,14,19,16,14,15,15, 0,18, 9,11,11, + 16,15,11,13,13, 0,16,11,12,13,16,17,14,16,17, 0, + 19,15,16,16,18, 0, 9,11,11,15,16,11,13,13,16,16, + 11,14,13,18,17,15,16,16,18,20,15,17,19, 0, 0,12, + 14,14,17,17,14,16,15, 0, 0,13,14,15,19, 0,16,18, + 20, 0, 0,16,16,18,18, 0,12,14,14,17,20,14,16,16, + 19, 0,14,16,14, 0,20,16,20,17, 0, 0,17, 0,15, 0, + 19, +}; + +static float _vq_quantthresh__44u6__p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u6__p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u6__p3_0 = { + _vq_quantthresh__44u6__p3_0, + _vq_quantmap__44u6__p3_0, + 5, + 5 +}; + +static static_codebook _44u6__p3_0 = { + 4, 625, + _vq_lengthlist__44u6__p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u6__p3_0, + NULL, + &_vq_auxt__44u6__p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u6__p4_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u6__p4_0[] = { + 4, 5, 5, 8, 8, 6, 7, 6, 9, 9, 6, 6, 7, 9, 9, 8, + 9, 9,11,11, 8, 9, 9,11,11, 6, 7, 7, 9, 9, 7, 8, + 8,10,10, 7, 7, 8, 9,10, 9,10,10,11,11, 9, 9,10, + 11,12, 6, 7, 7, 9, 9, 7, 8, 7,10, 9, 7, 8, 8,10, + 10, 9,10, 9,12,11, 9,10,10,12,11, 8, 9, 9,11,11, + 9,10,10,12,12, 9,10,10,12,12,11,12,12,14,13,11, + 11,12,13,13, 8, 9, 9,11,11, 9,10,10,12,12, 9,10, + 10,12,12,11,12,11,13,12,11,12,12,13,13, 5, 7, 7, + 9, 9, 7, 8, 7,10,10, 7, 7, 8,10,10, 9,10,10,12, + 11, 9,10,10,11,12, 7, 8, 8,10,10, 8, 8, 9,11,11, + 8, 9, 9,11,11,10,10,11,12,13,10,10,11,12,12, 6, + 7, 7,10,10, 7, 9, 8,11,10, 8, 8, 9,10,11,10,11, + 10,13,11,10,11,11,12,12, 9,10,10,12,12,10,10,11, + 13,13,10,11,11,12,13,12,12,12,13,14,12,12,13,14, + 14, 9,10,10,12,12, 9,10,10,13,12,10,11,11,13,13, + 11,12,11,14,12,12,13,13,14,14, 6, 7, 7, 9, 9, 7, + 8, 7,10,10, 7, 8, 8,10,10, 9,10,10,12,11, 9,10, + 10,11,12, 6, 7, 7,10,10, 8, 9, 8,11,10, 7, 8, 9, + 10,11,10,11,11,12,12,10,10,11,11,13, 7, 8, 8,10, + 10, 8, 9, 9,11,11, 8, 9, 8,11,11,10,11,10,13,12, + 10,11,11,13,12, 9,10,10,12,12,10,11,11,13,12, 9, + 10,10,12,13,12,13,12,14,14,11,11,12,12,14, 9,10, + 10,12,12,10,11,11,13,13,10,11,10,13,12,12,12,12, + 14,14,12,13,12,14,13, 8, 9, 9,11,11, 9,10,10,12, + 12, 9,10,10,12,12,11,12,12,14,13,11,12,12,13,14, + 9,10,10,12,12,10,11,11,13,13,10,11,11,13,13,12, + 12,13,14,15,12,12,13,14,14, 9,10,10,12,12, 9,11, + 10,13,12,10,10,11,12,13,12,13,12,14,13,12,12,13, + 14,15,11,12,12,14,13,11,12,12,14,14,12,13,13,14, + 14,13,13,14,14,16,13,14,14,15,15,11,12,11,13,13, + 11,12,11,14,13,12,12,13,14,15,12,14,12,15,12,13, + 14,15,15,16, 8, 9, 9,11,11, 9,10,10,12,12, 9,10, + 10,12,12,11,12,12,14,13,11,12,12,13,13, 9,10,10, + 12,12,10,11,10,13,12, 9,10,11,12,13,12,13,12,14, + 14,12,12,13,13,14, 9,10,10,12,12,10,11,11,13,13, + 10,11,11,13,13,12,13,12,14,14,12,13,13,14,14,11, + 11,11,13,13,12,13,12,14,14,11,11,12,13,14,14,14, + 14,16,15,12,12,14,12,15,11,12,12,13,14,12,13,13, + 14,15,11,12,12,14,14,13,14,14,16,16,13,14,13,16, + 13, +}; + +static float _vq_quantthresh__44u6__p4_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u6__p4_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u6__p4_0 = { + _vq_quantthresh__44u6__p4_0, + _vq_quantmap__44u6__p4_0, + 5, + 5 +}; + +static static_codebook _44u6__p4_0 = { + 4, 625, + _vq_lengthlist__44u6__p4_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u6__p4_0, + NULL, + &_vq_auxt__44u6__p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u6__p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u6__p5_0[] = { + 2, 3, 3, 6, 6, 8, 8,10,10, 4, 5, 5, 8, 7, 8, 8, + 11,11, 3, 5, 5, 7, 8, 8, 8,11,11, 6, 8, 7, 9, 9, + 10, 9,12,11, 6, 7, 8, 9, 9, 9,10,11,12, 8, 8, 8, + 10, 9,12,11,13,13, 8, 8, 9, 9,10,11,12,13,13,10, + 11,11,12,12,13,13,14,14,10,10,11,11,12,13,13,14, + 14, +}; + +static float _vq_quantthresh__44u6__p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44u6__p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u6__p5_0 = { + _vq_quantthresh__44u6__p5_0, + _vq_quantmap__44u6__p5_0, + 9, + 9 +}; + +static static_codebook _44u6__p5_0 = { + 2, 81, + _vq_lengthlist__44u6__p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44u6__p5_0, + NULL, + &_vq_auxt__44u6__p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u6__p6_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u6__p6_0[] = { + 3, 4, 4, 5, 5, 7, 7, 9, 9, 4, 5, 4, 6, 6, 7, 7, + 9, 9, 4, 4, 5, 6, 6, 7, 8, 9, 9, 5, 6, 6, 7, 7, + 8, 8,10,10, 5, 6, 6, 7, 7, 8, 8,10,10, 7, 8, 7, + 8, 8,10, 9,11,11, 7, 7, 8, 8, 8, 9,10,10,11, 9, + 9, 9,10,10,11,11,12,11, 9, 9, 9,10,10,11,11,11, + 12, +}; + +static float _vq_quantthresh__44u6__p6_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44u6__p6_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u6__p6_0 = { + _vq_quantthresh__44u6__p6_0, + _vq_quantmap__44u6__p6_0, + 9, + 9 +}; + +static static_codebook _44u6__p6_0 = { + 2, 81, + _vq_lengthlist__44u6__p6_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44u6__p6_0, + NULL, + &_vq_auxt__44u6__p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u6__p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u6__p7_0[] = { + 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 9, 8, 7,10,10, 8, + 10,10, 5, 8, 9, 7,10,10, 7,10, 9, 4, 8, 8, 9,11, + 11, 8,11,11, 7,11,11,10,10,13,10,13,13, 7,11,11, + 10,13,12,10,13,13, 5, 9, 8, 8,11,11, 9,11,11, 7, + 11,11,10,13,13,10,12,13, 7,11,11,10,13,13, 9,13, + 10, +}; + +static float _vq_quantthresh__44u6__p7_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44u6__p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u6__p7_0 = { + _vq_quantthresh__44u6__p7_0, + _vq_quantmap__44u6__p7_0, + 3, + 3 +}; + +static static_codebook _44u6__p7_0 = { + 4, 81, + _vq_lengthlist__44u6__p7_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44u6__p7_0, + NULL, + &_vq_auxt__44u6__p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u6__p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u6__p7_1[] = { + 3, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8, 4, 5, 5, 7, 6, + 8, 8, 8, 8, 8, 8, 4, 5, 5, 6, 7, 8, 8, 8, 8, 8, + 8, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 6, 7, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 7, 8, 8, 8, 8, 8, 8, 9, 9, + 9, 9, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 8, 8, 8, + 8, 8, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 8, 8, + 8, 8, 8, 9, 9, 9, 9, 9, 9, +}; + +static float _vq_quantthresh__44u6__p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44u6__p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u6__p7_1 = { + _vq_quantthresh__44u6__p7_1, + _vq_quantmap__44u6__p7_1, + 11, + 11 +}; + +static static_codebook _44u6__p7_1 = { + 2, 121, + _vq_lengthlist__44u6__p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44u6__p7_1, + NULL, + &_vq_auxt__44u6__p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u6__p8_0[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u6__p8_0[] = { + 1, 4, 4, 6, 6, 8, 8, 9, 9,10,10, 4, 6, 6, 7, 7, + 9, 9,10,10,11,11, 4, 6, 6, 7, 7, 9, 9,10,10,11, + 11, 6, 8, 8, 9, 9,10,10,11,11,12,12, 6, 8, 8, 9, + 9,10,10,11,11,12,12, 8, 9, 9,10,10,11,11,12,12, + 13,13, 8, 9, 9,10,10,11,11,12,12,13,13,10,10,10, + 11,11,13,13,13,13,15,14, 9,10,10,12,11,12,13,13, + 13,14,15,11,12,12,13,13,13,13,15,14,15,15,11,11, + 12,13,13,14,14,14,15,15,15, +}; + +static float _vq_quantthresh__44u6__p8_0[] = { + -49.5, -38.5, -27.5, -16.5, -5.5, 5.5, 16.5, 27.5, + 38.5, 49.5, +}; + +static long _vq_quantmap__44u6__p8_0[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u6__p8_0 = { + _vq_quantthresh__44u6__p8_0, + _vq_quantmap__44u6__p8_0, + 11, + 11 +}; + +static static_codebook _44u6__p8_0 = { + 2, 121, + _vq_lengthlist__44u6__p8_0, + 1, -524582912, 1618345984, 4, 0, + _vq_quantlist__44u6__p8_0, + NULL, + &_vq_auxt__44u6__p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u6__p8_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u6__p8_1[] = { + 3, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 5, 6, 5, 7, 7, + 7, 7, 8, 7, 8, 8, 5, 5, 6, 6, 7, 7, 7, 7, 7, 8, + 8, 6, 7, 7, 7, 7, 8, 7, 8, 8, 8, 8, 6, 6, 7, 7, + 7, 7, 8, 8, 8, 8, 8, 7, 7, 7, 8, 8, 8, 8, 8, 8, + 8, 8, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 7, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44u6__p8_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44u6__p8_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u6__p8_1 = { + _vq_quantthresh__44u6__p8_1, + _vq_quantmap__44u6__p8_1, + 11, + 11 +}; + +static static_codebook _44u6__p8_1 = { + 2, 121, + _vq_lengthlist__44u6__p8_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44u6__p8_1, + NULL, + &_vq_auxt__44u6__p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u6__p9_0[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44u6__p9_0[] = { + 1, 3, 2, 9, 8,15,15,15,15,15,15,15,15,15,15, 4, + 8, 9,13,14,14,14,14,14,14,14,14,14,14,14, 5, 8, + 9,14,14,14,14,14,14,14,14,14,14,14,14,11,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,11,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, + 14, +}; + +static float _vq_quantthresh__44u6__p9_0[] = { + -1657.5, -1402.5, -1147.5, -892.5, -637.5, -382.5, -127.5, 127.5, + 382.5, 637.5, 892.5, 1147.5, 1402.5, 1657.5, +}; + +static long _vq_quantmap__44u6__p9_0[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44u6__p9_0 = { + _vq_quantthresh__44u6__p9_0, + _vq_quantmap__44u6__p9_0, + 15, + 15 +}; + +static static_codebook _44u6__p9_0 = { + 2, 225, + _vq_lengthlist__44u6__p9_0, + 1, -514071552, 1627381760, 4, 0, + _vq_quantlist__44u6__p9_0, + NULL, + &_vq_auxt__44u6__p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u6__p9_1[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44u6__p9_1[] = { + 1, 4, 4, 7, 7, 8, 9, 8, 8, 9, 8, 9, 8, 9, 9, 4, + 7, 6, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 4, 7, + 6, 9, 9,10,10, 9, 9,10,10,10,10,11,11, 7, 9, 8, + 10,10,11,11,10,10,11,11,11,11,11,11, 7, 8, 9,10, + 10,11,11,10,10,11,11,11,11,11,12, 8,10,10,11,11, + 12,12,11,11,12,12,12,12,13,12, 8,10,10,11,11,12, + 11,11,11,11,12,12,12,12,13, 8, 9, 9,11,10,11,11, + 12,12,12,12,13,12,13,12, 8, 9, 9,11,11,11,11,12, + 12,12,12,12,13,13,13, 9,10,10,11,12,12,12,12,12, + 13,13,13,13,13,13, 9,10,10,11,11,12,12,12,12,13, + 13,13,13,14,13,10,10,10,12,11,12,12,13,13,13,13, + 13,13,13,13,10,10,11,11,11,12,12,13,13,13,13,13, + 13,13,13,10,11,11,12,12,13,12,12,13,13,13,13,13, + 13,14,10,11,11,12,12,13,12,13,13,13,14,13,13,14, + 13, +}; + +static float _vq_quantthresh__44u6__p9_1[] = { + -110.5, -93.5, -76.5, -59.5, -42.5, -25.5, -8.5, 8.5, + 25.5, 42.5, 59.5, 76.5, 93.5, 110.5, +}; + +static long _vq_quantmap__44u6__p9_1[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44u6__p9_1 = { + _vq_quantthresh__44u6__p9_1, + _vq_quantmap__44u6__p9_1, + 15, + 15 +}; + +static static_codebook _44u6__p9_1 = { + 2, 225, + _vq_lengthlist__44u6__p9_1, + 1, -522338304, 1620115456, 4, 0, + _vq_quantlist__44u6__p9_1, + NULL, + &_vq_auxt__44u6__p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u6__p9_2[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44u6__p9_2[] = { + 3, 5, 5, 7, 7, 8, 8, 8, 8, 8, 8, 9, 8, 8, 9, 9, + 9, 5, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, + 9, 9, 5, 6, 6, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, + 9, 9, 9, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10, 9, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10, 9, 9, 9,10, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9,10, 9, 9, 9,10, 9, 9,10, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10,10,10, 9,10, 9,10,10, + 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10, 9,10,10, 9, 9, + 10, +}; + +static float _vq_quantthresh__44u6__p9_2[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44u6__p9_2[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44u6__p9_2 = { + _vq_quantthresh__44u6__p9_2, + _vq_quantmap__44u6__p9_2, + 17, + 17 +}; + +static static_codebook _44u6__p9_2 = { + 2, 289, + _vq_lengthlist__44u6__p9_2, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44u6__p9_2, + NULL, + &_vq_auxt__44u6__p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44u6__short[] = { + 4,11,16,13,17,13,17,16,17,17, 4, 7, 9, 9,13,10, + 16,12,16,17, 7, 6, 5, 7, 8, 9,12,12,16,17, 6, 9, + 7, 9,10,10,15,15,17,17, 6, 7, 5, 7, 5, 7, 7,10, + 16,17, 7, 9, 8, 9, 8,10,11,11,15,17, 7, 7, 7, 8, + 5, 8, 8, 9,15,17, 8, 7, 9, 9, 7, 8, 7, 2, 7,15, + 14,13,13,15, 5,10, 4, 3, 6,17,17,15,13,17, 7,11, + 7, 6, 9,16, +}; + +static static_codebook _huff_book__44u6__short = { + 2, 100, + _huff_lengthlist__44u6__short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44u7__long[] = { + 3, 9,14,13,15,14,16,13,13,14, 5, 5, 7, 7, 8, 9, + 11,10,12,15,10, 6, 5, 6, 6, 9,10,10,13,16,10, 6, + 6, 6, 6, 8, 9, 9,12,15,14, 7, 6, 6, 5, 6, 6, 8, + 12,15,10, 8, 7, 7, 6, 7, 7, 7,11,13,14,10, 9, 8, + 5, 6, 4, 5, 9,12,10, 9, 9, 8, 6, 6, 5, 3, 6,11, + 12,11,12,12,10, 9, 8, 5, 5, 8,10,11,15,13,13,13, + 12, 8, 6, 7, +}; + +static static_codebook _huff_book__44u7__long = { + 2, 100, + _huff_lengthlist__44u7__long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44u7__p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u7__p1_0[] = { + 1, 4, 4, 4, 7, 7, 5, 7, 7, 5, 8, 8, 8,10,10, 7, + 10,10, 5, 8, 8, 7,10,10, 8,10,10, 5, 8, 8, 8,11, + 10, 8,10,10, 8,10,10,10,12,13,10,13,13, 7,10,10, + 10,13,12,10,13,13, 5, 8, 8, 8,11,10, 8,10,11, 7, + 10,10,10,13,13,10,12,13, 8,11,11,10,13,13,10,13, + 12, +}; + +static float _vq_quantthresh__44u7__p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u7__p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u7__p1_0 = { + _vq_quantthresh__44u7__p1_0, + _vq_quantmap__44u7__p1_0, + 3, + 3 +}; + +static static_codebook _44u7__p1_0 = { + 4, 81, + _vq_lengthlist__44u7__p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u7__p1_0, + NULL, + &_vq_auxt__44u7__p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u7__p2_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u7__p2_0[] = { + 3, 4, 4, 5, 6, 6, 5, 6, 6, 5, 6, 6, 6, 8, 8, 6, + 7, 8, 5, 6, 6, 6, 8, 7, 6, 8, 8, 5, 6, 6, 6, 8, + 7, 6, 8, 8, 6, 8, 8, 8, 9, 9, 8, 9, 9, 6, 8, 7, + 7, 9, 8, 8, 9, 9, 5, 6, 6, 6, 8, 7, 6, 8, 8, 6, + 8, 8, 8, 9, 9, 7, 8, 9, 6, 8, 8, 8, 9, 9, 8, 9, + 9, +}; + +static float _vq_quantthresh__44u7__p2_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u7__p2_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u7__p2_0 = { + _vq_quantthresh__44u7__p2_0, + _vq_quantmap__44u7__p2_0, + 3, + 3 +}; + +static static_codebook _44u7__p2_0 = { + 4, 81, + _vq_lengthlist__44u7__p2_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u7__p2_0, + NULL, + &_vq_auxt__44u7__p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u7__p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u7__p3_0[] = { + 2, 5, 4, 8, 8, 5, 7, 6, 9, 9, 5, 6, 7, 9, 9, 8, + 9, 9,13,12, 8, 9,10,12,13, 5, 7, 7,10, 9, 7, 9, + 9,11,11, 6, 8, 9,11,11,10,11,11,14,14, 9,10,11, + 13,14, 5, 7, 7, 9, 9, 7, 9, 8,11,11, 7, 9, 9,11, + 11, 9,11,10,14,13,10,11,11,14,14, 8,10,10,14,13, + 10,11,12,15,14, 9,11,11,15,14,13,14,14,16,16,12, + 13,14,17,16, 8,10,10,13,13, 9,11,11,14,15,10,11, + 12,14,15,12,14,13,16,16,13,14,15,15,17, 5, 7, 7, + 10,10, 7, 9, 9,11,11, 7, 9, 9,11,11,10,12,11,15, + 14,10,11,12,14,14, 7, 9, 9,12,12, 9,11,11,13,13, + 9,11,11,13,13,11,13,13,14,17,11,13,13,15,16, 6, + 9, 9,11,11, 8,11,10,13,12, 9,11,11,13,13,11,13, + 12,16,14,11,13,13,16,16,10,12,12,15,15,11,13,13, + 16,16,11,13,13,16,15,14,16,17,17,19,14,16,16,18, + 0, 9,11,11,14,15,10,13,12,16,15,11,13,13,16,16, + 14,15,14, 0,16,14,16,16,18, 0, 5, 7, 7,10,10, 7, + 9, 9,12,11, 7, 9, 9,11,12,10,11,11,15,14,10,11, + 12,14,14, 6, 9, 9,11,11, 9,11,11,13,13, 8,10,11, + 12,13,11,13,13,17,15,11,12,13,14,15, 7, 9, 9,11, + 12, 9,11,11,13,13, 9,11,11,13,13,11,13,12,16,16, + 11,13,13,15,14, 9,11,11,14,15,11,13,13,16,15,10, + 12,13,16,16,15,16,16, 0, 0,14,13,15,16,18,10,11, + 11,15,15,11,13,14,16,18,11,13,13,16,15,15,16,16, + 19, 0,14,15,15,16,16, 8,10,10,13,13,10,12,11,16, + 15,10,11,11,16,15,13,15,16,18, 0,13,14,15,17,17, + 9,11,11,15,15,11,13,13,16,18,11,13,13,16,17,15, + 16,16, 0, 0,15,18,16, 0,17, 9,11,11,15,15,11,13, + 12,17,15,11,13,14,16,17,15,18,15, 0,17,15,16,16, + 18,19,13,15,14, 0,18,14,16,16,19,18,14,16,15,19, + 19,16,18,19, 0, 0,16,17, 0, 0, 0,12,14,14,17,17, + 13,16,14, 0,18,14,16,15,18, 0,16,18,16,19,17,18, + 19,17, 0, 0, 8,10,10,14,14, 9,12,11,15,15,10,11, + 12,15,17,13,15,15,18,16,14,16,15,18,17, 9,11,11, + 16,15,11,13,13, 0,16,11,12,13,16,15,15,16,16, 0, + 17,15,15,16,18,17, 9,12,11,15,17,11,13,13,16,16, + 11,14,13,16,16,15,15,16,18,19,16,18,16, 0, 0,12, + 14,14, 0,16,14,16,16, 0,18,13,14,15,16, 0,17,16, + 18, 0, 0,16,16,17,19, 0,13,14,14,17, 0,14,17,16, + 0,19,14,15,15,18,19,17,16,18, 0, 0,15,19,16, 0, + 0, +}; + +static float _vq_quantthresh__44u7__p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u7__p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u7__p3_0 = { + _vq_quantthresh__44u7__p3_0, + _vq_quantmap__44u7__p3_0, + 5, + 5 +}; + +static static_codebook _44u7__p3_0 = { + 4, 625, + _vq_lengthlist__44u7__p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u7__p3_0, + NULL, + &_vq_auxt__44u7__p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u7__p4_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u7__p4_0[] = { + 4, 5, 5, 8, 8, 6, 7, 6, 9, 9, 6, 6, 7, 9, 9, 8, + 9, 9,11,11, 8, 9, 9,10,11, 6, 7, 7, 9, 9, 7, 8, + 8,10,10, 6, 7, 8, 9,10, 9,10,10,12,12, 9, 9,10, + 11,12, 6, 7, 7, 9, 9, 6, 8, 7,10, 9, 7, 8, 8,10, + 10, 9,10, 9,12,11, 9,10,10,12,11, 8, 9, 9,11,11, + 9,10,10,12,12, 9,10,10,12,12,11,12,12,13,14,11, + 11,12,13,13, 8, 9, 9,11,11, 9,10,10,12,11, 9,10, + 10,12,12,11,12,11,13,13,11,12,12,13,13, 6, 7, 7, + 9, 9, 7, 8, 7,10,10, 7, 7, 8,10,10, 9,10,10,12, + 11, 9,10,10,12,12, 7, 8, 8,10,10, 8, 8, 9,11,11, + 8, 9, 9,11,11,10,11,11,12,12,10,10,11,12,13, 6, + 7, 7,10,10, 7, 9, 8,11,10, 8, 8, 9,10,11,10,11, + 10,13,11,10,11,11,12,12, 9,10,10,12,12,10,10,11, + 13,13,10,11,11,13,12,12,12,13,13,14,12,12,13,14, + 14, 9,10,10,12,12, 9,10,10,12,12,10,11,11,13,13, + 11,12,11,14,12,12,13,13,14,14, 6, 7, 7, 9, 9, 7, + 8, 7,10,10, 7, 7, 8,10,10, 9,10,10,12,11, 9,10, + 10,11,12, 6, 7, 7,10,10, 8, 9, 8,11,10, 7, 8, 9, + 10,11,10,11,11,13,12,10,10,11,11,13, 7, 8, 8,10, + 10, 8, 9, 9,11,11, 8, 9, 9,11,11,10,11,10,13,12, + 10,11,11,12,12, 9,10,10,12,12,10,11,11,13,12, 9, + 10,10,12,13,12,13,12,14,14,11,11,12,12,14, 9,10, + 10,12,12,10,11,11,13,13,10,11,11,13,13,12,13,12, + 14,14,12,13,12,14,13, 8, 9, 9,11,11, 9,10,10,12, + 12, 9,10,10,12,12,11,12,12,14,13,11,12,12,13,13, + 9,10,10,12,12,10,11,11,13,13,10,11,11,13,12,12, + 13,13,14,14,12,12,13,14,14, 9,10,10,12,12, 9,11, + 10,13,12,10,10,11,12,13,11,13,12,14,13,12,12,13, + 14,14,11,12,12,13,13,11,12,13,14,14,12,13,13,14, + 14,13,13,14,14,16,13,14,14,16,16,11,11,11,13,13, + 11,12,11,14,13,12,12,13,14,15,13,14,12,16,13,14, + 14,14,15,16, 8, 9, 9,11,11, 9,10,10,12,12, 9,10, + 10,12,12,11,12,12,14,13,11,12,12,13,14, 9,10,10, + 12,12,10,11,10,13,12, 9,10,11,12,13,12,13,12,14, + 14,12,12,13,13,14, 9,10,10,12,12,10,11,11,12,13, + 10,11,11,13,13,12,13,12,14,14,12,13,13,14,14,11, + 12,12,13,13,12,13,12,14,14,11,11,12,13,14,13,15, + 14,16,15,13,12,14,13,16,11,12,12,13,13,12,13,13, + 14,14,12,12,12,14,14,13,14,14,15,15,13,14,13,16, + 14, +}; + +static float _vq_quantthresh__44u7__p4_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u7__p4_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u7__p4_0 = { + _vq_quantthresh__44u7__p4_0, + _vq_quantmap__44u7__p4_0, + 5, + 5 +}; + +static static_codebook _44u7__p4_0 = { + 4, 625, + _vq_lengthlist__44u7__p4_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u7__p4_0, + NULL, + &_vq_auxt__44u7__p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u7__p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u7__p5_0[] = { + 2, 3, 3, 6, 6, 7, 8,10,10, 4, 5, 5, 8, 7, 8, 8, + 11,11, 3, 5, 5, 7, 7, 8, 9,11,11, 6, 8, 7, 9, 9, + 10,10,12,12, 6, 7, 8, 9,10,10,10,12,12, 8, 8, 8, + 10,10,12,11,13,13, 8, 8, 9,10,10,11,11,13,13,10, + 11,11,12,12,13,13,14,14,10,11,11,12,12,13,13,14, + 14, +}; + +static float _vq_quantthresh__44u7__p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44u7__p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u7__p5_0 = { + _vq_quantthresh__44u7__p5_0, + _vq_quantmap__44u7__p5_0, + 9, + 9 +}; + +static static_codebook _44u7__p5_0 = { + 2, 81, + _vq_lengthlist__44u7__p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44u7__p5_0, + NULL, + &_vq_auxt__44u7__p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u7__p6_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u7__p6_0[] = { + 3, 4, 4, 5, 5, 7, 7, 9, 9, 4, 5, 4, 6, 6, 8, 7, + 9, 9, 4, 4, 5, 6, 6, 7, 7, 9, 9, 5, 6, 6, 7, 7, + 8, 8,10,10, 5, 6, 6, 7, 7, 8, 8,10,10, 7, 8, 7, + 8, 8,10, 9,11,11, 7, 7, 8, 8, 8, 9,10,11,11, 9, + 9, 9,10,10,11,10,12,11, 9, 9, 9,10,10,11,11,11, + 12, +}; + +static float _vq_quantthresh__44u7__p6_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44u7__p6_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u7__p6_0 = { + _vq_quantthresh__44u7__p6_0, + _vq_quantmap__44u7__p6_0, + 9, + 9 +}; + +static static_codebook _44u7__p6_0 = { + 2, 81, + _vq_lengthlist__44u7__p6_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44u7__p6_0, + NULL, + &_vq_auxt__44u7__p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u7__p7_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u7__p7_0[] = { + 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 9, 8, 8, 9, 9, 7, + 10,10, 5, 8, 9, 7, 9,10, 8, 9, 9, 4, 9, 9, 9,11, + 10, 8,10,10, 7,11,10,10,10,12,10,12,12, 7,10,10, + 10,12,11,10,12,12, 5, 9, 9, 8,10,10, 9,11,11, 7, + 11,10,10,12,12,10,11,12, 7,10,11,10,12,12,10,12, + 10, +}; + +static float _vq_quantthresh__44u7__p7_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44u7__p7_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u7__p7_0 = { + _vq_quantthresh__44u7__p7_0, + _vq_quantmap__44u7__p7_0, + 3, + 3 +}; + +static static_codebook _44u7__p7_0 = { + 4, 81, + _vq_lengthlist__44u7__p7_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44u7__p7_0, + NULL, + &_vq_auxt__44u7__p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u7__p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u7__p7_1[] = { + 3, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8, 4, 5, 5, 6, 6, + 8, 7, 8, 8, 8, 8, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8, + 8, 6, 7, 6, 7, 7, 8, 8, 9, 9, 9, 9, 6, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 7, 8, 7, 8, 8, 9, 9, 9, 9, + 9, 9, 7, 7, 8, 8, 8, 9, 9, 9, 9, 9, 9, 8, 8, 8, + 9, 9, 9, 9,10, 9, 9, 9, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9,10, 8, 8, 8, 9, 9, 9, 9,10, 9,10,10, 8, 8, + 8, 9, 9, 9, 9, 9,10,10,10, +}; + +static float _vq_quantthresh__44u7__p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44u7__p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u7__p7_1 = { + _vq_quantthresh__44u7__p7_1, + _vq_quantmap__44u7__p7_1, + 11, + 11 +}; + +static static_codebook _44u7__p7_1 = { + 2, 121, + _vq_lengthlist__44u7__p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44u7__p7_1, + NULL, + &_vq_auxt__44u7__p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u7__p8_0[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u7__p8_0[] = { + 1, 4, 4, 6, 6, 8, 8,10,10,11,11, 4, 6, 6, 7, 7, + 9, 9,11,10,12,12, 5, 6, 5, 7, 7, 9, 9,10,11,12, + 12, 6, 7, 7, 8, 8,10,10,11,11,13,13, 6, 7, 7, 8, + 8,10,10,11,12,13,13, 8, 9, 9,10,10,11,11,12,12, + 14,14, 8, 9, 9,10,10,11,11,12,12,14,14,10,10,10, + 11,11,13,12,14,14,15,15,10,10,10,12,12,13,13,14, + 14,15,15,11,12,12,13,13,14,14,15,14,16,15,11,12, + 12,13,13,14,14,15,15,15,16, +}; + +static float _vq_quantthresh__44u7__p8_0[] = { + -49.5, -38.5, -27.5, -16.5, -5.5, 5.5, 16.5, 27.5, + 38.5, 49.5, +}; + +static long _vq_quantmap__44u7__p8_0[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u7__p8_0 = { + _vq_quantthresh__44u7__p8_0, + _vq_quantmap__44u7__p8_0, + 11, + 11 +}; + +static static_codebook _44u7__p8_0 = { + 2, 121, + _vq_lengthlist__44u7__p8_0, + 1, -524582912, 1618345984, 4, 0, + _vq_quantlist__44u7__p8_0, + NULL, + &_vq_auxt__44u7__p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u7__p8_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u7__p8_1[] = { + 4, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 5, 6, 6, 7, 7, + 7, 7, 7, 7, 7, 7, 5, 6, 6, 6, 7, 7, 7, 7, 7, 7, + 7, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 6, 7, 7, 7, + 7, 7, 7, 7, 7, 8, 8, 7, 7, 7, 7, 7, 8, 7, 8, 8, + 8, 8, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 7, 7, 7, + 7, 7, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 8, 8, 8, + 8, 8, 8, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44u7__p8_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44u7__p8_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u7__p8_1 = { + _vq_quantthresh__44u7__p8_1, + _vq_quantmap__44u7__p8_1, + 11, + 11 +}; + +static static_codebook _44u7__p8_1 = { + 2, 121, + _vq_lengthlist__44u7__p8_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44u7__p8_1, + NULL, + &_vq_auxt__44u7__p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u7__p9_0[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u7__p9_0[] = { + 1, 3, 3,10,10,10,10,10,10,10,10, 4,10,10,10,10, + 10,10,10,10,10,10, 4,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, +}; + +static float _vq_quantthresh__44u7__p9_0[] = { + -2866.5, -2229.5, -1592.5, -955.5, -318.5, 318.5, 955.5, 1592.5, + 2229.5, 2866.5, +}; + +static long _vq_quantmap__44u7__p9_0[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u7__p9_0 = { + _vq_quantthresh__44u7__p9_0, + _vq_quantmap__44u7__p9_0, + 11, + 11 +}; + +static static_codebook _44u7__p9_0 = { + 2, 121, + _vq_lengthlist__44u7__p9_0, + 1, -512171520, 1630791680, 4, 0, + _vq_quantlist__44u7__p9_0, + NULL, + &_vq_auxt__44u7__p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u7__p9_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u7__p9_1[] = { + 1, 4, 4, 6, 5, 8, 6, 9, 8,10, 9,11,10, 4, 6, 6, + 8, 8, 9, 9,11,10,11,11,11,11, 4, 6, 6, 8, 8,10, + 9,11,11,11,11,11,12, 6, 8, 8,10,10,11,11,12,12, + 13,12,13,13, 6, 8, 8,10,10,11,11,12,12,12,13,14, + 13, 8,10,10,11,11,12,13,14,14,14,14,15,15, 8,10, + 10,11,12,12,13,13,14,14,14,14,15, 9,11,11,13,13, + 14,14,15,14,16,15,17,15, 9,11,11,12,13,14,14,15, + 14,15,15,15,16,10,12,12,13,14,15,15,15,15,16,17, + 16,17,10,13,12,13,14,14,16,16,16,16,15,16,17,11, + 13,13,14,15,14,17,15,16,17,17,17,17,11,13,13,14, + 15,15,15,15,17,17,16,17,16, +}; + +static float _vq_quantthresh__44u7__p9_1[] = { + -269.5, -220.5, -171.5, -122.5, -73.5, -24.5, 24.5, 73.5, + 122.5, 171.5, 220.5, 269.5, +}; + +static long _vq_quantmap__44u7__p9_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u7__p9_1 = { + _vq_quantthresh__44u7__p9_1, + _vq_quantmap__44u7__p9_1, + 13, + 13 +}; + +static static_codebook _44u7__p9_1 = { + 2, 169, + _vq_lengthlist__44u7__p9_1, + 1, -518889472, 1622704128, 4, 0, + _vq_quantlist__44u7__p9_1, + NULL, + &_vq_auxt__44u7__p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u7__p9_2[] = { + 24, + 23, + 25, + 22, + 26, + 21, + 27, + 20, + 28, + 19, + 29, + 18, + 30, + 17, + 31, + 16, + 32, + 15, + 33, + 14, + 34, + 13, + 35, + 12, + 36, + 11, + 37, + 10, + 38, + 9, + 39, + 8, + 40, + 7, + 41, + 6, + 42, + 5, + 43, + 4, + 44, + 3, + 45, + 2, + 46, + 1, + 47, + 0, + 48, +}; + +static long _vq_lengthlist__44u7__p9_2[] = { + 2, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, + 8, +}; + +static float _vq_quantthresh__44u7__p9_2[] = { + -23.5, -22.5, -21.5, -20.5, -19.5, -18.5, -17.5, -16.5, + -15.5, -14.5, -13.5, -12.5, -11.5, -10.5, -9.5, -8.5, + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, + 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, + 16.5, 17.5, 18.5, 19.5, 20.5, 21.5, 22.5, 23.5, +}; + +static long _vq_quantmap__44u7__p9_2[] = { + 47, 45, 43, 41, 39, 37, 35, 33, + 31, 29, 27, 25, 23, 21, 19, 17, + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, + 48, +}; + +static encode_aux_threshmatch _vq_auxt__44u7__p9_2 = { + _vq_quantthresh__44u7__p9_2, + _vq_quantmap__44u7__p9_2, + 49, + 49 +}; + +static static_codebook _44u7__p9_2 = { + 1, 49, + _vq_lengthlist__44u7__p9_2, + 1, -526909440, 1611661312, 6, 0, + _vq_quantlist__44u7__p9_2, + NULL, + &_vq_auxt__44u7__p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44u7__short[] = { + 5,12,17,16,16,17,17,17,17,17, 4, 7,11,11,12, 9, + 17,10,17,17, 7, 7, 8, 9, 7, 9,11,10,15,17, 7, 9, + 10,11,10,12,14,12,16,17, 7, 8, 5, 7, 4, 7, 7, 8, + 16,16, 6,10, 9,10, 7,10,11,11,16,17, 6, 8, 8, 9, + 5, 7, 5, 8,16,17, 5, 5, 8, 7, 6, 7, 7, 6, 6,14, + 12,10,12,11, 7,11, 4, 4, 2, 7,17,15,15,15, 8,15, + 6, 8, 5, 9, +}; + +static static_codebook _huff_book__44u7__short = { + 2, 100, + _huff_lengthlist__44u7__short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44u8__long[] = { + 3, 9,13,14,14,15,14,14,15,15, 5, 4, 6, 8,10,12, + 12,14,15,15, 9, 5, 4, 5, 8,10,11,13,16,16,10, 7, + 4, 3, 5, 7, 9,11,13,13,10, 9, 7, 4, 4, 6, 8,10, + 12,14,13,11, 9, 6, 5, 5, 6, 8,12,14,13,11,10, 8, + 7, 6, 6, 7,10,14,13,11,12,10, 8, 7, 6, 6, 9,13, + 12,11,14,12,11, 9, 8, 7, 9,11,11,12,14,13,14,11, + 10, 8, 8, 9, +}; + +static static_codebook _huff_book__44u8__long = { + 2, 100, + _huff_lengthlist__44u8__long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44u8__short[] = { + 6,14,18,18,17,17,17,17,17,17, 4, 7, 9, 9,10,13, + 15,17,17,17, 6, 7, 5, 6, 8,11,16,17,16,17, 5, 7, + 5, 4, 6,10,14,17,17,17, 6, 6, 6, 5, 7,10,13,16, + 17,17, 7, 6, 7, 7, 7, 8, 7,10,15,16,12, 9, 9, 6, + 6, 5, 3, 5,11,15,14,14,13, 5, 5, 7, 3, 4, 8,15, + 17,17,13, 7, 7,10, 6, 6,10,15,17,17,16,10,11,14, + 10,10,15,17, +}; + +static static_codebook _huff_book__44u8__short = { + 2, 100, + _huff_lengthlist__44u8__short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u8_p1_0[] = { + 1, 5, 5, 5, 7, 7, 5, 7, 7, 5, 7, 7, 8, 9, 9, 7, + 9, 9, 5, 7, 7, 7, 9, 9, 8, 9, 9, 5, 7, 7, 7, 9, + 9, 7, 9, 9, 7, 9, 9, 9,10,11, 9,11,10, 7, 9, 9, + 9,11,10, 9,10,11, 5, 7, 7, 7, 9, 9, 7, 9, 9, 7, + 9, 9, 9,11,10, 9,10,10, 8, 9, 9, 9,11,11, 9,11, + 10, +}; + +static float _vq_quantthresh__44u8_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u8_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p1_0 = { + _vq_quantthresh__44u8_p1_0, + _vq_quantmap__44u8_p1_0, + 3, + 3 +}; + +static static_codebook _44u8_p1_0 = { + 4, 81, + _vq_lengthlist__44u8_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u8_p1_0, + NULL, + &_vq_auxt__44u8_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u8_p2_0[] = { + 4, 5, 5, 8, 8, 5, 7, 6, 9, 9, 5, 6, 7, 9, 9, 8, + 9, 9,11,11, 8, 9, 9,11,11, 5, 7, 7, 9, 9, 7, 8, + 8,10,10, 7, 8, 8,10,10, 9,10,10,12,12, 9,10,10, + 11,12, 5, 7, 7, 9, 9, 7, 8, 7,10,10, 7, 8, 8,10, + 10, 9,10, 9,12,11, 9,10,10,12,12, 8, 9, 9,12,11, + 9,10,10,12,12, 9,10,10,12,12,11,12,12,14,14,11, + 11,12,13,14, 8, 9, 9,11,11, 9,10,10,12,12, 9,10, + 10,12,12,11,12,11,13,13,11,12,12,14,14, 5, 7, 7, + 9, 9, 7, 8, 8,10,10, 7, 8, 8,10,10, 9,10,10,12, + 12, 9,10,10,11,12, 7, 8, 8,10,10, 8, 9, 9,11,11, + 8, 9, 9,11,11,10,11,11,12,13,10,11,11,12,13, 6, + 8, 8,10,10, 8, 9, 8,11,10, 8, 9, 9,11,11,10,11, + 10,13,12,10,11,11,13,13, 9,10,10,12,12,10,11,11, + 13,13,10,11,11,13,13,12,12,13,13,14,12,13,13,14, + 14, 9,10,10,12,12,10,11,10,13,12,10,11,11,13,13, + 11,13,12,14,13,12,13,13,14,14, 5, 7, 7, 9, 9, 7, + 8, 8,10,10, 7, 8, 8,10,10, 9,10,10,12,12, 9,10, + 10,12,12, 7, 8, 8,10,10, 8, 9, 9,11,11, 8, 8, 9, + 10,11,10,11,11,13,13,10,10,11,12,13, 7, 8, 8,10, + 10, 8, 9, 9,11,11, 8, 9, 9,11,11,10,11,11,13,13, + 10,11,11,13,12, 9,10,10,12,12,10,11,11,13,13,10, + 10,11,12,13,12,13,13,14,14,12,12,13,13,14, 9,10, + 10,12,12,10,11,11,13,13,10,11,11,13,13,12,13,13, + 15,14,12,13,13,14,13, 8, 9, 9,11,11, 9,10,10,12, + 12, 9,10,10,12,12,12,12,12,14,13,11,12,12,14,14, + 9,10,10,12,12,10,11,11,13,13,10,11,11,13,13,12, + 13,13,14,15,12,13,13,14,15, 9,10,10,12,12,10,11, + 10,13,12,10,11,11,13,13,12,13,12,15,14,12,13,13, + 14,15,11,12,12,14,14,12,13,13,14,14,12,13,13,15, + 14,14,14,14,14,16,14,14,15,16,16,11,12,12,14,14, + 11,12,12,14,14,12,13,13,14,15,13,14,13,16,14,14, + 14,14,16,16, 8, 9, 9,11,11, 9,10,10,12,12, 9,10, + 10,12,12,11,12,12,14,13,11,12,12,14,14, 9,10,10, + 12,12,10,11,11,13,13,10,10,11,12,13,12,13,13,15, + 14,12,12,13,13,14, 9,10,10,12,12,10,11,11,13,13, + 10,11,11,13,13,12,13,13,14,14,12,13,13,15,14,11, + 12,12,14,13,12,13,13,15,14,11,12,12,13,14,14,15, + 14,16,15,13,13,14,13,16,11,12,12,14,14,12,13,13, + 14,15,12,13,12,15,14,14,14,14,16,15,14,15,13,16, + 14, +}; + +static float _vq_quantthresh__44u8_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u8_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p2_0 = { + _vq_quantthresh__44u8_p2_0, + _vq_quantmap__44u8_p2_0, + 5, + 5 +}; + +static static_codebook _44u8_p2_0 = { + 4, 625, + _vq_lengthlist__44u8_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u8_p2_0, + NULL, + &_vq_auxt__44u8_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u8_p3_0[] = { + 3, 4, 4, 5, 5, 7, 7, 9, 9, 4, 5, 4, 6, 6, 7, 7, + 9, 9, 4, 4, 5, 6, 6, 7, 7, 9, 9, 5, 6, 6, 7, 7, + 8, 8,10,10, 6, 6, 6, 7, 7, 8, 8,10,10, 7, 7, 7, + 8, 8, 9, 9,11,10, 7, 7, 7, 8, 8, 9, 9,10,11, 9, + 9, 9,10,10,11,10,12,11, 9, 9, 9, 9,10,11,11,11, + 12, +}; + +static float _vq_quantthresh__44u8_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44u8_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p3_0 = { + _vq_quantthresh__44u8_p3_0, + _vq_quantmap__44u8_p3_0, + 9, + 9 +}; + +static static_codebook _44u8_p3_0 = { + 2, 81, + _vq_lengthlist__44u8_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44u8_p3_0, + NULL, + &_vq_auxt__44u8_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p4_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44u8_p4_0[] = { + 4, 4, 4, 6, 6, 7, 7, 8, 8, 8, 8,10,10,11,11,11, + 11, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,10,10,11,11, + 12,12, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9,10,10,11, + 11,12,12, 6, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9,10,10, + 11,11,12,12, 6, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9,10, + 10,11,11,12,12, 7, 7, 7, 8, 8, 9, 8,10, 9,10, 9, + 11,10,12,11,13,12, 7, 7, 7, 8, 8, 8, 9, 9,10, 9, + 10,10,11,11,12,12,13, 8, 8, 8, 9, 9, 9, 9,10,10, + 11,10,11,11,12,12,13,13, 8, 8, 8, 9, 9, 9,10,10, + 10,10,11,11,11,12,12,12,13, 8, 9, 9, 9, 9,10, 9, + 11,10,11,11,12,11,13,12,13,13, 8, 9, 9, 9, 9, 9, + 10,10,11,11,11,11,12,12,13,13,13,10,10,10,10,10, + 11,10,11,11,12,11,13,12,13,13,14,13,10,10,10,10, + 10,10,11,11,11,11,12,12,13,13,13,13,14,11,11,11, + 11,11,12,11,12,12,13,12,13,13,14,13,14,14,11,11, + 11,11,11,11,12,12,12,12,13,13,13,13,14,14,14,11, + 12,12,12,12,13,12,13,12,13,13,14,13,14,14,14,14, + 11,12,12,12,12,12,12,13,13,13,13,13,14,14,14,14, + 14, +}; + +static float _vq_quantthresh__44u8_p4_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44u8_p4_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p4_0 = { + _vq_quantthresh__44u8_p4_0, + _vq_quantmap__44u8_p4_0, + 17, + 17 +}; + +static static_codebook _44u8_p4_0 = { + 2, 289, + _vq_lengthlist__44u8_p4_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44u8_p4_0, + NULL, + &_vq_auxt__44u8_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p5_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u8_p5_0[] = { + 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 8, 8, 8, 9, 9, 7, + 9, 9, 5, 8, 8, 7, 9, 9, 8, 9, 9, 5, 8, 8, 8,10, + 10, 8,10,10, 7,10,10, 9,10,12, 9,12,11, 7,10,10, + 9,11,10, 9,11,12, 5, 8, 8, 8,10,10, 8,10,10, 7, + 10,10, 9,11,11, 9,10,11, 7,10,10, 9,11,11,10,12, + 10, +}; + +static float _vq_quantthresh__44u8_p5_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44u8_p5_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p5_0 = { + _vq_quantthresh__44u8_p5_0, + _vq_quantmap__44u8_p5_0, + 3, + 3 +}; + +static static_codebook _44u8_p5_0 = { + 4, 81, + _vq_lengthlist__44u8_p5_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44u8_p5_0, + NULL, + &_vq_auxt__44u8_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p5_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u8_p5_1[] = { + 4, 5, 5, 6, 6, 7, 7, 7, 7, 8, 8, 5, 5, 5, 6, 6, + 7, 7, 8, 8, 8, 8, 5, 5, 5, 6, 6, 7, 7, 7, 8, 8, + 8, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 6, 6, 6, 7, + 7, 7, 7, 8, 8, 8, 8, 7, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 7, 8, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 7, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 9, 9, +}; + +static float _vq_quantthresh__44u8_p5_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44u8_p5_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p5_1 = { + _vq_quantthresh__44u8_p5_1, + _vq_quantmap__44u8_p5_1, + 11, + 11 +}; + +static static_codebook _44u8_p5_1 = { + 2, 121, + _vq_lengthlist__44u8_p5_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44u8_p5_1, + NULL, + &_vq_auxt__44u8_p5_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u8_p6_0[] = { + 2, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10, 4, 6, 5, + 7, 7, 8, 8, 8, 8, 9, 9,10,10, 4, 6, 6, 7, 7, 8, + 8, 8, 8, 9, 9,10,10, 6, 7, 7, 7, 8, 8, 8, 8, 9, + 9,10,10,10, 6, 7, 7, 8, 8, 8, 8, 9, 8,10, 9,11, + 10, 7, 8, 8, 8, 8, 8, 9, 9, 9,10,10,11,11, 7, 8, + 8, 8, 8, 9, 8, 9, 9,10,10,11,11, 8, 8, 8, 9, 9, + 9, 9, 9,10,10,10,11,11, 8, 8, 8, 9, 9, 9, 9,10, + 9,10,10,11,11, 9, 9, 9, 9,10,10,10,10,10,10,11, + 11,12, 9, 9, 9,10, 9,10,10,10,10,11,10,12,11,10, + 10,10,10,10,11,11,11,11,11,12,12,12,10,10,10,10, + 11,11,11,11,11,12,11,12,12, +}; + +static float _vq_quantthresh__44u8_p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44u8_p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p6_0 = { + _vq_quantthresh__44u8_p6_0, + _vq_quantmap__44u8_p6_0, + 13, + 13 +}; + +static static_codebook _44u8_p6_0 = { + 2, 169, + _vq_lengthlist__44u8_p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44u8_p6_0, + NULL, + &_vq_auxt__44u8_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u8_p6_1[] = { + 3, 4, 4, 5, 5, 4, 5, 5, 5, 5, 4, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, +}; + +static float _vq_quantthresh__44u8_p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u8_p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p6_1 = { + _vq_quantthresh__44u8_p6_1, + _vq_quantmap__44u8_p6_1, + 5, + 5 +}; + +static static_codebook _44u8_p6_1 = { + 2, 25, + _vq_lengthlist__44u8_p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u8_p6_1, + NULL, + &_vq_auxt__44u8_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u8_p7_0[] = { + 1, 4, 5, 6, 6, 7, 7, 8, 8,10,10,11,11, 5, 6, 6, + 7, 7, 8, 8, 9, 9,11,10,12,11, 5, 6, 6, 7, 7, 8, + 8, 9, 9,10,11,11,12, 6, 7, 7, 8, 8, 9, 9,10,10, + 11,11,12,12, 6, 7, 7, 8, 8, 9, 9,10,10,11,12,13, + 12, 7, 8, 8, 9, 9,10,10,11,11,12,12,13,13, 8, 8, + 8, 9, 9,10,10,11,11,12,12,13,13, 9, 9, 9,10,10, + 11,11,12,12,13,13,14,14, 9, 9, 9,10,10,11,11,12, + 12,13,13,14,14,10,11,11,12,11,13,12,13,13,14,14, + 15,15,10,11,11,11,12,12,13,13,14,14,14,15,15,11, + 12,12,13,13,14,13,15,14,15,15,16,15,11,11,12,13, + 13,13,14,14,14,15,15,15,16, +}; + +static float _vq_quantthresh__44u8_p7_0[] = { + -60.5, -49.5, -38.5, -27.5, -16.5, -5.5, 5.5, 16.5, + 27.5, 38.5, 49.5, 60.5, +}; + +static long _vq_quantmap__44u8_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p7_0 = { + _vq_quantthresh__44u8_p7_0, + _vq_quantmap__44u8_p7_0, + 13, + 13 +}; + +static static_codebook _44u8_p7_0 = { + 2, 169, + _vq_lengthlist__44u8_p7_0, + 1, -523206656, 1618345984, 4, 0, + _vq_quantlist__44u8_p7_0, + NULL, + &_vq_auxt__44u8_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u8_p7_1[] = { + 4, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 5, 6, 6, 7, 7, + 7, 7, 7, 7, 7, 7, 5, 6, 6, 7, 7, 7, 7, 7, 7, 7, + 7, 6, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 6, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 8, 7, 7, 7, 7, 7, 7, 7, 8, 8, + 8, 8, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 7, 7, 7, + 8, 7, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 8, 8, 8, + 8, 8, 8, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, + 7, 8, 8, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44u8_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44u8_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p7_1 = { + _vq_quantthresh__44u8_p7_1, + _vq_quantmap__44u8_p7_1, + 11, + 11 +}; + +static static_codebook _44u8_p7_1 = { + 2, 121, + _vq_lengthlist__44u8_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44u8_p7_1, + NULL, + &_vq_auxt__44u8_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p8_0[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44u8_p8_0[] = { + 1, 4, 4, 7, 7, 8, 8, 8, 7, 9, 8,10, 9,11,10, 4, + 6, 6, 8, 8,10, 9, 9, 9,10,10,11,10,12,10, 4, 6, + 6, 8, 8,10,10, 9, 9,10,10,11,11,11,12, 7, 8, 8, + 10,10,11,11,11,10,12,11,12,12,13,11, 7, 8, 8,10, + 10,11,11,10,10,11,11,12,12,13,13, 8,10,10,11,11, + 12,11,12,11,13,12,13,12,14,13, 8,10, 9,11,11,12, + 12,12,12,12,12,13,13,14,13, 8, 9, 9,11,10,12,11, + 13,12,13,13,14,13,14,13, 8, 9, 9,10,11,12,12,12, + 12,13,13,14,15,14,14, 9,10,10,12,11,13,12,13,13, + 14,13,14,14,14,14, 9,10,10,12,12,12,12,13,13,14, + 14,14,15,14,14,10,11,11,13,12,13,12,14,14,14,14, + 14,14,15,15,10,11,11,12,12,13,13,14,14,14,15,15, + 14,16,15,11,12,12,13,12,14,14,14,13,15,14,15,15, + 15,17,11,12,12,13,13,14,14,14,15,15,14,15,15,14, + 17, +}; + +static float _vq_quantthresh__44u8_p8_0[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__44u8_p8_0[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p8_0 = { + _vq_quantthresh__44u8_p8_0, + _vq_quantmap__44u8_p8_0, + 15, + 15 +}; + +static static_codebook _44u8_p8_0 = { + 2, 225, + _vq_lengthlist__44u8_p8_0, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__44u8_p8_0, + NULL, + &_vq_auxt__44u8_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p8_1[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__44u8_p8_1[] = { + 4, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 6, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 5, 6, 6, 7, 7, 8, + 8, 9, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, + 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10, 9,10, 8, 8, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 9,10, + 10, 9,10, 8, 9, 8, 9, 9, 9, 9, 9, 9, 9, 9,10, 9, + 10,10,10,10,10,10,10,10, 8, 9, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9,10,10,10,10, 9,10,10, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10, 9,10,10,10,10,10,10, + 10,10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 9,10, + 10,10,10,10,10,10,10, 9, 9, 9, 9, 9, 9, 9,10, 9, + 10,10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9,10,10,10,10,10,10,10,10,10,10, + 10, 9, 9, 9, 9, 9, 9,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10, 9, 9, 9, 9, 9, 9, 9,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, 9, + 9, 9,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 9, 9, 9, 9, 9,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10, 9, 9, 9,10, 9,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, 9,10, + 9,10,10,10,10,10,10,10,10,10,10,10,10,10,10, 9, + 9, 9, 9, 9, 9,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10, 9, 9, 9,10, 9,10, 9,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10, +}; + +static float _vq_quantthresh__44u8_p8_1[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__44u8_p8_1[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p8_1 = { + _vq_quantthresh__44u8_p8_1, + _vq_quantmap__44u8_p8_1, + 21, + 21 +}; + +static static_codebook _44u8_p8_1 = { + 2, 441, + _vq_lengthlist__44u8_p8_1, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__44u8_p8_1, + NULL, + &_vq_auxt__44u8_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p9_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u8_p9_0[] = { + 1, 3, 3, 9, 9, 9, 9, 9, 9, 4, 9, 9, 9, 9, 9, 9, + 9, 9, 5, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, + 8, +}; + +static float _vq_quantthresh__44u8_p9_0[] = { + -3258.5, -2327.5, -1396.5, -465.5, 465.5, 1396.5, 2327.5, 3258.5, +}; + +static long _vq_quantmap__44u8_p9_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p9_0 = { + _vq_quantthresh__44u8_p9_0, + _vq_quantmap__44u8_p9_0, + 9, + 9 +}; + +static static_codebook _44u8_p9_0 = { + 2, 81, + _vq_lengthlist__44u8_p9_0, + 1, -511895552, 1631393792, 4, 0, + _vq_quantlist__44u8_p9_0, + NULL, + &_vq_auxt__44u8_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p9_1[] = { + 9, + 8, + 10, + 7, + 11, + 6, + 12, + 5, + 13, + 4, + 14, + 3, + 15, + 2, + 16, + 1, + 17, + 0, + 18, +}; + +static long _vq_lengthlist__44u8_p9_1[] = { + 1, 4, 4, 7, 7, 8, 7, 8, 6, 9, 7,10, 8,11,10,11, + 11,11,11, 4, 7, 6, 9, 9,10, 9, 9, 9,10,10,11,10, + 11,10,11,11,13,11, 4, 7, 7, 9, 9, 9, 9, 9, 9,10, + 10,11,10,11,11,11,12,11,12, 7, 9, 8,11,11,11,11, + 10,10,11,11,12,12,12,12,12,12,14,13, 7, 8, 9,10, + 11,11,11,10,10,11,11,11,11,12,12,14,12,13,14, 8, + 9, 9,11,11,11,11,11,11,12,12,14,12,15,14,14,14, + 15,14, 8, 9, 9,11,11,11,11,12,11,12,12,13,13,13, + 13,13,13,14,14, 8, 9, 9,11,10,12,11,12,12,13,13, + 13,13,15,14,14,14,16,16, 8, 9, 9,10,11,11,12,12, + 12,13,13,13,14,14,14,15,16,15,15, 9,10,10,11,12, + 12,13,13,13,14,14,16,14,14,16,16,16,16,15, 9,10, + 10,11,11,12,13,13,14,15,14,16,14,15,16,16,16,16, + 15,10,11,11,12,13,13,14,15,15,15,15,15,16,15,16, + 15,16,15,15,10,11,11,13,13,14,13,13,15,14,15,15, + 16,15,15,15,16,15,16,10,12,12,14,14,14,14,14,16, + 16,15,15,15,16,16,16,16,16,16,11,12,12,14,14,14, + 14,15,15,16,15,16,15,16,15,16,16,16,16,12,12,13, + 14,14,15,16,16,16,16,16,16,15,16,16,16,16,16,16, + 12,13,13,14,14,14,14,15,16,15,16,16,16,16,16,16, + 16,16,16,12,13,14,14,14,16,15,16,15,16,16,16,16, + 16,16,16,16,16,16,12,14,13,14,15,15,15,16,15,16, + 16,15,16,16,16,16,16,16,16, +}; + +static float _vq_quantthresh__44u8_p9_1[] = { + -416.5, -367.5, -318.5, -269.5, -220.5, -171.5, -122.5, -73.5, + -24.5, 24.5, 73.5, 122.5, 171.5, 220.5, 269.5, 318.5, + 367.5, 416.5, +}; + +static long _vq_quantmap__44u8_p9_1[] = { + 17, 15, 13, 11, 9, 7, 5, 3, + 1, 0, 2, 4, 6, 8, 10, 12, + 14, 16, 18, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p9_1 = { + _vq_quantthresh__44u8_p9_1, + _vq_quantmap__44u8_p9_1, + 19, + 19 +}; + +static static_codebook _44u8_p9_1 = { + 2, 361, + _vq_lengthlist__44u8_p9_1, + 1, -518287360, 1622704128, 5, 0, + _vq_quantlist__44u8_p9_1, + NULL, + &_vq_auxt__44u8_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u8_p9_2[] = { + 24, + 23, + 25, + 22, + 26, + 21, + 27, + 20, + 28, + 19, + 29, + 18, + 30, + 17, + 31, + 16, + 32, + 15, + 33, + 14, + 34, + 13, + 35, + 12, + 36, + 11, + 37, + 10, + 38, + 9, + 39, + 8, + 40, + 7, + 41, + 6, + 42, + 5, + 43, + 4, + 44, + 3, + 45, + 2, + 46, + 1, + 47, + 0, + 48, +}; + +static long _vq_lengthlist__44u8_p9_2[] = { + 2, 3, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, +}; + +static float _vq_quantthresh__44u8_p9_2[] = { + -23.5, -22.5, -21.5, -20.5, -19.5, -18.5, -17.5, -16.5, + -15.5, -14.5, -13.5, -12.5, -11.5, -10.5, -9.5, -8.5, + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, + 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, + 16.5, 17.5, 18.5, 19.5, 20.5, 21.5, 22.5, 23.5, +}; + +static long _vq_quantmap__44u8_p9_2[] = { + 47, 45, 43, 41, 39, 37, 35, 33, + 31, 29, 27, 25, 23, 21, 19, 17, + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, + 48, +}; + +static encode_aux_threshmatch _vq_auxt__44u8_p9_2 = { + _vq_quantthresh__44u8_p9_2, + _vq_quantmap__44u8_p9_2, + 49, + 49 +}; + +static static_codebook _44u8_p9_2 = { + 1, 49, + _vq_lengthlist__44u8_p9_2, + 1, -526909440, 1611661312, 6, 0, + _vq_quantlist__44u8_p9_2, + NULL, + &_vq_auxt__44u8_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44u9__long[] = { + 3, 9,13,13,14,15,14,14,15,15, 5, 5, 9,10,12,12, + 13,14,16,15,10, 6, 6, 6, 8,11,12,13,16,15,11, 7, + 5, 3, 5, 8,10,12,15,15,10,10, 7, 4, 3, 5, 8,10, + 12,12,12,12, 9, 7, 5, 4, 6, 8,10,13,13,12,11, 9, + 7, 5, 5, 6, 9,12,14,12,12,10, 8, 6, 6, 6, 7,11, + 13,12,14,13,10, 8, 7, 7, 7,10,11,11,12,13,12,11, + 10, 8, 8, 9, +}; + +static static_codebook _huff_book__44u9__long = { + 2, 100, + _huff_lengthlist__44u9__long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _huff_lengthlist__44u9__short[] = { + 9,16,18,18,17,17,17,17,17,17, 5, 8,11,12,11,12, + 17,17,16,16, 6, 6, 8, 8, 9,10,14,15,16,16, 6, 7, + 7, 4, 6, 9,13,16,16,16, 6, 6, 7, 4, 5, 8,11,15, + 17,16, 7, 6, 7, 6, 6, 8, 9,10,14,16,11, 8, 8, 7, + 6, 6, 3, 4,10,15,14,12,12,10, 5, 6, 3, 3, 8,13, + 15,17,15,11, 6, 8, 6, 6, 9,14,17,15,15,12, 8,10, + 9, 9,12,15, +}; + +static static_codebook _huff_book__44u9__short = { + 2, 100, + _huff_lengthlist__44u9__short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u9_p1_0[] = { + 1, 5, 5, 5, 7, 7, 5, 7, 7, 5, 7, 7, 7, 9, 9, 7, + 9, 9, 5, 7, 7, 7, 9, 9, 7, 9, 9, 5, 7, 7, 7, 9, + 9, 7, 9, 9, 8, 9, 9, 9,10,11, 9,11,11, 7, 9, 9, + 9,11,10, 9,11,11, 5, 7, 7, 7, 9, 9, 8, 9,10, 7, + 9, 9, 9,11,11, 9,10,11, 7, 9,10, 9,11,11, 9,11, + 10, +}; + +static float _vq_quantthresh__44u9_p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44u9_p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p1_0 = { + _vq_quantthresh__44u9_p1_0, + _vq_quantmap__44u9_p1_0, + 3, + 3 +}; + +static static_codebook _44u9_p1_0 = { + 4, 81, + _vq_lengthlist__44u9_p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44u9_p1_0, + NULL, + &_vq_auxt__44u9_p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p2_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u9_p2_0[] = { + 3, 5, 5, 8, 8, 5, 7, 7, 9, 9, 6, 7, 7, 9, 9, 8, + 9, 9,11,10, 8, 9, 9,11,11, 6, 7, 7, 9, 9, 7, 8, + 8,10,10, 7, 8, 8, 9,10, 9,10,10,11,11, 9, 9,10, + 11,11, 6, 7, 7, 9, 9, 7, 8, 8,10, 9, 7, 8, 8,10, + 10, 9,10, 9,11,11, 9,10,10,11,11, 8, 9, 9,11,11, + 9,10,10,12,11, 9,10,10,11,12,11,11,11,13,13,11, + 11,11,12,13, 8, 9, 9,11,11, 9,10,10,11,11, 9,10, + 10,12,11,11,12,11,13,12,11,11,12,13,13, 6, 7, 7, + 9, 9, 7, 8, 8,10,10, 7, 8, 8,10,10, 9,10,10,12, + 11, 9,10,10,11,12, 7, 8, 8,10,10, 8, 9, 9,11,11, + 8, 9, 9,10,10,10,11,11,12,12,10,10,11,12,12, 7, + 8, 8,10,10, 8, 9, 8,10,10, 8, 9, 9,10,10,10,11, + 10,12,11,10,10,11,12,12, 9,10,10,11,12,10,11,11, + 12,12,10,11,10,12,12,12,12,12,13,13,11,12,12,13, + 13, 9,10,10,11,11, 9,10,10,12,12,10,11,11,12,13, + 11,12,11,13,12,12,12,12,13,14, 6, 7, 7, 9, 9, 7, + 8, 8,10,10, 7, 8, 8,10,10, 9,10,10,11,11, 9,10, + 10,11,12, 7, 8, 8,10,10, 8, 9, 9,11,10, 8, 8, 9, + 10,10,10,11,10,12,12,10,10,11,11,12, 7, 8, 8,10, + 10, 8, 9, 9,10,10, 8, 9, 9,10,10,10,11,10,12,12, + 10,11,10,12,12, 9,10,10,12,11,10,11,11,12,12, 9, + 10,10,12,12,12,12,12,13,13,11,11,12,12,14, 9,10, + 10,11,12,10,11,11,12,12,10,11,11,12,12,11,12,12, + 14,14,12,12,12,13,13, 8, 9, 9,11,11, 9,10,10,12, + 11, 9,10,10,12,12,11,12,11,13,13,11,11,12,13,13, + 9,10,10,12,12,10,11,11,12,12,10,11,11,12,12,12, + 12,12,14,14,12,12,12,13,13, 9,10,10,12,11,10,11, + 10,12,12,10,11,11,12,12,11,12,12,14,13,12,12,12, + 13,14,11,12,11,13,13,11,12,12,13,13,12,12,12,14, + 14,13,13,13,13,15,13,13,14,15,15,11,11,11,13,13, + 11,12,11,13,13,11,12,12,13,13,12,13,12,15,13,13, + 13,14,14,15, 8, 9, 9,11,11, 9,10,10,11,12, 9,10, + 10,11,12,11,12,11,13,13,11,12,12,13,13, 9,10,10, + 11,12,10,11,10,12,12,10,10,11,12,13,12,12,12,14, + 13,11,12,12,13,14, 9,10,10,12,12,10,11,11,12,12, + 10,11,11,12,12,12,12,12,14,13,12,12,12,14,13,11, + 11,11,13,13,11,12,12,14,13,11,11,12,13,13,13,13, + 13,15,14,12,12,13,13,15,11,12,12,13,13,12,12,12, + 13,14,11,12,12,13,13,13,13,14,14,15,13,13,13,14, + 14, +}; + +static float _vq_quantthresh__44u9_p2_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u9_p2_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p2_0 = { + _vq_quantthresh__44u9_p2_0, + _vq_quantmap__44u9_p2_0, + 5, + 5 +}; + +static static_codebook _44u9_p2_0 = { + 4, 625, + _vq_lengthlist__44u9_p2_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u9_p2_0, + NULL, + &_vq_auxt__44u9_p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p3_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44u9_p3_0[] = { + 3, 4, 4, 5, 5, 7, 7, 8, 8, 4, 5, 5, 6, 6, 7, 7, + 9, 9, 4, 4, 5, 6, 6, 7, 7, 9, 9, 5, 6, 6, 7, 7, + 8, 8, 9, 9, 5, 6, 6, 7, 7, 8, 8, 9, 9, 7, 7, 7, + 8, 8, 9, 9,10,10, 7, 7, 7, 8, 8, 9, 9,10,10, 8, + 9, 9,10, 9,10,10,11,11, 8, 9, 9, 9,10,10,10,11, + 11, +}; + +static float _vq_quantthresh__44u9_p3_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44u9_p3_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p3_0 = { + _vq_quantthresh__44u9_p3_0, + _vq_quantmap__44u9_p3_0, + 9, + 9 +}; + +static static_codebook _44u9_p3_0 = { + 2, 81, + _vq_lengthlist__44u9_p3_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44u9_p3_0, + NULL, + &_vq_auxt__44u9_p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p4_0[] = { + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + 0, + 16, +}; + +static long _vq_lengthlist__44u9_p4_0[] = { + 4, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10,11, + 11, 5, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10, + 11,11, 5, 5, 5, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9,10, + 10,11,11, 6, 6, 6, 7, 6, 7, 7, 8, 8, 9, 9,10,10, + 11,11,12,11, 6, 6, 6, 6, 7, 7, 7, 8, 8, 9, 9,10, + 10,11,11,11,12, 7, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9, + 10,10,11,11,12,12, 7, 7, 7, 7, 7, 8, 8, 9, 9, 9, + 9,10,10,11,11,12,12, 8, 8, 8, 8, 8, 9, 8,10, 9, + 10,10,11,10,12,11,13,12, 8, 8, 8, 8, 8, 9, 9, 9, + 10,10,10,10,11,11,12,12,12, 8, 8, 8, 9, 9, 9, 9, + 10,10,11,10,12,11,12,12,13,12, 8, 8, 8, 9, 9, 9, + 9,10,10,10,11,11,11,12,12,12,13, 9, 9, 9,10,10, + 10,10,11,10,11,11,12,11,13,12,13,13, 9, 9,10,10, + 10,10,10,10,11,11,11,11,12,12,13,13,13,10,11,10, + 11,11,11,11,12,11,12,12,13,12,13,13,14,13,10,10, + 10,11,11,11,11,11,12,12,12,12,13,13,13,13,14,11, + 11,11,12,11,12,12,12,12,13,13,13,13,14,13,14,14, + 11,11,11,11,12,12,12,12,12,12,13,13,13,13,14,14, + 14, +}; + +static float _vq_quantthresh__44u9_p4_0[] = { + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, +}; + +static long _vq_quantmap__44u9_p4_0[] = { + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p4_0 = { + _vq_quantthresh__44u9_p4_0, + _vq_quantmap__44u9_p4_0, + 17, + 17 +}; + +static static_codebook _44u9_p4_0 = { + 2, 289, + _vq_lengthlist__44u9_p4_0, + 1, -529530880, 1611661312, 5, 0, + _vq_quantlist__44u9_p4_0, + NULL, + &_vq_auxt__44u9_p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p5_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44u9_p5_0[] = { + 1, 4, 4, 5, 7, 7, 5, 7, 7, 5, 8, 8, 8, 9, 9, 7, + 9, 9, 5, 8, 8, 7, 9, 9, 8, 9, 9, 5, 8, 8, 8,10, + 10, 8,10,10, 7,10,10, 9,10,12, 9,11,11, 7,10,10, + 9,11,10, 9,11,12, 5, 8, 8, 8,10,10, 8,10,10, 7, + 10,10, 9,12,11, 9,10,11, 7,10,10, 9,11,11,10,12, + 10, +}; + +static float _vq_quantthresh__44u9_p5_0[] = { + -5.5, 5.5, +}; + +static long _vq_quantmap__44u9_p5_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p5_0 = { + _vq_quantthresh__44u9_p5_0, + _vq_quantmap__44u9_p5_0, + 3, + 3 +}; + +static static_codebook _44u9_p5_0 = { + 4, 81, + _vq_lengthlist__44u9_p5_0, + 1, -529137664, 1618345984, 2, 0, + _vq_quantlist__44u9_p5_0, + NULL, + &_vq_auxt__44u9_p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p5_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u9_p5_1[] = { + 5, 5, 5, 6, 6, 7, 7, 7, 7, 7, 7, 5, 6, 6, 6, 6, + 7, 7, 7, 7, 8, 7, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, + 7, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 6, 6, 6, 7, + 7, 7, 7, 7, 7, 8, 8, 7, 7, 7, 7, 7, 8, 7, 8, 8, + 8, 8, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 7, 7, 7, + 8, 7, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 7, 8, 7, 8, 8, 8, 8, 8, 8, 8, 8, 7, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44u9_p5_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44u9_p5_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p5_1 = { + _vq_quantthresh__44u9_p5_1, + _vq_quantmap__44u9_p5_1, + 11, + 11 +}; + +static static_codebook _44u9_p5_1 = { + 2, 121, + _vq_lengthlist__44u9_p5_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44u9_p5_1, + NULL, + &_vq_auxt__44u9_p5_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u9_p6_0[] = { + 2, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9,10,10, 4, 6, 5, + 7, 7, 8, 8, 8, 8, 9, 9,10,10, 4, 5, 6, 7, 7, 8, + 8, 8, 8, 9, 9,10,10, 6, 7, 7, 8, 8, 8, 8, 9, 9, + 10,10,10,10, 6, 7, 7, 8, 8, 8, 8, 9, 9,10,10,10, + 10, 7, 8, 8, 8, 8, 9, 9, 9, 9,10,10,11,11, 7, 8, + 8, 8, 8, 9, 9, 9, 9,10,10,11,11, 8, 8, 8, 9, 9, + 9, 9, 9,10,10,10,11,11, 8, 8, 8, 9, 9, 9, 9,10, + 9,10,10,11,11, 9, 9, 9,10,10,10,10,10,11,11,11, + 11,12, 9, 9, 9,10,10,10,10,10,10,11,10,12,11,10, + 10,10,10,10,11,11,11,11,11,12,12,12,10,10,10,10, + 10,11,11,11,11,12,11,12,12, +}; + +static float _vq_quantthresh__44u9_p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44u9_p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p6_0 = { + _vq_quantthresh__44u9_p6_0, + _vq_quantmap__44u9_p6_0, + 13, + 13 +}; + +static static_codebook _44u9_p6_0 = { + 2, 169, + _vq_lengthlist__44u9_p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44u9_p6_0, + NULL, + &_vq_auxt__44u9_p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44u9_p6_1[] = { + 4, 4, 4, 5, 5, 4, 5, 4, 5, 5, 4, 4, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, +}; + +static float _vq_quantthresh__44u9_p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44u9_p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p6_1 = { + _vq_quantthresh__44u9_p6_1, + _vq_quantmap__44u9_p6_1, + 5, + 5 +}; + +static static_codebook _44u9_p6_1 = { + 2, 25, + _vq_lengthlist__44u9_p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44u9_p6_1, + NULL, + &_vq_auxt__44u9_p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p7_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44u9_p7_0[] = { + 1, 4, 5, 6, 6, 7, 7, 8, 9,10,10,11,11, 5, 6, 6, + 7, 7, 8, 8, 9, 9,10,10,11,11, 5, 6, 6, 7, 7, 8, + 8, 9, 9,10,10,11,11, 6, 7, 7, 8, 8, 9, 9,10,10, + 11,11,12,12, 6, 7, 7, 8, 8, 9, 9,10,10,11,11,12, + 12, 8, 8, 8, 9, 9,10,10,11,11,12,12,13,13, 8, 8, + 8, 9, 9,10,10,11,11,12,12,13,13, 9, 9, 9,10,10, + 11,11,12,12,13,13,13,13, 9, 9, 9,10,10,11,11,12, + 12,13,13,14,14,10,10,10,11,11,12,12,13,13,14,13, + 15,14,10,10,10,11,11,12,12,13,13,14,14,14,14,11, + 11,12,12,12,13,13,14,14,14,14,15,15,11,11,12,12, + 12,13,13,14,14,14,15,15,15, +}; + +static float _vq_quantthresh__44u9_p7_0[] = { + -60.5, -49.5, -38.5, -27.5, -16.5, -5.5, 5.5, 16.5, + 27.5, 38.5, 49.5, 60.5, +}; + +static long _vq_quantmap__44u9_p7_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p7_0 = { + _vq_quantthresh__44u9_p7_0, + _vq_quantmap__44u9_p7_0, + 13, + 13 +}; + +static static_codebook _44u9_p7_0 = { + 2, 169, + _vq_lengthlist__44u9_p7_0, + 1, -523206656, 1618345984, 4, 0, + _vq_quantlist__44u9_p7_0, + NULL, + &_vq_auxt__44u9_p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p7_1[] = { + 5, + 4, + 6, + 3, + 7, + 2, + 8, + 1, + 9, + 0, + 10, +}; + +static long _vq_lengthlist__44u9_p7_1[] = { + 5, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 7, 7, + 7, 7, 7, 7, 7, 7, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 8, 8, 7, 7, 7, 7, 7, 7, 7, 8, 7, 8, 8, 7, 7, + 7, 7, 7, 7, 7, 8, 8, 8, 8, +}; + +static float _vq_quantthresh__44u9_p7_1[] = { + -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, + 3.5, 4.5, +}; + +static long _vq_quantmap__44u9_p7_1[] = { + 9, 7, 5, 3, 1, 0, 2, 4, + 6, 8, 10, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p7_1 = { + _vq_quantthresh__44u9_p7_1, + _vq_quantmap__44u9_p7_1, + 11, + 11 +}; + +static static_codebook _44u9_p7_1 = { + 2, 121, + _vq_lengthlist__44u9_p7_1, + 1, -531365888, 1611661312, 4, 0, + _vq_quantlist__44u9_p7_1, + NULL, + &_vq_auxt__44u9_p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p8_0[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44u9_p8_0[] = { + 1, 4, 4, 7, 7, 8, 8, 8, 8, 9, 9,10, 9,11,10, 4, + 6, 6, 8, 8, 9, 9, 9, 9,10,10,11,10,12,10, 4, 6, + 6, 8, 8, 9,10, 9, 9,10,10,11,11,12,12, 7, 8, 8, + 10,10,11,11,10,10,11,11,12,12,13,12, 7, 8, 8,10, + 10,11,11,10,10,11,11,12,12,12,13, 8,10, 9,11,11, + 12,12,11,11,12,12,13,13,14,13, 8, 9, 9,11,11,12, + 12,11,12,12,12,13,13,14,13, 8, 9, 9,10,10,12,11, + 13,12,13,13,14,13,15,14, 8, 9, 9,10,10,11,12,12, + 12,13,13,13,14,14,14, 9,10,10,12,11,13,12,13,13, + 14,13,14,14,14,15, 9,10,10,11,12,12,12,13,13,14, + 14,14,15,15,15,10,11,11,12,12,13,13,14,14,14,14, + 15,14,16,15,10,11,11,12,12,13,13,13,14,14,14,14, + 14,15,16,11,12,12,13,13,14,13,14,14,15,14,15,16, + 16,16,11,12,12,13,13,14,13,14,14,15,15,15,16,15, + 15, +}; + +static float _vq_quantthresh__44u9_p8_0[] = { + -136.5, -115.5, -94.5, -73.5, -52.5, -31.5, -10.5, 10.5, + 31.5, 52.5, 73.5, 94.5, 115.5, 136.5, +}; + +static long _vq_quantmap__44u9_p8_0[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p8_0 = { + _vq_quantthresh__44u9_p8_0, + _vq_quantmap__44u9_p8_0, + 15, + 15 +}; + +static static_codebook _44u9_p8_0 = { + 2, 225, + _vq_lengthlist__44u9_p8_0, + 1, -520986624, 1620377600, 4, 0, + _vq_quantlist__44u9_p8_0, + NULL, + &_vq_auxt__44u9_p8_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p8_1[] = { + 10, + 9, + 11, + 8, + 12, + 7, + 13, + 6, + 14, + 5, + 15, + 4, + 16, + 3, + 17, + 2, + 18, + 1, + 19, + 0, + 20, +}; + +static long _vq_lengthlist__44u9_p8_1[] = { + 4, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 6, 6, 6, 7, 7, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 6, 6, 6, 7, 7, 8, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 7, + 7, 7, 8, 8, 8, 8, 9, 8, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9,10, 9,10,10,10, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9,10,10, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 10, 9,10, 9,10,10,10,10, 8, 8, 8, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9,10,10, 9,10,10,10,10,10, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9,10, 9,10,10,10,10,10,10, + 10,10, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,10,10,10, + 10,10,10,10,10,10,10, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9,10,10,10,10,10,10,10,10,10, + 10, 9, 9, 9, 9, 9, 9, 9,10, 9,10,10,10,10,10,10, + 10,10,10,10,10,10, 9, 9, 9, 9, 9, 9, 9, 9,10,10, + 10,10,10,10,10,10,10,10,10,10,10, 9, 9, 9, 9, 9, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 9, 9, 9, 9,10, 9, 9,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10, 9, 9, 9,10, 9,10, 9,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10, 9, 9, 9,10, 9,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, 9, + 9, 9, 9, 9,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10, 9, 9, 9,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10, +}; + +static float _vq_quantthresh__44u9_p8_1[] = { + -9.5, -8.5, -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, + -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, + 6.5, 7.5, 8.5, 9.5, +}; + +static long _vq_quantmap__44u9_p8_1[] = { + 19, 17, 15, 13, 11, 9, 7, 5, + 3, 1, 0, 2, 4, 6, 8, 10, + 12, 14, 16, 18, 20, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p8_1 = { + _vq_quantthresh__44u9_p8_1, + _vq_quantmap__44u9_p8_1, + 21, + 21 +}; + +static static_codebook _44u9_p8_1 = { + 2, 441, + _vq_lengthlist__44u9_p8_1, + 1, -529268736, 1611661312, 5, 0, + _vq_quantlist__44u9_p8_1, + NULL, + &_vq_auxt__44u9_p8_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p9_0[] = { + 7, + 6, + 8, + 5, + 9, + 4, + 10, + 3, + 11, + 2, + 12, + 1, + 13, + 0, + 14, +}; + +static long _vq_lengthlist__44u9_p9_0[] = { + 1, 3, 3,11,11,11,11,11,11,11,11,11,11,11,11, 4, + 10,11,11,11,11,11,11,11,11,11,11,11,11,11, 4,10, + 10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10, +}; + +static float _vq_quantthresh__44u9_p9_0[] = { + -6051.5, -5120.5, -4189.5, -3258.5, -2327.5, -1396.5, -465.5, 465.5, + 1396.5, 2327.5, 3258.5, 4189.5, 5120.5, 6051.5, +}; + +static long _vq_quantmap__44u9_p9_0[] = { + 13, 11, 9, 7, 5, 3, 1, 0, + 2, 4, 6, 8, 10, 12, 14, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p9_0 = { + _vq_quantthresh__44u9_p9_0, + _vq_quantmap__44u9_p9_0, + 15, + 15 +}; + +static static_codebook _44u9_p9_0 = { + 2, 225, + _vq_lengthlist__44u9_p9_0, + 1, -510036736, 1631393792, 4, 0, + _vq_quantlist__44u9_p9_0, + NULL, + &_vq_auxt__44u9_p9_0, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p9_1[] = { + 9, + 8, + 10, + 7, + 11, + 6, + 12, + 5, + 13, + 4, + 14, + 3, + 15, + 2, + 16, + 1, + 17, + 0, + 18, +}; + +static long _vq_lengthlist__44u9_p9_1[] = { + 1, 4, 4, 7, 7, 8, 7, 8, 7, 9, 8,10, 9,10,10,11, + 11,12,12, 4, 7, 6, 9, 9,10, 9, 9, 8,10,10,11,10, + 12,10,13,12,13,12, 4, 6, 6, 9, 9, 9, 9, 9, 9,10, + 10,11,11,11,12,12,12,12,12, 7, 9, 8,11,10,10,10, + 11,10,11,11,12,12,13,12,13,13,13,13, 7, 8, 9,10, + 10,11,11,10,10,11,11,11,12,13,13,13,13,14,14, 8, + 9, 9,11,11,12,11,12,12,13,12,12,13,13,14,15,14, + 14,14, 8, 9, 9,10,11,11,11,12,12,13,12,13,13,14, + 14,14,15,14,16, 8, 9, 9,11,10,12,12,12,12,15,13, + 13,13,17,14,15,15,15,14, 8, 9, 9,10,11,11,12,13, + 12,13,13,13,14,15,14,14,14,16,15, 9,11,10,12,12, + 13,13,13,13,14,14,16,15,14,14,14,15,15,17, 9,10, + 10,11,11,13,13,13,14,14,13,15,14,15,14,15,16,15, + 16,10,11,11,12,12,13,14,15,14,15,14,14,15,17,16, + 15,15,17,17,10,12,11,13,12,14,14,13,14,15,15,15, + 15,16,17,17,15,17,16,11,12,12,14,13,15,14,15,16, + 17,15,17,15,17,15,15,16,17,15,11,11,12,14,14,14, + 14,14,15,15,16,15,17,17,17,16,17,16,15,12,12,13, + 14,14,14,15,14,15,15,16,16,17,16,17,15,17,17,16, + 12,14,12,14,14,15,15,15,14,14,16,16,16,15,16,16, + 15,17,15,12,13,13,14,15,14,15,17,15,17,16,17,17, + 17,16,17,16,17,17,12,13,13,14,16,15,15,15,16,15, + 17,17,15,17,15,17,16,16,17, +}; + +static float _vq_quantthresh__44u9_p9_1[] = { + -416.5, -367.5, -318.5, -269.5, -220.5, -171.5, -122.5, -73.5, + -24.5, 24.5, 73.5, 122.5, 171.5, 220.5, 269.5, 318.5, + 367.5, 416.5, +}; + +static long _vq_quantmap__44u9_p9_1[] = { + 17, 15, 13, 11, 9, 7, 5, 3, + 1, 0, 2, 4, 6, 8, 10, 12, + 14, 16, 18, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p9_1 = { + _vq_quantthresh__44u9_p9_1, + _vq_quantmap__44u9_p9_1, + 19, + 19 +}; + +static static_codebook _44u9_p9_1 = { + 2, 361, + _vq_lengthlist__44u9_p9_1, + 1, -518287360, 1622704128, 5, 0, + _vq_quantlist__44u9_p9_1, + NULL, + &_vq_auxt__44u9_p9_1, + NULL, + 0 +}; + +static long _vq_quantlist__44u9_p9_2[] = { + 24, + 23, + 25, + 22, + 26, + 21, + 27, + 20, + 28, + 19, + 29, + 18, + 30, + 17, + 31, + 16, + 32, + 15, + 33, + 14, + 34, + 13, + 35, + 12, + 36, + 11, + 37, + 10, + 38, + 9, + 39, + 8, + 40, + 7, + 41, + 6, + 42, + 5, + 43, + 4, + 44, + 3, + 45, + 2, + 46, + 1, + 47, + 0, + 48, +}; + +static long _vq_lengthlist__44u9_p9_2[] = { + 2, 4, 4, 5, 4, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 7, 6, 7, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, +}; + +static float _vq_quantthresh__44u9_p9_2[] = { + -23.5, -22.5, -21.5, -20.5, -19.5, -18.5, -17.5, -16.5, + -15.5, -14.5, -13.5, -12.5, -11.5, -10.5, -9.5, -8.5, + -7.5, -6.5, -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, + 0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, + 8.5, 9.5, 10.5, 11.5, 12.5, 13.5, 14.5, 15.5, + 16.5, 17.5, 18.5, 19.5, 20.5, 21.5, 22.5, 23.5, +}; + +static long _vq_quantmap__44u9_p9_2[] = { + 47, 45, 43, 41, 39, 37, 35, 33, + 31, 29, 27, 25, 23, 21, 19, 17, + 15, 13, 11, 9, 7, 5, 3, 1, + 0, 2, 4, 6, 8, 10, 12, 14, + 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, + 48, +}; + +static encode_aux_threshmatch _vq_auxt__44u9_p9_2 = { + _vq_quantthresh__44u9_p9_2, + _vq_quantmap__44u9_p9_2, + 49, + 49 +}; + +static static_codebook _44u9_p9_2 = { + 1, 49, + _vq_lengthlist__44u9_p9_2, + 1, -526909440, 1611661312, 6, 0, + _vq_quantlist__44u9_p9_2, + NULL, + &_vq_auxt__44u9_p9_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44un1__long[] = { + 5, 6,12, 9,14, 9, 9,19, 6, 1, 5, 5, 8, 7, 9,19, + 12, 4, 4, 7, 7, 9,11,18, 9, 5, 6, 6, 8, 7, 8,17, + 14, 8, 7, 8, 8,10,12,18, 9, 6, 8, 6, 8, 6, 8,18, + 9, 8,11, 8,11, 7, 5,15,16,18,18,18,17,15,11,18, +}; + +static static_codebook _huff_book__44un1__long = { + 2, 64, + _huff_lengthlist__44un1__long, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + +static long _vq_quantlist__44un1__p1_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44un1__p1_0[] = { + 1, 4, 4, 5, 8, 7, 5, 7, 8, 5, 8, 8, 8,10,11, 8, + 10,11, 5, 8, 8, 8,11,10, 8,11,10, 4, 9, 9, 8,11, + 11, 8,11,11, 8,12,11,10,12,14,11,13,13, 7,11,11, + 10,13,11,11,13,14, 4, 8, 9, 8,11,11, 8,11,12, 7, + 11,11,11,14,13,10,11,13, 8,11,12,11,13,13,10,14, + 12, +}; + +static float _vq_quantthresh__44un1__p1_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44un1__p1_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44un1__p1_0 = { + _vq_quantthresh__44un1__p1_0, + _vq_quantmap__44un1__p1_0, + 3, + 3 +}; + +static static_codebook _44un1__p1_0 = { + 4, 81, + _vq_lengthlist__44un1__p1_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44un1__p1_0, + NULL, + &_vq_auxt__44un1__p1_0, + NULL, + 0 +}; + +static long _vq_quantlist__44un1__p2_0[] = { + 1, + 0, + 2, +}; + +static long _vq_lengthlist__44un1__p2_0[] = { + 2, 4, 4, 5, 6, 6, 5, 6, 6, 5, 7, 7, 7, 8, 8, 6, + 7, 9, 5, 7, 7, 6, 8, 7, 7, 9, 8, 4, 7, 7, 7, 9, + 8, 7, 8, 8, 7, 9, 8, 8, 8,10, 9,10,10, 6, 8, 8, + 7,10, 8, 9,10,10, 5, 7, 7, 7, 8, 8, 7, 8, 9, 6, + 8, 8, 9,10,10, 7, 8,10, 6, 8, 9, 9,10,10, 8,10, + 8, +}; + +static float _vq_quantthresh__44un1__p2_0[] = { + -0.5, 0.5, +}; + +static long _vq_quantmap__44un1__p2_0[] = { + 1, 0, 2, +}; + +static encode_aux_threshmatch _vq_auxt__44un1__p2_0 = { + _vq_quantthresh__44un1__p2_0, + _vq_quantmap__44un1__p2_0, + 3, + 3 +}; + +static static_codebook _44un1__p2_0 = { + 4, 81, + _vq_lengthlist__44un1__p2_0, + 1, -535822336, 1611661312, 2, 0, + _vq_quantlist__44un1__p2_0, + NULL, + &_vq_auxt__44un1__p2_0, + NULL, + 0 +}; + +static long _vq_quantlist__44un1__p3_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44un1__p3_0[] = { + 1, 5, 5, 8, 8, 5, 8, 7, 9, 9, 5, 7, 8, 9, 9, 9, + 10, 9,12,12, 9, 9,10,11,12, 6, 8, 8,10,10, 8,10, + 10,11,11, 8, 9,10,11,11,10,11,11,13,13,10,11,11, + 12,13, 6, 8, 8,10,10, 8,10, 9,11,11, 8,10,10,11, + 11,10,11,11,13,12,10,11,11,13,12, 9,11,11,15,13, + 10,12,11,15,13,10,11,11,15,14,12,14,13,16,15,12, + 13,13,17,16, 9,11,11,13,15,10,11,12,14,15,10,11, + 12,14,15,12,13,13,15,16,12,13,13,16,16, 5, 8, 8, + 11,11, 8,10,10,12,12, 8,10,10,12,12,11,12,12,14, + 14,11,12,12,14,14, 8,11,10,13,12,10,11,12,12,13, + 10,12,12,13,13,12,12,13,13,15,11,12,13,15,14, 7, + 10,10,12,12, 9,12,11,13,12,10,12,12,13,14,12,13, + 12,15,13,11,13,12,14,15,10,12,12,16,14,11,12,12, + 16,15,11,13,12,17,16,13,13,15,15,17,13,15,15,20, + 17,10,12,12,14,16,11,12,12,15,15,11,13,13,15,18, + 13,14,13,15,15,13,15,14,16,16, 5, 8, 8,11,11, 8, + 10,10,12,12, 8,10,10,12,12,11,12,12,14,14,11,12, + 12,14,15, 7,10,10,13,12,10,12,12,14,13, 9,10,12, + 12,13,11,13,13,15,15,11,12,13,13,15, 8,10,10,12, + 13,10,12,12,13,13,10,12,11,13,13,11,13,12,15,15, + 12,13,12,15,13,10,12,12,16,14,11,12,12,16,15,10, + 12,12,16,14,14,15,14,18,16,13,13,14,15,16,10,12, + 12,14,16,11,13,13,16,16,11,13,12,14,16,13,15,15, + 18,18,13,15,13,16,14, 8,11,11,16,16,10,13,13,17, + 16,10,12,12,16,15,14,16,15,20,17,13,14,14,17,17, + 9,12,12,16,16,11,13,14,16,17,11,13,13,16,16,15, + 15,19,18, 0,14,15,15,18,18, 9,12,12,17,16,11,13, + 12,17,16,11,12,13,15,17,15,16,15, 0,19,14,15,14, + 19,18,12,14,14, 0,16,13,14,14,19,18,13,15,16,17, + 16,15,15,17,18, 0,14,16,16,19, 0,12,14,14,16,18, + 13,15,13,17,18,13,15,14,17,18,15,18,14,18,18,16, + 17,16, 0,17, 8,11,11,15,15,10,12,12,16,16,10,13, + 13,16,16,13,15,14,17,17,14,15,17,17,18, 9,12,12, + 16,15,11,13,13,16,16,11,12,13,17,17,14,14,15,17, + 17,14,15,16, 0,18, 9,12,12,16,17,11,13,13,16,17, + 11,14,13,18,17,14,16,14,17,17,15,17,17,18,18,12, + 14,14, 0,16,13,15,15,19, 0,12,13,15, 0, 0,14,17, + 16,19, 0,16,15,18,18, 0,12,14,14,17, 0,13,14,14, + 17, 0,13,15,14, 0,18,15,16,16, 0,18,15,18,15, 0, + 17, +}; + +static float _vq_quantthresh__44un1__p3_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44un1__p3_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44un1__p3_0 = { + _vq_quantthresh__44un1__p3_0, + _vq_quantmap__44un1__p3_0, + 5, + 5 +}; + +static static_codebook _44un1__p3_0 = { + 4, 625, + _vq_lengthlist__44un1__p3_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44un1__p3_0, + NULL, + &_vq_auxt__44un1__p3_0, + NULL, + 0 +}; + +static long _vq_quantlist__44un1__p4_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44un1__p4_0[] = { + 3, 5, 5, 9, 9, 5, 6, 6,10, 9, 5, 6, 6, 9,10,10, + 10,10,12,11, 9,10,10,12,12, 5, 7, 7,10,10, 7, 7, + 8,10,11, 7, 7, 8,10,11,10,10,11,11,13,10,10,11, + 11,13, 6, 7, 7,10,10, 7, 8, 7,11,10, 7, 8, 7,10, + 10,10,11, 9,13,11,10,11,10,13,11,10,10,10,14,13, + 10,11,11,14,13,10,10,11,13,14,12,12,13,15,15,12, + 12,13,13,14,10,10,10,12,13,10,11,10,13,13,10,11, + 11,13,13,12,13,12,14,13,12,13,13,14,13, 5, 7, 7, + 10,10, 7, 8, 8,11,10, 7, 8, 8,10,10,11,11,11,13, + 13,10,11,11,12,12, 7, 8, 8,11,11, 7, 8, 9,10,12, + 8, 9, 9,11,11,11,10,12,11,14,11,11,12,13,13, 6, + 8, 8,10,11, 7, 9, 7,12,10, 8, 9,10,11,12,10,12, + 10,14,11,11,12,11,13,13,10,11,11,14,14,10,10,11, + 13,14,11,12,12,15,13,12,11,14,12,16,12,13,14,15, + 16,10,10,11,13,14,10,11,10,14,12,11,12,12,13,14, + 12,13,11,15,12,14,14,14,15,15, 5, 7, 7,10,10, 7, + 8, 8,10,10, 7, 8, 8,10,11,10,11,10,12,12,10,11, + 11,12,13, 6, 8, 8,11,11, 8, 9, 9,12,11, 7, 7, 9, + 10,12,11,11,11,12,13,11,10,12,11,15, 7, 8, 8,11, + 11, 8, 9, 9,11,11, 7, 9, 8,12,10,11,12,11,13,12, + 11,12,10,15,11,10,11,10,14,12,11,12,11,14,13,10, + 10,11,13,14,13,13,13,17,15,12,11,14,12,15,10,10, + 11,13,14,11,12,12,14,14,10,11,10,14,13,13,14,13, + 16,17,12,14,11,16,12, 9,10,10,14,13,10,11,10,14, + 14,10,11,11,13,13,13,14,14,16,15,12,13,13,14,14, + 9,11,10,14,13,10,10,12,13,14,11,12,11,14,13,13, + 14,14,14,15,13,14,14,15,15, 9,10,11,13,14,10,11, + 10,15,13,11,11,12,12,15,13,14,12,15,14,13,13,14, + 14,15,12,13,12,16,14,11,11,12,15,14,13,15,13,16, + 14,13,12,15,12,17,15,16,15,16,16,12,12,13,13,15, + 11,13,11,15,14,13,13,14,15,17,13,14,12, 0,13,14, + 15,14,15, 0, 9,10,10,13,13,10,11,11,13,13,10,11, + 11,13,13,12,13,12,14,14,13,14,14,15,17, 9,10,10, + 13,13,11,12,11,15,12,10,10,11,13,16,13,14,13,15, + 14,13,13,14,15,16,10,10,11,13,14,11,11,12,13,14, + 10,12,11,14,14,13,13,13,14,15,13,15,13,16,15,12, + 13,12,15,13,12,15,13,15,15,11,11,13,14,15,15,15, + 15,15,17,13,12,14,13,17,12,12,14,14,15,13,13,14, + 14,16,11,13,11,16,15,14,16,16,17, 0,14,13,11,16, + 12, +}; + +static float _vq_quantthresh__44un1__p4_0[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44un1__p4_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44un1__p4_0 = { + _vq_quantthresh__44un1__p4_0, + _vq_quantmap__44un1__p4_0, + 5, + 5 +}; + +static static_codebook _44un1__p4_0 = { + 4, 625, + _vq_lengthlist__44un1__p4_0, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44un1__p4_0, + NULL, + &_vq_auxt__44un1__p4_0, + NULL, + 0 +}; + +static long _vq_quantlist__44un1__p5_0[] = { + 4, + 3, + 5, + 2, + 6, + 1, + 7, + 0, + 8, +}; + +static long _vq_lengthlist__44un1__p5_0[] = { + 1, 4, 4, 7, 7, 8, 8, 9, 9, 4, 6, 5, 8, 7, 8, 8, + 10, 9, 4, 6, 6, 8, 8, 8, 8,10,10, 7, 8, 7, 9, 9, + 9, 9,11,10, 7, 8, 8, 9, 9, 9, 9,10,11, 8, 8, 8, + 9, 9,10,10,11,11, 8, 8, 8, 9, 9,10,10,11,11, 9, + 10,10,11,10,11,11,12,12, 9,10,10,10,11,11,11,12, + 12, +}; + +static float _vq_quantthresh__44un1__p5_0[] = { + -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, 2.5, 3.5, +}; + +static long _vq_quantmap__44un1__p5_0[] = { + 7, 5, 3, 1, 0, 2, 4, 6, + 8, +}; + +static encode_aux_threshmatch _vq_auxt__44un1__p5_0 = { + _vq_quantthresh__44un1__p5_0, + _vq_quantmap__44un1__p5_0, + 9, + 9 +}; + +static static_codebook _44un1__p5_0 = { + 2, 81, + _vq_lengthlist__44un1__p5_0, + 1, -531628032, 1611661312, 4, 0, + _vq_quantlist__44un1__p5_0, + NULL, + &_vq_auxt__44un1__p5_0, + NULL, + 0 +}; + +static long _vq_quantlist__44un1__p6_0[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44un1__p6_0[] = { + 1, 4, 4, 6, 6, 8, 8,10,10,11,11,15,15, 4, 5, 5, + 8, 8, 9, 9,11,11,12,12,16,16, 4, 5, 6, 8, 8, 9, + 9,11,11,12,12,14,14, 7, 8, 8, 9, 9,10,10,11,12, + 13,13,16,17, 7, 8, 8, 9, 9,10,10,12,12,12,13,15, + 15, 9,10,10,10,10,11,11,12,12,13,13,15,16, 9, 9, + 9,10,10,11,11,13,12,13,13,17,17,10,11,11,11,12, + 12,12,13,13,14,15, 0,18,10,11,11,12,12,12,13,14, + 13,14,14,17,16,11,12,12,13,13,14,14,14,14,15,16, + 17,16,11,12,12,13,13,14,14,14,14,15,15,17,17,14, + 15,15,16,16,16,17,17,16, 0,17, 0,18,14,15,15,16, + 16, 0,15,18,18, 0,16, 0, 0, +}; + +static float _vq_quantthresh__44un1__p6_0[] = { + -27.5, -22.5, -17.5, -12.5, -7.5, -2.5, 2.5, 7.5, + 12.5, 17.5, 22.5, 27.5, +}; + +static long _vq_quantmap__44un1__p6_0[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44un1__p6_0 = { + _vq_quantthresh__44un1__p6_0, + _vq_quantmap__44un1__p6_0, + 13, + 13 +}; + +static static_codebook _44un1__p6_0 = { + 2, 169, + _vq_lengthlist__44un1__p6_0, + 1, -526516224, 1616117760, 4, 0, + _vq_quantlist__44un1__p6_0, + NULL, + &_vq_auxt__44un1__p6_0, + NULL, + 0 +}; + +static long _vq_quantlist__44un1__p6_1[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44un1__p6_1[] = { + 2, 4, 4, 5, 5, 4, 5, 5, 5, 5, 4, 5, 5, 6, 5, 5, + 6, 5, 6, 6, 5, 6, 6, 6, 6, +}; + +static float _vq_quantthresh__44un1__p6_1[] = { + -1.5, -0.5, 0.5, 1.5, +}; + +static long _vq_quantmap__44un1__p6_1[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44un1__p6_1 = { + _vq_quantthresh__44un1__p6_1, + _vq_quantmap__44un1__p6_1, + 5, + 5 +}; + +static static_codebook _44un1__p6_1 = { + 2, 25, + _vq_lengthlist__44un1__p6_1, + 1, -533725184, 1611661312, 3, 0, + _vq_quantlist__44un1__p6_1, + NULL, + &_vq_auxt__44un1__p6_1, + NULL, + 0 +}; + +static long _vq_quantlist__44un1__p7_0[] = { + 2, + 1, + 3, + 0, + 4, +}; + +static long _vq_lengthlist__44un1__p7_0[] = { + 1, 5, 3,11,11,11,11,11,11,11, 8,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,10,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,10,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11, 8,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,10, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11, 7,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,10,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10, + 10, +}; + +static float _vq_quantthresh__44un1__p7_0[] = { + -253.5, -84.5, 84.5, 253.5, +}; + +static long _vq_quantmap__44un1__p7_0[] = { + 3, 1, 0, 2, 4, +}; + +static encode_aux_threshmatch _vq_auxt__44un1__p7_0 = { + _vq_quantthresh__44un1__p7_0, + _vq_quantmap__44un1__p7_0, + 5, + 5 +}; + +static static_codebook _44un1__p7_0 = { + 4, 625, + _vq_lengthlist__44un1__p7_0, + 1, -518709248, 1626677248, 3, 0, + _vq_quantlist__44un1__p7_0, + NULL, + &_vq_auxt__44un1__p7_0, + NULL, + 0 +}; + +static long _vq_quantlist__44un1__p7_1[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44un1__p7_1[] = { + 1, 4, 4, 6, 6, 6, 6, 9, 8, 9, 8, 8, 8, 5, 7, 7, + 7, 7, 8, 8, 8,10, 8,10, 8, 9, 5, 7, 7, 8, 7, 7, + 8,10,10,11,10,12,11, 7, 8, 8, 9, 9, 9,10,11,11, + 11,11,11,11, 7, 8, 8, 8, 9, 9, 9,10,10,10,11,11, + 12, 7, 8, 8, 9, 9,10,11,11,12,11,12,11,11, 7, 8, + 8, 9, 9,10,10,11,11,11,12,12,11, 8,10,10,10,10, + 11,11,14,11,12,12,12,13, 9,10,10,10,10,12,11,14, + 11,14,11,12,13,10,11,11,11,11,13,11,14,14,13,13, + 13,14,11,11,11,12,11,12,12,12,13,14,14,13,14,12, + 11,12,12,12,12,13,13,13,14,13,14,14,11,12,12,14, + 12,13,13,12,13,13,14,14,14, +}; + +static float _vq_quantthresh__44un1__p7_1[] = { + -71.5, -58.5, -45.5, -32.5, -19.5, -6.5, 6.5, 19.5, + 32.5, 45.5, 58.5, 71.5, +}; + +static long _vq_quantmap__44un1__p7_1[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44un1__p7_1 = { + _vq_quantthresh__44un1__p7_1, + _vq_quantmap__44un1__p7_1, + 13, + 13 +}; + +static static_codebook _44un1__p7_1 = { + 2, 169, + _vq_lengthlist__44un1__p7_1, + 1, -523010048, 1618608128, 4, 0, + _vq_quantlist__44un1__p7_1, + NULL, + &_vq_auxt__44un1__p7_1, + NULL, + 0 +}; + +static long _vq_quantlist__44un1__p7_2[] = { + 6, + 5, + 7, + 4, + 8, + 3, + 9, + 2, + 10, + 1, + 11, + 0, + 12, +}; + +static long _vq_lengthlist__44un1__p7_2[] = { + 3, 4, 4, 6, 6, 7, 7, 8, 8, 9, 9, 9, 8, 4, 5, 5, + 6, 6, 8, 8, 9, 8, 9, 9, 9, 9, 4, 5, 5, 7, 6, 8, + 8, 8, 8, 9, 8, 9, 8, 6, 7, 7, 7, 8, 8, 8, 9, 9, + 9, 9, 9, 9, 6, 7, 7, 7, 7, 8, 8, 9, 9, 9, 9, 9, + 9, 7, 8, 8, 8, 8, 9, 8, 9, 9,10, 9, 9,10, 7, 8, + 8, 8, 8, 9, 9, 9, 9, 9, 9,10,10, 8, 9, 9, 9, 9, + 9, 9, 9, 9,10,10, 9,10, 8, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9,10,10, 9, 9, 9,10, 9, 9,10, 9, 9,10,10, + 10,10, 9, 9, 9, 9, 9, 9, 9,10, 9,10,10,10,10, 9, + 9, 9,10, 9, 9,10,10, 9,10,10,10,10, 9, 9, 9,10, + 9, 9, 9,10,10,10,10,10,10, +}; + +static float _vq_quantthresh__44un1__p7_2[] = { + -5.5, -4.5, -3.5, -2.5, -1.5, -0.5, 0.5, 1.5, + 2.5, 3.5, 4.5, 5.5, +}; + +static long _vq_quantmap__44un1__p7_2[] = { + 11, 9, 7, 5, 3, 1, 0, 2, + 4, 6, 8, 10, 12, +}; + +static encode_aux_threshmatch _vq_auxt__44un1__p7_2 = { + _vq_quantthresh__44un1__p7_2, + _vq_quantmap__44un1__p7_2, + 13, + 13 +}; + +static static_codebook _44un1__p7_2 = { + 2, 169, + _vq_lengthlist__44un1__p7_2, + 1, -531103744, 1611661312, 4, 0, + _vq_quantlist__44un1__p7_2, + NULL, + &_vq_auxt__44un1__p7_2, + NULL, + 0 +}; + +static long _huff_lengthlist__44un1__short[] = { + 12,12,14,12,14,14,14,14,12, 6, 6, 8, 9, 9,11,14, + 12, 4, 2, 6, 6, 7,11,14,13, 6, 5, 7, 8, 9,11,14, + 13, 8, 5, 8, 6, 8,12,14,12, 7, 7, 8, 8, 8,10,14, + 12, 6, 3, 4, 4, 4, 7,14,11, 7, 4, 6, 6, 6, 8,14, +}; + +static static_codebook _huff_book__44un1__short = { + 2, 64, + _huff_lengthlist__44un1__short, + 0, 0, 0, 0, 0, + NULL, + NULL, + NULL, + NULL, + 0 +}; + diff --git a/Engine/lib/libvorbis/lib/codebook.c b/Engine/lib/libvorbis/lib/codebook.c new file mode 100644 index 000000000..7bbefa387 --- /dev/null +++ b/Engine/lib/libvorbis/lib/codebook.c @@ -0,0 +1,634 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: basic codebook pack/unpack/code/decode operations + last mod: $Id: codebook.c 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#include +#include +#include +#include +#include "vorbis/codec.h" +#include "codebook.h" +#include "scales.h" +#include "misc.h" +#include "os.h" + +/* packs the given codebook into the bitstream **************************/ + +int vorbis_staticbook_pack(const static_codebook *c,oggpack_buffer *opb){ + long i,j; + int ordered=0; + + /* first the basic parameters */ + oggpack_write(opb,0x564342,24); + oggpack_write(opb,c->dim,16); + oggpack_write(opb,c->entries,24); + + /* pack the codewords. There are two packings; length ordered and + length random. Decide between the two now. */ + + for(i=1;ientries;i++) + if(c->lengthlist[i-1]==0 || c->lengthlist[i]lengthlist[i-1])break; + if(i==c->entries)ordered=1; + + if(ordered){ + /* length ordered. We only need to say how many codewords of + each length. The actual codewords are generated + deterministically */ + + long count=0; + oggpack_write(opb,1,1); /* ordered */ + oggpack_write(opb,c->lengthlist[0]-1,5); /* 1 to 32 */ + + for(i=1;ientries;i++){ + long this=c->lengthlist[i]; + long last=c->lengthlist[i-1]; + if(this>last){ + for(j=last;jentries-count)); + count=i; + } + } + } + oggpack_write(opb,i-count,_ilog(c->entries-count)); + + }else{ + /* length random. Again, we don't code the codeword itself, just + the length. This time, though, we have to encode each length */ + oggpack_write(opb,0,1); /* unordered */ + + /* algortihmic mapping has use for 'unused entries', which we tag + here. The algorithmic mapping happens as usual, but the unused + entry has no codeword. */ + for(i=0;ientries;i++) + if(c->lengthlist[i]==0)break; + + if(i==c->entries){ + oggpack_write(opb,0,1); /* no unused entries */ + for(i=0;ientries;i++) + oggpack_write(opb,c->lengthlist[i]-1,5); + }else{ + oggpack_write(opb,1,1); /* we have unused entries; thus we tag */ + for(i=0;ientries;i++){ + if(c->lengthlist[i]==0){ + oggpack_write(opb,0,1); + }else{ + oggpack_write(opb,1,1); + oggpack_write(opb,c->lengthlist[i]-1,5); + } + } + } + } + + /* is the entry number the desired return value, or do we have a + mapping? If we have a mapping, what type? */ + oggpack_write(opb,c->maptype,4); + switch(c->maptype){ + case 0: + /* no mapping */ + break; + case 1:case 2: + /* implicitly populated value mapping */ + /* explicitly populated value mapping */ + + if(!c->quantlist){ + /* no quantlist? error */ + return(-1); + } + + /* values that define the dequantization */ + oggpack_write(opb,c->q_min,32); + oggpack_write(opb,c->q_delta,32); + oggpack_write(opb,c->q_quant-1,4); + oggpack_write(opb,c->q_sequencep,1); + + { + int quantvals; + switch(c->maptype){ + case 1: + /* a single column of (c->entries/c->dim) quantized values for + building a full value list algorithmically (square lattice) */ + quantvals=_book_maptype1_quantvals(c); + break; + case 2: + /* every value (c->entries*c->dim total) specified explicitly */ + quantvals=c->entries*c->dim; + break; + default: /* NOT_REACHABLE */ + quantvals=-1; + } + + /* quantized values */ + for(i=0;iquantlist[i]),c->q_quant); + + } + break; + default: + /* error case; we don't have any other map types now */ + return(-1); + } + + return(0); +} + +/* unpacks a codebook from the packet buffer into the codebook struct, + readies the codebook auxiliary structures for decode *************/ +int vorbis_staticbook_unpack(oggpack_buffer *opb,static_codebook *s){ + long i,j; + memset(s,0,sizeof(*s)); + s->allocedp=1; + + /* make sure alignment is correct */ + if(oggpack_read(opb,24)!=0x564342)goto _eofout; + + /* first the basic parameters */ + s->dim=oggpack_read(opb,16); + s->entries=oggpack_read(opb,24); + if(s->entries==-1)goto _eofout; + + /* codeword ordering.... length ordered or unordered? */ + switch((int)oggpack_read(opb,1)){ + case 0: + /* unordered */ + s->lengthlist=_ogg_malloc(sizeof(*s->lengthlist)*s->entries); + + /* allocated but unused entries? */ + if(oggpack_read(opb,1)){ + /* yes, unused entries */ + + for(i=0;ientries;i++){ + if(oggpack_read(opb,1)){ + long num=oggpack_read(opb,5); + if(num==-1)goto _eofout; + s->lengthlist[i]=num+1; + }else + s->lengthlist[i]=0; + } + }else{ + /* all entries used; no tagging */ + for(i=0;ientries;i++){ + long num=oggpack_read(opb,5); + if(num==-1)goto _eofout; + s->lengthlist[i]=num+1; + } + } + + break; + case 1: + /* ordered */ + { + long length=oggpack_read(opb,5)+1; + s->lengthlist=_ogg_malloc(sizeof(*s->lengthlist)*s->entries); + + for(i=0;ientries;){ + long num=oggpack_read(opb,_ilog(s->entries-i)); + if(num==-1)goto _eofout; + for(j=0;jentries;j++,i++) + s->lengthlist[i]=length; + length++; + } + } + break; + default: + /* EOF */ + return(-1); + } + + /* Do we have a mapping to unpack? */ + switch((s->maptype=oggpack_read(opb,4))){ + case 0: + /* no mapping */ + break; + case 1: case 2: + /* implicitly populated value mapping */ + /* explicitly populated value mapping */ + + s->q_min=oggpack_read(opb,32); + s->q_delta=oggpack_read(opb,32); + s->q_quant=oggpack_read(opb,4)+1; + s->q_sequencep=oggpack_read(opb,1); + + { + int quantvals=0; + switch(s->maptype){ + case 1: + quantvals=_book_maptype1_quantvals(s); + break; + case 2: + quantvals=s->entries*s->dim; + break; + } + + /* quantized values */ + s->quantlist=_ogg_malloc(sizeof(*s->quantlist)*quantvals); + for(i=0;iquantlist[i]=oggpack_read(opb,s->q_quant); + + if(quantvals&&s->quantlist[quantvals-1]==-1)goto _eofout; + } + break; + default: + goto _errout; + } + + /* all set */ + return(0); + + _errout: + _eofout: + vorbis_staticbook_clear(s); + return(-1); +} + +/* returns the number of bits ************************************************/ +int vorbis_book_encode(codebook *book, int a, oggpack_buffer *b){ + if(a<0 || a>=book->c->entries)return(0); + oggpack_write(b,book->codelist[a],book->c->lengthlist[a]); + return(book->c->lengthlist[a]); +} + +/* One the encode side, our vector writers are each designed for a +specific purpose, and the encoder is not flexible without modification: + +The LSP vector coder uses a single stage nearest-match with no +interleave, so no step and no error return. This is specced by floor0 +and doesn't change. + +Residue0 encoding interleaves, uses multiple stages, and each stage +peels of a specific amount of resolution from a lattice (thus we want +to match by threshold, not nearest match). Residue doesn't *have* to +be encoded that way, but to change it, one will need to add more +infrastructure on the encode side (decode side is specced and simpler) */ + +/* floor0 LSP (single stage, non interleaved, nearest match) */ +/* returns entry number and *modifies a* to the quantization value *****/ +int vorbis_book_errorv(codebook *book,float *a){ + int dim=book->dim,k; + int best=_best(book,a,1); + for(k=0;kvaluelist+best*dim)[k]; + return(best); +} + +/* returns the number of bits and *modifies a* to the quantization value *****/ +int vorbis_book_encodev(codebook *book,int best,float *a,oggpack_buffer *b){ + int k,dim=book->dim; + for(k=0;kvaluelist+best*dim)[k]; + return(vorbis_book_encode(book,best,b)); +} + +/* the 'eliminate the decode tree' optimization actually requires the + codewords to be MSb first, not LSb. This is an annoying inelegancy + (and one of the first places where carefully thought out design + turned out to be wrong; Vorbis II and future Ogg codecs should go + to an MSb bitpacker), but not actually the huge hit it appears to + be. The first-stage decode table catches most words so that + bitreverse is not in the main execution path. */ + +static ogg_uint32_t bitreverse(ogg_uint32_t x){ + x= ((x>>16)&0x0000ffff) | ((x<<16)&0xffff0000); + x= ((x>> 8)&0x00ff00ff) | ((x<< 8)&0xff00ff00); + x= ((x>> 4)&0x0f0f0f0f) | ((x<< 4)&0xf0f0f0f0); + x= ((x>> 2)&0x33333333) | ((x<< 2)&0xcccccccc); + return((x>> 1)&0x55555555) | ((x<< 1)&0xaaaaaaaa); +} + +STIN long decode_packed_entry_number(codebook *book, oggpack_buffer *b){ + int read=book->dec_maxlength; + long lo,hi; + long lok = oggpack_look(b,book->dec_firsttablen); + + if (lok >= 0) { + long entry = book->dec_firsttable[lok]; + if(entry&0x80000000UL){ + lo=(entry>>15)&0x7fff; + hi=book->used_entries-(entry&0x7fff); + }else{ + oggpack_adv(b, book->dec_codelengths[entry-1]); + return(entry-1); + } + }else{ + lo=0; + hi=book->used_entries; + } + + lok = oggpack_look(b, read); + + while(lok<0 && read>1) + lok = oggpack_look(b, --read); + if(lok<0)return -1; + + /* bisect search for the codeword in the ordered list */ + { + ogg_uint32_t testword=bitreverse((ogg_uint32_t)lok); + + while(hi-lo>1){ + long p=(hi-lo)>>1; + long test=book->codelist[lo+p]>testword; + lo+=p&(test-1); + hi-=p&(-test); + } + + if(book->dec_codelengths[lo]<=read){ + oggpack_adv(b, book->dec_codelengths[lo]); + return(lo); + } + } + + oggpack_adv(b, read); + + return(-1); +} + +/* Decode side is specced and easier, because we don't need to find + matches using different criteria; we simply read and map. There are + two things we need to do 'depending': + + We may need to support interleave. We don't really, but it's + convenient to do it here rather than rebuild the vector later. + + Cascades may be additive or multiplicitive; this is not inherent in + the codebook, but set in the code using the codebook. Like + interleaving, it's easiest to do it here. + addmul==0 -> declarative (set the value) + addmul==1 -> additive + addmul==2 -> multiplicitive */ + +/* returns the [original, not compacted] entry number or -1 on eof *********/ +long vorbis_book_decode(codebook *book, oggpack_buffer *b){ + if(book->used_entries>0){ + long packed_entry=decode_packed_entry_number(book,b); + if(packed_entry>=0) + return(book->dec_index[packed_entry]); + } + + /* if there's no dec_index, the codebook unpacking isn't collapsed */ + return(-1); +} + +/* returns 0 on OK or -1 on eof *************************************/ +long vorbis_book_decodevs_add(codebook *book,float *a,oggpack_buffer *b,int n){ + if(book->used_entries>0){ + int step=n/book->dim; + long *entry = alloca(sizeof(*entry)*step); + float **t = alloca(sizeof(*t)*step); + int i,j,o; + + for (i = 0; i < step; i++) { + entry[i]=decode_packed_entry_number(book,b); + if(entry[i]==-1)return(-1); + t[i] = book->valuelist+entry[i]*book->dim; + } + for(i=0,o=0;idim;i++,o+=step) + for (j=0;jused_entries>0){ + int i,j,entry; + float *t; + + if(book->dim>8){ + for(i=0;ivaluelist+entry*book->dim; + for (j=0;jdim;) + a[i++]+=t[j++]; + } + }else{ + for(i=0;ivaluelist+entry*book->dim; + j=0; + switch((int)book->dim){ + case 8: + a[i++]+=t[j++]; + case 7: + a[i++]+=t[j++]; + case 6: + a[i++]+=t[j++]; + case 5: + a[i++]+=t[j++]; + case 4: + a[i++]+=t[j++]; + case 3: + a[i++]+=t[j++]; + case 2: + a[i++]+=t[j++]; + case 1: + a[i++]+=t[j++]; + case 0: + break; + } + } + } + } + return(0); +} + +long vorbis_book_decodev_set(codebook *book,float *a,oggpack_buffer *b,int n){ + if(book->used_entries>0){ + int i,j,entry; + float *t; + + for(i=0;ivaluelist+entry*book->dim; + for (j=0;jdim;) + a[i++]=t[j++]; + } + }else{ + int i,j; + + for(i=0;idim;) + a[i++]=0.f; + } + } + return(0); +} + +long vorbis_book_decodevv_add(codebook *book,float **a,long offset,int ch, + oggpack_buffer *b,int n){ + + long i,j,entry; + int chptr=0; + if(book->used_entries>0){ + for(i=offset/ch;i<(offset+n)/ch;){ + entry = decode_packed_entry_number(book,b); + if(entry==-1)return(-1); + { + const float *t = book->valuelist+entry*book->dim; + for (j=0;jdim;j++){ + a[chptr++][i]+=t[j]; + if(chptr==ch){ + chptr=0; + i++; + } + } + } + } + } + return(0); +} + +#ifdef _V_SELFTEST +/* Simple enough; pack a few candidate codebooks, unpack them. Code a + number of vectors through (keeping track of the quantized values), + and decode using the unpacked book. quantized version of in should + exactly equal out */ + +#include + +#include "vorbis/book/lsp20_0.vqh" +#include "vorbis/book/res0a_13.vqh" +#define TESTSIZE 40 + +float test1[TESTSIZE]={ + 0.105939f, + 0.215373f, + 0.429117f, + 0.587974f, + + 0.181173f, + 0.296583f, + 0.515707f, + 0.715261f, + + 0.162327f, + 0.263834f, + 0.342876f, + 0.406025f, + + 0.103571f, + 0.223561f, + 0.368513f, + 0.540313f, + + 0.136672f, + 0.395882f, + 0.587183f, + 0.652476f, + + 0.114338f, + 0.417300f, + 0.525486f, + 0.698679f, + + 0.147492f, + 0.324481f, + 0.643089f, + 0.757582f, + + 0.139556f, + 0.215795f, + 0.324559f, + 0.399387f, + + 0.120236f, + 0.267420f, + 0.446940f, + 0.608760f, + + 0.115587f, + 0.287234f, + 0.571081f, + 0.708603f, +}; + +float test3[TESTSIZE]={ + 0,1,-2,3,4,-5,6,7,8,9, + 8,-2,7,-1,4,6,8,3,1,-9, + 10,11,12,13,14,15,26,17,18,19, + 30,-25,-30,-1,-5,-32,4,3,-2,0}; + +static_codebook *testlist[]={&_vq_book_lsp20_0, + &_vq_book_res0a_13,NULL}; +float *testvec[]={test1,test3}; + +int main(){ + oggpack_buffer write; + oggpack_buffer read; + long ptr=0,i; + oggpack_writeinit(&write); + + fprintf(stderr,"Testing codebook abstraction...:\n"); + + while(testlist[ptr]){ + codebook c; + static_codebook s; + float *qv=alloca(sizeof(*qv)*TESTSIZE); + float *iv=alloca(sizeof(*iv)*TESTSIZE); + memcpy(qv,testvec[ptr],sizeof(*qv)*TESTSIZE); + memset(iv,0,sizeof(*iv)*TESTSIZE); + + fprintf(stderr,"\tpacking/coding %ld... ",ptr); + + /* pack the codebook, write the testvector */ + oggpack_reset(&write); + vorbis_book_init_encode(&c,testlist[ptr]); /* get it into memory + we can write */ + vorbis_staticbook_pack(testlist[ptr],&write); + fprintf(stderr,"Codebook size %ld bytes... ",oggpack_bytes(&write)); + for(i=0;i.000001){ + fprintf(stderr,"read (%g) != written (%g) at position (%ld)\n", + iv[i],qv[i],i); + exit(1); + } + + fprintf(stderr,"OK\n"); + ptr++; + } + + /* The above is the trivial stuff; now try unquantizing a log scale codebook */ + + exit(0); +} + +#endif diff --git a/Engine/lib/libvorbis/lib/codebook.h b/Engine/lib/libvorbis/lib/codebook.h new file mode 100644 index 000000000..02c8e1265 --- /dev/null +++ b/Engine/lib/libvorbis/lib/codebook.h @@ -0,0 +1,160 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: basic shared codebook operations + last mod: $Id: codebook.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#ifndef _V_CODEBOOK_H_ +#define _V_CODEBOOK_H_ + +#include + +/* This structure encapsulates huffman and VQ style encoding books; it + doesn't do anything specific to either. + + valuelist/quantlist are nonNULL (and q_* significant) only if + there's entry->value mapping to be done. + + If encode-side mapping must be done (and thus the entry needs to be + hunted), the auxiliary encode pointer will point to a decision + tree. This is true of both VQ and huffman, but is mostly useful + with VQ. + +*/ + +typedef struct static_codebook{ + long dim; /* codebook dimensions (elements per vector) */ + long entries; /* codebook entries */ + long *lengthlist; /* codeword lengths in bits */ + + /* mapping ***************************************************************/ + int maptype; /* 0=none + 1=implicitly populated values from map column + 2=listed arbitrary values */ + + /* The below does a linear, single monotonic sequence mapping. */ + long q_min; /* packed 32 bit float; quant value 0 maps to minval */ + long q_delta; /* packed 32 bit float; val 1 - val 0 == delta */ + int q_quant; /* bits: 0 < quant <= 16 */ + int q_sequencep; /* bitflag */ + + long *quantlist; /* map == 1: (int)(entries^(1/dim)) element column map + map == 2: list of dim*entries quantized entry vals + */ + + /* encode helpers ********************************************************/ + struct encode_aux_nearestmatch *nearest_tree; + struct encode_aux_threshmatch *thresh_tree; + struct encode_aux_pigeonhole *pigeon_tree; + + int allocedp; +} static_codebook; + +/* this structures an arbitrary trained book to quickly find the + nearest cell match */ +typedef struct encode_aux_nearestmatch{ + /* pre-calculated partitioning tree */ + long *ptr0; + long *ptr1; + + long *p; /* decision points (each is an entry) */ + long *q; /* decision points (each is an entry) */ + long aux; /* number of tree entries */ + long alloc; +} encode_aux_nearestmatch; + +/* assumes a maptype of 1; encode side only, so that's OK */ +typedef struct encode_aux_threshmatch{ + float *quantthresh; + long *quantmap; + int quantvals; + int threshvals; +} encode_aux_threshmatch; + +typedef struct encode_aux_pigeonhole{ + float min; + float del; + + int mapentries; + int quantvals; + long *pigeonmap; + + long fittotal; + long *fitlist; + long *fitmap; + long *fitlength; +} encode_aux_pigeonhole; + +typedef struct codebook{ + long dim; /* codebook dimensions (elements per vector) */ + long entries; /* codebook entries */ + long used_entries; /* populated codebook entries */ + const static_codebook *c; + + /* for encode, the below are entry-ordered, fully populated */ + /* for decode, the below are ordered by bitreversed codeword and only + used entries are populated */ + float *valuelist; /* list of dim*entries actual entry values */ + ogg_uint32_t *codelist; /* list of bitstream codewords for each entry */ + + int *dec_index; /* only used if sparseness collapsed */ + char *dec_codelengths; + ogg_uint32_t *dec_firsttable; + int dec_firsttablen; + int dec_maxlength; + +} codebook; + +extern void vorbis_staticbook_clear(static_codebook *b); +extern void vorbis_staticbook_destroy(static_codebook *b); +extern int vorbis_book_init_encode(codebook *dest,const static_codebook *source); +extern int vorbis_book_init_decode(codebook *dest,const static_codebook *source); +extern void vorbis_book_clear(codebook *b); + +extern float *_book_unquantize(const static_codebook *b,int n,int *map); +extern float *_book_logdist(const static_codebook *b,float *vals); +extern float _float32_unpack(long val); +extern long _float32_pack(float val); +extern int _best(codebook *book, float *a, int step); +extern int _ilog(unsigned int v); +extern long _book_maptype1_quantvals(const static_codebook *b); + +extern int vorbis_book_besterror(codebook *book,float *a,int step,int addmul); +extern long vorbis_book_codeword(codebook *book,int entry); +extern long vorbis_book_codelen(codebook *book,int entry); + + + +extern int vorbis_staticbook_pack(const static_codebook *c,oggpack_buffer *b); +extern int vorbis_staticbook_unpack(oggpack_buffer *b,static_codebook *c); + +extern int vorbis_book_encode(codebook *book, int a, oggpack_buffer *b); +extern int vorbis_book_errorv(codebook *book, float *a); +extern int vorbis_book_encodev(codebook *book, int best,float *a, + oggpack_buffer *b); + +extern long vorbis_book_decode(codebook *book, oggpack_buffer *b); +extern long vorbis_book_decodevs_add(codebook *book, float *a, + oggpack_buffer *b,int n); +extern long vorbis_book_decodev_set(codebook *book, float *a, + oggpack_buffer *b,int n); +extern long vorbis_book_decodev_add(codebook *book, float *a, + oggpack_buffer *b,int n); +extern long vorbis_book_decodevv_add(codebook *book, float **a, + long off,int ch, + oggpack_buffer *b,int n); + + + +#endif diff --git a/Engine/lib/libvorbis/lib/codec_internal.h b/Engine/lib/libvorbis/lib/codec_internal.h new file mode 100644 index 000000000..9cefef195 --- /dev/null +++ b/Engine/lib/libvorbis/lib/codec_internal.h @@ -0,0 +1,137 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: libvorbis codec headers + last mod: $Id: codec_internal.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#ifndef _V_CODECI_H_ +#define _V_CODECI_H_ + +#include "envelope.h" +#include "codebook.h" + +#define BLOCKTYPE_IMPULSE 0 +#define BLOCKTYPE_PADDING 1 +#define BLOCKTYPE_TRANSITION 0 +#define BLOCKTYPE_LONG 1 + +#define PACKETBLOBS 15 + +typedef struct vorbis_block_internal{ + float **pcmdelay; /* this is a pointer into local storage */ + float ampmax; + int blocktype; + + oggpack_buffer *packetblob[PACKETBLOBS]; /* initialized, must be freed; + blob [PACKETBLOBS/2] points to + the oggpack_buffer in the + main vorbis_block */ +} vorbis_block_internal; + +typedef void vorbis_look_floor; +typedef void vorbis_look_residue; +typedef void vorbis_look_transform; + +/* mode ************************************************************/ +typedef struct { + int blockflag; + int windowtype; + int transformtype; + int mapping; +} vorbis_info_mode; + +typedef void vorbis_info_floor; +typedef void vorbis_info_residue; +typedef void vorbis_info_mapping; + +#include "psy.h" +#include "bitrate.h" + +typedef struct private_state { + /* local lookup storage */ + envelope_lookup *ve; /* envelope lookup */ + int window[2]; + vorbis_look_transform **transform[2]; /* block, type */ + drft_lookup fft_look[2]; + + int modebits; + vorbis_look_floor **flr; + vorbis_look_residue **residue; + vorbis_look_psy *psy; + vorbis_look_psy_global *psy_g_look; + + /* local storage, only used on the encoding side. This way the + application does not need to worry about freeing some packets' + memory and not others'; packet storage is always tracked. + Cleared next call to a _dsp_ function */ + unsigned char *header; + unsigned char *header1; + unsigned char *header2; + + bitrate_manager_state bms; + + ogg_int64_t sample_count; +} private_state; + +/* codec_setup_info contains all the setup information specific to the + specific compression/decompression mode in progress (eg, + psychoacoustic settings, channel setup, options, codebook + etc). +*********************************************************************/ + +#include "highlevel.h" +typedef struct codec_setup_info { + + /* Vorbis supports only short and long blocks, but allows the + encoder to choose the sizes */ + + long blocksizes[2]; + + /* modes are the primary means of supporting on-the-fly different + blocksizes, different channel mappings (LR or M/A), + different residue backends, etc. Each mode consists of a + blocksize flag and a mapping (along with the mapping setup */ + + int modes; + int maps; + int floors; + int residues; + int books; + int psys; /* encode only */ + + vorbis_info_mode *mode_param[64]; + int map_type[64]; + vorbis_info_mapping *map_param[64]; + int floor_type[64]; + vorbis_info_floor *floor_param[64]; + int residue_type[64]; + vorbis_info_residue *residue_param[64]; + static_codebook *book_param[256]; + codebook *fullbooks; + + vorbis_info_psy *psy_param[4]; /* encode only */ + vorbis_info_psy_global psy_g_param; + + bitrate_manager_info bi; + highlevel_encode_setup hi; /* used only by vorbisenc.c. It's a + highly redundant structure, but + improves clarity of program flow. */ + int halfrate_flag; /* painless downsample for decode */ +} codec_setup_info; + +extern vorbis_look_psy_global *_vp_global_look(vorbis_info *vi); +extern void _vp_global_free(vorbis_look_psy_global *look); + +#endif + diff --git a/Engine/lib/libvorbis/lib/envelope.c b/Engine/lib/libvorbis/lib/envelope.c new file mode 100644 index 000000000..9cb934137 --- /dev/null +++ b/Engine/lib/libvorbis/lib/envelope.c @@ -0,0 +1,382 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: PCM data envelope analysis + last mod: $Id: envelope.c 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#include +#include +#include +#include +#include +#include "vorbis/codec.h" +#include "codec_internal.h" + +#include "os.h" +#include "scales.h" +#include "envelope.h" +#include "mdct.h" +#include "misc.h" + +void _ve_envelope_init(envelope_lookup *e,vorbis_info *vi){ + codec_setup_info *ci=vi->codec_setup; + vorbis_info_psy_global *gi=&ci->psy_g_param; + int ch=vi->channels; + int i,j; + int n=e->winlength=128; + e->searchstep=64; /* not random */ + + e->minenergy=gi->preecho_minenergy; + e->ch=ch; + e->storage=128; + e->cursor=ci->blocksizes[1]/2; + e->mdct_win=_ogg_calloc(n,sizeof(*e->mdct_win)); + mdct_init(&e->mdct,n); + + for(i=0;imdct_win[i]=sin(i/(n-1.)*M_PI); + e->mdct_win[i]*=e->mdct_win[i]; + } + + /* magic follows */ + e->band[0].begin=2; e->band[0].end=4; + e->band[1].begin=4; e->band[1].end=5; + e->band[2].begin=6; e->band[2].end=6; + e->band[3].begin=9; e->band[3].end=8; + e->band[4].begin=13; e->band[4].end=8; + e->band[5].begin=17; e->band[5].end=8; + e->band[6].begin=22; e->band[6].end=8; + + for(j=0;jband[j].end; + e->band[j].window=_ogg_malloc(n*sizeof(*e->band[0].window)); + for(i=0;iband[j].window[i]=sin((i+.5)/n*M_PI); + e->band[j].total+=e->band[j].window[i]; + } + e->band[j].total=1./e->band[j].total; + } + + e->filter=_ogg_calloc(VE_BANDS*ch,sizeof(*e->filter)); + e->mark=_ogg_calloc(e->storage,sizeof(*e->mark)); + +} + +void _ve_envelope_clear(envelope_lookup *e){ + int i; + mdct_clear(&e->mdct); + for(i=0;iband[i].window); + _ogg_free(e->mdct_win); + _ogg_free(e->filter); + _ogg_free(e->mark); + memset(e,0,sizeof(*e)); +} + +/* fairly straight threshhold-by-band based until we find something + that works better and isn't patented. */ + +static int _ve_amp(envelope_lookup *ve, + vorbis_info_psy_global *gi, + float *data, + envelope_band *bands, + envelope_filter_state *filters, + long pos){ + long n=ve->winlength; + int ret=0; + long i,j; + float decay; + + /* we want to have a 'minimum bar' for energy, else we're just + basing blocks on quantization noise that outweighs the signal + itself (for low power signals) */ + + float minV=ve->minenergy; + float *vec=alloca(n*sizeof(*vec)); + + /* stretch is used to gradually lengthen the number of windows + considered prevoius-to-potential-trigger */ + int stretch=max(VE_MINSTRETCH,ve->stretch/2); + float penalty=gi->stretch_penalty-(ve->stretch/2-VE_MINSTRETCH); + if(penalty<0.f)penalty=0.f; + if(penalty>gi->stretch_penalty)penalty=gi->stretch_penalty; + + /*_analysis_output_always("lpcm",seq2,data,n,0,0, + totalshift+pos*ve->searchstep);*/ + + /* window and transform */ + for(i=0;imdct_win[i]; + mdct_forward(&ve->mdct,vec,vec); + + /*_analysis_output_always("mdct",seq2,vec,n/2,0,1,0); */ + + /* near-DC spreading function; this has nothing to do with + psychoacoustics, just sidelobe leakage and window size */ + { + float temp=vec[0]*vec[0]+.7*vec[1]*vec[1]+.2*vec[2]*vec[2]; + int ptr=filters->nearptr; + + /* the accumulation is regularly refreshed from scratch to avoid + floating point creep */ + if(ptr==0){ + decay=filters->nearDC_acc=filters->nearDC_partialacc+temp; + filters->nearDC_partialacc=temp; + }else{ + decay=filters->nearDC_acc+=temp; + filters->nearDC_partialacc+=temp; + } + filters->nearDC_acc-=filters->nearDC[ptr]; + filters->nearDC[ptr]=temp; + + decay*=(1./(VE_NEARDC+1)); + filters->nearptr++; + if(filters->nearptr>=VE_NEARDC)filters->nearptr=0; + decay=todB(&decay)*.5-15.f; + } + + /* perform spreading and limiting, also smooth the spectrum. yes, + the MDCT results in all real coefficients, but it still *behaves* + like real/imaginary pairs */ + for(i=0;i>1]=val; + decay-=8.; + } + + /*_analysis_output_always("spread",seq2++,vec,n/4,0,0,0);*/ + + /* perform preecho/postecho triggering by band */ + for(j=0;j=VE_AMP)filters[j].ampptr=0; + } + + /* look at min/max, decide trigger */ + if(valmax>gi->preecho_thresh[j]+penalty){ + ret|=1; + ret|=4; + } + if(valminpostecho_thresh[j]-penalty)ret|=2; + } + + return(ret); +} + +#if 0 +static int seq=0; +static ogg_int64_t totalshift=-1024; +#endif + +long _ve_envelope_search(vorbis_dsp_state *v){ + vorbis_info *vi=v->vi; + codec_setup_info *ci=vi->codec_setup; + vorbis_info_psy_global *gi=&ci->psy_g_param; + envelope_lookup *ve=((private_state *)(v->backend_state))->ve; + long i,j; + + int first=ve->current/ve->searchstep; + int last=v->pcm_current/ve->searchstep-VE_WIN; + if(first<0)first=0; + + /* make sure we have enough storage to match the PCM */ + if(last+VE_WIN+VE_POST>ve->storage){ + ve->storage=last+VE_WIN+VE_POST; /* be sure */ + ve->mark=_ogg_realloc(ve->mark,ve->storage*sizeof(*ve->mark)); + } + + for(j=first;jstretch++; + if(ve->stretch>VE_MAXSTRETCH*2) + ve->stretch=VE_MAXSTRETCH*2; + + for(i=0;ich;i++){ + float *pcm=v->pcm[i]+ve->searchstep*(j); + ret|=_ve_amp(ve,gi,pcm,ve->band,ve->filter+i*VE_BANDS,j); + } + + ve->mark[j+VE_POST]=0; + if(ret&1){ + ve->mark[j]=1; + ve->mark[j+1]=1; + } + + if(ret&2){ + ve->mark[j]=1; + if(j>0)ve->mark[j-1]=1; + } + + if(ret&4)ve->stretch=-1; + } + + ve->current=last*ve->searchstep; + + { + long centerW=v->centerW; + long testW= + centerW+ + ci->blocksizes[v->W]/4+ + ci->blocksizes[1]/2+ + ci->blocksizes[0]/4; + + j=ve->cursor; + + while(jcurrent-(ve->searchstep)){/* account for postecho + working back one window */ + if(j>=testW)return(1); + + ve->cursor=j; + + if(ve->mark[j/ve->searchstep]){ + if(j>centerW){ + +#if 0 + if(j>ve->curmark){ + float *marker=alloca(v->pcm_current*sizeof(*marker)); + int l,m; + memset(marker,0,sizeof(*marker)*v->pcm_current); + fprintf(stderr,"mark! seq=%d, cursor:%fs time:%fs\n", + seq, + (totalshift+ve->cursor)/44100., + (totalshift+j)/44100.); + _analysis_output_always("pcmL",seq,v->pcm[0],v->pcm_current,0,0,totalshift); + _analysis_output_always("pcmR",seq,v->pcm[1],v->pcm_current,0,0,totalshift); + + _analysis_output_always("markL",seq,v->pcm[0],j,0,0,totalshift); + _analysis_output_always("markR",seq,v->pcm[1],j,0,0,totalshift); + + for(m=0;msearchstep]=ve->filter[m].markers[l]*.1; + _analysis_output_always(buf,seq,marker,v->pcm_current,0,0,totalshift); + } + + for(m=0;msearchstep]=ve->filter[m+VE_BANDS].markers[l]*.1; + _analysis_output_always(buf,seq,marker,v->pcm_current,0,0,totalshift); + } + + for(l=0;lsearchstep]=ve->mark[l]*.4; + _analysis_output_always("mark",seq,marker,v->pcm_current,0,0,totalshift); + + + seq++; + + } +#endif + + ve->curmark=j; + if(j>=testW)return(1); + return(0); + } + } + j+=ve->searchstep; + } + } + + return(-1); +} + +int _ve_envelope_mark(vorbis_dsp_state *v){ + envelope_lookup *ve=((private_state *)(v->backend_state))->ve; + vorbis_info *vi=v->vi; + codec_setup_info *ci=vi->codec_setup; + long centerW=v->centerW; + long beginW=centerW-ci->blocksizes[v->W]/4; + long endW=centerW+ci->blocksizes[v->W]/4; + if(v->W){ + beginW-=ci->blocksizes[v->lW]/4; + endW+=ci->blocksizes[v->nW]/4; + }else{ + beginW-=ci->blocksizes[0]/4; + endW+=ci->blocksizes[0]/4; + } + + if(ve->curmark>=beginW && ve->curmarksearchstep; + long last=endW/ve->searchstep; + long i; + for(i=first;imark[i])return(1); + } + return(0); +} + +void _ve_envelope_shift(envelope_lookup *e,long shift){ + int smallsize=e->current/e->searchstep+VE_POST; /* adjust for placing marks + ahead of ve->current */ + int smallshift=shift/e->searchstep; + + memmove(e->mark,e->mark+smallshift,(smallsize-smallshift)*sizeof(*e->mark)); + +#if 0 + for(i=0;ich;i++) + memmove(e->filter[i].markers, + e->filter[i].markers+smallshift, + (1024-smallshift)*sizeof(*(*e->filter).markers)); + totalshift+=shift; +#endif + + e->current-=shift; + if(e->curmark>=0) + e->curmark-=shift; + e->cursor-=shift; +} + + + + + + diff --git a/Engine/lib/libvorbis/lib/envelope.h b/Engine/lib/libvorbis/lib/envelope.h new file mode 100644 index 000000000..dc64ac121 --- /dev/null +++ b/Engine/lib/libvorbis/lib/envelope.h @@ -0,0 +1,81 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: PCM data envelope analysis and manipulation + last mod: $Id: envelope.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#ifndef _V_ENVELOPE_ +#define _V_ENVELOPE_ + +#include "mdct.h" + +#define VE_PRE 16 +#define VE_WIN 4 +#define VE_POST 2 +#define VE_AMP (VE_PRE+VE_POST-1) + +#define VE_BANDS 7 +#define VE_NEARDC 15 + +#define VE_MINSTRETCH 2 /* a bit less than short block */ +#define VE_MAXSTRETCH 12 /* one-third full block */ + +typedef struct { + float ampbuf[VE_AMP]; + int ampptr; + + float nearDC[VE_NEARDC]; + float nearDC_acc; + float nearDC_partialacc; + int nearptr; + +} envelope_filter_state; + +typedef struct { + int begin; + int end; + float *window; + float total; +} envelope_band; + +typedef struct { + int ch; + int winlength; + int searchstep; + float minenergy; + + mdct_lookup mdct; + float *mdct_win; + + envelope_band band[VE_BANDS]; + envelope_filter_state *filter; + int stretch; + + int *mark; + + long storage; + long current; + long curmark; + long cursor; +} envelope_lookup; + +extern void _ve_envelope_init(envelope_lookup *e,vorbis_info *vi); +extern void _ve_envelope_clear(envelope_lookup *e); +extern long _ve_envelope_search(vorbis_dsp_state *v); +extern void _ve_envelope_shift(envelope_lookup *e,long shift); +extern int _ve_envelope_mark(vorbis_dsp_state *v); + + +#endif + diff --git a/Engine/lib/libvorbis/lib/floor0.c b/Engine/lib/libvorbis/lib/floor0.c new file mode 100644 index 000000000..be6021b65 --- /dev/null +++ b/Engine/lib/libvorbis/lib/floor0.c @@ -0,0 +1,223 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: floor backend 0 implementation + last mod: $Id: floor0.c 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#include +#include +#include +#include +#include "vorbis/codec.h" +#include "codec_internal.h" +#include "registry.h" +#include "lpc.h" +#include "lsp.h" +#include "codebook.h" +#include "scales.h" +#include "misc.h" +#include "os.h" + +#include "misc.h" +#include + +typedef struct { + int ln; + int m; + int **linearmap; + int n[2]; + + vorbis_info_floor0 *vi; + + long bits; + long frames; +} vorbis_look_floor0; + + +/***********************************************/ + +static void floor0_free_info(vorbis_info_floor *i){ + vorbis_info_floor0 *info=(vorbis_info_floor0 *)i; + if(info){ + memset(info,0,sizeof(*info)); + _ogg_free(info); + } +} + +static void floor0_free_look(vorbis_look_floor *i){ + vorbis_look_floor0 *look=(vorbis_look_floor0 *)i; + if(look){ + + if(look->linearmap){ + + if(look->linearmap[0])_ogg_free(look->linearmap[0]); + if(look->linearmap[1])_ogg_free(look->linearmap[1]); + + _ogg_free(look->linearmap); + } + memset(look,0,sizeof(*look)); + _ogg_free(look); + } +} + +static vorbis_info_floor *floor0_unpack (vorbis_info *vi,oggpack_buffer *opb){ + codec_setup_info *ci=vi->codec_setup; + int j; + + vorbis_info_floor0 *info=_ogg_malloc(sizeof(*info)); + info->order=oggpack_read(opb,8); + info->rate=oggpack_read(opb,16); + info->barkmap=oggpack_read(opb,16); + info->ampbits=oggpack_read(opb,6); + info->ampdB=oggpack_read(opb,8); + info->numbooks=oggpack_read(opb,4)+1; + + if(info->order<1)goto err_out; + if(info->rate<1)goto err_out; + if(info->barkmap<1)goto err_out; + if(info->numbooks<1)goto err_out; + + for(j=0;jnumbooks;j++){ + info->books[j]=oggpack_read(opb,8); + if(info->books[j]<0 || info->books[j]>=ci->books)goto err_out; + } + return(info); + + err_out: + floor0_free_info(info); + return(NULL); +} + +/* initialize Bark scale and normalization lookups. We could do this + with static tables, but Vorbis allows a number of possible + combinations, so it's best to do it computationally. + + The below is authoritative in terms of defining scale mapping. + Note that the scale depends on the sampling rate as well as the + linear block and mapping sizes */ + +static void floor0_map_lazy_init(vorbis_block *vb, + vorbis_info_floor *infoX, + vorbis_look_floor0 *look){ + if(!look->linearmap[vb->W]){ + vorbis_dsp_state *vd=vb->vd; + vorbis_info *vi=vd->vi; + codec_setup_info *ci=vi->codec_setup; + vorbis_info_floor0 *info=(vorbis_info_floor0 *)infoX; + int W=vb->W; + int n=ci->blocksizes[W]/2,j; + + /* we choose a scaling constant so that: + floor(bark(rate/2-1)*C)=mapped-1 + floor(bark(rate/2)*C)=mapped */ + float scale=look->ln/toBARK(info->rate/2.f); + + /* the mapping from a linear scale to a smaller bark scale is + straightforward. We do *not* make sure that the linear mapping + does not skip bark-scale bins; the decoder simply skips them and + the encoder may do what it wishes in filling them. They're + necessary in some mapping combinations to keep the scale spacing + accurate */ + look->linearmap[W]=_ogg_malloc((n+1)*sizeof(**look->linearmap)); + for(j=0;jrate/2.f)/n*j) + *scale); /* bark numbers represent band edges */ + if(val>=look->ln)val=look->ln-1; /* guard against the approximation */ + look->linearmap[W][j]=val; + } + look->linearmap[W][j]=-1; + look->n[W]=n; + } +} + +static vorbis_look_floor *floor0_look(vorbis_dsp_state *vd, + vorbis_info_floor *i){ + vorbis_info_floor0 *info=(vorbis_info_floor0 *)i; + vorbis_look_floor0 *look=_ogg_calloc(1,sizeof(*look)); + look->m=info->order; + look->ln=info->barkmap; + look->vi=info; + + look->linearmap=_ogg_calloc(2,sizeof(*look->linearmap)); + + return look; +} + +static void *floor0_inverse1(vorbis_block *vb,vorbis_look_floor *i){ + vorbis_look_floor0 *look=(vorbis_look_floor0 *)i; + vorbis_info_floor0 *info=look->vi; + int j,k; + + int ampraw=oggpack_read(&vb->opb,info->ampbits); + if(ampraw>0){ /* also handles the -1 out of data case */ + long maxval=(1<ampbits)-1; + float amp=(float)ampraw/maxval*info->ampdB; + int booknum=oggpack_read(&vb->opb,_ilog(info->numbooks)); + + if(booknum!=-1 && booknumnumbooks){ /* be paranoid */ + codec_setup_info *ci=vb->vd->vi->codec_setup; + codebook *b=ci->fullbooks+info->books[booknum]; + float last=0.f; + + /* the additional b->dim is a guard against any possible stack + smash; b->dim is provably more than we can overflow the + vector */ + float *lsp=_vorbis_block_alloc(vb,sizeof(*lsp)*(look->m+b->dim+1)); + + for(j=0;jm;j+=b->dim) + if(vorbis_book_decodev_set(b,lsp+j,&vb->opb,b->dim)==-1)goto eop; + for(j=0;jm;){ + for(k=0;kdim;k++,j++)lsp[j]+=last; + last=lsp[j-1]; + } + + lsp[look->m]=amp; + return(lsp); + } + } + eop: + return(NULL); +} + +static int floor0_inverse2(vorbis_block *vb,vorbis_look_floor *i, + void *memo,float *out){ + vorbis_look_floor0 *look=(vorbis_look_floor0 *)i; + vorbis_info_floor0 *info=look->vi; + + floor0_map_lazy_init(vb,info,look); + + if(memo){ + float *lsp=(float *)memo; + float amp=lsp[look->m]; + + /* take the coefficients back to a spectral envelope curve */ + vorbis_lsp_to_curve(out, + look->linearmap[vb->W], + look->n[vb->W], + look->ln, + lsp,look->m,amp,(float)info->ampdB); + return(1); + } + memset(out,0,sizeof(*out)*look->n[vb->W]); + return(0); +} + +/* export hooks */ +vorbis_func_floor floor0_exportbundle={ + NULL,&floor0_unpack,&floor0_look,&floor0_free_info, + &floor0_free_look,&floor0_inverse1,&floor0_inverse2 +}; + + + diff --git a/Engine/lib/libvorbis/lib/floor1.c b/Engine/lib/libvorbis/lib/floor1.c new file mode 100644 index 000000000..0e06c6dbc --- /dev/null +++ b/Engine/lib/libvorbis/lib/floor1.c @@ -0,0 +1,1094 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: floor backend 1 implementation + last mod: $Id: floor1.c 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#include +#include +#include +#include +#include "vorbis/codec.h" +#include "codec_internal.h" +#include "registry.h" +#include "codebook.h" +#include "misc.h" +#include "scales.h" + +#include + +#define floor1_rangedB 140 /* floor 1 fixed at -140dB to 0dB range */ + +typedef struct { + int sorted_index[VIF_POSIT+2]; + int forward_index[VIF_POSIT+2]; + int reverse_index[VIF_POSIT+2]; + + int hineighbor[VIF_POSIT]; + int loneighbor[VIF_POSIT]; + int posts; + + int n; + int quant_q; + vorbis_info_floor1 *vi; + + long phrasebits; + long postbits; + long frames; +} vorbis_look_floor1; + +typedef struct lsfit_acc{ + long x0; + long x1; + + long xa; + long ya; + long x2a; + long y2a; + long xya; + long an; +} lsfit_acc; + +/***********************************************/ + +static void floor1_free_info(vorbis_info_floor *i){ + vorbis_info_floor1 *info=(vorbis_info_floor1 *)i; + if(info){ + memset(info,0,sizeof(*info)); + _ogg_free(info); + } +} + +static void floor1_free_look(vorbis_look_floor *i){ + vorbis_look_floor1 *look=(vorbis_look_floor1 *)i; + if(look){ + /*fprintf(stderr,"floor 1 bit usage %f:%f (%f total)\n", + (float)look->phrasebits/look->frames, + (float)look->postbits/look->frames, + (float)(look->postbits+look->phrasebits)/look->frames);*/ + + memset(look,0,sizeof(*look)); + _ogg_free(look); + } +} + +static int ilog(unsigned int v){ + int ret=0; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +static int ilog2(unsigned int v){ + int ret=0; + if(v)--v; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +static void floor1_pack (vorbis_info_floor *i,oggpack_buffer *opb){ + vorbis_info_floor1 *info=(vorbis_info_floor1 *)i; + int j,k; + int count=0; + int rangebits; + int maxposit=info->postlist[1]; + int maxclass=-1; + + /* save out partitions */ + oggpack_write(opb,info->partitions,5); /* only 0 to 31 legal */ + for(j=0;jpartitions;j++){ + oggpack_write(opb,info->partitionclass[j],4); /* only 0 to 15 legal */ + if(maxclasspartitionclass[j])maxclass=info->partitionclass[j]; + } + + /* save out partition classes */ + for(j=0;jclass_dim[j]-1,3); /* 1 to 8 */ + oggpack_write(opb,info->class_subs[j],2); /* 0 to 3 */ + if(info->class_subs[j])oggpack_write(opb,info->class_book[j],8); + for(k=0;k<(1<class_subs[j]);k++) + oggpack_write(opb,info->class_subbook[j][k]+1,8); + } + + /* save out the post list */ + oggpack_write(opb,info->mult-1,2); /* only 1,2,3,4 legal now */ + oggpack_write(opb,ilog2(maxposit),4); + rangebits=ilog2(maxposit); + + for(j=0,k=0;jpartitions;j++){ + count+=info->class_dim[info->partitionclass[j]]; + for(;kpostlist[k+2],rangebits); + } +} + + +static vorbis_info_floor *floor1_unpack (vorbis_info *vi,oggpack_buffer *opb){ + codec_setup_info *ci=vi->codec_setup; + int j,k,count=0,maxclass=-1,rangebits; + + vorbis_info_floor1 *info=_ogg_calloc(1,sizeof(*info)); + /* read partitions */ + info->partitions=oggpack_read(opb,5); /* only 0 to 31 legal */ + for(j=0;jpartitions;j++){ + info->partitionclass[j]=oggpack_read(opb,4); /* only 0 to 15 legal */ + if(maxclasspartitionclass[j])maxclass=info->partitionclass[j]; + } + + /* read partition classes */ + for(j=0;jclass_dim[j]=oggpack_read(opb,3)+1; /* 1 to 8 */ + info->class_subs[j]=oggpack_read(opb,2); /* 0,1,2,3 bits */ + if(info->class_subs[j]<0) + goto err_out; + if(info->class_subs[j])info->class_book[j]=oggpack_read(opb,8); + if(info->class_book[j]<0 || info->class_book[j]>=ci->books) + goto err_out; + for(k=0;k<(1<class_subs[j]);k++){ + info->class_subbook[j][k]=oggpack_read(opb,8)-1; + if(info->class_subbook[j][k]<-1 || info->class_subbook[j][k]>=ci->books) + goto err_out; + } + } + + /* read the post list */ + info->mult=oggpack_read(opb,2)+1; /* only 1,2,3,4 legal now */ + rangebits=oggpack_read(opb,4); + + for(j=0,k=0;jpartitions;j++){ + count+=info->class_dim[info->partitionclass[j]]; + for(;kpostlist[k+2]=oggpack_read(opb,rangebits); + if(t<0 || t>=(1<postlist[0]=0; + info->postlist[1]=1<vi=info; + look->n=info->postlist[1]; + + /* we drop each position value in-between already decoded values, + and use linear interpolation to predict each new value past the + edges. The positions are read in the order of the position + list... we precompute the bounding positions in the lookup. Of + course, the neighbors can change (if a position is declined), but + this is an initial mapping */ + + for(i=0;ipartitions;i++)n+=info->class_dim[info->partitionclass[i]]; + n+=2; + look->posts=n; + + /* also store a sorted position index */ + for(i=0;ipostlist+i; + qsort(sortpointer,n,sizeof(*sortpointer),icomp); + + /* points from sort order back to range number */ + for(i=0;iforward_index[i]=sortpointer[i]-info->postlist; + /* points from range order to sorted position */ + for(i=0;ireverse_index[look->forward_index[i]]=i; + /* we actually need the post values too */ + for(i=0;isorted_index[i]=info->postlist[look->forward_index[i]]; + + /* quantize values to multiplier spec */ + switch(info->mult){ + case 1: /* 1024 -> 256 */ + look->quant_q=256; + break; + case 2: /* 1024 -> 128 */ + look->quant_q=128; + break; + case 3: /* 1024 -> 86 */ + look->quant_q=86; + break; + case 4: /* 1024 -> 64 */ + look->quant_q=64; + break; + } + + /* discover our neighbors for decode where we don't use fit flags + (that would push the neighbors outward) */ + for(i=0;in; + int currentx=info->postlist[i+2]; + for(j=0;jpostlist[j]; + if(x>lx && xcurrentx){ + hi=j; + hx=x; + } + } + look->loneighbor[i]=lo; + look->hineighbor[i]=hi; + } + + return(look); +} + +static int render_point(int x0,int x1,int y0,int y1,int x){ + y0&=0x7fff; /* mask off flag */ + y1&=0x7fff; + + { + int dy=y1-y0; + int adx=x1-x0; + int ady=abs(dy); + int err=ady*(x-x0); + + int off=err/adx; + if(dy<0)return(y0-off); + return(y0+off); + } +} + +static int vorbis_dBquant(const float *x){ + int i= *x*7.3142857f+1023.5f; + if(i>1023)return(1023); + if(i<0)return(0); + return i; +} + +static float FLOOR1_fromdB_LOOKUP[256]={ + 1.0649863e-07F, 1.1341951e-07F, 1.2079015e-07F, 1.2863978e-07F, + 1.3699951e-07F, 1.4590251e-07F, 1.5538408e-07F, 1.6548181e-07F, + 1.7623575e-07F, 1.8768855e-07F, 1.9988561e-07F, 2.128753e-07F, + 2.2670913e-07F, 2.4144197e-07F, 2.5713223e-07F, 2.7384213e-07F, + 2.9163793e-07F, 3.1059021e-07F, 3.3077411e-07F, 3.5226968e-07F, + 3.7516214e-07F, 3.9954229e-07F, 4.2550680e-07F, 4.5315863e-07F, + 4.8260743e-07F, 5.1396998e-07F, 5.4737065e-07F, 5.8294187e-07F, + 6.2082472e-07F, 6.6116941e-07F, 7.0413592e-07F, 7.4989464e-07F, + 7.9862701e-07F, 8.5052630e-07F, 9.0579828e-07F, 9.6466216e-07F, + 1.0273513e-06F, 1.0941144e-06F, 1.1652161e-06F, 1.2409384e-06F, + 1.3215816e-06F, 1.4074654e-06F, 1.4989305e-06F, 1.5963394e-06F, + 1.7000785e-06F, 1.8105592e-06F, 1.9282195e-06F, 2.0535261e-06F, + 2.1869758e-06F, 2.3290978e-06F, 2.4804557e-06F, 2.6416497e-06F, + 2.8133190e-06F, 2.9961443e-06F, 3.1908506e-06F, 3.3982101e-06F, + 3.6190449e-06F, 3.8542308e-06F, 4.1047004e-06F, 4.3714470e-06F, + 4.6555282e-06F, 4.9580707e-06F, 5.2802740e-06F, 5.6234160e-06F, + 5.9888572e-06F, 6.3780469e-06F, 6.7925283e-06F, 7.2339451e-06F, + 7.7040476e-06F, 8.2047000e-06F, 8.7378876e-06F, 9.3057248e-06F, + 9.9104632e-06F, 1.0554501e-05F, 1.1240392e-05F, 1.1970856e-05F, + 1.2748789e-05F, 1.3577278e-05F, 1.4459606e-05F, 1.5399272e-05F, + 1.6400004e-05F, 1.7465768e-05F, 1.8600792e-05F, 1.9809576e-05F, + 2.1096914e-05F, 2.2467911e-05F, 2.3928002e-05F, 2.5482978e-05F, + 2.7139006e-05F, 2.8902651e-05F, 3.0780908e-05F, 3.2781225e-05F, + 3.4911534e-05F, 3.7180282e-05F, 3.9596466e-05F, 4.2169667e-05F, + 4.4910090e-05F, 4.7828601e-05F, 5.0936773e-05F, 5.4246931e-05F, + 5.7772202e-05F, 6.1526565e-05F, 6.5524908e-05F, 6.9783085e-05F, + 7.4317983e-05F, 7.9147585e-05F, 8.4291040e-05F, 8.9768747e-05F, + 9.5602426e-05F, 0.00010181521F, 0.00010843174F, 0.00011547824F, + 0.00012298267F, 0.00013097477F, 0.00013948625F, 0.00014855085F, + 0.00015820453F, 0.00016848555F, 0.00017943469F, 0.00019109536F, + 0.00020351382F, 0.00021673929F, 0.00023082423F, 0.00024582449F, + 0.00026179955F, 0.00027881276F, 0.00029693158F, 0.00031622787F, + 0.00033677814F, 0.00035866388F, 0.00038197188F, 0.00040679456F, + 0.00043323036F, 0.00046138411F, 0.00049136745F, 0.00052329927F, + 0.00055730621F, 0.00059352311F, 0.00063209358F, 0.00067317058F, + 0.00071691700F, 0.00076350630F, 0.00081312324F, 0.00086596457F, + 0.00092223983F, 0.00098217216F, 0.0010459992F, 0.0011139742F, + 0.0011863665F, 0.0012634633F, 0.0013455702F, 0.0014330129F, + 0.0015261382F, 0.0016253153F, 0.0017309374F, 0.0018434235F, + 0.0019632195F, 0.0020908006F, 0.0022266726F, 0.0023713743F, + 0.0025254795F, 0.0026895994F, 0.0028643847F, 0.0030505286F, + 0.0032487691F, 0.0034598925F, 0.0036847358F, 0.0039241906F, + 0.0041792066F, 0.0044507950F, 0.0047400328F, 0.0050480668F, + 0.0053761186F, 0.0057254891F, 0.0060975636F, 0.0064938176F, + 0.0069158225F, 0.0073652516F, 0.0078438871F, 0.0083536271F, + 0.0088964928F, 0.009474637F, 0.010090352F, 0.010746080F, + 0.011444421F, 0.012188144F, 0.012980198F, 0.013823725F, + 0.014722068F, 0.015678791F, 0.016697687F, 0.017782797F, + 0.018938423F, 0.020169149F, 0.021479854F, 0.022875735F, + 0.024362330F, 0.025945531F, 0.027631618F, 0.029427276F, + 0.031339626F, 0.033376252F, 0.035545228F, 0.037855157F, + 0.040315199F, 0.042935108F, 0.045725273F, 0.048696758F, + 0.051861348F, 0.055231591F, 0.058820850F, 0.062643361F, + 0.066714279F, 0.071049749F, 0.075666962F, 0.080584227F, + 0.085821044F, 0.091398179F, 0.097337747F, 0.10366330F, + 0.11039993F, 0.11757434F, 0.12521498F, 0.13335215F, + 0.14201813F, 0.15124727F, 0.16107617F, 0.17154380F, + 0.18269168F, 0.19456402F, 0.20720788F, 0.22067342F, + 0.23501402F, 0.25028656F, 0.26655159F, 0.28387361F, + 0.30232132F, 0.32196786F, 0.34289114F, 0.36517414F, + 0.38890521F, 0.41417847F, 0.44109412F, 0.46975890F, + 0.50028648F, 0.53279791F, 0.56742212F, 0.60429640F, + 0.64356699F, 0.68538959F, 0.72993007F, 0.77736504F, + 0.82788260F, 0.88168307F, 0.9389798F, 1.F, +}; + +static void render_line(int n, int x0,int x1,int y0,int y1,float *d){ + int dy=y1-y0; + int adx=x1-x0; + int ady=abs(dy); + int base=dy/adx; + int sy=(dy<0?base-1:base+1); + int x=x0; + int y=y0; + int err=0; + + ady-=abs(base*adx); + + if(n>x1)n=x1; + + if(x=adx){ + err-=adx; + y+=sy; + }else{ + y+=base; + } + d[x]*=FLOOR1_fromdB_LOOKUP[y]; + } +} + +static void render_line0(int x0,int x1,int y0,int y1,int *d){ + int dy=y1-y0; + int adx=x1-x0; + int ady=abs(dy); + int base=dy/adx; + int sy=(dy<0?base-1:base+1); + int x=x0; + int y=y0; + int err=0; + + ady-=abs(base*adx); + + d[x]=y; + while(++x=adx){ + err-=adx; + y+=sy; + }else{ + y+=base; + } + d[x]=y; + } +} + +/* the floor has already been filtered to only include relevant sections */ +static int accumulate_fit(const float *flr,const float *mdct, + int x0, int x1,lsfit_acc *a, + int n,vorbis_info_floor1 *info){ + long i; + int quantized=vorbis_dBquant(flr+x0); + + long xa=0,ya=0,x2a=0,y2a=0,xya=0,na=0, xb=0,yb=0,x2b=0,y2b=0,xyb=0,nb=0; + + memset(a,0,sizeof(*a)); + a->x0=x0; + a->x1=x1; + if(x1>=n)x1=n-1; + + for(i=x0;i<=x1;i++){ + int quantized=vorbis_dBquant(flr+i); + if(quantized){ + if(mdct[i]+info->twofitatten>=flr[i]){ + xa += i; + ya += quantized; + x2a += i*i; + y2a += quantized*quantized; + xya += i*quantized; + na++; + }else{ + xb += i; + yb += quantized; + x2b += i*i; + y2b += quantized*quantized; + xyb += i*quantized; + nb++; + } + } + } + + xb+=xa; + yb+=ya; + x2b+=x2a; + y2b+=y2a; + xyb+=xya; + nb+=na; + + /* weight toward the actually used frequencies if we meet the threshhold */ + { + int weight=nb*info->twofitweight/(na+1); + + a->xa=xa*weight+xb; + a->ya=ya*weight+yb; + a->x2a=x2a*weight+x2b; + a->y2a=y2a*weight+y2b; + a->xya=xya*weight+xyb; + a->an=na*weight+nb; + } + + return(na); +} + +static void fit_line(lsfit_acc *a,int fits,int *y0,int *y1){ + long x=0,y=0,x2=0,y2=0,xy=0,an=0,i; + long x0=a[0].x0; + long x1=a[fits-1].x1; + + for(i=0;i=0){ + x+= x0; + y+= *y0; + x2+= x0 * x0; + y2+= *y0 * *y0; + xy+= *y0 * x0; + an++; + } + + if(*y1>=0){ + x+= x1; + y+= *y1; + x2+= x1 * x1; + y2+= *y1 * *y1; + xy+= *y1 * x1; + an++; + } + + if(an){ + /* need 64 bit multiplies, which C doesn't give portably as int */ + double fx=x; + double fy=y; + double fx2=x2; + double fxy=xy; + double denom=1./(an*fx2-fx*fx); + double a=(fy*fx2-fxy*fx)*denom; + double b=(an*fxy-fx*fy)*denom; + *y0=rint(a+b*x0); + *y1=rint(a+b*x1); + + /* limit to our range! */ + if(*y0>1023)*y0=1023; + if(*y1>1023)*y1=1023; + if(*y0<0)*y0=0; + if(*y1<0)*y1=0; + + }else{ + *y0=0; + *y1=0; + } +} + +/*static void fit_line_point(lsfit_acc *a,int fits,int *y0,int *y1){ + long y=0; + int i; + + for(i=0;itwofitatten>=mask[x]){ + if(y+info->maxovermaxunder>val)return(1); + } + + while(++x=adx){ + err-=adx; + y+=sy; + }else{ + y+=base; + } + + val=vorbis_dBquant(mask+x); + mse+=((y-val)*(y-val)); + n++; + if(mdct[x]+info->twofitatten>=mask[x]){ + if(val){ + if(y+info->maxovermaxunder>val)return(1); + } + } + } + + if(info->maxover*info->maxover/n>info->maxerr)return(0); + if(info->maxunder*info->maxunder/n>info->maxerr)return(0); + if(mse/n>info->maxerr)return(1); + return(0); +} + +static int post_Y(int *A,int *B,int pos){ + if(A[pos]<0) + return B[pos]; + if(B[pos]<0) + return A[pos]; + + return (A[pos]+B[pos])>>1; +} + +static int seq=0; + +int *floor1_fit(vorbis_block *vb,vorbis_look_floor1 *look, + const float *logmdct, /* in */ + const float *logmask){ + long i,j; + vorbis_info_floor1 *info=look->vi; + long n=look->n; + long posts=look->posts; + long nonzero=0; + lsfit_acc fits[VIF_POSIT+1]; + int fit_valueA[VIF_POSIT+2]; /* index by range list position */ + int fit_valueB[VIF_POSIT+2]; /* index by range list position */ + + int loneighbor[VIF_POSIT+2]; /* sorted index of range list position (+2) */ + int hineighbor[VIF_POSIT+2]; + int *output=NULL; + int memo[VIF_POSIT+2]; + + for(i=0;isorted_index[i], + look->sorted_index[i+1],fits+i, + n,info); + } + + if(nonzero){ + /* start by fitting the implicit base case.... */ + int y0=-200; + int y1=-200; + fit_line(fits,posts-1,&y0,&y1); + + fit_valueA[0]=y0; + fit_valueB[0]=y0; + fit_valueB[1]=y1; + fit_valueA[1]=y1; + + /* Non degenerate case */ + /* start progressive splitting. This is a greedy, non-optimal + algorithm, but simple and close enough to the best + answer. */ + for(i=2;ireverse_index[i]; + int ln=loneighbor[sortpos]; + int hn=hineighbor[sortpos]; + + /* eliminate repeat searches of a particular range with a memo */ + if(memo[ln]!=hn){ + /* haven't performed this error search yet */ + int lsortpos=look->reverse_index[ln]; + int hsortpos=look->reverse_index[hn]; + memo[ln]=hn; + + { + /* A note: we want to bound/minimize *local*, not global, error */ + int lx=info->postlist[ln]; + int hx=info->postlist[hn]; + int ly=post_Y(fit_valueA,fit_valueB,ln); + int hy=post_Y(fit_valueA,fit_valueB,hn); + + if(ly==-1 || hy==-1){ + exit(1); + } + + if(inspect_error(lx,hx,ly,hy,logmask,logmdct,info)){ + /* outside error bounds/begin search area. Split it. */ + int ly0=-200; + int ly1=-200; + int hy0=-200; + int hy1=-200; + fit_line(fits+lsortpos,sortpos-lsortpos,&ly0,&ly1); + fit_line(fits+sortpos,hsortpos-sortpos,&hy0,&hy1); + + /* store new edge values */ + fit_valueB[ln]=ly0; + if(ln==0)fit_valueA[ln]=ly0; + fit_valueA[i]=ly1; + fit_valueB[i]=hy0; + fit_valueA[hn]=hy1; + if(hn==1)fit_valueB[hn]=hy1; + + if(ly1>=0 || hy0>=0){ + /* store new neighbor values */ + for(j=sortpos-1;j>=0;j--) + if(hineighbor[j]==hn) + hineighbor[j]=i; + else + break; + for(j=sortpos+1;jloneighbor[i-2]; + int hn=look->hineighbor[i-2]; + int x0=info->postlist[ln]; + int x1=info->postlist[hn]; + int y0=output[ln]; + int y1=output[hn]; + + int predicted=render_point(x0,x1,y0,y1,info->postlist[i]); + int vx=post_Y(fit_valueA,fit_valueB,i); + + if(vx>=0 && predicted!=vx){ + output[i]=vx; + }else{ + output[i]= predicted|0x8000; + } + } + } + + return(output); + +} + +int *floor1_interpolate_fit(vorbis_block *vb,vorbis_look_floor1 *look, + int *A,int *B, + int del){ + + long i; + long posts=look->posts; + int *output=NULL; + + if(A && B){ + output=_vorbis_block_alloc(vb,sizeof(*output)*posts); + + for(i=0;i>16; + if(A[i]&0x8000 && B[i]&0x8000)output[i]|=0x8000; + } + } + + return(output); +} + + +int floor1_encode(oggpack_buffer *opb,vorbis_block *vb, + vorbis_look_floor1 *look, + int *post,int *ilogmask){ + + long i,j; + vorbis_info_floor1 *info=look->vi; + long n=look->n; + long posts=look->posts; + codec_setup_info *ci=vb->vd->vi->codec_setup; + int out[VIF_POSIT+2]; + static_codebook **sbooks=ci->book_param; + codebook *books=ci->fullbooks; + static long seq=0; + + /* quantize values to multiplier spec */ + if(post){ + for(i=0;imult){ + case 1: /* 1024 -> 256 */ + val>>=2; + break; + case 2: /* 1024 -> 128 */ + val>>=3; + break; + case 3: /* 1024 -> 86 */ + val/=12; + break; + case 4: /* 1024 -> 64 */ + val>>=4; + break; + } + post[i]=val | (post[i]&0x8000); + } + + out[0]=post[0]; + out[1]=post[1]; + + /* find prediction values for each post and subtract them */ + for(i=2;iloneighbor[i-2]; + int hn=look->hineighbor[i-2]; + int x0=info->postlist[ln]; + int x1=info->postlist[hn]; + int y0=post[ln]; + int y1=post[hn]; + + int predicted=render_point(x0,x1,y0,y1,info->postlist[i]); + + if((post[i]&0x8000) || (predicted==post[i])){ + post[i]=predicted|0x8000; /* in case there was roundoff jitter + in interpolation */ + out[i]=0; + }else{ + int headroom=(look->quant_q-predictedquant_q-predicted:predicted); + + int val=post[i]-predicted; + + /* at this point the 'deviation' value is in the range +/- max + range, but the real, unique range can always be mapped to + only [0-maxrange). So we want to wrap the deviation into + this limited range, but do it in the way that least screws + an essentially gaussian probability distribution. */ + + if(val<0) + if(val<-headroom) + val=headroom-val-1; + else + val=-1-(val<<1); + else + if(val>=headroom) + val= val+headroom; + else + val<<=1; + + out[i]=val; + post[ln]&=0x7fff; + post[hn]&=0x7fff; + } + } + + /* we have everything we need. pack it out */ + /* mark nontrivial floor */ + oggpack_write(opb,1,1); + + /* beginning/end post */ + look->frames++; + look->postbits+=ilog(look->quant_q-1)*2; + oggpack_write(opb,out[0],ilog(look->quant_q-1)); + oggpack_write(opb,out[1],ilog(look->quant_q-1)); + + + /* partition by partition */ + for(i=0,j=2;ipartitions;i++){ + int class=info->partitionclass[i]; + int cdim=info->class_dim[class]; + int csubbits=info->class_subs[class]; + int csub=1<class_subbook[class][k]; + if(booknum<0){ + maxval[k]=1; + }else{ + maxval[k]=sbooks[info->class_subbook[class][k]]->entries; + } + } + for(k=0;kphrasebits+= + vorbis_book_encode(books+info->class_book[class],cval,opb); + +#ifdef TRAIN_FLOOR1 + { + FILE *of; + char buffer[80]; + sprintf(buffer,"line_%dx%ld_class%d.vqd", + vb->pcmend/2,posts-2,class); + of=fopen(buffer,"a"); + fprintf(of,"%d\n",cval); + fclose(of); + } +#endif + } + + /* write post values */ + for(k=0;kclass_subbook[class][bookas[k]]; + if(book>=0){ + /* hack to allow training with 'bad' books */ + if(out[j+k]<(books+book)->entries) + look->postbits+=vorbis_book_encode(books+book, + out[j+k],opb); + /*else + fprintf(stderr,"+!");*/ + +#ifdef TRAIN_FLOOR1 + { + FILE *of; + char buffer[80]; + sprintf(buffer,"line_%dx%ld_%dsub%d.vqd", + vb->pcmend/2,posts-2,class,bookas[k]); + of=fopen(buffer,"a"); + fprintf(of,"%d\n",out[j+k]); + fclose(of); + } +#endif + } + } + j+=cdim; + } + + { + /* generate quantized floor equivalent to what we'd unpack in decode */ + /* render the lines */ + int hx=0; + int lx=0; + int ly=post[0]*info->mult; + for(j=1;jposts;j++){ + int current=look->forward_index[j]; + int hy=post[current]&0x7fff; + if(hy==post[current]){ + + hy*=info->mult; + hx=info->postlist[current]; + + render_line0(lx,hx,ly,hy,ilogmask); + + lx=hx; + ly=hy; + } + } + for(j=hx;jpcmend/2;j++)ilogmask[j]=ly; /* be certain */ + seq++; + return(1); + } + }else{ + oggpack_write(opb,0,1); + memset(ilogmask,0,vb->pcmend/2*sizeof(*ilogmask)); + seq++; + return(0); + } +} + +static void *floor1_inverse1(vorbis_block *vb,vorbis_look_floor *in){ + vorbis_look_floor1 *look=(vorbis_look_floor1 *)in; + vorbis_info_floor1 *info=look->vi; + codec_setup_info *ci=vb->vd->vi->codec_setup; + + int i,j,k; + codebook *books=ci->fullbooks; + + /* unpack wrapped/predicted values from stream */ + if(oggpack_read(&vb->opb,1)==1){ + int *fit_value=_vorbis_block_alloc(vb,(look->posts)*sizeof(*fit_value)); + + fit_value[0]=oggpack_read(&vb->opb,ilog(look->quant_q-1)); + fit_value[1]=oggpack_read(&vb->opb,ilog(look->quant_q-1)); + + /* partition by partition */ + for(i=0,j=2;ipartitions;i++){ + int class=info->partitionclass[i]; + int cdim=info->class_dim[class]; + int csubbits=info->class_subs[class]; + int csub=1<class_book[class],&vb->opb); + + if(cval==-1)goto eop; + } + + for(k=0;kclass_subbook[class][cval&(csub-1)]; + cval>>=csubbits; + if(book>=0){ + if((fit_value[j+k]=vorbis_book_decode(books+book,&vb->opb))==-1) + goto eop; + }else{ + fit_value[j+k]=0; + } + } + j+=cdim; + } + + /* unwrap positive values and reconsitute via linear interpolation */ + for(i=2;iposts;i++){ + int predicted=render_point(info->postlist[look->loneighbor[i-2]], + info->postlist[look->hineighbor[i-2]], + fit_value[look->loneighbor[i-2]], + fit_value[look->hineighbor[i-2]], + info->postlist[i]); + int hiroom=look->quant_q-predicted; + int loroom=predicted; + int room=(hiroom=room){ + if(hiroom>loroom){ + val = val-loroom; + }else{ + val = -1-(val-hiroom); + } + }else{ + if(val&1){ + val= -((val+1)>>1); + }else{ + val>>=1; + } + } + + fit_value[i]=val+predicted; + fit_value[look->loneighbor[i-2]]&=0x7fff; + fit_value[look->hineighbor[i-2]]&=0x7fff; + + }else{ + fit_value[i]=predicted|0x8000; + } + + } + + return(fit_value); + } + eop: + return(NULL); +} + +static int floor1_inverse2(vorbis_block *vb,vorbis_look_floor *in,void *memo, + float *out){ + vorbis_look_floor1 *look=(vorbis_look_floor1 *)in; + vorbis_info_floor1 *info=look->vi; + + codec_setup_info *ci=vb->vd->vi->codec_setup; + int n=ci->blocksizes[vb->W]/2; + int j; + + if(memo){ + /* render the lines */ + int *fit_value=(int *)memo; + int hx=0; + int lx=0; + int ly=fit_value[0]*info->mult; + for(j=1;jposts;j++){ + int current=look->forward_index[j]; + int hy=fit_value[current]&0x7fff; + if(hy==fit_value[current]){ + + hy*=info->mult; + hx=info->postlist[current]; + + render_line(n,lx,hx,ly,hy,out); + + lx=hx; + ly=hy; + } + } + for(j=hx;j header packets + last mod: $Id: info.c 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +/* general handling of the header and the vorbis_info structure (and + substructures) */ + +#include +#include +#include +#include +#include "vorbis/codec.h" +#include "codec_internal.h" +#include "codebook.h" +#include "registry.h" +#include "window.h" +#include "psy.h" +#include "misc.h" +#include "os.h" + +/* helpers */ +static int ilog2(unsigned int v){ + int ret=0; + if(v)--v; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +static void _v_writestring(oggpack_buffer *o,char *s, int bytes){ + + while(bytes--){ + oggpack_write(o,*s++,8); + } +} + +static void _v_readstring(oggpack_buffer *o,char *buf,int bytes){ + while(bytes--){ + *buf++=oggpack_read(o,8); + } +} + +void vorbis_comment_init(vorbis_comment *vc){ + memset(vc,0,sizeof(*vc)); +} + +void vorbis_comment_add(vorbis_comment *vc,char *comment){ + vc->user_comments=_ogg_realloc(vc->user_comments, + (vc->comments+2)*sizeof(*vc->user_comments)); + vc->comment_lengths=_ogg_realloc(vc->comment_lengths, + (vc->comments+2)*sizeof(*vc->comment_lengths)); + vc->comment_lengths[vc->comments]=strlen(comment); + vc->user_comments[vc->comments]=_ogg_malloc(vc->comment_lengths[vc->comments]+1); + strcpy(vc->user_comments[vc->comments], comment); + vc->comments++; + vc->user_comments[vc->comments]=NULL; +} + +void vorbis_comment_add_tag(vorbis_comment *vc, char *tag, char *contents){ + char *comment=alloca(strlen(tag)+strlen(contents)+2); /* +2 for = and \0 */ + strcpy(comment, tag); + strcat(comment, "="); + strcat(comment, contents); + vorbis_comment_add(vc, comment); +} + +/* This is more or less the same as strncasecmp - but that doesn't exist + * everywhere, and this is a fairly trivial function, so we include it */ +static int tagcompare(const char *s1, const char *s2, int n){ + int c=0; + while(c < n){ + if(toupper(s1[c]) != toupper(s2[c])) + return !0; + c++; + } + return 0; +} + +char *vorbis_comment_query(vorbis_comment *vc, char *tag, int count){ + long i; + int found = 0; + int taglen = strlen(tag)+1; /* +1 for the = we append */ + char *fulltag = alloca(taglen+ 1); + + strcpy(fulltag, tag); + strcat(fulltag, "="); + + for(i=0;icomments;i++){ + if(!tagcompare(vc->user_comments[i], fulltag, taglen)){ + if(count == found) + /* We return a pointer to the data, not a copy */ + return vc->user_comments[i] + taglen; + else + found++; + } + } + return NULL; /* didn't find anything */ +} + +int vorbis_comment_query_count(vorbis_comment *vc, char *tag){ + int i,count=0; + int taglen = strlen(tag)+1; /* +1 for the = we append */ + char *fulltag = alloca(taglen+1); + strcpy(fulltag,tag); + strcat(fulltag, "="); + + for(i=0;icomments;i++){ + if(!tagcompare(vc->user_comments[i], fulltag, taglen)) + count++; + } + + return count; +} + +void vorbis_comment_clear(vorbis_comment *vc){ + if(vc){ + long i; + for(i=0;icomments;i++) + if(vc->user_comments[i])_ogg_free(vc->user_comments[i]); + if(vc->user_comments)_ogg_free(vc->user_comments); + if(vc->comment_lengths)_ogg_free(vc->comment_lengths); + if(vc->vendor)_ogg_free(vc->vendor); + memset(vc,0,sizeof(*vc)); + } +} + +/* blocksize 0 is guaranteed to be short, 1 is guaranteed to be long. + They may be equal, but short will never ge greater than long */ +int vorbis_info_blocksize(vorbis_info *vi,int zo){ + codec_setup_info *ci = vi->codec_setup; + return ci ? ci->blocksizes[zo] : -1; +} + +/* used by synthesis, which has a full, alloced vi */ +void vorbis_info_init(vorbis_info *vi){ + memset(vi,0,sizeof(*vi)); + vi->codec_setup=_ogg_calloc(1,sizeof(codec_setup_info)); +} + +void vorbis_info_clear(vorbis_info *vi){ + codec_setup_info *ci=vi->codec_setup; + int i; + + if(ci){ + + for(i=0;imodes;i++) + if(ci->mode_param[i])_ogg_free(ci->mode_param[i]); + + for(i=0;imaps;i++) /* unpack does the range checking */ + if(ci->map_param[i]) /* this may be cleaning up an aborted + unpack, in which case the below type + cannot be trusted */ + _mapping_P[ci->map_type[i]]->free_info(ci->map_param[i]); + + for(i=0;ifloors;i++) /* unpack does the range checking */ + if(ci->floor_param[i]) /* this may be cleaning up an aborted + unpack, in which case the below type + cannot be trusted */ + _floor_P[ci->floor_type[i]]->free_info(ci->floor_param[i]); + + for(i=0;iresidues;i++) /* unpack does the range checking */ + if(ci->residue_param[i]) /* this may be cleaning up an aborted + unpack, in which case the below type + cannot be trusted */ + _residue_P[ci->residue_type[i]]->free_info(ci->residue_param[i]); + + for(i=0;ibooks;i++){ + if(ci->book_param[i]){ + /* knows if the book was not alloced */ + vorbis_staticbook_destroy(ci->book_param[i]); + } + if(ci->fullbooks) + vorbis_book_clear(ci->fullbooks+i); + } + if(ci->fullbooks) + _ogg_free(ci->fullbooks); + + for(i=0;ipsys;i++) + _vi_psy_free(ci->psy_param[i]); + + _ogg_free(ci); + } + + memset(vi,0,sizeof(*vi)); +} + +/* Header packing/unpacking ********************************************/ + +static int _vorbis_unpack_info(vorbis_info *vi,oggpack_buffer *opb){ + codec_setup_info *ci=vi->codec_setup; + if(!ci)return(OV_EFAULT); + + vi->version=oggpack_read(opb,32); + if(vi->version!=0)return(OV_EVERSION); + + vi->channels=oggpack_read(opb,8); + vi->rate=oggpack_read(opb,32); + + vi->bitrate_upper=oggpack_read(opb,32); + vi->bitrate_nominal=oggpack_read(opb,32); + vi->bitrate_lower=oggpack_read(opb,32); + + ci->blocksizes[0]=1<blocksizes[1]=1<rate<1)goto err_out; + if(vi->channels<1)goto err_out; + if(ci->blocksizes[0]<64)goto err_out; + if(ci->blocksizes[1]blocksizes[0])goto err_out; + if(ci->blocksizes[1]>8192)goto err_out; + + if(oggpack_read(opb,1)!=1)goto err_out; /* EOP check */ + + return(0); + err_out: + vorbis_info_clear(vi); + return(OV_EBADHEADER); +} + +static int _vorbis_unpack_comment(vorbis_comment *vc,oggpack_buffer *opb){ + int i; + int vendorlen=oggpack_read(opb,32); + if(vendorlen<0)goto err_out; + vc->vendor=_ogg_calloc(vendorlen+1,1); + _v_readstring(opb,vc->vendor,vendorlen); + vc->comments=oggpack_read(opb,32); + if(vc->comments<0)goto err_out; + vc->user_comments=_ogg_calloc(vc->comments+1,sizeof(*vc->user_comments)); + vc->comment_lengths=_ogg_calloc(vc->comments+1, sizeof(*vc->comment_lengths)); + + for(i=0;icomments;i++){ + int len=oggpack_read(opb,32); + if(len<0)goto err_out; + vc->comment_lengths[i]=len; + vc->user_comments[i]=_ogg_calloc(len+1,1); + _v_readstring(opb,vc->user_comments[i],len); + } + if(oggpack_read(opb,1)!=1)goto err_out; /* EOP check */ + + return(0); + err_out: + vorbis_comment_clear(vc); + return(OV_EBADHEADER); +} + +/* all of the real encoding details are here. The modes, books, + everything */ +static int _vorbis_unpack_books(vorbis_info *vi,oggpack_buffer *opb){ + codec_setup_info *ci=vi->codec_setup; + int i; + if(!ci)return(OV_EFAULT); + + /* codebooks */ + ci->books=oggpack_read(opb,8)+1; + /*ci->book_param=_ogg_calloc(ci->books,sizeof(*ci->book_param));*/ + for(i=0;ibooks;i++){ + ci->book_param[i]=_ogg_calloc(1,sizeof(*ci->book_param[i])); + if(vorbis_staticbook_unpack(opb,ci->book_param[i]))goto err_out; + } + + /* time backend settings; hooks are unused */ + { + int times=oggpack_read(opb,6)+1; + for(i=0;i=VI_TIMEB)goto err_out; + } + } + + /* floor backend settings */ + ci->floors=oggpack_read(opb,6)+1; + /*ci->floor_type=_ogg_malloc(ci->floors*sizeof(*ci->floor_type));*/ + /*ci->floor_param=_ogg_calloc(ci->floors,sizeof(void *));*/ + for(i=0;ifloors;i++){ + ci->floor_type[i]=oggpack_read(opb,16); + if(ci->floor_type[i]<0 || ci->floor_type[i]>=VI_FLOORB)goto err_out; + ci->floor_param[i]=_floor_P[ci->floor_type[i]]->unpack(vi,opb); + if(!ci->floor_param[i])goto err_out; + } + + /* residue backend settings */ + ci->residues=oggpack_read(opb,6)+1; + /*ci->residue_type=_ogg_malloc(ci->residues*sizeof(*ci->residue_type));*/ + /*ci->residue_param=_ogg_calloc(ci->residues,sizeof(void *));*/ + for(i=0;iresidues;i++){ + ci->residue_type[i]=oggpack_read(opb,16); + if(ci->residue_type[i]<0 || ci->residue_type[i]>=VI_RESB)goto err_out; + ci->residue_param[i]=_residue_P[ci->residue_type[i]]->unpack(vi,opb); + if(!ci->residue_param[i])goto err_out; + } + + /* map backend settings */ + ci->maps=oggpack_read(opb,6)+1; + /*ci->map_type=_ogg_malloc(ci->maps*sizeof(*ci->map_type));*/ + /*ci->map_param=_ogg_calloc(ci->maps,sizeof(void *));*/ + for(i=0;imaps;i++){ + ci->map_type[i]=oggpack_read(opb,16); + if(ci->map_type[i]<0 || ci->map_type[i]>=VI_MAPB)goto err_out; + ci->map_param[i]=_mapping_P[ci->map_type[i]]->unpack(vi,opb); + if(!ci->map_param[i])goto err_out; + } + + /* mode settings */ + ci->modes=oggpack_read(opb,6)+1; + /*vi->mode_param=_ogg_calloc(vi->modes,sizeof(void *));*/ + for(i=0;imodes;i++){ + ci->mode_param[i]=_ogg_calloc(1,sizeof(*ci->mode_param[i])); + ci->mode_param[i]->blockflag=oggpack_read(opb,1); + ci->mode_param[i]->windowtype=oggpack_read(opb,16); + ci->mode_param[i]->transformtype=oggpack_read(opb,16); + ci->mode_param[i]->mapping=oggpack_read(opb,8); + + if(ci->mode_param[i]->windowtype>=VI_WINDOWB)goto err_out; + if(ci->mode_param[i]->transformtype>=VI_WINDOWB)goto err_out; + if(ci->mode_param[i]->mapping>=ci->maps)goto err_out; + } + + if(oggpack_read(opb,1)!=1)goto err_out; /* top level EOP check */ + + return(0); + err_out: + vorbis_info_clear(vi); + return(OV_EBADHEADER); +} + +/* Is this packet a vorbis ID header? */ +int vorbis_synthesis_idheader(ogg_packet *op){ + oggpack_buffer opb; + char buffer[6]; + + if(op){ + oggpack_readinit(&opb,op->packet,op->bytes); + + if(!op->b_o_s) + return(0); /* Not the initial packet */ + + if(oggpack_read(&opb,8) != 1) + return 0; /* not an ID header */ + + memset(buffer,0,6); + _v_readstring(&opb,buffer,6); + if(memcmp(buffer,"vorbis",6)) + return 0; /* not vorbis */ + + return 1; + } + + return 0; +} + +/* The Vorbis header is in three packets; the initial small packet in + the first page that identifies basic parameters, a second packet + with bitstream comments and a third packet that holds the + codebook. */ + +int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc,ogg_packet *op){ + oggpack_buffer opb; + + if(op){ + oggpack_readinit(&opb,op->packet,op->bytes); + + /* Which of the three types of header is this? */ + /* Also verify header-ness, vorbis */ + { + char buffer[6]; + int packtype=oggpack_read(&opb,8); + memset(buffer,0,6); + _v_readstring(&opb,buffer,6); + if(memcmp(buffer,"vorbis",6)){ + /* not a vorbis header */ + return(OV_ENOTVORBIS); + } + switch(packtype){ + case 0x01: /* least significant *bit* is read first */ + if(!op->b_o_s){ + /* Not the initial packet */ + return(OV_EBADHEADER); + } + if(vi->rate!=0){ + /* previously initialized info header */ + return(OV_EBADHEADER); + } + + return(_vorbis_unpack_info(vi,&opb)); + + case 0x03: /* least significant *bit* is read first */ + if(vi->rate==0){ + /* um... we didn't get the initial header */ + return(OV_EBADHEADER); + } + + return(_vorbis_unpack_comment(vc,&opb)); + + case 0x05: /* least significant *bit* is read first */ + if(vi->rate==0 || vc->vendor==NULL){ + /* um... we didn;t get the initial header or comments yet */ + return(OV_EBADHEADER); + } + + return(_vorbis_unpack_books(vi,&opb)); + + default: + /* Not a valid vorbis header type */ + return(OV_EBADHEADER); + break; + } + } + } + return(OV_EBADHEADER); +} + +/* pack side **********************************************************/ + +static int _vorbis_pack_info(oggpack_buffer *opb,vorbis_info *vi){ + codec_setup_info *ci=vi->codec_setup; + if(!ci)return(OV_EFAULT); + + /* preamble */ + oggpack_write(opb,0x01,8); + _v_writestring(opb,"vorbis", 6); + + /* basic information about the stream */ + oggpack_write(opb,0x00,32); + oggpack_write(opb,vi->channels,8); + oggpack_write(opb,vi->rate,32); + + oggpack_write(opb,vi->bitrate_upper,32); + oggpack_write(opb,vi->bitrate_nominal,32); + oggpack_write(opb,vi->bitrate_lower,32); + + oggpack_write(opb,ilog2(ci->blocksizes[0]),4); + oggpack_write(opb,ilog2(ci->blocksizes[1]),4); + oggpack_write(opb,1,1); + + return(0); +} + +static int _vorbis_pack_comment(oggpack_buffer *opb,vorbis_comment *vc){ + char temp[]="Xiph.Org libVorbis I 20070622"; + int bytes = strlen(temp); + + /* preamble */ + oggpack_write(opb,0x03,8); + _v_writestring(opb,"vorbis", 6); + + /* vendor */ + oggpack_write(opb,bytes,32); + _v_writestring(opb,temp, bytes); + + /* comments */ + + oggpack_write(opb,vc->comments,32); + if(vc->comments){ + int i; + for(i=0;icomments;i++){ + if(vc->user_comments[i]){ + oggpack_write(opb,vc->comment_lengths[i],32); + _v_writestring(opb,vc->user_comments[i], vc->comment_lengths[i]); + }else{ + oggpack_write(opb,0,32); + } + } + } + oggpack_write(opb,1,1); + + return(0); +} + +static int _vorbis_pack_books(oggpack_buffer *opb,vorbis_info *vi){ + codec_setup_info *ci=vi->codec_setup; + int i; + if(!ci)return(OV_EFAULT); + + oggpack_write(opb,0x05,8); + _v_writestring(opb,"vorbis", 6); + + /* books */ + oggpack_write(opb,ci->books-1,8); + for(i=0;ibooks;i++) + if(vorbis_staticbook_pack(ci->book_param[i],opb))goto err_out; + + /* times; hook placeholders */ + oggpack_write(opb,0,6); + oggpack_write(opb,0,16); + + /* floors */ + oggpack_write(opb,ci->floors-1,6); + for(i=0;ifloors;i++){ + oggpack_write(opb,ci->floor_type[i],16); + if(_floor_P[ci->floor_type[i]]->pack) + _floor_P[ci->floor_type[i]]->pack(ci->floor_param[i],opb); + else + goto err_out; + } + + /* residues */ + oggpack_write(opb,ci->residues-1,6); + for(i=0;iresidues;i++){ + oggpack_write(opb,ci->residue_type[i],16); + _residue_P[ci->residue_type[i]]->pack(ci->residue_param[i],opb); + } + + /* maps */ + oggpack_write(opb,ci->maps-1,6); + for(i=0;imaps;i++){ + oggpack_write(opb,ci->map_type[i],16); + _mapping_P[ci->map_type[i]]->pack(vi,ci->map_param[i],opb); + } + + /* modes */ + oggpack_write(opb,ci->modes-1,6); + for(i=0;imodes;i++){ + oggpack_write(opb,ci->mode_param[i]->blockflag,1); + oggpack_write(opb,ci->mode_param[i]->windowtype,16); + oggpack_write(opb,ci->mode_param[i]->transformtype,16); + oggpack_write(opb,ci->mode_param[i]->mapping,8); + } + oggpack_write(opb,1,1); + + return(0); +err_out: + return(-1); +} + +int vorbis_commentheader_out(vorbis_comment *vc, + ogg_packet *op){ + + oggpack_buffer opb; + + oggpack_writeinit(&opb); + if(_vorbis_pack_comment(&opb,vc)) return OV_EIMPL; + + op->packet = _ogg_malloc(oggpack_bytes(&opb)); + memcpy(op->packet, opb.buffer, oggpack_bytes(&opb)); + + op->bytes=oggpack_bytes(&opb); + op->b_o_s=0; + op->e_o_s=0; + op->granulepos=0; + op->packetno=1; + + return 0; +} + +int vorbis_analysis_headerout(vorbis_dsp_state *v, + vorbis_comment *vc, + ogg_packet *op, + ogg_packet *op_comm, + ogg_packet *op_code){ + int ret=OV_EIMPL; + vorbis_info *vi=v->vi; + oggpack_buffer opb; + private_state *b=v->backend_state; + + if(!b){ + ret=OV_EFAULT; + goto err_out; + } + + /* first header packet **********************************************/ + + oggpack_writeinit(&opb); + if(_vorbis_pack_info(&opb,vi))goto err_out; + + /* build the packet */ + if(b->header)_ogg_free(b->header); + b->header=_ogg_malloc(oggpack_bytes(&opb)); + memcpy(b->header,opb.buffer,oggpack_bytes(&opb)); + op->packet=b->header; + op->bytes=oggpack_bytes(&opb); + op->b_o_s=1; + op->e_o_s=0; + op->granulepos=0; + op->packetno=0; + + /* second header packet (comments) **********************************/ + + oggpack_reset(&opb); + if(_vorbis_pack_comment(&opb,vc))goto err_out; + + if(b->header1)_ogg_free(b->header1); + b->header1=_ogg_malloc(oggpack_bytes(&opb)); + memcpy(b->header1,opb.buffer,oggpack_bytes(&opb)); + op_comm->packet=b->header1; + op_comm->bytes=oggpack_bytes(&opb); + op_comm->b_o_s=0; + op_comm->e_o_s=0; + op_comm->granulepos=0; + op_comm->packetno=1; + + /* third header packet (modes/codebooks) ****************************/ + + oggpack_reset(&opb); + if(_vorbis_pack_books(&opb,vi))goto err_out; + + if(b->header2)_ogg_free(b->header2); + b->header2=_ogg_malloc(oggpack_bytes(&opb)); + memcpy(b->header2,opb.buffer,oggpack_bytes(&opb)); + op_code->packet=b->header2; + op_code->bytes=oggpack_bytes(&opb); + op_code->b_o_s=0; + op_code->e_o_s=0; + op_code->granulepos=0; + op_code->packetno=2; + + oggpack_writeclear(&opb); + return(0); + err_out: + oggpack_writeclear(&opb); + memset(op,0,sizeof(*op)); + memset(op_comm,0,sizeof(*op_comm)); + memset(op_code,0,sizeof(*op_code)); + + if(b){ + if(b->header)_ogg_free(b->header); + if(b->header1)_ogg_free(b->header1); + if(b->header2)_ogg_free(b->header2); + b->header=NULL; + b->header1=NULL; + b->header2=NULL; + } + return(ret); +} + +double vorbis_granule_time(vorbis_dsp_state *v,ogg_int64_t granulepos){ + if(granulepos>=0) + return((double)granulepos/v->vi->rate); + return(-1); +} diff --git a/Engine/lib/libvorbis/lib/lookup.c b/Engine/lib/libvorbis/lib/lookup.c new file mode 100644 index 000000000..06c53fee5 --- /dev/null +++ b/Engine/lib/libvorbis/lib/lookup.c @@ -0,0 +1,94 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: lookup based functions + last mod: $Id: lookup.c 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#include +#include "lookup.h" +#include "lookup_data.h" +#include "os.h" +#include "misc.h" + +#ifdef FLOAT_LOOKUP + +/* interpolated lookup based cos function, domain 0 to PI only */ +float vorbis_coslook(float a){ + double d=a*(.31830989*(float)COS_LOOKUP_SZ); + int i=vorbis_ftoi(d-.5); + + return COS_LOOKUP[i]+ (d-i)*(COS_LOOKUP[i+1]-COS_LOOKUP[i]); +} + +/* interpolated 1./sqrt(p) where .5 <= p < 1. */ +float vorbis_invsqlook(float a){ + double d=a*(2.f*(float)INVSQ_LOOKUP_SZ)-(float)INVSQ_LOOKUP_SZ; + int i=vorbis_ftoi(d-.5f); + return INVSQ_LOOKUP[i]+ (d-i)*(INVSQ_LOOKUP[i+1]-INVSQ_LOOKUP[i]); +} + +/* interpolated 1./sqrt(p) where .5 <= p < 1. */ +float vorbis_invsq2explook(int a){ + return INVSQ2EXP_LOOKUP[a-INVSQ2EXP_LOOKUP_MIN]; +} + +#include +/* interpolated lookup based fromdB function, domain -140dB to 0dB only */ +float vorbis_fromdBlook(float a){ + int i=vorbis_ftoi(a*((float)(-(1<=(FROMdB_LOOKUP_SZ<>FROMdB_SHIFT]*FROMdB2_LOOKUP[i&FROMdB2_MASK]); +} + +#endif + +#ifdef INT_LOOKUP +/* interpolated 1./sqrt(p) where .5 <= a < 1. (.100000... to .111111...) in + 16.16 format + + returns in m.8 format */ +long vorbis_invsqlook_i(long a,long e){ + long i=(a&0x7fff)>>(INVSQ_LOOKUP_I_SHIFT-1); + long d=(a&INVSQ_LOOKUP_I_MASK)<<(16-INVSQ_LOOKUP_I_SHIFT); /* 0.16 */ + long val=INVSQ_LOOKUP_I[i]- /* 1.16 */ + (((INVSQ_LOOKUP_I[i]-INVSQ_LOOKUP_I[i+1])* /* 0.16 */ + d)>>16); /* result 1.16 */ + + e+=32; + if(e&1)val=(val*5792)>>13; /* multiply val by 1/sqrt(2) */ + e=(e>>1)-8; + + return(val>>e); +} + +/* interpolated lookup based fromdB function, domain -140dB to 0dB only */ +/* a is in n.12 format */ +float vorbis_fromdBlook_i(long a){ + int i=(-a)>>(12-FROMdB2_SHIFT); + return (i<0)?1.f: + ((i>=(FROMdB_LOOKUP_SZ<>FROMdB_SHIFT]*FROMdB2_LOOKUP[i&FROMdB2_MASK]); +} + +/* interpolated lookup based cos function, domain 0 to PI only */ +/* a is in 0.16 format, where 0==0, 2^^16-1==PI, return 0.14 */ +long vorbis_coslook_i(long a){ + int i=a>>COS_LOOKUP_I_SHIFT; + int d=a&COS_LOOKUP_I_MASK; + return COS_LOOKUP_I[i]- ((d*(COS_LOOKUP_I[i]-COS_LOOKUP_I[i+1]))>> + COS_LOOKUP_I_SHIFT); +} + +#endif diff --git a/Engine/lib/libvorbis/lib/lookup.h b/Engine/lib/libvorbis/lib/lookup.h new file mode 100644 index 000000000..e9e22f285 --- /dev/null +++ b/Engine/lib/libvorbis/lib/lookup.h @@ -0,0 +1,32 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: lookup based functions + last mod: $Id: lookup.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#ifndef _V_LOOKUP_H_ + +#ifdef FLOAT_LOOKUP +extern float vorbis_coslook(float a); +extern float vorbis_invsqlook(float a); +extern float vorbis_invsq2explook(int a); +extern float vorbis_fromdBlook(float a); +#endif +#ifdef INT_LOOKUP +extern long vorbis_invsqlook_i(long a,long e); +extern long vorbis_coslook_i(long a); +extern float vorbis_fromdBlook_i(long a); +#endif + +#endif diff --git a/Engine/lib/libvorbis/lib/lookup_data.h b/Engine/lib/libvorbis/lib/lookup_data.h new file mode 100644 index 000000000..b3a022a5d --- /dev/null +++ b/Engine/lib/libvorbis/lib/lookup_data.h @@ -0,0 +1,189 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: lookup data; generated by lookups.pl; edit there + last mod: $Id: lookup_data.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#ifndef _V_LOOKUP_DATA_H_ + +#ifdef FLOAT_LOOKUP +#define COS_LOOKUP_SZ 128 +static float COS_LOOKUP[COS_LOOKUP_SZ+1]={ + +1.0000000000000f,+0.9996988186962f,+0.9987954562052f,+0.9972904566787f, + +0.9951847266722f,+0.9924795345987f,+0.9891765099648f,+0.9852776423889f, + +0.9807852804032f,+0.9757021300385f,+0.9700312531945f,+0.9637760657954f, + +0.9569403357322f,+0.9495281805930f,+0.9415440651830f,+0.9329927988347f, + +0.9238795325113f,+0.9142097557035f,+0.9039892931234f,+0.8932243011955f, + +0.8819212643484f,+0.8700869911087f,+0.8577286100003f,+0.8448535652497f, + +0.8314696123025f,+0.8175848131516f,+0.8032075314806f,+0.7883464276266f, + +0.7730104533627f,+0.7572088465065f,+0.7409511253550f,+0.7242470829515f, + +0.7071067811865f,+0.6895405447371f,+0.6715589548470f,+0.6531728429538f, + +0.6343932841636f,+0.6152315905806f,+0.5956993044924f,+0.5758081914178f, + +0.5555702330196f,+0.5349976198871f,+0.5141027441932f,+0.4928981922298f, + +0.4713967368260f,+0.4496113296546f,+0.4275550934303f,+0.4052413140050f, + +0.3826834323651f,+0.3598950365350f,+0.3368898533922f,+0.3136817403989f, + +0.2902846772545f,+0.2667127574749f,+0.2429801799033f,+0.2191012401569f, + +0.1950903220161f,+0.1709618887603f,+0.1467304744554f,+0.1224106751992f, + +0.0980171403296f,+0.0735645635997f,+0.0490676743274f,+0.0245412285229f, + +0.0000000000000f,-0.0245412285229f,-0.0490676743274f,-0.0735645635997f, + -0.0980171403296f,-0.1224106751992f,-0.1467304744554f,-0.1709618887603f, + -0.1950903220161f,-0.2191012401569f,-0.2429801799033f,-0.2667127574749f, + -0.2902846772545f,-0.3136817403989f,-0.3368898533922f,-0.3598950365350f, + -0.3826834323651f,-0.4052413140050f,-0.4275550934303f,-0.4496113296546f, + -0.4713967368260f,-0.4928981922298f,-0.5141027441932f,-0.5349976198871f, + -0.5555702330196f,-0.5758081914178f,-0.5956993044924f,-0.6152315905806f, + -0.6343932841636f,-0.6531728429538f,-0.6715589548470f,-0.6895405447371f, + -0.7071067811865f,-0.7242470829515f,-0.7409511253550f,-0.7572088465065f, + -0.7730104533627f,-0.7883464276266f,-0.8032075314806f,-0.8175848131516f, + -0.8314696123025f,-0.8448535652497f,-0.8577286100003f,-0.8700869911087f, + -0.8819212643484f,-0.8932243011955f,-0.9039892931234f,-0.9142097557035f, + -0.9238795325113f,-0.9329927988347f,-0.9415440651830f,-0.9495281805930f, + -0.9569403357322f,-0.9637760657954f,-0.9700312531945f,-0.9757021300385f, + -0.9807852804032f,-0.9852776423889f,-0.9891765099648f,-0.9924795345987f, + -0.9951847266722f,-0.9972904566787f,-0.9987954562052f,-0.9996988186962f, + -1.0000000000000f, +}; + +#define INVSQ_LOOKUP_SZ 32 +static float INVSQ_LOOKUP[INVSQ_LOOKUP_SZ+1]={ + 1.414213562373f,1.392621247646f,1.371988681140f,1.352246807566f, + 1.333333333333f,1.315191898443f,1.297771369046f,1.281025230441f, + 1.264911064067f,1.249390095109f,1.234426799697f,1.219988562661f, + 1.206045378311f,1.192569588000f,1.179535649239f,1.166919931983f, + 1.154700538379f,1.142857142857f,1.131370849898f,1.120224067222f, + 1.109400392450f,1.098884511590f,1.088662107904f,1.078719779941f, + 1.069044967650f,1.059625885652f,1.050451462878f,1.041511287847f, + 1.032795558989f,1.024295039463f,1.016001016002f,1.007905261358f, + 1.000000000000f, +}; + +#define INVSQ2EXP_LOOKUP_MIN (-32) +#define INVSQ2EXP_LOOKUP_MAX 32 +static float INVSQ2EXP_LOOKUP[INVSQ2EXP_LOOKUP_MAX-\ + INVSQ2EXP_LOOKUP_MIN+1]={ + 65536.f, 46340.95001f, 32768.f, 23170.47501f, + 16384.f, 11585.2375f, 8192.f, 5792.618751f, + 4096.f, 2896.309376f, 2048.f, 1448.154688f, + 1024.f, 724.0773439f, 512.f, 362.038672f, + 256.f, 181.019336f, 128.f, 90.50966799f, + 64.f, 45.254834f, 32.f, 22.627417f, + 16.f, 11.3137085f, 8.f, 5.656854249f, + 4.f, 2.828427125f, 2.f, 1.414213562f, + 1.f, 0.7071067812f, 0.5f, 0.3535533906f, + 0.25f, 0.1767766953f, 0.125f, 0.08838834765f, + 0.0625f, 0.04419417382f, 0.03125f, 0.02209708691f, + 0.015625f, 0.01104854346f, 0.0078125f, 0.005524271728f, + 0.00390625f, 0.002762135864f, 0.001953125f, 0.001381067932f, + 0.0009765625f, 0.000690533966f, 0.00048828125f, 0.000345266983f, + 0.000244140625f,0.0001726334915f,0.0001220703125f,8.631674575e-05f, + 6.103515625e-05f,4.315837288e-05f,3.051757812e-05f,2.157918644e-05f, + 1.525878906e-05f, +}; + +#endif + +#define FROMdB_LOOKUP_SZ 35 +#define FROMdB2_LOOKUP_SZ 32 +#define FROMdB_SHIFT 5 +#define FROMdB2_SHIFT 3 +#define FROMdB2_MASK 31 +static float FROMdB_LOOKUP[FROMdB_LOOKUP_SZ]={ + 1.f, 0.6309573445f, 0.3981071706f, 0.2511886432f, + 0.1584893192f, 0.1f, 0.06309573445f, 0.03981071706f, + 0.02511886432f, 0.01584893192f, 0.01f, 0.006309573445f, + 0.003981071706f, 0.002511886432f, 0.001584893192f, 0.001f, + 0.0006309573445f,0.0003981071706f,0.0002511886432f,0.0001584893192f, + 0.0001f,6.309573445e-05f,3.981071706e-05f,2.511886432e-05f, + 1.584893192e-05f, 1e-05f,6.309573445e-06f,3.981071706e-06f, + 2.511886432e-06f,1.584893192e-06f, 1e-06f,6.309573445e-07f, + 3.981071706e-07f,2.511886432e-07f,1.584893192e-07f, +}; + +static float FROMdB2_LOOKUP[FROMdB2_LOOKUP_SZ]={ + 0.9928302478f, 0.9786445908f, 0.9646616199f, 0.9508784391f, + 0.9372921937f, 0.92390007f, 0.9106992942f, 0.8976871324f, + 0.8848608897f, 0.8722179097f, 0.8597555737f, 0.8474713009f, + 0.835362547f, 0.8234268041f, 0.8116616003f, 0.8000644989f, + 0.7886330981f, 0.7773650302f, 0.7662579617f, 0.755309592f, + 0.7445176537f, 0.7338799116f, 0.7233941627f, 0.7130582353f, + 0.7028699885f, 0.6928273125f, 0.6829281272f, 0.6731703824f, + 0.6635520573f, 0.6540711597f, 0.6447257262f, 0.6355138211f, +}; + +#ifdef INT_LOOKUP + +#define INVSQ_LOOKUP_I_SHIFT 10 +#define INVSQ_LOOKUP_I_MASK 1023 +static long INVSQ_LOOKUP_I[64+1]={ + 92682l, 91966l, 91267l, 90583l, + 89915l, 89261l, 88621l, 87995l, + 87381l, 86781l, 86192l, 85616l, + 85051l, 84497l, 83953l, 83420l, + 82897l, 82384l, 81880l, 81385l, + 80899l, 80422l, 79953l, 79492l, + 79039l, 78594l, 78156l, 77726l, + 77302l, 76885l, 76475l, 76072l, + 75674l, 75283l, 74898l, 74519l, + 74146l, 73778l, 73415l, 73058l, + 72706l, 72359l, 72016l, 71679l, + 71347l, 71019l, 70695l, 70376l, + 70061l, 69750l, 69444l, 69141l, + 68842l, 68548l, 68256l, 67969l, + 67685l, 67405l, 67128l, 66855l, + 66585l, 66318l, 66054l, 65794l, + 65536l, +}; + +#define COS_LOOKUP_I_SHIFT 9 +#define COS_LOOKUP_I_MASK 511 +#define COS_LOOKUP_I_SZ 128 +static long COS_LOOKUP_I[COS_LOOKUP_I_SZ+1]={ + 16384l, 16379l, 16364l, 16340l, + 16305l, 16261l, 16207l, 16143l, + 16069l, 15986l, 15893l, 15791l, + 15679l, 15557l, 15426l, 15286l, + 15137l, 14978l, 14811l, 14635l, + 14449l, 14256l, 14053l, 13842l, + 13623l, 13395l, 13160l, 12916l, + 12665l, 12406l, 12140l, 11866l, + 11585l, 11297l, 11003l, 10702l, + 10394l, 10080l, 9760l, 9434l, + 9102l, 8765l, 8423l, 8076l, + 7723l, 7366l, 7005l, 6639l, + 6270l, 5897l, 5520l, 5139l, + 4756l, 4370l, 3981l, 3590l, + 3196l, 2801l, 2404l, 2006l, + 1606l, 1205l, 804l, 402l, + 0l, -401l, -803l, -1204l, + -1605l, -2005l, -2403l, -2800l, + -3195l, -3589l, -3980l, -4369l, + -4755l, -5138l, -5519l, -5896l, + -6269l, -6638l, -7004l, -7365l, + -7722l, -8075l, -8422l, -8764l, + -9101l, -9433l, -9759l, -10079l, + -10393l, -10701l, -11002l, -11296l, + -11584l, -11865l, -12139l, -12405l, + -12664l, -12915l, -13159l, -13394l, + -13622l, -13841l, -14052l, -14255l, + -14448l, -14634l, -14810l, -14977l, + -15136l, -15285l, -15425l, -15556l, + -15678l, -15790l, -15892l, -15985l, + -16068l, -16142l, -16206l, -16260l, + -16304l, -16339l, -16363l, -16378l, + -16383l, +}; + +#endif + +#endif diff --git a/Engine/lib/libvorbis/lib/lpc.c b/Engine/lib/libvorbis/lib/lpc.c new file mode 100644 index 000000000..ea2cf90ff --- /dev/null +++ b/Engine/lib/libvorbis/lib/lpc.c @@ -0,0 +1,149 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: LPC low level routines + last mod: $Id: lpc.c 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +/* Some of these routines (autocorrelator, LPC coefficient estimator) + are derived from code written by Jutta Degener and Carsten Bormann; + thus we include their copyright below. The entirety of this file + is freely redistributable on the condition that both of these + copyright notices are preserved without modification. */ + +/* Preserved Copyright: *********************************************/ + +/* Copyright 1992, 1993, 1994 by Jutta Degener and Carsten Bormann, +Technische Universita"t Berlin + +Any use of this software is permitted provided that this notice is not +removed and that neither the authors nor the Technische Universita"t +Berlin are deemed to have made any representations as to the +suitability of this software for any purpose nor are held responsible +for any defects of this software. THERE IS ABSOLUTELY NO WARRANTY FOR +THIS SOFTWARE. + +As a matter of courtesy, the authors request to be informed about uses +this software has found, about bugs in this software, and about any +improvements that may be of general interest. + +Berlin, 28.11.1994 +Jutta Degener +Carsten Bormann + +*********************************************************************/ + +#include +#include +#include +#include "os.h" +#include "smallft.h" +#include "lpc.h" +#include "scales.h" +#include "misc.h" + +/* Autocorrelation LPC coeff generation algorithm invented by + N. Levinson in 1947, modified by J. Durbin in 1959. */ + +/* Input : n elements of time doamin data + Output: m lpc coefficients, excitation energy */ + +float vorbis_lpc_from_data(float *data,float *lpci,int n,int m){ + double *aut=alloca(sizeof(*aut)*(m+1)); + double *lpc=alloca(sizeof(*lpc)*(m)); + double error; + int i,j; + + /* autocorrelation, p+1 lag coefficients */ + j=m+1; + while(j--){ + double d=0; /* double needed for accumulator depth */ + for(i=j;i +#include +#include +#include "lsp.h" +#include "os.h" +#include "misc.h" +#include "lookup.h" +#include "scales.h" + +/* three possible LSP to f curve functions; the exact computation + (float), a lookup based float implementation, and an integer + implementation. The float lookup is likely the optimal choice on + any machine with an FPU. The integer implementation is *not* fixed + point (due to the need for a large dynamic range and thus a + seperately tracked exponent) and thus much more complex than the + relatively simple float implementations. It's mostly for future + work on a fully fixed point implementation for processors like the + ARM family. */ + +/* undefine both for the 'old' but more precise implementation */ +#define FLOAT_LOOKUP +#undef INT_LOOKUP + +#ifdef FLOAT_LOOKUP +#include "lookup.c" /* catch this in the build system; we #include for + compilers (like gcc) that can't inline across + modules */ + +/* side effect: changes *lsp to cosines of lsp */ +void vorbis_lsp_to_curve(float *curve,int *map,int n,int ln,float *lsp,int m, + float amp,float ampoffset){ + int i; + float wdel=M_PI/ln; + vorbis_fpu_control fpu; + + vorbis_fpu_setround(&fpu); + for(i=0;i>1; + + do{ + q*=ftmp[0]-w; + p*=ftmp[1]-w; + ftmp+=2; + }while(--c); + + if(m&1){ + /* odd order filter; slightly assymetric */ + /* the last coefficient */ + q*=ftmp[0]-w; + q*=q; + p*=p*(1.f-w*w); + }else{ + /* even order filter; still symmetric */ + q*=q*(1.f+w); + p*=p*(1.f-w); + } + + q=frexp(p+q,&qexp); + q=vorbis_fromdBlook(amp* + vorbis_invsqlook(q)* + vorbis_invsq2explook(qexp+m)- + ampoffset); + + do{ + curve[i++]*=q; + }while(map[i]==k); + } + vorbis_fpu_restore(fpu); +} + +#else + +#ifdef INT_LOOKUP +#include "lookup.c" /* catch this in the build system; we #include for + compilers (like gcc) that can't inline across + modules */ + +static int MLOOP_1[64]={ + 0,10,11,11, 12,12,12,12, 13,13,13,13, 13,13,13,13, + 14,14,14,14, 14,14,14,14, 14,14,14,14, 14,14,14,14, + 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, + 15,15,15,15, 15,15,15,15, 15,15,15,15, 15,15,15,15, +}; + +static int MLOOP_2[64]={ + 0,4,5,5, 6,6,6,6, 7,7,7,7, 7,7,7,7, + 8,8,8,8, 8,8,8,8, 8,8,8,8, 8,8,8,8, + 9,9,9,9, 9,9,9,9, 9,9,9,9, 9,9,9,9, + 9,9,9,9, 9,9,9,9, 9,9,9,9, 9,9,9,9, +}; + +static int MLOOP_3[8]={0,1,2,2,3,3,3,3}; + + +/* side effect: changes *lsp to cosines of lsp */ +void vorbis_lsp_to_curve(float *curve,int *map,int n,int ln,float *lsp,int m, + float amp,float ampoffset){ + + /* 0 <= m < 256 */ + + /* set up for using all int later */ + int i; + int ampoffseti=rint(ampoffset*4096.f); + int ampi=rint(amp*16.f); + long *ilsp=alloca(m*sizeof(*ilsp)); + for(i=0;i>25])) + if(!(shift=MLOOP_2[(pi|qi)>>19])) + shift=MLOOP_3[(pi|qi)>>16]; + qi=(qi>>shift)*labs(ilsp[j-1]-wi); + pi=(pi>>shift)*labs(ilsp[j]-wi); + qexp+=shift; + } + if(!(shift=MLOOP_1[(pi|qi)>>25])) + if(!(shift=MLOOP_2[(pi|qi)>>19])) + shift=MLOOP_3[(pi|qi)>>16]; + + /* pi,qi normalized collectively, both tracked using qexp */ + + if(m&1){ + /* odd order filter; slightly assymetric */ + /* the last coefficient */ + qi=(qi>>shift)*labs(ilsp[j-1]-wi); + pi=(pi>>shift)<<14; + qexp+=shift; + + if(!(shift=MLOOP_1[(pi|qi)>>25])) + if(!(shift=MLOOP_2[(pi|qi)>>19])) + shift=MLOOP_3[(pi|qi)>>16]; + + pi>>=shift; + qi>>=shift; + qexp+=shift-14*((m+1)>>1); + + pi=((pi*pi)>>16); + qi=((qi*qi)>>16); + qexp=qexp*2+m; + + pi*=(1<<14)-((wi*wi)>>14); + qi+=pi>>14; + + }else{ + /* even order filter; still symmetric */ + + /* p*=p(1-w), q*=q(1+w), let normalization drift because it isn't + worth tracking step by step */ + + pi>>=shift; + qi>>=shift; + qexp+=shift-7*m; + + pi=((pi*pi)>>16); + qi=((qi*qi)>>16); + qexp=qexp*2+m; + + pi*=(1<<14)-wi; + qi*=(1<<14)+wi; + qi=(qi+pi)>>14; + + } + + + /* we've let the normalization drift because it wasn't important; + however, for the lookup, things must be normalized again. We + need at most one right shift or a number of left shifts */ + + if(qi&0xffff0000){ /* checks for 1.xxxxxxxxxxxxxxxx */ + qi>>=1; qexp++; + }else + while(qi && !(qi&0x8000)){ /* checks for 0.0xxxxxxxxxxxxxxx or less*/ + qi<<=1; qexp--; + } + + amp=vorbis_fromdBlook_i(ampi* /* n.4 */ + vorbis_invsqlook_i(qi,qexp)- + /* m.8, m+n<=8 */ + ampoffseti); /* 8.12[0] */ + + curve[i]*=amp; + while(map[++i]==k)curve[i]*=amp; + } +} + +#else + +/* old, nonoptimized but simple version for any poor sap who needs to + figure out what the hell this code does, or wants the other + fraction of a dB precision */ + +/* side effect: changes *lsp to cosines of lsp */ +void vorbis_lsp_to_curve(float *curve,int *map,int n,int ln,float *lsp,int m, + float amp,float ampoffset){ + int i; + float wdel=M_PI/ln; + for(i=0;i= i; j--) { + g[j-2] -= g[j]; + g[j] += g[j]; + } + } +} + +static int comp(const void *a,const void *b){ + return (*(float *)a<*(float *)b)-(*(float *)a>*(float *)b); +} + +/* Newton-Raphson-Maehly actually functioned as a decent root finder, + but there are root sets for which it gets into limit cycles + (exacerbated by zero suppression) and fails. We can't afford to + fail, even if the failure is 1 in 100,000,000, so we now use + Laguerre and later polish with Newton-Raphson (which can then + afford to fail) */ + +#define EPSILON 10e-7 +static int Laguerre_With_Deflation(float *a,int ord,float *r){ + int i,m; + double lastdelta=0.f; + double *defl=alloca(sizeof(*defl)*(ord+1)); + for(i=0;i<=ord;i++)defl[i]=a[i]; + + for(m=ord;m>0;m--){ + double new=0.f,delta; + + /* iterate a root */ + while(1){ + double p=defl[m],pp=0.f,ppp=0.f,denom; + + /* eval the polynomial and its first two derivatives */ + for(i=m;i>0;i--){ + ppp = new*ppp + pp; + pp = new*pp + p; + p = new*p + defl[i-1]; + } + + /* Laguerre's method */ + denom=(m-1) * ((m-1)*pp*pp - m*p*ppp); + if(denom<0) + return(-1); /* complex root! The LPC generator handed us a bad filter */ + + if(pp>0){ + denom = pp + sqrt(denom); + if(denom-(EPSILON))denom=-(EPSILON); + } + + delta = m*p/denom; + new -= delta; + + if(delta<0.f)delta*=-1; + + if(fabs(delta/new)<10e-12)break; + lastdelta=delta; + } + + r[m-1]=new; + + /* forward deflation */ + + for(i=m;i>0;i--) + defl[i-1]+=new*defl[i]; + defl++; + + } + return(0); +} + + +/* for spit-and-polish only */ +static int Newton_Raphson(float *a,int ord,float *r){ + int i, k, count=0; + double error=1.f; + double *root=alloca(ord*sizeof(*root)); + + for(i=0; i1e-20){ + error=0; + + for(i=0; i= 0; k--) { + + pp= pp* rooti + p; + p = p * rooti + a[k]; + } + + delta = p/pp; + root[i] -= delta; + error+= delta*delta; + } + + if(count>40)return(-1); + + count++; + } + + /* Replaced the original bubble sort with a real sort. With your + help, we can eliminate the bubble sort in our lifetime. --Monty */ + + for(i=0; i>1; + int g1_order,g2_order; + float *g1=alloca(sizeof(*g1)*(order2+1)); + float *g2=alloca(sizeof(*g2)*(order2+1)); + float *g1r=alloca(sizeof(*g1r)*(order2+1)); + float *g2r=alloca(sizeof(*g2r)*(order2+1)); + int i; + + /* even and odd are slightly different base cases */ + g1_order=(m+1)>>1; + g2_order=(m) >>1; + + /* Compute the lengths of the x polynomials. */ + /* Compute the first half of K & R F1 & F2 polynomials. */ + /* Compute half of the symmetric and antisymmetric polynomials. */ + /* Remove the roots at +1 and -1. */ + + g1[g1_order] = 1.f; + for(i=1;i<=g1_order;i++) g1[g1_order-i] = lpc[i-1]+lpc[m-i]; + g2[g2_order] = 1.f; + for(i=1;i<=g2_order;i++) g2[g2_order-i] = lpc[i-1]-lpc[m-i]; + + if(g1_order>g2_order){ + for(i=2; i<=g2_order;i++) g2[g2_order-i] += g2[g2_order-i+2]; + }else{ + for(i=1; i<=g1_order;i++) g1[g1_order-i] -= g1[g1_order-i+1]; + for(i=1; i<=g2_order;i++) g2[g2_order-i] += g2[g2_order-i+1]; + } + + /* Convert into polynomials in cos(alpha) */ + cheby(g1,g1_order); + cheby(g2,g2_order); + + /* Find the roots of the 2 even polynomials.*/ + if(Laguerre_With_Deflation(g1,g1_order,g1r) || + Laguerre_With_Deflation(g2,g2_order,g2r)) + return(-1); + + Newton_Raphson(g1,g1_order,g1r); /* if it fails, it leaves g1r alone */ + Newton_Raphson(g2,g2_order,g2r); /* if it fails, it leaves g2r alone */ + + qsort(g1r,g1_order,sizeof(*g1r),comp); + qsort(g2r,g2_order,sizeof(*g2r),comp); + + for(i=0;i +#include +#include +#include +#include +#include "vorbis/codec.h" +#include "codec_internal.h" +#include "codebook.h" +#include "window.h" +#include "registry.h" +#include "psy.h" +#include "misc.h" + +/* simplistic, wasteful way of doing this (unique lookup for each + mode/submapping); there should be a central repository for + identical lookups. That will require minor work, so I'm putting it + off as low priority. + + Why a lookup for each backend in a given mode? Because the + blocksize is set by the mode, and low backend lookups may require + parameters from other areas of the mode/mapping */ + +static void mapping0_free_info(vorbis_info_mapping *i){ + vorbis_info_mapping0 *info=(vorbis_info_mapping0 *)i; + if(info){ + memset(info,0,sizeof(*info)); + _ogg_free(info); + } +} + +static int ilog(unsigned int v){ + int ret=0; + if(v)--v; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +static void mapping0_pack(vorbis_info *vi,vorbis_info_mapping *vm, + oggpack_buffer *opb){ + int i; + vorbis_info_mapping0 *info=(vorbis_info_mapping0 *)vm; + + /* another 'we meant to do it this way' hack... up to beta 4, we + packed 4 binary zeros here to signify one submapping in use. We + now redefine that to mean four bitflags that indicate use of + deeper features; bit0:submappings, bit1:coupling, + bit2,3:reserved. This is backward compatable with all actual uses + of the beta code. */ + + if(info->submaps>1){ + oggpack_write(opb,1,1); + oggpack_write(opb,info->submaps-1,4); + }else + oggpack_write(opb,0,1); + + if(info->coupling_steps>0){ + oggpack_write(opb,1,1); + oggpack_write(opb,info->coupling_steps-1,8); + + for(i=0;icoupling_steps;i++){ + oggpack_write(opb,info->coupling_mag[i],ilog(vi->channels)); + oggpack_write(opb,info->coupling_ang[i],ilog(vi->channels)); + } + }else + oggpack_write(opb,0,1); + + oggpack_write(opb,0,2); /* 2,3:reserved */ + + /* we don't write the channel submappings if we only have one... */ + if(info->submaps>1){ + for(i=0;ichannels;i++) + oggpack_write(opb,info->chmuxlist[i],4); + } + for(i=0;isubmaps;i++){ + oggpack_write(opb,0,8); /* time submap unused */ + oggpack_write(opb,info->floorsubmap[i],8); + oggpack_write(opb,info->residuesubmap[i],8); + } +} + +/* also responsible for range checking */ +static vorbis_info_mapping *mapping0_unpack(vorbis_info *vi,oggpack_buffer *opb){ + int i; + vorbis_info_mapping0 *info=_ogg_calloc(1,sizeof(*info)); + codec_setup_info *ci=vi->codec_setup; + memset(info,0,sizeof(*info)); + + if(oggpack_read(opb,1)) + info->submaps=oggpack_read(opb,4)+1; + else + info->submaps=1; + + if(oggpack_read(opb,1)){ + info->coupling_steps=oggpack_read(opb,8)+1; + + for(i=0;icoupling_steps;i++){ + int testM=info->coupling_mag[i]=oggpack_read(opb,ilog(vi->channels)); + int testA=info->coupling_ang[i]=oggpack_read(opb,ilog(vi->channels)); + + if(testM<0 || + testA<0 || + testM==testA || + testM>=vi->channels || + testA>=vi->channels) goto err_out; + } + + } + + if(oggpack_read(opb,2)>0)goto err_out; /* 2,3:reserved */ + + if(info->submaps>1){ + for(i=0;ichannels;i++){ + info->chmuxlist[i]=oggpack_read(opb,4); + if(info->chmuxlist[i]>=info->submaps)goto err_out; + } + } + for(i=0;isubmaps;i++){ + oggpack_read(opb,8); /* time submap unused */ + info->floorsubmap[i]=oggpack_read(opb,8); + if(info->floorsubmap[i]>=ci->floors)goto err_out; + info->residuesubmap[i]=oggpack_read(opb,8); + if(info->residuesubmap[i]>=ci->residues)goto err_out; + } + + return info; + + err_out: + mapping0_free_info(info); + return(NULL); +} + +#include "os.h" +#include "lpc.h" +#include "lsp.h" +#include "envelope.h" +#include "mdct.h" +#include "psy.h" +#include "scales.h" + +#if 0 +static long seq=0; +static ogg_int64_t total=0; +static float FLOOR1_fromdB_LOOKUP[256]={ + 1.0649863e-07F, 1.1341951e-07F, 1.2079015e-07F, 1.2863978e-07F, + 1.3699951e-07F, 1.4590251e-07F, 1.5538408e-07F, 1.6548181e-07F, + 1.7623575e-07F, 1.8768855e-07F, 1.9988561e-07F, 2.128753e-07F, + 2.2670913e-07F, 2.4144197e-07F, 2.5713223e-07F, 2.7384213e-07F, + 2.9163793e-07F, 3.1059021e-07F, 3.3077411e-07F, 3.5226968e-07F, + 3.7516214e-07F, 3.9954229e-07F, 4.2550680e-07F, 4.5315863e-07F, + 4.8260743e-07F, 5.1396998e-07F, 5.4737065e-07F, 5.8294187e-07F, + 6.2082472e-07F, 6.6116941e-07F, 7.0413592e-07F, 7.4989464e-07F, + 7.9862701e-07F, 8.5052630e-07F, 9.0579828e-07F, 9.6466216e-07F, + 1.0273513e-06F, 1.0941144e-06F, 1.1652161e-06F, 1.2409384e-06F, + 1.3215816e-06F, 1.4074654e-06F, 1.4989305e-06F, 1.5963394e-06F, + 1.7000785e-06F, 1.8105592e-06F, 1.9282195e-06F, 2.0535261e-06F, + 2.1869758e-06F, 2.3290978e-06F, 2.4804557e-06F, 2.6416497e-06F, + 2.8133190e-06F, 2.9961443e-06F, 3.1908506e-06F, 3.3982101e-06F, + 3.6190449e-06F, 3.8542308e-06F, 4.1047004e-06F, 4.3714470e-06F, + 4.6555282e-06F, 4.9580707e-06F, 5.2802740e-06F, 5.6234160e-06F, + 5.9888572e-06F, 6.3780469e-06F, 6.7925283e-06F, 7.2339451e-06F, + 7.7040476e-06F, 8.2047000e-06F, 8.7378876e-06F, 9.3057248e-06F, + 9.9104632e-06F, 1.0554501e-05F, 1.1240392e-05F, 1.1970856e-05F, + 1.2748789e-05F, 1.3577278e-05F, 1.4459606e-05F, 1.5399272e-05F, + 1.6400004e-05F, 1.7465768e-05F, 1.8600792e-05F, 1.9809576e-05F, + 2.1096914e-05F, 2.2467911e-05F, 2.3928002e-05F, 2.5482978e-05F, + 2.7139006e-05F, 2.8902651e-05F, 3.0780908e-05F, 3.2781225e-05F, + 3.4911534e-05F, 3.7180282e-05F, 3.9596466e-05F, 4.2169667e-05F, + 4.4910090e-05F, 4.7828601e-05F, 5.0936773e-05F, 5.4246931e-05F, + 5.7772202e-05F, 6.1526565e-05F, 6.5524908e-05F, 6.9783085e-05F, + 7.4317983e-05F, 7.9147585e-05F, 8.4291040e-05F, 8.9768747e-05F, + 9.5602426e-05F, 0.00010181521F, 0.00010843174F, 0.00011547824F, + 0.00012298267F, 0.00013097477F, 0.00013948625F, 0.00014855085F, + 0.00015820453F, 0.00016848555F, 0.00017943469F, 0.00019109536F, + 0.00020351382F, 0.00021673929F, 0.00023082423F, 0.00024582449F, + 0.00026179955F, 0.00027881276F, 0.00029693158F, 0.00031622787F, + 0.00033677814F, 0.00035866388F, 0.00038197188F, 0.00040679456F, + 0.00043323036F, 0.00046138411F, 0.00049136745F, 0.00052329927F, + 0.00055730621F, 0.00059352311F, 0.00063209358F, 0.00067317058F, + 0.00071691700F, 0.00076350630F, 0.00081312324F, 0.00086596457F, + 0.00092223983F, 0.00098217216F, 0.0010459992F, 0.0011139742F, + 0.0011863665F, 0.0012634633F, 0.0013455702F, 0.0014330129F, + 0.0015261382F, 0.0016253153F, 0.0017309374F, 0.0018434235F, + 0.0019632195F, 0.0020908006F, 0.0022266726F, 0.0023713743F, + 0.0025254795F, 0.0026895994F, 0.0028643847F, 0.0030505286F, + 0.0032487691F, 0.0034598925F, 0.0036847358F, 0.0039241906F, + 0.0041792066F, 0.0044507950F, 0.0047400328F, 0.0050480668F, + 0.0053761186F, 0.0057254891F, 0.0060975636F, 0.0064938176F, + 0.0069158225F, 0.0073652516F, 0.0078438871F, 0.0083536271F, + 0.0088964928F, 0.009474637F, 0.010090352F, 0.010746080F, + 0.011444421F, 0.012188144F, 0.012980198F, 0.013823725F, + 0.014722068F, 0.015678791F, 0.016697687F, 0.017782797F, + 0.018938423F, 0.020169149F, 0.021479854F, 0.022875735F, + 0.024362330F, 0.025945531F, 0.027631618F, 0.029427276F, + 0.031339626F, 0.033376252F, 0.035545228F, 0.037855157F, + 0.040315199F, 0.042935108F, 0.045725273F, 0.048696758F, + 0.051861348F, 0.055231591F, 0.058820850F, 0.062643361F, + 0.066714279F, 0.071049749F, 0.075666962F, 0.080584227F, + 0.085821044F, 0.091398179F, 0.097337747F, 0.10366330F, + 0.11039993F, 0.11757434F, 0.12521498F, 0.13335215F, + 0.14201813F, 0.15124727F, 0.16107617F, 0.17154380F, + 0.18269168F, 0.19456402F, 0.20720788F, 0.22067342F, + 0.23501402F, 0.25028656F, 0.26655159F, 0.28387361F, + 0.30232132F, 0.32196786F, 0.34289114F, 0.36517414F, + 0.38890521F, 0.41417847F, 0.44109412F, 0.46975890F, + 0.50028648F, 0.53279791F, 0.56742212F, 0.60429640F, + 0.64356699F, 0.68538959F, 0.72993007F, 0.77736504F, + 0.82788260F, 0.88168307F, 0.9389798F, 1.F, +}; + +#endif + +extern int *floor1_fit(vorbis_block *vb,vorbis_look_floor *look, + const float *logmdct, /* in */ + const float *logmask); +extern int *floor1_interpolate_fit(vorbis_block *vb,vorbis_look_floor *look, + int *A,int *B, + int del); +extern int floor1_encode(oggpack_buffer *opb,vorbis_block *vb, + vorbis_look_floor *look, + int *post,int *ilogmask); + + +static int mapping0_forward(vorbis_block *vb){ + vorbis_dsp_state *vd=vb->vd; + vorbis_info *vi=vd->vi; + codec_setup_info *ci=vi->codec_setup; + private_state *b=vb->vd->backend_state; + vorbis_block_internal *vbi=(vorbis_block_internal *)vb->internal; + int n=vb->pcmend; + int i,j,k; + + int *nonzero = alloca(sizeof(*nonzero)*vi->channels); + float **gmdct = _vorbis_block_alloc(vb,vi->channels*sizeof(*gmdct)); + int **ilogmaskch= _vorbis_block_alloc(vb,vi->channels*sizeof(*ilogmaskch)); + int ***floor_posts = _vorbis_block_alloc(vb,vi->channels*sizeof(*floor_posts)); + + float global_ampmax=vbi->ampmax; + float *local_ampmax=alloca(sizeof(*local_ampmax)*vi->channels); + int blocktype=vbi->blocktype; + + int modenumber=vb->W; + vorbis_info_mapping0 *info=ci->map_param[modenumber]; + vorbis_look_psy *psy_look= + b->psy+blocktype+(vb->W?2:0); + + vb->mode=modenumber; + + for(i=0;ichannels;i++){ + float scale=4.f/n; + float scale_dB; + + float *pcm =vb->pcm[i]; + float *logfft =pcm; + + gmdct[i]=_vorbis_block_alloc(vb,n/2*sizeof(**gmdct)); + + scale_dB=todB(&scale) + .345; /* + .345 is a hack; the original + todB estimation used on IEEE 754 + compliant machines had a bug that + returned dB values about a third + of a decibel too high. The bug + was harmless because tunings + implicitly took that into + account. However, fixing the bug + in the estimator requires + changing all the tunings as well. + For now, it's easier to sync + things back up here, and + recalibrate the tunings in the + next major model upgrade. */ + +#if 0 + if(vi->channels==2) + if(i==0) + _analysis_output("pcmL",seq,pcm,n,0,0,total-n/2); + else + _analysis_output("pcmR",seq,pcm,n,0,0,total-n/2); +#endif + + /* window the PCM data */ + _vorbis_apply_window(pcm,b->window,ci->blocksizes,vb->lW,vb->W,vb->nW); + +#if 0 + if(vi->channels==2) + if(i==0) + _analysis_output("windowedL",seq,pcm,n,0,0,total-n/2); + else + _analysis_output("windowedR",seq,pcm,n,0,0,total-n/2); +#endif + + /* transform the PCM data */ + /* only MDCT right now.... */ + mdct_forward(b->transform[vb->W][0],pcm,gmdct[i]); + + /* FFT yields more accurate tonal estimation (not phase sensitive) */ + drft_forward(&b->fft_look[vb->W],pcm); + logfft[0]=scale_dB+todB(pcm) + .345; /* + .345 is a hack; the + original todB estimation used on + IEEE 754 compliant machines had a + bug that returned dB values about + a third of a decibel too high. + The bug was harmless because + tunings implicitly took that into + account. However, fixing the bug + in the estimator requires + changing all the tunings as well. + For now, it's easier to sync + things back up here, and + recalibrate the tunings in the + next major model upgrade. */ + local_ampmax[i]=logfft[0]; + for(j=1;j>1]=scale_dB+.5f*todB(&temp) + .345; /* + + .345 is a hack; the original todB + estimation used on IEEE 754 + compliant machines had a bug that + returned dB values about a third + of a decibel too high. The bug + was harmless because tunings + implicitly took that into + account. However, fixing the bug + in the estimator requires + changing all the tunings as well. + For now, it's easier to sync + things back up here, and + recalibrate the tunings in the + next major model upgrade. */ + if(temp>local_ampmax[i])local_ampmax[i]=temp; + } + + if(local_ampmax[i]>0.f)local_ampmax[i]=0.f; + if(local_ampmax[i]>global_ampmax)global_ampmax=local_ampmax[i]; + +#if 0 + if(vi->channels==2){ + if(i==0){ + _analysis_output("fftL",seq,logfft,n/2,1,0,0); + }else{ + _analysis_output("fftR",seq,logfft,n/2,1,0,0); + } + } +#endif + + } + + { + float *noise = _vorbis_block_alloc(vb,n/2*sizeof(*noise)); + float *tone = _vorbis_block_alloc(vb,n/2*sizeof(*tone)); + + for(i=0;ichannels;i++){ + /* the encoder setup assumes that all the modes used by any + specific bitrate tweaking use the same floor */ + + int submap=info->chmuxlist[i]; + + /* the following makes things clearer to *me* anyway */ + float *mdct =gmdct[i]; + float *logfft =vb->pcm[i]; + + float *logmdct =logfft+n/2; + float *logmask =logfft; + + vb->mode=modenumber; + + floor_posts[i]=_vorbis_block_alloc(vb,PACKETBLOBS*sizeof(**floor_posts)); + memset(floor_posts[i],0,sizeof(**floor_posts)*PACKETBLOBS); + + for(j=0;jchannels==2){ + if(i==0) + _analysis_output("mdctL",seq,logmdct,n/2,1,0,0); + else + _analysis_output("mdctR",seq,logmdct,n/2,1,0,0); + }else{ + _analysis_output("mdct",seq,logmdct,n/2,1,0,0); + } +#endif + + /* first step; noise masking. Not only does 'noise masking' + give us curves from which we can decide how much resolution + to give noise parts of the spectrum, it also implicitly hands + us a tonality estimate (the larger the value in the + 'noise_depth' vector, the more tonal that area is) */ + + _vp_noisemask(psy_look, + logmdct, + noise); /* noise does not have by-frequency offset + bias applied yet */ +#if 0 + if(vi->channels==2){ + if(i==0) + _analysis_output("noiseL",seq,noise,n/2,1,0,0); + else + _analysis_output("noiseR",seq,noise,n/2,1,0,0); + } +#endif + + /* second step: 'all the other crap'; all the stuff that isn't + computed/fit for bitrate management goes in the second psy + vector. This includes tone masking, peak limiting and ATH */ + + _vp_tonemask(psy_look, + logfft, + tone, + global_ampmax, + local_ampmax[i]); + +#if 0 + if(vi->channels==2){ + if(i==0) + _analysis_output("toneL",seq,tone,n/2,1,0,0); + else + _analysis_output("toneR",seq,tone,n/2,1,0,0); + } +#endif + + /* third step; we offset the noise vectors, overlay tone + masking. We then do a floor1-specific line fit. If we're + performing bitrate management, the line fit is performed + multiple times for up/down tweakage on demand. */ + +#if 0 + { + float aotuv[psy_look->n]; +#endif + + _vp_offset_and_mix(psy_look, + noise, + tone, + 1, + logmask, + mdct, + logmdct); + +#if 0 + if(vi->channels==2){ + if(i==0) + _analysis_output("aotuvM1_L",seq,aotuv,psy_look->n,1,1,0); + else + _analysis_output("aotuvM1_R",seq,aotuv,psy_look->n,1,1,0); + } + } +#endif + + +#if 0 + if(vi->channels==2){ + if(i==0) + _analysis_output("mask1L",seq,logmask,n/2,1,0,0); + else + _analysis_output("mask1R",seq,logmask,n/2,1,0,0); + } +#endif + + /* this algorithm is hardwired to floor 1 for now; abort out if + we're *not* floor1. This won't happen unless someone has + broken the encode setup lib. Guard it anyway. */ + if(ci->floor_type[info->floorsubmap[submap]]!=1)return(-1); + + floor_posts[i][PACKETBLOBS/2]= + floor1_fit(vb,b->flr[info->floorsubmap[submap]], + logmdct, + logmask); + + /* are we managing bitrate? If so, perform two more fits for + later rate tweaking (fits represent hi/lo) */ + if(vorbis_bitrate_managed(vb) && floor_posts[i][PACKETBLOBS/2]){ + /* higher rate by way of lower noise curve */ + + _vp_offset_and_mix(psy_look, + noise, + tone, + 2, + logmask, + mdct, + logmdct); + +#if 0 + if(vi->channels==2){ + if(i==0) + _analysis_output("mask2L",seq,logmask,n/2,1,0,0); + else + _analysis_output("mask2R",seq,logmask,n/2,1,0,0); + } +#endif + + floor_posts[i][PACKETBLOBS-1]= + floor1_fit(vb,b->flr[info->floorsubmap[submap]], + logmdct, + logmask); + + /* lower rate by way of higher noise curve */ + _vp_offset_and_mix(psy_look, + noise, + tone, + 0, + logmask, + mdct, + logmdct); + +#if 0 + if(vi->channels==2) + if(i==0) + _analysis_output("mask0L",seq,logmask,n/2,1,0,0); + else + _analysis_output("mask0R",seq,logmask,n/2,1,0,0); +#endif + + floor_posts[i][0]= + floor1_fit(vb,b->flr[info->floorsubmap[submap]], + logmdct, + logmask); + + /* we also interpolate a range of intermediate curves for + intermediate rates */ + for(k=1;kflr[info->floorsubmap[submap]], + floor_posts[i][0], + floor_posts[i][PACKETBLOBS/2], + k*65536/(PACKETBLOBS/2)); + for(k=PACKETBLOBS/2+1;kflr[info->floorsubmap[submap]], + floor_posts[i][PACKETBLOBS/2], + floor_posts[i][PACKETBLOBS-1], + (k-PACKETBLOBS/2)*65536/(PACKETBLOBS/2)); + } + } + } + vbi->ampmax=global_ampmax; + + /* + the next phases are performed once for vbr-only and PACKETBLOB + times for bitrate managed modes. + + 1) encode actual mode being used + 2) encode the floor for each channel, compute coded mask curve/res + 3) normalize and couple. + 4) encode residue + 5) save packet bytes to the packetblob vector + + */ + + /* iterate over the many masking curve fits we've created */ + + { + float **res_bundle=alloca(sizeof(*res_bundle)*vi->channels); + float **couple_bundle=alloca(sizeof(*couple_bundle)*vi->channels); + int *zerobundle=alloca(sizeof(*zerobundle)*vi->channels); + int **sortindex=alloca(sizeof(*sortindex)*vi->channels); + float **mag_memo; + int **mag_sort; + + if(info->coupling_steps){ + mag_memo=_vp_quantize_couple_memo(vb, + &ci->psy_g_param, + psy_look, + info, + gmdct); + + mag_sort=_vp_quantize_couple_sort(vb, + psy_look, + info, + mag_memo); + + hf_reduction(&ci->psy_g_param, + psy_look, + info, + mag_memo); + } + + memset(sortindex,0,sizeof(*sortindex)*vi->channels); + if(psy_look->vi->normal_channel_p){ + for(i=0;ichannels;i++){ + float *mdct =gmdct[i]; + sortindex[i]=alloca(sizeof(**sortindex)*n/2); + _vp_noise_normalize_sort(psy_look,mdct,sortindex[i]); + } + } + + for(k=(vorbis_bitrate_managed(vb)?0:PACKETBLOBS/2); + k<=(vorbis_bitrate_managed(vb)?PACKETBLOBS-1:PACKETBLOBS/2); + k++){ + oggpack_buffer *opb=vbi->packetblob[k]; + + /* start out our new packet blob with packet type and mode */ + /* Encode the packet type */ + oggpack_write(opb,0,1); + /* Encode the modenumber */ + /* Encode frame mode, pre,post windowsize, then dispatch */ + oggpack_write(opb,modenumber,b->modebits); + if(vb->W){ + oggpack_write(opb,vb->lW,1); + oggpack_write(opb,vb->nW,1); + } + + /* encode floor, compute masking curve, sep out residue */ + for(i=0;ichannels;i++){ + int submap=info->chmuxlist[i]; + float *mdct =gmdct[i]; + float *res =vb->pcm[i]; + int *ilogmask=ilogmaskch[i]= + _vorbis_block_alloc(vb,n/2*sizeof(**gmdct)); + + nonzero[i]=floor1_encode(opb,vb,b->flr[info->floorsubmap[submap]], + floor_posts[i][k], + ilogmask); +#if 0 + { + char buf[80]; + sprintf(buf,"maskI%c%d",i?'R':'L',k); + float work[n/2]; + for(j=0;jpsy_g_param.sliding_lowpass[vb->W][k]); + + _vp_noise_normalize(psy_look,res,res+n/2,sortindex[i]); + + +#if 0 + { + char buf[80]; + float work[n/2]; + for(j=0;jcoupling_steps){ + _vp_couple(k, + &ci->psy_g_param, + psy_look, + info, + vb->pcm, + mag_memo, + mag_sort, + ilogmaskch, + nonzero, + ci->psy_g_param.sliding_lowpass[vb->W][k]); + } + + /* classify and encode by submap */ + for(i=0;isubmaps;i++){ + int ch_in_bundle=0; + long **classifications; + int resnum=info->residuesubmap[i]; + + for(j=0;jchannels;j++){ + if(info->chmuxlist[j]==i){ + zerobundle[ch_in_bundle]=0; + if(nonzero[j])zerobundle[ch_in_bundle]=1; + res_bundle[ch_in_bundle]=vb->pcm[j]; + couple_bundle[ch_in_bundle++]=vb->pcm[j]+n/2; + } + } + + classifications=_residue_P[ci->residue_type[resnum]]-> + class(vb,b->residue[resnum],couple_bundle,zerobundle,ch_in_bundle); + + _residue_P[ci->residue_type[resnum]]-> + forward(opb,vb,b->residue[resnum], + couple_bundle,NULL,zerobundle,ch_in_bundle,classifications); + } + + /* ok, done encoding. Next protopacket. */ + } + + } + +#if 0 + seq++; + total+=ci->blocksizes[vb->W]/4+ci->blocksizes[vb->nW]/4; +#endif + return(0); +} + +static int mapping0_inverse(vorbis_block *vb,vorbis_info_mapping *l){ + vorbis_dsp_state *vd=vb->vd; + vorbis_info *vi=vd->vi; + codec_setup_info *ci=vi->codec_setup; + private_state *b=vd->backend_state; + vorbis_info_mapping0 *info=(vorbis_info_mapping0 *)l; + int hs=ci->halfrate_flag; + + int i,j; + long n=vb->pcmend=ci->blocksizes[vb->W]; + + float **pcmbundle=alloca(sizeof(*pcmbundle)*vi->channels); + int *zerobundle=alloca(sizeof(*zerobundle)*vi->channels); + + int *nonzero =alloca(sizeof(*nonzero)*vi->channels); + void **floormemo=alloca(sizeof(*floormemo)*vi->channels); + + /* recover the spectral envelope; store it in the PCM vector for now */ + for(i=0;ichannels;i++){ + int submap=info->chmuxlist[i]; + floormemo[i]=_floor_P[ci->floor_type[info->floorsubmap[submap]]]-> + inverse1(vb,b->flr[info->floorsubmap[submap]]); + if(floormemo[i]) + nonzero[i]=1; + else + nonzero[i]=0; + memset(vb->pcm[i],0,sizeof(*vb->pcm[i])*n/2); + } + + /* channel coupling can 'dirty' the nonzero listing */ + for(i=0;icoupling_steps;i++){ + if(nonzero[info->coupling_mag[i]] || + nonzero[info->coupling_ang[i]]){ + nonzero[info->coupling_mag[i]]=1; + nonzero[info->coupling_ang[i]]=1; + } + } + + /* recover the residue into our working vectors */ + for(i=0;isubmaps;i++){ + int ch_in_bundle=0; + for(j=0;jchannels;j++){ + if(info->chmuxlist[j]==i){ + if(nonzero[j]) + zerobundle[ch_in_bundle]=1; + else + zerobundle[ch_in_bundle]=0; + pcmbundle[ch_in_bundle++]=vb->pcm[j]; + } + } + + _residue_P[ci->residue_type[info->residuesubmap[i]]]-> + inverse(vb,b->residue[info->residuesubmap[i]], + pcmbundle,zerobundle,ch_in_bundle); + } + + /* channel coupling */ + for(i=info->coupling_steps-1;i>=0;i--){ + float *pcmM=vb->pcm[info->coupling_mag[i]]; + float *pcmA=vb->pcm[info->coupling_ang[i]]; + + for(j=0;j0) + if(ang>0){ + pcmM[j]=mag; + pcmA[j]=mag-ang; + }else{ + pcmA[j]=mag; + pcmM[j]=mag+ang; + } + else + if(ang>0){ + pcmM[j]=mag; + pcmA[j]=mag+ang; + }else{ + pcmA[j]=mag; + pcmM[j]=mag-ang; + } + } + } + + /* compute and apply spectral envelope */ + for(i=0;ichannels;i++){ + float *pcm=vb->pcm[i]; + int submap=info->chmuxlist[i]; + _floor_P[ci->floor_type[info->floorsubmap[submap]]]-> + inverse2(vb,b->flr[info->floorsubmap[submap]], + floormemo[i],pcm); + } + + /* transform the PCM data; takes PCM vector, vb; modifies PCM vector */ + /* only MDCT right now.... */ + for(i=0;ichannels;i++){ + float *pcm=vb->pcm[i]; + mdct_backward(b->transform[vb->W][0],pcm,pcm); + } + + /* all done! */ + return(0); +} + +/* export hooks */ +vorbis_func_mapping mapping0_exportbundle={ + &mapping0_pack, + &mapping0_unpack, + &mapping0_free_info, + &mapping0_forward, + &mapping0_inverse +}; + diff --git a/Engine/lib/libvorbis/lib/masking.h b/Engine/lib/libvorbis/lib/masking.h new file mode 100644 index 000000000..c10be9a5e --- /dev/null +++ b/Engine/lib/libvorbis/lib/masking.h @@ -0,0 +1,785 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: masking curve data for psychoacoustics + last mod: $Id: masking.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#ifndef _V_MASKING_H_ +#define _V_MASKING_H_ + +/* more detailed ATH; the bass if flat to save stressing the floor + overly for only a bin or two of savings. */ + +#define MAX_ATH 88 +static float ATH[]={ + /*15*/ -51, -52, -53, -54, -55, -56, -57, -58, + /*31*/ -59, -60, -61, -62, -63, -64, -65, -66, + /*63*/ -67, -68, -69, -70, -71, -72, -73, -74, + /*125*/ -75, -76, -77, -78, -80, -81, -82, -83, + /*250*/ -84, -85, -86, -87, -88, -88, -89, -89, + /*500*/ -90, -91, -91, -92, -93, -94, -95, -96, + /*1k*/ -96, -97, -98, -98, -99, -99,-100,-100, + /*2k*/ -101,-102,-103,-104,-106,-107,-107,-107, + /*4k*/ -107,-105,-103,-102,-101, -99, -98, -96, + /*8k*/ -95, -95, -96, -97, -96, -95, -93, -90, + /*16k*/ -80, -70, -50, -40, -30, -30, -30, -30 +}; + +/* The tone masking curves from Ehmer's and Fielder's papers have been + replaced by an empirically collected data set. The previously + published values were, far too often, simply on crack. */ + +#define EHMER_OFFSET 16 +#define EHMER_MAX 56 + +/* masking tones from -50 to 0dB, 62.5 through 16kHz at half octaves + test tones from -2 octaves to +5 octaves sampled at eighth octaves */ +/* (Vorbis 0dB, the loudest possible tone, is assumed to be ~100dB SPL + for collection of these curves) */ + +static float tonemasks[P_BANDS][6][EHMER_MAX]={ + /* 62.5 Hz */ + {{ -60, -60, -60, -60, -60, -60, -60, -60, + -60, -60, -60, -60, -62, -62, -65, -73, + -69, -68, -68, -67, -70, -70, -72, -74, + -75, -79, -79, -80, -83, -88, -93, -100, + -110, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -48, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -53, -61, -66, + -66, -68, -67, -70, -76, -76, -72, -73, + -75, -76, -78, -79, -83, -88, -93, -100, + -110, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -37, -37, -37, -37, -37, -37, -37, -37, + -38, -40, -42, -46, -48, -53, -55, -62, + -65, -58, -56, -56, -61, -60, -65, -67, + -69, -71, -77, -77, -78, -80, -82, -84, + -88, -93, -98, -106, -112, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -25, -25, -25, -25, -25, -25, -25, -25, + -25, -26, -27, -29, -32, -38, -48, -52, + -52, -50, -48, -48, -51, -52, -54, -60, + -67, -67, -66, -68, -69, -73, -73, -76, + -80, -81, -81, -85, -85, -86, -88, -93, + -100, -110, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -16, -16, -16, -16, -16, -16, -16, -16, + -17, -19, -20, -22, -26, -28, -31, -40, + -47, -39, -39, -40, -42, -43, -47, -51, + -57, -52, -55, -55, -60, -58, -62, -63, + -70, -67, -69, -72, -73, -77, -80, -82, + -83, -87, -90, -94, -98, -104, -115, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -8, -8, -8, -8, -8, -8, -8, -8, + -8, -8, -10, -11, -15, -19, -25, -30, + -34, -31, -30, -31, -29, -32, -35, -42, + -48, -42, -44, -46, -50, -50, -51, -52, + -59, -54, -55, -55, -58, -62, -63, -66, + -72, -73, -76, -75, -78, -80, -80, -81, + -84, -88, -90, -94, -98, -101, -106, -110}}, + /* 88Hz */ + {{ -66, -66, -66, -66, -66, -66, -66, -66, + -66, -66, -66, -66, -66, -67, -67, -67, + -76, -72, -71, -74, -76, -76, -75, -78, + -79, -79, -81, -83, -86, -89, -93, -97, + -100, -105, -110, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -47, -47, -47, -47, -47, -47, -47, -47, + -47, -47, -47, -48, -51, -55, -59, -66, + -66, -66, -67, -66, -68, -69, -70, -74, + -79, -77, -77, -78, -80, -81, -82, -84, + -86, -88, -91, -95, -100, -108, -116, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -36, -36, -36, -36, -36, -36, -36, -36, + -36, -37, -37, -41, -44, -48, -51, -58, + -62, -60, -57, -59, -59, -60, -63, -65, + -72, -71, -70, -72, -74, -77, -76, -78, + -81, -81, -80, -83, -86, -91, -96, -100, + -105, -110, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -28, -28, -28, -28, -28, -28, -28, -28, + -28, -30, -32, -32, -33, -35, -41, -49, + -50, -49, -47, -48, -48, -52, -51, -57, + -65, -61, -59, -61, -64, -69, -70, -74, + -77, -77, -78, -81, -84, -85, -87, -90, + -92, -96, -100, -107, -112, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -19, -19, -19, -19, -19, -19, -19, -19, + -20, -21, -23, -27, -30, -35, -36, -41, + -46, -44, -42, -40, -41, -41, -43, -48, + -55, -53, -52, -53, -56, -59, -58, -60, + -67, -66, -69, -71, -72, -75, -79, -81, + -84, -87, -90, -93, -97, -101, -107, -114, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -9, -9, -9, -9, -9, -9, -9, -9, + -11, -12, -12, -15, -16, -20, -23, -30, + -37, -34, -33, -34, -31, -32, -32, -38, + -47, -44, -41, -40, -47, -49, -46, -46, + -58, -50, -50, -54, -58, -62, -64, -67, + -67, -70, -72, -76, -79, -83, -87, -91, + -96, -100, -104, -110, -999, -999, -999, -999}}, + /* 125 Hz */ + {{ -62, -62, -62, -62, -62, -62, -62, -62, + -62, -62, -63, -64, -66, -67, -66, -68, + -75, -72, -76, -75, -76, -78, -79, -82, + -84, -85, -90, -94, -101, -110, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -59, -59, -59, -59, -59, -59, -59, -59, + -59, -59, -59, -60, -60, -61, -63, -66, + -71, -68, -70, -70, -71, -72, -72, -75, + -81, -78, -79, -82, -83, -86, -90, -97, + -103, -113, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -53, -53, -53, -53, -53, -53, -53, -53, + -53, -54, -55, -57, -56, -57, -55, -61, + -65, -60, -60, -62, -63, -63, -66, -68, + -74, -73, -75, -75, -78, -80, -80, -82, + -85, -90, -96, -101, -108, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -46, -46, -46, -46, -46, -46, -46, -46, + -46, -46, -47, -47, -47, -47, -48, -51, + -57, -51, -49, -50, -51, -53, -54, -59, + -66, -60, -62, -67, -67, -70, -72, -75, + -76, -78, -81, -85, -88, -94, -97, -104, + -112, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -36, -36, -36, -36, -36, -36, -36, -36, + -39, -41, -42, -42, -39, -38, -41, -43, + -52, -44, -40, -39, -37, -37, -40, -47, + -54, -50, -48, -50, -55, -61, -59, -62, + -66, -66, -66, -69, -69, -73, -74, -74, + -75, -77, -79, -82, -87, -91, -95, -100, + -108, -115, -999, -999, -999, -999, -999, -999}, + { -28, -26, -24, -22, -20, -20, -23, -29, + -30, -31, -28, -27, -28, -28, -28, -35, + -40, -33, -32, -29, -30, -30, -30, -37, + -45, -41, -37, -38, -45, -47, -47, -48, + -53, -49, -48, -50, -49, -49, -51, -52, + -58, -56, -57, -56, -60, -61, -62, -70, + -72, -74, -78, -83, -88, -93, -100, -106}}, + /* 177 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -110, -105, -100, -95, -91, -87, -83, + -80, -78, -76, -78, -78, -81, -83, -85, + -86, -85, -86, -87, -90, -97, -107, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -110, -105, -100, -95, -90, + -85, -81, -77, -73, -70, -67, -67, -68, + -75, -73, -70, -69, -70, -72, -75, -79, + -84, -83, -84, -86, -88, -89, -89, -93, + -98, -105, -112, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-105, -100, -95, -90, -85, -80, -76, -71, + -68, -68, -65, -63, -63, -62, -62, -64, + -65, -64, -61, -62, -63, -64, -66, -68, + -73, -73, -74, -75, -76, -81, -83, -85, + -88, -89, -92, -95, -100, -108, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -80, -75, -71, -68, -65, -63, -62, -61, + -61, -61, -61, -59, -56, -57, -53, -50, + -58, -52, -50, -50, -52, -53, -54, -58, + -67, -63, -67, -68, -72, -75, -78, -80, + -81, -81, -82, -85, -89, -90, -93, -97, + -101, -107, -114, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + { -65, -61, -59, -57, -56, -55, -55, -56, + -56, -57, -55, -53, -52, -47, -44, -44, + -50, -44, -41, -39, -39, -42, -40, -46, + -51, -49, -50, -53, -54, -63, -60, -61, + -62, -66, -66, -66, -70, -73, -74, -75, + -76, -75, -79, -85, -89, -91, -96, -102, + -110, -999, -999, -999, -999, -999, -999, -999}, + { -52, -50, -49, -49, -48, -48, -48, -49, + -50, -50, -49, -46, -43, -39, -35, -33, + -38, -36, -32, -29, -32, -32, -32, -35, + -44, -39, -38, -38, -46, -50, -45, -46, + -53, -50, -50, -50, -54, -54, -53, -53, + -56, -57, -59, -66, -70, -72, -74, -79, + -83, -85, -90, -97, -114, -999, -999, -999}}, + /* 250 Hz */ + {{-999, -999, -999, -999, -999, -999, -110, -105, + -100, -95, -90, -86, -80, -75, -75, -79, + -80, -79, -80, -81, -82, -88, -95, -103, + -110, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -108, -103, -98, -93, + -88, -83, -79, -78, -75, -71, -67, -68, + -73, -73, -72, -73, -75, -77, -80, -82, + -88, -93, -100, -107, -114, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -110, -105, -101, -96, -90, + -86, -81, -77, -73, -69, -66, -61, -62, + -66, -64, -62, -65, -66, -70, -72, -76, + -81, -80, -84, -90, -95, -102, -110, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -107, -103, -97, -92, -88, + -83, -79, -74, -70, -66, -59, -53, -58, + -62, -55, -54, -54, -54, -58, -61, -62, + -72, -70, -72, -75, -78, -80, -81, -80, + -83, -83, -88, -93, -100, -107, -115, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -105, -100, -95, -90, -85, + -80, -75, -70, -66, -62, -56, -48, -44, + -48, -46, -46, -43, -46, -48, -48, -51, + -58, -58, -59, -60, -62, -62, -61, -61, + -65, -64, -65, -68, -70, -74, -75, -78, + -81, -86, -95, -110, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -105, -100, -95, -90, -85, -80, + -75, -70, -65, -61, -55, -49, -39, -33, + -40, -35, -32, -38, -40, -33, -35, -37, + -46, -41, -45, -44, -46, -42, -45, -46, + -52, -50, -50, -50, -54, -54, -55, -57, + -62, -64, -66, -68, -70, -76, -81, -90, + -100, -110, -999, -999, -999, -999, -999, -999}}, + /* 354 hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -105, -98, -90, -85, -82, -83, -80, -78, + -84, -79, -80, -83, -87, -89, -91, -93, + -99, -106, -117, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -105, -98, -90, -85, -80, -75, -70, -68, + -74, -72, -74, -77, -80, -82, -85, -87, + -92, -89, -91, -95, -100, -106, -112, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -105, -98, -90, -83, -75, -71, -63, -64, + -67, -62, -64, -67, -70, -73, -77, -81, + -84, -83, -85, -89, -90, -93, -98, -104, + -109, -114, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -103, -96, -88, -81, -75, -68, -58, -54, + -56, -54, -56, -56, -58, -60, -63, -66, + -74, -69, -72, -72, -75, -74, -77, -81, + -81, -82, -84, -87, -93, -96, -99, -104, + -110, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -108, -102, -96, + -91, -85, -80, -74, -68, -60, -51, -46, + -48, -46, -43, -45, -47, -47, -49, -48, + -56, -53, -55, -58, -57, -63, -58, -60, + -66, -64, -67, -70, -70, -74, -77, -84, + -86, -89, -91, -93, -94, -101, -109, -118, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -108, -103, -98, -93, -88, + -83, -78, -73, -68, -60, -53, -44, -35, + -38, -38, -34, -34, -36, -40, -41, -44, + -51, -45, -46, -47, -46, -54, -50, -49, + -50, -50, -50, -51, -54, -57, -58, -60, + -66, -66, -66, -64, -65, -68, -77, -82, + -87, -95, -110, -999, -999, -999, -999, -999}}, + /* 500 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -107, -102, -97, -92, -87, -83, -78, -75, + -82, -79, -83, -85, -89, -92, -95, -98, + -101, -105, -109, -113, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -106, + -100, -95, -90, -86, -81, -78, -74, -69, + -74, -74, -76, -79, -83, -84, -86, -89, + -92, -97, -93, -100, -103, -107, -110, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -106, -100, + -95, -90, -87, -83, -80, -75, -69, -60, + -66, -66, -68, -70, -74, -78, -79, -81, + -81, -83, -84, -87, -93, -96, -99, -103, + -107, -110, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -108, -103, -98, + -93, -89, -85, -82, -78, -71, -62, -55, + -58, -58, -54, -54, -55, -59, -61, -62, + -70, -66, -66, -67, -70, -72, -75, -78, + -84, -84, -84, -88, -91, -90, -95, -98, + -102, -103, -106, -110, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -108, -103, -98, -94, + -90, -87, -82, -79, -73, -67, -58, -47, + -50, -45, -41, -45, -48, -44, -44, -49, + -54, -51, -48, -47, -49, -50, -51, -57, + -58, -60, -63, -69, -70, -69, -71, -74, + -78, -82, -90, -95, -101, -105, -110, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -105, -101, -97, -93, -90, + -85, -80, -77, -72, -65, -56, -48, -37, + -40, -36, -34, -40, -50, -47, -38, -41, + -47, -38, -35, -39, -38, -43, -40, -45, + -50, -45, -44, -47, -50, -55, -48, -48, + -52, -66, -70, -76, -82, -90, -97, -105, + -110, -999, -999, -999, -999, -999, -999, -999}}, + /* 707 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -108, -103, -98, -93, -86, -79, -76, + -83, -81, -85, -87, -89, -93, -98, -102, + -107, -112, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -108, -103, -98, -93, -86, -79, -71, + -77, -74, -77, -79, -81, -84, -85, -90, + -92, -93, -92, -98, -101, -108, -112, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -108, -103, -98, -93, -87, -78, -68, -65, + -66, -62, -65, -67, -70, -73, -75, -78, + -82, -82, -83, -84, -91, -93, -98, -102, + -106, -110, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -105, -100, -95, -90, -82, -74, -62, -57, + -58, -56, -51, -52, -52, -54, -54, -58, + -66, -59, -60, -63, -66, -69, -73, -79, + -83, -84, -80, -81, -81, -82, -88, -92, + -98, -105, -113, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -107, + -102, -97, -92, -84, -79, -69, -57, -47, + -52, -47, -44, -45, -50, -52, -42, -42, + -53, -43, -43, -48, -51, -56, -55, -52, + -57, -59, -61, -62, -67, -71, -78, -83, + -86, -94, -98, -103, -110, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -105, -100, + -95, -90, -84, -78, -70, -61, -51, -41, + -40, -38, -40, -46, -52, -51, -41, -40, + -46, -40, -38, -38, -41, -46, -41, -46, + -47, -43, -43, -45, -41, -45, -56, -67, + -68, -83, -87, -90, -95, -102, -107, -113, + -999, -999, -999, -999, -999, -999, -999, -999}}, + /* 1000 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -109, -105, -101, -96, -91, -84, -77, + -82, -82, -85, -89, -94, -100, -106, -110, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -106, -103, -98, -92, -85, -80, -71, + -75, -72, -76, -80, -84, -86, -89, -93, + -100, -107, -113, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -107, + -104, -101, -97, -92, -88, -84, -80, -64, + -66, -63, -64, -66, -69, -73, -77, -83, + -83, -86, -91, -98, -104, -111, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -107, + -104, -101, -97, -92, -90, -84, -74, -57, + -58, -52, -55, -54, -50, -52, -50, -52, + -63, -62, -69, -76, -77, -78, -78, -79, + -82, -88, -94, -100, -106, -111, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -106, -102, + -98, -95, -90, -85, -83, -78, -70, -50, + -50, -41, -44, -49, -47, -50, -50, -44, + -55, -46, -47, -48, -48, -54, -49, -49, + -58, -62, -71, -81, -87, -92, -97, -102, + -108, -114, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -106, -102, + -98, -95, -90, -85, -83, -78, -70, -45, + -43, -41, -47, -50, -51, -50, -49, -45, + -47, -41, -44, -41, -39, -43, -38, -37, + -40, -41, -44, -50, -58, -65, -73, -79, + -85, -92, -97, -101, -105, -109, -113, -999, + -999, -999, -999, -999, -999, -999, -999, -999}}, + /* 1414 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -107, -100, -95, -87, -81, + -85, -83, -88, -93, -100, -107, -114, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -107, -101, -95, -88, -83, -76, + -73, -72, -79, -84, -90, -95, -100, -105, + -110, -115, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -104, -98, -92, -87, -81, -70, + -65, -62, -67, -71, -74, -80, -85, -91, + -95, -99, -103, -108, -111, -114, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -103, -97, -90, -85, -76, -60, + -56, -54, -60, -62, -61, -56, -63, -65, + -73, -74, -77, -75, -78, -81, -86, -87, + -88, -91, -94, -98, -103, -110, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -105, + -100, -97, -92, -86, -81, -79, -70, -57, + -51, -47, -51, -58, -60, -56, -53, -50, + -58, -52, -50, -50, -53, -55, -64, -69, + -71, -85, -82, -78, -81, -85, -95, -102, + -112, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -105, + -100, -97, -92, -85, -83, -79, -72, -49, + -40, -43, -43, -54, -56, -51, -50, -40, + -43, -38, -36, -35, -37, -38, -37, -44, + -54, -60, -57, -60, -70, -75, -84, -92, + -103, -112, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}}, + /* 2000 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -110, -102, -95, -89, -82, + -83, -84, -90, -92, -99, -107, -113, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -107, -101, -95, -89, -83, -72, + -74, -78, -85, -88, -88, -90, -92, -98, + -105, -111, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -109, -103, -97, -93, -87, -81, -70, + -70, -67, -75, -73, -76, -79, -81, -83, + -88, -89, -97, -103, -110, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -107, -100, -94, -88, -83, -75, -63, + -59, -59, -63, -66, -60, -62, -67, -67, + -77, -76, -81, -88, -86, -92, -96, -102, + -109, -116, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -105, -98, -92, -86, -81, -73, -56, + -52, -47, -55, -60, -58, -52, -51, -45, + -49, -50, -53, -54, -61, -71, -70, -69, + -78, -79, -87, -90, -96, -104, -112, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -103, -96, -90, -86, -78, -70, -51, + -42, -47, -48, -55, -54, -54, -53, -42, + -35, -28, -33, -38, -37, -44, -47, -49, + -54, -63, -68, -78, -82, -89, -94, -99, + -104, -109, -114, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}}, + /* 2828 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -110, -100, -90, -79, + -85, -81, -82, -82, -89, -94, -99, -103, + -109, -115, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -105, -97, -85, -72, + -74, -70, -70, -70, -76, -85, -91, -93, + -97, -103, -109, -115, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -112, -93, -81, -68, + -62, -60, -60, -57, -63, -70, -77, -82, + -90, -93, -98, -104, -109, -113, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -113, -100, -93, -84, -63, + -58, -48, -53, -54, -52, -52, -57, -64, + -66, -76, -83, -81, -85, -85, -90, -95, + -98, -101, -103, -106, -108, -111, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -105, -95, -86, -74, -53, + -50, -38, -43, -49, -43, -42, -39, -39, + -46, -52, -57, -56, -72, -69, -74, -81, + -87, -92, -94, -97, -99, -102, -105, -108, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -108, -99, -90, -76, -66, -45, + -43, -41, -44, -47, -43, -47, -40, -30, + -31, -31, -39, -33, -40, -41, -43, -53, + -59, -70, -73, -77, -79, -82, -84, -87, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}}, + /* 4000 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -110, -91, -76, + -75, -85, -93, -98, -104, -110, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -110, -91, -70, + -70, -75, -86, -89, -94, -98, -101, -106, + -110, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -110, -95, -80, -60, + -65, -64, -74, -83, -88, -91, -95, -99, + -103, -107, -110, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -110, -95, -80, -58, + -55, -49, -66, -68, -71, -78, -78, -80, + -88, -85, -89, -97, -100, -105, -110, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -110, -95, -80, -53, + -52, -41, -59, -59, -49, -58, -56, -63, + -86, -79, -90, -93, -98, -103, -107, -112, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -110, -97, -91, -73, -45, + -40, -33, -53, -61, -49, -54, -50, -50, + -60, -52, -67, -74, -81, -92, -96, -100, + -105, -110, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}}, + /* 5657 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -113, -106, -99, -92, -77, + -80, -88, -97, -106, -115, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -116, -109, -102, -95, -89, -74, + -72, -88, -87, -95, -102, -109, -116, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -116, -109, -102, -95, -89, -75, + -66, -74, -77, -78, -86, -87, -90, -96, + -105, -115, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -115, -108, -101, -94, -88, -66, + -56, -61, -70, -65, -78, -72, -83, -84, + -93, -98, -105, -110, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -110, -105, -95, -89, -82, -57, + -52, -52, -59, -56, -59, -58, -69, -67, + -88, -82, -82, -89, -94, -100, -108, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -110, -101, -96, -90, -83, -77, -54, + -43, -38, -50, -48, -52, -48, -42, -42, + -51, -52, -53, -59, -65, -71, -78, -85, + -95, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}}, + /* 8000 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -120, -105, -86, -68, + -78, -79, -90, -100, -110, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -120, -105, -86, -66, + -73, -77, -88, -96, -105, -115, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -120, -105, -92, -80, -61, + -64, -68, -80, -87, -92, -100, -110, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -120, -104, -91, -79, -52, + -60, -54, -64, -69, -77, -80, -82, -84, + -85, -87, -88, -90, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -118, -100, -87, -77, -49, + -50, -44, -58, -61, -61, -67, -65, -62, + -62, -62, -65, -68, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -115, -98, -84, -62, -49, + -44, -38, -46, -49, -49, -46, -39, -37, + -39, -40, -42, -43, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}}, + /* 11314 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -110, -88, -74, + -77, -82, -82, -85, -90, -94, -99, -104, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -110, -88, -66, + -70, -81, -80, -81, -84, -88, -91, -93, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -110, -88, -61, + -63, -70, -71, -74, -77, -80, -83, -85, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -110, -86, -62, + -63, -62, -62, -58, -52, -50, -50, -52, + -54, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -118, -108, -84, -53, + -50, -50, -50, -55, -47, -45, -40, -40, + -40, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -118, -100, -73, -43, + -37, -42, -43, -53, -38, -37, -35, -35, + -38, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}}, + /* 16000 Hz */ + {{-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -110, -100, -91, -84, -74, + -80, -80, -80, -80, -80, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -110, -100, -91, -84, -74, + -68, -68, -68, -68, -68, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -110, -100, -86, -78, -70, + -60, -45, -30, -21, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -110, -100, -87, -78, -67, + -48, -38, -29, -21, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -110, -100, -86, -69, -56, + -45, -35, -33, -29, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}, + {-999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -110, -100, -83, -71, -48, + -27, -38, -37, -34, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999, + -999, -999, -999, -999, -999, -999, -999, -999}} +}; + +#endif diff --git a/Engine/lib/libvorbis/lib/mdct.c b/Engine/lib/libvorbis/lib/mdct.c new file mode 100644 index 000000000..206d99f7b --- /dev/null +++ b/Engine/lib/libvorbis/lib/mdct.c @@ -0,0 +1,564 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: normalized modified discrete cosine transform + power of two length transform only [64 <= n ] + last mod: $Id: mdct.c 13293 2007-07-24 00:09:47Z xiphmont $ + + Original algorithm adapted long ago from _The use of multirate filter + banks for coding of high quality digital audio_, by T. Sporer, + K. Brandenburg and B. Edler, collection of the European Signal + Processing Conference (EUSIPCO), Amsterdam, June 1992, Vol.1, pp + 211-214 + + The below code implements an algorithm that no longer looks much like + that presented in the paper, but the basic structure remains if you + dig deep enough to see it. + + This module DOES NOT INCLUDE code to generate/apply the window + function. Everybody has their own weird favorite including me... I + happen to like the properties of y=sin(.5PI*sin^2(x)), but others may + vehemently disagree. + + ********************************************************************/ + +/* this can also be run as an integer transform by uncommenting a + define in mdct.h; the integerization is a first pass and although + it's likely stable for Vorbis, the dynamic range is constrained and + roundoff isn't done (so it's noisy). Consider it functional, but + only a starting point. There's no point on a machine with an FPU */ + +#include +#include +#include +#include +#include "vorbis/codec.h" +#include "mdct.h" +#include "os.h" +#include "misc.h" + +/* build lookups for trig functions; also pre-figure scaling and + some window function algebra. */ + +void mdct_init(mdct_lookup *lookup,int n){ + int *bitrev=_ogg_malloc(sizeof(*bitrev)*(n/4)); + DATA_TYPE *T=_ogg_malloc(sizeof(*T)*(n+n/4)); + + int i; + int n2=n>>1; + int log2n=lookup->log2n=rint(log((float)n)/log(2.f)); + lookup->n=n; + lookup->trig=T; + lookup->bitrev=bitrev; + +/* trig lookups... */ + + for(i=0;i>j;j++) + if((msb>>j)&i)acc|=1<scale=FLOAT_CONV(4.f/n); +} + +/* 8 point butterfly (in place, 4 register) */ +STIN void mdct_butterfly_8(DATA_TYPE *x){ + REG_TYPE r0 = x[6] + x[2]; + REG_TYPE r1 = x[6] - x[2]; + REG_TYPE r2 = x[4] + x[0]; + REG_TYPE r3 = x[4] - x[0]; + + x[6] = r0 + r2; + x[4] = r0 - r2; + + r0 = x[5] - x[1]; + r2 = x[7] - x[3]; + x[0] = r1 + r0; + x[2] = r1 - r0; + + r0 = x[5] + x[1]; + r1 = x[7] + x[3]; + x[3] = r2 + r3; + x[1] = r2 - r3; + x[7] = r1 + r0; + x[5] = r1 - r0; + +} + +/* 16 point butterfly (in place, 4 register) */ +STIN void mdct_butterfly_16(DATA_TYPE *x){ + REG_TYPE r0 = x[1] - x[9]; + REG_TYPE r1 = x[0] - x[8]; + + x[8] += x[0]; + x[9] += x[1]; + x[0] = MULT_NORM((r0 + r1) * cPI2_8); + x[1] = MULT_NORM((r0 - r1) * cPI2_8); + + r0 = x[3] - x[11]; + r1 = x[10] - x[2]; + x[10] += x[2]; + x[11] += x[3]; + x[2] = r0; + x[3] = r1; + + r0 = x[12] - x[4]; + r1 = x[13] - x[5]; + x[12] += x[4]; + x[13] += x[5]; + x[4] = MULT_NORM((r0 - r1) * cPI2_8); + x[5] = MULT_NORM((r0 + r1) * cPI2_8); + + r0 = x[14] - x[6]; + r1 = x[15] - x[7]; + x[14] += x[6]; + x[15] += x[7]; + x[6] = r0; + x[7] = r1; + + mdct_butterfly_8(x); + mdct_butterfly_8(x+8); +} + +/* 32 point butterfly (in place, 4 register) */ +STIN void mdct_butterfly_32(DATA_TYPE *x){ + REG_TYPE r0 = x[30] - x[14]; + REG_TYPE r1 = x[31] - x[15]; + + x[30] += x[14]; + x[31] += x[15]; + x[14] = r0; + x[15] = r1; + + r0 = x[28] - x[12]; + r1 = x[29] - x[13]; + x[28] += x[12]; + x[29] += x[13]; + x[12] = MULT_NORM( r0 * cPI1_8 - r1 * cPI3_8 ); + x[13] = MULT_NORM( r0 * cPI3_8 + r1 * cPI1_8 ); + + r0 = x[26] - x[10]; + r1 = x[27] - x[11]; + x[26] += x[10]; + x[27] += x[11]; + x[10] = MULT_NORM(( r0 - r1 ) * cPI2_8); + x[11] = MULT_NORM(( r0 + r1 ) * cPI2_8); + + r0 = x[24] - x[8]; + r1 = x[25] - x[9]; + x[24] += x[8]; + x[25] += x[9]; + x[8] = MULT_NORM( r0 * cPI3_8 - r1 * cPI1_8 ); + x[9] = MULT_NORM( r1 * cPI3_8 + r0 * cPI1_8 ); + + r0 = x[22] - x[6]; + r1 = x[7] - x[23]; + x[22] += x[6]; + x[23] += x[7]; + x[6] = r1; + x[7] = r0; + + r0 = x[4] - x[20]; + r1 = x[5] - x[21]; + x[20] += x[4]; + x[21] += x[5]; + x[4] = MULT_NORM( r1 * cPI1_8 + r0 * cPI3_8 ); + x[5] = MULT_NORM( r1 * cPI3_8 - r0 * cPI1_8 ); + + r0 = x[2] - x[18]; + r1 = x[3] - x[19]; + x[18] += x[2]; + x[19] += x[3]; + x[2] = MULT_NORM(( r1 + r0 ) * cPI2_8); + x[3] = MULT_NORM(( r1 - r0 ) * cPI2_8); + + r0 = x[0] - x[16]; + r1 = x[1] - x[17]; + x[16] += x[0]; + x[17] += x[1]; + x[0] = MULT_NORM( r1 * cPI3_8 + r0 * cPI1_8 ); + x[1] = MULT_NORM( r1 * cPI1_8 - r0 * cPI3_8 ); + + mdct_butterfly_16(x); + mdct_butterfly_16(x+16); + +} + +/* N point first stage butterfly (in place, 2 register) */ +STIN void mdct_butterfly_first(DATA_TYPE *T, + DATA_TYPE *x, + int points){ + + DATA_TYPE *x1 = x + points - 8; + DATA_TYPE *x2 = x + (points>>1) - 8; + REG_TYPE r0; + REG_TYPE r1; + + do{ + + r0 = x1[6] - x2[6]; + r1 = x1[7] - x2[7]; + x1[6] += x2[6]; + x1[7] += x2[7]; + x2[6] = MULT_NORM(r1 * T[1] + r0 * T[0]); + x2[7] = MULT_NORM(r1 * T[0] - r0 * T[1]); + + r0 = x1[4] - x2[4]; + r1 = x1[5] - x2[5]; + x1[4] += x2[4]; + x1[5] += x2[5]; + x2[4] = MULT_NORM(r1 * T[5] + r0 * T[4]); + x2[5] = MULT_NORM(r1 * T[4] - r0 * T[5]); + + r0 = x1[2] - x2[2]; + r1 = x1[3] - x2[3]; + x1[2] += x2[2]; + x1[3] += x2[3]; + x2[2] = MULT_NORM(r1 * T[9] + r0 * T[8]); + x2[3] = MULT_NORM(r1 * T[8] - r0 * T[9]); + + r0 = x1[0] - x2[0]; + r1 = x1[1] - x2[1]; + x1[0] += x2[0]; + x1[1] += x2[1]; + x2[0] = MULT_NORM(r1 * T[13] + r0 * T[12]); + x2[1] = MULT_NORM(r1 * T[12] - r0 * T[13]); + + x1-=8; + x2-=8; + T+=16; + + }while(x2>=x); +} + +/* N/stage point generic N stage butterfly (in place, 2 register) */ +STIN void mdct_butterfly_generic(DATA_TYPE *T, + DATA_TYPE *x, + int points, + int trigint){ + + DATA_TYPE *x1 = x + points - 8; + DATA_TYPE *x2 = x + (points>>1) - 8; + REG_TYPE r0; + REG_TYPE r1; + + do{ + + r0 = x1[6] - x2[6]; + r1 = x1[7] - x2[7]; + x1[6] += x2[6]; + x1[7] += x2[7]; + x2[6] = MULT_NORM(r1 * T[1] + r0 * T[0]); + x2[7] = MULT_NORM(r1 * T[0] - r0 * T[1]); + + T+=trigint; + + r0 = x1[4] - x2[4]; + r1 = x1[5] - x2[5]; + x1[4] += x2[4]; + x1[5] += x2[5]; + x2[4] = MULT_NORM(r1 * T[1] + r0 * T[0]); + x2[5] = MULT_NORM(r1 * T[0] - r0 * T[1]); + + T+=trigint; + + r0 = x1[2] - x2[2]; + r1 = x1[3] - x2[3]; + x1[2] += x2[2]; + x1[3] += x2[3]; + x2[2] = MULT_NORM(r1 * T[1] + r0 * T[0]); + x2[3] = MULT_NORM(r1 * T[0] - r0 * T[1]); + + T+=trigint; + + r0 = x1[0] - x2[0]; + r1 = x1[1] - x2[1]; + x1[0] += x2[0]; + x1[1] += x2[1]; + x2[0] = MULT_NORM(r1 * T[1] + r0 * T[0]); + x2[1] = MULT_NORM(r1 * T[0] - r0 * T[1]); + + T+=trigint; + x1-=8; + x2-=8; + + }while(x2>=x); +} + +STIN void mdct_butterflies(mdct_lookup *init, + DATA_TYPE *x, + int points){ + + DATA_TYPE *T=init->trig; + int stages=init->log2n-5; + int i,j; + + if(--stages>0){ + mdct_butterfly_first(T,x,points); + } + + for(i=1;--stages>0;i++){ + for(j=0;j<(1<>i)*j,points>>i,4<trig)_ogg_free(l->trig); + if(l->bitrev)_ogg_free(l->bitrev); + memset(l,0,sizeof(*l)); + } +} + +STIN void mdct_bitreverse(mdct_lookup *init, + DATA_TYPE *x){ + int n = init->n; + int *bit = init->bitrev; + DATA_TYPE *w0 = x; + DATA_TYPE *w1 = x = w0+(n>>1); + DATA_TYPE *T = init->trig+n; + + do{ + DATA_TYPE *x0 = x+bit[0]; + DATA_TYPE *x1 = x+bit[1]; + + REG_TYPE r0 = x0[1] - x1[1]; + REG_TYPE r1 = x0[0] + x1[0]; + REG_TYPE r2 = MULT_NORM(r1 * T[0] + r0 * T[1]); + REG_TYPE r3 = MULT_NORM(r1 * T[1] - r0 * T[0]); + + w1 -= 4; + + r0 = HALVE(x0[1] + x1[1]); + r1 = HALVE(x0[0] - x1[0]); + + w0[0] = r0 + r2; + w1[2] = r0 - r2; + w0[1] = r1 + r3; + w1[3] = r3 - r1; + + x0 = x+bit[2]; + x1 = x+bit[3]; + + r0 = x0[1] - x1[1]; + r1 = x0[0] + x1[0]; + r2 = MULT_NORM(r1 * T[2] + r0 * T[3]); + r3 = MULT_NORM(r1 * T[3] - r0 * T[2]); + + r0 = HALVE(x0[1] + x1[1]); + r1 = HALVE(x0[0] - x1[0]); + + w0[2] = r0 + r2; + w1[0] = r0 - r2; + w0[3] = r1 + r3; + w1[1] = r3 - r1; + + T += 4; + bit += 4; + w0 += 4; + + }while(w0n; + int n2=n>>1; + int n4=n>>2; + + /* rotate */ + + DATA_TYPE *iX = in+n2-7; + DATA_TYPE *oX = out+n2+n4; + DATA_TYPE *T = init->trig+n4; + + do{ + oX -= 4; + oX[0] = MULT_NORM(-iX[2] * T[3] - iX[0] * T[2]); + oX[1] = MULT_NORM (iX[0] * T[3] - iX[2] * T[2]); + oX[2] = MULT_NORM(-iX[6] * T[1] - iX[4] * T[0]); + oX[3] = MULT_NORM (iX[4] * T[1] - iX[6] * T[0]); + iX -= 8; + T += 4; + }while(iX>=in); + + iX = in+n2-8; + oX = out+n2+n4; + T = init->trig+n4; + + do{ + T -= 4; + oX[0] = MULT_NORM (iX[4] * T[3] + iX[6] * T[2]); + oX[1] = MULT_NORM (iX[4] * T[2] - iX[6] * T[3]); + oX[2] = MULT_NORM (iX[0] * T[1] + iX[2] * T[0]); + oX[3] = MULT_NORM (iX[0] * T[0] - iX[2] * T[1]); + iX -= 8; + oX += 4; + }while(iX>=in); + + mdct_butterflies(init,out+n2,n2); + mdct_bitreverse(init,out); + + /* roatate + window */ + + { + DATA_TYPE *oX1=out+n2+n4; + DATA_TYPE *oX2=out+n2+n4; + DATA_TYPE *iX =out; + T =init->trig+n2; + + do{ + oX1-=4; + + oX1[3] = MULT_NORM (iX[0] * T[1] - iX[1] * T[0]); + oX2[0] = -MULT_NORM (iX[0] * T[0] + iX[1] * T[1]); + + oX1[2] = MULT_NORM (iX[2] * T[3] - iX[3] * T[2]); + oX2[1] = -MULT_NORM (iX[2] * T[2] + iX[3] * T[3]); + + oX1[1] = MULT_NORM (iX[4] * T[5] - iX[5] * T[4]); + oX2[2] = -MULT_NORM (iX[4] * T[4] + iX[5] * T[5]); + + oX1[0] = MULT_NORM (iX[6] * T[7] - iX[7] * T[6]); + oX2[3] = -MULT_NORM (iX[6] * T[6] + iX[7] * T[7]); + + oX2+=4; + iX += 8; + T += 8; + }while(iXoX2); + } +} + +void mdct_forward(mdct_lookup *init, DATA_TYPE *in, DATA_TYPE *out){ + int n=init->n; + int n2=n>>1; + int n4=n>>2; + int n8=n>>3; + DATA_TYPE *w=alloca(n*sizeof(*w)); /* forward needs working space */ + DATA_TYPE *w2=w+n2; + + /* rotate */ + + /* window + rotate + step 1 */ + + REG_TYPE r0; + REG_TYPE r1; + DATA_TYPE *x0=in+n2+n4; + DATA_TYPE *x1=x0+1; + DATA_TYPE *T=init->trig+n2; + + int i=0; + + for(i=0;itrig+n2; + x0=out+n2; + + for(i=0;iscale); + x0[0] =MULT_NORM((w[0]*T[1]-w[1]*T[0])*init->scale); + w+=2; + T+=2; + } +} + diff --git a/Engine/lib/libvorbis/lib/mdct.h b/Engine/lib/libvorbis/lib/mdct.h new file mode 100644 index 000000000..58a6ac046 --- /dev/null +++ b/Engine/lib/libvorbis/lib/mdct.h @@ -0,0 +1,83 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: modified discrete cosine transform prototypes + last mod: $Id: mdct.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#ifndef _OGG_mdct_H_ +#define _OGG_mdct_H_ + +#include "vorbis/codec.h" + + + + + +/*#define MDCT_INTEGERIZED <- be warned there could be some hurt left here*/ +#ifdef MDCT_INTEGERIZED + +#define DATA_TYPE int +#define REG_TYPE register int +#define TRIGBITS 14 +#define cPI3_8 6270 +#define cPI2_8 11585 +#define cPI1_8 15137 + +#define FLOAT_CONV(x) ((int)((x)*(1<>TRIGBITS) +#define HALVE(x) ((x)>>1) + +#else + +#define DATA_TYPE float +#define REG_TYPE float +#define cPI3_8 .38268343236508977175F +#define cPI2_8 .70710678118654752441F +#define cPI1_8 .92387953251128675613F + +#define FLOAT_CONV(x) (x) +#define MULT_NORM(x) (x) +#define HALVE(x) ((x)*.5f) + +#endif + + +typedef struct { + int n; + int log2n; + + DATA_TYPE *trig; + int *bitrev; + + DATA_TYPE scale; +} mdct_lookup; + +extern void mdct_init(mdct_lookup *lookup,int n); +extern void mdct_clear(mdct_lookup *l); +extern void mdct_forward(mdct_lookup *init, DATA_TYPE *in, DATA_TYPE *out); +extern void mdct_backward(mdct_lookup *init, DATA_TYPE *in, DATA_TYPE *out); + +#endif + + + + + + + + + + + + diff --git a/Engine/lib/libvorbis/lib/misc.h b/Engine/lib/libvorbis/lib/misc.h new file mode 100644 index 000000000..27ecc2c09 --- /dev/null +++ b/Engine/lib/libvorbis/lib/misc.h @@ -0,0 +1,52 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: miscellaneous prototypes + last mod: $Id: misc.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#ifndef _V_RANDOM_H_ +#define _V_RANDOM_H_ +#include "vorbis/codec.h" + +extern int analysis_noisy; + +extern void *_vorbis_block_alloc(vorbis_block *vb,long bytes); +extern void _vorbis_block_ripcord(vorbis_block *vb); +extern void _analysis_output(char *base,int i,float *v,int n,int bark,int dB, + ogg_int64_t off); + +#ifdef DEBUG_MALLOC + +#define _VDBG_GRAPHFILE "malloc.m" +extern void *_VDBG_malloc(void *ptr,long bytes,char *file,long line); +extern void _VDBG_free(void *ptr,char *file,long line); + +#ifndef MISC_C +#undef _ogg_malloc +#undef _ogg_calloc +#undef _ogg_realloc +#undef _ogg_free + +#define _ogg_malloc(x) _VDBG_malloc(NULL,(x),__FILE__,__LINE__) +#define _ogg_calloc(x,y) _VDBG_malloc(NULL,(x)*(y),__FILE__,__LINE__) +#define _ogg_realloc(x,y) _VDBG_malloc((x),(y),__FILE__,__LINE__) +#define _ogg_free(x) _VDBG_free((x),__FILE__,__LINE__) +#endif +#endif + +#endif + + + + diff --git a/Engine/lib/libvorbis/lib/modes/floor_all.h b/Engine/lib/libvorbis/lib/modes/floor_all.h new file mode 100644 index 000000000..bd148902a --- /dev/null +++ b/Engine/lib/libvorbis/lib/modes/floor_all.h @@ -0,0 +1,248 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: key floor settings + last mod: $Id: floor_all.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#include "vorbis/codec.h" +#include "backends.h" +#include "books/floor/floor_books.h" + +static static_codebook *_floor_128x4_books[]={ + &_huff_book_line_128x4_class0, + &_huff_book_line_128x4_0sub0, + &_huff_book_line_128x4_0sub1, + &_huff_book_line_128x4_0sub2, + &_huff_book_line_128x4_0sub3, +}; +static static_codebook *_floor_256x4_books[]={ + &_huff_book_line_256x4_class0, + &_huff_book_line_256x4_0sub0, + &_huff_book_line_256x4_0sub1, + &_huff_book_line_256x4_0sub2, + &_huff_book_line_256x4_0sub3, +}; +static static_codebook *_floor_128x7_books[]={ + &_huff_book_line_128x7_class0, + &_huff_book_line_128x7_class1, + + &_huff_book_line_128x7_0sub1, + &_huff_book_line_128x7_0sub2, + &_huff_book_line_128x7_0sub3, + &_huff_book_line_128x7_1sub1, + &_huff_book_line_128x7_1sub2, + &_huff_book_line_128x7_1sub3, +}; +static static_codebook *_floor_256x7_books[]={ + &_huff_book_line_256x7_class0, + &_huff_book_line_256x7_class1, + + &_huff_book_line_256x7_0sub1, + &_huff_book_line_256x7_0sub2, + &_huff_book_line_256x7_0sub3, + &_huff_book_line_256x7_1sub1, + &_huff_book_line_256x7_1sub2, + &_huff_book_line_256x7_1sub3, +}; +static static_codebook *_floor_128x11_books[]={ + &_huff_book_line_128x11_class1, + &_huff_book_line_128x11_class2, + &_huff_book_line_128x11_class3, + + &_huff_book_line_128x11_0sub0, + &_huff_book_line_128x11_1sub0, + &_huff_book_line_128x11_1sub1, + &_huff_book_line_128x11_2sub1, + &_huff_book_line_128x11_2sub2, + &_huff_book_line_128x11_2sub3, + &_huff_book_line_128x11_3sub1, + &_huff_book_line_128x11_3sub2, + &_huff_book_line_128x11_3sub3, +}; +static static_codebook *_floor_128x17_books[]={ + &_huff_book_line_128x17_class1, + &_huff_book_line_128x17_class2, + &_huff_book_line_128x17_class3, + + &_huff_book_line_128x17_0sub0, + &_huff_book_line_128x17_1sub0, + &_huff_book_line_128x17_1sub1, + &_huff_book_line_128x17_2sub1, + &_huff_book_line_128x17_2sub2, + &_huff_book_line_128x17_2sub3, + &_huff_book_line_128x17_3sub1, + &_huff_book_line_128x17_3sub2, + &_huff_book_line_128x17_3sub3, +}; +static static_codebook *_floor_256x4low_books[]={ + &_huff_book_line_256x4low_class0, + &_huff_book_line_256x4low_0sub0, + &_huff_book_line_256x4low_0sub1, + &_huff_book_line_256x4low_0sub2, + &_huff_book_line_256x4low_0sub3, +}; +static static_codebook *_floor_1024x27_books[]={ + &_huff_book_line_1024x27_class1, + &_huff_book_line_1024x27_class2, + &_huff_book_line_1024x27_class3, + &_huff_book_line_1024x27_class4, + + &_huff_book_line_1024x27_0sub0, + &_huff_book_line_1024x27_1sub0, + &_huff_book_line_1024x27_1sub1, + &_huff_book_line_1024x27_2sub0, + &_huff_book_line_1024x27_2sub1, + &_huff_book_line_1024x27_3sub1, + &_huff_book_line_1024x27_3sub2, + &_huff_book_line_1024x27_3sub3, + &_huff_book_line_1024x27_4sub1, + &_huff_book_line_1024x27_4sub2, + &_huff_book_line_1024x27_4sub3, +}; +static static_codebook *_floor_2048x27_books[]={ + &_huff_book_line_2048x27_class1, + &_huff_book_line_2048x27_class2, + &_huff_book_line_2048x27_class3, + &_huff_book_line_2048x27_class4, + + &_huff_book_line_2048x27_0sub0, + &_huff_book_line_2048x27_1sub0, + &_huff_book_line_2048x27_1sub1, + &_huff_book_line_2048x27_2sub0, + &_huff_book_line_2048x27_2sub1, + &_huff_book_line_2048x27_3sub1, + &_huff_book_line_2048x27_3sub2, + &_huff_book_line_2048x27_3sub3, + &_huff_book_line_2048x27_4sub1, + &_huff_book_line_2048x27_4sub2, + &_huff_book_line_2048x27_4sub3, +}; + +static static_codebook *_floor_512x17_books[]={ + &_huff_book_line_512x17_class1, + &_huff_book_line_512x17_class2, + &_huff_book_line_512x17_class3, + + &_huff_book_line_512x17_0sub0, + &_huff_book_line_512x17_1sub0, + &_huff_book_line_512x17_1sub1, + &_huff_book_line_512x17_2sub1, + &_huff_book_line_512x17_2sub2, + &_huff_book_line_512x17_2sub3, + &_huff_book_line_512x17_3sub1, + &_huff_book_line_512x17_3sub2, + &_huff_book_line_512x17_3sub3, +}; + +static static_codebook **_floor_books[10]={ + _floor_128x4_books, + _floor_256x4_books, + _floor_128x7_books, + _floor_256x7_books, + _floor_128x11_books, + _floor_128x17_books, + _floor_256x4low_books, + _floor_1024x27_books, + _floor_2048x27_books, + _floor_512x17_books, +}; + +static vorbis_info_floor1 _floor[10]={ + /* 128 x 4 */ + { + 1,{0},{4},{2},{0}, + {{1,2,3,4}}, + 4,{0,128, 33,8,16,70}, + + 60,30,500, 1.,18., -1 + }, + /* 256 x 4 */ + { + 1,{0},{4},{2},{0}, + {{1,2,3,4}}, + 4,{0,256, 66,16,32,140}, + + 60,30,500, 1.,18., -1 + }, + /* 128 x 7 */ + { + 2,{0,1},{3,4},{2,2},{0,1}, + {{-1,2,3,4},{-1,5,6,7}}, + 4,{0,128, 14,4,58, 2,8,28,90}, + + 60,30,500, 1.,18., -1 + }, + /* 256 x 7 */ + { + 2,{0,1},{3,4},{2,2},{0,1}, + {{-1,2,3,4},{-1,5,6,7}}, + 4,{0,256, 28,8,116, 4,16,56,180}, + + 60,30,500, 1.,18., -1 + }, + /* 128 x 11 */ + { + 4,{0,1,2,3},{2,3,3,3},{0,1,2,2},{-1,0,1,2}, + {{3},{4,5},{-1,6,7,8},{-1,9,10,11}}, + + 2,{0,128, 8,33, 4,16,70, 2,6,12, 23,46,90}, + + 60,30,500, 1,18., -1 + }, + /* 128 x 17 */ + { + 6,{0,1,1,2,3,3},{2,3,3,3},{0,1,2,2},{-1,0,1,2}, + {{3},{4,5},{-1,6,7,8},{-1,9,10,11}}, + 2,{0,128, 12,46, 4,8,16, 23,33,70, 2,6,10, 14,19,28, 39,58,90}, + + 60,30,500, 1,18., -1 + }, + /* 256 x 4 (low bitrate version) */ + { + 1,{0},{4},{2},{0}, + {{1,2,3,4}}, + 4,{0,256, 66,16,32,140}, + + 60,30,500, 1.,18., -1 + }, + /* 1024 x 27 */ + { + 8,{0,1,2,2,3,3,4,4},{3,4,3,4,3},{0,1,1,2,2},{-1,0,1,2,3}, + {{4},{5,6},{7,8},{-1,9,10,11},{-1,12,13,14}}, + 2,{0,1024, 93,23,372, 6,46,186,750, 14,33,65, 130,260,556, + 3,10,18,28, 39,55,79,111, 158,220,312, 464,650,850}, + + 60,30,500, 3,18., -1 /* lowpass */ + }, + /* 2048 x 27 */ + { + 8,{0,1,2,2,3,3,4,4},{3,4,3,4,3},{0,1,1,2,2},{-1,0,1,2,3}, + {{4},{5,6},{7,8},{-1,9,10,11},{-1,12,13,14}}, + 2,{0,2048, 186,46,744, 12,92,372,1500, 28,66,130, 260,520,1112, + 6,20,36,56, 78,110,158,222, 316,440,624, 928,1300,1700}, + + 60,30,500, 3,18., -1 /* lowpass */ + }, + /* 512 x 17 */ + { + 6,{0,1,1,2,3,3},{2,3,3,3},{0,1,2,2},{-1,0,1,2}, + {{3},{4,5},{-1,6,7,8},{-1,9,10,11}}, + 2,{0,512, 46,186, 16,33,65, 93,130,278, + 7,23,39, 55,79,110, 156,232,360}, + + 60,30,500, 1,18., -1 /* lowpass! */ + }, + +}; + diff --git a/Engine/lib/libvorbis/lib/modes/psych_11.h b/Engine/lib/libvorbis/lib/modes/psych_11.h new file mode 100644 index 000000000..bb6e70cd3 --- /dev/null +++ b/Engine/lib/libvorbis/lib/modes/psych_11.h @@ -0,0 +1,51 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: 11kHz settings + last mod: $Id: psych_11.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +static double _psy_lowpass_11[3]={4.5,5.5,30.,}; + +static att3 _psy_tone_masteratt_11[3]={ + {{ 30, 25, 12}, 0, 0}, /* 0 */ + {{ 30, 25, 12}, 0, 0}, /* 0 */ + {{ 20, 0, -14}, 0, 0}, /* 0 */ +}; + +static vp_adjblock _vp_tonemask_adj_11[3]={ + /* adjust for mode zero */ + /* 63 125 250 500 1 2 4 8 16 */ + {{-20,-20,-20,-20,-20,-16,-10, 0, 0, 0, 0,10, 2, 0,99,99,99}}, /* 0 */ + {{-20,-20,-20,-20,-20,-16,-10, 0, 0, 0, 0, 5, 0, 0,99,99,99}}, /* 1 */ + {{-20,-20,-20,-20,-20,-16,-10, 0, 0, 0, 0, 0, 0, 0,99,99,99}}, /* 2 */ +}; + + +static noise3 _psy_noisebias_11[3]={ + /* 63 125 250 500 1k 2k 4k 8k 16k*/ + {{{-10,-10,-10,-10, -5, -5, -5, 0, 4, 10, 10, 12, 12, 12, 99, 99, 99}, + {-15,-15,-15,-15,-10,-10, -5, 0, 0, 4, 4, 5, 5, 10, 99, 99, 99}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10, -6, -8, -8, -6, -6, 99, 99, 99}}}, + + {{{-10,-10,-10,-10, -5, -5, -5, 0, 4, 10, 10, 12, 12, 12, 99, 99, 99}, + {-15,-15,-15,-15,-10,-10, -5, -5, -5, 0, 0, 0, 0, 0, 99, 99, 99}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10, -6, -8, -8, -6, -6, 99, 99, 99}}}, + + {{{-15,-15,-15,-15,-15,-12,-10, -8, 0, 2, 4, 4, 5, 5, 99, 99, 99}, + {-30,-30,-30,-30,-26,-22,-20,-14,-12,-12,-10,-10,-10,-10, 99, 99, 99}, + {-30,-30,-30,-30,-26,-26,-26,-26,-26,-26,-26,-26,-26,-24, 99, 99, 99}}}, +}; + +static double _noise_thresh_11[3]={ .3,.5,.5 }; + diff --git a/Engine/lib/libvorbis/lib/modes/psych_16.h b/Engine/lib/libvorbis/lib/modes/psych_16.h new file mode 100644 index 000000000..023993470 --- /dev/null +++ b/Engine/lib/libvorbis/lib/modes/psych_16.h @@ -0,0 +1,136 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: 16kHz settings + last mod: $Id: psych_16.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +/* stereo mode by base quality level */ +static adj_stereo _psy_stereo_modes_16[4]={ + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 */ + {{ 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}, + { 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, + { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 4, 4}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + {{ 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}, + { 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, + { 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 4, 4, 4, 4, 4}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + {{ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}, + { 5, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}, + { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, +}; + +static double _psy_lowpass_16[4]={6.5,8,30.,99.}; + +static att3 _psy_tone_masteratt_16[4]={ + {{ 30, 25, 12}, 0, 0}, /* 0 */ + {{ 25, 22, 12}, 0, 0}, /* 0 */ + {{ 20, 12, 0}, 0, 0}, /* 0 */ + {{ 15, 0, -14}, 0, 0}, /* 0 */ +}; + +static vp_adjblock _vp_tonemask_adj_16[4]={ + /* adjust for mode zero */ + /* 63 125 250 500 1 2 4 8 16 */ + {{-20,-20,-20,-20,-20,-16,-10, 0, 0, 0, 0,10, 0, 0, 0, 0, 0}}, /* 0 */ + {{-20,-20,-20,-20,-20,-16,-10, 0, 0, 0, 0,10, 0, 0, 0, 0, 0}}, /* 1 */ + {{-20,-20,-20,-20,-20,-16,-10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, /* 2 */ + {{-30,-30,-30,-30,-30,-26,-20,-10, -5, 0, 0, 0, 0, 0, 0, 0, 0}}, /* 2 */ +}; + + +static noise3 _psy_noisebias_16_short[4]={ + /* 63 125 250 500 1k 2k 4k 8k 16k*/ + {{{-15,-15,-15,-15,-15,-10,-10,-5, 4, 10, 10, 10, 10, 12, 12, 14, 20}, + {-15,-15,-15,-15,-15,-10,-10, -5, 0, 0, 4, 5, 5, 6, 8, 8, 15}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10, -6, -8, -8, -6, -6, -6, -6, -6}}}, + + {{{-15,-15,-15,-15,-15,-10,-10,-5, 4, 6, 6, 6, 6, 8, 10, 12, 20}, + {-15,-15,-15,-15,-15,-15,-15,-10, -5, -5, -5, 4, 5, 6, 8, 8, 15}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10,-10,-10,-10,-10,-10,-10,-10,-10}}}, + + {{{-15,-15,-15,-15,-15,-12,-10, -8, 0, 2, 4, 4, 5, 5, 5, 8, 12}, + {-20,-20,-20,-20,-16,-12,-20,-14,-10,-10, -8, 0, 0, 0, 0, 2, 5}, + {-30,-30,-30,-30,-26,-26,-26,-26,-26,-26,-26,-26,-26,-24,-20,-20,-20}}}, + + {{{-15,-15,-15,-15,-15,-12,-10, -8, -5, -5, -5, -5, -5, 0, 0, 0, 6}, + {-30,-30,-30,-30,-26,-22,-20,-14,-12,-12,-10,-10,-10,-10,-10,-10, -6}, + {-30,-30,-30,-30,-26,-26,-26,-26,-26,-26,-26,-26,-26,-24,-20,-20,-20}}}, +}; + +static noise3 _psy_noisebias_16_impulse[4]={ + /* 63 125 250 500 1k 2k 4k 8k 16k*/ + {{{-15,-15,-15,-15,-15,-10,-10,-5, 4, 10, 10, 10, 10, 12, 12, 14, 20}, + {-15,-15,-15,-15,-15,-10,-10, -5, 0, 0, 4, 5, 5, 6, 8, 8, 15}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10, -6, -8, -8, -6, -6, -6, -6, -6}}}, + + {{{-15,-15,-15,-15,-15,-10,-10,-5, 4, 4, 4, 4, 5, 5, 6, 8, 15}, + {-15,-15,-15,-15,-15,-15,-15,-10, -5, -5, -5, 0, 0, 0, 0, 4, 10}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10,-10,-10,-10,-10,-10,-10,-10,-10}}}, + + {{{-15,-15,-15,-15,-15,-12,-10, -8, 0, 0, 0, 0, 0, 0, 0, 4, 10}, + {-20,-20,-20,-20,-16,-12,-20,-14,-10,-10,-10,-10,-10,-10,-10, -7, -5}, + {-30,-30,-30,-30,-26,-26,-26,-26,-26,-26,-26,-26,-26,-24,-20,-20,-20}}}, + + {{{-15,-15,-15,-15,-15,-12,-10, -8, -5, -5, -5, -5, -5, 0, 0, 0, 6}, + {-30,-30,-30,-30,-26,-22,-20,-18,-18,-18,-20,-20,-20,-20,-20,-20,-16}, + {-30,-30,-30,-30,-26,-26,-26,-26,-26,-26,-26,-26,-26,-24,-20,-20,-20}}}, +}; + +static noise3 _psy_noisebias_16[4]={ + /* 63 125 250 500 1k 2k 4k 8k 16k*/ + {{{-10,-10,-10,-10, -5, -5, -5, 0, 4, 6, 8, 8, 10, 10, 10, 14, 20}, + {-10,-10,-10,-10,-10, -5, -2, -2, 0, 0, 0, 4, 5, 6, 8, 8, 15}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10, -6, -8, -8, -6, -6, -6, -6, -6}}}, + + {{{-10,-10,-10,-10, -5, -5, -5, 0, 4, 6, 6, 6, 6, 8, 10, 12, 20}, + {-15,-15,-15,-15,-15,-10, -5, -5, 0, 0, 0, 4, 5, 6, 8, 8, 15}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10, -6, -8, -8, -6, -6, -6, -6, -6}}}, + + {{{-15,-15,-15,-15,-15,-12,-10, -8, 0, 2, 4, 4, 5, 5, 5, 8, 12}, + {-20,-20,-20,-20,-16,-12,-20,-10, -5, -5, 0, 0, 0, 0, 0, 2, 5}, + {-30,-30,-30,-30,-26,-26,-26,-26,-26,-26,-26,-26,-26,-24,-20,-20,-20}}}, + + {{{-15,-15,-15,-15,-15,-12,-10, -8, -5, -5, -5, -5, -5, 0, 0, 0, 6}, + {-30,-30,-30,-30,-26,-22,-20,-14,-12,-12,-10,-10,-10,-10,-10,-10, -6}, + {-30,-30,-30,-30,-26,-26,-26,-26,-26,-26,-26,-26,-26,-24,-20,-20,-20}}}, +}; + +static noiseguard _psy_noiseguards_16[4]={ + {10,10,-1}, + {10,10,-1}, + {20,20,-1}, + {20,20,-1}, +}; + +static double _noise_thresh_16[4]={ .3,.5,.5,.5 }; + +static int _noise_start_16[3]={ 256,256,9999 }; +static int _noise_part_16[4]={ 8,8,8,8 }; + +static int _psy_ath_floater_16[4]={ + -100,-100,-100,-105, +}; + +static int _psy_ath_abs_16[4]={ + -130,-130,-130,-140, +}; + + + diff --git a/Engine/lib/libvorbis/lib/modes/psych_44.h b/Engine/lib/libvorbis/lib/modes/psych_44.h new file mode 100644 index 000000000..531316ee3 --- /dev/null +++ b/Engine/lib/libvorbis/lib/modes/psych_44.h @@ -0,0 +1,666 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: key psychoacoustic settings for 44.1/48kHz + last mod: $Id: psych_44.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + + +/* preecho trigger settings *****************************************/ + +static vorbis_info_psy_global _psy_global_44[5]={ + + {8, /* lines per eighth octave */ + {20.f,14.f,12.f,12.f,12.f,12.f,12.f}, + {-60.f,-30.f,-40.f,-40.f,-40.f,-40.f,-40.f}, 2,-75.f, + -6.f, + {99.},{{99.},{99.}},{0},{0},{{0.},{0.}} + }, + {8, /* lines per eighth octave */ + {14.f,10.f,10.f,10.f,10.f,10.f,10.f}, + {-40.f,-30.f,-25.f,-25.f,-25.f,-25.f,-25.f}, 2,-80.f, + -6.f, + {99.},{{99.},{99.}},{0},{0},{{0.},{0.}} + }, + {8, /* lines per eighth octave */ + {12.f,10.f,10.f,10.f,10.f,10.f,10.f}, + {-20.f,-20.f,-15.f,-15.f,-15.f,-15.f,-15.f}, 0,-80.f, + -6.f, + {99.},{{99.},{99.}},{0},{0},{{0.},{0.}} + }, + {8, /* lines per eighth octave */ + {10.f,8.f,8.f,8.f,8.f,8.f,8.f}, + {-20.f,-15.f,-12.f,-12.f,-12.f,-12.f,-12.f}, 0,-80.f, + -6.f, + {99.},{{99.},{99.}},{0},{0},{{0.},{0.}} + }, + {8, /* lines per eighth octave */ + {10.f,6.f,6.f,6.f,6.f,6.f,6.f}, + {-15.f,-15.f,-12.f,-12.f,-12.f,-12.f,-12.f}, 0,-85.f, + -6.f, + {99.},{{99.},{99.}},{0},{0},{{0.},{0.}} + }, +}; + +/* noise compander lookups * low, mid, high quality ****************/ +static compandblock _psy_compand_44[6]={ + /* sub-mode Z short */ + {{ + 0, 1, 2, 3, 4, 5, 6, 7, /* 7dB */ + 8, 9,10,11,12,13,14, 15, /* 15dB */ + 16,17,18,19,20,21,22, 23, /* 23dB */ + 24,25,26,27,28,29,30, 31, /* 31dB */ + 32,33,34,35,36,37,38, 39, /* 39dB */ + }}, + /* mode_Z nominal short */ + {{ + 0, 1, 2, 3, 4, 5, 6, 6, /* 7dB */ + 7, 7, 7, 7, 6, 6, 6, 7, /* 15dB */ + 7, 8, 9,10,11,12,13, 14, /* 23dB */ + 15,16,17,17,17,18,18, 19, /* 31dB */ + 19,19,20,21,22,23,24, 25, /* 39dB */ + }}, + /* mode A short */ + {{ + 0, 1, 2, 3, 4, 5, 5, 5, /* 7dB */ + 6, 6, 6, 5, 4, 4, 4, 4, /* 15dB */ + 4, 4, 5, 5, 5, 6, 6, 6, /* 23dB */ + 7, 7, 7, 8, 8, 8, 9, 10, /* 31dB */ + 11,12,13,14,15,16,17, 18, /* 39dB */ + }}, + /* sub-mode Z long */ + {{ + 0, 1, 2, 3, 4, 5, 6, 7, /* 7dB */ + 8, 9,10,11,12,13,14, 15, /* 15dB */ + 16,17,18,19,20,21,22, 23, /* 23dB */ + 24,25,26,27,28,29,30, 31, /* 31dB */ + 32,33,34,35,36,37,38, 39, /* 39dB */ + }}, + /* mode_Z nominal long */ + {{ + 0, 1, 2, 3, 4, 5, 6, 7, /* 7dB */ + 8, 9,10,11,12,12,13, 13, /* 15dB */ + 13,14,14,14,15,15,15, 15, /* 23dB */ + 16,16,17,17,17,18,18, 19, /* 31dB */ + 19,19,20,21,22,23,24, 25, /* 39dB */ + }}, + /* mode A long */ + {{ + 0, 1, 2, 3, 4, 5, 6, 7, /* 7dB */ + 8, 8, 7, 6, 5, 4, 4, 4, /* 15dB */ + 4, 4, 5, 5, 5, 6, 6, 6, /* 23dB */ + 7, 7, 7, 8, 8, 8, 9, 10, /* 31dB */ + 11,12,13,14,15,16,17, 18, /* 39dB */ + }} +}; + +/* tonal masking curve level adjustments *************************/ + +static vp_adjblock _vp_tonemask_adj_longblock[12]={ + + /* 63 125 250 500 1 2 4 8 16 */ + + {{ -3, -8,-13,-15,-10,-10,-10,-10,-10,-10,-10, 0, 0, 0, 0, 0, 0}}, /* -1 */ + +/* {{-15,-15,-15,-15,-10, -8, -4, -2, 0, 0, 0, 10, 0, 0, 0, 0, 0}}, 0 */ + {{ -4,-10,-14,-16,-15,-14,-13,-12,-12,-12,-11, -1, -1, -1, -1, -1, 0}}, /* 0 */ + +/* {{-15,-15,-15,-15,-15,-12,-10, -8, 0, 0, 0, 5, 0, 0, 0, 0, 0}}, 1 */ + {{ -6,-12,-14,-16,-15,-15,-14,-13,-13,-12,-12, -2, -2, -1, -1, -1, 0}}, /* 1 */ + +/* {{-15,-15,-15,-15,-15,-12,-10, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, 2 */ + {{-12,-13,-14,-16,-16,-16,-15,-14,-13,-12,-12, -6, -3, -1, -1, -1, 0}}, /* 2 */ + +/* {{-15,-15,-15,-15,-15,-12,-10, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, 3 */ + {{-15,-15,-15,-16,-16,-16,-16,-14,-13,-13,-13,-10, -4, -2, -1, -1, 0}}, /* 3 */ + +/* {{-15,-15,-15,-15,-15,-12,-10, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, *//* 4 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-13,-11, -7 -3, -1, -1 , 0}}, /* 4 */ + +/* {{-15,-15,-15,-15,-15,-12,-10, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, 5 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-13,-11, -7 -3, -1, -1 , 0}}, /* 5 */ + +/* {{-15,-15,-15,-15,-15,-12,-10, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, 6 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-14,-12, -8, -4, -2, -2, 0}}, /* 6 */ + +/* {{-15,-15,-15,-15,-15,-12,-10, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, 7 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-14,-12, -9, -4, -2, -2, 0}}, /* 7 */ + +/* {{-15,-15,-15,-15,-15,-12,-10, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, 8 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-14,-12, -9, -4, -2, -2, 0}}, /* 8 */ + +/* {{-15,-15,-15,-15,-15,-12,-10, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, 9 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-14,-12, -9, -4, -2, -2, 0}}, /* 9 */ + +/* {{-15,-15,-15,-15,-15,-12,-10, -8, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, 10 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-14,-12, -9, -4, -2, -2, 0}}, /* 10 */ +}; + +static vp_adjblock _vp_tonemask_adj_otherblock[12]={ + /* 63 125 250 500 1 2 4 8 16 */ + + {{ -3, -8,-13,-15,-10,-10, -9, -9, -9, -9, -9, 1, 1, 1, 1, 1, 1}}, /* -1 */ + +/* {{-20,-20,-20,-20,-14,-12,-10, -8, -4, 0, 0, 10, 0, 0, 0, 0, 0}}, 0 */ + {{ -4,-10,-14,-16,-14,-13,-12,-12,-11,-11,-10, 0, 0, 0, 0, 0, 0}}, /* 0 */ + +/* {{-20,-20,-20,-20,-20,-18,-16,-14,-10, 0, 0, 5, 0, 0, 0, 0, 0}}, 1 */ + {{ -6,-12,-14,-16,-15,-15,-14,-13,-13,-12,-12, -2, -2, -1, 0, 0, 0}}, /* 1 */ + +/* {{-20,-20,-20,-20,-20,-18,-16,-14,-10, 0, 0, 0, 0, 0, 0, 0, 0}}, 2 */ + {{-12,-13,-14,-16,-16,-16,-15,-14,-13,-12,-12, -5, -2, -1, 0, 0, 0}}, /* 2 */ + +/* {{-20,-20,-20,-20,-20,-18,-16,-14,-10, 0, 0, 0, 0, 0, 0, 0, 0}}, 3 */ + {{-15,-15,-15,-16,-16,-16,-16,-14,-13,-13,-13,-10, -4, -2, 0, 0, 0}}, /* 3 */ + +/* {{-20,-20,-20,-20,-20,-18,-16,-14,-10, 0, 0, 0, 0, 0, 0, 0, 0}}, 4 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-13,-11, -7 -3, -1, -1 , 0}}, /* 4 */ + +/* {{-20,-20,-20,-20,-20,-18,-16,-14,-10, 0, 0, 0, 0, 0, 0, 0, 0}}, 5 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-13,-11, -7 -3, -1, -1 , 0}}, /* 5 */ + +/* {{-20,-20,-20,-20,-20,-18,-16,-14,-10, 0, 0, 0, 0, 0, 0, 0, 0}}, 6 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-14,-12, -8, -4, -2, -2, 0}}, /* 6 */ + +/* {{-20,-20,-20,-20,-20,-18,-16,-14,-10, 0, 0, 0, 0, 0, 0, 0, 0}}, 7 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-14,-12, -9, -4, -2, -2, 0}}, /* 7 */ + +/* {{-20,-20,-20,-20,-20,-18,-16,-14,-10, 0, 0, 0, 0, 0, 0, 0, 0}}, 8 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-14,-12, -9, -4, -2, -2, 0}}, /* 8 */ + +/* {{-20,-20,-20,-20,-20,-18,-16,-14,-10, 0, 0, 0, 0, 0, 0, 0, 0}}, 9 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-14,-12, -9, -4, -2, -2, 0}}, /* 9 */ + +/* {{-20,-20,-20,-20,-20,-18,-16,-14,-10, 0, 0, 0, 0, 0, 0, 0, 0}}, 10 */ + {{-16,-16,-16,-16,-16,-16,-16,-15,-14,-14,-14,-12, -9, -4, -2, -2, 0}}, /* 10 */ +}; + +/* noise bias (transition block) */ +static noise3 _psy_noisebias_trans[12]={ + /* 63 125 250 500 1k 2k 4k 8k 16k*/ + /* -1 */ + {{{-10,-10,-10,-10,-10, -4, 0, 0, 4, 8, 8, 8, 8, 10, 12, 14, 20}, + {-30,-30,-30,-30,-26,-20,-16, -8, -6, -6, -2, 2, 2, 3, 6, 6, 15}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10, -6, -8, -8, -6, -6, -6, -4, -2}}}, + /* 0 + {{{-15,-15,-15,-15,-15,-12,-10, -8, 0, 2, 4, 4, 5, 5, 5, 8, 10}, + {-30,-30,-30,-30,-26,-22,-20,-14, -8, -4, 0, 0, 0, 0, 2, 4, 10}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -6, -6, -6, -6, -4, -4, -4, -2}}},*/ + {{{-15,-15,-15,-15,-15,-12, -6, -4, 0, 2, 4, 4, 5, 5, 5, 8, 10}, + {-30,-30,-30,-30,-26,-22,-20,-14, -8, -4, 0, 0, 0, 0, 2, 3, 6}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -6, -6, -6, -6, -4, -4, -4, -2}}}, + /* 1 + {{{-15,-15,-15,-15,-15,-12,-10, -8, 0, 2, 4, 4, 5, 5, 5, 8, 10}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -4, -2, -2, -2, -2, 0, 2, 8}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -8, -8, -8, -8, -6, -6, -6, -4}}},*/ + {{{-15,-15,-15,-15,-15,-12,-10, -8, 0, 2, 4, 4, 5, 5, 5, 8, 10}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -4, -2, -2, -2, -2, 0, 1, 4}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -8, -8, -8, -8, -6, -6, -6, -4}}}, + /* 2 + {{{-15,-15,-15,-15,-15,-12,-10, -8, 0, 2, 2, 2, 4, 4, 5, 6, 10}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -4, -2, -2, -2, -2, 0, 2, 6}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10,-10,-10,-10,-10, -8, -8, -8, -4}}}, */ + {{{-15,-15,-15,-15,-15,-12,-10, -8, 0, 2, 2, 2, 4, 4, 5, 6, 10}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -4, -3, -3, -3, -2, -1, 0, 3}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10,-10,-10,-10,-10, -8, -8, -7, -4}}}, + /* 3 + {{{-15,-15,-15,-15,-15,-12,-10, -8, 0, 2, 2, 2, 4, 4, 4, 5, 8}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -4, -3, -3, -3, -3, -1, 1, 6}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10,-10,-10,-10,-10, -8, -8, -8, -4}}},*/ + {{{-15,-15,-15,-15,-15,-12,-10, -8, 0, 2, 2, 2, 4, 4, 4, 5, 8}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -4, -3, -3, -3, -3, -2, 0, 2}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10,-10,-10,-10,-10, -8, -8, -8, -4}}}, + /* 4 + {{{-20,-20,-20,-20,-20,-18,-14, -8, -1, 1, 1, 1, 2, 3, 3, 4, 7}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -4, -3, -3, -3, -3, -1, 1, 5}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10,-10,-10,-10,-10, -8, -8, -8, -4}}},*/ + {{{-20,-20,-20,-20,-20,-18,-14, -8, -1, 1, 1, 1, 2, 3, 3, 4, 7}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -4, -3, -3, -3, -3, -2, -1, 1}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10,-10,-10,-10,-10, -8, -8, -8, -4}}}, + /* 5 + {{{-24,-24,-24,-24,-20,-18,-14, -8, -1, 1, 1, 1, 2, 3, 3, 4, 7}, + {-32,-32,-32,-32,-28,-24,-22,-16,-12, -6, -4, -4, -4, -4, -2, -1, 2}, + {-34,-34,-34,-34,-30,-24,-24,-18,-14,-12,-12,-12,-12,-10,-10, -9, -5}}}, */ + {{{-24,-24,-24,-24,-20,-18,-14, -8, -1, 1, 1, 1, 2, 3, 3, 4, 7}, + {-32,-32,-32,-32,-28,-24,-22,-16,-12, -6, -4, -4, -4, -4, -3, -1, 0}, + {-34,-34,-34,-34,-30,-24,-24,-18,-14,-12,-12,-12,-12,-10,-10, -9, -5}}}, + /* 6 + {{{-24,-24,-24,-24,-20,-18,-14, -8, -1, 1, 1, 1, 2, 3, 3, 4, 7}, + {-32,-32,-32,-32,-28,-24,-24,-18,-14, -8, -6, -6, -6, -6, -4, -2, 1}, + {-34,-34,-34,-34,-30,-26,-24,-18,-17,-15,-15,-15,-15,-13,-13,-12, -8}}},*/ + {{{-24,-24,-24,-24,-20,-18,-14, -8, -1, 1, 1, 1, 2, 3, 3, 4, 7}, + {-32,-32,-32,-32,-28,-24,-24,-18,-14, -8, -6, -6, -6, -6, -5, -2, 0}, + {-34,-34,-34,-34,-30,-26,-26,-24,-22,-19,-19,-19,-19,-18,-17,-16,-12}}}, + /* 7 + {{{-24,-24,-24,-24,-20,-18,-14, -8, -1, 1, 1, 1, 2, 3, 3, 4, 7}, + {-32,-32,-32,-32,-28,-24,-24,-18,-14,-12,-10, -8, -8, -8, -6, -4, 0}, + {-34,-34,-34,-34,-30,-26,-26,-24,-22,-19,-19,-19,-19,-18,-17,-16,-12}}},*/ + {{{-24,-24,-24,-24,-20,-18,-14, -8, -1, 1, 1, 1, 2, 3, 3, 4, 7}, + {-32,-32,-32,-32,-28,-24,-24,-24,-18,-14,-12,-10,-10,-10, -8, -6, -2}, + {-34,-34,-34,-34,-30,-26,-26,-26,-24,-24,-24,-24,-24,-24,-24,-20,-16}}}, + /* 8 + {{{-24,-24,-24,-24,-22,-20,-15,-10, -8, -2, 0, 0, 0, 1, 2, 3, 7}, + {-36,-36,-36,-36,-30,-30,-30,-24,-18,-14,-12,-10,-10,-10, -8, -6, -2}, + {-36,-36,-36,-36,-34,-30,-28,-26,-24,-24,-24,-24,-24,-24,-24,-20,-16}}},*/ + {{{-24,-24,-24,-24,-22,-20,-15,-10, -8, -2, 0, 0, 0, 1, 2, 3, 7}, + {-36,-36,-36,-36,-30,-30,-30,-24,-20,-16,-16,-16,-16,-14,-12,-10, -7}, + {-36,-36,-36,-36,-34,-30,-28,-26,-24,-30,-30,-30,-30,-30,-30,-24,-20}}}, + /* 9 + {{{-28,-28,-28,-28,-28,-28,-28,-20,-14, -8, -4, -4, -4, -4, -4, -2, 2}, + {-36,-36,-36,-36,-34,-32,-32,-28,-20,-16,-16,-16,-16,-14,-12,-10, -7}, + {-40,-40,-40,-40,-40,-40,-40,-32,-30,-30,-30,-30,-30,-30,-30,-24,-20}}},*/ + {{{-28,-28,-28,-28,-28,-28,-28,-20,-14, -8, -4, -4, -4, -4, -4, -2, 2}, + {-38,-38,-38,-38,-36,-34,-34,-30,-24,-20,-20,-20,-20,-18,-16,-12,-10}, + {-40,-40,-40,-40,-40,-40,-40,-38,-35,-35,-35,-35,-35,-35,-35,-35,-30}}}, + /* 10 */ + {{{-30,-30,-30,-30,-30,-30,-30,-28,-20,-14,-14,-14,-14,-14,-14,-12,-10}, + {-40,-40,-40,-40,-40,-40,-40,-40,-35,-30,-30,-30,-30,-30,-30,-30,-20}, + {-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40}}}, +}; + +/* noise bias (long block) */ +static noise3 _psy_noisebias_long[12]={ + /*63 125 250 500 1k 2k 4k 8k 16k*/ + /* -1 */ + {{{-10,-10,-10,-10,-10, -4, 0, 0, 0, 6, 6, 6, 6, 10, 10, 12, 20}, + {-20,-20,-20,-20,-20,-20,-10, -2, 0, 0, 0, 0, 0, 2, 4, 6, 15}, + {-20,-20,-20,-20,-20,-20,-20,-10, -6, -6, -6, -6, -6, -4, -4, -4, -2}}}, + + /* 0 */ + /* {{{-10,-10,-10,-10,-10,-10, -8, 2, 2, 2, 4, 4, 5, 5, 5, 8, 10}, + {-20,-20,-20,-20,-20,-20,-20,-14, -6, 0, 0, 0, 0, 0, 2, 4, 10}, + {-20,-20,-20,-20,-20,-20,-20,-14, -8, -6, -6, -6, -6, -4, -4, -4, -2}}},*/ + {{{-10,-10,-10,-10,-10,-10, -8, 2, 2, 2, 4, 4, 5, 5, 5, 8, 10}, + {-20,-20,-20,-20,-20,-20,-20,-14, -6, 0, 0, 0, 0, 0, 2, 3, 6}, + {-20,-20,-20,-20,-20,-20,-20,-14, -8, -6, -6, -6, -6, -4, -4, -4, -2}}}, + /* 1 */ + /* {{{-10,-10,-10,-10,-10,-10, -8, -4, 0, 2, 4, 4, 5, 5, 5, 8, 10}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10, -4, -2, -2, -2, -2, 0, 2, 8}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10, -8, -8, -8, -8, -6, -6, -6, -4}}},*/ + {{{-10,-10,-10,-10,-10,-10, -8, -4, 0, 2, 4, 4, 5, 5, 5, 8, 10}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10, -4, -2, -2, -2, -2, 0, 1, 4}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10, -8, -8, -8, -8, -6, -6, -6, -4}}}, + /* 2 */ + /* {{{-10,-10,-10,-10,-10,-10,-10, -8, 0, 2, 2, 2, 4, 4, 5, 6, 10}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10, -4, -2, -2, -2, -2, 0, 2, 6}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10,-10,-10,-10,-10, -8, -8, -8, -4}}},*/ + {{{-10,-10,-10,-10,-10,-10,-10, -8, 0, 2, 2, 2, 4, 4, 5, 6, 10}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10, -4, -3, -3, -3, -2, -1, 0, 3}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10,-10,-10,-10,-10, -8, -8, -8, -4}}}, + /* 3 */ + /* {{{-10,-10,-10,-10,-10,-10,-10, -8, 0, 2, 2, 2, 4, 4, 4, 5, 8}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10, -4, -3, -3, -3, -3, -1, 1, 6}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10,-10,-10,-10,-10, -8, -8, -8, -4}}},*/ + {{{-10,-10,-10,-10,-10,-10,-10, -8, 0, 2, 2, 2, 4, 4, 4, 5, 8}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10, -4, -3, -3, -3, -3, -2, 0, 2}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10,-10,-10,-10,-10, -8, -8, -8, -5}}}, + /* 4 */ + /* {{{-15,-15,-15,-15,-15,-15,-15,-10, -4, 1, 1, 1, 2, 3, 3, 4, 7}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10, -4, -3, -3, -3, -3, -1, 1, 5}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10,-10,-10,-10,-10, -8, -8, -8, -4}}},*/ + {{{-15,-15,-15,-15,-15,-15,-15,-10, -4, 1, 1, 1, 2, 3, 3, 4, 7}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10, -4, -3, -3, -3, -3, -2, -1, 1}, + {-20,-20,-20,-20,-20,-20,-20,-14,-10,-10,-10,-10,-10, -8, -8, -8, -7}}}, + /* 5 */ + /* {{{-15,-15,-15,-15,-15,-15,-15,-10, -4, 1, 1, 1, 2, 3, 3, 4, 7}, + {-22,-22,-22,-22,-22,-22,-22,-16,-12, -6, -4, -4, -4, -4, -2, -1, 2}, + {-24,-24,-24,-24,-24,-24,-24,-18,-14,-12,-12,-12,-12,-10,-10, -9, -5}}},*/ + {{{-15,-15,-15,-15,-15,-15,-15,-10, -4, 1, 1, 1, 2, 3, 3, 4, 7}, + {-22,-22,-22,-22,-22,-22,-22,-16,-12, -6, -4, -4, -4, -4, -3, -1, 0}, + {-24,-24,-24,-24,-24,-24,-24,-18,-14,-12,-12,-12,-12,-10,-10, -9, -8}}}, + /* 6 */ + /* {{{-15,-15,-15,-15,-15,-15,-15,-10, -4, 1, 1, 1, 2, 3, 3, 4, 7}, + {-24,-24,-24,-24,-24,-24,-24,-18,-14, -8, -6, -6, -6, -6, -4, -2, 1}, + {-26,-26,-26,-26,-26,-26,-26,-18,-16,-15,-15,-15,-15,-13,-13,-12, -8}}},*/ + {{{-15,-15,-15,-15,-15,-15,-15,-10, -4, 1, 1, 1, 2, 3, 3, 4, 7}, + {-24,-24,-24,-24,-24,-24,-24,-18,-14, -8, -6, -6, -6, -6, -5, -2, 0}, + {-26,-26,-26,-26,-26,-26,-26,-18,-16,-15,-15,-15,-15,-13,-13,-12,-10}}}, + /* 7 */ + {{{-15,-15,-15,-15,-15,-15,-15,-10, -4, 1, 1, 1, 2, 3, 3, 4, 7}, + {-24,-24,-24,-24,-24,-24,-24,-18,-14,-10, -8, -8, -8, -8, -6, -4, 0}, + {-26,-26,-26,-26,-26,-26,-26,-22,-20,-19,-19,-19,-19,-18,-17,-16,-12}}}, + /* 8 */ + {{{-15,-15,-15,-15,-15,-15,-15,-10, -4, 0, 0, 0, 0, 1, 2, 3, 7}, + {-26,-26,-26,-26,-26,-26,-26,-20,-16,-12,-10,-10,-10,-10, -8, -6, -2}, + {-28,-28,-28,-28,-28,-28,-28,-26,-24,-24,-24,-24,-24,-24,-24,-20,-16}}}, + /* 9 */ + {{{-22,-22,-22,-22,-22,-22,-22,-18,-14, -8, -4, -4, -4, -4, -4, -2, 2}, + {-26,-26,-26,-26,-26,-26,-26,-22,-18,-16,-16,-16,-16,-14,-12,-10, -7}, + {-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-24,-20}}}, + /* 10 */ + {{{-24,-24,-24,-24,-24,-24,-24,-24,-24,-18,-14,-14,-14,-14,-14,-12,-10}, + {-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-30,-20}, + {-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40}}}, +}; + +/* noise bias (impulse block) */ +static noise3 _psy_noisebias_impulse[12]={ + /* 63 125 250 500 1k 2k 4k 8k 16k*/ + /* -1 */ + {{{-10,-10,-10,-10,-10, -4, 0, 0, 4, 8, 8, 8, 8, 10, 12, 14, 20}, + {-30,-30,-30,-30,-26,-20,-16, -8, -6, -6, -2, 2, 2, 3, 6, 6, 15}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10, -6, -8, -8, -6, -6, -6, -4, -2}}}, + + /* 0 */ + /* {{{-10,-10,-10,-10,-10, -4, 0, 0, 4, 4, 8, 8, 8, 10, 12, 14, 20}, + {-30,-30,-30,-30,-26,-22,-20,-14, -6, -2, 0, 0, 0, 0, 2, 4, 10}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10, -6, -8, -8, -6, -6, -6, -4, -2}}},*/ + {{{-10,-10,-10,-10,-10, -4, 0, 0, 4, 4, 8, 8, 8, 10, 12, 14, 20}, + {-30,-30,-30,-30,-26,-22,-20,-14, -6, -2, 0, 0, 0, 0, 2, 3, 6}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10, -6, -8, -8, -6, -6, -6, -4, -2}}}, + /* 1 */ + {{{-12,-12,-12,-12,-12, -8, -6, -4, 0, 4, 4, 4, 4, 10, 12, 14, 20}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -6, -4, -4, -2, -2, -2, -2, 2}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -8,-10,-10, -8, -8, -8, -6, -4}}}, + /* 2 */ + {{{-14,-14,-14,-14,-14,-10, -8, -6, -2, 2, 2, 2, 2, 8, 10, 10, 16}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -6, -6, -6, -4, -4, -4, -2, 0}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10,-10,-10,-10,-10,-10,-10, -8, -4}}}, + /* 3 */ + {{{-14,-14,-14,-14,-14,-10, -8, -6, -2, 2, 2, 2, 2, 6, 8, 8, 14}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -6, -6, -6, -4, -4, -4, -2, 0}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10,-10,-10,-10,-10,-10,-10, -8, -4}}}, + /* 4 */ + {{{-16,-16,-16,-16,-16,-12,-10, -6, -2, 0, 0, 0, 0, 4, 6, 6, 12}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -6, -6, -6, -4, -4, -4, -2, 0}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10,-10,-10,-10,-10,-10,-10, -8, -4}}}, + /* 5 */ + {{{-20,-20,-20,-20,-20,-18,-14,-10, -4, 0, 0, 0, 0, 4, 4, 6, 11}, + {-32,-32,-32,-32,-28,-24,-22,-16,-10, -6, -8, -8, -6, -6, -6, -4, -2}, + {-34,-34,-34,-34,-30,-26,-24,-18,-14,-12,-12,-12,-12,-12,-10, -9, -5}}}, + /* 6 + {{{-20,-20,-20,-20,-20,-18,-14,-10, -4, 0, 0, 0, 0, 4, 4, 6, 11}, + {-34,-34,-34,-34,-30,-30,-24,-20,-12,-12,-14,-14,-10, -9, -8, -6, -4}, + {-34,-34,-34,-34,-34,-30,-26,-20,-16,-15,-15,-15,-15,-15,-13,-12, -8}}},*/ + {{{-20,-20,-20,-20,-20,-18,-14,-10, -4, 0, 0, 0, 0, 4, 4, 6, 11}, + {-34,-34,-34,-34,-30,-30,-30,-24,-16,-16,-16,-16,-16,-16,-14,-14,-12}, + {-36,-36,-36,-36,-36,-34,-28,-24,-20,-20,-20,-20,-20,-20,-20,-18,-16}}}, + /* 7 */ + /* {{{-22,-22,-22,-22,-22,-20,-14,-10, -6, 0, 0, 0, 0, 4, 4, 6, 11}, + {-34,-34,-34,-34,-30,-30,-24,-20,-14,-14,-16,-16,-14,-12,-10,-10,-10}, + {-34,-34,-34,-34,-32,-32,-30,-24,-20,-19,-19,-19,-19,-19,-17,-16,-12}}},*/ + {{{-22,-22,-22,-22,-22,-20,-14,-10, -6, 0, 0, 0, 0, 4, 4, 6, 11}, + {-34,-34,-34,-34,-30,-30,-30,-30,-26,-26,-26,-26,-26,-26,-26,-24,-22}, + {-40,-40,-40,-40,-40,-40,-40,-32,-30,-30,-30,-30,-30,-30,-30,-30,-24}}}, + /* 8 */ + /* {{{-24,-24,-24,-24,-24,-22,-14,-10, -6, -1, -1, -1, -1, 3, 3, 5, 10}, + {-34,-34,-34,-34,-30,-30,-30,-24,-20,-20,-20,-20,-20,-18,-16,-16,-14}, + {-36,-36,-36,-36,-36,-34,-28,-24,-24,-24,-24,-24,-24,-24,-24,-20,-16}}},*/ + {{{-24,-24,-24,-24,-24,-22,-14,-10, -6, -1, -1, -1, -1, 3, 3, 5, 10}, + {-34,-34,-34,-34,-34,-32,-32,-30,-26,-26,-26,-26,-26,-26,-26,-26,-24}, + {-40,-40,-40,-40,-40,-40,-40,-32,-30,-30,-30,-30,-30,-30,-30,-30,-24}}}, + /* 9 */ + /* {{{-28,-28,-28,-28,-28,-28,-28,-20,-14, -8, -4, -4, -4, -4, -4, -2, 2}, + {-36,-36,-36,-36,-34,-32,-32,-30,-26,-26,-26,-26,-26,-22,-20,-20,-18}, + {-40,-40,-40,-40,-40,-40,-40,-32,-30,-30,-30,-30,-30,-30,-30,-24,-20}}},*/ + {{{-28,-28,-28,-28,-28,-28,-28,-20,-14, -8, -4, -4, -4, -4, -4, -2, 2}, + {-36,-36,-36,-36,-34,-32,-32,-30,-26,-26,-26,-26,-26,-26,-26,-26,-26}, + {-40,-40,-40,-40,-40,-40,-40,-32,-30,-30,-30,-30,-30,-30,-30,-24,-20}}}, + /* 10 */ + {{{-30,-30,-30,-30,-30,-26,-24,-24,-24,-20,-16,-16,-16,-16,-16,-14,-12}, + {-40,-40,-40,-40,-40,-40,-40,-40,-35,-30,-30,-30,-30,-30,-30,-30,-26}, + {-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40}}}, +}; + +/* noise bias (padding block) */ +static noise3 _psy_noisebias_padding[12]={ + /* 63 125 250 500 1k 2k 4k 8k 16k*/ + + /* -1 */ + {{{-10,-10,-10,-10,-10, -4, 0, 0, 4, 8, 8, 8, 8, 10, 12, 14, 20}, + {-30,-30,-30,-30,-26,-20,-16, -8, -6, -6, -2, 2, 2, 3, 6, 6, 15}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10, -6, -8, -8, -6, -6, -6, -4, -2}}}, + + /* 0 */ + {{{-10,-10,-10,-10,-10, -4, 0, 0, 4, 8, 8, 8, 8, 10, 12, 14, 20}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -4, -2, 2, 3, 6, 6, 8, 10}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -4, -4, -4, -4, -4, -2, 0, 2}}}, + /* 1 */ + {{{-12,-12,-12,-12,-12, -8, -6, -4, 0, 4, 4, 4, 4, 10, 12, 14, 20}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -4, 0, 0, 0, 2, 2, 4, 8}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -6, -6, -6, -6, -6, -4, -2, 0}}}, + /* 2 */ + /* {{{-14,-14,-14,-14,-14,-10, -8, -6, -2, 2, 2, 2, 2, 8, 10, 10, 16}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -4, 0, 0, 0, 2, 2, 4, 8}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -8, -8, -8, -8, -8, -6, -4, -2}}},*/ + {{{-14,-14,-14,-14,-14,-10, -8, -6, -2, 2, 2, 2, 2, 8, 10, 10, 16}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -6, -1, -1, -1, 0, 0, 2, 6}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -8, -8, -8, -8, -8, -6, -4, -2}}}, + /* 3 */ + {{{-14,-14,-14,-14,-14,-10, -8, -6, -2, 2, 2, 2, 2, 6, 8, 8, 14}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -6, -1, -1, -1, 0, 0, 2, 6}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -8, -8, -8, -8, -8, -6, -4, -2}}}, + /* 4 */ + {{{-16,-16,-16,-16,-16,-12,-10, -6, -2, 0, 0, 0, 0, 4, 6, 6, 12}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -6, -1, -1, -1, -1, 0, 2, 6}, + {-30,-30,-30,-30,-26,-22,-20,-14,-10, -8, -8, -8, -8, -8, -6, -4, -2}}}, + /* 5 */ + {{{-20,-20,-20,-20,-20,-18,-14,-10, -4, 0, 0, 0, 0, 4, 6, 6, 12}, + {-32,-32,-32,-32,-28,-24,-22,-16,-12, -6, -3, -3, -3, -3, -2, 0, 4}, + {-34,-34,-34,-34,-30,-26,-24,-18,-14,-10,-10,-10,-10,-10, -8, -5, -3}}}, + /* 6 */ + {{{-20,-20,-20,-20,-20,-18,-14,-10, -4, 0, 0, 0, 0, 4, 6, 6, 12}, + {-34,-34,-34,-34,-30,-30,-24,-20,-14, -8, -4, -4, -4, -4, -3, -1, 4}, + {-34,-34,-34,-34,-34,-30,-26,-20,-16,-13,-13,-13,-13,-13,-11, -8, -6}}}, + /* 7 */ + {{{-20,-20,-20,-20,-20,-18,-14,-10, -4, 0, 0, 0, 0, 4, 6, 6, 12}, + {-34,-34,-34,-34,-30,-30,-30,-24,-16,-10, -8, -6, -6, -6, -5, -3, 1}, + {-34,-34,-34,-34,-32,-32,-28,-22,-18,-16,-16,-16,-16,-16,-14,-12,-10}}}, + /* 8 */ + {{{-22,-22,-22,-22,-22,-20,-14,-10, -4, 0, 0, 0, 0, 3, 5, 5, 11}, + {-34,-34,-34,-34,-30,-30,-30,-24,-16,-12,-10, -8, -8, -8, -7, -5, -2}, + {-36,-36,-36,-36,-36,-34,-28,-22,-20,-20,-20,-20,-20,-20,-20,-16,-14}}}, + /* 9 */ + {{{-28,-28,-28,-28,-28,-28,-28,-20,-14, -8, -2, -2, -2, -2, 0, 2, 6}, + {-36,-36,-36,-36,-34,-32,-32,-24,-16,-12,-12,-12,-12,-12,-10, -8, -5}, + {-40,-40,-40,-40,-40,-40,-40,-32,-26,-24,-24,-24,-24,-24,-24,-20,-18}}}, + /* 10 */ + {{{-30,-30,-30,-30,-30,-26,-24,-24,-24,-20,-12,-12,-12,-12,-12,-10, -8}, + {-40,-40,-40,-40,-40,-40,-40,-40,-35,-30,-25,-25,-25,-25,-25,-25,-15}, + {-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40,-40}}}, +}; + + +static noiseguard _psy_noiseguards_44[4]={ + {3,3,15}, + {3,3,15}, + {10,10,100}, + {10,10,100}, +}; + +static int _psy_tone_suppress[12]={ + -20,-20,-20,-20,-20,-24,-30,-40,-40,-45,-45,-45, +}; +static int _psy_tone_0dB[12]={ + 90,90,95,95,95,95,105,105,105,105,105,105, +}; +static int _psy_noise_suppress[12]={ + -20,-20,-24,-24,-24,-24,-30,-40,-40,-45,-45,-45, +}; + +static vorbis_info_psy _psy_info_template={ + /* blockflag */ + -1, + /* ath_adjatt, ath_maxatt */ + -140.,-140., + /* tonemask att boost/decay,suppr,curves */ + {0.f,0.f,0.f}, 0.,0., -40.f, {0.}, + + /*noisemaskp,supp, low/high window, low/hi guard, minimum */ + 1, -0.f, .5f, .5f, 0,0,0, + /* noiseoffset*3, noisecompand, max_curve_dB */ + {{-1},{-1},{-1}},{-1},105.f, + /* noise normalization - channel_p, point_p, start, partition, thresh. */ + 0,0,-1,-1,0., +}; + +/* ath ****************/ + +static int _psy_ath_floater[12]={ + -100,-100,-100,-100,-100,-100,-105,-105,-105,-105,-110,-120, +}; +static int _psy_ath_abs[12]={ + -130,-130,-130,-130,-140,-140,-140,-140,-140,-140,-140,-150, +}; + +/* stereo setup. These don't map directly to quality level, there's + an additional indirection as several of the below may be used in a + single bitmanaged stream + +****************/ + +/* various stereo possibilities */ + +/* stereo mode by base quality level */ +static adj_stereo _psy_stereo_modes_44[12]={ + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 -1 */ + {{ 4, 4, 4, 4, 4, 4, 4, 3, 2, 2, 1, 0, 0, 0, 0}, + { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 5, 4, 3}, + { 1, 2, 3, 4, 4, 4, 4, 4, 4, 5, 6, 7, 8, 8, 8}, + { 12,12.5, 13,13.5, 14,14.5, 15, 99, 99, 99, 99, 99, 99, 99, 99}}, + +/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 0 */ +/*{{ 4, 4, 4, 4, 4, 4, 4, 3, 2, 2, 1, 0, 0, 0, 0}, + { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 5, 4, 3}, + { 1, 2, 3, 4, 5, 5, 6, 6, 6, 6, 6, 7, 8, 8, 8}, + { 12,12.5, 13,13.5, 14,14.5, 15, 99, 99, 99, 99, 99, 99, 99, 99}},*/ + {{ 4, 4, 4, 4, 4, 4, 4, 3, 2, 1, 0, 0, 0, 0, 0}, + { 8, 8, 8, 8, 6, 6, 5, 5, 5, 5, 5, 5, 5, 4, 3}, + { 1, 2, 3, 4, 4, 5, 6, 6, 6, 6, 6, 8, 8, 8, 8}, + { 12,12.5, 13,13.5, 14,14.5, 15, 99, 99, 99, 99, 99, 99, 99, 99}}, + + + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1 */ + {{ 3, 3, 3, 3, 3, 3, 3, 3, 2, 1, 0, 0, 0, 0, 0}, + { 8, 8, 8, 8, 6, 6, 5, 5, 5, 5, 5, 5, 5, 4, 3}, + { 1, 2, 3, 4, 4, 5, 6, 6, 6, 6, 6, 8, 8, 8, 8}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + + + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 2 */ + /* {{ 3, 3, 3, 3, 3, 3, 2, 2, 2, 1, 0, 0, 0, 0, 0}, + { 8, 8, 8, 6, 5, 5, 5, 5, 5, 5, 5, 4, 3, 2, 1}, + { 3, 4, 4, 4, 5, 6, 6, 6, 6, 6, 6, 8, 8, 8, 8}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, */ + {{ 3, 3, 3, 3, 3, 3, 3, 2, 1, 1, 0, 0, 0, 0, 0}, + { 8, 8, 6, 6, 5, 5, 4, 4, 4, 4, 4, 4, 3, 2, 1}, + { 3, 4, 4, 5, 5, 6, 6, 6, 6, 6, 6, 8, 8, 8, 8}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 3 */ + {{ 2, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, + { 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 2, 1}, + { 4, 4, 5, 6, 6, 6, 6, 6, 8, 8, 10, 10, 10, 10, 10}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 4 */ + {{ 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 2, 1, 0}, + { 6, 6, 6, 8, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 5 */ + /* {{ 2, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0}, + { 6, 6, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}},*/ + {{ 2, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0}, + { 6, 7, 8, 8, 8, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 6 */ + /* {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 3, 3, 3, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, */ + {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 3, 3, 3, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 8, 8, 8, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 7 */ + /* {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 3, 3, 3, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}},*/ + {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 3, 3, 3, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 8, 8, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 8 */ + /* {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}},*/ + {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 8, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 9 */ + {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 10 */ + {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, +}; + +/* tone master attenuation by base quality mode and bitrate tweak */ +static att3 _psy_tone_masteratt_44[12]={ + {{ 35, 21, 9}, 0, 0}, /* -1 */ + {{ 30, 20, 8}, -2, 1.25}, /* 0 */ + /* {{ 25, 14, 4}, 0, 0}, *//* 1 */ + {{ 25, 12, 2}, 0, 0}, /* 1 */ + /* {{ 20, 10, -2}, 0, 0}, *//* 2 */ + {{ 20, 9, -3}, 0, 0}, /* 2 */ + {{ 20, 9, -4}, 0, 0}, /* 3 */ + {{ 20, 9, -4}, 0, 0}, /* 4 */ + {{ 20, 6, -6}, 0, 0}, /* 5 */ + {{ 20, 3, -10}, 0, 0}, /* 6 */ + {{ 18, 1, -14}, 0, 0}, /* 7 */ + {{ 18, 0, -16}, 0, 0}, /* 8 */ + {{ 18, -2, -16}, 0, 0}, /* 9 */ + {{ 12, -2, -20}, 0, 0}, /* 10 */ +}; + +/* lowpass by mode **************/ +static double _psy_lowpass_44[12]={ + /* 15.1,15.8,16.5,17.9,20.5,48.,999.,999.,999.,999.,999. */ + 13.9,15.1,15.8,16.5,17.2,18.9,20.1,48.,999.,999.,999.,999. +}; + +/* noise normalization **********/ + +static int _noise_start_short_44[11]={ + /* 16,16,16,16,32,32,9999,9999,9999,9999 */ + 32,16,16,16,32,9999,9999,9999,9999,9999,9999 +}; +static int _noise_start_long_44[11]={ + /* 128,128,128,256,512,512,9999,9999,9999,9999 */ + 256,128,128,256,512,9999,9999,9999,9999,9999,9999 +}; + +static int _noise_part_short_44[11]={ + 8,8,8,8,8,8,8,8,8,8,8 +}; +static int _noise_part_long_44[11]={ + 32,32,32,32,32,32,32,32,32,32,32 +}; + +static double _noise_thresh_44[11]={ + /* .2,.2,.3,.4,.5,.5,9999.,9999.,9999.,9999., */ + .2,.2,.2,.4,.6,9999.,9999.,9999.,9999.,9999.,9999., +}; + +static double _noise_thresh_5only[2]={ + .5,.5, +}; diff --git a/Engine/lib/libvorbis/lib/modes/psych_8.h b/Engine/lib/libvorbis/lib/modes/psych_8.h new file mode 100644 index 000000000..642fb3ebe --- /dev/null +++ b/Engine/lib/libvorbis/lib/modes/psych_8.h @@ -0,0 +1,102 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: 8kHz psychoacoustic settings + last mod: $Id: psych_8.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +static att3 _psy_tone_masteratt_8[3]={ + {{ 32, 25, 12}, 0, 0}, /* 0 */ + {{ 30, 25, 12}, 0, 0}, /* 0 */ + {{ 20, 0, -14}, 0, 0}, /* 0 */ +}; + +static vp_adjblock _vp_tonemask_adj_8[3]={ + /* adjust for mode zero */ + /* 63 125 250 500 1 2 4 8 16 */ + {{-15,-15,-15,-15,-10,-10, -6, 0, 0, 0, 0,10, 0, 0,99,99,99}}, /* 1 */ + {{-15,-15,-15,-15,-10,-10, -6, 0, 0, 0, 0,10, 0, 0,99,99,99}}, /* 1 */ + {{-15,-15,-15,-15,-10,-10, -6, 0, 0, 0, 0, 0, 0, 0,99,99,99}}, /* 1 */ +}; + + +static noise3 _psy_noisebias_8[3]={ + /* 63 125 250 500 1k 2k 4k 8k 16k*/ + {{{-10,-10,-10,-10, -5, -5, -5, 0, 4, 8, 8, 8, 10, 10, 99, 99, 99}, + {-10,-10,-10,-10, -5, -5, -5, 0, 0, 4, 4, 4, 4, 4, 99, 99, 99}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10, -6, -8, -8, -6, -6, 99, 99, 99}}}, + + {{{-10,-10,-10,-10, -5, -5, -5, 0, 4, 8, 8, 8, 10, 10, 99, 99, 99}, + {-10,-10,-10,-10,-10,-10, -5, -5, -5, 0, 0, 0, 0, 0, 99, 99, 99}, + {-30,-30,-30,-30,-30,-24,-20,-14,-10, -6, -8, -8, -6, -6, 99, 99, 99}}}, + + {{{-15,-15,-15,-15,-15,-12,-10, -8, 0, 2, 4, 4, 5, 5, 99, 99, 99}, + {-30,-30,-30,-30,-26,-22,-20,-14,-12,-12,-10,-10,-10,-10, 99, 99, 99}, + {-30,-30,-30,-30,-26,-26,-26,-26,-26,-26,-26,-26,-26,-24, 99, 99, 99}}}, +}; + +/* stereo mode by base quality level */ +static adj_stereo _psy_stereo_modes_8[3]={ + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 */ + {{ 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}, + { 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + {{ 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}, + { 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, + {{ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}, + { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}, + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + { 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99}}, +}; + +static noiseguard _psy_noiseguards_8[2]={ + {10,10,-1}, + {10,10,-1}, +}; + +static compandblock _psy_compand_8[2]={ + {{ + 0, 1, 2, 3, 4, 5, 6, 7, /* 7dB */ + 8, 8, 9, 9,10,10,11, 11, /* 15dB */ + 12,12,13,13,14,14,15, 15, /* 23dB */ + 16,16,17,17,17,18,18, 19, /* 31dB */ + 19,19,20,21,22,23,24, 25, /* 39dB */ + }}, + {{ + 0, 1, 2, 3, 4, 5, 6, 6, /* 7dB */ + 7, 7, 6, 6, 5, 5, 4, 4, /* 15dB */ + 3, 3, 3, 4, 5, 6, 7, 8, /* 23dB */ + 9,10,11,12,13,14,15, 16, /* 31dB */ + 17,18,19,20,21,22,23, 24, /* 39dB */ + }}, +}; + +static double _psy_lowpass_8[3]={3.,4.,4.}; +static int _noise_start_8[2]={ + 64,64, +}; +static int _noise_part_8[2]={ + 8,8, +}; + +static int _psy_ath_floater_8[3]={ + -100,-100,-105, +}; + +static int _psy_ath_abs_8[3]={ + -130,-130,-140, +}; + diff --git a/Engine/lib/libvorbis/lib/modes/residue_16.h b/Engine/lib/libvorbis/lib/modes/residue_16.h new file mode 100644 index 000000000..fd93887b5 --- /dev/null +++ b/Engine/lib/libvorbis/lib/modes/residue_16.h @@ -0,0 +1,163 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: toplevel residue templates 16/22kHz + last mod: $Id: residue_16.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +/***** residue backends *********************************************/ + +static static_bookblock _resbook_16s_0={ + { + {0}, + {0,0,&_16c0_s_p1_0}, + {0}, + {0,0,&_16c0_s_p3_0}, + {0,0,&_16c0_s_p4_0}, + {0,0,&_16c0_s_p5_0}, + {0,0,&_16c0_s_p6_0}, + {&_16c0_s_p7_0,&_16c0_s_p7_1}, + {&_16c0_s_p8_0,&_16c0_s_p8_1}, + {&_16c0_s_p9_0,&_16c0_s_p9_1,&_16c0_s_p9_2} + } +}; +static static_bookblock _resbook_16s_1={ + { + {0}, + {0,0,&_16c1_s_p1_0}, + {0}, + {0,0,&_16c1_s_p3_0}, + {0,0,&_16c1_s_p4_0}, + {0,0,&_16c1_s_p5_0}, + {0,0,&_16c1_s_p6_0}, + {&_16c1_s_p7_0,&_16c1_s_p7_1}, + {&_16c1_s_p8_0,&_16c1_s_p8_1}, + {&_16c1_s_p9_0,&_16c1_s_p9_1,&_16c1_s_p9_2} + } +}; +static static_bookblock _resbook_16s_2={ + { + {0}, + {0,0,&_16c2_s_p1_0}, + {0,0,&_16c2_s_p2_0}, + {0,0,&_16c2_s_p3_0}, + {0,0,&_16c2_s_p4_0}, + {&_16c2_s_p5_0,&_16c2_s_p5_1}, + {&_16c2_s_p6_0,&_16c2_s_p6_1}, + {&_16c2_s_p7_0,&_16c2_s_p7_1}, + {&_16c2_s_p8_0,&_16c2_s_p8_1}, + {&_16c2_s_p9_0,&_16c2_s_p9_1,&_16c2_s_p9_2} + } +}; + +static vorbis_residue_template _res_16s_0[]={ + {2,0, &_residue_44_mid, + &_huff_book__16c0_s_single,&_huff_book__16c0_s_single, + &_resbook_16s_0,&_resbook_16s_0}, +}; +static vorbis_residue_template _res_16s_1[]={ + {2,0, &_residue_44_mid, + &_huff_book__16c1_s_short,&_huff_book__16c1_s_short, + &_resbook_16s_1,&_resbook_16s_1}, + + {2,0, &_residue_44_mid, + &_huff_book__16c1_s_long,&_huff_book__16c1_s_long, + &_resbook_16s_1,&_resbook_16s_1} +}; +static vorbis_residue_template _res_16s_2[]={ + {2,0, &_residue_44_high, + &_huff_book__16c2_s_short,&_huff_book__16c2_s_short, + &_resbook_16s_2,&_resbook_16s_2}, + + {2,0, &_residue_44_high, + &_huff_book__16c2_s_long,&_huff_book__16c2_s_long, + &_resbook_16s_2,&_resbook_16s_2} +}; + +static vorbis_mapping_template _mapres_template_16_stereo[3]={ + { _map_nominal, _res_16s_0 }, /* 0 */ + { _map_nominal, _res_16s_1 }, /* 1 */ + { _map_nominal, _res_16s_2 }, /* 2 */ +}; + +static static_bookblock _resbook_16u_0={ + { + {0}, + {0,0,&_16u0__p1_0}, + {0,0,&_16u0__p2_0}, + {0,0,&_16u0__p3_0}, + {0,0,&_16u0__p4_0}, + {0,0,&_16u0__p5_0}, + {&_16u0__p6_0,&_16u0__p6_1}, + {&_16u0__p7_0,&_16u0__p7_1,&_16u0__p7_2} + } +}; +static static_bookblock _resbook_16u_1={ + { + {0}, + {0,0,&_16u1__p1_0}, + {0,0,&_16u1__p2_0}, + {0,0,&_16u1__p3_0}, + {0,0,&_16u1__p4_0}, + {0,0,&_16u1__p5_0}, + {0,0,&_16u1__p6_0}, + {&_16u1__p7_0,&_16u1__p7_1}, + {&_16u1__p8_0,&_16u1__p8_1}, + {&_16u1__p9_0,&_16u1__p9_1,&_16u1__p9_2} + } +}; +static static_bookblock _resbook_16u_2={ + { + {0}, + {0,0,&_16u2_p1_0}, + {0,0,&_16u2_p2_0}, + {0,0,&_16u2_p3_0}, + {0,0,&_16u2_p4_0}, + {&_16u2_p5_0,&_16u2_p5_1}, + {&_16u2_p6_0,&_16u2_p6_1}, + {&_16u2_p7_0,&_16u2_p7_1}, + {&_16u2_p8_0,&_16u2_p8_1}, + {&_16u2_p9_0,&_16u2_p9_1,&_16u2_p9_2} + } +}; + +static vorbis_residue_template _res_16u_0[]={ + {1,0, &_residue_44_low_un, + &_huff_book__16u0__single,&_huff_book__16u0__single, + &_resbook_16u_0,&_resbook_16u_0}, +}; +static vorbis_residue_template _res_16u_1[]={ + {1,0, &_residue_44_mid_un, + &_huff_book__16u1__short,&_huff_book__16u1__short, + &_resbook_16u_1,&_resbook_16u_1}, + + {1,0, &_residue_44_mid_un, + &_huff_book__16u1__long,&_huff_book__16u1__long, + &_resbook_16u_1,&_resbook_16u_1} +}; +static vorbis_residue_template _res_16u_2[]={ + {1,0, &_residue_44_hi_un, + &_huff_book__16u2__short,&_huff_book__16u2__short, + &_resbook_16u_2,&_resbook_16u_2}, + + {1,0, &_residue_44_hi_un, + &_huff_book__16u2__long,&_huff_book__16u2__long, + &_resbook_16u_2,&_resbook_16u_2} +}; + + +static vorbis_mapping_template _mapres_template_16_uncoupled[3]={ + { _map_nominal_u, _res_16u_0 }, /* 0 */ + { _map_nominal_u, _res_16u_1 }, /* 1 */ + { _map_nominal_u, _res_16u_2 }, /* 2 */ +}; diff --git a/Engine/lib/libvorbis/lib/modes/residue_44.h b/Engine/lib/libvorbis/lib/modes/residue_44.h new file mode 100644 index 000000000..45017b4c4 --- /dev/null +++ b/Engine/lib/libvorbis/lib/modes/residue_44.h @@ -0,0 +1,294 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: toplevel residue templates for 32/44.1/48kHz + last mod: $Id: residue_44.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#include "vorbis/codec.h" +#include "backends.h" +#include "books/coupled/res_books_stereo.h" + +/***** residue backends *********************************************/ + +static vorbis_info_residue0 _residue_44_low={ + 0,-1, -1, 9,-1, + /* 0 1 2 3 4 5 6 7 */ + {0}, + {-1}, + { .5, 1.5, 2.5, 2.5, 4.5, 8.5, 16.5, 32.5}, + { .5, .5, .5, 999., 4.5, 8.5, 16.5, 32.5}, +}; + +static vorbis_info_residue0 _residue_44_mid={ + 0,-1, -1, 10,-1, + /* 0 1 2 3 4 5 6 7 8 */ + {0}, + {-1}, + { .5, 1.5, 1.5, 2.5, 2.5, 4.5, 8.5, 16.5, 32.5}, + { .5, .5, 999., .5, 999., 4.5, 8.5, 16.5, 32.5}, +}; + +static vorbis_info_residue0 _residue_44_high={ + 0,-1, -1, 10,-1, + /* 0 1 2 3 4 5 6 7 8 */ + {0}, + {-1}, + { .5, 1.5, 2.5, 4.5, 8.5, 16.5, 32.5, 71.5,157.5}, + { .5, 1.5, 2.5, 3.5, 4.5, 8.5, 16.5, 71.5,157.5}, +}; + +static static_bookblock _resbook_44s_n1={ + { + {0},{0,0,&_44cn1_s_p1_0},{0,0,&_44cn1_s_p2_0}, + {0,0,&_44cn1_s_p3_0},{0,0,&_44cn1_s_p4_0},{0,0,&_44cn1_s_p5_0}, + {&_44cn1_s_p6_0,&_44cn1_s_p6_1},{&_44cn1_s_p7_0,&_44cn1_s_p7_1}, + {&_44cn1_s_p8_0,&_44cn1_s_p8_1,&_44cn1_s_p8_2} + } +}; +static static_bookblock _resbook_44sm_n1={ + { + {0},{0,0,&_44cn1_sm_p1_0},{0,0,&_44cn1_sm_p2_0}, + {0,0,&_44cn1_sm_p3_0},{0,0,&_44cn1_sm_p4_0},{0,0,&_44cn1_sm_p5_0}, + {&_44cn1_sm_p6_0,&_44cn1_sm_p6_1},{&_44cn1_sm_p7_0,&_44cn1_sm_p7_1}, + {&_44cn1_sm_p8_0,&_44cn1_sm_p8_1,&_44cn1_sm_p8_2} + } +}; + +static static_bookblock _resbook_44s_0={ + { + {0},{0,0,&_44c0_s_p1_0},{0,0,&_44c0_s_p2_0}, + {0,0,&_44c0_s_p3_0},{0,0,&_44c0_s_p4_0},{0,0,&_44c0_s_p5_0}, + {&_44c0_s_p6_0,&_44c0_s_p6_1},{&_44c0_s_p7_0,&_44c0_s_p7_1}, + {&_44c0_s_p8_0,&_44c0_s_p8_1,&_44c0_s_p8_2} + } +}; +static static_bookblock _resbook_44sm_0={ + { + {0},{0,0,&_44c0_sm_p1_0},{0,0,&_44c0_sm_p2_0}, + {0,0,&_44c0_sm_p3_0},{0,0,&_44c0_sm_p4_0},{0,0,&_44c0_sm_p5_0}, + {&_44c0_sm_p6_0,&_44c0_sm_p6_1},{&_44c0_sm_p7_0,&_44c0_sm_p7_1}, + {&_44c0_sm_p8_0,&_44c0_sm_p8_1,&_44c0_sm_p8_2} + } +}; + +static static_bookblock _resbook_44s_1={ + { + {0},{0,0,&_44c1_s_p1_0},{0,0,&_44c1_s_p2_0}, + {0,0,&_44c1_s_p3_0},{0,0,&_44c1_s_p4_0},{0,0,&_44c1_s_p5_0}, + {&_44c1_s_p6_0,&_44c1_s_p6_1},{&_44c1_s_p7_0,&_44c1_s_p7_1}, + {&_44c1_s_p8_0,&_44c1_s_p8_1,&_44c1_s_p8_2} + } +}; +static static_bookblock _resbook_44sm_1={ + { + {0},{0,0,&_44c1_sm_p1_0},{0,0,&_44c1_sm_p2_0}, + {0,0,&_44c1_sm_p3_0},{0,0,&_44c1_sm_p4_0},{0,0,&_44c1_sm_p5_0}, + {&_44c1_sm_p6_0,&_44c1_sm_p6_1},{&_44c1_sm_p7_0,&_44c1_sm_p7_1}, + {&_44c1_sm_p8_0,&_44c1_sm_p8_1,&_44c1_sm_p8_2} + } +}; + +static static_bookblock _resbook_44s_2={ + { + {0},{0,0,&_44c2_s_p1_0},{0,0,&_44c2_s_p2_0},{0,0,&_44c2_s_p3_0}, + {0,0,&_44c2_s_p4_0},{0,0,&_44c2_s_p5_0},{0,0,&_44c2_s_p6_0}, + {&_44c2_s_p7_0,&_44c2_s_p7_1},{&_44c2_s_p8_0,&_44c2_s_p8_1}, + {&_44c2_s_p9_0,&_44c2_s_p9_1,&_44c2_s_p9_2} + } +}; +static static_bookblock _resbook_44s_3={ + { + {0},{0,0,&_44c3_s_p1_0},{0,0,&_44c3_s_p2_0},{0,0,&_44c3_s_p3_0}, + {0,0,&_44c3_s_p4_0},{0,0,&_44c3_s_p5_0},{0,0,&_44c3_s_p6_0}, + {&_44c3_s_p7_0,&_44c3_s_p7_1},{&_44c3_s_p8_0,&_44c3_s_p8_1}, + {&_44c3_s_p9_0,&_44c3_s_p9_1,&_44c3_s_p9_2} + } +}; +static static_bookblock _resbook_44s_4={ + { + {0},{0,0,&_44c4_s_p1_0},{0,0,&_44c4_s_p2_0},{0,0,&_44c4_s_p3_0}, + {0,0,&_44c4_s_p4_0},{0,0,&_44c4_s_p5_0},{0,0,&_44c4_s_p6_0}, + {&_44c4_s_p7_0,&_44c4_s_p7_1},{&_44c4_s_p8_0,&_44c4_s_p8_1}, + {&_44c4_s_p9_0,&_44c4_s_p9_1,&_44c4_s_p9_2} + } +}; +static static_bookblock _resbook_44s_5={ + { + {0},{0,0,&_44c5_s_p1_0},{0,0,&_44c5_s_p2_0},{0,0,&_44c5_s_p3_0}, + {0,0,&_44c5_s_p4_0},{0,0,&_44c5_s_p5_0},{0,0,&_44c5_s_p6_0}, + {&_44c5_s_p7_0,&_44c5_s_p7_1},{&_44c5_s_p8_0,&_44c5_s_p8_1}, + {&_44c5_s_p9_0,&_44c5_s_p9_1,&_44c5_s_p9_2} + } +}; +static static_bookblock _resbook_44s_6={ + { + {0},{0,0,&_44c6_s_p1_0},{0,0,&_44c6_s_p2_0},{0,0,&_44c6_s_p3_0}, + {0,0,&_44c6_s_p4_0}, + {&_44c6_s_p5_0,&_44c6_s_p5_1}, + {&_44c6_s_p6_0,&_44c6_s_p6_1}, + {&_44c6_s_p7_0,&_44c6_s_p7_1}, + {&_44c6_s_p8_0,&_44c6_s_p8_1}, + {&_44c6_s_p9_0,&_44c6_s_p9_1,&_44c6_s_p9_2} + } +}; +static static_bookblock _resbook_44s_7={ + { + {0},{0,0,&_44c7_s_p1_0},{0,0,&_44c7_s_p2_0},{0,0,&_44c7_s_p3_0}, + {0,0,&_44c7_s_p4_0}, + {&_44c7_s_p5_0,&_44c7_s_p5_1}, + {&_44c7_s_p6_0,&_44c7_s_p6_1}, + {&_44c7_s_p7_0,&_44c7_s_p7_1}, + {&_44c7_s_p8_0,&_44c7_s_p8_1}, + {&_44c7_s_p9_0,&_44c7_s_p9_1,&_44c7_s_p9_2} + } +}; +static static_bookblock _resbook_44s_8={ + { + {0},{0,0,&_44c8_s_p1_0},{0,0,&_44c8_s_p2_0},{0,0,&_44c8_s_p3_0}, + {0,0,&_44c8_s_p4_0}, + {&_44c8_s_p5_0,&_44c8_s_p5_1}, + {&_44c8_s_p6_0,&_44c8_s_p6_1}, + {&_44c8_s_p7_0,&_44c8_s_p7_1}, + {&_44c8_s_p8_0,&_44c8_s_p8_1}, + {&_44c8_s_p9_0,&_44c8_s_p9_1,&_44c8_s_p9_2} + } +}; +static static_bookblock _resbook_44s_9={ + { + {0},{0,0,&_44c9_s_p1_0},{0,0,&_44c9_s_p2_0},{0,0,&_44c9_s_p3_0}, + {0,0,&_44c9_s_p4_0}, + {&_44c9_s_p5_0,&_44c9_s_p5_1}, + {&_44c9_s_p6_0,&_44c9_s_p6_1}, + {&_44c9_s_p7_0,&_44c9_s_p7_1}, + {&_44c9_s_p8_0,&_44c9_s_p8_1}, + {&_44c9_s_p9_0,&_44c9_s_p9_1,&_44c9_s_p9_2} + } +}; + +static vorbis_residue_template _res_44s_n1[]={ + {2,0, &_residue_44_low, + &_huff_book__44cn1_s_short,&_huff_book__44cn1_sm_short, + &_resbook_44s_n1,&_resbook_44sm_n1}, + + {2,0, &_residue_44_low, + &_huff_book__44cn1_s_long,&_huff_book__44cn1_sm_long, + &_resbook_44s_n1,&_resbook_44sm_n1} +}; +static vorbis_residue_template _res_44s_0[]={ + {2,0, &_residue_44_low, + &_huff_book__44c0_s_short,&_huff_book__44c0_sm_short, + &_resbook_44s_0,&_resbook_44sm_0}, + + {2,0, &_residue_44_low, + &_huff_book__44c0_s_long,&_huff_book__44c0_sm_long, + &_resbook_44s_0,&_resbook_44sm_0} +}; +static vorbis_residue_template _res_44s_1[]={ + {2,0, &_residue_44_low, + &_huff_book__44c1_s_short,&_huff_book__44c1_sm_short, + &_resbook_44s_1,&_resbook_44sm_1}, + + {2,0, &_residue_44_low, + &_huff_book__44c1_s_long,&_huff_book__44c1_sm_long, + &_resbook_44s_1,&_resbook_44sm_1} +}; + +static vorbis_residue_template _res_44s_2[]={ + {2,0, &_residue_44_mid, + &_huff_book__44c2_s_short,&_huff_book__44c2_s_short, + &_resbook_44s_2,&_resbook_44s_2}, + + {2,0, &_residue_44_mid, + &_huff_book__44c2_s_long,&_huff_book__44c2_s_long, + &_resbook_44s_2,&_resbook_44s_2} +}; +static vorbis_residue_template _res_44s_3[]={ + {2,0, &_residue_44_mid, + &_huff_book__44c3_s_short,&_huff_book__44c3_s_short, + &_resbook_44s_3,&_resbook_44s_3}, + + {2,0, &_residue_44_mid, + &_huff_book__44c3_s_long,&_huff_book__44c3_s_long, + &_resbook_44s_3,&_resbook_44s_3} +}; +static vorbis_residue_template _res_44s_4[]={ + {2,0, &_residue_44_mid, + &_huff_book__44c4_s_short,&_huff_book__44c4_s_short, + &_resbook_44s_4,&_resbook_44s_4}, + + {2,0, &_residue_44_mid, + &_huff_book__44c4_s_long,&_huff_book__44c4_s_long, + &_resbook_44s_4,&_resbook_44s_4} +}; +static vorbis_residue_template _res_44s_5[]={ + {2,0, &_residue_44_mid, + &_huff_book__44c5_s_short,&_huff_book__44c5_s_short, + &_resbook_44s_5,&_resbook_44s_5}, + + {2,0, &_residue_44_mid, + &_huff_book__44c5_s_long,&_huff_book__44c5_s_long, + &_resbook_44s_5,&_resbook_44s_5} +}; +static vorbis_residue_template _res_44s_6[]={ + {2,0, &_residue_44_high, + &_huff_book__44c6_s_short,&_huff_book__44c6_s_short, + &_resbook_44s_6,&_resbook_44s_6}, + + {2,0, &_residue_44_high, + &_huff_book__44c6_s_long,&_huff_book__44c6_s_long, + &_resbook_44s_6,&_resbook_44s_6} +}; +static vorbis_residue_template _res_44s_7[]={ + {2,0, &_residue_44_high, + &_huff_book__44c7_s_short,&_huff_book__44c7_s_short, + &_resbook_44s_7,&_resbook_44s_7}, + + {2,0, &_residue_44_high, + &_huff_book__44c7_s_long,&_huff_book__44c7_s_long, + &_resbook_44s_7,&_resbook_44s_7} +}; +static vorbis_residue_template _res_44s_8[]={ + {2,0, &_residue_44_high, + &_huff_book__44c8_s_short,&_huff_book__44c8_s_short, + &_resbook_44s_8,&_resbook_44s_8}, + + {2,0, &_residue_44_high, + &_huff_book__44c8_s_long,&_huff_book__44c8_s_long, + &_resbook_44s_8,&_resbook_44s_8} +}; +static vorbis_residue_template _res_44s_9[]={ + {2,0, &_residue_44_high, + &_huff_book__44c9_s_short,&_huff_book__44c9_s_short, + &_resbook_44s_9,&_resbook_44s_9}, + + {2,0, &_residue_44_high, + &_huff_book__44c9_s_long,&_huff_book__44c9_s_long, + &_resbook_44s_9,&_resbook_44s_9} +}; + +static vorbis_mapping_template _mapres_template_44_stereo[]={ + { _map_nominal, _res_44s_n1 }, /* -1 */ + { _map_nominal, _res_44s_0 }, /* 0 */ + { _map_nominal, _res_44s_1 }, /* 1 */ + { _map_nominal, _res_44s_2 }, /* 2 */ + { _map_nominal, _res_44s_3 }, /* 3 */ + { _map_nominal, _res_44s_4 }, /* 4 */ + { _map_nominal, _res_44s_5 }, /* 5 */ + { _map_nominal, _res_44s_6 }, /* 6 */ + { _map_nominal, _res_44s_7 }, /* 7 */ + { _map_nominal, _res_44s_8 }, /* 8 */ + { _map_nominal, _res_44s_9 }, /* 9 */ +}; + + diff --git a/Engine/lib/libvorbis/lib/modes/residue_44u.h b/Engine/lib/libvorbis/lib/modes/residue_44u.h new file mode 100644 index 000000000..d90f0d372 --- /dev/null +++ b/Engine/lib/libvorbis/lib/modes/residue_44u.h @@ -0,0 +1,318 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: toplevel residue templates for 32/44.1/48kHz uncoupled + last mod: $Id: residue_44u.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#include "vorbis/codec.h" +#include "backends.h" +#include "books/uncoupled/res_books_uncoupled.h" + +/***** residue backends *********************************************/ + + +static vorbis_info_residue0 _residue_44_low_un={ + 0,-1, -1, 8,-1, + {0}, + {-1}, + { .5, 1.5, 1.5, 2.5, 2.5, 4.5, 28.5}, + { -1, 25, -1, 45, -1, -1, -1} +}; + +static vorbis_info_residue0 _residue_44_mid_un={ + 0,-1, -1, 10,-1, + /* 0 1 2 3 4 5 6 7 8 9 */ + {0}, + {-1}, + { .5, 1.5, 1.5, 2.5, 2.5, 4.5, 4.5, 16.5, 60.5}, + { -1, 30, -1, 50, -1, 80, -1, -1, -1} +}; + +static vorbis_info_residue0 _residue_44_hi_un={ + 0,-1, -1, 10,-1, + /* 0 1 2 3 4 5 6 7 8 9 */ + {0}, + {-1}, + { .5, 1.5, 2.5, 4.5, 8.5, 16.5, 32.5, 71.5,157.5}, + { -1, -1, -1, -1, -1, -1, -1, -1, -1} +}; + +/* mapping conventions: + only one submap (this would change for efficient 5.1 support for example)*/ +/* Four psychoacoustic profiles are used, one for each blocktype */ +static vorbis_info_mapping0 _map_nominal_u[2]={ + {1, {0,0}, {0}, {0}, 0,{0},{0}}, + {1, {0,0}, {1}, {1}, 0,{0},{0}} +}; + +static static_bookblock _resbook_44u_n1={ + { + {0}, + {0,0,&_44un1__p1_0}, + {0,0,&_44un1__p2_0}, + {0,0,&_44un1__p3_0}, + {0,0,&_44un1__p4_0}, + {0,0,&_44un1__p5_0}, + {&_44un1__p6_0,&_44un1__p6_1}, + {&_44un1__p7_0,&_44un1__p7_1,&_44un1__p7_2} + } +}; +static static_bookblock _resbook_44u_0={ + { + {0}, + {0,0,&_44u0__p1_0}, + {0,0,&_44u0__p2_0}, + {0,0,&_44u0__p3_0}, + {0,0,&_44u0__p4_0}, + {0,0,&_44u0__p5_0}, + {&_44u0__p6_0,&_44u0__p6_1}, + {&_44u0__p7_0,&_44u0__p7_1,&_44u0__p7_2} + } +}; +static static_bookblock _resbook_44u_1={ + { + {0}, + {0,0,&_44u1__p1_0}, + {0,0,&_44u1__p2_0}, + {0,0,&_44u1__p3_0}, + {0,0,&_44u1__p4_0}, + {0,0,&_44u1__p5_0}, + {&_44u1__p6_0,&_44u1__p6_1}, + {&_44u1__p7_0,&_44u1__p7_1,&_44u1__p7_2} + } +}; +static static_bookblock _resbook_44u_2={ + { + {0}, + {0,0,&_44u2__p1_0}, + {0,0,&_44u2__p2_0}, + {0,0,&_44u2__p3_0}, + {0,0,&_44u2__p4_0}, + {0,0,&_44u2__p5_0}, + {&_44u2__p6_0,&_44u2__p6_1}, + {&_44u2__p7_0,&_44u2__p7_1,&_44u2__p7_2} + } +}; +static static_bookblock _resbook_44u_3={ + { + {0}, + {0,0,&_44u3__p1_0}, + {0,0,&_44u3__p2_0}, + {0,0,&_44u3__p3_0}, + {0,0,&_44u3__p4_0}, + {0,0,&_44u3__p5_0}, + {&_44u3__p6_0,&_44u3__p6_1}, + {&_44u3__p7_0,&_44u3__p7_1,&_44u3__p7_2} + } +}; +static static_bookblock _resbook_44u_4={ + { + {0}, + {0,0,&_44u4__p1_0}, + {0,0,&_44u4__p2_0}, + {0,0,&_44u4__p3_0}, + {0,0,&_44u4__p4_0}, + {0,0,&_44u4__p5_0}, + {&_44u4__p6_0,&_44u4__p6_1}, + {&_44u4__p7_0,&_44u4__p7_1,&_44u4__p7_2} + } +}; +static static_bookblock _resbook_44u_5={ + { + {0}, + {0,0,&_44u5__p1_0}, + {0,0,&_44u5__p2_0}, + {0,0,&_44u5__p3_0}, + {0,0,&_44u5__p4_0}, + {0,0,&_44u5__p5_0}, + {0,0,&_44u5__p6_0}, + {&_44u5__p7_0,&_44u5__p7_1}, + {&_44u5__p8_0,&_44u5__p8_1}, + {&_44u5__p9_0,&_44u5__p9_1,&_44u5__p9_2} + } +}; +static static_bookblock _resbook_44u_6={ + { + {0}, + {0,0,&_44u6__p1_0}, + {0,0,&_44u6__p2_0}, + {0,0,&_44u6__p3_0}, + {0,0,&_44u6__p4_0}, + {0,0,&_44u6__p5_0}, + {0,0,&_44u6__p6_0}, + {&_44u6__p7_0,&_44u6__p7_1}, + {&_44u6__p8_0,&_44u6__p8_1}, + {&_44u6__p9_0,&_44u6__p9_1,&_44u6__p9_2} + } +}; +static static_bookblock _resbook_44u_7={ + { + {0}, + {0,0,&_44u7__p1_0}, + {0,0,&_44u7__p2_0}, + {0,0,&_44u7__p3_0}, + {0,0,&_44u7__p4_0}, + {0,0,&_44u7__p5_0}, + {0,0,&_44u7__p6_0}, + {&_44u7__p7_0,&_44u7__p7_1}, + {&_44u7__p8_0,&_44u7__p8_1}, + {&_44u7__p9_0,&_44u7__p9_1,&_44u7__p9_2} + } +}; +static static_bookblock _resbook_44u_8={ + { + {0}, + {0,0,&_44u8_p1_0}, + {0,0,&_44u8_p2_0}, + {0,0,&_44u8_p3_0}, + {0,0,&_44u8_p4_0}, + {&_44u8_p5_0,&_44u8_p5_1}, + {&_44u8_p6_0,&_44u8_p6_1}, + {&_44u8_p7_0,&_44u8_p7_1}, + {&_44u8_p8_0,&_44u8_p8_1}, + {&_44u8_p9_0,&_44u8_p9_1,&_44u8_p9_2} + } +}; +static static_bookblock _resbook_44u_9={ + { + {0}, + {0,0,&_44u9_p1_0}, + {0,0,&_44u9_p2_0}, + {0,0,&_44u9_p3_0}, + {0,0,&_44u9_p4_0}, + {&_44u9_p5_0,&_44u9_p5_1}, + {&_44u9_p6_0,&_44u9_p6_1}, + {&_44u9_p7_0,&_44u9_p7_1}, + {&_44u9_p8_0,&_44u9_p8_1}, + {&_44u9_p9_0,&_44u9_p9_1,&_44u9_p9_2} + } +}; + +static vorbis_residue_template _res_44u_n1[]={ + {1,0, &_residue_44_low_un, + &_huff_book__44un1__short,&_huff_book__44un1__short, + &_resbook_44u_n1,&_resbook_44u_n1}, + + {1,0, &_residue_44_low_un, + &_huff_book__44un1__long,&_huff_book__44un1__long, + &_resbook_44u_n1,&_resbook_44u_n1} +}; +static vorbis_residue_template _res_44u_0[]={ + {1,0, &_residue_44_low_un, + &_huff_book__44u0__short,&_huff_book__44u0__short, + &_resbook_44u_0,&_resbook_44u_0}, + + {1,0, &_residue_44_low_un, + &_huff_book__44u0__long,&_huff_book__44u0__long, + &_resbook_44u_0,&_resbook_44u_0} +}; +static vorbis_residue_template _res_44u_1[]={ + {1,0, &_residue_44_low_un, + &_huff_book__44u1__short,&_huff_book__44u1__short, + &_resbook_44u_1,&_resbook_44u_1}, + + {1,0, &_residue_44_low_un, + &_huff_book__44u1__long,&_huff_book__44u1__long, + &_resbook_44u_1,&_resbook_44u_1} +}; +static vorbis_residue_template _res_44u_2[]={ + {1,0, &_residue_44_low_un, + &_huff_book__44u2__short,&_huff_book__44u2__short, + &_resbook_44u_2,&_resbook_44u_2}, + + {1,0, &_residue_44_low_un, + &_huff_book__44u2__long,&_huff_book__44u2__long, + &_resbook_44u_2,&_resbook_44u_2} +}; +static vorbis_residue_template _res_44u_3[]={ + {1,0, &_residue_44_low_un, + &_huff_book__44u3__short,&_huff_book__44u3__short, + &_resbook_44u_3,&_resbook_44u_3}, + + {1,0, &_residue_44_low_un, + &_huff_book__44u3__long,&_huff_book__44u3__long, + &_resbook_44u_3,&_resbook_44u_3} +}; +static vorbis_residue_template _res_44u_4[]={ + {1,0, &_residue_44_low_un, + &_huff_book__44u4__short,&_huff_book__44u4__short, + &_resbook_44u_4,&_resbook_44u_4}, + + {1,0, &_residue_44_low_un, + &_huff_book__44u4__long,&_huff_book__44u4__long, + &_resbook_44u_4,&_resbook_44u_4} +}; + +static vorbis_residue_template _res_44u_5[]={ + {1,0, &_residue_44_mid_un, + &_huff_book__44u5__short,&_huff_book__44u5__short, + &_resbook_44u_5,&_resbook_44u_5}, + + {1,0, &_residue_44_mid_un, + &_huff_book__44u5__long,&_huff_book__44u5__long, + &_resbook_44u_5,&_resbook_44u_5} +}; + +static vorbis_residue_template _res_44u_6[]={ + {1,0, &_residue_44_mid_un, + &_huff_book__44u6__short,&_huff_book__44u6__short, + &_resbook_44u_6,&_resbook_44u_6}, + + {1,0, &_residue_44_mid_un, + &_huff_book__44u6__long,&_huff_book__44u6__long, + &_resbook_44u_6,&_resbook_44u_6} +}; + +static vorbis_residue_template _res_44u_7[]={ + {1,0, &_residue_44_mid_un, + &_huff_book__44u7__short,&_huff_book__44u7__short, + &_resbook_44u_7,&_resbook_44u_7}, + + {1,0, &_residue_44_mid_un, + &_huff_book__44u7__long,&_huff_book__44u7__long, + &_resbook_44u_7,&_resbook_44u_7} +}; + +static vorbis_residue_template _res_44u_8[]={ + {1,0, &_residue_44_hi_un, + &_huff_book__44u8__short,&_huff_book__44u8__short, + &_resbook_44u_8,&_resbook_44u_8}, + + {1,0, &_residue_44_hi_un, + &_huff_book__44u8__long,&_huff_book__44u8__long, + &_resbook_44u_8,&_resbook_44u_8} +}; +static vorbis_residue_template _res_44u_9[]={ + {1,0, &_residue_44_hi_un, + &_huff_book__44u9__short,&_huff_book__44u9__short, + &_resbook_44u_9,&_resbook_44u_9}, + + {1,0, &_residue_44_hi_un, + &_huff_book__44u9__long,&_huff_book__44u9__long, + &_resbook_44u_9,&_resbook_44u_9} +}; + +static vorbis_mapping_template _mapres_template_44_uncoupled[]={ + { _map_nominal_u, _res_44u_n1 }, /* -1 */ + { _map_nominal_u, _res_44u_0 }, /* 0 */ + { _map_nominal_u, _res_44u_1 }, /* 1 */ + { _map_nominal_u, _res_44u_2 }, /* 2 */ + { _map_nominal_u, _res_44u_3 }, /* 3 */ + { _map_nominal_u, _res_44u_4 }, /* 4 */ + { _map_nominal_u, _res_44u_5 }, /* 5 */ + { _map_nominal_u, _res_44u_6 }, /* 6 */ + { _map_nominal_u, _res_44u_7 }, /* 7 */ + { _map_nominal_u, _res_44u_8 }, /* 8 */ + { _map_nominal_u, _res_44u_9 }, /* 9 */ +}; diff --git a/Engine/lib/libvorbis/lib/modes/residue_8.h b/Engine/lib/libvorbis/lib/modes/residue_8.h new file mode 100644 index 000000000..8be1c741b --- /dev/null +++ b/Engine/lib/libvorbis/lib/modes/residue_8.h @@ -0,0 +1,109 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: toplevel residue templates 8/11kHz + last mod: $Id: residue_8.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#include "vorbis/codec.h" +#include "backends.h" + +/***** residue backends *********************************************/ + +static static_bookblock _resbook_8s_0={ + { + {0}, + {0,0,&_8c0_s_p1_0}, + {0}, + {0,0,&_8c0_s_p3_0}, + {0,0,&_8c0_s_p4_0}, + {0,0,&_8c0_s_p5_0}, + {0,0,&_8c0_s_p6_0}, + {&_8c0_s_p7_0,&_8c0_s_p7_1}, + {&_8c0_s_p8_0,&_8c0_s_p8_1}, + {&_8c0_s_p9_0,&_8c0_s_p9_1,&_8c0_s_p9_2} + } +}; +static static_bookblock _resbook_8s_1={ + { + {0}, + {0,0,&_8c1_s_p1_0}, + {0}, + {0,0,&_8c1_s_p3_0}, + {0,0,&_8c1_s_p4_0}, + {0,0,&_8c1_s_p5_0}, + {0,0,&_8c1_s_p6_0}, + {&_8c1_s_p7_0,&_8c1_s_p7_1}, + {&_8c1_s_p8_0,&_8c1_s_p8_1}, + {&_8c1_s_p9_0,&_8c1_s_p9_1,&_8c1_s_p9_2} + } +}; + +static vorbis_residue_template _res_8s_0[]={ + {2,0, &_residue_44_mid, + &_huff_book__8c0_s_single,&_huff_book__8c0_s_single, + &_resbook_8s_0,&_resbook_8s_0}, +}; +static vorbis_residue_template _res_8s_1[]={ + {2,0, &_residue_44_mid, + &_huff_book__8c1_s_single,&_huff_book__8c1_s_single, + &_resbook_8s_1,&_resbook_8s_1}, +}; + +static vorbis_mapping_template _mapres_template_8_stereo[2]={ + { _map_nominal, _res_8s_0 }, /* 0 */ + { _map_nominal, _res_8s_1 }, /* 1 */ +}; + +static static_bookblock _resbook_8u_0={ + { + {0}, + {0,0,&_8u0__p1_0}, + {0,0,&_8u0__p2_0}, + {0,0,&_8u0__p3_0}, + {0,0,&_8u0__p4_0}, + {0,0,&_8u0__p5_0}, + {&_8u0__p6_0,&_8u0__p6_1}, + {&_8u0__p7_0,&_8u0__p7_1,&_8u0__p7_2} + } +}; +static static_bookblock _resbook_8u_1={ + { + {0}, + {0,0,&_8u1__p1_0}, + {0,0,&_8u1__p2_0}, + {0,0,&_8u1__p3_0}, + {0,0,&_8u1__p4_0}, + {0,0,&_8u1__p5_0}, + {0,0,&_8u1__p6_0}, + {&_8u1__p7_0,&_8u1__p7_1}, + {&_8u1__p8_0,&_8u1__p8_1}, + {&_8u1__p9_0,&_8u1__p9_1,&_8u1__p9_2} + } +}; + +static vorbis_residue_template _res_8u_0[]={ + {1,0, &_residue_44_low_un, + &_huff_book__8u0__single,&_huff_book__8u0__single, + &_resbook_8u_0,&_resbook_8u_0}, +}; +static vorbis_residue_template _res_8u_1[]={ + {1,0, &_residue_44_mid_un, + &_huff_book__8u1__single,&_huff_book__8u1__single, + &_resbook_8u_1,&_resbook_8u_1}, +}; + +static vorbis_mapping_template _mapres_template_8_uncoupled[2]={ + { _map_nominal_u, _res_8u_0 }, /* 0 */ + { _map_nominal_u, _res_8u_1 }, /* 1 */ +}; diff --git a/Engine/lib/libvorbis/lib/modes/setup_11.h b/Engine/lib/libvorbis/lib/modes/setup_11.h new file mode 100644 index 000000000..7695b3da3 --- /dev/null +++ b/Engine/lib/libvorbis/lib/modes/setup_11.h @@ -0,0 +1,141 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: 11kHz settings + last mod: $Id: setup_11.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#include "psych_11.h" + +static int blocksize_11[2]={ + 512,512 +}; + +static int _floor_mapping_11[2]={ + 6,6, +}; + +static double rate_mapping_11[3]={ + 8000.,13000.,44000., +}; + +static double rate_mapping_11_uncoupled[3]={ + 12000.,20000.,50000., +}; + +static double quality_mapping_11[3]={ + -.1,.0,1. +}; + +ve_setup_data_template ve_setup_11_stereo={ + 2, + rate_mapping_11, + quality_mapping_11, + 2, + 9000, + 15000, + + blocksize_11, + blocksize_11, + + _psy_tone_masteratt_11, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_11, + NULL, + _vp_tonemask_adj_11, + + _psy_noiseguards_8, + _psy_noisebias_11, + _psy_noisebias_11, + NULL, + NULL, + _psy_noise_suppress, + + _psy_compand_8, + _psy_compand_8_mapping, + NULL, + + {_noise_start_8,_noise_start_8}, + {_noise_part_8,_noise_part_8}, + _noise_thresh_11, + + _psy_ath_floater_8, + _psy_ath_abs_8, + + _psy_lowpass_11, + + _psy_global_44, + _global_mapping_8, + _psy_stereo_modes_8, + + _floor_books, + _floor, + _floor_mapping_11, + NULL, + + _mapres_template_8_stereo +}; + +ve_setup_data_template ve_setup_11_uncoupled={ + 2, + rate_mapping_11_uncoupled, + quality_mapping_11, + -1, + 9000, + 15000, + + blocksize_11, + blocksize_11, + + _psy_tone_masteratt_11, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_11, + NULL, + _vp_tonemask_adj_11, + + _psy_noiseguards_8, + _psy_noisebias_11, + _psy_noisebias_11, + NULL, + NULL, + _psy_noise_suppress, + + _psy_compand_8, + _psy_compand_8_mapping, + NULL, + + {_noise_start_8,_noise_start_8}, + {_noise_part_8,_noise_part_8}, + _noise_thresh_11, + + _psy_ath_floater_8, + _psy_ath_abs_8, + + _psy_lowpass_11, + + _psy_global_44, + _global_mapping_8, + _psy_stereo_modes_8, + + _floor_books, + _floor, + _floor_mapping_11, + NULL, + + _mapres_template_8_uncoupled +}; + diff --git a/Engine/lib/libvorbis/lib/modes/setup_16.h b/Engine/lib/libvorbis/lib/modes/setup_16.h new file mode 100644 index 000000000..9ada8e047 --- /dev/null +++ b/Engine/lib/libvorbis/lib/modes/setup_16.h @@ -0,0 +1,149 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: 16kHz settings + last mod: $Id: setup_16.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#include "psych_16.h" +#include "residue_16.h" + +static int blocksize_16_short[3]={ + 1024,512,512 +}; +static int blocksize_16_long[3]={ + 1024,1024,1024 +}; + +static int _floor_mapping_16_short[3]={ + 9,3,3 +}; +static int _floor_mapping_16[3]={ + 9,9,9 +}; + +static double rate_mapping_16[4]={ + 12000.,20000.,44000.,86000. +}; + +static double rate_mapping_16_uncoupled[4]={ + 16000.,28000.,64000.,100000. +}; + +static double _global_mapping_16[4]={ 1., 2., 3., 4. }; + +static double quality_mapping_16[4]={ -.1,.05,.5,1. }; + +static double _psy_compand_16_mapping[4]={ 0., .8, 1., 1.}; + +ve_setup_data_template ve_setup_16_stereo={ + 3, + rate_mapping_16, + quality_mapping_16, + 2, + 15000, + 19000, + + blocksize_16_short, + blocksize_16_long, + + _psy_tone_masteratt_16, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_16, + _vp_tonemask_adj_16, + _vp_tonemask_adj_16, + + _psy_noiseguards_16, + _psy_noisebias_16_impulse, + _psy_noisebias_16_short, + _psy_noisebias_16_short, + _psy_noisebias_16, + _psy_noise_suppress, + + _psy_compand_8, + _psy_compand_16_mapping, + _psy_compand_16_mapping, + + {_noise_start_16,_noise_start_16}, + { _noise_part_16, _noise_part_16}, + _noise_thresh_16, + + _psy_ath_floater_16, + _psy_ath_abs_16, + + _psy_lowpass_16, + + _psy_global_44, + _global_mapping_16, + _psy_stereo_modes_16, + + _floor_books, + _floor, + _floor_mapping_16_short, + _floor_mapping_16, + + _mapres_template_16_stereo +}; + +ve_setup_data_template ve_setup_16_uncoupled={ + 3, + rate_mapping_16_uncoupled, + quality_mapping_16, + -1, + 15000, + 19000, + + blocksize_16_short, + blocksize_16_long, + + _psy_tone_masteratt_16, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_16, + _vp_tonemask_adj_16, + _vp_tonemask_adj_16, + + _psy_noiseguards_8, + _psy_noisebias_16_impulse, + _psy_noisebias_16_short, + _psy_noisebias_16_short, + _psy_noisebias_16, + _psy_noise_suppress, + + _psy_compand_8, + _psy_compand_16_mapping, + _psy_compand_16_mapping, + + {_noise_start_16,_noise_start_16}, + { _noise_part_16, _noise_part_16}, + _noise_thresh_16, + + _psy_ath_floater_16, + _psy_ath_abs_16, + + _psy_lowpass_16, + + _psy_global_44, + _global_mapping_16, + _psy_stereo_modes_16, + + _floor_books, + _floor, + _floor_mapping_16_short, + _floor_mapping_16, + + _mapres_template_16_uncoupled +}; diff --git a/Engine/lib/libvorbis/lib/modes/setup_22.h b/Engine/lib/libvorbis/lib/modes/setup_22.h new file mode 100644 index 000000000..b528bd37f --- /dev/null +++ b/Engine/lib/libvorbis/lib/modes/setup_22.h @@ -0,0 +1,128 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: 22kHz settings + last mod: $Id: setup_22.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +static double rate_mapping_22[4]={ + 15000.,20000.,44000.,86000. +}; + +static double rate_mapping_22_uncoupled[4]={ + 16000.,28000.,50000.,90000. +}; + +static double _psy_lowpass_22[4]={9.5,11.,30.,99.}; + +ve_setup_data_template ve_setup_22_stereo={ + 3, + rate_mapping_22, + quality_mapping_16, + 2, + 19000, + 26000, + + blocksize_16_short, + blocksize_16_long, + + _psy_tone_masteratt_16, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_16, + _vp_tonemask_adj_16, + _vp_tonemask_adj_16, + + _psy_noiseguards_16, + _psy_noisebias_16_impulse, + _psy_noisebias_16_short, + _psy_noisebias_16_short, + _psy_noisebias_16, + _psy_noise_suppress, + + _psy_compand_8, + _psy_compand_8_mapping, + _psy_compand_8_mapping, + + {_noise_start_16,_noise_start_16}, + { _noise_part_16, _noise_part_16}, + _noise_thresh_16, + + _psy_ath_floater_16, + _psy_ath_abs_16, + + _psy_lowpass_22, + + _psy_global_44, + _global_mapping_16, + _psy_stereo_modes_16, + + _floor_books, + _floor, + _floor_mapping_16_short, + _floor_mapping_16, + + _mapres_template_16_stereo +}; + +ve_setup_data_template ve_setup_22_uncoupled={ + 3, + rate_mapping_22_uncoupled, + quality_mapping_16, + -1, + 19000, + 26000, + + blocksize_16_short, + blocksize_16_long, + + _psy_tone_masteratt_16, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_16, + _vp_tonemask_adj_16, + _vp_tonemask_adj_16, + + _psy_noiseguards_8, + _psy_noisebias_16_impulse, + _psy_noisebias_16_short, + _psy_noisebias_16_short, + _psy_noisebias_16, + _psy_noise_suppress, + + _psy_compand_8, + _psy_compand_8_mapping, + _psy_compand_8_mapping, + + {_noise_start_16,_noise_start_16}, + { _noise_part_16, _noise_part_16}, + _noise_thresh_16, + + _psy_ath_floater_16, + _psy_ath_abs_16, + + _psy_lowpass_22, + + _psy_global_44, + _global_mapping_16, + _psy_stereo_modes_16, + + _floor_books, + _floor, + _floor_mapping_16_short, + _floor_mapping_16, + + _mapres_template_16_uncoupled +}; diff --git a/Engine/lib/libvorbis/lib/modes/setup_32.h b/Engine/lib/libvorbis/lib/modes/setup_32.h new file mode 100644 index 000000000..e8f1fde5b --- /dev/null +++ b/Engine/lib/libvorbis/lib/modes/setup_32.h @@ -0,0 +1,132 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: toplevel settings for 32kHz + last mod: $Id: setup_32.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +static double rate_mapping_32[12]={ + 18000.,28000.,35000.,45000.,56000.,60000., + 75000.,90000.,100000.,115000.,150000.,190000., +}; + +static double rate_mapping_32_un[12]={ + 30000.,42000.,52000.,64000.,72000.,78000., + 86000.,92000.,110000.,120000.,140000.,190000., +}; + +static double _psy_lowpass_32[12]={ + 12.3,13.,13.,14.,15.,99.,99.,99.,99.,99.,99.,99. +}; + +ve_setup_data_template ve_setup_32_stereo={ + 11, + rate_mapping_32, + quality_mapping_44, + 2, + 26000, + 40000, + + blocksize_short_44, + blocksize_long_44, + + _psy_tone_masteratt_44, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_otherblock, + _vp_tonemask_adj_longblock, + _vp_tonemask_adj_otherblock, + + _psy_noiseguards_44, + _psy_noisebias_impulse, + _psy_noisebias_padding, + _psy_noisebias_trans, + _psy_noisebias_long, + _psy_noise_suppress, + + _psy_compand_44, + _psy_compand_short_mapping, + _psy_compand_long_mapping, + + {_noise_start_short_44,_noise_start_long_44}, + {_noise_part_short_44,_noise_part_long_44}, + _noise_thresh_44, + + _psy_ath_floater, + _psy_ath_abs, + + _psy_lowpass_32, + + _psy_global_44, + _global_mapping_44, + _psy_stereo_modes_44, + + _floor_books, + _floor, + _floor_short_mapping_44, + _floor_long_mapping_44, + + _mapres_template_44_stereo +}; + +ve_setup_data_template ve_setup_32_uncoupled={ + 11, + rate_mapping_32_un, + quality_mapping_44, + -1, + 26000, + 40000, + + blocksize_short_44, + blocksize_long_44, + + _psy_tone_masteratt_44, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_otherblock, + _vp_tonemask_adj_longblock, + _vp_tonemask_adj_otherblock, + + _psy_noiseguards_44, + _psy_noisebias_impulse, + _psy_noisebias_padding, + _psy_noisebias_trans, + _psy_noisebias_long, + _psy_noise_suppress, + + _psy_compand_44, + _psy_compand_short_mapping, + _psy_compand_long_mapping, + + {_noise_start_short_44,_noise_start_long_44}, + {_noise_part_short_44,_noise_part_long_44}, + _noise_thresh_44, + + _psy_ath_floater, + _psy_ath_abs, + + _psy_lowpass_32, + + _psy_global_44, + _global_mapping_44, + NULL, + + _floor_books, + _floor, + _floor_short_mapping_44, + _floor_long_mapping_44, + + _mapres_template_44_uncoupled +}; diff --git a/Engine/lib/libvorbis/lib/modes/setup_44.h b/Engine/lib/libvorbis/lib/modes/setup_44.h new file mode 100644 index 000000000..dbaf8b65f --- /dev/null +++ b/Engine/lib/libvorbis/lib/modes/setup_44.h @@ -0,0 +1,107 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: toplevel settings for 44.1/48kHz + last mod: $Id: setup_44.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#include "modes/floor_all.h" +#include "modes/residue_44.h" +#include "modes/psych_44.h" + +static double rate_mapping_44_stereo[12]={ + 22500.,32000.,40000.,48000.,56000.,64000., + 80000.,96000.,112000.,128000.,160000.,250001. +}; + +static double quality_mapping_44[12]={ + -.1,.0,.1,.2,.3,.4,.5,.6,.7,.8,.9,1.0 +}; + +static int blocksize_short_44[11]={ + 512,256,256,256,256,256,256,256,256,256,256 +}; +static int blocksize_long_44[11]={ + 4096,2048,2048,2048,2048,2048,2048,2048,2048,2048,2048 +}; + +static double _psy_compand_short_mapping[12]={ + 0.5, 1., 1., 1.3, 1.6, 2., 2., 2., 2., 2., 2., 2. +}; +static double _psy_compand_long_mapping[12]={ + 3.5, 4., 4., 4.3, 4.6, 5., 5., 5., 5., 5., 5., 5. +}; + +static double _global_mapping_44[12]={ + /* 1., 1., 1.5, 2., 2., 2.5, 2.7, 3.0, 3.5, 4., 4. */ + 0., 1., 1., 1.5, 2., 2., 2.5, 2.7, 3.0, 3.7, 4., 4. +}; + +static int _floor_short_mapping_44[11]={ + 1,0,0,2,2,4,5,5,5,5,5 +}; +static int _floor_long_mapping_44[11]={ + 8,7,7,7,7,7,7,7,7,7,7 +}; + +ve_setup_data_template ve_setup_44_stereo={ + 11, + rate_mapping_44_stereo, + quality_mapping_44, + 2, + 40000, + 50000, + + blocksize_short_44, + blocksize_long_44, + + _psy_tone_masteratt_44, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_otherblock, + _vp_tonemask_adj_longblock, + _vp_tonemask_adj_otherblock, + + _psy_noiseguards_44, + _psy_noisebias_impulse, + _psy_noisebias_padding, + _psy_noisebias_trans, + _psy_noisebias_long, + _psy_noise_suppress, + + _psy_compand_44, + _psy_compand_short_mapping, + _psy_compand_long_mapping, + + {_noise_start_short_44,_noise_start_long_44}, + {_noise_part_short_44,_noise_part_long_44}, + _noise_thresh_44, + + _psy_ath_floater, + _psy_ath_abs, + + _psy_lowpass_44, + + _psy_global_44, + _global_mapping_44, + _psy_stereo_modes_44, + + _floor_books, + _floor, + _floor_short_mapping_44, + _floor_long_mapping_44, + + _mapres_template_44_stereo +}; + diff --git a/Engine/lib/libvorbis/lib/modes/setup_44u.h b/Engine/lib/libvorbis/lib/modes/setup_44u.h new file mode 100644 index 000000000..b2b515f86 --- /dev/null +++ b/Engine/lib/libvorbis/lib/modes/setup_44u.h @@ -0,0 +1,74 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: toplevel settings for 44.1/48kHz uncoupled modes + last mod: $Id: setup_44u.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#include "modes/residue_44u.h" + +static double rate_mapping_44_un[12]={ + 32000.,48000.,60000.,70000.,80000.,86000., + 96000.,110000.,120000.,140000.,160000.,240001. +}; + +ve_setup_data_template ve_setup_44_uncoupled={ + 11, + rate_mapping_44_un, + quality_mapping_44, + -1, + 40000, + 50000, + + blocksize_short_44, + blocksize_long_44, + + _psy_tone_masteratt_44, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_otherblock, + _vp_tonemask_adj_longblock, + _vp_tonemask_adj_otherblock, + + _psy_noiseguards_44, + _psy_noisebias_impulse, + _psy_noisebias_padding, + _psy_noisebias_trans, + _psy_noisebias_long, + _psy_noise_suppress, + + _psy_compand_44, + _psy_compand_short_mapping, + _psy_compand_long_mapping, + + {_noise_start_short_44,_noise_start_long_44}, + {_noise_part_short_44,_noise_part_long_44}, + _noise_thresh_44, + + _psy_ath_floater, + _psy_ath_abs, + + _psy_lowpass_44, + + _psy_global_44, + _global_mapping_44, + NULL, + + _floor_books, + _floor, + _floor_short_mapping_44, + _floor_long_mapping_44, + + _mapres_template_44_uncoupled +}; diff --git a/Engine/lib/libvorbis/lib/modes/setup_8.h b/Engine/lib/libvorbis/lib/modes/setup_8.h new file mode 100644 index 000000000..8d0c86821 --- /dev/null +++ b/Engine/lib/libvorbis/lib/modes/setup_8.h @@ -0,0 +1,146 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: 8kHz settings + last mod: $Id: setup_8.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#include "psych_8.h" +#include "residue_8.h" + +static int blocksize_8[2]={ + 512,512 +}; + +static int _floor_mapping_8[2]={ + 6,6, +}; + +static double rate_mapping_8[3]={ + 6000.,9000.,32000., +}; + +static double rate_mapping_8_uncoupled[3]={ + 8000.,14000.,42000., +}; + +static double quality_mapping_8[3]={ + -.1,.0,1. +}; + +static double _psy_compand_8_mapping[3]={ 0., 1., 1.}; + +static double _global_mapping_8[3]={ 1., 2., 3. }; + +ve_setup_data_template ve_setup_8_stereo={ + 2, + rate_mapping_8, + quality_mapping_8, + 2, + 8000, + 9000, + + blocksize_8, + blocksize_8, + + _psy_tone_masteratt_8, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_8, + NULL, + _vp_tonemask_adj_8, + + _psy_noiseguards_8, + _psy_noisebias_8, + _psy_noisebias_8, + NULL, + NULL, + _psy_noise_suppress, + + _psy_compand_8, + _psy_compand_8_mapping, + NULL, + + {_noise_start_8,_noise_start_8}, + {_noise_part_8,_noise_part_8}, + _noise_thresh_5only, + + _psy_ath_floater_8, + _psy_ath_abs_8, + + _psy_lowpass_8, + + _psy_global_44, + _global_mapping_8, + _psy_stereo_modes_8, + + _floor_books, + _floor, + _floor_mapping_8, + NULL, + + _mapres_template_8_stereo +}; + +ve_setup_data_template ve_setup_8_uncoupled={ + 2, + rate_mapping_8_uncoupled, + quality_mapping_8, + -1, + 8000, + 9000, + + blocksize_8, + blocksize_8, + + _psy_tone_masteratt_8, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_8, + NULL, + _vp_tonemask_adj_8, + + _psy_noiseguards_8, + _psy_noisebias_8, + _psy_noisebias_8, + NULL, + NULL, + _psy_noise_suppress, + + _psy_compand_8, + _psy_compand_8_mapping, + NULL, + + {_noise_start_8,_noise_start_8}, + {_noise_part_8,_noise_part_8}, + _noise_thresh_5only, + + _psy_ath_floater_8, + _psy_ath_abs_8, + + _psy_lowpass_8, + + _psy_global_44, + _global_mapping_8, + _psy_stereo_modes_8, + + _floor_books, + _floor, + _floor_mapping_8, + NULL, + + _mapres_template_8_uncoupled +}; + diff --git a/Engine/lib/libvorbis/lib/modes/setup_X.h b/Engine/lib/libvorbis/lib/modes/setup_X.h new file mode 100644 index 000000000..88bfd237c --- /dev/null +++ b/Engine/lib/libvorbis/lib/modes/setup_X.h @@ -0,0 +1,226 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: catch-all toplevel settings for q modes only + last mod: $Id: setup_X.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +static double rate_mapping_X[12]={ + -1.,-1.,-1.,-1.,-1.,-1., + -1.,-1.,-1.,-1.,-1.,-1. +}; + +ve_setup_data_template ve_setup_X_stereo={ + 11, + rate_mapping_X, + quality_mapping_44, + 2, + 50000, + 200000, + + blocksize_short_44, + blocksize_long_44, + + _psy_tone_masteratt_44, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_otherblock, + _vp_tonemask_adj_longblock, + _vp_tonemask_adj_otherblock, + + _psy_noiseguards_44, + _psy_noisebias_impulse, + _psy_noisebias_padding, + _psy_noisebias_trans, + _psy_noisebias_long, + _psy_noise_suppress, + + _psy_compand_44, + _psy_compand_short_mapping, + _psy_compand_long_mapping, + + {_noise_start_short_44,_noise_start_long_44}, + {_noise_part_short_44,_noise_part_long_44}, + _noise_thresh_44, + + _psy_ath_floater, + _psy_ath_abs, + + _psy_lowpass_44, + + _psy_global_44, + _global_mapping_44, + _psy_stereo_modes_44, + + _floor_books, + _floor, + _floor_short_mapping_44, + _floor_long_mapping_44, + + _mapres_template_44_stereo +}; + +ve_setup_data_template ve_setup_X_uncoupled={ + 11, + rate_mapping_X, + quality_mapping_44, + -1, + 50000, + 200000, + + blocksize_short_44, + blocksize_long_44, + + _psy_tone_masteratt_44, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_otherblock, + _vp_tonemask_adj_longblock, + _vp_tonemask_adj_otherblock, + + _psy_noiseguards_44, + _psy_noisebias_impulse, + _psy_noisebias_padding, + _psy_noisebias_trans, + _psy_noisebias_long, + _psy_noise_suppress, + + _psy_compand_44, + _psy_compand_short_mapping, + _psy_compand_long_mapping, + + {_noise_start_short_44,_noise_start_long_44}, + {_noise_part_short_44,_noise_part_long_44}, + _noise_thresh_44, + + _psy_ath_floater, + _psy_ath_abs, + + _psy_lowpass_44, + + _psy_global_44, + _global_mapping_44, + NULL, + + _floor_books, + _floor, + _floor_short_mapping_44, + _floor_long_mapping_44, + + _mapres_template_44_uncoupled +}; + +ve_setup_data_template ve_setup_XX_stereo={ + 2, + rate_mapping_X, + quality_mapping_8, + 2, + 0, + 8000, + + blocksize_8, + blocksize_8, + + _psy_tone_masteratt_8, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_8, + NULL, + _vp_tonemask_adj_8, + + _psy_noiseguards_8, + _psy_noisebias_8, + _psy_noisebias_8, + NULL, + NULL, + _psy_noise_suppress, + + _psy_compand_8, + _psy_compand_8_mapping, + NULL, + + {_noise_start_8,_noise_start_8}, + {_noise_part_8,_noise_part_8}, + _noise_thresh_5only, + + _psy_ath_floater_8, + _psy_ath_abs_8, + + _psy_lowpass_8, + + _psy_global_44, + _global_mapping_8, + _psy_stereo_modes_8, + + _floor_books, + _floor, + _floor_mapping_8, + NULL, + + _mapres_template_8_stereo +}; + +ve_setup_data_template ve_setup_XX_uncoupled={ + 2, + rate_mapping_X, + quality_mapping_8, + -1, + 0, + 8000, + + blocksize_8, + blocksize_8, + + _psy_tone_masteratt_8, + _psy_tone_0dB, + _psy_tone_suppress, + + _vp_tonemask_adj_8, + NULL, + _vp_tonemask_adj_8, + + _psy_noiseguards_8, + _psy_noisebias_8, + _psy_noisebias_8, + NULL, + NULL, + _psy_noise_suppress, + + _psy_compand_8, + _psy_compand_8_mapping, + NULL, + + {_noise_start_8,_noise_start_8}, + {_noise_part_8,_noise_part_8}, + _noise_thresh_5only, + + _psy_ath_floater_8, + _psy_ath_abs_8, + + _psy_lowpass_8, + + _psy_global_44, + _global_mapping_8, + _psy_stereo_modes_8, + + _floor_books, + _floor, + _floor_mapping_8, + NULL, + + _mapres_template_8_uncoupled +}; + diff --git a/Engine/lib/libvorbis/lib/os.h b/Engine/lib/libvorbis/lib/os.h new file mode 100644 index 000000000..5edde0a3e --- /dev/null +++ b/Engine/lib/libvorbis/lib/os.h @@ -0,0 +1,156 @@ +#ifndef _OS_H +#define _OS_H +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: #ifdef jail to whip a few platforms into the UNIX ideal. + last mod: $Id: os.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "misc.h" + +#ifndef _V_IFDEFJAIL_H_ +# define _V_IFDEFJAIL_H_ + +# ifdef __GNUC__ +# define STIN static __inline__ +# elif _WIN32 +# define STIN static __inline +# else +# define STIN static +# endif + +#ifdef DJGPP +# define rint(x) (floor((x)+0.5f)) +#endif + +#ifndef M_PI +# define M_PI (3.1415926536f) +#endif + +#if defined(_WIN32) && !defined(__SYMBIAN32__) +# include +# define rint(x) (floor((x)+0.5f)) +# define NO_FLOAT_MATH_LIB +# define FAST_HYPOT(a, b) sqrt((a)*(a) + (b)*(b)) +#endif + +#if defined(__SYMBIAN32__) && defined(__WINS__) +void *_alloca(size_t size); +# define alloca _alloca +#endif + +#ifndef FAST_HYPOT +# define FAST_HYPOT hypot +#endif + +#endif + +#ifdef HAVE_ALLOCA_H +# include +#endif + +#ifdef USE_MEMORY_H +# include +#endif + +#ifndef min +# define min(x,y) ((x)>(y)?(y):(x)) +#endif + +#ifndef max +# define max(x,y) ((x)<(y)?(y):(x)) +#endif + +#if defined(__i386__) && defined(__GNUC__) && !defined(__BEOS__) +# define VORBIS_FPU_CONTROL +/* both GCC and MSVC are kinda stupid about rounding/casting to int. + Because of encapsulation constraints (GCC can't see inside the asm + block and so we end up doing stupid things like a store/load that + is collectively a noop), we do it this way */ + +/* we must set up the fpu before this works!! */ + +typedef ogg_int16_t vorbis_fpu_control; + +static inline void vorbis_fpu_setround(vorbis_fpu_control *fpu){ + ogg_int16_t ret; + ogg_int16_t temp; + __asm__ __volatile__("fnstcw %0\n\t" + "movw %0,%%dx\n\t" + "orw $62463,%%dx\n\t" + "movw %%dx,%1\n\t" + "fldcw %1\n\t":"=m"(ret):"m"(temp): "dx"); + *fpu=ret; +} + +static inline void vorbis_fpu_restore(vorbis_fpu_control fpu){ + __asm__ __volatile__("fldcw %0":: "m"(fpu)); +} + +/* assumes the FPU is in round mode! */ +static inline int vorbis_ftoi(double f){ /* yes, double! Otherwise, + we get extra fst/fld to + truncate precision */ + int i; + __asm__("fistl %0": "=m"(i) : "t"(f)); + return(i); +} +#endif + + +#if defined(_WIN32) && !defined(__GNUC__) && !defined(__BORLANDC__) && !defined(_XBOX_VER) +# define VORBIS_FPU_CONTROL + +typedef ogg_int16_t vorbis_fpu_control; + +static __inline int vorbis_ftoi(double f){ + int i; + __asm{ + fld f + fistp i + } + return i; +} + +static __inline void vorbis_fpu_setround(vorbis_fpu_control *fpu){ +} + +static __inline void vorbis_fpu_restore(vorbis_fpu_control fpu){ +} + +#endif + + +#ifndef VORBIS_FPU_CONTROL + +typedef int vorbis_fpu_control; + +static int vorbis_ftoi(double f){ + return (int)(f+.5); +} + +/* We don't have special code for this compiler/arch, so do it the slow way */ +# define vorbis_fpu_setround(vorbis_fpu_control) {} +# define vorbis_fpu_restore(vorbis_fpu_control) {} + +#endif + +#endif /* _OS_H */ diff --git a/Engine/lib/libvorbis/lib/psy.c b/Engine/lib/libvorbis/lib/psy.c new file mode 100644 index 000000000..98123f735 --- /dev/null +++ b/Engine/lib/libvorbis/lib/psy.c @@ -0,0 +1,1227 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: psychoacoustics not including preecho + last mod: $Id: psy.c 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#include +#include +#include +#include "vorbis/codec.h" +#include "codec_internal.h" + +#include "masking.h" +#include "psy.h" +#include "os.h" +#include "lpc.h" +#include "smallft.h" +#include "scales.h" +#include "misc.h" + +#define NEGINF -9999.f +static double stereo_threshholds[]={0.0, .5, 1.0, 1.5, 2.5, 4.5, 8.5, 16.5, 9e10}; +static double stereo_threshholds_limited[]={0.0, .5, 1.0, 1.5, 2.0, 2.5, 4.5, 8.5, 9e10}; + +vorbis_look_psy_global *_vp_global_look(vorbis_info *vi){ + codec_setup_info *ci=vi->codec_setup; + vorbis_info_psy_global *gi=&ci->psy_g_param; + vorbis_look_psy_global *look=_ogg_calloc(1,sizeof(*look)); + + look->channels=vi->channels; + + look->ampmax=-9999.; + look->gi=gi; + return(look); +} + +void _vp_global_free(vorbis_look_psy_global *look){ + if(look){ + memset(look,0,sizeof(*look)); + _ogg_free(look); + } +} + +void _vi_gpsy_free(vorbis_info_psy_global *i){ + if(i){ + memset(i,0,sizeof(*i)); + _ogg_free(i); + } +} + +void _vi_psy_free(vorbis_info_psy *i){ + if(i){ + memset(i,0,sizeof(*i)); + _ogg_free(i); + } +} + +static void min_curve(float *c, + float *c2){ + int i; + for(i=0;ic[i])c[i]=c2[i]; +} + +static void attenuate_curve(float *c,float att){ + int i; + for(i=0;iATH[j+k+ath_offset])min=ATH[j+k+ath_offset]; + }else{ + if(min>ATH[MAX_ATH-1])min=ATH[MAX_ATH-1]; + } + ath[j]=min; + } + + /* copy curves into working space, replicate the 50dB curve to 30 + and 40, replicate the 100dB curve to 110 */ + for(j=0;j<6;j++) + memcpy(workc[i][j+2],tonemasks[i][j],EHMER_MAX*sizeof(*tonemasks[i][j])); + memcpy(workc[i][0],tonemasks[i][0],EHMER_MAX*sizeof(*tonemasks[i][0])); + memcpy(workc[i][1],tonemasks[i][0],EHMER_MAX*sizeof(*tonemasks[i][0])); + + /* apply centered curve boost/decay */ + for(j=0;j0)adj=0.; + if(adj>0. && center_boost<0)adj=0.; + workc[i][j][k]+=adj; + } + } + + /* normalize curves so the driving amplitude is 0dB */ + /* make temp curves with the ATH overlayed */ + for(j=0;j an eighth of an octave and that the eighth + octave values may also be composited. */ + + /* which octave curves will we be compositing? */ + bin=floor(fromOC(i*.5)/binHz); + lo_curve= ceil(toOC(bin*binHz+1)*2); + hi_curve= floor(toOC((bin+1)*binHz)*2); + if(lo_curve>i)lo_curve=i; + if(lo_curve<0)lo_curve=0; + if(hi_curve>=P_BANDS)hi_curve=P_BANDS-1; + + for(m=0;mn)lo_bin=n; + if(lo_binn)hi_bin=n; + + for(;lworkc[k][m][j]) + brute_buffer[l]=workc[k][m][j]; + } + + for(;lworkc[k][m][EHMER_MAX-1]) + brute_buffer[l]=workc[k][m][EHMER_MAX-1]; + + } + + /* be equally paranoid about being valid up to next half ocatve */ + if(i+1n)lo_bin=n; + if(lo_binn)hi_bin=n; + + for(;lworkc[k][m][j]) + brute_buffer[l]=workc[k][m][j]; + } + + for(;lworkc[k][m][EHMER_MAX-1]) + brute_buffer[l]=workc[k][m][EHMER_MAX-1]; + + } + + + for(j=0;j=n){ + ret[i][m][j+2]=-999.; + }else{ + ret[i][m][j+2]=brute_buffer[bin]; + } + } + } + + /* add fenceposts */ + for(j=0;j-200.f)break; + ret[i][m][0]=j; + + for(j=EHMER_MAX-1;j>EHMER_OFFSET+1;j--) + if(ret[i][m][j+2]>-200.f) + break; + ret[i][m][1]=j; + + } + } + + return(ret); +} + +void _vp_psy_init(vorbis_look_psy *p,vorbis_info_psy *vi, + vorbis_info_psy_global *gi,int n,long rate){ + long i,j,lo=-99,hi=1; + long maxoc; + memset(p,0,sizeof(*p)); + + p->eighth_octave_lines=gi->eighth_octave_lines; + p->shiftoc=rint(log(gi->eighth_octave_lines*8.f)/log(2.f))-1; + + p->firstoc=toOC(.25f*rate*.5/n)*(1<<(p->shiftoc+1))-gi->eighth_octave_lines; + maxoc=toOC((n+.25f)*rate*.5/n)*(1<<(p->shiftoc+1))+.5f; + p->total_octave_lines=maxoc-p->firstoc+1; + p->ath=_ogg_malloc(n*sizeof(*p->ath)); + + p->octave=_ogg_malloc(n*sizeof(*p->octave)); + p->bark=_ogg_malloc(n*sizeof(*p->bark)); + p->vi=vi; + p->n=n; + p->rate=rate; + + /* AoTuV HF weighting */ + p->m_val = 1.; + if(rate < 26000) p->m_val = 0; + else if(rate < 38000) p->m_val = .94; /* 32kHz */ + else if(rate > 46000) p->m_val = 1.275; /* 48kHz */ + + /* set up the lookups for a given blocksize and sample rate */ + + for(i=0,j=0;iath[j]=base+100.; + base+=delta; + } + } + } + + for(i=0;inoisewindowlominnoisewindowlo);lo++); + + for(;hi<=n && (hinoisewindowhimin || + toBARK(rate/(2*n)*hi)<(bark+vi->noisewindowhi));hi++); + + p->bark[i]=((lo-1)<<16)+(hi-1); + + } + + for(i=0;ioctave[i]=toOC((i+.25f)*.5*rate/n)*(1<<(p->shiftoc+1))+.5f; + + p->tonecurves=setup_tone_curves(vi->toneatt,rate*.5/n,n, + vi->tone_centerboost,vi->tone_decay); + + /* set up rolling noise median */ + p->noiseoffset=_ogg_malloc(P_NOISECURVES*sizeof(*p->noiseoffset)); + for(i=0;inoiseoffset[i]=_ogg_malloc(n*sizeof(**p->noiseoffset)); + + for(i=0;i=P_BANDS-1)halfoc=P_BANDS-1; + inthalfoc=(int)halfoc; + del=halfoc-inthalfoc; + + for(j=0;jnoiseoffset[j][i]= + p->vi->noiseoff[j][inthalfoc]*(1.-del) + + p->vi->noiseoff[j][inthalfoc+1]*del; + + } +#if 0 + { + static int ls=0; + _analysis_output_always("noiseoff0",ls,p->noiseoffset[0],n,1,0,0); + _analysis_output_always("noiseoff1",ls,p->noiseoffset[1],n,1,0,0); + _analysis_output_always("noiseoff2",ls++,p->noiseoffset[2],n,1,0,0); + } +#endif +} + +void _vp_psy_clear(vorbis_look_psy *p){ + int i,j; + if(p){ + if(p->ath)_ogg_free(p->ath); + if(p->octave)_ogg_free(p->octave); + if(p->bark)_ogg_free(p->bark); + if(p->tonecurves){ + for(i=0;itonecurves[i][j]); + } + _ogg_free(p->tonecurves[i]); + } + _ogg_free(p->tonecurves); + } + if(p->noiseoffset){ + for(i=0;inoiseoffset[i]); + } + _ogg_free(p->noiseoffset); + } + memset(p,0,sizeof(*p)); + } +} + +/* octave/(8*eighth_octave_lines) x scale and dB y scale */ +static void seed_curve(float *seed, + const float **curves, + float amp, + int oc, int n, + int linesper,float dBoffset){ + int i,post1; + int seedptr; + const float *posts,*curve; + + int choice=(int)((amp+dBoffset-P_LEVEL_0)*.1f); + choice=max(choice,0); + choice=min(choice,P_LEVELS-1); + posts=curves[choice]; + curve=posts+2; + post1=(int)posts[1]; + seedptr=oc+(posts[0]-EHMER_OFFSET)*linesper-(linesper>>1); + + for(i=posts[0];i0){ + float lin=amp+curve[i]; + if(seed[seedptr]=n)break; + } +} + +static void seed_loop(vorbis_look_psy *p, + const float ***curves, + const float *f, + const float *flr, + float *seed, + float specmax){ + vorbis_info_psy *vi=p->vi; + long n=p->n,i; + float dBoffset=vi->max_curve_dB-specmax; + + /* prime the working vector with peak values */ + + for(i=0;ioctave[i]; + while(i+1octave[i+1]==oc){ + i++; + if(f[i]>max)max=f[i]; + } + + if(max+6.f>flr[i]){ + oc=oc>>p->shiftoc; + + if(oc>=P_BANDS)oc=P_BANDS-1; + if(oc<0)oc=0; + + seed_curve(seed, + curves[oc], + max, + p->octave[i]-p->firstoc, + p->total_octave_lines, + p->eighth_octave_lines, + dBoffset); + } + } +} + +static void seed_chase(float *seeds, int linesper, long n){ + long *posstack=alloca(n*sizeof(*posstack)); + float *ampstack=alloca(n*sizeof(*ampstack)); + long stack=0; + long pos=0; + long i; + + for(i=0;i1 && ampstack[stack-1]<=ampstack[stack-2] && + iampstack[i]){ + endpos=posstack[i+1]; + }else{ + endpos=posstack[i]+linesper+1; /* +1 is important, else bin 0 is + discarded in short frames */ + } + if(endpos>n)endpos=n; + for(;pos +static void max_seeds(vorbis_look_psy *p, + float *seed, + float *flr){ + long n=p->total_octave_lines; + int linesper=p->eighth_octave_lines; + long linpos=0; + long pos; + + seed_chase(seed,linesper,n); /* for masking */ + + pos=p->octave[0]-p->firstoc-(linesper>>1); + + while(linpos+1n){ + float minV=seed[pos]; + long end=((p->octave[linpos]+p->octave[linpos+1])>>1)-p->firstoc; + if(minV>p->vi->tone_abs_limit)minV=p->vi->tone_abs_limit; + while(pos+1<=end){ + pos++; + if((seed[pos]>NEGINF && seed[pos]firstoc; + for(;linposn && p->octave[linpos]<=end;linpos++) + if(flr[linpos]total_octave_lines-1]; + for(;linposn;linpos++) + if(flr[linpos]> 16; + if( lo>=0 ) break; + hi = b[i] & 0xffff; + + tN = N[hi] + N[-lo]; + tX = X[hi] - X[-lo]; + tXX = XX[hi] + XX[-lo]; + tY = Y[hi] + Y[-lo]; + tXY = XY[hi] - XY[-lo]; + + A = tY * tXX - tX * tXY; + B = tN * tXY - tX * tY; + D = tN * tXX - tX * tX; + R = (A + x * B) / D; + if (R < 0.f) + R = 0.f; + + noise[i] = R - offset; + } + + for ( ;; i++, x += 1.f) { + + lo = b[i] >> 16; + hi = b[i] & 0xffff; + if(hi>=n)break; + + tN = N[hi] - N[lo]; + tX = X[hi] - X[lo]; + tXX = XX[hi] - XX[lo]; + tY = Y[hi] - Y[lo]; + tXY = XY[hi] - XY[lo]; + + A = tY * tXX - tX * tXY; + B = tN * tXY - tX * tY; + D = tN * tXX - tX * tX; + R = (A + x * B) / D; + if (R < 0.f) R = 0.f; + + noise[i] = R - offset; + } + for ( ; i < n; i++, x += 1.f) { + + R = (A + x * B) / D; + if (R < 0.f) R = 0.f; + + noise[i] = R - offset; + } + + if (fixed <= 0) return; + + for (i = 0, x = 0.f;; i++, x += 1.f) { + hi = i + fixed / 2; + lo = hi - fixed; + if(lo>=0)break; + + tN = N[hi] + N[-lo]; + tX = X[hi] - X[-lo]; + tXX = XX[hi] + XX[-lo]; + tY = Y[hi] + Y[-lo]; + tXY = XY[hi] - XY[-lo]; + + + A = tY * tXX - tX * tXY; + B = tN * tXY - tX * tY; + D = tN * tXX - tX * tX; + R = (A + x * B) / D; + + if (R - offset < noise[i]) noise[i] = R - offset; + } + for ( ;; i++, x += 1.f) { + + hi = i + fixed / 2; + lo = hi - fixed; + if(hi>=n)break; + + tN = N[hi] - N[lo]; + tX = X[hi] - X[lo]; + tXX = XX[hi] - XX[lo]; + tY = Y[hi] - Y[lo]; + tXY = XY[hi] - XY[lo]; + + A = tY * tXX - tX * tXY; + B = tN * tXY - tX * tY; + D = tN * tXX - tX * tX; + R = (A + x * B) / D; + + if (R - offset < noise[i]) noise[i] = R - offset; + } + for ( ; i < n; i++, x += 1.f) { + R = (A + x * B) / D; + if (R - offset < noise[i]) noise[i] = R - offset; + } +} + +static float FLOOR1_fromdB_INV_LOOKUP[256]={ + 0.F, 8.81683e+06F, 8.27882e+06F, 7.77365e+06F, + 7.29930e+06F, 6.85389e+06F, 6.43567e+06F, 6.04296e+06F, + 5.67422e+06F, 5.32798e+06F, 5.00286e+06F, 4.69759e+06F, + 4.41094e+06F, 4.14178e+06F, 3.88905e+06F, 3.65174e+06F, + 3.42891e+06F, 3.21968e+06F, 3.02321e+06F, 2.83873e+06F, + 2.66551e+06F, 2.50286e+06F, 2.35014e+06F, 2.20673e+06F, + 2.07208e+06F, 1.94564e+06F, 1.82692e+06F, 1.71544e+06F, + 1.61076e+06F, 1.51247e+06F, 1.42018e+06F, 1.33352e+06F, + 1.25215e+06F, 1.17574e+06F, 1.10400e+06F, 1.03663e+06F, + 973377.F, 913981.F, 858210.F, 805842.F, + 756669.F, 710497.F, 667142.F, 626433.F, + 588208.F, 552316.F, 518613.F, 486967.F, + 457252.F, 429351.F, 403152.F, 378551.F, + 355452.F, 333762.F, 313396.F, 294273.F, + 276316.F, 259455.F, 243623.F, 228757.F, + 214798.F, 201691.F, 189384.F, 177828.F, + 166977.F, 156788.F, 147221.F, 138237.F, + 129802.F, 121881.F, 114444.F, 107461.F, + 100903.F, 94746.3F, 88964.9F, 83536.2F, + 78438.8F, 73652.5F, 69158.2F, 64938.1F, + 60975.6F, 57254.9F, 53761.2F, 50480.6F, + 47400.3F, 44507.9F, 41792.0F, 39241.9F, + 36847.3F, 34598.9F, 32487.7F, 30505.3F, + 28643.8F, 26896.0F, 25254.8F, 23713.7F, + 22266.7F, 20908.0F, 19632.2F, 18434.2F, + 17309.4F, 16253.1F, 15261.4F, 14330.1F, + 13455.7F, 12634.6F, 11863.7F, 11139.7F, + 10460.0F, 9821.72F, 9222.39F, 8659.64F, + 8131.23F, 7635.06F, 7169.17F, 6731.70F, + 6320.93F, 5935.23F, 5573.06F, 5232.99F, + 4913.67F, 4613.84F, 4332.30F, 4067.94F, + 3819.72F, 3586.64F, 3367.78F, 3162.28F, + 2969.31F, 2788.13F, 2617.99F, 2458.24F, + 2308.24F, 2167.39F, 2035.14F, 1910.95F, + 1794.35F, 1684.85F, 1582.04F, 1485.51F, + 1394.86F, 1309.75F, 1229.83F, 1154.78F, + 1084.32F, 1018.15F, 956.024F, 897.687F, + 842.910F, 791.475F, 743.179F, 697.830F, + 655.249F, 615.265F, 577.722F, 542.469F, + 509.367F, 478.286F, 449.101F, 421.696F, + 395.964F, 371.803F, 349.115F, 327.812F, + 307.809F, 289.026F, 271.390F, 254.830F, + 239.280F, 224.679F, 210.969F, 198.096F, + 186.008F, 174.658F, 164.000F, 153.993F, + 144.596F, 135.773F, 127.488F, 119.708F, + 112.404F, 105.545F, 99.1046F, 93.0572F, + 87.3788F, 82.0469F, 77.0404F, 72.3394F, + 67.9252F, 63.7804F, 59.8885F, 56.2341F, + 52.8027F, 49.5807F, 46.5553F, 43.7144F, + 41.0470F, 38.5423F, 36.1904F, 33.9821F, + 31.9085F, 29.9614F, 28.1332F, 26.4165F, + 24.8045F, 23.2910F, 21.8697F, 20.5352F, + 19.2822F, 18.1056F, 17.0008F, 15.9634F, + 14.9893F, 14.0746F, 13.2158F, 12.4094F, + 11.6522F, 10.9411F, 10.2735F, 9.64662F, + 9.05798F, 8.50526F, 7.98626F, 7.49894F, + 7.04135F, 6.61169F, 6.20824F, 5.82941F, + 5.47370F, 5.13970F, 4.82607F, 4.53158F, + 4.25507F, 3.99542F, 3.75162F, 3.52269F, + 3.30774F, 3.10590F, 2.91638F, 2.73842F, + 2.57132F, 2.41442F, 2.26709F, 2.12875F, + 1.99885F, 1.87688F, 1.76236F, 1.65482F, + 1.55384F, 1.45902F, 1.36999F, 1.28640F, + 1.20790F, 1.13419F, 1.06499F, 1.F +}; + +void _vp_remove_floor(vorbis_look_psy *p, + float *mdct, + int *codedflr, + float *residue, + int sliding_lowpass){ + + int i,n=p->n; + + if(sliding_lowpass>n)sliding_lowpass=n; + + for(i=0;in; + float *work=alloca(n*sizeof(*work)); + + bark_noise_hybridmp(n,p->bark,logmdct,logmask, + 140.,-1); + + for(i=0;ibark,work,logmask,0., + p->vi->noisewindowfixed); + + for(i=0;i=NOISE_COMPAND_LEVELS)dB=NOISE_COMPAND_LEVELS-1; + if(dB<0)dB=0; + logmask[i]= work[i]+p->vi->noisecompand[dB]; + } + +} + +void _vp_tonemask(vorbis_look_psy *p, + float *logfft, + float *logmask, + float global_specmax, + float local_specmax){ + + int i,n=p->n; + + float *seed=alloca(sizeof(*seed)*p->total_octave_lines); + float att=local_specmax+p->vi->ath_adjatt; + for(i=0;itotal_octave_lines;i++)seed[i]=NEGINF; + + /* set the ATH (floating below localmax, not global max by a + specified att) */ + if(attvi->ath_maxatt)att=p->vi->ath_maxatt; + + for(i=0;iath[i]+att; + + /* tone masking */ + seed_loop(p,(const float ***)p->tonecurves,logfft,logmask,seed,global_specmax); + max_seeds(p,seed,logmask); + +} + +void _vp_offset_and_mix(vorbis_look_psy *p, + float *noise, + float *tone, + int offset_select, + float *logmask, + float *mdct, + float *logmdct){ + int i,n=p->n; + float de, coeffi, cx;/* AoTuV */ + float toneatt=p->vi->tone_masteratt[offset_select]; + + cx = p->m_val; + + for(i=0;inoiseoffset[offset_select][i]; + if(val>p->vi->noisemaxsupp)val=p->vi->noisemaxsupp; + logmask[i]=max(val,tone[i]+toneatt); + + + /* AoTuV */ + /** @ M1 ** + The following codes improve a noise problem. + A fundamental idea uses the value of masking and carries out + the relative compensation of the MDCT. + However, this code is not perfect and all noise problems cannot be solved. + by Aoyumi @ 2004/04/18 + */ + + if(offset_select == 1) { + coeffi = -17.2; /* coeffi is a -17.2dB threshold */ + val = val - logmdct[i]; /* val == mdct line value relative to floor in dB */ + + if(val > coeffi){ + /* mdct value is > -17.2 dB below floor */ + + de = 1.0-((val-coeffi)*0.005*cx); + /* pro-rated attenuation: + -0.00 dB boost if mdct value is -17.2dB (relative to floor) + -0.77 dB boost if mdct value is 0dB (relative to floor) + -1.64 dB boost if mdct value is +17.2dB (relative to floor) + etc... */ + + if(de < 0) de = 0.0001; + }else + /* mdct value is <= -17.2 dB below floor */ + + de = 1.0-((val-coeffi)*0.0003*cx); + /* pro-rated attenuation: + +0.00 dB atten if mdct value is -17.2dB (relative to floor) + +0.45 dB atten if mdct value is -34.4dB (relative to floor) + etc... */ + + mdct[i] *= de; + + } + } +} + +float _vp_ampmax_decay(float amp,vorbis_dsp_state *vd){ + vorbis_info *vi=vd->vi; + codec_setup_info *ci=vi->codec_setup; + vorbis_info_psy_global *gi=&ci->psy_g_param; + + int n=ci->blocksizes[vd->W]/2; + float secs=(float)n/vi->rate; + + amp+=secs*gi->ampmax_att_per_sec; + if(amp<-9999)amp=-9999; + return(amp); +} + +static void couple_lossless(float A, float B, + float *qA, float *qB){ + int test1=fabs(*qA)>fabs(*qB); + test1-= fabs(*qA)fabs(B))<<1)-1; + if(test1==1){ + *qB=(*qA>0.f?*qA-*qB:*qB-*qA); + }else{ + float temp=*qB; + *qB=(*qB>0.f?*qA-*qB:*qB-*qA); + *qA=temp; + } + + if(*qB>fabs(*qA)*1.9999f){ + *qB= -fabs(*qA)*2.f; + *qA= -*qA; + } +} + +static float hypot_lookup[32]={ + -0.009935, -0.011245, -0.012726, -0.014397, + -0.016282, -0.018407, -0.020800, -0.023494, + -0.026522, -0.029923, -0.033737, -0.038010, + -0.042787, -0.048121, -0.054064, -0.060671, + -0.068000, -0.076109, -0.085054, -0.094892, + -0.105675, -0.117451, -0.130260, -0.144134, + -0.159093, -0.175146, -0.192286, -0.210490, + -0.229718, -0.249913, -0.271001, -0.292893}; + +static void precomputed_couple_point(float premag, + int floorA,int floorB, + float *mag, float *ang){ + + int test=(floorA>floorB)-1; + int offset=31-abs(floorA-floorB); + float floormag=hypot_lookup[((offset<0)-1)&offset]+1.f; + + floormag*=FLOOR1_fromdB_INV_LOOKUP[(floorB&test)|(floorA&(~test))]; + + *mag=premag*floormag; + *ang=0.f; +} + +/* just like below, this is currently set up to only do + single-step-depth coupling. Otherwise, we'd have to do more + copying (which will be inevitable later) */ + +/* doing the real circular magnitude calculation is audibly superior + to (A+B)/sqrt(2) */ +static float dipole_hypot(float a, float b){ + if(a>0.){ + if(b>0.)return sqrt(a*a+b*b); + if(a>-b)return sqrt(a*a-b*b); + return -sqrt(b*b-a*a); + } + if(b<0.)return -sqrt(a*a+b*b); + if(-a>b)return -sqrt(a*a-b*b); + return sqrt(b*b-a*a); +} +static float round_hypot(float a, float b){ + if(a>0.){ + if(b>0.)return sqrt(a*a+b*b); + if(a>-b)return sqrt(a*a+b*b); + return -sqrt(b*b+a*a); + } + if(b<0.)return -sqrt(a*a+b*b); + if(-a>b)return -sqrt(a*a+b*b); + return sqrt(b*b+a*a); +} + +/* revert to round hypot for now */ +float **_vp_quantize_couple_memo(vorbis_block *vb, + vorbis_info_psy_global *g, + vorbis_look_psy *p, + vorbis_info_mapping0 *vi, + float **mdct){ + + int i,j,n=p->n; + float **ret=_vorbis_block_alloc(vb,vi->coupling_steps*sizeof(*ret)); + int limit=g->coupling_pointlimit[p->vi->blockflag][PACKETBLOBS/2]; + + for(i=0;icoupling_steps;i++){ + float *mdctM=mdct[vi->coupling_mag[i]]; + float *mdctA=mdct[vi->coupling_ang[i]]; + ret[i]=_vorbis_block_alloc(vb,n*sizeof(**ret)); + for(j=0;jf2); +} + +int **_vp_quantize_couple_sort(vorbis_block *vb, + vorbis_look_psy *p, + vorbis_info_mapping0 *vi, + float **mags){ + + + if(p->vi->normal_point_p){ + int i,j,k,n=p->n; + int **ret=_vorbis_block_alloc(vb,vi->coupling_steps*sizeof(*ret)); + int partition=p->vi->normal_partition; + float **work=alloca(sizeof(*work)*partition); + + for(i=0;icoupling_steps;i++){ + ret[i]=_vorbis_block_alloc(vb,n*sizeof(**ret)); + + for(j=0;jn; + vorbis_info_psy *vi=p->vi; + int partition=vi->normal_partition; + float **work=alloca(sizeof(*work)*partition); + int start=vi->normal_start; + + for(j=start;jn)partition=n-j; + for(i=0;in; + vorbis_info_psy *vi=p->vi; + int partition=vi->normal_partition; + int start=vi->normal_start; + + if(start>n)start=n; + + if(vi->normal_channel_p){ + for(;j=.25f){ + out[k]=rint(in[k]); + acc-=in[k]*in[k]; + flag=1; + }else{ + if(accnormal_thresh)break; + out[k]=unitnorm(in[k]); + acc-=1.; + } + } + + for(;in; + + /* perform any requested channel coupling */ + /* point stereo can only be used in a first stage (in this encoder) + because of the dependency on floor lookups */ + for(i=0;icoupling_steps;i++){ + + /* once we're doing multistage coupling in which a channel goes + through more than one coupling step, the floor vector + magnitudes will also have to be recalculated an propogated + along with PCM. Right now, we're not (that will wait until 5.1 + most likely), so the code isn't here yet. The memory management + here is all assuming single depth couplings anyway. */ + + /* make sure coupling a zero and a nonzero channel results in two + nonzero channels. */ + if(nonzero[vi->coupling_mag[i]] || + nonzero[vi->coupling_ang[i]]){ + + + float *rM=res[vi->coupling_mag[i]]; + float *rA=res[vi->coupling_ang[i]]; + float *qM=rM+n; + float *qA=rA+n; + int *floorM=ifloor[vi->coupling_mag[i]]; + int *floorA=ifloor[vi->coupling_ang[i]]; + float prepoint=stereo_threshholds[g->coupling_prepointamp[blobno]]; + float postpoint=stereo_threshholds[g->coupling_postpointamp[blobno]]; + int partition=(p->vi->normal_point_p?p->vi->normal_partition:p->n); + int limit=g->coupling_pointlimit[p->vi->blockflag][blobno]; + int pointlimit=limit; + + nonzero[vi->coupling_mag[i]]=1; + nonzero[vi->coupling_ang[i]]=1; + + /* The threshold of a stereo is changed with the size of n */ + if(n > 1000) + postpoint=stereo_threshholds_limited[g->coupling_postpointamp[blobno]]; + + for(j=0;jn;j+=partition){ + float acc=0.f; + + for(k=0;k=limit && fabs(rM[l])vi->normal_point_p){ + for(k=0;k=p->vi->normal_thresh;k++){ + int l=mag_sort[i][j+k]; + if(l=pointlimit && rint(qM[l])==0.f){ + qM[l]=unitnorm(qM[l]); + acc-=1.f; + } + } + } + } + } + } +} + +/* AoTuV */ +/** @ M2 ** + The boost problem by the combination of noise normalization and point stereo is eased. + However, this is a temporary patch. + by Aoyumi @ 2004/04/18 +*/ + +void hf_reduction(vorbis_info_psy_global *g, + vorbis_look_psy *p, + vorbis_info_mapping0 *vi, + float **mdct){ + + int i,j,n=p->n, de=0.3*p->m_val; + int limit=g->coupling_pointlimit[p->vi->blockflag][PACKETBLOBS/2]; + int start=p->vi->normal_start; + + for(i=0; icoupling_steps; i++){ + /* for(j=start; j +#include +#include +#include +#include "vorbis/codec.h" +#include "codec_internal.h" +#include "registry.h" +#include "codebook.h" +#include "misc.h" +#include "os.h" + +#if defined(TRAIN_RES) || defined (TRAIN_RESAUX) +#include +#endif + +typedef struct { + vorbis_info_residue0 *info; + + int parts; + int stages; + codebook *fullbooks; + codebook *phrasebook; + codebook ***partbooks; + + int partvals; + int **decodemap; + + long postbits; + long phrasebits; + long frames; + +#if defined(TRAIN_RES) || defined(TRAIN_RESAUX) + int train_seq; + long *training_data[8][64]; + float training_max[8][64]; + float training_min[8][64]; + float tmin; + float tmax; +#endif + +} vorbis_look_residue0; + +void res0_free_info(vorbis_info_residue *i){ + vorbis_info_residue0 *info=(vorbis_info_residue0 *)i; + if(info){ + memset(info,0,sizeof(*info)); + _ogg_free(info); + } +} + +void res0_free_look(vorbis_look_residue *i){ + int j; + if(i){ + + vorbis_look_residue0 *look=(vorbis_look_residue0 *)i; + +#ifdef TRAIN_RES + { + int j,k,l; + for(j=0;jparts;j++){ + /*fprintf(stderr,"partition %d: ",j);*/ + for(k=0;k<8;k++) + if(look->training_data[k][j]){ + char buffer[80]; + FILE *of; + codebook *statebook=look->partbooks[j][k]; + + /* long and short into the same bucket by current convention */ + sprintf(buffer,"res_part%d_pass%d.vqd",j,k); + of=fopen(buffer,"a"); + + for(l=0;lentries;l++) + fprintf(of,"%d:%ld\n",l,look->training_data[k][j][l]); + + fclose(of); + + /*fprintf(stderr,"%d(%.2f|%.2f) ",k, + look->training_min[k][j],look->training_max[k][j]);*/ + + _ogg_free(look->training_data[k][j]); + look->training_data[k][j]=NULL; + } + /*fprintf(stderr,"\n");*/ + } + } + fprintf(stderr,"min/max residue: %g::%g\n",look->tmin,look->tmax); + + /*fprintf(stderr,"residue bit usage %f:%f (%f total)\n", + (float)look->phrasebits/look->frames, + (float)look->postbits/look->frames, + (float)(look->postbits+look->phrasebits)/look->frames);*/ +#endif + + + /*vorbis_info_residue0 *info=look->info; + + fprintf(stderr, + "%ld frames encoded in %ld phrasebits and %ld residue bits " + "(%g/frame) \n",look->frames,look->phrasebits, + look->resbitsflat, + (look->phrasebits+look->resbitsflat)/(float)look->frames); + + for(j=0;jparts;j++){ + long acc=0; + fprintf(stderr,"\t[%d] == ",j); + for(k=0;kstages;k++) + if((info->secondstages[j]>>k)&1){ + fprintf(stderr,"%ld,",look->resbits[j][k]); + acc+=look->resbits[j][k]; + } + + fprintf(stderr,":: (%ld vals) %1.2fbits/sample\n",look->resvals[j], + acc?(float)acc/(look->resvals[j]*info->grouping):0); + } + fprintf(stderr,"\n");*/ + + for(j=0;jparts;j++) + if(look->partbooks[j])_ogg_free(look->partbooks[j]); + _ogg_free(look->partbooks); + for(j=0;jpartvals;j++) + _ogg_free(look->decodemap[j]); + _ogg_free(look->decodemap); + + memset(look,0,sizeof(*look)); + _ogg_free(look); + } +} + +static int ilog(unsigned int v){ + int ret=0; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +static int icount(unsigned int v){ + int ret=0; + while(v){ + ret+=v&1; + v>>=1; + } + return(ret); +} + + +void res0_pack(vorbis_info_residue *vr,oggpack_buffer *opb){ + vorbis_info_residue0 *info=(vorbis_info_residue0 *)vr; + int j,acc=0; + oggpack_write(opb,info->begin,24); + oggpack_write(opb,info->end,24); + + oggpack_write(opb,info->grouping-1,24); /* residue vectors to group and + code with a partitioned book */ + oggpack_write(opb,info->partitions-1,6); /* possible partition choices */ + oggpack_write(opb,info->groupbook,8); /* group huffman book */ + + /* secondstages is a bitmask; as encoding progresses pass by pass, a + bitmask of one indicates this partition class has bits to write + this pass */ + for(j=0;jpartitions;j++){ + if(ilog(info->secondstages[j])>3){ + /* yes, this is a minor hack due to not thinking ahead */ + oggpack_write(opb,info->secondstages[j],3); + oggpack_write(opb,1,1); + oggpack_write(opb,info->secondstages[j]>>3,5); + }else + oggpack_write(opb,info->secondstages[j],4); /* trailing zero */ + acc+=icount(info->secondstages[j]); + } + for(j=0;jbooklist[j],8); + +} + +/* vorbis_info is for range checking */ +vorbis_info_residue *res0_unpack(vorbis_info *vi,oggpack_buffer *opb){ + int j,acc=0; + vorbis_info_residue0 *info=_ogg_calloc(1,sizeof(*info)); + codec_setup_info *ci=vi->codec_setup; + + info->begin=oggpack_read(opb,24); + info->end=oggpack_read(opb,24); + info->grouping=oggpack_read(opb,24)+1; + info->partitions=oggpack_read(opb,6)+1; + info->groupbook=oggpack_read(opb,8); + + for(j=0;jpartitions;j++){ + int cascade=oggpack_read(opb,3); + if(oggpack_read(opb,1)) + cascade|=(oggpack_read(opb,5)<<3); + info->secondstages[j]=cascade; + + acc+=icount(cascade); + } + for(j=0;jbooklist[j]=oggpack_read(opb,8); + + if(info->groupbook>=ci->books)goto errout; + for(j=0;jbooklist[j]>=ci->books)goto errout; + + return(info); + errout: + res0_free_info(info); + return(NULL); +} + +vorbis_look_residue *res0_look(vorbis_dsp_state *vd, + vorbis_info_residue *vr){ + vorbis_info_residue0 *info=(vorbis_info_residue0 *)vr; + vorbis_look_residue0 *look=_ogg_calloc(1,sizeof(*look)); + codec_setup_info *ci=vd->vi->codec_setup; + + int j,k,acc=0; + int dim; + int maxstage=0; + look->info=info; + + look->parts=info->partitions; + look->fullbooks=ci->fullbooks; + look->phrasebook=ci->fullbooks+info->groupbook; + dim=look->phrasebook->dim; + + look->partbooks=_ogg_calloc(look->parts,sizeof(*look->partbooks)); + + for(j=0;jparts;j++){ + int stages=ilog(info->secondstages[j]); + if(stages){ + if(stages>maxstage)maxstage=stages; + look->partbooks[j]=_ogg_calloc(stages,sizeof(*look->partbooks[j])); + for(k=0;ksecondstages[j]&(1<partbooks[j][k]=ci->fullbooks+info->booklist[acc++]; +#ifdef TRAIN_RES + look->training_data[k][j]=_ogg_calloc(look->partbooks[j][k]->entries, + sizeof(***look->training_data)); +#endif + } + } + } + + look->partvals=rint(pow((float)look->parts,(float)dim)); + look->stages=maxstage; + look->decodemap=_ogg_malloc(look->partvals*sizeof(*look->decodemap)); + for(j=0;jpartvals;j++){ + long val=j; + long mult=look->partvals/look->parts; + look->decodemap[j]=_ogg_malloc(dim*sizeof(*look->decodemap[j])); + for(k=0;kparts; + look->decodemap[j][k]=deco; + } + } +#if defined(TRAIN_RES) || defined (TRAIN_RESAUX) + { + static int train_seq=0; + look->train_seq=train_seq++; + } +#endif + return(look); +} + +/* break an abstraction and copy some code for performance purposes */ +static int local_book_besterror(codebook *book,float *a){ + int dim=book->dim,i,k,o; + int best=0; + encode_aux_threshmatch *tt=book->c->thresh_tree; + + /* find the quant val of each scalar */ + for(k=0,o=dim;kthreshvals>>1; + + if(valquantthresh[i]){ + if(valquantthresh[i-1]){ + for(--i;i>0;--i) + if(val>=tt->quantthresh[i-1]) + break; + } + }else{ + + for(++i;ithreshvals-1;++i) + if(valquantthresh[i])break; + + } + + best=(best*tt->quantvals)+tt->quantmap[i]; + } + /* regular lattices are easy :-) */ + + if(book->c->lengthlist[best]<=0){ + const static_codebook *c=book->c; + int i,j; + float bestf=0.f; + float *e=book->valuelist; + best=-1; + for(i=0;ientries;i++){ + if(c->lengthlist[i]>0){ + float this=0.f; + for(j=0;j-1){ + float *ptr=book->valuelist+best*dim; + for(i=0;idim; + int step=n/dim; + + for(i=0;i0) + acc[entry]++; +#endif + + bits+=vorbis_book_encode(book,entry,opb); + + } + + return(bits); +} + +static long **_01class(vorbis_block *vb,vorbis_look_residue *vl, + float **in,int ch){ + long i,j,k; + vorbis_look_residue0 *look=(vorbis_look_residue0 *)vl; + vorbis_info_residue0 *info=look->info; + vorbis_info *vi=vb->vd->vi; + codec_setup_info *ci=vi->codec_setup; + + /* move all this setup out later */ + int samples_per_partition=info->grouping; + int possible_partitions=info->partitions; + int n=info->end-info->begin; + + int partvals=n/samples_per_partition; + long **partword=_vorbis_block_alloc(vb,ch*sizeof(*partword)); + float scale=100./samples_per_partition; + + /* we find the partition type for each partition of each + channel. We'll go back and do the interleaved encoding in a + bit. For now, clarity */ + + for(i=0;ibegin; + for(j=0;jmax)max=fabs(in[j][offset+k]); + ent+=fabs(rint(in[j][offset+k])); + } + ent*=scale; + + for(k=0;kclassmetric1[k] && + (info->classmetric2[k]<0 || (int)entclassmetric2[k])) + break; + + partword[j][i]=k; + } + } + +#ifdef TRAIN_RESAUX + { + FILE *of; + char buffer[80]; + + for(i=0;itrain_seq); + of=fopen(buffer,"a"); + for(j=0;jframes++; + + return(partword); +} + +/* designed for stereo or other modes where the partition size is an + integer multiple of the number of channels encoded in the current + submap */ +static long **_2class(vorbis_block *vb,vorbis_look_residue *vl,float **in, + int ch){ + long i,j,k,l; + vorbis_look_residue0 *look=(vorbis_look_residue0 *)vl; + vorbis_info_residue0 *info=look->info; + + /* move all this setup out later */ + int samples_per_partition=info->grouping; + int possible_partitions=info->partitions; + int n=info->end-info->begin; + + int partvals=n/samples_per_partition; + long **partword=_vorbis_block_alloc(vb,sizeof(*partword)); + +#if defined(TRAIN_RES) || defined (TRAIN_RESAUX) + FILE *of; + char buffer[80]; +#endif + + partword[0]=_vorbis_block_alloc(vb,n*ch/samples_per_partition*sizeof(*partword[0])); + memset(partword[0],0,n*ch/samples_per_partition*sizeof(*partword[0])); + + for(i=0,l=info->begin/ch;imagmax)magmax=fabs(in[0][l]); + for(k=1;kangmax)angmax=fabs(in[k][l]); + l++; + } + + for(j=0;jclassmetric1[j] && + angmax<=info->classmetric2[j]) + break; + + partword[0][i]=j; + + } + +#ifdef TRAIN_RESAUX + sprintf(buffer,"resaux_%d.vqd",look->train_seq); + of=fopen(buffer,"a"); + for(i=0;iframes++; + + return(partword); +} + +static int _01forward(oggpack_buffer *opb, + vorbis_block *vb,vorbis_look_residue *vl, + float **in,int ch, + long **partword, + int (*encode)(oggpack_buffer *,float *,int, + codebook *,long *)){ + long i,j,k,s; + vorbis_look_residue0 *look=(vorbis_look_residue0 *)vl; + vorbis_info_residue0 *info=look->info; + + vorbis_dsp_state *vd=vb->vd; + + /* move all this setup out later */ + int samples_per_partition=info->grouping; + int possible_partitions=info->partitions; + int partitions_per_word=look->phrasebook->dim; + int n=info->end-info->begin; + + int partvals=n/samples_per_partition; + long resbits[128]; + long resvals[128]; + +#ifdef TRAIN_RES + for(i=0;ibegin;jlook->tmax)look->tmax=in[i][j]; + if(in[i][j]tmin)look->tmin=in[i][j]; + } +#endif + + memset(resbits,0,sizeof(resbits)); + memset(resvals,0,sizeof(resvals)); + + /* we code the partition words for each channel, then the residual + words for a partition per channel until we've written all the + residual words for that partition word. Then write the next + partition channel words... */ + + for(s=0;sstages;s++){ + + for(i=0;iphrasebook->entries) + look->phrasebits+=vorbis_book_encode(look->phrasebook,val,opb); +#if 0 /*def TRAIN_RES*/ + else + fprintf(stderr,"!"); +#endif + + } + } + + /* now we encode interleaved residual values for the partitions */ + for(k=0;kbegin; + + for(j=0;jsecondstages[partword[j][i]]&(1<partbooks[partword[j][i]][s]; + if(statebook){ + int ret; + long *accumulator=NULL; + +#ifdef TRAIN_RES + accumulator=look->training_data[s][partword[j][i]]; + { + int l; + float *samples=in[j]+offset; + for(l=0;ltraining_min[s][partword[j][i]]) + look->training_min[s][partword[j][i]]=samples[l]; + if(samples[l]>look->training_max[s][partword[j][i]]) + look->training_max[s][partword[j][i]]=samples[l]; + } + } +#endif + + ret=encode(opb,in[j]+offset,samples_per_partition, + statebook,accumulator); + + look->postbits+=ret; + resbits[partword[j][i]]+=ret; + } + } + } + } + } + } + + /*{ + long total=0; + long totalbits=0; + fprintf(stderr,"%d :: ",vb->mode); + for(k=0;kinfo; + + /* move all this setup out later */ + int samples_per_partition=info->grouping; + int partitions_per_word=look->phrasebook->dim; + int max=vb->pcmend>>1; + int end=(info->endend:max); + int n=end-info->begin; + + if(n>0){ + int partvals=n/samples_per_partition; + int partwords=(partvals+partitions_per_word-1)/partitions_per_word; + int ***partword=alloca(ch*sizeof(*partword)); + + for(j=0;jstages;s++){ + + /* each loop decodes on partition codeword containing + partitions_per_word partitions */ + for(i=0,l=0;iphrasebook,&vb->opb); + + if(temp==-1)goto eopbreak; + partword[j][l]=look->decodemap[temp]; + if(partword[j][l]==NULL)goto errout; + } + } + + /* now we decode residual values for the partitions */ + for(k=0;kbegin+i*samples_per_partition; + if(info->secondstages[partword[j][l][k]]&(1<partbooks[partword[j][l][k]][s]; + if(stagebook){ + if(decodepart(stagebook,in[j]+offset,&vb->opb, + samples_per_partition)==-1)goto eopbreak; + } + } + } + } + } + } + errout: + eopbreak: + return(0); +} + +#if 0 +/* residue 0 and 1 are just slight variants of one another. 0 is + interleaved, 1 is not */ +long **res0_class(vorbis_block *vb,vorbis_look_residue *vl, + float **in,int *nonzero,int ch){ + /* we encode only the nonzero parts of a bundle */ + int i,used=0; + for(i=0;ipcmend/2; + for(i=0;ipcmend/2; + for(i=0;ipcmend/2,used=0; + + /* don't duplicate the code; use a working vector hack for now and + reshape ourselves into a single channel res1 */ + /* ugly; reallocs for each coupling pass :-( */ + float *work=_vorbis_block_alloc(vb,ch*n*sizeof(*work)); + for(i=0;iinfo; + + /* move all this setup out later */ + int samples_per_partition=info->grouping; + int partitions_per_word=look->phrasebook->dim; + int max=(vb->pcmend*ch)>>1; + int end=(info->endend:max); + int n=end-info->begin; + + if(n>0){ + int partvals=n/samples_per_partition; + int partwords=(partvals+partitions_per_word-1)/partitions_per_word; + int **partword=_vorbis_block_alloc(vb,partwords*sizeof(*partword)); + + for(i=0;istages;s++){ + for(i=0,l=0;iphrasebook,&vb->opb); + if(temp==-1)goto eopbreak; + partword[l]=look->decodemap[temp]; + if(partword[l]==NULL)goto errout; + } + + /* now we decode residual values for the partitions */ + for(k=0;ksecondstages[partword[l][k]]&(1<partbooks[partword[l][k]][s]; + + if(stagebook){ + if(vorbis_book_decodevv_add(stagebook,in, + i*samples_per_partition+info->begin,ch, + &vb->opb,samples_per_partition)==-1) + goto eopbreak; + } + } + } + } + } + errout: + eopbreak: + return(0); +} + + +vorbis_func_residue residue0_exportbundle={ + NULL, + &res0_unpack, + &res0_look, + &res0_free_info, + &res0_free_look, + NULL, + NULL, + &res0_inverse +}; + +vorbis_func_residue residue1_exportbundle={ + &res0_pack, + &res0_unpack, + &res0_look, + &res0_free_info, + &res0_free_look, + &res1_class, + &res1_forward, + &res1_inverse +}; + +vorbis_func_residue residue2_exportbundle={ + &res0_pack, + &res0_unpack, + &res0_look, + &res0_free_info, + &res0_free_look, + &res2_class, + &res2_forward, + &res2_inverse +}; + diff --git a/Engine/lib/libvorbis/lib/scales.h b/Engine/lib/libvorbis/lib/scales.h new file mode 100644 index 000000000..4708b2365 --- /dev/null +++ b/Engine/lib/libvorbis/lib/scales.h @@ -0,0 +1,86 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: linear scale -> dB, Bark and Mel scales + last mod: $Id: scales.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#ifndef _V_SCALES_H_ +#define _V_SCALES_H_ + +#include +#include "os.h" + +/* 20log10(x) */ +#define VORBIS_IEEE_FLOAT32 1 +#ifdef VORBIS_IEEE_FLOAT32 + +static float unitnorm(float x){ + union { + ogg_uint32_t i; + float f; + } ix; + ix.f = x; + ix.i = (ix.i & 0x80000000U) | (0x3f800000U); + return ix.f; +} + +/* Segher was off (too high) by ~ .3 decibel. Center the conversion correctly. */ +static float todB(const float *x){ + union { + ogg_uint32_t i; + float f; + } ix; + ix.f = *x; + ix.i = ix.i&0x7fffffff; + return (float)(ix.i * 7.17711438e-7f -764.6161886f); +} + +#define todB_nn(x) todB(x) + +#else + +static float unitnorm(float x){ + if(x<0)return(-1.f); + return(1.f); +} + +#define todB(x) (*(x)==0?-400.f:log(*(x)**(x))*4.34294480f) +#define todB_nn(x) (*(x)==0.f?-400.f:log(*(x))*8.6858896f) + +#endif + +#define fromdB(x) (exp((x)*.11512925f)) + +/* The bark scale equations are approximations, since the original + table was somewhat hand rolled. The below are chosen to have the + best possible fit to the rolled tables, thus their somewhat odd + appearance (these are more accurate and over a longer range than + the oft-quoted bark equations found in the texts I have). The + approximations are valid from 0 - 30kHz (nyquist) or so. + + all f in Hz, z in Bark */ + +#define toBARK(n) (13.1f*atan(.00074f*(n))+2.24f*atan((n)*(n)*1.85e-8f)+1e-4f*(n)) +#define fromBARK(z) (102.f*(z)-2.f*pow(z,2.f)+.4f*pow(z,3.f)+pow(1.46f,z)-1.f) +#define toMEL(n) (log(1.f+(n)*.001f)*1442.695f) +#define fromMEL(m) (1000.f*exp((m)/1442.695f)-1000.f) + +/* Frequency to octave. We arbitrarily declare 63.5 Hz to be octave + 0.0 */ + +#define toOC(n) (log(n)*1.442695f-5.965784f) +#define fromOC(o) (exp(((o)+5.965784f)*.693147f)) + +#endif + diff --git a/Engine/lib/libvorbis/lib/sharedbook.c b/Engine/lib/libvorbis/lib/sharedbook.c new file mode 100644 index 000000000..1bf4140ed --- /dev/null +++ b/Engine/lib/libvorbis/lib/sharedbook.c @@ -0,0 +1,735 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: basic shared codebook operations + last mod: $Id: sharedbook.c 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#include +#include +#include +#include +#include "os.h" +#include "misc.h" +#include "vorbis/codec.h" +#include "codebook.h" +#include "scales.h" + +/**** pack/unpack helpers ******************************************/ +int _ilog(unsigned int v){ + int ret=0; + while(v){ + ret++; + v>>=1; + } + return(ret); +} + +/* 32 bit float (not IEEE; nonnormalized mantissa + + biased exponent) : neeeeeee eeemmmmm mmmmmmmm mmmmmmmm + Why not IEEE? It's just not that important here. */ + +#define VQ_FEXP 10 +#define VQ_FMAN 21 +#define VQ_FEXP_BIAS 768 /* bias toward values smaller than 1. */ + +/* doesn't currently guard under/overflow */ +long _float32_pack(float val){ + int sign=0; + long exp; + long mant; + if(val<0){ + sign=0x80000000; + val= -val; + } + exp= floor(log(val)/log(2.f)); + mant=rint(ldexp(val,(VQ_FMAN-1)-exp)); + exp=(exp+VQ_FEXP_BIAS)<>VQ_FMAN; + if(sign)mant= -mant; + return(ldexp(mant,exp-(VQ_FMAN-1)-VQ_FEXP_BIAS)); +} + +/* given a list of word lengths, generate a list of codewords. Works + for length ordered or unordered, always assigns the lowest valued + codewords first. Extended to handle unused entries (length 0) */ +ogg_uint32_t *_make_words(long *l,long n,long sparsecount){ + long i,j,count=0; + ogg_uint32_t marker[33]; + ogg_uint32_t *r=_ogg_malloc((sparsecount?sparsecount:n)*sizeof(*r)); + memset(marker,0,sizeof(marker)); + + for(i=0;i0){ + ogg_uint32_t entry=marker[length]; + + /* when we claim a node for an entry, we also claim the nodes + below it (pruning off the imagined tree that may have dangled + from it) as well as blocking the use of any nodes directly + above for leaves */ + + /* update ourself */ + if(length<32 && (entry>>length)){ + /* error condition; the lengths must specify an overpopulated tree */ + _ogg_free(r); + return(NULL); + } + r[count++]=entry; + + /* Look to see if the next shorter marker points to the node + above. if so, update it and repeat. */ + { + for(j=length;j>0;j--){ + + if(marker[j]&1){ + /* have to jump branches */ + if(j==1) + marker[1]++; + else + marker[j]=marker[j-1]<<1; + break; /* invariant says next upper marker would already + have been moved if it was on the same path */ + } + marker[j]++; + } + } + + /* prune the tree; the implicit invariant says all the longer + markers were dangling from our just-taken node. Dangle them + from our *new* node. */ + for(j=length+1;j<33;j++) + if((marker[j]>>1) == entry){ + entry=marker[j]; + marker[j]=marker[j-1]<<1; + }else + break; + }else + if(sparsecount==0)count++; + } + + /* bitreverse the words because our bitwise packer/unpacker is LSb + endian */ + for(i=0,count=0;i>j)&1; + } + + if(sparsecount){ + if(l[i]) + r[count++]=temp; + }else + r[count++]=temp; + } + + return(r); +} + +/* there might be a straightforward one-line way to do the below + that's portable and totally safe against roundoff, but I haven't + thought of it. Therefore, we opt on the side of caution */ +long _book_maptype1_quantvals(const static_codebook *b){ + long vals=floor(pow((float)b->entries,1.f/b->dim)); + + /* the above *should* be reliable, but we'll not assume that FP is + ever reliable when bitstream sync is at stake; verify via integer + means that vals really is the greatest value of dim for which + vals^b->bim <= b->entries */ + /* treat the above as an initial guess */ + while(1){ + long acc=1; + long acc1=1; + int i; + for(i=0;idim;i++){ + acc*=vals; + acc1*=vals+1; + } + if(acc<=b->entries && acc1>b->entries){ + return(vals); + }else{ + if(acc>b->entries){ + vals--; + }else{ + vals++; + } + } + } +} + +/* unpack the quantized list of values for encode/decode ***********/ +/* we need to deal with two map types: in map type 1, the values are + generated algorithmically (each column of the vector counts through + the values in the quant vector). in map type 2, all the values came + in in an explicit list. Both value lists must be unpacked */ +float *_book_unquantize(const static_codebook *b,int n,int *sparsemap){ + long j,k,count=0; + if(b->maptype==1 || b->maptype==2){ + int quantvals; + float mindel=_float32_unpack(b->q_min); + float delta=_float32_unpack(b->q_delta); + float *r=_ogg_calloc(n*b->dim,sizeof(*r)); + + /* maptype 1 and 2 both use a quantized value vector, but + different sizes */ + switch(b->maptype){ + case 1: + /* most of the time, entries%dimensions == 0, but we need to be + well defined. We define that the possible vales at each + scalar is values == entries/dim. If entries%dim != 0, we'll + have 'too few' values (values*dimentries;j++){ + if((sparsemap && b->lengthlist[j]) || !sparsemap){ + float last=0.f; + int indexdiv=1; + for(k=0;kdim;k++){ + int index= (j/indexdiv)%quantvals; + float val=b->quantlist[index]; + val=fabs(val)*delta+mindel+last; + if(b->q_sequencep)last=val; + if(sparsemap) + r[sparsemap[count]*b->dim+k]=val; + else + r[count*b->dim+k]=val; + indexdiv*=quantvals; + } + count++; + } + + } + break; + case 2: + for(j=0;jentries;j++){ + if((sparsemap && b->lengthlist[j]) || !sparsemap){ + float last=0.f; + + for(k=0;kdim;k++){ + float val=b->quantlist[j*b->dim+k]; + val=fabs(val)*delta+mindel+last; + if(b->q_sequencep)last=val; + if(sparsemap) + r[sparsemap[count]*b->dim+k]=val; + else + r[count*b->dim+k]=val; + } + count++; + } + } + break; + } + + return(r); + } + return(NULL); +} + +void vorbis_staticbook_clear(static_codebook *b){ + if(b->allocedp){ + if(b->quantlist)_ogg_free(b->quantlist); + if(b->lengthlist)_ogg_free(b->lengthlist); + if(b->nearest_tree){ + _ogg_free(b->nearest_tree->ptr0); + _ogg_free(b->nearest_tree->ptr1); + _ogg_free(b->nearest_tree->p); + _ogg_free(b->nearest_tree->q); + memset(b->nearest_tree,0,sizeof(*b->nearest_tree)); + _ogg_free(b->nearest_tree); + } + if(b->thresh_tree){ + _ogg_free(b->thresh_tree->quantthresh); + _ogg_free(b->thresh_tree->quantmap); + memset(b->thresh_tree,0,sizeof(*b->thresh_tree)); + _ogg_free(b->thresh_tree); + } + + memset(b,0,sizeof(*b)); + } +} + +void vorbis_staticbook_destroy(static_codebook *b){ + if(b->allocedp){ + vorbis_staticbook_clear(b); + _ogg_free(b); + } +} + +void vorbis_book_clear(codebook *b){ + /* static book is not cleared; we're likely called on the lookup and + the static codebook belongs to the info struct */ + if(b->valuelist)_ogg_free(b->valuelist); + if(b->codelist)_ogg_free(b->codelist); + + if(b->dec_index)_ogg_free(b->dec_index); + if(b->dec_codelengths)_ogg_free(b->dec_codelengths); + if(b->dec_firsttable)_ogg_free(b->dec_firsttable); + + memset(b,0,sizeof(*b)); +} + +int vorbis_book_init_encode(codebook *c,const static_codebook *s){ + + memset(c,0,sizeof(*c)); + c->c=s; + c->entries=s->entries; + c->used_entries=s->entries; + c->dim=s->dim; + c->codelist=_make_words(s->lengthlist,s->entries,0); + c->valuelist=_book_unquantize(s,s->entries,NULL); + + return(0); +} + +static ogg_uint32_t bitreverse(ogg_uint32_t x){ + x= ((x>>16)&0x0000ffffUL) | ((x<<16)&0xffff0000UL); + x= ((x>> 8)&0x00ff00ffUL) | ((x<< 8)&0xff00ff00UL); + x= ((x>> 4)&0x0f0f0f0fUL) | ((x<< 4)&0xf0f0f0f0UL); + x= ((x>> 2)&0x33333333UL) | ((x<< 2)&0xccccccccUL); + return((x>> 1)&0x55555555UL) | ((x<< 1)&0xaaaaaaaaUL); +} + +static int sort32a(const void *a,const void *b){ + return ( **(ogg_uint32_t **)a>**(ogg_uint32_t **)b)- + ( **(ogg_uint32_t **)a<**(ogg_uint32_t **)b); +} + +/* decode codebook arrangement is more heavily optimized than encode */ +int vorbis_book_init_decode(codebook *c,const static_codebook *s){ + int i,j,n=0,tabn; + int *sortindex; + memset(c,0,sizeof(*c)); + + /* count actually used entries */ + for(i=0;ientries;i++) + if(s->lengthlist[i]>0) + n++; + + c->entries=s->entries; + c->used_entries=n; + c->dim=s->dim; + + if(n>0){ + + /* two different remappings go on here. + + First, we collapse the likely sparse codebook down only to + actually represented values/words. This collapsing needs to be + indexed as map-valueless books are used to encode original entry + positions as integers. + + Second, we reorder all vectors, including the entry index above, + by sorted bitreversed codeword to allow treeless decode. */ + + /* perform sort */ + ogg_uint32_t *codes=_make_words(s->lengthlist,s->entries,c->used_entries); + ogg_uint32_t **codep=alloca(sizeof(*codep)*n); + + if(codes==NULL)goto err_out; + + for(i=0;icodelist=_ogg_malloc(n*sizeof(*c->codelist)); + /* the index is a reverse index */ + for(i=0;icodelist[sortindex[i]]=codes[i]; + _ogg_free(codes); + + + c->valuelist=_book_unquantize(s,n,sortindex); + c->dec_index=_ogg_malloc(n*sizeof(*c->dec_index)); + + for(n=0,i=0;ientries;i++) + if(s->lengthlist[i]>0) + c->dec_index[sortindex[n++]]=i; + + c->dec_codelengths=_ogg_malloc(n*sizeof(*c->dec_codelengths)); + for(n=0,i=0;ientries;i++) + if(s->lengthlist[i]>0) + c->dec_codelengths[sortindex[n++]]=s->lengthlist[i]; + + c->dec_firsttablen=_ilog(c->used_entries)-4; /* this is magic */ + if(c->dec_firsttablen<5)c->dec_firsttablen=5; + if(c->dec_firsttablen>8)c->dec_firsttablen=8; + + tabn=1<dec_firsttablen; + c->dec_firsttable=_ogg_calloc(tabn,sizeof(*c->dec_firsttable)); + c->dec_maxlength=0; + + for(i=0;idec_maxlengthdec_codelengths[i]) + c->dec_maxlength=c->dec_codelengths[i]; + if(c->dec_codelengths[i]<=c->dec_firsttablen){ + ogg_uint32_t orig=bitreverse(c->codelist[i]); + for(j=0;j<(1<<(c->dec_firsttablen-c->dec_codelengths[i]));j++) + c->dec_firsttable[orig|(j<dec_codelengths[i])]=i+1; + } + } + + /* now fill in 'unused' entries in the firsttable with hi/lo search + hints for the non-direct-hits */ + { + ogg_uint32_t mask=0xfffffffeUL<<(31-c->dec_firsttablen); + long lo=0,hi=0; + + for(i=0;idec_firsttablen); + if(c->dec_firsttable[bitreverse(word)]==0){ + while((lo+1)codelist[lo+1]<=word)lo++; + while( hi=(c->codelist[hi]&mask))hi++; + + /* we only actually have 15 bits per hint to play with here. + In order to overflow gracefully (nothing breaks, efficiency + just drops), encode as the difference from the extremes. */ + { + unsigned long loval=lo; + unsigned long hival=n-hi; + + if(loval>0x7fff)loval=0x7fff; + if(hival>0x7fff)hival=0x7fff; + c->dec_firsttable[bitreverse(word)]= + 0x80000000UL | (loval<<15) | hival; + } + } + } + } + } + + return(0); + err_out: + vorbis_book_clear(c); + return(-1); +} + +static float _dist(int el,float *ref, float *b,int step){ + int i; + float acc=0.f; + for(i=0;ic->thresh_tree; + +#if 0 + encode_aux_nearestmatch *nt=book->c->nearest_tree; + encode_aux_pigeonhole *pt=book->c->pigeon_tree; +#endif + int dim=book->dim; + int k,o; + /*int savebest=-1; + float saverr;*/ + + /* do we have a threshhold encode hint? */ + if(tt){ + int index=0,i; + /* find the quant val of each scalar */ + for(k=0,o=step*(dim-1);kthreshvals>>1; + if(a[o]quantthresh[i]){ + + for(;i>0;i--) + if(a[o]>=tt->quantthresh[i-1]) + break; + + }else{ + + for(i++;ithreshvals-1;i++) + if(a[o]quantthresh[i])break; + + } + + index=(index*tt->quantvals)+tt->quantmap[i]; + } + /* regular lattices are easy :-) */ + if(book->c->lengthlist[index]>0) /* is this unused? If so, we'll + use a decision tree after all + and fall through*/ + return(index); + } + +#if 0 + /* do we have a pigeonhole encode hint? */ + if(pt){ + const static_codebook *c=book->c; + int i,besti=-1; + float best=0.f; + int entry=0; + + /* dealing with sequentialness is a pain in the ass */ + if(c->q_sequencep){ + int pv; + long mul=1; + float qlast=0; + for(k=0,o=0;kmin)/pt->del); + if(pv<0 || pv>=pt->mapentries)break; + entry+=pt->pigeonmap[pv]*mul; + mul*=pt->quantvals; + qlast+=pv*pt->del+pt->min; + } + }else{ + for(k=0,o=step*(dim-1);kmin)/pt->del); + if(pv<0 || pv>=pt->mapentries)break; + entry=entry*pt->quantvals+pt->pigeonmap[pv]; + } + } + + /* must be within the pigeonholable range; if we quant outside (or + in an entry that we define no list for), brute force it */ + if(k==dim && pt->fitlength[entry]){ + /* search the abbreviated list */ + long *list=pt->fitlist+pt->fitmap[entry]; + for(i=0;ifitlength[entry];i++){ + float this=_dist(dim,book->valuelist+list[i]*dim,a,step); + if(besti==-1 || thisvaluelist+nt->p[ptr]; + float *q=book->valuelist+nt->q[ptr]; + + for(k=0,o=0;k0.f) /* in A */ + ptr= -nt->ptr0[ptr]; + else /* in B */ + ptr= -nt->ptr1[ptr]; + if(ptr<=0)break; + } + return(-ptr); + } +#endif + + /* brute force it! */ + { + const static_codebook *c=book->c; + int i,besti=-1; + float best=0.f; + float *e=book->valuelist; + for(i=0;ientries;i++){ + if(c->lengthlist[i]>0){ + float this=_dist(dim,e,a,step); + if(besti==-1 || thisvaluelist+savebest*dim)[i]); + fprintf(stderr,"\n" + "bruteforce (entry %d, err %g):",besti,best); + for(i=0;ivaluelist+besti*dim)[i]); + fprintf(stderr,"\n"); + }*/ + return(besti); + } +} + +long vorbis_book_codeword(codebook *book,int entry){ + if(book->c) /* only use with encode; decode optimizations are + allowed to break this */ + return book->codelist[entry]; + return -1; +} + +long vorbis_book_codelen(codebook *book,int entry){ + if(book->c) /* only use with encode; decode optimizations are + allowed to break this */ + return book->c->lengthlist[entry]; + return -1; +} + +#ifdef _V_SELFTEST + +/* Unit tests of the dequantizer; this stuff will be OK + cross-platform, I simply want to be sure that special mapping cases + actually work properly; a bug could go unnoticed for a while */ + +#include + +/* cases: + + no mapping + full, explicit mapping + algorithmic mapping + + nonsequential + sequential +*/ + +static long full_quantlist1[]={0,1,2,3, 4,5,6,7, 8,3,6,1}; +static long partial_quantlist1[]={0,7,2}; + +/* no mapping */ +static_codebook test1={ + 4,16, + NULL, + 0, + 0,0,0,0, + NULL, + NULL,NULL +}; +static float *test1_result=NULL; + +/* linear, full mapping, nonsequential */ +static_codebook test2={ + 4,3, + NULL, + 2, + -533200896,1611661312,4,0, + full_quantlist1, + NULL,NULL +}; +static float test2_result[]={-3,-2,-1,0, 1,2,3,4, 5,0,3,-2}; + +/* linear, full mapping, sequential */ +static_codebook test3={ + 4,3, + NULL, + 2, + -533200896,1611661312,4,1, + full_quantlist1, + NULL,NULL +}; +static float test3_result[]={-3,-5,-6,-6, 1,3,6,10, 5,5,8,6}; + +/* linear, algorithmic mapping, nonsequential */ +static_codebook test4={ + 3,27, + NULL, + 1, + -533200896,1611661312,4,0, + partial_quantlist1, + NULL,NULL +}; +static float test4_result[]={-3,-3,-3, 4,-3,-3, -1,-3,-3, + -3, 4,-3, 4, 4,-3, -1, 4,-3, + -3,-1,-3, 4,-1,-3, -1,-1,-3, + -3,-3, 4, 4,-3, 4, -1,-3, 4, + -3, 4, 4, 4, 4, 4, -1, 4, 4, + -3,-1, 4, 4,-1, 4, -1,-1, 4, + -3,-3,-1, 4,-3,-1, -1,-3,-1, + -3, 4,-1, 4, 4,-1, -1, 4,-1, + -3,-1,-1, 4,-1,-1, -1,-1,-1}; + +/* linear, algorithmic mapping, sequential */ +static_codebook test5={ + 3,27, + NULL, + 1, + -533200896,1611661312,4,1, + partial_quantlist1, + NULL,NULL +}; +static float test5_result[]={-3,-6,-9, 4, 1,-2, -1,-4,-7, + -3, 1,-2, 4, 8, 5, -1, 3, 0, + -3,-4,-7, 4, 3, 0, -1,-2,-5, + -3,-6,-2, 4, 1, 5, -1,-4, 0, + -3, 1, 5, 4, 8,12, -1, 3, 7, + -3,-4, 0, 4, 3, 7, -1,-2, 2, + -3,-6,-7, 4, 1, 0, -1,-4,-5, + -3, 1, 0, 4, 8, 7, -1, 3, 2, + -3,-4,-5, 4, 3, 2, -1,-2,-3}; + +void run_test(static_codebook *b,float *comp){ + float *out=_book_unquantize(b,b->entries,NULL); + int i; + + if(comp){ + if(!out){ + fprintf(stderr,"_book_unquantize incorrectly returned NULL\n"); + exit(1); + } + + for(i=0;ientries*b->dim;i++) + if(fabs(out[i]-comp[i])>.0001){ + fprintf(stderr,"disagreement in unquantized and reference data:\n" + "position %d, %g != %g\n",i,out[i],comp[i]); + exit(1); + } + + }else{ + if(out){ + fprintf(stderr,"_book_unquantize returned a value array: \n" + " correct result should have been NULL\n"); + exit(1); + } + } +} + +int main(){ + /* run the nine dequant tests, and compare to the hand-rolled results */ + fprintf(stderr,"Dequant test 1... "); + run_test(&test1,test1_result); + fprintf(stderr,"OK\nDequant test 2... "); + run_test(&test2,test2_result); + fprintf(stderr,"OK\nDequant test 3... "); + run_test(&test3,test3_result); + fprintf(stderr,"OK\nDequant test 4... "); + run_test(&test4,test4_result); + fprintf(stderr,"OK\nDequant test 5... "); + run_test(&test5,test5_result); + fprintf(stderr,"OK\n\n"); + + return(0); +} + +#endif diff --git a/Engine/lib/libvorbis/lib/smallft.c b/Engine/lib/libvorbis/lib/smallft.c new file mode 100644 index 000000000..a78e92368 --- /dev/null +++ b/Engine/lib/libvorbis/lib/smallft.c @@ -0,0 +1,1255 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: *unnormalized* fft transform + last mod: $Id: smallft.c 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +/* FFT implementation from OggSquish, minus cosine transforms, + * minus all but radix 2/4 case. In Vorbis we only need this + * cut-down version. + * + * To do more than just power-of-two sized vectors, see the full + * version I wrote for NetLib. + * + * Note that the packing is a little strange; rather than the FFT r/i + * packing following R_0, I_n, R_1, I_1, R_2, I_2 ... R_n-1, I_n-1, + * it follows R_0, R_1, I_1, R_2, I_2 ... R_n-1, I_n-1, I_n like the + * FORTRAN version + */ + +#include +#include +#include +#include "smallft.h" +#include "os.h" +#include "misc.h" + +static void drfti1(int n, float *wa, int *ifac){ + static int ntryh[4] = { 4,2,3,5 }; + static float tpi = 6.28318530717958648f; + float arg,argh,argld,fi; + int ntry=0,i,j=-1; + int k1, l1, l2, ib; + int ld, ii, ip, is, nq, nr; + int ido, ipm, nfm1; + int nl=n; + int nf=0; + + L101: + j++; + if (j < 4) + ntry=ntryh[j]; + else + ntry+=2; + + L104: + nq=nl/ntry; + nr=nl-ntry*nq; + if (nr!=0) goto L101; + + nf++; + ifac[nf+1]=ntry; + nl=nq; + if(ntry!=2)goto L107; + if(nf==1)goto L107; + + for (i=1;i>1; + ipp2=ip; + idp2=ido; + nbd=(ido-1)>>1; + t0=l1*ido; + t10=ip*ido; + + if(ido==1)goto L119; + for(ik=0;ikl1){ + for(j=1;j>1; + ipp2=ip; + ipph=(ip+1)>>1; + if(idol1)goto L139; + + is= -ido-1; + t1=0; + for(j=1;jn==1)return; + drftf1(l->n,data,l->trigcache,l->trigcache+l->n,l->splitcache); +} + +void drft_backward(drft_lookup *l,float *data){ + if (l->n==1)return; + drftb1(l->n,data,l->trigcache,l->trigcache+l->n,l->splitcache); +} + +void drft_init(drft_lookup *l,int n){ + l->n=n; + l->trigcache=_ogg_calloc(3*n,sizeof(*l->trigcache)); + l->splitcache=_ogg_calloc(32,sizeof(*l->splitcache)); + fdrffti(n, l->trigcache, l->splitcache); +} + +void drft_clear(drft_lookup *l){ + if(l){ + if(l->trigcache)_ogg_free(l->trigcache); + if(l->splitcache)_ogg_free(l->splitcache); + memset(l,0,sizeof(*l)); + } +} diff --git a/Engine/lib/libvorbis/lib/smallft.h b/Engine/lib/libvorbis/lib/smallft.h new file mode 100644 index 000000000..456497326 --- /dev/null +++ b/Engine/lib/libvorbis/lib/smallft.h @@ -0,0 +1,34 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: fft transform + last mod: $Id: smallft.h 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#ifndef _V_SMFT_H_ +#define _V_SMFT_H_ + +#include "vorbis/codec.h" + +typedef struct { + int n; + float *trigcache; + int *splitcache; +} drft_lookup; + +extern void drft_forward(drft_lookup *l,float *data); +extern void drft_backward(drft_lookup *l,float *data); +extern void drft_init(drft_lookup *l,int n); +extern void drft_clear(drft_lookup *l); + +#endif diff --git a/Engine/lib/libvorbis/lib/synthesis.c b/Engine/lib/libvorbis/lib/synthesis.c new file mode 100644 index 000000000..8872f57ad --- /dev/null +++ b/Engine/lib/libvorbis/lib/synthesis.c @@ -0,0 +1,170 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: single-block PCM synthesis + last mod: $Id: synthesis.c 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#include +#include +#include "vorbis/codec.h" +#include "codec_internal.h" +#include "registry.h" +#include "misc.h" +#include "os.h" + +int vorbis_synthesis(vorbis_block *vb,ogg_packet *op){ + vorbis_dsp_state *vd=vb->vd; + private_state *b=vd->backend_state; + vorbis_info *vi=vd->vi; + codec_setup_info *ci=vi->codec_setup; + oggpack_buffer *opb=&vb->opb; + int type,mode,i; + + /* first things first. Make sure decode is ready */ + _vorbis_block_ripcord(vb); + oggpack_readinit(opb,op->packet,op->bytes); + + /* Check the packet type */ + if(oggpack_read(opb,1)!=0){ + /* Oops. This is not an audio data packet */ + return(OV_ENOTAUDIO); + } + + /* read our mode and pre/post windowsize */ + mode=oggpack_read(opb,b->modebits); + if(mode==-1)return(OV_EBADPACKET); + + vb->mode=mode; + vb->W=ci->mode_param[mode]->blockflag; + if(vb->W){ + + /* this doesn;t get mapped through mode selection as it's used + only for window selection */ + vb->lW=oggpack_read(opb,1); + vb->nW=oggpack_read(opb,1); + if(vb->nW==-1) return(OV_EBADPACKET); + }else{ + vb->lW=0; + vb->nW=0; + } + + /* more setup */ + vb->granulepos=op->granulepos; + vb->sequence=op->packetno; + vb->eofflag=op->e_o_s; + + /* alloc pcm passback storage */ + vb->pcmend=ci->blocksizes[vb->W]; + vb->pcm=_vorbis_block_alloc(vb,sizeof(*vb->pcm)*vi->channels); + for(i=0;ichannels;i++) + vb->pcm[i]=_vorbis_block_alloc(vb,vb->pcmend*sizeof(*vb->pcm[i])); + + /* unpack_header enforces range checking */ + type=ci->map_type[ci->mode_param[mode]->mapping]; + + return(_mapping_P[type]->inverse(vb,ci->map_param[ci->mode_param[mode]-> + mapping])); +} + +/* used to track pcm position without actually performing decode. + Useful for sequential 'fast forward' */ +int vorbis_synthesis_trackonly(vorbis_block *vb,ogg_packet *op){ + vorbis_dsp_state *vd=vb->vd; + private_state *b=vd->backend_state; + vorbis_info *vi=vd->vi; + codec_setup_info *ci=vi->codec_setup; + oggpack_buffer *opb=&vb->opb; + int mode; + + /* first things first. Make sure decode is ready */ + _vorbis_block_ripcord(vb); + oggpack_readinit(opb,op->packet,op->bytes); + + /* Check the packet type */ + if(oggpack_read(opb,1)!=0){ + /* Oops. This is not an audio data packet */ + return(OV_ENOTAUDIO); + } + + /* read our mode and pre/post windowsize */ + mode=oggpack_read(opb,b->modebits); + if(mode==-1)return(OV_EBADPACKET); + + vb->mode=mode; + vb->W=ci->mode_param[mode]->blockflag; + if(vb->W){ + vb->lW=oggpack_read(opb,1); + vb->nW=oggpack_read(opb,1); + if(vb->nW==-1) return(OV_EBADPACKET); + }else{ + vb->lW=0; + vb->nW=0; + } + + /* more setup */ + vb->granulepos=op->granulepos; + vb->sequence=op->packetno; + vb->eofflag=op->e_o_s; + + /* no pcm */ + vb->pcmend=0; + vb->pcm=NULL; + + return(0); +} + +long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op){ + codec_setup_info *ci=vi->codec_setup; + oggpack_buffer opb; + int mode; + + oggpack_readinit(&opb,op->packet,op->bytes); + + /* Check the packet type */ + if(oggpack_read(&opb,1)!=0){ + /* Oops. This is not an audio data packet */ + return(OV_ENOTAUDIO); + } + + { + int modebits=0; + int v=ci->modes; + while(v>1){ + modebits++; + v>>=1; + } + + /* read our mode and pre/post windowsize */ + mode=oggpack_read(&opb,modebits); + } + if(mode==-1)return(OV_EBADPACKET); + return(ci->blocksizes[ci->mode_param[mode]->blockflag]); +} + +int vorbis_synthesis_halfrate(vorbis_info *vi,int flag){ + /* set / clear half-sample-rate mode */ + codec_setup_info *ci=vi->codec_setup; + + /* right now, our MDCT can't handle < 64 sample windows. */ + if(ci->blocksizes[0]<=64 && flag)return -1; + ci->halfrate_flag=(flag?1:0); + return 0; +} + +int vorbis_synthesis_halfrate_p(vorbis_info *vi){ + codec_setup_info *ci=vi->codec_setup; + return ci->halfrate_flag; +} + + diff --git a/Engine/lib/libvorbis/lib/vorbisenc.c b/Engine/lib/libvorbis/lib/vorbisenc.c new file mode 100644 index 000000000..4915a58c8 --- /dev/null +++ b/Engine/lib/libvorbis/lib/vorbisenc.c @@ -0,0 +1,1184 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: simple programmatic interface for encoder mode setup + last mod: $Id: vorbisenc.c 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#include +#include +#include + +#include "vorbis/codec.h" +#include "vorbis/vorbisenc.h" + +#include "codec_internal.h" + +#include "os.h" +#include "misc.h" + +/* careful with this; it's using static array sizing to make managing + all the modes a little less annoying. If we use a residue backend + with > 12 partition types, or a different division of iteration, + this needs to be updated. */ +typedef struct { + static_codebook *books[12][3]; +} static_bookblock; + +typedef struct { + int res_type; + int limit_type; /* 0 lowpass limited, 1 point stereo limited */ + vorbis_info_residue0 *res; + static_codebook *book_aux; + static_codebook *book_aux_managed; + static_bookblock *books_base; + static_bookblock *books_base_managed; +} vorbis_residue_template; + +typedef struct { + vorbis_info_mapping0 *map; + vorbis_residue_template *res; +} vorbis_mapping_template; + +typedef struct vp_adjblock{ + int block[P_BANDS]; +} vp_adjblock; + +typedef struct { + int data[NOISE_COMPAND_LEVELS]; +} compandblock; + +/* high level configuration information for setting things up + step-by-step with the detailed vorbis_encode_ctl interface. + There's a fair amount of redundancy such that interactive setup + does not directly deal with any vorbis_info or codec_setup_info + initialization; it's all stored (until full init) in this highlevel + setup, then flushed out to the real codec setup structs later. */ + +typedef struct { + int att[P_NOISECURVES]; + float boost; + float decay; +} att3; +typedef struct { int data[P_NOISECURVES]; } adj3; + +typedef struct { + int pre[PACKETBLOBS]; + int post[PACKETBLOBS]; + float kHz[PACKETBLOBS]; + float lowpasskHz[PACKETBLOBS]; +} adj_stereo; + +typedef struct { + int lo; + int hi; + int fixed; +} noiseguard; +typedef struct { + int data[P_NOISECURVES][17]; +} noise3; + +typedef struct { + int mappings; + double *rate_mapping; + double *quality_mapping; + int coupling_restriction; + long samplerate_min_restriction; + long samplerate_max_restriction; + + + int *blocksize_short; + int *blocksize_long; + + att3 *psy_tone_masteratt; + int *psy_tone_0dB; + int *psy_tone_dBsuppress; + + vp_adjblock *psy_tone_adj_impulse; + vp_adjblock *psy_tone_adj_long; + vp_adjblock *psy_tone_adj_other; + + noiseguard *psy_noiseguards; + noise3 *psy_noise_bias_impulse; + noise3 *psy_noise_bias_padding; + noise3 *psy_noise_bias_trans; + noise3 *psy_noise_bias_long; + int *psy_noise_dBsuppress; + + compandblock *psy_noise_compand; + double *psy_noise_compand_short_mapping; + double *psy_noise_compand_long_mapping; + + int *psy_noise_normal_start[2]; + int *psy_noise_normal_partition[2]; + double *psy_noise_normal_thresh; + + int *psy_ath_float; + int *psy_ath_abs; + + double *psy_lowpass; + + vorbis_info_psy_global *global_params; + double *global_mapping; + adj_stereo *stereo_modes; + + static_codebook ***floor_books; + vorbis_info_floor1 *floor_params; + int *floor_short_mapping; + int *floor_long_mapping; + + vorbis_mapping_template *maps; +} ve_setup_data_template; + +/* a few static coder conventions */ +static vorbis_info_mode _mode_template[2]={ + {0,0,0,0}, + {1,0,0,1} +}; + +static vorbis_info_mapping0 _map_nominal[2]={ + {1, {0,0}, {0}, {0}, 1,{0},{1}}, + {1, {0,0}, {1}, {1}, 1,{0},{1}} +}; + +#include "modes/setup_44.h" +#include "modes/setup_44u.h" +#include "modes/setup_32.h" +#include "modes/setup_8.h" +#include "modes/setup_11.h" +#include "modes/setup_16.h" +#include "modes/setup_22.h" +#include "modes/setup_X.h" + +static ve_setup_data_template *setup_list[]={ + &ve_setup_44_stereo, + &ve_setup_44_uncoupled, + + &ve_setup_32_stereo, + &ve_setup_32_uncoupled, + + &ve_setup_22_stereo, + &ve_setup_22_uncoupled, + &ve_setup_16_stereo, + &ve_setup_16_uncoupled, + + &ve_setup_11_stereo, + &ve_setup_11_uncoupled, + &ve_setup_8_stereo, + &ve_setup_8_uncoupled, + + &ve_setup_X_stereo, + &ve_setup_X_uncoupled, + &ve_setup_XX_stereo, + &ve_setup_XX_uncoupled, + 0 +}; + +static int vorbis_encode_toplevel_setup(vorbis_info *vi,int ch,long rate){ + if(vi && vi->codec_setup){ + + vi->version=0; + vi->channels=ch; + vi->rate=rate; + + return(0); + } + return(OV_EINVAL); +} + +static void vorbis_encode_floor_setup(vorbis_info *vi,double s,int block, + static_codebook ***books, + vorbis_info_floor1 *in, + int *x){ + int i,k,is=s; + vorbis_info_floor1 *f=_ogg_calloc(1,sizeof(*f)); + codec_setup_info *ci=vi->codec_setup; + + memcpy(f,in+x[is],sizeof(*f)); + /* fill in the lowpass field, even if it's temporary */ + f->n=ci->blocksizes[block]>>1; + + /* books */ + { + int partitions=f->partitions; + int maxclass=-1; + int maxbook=-1; + for(i=0;ipartitionclass[i]>maxclass)maxclass=f->partitionclass[i]; + for(i=0;i<=maxclass;i++){ + if(f->class_book[i]>maxbook)maxbook=f->class_book[i]; + f->class_book[i]+=ci->books; + for(k=0;k<(1<class_subs[i]);k++){ + if(f->class_subbook[i][k]>maxbook)maxbook=f->class_subbook[i][k]; + if(f->class_subbook[i][k]>=0)f->class_subbook[i][k]+=ci->books; + } + } + + for(i=0;i<=maxbook;i++) + ci->book_param[ci->books++]=books[x[is]][i]; + } + + /* for now, we're only using floor 1 */ + ci->floor_type[ci->floors]=1; + ci->floor_param[ci->floors]=f; + ci->floors++; + + return; +} + +static void vorbis_encode_global_psych_setup(vorbis_info *vi,double s, + vorbis_info_psy_global *in, + double *x){ + int i,is=s; + double ds=s-is; + codec_setup_info *ci=vi->codec_setup; + vorbis_info_psy_global *g=&ci->psy_g_param; + + memcpy(g,in+(int)x[is],sizeof(*g)); + + ds=x[is]*(1.-ds)+x[is+1]*ds; + is=(int)ds; + ds-=is; + if(ds==0 && is>0){ + is--; + ds=1.; + } + + /* interpolate the trigger threshholds */ + for(i=0;i<4;i++){ + g->preecho_thresh[i]=in[is].preecho_thresh[i]*(1.-ds)+in[is+1].preecho_thresh[i]*ds; + g->postecho_thresh[i]=in[is].postecho_thresh[i]*(1.-ds)+in[is+1].postecho_thresh[i]*ds; + } + g->ampmax_att_per_sec=ci->hi.amplitude_track_dBpersec; + return; +} + +static void vorbis_encode_global_stereo(vorbis_info *vi, + highlevel_encode_setup *hi, + adj_stereo *p){ + float s=hi->stereo_point_setting; + int i,is=s; + double ds=s-is; + codec_setup_info *ci=vi->codec_setup; + vorbis_info_psy_global *g=&ci->psy_g_param; + + if(p){ + memcpy(g->coupling_prepointamp,p[is].pre,sizeof(*p[is].pre)*PACKETBLOBS); + memcpy(g->coupling_postpointamp,p[is].post,sizeof(*p[is].post)*PACKETBLOBS); + + if(hi->managed){ + /* interpolate the kHz threshholds */ + for(i=0;icoupling_pointlimit[0][i]=kHz*1000./vi->rate*ci->blocksizes[0]; + g->coupling_pointlimit[1][i]=kHz*1000./vi->rate*ci->blocksizes[1]; + g->coupling_pkHz[i]=kHz; + + kHz=p[is].lowpasskHz[i]*(1.-ds)+p[is+1].lowpasskHz[i]*ds; + g->sliding_lowpass[0][i]=kHz*1000./vi->rate*ci->blocksizes[0]; + g->sliding_lowpass[1][i]=kHz*1000./vi->rate*ci->blocksizes[1]; + + } + }else{ + float kHz=p[is].kHz[PACKETBLOBS/2]*(1.-ds)+p[is+1].kHz[PACKETBLOBS/2]*ds; + for(i=0;icoupling_pointlimit[0][i]=kHz*1000./vi->rate*ci->blocksizes[0]; + g->coupling_pointlimit[1][i]=kHz*1000./vi->rate*ci->blocksizes[1]; + g->coupling_pkHz[i]=kHz; + } + + kHz=p[is].lowpasskHz[PACKETBLOBS/2]*(1.-ds)+p[is+1].lowpasskHz[PACKETBLOBS/2]*ds; + for(i=0;isliding_lowpass[0][i]=kHz*1000./vi->rate*ci->blocksizes[0]; + g->sliding_lowpass[1][i]=kHz*1000./vi->rate*ci->blocksizes[1]; + } + } + }else{ + for(i=0;isliding_lowpass[0][i]=ci->blocksizes[0]; + g->sliding_lowpass[1][i]=ci->blocksizes[1]; + } + } + return; +} + +static void vorbis_encode_psyset_setup(vorbis_info *vi,double s, + int *nn_start, + int *nn_partition, + double *nn_thresh, + int block){ + codec_setup_info *ci=vi->codec_setup; + vorbis_info_psy *p=ci->psy_param[block]; + highlevel_encode_setup *hi=&ci->hi; + int is=s; + + if(block>=ci->psys) + ci->psys=block+1; + if(!p){ + p=_ogg_calloc(1,sizeof(*p)); + ci->psy_param[block]=p; + } + + memcpy(p,&_psy_info_template,sizeof(*p)); + p->blockflag=block>>1; + + if(hi->noise_normalize_p){ + p->normal_channel_p=1; + p->normal_point_p=1; + p->normal_start=nn_start[is]; + p->normal_partition=nn_partition[is]; + p->normal_thresh=nn_thresh[is]; + } + + return; +} + +static void vorbis_encode_tonemask_setup(vorbis_info *vi,double s,int block, + att3 *att, + int *max, + vp_adjblock *in){ + int i,is=s; + double ds=s-is; + codec_setup_info *ci=vi->codec_setup; + vorbis_info_psy *p=ci->psy_param[block]; + + /* 0 and 2 are only used by bitmanagement, but there's no harm to always + filling the values in here */ + p->tone_masteratt[0]=att[is].att[0]*(1.-ds)+att[is+1].att[0]*ds; + p->tone_masteratt[1]=att[is].att[1]*(1.-ds)+att[is+1].att[1]*ds; + p->tone_masteratt[2]=att[is].att[2]*(1.-ds)+att[is+1].att[2]*ds; + p->tone_centerboost=att[is].boost*(1.-ds)+att[is+1].boost*ds; + p->tone_decay=att[is].decay*(1.-ds)+att[is+1].decay*ds; + + p->max_curve_dB=max[is]*(1.-ds)+max[is+1]*ds; + + for(i=0;itoneatt[i]=in[is].block[i]*(1.-ds)+in[is+1].block[i]*ds; + return; +} + + +static void vorbis_encode_compand_setup(vorbis_info *vi,double s,int block, + compandblock *in, double *x){ + int i,is=s; + double ds=s-is; + codec_setup_info *ci=vi->codec_setup; + vorbis_info_psy *p=ci->psy_param[block]; + + ds=x[is]*(1.-ds)+x[is+1]*ds; + is=(int)ds; + ds-=is; + if(ds==0 && is>0){ + is--; + ds=1.; + } + + /* interpolate the compander settings */ + for(i=0;inoisecompand[i]=in[is].data[i]*(1.-ds)+in[is+1].data[i]*ds; + return; +} + +static void vorbis_encode_peak_setup(vorbis_info *vi,double s,int block, + int *suppress){ + int is=s; + double ds=s-is; + codec_setup_info *ci=vi->codec_setup; + vorbis_info_psy *p=ci->psy_param[block]; + + p->tone_abs_limit=suppress[is]*(1.-ds)+suppress[is+1]*ds; + + return; +} + +static void vorbis_encode_noisebias_setup(vorbis_info *vi,double s,int block, + int *suppress, + noise3 *in, + noiseguard *guard, + double userbias){ + int i,is=s,j; + double ds=s-is; + codec_setup_info *ci=vi->codec_setup; + vorbis_info_psy *p=ci->psy_param[block]; + + p->noisemaxsupp=suppress[is]*(1.-ds)+suppress[is+1]*ds; + p->noisewindowlomin=guard[block].lo; + p->noisewindowhimin=guard[block].hi; + p->noisewindowfixed=guard[block].fixed; + + for(j=0;jnoiseoff[j][i]=in[is].data[j][i]*(1.-ds)+in[is+1].data[j][i]*ds; + + /* impulse blocks may take a user specified bias to boost the + nominal/high noise encoding depth */ + for(j=0;jnoiseoff[j][0]+6; /* the lowest it can go */ + for(i=0;inoiseoff[j][i]+=userbias; + if(p->noiseoff[j][i]noiseoff[j][i]=min; + } + } + + return; +} + +static void vorbis_encode_ath_setup(vorbis_info *vi,int block){ + codec_setup_info *ci=vi->codec_setup; + vorbis_info_psy *p=ci->psy_param[block]; + + p->ath_adjatt=ci->hi.ath_floating_dB; + p->ath_maxatt=ci->hi.ath_absolute_dB; + return; +} + + +static int book_dup_or_new(codec_setup_info *ci,static_codebook *book){ + int i; + for(i=0;ibooks;i++) + if(ci->book_param[i]==book)return(i); + + return(ci->books++); +} + +static void vorbis_encode_blocksize_setup(vorbis_info *vi,double s, + int *shortb,int *longb){ + + codec_setup_info *ci=vi->codec_setup; + int is=s; + + int blockshort=shortb[is]; + int blocklong=longb[is]; + ci->blocksizes[0]=blockshort; + ci->blocksizes[1]=blocklong; + +} + +static void vorbis_encode_residue_setup(vorbis_info *vi, + int number, int block, + vorbis_residue_template *res){ + + codec_setup_info *ci=vi->codec_setup; + int i,n; + + vorbis_info_residue0 *r=ci->residue_param[number]= + _ogg_malloc(sizeof(*r)); + + memcpy(r,res->res,sizeof(*r)); + if(ci->residues<=number)ci->residues=number+1; + + switch(ci->blocksizes[block]){ + case 64:case 128:case 256: + r->grouping=16; + break; + default: + r->grouping=32; + break; + } + ci->residue_type[number]=res->res_type; + + /* to be adjusted by lowpass/pointlimit later */ + n=r->end=ci->blocksizes[block]>>1; + if(res->res_type==2) + n=r->end*=vi->channels; + + /* fill in all the books */ + { + int booklist=0,k; + + if(ci->hi.managed){ + for(i=0;ipartitions;i++) + for(k=0;k<3;k++) + if(res->books_base_managed->books[i][k]) + r->secondstages[i]|=(1<groupbook=book_dup_or_new(ci,res->book_aux_managed); + ci->book_param[r->groupbook]=res->book_aux_managed; + + for(i=0;ipartitions;i++){ + for(k=0;k<3;k++){ + if(res->books_base_managed->books[i][k]){ + int bookid=book_dup_or_new(ci,res->books_base_managed->books[i][k]); + r->booklist[booklist++]=bookid; + ci->book_param[bookid]=res->books_base_managed->books[i][k]; + } + } + } + + }else{ + + for(i=0;ipartitions;i++) + for(k=0;k<3;k++) + if(res->books_base->books[i][k]) + r->secondstages[i]|=(1<groupbook=book_dup_or_new(ci,res->book_aux); + ci->book_param[r->groupbook]=res->book_aux; + + for(i=0;ipartitions;i++){ + for(k=0;k<3;k++){ + if(res->books_base->books[i][k]){ + int bookid=book_dup_or_new(ci,res->books_base->books[i][k]); + r->booklist[booklist++]=bookid; + ci->book_param[bookid]=res->books_base->books[i][k]; + } + } + } + } + } + + /* lowpass setup/pointlimit */ + { + double freq=ci->hi.lowpass_kHz*1000.; + vorbis_info_floor1 *f=ci->floor_param[block]; /* by convention */ + double nyq=vi->rate/2.; + long blocksize=ci->blocksizes[block]>>1; + + /* lowpass needs to be set in the floor and the residue. */ + if(freq>nyq)freq=nyq; + /* in the floor, the granularity can be very fine; it doesn't alter + the encoding structure, only the samples used to fit the floor + approximation */ + f->n=freq/nyq*blocksize; + + /* this res may by limited by the maximum pointlimit of the mode, + not the lowpass. the floor is always lowpass limited. */ + if(res->limit_type){ + if(ci->hi.managed) + freq=ci->psy_g_param.coupling_pkHz[PACKETBLOBS-1]*1000.; + else + freq=ci->psy_g_param.coupling_pkHz[PACKETBLOBS/2]*1000.; + if(freq>nyq)freq=nyq; + } + + /* in the residue, we're constrained, physically, by partition + boundaries. We still lowpass 'wherever', but we have to round up + here to next boundary, or the vorbis spec will round it *down* to + previous boundary in encode/decode */ + if(ci->residue_type[block]==2) + r->end=(int)((freq/nyq*blocksize*2)/r->grouping+.9)* /* round up only if we're well past */ + r->grouping; + else + r->end=(int)((freq/nyq*blocksize)/r->grouping+.9)* /* round up only if we're well past */ + r->grouping; + } +} + +/* we assume two maps in this encoder */ +static void vorbis_encode_map_n_res_setup(vorbis_info *vi,double s, + vorbis_mapping_template *maps){ + + codec_setup_info *ci=vi->codec_setup; + int i,j,is=s,modes=2; + vorbis_info_mapping0 *map=maps[is].map; + vorbis_info_mode *mode=_mode_template; + vorbis_residue_template *res=maps[is].res; + + if(ci->blocksizes[0]==ci->blocksizes[1])modes=1; + + for(i=0;imap_param[i]=_ogg_calloc(1,sizeof(*map)); + ci->mode_param[i]=_ogg_calloc(1,sizeof(*mode)); + + memcpy(ci->mode_param[i],mode+i,sizeof(*_mode_template)); + if(i>=ci->modes)ci->modes=i+1; + + ci->map_type[i]=0; + memcpy(ci->map_param[i],map+i,sizeof(*map)); + if(i>=ci->maps)ci->maps=i+1; + + for(j=0;jcodec_setup; + highlevel_encode_setup *hi=&ci->hi; + ve_setup_data_template *setup=(ve_setup_data_template *)hi->setup; + int is=hi->base_setting; + double ds=hi->base_setting-is; + int ch=vi->channels; + double *r=setup->rate_mapping; + + if(r==NULL) + return(-1); + + return((r[is]*(1.-ds)+r[is+1]*ds)*ch); +} + +static void get_setup_template(vorbis_info *vi, + long ch,long srate, + double req,int q_or_bitrate){ + int i=0,j; + codec_setup_info *ci=vi->codec_setup; + highlevel_encode_setup *hi=&ci->hi; + if(q_or_bitrate)req/=ch; + + while(setup_list[i]){ + if(setup_list[i]->coupling_restriction==-1 || + setup_list[i]->coupling_restriction==ch){ + if(srate>=setup_list[i]->samplerate_min_restriction && + srate<=setup_list[i]->samplerate_max_restriction){ + int mappings=setup_list[i]->mappings; + double *map=(q_or_bitrate? + setup_list[i]->rate_mapping: + setup_list[i]->quality_mapping); + + /* the template matches. Does the requested quality mode + fall within this template's modes? */ + if(reqmap[setup_list[i]->mappings]){++i;continue;} + for(j=0;j=map[j] && reqsetup=setup_list[i]; + if(j==mappings) + hi->base_setting=j-.001; + else{ + float low=map[j]; + float high=map[j+1]; + float del=(req-low)/(high-low); + hi->base_setting=j+del; + } + + return; + } + } + i++; + } + + hi->setup=NULL; +} + +/* encoders will need to use vorbis_info_init beforehand and call + vorbis_info clear when all done */ + +/* two interfaces; this, more detailed one, and later a convenience + layer on top */ + +/* the final setup call */ +int vorbis_encode_setup_init(vorbis_info *vi){ + int i0=0,singleblock=0; + codec_setup_info *ci=vi->codec_setup; + ve_setup_data_template *setup=NULL; + highlevel_encode_setup *hi=&ci->hi; + + if(ci==NULL)return(OV_EINVAL); + if(!hi->impulse_block_p)i0=1; + + /* too low/high an ATH floater is nonsensical, but doesn't break anything */ + if(hi->ath_floating_dB>-80)hi->ath_floating_dB=-80; + if(hi->ath_floating_dB<-200)hi->ath_floating_dB=-200; + + /* again, bound this to avoid the app shooting itself int he foot + too badly */ + if(hi->amplitude_track_dBpersec>0.)hi->amplitude_track_dBpersec=0.; + if(hi->amplitude_track_dBpersec<-99999.)hi->amplitude_track_dBpersec=-99999.; + + /* get the appropriate setup template; matches the fetch in previous + stages */ + setup=(ve_setup_data_template *)hi->setup; + if(setup==NULL)return(OV_EINVAL); + + hi->set_in_stone=1; + /* choose block sizes from configured sizes as well as paying + attention to long_block_p and short_block_p. If the configured + short and long blocks are the same length, we set long_block_p + and unset short_block_p */ + vorbis_encode_blocksize_setup(vi,hi->base_setting, + setup->blocksize_short, + setup->blocksize_long); + if(ci->blocksizes[0]==ci->blocksizes[1])singleblock=1; + + /* floor setup; choose proper floor params. Allocated on the floor + stack in order; if we alloc only long floor, it's 0 */ + vorbis_encode_floor_setup(vi,hi->short_setting,0, + setup->floor_books, + setup->floor_params, + setup->floor_short_mapping); + if(!singleblock) + vorbis_encode_floor_setup(vi,hi->long_setting,1, + setup->floor_books, + setup->floor_params, + setup->floor_long_mapping); + + /* setup of [mostly] short block detection and stereo*/ + vorbis_encode_global_psych_setup(vi,hi->trigger_setting, + setup->global_params, + setup->global_mapping); + vorbis_encode_global_stereo(vi,hi,setup->stereo_modes); + + /* basic psych setup and noise normalization */ + vorbis_encode_psyset_setup(vi,hi->short_setting, + setup->psy_noise_normal_start[0], + setup->psy_noise_normal_partition[0], + setup->psy_noise_normal_thresh, + 0); + vorbis_encode_psyset_setup(vi,hi->short_setting, + setup->psy_noise_normal_start[0], + setup->psy_noise_normal_partition[0], + setup->psy_noise_normal_thresh, + 1); + if(!singleblock){ + vorbis_encode_psyset_setup(vi,hi->long_setting, + setup->psy_noise_normal_start[1], + setup->psy_noise_normal_partition[1], + setup->psy_noise_normal_thresh, + 2); + vorbis_encode_psyset_setup(vi,hi->long_setting, + setup->psy_noise_normal_start[1], + setup->psy_noise_normal_partition[1], + setup->psy_noise_normal_thresh, + 3); + } + + /* tone masking setup */ + vorbis_encode_tonemask_setup(vi,hi->block[i0].tone_mask_setting,0, + setup->psy_tone_masteratt, + setup->psy_tone_0dB, + setup->psy_tone_adj_impulse); + vorbis_encode_tonemask_setup(vi,hi->block[1].tone_mask_setting,1, + setup->psy_tone_masteratt, + setup->psy_tone_0dB, + setup->psy_tone_adj_other); + if(!singleblock){ + vorbis_encode_tonemask_setup(vi,hi->block[2].tone_mask_setting,2, + setup->psy_tone_masteratt, + setup->psy_tone_0dB, + setup->psy_tone_adj_other); + vorbis_encode_tonemask_setup(vi,hi->block[3].tone_mask_setting,3, + setup->psy_tone_masteratt, + setup->psy_tone_0dB, + setup->psy_tone_adj_long); + } + + /* noise companding setup */ + vorbis_encode_compand_setup(vi,hi->block[i0].noise_compand_setting,0, + setup->psy_noise_compand, + setup->psy_noise_compand_short_mapping); + vorbis_encode_compand_setup(vi,hi->block[1].noise_compand_setting,1, + setup->psy_noise_compand, + setup->psy_noise_compand_short_mapping); + if(!singleblock){ + vorbis_encode_compand_setup(vi,hi->block[2].noise_compand_setting,2, + setup->psy_noise_compand, + setup->psy_noise_compand_long_mapping); + vorbis_encode_compand_setup(vi,hi->block[3].noise_compand_setting,3, + setup->psy_noise_compand, + setup->psy_noise_compand_long_mapping); + } + + /* peak guarding setup */ + vorbis_encode_peak_setup(vi,hi->block[i0].tone_peaklimit_setting,0, + setup->psy_tone_dBsuppress); + vorbis_encode_peak_setup(vi,hi->block[1].tone_peaklimit_setting,1, + setup->psy_tone_dBsuppress); + if(!singleblock){ + vorbis_encode_peak_setup(vi,hi->block[2].tone_peaklimit_setting,2, + setup->psy_tone_dBsuppress); + vorbis_encode_peak_setup(vi,hi->block[3].tone_peaklimit_setting,3, + setup->psy_tone_dBsuppress); + } + + /* noise bias setup */ + vorbis_encode_noisebias_setup(vi,hi->block[i0].noise_bias_setting,0, + setup->psy_noise_dBsuppress, + setup->psy_noise_bias_impulse, + setup->psy_noiseguards, + (i0==0?hi->impulse_noisetune:0.)); + vorbis_encode_noisebias_setup(vi,hi->block[1].noise_bias_setting,1, + setup->psy_noise_dBsuppress, + setup->psy_noise_bias_padding, + setup->psy_noiseguards,0.); + if(!singleblock){ + vorbis_encode_noisebias_setup(vi,hi->block[2].noise_bias_setting,2, + setup->psy_noise_dBsuppress, + setup->psy_noise_bias_trans, + setup->psy_noiseguards,0.); + vorbis_encode_noisebias_setup(vi,hi->block[3].noise_bias_setting,3, + setup->psy_noise_dBsuppress, + setup->psy_noise_bias_long, + setup->psy_noiseguards,0.); + } + + vorbis_encode_ath_setup(vi,0); + vorbis_encode_ath_setup(vi,1); + if(!singleblock){ + vorbis_encode_ath_setup(vi,2); + vorbis_encode_ath_setup(vi,3); + } + + vorbis_encode_map_n_res_setup(vi,hi->base_setting,setup->maps); + + /* set bitrate readonlies and management */ + if(hi->bitrate_av>0) + vi->bitrate_nominal=hi->bitrate_av; + else{ + vi->bitrate_nominal=setting_to_approx_bitrate(vi); + } + + vi->bitrate_lower=hi->bitrate_min; + vi->bitrate_upper=hi->bitrate_max; + if(hi->bitrate_av) + vi->bitrate_window=(double)hi->bitrate_reservoir/hi->bitrate_av; + else + vi->bitrate_window=0.; + + if(hi->managed){ + ci->bi.avg_rate=hi->bitrate_av; + ci->bi.min_rate=hi->bitrate_min; + ci->bi.max_rate=hi->bitrate_max; + + ci->bi.reservoir_bits=hi->bitrate_reservoir; + ci->bi.reservoir_bias= + hi->bitrate_reservoir_bias; + + ci->bi.slew_damp=hi->bitrate_av_damp; + + } + + return(0); + +} + +static int vorbis_encode_setup_setting(vorbis_info *vi, + long channels, + long rate){ + int ret=0,i,is; + codec_setup_info *ci=vi->codec_setup; + highlevel_encode_setup *hi=&ci->hi; + ve_setup_data_template *setup=hi->setup; + double ds; + + ret=vorbis_encode_toplevel_setup(vi,channels,rate); + if(ret)return(ret); + + is=hi->base_setting; + ds=hi->base_setting-is; + + hi->short_setting=hi->base_setting; + hi->long_setting=hi->base_setting; + + hi->managed=0; + + hi->impulse_block_p=1; + hi->noise_normalize_p=1; + + hi->stereo_point_setting=hi->base_setting; + hi->lowpass_kHz= + setup->psy_lowpass[is]*(1.-ds)+setup->psy_lowpass[is+1]*ds; + + hi->ath_floating_dB=setup->psy_ath_float[is]*(1.-ds)+ + setup->psy_ath_float[is+1]*ds; + hi->ath_absolute_dB=setup->psy_ath_abs[is]*(1.-ds)+ + setup->psy_ath_abs[is+1]*ds; + + hi->amplitude_track_dBpersec=-6.; + hi->trigger_setting=hi->base_setting; + + for(i=0;i<4;i++){ + hi->block[i].tone_mask_setting=hi->base_setting; + hi->block[i].tone_peaklimit_setting=hi->base_setting; + hi->block[i].noise_bias_setting=hi->base_setting; + hi->block[i].noise_compand_setting=hi->base_setting; + } + + return(ret); +} + +int vorbis_encode_setup_vbr(vorbis_info *vi, + long channels, + long rate, + float quality){ + codec_setup_info *ci=vi->codec_setup; + highlevel_encode_setup *hi=&ci->hi; + + quality+=.0000001; + if(quality>=1.)quality=.9999; + + get_setup_template(vi,channels,rate,quality,0); + if(!hi->setup)return OV_EIMPL; + + return vorbis_encode_setup_setting(vi,channels,rate); +} + +int vorbis_encode_init_vbr(vorbis_info *vi, + long channels, + long rate, + + float base_quality /* 0. to 1. */ + ){ + int ret=0; + + ret=vorbis_encode_setup_vbr(vi,channels,rate,base_quality); + + if(ret){ + vorbis_info_clear(vi); + return ret; + } + ret=vorbis_encode_setup_init(vi); + if(ret) + vorbis_info_clear(vi); + return(ret); +} + +int vorbis_encode_setup_managed(vorbis_info *vi, + long channels, + long rate, + + long max_bitrate, + long nominal_bitrate, + long min_bitrate){ + + codec_setup_info *ci=vi->codec_setup; + highlevel_encode_setup *hi=&ci->hi; + double tnominal=nominal_bitrate; + int ret=0; + + if(nominal_bitrate<=0.){ + if(max_bitrate>0.){ + if(min_bitrate>0.) + nominal_bitrate=(max_bitrate+min_bitrate)*.5; + else + nominal_bitrate=max_bitrate*.875; + }else{ + if(min_bitrate>0.){ + nominal_bitrate=min_bitrate; + }else{ + return(OV_EINVAL); + } + } + } + + get_setup_template(vi,channels,rate,nominal_bitrate,1); + if(!hi->setup)return OV_EIMPL; + + ret=vorbis_encode_setup_setting(vi,channels,rate); + if(ret){ + vorbis_info_clear(vi); + return ret; + } + + /* initialize management with sane defaults */ + hi->managed=1; + hi->bitrate_min=min_bitrate; + hi->bitrate_max=max_bitrate; + hi->bitrate_av=tnominal; + hi->bitrate_av_damp=1.5f; /* full range in no less than 1.5 second */ + hi->bitrate_reservoir=nominal_bitrate*2; + hi->bitrate_reservoir_bias=.1; /* bias toward hoarding bits */ + + return(ret); + +} + +int vorbis_encode_init(vorbis_info *vi, + long channels, + long rate, + + long max_bitrate, + long nominal_bitrate, + long min_bitrate){ + + int ret=vorbis_encode_setup_managed(vi,channels,rate, + max_bitrate, + nominal_bitrate, + min_bitrate); + if(ret){ + vorbis_info_clear(vi); + return(ret); + } + + ret=vorbis_encode_setup_init(vi); + if(ret) + vorbis_info_clear(vi); + return(ret); +} + +int vorbis_encode_ctl(vorbis_info *vi,int number,void *arg){ + if(vi){ + codec_setup_info *ci=vi->codec_setup; + highlevel_encode_setup *hi=&ci->hi; + int setp=(number&0xf); /* a read request has a low nibble of 0 */ + + if(setp && hi->set_in_stone)return(OV_EINVAL); + + switch(number){ + + /* now deprecated *****************/ + case OV_ECTL_RATEMANAGE_GET: + { + + struct ovectl_ratemanage_arg *ai= + (struct ovectl_ratemanage_arg *)arg; + + ai->management_active=hi->managed; + ai->bitrate_hard_window=ai->bitrate_av_window= + (double)hi->bitrate_reservoir/vi->rate; + ai->bitrate_av_window_center=1.; + ai->bitrate_hard_min=hi->bitrate_min; + ai->bitrate_hard_max=hi->bitrate_max; + ai->bitrate_av_lo=hi->bitrate_av; + ai->bitrate_av_hi=hi->bitrate_av; + + } + return(0); + + /* now deprecated *****************/ + case OV_ECTL_RATEMANAGE_SET: + { + struct ovectl_ratemanage_arg *ai= + (struct ovectl_ratemanage_arg *)arg; + if(ai==NULL){ + hi->managed=0; + }else{ + hi->managed=ai->management_active; + vorbis_encode_ctl(vi,OV_ECTL_RATEMANAGE_AVG,arg); + vorbis_encode_ctl(vi,OV_ECTL_RATEMANAGE_HARD,arg); + } + } + return 0; + + /* now deprecated *****************/ + case OV_ECTL_RATEMANAGE_AVG: + { + struct ovectl_ratemanage_arg *ai= + (struct ovectl_ratemanage_arg *)arg; + if(ai==NULL){ + hi->bitrate_av=0; + }else{ + hi->bitrate_av=(ai->bitrate_av_lo+ai->bitrate_av_hi)*.5; + } + } + return(0); + /* now deprecated *****************/ + case OV_ECTL_RATEMANAGE_HARD: + { + struct ovectl_ratemanage_arg *ai= + (struct ovectl_ratemanage_arg *)arg; + if(ai==NULL){ + hi->bitrate_min=0; + hi->bitrate_max=0; + }else{ + hi->bitrate_min=ai->bitrate_hard_min; + hi->bitrate_max=ai->bitrate_hard_max; + hi->bitrate_reservoir=ai->bitrate_hard_window* + (hi->bitrate_max+hi->bitrate_min)*.5; + } + if(hi->bitrate_reservoir<128.) + hi->bitrate_reservoir=128.; + } + return(0); + + /* replacement ratemanage interface */ + case OV_ECTL_RATEMANAGE2_GET: + { + struct ovectl_ratemanage2_arg *ai= + (struct ovectl_ratemanage2_arg *)arg; + if(ai==NULL)return OV_EINVAL; + + ai->management_active=hi->managed; + ai->bitrate_limit_min_kbps=hi->bitrate_min/1000; + ai->bitrate_limit_max_kbps=hi->bitrate_max/1000; + ai->bitrate_average_kbps=hi->bitrate_av/1000; + ai->bitrate_average_damping=hi->bitrate_av_damp; + ai->bitrate_limit_reservoir_bits=hi->bitrate_reservoir; + ai->bitrate_limit_reservoir_bias=hi->bitrate_reservoir_bias; + } + return (0); + case OV_ECTL_RATEMANAGE2_SET: + { + struct ovectl_ratemanage2_arg *ai= + (struct ovectl_ratemanage2_arg *)arg; + if(ai==NULL){ + hi->managed=0; + }else{ + /* sanity check; only catch invariant violations */ + if(ai->bitrate_limit_min_kbps>0 && + ai->bitrate_average_kbps>0 && + ai->bitrate_limit_min_kbps>ai->bitrate_average_kbps) + return OV_EINVAL; + + if(ai->bitrate_limit_max_kbps>0 && + ai->bitrate_average_kbps>0 && + ai->bitrate_limit_max_kbpsbitrate_average_kbps) + return OV_EINVAL; + + if(ai->bitrate_limit_min_kbps>0 && + ai->bitrate_limit_max_kbps>0 && + ai->bitrate_limit_min_kbps>ai->bitrate_limit_max_kbps) + return OV_EINVAL; + + if(ai->bitrate_average_damping <= 0.) + return OV_EINVAL; + + if(ai->bitrate_limit_reservoir_bits < 0) + return OV_EINVAL; + + if(ai->bitrate_limit_reservoir_bias < 0.) + return OV_EINVAL; + + if(ai->bitrate_limit_reservoir_bias > 1.) + return OV_EINVAL; + + hi->managed=ai->management_active; + hi->bitrate_min=ai->bitrate_limit_min_kbps * 1000; + hi->bitrate_max=ai->bitrate_limit_max_kbps * 1000; + hi->bitrate_av=ai->bitrate_average_kbps * 1000; + hi->bitrate_av_damp=ai->bitrate_average_damping; + hi->bitrate_reservoir=ai->bitrate_limit_reservoir_bits; + hi->bitrate_reservoir_bias=ai->bitrate_limit_reservoir_bias; + } + } + return 0; + + case OV_ECTL_LOWPASS_GET: + { + double *farg=(double *)arg; + *farg=hi->lowpass_kHz; + } + return(0); + case OV_ECTL_LOWPASS_SET: + { + double *farg=(double *)arg; + hi->lowpass_kHz=*farg; + + if(hi->lowpass_kHz<2.)hi->lowpass_kHz=2.; + if(hi->lowpass_kHz>99.)hi->lowpass_kHz=99.; + } + return(0); + case OV_ECTL_IBLOCK_GET: + { + double *farg=(double *)arg; + *farg=hi->impulse_noisetune; + } + return(0); + case OV_ECTL_IBLOCK_SET: + { + double *farg=(double *)arg; + hi->impulse_noisetune=*farg; + + if(hi->impulse_noisetune>0.)hi->impulse_noisetune=0.; + if(hi->impulse_noisetune<-15.)hi->impulse_noisetune=-15.; + } + return(0); + } + + + return(OV_EIMPL); + } + return(OV_EINVAL); +} diff --git a/Engine/lib/libvorbis/lib/vorbisfile.c b/Engine/lib/libvorbis/lib/vorbisfile.c new file mode 100644 index 000000000..f63f619c6 --- /dev/null +++ b/Engine/lib/libvorbis/lib/vorbisfile.c @@ -0,0 +1,2194 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: stdio-based convenience library for opening/seeking/decoding + last mod: $Id: vorbisfile.c 13294 2007-07-24 01:08:23Z xiphmont $ + + ********************************************************************/ + +#include +#include +#include +#include +#include + +#include "vorbis/codec.h" +#include "vorbis/vorbisfile.h" + +#include "os.h" +#include "misc.h" + +/* A 'chained bitstream' is a Vorbis bitstream that contains more than + one logical bitstream arranged end to end (the only form of Ogg + multiplexing allowed in a Vorbis bitstream; grouping [parallel + multiplexing] is not allowed in Vorbis) */ + +/* A Vorbis file can be played beginning to end (streamed) without + worrying ahead of time about chaining (see decoder_example.c). If + we have the whole file, however, and want random access + (seeking/scrubbing) or desire to know the total length/time of a + file, we need to account for the possibility of chaining. */ + +/* We can handle things a number of ways; we can determine the entire + bitstream structure right off the bat, or find pieces on demand. + This example determines and caches structure for the entire + bitstream, but builds a virtual decoder on the fly when moving + between links in the chain. */ + +/* There are also different ways to implement seeking. Enough + information exists in an Ogg bitstream to seek to + sample-granularity positions in the output. Or, one can seek by + picking some portion of the stream roughly in the desired area if + we only want coarse navigation through the stream. */ + +/************************************************************************* + * Many, many internal helpers. The intention is not to be confusing; + * rampant duplication and monolithic function implementation would be + * harder to understand anyway. The high level functions are last. Begin + * grokking near the end of the file */ + +/* read a little more data from the file/pipe into the ogg_sync framer +*/ +#define CHUNKSIZE 65536 + +static long _get_data(OggVorbis_File *vf){ + errno=0; + if(!(vf->callbacks.read_func))return(-1); + if(vf->datasource){ + char *buffer=ogg_sync_buffer(&vf->oy,CHUNKSIZE); + long bytes=(vf->callbacks.read_func)(buffer,1,CHUNKSIZE,vf->datasource); + if(bytes>0)ogg_sync_wrote(&vf->oy,bytes); + if(bytes==0 && errno)return(-1); + return(bytes); + }else + return(0); +} + +/* save a tiny smidge of verbosity to make the code more readable */ +static int _seek_helper(OggVorbis_File *vf,ogg_int64_t offset){ + if(vf->datasource){ + if(!(vf->callbacks.seek_func)|| + (vf->callbacks.seek_func)(vf->datasource, offset, SEEK_SET) == -1) + return OV_EREAD; + vf->offset=offset; + ogg_sync_reset(&vf->oy); + }else{ + /* shouldn't happen unless someone writes a broken callback */ + return OV_EFAULT; + } + return 0; +} + +/* The read/seek functions track absolute position within the stream */ + +/* from the head of the stream, get the next page. boundary specifies + if the function is allowed to fetch more data from the stream (and + how much) or only use internally buffered data. + + boundary: -1) unbounded search + 0) read no additional data; use cached only + n) search for a new page beginning for n bytes + + return: <0) did not find a page (OV_FALSE, OV_EOF, OV_EREAD) + n) found a page at absolute offset n */ + +static ogg_int64_t _get_next_page(OggVorbis_File *vf,ogg_page *og, + ogg_int64_t boundary){ + if(boundary>0)boundary+=vf->offset; + while(1){ + long more; + + if(boundary>0 && vf->offset>=boundary)return(OV_FALSE); + more=ogg_sync_pageseek(&vf->oy,og); + + if(more<0){ + /* skipped n bytes */ + vf->offset-=more; + }else{ + if(more==0){ + /* send more paramedics */ + if(!boundary)return(OV_FALSE); + { + long ret=_get_data(vf); + if(ret==0)return(OV_EOF); + if(ret<0)return(OV_EREAD); + } + }else{ + /* got a page. Return the offset at the page beginning, + advance the internal offset past the page end */ + ogg_int64_t ret=vf->offset; + vf->offset+=more; + return(ret); + + } + } + } +} + +/* find the latest page beginning before the current stream cursor + position. Much dirtier than the above as Ogg doesn't have any + backward search linkage. no 'readp' as it will certainly have to + read. */ +/* returns offset or OV_EREAD, OV_FAULT */ +static ogg_int64_t _get_prev_page(OggVorbis_File *vf,ogg_page *og){ + ogg_int64_t begin=vf->offset; + ogg_int64_t end=begin; + ogg_int64_t ret; + ogg_int64_t offset=-1; + + while(offset==-1){ + begin-=CHUNKSIZE; + if(begin<0) + begin=0; + + ret=_seek_helper(vf,begin); + if(ret)return(ret); + + while(vf->offsetoffset); + if(ret==OV_EREAD)return(OV_EREAD); + if(ret<0){ + break; + }else{ + offset=ret; + } + } + } + + /* we have the offset. Actually snork and hold the page now */ + ret=_seek_helper(vf,offset); + if(ret)return(ret); + + ret=_get_next_page(vf,og,CHUNKSIZE); + if(ret<0) + /* this shouldn't be possible */ + return(OV_EFAULT); + + return(offset); +} + +static void _add_serialno(ogg_page *og,long **serialno_list, int *n){ + long s = ogg_page_serialno(og); + (*n)++; + + if(serialno_list){ + *serialno_list = _ogg_realloc(*serialno_list, sizeof(*serialno_list)*(*n)); + }else{ + *serialno_list = _ogg_malloc(sizeof(**serialno_list)); + } + + (*serialno_list)[(*n)-1] = s; +} + +/* returns nonzero if found */ +static int _lookup_serialno(ogg_page *og, long *serialno_list, int n){ + long s = ogg_page_serialno(og); + + if(serialno_list){ + while(n--){ + if(*serialno_list == s) return 1; + serialno_list++; + } + } + return 0; +} + +/* start parsing pages at current offset, remembering all serial + numbers. Stop logging at first non-bos page */ +static int _get_serialnos(OggVorbis_File *vf, long **s, int *n){ + ogg_page og; + + *s=NULL; + *n=0; + + while(1){ + ogg_int64_t llret=_get_next_page(vf,&og,CHUNKSIZE); + if(llret==OV_EOF)return(0); + if(llret<0)return(llret); + if(!ogg_page_bos(&og)) return 0; + + /* look for duplicate serialnos; add this one if unique */ + if(_lookup_serialno(&og,*s,*n)){ + if(*s)_ogg_free(*s); + *s=0; + *n=0; + return(OV_EBADHEADER); + } + + _add_serialno(&og,s,n); + } +} + +/* finds each bitstream link one at a time using a bisection search + (has to begin by knowing the offset of the lb's initial page). + Recurses for each link so it can alloc the link storage after + finding them all, then unroll and fill the cache at the same time */ +static int _bisect_forward_serialno(OggVorbis_File *vf, + ogg_int64_t begin, + ogg_int64_t searched, + ogg_int64_t end, + long *currentno_list, + int currentnos, + long m){ + ogg_int64_t endsearched=end; + ogg_int64_t next=end; + ogg_page og; + ogg_int64_t ret; + + /* the below guards against garbage seperating the last and + first pages of two links. */ + while(searched=0)next=ret; + }else{ + searched=ret+og.header_len+og.body_len; + } + } + + { + long *next_serialno_list=NULL; + int next_serialnos=0; + + ret=_seek_helper(vf,next); + if(ret)return(ret); + ret=_get_serialnos(vf,&next_serialno_list,&next_serialnos); + if(ret)return(ret); + + if(searched>=end || next_serialnos==0){ + vf->links=m+1; + vf->offsets=_ogg_malloc((vf->links+1)*sizeof(*vf->offsets)); + vf->offsets[m+1]=searched; + }else{ + ret=_bisect_forward_serialno(vf,next,vf->offset, + end,next_serialno_list,next_serialnos,m+1); + if(ret)return(ret); + } + + if(next_serialno_list)_ogg_free(next_serialno_list); + } + vf->offsets[m]=begin; + return(0); +} + +/* uses the local ogg_stream storage in vf; this is important for + non-streaming input sources */ +static int _fetch_headers(OggVorbis_File *vf,vorbis_info *vi,vorbis_comment *vc, + long *serialno,ogg_page *og_ptr){ + ogg_page og; + ogg_packet op; + int i,ret; + int allbos=0; + + if(!og_ptr){ + ogg_int64_t llret=_get_next_page(vf,&og,CHUNKSIZE); + if(llret==OV_EREAD)return(OV_EREAD); + if(llret<0)return(OV_ENOTVORBIS); + og_ptr=&og; + } + + vorbis_info_init(vi); + vorbis_comment_init(vc); + + /* extract the first set of vorbis headers we see in the headerset */ + + while(1){ + + /* if we're past the ID headers, we won't be finding a Vorbis + stream in this link */ + if(!ogg_page_bos(og_ptr)){ + ret = OV_ENOTVORBIS; + goto bail_header; + } + + /* prospective stream setup; we need a stream to get packets */ + ogg_stream_reset_serialno(&vf->os,ogg_page_serialno(og_ptr)); + ogg_stream_pagein(&vf->os,og_ptr); + + if(ogg_stream_packetout(&vf->os,&op) > 0 && + vorbis_synthesis_idheader(&op)){ + + /* continue Vorbis header load; past this point, any error will + render this link useless (we won't continue looking for more + Vorbis streams */ + if(serialno)*serialno=vf->os.serialno; + vf->ready_state=STREAMSET; + if((ret=vorbis_synthesis_headerin(vi,vc,&op))) + goto bail_header; + + i=0; + while(i<2){ /* get a page loop */ + + while(i<2){ /* get a packet loop */ + + int result=ogg_stream_packetout(&vf->os,&op); + if(result==0)break; + if(result==-1){ + ret=OV_EBADHEADER; + goto bail_header; + } + + if((ret=vorbis_synthesis_headerin(vi,vc,&op))) + goto bail_header; + + i++; + } + + while(i<2){ + if(_get_next_page(vf,og_ptr,CHUNKSIZE)<0){ + ret=OV_EBADHEADER; + goto bail_header; + } + + /* if this page belongs to the correct stream, go parse it */ + if(vf->os.serialno == ogg_page_serialno(og_ptr)){ + ogg_stream_pagein(&vf->os,og_ptr); + break; + } + + /* if we never see the final vorbis headers before the link + ends, abort */ + if(ogg_page_bos(og_ptr)){ + if(allbos){ + ret = OV_EBADHEADER; + goto bail_header; + }else + allbos=1; + } + + /* otherwise, keep looking */ + } + } + + return 0; + } + + /* this wasn't vorbis, get next page, try again */ + { + ogg_int64_t llret=_get_next_page(vf,og_ptr,CHUNKSIZE); + if(llret==OV_EREAD)return(OV_EREAD); + if(llret<0)return(OV_ENOTVORBIS); + } + } + + bail_header: + vorbis_info_clear(vi); + vorbis_comment_clear(vc); + vf->ready_state=OPENED; + + return ret; +} + +/* last step of the OggVorbis_File initialization; get all the + vorbis_info structs and PCM positions. Only called by the seekable + initialization (local stream storage is hacked slightly; pay + attention to how that's done) */ + +/* this is void and does not propogate errors up because we want to be + able to open and use damaged bitstreams as well as we can. Just + watch out for missing information for links in the OggVorbis_File + struct */ +static void _prefetch_all_headers(OggVorbis_File *vf, ogg_int64_t dataoffset){ + ogg_page og; + int i; + ogg_int64_t ret; + + vf->vi=_ogg_realloc(vf->vi,vf->links*sizeof(*vf->vi)); + vf->vc=_ogg_realloc(vf->vc,vf->links*sizeof(*vf->vc)); + vf->serialnos=_ogg_malloc(vf->links*sizeof(*vf->serialnos)); + vf->dataoffsets=_ogg_malloc(vf->links*sizeof(*vf->dataoffsets)); + vf->pcmlengths=_ogg_malloc(vf->links*2*sizeof(*vf->pcmlengths)); + + for(i=0;ilinks;i++){ + if(i==0){ + /* we already grabbed the initial header earlier. Just set the offset */ + vf->serialnos[i]=vf->current_serialno; + vf->dataoffsets[i]=dataoffset; + ret=_seek_helper(vf,dataoffset); + if(ret) + vf->dataoffsets[i]=-1; + + }else{ + + /* seek to the location of the initial header */ + + ret=_seek_helper(vf,vf->offsets[i]); + if(ret){ + vf->dataoffsets[i]=-1; + }else{ + if(_fetch_headers(vf,vf->vi+i,vf->vc+i,vf->serialnos+i,NULL)<0){ + vf->dataoffsets[i]=-1; + }else{ + vf->dataoffsets[i]=vf->offset; + } + } + } + + /* fetch beginning PCM offset */ + + if(vf->dataoffsets[i]!=-1){ + ogg_int64_t accumulated=0; + long lastblock=-1; + int result; + + ogg_stream_reset_serialno(&vf->os,vf->serialnos[i]); + + while(1){ + ogg_packet op; + + ret=_get_next_page(vf,&og,-1); + if(ret<0) + /* this should not be possible unless the file is + truncated/mangled */ + break; + + if(ogg_page_bos(&og)) break; + + if(ogg_page_serialno(&og)!=vf->serialnos[i]) + continue; + + /* count blocksizes of all frames in the page */ + ogg_stream_pagein(&vf->os,&og); + while((result=ogg_stream_packetout(&vf->os,&op))){ + if(result>0){ /* ignore holes */ + long thisblock=vorbis_packet_blocksize(vf->vi+i,&op); + if(lastblock!=-1) + accumulated+=(lastblock+thisblock)>>2; + lastblock=thisblock; + } + } + + if(ogg_page_granulepos(&og)!=-1){ + /* pcm offset of last packet on the first audio page */ + accumulated= ogg_page_granulepos(&og)-accumulated; + break; + } + } + + /* less than zero? This is a stream with samples trimmed off + the beginning, a normal occurrence; set the offset to zero */ + if(accumulated<0)accumulated=0; + + vf->pcmlengths[i*2]=accumulated; + } + + /* get the PCM length of this link. To do this, + get the last page of the stream */ + { + ogg_int64_t end=vf->offsets[i+1]; + ret=_seek_helper(vf,end); + if(ret){ + /* this should not be possible */ + vorbis_info_clear(vf->vi+i); + vorbis_comment_clear(vf->vc+i); + }else{ + + while(1){ + ret=_get_prev_page(vf,&og); + if(ret<0){ + /* this should not be possible */ + vorbis_info_clear(vf->vi+i); + vorbis_comment_clear(vf->vc+i); + break; + } + if(ogg_page_serialno(&og)==vf->serialnos[i]){ + if(ogg_page_granulepos(&og)!=-1){ + vf->pcmlengths[i*2+1]=ogg_page_granulepos(&og)-vf->pcmlengths[i*2]; + break; + } + } + vf->offset=ret; + } + } + } + } +} + +static int _make_decode_ready(OggVorbis_File *vf){ + if(vf->ready_state>STREAMSET)return 0; + if(vf->ready_stateseekable){ + if(vorbis_synthesis_init(&vf->vd,vf->vi+vf->current_link)) + return OV_EBADLINK; + }else{ + if(vorbis_synthesis_init(&vf->vd,vf->vi)) + return OV_EBADLINK; + } + vorbis_block_init(&vf->vd,&vf->vb); + vf->ready_state=INITSET; + vf->bittrack=0.f; + vf->samptrack=0.f; + return 0; +} + +static int _open_seekable2(OggVorbis_File *vf){ + ogg_int64_t dataoffset=vf->offset,end; + long *serialno_list=NULL; + int serialnos=0; + int ret; + ogg_page og; + + /* we're partially open and have a first link header state in + storage in vf */ + /* we can seek, so set out learning all about this file */ + if(vf->callbacks.seek_func && vf->callbacks.tell_func){ + (vf->callbacks.seek_func)(vf->datasource,0,SEEK_END); + vf->offset=vf->end=(vf->callbacks.tell_func)(vf->datasource); + }else{ + vf->offset=vf->end=-1; + } + + /* If seek_func is implemented, tell_func must also be implemented */ + if(vf->end==-1) return(OV_EINVAL); + + /* We get the offset for the last page of the physical bitstream. + Most OggVorbis files will contain a single logical bitstream */ + end=_get_prev_page(vf,&og); + if(end<0)return(end); + + /* back to beginning, learn all serialnos of first link */ + ret=_seek_helper(vf,0); + if(ret)return(ret); + ret=_get_serialnos(vf,&serialno_list,&serialnos); + if(ret)return(ret); + + /* now determine bitstream structure recursively */ + if(_bisect_forward_serialno(vf,0,0,end+1,serialno_list,serialnos,0)<0)return(OV_EREAD); + if(serialno_list)_ogg_free(serialno_list); + + /* the initial header memory is referenced by vf after; don't free it */ + _prefetch_all_headers(vf,dataoffset); + return(ov_raw_seek(vf,0)); +} + +/* clear out the current logical bitstream decoder */ +static void _decode_clear(OggVorbis_File *vf){ + vorbis_dsp_clear(&vf->vd); + vorbis_block_clear(&vf->vb); + vf->ready_state=OPENED; +} + +/* fetch and process a packet. Handles the case where we're at a + bitstream boundary and dumps the decoding machine. If the decoding + machine is unloaded, it loads it. It also keeps pcm_offset up to + date (seek and read both use this. seek uses a special hack with + readp). + + return: <0) error, OV_HOLE (lost packet) or OV_EOF + 0) need more data (only if readp==0) + 1) got a packet +*/ + +static int _fetch_and_process_packet(OggVorbis_File *vf, + ogg_packet *op_in, + int readp, + int spanp){ + ogg_page og; + + /* handle one packet. Try to fetch it from current stream state */ + /* extract packets from page */ + while(1){ + + /* process a packet if we can. If the machine isn't loaded, + neither is a page */ + if(vf->ready_state==INITSET){ + while(1) { + ogg_packet op; + ogg_packet *op_ptr=(op_in?op_in:&op); + int result=ogg_stream_packetout(&vf->os,op_ptr); + ogg_int64_t granulepos; + + op_in=NULL; + if(result==-1)return(OV_HOLE); /* hole in the data. */ + if(result>0){ + /* got a packet. process it */ + granulepos=op_ptr->granulepos; + if(!vorbis_synthesis(&vf->vb,op_ptr)){ /* lazy check for lazy + header handling. The + header packets aren't + audio, so if/when we + submit them, + vorbis_synthesis will + reject them */ + + /* suck in the synthesis data and track bitrate */ + { + int oldsamples=vorbis_synthesis_pcmout(&vf->vd,NULL); + /* for proper use of libvorbis within libvorbisfile, + oldsamples will always be zero. */ + if(oldsamples)return(OV_EFAULT); + + vorbis_synthesis_blockin(&vf->vd,&vf->vb); + vf->samptrack+=vorbis_synthesis_pcmout(&vf->vd,NULL)-oldsamples; + vf->bittrack+=op_ptr->bytes*8; + } + + /* update the pcm offset. */ + if(granulepos!=-1 && !op_ptr->e_o_s){ + int link=(vf->seekable?vf->current_link:0); + int i,samples; + + /* this packet has a pcm_offset on it (the last packet + completed on a page carries the offset) After processing + (above), we know the pcm position of the *last* sample + ready to be returned. Find the offset of the *first* + + As an aside, this trick is inaccurate if we begin + reading anew right at the last page; the end-of-stream + granulepos declares the last frame in the stream, and the + last packet of the last page may be a partial frame. + So, we need a previous granulepos from an in-sequence page + to have a reference point. Thus the !op_ptr->e_o_s clause + above */ + + if(vf->seekable && link>0) + granulepos-=vf->pcmlengths[link*2]; + if(granulepos<0)granulepos=0; /* actually, this + shouldn't be possible + here unless the stream + is very broken */ + + samples=vorbis_synthesis_pcmout(&vf->vd,NULL); + + granulepos-=samples; + for(i=0;ipcmlengths[i*2+1]; + vf->pcm_offset=granulepos; + } + return(1); + } + } + else + break; + } + } + + if(vf->ready_state>=OPENED){ + ogg_int64_t ret; + + while(1){ + /* the loop is not strictly necessary, but there's no sense in + doing the extra checks of the larger loop for the common + case in a multiplexed bistream where the page is simply + part of a different logical bitstream; keep reading until + we get one with the correct serialno */ + + if(!readp)return(0); + if((ret=_get_next_page(vf,&og,-1))<0){ + return(OV_EOF); /* eof. leave unitialized */ + } + + /* bitrate tracking; add the header's bytes here, the body bytes + are done by packet above */ + vf->bittrack+=og.header_len*8; + + if(vf->ready_state==INITSET){ + if(vf->current_serialno!=ogg_page_serialno(&og)){ + + /* two possibilities: + 1) our decoding just traversed a bitstream boundary + 2) another stream is multiplexed into this logical section? */ + + if(ogg_page_bos(&og)){ + /* boundary case */ + if(!spanp) + return(OV_EOF); + + _decode_clear(vf); + + if(!vf->seekable){ + vorbis_info_clear(vf->vi); + vorbis_comment_clear(vf->vc); + } + break; + + }else + continue; /* possibility #2 */ + } + } + + break; + } + } + + /* Do we need to load a new machine before submitting the page? */ + /* This is different in the seekable and non-seekable cases. + + In the seekable case, we already have all the header + information loaded and cached; we just initialize the machine + with it and continue on our merry way. + + In the non-seekable (streaming) case, we'll only be at a + boundary if we just left the previous logical bitstream and + we're now nominally at the header of the next bitstream + */ + + if(vf->ready_state!=INITSET){ + int link; + + if(vf->ready_stateseekable){ + long serialno = ogg_page_serialno(&og); + + /* match the serialno to bitstream section. We use this rather than + offset positions to avoid problems near logical bitstream + boundaries */ + + for(link=0;linklinks;link++) + if(vf->serialnos[link]==serialno)break; + + if(link==vf->links) continue; /* not the desired Vorbis + bitstream section; keep + trying */ + + vf->current_serialno=serialno; + vf->current_link=link; + + ogg_stream_reset_serialno(&vf->os,vf->current_serialno); + vf->ready_state=STREAMSET; + + }else{ + /* we're streaming */ + /* fetch the three header packets, build the info struct */ + + int ret=_fetch_headers(vf,vf->vi,vf->vc,&vf->current_serialno,&og); + if(ret)return(ret); + vf->current_link++; + link=0; + } + } + + { + int ret=_make_decode_ready(vf); + if(ret<0)return ret; + } + } + + /* the buffered page is the data we want, and we're ready for it; + add it to the stream state */ + ogg_stream_pagein(&vf->os,&og); + + } +} + +/* if, eg, 64 bit stdio is configured by default, this will build with + fseek64 */ +static int _fseek64_wrap(FILE *f,ogg_int64_t off,int whence){ + if(f==NULL)return(-1); + return fseek(f,off,whence); +} + +static int _ov_open1(void *f,OggVorbis_File *vf,char *initial, + long ibytes, ov_callbacks callbacks){ + int offsettest=((f && callbacks.seek_func)?callbacks.seek_func(f,0,SEEK_CUR):-1); + int ret; + + memset(vf,0,sizeof(*vf)); + vf->datasource=f; + vf->callbacks = callbacks; + + /* init the framing state */ + ogg_sync_init(&vf->oy); + + /* perhaps some data was previously read into a buffer for testing + against other stream types. Allow initialization from this + previously read data (as we may be reading from a non-seekable + stream) */ + if(initial){ + char *buffer=ogg_sync_buffer(&vf->oy,ibytes); + memcpy(buffer,initial,ibytes); + ogg_sync_wrote(&vf->oy,ibytes); + } + + /* can we seek? Stevens suggests the seek test was portable */ + if(offsettest!=-1)vf->seekable=1; + + /* No seeking yet; Set up a 'single' (current) logical bitstream + entry for partial open */ + vf->links=1; + vf->vi=_ogg_calloc(vf->links,sizeof(*vf->vi)); + vf->vc=_ogg_calloc(vf->links,sizeof(*vf->vc)); + ogg_stream_init(&vf->os,-1); /* fill in the serialno later */ + + /* Try to fetch the headers, maintaining all the storage */ + if((ret=_fetch_headers(vf,vf->vi,vf->vc,&vf->current_serialno,NULL))<0){ + vf->datasource=NULL; + ov_clear(vf); + }else + vf->ready_state=PARTOPEN; + return(ret); +} + +static int _ov_open2(OggVorbis_File *vf){ + if(vf->ready_state != PARTOPEN) return OV_EINVAL; + vf->ready_state=OPENED; + if(vf->seekable){ + int ret=_open_seekable2(vf); + if(ret){ + vf->datasource=NULL; + ov_clear(vf); + } + return(ret); + }else + vf->ready_state=STREAMSET; + + return 0; +} + + +/* clear out the OggVorbis_File struct */ +int ov_clear(OggVorbis_File *vf){ + if(vf){ + vorbis_block_clear(&vf->vb); + vorbis_dsp_clear(&vf->vd); + ogg_stream_clear(&vf->os); + + if(vf->vi && vf->links){ + int i; + for(i=0;ilinks;i++){ + vorbis_info_clear(vf->vi+i); + vorbis_comment_clear(vf->vc+i); + } + _ogg_free(vf->vi); + _ogg_free(vf->vc); + } + if(vf->dataoffsets)_ogg_free(vf->dataoffsets); + if(vf->pcmlengths)_ogg_free(vf->pcmlengths); + if(vf->serialnos)_ogg_free(vf->serialnos); + if(vf->offsets)_ogg_free(vf->offsets); + ogg_sync_clear(&vf->oy); + if(vf->datasource && vf->callbacks.close_func) + (vf->callbacks.close_func)(vf->datasource); + memset(vf,0,sizeof(*vf)); + } +#ifdef DEBUG_LEAKS + _VDBG_dump(); +#endif + return(0); +} + +/* inspects the OggVorbis file and finds/documents all the logical + bitstreams contained in it. Tries to be tolerant of logical + bitstream sections that are truncated/woogie. + + return: -1) error + 0) OK +*/ + +int ov_open_callbacks(void *f,OggVorbis_File *vf,char *initial,long ibytes, + ov_callbacks callbacks){ + int ret=_ov_open1(f,vf,initial,ibytes,callbacks); + if(ret)return ret; + return _ov_open2(vf); +} + +int ov_open(FILE *f,OggVorbis_File *vf,char *initial,long ibytes){ + ov_callbacks callbacks = { + (size_t (*)(void *, size_t, size_t, void *)) fread, + (int (*)(void *, ogg_int64_t, int)) _fseek64_wrap, + (int (*)(void *)) fclose, + (long (*)(void *)) ftell + }; + + return ov_open_callbacks((void *)f, vf, initial, ibytes, callbacks); +} + +int ov_fopen(char *path,OggVorbis_File *vf){ + int ret; + FILE *f = fopen(path,"rb"); + if(!f) return -1; + + ret = ov_open(f,vf,NULL,0); + if(ret) fclose(f); + return ret; +} + + +/* cheap hack for game usage where downsampling is desirable; there's + no need for SRC as we can just do it cheaply in libvorbis. */ + +int ov_halfrate(OggVorbis_File *vf,int flag){ + int i; + if(vf->vi==NULL)return OV_EINVAL; + if(!vf->seekable)return OV_EINVAL; + if(vf->ready_state>=STREAMSET) + _decode_clear(vf); /* clear out stream state; later on libvorbis + will be able to swap this on the fly, but + for now dumping the decode machine is needed + to reinit the MDCT lookups. 1.1 libvorbis + is planned to be able to switch on the fly */ + + for(i=0;ilinks;i++){ + if(vorbis_synthesis_halfrate(vf->vi+i,flag)){ + ov_halfrate(vf,0); + return OV_EINVAL; + } + } + return 0; +} + +int ov_halfrate_p(OggVorbis_File *vf){ + if(vf->vi==NULL)return OV_EINVAL; + return vorbis_synthesis_halfrate_p(vf->vi); +} + +/* Only partially open the vorbis file; test for Vorbisness, and load + the headers for the first chain. Do not seek (although test for + seekability). Use ov_test_open to finish opening the file, else + ov_clear to close/free it. Same return codes as open. */ + +int ov_test_callbacks(void *f,OggVorbis_File *vf,char *initial,long ibytes, + ov_callbacks callbacks) +{ + return _ov_open1(f,vf,initial,ibytes,callbacks); +} + +int ov_test(FILE *f,OggVorbis_File *vf,char *initial,long ibytes){ + ov_callbacks callbacks = { + (size_t (*)(void *, size_t, size_t, void *)) fread, + (int (*)(void *, ogg_int64_t, int)) _fseek64_wrap, + (int (*)(void *)) fclose, + (long (*)(void *)) ftell + }; + + return ov_test_callbacks((void *)f, vf, initial, ibytes, callbacks); +} + +int ov_test_open(OggVorbis_File *vf){ + if(vf->ready_state!=PARTOPEN)return(OV_EINVAL); + return _ov_open2(vf); +} + +/* How many logical bitstreams in this physical bitstream? */ +long ov_streams(OggVorbis_File *vf){ + return vf->links; +} + +/* Is the FILE * associated with vf seekable? */ +long ov_seekable(OggVorbis_File *vf){ + return vf->seekable; +} + +/* returns the bitrate for a given logical bitstream or the entire + physical bitstream. If the file is open for random access, it will + find the *actual* average bitrate. If the file is streaming, it + returns the nominal bitrate (if set) else the average of the + upper/lower bounds (if set) else -1 (unset). + + If you want the actual bitrate field settings, get them from the + vorbis_info structs */ + +long ov_bitrate(OggVorbis_File *vf,int i){ + if(vf->ready_state=vf->links)return(OV_EINVAL); + if(!vf->seekable && i!=0)return(ov_bitrate(vf,0)); + if(i<0){ + ogg_int64_t bits=0; + int i; + float br; + for(i=0;ilinks;i++) + bits+=(vf->offsets[i+1]-vf->dataoffsets[i])*8; + /* This once read: return(rint(bits/ov_time_total(vf,-1))); + * gcc 3.x on x86 miscompiled this at optimisation level 2 and above, + * so this is slightly transformed to make it work. + */ + br = bits/ov_time_total(vf,-1); + return(rint(br)); + }else{ + if(vf->seekable){ + /* return the actual bitrate */ + return(rint((vf->offsets[i+1]-vf->dataoffsets[i])*8/ov_time_total(vf,i))); + }else{ + /* return nominal if set */ + if(vf->vi[i].bitrate_nominal>0){ + return vf->vi[i].bitrate_nominal; + }else{ + if(vf->vi[i].bitrate_upper>0){ + if(vf->vi[i].bitrate_lower>0){ + return (vf->vi[i].bitrate_upper+vf->vi[i].bitrate_lower)/2; + }else{ + return vf->vi[i].bitrate_upper; + } + } + return(OV_FALSE); + } + } + } +} + +/* returns the actual bitrate since last call. returns -1 if no + additional data to offer since last call (or at beginning of stream), + EINVAL if stream is only partially open +*/ +long ov_bitrate_instant(OggVorbis_File *vf){ + int link=(vf->seekable?vf->current_link:0); + long ret; + if(vf->ready_statesamptrack==0)return(OV_FALSE); + ret=vf->bittrack/vf->samptrack*vf->vi[link].rate+.5; + vf->bittrack=0.f; + vf->samptrack=0.f; + return(ret); +} + +/* Guess */ +long ov_serialnumber(OggVorbis_File *vf,int i){ + if(i>=vf->links)return(ov_serialnumber(vf,vf->links-1)); + if(!vf->seekable && i>=0)return(ov_serialnumber(vf,-1)); + if(i<0){ + return(vf->current_serialno); + }else{ + return(vf->serialnos[i]); + } +} + +/* returns: total raw (compressed) length of content if i==-1 + raw (compressed) length of that logical bitstream for i==0 to n + OV_EINVAL if the stream is not seekable (we can't know the length) + or if stream is only partially open +*/ +ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i){ + if(vf->ready_stateseekable || i>=vf->links)return(OV_EINVAL); + if(i<0){ + ogg_int64_t acc=0; + int i; + for(i=0;ilinks;i++) + acc+=ov_raw_total(vf,i); + return(acc); + }else{ + return(vf->offsets[i+1]-vf->offsets[i]); + } +} + +/* returns: total PCM length (samples) of content if i==-1 PCM length + (samples) of that logical bitstream for i==0 to n + OV_EINVAL if the stream is not seekable (we can't know the + length) or only partially open +*/ +ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i){ + if(vf->ready_stateseekable || i>=vf->links)return(OV_EINVAL); + if(i<0){ + ogg_int64_t acc=0; + int i; + for(i=0;ilinks;i++) + acc+=ov_pcm_total(vf,i); + return(acc); + }else{ + return(vf->pcmlengths[i*2+1]); + } +} + +/* returns: total seconds of content if i==-1 + seconds in that logical bitstream for i==0 to n + OV_EINVAL if the stream is not seekable (we can't know the + length) or only partially open +*/ +double ov_time_total(OggVorbis_File *vf,int i){ + if(vf->ready_stateseekable || i>=vf->links)return(OV_EINVAL); + if(i<0){ + double acc=0; + int i; + for(i=0;ilinks;i++) + acc+=ov_time_total(vf,i); + return(acc); + }else{ + return((double)(vf->pcmlengths[i*2+1])/vf->vi[i].rate); + } +} + +/* seek to an offset relative to the *compressed* data. This also + scans packets to update the PCM cursor. It will cross a logical + bitstream boundary, but only if it can't get any packets out of the + tail of the bitstream we seek to (so no surprises). + + returns zero on success, nonzero on failure */ + +int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos){ + ogg_stream_state work_os; + int ret; + + if(vf->ready_stateseekable) + return(OV_ENOSEEK); /* don't dump machine if we can't seek */ + + if(pos<0 || pos>vf->end)return(OV_EINVAL); + + /* don't yet clear out decoding machine (if it's initialized), in + the case we're in the same link. Restart the decode lapping, and + let _fetch_and_process_packet deal with a potential bitstream + boundary */ + vf->pcm_offset=-1; + ogg_stream_reset_serialno(&vf->os, + vf->current_serialno); /* must set serialno */ + vorbis_synthesis_restart(&vf->vd); + + ret=_seek_helper(vf,pos); + if(ret)goto seek_error; + + /* we need to make sure the pcm_offset is set, but we don't want to + advance the raw cursor past good packets just to get to the first + with a granulepos. That's not equivalent behavior to beginning + decoding as immediately after the seek position as possible. + + So, a hack. We use two stream states; a local scratch state and + the shared vf->os stream state. We use the local state to + scan, and the shared state as a buffer for later decode. + + Unfortuantely, on the last page we still advance to last packet + because the granulepos on the last page is not necessarily on a + packet boundary, and we need to make sure the granpos is + correct. + */ + + { + ogg_page og; + ogg_packet op; + int lastblock=0; + int accblock=0; + int thisblock=0; + int eosflag=0; + + ogg_stream_init(&work_os,vf->current_serialno); /* get the memory ready */ + ogg_stream_reset(&work_os); /* eliminate the spurious OV_HOLE + return from not necessarily + starting from the beginning */ + + while(1){ + if(vf->ready_state>=STREAMSET){ + /* snarf/scan a packet if we can */ + int result=ogg_stream_packetout(&work_os,&op); + + if(result>0){ + + if(vf->vi[vf->current_link].codec_setup){ + thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op); + if(thisblock<0){ + ogg_stream_packetout(&vf->os,NULL); + thisblock=0; + }else{ + + if(eosflag) + ogg_stream_packetout(&vf->os,NULL); + else + if(lastblock)accblock+=(lastblock+thisblock)>>2; + } + + if(op.granulepos!=-1){ + int i,link=vf->current_link; + ogg_int64_t granulepos=op.granulepos-vf->pcmlengths[link*2]; + if(granulepos<0)granulepos=0; + + for(i=0;ipcmlengths[i*2+1]; + vf->pcm_offset=granulepos-accblock; + break; + } + lastblock=thisblock; + continue; + }else + ogg_stream_packetout(&vf->os,NULL); + } + } + + if(!lastblock){ + if(_get_next_page(vf,&og,-1)<0){ + vf->pcm_offset=ov_pcm_total(vf,-1); + break; + } + }else{ + /* huh? Bogus stream with packets but no granulepos */ + vf->pcm_offset=-1; + break; + } + + /* has our decoding just traversed a bitstream boundary? */ + if(vf->ready_state>=STREAMSET){ + if(vf->current_serialno!=ogg_page_serialno(&og)){ + + /* two possibilities: + 1) our decoding just traversed a bitstream boundary + 2) another stream is multiplexed into this logical section? */ + + if(ogg_page_bos(&og)){ + /* we traversed */ + _decode_clear(vf); /* clear out stream state */ + ogg_stream_clear(&work_os); + } /* else, do nothing; next loop will scoop another page */ + } + } + + if(vf->ready_statelinks;link++) + if(vf->serialnos[link]==serialno)break; + + if(link==vf->links) continue; /* not the desired Vorbis + bitstream section; keep + trying */ + vf->current_link=link; + vf->current_serialno=serialno; + ogg_stream_reset_serialno(&vf->os,serialno); + ogg_stream_reset_serialno(&work_os,serialno); + vf->ready_state=STREAMSET; + + } + + ogg_stream_pagein(&vf->os,&og); + ogg_stream_pagein(&work_os,&og); + eosflag=ogg_page_eos(&og); + } + } + + ogg_stream_clear(&work_os); + vf->bittrack=0.f; + vf->samptrack=0.f; + return(0); + + seek_error: + /* dump the machine so we're in a known state */ + vf->pcm_offset=-1; + ogg_stream_clear(&work_os); + _decode_clear(vf); + return OV_EBADLINK; +} + +/* Page granularity seek (faster than sample granularity because we + don't do the last bit of decode to find a specific sample). + + Seek to the last [granule marked] page preceeding the specified pos + location, such that decoding past the returned point will quickly + arrive at the requested position. */ +int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos){ + int link=-1; + ogg_int64_t result=0; + ogg_int64_t total=ov_pcm_total(vf,-1); + + if(vf->ready_stateseekable)return(OV_ENOSEEK); + + if(pos<0 || pos>total)return(OV_EINVAL); + + /* which bitstream section does this pcm offset occur in? */ + for(link=vf->links-1;link>=0;link--){ + total-=vf->pcmlengths[link*2+1]; + if(pos>=total)break; + } + + /* search within the logical bitstream for the page with the highest + pcm_pos preceeding (or equal to) pos. There is a danger here; + missing pages or incorrect frame number information in the + bitstream could make our task impossible. Account for that (it + would be an error condition) */ + + /* new search algorithm by HB (Nicholas Vinen) */ + { + ogg_int64_t end=vf->offsets[link+1]; + ogg_int64_t begin=vf->offsets[link]; + ogg_int64_t begintime = vf->pcmlengths[link*2]; + ogg_int64_t endtime = vf->pcmlengths[link*2+1]+begintime; + ogg_int64_t target=pos-total+begintime; + ogg_int64_t best=begin; + + ogg_page og; + while(beginoffset); + if(result==OV_EREAD) goto seek_error; + if(result<0){ + if(bisect<=begin+1) + end=begin; /* found it */ + else{ + if(bisect==0) goto seek_error; + bisect-=CHUNKSIZE; + if(bisect<=begin)bisect=begin+1; + result=_seek_helper(vf,bisect); + if(result) goto seek_error; + } + }else{ + ogg_int64_t granulepos; + + if(ogg_page_serialno(&og)!=vf->serialnos[link]) + continue; + + granulepos=ogg_page_granulepos(&og); + if(granulepos==-1)continue; + + if(granuleposoffset; /* raw offset of next page */ + begintime=granulepos; + + if(target-begintime>44100)break; + bisect=begin; /* *not* begin + 1 */ + }else{ + if(bisect<=begin+1) + end=begin; /* found it */ + else{ + if(end==vf->offset){ /* we're pretty close - we'd be stuck in */ + end=result; + bisect-=CHUNKSIZE; /* an endless loop otherwise. */ + if(bisect<=begin)bisect=begin+1; + result=_seek_helper(vf,bisect); + if(result) goto seek_error; + }else{ + end=bisect; + endtime=granulepos; + break; + } + } + } + } + } + } + + /* found our page. seek to it, update pcm offset. Easier case than + raw_seek, don't keep packets preceeding granulepos. */ + { + ogg_page og; + ogg_packet op; + + /* seek */ + result=_seek_helper(vf,best); + vf->pcm_offset=-1; + if(result) goto seek_error; + result=_get_next_page(vf,&og,-1); + if(result<0) goto seek_error; + + if(link!=vf->current_link){ + /* Different link; dump entire decode machine */ + _decode_clear(vf); + + vf->current_link=link; + vf->current_serialno=vf->serialnos[link]; + vf->ready_state=STREAMSET; + + }else{ + vorbis_synthesis_restart(&vf->vd); + } + + ogg_stream_reset_serialno(&vf->os,vf->current_serialno); + ogg_stream_pagein(&vf->os,&og); + + /* pull out all but last packet; the one with granulepos */ + while(1){ + result=ogg_stream_packetpeek(&vf->os,&op); + if(result==0){ + /* !!! the packet finishing this page originated on a + preceeding page. Keep fetching previous pages until we + get one with a granulepos or without the 'continued' flag + set. Then just use raw_seek for simplicity. */ + + result=_seek_helper(vf,best); + if(result<0) goto seek_error; + + while(1){ + result=_get_prev_page(vf,&og); + if(result<0) goto seek_error; + if(ogg_page_serialno(&og)==vf->current_serialno && + (ogg_page_granulepos(&og)>-1 || + !ogg_page_continued(&og))){ + return ov_raw_seek(vf,result); + } + vf->offset=result; + } + } + if(result<0){ + result = OV_EBADPACKET; + goto seek_error; + } + if(op.granulepos!=-1){ + vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2]; + if(vf->pcm_offset<0)vf->pcm_offset=0; + vf->pcm_offset+=total; + break; + }else + result=ogg_stream_packetout(&vf->os,NULL); + } + } + } + + /* verify result */ + if(vf->pcm_offset>pos || pos>ov_pcm_total(vf,-1)){ + result=OV_EFAULT; + goto seek_error; + } + vf->bittrack=0.f; + vf->samptrack=0.f; + return(0); + + seek_error: + /* dump machine so we're in a known state */ + vf->pcm_offset=-1; + _decode_clear(vf); + return (int)result; +} + +/* seek to a sample offset relative to the decompressed pcm stream + returns zero on success, nonzero on failure */ + +int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos){ + int thisblock,lastblock=0; + int ret=ov_pcm_seek_page(vf,pos); + if(ret<0)return(ret); + if((ret=_make_decode_ready(vf)))return ret; + + /* discard leading packets we don't need for the lapping of the + position we want; don't decode them */ + + while(1){ + ogg_packet op; + ogg_page og; + + int ret=ogg_stream_packetpeek(&vf->os,&op); + if(ret>0){ + thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op); + if(thisblock<0){ + ogg_stream_packetout(&vf->os,NULL); + continue; /* non audio packet */ + } + if(lastblock)vf->pcm_offset+=(lastblock+thisblock)>>2; + + if(vf->pcm_offset+((thisblock+ + vorbis_info_blocksize(vf->vi,1))>>2)>=pos)break; + + /* remove the packet from packet queue and track its granulepos */ + ogg_stream_packetout(&vf->os,NULL); + vorbis_synthesis_trackonly(&vf->vb,&op); /* set up a vb with + only tracking, no + pcm_decode */ + vorbis_synthesis_blockin(&vf->vd,&vf->vb); + + /* end of logical stream case is hard, especially with exact + length positioning. */ + + if(op.granulepos>-1){ + int i; + /* always believe the stream markers */ + vf->pcm_offset=op.granulepos-vf->pcmlengths[vf->current_link*2]; + if(vf->pcm_offset<0)vf->pcm_offset=0; + for(i=0;icurrent_link;i++) + vf->pcm_offset+=vf->pcmlengths[i*2+1]; + } + + lastblock=thisblock; + + }else{ + if(ret<0 && ret!=OV_HOLE)break; + + /* suck in a new page */ + if(_get_next_page(vf,&og,-1)<0)break; + if(ogg_page_bos(&og))_decode_clear(vf); + + if(vf->ready_statelinks;link++) + if(vf->serialnos[link]==serialno)break; + if(link==vf->links) continue; + vf->current_link=link; + + vf->ready_state=STREAMSET; + vf->current_serialno=ogg_page_serialno(&og); + ogg_stream_reset_serialno(&vf->os,serialno); + ret=_make_decode_ready(vf); + if(ret)return ret; + lastblock=0; + } + + ogg_stream_pagein(&vf->os,&og); + } + } + + vf->bittrack=0.f; + vf->samptrack=0.f; + /* discard samples until we reach the desired position. Crossing a + logical bitstream boundary with abandon is OK. */ + while(vf->pcm_offsetpcm_offset; + long samples=vorbis_synthesis_pcmout(&vf->vd,NULL); + + if(samples>target)samples=target; + vorbis_synthesis_read(&vf->vd,samples); + vf->pcm_offset+=samples; + + if(samplespcm_offset=ov_pcm_total(vf,-1); /* eof */ + } + return 0; +} + +/* seek to a playback time relative to the decompressed pcm stream + returns zero on success, nonzero on failure */ +int ov_time_seek(OggVorbis_File *vf,double seconds){ + /* translate time to PCM position and call ov_pcm_seek */ + + int link=-1; + ogg_int64_t pcm_total=0; + double time_total=0.; + + if(vf->ready_stateseekable)return(OV_ENOSEEK); + if(seconds<0)return(OV_EINVAL); + + /* which bitstream section does this time offset occur in? */ + for(link=0;linklinks;link++){ + double addsec = ov_time_total(vf,link); + if(secondspcmlengths[link*2+1]; + } + + if(link==vf->links)return(OV_EINVAL); + + /* enough information to convert time offset to pcm offset */ + { + ogg_int64_t target=pcm_total+(seconds-time_total)*vf->vi[link].rate; + return(ov_pcm_seek(vf,target)); + } +} + +/* page-granularity version of ov_time_seek + returns zero on success, nonzero on failure */ +int ov_time_seek_page(OggVorbis_File *vf,double seconds){ + /* translate time to PCM position and call ov_pcm_seek */ + + int link=-1; + ogg_int64_t pcm_total=0; + double time_total=0.; + + if(vf->ready_stateseekable)return(OV_ENOSEEK); + if(seconds<0)return(OV_EINVAL); + + /* which bitstream section does this time offset occur in? */ + for(link=0;linklinks;link++){ + double addsec = ov_time_total(vf,link); + if(secondspcmlengths[link*2+1]; + } + + if(link==vf->links)return(OV_EINVAL); + + /* enough information to convert time offset to pcm offset */ + { + ogg_int64_t target=pcm_total+(seconds-time_total)*vf->vi[link].rate; + return(ov_pcm_seek_page(vf,target)); + } +} + +/* tell the current stream offset cursor. Note that seek followed by + tell will likely not give the set offset due to caching */ +ogg_int64_t ov_raw_tell(OggVorbis_File *vf){ + if(vf->ready_stateoffset); +} + +/* return PCM offset (sample) of next PCM sample to be read */ +ogg_int64_t ov_pcm_tell(OggVorbis_File *vf){ + if(vf->ready_statepcm_offset); +} + +/* return time offset (seconds) of next PCM sample to be read */ +double ov_time_tell(OggVorbis_File *vf){ + int link=0; + ogg_int64_t pcm_total=0; + double time_total=0.f; + + if(vf->ready_stateseekable){ + pcm_total=ov_pcm_total(vf,-1); + time_total=ov_time_total(vf,-1); + + /* which bitstream section does this time offset occur in? */ + for(link=vf->links-1;link>=0;link--){ + pcm_total-=vf->pcmlengths[link*2+1]; + time_total-=ov_time_total(vf,link); + if(vf->pcm_offset>=pcm_total)break; + } + } + + return((double)time_total+(double)(vf->pcm_offset-pcm_total)/vf->vi[link].rate); +} + +/* link: -1) return the vorbis_info struct for the bitstream section + currently being decoded + 0-n) to request information for a specific bitstream section + + In the case of a non-seekable bitstream, any call returns the + current bitstream. NULL in the case that the machine is not + initialized */ + +vorbis_info *ov_info(OggVorbis_File *vf,int link){ + if(vf->seekable){ + if(link<0) + if(vf->ready_state>=STREAMSET) + return vf->vi+vf->current_link; + else + return vf->vi; + else + if(link>=vf->links) + return NULL; + else + return vf->vi+link; + }else{ + return vf->vi; + } +} + +/* grr, strong typing, grr, no templates/inheritence, grr */ +vorbis_comment *ov_comment(OggVorbis_File *vf,int link){ + if(vf->seekable){ + if(link<0) + if(vf->ready_state>=STREAMSET) + return vf->vc+vf->current_link; + else + return vf->vc; + else + if(link>=vf->links) + return NULL; + else + return vf->vc+link; + }else{ + return vf->vc; + } +} + +static int host_is_big_endian() { + ogg_int32_t pattern = 0xfeedface; /* deadbeef */ + unsigned char *bytewise = (unsigned char *)&pattern; + if (bytewise[0] == 0xfe) return 1; + return 0; +} + +/* up to this point, everything could more or less hide the multiple + logical bitstream nature of chaining from the toplevel application + if the toplevel application didn't particularly care. However, at + the point that we actually read audio back, the multiple-section + nature must surface: Multiple bitstream sections do not necessarily + have to have the same number of channels or sampling rate. + + ov_read returns the sequential logical bitstream number currently + being decoded along with the PCM data in order that the toplevel + application can take action on channel/sample rate changes. This + number will be incremented even for streamed (non-seekable) streams + (for seekable streams, it represents the actual logical bitstream + index within the physical bitstream. Note that the accessor + functions above are aware of this dichotomy). + + input values: buffer) a buffer to hold packed PCM data for return + length) the byte length requested to be placed into buffer + bigendianp) should the data be packed LSB first (0) or + MSB first (1) + word) word size for output. currently 1 (byte) or + 2 (16 bit short) + + return values: <0) error/hole in data (OV_HOLE), partial open (OV_EINVAL) + 0) EOF + n) number of bytes of PCM actually returned. The + below works on a packet-by-packet basis, so the + return length is not related to the 'length' passed + in, just guaranteed to fit. + + *section) set to the logical bitstream number */ + +long ov_read(OggVorbis_File *vf,char *buffer,int length, + int bigendianp,int word,int sgned,int *bitstream){ + int i,j; + int host_endian = host_is_big_endian(); + + float **pcm; + long samples; + + if(vf->ready_stateready_state==INITSET){ + samples=vorbis_synthesis_pcmout(&vf->vd,&pcm); + if(samples)break; + } + + /* suck in another packet */ + { + int ret=_fetch_and_process_packet(vf,NULL,1,1); + if(ret==OV_EOF) + return(0); + if(ret<=0) + return(ret); + } + + } + + if(samples>0){ + + /* yay! proceed to pack data into the byte buffer */ + + long channels=ov_info(vf,-1)->channels; + long bytespersample=word * channels; + vorbis_fpu_control fpu; + if(samples>length/bytespersample)samples=length/bytespersample; + + if(samples <= 0) + return OV_EINVAL; + + /* a tight loop to pack each size */ + { + int val; + if(word==1){ + int off=(sgned?0:128); + vorbis_fpu_setround(&fpu); + for(j=0;j127)val=127; + else if(val<-128)val=-128; + *buffer++=val+off; + } + vorbis_fpu_restore(fpu); + }else{ + int off=(sgned?0:32768); + + if(host_endian==bigendianp){ + if(sgned){ + + vorbis_fpu_setround(&fpu); + for(i=0;i32767)val=32767; + else if(val<-32768)val=-32768; + *dest=val; + dest+=channels; + } + } + vorbis_fpu_restore(fpu); + + }else{ + + vorbis_fpu_setround(&fpu); + for(i=0;i32767)val=32767; + else if(val<-32768)val=-32768; + *dest=val+off; + dest+=channels; + } + } + vorbis_fpu_restore(fpu); + + } + }else if(bigendianp){ + + vorbis_fpu_setround(&fpu); + for(j=0;j32767)val=32767; + else if(val<-32768)val=-32768; + val+=off; + *buffer++=(val>>8); + *buffer++=(val&0xff); + } + vorbis_fpu_restore(fpu); + + }else{ + int val; + vorbis_fpu_setround(&fpu); + for(j=0;j32767)val=32767; + else if(val<-32768)val=-32768; + val+=off; + *buffer++=(val&0xff); + *buffer++=(val>>8); + } + vorbis_fpu_restore(fpu); + + } + } + } + + vorbis_synthesis_read(&vf->vd,samples); + vf->pcm_offset+=samples; + if(bitstream)*bitstream=vf->current_link; + return(samples*bytespersample); + }else{ + return(samples); + } +} + +/* input values: pcm_channels) a float vector per channel of output + length) the sample length being read by the app + + return values: <0) error/hole in data (OV_HOLE), partial open (OV_EINVAL) + 0) EOF + n) number of samples of PCM actually returned. The + below works on a packet-by-packet basis, so the + return length is not related to the 'length' passed + in, just guaranteed to fit. + + *section) set to the logical bitstream number */ + + + +long ov_read_float(OggVorbis_File *vf,float ***pcm_channels,int length, + int *bitstream){ + + if(vf->ready_stateready_state==INITSET){ + float **pcm; + long samples=vorbis_synthesis_pcmout(&vf->vd,&pcm); + if(samples){ + if(pcm_channels)*pcm_channels=pcm; + if(samples>length)samples=length; + vorbis_synthesis_read(&vf->vd,samples); + vf->pcm_offset+=samples; + if(bitstream)*bitstream=vf->current_link; + return samples; + + } + } + + /* suck in another packet */ + { + int ret=_fetch_and_process_packet(vf,NULL,1,1); + if(ret==OV_EOF)return(0); + if(ret<=0)return(ret); + } + + } +} + +extern float *vorbis_window(vorbis_dsp_state *v,int W); +extern void _analysis_output_always(char *base,int i,float *v,int n,int bark,int dB, + ogg_int64_t off); + +static void _ov_splice(float **pcm,float **lappcm, + int n1, int n2, + int ch1, int ch2, + float *w1, float *w2){ + int i,j; + float *w=w1; + int n=n1; + + if(n1>n2){ + n=n2; + w=w2; + } + + /* splice */ + for(j=0;jready_state==INITSET)break; + /* suck in another packet */ + { + int ret=_fetch_and_process_packet(vf,NULL,1,0); + if(ret<0 && ret!=OV_HOLE)return(ret); + } + } + return 0; +} + +/* make sure vf is INITSET and that we have a primed buffer; if + we're crosslapping at a stream section boundary, this also makes + sure we're sanity checking against the right stream information */ +static int _ov_initprime(OggVorbis_File *vf){ + vorbis_dsp_state *vd=&vf->vd; + while(1){ + if(vf->ready_state==INITSET) + if(vorbis_synthesis_pcmout(vd,NULL))break; + + /* suck in another packet */ + { + int ret=_fetch_and_process_packet(vf,NULL,1,0); + if(ret<0 && ret!=OV_HOLE)return(ret); + } + } + return 0; +} + +/* grab enough data for lapping from vf; this may be in the form of + unreturned, already-decoded pcm, remaining PCM we will need to + decode, or synthetic postextrapolation from last packets. */ +static void _ov_getlap(OggVorbis_File *vf,vorbis_info *vi,vorbis_dsp_state *vd, + float **lappcm,int lapsize){ + int lapcount=0,i; + float **pcm; + + /* try first to decode the lapping data */ + while(lapcountlapsize-lapcount)samples=lapsize-lapcount; + for(i=0;ichannels;i++) + memcpy(lappcm[i]+lapcount,pcm[i],sizeof(**pcm)*samples); + lapcount+=samples; + vorbis_synthesis_read(vd,samples); + }else{ + /* suck in another packet */ + int ret=_fetch_and_process_packet(vf,NULL,1,0); /* do *not* span */ + if(ret==OV_EOF)break; + } + } + if(lapcountvd,&pcm); + if(samples==0){ + for(i=0;ichannels;i++) + memset(lappcm[i]+lapcount,0,sizeof(**pcm)*lapsize-lapcount); + lapcount=lapsize; + }else{ + if(samples>lapsize-lapcount)samples=lapsize-lapcount; + for(i=0;ichannels;i++) + memcpy(lappcm[i]+lapcount,pcm[i],sizeof(**pcm)*samples); + lapcount+=samples; + } + } +} + +/* this sets up crosslapping of a sample by using trailing data from + sample 1 and lapping it into the windowing buffer of sample 2 */ +int ov_crosslap(OggVorbis_File *vf1, OggVorbis_File *vf2){ + vorbis_info *vi1,*vi2; + float **lappcm; + float **pcm; + float *w1,*w2; + int n1,n2,i,ret,hs1,hs2; + + if(vf1==vf2)return(0); /* degenerate case */ + if(vf1->ready_stateready_statechannels); + n1=vorbis_info_blocksize(vi1,0)>>(1+hs1); + n2=vorbis_info_blocksize(vi2,0)>>(1+hs2); + w1=vorbis_window(&vf1->vd,0); + w2=vorbis_window(&vf2->vd,0); + + for(i=0;ichannels;i++) + lappcm[i]=alloca(sizeof(**lappcm)*n1); + + _ov_getlap(vf1,vi1,&vf1->vd,lappcm,n1); + + /* have a lapping buffer from vf1; now to splice it into the lapping + buffer of vf2 */ + /* consolidate and expose the buffer. */ + vorbis_synthesis_lapout(&vf2->vd,&pcm); + _analysis_output_always("pcmL",0,pcm[0],n1*2,0,0,0); + _analysis_output_always("pcmR",0,pcm[1],n1*2,0,0,0); + + /* splice */ + _ov_splice(pcm,lappcm,n1,n2,vi1->channels,vi2->channels,w1,w2); + + /* done */ + return(0); +} + +static int _ov_64_seek_lap(OggVorbis_File *vf,ogg_int64_t pos, + int (*localseek)(OggVorbis_File *,ogg_int64_t)){ + vorbis_info *vi; + float **lappcm; + float **pcm; + float *w1,*w2; + int n1,n2,ch1,ch2,hs; + int i,ret; + + if(vf->ready_statechannels; + n1=vorbis_info_blocksize(vi,0)>>(1+hs); + w1=vorbis_window(&vf->vd,0); /* window arrays from libvorbis are + persistent; even if the decode state + from this link gets dumped, this + window array continues to exist */ + + lappcm=alloca(sizeof(*lappcm)*ch1); + for(i=0;ivd,lappcm,n1); + + /* have lapping data; seek and prime the buffer */ + ret=localseek(vf,pos); + if(ret)return ret; + ret=_ov_initprime(vf); + if(ret)return(ret); + + /* Guard against cross-link changes; they're perfectly legal */ + vi=ov_info(vf,-1); + ch2=vi->channels; + n2=vorbis_info_blocksize(vi,0)>>(1+hs); + w2=vorbis_window(&vf->vd,0); + + /* consolidate and expose the buffer. */ + vorbis_synthesis_lapout(&vf->vd,&pcm); + + /* splice */ + _ov_splice(pcm,lappcm,n1,n2,ch1,ch2,w1,w2); + + /* done */ + return(0); +} + +int ov_raw_seek_lap(OggVorbis_File *vf,ogg_int64_t pos){ + return _ov_64_seek_lap(vf,pos,ov_raw_seek); +} + +int ov_pcm_seek_lap(OggVorbis_File *vf,ogg_int64_t pos){ + return _ov_64_seek_lap(vf,pos,ov_pcm_seek); +} + +int ov_pcm_seek_page_lap(OggVorbis_File *vf,ogg_int64_t pos){ + return _ov_64_seek_lap(vf,pos,ov_pcm_seek_page); +} + +static int _ov_d_seek_lap(OggVorbis_File *vf,double pos, + int (*localseek)(OggVorbis_File *,double)){ + vorbis_info *vi; + float **lappcm; + float **pcm; + float *w1,*w2; + int n1,n2,ch1,ch2,hs; + int i,ret; + + if(vf->ready_statechannels; + n1=vorbis_info_blocksize(vi,0)>>(1+hs); + w1=vorbis_window(&vf->vd,0); /* window arrays from libvorbis are + persistent; even if the decode state + from this link gets dumped, this + window array continues to exist */ + + lappcm=alloca(sizeof(*lappcm)*ch1); + for(i=0;ivd,lappcm,n1); + + /* have lapping data; seek and prime the buffer */ + ret=localseek(vf,pos); + if(ret)return ret; + ret=_ov_initprime(vf); + if(ret)return(ret); + + /* Guard against cross-link changes; they're perfectly legal */ + vi=ov_info(vf,-1); + ch2=vi->channels; + n2=vorbis_info_blocksize(vi,0)>>(1+hs); + w2=vorbis_window(&vf->vd,0); + + /* consolidate and expose the buffer. */ + vorbis_synthesis_lapout(&vf->vd,&pcm); + + /* splice */ + _ov_splice(pcm,lappcm,n1,n2,ch1,ch2,w1,w2); + + /* done */ + return(0); +} + +int ov_time_seek_lap(OggVorbis_File *vf,double pos){ + return _ov_d_seek_lap(vf,pos,ov_time_seek); +} + +int ov_time_seek_page_lap(OggVorbis_File *vf,double pos){ + return _ov_d_seek_lap(vf,pos,ov_time_seek_page); +} diff --git a/Engine/lib/libvorbis/lib/window.c b/Engine/lib/libvorbis/lib/window.c new file mode 100644 index 000000000..6cf6d40a1 --- /dev/null +++ b/Engine/lib/libvorbis/lib/window.c @@ -0,0 +1,2136 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: window functions + last mod: $Id: window.c 13293 2007-07-24 00:09:47Z xiphmont $ + + ********************************************************************/ + +#include +#include +#include "os.h" +#include "misc.h" + +static float vwin64[32] = { + 0.0009460463F, 0.0085006468F, 0.0235352254F, 0.0458950567F, + 0.0753351908F, 0.1115073077F, 0.1539457973F, 0.2020557475F, + 0.2551056759F, 0.3122276645F, 0.3724270287F, 0.4346027792F, + 0.4975789974F, 0.5601459521F, 0.6211085051F, 0.6793382689F, + 0.7338252629F, 0.7837245849F, 0.8283939355F, 0.8674186656F, + 0.9006222429F, 0.9280614787F, 0.9500073081F, 0.9669131782F, + 0.9793740220F, 0.9880792941F, 0.9937636139F, 0.9971582668F, + 0.9989462667F, 0.9997230082F, 0.9999638688F, 0.9999995525F, +}; + +static float vwin128[64] = { + 0.0002365472F, 0.0021280687F, 0.0059065254F, 0.0115626550F, + 0.0190823442F, 0.0284463735F, 0.0396300935F, 0.0526030430F, + 0.0673285281F, 0.0837631763F, 0.1018564887F, 0.1215504095F, + 0.1427789367F, 0.1654677960F, 0.1895342001F, 0.2148867160F, + 0.2414252576F, 0.2690412240F, 0.2976177952F, 0.3270303960F, + 0.3571473350F, 0.3878306189F, 0.4189369387F, 0.4503188188F, + 0.4818259135F, 0.5133064334F, 0.5446086751F, 0.5755826278F, + 0.6060816248F, 0.6359640047F, 0.6650947483F, 0.6933470543F, + 0.7206038179F, 0.7467589810F, 0.7717187213F, 0.7954024542F, + 0.8177436264F, 0.8386902831F, 0.8582053981F, 0.8762669622F, + 0.8928678298F, 0.9080153310F, 0.9217306608F, 0.9340480615F, + 0.9450138200F, 0.9546851041F, 0.9631286621F, 0.9704194171F, + 0.9766389810F, 0.9818741197F, 0.9862151938F, 0.9897546035F, + 0.9925852598F, 0.9947991032F, 0.9964856900F, 0.9977308602F, + 0.9986155015F, 0.9992144193F, 0.9995953200F, 0.9998179155F, + 0.9999331503F, 0.9999825563F, 0.9999977357F, 0.9999999720F, +}; + +static float vwin256[128] = { + 0.0000591390F, 0.0005321979F, 0.0014780301F, 0.0028960636F, + 0.0047854363F, 0.0071449926F, 0.0099732775F, 0.0132685298F, + 0.0170286741F, 0.0212513119F, 0.0259337111F, 0.0310727950F, + 0.0366651302F, 0.0427069140F, 0.0491939614F, 0.0561216907F, + 0.0634851102F, 0.0712788035F, 0.0794969160F, 0.0881331402F, + 0.0971807028F, 0.1066323515F, 0.1164803426F, 0.1267164297F, + 0.1373318534F, 0.1483173323F, 0.1596630553F, 0.1713586755F, + 0.1833933062F, 0.1957555184F, 0.2084333404F, 0.2214142599F, + 0.2346852280F, 0.2482326664F, 0.2620424757F, 0.2761000481F, + 0.2903902813F, 0.3048975959F, 0.3196059553F, 0.3344988887F, + 0.3495595160F, 0.3647705766F, 0.3801144597F, 0.3955732382F, + 0.4111287047F, 0.4267624093F, 0.4424557009F, 0.4581897696F, + 0.4739456913F, 0.4897044744F, 0.5054471075F, 0.5211546088F, + 0.5368080763F, 0.5523887395F, 0.5678780103F, 0.5832575361F, + 0.5985092508F, 0.6136154277F, 0.6285587300F, 0.6433222619F, + 0.6578896175F, 0.6722449294F, 0.6863729144F, 0.7002589187F, + 0.7138889597F, 0.7272497662F, 0.7403288154F, 0.7531143679F, + 0.7655954985F, 0.7777621249F, 0.7896050322F, 0.8011158947F, + 0.8122872932F, 0.8231127294F, 0.8335866365F, 0.8437043850F, + 0.8534622861F, 0.8628575905F, 0.8718884835F, 0.8805540765F, + 0.8888543947F, 0.8967903616F, 0.9043637797F, 0.9115773078F, + 0.9184344360F, 0.9249394562F, 0.9310974312F, 0.9369141608F, + 0.9423961446F, 0.9475505439F, 0.9523851406F, 0.9569082947F, + 0.9611289005F, 0.9650563408F, 0.9687004405F, 0.9720714191F, + 0.9751798427F, 0.9780365753F, 0.9806527301F, 0.9830396204F, + 0.9852087111F, 0.9871715701F, 0.9889398207F, 0.9905250941F, + 0.9919389832F, 0.9931929973F, 0.9942985174F, 0.9952667537F, + 0.9961087037F, 0.9968351119F, 0.9974564312F, 0.9979827858F, + 0.9984239359F, 0.9987892441F, 0.9990876435F, 0.9993276081F, + 0.9995171241F, 0.9996636648F, 0.9997741654F, 0.9998550016F, + 0.9999119692F, 0.9999502656F, 0.9999744742F, 0.9999885497F, + 0.9999958064F, 0.9999989077F, 0.9999998584F, 0.9999999983F, +}; + +static float vwin512[256] = { + 0.0000147849F, 0.0001330607F, 0.0003695946F, 0.0007243509F, + 0.0011972759F, 0.0017882983F, 0.0024973285F, 0.0033242588F, + 0.0042689632F, 0.0053312973F, 0.0065110982F, 0.0078081841F, + 0.0092223540F, 0.0107533880F, 0.0124010466F, 0.0141650703F, + 0.0160451800F, 0.0180410758F, 0.0201524373F, 0.0223789233F, + 0.0247201710F, 0.0271757958F, 0.0297453914F, 0.0324285286F, + 0.0352247556F, 0.0381335972F, 0.0411545545F, 0.0442871045F, + 0.0475306997F, 0.0508847676F, 0.0543487103F, 0.0579219038F, + 0.0616036982F, 0.0653934164F, 0.0692903546F, 0.0732937809F, + 0.0774029356F, 0.0816170305F, 0.0859352485F, 0.0903567428F, + 0.0948806375F, 0.0995060259F, 0.1042319712F, 0.1090575056F, + 0.1139816300F, 0.1190033137F, 0.1241214941F, 0.1293350764F, + 0.1346429333F, 0.1400439046F, 0.1455367974F, 0.1511203852F, + 0.1567934083F, 0.1625545735F, 0.1684025537F, 0.1743359881F, + 0.1803534820F, 0.1864536069F, 0.1926349000F, 0.1988958650F, + 0.2052349715F, 0.2116506555F, 0.2181413191F, 0.2247053313F, + 0.2313410275F, 0.2380467105F, 0.2448206500F, 0.2516610835F, + 0.2585662164F, 0.2655342226F, 0.2725632448F, 0.2796513950F, + 0.2867967551F, 0.2939973773F, 0.3012512852F, 0.3085564739F, + 0.3159109111F, 0.3233125375F, 0.3307592680F, 0.3382489922F, + 0.3457795756F, 0.3533488602F, 0.3609546657F, 0.3685947904F, + 0.3762670121F, 0.3839690896F, 0.3916987634F, 0.3994537572F, + 0.4072317788F, 0.4150305215F, 0.4228476653F, 0.4306808783F, + 0.4385278181F, 0.4463861329F, 0.4542534630F, 0.4621274424F, + 0.4700057001F, 0.4778858615F, 0.4857655502F, 0.4936423891F, + 0.5015140023F, 0.5093780165F, 0.5172320626F, 0.5250737772F, + 0.5329008043F, 0.5407107971F, 0.5485014192F, 0.5562703465F, + 0.5640152688F, 0.5717338914F, 0.5794239366F, 0.5870831457F, + 0.5947092801F, 0.6023001235F, 0.6098534829F, 0.6173671907F, + 0.6248391059F, 0.6322671161F, 0.6396491384F, 0.6469831217F, + 0.6542670475F, 0.6614989319F, 0.6686768267F, 0.6757988210F, + 0.6828630426F, 0.6898676592F, 0.6968108799F, 0.7036909564F, + 0.7105061843F, 0.7172549043F, 0.7239355032F, 0.7305464154F, + 0.7370861235F, 0.7435531598F, 0.7499461068F, 0.7562635986F, + 0.7625043214F, 0.7686670148F, 0.7747504721F, 0.7807535410F, + 0.7866751247F, 0.7925141825F, 0.7982697296F, 0.8039408387F, + 0.8095266395F, 0.8150263196F, 0.8204391248F, 0.8257643590F, + 0.8310013848F, 0.8361496236F, 0.8412085555F, 0.8461777194F, + 0.8510567129F, 0.8558451924F, 0.8605428730F, 0.8651495278F, + 0.8696649882F, 0.8740891432F, 0.8784219392F, 0.8826633797F, + 0.8868135244F, 0.8908724888F, 0.8948404441F, 0.8987176157F, + 0.9025042831F, 0.9062007791F, 0.9098074886F, 0.9133248482F, + 0.9167533451F, 0.9200935163F, 0.9233459472F, 0.9265112712F, + 0.9295901680F, 0.9325833632F, 0.9354916263F, 0.9383157705F, + 0.9410566504F, 0.9437151618F, 0.9462922398F, 0.9487888576F, + 0.9512060252F, 0.9535447882F, 0.9558062262F, 0.9579914516F, + 0.9601016078F, 0.9621378683F, 0.9641014348F, 0.9659935361F, + 0.9678154261F, 0.9695683830F, 0.9712537071F, 0.9728727198F, + 0.9744267618F, 0.9759171916F, 0.9773453842F, 0.9787127293F, + 0.9800206298F, 0.9812705006F, 0.9824637665F, 0.9836018613F, + 0.9846862258F, 0.9857183066F, 0.9866995544F, 0.9876314227F, + 0.9885153662F, 0.9893528393F, 0.9901452948F, 0.9908941823F, + 0.9916009470F, 0.9922670279F, 0.9928938570F, 0.9934828574F, + 0.9940354423F, 0.9945530133F, 0.9950369595F, 0.9954886562F, + 0.9959094633F, 0.9963007242F, 0.9966637649F, 0.9969998925F, + 0.9973103939F, 0.9975965351F, 0.9978595598F, 0.9981006885F, + 0.9983211172F, 0.9985220166F, 0.9987045311F, 0.9988697776F, + 0.9990188449F, 0.9991527924F, 0.9992726499F, 0.9993794157F, + 0.9994740570F, 0.9995575079F, 0.9996306699F, 0.9996944099F, + 0.9997495605F, 0.9997969190F, 0.9998372465F, 0.9998712678F, + 0.9998996704F, 0.9999231041F, 0.9999421807F, 0.9999574732F, + 0.9999695157F, 0.9999788026F, 0.9999857885F, 0.9999908879F, + 0.9999944746F, 0.9999968817F, 0.9999984010F, 0.9999992833F, + 0.9999997377F, 0.9999999317F, 0.9999999911F, 0.9999999999F, +}; + +static float vwin1024[512] = { + 0.0000036962F, 0.0000332659F, 0.0000924041F, 0.0001811086F, + 0.0002993761F, 0.0004472021F, 0.0006245811F, 0.0008315063F, + 0.0010679699F, 0.0013339631F, 0.0016294757F, 0.0019544965F, + 0.0023090133F, 0.0026930125F, 0.0031064797F, 0.0035493989F, + 0.0040217533F, 0.0045235250F, 0.0050546946F, 0.0056152418F, + 0.0062051451F, 0.0068243817F, 0.0074729278F, 0.0081507582F, + 0.0088578466F, 0.0095941655F, 0.0103596863F, 0.0111543789F, + 0.0119782122F, 0.0128311538F, 0.0137131701F, 0.0146242260F, + 0.0155642855F, 0.0165333111F, 0.0175312640F, 0.0185581042F, + 0.0196137903F, 0.0206982797F, 0.0218115284F, 0.0229534910F, + 0.0241241208F, 0.0253233698F, 0.0265511886F, 0.0278075263F, + 0.0290923308F, 0.0304055484F, 0.0317471241F, 0.0331170013F, + 0.0345151222F, 0.0359414274F, 0.0373958560F, 0.0388783456F, + 0.0403888325F, 0.0419272511F, 0.0434935347F, 0.0450876148F, + 0.0467094213F, 0.0483588828F, 0.0500359261F, 0.0517404765F, + 0.0534724575F, 0.0552317913F, 0.0570183983F, 0.0588321971F, + 0.0606731048F, 0.0625410369F, 0.0644359070F, 0.0663576272F, + 0.0683061077F, 0.0702812571F, 0.0722829821F, 0.0743111878F, + 0.0763657775F, 0.0784466526F, 0.0805537129F, 0.0826868561F, + 0.0848459782F, 0.0870309736F, 0.0892417345F, 0.0914781514F, + 0.0937401128F, 0.0960275056F, 0.0983402145F, 0.1006781223F, + 0.1030411101F, 0.1054290568F, 0.1078418397F, 0.1102793336F, + 0.1127414119F, 0.1152279457F, 0.1177388042F, 0.1202738544F, + 0.1228329618F, 0.1254159892F, 0.1280227980F, 0.1306532471F, + 0.1333071937F, 0.1359844927F, 0.1386849970F, 0.1414085575F, + 0.1441550230F, 0.1469242403F, 0.1497160539F, 0.1525303063F, + 0.1553668381F, 0.1582254875F, 0.1611060909F, 0.1640084822F, + 0.1669324936F, 0.1698779549F, 0.1728446939F, 0.1758325362F, + 0.1788413055F, 0.1818708232F, 0.1849209084F, 0.1879913785F, + 0.1910820485F, 0.1941927312F, 0.1973232376F, 0.2004733764F, + 0.2036429541F, 0.2068317752F, 0.2100396421F, 0.2132663552F, + 0.2165117125F, 0.2197755102F, 0.2230575422F, 0.2263576007F, + 0.2296754753F, 0.2330109540F, 0.2363638225F, 0.2397338646F, + 0.2431208619F, 0.2465245941F, 0.2499448389F, 0.2533813719F, + 0.2568339669F, 0.2603023956F, 0.2637864277F, 0.2672858312F, + 0.2708003718F, 0.2743298135F, 0.2778739186F, 0.2814324472F, + 0.2850051576F, 0.2885918065F, 0.2921921485F, 0.2958059366F, + 0.2994329219F, 0.3030728538F, 0.3067254799F, 0.3103905462F, + 0.3140677969F, 0.3177569747F, 0.3214578205F, 0.3251700736F, + 0.3288934718F, 0.3326277513F, 0.3363726468F, 0.3401278914F, + 0.3438932168F, 0.3476683533F, 0.3514530297F, 0.3552469734F, + 0.3590499106F, 0.3628615659F, 0.3666816630F, 0.3705099239F, + 0.3743460698F, 0.3781898204F, 0.3820408945F, 0.3858990095F, + 0.3897638820F, 0.3936352274F, 0.3975127601F, 0.4013961936F, + 0.4052852405F, 0.4091796123F, 0.4130790198F, 0.4169831732F, + 0.4208917815F, 0.4248045534F, 0.4287211965F, 0.4326414181F, + 0.4365649248F, 0.4404914225F, 0.4444206167F, 0.4483522125F, + 0.4522859146F, 0.4562214270F, 0.4601584538F, 0.4640966984F, + 0.4680358644F, 0.4719756548F, 0.4759157726F, 0.4798559209F, + 0.4837958024F, 0.4877351199F, 0.4916735765F, 0.4956108751F, + 0.4995467188F, 0.5034808109F, 0.5074128550F, 0.5113425550F, + 0.5152696149F, 0.5191937395F, 0.5231146336F, 0.5270320028F, + 0.5309455530F, 0.5348549910F, 0.5387600239F, 0.5426603597F, + 0.5465557070F, 0.5504457754F, 0.5543302752F, 0.5582089175F, + 0.5620814145F, 0.5659474793F, 0.5698068262F, 0.5736591704F, + 0.5775042283F, 0.5813417176F, 0.5851713571F, 0.5889928670F, + 0.5928059689F, 0.5966103856F, 0.6004058415F, 0.6041920626F, + 0.6079687761F, 0.6117357113F, 0.6154925986F, 0.6192391705F, + 0.6229751612F, 0.6267003064F, 0.6304143441F, 0.6341170137F, + 0.6378080569F, 0.6414872173F, 0.6451542405F, 0.6488088741F, + 0.6524508681F, 0.6560799742F, 0.6596959469F, 0.6632985424F, + 0.6668875197F, 0.6704626398F, 0.6740236662F, 0.6775703649F, + 0.6811025043F, 0.6846198554F, 0.6881221916F, 0.6916092892F, + 0.6950809269F, 0.6985368861F, 0.7019769510F, 0.7054009085F, + 0.7088085484F, 0.7121996632F, 0.7155740484F, 0.7189315023F, + 0.7222718263F, 0.7255948245F, 0.7289003043F, 0.7321880760F, + 0.7354579530F, 0.7387097518F, 0.7419432921F, 0.7451583966F, + 0.7483548915F, 0.7515326059F, 0.7546913723F, 0.7578310265F, + 0.7609514077F, 0.7640523581F, 0.7671337237F, 0.7701953535F, + 0.7732371001F, 0.7762588195F, 0.7792603711F, 0.7822416178F, + 0.7852024259F, 0.7881426654F, 0.7910622097F, 0.7939609356F, + 0.7968387237F, 0.7996954579F, 0.8025310261F, 0.8053453193F, + 0.8081382324F, 0.8109096638F, 0.8136595156F, 0.8163876936F, + 0.8190941071F, 0.8217786690F, 0.8244412960F, 0.8270819086F, + 0.8297004305F, 0.8322967896F, 0.8348709171F, 0.8374227481F, + 0.8399522213F, 0.8424592789F, 0.8449438672F, 0.8474059356F, + 0.8498454378F, 0.8522623306F, 0.8546565748F, 0.8570281348F, + 0.8593769787F, 0.8617030779F, 0.8640064080F, 0.8662869477F, + 0.8685446796F, 0.8707795899F, 0.8729916682F, 0.8751809079F, + 0.8773473059F, 0.8794908626F, 0.8816115819F, 0.8837094713F, + 0.8857845418F, 0.8878368079F, 0.8898662874F, 0.8918730019F, + 0.8938569760F, 0.8958182380F, 0.8977568194F, 0.8996727552F, + 0.9015660837F, 0.9034368465F, 0.9052850885F, 0.9071108577F, + 0.9089142057F, 0.9106951869F, 0.9124538591F, 0.9141902832F, + 0.9159045233F, 0.9175966464F, 0.9192667228F, 0.9209148257F, + 0.9225410313F, 0.9241454187F, 0.9257280701F, 0.9272890704F, + 0.9288285075F, 0.9303464720F, 0.9318430576F, 0.9333183603F, + 0.9347724792F, 0.9362055158F, 0.9376175745F, 0.9390087622F, + 0.9403791881F, 0.9417289644F, 0.9430582055F, 0.9443670283F, + 0.9456555521F, 0.9469238986F, 0.9481721917F, 0.9494005577F, + 0.9506091252F, 0.9517980248F, 0.9529673894F, 0.9541173540F, + 0.9552480557F, 0.9563596334F, 0.9574522282F, 0.9585259830F, + 0.9595810428F, 0.9606175542F, 0.9616356656F, 0.9626355274F, + 0.9636172915F, 0.9645811114F, 0.9655271425F, 0.9664555414F, + 0.9673664664F, 0.9682600774F, 0.9691365355F, 0.9699960034F, + 0.9708386448F, 0.9716646250F, 0.9724741103F, 0.9732672685F, + 0.9740442683F, 0.9748052795F, 0.9755504729F, 0.9762800205F, + 0.9769940950F, 0.9776928703F, 0.9783765210F, 0.9790452223F, + 0.9796991504F, 0.9803384823F, 0.9809633954F, 0.9815740679F, + 0.9821706784F, 0.9827534063F, 0.9833224312F, 0.9838779332F, + 0.9844200928F, 0.9849490910F, 0.9854651087F, 0.9859683274F, + 0.9864589286F, 0.9869370940F, 0.9874030054F, 0.9878568447F, + 0.9882987937F, 0.9887290343F, 0.9891477481F, 0.9895551169F, + 0.9899513220F, 0.9903365446F, 0.9907109658F, 0.9910747662F, + 0.9914281260F, 0.9917712252F, 0.9921042433F, 0.9924273593F, + 0.9927407516F, 0.9930445982F, 0.9933390763F, 0.9936243626F, + 0.9939006331F, 0.9941680631F, 0.9944268269F, 0.9946770982F, + 0.9949190498F, 0.9951528537F, 0.9953786808F, 0.9955967011F, + 0.9958070836F, 0.9960099963F, 0.9962056061F, 0.9963940787F, + 0.9965755786F, 0.9967502693F, 0.9969183129F, 0.9970798704F, + 0.9972351013F, 0.9973841640F, 0.9975272151F, 0.9976644103F, + 0.9977959036F, 0.9979218476F, 0.9980423932F, 0.9981576901F, + 0.9982678862F, 0.9983731278F, 0.9984735596F, 0.9985693247F, + 0.9986605645F, 0.9987474186F, 0.9988300248F, 0.9989085193F, + 0.9989830364F, 0.9990537085F, 0.9991206662F, 0.9991840382F, + 0.9992439513F, 0.9993005303F, 0.9993538982F, 0.9994041757F, + 0.9994514817F, 0.9994959330F, 0.9995376444F, 0.9995767286F, + 0.9996132960F, 0.9996474550F, 0.9996793121F, 0.9997089710F, + 0.9997365339F, 0.9997621003F, 0.9997857677F, 0.9998076311F, + 0.9998277836F, 0.9998463156F, 0.9998633155F, 0.9998788692F, + 0.9998930603F, 0.9999059701F, 0.9999176774F, 0.9999282586F, + 0.9999377880F, 0.9999463370F, 0.9999539749F, 0.9999607685F, + 0.9999667820F, 0.9999720773F, 0.9999767136F, 0.9999807479F, + 0.9999842344F, 0.9999872249F, 0.9999897688F, 0.9999919127F, + 0.9999937009F, 0.9999951749F, 0.9999963738F, 0.9999973342F, + 0.9999980900F, 0.9999986724F, 0.9999991103F, 0.9999994297F, + 0.9999996543F, 0.9999998049F, 0.9999999000F, 0.9999999552F, + 0.9999999836F, 0.9999999957F, 0.9999999994F, 1.0000000000F, +}; + +static float vwin2048[1024] = { + 0.0000009241F, 0.0000083165F, 0.0000231014F, 0.0000452785F, + 0.0000748476F, 0.0001118085F, 0.0001561608F, 0.0002079041F, + 0.0002670379F, 0.0003335617F, 0.0004074748F, 0.0004887765F, + 0.0005774661F, 0.0006735427F, 0.0007770054F, 0.0008878533F, + 0.0010060853F, 0.0011317002F, 0.0012646969F, 0.0014050742F, + 0.0015528307F, 0.0017079650F, 0.0018704756F, 0.0020403610F, + 0.0022176196F, 0.0024022497F, 0.0025942495F, 0.0027936173F, + 0.0030003511F, 0.0032144490F, 0.0034359088F, 0.0036647286F, + 0.0039009061F, 0.0041444391F, 0.0043953253F, 0.0046535621F, + 0.0049191472F, 0.0051920781F, 0.0054723520F, 0.0057599664F, + 0.0060549184F, 0.0063572052F, 0.0066668239F, 0.0069837715F, + 0.0073080449F, 0.0076396410F, 0.0079785566F, 0.0083247884F, + 0.0086783330F, 0.0090391871F, 0.0094073470F, 0.0097828092F, + 0.0101655700F, 0.0105556258F, 0.0109529726F, 0.0113576065F, + 0.0117695237F, 0.0121887200F, 0.0126151913F, 0.0130489335F, + 0.0134899422F, 0.0139382130F, 0.0143937415F, 0.0148565233F, + 0.0153265536F, 0.0158038279F, 0.0162883413F, 0.0167800889F, + 0.0172790660F, 0.0177852675F, 0.0182986882F, 0.0188193231F, + 0.0193471668F, 0.0198822141F, 0.0204244594F, 0.0209738974F, + 0.0215305225F, 0.0220943289F, 0.0226653109F, 0.0232434627F, + 0.0238287784F, 0.0244212519F, 0.0250208772F, 0.0256276481F, + 0.0262415582F, 0.0268626014F, 0.0274907711F, 0.0281260608F, + 0.0287684638F, 0.0294179736F, 0.0300745833F, 0.0307382859F, + 0.0314090747F, 0.0320869424F, 0.0327718819F, 0.0334638860F, + 0.0341629474F, 0.0348690586F, 0.0355822122F, 0.0363024004F, + 0.0370296157F, 0.0377638502F, 0.0385050960F, 0.0392533451F, + 0.0400085896F, 0.0407708211F, 0.0415400315F, 0.0423162123F, + 0.0430993552F, 0.0438894515F, 0.0446864926F, 0.0454904698F, + 0.0463013742F, 0.0471191969F, 0.0479439288F, 0.0487755607F, + 0.0496140836F, 0.0504594879F, 0.0513117642F, 0.0521709031F, + 0.0530368949F, 0.0539097297F, 0.0547893979F, 0.0556758894F, + 0.0565691941F, 0.0574693019F, 0.0583762026F, 0.0592898858F, + 0.0602103410F, 0.0611375576F, 0.0620715250F, 0.0630122324F, + 0.0639596688F, 0.0649138234F, 0.0658746848F, 0.0668422421F, + 0.0678164838F, 0.0687973985F, 0.0697849746F, 0.0707792005F, + 0.0717800645F, 0.0727875547F, 0.0738016591F, 0.0748223656F, + 0.0758496620F, 0.0768835359F, 0.0779239751F, 0.0789709668F, + 0.0800244985F, 0.0810845574F, 0.0821511306F, 0.0832242052F, + 0.0843037679F, 0.0853898056F, 0.0864823050F, 0.0875812525F, + 0.0886866347F, 0.0897984378F, 0.0909166480F, 0.0920412513F, + 0.0931722338F, 0.0943095813F, 0.0954532795F, 0.0966033140F, + 0.0977596702F, 0.0989223336F, 0.1000912894F, 0.1012665227F, + 0.1024480185F, 0.1036357616F, 0.1048297369F, 0.1060299290F, + 0.1072363224F, 0.1084489014F, 0.1096676504F, 0.1108925534F, + 0.1121235946F, 0.1133607577F, 0.1146040267F, 0.1158533850F, + 0.1171088163F, 0.1183703040F, 0.1196378312F, 0.1209113812F, + 0.1221909370F, 0.1234764815F, 0.1247679974F, 0.1260654674F, + 0.1273688740F, 0.1286781995F, 0.1299934263F, 0.1313145365F, + 0.1326415121F, 0.1339743349F, 0.1353129866F, 0.1366574490F, + 0.1380077035F, 0.1393637315F, 0.1407255141F, 0.1420930325F, + 0.1434662677F, 0.1448452004F, 0.1462298115F, 0.1476200814F, + 0.1490159906F, 0.1504175195F, 0.1518246482F, 0.1532373569F, + 0.1546556253F, 0.1560794333F, 0.1575087606F, 0.1589435866F, + 0.1603838909F, 0.1618296526F, 0.1632808509F, 0.1647374648F, + 0.1661994731F, 0.1676668546F, 0.1691395880F, 0.1706176516F, + 0.1721010238F, 0.1735896829F, 0.1750836068F, 0.1765827736F, + 0.1780871610F, 0.1795967468F, 0.1811115084F, 0.1826314234F, + 0.1841564689F, 0.1856866221F, 0.1872218600F, 0.1887621595F, + 0.1903074974F, 0.1918578503F, 0.1934131947F, 0.1949735068F, + 0.1965387630F, 0.1981089393F, 0.1996840117F, 0.2012639560F, + 0.2028487479F, 0.2044383630F, 0.2060327766F, 0.2076319642F, + 0.2092359007F, 0.2108445614F, 0.2124579211F, 0.2140759545F, + 0.2156986364F, 0.2173259411F, 0.2189578432F, 0.2205943168F, + 0.2222353361F, 0.2238808751F, 0.2255309076F, 0.2271854073F, + 0.2288443480F, 0.2305077030F, 0.2321754457F, 0.2338475493F, + 0.2355239869F, 0.2372047315F, 0.2388897560F, 0.2405790329F, + 0.2422725350F, 0.2439702347F, 0.2456721043F, 0.2473781159F, + 0.2490882418F, 0.2508024539F, 0.2525207240F, 0.2542430237F, + 0.2559693248F, 0.2576995986F, 0.2594338166F, 0.2611719498F, + 0.2629139695F, 0.2646598466F, 0.2664095520F, 0.2681630564F, + 0.2699203304F, 0.2716813445F, 0.2734460691F, 0.2752144744F, + 0.2769865307F, 0.2787622079F, 0.2805414760F, 0.2823243047F, + 0.2841106637F, 0.2859005227F, 0.2876938509F, 0.2894906179F, + 0.2912907928F, 0.2930943447F, 0.2949012426F, 0.2967114554F, + 0.2985249520F, 0.3003417009F, 0.3021616708F, 0.3039848301F, + 0.3058111471F, 0.3076405901F, 0.3094731273F, 0.3113087266F, + 0.3131473560F, 0.3149889833F, 0.3168335762F, 0.3186811024F, + 0.3205315294F, 0.3223848245F, 0.3242409552F, 0.3260998886F, + 0.3279615918F, 0.3298260319F, 0.3316931758F, 0.3335629903F, + 0.3354354423F, 0.3373104982F, 0.3391881247F, 0.3410682882F, + 0.3429509551F, 0.3448360917F, 0.3467236642F, 0.3486136387F, + 0.3505059811F, 0.3524006575F, 0.3542976336F, 0.3561968753F, + 0.3580983482F, 0.3600020179F, 0.3619078499F, 0.3638158096F, + 0.3657258625F, 0.3676379737F, 0.3695521086F, 0.3714682321F, + 0.3733863094F, 0.3753063055F, 0.3772281852F, 0.3791519134F, + 0.3810774548F, 0.3830047742F, 0.3849338362F, 0.3868646053F, + 0.3887970459F, 0.3907311227F, 0.3926667998F, 0.3946040417F, + 0.3965428125F, 0.3984830765F, 0.4004247978F, 0.4023679403F, + 0.4043124683F, 0.4062583455F, 0.4082055359F, 0.4101540034F, + 0.4121037117F, 0.4140546246F, 0.4160067058F, 0.4179599190F, + 0.4199142277F, 0.4218695956F, 0.4238259861F, 0.4257833627F, + 0.4277416888F, 0.4297009279F, 0.4316610433F, 0.4336219983F, + 0.4355837562F, 0.4375462803F, 0.4395095337F, 0.4414734797F, + 0.4434380815F, 0.4454033021F, 0.4473691046F, 0.4493354521F, + 0.4513023078F, 0.4532696345F, 0.4552373954F, 0.4572055533F, + 0.4591740713F, 0.4611429123F, 0.4631120393F, 0.4650814151F, + 0.4670510028F, 0.4690207650F, 0.4709906649F, 0.4729606651F, + 0.4749307287F, 0.4769008185F, 0.4788708972F, 0.4808409279F, + 0.4828108732F, 0.4847806962F, 0.4867503597F, 0.4887198264F, + 0.4906890593F, 0.4926580213F, 0.4946266753F, 0.4965949840F, + 0.4985629105F, 0.5005304176F, 0.5024974683F, 0.5044640255F, + 0.5064300522F, 0.5083955114F, 0.5103603659F, 0.5123245790F, + 0.5142881136F, 0.5162509328F, 0.5182129997F, 0.5201742774F, + 0.5221347290F, 0.5240943178F, 0.5260530070F, 0.5280107598F, + 0.5299675395F, 0.5319233095F, 0.5338780330F, 0.5358316736F, + 0.5377841946F, 0.5397355596F, 0.5416857320F, 0.5436346755F, + 0.5455823538F, 0.5475287304F, 0.5494737691F, 0.5514174337F, + 0.5533596881F, 0.5553004962F, 0.5572398218F, 0.5591776291F, + 0.5611138821F, 0.5630485449F, 0.5649815818F, 0.5669129570F, + 0.5688426349F, 0.5707705799F, 0.5726967564F, 0.5746211290F, + 0.5765436624F, 0.5784643212F, 0.5803830702F, 0.5822998743F, + 0.5842146984F, 0.5861275076F, 0.5880382669F, 0.5899469416F, + 0.5918534968F, 0.5937578981F, 0.5956601107F, 0.5975601004F, + 0.5994578326F, 0.6013532732F, 0.6032463880F, 0.6051371429F, + 0.6070255039F, 0.6089114372F, 0.6107949090F, 0.6126758856F, + 0.6145543334F, 0.6164302191F, 0.6183035092F, 0.6201741706F, + 0.6220421700F, 0.6239074745F, 0.6257700513F, 0.6276298674F, + 0.6294868903F, 0.6313410873F, 0.6331924262F, 0.6350408745F, + 0.6368864001F, 0.6387289710F, 0.6405685552F, 0.6424051209F, + 0.6442386364F, 0.6460690702F, 0.6478963910F, 0.6497205673F, + 0.6515415682F, 0.6533593625F, 0.6551739194F, 0.6569852082F, + 0.6587931984F, 0.6605978593F, 0.6623991609F, 0.6641970728F, + 0.6659915652F, 0.6677826081F, 0.6695701718F, 0.6713542268F, + 0.6731347437F, 0.6749116932F, 0.6766850461F, 0.6784547736F, + 0.6802208469F, 0.6819832374F, 0.6837419164F, 0.6854968559F, + 0.6872480275F, 0.6889954034F, 0.6907389556F, 0.6924786566F, + 0.6942144788F, 0.6959463950F, 0.6976743780F, 0.6993984008F, + 0.7011184365F, 0.7028344587F, 0.7045464407F, 0.7062543564F, + 0.7079581796F, 0.7096578844F, 0.7113534450F, 0.7130448359F, + 0.7147320316F, 0.7164150070F, 0.7180937371F, 0.7197681970F, + 0.7214383620F, 0.7231042077F, 0.7247657098F, 0.7264228443F, + 0.7280755871F, 0.7297239147F, 0.7313678035F, 0.7330072301F, + 0.7346421715F, 0.7362726046F, 0.7378985069F, 0.7395198556F, + 0.7411366285F, 0.7427488034F, 0.7443563584F, 0.7459592717F, + 0.7475575218F, 0.7491510873F, 0.7507399471F, 0.7523240803F, + 0.7539034661F, 0.7554780839F, 0.7570479136F, 0.7586129349F, + 0.7601731279F, 0.7617284730F, 0.7632789506F, 0.7648245416F, + 0.7663652267F, 0.7679009872F, 0.7694318044F, 0.7709576599F, + 0.7724785354F, 0.7739944130F, 0.7755052749F, 0.7770111035F, + 0.7785118815F, 0.7800075916F, 0.7814982170F, 0.7829837410F, + 0.7844641472F, 0.7859394191F, 0.7874095408F, 0.7888744965F, + 0.7903342706F, 0.7917888476F, 0.7932382124F, 0.7946823501F, + 0.7961212460F, 0.7975548855F, 0.7989832544F, 0.8004063386F, + 0.8018241244F, 0.8032365981F, 0.8046437463F, 0.8060455560F, + 0.8074420141F, 0.8088331080F, 0.8102188253F, 0.8115991536F, + 0.8129740810F, 0.8143435957F, 0.8157076861F, 0.8170663409F, + 0.8184195489F, 0.8197672994F, 0.8211095817F, 0.8224463853F, + 0.8237777001F, 0.8251035161F, 0.8264238235F, 0.8277386129F, + 0.8290478750F, 0.8303516008F, 0.8316497814F, 0.8329424083F, + 0.8342294731F, 0.8355109677F, 0.8367868841F, 0.8380572148F, + 0.8393219523F, 0.8405810893F, 0.8418346190F, 0.8430825345F, + 0.8443248294F, 0.8455614974F, 0.8467925323F, 0.8480179285F, + 0.8492376802F, 0.8504517822F, 0.8516602292F, 0.8528630164F, + 0.8540601391F, 0.8552515928F, 0.8564373733F, 0.8576174766F, + 0.8587918990F, 0.8599606368F, 0.8611236868F, 0.8622810460F, + 0.8634327113F, 0.8645786802F, 0.8657189504F, 0.8668535195F, + 0.8679823857F, 0.8691055472F, 0.8702230025F, 0.8713347503F, + 0.8724407896F, 0.8735411194F, 0.8746357394F, 0.8757246489F, + 0.8768078479F, 0.8778853364F, 0.8789571146F, 0.8800231832F, + 0.8810835427F, 0.8821381942F, 0.8831871387F, 0.8842303777F, + 0.8852679127F, 0.8862997456F, 0.8873258784F, 0.8883463132F, + 0.8893610527F, 0.8903700994F, 0.8913734562F, 0.8923711263F, + 0.8933631129F, 0.8943494196F, 0.8953300500F, 0.8963050083F, + 0.8972742985F, 0.8982379249F, 0.8991958922F, 0.9001482052F, + 0.9010948688F, 0.9020358883F, 0.9029712690F, 0.9039010165F, + 0.9048251367F, 0.9057436357F, 0.9066565195F, 0.9075637946F, + 0.9084654678F, 0.9093615456F, 0.9102520353F, 0.9111369440F, + 0.9120162792F, 0.9128900484F, 0.9137582595F, 0.9146209204F, + 0.9154780394F, 0.9163296248F, 0.9171756853F, 0.9180162296F, + 0.9188512667F, 0.9196808057F, 0.9205048559F, 0.9213234270F, + 0.9221365285F, 0.9229441704F, 0.9237463629F, 0.9245431160F, + 0.9253344404F, 0.9261203465F, 0.9269008453F, 0.9276759477F, + 0.9284456648F, 0.9292100080F, 0.9299689889F, 0.9307226190F, + 0.9314709103F, 0.9322138747F, 0.9329515245F, 0.9336838721F, + 0.9344109300F, 0.9351327108F, 0.9358492275F, 0.9365604931F, + 0.9372665208F, 0.9379673239F, 0.9386629160F, 0.9393533107F, + 0.9400385220F, 0.9407185637F, 0.9413934501F, 0.9420631954F, + 0.9427278141F, 0.9433873208F, 0.9440417304F, 0.9446910576F, + 0.9453353176F, 0.9459745255F, 0.9466086968F, 0.9472378469F, + 0.9478619915F, 0.9484811463F, 0.9490953274F, 0.9497045506F, + 0.9503088323F, 0.9509081888F, 0.9515026365F, 0.9520921921F, + 0.9526768723F, 0.9532566940F, 0.9538316742F, 0.9544018300F, + 0.9549671786F, 0.9555277375F, 0.9560835241F, 0.9566345562F, + 0.9571808513F, 0.9577224275F, 0.9582593027F, 0.9587914949F, + 0.9593190225F, 0.9598419038F, 0.9603601571F, 0.9608738012F, + 0.9613828546F, 0.9618873361F, 0.9623872646F, 0.9628826591F, + 0.9633735388F, 0.9638599227F, 0.9643418303F, 0.9648192808F, + 0.9652922939F, 0.9657608890F, 0.9662250860F, 0.9666849046F, + 0.9671403646F, 0.9675914861F, 0.9680382891F, 0.9684807937F, + 0.9689190202F, 0.9693529890F, 0.9697827203F, 0.9702082347F, + 0.9706295529F, 0.9710466953F, 0.9714596828F, 0.9718685362F, + 0.9722732762F, 0.9726739240F, 0.9730705005F, 0.9734630267F, + 0.9738515239F, 0.9742360134F, 0.9746165163F, 0.9749930540F, + 0.9753656481F, 0.9757343198F, 0.9760990909F, 0.9764599829F, + 0.9768170175F, 0.9771702164F, 0.9775196013F, 0.9778651941F, + 0.9782070167F, 0.9785450909F, 0.9788794388F, 0.9792100824F, + 0.9795370437F, 0.9798603449F, 0.9801800080F, 0.9804960554F, + 0.9808085092F, 0.9811173916F, 0.9814227251F, 0.9817245318F, + 0.9820228343F, 0.9823176549F, 0.9826090160F, 0.9828969402F, + 0.9831814498F, 0.9834625674F, 0.9837403156F, 0.9840147169F, + 0.9842857939F, 0.9845535692F, 0.9848180654F, 0.9850793052F, + 0.9853373113F, 0.9855921062F, 0.9858437127F, 0.9860921535F, + 0.9863374512F, 0.9865796287F, 0.9868187085F, 0.9870547136F, + 0.9872876664F, 0.9875175899F, 0.9877445067F, 0.9879684396F, + 0.9881894112F, 0.9884074444F, 0.9886225619F, 0.9888347863F, + 0.9890441404F, 0.9892506468F, 0.9894543284F, 0.9896552077F, + 0.9898533074F, 0.9900486502F, 0.9902412587F, 0.9904311555F, + 0.9906183633F, 0.9908029045F, 0.9909848019F, 0.9911640779F, + 0.9913407550F, 0.9915148557F, 0.9916864025F, 0.9918554179F, + 0.9920219241F, 0.9921859437F, 0.9923474989F, 0.9925066120F, + 0.9926633054F, 0.9928176012F, 0.9929695218F, 0.9931190891F, + 0.9932663254F, 0.9934112527F, 0.9935538932F, 0.9936942686F, + 0.9938324012F, 0.9939683126F, 0.9941020248F, 0.9942335597F, + 0.9943629388F, 0.9944901841F, 0.9946153170F, 0.9947383593F, + 0.9948593325F, 0.9949782579F, 0.9950951572F, 0.9952100516F, + 0.9953229625F, 0.9954339111F, 0.9955429186F, 0.9956500062F, + 0.9957551948F, 0.9958585056F, 0.9959599593F, 0.9960595769F, + 0.9961573792F, 0.9962533869F, 0.9963476206F, 0.9964401009F, + 0.9965308483F, 0.9966198833F, 0.9967072261F, 0.9967928971F, + 0.9968769164F, 0.9969593041F, 0.9970400804F, 0.9971192651F, + 0.9971968781F, 0.9972729391F, 0.9973474680F, 0.9974204842F, + 0.9974920074F, 0.9975620569F, 0.9976306521F, 0.9976978122F, + 0.9977635565F, 0.9978279039F, 0.9978908736F, 0.9979524842F, + 0.9980127547F, 0.9980717037F, 0.9981293499F, 0.9981857116F, + 0.9982408073F, 0.9982946554F, 0.9983472739F, 0.9983986810F, + 0.9984488947F, 0.9984979328F, 0.9985458132F, 0.9985925534F, + 0.9986381711F, 0.9986826838F, 0.9987261086F, 0.9987684630F, + 0.9988097640F, 0.9988500286F, 0.9988892738F, 0.9989275163F, + 0.9989647727F, 0.9990010597F, 0.9990363938F, 0.9990707911F, + 0.9991042679F, 0.9991368404F, 0.9991685244F, 0.9991993358F, + 0.9992292905F, 0.9992584038F, 0.9992866914F, 0.9993141686F, + 0.9993408506F, 0.9993667526F, 0.9993918895F, 0.9994162761F, + 0.9994399273F, 0.9994628576F, 0.9994850815F, 0.9995066133F, + 0.9995274672F, 0.9995476574F, 0.9995671978F, 0.9995861021F, + 0.9996043841F, 0.9996220573F, 0.9996391352F, 0.9996556310F, + 0.9996715579F, 0.9996869288F, 0.9997017568F, 0.9997160543F, + 0.9997298342F, 0.9997431088F, 0.9997558905F, 0.9997681914F, + 0.9997800236F, 0.9997913990F, 0.9998023292F, 0.9998128261F, + 0.9998229009F, 0.9998325650F, 0.9998418296F, 0.9998507058F, + 0.9998592044F, 0.9998673362F, 0.9998751117F, 0.9998825415F, + 0.9998896358F, 0.9998964047F, 0.9999028584F, 0.9999090066F, + 0.9999148590F, 0.9999204253F, 0.9999257148F, 0.9999307368F, + 0.9999355003F, 0.9999400144F, 0.9999442878F, 0.9999483293F, + 0.9999521472F, 0.9999557499F, 0.9999591457F, 0.9999623426F, + 0.9999653483F, 0.9999681708F, 0.9999708175F, 0.9999732959F, + 0.9999756132F, 0.9999777765F, 0.9999797928F, 0.9999816688F, + 0.9999834113F, 0.9999850266F, 0.9999865211F, 0.9999879009F, + 0.9999891721F, 0.9999903405F, 0.9999914118F, 0.9999923914F, + 0.9999932849F, 0.9999940972F, 0.9999948336F, 0.9999954989F, + 0.9999960978F, 0.9999966349F, 0.9999971146F, 0.9999975411F, + 0.9999979185F, 0.9999982507F, 0.9999985414F, 0.9999987944F, + 0.9999990129F, 0.9999992003F, 0.9999993596F, 0.9999994939F, + 0.9999996059F, 0.9999996981F, 0.9999997732F, 0.9999998333F, + 0.9999998805F, 0.9999999170F, 0.9999999444F, 0.9999999643F, + 0.9999999784F, 0.9999999878F, 0.9999999937F, 0.9999999972F, + 0.9999999990F, 0.9999999997F, 1.0000000000F, 1.0000000000F, +}; + +static float vwin4096[2048] = { + 0.0000002310F, 0.0000020791F, 0.0000057754F, 0.0000113197F, + 0.0000187121F, 0.0000279526F, 0.0000390412F, 0.0000519777F, + 0.0000667623F, 0.0000833949F, 0.0001018753F, 0.0001222036F, + 0.0001443798F, 0.0001684037F, 0.0001942754F, 0.0002219947F, + 0.0002515616F, 0.0002829761F, 0.0003162380F, 0.0003513472F, + 0.0003883038F, 0.0004271076F, 0.0004677584F, 0.0005102563F, + 0.0005546011F, 0.0006007928F, 0.0006488311F, 0.0006987160F, + 0.0007504474F, 0.0008040251F, 0.0008594490F, 0.0009167191F, + 0.0009758351F, 0.0010367969F, 0.0010996044F, 0.0011642574F, + 0.0012307558F, 0.0012990994F, 0.0013692880F, 0.0014413216F, + 0.0015151998F, 0.0015909226F, 0.0016684898F, 0.0017479011F, + 0.0018291565F, 0.0019122556F, 0.0019971983F, 0.0020839845F, + 0.0021726138F, 0.0022630861F, 0.0023554012F, 0.0024495588F, + 0.0025455588F, 0.0026434008F, 0.0027430847F, 0.0028446103F, + 0.0029479772F, 0.0030531853F, 0.0031602342F, 0.0032691238F, + 0.0033798538F, 0.0034924239F, 0.0036068338F, 0.0037230833F, + 0.0038411721F, 0.0039610999F, 0.0040828664F, 0.0042064714F, + 0.0043319145F, 0.0044591954F, 0.0045883139F, 0.0047192696F, + 0.0048520622F, 0.0049866914F, 0.0051231569F, 0.0052614583F, + 0.0054015953F, 0.0055435676F, 0.0056873748F, 0.0058330166F, + 0.0059804926F, 0.0061298026F, 0.0062809460F, 0.0064339226F, + 0.0065887320F, 0.0067453738F, 0.0069038476F, 0.0070641531F, + 0.0072262899F, 0.0073902575F, 0.0075560556F, 0.0077236838F, + 0.0078931417F, 0.0080644288F, 0.0082375447F, 0.0084124891F, + 0.0085892615F, 0.0087678614F, 0.0089482885F, 0.0091305422F, + 0.0093146223F, 0.0095005281F, 0.0096882592F, 0.0098778153F, + 0.0100691958F, 0.0102624002F, 0.0104574281F, 0.0106542791F, + 0.0108529525F, 0.0110534480F, 0.0112557651F, 0.0114599032F, + 0.0116658618F, 0.0118736405F, 0.0120832387F, 0.0122946560F, + 0.0125078917F, 0.0127229454F, 0.0129398166F, 0.0131585046F, + 0.0133790090F, 0.0136013292F, 0.0138254647F, 0.0140514149F, + 0.0142791792F, 0.0145087572F, 0.0147401481F, 0.0149733515F, + 0.0152083667F, 0.0154451932F, 0.0156838304F, 0.0159242777F, + 0.0161665345F, 0.0164106001F, 0.0166564741F, 0.0169041557F, + 0.0171536443F, 0.0174049393F, 0.0176580401F, 0.0179129461F, + 0.0181696565F, 0.0184281708F, 0.0186884883F, 0.0189506084F, + 0.0192145303F, 0.0194802535F, 0.0197477772F, 0.0200171008F, + 0.0202882236F, 0.0205611449F, 0.0208358639F, 0.0211123801F, + 0.0213906927F, 0.0216708011F, 0.0219527043F, 0.0222364019F, + 0.0225218930F, 0.0228091769F, 0.0230982529F, 0.0233891203F, + 0.0236817782F, 0.0239762259F, 0.0242724628F, 0.0245704880F, + 0.0248703007F, 0.0251719002F, 0.0254752858F, 0.0257804565F, + 0.0260874117F, 0.0263961506F, 0.0267066722F, 0.0270189760F, + 0.0273330609F, 0.0276489263F, 0.0279665712F, 0.0282859949F, + 0.0286071966F, 0.0289301753F, 0.0292549303F, 0.0295814607F, + 0.0299097656F, 0.0302398442F, 0.0305716957F, 0.0309053191F, + 0.0312407135F, 0.0315778782F, 0.0319168122F, 0.0322575145F, + 0.0325999844F, 0.0329442209F, 0.0332902231F, 0.0336379900F, + 0.0339875208F, 0.0343388146F, 0.0346918703F, 0.0350466871F, + 0.0354032640F, 0.0357616000F, 0.0361216943F, 0.0364835458F, + 0.0368471535F, 0.0372125166F, 0.0375796339F, 0.0379485046F, + 0.0383191276F, 0.0386915020F, 0.0390656267F, 0.0394415008F, + 0.0398191231F, 0.0401984927F, 0.0405796086F, 0.0409624698F, + 0.0413470751F, 0.0417334235F, 0.0421215141F, 0.0425113457F, + 0.0429029172F, 0.0432962277F, 0.0436912760F, 0.0440880610F, + 0.0444865817F, 0.0448868370F, 0.0452888257F, 0.0456925468F, + 0.0460979992F, 0.0465051816F, 0.0469140931F, 0.0473247325F, + 0.0477370986F, 0.0481511902F, 0.0485670064F, 0.0489845458F, + 0.0494038074F, 0.0498247899F, 0.0502474922F, 0.0506719131F, + 0.0510980514F, 0.0515259060F, 0.0519554756F, 0.0523867590F, + 0.0528197550F, 0.0532544624F, 0.0536908800F, 0.0541290066F, + 0.0545688408F, 0.0550103815F, 0.0554536274F, 0.0558985772F, + 0.0563452297F, 0.0567935837F, 0.0572436377F, 0.0576953907F, + 0.0581488412F, 0.0586039880F, 0.0590608297F, 0.0595193651F, + 0.0599795929F, 0.0604415117F, 0.0609051202F, 0.0613704170F, + 0.0618374009F, 0.0623060704F, 0.0627764243F, 0.0632484611F, + 0.0637221795F, 0.0641975781F, 0.0646746555F, 0.0651534104F, + 0.0656338413F, 0.0661159469F, 0.0665997257F, 0.0670851763F, + 0.0675722973F, 0.0680610873F, 0.0685515448F, 0.0690436684F, + 0.0695374567F, 0.0700329081F, 0.0705300213F, 0.0710287947F, + 0.0715292269F, 0.0720313163F, 0.0725350616F, 0.0730404612F, + 0.0735475136F, 0.0740562172F, 0.0745665707F, 0.0750785723F, + 0.0755922207F, 0.0761075143F, 0.0766244515F, 0.0771430307F, + 0.0776632505F, 0.0781851092F, 0.0787086052F, 0.0792337371F, + 0.0797605032F, 0.0802889018F, 0.0808189315F, 0.0813505905F, + 0.0818838773F, 0.0824187903F, 0.0829553277F, 0.0834934881F, + 0.0840332697F, 0.0845746708F, 0.0851176899F, 0.0856623252F, + 0.0862085751F, 0.0867564379F, 0.0873059119F, 0.0878569954F, + 0.0884096867F, 0.0889639840F, 0.0895198858F, 0.0900773902F, + 0.0906364955F, 0.0911972000F, 0.0917595019F, 0.0923233995F, + 0.0928888909F, 0.0934559745F, 0.0940246485F, 0.0945949110F, + 0.0951667604F, 0.0957401946F, 0.0963152121F, 0.0968918109F, + 0.0974699893F, 0.0980497454F, 0.0986310773F, 0.0992139832F, + 0.0997984614F, 0.1003845098F, 0.1009721267F, 0.1015613101F, + 0.1021520582F, 0.1027443692F, 0.1033382410F, 0.1039336718F, + 0.1045306597F, 0.1051292027F, 0.1057292990F, 0.1063309466F, + 0.1069341435F, 0.1075388878F, 0.1081451776F, 0.1087530108F, + 0.1093623856F, 0.1099732998F, 0.1105857516F, 0.1111997389F, + 0.1118152597F, 0.1124323121F, 0.1130508939F, 0.1136710032F, + 0.1142926379F, 0.1149157960F, 0.1155404755F, 0.1161666742F, + 0.1167943901F, 0.1174236211F, 0.1180543652F, 0.1186866202F, + 0.1193203841F, 0.1199556548F, 0.1205924300F, 0.1212307078F, + 0.1218704860F, 0.1225117624F, 0.1231545349F, 0.1237988013F, + 0.1244445596F, 0.1250918074F, 0.1257405427F, 0.1263907632F, + 0.1270424667F, 0.1276956512F, 0.1283503142F, 0.1290064537F, + 0.1296640674F, 0.1303231530F, 0.1309837084F, 0.1316457312F, + 0.1323092193F, 0.1329741703F, 0.1336405820F, 0.1343084520F, + 0.1349777782F, 0.1356485582F, 0.1363207897F, 0.1369944704F, + 0.1376695979F, 0.1383461700F, 0.1390241842F, 0.1397036384F, + 0.1403845300F, 0.1410668567F, 0.1417506162F, 0.1424358061F, + 0.1431224240F, 0.1438104674F, 0.1444999341F, 0.1451908216F, + 0.1458831274F, 0.1465768492F, 0.1472719844F, 0.1479685308F, + 0.1486664857F, 0.1493658468F, 0.1500666115F, 0.1507687775F, + 0.1514723422F, 0.1521773031F, 0.1528836577F, 0.1535914035F, + 0.1543005380F, 0.1550110587F, 0.1557229631F, 0.1564362485F, + 0.1571509124F, 0.1578669524F, 0.1585843657F, 0.1593031499F, + 0.1600233024F, 0.1607448205F, 0.1614677017F, 0.1621919433F, + 0.1629175428F, 0.1636444975F, 0.1643728047F, 0.1651024619F, + 0.1658334665F, 0.1665658156F, 0.1672995067F, 0.1680345371F, + 0.1687709041F, 0.1695086050F, 0.1702476372F, 0.1709879978F, + 0.1717296843F, 0.1724726938F, 0.1732170237F, 0.1739626711F, + 0.1747096335F, 0.1754579079F, 0.1762074916F, 0.1769583819F, + 0.1777105760F, 0.1784640710F, 0.1792188642F, 0.1799749529F, + 0.1807323340F, 0.1814910049F, 0.1822509628F, 0.1830122046F, + 0.1837747277F, 0.1845385292F, 0.1853036062F, 0.1860699558F, + 0.1868375751F, 0.1876064613F, 0.1883766114F, 0.1891480226F, + 0.1899206919F, 0.1906946164F, 0.1914697932F, 0.1922462194F, + 0.1930238919F, 0.1938028079F, 0.1945829643F, 0.1953643583F, + 0.1961469868F, 0.1969308468F, 0.1977159353F, 0.1985022494F, + 0.1992897859F, 0.2000785420F, 0.2008685145F, 0.2016597005F, + 0.2024520968F, 0.2032457005F, 0.2040405084F, 0.2048365175F, + 0.2056337247F, 0.2064321269F, 0.2072317211F, 0.2080325041F, + 0.2088344727F, 0.2096376240F, 0.2104419547F, 0.2112474618F, + 0.2120541420F, 0.2128619923F, 0.2136710094F, 0.2144811902F, + 0.2152925315F, 0.2161050301F, 0.2169186829F, 0.2177334866F, + 0.2185494381F, 0.2193665340F, 0.2201847712F, 0.2210041465F, + 0.2218246565F, 0.2226462981F, 0.2234690680F, 0.2242929629F, + 0.2251179796F, 0.2259441147F, 0.2267713650F, 0.2275997272F, + 0.2284291979F, 0.2292597739F, 0.2300914518F, 0.2309242283F, + 0.2317581001F, 0.2325930638F, 0.2334291160F, 0.2342662534F, + 0.2351044727F, 0.2359437703F, 0.2367841431F, 0.2376255875F, + 0.2384681001F, 0.2393116776F, 0.2401563165F, 0.2410020134F, + 0.2418487649F, 0.2426965675F, 0.2435454178F, 0.2443953122F, + 0.2452462474F, 0.2460982199F, 0.2469512262F, 0.2478052628F, + 0.2486603262F, 0.2495164129F, 0.2503735194F, 0.2512316421F, + 0.2520907776F, 0.2529509222F, 0.2538120726F, 0.2546742250F, + 0.2555373760F, 0.2564015219F, 0.2572666593F, 0.2581327845F, + 0.2589998939F, 0.2598679840F, 0.2607370510F, 0.2616070916F, + 0.2624781019F, 0.2633500783F, 0.2642230173F, 0.2650969152F, + 0.2659717684F, 0.2668475731F, 0.2677243257F, 0.2686020226F, + 0.2694806601F, 0.2703602344F, 0.2712407419F, 0.2721221789F, + 0.2730045417F, 0.2738878265F, 0.2747720297F, 0.2756571474F, + 0.2765431760F, 0.2774301117F, 0.2783179508F, 0.2792066895F, + 0.2800963240F, 0.2809868505F, 0.2818782654F, 0.2827705647F, + 0.2836637447F, 0.2845578016F, 0.2854527315F, 0.2863485307F, + 0.2872451953F, 0.2881427215F, 0.2890411055F, 0.2899403433F, + 0.2908404312F, 0.2917413654F, 0.2926431418F, 0.2935457567F, + 0.2944492061F, 0.2953534863F, 0.2962585932F, 0.2971645230F, + 0.2980712717F, 0.2989788356F, 0.2998872105F, 0.3007963927F, + 0.3017063781F, 0.3026171629F, 0.3035287430F, 0.3044411145F, + 0.3053542736F, 0.3062682161F, 0.3071829381F, 0.3080984356F, + 0.3090147047F, 0.3099317413F, 0.3108495414F, 0.3117681011F, + 0.3126874163F, 0.3136074830F, 0.3145282972F, 0.3154498548F, + 0.3163721517F, 0.3172951841F, 0.3182189477F, 0.3191434385F, + 0.3200686525F, 0.3209945856F, 0.3219212336F, 0.3228485927F, + 0.3237766585F, 0.3247054271F, 0.3256348943F, 0.3265650560F, + 0.3274959081F, 0.3284274465F, 0.3293596671F, 0.3302925657F, + 0.3312261382F, 0.3321603804F, 0.3330952882F, 0.3340308574F, + 0.3349670838F, 0.3359039634F, 0.3368414919F, 0.3377796651F, + 0.3387184789F, 0.3396579290F, 0.3405980113F, 0.3415387216F, + 0.3424800556F, 0.3434220091F, 0.3443645779F, 0.3453077578F, + 0.3462515446F, 0.3471959340F, 0.3481409217F, 0.3490865036F, + 0.3500326754F, 0.3509794328F, 0.3519267715F, 0.3528746873F, + 0.3538231759F, 0.3547722330F, 0.3557218544F, 0.3566720357F, + 0.3576227727F, 0.3585740610F, 0.3595258964F, 0.3604782745F, + 0.3614311910F, 0.3623846417F, 0.3633386221F, 0.3642931280F, + 0.3652481549F, 0.3662036987F, 0.3671597548F, 0.3681163191F, + 0.3690733870F, 0.3700309544F, 0.3709890167F, 0.3719475696F, + 0.3729066089F, 0.3738661299F, 0.3748261285F, 0.3757866002F, + 0.3767475406F, 0.3777089453F, 0.3786708100F, 0.3796331302F, + 0.3805959014F, 0.3815591194F, 0.3825227796F, 0.3834868777F, + 0.3844514093F, 0.3854163698F, 0.3863817549F, 0.3873475601F, + 0.3883137810F, 0.3892804131F, 0.3902474521F, 0.3912148933F, + 0.3921827325F, 0.3931509650F, 0.3941195865F, 0.3950885925F, + 0.3960579785F, 0.3970277400F, 0.3979978725F, 0.3989683716F, + 0.3999392328F, 0.4009104516F, 0.4018820234F, 0.4028539438F, + 0.4038262084F, 0.4047988125F, 0.4057717516F, 0.4067450214F, + 0.4077186172F, 0.4086925345F, 0.4096667688F, 0.4106413155F, + 0.4116161703F, 0.4125913284F, 0.4135667854F, 0.4145425368F, + 0.4155185780F, 0.4164949044F, 0.4174715116F, 0.4184483949F, + 0.4194255498F, 0.4204029718F, 0.4213806563F, 0.4223585987F, + 0.4233367946F, 0.4243152392F, 0.4252939281F, 0.4262728566F, + 0.4272520202F, 0.4282314144F, 0.4292110345F, 0.4301908760F, + 0.4311709343F, 0.4321512047F, 0.4331316828F, 0.4341123639F, + 0.4350932435F, 0.4360743168F, 0.4370555794F, 0.4380370267F, + 0.4390186540F, 0.4400004567F, 0.4409824303F, 0.4419645701F, + 0.4429468716F, 0.4439293300F, 0.4449119409F, 0.4458946996F, + 0.4468776014F, 0.4478606418F, 0.4488438162F, 0.4498271199F, + 0.4508105483F, 0.4517940967F, 0.4527777607F, 0.4537615355F, + 0.4547454165F, 0.4557293991F, 0.4567134786F, 0.4576976505F, + 0.4586819101F, 0.4596662527F, 0.4606506738F, 0.4616351687F, + 0.4626197328F, 0.4636043614F, 0.4645890499F, 0.4655737936F, + 0.4665585880F, 0.4675434284F, 0.4685283101F, 0.4695132286F, + 0.4704981791F, 0.4714831570F, 0.4724681577F, 0.4734531766F, + 0.4744382089F, 0.4754232501F, 0.4764082956F, 0.4773933406F, + 0.4783783806F, 0.4793634108F, 0.4803484267F, 0.4813334237F, + 0.4823183969F, 0.4833033419F, 0.4842882540F, 0.4852731285F, + 0.4862579608F, 0.4872427462F, 0.4882274802F, 0.4892121580F, + 0.4901967751F, 0.4911813267F, 0.4921658083F, 0.4931502151F, + 0.4941345427F, 0.4951187863F, 0.4961029412F, 0.4970870029F, + 0.4980709667F, 0.4990548280F, 0.5000385822F, 0.5010222245F, + 0.5020057505F, 0.5029891553F, 0.5039724345F, 0.5049555834F, + 0.5059385973F, 0.5069214716F, 0.5079042018F, 0.5088867831F, + 0.5098692110F, 0.5108514808F, 0.5118335879F, 0.5128155277F, + 0.5137972956F, 0.5147788869F, 0.5157602971F, 0.5167415215F, + 0.5177225555F, 0.5187033945F, 0.5196840339F, 0.5206644692F, + 0.5216446956F, 0.5226247086F, 0.5236045035F, 0.5245840759F, + 0.5255634211F, 0.5265425344F, 0.5275214114F, 0.5285000474F, + 0.5294784378F, 0.5304565781F, 0.5314344637F, 0.5324120899F, + 0.5333894522F, 0.5343665461F, 0.5353433670F, 0.5363199102F, + 0.5372961713F, 0.5382721457F, 0.5392478287F, 0.5402232159F, + 0.5411983027F, 0.5421730845F, 0.5431475569F, 0.5441217151F, + 0.5450955548F, 0.5460690714F, 0.5470422602F, 0.5480151169F, + 0.5489876368F, 0.5499598155F, 0.5509316484F, 0.5519031310F, + 0.5528742587F, 0.5538450271F, 0.5548154317F, 0.5557854680F, + 0.5567551314F, 0.5577244174F, 0.5586933216F, 0.5596618395F, + 0.5606299665F, 0.5615976983F, 0.5625650302F, 0.5635319580F, + 0.5644984770F, 0.5654645828F, 0.5664302709F, 0.5673955370F, + 0.5683603765F, 0.5693247850F, 0.5702887580F, 0.5712522912F, + 0.5722153800F, 0.5731780200F, 0.5741402069F, 0.5751019362F, + 0.5760632034F, 0.5770240042F, 0.5779843341F, 0.5789441889F, + 0.5799035639F, 0.5808624549F, 0.5818208575F, 0.5827787673F, + 0.5837361800F, 0.5846930910F, 0.5856494961F, 0.5866053910F, + 0.5875607712F, 0.5885156324F, 0.5894699703F, 0.5904237804F, + 0.5913770586F, 0.5923298004F, 0.5932820016F, 0.5942336578F, + 0.5951847646F, 0.5961353179F, 0.5970853132F, 0.5980347464F, + 0.5989836131F, 0.5999319090F, 0.6008796298F, 0.6018267713F, + 0.6027733292F, 0.6037192993F, 0.6046646773F, 0.6056094589F, + 0.6065536400F, 0.6074972162F, 0.6084401833F, 0.6093825372F, + 0.6103242736F, 0.6112653884F, 0.6122058772F, 0.6131457359F, + 0.6140849604F, 0.6150235464F, 0.6159614897F, 0.6168987862F, + 0.6178354318F, 0.6187714223F, 0.6197067535F, 0.6206414213F, + 0.6215754215F, 0.6225087501F, 0.6234414028F, 0.6243733757F, + 0.6253046646F, 0.6262352654F, 0.6271651739F, 0.6280943862F, + 0.6290228982F, 0.6299507057F, 0.6308778048F, 0.6318041913F, + 0.6327298612F, 0.6336548105F, 0.6345790352F, 0.6355025312F, + 0.6364252945F, 0.6373473211F, 0.6382686070F, 0.6391891483F, + 0.6401089409F, 0.6410279808F, 0.6419462642F, 0.6428637869F, + 0.6437805452F, 0.6446965350F, 0.6456117524F, 0.6465261935F, + 0.6474398544F, 0.6483527311F, 0.6492648197F, 0.6501761165F, + 0.6510866174F, 0.6519963186F, 0.6529052162F, 0.6538133064F, + 0.6547205854F, 0.6556270492F, 0.6565326941F, 0.6574375162F, + 0.6583415117F, 0.6592446769F, 0.6601470079F, 0.6610485009F, + 0.6619491521F, 0.6628489578F, 0.6637479143F, 0.6646460177F, + 0.6655432643F, 0.6664396505F, 0.6673351724F, 0.6682298264F, + 0.6691236087F, 0.6700165157F, 0.6709085436F, 0.6717996889F, + 0.6726899478F, 0.6735793167F, 0.6744677918F, 0.6753553697F, + 0.6762420466F, 0.6771278190F, 0.6780126832F, 0.6788966357F, + 0.6797796728F, 0.6806617909F, 0.6815429866F, 0.6824232562F, + 0.6833025961F, 0.6841810030F, 0.6850584731F, 0.6859350031F, + 0.6868105894F, 0.6876852284F, 0.6885589168F, 0.6894316510F, + 0.6903034275F, 0.6911742430F, 0.6920440939F, 0.6929129769F, + 0.6937808884F, 0.6946478251F, 0.6955137837F, 0.6963787606F, + 0.6972427525F, 0.6981057560F, 0.6989677678F, 0.6998287845F, + 0.7006888028F, 0.7015478194F, 0.7024058309F, 0.7032628340F, + 0.7041188254F, 0.7049738019F, 0.7058277601F, 0.7066806969F, + 0.7075326089F, 0.7083834929F, 0.7092333457F, 0.7100821640F, + 0.7109299447F, 0.7117766846F, 0.7126223804F, 0.7134670291F, + 0.7143106273F, 0.7151531721F, 0.7159946602F, 0.7168350885F, + 0.7176744539F, 0.7185127534F, 0.7193499837F, 0.7201861418F, + 0.7210212247F, 0.7218552293F, 0.7226881526F, 0.7235199914F, + 0.7243507428F, 0.7251804039F, 0.7260089715F, 0.7268364426F, + 0.7276628144F, 0.7284880839F, 0.7293122481F, 0.7301353040F, + 0.7309572487F, 0.7317780794F, 0.7325977930F, 0.7334163868F, + 0.7342338579F, 0.7350502033F, 0.7358654202F, 0.7366795059F, + 0.7374924573F, 0.7383042718F, 0.7391149465F, 0.7399244787F, + 0.7407328655F, 0.7415401041F, 0.7423461920F, 0.7431511261F, + 0.7439549040F, 0.7447575227F, 0.7455589797F, 0.7463592723F, + 0.7471583976F, 0.7479563532F, 0.7487531363F, 0.7495487443F, + 0.7503431745F, 0.7511364244F, 0.7519284913F, 0.7527193726F, + 0.7535090658F, 0.7542975683F, 0.7550848776F, 0.7558709910F, + 0.7566559062F, 0.7574396205F, 0.7582221314F, 0.7590034366F, + 0.7597835334F, 0.7605624194F, 0.7613400923F, 0.7621165495F, + 0.7628917886F, 0.7636658072F, 0.7644386030F, 0.7652101735F, + 0.7659805164F, 0.7667496292F, 0.7675175098F, 0.7682841556F, + 0.7690495645F, 0.7698137341F, 0.7705766622F, 0.7713383463F, + 0.7720987844F, 0.7728579741F, 0.7736159132F, 0.7743725994F, + 0.7751280306F, 0.7758822046F, 0.7766351192F, 0.7773867722F, + 0.7781371614F, 0.7788862848F, 0.7796341401F, 0.7803807253F, + 0.7811260383F, 0.7818700769F, 0.7826128392F, 0.7833543230F, + 0.7840945263F, 0.7848334471F, 0.7855710833F, 0.7863074330F, + 0.7870424941F, 0.7877762647F, 0.7885087428F, 0.7892399264F, + 0.7899698137F, 0.7906984026F, 0.7914256914F, 0.7921516780F, + 0.7928763607F, 0.7935997375F, 0.7943218065F, 0.7950425661F, + 0.7957620142F, 0.7964801492F, 0.7971969692F, 0.7979124724F, + 0.7986266570F, 0.7993395214F, 0.8000510638F, 0.8007612823F, + 0.8014701754F, 0.8021777413F, 0.8028839784F, 0.8035888849F, + 0.8042924592F, 0.8049946997F, 0.8056956048F, 0.8063951727F, + 0.8070934020F, 0.8077902910F, 0.8084858381F, 0.8091800419F, + 0.8098729007F, 0.8105644130F, 0.8112545774F, 0.8119433922F, + 0.8126308561F, 0.8133169676F, 0.8140017251F, 0.8146851272F, + 0.8153671726F, 0.8160478598F, 0.8167271874F, 0.8174051539F, + 0.8180817582F, 0.8187569986F, 0.8194308741F, 0.8201033831F, + 0.8207745244F, 0.8214442966F, 0.8221126986F, 0.8227797290F, + 0.8234453865F, 0.8241096700F, 0.8247725781F, 0.8254341097F, + 0.8260942636F, 0.8267530385F, 0.8274104334F, 0.8280664470F, + 0.8287210782F, 0.8293743259F, 0.8300261889F, 0.8306766662F, + 0.8313257566F, 0.8319734591F, 0.8326197727F, 0.8332646963F, + 0.8339082288F, 0.8345503692F, 0.8351911167F, 0.8358304700F, + 0.8364684284F, 0.8371049907F, 0.8377401562F, 0.8383739238F, + 0.8390062927F, 0.8396372618F, 0.8402668305F, 0.8408949977F, + 0.8415217626F, 0.8421471245F, 0.8427710823F, 0.8433936354F, + 0.8440147830F, 0.8446345242F, 0.8452528582F, 0.8458697844F, + 0.8464853020F, 0.8470994102F, 0.8477121084F, 0.8483233958F, + 0.8489332718F, 0.8495417356F, 0.8501487866F, 0.8507544243F, + 0.8513586479F, 0.8519614568F, 0.8525628505F, 0.8531628283F, + 0.8537613897F, 0.8543585341F, 0.8549542611F, 0.8555485699F, + 0.8561414603F, 0.8567329315F, 0.8573229832F, 0.8579116149F, + 0.8584988262F, 0.8590846165F, 0.8596689855F, 0.8602519327F, + 0.8608334577F, 0.8614135603F, 0.8619922399F, 0.8625694962F, + 0.8631453289F, 0.8637197377F, 0.8642927222F, 0.8648642821F, + 0.8654344172F, 0.8660031272F, 0.8665704118F, 0.8671362708F, + 0.8677007039F, 0.8682637109F, 0.8688252917F, 0.8693854460F, + 0.8699441737F, 0.8705014745F, 0.8710573485F, 0.8716117953F, + 0.8721648150F, 0.8727164073F, 0.8732665723F, 0.8738153098F, + 0.8743626197F, 0.8749085021F, 0.8754529569F, 0.8759959840F, + 0.8765375835F, 0.8770777553F, 0.8776164996F, 0.8781538162F, + 0.8786897054F, 0.8792241670F, 0.8797572013F, 0.8802888082F, + 0.8808189880F, 0.8813477407F, 0.8818750664F, 0.8824009653F, + 0.8829254375F, 0.8834484833F, 0.8839701028F, 0.8844902961F, + 0.8850090636F, 0.8855264054F, 0.8860423218F, 0.8865568131F, + 0.8870698794F, 0.8875815212F, 0.8880917386F, 0.8886005319F, + 0.8891079016F, 0.8896138479F, 0.8901183712F, 0.8906214719F, + 0.8911231503F, 0.8916234067F, 0.8921222417F, 0.8926196556F, + 0.8931156489F, 0.8936102219F, 0.8941033752F, 0.8945951092F, + 0.8950854244F, 0.8955743212F, 0.8960618003F, 0.8965478621F, + 0.8970325071F, 0.8975157359F, 0.8979975490F, 0.8984779471F, + 0.8989569307F, 0.8994345004F, 0.8999106568F, 0.9003854005F, + 0.9008587323F, 0.9013306526F, 0.9018011623F, 0.9022702619F, + 0.9027379521F, 0.9032042337F, 0.9036691074F, 0.9041325739F, + 0.9045946339F, 0.9050552882F, 0.9055145376F, 0.9059723828F, + 0.9064288246F, 0.9068838638F, 0.9073375013F, 0.9077897379F, + 0.9082405743F, 0.9086900115F, 0.9091380503F, 0.9095846917F, + 0.9100299364F, 0.9104737854F, 0.9109162397F, 0.9113573001F, + 0.9117969675F, 0.9122352430F, 0.9126721275F, 0.9131076219F, + 0.9135417273F, 0.9139744447F, 0.9144057750F, 0.9148357194F, + 0.9152642787F, 0.9156914542F, 0.9161172468F, 0.9165416576F, + 0.9169646877F, 0.9173863382F, 0.9178066102F, 0.9182255048F, + 0.9186430232F, 0.9190591665F, 0.9194739359F, 0.9198873324F, + 0.9202993574F, 0.9207100120F, 0.9211192973F, 0.9215272147F, + 0.9219337653F, 0.9223389504F, 0.9227427713F, 0.9231452290F, + 0.9235463251F, 0.9239460607F, 0.9243444371F, 0.9247414557F, + 0.9251371177F, 0.9255314245F, 0.9259243774F, 0.9263159778F, + 0.9267062270F, 0.9270951264F, 0.9274826774F, 0.9278688814F, + 0.9282537398F, 0.9286372540F, 0.9290194254F, 0.9294002555F, + 0.9297797458F, 0.9301578976F, 0.9305347125F, 0.9309101919F, + 0.9312843373F, 0.9316571503F, 0.9320286323F, 0.9323987849F, + 0.9327676097F, 0.9331351080F, 0.9335012816F, 0.9338661320F, + 0.9342296607F, 0.9345918694F, 0.9349527596F, 0.9353123330F, + 0.9356705911F, 0.9360275357F, 0.9363831683F, 0.9367374905F, + 0.9370905042F, 0.9374422108F, 0.9377926122F, 0.9381417099F, + 0.9384895057F, 0.9388360014F, 0.9391811985F, 0.9395250989F, + 0.9398677043F, 0.9402090165F, 0.9405490371F, 0.9408877680F, + 0.9412252110F, 0.9415613678F, 0.9418962402F, 0.9422298301F, + 0.9425621392F, 0.9428931695F, 0.9432229226F, 0.9435514005F, + 0.9438786050F, 0.9442045381F, 0.9445292014F, 0.9448525971F, + 0.9451747268F, 0.9454955926F, 0.9458151963F, 0.9461335399F, + 0.9464506253F, 0.9467664545F, 0.9470810293F, 0.9473943517F, + 0.9477064238F, 0.9480172474F, 0.9483268246F, 0.9486351573F, + 0.9489422475F, 0.9492480973F, 0.9495527087F, 0.9498560837F, + 0.9501582243F, 0.9504591325F, 0.9507588105F, 0.9510572603F, + 0.9513544839F, 0.9516504834F, 0.9519452609F, 0.9522388186F, + 0.9525311584F, 0.9528222826F, 0.9531121932F, 0.9534008923F, + 0.9536883821F, 0.9539746647F, 0.9542597424F, 0.9545436171F, + 0.9548262912F, 0.9551077667F, 0.9553880459F, 0.9556671309F, + 0.9559450239F, 0.9562217272F, 0.9564972429F, 0.9567715733F, + 0.9570447206F, 0.9573166871F, 0.9575874749F, 0.9578570863F, + 0.9581255236F, 0.9583927890F, 0.9586588849F, 0.9589238134F, + 0.9591875769F, 0.9594501777F, 0.9597116180F, 0.9599719003F, + 0.9602310267F, 0.9604889995F, 0.9607458213F, 0.9610014942F, + 0.9612560206F, 0.9615094028F, 0.9617616433F, 0.9620127443F, + 0.9622627083F, 0.9625115376F, 0.9627592345F, 0.9630058016F, + 0.9632512411F, 0.9634955555F, 0.9637387471F, 0.9639808185F, + 0.9642217720F, 0.9644616100F, 0.9647003349F, 0.9649379493F, + 0.9651744556F, 0.9654098561F, 0.9656441534F, 0.9658773499F, + 0.9661094480F, 0.9663404504F, 0.9665703593F, 0.9667991774F, + 0.9670269071F, 0.9672535509F, 0.9674791114F, 0.9677035909F, + 0.9679269921F, 0.9681493174F, 0.9683705694F, 0.9685907506F, + 0.9688098636F, 0.9690279108F, 0.9692448948F, 0.9694608182F, + 0.9696756836F, 0.9698894934F, 0.9701022503F, 0.9703139569F, + 0.9705246156F, 0.9707342291F, 0.9709428000F, 0.9711503309F, + 0.9713568243F, 0.9715622829F, 0.9717667093F, 0.9719701060F, + 0.9721724757F, 0.9723738210F, 0.9725741446F, 0.9727734490F, + 0.9729717369F, 0.9731690109F, 0.9733652737F, 0.9735605279F, + 0.9737547762F, 0.9739480212F, 0.9741402656F, 0.9743315120F, + 0.9745217631F, 0.9747110216F, 0.9748992901F, 0.9750865714F, + 0.9752728681F, 0.9754581829F, 0.9756425184F, 0.9758258775F, + 0.9760082627F, 0.9761896768F, 0.9763701224F, 0.9765496024F, + 0.9767281193F, 0.9769056760F, 0.9770822751F, 0.9772579193F, + 0.9774326114F, 0.9776063542F, 0.9777791502F, 0.9779510023F, + 0.9781219133F, 0.9782918858F, 0.9784609226F, 0.9786290264F, + 0.9787962000F, 0.9789624461F, 0.9791277676F, 0.9792921671F, + 0.9794556474F, 0.9796182113F, 0.9797798615F, 0.9799406009F, + 0.9801004321F, 0.9802593580F, 0.9804173813F, 0.9805745049F, + 0.9807307314F, 0.9808860637F, 0.9810405046F, 0.9811940568F, + 0.9813467232F, 0.9814985065F, 0.9816494095F, 0.9817994351F, + 0.9819485860F, 0.9820968650F, 0.9822442750F, 0.9823908186F, + 0.9825364988F, 0.9826813184F, 0.9828252801F, 0.9829683868F, + 0.9831106413F, 0.9832520463F, 0.9833926048F, 0.9835323195F, + 0.9836711932F, 0.9838092288F, 0.9839464291F, 0.9840827969F, + 0.9842183351F, 0.9843530464F, 0.9844869337F, 0.9846199998F, + 0.9847522475F, 0.9848836798F, 0.9850142993F, 0.9851441090F, + 0.9852731117F, 0.9854013101F, 0.9855287073F, 0.9856553058F, + 0.9857811087F, 0.9859061188F, 0.9860303388F, 0.9861537717F, + 0.9862764202F, 0.9863982872F, 0.9865193756F, 0.9866396882F, + 0.9867592277F, 0.9868779972F, 0.9869959993F, 0.9871132370F, + 0.9872297131F, 0.9873454304F, 0.9874603918F, 0.9875746001F, + 0.9876880581F, 0.9878007688F, 0.9879127348F, 0.9880239592F, + 0.9881344447F, 0.9882441941F, 0.9883532104F, 0.9884614962F, + 0.9885690546F, 0.9886758883F, 0.9887820001F, 0.9888873930F, + 0.9889920697F, 0.9890960331F, 0.9891992859F, 0.9893018312F, + 0.9894036716F, 0.9895048100F, 0.9896052493F, 0.9897049923F, + 0.9898040418F, 0.9899024006F, 0.9900000717F, 0.9900970577F, + 0.9901933616F, 0.9902889862F, 0.9903839343F, 0.9904782087F, + 0.9905718122F, 0.9906647477F, 0.9907570180F, 0.9908486259F, + 0.9909395742F, 0.9910298658F, 0.9911195034F, 0.9912084899F, + 0.9912968281F, 0.9913845208F, 0.9914715708F, 0.9915579810F, + 0.9916437540F, 0.9917288928F, 0.9918134001F, 0.9918972788F, + 0.9919805316F, 0.9920631613F, 0.9921451707F, 0.9922265626F, + 0.9923073399F, 0.9923875052F, 0.9924670615F, 0.9925460114F, + 0.9926243577F, 0.9927021033F, 0.9927792508F, 0.9928558032F, + 0.9929317631F, 0.9930071333F, 0.9930819167F, 0.9931561158F, + 0.9932297337F, 0.9933027728F, 0.9933752362F, 0.9934471264F, + 0.9935184462F, 0.9935891985F, 0.9936593859F, 0.9937290112F, + 0.9937980771F, 0.9938665864F, 0.9939345418F, 0.9940019460F, + 0.9940688018F, 0.9941351118F, 0.9942008789F, 0.9942661057F, + 0.9943307950F, 0.9943949494F, 0.9944585717F, 0.9945216645F, + 0.9945842307F, 0.9946462728F, 0.9947077936F, 0.9947687957F, + 0.9948292820F, 0.9948892550F, 0.9949487174F, 0.9950076719F, + 0.9950661212F, 0.9951240679F, 0.9951815148F, 0.9952384645F, + 0.9952949196F, 0.9953508828F, 0.9954063568F, 0.9954613442F, + 0.9955158476F, 0.9955698697F, 0.9956234132F, 0.9956764806F, + 0.9957290746F, 0.9957811978F, 0.9958328528F, 0.9958840423F, + 0.9959347688F, 0.9959850351F, 0.9960348435F, 0.9960841969F, + 0.9961330977F, 0.9961815486F, 0.9962295521F, 0.9962771108F, + 0.9963242274F, 0.9963709043F, 0.9964171441F, 0.9964629494F, + 0.9965083228F, 0.9965532668F, 0.9965977840F, 0.9966418768F, + 0.9966855479F, 0.9967287998F, 0.9967716350F, 0.9968140559F, + 0.9968560653F, 0.9968976655F, 0.9969388591F, 0.9969796485F, + 0.9970200363F, 0.9970600250F, 0.9970996170F, 0.9971388149F, + 0.9971776211F, 0.9972160380F, 0.9972540683F, 0.9972917142F, + 0.9973289783F, 0.9973658631F, 0.9974023709F, 0.9974385042F, + 0.9974742655F, 0.9975096571F, 0.9975446816F, 0.9975793413F, + 0.9976136386F, 0.9976475759F, 0.9976811557F, 0.9977143803F, + 0.9977472521F, 0.9977797736F, 0.9978119470F, 0.9978437748F, + 0.9978752593F, 0.9979064029F, 0.9979372079F, 0.9979676768F, + 0.9979978117F, 0.9980276151F, 0.9980570893F, 0.9980862367F, + 0.9981150595F, 0.9981435600F, 0.9981717406F, 0.9981996035F, + 0.9982271511F, 0.9982543856F, 0.9982813093F, 0.9983079246F, + 0.9983342336F, 0.9983602386F, 0.9983859418F, 0.9984113456F, + 0.9984364522F, 0.9984612638F, 0.9984857825F, 0.9985100108F, + 0.9985339507F, 0.9985576044F, 0.9985809743F, 0.9986040624F, + 0.9986268710F, 0.9986494022F, 0.9986716583F, 0.9986936413F, + 0.9987153535F, 0.9987367969F, 0.9987579738F, 0.9987788864F, + 0.9987995366F, 0.9988199267F, 0.9988400587F, 0.9988599348F, + 0.9988795572F, 0.9988989278F, 0.9989180487F, 0.9989369222F, + 0.9989555501F, 0.9989739347F, 0.9989920780F, 0.9990099820F, + 0.9990276487F, 0.9990450803F, 0.9990622787F, 0.9990792460F, + 0.9990959841F, 0.9991124952F, 0.9991287812F, 0.9991448440F, + 0.9991606858F, 0.9991763084F, 0.9991917139F, 0.9992069042F, + 0.9992218813F, 0.9992366471F, 0.9992512035F, 0.9992655525F, + 0.9992796961F, 0.9992936361F, 0.9993073744F, 0.9993209131F, + 0.9993342538F, 0.9993473987F, 0.9993603494F, 0.9993731080F, + 0.9993856762F, 0.9993980559F, 0.9994102490F, 0.9994222573F, + 0.9994340827F, 0.9994457269F, 0.9994571918F, 0.9994684793F, + 0.9994795910F, 0.9994905288F, 0.9995012945F, 0.9995118898F, + 0.9995223165F, 0.9995325765F, 0.9995426713F, 0.9995526029F, + 0.9995623728F, 0.9995719829F, 0.9995814349F, 0.9995907304F, + 0.9995998712F, 0.9996088590F, 0.9996176954F, 0.9996263821F, + 0.9996349208F, 0.9996433132F, 0.9996515609F, 0.9996596656F, + 0.9996676288F, 0.9996754522F, 0.9996831375F, 0.9996906862F, + 0.9996981000F, 0.9997053804F, 0.9997125290F, 0.9997195474F, + 0.9997264371F, 0.9997331998F, 0.9997398369F, 0.9997463500F, + 0.9997527406F, 0.9997590103F, 0.9997651606F, 0.9997711930F, + 0.9997771089F, 0.9997829098F, 0.9997885973F, 0.9997941728F, + 0.9997996378F, 0.9998049936F, 0.9998102419F, 0.9998153839F, + 0.9998204211F, 0.9998253550F, 0.9998301868F, 0.9998349182F, + 0.9998395503F, 0.9998440847F, 0.9998485226F, 0.9998528654F, + 0.9998571146F, 0.9998612713F, 0.9998653370F, 0.9998693130F, + 0.9998732007F, 0.9998770012F, 0.9998807159F, 0.9998843461F, + 0.9998878931F, 0.9998913581F, 0.9998947424F, 0.9998980473F, + 0.9999012740F, 0.9999044237F, 0.9999074976F, 0.9999104971F, + 0.9999134231F, 0.9999162771F, 0.9999190601F, 0.9999217733F, + 0.9999244179F, 0.9999269950F, 0.9999295058F, 0.9999319515F, + 0.9999343332F, 0.9999366519F, 0.9999389088F, 0.9999411050F, + 0.9999432416F, 0.9999453196F, 0.9999473402F, 0.9999493044F, + 0.9999512132F, 0.9999530677F, 0.9999548690F, 0.9999566180F, + 0.9999583157F, 0.9999599633F, 0.9999615616F, 0.9999631116F, + 0.9999646144F, 0.9999660709F, 0.9999674820F, 0.9999688487F, + 0.9999701719F, 0.9999714526F, 0.9999726917F, 0.9999738900F, + 0.9999750486F, 0.9999761682F, 0.9999772497F, 0.9999782941F, + 0.9999793021F, 0.9999802747F, 0.9999812126F, 0.9999821167F, + 0.9999829878F, 0.9999838268F, 0.9999846343F, 0.9999854113F, + 0.9999861584F, 0.9999868765F, 0.9999875664F, 0.9999882287F, + 0.9999888642F, 0.9999894736F, 0.9999900577F, 0.9999906172F, + 0.9999911528F, 0.9999916651F, 0.9999921548F, 0.9999926227F, + 0.9999930693F, 0.9999934954F, 0.9999939015F, 0.9999942883F, + 0.9999946564F, 0.9999950064F, 0.9999953390F, 0.9999956547F, + 0.9999959541F, 0.9999962377F, 0.9999965062F, 0.9999967601F, + 0.9999969998F, 0.9999972260F, 0.9999974392F, 0.9999976399F, + 0.9999978285F, 0.9999980056F, 0.9999981716F, 0.9999983271F, + 0.9999984724F, 0.9999986081F, 0.9999987345F, 0.9999988521F, + 0.9999989613F, 0.9999990625F, 0.9999991562F, 0.9999992426F, + 0.9999993223F, 0.9999993954F, 0.9999994625F, 0.9999995239F, + 0.9999995798F, 0.9999996307F, 0.9999996768F, 0.9999997184F, + 0.9999997559F, 0.9999997895F, 0.9999998195F, 0.9999998462F, + 0.9999998698F, 0.9999998906F, 0.9999999088F, 0.9999999246F, + 0.9999999383F, 0.9999999500F, 0.9999999600F, 0.9999999684F, + 0.9999999754F, 0.9999999811F, 0.9999999858F, 0.9999999896F, + 0.9999999925F, 0.9999999948F, 0.9999999965F, 0.9999999978F, + 0.9999999986F, 0.9999999992F, 0.9999999996F, 0.9999999998F, + 0.9999999999F, 1.0000000000F, 1.0000000000F, 1.0000000000F, +}; + +static float vwin8192[4096] = { + 0.0000000578F, 0.0000005198F, 0.0000014438F, 0.0000028299F, + 0.0000046780F, 0.0000069882F, 0.0000097604F, 0.0000129945F, + 0.0000166908F, 0.0000208490F, 0.0000254692F, 0.0000305515F, + 0.0000360958F, 0.0000421021F, 0.0000485704F, 0.0000555006F, + 0.0000628929F, 0.0000707472F, 0.0000790635F, 0.0000878417F, + 0.0000970820F, 0.0001067842F, 0.0001169483F, 0.0001275744F, + 0.0001386625F, 0.0001502126F, 0.0001622245F, 0.0001746984F, + 0.0001876343F, 0.0002010320F, 0.0002148917F, 0.0002292132F, + 0.0002439967F, 0.0002592421F, 0.0002749493F, 0.0002911184F, + 0.0003077493F, 0.0003248421F, 0.0003423967F, 0.0003604132F, + 0.0003788915F, 0.0003978316F, 0.0004172335F, 0.0004370971F, + 0.0004574226F, 0.0004782098F, 0.0004994587F, 0.0005211694F, + 0.0005433418F, 0.0005659759F, 0.0005890717F, 0.0006126292F, + 0.0006366484F, 0.0006611292F, 0.0006860716F, 0.0007114757F, + 0.0007373414F, 0.0007636687F, 0.0007904576F, 0.0008177080F, + 0.0008454200F, 0.0008735935F, 0.0009022285F, 0.0009313250F, + 0.0009608830F, 0.0009909025F, 0.0010213834F, 0.0010523257F, + 0.0010837295F, 0.0011155946F, 0.0011479211F, 0.0011807090F, + 0.0012139582F, 0.0012476687F, 0.0012818405F, 0.0013164736F, + 0.0013515679F, 0.0013871235F, 0.0014231402F, 0.0014596182F, + 0.0014965573F, 0.0015339576F, 0.0015718190F, 0.0016101415F, + 0.0016489251F, 0.0016881698F, 0.0017278754F, 0.0017680421F, + 0.0018086698F, 0.0018497584F, 0.0018913080F, 0.0019333185F, + 0.0019757898F, 0.0020187221F, 0.0020621151F, 0.0021059690F, + 0.0021502837F, 0.0021950591F, 0.0022402953F, 0.0022859921F, + 0.0023321497F, 0.0023787679F, 0.0024258467F, 0.0024733861F, + 0.0025213861F, 0.0025698466F, 0.0026187676F, 0.0026681491F, + 0.0027179911F, 0.0027682935F, 0.0028190562F, 0.0028702794F, + 0.0029219628F, 0.0029741066F, 0.0030267107F, 0.0030797749F, + 0.0031332994F, 0.0031872841F, 0.0032417289F, 0.0032966338F, + 0.0033519988F, 0.0034078238F, 0.0034641089F, 0.0035208539F, + 0.0035780589F, 0.0036357237F, 0.0036938485F, 0.0037524331F, + 0.0038114775F, 0.0038709817F, 0.0039309456F, 0.0039913692F, + 0.0040522524F, 0.0041135953F, 0.0041753978F, 0.0042376599F, + 0.0043003814F, 0.0043635624F, 0.0044272029F, 0.0044913028F, + 0.0045558620F, 0.0046208806F, 0.0046863585F, 0.0047522955F, + 0.0048186919F, 0.0048855473F, 0.0049528619F, 0.0050206356F, + 0.0050888684F, 0.0051575601F, 0.0052267108F, 0.0052963204F, + 0.0053663890F, 0.0054369163F, 0.0055079025F, 0.0055793474F, + 0.0056512510F, 0.0057236133F, 0.0057964342F, 0.0058697137F, + 0.0059434517F, 0.0060176482F, 0.0060923032F, 0.0061674166F, + 0.0062429883F, 0.0063190183F, 0.0063955066F, 0.0064724532F, + 0.0065498579F, 0.0066277207F, 0.0067060416F, 0.0067848205F, + 0.0068640575F, 0.0069437523F, 0.0070239051F, 0.0071045157F, + 0.0071855840F, 0.0072671102F, 0.0073490940F, 0.0074315355F, + 0.0075144345F, 0.0075977911F, 0.0076816052F, 0.0077658768F, + 0.0078506057F, 0.0079357920F, 0.0080214355F, 0.0081075363F, + 0.0081940943F, 0.0082811094F, 0.0083685816F, 0.0084565108F, + 0.0085448970F, 0.0086337401F, 0.0087230401F, 0.0088127969F, + 0.0089030104F, 0.0089936807F, 0.0090848076F, 0.0091763911F, + 0.0092684311F, 0.0093609276F, 0.0094538805F, 0.0095472898F, + 0.0096411554F, 0.0097354772F, 0.0098302552F, 0.0099254894F, + 0.0100211796F, 0.0101173259F, 0.0102139281F, 0.0103109863F, + 0.0104085002F, 0.0105064700F, 0.0106048955F, 0.0107037766F, + 0.0108031133F, 0.0109029056F, 0.0110031534F, 0.0111038565F, + 0.0112050151F, 0.0113066289F, 0.0114086980F, 0.0115112222F, + 0.0116142015F, 0.0117176359F, 0.0118215252F, 0.0119258695F, + 0.0120306686F, 0.0121359225F, 0.0122416312F, 0.0123477944F, + 0.0124544123F, 0.0125614847F, 0.0126690116F, 0.0127769928F, + 0.0128854284F, 0.0129943182F, 0.0131036623F, 0.0132134604F, + 0.0133237126F, 0.0134344188F, 0.0135455790F, 0.0136571929F, + 0.0137692607F, 0.0138817821F, 0.0139947572F, 0.0141081859F, + 0.0142220681F, 0.0143364037F, 0.0144511927F, 0.0145664350F, + 0.0146821304F, 0.0147982791F, 0.0149148808F, 0.0150319355F, + 0.0151494431F, 0.0152674036F, 0.0153858168F, 0.0155046828F, + 0.0156240014F, 0.0157437726F, 0.0158639962F, 0.0159846723F, + 0.0161058007F, 0.0162273814F, 0.0163494142F, 0.0164718991F, + 0.0165948361F, 0.0167182250F, 0.0168420658F, 0.0169663584F, + 0.0170911027F, 0.0172162987F, 0.0173419462F, 0.0174680452F, + 0.0175945956F, 0.0177215974F, 0.0178490504F, 0.0179769545F, + 0.0181053098F, 0.0182341160F, 0.0183633732F, 0.0184930812F, + 0.0186232399F, 0.0187538494F, 0.0188849094F, 0.0190164200F, + 0.0191483809F, 0.0192807923F, 0.0194136539F, 0.0195469656F, + 0.0196807275F, 0.0198149394F, 0.0199496012F, 0.0200847128F, + 0.0202202742F, 0.0203562853F, 0.0204927460F, 0.0206296561F, + 0.0207670157F, 0.0209048245F, 0.0210430826F, 0.0211817899F, + 0.0213209462F, 0.0214605515F, 0.0216006057F, 0.0217411086F, + 0.0218820603F, 0.0220234605F, 0.0221653093F, 0.0223076066F, + 0.0224503521F, 0.0225935459F, 0.0227371879F, 0.0228812779F, + 0.0230258160F, 0.0231708018F, 0.0233162355F, 0.0234621169F, + 0.0236084459F, 0.0237552224F, 0.0239024462F, 0.0240501175F, + 0.0241982359F, 0.0243468015F, 0.0244958141F, 0.0246452736F, + 0.0247951800F, 0.0249455331F, 0.0250963329F, 0.0252475792F, + 0.0253992720F, 0.0255514111F, 0.0257039965F, 0.0258570281F, + 0.0260105057F, 0.0261644293F, 0.0263187987F, 0.0264736139F, + 0.0266288747F, 0.0267845811F, 0.0269407330F, 0.0270973302F, + 0.0272543727F, 0.0274118604F, 0.0275697930F, 0.0277281707F, + 0.0278869932F, 0.0280462604F, 0.0282059723F, 0.0283661287F, + 0.0285267295F, 0.0286877747F, 0.0288492641F, 0.0290111976F, + 0.0291735751F, 0.0293363965F, 0.0294996617F, 0.0296633706F, + 0.0298275231F, 0.0299921190F, 0.0301571583F, 0.0303226409F, + 0.0304885667F, 0.0306549354F, 0.0308217472F, 0.0309890017F, + 0.0311566989F, 0.0313248388F, 0.0314934211F, 0.0316624459F, + 0.0318319128F, 0.0320018220F, 0.0321721732F, 0.0323429663F, + 0.0325142013F, 0.0326858779F, 0.0328579962F, 0.0330305559F, + 0.0332035570F, 0.0333769994F, 0.0335508829F, 0.0337252074F, + 0.0338999728F, 0.0340751790F, 0.0342508259F, 0.0344269134F, + 0.0346034412F, 0.0347804094F, 0.0349578178F, 0.0351356663F, + 0.0353139548F, 0.0354926831F, 0.0356718511F, 0.0358514588F, + 0.0360315059F, 0.0362119924F, 0.0363929182F, 0.0365742831F, + 0.0367560870F, 0.0369383297F, 0.0371210113F, 0.0373041315F, + 0.0374876902F, 0.0376716873F, 0.0378561226F, 0.0380409961F, + 0.0382263077F, 0.0384120571F, 0.0385982443F, 0.0387848691F, + 0.0389719315F, 0.0391594313F, 0.0393473683F, 0.0395357425F, + 0.0397245537F, 0.0399138017F, 0.0401034866F, 0.0402936080F, + 0.0404841660F, 0.0406751603F, 0.0408665909F, 0.0410584576F, + 0.0412507603F, 0.0414434988F, 0.0416366731F, 0.0418302829F, + 0.0420243282F, 0.0422188088F, 0.0424137246F, 0.0426090755F, + 0.0428048613F, 0.0430010819F, 0.0431977371F, 0.0433948269F, + 0.0435923511F, 0.0437903095F, 0.0439887020F, 0.0441875285F, + 0.0443867889F, 0.0445864830F, 0.0447866106F, 0.0449871717F, + 0.0451881661F, 0.0453895936F, 0.0455914542F, 0.0457937477F, + 0.0459964738F, 0.0461996326F, 0.0464032239F, 0.0466072475F, + 0.0468117032F, 0.0470165910F, 0.0472219107F, 0.0474276622F, + 0.0476338452F, 0.0478404597F, 0.0480475056F, 0.0482549827F, + 0.0484628907F, 0.0486712297F, 0.0488799994F, 0.0490891998F, + 0.0492988306F, 0.0495088917F, 0.0497193830F, 0.0499303043F, + 0.0501416554F, 0.0503534363F, 0.0505656468F, 0.0507782867F, + 0.0509913559F, 0.0512048542F, 0.0514187815F, 0.0516331376F, + 0.0518479225F, 0.0520631358F, 0.0522787775F, 0.0524948475F, + 0.0527113455F, 0.0529282715F, 0.0531456252F, 0.0533634066F, + 0.0535816154F, 0.0538002515F, 0.0540193148F, 0.0542388051F, + 0.0544587222F, 0.0546790660F, 0.0548998364F, 0.0551210331F, + 0.0553426561F, 0.0555647051F, 0.0557871801F, 0.0560100807F, + 0.0562334070F, 0.0564571587F, 0.0566813357F, 0.0569059378F, + 0.0571309649F, 0.0573564168F, 0.0575822933F, 0.0578085942F, + 0.0580353195F, 0.0582624689F, 0.0584900423F, 0.0587180396F, + 0.0589464605F, 0.0591753049F, 0.0594045726F, 0.0596342635F, + 0.0598643774F, 0.0600949141F, 0.0603258735F, 0.0605572555F, + 0.0607890597F, 0.0610212862F, 0.0612539346F, 0.0614870049F, + 0.0617204968F, 0.0619544103F, 0.0621887451F, 0.0624235010F, + 0.0626586780F, 0.0628942758F, 0.0631302942F, 0.0633667331F, + 0.0636035923F, 0.0638408717F, 0.0640785710F, 0.0643166901F, + 0.0645552288F, 0.0647941870F, 0.0650335645F, 0.0652733610F, + 0.0655135765F, 0.0657542108F, 0.0659952636F, 0.0662367348F, + 0.0664786242F, 0.0667209316F, 0.0669636570F, 0.0672068000F, + 0.0674503605F, 0.0676943384F, 0.0679387334F, 0.0681835454F, + 0.0684287742F, 0.0686744196F, 0.0689204814F, 0.0691669595F, + 0.0694138536F, 0.0696611637F, 0.0699088894F, 0.0701570307F, + 0.0704055873F, 0.0706545590F, 0.0709039458F, 0.0711537473F, + 0.0714039634F, 0.0716545939F, 0.0719056387F, 0.0721570975F, + 0.0724089702F, 0.0726612565F, 0.0729139563F, 0.0731670694F, + 0.0734205956F, 0.0736745347F, 0.0739288866F, 0.0741836510F, + 0.0744388277F, 0.0746944166F, 0.0749504175F, 0.0752068301F, + 0.0754636543F, 0.0757208899F, 0.0759785367F, 0.0762365946F, + 0.0764950632F, 0.0767539424F, 0.0770132320F, 0.0772729319F, + 0.0775330418F, 0.0777935616F, 0.0780544909F, 0.0783158298F, + 0.0785775778F, 0.0788397349F, 0.0791023009F, 0.0793652755F, + 0.0796286585F, 0.0798924498F, 0.0801566492F, 0.0804212564F, + 0.0806862712F, 0.0809516935F, 0.0812175231F, 0.0814837597F, + 0.0817504031F, 0.0820174532F, 0.0822849097F, 0.0825527724F, + 0.0828210412F, 0.0830897158F, 0.0833587960F, 0.0836282816F, + 0.0838981724F, 0.0841684682F, 0.0844391688F, 0.0847102740F, + 0.0849817835F, 0.0852536973F, 0.0855260150F, 0.0857987364F, + 0.0860718614F, 0.0863453897F, 0.0866193211F, 0.0868936554F, + 0.0871683924F, 0.0874435319F, 0.0877190737F, 0.0879950175F, + 0.0882713632F, 0.0885481105F, 0.0888252592F, 0.0891028091F, + 0.0893807600F, 0.0896591117F, 0.0899378639F, 0.0902170165F, + 0.0904965692F, 0.0907765218F, 0.0910568740F, 0.0913376258F, + 0.0916187767F, 0.0919003268F, 0.0921822756F, 0.0924646230F, + 0.0927473687F, 0.0930305126F, 0.0933140545F, 0.0935979940F, + 0.0938823310F, 0.0941670653F, 0.0944521966F, 0.0947377247F, + 0.0950236494F, 0.0953099704F, 0.0955966876F, 0.0958838007F, + 0.0961713094F, 0.0964592136F, 0.0967475131F, 0.0970362075F, + 0.0973252967F, 0.0976147805F, 0.0979046585F, 0.0981949307F, + 0.0984855967F, 0.0987766563F, 0.0990681093F, 0.0993599555F, + 0.0996521945F, 0.0999448263F, 0.1002378506F, 0.1005312671F, + 0.1008250755F, 0.1011192757F, 0.1014138675F, 0.1017088505F, + 0.1020042246F, 0.1022999895F, 0.1025961450F, 0.1028926909F, + 0.1031896268F, 0.1034869526F, 0.1037846680F, 0.1040827729F, + 0.1043812668F, 0.1046801497F, 0.1049794213F, 0.1052790813F, + 0.1055791294F, 0.1058795656F, 0.1061803894F, 0.1064816006F, + 0.1067831991F, 0.1070851846F, 0.1073875568F, 0.1076903155F, + 0.1079934604F, 0.1082969913F, 0.1086009079F, 0.1089052101F, + 0.1092098975F, 0.1095149699F, 0.1098204270F, 0.1101262687F, + 0.1104324946F, 0.1107391045F, 0.1110460982F, 0.1113534754F, + 0.1116612359F, 0.1119693793F, 0.1122779055F, 0.1125868142F, + 0.1128961052F, 0.1132057781F, 0.1135158328F, 0.1138262690F, + 0.1141370863F, 0.1144482847F, 0.1147598638F, 0.1150718233F, + 0.1153841631F, 0.1156968828F, 0.1160099822F, 0.1163234610F, + 0.1166373190F, 0.1169515559F, 0.1172661714F, 0.1175811654F, + 0.1178965374F, 0.1182122874F, 0.1185284149F, 0.1188449198F, + 0.1191618018F, 0.1194790606F, 0.1197966960F, 0.1201147076F, + 0.1204330953F, 0.1207518587F, 0.1210709976F, 0.1213905118F, + 0.1217104009F, 0.1220306647F, 0.1223513029F, 0.1226723153F, + 0.1229937016F, 0.1233154615F, 0.1236375948F, 0.1239601011F, + 0.1242829803F, 0.1246062319F, 0.1249298559F, 0.1252538518F, + 0.1255782195F, 0.1259029586F, 0.1262280689F, 0.1265535501F, + 0.1268794019F, 0.1272056241F, 0.1275322163F, 0.1278591784F, + 0.1281865099F, 0.1285142108F, 0.1288422805F, 0.1291707190F, + 0.1294995259F, 0.1298287009F, 0.1301582437F, 0.1304881542F, + 0.1308184319F, 0.1311490766F, 0.1314800881F, 0.1318114660F, + 0.1321432100F, 0.1324753200F, 0.1328077955F, 0.1331406364F, + 0.1334738422F, 0.1338074129F, 0.1341413479F, 0.1344756472F, + 0.1348103103F, 0.1351453370F, 0.1354807270F, 0.1358164801F, + 0.1361525959F, 0.1364890741F, 0.1368259145F, 0.1371631167F, + 0.1375006805F, 0.1378386056F, 0.1381768917F, 0.1385155384F, + 0.1388545456F, 0.1391939129F, 0.1395336400F, 0.1398737266F, + 0.1402141724F, 0.1405549772F, 0.1408961406F, 0.1412376623F, + 0.1415795421F, 0.1419217797F, 0.1422643746F, 0.1426073268F, + 0.1429506358F, 0.1432943013F, 0.1436383231F, 0.1439827008F, + 0.1443274342F, 0.1446725229F, 0.1450179667F, 0.1453637652F, + 0.1457099181F, 0.1460564252F, 0.1464032861F, 0.1467505006F, + 0.1470980682F, 0.1474459888F, 0.1477942620F, 0.1481428875F, + 0.1484918651F, 0.1488411942F, 0.1491908748F, 0.1495409065F, + 0.1498912889F, 0.1502420218F, 0.1505931048F, 0.1509445376F, + 0.1512963200F, 0.1516484516F, 0.1520009321F, 0.1523537612F, + 0.1527069385F, 0.1530604638F, 0.1534143368F, 0.1537685571F, + 0.1541231244F, 0.1544780384F, 0.1548332987F, 0.1551889052F, + 0.1555448574F, 0.1559011550F, 0.1562577978F, 0.1566147853F, + 0.1569721173F, 0.1573297935F, 0.1576878135F, 0.1580461771F, + 0.1584048838F, 0.1587639334F, 0.1591233255F, 0.1594830599F, + 0.1598431361F, 0.1602035540F, 0.1605643131F, 0.1609254131F, + 0.1612868537F, 0.1616486346F, 0.1620107555F, 0.1623732160F, + 0.1627360158F, 0.1630991545F, 0.1634626319F, 0.1638264476F, + 0.1641906013F, 0.1645550926F, 0.1649199212F, 0.1652850869F, + 0.1656505892F, 0.1660164278F, 0.1663826024F, 0.1667491127F, + 0.1671159583F, 0.1674831388F, 0.1678506541F, 0.1682185036F, + 0.1685866872F, 0.1689552044F, 0.1693240549F, 0.1696932384F, + 0.1700627545F, 0.1704326029F, 0.1708027833F, 0.1711732952F, + 0.1715441385F, 0.1719153127F, 0.1722868175F, 0.1726586526F, + 0.1730308176F, 0.1734033121F, 0.1737761359F, 0.1741492886F, + 0.1745227698F, 0.1748965792F, 0.1752707164F, 0.1756451812F, + 0.1760199731F, 0.1763950918F, 0.1767705370F, 0.1771463083F, + 0.1775224054F, 0.1778988279F, 0.1782755754F, 0.1786526477F, + 0.1790300444F, 0.1794077651F, 0.1797858094F, 0.1801641771F, + 0.1805428677F, 0.1809218810F, 0.1813012165F, 0.1816808739F, + 0.1820608528F, 0.1824411530F, 0.1828217739F, 0.1832027154F, + 0.1835839770F, 0.1839655584F, 0.1843474592F, 0.1847296790F, + 0.1851122175F, 0.1854950744F, 0.1858782492F, 0.1862617417F, + 0.1866455514F, 0.1870296780F, 0.1874141211F, 0.1877988804F, + 0.1881839555F, 0.1885693461F, 0.1889550517F, 0.1893410721F, + 0.1897274068F, 0.1901140555F, 0.1905010178F, 0.1908882933F, + 0.1912758818F, 0.1916637828F, 0.1920519959F, 0.1924405208F, + 0.1928293571F, 0.1932185044F, 0.1936079625F, 0.1939977308F, + 0.1943878091F, 0.1947781969F, 0.1951688939F, 0.1955598998F, + 0.1959512141F, 0.1963428364F, 0.1967347665F, 0.1971270038F, + 0.1975195482F, 0.1979123990F, 0.1983055561F, 0.1986990190F, + 0.1990927873F, 0.1994868607F, 0.1998812388F, 0.2002759212F, + 0.2006709075F, 0.2010661974F, 0.2014617904F, 0.2018576862F, + 0.2022538844F, 0.2026503847F, 0.2030471865F, 0.2034442897F, + 0.2038416937F, 0.2042393982F, 0.2046374028F, 0.2050357071F, + 0.2054343107F, 0.2058332133F, 0.2062324145F, 0.2066319138F, + 0.2070317110F, 0.2074318055F, 0.2078321970F, 0.2082328852F, + 0.2086338696F, 0.2090351498F, 0.2094367255F, 0.2098385962F, + 0.2102407617F, 0.2106432213F, 0.2110459749F, 0.2114490220F, + 0.2118523621F, 0.2122559950F, 0.2126599202F, 0.2130641373F, + 0.2134686459F, 0.2138734456F, 0.2142785361F, 0.2146839168F, + 0.2150895875F, 0.2154955478F, 0.2159017972F, 0.2163083353F, + 0.2167151617F, 0.2171222761F, 0.2175296780F, 0.2179373670F, + 0.2183453428F, 0.2187536049F, 0.2191621529F, 0.2195709864F, + 0.2199801051F, 0.2203895085F, 0.2207991961F, 0.2212091677F, + 0.2216194228F, 0.2220299610F, 0.2224407818F, 0.2228518850F, + 0.2232632699F, 0.2236749364F, 0.2240868839F, 0.2244991121F, + 0.2249116204F, 0.2253244086F, 0.2257374763F, 0.2261508229F, + 0.2265644481F, 0.2269783514F, 0.2273925326F, 0.2278069911F, + 0.2282217265F, 0.2286367384F, 0.2290520265F, 0.2294675902F, + 0.2298834292F, 0.2302995431F, 0.2307159314F, 0.2311325937F, + 0.2315495297F, 0.2319667388F, 0.2323842207F, 0.2328019749F, + 0.2332200011F, 0.2336382988F, 0.2340568675F, 0.2344757070F, + 0.2348948166F, 0.2353141961F, 0.2357338450F, 0.2361537629F, + 0.2365739493F, 0.2369944038F, 0.2374151261F, 0.2378361156F, + 0.2382573720F, 0.2386788948F, 0.2391006836F, 0.2395227380F, + 0.2399450575F, 0.2403676417F, 0.2407904902F, 0.2412136026F, + 0.2416369783F, 0.2420606171F, 0.2424845185F, 0.2429086820F, + 0.2433331072F, 0.2437577936F, 0.2441827409F, 0.2446079486F, + 0.2450334163F, 0.2454591435F, 0.2458851298F, 0.2463113747F, + 0.2467378779F, 0.2471646389F, 0.2475916573F, 0.2480189325F, + 0.2484464643F, 0.2488742521F, 0.2493022955F, 0.2497305940F, + 0.2501591473F, 0.2505879549F, 0.2510170163F, 0.2514463311F, + 0.2518758989F, 0.2523057193F, 0.2527357916F, 0.2531661157F, + 0.2535966909F, 0.2540275169F, 0.2544585931F, 0.2548899193F, + 0.2553214948F, 0.2557533193F, 0.2561853924F, 0.2566177135F, + 0.2570502822F, 0.2574830981F, 0.2579161608F, 0.2583494697F, + 0.2587830245F, 0.2592168246F, 0.2596508697F, 0.2600851593F, + 0.2605196929F, 0.2609544701F, 0.2613894904F, 0.2618247534F, + 0.2622602586F, 0.2626960055F, 0.2631319938F, 0.2635682230F, + 0.2640046925F, 0.2644414021F, 0.2648783511F, 0.2653155391F, + 0.2657529657F, 0.2661906305F, 0.2666285329F, 0.2670666725F, + 0.2675050489F, 0.2679436616F, 0.2683825101F, 0.2688215940F, + 0.2692609127F, 0.2697004660F, 0.2701402532F, 0.2705802739F, + 0.2710205278F, 0.2714610142F, 0.2719017327F, 0.2723426830F, + 0.2727838644F, 0.2732252766F, 0.2736669191F, 0.2741087914F, + 0.2745508930F, 0.2749932235F, 0.2754357824F, 0.2758785693F, + 0.2763215837F, 0.2767648251F, 0.2772082930F, 0.2776519870F, + 0.2780959066F, 0.2785400513F, 0.2789844207F, 0.2794290143F, + 0.2798738316F, 0.2803188722F, 0.2807641355F, 0.2812096211F, + 0.2816553286F, 0.2821012574F, 0.2825474071F, 0.2829937773F, + 0.2834403673F, 0.2838871768F, 0.2843342053F, 0.2847814523F, + 0.2852289174F, 0.2856765999F, 0.2861244996F, 0.2865726159F, + 0.2870209482F, 0.2874694962F, 0.2879182594F, 0.2883672372F, + 0.2888164293F, 0.2892658350F, 0.2897154540F, 0.2901652858F, + 0.2906153298F, 0.2910655856F, 0.2915160527F, 0.2919667306F, + 0.2924176189F, 0.2928687171F, 0.2933200246F, 0.2937715409F, + 0.2942232657F, 0.2946751984F, 0.2951273386F, 0.2955796856F, + 0.2960322391F, 0.2964849986F, 0.2969379636F, 0.2973911335F, + 0.2978445080F, 0.2982980864F, 0.2987518684F, 0.2992058534F, + 0.2996600409F, 0.3001144305F, 0.3005690217F, 0.3010238139F, + 0.3014788067F, 0.3019339995F, 0.3023893920F, 0.3028449835F, + 0.3033007736F, 0.3037567618F, 0.3042129477F, 0.3046693306F, + 0.3051259102F, 0.3055826859F, 0.3060396572F, 0.3064968236F, + 0.3069541847F, 0.3074117399F, 0.3078694887F, 0.3083274307F, + 0.3087855653F, 0.3092438920F, 0.3097024104F, 0.3101611199F, + 0.3106200200F, 0.3110791103F, 0.3115383902F, 0.3119978592F, + 0.3124575169F, 0.3129173627F, 0.3133773961F, 0.3138376166F, + 0.3142980238F, 0.3147586170F, 0.3152193959F, 0.3156803598F, + 0.3161415084F, 0.3166028410F, 0.3170643573F, 0.3175260566F, + 0.3179879384F, 0.3184500023F, 0.3189122478F, 0.3193746743F, + 0.3198372814F, 0.3203000685F, 0.3207630351F, 0.3212261807F, + 0.3216895048F, 0.3221530069F, 0.3226166865F, 0.3230805430F, + 0.3235445760F, 0.3240087849F, 0.3244731693F, 0.3249377285F, + 0.3254024622F, 0.3258673698F, 0.3263324507F, 0.3267977045F, + 0.3272631306F, 0.3277287286F, 0.3281944978F, 0.3286604379F, + 0.3291265482F, 0.3295928284F, 0.3300592777F, 0.3305258958F, + 0.3309926821F, 0.3314596361F, 0.3319267573F, 0.3323940451F, + 0.3328614990F, 0.3333291186F, 0.3337969033F, 0.3342648525F, + 0.3347329658F, 0.3352012427F, 0.3356696825F, 0.3361382849F, + 0.3366070492F, 0.3370759749F, 0.3375450616F, 0.3380143087F, + 0.3384837156F, 0.3389532819F, 0.3394230071F, 0.3398928905F, + 0.3403629317F, 0.3408331302F, 0.3413034854F, 0.3417739967F, + 0.3422446638F, 0.3427154860F, 0.3431864628F, 0.3436575938F, + 0.3441288782F, 0.3446003158F, 0.3450719058F, 0.3455436478F, + 0.3460155412F, 0.3464875856F, 0.3469597804F, 0.3474321250F, + 0.3479046189F, 0.3483772617F, 0.3488500527F, 0.3493229914F, + 0.3497960774F, 0.3502693100F, 0.3507426887F, 0.3512162131F, + 0.3516898825F, 0.3521636965F, 0.3526376545F, 0.3531117559F, + 0.3535860003F, 0.3540603870F, 0.3545349157F, 0.3550095856F, + 0.3554843964F, 0.3559593474F, 0.3564344381F, 0.3569096680F, + 0.3573850366F, 0.3578605432F, 0.3583361875F, 0.3588119687F, + 0.3592878865F, 0.3597639402F, 0.3602401293F, 0.3607164533F, + 0.3611929117F, 0.3616695038F, 0.3621462292F, 0.3626230873F, + 0.3631000776F, 0.3635771995F, 0.3640544525F, 0.3645318360F, + 0.3650093496F, 0.3654869926F, 0.3659647645F, 0.3664426648F, + 0.3669206930F, 0.3673988484F, 0.3678771306F, 0.3683555390F, + 0.3688340731F, 0.3693127322F, 0.3697915160F, 0.3702704237F, + 0.3707494549F, 0.3712286091F, 0.3717078857F, 0.3721872840F, + 0.3726668037F, 0.3731464441F, 0.3736262047F, 0.3741060850F, + 0.3745860843F, 0.3750662023F, 0.3755464382F, 0.3760267915F, + 0.3765072618F, 0.3769878484F, 0.3774685509F, 0.3779493686F, + 0.3784303010F, 0.3789113475F, 0.3793925076F, 0.3798737809F, + 0.3803551666F, 0.3808366642F, 0.3813182733F, 0.3817999932F, + 0.3822818234F, 0.3827637633F, 0.3832458124F, 0.3837279702F, + 0.3842102360F, 0.3846926093F, 0.3851750897F, 0.3856576764F, + 0.3861403690F, 0.3866231670F, 0.3871060696F, 0.3875890765F, + 0.3880721870F, 0.3885554007F, 0.3890387168F, 0.3895221349F, + 0.3900056544F, 0.3904892748F, 0.3909729955F, 0.3914568160F, + 0.3919407356F, 0.3924247539F, 0.3929088702F, 0.3933930841F, + 0.3938773949F, 0.3943618021F, 0.3948463052F, 0.3953309035F, + 0.3958155966F, 0.3963003838F, 0.3967852646F, 0.3972702385F, + 0.3977553048F, 0.3982404631F, 0.3987257127F, 0.3992110531F, + 0.3996964838F, 0.4001820041F, 0.4006676136F, 0.4011533116F, + 0.4016390976F, 0.4021249710F, 0.4026109313F, 0.4030969779F, + 0.4035831102F, 0.4040693277F, 0.4045556299F, 0.4050420160F, + 0.4055284857F, 0.4060150383F, 0.4065016732F, 0.4069883899F, + 0.4074751879F, 0.4079620665F, 0.4084490252F, 0.4089360635F, + 0.4094231807F, 0.4099103763F, 0.4103976498F, 0.4108850005F, + 0.4113724280F, 0.4118599315F, 0.4123475107F, 0.4128351648F, + 0.4133228934F, 0.4138106959F, 0.4142985716F, 0.4147865201F, + 0.4152745408F, 0.4157626330F, 0.4162507963F, 0.4167390301F, + 0.4172273337F, 0.4177157067F, 0.4182041484F, 0.4186926583F, + 0.4191812359F, 0.4196698805F, 0.4201585915F, 0.4206473685F, + 0.4211362108F, 0.4216251179F, 0.4221140892F, 0.4226031241F, + 0.4230922221F, 0.4235813826F, 0.4240706050F, 0.4245598887F, + 0.4250492332F, 0.4255386379F, 0.4260281022F, 0.4265176256F, + 0.4270072075F, 0.4274968473F, 0.4279865445F, 0.4284762984F, + 0.4289661086F, 0.4294559743F, 0.4299458951F, 0.4304358704F, + 0.4309258996F, 0.4314159822F, 0.4319061175F, 0.4323963050F, + 0.4328865441F, 0.4333768342F, 0.4338671749F, 0.4343575654F, + 0.4348480052F, 0.4353384938F, 0.4358290306F, 0.4363196149F, + 0.4368102463F, 0.4373009241F, 0.4377916478F, 0.4382824168F, + 0.4387732305F, 0.4392640884F, 0.4397549899F, 0.4402459343F, + 0.4407369212F, 0.4412279499F, 0.4417190198F, 0.4422101305F, + 0.4427012813F, 0.4431924717F, 0.4436837010F, 0.4441749686F, + 0.4446662742F, 0.4451576169F, 0.4456489963F, 0.4461404118F, + 0.4466318628F, 0.4471233487F, 0.4476148690F, 0.4481064230F, + 0.4485980103F, 0.4490896302F, 0.4495812821F, 0.4500729654F, + 0.4505646797F, 0.4510564243F, 0.4515481986F, 0.4520400021F, + 0.4525318341F, 0.4530236942F, 0.4535155816F, 0.4540074959F, + 0.4544994365F, 0.4549914028F, 0.4554833941F, 0.4559754100F, + 0.4564674499F, 0.4569595131F, 0.4574515991F, 0.4579437074F, + 0.4584358372F, 0.4589279881F, 0.4594201595F, 0.4599123508F, + 0.4604045615F, 0.4608967908F, 0.4613890383F, 0.4618813034F, + 0.4623735855F, 0.4628658841F, 0.4633581984F, 0.4638505281F, + 0.4643428724F, 0.4648352308F, 0.4653276028F, 0.4658199877F, + 0.4663123849F, 0.4668047940F, 0.4672972143F, 0.4677896451F, + 0.4682820861F, 0.4687745365F, 0.4692669958F, 0.4697594634F, + 0.4702519387F, 0.4707444211F, 0.4712369102F, 0.4717294052F, + 0.4722219056F, 0.4727144109F, 0.4732069204F, 0.4736994336F, + 0.4741919498F, 0.4746844686F, 0.4751769893F, 0.4756695113F, + 0.4761620341F, 0.4766545571F, 0.4771470797F, 0.4776396013F, + 0.4781321213F, 0.4786246392F, 0.4791171544F, 0.4796096663F, + 0.4801021744F, 0.4805946779F, 0.4810871765F, 0.4815796694F, + 0.4820721561F, 0.4825646360F, 0.4830571086F, 0.4835495732F, + 0.4840420293F, 0.4845344763F, 0.4850269136F, 0.4855193407F, + 0.4860117569F, 0.4865041617F, 0.4869965545F, 0.4874889347F, + 0.4879813018F, 0.4884736551F, 0.4889659941F, 0.4894583182F, + 0.4899506268F, 0.4904429193F, 0.4909351952F, 0.4914274538F, + 0.4919196947F, 0.4924119172F, 0.4929041207F, 0.4933963046F, + 0.4938884685F, 0.4943806116F, 0.4948727335F, 0.4953648335F, + 0.4958569110F, 0.4963489656F, 0.4968409965F, 0.4973330032F, + 0.4978249852F, 0.4983169419F, 0.4988088726F, 0.4993007768F, + 0.4997926539F, 0.5002845034F, 0.5007763247F, 0.5012681171F, + 0.5017598801F, 0.5022516132F, 0.5027433157F, 0.5032349871F, + 0.5037266268F, 0.5042182341F, 0.5047098086F, 0.5052013497F, + 0.5056928567F, 0.5061843292F, 0.5066757664F, 0.5071671679F, + 0.5076585330F, 0.5081498613F, 0.5086411520F, 0.5091324047F, + 0.5096236187F, 0.5101147934F, 0.5106059284F, 0.5110970230F, + 0.5115880766F, 0.5120790887F, 0.5125700587F, 0.5130609860F, + 0.5135518700F, 0.5140427102F, 0.5145335059F, 0.5150242566F, + 0.5155149618F, 0.5160056208F, 0.5164962331F, 0.5169867980F, + 0.5174773151F, 0.5179677837F, 0.5184582033F, 0.5189485733F, + 0.5194388931F, 0.5199291621F, 0.5204193798F, 0.5209095455F, + 0.5213996588F, 0.5218897190F, 0.5223797256F, 0.5228696779F, + 0.5233595755F, 0.5238494177F, 0.5243392039F, 0.5248289337F, + 0.5253186063F, 0.5258082213F, 0.5262977781F, 0.5267872760F, + 0.5272767146F, 0.5277660932F, 0.5282554112F, 0.5287446682F, + 0.5292338635F, 0.5297229965F, 0.5302120667F, 0.5307010736F, + 0.5311900164F, 0.5316788947F, 0.5321677079F, 0.5326564554F, + 0.5331451366F, 0.5336337511F, 0.5341222981F, 0.5346107771F, + 0.5350991876F, 0.5355875290F, 0.5360758007F, 0.5365640021F, + 0.5370521327F, 0.5375401920F, 0.5380281792F, 0.5385160939F, + 0.5390039355F, 0.5394917034F, 0.5399793971F, 0.5404670159F, + 0.5409545594F, 0.5414420269F, 0.5419294179F, 0.5424167318F, + 0.5429039680F, 0.5433911261F, 0.5438782053F, 0.5443652051F, + 0.5448521250F, 0.5453389644F, 0.5458257228F, 0.5463123995F, + 0.5467989940F, 0.5472855057F, 0.5477719341F, 0.5482582786F, + 0.5487445387F, 0.5492307137F, 0.5497168031F, 0.5502028063F, + 0.5506887228F, 0.5511745520F, 0.5516602934F, 0.5521459463F, + 0.5526315103F, 0.5531169847F, 0.5536023690F, 0.5540876626F, + 0.5545728649F, 0.5550579755F, 0.5555429937F, 0.5560279189F, + 0.5565127507F, 0.5569974884F, 0.5574821315F, 0.5579666794F, + 0.5584511316F, 0.5589354875F, 0.5594197465F, 0.5599039080F, + 0.5603879716F, 0.5608719367F, 0.5613558026F, 0.5618395689F, + 0.5623232350F, 0.5628068002F, 0.5632902642F, 0.5637736262F, + 0.5642568858F, 0.5647400423F, 0.5652230953F, 0.5657060442F, + 0.5661888883F, 0.5666716272F, 0.5671542603F, 0.5676367870F, + 0.5681192069F, 0.5686015192F, 0.5690837235F, 0.5695658192F, + 0.5700478058F, 0.5705296827F, 0.5710114494F, 0.5714931052F, + 0.5719746497F, 0.5724560822F, 0.5729374023F, 0.5734186094F, + 0.5738997029F, 0.5743806823F, 0.5748615470F, 0.5753422965F, + 0.5758229301F, 0.5763034475F, 0.5767838480F, 0.5772641310F, + 0.5777442960F, 0.5782243426F, 0.5787042700F, 0.5791840778F, + 0.5796637654F, 0.5801433322F, 0.5806227778F, 0.5811021016F, + 0.5815813029F, 0.5820603814F, 0.5825393363F, 0.5830181673F, + 0.5834968737F, 0.5839754549F, 0.5844539105F, 0.5849322399F, + 0.5854104425F, 0.5858885179F, 0.5863664653F, 0.5868442844F, + 0.5873219746F, 0.5877995353F, 0.5882769660F, 0.5887542661F, + 0.5892314351F, 0.5897084724F, 0.5901853776F, 0.5906621500F, + 0.5911387892F, 0.5916152945F, 0.5920916655F, 0.5925679016F, + 0.5930440022F, 0.5935199669F, 0.5939957950F, 0.5944714861F, + 0.5949470396F, 0.5954224550F, 0.5958977317F, 0.5963728692F, + 0.5968478669F, 0.5973227244F, 0.5977974411F, 0.5982720163F, + 0.5987464497F, 0.5992207407F, 0.5996948887F, 0.6001688932F, + 0.6006427537F, 0.6011164696F, 0.6015900405F, 0.6020634657F, + 0.6025367447F, 0.6030098770F, 0.6034828621F, 0.6039556995F, + 0.6044283885F, 0.6049009288F, 0.6053733196F, 0.6058455606F, + 0.6063176512F, 0.6067895909F, 0.6072613790F, 0.6077330152F, + 0.6082044989F, 0.6086758295F, 0.6091470065F, 0.6096180294F, + 0.6100888977F, 0.6105596108F, 0.6110301682F, 0.6115005694F, + 0.6119708139F, 0.6124409011F, 0.6129108305F, 0.6133806017F, + 0.6138502139F, 0.6143196669F, 0.6147889599F, 0.6152580926F, + 0.6157270643F, 0.6161958746F, 0.6166645230F, 0.6171330088F, + 0.6176013317F, 0.6180694910F, 0.6185374863F, 0.6190053171F, + 0.6194729827F, 0.6199404828F, 0.6204078167F, 0.6208749841F, + 0.6213419842F, 0.6218088168F, 0.6222754811F, 0.6227419768F, + 0.6232083032F, 0.6236744600F, 0.6241404465F, 0.6246062622F, + 0.6250719067F, 0.6255373795F, 0.6260026799F, 0.6264678076F, + 0.6269327619F, 0.6273975425F, 0.6278621487F, 0.6283265800F, + 0.6287908361F, 0.6292549163F, 0.6297188201F, 0.6301825471F, + 0.6306460966F, 0.6311094683F, 0.6315726617F, 0.6320356761F, + 0.6324985111F, 0.6329611662F, 0.6334236410F, 0.6338859348F, + 0.6343480472F, 0.6348099777F, 0.6352717257F, 0.6357332909F, + 0.6361946726F, 0.6366558704F, 0.6371168837F, 0.6375777122F, + 0.6380383552F, 0.6384988123F, 0.6389590830F, 0.6394191668F, + 0.6398790631F, 0.6403387716F, 0.6407982916F, 0.6412576228F, + 0.6417167645F, 0.6421757163F, 0.6426344778F, 0.6430930483F, + 0.6435514275F, 0.6440096149F, 0.6444676098F, 0.6449254119F, + 0.6453830207F, 0.6458404356F, 0.6462976562F, 0.6467546820F, + 0.6472115125F, 0.6476681472F, 0.6481245856F, 0.6485808273F, + 0.6490368717F, 0.6494927183F, 0.6499483667F, 0.6504038164F, + 0.6508590670F, 0.6513141178F, 0.6517689684F, 0.6522236185F, + 0.6526780673F, 0.6531323146F, 0.6535863598F, 0.6540402024F, + 0.6544938419F, 0.6549472779F, 0.6554005099F, 0.6558535373F, + 0.6563063598F, 0.6567589769F, 0.6572113880F, 0.6576635927F, + 0.6581155906F, 0.6585673810F, 0.6590189637F, 0.6594703380F, + 0.6599215035F, 0.6603724598F, 0.6608232064F, 0.6612737427F, + 0.6617240684F, 0.6621741829F, 0.6626240859F, 0.6630737767F, + 0.6635232550F, 0.6639725202F, 0.6644215720F, 0.6648704098F, + 0.6653190332F, 0.6657674417F, 0.6662156348F, 0.6666636121F, + 0.6671113731F, 0.6675589174F, 0.6680062445F, 0.6684533538F, + 0.6689002450F, 0.6693469177F, 0.6697933712F, 0.6702396052F, + 0.6706856193F, 0.6711314129F, 0.6715769855F, 0.6720223369F, + 0.6724674664F, 0.6729123736F, 0.6733570581F, 0.6738015194F, + 0.6742457570F, 0.6746897706F, 0.6751335596F, 0.6755771236F, + 0.6760204621F, 0.6764635747F, 0.6769064609F, 0.6773491204F, + 0.6777915525F, 0.6782337570F, 0.6786757332F, 0.6791174809F, + 0.6795589995F, 0.6800002886F, 0.6804413477F, 0.6808821765F, + 0.6813227743F, 0.6817631409F, 0.6822032758F, 0.6826431785F, + 0.6830828485F, 0.6835222855F, 0.6839614890F, 0.6844004585F, + 0.6848391936F, 0.6852776939F, 0.6857159589F, 0.6861539883F, + 0.6865917815F, 0.6870293381F, 0.6874666576F, 0.6879037398F, + 0.6883405840F, 0.6887771899F, 0.6892135571F, 0.6896496850F, + 0.6900855733F, 0.6905212216F, 0.6909566294F, 0.6913917963F, + 0.6918267218F, 0.6922614055F, 0.6926958471F, 0.6931300459F, + 0.6935640018F, 0.6939977141F, 0.6944311825F, 0.6948644066F, + 0.6952973859F, 0.6957301200F, 0.6961626085F, 0.6965948510F, + 0.6970268470F, 0.6974585961F, 0.6978900980F, 0.6983213521F, + 0.6987523580F, 0.6991831154F, 0.6996136238F, 0.7000438828F, + 0.7004738921F, 0.7009036510F, 0.7013331594F, 0.7017624166F, + 0.7021914224F, 0.7026201763F, 0.7030486779F, 0.7034769268F, + 0.7039049226F, 0.7043326648F, 0.7047601531F, 0.7051873870F, + 0.7056143662F, 0.7060410902F, 0.7064675586F, 0.7068937711F, + 0.7073197271F, 0.7077454264F, 0.7081708684F, 0.7085960529F, + 0.7090209793F, 0.7094456474F, 0.7098700566F, 0.7102942066F, + 0.7107180970F, 0.7111417274F, 0.7115650974F, 0.7119882066F, + 0.7124110545F, 0.7128336409F, 0.7132559653F, 0.7136780272F, + 0.7140998264F, 0.7145213624F, 0.7149426348F, 0.7153636433F, + 0.7157843874F, 0.7162048668F, 0.7166250810F, 0.7170450296F, + 0.7174647124F, 0.7178841289F, 0.7183032786F, 0.7187221613F, + 0.7191407765F, 0.7195591239F, 0.7199772030F, 0.7203950135F, + 0.7208125550F, 0.7212298271F, 0.7216468294F, 0.7220635616F, + 0.7224800233F, 0.7228962140F, 0.7233121335F, 0.7237277813F, + 0.7241431571F, 0.7245582604F, 0.7249730910F, 0.7253876484F, + 0.7258019322F, 0.7262159422F, 0.7266296778F, 0.7270431388F, + 0.7274563247F, 0.7278692353F, 0.7282818700F, 0.7286942287F, + 0.7291063108F, 0.7295181160F, 0.7299296440F, 0.7303408944F, + 0.7307518669F, 0.7311625609F, 0.7315729763F, 0.7319831126F, + 0.7323929695F, 0.7328025466F, 0.7332118435F, 0.7336208600F, + 0.7340295955F, 0.7344380499F, 0.7348462226F, 0.7352541134F, + 0.7356617220F, 0.7360690478F, 0.7364760907F, 0.7368828502F, + 0.7372893259F, 0.7376955176F, 0.7381014249F, 0.7385070475F, + 0.7389123849F, 0.7393174368F, 0.7397222029F, 0.7401266829F, + 0.7405308763F, 0.7409347829F, 0.7413384023F, 0.7417417341F, + 0.7421447780F, 0.7425475338F, 0.7429500009F, 0.7433521791F, + 0.7437540681F, 0.7441556674F, 0.7445569769F, 0.7449579960F, + 0.7453587245F, 0.7457591621F, 0.7461593084F, 0.7465591631F, + 0.7469587259F, 0.7473579963F, 0.7477569741F, 0.7481556590F, + 0.7485540506F, 0.7489521486F, 0.7493499526F, 0.7497474623F, + 0.7501446775F, 0.7505415977F, 0.7509382227F, 0.7513345521F, + 0.7517305856F, 0.7521263229F, 0.7525217636F, 0.7529169074F, + 0.7533117541F, 0.7537063032F, 0.7541005545F, 0.7544945076F, + 0.7548881623F, 0.7552815182F, 0.7556745749F, 0.7560673323F, + 0.7564597899F, 0.7568519474F, 0.7572438046F, 0.7576353611F, + 0.7580266166F, 0.7584175708F, 0.7588082235F, 0.7591985741F, + 0.7595886226F, 0.7599783685F, 0.7603678116F, 0.7607569515F, + 0.7611457879F, 0.7615343206F, 0.7619225493F, 0.7623104735F, + 0.7626980931F, 0.7630854078F, 0.7634724171F, 0.7638591209F, + 0.7642455188F, 0.7646316106F, 0.7650173959F, 0.7654028744F, + 0.7657880459F, 0.7661729100F, 0.7665574664F, 0.7669417150F, + 0.7673256553F, 0.7677092871F, 0.7680926100F, 0.7684756239F, + 0.7688583284F, 0.7692407232F, 0.7696228080F, 0.7700045826F, + 0.7703860467F, 0.7707671999F, 0.7711480420F, 0.7715285728F, + 0.7719087918F, 0.7722886989F, 0.7726682938F, 0.7730475762F, + 0.7734265458F, 0.7738052023F, 0.7741835454F, 0.7745615750F, + 0.7749392906F, 0.7753166921F, 0.7756937791F, 0.7760705514F, + 0.7764470087F, 0.7768231508F, 0.7771989773F, 0.7775744880F, + 0.7779496827F, 0.7783245610F, 0.7786991227F, 0.7790733676F, + 0.7794472953F, 0.7798209056F, 0.7801941982F, 0.7805671729F, + 0.7809398294F, 0.7813121675F, 0.7816841869F, 0.7820558873F, + 0.7824272684F, 0.7827983301F, 0.7831690720F, 0.7835394940F, + 0.7839095957F, 0.7842793768F, 0.7846488373F, 0.7850179767F, + 0.7853867948F, 0.7857552914F, 0.7861234663F, 0.7864913191F, + 0.7868588497F, 0.7872260578F, 0.7875929431F, 0.7879595055F, + 0.7883257445F, 0.7886916601F, 0.7890572520F, 0.7894225198F, + 0.7897874635F, 0.7901520827F, 0.7905163772F, 0.7908803468F, + 0.7912439912F, 0.7916073102F, 0.7919703035F, 0.7923329710F, + 0.7926953124F, 0.7930573274F, 0.7934190158F, 0.7937803774F, + 0.7941414120F, 0.7945021193F, 0.7948624991F, 0.7952225511F, + 0.7955822752F, 0.7959416711F, 0.7963007387F, 0.7966594775F, + 0.7970178875F, 0.7973759685F, 0.7977337201F, 0.7980911422F, + 0.7984482346F, 0.7988049970F, 0.7991614292F, 0.7995175310F, + 0.7998733022F, 0.8002287426F, 0.8005838519F, 0.8009386299F, + 0.8012930765F, 0.8016471914F, 0.8020009744F, 0.8023544253F, + 0.8027075438F, 0.8030603298F, 0.8034127831F, 0.8037649035F, + 0.8041166906F, 0.8044681445F, 0.8048192647F, 0.8051700512F, + 0.8055205038F, 0.8058706222F, 0.8062204062F, 0.8065698556F, + 0.8069189702F, 0.8072677499F, 0.8076161944F, 0.8079643036F, + 0.8083120772F, 0.8086595151F, 0.8090066170F, 0.8093533827F, + 0.8096998122F, 0.8100459051F, 0.8103916613F, 0.8107370806F, + 0.8110821628F, 0.8114269077F, 0.8117713151F, 0.8121153849F, + 0.8124591169F, 0.8128025108F, 0.8131455666F, 0.8134882839F, + 0.8138306627F, 0.8141727027F, 0.8145144038F, 0.8148557658F, + 0.8151967886F, 0.8155374718F, 0.8158778154F, 0.8162178192F, + 0.8165574830F, 0.8168968067F, 0.8172357900F, 0.8175744328F, + 0.8179127349F, 0.8182506962F, 0.8185883164F, 0.8189255955F, + 0.8192625332F, 0.8195991295F, 0.8199353840F, 0.8202712967F, + 0.8206068673F, 0.8209420958F, 0.8212769820F, 0.8216115256F, + 0.8219457266F, 0.8222795848F, 0.8226131000F, 0.8229462721F, + 0.8232791009F, 0.8236115863F, 0.8239437280F, 0.8242755260F, + 0.8246069801F, 0.8249380901F, 0.8252688559F, 0.8255992774F, + 0.8259293544F, 0.8262590867F, 0.8265884741F, 0.8269175167F, + 0.8272462141F, 0.8275745663F, 0.8279025732F, 0.8282302344F, + 0.8285575501F, 0.8288845199F, 0.8292111437F, 0.8295374215F, + 0.8298633530F, 0.8301889382F, 0.8305141768F, 0.8308390688F, + 0.8311636141F, 0.8314878124F, 0.8318116637F, 0.8321351678F, + 0.8324583246F, 0.8327811340F, 0.8331035957F, 0.8334257098F, + 0.8337474761F, 0.8340688944F, 0.8343899647F, 0.8347106867F, + 0.8350310605F, 0.8353510857F, 0.8356707624F, 0.8359900904F, + 0.8363090696F, 0.8366276999F, 0.8369459811F, 0.8372639131F, + 0.8375814958F, 0.8378987292F, 0.8382156130F, 0.8385321472F, + 0.8388483316F, 0.8391641662F, 0.8394796508F, 0.8397947853F, + 0.8401095697F, 0.8404240037F, 0.8407380873F, 0.8410518204F, + 0.8413652029F, 0.8416782347F, 0.8419909156F, 0.8423032456F, + 0.8426152245F, 0.8429268523F, 0.8432381289F, 0.8435490541F, + 0.8438596279F, 0.8441698502F, 0.8444797208F, 0.8447892396F, + 0.8450984067F, 0.8454072218F, 0.8457156849F, 0.8460237959F, + 0.8463315547F, 0.8466389612F, 0.8469460154F, 0.8472527170F, + 0.8475590661F, 0.8478650625F, 0.8481707063F, 0.8484759971F, + 0.8487809351F, 0.8490855201F, 0.8493897521F, 0.8496936308F, + 0.8499971564F, 0.8503003286F, 0.8506031474F, 0.8509056128F, + 0.8512077246F, 0.8515094828F, 0.8518108872F, 0.8521119379F, + 0.8524126348F, 0.8527129777F, 0.8530129666F, 0.8533126015F, + 0.8536118822F, 0.8539108087F, 0.8542093809F, 0.8545075988F, + 0.8548054623F, 0.8551029712F, 0.8554001257F, 0.8556969255F, + 0.8559933707F, 0.8562894611F, 0.8565851968F, 0.8568805775F, + 0.8571756034F, 0.8574702743F, 0.8577645902F, 0.8580585509F, + 0.8583521566F, 0.8586454070F, 0.8589383021F, 0.8592308420F, + 0.8595230265F, 0.8598148556F, 0.8601063292F, 0.8603974473F, + 0.8606882098F, 0.8609786167F, 0.8612686680F, 0.8615583636F, + 0.8618477034F, 0.8621366874F, 0.8624253156F, 0.8627135878F, + 0.8630015042F, 0.8632890646F, 0.8635762690F, 0.8638631173F, + 0.8641496096F, 0.8644357457F, 0.8647215257F, 0.8650069495F, + 0.8652920171F, 0.8655767283F, 0.8658610833F, 0.8661450820F, + 0.8664287243F, 0.8667120102F, 0.8669949397F, 0.8672775127F, + 0.8675597293F, 0.8678415894F, 0.8681230929F, 0.8684042398F, + 0.8686850302F, 0.8689654640F, 0.8692455412F, 0.8695252617F, + 0.8698046255F, 0.8700836327F, 0.8703622831F, 0.8706405768F, + 0.8709185138F, 0.8711960940F, 0.8714733174F, 0.8717501840F, + 0.8720266939F, 0.8723028469F, 0.8725786430F, 0.8728540824F, + 0.8731291648F, 0.8734038905F, 0.8736782592F, 0.8739522711F, + 0.8742259261F, 0.8744992242F, 0.8747721653F, 0.8750447496F, + 0.8753169770F, 0.8755888475F, 0.8758603611F, 0.8761315177F, + 0.8764023175F, 0.8766727603F, 0.8769428462F, 0.8772125752F, + 0.8774819474F, 0.8777509626F, 0.8780196209F, 0.8782879224F, + 0.8785558669F, 0.8788234546F, 0.8790906854F, 0.8793575594F, + 0.8796240765F, 0.8798902368F, 0.8801560403F, 0.8804214870F, + 0.8806865768F, 0.8809513099F, 0.8812156863F, 0.8814797059F, + 0.8817433687F, 0.8820066749F, 0.8822696243F, 0.8825322171F, + 0.8827944532F, 0.8830563327F, 0.8833178556F, 0.8835790219F, + 0.8838398316F, 0.8841002848F, 0.8843603815F, 0.8846201217F, + 0.8848795054F, 0.8851385327F, 0.8853972036F, 0.8856555182F, + 0.8859134764F, 0.8861710783F, 0.8864283239F, 0.8866852133F, + 0.8869417464F, 0.8871979234F, 0.8874537443F, 0.8877092090F, + 0.8879643177F, 0.8882190704F, 0.8884734671F, 0.8887275078F, + 0.8889811927F, 0.8892345216F, 0.8894874948F, 0.8897401122F, + 0.8899923738F, 0.8902442798F, 0.8904958301F, 0.8907470248F, + 0.8909978640F, 0.8912483477F, 0.8914984759F, 0.8917482487F, + 0.8919976662F, 0.8922467284F, 0.8924954353F, 0.8927437871F, + 0.8929917837F, 0.8932394252F, 0.8934867118F, 0.8937336433F, + 0.8939802199F, 0.8942264417F, 0.8944723087F, 0.8947178210F, + 0.8949629785F, 0.8952077815F, 0.8954522299F, 0.8956963239F, + 0.8959400634F, 0.8961834486F, 0.8964264795F, 0.8966691561F, + 0.8969114786F, 0.8971534470F, 0.8973950614F, 0.8976363219F, + 0.8978772284F, 0.8981177812F, 0.8983579802F, 0.8985978256F, + 0.8988373174F, 0.8990764556F, 0.8993152405F, 0.8995536720F, + 0.8997917502F, 0.9000294751F, 0.9002668470F, 0.9005038658F, + 0.9007405317F, 0.9009768446F, 0.9012128048F, 0.9014484123F, + 0.9016836671F, 0.9019185693F, 0.9021531191F, 0.9023873165F, + 0.9026211616F, 0.9028546546F, 0.9030877954F, 0.9033205841F, + 0.9035530210F, 0.9037851059F, 0.9040168392F, 0.9042482207F, + 0.9044792507F, 0.9047099293F, 0.9049402564F, 0.9051702323F, + 0.9053998569F, 0.9056291305F, 0.9058580531F, 0.9060866248F, + 0.9063148457F, 0.9065427159F, 0.9067702355F, 0.9069974046F, + 0.9072242233F, 0.9074506917F, 0.9076768100F, 0.9079025782F, + 0.9081279964F, 0.9083530647F, 0.9085777833F, 0.9088021523F, + 0.9090261717F, 0.9092498417F, 0.9094731623F, 0.9096961338F, + 0.9099187561F, 0.9101410295F, 0.9103629540F, 0.9105845297F, + 0.9108057568F, 0.9110266354F, 0.9112471656F, 0.9114673475F, + 0.9116871812F, 0.9119066668F, 0.9121258046F, 0.9123445945F, + 0.9125630367F, 0.9127811314F, 0.9129988786F, 0.9132162785F, + 0.9134333312F, 0.9136500368F, 0.9138663954F, 0.9140824073F, + 0.9142980724F, 0.9145133910F, 0.9147283632F, 0.9149429890F, + 0.9151572687F, 0.9153712023F, 0.9155847900F, 0.9157980319F, + 0.9160109282F, 0.9162234790F, 0.9164356844F, 0.9166475445F, + 0.9168590595F, 0.9170702296F, 0.9172810548F, 0.9174915354F, + 0.9177016714F, 0.9179114629F, 0.9181209102F, 0.9183300134F, + 0.9185387726F, 0.9187471879F, 0.9189552595F, 0.9191629876F, + 0.9193703723F, 0.9195774136F, 0.9197841119F, 0.9199904672F, + 0.9201964797F, 0.9204021495F, 0.9206074767F, 0.9208124616F, + 0.9210171043F, 0.9212214049F, 0.9214253636F, 0.9216289805F, + 0.9218322558F, 0.9220351896F, 0.9222377821F, 0.9224400335F, + 0.9226419439F, 0.9228435134F, 0.9230447423F, 0.9232456307F, + 0.9234461787F, 0.9236463865F, 0.9238462543F, 0.9240457822F, + 0.9242449704F, 0.9244438190F, 0.9246423282F, 0.9248404983F, + 0.9250383293F, 0.9252358214F, 0.9254329747F, 0.9256297896F, + 0.9258262660F, 0.9260224042F, 0.9262182044F, 0.9264136667F, + 0.9266087913F, 0.9268035783F, 0.9269980280F, 0.9271921405F, + 0.9273859160F, 0.9275793546F, 0.9277724566F, 0.9279652221F, + 0.9281576513F, 0.9283497443F, 0.9285415014F, 0.9287329227F, + 0.9289240084F, 0.9291147586F, 0.9293051737F, 0.9294952536F, + 0.9296849987F, 0.9298744091F, 0.9300634850F, 0.9302522266F, + 0.9304406340F, 0.9306287074F, 0.9308164471F, 0.9310038532F, + 0.9311909259F, 0.9313776654F, 0.9315640719F, 0.9317501455F, + 0.9319358865F, 0.9321212951F, 0.9323063713F, 0.9324911155F, + 0.9326755279F, 0.9328596085F, 0.9330433577F, 0.9332267756F, + 0.9334098623F, 0.9335926182F, 0.9337750434F, 0.9339571380F, + 0.9341389023F, 0.9343203366F, 0.9345014409F, 0.9346822155F, + 0.9348626606F, 0.9350427763F, 0.9352225630F, 0.9354020207F, + 0.9355811498F, 0.9357599503F, 0.9359384226F, 0.9361165667F, + 0.9362943830F, 0.9364718716F, 0.9366490327F, 0.9368258666F, + 0.9370023733F, 0.9371785533F, 0.9373544066F, 0.9375299335F, + 0.9377051341F, 0.9378800087F, 0.9380545576F, 0.9382287809F, + 0.9384026787F, 0.9385762515F, 0.9387494993F, 0.9389224223F, + 0.9390950209F, 0.9392672951F, 0.9394392453F, 0.9396108716F, + 0.9397821743F, 0.9399531536F, 0.9401238096F, 0.9402941427F, + 0.9404641530F, 0.9406338407F, 0.9408032061F, 0.9409722495F, + 0.9411409709F, 0.9413093707F, 0.9414774491F, 0.9416452062F, + 0.9418126424F, 0.9419797579F, 0.9421465528F, 0.9423130274F, + 0.9424791819F, 0.9426450166F, 0.9428105317F, 0.9429757274F, + 0.9431406039F, 0.9433051616F, 0.9434694005F, 0.9436333209F, + 0.9437969232F, 0.9439602074F, 0.9441231739F, 0.9442858229F, + 0.9444481545F, 0.9446101691F, 0.9447718669F, 0.9449332481F, + 0.9450943129F, 0.9452550617F, 0.9454154945F, 0.9455756118F, + 0.9457354136F, 0.9458949003F, 0.9460540721F, 0.9462129292F, + 0.9463714719F, 0.9465297003F, 0.9466876149F, 0.9468452157F, + 0.9470025031F, 0.9471594772F, 0.9473161384F, 0.9474724869F, + 0.9476285229F, 0.9477842466F, 0.9479396584F, 0.9480947585F, + 0.9482495470F, 0.9484040243F, 0.9485581906F, 0.9487120462F, + 0.9488655913F, 0.9490188262F, 0.9491717511F, 0.9493243662F, + 0.9494766718F, 0.9496286683F, 0.9497803557F, 0.9499317345F, + 0.9500828047F, 0.9502335668F, 0.9503840209F, 0.9505341673F, + 0.9506840062F, 0.9508335380F, 0.9509827629F, 0.9511316810F, + 0.9512802928F, 0.9514285984F, 0.9515765982F, 0.9517242923F, + 0.9518716810F, 0.9520187646F, 0.9521655434F, 0.9523120176F, + 0.9524581875F, 0.9526040534F, 0.9527496154F, 0.9528948739F, + 0.9530398292F, 0.9531844814F, 0.9533288310F, 0.9534728780F, + 0.9536166229F, 0.9537600659F, 0.9539032071F, 0.9540460470F, + 0.9541885858F, 0.9543308237F, 0.9544727611F, 0.9546143981F, + 0.9547557351F, 0.9548967723F, 0.9550375100F, 0.9551779485F, + 0.9553180881F, 0.9554579290F, 0.9555974714F, 0.9557367158F, + 0.9558756623F, 0.9560143112F, 0.9561526628F, 0.9562907174F, + 0.9564284752F, 0.9565659366F, 0.9567031017F, 0.9568399710F, + 0.9569765446F, 0.9571128229F, 0.9572488061F, 0.9573844944F, + 0.9575198883F, 0.9576549879F, 0.9577897936F, 0.9579243056F, + 0.9580585242F, 0.9581924497F, 0.9583260824F, 0.9584594226F, + 0.9585924705F, 0.9587252264F, 0.9588576906F, 0.9589898634F, + 0.9591217452F, 0.9592533360F, 0.9593846364F, 0.9595156465F, + 0.9596463666F, 0.9597767971F, 0.9599069382F, 0.9600367901F, + 0.9601663533F, 0.9602956279F, 0.9604246143F, 0.9605533128F, + 0.9606817236F, 0.9608098471F, 0.9609376835F, 0.9610652332F, + 0.9611924963F, 0.9613194733F, 0.9614461644F, 0.9615725699F, + 0.9616986901F, 0.9618245253F, 0.9619500757F, 0.9620753418F, + 0.9622003238F, 0.9623250219F, 0.9624494365F, 0.9625735679F, + 0.9626974163F, 0.9628209821F, 0.9629442656F, 0.9630672671F, + 0.9631899868F, 0.9633124251F, 0.9634345822F, 0.9635564585F, + 0.9636780543F, 0.9637993699F, 0.9639204056F, 0.9640411616F, + 0.9641616383F, 0.9642818359F, 0.9644017549F, 0.9645213955F, + 0.9646407579F, 0.9647598426F, 0.9648786497F, 0.9649971797F, + 0.9651154328F, 0.9652334092F, 0.9653511095F, 0.9654685337F, + 0.9655856823F, 0.9657025556F, 0.9658191538F, 0.9659354773F, + 0.9660515263F, 0.9661673013F, 0.9662828024F, 0.9663980300F, + 0.9665129845F, 0.9666276660F, 0.9667420750F, 0.9668562118F, + 0.9669700766F, 0.9670836698F, 0.9671969917F, 0.9673100425F, + 0.9674228227F, 0.9675353325F, 0.9676475722F, 0.9677595422F, + 0.9678712428F, 0.9679826742F, 0.9680938368F, 0.9682047309F, + 0.9683153569F, 0.9684257150F, 0.9685358056F, 0.9686456289F, + 0.9687551853F, 0.9688644752F, 0.9689734987F, 0.9690822564F, + 0.9691907483F, 0.9692989750F, 0.9694069367F, 0.9695146337F, + 0.9696220663F, 0.9697292349F, 0.9698361398F, 0.9699427813F, + 0.9700491597F, 0.9701552754F, 0.9702611286F, 0.9703667197F, + 0.9704720490F, 0.9705771169F, 0.9706819236F, 0.9707864695F, + 0.9708907549F, 0.9709947802F, 0.9710985456F, 0.9712020514F, + 0.9713052981F, 0.9714082859F, 0.9715110151F, 0.9716134862F, + 0.9717156993F, 0.9718176549F, 0.9719193532F, 0.9720207946F, + 0.9721219794F, 0.9722229080F, 0.9723235806F, 0.9724239976F, + 0.9725241593F, 0.9726240661F, 0.9727237183F, 0.9728231161F, + 0.9729222601F, 0.9730211503F, 0.9731197873F, 0.9732181713F, + 0.9733163027F, 0.9734141817F, 0.9735118088F, 0.9736091842F, + 0.9737063083F, 0.9738031814F, 0.9738998039F, 0.9739961760F, + 0.9740922981F, 0.9741881706F, 0.9742837938F, 0.9743791680F, + 0.9744742935F, 0.9745691707F, 0.9746637999F, 0.9747581814F, + 0.9748523157F, 0.9749462029F, 0.9750398435F, 0.9751332378F, + 0.9752263861F, 0.9753192887F, 0.9754119461F, 0.9755043585F, + 0.9755965262F, 0.9756884496F, 0.9757801291F, 0.9758715650F, + 0.9759627575F, 0.9760537071F, 0.9761444141F, 0.9762348789F, + 0.9763251016F, 0.9764150828F, 0.9765048228F, 0.9765943218F, + 0.9766835802F, 0.9767725984F, 0.9768613767F, 0.9769499154F, + 0.9770382149F, 0.9771262755F, 0.9772140976F, 0.9773016815F, + 0.9773890275F, 0.9774761360F, 0.9775630073F, 0.9776496418F, + 0.9777360398F, 0.9778222016F, 0.9779081277F, 0.9779938182F, + 0.9780792736F, 0.9781644943F, 0.9782494805F, 0.9783342326F, + 0.9784187509F, 0.9785030359F, 0.9785870877F, 0.9786709069F, + 0.9787544936F, 0.9788378484F, 0.9789209714F, 0.9790038631F, + 0.9790865238F, 0.9791689538F, 0.9792511535F, 0.9793331232F, + 0.9794148633F, 0.9794963742F, 0.9795776561F, 0.9796587094F, + 0.9797395345F, 0.9798201316F, 0.9799005013F, 0.9799806437F, + 0.9800605593F, 0.9801402483F, 0.9802197112F, 0.9802989483F, + 0.9803779600F, 0.9804567465F, 0.9805353082F, 0.9806136455F, + 0.9806917587F, 0.9807696482F, 0.9808473143F, 0.9809247574F, + 0.9810019778F, 0.9810789759F, 0.9811557519F, 0.9812323064F, + 0.9813086395F, 0.9813847517F, 0.9814606433F, 0.9815363147F, + 0.9816117662F, 0.9816869981F, 0.9817620108F, 0.9818368047F, + 0.9819113801F, 0.9819857374F, 0.9820598769F, 0.9821337989F, + 0.9822075038F, 0.9822809920F, 0.9823542638F, 0.9824273195F, + 0.9825001596F, 0.9825727843F, 0.9826451940F, 0.9827173891F, + 0.9827893700F, 0.9828611368F, 0.9829326901F, 0.9830040302F, + 0.9830751574F, 0.9831460720F, 0.9832167745F, 0.9832872652F, + 0.9833575444F, 0.9834276124F, 0.9834974697F, 0.9835671166F, + 0.9836365535F, 0.9837057806F, 0.9837747983F, 0.9838436071F, + 0.9839122072F, 0.9839805990F, 0.9840487829F, 0.9841167591F, + 0.9841845282F, 0.9842520903F, 0.9843194459F, 0.9843865953F, + 0.9844535389F, 0.9845202771F, 0.9845868101F, 0.9846531383F, + 0.9847192622F, 0.9847851820F, 0.9848508980F, 0.9849164108F, + 0.9849817205F, 0.9850468276F, 0.9851117324F, 0.9851764352F, + 0.9852409365F, 0.9853052366F, 0.9853693358F, 0.9854332344F, + 0.9854969330F, 0.9855604317F, 0.9856237309F, 0.9856868310F, + 0.9857497325F, 0.9858124355F, 0.9858749404F, 0.9859372477F, + 0.9859993577F, 0.9860612707F, 0.9861229871F, 0.9861845072F, + 0.9862458315F, 0.9863069601F, 0.9863678936F, 0.9864286322F, + 0.9864891764F, 0.9865495264F, 0.9866096826F, 0.9866696454F, + 0.9867294152F, 0.9867889922F, 0.9868483769F, 0.9869075695F, + 0.9869665706F, 0.9870253803F, 0.9870839991F, 0.9871424273F, + 0.9872006653F, 0.9872587135F, 0.9873165721F, 0.9873742415F, + 0.9874317222F, 0.9874890144F, 0.9875461185F, 0.9876030348F, + 0.9876597638F, 0.9877163057F, 0.9877726610F, 0.9878288300F, + 0.9878848130F, 0.9879406104F, 0.9879962225F, 0.9880516497F, + 0.9881068924F, 0.9881619509F, 0.9882168256F, 0.9882715168F, + 0.9883260249F, 0.9883803502F, 0.9884344931F, 0.9884884539F, + 0.9885422331F, 0.9885958309F, 0.9886492477F, 0.9887024838F, + 0.9887555397F, 0.9888084157F, 0.9888611120F, 0.9889136292F, + 0.9889659675F, 0.9890181273F, 0.9890701089F, 0.9891219128F, + 0.9891735392F, 0.9892249885F, 0.9892762610F, 0.9893273572F, + 0.9893782774F, 0.9894290219F, 0.9894795911F, 0.9895299853F, + 0.9895802049F, 0.9896302502F, 0.9896801217F, 0.9897298196F, + 0.9897793443F, 0.9898286961F, 0.9898778755F, 0.9899268828F, + 0.9899757183F, 0.9900243823F, 0.9900728753F, 0.9901211976F, + 0.9901693495F, 0.9902173314F, 0.9902651436F, 0.9903127865F, + 0.9903602605F, 0.9904075659F, 0.9904547031F, 0.9905016723F, + 0.9905484740F, 0.9905951086F, 0.9906415763F, 0.9906878775F, + 0.9907340126F, 0.9907799819F, 0.9908257858F, 0.9908714247F, + 0.9909168988F, 0.9909622086F, 0.9910073543F, 0.9910523364F, + 0.9910971552F, 0.9911418110F, 0.9911863042F, 0.9912306351F, + 0.9912748042F, 0.9913188117F, 0.9913626580F, 0.9914063435F, + 0.9914498684F, 0.9914932333F, 0.9915364383F, 0.9915794839F, + 0.9916223703F, 0.9916650981F, 0.9917076674F, 0.9917500787F, + 0.9917923323F, 0.9918344286F, 0.9918763679F, 0.9919181505F, + 0.9919597769F, 0.9920012473F, 0.9920425621F, 0.9920837217F, + 0.9921247263F, 0.9921655765F, 0.9922062724F, 0.9922468145F, + 0.9922872030F, 0.9923274385F, 0.9923675211F, 0.9924074513F, + 0.9924472294F, 0.9924868557F, 0.9925263306F, 0.9925656544F, + 0.9926048275F, 0.9926438503F, 0.9926827230F, 0.9927214461F, + 0.9927600199F, 0.9927984446F, 0.9928367208F, 0.9928748486F, + 0.9929128285F, 0.9929506608F, 0.9929883459F, 0.9930258841F, + 0.9930632757F, 0.9931005211F, 0.9931376207F, 0.9931745747F, + 0.9932113836F, 0.9932480476F, 0.9932845671F, 0.9933209425F, + 0.9933571742F, 0.9933932623F, 0.9934292074F, 0.9934650097F, + 0.9935006696F, 0.9935361874F, 0.9935715635F, 0.9936067982F, + 0.9936418919F, 0.9936768448F, 0.9937116574F, 0.9937463300F, + 0.9937808629F, 0.9938152565F, 0.9938495111F, 0.9938836271F, + 0.9939176047F, 0.9939514444F, 0.9939851465F, 0.9940187112F, + 0.9940521391F, 0.9940854303F, 0.9941185853F, 0.9941516044F, + 0.9941844879F, 0.9942172361F, 0.9942498495F, 0.9942823283F, + 0.9943146729F, 0.9943468836F, 0.9943789608F, 0.9944109047F, + 0.9944427158F, 0.9944743944F, 0.9945059408F, 0.9945373553F, + 0.9945686384F, 0.9945997902F, 0.9946308112F, 0.9946617017F, + 0.9946924621F, 0.9947230926F, 0.9947535937F, 0.9947839656F, + 0.9948142086F, 0.9948443232F, 0.9948743097F, 0.9949041683F, + 0.9949338995F, 0.9949635035F, 0.9949929807F, 0.9950223315F, + 0.9950515561F, 0.9950806549F, 0.9951096282F, 0.9951384764F, + 0.9951671998F, 0.9951957987F, 0.9952242735F, 0.9952526245F, + 0.9952808520F, 0.9953089564F, 0.9953369380F, 0.9953647971F, + 0.9953925340F, 0.9954201491F, 0.9954476428F, 0.9954750153F, + 0.9955022670F, 0.9955293981F, 0.9955564092F, 0.9955833003F, + 0.9956100720F, 0.9956367245F, 0.9956632582F, 0.9956896733F, + 0.9957159703F, 0.9957421494F, 0.9957682110F, 0.9957941553F, + 0.9958199828F, 0.9958456937F, 0.9958712884F, 0.9958967672F, + 0.9959221305F, 0.9959473784F, 0.9959725115F, 0.9959975300F, + 0.9960224342F, 0.9960472244F, 0.9960719011F, 0.9960964644F, + 0.9961209148F, 0.9961452525F, 0.9961694779F, 0.9961935913F, + 0.9962175930F, 0.9962414834F, 0.9962652627F, 0.9962889313F, + 0.9963124895F, 0.9963359377F, 0.9963592761F, 0.9963825051F, + 0.9964056250F, 0.9964286361F, 0.9964515387F, 0.9964743332F, + 0.9964970198F, 0.9965195990F, 0.9965420709F, 0.9965644360F, + 0.9965866946F, 0.9966088469F, 0.9966308932F, 0.9966528340F, + 0.9966746695F, 0.9966964001F, 0.9967180260F, 0.9967395475F, + 0.9967609651F, 0.9967822789F, 0.9968034894F, 0.9968245968F, + 0.9968456014F, 0.9968665036F, 0.9968873037F, 0.9969080019F, + 0.9969285987F, 0.9969490942F, 0.9969694889F, 0.9969897830F, + 0.9970099769F, 0.9970300708F, 0.9970500651F, 0.9970699601F, + 0.9970897561F, 0.9971094533F, 0.9971290522F, 0.9971485531F, + 0.9971679561F, 0.9971872617F, 0.9972064702F, 0.9972255818F, + 0.9972445968F, 0.9972635157F, 0.9972823386F, 0.9973010659F, + 0.9973196980F, 0.9973382350F, 0.9973566773F, 0.9973750253F, + 0.9973932791F, 0.9974114392F, 0.9974295059F, 0.9974474793F, + 0.9974653599F, 0.9974831480F, 0.9975008438F, 0.9975184476F, + 0.9975359598F, 0.9975533806F, 0.9975707104F, 0.9975879495F, + 0.9976050981F, 0.9976221566F, 0.9976391252F, 0.9976560043F, + 0.9976727941F, 0.9976894950F, 0.9977061073F, 0.9977226312F, + 0.9977390671F, 0.9977554152F, 0.9977716759F, 0.9977878495F, + 0.9978039361F, 0.9978199363F, 0.9978358501F, 0.9978516780F, + 0.9978674202F, 0.9978830771F, 0.9978986488F, 0.9979141358F, + 0.9979295383F, 0.9979448566F, 0.9979600909F, 0.9979752417F, + 0.9979903091F, 0.9980052936F, 0.9980201952F, 0.9980350145F, + 0.9980497515F, 0.9980644067F, 0.9980789804F, 0.9980934727F, + 0.9981078841F, 0.9981222147F, 0.9981364649F, 0.9981506350F, + 0.9981647253F, 0.9981787360F, 0.9981926674F, 0.9982065199F, + 0.9982202936F, 0.9982339890F, 0.9982476062F, 0.9982611456F, + 0.9982746074F, 0.9982879920F, 0.9983012996F, 0.9983145304F, + 0.9983276849F, 0.9983407632F, 0.9983537657F, 0.9983666926F, + 0.9983795442F, 0.9983923208F, 0.9984050226F, 0.9984176501F, + 0.9984302033F, 0.9984426827F, 0.9984550884F, 0.9984674208F, + 0.9984796802F, 0.9984918667F, 0.9985039808F, 0.9985160227F, + 0.9985279926F, 0.9985398909F, 0.9985517177F, 0.9985634734F, + 0.9985751583F, 0.9985867727F, 0.9985983167F, 0.9986097907F, + 0.9986211949F, 0.9986325297F, 0.9986437953F, 0.9986549919F, + 0.9986661199F, 0.9986771795F, 0.9986881710F, 0.9986990946F, + 0.9987099507F, 0.9987207394F, 0.9987314611F, 0.9987421161F, + 0.9987527045F, 0.9987632267F, 0.9987736829F, 0.9987840734F, + 0.9987943985F, 0.9988046584F, 0.9988148534F, 0.9988249838F, + 0.9988350498F, 0.9988450516F, 0.9988549897F, 0.9988648641F, + 0.9988746753F, 0.9988844233F, 0.9988941086F, 0.9989037313F, + 0.9989132918F, 0.9989227902F, 0.9989322269F, 0.9989416021F, + 0.9989509160F, 0.9989601690F, 0.9989693613F, 0.9989784931F, + 0.9989875647F, 0.9989965763F, 0.9990055283F, 0.9990144208F, + 0.9990232541F, 0.9990320286F, 0.9990407443F, 0.9990494016F, + 0.9990580008F, 0.9990665421F, 0.9990750257F, 0.9990834519F, + 0.9990918209F, 0.9991001331F, 0.9991083886F, 0.9991165877F, + 0.9991247307F, 0.9991328177F, 0.9991408491F, 0.9991488251F, + 0.9991567460F, 0.9991646119F, 0.9991724232F, 0.9991801801F, + 0.9991878828F, 0.9991955316F, 0.9992031267F, 0.9992106684F, + 0.9992181569F, 0.9992255925F, 0.9992329753F, 0.9992403057F, + 0.9992475839F, 0.9992548101F, 0.9992619846F, 0.9992691076F, + 0.9992761793F, 0.9992832001F, 0.9992901701F, 0.9992970895F, + 0.9993039587F, 0.9993107777F, 0.9993175470F, 0.9993242667F, + 0.9993309371F, 0.9993375583F, 0.9993441307F, 0.9993506545F, + 0.9993571298F, 0.9993635570F, 0.9993699362F, 0.9993762678F, + 0.9993825519F, 0.9993887887F, 0.9993949785F, 0.9994011216F, + 0.9994072181F, 0.9994132683F, 0.9994192725F, 0.9994252307F, + 0.9994311434F, 0.9994370107F, 0.9994428327F, 0.9994486099F, + 0.9994543423F, 0.9994600303F, 0.9994656739F, 0.9994712736F, + 0.9994768294F, 0.9994823417F, 0.9994878105F, 0.9994932363F, + 0.9994986191F, 0.9995039592F, 0.9995092568F, 0.9995145122F, + 0.9995197256F, 0.9995248971F, 0.9995300270F, 0.9995351156F, + 0.9995401630F, 0.9995451695F, 0.9995501352F, 0.9995550604F, + 0.9995599454F, 0.9995647903F, 0.9995695953F, 0.9995743607F, + 0.9995790866F, 0.9995837734F, 0.9995884211F, 0.9995930300F, + 0.9995976004F, 0.9996021324F, 0.9996066263F, 0.9996110822F, + 0.9996155004F, 0.9996198810F, 0.9996242244F, 0.9996285306F, + 0.9996327999F, 0.9996370326F, 0.9996412287F, 0.9996453886F, + 0.9996495125F, 0.9996536004F, 0.9996576527F, 0.9996616696F, + 0.9996656512F, 0.9996695977F, 0.9996735094F, 0.9996773865F, + 0.9996812291F, 0.9996850374F, 0.9996888118F, 0.9996925523F, + 0.9996962591F, 0.9996999325F, 0.9997035727F, 0.9997071798F, + 0.9997107541F, 0.9997142957F, 0.9997178049F, 0.9997212818F, + 0.9997247266F, 0.9997281396F, 0.9997315209F, 0.9997348708F, + 0.9997381893F, 0.9997414767F, 0.9997447333F, 0.9997479591F, + 0.9997511544F, 0.9997543194F, 0.9997574542F, 0.9997605591F, + 0.9997636342F, 0.9997666797F, 0.9997696958F, 0.9997726828F, + 0.9997756407F, 0.9997785698F, 0.9997814703F, 0.9997843423F, + 0.9997871860F, 0.9997900016F, 0.9997927894F, 0.9997955494F, + 0.9997982818F, 0.9998009869F, 0.9998036648F, 0.9998063157F, + 0.9998089398F, 0.9998115373F, 0.9998141082F, 0.9998166529F, + 0.9998191715F, 0.9998216642F, 0.9998241311F, 0.9998265724F, + 0.9998289884F, 0.9998313790F, 0.9998337447F, 0.9998360854F, + 0.9998384015F, 0.9998406930F, 0.9998429602F, 0.9998452031F, + 0.9998474221F, 0.9998496171F, 0.9998517885F, 0.9998539364F, + 0.9998560610F, 0.9998581624F, 0.9998602407F, 0.9998622962F, + 0.9998643291F, 0.9998663394F, 0.9998683274F, 0.9998702932F, + 0.9998722370F, 0.9998741589F, 0.9998760591F, 0.9998779378F, + 0.9998797952F, 0.9998816313F, 0.9998834464F, 0.9998852406F, + 0.9998870141F, 0.9998887670F, 0.9998904995F, 0.9998922117F, + 0.9998939039F, 0.9998955761F, 0.9998972285F, 0.9998988613F, + 0.9999004746F, 0.9999020686F, 0.9999036434F, 0.9999051992F, + 0.9999067362F, 0.9999082544F, 0.9999097541F, 0.9999112354F, + 0.9999126984F, 0.9999141433F, 0.9999155703F, 0.9999169794F, + 0.9999183709F, 0.9999197449F, 0.9999211014F, 0.9999224408F, + 0.9999237631F, 0.9999250684F, 0.9999263570F, 0.9999276289F, + 0.9999288843F, 0.9999301233F, 0.9999313461F, 0.9999325529F, + 0.9999337437F, 0.9999349187F, 0.9999360780F, 0.9999372218F, + 0.9999383503F, 0.9999394635F, 0.9999405616F, 0.9999416447F, + 0.9999427129F, 0.9999437665F, 0.9999448055F, 0.9999458301F, + 0.9999468404F, 0.9999478365F, 0.9999488185F, 0.9999497867F, + 0.9999507411F, 0.9999516819F, 0.9999526091F, 0.9999535230F, + 0.9999544236F, 0.9999553111F, 0.9999561856F, 0.9999570472F, + 0.9999578960F, 0.9999587323F, 0.9999595560F, 0.9999603674F, + 0.9999611666F, 0.9999619536F, 0.9999627286F, 0.9999634917F, + 0.9999642431F, 0.9999649828F, 0.9999657110F, 0.9999664278F, + 0.9999671334F, 0.9999678278F, 0.9999685111F, 0.9999691835F, + 0.9999698451F, 0.9999704960F, 0.9999711364F, 0.9999717662F, + 0.9999723858F, 0.9999729950F, 0.9999735942F, 0.9999741834F, + 0.9999747626F, 0.9999753321F, 0.9999758919F, 0.9999764421F, + 0.9999769828F, 0.9999775143F, 0.9999780364F, 0.9999785495F, + 0.9999790535F, 0.9999795485F, 0.9999800348F, 0.9999805124F, + 0.9999809813F, 0.9999814417F, 0.9999818938F, 0.9999823375F, + 0.9999827731F, 0.9999832005F, 0.9999836200F, 0.9999840316F, + 0.9999844353F, 0.9999848314F, 0.9999852199F, 0.9999856008F, + 0.9999859744F, 0.9999863407F, 0.9999866997F, 0.9999870516F, + 0.9999873965F, 0.9999877345F, 0.9999880656F, 0.9999883900F, + 0.9999887078F, 0.9999890190F, 0.9999893237F, 0.9999896220F, + 0.9999899140F, 0.9999901999F, 0.9999904796F, 0.9999907533F, + 0.9999910211F, 0.9999912830F, 0.9999915391F, 0.9999917896F, + 0.9999920345F, 0.9999922738F, 0.9999925077F, 0.9999927363F, + 0.9999929596F, 0.9999931777F, 0.9999933907F, 0.9999935987F, + 0.9999938018F, 0.9999940000F, 0.9999941934F, 0.9999943820F, + 0.9999945661F, 0.9999947456F, 0.9999949206F, 0.9999950912F, + 0.9999952575F, 0.9999954195F, 0.9999955773F, 0.9999957311F, + 0.9999958807F, 0.9999960265F, 0.9999961683F, 0.9999963063F, + 0.9999964405F, 0.9999965710F, 0.9999966979F, 0.9999968213F, + 0.9999969412F, 0.9999970576F, 0.9999971707F, 0.9999972805F, + 0.9999973871F, 0.9999974905F, 0.9999975909F, 0.9999976881F, + 0.9999977824F, 0.9999978738F, 0.9999979624F, 0.9999980481F, + 0.9999981311F, 0.9999982115F, 0.9999982892F, 0.9999983644F, + 0.9999984370F, 0.9999985072F, 0.9999985750F, 0.9999986405F, + 0.9999987037F, 0.9999987647F, 0.9999988235F, 0.9999988802F, + 0.9999989348F, 0.9999989873F, 0.9999990379F, 0.9999990866F, + 0.9999991334F, 0.9999991784F, 0.9999992217F, 0.9999992632F, + 0.9999993030F, 0.9999993411F, 0.9999993777F, 0.9999994128F, + 0.9999994463F, 0.9999994784F, 0.9999995091F, 0.9999995384F, + 0.9999995663F, 0.9999995930F, 0.9999996184F, 0.9999996426F, + 0.9999996657F, 0.9999996876F, 0.9999997084F, 0.9999997282F, + 0.9999997469F, 0.9999997647F, 0.9999997815F, 0.9999997973F, + 0.9999998123F, 0.9999998265F, 0.9999998398F, 0.9999998524F, + 0.9999998642F, 0.9999998753F, 0.9999998857F, 0.9999998954F, + 0.9999999045F, 0.9999999130F, 0.9999999209F, 0.9999999282F, + 0.9999999351F, 0.9999999414F, 0.9999999472F, 0.9999999526F, + 0.9999999576F, 0.9999999622F, 0.9999999664F, 0.9999999702F, + 0.9999999737F, 0.9999999769F, 0.9999999798F, 0.9999999824F, + 0.9999999847F, 0.9999999868F, 0.9999999887F, 0.9999999904F, + 0.9999999919F, 0.9999999932F, 0.9999999943F, 0.9999999953F, + 0.9999999961F, 0.9999999969F, 0.9999999975F, 0.9999999980F, + 0.9999999985F, 0.9999999988F, 0.9999999991F, 0.9999999993F, + 0.9999999995F, 0.9999999997F, 0.9999999998F, 0.9999999999F, + 0.9999999999F, 1.0000000000F, 1.0000000000F, 1.0000000000F, + 1.0000000000F, 1.0000000000F, 1.0000000000F, 1.0000000000F, +}; + +static float *vwin[8] = { + vwin64, + vwin128, + vwin256, + vwin512, + vwin1024, + vwin2048, + vwin4096, + vwin8192, +}; + +float *_vorbis_window_get(int n){ + return vwin[n]; +} + +void _vorbis_apply_window(float *d,int *winno,long *blocksizes, + int lW,int W,int nW){ + lW=(W?lW:0); + nW=(W?nW:0); + + { + float *windowLW=vwin[winno[lW]]; + float *windowNW=vwin[winno[nW]]; + + long n=blocksizes[W]; + long ln=blocksizes[lW]; + long rn=blocksizes[nW]; + + long leftbegin=n/4-ln/4; + long leftend=leftbegin+ln/2; + + long rightbegin=n/2+n/4-rn/4; + long rightend=rightbegin+rn/2; + + int i,p; + + for(i=0;i wrote the original ansi2knr and +continues to maintain the current version; most of the code in the current +version is his work. ansi2knr also includes contributions by Francois +Pinard and Jim Avera . diff --git a/Engine/lib/ljpeg/extras/ansi2knr.c b/Engine/lib/ljpeg/extras/ansi2knr.c new file mode 100644 index 000000000..4e05fc2d3 --- /dev/null +++ b/Engine/lib/ljpeg/extras/ansi2knr.c @@ -0,0 +1,693 @@ +/* ansi2knr.c */ +/* Convert ANSI C function definitions to K&R ("traditional C") syntax */ + +/* +ansi2knr is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY. No author or distributor accepts responsibility to anyone for the +consequences of using it or for whether it serves any particular purpose or +works at all, unless he says so in writing. Refer to the GNU General Public +License (the "GPL") for full details. + +Everyone is granted permission to copy, modify and redistribute ansi2knr, +but only under the conditions described in the GPL. A copy of this license +is supposed to have been given to you along with ansi2knr so you can know +your rights and responsibilities. It should be in a file named COPYLEFT. +[In the IJG distribution, the GPL appears below, not in a separate file.] +Among other things, the copyright notice and this notice must be preserved +on all copies. + +We explicitly state here what we believe is already implied by the GPL: if +the ansi2knr program is distributed as a separate set of sources and a +separate executable file which are aggregated on a storage medium together +with another program, this in itself does not bring the other program under +the GPL, nor does the mere fact that such a program or the procedures for +constructing it invoke the ansi2knr executable bring any other part of the +program under the GPL. +*/ + +/* +---------- Here is the GNU GPL file COPYLEFT, referred to above ---------- +----- These terms do NOT apply to the JPEG software itself; see README ------ + + GHOSTSCRIPT GENERAL PUBLIC LICENSE + (Clarified 11 Feb 1988) + + Copyright (C) 1988 Richard M. Stallman + Everyone is permitted to copy and distribute verbatim copies of this + license, but changing it is not allowed. You can also use this wording + to make the terms for other programs. + + The license agreements of most software companies keep you at the +mercy of those companies. By contrast, our general public license is +intended to give everyone the right to share Ghostscript. To make sure +that you get the rights we want you to have, we need to make +restrictions that forbid anyone to deny you these rights or to ask you +to surrender the rights. Hence this license agreement. + + Specifically, we want to make sure that you have the right to give +away copies of Ghostscript, that you receive source code or else can get +it if you want it, that you can change Ghostscript or use pieces of it +in new free programs, and that you know you can do these things. + + To make sure that everyone has such rights, we have to forbid you to +deprive anyone else of these rights. For example, if you distribute +copies of Ghostscript, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must tell them their rights. + + Also, for our own protection, we must make certain that everyone finds +out that there is no warranty for Ghostscript. If Ghostscript is +modified by someone else and passed on, we want its recipients to know +that what they have is not what we distributed, so that any problems +introduced by others will not reflect on our reputation. + + Therefore we (Richard M. Stallman and the Free Software Foundation, +Inc.) make the following terms which say what you must do to be allowed +to distribute or change Ghostscript. + + + COPYING POLICIES + + 1. You may copy and distribute verbatim copies of Ghostscript source +code as you receive it, in any medium, provided that you conspicuously +and appropriately publish on each copy a valid copyright and license +notice "Copyright (C) 1989 Aladdin Enterprises. All rights reserved. +Distributed by Free Software Foundation, Inc." (or with whatever year is +appropriate); keep intact the notices on all files that refer to this +License Agreement and to the absence of any warranty; and give any other +recipients of the Ghostscript program a copy of this License Agreement +along with the program. You may charge a distribution fee for the +physical act of transferring a copy. + + 2. You may modify your copy or copies of Ghostscript or any portion of +it, and copy and distribute such modifications under the terms of +Paragraph 1 above, provided that you also do the following: + + a) cause the modified files to carry prominent notices stating + that you changed the files and the date of any change; and + + b) cause the whole of any work that you distribute or publish, + that in whole or in part contains or is a derivative of Ghostscript + or any part thereof, to be licensed at no charge to all third + parties on terms identical to those contained in this License + Agreement (except that you may choose to grant more extensive + warranty protection to some or all third parties, at your option). + + c) You may charge a distribution fee for the physical act of + transferring a copy, and you may at your option offer warranty + protection in exchange for a fee. + +Mere aggregation of another unrelated program with this program (or its +derivative) on a volume of a storage or distribution medium does not bring +the other program under the scope of these terms. + + 3. You may copy and distribute Ghostscript (or a portion or derivative +of it, under Paragraph 2) in object code or executable form under the +terms of Paragraphs 1 and 2 above provided that you also do one of the +following: + + a) accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of + Paragraphs 1 and 2 above; or, + + b) accompany it with a written offer, valid for at least three + years, to give any third party free (except for a nominal + shipping charge) a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of + Paragraphs 1 and 2 above; or, + + c) accompany it with the information you received as to where the + corresponding source code may be obtained. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form alone.) + +For an executable file, complete source code means all the source code for +all modules it contains; but, as a special exception, it need not include +source code for modules which are standard libraries that accompany the +operating system on which the executable file runs. + + 4. You may not copy, sublicense, distribute or transfer Ghostscript +except as expressly provided under this License Agreement. Any attempt +otherwise to copy, sublicense, distribute or transfer Ghostscript is +void and your rights to use the program under this License agreement +shall be automatically terminated. However, parties who have received +computer software programs from you with this License Agreement will not +have their licenses terminated so long as such parties remain in full +compliance. + + 5. If you wish to incorporate parts of Ghostscript into other free +programs whose distribution conditions are different, write to the Free +Software Foundation at 675 Mass Ave, Cambridge, MA 02139. We have not +yet worked out a simple rule that can be stated here, but we will often +permit this. We will be guided by the two goals of preserving the free +status of all derivatives of our free software and of promoting the +sharing and reuse of software. + +Your comments and suggestions about our licensing policies and our +software are welcome! Please contact the Free Software Foundation, +Inc., 675 Mass Ave, Cambridge, MA 02139, or call (617) 876-3296. + + NO WARRANTY + + BECAUSE GHOSTSCRIPT IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY +NO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW. EXCEPT +WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC, RICHARD +M. STALLMAN, ALADDIN ENTERPRISES, L. PETER DEUTSCH, AND/OR OTHER PARTIES +PROVIDE GHOSTSCRIPT "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER +EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE +ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF GHOSTSCRIPT IS WITH +YOU. SHOULD GHOSTSCRIPT PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL +NECESSARY SERVICING, REPAIR OR CORRECTION. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M. +STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., L. PETER DEUTSCH, ALADDIN +ENTERPRISES, AND/OR ANY OTHER PARTY WHO MAY MODIFY AND REDISTRIBUTE +GHOSTSCRIPT AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING +ANY LOST PROFITS, LOST MONIES, OR OTHER SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE +(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED +INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR A FAILURE OF THE +PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) GHOSTSCRIPT, EVEN IF YOU +HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, OR FOR ANY CLAIM +BY ANY OTHER PARTY. + +-------------------- End of file COPYLEFT ------------------------------ +*/ + +/* + * Usage: + ansi2knr input_file [output_file] + * If no output_file is supplied, output goes to stdout. + * There are no error messages. + * + * ansi2knr recognizes function definitions by seeing a non-keyword + * identifier at the left margin, followed by a left parenthesis, + * with a right parenthesis as the last character on the line, + * and with a left brace as the first token on the following line + * (ignoring possible intervening comments). + * It will recognize a multi-line header provided that no intervening + * line ends with a left or right brace or a semicolon. + * These algorithms ignore whitespace and comments, except that + * the function name must be the first thing on the line. + * The following constructs will confuse it: + * - Any other construct that starts at the left margin and + * follows the above syntax (such as a macro or function call). + * - Some macros that tinker with the syntax of the function header. + */ + +/* + * The original and principal author of ansi2knr is L. Peter Deutsch + * . Other authors are noted in the change history + * that follows (in reverse chronological order): + lpd 96-01-21 added code to cope with not HAVE_CONFIG_H and with + compilers that don't understand void, as suggested by + Tom Lane + lpd 96-01-15 changed to require that the first non-comment token + on the line following a function header be a left brace, + to reduce sensitivity to macros, as suggested by Tom Lane + + lpd 95-06-22 removed #ifndefs whose sole purpose was to define + undefined preprocessor symbols as 0; changed all #ifdefs + for configuration symbols to #ifs + lpd 95-04-05 changed copyright notice to make it clear that + including ansi2knr in a program does not bring the entire + program under the GPL + lpd 94-12-18 added conditionals for systems where ctype macros + don't handle 8-bit characters properly, suggested by + Francois Pinard ; + removed --varargs switch (this is now the default) + lpd 94-10-10 removed CONFIG_BROKETS conditional + lpd 94-07-16 added some conditionals to help GNU `configure', + suggested by Francois Pinard ; + properly erase prototype args in function parameters, + contributed by Jim Avera ; + correct error in writeblanks (it shouldn't erase EOLs) + lpd 89-xx-xx original version + */ + +/* Most of the conditionals here are to make ansi2knr work with */ +/* or without the GNU configure machinery. */ + +#if HAVE_CONFIG_H +# include +#endif + +#include +#include + +#if HAVE_CONFIG_H + +/* + For properly autoconfiguring ansi2knr, use AC_CONFIG_HEADER(config.h). + This will define HAVE_CONFIG_H and so, activate the following lines. + */ + +# if STDC_HEADERS || HAVE_STRING_H +# include +# else +# include +# endif + +#else /* not HAVE_CONFIG_H */ + +/* Otherwise do it the hard way */ + +# ifdef BSD +# include +# else +# ifdef VMS + extern int strlen(), strncmp(); +# else +# include +# endif +# endif + +#endif /* not HAVE_CONFIG_H */ + +#if STDC_HEADERS +# include +#else +/* + malloc and free should be declared in stdlib.h, + but if you've got a K&R compiler, they probably aren't. + */ +# ifdef MSDOS +# include +# else +# ifdef VMS + extern char *malloc(); + extern void free(); +# else + extern char *malloc(); + extern int free(); +# endif +# endif + +#endif + +/* + * The ctype macros don't always handle 8-bit characters correctly. + * Compensate for this here. + */ +#ifdef isascii +# undef HAVE_ISASCII /* just in case */ +# define HAVE_ISASCII 1 +#else +#endif +#if STDC_HEADERS || !HAVE_ISASCII +# define is_ascii(c) 1 +#else +# define is_ascii(c) isascii(c) +#endif + +#define is_space(c) (is_ascii(c) && isspace(c)) +#define is_alpha(c) (is_ascii(c) && isalpha(c)) +#define is_alnum(c) (is_ascii(c) && isalnum(c)) + +/* Scanning macros */ +#define isidchar(ch) (is_alnum(ch) || (ch) == '_') +#define isidfirstchar(ch) (is_alpha(ch) || (ch) == '_') + +/* Forward references */ +char *skipspace(); +int writeblanks(); +int test1(); +int convert1(); + +/* The main program */ +int +main(argc, argv) + int argc; + char *argv[]; +{ FILE *in, *out; +#define bufsize 5000 /* arbitrary size */ + char *buf; + char *line; + char *more; + /* + * In previous versions, ansi2knr recognized a --varargs switch. + * If this switch was supplied, ansi2knr would attempt to convert + * a ... argument to va_alist and va_dcl; if this switch was not + * supplied, ansi2knr would simply drop any such arguments. + * Now, ansi2knr always does this conversion, and we only + * check for this switch for backward compatibility. + */ + int convert_varargs = 1; + + if ( argc > 1 && argv[1][0] == '-' ) + { if ( !strcmp(argv[1], "--varargs") ) + { convert_varargs = 1; + argc--; + argv++; + } + else + { fprintf(stderr, "Unrecognized switch: %s\n", argv[1]); + exit(1); + } + } + switch ( argc ) + { + default: + printf("Usage: ansi2knr input_file [output_file]\n"); + exit(0); + case 2: + out = stdout; + break; + case 3: + out = fopen(argv[2], "w"); + if ( out == NULL ) + { fprintf(stderr, "Cannot open output file %s\n", argv[2]); + exit(1); + } + } + in = fopen(argv[1], "r"); + if ( in == NULL ) + { fprintf(stderr, "Cannot open input file %s\n", argv[1]); + exit(1); + } + fprintf(out, "#line 1 \"%s\"\n", argv[1]); + buf = malloc(bufsize); + line = buf; + while ( fgets(line, (unsigned)(buf + bufsize - line), in) != NULL ) + { +test: line += strlen(line); + switch ( test1(buf) ) + { + case 2: /* a function header */ + convert1(buf, out, 1, convert_varargs); + break; + case 1: /* a function */ + /* Check for a { at the start of the next line. */ + more = ++line; +f: if ( line >= buf + (bufsize - 1) ) /* overflow check */ + goto wl; + if ( fgets(line, (unsigned)(buf + bufsize - line), in) == NULL ) + goto wl; + switch ( *skipspace(more, 1) ) + { + case '{': + /* Definitely a function header. */ + convert1(buf, out, 0, convert_varargs); + fputs(more, out); + break; + case 0: + /* The next line was blank or a comment: */ + /* keep scanning for a non-comment. */ + line += strlen(line); + goto f; + default: + /* buf isn't a function header, but */ + /* more might be. */ + fputs(buf, out); + strcpy(buf, more); + line = buf; + goto test; + } + break; + case -1: /* maybe the start of a function */ + if ( line != buf + (bufsize - 1) ) /* overflow check */ + continue; + /* falls through */ + default: /* not a function */ +wl: fputs(buf, out); + break; + } + line = buf; + } + if ( line != buf ) + fputs(buf, out); + free(buf); + fclose(out); + fclose(in); + return 0; +} + +/* Skip over space and comments, in either direction. */ +char * +skipspace(p, dir) + register char *p; + register int dir; /* 1 for forward, -1 for backward */ +{ for ( ; ; ) + { while ( is_space(*p) ) + p += dir; + if ( !(*p == '/' && p[dir] == '*') ) + break; + p += dir; p += dir; + while ( !(*p == '*' && p[dir] == '/') ) + { if ( *p == 0 ) + return p; /* multi-line comment?? */ + p += dir; + } + p += dir; p += dir; + } + return p; +} + +/* + * Write blanks over part of a string. + * Don't overwrite end-of-line characters. + */ +int +writeblanks(start, end) + char *start; + char *end; +{ char *p; + for ( p = start; p < end; p++ ) + if ( *p != '\r' && *p != '\n' ) + *p = ' '; + return 0; +} + +/* + * Test whether the string in buf is a function definition. + * The string may contain and/or end with a newline. + * Return as follows: + * 0 - definitely not a function definition; + * 1 - definitely a function definition; + * 2 - definitely a function prototype (NOT USED); + * -1 - may be the beginning of a function definition, + * append another line and look again. + * The reason we don't attempt to convert function prototypes is that + * Ghostscript's declaration-generating macros look too much like + * prototypes, and confuse the algorithms. + */ +int +test1(buf) + char *buf; +{ register char *p = buf; + char *bend; + char *endfn; + int contin; + + if ( !isidfirstchar(*p) ) + return 0; /* no name at left margin */ + bend = skipspace(buf + strlen(buf) - 1, -1); + switch ( *bend ) + { + case ';': contin = 0 /*2*/; break; + case ')': contin = 1; break; + case '{': return 0; /* not a function */ + case '}': return 0; /* not a function */ + default: contin = -1; + } + while ( isidchar(*p) ) + p++; + endfn = p; + p = skipspace(p, 1); + if ( *p++ != '(' ) + return 0; /* not a function */ + p = skipspace(p, 1); + if ( *p == ')' ) + return 0; /* no parameters */ + /* Check that the apparent function name isn't a keyword. */ + /* We only need to check for keywords that could be followed */ + /* by a left parenthesis (which, unfortunately, is most of them). */ + { static char *words[] = + { "asm", "auto", "case", "char", "const", "double", + "extern", "float", "for", "if", "int", "long", + "register", "return", "short", "signed", "sizeof", + "static", "switch", "typedef", "unsigned", + "void", "volatile", "while", 0 + }; + char **key = words; + char *kp; + int len = endfn - buf; + + while ( (kp = *key) != 0 ) + { if ( strlen(kp) == len && !strncmp(kp, buf, len) ) + return 0; /* name is a keyword */ + key++; + } + } + return contin; +} + +/* Convert a recognized function definition or header to K&R syntax. */ +int +convert1(buf, out, header, convert_varargs) + char *buf; + FILE *out; + int header; /* Boolean */ + int convert_varargs; /* Boolean */ +{ char *endfn; + register char *p; + char **breaks; + unsigned num_breaks = 2; /* for testing */ + char **btop; + char **bp; + char **ap; + char *vararg = 0; + + /* Pre-ANSI implementations don't agree on whether strchr */ + /* is called strchr or index, so we open-code it here. */ + for ( endfn = buf; *(endfn++) != '('; ) + ; +top: p = endfn; + breaks = (char **)malloc(sizeof(char *) * num_breaks * 2); + if ( breaks == 0 ) + { /* Couldn't allocate break table, give up */ + fprintf(stderr, "Unable to allocate break table!\n"); + fputs(buf, out); + return -1; + } + btop = breaks + num_breaks * 2 - 2; + bp = breaks; + /* Parse the argument list */ + do + { int level = 0; + char *lp = NULL; + char *rp; + char *end = NULL; + + if ( bp >= btop ) + { /* Filled up break table. */ + /* Allocate a bigger one and start over. */ + free((char *)breaks); + num_breaks <<= 1; + goto top; + } + *bp++ = p; + /* Find the end of the argument */ + for ( ; end == NULL; p++ ) + { switch(*p) + { + case ',': + if ( !level ) end = p; + break; + case '(': + if ( !level ) lp = p; + level++; + break; + case ')': + if ( --level < 0 ) end = p; + else rp = p; + break; + case '/': + p = skipspace(p, 1) - 1; + break; + default: + ; + } + } + /* Erase any embedded prototype parameters. */ + if ( lp ) + writeblanks(lp + 1, rp); + p--; /* back up over terminator */ + /* Find the name being declared. */ + /* This is complicated because of procedure and */ + /* array modifiers. */ + for ( ; ; ) + { p = skipspace(p - 1, -1); + switch ( *p ) + { + case ']': /* skip array dimension(s) */ + case ')': /* skip procedure args OR name */ + { int level = 1; + while ( level ) + switch ( *--p ) + { + case ']': case ')': level++; break; + case '[': case '(': level--; break; + case '/': p = skipspace(p, -1) + 1; break; + default: ; + } + } + if ( *p == '(' && *skipspace(p + 1, 1) == '*' ) + { /* We found the name being declared */ + while ( !isidfirstchar(*p) ) + p = skipspace(p, 1) + 1; + goto found; + } + break; + default: + goto found; + } + } +found: if ( *p == '.' && p[-1] == '.' && p[-2] == '.' ) + { if ( convert_varargs ) + { *bp++ = "va_alist"; + vararg = p-2; + } + else + { p++; + if ( bp == breaks + 1 ) /* sole argument */ + writeblanks(breaks[0], p); + else + writeblanks(bp[-1] - 1, p); + bp--; + } + } + else + { while ( isidchar(*p) ) p--; + *bp++ = p+1; + } + p = end; + } + while ( *p++ == ',' ); + *bp = p; + /* Make a special check for 'void' arglist */ + if ( bp == breaks+2 ) + { p = skipspace(breaks[0], 1); + if ( !strncmp(p, "void", 4) ) + { p = skipspace(p+4, 1); + if ( p == breaks[2] - 1 ) + { bp = breaks; /* yup, pretend arglist is empty */ + writeblanks(breaks[0], p + 1); + } + } + } + /* Put out the function name and left parenthesis. */ + p = buf; + while ( p != endfn ) putc(*p, out), p++; + /* Put out the declaration. */ + if ( header ) + { fputs(");", out); + for ( p = breaks[0]; *p; p++ ) + if ( *p == '\r' || *p == '\n' ) + putc(*p, out); + } + else + { for ( ap = breaks+1; ap < bp; ap += 2 ) + { p = *ap; + while ( isidchar(*p) ) + putc(*p, out), p++; + if ( ap < bp - 1 ) + fputs(", ", out); + } + fputs(") ", out); + /* Put out the argument declarations */ + for ( ap = breaks+2; ap <= bp; ap += 2 ) + (*ap)[-1] = ';'; + if ( vararg != 0 ) + { *vararg = 0; + fputs(breaks[0], out); /* any prior args */ + fputs("va_dcl", out); /* the final arg */ + fputs(bp[0], out); + } + else + fputs(breaks[0], out); + } + free((char *)breaks); + return 0; +} diff --git a/Engine/lib/ljpeg/extras/cderror.h b/Engine/lib/ljpeg/extras/cderror.h new file mode 100644 index 000000000..70435e161 --- /dev/null +++ b/Engine/lib/ljpeg/extras/cderror.h @@ -0,0 +1,132 @@ +/* + * cderror.h + * + * Copyright (C) 1994-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the error and message codes for the cjpeg/djpeg + * applications. These strings are not needed as part of the JPEG library + * proper. + * Edit this file to add new codes, or to translate the message strings to + * some other language. + */ + +/* + * To define the enum list of message codes, include this file without + * defining macro JMESSAGE. To create a message string table, include it + * again with a suitable JMESSAGE definition (see jerror.c for an example). + */ +#ifndef JMESSAGE +#ifndef CDERROR_H +#define CDERROR_H +/* First time through, define the enum list */ +#define JMAKE_ENUM_LIST +#else +/* Repeated inclusions of this file are no-ops unless JMESSAGE is defined */ +#define JMESSAGE(code,string) +#endif /* CDERROR_H */ +#endif /* JMESSAGE */ + +#ifdef JMAKE_ENUM_LIST + +typedef enum { + +#define JMESSAGE(code,string) code , + +#endif /* JMAKE_ENUM_LIST */ + +JMESSAGE(JMSG_FIRSTADDONCODE=1000, NULL) /* Must be first entry! */ + +#ifdef BMP_SUPPORTED +JMESSAGE(JERR_BMP_BADCMAP, "Unsupported BMP colormap format") +JMESSAGE(JERR_BMP_BADDEPTH, "Only 8- and 24-bit BMP files are supported") +JMESSAGE(JERR_BMP_BADHEADER, "Invalid BMP file: bad header length") +JMESSAGE(JERR_BMP_BADPLANES, "Invalid BMP file: biPlanes not equal to 1") +JMESSAGE(JERR_BMP_COLORSPACE, "BMP output must be grayscale or RGB") +JMESSAGE(JERR_BMP_COMPRESSED, "Sorry, compressed BMPs not yet supported") +JMESSAGE(JERR_BMP_NOT, "Not a BMP file - does not start with BM") +JMESSAGE(JTRC_BMP, "%ux%u 24-bit BMP image") +JMESSAGE(JTRC_BMP_MAPPED, "%ux%u 8-bit colormapped BMP image") +JMESSAGE(JTRC_BMP_OS2, "%ux%u 24-bit OS2 BMP image") +JMESSAGE(JTRC_BMP_OS2_MAPPED, "%ux%u 8-bit colormapped OS2 BMP image") +#endif /* BMP_SUPPORTED */ + +#ifdef GIF_SUPPORTED +JMESSAGE(JERR_GIF_BUG, "GIF output got confused") +JMESSAGE(JERR_GIF_CODESIZE, "Bogus GIF codesize %d") +JMESSAGE(JERR_GIF_COLORSPACE, "GIF output must be grayscale or RGB") +JMESSAGE(JERR_GIF_IMAGENOTFOUND, "Too few images in GIF file") +JMESSAGE(JERR_GIF_NOT, "Not a GIF file") +JMESSAGE(JTRC_GIF, "%ux%ux%d GIF image") +JMESSAGE(JTRC_GIF_BADVERSION, + "Warning: unexpected GIF version number '%c%c%c'") +JMESSAGE(JTRC_GIF_EXTENSION, "Ignoring GIF extension block of type 0x%02x") +JMESSAGE(JTRC_GIF_NONSQUARE, "Caution: nonsquare pixels in input") +JMESSAGE(JWRN_GIF_BADDATA, "Corrupt data in GIF file") +JMESSAGE(JWRN_GIF_CHAR, "Bogus char 0x%02x in GIF file, ignoring") +JMESSAGE(JWRN_GIF_ENDCODE, "Premature end of GIF image") +JMESSAGE(JWRN_GIF_NOMOREDATA, "Ran out of GIF bits") +#endif /* GIF_SUPPORTED */ + +#ifdef PPM_SUPPORTED +JMESSAGE(JERR_PPM_COLORSPACE, "PPM output must be grayscale or RGB") +JMESSAGE(JERR_PPM_NONNUMERIC, "Nonnumeric data in PPM file") +JMESSAGE(JERR_PPM_NOT, "Not a PPM/PGM file") +JMESSAGE(JTRC_PGM, "%ux%u PGM image") +JMESSAGE(JTRC_PGM_TEXT, "%ux%u text PGM image") +JMESSAGE(JTRC_PPM, "%ux%u PPM image") +JMESSAGE(JTRC_PPM_TEXT, "%ux%u text PPM image") +#endif /* PPM_SUPPORTED */ + +#ifdef RLE_SUPPORTED +JMESSAGE(JERR_RLE_BADERROR, "Bogus error code from RLE library") +JMESSAGE(JERR_RLE_COLORSPACE, "RLE output must be grayscale or RGB") +JMESSAGE(JERR_RLE_DIMENSIONS, "Image dimensions (%ux%u) too large for RLE") +JMESSAGE(JERR_RLE_EMPTY, "Empty RLE file") +JMESSAGE(JERR_RLE_EOF, "Premature EOF in RLE header") +JMESSAGE(JERR_RLE_MEM, "Insufficient memory for RLE header") +JMESSAGE(JERR_RLE_NOT, "Not an RLE file") +JMESSAGE(JERR_RLE_TOOMANYCHANNELS, "Cannot handle %d output channels for RLE") +JMESSAGE(JERR_RLE_UNSUPPORTED, "Cannot handle this RLE setup") +JMESSAGE(JTRC_RLE, "%ux%u full-color RLE file") +JMESSAGE(JTRC_RLE_FULLMAP, "%ux%u full-color RLE file with map of length %d") +JMESSAGE(JTRC_RLE_GRAY, "%ux%u grayscale RLE file") +JMESSAGE(JTRC_RLE_MAPGRAY, "%ux%u grayscale RLE file with map of length %d") +JMESSAGE(JTRC_RLE_MAPPED, "%ux%u colormapped RLE file with map of length %d") +#endif /* RLE_SUPPORTED */ + +#ifdef TARGA_SUPPORTED +JMESSAGE(JERR_TGA_BADCMAP, "Unsupported Targa colormap format") +JMESSAGE(JERR_TGA_BADPARMS, "Invalid or unsupported Targa file") +JMESSAGE(JERR_TGA_COLORSPACE, "Targa output must be grayscale or RGB") +JMESSAGE(JTRC_TGA, "%ux%u RGB Targa image") +JMESSAGE(JTRC_TGA_GRAY, "%ux%u grayscale Targa image") +JMESSAGE(JTRC_TGA_MAPPED, "%ux%u colormapped Targa image") +#else +JMESSAGE(JERR_TGA_NOTCOMP, "Targa support was not compiled") +#endif /* TARGA_SUPPORTED */ + +JMESSAGE(JERR_BAD_CMAP_FILE, + "Color map file is invalid or of unsupported format") +JMESSAGE(JERR_TOO_MANY_COLORS, + "Output file format cannot handle %d colormap entries") +JMESSAGE(JERR_UNGETC_FAILED, "ungetc failed") +#ifdef TARGA_SUPPORTED +JMESSAGE(JERR_UNKNOWN_FORMAT, + "Unrecognized input file format --- perhaps you need -targa") +#else +JMESSAGE(JERR_UNKNOWN_FORMAT, "Unrecognized input file format") +#endif +JMESSAGE(JERR_UNSUPPORTED_FORMAT, "Unsupported output file format") + +#ifdef JMAKE_ENUM_LIST + + JMSG_LASTADDONCODE +} ADDON_MESSAGE_CODE; + +#undef JMAKE_ENUM_LIST +#endif /* JMAKE_ENUM_LIST */ + +/* Zap JMESSAGE macro so that future re-inclusions do nothing by default */ +#undef JMESSAGE diff --git a/Engine/lib/ljpeg/extras/cdjpeg.c b/Engine/lib/ljpeg/extras/cdjpeg.c new file mode 100644 index 000000000..b6250ff97 --- /dev/null +++ b/Engine/lib/ljpeg/extras/cdjpeg.c @@ -0,0 +1,181 @@ +/* + * cdjpeg.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains common support routines used by the IJG application + * programs (cjpeg, djpeg, jpegtran). + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ +#include /* to declare isupper(), tolower() */ +#ifdef NEED_SIGNAL_CATCHER +#include /* to declare signal() */ +#endif +#ifdef USE_SETMODE +#include /* to declare setmode()'s parameter macros */ +/* If you have setmode() but not , just delete this line: */ +#include /* to declare setmode() */ +#endif + + +/* + * Signal catcher to ensure that temporary files are removed before aborting. + * NB: for Amiga Manx C this is actually a global routine named _abort(); + * we put "#define signal_catcher _abort" in jconfig.h. Talk about bogus... + */ + +#ifdef NEED_SIGNAL_CATCHER + +static j_common_ptr sig_cinfo; + +void /* must be global for Manx C */ +signal_catcher (int signum) +{ + if (sig_cinfo != NULL) { + if (sig_cinfo->err != NULL) /* turn off trace output */ + sig_cinfo->err->trace_level = 0; + jpeg_destroy(sig_cinfo); /* clean up memory allocation & temp files */ + } + exit(EXIT_FAILURE); +} + + +GLOBAL(void) +enable_signal_catcher (j_common_ptr cinfo) +{ + sig_cinfo = cinfo; +#ifdef SIGINT /* not all systems have SIGINT */ + signal(SIGINT, signal_catcher); +#endif +#ifdef SIGTERM /* not all systems have SIGTERM */ + signal(SIGTERM, signal_catcher); +#endif +} + +#endif + + +/* + * Optional progress monitor: display a percent-done figure on stderr. + */ + +#ifdef PROGRESS_REPORT + +METHODDEF(void) +progress_monitor (j_common_ptr cinfo) +{ + cd_progress_ptr prog = (cd_progress_ptr) cinfo->progress; + int total_passes = prog->pub.total_passes + prog->total_extra_passes; + int percent_done = (int) (prog->pub.pass_counter*100L/prog->pub.pass_limit); + + if (percent_done != prog->percent_done) { + prog->percent_done = percent_done; + if (total_passes > 1) { + fprintf(stderr, "\rPass %d/%d: %3d%% ", + prog->pub.completed_passes + prog->completed_extra_passes + 1, + total_passes, percent_done); + } else { + fprintf(stderr, "\r %3d%% ", percent_done); + } + fflush(stderr); + } +} + + +GLOBAL(void) +start_progress_monitor (j_common_ptr cinfo, cd_progress_ptr progress) +{ + /* Enable progress display, unless trace output is on */ + if (cinfo->err->trace_level == 0) { + progress->pub.progress_monitor = progress_monitor; + progress->completed_extra_passes = 0; + progress->total_extra_passes = 0; + progress->percent_done = -1; + cinfo->progress = &progress->pub; + } +} + + +GLOBAL(void) +end_progress_monitor (j_common_ptr cinfo) +{ + /* Clear away progress display */ + if (cinfo->err->trace_level == 0) { + fprintf(stderr, "\r \r"); + fflush(stderr); + } +} + +#endif + + +/* + * Case-insensitive matching of possibly-abbreviated keyword switches. + * keyword is the constant keyword (must be lower case already), + * minchars is length of minimum legal abbreviation. + */ + +GLOBAL(boolean) +keymatch (char * arg, const char * keyword, int minchars) +{ + register int ca, ck; + register int nmatched = 0; + + while ((ca = *arg++) != '\0') { + if ((ck = *keyword++) == '\0') + return FALSE; /* arg longer than keyword, no good */ + if (isupper(ca)) /* force arg to lcase (assume ck is already) */ + ca = tolower(ca); + if (ca != ck) + return FALSE; /* no good */ + nmatched++; /* count matched characters */ + } + /* reached end of argument; fail if it's too short for unique abbrev */ + if (nmatched < minchars) + return FALSE; + return TRUE; /* A-OK */ +} + + +/* + * Routines to establish binary I/O mode for stdin and stdout. + * Non-Unix systems often require some hacking to get out of text mode. + */ + +GLOBAL(FILE *) +read_stdin (void) +{ + FILE * input_file = stdin; + +#ifdef USE_SETMODE /* need to hack file mode? */ + setmode(fileno(stdin), O_BINARY); +#endif +#ifdef USE_FDOPEN /* need to re-open in binary mode? */ + if ((input_file = fdopen(fileno(stdin), READ_BINARY)) == NULL) { + fprintf(stderr, "Cannot reopen stdin\n"); + exit(EXIT_FAILURE); + } +#endif + return input_file; +} + + +GLOBAL(FILE *) +write_stdout (void) +{ + FILE * output_file = stdout; + +#ifdef USE_SETMODE /* need to hack file mode? */ + setmode(fileno(stdout), O_BINARY); +#endif +#ifdef USE_FDOPEN /* need to re-open in binary mode? */ + if ((output_file = fdopen(fileno(stdout), WRITE_BINARY)) == NULL) { + fprintf(stderr, "Cannot reopen stdout\n"); + exit(EXIT_FAILURE); + } +#endif + return output_file; +} diff --git a/Engine/lib/ljpeg/extras/cdjpeg.h b/Engine/lib/ljpeg/extras/cdjpeg.h new file mode 100644 index 000000000..2b387b6e5 --- /dev/null +++ b/Engine/lib/ljpeg/extras/cdjpeg.h @@ -0,0 +1,184 @@ +/* + * cdjpeg.h + * + * Copyright (C) 1994-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains common declarations for the sample applications + * cjpeg and djpeg. It is NOT used by the core JPEG library. + */ + +#define JPEG_CJPEG_DJPEG /* define proper options in jconfig.h */ +#define JPEG_INTERNAL_OPTIONS /* cjpeg.c,djpeg.c need to see xxx_SUPPORTED */ +#include "jinclude.h" +#include "jpeglib.h" +#include "jerror.h" /* get library error codes too */ +#include "cderror.h" /* get application-specific error codes */ + + +/* + * Object interface for cjpeg's source file decoding modules + */ + +typedef struct cjpeg_source_struct * cjpeg_source_ptr; + +struct cjpeg_source_struct { + JMETHOD(void, start_input, (j_compress_ptr cinfo, + cjpeg_source_ptr sinfo)); + JMETHOD(JDIMENSION, get_pixel_rows, (j_compress_ptr cinfo, + cjpeg_source_ptr sinfo)); + JMETHOD(void, finish_input, (j_compress_ptr cinfo, + cjpeg_source_ptr sinfo)); + + FILE *input_file; + + JSAMPARRAY buffer; + JDIMENSION buffer_height; +}; + + +/* + * Object interface for djpeg's output file encoding modules + */ + +typedef struct djpeg_dest_struct * djpeg_dest_ptr; + +struct djpeg_dest_struct { + /* start_output is called after jpeg_start_decompress finishes. + * The color map will be ready at this time, if one is needed. + */ + JMETHOD(void, start_output, (j_decompress_ptr cinfo, + djpeg_dest_ptr dinfo)); + /* Emit the specified number of pixel rows from the buffer. */ + JMETHOD(void, put_pixel_rows, (j_decompress_ptr cinfo, + djpeg_dest_ptr dinfo, + JDIMENSION rows_supplied)); + /* Finish up at the end of the image. */ + JMETHOD(void, finish_output, (j_decompress_ptr cinfo, + djpeg_dest_ptr dinfo)); + + /* Target file spec; filled in by djpeg.c after object is created. */ + FILE * output_file; + + /* Output pixel-row buffer. Created by module init or start_output. + * Width is cinfo->output_width * cinfo->output_components; + * height is buffer_height. + */ + JSAMPARRAY buffer; + JDIMENSION buffer_height; +}; + + +/* + * cjpeg/djpeg may need to perform extra passes to convert to or from + * the source/destination file format. The JPEG library does not know + * about these passes, but we'd like them to be counted by the progress + * monitor. We use an expanded progress monitor object to hold the + * additional pass count. + */ + +struct cdjpeg_progress_mgr { + struct jpeg_progress_mgr pub; /* fields known to JPEG library */ + int completed_extra_passes; /* extra passes completed */ + int total_extra_passes; /* total extra */ + /* last printed percentage stored here to avoid multiple printouts */ + int percent_done; +}; + +typedef struct cdjpeg_progress_mgr * cd_progress_ptr; + + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jinit_read_bmp jIRdBMP +#define jinit_write_bmp jIWrBMP +#define jinit_read_gif jIRdGIF +#define jinit_write_gif jIWrGIF +#define jinit_read_ppm jIRdPPM +#define jinit_write_ppm jIWrPPM +#define jinit_read_rle jIRdRLE +#define jinit_write_rle jIWrRLE +#define jinit_read_targa jIRdTarga +#define jinit_write_targa jIWrTarga +#define read_quant_tables RdQTables +#define read_scan_script RdScnScript +#define set_quant_slots SetQSlots +#define set_sample_factors SetSFacts +#define read_color_map RdCMap +#define enable_signal_catcher EnSigCatcher +#define start_progress_monitor StProgMon +#define end_progress_monitor EnProgMon +#define read_stdin RdStdin +#define write_stdout WrStdout +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + +/* Module selection routines for I/O modules. */ + +EXTERN(cjpeg_source_ptr) jinit_read_bmp JPP((j_compress_ptr cinfo)); +EXTERN(djpeg_dest_ptr) jinit_write_bmp JPP((j_decompress_ptr cinfo, + boolean is_os2)); +EXTERN(cjpeg_source_ptr) jinit_read_gif JPP((j_compress_ptr cinfo)); +EXTERN(djpeg_dest_ptr) jinit_write_gif JPP((j_decompress_ptr cinfo)); +EXTERN(cjpeg_source_ptr) jinit_read_ppm JPP((j_compress_ptr cinfo)); +EXTERN(djpeg_dest_ptr) jinit_write_ppm JPP((j_decompress_ptr cinfo)); +EXTERN(cjpeg_source_ptr) jinit_read_rle JPP((j_compress_ptr cinfo)); +EXTERN(djpeg_dest_ptr) jinit_write_rle JPP((j_decompress_ptr cinfo)); +EXTERN(cjpeg_source_ptr) jinit_read_targa JPP((j_compress_ptr cinfo)); +EXTERN(djpeg_dest_ptr) jinit_write_targa JPP((j_decompress_ptr cinfo)); + +/* cjpeg support routines (in rdswitch.c) */ + +EXTERN(boolean) read_quant_tables JPP((j_compress_ptr cinfo, char * filename, + int scale_factor, boolean force_baseline)); +EXTERN(boolean) read_scan_script JPP((j_compress_ptr cinfo, char * filename)); +EXTERN(boolean) set_quant_slots JPP((j_compress_ptr cinfo, char *arg)); +EXTERN(boolean) set_sample_factors JPP((j_compress_ptr cinfo, char *arg)); + +/* djpeg support routines (in rdcolmap.c) */ + +EXTERN(void) read_color_map JPP((j_decompress_ptr cinfo, FILE * infile)); + +/* common support routines (in cdjpeg.c) */ + +EXTERN(void) enable_signal_catcher JPP((j_common_ptr cinfo)); +EXTERN(void) start_progress_monitor JPP((j_common_ptr cinfo, + cd_progress_ptr progress)); +EXTERN(void) end_progress_monitor JPP((j_common_ptr cinfo)); +EXTERN(boolean) keymatch JPP((char * arg, const char * keyword, int minchars)); +EXTERN(FILE *) read_stdin JPP((void)); +EXTERN(FILE *) write_stdout JPP((void)); + +/* miscellaneous useful macros */ + +#ifdef DONT_USE_B_MODE /* define mode parameters for fopen() */ +#define READ_BINARY "r" +#define WRITE_BINARY "w" +#else +#ifdef VMS /* VMS is very nonstandard */ +#define READ_BINARY "rb", "ctx=stm" +#define WRITE_BINARY "wb", "ctx=stm" +#else /* standard ANSI-compliant case */ +#define READ_BINARY "rb" +#define WRITE_BINARY "wb" +#endif +#endif + +#ifndef EXIT_FAILURE /* define exit() codes if not provided */ +#define EXIT_FAILURE 1 +#endif +#ifndef EXIT_SUCCESS +#ifdef VMS +#define EXIT_SUCCESS 1 /* VMS is very nonstandard */ +#else +#define EXIT_SUCCESS 0 +#endif +#endif +#ifndef EXIT_WARNING +#ifdef VMS +#define EXIT_WARNING 1 /* VMS is very nonstandard */ +#else +#define EXIT_WARNING 2 +#endif +#endif diff --git a/Engine/lib/ljpeg/extras/change.log b/Engine/lib/ljpeg/extras/change.log new file mode 100644 index 000000000..74102c0db --- /dev/null +++ b/Engine/lib/ljpeg/extras/change.log @@ -0,0 +1,217 @@ +CHANGE LOG for Independent JPEG Group's JPEG software + + +Version 6b 27-Mar-1998 +----------------------- + +jpegtran has new features for lossless image transformations (rotation +and flipping) as well as "lossless" reduction to grayscale. + +jpegtran now copies comments by default; it has a -copy switch to enable +copying all APPn blocks as well, or to suppress comments. (Formerly it +always suppressed comments and APPn blocks.) jpegtran now also preserves +JFIF version and resolution information. + +New decompressor library feature: COM and APPn markers found in the input +file can be saved in memory for later use by the application. (Before, +you had to code this up yourself with a custom marker processor.) + +There is an unused field "void * client_data" now in compress and decompress +parameter structs; this may be useful in some applications. + +JFIF version number information is now saved by the decoder and accepted by +the encoder. jpegtran uses this to copy the source file's version number, +to ensure "jpegtran -copy all" won't create bogus files that contain JFXX +extensions but claim to be version 1.01. Applications that generate their +own JFXX extension markers also (finally) have a supported way to cause the +encoder to emit JFIF version number 1.02. + +djpeg's trace mode reports JFIF 1.02 thumbnail images as such, rather +than as unknown APP0 markers. + +In -verbose mode, djpeg and rdjpgcom will try to print the contents of +APP12 markers as text. Some digital cameras store useful text information +in APP12 markers. + +Handling of truncated data streams is more robust: blocks beyond the one in +which the error occurs will be output as uniform gray, or left unchanged +if decoding a progressive JPEG. The appearance no longer depends on the +Huffman tables being used. + +Huffman tables are checked for validity much more carefully than before. + +To avoid the Unisys LZW patent, djpeg's GIF output capability has been +changed to produce "uncompressed GIFs", and cjpeg's GIF input capability +has been removed altogether. We're not happy about it either, but there +seems to be no good alternative. + +The configure script now supports building libjpeg as a shared library +on many flavors of Unix (all the ones that GNU libtool knows how to +build shared libraries for). Use "./configure --enable-shared" to +try this out. + +New jconfig file and makefiles for Microsoft Visual C++ and Developer Studio. +Also, a jconfig file and a build script for Metrowerks CodeWarrior +on Apple Macintosh. makefile.dj has been updated for DJGPP v2, and there +are miscellaneous other minor improvements in the makefiles. + +jmemmac.c now knows how to create temporary files following Mac System 7 +conventions. + +djpeg's -map switch is now able to read raw-format PPM files reliably. + +cjpeg -progressive -restart no longer generates any unnecessary DRI markers. + +Multiple calls to jpeg_simple_progression for a single JPEG object +no longer leak memory. + + +Version 6a 7-Feb-96 +-------------------- + +Library initialization sequence modified to detect version mismatches +and struct field packing mismatches between library and calling application. +This change requires applications to be recompiled, but does not require +any application source code change. + +All routine declarations changed to the style "GLOBAL(type) name ...", +that is, GLOBAL, LOCAL, METHODDEF, EXTERN are now macros taking the +routine's return type as an argument. This makes it possible to add +Microsoft-style linkage keywords to all the routines by changing just +these macros. Note that any application code that was using these macros +will have to be changed. + +DCT coefficient quantization tables are now stored in normal array order +rather than zigzag order. Application code that calls jpeg_add_quant_table, +or otherwise manipulates quantization tables directly, will need to be +changed. If you need to make such code work with either older or newer +versions of the library, a test like "#if JPEG_LIB_VERSION >= 61" is +recommended. + +djpeg's trace capability now dumps DQT tables in natural order, not zigzag +order. This allows the trace output to be made into a "-qtables" file +more easily. + +New system-dependent memory manager module for use on Apple Macintosh. + +Fix bug in cjpeg's -smooth option: last one or two scanlines would be +duplicates of the prior line unless the image height mod 16 was 1 or 2. + +Repair minor problems in VMS, BCC, MC6 makefiles. + +New configure script based on latest GNU Autoconf. + +Correct the list of include files needed by MetroWerks C for ccommand(). + +Numerous small documentation updates. + + +Version 6 2-Aug-95 +------------------- + +Progressive JPEG support: library can read and write full progressive JPEG +files. A "buffered image" mode supports incremental decoding for on-the-fly +display of progressive images. Simply recompiling an existing IJG-v5-based +decoder with v6 should allow it to read progressive files, though of course +without any special progressive display. + +New "jpegtran" application performs lossless transcoding between different +JPEG formats; primarily, it can be used to convert baseline to progressive +JPEG and vice versa. In support of jpegtran, the library now allows lossless +reading and writing of JPEG files as DCT coefficient arrays. This ability +may be of use in other applications. + +Notes for programmers: +* We changed jpeg_start_decompress() to be able to suspend; this makes all +decoding modes available to suspending-input applications. However, +existing applications that use suspending input will need to be changed +to check the return value from jpeg_start_decompress(). You don't need to +do anything if you don't use a suspending data source. +* We changed the interface to the virtual array routines: access_virt_array +routines now take a count of the number of rows to access this time. The +last parameter to request_virt_array routines is now interpreted as the +maximum number of rows that may be accessed at once, but not necessarily +the height of every access. + + +Version 5b 15-Mar-95 +--------------------- + +Correct bugs with grayscale images having v_samp_factor > 1. + +jpeg_write_raw_data() now supports output suspension. + +Correct bugs in "configure" script for case of compiling in +a directory other than the one containing the source files. + +Repair bug in jquant1.c: sometimes didn't use as many colors as it could. + +Borland C makefile and jconfig file work under either MS-DOS or OS/2. + +Miscellaneous improvements to documentation. + + +Version 5a 7-Dec-94 +-------------------- + +Changed color conversion roundoff behavior so that grayscale values are +represented exactly. (This causes test image files to change.) + +Make ordered dither use 16x16 instead of 4x4 pattern for a small quality +improvement. + +New configure script based on latest GNU Autoconf. +Fix configure script to handle CFLAGS correctly. +Rename *.auto files to *.cfg, so that configure script still works if +file names have been truncated for DOS. + +Fix bug in rdbmp.c: didn't allow for extra data between header and image. + +Modify rdppm.c/wrppm.c to handle 2-byte raw PPM/PGM formats for 12-bit data. + +Fix several bugs in rdrle.c. + +NEED_SHORT_EXTERNAL_NAMES option was broken. + +Revise jerror.h/jerror.c for more flexibility in message table. + +Repair oversight in jmemname.c NO_MKTEMP case: file could be there +but unreadable. + + +Version 5 24-Sep-94 +-------------------- + +Version 5 represents a nearly complete redesign and rewrite of the IJG +software. Major user-visible changes include: + * Automatic configuration simplifies installation for most Unix systems. + * A range of speed vs. image quality tradeoffs are supported. + This includes resizing of an image during decompression: scaling down + by a factor of 1/2, 1/4, or 1/8 is handled very efficiently. + * New programs rdjpgcom and wrjpgcom allow insertion and extraction + of text comments in a JPEG file. + +The application programmer's interface to the library has changed completely. +Notable improvements include: + * We have eliminated the use of callback routines for handling the + uncompressed image data. The application now sees the library as a + set of routines that it calls to read or write image data on a + scanline-by-scanline basis. + * The application image data is represented in a conventional interleaved- + pixel format, rather than as a separate array for each color channel. + This can save a copying step in many programs. + * The handling of compressed data has been cleaned up: the application can + supply routines to source or sink the compressed data. It is possible to + suspend processing on source/sink buffer overrun, although this is not + supported in all operating modes. + * All static state has been eliminated from the library, so that multiple + instances of compression or decompression can be active concurrently. + * JPEG abbreviated datastream formats are supported, ie, quantization and + Huffman tables can be stored separately from the image data. + * And not only that, but the documentation of the library has improved + considerably! + + +The last widely used release before the version 5 rewrite was version 4A of +18-Feb-93. Change logs before that point have been discarded, since they +are not of much interest after the rewrite. diff --git a/Engine/lib/ljpeg/extras/cjpeg.1 b/Engine/lib/ljpeg/extras/cjpeg.1 new file mode 100644 index 000000000..d175a961c --- /dev/null +++ b/Engine/lib/ljpeg/extras/cjpeg.1 @@ -0,0 +1,292 @@ +.TH CJPEG 1 "20 March 1998" +.SH NAME +cjpeg \- compress an image file to a JPEG file +.SH SYNOPSIS +.B cjpeg +[ +.I options +] +[ +.I filename +] +.LP +.SH DESCRIPTION +.LP +.B cjpeg +compresses the named image file, or the standard input if no file is +named, and produces a JPEG/JFIF file on the standard output. +The currently supported input file formats are: PPM (PBMPLUS color +format), PGM (PBMPLUS gray-scale format), BMP, Targa, and RLE (Utah Raster +Toolkit format). (RLE is supported only if the URT library is available.) +.SH OPTIONS +All switch names may be abbreviated; for example, +.B \-grayscale +may be written +.B \-gray +or +.BR \-gr . +Most of the "basic" switches can be abbreviated to as little as one letter. +Upper and lower case are equivalent (thus +.B \-BMP +is the same as +.BR \-bmp ). +British spellings are also accepted (e.g., +.BR \-greyscale ), +though for brevity these are not mentioned below. +.PP +The basic switches are: +.TP +.BI \-quality " N" +Scale quantization tables to adjust image quality. Quality is 0 (worst) to +100 (best); default is 75. (See below for more info.) +.TP +.B \-grayscale +Create monochrome JPEG file from color input. Be sure to use this switch when +compressing a grayscale BMP file, because +.B cjpeg +isn't bright enough to notice whether a BMP file uses only shades of gray. +By saying +.BR \-grayscale , +you'll get a smaller JPEG file that takes less time to process. +.TP +.B \-optimize +Perform optimization of entropy encoding parameters. Without this, default +encoding parameters are used. +.B \-optimize +usually makes the JPEG file a little smaller, but +.B cjpeg +runs somewhat slower and needs much more memory. Image quality and speed of +decompression are unaffected by +.BR \-optimize . +.TP +.B \-progressive +Create progressive JPEG file (see below). +.TP +.B \-targa +Input file is Targa format. Targa files that contain an "identification" +field will not be automatically recognized by +.BR cjpeg ; +for such files you must specify +.B \-targa +to make +.B cjpeg +treat the input as Targa format. +For most Targa files, you won't need this switch. +.PP +The +.B \-quality +switch lets you trade off compressed file size against quality of the +reconstructed image: the higher the quality setting, the larger the JPEG file, +and the closer the output image will be to the original input. Normally you +want to use the lowest quality setting (smallest file) that decompresses into +something visually indistinguishable from the original image. For this +purpose the quality setting should be between 50 and 95; the default of 75 is +often about right. If you see defects at +.B \-quality +75, then go up 5 or 10 counts at a time until you are happy with the output +image. (The optimal setting will vary from one image to another.) +.PP +.B \-quality +100 will generate a quantization table of all 1's, minimizing loss in the +quantization step (but there is still information loss in subsampling, as well +as roundoff error). This setting is mainly of interest for experimental +purposes. Quality values above about 95 are +.B not +recommended for normal use; the compressed file size goes up dramatically for +hardly any gain in output image quality. +.PP +In the other direction, quality values below 50 will produce very small files +of low image quality. Settings around 5 to 10 might be useful in preparing an +index of a large image library, for example. Try +.B \-quality +2 (or so) for some amusing Cubist effects. (Note: quality +values below about 25 generate 2-byte quantization tables, which are +considered optional in the JPEG standard. +.B cjpeg +emits a warning message when you give such a quality value, because some +other JPEG programs may be unable to decode the resulting file. Use +.B \-baseline +if you need to ensure compatibility at low quality values.) +.PP +The +.B \-progressive +switch creates a "progressive JPEG" file. In this type of JPEG file, the data +is stored in multiple scans of increasing quality. If the file is being +transmitted over a slow communications link, the decoder can use the first +scan to display a low-quality image very quickly, and can then improve the +display with each subsequent scan. The final image is exactly equivalent to a +standard JPEG file of the same quality setting, and the total file size is +about the same --- often a little smaller. +.B Caution: +progressive JPEG is not yet widely implemented, so many decoders will be +unable to view a progressive JPEG file at all. +.PP +Switches for advanced users: +.TP +.B \-dct int +Use integer DCT method (default). +.TP +.B \-dct fast +Use fast integer DCT (less accurate). +.TP +.B \-dct float +Use floating-point DCT method. +The float method is very slightly more accurate than the int method, but is +much slower unless your machine has very fast floating-point hardware. Also +note that results of the floating-point method may vary slightly across +machines, while the integer methods should give the same results everywhere. +The fast integer method is much less accurate than the other two. +.TP +.BI \-restart " N" +Emit a JPEG restart marker every N MCU rows, or every N MCU blocks if "B" is +attached to the number. +.B \-restart 0 +(the default) means no restart markers. +.TP +.BI \-smooth " N" +Smooth the input image to eliminate dithering noise. N, ranging from 1 to +100, indicates the strength of smoothing. 0 (the default) means no smoothing. +.TP +.BI \-maxmemory " N" +Set limit for amount of memory to use in processing large images. Value is +in thousands of bytes, or millions of bytes if "M" is attached to the +number. For example, +.B \-max 4m +selects 4000000 bytes. If more space is needed, temporary files will be used. +.TP +.BI \-outfile " name" +Send output image to the named file, not to standard output. +.TP +.B \-verbose +Enable debug printout. More +.BR \-v 's +give more output. Also, version information is printed at startup. +.TP +.B \-debug +Same as +.BR \-verbose . +.PP +The +.B \-restart +option inserts extra markers that allow a JPEG decoder to resynchronize after +a transmission error. Without restart markers, any damage to a compressed +file will usually ruin the image from the point of the error to the end of the +image; with restart markers, the damage is usually confined to the portion of +the image up to the next restart marker. Of course, the restart markers +occupy extra space. We recommend +.B \-restart 1 +for images that will be transmitted across unreliable networks such as Usenet. +.PP +The +.B \-smooth +option filters the input to eliminate fine-scale noise. This is often useful +when converting dithered images to JPEG: a moderate smoothing factor of 10 to +50 gets rid of dithering patterns in the input file, resulting in a smaller +JPEG file and a better-looking image. Too large a smoothing factor will +visibly blur the image, however. +.PP +Switches for wizards: +.TP +.B \-baseline +Force baseline-compatible quantization tables to be generated. This clamps +quantization values to 8 bits even at low quality settings. (This switch is +poorly named, since it does not ensure that the output is actually baseline +JPEG. For example, you can use +.B \-baseline +and +.B \-progressive +together.) +.TP +.BI \-qtables " file" +Use the quantization tables given in the specified text file. +.TP +.BI \-qslots " N[,...]" +Select which quantization table to use for each color component. +.TP +.BI \-sample " HxV[,...]" +Set JPEG sampling factors for each color component. +.TP +.BI \-scans " file" +Use the scan script given in the specified text file. +.PP +The "wizard" switches are intended for experimentation with JPEG. If you +don't know what you are doing, \fBdon't use them\fR. These switches are +documented further in the file wizard.doc. +.SH EXAMPLES +.LP +This example compresses the PPM file foo.ppm with a quality factor of +60 and saves the output as foo.jpg: +.IP +.B cjpeg \-quality +.I 60 foo.ppm +.B > +.I foo.jpg +.SH HINTS +Color GIF files are not the ideal input for JPEG; JPEG is really intended for +compressing full-color (24-bit) images. In particular, don't try to convert +cartoons, line drawings, and other images that have only a few distinct +colors. GIF works great on these, JPEG does not. If you want to convert a +GIF to JPEG, you should experiment with +.BR cjpeg 's +.B \-quality +and +.B \-smooth +options to get a satisfactory conversion. +.B \-smooth 10 +or so is often helpful. +.PP +Avoid running an image through a series of JPEG compression/decompression +cycles. Image quality loss will accumulate; after ten or so cycles the image +may be noticeably worse than it was after one cycle. It's best to use a +lossless format while manipulating an image, then convert to JPEG format when +you are ready to file the image away. +.PP +The +.B \-optimize +option to +.B cjpeg +is worth using when you are making a "final" version for posting or archiving. +It's also a win when you are using low quality settings to make very small +JPEG files; the percentage improvement is often a lot more than it is on +larger files. (At present, +.B \-optimize +mode is always selected when generating progressive JPEG files.) +.SH ENVIRONMENT +.TP +.B JPEGMEM +If this environment variable is set, its value is the default memory limit. +The value is specified as described for the +.B \-maxmemory +switch. +.B JPEGMEM +overrides the default value specified when the program was compiled, and +itself is overridden by an explicit +.BR \-maxmemory . +.SH SEE ALSO +.BR djpeg (1), +.BR jpegtran (1), +.BR rdjpgcom (1), +.BR wrjpgcom (1) +.br +.BR ppm (5), +.BR pgm (5) +.br +Wallace, Gregory K. "The JPEG Still Picture Compression Standard", +Communications of the ACM, April 1991 (vol. 34, no. 4), pp. 30-44. +.SH AUTHOR +Independent JPEG Group +.SH BUGS +Arithmetic coding is not supported for legal reasons. +.PP +GIF input files are no longer supported, to avoid the Unisys LZW patent. +Use a Unisys-licensed program if you need to read a GIF file. (Conversion +of GIF files to JPEG is usually a bad idea anyway.) +.PP +Not all variants of BMP and Targa file formats are supported. +.PP +The +.B \-targa +switch is not a bug, it's a feature. (It would be a bug if the Targa format +designers had not been clueless.) +.PP +Still not as fast as we'd like. diff --git a/Engine/lib/ljpeg/extras/cjpeg.c b/Engine/lib/ljpeg/extras/cjpeg.c new file mode 100644 index 000000000..f2a929f0c --- /dev/null +++ b/Engine/lib/ljpeg/extras/cjpeg.c @@ -0,0 +1,606 @@ +/* + * cjpeg.c + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a command-line user interface for the JPEG compressor. + * It should work on any system with Unix- or MS-DOS-style command lines. + * + * Two different command line styles are permitted, depending on the + * compile-time switch TWO_FILE_COMMANDLINE: + * cjpeg [options] inputfile outputfile + * cjpeg [options] [inputfile] + * In the second style, output is always to standard output, which you'd + * normally redirect to a file or pipe to some other program. Input is + * either from a named file or from standard input (typically redirected). + * The second style is convenient on Unix but is unhelpful on systems that + * don't support pipes. Also, you MUST use the first style if your system + * doesn't do binary I/O to stdin/stdout. + * To simplify script writing, the "-outfile" switch is provided. The syntax + * cjpeg [options] -outfile outputfile inputfile + * works regardless of which command line style is used. + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ +#include "jversion.h" /* for version message */ + +#ifdef USE_CCOMMAND /* command-line reader for Macintosh */ +#ifdef __MWERKS__ +#include /* Metrowerks needs this */ +#include /* ... and this */ +#endif +#ifdef THINK_C +#include /* Think declares it here */ +#endif +#endif + + +/* Create the add-on message string table. */ + +#define JMESSAGE(code,string) string , + +static const char * const cdjpeg_message_table[] = { +#include "cderror.h" + NULL +}; + + +/* + * This routine determines what format the input file is, + * and selects the appropriate input-reading module. + * + * To determine which family of input formats the file belongs to, + * we may look only at the first byte of the file, since C does not + * guarantee that more than one character can be pushed back with ungetc. + * Looking at additional bytes would require one of these approaches: + * 1) assume we can fseek() the input file (fails for piped input); + * 2) assume we can push back more than one character (works in + * some C implementations, but unportable); + * 3) provide our own buffering (breaks input readers that want to use + * stdio directly, such as the RLE library); + * or 4) don't put back the data, and modify the input_init methods to assume + * they start reading after the start of file (also breaks RLE library). + * #1 is attractive for MS-DOS but is untenable on Unix. + * + * The most portable solution for file types that can't be identified by their + * first byte is to make the user tell us what they are. This is also the + * only approach for "raw" file types that contain only arbitrary values. + * We presently apply this method for Targa files. Most of the time Targa + * files start with 0x00, so we recognize that case. Potentially, however, + * a Targa file could start with any byte value (byte 0 is the length of the + * seldom-used ID field), so we provide a switch to force Targa input mode. + */ + +static boolean is_targa; /* records user -targa switch */ + + +LOCAL(cjpeg_source_ptr) +select_file_type (j_compress_ptr cinfo, FILE * infile) +{ + int c; + + if (is_targa) { +#ifdef TARGA_SUPPORTED + return jinit_read_targa(cinfo); +#else + ERREXIT(cinfo, JERR_TGA_NOTCOMP); +#endif + } + + if ((c = getc(infile)) == EOF) + ERREXIT(cinfo, JERR_INPUT_EMPTY); + if (ungetc(c, infile) == EOF) + ERREXIT(cinfo, JERR_UNGETC_FAILED); + + switch (c) { +#ifdef BMP_SUPPORTED + case 'B': + return jinit_read_bmp(cinfo); +#endif +#ifdef GIF_SUPPORTED + case 'G': + return jinit_read_gif(cinfo); +#endif +#ifdef PPM_SUPPORTED + case 'P': + return jinit_read_ppm(cinfo); +#endif +#ifdef RLE_SUPPORTED + case 'R': + return jinit_read_rle(cinfo); +#endif +#ifdef TARGA_SUPPORTED + case 0x00: + return jinit_read_targa(cinfo); +#endif + default: + ERREXIT(cinfo, JERR_UNKNOWN_FORMAT); + break; + } + + return NULL; /* suppress compiler warnings */ +} + + +/* + * Argument-parsing code. + * The switch parser is designed to be useful with DOS-style command line + * syntax, ie, intermixed switches and file names, where only the switches + * to the left of a given file name affect processing of that file. + * The main program in this file doesn't actually use this capability... + */ + + +static const char * progname; /* program name for error messages */ +static char * outfilename; /* for -outfile switch */ + + +LOCAL(void) +usage (void) +/* complain about bad command line */ +{ + fprintf(stderr, "usage: %s [switches] ", progname); +#ifdef TWO_FILE_COMMANDLINE + fprintf(stderr, "inputfile outputfile\n"); +#else + fprintf(stderr, "[inputfile]\n"); +#endif + + fprintf(stderr, "Switches (names may be abbreviated):\n"); + fprintf(stderr, " -quality N Compression quality (0..100; 5-95 is useful range)\n"); + fprintf(stderr, " -grayscale Create monochrome JPEG file\n"); +#ifdef ENTROPY_OPT_SUPPORTED + fprintf(stderr, " -optimize Optimize Huffman table (smaller file, but slow compression)\n"); +#endif +#ifdef C_PROGRESSIVE_SUPPORTED + fprintf(stderr, " -progressive Create progressive JPEG file\n"); +#endif +#ifdef TARGA_SUPPORTED + fprintf(stderr, " -targa Input file is Targa format (usually not needed)\n"); +#endif + fprintf(stderr, "Switches for advanced users:\n"); +#ifdef DCT_ISLOW_SUPPORTED + fprintf(stderr, " -dct int Use integer DCT method%s\n", + (JDCT_DEFAULT == JDCT_ISLOW ? " (default)" : "")); +#endif +#ifdef DCT_IFAST_SUPPORTED + fprintf(stderr, " -dct fast Use fast integer DCT (less accurate)%s\n", + (JDCT_DEFAULT == JDCT_IFAST ? " (default)" : "")); +#endif +#ifdef DCT_FLOAT_SUPPORTED + fprintf(stderr, " -dct float Use floating-point DCT method%s\n", + (JDCT_DEFAULT == JDCT_FLOAT ? " (default)" : "")); +#endif + fprintf(stderr, " -restart N Set restart interval in rows, or in blocks with B\n"); +#ifdef INPUT_SMOOTHING_SUPPORTED + fprintf(stderr, " -smooth N Smooth dithered input (N=1..100 is strength)\n"); +#endif + fprintf(stderr, " -maxmemory N Maximum memory to use (in kbytes)\n"); + fprintf(stderr, " -outfile name Specify name for output file\n"); + fprintf(stderr, " -verbose or -debug Emit debug output\n"); + fprintf(stderr, "Switches for wizards:\n"); +#ifdef C_ARITH_CODING_SUPPORTED + fprintf(stderr, " -arithmetic Use arithmetic coding\n"); +#endif + fprintf(stderr, " -baseline Force baseline quantization tables\n"); + fprintf(stderr, " -qtables file Use quantization tables given in file\n"); + fprintf(stderr, " -qslots N[,...] Set component quantization tables\n"); + fprintf(stderr, " -sample HxV[,...] Set component sampling factors\n"); +#ifdef C_MULTISCAN_FILES_SUPPORTED + fprintf(stderr, " -scans file Create multi-scan JPEG per script file\n"); +#endif + exit(EXIT_FAILURE); +} + + +LOCAL(int) +parse_switches (j_compress_ptr cinfo, int argc, char **argv, + int last_file_arg_seen, boolean for_real) +/* Parse optional switches. + * Returns argv[] index of first file-name argument (== argc if none). + * Any file names with indexes <= last_file_arg_seen are ignored; + * they have presumably been processed in a previous iteration. + * (Pass 0 for last_file_arg_seen on the first or only iteration.) + * for_real is FALSE on the first (dummy) pass; we may skip any expensive + * processing. + */ +{ + int argn; + char * arg; + int quality; /* -quality parameter */ + int q_scale_factor; /* scaling percentage for -qtables */ + boolean force_baseline; + boolean simple_progressive; + char * qtablefile = NULL; /* saves -qtables filename if any */ + char * qslotsarg = NULL; /* saves -qslots parm if any */ + char * samplearg = NULL; /* saves -sample parm if any */ + char * scansarg = NULL; /* saves -scans parm if any */ + + /* Set up default JPEG parameters. */ + /* Note that default -quality level need not, and does not, + * match the default scaling for an explicit -qtables argument. + */ + quality = 75; /* default -quality value */ + q_scale_factor = 100; /* default to no scaling for -qtables */ + force_baseline = FALSE; /* by default, allow 16-bit quantizers */ + simple_progressive = FALSE; + is_targa = FALSE; + outfilename = NULL; + cinfo->err->trace_level = 0; + + /* Scan command line options, adjust parameters */ + + for (argn = 1; argn < argc; argn++) { + arg = argv[argn]; + if (*arg != '-') { + /* Not a switch, must be a file name argument */ + if (argn <= last_file_arg_seen) { + outfilename = NULL; /* -outfile applies to just one input file */ + continue; /* ignore this name if previously processed */ + } + break; /* else done parsing switches */ + } + arg++; /* advance past switch marker character */ + + if (keymatch(arg, "arithmetic", 1)) { + /* Use arithmetic coding. */ +#ifdef C_ARITH_CODING_SUPPORTED + cinfo->arith_code = TRUE; +#else + fprintf(stderr, "%s: sorry, arithmetic coding not supported\n", + progname); + exit(EXIT_FAILURE); +#endif + + } else if (keymatch(arg, "baseline", 1)) { + /* Force baseline-compatible output (8-bit quantizer values). */ + force_baseline = TRUE; + + } else if (keymatch(arg, "dct", 2)) { + /* Select DCT algorithm. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + if (keymatch(argv[argn], "int", 1)) { + cinfo->dct_method = JDCT_ISLOW; + } else if (keymatch(argv[argn], "fast", 2)) { + cinfo->dct_method = JDCT_IFAST; + } else if (keymatch(argv[argn], "float", 2)) { + cinfo->dct_method = JDCT_FLOAT; + } else + usage(); + + } else if (keymatch(arg, "debug", 1) || keymatch(arg, "verbose", 1)) { + /* Enable debug printouts. */ + /* On first -d, print version identification */ + static boolean printed_version = FALSE; + + if (! printed_version) { + fprintf(stderr, "Independent JPEG Group's CJPEG, version %s\n%s\n", + JVERSION, JCOPYRIGHT); + printed_version = TRUE; + } + cinfo->err->trace_level++; + + } else if (keymatch(arg, "grayscale", 2) || keymatch(arg, "greyscale",2)) { + /* Force a monochrome JPEG file to be generated. */ + jpeg_set_colorspace(cinfo, JCS_GRAYSCALE); + + } else if (keymatch(arg, "maxmemory", 3)) { + /* Maximum memory in Kb (or Mb with 'm'). */ + long lval; + char ch = 'x'; + + if (++argn >= argc) /* advance to next argument */ + usage(); + if (sscanf(argv[argn], "%ld%c", &lval, &ch) < 1) + usage(); + if (ch == 'm' || ch == 'M') + lval *= 1000L; + cinfo->mem->max_memory_to_use = lval * 1000L; + + } else if (keymatch(arg, "optimize", 1) || keymatch(arg, "optimise", 1)) { + /* Enable entropy parm optimization. */ +#ifdef ENTROPY_OPT_SUPPORTED + cinfo->optimize_coding = TRUE; +#else + fprintf(stderr, "%s: sorry, entropy optimization was not compiled\n", + progname); + exit(EXIT_FAILURE); +#endif + + } else if (keymatch(arg, "outfile", 4)) { + /* Set output file name. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + outfilename = argv[argn]; /* save it away for later use */ + + } else if (keymatch(arg, "progressive", 1)) { + /* Select simple progressive mode. */ +#ifdef C_PROGRESSIVE_SUPPORTED + simple_progressive = TRUE; + /* We must postpone execution until num_components is known. */ +#else + fprintf(stderr, "%s: sorry, progressive output was not compiled\n", + progname); + exit(EXIT_FAILURE); +#endif + + } else if (keymatch(arg, "quality", 1)) { + /* Quality factor (quantization table scaling factor). */ + if (++argn >= argc) /* advance to next argument */ + usage(); + if (sscanf(argv[argn], "%d", &quality) != 1) + usage(); + /* Change scale factor in case -qtables is present. */ + q_scale_factor = jpeg_quality_scaling(quality); + + } else if (keymatch(arg, "qslots", 2)) { + /* Quantization table slot numbers. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + qslotsarg = argv[argn]; + /* Must delay setting qslots until after we have processed any + * colorspace-determining switches, since jpeg_set_colorspace sets + * default quant table numbers. + */ + + } else if (keymatch(arg, "qtables", 2)) { + /* Quantization tables fetched from file. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + qtablefile = argv[argn]; + /* We postpone actually reading the file in case -quality comes later. */ + + } else if (keymatch(arg, "restart", 1)) { + /* Restart interval in MCU rows (or in MCUs with 'b'). */ + long lval; + char ch = 'x'; + + if (++argn >= argc) /* advance to next argument */ + usage(); + if (sscanf(argv[argn], "%ld%c", &lval, &ch) < 1) + usage(); + if (lval < 0 || lval > 65535L) + usage(); + if (ch == 'b' || ch == 'B') { + cinfo->restart_interval = (unsigned int) lval; + cinfo->restart_in_rows = 0; /* else prior '-restart n' overrides me */ + } else { + cinfo->restart_in_rows = (int) lval; + /* restart_interval will be computed during startup */ + } + + } else if (keymatch(arg, "sample", 2)) { + /* Set sampling factors. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + samplearg = argv[argn]; + /* Must delay setting sample factors until after we have processed any + * colorspace-determining switches, since jpeg_set_colorspace sets + * default sampling factors. + */ + + } else if (keymatch(arg, "scans", 2)) { + /* Set scan script. */ +#ifdef C_MULTISCAN_FILES_SUPPORTED + if (++argn >= argc) /* advance to next argument */ + usage(); + scansarg = argv[argn]; + /* We must postpone reading the file in case -progressive appears. */ +#else + fprintf(stderr, "%s: sorry, multi-scan output was not compiled\n", + progname); + exit(EXIT_FAILURE); +#endif + + } else if (keymatch(arg, "smooth", 2)) { + /* Set input smoothing factor. */ + int val; + + if (++argn >= argc) /* advance to next argument */ + usage(); + if (sscanf(argv[argn], "%d", &val) != 1) + usage(); + if (val < 0 || val > 100) + usage(); + cinfo->smoothing_factor = val; + + } else if (keymatch(arg, "targa", 1)) { + /* Input file is Targa format. */ + is_targa = TRUE; + + } else { + usage(); /* bogus switch */ + } + } + + /* Post-switch-scanning cleanup */ + + if (for_real) { + + /* Set quantization tables for selected quality. */ + /* Some or all may be overridden if -qtables is present. */ + jpeg_set_quality(cinfo, quality, force_baseline); + + if (qtablefile != NULL) /* process -qtables if it was present */ + if (! read_quant_tables(cinfo, qtablefile, + q_scale_factor, force_baseline)) + usage(); + + if (qslotsarg != NULL) /* process -qslots if it was present */ + if (! set_quant_slots(cinfo, qslotsarg)) + usage(); + + if (samplearg != NULL) /* process -sample if it was present */ + if (! set_sample_factors(cinfo, samplearg)) + usage(); + +#ifdef C_PROGRESSIVE_SUPPORTED + if (simple_progressive) /* process -progressive; -scans can override */ + jpeg_simple_progression(cinfo); +#endif + +#ifdef C_MULTISCAN_FILES_SUPPORTED + if (scansarg != NULL) /* process -scans if it was present */ + if (! read_scan_script(cinfo, scansarg)) + usage(); +#endif + } + + return argn; /* return index of next arg (file name) */ +} + + +/* + * The main program. + */ + +int +main (int argc, char **argv) +{ + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; +#ifdef PROGRESS_REPORT + struct cdjpeg_progress_mgr progress; +#endif + int file_index; + cjpeg_source_ptr src_mgr; + FILE * input_file; + FILE * output_file; + JDIMENSION num_scanlines; + + /* On Mac, fetch a command line. */ +#ifdef USE_CCOMMAND + argc = ccommand(&argv); +#endif + + progname = argv[0]; + if (progname == NULL || progname[0] == 0) + progname = "cjpeg"; /* in case C library doesn't provide it */ + + /* Initialize the JPEG compression object with default error handling. */ + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + /* Add some application-specific error messages (from cderror.h) */ + jerr.addon_message_table = cdjpeg_message_table; + jerr.first_addon_message = JMSG_FIRSTADDONCODE; + jerr.last_addon_message = JMSG_LASTADDONCODE; + + /* Now safe to enable signal catcher. */ +#ifdef NEED_SIGNAL_CATCHER + enable_signal_catcher((j_common_ptr) &cinfo); +#endif + + /* Initialize JPEG parameters. + * Much of this may be overridden later. + * In particular, we don't yet know the input file's color space, + * but we need to provide some value for jpeg_set_defaults() to work. + */ + + cinfo.in_color_space = JCS_RGB; /* arbitrary guess */ + jpeg_set_defaults(&cinfo); + + /* Scan command line to find file names. + * It is convenient to use just one switch-parsing routine, but the switch + * values read here are ignored; we will rescan the switches after opening + * the input file. + */ + + file_index = parse_switches(&cinfo, argc, argv, 0, FALSE); + +#ifdef TWO_FILE_COMMANDLINE + /* Must have either -outfile switch or explicit output file name */ + if (outfilename == NULL) { + if (file_index != argc-2) { + fprintf(stderr, "%s: must name one input and one output file\n", + progname); + usage(); + } + outfilename = argv[file_index+1]; + } else { + if (file_index != argc-1) { + fprintf(stderr, "%s: must name one input and one output file\n", + progname); + usage(); + } + } +#else + /* Unix style: expect zero or one file name */ + if (file_index < argc-1) { + fprintf(stderr, "%s: only one input file\n", progname); + usage(); + } +#endif /* TWO_FILE_COMMANDLINE */ + + /* Open the input file. */ + if (file_index < argc) { + if ((input_file = fopen(argv[file_index], READ_BINARY)) == NULL) { + fprintf(stderr, "%s: can't open %s\n", progname, argv[file_index]); + exit(EXIT_FAILURE); + } + } else { + /* default input file is stdin */ + input_file = read_stdin(); + } + + /* Open the output file. */ + if (outfilename != NULL) { + if ((output_file = fopen(outfilename, WRITE_BINARY)) == NULL) { + fprintf(stderr, "%s: can't open %s\n", progname, outfilename); + exit(EXIT_FAILURE); + } + } else { + /* default output file is stdout */ + output_file = write_stdout(); + } + +#ifdef PROGRESS_REPORT + start_progress_monitor((j_common_ptr) &cinfo, &progress); +#endif + + /* Figure out the input file format, and set up to read it. */ + src_mgr = select_file_type(&cinfo, input_file); + src_mgr->input_file = input_file; + + /* Read the input file header to obtain file size & colorspace. */ + (*src_mgr->start_input) (&cinfo, src_mgr); + + /* Now that we know input colorspace, fix colorspace-dependent defaults */ + jpeg_default_colorspace(&cinfo); + + /* Adjust default compression parameters by re-parsing the options */ + file_index = parse_switches(&cinfo, argc, argv, 0, TRUE); + + /* Specify data destination for compression */ + jpeg_stdio_dest(&cinfo, output_file); + + /* Start compressor */ + jpeg_start_compress(&cinfo, TRUE); + + /* Process data */ + while (cinfo.next_scanline < cinfo.image_height) { + num_scanlines = (*src_mgr->get_pixel_rows) (&cinfo, src_mgr); + (void) jpeg_write_scanlines(&cinfo, src_mgr->buffer, num_scanlines); + } + + /* Finish compression and release memory */ + (*src_mgr->finish_input) (&cinfo, src_mgr); + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + + /* Close files, if we opened them */ + if (input_file != stdin) + fclose(input_file); + if (output_file != stdout) + fclose(output_file); + +#ifdef PROGRESS_REPORT + end_progress_monitor((j_common_ptr) &cinfo); +#endif + + /* All done. */ + exit(jerr.num_warnings ? EXIT_WARNING : EXIT_SUCCESS); + return 0; /* suppress no-return-value warnings */ +} diff --git a/Engine/lib/ljpeg/extras/ckconfig.c b/Engine/lib/ljpeg/extras/ckconfig.c new file mode 100644 index 000000000..34baf795b --- /dev/null +++ b/Engine/lib/ljpeg/extras/ckconfig.c @@ -0,0 +1,402 @@ +/* + * ckconfig.c + * + * Copyright (C) 1991-1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + */ + +/* + * This program is intended to help you determine how to configure the JPEG + * software for installation on a particular system. The idea is to try to + * compile and execute this program. If your compiler fails to compile the + * program, make changes as indicated in the comments below. Once you can + * compile the program, run it, and it will produce a "jconfig.h" file for + * your system. + * + * As a general rule, each time you try to compile this program, + * pay attention only to the *first* error message you get from the compiler. + * Many C compilers will issue lots of spurious error messages once they + * have gotten confused. Go to the line indicated in the first error message, + * and read the comments preceding that line to see what to change. + * + * Almost all of the edits you may need to make to this program consist of + * changing a line that reads "#define SOME_SYMBOL" to "#undef SOME_SYMBOL", + * or vice versa. This is called defining or undefining that symbol. + */ + + +/* First we must see if your system has the include files we need. + * We start out with the assumption that your system has all the ANSI-standard + * include files. If you get any error trying to include one of these files, + * undefine the corresponding HAVE_xxx symbol. + */ + +#define HAVE_STDDEF_H /* replace 'define' by 'undef' if error here */ +#ifdef HAVE_STDDEF_H /* next line will be skipped if you undef... */ +#include +#endif + +#define HAVE_STDLIB_H /* same thing for stdlib.h */ +#ifdef HAVE_STDLIB_H +#include +#endif + +#include /* If you ain't got this, you ain't got C. */ + +/* We have to see if your string functions are defined by + * strings.h (old BSD convention) or string.h (everybody else). + * We try the non-BSD convention first; define NEED_BSD_STRINGS + * if the compiler says it can't find string.h. + */ + +#undef NEED_BSD_STRINGS + +#ifdef NEED_BSD_STRINGS +#include +#else +#include +#endif + +/* On some systems (especially older Unix machines), type size_t is + * defined only in the include file . If you get a failure + * on the size_t test below, try defining NEED_SYS_TYPES_H. + */ + +#undef NEED_SYS_TYPES_H /* start by assuming we don't need it */ +#ifdef NEED_SYS_TYPES_H +#include +#endif + + +/* Usually type size_t is defined in one of the include files we've included + * above. If not, you'll get an error on the "typedef size_t my_size_t;" line. + * In that case, first try defining NEED_SYS_TYPES_H just above. + * If that doesn't work, you'll have to search through your system library + * to figure out which include file defines "size_t". Look for a line that + * says "typedef something-or-other size_t;". Then, change the line below + * that says "#include " to instead include the file + * you found size_t in, and define NEED_SPECIAL_INCLUDE. If you can't find + * type size_t anywhere, try replacing "#include " with + * "typedef unsigned int size_t;". + */ + +#undef NEED_SPECIAL_INCLUDE /* assume we DON'T need it, for starters */ + +#ifdef NEED_SPECIAL_INCLUDE +#include +#endif + +typedef size_t my_size_t; /* The payoff: do we have size_t now? */ + + +/* The next question is whether your compiler supports ANSI-style function + * prototypes. You need to know this in order to choose between using + * makefile.ansi and using makefile.unix. + * The #define line below is set to assume you have ANSI function prototypes. + * If you get an error in this group of lines, undefine HAVE_PROTOTYPES. + */ + +#define HAVE_PROTOTYPES + +#ifdef HAVE_PROTOTYPES +int testfunction (int arg1, int * arg2); /* check prototypes */ + +struct methods_struct { /* check method-pointer declarations */ + int (*error_exit) (char *msgtext); + int (*trace_message) (char *msgtext); + int (*another_method) (void); +}; + +int testfunction (int arg1, int * arg2) /* check definitions */ +{ + return arg2[arg1]; +} + +int test2function (void) /* check void arg list */ +{ + return 0; +} +#endif + + +/* Now we want to find out if your compiler knows what "unsigned char" means. + * If you get an error on the "unsigned char un_char;" line, + * then undefine HAVE_UNSIGNED_CHAR. + */ + +#define HAVE_UNSIGNED_CHAR + +#ifdef HAVE_UNSIGNED_CHAR +unsigned char un_char; +#endif + + +/* Now we want to find out if your compiler knows what "unsigned short" means. + * If you get an error on the "unsigned short un_short;" line, + * then undefine HAVE_UNSIGNED_SHORT. + */ + +#define HAVE_UNSIGNED_SHORT + +#ifdef HAVE_UNSIGNED_SHORT +unsigned short un_short; +#endif + + +/* Now we want to find out if your compiler understands type "void". + * If you get an error anywhere in here, undefine HAVE_VOID. + */ + +#define HAVE_VOID + +#ifdef HAVE_VOID +/* Caution: a C++ compiler will insist on complete prototypes */ +typedef void * void_ptr; /* check void * */ +#ifdef HAVE_PROTOTYPES /* check ptr to function returning void */ +typedef void (*void_func) (int a, int b); +#else +typedef void (*void_func) (); +#endif + +#ifdef HAVE_PROTOTYPES /* check void function result */ +void test3function (void_ptr arg1, void_func arg2) +#else +void test3function (arg1, arg2) + void_ptr arg1; + void_func arg2; +#endif +{ + char * locptr = (char *) arg1; /* check casting to and from void * */ + arg1 = (void *) locptr; + (*arg2) (1, 2); /* check call of fcn returning void */ +} +#endif + + +/* Now we want to find out if your compiler knows what "const" means. + * If you get an error here, undefine HAVE_CONST. + */ + +#define HAVE_CONST + +#ifdef HAVE_CONST +static const int carray[3] = {1, 2, 3}; + +#ifdef HAVE_PROTOTYPES +int test4function (const int arg1) +#else +int test4function (arg1) + const int arg1; +#endif +{ + return carray[arg1]; +} +#endif + + +/* If you get an error or warning about this structure definition, + * define INCOMPLETE_TYPES_BROKEN. + */ + +#undef INCOMPLETE_TYPES_BROKEN + +#ifndef INCOMPLETE_TYPES_BROKEN +typedef struct undefined_structure * undef_struct_ptr; +#endif + + +/* If you get an error about duplicate names, + * define NEED_SHORT_EXTERNAL_NAMES. + */ + +#undef NEED_SHORT_EXTERNAL_NAMES + +#ifndef NEED_SHORT_EXTERNAL_NAMES + +int possibly_duplicate_function () +{ + return 0; +} + +int possibly_dupli_function () +{ + return 1; +} + +#endif + + + +/************************************************************************ + * OK, that's it. You should not have to change anything beyond this + * point in order to compile and execute this program. (You might get + * some warnings, but you can ignore them.) + * When you run the program, it will make a couple more tests that it + * can do automatically, and then it will create jconfig.h and print out + * any additional suggestions it has. + ************************************************************************ + */ + + +#ifdef HAVE_PROTOTYPES +int is_char_signed (int arg) +#else +int is_char_signed (arg) + int arg; +#endif +{ + if (arg == 189) { /* expected result for unsigned char */ + return 0; /* type char is unsigned */ + } + else if (arg != -67) { /* expected result for signed char */ + printf("Hmm, it seems 'char' is not eight bits wide on your machine.\n"); + printf("I fear the JPEG software will not work at all.\n\n"); + } + return 1; /* assume char is signed otherwise */ +} + + +#ifdef HAVE_PROTOTYPES +int is_shifting_signed (long arg) +#else +int is_shifting_signed (arg) + long arg; +#endif +/* See whether right-shift on a long is signed or not. */ +{ + long res = arg >> 4; + + if (res == -0x7F7E80CL) { /* expected result for signed shift */ + return 1; /* right shift is signed */ + } + /* see if unsigned-shift hack will fix it. */ + /* we can't just test exact value since it depends on width of long... */ + res |= (~0L) << (32-4); + if (res == -0x7F7E80CL) { /* expected result now? */ + return 0; /* right shift is unsigned */ + } + printf("Right shift isn't acting as I expect it to.\n"); + printf("I fear the JPEG software will not work at all.\n\n"); + return 0; /* try it with unsigned anyway */ +} + + +#ifdef HAVE_PROTOTYPES +int main (int argc, char ** argv) +#else +int main (argc, argv) + int argc; + char ** argv; +#endif +{ + char signed_char_check = (char) (-67); + FILE *outfile; + + /* Attempt to write jconfig.h */ + if ((outfile = fopen("jconfig.h", "w")) == NULL) { + printf("Failed to write jconfig.h\n"); + return 1; + } + + /* Write out all the info */ + fprintf(outfile, "/* jconfig.h --- generated by ckconfig.c */\n"); + fprintf(outfile, "/* see jconfig.doc for explanations */\n\n"); +#ifdef HAVE_PROTOTYPES + fprintf(outfile, "#define HAVE_PROTOTYPES\n"); +#else + fprintf(outfile, "#undef HAVE_PROTOTYPES\n"); +#endif +#ifdef HAVE_UNSIGNED_CHAR + fprintf(outfile, "#define HAVE_UNSIGNED_CHAR\n"); +#else + fprintf(outfile, "#undef HAVE_UNSIGNED_CHAR\n"); +#endif +#ifdef HAVE_UNSIGNED_SHORT + fprintf(outfile, "#define HAVE_UNSIGNED_SHORT\n"); +#else + fprintf(outfile, "#undef HAVE_UNSIGNED_SHORT\n"); +#endif +#ifdef HAVE_VOID + fprintf(outfile, "/* #define void char */\n"); +#else + fprintf(outfile, "#define void char\n"); +#endif +#ifdef HAVE_CONST + fprintf(outfile, "/* #define const */\n"); +#else + fprintf(outfile, "#define const\n"); +#endif + if (is_char_signed((int) signed_char_check)) + fprintf(outfile, "#undef CHAR_IS_UNSIGNED\n"); + else + fprintf(outfile, "#define CHAR_IS_UNSIGNED\n"); +#ifdef HAVE_STDDEF_H + fprintf(outfile, "#define HAVE_STDDEF_H\n"); +#else + fprintf(outfile, "#undef HAVE_STDDEF_H\n"); +#endif +#ifdef HAVE_STDLIB_H + fprintf(outfile, "#define HAVE_STDLIB_H\n"); +#else + fprintf(outfile, "#undef HAVE_STDLIB_H\n"); +#endif +#ifdef NEED_BSD_STRINGS + fprintf(outfile, "#define NEED_BSD_STRINGS\n"); +#else + fprintf(outfile, "#undef NEED_BSD_STRINGS\n"); +#endif +#ifdef NEED_SYS_TYPES_H + fprintf(outfile, "#define NEED_SYS_TYPES_H\n"); +#else + fprintf(outfile, "#undef NEED_SYS_TYPES_H\n"); +#endif + fprintf(outfile, "#undef NEED_FAR_POINTERS\n"); +#ifdef NEED_SHORT_EXTERNAL_NAMES + fprintf(outfile, "#define NEED_SHORT_EXTERNAL_NAMES\n"); +#else + fprintf(outfile, "#undef NEED_SHORT_EXTERNAL_NAMES\n"); +#endif +#ifdef INCOMPLETE_TYPES_BROKEN + fprintf(outfile, "#define INCOMPLETE_TYPES_BROKEN\n"); +#else + fprintf(outfile, "#undef INCOMPLETE_TYPES_BROKEN\n"); +#endif + fprintf(outfile, "\n#ifdef JPEG_INTERNALS\n\n"); + if (is_shifting_signed(-0x7F7E80B1L)) + fprintf(outfile, "#undef RIGHT_SHIFT_IS_UNSIGNED\n"); + else + fprintf(outfile, "#define RIGHT_SHIFT_IS_UNSIGNED\n"); + fprintf(outfile, "\n#endif /* JPEG_INTERNALS */\n"); + fprintf(outfile, "\n#ifdef JPEG_CJPEG_DJPEG\n\n"); + fprintf(outfile, "#define BMP_SUPPORTED /* BMP image file format */\n"); + fprintf(outfile, "#define GIF_SUPPORTED /* GIF image file format */\n"); + fprintf(outfile, "#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */\n"); + fprintf(outfile, "#undef RLE_SUPPORTED /* Utah RLE image file format */\n"); + fprintf(outfile, "#define TARGA_SUPPORTED /* Targa image file format */\n\n"); + fprintf(outfile, "#undef TWO_FILE_COMMANDLINE /* You may need this on non-Unix systems */\n"); + fprintf(outfile, "#undef NEED_SIGNAL_CATCHER /* Define this if you use jmemname.c */\n"); + fprintf(outfile, "#undef DONT_USE_B_MODE\n"); + fprintf(outfile, "/* #define PROGRESS_REPORT */ /* optional */\n"); + fprintf(outfile, "\n#endif /* JPEG_CJPEG_DJPEG */\n"); + + /* Close the jconfig.h file */ + fclose(outfile); + + /* User report */ + printf("Configuration check for Independent JPEG Group's software done.\n"); + printf("\nI have written the jconfig.h file for you.\n\n"); +#ifdef HAVE_PROTOTYPES + printf("You should use makefile.ansi as the starting point for your Makefile.\n"); +#else + printf("You should use makefile.unix as the starting point for your Makefile.\n"); +#endif + +#ifdef NEED_SPECIAL_INCLUDE + printf("\nYou'll need to change jconfig.h to include the system include file\n"); + printf("that you found type size_t in, or add a direct definition of type\n"); + printf("size_t if that's what you used. Just add it to the end.\n"); +#endif + + return 0; +} diff --git a/Engine/lib/ljpeg/extras/coderules.doc b/Engine/lib/ljpeg/extras/coderules.doc new file mode 100644 index 000000000..0ab5d9bd3 --- /dev/null +++ b/Engine/lib/ljpeg/extras/coderules.doc @@ -0,0 +1,118 @@ +IJG JPEG LIBRARY: CODING RULES + +Copyright (C) 1991-1996, Thomas G. Lane. +This file is part of the Independent JPEG Group's software. +For conditions of distribution and use, see the accompanying README file. + + +Since numerous people will be contributing code and bug fixes, it's important +to establish a common coding style. The goal of using similar coding styles +is much more important than the details of just what that style is. + +In general we follow the recommendations of "Recommended C Style and Coding +Standards" revision 6.1 (Cannon et al. as modified by Spencer, Keppel and +Brader). This document is available in the IJG FTP archive (see +jpeg/doc/cstyle.ms.tbl.Z, or cstyle.txt.Z for those without nroff/tbl). + +Block comments should be laid out thusly: + +/* + * Block comments in this style. + */ + +We indent statements in K&R style, e.g., + if (test) { + then-part; + } else { + else-part; + } +with two spaces per indentation level. (This indentation convention is +handled automatically by GNU Emacs and many other text editors.) + +Multi-word names should be written in lower case with underscores, e.g., +multi_word_name (not multiWordName). Preprocessor symbols and enum constants +are similar but upper case (MULTI_WORD_NAME). Names should be unique within +the first fifteen characters. (On some older systems, global names must be +unique within six characters. We accommodate this without cluttering the +source code by using macros to substitute shorter names.) + +We use function prototypes everywhere; we rely on automatic source code +transformation to feed prototype-less C compilers. Transformation is done +by the simple and portable tool 'ansi2knr.c' (courtesy of Ghostscript). +ansi2knr is not very bright, so it imposes a format requirement on function +declarations: the function name MUST BEGIN IN COLUMN 1. Thus all functions +should be written in the following style: + +LOCAL(int *) +function_name (int a, char *b) +{ + code... +} + +Note that each function definition must begin with GLOBAL(type), LOCAL(type), +or METHODDEF(type). These macros expand to "static type" or just "type" as +appropriate. They provide a readable indication of the routine's usage and +can readily be changed for special needs. (For instance, special linkage +keywords can be inserted for use in Windows DLLs.) + +ansi2knr does not transform method declarations (function pointers in +structs). We handle these with a macro JMETHOD, defined as + #ifdef HAVE_PROTOTYPES + #define JMETHOD(type,methodname,arglist) type (*methodname) arglist + #else + #define JMETHOD(type,methodname,arglist) type (*methodname) () + #endif +which is used like this: + struct function_pointers { + JMETHOD(void, init_entropy_encoder, (int somearg, jparms *jp)); + JMETHOD(void, term_entropy_encoder, (void)); + }; +Note the set of parentheses surrounding the parameter list. + +A similar solution is used for forward and external function declarations +(see the EXTERN and JPP macros). + +If the code is to work on non-ANSI compilers, we cannot rely on a prototype +declaration to coerce actual parameters into the right types. Therefore, use +explicit casts on actual parameters whenever the actual parameter type is not +identical to the formal parameter. Beware of implicit conversions to "int". + +It seems there are some non-ANSI compilers in which the sizeof() operator +is defined to return int, yet size_t is defined as long. Needless to say, +this is brain-damaged. Always use the SIZEOF() macro in place of sizeof(), +so that the result is guaranteed to be of type size_t. + + +The JPEG library is intended to be used within larger programs. Furthermore, +we want it to be reentrant so that it can be used by applications that process +multiple images concurrently. The following rules support these requirements: + +1. Avoid direct use of file I/O, "malloc", error report printouts, etc; +pass these through the common routines provided. + +2. Minimize global namespace pollution. Functions should be declared static +wherever possible. (Note that our method-based calling conventions help this +a lot: in many modules only the initialization function will ever need to be +called directly, so only that function need be externally visible.) All +global function names should begin with "jpeg_", and should have an +abbreviated name (unique in the first six characters) substituted by macro +when NEED_SHORT_EXTERNAL_NAMES is set. + +3. Don't use global variables; anything that must be used in another module +should be in the common data structures. + +4. Don't use static variables except for read-only constant tables. Variables +that should be private to a module can be placed into private structures (see +the system architecture document, structure.doc). + +5. Source file names should begin with "j" for files that are part of the +library proper; source files that are not part of the library, such as cjpeg.c +and djpeg.c, do not begin with "j". Keep source file names to eight +characters (plus ".c" or ".h", etc) to make life easy for MS-DOSers. Keep +compression and decompression code in separate source files --- some +applications may want only one half of the library. + +Note: these rules (particularly #4) are not followed religiously in the +modules that are used in cjpeg/djpeg but are not part of the JPEG library +proper. Those modules are not really intended to be used in other +applications. diff --git a/Engine/lib/ljpeg/extras/config.guess b/Engine/lib/ljpeg/extras/config.guess new file mode 100644 index 000000000..413ed41c0 --- /dev/null +++ b/Engine/lib/ljpeg/extras/config.guess @@ -0,0 +1,883 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 93, 94, 95, 96, 1997 Free Software Foundation, Inc. +# +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Written by Per Bothner . +# The master version of this file is at the FSF in /home/gd/gnu/lib. +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# The plan is that this can be called by configure scripts if you +# don't specify an explicit system type (host/target name). +# +# Only a few systems have been added to this list; please add others +# (but try to keep the structure clean). +# + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 8/24/94.) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +trap 'rm -f dummy.c dummy.o dummy; exit 1' 1 2 15 + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + alpha:OSF1:*:*) + if test $UNAME_RELEASE = "V4.0"; then + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + fi + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + cat <dummy.s + .globl main + .ent main +main: + .frame \$30,0,\$26,0 + .prologue 0 + .long 0x47e03d80 # implver $0 + lda \$2,259 + .long 0x47e20c21 # amask $2,$1 + srl \$1,8,\$2 + sll \$2,2,\$2 + sll \$0,3,\$0 + addl \$1,\$0,\$0 + addl \$2,\$0,\$0 + ret \$31,(\$26),1 + .end main +EOF + ${CC-cc} dummy.s -o dummy 2>/dev/null + if test "$?" = 0 ; then + ./dummy + case "$?" in + 7) + UNAME_MACHINE="alpha" + ;; + 15) + UNAME_MACHINE="alphaev5" + ;; + 14) + UNAME_MACHINE="alphaev56" + ;; + 10) + UNAME_MACHINE="alphapca56" + ;; + 16) + UNAME_MACHINE="alphaev6" + ;; + esac + fi + rm -f dummy.s dummy + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr [[A-Z]] [[a-z]]` + exit 0 ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit 0 ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-cbm-sysv4 + exit 0;; + amiga:NetBSD:*:*) + echo m68k-cbm-netbsd${UNAME_RELEASE} + exit 0 ;; + amiga:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + arc64:OpenBSD:*:*) + echo mips64el-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + arc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + hkmips:OpenBSD:*:*) + echo mips-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + pmax:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sgi:OpenBSD:*:*) + echo mips-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + wgrisc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit 0;; + arm32:NetBSD:*:*) + echo arm-unknown-netbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + exit 0 ;; + SR2?01:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit 0;; + Pyramid*:OSx*:*:*|MIS*:OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit 0 ;; + NILE:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit 0 ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + i86pc:SunOS:5.*:*) + echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit 0 ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit 0 ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(head -1 /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit 0 ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit 0 ;; + atari*:NetBSD:*:*) + echo m68k-atari-netbsd${UNAME_RELEASE} + exit 0 ;; + atari*:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sun3*:NetBSD:*:*) + echo m68k-sun-netbsd${UNAME_RELEASE} + exit 0 ;; + sun3*:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mac68k:NetBSD:*:*) + echo m68k-apple-netbsd${UNAME_RELEASE} + exit 0 ;; + mac68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme88k:OpenBSD:*:*) + echo m88k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit 0 ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit 0 ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + 2020:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit 0 ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + sed 's/^ //' << EOF >dummy.c + int main (argc, argv) int argc; char **argv; { + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + ${CC-cc} dummy.c -o dummy \ + && ./dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \ + && rm dummy.c dummy && exit 0 + rm -f dummy.c dummy + echo mips-mips-riscos${UNAME_RELEASE} + exit 0 ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit 0 ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit 0 ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit 0 ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit 0 ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 -o $UNAME_PROCESSOR = mc88110 ] ; then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx \ + -o ${TARGET_BINARY_INTERFACE}x = x ] ; then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else echo i586-dg-dgux${UNAME_RELEASE} + fi + exit 0 ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit 0 ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit 0 ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit 0 ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit 0 ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit 0 ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i?86:AIX:*:*) + echo i386-ibm-aix + exit 0 ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + sed 's/^ //' << EOF >dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + ${CC-cc} dummy.c -o dummy && ./dummy && rm dummy.c dummy && exit 0 + rm -f dummy.c dummy + echo rs6000-ibm-aix3.2.5 + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit 0 ;; + *:AIX:*:4) + if /usr/sbin/lsattr -EHl proc0 | grep POWER >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=4.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit 0 ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit 0 ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC NetBSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit 0 ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit 0 ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit 0 ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit 0 ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit 0 ;; + 9000/[3478]??:HP-UX:*:*) + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/7?? | 9000/8?[1679] ) HP_ARCH=hppa1.1 ;; + 9000/8?? ) HP_ARCH=hppa1.0 ;; + esac + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit 0 ;; + 3050*:HI-UX:*:*) + sed 's/^ //' << EOF >dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + ${CC-cc} dummy.c -o dummy && ./dummy && rm dummy.c dummy && exit 0 + rm -f dummy.c dummy + echo unknown-hitachi-hiuxwe2 + exit 0 ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit 0 ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit 0 ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit 0 ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit 0 ;; + i?86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit 0 ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit 0 ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit 0 ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit 0 ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit 0 ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit 0 ;; + CRAY*X-MP:*:*:*) + echo xmp-cray-unicos + exit 0 ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} + exit 0 ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ + exit 0 ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} + exit 0 ;; + CRAY-2:*:*:*) + echo cray2-cray-unicos + exit 0 ;; + F300:UNIX_System_V:*:*) + FUJITSU_SYS=`uname -p | tr [A-Z] [a-z] | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "f300-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit 0 ;; + F301:UNIX_System_V:*:*) + echo f301-fujitsu-uxpv`echo $UNAME_RELEASE | sed 's/ .*//'` + exit 0 ;; + hp3[0-9][05]:NetBSD:*:*) + echo m68k-hp-netbsd${UNAME_RELEASE} + exit 0 ;; + hp300:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + i?86:BSD/386:*:* | *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit 0 ;; + *:FreeBSD:*:*) + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit 0 ;; + *:NetBSD:*:*) + echo ${UNAME_MACHINE}-unknown-netbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + exit 0 ;; + *:OpenBSD:*:*) + echo ${UNAME_MACHINE}-unknown-openbsd`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + exit 0 ;; + i*:CYGWIN*:*) + echo i386-pc-cygwin32 + exit 0 ;; + i*:MINGW*:*) + echo i386-pc-mingw32 + exit 0 ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin32 + exit 0 ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + *:GNU:*:*) + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit 0 ;; + *:Linux:*:*) + # The BFD linker knows what the default object file format is, so + # first see if it will tell us. + ld_help_string=`ld --help 2>&1` + ld_supported_emulations=`echo $ld_help_string \ + | sed -ne '/supported emulations:/!d + s/[ ][ ]*/ /g + s/.*supported emulations: *// + s/ .*// + p'` + case "$ld_supported_emulations" in + i?86linux) echo "${UNAME_MACHINE}-pc-linux-gnuaout" ; exit 0 ;; + i?86coff) echo "${UNAME_MACHINE}-pc-linux-gnucoff" ; exit 0 ;; + sparclinux) echo "${UNAME_MACHINE}-unknown-linux-gnuaout" ; exit 0 ;; + m68klinux) echo "${UNAME_MACHINE}-unknown-linux-gnuaout" ; exit 0 ;; + elf32ppc) echo "powerpc-unknown-linux-gnu" ; exit 0 ;; + esac + + if test "${UNAME_MACHINE}" = "alpha" ; then + sed 's/^ //' <dummy.s + .globl main + .ent main + main: + .frame \$30,0,\$26,0 + .prologue 0 + .long 0x47e03d80 # implver $0 + lda \$2,259 + .long 0x47e20c21 # amask $2,$1 + srl \$1,8,\$2 + sll \$2,2,\$2 + sll \$0,3,\$0 + addl \$1,\$0,\$0 + addl \$2,\$0,\$0 + ret \$31,(\$26),1 + .end main +EOF + LIBC="" + ${CC-cc} dummy.s -o dummy 2>/dev/null + if test "$?" = 0 ; then + ./dummy + case "$?" in + 7) + UNAME_MACHINE="alpha" + ;; + 15) + UNAME_MACHINE="alphaev5" + ;; + 14) + UNAME_MACHINE="alphaev56" + ;; + 10) + UNAME_MACHINE="alphapca56" + ;; + 16) + UNAME_MACHINE="alphaev6" + ;; + esac + + objdump --private-headers dummy | \ + grep ld.so.1 > /dev/null + if test "$?" = 0 ; then + LIBC="libc1" + fi + fi + rm -f dummy.s dummy + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} ; exit 0 + elif test "${UNAME_MACHINE}" = "mips" ; then + cat >dummy.c </dev/null && ./dummy "${UNAME_MACHINE}" && rm dummy.c dummy && exit 0 + rm -f dummy.c dummy + else + # Either a pre-BFD a.out linker (linux-gnuoldld) + # or one that does not give us useful --help. + # GCC wants to distinguish between linux-gnuoldld and linux-gnuaout. + # If ld does not provide *any* "supported emulations:" + # that means it is gnuoldld. + echo "$ld_help_string" | grep >/dev/null 2>&1 "supported emulations:" + test $? != 0 && echo "${UNAME_MACHINE}-pc-linux-gnuoldld" && exit 0 + + case "${UNAME_MACHINE}" in + i?86) + VENDOR=pc; + ;; + *) + VENDOR=unknown; + ;; + esac + # Determine whether the default compiler is a.out or elf + cat >dummy.c < +main(argc, argv) + int argc; + char *argv[]; +{ +#ifdef __ELF__ +# ifdef __GLIBC__ +# if __GLIBC__ >= 2 + printf ("%s-${VENDOR}-linux-gnu\n", argv[1]); +# else + printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]); +# endif +# else + printf ("%s-${VENDOR}-linux-gnulibc1\n", argv[1]); +# endif +#else + printf ("%s-${VENDOR}-linux-gnuaout\n", argv[1]); +#endif + return 0; +} +EOF + ${CC-cc} dummy.c -o dummy 2>/dev/null && ./dummy "${UNAME_MACHINE}" && rm dummy.c dummy && exit 0 + rm -f dummy.c dummy + fi ;; +# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. earlier versions +# are messed up and put the nodename in both sysname and nodename. + i?86:DYNIX/ptx:4*:*) + echo i386-sequent-sysv4 + exit 0 ;; + i?86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit 0 ;; + i?86:*:4.*:* | i?86:SYSTEM_V:4.*:*) + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_RELEASE} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_RELEASE} + fi + exit 0 ;; + i?86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|egrep Release|sed -e 's/.*= //')` + (/bin/uname -X|egrep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|egrep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit 0 ;; + pc:*:*:*) + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i386. + echo i386-pc-msdosdjgpp + exit 0 ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit 0 ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit 0 ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit 0 ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit 0 ;; + M68*:*:R3V[567]*:*) + test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;; + 3[34]??:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 4850:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4.3${OS_REL} && exit 0 + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4 && exit 0 ;; + m68*:LynxOS:2.*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit 0 ;; + i?86:LynxOS:2.*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + rs6000:LynxOS:2.*:* | PowerPC:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit 0 ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit 0 ;; + PENTIUM:CPunix:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit 0 ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit 0 ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit 0 ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit 0 ;; + news*:NEWS-OS:*:6*) + echo mips-sony-newsos6 + exit 0 ;; + R3000:*System_V*:*:* | R4000:UNIX_SYSV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit 0 ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +cat >dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +#if !defined (ultrix) + printf ("vax-dec-bsd\n"); exit (0); +#else + printf ("vax-dec-ultrix\n"); exit (0); +#endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +${CC-cc} dummy.c -o dummy 2>/dev/null && ./dummy && rm dummy.c dummy && exit 0 +rm -f dummy.c dummy + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit 0 ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + c34*) + echo c34-convex-bsd + exit 0 ;; + c38*) + echo c38-convex-bsd + exit 0 ;; + c4*) + echo c4-convex-bsd + exit 0 ;; + esac +fi + +#echo '(Unable to guess system type)' 1>&2 + +exit 1 diff --git a/Engine/lib/ljpeg/extras/config.sub b/Engine/lib/ljpeg/extras/config.sub new file mode 100644 index 000000000..213a6d47d --- /dev/null +++ b/Engine/lib/ljpeg/extras/config.sub @@ -0,0 +1,954 @@ +#! /bin/sh +# Configuration validation subroutine script, version 1.1. +# Copyright (C) 1991, 92, 93, 94, 95, 96, 1997 Free Software Foundation, Inc. +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +if [ x$1 = x ] +then + echo Configuration name missing. 1>&2 + echo "Usage: $0 CPU-MFR-OPSYS" 1>&2 + echo "or $0 ALIAS" 1>&2 + echo where ALIAS is a recognized configuration type. 1>&2 + exit 1 +fi + +# First pass through any local machine types. +case $1 in + *local*) + echo $1 + exit 0 + ;; + *) + ;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + linux-gnu*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple) + os= + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco5) + os=sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + tahoe | i860 | m32r | m68k | m68000 | m88k | ns32k | arc | arm \ + | arme[lb] | pyramid | mn10200 | mn10300 \ + | tron | a29k | 580 | i960 | h8300 | hppa | hppa1.0 | hppa1.1 \ + | alpha | alphaev5 | alphaev56 | we32k | ns16k | clipper \ + | i370 | sh | powerpc | powerpcle | 1750a | dsp16xx | pdp11 \ + | mips64 | mipsel | mips64el | mips64orion | mips64orionel \ + | mipstx39 | mipstx39el \ + | sparc | sparclet | sparclite | sparc64 | v850) + basic_machine=$basic_machine-unknown + ;; + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i[3456]86) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + vax-* | tahoe-* | i[3456]86-* | i860-* | m32r-* | m68k-* | m68000-* \ + | m88k-* | sparc-* | ns32k-* | fx80-* | arc-* | arm-* | c[123]* \ + | mips-* | pyramid-* | tron-* | a29k-* | romp-* | rs6000-* \ + | power-* | none-* | 580-* | cray2-* | h8300-* | i960-* \ + | xmp-* | ymp-* | hppa-* | hppa1.0-* | hppa1.1-* \ + | alpha-* | alphaev5-* | alphaev56-* | we32k-* | cydra-* \ + | ns16k-* | pn-* | np1-* | xps100-* | clipper-* | orion-* \ + | sparclite-* | pdp11-* | sh-* | powerpc-* | powerpcle-* \ + | sparc64-* | mips64-* | mipsel-* \ + | mips64el-* | mips64orion-* | mips64orionel-* \ + | mipstx39-* | mipstx39el-* \ + | f301-*) + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-cbm + ;; + amigaos | amigados) + basic_machine=m68k-cbm + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-cbm + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | ymp) + basic_machine=ymp-cray + os=-unicos + ;; + cray2) + basic_machine=cray2-cray + os=-unicos + ;; + [ctj]90-cray) + basic_machine=c90-cray + os=-unicos + ;; + crds | unos) + basic_machine=m68k-crds + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k7[0-9][0-9] | hp7[0-9][0-9] | hp9k8[0-9]7 | hp8[0-9]7) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + os=-mvs + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i[3456]86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i[3456]86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i[3456]86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i[3456]86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + miniframe) + basic_machine=m68000-convergent + ;; + mipsel*-linux*) + basic_machine=mipsel-unknown + os=-linux-gnu + ;; + mips*-linux*) + basic_machine=mips-unknown + os=-linux-gnu + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + np1) + basic_machine=np1-gould + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pentium | p5) + basic_machine=i586-intel + ;; + pentiumpro | p6) + basic_machine=i686-intel + ;; + pentium-* | p5-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + k5) + # We don't have specific support for AMD's K5 yet, so just call it a Pentium + basic_machine=i586-amd + ;; + nexen) + # We don't have specific support for Nexgen yet, so just call it a Pentium + basic_machine=i586-nexgen + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=rs6000-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + xmp) + basic_machine=xmp-cray + os=-unicos + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + mips) + if [ x$os = x-linux-gnu ]; then + basic_machine=mips-unknown + else + basic_machine=mips-mips + fi + ;; + romp) + basic_machine=romp-ibm + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sparc) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \ + | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -cygwin32* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux-gnu* | -uxpv*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -ctix* | -uts*) + os=-sysv + ;; + -ns2 ) + os=-nextstep2 + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -xenix) + os=-xenix + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + *-acorn) + os=-riscix1.2 + ;; + arm*-semi) + os=-aout + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-ibm) + os=-aix + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f301-fujitsu) + os=-uxpv + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -aix*) + vendor=ibm + ;; + -hpux*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -vxsim* | -vxworks*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os diff --git a/Engine/lib/ljpeg/extras/configure b/Engine/lib/ljpeg/extras/configure new file mode 100644 index 000000000..35c9db5ca --- /dev/null +++ b/Engine/lib/ljpeg/extras/configure @@ -0,0 +1,2011 @@ +#! /bin/sh + +# Guess values for system-dependent variables and create Makefiles. +# Generated automatically using autoconf version 2.12 +# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Defaults: +ac_help= +ac_default_prefix=/usr/local +# Any additions from configure.in: +ac_help="$ac_help + --enable-shared build shared library using GNU libtool" +ac_help="$ac_help + --enable-static build static library using GNU libtool" +ac_help="$ac_help + --enable-maxmem[=N] enable use of temp files, set max mem usage to N MB" +ac_help="$ac_help +" + +# Initialize some variables set by options. +# The variables have the same names as the options, with +# dashes changed to underlines. +build=NONE +cache_file=./config.cache +exec_prefix=NONE +host=NONE +no_create= +nonopt=NONE +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +target=NONE +verbose= +x_includes=NONE +x_libraries=NONE +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +# Initialize some other variables. +subdirs= +MFLAGS= MAKEFLAGS= +# Maximum number of lines to put in a shell here document. +ac_max_here_lines=12 + +ac_prev= +for ac_option +do + + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + case "$ac_option" in + -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) ac_optarg= ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case "$ac_option" in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir="$ac_optarg" ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build="$ac_optarg" ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file="$ac_optarg" ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir="$ac_optarg" ;; + + -disable-* | --disable-*) + ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + eval "enable_${ac_feature}=no" ;; + + -enable-* | --enable-*) + ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "enable_${ac_feature}='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix="$ac_optarg" ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he) + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat << EOF +Usage: configure [options] [host] +Options: [defaults in brackets after descriptions] +Configuration: + --cache-file=FILE cache test results in FILE + --help print this message + --no-create do not create output files + --quiet, --silent do not print \`checking...' messages + --version print the version of autoconf that created configure +Directory and file names: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [same as prefix] + --bindir=DIR user executables in DIR [EPREFIX/bin] + --sbindir=DIR system admin executables in DIR [EPREFIX/sbin] + --libexecdir=DIR program executables in DIR [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data in DIR + [PREFIX/share] + --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data in DIR + [PREFIX/com] + --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var] + --libdir=DIR object code libraries in DIR [EPREFIX/lib] + --includedir=DIR C header files in DIR [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include] + --infodir=DIR info documentation in DIR [PREFIX/info] + --mandir=DIR man documentation in DIR [PREFIX/man] + --srcdir=DIR find the sources in DIR [configure dir or ..] + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM + run sed PROGRAM on installed program names +EOF + cat << EOF +Host type: + --build=BUILD configure for building on BUILD [BUILD=HOST] + --host=HOST configure for HOST [guessed] + --target=TARGET configure for TARGET [TARGET=HOST] +Features and packages: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --x-includes=DIR X include files are in DIR + --x-libraries=DIR X library files are in DIR +EOF + if test -n "$ac_help"; then + echo "--enable and --with options recognized:$ac_help" + fi + exit 0 ;; + + -host | --host | --hos | --ho) + ac_prev=host ;; + -host=* | --host=* | --hos=* | --ho=*) + host="$ac_optarg" ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir="$ac_optarg" ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir="$ac_optarg" ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir="$ac_optarg" ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir="$ac_optarg" ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir="$ac_optarg" ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir="$ac_optarg" ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir="$ac_optarg" ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix="$ac_optarg" ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix="$ac_optarg" ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix="$ac_optarg" ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name="$ac_optarg" ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir="$ac_optarg" ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir="$ac_optarg" ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site="$ac_optarg" ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir="$ac_optarg" ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir="$ac_optarg" ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target="$ac_optarg" ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers) + echo "configure generated by autoconf version 2.12" + exit 0 ;; + + -with-* | --with-*) + ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "with_${ac_package}='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`echo $ac_option|sed -e 's/-*without-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + eval "with_${ac_package}=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes="$ac_optarg" ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries="$ac_optarg" ;; + + -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } + ;; + + *=*) + varname=`echo "$ac_option"|sed -e 's/=.*//'` + # Reject names that aren't valid shell variable names. + if test -n "`echo $varname| sed 's/[a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $varname: invalid shell variable name" 1>&2; exit 1; } + fi + val="`echo "$ac_option"|sed 's/[^=]*=//'`" + test -n "$verbose" && echo " setting shell variable $varname to $val" + eval "$varname='$val'" + eval "export $varname" ;; + + *) + if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then + echo "configure: warning: $ac_option: invalid host type" 1>&2 + fi + if test "x$nonopt" != xNONE; then + { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } + fi + nonopt="$ac_option" + ;; + + esac +done + +if test -n "$ac_prev"; then + { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } +fi + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +# File descriptor usage: +# 0 standard input +# 1 file creation +# 2 errors and warnings +# 3 some systems may open it to /dev/tty +# 4 used on the Kubota Titan +# 6 checking for... messages and results +# 5 compiler messages saved in config.log +if test "$silent" = yes; then + exec 6>/dev/null +else + exec 6>&1 +fi +exec 5>./config.log + +echo "\ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. +" 1>&5 + +# Strip out --no-create and --no-recursion so they do not pile up. +# Also quote any args containing shell metacharacters. +ac_configure_args= +for ac_arg +do + case "$ac_arg" in + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) ;; + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) + ac_configure_args="$ac_configure_args '$ac_arg'" ;; + *) ac_configure_args="$ac_configure_args $ac_arg" ;; + esac +done + +# NLS nuisances. +# Only set these to C if already set. These must not be set unconditionally +# because not all systems understand e.g. LANG=C (notably SCO). +# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'! +# Non-C LC_CTYPE values break the ctype check. +if test "${LANG+set}" = set; then LANG=C; export LANG; fi +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi +if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo > confdefs.h + +# A filename unique to this package, relative to the directory that +# configure is in, which we can look for to find out if srcdir is correct. +ac_unique_file=jcmaster.c + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } + else + { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } + fi +fi +srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` + +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + echo "loading site script $ac_site_file" + . "$ac_site_file" + fi +done + + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then + # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then + ac_n= ac_c=' +' ac_t=' ' + else + ac_n=-n ac_c= ac_t= + fi +else + ac_n= ac_c='\c' ac_t= +fi + + + +# Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:538: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="gcc" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:567: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + ac_prog_rejected=no + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + break + fi + done + IFS="$ac_save_ifs" +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# -gt 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + set dummy "$ac_dir/$ac_word" "$@" + shift + ac_cv_prog_CC="$@" + fi +fi +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } +fi + +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:615: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + ac_cv_prog_cc_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cc_cross=no + else + ac_cv_prog_cc_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cc_works=no +fi +rm -fr conftest* + +echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 +if test $ac_cv_prog_cc_works = no; then + { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:649: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 +cross_compiling=$ac_cv_prog_cc_cross + +echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 +echo "configure:654: checking whether we are using GNU C" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gcc=yes +else + ac_cv_prog_gcc=no +fi +fi + +echo "$ac_t""$ac_cv_prog_gcc" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + GCC=yes + test "${CFLAGS+set}" = set || CFLAGS="-O2" +else + GCC= + test "${CFLAGS+set}" = set || CFLAGS="-O" +fi + +echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 +echo "configure:681: checking how to run the C preprocessor" >&5 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then +if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + # This must be in double quotes, not single quotes, because CPP may get + # substituted into the Makefile and "${CC-cc}" will confuse make. + CPP="${CC-cc} -E" + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:702: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -E -traditional-cpp" + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:719: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP=/lib/cpp +fi +rm -f conftest* +fi +rm -f conftest* + ac_cv_prog_CPP="$CPP" +fi + CPP="$ac_cv_prog_CPP" +else + ac_cv_prog_CPP="$CPP" +fi +echo "$ac_t""$CPP" 1>&6 + +echo $ac_n "checking for function prototypes""... $ac_c" 1>&6 +echo "configure:742: checking for function prototypes" >&5 +if eval "test \"`echo '$''{'ijg_cv_have_prototypes'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ijg_cv_have_prototypes=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ijg_cv_have_prototypes=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ijg_cv_have_prototypes" 1>&6 +if test $ijg_cv_have_prototypes = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_PROTOTYPES +EOF + +else + echo Your compiler does not seem to know about function prototypes. + echo Perhaps it needs a special switch to enable ANSI C mode. + echo If so, we recommend running configure like this: + echo " ./configure CC='cc -switch'" + echo where -switch is the proper switch. +fi +ac_safe=`echo "stddef.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for stddef.h""... $ac_c" 1>&6 +echo "configure:792: checking for stddef.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:802: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_STDDEF_H +EOF + +else + echo "$ac_t""no" 1>&6 +fi + +ac_safe=`echo "stdlib.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for stdlib.h""... $ac_c" 1>&6 +echo "configure:828: checking for stdlib.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:838: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_STDLIB_H +EOF + +else + echo "$ac_t""no" 1>&6 +fi + +ac_safe=`echo "string.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for string.h""... $ac_c" 1>&6 +echo "configure:864: checking for string.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:874: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + : +else + echo "$ac_t""no" 1>&6 +cat >> confdefs.h <<\EOF +#define NEED_BSD_STRINGS +EOF + +fi + +echo $ac_n "checking for size_t""... $ac_c" 1>&6 +echo "configure:900: checking for size_t" >&5 +cat > conftest.$ac_ext < +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#include +#ifdef NEED_BSD_STRINGS +#include +#else +#include +#endif +typedef size_t my_size_t; + +int main() { + my_size_t foovar; +; return 0; } +EOF +if { (eval echo configure:923: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ijg_size_t_ok=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ijg_size_t_ok="not ANSI, perhaps it is in sys/types.h" +fi +rm -f conftest* +echo "$ac_t""$ijg_size_t_ok" 1>&6 +if test "$ijg_size_t_ok" != yes; then +ac_safe=`echo "sys/types.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for sys/types.h""... $ac_c" 1>&6 +echo "configure:937: checking for sys/types.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:947: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define NEED_SYS_TYPES_H +EOF + +cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "size_t" >/dev/null 2>&1; then + rm -rf conftest* + ijg_size_t_ok="size_t is in sys/types.h" +else + rm -rf conftest* + ijg_size_t_ok=no +fi +rm -f conftest* + +else + echo "$ac_t""no" 1>&6 +ijg_size_t_ok=no +fi + +echo "$ac_t""$ijg_size_t_ok" 1>&6 +if test "$ijg_size_t_ok" = no; then + echo Type size_t is not defined in any of the usual places. + echo Try putting '"typedef unsigned int size_t;"' in jconfig.h. +fi +fi +echo $ac_n "checking for type unsigned char""... $ac_c" 1>&6 +echo "configure:994: checking for type unsigned char" >&5 +cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + echo "$ac_t""yes" 1>&6 +cat >> confdefs.h <<\EOF +#define HAVE_UNSIGNED_CHAR +EOF + +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + echo "$ac_t""no" 1>&6 +fi +rm -f conftest* +echo $ac_n "checking for type unsigned short""... $ac_c" 1>&6 +echo "configure:1018: checking for type unsigned short" >&5 +cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + echo "$ac_t""yes" 1>&6 +cat >> confdefs.h <<\EOF +#define HAVE_UNSIGNED_SHORT +EOF + +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + echo "$ac_t""no" 1>&6 +fi +rm -f conftest* +echo $ac_n "checking for type void""... $ac_c" 1>&6 +echo "configure:1042: checking for type void" >&5 +cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + echo "$ac_t""yes" 1>&6 +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + echo "$ac_t""no" 1>&6 +cat >> confdefs.h <<\EOF +#define void char +EOF + +fi +rm -f conftest* + +echo $ac_n "checking for working const""... $ac_c" 1>&6 +echo "configure:1088: checking for working const" >&5 +if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <j = 5; +} +{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; +} + +; return 0; } +EOF +if { (eval echo configure:1142: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_c_const=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_c_const=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_c_const" 1>&6 +if test $ac_cv_c_const = no; then + cat >> confdefs.h <<\EOF +#define const +EOF + +fi + +echo $ac_n "checking for inline""... $ac_c" 1>&6 +echo "configure:1163: checking for inline" >&5 +ijg_cv_inline="" +cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ijg_cv_inline="__inline__" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ijg_cv_inline="__inline" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ijg_cv_inline="inline" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* +fi +rm -f conftest* +fi +rm -f conftest* +echo "$ac_t""$ijg_cv_inline" 1>&6 +cat >> confdefs.h <&6 +echo "configure:1224: checking for broken incomplete types" >&5 +cat > conftest.$ac_ext <&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + echo "$ac_t""ok" 1>&6 +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + echo "$ac_t""broken" 1>&6 +cat >> confdefs.h <<\EOF +#define INCOMPLETE_TYPES_BROKEN +EOF + +fi +rm -f conftest* +echo $ac_n "checking for short external names""... $ac_c" 1>&6 +echo "configure:1248: checking for short external names" >&5 +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + echo "$ac_t""ok" 1>&6 +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + echo "$ac_t""short" 1>&6 +cat >> confdefs.h <<\EOF +#define NEED_SHORT_EXTERNAL_NAMES +EOF + +fi +rm -f conftest* +echo $ac_n "checking to see if char is signed""... $ac_c" 1>&6 +echo "configure:1275: checking to see if char is signed" >&5 +if test "$cross_compiling" = yes; then + echo Assuming that char is signed on target machine. +echo If it is unsigned, this will be a little bit inefficient. + +else + cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + echo "$ac_t""no" 1>&6 +cat >> confdefs.h <<\EOF +#define CHAR_IS_UNSIGNED +EOF + +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + echo "$ac_t""yes" 1>&6 +fi +rm -fr conftest* +fi + +echo $ac_n "checking to see if right shift is signed""... $ac_c" 1>&6 +echo "configure:1323: checking to see if right shift is signed" >&5 +if test "$cross_compiling" = yes; then + echo "$ac_t""Assuming that right shift is signed on target machine." 1>&6 +else + cat > conftest.$ac_ext <> 4; + + if (res == -0x7F7E80CL) { /* expected result for signed shift */ + return 1; /* right shift is signed */ + } + /* see if unsigned-shift hack will fix it. */ + /* we can't just test exact value since it depends on width of long... */ + res |= (~0L) << (32-4); + if (res == -0x7F7E80CL) { /* expected result now? */ + return 0; /* right shift is unsigned */ + } + printf("Right shift isn't acting as I expect it to.\n"); + printf("I fear the JPEG software will not work at all.\n\n"); + return 0; /* try it with unsigned anyway */ +} +main() { + exit(is_shifting_signed(-0x7F7E80B1L)); +} +EOF +if { (eval echo configure:1358: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + echo "$ac_t""no" 1>&6 +cat >> confdefs.h <<\EOF +#define RIGHT_SHIFT_IS_UNSIGNED +EOF + +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + echo "$ac_t""yes" 1>&6 +fi +rm -fr conftest* +fi + +echo $ac_n "checking to see if fopen accepts b spec""... $ac_c" 1>&6 +echo "configure:1375: checking to see if fopen accepts b spec" >&5 +if test "$cross_compiling" = yes; then + echo "$ac_t""Assuming that it does." 1>&6 +else + cat > conftest.$ac_ext < +main() { + if (fopen("conftestdata", "wb") != NULL) + exit(0); + exit(1); +} +EOF +if { (eval echo configure:1390: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest && (./conftest; exit) 2>/dev/null +then + echo "$ac_t""yes" 1>&6 +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + echo "$ac_t""no" 1>&6 +cat >> confdefs.h <<\EOF +#define DONT_USE_B_MODE +EOF + +fi +rm -fr conftest* +fi + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; } +fi +ac_config_guess=$ac_aux_dir/config.guess +ac_config_sub=$ac_aux_dir/config.sub +ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 +echo "configure:1436: checking for a BSD compatible install" >&5 +if test -z "$INSTALL"; then +if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + # Account for people who put trailing slashes in PATH elements. + case "$ac_dir/" in + /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + for ac_prog in ginstall installbsd scoinst install; do + if test -f $ac_dir/$ac_prog; then + if test $ac_prog = install && + grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + # OSF/1 installbsd also uses dspmsg, but is usable. + : + else + ac_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + done + ;; + esac + done + IFS="$ac_save_IFS" + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL="$ac_cv_path_install" + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL="$ac_install_sh" + fi +fi +echo "$ac_t""$INSTALL" 1>&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +# Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1488: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_RANLIB="ranlib" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":" +fi +fi +RANLIB="$ac_cv_prog_RANLIB" +if test -n "$RANLIB"; then + echo "$ac_t""$RANLIB" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + +# Decide whether to use libtool, +# and if so whether to build shared, static, or both flavors of library. +LTSHARED="no" +# Check whether --enable-shared or --disable-shared was given. +if test "${enable_shared+set}" = set; then + enableval="$enable_shared" + LTSHARED="$enableval" +fi + +LTSTATIC="no" +# Check whether --enable-static or --disable-static was given. +if test "${enable_static+set}" = set; then + enableval="$enable_static" + LTSTATIC="$enableval" +fi + +if test "x$LTSHARED" != xno -o "x$LTSTATIC" != xno; then + USELIBTOOL="yes" + LIBTOOL="./libtool" + O="lo" + A="la" + LN='$(LIBTOOL) --mode=link $(CC)' + INSTALL_LIB='$(LIBTOOL) --mode=install ${INSTALL}' + INSTALL_PROGRAM="\$(LIBTOOL) --mode=install $INSTALL_PROGRAM" +else + USELIBTOOL="no" + LIBTOOL="" + O="o" + A="a" + LN='$(CC)' + INSTALL_LIB="$INSTALL_DATA" +fi + + + + + + +# Configure libtool if needed. +if test $USELIBTOOL = yes; then + disable_shared= + disable_static= + if test "x$LTSHARED" = xno; then + disable_shared="--disable-shared" + fi + if test "x$LTSTATIC" = xno; then + disable_static="--disable-static" + fi + $srcdir/ltconfig $disable_shared $disable_static $srcdir/ltmain.sh +fi + +# Select memory manager depending on user input. +# If no "-enable-maxmem", use jmemnobs +MEMORYMGR='jmemnobs.$(O)' +MAXMEM="no" +# Check whether --enable-maxmem or --disable-maxmem was given. +if test "${enable_maxmem+set}" = set; then + enableval="$enable_maxmem" + MAXMEM="$enableval" +fi + +# support --with-maxmem for backwards compatibility with IJG V5. +# Check whether --with-maxmem or --without-maxmem was given. +if test "${with_maxmem+set}" = set; then + withval="$with_maxmem" + MAXMEM="$withval" +fi + +if test "x$MAXMEM" = xyes; then + MAXMEM=1 +fi +if test "x$MAXMEM" != xno; then + if test -n "`echo $MAXMEM | sed 's/[0-9]//g'`"; then + { echo "configure: error: non-numeric argument to --enable-maxmem" 1>&2; exit 1; } + fi + DEFAULTMAXMEM=`expr $MAXMEM \* 1048576` +cat >> confdefs.h <&6 +echo "configure:1596: checking for 'tmpfile()'" >&5 +cat > conftest.$ac_ext < +int main() { + FILE * tfile = tmpfile(); +; return 0; } +EOF +if { (eval echo configure:1605: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + echo "$ac_t""yes" 1>&6 +MEMORYMGR='jmemansi.$(O)' +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + echo "$ac_t""no" 1>&6 +MEMORYMGR='jmemname.$(O)' +cat >> confdefs.h <<\EOF +#define NEED_SIGNAL_CATCHER +EOF + +echo $ac_n "checking for 'mktemp()'""... $ac_c" 1>&6 +echo "configure:1620: checking for 'mktemp()'" >&5 +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest; then + rm -rf conftest* + echo "$ac_t""yes" 1>&6 +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + echo "$ac_t""no" 1>&6 +cat >> confdefs.h <<\EOF +#define NO_MKTEMP +EOF + +fi +rm -f conftest* +fi +rm -f conftest* +fi + + +# Extract the library version ID from jpeglib.h. +echo $ac_n "checking libjpeg version number""... $ac_c" 1>&6 +echo "configure:1650: checking libjpeg version number" >&5 +JPEG_LIB_VERSION=`sed -e '/^#define JPEG_LIB_VERSION/!d' -e 's/^[^0-9]*\([0-9][0-9]*\).*$/\1/' $srcdir/jpeglib.h` +echo "$ac_t""$JPEG_LIB_VERSION" 1>&6 + + +# Prepare to massage makefile.cfg correctly. +if test $ijg_cv_have_prototypes = yes; then + A2K_DEPS="" + COM_A2K="# " +else + A2K_DEPS="ansi2knr" + COM_A2K="" +fi + + +# ansi2knr needs -DBSD if string.h is missing +if test $ac_cv_header_string_h = no; then + ANSI2KNRFLAGS="-DBSD" +else + ANSI2KNRFLAGS="" +fi + +# Substitutions to enable or disable libtool-related stuff +if test $USELIBTOOL = yes -a $ijg_cv_have_prototypes = yes; then + COM_LT="" +else + COM_LT="# " +fi + +if test "x$LTSHARED" != xno; then + FORCE_INSTALL_LIB="install-lib" +else + FORCE_INSTALL_LIB="" +fi + +# Set up -I directives +if test "x$srcdir" = x.; then + INCLUDEFLAGS='-I$(srcdir)' +else + INCLUDEFLAGS='-I. -I$(srcdir)' +fi + +trap '' 1 2 15 + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Any assignment to VPATH causes Sun make to only execute +# the first set of double-colon rules, so remove it if not needed. +# If there is a colon in the path, we need to keep it. +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' +fi + +trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 + +DEFS=-DHAVE_CONFIG_H + +# Without the "./", some shells look in PATH for config.status. +: ${CONFIG_STATUS=./config.status} + +echo creating $CONFIG_STATUS +rm -f $CONFIG_STATUS +cat > $CONFIG_STATUS </dev/null | sed 1q`: +# +# $0 $ac_configure_args +# +# Compiler output produced by configure, useful for debugging +# configure, is in ./config.log if it exists. + +ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" +for ac_option +do + case "\$ac_option" in + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" + exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; + -version | --version | --versio | --versi | --vers | --ver | --ve | --v) + echo "$CONFIG_STATUS generated by autoconf version 2.12" + exit 0 ;; + -help | --help | --hel | --he | --h) + echo "\$ac_cs_usage"; exit 0 ;; + *) echo "\$ac_cs_usage"; exit 1 ;; + esac +done + +ac_given_srcdir=$srcdir +ac_given_INSTALL="$INSTALL" + +trap 'rm -fr `echo "Makefile:makefile.cfg jconfig.h:jconfig.cfg" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 +EOF +cat >> $CONFIG_STATUS < conftest.subs <<\\CEOF +$ac_vpsub +$extrasub +s%@CFLAGS@%$CFLAGS%g +s%@CPPFLAGS@%$CPPFLAGS%g +s%@CXXFLAGS@%$CXXFLAGS%g +s%@DEFS@%$DEFS%g +s%@LDFLAGS@%$LDFLAGS%g +s%@LIBS@%$LIBS%g +s%@exec_prefix@%$exec_prefix%g +s%@prefix@%$prefix%g +s%@program_transform_name@%$program_transform_name%g +s%@bindir@%$bindir%g +s%@sbindir@%$sbindir%g +s%@libexecdir@%$libexecdir%g +s%@datadir@%$datadir%g +s%@sysconfdir@%$sysconfdir%g +s%@sharedstatedir@%$sharedstatedir%g +s%@localstatedir@%$localstatedir%g +s%@libdir@%$libdir%g +s%@includedir@%$includedir%g +s%@oldincludedir@%$oldincludedir%g +s%@infodir@%$infodir%g +s%@mandir@%$mandir%g +s%@CC@%$CC%g +s%@CPP@%$CPP%g +s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g +s%@INSTALL_DATA@%$INSTALL_DATA%g +s%@RANLIB@%$RANLIB%g +s%@LIBTOOL@%$LIBTOOL%g +s%@O@%$O%g +s%@A@%$A%g +s%@LN@%$LN%g +s%@INSTALL_LIB@%$INSTALL_LIB%g +s%@MEMORYMGR@%$MEMORYMGR%g +s%@JPEG_LIB_VERSION@%$JPEG_LIB_VERSION%g +s%@A2K_DEPS@%$A2K_DEPS%g +s%@COM_A2K@%$COM_A2K%g +s%@ANSI2KNRFLAGS@%$ANSI2KNRFLAGS%g +s%@COM_LT@%$COM_LT%g +s%@FORCE_INSTALL_LIB@%$FORCE_INSTALL_LIB%g +s%@INCLUDEFLAGS@%$INCLUDEFLAGS%g + +CEOF +EOF + +cat >> $CONFIG_STATUS <<\EOF + +# Split the substitutions into bite-sized pieces for seds with +# small command number limits, like on Digital OSF/1 and HP-UX. +ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script. +ac_file=1 # Number of current file. +ac_beg=1 # First line for current file. +ac_end=$ac_max_sed_cmds # Line after last line for current file. +ac_more_lines=: +ac_sed_cmds="" +while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file + else + sed "${ac_end}q" conftest.subs > conftest.s$ac_file + fi + if test ! -s conftest.s$ac_file; then + ac_more_lines=false + rm -f conftest.s$ac_file + else + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f conftest.s$ac_file" + else + ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file" + fi + ac_file=`expr $ac_file + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_cmds` + fi +done +if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat +fi +EOF + +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories. + + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` + else + ac_dir_suffix= ac_dots= + fi + + case "$ac_given_srcdir" in + .) srcdir=. + if test -z "$ac_dots"; then top_srcdir=. + else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; + /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; + *) # Relative path. + srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" + top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + + case "$ac_given_INSTALL" in + [/$]*) INSTALL="$ac_given_INSTALL" ;; + *) INSTALL="$ac_dots$ac_given_INSTALL" ;; + esac + + echo creating "$ac_file" + rm -f "$ac_file" + configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." + case "$ac_file" in + *Makefile*) ac_comsub="1i\\ +# $configure_input" ;; + *) ac_comsub= ;; + esac + + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + sed -e "$ac_comsub +s%@configure_input@%$configure_input%g +s%@srcdir@%$srcdir%g +s%@top_srcdir@%$top_srcdir%g +s%@INSTALL@%$INSTALL%g +" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file +fi; done +rm -f conftest.s* + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='\([ ][ ]*\)[^ ]*%\1#\2' +ac_dC='\3' +ac_dD='%g' +# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE". +ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='\([ ]\)%\1#\2define\3' +ac_uC=' ' +ac_uD='\4%g' +# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_eB='$%\1#\2define\3' +ac_eC=' ' +ac_eD='%g' + +if test "${CONFIG_HEADERS+set}" != set; then +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +fi +for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + echo creating $ac_file + + rm -f conftest.frag conftest.in conftest.out + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + cat $ac_file_inputs > conftest.in + +EOF + +# Transform confdefs.h into a sed script conftest.vals that substitutes +# the proper values into config.h.in to produce config.h. And first: +# Protect against being on the right side of a sed subst in config.status. +# Protect against being in an unquoted here document in config.status. +rm -f conftest.vals +cat > conftest.hdr <<\EOF +s/[\\&%]/\\&/g +s%[\\$`]%\\&%g +s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp +s%ac_d%ac_u%gp +s%ac_u%ac_e%gp +EOF +sed -n -f conftest.hdr confdefs.h > conftest.vals +rm -f conftest.hdr + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >> conftest.vals <<\EOF +EOF + +# Break up conftest.vals because some shells have a limit on +# the size of here documents, and old seds have small limits too. + +rm -f conftest.tail +while : +do + ac_lines=`grep -c . conftest.vals` + # grep -c gives empty output for an empty file on some AIX systems. + if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi + # Write a limited-size here document to conftest.frag. + echo ' cat > conftest.frag <> $CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS + echo 'CEOF + sed -f conftest.frag conftest.in > conftest.out + rm -f conftest.in + mv conftest.out conftest.in +' >> $CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail + rm -f conftest.vals + mv conftest.tail conftest.vals +done +rm -f conftest.vals + +cat >> $CONFIG_STATUS <<\EOF + rm -f conftest.frag conftest.h + echo "/* $ac_file. Generated automatically by configure. */" > conftest.h + cat conftest.in >> conftest.h + rm -f conftest.in + if cmp -s $ac_file conftest.h 2>/dev/null; then + echo "$ac_file is unchanged" + rm -f conftest.h + else + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + fi + rm -f $ac_file + mv conftest.h $ac_file + fi +fi; done + +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF + +exit 0 +EOF +chmod +x $CONFIG_STATUS +rm -fr confdefs* $ac_clean_files +test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 + diff --git a/Engine/lib/ljpeg/extras/djpeg.1 b/Engine/lib/ljpeg/extras/djpeg.1 new file mode 100644 index 000000000..11beb6a51 --- /dev/null +++ b/Engine/lib/ljpeg/extras/djpeg.1 @@ -0,0 +1,253 @@ +.TH DJPEG 1 "22 August 1997" +.SH NAME +djpeg \- decompress a JPEG file to an image file +.SH SYNOPSIS +.B djpeg +[ +.I options +] +[ +.I filename +] +.LP +.SH DESCRIPTION +.LP +.B djpeg +decompresses the named JPEG file, or the standard input if no file is named, +and produces an image file on the standard output. PBMPLUS (PPM/PGM), BMP, +GIF, Targa, or RLE (Utah Raster Toolkit) output format can be selected. +(RLE is supported only if the URT library is available.) +.SH OPTIONS +All switch names may be abbreviated; for example, +.B \-grayscale +may be written +.B \-gray +or +.BR \-gr . +Most of the "basic" switches can be abbreviated to as little as one letter. +Upper and lower case are equivalent (thus +.B \-BMP +is the same as +.BR \-bmp ). +British spellings are also accepted (e.g., +.BR \-greyscale ), +though for brevity these are not mentioned below. +.PP +The basic switches are: +.TP +.BI \-colors " N" +Reduce image to at most N colors. This reduces the number of colors used in +the output image, so that it can be displayed on a colormapped display or +stored in a colormapped file format. For example, if you have an 8-bit +display, you'd need to reduce to 256 or fewer colors. +.TP +.BI \-quantize " N" +Same as +.BR \-colors . +.B \-colors +is the recommended name, +.B \-quantize +is provided only for backwards compatibility. +.TP +.B \-fast +Select recommended processing options for fast, low quality output. (The +default options are chosen for highest quality output.) Currently, this is +equivalent to \fB\-dct fast \-nosmooth \-onepass \-dither ordered\fR. +.TP +.B \-grayscale +Force gray-scale output even if JPEG file is color. Useful for viewing on +monochrome displays; also, +.B djpeg +runs noticeably faster in this mode. +.TP +.BI \-scale " M/N" +Scale the output image by a factor M/N. Currently the scale factor must be +1/1, 1/2, 1/4, or 1/8. Scaling is handy if the image is larger than your +screen; also, +.B djpeg +runs much faster when scaling down the output. +.TP +.B \-bmp +Select BMP output format (Windows flavor). 8-bit colormapped format is +emitted if +.B \-colors +or +.B \-grayscale +is specified, or if the JPEG file is gray-scale; otherwise, 24-bit full-color +format is emitted. +.TP +.B \-gif +Select GIF output format. Since GIF does not support more than 256 colors, +.B \-colors 256 +is assumed (unless you specify a smaller number of colors). +.TP +.B \-os2 +Select BMP output format (OS/2 1.x flavor). 8-bit colormapped format is +emitted if +.B \-colors +or +.B \-grayscale +is specified, or if the JPEG file is gray-scale; otherwise, 24-bit full-color +format is emitted. +.TP +.B \-pnm +Select PBMPLUS (PPM/PGM) output format (this is the default format). +PGM is emitted if the JPEG file is gray-scale or if +.B \-grayscale +is specified; otherwise PPM is emitted. +.TP +.B \-rle +Select RLE output format. (Requires URT library.) +.TP +.B \-targa +Select Targa output format. Gray-scale format is emitted if the JPEG file is +gray-scale or if +.B \-grayscale +is specified; otherwise, colormapped format is emitted if +.B \-colors +is specified; otherwise, 24-bit full-color format is emitted. +.PP +Switches for advanced users: +.TP +.B \-dct int +Use integer DCT method (default). +.TP +.B \-dct fast +Use fast integer DCT (less accurate). +.TP +.B \-dct float +Use floating-point DCT method. +The float method is very slightly more accurate than the int method, but is +much slower unless your machine has very fast floating-point hardware. Also +note that results of the floating-point method may vary slightly across +machines, while the integer methods should give the same results everywhere. +The fast integer method is much less accurate than the other two. +.TP +.B \-dither fs +Use Floyd-Steinberg dithering in color quantization. +.TP +.B \-dither ordered +Use ordered dithering in color quantization. +.TP +.B \-dither none +Do not use dithering in color quantization. +By default, Floyd-Steinberg dithering is applied when quantizing colors; this +is slow but usually produces the best results. Ordered dither is a compromise +between speed and quality; no dithering is fast but usually looks awful. Note +that these switches have no effect unless color quantization is being done. +Ordered dither is only available in +.B \-onepass +mode. +.TP +.BI \-map " file" +Quantize to the colors used in the specified image file. This is useful for +producing multiple files with identical color maps, or for forcing a +predefined set of colors to be used. The +.I file +must be a GIF or PPM file. This option overrides +.B \-colors +and +.BR \-onepass . +.TP +.B \-nosmooth +Use a faster, lower-quality upsampling routine. +.TP +.B \-onepass +Use one-pass instead of two-pass color quantization. The one-pass method is +faster and needs less memory, but it produces a lower-quality image. +.B \-onepass +is ignored unless you also say +.B \-colors +.IR N . +Also, the one-pass method is always used for gray-scale output (the two-pass +method is no improvement then). +.TP +.BI \-maxmemory " N" +Set limit for amount of memory to use in processing large images. Value is +in thousands of bytes, or millions of bytes if "M" is attached to the +number. For example, +.B \-max 4m +selects 4000000 bytes. If more space is needed, temporary files will be used. +.TP +.BI \-outfile " name" +Send output image to the named file, not to standard output. +.TP +.B \-verbose +Enable debug printout. More +.BR \-v 's +give more output. Also, version information is printed at startup. +.TP +.B \-debug +Same as +.BR \-verbose . +.SH EXAMPLES +.LP +This example decompresses the JPEG file foo.jpg, quantizes it to +256 colors, and saves the output in 8-bit BMP format in foo.bmp: +.IP +.B djpeg \-colors 256 \-bmp +.I foo.jpg +.B > +.I foo.bmp +.SH HINTS +To get a quick preview of an image, use the +.B \-grayscale +and/or +.B \-scale +switches. +.B \-grayscale \-scale 1/8 +is the fastest case. +.PP +Several options are available that trade off image quality to gain speed. +.B \-fast +turns on the recommended settings. +.PP +.B \-dct fast +and/or +.B \-nosmooth +gain speed at a small sacrifice in quality. +When producing a color-quantized image, +.B \-onepass \-dither ordered +is fast but much lower quality than the default behavior. +.B \-dither none +may give acceptable results in two-pass mode, but is seldom tolerable in +one-pass mode. +.PP +If you are fortunate enough to have very fast floating point hardware, +\fB\-dct float\fR may be even faster than \fB\-dct fast\fR. But on most +machines \fB\-dct float\fR is slower than \fB\-dct int\fR; in this case it is +not worth using, because its theoretical accuracy advantage is too small to be +significant in practice. +.SH ENVIRONMENT +.TP +.B JPEGMEM +If this environment variable is set, its value is the default memory limit. +The value is specified as described for the +.B \-maxmemory +switch. +.B JPEGMEM +overrides the default value specified when the program was compiled, and +itself is overridden by an explicit +.BR \-maxmemory . +.SH SEE ALSO +.BR cjpeg (1), +.BR jpegtran (1), +.BR rdjpgcom (1), +.BR wrjpgcom (1) +.br +.BR ppm (5), +.BR pgm (5) +.br +Wallace, Gregory K. "The JPEG Still Picture Compression Standard", +Communications of the ACM, April 1991 (vol. 34, no. 4), pp. 30-44. +.SH AUTHOR +Independent JPEG Group +.SH BUGS +Arithmetic coding is not supported for legal reasons. +.PP +To avoid the Unisys LZW patent, +.B djpeg +produces uncompressed GIF files. These are larger than they should be, but +are readable by standard GIF decoders. +.PP +Still not as fast as we'd like. diff --git a/Engine/lib/ljpeg/extras/djpeg.c b/Engine/lib/ljpeg/extras/djpeg.c new file mode 100644 index 000000000..e099e90ae --- /dev/null +++ b/Engine/lib/ljpeg/extras/djpeg.c @@ -0,0 +1,616 @@ +/* + * djpeg.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a command-line user interface for the JPEG decompressor. + * It should work on any system with Unix- or MS-DOS-style command lines. + * + * Two different command line styles are permitted, depending on the + * compile-time switch TWO_FILE_COMMANDLINE: + * djpeg [options] inputfile outputfile + * djpeg [options] [inputfile] + * In the second style, output is always to standard output, which you'd + * normally redirect to a file or pipe to some other program. Input is + * either from a named file or from standard input (typically redirected). + * The second style is convenient on Unix but is unhelpful on systems that + * don't support pipes. Also, you MUST use the first style if your system + * doesn't do binary I/O to stdin/stdout. + * To simplify script writing, the "-outfile" switch is provided. The syntax + * djpeg [options] -outfile outputfile inputfile + * works regardless of which command line style is used. + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ +#include "jversion.h" /* for version message */ + +#include /* to declare isprint() */ + +#ifdef USE_CCOMMAND /* command-line reader for Macintosh */ +#ifdef __MWERKS__ +#include /* Metrowerks needs this */ +#include /* ... and this */ +#endif +#ifdef THINK_C +#include /* Think declares it here */ +#endif +#endif + + +/* Create the add-on message string table. */ + +#define JMESSAGE(code,string) string , + +static const char * const cdjpeg_message_table[] = { +#include "cderror.h" + NULL +}; + + +/* + * This list defines the known output image formats + * (not all of which need be supported by a given version). + * You can change the default output format by defining DEFAULT_FMT; + * indeed, you had better do so if you undefine PPM_SUPPORTED. + */ + +typedef enum { + FMT_BMP, /* BMP format (Windows flavor) */ + FMT_GIF, /* GIF format */ + FMT_OS2, /* BMP format (OS/2 flavor) */ + FMT_PPM, /* PPM/PGM (PBMPLUS formats) */ + FMT_RLE, /* RLE format */ + FMT_TARGA, /* Targa format */ + FMT_TIFF /* TIFF format */ +} IMAGE_FORMATS; + +#ifndef DEFAULT_FMT /* so can override from CFLAGS in Makefile */ +#define DEFAULT_FMT FMT_PPM +#endif + +static IMAGE_FORMATS requested_fmt; + + +/* + * Argument-parsing code. + * The switch parser is designed to be useful with DOS-style command line + * syntax, ie, intermixed switches and file names, where only the switches + * to the left of a given file name affect processing of that file. + * The main program in this file doesn't actually use this capability... + */ + + +static const char * progname; /* program name for error messages */ +static char * outfilename; /* for -outfile switch */ + + +LOCAL(void) +usage (void) +/* complain about bad command line */ +{ + fprintf(stderr, "usage: %s [switches] ", progname); +#ifdef TWO_FILE_COMMANDLINE + fprintf(stderr, "inputfile outputfile\n"); +#else + fprintf(stderr, "[inputfile]\n"); +#endif + + fprintf(stderr, "Switches (names may be abbreviated):\n"); + fprintf(stderr, " -colors N Reduce image to no more than N colors\n"); + fprintf(stderr, " -fast Fast, low-quality processing\n"); + fprintf(stderr, " -grayscale Force grayscale output\n"); +#ifdef IDCT_SCALING_SUPPORTED + fprintf(stderr, " -scale M/N Scale output image by fraction M/N, eg, 1/8\n"); +#endif +#ifdef BMP_SUPPORTED + fprintf(stderr, " -bmp Select BMP output format (Windows style)%s\n", + (DEFAULT_FMT == FMT_BMP ? " (default)" : "")); +#endif +#ifdef GIF_SUPPORTED + fprintf(stderr, " -gif Select GIF output format%s\n", + (DEFAULT_FMT == FMT_GIF ? " (default)" : "")); +#endif +#ifdef BMP_SUPPORTED + fprintf(stderr, " -os2 Select BMP output format (OS/2 style)%s\n", + (DEFAULT_FMT == FMT_OS2 ? " (default)" : "")); +#endif +#ifdef PPM_SUPPORTED + fprintf(stderr, " -pnm Select PBMPLUS (PPM/PGM) output format%s\n", + (DEFAULT_FMT == FMT_PPM ? " (default)" : "")); +#endif +#ifdef RLE_SUPPORTED + fprintf(stderr, " -rle Select Utah RLE output format%s\n", + (DEFAULT_FMT == FMT_RLE ? " (default)" : "")); +#endif +#ifdef TARGA_SUPPORTED + fprintf(stderr, " -targa Select Targa output format%s\n", + (DEFAULT_FMT == FMT_TARGA ? " (default)" : "")); +#endif + fprintf(stderr, "Switches for advanced users:\n"); +#ifdef DCT_ISLOW_SUPPORTED + fprintf(stderr, " -dct int Use integer DCT method%s\n", + (JDCT_DEFAULT == JDCT_ISLOW ? " (default)" : "")); +#endif +#ifdef DCT_IFAST_SUPPORTED + fprintf(stderr, " -dct fast Use fast integer DCT (less accurate)%s\n", + (JDCT_DEFAULT == JDCT_IFAST ? " (default)" : "")); +#endif +#ifdef DCT_FLOAT_SUPPORTED + fprintf(stderr, " -dct float Use floating-point DCT method%s\n", + (JDCT_DEFAULT == JDCT_FLOAT ? " (default)" : "")); +#endif + fprintf(stderr, " -dither fs Use F-S dithering (default)\n"); + fprintf(stderr, " -dither none Don't use dithering in quantization\n"); + fprintf(stderr, " -dither ordered Use ordered dither (medium speed, quality)\n"); +#ifdef QUANT_2PASS_SUPPORTED + fprintf(stderr, " -map FILE Map to colors used in named image file\n"); +#endif + fprintf(stderr, " -nosmooth Don't use high-quality upsampling\n"); +#ifdef QUANT_1PASS_SUPPORTED + fprintf(stderr, " -onepass Use 1-pass quantization (fast, low quality)\n"); +#endif + fprintf(stderr, " -maxmemory N Maximum memory to use (in kbytes)\n"); + fprintf(stderr, " -outfile name Specify name for output file\n"); + fprintf(stderr, " -verbose or -debug Emit debug output\n"); + exit(EXIT_FAILURE); +} + + +LOCAL(int) +parse_switches (j_decompress_ptr cinfo, int argc, char **argv, + int last_file_arg_seen, boolean for_real) +/* Parse optional switches. + * Returns argv[] index of first file-name argument (== argc if none). + * Any file names with indexes <= last_file_arg_seen are ignored; + * they have presumably been processed in a previous iteration. + * (Pass 0 for last_file_arg_seen on the first or only iteration.) + * for_real is FALSE on the first (dummy) pass; we may skip any expensive + * processing. + */ +{ + int argn; + char * arg; + + /* Set up default JPEG parameters. */ + requested_fmt = DEFAULT_FMT; /* set default output file format */ + outfilename = NULL; + cinfo->err->trace_level = 0; + + /* Scan command line options, adjust parameters */ + + for (argn = 1; argn < argc; argn++) { + arg = argv[argn]; + if (*arg != '-') { + /* Not a switch, must be a file name argument */ + if (argn <= last_file_arg_seen) { + outfilename = NULL; /* -outfile applies to just one input file */ + continue; /* ignore this name if previously processed */ + } + break; /* else done parsing switches */ + } + arg++; /* advance past switch marker character */ + + if (keymatch(arg, "bmp", 1)) { + /* BMP output format. */ + requested_fmt = FMT_BMP; + + } else if (keymatch(arg, "colors", 1) || keymatch(arg, "colours", 1) || + keymatch(arg, "quantize", 1) || keymatch(arg, "quantise", 1)) { + /* Do color quantization. */ + int val; + + if (++argn >= argc) /* advance to next argument */ + usage(); + if (sscanf(argv[argn], "%d", &val) != 1) + usage(); + cinfo->desired_number_of_colors = val; + cinfo->quantize_colors = TRUE; + + } else if (keymatch(arg, "dct", 2)) { + /* Select IDCT algorithm. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + if (keymatch(argv[argn], "int", 1)) { + cinfo->dct_method = JDCT_ISLOW; + } else if (keymatch(argv[argn], "fast", 2)) { + cinfo->dct_method = JDCT_IFAST; + } else if (keymatch(argv[argn], "float", 2)) { + cinfo->dct_method = JDCT_FLOAT; + } else + usage(); + + } else if (keymatch(arg, "dither", 2)) { + /* Select dithering algorithm. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + if (keymatch(argv[argn], "fs", 2)) { + cinfo->dither_mode = JDITHER_FS; + } else if (keymatch(argv[argn], "none", 2)) { + cinfo->dither_mode = JDITHER_NONE; + } else if (keymatch(argv[argn], "ordered", 2)) { + cinfo->dither_mode = JDITHER_ORDERED; + } else + usage(); + + } else if (keymatch(arg, "debug", 1) || keymatch(arg, "verbose", 1)) { + /* Enable debug printouts. */ + /* On first -d, print version identification */ + static boolean printed_version = FALSE; + + if (! printed_version) { + fprintf(stderr, "Independent JPEG Group's DJPEG, version %s\n%s\n", + JVERSION, JCOPYRIGHT); + printed_version = TRUE; + } + cinfo->err->trace_level++; + + } else if (keymatch(arg, "fast", 1)) { + /* Select recommended processing options for quick-and-dirty output. */ + cinfo->two_pass_quantize = FALSE; + cinfo->dither_mode = JDITHER_ORDERED; + if (! cinfo->quantize_colors) /* don't override an earlier -colors */ + cinfo->desired_number_of_colors = 216; + cinfo->dct_method = JDCT_FASTEST; + cinfo->do_fancy_upsampling = FALSE; + + } else if (keymatch(arg, "gif", 1)) { + /* GIF output format. */ + requested_fmt = FMT_GIF; + + } else if (keymatch(arg, "grayscale", 2) || keymatch(arg, "greyscale",2)) { + /* Force monochrome output. */ + cinfo->out_color_space = JCS_GRAYSCALE; + + } else if (keymatch(arg, "map", 3)) { + /* Quantize to a color map taken from an input file. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + if (for_real) { /* too expensive to do twice! */ +#ifdef QUANT_2PASS_SUPPORTED /* otherwise can't quantize to supplied map */ + FILE * mapfile; + + if ((mapfile = fopen(argv[argn], READ_BINARY)) == NULL) { + fprintf(stderr, "%s: can't open %s\n", progname, argv[argn]); + exit(EXIT_FAILURE); + } + read_color_map(cinfo, mapfile); + fclose(mapfile); + cinfo->quantize_colors = TRUE; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } + + } else if (keymatch(arg, "maxmemory", 3)) { + /* Maximum memory in Kb (or Mb with 'm'). */ + long lval; + char ch = 'x'; + + if (++argn >= argc) /* advance to next argument */ + usage(); + if (sscanf(argv[argn], "%ld%c", &lval, &ch) < 1) + usage(); + if (ch == 'm' || ch == 'M') + lval *= 1000L; + cinfo->mem->max_memory_to_use = lval * 1000L; + + } else if (keymatch(arg, "nosmooth", 3)) { + /* Suppress fancy upsampling */ + cinfo->do_fancy_upsampling = FALSE; + + } else if (keymatch(arg, "onepass", 3)) { + /* Use fast one-pass quantization. */ + cinfo->two_pass_quantize = FALSE; + + } else if (keymatch(arg, "os2", 3)) { + /* BMP output format (OS/2 flavor). */ + requested_fmt = FMT_OS2; + + } else if (keymatch(arg, "outfile", 4)) { + /* Set output file name. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + outfilename = argv[argn]; /* save it away for later use */ + + } else if (keymatch(arg, "pnm", 1) || keymatch(arg, "ppm", 1)) { + /* PPM/PGM output format. */ + requested_fmt = FMT_PPM; + + } else if (keymatch(arg, "rle", 1)) { + /* RLE output format. */ + requested_fmt = FMT_RLE; + + } else if (keymatch(arg, "scale", 1)) { + /* Scale the output image by a fraction M/N. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + if (sscanf(argv[argn], "%d/%d", + &cinfo->scale_num, &cinfo->scale_denom) != 2) + usage(); + + } else if (keymatch(arg, "targa", 1)) { + /* Targa output format. */ + requested_fmt = FMT_TARGA; + + } else { + usage(); /* bogus switch */ + } + } + + return argn; /* return index of next arg (file name) */ +} + + +/* + * Marker processor for COM and interesting APPn markers. + * This replaces the library's built-in processor, which just skips the marker. + * We want to print out the marker as text, to the extent possible. + * Note this code relies on a non-suspending data source. + */ + +LOCAL(unsigned int) +jpeg_getc (j_decompress_ptr cinfo) +/* Read next byte */ +{ + struct jpeg_source_mgr * datasrc = cinfo->src; + + if (datasrc->bytes_in_buffer == 0) { + if (! (*datasrc->fill_input_buffer) (cinfo)) + ERREXIT(cinfo, JERR_CANT_SUSPEND); + } + datasrc->bytes_in_buffer--; + return GETJOCTET(*datasrc->next_input_byte++); +} + + +METHODDEF(boolean) +print_text_marker (j_decompress_ptr cinfo) +{ + boolean traceit = (cinfo->err->trace_level >= 1); + INT32 length; + unsigned int ch; + unsigned int lastch = 0; + + length = jpeg_getc(cinfo) << 8; + length += jpeg_getc(cinfo); + length -= 2; /* discount the length word itself */ + + if (traceit) { + if (cinfo->unread_marker == JPEG_COM) + fprintf(stderr, "Comment, length %ld:\n", (long) length); + else /* assume it is an APPn otherwise */ + fprintf(stderr, "APP%d, length %ld:\n", + cinfo->unread_marker - JPEG_APP0, (long) length); + } + + while (--length >= 0) { + ch = jpeg_getc(cinfo); + if (traceit) { + /* Emit the character in a readable form. + * Nonprintables are converted to \nnn form, + * while \ is converted to \\. + * Newlines in CR, CR/LF, or LF form will be printed as one newline. + */ + if (ch == '\r') { + fprintf(stderr, "\n"); + } else if (ch == '\n') { + if (lastch != '\r') + fprintf(stderr, "\n"); + } else if (ch == '\\') { + fprintf(stderr, "\\\\"); + } else if (isprint(ch)) { + putc(ch, stderr); + } else { + fprintf(stderr, "\\%03o", ch); + } + lastch = ch; + } + } + + if (traceit) + fprintf(stderr, "\n"); + + return TRUE; +} + + +/* + * The main program. + */ + +int +main (int argc, char **argv) +{ + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; +#ifdef PROGRESS_REPORT + struct cdjpeg_progress_mgr progress; +#endif + int file_index; + djpeg_dest_ptr dest_mgr = NULL; + FILE * input_file; + FILE * output_file; + JDIMENSION num_scanlines; + + /* On Mac, fetch a command line. */ +#ifdef USE_CCOMMAND + argc = ccommand(&argv); +#endif + + progname = argv[0]; + if (progname == NULL || progname[0] == 0) + progname = "djpeg"; /* in case C library doesn't provide it */ + + /* Initialize the JPEG decompression object with default error handling. */ + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_decompress(&cinfo); + /* Add some application-specific error messages (from cderror.h) */ + jerr.addon_message_table = cdjpeg_message_table; + jerr.first_addon_message = JMSG_FIRSTADDONCODE; + jerr.last_addon_message = JMSG_LASTADDONCODE; + + /* Insert custom marker processor for COM and APP12. + * APP12 is used by some digital camera makers for textual info, + * so we provide the ability to display it as text. + * If you like, additional APPn marker types can be selected for display, + * but don't try to override APP0 or APP14 this way (see libjpeg.doc). + */ + jpeg_set_marker_processor(&cinfo, JPEG_COM, print_text_marker); + jpeg_set_marker_processor(&cinfo, JPEG_APP0+12, print_text_marker); + + /* Now safe to enable signal catcher. */ +#ifdef NEED_SIGNAL_CATCHER + enable_signal_catcher((j_common_ptr) &cinfo); +#endif + + /* Scan command line to find file names. */ + /* It is convenient to use just one switch-parsing routine, but the switch + * values read here are ignored; we will rescan the switches after opening + * the input file. + * (Exception: tracing level set here controls verbosity for COM markers + * found during jpeg_read_header...) + */ + + file_index = parse_switches(&cinfo, argc, argv, 0, FALSE); + +#ifdef TWO_FILE_COMMANDLINE + /* Must have either -outfile switch or explicit output file name */ + if (outfilename == NULL) { + if (file_index != argc-2) { + fprintf(stderr, "%s: must name one input and one output file\n", + progname); + usage(); + } + outfilename = argv[file_index+1]; + } else { + if (file_index != argc-1) { + fprintf(stderr, "%s: must name one input and one output file\n", + progname); + usage(); + } + } +#else + /* Unix style: expect zero or one file name */ + if (file_index < argc-1) { + fprintf(stderr, "%s: only one input file\n", progname); + usage(); + } +#endif /* TWO_FILE_COMMANDLINE */ + + /* Open the input file. */ + if (file_index < argc) { + if ((input_file = fopen(argv[file_index], READ_BINARY)) == NULL) { + fprintf(stderr, "%s: can't open %s\n", progname, argv[file_index]); + exit(EXIT_FAILURE); + } + } else { + /* default input file is stdin */ + input_file = read_stdin(); + } + + /* Open the output file. */ + if (outfilename != NULL) { + if ((output_file = fopen(outfilename, WRITE_BINARY)) == NULL) { + fprintf(stderr, "%s: can't open %s\n", progname, outfilename); + exit(EXIT_FAILURE); + } + } else { + /* default output file is stdout */ + output_file = write_stdout(); + } + +#ifdef PROGRESS_REPORT + start_progress_monitor((j_common_ptr) &cinfo, &progress); +#endif + + /* Specify data source for decompression */ + jpeg_stdio_src(&cinfo, input_file); + + /* Read file header, set default decompression parameters */ + (void) jpeg_read_header(&cinfo, TRUE); + + /* Adjust default decompression parameters by re-parsing the options */ + file_index = parse_switches(&cinfo, argc, argv, 0, TRUE); + + /* Initialize the output module now to let it override any crucial + * option settings (for instance, GIF wants to force color quantization). + */ + switch (requested_fmt) { +#ifdef BMP_SUPPORTED + case FMT_BMP: + dest_mgr = jinit_write_bmp(&cinfo, FALSE); + break; + case FMT_OS2: + dest_mgr = jinit_write_bmp(&cinfo, TRUE); + break; +#endif +#ifdef GIF_SUPPORTED + case FMT_GIF: + dest_mgr = jinit_write_gif(&cinfo); + break; +#endif +#ifdef PPM_SUPPORTED + case FMT_PPM: + dest_mgr = jinit_write_ppm(&cinfo); + break; +#endif +#ifdef RLE_SUPPORTED + case FMT_RLE: + dest_mgr = jinit_write_rle(&cinfo); + break; +#endif +#ifdef TARGA_SUPPORTED + case FMT_TARGA: + dest_mgr = jinit_write_targa(&cinfo); + break; +#endif + default: + ERREXIT(&cinfo, JERR_UNSUPPORTED_FORMAT); + break; + } + dest_mgr->output_file = output_file; + + /* Start decompressor */ + (void) jpeg_start_decompress(&cinfo); + + /* Write output file header */ + (*dest_mgr->start_output) (&cinfo, dest_mgr); + + /* Process data */ + while (cinfo.output_scanline < cinfo.output_height) { + num_scanlines = jpeg_read_scanlines(&cinfo, dest_mgr->buffer, + dest_mgr->buffer_height); + (*dest_mgr->put_pixel_rows) (&cinfo, dest_mgr, num_scanlines); + } + +#ifdef PROGRESS_REPORT + /* Hack: count final pass as done in case finish_output does an extra pass. + * The library won't have updated completed_passes. + */ + progress.pub.completed_passes = progress.pub.total_passes; +#endif + + /* Finish decompression and release memory. + * I must do it in this order because output module has allocated memory + * of lifespan JPOOL_IMAGE; it needs to finish before releasing memory. + */ + (*dest_mgr->finish_output) (&cinfo, dest_mgr); + (void) jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + + /* Close files, if we opened them */ + if (input_file != stdin) + fclose(input_file); + if (output_file != stdout) + fclose(output_file); + +#ifdef PROGRESS_REPORT + end_progress_monitor((j_common_ptr) &cinfo); +#endif + + /* All done. */ + exit(jerr.num_warnings ? EXIT_WARNING : EXIT_SUCCESS); + return 0; /* suppress no-return-value warnings */ +} diff --git a/Engine/lib/ljpeg/extras/example.c b/Engine/lib/ljpeg/extras/example.c new file mode 100644 index 000000000..7fc354f04 --- /dev/null +++ b/Engine/lib/ljpeg/extras/example.c @@ -0,0 +1,433 @@ +/* + * example.c + * + * This file illustrates how to use the IJG code as a subroutine library + * to read or write JPEG image files. You should look at this code in + * conjunction with the documentation file libjpeg.doc. + * + * This code will not do anything useful as-is, but it may be helpful as a + * skeleton for constructing routines that call the JPEG library. + * + * We present these routines in the same coding style used in the JPEG code + * (ANSI function definitions, etc); but you are of course free to code your + * routines in a different style if you prefer. + */ + +#include + +/* + * Include file for users of JPEG library. + * You will need to have included system headers that define at least + * the typedefs FILE and size_t before you can include jpeglib.h. + * (stdio.h is sufficient on ANSI-conforming systems.) + * You may also wish to include "jerror.h". + */ + +#include "jpeglib.h" + +/* + * is used for the optional error recovery mechanism shown in + * the second part of the example. + */ + +#include + + + +/******************** JPEG COMPRESSION SAMPLE INTERFACE *******************/ + +/* This half of the example shows how to feed data into the JPEG compressor. + * We present a minimal version that does not worry about refinements such + * as error recovery (the JPEG code will just exit() if it gets an error). + */ + + +/* + * IMAGE DATA FORMATS: + * + * The standard input image format is a rectangular array of pixels, with + * each pixel having the same number of "component" values (color channels). + * Each pixel row is an array of JSAMPLEs (which typically are unsigned chars). + * If you are working with color data, then the color values for each pixel + * must be adjacent in the row; for example, R,G,B,R,G,B,R,G,B,... for 24-bit + * RGB color. + * + * For this example, we'll assume that this data structure matches the way + * our application has stored the image in memory, so we can just pass a + * pointer to our image buffer. In particular, let's say that the image is + * RGB color and is described by: + */ + +extern JSAMPLE * image_buffer; /* Points to large array of R,G,B-order data */ +extern int image_height; /* Number of rows in image */ +extern int image_width; /* Number of columns in image */ + + +/* + * Sample routine for JPEG compression. We assume that the target file name + * and a compression quality factor are passed in. + */ + +GLOBAL(void) +write_JPEG_file (char * filename, int quality) +{ + /* This struct contains the JPEG compression parameters and pointers to + * working space (which is allocated as needed by the JPEG library). + * It is possible to have several such structures, representing multiple + * compression/decompression processes, in existence at once. We refer + * to any one struct (and its associated working data) as a "JPEG object". + */ + struct jpeg_compress_struct cinfo; + /* This struct represents a JPEG error handler. It is declared separately + * because applications often want to supply a specialized error handler + * (see the second half of this file for an example). But here we just + * take the easy way out and use the standard error handler, which will + * print a message on stderr and call exit() if compression fails. + * Note that this struct must live as long as the main JPEG parameter + * struct, to avoid dangling-pointer problems. + */ + struct jpeg_error_mgr jerr; + /* More stuff */ + FILE * outfile; /* target file */ + JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */ + int row_stride; /* physical row width in image buffer */ + + /* Step 1: allocate and initialize JPEG compression object */ + + /* We have to set up the error handler first, in case the initialization + * step fails. (Unlikely, but it could happen if you are out of memory.) + * This routine fills in the contents of struct jerr, and returns jerr's + * address which we place into the link field in cinfo. + */ + cinfo.err = jpeg_std_error(&jerr); + /* Now we can initialize the JPEG compression object. */ + jpeg_create_compress(&cinfo); + + /* Step 2: specify data destination (eg, a file) */ + /* Note: steps 2 and 3 can be done in either order. */ + + /* Here we use the library-supplied code to send compressed data to a + * stdio stream. You can also write your own code to do something else. + * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that + * requires it in order to write binary files. + */ + if ((outfile = fopen(filename, "wb")) == NULL) { + fprintf(stderr, "can't open %s\n", filename); + exit(1); + } + jpeg_stdio_dest(&cinfo, outfile); + + /* Step 3: set parameters for compression */ + + /* First we supply a description of the input image. + * Four fields of the cinfo struct must be filled in: + */ + cinfo.image_width = image_width; /* image width and height, in pixels */ + cinfo.image_height = image_height; + cinfo.input_components = 3; /* # of color components per pixel */ + cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ + /* Now use the library's routine to set default compression parameters. + * (You must set at least cinfo.in_color_space before calling this, + * since the defaults depend on the source color space.) + */ + jpeg_set_defaults(&cinfo); + /* Now you can set any non-default parameters you wish to. + * Here we just illustrate the use of quality (quantization table) scaling: + */ + jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */); + + /* Step 4: Start compressor */ + + /* TRUE ensures that we will write a complete interchange-JPEG file. + * Pass TRUE unless you are very sure of what you're doing. + */ + jpeg_start_compress(&cinfo, TRUE); + + /* Step 5: while (scan lines remain to be written) */ + /* jpeg_write_scanlines(...); */ + + /* Here we use the library's state variable cinfo.next_scanline as the + * loop counter, so that we don't have to keep track ourselves. + * To keep things simple, we pass one scanline per call; you can pass + * more if you wish, though. + */ + row_stride = image_width * 3; /* JSAMPLEs per row in image_buffer */ + + while (cinfo.next_scanline < cinfo.image_height) { + /* jpeg_write_scanlines expects an array of pointers to scanlines. + * Here the array is only one element long, but you could pass + * more than one scanline at a time if that's more convenient. + */ + row_pointer[0] = & image_buffer[cinfo.next_scanline * row_stride]; + (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); + } + + /* Step 6: Finish compression */ + + jpeg_finish_compress(&cinfo); + /* After finish_compress, we can close the output file. */ + fclose(outfile); + + /* Step 7: release JPEG compression object */ + + /* This is an important step since it will release a good deal of memory. */ + jpeg_destroy_compress(&cinfo); + + /* And we're done! */ +} + + +/* + * SOME FINE POINTS: + * + * In the above loop, we ignored the return value of jpeg_write_scanlines, + * which is the number of scanlines actually written. We could get away + * with this because we were only relying on the value of cinfo.next_scanline, + * which will be incremented correctly. If you maintain additional loop + * variables then you should be careful to increment them properly. + * Actually, for output to a stdio stream you needn't worry, because + * then jpeg_write_scanlines will write all the lines passed (or else exit + * with a fatal error). Partial writes can only occur if you use a data + * destination module that can demand suspension of the compressor. + * (If you don't know what that's for, you don't need it.) + * + * If the compressor requires full-image buffers (for entropy-coding + * optimization or a multi-scan JPEG file), it will create temporary + * files for anything that doesn't fit within the maximum-memory setting. + * (Note that temp files are NOT needed if you use the default parameters.) + * On some systems you may need to set up a signal handler to ensure that + * temporary files are deleted if the program is interrupted. See libjpeg.doc. + * + * Scanlines MUST be supplied in top-to-bottom order if you want your JPEG + * files to be compatible with everyone else's. If you cannot readily read + * your data in that order, you'll need an intermediate array to hold the + * image. See rdtarga.c or rdbmp.c for examples of handling bottom-to-top + * source data using the JPEG code's internal virtual-array mechanisms. + */ + + + +/******************** JPEG DECOMPRESSION SAMPLE INTERFACE *******************/ + +/* This half of the example shows how to read data from the JPEG decompressor. + * It's a bit more refined than the above, in that we show: + * (a) how to modify the JPEG library's standard error-reporting behavior; + * (b) how to allocate workspace using the library's memory manager. + * + * Just to make this example a little different from the first one, we'll + * assume that we do not intend to put the whole image into an in-memory + * buffer, but to send it line-by-line someplace else. We need a one- + * scanline-high JSAMPLE array as a work buffer, and we will let the JPEG + * memory manager allocate it for us. This approach is actually quite useful + * because we don't need to remember to deallocate the buffer separately: it + * will go away automatically when the JPEG object is cleaned up. + */ + + +/* + * ERROR HANDLING: + * + * The JPEG library's standard error handler (jerror.c) is divided into + * several "methods" which you can override individually. This lets you + * adjust the behavior without duplicating a lot of code, which you might + * have to update with each future release. + * + * Our example here shows how to override the "error_exit" method so that + * control is returned to the library's caller when a fatal error occurs, + * rather than calling exit() as the standard error_exit method does. + * + * We use C's setjmp/longjmp facility to return control. This means that the + * routine which calls the JPEG library must first execute a setjmp() call to + * establish the return point. We want the replacement error_exit to do a + * longjmp(). But we need to make the setjmp buffer accessible to the + * error_exit routine. To do this, we make a private extension of the + * standard JPEG error handler object. (If we were using C++, we'd say we + * were making a subclass of the regular error handler.) + * + * Here's the extended error handler struct: + */ + +struct my_error_mgr { + struct jpeg_error_mgr pub; /* "public" fields */ + + jmp_buf setjmp_buffer; /* for return to caller */ +}; + +typedef struct my_error_mgr * my_error_ptr; + +/* + * Here's the routine that will replace the standard error_exit method: + */ + +METHODDEF(void) +my_error_exit (j_common_ptr cinfo) +{ + /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */ + my_error_ptr myerr = (my_error_ptr) cinfo->err; + + /* Always display the message. */ + /* We could postpone this until after returning, if we chose. */ + (*cinfo->err->output_message) (cinfo); + + /* Return control to the setjmp point */ + longjmp(myerr->setjmp_buffer, 1); +} + + +/* + * Sample routine for JPEG decompression. We assume that the source file name + * is passed in. We want to return 1 on success, 0 on error. + */ + + +GLOBAL(int) +read_JPEG_file (char * filename) +{ + /* This struct contains the JPEG decompression parameters and pointers to + * working space (which is allocated as needed by the JPEG library). + */ + struct jpeg_decompress_struct cinfo; + /* We use our private extension JPEG error handler. + * Note that this struct must live as long as the main JPEG parameter + * struct, to avoid dangling-pointer problems. + */ + struct my_error_mgr jerr; + /* More stuff */ + FILE * infile; /* source file */ + JSAMPARRAY buffer; /* Output row buffer */ + int row_stride; /* physical row width in output buffer */ + + /* In this example we want to open the input file before doing anything else, + * so that the setjmp() error recovery below can assume the file is open. + * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that + * requires it in order to read binary files. + */ + + if ((infile = fopen(filename, "rb")) == NULL) { + fprintf(stderr, "can't open %s\n", filename); + return 0; + } + + /* Step 1: allocate and initialize JPEG decompression object */ + + /* We set up the normal JPEG error routines, then override error_exit. */ + cinfo.err = jpeg_std_error(&jerr.pub); + jerr.pub.error_exit = my_error_exit; + /* Establish the setjmp return context for my_error_exit to use. */ + if (setjmp(jerr.setjmp_buffer)) { + /* If we get here, the JPEG code has signaled an error. + * We need to clean up the JPEG object, close the input file, and return. + */ + jpeg_destroy_decompress(&cinfo); + fclose(infile); + return 0; + } + /* Now we can initialize the JPEG decompression object. */ + jpeg_create_decompress(&cinfo); + + /* Step 2: specify data source (eg, a file) */ + + jpeg_stdio_src(&cinfo, infile); + + /* Step 3: read file parameters with jpeg_read_header() */ + + (void) jpeg_read_header(&cinfo, TRUE); + /* We can ignore the return value from jpeg_read_header since + * (a) suspension is not possible with the stdio data source, and + * (b) we passed TRUE to reject a tables-only JPEG file as an error. + * See libjpeg.doc for more info. + */ + + /* Step 4: set parameters for decompression */ + + /* In this example, we don't need to change any of the defaults set by + * jpeg_read_header(), so we do nothing here. + */ + + /* Step 5: Start decompressor */ + + (void) jpeg_start_decompress(&cinfo); + /* We can ignore the return value since suspension is not possible + * with the stdio data source. + */ + + /* We may need to do some setup of our own at this point before reading + * the data. After jpeg_start_decompress() we have the correct scaled + * output image dimensions available, as well as the output colormap + * if we asked for color quantization. + * In this example, we need to make an output work buffer of the right size. + */ + /* JSAMPLEs per row in output buffer */ + row_stride = cinfo.output_width * cinfo.output_components; + /* Make a one-row-high sample array that will go away when done with image */ + buffer = (*cinfo.mem->alloc_sarray) + ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); + + /* Step 6: while (scan lines remain to be read) */ + /* jpeg_read_scanlines(...); */ + + /* Here we use the library's state variable cinfo.output_scanline as the + * loop counter, so that we don't have to keep track ourselves. + */ + while (cinfo.output_scanline < cinfo.output_height) { + /* jpeg_read_scanlines expects an array of pointers to scanlines. + * Here the array is only one element long, but you could ask for + * more than one scanline at a time if that's more convenient. + */ + (void) jpeg_read_scanlines(&cinfo, buffer, 1); + /* Assume put_scanline_someplace wants a pointer and sample count. */ + put_scanline_someplace(buffer[0], row_stride); + } + + /* Step 7: Finish decompression */ + + (void) jpeg_finish_decompress(&cinfo); + /* We can ignore the return value since suspension is not possible + * with the stdio data source. + */ + + /* Step 8: Release JPEG decompression object */ + + /* This is an important step since it will release a good deal of memory. */ + jpeg_destroy_decompress(&cinfo); + + /* After finish_decompress, we can close the input file. + * Here we postpone it until after no more JPEG errors are possible, + * so as to simplify the setjmp error logic above. (Actually, I don't + * think that jpeg_destroy can do an error exit, but why assume anything...) + */ + fclose(infile); + + /* At this point you may want to check to see whether any corrupt-data + * warnings occurred (test whether jerr.pub.num_warnings is nonzero). + */ + + /* And we're done! */ + return 1; +} + + +/* + * SOME FINE POINTS: + * + * In the above code, we ignored the return value of jpeg_read_scanlines, + * which is the number of scanlines actually read. We could get away with + * this because we asked for only one line at a time and we weren't using + * a suspending data source. See libjpeg.doc for more info. + * + * We cheated a bit by calling alloc_sarray() after jpeg_start_decompress(); + * we should have done it beforehand to ensure that the space would be + * counted against the JPEG max_memory setting. In some systems the above + * code would risk an out-of-memory error. However, in general we don't + * know the output image dimensions before jpeg_start_decompress(), unless we + * call jpeg_calc_output_dimensions(). See libjpeg.doc for more about this. + * + * Scanlines are returned in the same order as they appear in the JPEG file, + * which is standardly top-to-bottom. If you must emit data bottom-to-top, + * you can use one of the virtual arrays provided by the JPEG memory manager + * to invert the data. See wrbmp.c for an example. + * + * As with compression, some operating modes may require temporary files. + * On some systems you may need to set up a signal handler to ensure that + * temporary files are deleted if the program is interrupted. See libjpeg.doc. + */ diff --git a/Engine/lib/ljpeg/extras/filelist.doc b/Engine/lib/ljpeg/extras/filelist.doc new file mode 100644 index 000000000..e14982ca5 --- /dev/null +++ b/Engine/lib/ljpeg/extras/filelist.doc @@ -0,0 +1,210 @@ +IJG JPEG LIBRARY: FILE LIST + +Copyright (C) 1994-1998, Thomas G. Lane. +This file is part of the Independent JPEG Group's software. +For conditions of distribution and use, see the accompanying README file. + + +Here is a road map to the files in the IJG JPEG distribution. The +distribution includes the JPEG library proper, plus two application +programs ("cjpeg" and "djpeg") which use the library to convert JPEG +files to and from some other popular image formats. A third application +"jpegtran" uses the library to do lossless conversion between different +variants of JPEG. There are also two stand-alone applications, +"rdjpgcom" and "wrjpgcom". + + +THE JPEG LIBRARY +================ + +Include files: + +jpeglib.h JPEG library's exported data and function declarations. +jconfig.h Configuration declarations. Note: this file is not present + in the distribution; it is generated during installation. +jmorecfg.h Additional configuration declarations; need not be changed + for a standard installation. +jerror.h Declares JPEG library's error and trace message codes. +jinclude.h Central include file used by all IJG .c files to reference + system include files. +jpegint.h JPEG library's internal data structures. +jchuff.h Private declarations for Huffman encoder modules. +jdhuff.h Private declarations for Huffman decoder modules. +jdct.h Private declarations for forward & reverse DCT subsystems. +jmemsys.h Private declarations for memory management subsystem. +jversion.h Version information. + +Applications using the library should include jpeglib.h (which in turn +includes jconfig.h and jmorecfg.h). Optionally, jerror.h may be included +if the application needs to reference individual JPEG error codes. The +other include files are intended for internal use and would not normally +be included by an application program. (cjpeg/djpeg/etc do use jinclude.h, +since its function is to improve portability of the whole IJG distribution. +Most other applications will directly include the system include files they +want, and hence won't need jinclude.h.) + + +C source code files: + +These files contain most of the functions intended to be called directly by +an application program: + +jcapimin.c Application program interface: core routines for compression. +jcapistd.c Application program interface: standard compression. +jdapimin.c Application program interface: core routines for decompression. +jdapistd.c Application program interface: standard decompression. +jcomapi.c Application program interface routines common to compression + and decompression. +jcparam.c Compression parameter setting helper routines. +jctrans.c API and library routines for transcoding compression. +jdtrans.c API and library routines for transcoding decompression. + +Compression side of the library: + +jcinit.c Initialization: determines which other modules to use. +jcmaster.c Master control: setup and inter-pass sequencing logic. +jcmainct.c Main buffer controller (preprocessor => JPEG compressor). +jcprepct.c Preprocessor buffer controller. +jccoefct.c Buffer controller for DCT coefficient buffer. +jccolor.c Color space conversion. +jcsample.c Downsampling. +jcdctmgr.c DCT manager (DCT implementation selection & control). +jfdctint.c Forward DCT using slow-but-accurate integer method. +jfdctfst.c Forward DCT using faster, less accurate integer method. +jfdctflt.c Forward DCT using floating-point arithmetic. +jchuff.c Huffman entropy coding for sequential JPEG. +jcphuff.c Huffman entropy coding for progressive JPEG. +jcmarker.c JPEG marker writing. +jdatadst.c Data destination manager for stdio output. + +Decompression side of the library: + +jdmaster.c Master control: determines which other modules to use. +jdinput.c Input controller: controls input processing modules. +jdmainct.c Main buffer controller (JPEG decompressor => postprocessor). +jdcoefct.c Buffer controller for DCT coefficient buffer. +jdpostct.c Postprocessor buffer controller. +jdmarker.c JPEG marker reading. +jdhuff.c Huffman entropy decoding for sequential JPEG. +jdphuff.c Huffman entropy decoding for progressive JPEG. +jddctmgr.c IDCT manager (IDCT implementation selection & control). +jidctint.c Inverse DCT using slow-but-accurate integer method. +jidctfst.c Inverse DCT using faster, less accurate integer method. +jidctflt.c Inverse DCT using floating-point arithmetic. +jidctred.c Inverse DCTs with reduced-size outputs. +jdsample.c Upsampling. +jdcolor.c Color space conversion. +jdmerge.c Merged upsampling/color conversion (faster, lower quality). +jquant1.c One-pass color quantization using a fixed-spacing colormap. +jquant2.c Two-pass color quantization using a custom-generated colormap. + Also handles one-pass quantization to an externally given map. +jdatasrc.c Data source manager for stdio input. + +Support files for both compression and decompression: + +jerror.c Standard error handling routines (application replaceable). +jmemmgr.c System-independent (more or less) memory management code. +jutils.c Miscellaneous utility routines. + +jmemmgr.c relies on a system-dependent memory management module. The IJG +distribution includes the following implementations of the system-dependent +module: + +jmemnobs.c "No backing store": assumes adequate virtual memory exists. +jmemansi.c Makes temporary files with ANSI-standard routine tmpfile(). +jmemname.c Makes temporary files with program-generated file names. +jmemdos.c Custom implementation for MS-DOS (16-bit environment only): + can use extended and expanded memory as well as temp files. +jmemmac.c Custom implementation for Apple Macintosh. + +Exactly one of the system-dependent modules should be configured into an +installed JPEG library (see install.doc for hints about which one to use). +On unusual systems you may find it worthwhile to make a special +system-dependent memory manager. + + +Non-C source code files: + +jmemdosa.asm 80x86 assembly code support for jmemdos.c; used only in + MS-DOS-specific configurations of the JPEG library. + + +CJPEG/DJPEG/JPEGTRAN +==================== + +Include files: + +cdjpeg.h Declarations shared by cjpeg/djpeg/jpegtran modules. +cderror.h Additional error and trace message codes for cjpeg et al. +transupp.h Declarations for jpegtran support routines in transupp.c. + +C source code files: + +cjpeg.c Main program for cjpeg. +djpeg.c Main program for djpeg. +jpegtran.c Main program for jpegtran. +cdjpeg.c Utility routines used by all three programs. +rdcolmap.c Code to read a colormap file for djpeg's "-map" switch. +rdswitch.c Code to process some of cjpeg's more complex switches. + Also used by jpegtran. +transupp.c Support code for jpegtran: lossless image manipulations. + +Image file reader modules for cjpeg: + +rdbmp.c BMP file input. +rdgif.c GIF file input (now just a stub). +rdppm.c PPM/PGM file input. +rdrle.c Utah RLE file input. +rdtarga.c Targa file input. + +Image file writer modules for djpeg: + +wrbmp.c BMP file output. +wrgif.c GIF file output (a mere shadow of its former self). +wrppm.c PPM/PGM file output. +wrrle.c Utah RLE file output. +wrtarga.c Targa file output. + + +RDJPGCOM/WRJPGCOM +================= + +C source code files: + +rdjpgcom.c Stand-alone rdjpgcom application. +wrjpgcom.c Stand-alone wrjpgcom application. + +These programs do not depend on the IJG library. They do use +jconfig.h and jinclude.h, only to improve portability. + + +ADDITIONAL FILES +================ + +Documentation (see README for a guide to the documentation files): + +README Master documentation file. +*.doc Other documentation files. +*.1 Documentation in Unix man page format. +change.log Version-to-version change highlights. +example.c Sample code for calling JPEG library. + +Configuration/installation files and programs (see install.doc for more info): + +configure Unix shell script to perform automatic configuration. +ltconfig Support scripts for configure (from GNU libtool). +ltmain.sh +config.guess +config.sub +install-sh Install shell script for those Unix systems lacking one. +ckconfig.c Program to generate jconfig.h on non-Unix systems. +jconfig.doc Template for making jconfig.h by hand. +makefile.* Sample makefiles for particular systems. +jconfig.* Sample jconfig.h for particular systems. +ansi2knr.c De-ANSIfier for pre-ANSI C compilers (courtesy of + L. Peter Deutsch and Aladdin Enterprises). + +Test files (see install.doc for test procedure): + +test*.* Source and comparison files for confidence test. + These are binary image files, NOT text files. diff --git a/Engine/lib/ljpeg/extras/install-sh b/Engine/lib/ljpeg/extras/install-sh new file mode 100644 index 000000000..e8436696c --- /dev/null +++ b/Engine/lib/ljpeg/extras/install-sh @@ -0,0 +1,250 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/Engine/lib/ljpeg/extras/install.doc b/Engine/lib/ljpeg/extras/install.doc new file mode 100644 index 000000000..3702b986b --- /dev/null +++ b/Engine/lib/ljpeg/extras/install.doc @@ -0,0 +1,1063 @@ +INSTALLATION INSTRUCTIONS for the Independent JPEG Group's JPEG software + +Copyright (C) 1991-1998, Thomas G. Lane. +This file is part of the Independent JPEG Group's software. +For conditions of distribution and use, see the accompanying README file. + + +This file explains how to configure and install the IJG software. We have +tried to make this software extremely portable and flexible, so that it can be +adapted to almost any environment. The downside of this decision is that the +installation process is complicated. We have provided shortcuts to simplify +the task on common systems. But in any case, you will need at least a little +familiarity with C programming and program build procedures for your system. + +If you are only using this software as part of a larger program, the larger +program's installation procedure may take care of configuring the IJG code. +For example, Ghostscript's installation script will configure the IJG code. +You don't need to read this file if you just want to compile Ghostscript. + +If you are on a Unix machine, you may not need to read this file at all. +Try doing + ./configure + make + make test +If that doesn't complain, do + make install +(better do "make -n install" first to see if the makefile will put the files +where you want them). Read further if you run into snags or want to customize +the code for your system. + + +TABLE OF CONTENTS +----------------- + +Before you start +Configuring the software: + using the automatic "configure" script + using one of the supplied jconfig and makefile files + by hand +Building the software +Testing the software +Installing the software +Optional stuff +Optimization +Hints for specific systems + + +BEFORE YOU START +================ + +Before installing the software you must unpack the distributed source code. +Since you are reading this file, you have probably already succeeded in this +task. However, there is a potential for error if you needed to convert the +files to the local standard text file format (for example, if you are on +MS-DOS you may have converted LF end-of-line to CR/LF). You must apply +such conversion to all the files EXCEPT those whose names begin with "test". +The test files contain binary data; if you change them in any way then the +self-test will give bad results. + +Please check the last section of this file to see if there are hints for the +specific machine or compiler you are using. + + +CONFIGURING THE SOFTWARE +======================== + +To configure the IJG code for your system, you need to create two files: + * jconfig.h: contains values for system-dependent #define symbols. + * Makefile: controls the compilation process. +(On a non-Unix machine, you may create "project files" or some other +substitute for a Makefile. jconfig.h is needed in any environment.) + +We provide three different ways to generate these files: + * On a Unix system, you can just run the "configure" script. + * We provide sample jconfig files and makefiles for popular machines; + if your machine matches one of the samples, just copy the right sample + files to jconfig.h and Makefile. + * If all else fails, read the instructions below and make your own files. + + +Configuring the software using the automatic "configure" script +--------------------------------------------------------------- + +If you are on a Unix machine, you can just type + ./configure +and let the configure script construct appropriate configuration files. +If you're using "csh" on an old version of System V, you might need to type + sh configure +instead to prevent csh from trying to execute configure itself. +Expect configure to run for a few minutes, particularly on slower machines; +it works by compiling a series of test programs. + +Configure was created with GNU Autoconf and it follows the usual conventions +for GNU configure scripts. It makes a few assumptions that you may want to +override. You can do this by providing optional switches to configure: + +* If you want to build libjpeg as a shared library, say + ./configure --enable-shared +To get both shared and static libraries, say + ./configure --enable-shared --enable-static +Note that these switches invoke GNU libtool to take care of system-dependent +shared library building methods. If things don't work this way, please try +running configure without either switch; that should build a static library +without using libtool. If that works, your problem is probably with libtool +not with the IJG code. libtool is fairly new and doesn't support all flavors +of Unix yet. (You might be able to find a newer version of libtool than the +one included with libjpeg; see ftp.gnu.org. Report libtool problems to +bug-libtool@gnu.org.) + +* Configure will use gcc (GNU C compiler) if it's available, otherwise cc. +To force a particular compiler to be selected, use the CC option, for example + ./configure CC='cc' +The same method can be used to include any unusual compiler switches. +For example, on HP-UX you probably want to say + ./configure CC='cc -Aa' +to get HP's compiler to run in ANSI mode. + +* The default CFLAGS setting is "-O" for non-gcc compilers, "-O2" for gcc. +You can override this by saying, for example, + ./configure CFLAGS='-g' +if you want to compile with debugging support. + +* Configure will set up the makefile so that "make install" will install files +into /usr/local/bin, /usr/local/man, etc. You can specify an installation +prefix other than "/usr/local" by giving configure the option "--prefix=PATH". + +* If you don't have a lot of swap space, you may need to enable the IJG +software's internal virtual memory mechanism. To do this, give the option +"--enable-maxmem=N" where N is the default maxmemory limit in megabytes. +This is discussed in more detail under "Selecting a memory manager", below. +You probably don't need to worry about this on reasonably-sized Unix machines, +unless you plan to process very large images. + +Configure has some other features that are useful if you are cross-compiling +or working in a network of multiple machine types; but if you need those +features, you probably already know how to use them. + + +Configuring the software using one of the supplied jconfig and makefile files +----------------------------------------------------------------------------- + +If you have one of these systems, you can just use the provided configuration +files: + +Makefile jconfig file System and/or compiler + +makefile.manx jconfig.manx Amiga, Manx Aztec C +makefile.sas jconfig.sas Amiga, SAS C +makeproj.mac jconfig.mac Apple Macintosh, Metrowerks CodeWarrior +mak*jpeg.st jconfig.st Atari ST/STE/TT, Pure C or Turbo C +makefile.bcc jconfig.bcc MS-DOS or OS/2, Borland C +makefile.dj jconfig.dj MS-DOS, DJGPP (Delorie's port of GNU C) +makefile.mc6 jconfig.mc6 MS-DOS, Microsoft C (16-bit only) +makefile.wat jconfig.wat MS-DOS, OS/2, or Windows NT, Watcom C +makefile.vc jconfig.vc Windows NT/95, MS Visual C++ +make*.ds jconfig.vc Windows NT/95, MS Developer Studio +makefile.mms jconfig.vms Digital VMS, with MMS software +makefile.vms jconfig.vms Digital VMS, without MMS software + +Copy the proper jconfig file to jconfig.h and the makefile to Makefile (or +whatever your system uses as the standard makefile name). For more info see +the appropriate system-specific hints section near the end of this file. + + +Configuring the software by hand +-------------------------------- + +First, generate a jconfig.h file. If you are moderately familiar with C, +the comments in jconfig.doc should be enough information to do this; just +copy jconfig.doc to jconfig.h and edit it appropriately. Otherwise, you may +prefer to use the ckconfig.c program. You will need to compile and execute +ckconfig.c by hand --- we hope you know at least enough to do that. +ckconfig.c may not compile the first try (in fact, the whole idea is for it +to fail if anything is going to). If you get compile errors, fix them by +editing ckconfig.c according to the directions given in ckconfig.c. Once +you get it to run, it will write a suitable jconfig.h file, and will also +print out some advice about which makefile to use. + +You may also want to look at the canned jconfig files, if there is one for a +system similar to yours. + +Second, select a makefile and copy it to Makefile (or whatever your system +uses as the standard makefile name). The most generic makefiles we provide +are + makefile.ansi: if your C compiler supports function prototypes + makefile.unix: if not. +(You have function prototypes if ckconfig.c put "#define HAVE_PROTOTYPES" +in jconfig.h.) You may want to start from one of the other makefiles if +there is one for a system similar to yours. + +Look over the selected Makefile and adjust options as needed. In particular +you may want to change the CC and CFLAGS definitions. For instance, if you +are using GCC, set CC=gcc. If you had to use any compiler switches to get +ckconfig.c to work, make sure the same switches are in CFLAGS. + +If you are on a system that doesn't use makefiles, you'll need to set up +project files (or whatever you do use) to compile all the source files and +link them into executable files cjpeg, djpeg, jpegtran, rdjpgcom, and wrjpgcom. +See the file lists in any of the makefiles to find out which files go into +each program. Note that the provided makefiles all make a "library" file +libjpeg first, but you don't have to do that if you don't want to; the file +lists identify which source files are actually needed for compression, +decompression, or both. As a last resort, you can make a batch script that +just compiles everything and links it all together; makefile.vms is an example +of this (it's for VMS systems that have no make-like utility). + +Here are comments about some specific configuration decisions you'll +need to make: + +Command line style +------------------ + +These programs can use a Unix-like command line style which supports +redirection and piping, like this: + cjpeg inputfile >outputfile + cjpeg outputfile + source program | cjpeg >outputfile +The simpler "two file" command line style is just + cjpeg inputfile outputfile +You may prefer the two-file style, particularly if you don't have pipes. + +You MUST use two-file style on any system that doesn't cope well with binary +data fed through stdin/stdout; this is true for some MS-DOS compilers, for +example. If you're not on a Unix system, it's safest to assume you need +two-file style. (But if your compiler provides either the Posix-standard +fdopen() library routine or a Microsoft-compatible setmode() routine, you +can safely use the Unix command line style, by defining USE_FDOPEN or +USE_SETMODE respectively.) + +To use the two-file style, make jconfig.h say "#define TWO_FILE_COMMANDLINE". + +Selecting a memory manager +-------------------------- + +The IJG code is capable of working on images that are too big to fit in main +memory; data is swapped out to temporary files as necessary. However, the +code to do this is rather system-dependent. We provide five different +memory managers: + +* jmemansi.c This version uses the ANSI-standard library routine tmpfile(), + which not all non-ANSI systems have. On some systems + tmpfile() may put the temporary file in a non-optimal + location; if you don't like what it does, use jmemname.c. + +* jmemname.c This version creates named temporary files. For anything + except a Unix machine, you'll need to configure the + select_file_name() routine appropriately; see the comments + near the head of jmemname.c. If you use this version, define + NEED_SIGNAL_CATCHER in jconfig.h to make sure the temp files + are removed if the program is aborted. + +* jmemnobs.c (That stands for No Backing Store :-).) This will compile on + almost any system, but it assumes you have enough main memory + or virtual memory to hold the biggest images you work with. + +* jmemdos.c This should be used with most 16-bit MS-DOS compilers. + See the system-specific notes about MS-DOS for more info. + IMPORTANT: if you use this, define USE_MSDOS_MEMMGR in + jconfig.h, and include the assembly file jmemdosa.asm in the + programs. The supplied makefiles and jconfig files for + 16-bit MS-DOS compilers already do both. + +* jmemmac.c Custom version for Apple Macintosh; see the system-specific + notes for Macintosh for more info. + +To use a particular memory manager, change the SYSDEPMEM variable in your +makefile to equal the corresponding object file name (for example, jmemansi.o +or jmemansi.obj for jmemansi.c). + +If you have plenty of (real or virtual) main memory, just use jmemnobs.c. +"Plenty" means about ten bytes for every pixel in the largest images +you plan to process, so a lot of systems don't meet this criterion. +If yours doesn't, try jmemansi.c first. If that doesn't compile, you'll have +to use jmemname.c; be sure to adjust select_file_name() for local conditions. +You may also need to change unlink() to remove() in close_backing_store(). + +Except with jmemnobs.c or jmemmac.c, you need to adjust the DEFAULT_MAX_MEM +setting to a reasonable value for your system (either by adding a #define for +DEFAULT_MAX_MEM to jconfig.h, or by adding a -D switch to the Makefile). +This value limits the amount of data space the program will attempt to +allocate. Code and static data space isn't counted, so the actual memory +needs for cjpeg or djpeg are typically 100 to 150Kb more than the max-memory +setting. Larger max-memory settings reduce the amount of I/O needed to +process a large image, but too large a value can result in "insufficient +memory" failures. On most Unix machines (and other systems with virtual +memory), just set DEFAULT_MAX_MEM to several million and forget it. At the +other end of the spectrum, for MS-DOS machines you probably can't go much +above 300K to 400K. (On MS-DOS the value refers to conventional memory only. +Extended/expanded memory is handled separately by jmemdos.c.) + + +BUILDING THE SOFTWARE +===================== + +Now you should be able to compile the software. Just say "make" (or +whatever's necessary to start the compilation). Have a cup of coffee. + +Here are some things that could go wrong: + +If your compiler complains about undefined structures, you should be able to +shut it up by putting "#define INCOMPLETE_TYPES_BROKEN" in jconfig.h. + +If you have trouble with missing system include files or inclusion of the +wrong ones, read jinclude.h. This shouldn't happen if you used configure +or ckconfig.c to set up jconfig.h. + +There are a fair number of routines that do not use all of their parameters; +some compilers will issue warnings about this, which you can ignore. There +are also a few configuration checks that may give "unreachable code" warnings. +Any other warning deserves investigation. + +If you don't have a getenv() library routine, define NO_GETENV. + +Also see the system-specific hints, below. + + +TESTING THE SOFTWARE +==================== + +As a quick test of functionality we've included a small sample image in +several forms: + testorig.jpg Starting point for the djpeg tests. + testimg.ppm The output of djpeg testorig.jpg + testimg.bmp The output of djpeg -bmp -colors 256 testorig.jpg + testimg.jpg The output of cjpeg testimg.ppm + testprog.jpg Progressive-mode equivalent of testorig.jpg. + testimgp.jpg The output of cjpeg -progressive -optimize testimg.ppm +(The first- and second-generation .jpg files aren't identical since JPEG is +lossy.) If you can generate duplicates of the testimg* files then you +probably have working programs. + +With most of the makefiles, "make test" will perform the necessary +comparisons. + +If you're using a makefile that doesn't provide the test option, run djpeg +and cjpeg by hand and compare the output files to testimg* with whatever +binary file comparison tool you have. The files should be bit-for-bit +identical. + +If the programs complain "MAX_ALLOC_CHUNK is wrong, please fix", then you +need to reduce MAX_ALLOC_CHUNK to a value that fits in type size_t. +Try adding "#define MAX_ALLOC_CHUNK 65520L" to jconfig.h. A less likely +configuration error is "ALIGN_TYPE is wrong, please fix": defining ALIGN_TYPE +as long should take care of that one. + +If the cjpeg test run fails with "Missing Huffman code table entry", it's a +good bet that you needed to define RIGHT_SHIFT_IS_UNSIGNED. Go back to the +configuration step and run ckconfig.c. (This is a good plan for any other +test failure, too.) + +If you are using Unix (one-file) command line style on a non-Unix system, +it's a good idea to check that binary I/O through stdin/stdout actually +works. You should get the same results from "djpeg out.ppm" +as from "djpeg -outfile out.ppm testorig.jpg". Note that the makefiles all +use the latter style and therefore do not exercise stdin/stdout! If this +check fails, try recompiling with USE_SETMODE or USE_FDOPEN defined. +If it still doesn't work, better use two-file style. + +If you chose a memory manager other than jmemnobs.c, you should test that +temporary-file usage works. Try "djpeg -bmp -colors 256 -max 0 testorig.jpg" +and make sure its output matches testimg.bmp. If you have any really large +images handy, try compressing them with -optimize and/or decompressing with +-colors 256 to make sure your DEFAULT_MAX_MEM setting is not too large. + +NOTE: this is far from an exhaustive test of the JPEG software; some modules, +such as 1-pass color quantization, are not exercised at all. It's just a +quick test to give you some confidence that you haven't missed something +major. + + +INSTALLING THE SOFTWARE +======================= + +Once you're done with the above steps, you can install the software by +copying the executable files (cjpeg, djpeg, jpegtran, rdjpgcom, and wrjpgcom) +to wherever you normally install programs. On Unix systems, you'll also want +to put the man pages (cjpeg.1, djpeg.1, jpegtran.1, rdjpgcom.1, wrjpgcom.1) +in the man-page directory. The pre-fab makefiles don't support this step +since there's such a wide variety of installation procedures on different +systems. + +If you generated a Makefile with the "configure" script, you can just say + make install +to install the programs and their man pages into the standard places. +(You'll probably need to be root to do this.) We recommend first saying + make -n install +to see where configure thought the files should go. You may need to edit +the Makefile, particularly if your system's conventions for man page +filenames don't match what configure expects. + +If you want to install the IJG library itself, for use in compiling other +programs besides ours, then you need to put the four include files + jpeglib.h jerror.h jconfig.h jmorecfg.h +into your include-file directory, and put the library file libjpeg.a +(extension may vary depending on system) wherever library files go. +If you generated a Makefile with "configure", it will do what it thinks +is the right thing if you say + make install-lib + + +OPTIONAL STUFF +============== + +Progress monitor: + +If you like, you can #define PROGRESS_REPORT (in jconfig.h) to enable display +of percent-done progress reports. The routine provided in cdjpeg.c merely +prints percentages to stderr, but you can customize it to do something +fancier. + +Utah RLE file format support: + +We distribute the software with support for RLE image files (Utah Raster +Toolkit format) disabled, because the RLE support won't compile without the +Utah library. If you have URT version 3.1 or later, you can enable RLE +support as follows: + 1. #define RLE_SUPPORTED in jconfig.h. + 2. Add a -I option to CFLAGS in the Makefile for the directory + containing the URT .h files (typically the "include" + subdirectory of the URT distribution). + 3. Add -L... -lrle to LDLIBS in the Makefile, where ... specifies + the directory containing the URT "librle.a" file (typically the + "lib" subdirectory of the URT distribution). + +Support for 12-bit-deep pixel data: + +The JPEG standard allows either 8-bit or 12-bit data precision. (For color, +this means 8 or 12 bits per channel, of course.) If you need to work with +deeper than 8-bit data, you can compile the IJG code for 12-bit operation. +To do so: + 1. In jmorecfg.h, define BITS_IN_JSAMPLE as 12 rather than 8. + 2. In jconfig.h, undefine BMP_SUPPORTED, RLE_SUPPORTED, and TARGA_SUPPORTED, + because the code for those formats doesn't handle 12-bit data and won't + even compile. (The PPM code does work, as explained below. The GIF + code works too; it scales 8-bit GIF data to and from 12-bit depth + automatically.) + 3. Compile. Don't expect "make test" to pass, since the supplied test + files are for 8-bit data. + +Currently, 12-bit support does not work on 16-bit-int machines. + +Note that a 12-bit version will not read 8-bit JPEG files, nor vice versa; +so you'll want to keep around a regular 8-bit compilation as well. +(Run-time selection of data depth, to allow a single copy that does both, +is possible but would probably slow things down considerably; it's very low +on our to-do list.) + +The PPM reader (rdppm.c) can read 12-bit data from either text-format or +binary-format PPM and PGM files. Binary-format PPM/PGM files which have a +maxval greater than 255 are assumed to use 2 bytes per sample, LSB first +(little-endian order). As of early 1995, 2-byte binary format is not +officially supported by the PBMPLUS library, but it is expected that a +future release of PBMPLUS will support it. Note that the PPM reader will +read files of any maxval regardless of the BITS_IN_JSAMPLE setting; incoming +data is automatically rescaled to either maxval=255 or maxval=4095 as +appropriate for the cjpeg bit depth. + +The PPM writer (wrppm.c) will normally write 2-byte binary PPM or PGM +format, maxval 4095, when compiled with BITS_IN_JSAMPLE=12. Since this +format is not yet widely supported, you can disable it by compiling wrppm.c +with PPM_NORAWWORD defined; then the data is scaled down to 8 bits to make a +standard 1-byte/sample PPM or PGM file. (Yes, this means still another copy +of djpeg to keep around. But hopefully you won't need it for very long. +Poskanzer's supposed to get that new PBMPLUS release out Real Soon Now.) + +Of course, if you are working with 12-bit data, you probably have it stored +in some other, nonstandard format. In that case you'll probably want to +write your own I/O modules to read and write your format. + +Note that a 12-bit version of cjpeg always runs in "-optimize" mode, in +order to generate valid Huffman tables. This is necessary because our +default Huffman tables only cover 8-bit data. + +Removing code: + +If you need to make a smaller version of the JPEG software, some optional +functions can be removed at compile time. See the xxx_SUPPORTED #defines in +jconfig.h and jmorecfg.h. If at all possible, we recommend that you leave in +decoder support for all valid JPEG files, to ensure that you can read anyone's +output. Taking out support for image file formats that you don't use is the +most painless way to make the programs smaller. Another possibility is to +remove some of the DCT methods: in particular, the "IFAST" method may not be +enough faster than the others to be worth keeping on your machine. (If you +do remove ISLOW or IFAST, be sure to redefine JDCT_DEFAULT or JDCT_FASTEST +to a supported method, by adding a #define in jconfig.h.) + + +OPTIMIZATION +============ + +Unless you own a Cray, you'll probably be interested in making the JPEG +software go as fast as possible. This section covers some machine-dependent +optimizations you may want to try. We suggest that before trying any of +this, you first get the basic installation to pass the self-test step. +Repeat the self-test after any optimization to make sure that you haven't +broken anything. + +The integer DCT routines perform a lot of multiplications. These +multiplications must yield 32-bit results, but none of their input values +are more than 16 bits wide. On many machines, notably the 680x0 and 80x86 +CPUs, a 16x16=>32 bit multiply instruction is faster than a full 32x32=>32 +bit multiply. Unfortunately there is no portable way to specify such a +multiplication in C, but some compilers can generate one when you use the +right combination of casts. See the MULTIPLYxxx macro definitions in +jdct.h. If your compiler makes "int" be 32 bits and "short" be 16 bits, +defining SHORTxSHORT_32 is fairly likely to work. When experimenting with +alternate definitions, be sure to test not only whether the code still works +(use the self-test), but also whether it is actually faster --- on some +compilers, alternate definitions may compute the right answer, yet be slower +than the default. Timing cjpeg on a large PGM (grayscale) input file is the +best way to check this, as the DCT will be the largest fraction of the runtime +in that mode. (Note: some of the distributed compiler-specific jconfig files +already contain #define switches to select appropriate MULTIPLYxxx +definitions.) + +If your machine has sufficiently fast floating point hardware, you may find +that the float DCT method is faster than the integer DCT methods, even +after tweaking the integer multiply macros. In that case you may want to +make the float DCT be the default method. (The only objection to this is +that float DCT results may vary slightly across machines.) To do that, add +"#define JDCT_DEFAULT JDCT_FLOAT" to jconfig.h. Even if you don't change +the default, you should redefine JDCT_FASTEST, which is the method selected +by djpeg's -fast switch. Don't forget to update the documentation files +(usage.doc and/or cjpeg.1, djpeg.1) to agree with what you've done. + +If access to "short" arrays is slow on your machine, it may be a win to +define type JCOEF as int rather than short. This will cost a good deal of +memory though, particularly in some multi-pass modes, so don't do it unless +you have memory to burn and short is REALLY slow. + +If your compiler can compile function calls in-line, make sure the INLINE +macro in jmorecfg.h is defined as the keyword that marks a function +inline-able. Some compilers have a switch that tells the compiler to inline +any function it thinks is profitable (e.g., -finline-functions for gcc). +Enabling such a switch is likely to make the compiled code bigger but faster. + +In general, it's worth trying the maximum optimization level of your compiler, +and experimenting with any optional optimizations such as loop unrolling. +(Unfortunately, far too many compilers have optimizer bugs ... be prepared to +back off if the code fails self-test.) If you do any experimentation along +these lines, please report the optimal settings to jpeg-info@uunet.uu.net so +we can mention them in future releases. Be sure to specify your machine and +compiler version. + + +HINTS FOR SPECIFIC SYSTEMS +========================== + +We welcome reports on changes needed for systems not mentioned here. Submit +'em to jpeg-info@uunet.uu.net. Also, if configure or ckconfig.c is wrong +about how to configure the JPEG software for your system, please let us know. + + +Acorn RISC OS: + +(Thanks to Simon Middleton for these hints on compiling with Desktop C.) +After renaming the files according to Acorn conventions, take a copy of +makefile.ansi, change all occurrences of 'libjpeg.a' to 'libjpeg.o' and +change these definitions as indicated: + +CFLAGS= -throwback -IC: -Wn +LDLIBS=C:o.Stubs +SYSDEPMEM=jmemansi.o +LN=Link +AR=LibFile -c -o + +Also add a new line '.c.o:; $(cc) $< $(cflags) -c -o $@'. Remove the +lines '$(RM) libjpeg.o' and '$(AR2) libjpeg.o' and the 'jconfig.h' +dependency section. + +Copy jconfig.doc to jconfig.h. Edit jconfig.h to define TWO_FILE_COMMANDLINE +and CHAR_IS_UNSIGNED. + +Run the makefile using !AMU not !Make. If you want to use the 'clean' and +'test' makefile entries then you will have to fiddle with the syntax a bit +and rename the test files. + + +Amiga: + +SAS C 6.50 reportedly is too buggy to compile the IJG code properly. +A patch to update to 6.51 is available from SAS or AmiNet FTP sites. + +The supplied config files are set up to use jmemname.c as the memory +manager, with temporary files being created on the device named by +"JPEGTMP:". + + +Atari ST/STE/TT: + +Copy the project files makcjpeg.st, makdjpeg.st, maktjpeg.st, and makljpeg.st +to cjpeg.prj, djpeg.prj, jpegtran.prj, and libjpeg.prj respectively. The +project files should work as-is with Pure C. For Turbo C, change library +filenames "pc..." to "tc..." in each project file. Note that libjpeg.prj +selects jmemansi.c as the recommended memory manager. You'll probably want to +adjust the DEFAULT_MAX_MEM setting --- you want it to be a couple hundred K +less than your normal free memory. Put "#define DEFAULT_MAX_MEM nnnn" into +jconfig.h to do this. + +To use the 68881/68882 coprocessor for the floating point DCT, add the +compiler option "-8" to the project files and replace pcfltlib.lib with +pc881lib.lib in cjpeg.prj and djpeg.prj. Or if you don't have a +coprocessor, you may prefer to remove the float DCT code by undefining +DCT_FLOAT_SUPPORTED in jmorecfg.h (since without a coprocessor, the float +code will be too slow to be useful). In that case, you can delete +pcfltlib.lib from the project files. + +Note that you must make libjpeg.lib before making cjpeg.ttp, djpeg.ttp, +or jpegtran.ttp. You'll have to perform the self-test by hand. + +We haven't bothered to include project files for rdjpgcom and wrjpgcom. +Those source files should just be compiled by themselves; they don't +depend on the JPEG library. + +There is a bug in some older versions of the Turbo C library which causes the +space used by temporary files created with "tmpfile()" not to be freed after +an abnormal program exit. If you check your disk afterwards, you will find +cluster chains that are allocated but not used by a file. This should not +happen in cjpeg/djpeg/jpegtran, since we enable a signal catcher to explicitly +close temp files before exiting. But if you use the JPEG library with your +own code, be sure to supply a signal catcher, or else use a different +system-dependent memory manager. + + +Cray: + +Should you be so fortunate as to be running JPEG on a Cray YMP, there is a +compiler bug in old versions of Cray's Standard C (prior to 3.1). If you +still have an old compiler, you'll need to insert a line reading +"#pragma novector" just before the loop + for (i = 1; i <= (int) htbl->bits[l]; i++) + huffsize[p++] = (char) l; +in fix_huff_tbl (in V5beta1, line 204 of jchuff.c and line 176 of jdhuff.c). +[This bug may or may not still occur with the current IJG code, but it's +probably a dead issue anyway...] + + +HP-UX: + +If you have HP-UX 7.05 or later with the "software development" C compiler, +you should run the compiler in ANSI mode. If using the configure script, +say + ./configure CC='cc -Aa' +(or -Ae if you prefer). If configuring by hand, use makefile.ansi and add +"-Aa" to the CFLAGS line in the makefile. + +If you have a pre-7.05 system, or if you are using the non-ANSI C compiler +delivered with a minimum HP-UX system, then you must use makefile.unix +(and do NOT add -Aa); or just run configure without the CC option. + +On HP 9000 series 800 machines, the HP C compiler is buggy in revisions prior +to A.08.07. If you get complaints about "not a typedef name", you'll have to +use makefile.unix, or run configure without the CC option. + + +Macintosh, generic comments: + +The supplied user-interface files (cjpeg.c, djpeg.c, etc) are set up to +provide a Unix-style command line interface. You can use this interface on +the Mac by means of the ccommand() library routine provided by Metrowerks +CodeWarrior or Think C. This is only appropriate for testing the library, +however; to make a user-friendly equivalent of cjpeg/djpeg you'd really want +to develop a Mac-style user interface. There isn't a complete example +available at the moment, but there are some helpful starting points: +1. Sam Bushell's free "To JPEG" applet provides drag-and-drop conversion to +JPEG under System 7 and later. This only illustrates how to use the +compression half of the library, but it does a very nice job of that part. +The CodeWarrior source code is available from http://www.pobox.com/~jsam. +2. Jim Brunner prepared a Mac-style user interface for both compression and +decompression. Unfortunately, it hasn't been updated since IJG v4, and +the library's API has changed considerably since then. Still it may be of +some help, particularly as a guide to compiling the IJG code under Think C. +Jim's code is available from the Info-Mac archives, at sumex-aim.stanford.edu +or mirrors thereof; see file /info-mac/dev/src/jpeg-convert-c.hqx. + +jmemmac.c is the recommended memory manager back end for Macintosh. It uses +NewPtr/DisposePtr instead of malloc/free, and has a Mac-specific +implementation of jpeg_mem_available(). It also creates temporary files that +follow Mac conventions. (That part of the code relies on System-7-or-later OS +functions. See the comments in jmemmac.c if you need to run it on System 6.) +NOTE that USE_MAC_MEMMGR must be defined in jconfig.h to use jmemmac.c. + +You can also use jmemnobs.c, if you don't care about handling images larger +than available memory. If you use any memory manager back end other than +jmemmac.c, we recommend replacing "malloc" and "free" by "NewPtr" and +"DisposePtr", because Mac C libraries often have peculiar implementations of +malloc/free. (For instance, free() may not return the freed space to the +Mac Memory Manager. This is undesirable for the IJG code because jmemmgr.c +already clumps space requests.) + + +Macintosh, Metrowerks CodeWarrior: + +The Unix-command-line-style interface can be used by defining USE_CCOMMAND. +You'll also need to define TWO_FILE_COMMANDLINE to avoid stdin/stdout. +This means that when using the cjpeg/djpeg programs, you'll have to type the +input and output file names in the "Arguments" text-edit box, rather than +using the file radio buttons. (Perhaps USE_FDOPEN or USE_SETMODE would +eliminate the problem, but I haven't heard from anyone who's tried it.) + +On 680x0 Macs, Metrowerks defines type "double" as a 10-byte IEEE extended +float. jmemmgr.c won't like this: it wants sizeof(ALIGN_TYPE) to be a power +of 2. Add "#define ALIGN_TYPE long" to jconfig.h to eliminate the complaint. + +The supplied configuration file jconfig.mac can be used for your jconfig.h; +it includes all the recommended symbol definitions. If you have AppleScript +installed, you can run the supplied script makeproj.mac to create CodeWarrior +project files for the library and the testbed applications, then build the +library and applications. (Thanks to Dan Sears and Don Agro for this nifty +hack, which saves us from trying to maintain CodeWarrior project files as part +of the IJG distribution...) + + +Macintosh, Think C: + +The documentation in Jim Brunner's "JPEG Convert" source code (see above) +includes detailed build instructions for Think C; it's probably somewhat +out of date for the current release, but may be helpful. + +If you want to build the minimal command line version, proceed as follows. +You'll have to prepare project files for the programs; we don't include any +in the distribution since they are not text files. Use the file lists in +any of the supplied makefiles as a guide. Also add the ANSI and Unix C +libraries in a separate segment. You may need to divide the JPEG files into +more than one segment; we recommend dividing compression and decompression +modules. Define USE_CCOMMAND in jconfig.h so that the ccommand() routine is +called. You must also define TWO_FILE_COMMANDLINE because stdin/stdout +don't handle binary data correctly. + +On 680x0 Macs, Think C defines type "double" as a 12-byte IEEE extended float. +jmemmgr.c won't like this: it wants sizeof(ALIGN_TYPE) to be a power of 2. +Add "#define ALIGN_TYPE long" to jconfig.h to eliminate the complaint. + +jconfig.mac should work as a jconfig.h configuration file for Think C, +but the makeproj.mac AppleScript script is specific to CodeWarrior. Sorry. + + +MIPS R3000: + +MIPS's cc version 1.31 has a rather nasty optimization bug. Don't use -O +if you have that compiler version. (Use "cc -V" to check the version.) +Note that the R3000 chip is found in workstations from DEC and others. + + +MS-DOS, generic comments for 16-bit compilers: + +The IJG code is designed to work well in 80x86 "small" or "medium" memory +models (i.e., data pointers are 16 bits unless explicitly declared "far"; +code pointers can be either size). You may be able to use small model to +compile cjpeg or djpeg by itself, but you will probably have to use medium +model for any larger application. This won't make much difference in +performance. You *will* take a noticeable performance hit if you use a +large-data memory model, and you should avoid "huge" model if at all +possible. Be sure that NEED_FAR_POINTERS is defined in jconfig.h if you use +a small-data memory model; be sure it is NOT defined if you use a large-data +model. (The supplied makefiles and jconfig files for Borland and Microsoft C +compile in medium model and define NEED_FAR_POINTERS.) + +The DOS-specific memory manager, jmemdos.c, should be used if possible. +It needs some assembly-code routines which are in jmemdosa.asm; make sure +your makefile assembles that file and includes it in the library. If you +don't have a suitable assembler, you can get pre-assembled object files for +jmemdosa by FTP from ftp.uu.net:/graphics/jpeg/jdosaobj.zip. (DOS-oriented +distributions of the IJG source code often include these object files.) + +When using jmemdos.c, jconfig.h must define USE_MSDOS_MEMMGR and must set +MAX_ALLOC_CHUNK to less than 64K (65520L is a typical value). If your +C library's far-heap malloc() can't allocate blocks that large, reduce +MAX_ALLOC_CHUNK to whatever it can handle. + +If you can't use jmemdos.c for some reason --- for example, because you +don't have an assembler to assemble jmemdosa.asm --- you'll have to fall +back to jmemansi.c or jmemname.c. You'll probably still need to set +MAX_ALLOC_CHUNK in jconfig.h, because most DOS C libraries won't malloc() +more than 64K at a time. IMPORTANT: if you use jmemansi.c or jmemname.c, +you will have to compile in a large-data memory model in order to get the +right stdio library. Too bad. + +wrjpgcom needs to be compiled in large model, because it malloc()s a 64KB +work area to hold the comment text. If your C library's malloc can't +handle that, reduce MAX_COM_LENGTH as necessary in wrjpgcom.c. + +Most MS-DOS compilers treat stdin/stdout as text files, so you must use +two-file command line style. But if your compiler has either fdopen() or +setmode(), you can use one-file style if you like. To do this, define +USE_SETMODE or USE_FDOPEN so that stdin/stdout will be set to binary mode. +(USE_SETMODE seems to work with more DOS compilers than USE_FDOPEN.) You +should test that I/O through stdin/stdout produces the same results as I/O +to explicitly named files... the "make test" procedures in the supplied +makefiles do NOT use stdin/stdout. + + +MS-DOS, generic comments for 32-bit compilers: + +None of the above comments about memory models apply if you are using a +32-bit flat-memory-space environment, such as DJGPP or Watcom C. (And you +should use one if you have it, as performance will be much better than +8086-compatible code!) For flat-memory-space compilers, do NOT define +NEED_FAR_POINTERS, and do NOT use jmemdos.c. Use jmemnobs.c if the +environment supplies adequate virtual memory, otherwise use jmemansi.c or +jmemname.c. + +You'll still need to be careful about binary I/O through stdin/stdout. +See the last paragraph of the previous section. + + +MS-DOS, Borland C: + +Be sure to convert all the source files to DOS text format (CR/LF newlines). +Although Borland C will often work OK with unmodified Unix (LF newlines) +source files, sometimes it will give bogus compile errors. +"Illegal character '#'" is the most common such error. (This is true with +Borland C 3.1, but perhaps is fixed in newer releases.) + +If you want one-file command line style, just undefine TWO_FILE_COMMANDLINE. +jconfig.bcc already includes #define USE_SETMODE to make this work. +(fdopen does not work correctly.) + + +MS-DOS, Microsoft C: + +makefile.mc6 works with Microsoft C, DOS Visual C++, etc. It should only +be used if you want to build a 16-bit (small or medium memory model) program. + +If you want one-file command line style, just undefine TWO_FILE_COMMANDLINE. +jconfig.mc6 already includes #define USE_SETMODE to make this work. +(fdopen does not work correctly.) + +Note that this makefile assumes that the working copy of itself is called +"makefile". If you want to call it something else, say "makefile.mak", +be sure to adjust the dependency line that reads "$(RFILE) : makefile". +Otherwise the make will fail because it doesn't know how to create "makefile". +Worse, some releases of Microsoft's make utilities give an incorrect error +message in this situation. + +Old versions of MS C fail with an "out of macro expansion space" error +because they can't cope with the macro TRACEMS8 (defined in jerror.h). +If this happens to you, the easiest solution is to change TRACEMS8 to +expand to nothing. You'll lose the ability to dump out JPEG coefficient +tables with djpeg -debug -debug, but at least you can compile. + +Original MS C 6.0 is very buggy; it compiles incorrect code unless you turn +off optimization entirely (remove -O from CFLAGS). 6.00A is better, but it +still generates bad code if you enable loop optimizations (-Ol or -Ox). + +MS C 8.0 crashes when compiling jquant1.c with optimization switch /Oo ... +which is on by default. To work around this bug, compile that one file +with /Oo-. + + +Microsoft Windows (all versions), generic comments: + +Some Windows system include files define typedef boolean as "unsigned char". +The IJG code also defines typedef boolean, but we make it "int" by default. +This doesn't affect the IJG programs because we don't import those Windows +include files. But if you use the JPEG library in your own program, and some +of your program's files import one definition of boolean while some import the +other, you can get all sorts of mysterious problems. A good preventive step +is to make the IJG library use "unsigned char" for boolean. To do that, +add something like this to your jconfig.h file: + /* Define "boolean" as unsigned char, not int, per Windows custom */ + #ifndef __RPCNDR_H__ /* don't conflict if rpcndr.h already read */ + typedef unsigned char boolean; + #endif + #define HAVE_BOOLEAN /* prevent jmorecfg.h from redefining it */ +(This is already in jconfig.vc, by the way.) + +windef.h contains the declarations + #define far + #define FAR far +Since jmorecfg.h tries to define FAR as empty, you may get a compiler +warning if you include both jpeglib.h and windef.h (which windows.h +includes). To suppress the warning, you can put "#ifndef FAR"/"#endif" +around the line "#define FAR" in jmorecfg.h. + +When using the library in a Windows application, you will almost certainly +want to modify or replace the error handler module jerror.c, since our +default error handler does a couple of inappropriate things: + 1. it tries to write error and warning messages on stderr; + 2. in event of a fatal error, it exits by calling exit(). + +A simple stopgap solution for problem 1 is to replace the line + fprintf(stderr, "%s\n", buffer); +(in output_message in jerror.c) with + MessageBox(GetActiveWindow(),buffer,"JPEG Error",MB_OK|MB_ICONERROR); +It's highly recommended that you at least do that much, since otherwise +error messages will disappear into nowhere. (Beginning with IJG v6b, this +code is already present in jerror.c; just define USE_WINDOWS_MESSAGEBOX in +jconfig.h to enable it.) + +The proper solution for problem 2 is to return control to your calling +application after a library error. This can be done with the setjmp/longjmp +technique discussed in libjpeg.doc and illustrated in example.c. (NOTE: +some older Windows C compilers provide versions of setjmp/longjmp that +don't actually work under Windows. You may need to use the Windows system +functions Catch and Throw instead.) + +The recommended memory manager under Windows is jmemnobs.c; in other words, +let Windows do any virtual memory management needed. You should NOT use +jmemdos.c nor jmemdosa.asm under Windows. + +For Windows 3.1, we recommend compiling in medium or large memory model; +for newer Windows versions, use a 32-bit flat memory model. (See the MS-DOS +sections above for more info about memory models.) In the 16-bit memory +models only, you'll need to put + #define MAX_ALLOC_CHUNK 65520L /* Maximum request to malloc() */ +into jconfig.h to limit allocation chunks to 64Kb. (Without that, you'd +have to use huge memory model, which slows things down unnecessarily.) +jmemnobs.c works without modification in large or flat memory models, but to +use medium model, you need to modify its jpeg_get_large and jpeg_free_large +routines to allocate far memory. In any case, you might like to replace +its calls to malloc and free with direct calls on Windows memory allocation +functions. + +You may also want to modify jdatasrc.c and jdatadst.c to use Windows file +operations rather than fread/fwrite. This is only necessary if your C +compiler doesn't provide a competent implementation of C stdio functions. + +You might want to tweak the RGB_xxx macros in jmorecfg.h so that the library +will accept or deliver color pixels in BGR sample order, not RGB; BGR order +is usually more convenient under Windows. Note that this change will break +the sample applications cjpeg/djpeg, but the library itself works fine. + + +Many people want to convert the IJG library into a DLL. This is reasonably +straightforward, but watch out for the following: + + 1. Don't try to compile as a DLL in small or medium memory model; use +large model, or even better, 32-bit flat model. Many places in the IJG code +assume the address of a local variable is an ordinary (not FAR) pointer; +that isn't true in a medium-model DLL. + + 2. Microsoft C cannot pass file pointers between applications and DLLs. +(See Microsoft Knowledge Base, PSS ID Number Q50336.) So jdatasrc.c and +jdatadst.c don't work if you open a file in your application and then pass +the pointer to the DLL. One workaround is to make jdatasrc.c/jdatadst.c +part of your main application rather than part of the DLL. + + 3. You'll probably need to modify the macros GLOBAL() and EXTERN() to +attach suitable linkage keywords to the exported routine names. Similarly, +you'll want to modify METHODDEF() and JMETHOD() to ensure function pointers +are declared in a way that lets application routines be called back through +the function pointers. These macros are in jmorecfg.h. Typical definitions +for a 16-bit DLL are: + #define GLOBAL(type) type _far _pascal _loadds _export + #define EXTERN(type) extern type _far _pascal _loadds + #define METHODDEF(type) static type _far _pascal + #define JMETHOD(type,methodname,arglist) \ + type (_far _pascal *methodname) arglist +For a 32-bit DLL you may want something like + #define GLOBAL(type) __declspec(dllexport) type + #define EXTERN(type) extern __declspec(dllexport) type +Although not all the GLOBAL routines are actually intended to be called by +the application, the performance cost of making them all DLL entry points is +negligible. + +The unmodified IJG library presents a very C-specific application interface, +so the resulting DLL is only usable from C or C++ applications. There has +been some talk of writing wrapper code that would present a simpler interface +usable from other languages, such as Visual Basic. This is on our to-do list +but hasn't been very high priority --- any volunteers out there? + + +Microsoft Windows, Borland C: + +The provided jconfig.bcc should work OK in a 32-bit Windows environment, +but you'll need to tweak it in a 16-bit environment (you'd need to define +NEED_FAR_POINTERS and MAX_ALLOC_CHUNK). Beware that makefile.bcc will need +alteration if you want to use it for Windows --- in particular, you should +use jmemnobs.c not jmemdos.c under Windows. + +Borland C++ 4.5 fails with an internal compiler error when trying to compile +jdmerge.c in 32-bit mode. If enough people complain, perhaps Borland will fix +it. In the meantime, the simplest known workaround is to add a redundant +definition of the variable range_limit in h2v1_merged_upsample(), at the head +of the block that handles odd image width (about line 268 in v6 jdmerge.c): + /* If image width is odd, do the last output column separately */ + if (cinfo->output_width & 1) { + register JSAMPLE * range_limit = cinfo->sample_range_limit; /* ADD THIS */ + cb = GETJSAMPLE(*inptr1); +Pretty bizarre, especially since the very similar routine h2v2_merged_upsample +doesn't trigger the bug. +Recent reports suggest that this bug does not occur with "bcc32a" (the +Pentium-optimized version of the compiler). + +Another report from a user of Borland C 4.5 was that incorrect code (leading +to a color shift in processed images) was produced if any of the following +optimization switch combinations were used: + -Ot -Og + -Ot -Op + -Ot -Om +So try backing off on optimization if you see such a problem. (Are there +several different releases all numbered "4.5"??) + + +Microsoft Windows, Microsoft Visual C++: + +jconfig.vc should work OK with any Microsoft compiler for a 32-bit memory +model. makefile.vc is intended for command-line use. (If you are using +the Developer Studio environment, you may prefer the DevStudio project +files; see below.) + +Some users feel that it's easier to call the library from C++ code if you +force VC++ to treat the library as C++ code, which you can do by renaming +all the *.c files to *.cpp (and adjusting the makefile to match). This +avoids the need to put extern "C" { ... } around #include "jpeglib.h" in +your C++ application. + + +Microsoft Windows, Microsoft Developer Studio: + +We include makefiles that should work as project files in DevStudio 4.2 or +later. There is a library makefile that builds the IJG library as a static +Win32 library, and an application makefile that builds the sample applications +as Win32 console applications. (Even if you only want the library, we +recommend building the applications so that you can run the self-test.) + +To use: +1. Copy jconfig.vc to jconfig.h, makelib.ds to jpeg.mak, and + makeapps.ds to apps.mak. (Note that the renaming is critical!) +2. Click on the .mak files to construct project workspaces. + (If you are using DevStudio more recent than 4.2, you'll probably + get a message saying that the makefiles are being updated.) +3. Build the library project, then the applications project. +4. Move the application .exe files from `app`\Release to an + appropriate location on your path. +5. To perform the self-test, execute the command line + NMAKE /f makefile.vc test + + +OS/2, Borland C++: + +Watch out for optimization bugs in older Borland compilers; you may need +to back off the optimization switch settings. See the comments in +makefile.bcc. + + +SGI: + +On some SGI systems, you may need to set "AR2= ar -ts" in the Makefile. +If you are using configure, you can do this by saying + ./configure RANLIB='ar -ts' +This change is not needed on all SGIs. Use it only if the make fails at the +stage of linking the completed programs. + +On the MIPS R4000 architecture (Indy, etc.), the compiler option "-mips2" +reportedly speeds up the float DCT method substantially, enough to make it +faster than the default int method (but still slower than the fast int +method). If you use -mips2, you may want to alter the default DCT method to +be float. To do this, put "#define JDCT_DEFAULT JDCT_FLOAT" in jconfig.h. + + +VMS: + +On an Alpha/VMS system with MMS, be sure to use the "/Marco=Alpha=1" +qualifier with MMS when building the JPEG package. + +VAX/VMS v5.5-1 may have problems with the test step of the build procedure +reporting differences when it compares the original and test images. If the +error points to the last block of the files, it is most likely bogus and may +be safely ignored. It seems to be because the files are Stream_LF and +Backup/Compare has difficulty with the (presumably) null padded files. +This problem was not observed on VAX/VMS v6.1 or AXP/VMS v6.1. diff --git a/Engine/lib/ljpeg/extras/jconfig.bcc b/Engine/lib/ljpeg/extras/jconfig.bcc new file mode 100644 index 000000000..c6c53ff63 --- /dev/null +++ b/Engine/lib/ljpeg/extras/jconfig.bcc @@ -0,0 +1,48 @@ +/* jconfig.bcc --- jconfig.h for Borland C (Turbo C) on MS-DOS or OS/2. */ +/* see jconfig.doc for explanations */ + +#define HAVE_PROTOTYPES +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT +/* #define void char */ +/* #define const */ +#undef CHAR_IS_UNSIGNED +#define HAVE_STDDEF_H +#define HAVE_STDLIB_H +#undef NEED_BSD_STRINGS +#undef NEED_SYS_TYPES_H +#ifdef __MSDOS__ +#define NEED_FAR_POINTERS /* for small or medium memory model */ +#endif +#undef NEED_SHORT_EXTERNAL_NAMES +#undef INCOMPLETE_TYPES_BROKEN /* this assumes you have -w-stu in CFLAGS */ + +#ifdef JPEG_INTERNALS + +#undef RIGHT_SHIFT_IS_UNSIGNED + +#ifdef __MSDOS__ +#define USE_MSDOS_MEMMGR /* Define this if you use jmemdos.c */ +#define MAX_ALLOC_CHUNK 65520L /* Maximum request to malloc() */ +#define USE_FMEM /* Borland has _fmemcpy() and _fmemset() */ +#endif + +#endif /* JPEG_INTERNALS */ + +#ifdef JPEG_CJPEG_DJPEG + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +#undef RLE_SUPPORTED /* Utah RLE image file format */ +#define TARGA_SUPPORTED /* Targa image file format */ + +#define TWO_FILE_COMMANDLINE +#define USE_SETMODE /* Borland has setmode() */ +#ifdef __MSDOS__ +#define NEED_SIGNAL_CATCHER /* Define this if you use jmemdos.c */ +#endif +#undef DONT_USE_B_MODE +#undef PROGRESS_REPORT /* optional */ + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/Engine/lib/ljpeg/extras/jconfig.cfg b/Engine/lib/ljpeg/extras/jconfig.cfg new file mode 100644 index 000000000..36a04fa84 --- /dev/null +++ b/Engine/lib/ljpeg/extras/jconfig.cfg @@ -0,0 +1,44 @@ +/* jconfig.cfg --- source file edited by configure script */ +/* see jconfig.doc for explanations */ + +#undef HAVE_PROTOTYPES +#undef HAVE_UNSIGNED_CHAR +#undef HAVE_UNSIGNED_SHORT +#undef void +#undef const +#undef CHAR_IS_UNSIGNED +#undef HAVE_STDDEF_H +#undef HAVE_STDLIB_H +#undef NEED_BSD_STRINGS +#undef NEED_SYS_TYPES_H +#undef NEED_FAR_POINTERS +#undef NEED_SHORT_EXTERNAL_NAMES +/* Define this if you get warnings about undefined structures. */ +#undef INCOMPLETE_TYPES_BROKEN + +#ifdef JPEG_INTERNALS + +#undef RIGHT_SHIFT_IS_UNSIGNED +#undef INLINE +/* These are for configuring the JPEG memory manager. */ +#undef DEFAULT_MAX_MEM +#undef NO_MKTEMP + +#endif /* JPEG_INTERNALS */ + +#ifdef JPEG_CJPEG_DJPEG + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +#undef RLE_SUPPORTED /* Utah RLE image file format */ +#define TARGA_SUPPORTED /* Targa image file format */ + +#undef TWO_FILE_COMMANDLINE +#undef NEED_SIGNAL_CATCHER +#undef DONT_USE_B_MODE + +/* Define this if you want percent-done progress reports from cjpeg/djpeg. */ +#undef PROGRESS_REPORT + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/Engine/lib/ljpeg/extras/jconfig.dj b/Engine/lib/ljpeg/extras/jconfig.dj new file mode 100644 index 000000000..f759a9dbd --- /dev/null +++ b/Engine/lib/ljpeg/extras/jconfig.dj @@ -0,0 +1,38 @@ +/* jconfig.dj --- jconfig.h for DJGPP (Delorie's GNU C port) on MS-DOS. */ +/* see jconfig.doc for explanations */ + +#define HAVE_PROTOTYPES +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT +/* #define void char */ +/* #define const */ +#undef CHAR_IS_UNSIGNED +#define HAVE_STDDEF_H +#define HAVE_STDLIB_H +#undef NEED_BSD_STRINGS +#undef NEED_SYS_TYPES_H +#undef NEED_FAR_POINTERS /* DJGPP uses flat 32-bit addressing */ +#undef NEED_SHORT_EXTERNAL_NAMES +#undef INCOMPLETE_TYPES_BROKEN + +#ifdef JPEG_INTERNALS + +#undef RIGHT_SHIFT_IS_UNSIGNED + +#endif /* JPEG_INTERNALS */ + +#ifdef JPEG_CJPEG_DJPEG + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +#undef RLE_SUPPORTED /* Utah RLE image file format */ +#define TARGA_SUPPORTED /* Targa image file format */ + +#undef TWO_FILE_COMMANDLINE /* optional */ +#define USE_SETMODE /* Needed to make one-file style work in DJGPP */ +#undef NEED_SIGNAL_CATCHER /* Define this if you use jmemname.c */ +#undef DONT_USE_B_MODE +#undef PROGRESS_REPORT /* optional */ + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/Engine/lib/ljpeg/extras/jconfig.doc b/Engine/lib/ljpeg/extras/jconfig.doc new file mode 100644 index 000000000..c18d1c064 --- /dev/null +++ b/Engine/lib/ljpeg/extras/jconfig.doc @@ -0,0 +1,155 @@ +/* + * jconfig.doc + * + * Copyright (C) 1991-1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file documents the configuration options that are required to + * customize the JPEG software for a particular system. + * + * The actual configuration options for a particular installation are stored + * in jconfig.h. On many machines, jconfig.h can be generated automatically + * or copied from one of the "canned" jconfig files that we supply. But if + * you need to generate a jconfig.h file by hand, this file tells you how. + * + * DO NOT EDIT THIS FILE --- IT WON'T ACCOMPLISH ANYTHING. + * EDIT A COPY NAMED JCONFIG.H. + */ + + +/* + * These symbols indicate the properties of your machine or compiler. + * #define the symbol if yes, #undef it if no. + */ + +/* Does your compiler support function prototypes? + * (If not, you also need to use ansi2knr, see install.doc) + */ +#define HAVE_PROTOTYPES + +/* Does your compiler support the declaration "unsigned char" ? + * How about "unsigned short" ? + */ +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT + +/* Define "void" as "char" if your compiler doesn't know about type void. + * NOTE: be sure to define void such that "void *" represents the most general + * pointer type, e.g., that returned by malloc(). + */ +/* #define void char */ + +/* Define "const" as empty if your compiler doesn't know the "const" keyword. + */ +/* #define const */ + +/* Define this if an ordinary "char" type is unsigned. + * If you're not sure, leaving it undefined will work at some cost in speed. + * If you defined HAVE_UNSIGNED_CHAR then the speed difference is minimal. + */ +#undef CHAR_IS_UNSIGNED + +/* Define this if your system has an ANSI-conforming file. + */ +#define HAVE_STDDEF_H + +/* Define this if your system has an ANSI-conforming file. + */ +#define HAVE_STDLIB_H + +/* Define this if your system does not have an ANSI/SysV , + * but does have a BSD-style . + */ +#undef NEED_BSD_STRINGS + +/* Define this if your system does not provide typedef size_t in any of the + * ANSI-standard places (stddef.h, stdlib.h, or stdio.h), but places it in + * instead. + */ +#undef NEED_SYS_TYPES_H + +/* For 80x86 machines, you need to define NEED_FAR_POINTERS, + * unless you are using a large-data memory model or 80386 flat-memory mode. + * On less brain-damaged CPUs this symbol must not be defined. + * (Defining this symbol causes large data structures to be referenced through + * "far" pointers and to be allocated with a special version of malloc.) + */ +#undef NEED_FAR_POINTERS + +/* Define this if your linker needs global names to be unique in less + * than the first 15 characters. + */ +#undef NEED_SHORT_EXTERNAL_NAMES + +/* Although a real ANSI C compiler can deal perfectly well with pointers to + * unspecified structures (see "incomplete types" in the spec), a few pre-ANSI + * and pseudo-ANSI compilers get confused. To keep one of these bozos happy, + * define INCOMPLETE_TYPES_BROKEN. This is not recommended unless you + * actually get "missing structure definition" warnings or errors while + * compiling the JPEG code. + */ +#undef INCOMPLETE_TYPES_BROKEN + + +/* + * The following options affect code selection within the JPEG library, + * but they don't need to be visible to applications using the library. + * To minimize application namespace pollution, the symbols won't be + * defined unless JPEG_INTERNALS has been defined. + */ + +#ifdef JPEG_INTERNALS + +/* Define this if your compiler implements ">>" on signed values as a logical + * (unsigned) shift; leave it undefined if ">>" is a signed (arithmetic) shift, + * which is the normal and rational definition. + */ +#undef RIGHT_SHIFT_IS_UNSIGNED + + +#endif /* JPEG_INTERNALS */ + + +/* + * The remaining options do not affect the JPEG library proper, + * but only the sample applications cjpeg/djpeg (see cjpeg.c, djpeg.c). + * Other applications can ignore these. + */ + +#ifdef JPEG_CJPEG_DJPEG + +/* These defines indicate which image (non-JPEG) file formats are allowed. */ + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +#undef RLE_SUPPORTED /* Utah RLE image file format */ +#define TARGA_SUPPORTED /* Targa image file format */ + +/* Define this if you want to name both input and output files on the command + * line, rather than using stdout and optionally stdin. You MUST do this if + * your system can't cope with binary I/O to stdin/stdout. See comments at + * head of cjpeg.c or djpeg.c. + */ +#undef TWO_FILE_COMMANDLINE + +/* Define this if your system needs explicit cleanup of temporary files. + * This is crucial under MS-DOS, where the temporary "files" may be areas + * of extended memory; on most other systems it's not as important. + */ +#undef NEED_SIGNAL_CATCHER + +/* By default, we open image files with fopen(...,"rb") or fopen(...,"wb"). + * This is necessary on systems that distinguish text files from binary files, + * and is harmless on most systems that don't. If you have one of the rare + * systems that complains about the "b" spec, define this symbol. + */ +#undef DONT_USE_B_MODE + +/* Define this if you want percent-done progress reports from cjpeg/djpeg. + */ +#undef PROGRESS_REPORT + + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/Engine/lib/ljpeg/extras/jconfig.mac b/Engine/lib/ljpeg/extras/jconfig.mac new file mode 100644 index 000000000..0de3efe24 --- /dev/null +++ b/Engine/lib/ljpeg/extras/jconfig.mac @@ -0,0 +1,43 @@ +/* jconfig.mac --- jconfig.h for CodeWarrior on Apple Macintosh */ +/* see jconfig.doc for explanations */ + +#define HAVE_PROTOTYPES +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT +/* #define void char */ +/* #define const */ +#undef CHAR_IS_UNSIGNED +#define HAVE_STDDEF_H +#define HAVE_STDLIB_H +#undef NEED_BSD_STRINGS +#undef NEED_SYS_TYPES_H +#undef NEED_FAR_POINTERS +#undef NEED_SHORT_EXTERNAL_NAMES +#undef INCOMPLETE_TYPES_BROKEN + +#ifdef JPEG_INTERNALS + +#undef RIGHT_SHIFT_IS_UNSIGNED + +#define USE_MAC_MEMMGR /* Define this if you use jmemmac.c */ + +#define ALIGN_TYPE long /* Needed for 680x0 Macs */ + +#endif /* JPEG_INTERNALS */ + +#ifdef JPEG_CJPEG_DJPEG + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +#undef RLE_SUPPORTED /* Utah RLE image file format */ +#define TARGA_SUPPORTED /* Targa image file format */ + +#define USE_CCOMMAND /* Command line reader for Macintosh */ +#define TWO_FILE_COMMANDLINE /* Binary I/O thru stdin/stdout doesn't work */ + +#undef NEED_SIGNAL_CATCHER +#undef DONT_USE_B_MODE +#undef PROGRESS_REPORT /* optional */ + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/Engine/lib/ljpeg/extras/jconfig.manx b/Engine/lib/ljpeg/extras/jconfig.manx new file mode 100644 index 000000000..6dd0d008e --- /dev/null +++ b/Engine/lib/ljpeg/extras/jconfig.manx @@ -0,0 +1,43 @@ +/* jconfig.manx --- jconfig.h for Amiga systems using Manx Aztec C ver 5.x. */ +/* see jconfig.doc for explanations */ + +#define HAVE_PROTOTYPES +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT +/* #define void char */ +/* #define const */ +#undef CHAR_IS_UNSIGNED +#define HAVE_STDDEF_H +#define HAVE_STDLIB_H +#undef NEED_BSD_STRINGS +#undef NEED_SYS_TYPES_H +#undef NEED_FAR_POINTERS +#undef NEED_SHORT_EXTERNAL_NAMES +#undef INCOMPLETE_TYPES_BROKEN + +#ifdef JPEG_INTERNALS + +#undef RIGHT_SHIFT_IS_UNSIGNED + +#define TEMP_DIRECTORY "JPEGTMP:" /* recommended setting for Amiga */ + +#define SHORTxSHORT_32 /* produces better DCT code with Aztec C */ + +#endif /* JPEG_INTERNALS */ + +#ifdef JPEG_CJPEG_DJPEG + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +#undef RLE_SUPPORTED /* Utah RLE image file format */ +#define TARGA_SUPPORTED /* Targa image file format */ + +#define TWO_FILE_COMMANDLINE +#define NEED_SIGNAL_CATCHER +#undef DONT_USE_B_MODE +#undef PROGRESS_REPORT /* optional */ + +#define signal_catcher _abort /* hack for Aztec C naming requirements */ + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/Engine/lib/ljpeg/extras/jconfig.mc6 b/Engine/lib/ljpeg/extras/jconfig.mc6 new file mode 100644 index 000000000..c55082df4 --- /dev/null +++ b/Engine/lib/ljpeg/extras/jconfig.mc6 @@ -0,0 +1,52 @@ +/* jconfig.mc6 --- jconfig.h for Microsoft C on MS-DOS, version 6.00A & up. */ +/* see jconfig.doc for explanations */ + +#define HAVE_PROTOTYPES +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT +/* #define void char */ +/* #define const */ +#undef CHAR_IS_UNSIGNED +#define HAVE_STDDEF_H +#define HAVE_STDLIB_H +#undef NEED_BSD_STRINGS +#undef NEED_SYS_TYPES_H +#define NEED_FAR_POINTERS /* for small or medium memory model */ +#undef NEED_SHORT_EXTERNAL_NAMES +#undef INCOMPLETE_TYPES_BROKEN + +#ifdef JPEG_INTERNALS + +#undef RIGHT_SHIFT_IS_UNSIGNED + +#define USE_MSDOS_MEMMGR /* Define this if you use jmemdos.c */ + +#define MAX_ALLOC_CHUNK 65520L /* Maximum request to malloc() */ + +#define USE_FMEM /* Microsoft has _fmemcpy() and _fmemset() */ + +#define NEED_FHEAPMIN /* far heap management routines are broken */ + +#define SHORTxLCONST_32 /* enable compiler-specific DCT optimization */ +/* Note: the above define is known to improve the code with Microsoft C 6.00A. + * I do not know whether it is good for later compiler versions. + * Please report any info on this point to jpeg-info@uunet.uu.net. + */ + +#endif /* JPEG_INTERNALS */ + +#ifdef JPEG_CJPEG_DJPEG + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +#undef RLE_SUPPORTED /* Utah RLE image file format */ +#define TARGA_SUPPORTED /* Targa image file format */ + +#define TWO_FILE_COMMANDLINE +#define USE_SETMODE /* Microsoft has setmode() */ +#define NEED_SIGNAL_CATCHER /* Define this if you use jmemdos.c */ +#undef DONT_USE_B_MODE +#undef PROGRESS_REPORT /* optional */ + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/Engine/lib/ljpeg/extras/jconfig.sas b/Engine/lib/ljpeg/extras/jconfig.sas new file mode 100644 index 000000000..efdac2229 --- /dev/null +++ b/Engine/lib/ljpeg/extras/jconfig.sas @@ -0,0 +1,43 @@ +/* jconfig.sas --- jconfig.h for Amiga systems using SAS C 6.0 and up. */ +/* see jconfig.doc for explanations */ + +#define HAVE_PROTOTYPES +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT +/* #define void char */ +/* #define const */ +#undef CHAR_IS_UNSIGNED +#define HAVE_STDDEF_H +#define HAVE_STDLIB_H +#undef NEED_BSD_STRINGS +#undef NEED_SYS_TYPES_H +#undef NEED_FAR_POINTERS +#undef NEED_SHORT_EXTERNAL_NAMES +#undef INCOMPLETE_TYPES_BROKEN + +#ifdef JPEG_INTERNALS + +#undef RIGHT_SHIFT_IS_UNSIGNED + +#define TEMP_DIRECTORY "JPEGTMP:" /* recommended setting for Amiga */ + +#define NO_MKTEMP /* SAS C doesn't have mktemp() */ + +#define SHORTxSHORT_32 /* produces better DCT code with SAS C */ + +#endif /* JPEG_INTERNALS */ + +#ifdef JPEG_CJPEG_DJPEG + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +#undef RLE_SUPPORTED /* Utah RLE image file format */ +#define TARGA_SUPPORTED /* Targa image file format */ + +#define TWO_FILE_COMMANDLINE +#define NEED_SIGNAL_CATCHER +#undef DONT_USE_B_MODE +#undef PROGRESS_REPORT /* optional */ + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/Engine/lib/ljpeg/extras/jconfig.st b/Engine/lib/ljpeg/extras/jconfig.st new file mode 100644 index 000000000..4421b7a1a --- /dev/null +++ b/Engine/lib/ljpeg/extras/jconfig.st @@ -0,0 +1,42 @@ +/* jconfig.st --- jconfig.h for Atari ST/STE/TT using Pure C or Turbo C. */ +/* see jconfig.doc for explanations */ + +#define HAVE_PROTOTYPES +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT +/* #define void char */ +/* #define const */ +#undef CHAR_IS_UNSIGNED +#define HAVE_STDDEF_H +#define HAVE_STDLIB_H +#undef NEED_BSD_STRINGS +#undef NEED_SYS_TYPES_H +#undef NEED_FAR_POINTERS +#undef NEED_SHORT_EXTERNAL_NAMES +#define INCOMPLETE_TYPES_BROKEN /* suppress undefined-structure warnings */ + +#ifdef JPEG_INTERNALS + +#undef RIGHT_SHIFT_IS_UNSIGNED + +#define ALIGN_TYPE long /* apparently double is a weird size? */ + +#endif /* JPEG_INTERNALS */ + +#ifdef JPEG_CJPEG_DJPEG + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +#undef RLE_SUPPORTED /* Utah RLE image file format */ +#define TARGA_SUPPORTED /* Targa image file format */ + +#define TWO_FILE_COMMANDLINE /* optional -- undef if you like Unix style */ +/* Note: if you undef TWO_FILE_COMMANDLINE, you may need to define + * USE_SETMODE. Some Atari compilers require it, some do not. + */ +#define NEED_SIGNAL_CATCHER /* needed if you use jmemname.c */ +#undef DONT_USE_B_MODE +#undef PROGRESS_REPORT /* optional */ + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/Engine/lib/ljpeg/extras/jconfig.vc b/Engine/lib/ljpeg/extras/jconfig.vc new file mode 100644 index 000000000..7e291c75b --- /dev/null +++ b/Engine/lib/ljpeg/extras/jconfig.vc @@ -0,0 +1,45 @@ +/* jconfig.vc --- jconfig.h for Microsoft Visual C++ on Windows 95 or NT. */ +/* see jconfig.doc for explanations */ + +#define HAVE_PROTOTYPES +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT +/* #define void char */ +/* #define const */ +#undef CHAR_IS_UNSIGNED +#define HAVE_STDDEF_H +#define HAVE_STDLIB_H +#undef NEED_BSD_STRINGS +#undef NEED_SYS_TYPES_H +#undef NEED_FAR_POINTERS /* we presume a 32-bit flat memory model */ +#undef NEED_SHORT_EXTERNAL_NAMES +#undef INCOMPLETE_TYPES_BROKEN + +/* Define "boolean" as unsigned char, not int, per Windows custom */ +#ifndef __RPCNDR_H__ /* don't conflict if rpcndr.h already read */ +typedef unsigned char boolean; +#endif +#define HAVE_BOOLEAN /* prevent jmorecfg.h from redefining it */ + + +#ifdef JPEG_INTERNALS + +#undef RIGHT_SHIFT_IS_UNSIGNED + +#endif /* JPEG_INTERNALS */ + +#ifdef JPEG_CJPEG_DJPEG + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +#undef RLE_SUPPORTED /* Utah RLE image file format */ +#define TARGA_SUPPORTED /* Targa image file format */ + +#define TWO_FILE_COMMANDLINE /* optional */ +#define USE_SETMODE /* Microsoft has setmode() */ +#undef NEED_SIGNAL_CATCHER +#undef DONT_USE_B_MODE +#undef PROGRESS_REPORT /* optional */ + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/Engine/lib/ljpeg/extras/jconfig.vms b/Engine/lib/ljpeg/extras/jconfig.vms new file mode 100644 index 000000000..55a6ffba5 --- /dev/null +++ b/Engine/lib/ljpeg/extras/jconfig.vms @@ -0,0 +1,37 @@ +/* jconfig.vms --- jconfig.h for use on Digital VMS. */ +/* see jconfig.doc for explanations */ + +#define HAVE_PROTOTYPES +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT +/* #define void char */ +/* #define const */ +#undef CHAR_IS_UNSIGNED +#define HAVE_STDDEF_H +#define HAVE_STDLIB_H +#undef NEED_BSD_STRINGS +#undef NEED_SYS_TYPES_H +#undef NEED_FAR_POINTERS +#undef NEED_SHORT_EXTERNAL_NAMES +#undef INCOMPLETE_TYPES_BROKEN + +#ifdef JPEG_INTERNALS + +#undef RIGHT_SHIFT_IS_UNSIGNED + +#endif /* JPEG_INTERNALS */ + +#ifdef JPEG_CJPEG_DJPEG + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +#undef RLE_SUPPORTED /* Utah RLE image file format */ +#define TARGA_SUPPORTED /* Targa image file format */ + +#define TWO_FILE_COMMANDLINE /* Needed on VMS */ +#undef NEED_SIGNAL_CATCHER +#undef DONT_USE_B_MODE +#undef PROGRESS_REPORT /* optional */ + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/Engine/lib/ljpeg/extras/jconfig.wat b/Engine/lib/ljpeg/extras/jconfig.wat new file mode 100644 index 000000000..6cc545bae --- /dev/null +++ b/Engine/lib/ljpeg/extras/jconfig.wat @@ -0,0 +1,38 @@ +/* jconfig.wat --- jconfig.h for Watcom C/C++ on MS-DOS or OS/2. */ +/* see jconfig.doc for explanations */ + +#define HAVE_PROTOTYPES +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT +/* #define void char */ +/* #define const */ +#define CHAR_IS_UNSIGNED +#define HAVE_STDDEF_H +#define HAVE_STDLIB_H +#undef NEED_BSD_STRINGS +#undef NEED_SYS_TYPES_H +#undef NEED_FAR_POINTERS /* Watcom uses flat 32-bit addressing */ +#undef NEED_SHORT_EXTERNAL_NAMES +#undef INCOMPLETE_TYPES_BROKEN + +#ifdef JPEG_INTERNALS + +#undef RIGHT_SHIFT_IS_UNSIGNED + +#endif /* JPEG_INTERNALS */ + +#ifdef JPEG_CJPEG_DJPEG + +#define BMP_SUPPORTED /* BMP image file format */ +#define GIF_SUPPORTED /* GIF image file format */ +#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +#undef RLE_SUPPORTED /* Utah RLE image file format */ +#define TARGA_SUPPORTED /* Targa image file format */ + +#undef TWO_FILE_COMMANDLINE /* optional */ +#define USE_SETMODE /* Needed to make one-file style work in Watcom */ +#undef NEED_SIGNAL_CATCHER /* Define this if you use jmemname.c */ +#undef DONT_USE_B_MODE +#undef PROGRESS_REPORT /* optional */ + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/Engine/lib/ljpeg/extras/jmemdosa.asm b/Engine/lib/ljpeg/extras/jmemdosa.asm new file mode 100644 index 000000000..ecd43729f --- /dev/null +++ b/Engine/lib/ljpeg/extras/jmemdosa.asm @@ -0,0 +1,379 @@ +; +; jmemdosa.asm +; +; Copyright (C) 1992, Thomas G. Lane. +; This file is part of the Independent JPEG Group's software. +; For conditions of distribution and use, see the accompanying README file. +; +; This file contains low-level interface routines to support the MS-DOS +; backing store manager (jmemdos.c). Routines are provided to access disk +; files through direct DOS calls, and to access XMS and EMS drivers. +; +; This file should assemble with Microsoft's MASM or any compatible +; assembler (including Borland's Turbo Assembler). If you haven't got +; a compatible assembler, better fall back to jmemansi.c or jmemname.c. +; +; To minimize dependence on the C compiler's register usage conventions, +; we save and restore all 8086 registers, even though most compilers only +; require SI,DI,DS to be preserved. Also, we use only 16-bit-wide return +; values, which everybody returns in AX. +; +; Based on code contributed by Ge' Weijers. +; + +JMEMDOSA_TXT segment byte public 'CODE' + + assume cs:JMEMDOSA_TXT + + public _jdos_open + public _jdos_close + public _jdos_seek + public _jdos_read + public _jdos_write + public _jxms_getdriver + public _jxms_calldriver + public _jems_available + public _jems_calldriver + +; +; short far jdos_open (short far * handle, char far * filename) +; +; Create and open a temporary file +; +_jdos_open proc far + push bp ; linkage + mov bp,sp + push si ; save all registers for safety + push di + push bx + push cx + push dx + push es + push ds + mov cx,0 ; normal file attributes + lds dx,dword ptr [bp+10] ; get filename pointer + mov ah,3ch ; create file + int 21h + jc open_err ; if failed, return error code + lds bx,dword ptr [bp+6] ; get handle pointer + mov word ptr [bx],ax ; save the handle + xor ax,ax ; return zero for OK +open_err: pop ds ; restore registers and exit + pop es + pop dx + pop cx + pop bx + pop di + pop si + pop bp + ret +_jdos_open endp + + +; +; short far jdos_close (short handle) +; +; Close the file handle +; +_jdos_close proc far + push bp ; linkage + mov bp,sp + push si ; save all registers for safety + push di + push bx + push cx + push dx + push es + push ds + mov bx,word ptr [bp+6] ; file handle + mov ah,3eh ; close file + int 21h + jc close_err ; if failed, return error code + xor ax,ax ; return zero for OK +close_err: pop ds ; restore registers and exit + pop es + pop dx + pop cx + pop bx + pop di + pop si + pop bp + ret +_jdos_close endp + + +; +; short far jdos_seek (short handle, long offset) +; +; Set file position +; +_jdos_seek proc far + push bp ; linkage + mov bp,sp + push si ; save all registers for safety + push di + push bx + push cx + push dx + push es + push ds + mov bx,word ptr [bp+6] ; file handle + mov dx,word ptr [bp+8] ; LS offset + mov cx,word ptr [bp+10] ; MS offset + mov ax,4200h ; absolute seek + int 21h + jc seek_err ; if failed, return error code + xor ax,ax ; return zero for OK +seek_err: pop ds ; restore registers and exit + pop es + pop dx + pop cx + pop bx + pop di + pop si + pop bp + ret +_jdos_seek endp + + +; +; short far jdos_read (short handle, void far * buffer, unsigned short count) +; +; Read from file +; +_jdos_read proc far + push bp ; linkage + mov bp,sp + push si ; save all registers for safety + push di + push bx + push cx + push dx + push es + push ds + mov bx,word ptr [bp+6] ; file handle + lds dx,dword ptr [bp+8] ; buffer address + mov cx,word ptr [bp+12] ; number of bytes + mov ah,3fh ; read file + int 21h + jc read_err ; if failed, return error code + cmp ax,word ptr [bp+12] ; make sure all bytes were read + je read_ok + mov ax,1 ; else return 1 for not OK + jmp short read_err +read_ok: xor ax,ax ; return zero for OK +read_err: pop ds ; restore registers and exit + pop es + pop dx + pop cx + pop bx + pop di + pop si + pop bp + ret +_jdos_read endp + + +; +; short far jdos_write (short handle, void far * buffer, unsigned short count) +; +; Write to file +; +_jdos_write proc far + push bp ; linkage + mov bp,sp + push si ; save all registers for safety + push di + push bx + push cx + push dx + push es + push ds + mov bx,word ptr [bp+6] ; file handle + lds dx,dword ptr [bp+8] ; buffer address + mov cx,word ptr [bp+12] ; number of bytes + mov ah,40h ; write file + int 21h + jc write_err ; if failed, return error code + cmp ax,word ptr [bp+12] ; make sure all bytes written + je write_ok + mov ax,1 ; else return 1 for not OK + jmp short write_err +write_ok: xor ax,ax ; return zero for OK +write_err: pop ds ; restore registers and exit + pop es + pop dx + pop cx + pop bx + pop di + pop si + pop bp + ret +_jdos_write endp + + +; +; void far jxms_getdriver (XMSDRIVER far *) +; +; Get the address of the XMS driver, or NULL if not available +; +_jxms_getdriver proc far + push bp ; linkage + mov bp,sp + push si ; save all registers for safety + push di + push bx + push cx + push dx + push es + push ds + mov ax,4300h ; call multiplex interrupt with + int 2fh ; a magic cookie, hex 4300 + cmp al,80h ; AL should contain hex 80 + je xmsavail + xor dx,dx ; no XMS driver available + xor ax,ax ; return a nil pointer + jmp short xmsavail_done +xmsavail: mov ax,4310h ; fetch driver address with + int 2fh ; another magic cookie + mov dx,es ; copy address to dx:ax + mov ax,bx +xmsavail_done: les bx,dword ptr [bp+6] ; get pointer to return value + mov word ptr es:[bx],ax + mov word ptr es:[bx+2],dx + pop ds ; restore registers and exit + pop es + pop dx + pop cx + pop bx + pop di + pop si + pop bp + ret +_jxms_getdriver endp + + +; +; void far jxms_calldriver (XMSDRIVER, XMScontext far *) +; +; The XMScontext structure contains values for the AX,DX,BX,SI,DS registers. +; These are loaded, the XMS call is performed, and the new values of the +; AX,DX,BX registers are written back to the context structure. +; +_jxms_calldriver proc far + push bp ; linkage + mov bp,sp + push si ; save all registers for safety + push di + push bx + push cx + push dx + push es + push ds + les bx,dword ptr [bp+10] ; get XMScontext pointer + mov ax,word ptr es:[bx] ; load registers + mov dx,word ptr es:[bx+2] + mov si,word ptr es:[bx+6] + mov ds,word ptr es:[bx+8] + mov bx,word ptr es:[bx+4] + call dword ptr [bp+6] ; call the driver + mov cx,bx ; save returned BX for a sec + les bx,dword ptr [bp+10] ; get XMScontext pointer + mov word ptr es:[bx],ax ; put back ax,dx,bx + mov word ptr es:[bx+2],dx + mov word ptr es:[bx+4],cx + pop ds ; restore registers and exit + pop es + pop dx + pop cx + pop bx + pop di + pop si + pop bp + ret +_jxms_calldriver endp + + +; +; short far jems_available (void) +; +; Have we got an EMS driver? (this comes straight from the EMS 4.0 specs) +; +_jems_available proc far + push si ; save all registers for safety + push di + push bx + push cx + push dx + push es + push ds + mov ax,3567h ; get interrupt vector 67h + int 21h + push cs + pop ds + mov di,000ah ; check offs 10 in returned seg + lea si,ASCII_device_name ; against literal string + mov cx,8 + cld + repe cmpsb + jne no_ems + mov ax,1 ; match, it's there + jmp short avail_done +no_ems: xor ax,ax ; it's not there +avail_done: pop ds ; restore registers and exit + pop es + pop dx + pop cx + pop bx + pop di + pop si + ret + +ASCII_device_name db "EMMXXXX0" + +_jems_available endp + + +; +; void far jems_calldriver (EMScontext far *) +; +; The EMScontext structure contains values for the AX,DX,BX,SI,DS registers. +; These are loaded, the EMS trap is performed, and the new values of the +; AX,DX,BX registers are written back to the context structure. +; +_jems_calldriver proc far + push bp ; linkage + mov bp,sp + push si ; save all registers for safety + push di + push bx + push cx + push dx + push es + push ds + les bx,dword ptr [bp+6] ; get EMScontext pointer + mov ax,word ptr es:[bx] ; load registers + mov dx,word ptr es:[bx+2] + mov si,word ptr es:[bx+6] + mov ds,word ptr es:[bx+8] + mov bx,word ptr es:[bx+4] + int 67h ; call the EMS driver + mov cx,bx ; save returned BX for a sec + les bx,dword ptr [bp+6] ; get EMScontext pointer + mov word ptr es:[bx],ax ; put back ax,dx,bx + mov word ptr es:[bx+2],dx + mov word ptr es:[bx+4],cx + pop ds ; restore registers and exit + pop es + pop dx + pop cx + pop bx + pop di + pop si + pop bp + ret +_jems_calldriver endp + +JMEMDOSA_TXT ends + + end diff --git a/Engine/lib/ljpeg/extras/jpegtran.1 b/Engine/lib/ljpeg/extras/jpegtran.1 new file mode 100644 index 000000000..6de18e2af --- /dev/null +++ b/Engine/lib/ljpeg/extras/jpegtran.1 @@ -0,0 +1,238 @@ +.TH JPEGTRAN 1 "3 August 1997" +.SH NAME +jpegtran \- lossless transformation of JPEG files +.SH SYNOPSIS +.B jpegtran +[ +.I options +] +[ +.I filename +] +.LP +.SH DESCRIPTION +.LP +.B jpegtran +performs various useful transformations of JPEG files. +It can translate the coded representation from one variant of JPEG to another, +for example from baseline JPEG to progressive JPEG or vice versa. It can also +perform some rearrangements of the image data, for example turning an image +from landscape to portrait format by rotation. +.PP +.B jpegtran +works by rearranging the compressed data (DCT coefficients), without +ever fully decoding the image. Therefore, its transformations are lossless: +there is no image degradation at all, which would not be true if you used +.B djpeg +followed by +.B cjpeg +to accomplish the same conversion. But by the same token, +.B jpegtran +cannot perform lossy operations such as changing the image quality. +.PP +.B jpegtran +reads the named JPEG/JFIF file, or the standard input if no file is +named, and produces a JPEG/JFIF file on the standard output. +.SH OPTIONS +All switch names may be abbreviated; for example, +.B \-optimize +may be written +.B \-opt +or +.BR \-o . +Upper and lower case are equivalent. +British spellings are also accepted (e.g., +.BR \-optimise ), +though for brevity these are not mentioned below. +.PP +To specify the coded JPEG representation used in the output file, +.B jpegtran +accepts a subset of the switches recognized by +.BR cjpeg : +.TP +.B \-optimize +Perform optimization of entropy encoding parameters. +.TP +.B \-progressive +Create progressive JPEG file. +.TP +.BI \-restart " N" +Emit a JPEG restart marker every N MCU rows, or every N MCU blocks if "B" is +attached to the number. +.TP +.BI \-scans " file" +Use the scan script given in the specified text file. +.PP +See +.BR cjpeg (1) +for more details about these switches. +If you specify none of these switches, you get a plain baseline-JPEG output +file. The quality setting and so forth are determined by the input file. +.PP +The image can be losslessly transformed by giving one of these switches: +.TP +.B \-flip horizontal +Mirror image horizontally (left-right). +.TP +.B \-flip vertical +Mirror image vertically (top-bottom). +.TP +.B \-rotate 90 +Rotate image 90 degrees clockwise. +.TP +.B \-rotate 180 +Rotate image 180 degrees. +.TP +.B \-rotate 270 +Rotate image 270 degrees clockwise (or 90 ccw). +.TP +.B \-transpose +Transpose image (across UL-to-LR axis). +.TP +.B \-transverse +Transverse transpose (across UR-to-LL axis). +.PP +The transpose transformation has no restrictions regarding image dimensions. +The other transformations operate rather oddly if the image dimensions are not +a multiple of the iMCU size (usually 8 or 16 pixels), because they can only +transform complete blocks of DCT coefficient data in the desired way. +.PP +.BR jpegtran 's +default behavior when transforming an odd-size image is designed +to preserve exact reversibility and mathematical consistency of the +transformation set. As stated, transpose is able to flip the entire image +area. Horizontal mirroring leaves any partial iMCU column at the right edge +untouched, but is able to flip all rows of the image. Similarly, vertical +mirroring leaves any partial iMCU row at the bottom edge untouched, but is +able to flip all columns. The other transforms can be built up as sequences +of transpose and flip operations; for consistency, their actions on edge +pixels are defined to be the same as the end result of the corresponding +transpose-and-flip sequence. +.PP +For practical use, you may prefer to discard any untransformable edge pixels +rather than having a strange-looking strip along the right and/or bottom edges +of a transformed image. To do this, add the +.B \-trim +switch: +.TP +.B \-trim +Drop non-transformable edge blocks. +.PP +Obviously, a transformation with +.B \-trim +is not reversible, so strictly speaking +.B jpegtran +with this switch is not lossless. Also, the expected mathematical +equivalences between the transformations no longer hold. For example, +.B \-rot 270 -trim +trims only the bottom edge, but +.B \-rot 90 -trim +followed by +.B \-rot 180 -trim +trims both edges. +.PP +Another not-strictly-lossless transformation switch is: +.TP +.B \-grayscale +Force grayscale output. +.PP +This option discards the chrominance channels if the input image is YCbCr +(ie, a standard color JPEG), resulting in a grayscale JPEG file. The +luminance channel is preserved exactly, so this is a better method of reducing +to grayscale than decompression, conversion, and recompression. This switch +is particularly handy for fixing a monochrome picture that was mistakenly +encoded as a color JPEG. (In such a case, the space savings from getting rid +of the near-empty chroma channels won't be large; but the decoding time for +a grayscale JPEG is substantially less than that for a color JPEG.) +.PP +.B jpegtran +also recognizes these switches that control what to do with "extra" markers, +such as comment blocks: +.TP +.B \-copy none +Copy no extra markers from source file. This setting suppresses all +comments and other excess baggage present in the source file. +.TP +.B \-copy comments +Copy only comment markers. This setting copies comments from the source file, +but discards any other inessential data. +.TP +.B \-copy all +Copy all extra markers. This setting preserves miscellaneous markers +found in the source file, such as JFIF thumbnails and Photoshop settings. +In some files these extra markers can be sizable. +.PP +The default behavior is +.BR "\-copy comments" . +(Note: in IJG releases v6 and v6a, +.B jpegtran +always did the equivalent of +.BR "\-copy none" .) +.PP +Additional switches recognized by jpegtran are: +.TP +.BI \-maxmemory " N" +Set limit for amount of memory to use in processing large images. Value is +in thousands of bytes, or millions of bytes if "M" is attached to the +number. For example, +.B \-max 4m +selects 4000000 bytes. If more space is needed, temporary files will be used. +.TP +.BI \-outfile " name" +Send output image to the named file, not to standard output. +.TP +.B \-verbose +Enable debug printout. More +.BR \-v 's +give more output. Also, version information is printed at startup. +.TP +.B \-debug +Same as +.BR \-verbose . +.SH EXAMPLES +.LP +This example converts a baseline JPEG file to progressive form: +.IP +.B jpegtran \-progressive +.I foo.jpg +.B > +.I fooprog.jpg +.PP +This example rotates an image 90 degrees clockwise, discarding any +unrotatable edge pixels: +.IP +.B jpegtran \-rot 90 -trim +.I foo.jpg +.B > +.I foo90.jpg +.SH ENVIRONMENT +.TP +.B JPEGMEM +If this environment variable is set, its value is the default memory limit. +The value is specified as described for the +.B \-maxmemory +switch. +.B JPEGMEM +overrides the default value specified when the program was compiled, and +itself is overridden by an explicit +.BR \-maxmemory . +.SH SEE ALSO +.BR cjpeg (1), +.BR djpeg (1), +.BR rdjpgcom (1), +.BR wrjpgcom (1) +.br +Wallace, Gregory K. "The JPEG Still Picture Compression Standard", +Communications of the ACM, April 1991 (vol. 34, no. 4), pp. 30-44. +.SH AUTHOR +Independent JPEG Group +.SH BUGS +Arithmetic coding is not supported for legal reasons. +.PP +The transform options can't transform odd-size images perfectly. Use +.B \-trim +if you don't like the results without it. +.PP +The entire image is read into memory and then written out again, even in +cases where this isn't really necessary. Expect swapping on large images, +especially when using the more complex transform options. diff --git a/Engine/lib/ljpeg/extras/libjpeg.doc b/Engine/lib/ljpeg/extras/libjpeg.doc new file mode 100644 index 000000000..689b206c0 --- /dev/null +++ b/Engine/lib/ljpeg/extras/libjpeg.doc @@ -0,0 +1,3006 @@ +USING THE IJG JPEG LIBRARY + +Copyright (C) 1994-1998, Thomas G. Lane. +This file is part of the Independent JPEG Group's software. +For conditions of distribution and use, see the accompanying README file. + + +This file describes how to use the IJG JPEG library within an application +program. Read it if you want to write a program that uses the library. + +The file example.c provides heavily commented skeleton code for calling the +JPEG library. Also see jpeglib.h (the include file to be used by application +programs) for full details about data structures and function parameter lists. +The library source code, of course, is the ultimate reference. + +Note that there have been *major* changes from the application interface +presented by IJG version 4 and earlier versions. The old design had several +inherent limitations, and it had accumulated a lot of cruft as we added +features while trying to minimize application-interface changes. We have +sacrificed backward compatibility in the version 5 rewrite, but we think the +improvements justify this. + + +TABLE OF CONTENTS +----------------- + +Overview: + Functions provided by the library + Outline of typical usage +Basic library usage: + Data formats + Compression details + Decompression details + Mechanics of usage: include files, linking, etc +Advanced features: + Compression parameter selection + Decompression parameter selection + Special color spaces + Error handling + Compressed data handling (source and destination managers) + I/O suspension + Progressive JPEG support + Buffered-image mode + Abbreviated datastreams and multiple images + Special markers + Raw (downsampled) image data + Really raw data: DCT coefficients + Progress monitoring + Memory management + Memory usage + Library compile-time options + Portability considerations + Notes for MS-DOS implementors + +You should read at least the overview and basic usage sections before trying +to program with the library. The sections on advanced features can be read +if and when you need them. + + +OVERVIEW +======== + +Functions provided by the library +--------------------------------- + +The IJG JPEG library provides C code to read and write JPEG-compressed image +files. The surrounding application program receives or supplies image data a +scanline at a time, using a straightforward uncompressed image format. All +details of color conversion and other preprocessing/postprocessing can be +handled by the library. + +The library includes a substantial amount of code that is not covered by the +JPEG standard but is necessary for typical applications of JPEG. These +functions preprocess the image before JPEG compression or postprocess it after +decompression. They include colorspace conversion, downsampling/upsampling, +and color quantization. The application indirectly selects use of this code +by specifying the format in which it wishes to supply or receive image data. +For example, if colormapped output is requested, then the decompression +library automatically invokes color quantization. + +A wide range of quality vs. speed tradeoffs are possible in JPEG processing, +and even more so in decompression postprocessing. The decompression library +provides multiple implementations that cover most of the useful tradeoffs, +ranging from very-high-quality down to fast-preview operation. On the +compression side we have generally not provided low-quality choices, since +compression is normally less time-critical. It should be understood that the +low-quality modes may not meet the JPEG standard's accuracy requirements; +nonetheless, they are useful for viewers. + +A word about functions *not* provided by the library. We handle a subset of +the ISO JPEG standard; most baseline, extended-sequential, and progressive +JPEG processes are supported. (Our subset includes all features now in common +use.) Unsupported ISO options include: + * Hierarchical storage + * Lossless JPEG + * Arithmetic entropy coding (unsupported for legal reasons) + * DNL marker + * Nonintegral subsampling ratios +We support both 8- and 12-bit data precision, but this is a compile-time +choice rather than a run-time choice; hence it is difficult to use both +precisions in a single application. + +By itself, the library handles only interchange JPEG datastreams --- in +particular the widely used JFIF file format. The library can be used by +surrounding code to process interchange or abbreviated JPEG datastreams that +are embedded in more complex file formats. (For example, this library is +used by the free LIBTIFF library to support JPEG compression in TIFF.) + + +Outline of typical usage +------------------------ + +The rough outline of a JPEG compression operation is: + + Allocate and initialize a JPEG compression object + Specify the destination for the compressed data (eg, a file) + Set parameters for compression, including image size & colorspace + jpeg_start_compress(...); + while (scan lines remain to be written) + jpeg_write_scanlines(...); + jpeg_finish_compress(...); + Release the JPEG compression object + +A JPEG compression object holds parameters and working state for the JPEG +library. We make creation/destruction of the object separate from starting +or finishing compression of an image; the same object can be re-used for a +series of image compression operations. This makes it easy to re-use the +same parameter settings for a sequence of images. Re-use of a JPEG object +also has important implications for processing abbreviated JPEG datastreams, +as discussed later. + +The image data to be compressed is supplied to jpeg_write_scanlines() from +in-memory buffers. If the application is doing file-to-file compression, +reading image data from the source file is the application's responsibility. +The library emits compressed data by calling a "data destination manager", +which typically will write the data into a file; but the application can +provide its own destination manager to do something else. + +Similarly, the rough outline of a JPEG decompression operation is: + + Allocate and initialize a JPEG decompression object + Specify the source of the compressed data (eg, a file) + Call jpeg_read_header() to obtain image info + Set parameters for decompression + jpeg_start_decompress(...); + while (scan lines remain to be read) + jpeg_read_scanlines(...); + jpeg_finish_decompress(...); + Release the JPEG decompression object + +This is comparable to the compression outline except that reading the +datastream header is a separate step. This is helpful because information +about the image's size, colorspace, etc is available when the application +selects decompression parameters. For example, the application can choose an +output scaling ratio that will fit the image into the available screen size. + +The decompression library obtains compressed data by calling a data source +manager, which typically will read the data from a file; but other behaviors +can be obtained with a custom source manager. Decompressed data is delivered +into in-memory buffers passed to jpeg_read_scanlines(). + +It is possible to abort an incomplete compression or decompression operation +by calling jpeg_abort(); or, if you do not need to retain the JPEG object, +simply release it by calling jpeg_destroy(). + +JPEG compression and decompression objects are two separate struct types. +However, they share some common fields, and certain routines such as +jpeg_destroy() can work on either type of object. + +The JPEG library has no static variables: all state is in the compression +or decompression object. Therefore it is possible to process multiple +compression and decompression operations concurrently, using multiple JPEG +objects. + +Both compression and decompression can be done in an incremental memory-to- +memory fashion, if suitable source/destination managers are used. See the +section on "I/O suspension" for more details. + + +BASIC LIBRARY USAGE +=================== + +Data formats +------------ + +Before diving into procedural details, it is helpful to understand the +image data format that the JPEG library expects or returns. + +The standard input image format is a rectangular array of pixels, with each +pixel having the same number of "component" or "sample" values (color +channels). You must specify how many components there are and the colorspace +interpretation of the components. Most applications will use RGB data +(three components per pixel) or grayscale data (one component per pixel). +PLEASE NOTE THAT RGB DATA IS THREE SAMPLES PER PIXEL, GRAYSCALE ONLY ONE. +A remarkable number of people manage to miss this, only to find that their +programs don't work with grayscale JPEG files. + +There is no provision for colormapped input. JPEG files are always full-color +or full grayscale (or sometimes another colorspace such as CMYK). You can +feed in a colormapped image by expanding it to full-color format. However +JPEG often doesn't work very well with source data that has been colormapped, +because of dithering noise. This is discussed in more detail in the JPEG FAQ +and the other references mentioned in the README file. + +Pixels are stored by scanlines, with each scanline running from left to +right. The component values for each pixel are adjacent in the row; for +example, R,G,B,R,G,B,R,G,B,... for 24-bit RGB color. Each scanline is an +array of data type JSAMPLE --- which is typically "unsigned char", unless +you've changed jmorecfg.h. (You can also change the RGB pixel layout, say +to B,G,R order, by modifying jmorecfg.h. But see the restrictions listed in +that file before doing so.) + +A 2-D array of pixels is formed by making a list of pointers to the starts of +scanlines; so the scanlines need not be physically adjacent in memory. Even +if you process just one scanline at a time, you must make a one-element +pointer array to conform to this structure. Pointers to JSAMPLE rows are of +type JSAMPROW, and the pointer to the pointer array is of type JSAMPARRAY. + +The library accepts or supplies one or more complete scanlines per call. +It is not possible to process part of a row at a time. Scanlines are always +processed top-to-bottom. You can process an entire image in one call if you +have it all in memory, but usually it's simplest to process one scanline at +a time. + +For best results, source data values should have the precision specified by +BITS_IN_JSAMPLE (normally 8 bits). For instance, if you choose to compress +data that's only 6 bits/channel, you should left-justify each value in a +byte before passing it to the compressor. If you need to compress data +that has more than 8 bits/channel, compile with BITS_IN_JSAMPLE = 12. +(See "Library compile-time options", later.) + + +The data format returned by the decompressor is the same in all details, +except that colormapped output is supported. (Again, a JPEG file is never +colormapped. But you can ask the decompressor to perform on-the-fly color +quantization to deliver colormapped output.) If you request colormapped +output then the returned data array contains a single JSAMPLE per pixel; +its value is an index into a color map. The color map is represented as +a 2-D JSAMPARRAY in which each row holds the values of one color component, +that is, colormap[i][j] is the value of the i'th color component for pixel +value (map index) j. Note that since the colormap indexes are stored in +JSAMPLEs, the maximum number of colors is limited by the size of JSAMPLE +(ie, at most 256 colors for an 8-bit JPEG library). + + +Compression details +------------------- + +Here we revisit the JPEG compression outline given in the overview. + +1. Allocate and initialize a JPEG compression object. + +A JPEG compression object is a "struct jpeg_compress_struct". (It also has +a bunch of subsidiary structures which are allocated via malloc(), but the +application doesn't control those directly.) This struct can be just a local +variable in the calling routine, if a single routine is going to execute the +whole JPEG compression sequence. Otherwise it can be static or allocated +from malloc(). + +You will also need a structure representing a JPEG error handler. The part +of this that the library cares about is a "struct jpeg_error_mgr". If you +are providing your own error handler, you'll typically want to embed the +jpeg_error_mgr struct in a larger structure; this is discussed later under +"Error handling". For now we'll assume you are just using the default error +handler. The default error handler will print JPEG error/warning messages +on stderr, and it will call exit() if a fatal error occurs. + +You must initialize the error handler structure, store a pointer to it into +the JPEG object's "err" field, and then call jpeg_create_compress() to +initialize the rest of the JPEG object. + +Typical code for this step, if you are using the default error handler, is + + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + ... + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + +jpeg_create_compress allocates a small amount of memory, so it could fail +if you are out of memory. In that case it will exit via the error handler; +that's why the error handler must be initialized first. + + +2. Specify the destination for the compressed data (eg, a file). + +As previously mentioned, the JPEG library delivers compressed data to a +"data destination" module. The library includes one data destination +module which knows how to write to a stdio stream. You can use your own +destination module if you want to do something else, as discussed later. + +If you use the standard destination module, you must open the target stdio +stream beforehand. Typical code for this step looks like: + + FILE * outfile; + ... + if ((outfile = fopen(filename, "wb")) == NULL) { + fprintf(stderr, "can't open %s\n", filename); + exit(1); + } + jpeg_stdio_dest(&cinfo, outfile); + +where the last line invokes the standard destination module. + +WARNING: it is critical that the binary compressed data be delivered to the +output file unchanged. On non-Unix systems the stdio library may perform +newline translation or otherwise corrupt binary data. To suppress this +behavior, you may need to use a "b" option to fopen (as shown above), or use +setmode() or another routine to put the stdio stream in binary mode. See +cjpeg.c and djpeg.c for code that has been found to work on many systems. + +You can select the data destination after setting other parameters (step 3), +if that's more convenient. You may not change the destination between +calling jpeg_start_compress() and jpeg_finish_compress(). + + +3. Set parameters for compression, including image size & colorspace. + +You must supply information about the source image by setting the following +fields in the JPEG object (cinfo structure): + + image_width Width of image, in pixels + image_height Height of image, in pixels + input_components Number of color channels (samples per pixel) + in_color_space Color space of source image + +The image dimensions are, hopefully, obvious. JPEG supports image dimensions +of 1 to 64K pixels in either direction. The input color space is typically +RGB or grayscale, and input_components is 3 or 1 accordingly. (See "Special +color spaces", later, for more info.) The in_color_space field must be +assigned one of the J_COLOR_SPACE enum constants, typically JCS_RGB or +JCS_GRAYSCALE. + +JPEG has a large number of compression parameters that determine how the +image is encoded. Most applications don't need or want to know about all +these parameters. You can set all the parameters to reasonable defaults by +calling jpeg_set_defaults(); then, if there are particular values you want +to change, you can do so after that. The "Compression parameter selection" +section tells about all the parameters. + +You must set in_color_space correctly before calling jpeg_set_defaults(), +because the defaults depend on the source image colorspace. However the +other three source image parameters need not be valid until you call +jpeg_start_compress(). There's no harm in calling jpeg_set_defaults() more +than once, if that happens to be convenient. + +Typical code for a 24-bit RGB source image is + + cinfo.image_width = Width; /* image width and height, in pixels */ + cinfo.image_height = Height; + cinfo.input_components = 3; /* # of color components per pixel */ + cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ + + jpeg_set_defaults(&cinfo); + /* Make optional parameter settings here */ + + +4. jpeg_start_compress(...); + +After you have established the data destination and set all the necessary +source image info and other parameters, call jpeg_start_compress() to begin +a compression cycle. This will initialize internal state, allocate working +storage, and emit the first few bytes of the JPEG datastream header. + +Typical code: + + jpeg_start_compress(&cinfo, TRUE); + +The "TRUE" parameter ensures that a complete JPEG interchange datastream +will be written. This is appropriate in most cases. If you think you might +want to use an abbreviated datastream, read the section on abbreviated +datastreams, below. + +Once you have called jpeg_start_compress(), you may not alter any JPEG +parameters or other fields of the JPEG object until you have completed +the compression cycle. + + +5. while (scan lines remain to be written) + jpeg_write_scanlines(...); + +Now write all the required image data by calling jpeg_write_scanlines() +one or more times. You can pass one or more scanlines in each call, up +to the total image height. In most applications it is convenient to pass +just one or a few scanlines at a time. The expected format for the passed +data is discussed under "Data formats", above. + +Image data should be written in top-to-bottom scanline order. The JPEG spec +contains some weasel wording about how top and bottom are application-defined +terms (a curious interpretation of the English language...) but if you want +your files to be compatible with everyone else's, you WILL use top-to-bottom +order. If the source data must be read in bottom-to-top order, you can use +the JPEG library's virtual array mechanism to invert the data efficiently. +Examples of this can be found in the sample application cjpeg. + +The library maintains a count of the number of scanlines written so far +in the next_scanline field of the JPEG object. Usually you can just use +this variable as the loop counter, so that the loop test looks like +"while (cinfo.next_scanline < cinfo.image_height)". + +Code for this step depends heavily on the way that you store the source data. +example.c shows the following code for the case of a full-size 2-D source +array containing 3-byte RGB pixels: + + JSAMPROW row_pointer[1]; /* pointer to a single row */ + int row_stride; /* physical row width in buffer */ + + row_stride = image_width * 3; /* JSAMPLEs per row in image_buffer */ + + while (cinfo.next_scanline < cinfo.image_height) { + row_pointer[0] = & image_buffer[cinfo.next_scanline * row_stride]; + jpeg_write_scanlines(&cinfo, row_pointer, 1); + } + +jpeg_write_scanlines() returns the number of scanlines actually written. +This will normally be equal to the number passed in, so you can usually +ignore the return value. It is different in just two cases: + * If you try to write more scanlines than the declared image height, + the additional scanlines are ignored. + * If you use a suspending data destination manager, output buffer overrun + will cause the compressor to return before accepting all the passed lines. + This feature is discussed under "I/O suspension", below. The normal + stdio destination manager will NOT cause this to happen. +In any case, the return value is the same as the change in the value of +next_scanline. + + +6. jpeg_finish_compress(...); + +After all the image data has been written, call jpeg_finish_compress() to +complete the compression cycle. This step is ESSENTIAL to ensure that the +last bufferload of data is written to the data destination. +jpeg_finish_compress() also releases working memory associated with the JPEG +object. + +Typical code: + + jpeg_finish_compress(&cinfo); + +If using the stdio destination manager, don't forget to close the output +stdio stream (if necessary) afterwards. + +If you have requested a multi-pass operating mode, such as Huffman code +optimization, jpeg_finish_compress() will perform the additional passes using +data buffered by the first pass. In this case jpeg_finish_compress() may take +quite a while to complete. With the default compression parameters, this will +not happen. + +It is an error to call jpeg_finish_compress() before writing the necessary +total number of scanlines. If you wish to abort compression, call +jpeg_abort() as discussed below. + +After completing a compression cycle, you may dispose of the JPEG object +as discussed next, or you may use it to compress another image. In that case +return to step 2, 3, or 4 as appropriate. If you do not change the +destination manager, the new datastream will be written to the same target. +If you do not change any JPEG parameters, the new datastream will be written +with the same parameters as before. Note that you can change the input image +dimensions freely between cycles, but if you change the input colorspace, you +should call jpeg_set_defaults() to adjust for the new colorspace; and then +you'll need to repeat all of step 3. + + +7. Release the JPEG compression object. + +When you are done with a JPEG compression object, destroy it by calling +jpeg_destroy_compress(). This will free all subsidiary memory (regardless of +the previous state of the object). Or you can call jpeg_destroy(), which +works for either compression or decompression objects --- this may be more +convenient if you are sharing code between compression and decompression +cases. (Actually, these routines are equivalent except for the declared type +of the passed pointer. To avoid gripes from ANSI C compilers, jpeg_destroy() +should be passed a j_common_ptr.) + +If you allocated the jpeg_compress_struct structure from malloc(), freeing +it is your responsibility --- jpeg_destroy() won't. Ditto for the error +handler structure. + +Typical code: + + jpeg_destroy_compress(&cinfo); + + +8. Aborting. + +If you decide to abort a compression cycle before finishing, you can clean up +in either of two ways: + +* If you don't need the JPEG object any more, just call + jpeg_destroy_compress() or jpeg_destroy() to release memory. This is + legitimate at any point after calling jpeg_create_compress() --- in fact, + it's safe even if jpeg_create_compress() fails. + +* If you want to re-use the JPEG object, call jpeg_abort_compress(), or call + jpeg_abort() which works on both compression and decompression objects. + This will return the object to an idle state, releasing any working memory. + jpeg_abort() is allowed at any time after successful object creation. + +Note that cleaning up the data destination, if required, is your +responsibility; neither of these routines will call term_destination(). +(See "Compressed data handling", below, for more about that.) + +jpeg_destroy() and jpeg_abort() are the only safe calls to make on a JPEG +object that has reported an error by calling error_exit (see "Error handling" +for more info). The internal state of such an object is likely to be out of +whack. Either of these two routines will return the object to a known state. + + +Decompression details +--------------------- + +Here we revisit the JPEG decompression outline given in the overview. + +1. Allocate and initialize a JPEG decompression object. + +This is just like initialization for compression, as discussed above, +except that the object is a "struct jpeg_decompress_struct" and you +call jpeg_create_decompress(). Error handling is exactly the same. + +Typical code: + + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + ... + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_decompress(&cinfo); + +(Both here and in the IJG code, we usually use variable name "cinfo" for +both compression and decompression objects.) + + +2. Specify the source of the compressed data (eg, a file). + +As previously mentioned, the JPEG library reads compressed data from a "data +source" module. The library includes one data source module which knows how +to read from a stdio stream. You can use your own source module if you want +to do something else, as discussed later. + +If you use the standard source module, you must open the source stdio stream +beforehand. Typical code for this step looks like: + + FILE * infile; + ... + if ((infile = fopen(filename, "rb")) == NULL) { + fprintf(stderr, "can't open %s\n", filename); + exit(1); + } + jpeg_stdio_src(&cinfo, infile); + +where the last line invokes the standard source module. + +WARNING: it is critical that the binary compressed data be read unchanged. +On non-Unix systems the stdio library may perform newline translation or +otherwise corrupt binary data. To suppress this behavior, you may need to use +a "b" option to fopen (as shown above), or use setmode() or another routine to +put the stdio stream in binary mode. See cjpeg.c and djpeg.c for code that +has been found to work on many systems. + +You may not change the data source between calling jpeg_read_header() and +jpeg_finish_decompress(). If you wish to read a series of JPEG images from +a single source file, you should repeat the jpeg_read_header() to +jpeg_finish_decompress() sequence without reinitializing either the JPEG +object or the data source module; this prevents buffered input data from +being discarded. + + +3. Call jpeg_read_header() to obtain image info. + +Typical code for this step is just + + jpeg_read_header(&cinfo, TRUE); + +This will read the source datastream header markers, up to the beginning +of the compressed data proper. On return, the image dimensions and other +info have been stored in the JPEG object. The application may wish to +consult this information before selecting decompression parameters. + +More complex code is necessary if + * A suspending data source is used --- in that case jpeg_read_header() + may return before it has read all the header data. See "I/O suspension", + below. The normal stdio source manager will NOT cause this to happen. + * Abbreviated JPEG files are to be processed --- see the section on + abbreviated datastreams. Standard applications that deal only in + interchange JPEG files need not be concerned with this case either. + +It is permissible to stop at this point if you just wanted to find out the +image dimensions and other header info for a JPEG file. In that case, +call jpeg_destroy() when you are done with the JPEG object, or call +jpeg_abort() to return it to an idle state before selecting a new data +source and reading another header. + + +4. Set parameters for decompression. + +jpeg_read_header() sets appropriate default decompression parameters based on +the properties of the image (in particular, its colorspace). However, you +may well want to alter these defaults before beginning the decompression. +For example, the default is to produce full color output from a color file. +If you want colormapped output you must ask for it. Other options allow the +returned image to be scaled and allow various speed/quality tradeoffs to be +selected. "Decompression parameter selection", below, gives details. + +If the defaults are appropriate, nothing need be done at this step. + +Note that all default values are set by each call to jpeg_read_header(). +If you reuse a decompression object, you cannot expect your parameter +settings to be preserved across cycles, as you can for compression. +You must set desired parameter values each time. + + +5. jpeg_start_decompress(...); + +Once the parameter values are satisfactory, call jpeg_start_decompress() to +begin decompression. This will initialize internal state, allocate working +memory, and prepare for returning data. + +Typical code is just + + jpeg_start_decompress(&cinfo); + +If you have requested a multi-pass operating mode, such as 2-pass color +quantization, jpeg_start_decompress() will do everything needed before data +output can begin. In this case jpeg_start_decompress() may take quite a while +to complete. With a single-scan (non progressive) JPEG file and default +decompression parameters, this will not happen; jpeg_start_decompress() will +return quickly. + +After this call, the final output image dimensions, including any requested +scaling, are available in the JPEG object; so is the selected colormap, if +colormapped output has been requested. Useful fields include + + output_width image width and height, as scaled + output_height + out_color_components # of color components in out_color_space + output_components # of color components returned per pixel + colormap the selected colormap, if any + actual_number_of_colors number of entries in colormap + +output_components is 1 (a colormap index) when quantizing colors; otherwise it +equals out_color_components. It is the number of JSAMPLE values that will be +emitted per pixel in the output arrays. + +Typically you will need to allocate data buffers to hold the incoming image. +You will need output_width * output_components JSAMPLEs per scanline in your +output buffer, and a total of output_height scanlines will be returned. + +Note: if you are using the JPEG library's internal memory manager to allocate +data buffers (as djpeg does), then the manager's protocol requires that you +request large buffers *before* calling jpeg_start_decompress(). This is a +little tricky since the output_XXX fields are not normally valid then. You +can make them valid by calling jpeg_calc_output_dimensions() after setting the +relevant parameters (scaling, output color space, and quantization flag). + + +6. while (scan lines remain to be read) + jpeg_read_scanlines(...); + +Now you can read the decompressed image data by calling jpeg_read_scanlines() +one or more times. At each call, you pass in the maximum number of scanlines +to be read (ie, the height of your working buffer); jpeg_read_scanlines() +will return up to that many lines. The return value is the number of lines +actually read. The format of the returned data is discussed under "Data +formats", above. Don't forget that grayscale and color JPEGs will return +different data formats! + +Image data is returned in top-to-bottom scanline order. If you must write +out the image in bottom-to-top order, you can use the JPEG library's virtual +array mechanism to invert the data efficiently. Examples of this can be +found in the sample application djpeg. + +The library maintains a count of the number of scanlines returned so far +in the output_scanline field of the JPEG object. Usually you can just use +this variable as the loop counter, so that the loop test looks like +"while (cinfo.output_scanline < cinfo.output_height)". (Note that the test +should NOT be against image_height, unless you never use scaling. The +image_height field is the height of the original unscaled image.) +The return value always equals the change in the value of output_scanline. + +If you don't use a suspending data source, it is safe to assume that +jpeg_read_scanlines() reads at least one scanline per call, until the +bottom of the image has been reached. + +If you use a buffer larger than one scanline, it is NOT safe to assume that +jpeg_read_scanlines() fills it. (The current implementation returns only a +few scanlines per call, no matter how large a buffer you pass.) So you must +always provide a loop that calls jpeg_read_scanlines() repeatedly until the +whole image has been read. + + +7. jpeg_finish_decompress(...); + +After all the image data has been read, call jpeg_finish_decompress() to +complete the decompression cycle. This causes working memory associated +with the JPEG object to be released. + +Typical code: + + jpeg_finish_decompress(&cinfo); + +If using the stdio source manager, don't forget to close the source stdio +stream if necessary. + +It is an error to call jpeg_finish_decompress() before reading the correct +total number of scanlines. If you wish to abort decompression, call +jpeg_abort() as discussed below. + +After completing a decompression cycle, you may dispose of the JPEG object as +discussed next, or you may use it to decompress another image. In that case +return to step 2 or 3 as appropriate. If you do not change the source +manager, the next image will be read from the same source. + + +8. Release the JPEG decompression object. + +When you are done with a JPEG decompression object, destroy it by calling +jpeg_destroy_decompress() or jpeg_destroy(). The previous discussion of +destroying compression objects applies here too. + +Typical code: + + jpeg_destroy_decompress(&cinfo); + + +9. Aborting. + +You can abort a decompression cycle by calling jpeg_destroy_decompress() or +jpeg_destroy() if you don't need the JPEG object any more, or +jpeg_abort_decompress() or jpeg_abort() if you want to reuse the object. +The previous discussion of aborting compression cycles applies here too. + + +Mechanics of usage: include files, linking, etc +----------------------------------------------- + +Applications using the JPEG library should include the header file jpeglib.h +to obtain declarations of data types and routines. Before including +jpeglib.h, include system headers that define at least the typedefs FILE and +size_t. On ANSI-conforming systems, including is sufficient; on +older Unix systems, you may need to define size_t. + +If the application needs to refer to individual JPEG library error codes, also +include jerror.h to define those symbols. + +jpeglib.h indirectly includes the files jconfig.h and jmorecfg.h. If you are +installing the JPEG header files in a system directory, you will want to +install all four files: jpeglib.h, jerror.h, jconfig.h, jmorecfg.h. + +The most convenient way to include the JPEG code into your executable program +is to prepare a library file ("libjpeg.a", or a corresponding name on non-Unix +machines) and reference it at your link step. If you use only half of the +library (only compression or only decompression), only that much code will be +included from the library, unless your linker is hopelessly brain-damaged. +The supplied makefiles build libjpeg.a automatically (see install.doc). + +While you can build the JPEG library as a shared library if the whim strikes +you, we don't really recommend it. The trouble with shared libraries is that +at some point you'll probably try to substitute a new version of the library +without recompiling the calling applications. That generally doesn't work +because the parameter struct declarations usually change with each new +version. In other words, the library's API is *not* guaranteed binary +compatible across versions; we only try to ensure source-code compatibility. +(In hindsight, it might have been smarter to hide the parameter structs from +applications and introduce a ton of access functions instead. Too late now, +however.) + +On some systems your application may need to set up a signal handler to ensure +that temporary files are deleted if the program is interrupted. This is most +critical if you are on MS-DOS and use the jmemdos.c memory manager back end; +it will try to grab extended memory for temp files, and that space will NOT be +freed automatically. See cjpeg.c or djpeg.c for an example signal handler. + +It may be worth pointing out that the core JPEG library does not actually +require the stdio library: only the default source/destination managers and +error handler need it. You can use the library in a stdio-less environment +if you replace those modules and use jmemnobs.c (or another memory manager of +your own devising). More info about the minimum system library requirements +may be found in jinclude.h. + + +ADVANCED FEATURES +================= + +Compression parameter selection +------------------------------- + +This section describes all the optional parameters you can set for JPEG +compression, as well as the "helper" routines provided to assist in this +task. Proper setting of some parameters requires detailed understanding +of the JPEG standard; if you don't know what a parameter is for, it's best +not to mess with it! See REFERENCES in the README file for pointers to +more info about JPEG. + +It's a good idea to call jpeg_set_defaults() first, even if you plan to set +all the parameters; that way your code is more likely to work with future JPEG +libraries that have additional parameters. For the same reason, we recommend +you use a helper routine where one is provided, in preference to twiddling +cinfo fields directly. + +The helper routines are: + +jpeg_set_defaults (j_compress_ptr cinfo) + This routine sets all JPEG parameters to reasonable defaults, using + only the input image's color space (field in_color_space, which must + already be set in cinfo). Many applications will only need to use + this routine and perhaps jpeg_set_quality(). + +jpeg_set_colorspace (j_compress_ptr cinfo, J_COLOR_SPACE colorspace) + Sets the JPEG file's colorspace (field jpeg_color_space) as specified, + and sets other color-space-dependent parameters appropriately. See + "Special color spaces", below, before using this. A large number of + parameters, including all per-component parameters, are set by this + routine; if you want to twiddle individual parameters you should call + jpeg_set_colorspace() before rather than after. + +jpeg_default_colorspace (j_compress_ptr cinfo) + Selects an appropriate JPEG colorspace based on cinfo->in_color_space, + and calls jpeg_set_colorspace(). This is actually a subroutine of + jpeg_set_defaults(). It's broken out in case you want to change + just the colorspace-dependent JPEG parameters. + +jpeg_set_quality (j_compress_ptr cinfo, int quality, boolean force_baseline) + Constructs JPEG quantization tables appropriate for the indicated + quality setting. The quality value is expressed on the 0..100 scale + recommended by IJG (cjpeg's "-quality" switch uses this routine). + Note that the exact mapping from quality values to tables may change + in future IJG releases as more is learned about DCT quantization. + If the force_baseline parameter is TRUE, then the quantization table + entries are constrained to the range 1..255 for full JPEG baseline + compatibility. In the current implementation, this only makes a + difference for quality settings below 25, and it effectively prevents + very small/low quality files from being generated. The IJG decoder + is capable of reading the non-baseline files generated at low quality + settings when force_baseline is FALSE, but other decoders may not be. + +jpeg_set_linear_quality (j_compress_ptr cinfo, int scale_factor, + boolean force_baseline) + Same as jpeg_set_quality() except that the generated tables are the + sample tables given in the JPEC spec section K.1, multiplied by the + specified scale factor (which is expressed as a percentage; thus + scale_factor = 100 reproduces the spec's tables). Note that larger + scale factors give lower quality. This entry point is useful for + conforming to the Adobe PostScript DCT conventions, but we do not + recommend linear scaling as a user-visible quality scale otherwise. + force_baseline again constrains the computed table entries to 1..255. + +int jpeg_quality_scaling (int quality) + Converts a value on the IJG-recommended quality scale to a linear + scaling percentage. Note that this routine may change or go away + in future releases --- IJG may choose to adopt a scaling method that + can't be expressed as a simple scalar multiplier, in which case the + premise of this routine collapses. Caveat user. + +jpeg_add_quant_table (j_compress_ptr cinfo, int which_tbl, + const unsigned int *basic_table, + int scale_factor, boolean force_baseline) + Allows an arbitrary quantization table to be created. which_tbl + indicates which table slot to fill. basic_table points to an array + of 64 unsigned ints given in normal array order. These values are + multiplied by scale_factor/100 and then clamped to the range 1..65535 + (or to 1..255 if force_baseline is TRUE). + CAUTION: prior to library version 6a, jpeg_add_quant_table expected + the basic table to be given in JPEG zigzag order. If you need to + write code that works with either older or newer versions of this + routine, you must check the library version number. Something like + "#if JPEG_LIB_VERSION >= 61" is the right test. + +jpeg_simple_progression (j_compress_ptr cinfo) + Generates a default scan script for writing a progressive-JPEG file. + This is the recommended method of creating a progressive file, + unless you want to make a custom scan sequence. You must ensure that + the JPEG color space is set correctly before calling this routine. + + +Compression parameters (cinfo fields) include: + +J_DCT_METHOD dct_method + Selects the algorithm used for the DCT step. Choices are: + JDCT_ISLOW: slow but accurate integer algorithm + JDCT_IFAST: faster, less accurate integer method + JDCT_FLOAT: floating-point method + JDCT_DEFAULT: default method (normally JDCT_ISLOW) + JDCT_FASTEST: fastest method (normally JDCT_IFAST) + The FLOAT method is very slightly more accurate than the ISLOW method, + but may give different results on different machines due to varying + roundoff behavior. The integer methods should give the same results + on all machines. On machines with sufficiently fast FP hardware, the + floating-point method may also be the fastest. The IFAST method is + considerably less accurate than the other two; its use is not + recommended if high quality is a concern. JDCT_DEFAULT and + JDCT_FASTEST are macros configurable by each installation. + +J_COLOR_SPACE jpeg_color_space +int num_components + The JPEG color space and corresponding number of components; see + "Special color spaces", below, for more info. We recommend using + jpeg_set_color_space() if you want to change these. + +boolean optimize_coding + TRUE causes the compressor to compute optimal Huffman coding tables + for the image. This requires an extra pass over the data and + therefore costs a good deal of space and time. The default is + FALSE, which tells the compressor to use the supplied or default + Huffman tables. In most cases optimal tables save only a few percent + of file size compared to the default tables. Note that when this is + TRUE, you need not supply Huffman tables at all, and any you do + supply will be overwritten. + +unsigned int restart_interval +int restart_in_rows + To emit restart markers in the JPEG file, set one of these nonzero. + Set restart_interval to specify the exact interval in MCU blocks. + Set restart_in_rows to specify the interval in MCU rows. (If + restart_in_rows is not 0, then restart_interval is set after the + image width in MCUs is computed.) Defaults are zero (no restarts). + One restart marker per MCU row is often a good choice. + NOTE: the overhead of restart markers is higher in grayscale JPEG + files than in color files, and MUCH higher in progressive JPEGs. + If you use restarts, you may want to use larger intervals in those + cases. + +const jpeg_scan_info * scan_info +int num_scans + By default, scan_info is NULL; this causes the compressor to write a + single-scan sequential JPEG file. If not NULL, scan_info points to + an array of scan definition records of length num_scans. The + compressor will then write a JPEG file having one scan for each scan + definition record. This is used to generate noninterleaved or + progressive JPEG files. The library checks that the scan array + defines a valid JPEG scan sequence. (jpeg_simple_progression creates + a suitable scan definition array for progressive JPEG.) This is + discussed further under "Progressive JPEG support". + +int smoothing_factor + If non-zero, the input image is smoothed; the value should be 1 for + minimal smoothing to 100 for maximum smoothing. Consult jcsample.c + for details of the smoothing algorithm. The default is zero. + +boolean write_JFIF_header + If TRUE, a JFIF APP0 marker is emitted. jpeg_set_defaults() and + jpeg_set_colorspace() set this TRUE if a JFIF-legal JPEG color space + (ie, YCbCr or grayscale) is selected, otherwise FALSE. + +UINT8 JFIF_major_version +UINT8 JFIF_minor_version + The version number to be written into the JFIF marker. + jpeg_set_defaults() initializes the version to 1.01 (major=minor=1). + You should set it to 1.02 (major=1, minor=2) if you plan to write + any JFIF 1.02 extension markers. + +UINT8 density_unit +UINT16 X_density +UINT16 Y_density + The resolution information to be written into the JFIF marker; + not used otherwise. density_unit may be 0 for unknown, + 1 for dots/inch, or 2 for dots/cm. The default values are 0,1,1 + indicating square pixels of unknown size. + +boolean write_Adobe_marker + If TRUE, an Adobe APP14 marker is emitted. jpeg_set_defaults() and + jpeg_set_colorspace() set this TRUE if JPEG color space RGB, CMYK, + or YCCK is selected, otherwise FALSE. It is generally a bad idea + to set both write_JFIF_header and write_Adobe_marker. In fact, + you probably shouldn't change the default settings at all --- the + default behavior ensures that the JPEG file's color space can be + recognized by the decoder. + +JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS] + Pointers to coefficient quantization tables, one per table slot, + or NULL if no table is defined for a slot. Usually these should + be set via one of the above helper routines; jpeg_add_quant_table() + is general enough to define any quantization table. The other + routines will set up table slot 0 for luminance quality and table + slot 1 for chrominance. + +JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS] +JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS] + Pointers to Huffman coding tables, one per table slot, or NULL if + no table is defined for a slot. Slots 0 and 1 are filled with the + JPEG sample tables by jpeg_set_defaults(). If you need to allocate + more table structures, jpeg_alloc_huff_table() may be used. + Note that optimal Huffman tables can be computed for an image + by setting optimize_coding, as discussed above; there's seldom + any need to mess with providing your own Huffman tables. + +There are some additional cinfo fields which are not documented here +because you currently can't change them; for example, you can't set +arith_code TRUE because arithmetic coding is unsupported. + + +Per-component parameters are stored in the struct cinfo.comp_info[i] for +component number i. Note that components here refer to components of the +JPEG color space, *not* the source image color space. A suitably large +comp_info[] array is allocated by jpeg_set_defaults(); if you choose not +to use that routine, it's up to you to allocate the array. + +int component_id + The one-byte identifier code to be recorded in the JPEG file for + this component. For the standard color spaces, we recommend you + leave the default values alone. + +int h_samp_factor +int v_samp_factor + Horizontal and vertical sampling factors for the component; must + be 1..4 according to the JPEG standard. Note that larger sampling + factors indicate a higher-resolution component; many people find + this behavior quite unintuitive. The default values are 2,2 for + luminance components and 1,1 for chrominance components, except + for grayscale where 1,1 is used. + +int quant_tbl_no + Quantization table number for component. The default value is + 0 for luminance components and 1 for chrominance components. + +int dc_tbl_no +int ac_tbl_no + DC and AC entropy coding table numbers. The default values are + 0 for luminance components and 1 for chrominance components. + +int component_index + Must equal the component's index in comp_info[]. (Beginning in + release v6, the compressor library will fill this in automatically; + you don't have to.) + + +Decompression parameter selection +--------------------------------- + +Decompression parameter selection is somewhat simpler than compression +parameter selection, since all of the JPEG internal parameters are +recorded in the source file and need not be supplied by the application. +(Unless you are working with abbreviated files, in which case see +"Abbreviated datastreams", below.) Decompression parameters control +the postprocessing done on the image to deliver it in a format suitable +for the application's use. Many of the parameters control speed/quality +tradeoffs, in which faster decompression may be obtained at the price of +a poorer-quality image. The defaults select the highest quality (slowest) +processing. + +The following fields in the JPEG object are set by jpeg_read_header() and +may be useful to the application in choosing decompression parameters: + +JDIMENSION image_width Width and height of image +JDIMENSION image_height +int num_components Number of color components +J_COLOR_SPACE jpeg_color_space Colorspace of image +boolean saw_JFIF_marker TRUE if a JFIF APP0 marker was seen + UINT8 JFIF_major_version Version information from JFIF marker + UINT8 JFIF_minor_version + UINT8 density_unit Resolution data from JFIF marker + UINT16 X_density + UINT16 Y_density +boolean saw_Adobe_marker TRUE if an Adobe APP14 marker was seen + UINT8 Adobe_transform Color transform code from Adobe marker + +The JPEG color space, unfortunately, is something of a guess since the JPEG +standard proper does not provide a way to record it. In practice most files +adhere to the JFIF or Adobe conventions, and the decoder will recognize these +correctly. See "Special color spaces", below, for more info. + + +The decompression parameters that determine the basic properties of the +returned image are: + +J_COLOR_SPACE out_color_space + Output color space. jpeg_read_header() sets an appropriate default + based on jpeg_color_space; typically it will be RGB or grayscale. + The application can change this field to request output in a different + colorspace. For example, set it to JCS_GRAYSCALE to get grayscale + output from a color file. (This is useful for previewing: grayscale + output is faster than full color since the color components need not + be processed.) Note that not all possible color space transforms are + currently implemented; you may need to extend jdcolor.c if you want an + unusual conversion. + +unsigned int scale_num, scale_denom + Scale the image by the fraction scale_num/scale_denom. Default is + 1/1, or no scaling. Currently, the only supported scaling ratios + are 1/1, 1/2, 1/4, and 1/8. (The library design allows for arbitrary + scaling ratios but this is not likely to be implemented any time soon.) + Smaller scaling ratios permit significantly faster decoding since + fewer pixels need be processed and a simpler IDCT method can be used. + +boolean quantize_colors + If set TRUE, colormapped output will be delivered. Default is FALSE, + meaning that full-color output will be delivered. + +The next three parameters are relevant only if quantize_colors is TRUE. + +int desired_number_of_colors + Maximum number of colors to use in generating a library-supplied color + map (the actual number of colors is returned in a different field). + Default 256. Ignored when the application supplies its own color map. + +boolean two_pass_quantize + If TRUE, an extra pass over the image is made to select a custom color + map for the image. This usually looks a lot better than the one-size- + fits-all colormap that is used otherwise. Default is TRUE. Ignored + when the application supplies its own color map. + +J_DITHER_MODE dither_mode + Selects color dithering method. Supported values are: + JDITHER_NONE no dithering: fast, very low quality + JDITHER_ORDERED ordered dither: moderate speed and quality + JDITHER_FS Floyd-Steinberg dither: slow, high quality + Default is JDITHER_FS. (At present, ordered dither is implemented + only in the single-pass, standard-colormap case. If you ask for + ordered dither when two_pass_quantize is TRUE or when you supply + an external color map, you'll get F-S dithering.) + +When quantize_colors is TRUE, the target color map is described by the next +two fields. colormap is set to NULL by jpeg_read_header(). The application +can supply a color map by setting colormap non-NULL and setting +actual_number_of_colors to the map size. Otherwise, jpeg_start_decompress() +selects a suitable color map and sets these two fields itself. +[Implementation restriction: at present, an externally supplied colormap is +only accepted for 3-component output color spaces.] + +JSAMPARRAY colormap + The color map, represented as a 2-D pixel array of out_color_components + rows and actual_number_of_colors columns. Ignored if not quantizing. + CAUTION: if the JPEG library creates its own colormap, the storage + pointed to by this field is released by jpeg_finish_decompress(). + Copy the colormap somewhere else first, if you want to save it. + +int actual_number_of_colors + The number of colors in the color map. + +Additional decompression parameters that the application may set include: + +J_DCT_METHOD dct_method + Selects the algorithm used for the DCT step. Choices are the same + as described above for compression. + +boolean do_fancy_upsampling + If TRUE, do careful upsampling of chroma components. If FALSE, + a faster but sloppier method is used. Default is TRUE. The visual + impact of the sloppier method is often very small. + +boolean do_block_smoothing + If TRUE, interblock smoothing is applied in early stages of decoding + progressive JPEG files; if FALSE, not. Default is TRUE. Early + progression stages look "fuzzy" with smoothing, "blocky" without. + In any case, block smoothing ceases to be applied after the first few + AC coefficients are known to full accuracy, so it is relevant only + when using buffered-image mode for progressive images. + +boolean enable_1pass_quant +boolean enable_external_quant +boolean enable_2pass_quant + These are significant only in buffered-image mode, which is + described in its own section below. + + +The output image dimensions are given by the following fields. These are +computed from the source image dimensions and the decompression parameters +by jpeg_start_decompress(). You can also call jpeg_calc_output_dimensions() +to obtain the values that will result from the current parameter settings. +This can be useful if you are trying to pick a scaling ratio that will get +close to a desired target size. It's also important if you are using the +JPEG library's memory manager to allocate output buffer space, because you +are supposed to request such buffers *before* jpeg_start_decompress(). + +JDIMENSION output_width Actual dimensions of output image. +JDIMENSION output_height +int out_color_components Number of color components in out_color_space. +int output_components Number of color components returned. +int rec_outbuf_height Recommended height of scanline buffer. + +When quantizing colors, output_components is 1, indicating a single color map +index per pixel. Otherwise it equals out_color_components. The output arrays +are required to be output_width * output_components JSAMPLEs wide. + +rec_outbuf_height is the recommended minimum height (in scanlines) of the +buffer passed to jpeg_read_scanlines(). If the buffer is smaller, the +library will still work, but time will be wasted due to unnecessary data +copying. In high-quality modes, rec_outbuf_height is always 1, but some +faster, lower-quality modes set it to larger values (typically 2 to 4). +If you are going to ask for a high-speed processing mode, you may as well +go to the trouble of honoring rec_outbuf_height so as to avoid data copying. +(An output buffer larger than rec_outbuf_height lines is OK, but won't +provide any material speed improvement over that height.) + + +Special color spaces +-------------------- + +The JPEG standard itself is "color blind" and doesn't specify any particular +color space. It is customary to convert color data to a luminance/chrominance +color space before compressing, since this permits greater compression. The +existing de-facto JPEG file format standards specify YCbCr or grayscale data +(JFIF), or grayscale, RGB, YCbCr, CMYK, or YCCK (Adobe). For special +applications such as multispectral images, other color spaces can be used, +but it must be understood that such files will be unportable. + +The JPEG library can handle the most common colorspace conversions (namely +RGB <=> YCbCr and CMYK <=> YCCK). It can also deal with data of an unknown +color space, passing it through without conversion. If you deal extensively +with an unusual color space, you can easily extend the library to understand +additional color spaces and perform appropriate conversions. + +For compression, the source data's color space is specified by field +in_color_space. This is transformed to the JPEG file's color space given +by jpeg_color_space. jpeg_set_defaults() chooses a reasonable JPEG color +space depending on in_color_space, but you can override this by calling +jpeg_set_colorspace(). Of course you must select a supported transformation. +jccolor.c currently supports the following transformations: + RGB => YCbCr + RGB => GRAYSCALE + YCbCr => GRAYSCALE + CMYK => YCCK +plus the null transforms: GRAYSCALE => GRAYSCALE, RGB => RGB, +YCbCr => YCbCr, CMYK => CMYK, YCCK => YCCK, and UNKNOWN => UNKNOWN. + +The de-facto file format standards (JFIF and Adobe) specify APPn markers that +indicate the color space of the JPEG file. It is important to ensure that +these are written correctly, or omitted if the JPEG file's color space is not +one of the ones supported by the de-facto standards. jpeg_set_colorspace() +will set the compression parameters to include or omit the APPn markers +properly, so long as it is told the truth about the JPEG color space. +For example, if you are writing some random 3-component color space without +conversion, don't try to fake out the library by setting in_color_space and +jpeg_color_space to JCS_YCbCr; use JCS_UNKNOWN. You may want to write an +APPn marker of your own devising to identify the colorspace --- see "Special +markers", below. + +When told that the color space is UNKNOWN, the library will default to using +luminance-quality compression parameters for all color components. You may +well want to change these parameters. See the source code for +jpeg_set_colorspace(), in jcparam.c, for details. + +For decompression, the JPEG file's color space is given in jpeg_color_space, +and this is transformed to the output color space out_color_space. +jpeg_read_header's setting of jpeg_color_space can be relied on if the file +conforms to JFIF or Adobe conventions, but otherwise it is no better than a +guess. If you know the JPEG file's color space for certain, you can override +jpeg_read_header's guess by setting jpeg_color_space. jpeg_read_header also +selects a default output color space based on (its guess of) jpeg_color_space; +set out_color_space to override this. Again, you must select a supported +transformation. jdcolor.c currently supports + YCbCr => GRAYSCALE + YCbCr => RGB + GRAYSCALE => RGB + YCCK => CMYK +as well as the null transforms. (Since GRAYSCALE=>RGB is provided, an +application can force grayscale JPEGs to look like color JPEGs if it only +wants to handle one case.) + +The two-pass color quantizer, jquant2.c, is specialized to handle RGB data +(it weights distances appropriately for RGB colors). You'll need to modify +the code if you want to use it for non-RGB output color spaces. Note that +jquant2.c is used to map to an application-supplied colormap as well as for +the normal two-pass colormap selection process. + +CAUTION: it appears that Adobe Photoshop writes inverted data in CMYK JPEG +files: 0 represents 100% ink coverage, rather than 0% ink as you'd expect. +This is arguably a bug in Photoshop, but if you need to work with Photoshop +CMYK files, you will have to deal with it in your application. We cannot +"fix" this in the library by inverting the data during the CMYK<=>YCCK +transform, because that would break other applications, notably Ghostscript. +Photoshop versions prior to 3.0 write EPS files containing JPEG-encoded CMYK +data in the same inverted-YCCK representation used in bare JPEG files, but +the surrounding PostScript code performs an inversion using the PS image +operator. I am told that Photoshop 3.0 will write uninverted YCCK in +EPS/JPEG files, and will omit the PS-level inversion. (But the data +polarity used in bare JPEG files will not change in 3.0.) In either case, +the JPEG library must not invert the data itself, or else Ghostscript would +read these EPS files incorrectly. + + +Error handling +-------------- + +When the default error handler is used, any error detected inside the JPEG +routines will cause a message to be printed on stderr, followed by exit(). +You can supply your own error handling routines to override this behavior +and to control the treatment of nonfatal warnings and trace/debug messages. +The file example.c illustrates the most common case, which is to have the +application regain control after an error rather than exiting. + +The JPEG library never writes any message directly; it always goes through +the error handling routines. Three classes of messages are recognized: + * Fatal errors: the library cannot continue. + * Warnings: the library can continue, but the data is corrupt, and a + damaged output image is likely to result. + * Trace/informational messages. These come with a trace level indicating + the importance of the message; you can control the verbosity of the + program by adjusting the maximum trace level that will be displayed. + +You may, if you wish, simply replace the entire JPEG error handling module +(jerror.c) with your own code. However, you can avoid code duplication by +only replacing some of the routines depending on the behavior you need. +This is accomplished by calling jpeg_std_error() as usual, but then overriding +some of the method pointers in the jpeg_error_mgr struct, as illustrated by +example.c. + +All of the error handling routines will receive a pointer to the JPEG object +(a j_common_ptr which points to either a jpeg_compress_struct or a +jpeg_decompress_struct; if you need to tell which, test the is_decompressor +field). This struct includes a pointer to the error manager struct in its +"err" field. Frequently, custom error handler routines will need to access +additional data which is not known to the JPEG library or the standard error +handler. The most convenient way to do this is to embed either the JPEG +object or the jpeg_error_mgr struct in a larger structure that contains +additional fields; then casting the passed pointer provides access to the +additional fields. Again, see example.c for one way to do it. (Beginning +with IJG version 6b, there is also a void pointer "client_data" in each +JPEG object, which the application can also use to find related data. +The library does not touch client_data at all.) + +The individual methods that you might wish to override are: + +error_exit (j_common_ptr cinfo) + Receives control for a fatal error. Information sufficient to + generate the error message has been stored in cinfo->err; call + output_message to display it. Control must NOT return to the caller; + generally this routine will exit() or longjmp() somewhere. + Typically you would override this routine to get rid of the exit() + default behavior. Note that if you continue processing, you should + clean up the JPEG object with jpeg_abort() or jpeg_destroy(). + +output_message (j_common_ptr cinfo) + Actual output of any JPEG message. Override this to send messages + somewhere other than stderr. Note that this method does not know + how to generate a message, only where to send it. + +format_message (j_common_ptr cinfo, char * buffer) + Constructs a readable error message string based on the error info + stored in cinfo->err. This method is called by output_message. Few + applications should need to override this method. One possible + reason for doing so is to implement dynamic switching of error message + language. + +emit_message (j_common_ptr cinfo, int msg_level) + Decide whether or not to emit a warning or trace message; if so, + calls output_message. The main reason for overriding this method + would be to abort on warnings. msg_level is -1 for warnings, + 0 and up for trace messages. + +Only error_exit() and emit_message() are called from the rest of the JPEG +library; the other two are internal to the error handler. + +The actual message texts are stored in an array of strings which is pointed to +by the field err->jpeg_message_table. The messages are numbered from 0 to +err->last_jpeg_message, and it is these code numbers that are used in the +JPEG library code. You could replace the message texts (for instance, with +messages in French or German) by changing the message table pointer. See +jerror.h for the default texts. CAUTION: this table will almost certainly +change or grow from one library version to the next. + +It may be useful for an application to add its own message texts that are +handled by the same mechanism. The error handler supports a second "add-on" +message table for this purpose. To define an addon table, set the pointer +err->addon_message_table and the message numbers err->first_addon_message and +err->last_addon_message. If you number the addon messages beginning at 1000 +or so, you won't have to worry about conflicts with the library's built-in +messages. See the sample applications cjpeg/djpeg for an example of using +addon messages (the addon messages are defined in cderror.h). + +Actual invocation of the error handler is done via macros defined in jerror.h: + ERREXITn(...) for fatal errors + WARNMSn(...) for corrupt-data warnings + TRACEMSn(...) for trace and informational messages. +These macros store the message code and any additional parameters into the +error handler struct, then invoke the error_exit() or emit_message() method. +The variants of each macro are for varying numbers of additional parameters. +The additional parameters are inserted into the generated message using +standard printf() format codes. + +See jerror.h and jerror.c for further details. + + +Compressed data handling (source and destination managers) +---------------------------------------------------------- + +The JPEG compression library sends its compressed data to a "destination +manager" module. The default destination manager just writes the data to a +stdio stream, but you can provide your own manager to do something else. +Similarly, the decompression library calls a "source manager" to obtain the +compressed data; you can provide your own source manager if you want the data +to come from somewhere other than a stdio stream. + +In both cases, compressed data is processed a bufferload at a time: the +destination or source manager provides a work buffer, and the library invokes +the manager only when the buffer is filled or emptied. (You could define a +one-character buffer to force the manager to be invoked for each byte, but +that would be rather inefficient.) The buffer's size and location are +controlled by the manager, not by the library. For example, if you desired to +decompress a JPEG datastream that was all in memory, you could just make the +buffer pointer and length point to the original data in memory. Then the +buffer-reload procedure would be invoked only if the decompressor ran off the +end of the datastream, which would indicate an erroneous datastream. + +The work buffer is defined as an array of datatype JOCTET, which is generally +"char" or "unsigned char". On a machine where char is not exactly 8 bits +wide, you must define JOCTET as a wider data type and then modify the data +source and destination modules to transcribe the work arrays into 8-bit units +on external storage. + +A data destination manager struct contains a pointer and count defining the +next byte to write in the work buffer and the remaining free space: + + JOCTET * next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + +The library increments the pointer and decrements the count until the buffer +is filled. The manager's empty_output_buffer method must reset the pointer +and count. The manager is expected to remember the buffer's starting address +and total size in private fields not visible to the library. + +A data destination manager provides three methods: + +init_destination (j_compress_ptr cinfo) + Initialize destination. This is called by jpeg_start_compress() + before any data is actually written. It must initialize + next_output_byte and free_in_buffer. free_in_buffer must be + initialized to a positive value. + +empty_output_buffer (j_compress_ptr cinfo) + This is called whenever the buffer has filled (free_in_buffer + reaches zero). In typical applications, it should write out the + *entire* buffer (use the saved start address and buffer length; + ignore the current state of next_output_byte and free_in_buffer). + Then reset the pointer & count to the start of the buffer, and + return TRUE indicating that the buffer has been dumped. + free_in_buffer must be set to a positive value when TRUE is + returned. A FALSE return should only be used when I/O suspension is + desired (this operating mode is discussed in the next section). + +term_destination (j_compress_ptr cinfo) + Terminate destination --- called by jpeg_finish_compress() after all + data has been written. In most applications, this must flush any + data remaining in the buffer. Use either next_output_byte or + free_in_buffer to determine how much data is in the buffer. + +term_destination() is NOT called by jpeg_abort() or jpeg_destroy(). If you +want the destination manager to be cleaned up during an abort, you must do it +yourself. + +You will also need code to create a jpeg_destination_mgr struct, fill in its +method pointers, and insert a pointer to the struct into the "dest" field of +the JPEG compression object. This can be done in-line in your setup code if +you like, but it's probably cleaner to provide a separate routine similar to +the jpeg_stdio_dest() routine of the supplied destination manager. + +Decompression source managers follow a parallel design, but with some +additional frammishes. The source manager struct contains a pointer and count +defining the next byte to read from the work buffer and the number of bytes +remaining: + + const JOCTET * next_input_byte; /* => next byte to read from buffer */ + size_t bytes_in_buffer; /* # of bytes remaining in buffer */ + +The library increments the pointer and decrements the count until the buffer +is emptied. The manager's fill_input_buffer method must reset the pointer and +count. In most applications, the manager must remember the buffer's starting +address and total size in private fields not visible to the library. + +A data source manager provides five methods: + +init_source (j_decompress_ptr cinfo) + Initialize source. This is called by jpeg_read_header() before any + data is actually read. Unlike init_destination(), it may leave + bytes_in_buffer set to 0 (in which case a fill_input_buffer() call + will occur immediately). + +fill_input_buffer (j_decompress_ptr cinfo) + This is called whenever bytes_in_buffer has reached zero and more + data is wanted. In typical applications, it should read fresh data + into the buffer (ignoring the current state of next_input_byte and + bytes_in_buffer), reset the pointer & count to the start of the + buffer, and return TRUE indicating that the buffer has been reloaded. + It is not necessary to fill the buffer entirely, only to obtain at + least one more byte. bytes_in_buffer MUST be set to a positive value + if TRUE is returned. A FALSE return should only be used when I/O + suspension is desired (this mode is discussed in the next section). + +skip_input_data (j_decompress_ptr cinfo, long num_bytes) + Skip num_bytes worth of data. The buffer pointer and count should + be advanced over num_bytes input bytes, refilling the buffer as + needed. This is used to skip over a potentially large amount of + uninteresting data (such as an APPn marker). In some applications + it may be possible to optimize away the reading of the skipped data, + but it's not clear that being smart is worth much trouble; large + skips are uncommon. bytes_in_buffer may be zero on return. + A zero or negative skip count should be treated as a no-op. + +resync_to_restart (j_decompress_ptr cinfo, int desired) + This routine is called only when the decompressor has failed to find + a restart (RSTn) marker where one is expected. Its mission is to + find a suitable point for resuming decompression. For most + applications, we recommend that you just use the default resync + procedure, jpeg_resync_to_restart(). However, if you are able to back + up in the input data stream, or if you have a-priori knowledge about + the likely location of restart markers, you may be able to do better. + Read the read_restart_marker() and jpeg_resync_to_restart() routines + in jdmarker.c if you think you'd like to implement your own resync + procedure. + +term_source (j_decompress_ptr cinfo) + Terminate source --- called by jpeg_finish_decompress() after all + data has been read. Often a no-op. + +For both fill_input_buffer() and skip_input_data(), there is no such thing +as an EOF return. If the end of the file has been reached, the routine has +a choice of exiting via ERREXIT() or inserting fake data into the buffer. +In most cases, generating a warning message and inserting a fake EOI marker +is the best course of action --- this will allow the decompressor to output +however much of the image is there. In pathological cases, the decompressor +may swallow the EOI and again demand data ... just keep feeding it fake EOIs. +jdatasrc.c illustrates the recommended error recovery behavior. + +term_source() is NOT called by jpeg_abort() or jpeg_destroy(). If you want +the source manager to be cleaned up during an abort, you must do it yourself. + +You will also need code to create a jpeg_source_mgr struct, fill in its method +pointers, and insert a pointer to the struct into the "src" field of the JPEG +decompression object. This can be done in-line in your setup code if you +like, but it's probably cleaner to provide a separate routine similar to the +jpeg_stdio_src() routine of the supplied source manager. + +For more information, consult the stdio source and destination managers +in jdatasrc.c and jdatadst.c. + + +I/O suspension +-------------- + +Some applications need to use the JPEG library as an incremental memory-to- +memory filter: when the compressed data buffer is filled or emptied, they want +control to return to the outer loop, rather than expecting that the buffer can +be emptied or reloaded within the data source/destination manager subroutine. +The library supports this need by providing an "I/O suspension" mode, which we +describe in this section. + +The I/O suspension mode is not a panacea: nothing is guaranteed about the +maximum amount of time spent in any one call to the library, so it will not +eliminate response-time problems in single-threaded applications. If you +need guaranteed response time, we suggest you "bite the bullet" and implement +a real multi-tasking capability. + +To use I/O suspension, cooperation is needed between the calling application +and the data source or destination manager; you will always need a custom +source/destination manager. (Please read the previous section if you haven't +already.) The basic idea is that the empty_output_buffer() or +fill_input_buffer() routine is a no-op, merely returning FALSE to indicate +that it has done nothing. Upon seeing this, the JPEG library suspends +operation and returns to its caller. The surrounding application is +responsible for emptying or refilling the work buffer before calling the +JPEG library again. + +Compression suspension: + +For compression suspension, use an empty_output_buffer() routine that returns +FALSE; typically it will not do anything else. This will cause the +compressor to return to the caller of jpeg_write_scanlines(), with the return +value indicating that not all the supplied scanlines have been accepted. +The application must make more room in the output buffer, adjust the output +buffer pointer/count appropriately, and then call jpeg_write_scanlines() +again, pointing to the first unconsumed scanline. + +When forced to suspend, the compressor will backtrack to a convenient stopping +point (usually the start of the current MCU); it will regenerate some output +data when restarted. Therefore, although empty_output_buffer() is only +called when the buffer is filled, you should NOT write out the entire buffer +after a suspension. Write only the data up to the current position of +next_output_byte/free_in_buffer. The data beyond that point will be +regenerated after resumption. + +Because of the backtracking behavior, a good-size output buffer is essential +for efficiency; you don't want the compressor to suspend often. (In fact, an +overly small buffer could lead to infinite looping, if a single MCU required +more data than would fit in the buffer.) We recommend a buffer of at least +several Kbytes. You may want to insert explicit code to ensure that you don't +call jpeg_write_scanlines() unless there is a reasonable amount of space in +the output buffer; in other words, flush the buffer before trying to compress +more data. + +The compressor does not allow suspension while it is trying to write JPEG +markers at the beginning and end of the file. This means that: + * At the beginning of a compression operation, there must be enough free + space in the output buffer to hold the header markers (typically 600 or + so bytes). The recommended buffer size is bigger than this anyway, so + this is not a problem as long as you start with an empty buffer. However, + this restriction might catch you if you insert large special markers, such + as a JFIF thumbnail image, without flushing the buffer afterwards. + * When you call jpeg_finish_compress(), there must be enough space in the + output buffer to emit any buffered data and the final EOI marker. In the + current implementation, half a dozen bytes should suffice for this, but + for safety's sake we recommend ensuring that at least 100 bytes are free + before calling jpeg_finish_compress(). + +A more significant restriction is that jpeg_finish_compress() cannot suspend. +This means you cannot use suspension with multi-pass operating modes, namely +Huffman code optimization and multiple-scan output. Those modes write the +whole file during jpeg_finish_compress(), which will certainly result in +buffer overrun. (Note that this restriction applies only to compression, +not decompression. The decompressor supports input suspension in all of its +operating modes.) + +Decompression suspension: + +For decompression suspension, use a fill_input_buffer() routine that simply +returns FALSE (except perhaps during error recovery, as discussed below). +This will cause the decompressor to return to its caller with an indication +that suspension has occurred. This can happen at four places: + * jpeg_read_header(): will return JPEG_SUSPENDED. + * jpeg_start_decompress(): will return FALSE, rather than its usual TRUE. + * jpeg_read_scanlines(): will return the number of scanlines already + completed (possibly 0). + * jpeg_finish_decompress(): will return FALSE, rather than its usual TRUE. +The surrounding application must recognize these cases, load more data into +the input buffer, and repeat the call. In the case of jpeg_read_scanlines(), +increment the passed pointers past any scanlines successfully read. + +Just as with compression, the decompressor will typically backtrack to a +convenient restart point before suspending. When fill_input_buffer() is +called, next_input_byte/bytes_in_buffer point to the current restart point, +which is where the decompressor will backtrack to if FALSE is returned. +The data beyond that position must NOT be discarded if you suspend; it needs +to be re-read upon resumption. In most implementations, you'll need to shift +this data down to the start of your work buffer and then load more data after +it. Again, this behavior means that a several-Kbyte work buffer is essential +for decent performance; furthermore, you should load a reasonable amount of +new data before resuming decompression. (If you loaded, say, only one new +byte each time around, you could waste a LOT of cycles.) + +The skip_input_data() source manager routine requires special care in a +suspension scenario. This routine is NOT granted the ability to suspend the +decompressor; it can decrement bytes_in_buffer to zero, but no more. If the +requested skip distance exceeds the amount of data currently in the input +buffer, then skip_input_data() must set bytes_in_buffer to zero and record the +additional skip distance somewhere else. The decompressor will immediately +call fill_input_buffer(), which should return FALSE, which will cause a +suspension return. The surrounding application must then arrange to discard +the recorded number of bytes before it resumes loading the input buffer. +(Yes, this design is rather baroque, but it avoids complexity in the far more +common case where a non-suspending source manager is used.) + +If the input data has been exhausted, we recommend that you emit a warning +and insert dummy EOI markers just as a non-suspending data source manager +would do. This can be handled either in the surrounding application logic or +within fill_input_buffer(); the latter is probably more efficient. If +fill_input_buffer() knows that no more data is available, it can set the +pointer/count to point to a dummy EOI marker and then return TRUE just as +though it had read more data in a non-suspending situation. + +The decompressor does not attempt to suspend within standard JPEG markers; +instead it will backtrack to the start of the marker and reprocess the whole +marker next time. Hence the input buffer must be large enough to hold the +longest standard marker in the file. Standard JPEG markers should normally +not exceed a few hundred bytes each (DHT tables are typically the longest). +We recommend at least a 2K buffer for performance reasons, which is much +larger than any correct marker is likely to be. For robustness against +damaged marker length counts, you may wish to insert a test in your +application for the case that the input buffer is completely full and yet +the decoder has suspended without consuming any data --- otherwise, if this +situation did occur, it would lead to an endless loop. (The library can't +provide this test since it has no idea whether "the buffer is full", or +even whether there is a fixed-size input buffer.) + +The input buffer would need to be 64K to allow for arbitrary COM or APPn +markers, but these are handled specially: they are either saved into allocated +memory, or skipped over by calling skip_input_data(). In the former case, +suspension is handled correctly, and in the latter case, the problem of +buffer overrun is placed on skip_input_data's shoulders, as explained above. +Note that if you provide your own marker handling routine for large markers, +you should consider how to deal with buffer overflow. + +Multiple-buffer management: + +In some applications it is desirable to store the compressed data in a linked +list of buffer areas, so as to avoid data copying. This can be handled by +having empty_output_buffer() or fill_input_buffer() set the pointer and count +to reference the next available buffer; FALSE is returned only if no more +buffers are available. Although seemingly straightforward, there is a +pitfall in this approach: the backtrack that occurs when FALSE is returned +could back up into an earlier buffer. For example, when fill_input_buffer() +is called, the current pointer & count indicate the backtrack restart point. +Since fill_input_buffer() will set the pointer and count to refer to a new +buffer, the restart position must be saved somewhere else. Suppose a second +call to fill_input_buffer() occurs in the same library call, and no +additional input data is available, so fill_input_buffer must return FALSE. +If the JPEG library has not moved the pointer/count forward in the current +buffer, then *the correct restart point is the saved position in the prior +buffer*. Prior buffers may be discarded only after the library establishes +a restart point within a later buffer. Similar remarks apply for output into +a chain of buffers. + +The library will never attempt to backtrack over a skip_input_data() call, +so any skipped data can be permanently discarded. You still have to deal +with the case of skipping not-yet-received data, however. + +It's much simpler to use only a single buffer; when fill_input_buffer() is +called, move any unconsumed data (beyond the current pointer/count) down to +the beginning of this buffer and then load new data into the remaining buffer +space. This approach requires a little more data copying but is far easier +to get right. + + +Progressive JPEG support +------------------------ + +Progressive JPEG rearranges the stored data into a series of scans of +increasing quality. In situations where a JPEG file is transmitted across a +slow communications link, a decoder can generate a low-quality image very +quickly from the first scan, then gradually improve the displayed quality as +more scans are received. The final image after all scans are complete is +identical to that of a regular (sequential) JPEG file of the same quality +setting. Progressive JPEG files are often slightly smaller than equivalent +sequential JPEG files, but the possibility of incremental display is the main +reason for using progressive JPEG. + +The IJG encoder library generates progressive JPEG files when given a +suitable "scan script" defining how to divide the data into scans. +Creation of progressive JPEG files is otherwise transparent to the encoder. +Progressive JPEG files can also be read transparently by the decoder library. +If the decoding application simply uses the library as defined above, it +will receive a final decoded image without any indication that the file was +progressive. Of course, this approach does not allow incremental display. +To perform incremental display, an application needs to use the decoder +library's "buffered-image" mode, in which it receives a decoded image +multiple times. + +Each displayed scan requires about as much work to decode as a full JPEG +image of the same size, so the decoder must be fairly fast in relation to the +data transmission rate in order to make incremental display useful. However, +it is possible to skip displaying the image and simply add the incoming bits +to the decoder's coefficient buffer. This is fast because only Huffman +decoding need be done, not IDCT, upsampling, colorspace conversion, etc. +The IJG decoder library allows the application to switch dynamically between +displaying the image and simply absorbing the incoming bits. A properly +coded application can automatically adapt the number of display passes to +suit the time available as the image is received. Also, a final +higher-quality display cycle can be performed from the buffered data after +the end of the file is reached. + +Progressive compression: + +To create a progressive JPEG file (or a multiple-scan sequential JPEG file), +set the scan_info cinfo field to point to an array of scan descriptors, and +perform compression as usual. Instead of constructing your own scan list, +you can call the jpeg_simple_progression() helper routine to create a +recommended progression sequence; this method should be used by all +applications that don't want to get involved in the nitty-gritty of +progressive scan sequence design. (If you want to provide user control of +scan sequences, you may wish to borrow the scan script reading code found +in rdswitch.c, so that you can read scan script files just like cjpeg's.) +When scan_info is not NULL, the compression library will store DCT'd data +into a buffer array as jpeg_write_scanlines() is called, and will emit all +the requested scans during jpeg_finish_compress(). This implies that +multiple-scan output cannot be created with a suspending data destination +manager, since jpeg_finish_compress() does not support suspension. We +should also note that the compressor currently forces Huffman optimization +mode when creating a progressive JPEG file, because the default Huffman +tables are unsuitable for progressive files. + +Progressive decompression: + +When buffered-image mode is not used, the decoder library will read all of +a multi-scan file during jpeg_start_decompress(), so that it can provide a +final decoded image. (Here "multi-scan" means either progressive or +multi-scan sequential.) This makes multi-scan files transparent to the +decoding application. However, existing applications that used suspending +input with version 5 of the IJG library will need to be modified to check +for a suspension return from jpeg_start_decompress(). + +To perform incremental display, an application must use the library's +buffered-image mode. This is described in the next section. + + +Buffered-image mode +------------------- + +In buffered-image mode, the library stores the partially decoded image in a +coefficient buffer, from which it can be read out as many times as desired. +This mode is typically used for incremental display of progressive JPEG files, +but it can be used with any JPEG file. Each scan of a progressive JPEG file +adds more data (more detail) to the buffered image. The application can +display in lockstep with the source file (one display pass per input scan), +or it can allow input processing to outrun display processing. By making +input and display processing run independently, it is possible for the +application to adapt progressive display to a wide range of data transmission +rates. + +The basic control flow for buffered-image decoding is + + jpeg_create_decompress() + set data source + jpeg_read_header() + set overall decompression parameters + cinfo.buffered_image = TRUE; /* select buffered-image mode */ + jpeg_start_decompress() + for (each output pass) { + adjust output decompression parameters if required + jpeg_start_output() /* start a new output pass */ + for (all scanlines in image) { + jpeg_read_scanlines() + display scanlines + } + jpeg_finish_output() /* terminate output pass */ + } + jpeg_finish_decompress() + jpeg_destroy_decompress() + +This differs from ordinary unbuffered decoding in that there is an additional +level of looping. The application can choose how many output passes to make +and how to display each pass. + +The simplest approach to displaying progressive images is to do one display +pass for each scan appearing in the input file. In this case the outer loop +condition is typically + while (! jpeg_input_complete(&cinfo)) +and the start-output call should read + jpeg_start_output(&cinfo, cinfo.input_scan_number); +The second parameter to jpeg_start_output() indicates which scan of the input +file is to be displayed; the scans are numbered starting at 1 for this +purpose. (You can use a loop counter starting at 1 if you like, but using +the library's input scan counter is easier.) The library automatically reads +data as necessary to complete each requested scan, and jpeg_finish_output() +advances to the next scan or end-of-image marker (hence input_scan_number +will be incremented by the time control arrives back at jpeg_start_output()). +With this technique, data is read from the input file only as needed, and +input and output processing run in lockstep. + +After reading the final scan and reaching the end of the input file, the +buffered image remains available; it can be read additional times by +repeating the jpeg_start_output()/jpeg_read_scanlines()/jpeg_finish_output() +sequence. For example, a useful technique is to use fast one-pass color +quantization for display passes made while the image is arriving, followed by +a final display pass using two-pass quantization for highest quality. This +is done by changing the library parameters before the final output pass. +Changing parameters between passes is discussed in detail below. + +In general the last scan of a progressive file cannot be recognized as such +until after it is read, so a post-input display pass is the best approach if +you want special processing in the final pass. + +When done with the image, be sure to call jpeg_finish_decompress() to release +the buffered image (or just use jpeg_destroy_decompress()). + +If input data arrives faster than it can be displayed, the application can +cause the library to decode input data in advance of what's needed to produce +output. This is done by calling the routine jpeg_consume_input(). +The return value is one of the following: + JPEG_REACHED_SOS: reached an SOS marker (the start of a new scan) + JPEG_REACHED_EOI: reached the EOI marker (end of image) + JPEG_ROW_COMPLETED: completed reading one MCU row of compressed data + JPEG_SCAN_COMPLETED: completed reading last MCU row of current scan + JPEG_SUSPENDED: suspended before completing any of the above +(JPEG_SUSPENDED can occur only if a suspending data source is used.) This +routine can be called at any time after initializing the JPEG object. It +reads some additional data and returns when one of the indicated significant +events occurs. (If called after the EOI marker is reached, it will +immediately return JPEG_REACHED_EOI without attempting to read more data.) + +The library's output processing will automatically call jpeg_consume_input() +whenever the output processing overtakes the input; thus, simple lockstep +display requires no direct calls to jpeg_consume_input(). But by adding +calls to jpeg_consume_input(), you can absorb data in advance of what is +being displayed. This has two benefits: + * You can limit buildup of unprocessed data in your input buffer. + * You can eliminate extra display passes by paying attention to the + state of the library's input processing. + +The first of these benefits only requires interspersing calls to +jpeg_consume_input() with your display operations and any other processing +you may be doing. To avoid wasting cycles due to backtracking, it's best to +call jpeg_consume_input() only after a hundred or so new bytes have arrived. +This is discussed further under "I/O suspension", above. (Note: the JPEG +library currently is not thread-safe. You must not call jpeg_consume_input() +from one thread of control if a different library routine is working on the +same JPEG object in another thread.) + +When input arrives fast enough that more than one new scan is available +before you start a new output pass, you may as well skip the output pass +corresponding to the completed scan. This occurs for free if you pass +cinfo.input_scan_number as the target scan number to jpeg_start_output(). +The input_scan_number field is simply the index of the scan currently being +consumed by the input processor. You can ensure that this is up-to-date by +emptying the input buffer just before calling jpeg_start_output(): call +jpeg_consume_input() repeatedly until it returns JPEG_SUSPENDED or +JPEG_REACHED_EOI. + +The target scan number passed to jpeg_start_output() is saved in the +cinfo.output_scan_number field. The library's output processing calls +jpeg_consume_input() whenever the current input scan number and row within +that scan is less than or equal to the current output scan number and row. +Thus, input processing can "get ahead" of the output processing but is not +allowed to "fall behind". You can achieve several different effects by +manipulating this interlock rule. For example, if you pass a target scan +number greater than the current input scan number, the output processor will +wait until that scan starts to arrive before producing any output. (To avoid +an infinite loop, the target scan number is automatically reset to the last +scan number when the end of image is reached. Thus, if you specify a large +target scan number, the library will just absorb the entire input file and +then perform an output pass. This is effectively the same as what +jpeg_start_decompress() does when you don't select buffered-image mode.) +When you pass a target scan number equal to the current input scan number, +the image is displayed no faster than the current input scan arrives. The +final possibility is to pass a target scan number less than the current input +scan number; this disables the input/output interlock and causes the output +processor to simply display whatever it finds in the image buffer, without +waiting for input. (However, the library will not accept a target scan +number less than one, so you can't avoid waiting for the first scan.) + +When data is arriving faster than the output display processing can advance +through the image, jpeg_consume_input() will store data into the buffered +image beyond the point at which the output processing is reading data out +again. If the input arrives fast enough, it may "wrap around" the buffer to +the point where the input is more than one whole scan ahead of the output. +If the output processing simply proceeds through its display pass without +paying attention to the input, the effect seen on-screen is that the lower +part of the image is one or more scans better in quality than the upper part. +Then, when the next output scan is started, you have a choice of what target +scan number to use. The recommended choice is to use the current input scan +number at that time, which implies that you've skipped the output scans +corresponding to the input scans that were completed while you processed the +previous output scan. In this way, the decoder automatically adapts its +speed to the arriving data, by skipping output scans as necessary to keep up +with the arriving data. + +When using this strategy, you'll want to be sure that you perform a final +output pass after receiving all the data; otherwise your last display may not +be full quality across the whole screen. So the right outer loop logic is +something like this: + do { + absorb any waiting input by calling jpeg_consume_input() + final_pass = jpeg_input_complete(&cinfo); + adjust output decompression parameters if required + jpeg_start_output(&cinfo, cinfo.input_scan_number); + ... + jpeg_finish_output() + } while (! final_pass); +rather than quitting as soon as jpeg_input_complete() returns TRUE. This +arrangement makes it simple to use higher-quality decoding parameters +for the final pass. But if you don't want to use special parameters for +the final pass, the right loop logic is like this: + for (;;) { + absorb any waiting input by calling jpeg_consume_input() + jpeg_start_output(&cinfo, cinfo.input_scan_number); + ... + jpeg_finish_output() + if (jpeg_input_complete(&cinfo) && + cinfo.input_scan_number == cinfo.output_scan_number) + break; + } +In this case you don't need to know in advance whether an output pass is to +be the last one, so it's not necessary to have reached EOF before starting +the final output pass; rather, what you want to test is whether the output +pass was performed in sync with the final input scan. This form of the loop +will avoid an extra output pass whenever the decoder is able (or nearly able) +to keep up with the incoming data. + +When the data transmission speed is high, you might begin a display pass, +then find that much or all of the file has arrived before you can complete +the pass. (You can detect this by noting the JPEG_REACHED_EOI return code +from jpeg_consume_input(), or equivalently by testing jpeg_input_complete().) +In this situation you may wish to abort the current display pass and start a +new one using the newly arrived information. To do so, just call +jpeg_finish_output() and then start a new pass with jpeg_start_output(). + +A variant strategy is to abort and restart display if more than one complete +scan arrives during an output pass; this can be detected by noting +JPEG_REACHED_SOS returns and/or examining cinfo.input_scan_number. This +idea should be employed with caution, however, since the display process +might never get to the bottom of the image before being aborted, resulting +in the lower part of the screen being several passes worse than the upper. +In most cases it's probably best to abort an output pass only if the whole +file has arrived and you want to begin the final output pass immediately. + +When receiving data across a communication link, we recommend always using +the current input scan number for the output target scan number; if a +higher-quality final pass is to be done, it should be started (aborting any +incomplete output pass) as soon as the end of file is received. However, +many other strategies are possible. For example, the application can examine +the parameters of the current input scan and decide whether to display it or +not. If the scan contains only chroma data, one might choose not to use it +as the target scan, expecting that the scan will be small and will arrive +quickly. To skip to the next scan, call jpeg_consume_input() until it +returns JPEG_REACHED_SOS or JPEG_REACHED_EOI. Or just use the next higher +number as the target scan for jpeg_start_output(); but that method doesn't +let you inspect the next scan's parameters before deciding to display it. + + +In buffered-image mode, jpeg_start_decompress() never performs input and +thus never suspends. An application that uses input suspension with +buffered-image mode must be prepared for suspension returns from these +routines: +* jpeg_start_output() performs input only if you request 2-pass quantization + and the target scan isn't fully read yet. (This is discussed below.) +* jpeg_read_scanlines(), as always, returns the number of scanlines that it + was able to produce before suspending. +* jpeg_finish_output() will read any markers following the target scan, + up to the end of the file or the SOS marker that begins another scan. + (But it reads no input if jpeg_consume_input() has already reached the + end of the file or a SOS marker beyond the target output scan.) +* jpeg_finish_decompress() will read until the end of file, and thus can + suspend if the end hasn't already been reached (as can be tested by + calling jpeg_input_complete()). +jpeg_start_output(), jpeg_finish_output(), and jpeg_finish_decompress() +all return TRUE if they completed their tasks, FALSE if they had to suspend. +In the event of a FALSE return, the application must load more input data +and repeat the call. Applications that use non-suspending data sources need +not check the return values of these three routines. + + +It is possible to change decoding parameters between output passes in the +buffered-image mode. The decoder library currently supports only very +limited changes of parameters. ONLY THE FOLLOWING parameter changes are +allowed after jpeg_start_decompress() is called: +* dct_method can be changed before each call to jpeg_start_output(). + For example, one could use a fast DCT method for early scans, changing + to a higher quality method for the final scan. +* dither_mode can be changed before each call to jpeg_start_output(); + of course this has no impact if not using color quantization. Typically + one would use ordered dither for initial passes, then switch to + Floyd-Steinberg dither for the final pass. Caution: changing dither mode + can cause more memory to be allocated by the library. Although the amount + of memory involved is not large (a scanline or so), it may cause the + initial max_memory_to_use specification to be exceeded, which in the worst + case would result in an out-of-memory failure. +* do_block_smoothing can be changed before each call to jpeg_start_output(). + This setting is relevant only when decoding a progressive JPEG image. + During the first DC-only scan, block smoothing provides a very "fuzzy" look + instead of the very "blocky" look seen without it; which is better seems a + matter of personal taste. But block smoothing is nearly always a win + during later stages, especially when decoding a successive-approximation + image: smoothing helps to hide the slight blockiness that otherwise shows + up on smooth gradients until the lowest coefficient bits are sent. +* Color quantization mode can be changed under the rules described below. + You *cannot* change between full-color and quantized output (because that + would alter the required I/O buffer sizes), but you can change which + quantization method is used. + +When generating color-quantized output, changing quantization method is a +very useful way of switching between high-speed and high-quality display. +The library allows you to change among its three quantization methods: +1. Single-pass quantization to a fixed color cube. + Selected by cinfo.two_pass_quantize = FALSE and cinfo.colormap = NULL. +2. Single-pass quantization to an application-supplied colormap. + Selected by setting cinfo.colormap to point to the colormap (the value of + two_pass_quantize is ignored); also set cinfo.actual_number_of_colors. +3. Two-pass quantization to a colormap chosen specifically for the image. + Selected by cinfo.two_pass_quantize = TRUE and cinfo.colormap = NULL. + (This is the default setting selected by jpeg_read_header, but it is + probably NOT what you want for the first pass of progressive display!) +These methods offer successively better quality and lesser speed. However, +only the first method is available for quantizing in non-RGB color spaces. + +IMPORTANT: because the different quantizer methods have very different +working-storage requirements, the library requires you to indicate which +one(s) you intend to use before you call jpeg_start_decompress(). (If we did +not require this, the max_memory_to_use setting would be a complete fiction.) +You do this by setting one or more of these three cinfo fields to TRUE: + enable_1pass_quant Fixed color cube colormap + enable_external_quant Externally-supplied colormap + enable_2pass_quant Two-pass custom colormap +All three are initialized FALSE by jpeg_read_header(). But +jpeg_start_decompress() automatically sets TRUE the one selected by the +current two_pass_quantize and colormap settings, so you only need to set the +enable flags for any other quantization methods you plan to change to later. + +After setting the enable flags correctly at jpeg_start_decompress() time, you +can change to any enabled quantization method by setting two_pass_quantize +and colormap properly just before calling jpeg_start_output(). The following +special rules apply: +1. You must explicitly set cinfo.colormap to NULL when switching to 1-pass + or 2-pass mode from a different mode, or when you want the 2-pass + quantizer to be re-run to generate a new colormap. +2. To switch to an external colormap, or to change to a different external + colormap than was used on the prior pass, you must call + jpeg_new_colormap() after setting cinfo.colormap. +NOTE: if you want to use the same colormap as was used in the prior pass, +you should not do either of these things. This will save some nontrivial +switchover costs. +(These requirements exist because cinfo.colormap will always be non-NULL +after completing a prior output pass, since both the 1-pass and 2-pass +quantizers set it to point to their output colormaps. Thus you have to +do one of these two things to notify the library that something has changed. +Yup, it's a bit klugy, but it's necessary to do it this way for backwards +compatibility.) + +Note that in buffered-image mode, the library generates any requested colormap +during jpeg_start_output(), not during jpeg_start_decompress(). + +When using two-pass quantization, jpeg_start_output() makes a pass over the +buffered image to determine the optimum color map; it therefore may take a +significant amount of time, whereas ordinarily it does little work. The +progress monitor hook is called during this pass, if defined. It is also +important to realize that if the specified target scan number is greater than +or equal to the current input scan number, jpeg_start_output() will attempt +to consume input as it makes this pass. If you use a suspending data source, +you need to check for a FALSE return from jpeg_start_output() under these +conditions. The combination of 2-pass quantization and a not-yet-fully-read +target scan is the only case in which jpeg_start_output() will consume input. + + +Application authors who support buffered-image mode may be tempted to use it +for all JPEG images, even single-scan ones. This will work, but it is +inefficient: there is no need to create an image-sized coefficient buffer for +single-scan images. Requesting buffered-image mode for such an image wastes +memory. Worse, it can cost time on large images, since the buffered data has +to be swapped out or written to a temporary file. If you are concerned about +maximum performance on baseline JPEG files, you should use buffered-image +mode only when the incoming file actually has multiple scans. This can be +tested by calling jpeg_has_multiple_scans(), which will return a correct +result at any time after jpeg_read_header() completes. + +It is also worth noting that when you use jpeg_consume_input() to let input +processing get ahead of output processing, the resulting pattern of access to +the coefficient buffer is quite nonsequential. It's best to use the memory +manager jmemnobs.c if you can (ie, if you have enough real or virtual main +memory). If not, at least make sure that max_memory_to_use is set as high as +possible. If the JPEG memory manager has to use a temporary file, you will +probably see a lot of disk traffic and poor performance. (This could be +improved with additional work on the memory manager, but we haven't gotten +around to it yet.) + +In some applications it may be convenient to use jpeg_consume_input() for all +input processing, including reading the initial markers; that is, you may +wish to call jpeg_consume_input() instead of jpeg_read_header() during +startup. This works, but note that you must check for JPEG_REACHED_SOS and +JPEG_REACHED_EOI return codes as the equivalent of jpeg_read_header's codes. +Once the first SOS marker has been reached, you must call +jpeg_start_decompress() before jpeg_consume_input() will consume more input; +it'll just keep returning JPEG_REACHED_SOS until you do. If you read a +tables-only file this way, jpeg_consume_input() will return JPEG_REACHED_EOI +without ever returning JPEG_REACHED_SOS; be sure to check for this case. +If this happens, the decompressor will not read any more input until you call +jpeg_abort() to reset it. It is OK to call jpeg_consume_input() even when not +using buffered-image mode, but in that case it's basically a no-op after the +initial markers have been read: it will just return JPEG_SUSPENDED. + + +Abbreviated datastreams and multiple images +------------------------------------------- + +A JPEG compression or decompression object can be reused to process multiple +images. This saves a small amount of time per image by eliminating the +"create" and "destroy" operations, but that isn't the real purpose of the +feature. Rather, reuse of an object provides support for abbreviated JPEG +datastreams. Object reuse can also simplify processing a series of images in +a single input or output file. This section explains these features. + +A JPEG file normally contains several hundred bytes worth of quantization +and Huffman tables. In a situation where many images will be stored or +transmitted with identical tables, this may represent an annoying overhead. +The JPEG standard therefore permits tables to be omitted. The standard +defines three classes of JPEG datastreams: + * "Interchange" datastreams contain an image and all tables needed to decode + the image. These are the usual kind of JPEG file. + * "Abbreviated image" datastreams contain an image, but are missing some or + all of the tables needed to decode that image. + * "Abbreviated table specification" (henceforth "tables-only") datastreams + contain only table specifications. +To decode an abbreviated image, it is necessary to load the missing table(s) +into the decoder beforehand. This can be accomplished by reading a separate +tables-only file. A variant scheme uses a series of images in which the first +image is an interchange (complete) datastream, while subsequent ones are +abbreviated and rely on the tables loaded by the first image. It is assumed +that once the decoder has read a table, it will remember that table until a +new definition for the same table number is encountered. + +It is the application designer's responsibility to figure out how to associate +the correct tables with an abbreviated image. While abbreviated datastreams +can be useful in a closed environment, their use is strongly discouraged in +any situation where data exchange with other applications might be needed. +Caveat designer. + +The JPEG library provides support for reading and writing any combination of +tables-only datastreams and abbreviated images. In both compression and +decompression objects, a quantization or Huffman table will be retained for +the lifetime of the object, unless it is overwritten by a new table definition. + + +To create abbreviated image datastreams, it is only necessary to tell the +compressor not to emit some or all of the tables it is using. Each +quantization and Huffman table struct contains a boolean field "sent_table", +which normally is initialized to FALSE. For each table used by the image, the +header-writing process emits the table and sets sent_table = TRUE unless it is +already TRUE. (In normal usage, this prevents outputting the same table +definition multiple times, as would otherwise occur because the chroma +components typically share tables.) Thus, setting this field to TRUE before +calling jpeg_start_compress() will prevent the table from being written at +all. + +If you want to create a "pure" abbreviated image file containing no tables, +just call "jpeg_suppress_tables(&cinfo, TRUE)" after constructing all the +tables. If you want to emit some but not all tables, you'll need to set the +individual sent_table fields directly. + +To create an abbreviated image, you must also call jpeg_start_compress() +with a second parameter of FALSE, not TRUE. Otherwise jpeg_start_compress() +will force all the sent_table fields to FALSE. (This is a safety feature to +prevent abbreviated images from being created accidentally.) + +To create a tables-only file, perform the same parameter setup that you +normally would, but instead of calling jpeg_start_compress() and so on, call +jpeg_write_tables(&cinfo). This will write an abbreviated datastream +containing only SOI, DQT and/or DHT markers, and EOI. All the quantization +and Huffman tables that are currently defined in the compression object will +be emitted unless their sent_tables flag is already TRUE, and then all the +sent_tables flags will be set TRUE. + +A sure-fire way to create matching tables-only and abbreviated image files +is to proceed as follows: + + create JPEG compression object + set JPEG parameters + set destination to tables-only file + jpeg_write_tables(&cinfo); + set destination to image file + jpeg_start_compress(&cinfo, FALSE); + write data... + jpeg_finish_compress(&cinfo); + +Since the JPEG parameters are not altered between writing the table file and +the abbreviated image file, the same tables are sure to be used. Of course, +you can repeat the jpeg_start_compress() ... jpeg_finish_compress() sequence +many times to produce many abbreviated image files matching the table file. + +You cannot suppress output of the computed Huffman tables when Huffman +optimization is selected. (If you could, there'd be no way to decode the +image...) Generally, you don't want to set optimize_coding = TRUE when +you are trying to produce abbreviated files. + +In some cases you might want to compress an image using tables which are +not stored in the application, but are defined in an interchange or +tables-only file readable by the application. This can be done by setting up +a JPEG decompression object to read the specification file, then copying the +tables into your compression object. See jpeg_copy_critical_parameters() +for an example of copying quantization tables. + + +To read abbreviated image files, you simply need to load the proper tables +into the decompression object before trying to read the abbreviated image. +If the proper tables are stored in the application program, you can just +allocate the table structs and fill in their contents directly. For example, +to load a fixed quantization table into table slot "n": + + if (cinfo.quant_tbl_ptrs[n] == NULL) + cinfo.quant_tbl_ptrs[n] = jpeg_alloc_quant_table((j_common_ptr) &cinfo); + quant_ptr = cinfo.quant_tbl_ptrs[n]; /* quant_ptr is JQUANT_TBL* */ + for (i = 0; i < 64; i++) { + /* Qtable[] is desired quantization table, in natural array order */ + quant_ptr->quantval[i] = Qtable[i]; + } + +Code to load a fixed Huffman table is typically (for AC table "n"): + + if (cinfo.ac_huff_tbl_ptrs[n] == NULL) + cinfo.ac_huff_tbl_ptrs[n] = jpeg_alloc_huff_table((j_common_ptr) &cinfo); + huff_ptr = cinfo.ac_huff_tbl_ptrs[n]; /* huff_ptr is JHUFF_TBL* */ + for (i = 1; i <= 16; i++) { + /* counts[i] is number of Huffman codes of length i bits, i=1..16 */ + huff_ptr->bits[i] = counts[i]; + } + for (i = 0; i < 256; i++) { + /* symbols[] is the list of Huffman symbols, in code-length order */ + huff_ptr->huffval[i] = symbols[i]; + } + +(Note that trying to set cinfo.quant_tbl_ptrs[n] to point directly at a +constant JQUANT_TBL object is not safe. If the incoming file happened to +contain a quantization table definition, your master table would get +overwritten! Instead allocate a working table copy and copy the master table +into it, as illustrated above. Ditto for Huffman tables, of course.) + +You might want to read the tables from a tables-only file, rather than +hard-wiring them into your application. The jpeg_read_header() call is +sufficient to read a tables-only file. You must pass a second parameter of +FALSE to indicate that you do not require an image to be present. Thus, the +typical scenario is + + create JPEG decompression object + set source to tables-only file + jpeg_read_header(&cinfo, FALSE); + set source to abbreviated image file + jpeg_read_header(&cinfo, TRUE); + set decompression parameters + jpeg_start_decompress(&cinfo); + read data... + jpeg_finish_decompress(&cinfo); + +In some cases, you may want to read a file without knowing whether it contains +an image or just tables. In that case, pass FALSE and check the return value +from jpeg_read_header(): it will be JPEG_HEADER_OK if an image was found, +JPEG_HEADER_TABLES_ONLY if only tables were found. (A third return value, +JPEG_SUSPENDED, is possible when using a suspending data source manager.) +Note that jpeg_read_header() will not complain if you read an abbreviated +image for which you haven't loaded the missing tables; the missing-table check +occurs later, in jpeg_start_decompress(). + + +It is possible to read a series of images from a single source file by +repeating the jpeg_read_header() ... jpeg_finish_decompress() sequence, +without releasing/recreating the JPEG object or the data source module. +(If you did reinitialize, any partial bufferload left in the data source +buffer at the end of one image would be discarded, causing you to lose the +start of the next image.) When you use this method, stored tables are +automatically carried forward, so some of the images can be abbreviated images +that depend on tables from earlier images. + +If you intend to write a series of images into a single destination file, +you might want to make a specialized data destination module that doesn't +flush the output buffer at term_destination() time. This would speed things +up by some trifling amount. Of course, you'd need to remember to flush the +buffer after the last image. You can make the later images be abbreviated +ones by passing FALSE to jpeg_start_compress(). + + +Special markers +--------------- + +Some applications may need to insert or extract special data in the JPEG +datastream. The JPEG standard provides marker types "COM" (comment) and +"APP0" through "APP15" (application) to hold application-specific data. +Unfortunately, the use of these markers is not specified by the standard. +COM markers are fairly widely used to hold user-supplied text. The JFIF file +format spec uses APP0 markers with specified initial strings to hold certain +data. Adobe applications use APP14 markers beginning with the string "Adobe" +for miscellaneous data. Other APPn markers are rarely seen, but might +contain almost anything. + +If you wish to store user-supplied text, we recommend you use COM markers +and place readable 7-bit ASCII text in them. Newline conventions are not +standardized --- expect to find LF (Unix style), CR/LF (DOS style), or CR +(Mac style). A robust COM reader should be able to cope with random binary +garbage, including nulls, since some applications generate COM markers +containing non-ASCII junk. (But yours should not be one of them.) + +For program-supplied data, use an APPn marker, and be sure to begin it with an +identifying string so that you can tell whether the marker is actually yours. +It's probably best to avoid using APP0 or APP14 for any private markers. +(NOTE: the upcoming SPIFF standard will use APP8 markers; we recommend you +not use APP8 markers for any private purposes, either.) + +Keep in mind that at most 65533 bytes can be put into one marker, but you +can have as many markers as you like. + +By default, the IJG compression library will write a JFIF APP0 marker if the +selected JPEG colorspace is grayscale or YCbCr, or an Adobe APP14 marker if +the selected colorspace is RGB, CMYK, or YCCK. You can disable this, but +we don't recommend it. The decompression library will recognize JFIF and +Adobe markers and will set the JPEG colorspace properly when one is found. + + +You can write special markers immediately following the datastream header by +calling jpeg_write_marker() after jpeg_start_compress() and before the first +call to jpeg_write_scanlines(). When you do this, the markers appear after +the SOI and the JFIF APP0 and Adobe APP14 markers (if written), but before +all else. Specify the marker type parameter as "JPEG_COM" for COM or +"JPEG_APP0 + n" for APPn. (Actually, jpeg_write_marker will let you write +any marker type, but we don't recommend writing any other kinds of marker.) +For example, to write a user comment string pointed to by comment_text: + jpeg_write_marker(cinfo, JPEG_COM, comment_text, strlen(comment_text)); + +If it's not convenient to store all the marker data in memory at once, +you can instead call jpeg_write_m_header() followed by multiple calls to +jpeg_write_m_byte(). If you do it this way, it's your responsibility to +call jpeg_write_m_byte() exactly the number of times given in the length +parameter to jpeg_write_m_header(). (This method lets you empty the +output buffer partway through a marker, which might be important when +using a suspending data destination module. In any case, if you are using +a suspending destination, you should flush its buffer after inserting +any special markers. See "I/O suspension".) + +Or, if you prefer to synthesize the marker byte sequence yourself, +you can just cram it straight into the data destination module. + +If you are writing JFIF 1.02 extension markers (thumbnail images), don't +forget to set cinfo.JFIF_minor_version = 2 so that the encoder will write the +correct JFIF version number in the JFIF header marker. The library's default +is to write version 1.01, but that's wrong if you insert any 1.02 extension +markers. (We could probably get away with just defaulting to 1.02, but there +used to be broken decoders that would complain about unknown minor version +numbers. To reduce compatibility risks it's safest not to write 1.02 unless +you are actually using 1.02 extensions.) + + +When reading, two methods of handling special markers are available: +1. You can ask the library to save the contents of COM and/or APPn markers +into memory, and then examine them at your leisure afterwards. +2. You can supply your own routine to process COM and/or APPn markers +on-the-fly as they are read. +The first method is simpler to use, especially if you are using a suspending +data source; writing a marker processor that copes with input suspension is +not easy (consider what happens if the marker is longer than your available +input buffer). However, the second method conserves memory since the marker +data need not be kept around after it's been processed. + +For either method, you'd normally set up marker handling after creating a +decompression object and before calling jpeg_read_header(), because the +markers of interest will typically be near the head of the file and so will +be scanned by jpeg_read_header. Once you've established a marker handling +method, it will be used for the life of that decompression object +(potentially many datastreams), unless you change it. Marker handling is +determined separately for COM markers and for each APPn marker code. + + +To save the contents of special markers in memory, call + jpeg_save_markers(cinfo, marker_code, length_limit) +where marker_code is the marker type to save, JPEG_COM or JPEG_APP0+n. +(To arrange to save all the special marker types, you need to call this +routine 17 times, for COM and APP0-APP15.) If the incoming marker is longer +than length_limit data bytes, only length_limit bytes will be saved; this +parameter allows you to avoid chewing up memory when you only need to see the +first few bytes of a potentially large marker. If you want to save all the +data, set length_limit to 0xFFFF; that is enough since marker lengths are only +16 bits. As a special case, setting length_limit to 0 prevents that marker +type from being saved at all. (That is the default behavior, in fact.) + +After jpeg_read_header() completes, you can examine the special markers by +following the cinfo->marker_list pointer chain. All the special markers in +the file appear in this list, in order of their occurrence in the file (but +omitting any markers of types you didn't ask for). Both the original data +length and the saved data length are recorded for each list entry; the latter +will not exceed length_limit for the particular marker type. Note that these +lengths exclude the marker length word, whereas the stored representation +within the JPEG file includes it. (Hence the maximum data length is really +only 65533.) + +It is possible that additional special markers appear in the file beyond the +SOS marker at which jpeg_read_header stops; if so, the marker list will be +extended during reading of the rest of the file. This is not expected to be +common, however. If you are short on memory you may want to reset the length +limit to zero for all marker types after finishing jpeg_read_header, to +ensure that the max_memory_to_use setting cannot be exceeded due to addition +of later markers. + +The marker list remains stored until you call jpeg_finish_decompress or +jpeg_abort, at which point the memory is freed and the list is set to empty. +(jpeg_destroy also releases the storage, of course.) + +Note that the library is internally interested in APP0 and APP14 markers; +if you try to set a small nonzero length limit on these types, the library +will silently force the length up to the minimum it wants. (But you can set +a zero length limit to prevent them from being saved at all.) Also, in a +16-bit environment, the maximum length limit may be constrained to less than +65533 by malloc() limitations. It is therefore best not to assume that the +effective length limit is exactly what you set it to be. + + +If you want to supply your own marker-reading routine, you do it by calling +jpeg_set_marker_processor(). A marker processor routine must have the +signature + boolean jpeg_marker_parser_method (j_decompress_ptr cinfo) +Although the marker code is not explicitly passed, the routine can find it +in cinfo->unread_marker. At the time of call, the marker proper has been +read from the data source module. The processor routine is responsible for +reading the marker length word and the remaining parameter bytes, if any. +Return TRUE to indicate success. (FALSE should be returned only if you are +using a suspending data source and it tells you to suspend. See the standard +marker processors in jdmarker.c for appropriate coding methods if you need to +use a suspending data source.) + +If you override the default APP0 or APP14 processors, it is up to you to +recognize JFIF and Adobe markers if you want colorspace recognition to occur +properly. We recommend copying and extending the default processors if you +want to do that. (A better idea is to save these marker types for later +examination by calling jpeg_save_markers(); that method doesn't interfere +with the library's own processing of these markers.) + +jpeg_set_marker_processor() and jpeg_save_markers() are mutually exclusive +--- if you call one it overrides any previous call to the other, for the +particular marker type specified. + +A simple example of an external COM processor can be found in djpeg.c. +Also, see jpegtran.c for an example of using jpeg_save_markers. + + +Raw (downsampled) image data +---------------------------- + +Some applications need to supply already-downsampled image data to the JPEG +compressor, or to receive raw downsampled data from the decompressor. The +library supports this requirement by allowing the application to write or +read raw data, bypassing the normal preprocessing or postprocessing steps. +The interface is different from the standard one and is somewhat harder to +use. If your interest is merely in bypassing color conversion, we recommend +that you use the standard interface and simply set jpeg_color_space = +in_color_space (or jpeg_color_space = out_color_space for decompression). +The mechanism described in this section is necessary only to supply or +receive downsampled image data, in which not all components have the same +dimensions. + + +To compress raw data, you must supply the data in the colorspace to be used +in the JPEG file (please read the earlier section on Special color spaces) +and downsampled to the sampling factors specified in the JPEG parameters. +You must supply the data in the format used internally by the JPEG library, +namely a JSAMPIMAGE array. This is an array of pointers to two-dimensional +arrays, each of type JSAMPARRAY. Each 2-D array holds the values for one +color component. This structure is necessary since the components are of +different sizes. If the image dimensions are not a multiple of the MCU size, +you must also pad the data correctly (usually, this is done by replicating +the last column and/or row). The data must be padded to a multiple of a DCT +block in each component: that is, each downsampled row must contain a +multiple of 8 valid samples, and there must be a multiple of 8 sample rows +for each component. (For applications such as conversion of digital TV +images, the standard image size is usually a multiple of the DCT block size, +so that no padding need actually be done.) + +The procedure for compression of raw data is basically the same as normal +compression, except that you call jpeg_write_raw_data() in place of +jpeg_write_scanlines(). Before calling jpeg_start_compress(), you must do +the following: + * Set cinfo->raw_data_in to TRUE. (It is set FALSE by jpeg_set_defaults().) + This notifies the library that you will be supplying raw data. + * Ensure jpeg_color_space is correct --- an explicit jpeg_set_colorspace() + call is a good idea. Note that since color conversion is bypassed, + in_color_space is ignored, except that jpeg_set_defaults() uses it to + choose the default jpeg_color_space setting. + * Ensure the sampling factors, cinfo->comp_info[i].h_samp_factor and + cinfo->comp_info[i].v_samp_factor, are correct. Since these indicate the + dimensions of the data you are supplying, it's wise to set them + explicitly, rather than assuming the library's defaults are what you want. + +To pass raw data to the library, call jpeg_write_raw_data() in place of +jpeg_write_scanlines(). The two routines work similarly except that +jpeg_write_raw_data takes a JSAMPIMAGE data array rather than JSAMPARRAY. +The scanlines count passed to and returned from jpeg_write_raw_data is +measured in terms of the component with the largest v_samp_factor. + +jpeg_write_raw_data() processes one MCU row per call, which is to say +v_samp_factor*DCTSIZE sample rows of each component. The passed num_lines +value must be at least max_v_samp_factor*DCTSIZE, and the return value will +be exactly that amount (or possibly some multiple of that amount, in future +library versions). This is true even on the last call at the bottom of the +image; don't forget to pad your data as necessary. + +The required dimensions of the supplied data can be computed for each +component as + cinfo->comp_info[i].width_in_blocks*DCTSIZE samples per row + cinfo->comp_info[i].height_in_blocks*DCTSIZE rows in image +after jpeg_start_compress() has initialized those fields. If the valid data +is smaller than this, it must be padded appropriately. For some sampling +factors and image sizes, additional dummy DCT blocks are inserted to make +the image a multiple of the MCU dimensions. The library creates such dummy +blocks itself; it does not read them from your supplied data. Therefore you +need never pad by more than DCTSIZE samples. An example may help here. +Assume 2h2v downsampling of YCbCr data, that is + cinfo->comp_info[0].h_samp_factor = 2 for Y + cinfo->comp_info[0].v_samp_factor = 2 + cinfo->comp_info[1].h_samp_factor = 1 for Cb + cinfo->comp_info[1].v_samp_factor = 1 + cinfo->comp_info[2].h_samp_factor = 1 for Cr + cinfo->comp_info[2].v_samp_factor = 1 +and suppose that the nominal image dimensions (cinfo->image_width and +cinfo->image_height) are 101x101 pixels. Then jpeg_start_compress() will +compute downsampled_width = 101 and width_in_blocks = 13 for Y, +downsampled_width = 51 and width_in_blocks = 7 for Cb and Cr (and the same +for the height fields). You must pad the Y data to at least 13*8 = 104 +columns and rows, the Cb/Cr data to at least 7*8 = 56 columns and rows. The +MCU height is max_v_samp_factor = 2 DCT rows so you must pass at least 16 +scanlines on each call to jpeg_write_raw_data(), which is to say 16 actual +sample rows of Y and 8 each of Cb and Cr. A total of 7 MCU rows are needed, +so you must pass a total of 7*16 = 112 "scanlines". The last DCT block row +of Y data is dummy, so it doesn't matter what you pass for it in the data +arrays, but the scanlines count must total up to 112 so that all of the Cb +and Cr data gets passed. + +Output suspension is supported with raw-data compression: if the data +destination module suspends, jpeg_write_raw_data() will return 0. +In this case the same data rows must be passed again on the next call. + + +Decompression with raw data output implies bypassing all postprocessing: +you cannot ask for rescaling or color quantization, for instance. More +seriously, you must deal with the color space and sampling factors present in +the incoming file. If your application only handles, say, 2h1v YCbCr data, +you must check for and fail on other color spaces or other sampling factors. +The library will not convert to a different color space for you. + +To obtain raw data output, set cinfo->raw_data_out = TRUE before +jpeg_start_decompress() (it is set FALSE by jpeg_read_header()). Be sure to +verify that the color space and sampling factors are ones you can handle. +Then call jpeg_read_raw_data() in place of jpeg_read_scanlines(). The +decompression process is otherwise the same as usual. + +jpeg_read_raw_data() returns one MCU row per call, and thus you must pass a +buffer of at least max_v_samp_factor*DCTSIZE scanlines (scanline counting is +the same as for raw-data compression). The buffer you pass must be large +enough to hold the actual data plus padding to DCT-block boundaries. As with +compression, any entirely dummy DCT blocks are not processed so you need not +allocate space for them, but the total scanline count includes them. The +above example of computing buffer dimensions for raw-data compression is +equally valid for decompression. + +Input suspension is supported with raw-data decompression: if the data source +module suspends, jpeg_read_raw_data() will return 0. You can also use +buffered-image mode to read raw data in multiple passes. + + +Really raw data: DCT coefficients +--------------------------------- + +It is possible to read or write the contents of a JPEG file as raw DCT +coefficients. This facility is mainly intended for use in lossless +transcoding between different JPEG file formats. Other possible applications +include lossless cropping of a JPEG image, lossless reassembly of a +multi-strip or multi-tile TIFF/JPEG file into a single JPEG datastream, etc. + +To read the contents of a JPEG file as DCT coefficients, open the file and do +jpeg_read_header() as usual. But instead of calling jpeg_start_decompress() +and jpeg_read_scanlines(), call jpeg_read_coefficients(). This will read the +entire image into a set of virtual coefficient-block arrays, one array per +component. The return value is a pointer to an array of virtual-array +descriptors. Each virtual array can be accessed directly using the JPEG +memory manager's access_virt_barray method (see Memory management, below, +and also read structure.doc's discussion of virtual array handling). Or, +for simple transcoding to a different JPEG file format, the array list can +just be handed directly to jpeg_write_coefficients(). + +Each block in the block arrays contains quantized coefficient values in +normal array order (not JPEG zigzag order). The block arrays contain only +DCT blocks containing real data; any entirely-dummy blocks added to fill out +interleaved MCUs at the right or bottom edges of the image are discarded +during reading and are not stored in the block arrays. (The size of each +block array can be determined from the width_in_blocks and height_in_blocks +fields of the component's comp_info entry.) This is also the data format +expected by jpeg_write_coefficients(). + +When you are done using the virtual arrays, call jpeg_finish_decompress() +to release the array storage and return the decompression object to an idle +state; or just call jpeg_destroy() if you don't need to reuse the object. + +If you use a suspending data source, jpeg_read_coefficients() will return +NULL if it is forced to suspend; a non-NULL return value indicates successful +completion. You need not test for a NULL return value when using a +non-suspending data source. + +It is also possible to call jpeg_read_coefficients() to obtain access to the +decoder's coefficient arrays during a normal decode cycle in buffered-image +mode. This frammish might be useful for progressively displaying an incoming +image and then re-encoding it without loss. To do this, decode in buffered- +image mode as discussed previously, then call jpeg_read_coefficients() after +the last jpeg_finish_output() call. The arrays will be available for your use +until you call jpeg_finish_decompress(). + + +To write the contents of a JPEG file as DCT coefficients, you must provide +the DCT coefficients stored in virtual block arrays. You can either pass +block arrays read from an input JPEG file by jpeg_read_coefficients(), or +allocate virtual arrays from the JPEG compression object and fill them +yourself. In either case, jpeg_write_coefficients() is substituted for +jpeg_start_compress() and jpeg_write_scanlines(). Thus the sequence is + * Create compression object + * Set all compression parameters as necessary + * Request virtual arrays if needed + * jpeg_write_coefficients() + * jpeg_finish_compress() + * Destroy or re-use compression object +jpeg_write_coefficients() is passed a pointer to an array of virtual block +array descriptors; the number of arrays is equal to cinfo.num_components. + +The virtual arrays need only have been requested, not realized, before +jpeg_write_coefficients() is called. A side-effect of +jpeg_write_coefficients() is to realize any virtual arrays that have been +requested from the compression object's memory manager. Thus, when obtaining +the virtual arrays from the compression object, you should fill the arrays +after calling jpeg_write_coefficients(). The data is actually written out +when you call jpeg_finish_compress(); jpeg_write_coefficients() only writes +the file header. + +When writing raw DCT coefficients, it is crucial that the JPEG quantization +tables and sampling factors match the way the data was encoded, or the +resulting file will be invalid. For transcoding from an existing JPEG file, +we recommend using jpeg_copy_critical_parameters(). This routine initializes +all the compression parameters to default values (like jpeg_set_defaults()), +then copies the critical information from a source decompression object. +The decompression object should have just been used to read the entire +JPEG input file --- that is, it should be awaiting jpeg_finish_decompress(). + +jpeg_write_coefficients() marks all tables stored in the compression object +as needing to be written to the output file (thus, it acts like +jpeg_start_compress(cinfo, TRUE)). This is for safety's sake, to avoid +emitting abbreviated JPEG files by accident. If you really want to emit an +abbreviated JPEG file, call jpeg_suppress_tables(), or set the tables' +individual sent_table flags, between calling jpeg_write_coefficients() and +jpeg_finish_compress(). + + +Progress monitoring +------------------- + +Some applications may need to regain control from the JPEG library every so +often. The typical use of this feature is to produce a percent-done bar or +other progress display. (For a simple example, see cjpeg.c or djpeg.c.) +Although you do get control back frequently during the data-transferring pass +(the jpeg_read_scanlines or jpeg_write_scanlines loop), any additional passes +will occur inside jpeg_finish_compress or jpeg_start_decompress; those +routines may take a long time to execute, and you don't get control back +until they are done. + +You can define a progress-monitor routine which will be called periodically +by the library. No guarantees are made about how often this call will occur, +so we don't recommend you use it for mouse tracking or anything like that. +At present, a call will occur once per MCU row, scanline, or sample row +group, whichever unit is convenient for the current processing mode; so the +wider the image, the longer the time between calls. During the data +transferring pass, only one call occurs per call of jpeg_read_scanlines or +jpeg_write_scanlines, so don't pass a large number of scanlines at once if +you want fine resolution in the progress count. (If you really need to use +the callback mechanism for time-critical tasks like mouse tracking, you could +insert additional calls inside some of the library's inner loops.) + +To establish a progress-monitor callback, create a struct jpeg_progress_mgr, +fill in its progress_monitor field with a pointer to your callback routine, +and set cinfo->progress to point to the struct. The callback will be called +whenever cinfo->progress is non-NULL. (This pointer is set to NULL by +jpeg_create_compress or jpeg_create_decompress; the library will not change +it thereafter. So if you allocate dynamic storage for the progress struct, +make sure it will live as long as the JPEG object does. Allocating from the +JPEG memory manager with lifetime JPOOL_PERMANENT will work nicely.) You +can use the same callback routine for both compression and decompression. + +The jpeg_progress_mgr struct contains four fields which are set by the library: + long pass_counter; /* work units completed in this pass */ + long pass_limit; /* total number of work units in this pass */ + int completed_passes; /* passes completed so far */ + int total_passes; /* total number of passes expected */ +During any one pass, pass_counter increases from 0 up to (not including) +pass_limit; the step size is usually but not necessarily 1. The pass_limit +value may change from one pass to another. The expected total number of +passes is in total_passes, and the number of passes already completed is in +completed_passes. Thus the fraction of work completed may be estimated as + completed_passes + (pass_counter/pass_limit) + -------------------------------------------- + total_passes +ignoring the fact that the passes may not be equal amounts of work. + +When decompressing, pass_limit can even change within a pass, because it +depends on the number of scans in the JPEG file, which isn't always known in +advance. The computed fraction-of-work-done may jump suddenly (if the library +discovers it has overestimated the number of scans) or even decrease (in the +opposite case). It is not wise to put great faith in the work estimate. + +When using the decompressor's buffered-image mode, the progress monitor work +estimate is likely to be completely unhelpful, because the library has no way +to know how many output passes will be demanded of it. Currently, the library +sets total_passes based on the assumption that there will be one more output +pass if the input file end hasn't yet been read (jpeg_input_complete() isn't +TRUE), but no more output passes if the file end has been reached when the +output pass is started. This means that total_passes will rise as additional +output passes are requested. If you have a way of determining the input file +size, estimating progress based on the fraction of the file that's been read +will probably be more useful than using the library's value. + + +Memory management +----------------- + +This section covers some key facts about the JPEG library's built-in memory +manager. For more info, please read structure.doc's section about the memory +manager, and consult the source code if necessary. + +All memory and temporary file allocation within the library is done via the +memory manager. If necessary, you can replace the "back end" of the memory +manager to control allocation yourself (for example, if you don't want the +library to use malloc() and free() for some reason). + +Some data is allocated "permanently" and will not be freed until the JPEG +object is destroyed. Most data is allocated "per image" and is freed by +jpeg_finish_compress, jpeg_finish_decompress, or jpeg_abort. You can call the +memory manager yourself to allocate structures that will automatically be +freed at these times. Typical code for this is + ptr = (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, size); +Use JPOOL_PERMANENT to get storage that lasts as long as the JPEG object. +Use alloc_large instead of alloc_small for anything bigger than a few Kbytes. +There are also alloc_sarray and alloc_barray routines that automatically +build 2-D sample or block arrays. + +The library's minimum space requirements to process an image depend on the +image's width, but not on its height, because the library ordinarily works +with "strip" buffers that are as wide as the image but just a few rows high. +Some operating modes (eg, two-pass color quantization) require full-image +buffers. Such buffers are treated as "virtual arrays": only the current strip +need be in memory, and the rest can be swapped out to a temporary file. + +If you use the simplest memory manager back end (jmemnobs.c), then no +temporary files are used; virtual arrays are simply malloc()'d. Images bigger +than memory can be processed only if your system supports virtual memory. +The other memory manager back ends support temporary files of various flavors +and thus work in machines without virtual memory. They may also be useful on +Unix machines if you need to process images that exceed available swap space. + +When using temporary files, the library will make the in-memory buffers for +its virtual arrays just big enough to stay within a "maximum memory" setting. +Your application can set this limit by setting cinfo->mem->max_memory_to_use +after creating the JPEG object. (Of course, there is still a minimum size for +the buffers, so the max-memory setting is effective only if it is bigger than +the minimum space needed.) If you allocate any large structures yourself, you +must allocate them before jpeg_start_compress() or jpeg_start_decompress() in +order to have them counted against the max memory limit. Also keep in mind +that space allocated with alloc_small() is ignored, on the assumption that +it's too small to be worth worrying about; so a reasonable safety margin +should be left when setting max_memory_to_use. + +If you use the jmemname.c or jmemdos.c memory manager back end, it is +important to clean up the JPEG object properly to ensure that the temporary +files get deleted. (This is especially crucial with jmemdos.c, where the +"temporary files" may be extended-memory segments; if they are not freed, +DOS will require a reboot to recover the memory.) Thus, with these memory +managers, it's a good idea to provide a signal handler that will trap any +early exit from your program. The handler should call either jpeg_abort() +or jpeg_destroy() for any active JPEG objects. A handler is not needed with +jmemnobs.c, and shouldn't be necessary with jmemansi.c or jmemmac.c either, +since the C library is supposed to take care of deleting files made with +tmpfile(). + + +Memory usage +------------ + +Working memory requirements while performing compression or decompression +depend on image dimensions, image characteristics (such as colorspace and +JPEG process), and operating mode (application-selected options). + +As of v6b, the decompressor requires: + 1. About 24K in more-or-less-fixed-size data. This varies a bit depending + on operating mode and image characteristics (particularly color vs. + grayscale), but it doesn't depend on image dimensions. + 2. Strip buffers (of size proportional to the image width) for IDCT and + upsampling results. The worst case for commonly used sampling factors + is about 34 bytes * width in pixels for a color image. A grayscale image + only needs about 8 bytes per pixel column. + 3. A full-image DCT coefficient buffer is needed to decode a multi-scan JPEG + file (including progressive JPEGs), or whenever you select buffered-image + mode. This takes 2 bytes/coefficient. At typical 2x2 sampling, that's + 3 bytes per pixel for a color image. Worst case (1x1 sampling) requires + 6 bytes/pixel. For grayscale, figure 2 bytes/pixel. + 4. To perform 2-pass color quantization, the decompressor also needs a + 128K color lookup table and a full-image pixel buffer (3 bytes/pixel). +This does not count any memory allocated by the application, such as a +buffer to hold the final output image. + +The above figures are valid for 8-bit JPEG data precision and a machine with +32-bit ints. For 12-bit JPEG data, double the size of the strip buffers and +quantization pixel buffer. The "fixed-size" data will be somewhat smaller +with 16-bit ints, larger with 64-bit ints. Also, CMYK or other unusual +color spaces will require different amounts of space. + +The full-image coefficient and pixel buffers, if needed at all, do not +have to be fully RAM resident; you can have the library use temporary +files instead when the total memory usage would exceed a limit you set. +(But if your OS supports virtual memory, it's probably better to just use +jmemnobs and let the OS do the swapping.) + +The compressor's memory requirements are similar, except that it has no need +for color quantization. Also, it needs a full-image DCT coefficient buffer +if Huffman-table optimization is asked for, even if progressive mode is not +requested. + +If you need more detailed information about memory usage in a particular +situation, you can enable the MEM_STATS code in jmemmgr.c. + + +Library compile-time options +---------------------------- + +A number of compile-time options are available by modifying jmorecfg.h. + +The JPEG standard provides for both the baseline 8-bit DCT process and +a 12-bit DCT process. The IJG code supports 12-bit lossy JPEG if you define +BITS_IN_JSAMPLE as 12 rather than 8. Note that this causes JSAMPLE to be +larger than a char, so it affects the surrounding application's image data. +The sample applications cjpeg and djpeg can support 12-bit mode only for PPM +and GIF file formats; you must disable the other file formats to compile a +12-bit cjpeg or djpeg. (install.doc has more information about that.) +At present, a 12-bit library can handle *only* 12-bit images, not both +precisions. (If you need to include both 8- and 12-bit libraries in a single +application, you could probably do it by defining NEED_SHORT_EXTERNAL_NAMES +for just one of the copies. You'd have to access the 8-bit and 12-bit copies +from separate application source files. This is untested ... if you try it, +we'd like to hear whether it works!) + +Note that a 12-bit library always compresses in Huffman optimization mode, +in order to generate valid Huffman tables. This is necessary because our +default Huffman tables only cover 8-bit data. If you need to output 12-bit +files in one pass, you'll have to supply suitable default Huffman tables. +You may also want to supply your own DCT quantization tables; the existing +quality-scaling code has been developed for 8-bit use, and probably doesn't +generate especially good tables for 12-bit. + +The maximum number of components (color channels) in the image is determined +by MAX_COMPONENTS. The JPEG standard allows up to 255 components, but we +expect that few applications will need more than four or so. + +On machines with unusual data type sizes, you may be able to improve +performance or reduce memory space by tweaking the various typedefs in +jmorecfg.h. In particular, on some RISC CPUs, access to arrays of "short"s +is quite slow; consider trading memory for speed by making JCOEF, INT16, and +UINT16 be "int" or "unsigned int". UINT8 is also a candidate to become int. +You probably don't want to make JSAMPLE be int unless you have lots of memory +to burn. + +You can reduce the size of the library by compiling out various optional +functions. To do this, undefine xxx_SUPPORTED symbols as necessary. + +You can also save a few K by not having text error messages in the library; +the standard error message table occupies about 5Kb. This is particularly +reasonable for embedded applications where there's no good way to display +a message anyway. To do this, remove the creation of the message table +(jpeg_std_message_table[]) from jerror.c, and alter format_message to do +something reasonable without it. You could output the numeric value of the +message code number, for example. If you do this, you can also save a couple +more K by modifying the TRACEMSn() macros in jerror.h to expand to nothing; +you don't need trace capability anyway, right? + + +Portability considerations +-------------------------- + +The JPEG library has been written to be extremely portable; the sample +applications cjpeg and djpeg are slightly less so. This section summarizes +the design goals in this area. (If you encounter any bugs that cause the +library to be less portable than is claimed here, we'd appreciate hearing +about them.) + +The code works fine on ANSI C, C++, and pre-ANSI C compilers, using any of +the popular system include file setups, and some not-so-popular ones too. +See install.doc for configuration procedures. + +The code is not dependent on the exact sizes of the C data types. As +distributed, we make the assumptions that + char is at least 8 bits wide + short is at least 16 bits wide + int is at least 16 bits wide + long is at least 32 bits wide +(These are the minimum requirements of the ANSI C standard.) Wider types will +work fine, although memory may be used inefficiently if char is much larger +than 8 bits or short is much bigger than 16 bits. The code should work +equally well with 16- or 32-bit ints. + +In a system where these assumptions are not met, you may be able to make the +code work by modifying the typedefs in jmorecfg.h. However, you will probably +have difficulty if int is less than 16 bits wide, since references to plain +int abound in the code. + +char can be either signed or unsigned, although the code runs faster if an +unsigned char type is available. If char is wider than 8 bits, you will need +to redefine JOCTET and/or provide custom data source/destination managers so +that JOCTET represents exactly 8 bits of data on external storage. + +The JPEG library proper does not assume ASCII representation of characters. +But some of the image file I/O modules in cjpeg/djpeg do have ASCII +dependencies in file-header manipulation; so does cjpeg's select_file_type() +routine. + +The JPEG library does not rely heavily on the C library. In particular, C +stdio is used only by the data source/destination modules and the error +handler, all of which are application-replaceable. (cjpeg/djpeg are more +heavily dependent on stdio.) malloc and free are called only from the memory +manager "back end" module, so you can use a different memory allocator by +replacing that one file. + +The code generally assumes that C names must be unique in the first 15 +characters. However, global function names can be made unique in the +first 6 characters by defining NEED_SHORT_EXTERNAL_NAMES. + +More info about porting the code may be gleaned by reading jconfig.doc, +jmorecfg.h, and jinclude.h. + + +Notes for MS-DOS implementors +----------------------------- + +The IJG code is designed to work efficiently in 80x86 "small" or "medium" +memory models (i.e., data pointers are 16 bits unless explicitly declared +"far"; code pointers can be either size). You may be able to use small +model to compile cjpeg or djpeg by itself, but you will probably have to use +medium model for any larger application. This won't make much difference in +performance. You *will* take a noticeable performance hit if you use a +large-data memory model (perhaps 10%-25%), and you should avoid "huge" model +if at all possible. + +The JPEG library typically needs 2Kb-3Kb of stack space. It will also +malloc about 20K-30K of near heap space while executing (and lots of far +heap, but that doesn't count in this calculation). This figure will vary +depending on selected operating mode, and to a lesser extent on image size. +There is also about 5Kb-6Kb of constant data which will be allocated in the +near data segment (about 4Kb of this is the error message table). +Thus you have perhaps 20K available for other modules' static data and near +heap space before you need to go to a larger memory model. The C library's +static data will account for several K of this, but that still leaves a good +deal for your needs. (If you are tight on space, you could reduce the sizes +of the I/O buffers allocated by jdatasrc.c and jdatadst.c, say from 4K to +1K. Another possibility is to move the error message table to far memory; +this should be doable with only localized hacking on jerror.c.) + +About 2K of the near heap space is "permanent" memory that will not be +released until you destroy the JPEG object. This is only an issue if you +save a JPEG object between compression or decompression operations. + +Far data space may also be a tight resource when you are dealing with large +images. The most memory-intensive case is decompression with two-pass color +quantization, or single-pass quantization to an externally supplied color +map. This requires a 128Kb color lookup table plus strip buffers amounting +to about 40 bytes per column for typical sampling ratios (eg, about 25600 +bytes for a 640-pixel-wide image). You may not be able to process wide +images if you have large data structures of your own. + +Of course, all of these concerns vanish if you use a 32-bit flat-memory-model +compiler, such as DJGPP or Watcom C. We highly recommend flat model if you +can use it; the JPEG library is significantly faster in flat model. diff --git a/Engine/lib/ljpeg/extras/ltconfig b/Engine/lib/ljpeg/extras/ltconfig new file mode 100644 index 000000000..2347e6943 --- /dev/null +++ b/Engine/lib/ljpeg/extras/ltconfig @@ -0,0 +1,1512 @@ +#! /bin/sh + +# ltconfig - Create a system-specific libtool. +# Copyright (C) 1996-1998 Free Software Foundation, Inc. +# Gordon Matzigkeit , 1996 +# +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# A lot of this script is taken from autoconf-2.10. + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +if test "${CDPATH+set}" = set; then CDPATH=; export CDPATH; fi + +echo=echo +if test "X`($echo '\t') 2>/dev/null`" = 'X\t'; then : +else + # The Solaris and AIX default echo program unquotes backslashes. + # This makes it impossible to quote backslashes using + # echo "$something" | sed 's/\\/\\\\/g' + # So, we emulate echo with printf '%s\n' + echo="printf %s\\n" + if test "X`($echo '\t') 2>/dev/null`" = 'X\t'; then : + else + # Oops. We have no working printf. Try to find a not-so-buggy echo. + echo=echo + IFS="${IFS= }"; save_ifs="$IFS"; IFS="${IFS}:" + for dir in $PATH /usr/ucb; do + if test -f $dir/echo && test "X`$dir/echo '\t'`" = 'X\t'; then + echo="$dir/echo" + break + fi + done + IFS="$save_ifs" + fi +fi + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +Xsed='sed -e s/^X//' +sed_quote_subst='s/\([\\"\\`$\\\\]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\([\\"\\`\\\\]\)/\\\1/g' + +# The name of this program. +progname=`$echo "X$0" | $Xsed -e 's%^.*/%%'` + +# Constants: +PROGRAM=ltconfig +PACKAGE=libtool +VERSION=1.2 +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.c 1>&5' +ac_link='${CC-cc} -o conftest $CFLAGS $CPPFLAGS $LDFLAGS conftest.c $LIBS 1>&5' +rm="rm -f" + +help="Try \`$progname --help' for more information." + +# Global variables: +can_build_shared=yes +enable_shared=yes +# All known linkers require a `.a' archive for static linking. +enable_static=yes +ltmain= +silent= +srcdir= +ac_config_guess= +ac_config_sub= +host= +nonopt= +verify_host=yes +with_gcc=no +with_gnu_ld=no + +old_AR="$AR" +old_CC="$CC" +old_CFLAGS="$CFLAGS" +old_CPPFLAGS="$CPPFLAGS" +old_LD="$LD" +old_LN_S="$LN_S" +old_NM="$NM" +old_RANLIB="$RANLIB" + +# Parse the command line options. +args= +prev= +for option +do + case "$option" in + -*=*) optarg=`echo "$option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) optarg= ;; + esac + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + eval "$prev=\$option" + prev= + continue + fi + + case "$option" in + --help) cat <&2 + echo "$help" 1>&2 + exit 1 + ;; + + *) + if test -z "$ltmain"; then + ltmain="$option" + elif test -z "$host"; then +# This generates an unnecessary warning for sparc-sun-solaris4.1.3_U1 +# if test -n "`echo $option| sed 's/[-a-z0-9.]//g'`"; then +# echo "$progname: warning \`$option' is not a valid host type" 1>&2 +# fi + host="$option" + else + echo "$progname: too many arguments" 1>&2 + echo "$help" 1>&2 + exit 1 + fi ;; + esac +done + +if test -z "$ltmain"; then + echo "$progname: you must specify a LTMAIN file" 1>&2 + echo "$help" 1>&2 + exit 1 +fi + +if test -f "$ltmain"; then : +else + echo "$progname: \`$ltmain' does not exist" 1>&2 + echo "$help" 1>&2 + exit 1 +fi + +# Quote any args containing shell metacharacters. +ltconfig_args= +for arg +do + case "$arg" in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) + ltconfig_args="$ltconfig_args '$arg'" ;; + *) ltconfig_args="$ltconfig_args $arg" ;; + esac +done + +# A relevant subset of AC_INIT. + +# File descriptor usage: +# 0 standard input +# 1 file creation +# 2 errors and warnings +# 3 some systems may open it to /dev/tty +# 4 used on the Kubota Titan +# 5 compiler messages saved in config.log +# 6 checking for... messages and results +if test "$silent" = yes; then + exec 6>/dev/null +else + exec 6>&1 +fi +exec 5>>./config.log + +# NLS nuisances. +# Only set LANG and LC_ALL to C if already set. +# These must not be set unconditionally because not all systems understand +# e.g. LANG=C (notably SCO). +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LANG+set}" = set; then LANG=C; export LANG; fi + +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then + # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then + ac_n= ac_c=' +' ac_t=' ' + else + ac_n=-n ac_c= ac_t= + fi +else + ac_n= ac_c='\c' ac_t= +fi + +if test -z "$srcdir"; then + # Assume the source directory is the same one as the path to ltmain.sh. + srcdir=`$echo "$ltmain" | $Xsed -e 's%/[^/]*$%%'` + test "$srcdir" = "$ltmain" && srcdir=. +fi + +trap "$rm conftest*; exit 1" 1 2 15 +if test "$verify_host" = yes; then + # Check for config.guess and config.sub. + ac_aux_dir= + for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/config.guess; then + ac_aux_dir=$ac_dir + break + fi + done + if test -z "$ac_aux_dir"; then + echo "$progname: cannot find config.guess in $srcdir $srcdir/.. $srcdir/../.." 1>&2 + echo "$help" 1>&2 + exit 1 + fi + ac_config_guess=$ac_aux_dir/config.guess + ac_config_sub=$ac_aux_dir/config.sub + + # Make sure we can run config.sub. + if $ac_config_sub sun4 >/dev/null 2>&1; then : + else + echo "$progname: cannot run $ac_config_sub" 1>&2 + echo "$help" 1>&2 + exit 1 + fi + + echo $ac_n "checking host system type""... $ac_c" 1>&6 + + host_alias=$host + case "$host_alias" in + "") + if host_alias=`$ac_config_guess`; then : + else + echo "$progname: cannot guess host type; you must specify one" 1>&2 + echo "$help" 1>&2 + exit 1 + fi ;; + esac + host=`$ac_config_sub $host_alias` + echo "$ac_t$host" 1>&6 + + # Make sure the host verified. + test -z "$host" && exit 1 + +elif test -z "$host"; then + echo "$progname: you must specify a host type if you use \`--no-verify'" 1>&2 + echo "$help" 1>&2 + exit 1 +else + host_alias=$host +fi + +# Transform linux* to *-*-linux-gnu*, to support old configure scripts. +case "$host_os" in +linux-gnu*) ;; +linux*) host=`echo $host | sed 's/^\(.*-.*-linux\)\(.*\)$/\1-gnu\2/'` +esac + +host_cpu=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +host_vendor=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +host_os=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + +case "$host_os" in +aix3*) + # AIX sometimes has problems with the GCC collect2 program. For some + # reason, if we set the COLLECT_NAMES environment variable, the problems + # vanish in a puff of smoke. + if test "${COLLECT_NAMES+set}" != set; then + COLLECT_NAMES= + export COLLECT_NAMES + fi + ;; +esac + +# Determine commands to create old-style static archives. +old_archive_cmds='$AR cru $oldlib$oldobjs' +old_postinstall_cmds='chmod 644 $oldlib' +old_postuninstall_cmds= + +# Set a sane default for `AR'. +test -z "$AR" && AR=ar + +# If RANLIB is not set, then run the test. +if test "${RANLIB+set}" != "set"; then + result=no + + echo $ac_n "checking for ranlib... $ac_c" 1>&6 + IFS="${IFS= }"; save_ifs="$IFS"; IFS="${IFS}:" + for dir in $PATH; do + test -z "$dir" && dir=. + if test -f $dir/ranlib; then + RANLIB="ranlib" + result="ranlib" + break + fi + done + IFS="$save_ifs" + + echo "$ac_t$result" 1>&6 +fi + +if test -n "$RANLIB"; then + old_archive_cmds="$old_archive_cmds;\$RANLIB \$oldlib" + old_postinstall_cmds="\$RANLIB \$oldlib;$old_postinstall_cmds" +fi + +# Check to see if we are using GCC. +if test "$with_gcc" != yes || test -z "$CC"; then + # If CC is not set, then try to find GCC or a usable CC. + if test -z "$CC"; then + echo $ac_n "checking for gcc... $ac_c" 1>&6 + IFS="${IFS= }"; save_ifs="$IFS"; IFS="${IFS}:" + for dir in $PATH; do + IFS="$save_ifs" + test -z "$dir" && dir=. + if test -f $dir/gcc; then + CC="gcc" + break + fi + done + IFS="$save_ifs" + + if test -n "$CC"; then + echo "$ac_t$CC" 1>&6 + else + echo "$ac_t"no 1>&6 + fi + fi + + # Not "gcc", so try "cc", rejecting "/usr/ucb/cc". + if test -z "$CC"; then + echo $ac_n "checking for cc... $ac_c" 1>&6 + IFS="${IFS= }"; save_ifs="$IFS"; IFS="${IFS}:" + cc_rejected=no + for dir in $PATH; do + test -z "$dir" && dir=. + if test -f $dir/cc; then + if test "$dir/cc" = "/usr/ucb/cc"; then + cc_rejected=yes + continue + fi + CC="cc" + break + fi + done + IFS="$save_ifs" + if test $cc_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $CC + shift + if test $# -gt 0; then + # We chose a different compiler from the bogus one. + # However, it has the same name, so the bogon will be chosen + # first if we set CC to just the name; use the full file name. + shift + set dummy "$dir/cc" "$@" + shift + CC="$@" + fi + fi + + if test -n "$CC"; then + echo "$ac_t$CC" 1>&6 + else + echo "$ac_t"no 1>&6 + fi + + if test -z "$CC"; then + echo "$progname: error: no acceptable cc found in \$PATH" 1>&2 + exit 1 + fi + fi + + # Now see if the compiler is really GCC. + with_gcc=no + echo $ac_n "checking whether we are using GNU C... $ac_c" 1>&6 + echo "$progname:424: checking whether we are using GNU C" >&5 + + $rm conftest.c + cat > conftest.c <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + with_gcc=yes + fi + $rm conftest.c + echo "$ac_t$with_gcc" 1>&6 +fi + +# Allow CC to be a program name with arguments. +set dummy $CC +compiler="$2" + +echo $ac_n "checking for $compiler option to produce PIC... $ac_c" 1>&6 +pic_flag= +special_shlib_compile_flags= +wl= +link_static_flag= +no_builtin_flag= + +if test "$with_gcc" = yes; then + wl='-Wl,' + link_static_flag='-static' + no_builtin_flag=' -fno-builtin' + + case "$host_os" in + aix3* | aix4* | irix5* | irix6* | osf3* | osf4*) + # PIC is the default for these OSes. + ;; + os2*) + # We can build DLLs from non-PIC. + ;; + amigaos*) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + pic_flag='-m68020 -resident32 -malways-restore-a4' + ;; + *) + pic_flag='-fPIC' + ;; + esac +else + # PORTME Check for PIC flags for the system compiler. + case "$host_os" in + aix3* | aix4*) + # All AIX code is PIC. + link_static_flag='-bnso -bI:/lib/syscalls.exp' + ;; + + hpux9* | hpux10*) + # Is there a better link_static_flag that works with the bundled CC? + wl='-Wl,' + link_static_flag="${wl}-a ${wl}archive" + pic_flag='+Z' + ;; + + irix5* | irix6*) + wl='-Wl,' + link_static_flag='-non_shared' + # PIC (with -KPIC) is the default. + ;; + + os2*) + # We can build DLLs from non-PIC. + ;; + + osf3* | osf4*) + # All OSF/1 code is PIC. + wl='-Wl,' + link_static_flag='-non_shared' + ;; + + sco3.2v5*) + pic_flag='-Kpic' + link_static_flag='-dn' + special_shlib_compile_flags='-belf' + ;; + + solaris2*) + pic_flag='-KPIC' + link_static_flag='-Bstatic' + wl='-Wl,' + ;; + + sunos4*) + pic_flag='-PIC' + link_static_flag='-Bstatic' + wl='-Qoption ld ' + ;; + + sysv4.2uw2*) + pic_flag='-KPIC' + link_static_flag='-Bstatic' + wl='-Wl,' + ;; + + uts4*) + pic_flag='-pic' + link_static_flag='-Bstatic' + ;; + + *) + can_build_shared=no + ;; + esac +fi + +if test -n "$pic_flag"; then + echo "$ac_t$pic_flag" 1>&6 + + # Check to make sure the pic_flag actually works. + echo $ac_n "checking if $compiler PIC flag $pic_flag works... $ac_c" 1>&6 + $rm conftest* + echo > conftest.c + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $pic_flag -DPIC" + echo "$progname:547: checking if $compiler PIC flag $pic_flag works" >&5 + if { (eval echo $progname:548: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>conftest.err; } && test -s conftest.o; then + # Append any warnings to the config.log. + cat conftest.err 1>&5 + + # On HP-UX, both CC and GCC only warn that PIC is supported... then they + # create non-PIC objects. So, if there were any warnings, we assume that + # PIC is not supported. + if test -s conftest.err; then + echo "$ac_t"no 1>&6 + can_build_shared=no + pic_flag= + else + echo "$ac_t"yes 1>&6 + pic_flag=" $pic_flag" + fi + else + # Append any errors to the config.log. + cat conftest.err 1>&5 + can_build_shared=no + pic_flag= + echo "$ac_t"no 1>&6 + fi + CFLAGS="$save_CFLAGS" + $rm conftest* +else + echo "$ac_t"none 1>&6 +fi + +# Check for any special shared library compilation flags. +if test -n "$special_shlib_compile_flags"; then + echo "$progname: warning: \`$CC' requires \`$special_shlib_compile_flags' to build shared libraries" 1>&2 + if echo "$old_CC $old_CFLAGS " | egrep -e "[ ]$special_shlib_compile_flags[ ]" >/dev/null; then : + else + echo "$progname: add \`$special_shlib_compile_flags' to the CC or CFLAGS env variable and reconfigure" 1>&2 + can_build_shared=no + fi +fi + +echo $ac_n "checking if $compiler static flag $link_static_flag works... $ac_c" 1>&6 +$rm conftest* +echo 'main(){return(0);}' > conftest.c +save_LDFLAGS="$LDFLAGS" +LDFLAGS="$LDFLAGS $link_static_flag" +echo "$progname:591: checking if $compiler static flag $link_static_flag works" >&5 +if { (eval echo $progname:592: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + echo "$ac_t$link_static_flag" 1>&6 +else + echo "$ac_t"none 1>&6 + link_static_flag= +fi +LDFLAGS="$save_LDFLAGS" +$rm conftest* + +if test -z "$LN_S"; then + # Check to see if we can use ln -s, or we need hard links. + echo $ac_n "checking whether ln -s works... $ac_c" 1>&6 + $rm conftestdata + if ln -s X conftestdata 2>/dev/null; then + $rm conftestdata + LN_S="ln -s" + else + LN_S=ln + fi + if test "$LN_S" = "ln -s"; then + echo "$ac_t"yes 1>&6 + else + echo "$ac_t"no 1>&6 + fi +fi + +# Make sure LD is an absolute path. +if test -z "$LD"; then + ac_prog=ld + if test "$with_gcc" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + echo $ac_n "checking for ld used by GCC... $ac_c" 1>&6 + echo "$progname:624: checking for ld used by GCC" >&5 + ac_prog=`($CC -print-prog-name=ld) 2>&5` + case "$ac_prog" in + # Accept absolute paths. + /* | [A-Za-z]:\\*) + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we are not using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac + elif test "$with_gnu_ld" = yes; then + echo $ac_n "checking for GNU ld... $ac_c" 1>&6 + echo "$progname:642: checking for GNU ld" >&5 + else + echo $ac_n "checking for non-GNU ld""... $ac_c" 1>&6 + echo "$progname:645: checking for non-GNU ld" >&5 + fi + + if test -z "$LD"; then + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog"; then + LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some GNU ld's only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + if "$LD" -v 2>&1 < /dev/null | egrep '(GNU|with BFD)' > /dev/null; then + test "$with_gnu_ld" != no && break + else + test "$with_gnu_ld" != yes && break + fi + fi + done + IFS="$ac_save_ifs" + fi + + if test -n "$LD"; then + echo "$ac_t$LD" 1>&6 + else + echo "$ac_t"no 1>&6 + fi + + if test -z "$LD"; then + echo "$progname: error: no acceptable ld found in \$PATH" 1>&2 + exit 1 + fi +fi + +# Check to see if it really is or is not GNU ld. +echo $ac_n "checking if the linker ($LD) is GNU ld... $ac_c" 1>&6 +# I'd rather use --version here, but apparently some GNU ld's only accept -v. +if $LD -v 2>&1 &5; then + with_gnu_ld=yes +else + with_gnu_ld=no +fi +echo "$ac_t$with_gnu_ld" 1>&6 + +# See if the linker supports building shared libraries. +echo $ac_n "checking whether the linker ($LD) supports shared libraries... $ac_c" 1>&6 + +allow_undefined_flag= +no_undefined_flag= +archive_cmds= +old_archive_from_new_cmds= +export_dynamic_flag_spec= +hardcode_libdir_flag_spec= +hardcode_libdir_separator= +hardcode_direct=no +hardcode_minus_L=no +hardcode_shlibpath_var=unsupported +runpath_var= + +case "$host_os" in +amigaos* | sunos4*) + # On these operating systems, we should treat GNU ld like the system ld. + gnu_ld_acts_native=yes + ;; +*) + gnu_ld_acts_native=no + ;; +esac + +ld_shlibs=yes +if test "$with_gnu_ld" = yes && test "$gnu_ld_acts_native" != yes; then + + # See if GNU ld supports shared libraries. + if $LD --help 2>&1 | egrep ': supported targets:.* elf' > /dev/null; then + archive_cmds='$CC -shared ${wl}-soname $wl$soname -o $lib$libobjs' + runpath_var=LD_RUN_PATH + ld_shlibs=yes + else + ld_shlibs=no + fi + + if test "$ld_shlibs" = yes; then + hardcode_libdir_flag_spec='${wl}--rpath ${wl}$libdir' + export_dynamic_flag_spec='${wl}--export-dynamic' + fi +else + # PORTME fill in a description of your system's linker (not GNU ld) + case "$host_os" in + aix3*) + allow_undefined_flag=unsupported + archive_cmds='$NM$libobjs | $global_symbol_pipe | sed '\''s/.* //'\'' > $lib.exp;$LD -o $objdir/$soname$libobjs -bE:$lib.exp -T512 -H512 -bM:SRE;$AR cru $lib $objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + hardcode_minus_L=yes + if test "$with_gcc" = yes && test -z "$link_static_flag"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + hardcode_direct=unsupported + fi + ;; + + aix4*) + allow_undefined_flag=unsupported + archive_cmds='$NM$libobjs | $global_symbol_pipe | sed '\''s/.* //'\'' > $lib.exp;$CC -o $objdir/$soname$libobjs ${wl}-bE:$lib.exp ${wl}-bM:SRE ${wl}-bnoentry;$AR cru $lib $objdir/$soname' + hardcode_direct=yes + hardcode_minus_L=yes + ;; + + amigaos*) + archive_cmds='$rm $objdir/a2ixlibrary.data;$echo "#define NAME $libname" > $objdir/a2ixlibrary.data;$echo "#define LIBRARY_ID 1" >> $objdir/a2ixlibrary.data;$echo "#define VERSION $major" >> $objdir/a2ixlibrary.data;$echo "#define REVISION $revision" >> $objdir/a2ixlibrary.data;$AR cru $lib$libobjs;$RANLIB $lib;(cd $objdir && a2ixlibrary -32)' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + archive_cmds='$LD -Bshareable -o $lib$libobjs /usr/lib/c++rt0.o' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2*) + archive_cmds='$LD -Bshareable -o $lib$libobjs' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + # FreeBSD 3, at last, uses gcc -shared to do shared libraries. + freebsd3*) + archive_cmds='$CC -shared -o $lib$libobjs' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + hpux9*) + archive_cmds='$rm $objdir/$soname;$LD -b +s +b $install_libdir -o $objdir/$soname$libobjs;mv $objdir/$soname $lib' + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_direct=yes + hardcode_minus_L=yes + export_dynamic_flag_spec='${wl}-E' + ;; + + hpux10*) + archive_cmds='$LD -b +h $soname +s +b $install_libdir -o $lib$libobjs' + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_direct=yes + hardcode_minus_L=yes + export_dynamic_flag_spec='${wl}-E' + ;; + + irix5* | irix6*) + archive_cmds='$LD -shared -o $lib -soname $soname -set_version $verstring$libobjs' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + ;; + + netbsd*) + # Tested with NetBSD 1.2 ld + archive_cmds='$LD -Bshareable -o $lib$libobjs' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + openbsd*) + archive_cmds='$LD -Bshareable -o $lib$libobjs' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + + os2*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + allow_undefined_flag=unsupported + archive_cmds='$echo "LIBRARY $libname INITINSTANCE" > $objdir/$libname.def;$echo "DESCRIPTION \"$libname\"" >> $objdir/$libname.def;$echo DATA >> $objdir/$libname.def;$echo " SINGLE NONSHARED" >> $objdir/$libname.def;$echo EXPORTS >> $objdir/$libname.def;emxexp$libobjs >> $objdir/$libname.def;$CC -Zdll -Zcrtdll -o $lib$libobjs $objdir/$libname.def' + old_archive_from_new_cmds='emximp -o $objdir/$libname.a $objdir/$libname.def' + ;; + + osf3* | osf4*) + allow_undefined_flag=' -expect_unresolved \*' + archive_cmds='$LD -shared${allow_undefined_flag} -o $lib -soname $soname -set_version $verstring$libobjs$deplibs' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; + + sco3.2v5*) + archive_cmds='$LD -G -o $lib$libobjs' + hardcode_direct=yes + ;; + + solaris2*) + no_undefined_flag=' -z text' + archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib$libobjs' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_shlibpath_var=no + + # Solaris 2 before 2.5 hardcodes -L paths. + case "$host_os" in + solaris2.[0-4]*) + hardcode_minus_L=yes + ;; + esac + ;; + + sunos4*) + if test "$with_gcc" = yes; then + archive_cmds='$CC -shared -o $lib$libobjs' + else + archive_cmds='$LD -assert pure-text -Bstatic -o $lib$libobjs' + fi + + if test "$with_gnu_ld" = yes; then + export_dynamic_flag_spec='${wl}-export-dynamic' + fi + hardcode_libdir_flag_spec='-L$libdir' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; + + uts4*) + archive_cmds='$LD -G -h $soname -o $lib$libobjs' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_direct=no + hardcode_minus_L=no + hardcode_shlibpath_var=no + ;; + + *) + ld_shlibs=no + can_build_shared=no + ;; + esac +fi +echo "$ac_t$ld_shlibs" 1>&6 + +if test -z "$NM"; then + echo $ac_n "checking for BSD-compatible nm... $ac_c" 1>&6 + case "$NM" in + /* | [A-Za-z]:\\*) ;; # Let the user override the test with a path. + *) + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}:" + for ac_dir in /usr/ucb /usr/ccs/bin $PATH /bin; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/nm; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the `sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + if ($ac_dir/nm -B /dev/null 2>&1 | sed '1q'; exit 0) | egrep /dev/null >/dev/null; then + NM="$ac_dir/nm -B" + elif ($ac_dir/nm -p /dev/null 2>&1 | sed '1q'; exit 0) | egrep /dev/null >/dev/null; then + NM="$ac_dir/nm -p" + else + NM="$ac_dir/nm" + fi + break + fi + done + IFS="$ac_save_ifs" + test -z "$NM" && NM=nm + ;; + esac + echo "$ac_t$NM" 1>&6 +fi + +# Check for command to grab the raw symbol name followed by C symbol from nm. +echo $ac_n "checking command to parse $NM output... $ac_c" 1>&6 + +# These are sane defaults that work on at least a few old systems. +# [They come from Ultrix. What could be older than Ultrix?!! ;)] + +# Character class describing NM global symbol codes. +symcode='[BCDEGRSTU]' + +# Regexp to match symbols that can be accessed directly from C. +sympat='\([_A-Za-z][_A-Za-z0-9]*\)' + +# Transform the above into a raw symbol and a C symbol. +symxfrm='\1 \1' + +# Define system-specific variables. +case "$host_os" in +aix*) + symcode='[BCDTU]' + ;; +irix*) + # Cannot use undefined symbols on IRIX because inlined functions mess us up. + symcode='[BCDEGRST]' + ;; +solaris2*) + symcode='[BDTU]' + ;; +esac + +# If we're using GNU nm, then use its standard symbol codes. +if $NM -V 2>&1 | egrep '(GNU|with BFD)' > /dev/null; then + symcode='[ABCDGISTUW]' +fi + +# Write the raw and C identifiers. +global_symbol_pipe="sed -n -e 's/^.* $symcode $sympat$/$symxfrm/p'" + +# Check to see that the pipe works correctly. +pipe_works=no +$rm conftest* +cat > conftest.c <&5 +if { (eval echo $progname:972: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; } && test -s conftest.o; then + # Now try to grab the symbols. + nlist=conftest.nm + if { echo "$progname:975: eval \"$NM conftest.o | $global_symbol_pipe > $nlist\"" >&5; eval "$NM conftest.o | $global_symbol_pipe > $nlist 2>&5"; } && test -s "$nlist"; then + + # Try sorting and uniquifying the output. + if sort "$nlist" | uniq > "$nlist"T; then + mv -f "$nlist"T "$nlist" + wcout=`wc "$nlist" 2>/dev/null` + count=`$echo "X$wcout" | $Xsed -e 's/^[ ]*\([0-9][0-9]*\).*$/\1/'` + (test "$count" -ge 0) 2>/dev/null || count=-1 + else + rm -f "$nlist"T + count=-1 + fi + + # Make sure that we snagged all the symbols we need. + if egrep ' nm_test_var$' "$nlist" >/dev/null; then + if egrep ' nm_test_func$' "$nlist" >/dev/null; then + cat < conftest.c +#ifdef __cplusplus +extern "C" { +#endif + +EOF + # Now generate the symbol file. + sed 's/^.* \(.*\)$/extern char \1;/' < "$nlist" >> conftest.c + + cat <> conftest.c +#if defined (__STDC__) && __STDC__ +# define __ptr_t void * +#else +# define __ptr_t char * +#endif + +/* The number of symbols in dld_preloaded_symbols, -1 if unsorted. */ +int dld_preloaded_symbol_count = $count; + +/* The mapping between symbol names and symbols. */ +struct { + char *name; + __ptr_t address; +} +dld_preloaded_symbols[] = +{ +EOF + sed 's/^\(.*\) \(.*\)$/ {"\1", (__ptr_t) \&\2},/' < "$nlist" >> conftest.c + cat <<\EOF >> conftest.c + {0, (__ptr_t) 0} +}; + +#ifdef __cplusplus +} +#endif +EOF + # Now try linking the two files. + mv conftest.o conftestm.o + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + LIBS='conftestm.o' + CFLAGS="$CFLAGS$no_builtin_flag" + if { (eval echo $progname:1033: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest; then + pipe_works=yes + else + echo "$progname: failed program was:" >&5 + cat conftest.c >&5 + fi + LIBS="$save_LIBS" + else + echo "cannot find nm_test_func in $nlist" >&5 + fi + else + echo "cannot find nm_test_var in $nlist" >&5 + fi + else + echo "cannot run $global_symbol_pipe" >&5 + fi +else + echo "$progname: failed program was:" >&5 + cat conftest.c >&5 +fi +$rm conftest* + +# Do not use the global_symbol_pipe unless it works. +echo "$ac_t$pipe_works" 1>&6 +test "$pipe_works" = yes || global_symbol_pipe= + +# Check hardcoding attributes. +echo $ac_n "checking how to hardcode library paths into programs... $ac_c" 1>&6 +hardcode_action= +if test -n "$hardcode_libdir_flag_spec" || \ + test -n "$runpath_var"; then + + # We can hardcode non-existant directories. + if test "$hardcode_direct" != no && \ + test "$hardcode_minus_L" != no && \ + test "$hardcode_shlibpath_var" != no; then + + # Linking always hardcodes the temporary library directory. + hardcode_action=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + hardcode_action=immediate + fi +elif test "$hardcode_direct" != yes && \ + test "$hardcode_minus_L" != yes && \ + test "$hardcode_shlibpath_var" != yes; then + # We cannot hardcode anything. + hardcode_action=unsupported +else + # We can only hardcode existing directories. + hardcode_action=relink +fi +echo "$ac_t$hardcode_action" 1>&6 +test "$hardcode_action" = unsupported && can_build_shared=no + + +reload_flag= +reload_cmds='$LD$reload_flag -o $output$reload_objs' +echo $ac_n "checking for $LD option to reload object files... $ac_c" 1>&6 +# PORTME Some linker may need a different reload flag. +reload_flag='-r' +echo "$ac_t$reload_flag" +test -n "$reload_flag" && reload_flag=" $reload_flag" + +# PORTME Fill in your ld.so characteristics +library_names_spec= +libname_spec='lib$name' +soname_spec= +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +version_type=none +dynamic_linker="$host_os ld.so" + +echo $ac_n "checking dynamic linker characteristics... $ac_c" 1>&6 +case "$host_os" in +aix3* | aix4*) + version_type=linux + library_names_spec='${libname}${release}.so.$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX has no versioning support, so we append a major version to the name. + soname_spec='${libname}${release}.so.$major' + ;; + +amigaos*) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "(cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a)"; (cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a) || exit 1; done' + ;; + +freebsd2* | freebsd3*) + version_type=sunos + library_names_spec='${libname}${release}.so.$versuffix $libname.so' + finish_cmds='PATH="$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + ;; + +gnu*) + version_type=sunos + library_names_spec='${libname}${release}.so.$versuffix' + shlibpath_var=LD_LIBRARY_PATH + ;; + +hpux9* | hpux10*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + dynamic_linker="$host_os dld.sl" + version_type=sunos + shlibpath_var=SHLIB_PATH + library_names_spec='${libname}${release}.sl.$versuffix ${libname}${release}.sl.$major $libname.sl' + soname_spec='${libname}${release}.sl.$major' + # HP-UX runs *really* slowly unless shared libraries are mode 555. + postinstall_cmds='chmod 555 $lib' + ;; + +irix5* | irix6*) + version_type=osf + soname_spec='${libname}${release}.so' + library_names_spec='${libname}${release}.so.$versuffix $libname.so' + shlibpath_var=LD_LIBRARY_PATH + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux-gnuoldld* | linux-gnuaout* | linux-gnucoff*) + dynamic_linker=no + ;; + +# This must be Linux ELF. +linux-gnu*) + version_type=linux + library_names_spec='${libname}${release}.so.$versuffix ${libname}${release}.so.$major $libname.so' + soname_spec='${libname}${release}.so.$major' + finish_cmds='PATH="$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + + if test -f /lib/ld.so.1; then + dynamic_linker='GNU ld.so' + else + # Only the GNU ld.so supports shared libraries on MkLinux. + case "$host_cpu" in + powerpc*) dynamic_linker=no ;; + *) dynamic_linker='Linux ld.so' ;; + esac + fi + ;; + +netbsd* | openbsd*) + version_type=sunos + library_names_spec='${libname}${release}.so.$versuffix' + finish_cmds='PATH="$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + ;; + +os2*) + libname_spec='$name' + library_names_spec='$libname.dll $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + +osf3* | osf4*) + version_type=osf + soname_spec='${libname}${release}.so' + library_names_spec='${libname}${release}.so.$versuffix $libname.so' + shlibpath_var=LD_LIBRARY_PATH + ;; + +sco3.2v5*) + version_type=osf + soname_spec='${libname}${release}.so.$major' + library_names_spec='${libname}${release}.so.$versuffix ${libname}${release}.so.$major $libname.so' + shlibpath_var=LD_LIBRARY_PATH + ;; + +solaris2*) + version_type=linux + library_names_spec='${libname}${release}.so.$versuffix ${libname}${release}.so.$major $libname.so' + soname_spec='${libname}${release}.so.$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +sunos4*) + version_type=sunos + library_names_spec='${libname}${release}.so.$versuffix' + finish_cmds='PATH="$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + ;; + +sysv4.2uw2*) + version_type=linux + library_names_spec='${libname}${release}.so.$versuffix ${libname}${release}.so.$major $libname.so' + soname_spec='${libname}${release}.so.$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +uts4*) + version_type=linux + library_names_spec='${libname}${release}.so.$versuffix ${libname}${release}.so.$major $libname.so' + soname_spec='${libname}${release}.so.$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +echo "$ac_t$dynamic_linker" +test "$dynamic_linker" = no && can_build_shared=no + +# Report the final consequences. +echo "checking if libtool supports shared libraries... $can_build_shared" 1>&6 + +echo $ac_n "checking whether to build shared libraries... $ac_c" 1>&6 +test "$can_build_shared" = "no" && enable_shared=no + +# On AIX, shared libraries and static libraries use the same namespace, and +# are all built from PIC. +case "$host_os" in +aix*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds;\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; +esac + +echo "$ac_t$enable_shared" 1>&6 + +# Make sure either enable_shared or enable_static is yes. +test "$enable_shared" = yes || enable_static=yes + +echo "checking whether to build static libraries... $enable_static" 1>&6 + +echo $ac_n "checking for objdir... $ac_c" 1>&6 +rm -f .libs 2>/dev/null +mkdir .libs 2>/dev/null +if test -d .libs; then + objdir=.libs +else + # MS-DOS does not allow filenames that begin with a dot. + objdir=_libs +fi +rmdir .libs 2>/dev/null +echo "$ac_t$objdir" 1>&6 + +# Copy echo and quote the copy, instead of the original, because it is +# used later. +ltecho="$echo" + +# Now quote all the things that may contain metacharacters. +for var in ltecho old_CC old_CFLAGS old_CPPFLAGS old_LD old_NM old_RANLIB \ + old_LN_S AR CC LD LN_S NM reload_flag reload_cmds wl pic_flag \ + link_static_flag no_builtin_flag export_dynamic_flag_spec \ + libname_spec library_names_spec soname_spec RANLIB \ + old_archive_cmds old_archive_from_new_cmds old_postinstall_cmds \ + old_postuninstall_cmds archive_cmds postinstall_cmds postuninstall_cmds \ + allow_undefined_flag no_undefined_flag \ + finish_cmds finish_eval global_symbol_pipe \ + hardcode_libdir_flag_spec hardcode_libdir_separator; do + + case "$var" in + reload_cmds | old_archive_cmds | old_archive_from_new_cmds | \ + old_postinstall_cmds | old_postuninstall_cmds | archive_cmds | \ + postinstall_cmds | postuninstall_cmds | finish_cmds) + # Double-quote double-evaled strings. + eval "$var=\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\"\`" + ;; + *) + eval "$var=\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`" + ;; + esac +done + +ofile=libtool +trap "$rm $ofile; exit 1" 1 2 15 +echo creating $ofile +$rm $ofile +cat < $ofile +#! /bin/sh + +# libtool - Provide generalized library-building support services. +# Generated automatically by $PROGRAM - GNU $PACKAGE $VERSION +# NOTE: Changes made to this file will be lost: look at ltconfig or ltmain.sh. +# +# Copyright (C) 1996-1998 Free Software Foundation, Inc. +# Gordon Matzigkeit , 1996 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This program was configured as follows, +# on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# +# CC="$old_CC" CFLAGS="$old_CFLAGS" CPPFLAGS="$old_CPPFLAGS" \\ +# LD="$old_LD" NM="$old_NM" RANLIB="$old_RANLIB" LN_S="$old_LN_S" \\ +# $0$ltconfig_args +# +# Compiler and other test output produced by $progname, useful for +# debugging $progname, is in ./config.log if it exists. + +# Sed that helps us avoid accidentally triggering echo(1) options like -n. +Xsed="sed -e s/^X//" + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +if test "\${CDPATH+set}" = set; then CDPATH=; export CDPATH; fi + +# An echo program that does not interpret backslashes. +echo="$ltecho" + +# The version of $progname that generated this script. +LTCONFIG_VERSION="$VERSION" + +# Shell to use when invoking shell scripts. +SHELL=${CONFIG_SHELL-/bin/sh} + +# Whether or not to build libtool libraries. +build_libtool_libs=$enable_shared + +# Whether or not to build old-style libraries. +build_old_libs=$enable_static + +# The host system. +host_alias="$host_alias" +host="$host" + +# The archiver. +AR="$AR" + +# The default C compiler. +CC="$CC" + +# The linker used to build libraries. +LD="$LD" + +# Whether we need hard or soft links. +LN_S="$LN_S" + +# A BSD-compatible nm program. +NM="$NM" + +# The name of the directory that contains temporary libtool files. +objdir="$objdir" + +# How to create reloadable object files. +reload_flag="$reload_flag" +reload_cmds="$reload_cmds" + +# How to pass a linker flag through the compiler. +wl="$wl" + +# Additional compiler flags for building library objects. +pic_flag="$pic_flag" + +# Compiler flag to prevent dynamic linking. +link_static_flag="$link_static_flag" + +# Compiler flag to turn off builtin functions. +no_builtin_flag="$no_builtin_flag" + +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec="$export_dynamic_flag_spec" + +# Library versioning type. +version_type=$version_type + +# Format of library name prefix. +libname_spec="$libname_spec" + +# List of archive names. First name is the real one, the rest are links. +# The last name is the one that the linker finds with -lNAME. +library_names_spec="$library_names_spec" + +# The coded name of the library, if different from the real name. +soname_spec="$soname_spec" + +# Commands used to build and install an old-style archive. +RANLIB="$RANLIB" +old_archive_cmds="$old_archive_cmds" +old_postinstall_cmds="$old_postinstall_cmds" +old_postuninstall_cmds="$old_postuninstall_cmds" + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds="$old_archive_from_new_cmds" + +# Commands used to build and install a shared archive. +archive_cmds="$archive_cmds" +postinstall_cmds="$postinstall_cmds" +postuninstall_cmds="$postuninstall_cmds" + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag="$allow_undefined_flag" + +# Flag that forces no undefined symbols. +no_undefined_flag="$no_undefined_flag" + +# Commands used to finish a libtool library installation in a directory. +finish_cmds="$finish_cmds" + +# Same as above, but a single script fragment to be evaled but not shown. +finish_eval="$finish_eval" + +# Take the output of nm and produce a listing of raw symbols and C names. +global_symbol_pipe="$global_symbol_pipe" + +# This is the shared library runtime path variable. +runpath_var=$runpath_var + +# This is the shared library path variable. +shlibpath_var=$shlibpath_var + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist. +hardcode_libdir_flag_spec="$hardcode_libdir_flag_spec" + +# Whether we need a single -rpath flag with a separated argument. +hardcode_libdir_separator="$hardcode_libdir_separator" + +# Set to yes if using DIR/libNAME.so during linking hardcodes DIR into the +# resulting binary. +hardcode_direct=$hardcode_direct + +# Set to yes if using the -LDIR flag during linking hardcodes DIR into the +# resulting binary. +hardcode_minus_L=$hardcode_minus_L + +# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into +# the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var + +EOF + +case "$host_os" in +aix3*) + cat <<\EOF >> $ofile +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test "${COLLECT_NAMES+set}" != set; then + COLLECT_NAMES= + export COLLECT_NAMES +fi + +EOF + ;; +esac + +# Append the ltmain.sh script. +cat "$ltmain" >> $ofile || (rm -f $ofile; exit 1) + +chmod +x $ofile +exit 0 + +# Local Variables: +# mode:shell-script +# sh-indentation:2 +# End: diff --git a/Engine/lib/ljpeg/extras/ltmain.sh b/Engine/lib/ljpeg/extras/ltmain.sh new file mode 100644 index 000000000..e9350b3fa --- /dev/null +++ b/Engine/lib/ljpeg/extras/ltmain.sh @@ -0,0 +1,2453 @@ +# ltmain.sh - Provide generalized library-building support services. +# NOTE: Changing this file will not affect anything until you rerun ltconfig. +# +# Copyright (C) 1996-1998 Free Software Foundation, Inc. +# Gordon Matzigkeit , 1996 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# The name of this program. +progname=`$echo "$0" | sed 's%^.*/%%'` +modename="$progname" + +# Constants. +PROGRAM=ltmain.sh +PACKAGE=libtool +VERSION=1.2 + +default_mode= +help="Try \`$progname --help' for more information." +magic="%%%MAGIC variable%%%" +mkdir="mkdir" +mv="mv -f" +rm="rm -f" + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +Xsed='sed -e s/^X//' +sed_quote_subst='s/\([\\`\\"$\\\\]\)/\\\1/g' + +# NLS nuisances. +# Only set LANG and LC_ALL to C if already set. +# These must not be set unconditionally because not all systems understand +# e.g. LANG=C (notably SCO). +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LANG+set}" = set; then LANG=C; export LANG; fi + +if test "$LTCONFIG_VERSION" != "$VERSION"; then + echo "$modename: ltconfig version \`$LTCONFIG_VERSION' does not match $PROGRAM version \`$VERSION'" 1>&2 + echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 + exit 1 +fi + +if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then + echo "$modename: not configured to build any kind of library" 1>&2 + echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 + exit 1 +fi + +# Global variables. +mode=$default_mode +nonopt= +prev= +prevopt= +run= +show="$echo" +show_help= +execute_dlfiles= + +# Parse our command line options once, thoroughly. +while test $# -gt 0 +do + arg="$1" + shift + + case "$arg" in + -*=*) optarg=`$echo "X$arg" | $Xsed -e 's/[-_a-zA-Z0-9]*=//'` ;; + *) optarg= ;; + esac + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case "$prev" in + execute_dlfiles) + eval "$prev=\"\$$prev \$arg\"" + ;; + *) + eval "$prev=\$arg" + ;; + esac + + prev= + prevopt= + continue + fi + + # Have we seen a non-optional argument yet? + case "$arg" in + --help) + show_help=yes + ;; + + --version) + echo "$PROGRAM (GNU $PACKAGE) $VERSION" + exit 0 + ;; + + --dry-run | -n) + run=: + ;; + + --features) + echo "host: $host" + if test "$build_libtool_libs" = yes; then + echo "enable shared libraries" + else + echo "disable shared libraries" + fi + if test "$build_old_libs" = yes; then + echo "enable static libraries" + else + echo "disable static libraries" + fi + exit 0 + ;; + + --finish) mode="finish" ;; + + --mode) prevopt="--mode" prev=mode ;; + --mode=*) mode="$optarg" ;; + + --quiet | --silent) + show=: + ;; + + -dlopen) + prevopt="-dlopen" + prev=execute_dlfiles + ;; + + -*) + $echo "$modename: unrecognized option \`$arg'" 1>&2 + $echo "$help" 1>&2 + exit 1 + ;; + + *) + nonopt="$arg" + break + ;; + esac +done + +if test -n "$prevopt"; then + $echo "$modename: option \`$prevopt' requires an argument" 1>&2 + $echo "$help" 1>&2 + exit 1 +fi + +if test -z "$show_help"; then + + # Infer the operation mode. + if test -z "$mode"; then + case "$nonopt" in + *cc | *++ | gcc* | *-gcc*) + mode=link + for arg + do + case "$arg" in + -c) + mode=compile + break + ;; + esac + done + ;; + *db | *dbx) + mode=execute + ;; + *install*|cp|mv) + mode=install + ;; + *rm) + mode=uninstall + ;; + *) + # If we have no mode, but dlfiles were specified, then do execute mode. + test -n "$execute_dlfiles" && mode=execute + + # Just use the default operation mode. + if test -z "$mode"; then + if test -n "$nonopt"; then + $echo "$modename: warning: cannot infer operation mode from \`$nonopt'" 1>&2 + else + $echo "$modename: warning: cannot infer operation mode without MODE-ARGS" 1>&2 + fi + fi + ;; + esac + fi + + # Only execute mode is allowed to have -dlopen flags. + if test -n "$execute_dlfiles" && test "$mode" != execute; then + $echo "$modename: unrecognized option \`-dlopen'" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + # Change the help message to a mode-specific one. + generic_help="$help" + help="Try \`$modename --help --mode=$mode' for more information." + + # These modes are in order of execution frequency so that they run quickly. + case "$mode" in + # libtool compile mode + compile) + modename="$modename: compile" + # Get the compilation command and the source file. + base_compile= + lastarg= + srcfile="$nonopt" + suppress_output= + + for arg + do + # Accept any command-line options. + case "$arg" in + -o) + $echo "$modename: you cannot specify the output filename with \`-o'" 1>&2 + $echo "$help" 1>&2 + exit 1 + ;; + + -static) + build_libtool_libs=no + build_old_libs=yes + continue + ;; + esac + + # Accept the current argument as the source file. + lastarg="$srcfile" + srcfile="$arg" + + # Aesthetically quote the previous argument. + + # Backslashify any backslashes, double quotes, and dollar signs. + # These are the only characters that are still specially + # interpreted inside of double-quoted scrings. + lastarg=`$echo "X$lastarg" | $Xsed -e "$sed_quote_subst"` + + # Double-quote args containing other shell metacharacters. + # Many Bourne shells cannot handle close brackets correctly in scan + # sets, so we specify it separately. + case "$lastarg" in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*) + lastarg="\"$lastarg\"" + ;; + esac + + # Add the previous argument to base_compile. + if test -z "$base_compile"; then + base_compile="$lastarg" + else + base_compile="$base_compile $lastarg" + fi + done + + # Get the name of the library object. + libobj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%'` + + # Recognize several different file suffixes. + xform='[cCFSfms]' + case "$libobj" in + *.ada) xform=ada ;; + *.adb) xform=adb ;; + *.ads) xform=ads ;; + *.asm) xform=asm ;; + *.c++) xform=c++ ;; + *.cc) xform=cc ;; + *.cpp) xform=cpp ;; + *.cxx) xform=cxx ;; + *.f90) xform=f90 ;; + *.for) xform=for ;; + esac + + libobj=`$echo "X$libobj" | $Xsed -e "s/\.$xform$/.lo/"` + + case "$libobj" in + *.lo) obj=`$echo "X$libobj" | $Xsed -e 's/\.lo$/.o/'` ;; + *) + $echo "$modename: cannot determine name of library object from \`$srcfile'" 1>&2 + exit 1 + ;; + esac + + if test -z "$base_compile"; then + $echo "$modename: you must specify a compilation command" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + # Delete any leftover library objects. + if test "$build_old_libs" = yes; then + $run $rm $obj $libobj + trap "$run $rm $obj $libobj; exit 1" 1 2 15 + else + $run $rm $libobj + trap "$run $rm $libobj; exit 1" 1 2 15 + fi + + # Only build a PIC object if we are building libtool libraries. + if test "$build_libtool_libs" = yes; then + # Without this assignment, base_compile gets emptied. + fbsd_hideous_sh_bug=$base_compile + + # All platforms use -DPIC, to notify preprocessed assembler code. + $show "$base_compile$pic_flag -DPIC $srcfile" + if $run eval "$base_compile\$pic_flag -DPIC \$srcfile"; then : + else + test -n "$obj" && $run $rm $obj + exit 1 + fi + + # If we have no pic_flag, then copy the object into place and finish. + if test -z "$pic_flag"; then + $show "$LN_S $obj $libobj" + $run $LN_S $obj $libobj + exit $? + fi + + # Just move the object, then go on to compile the next one + $show "$mv $obj $libobj" + $run $mv $obj $libobj || exit 1 + + # Allow error messages only from the first compilation. + suppress_output=' >/dev/null 2>&1' + fi + + # Only build a position-dependent object if we build old libraries. + if test "$build_old_libs" = yes; then + # Suppress compiler output if we already did a PIC compilation. + $show "$base_compile $srcfile$suppress_output" + if $run eval "$base_compile \$srcfile$suppress_output"; then : + else + $run $rm $obj $libobj + exit 1 + fi + fi + + # Create an invalid libtool object if no PIC, so that we do not + # accidentally link it into a program. + if test "$build_libtool_libs" != yes; then + $show "echo timestamp > $libobj" + $run eval "echo timestamp > \$libobj" || exit $? + fi + + exit 0 + ;; + + # libtool link mode + link) + modename="$modename: link" + CC="$nonopt" + allow_undefined=yes + compile_command="$CC" + finalize_command="$CC" + + compile_shlibpath= + finalize_shlibpath= + deplibs= + dlfiles= + dlprefiles= + export_dynamic=no + hardcode_libdirs= + libobjs= + link_against_libtool_libs= + ltlibs= + objs= + prev= + prevarg= + release= + rpath= + perm_rpath= + temp_rpath= + vinfo= + + # We need to know -static, to get the right output filenames. + for arg + do + case "$arg" in + -all-static | -static) + if test "X$arg" = "X-all-static" && test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then + $echo "$modename: warning: complete static linking is impossible in this configuration" 1>&2 + fi + build_libtool_libs=no + build_old_libs=yes + break + ;; + esac + done + + # See if our shared archives depend on static archives. + test -n "$old_archive_from_new_cmds" && build_old_libs=yes + + # Go through the arguments, transforming them on the way. + for arg + do + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case "$prev" in + output) + compile_command="$compile_command @OUTPUT@" + finalize_command="$finalize_command @OUTPUT@" + ;; + esac + + case "$prev" in + dlfiles|dlprefiles) + case "$arg" in + *.la | *.lo) ;; # We handle these cases below. + *) + dlprefiles="$dlprefiles $arg" + test "$prev" = dlfiles && dlfiles="$dlfiles $arg" + prev= + ;; + esac + ;; + release) + release="-$arg" + prev= + continue + ;; + rpath) + rpath="$rpath $arg" + prev= + continue + ;; + *) + eval "$prev=\"\$arg\"" + prev= + continue + ;; + esac + fi + + prevarg="$arg" + + case "$arg" in + -all-static) + if test -n "$link_static_flag"; then + compile_command="$compile_command $link_static_flag" + finalize_command="$finalize_command $link_static_flag" + fi + continue + ;; + + -allow-undefined) + # FIXME: remove this flag sometime in the future. + $echo "$modename: \`-allow-undefined' is deprecated because it is the default" 1>&2 + continue + ;; + + -dlopen) + prev=dlfiles + continue + ;; + + -dlpreopen) + prev=dlprefiles + continue + ;; + + -export-dynamic) + if test "$export_dynamic" != yes; then + export_dynamic=yes + if test -n "$export_dynamic_flag_spec"; then + eval arg=\"$export_dynamic_flag_spec\" + else + arg= + fi + + # Add the symbol object into the linking commands. + compile_command="$compile_command @SYMFILE@" + finalize_command="$finalize_command @SYMFILE@" + fi + ;; + + -L*) + dir=`$echo "X$arg" | $Xsed -e 's%^-L\(.*\)$%\1%'` + case "$dir" in + /* | [A-Za-z]:\\*) + # Add the corresponding hardcode_libdir_flag, if it is not identical. + ;; + *) + $echo "$modename: \`-L$dir' cannot specify a relative directory" 1>&2 + exit 1 + ;; + esac + deplibs="$deplibs $arg" + ;; + + -l*) deplibs="$deplibs $arg" ;; + + -no-undefined) + allow_undefined=no + continue + ;; + + -o) prev=output ;; + + -release) + prev=release + continue + ;; + + -rpath) + prev=rpath + continue + ;; + + -static) + # If we have no pic_flag, then this is the same as -all-static. + if test -z "$pic_flag" && test -n "$link_static_flag"; then + compile_command="$compile_command $link_static_flag" + finalize_command="$finalize_command $link_static_flag" + fi + continue + ;; + + -version-info) + prev=vinfo + continue + ;; + + # Some other compiler flag. + -* | +*) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case "$arg" in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*) + arg="\"$arg\"" + ;; + esac + ;; + + *.o | *.a) + # A standard object. + objs="$objs $arg" + ;; + + *.lo) + # A library object. + if test "$prev" = dlfiles; then + dlfiles="$dlfiles $arg" + if test "$build_libtool_libs" = yes; then + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + if test "$prev" = dlprefiles; then + # Preload the old-style object. + dlprefiles="$dlprefiles "`$echo "X$arg" | $Xsed -e 's/\.lo$/\.o/'` + prev= + fi + libobjs="$libobjs $arg" + ;; + + *.la) + # A libtool-controlled library. + + dlname= + libdir= + library_names= + old_library= + + # Check to see that this really is a libtool archive. + if (sed -e '2q' $arg | egrep '^# Generated by ltmain\.sh') >/dev/null 2>&1; then : + else + $echo "$modename: \`$arg' is not a valid libtool archive" 1>&2 + exit 1 + fi + + # If there is no directory component, then add one. + case "$arg" in + */* | *\\*) . $arg ;; + *) . ./$arg ;; + esac + + if test -z "$libdir"; then + $echo "$modename: \`$arg' contains no -rpath information" 1>&2 + exit 1 + fi + + # Get the name of the library we link against. + linklib= + for l in $old_library $library_names; do + linklib="$l" + done + + if test -z "$linklib"; then + $echo "$modename: cannot find name of link library for \`$arg'" 1>&2 + exit 1 + fi + + # Find the relevant object directory and library name. + name=`$echo "X$arg" | $Xsed -e 's%^.*/%%' -e 's/\.la$//' -e 's/^lib//'` + dir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` + if test "X$dir" = "X$arg"; then + dir="$objdir" + else + dir="$dir/$objdir" + fi + + # This library was specified with -dlopen. + if test "$prev" = dlfiles; then + dlfiles="$dlfiles $arg" + if test -z "$dlname"; then + # If there is no dlname, we need to preload. + prev=dlprefiles + else + # We should not create a dependency on this library, but we + # may need any libraries it requires. + compile_command="$compile_command$dependency_libs" + finalize_command="$finalize_command$dependency_libs" + prev= + continue + fi + fi + + # The library was specified with -dlpreopen. + if test "$prev" = dlprefiles; then + # Prefer using a static library (so that no silly _DYNAMIC symbols + # are required to link). + if test -n "$old_library"; then + dlprefiles="$dlprefiles $dir/$old_library" + else + dlprefiles="$dlprefiles $dir/$linklib" + fi + prev= + fi + + if test "$build_libtool_libs" = yes && test -n "$library_names"; then + link_against_libtool_libs="$link_against_libtool_libs $arg" + if test -n "$shlibpath_var"; then + # Make sure the rpath contains only unique directories. + case "$temp_rpath " in + *" $dir "*) ;; + *) temp_rpath="$temp_rpath $dir" ;; + esac + fi + + # This is the magic to use -rpath. + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + # Put the magic libdir with the hardcode flag. + hardcode_libdirs="$libdir" + libdir="@HARDCODE_LIBDIRS@" + else + # Just accumulate the unique libdirs. + case "$hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator" in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + libdir= + fi + fi + + if test -n "$libdir"; then + eval flag=\"$hardcode_libdir_flag_spec\" + + compile_command="$compile_command $flag" + finalize_command="$finalize_command $flag" + fi + elif test -n "$runpath_var"; then + # Do the same for the permanent run path. + case "$perm_rpath " in + *" $libdir "*) ;; + *) perm_rpath="$perm_rpath $libdir" ;; + esac + fi + + + case "$hardcode_action" in + immediate) + if test "$hardcode_direct" = no; then + compile_command="$compile_command $dir/$linklib" + elif test "$hardcode_minus_L" = no; then + compile_command="$compile_command -L$dir -l$name" + elif test "$hardcode_shlibpath_var" = no; then + compile_shlibpath="$compile_shlibpath$dir:" + compile_command="$compile_command -l$name" + fi + ;; + + relink) + # We need an absolute path. + case "$dir" in + /* | [A-Za-z]:\\*) ;; + *) + absdir=`cd "$dir" && pwd` + if test -z "$absdir"; then + $echo "$modename: cannot determine absolute directory name of \`$dir'" 1>&2 + exit 1 + fi + dir="$absdir" + ;; + esac + + if test "$hardcode_direct" = yes; then + compile_command="$compile_command $dir/$linklib" + elif test "$hardcode_minus_L" = yes; then + compile_command="$compile_command -L$dir -l$name" + elif test "$hardcode_shlibpath_var" = yes; then + compile_shlibpath="$compile_shlibpath$dir:" + compile_command="$compile_command -l$name" + fi + ;; + + *) + $echo "$modename: \`$hardcode_action' is an unknown hardcode action" 1>&2 + exit 1 + ;; + esac + + # Finalize command for both is simple: just hardcode it. + if test "$hardcode_direct" = yes; then + finalize_command="$finalize_command $libdir/$linklib" + elif test "$hardcode_minus_L" = yes; then + finalize_command="$finalize_command -L$libdir -l$name" + elif test "$hardcode_shlibpath_var" = yes; then + finalize_shlibpath="$finalize_shlibpath$libdir:" + finalize_command="$finalize_command -l$name" + else + # We cannot seem to hardcode it, guess we'll fake it. + finalize_command="$finalize_command -L$libdir -l$name" + fi + else + # Transform directly to old archives if we don't build new libraries. + if test -n "$pic_flag" && test -z "$old_library"; then + $echo "$modename: cannot find static library for \`$arg'" 1>&2 + exit 1 + fi + + # Here we assume that one of hardcode_direct or hardcode_minus_L + # is not unsupported. This is valid on all known static and + # shared platforms. + if test "$hardcode_direct" != unsupported; then + test -n "$old_library" && linklib="$old_library" + compile_command="$compile_command $dir/$linklib" + finalize_command="$finalize_command $dir/$linklib" + else + compile_command="$compile_command -L$dir -l$name" + finalize_command="$finalize_command -L$dir -l$name" + fi + fi + + # Add in any libraries that this one depends upon. + compile_command="$compile_command$dependency_libs" + finalize_command="$finalize_command$dependency_libs" + continue + ;; + + # Some other compiler argument. + *) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case "$arg" in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*) + arg="\"$arg\"" + ;; + esac + ;; + esac + + # Now actually substitute the argument into the commands. + if test -n "$arg"; then + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + fi + done + + if test -n "$prev"; then + $echo "$modename: the \`$prevarg' option requires an argument" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + if test -n "$vinfo" && test -n "$release"; then + $echo "$modename: you cannot specify both \`-version-info' and \`-release'" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + oldlib= + oldobjs= + case "$output" in + "") + $echo "$modename: you must specify an output file" 1>&2 + $echo "$help" 1>&2 + exit 1 + ;; + + */* | *\\*) + $echo "$modename: output file \`$output' must have no directory components" 1>&2 + exit 1 + ;; + + *.a) + # Now set the variables for building old libraries. + build_libtool_libs=no + build_old_libs=yes + oldlib="$output" + $show "$rm $oldlib" + $run $rm $oldlib + ;; + + *.la) + # Make sure we only generate libraries of the form `libNAME.la'. + case "$output" in + lib*) ;; + *) + $echo "$modename: libtool library \`$arg' must begin with \`lib'" 1>&2 + $echo "$help" 1>&2 + exit 1 + ;; + esac + + name=`$echo "X$output" | $Xsed -e 's/\.la$//' -e 's/^lib//'` + eval libname=\"$libname_spec\" + + # All the library-specific variables (install_libdir is set above). + library_names= + old_library= + dlname= + current=0 + revision=0 + age=0 + + if test -n "$objs"; then + $echo "$modename: cannot build libtool library \`$output' from non-libtool objects:$objs" 2>&1 + exit 1 + fi + + # How the heck are we supposed to write a wrapper for a shared library? + if test -n "$link_against_libtool_libs"; then + $echo "$modename: libtool library \`$output' may not depend on uninstalled libraries:$link_against_libtool_libs" 1>&2 + exit 1 + fi + + if test -n "$dlfiles$dlprefiles"; then + $echo "$modename: warning: \`-dlopen' is ignored while creating libtool libraries" 1>&2 + # Nullify the symbol file. + compile_command=`$echo "X$compile_command" | $Xsed -e "s% @SYMFILE@%%"` + finalize_command=`$echo "X$finalize_command" | $Xsed -e "s% @SYMFILE@%%"` + fi + + if test -z "$rpath"; then + $echo "$modename: you must specify an installation directory with \`-rpath'" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + set dummy $rpath + if test $# -gt 2; then + $echo "$modename: warning: ignoring multiple \`-rpath's for a libtool library" 1>&2 + fi + install_libdir="$2" + + # Parse the version information argument. + IFS="${IFS= }"; save_ifs="$IFS"; IFS=':' + set dummy $vinfo + IFS="$save_ifs" + + if test -n "$5"; then + $echo "$modename: too many parameters to \`-version-info'" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + test -n "$2" && current="$2" + test -n "$3" && revision="$3" + test -n "$4" && age="$4" + + # Check that each of the things are valid numbers. + case "$current" in + 0 | [1-9] | [1-9][0-9]*) ;; + *) + $echo "$modename: CURRENT \`$current' is not a nonnegative integer" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit 1 + ;; + esac + + case "$revision" in + 0 | [1-9] | [1-9][0-9]*) ;; + *) + $echo "$modename: REVISION \`$revision' is not a nonnegative integer" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit 1 + ;; + esac + + case "$age" in + 0 | [1-9] | [1-9][0-9]*) ;; + *) + $echo "$modename: AGE \`$age' is not a nonnegative integer" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit 1 + ;; + esac + + if test $age -gt $current; then + $echo "$modename: AGE \`$age' is greater than the current interface number \`$current'" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit 1 + fi + + # Calculate the version variables. + version_vars="version_type current age revision" + case "$version_type" in + none) ;; + + linux) + version_vars="$version_vars major versuffix" + major=`expr $current - $age` + versuffix="$major.$age.$revision" + ;; + + osf) + version_vars="$version_vars versuffix verstring" + major=`expr $current - $age` + versuffix="$current.$age.$revision" + verstring="$versuffix" + + # Add in all the interfaces that we are compatible with. + loop=$age + while test $loop != 0; do + iface=`expr $current - $loop` + loop=`expr $loop - 1` + verstring="$verstring:${iface}.0" + done + + # Make executables depend on our current version. + verstring="$verstring:${current}.0" + ;; + + sunos) + version_vars="$version_vars major versuffix" + major="$current" + versuffix="$current.$revision" + ;; + + *) + $echo "$modename: unknown library version type \`$version_type'" 1>&2 + echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 + exit 1 + ;; + esac + + # Create the output directory, or remove our outputs if we need to. + if test -d $objdir; then + $show "$rm $objdir/$output $objdir/$libname.* $objdir/${libname}${release}.*" + $run $rm $objdir/$output $objdir/$libname.* $objdir/${libname}${release}.* + else + $show "$mkdir $objdir" + $run $mkdir $objdir + status=$? + if test $status -eq 0 || test -d $objdir; then : + else + exit $status + fi + fi + + # Check to see if the archive will have undefined symbols. + if test "$allow_undefined" = yes; then + if test "$allow_undefined_flag" = unsupported; then + $echo "$modename: warning: undefined symbols not allowed in $host shared libraries" 1>&2 + build_libtool_libs=no + build_old_libs=yes + fi + else + # Don't allow undefined symbols. + allow_undefined_flag="$no_undefined_flag" + fi + + # Add libc to deplibs on all systems. + dependency_libs="$deplibs" + deplibs="$deplibs -lc" + + if test "$build_libtool_libs" = yes; then + # Get the real and link names of the library. + eval library_names=\"$library_names_spec\" + set dummy $library_names + realname="$2" + shift; shift + + if test -n "$soname_spec"; then + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + + lib="$objdir/$realname" + for link + do + linknames="$linknames $link" + done + + # Use standard objects if they are PIC. + test -z "$pic_flag" && libobjs=`$echo "X$libobjs " | $Xsed -e 's/\.lo /.o /g' -e 's/ $//g'` + + # Do each of the archive commands. + eval cmds=\"$archive_cmds\" + IFS="${IFS= }"; save_ifs="$IFS"; IFS=';' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + + # Create links to the real library. + for linkname in $linknames; do + $show "(cd $objdir && $LN_S $realname $linkname)" + $run eval '(cd $objdir && $LN_S $realname $linkname)' || exit $? + done + + # If -export-dynamic was specified, set the dlname. + if test "$export_dynamic" = yes; then + # On all known operating systems, these are identical. + dlname="$soname" + fi + fi + + # Now set the variables for building old libraries. + oldlib="$objdir/$libname.a" + ;; + + *.lo | *.o) + if test -n "$link_against_libtool_libs"; then + $echo "$modename: error: cannot link libtool libraries into reloadable objects" 1>&2 + exit 1 + fi + + if test -n "$deplibs"; then + $echo "$modename: warning: \`-l' and \`-L' are ignored while creating objects" 1>&2 + fi + + if test -n "$dlfiles$dlprefiles"; then + $echo "$modename: warning: \`-dlopen' is ignored while creating objects" 1>&2 + # Nullify the symbol file. + compile_command=`$echo "X$compile_command" | $Xsed -e "s% @SYMFILE@%%"` + finalize_command=`$echo "X$finalize_command" | $Xsed -e "s% @SYMFILE@%%"` + fi + + if test -n "$rpath"; then + $echo "$modename: warning: \`-rpath' is ignored while creating objects" 1>&2 + fi + + if test -n "$vinfo"; then + $echo "$modename: warning: \`-version-info' is ignored while creating objects" 1>&2 + fi + + if test -n "$release"; then + $echo "$modename: warning: \`-release' is ignored while creating objects" 1>&2 + fi + + case "$output" in + *.lo) + if test -n "$objs"; then + $echo "$modename: cannot build library object \`$output' from non-libtool objects" 1>&2 + exit 1 + fi + libobj="$output" + obj=`$echo "X$output" | $Xsed -e 's/\.lo$/.o/'` + ;; + *) + libobj= + obj="$output" + ;; + esac + + # Delete the old objects. + $run $rm $obj $libobj + + # Create the old-style object. + reload_objs="$objs"`$echo "X$libobjs " | $Xsed -e 's/[^ ]*\.a //g' -e 's/\.lo /.o /g' -e 's/ $//g'` + + output="$obj" + eval cmds=\"$reload_cmds\" + IFS="${IFS= }"; save_ifs="$IFS"; IFS=';' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + + # Exit if we aren't doing a library object file. + test -z "$libobj" && exit 0 + + if test "$build_libtool_libs" != yes; then + # Create an invalid libtool object if no PIC, so that we don't + # accidentally link it into a program. + $show "echo timestamp > $libobj" + $run eval "echo timestamp > $libobj" || exit $? + exit 0 + fi + + if test -n "$pic_flag"; then + # Only do commands if we really have different PIC objects. + reload_objs="$libobjs" + output="$libobj" + eval cmds=\"$reload_cmds\" + IFS="${IFS= }"; save_ifs="$IFS"; IFS=';' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + else + # Just create a symlink. + $show "$LN_S $obj $libobj" + $run $LN_S $obj $libobj || exit 1 + fi + + exit 0 + ;; + + *) + if test -n "$vinfo"; then + $echo "$modename: warning: \`-version-info' is ignored while linking programs" 1>&2 + fi + + if test -n "$release"; then + $echo "$modename: warning: \`-release' is ignored while creating objects" 1>&2 + fi + + if test -n "$rpath"; then + # If the user specified any rpath flags, then add them. + for libdir in $rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + # Put the magic libdir with the hardcode flag. + hardcode_libdirs="$libdir" + libdir="@HARDCODE_LIBDIRS@" + else + # Just accumulate the unique libdirs. + case "$hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator" in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + libdir= + fi + fi + + if test -n "$libdir"; then + eval flag=\"$hardcode_libdir_flag_spec\" + + compile_command="$compile_command $flag" + finalize_command="$finalize_command $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) perm_rpath="$perm_rpath $libdir" ;; + esac + fi + done + fi + + # Substitute the hardcoded libdirs into the compile commands. + if test -n "$hardcode_libdir_separator"; then + compile_command=`$echo "X$compile_command" | $Xsed -e "s%@HARDCODE_LIBDIRS@%$hardcode_libdirs%g"` + finalize_command=`$echo "X$finalize_command" | $Xsed -e "s%@HARDCODE_LIBDIRS@%$hardcode_libdirs%g"` + fi + + if test -n "$libobjs" && test "$build_old_libs" = yes; then + # Transform all the library objects into standard objects. + compile_command=`$echo "X$compile_command " | $Xsed -e 's/\.lo /.o /g' -e 's/ $//'` + finalize_command=`$echo "X$finalize_command " | $Xsed -e 's/\.lo /.o /g' -e 's/ $//'` + fi + + if test "$export_dynamic" = yes && test -n "$NM" && test -n "$global_symbol_pipe"; then + dlsyms="${output}S.c" + else + dlsyms= + fi + + if test -n "$dlsyms"; then + # Add our own program objects to the preloaded list. + dlprefiles=`$echo "X$objs$dlprefiles " | $Xsed -e 's/\.lo /.o /g' -e 's/ $//'` + + # Discover the nlist of each of the dlfiles. + nlist="$objdir/${output}.nm" + + if test -d $objdir; then + $show "$rm $nlist ${nlist}T" + $run $rm "$nlist" "${nlist}T" + else + $show "$mkdir $objdir" + $run $mkdir $objdir + status=$? + if test $status -eq 0 || test -d $objdir; then : + else + exit $status + fi + fi + + for arg in $dlprefiles; do + $show "extracting global C symbols from \`$arg'" + $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'" + done + + # Parse the name list into a source file. + $show "creating $objdir/$dlsyms" + if test -z "$run"; then + # Make sure we at least have an empty file. + test -f "$nlist" || : > "$nlist" + + # Try sorting and uniquifying the output. + if sort "$nlist" | uniq > "$nlist"T; then + mv -f "$nlist"T "$nlist" + wcout=`wc "$nlist" 2>/dev/null` + count=`echo "X$wcout" | $Xsed -e 's/^[ ]*\([0-9][0-9]*\).*$/\1/'` + (test "$count" -ge 0) 2>/dev/null || count=-1 + else + $rm "$nlist"T + count=-1 + fi + + case "$dlsyms" in + "") ;; + *.c) + $echo > "$objdir/$dlsyms" "\ +/* $dlsyms - symbol resolution table for \`$output' dlsym emulation. */ +/* Generated by $PROGRAM - GNU $PACKAGE $VERSION */ + +#ifdef __cplusplus +extern \"C\" { +#endif + +/* Prevent the only kind of declaration conflicts we can make. */ +#define dld_preloaded_symbol_count some_other_symbol +#define dld_preloaded_symbols some_other_symbol + +/* External symbol declarations for the compiler. */\ +" + + if test -f "$nlist"; then + sed -e 's/^.* \(.*\)$/extern char \1;/' < "$nlist" >> "$objdir/$dlsyms" + else + echo '/* NONE */' >> "$objdir/$dlsyms" + fi + + $echo >> "$objdir/$dlsyms" "\ + +#undef dld_preloaded_symbol_count +#undef dld_preloaded_symbols + +#if defined (__STDC__) && __STDC__ +# define __ptr_t void * +#else +# define __ptr_t char * +#endif + +/* The number of symbols in dld_preloaded_symbols, -1 if unsorted. */ +int dld_preloaded_symbol_count = $count; + +/* The mapping between symbol names and symbols. */ +struct { + char *name; + __ptr_t address; +} +dld_preloaded_symbols[] = +{\ +" + + if test -f "$nlist"; then + sed 's/^\(.*\) \(.*\)$/ {"\1", (__ptr_t) \&\2},/' < "$nlist" >> "$objdir/$dlsyms" + fi + + $echo >> "$objdir/$dlsyms" "\ + {0, (__ptr_t) 0} +}; + +#ifdef __cplusplus +} +#endif\ +" + ;; + + *) + $echo "$modename: unknown suffix for \`$dlsyms'" 1>&2 + exit 1 + ;; + esac + fi + + # Now compile the dynamic symbol file. + $show "(cd $objdir && $CC -c$no_builtin_flag \"$dlsyms\")" + $run eval '(cd $objdir && $CC -c$no_builtin_flag "$dlsyms")' || exit $? + + # Transform the symbol file into the correct name. + compile_command=`$echo "X$compile_command" | $Xsed -e "s%@SYMFILE@%$objdir/${output}S.o%"` + finalize_command=`$echo "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$objdir/${output}S.o%"` + elif test "$export_dynamic" != yes; then + test -n "$dlfiles$dlprefiles" && $echo "$modename: warning: \`-dlopen' and \`-dlpreopen' are ignored without \`-export-dynamic'" 1>&2 + else + # We keep going just in case the user didn't refer to + # dld_preloaded_symbols. The linker will fail if global_symbol_pipe + # really was required. + $echo "$modename: not configured to extract global symbols from dlpreopened files" 1>&2 + + # Nullify the symbol file. + compile_command=`$echo "X$compile_command" | $Xsed -e "s% @SYMFILE@%%"` + finalize_command=`$echo "X$finalize_command" | $Xsed -e "s% @SYMFILE@%%"` + fi + + if test -z "$link_against_libtool_libs" || test "$build_libtool_libs" != yes; then + # Replace the output file specification. + compile_command=`$echo "X$compile_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` + finalize_command=`$echo "X$finalize_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` + + # We have no uninstalled library dependencies, so finalize right now. + $show "$compile_command" + $run eval "$compile_command" + exit $? + fi + + # Replace the output file specification. + compile_command=`$echo "X$compile_command" | $Xsed -e 's%@OUTPUT@%'"$objdir/$output"'%g'` + finalize_command=`$echo "X$finalize_command" | $Xsed -e 's%@OUTPUT@%'"$objdir/$output"'T%g'` + + # Create the binary in the object directory, then wrap it. + if test -d $objdir; then : + else + $show "$mkdir $objdir" + $run $mkdir $objdir + status=$? + if test $status -eq 0 || test -d $objdir; then : + else + exit $status + fi + fi + + if test -n "$shlibpath_var"; then + # We should set the shlibpath_var + rpath= + for dir in $temp_rpath; do + case "$dir" in + /* | [A-Za-z]:\\*) + # Absolute path. + rpath="$rpath$dir:" + ;; + *) + # Relative path: add a thisdir entry. + rpath="$rpath\$thisdir/$dir:" + ;; + esac + done + temp_rpath="$rpath" + fi + + # Delete the old output file. + $run $rm $output + + if test -n "$compile_shlibpath"; then + compile_command="$shlibpath_var=\"$compile_shlibpath\$$shlibpath_var\" $compile_command" + fi + if test -n "$finalize_shlibpath"; then + finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" + fi + + if test -n "$runpath_var" && test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + rpath="$rpath$dir:" + done + compile_command="$runpath_var=\"$rpath\$$runpath_var\" $compile_command" + finalize_command="$runpath_var=\"$rpath\$$runpath_var\" $finalize_command" + fi + + case "$hardcode_action" in + relink) + # AGH! Flame the AIX and HP-UX people for me, will ya? + $echo "$modename: warning: using a buggy system linker" 1>&2 + $echo "$modename: relinking will be required before \`$output' can be installed" 1>&2 + ;; + esac + + $show "$compile_command" + $run eval "$compile_command" || exit $? + + # Now create the wrapper script. + $show "creating $output" + + # Quote the finalize command for shipping. + finalize_command=`$echo "X$finalize_command" | $Xsed -e "$sed_quote_subst"` + + # Quote $echo for shipping. + qecho=`$echo "X$echo" | $Xsed -e "$sed_quote_subst"` + + # Only actually do things if our run command is non-null. + if test -z "$run"; then + $rm $output + trap "$rm $output; exit 1" 1 2 15 + + $echo > $output "\ +#! /bin/sh + +# $output - temporary wrapper script for $objdir/$output +# Generated by ltmain.sh - GNU $PACKAGE $VERSION +# +# The $output program cannot be directly executed until all the libtool +# libraries that it depends on are installed. +# +# This wrapper script should never be moved out of \``pwd`'. +# If it is, it will not operate correctly. + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +Xsed='sed -e s/^X//' +sed_quote_subst='$sed_quote_subst' + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +if test \"\${CDPATH+set}\" = set; then CDPATH=; export CDPATH; fi + +# This environment variable determines our operation mode. +if test \"\$libtool_install_magic\" = \"$magic\"; then + # install mode needs the following variables: + link_against_libtool_libs='$link_against_libtool_libs' + finalize_command=\"$finalize_command\" +else + # When we are sourced in execute mode, \$file and \$echo are already set. + if test \"\$libtool_execute_magic\" = \"$magic\"; then : + else + echo=\"$qecho\" + file=\"\$0\" + fi\ +" + $echo >> $output "\ + + # Find the directory that this script lives in. + thisdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*$%%'\` + test \"x\$thisdir\" = \"x\$file\" && thisdir=. + + # Follow symbolic links until we get to the real thisdir. + file=\`ls -ld \"\$file\" | sed -n 's/.*-> //p'\` + while test -n \"\$file\"; do + destdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*\$%%'\` + + # If there was a directory component, then change thisdir. + if test \"x\$destdir\" != \"x\$file\"; then + case \"\$destdir\" in + /* | [A-Za-z]:\\*) thisdir=\"\$destdir\" ;; + *) thisdir=\"\$thisdir/\$destdir\" ;; + esac + fi + + file=\`\$echo \"X\$file\" | \$Xsed -e 's%^.*/%%'\` + file=\`ls -ld \"\$thisdir/\$file\" | sed -n 's/.*-> //p'\` + done + + # Try to get the absolute directory name. + absdir=\`cd \"\$thisdir\" && pwd\` + test -n \"\$absdir\" && thisdir=\"\$absdir\" + + progdir=\"\$thisdir/$objdir\" + program='$output' + + if test -f \"\$progdir/\$program\"; then" + + # Export our shlibpath_var if we have one. + if test -n "$shlibpath_var" && test -n "$temp_rpath"; then + $echo >> $output "\ + # Add our own library path to $shlibpath_var + $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" + + # Some systems cannot cope with colon-terminated $shlibpath_var + $shlibpath_var=\`\$echo \"X\$$shlibpath_var\" | \$Xsed -e 's/:*\$//'\` + + export $shlibpath_var +" + fi + + $echo >> $output "\ + if test \"\$libtool_execute_magic\" != \"$magic\"; then + # Run the actual program with our arguments. + + # Export the path to the program. + PATH=\"\$progdir:\$PATH\" + export PATH + + exec \$program \${1+\"\$@\"} + + \$echo \"\$0: cannot exec \$program \${1+\"\$@\"}\" + exit 1 + fi + else + # The program doesn't exist. + \$echo \"\$0: error: \$progdir/\$program does not exist\" 1>&2 + \$echo \"This script is just a wrapper for \$program.\" 1>&2 + echo \"See the $PACKAGE documentation for more information.\" 1>&2 + exit 1 + fi +fi\ +" + chmod +x $output + fi + exit 0 + ;; + esac + + # See if we need to build an old-fashioned archive. + if test "$build_old_libs" = "yes"; then + # Transform .lo files to .o files. + oldobjs="$objs"`$echo "X$libobjs " | $Xsed -e 's/[^ ]*\.a //g' -e 's/\.lo /.o /g' -e 's/ $//g'` + + # Do each command in the archive commands. + if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then + eval cmds=\"$old_archive_from_new_cmds\" + else + eval cmds=\"$old_archive_cmds\" + fi + IFS="${IFS= }"; save_ifs="$IFS"; IFS=';' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + fi + + # Now create the libtool archive. + case "$output" in + *.la) + old_library= + test "$build_old_libs" = yes && old_library="$libname.a" + + $show "creating $output" + + # Only create the output if not a dry run. + if test -z "$run"; then + $echo > $output "\ +# $output - a libtool library file +# Generated by ltmain.sh - GNU $PACKAGE $VERSION + +# The name that we can dlopen(3). +dlname='$dlname' + +# Names of this library. +library_names='$library_names' + +# The name of the static archive. +old_library='$old_library' + +# Libraries that this one depends upon. +dependency_libs='$dependency_libs' + +# Version information for $libname. +current=$current +age=$age +revision=$revision + +# Directory that this library needs to be installed in: +libdir='$install_libdir'\ +" + fi + + # Do a symbolic link so that the libtool archive can be found in + # LD_LIBRARY_PATH before the program is installed. + $show "(cd $objdir && $LN_S ../$output $output)" + $run eval "(cd $objdir && $LN_S ../$output $output)" || exit 1 + ;; + esac + exit 0 + ;; + + # libtool install mode + install) + modename="$modename: install" + + # There may be an optional /bin/sh argument at the beginning of + # install_prog (especially on Windows NT). + if test "$nonopt" = "$SHELL"; then + # Aesthetically quote it. + arg=`$echo "X$nonopt" | $Xsed -e "$sed_quote_subst"` + case "$arg" in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*) + arg="\"$arg\"" + ;; + esac + install_prog="$arg " + arg="$1" + shift + else + install_prog= + arg="$nonopt" + fi + + # The real first argument should be the name of the installation program. + # Aesthetically quote it. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case "$arg" in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*) + arg="\"$arg\"" + ;; + esac + install_prog="$install_prog$arg" + + # We need to accept at least all the BSD install flags. + dest= + files= + opts= + prev= + install_type= + isdir= + stripme= + for arg + do + if test -n "$dest"; then + files="$files $dest" + dest="$arg" + continue + fi + + case "$arg" in + -d) isdir=yes ;; + -f) prev="-f" ;; + -g) prev="-g" ;; + -m) prev="-m" ;; + -o) prev="-o" ;; + -s) + stripme=" -s" + continue + ;; + -*) ;; + + *) + # If the previous option needed an argument, then skip it. + if test -n "$prev"; then + prev= + else + dest="$arg" + continue + fi + ;; + esac + + # Aesthetically quote the argument. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case "$arg" in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*) + arg="\"$arg\"" + ;; + esac + install_prog="$install_prog $arg" + done + + if test -z "$install_prog"; then + $echo "$modename: you must specify an install program" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + if test -n "$prev"; then + $echo "$modename: the \`$prev' option requires an argument" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + if test -z "$files"; then + if test -z "$dest"; then + $echo "$modename: no file or destination specified" 1>&2 + else + $echo "$modename: you must specify a destination" 1>&2 + fi + $echo "$help" 1>&2 + exit 1 + fi + + # Strip any trailing slash from the destination. + dest=`$echo "X$dest" | $Xsed -e 's%/$%%'` + + # Check to see that the destination is a directory. + test -d "$dest" && isdir=yes + if test -n "$isdir"; then + destdir="$dest" + destname= + else + destdir=`$echo "X$dest" | $Xsed -e 's%/[^/]*$%%'` + test "X$destdir" = "X$dest" && destdir=. + destname=`$echo "X$dest" | $Xsed -e 's%^.*/%%'` + + # Not a directory, so check to see that there is only one file specified. + set dummy $files + if test $# -gt 2; then + $echo "$modename: \`$dest' is not a directory" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + fi + case "$destdir" in + /* | [A-Za-z]:\\*) ;; + *) + for file in $files; do + case "$file" in + *.lo) ;; + *) + $echo "$modename: \`$destdir' must be an absolute directory name" 1>&2 + $echo "$help" 1>&2 + exit 1 + ;; + esac + done + ;; + esac + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic="$magic" + + staticlibs= + future_libdirs= + current_libdirs= + for file in $files; do + + # Do each installation. + case "$file" in + *.a) + # Do the static libraries later. + staticlibs="$staticlibs $file" + ;; + + *.la) + # Check to see that this really is a libtool archive. + if (sed -e '2q' $file | egrep '^# Generated by ltmain\.sh') >/dev/null 2>&1; then : + else + $echo "$modename: \`$file' is not a valid libtool archive" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + library_names= + old_library= + # If there is no directory component, then add one. + case "$file" in + */* | *\\*) . $file ;; + *) . ./$file ;; + esac + + # Add the libdir to current_libdirs if it is the destination. + if test "X$destdir" = "X$libdir"; then + case "$current_libdirs " in + *" $libdir "*) ;; + *) current_libdirs="$current_libdirs $libdir" ;; + esac + else + # Note the libdir as a future libdir. + case "$future_libdirs " in + *" $libdir "*) ;; + *) future_libdirs="$future_libdirs $libdir" ;; + esac + fi + + dir="`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'`/" + test "X$dir" = "X$file/" && dir= + dir="$dir$objdir" + + # See the names of the shared library. + set dummy $library_names + if test -n "$2"; then + realname="$2" + shift + shift + + # Install the shared library and build the symlinks. + $show "$install_prog $dir/$realname $destdir/$realname" + $run eval "$install_prog $dir/$realname $destdir/$realname" || exit $? + test "X$dlname" = "X$realname" && dlname= + + if test $# -gt 0; then + # Delete the old symlinks. + rmcmd="$rm" + for linkname + do + rmcmd="$rmcmd $destdir/$linkname" + done + $show "$rmcmd" + $run $rmcmd + + # ... and create new ones. + for linkname + do + test "X$dlname" = "X$linkname" && dlname= + $show "(cd $destdir && $LN_S $realname $linkname)" + $run eval "(cd $destdir && $LN_S $realname $linkname)" + done + fi + + if test -n "$dlname"; then + # Install the dynamically-loadable library. + $show "$install_prog $dir/$dlname $destdir/$dlname" + $run eval "$install_prog $dir/$dlname $destdir/$dlname" || exit $? + fi + + # Do each command in the postinstall commands. + lib="$destdir/$realname" + eval cmds=\"$postinstall_cmds\" + IFS="${IFS= }"; save_ifs="$IFS"; IFS=';' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + fi + + # Install the pseudo-library for information purposes. + name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + $show "$install_prog $file $destdir/$name" + $run eval "$install_prog $file $destdir/$name" || exit $? + + # Maybe install the static library, too. + test -n "$old_library" && staticlibs="$staticlibs $dir/$old_library" + ;; + + *.lo) + # Install (i.e. copy) a libtool object. + + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile="$destdir/$destname" + else + destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + destfile="$destdir/$destfile" + fi + + # Deduce the name of the destination old-style object file. + case "$destfile" in + *.lo) + staticdest=`$echo "X$destfile" | $Xsed -e 's/\.lo$/\.o/'` + ;; + *.o) + staticdest="$destfile" + destfile= + ;; + *) + $echo "$modename: cannot copy a libtool object to \`$destfile'" 1>&2 + $echo "$help" 1>&2 + exit 1 + ;; + esac + + # Install the libtool object if requested. + if test -n "$destfile"; then + $show "$install_prog $file $destfile" + $run eval "$install_prog $file $destfile" || exit $? + fi + + # Install the old object if enabled. + if test "$build_old_libs" = yes; then + # Deduce the name of the old-style object file. + staticobj=`$echo "X$file" | $Xsed -e 's/\.lo$/\.o/'` + + $show "$install_prog $staticobj $staticdest" + $run eval "$install_prog \$staticobj \$staticdest" || exit $? + fi + exit 0 + ;; + + *) + # Do a test to see if this is really a libtool program. + if (sed -e '4q' $file | egrep '^# Generated by ltmain\.sh') >/dev/null 2>&1; then + link_against_libtool_libs= + finalize_command= + + # If there is no directory component, then add one. + case "$file" in + */* | *\\*) . $file ;; + *) . ./$file ;; + esac + + # Check the variables that should have been set. + if test -z "$link_against_libtool_libs" || test -z "$finalize_command"; then + $echo "$modename: invalid libtool wrapper script \`$file'" 1>&2 + exit 1 + fi + + finalize=yes + for lib in $link_against_libtool_libs; do + # Check to see that each library is installed. + libdir= + if test -f "$lib"; then + # If there is no directory component, then add one. + case "$lib" in + */* | *\\*) . $lib ;; + *) . ./$lib ;; + esac + fi + libfile="$libdir/`$echo "X$lib" | $Xsed -e 's%^.*/%%g'`" + if test -z "$libdir"; then + $echo "$modename: warning: \`$lib' contains no -rpath information" 1>&2 + elif test -f "$libfile"; then : + else + $echo "$modename: warning: \`$lib' has not been installed in \`$libdir'" 1>&2 + finalize=no + fi + done + + if test "$hardcode_action" = relink; then + if test "$finalize" = yes; then + $echo "$modename: warning: relinking \`$file' on behalf of your buggy system linker" 1>&2 + $show "$finalize_command" + if $run eval "$finalize_command"; then : + else + $echo "$modename: error: relink \`$file' with the above command before installing it" 1>&2 + continue + fi + file="$objdir/$file"T + else + $echo "$modename: warning: cannot relink \`$file' on behalf of your buggy system linker" 1>&2 + fi + else + # Install the binary that we compiled earlier. + file=`$echo "X$file" | $Xsed -e "s%\([^/]*\)$%$objdir/\1%"` + fi + fi + + $show "$install_prog$stripme $file $dest" + $run eval "$install_prog\$stripme \$file \$dest" || exit $? + ;; + esac + done + + for file in $staticlibs; do + name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + + # Set up the ranlib parameters. + oldlib="$destdir/$name" + + $show "$install_prog $file $oldlib" + $run eval "$install_prog \$file \$oldlib" || exit $? + + # Do each command in the postinstall commands. + eval cmds=\"$old_postinstall_cmds\" + IFS="${IFS= }"; save_ifs="$IFS"; IFS=';' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + done + + if test -n "$future_libdirs"; then + $echo "$modename: warning: remember to run \`$progname --finish$future_libdirs'" 1>&2 + fi + + if test -n "$current_libdirs"; then + # Maybe just do a dry run. + test -n "$run" && current_libdirs=" -n$current_libdirs" + exec $SHELL $0 --finish$current_libdirs + exit 1 + fi + + exit 0 + ;; + + # libtool finish mode + finish) + modename="$modename: finish" + libdirs="$nonopt" + + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + for dir + do + libdirs="$libdirs $dir" + done + + for libdir in $libdirs; do + if test -n "$finish_cmds"; then + # Do each command in the finish commands. + eval cmds=\"$finish_cmds\" + IFS="${IFS= }"; save_ifs="$IFS"; IFS=';' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" + done + IFS="$save_ifs" + fi + if test -n "$finish_eval"; then + # Do the single finish_eval. + eval cmds=\"$finish_eval\" + $run eval "$cmds" + fi + done + fi + + echo "------------------------------------------------------------------------------" + echo "Libraries have been installed in:" + for libdir in $libdirs; do + echo " $libdir" + done + echo + echo "To link against installed libraries in a given directory, LIBDIR," + echo "you must use the \`-LLIBDIR' flag during linking." + echo + echo " You will also need to do one of the following:" + if test -n "$shlibpath_var"; then + echo " - add LIBDIR to the \`$shlibpath_var' environment variable" + echo " during execution" + fi + if test -n "$runpath_var"; then + echo " - add LIBDIR to the \`$runpath_var' environment variable" + echo " during linking" + fi + if test -n "$hardcode_libdir_flag_spec"; then + libdir=LIBDIR + eval flag=\"$hardcode_libdir_flag_spec\" + + echo " - use the \`$flag' linker flag" + fi + if test -f /etc/ld.so.conf; then + echo " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'" + fi + echo + echo "See any operating system documentation about shared libraries for" + echo "more information, such as the ld(1) and ld.so(8) manual pages." + echo "------------------------------------------------------------------------------" + exit 0 + ;; + + # libtool execute mode + execute) + modename="$modename: execute" + + # The first argument is the command name. + cmd="$nonopt" + if test -z "$cmd"; then + $echo "$modename: you must specify a COMMAND" 1>&2 + $echo "$help" + exit 1 + fi + + # Handle -dlopen flags immediately. + for file in $execute_dlfiles; do + if test -f "$file"; then : + else + $echo "$modename: \`$file' is not a file" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + dir= + case "$file" in + *.la) + # Check to see that this really is a libtool archive. + if (sed -e '2q' $file | egrep '^# Generated by ltmain\.sh') >/dev/null 2>&1; then : + else + $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + # Read the libtool library. + dlname= + library_names= + + # If there is no directory component, then add one. + case "$file" in + */* | *\\*) . $file ;; + *) . ./$file ;; + esac + + # Skip this library if it cannot be dlopened. + if test -z "$dlname"; then + # Warn if it was a shared library. + test -n "$library_names" && $echo "$modename: warning: \`$file' was not linked with \`-export-dynamic'" + continue + fi + + dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` + test "X$dir" = "X$file" && dir=. + + if test -f "$dir/$objdir/$dlname"; then + dir="$dir/$objdir" + else + $echo "$modename: cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" 1>&2 + exit 1 + fi + ;; + + *.lo) + # Just add the directory containing the .lo file. + dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` + test "X$dir" = "X$file" && dir=. + ;; + + *) + $echo "$modename: warning \`-dlopen' is ignored for non-libtool libraries and objects" 1>&2 + continue + ;; + esac + + # Get the absolute pathname. + absdir=`cd "$dir" && pwd` + test -n "$absdir" && dir="$absdir" + + # Now add the directory to shlibpath_var. + if eval "test -z \"\$$shlibpath_var\""; then + eval "$shlibpath_var=\"\$dir\"" + else + eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" + fi + done + + # This variable tells wrapper scripts just to set shlibpath_var + # rather than running their programs. + libtool_execute_magic="$magic" + + # Check if any of the arguments is a wrapper script. + args= + for file + do + case "$file" in + -*) ;; + *) + # Do a test to see if this is really a libtool program. + if (sed -e '4q' $file | egrep '^# Generated by ltmain\.sh') >/dev/null 2>&1; then + # If there is no directory component, then add one. + case "$file" in + */* | *\\*) . $file ;; + *) . ./$file ;; + esac + + # Transform arg to wrapped name. + file="$progdir/$program" + fi + ;; + esac + # Quote arguments (to preserve shell metacharacters). + file=`$echo "X$file" | $Xsed -e "$sed_quote_subst"` + args="$args \"$file\"" + done + + if test -z "$run"; then + # Export the shlibpath_var. + eval "export $shlibpath_var" + + # Now actually exec the command. + eval "exec \$cmd$args" + + $echo "$modename: cannot exec \$cmd$args" + exit 1 + else + # Display what would be done. + eval "\$echo \"\$shlibpath_var=\$$shlibpath_var\"" + $echo "export $shlibpath_var" + $echo "$cmd$args" + exit 0 + fi + ;; + + # libtool uninstall mode + uninstall) + modename="$modename: uninstall" + rm="$nonopt" + files= + + for arg + do + case "$arg" in + -*) rm="$rm $arg" ;; + *) files="$files $arg" ;; + esac + done + + if test -z "$rm"; then + $echo "$modename: you must specify an RM program" 1>&2 + $echo "$help" 1>&2 + exit 1 + fi + + for file in $files; do + dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` + test "X$dir" = "X$file" && dir=. + name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + + rmfiles="$file" + + case "$name" in + *.la) + # Possibly a libtool archive, so verify it. + if (sed -e '2q' $file | egrep '^# Generated by ltmain\.sh') >/dev/null 2>&1; then + . $dir/$name + + # Delete the libtool libraries and symlinks. + for n in $library_names; do + rmfiles="$rmfiles $dir/$n" + test "X$n" = "X$dlname" && dlname= + done + test -n "$dlname" && rmfiles="$rmfiles $dir/$dlname" + test -n "$old_library" && rmfiles="$rmfiles $dir/$old_library" + + $show "$rm $rmfiles" + $run $rm $rmfiles + + if test -n "$library_names"; then + # Do each command in the postuninstall commands. + eval cmds=\"$postuninstall_cmds\" + IFS="${IFS= }"; save_ifs="$IFS"; IFS=';' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" + done + IFS="$save_ifs" + fi + + if test -n "$old_library"; then + # Do each command in the old_postuninstall commands. + eval cmds=\"$old_postuninstall_cmds\" + IFS="${IFS= }"; save_ifs="$IFS"; IFS=';' + for cmd in $cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" + done + IFS="$save_ifs" + fi + + # FIXME: should reinstall the best remaining shared library. + fi + ;; + + *.lo) + if test "$build_old_libs" = yes; then + oldobj=`$echo "X$name" | $Xsed -e 's/\.lo$/\.o/'` + rmfiles="$rmfiles $dir/$oldobj" + fi + $show "$rm $rmfiles" + $run $rm $rmfiles + ;; + + *) + $show "$rm $rmfiles" + $run $rm $rmfiles + ;; + esac + done + exit 0 + ;; + + "") + $echo "$modename: you must specify a MODE" 1>&2 + $echo "$generic_help" 1>&2 + exit 1 + ;; + esac + + $echo "$modename: invalid operation mode \`$mode'" 1>&2 + $echo "$generic_help" 1>&2 + exit 1 +fi # test -z "$show_help" + +# We need to display help for each of the modes. +case "$mode" in +"") $echo \ +"Usage: $modename [OPTION]... [MODE-ARG]... + +Provide generalized library-building support services. + +-n, --dry-run display commands without modifying any files + --features display configuration information and exit + --finish same as \`--mode=finish' + --help display this help message and exit + --mode=MODE use operation mode MODE [default=inferred from MODE-ARGS] + --quiet same as \`--silent' + --silent don't print informational messages + --version print version information + +MODE must be one of the following: + + compile compile a source file into a libtool object + execute automatically set library path, then run a program + finish complete the installation of libtool libraries + install install libraries or executables + link create a library or an executable + uninstall remove libraries from an installed directory + +MODE-ARGS vary depending on the MODE. Try \`$modename --help --mode=MODE' for +a more detailed description of MODE." + exit 0 + ;; + +compile) + $echo \ +"Usage: $modename [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE + +Compile a source file into a libtool library object. + +COMPILE-COMMAND is a command to be used in creating a \`standard' object file +from the given SOURCEFILE. + +The output file name is determined by removing the directory component from +SOURCEFILE, then substituting the C source code suffix \`.c' with the +library object suffix, \`.lo'." + ;; + +execute) + $echo \ +"Usage: $modename [OPTION]... --mode=execute COMMAND [ARGS]... + +Automatically set library path, then run a program. + +This mode accepts the following additional options: + + -dlopen FILE add the directory containing FILE to the library path + +This mode sets the library path environment variable according to \`-dlopen' +flags. + +If any of the ARGS are libtool executable wrappers, then they are translated +into their corresponding uninstalled binary, and any of their required library +directories are added to the library path. + +Then, COMMAND is executed, with ARGS as arguments." + ;; + +finish) + $echo \ +"Usage: $modename [OPTION]... --mode=finish [LIBDIR]... + +Complete the installation of libtool libraries. + +Each LIBDIR is a directory that contains libtool libraries. + +The commands that this mode executes may require superuser privileges. Use +the \`--dry-run' option if you just want to see what would be executed." + ;; + +install) + $echo \ +"Usage: $modename [OPTION]... --mode=install INSTALL-COMMAND... + +Install executables or libraries. + +INSTALL-COMMAND is the installation command. The first component should be +either the \`install' or \`cp' program. + +The rest of the components are interpreted as arguments to that command (only +BSD-compatible install options are recognized)." + ;; + +link) + $echo \ +"Usage: $modename [OPTION]... --mode=link LINK-COMMAND... + +Link object files or libraries together to form another library, or to +create an executable program. + +LINK-COMMAND is a command using the C compiler that you would use to create +a program from several object files. + +The following components of LINK-COMMAND are treated specially: + + -all-static do not do any dynamic linking at all + -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime + -dlpreopen FILE link in FILE and add its symbols to dld_preloaded_symbols + -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) + -LLIBDIR search LIBDIR for required installed libraries + -lNAME OUTPUT-FILE requires the installed library libNAME + -no-undefined declare that a library does not refer to external symbols + -o OUTPUT-FILE create OUTPUT-FILE from the specified objects + -release RELEASE specify package release information + -rpath LIBDIR the created library will eventually be installed in LIBDIR + -static do not do any dynamic linking of libtool libraries + -version-info CURRENT[:REVISION[:AGE]] + specify library version info [each variable defaults to 0] + +All other options (arguments beginning with \`-') are ignored. + +Every other argument is treated as a filename. Files ending in \`.la' are +treated as uninstalled libtool libraries, other files are standard or library +object files. + +If the OUTPUT-FILE ends in \`.la', then a libtool library is created, only +library objects (\`.lo' files) may be specified, and \`-rpath' is required. + +If OUTPUT-FILE ends in \`.a', then a standard library is created using \`ar' +and \`ranlib'. + +If OUTPUT-FILE ends in \`.lo' or \`.o', then a reloadable object file is +created, otherwise an executable program is created." + ;; + +uninstall) + $echo +"Usage: $modename [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... + +Remove libraries from an installation directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +to RM. + +If FILE is a libtool library, all the files associated with it are deleted. +Otherwise, only FILE itself is deleted using RM." + ;; + +*) + $echo "$modename: invalid operation mode \`$mode'" 1>&2 + $echo "$help" 1>&2 + exit 1 + ;; +esac + +echo +$echo "Try \`$modename --help' for more information about other modes." + +exit 0 + +# Local Variables: +# mode:shell-script +# sh-indentation:2 +# End: diff --git a/Engine/lib/ljpeg/extras/makcjpeg.st b/Engine/lib/ljpeg/extras/makcjpeg.st new file mode 100644 index 000000000..fc72c8982 --- /dev/null +++ b/Engine/lib/ljpeg/extras/makcjpeg.st @@ -0,0 +1,38 @@ +; Project file for Independent JPEG Group's software +; +; This project file is for Atari ST/STE/TT systems using Pure C or Turbo C. +; Thanks to Frank Moehle (Frank.Moehle@arbi.informatik.uni-oldenburg.de), +; Dr. B. Setzepfandt (bernd@gina.uni-muenster.de), +; and Guido Vollbeding (guivol@esc.de). +; +; To use this file, rename it to cjpeg.prj. +; If you are using Turbo C, change filenames beginning with "pc..." to "tc..." +; Read installation instructions before trying to make the program! +; +; +; * * * Output file * * * +cjpeg.ttp +; +; * * * COMPILER OPTIONS * * * +.C[-P] ; absolute calls +.C[-M] ; and no string merging, folks +.C[-w-cln] ; no "constant is long" warnings +.C[-w-par] ; no "parameter xxxx unused" +.C[-w-rch] ; no "unreachable code" +.C[-wsig] ; warn if significant digits may be lost += +; * * * * List of modules * * * * +pcstart.o +cjpeg.c (cdjpeg.h,jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jerror.h,cderror.h,jversion.h) +cdjpeg.c (cdjpeg.h,jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jerror.h,cderror.h) +rdswitch.c (cdjpeg.h,jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jerror.h,cderror.h) +rdppm.c (cdjpeg.h,jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jerror.h,cderror.h) +rdgif.c (cdjpeg.h,jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jerror.h,cderror.h) +rdtarga.c (cdjpeg.h,jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jerror.h,cderror.h) +rdbmp.c (cdjpeg.h,jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jerror.h,cderror.h) +rdrle.c (cdjpeg.h,jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jerror.h,cderror.h) +libjpeg.lib ; built by libjpeg.prj +pcfltlib.lib ; floating point library +; the float library can be omitted if you've turned off DCT_FLOAT_SUPPORTED +pcstdlib.lib ; standard library +pcextlib.lib ; extended library diff --git a/Engine/lib/ljpeg/extras/makdjpeg.st b/Engine/lib/ljpeg/extras/makdjpeg.st new file mode 100644 index 000000000..322672631 --- /dev/null +++ b/Engine/lib/ljpeg/extras/makdjpeg.st @@ -0,0 +1,38 @@ +; Project file for Independent JPEG Group's software +; +; This project file is for Atari ST/STE/TT systems using Pure C or Turbo C. +; Thanks to Frank Moehle (Frank.Moehle@arbi.informatik.uni-oldenburg.de), +; Dr. B. Setzepfandt (bernd@gina.uni-muenster.de), +; and Guido Vollbeding (guivol@esc.de). +; +; To use this file, rename it to djpeg.prj. +; If you are using Turbo C, change filenames beginning with "pc..." to "tc..." +; Read installation instructions before trying to make the program! +; +; +; * * * Output file * * * +djpeg.ttp +; +; * * * COMPILER OPTIONS * * * +.C[-P] ; absolute calls +.C[-M] ; and no string merging, folks +.C[-w-cln] ; no "constant is long" warnings +.C[-w-par] ; no "parameter xxxx unused" +.C[-w-rch] ; no "unreachable code" +.C[-wsig] ; warn if significant digits may be lost += +; * * * * List of modules * * * * +pcstart.o +djpeg.c (cdjpeg.h,jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jerror.h,cderror.h,jversion.h) +cdjpeg.c (cdjpeg.h,jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jerror.h,cderror.h) +rdcolmap.c (cdjpeg.h,jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jerror.h,cderror.h) +wrppm.c (cdjpeg.h,jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jerror.h,cderror.h) +wrgif.c (cdjpeg.h,jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jerror.h,cderror.h) +wrtarga.c (cdjpeg.h,jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jerror.h,cderror.h) +wrbmp.c (cdjpeg.h,jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jerror.h,cderror.h) +wrrle.c (cdjpeg.h,jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jerror.h,cderror.h) +libjpeg.lib ; built by libjpeg.prj +pcfltlib.lib ; floating point library +; the float library can be omitted if you've turned off DCT_FLOAT_SUPPORTED +pcstdlib.lib ; standard library +pcextlib.lib ; extended library diff --git a/Engine/lib/ljpeg/extras/makeapps.ds b/Engine/lib/ljpeg/extras/makeapps.ds new file mode 100644 index 000000000..bedd038a3 --- /dev/null +++ b/Engine/lib/ljpeg/extras/makeapps.ds @@ -0,0 +1,828 @@ +# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +!IF "$(CFG)" == "" +CFG=cjpeg - Win32 +!MESSAGE No configuration specified. Defaulting to cjpeg - Win32. +!ENDIF + +!IF "$(CFG)" != "cjpeg - Win32" && "$(CFG)" != "djpeg - Win32" &&\ + "$(CFG)" != "jpegtran - Win32" && "$(CFG)" != "rdjpgcom - Win32" &&\ + "$(CFG)" != "wrjpgcom - Win32" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE on this makefile +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "apps.mak" CFG="cjpeg - Win32" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "cjpeg - Win32" (based on "Win32 (x86) Console Application") +!MESSAGE "djpeg - Win32" (based on "Win32 (x86) Console Application") +!MESSAGE "jpegtran - Win32" (based on "Win32 (x86) Console Application") +!MESSAGE "rdjpgcom - Win32" (based on "Win32 (x86) Console Application") +!MESSAGE "wrjpgcom - Win32" (based on "Win32 (x86) Console Application") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF +################################################################################ +# Begin Project +# PROP Target_Last_Scanned "cjpeg - Win32" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "cjpeg - Win32" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "cjpeg\Release" +# PROP BASE Intermediate_Dir "cjpeg\Release" +# PROP BASE Target_Dir "cjpeg" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "cjpeg\Release" +# PROP Intermediate_Dir "cjpeg\Release" +# PROP Target_Dir "cjpeg" +OUTDIR=.\cjpeg\Release +INTDIR=.\cjpeg\Release + +ALL : "$(OUTDIR)\cjpeg.exe" + +CLEAN : + -@erase "$(INTDIR)\cjpeg.obj" + -@erase "$(INTDIR)\rdppm.obj" + -@erase "$(INTDIR)\rdgif.obj" + -@erase "$(INTDIR)\rdtarga.obj" + -@erase "$(INTDIR)\rdrle.obj" + -@erase "$(INTDIR)\rdbmp.obj" + -@erase "$(INTDIR)\rdswitch.obj" + -@erase "$(INTDIR)\cdjpeg.obj" + -@erase "$(OUTDIR)\cjpeg.exe" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c +CPP_PROJ=/nologo /ML /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE"\ + /Fp"$(INTDIR)/cjpeg.pch" /YX /Fo"$(INTDIR)/" /c +CPP_OBJS=.\cjpeg\Release/ +CPP_SBRS=.\. +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/cjpeg.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 Release\jpeg.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +LINK32_FLAGS=Release\jpeg.lib kernel32.lib user32.lib gdi32.lib winspool.lib\ + comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib\ + odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:no\ + /pdb:"$(OUTDIR)/cjpeg.pdb" /machine:I386 /out:"$(OUTDIR)/cjpeg.exe" +LINK32_OBJS= \ + "$(INTDIR)\cjpeg.obj" \ + "$(INTDIR)\rdppm.obj" \ + "$(INTDIR)\rdgif.obj" \ + "$(INTDIR)\rdtarga.obj" \ + "$(INTDIR)\rdrle.obj" \ + "$(INTDIR)\rdbmp.obj" \ + "$(INTDIR)\rdswitch.obj" \ + "$(INTDIR)\cdjpeg.obj" \ + + +"$(OUTDIR)\cjpeg.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "djpeg - Win32" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "djpeg\Release" +# PROP BASE Intermediate_Dir "djpeg\Release" +# PROP BASE Target_Dir "djpeg" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "djpeg\Release" +# PROP Intermediate_Dir "djpeg\Release" +# PROP Target_Dir "djpeg" +OUTDIR=.\djpeg\Release +INTDIR=.\djpeg\Release + +ALL : "$(OUTDIR)\djpeg.exe" + +CLEAN : + -@erase "$(INTDIR)\djpeg.obj" + -@erase "$(INTDIR)\wrppm.obj" + -@erase "$(INTDIR)\wrgif.obj" + -@erase "$(INTDIR)\wrtarga.obj" + -@erase "$(INTDIR)\wrrle.obj" + -@erase "$(INTDIR)\wrbmp.obj" + -@erase "$(INTDIR)\rdcolmap.obj" + -@erase "$(INTDIR)\cdjpeg.obj" + -@erase "$(OUTDIR)\djpeg.exe" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c +CPP_PROJ=/nologo /ML /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE"\ + /Fp"$(INTDIR)/djpeg.pch" /YX /Fo"$(INTDIR)/" /c +CPP_OBJS=.\djpeg\Release/ +CPP_SBRS=.\. +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/djpeg.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 Release\jpeg.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +LINK32_FLAGS=Release\jpeg.lib kernel32.lib user32.lib gdi32.lib winspool.lib\ + comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib\ + odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:no\ + /pdb:"$(OUTDIR)/djpeg.pdb" /machine:I386 /out:"$(OUTDIR)/djpeg.exe" +LINK32_OBJS= \ + "$(INTDIR)\djpeg.obj" \ + "$(INTDIR)\wrppm.obj" \ + "$(INTDIR)\wrgif.obj" \ + "$(INTDIR)\wrtarga.obj" \ + "$(INTDIR)\wrrle.obj" \ + "$(INTDIR)\wrbmp.obj" \ + "$(INTDIR)\rdcolmap.obj" \ + "$(INTDIR)\cdjpeg.obj" \ + + +"$(OUTDIR)\djpeg.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "jpegtran - Win32" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "jpegtran\Release" +# PROP BASE Intermediate_Dir "jpegtran\Release" +# PROP BASE Target_Dir "jpegtran" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "jpegtran\Release" +# PROP Intermediate_Dir "jpegtran\Release" +# PROP Target_Dir "jpegtran" +OUTDIR=.\jpegtran\Release +INTDIR=.\jpegtran\Release + +ALL : "$(OUTDIR)\jpegtran.exe" + +CLEAN : + -@erase "$(INTDIR)\jpegtran.obj" + -@erase "$(INTDIR)\rdswitch.obj" + -@erase "$(INTDIR)\cdjpeg.obj" + -@erase "$(INTDIR)\transupp.obj" + -@erase "$(OUTDIR)\jpegtran.exe" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c +CPP_PROJ=/nologo /ML /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE"\ + /Fp"$(INTDIR)/jpegtran.pch" /YX /Fo"$(INTDIR)/" /c +CPP_OBJS=.\jpegtran\Release/ +CPP_SBRS=.\. +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/jpegtran.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 Release\jpeg.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +LINK32_FLAGS=Release\jpeg.lib kernel32.lib user32.lib gdi32.lib winspool.lib\ + comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib\ + odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:no\ + /pdb:"$(OUTDIR)/jpegtran.pdb" /machine:I386 /out:"$(OUTDIR)/jpegtran.exe" +LINK32_OBJS= \ + "$(INTDIR)\jpegtran.obj" \ + "$(INTDIR)\rdswitch.obj" \ + "$(INTDIR)\cdjpeg.obj" \ + "$(INTDIR)\transupp.obj" \ + + +"$(OUTDIR)\jpegtran.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "rdjpgcom - Win32" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "rdjpgcom\Release" +# PROP BASE Intermediate_Dir "rdjpgcom\Release" +# PROP BASE Target_Dir "rdjpgcom" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "rdjpgcom\Release" +# PROP Intermediate_Dir "rdjpgcom\Release" +# PROP Target_Dir "rdjpgcom" +OUTDIR=.\rdjpgcom\Release +INTDIR=.\rdjpgcom\Release + +ALL : "$(OUTDIR)\rdjpgcom.exe" + +CLEAN : + -@erase "$(INTDIR)\rdjpgcom.obj" + -@erase "$(OUTDIR)\rdjpgcom.exe" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c +CPP_PROJ=/nologo /ML /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE"\ + /Fp"$(INTDIR)/rdjpgcom.pch" /YX /Fo"$(INTDIR)/" /c +CPP_OBJS=.\rdjpgcom\Release/ +CPP_SBRS=.\. +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/rdjpgcom.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 Release\jpeg.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +LINK32_FLAGS=Release\jpeg.lib kernel32.lib user32.lib gdi32.lib winspool.lib\ + comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib\ + odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:no\ + /pdb:"$(OUTDIR)/rdjpgcom.pdb" /machine:I386 /out:"$(OUTDIR)/rdjpgcom.exe" +LINK32_OBJS= \ + "$(INTDIR)\rdjpgcom.obj" + +"$(OUTDIR)\rdjpgcom.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "wrjpgcom - Win32" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "wrjpgcom\Release" +# PROP BASE Intermediate_Dir "wrjpgcom\Release" +# PROP BASE Target_Dir "wrjpgcom" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "wrjpgcom\Release" +# PROP Intermediate_Dir "wrjpgcom\Release" +# PROP Target_Dir "wrjpgcom" +OUTDIR=.\wrjpgcom\Release +INTDIR=.\wrjpgcom\Release + +ALL : "$(OUTDIR)\wrjpgcom.exe" + +CLEAN : + -@erase "$(INTDIR)\wrjpgcom.obj" + -@erase "$(OUTDIR)\wrjpgcom.exe" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c +CPP_PROJ=/nologo /ML /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE"\ + /Fp"$(INTDIR)/wrjpgcom.pch" /YX /Fo"$(INTDIR)/" /c +CPP_OBJS=.\wrjpgcom\Release/ +CPP_SBRS=.\. +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/wrjpgcom.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 Release\jpeg.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +LINK32_FLAGS=Release\jpeg.lib kernel32.lib user32.lib gdi32.lib winspool.lib\ + comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib\ + odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:no\ + /pdb:"$(OUTDIR)/wrjpgcom.pdb" /machine:I386 /out:"$(OUTDIR)/wrjpgcom.exe" +LINK32_OBJS= \ + "$(INTDIR)\wrjpgcom.obj" + +"$(OUTDIR)\wrjpgcom.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ENDIF + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.c{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +################################################################################ +# Begin Target + +# Name "cjpeg - Win32" + +!IF "$(CFG)" == "cjpeg - Win32" + +!ENDIF + +################################################################################ +# Begin Source File + +SOURCE="cjpeg.c" +DEP_CPP_CJPEG=\ + "cdjpeg.h"\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jerror.h"\ + "cderror.h"\ + "jversion.h"\ + + +"$(INTDIR)\cjpeg.obj" : $(SOURCE) $(DEP_CPP_CJPEG) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="cdjpeg.c" +DEP_CPP_CDJPE=\ + "cdjpeg.h"\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jerror.h"\ + "cderror.h"\ + + +"$(INTDIR)\cdjpeg.obj" : $(SOURCE) $(DEP_CPP_CDJPE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="rdswitch.c" +DEP_CPP_RDSWI=\ + "cdjpeg.h"\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jerror.h"\ + "cderror.h"\ + + +"$(INTDIR)\rdswitch.obj" : $(SOURCE) $(DEP_CPP_RDSWI) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="rdppm.c" +DEP_CPP_RDPPM=\ + "cdjpeg.h"\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jerror.h"\ + "cderror.h"\ + + +"$(INTDIR)\rdppm.obj" : $(SOURCE) $(DEP_CPP_RDPPM) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="rdgif.c" +DEP_CPP_RDGIF=\ + "cdjpeg.h"\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jerror.h"\ + "cderror.h"\ + + +"$(INTDIR)\rdgif.obj" : $(SOURCE) $(DEP_CPP_RDGIF) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="rdtarga.c" +DEP_CPP_RDTAR=\ + "cdjpeg.h"\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jerror.h"\ + "cderror.h"\ + + +"$(INTDIR)\rdtarga.obj" : $(SOURCE) $(DEP_CPP_RDTAR) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="rdbmp.c" +DEP_CPP_RDBMP=\ + "cdjpeg.h"\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jerror.h"\ + "cderror.h"\ + + +"$(INTDIR)\rdbmp.obj" : $(SOURCE) $(DEP_CPP_RDBMP) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="rdrle.c" +DEP_CPP_RDRLE=\ + "cdjpeg.h"\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jerror.h"\ + "cderror.h"\ + + +"$(INTDIR)\rdrle.obj" : $(SOURCE) $(DEP_CPP_RDRLE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +# End Target +################################################################################ +# Begin Target + +# Name "djpeg - Win32" + +!IF "$(CFG)" == "djpeg - Win32" + +!ENDIF + +################################################################################ +# Begin Source File + +SOURCE="djpeg.c" +DEP_CPP_DJPEG=\ + "cdjpeg.h"\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jerror.h"\ + "cderror.h"\ + "jversion.h"\ + + +"$(INTDIR)\djpeg.obj" : $(SOURCE) $(DEP_CPP_DJPEG) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="cdjpeg.c" +DEP_CPP_CDJPE=\ + "cdjpeg.h"\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jerror.h"\ + "cderror.h"\ + + +"$(INTDIR)\cdjpeg.obj" : $(SOURCE) $(DEP_CPP_CDJPE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="rdcolmap.c" +DEP_CPP_RDCOL=\ + "cdjpeg.h"\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jerror.h"\ + "cderror.h"\ + + +"$(INTDIR)\rdcolmap.obj" : $(SOURCE) $(DEP_CPP_RDCOL) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="wrppm.c" +DEP_CPP_WRPPM=\ + "cdjpeg.h"\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jerror.h"\ + "cderror.h"\ + + +"$(INTDIR)\wrppm.obj" : $(SOURCE) $(DEP_CPP_WRPPM) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="wrgif.c" +DEP_CPP_WRGIF=\ + "cdjpeg.h"\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jerror.h"\ + "cderror.h"\ + + +"$(INTDIR)\wrgif.obj" : $(SOURCE) $(DEP_CPP_WRGIF) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="wrtarga.c" +DEP_CPP_WRTAR=\ + "cdjpeg.h"\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jerror.h"\ + "cderror.h"\ + + +"$(INTDIR)\wrtarga.obj" : $(SOURCE) $(DEP_CPP_WRTAR) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="wrbmp.c" +DEP_CPP_WRBMP=\ + "cdjpeg.h"\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jerror.h"\ + "cderror.h"\ + + +"$(INTDIR)\wrbmp.obj" : $(SOURCE) $(DEP_CPP_WRBMP) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="wrrle.c" +DEP_CPP_WRRLE=\ + "cdjpeg.h"\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jerror.h"\ + "cderror.h"\ + + +"$(INTDIR)\wrrle.obj" : $(SOURCE) $(DEP_CPP_WRRLE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +# End Target +################################################################################ +# Begin Target + +# Name "jpegtran - Win32" + +!IF "$(CFG)" == "jpegtran - Win32" + +!ENDIF + +################################################################################ +# Begin Source File + +SOURCE="jpegtran.c" +DEP_CPP_JPEGT=\ + "cdjpeg.h"\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jerror.h"\ + "cderror.h"\ + "transupp.h"\ + "jversion.h"\ + + +"$(INTDIR)\jpegtran.obj" : $(SOURCE) $(DEP_CPP_JPEGT) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="cdjpeg.c" +DEP_CPP_CDJPE=\ + "cdjpeg.h"\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jerror.h"\ + "cderror.h"\ + + +"$(INTDIR)\cdjpeg.obj" : $(SOURCE) $(DEP_CPP_CDJPE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="rdswitch.c" +DEP_CPP_RDSWI=\ + "cdjpeg.h"\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jerror.h"\ + "cderror.h"\ + + +"$(INTDIR)\rdswitch.obj" : $(SOURCE) $(DEP_CPP_RDSWI) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="transupp.c" +DEP_CPP_TRANS=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + "transupp.h"\ + + +"$(INTDIR)\transupp.obj" : $(SOURCE) $(DEP_CPP_TRANS) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +# End Target +################################################################################ +# Begin Target + +# Name "rdjpgcom - Win32" + +!IF "$(CFG)" == "rdjpgcom - Win32" + +!ENDIF + +################################################################################ +# Begin Source File + +SOURCE="rdjpgcom.c" +DEP_CPP_RDJPG=\ + "jinclude.h"\ + "jconfig.h"\ + + +"$(INTDIR)\rdjpgcom.obj" : $(SOURCE) $(DEP_CPP_RDJPG) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +# End Target +################################################################################ +# Begin Target + +# Name "wrjpgcom - Win32" + +!IF "$(CFG)" == "wrjpgcom - Win32" + +!ENDIF + +################################################################################ +# Begin Source File + +SOURCE="wrjpgcom.c" +DEP_CPP_WRJPG=\ + "jinclude.h"\ + "jconfig.h"\ + + +"$(INTDIR)\wrjpgcom.obj" : $(SOURCE) $(DEP_CPP_WRJPG) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +# End Target +# End Project +################################################################################ + diff --git a/Engine/lib/ljpeg/extras/makefile.ansi b/Engine/lib/ljpeg/extras/makefile.ansi new file mode 100644 index 000000000..829191359 --- /dev/null +++ b/Engine/lib/ljpeg/extras/makefile.ansi @@ -0,0 +1,214 @@ +# Makefile for Independent JPEG Group's software + +# This makefile is suitable for Unix-like systems with ANSI-capable compilers. +# If you have a non-ANSI compiler, makefile.unix is a better starting point. + +# Read installation instructions before saying "make" !! + +# The name of your C compiler: +CC= cc + +# You may need to adjust these cc options: +CFLAGS= -O +# Generally, we recommend defining any configuration symbols in jconfig.h, +# NOT via -D switches here. + +# Link-time cc options: +LDFLAGS= + +# To link any special libraries, add the necessary -l commands here. +LDLIBS= + +# Put here the object file name for the correct system-dependent memory +# manager file. For Unix this is usually jmemnobs.o, but you may want +# to use jmemansi.o or jmemname.o if you have limited swap space. +SYSDEPMEM= jmemnobs.o + +# miscellaneous OS-dependent stuff +# linker +LN= $(CC) +# file deletion command +RM= rm -f +# library (.a) file creation command +AR= ar rc +# second step in .a creation (use "touch" if not needed) +AR2= ranlib + +# End of configurable options. + + +# source files: JPEG library proper +LIBSOURCES= jcapimin.c jcapistd.c jccoefct.c jccolor.c jcdctmgr.c jchuff.c \ + jcinit.c jcmainct.c jcmarker.c jcmaster.c jcomapi.c jcparam.c \ + jcphuff.c jcprepct.c jcsample.c jctrans.c jdapimin.c jdapistd.c \ + jdatadst.c jdatasrc.c jdcoefct.c jdcolor.c jddctmgr.c jdhuff.c \ + jdinput.c jdmainct.c jdmarker.c jdmaster.c jdmerge.c jdphuff.c \ + jdpostct.c jdsample.c jdtrans.c jerror.c jfdctflt.c jfdctfst.c \ + jfdctint.c jidctflt.c jidctfst.c jidctint.c jidctred.c jquant1.c \ + jquant2.c jutils.c jmemmgr.c +# memmgr back ends: compile only one of these into a working library +SYSDEPSOURCES= jmemansi.c jmemname.c jmemnobs.c jmemdos.c jmemmac.c +# source files: cjpeg/djpeg/jpegtran applications, also rdjpgcom/wrjpgcom +APPSOURCES= cjpeg.c djpeg.c jpegtran.c rdjpgcom.c wrjpgcom.c cdjpeg.c \ + rdcolmap.c rdswitch.c transupp.c rdppm.c wrppm.c rdgif.c wrgif.c \ + rdtarga.c wrtarga.c rdbmp.c wrbmp.c rdrle.c wrrle.c +SOURCES= $(LIBSOURCES) $(SYSDEPSOURCES) $(APPSOURCES) +# files included by source files +INCLUDES= jchuff.h jdhuff.h jdct.h jerror.h jinclude.h jmemsys.h jmorecfg.h \ + jpegint.h jpeglib.h jversion.h cdjpeg.h cderror.h transupp.h +# documentation, test, and support files +DOCS= README install.doc usage.doc cjpeg.1 djpeg.1 jpegtran.1 rdjpgcom.1 \ + wrjpgcom.1 wizard.doc example.c libjpeg.doc structure.doc \ + coderules.doc filelist.doc change.log +MKFILES= configure makefile.cfg makefile.ansi makefile.unix makefile.bcc \ + makefile.mc6 makefile.dj makefile.wat makefile.vc makelib.ds \ + makeapps.ds makeproj.mac makcjpeg.st makdjpeg.st makljpeg.st \ + maktjpeg.st makefile.manx makefile.sas makefile.mms makefile.vms \ + makvms.opt +CONFIGFILES= jconfig.cfg jconfig.bcc jconfig.mc6 jconfig.dj jconfig.wat \ + jconfig.vc jconfig.mac jconfig.st jconfig.manx jconfig.sas \ + jconfig.vms +CONFIGUREFILES= config.guess config.sub install-sh ltconfig ltmain.sh +OTHERFILES= jconfig.doc ckconfig.c ansi2knr.c ansi2knr.1 jmemdosa.asm +TESTFILES= testorig.jpg testimg.ppm testimg.bmp testimg.jpg testprog.jpg \ + testimgp.jpg +DISTFILES= $(DOCS) $(MKFILES) $(CONFIGFILES) $(SOURCES) $(INCLUDES) \ + $(CONFIGUREFILES) $(OTHERFILES) $(TESTFILES) +# library object files common to compression and decompression +COMOBJECTS= jcomapi.o jutils.o jerror.o jmemmgr.o $(SYSDEPMEM) +# compression library object files +CLIBOBJECTS= jcapimin.o jcapistd.o jctrans.o jcparam.o jdatadst.o jcinit.o \ + jcmaster.o jcmarker.o jcmainct.o jcprepct.o jccoefct.o jccolor.o \ + jcsample.o jchuff.o jcphuff.o jcdctmgr.o jfdctfst.o jfdctflt.o \ + jfdctint.o +# decompression library object files +DLIBOBJECTS= jdapimin.o jdapistd.o jdtrans.o jdatasrc.o jdmaster.o \ + jdinput.o jdmarker.o jdhuff.o jdphuff.o jdmainct.o jdcoefct.o \ + jdpostct.o jddctmgr.o jidctfst.o jidctflt.o jidctint.o jidctred.o \ + jdsample.o jdcolor.o jquant1.o jquant2.o jdmerge.o +# These objectfiles are included in libjpeg.a +LIBOBJECTS= $(CLIBOBJECTS) $(DLIBOBJECTS) $(COMOBJECTS) +# object files for sample applications (excluding library files) +COBJECTS= cjpeg.o rdppm.o rdgif.o rdtarga.o rdrle.o rdbmp.o rdswitch.o \ + cdjpeg.o +DOBJECTS= djpeg.o wrppm.o wrgif.o wrtarga.o wrrle.o wrbmp.o rdcolmap.o \ + cdjpeg.o +TROBJECTS= jpegtran.o rdswitch.o cdjpeg.o transupp.o + + +all: libjpeg.a cjpeg djpeg jpegtran rdjpgcom wrjpgcom + +libjpeg.a: $(LIBOBJECTS) + $(RM) libjpeg.a + $(AR) libjpeg.a $(LIBOBJECTS) + $(AR2) libjpeg.a + +cjpeg: $(COBJECTS) libjpeg.a + $(LN) $(LDFLAGS) -o cjpeg $(COBJECTS) libjpeg.a $(LDLIBS) + +djpeg: $(DOBJECTS) libjpeg.a + $(LN) $(LDFLAGS) -o djpeg $(DOBJECTS) libjpeg.a $(LDLIBS) + +jpegtran: $(TROBJECTS) libjpeg.a + $(LN) $(LDFLAGS) -o jpegtran $(TROBJECTS) libjpeg.a $(LDLIBS) + +rdjpgcom: rdjpgcom.o + $(LN) $(LDFLAGS) -o rdjpgcom rdjpgcom.o $(LDLIBS) + +wrjpgcom: wrjpgcom.o + $(LN) $(LDFLAGS) -o wrjpgcom wrjpgcom.o $(LDLIBS) + +jconfig.h: jconfig.doc + echo You must prepare a system-dependent jconfig.h file. + echo Please read the installation directions in install.doc. + exit 1 + +clean: + $(RM) *.o cjpeg djpeg jpegtran libjpeg.a rdjpgcom wrjpgcom + $(RM) core testout* + +test: cjpeg djpeg jpegtran + $(RM) testout* + ./djpeg -dct int -ppm -outfile testout.ppm testorig.jpg + ./djpeg -dct int -bmp -colors 256 -outfile testout.bmp testorig.jpg + ./cjpeg -dct int -outfile testout.jpg testimg.ppm + ./djpeg -dct int -ppm -outfile testoutp.ppm testprog.jpg + ./cjpeg -dct int -progressive -opt -outfile testoutp.jpg testimg.ppm + ./jpegtran -outfile testoutt.jpg testprog.jpg + cmp testimg.ppm testout.ppm + cmp testimg.bmp testout.bmp + cmp testimg.jpg testout.jpg + cmp testimg.ppm testoutp.ppm + cmp testimgp.jpg testoutp.jpg + cmp testorig.jpg testoutt.jpg + + +jcapimin.o: jcapimin.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcapistd.o: jcapistd.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jccoefct.o: jccoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jccolor.o: jccolor.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcdctmgr.o: jcdctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jchuff.o: jchuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jchuff.h +jcinit.o: jcinit.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcmainct.o: jcmainct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcmarker.o: jcmarker.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcmaster.o: jcmaster.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcomapi.o: jcomapi.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcparam.o: jcparam.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcphuff.o: jcphuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jchuff.h +jcprepct.o: jcprepct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcsample.o: jcsample.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jctrans.o: jctrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdapimin.o: jdapimin.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdapistd.o: jdapistd.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdatadst.o: jdatadst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h +jdatasrc.o: jdatasrc.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h +jdcoefct.o: jdcoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdcolor.o: jdcolor.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jddctmgr.o: jddctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jdhuff.o: jdhuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdhuff.h +jdinput.o: jdinput.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmainct.o: jdmainct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmarker.o: jdmarker.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmaster.o: jdmaster.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmerge.o: jdmerge.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdphuff.o: jdphuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdhuff.h +jdpostct.o: jdpostct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdsample.o: jdsample.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdtrans.o: jdtrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jerror.o: jerror.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jversion.h jerror.h +jfdctflt.o: jfdctflt.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jfdctfst.o: jfdctfst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jfdctint.o: jfdctint.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctflt.o: jidctflt.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctfst.o: jidctfst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctint.o: jidctint.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctred.o: jidctred.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jquant1.o: jquant1.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jquant2.o: jquant2.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jutils.o: jutils.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jmemmgr.o: jmemmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemansi.o: jmemansi.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemname.o: jmemname.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemnobs.o: jmemnobs.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemdos.o: jmemdos.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemmac.o: jmemmac.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +cjpeg.o: cjpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h jversion.h +djpeg.o: djpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h jversion.h +jpegtran.o: jpegtran.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h transupp.h jversion.h +rdjpgcom.o: rdjpgcom.c jinclude.h jconfig.h +wrjpgcom.o: wrjpgcom.c jinclude.h jconfig.h +cdjpeg.o: cdjpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdcolmap.o: rdcolmap.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdswitch.o: rdswitch.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +transupp.o: transupp.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h transupp.h +rdppm.o: rdppm.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrppm.o: wrppm.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdgif.o: rdgif.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrgif.o: wrgif.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdtarga.o: rdtarga.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrtarga.o: wrtarga.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdbmp.o: rdbmp.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrbmp.o: wrbmp.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdrle.o: rdrle.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrrle.o: wrrle.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h diff --git a/Engine/lib/ljpeg/extras/makefile.bcc b/Engine/lib/ljpeg/extras/makefile.bcc new file mode 100644 index 000000000..a1cfcde66 --- /dev/null +++ b/Engine/lib/ljpeg/extras/makefile.bcc @@ -0,0 +1,285 @@ +# Makefile for Independent JPEG Group's software + +# This makefile is suitable for Borland C on MS-DOS or OS/2. +# It works with Borland C++ for DOS, revision 3.0 or later, +# and has been tested with Borland C++ for OS/2. +# Watch out for optimization bugs in the OS/2 compilers --- see notes below! +# Thanks to Tom Wright and Ge' Weijers (original DOS) and +# Ken Porter (OS/2) for this file. + +# Read installation instructions before saying "make" !! + +# Are we under DOS or OS/2? +!if !$d(DOS) && !$d(OS2) +!if $d(__OS2__) +OS2=1 +!else +DOS=1 +!endif +!endif + +# The name of your C compiler: +CC= bcc + +# You may need to adjust these cc options: +!if $d(DOS) +CFLAGS= -O2 -mm -w-par -w-stu -w-ccc -w-rch +!else +CFLAGS= -O1 -w-par -w-stu -w-ccc -w-rch +!endif +# -O2 enables full code optimization (for pre-3.0 Borland C++, use -O -G -Z). +# -O2 is buggy in Borland OS/2 C++ revision 2.0, so use -O1 there for now. +# If you have Borland OS/2 C++ revision 1.0, use -O or no optimization at all. +# -mm selects medium memory model (near data, far code pointers; DOS only!) +# -w-par suppresses warnings about unused function parameters +# -w-stu suppresses warnings about incomplete structures +# -w-ccc suppresses warnings about compile-time-constant conditions +# -w-rch suppresses warnings about unreachable code +# Generally, we recommend defining any configuration symbols in jconfig.h, +# NOT via -D switches here. + +# Link-time cc options: +!if $d(DOS) +LDFLAGS= -mm +# memory model option here must match CFLAGS! +!else +LDFLAGS= +# -lai full-screen app +# -lc case-significant link +!endif + +# Put here the object file name for the correct system-dependent memory +# manager file. +# For DOS, we recommend jmemdos.c and jmemdosa.asm. +# For OS/2, we recommend jmemnobs.c (flat memory!) +# SYSDEPMEMLIB must list the same files with "+" signs for the librarian. +!if $d(DOS) +SYSDEPMEM= jmemdos.obj jmemdosa.obj +SYSDEPMEMLIB= +jmemdos.obj +jmemdosa.obj +!else +SYSDEPMEM= jmemnobs.obj +SYSDEPMEMLIB= +jmemnobs.obj +!endif + +# End of configurable options. + + +# source files: JPEG library proper +LIBSOURCES= jcapimin.c jcapistd.c jccoefct.c jccolor.c jcdctmgr.c jchuff.c \ + jcinit.c jcmainct.c jcmarker.c jcmaster.c jcomapi.c jcparam.c \ + jcphuff.c jcprepct.c jcsample.c jctrans.c jdapimin.c jdapistd.c \ + jdatadst.c jdatasrc.c jdcoefct.c jdcolor.c jddctmgr.c jdhuff.c \ + jdinput.c jdmainct.c jdmarker.c jdmaster.c jdmerge.c jdphuff.c \ + jdpostct.c jdsample.c jdtrans.c jerror.c jfdctflt.c jfdctfst.c \ + jfdctint.c jidctflt.c jidctfst.c jidctint.c jidctred.c jquant1.c \ + jquant2.c jutils.c jmemmgr.c +# memmgr back ends: compile only one of these into a working library +SYSDEPSOURCES= jmemansi.c jmemname.c jmemnobs.c jmemdos.c jmemmac.c +# source files: cjpeg/djpeg/jpegtran applications, also rdjpgcom/wrjpgcom +APPSOURCES= cjpeg.c djpeg.c jpegtran.c rdjpgcom.c wrjpgcom.c cdjpeg.c \ + rdcolmap.c rdswitch.c transupp.c rdppm.c wrppm.c rdgif.c wrgif.c \ + rdtarga.c wrtarga.c rdbmp.c wrbmp.c rdrle.c wrrle.c +SOURCES= $(LIBSOURCES) $(SYSDEPSOURCES) $(APPSOURCES) +# files included by source files +INCLUDES= jchuff.h jdhuff.h jdct.h jerror.h jinclude.h jmemsys.h jmorecfg.h \ + jpegint.h jpeglib.h jversion.h cdjpeg.h cderror.h transupp.h +# documentation, test, and support files +DOCS= README install.doc usage.doc cjpeg.1 djpeg.1 jpegtran.1 rdjpgcom.1 \ + wrjpgcom.1 wizard.doc example.c libjpeg.doc structure.doc \ + coderules.doc filelist.doc change.log +MKFILES= configure makefile.cfg makefile.ansi makefile.unix makefile.bcc \ + makefile.mc6 makefile.dj makefile.wat makefile.vc makelib.ds \ + makeapps.ds makeproj.mac makcjpeg.st makdjpeg.st makljpeg.st \ + maktjpeg.st makefile.manx makefile.sas makefile.mms makefile.vms \ + makvms.opt +CONFIGFILES= jconfig.cfg jconfig.bcc jconfig.mc6 jconfig.dj jconfig.wat \ + jconfig.vc jconfig.mac jconfig.st jconfig.manx jconfig.sas \ + jconfig.vms +CONFIGUREFILES= config.guess config.sub install-sh ltconfig ltmain.sh +OTHERFILES= jconfig.doc ckconfig.c ansi2knr.c ansi2knr.1 jmemdosa.asm +TESTFILES= testorig.jpg testimg.ppm testimg.bmp testimg.jpg testprog.jpg \ + testimgp.jpg +DISTFILES= $(DOCS) $(MKFILES) $(CONFIGFILES) $(SOURCES) $(INCLUDES) \ + $(CONFIGUREFILES) $(OTHERFILES) $(TESTFILES) +# library object files common to compression and decompression +COMOBJECTS= jcomapi.obj jutils.obj jerror.obj jmemmgr.obj $(SYSDEPMEM) +# compression library object files +CLIBOBJECTS= jcapimin.obj jcapistd.obj jctrans.obj jcparam.obj jdatadst.obj \ + jcinit.obj jcmaster.obj jcmarker.obj jcmainct.obj jcprepct.obj \ + jccoefct.obj jccolor.obj jcsample.obj jchuff.obj jcphuff.obj \ + jcdctmgr.obj jfdctfst.obj jfdctflt.obj jfdctint.obj +# decompression library object files +DLIBOBJECTS= jdapimin.obj jdapistd.obj jdtrans.obj jdatasrc.obj \ + jdmaster.obj jdinput.obj jdmarker.obj jdhuff.obj jdphuff.obj \ + jdmainct.obj jdcoefct.obj jdpostct.obj jddctmgr.obj jidctfst.obj \ + jidctflt.obj jidctint.obj jidctred.obj jdsample.obj jdcolor.obj \ + jquant1.obj jquant2.obj jdmerge.obj +# These objectfiles are included in libjpeg.lib +LIBOBJECTS= $(CLIBOBJECTS) $(DLIBOBJECTS) $(COMOBJECTS) +# object files for sample applications (excluding library files) +COBJECTS= cjpeg.obj rdppm.obj rdgif.obj rdtarga.obj rdrle.obj rdbmp.obj \ + rdswitch.obj cdjpeg.obj +DOBJECTS= djpeg.obj wrppm.obj wrgif.obj wrtarga.obj wrrle.obj wrbmp.obj \ + rdcolmap.obj cdjpeg.obj +TROBJECTS= jpegtran.obj rdswitch.obj cdjpeg.obj transupp.obj + + +all: libjpeg.lib cjpeg.exe djpeg.exe jpegtran.exe rdjpgcom.exe wrjpgcom.exe + +libjpeg.lib: $(LIBOBJECTS) + - del libjpeg.lib + tlib libjpeg.lib /E /C @&&| ++jcapimin.obj +jcapistd.obj +jctrans.obj +jcparam.obj +jdatadst.obj & ++jcinit.obj +jcmaster.obj +jcmarker.obj +jcmainct.obj +jcprepct.obj & ++jccoefct.obj +jccolor.obj +jcsample.obj +jchuff.obj +jcphuff.obj & ++jcdctmgr.obj +jfdctfst.obj +jfdctflt.obj +jfdctint.obj +jdapimin.obj & ++jdapistd.obj +jdtrans.obj +jdatasrc.obj +jdmaster.obj +jdinput.obj & ++jdmarker.obj +jdhuff.obj +jdphuff.obj +jdmainct.obj +jdcoefct.obj & ++jdpostct.obj +jddctmgr.obj +jidctfst.obj +jidctflt.obj +jidctint.obj & ++jidctred.obj +jdsample.obj +jdcolor.obj +jquant1.obj +jquant2.obj & ++jdmerge.obj +jcomapi.obj +jutils.obj +jerror.obj +jmemmgr.obj & +$(SYSDEPMEMLIB) +| + +cjpeg.exe: $(COBJECTS) libjpeg.lib + $(CC) $(LDFLAGS) -ecjpeg.exe $(COBJECTS) libjpeg.lib + +djpeg.exe: $(DOBJECTS) libjpeg.lib + $(CC) $(LDFLAGS) -edjpeg.exe $(DOBJECTS) libjpeg.lib + +jpegtran.exe: $(TROBJECTS) libjpeg.lib + $(CC) $(LDFLAGS) -ejpegtran.exe $(TROBJECTS) libjpeg.lib + +rdjpgcom.exe: rdjpgcom.c +!if $d(DOS) + $(CC) -ms -O rdjpgcom.c +!else + $(CC) $(CFLAGS) rdjpgcom.c +!endif + +# On DOS, wrjpgcom needs large model so it can malloc a 64K chunk +wrjpgcom.exe: wrjpgcom.c +!if $d(DOS) + $(CC) -ml -O wrjpgcom.c +!else + $(CC) $(CFLAGS) wrjpgcom.c +!endif + +# This "{}" syntax allows Borland Make to "batch" source files. +# In this way, each run of the compiler can build many modules. +.c.obj: + $(CC) $(CFLAGS) -c{ $<} + +jconfig.h: jconfig.doc + echo You must prepare a system-dependent jconfig.h file. + echo Please read the installation directions in install.doc. + exit 1 + +clean: + - del *.obj + - del libjpeg.lib + - del cjpeg.exe + - del djpeg.exe + - del jpegtran.exe + - del rdjpgcom.exe + - del wrjpgcom.exe + - del testout*.* + +test: cjpeg.exe djpeg.exe jpegtran.exe + - del testout*.* + djpeg -dct int -ppm -outfile testout.ppm testorig.jpg + djpeg -dct int -bmp -colors 256 -outfile testout.bmp testorig.jpg + cjpeg -dct int -outfile testout.jpg testimg.ppm + djpeg -dct int -ppm -outfile testoutp.ppm testprog.jpg + cjpeg -dct int -progressive -opt -outfile testoutp.jpg testimg.ppm + jpegtran -outfile testoutt.jpg testprog.jpg +!if $d(DOS) + fc /b testimg.ppm testout.ppm + fc /b testimg.bmp testout.bmp + fc /b testimg.jpg testout.jpg + fc /b testimg.ppm testoutp.ppm + fc /b testimgp.jpg testoutp.jpg + fc /b testorig.jpg testoutt.jpg +!else + echo n > n.tmp + comp testimg.ppm testout.ppm < n.tmp + comp testimg.bmp testout.bmp < n.tmp + comp testimg.jpg testout.jpg < n.tmp + comp testimg.ppm testoutp.ppm < n.tmp + comp testimgp.jpg testoutp.jpg < n.tmp + comp testorig.jpg testoutt.jpg < n.tmp + del n.tmp +!endif + + +jcapimin.obj: jcapimin.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcapistd.obj: jcapistd.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jccoefct.obj: jccoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jccolor.obj: jccolor.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcdctmgr.obj: jcdctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jchuff.obj: jchuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jchuff.h +jcinit.obj: jcinit.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcmainct.obj: jcmainct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcmarker.obj: jcmarker.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcmaster.obj: jcmaster.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcomapi.obj: jcomapi.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcparam.obj: jcparam.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcphuff.obj: jcphuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jchuff.h +jcprepct.obj: jcprepct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcsample.obj: jcsample.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jctrans.obj: jctrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdapimin.obj: jdapimin.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdapistd.obj: jdapistd.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdatadst.obj: jdatadst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h +jdatasrc.obj: jdatasrc.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h +jdcoefct.obj: jdcoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdcolor.obj: jdcolor.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jddctmgr.obj: jddctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jdhuff.obj: jdhuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdhuff.h +jdinput.obj: jdinput.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmainct.obj: jdmainct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmarker.obj: jdmarker.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmaster.obj: jdmaster.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmerge.obj: jdmerge.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdphuff.obj: jdphuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdhuff.h +jdpostct.obj: jdpostct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdsample.obj: jdsample.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdtrans.obj: jdtrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jerror.obj: jerror.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jversion.h jerror.h +jfdctflt.obj: jfdctflt.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jfdctfst.obj: jfdctfst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jfdctint.obj: jfdctint.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctflt.obj: jidctflt.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctfst.obj: jidctfst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctint.obj: jidctint.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctred.obj: jidctred.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jquant1.obj: jquant1.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jquant2.obj: jquant2.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jutils.obj: jutils.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jmemmgr.obj: jmemmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemansi.obj: jmemansi.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemname.obj: jmemname.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemnobs.obj: jmemnobs.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemdos.obj: jmemdos.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemmac.obj: jmemmac.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +cjpeg.obj: cjpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h jversion.h +djpeg.obj: djpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h jversion.h +jpegtran.obj: jpegtran.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h transupp.h jversion.h +rdjpgcom.obj: rdjpgcom.c jinclude.h jconfig.h +wrjpgcom.obj: wrjpgcom.c jinclude.h jconfig.h +cdjpeg.obj: cdjpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdcolmap.obj: rdcolmap.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdswitch.obj: rdswitch.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +transupp.obj: transupp.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h transupp.h +rdppm.obj: rdppm.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrppm.obj: wrppm.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdgif.obj: rdgif.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrgif.obj: wrgif.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdtarga.obj: rdtarga.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrtarga.obj: wrtarga.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdbmp.obj: rdbmp.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrbmp.obj: wrbmp.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdrle.obj: rdrle.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrrle.obj: wrrle.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +jmemdosa.obj: jmemdosa.asm + tasm /mx jmemdosa.asm diff --git a/Engine/lib/ljpeg/extras/makefile.cfg b/Engine/lib/ljpeg/extras/makefile.cfg new file mode 100644 index 000000000..f25e42e3e --- /dev/null +++ b/Engine/lib/ljpeg/extras/makefile.cfg @@ -0,0 +1,319 @@ +# Makefile for Independent JPEG Group's software + +# makefile.cfg is edited by configure to produce a custom Makefile. + +# Read installation instructions before saying "make" !! + +# For compiling with source and object files in different directories. +srcdir = @srcdir@ +VPATH = @srcdir@ + +# Where to install the programs and man pages. +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = $(exec_prefix)/bin +libdir = $(exec_prefix)/lib +includedir = $(prefix)/include +binprefix = +manprefix = +manext = 1 +mandir = $(prefix)/man/man$(manext) + +# The name of your C compiler: +CC= @CC@ + +# You may need to adjust these cc options: +CFLAGS= @CFLAGS@ @CPPFLAGS@ @INCLUDEFLAGS@ +# Generally, we recommend defining any configuration symbols in jconfig.h, +# NOT via -D switches here. +# However, any special defines for ansi2knr.c may be included here: +ANSI2KNRFLAGS= @ANSI2KNRFLAGS@ + +# Link-time cc options: +LDFLAGS= @LDFLAGS@ + +# To link any special libraries, add the necessary -l commands here. +LDLIBS= @LIBS@ + +# If using GNU libtool, LIBTOOL references it; if not, LIBTOOL is empty. +LIBTOOL = @LIBTOOL@ +# $(O) expands to "lo" if using libtool, plain "o" if not. +# Similarly, $(A) expands to "la" or "a". +O = @O@ +A = @A@ + +# Library version ID; libtool uses this for the shared library version number. +# Note: we suggest this match the macro of the same name in jpeglib.h. +JPEG_LIB_VERSION = @JPEG_LIB_VERSION@ + +# Put here the object file name for the correct system-dependent memory +# manager file. For Unix this is usually jmemnobs.o, but you may want +# to use jmemansi.o or jmemname.o if you have limited swap space. +SYSDEPMEM= @MEMORYMGR@ + +# miscellaneous OS-dependent stuff +SHELL= /bin/sh +# linker +LN= @LN@ +# file deletion command +RM= rm -f +# directory creation command +MKDIR= mkdir +# library (.a) file creation command +AR= ar rc +# second step in .a creation (use "touch" if not needed) +AR2= @RANLIB@ +# installation program +INSTALL= @INSTALL@ +INSTALL_PROGRAM= @INSTALL_PROGRAM@ +INSTALL_LIB= @INSTALL_LIB@ +INSTALL_DATA= @INSTALL_DATA@ + +# End of configurable options. + + +# source files: JPEG library proper +LIBSOURCES= jcapimin.c jcapistd.c jccoefct.c jccolor.c jcdctmgr.c jchuff.c \ + jcinit.c jcmainct.c jcmarker.c jcmaster.c jcomapi.c jcparam.c \ + jcphuff.c jcprepct.c jcsample.c jctrans.c jdapimin.c jdapistd.c \ + jdatadst.c jdatasrc.c jdcoefct.c jdcolor.c jddctmgr.c jdhuff.c \ + jdinput.c jdmainct.c jdmarker.c jdmaster.c jdmerge.c jdphuff.c \ + jdpostct.c jdsample.c jdtrans.c jerror.c jfdctflt.c jfdctfst.c \ + jfdctint.c jidctflt.c jidctfst.c jidctint.c jidctred.c jquant1.c \ + jquant2.c jutils.c jmemmgr.c +# memmgr back ends: compile only one of these into a working library +SYSDEPSOURCES= jmemansi.c jmemname.c jmemnobs.c jmemdos.c jmemmac.c +# source files: cjpeg/djpeg/jpegtran applications, also rdjpgcom/wrjpgcom +APPSOURCES= cjpeg.c djpeg.c jpegtran.c rdjpgcom.c wrjpgcom.c cdjpeg.c \ + rdcolmap.c rdswitch.c transupp.c rdppm.c wrppm.c rdgif.c wrgif.c \ + rdtarga.c wrtarga.c rdbmp.c wrbmp.c rdrle.c wrrle.c +SOURCES= $(LIBSOURCES) $(SYSDEPSOURCES) $(APPSOURCES) +# files included by source files +INCLUDES= jchuff.h jdhuff.h jdct.h jerror.h jinclude.h jmemsys.h jmorecfg.h \ + jpegint.h jpeglib.h jversion.h cdjpeg.h cderror.h transupp.h +# documentation, test, and support files +DOCS= README install.doc usage.doc cjpeg.1 djpeg.1 jpegtran.1 rdjpgcom.1 \ + wrjpgcom.1 wizard.doc example.c libjpeg.doc structure.doc \ + coderules.doc filelist.doc change.log +MKFILES= configure makefile.cfg makefile.ansi makefile.unix makefile.bcc \ + makefile.mc6 makefile.dj makefile.wat makefile.vc makelib.ds \ + makeapps.ds makeproj.mac makcjpeg.st makdjpeg.st makljpeg.st \ + maktjpeg.st makefile.manx makefile.sas makefile.mms makefile.vms \ + makvms.opt +CONFIGFILES= jconfig.cfg jconfig.bcc jconfig.mc6 jconfig.dj jconfig.wat \ + jconfig.vc jconfig.mac jconfig.st jconfig.manx jconfig.sas \ + jconfig.vms +CONFIGUREFILES= config.guess config.sub install-sh ltconfig ltmain.sh +OTHERFILES= jconfig.doc ckconfig.c ansi2knr.c ansi2knr.1 jmemdosa.asm +TESTFILES= testorig.jpg testimg.ppm testimg.bmp testimg.jpg testprog.jpg \ + testimgp.jpg +DISTFILES= $(DOCS) $(MKFILES) $(CONFIGFILES) $(SOURCES) $(INCLUDES) \ + $(CONFIGUREFILES) $(OTHERFILES) $(TESTFILES) +# library object files common to compression and decompression +COMOBJECTS= jcomapi.$(O) jutils.$(O) jerror.$(O) jmemmgr.$(O) $(SYSDEPMEM) +# compression library object files +CLIBOBJECTS= jcapimin.$(O) jcapistd.$(O) jctrans.$(O) jcparam.$(O) \ + jdatadst.$(O) jcinit.$(O) jcmaster.$(O) jcmarker.$(O) jcmainct.$(O) \ + jcprepct.$(O) jccoefct.$(O) jccolor.$(O) jcsample.$(O) jchuff.$(O) \ + jcphuff.$(O) jcdctmgr.$(O) jfdctfst.$(O) jfdctflt.$(O) \ + jfdctint.$(O) +# decompression library object files +DLIBOBJECTS= jdapimin.$(O) jdapistd.$(O) jdtrans.$(O) jdatasrc.$(O) \ + jdmaster.$(O) jdinput.$(O) jdmarker.$(O) jdhuff.$(O) jdphuff.$(O) \ + jdmainct.$(O) jdcoefct.$(O) jdpostct.$(O) jddctmgr.$(O) \ + jidctfst.$(O) jidctflt.$(O) jidctint.$(O) jidctred.$(O) \ + jdsample.$(O) jdcolor.$(O) jquant1.$(O) jquant2.$(O) jdmerge.$(O) +# These objectfiles are included in libjpeg.a +LIBOBJECTS= $(CLIBOBJECTS) $(DLIBOBJECTS) $(COMOBJECTS) +# object files for sample applications (excluding library files) +COBJECTS= cjpeg.$(O) rdppm.$(O) rdgif.$(O) rdtarga.$(O) rdrle.$(O) \ + rdbmp.$(O) rdswitch.$(O) cdjpeg.$(O) +DOBJECTS= djpeg.$(O) wrppm.$(O) wrgif.$(O) wrtarga.$(O) wrrle.$(O) \ + wrbmp.$(O) rdcolmap.$(O) cdjpeg.$(O) +TROBJECTS= jpegtran.$(O) rdswitch.$(O) cdjpeg.$(O) transupp.$(O) + + +all: @A2K_DEPS@ libjpeg.$(A) cjpeg djpeg jpegtran rdjpgcom wrjpgcom + +# Special compilation rules to support ansi2knr and libtool. +.SUFFIXES: .lo .la + +# How to compile with libtool. +@COM_LT@.c.lo: +@COM_LT@ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) -c $(srcdir)/$*.c + +# How to use ansi2knr, when not using libtool. +@COM_A2K@.c.o: +@COM_A2K@ ./ansi2knr $(srcdir)/$*.c knr/$*.c +@COM_A2K@ $(CC) $(CFLAGS) -c knr/$*.c +@COM_A2K@ $(RM) knr/$*.c + +# How to use ansi2knr AND libtool. +@COM_A2K@.c.lo: +@COM_A2K@ ./ansi2knr $(srcdir)/$*.c knr/$*.c +@COM_A2K@ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) -c knr/$*.c +@COM_A2K@ $(RM) knr/$*.c + +ansi2knr: ansi2knr.c + $(CC) $(CFLAGS) $(ANSI2KNRFLAGS) -o ansi2knr $(srcdir)/ansi2knr.c + $(MKDIR) knr + +# the library: + +# without libtool: +libjpeg.a: @A2K_DEPS@ $(LIBOBJECTS) + $(RM) libjpeg.a + $(AR) libjpeg.a $(LIBOBJECTS) + $(AR2) libjpeg.a + +# with libtool: +libjpeg.la: @A2K_DEPS@ $(LIBOBJECTS) + $(LIBTOOL) --mode=link $(CC) -o libjpeg.la $(LIBOBJECTS) \ + -rpath $(libdir) -version-info $(JPEG_LIB_VERSION) + +# sample programs: + +cjpeg: $(COBJECTS) libjpeg.$(A) + $(LN) $(LDFLAGS) -o cjpeg $(COBJECTS) libjpeg.$(A) $(LDLIBS) + +djpeg: $(DOBJECTS) libjpeg.$(A) + $(LN) $(LDFLAGS) -o djpeg $(DOBJECTS) libjpeg.$(A) $(LDLIBS) + +jpegtran: $(TROBJECTS) libjpeg.$(A) + $(LN) $(LDFLAGS) -o jpegtran $(TROBJECTS) libjpeg.$(A) $(LDLIBS) + +rdjpgcom: rdjpgcom.$(O) + $(LN) $(LDFLAGS) -o rdjpgcom rdjpgcom.$(O) $(LDLIBS) + +wrjpgcom: wrjpgcom.$(O) + $(LN) $(LDFLAGS) -o wrjpgcom wrjpgcom.$(O) $(LDLIBS) + +# Installation rules: + +install: cjpeg djpeg jpegtran rdjpgcom wrjpgcom @FORCE_INSTALL_LIB@ + $(INSTALL_PROGRAM) cjpeg $(bindir)/$(binprefix)cjpeg + $(INSTALL_PROGRAM) djpeg $(bindir)/$(binprefix)djpeg + $(INSTALL_PROGRAM) jpegtran $(bindir)/$(binprefix)jpegtran + $(INSTALL_PROGRAM) rdjpgcom $(bindir)/$(binprefix)rdjpgcom + $(INSTALL_PROGRAM) wrjpgcom $(bindir)/$(binprefix)wrjpgcom + $(INSTALL_DATA) $(srcdir)/cjpeg.1 $(mandir)/$(manprefix)cjpeg.$(manext) + $(INSTALL_DATA) $(srcdir)/djpeg.1 $(mandir)/$(manprefix)djpeg.$(manext) + $(INSTALL_DATA) $(srcdir)/jpegtran.1 $(mandir)/$(manprefix)jpegtran.$(manext) + $(INSTALL_DATA) $(srcdir)/rdjpgcom.1 $(mandir)/$(manprefix)rdjpgcom.$(manext) + $(INSTALL_DATA) $(srcdir)/wrjpgcom.1 $(mandir)/$(manprefix)wrjpgcom.$(manext) + +install-lib: libjpeg.$(A) install-headers + $(INSTALL_LIB) libjpeg.$(A) $(libdir)/$(binprefix)libjpeg.$(A) + +install-headers: jconfig.h + $(INSTALL_DATA) jconfig.h $(includedir)/jconfig.h + $(INSTALL_DATA) $(srcdir)/jpeglib.h $(includedir)/jpeglib.h + $(INSTALL_DATA) $(srcdir)/jmorecfg.h $(includedir)/jmorecfg.h + $(INSTALL_DATA) $(srcdir)/jerror.h $(includedir)/jerror.h + +clean: + $(RM) *.o *.lo libjpeg.a libjpeg.la + $(RM) cjpeg djpeg jpegtran rdjpgcom wrjpgcom + $(RM) ansi2knr core testout* config.log config.status + $(RM) -r knr .libs _libs + +distclean: clean + $(RM) Makefile jconfig.h libtool config.cache + +test: cjpeg djpeg jpegtran + $(RM) testout* + ./djpeg -dct int -ppm -outfile testout.ppm $(srcdir)/testorig.jpg + ./djpeg -dct int -bmp -colors 256 -outfile testout.bmp $(srcdir)/testorig.jpg + ./cjpeg -dct int -outfile testout.jpg $(srcdir)/testimg.ppm + ./djpeg -dct int -ppm -outfile testoutp.ppm $(srcdir)/testprog.jpg + ./cjpeg -dct int -progressive -opt -outfile testoutp.jpg $(srcdir)/testimg.ppm + ./jpegtran -outfile testoutt.jpg $(srcdir)/testprog.jpg + cmp $(srcdir)/testimg.ppm testout.ppm + cmp $(srcdir)/testimg.bmp testout.bmp + cmp $(srcdir)/testimg.jpg testout.jpg + cmp $(srcdir)/testimg.ppm testoutp.ppm + cmp $(srcdir)/testimgp.jpg testoutp.jpg + cmp $(srcdir)/testorig.jpg testoutt.jpg + +check: test + +# Mistake catcher: + +jconfig.h: jconfig.doc + echo You must prepare a system-dependent jconfig.h file. + echo Please read the installation directions in install.doc. + exit 1 + +# GNU Make likes to know which target names are not really files to be made: +.PHONY: all install install-lib install-headers clean distclean test check + + +jcapimin.$(O): jcapimin.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcapistd.$(O): jcapistd.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jccoefct.$(O): jccoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jccolor.$(O): jccolor.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcdctmgr.$(O): jcdctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jchuff.$(O): jchuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jchuff.h +jcinit.$(O): jcinit.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcmainct.$(O): jcmainct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcmarker.$(O): jcmarker.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcmaster.$(O): jcmaster.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcomapi.$(O): jcomapi.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcparam.$(O): jcparam.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcphuff.$(O): jcphuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jchuff.h +jcprepct.$(O): jcprepct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcsample.$(O): jcsample.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jctrans.$(O): jctrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdapimin.$(O): jdapimin.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdapistd.$(O): jdapistd.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdatadst.$(O): jdatadst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h +jdatasrc.$(O): jdatasrc.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h +jdcoefct.$(O): jdcoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdcolor.$(O): jdcolor.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jddctmgr.$(O): jddctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jdhuff.$(O): jdhuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdhuff.h +jdinput.$(O): jdinput.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmainct.$(O): jdmainct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmarker.$(O): jdmarker.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmaster.$(O): jdmaster.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmerge.$(O): jdmerge.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdphuff.$(O): jdphuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdhuff.h +jdpostct.$(O): jdpostct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdsample.$(O): jdsample.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdtrans.$(O): jdtrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jerror.$(O): jerror.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jversion.h jerror.h +jfdctflt.$(O): jfdctflt.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jfdctfst.$(O): jfdctfst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jfdctint.$(O): jfdctint.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctflt.$(O): jidctflt.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctfst.$(O): jidctfst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctint.$(O): jidctint.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctred.$(O): jidctred.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jquant1.$(O): jquant1.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jquant2.$(O): jquant2.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jutils.$(O): jutils.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jmemmgr.$(O): jmemmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemansi.$(O): jmemansi.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemname.$(O): jmemname.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemnobs.$(O): jmemnobs.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemdos.$(O): jmemdos.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemmac.$(O): jmemmac.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +cjpeg.$(O): cjpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h jversion.h +djpeg.$(O): djpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h jversion.h +jpegtran.$(O): jpegtran.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h transupp.h jversion.h +rdjpgcom.$(O): rdjpgcom.c jinclude.h jconfig.h +wrjpgcom.$(O): wrjpgcom.c jinclude.h jconfig.h +cdjpeg.$(O): cdjpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdcolmap.$(O): rdcolmap.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdswitch.$(O): rdswitch.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +transupp.$(O): transupp.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h transupp.h +rdppm.$(O): rdppm.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrppm.$(O): wrppm.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdgif.$(O): rdgif.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrgif.$(O): wrgif.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdtarga.$(O): rdtarga.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrtarga.$(O): wrtarga.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdbmp.$(O): rdbmp.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrbmp.$(O): wrbmp.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdrle.$(O): rdrle.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrrle.$(O): wrrle.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h diff --git a/Engine/lib/ljpeg/extras/makefile.dj b/Engine/lib/ljpeg/extras/makefile.dj new file mode 100644 index 000000000..f766d25e3 --- /dev/null +++ b/Engine/lib/ljpeg/extras/makefile.dj @@ -0,0 +1,220 @@ +# Makefile for Independent JPEG Group's software + +# This makefile is for DJGPP (Delorie's GNU C port on MS-DOS), v2.0 or later. +# Thanks to Frank J. Donahoe for this version. + +# Read installation instructions before saying "make" !! + +# The name of your C compiler: +CC= gcc + +# You may need to adjust these cc options: +CFLAGS= -O2 -Wall -I. +# Generally, we recommend defining any configuration symbols in jconfig.h, +# NOT via -D switches here. + +# Link-time cc options: +LDFLAGS= -s + +# To link any special libraries, add the necessary -l commands here. +LDLIBS= + +# Put here the object file name for the correct system-dependent memory +# manager file. For DJGPP this is usually jmemnobs.o, but you could +# use jmemname.o if you want to use named temp files instead of swap space. +SYSDEPMEM= jmemnobs.o + +# miscellaneous OS-dependent stuff +# linker +LN= $(CC) +# file deletion command +RM= del +# library (.a) file creation command +AR= ar rc +# second step in .a creation (use "touch" if not needed) +AR2= ranlib + +# End of configurable options. + + +# source files: JPEG library proper +LIBSOURCES= jcapimin.c jcapistd.c jccoefct.c jccolor.c jcdctmgr.c jchuff.c \ + jcinit.c jcmainct.c jcmarker.c jcmaster.c jcomapi.c jcparam.c \ + jcphuff.c jcprepct.c jcsample.c jctrans.c jdapimin.c jdapistd.c \ + jdatadst.c jdatasrc.c jdcoefct.c jdcolor.c jddctmgr.c jdhuff.c \ + jdinput.c jdmainct.c jdmarker.c jdmaster.c jdmerge.c jdphuff.c \ + jdpostct.c jdsample.c jdtrans.c jerror.c jfdctflt.c jfdctfst.c \ + jfdctint.c jidctflt.c jidctfst.c jidctint.c jidctred.c jquant1.c \ + jquant2.c jutils.c jmemmgr.c +# memmgr back ends: compile only one of these into a working library +SYSDEPSOURCES= jmemansi.c jmemname.c jmemnobs.c jmemdos.c jmemmac.c +# source files: cjpeg/djpeg/jpegtran applications, also rdjpgcom/wrjpgcom +APPSOURCES= cjpeg.c djpeg.c jpegtran.c rdjpgcom.c wrjpgcom.c cdjpeg.c \ + rdcolmap.c rdswitch.c transupp.c rdppm.c wrppm.c rdgif.c wrgif.c \ + rdtarga.c wrtarga.c rdbmp.c wrbmp.c rdrle.c wrrle.c +SOURCES= $(LIBSOURCES) $(SYSDEPSOURCES) $(APPSOURCES) +# files included by source files +INCLUDES= jchuff.h jdhuff.h jdct.h jerror.h jinclude.h jmemsys.h jmorecfg.h \ + jpegint.h jpeglib.h jversion.h cdjpeg.h cderror.h transupp.h +# documentation, test, and support files +DOCS= README install.doc usage.doc cjpeg.1 djpeg.1 jpegtran.1 rdjpgcom.1 \ + wrjpgcom.1 wizard.doc example.c libjpeg.doc structure.doc \ + coderules.doc filelist.doc change.log +MKFILES= configure makefile.cfg makefile.ansi makefile.unix makefile.bcc \ + makefile.mc6 makefile.dj makefile.wat makefile.vc makelib.ds \ + makeapps.ds makeproj.mac makcjpeg.st makdjpeg.st makljpeg.st \ + maktjpeg.st makefile.manx makefile.sas makefile.mms makefile.vms \ + makvms.opt +CONFIGFILES= jconfig.cfg jconfig.bcc jconfig.mc6 jconfig.dj jconfig.wat \ + jconfig.vc jconfig.mac jconfig.st jconfig.manx jconfig.sas \ + jconfig.vms +CONFIGUREFILES= config.guess config.sub install-sh ltconfig ltmain.sh +OTHERFILES= jconfig.doc ckconfig.c ansi2knr.c ansi2knr.1 jmemdosa.asm +TESTFILES= testorig.jpg testimg.ppm testimg.bmp testimg.jpg testprog.jpg \ + testimgp.jpg +DISTFILES= $(DOCS) $(MKFILES) $(CONFIGFILES) $(SOURCES) $(INCLUDES) \ + $(CONFIGUREFILES) $(OTHERFILES) $(TESTFILES) +# library object files common to compression and decompression +COMOBJECTS= jcomapi.o jutils.o jerror.o jmemmgr.o $(SYSDEPMEM) +# compression library object files +CLIBOBJECTS= jcapimin.o jcapistd.o jctrans.o jcparam.o jdatadst.o jcinit.o \ + jcmaster.o jcmarker.o jcmainct.o jcprepct.o jccoefct.o jccolor.o \ + jcsample.o jchuff.o jcphuff.o jcdctmgr.o jfdctfst.o jfdctflt.o \ + jfdctint.o +# decompression library object files +DLIBOBJECTS= jdapimin.o jdapistd.o jdtrans.o jdatasrc.o jdmaster.o \ + jdinput.o jdmarker.o jdhuff.o jdphuff.o jdmainct.o jdcoefct.o \ + jdpostct.o jddctmgr.o jidctfst.o jidctflt.o jidctint.o jidctred.o \ + jdsample.o jdcolor.o jquant1.o jquant2.o jdmerge.o +# These objectfiles are included in libjpeg.a +LIBOBJECTS= $(CLIBOBJECTS) $(DLIBOBJECTS) $(COMOBJECTS) +# object files for sample applications (excluding library files) +COBJECTS= cjpeg.o rdppm.o rdgif.o rdtarga.o rdrle.o rdbmp.o rdswitch.o \ + cdjpeg.o +DOBJECTS= djpeg.o wrppm.o wrgif.o wrtarga.o wrrle.o wrbmp.o rdcolmap.o \ + cdjpeg.o +TROBJECTS= jpegtran.o rdswitch.o cdjpeg.o transupp.o + + +all: libjpeg.a cjpeg.exe djpeg.exe jpegtran.exe rdjpgcom.exe wrjpgcom.exe + +libjpeg.a: $(LIBOBJECTS) + $(RM) libjpeg.a + $(AR) libjpeg.a $(LIBOBJECTS) + $(AR2) libjpeg.a + +cjpeg.exe: $(COBJECTS) libjpeg.a + $(LN) $(LDFLAGS) -o cjpeg.exe $(COBJECTS) libjpeg.a $(LDLIBS) + +djpeg.exe: $(DOBJECTS) libjpeg.a + $(LN) $(LDFLAGS) -o djpeg.exe $(DOBJECTS) libjpeg.a $(LDLIBS) + +jpegtran.exe: $(TROBJECTS) libjpeg.a + $(LN) $(LDFLAGS) -o jpegtran.exe $(TROBJECTS) libjpeg.a $(LDLIBS) + +rdjpgcom.exe: rdjpgcom.o + $(LN) $(LDFLAGS) -o rdjpgcom.exe rdjpgcom.o $(LDLIBS) + +wrjpgcom.exe: wrjpgcom.o + $(LN) $(LDFLAGS) -o wrjpgcom.exe wrjpgcom.o $(LDLIBS) + +jconfig.h: jconfig.doc + echo You must prepare a system-dependent jconfig.h file. + echo Please read the installation directions in install.doc. + exit 1 + +clean: + $(RM) *.o + $(RM) cjpeg.exe + $(RM) djpeg.exe + $(RM) jpegtran.exe + $(RM) rdjpgcom.exe + $(RM) wrjpgcom.exe + $(RM) libjpeg.a + $(RM) testout*.* + +test: cjpeg.exe djpeg.exe jpegtran.exe + $(RM) testout*.* + ./djpeg -dct int -ppm -outfile testout.ppm testorig.jpg + ./djpeg -dct int -bmp -colors 256 -outfile testout.bmp testorig.jpg + ./cjpeg -dct int -outfile testout.jpg testimg.ppm + ./djpeg -dct int -ppm -outfile testoutp.ppm testprog.jpg + ./cjpeg -dct int -progressive -opt -outfile testoutp.jpg testimg.ppm + ./jpegtran -outfile testoutt.jpg testprog.jpg + fc /b testimg.ppm testout.ppm + fc /b testimg.bmp testout.bmp + fc /b testimg.jpg testout.jpg + fc /b testimg.ppm testoutp.ppm + fc /b testimgp.jpg testoutp.jpg + fc /b testorig.jpg testoutt.jpg + + +jcapimin.o: jcapimin.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcapistd.o: jcapistd.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jccoefct.o: jccoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jccolor.o: jccolor.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcdctmgr.o: jcdctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jchuff.o: jchuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jchuff.h +jcinit.o: jcinit.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcmainct.o: jcmainct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcmarker.o: jcmarker.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcmaster.o: jcmaster.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcomapi.o: jcomapi.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcparam.o: jcparam.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcphuff.o: jcphuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jchuff.h +jcprepct.o: jcprepct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcsample.o: jcsample.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jctrans.o: jctrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdapimin.o: jdapimin.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdapistd.o: jdapistd.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdatadst.o: jdatadst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h +jdatasrc.o: jdatasrc.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h +jdcoefct.o: jdcoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdcolor.o: jdcolor.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jddctmgr.o: jddctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jdhuff.o: jdhuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdhuff.h +jdinput.o: jdinput.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmainct.o: jdmainct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmarker.o: jdmarker.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmaster.o: jdmaster.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmerge.o: jdmerge.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdphuff.o: jdphuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdhuff.h +jdpostct.o: jdpostct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdsample.o: jdsample.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdtrans.o: jdtrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jerror.o: jerror.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jversion.h jerror.h +jfdctflt.o: jfdctflt.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jfdctfst.o: jfdctfst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jfdctint.o: jfdctint.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctflt.o: jidctflt.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctfst.o: jidctfst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctint.o: jidctint.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctred.o: jidctred.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jquant1.o: jquant1.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jquant2.o: jquant2.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jutils.o: jutils.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jmemmgr.o: jmemmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemansi.o: jmemansi.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemname.o: jmemname.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemnobs.o: jmemnobs.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemdos.o: jmemdos.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemmac.o: jmemmac.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +cjpeg.o: cjpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h jversion.h +djpeg.o: djpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h jversion.h +jpegtran.o: jpegtran.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h transupp.h jversion.h +rdjpgcom.o: rdjpgcom.c jinclude.h jconfig.h +wrjpgcom.o: wrjpgcom.c jinclude.h jconfig.h +cdjpeg.o: cdjpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdcolmap.o: rdcolmap.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdswitch.o: rdswitch.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +transupp.o: transupp.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h transupp.h +rdppm.o: rdppm.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrppm.o: wrppm.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdgif.o: rdgif.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrgif.o: wrgif.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdtarga.o: rdtarga.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrtarga.o: wrtarga.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdbmp.o: rdbmp.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrbmp.o: wrbmp.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdrle.o: rdrle.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrrle.o: wrrle.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h diff --git a/Engine/lib/ljpeg/extras/makefile.manx b/Engine/lib/ljpeg/extras/makefile.manx new file mode 100644 index 000000000..4cb42d17c --- /dev/null +++ b/Engine/lib/ljpeg/extras/makefile.manx @@ -0,0 +1,214 @@ +# Makefile for Independent JPEG Group's software + +# This makefile is for Amiga systems using Manx Aztec C ver 5.x. +# Thanks to D.J. James (djjames@cup.portal.com) for this version. + +# Read installation instructions before saying "make" !! + +# The name of your C compiler: +CC= cc + +# You may need to adjust these cc options: +# Uncomment for generic 68000 code (will work on any Amiga) +ARCHFLAGS= -sn + +# Uncomment for 68020/68030 code (faster, but won't run on 68000 CPU) +#ARCHFLAGS= -c2 + +CFLAGS= -MC -MD $(ARCHFLAGS) -spfam -r4 + +# Link-time cc options: +LDFLAGS= -g + +# To link any special libraries, add the necessary -l commands here. +LDLIBS= -lml -lcl + +# Put here the object file name for the correct system-dependent memory +# manager file. For Amiga we recommend jmemname.o. +SYSDEPMEM= jmemname.o + +# miscellaneous OS-dependent stuff +# linker +LN= ln +# file deletion command +RM= delete quiet +# library (.lib) file creation command +AR= lb + +# End of configurable options. + + +# source files: JPEG library proper +LIBSOURCES= jcapimin.c jcapistd.c jccoefct.c jccolor.c jcdctmgr.c jchuff.c \ + jcinit.c jcmainct.c jcmarker.c jcmaster.c jcomapi.c jcparam.c \ + jcphuff.c jcprepct.c jcsample.c jctrans.c jdapimin.c jdapistd.c \ + jdatadst.c jdatasrc.c jdcoefct.c jdcolor.c jddctmgr.c jdhuff.c \ + jdinput.c jdmainct.c jdmarker.c jdmaster.c jdmerge.c jdphuff.c \ + jdpostct.c jdsample.c jdtrans.c jerror.c jfdctflt.c jfdctfst.c \ + jfdctint.c jidctflt.c jidctfst.c jidctint.c jidctred.c jquant1.c \ + jquant2.c jutils.c jmemmgr.c +# memmgr back ends: compile only one of these into a working library +SYSDEPSOURCES= jmemansi.c jmemname.c jmemnobs.c jmemdos.c jmemmac.c +# source files: cjpeg/djpeg/jpegtran applications, also rdjpgcom/wrjpgcom +APPSOURCES= cjpeg.c djpeg.c jpegtran.c rdjpgcom.c wrjpgcom.c cdjpeg.c \ + rdcolmap.c rdswitch.c transupp.c rdppm.c wrppm.c rdgif.c wrgif.c \ + rdtarga.c wrtarga.c rdbmp.c wrbmp.c rdrle.c wrrle.c +SOURCES= $(LIBSOURCES) $(SYSDEPSOURCES) $(APPSOURCES) +# files included by source files +INCLUDES= jchuff.h jdhuff.h jdct.h jerror.h jinclude.h jmemsys.h jmorecfg.h \ + jpegint.h jpeglib.h jversion.h cdjpeg.h cderror.h transupp.h +# documentation, test, and support files +DOCS= README install.doc usage.doc cjpeg.1 djpeg.1 jpegtran.1 rdjpgcom.1 \ + wrjpgcom.1 wizard.doc example.c libjpeg.doc structure.doc \ + coderules.doc filelist.doc change.log +MKFILES= configure makefile.cfg makefile.ansi makefile.unix makefile.bcc \ + makefile.mc6 makefile.dj makefile.wat makefile.vc makelib.ds \ + makeapps.ds makeproj.mac makcjpeg.st makdjpeg.st makljpeg.st \ + maktjpeg.st makefile.manx makefile.sas makefile.mms makefile.vms \ + makvms.opt +CONFIGFILES= jconfig.cfg jconfig.bcc jconfig.mc6 jconfig.dj jconfig.wat \ + jconfig.vc jconfig.mac jconfig.st jconfig.manx jconfig.sas \ + jconfig.vms +CONFIGUREFILES= config.guess config.sub install-sh ltconfig ltmain.sh +OTHERFILES= jconfig.doc ckconfig.c ansi2knr.c ansi2knr.1 jmemdosa.asm +TESTFILES= testorig.jpg testimg.ppm testimg.bmp testimg.jpg testprog.jpg \ + testimgp.jpg +DISTFILES= $(DOCS) $(MKFILES) $(CONFIGFILES) $(SOURCES) $(INCLUDES) \ + $(CONFIGUREFILES) $(OTHERFILES) $(TESTFILES) +# library object files common to compression and decompression +COMOBJECTS= jcomapi.o jutils.o jerror.o jmemmgr.o $(SYSDEPMEM) +# compression library object files +CLIBOBJECTS= jcapimin.o jcapistd.o jctrans.o jcparam.o jdatadst.o jcinit.o \ + jcmaster.o jcmarker.o jcmainct.o jcprepct.o jccoefct.o jccolor.o \ + jcsample.o jchuff.o jcphuff.o jcdctmgr.o jfdctfst.o jfdctflt.o \ + jfdctint.o +# decompression library object files +DLIBOBJECTS= jdapimin.o jdapistd.o jdtrans.o jdatasrc.o jdmaster.o \ + jdinput.o jdmarker.o jdhuff.o jdphuff.o jdmainct.o jdcoefct.o \ + jdpostct.o jddctmgr.o jidctfst.o jidctflt.o jidctint.o jidctred.o \ + jdsample.o jdcolor.o jquant1.o jquant2.o jdmerge.o +# These objectfiles are included in libjpeg.lib +LIBOBJECTS= $(CLIBOBJECTS) $(DLIBOBJECTS) $(COMOBJECTS) +# object files for sample applications (excluding library files) +COBJECTS= cjpeg.o rdppm.o rdgif.o rdtarga.o rdrle.o rdbmp.o rdswitch.o \ + cdjpeg.o +DOBJECTS= djpeg.o wrppm.o wrgif.o wrtarga.o wrrle.o wrbmp.o rdcolmap.o \ + cdjpeg.o +TROBJECTS= jpegtran.o rdswitch.o cdjpeg.o transupp.o + + +all: libjpeg.lib cjpeg djpeg jpegtran rdjpgcom wrjpgcom + +libjpeg.lib: $(LIBOBJECTS) + -$(RM) libjpeg.lib + $(AR) libjpeg.lib $(LIBOBJECTS) + +cjpeg: $(COBJECTS) libjpeg.lib + $(LN) $(LDFLAGS) -o cjpeg $(COBJECTS) libjpeg.lib $(LDLIBS) + +djpeg: $(DOBJECTS) libjpeg.lib + $(LN) $(LDFLAGS) -o djpeg $(DOBJECTS) libjpeg.lib $(LDLIBS) + +jpegtran: $(TROBJECTS) libjpeg.lib + $(LN) $(LDFLAGS) -o jpegtran $(TROBJECTS) libjpeg.lib $(LDLIBS) + +rdjpgcom: rdjpgcom.o + $(LN) $(LDFLAGS) -o rdjpgcom rdjpgcom.o $(LDLIBS) + +wrjpgcom: wrjpgcom.o + $(LN) $(LDFLAGS) -o wrjpgcom wrjpgcom.o $(LDLIBS) + +jconfig.h: jconfig.doc + echo You must prepare a system-dependent jconfig.h file. + echo Please read the installation directions in install.doc. + exit 1 + +clean: + -$(RM) *.o cjpeg djpeg jpegtran libjpeg.lib rdjpgcom wrjpgcom + -$(RM) core testout*.* + +test: cjpeg djpeg jpegtran + -$(RM) testout*.* + djpeg -dct int -ppm -outfile testout.ppm testorig.jpg + djpeg -dct int -bmp -colors 256 -outfile testout.bmp testorig.jpg + cjpeg -dct int -outfile testout.jpg testimg.ppm + djpeg -dct int -ppm -outfile testoutp.ppm testprog.jpg + cjpeg -dct int -progressive -opt -outfile testoutp.jpg testimg.ppm + jpegtran -outfile testoutt.jpg testprog.jpg + cmp testimg.ppm testout.ppm + cmp testimg.bmp testout.bmp + cmp testimg.jpg testout.jpg + cmp testimg.ppm testoutp.ppm + cmp testimgp.jpg testoutp.jpg + cmp testorig.jpg testoutt.jpg + + +jcapimin.o: jcapimin.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcapistd.o: jcapistd.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jccoefct.o: jccoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jccolor.o: jccolor.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcdctmgr.o: jcdctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jchuff.o: jchuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jchuff.h +jcinit.o: jcinit.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcmainct.o: jcmainct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcmarker.o: jcmarker.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcmaster.o: jcmaster.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcomapi.o: jcomapi.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcparam.o: jcparam.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcphuff.o: jcphuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jchuff.h +jcprepct.o: jcprepct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcsample.o: jcsample.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jctrans.o: jctrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdapimin.o: jdapimin.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdapistd.o: jdapistd.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdatadst.o: jdatadst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h +jdatasrc.o: jdatasrc.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h +jdcoefct.o: jdcoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdcolor.o: jdcolor.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jddctmgr.o: jddctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jdhuff.o: jdhuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdhuff.h +jdinput.o: jdinput.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmainct.o: jdmainct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmarker.o: jdmarker.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmaster.o: jdmaster.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmerge.o: jdmerge.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdphuff.o: jdphuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdhuff.h +jdpostct.o: jdpostct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdsample.o: jdsample.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdtrans.o: jdtrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jerror.o: jerror.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jversion.h jerror.h +jfdctflt.o: jfdctflt.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jfdctfst.o: jfdctfst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jfdctint.o: jfdctint.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctflt.o: jidctflt.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctfst.o: jidctfst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctint.o: jidctint.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctred.o: jidctred.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jquant1.o: jquant1.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jquant2.o: jquant2.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jutils.o: jutils.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jmemmgr.o: jmemmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemansi.o: jmemansi.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemname.o: jmemname.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemnobs.o: jmemnobs.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemdos.o: jmemdos.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemmac.o: jmemmac.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +cjpeg.o: cjpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h jversion.h +djpeg.o: djpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h jversion.h +jpegtran.o: jpegtran.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h transupp.h jversion.h +rdjpgcom.o: rdjpgcom.c jinclude.h jconfig.h +wrjpgcom.o: wrjpgcom.c jinclude.h jconfig.h +cdjpeg.o: cdjpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdcolmap.o: rdcolmap.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdswitch.o: rdswitch.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +transupp.o: transupp.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h transupp.h +rdppm.o: rdppm.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrppm.o: wrppm.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdgif.o: rdgif.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrgif.o: wrgif.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdtarga.o: rdtarga.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrtarga.o: wrtarga.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdbmp.o: rdbmp.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrbmp.o: wrbmp.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdrle.o: rdrle.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrrle.o: wrrle.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h diff --git a/Engine/lib/ljpeg/extras/makefile.mc6 b/Engine/lib/ljpeg/extras/makefile.mc6 new file mode 100644 index 000000000..6aff05464 --- /dev/null +++ b/Engine/lib/ljpeg/extras/makefile.mc6 @@ -0,0 +1,249 @@ +# Makefile for Independent JPEG Group's software + +# This makefile is for Microsoft C for MS-DOS, version 6.00A and up. +# Use NMAKE, not Microsoft's brain-damaged MAKE. +# Thanks to Alan Wright and Chris Turner of Olivetti Research Ltd. + +# Read installation instructions before saying "nmake" !! + +# You may need to adjust these compiler options: +CFLAGS = -AM -Oecigt -Gs -W3 +# -AM medium memory model (or use -AS for small model, if you remove features) +# -Oecigt -Gs maximum safe optimisation (-Ol has bugs in MSC 6.00A) +# -W3 warning level 3 +# You might also want to add -G2 if you have an 80286, etc. +# Generally, we recommend defining any configuration symbols in jconfig.h, +# NOT via -D switches here. + +# Jan-Herman Buining suggests the following switches for MS C 8.0 and a 486: +# CFLAGS = /AM /f- /FPi87 /G3 /Gs /Gy /Ob1 /Oc /Oe /Og /Oi /Ol /On /Oo /Ot \ +# /OV4 /W3 +# except for jquant1.c, which must be compiled with /Oo- to avoid a compiler +# crash. + +# Ingar Steinsland suggests the following switches when building +# a 16-bit Windows DLL: +# CFLAGS = -ALw -Gsw -Zpe -W3 -O2 -Zi -Zd + +# Put here the object file name for the correct system-dependent memory +# manager file. For DOS, we recommend jmemdos.c and jmemdosa.asm. +# (But not for Windows; see install.doc if you use this makefile for Windows.) +SYSDEPMEM= jmemdos.obj jmemdosa.obj +# SYSDEPMEMLIB must list the same files with "+" signs for the librarian. +SYSDEPMEMLIB= +jmemdos.obj +jmemdosa.obj + +# End of configurable options. + + +# source files: JPEG library proper +LIBSOURCES= jcapimin.c jcapistd.c jccoefct.c jccolor.c jcdctmgr.c jchuff.c \ + jcinit.c jcmainct.c jcmarker.c jcmaster.c jcomapi.c jcparam.c \ + jcphuff.c jcprepct.c jcsample.c jctrans.c jdapimin.c jdapistd.c \ + jdatadst.c jdatasrc.c jdcoefct.c jdcolor.c jddctmgr.c jdhuff.c \ + jdinput.c jdmainct.c jdmarker.c jdmaster.c jdmerge.c jdphuff.c \ + jdpostct.c jdsample.c jdtrans.c jerror.c jfdctflt.c jfdctfst.c \ + jfdctint.c jidctflt.c jidctfst.c jidctint.c jidctred.c jquant1.c \ + jquant2.c jutils.c jmemmgr.c +# memmgr back ends: compile only one of these into a working library +SYSDEPSOURCES= jmemansi.c jmemname.c jmemnobs.c jmemdos.c jmemmac.c +# source files: cjpeg/djpeg/jpegtran applications, also rdjpgcom/wrjpgcom +APPSOURCES= cjpeg.c djpeg.c jpegtran.c rdjpgcom.c wrjpgcom.c cdjpeg.c \ + rdcolmap.c rdswitch.c transupp.c rdppm.c wrppm.c rdgif.c wrgif.c \ + rdtarga.c wrtarga.c rdbmp.c wrbmp.c rdrle.c wrrle.c +SOURCES= $(LIBSOURCES) $(SYSDEPSOURCES) $(APPSOURCES) +# files included by source files +INCLUDES= jchuff.h jdhuff.h jdct.h jerror.h jinclude.h jmemsys.h jmorecfg.h \ + jpegint.h jpeglib.h jversion.h cdjpeg.h cderror.h transupp.h +# documentation, test, and support files +DOCS= README install.doc usage.doc cjpeg.1 djpeg.1 jpegtran.1 rdjpgcom.1 \ + wrjpgcom.1 wizard.doc example.c libjpeg.doc structure.doc \ + coderules.doc filelist.doc change.log +MKFILES= configure makefile.cfg makefile.ansi makefile.unix makefile.bcc \ + makefile.mc6 makefile.dj makefile.wat makefile.vc makelib.ds \ + makeapps.ds makeproj.mac makcjpeg.st makdjpeg.st makljpeg.st \ + maktjpeg.st makefile.manx makefile.sas makefile.mms makefile.vms \ + makvms.opt +CONFIGFILES= jconfig.cfg jconfig.bcc jconfig.mc6 jconfig.dj jconfig.wat \ + jconfig.vc jconfig.mac jconfig.st jconfig.manx jconfig.sas \ + jconfig.vms +CONFIGUREFILES= config.guess config.sub install-sh ltconfig ltmain.sh +OTHERFILES= jconfig.doc ckconfig.c ansi2knr.c ansi2knr.1 jmemdosa.asm +TESTFILES= testorig.jpg testimg.ppm testimg.bmp testimg.jpg testprog.jpg \ + testimgp.jpg +DISTFILES= $(DOCS) $(MKFILES) $(CONFIGFILES) $(SOURCES) $(INCLUDES) \ + $(CONFIGUREFILES) $(OTHERFILES) $(TESTFILES) +# library object files common to compression and decompression +COMOBJECTS= jcomapi.obj jutils.obj jerror.obj jmemmgr.obj $(SYSDEPMEM) +# compression library object files +CLIBOBJECTS= jcapimin.obj jcapistd.obj jctrans.obj jcparam.obj jdatadst.obj \ + jcinit.obj jcmaster.obj jcmarker.obj jcmainct.obj jcprepct.obj \ + jccoefct.obj jccolor.obj jcsample.obj jchuff.obj jcphuff.obj \ + jcdctmgr.obj jfdctfst.obj jfdctflt.obj jfdctint.obj +# decompression library object files +DLIBOBJECTS= jdapimin.obj jdapistd.obj jdtrans.obj jdatasrc.obj \ + jdmaster.obj jdinput.obj jdmarker.obj jdhuff.obj jdphuff.obj \ + jdmainct.obj jdcoefct.obj jdpostct.obj jddctmgr.obj jidctfst.obj \ + jidctflt.obj jidctint.obj jidctred.obj jdsample.obj jdcolor.obj \ + jquant1.obj jquant2.obj jdmerge.obj +# These objectfiles are included in libjpeg.lib +LIBOBJECTS= $(CLIBOBJECTS) $(DLIBOBJECTS) $(COMOBJECTS) +# object files for sample applications (excluding library files) +COBJECTS= cjpeg.obj rdppm.obj rdgif.obj rdtarga.obj rdrle.obj rdbmp.obj \ + rdswitch.obj cdjpeg.obj +DOBJECTS= djpeg.obj wrppm.obj wrgif.obj wrtarga.obj wrrle.obj wrbmp.obj \ + rdcolmap.obj cdjpeg.obj +TROBJECTS= jpegtran.obj rdswitch.obj cdjpeg.obj transupp.obj + +# need linker response file because file list > 128 chars +RFILE = libjpeg.ans + + +all: libjpeg.lib cjpeg.exe djpeg.exe jpegtran.exe rdjpgcom.exe wrjpgcom.exe + +libjpeg.lib: $(LIBOBJECTS) $(RFILE) + del libjpeg.lib + lib @$(RFILE) + +# linker response file for building libjpeg.lib +$(RFILE) : makefile + del $(RFILE) + echo libjpeg.lib >$(RFILE) +# silly want-to-create-it prompt: + echo y >>$(RFILE) + echo +jcapimin.obj +jcapistd.obj +jctrans.obj +jcparam.obj & >>$(RFILE) + echo +jdatadst.obj +jcinit.obj +jcmaster.obj +jcmarker.obj & >>$(RFILE) + echo +jcmainct.obj +jcprepct.obj +jccoefct.obj & >>$(RFILE) + echo +jccolor.obj +jcsample.obj +jchuff.obj +jcphuff.obj & >>$(RFILE) + echo +jcdctmgr.obj +jfdctfst.obj +jfdctflt.obj & >>$(RFILE) + echo +jfdctint.obj +jdapimin.obj +jdapistd.obj & >>$(RFILE) + echo +jdtrans.obj +jdatasrc.obj +jdmaster.obj +jdinput.obj & >>$(RFILE) + echo +jdmarker.obj +jdhuff.obj +jdphuff.obj +jdmainct.obj & >>$(RFILE) + echo +jdcoefct.obj +jdpostct.obj +jddctmgr.obj & >>$(RFILE) + echo +jidctfst.obj +jidctflt.obj +jidctint.obj & >>$(RFILE) + echo +jidctred.obj +jdsample.obj +jdcolor.obj +jquant1.obj & >>$(RFILE) + echo +jquant2.obj +jdmerge.obj +jcomapi.obj +jutils.obj & >>$(RFILE) + echo +jerror.obj +jmemmgr.obj & >>$(RFILE) + echo $(SYSDEPMEMLIB) ; >>$(RFILE) + +cjpeg.exe: $(COBJECTS) libjpeg.lib + echo $(COBJECTS) >cjpeg.lst + link /STACK:4096 /EXEPACK @cjpeg.lst, cjpeg.exe, , libjpeg.lib, ; + del cjpeg.lst + +djpeg.exe: $(DOBJECTS) libjpeg.lib + echo $(DOBJECTS) >djpeg.lst + link /STACK:4096 /EXEPACK @djpeg.lst, djpeg.exe, , libjpeg.lib, ; + del djpeg.lst + +jpegtran.exe: $(TROBJECTS) libjpeg.lib + link /STACK:4096 /EXEPACK $(TROBJECTS), jpegtran.exe, , libjpeg.lib, ; + +rdjpgcom.exe: rdjpgcom.c + $(CC) -AS -O -W3 rdjpgcom.c + +# wrjpgcom needs large model so it can malloc a 64K chunk +wrjpgcom.exe: wrjpgcom.c + $(CC) -AL -O -W3 wrjpgcom.c + +jconfig.h: jconfig.doc + echo You must prepare a system-dependent jconfig.h file. + echo Please read the installation directions in install.doc. + exit 1 + +clean: + del *.obj + del libjpeg.lib + del cjpeg.exe + del djpeg.exe + del jpegtran.exe + del rdjpgcom.exe + del wrjpgcom.exe + del testout*.* + +test: cjpeg.exe djpeg.exe jpegtran.exe + del testout*.* + djpeg -dct int -ppm -outfile testout.ppm testorig.jpg + djpeg -dct int -bmp -colors 256 -outfile testout.bmp testorig.jpg + cjpeg -dct int -outfile testout.jpg testimg.ppm + djpeg -dct int -ppm -outfile testoutp.ppm testprog.jpg + cjpeg -dct int -progressive -opt -outfile testoutp.jpg testimg.ppm + jpegtran -outfile testoutt.jpg testprog.jpg + fc /b testimg.ppm testout.ppm + fc /b testimg.bmp testout.bmp + fc /b testimg.jpg testout.jpg + fc /b testimg.ppm testoutp.ppm + fc /b testimgp.jpg testoutp.jpg + fc /b testorig.jpg testoutt.jpg + + +jcapimin.obj: jcapimin.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcapistd.obj: jcapistd.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jccoefct.obj: jccoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jccolor.obj: jccolor.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcdctmgr.obj: jcdctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jchuff.obj: jchuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jchuff.h +jcinit.obj: jcinit.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcmainct.obj: jcmainct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcmarker.obj: jcmarker.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcmaster.obj: jcmaster.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcomapi.obj: jcomapi.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcparam.obj: jcparam.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcphuff.obj: jcphuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jchuff.h +jcprepct.obj: jcprepct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcsample.obj: jcsample.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jctrans.obj: jctrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdapimin.obj: jdapimin.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdapistd.obj: jdapistd.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdatadst.obj: jdatadst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h +jdatasrc.obj: jdatasrc.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h +jdcoefct.obj: jdcoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdcolor.obj: jdcolor.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jddctmgr.obj: jddctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jdhuff.obj: jdhuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdhuff.h +jdinput.obj: jdinput.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmainct.obj: jdmainct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmarker.obj: jdmarker.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmaster.obj: jdmaster.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmerge.obj: jdmerge.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdphuff.obj: jdphuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdhuff.h +jdpostct.obj: jdpostct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdsample.obj: jdsample.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdtrans.obj: jdtrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jerror.obj: jerror.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jversion.h jerror.h +jfdctflt.obj: jfdctflt.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jfdctfst.obj: jfdctfst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jfdctint.obj: jfdctint.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctflt.obj: jidctflt.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctfst.obj: jidctfst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctint.obj: jidctint.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctred.obj: jidctred.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jquant1.obj: jquant1.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jquant2.obj: jquant2.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jutils.obj: jutils.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jmemmgr.obj: jmemmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemansi.obj: jmemansi.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemname.obj: jmemname.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemnobs.obj: jmemnobs.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemdos.obj: jmemdos.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemmac.obj: jmemmac.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +cjpeg.obj: cjpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h jversion.h +djpeg.obj: djpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h jversion.h +jpegtran.obj: jpegtran.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h transupp.h jversion.h +rdjpgcom.obj: rdjpgcom.c jinclude.h jconfig.h +wrjpgcom.obj: wrjpgcom.c jinclude.h jconfig.h +cdjpeg.obj: cdjpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdcolmap.obj: rdcolmap.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdswitch.obj: rdswitch.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +transupp.obj: transupp.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h transupp.h +rdppm.obj: rdppm.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrppm.obj: wrppm.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdgif.obj: rdgif.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrgif.obj: wrgif.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdtarga.obj: rdtarga.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrtarga.obj: wrtarga.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdbmp.obj: rdbmp.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrbmp.obj: wrbmp.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdrle.obj: rdrle.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrrle.obj: wrrle.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +jmemdosa.obj : jmemdosa.asm + masm /mx $*; diff --git a/Engine/lib/ljpeg/extras/makefile.mms b/Engine/lib/ljpeg/extras/makefile.mms new file mode 100644 index 000000000..cf130e5b9 --- /dev/null +++ b/Engine/lib/ljpeg/extras/makefile.mms @@ -0,0 +1,218 @@ +# Makefile for Independent JPEG Group's software + +# This makefile is for use with MMS on Digital VMS systems. +# Thanks to Rick Dyson (dyson@iowasp.physics.uiowa.edu) +# and Tim Bell (tbell@netcom.com) for their help. + +# Read installation instructions before saying "MMS" !! + +# You may need to adjust these cc options: +CFLAGS= $(CFLAGS) /NoDebug /Optimize +# Generally, we recommend defining any configuration symbols in jconfig.h, +# NOT via /Define switches here. +.ifdef ALPHA +OPT= +.else +OPT= ,Sys$Disk:[]MAKVMS.OPT/Option +.endif + +# Put here the object file name for the correct system-dependent memory +# manager file. For Unix this is usually jmemnobs.o, but you may want +# to use jmemansi.o or jmemname.o if you have limited swap space. +SYSDEPMEM= jmemnobs.obj + +# End of configurable options. + + +# source files: JPEG library proper +LIBSOURCES= jcapimin.c jcapistd.c jccoefct.c jccolor.c jcdctmgr.c jchuff.c \ + jcinit.c jcmainct.c jcmarker.c jcmaster.c jcomapi.c jcparam.c \ + jcphuff.c jcprepct.c jcsample.c jctrans.c jdapimin.c jdapistd.c \ + jdatadst.c jdatasrc.c jdcoefct.c jdcolor.c jddctmgr.c jdhuff.c \ + jdinput.c jdmainct.c jdmarker.c jdmaster.c jdmerge.c jdphuff.c \ + jdpostct.c jdsample.c jdtrans.c jerror.c jfdctflt.c jfdctfst.c \ + jfdctint.c jidctflt.c jidctfst.c jidctint.c jidctred.c jquant1.c \ + jquant2.c jutils.c jmemmgr.c +# memmgr back ends: compile only one of these into a working library +SYSDEPSOURCES= jmemansi.c jmemname.c jmemnobs.c jmemdos.c jmemmac.c +# source files: cjpeg/djpeg/jpegtran applications, also rdjpgcom/wrjpgcom +APPSOURCES= cjpeg.c djpeg.c jpegtran.c rdjpgcom.c wrjpgcom.c cdjpeg.c \ + rdcolmap.c rdswitch.c transupp.c rdppm.c wrppm.c rdgif.c wrgif.c \ + rdtarga.c wrtarga.c rdbmp.c wrbmp.c rdrle.c wrrle.c +SOURCES= $(LIBSOURCES) $(SYSDEPSOURCES) $(APPSOURCES) +# files included by source files +INCLUDES= jchuff.h jdhuff.h jdct.h jerror.h jinclude.h jmemsys.h jmorecfg.h \ + jpegint.h jpeglib.h jversion.h cdjpeg.h cderror.h transupp.h +# documentation, test, and support files +DOCS= README install.doc usage.doc cjpeg.1 djpeg.1 jpegtran.1 rdjpgcom.1 \ + wrjpgcom.1 wizard.doc example.c libjpeg.doc structure.doc \ + coderules.doc filelist.doc change.log +MKFILES= configure makefile.cfg makefile.ansi makefile.unix makefile.bcc \ + makefile.mc6 makefile.dj makefile.wat makefile.vc makelib.ds \ + makeapps.ds makeproj.mac makcjpeg.st makdjpeg.st makljpeg.st \ + maktjpeg.st makefile.manx makefile.sas makefile.mms makefile.vms \ + makvms.opt +CONFIGFILES= jconfig.cfg jconfig.bcc jconfig.mc6 jconfig.dj jconfig.wat \ + jconfig.vc jconfig.mac jconfig.st jconfig.manx jconfig.sas \ + jconfig.vms +CONFIGUREFILES= config.guess config.sub install-sh ltconfig ltmain.sh +OTHERFILES= jconfig.doc ckconfig.c ansi2knr.c ansi2knr.1 jmemdosa.asm +TESTFILES= testorig.jpg testimg.ppm testimg.bmp testimg.jpg testprog.jpg \ + testimgp.jpg +DISTFILES= $(DOCS) $(MKFILES) $(CONFIGFILES) $(SOURCES) $(INCLUDES) \ + $(CONFIGUREFILES) $(OTHERFILES) $(TESTFILES) +# library object files common to compression and decompression +COMOBJECTS= jcomapi.obj jutils.obj jerror.obj jmemmgr.obj $(SYSDEPMEM) +# compression library object files +CLIBOBJECTS= jcapimin.obj jcapistd.obj jctrans.obj jcparam.obj jdatadst.obj \ + jcinit.obj jcmaster.obj jcmarker.obj jcmainct.obj jcprepct.obj \ + jccoefct.obj jccolor.obj jcsample.obj jchuff.obj jcphuff.obj \ + jcdctmgr.obj jfdctfst.obj jfdctflt.obj jfdctint.obj +# decompression library object files +DLIBOBJECTS= jdapimin.obj jdapistd.obj jdtrans.obj jdatasrc.obj \ + jdmaster.obj jdinput.obj jdmarker.obj jdhuff.obj jdphuff.obj \ + jdmainct.obj jdcoefct.obj jdpostct.obj jddctmgr.obj jidctfst.obj \ + jidctflt.obj jidctint.obj jidctred.obj jdsample.obj jdcolor.obj \ + jquant1.obj jquant2.obj jdmerge.obj +# These objectfiles are included in libjpeg.olb +LIBOBJECTS= $(CLIBOBJECTS) $(DLIBOBJECTS) $(COMOBJECTS) +# object files for sample applications (excluding library files) +COBJECTS= cjpeg.obj rdppm.obj rdgif.obj rdtarga.obj rdrle.obj rdbmp.obj \ + rdswitch.obj cdjpeg.obj +DOBJECTS= djpeg.obj wrppm.obj wrgif.obj wrtarga.obj wrrle.obj wrbmp.obj \ + rdcolmap.obj cdjpeg.obj +TROBJECTS= jpegtran.obj rdswitch.obj cdjpeg.obj transupp.obj +# objectfile lists with commas --- what a crock +COBJLIST= cjpeg.obj,rdppm.obj,rdgif.obj,rdtarga.obj,rdrle.obj,rdbmp.obj,\ + rdswitch.obj,cdjpeg.obj +DOBJLIST= djpeg.obj,wrppm.obj,wrgif.obj,wrtarga.obj,wrrle.obj,wrbmp.obj,\ + rdcolmap.obj,cdjpeg.obj +TROBJLIST= jpegtran.obj,rdswitch.obj,cdjpeg.obj,transupp.obj +LIBOBJLIST= jcapimin.obj,jcapistd.obj,jctrans.obj,jcparam.obj,jdatadst.obj,\ + jcinit.obj,jcmaster.obj,jcmarker.obj,jcmainct.obj,jcprepct.obj,\ + jccoefct.obj,jccolor.obj,jcsample.obj,jchuff.obj,jcphuff.obj,\ + jcdctmgr.obj,jfdctfst.obj,jfdctflt.obj,jfdctint.obj,jdapimin.obj,\ + jdapistd.obj,jdtrans.obj,jdatasrc.obj,jdmaster.obj,jdinput.obj,\ + jdmarker.obj,jdhuff.obj,jdphuff.obj,jdmainct.obj,jdcoefct.obj,\ + jdpostct.obj,jddctmgr.obj,jidctfst.obj,jidctflt.obj,jidctint.obj,\ + jidctred.obj,jdsample.obj,jdcolor.obj,jquant1.obj,jquant2.obj,\ + jdmerge.obj,jcomapi.obj,jutils.obj,jerror.obj,jmemmgr.obj,$(SYSDEPMEM) + + +.first + @- Define /NoLog Sys Sys$Library + +ALL : libjpeg.olb cjpeg.exe djpeg.exe jpegtran.exe rdjpgcom.exe wrjpgcom.exe + @ Continue + +libjpeg.olb : $(LIBOBJECTS) + Library /Create libjpeg.olb $(LIBOBJLIST) + +cjpeg.exe : $(COBJECTS) libjpeg.olb + $(LINK) $(LFLAGS) /Executable = cjpeg.exe $(COBJLIST),libjpeg.olb/Library$(OPT) + +djpeg.exe : $(DOBJECTS) libjpeg.olb + $(LINK) $(LFLAGS) /Executable = djpeg.exe $(DOBJLIST),libjpeg.olb/Library$(OPT) + +jpegtran.exe : $(TROBJECTS) libjpeg.olb + $(LINK) $(LFLAGS) /Executable = jpegtran.exe $(TROBJLIST),libjpeg.olb/Library$(OPT) + +rdjpgcom.exe : rdjpgcom.obj + $(LINK) $(LFLAGS) /Executable = rdjpgcom.exe rdjpgcom.obj$(OPT) + +wrjpgcom.exe : wrjpgcom.obj + $(LINK) $(LFLAGS) /Executable = wrjpgcom.exe wrjpgcom.obj$(OPT) + +jconfig.h : jconfig.vms + @- Copy jconfig.vms jconfig.h + +clean : + @- Set Protection = Owner:RWED *.*;-1 + @- Set Protection = Owner:RWED *.OBJ + - Purge /NoLog /NoConfirm *.* + - Delete /NoLog /NoConfirm *.OBJ; + +test : cjpeg.exe djpeg.exe jpegtran.exe + mcr sys$disk:[]djpeg -dct int -ppm -outfile testout.ppm testorig.jpg + mcr sys$disk:[]djpeg -dct int -bmp -colors 256 -outfile testout.bmp testorig.jpg + mcr sys$disk:[]cjpeg -dct int -outfile testout.jpg testimg.ppm + mcr sys$disk:[]djpeg -dct int -ppm -outfile testoutp.ppm testprog.jpg + mcr sys$disk:[]cjpeg -dct int -progressive -opt -outfile testoutp.jpg testimg.ppm + mcr sys$disk:[]jpegtran -outfile testoutt.jpg testprog.jpg + - Backup /Compare/Log testimg.ppm testout.ppm + - Backup /Compare/Log testimg.bmp testout.bmp + - Backup /Compare/Log testimg.jpg testout.jpg + - Backup /Compare/Log testimg.ppm testoutp.ppm + - Backup /Compare/Log testimgp.jpg testoutp.jpg + - Backup /Compare/Log testorig.jpg testoutt.jpg + + +jcapimin.obj : jcapimin.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcapistd.obj : jcapistd.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jccoefct.obj : jccoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jccolor.obj : jccolor.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcdctmgr.obj : jcdctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jchuff.obj : jchuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jchuff.h +jcinit.obj : jcinit.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcmainct.obj : jcmainct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcmarker.obj : jcmarker.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcmaster.obj : jcmaster.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcomapi.obj : jcomapi.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcparam.obj : jcparam.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcphuff.obj : jcphuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jchuff.h +jcprepct.obj : jcprepct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcsample.obj : jcsample.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jctrans.obj : jctrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdapimin.obj : jdapimin.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdapistd.obj : jdapistd.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdatadst.obj : jdatadst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h +jdatasrc.obj : jdatasrc.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h +jdcoefct.obj : jdcoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdcolor.obj : jdcolor.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jddctmgr.obj : jddctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jdhuff.obj : jdhuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdhuff.h +jdinput.obj : jdinput.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmainct.obj : jdmainct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmarker.obj : jdmarker.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmaster.obj : jdmaster.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmerge.obj : jdmerge.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdphuff.obj : jdphuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdhuff.h +jdpostct.obj : jdpostct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdsample.obj : jdsample.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdtrans.obj : jdtrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jerror.obj : jerror.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jversion.h jerror.h +jfdctflt.obj : jfdctflt.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jfdctfst.obj : jfdctfst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jfdctint.obj : jfdctint.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctflt.obj : jidctflt.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctfst.obj : jidctfst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctint.obj : jidctint.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctred.obj : jidctred.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jquant1.obj : jquant1.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jquant2.obj : jquant2.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jutils.obj : jutils.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jmemmgr.obj : jmemmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemansi.obj : jmemansi.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemname.obj : jmemname.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemnobs.obj : jmemnobs.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemdos.obj : jmemdos.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemmac.obj : jmemmac.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +cjpeg.obj : cjpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h jversion.h +djpeg.obj : djpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h jversion.h +jpegtran.obj : jpegtran.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h transupp.h jversion.h +rdjpgcom.obj : rdjpgcom.c jinclude.h jconfig.h +wrjpgcom.obj : wrjpgcom.c jinclude.h jconfig.h +cdjpeg.obj : cdjpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdcolmap.obj : rdcolmap.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdswitch.obj : rdswitch.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +transupp.obj : transupp.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h transupp.h +rdppm.obj : rdppm.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrppm.obj : wrppm.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdgif.obj : rdgif.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrgif.obj : wrgif.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdtarga.obj : rdtarga.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrtarga.obj : wrtarga.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdbmp.obj : rdbmp.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrbmp.obj : wrbmp.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdrle.obj : rdrle.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrrle.obj : wrrle.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h diff --git a/Engine/lib/ljpeg/extras/makefile.sas b/Engine/lib/ljpeg/extras/makefile.sas new file mode 100644 index 000000000..f296faf06 --- /dev/null +++ b/Engine/lib/ljpeg/extras/makefile.sas @@ -0,0 +1,252 @@ +# Makefile for Independent JPEG Group's software + +# This makefile is for Amiga systems using SAS C 6.0 and up. +# Thanks to Ed Hanway, Mark Rinfret, and Jim Zepeda. + +# Read installation instructions before saying "make" !! + +# The name of your C compiler: +CC= sc + +# You may need to adjust these cc options: +# Uncomment the following lines for generic 680x0 version +ARCHFLAGS= cpu=any +SUFFIX= + +# Uncomment the following lines for 68030-only version +#ARCHFLAGS= cpu=68030 +#SUFFIX=.030 + +CFLAGS= nostackcheck data=near parms=register optimize $(ARCHFLAGS) \ + ignore=104 ignore=304 ignore=306 +# ignore=104 disables warnings for mismatched const qualifiers +# ignore=304 disables warnings for variables being optimized out +# ignore=306 disables warnings for the inlining of functions +# Generally, we recommend defining any configuration symbols in jconfig.h, +# NOT via define switches here. + +# Link-time cc options: +LDFLAGS= SC SD ND BATCH + +# To link any special libraries, add the necessary commands here. +LDLIBS= LIB:scm.lib LIB:sc.lib + +# Put here the object file name for the correct system-dependent memory +# manager file. For Amiga we recommend jmemname.o. +SYSDEPMEM= jmemname.o + +# miscellaneous OS-dependent stuff +# linker +LN= slink +# file deletion command +RM= delete quiet +# library (.lib) file creation command +AR= oml + +# End of configurable options. + + +# source files: JPEG library proper +LIBSOURCES= jcapimin.c jcapistd.c jccoefct.c jccolor.c jcdctmgr.c jchuff.c \ + jcinit.c jcmainct.c jcmarker.c jcmaster.c jcomapi.c jcparam.c \ + jcphuff.c jcprepct.c jcsample.c jctrans.c jdapimin.c jdapistd.c \ + jdatadst.c jdatasrc.c jdcoefct.c jdcolor.c jddctmgr.c jdhuff.c \ + jdinput.c jdmainct.c jdmarker.c jdmaster.c jdmerge.c jdphuff.c \ + jdpostct.c jdsample.c jdtrans.c jerror.c jfdctflt.c jfdctfst.c \ + jfdctint.c jidctflt.c jidctfst.c jidctint.c jidctred.c jquant1.c \ + jquant2.c jutils.c jmemmgr.c +# memmgr back ends: compile only one of these into a working library +SYSDEPSOURCES= jmemansi.c jmemname.c jmemnobs.c jmemdos.c jmemmac.c +# source files: cjpeg/djpeg/jpegtran applications, also rdjpgcom/wrjpgcom +APPSOURCES= cjpeg.c djpeg.c jpegtran.c rdjpgcom.c wrjpgcom.c cdjpeg.c \ + rdcolmap.c rdswitch.c transupp.c rdppm.c wrppm.c rdgif.c wrgif.c \ + rdtarga.c wrtarga.c rdbmp.c wrbmp.c rdrle.c wrrle.c +SOURCES= $(LIBSOURCES) $(SYSDEPSOURCES) $(APPSOURCES) +# files included by source files +INCLUDES= jchuff.h jdhuff.h jdct.h jerror.h jinclude.h jmemsys.h jmorecfg.h \ + jpegint.h jpeglib.h jversion.h cdjpeg.h cderror.h transupp.h +# documentation, test, and support files +DOCS= README install.doc usage.doc cjpeg.1 djpeg.1 jpegtran.1 rdjpgcom.1 \ + wrjpgcom.1 wizard.doc example.c libjpeg.doc structure.doc \ + coderules.doc filelist.doc change.log +MKFILES= configure makefile.cfg makefile.ansi makefile.unix makefile.bcc \ + makefile.mc6 makefile.dj makefile.wat makefile.vc makelib.ds \ + makeapps.ds makeproj.mac makcjpeg.st makdjpeg.st makljpeg.st \ + maktjpeg.st makefile.manx makefile.sas makefile.mms makefile.vms \ + makvms.opt +CONFIGFILES= jconfig.cfg jconfig.bcc jconfig.mc6 jconfig.dj jconfig.wat \ + jconfig.vc jconfig.mac jconfig.st jconfig.manx jconfig.sas \ + jconfig.vms +CONFIGUREFILES= config.guess config.sub install-sh ltconfig ltmain.sh +OTHERFILES= jconfig.doc ckconfig.c ansi2knr.c ansi2knr.1 jmemdosa.asm +TESTFILES= testorig.jpg testimg.ppm testimg.bmp testimg.jpg testprog.jpg \ + testimgp.jpg +DISTFILES= $(DOCS) $(MKFILES) $(CONFIGFILES) $(SOURCES) $(INCLUDES) \ + $(CONFIGUREFILES) $(OTHERFILES) $(TESTFILES) +# library object files common to compression and decompression +COMOBJECTS= jcomapi.o jutils.o jerror.o jmemmgr.o $(SYSDEPMEM) +# compression library object files +CLIBOBJECTS= jcapimin.o jcapistd.o jctrans.o jcparam.o jdatadst.o jcinit.o \ + jcmaster.o jcmarker.o jcmainct.o jcprepct.o jccoefct.o jccolor.o \ + jcsample.o jchuff.o jcphuff.o jcdctmgr.o jfdctfst.o jfdctflt.o \ + jfdctint.o +# decompression library object files +DLIBOBJECTS= jdapimin.o jdapistd.o jdtrans.o jdatasrc.o jdmaster.o \ + jdinput.o jdmarker.o jdhuff.o jdphuff.o jdmainct.o jdcoefct.o \ + jdpostct.o jddctmgr.o jidctfst.o jidctflt.o jidctint.o jidctred.o \ + jdsample.o jdcolor.o jquant1.o jquant2.o jdmerge.o +# These objectfiles are included in libjpeg.lib +LIBOBJECTS= $(CLIBOBJECTS) $(DLIBOBJECTS) $(COMOBJECTS) +# object files for sample applications (excluding library files) +COBJECTS= cjpeg.o rdppm.o rdgif.o rdtarga.o rdrle.o rdbmp.o rdswitch.o \ + cdjpeg.o +DOBJECTS= djpeg.o wrppm.o wrgif.o wrtarga.o wrrle.o wrbmp.o rdcolmap.o \ + cdjpeg.o +TROBJECTS= jpegtran.o rdswitch.o cdjpeg.o transupp.o + + +all: libjpeg.lib cjpeg$(SUFFIX) djpeg$(SUFFIX) jpegtran$(SUFFIX) rdjpgcom$(SUFFIX) wrjpgcom$(SUFFIX) + +# note: do several AR steps to avoid command line length limitations + +libjpeg.lib: $(LIBOBJECTS) + -$(RM) libjpeg.lib + $(AR) libjpeg.lib r $(CLIBOBJECTS) + $(AR) libjpeg.lib r $(DLIBOBJECTS) + $(AR) libjpeg.lib r $(COMOBJECTS) + +cjpeg$(SUFFIX): $(COBJECTS) libjpeg.lib + $(LN) + +# You may want to adjust these compiler options: +CFLAGS= $(cflags) $(cdebug) $(cvars) -I. +# Generally, we recommend defining any configuration symbols in jconfig.h, +# NOT via -D switches here. + +# Link-time options: +LDFLAGS= $(ldebug) $(conlflags) + +# To link any special libraries, add the necessary commands here. +LDLIBS= $(conlibs) + +# Put here the object file name for the correct system-dependent memory +# manager file. For NT we suggest jmemnobs.obj, which expects the OS to +# provide adequate virtual memory. +SYSDEPMEM= jmemnobs.obj + +# miscellaneous OS-dependent stuff +# file deletion command +RM= del + +# End of configurable options. + + +# source files: JPEG library proper +LIBSOURCES= jcapimin.c jcapistd.c jccoefct.c jccolor.c jcdctmgr.c jchuff.c \ + jcinit.c jcmainct.c jcmarker.c jcmaster.c jcomapi.c jcparam.c \ + jcphuff.c jcprepct.c jcsample.c jctrans.c jdapimin.c jdapistd.c \ + jdatadst.c jdatasrc.c jdcoefct.c jdcolor.c jddctmgr.c jdhuff.c \ + jdinput.c jdmainct.c jdmarker.c jdmaster.c jdmerge.c jdphuff.c \ + jdpostct.c jdsample.c jdtrans.c jerror.c jfdctflt.c jfdctfst.c \ + jfdctint.c jidctflt.c jidctfst.c jidctint.c jidctred.c jquant1.c \ + jquant2.c jutils.c jmemmgr.c +# memmgr back ends: compile only one of these into a working library +SYSDEPSOURCES= jmemansi.c jmemname.c jmemnobs.c jmemdos.c jmemmac.c +# source files: cjpeg/djpeg/jpegtran applications, also rdjpgcom/wrjpgcom +APPSOURCES= cjpeg.c djpeg.c jpegtran.c rdjpgcom.c wrjpgcom.c cdjpeg.c \ + rdcolmap.c rdswitch.c transupp.c rdppm.c wrppm.c rdgif.c wrgif.c \ + rdtarga.c wrtarga.c rdbmp.c wrbmp.c rdrle.c wrrle.c +SOURCES= $(LIBSOURCES) $(SYSDEPSOURCES) $(APPSOURCES) +# files included by source files +INCLUDES= jchuff.h jdhuff.h jdct.h jerror.h jinclude.h jmemsys.h jmorecfg.h \ + jpegint.h jpeglib.h jversion.h cdjpeg.h cderror.h transupp.h +# documentation, test, and support files +DOCS= README install.doc usage.doc cjpeg.1 djpeg.1 jpegtran.1 rdjpgcom.1 \ + wrjpgcom.1 wizard.doc example.c libjpeg.doc structure.doc \ + coderules.doc filelist.doc change.log +MKFILES= configure makefile.cfg makefile.ansi makefile.unix makefile.bcc \ + makefile.mc6 makefile.dj makefile.wat makefile.vc makelib.ds \ + makeapps.ds makeproj.mac makcjpeg.st makdjpeg.st makljpeg.st \ + maktjpeg.st makefile.manx makefile.sas makefile.mms makefile.vms \ + makvms.opt +CONFIGFILES= jconfig.cfg jconfig.bcc jconfig.mc6 jconfig.dj jconfig.wat \ + jconfig.vc jconfig.mac jconfig.st jconfig.manx jconfig.sas \ + jconfig.vms +CONFIGUREFILES= config.guess config.sub install-sh ltconfig ltmain.sh +OTHERFILES= jconfig.doc ckconfig.c ansi2knr.c ansi2knr.1 jmemdosa.asm +TESTFILES= testorig.jpg testimg.ppm testimg.bmp testimg.jpg testprog.jpg \ + testimgp.jpg +DISTFILES= $(DOCS) $(MKFILES) $(CONFIGFILES) $(SOURCES) $(INCLUDES) \ + $(CONFIGUREFILES) $(OTHERFILES) $(TESTFILES) +# library object files common to compression and decompression +COMOBJECTS= jcomapi.obj jutils.obj jerror.obj jmemmgr.obj $(SYSDEPMEM) +# compression library object files +CLIBOBJECTS= jcapimin.obj jcapistd.obj jctrans.obj jcparam.obj jdatadst.obj \ + jcinit.obj jcmaster.obj jcmarker.obj jcmainct.obj jcprepct.obj \ + jccoefct.obj jccolor.obj jcsample.obj jchuff.obj jcphuff.obj \ + jcdctmgr.obj jfdctfst.obj jfdctflt.obj jfdctint.obj +# decompression library object files +DLIBOBJECTS= jdapimin.obj jdapistd.obj jdtrans.obj jdatasrc.obj \ + jdmaster.obj jdinput.obj jdmarker.obj jdhuff.obj jdphuff.obj \ + jdmainct.obj jdcoefct.obj jdpostct.obj jddctmgr.obj jidctfst.obj \ + jidctflt.obj jidctint.obj jidctred.obj jdsample.obj jdcolor.obj \ + jquant1.obj jquant2.obj jdmerge.obj +# These objectfiles are included in libjpeg.lib +LIBOBJECTS= $(CLIBOBJECTS) $(DLIBOBJECTS) $(COMOBJECTS) +# object files for sample applications (excluding library files) +COBJECTS= cjpeg.obj rdppm.obj rdgif.obj rdtarga.obj rdrle.obj rdbmp.obj \ + rdswitch.obj cdjpeg.obj +DOBJECTS= djpeg.obj wrppm.obj wrgif.obj wrtarga.obj wrrle.obj wrbmp.obj \ + rdcolmap.obj cdjpeg.obj +TROBJECTS= jpegtran.obj rdswitch.obj cdjpeg.obj transupp.obj + +# Template command for compiling .c to .obj +.c.obj: + $(cc) $(CFLAGS) $*.c + + +all: libjpeg.lib cjpeg.exe djpeg.exe jpegtran.exe rdjpgcom.exe wrjpgcom.exe + +libjpeg.lib: $(LIBOBJECTS) + $(RM) libjpeg.lib + lib -out:libjpeg.lib $(LIBOBJECTS) + +cjpeg.exe: $(COBJECTS) libjpeg.lib + $(link) $(LDFLAGS) -out:cjpeg.exe $(COBJECTS) libjpeg.lib $(LDLIBS) + +djpeg.exe: $(DOBJECTS) libjpeg.lib + $(link) $(LDFLAGS) -out:djpeg.exe $(DOBJECTS) libjpeg.lib $(LDLIBS) + +jpegtran.exe: $(TROBJECTS) libjpeg.lib + $(link) $(LDFLAGS) -out:jpegtran.exe $(TROBJECTS) libjpeg.lib $(LDLIBS) + +rdjpgcom.exe: rdjpgcom.obj + $(link) $(LDFLAGS) -out:rdjpgcom.exe rdjpgcom.obj $(LDLIBS) + +wrjpgcom.exe: wrjpgcom.obj + $(link) $(LDFLAGS) -out:wrjpgcom.exe wrjpgcom.obj $(LDLIBS) + + +clean: + $(RM) *.obj *.exe libjpeg.lib + $(RM) testout* + +test: cjpeg.exe djpeg.exe jpegtran.exe + $(RM) testout* + .\djpeg -dct int -ppm -outfile testout.ppm testorig.jpg + .\djpeg -dct int -bmp -colors 256 -outfile testout.bmp testorig.jpg + .\cjpeg -dct int -outfile testout.jpg testimg.ppm + .\djpeg -dct int -ppm -outfile testoutp.ppm testprog.jpg + .\cjpeg -dct int -progressive -opt -outfile testoutp.jpg testimg.ppm + .\jpegtran -outfile testoutt.jpg testprog.jpg + fc /b testimg.ppm testout.ppm + fc /b testimg.bmp testout.bmp + fc /b testimg.jpg testout.jpg + fc /b testimg.ppm testoutp.ppm + fc /b testimgp.jpg testoutp.jpg + fc /b testorig.jpg testoutt.jpg + + +jcapimin.obj: jcapimin.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcapistd.obj: jcapistd.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jccoefct.obj: jccoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jccolor.obj: jccolor.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcdctmgr.obj: jcdctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jchuff.obj: jchuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jchuff.h +jcinit.obj: jcinit.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcmainct.obj: jcmainct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcmarker.obj: jcmarker.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcmaster.obj: jcmaster.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcomapi.obj: jcomapi.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcparam.obj: jcparam.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcphuff.obj: jcphuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jchuff.h +jcprepct.obj: jcprepct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcsample.obj: jcsample.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jctrans.obj: jctrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdapimin.obj: jdapimin.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdapistd.obj: jdapistd.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdatadst.obj: jdatadst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h +jdatasrc.obj: jdatasrc.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h +jdcoefct.obj: jdcoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdcolor.obj: jdcolor.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jddctmgr.obj: jddctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jdhuff.obj: jdhuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdhuff.h +jdinput.obj: jdinput.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmainct.obj: jdmainct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmarker.obj: jdmarker.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmaster.obj: jdmaster.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmerge.obj: jdmerge.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdphuff.obj: jdphuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdhuff.h +jdpostct.obj: jdpostct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdsample.obj: jdsample.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdtrans.obj: jdtrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jerror.obj: jerror.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jversion.h jerror.h +jfdctflt.obj: jfdctflt.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jfdctfst.obj: jfdctfst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jfdctint.obj: jfdctint.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctflt.obj: jidctflt.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctfst.obj: jidctfst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctint.obj: jidctint.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctred.obj: jidctred.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jquant1.obj: jquant1.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jquant2.obj: jquant2.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jutils.obj: jutils.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jmemmgr.obj: jmemmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemansi.obj: jmemansi.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemname.obj: jmemname.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemnobs.obj: jmemnobs.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemdos.obj: jmemdos.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemmac.obj: jmemmac.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +cjpeg.obj: cjpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h jversion.h +djpeg.obj: djpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h jversion.h +jpegtran.obj: jpegtran.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h transupp.h jversion.h +rdjpgcom.obj: rdjpgcom.c jinclude.h jconfig.h +wrjpgcom.obj: wrjpgcom.c jinclude.h jconfig.h +cdjpeg.obj: cdjpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdcolmap.obj: rdcolmap.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdswitch.obj: rdswitch.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +transupp.obj: transupp.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h transupp.h +rdppm.obj: rdppm.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrppm.obj: wrppm.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdgif.obj: rdgif.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrgif.obj: wrgif.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdtarga.obj: rdtarga.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrtarga.obj: wrtarga.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdbmp.obj: rdbmp.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrbmp.obj: wrbmp.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdrle.obj: rdrle.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrrle.obj: wrrle.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h diff --git a/Engine/lib/ljpeg/extras/makefile.vms b/Engine/lib/ljpeg/extras/makefile.vms new file mode 100644 index 000000000..a42358d05 --- /dev/null +++ b/Engine/lib/ljpeg/extras/makefile.vms @@ -0,0 +1,142 @@ +$! Makefile for Independent JPEG Group's software +$! +$! This is a command procedure for Digital VMS systems that do not have MMS. +$! It builds the JPEG software by brute force, recompiling everything whether +$! or not it is necessary. It then runs the basic self-test. +$! Thanks to Rick Dyson (dyson@iowasp.physics.uiowa.edu) +$! and Tim Bell (tbell@netcom.com) for their help. +$! +$! Read installation instructions before running this!! +$! +$ If F$Mode () .eqs. "INTERACTIVE" +$ Then +$ VERIFY = F$Verify (0) +$ Else +$ VERIFY = F$Verify (1) +$ EndIf +$ On Control_Y Then GoTo End +$ On Error Then GoTo End +$ +$ If F$GetSyi ("HW_MODEL") .gt. 1023 +$ Then +$ OPT = "" +$ Else +$ OPT = ",Sys$Disk:[]makvms.opt/Option" +$ EndIf +$ +$ DoCompile := CC /NoDebug /Optimize /NoList +$! +$ DoCompile jcapimin.c +$ DoCompile jcapistd.c +$ DoCompile jctrans.c +$ DoCompile jcparam.c +$ DoCompile jdatadst.c +$ DoCompile jcinit.c +$ DoCompile jcmaster.c +$ DoCompile jcmarker.c +$ DoCompile jcmainct.c +$ DoCompile jcprepct.c +$ DoCompile jccoefct.c +$ DoCompile jccolor.c +$ DoCompile jcsample.c +$ DoCompile jchuff.c +$ DoCompile jcphuff.c +$ DoCompile jcdctmgr.c +$ DoCompile jfdctfst.c +$ DoCompile jfdctflt.c +$ DoCompile jfdctint.c +$ DoCompile jdapimin.c +$ DoCompile jdapistd.c +$ DoCompile jdtrans.c +$ DoCompile jdatasrc.c +$ DoCompile jdmaster.c +$ DoCompile jdinput.c +$ DoCompile jdmarker.c +$ DoCompile jdhuff.c +$ DoCompile jdphuff.c +$ DoCompile jdmainct.c +$ DoCompile jdcoefct.c +$ DoCompile jdpostct.c +$ DoCompile jddctmgr.c +$ DoCompile jidctfst.c +$ DoCompile jidctflt.c +$ DoCompile jidctint.c +$ DoCompile jidctred.c +$ DoCompile jdsample.c +$ DoCompile jdcolor.c +$ DoCompile jquant1.c +$ DoCompile jquant2.c +$ DoCompile jdmerge.c +$ DoCompile jcomapi.c +$ DoCompile jutils.c +$ DoCompile jerror.c +$ DoCompile jmemmgr.c +$ DoCompile jmemnobs.c +$! +$ Library /Create libjpeg.olb jcapimin.obj,jcapistd.obj,jctrans.obj, - + jcparam.obj,jdatadst.obj,jcinit.obj,jcmaster.obj,jcmarker.obj, - + jcmainct.obj,jcprepct.obj,jccoefct.obj,jccolor.obj,jcsample.obj, - + jchuff.obj,jcphuff.obj,jcdctmgr.obj,jfdctfst.obj,jfdctflt.obj, - + jfdctint.obj,jdapimin.obj,jdapistd.obj,jdtrans.obj,jdatasrc.obj, - + jdmaster.obj,jdinput.obj,jdmarker.obj,jdhuff.obj,jdphuff.obj, - + jdmainct.obj,jdcoefct.obj,jdpostct.obj,jddctmgr.obj,jidctfst.obj, - + jidctflt.obj,jidctint.obj,jidctred.obj,jdsample.obj,jdcolor.obj, - + jquant1.obj,jquant2.obj,jdmerge.obj,jcomapi.obj,jutils.obj, - + jerror.obj,jmemmgr.obj,jmemnobs.obj +$! +$ DoCompile cjpeg.c +$ DoCompile rdppm.c +$ DoCompile rdgif.c +$ DoCompile rdtarga.c +$ DoCompile rdrle.c +$ DoCompile rdbmp.c +$ DoCompile rdswitch.c +$ DoCompile cdjpeg.c +$! +$ Link /NoMap /Executable = cjpeg.exe cjpeg.obj,rdppm.obj,rdgif.obj, - + rdtarga.obj,rdrle.obj,rdbmp.obj,rdswitch.obj,cdjpeg.obj,libjpeg.olb/Library'OPT' +$! +$ DoCompile djpeg.c +$ DoCompile wrppm.c +$ DoCompile wrgif.c +$ DoCompile wrtarga.c +$ DoCompile wrrle.c +$ DoCompile wrbmp.c +$ DoCompile rdcolmap.c +$ DoCompile cdjpeg.c +$! +$ Link /NoMap /Executable = djpeg.exe djpeg.obj,wrppm.obj,wrgif.obj, - + wrtarga.obj,wrrle.obj,wrbmp.obj,rdcolmap.obj,cdjpeg.obj,libjpeg.olb/Library'OPT' +$! +$ DoCompile jpegtran.c +$ DoCompile rdswitch.c +$ DoCompile cdjpeg.c +$ DoCompile transupp.c +$! +$ Link /NoMap /Executable = jpegtran.exe jpegtran.obj,rdswitch.obj, - + cdjpeg.obj,transupp.obj,libjpeg.olb/Library'OPT' +$! +$ DoCompile rdjpgcom.c +$ Link /NoMap /Executable = rdjpgcom.exe rdjpgcom.obj'OPT' +$! +$ DoCompile wrjpgcom.c +$ Link /NoMap /Executable = wrjpgcom.exe wrjpgcom.obj'OPT' +$! +$! Run the self-test +$! +$ mcr sys$disk:[]djpeg -dct int -ppm -outfile testout.ppm testorig.jpg +$ mcr sys$disk:[]djpeg -dct int -bmp -colors 256 -outfile testout.bmp testorig.jpg +$ mcr sys$disk:[]cjpeg -dct int -outfile testout.jpg testimg.ppm +$ mcr sys$disk:[]djpeg -dct int -ppm -outfile testoutp.ppm testprog.jpg +$ mcr sys$disk:[]cjpeg -dct int -progressive -opt -outfile testoutp.jpg testimg.ppm +$ mcr sys$disk:[]jpegtran -outfile testoutt.jpg testprog.jpg +$ Backup /Compare/Log testimg.ppm testout.ppm +$ Backup /Compare/Log testimg.bmp testout.bmp +$ Backup /Compare/Log testimg.jpg testout.jpg +$ Backup /Compare/Log testimg.ppm testoutp.ppm +$ Backup /Compare/Log testimgp.jpg testoutp.jpg +$ Backup /Compare/Log testorig.jpg testoutt.jpg +$! +$End: +$ If Verify Then Set Verify +$ Exit diff --git a/Engine/lib/ljpeg/extras/makefile.wat b/Engine/lib/ljpeg/extras/makefile.wat new file mode 100644 index 000000000..d953e466f --- /dev/null +++ b/Engine/lib/ljpeg/extras/makefile.wat @@ -0,0 +1,233 @@ +# Makefile for Independent JPEG Group's software + +# This makefile is suitable for Watcom C/C++ 10.0 on MS-DOS (using +# dos4g extender), OS/2, and Windows NT console mode. +# Thanks to Janos Haide, jhaide@btrvtech.com. + +# Read installation instructions before saying "wmake" !! + +# Uncomment line for desired system +SYSTEM=DOS +#SYSTEM=OS2 +#SYSTEM=NT + +# The name of your C compiler: +CC= wcl386 + +# You may need to adjust these cc options: +CFLAGS= -4r -ort -wx -zq -bt=$(SYSTEM) +# Caution: avoid -ol or -ox; these generate bad code with 10.0 or 10.0a. +# Generally, we recommend defining any configuration symbols in jconfig.h, +# NOT via -D switches here. + +# Link-time cc options: +!ifeq SYSTEM DOS +LDFLAGS= -zq -l=dos4g +!else ifeq SYSTEM OS2 +LDFLAGS= -zq -l=os2v2 +!else ifeq SYSTEM NT +LDFLAGS= -zq -l=nt +!endif + +# Put here the object file name for the correct system-dependent memory +# manager file. jmemnobs should work fine for dos4g or OS/2 environment. +SYSDEPMEM= jmemnobs.obj + +# End of configurable options. + + +# source files: JPEG library proper +LIBSOURCES= jcapimin.c jcapistd.c jccoefct.c jccolor.c jcdctmgr.c jchuff.c & + jcinit.c jcmainct.c jcmarker.c jcmaster.c jcomapi.c jcparam.c & + jcphuff.c jcprepct.c jcsample.c jctrans.c jdapimin.c jdapistd.c & + jdatadst.c jdatasrc.c jdcoefct.c jdcolor.c jddctmgr.c jdhuff.c & + jdinput.c jdmainct.c jdmarker.c jdmaster.c jdmerge.c jdphuff.c & + jdpostct.c jdsample.c jdtrans.c jerror.c jfdctflt.c jfdctfst.c & + jfdctint.c jidctflt.c jidctfst.c jidctint.c jidctred.c jquant1.c & + jquant2.c jutils.c jmemmgr.c +# memmgr back ends: compile only one of these into a working library +SYSDEPSOURCES= jmemansi.c jmemname.c jmemnobs.c jmemdos.c jmemmac.c +# source files: cjpeg/djpeg/jpegtran applications, also rdjpgcom/wrjpgcom +APPSOURCES= cjpeg.c djpeg.c jpegtran.c rdjpgcom.c wrjpgcom.c cdjpeg.c & + rdcolmap.c rdswitch.c transupp.c rdppm.c wrppm.c rdgif.c wrgif.c & + rdtarga.c wrtarga.c rdbmp.c wrbmp.c rdrle.c wrrle.c +SOURCES= $(LIBSOURCES) $(SYSDEPSOURCES) $(APPSOURCES) +# files included by source files +INCLUDES= jchuff.h jdhuff.h jdct.h jerror.h jinclude.h jmemsys.h jmorecfg.h & + jpegint.h jpeglib.h jversion.h cdjpeg.h cderror.h transupp.h +# documentation, test, and support files +DOCS= README install.doc usage.doc cjpeg.1 djpeg.1 jpegtran.1 rdjpgcom.1 & + wrjpgcom.1 wizard.doc example.c libjpeg.doc structure.doc & + coderules.doc filelist.doc change.log +MKFILES= configure makefile.cfg makefile.ansi makefile.unix makefile.bcc & + makefile.mc6 makefile.dj makefile.wat makefile.vc makelib.ds & + makeapps.ds makeproj.mac makcjpeg.st makdjpeg.st makljpeg.st & + maktjpeg.st makefile.manx makefile.sas makefile.mms makefile.vms & + makvms.opt +CONFIGFILES= jconfig.cfg jconfig.bcc jconfig.mc6 jconfig.dj jconfig.wat & + jconfig.vc jconfig.mac jconfig.st jconfig.manx jconfig.sas & + jconfig.vms +CONFIGUREFILES= config.guess config.sub install-sh ltconfig ltmain.sh +OTHERFILES= jconfig.doc ckconfig.c ansi2knr.c ansi2knr.1 jmemdosa.asm +TESTFILES= testorig.jpg testimg.ppm testimg.bmp testimg.jpg testprog.jpg & + testimgp.jpg +DISTFILES= $(DOCS) $(MKFILES) $(CONFIGFILES) $(SOURCES) $(INCLUDES) & + $(CONFIGUREFILES) $(OTHERFILES) $(TESTFILES) +# library object files common to compression and decompression +COMOBJECTS= jcomapi.obj jutils.obj jerror.obj jmemmgr.obj $(SYSDEPMEM) +# compression library object files +CLIBOBJECTS= jcapimin.obj jcapistd.obj jctrans.obj jcparam.obj jdatadst.obj & + jcinit.obj jcmaster.obj jcmarker.obj jcmainct.obj jcprepct.obj & + jccoefct.obj jccolor.obj jcsample.obj jchuff.obj jcphuff.obj & + jcdctmgr.obj jfdctfst.obj jfdctflt.obj jfdctint.obj +# decompression library object files +DLIBOBJECTS= jdapimin.obj jdapistd.obj jdtrans.obj jdatasrc.obj & + jdmaster.obj jdinput.obj jdmarker.obj jdhuff.obj jdphuff.obj & + jdmainct.obj jdcoefct.obj jdpostct.obj jddctmgr.obj jidctfst.obj & + jidctflt.obj jidctint.obj jidctred.obj jdsample.obj jdcolor.obj & + jquant1.obj jquant2.obj jdmerge.obj +# These objectfiles are included in libjpeg.lib +LIBOBJECTS= $(CLIBOBJECTS) $(DLIBOBJECTS) $(COMOBJECTS) +# object files for sample applications (excluding library files) +COBJECTS= cjpeg.obj rdppm.obj rdgif.obj rdtarga.obj rdrle.obj rdbmp.obj & + rdswitch.obj cdjpeg.obj +DOBJECTS= djpeg.obj wrppm.obj wrgif.obj wrtarga.obj wrrle.obj wrbmp.obj & + rdcolmap.obj cdjpeg.obj +TROBJECTS= jpegtran.obj rdswitch.obj cdjpeg.obj transupp.obj + + +all: libjpeg.lib cjpeg.exe djpeg.exe jpegtran.exe rdjpgcom.exe wrjpgcom.exe + +libjpeg.lib: $(LIBOBJECTS) + - del libjpeg.lib + * wlib -n libjpeg.lib $(LIBOBJECTS) + +cjpeg.exe: $(COBJECTS) libjpeg.lib + $(CC) $(LDFLAGS) $(COBJECTS) libjpeg.lib + +djpeg.exe: $(DOBJECTS) libjpeg.lib + $(CC) $(LDFLAGS) $(DOBJECTS) libjpeg.lib + +jpegtran.exe: $(TROBJECTS) libjpeg.lib + $(CC) $(LDFLAGS) $(TROBJECTS) libjpeg.lib + +rdjpgcom.exe: rdjpgcom.c + $(CC) $(CFLAGS) $(LDFLAGS) rdjpgcom.c + +wrjpgcom.exe: wrjpgcom.c + $(CC) $(CFLAGS) $(LDFLAGS) wrjpgcom.c + +.c.obj: + $(CC) $(CFLAGS) -c $< + +jconfig.h: jconfig.doc + echo You must prepare a system-dependent jconfig.h file. + echo Please read the installation directions in install.doc. + exit 1 + +clean: .SYMBOLIC + - del *.obj + - del libjpeg.lib + - del cjpeg.exe + - del djpeg.exe + - del jpegtran.exe + - del rdjpgcom.exe + - del wrjpgcom.exe + - del testout*.* + +test: cjpeg.exe djpeg.exe jpegtran.exe .SYMBOLIC + - del testout*.* + djpeg -dct int -ppm -outfile testout.ppm testorig.jpg + djpeg -dct int -bmp -colors 256 -outfile testout.bmp testorig.jpg + cjpeg -dct int -outfile testout.jpg testimg.ppm + djpeg -dct int -ppm -outfile testoutp.ppm testprog.jpg + cjpeg -dct int -progressive -opt -outfile testoutp.jpg testimg.ppm + jpegtran -outfile testoutt.jpg testprog.jpg +!ifeq SYSTEM DOS + fc /b testimg.ppm testout.ppm + fc /b testimg.bmp testout.bmp + fc /b testimg.jpg testout.jpg + fc /b testimg.ppm testoutp.ppm + fc /b testimgp.jpg testoutp.jpg + fc /b testorig.jpg testoutt.jpg +!else + echo n > n.tmp + comp testimg.ppm testout.ppm < n.tmp + comp testimg.bmp testout.bmp < n.tmp + comp testimg.jpg testout.jpg < n.tmp + comp testimg.ppm testoutp.ppm < n.tmp + comp testimgp.jpg testoutp.jpg < n.tmp + comp testorig.jpg testoutt.jpg < n.tmp + del n.tmp +!endif + + +jcapimin.obj: jcapimin.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcapistd.obj: jcapistd.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jccoefct.obj: jccoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jccolor.obj: jccolor.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcdctmgr.obj: jcdctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jchuff.obj: jchuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jchuff.h +jcinit.obj: jcinit.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcmainct.obj: jcmainct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcmarker.obj: jcmarker.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcmaster.obj: jcmaster.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcomapi.obj: jcomapi.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcparam.obj: jcparam.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcphuff.obj: jcphuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jchuff.h +jcprepct.obj: jcprepct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jcsample.obj: jcsample.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jctrans.obj: jctrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdapimin.obj: jdapimin.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdapistd.obj: jdapistd.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdatadst.obj: jdatadst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h +jdatasrc.obj: jdatasrc.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h +jdcoefct.obj: jdcoefct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdcolor.obj: jdcolor.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jddctmgr.obj: jddctmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jdhuff.obj: jdhuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdhuff.h +jdinput.obj: jdinput.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmainct.obj: jdmainct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmarker.obj: jdmarker.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmaster.obj: jdmaster.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdmerge.obj: jdmerge.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdphuff.obj: jdphuff.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdhuff.h +jdpostct.obj: jdpostct.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdsample.obj: jdsample.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jdtrans.obj: jdtrans.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jerror.obj: jerror.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jversion.h jerror.h +jfdctflt.obj: jfdctflt.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jfdctfst.obj: jfdctfst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jfdctint.obj: jfdctint.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctflt.obj: jidctflt.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctfst.obj: jidctfst.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctint.obj: jidctint.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jidctred.obj: jidctred.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jdct.h +jquant1.obj: jquant1.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jquant2.obj: jquant2.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jutils.obj: jutils.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h +jmemmgr.obj: jmemmgr.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemansi.obj: jmemansi.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemname.obj: jmemname.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemnobs.obj: jmemnobs.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemdos.obj: jmemdos.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +jmemmac.obj: jmemmac.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h jmemsys.h +cjpeg.obj: cjpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h jversion.h +djpeg.obj: djpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h jversion.h +jpegtran.obj: jpegtran.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h transupp.h jversion.h +rdjpgcom.obj: rdjpgcom.c jinclude.h jconfig.h +wrjpgcom.obj: wrjpgcom.c jinclude.h jconfig.h +cdjpeg.obj: cdjpeg.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdcolmap.obj: rdcolmap.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdswitch.obj: rdswitch.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +transupp.obj: transupp.c jinclude.h jconfig.h jpeglib.h jmorecfg.h jpegint.h jerror.h transupp.h +rdppm.obj: rdppm.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrppm.obj: wrppm.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdgif.obj: rdgif.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrgif.obj: wrgif.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdtarga.obj: rdtarga.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrtarga.obj: wrtarga.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdbmp.obj: rdbmp.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrbmp.obj: wrbmp.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +rdrle.obj: rdrle.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h +wrrle.obj: wrrle.c cdjpeg.h jinclude.h jconfig.h jpeglib.h jmorecfg.h jerror.h cderror.h diff --git a/Engine/lib/ljpeg/extras/makelib.ds b/Engine/lib/ljpeg/extras/makelib.ds new file mode 100644 index 000000000..c7ad36d09 --- /dev/null +++ b/Engine/lib/ljpeg/extras/makelib.ds @@ -0,0 +1,1046 @@ +# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +!IF "$(CFG)" == "" +CFG=jpeg - Win32 +!MESSAGE No configuration specified. Defaulting to jpeg - Win32. +!ENDIF + +!IF "$(CFG)" != "jpeg - Win32" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE on this makefile +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "jpeg.mak" CFG="jpeg - Win32" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "jpeg - Win32" (based on "Win32 (x86) Static Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF +################################################################################ +# Begin Project +# PROP Target_Last_Scanned "jpeg - Win32" +CPP=cl.exe + +!IF "$(CFG)" == "jpeg - Win32" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +OUTDIR=.\Release +INTDIR=.\Release + +ALL : "$(OUTDIR)\jpeg.lib" + +CLEAN : + -@erase "$(INTDIR)\jcapimin.obj" + -@erase "$(INTDIR)\jcapistd.obj" + -@erase "$(INTDIR)\jctrans.obj" + -@erase "$(INTDIR)\jcparam.obj" + -@erase "$(INTDIR)\jdatadst.obj" + -@erase "$(INTDIR)\jcinit.obj" + -@erase "$(INTDIR)\jcmaster.obj" + -@erase "$(INTDIR)\jcmarker.obj" + -@erase "$(INTDIR)\jcmainct.obj" + -@erase "$(INTDIR)\jcprepct.obj" + -@erase "$(INTDIR)\jccoefct.obj" + -@erase "$(INTDIR)\jccolor.obj" + -@erase "$(INTDIR)\jcsample.obj" + -@erase "$(INTDIR)\jchuff.obj" + -@erase "$(INTDIR)\jcphuff.obj" + -@erase "$(INTDIR)\jcdctmgr.obj" + -@erase "$(INTDIR)\jfdctfst.obj" + -@erase "$(INTDIR)\jfdctflt.obj" + -@erase "$(INTDIR)\jfdctint.obj" + -@erase "$(INTDIR)\jdapimin.obj" + -@erase "$(INTDIR)\jdapistd.obj" + -@erase "$(INTDIR)\jdtrans.obj" + -@erase "$(INTDIR)\jdatasrc.obj" + -@erase "$(INTDIR)\jdmaster.obj" + -@erase "$(INTDIR)\jdinput.obj" + -@erase "$(INTDIR)\jdmarker.obj" + -@erase "$(INTDIR)\jdhuff.obj" + -@erase "$(INTDIR)\jdphuff.obj" + -@erase "$(INTDIR)\jdmainct.obj" + -@erase "$(INTDIR)\jdcoefct.obj" + -@erase "$(INTDIR)\jdpostct.obj" + -@erase "$(INTDIR)\jddctmgr.obj" + -@erase "$(INTDIR)\jidctfst.obj" + -@erase "$(INTDIR)\jidctflt.obj" + -@erase "$(INTDIR)\jidctint.obj" + -@erase "$(INTDIR)\jidctred.obj" + -@erase "$(INTDIR)\jdsample.obj" + -@erase "$(INTDIR)\jdcolor.obj" + -@erase "$(INTDIR)\jquant1.obj" + -@erase "$(INTDIR)\jquant2.obj" + -@erase "$(INTDIR)\jdmerge.obj" + -@erase "$(INTDIR)\jcomapi.obj" + -@erase "$(INTDIR)\jutils.obj" + -@erase "$(INTDIR)\jerror.obj" + -@erase "$(INTDIR)\jmemmgr.obj" + -@erase "$(INTDIR)\jmemnobs.obj" + -@erase "$(OUTDIR)\jpeg.lib" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +CPP_PROJ=/nologo /ML /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS"\ + /Fp"$(INTDIR)/jpeg.pch" /YX /Fo"$(INTDIR)/" /c +CPP_OBJS=.\Release/ +CPP_SBRS=.\. +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o"$(OUTDIR)/jpeg.bsc" +BSC32_SBRS= \ + +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo +LIB32_FLAGS=/nologo /out:"$(OUTDIR)/jpeg.lib" +LIB32_OBJS= \ + "$(INTDIR)\jcapimin.obj" \ + "$(INTDIR)\jcapistd.obj" \ + "$(INTDIR)\jctrans.obj" \ + "$(INTDIR)\jcparam.obj" \ + "$(INTDIR)\jdatadst.obj" \ + "$(INTDIR)\jcinit.obj" \ + "$(INTDIR)\jcmaster.obj" \ + "$(INTDIR)\jcmarker.obj" \ + "$(INTDIR)\jcmainct.obj" \ + "$(INTDIR)\jcprepct.obj" \ + "$(INTDIR)\jccoefct.obj" \ + "$(INTDIR)\jccolor.obj" \ + "$(INTDIR)\jcsample.obj" \ + "$(INTDIR)\jchuff.obj" \ + "$(INTDIR)\jcphuff.obj" \ + "$(INTDIR)\jcdctmgr.obj" \ + "$(INTDIR)\jfdctfst.obj" \ + "$(INTDIR)\jfdctflt.obj" \ + "$(INTDIR)\jfdctint.obj" \ + "$(INTDIR)\jdapimin.obj" \ + "$(INTDIR)\jdapistd.obj" \ + "$(INTDIR)\jdtrans.obj" \ + "$(INTDIR)\jdatasrc.obj" \ + "$(INTDIR)\jdmaster.obj" \ + "$(INTDIR)\jdinput.obj" \ + "$(INTDIR)\jdmarker.obj" \ + "$(INTDIR)\jdhuff.obj" \ + "$(INTDIR)\jdphuff.obj" \ + "$(INTDIR)\jdmainct.obj" \ + "$(INTDIR)\jdcoefct.obj" \ + "$(INTDIR)\jdpostct.obj" \ + "$(INTDIR)\jddctmgr.obj" \ + "$(INTDIR)\jidctfst.obj" \ + "$(INTDIR)\jidctflt.obj" \ + "$(INTDIR)\jidctint.obj" \ + "$(INTDIR)\jidctred.obj" \ + "$(INTDIR)\jdsample.obj" \ + "$(INTDIR)\jdcolor.obj" \ + "$(INTDIR)\jquant1.obj" \ + "$(INTDIR)\jquant2.obj" \ + "$(INTDIR)\jdmerge.obj" \ + "$(INTDIR)\jcomapi.obj" \ + "$(INTDIR)\jutils.obj" \ + "$(INTDIR)\jerror.obj" \ + "$(INTDIR)\jmemmgr.obj" \ + "$(INTDIR)\jmemnobs.obj" + +"$(OUTDIR)\jpeg.lib" : "$(OUTDIR)" $(DEF_FILE) $(LIB32_OBJS) + $(LIB32) @<< + $(LIB32_FLAGS) $(DEF_FLAGS) $(LIB32_OBJS) +<< + +!ENDIF + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.c{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_SBRS)}.sbr: + $(CPP) $(CPP_PROJ) $< + +################################################################################ +# Begin Target + +# Name "jpeg - Win32" + +!IF "$(CFG)" == "jpeg - Win32" + +!ENDIF + +################################################################################ +# Begin Source File + +SOURCE="jcapimin.c" +DEP_CPP_JCAPI=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jcapimin.obj" : $(SOURCE) $(DEP_CPP_JCAPI) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jcapistd.c" +DEP_CPP_JCAPIS=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jcapistd.obj" : $(SOURCE) $(DEP_CPP_JCAPIS) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jccoefct.c" +DEP_CPP_JCCOE=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jccoefct.obj" : $(SOURCE) $(DEP_CPP_JCCOE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jccolor.c" +DEP_CPP_JCCOL=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jccolor.obj" : $(SOURCE) $(DEP_CPP_JCCOL) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jcdctmgr.c" +DEP_CPP_JCDCT=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + "jdct.h"\ + + +"$(INTDIR)\jcdctmgr.obj" : $(SOURCE) $(DEP_CPP_JCDCT) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jchuff.c" +DEP_CPP_JCHUF=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + "jchuff.h"\ + + +"$(INTDIR)\jchuff.obj" : $(SOURCE) $(DEP_CPP_JCHUF) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jcinit.c" +DEP_CPP_JCINI=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jcinit.obj" : $(SOURCE) $(DEP_CPP_JCINI) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jcmainct.c" +DEP_CPP_JCMAI=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jcmainct.obj" : $(SOURCE) $(DEP_CPP_JCMAI) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jcmarker.c" +DEP_CPP_JCMAR=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jcmarker.obj" : $(SOURCE) $(DEP_CPP_JCMAR) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jcmaster.c" +DEP_CPP_JCMAS=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jcmaster.obj" : $(SOURCE) $(DEP_CPP_JCMAS) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jcomapi.c" +DEP_CPP_JCOMA=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jcomapi.obj" : $(SOURCE) $(DEP_CPP_JCOMA) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jcparam.c" +DEP_CPP_JCPAR=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jcparam.obj" : $(SOURCE) $(DEP_CPP_JCPAR) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jcphuff.c" +DEP_CPP_JCPHU=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + "jchuff.h"\ + + +"$(INTDIR)\jcphuff.obj" : $(SOURCE) $(DEP_CPP_JCPHU) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jcprepct.c" +DEP_CPP_JCPRE=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jcprepct.obj" : $(SOURCE) $(DEP_CPP_JCPRE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jcsample.c" +DEP_CPP_JCSAM=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jcsample.obj" : $(SOURCE) $(DEP_CPP_JCSAM) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jctrans.c" +DEP_CPP_JCTRA=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jctrans.obj" : $(SOURCE) $(DEP_CPP_JCTRA) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jdapimin.c" +DEP_CPP_JDAPI=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jdapimin.obj" : $(SOURCE) $(DEP_CPP_JDAPI) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jdapistd.c" +DEP_CPP_JDAPIS=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jdapistd.obj" : $(SOURCE) $(DEP_CPP_JDAPIS) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jdatadst.c" +DEP_CPP_JDATA=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jerror.h"\ + + +"$(INTDIR)\jdatadst.obj" : $(SOURCE) $(DEP_CPP_JDATA) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jdatasrc.c" +DEP_CPP_JDATAS=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jerror.h"\ + + +"$(INTDIR)\jdatasrc.obj" : $(SOURCE) $(DEP_CPP_JDATAS) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jdcoefct.c" +DEP_CPP_JDCOE=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jdcoefct.obj" : $(SOURCE) $(DEP_CPP_JDCOE) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jdcolor.c" +DEP_CPP_JDCOL=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jdcolor.obj" : $(SOURCE) $(DEP_CPP_JDCOL) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jddctmgr.c" +DEP_CPP_JDDCT=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + "jdct.h"\ + + +"$(INTDIR)\jddctmgr.obj" : $(SOURCE) $(DEP_CPP_JDDCT) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jdhuff.c" +DEP_CPP_JDHUF=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + "jdhuff.h"\ + + +"$(INTDIR)\jdhuff.obj" : $(SOURCE) $(DEP_CPP_JDHUF) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jdinput.c" +DEP_CPP_JDINP=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jdinput.obj" : $(SOURCE) $(DEP_CPP_JDINP) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jdmainct.c" +DEP_CPP_JDMAI=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jdmainct.obj" : $(SOURCE) $(DEP_CPP_JDMAI) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jdmarker.c" +DEP_CPP_JDMAR=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jdmarker.obj" : $(SOURCE) $(DEP_CPP_JDMAR) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jdmaster.c" +DEP_CPP_JDMAS=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jdmaster.obj" : $(SOURCE) $(DEP_CPP_JDMAS) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jdmerge.c" +DEP_CPP_JDMER=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jdmerge.obj" : $(SOURCE) $(DEP_CPP_JDMER) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jdphuff.c" +DEP_CPP_JDPHU=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + "jdhuff.h"\ + + +"$(INTDIR)\jdphuff.obj" : $(SOURCE) $(DEP_CPP_JDPHU) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jdpostct.c" +DEP_CPP_JDPOS=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jdpostct.obj" : $(SOURCE) $(DEP_CPP_JDPOS) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jdsample.c" +DEP_CPP_JDSAM=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jdsample.obj" : $(SOURCE) $(DEP_CPP_JDSAM) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jdtrans.c" +DEP_CPP_JDTRA=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jdtrans.obj" : $(SOURCE) $(DEP_CPP_JDTRA) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jerror.c" +DEP_CPP_JERRO=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jversion.h"\ + "jerror.h"\ + + +"$(INTDIR)\jerror.obj" : $(SOURCE) $(DEP_CPP_JERRO) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jfdctflt.c" +DEP_CPP_JFDCT=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + "jdct.h"\ + + +"$(INTDIR)\jfdctflt.obj" : $(SOURCE) $(DEP_CPP_JFDCT) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jfdctfst.c" +DEP_CPP_JFDCTF=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + "jdct.h"\ + + +"$(INTDIR)\jfdctfst.obj" : $(SOURCE) $(DEP_CPP_JFDCTF) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jfdctint.c" +DEP_CPP_JFDCTI=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + "jdct.h"\ + + +"$(INTDIR)\jfdctint.obj" : $(SOURCE) $(DEP_CPP_JFDCTI) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jidctflt.c" +DEP_CPP_JIDCT=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + "jdct.h"\ + + +"$(INTDIR)\jidctflt.obj" : $(SOURCE) $(DEP_CPP_JIDCT) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jidctfst.c" +DEP_CPP_JIDCTF=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + "jdct.h"\ + + +"$(INTDIR)\jidctfst.obj" : $(SOURCE) $(DEP_CPP_JIDCTF) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jidctint.c" +DEP_CPP_JIDCTI=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + "jdct.h"\ + + +"$(INTDIR)\jidctint.obj" : $(SOURCE) $(DEP_CPP_JIDCTI) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jidctred.c" +DEP_CPP_JIDCTR=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + "jdct.h"\ + + +"$(INTDIR)\jidctred.obj" : $(SOURCE) $(DEP_CPP_JIDCTR) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jquant1.c" +DEP_CPP_JQUAN=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jquant1.obj" : $(SOURCE) $(DEP_CPP_JQUAN) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jquant2.c" +DEP_CPP_JQUANT=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jquant2.obj" : $(SOURCE) $(DEP_CPP_JQUANT) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jutils.c" +DEP_CPP_JUTIL=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + + +"$(INTDIR)\jutils.obj" : $(SOURCE) $(DEP_CPP_JUTIL) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jmemmgr.c" +DEP_CPP_JMEMM=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + "jmemsys.h"\ + + +"$(INTDIR)\jmemmgr.obj" : $(SOURCE) $(DEP_CPP_JMEMM) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +################################################################################ +# Begin Source File + +SOURCE="jmemnobs.c" +DEP_CPP_JMEMN=\ + "jinclude.h"\ + "jconfig.h"\ + "jpeglib.h"\ + "jmorecfg.h"\ + "jpegint.h"\ + "jerror.h"\ + "jmemsys.h"\ + + +"$(INTDIR)\jmemnobs.obj" : $(SOURCE) $(DEP_CPP_JMEMN) "$(INTDIR)" + $(CPP) $(CPP_PROJ) $(SOURCE) + + +# End Source File +# End Target +# End Project +################################################################################ + diff --git a/Engine/lib/ljpeg/extras/makeproj.mac b/Engine/lib/ljpeg/extras/makeproj.mac new file mode 100644 index 000000000..ed277c83d --- /dev/null +++ b/Engine/lib/ljpeg/extras/makeproj.mac @@ -0,0 +1,213 @@ +-- +-- makeproj.mac +-- +-- This AppleScript builds Code Warrior PRO Release 2 project files for the +-- libjpeg library as well as the test programs 'cjpeg', 'djpeg', 'jpegtran'. +-- (We'd distribute real project files, except they're not text +-- and would create maintenance headaches.) +-- +-- The script then compiles and links the library and the test programs. +-- NOTE: if you haven't already created a 'jconfig.h' file, the script +-- automatically copies 'jconfig.mac' to 'jconfig.h'. +-- +-- To use this script, you must have AppleScript 1.1 or later installed +-- and a suitable AppleScript editor like Script Editor or Script Debugger +-- (http://www.latenightsw.com). Open this file with your AppleScript +-- editor and execute the "run" command to build the projects. +-- +-- Thanks to Dan Sears and Don Agro for this script. +-- Questions about this script can be addressed to dogpark@interlog.com +-- + +on run + + choose folder with prompt ">>> Select IJG source folder <<<" + set ijg_folder to result + + choose folder with prompt ">>> Select MetroWerks folder <<<" + set cw_folder to result + + -- if jconfig.h doesn't already exist, copy jconfig.mac + + tell application "Finder" + if not (exists file "jconfig.h" of ijg_folder) then + duplicate {file "jconfig.mac" of folder ijg_folder} + select file "jconfig.mac copy" of folder ijg_folder + set name of selection to "jconfig.h" + end if + end tell + + tell application "CodeWarrior IDE 2.1" + with timeout of 10000 seconds + + -- create libjpeg project + + activate + Create Project (ijg_folder as string) & "libjpeg.proj" + Set Preferences of panel "Target Settings" to {Target Name:"libjpeg"} + Set Preferences of panel "PPC Project" to {File Name:"libjpeg"} + Set Preferences of panel "Target Settings" to {Linker:"MacOS PPC Linker"} + Set Preferences of panel "PPC Project" to {Project Type:library} + Set Preferences of panel "C/C++ Compiler" to {ANSI Strict:true} + Set Preferences of panel "C/C++ Compiler" to {Enums Always Ints:true} + Set Preferences of panel "PPC Codegen" to {Struct Alignment:PowerPC} + Set Preferences of panel "PPC Linker" to {Generate SYM File:false} + + Add Files (ijg_folder as string) & "jcapimin.c" To Segment 1 + Add Files (ijg_folder as string) & "jcapistd.c" To Segment 1 + Add Files (ijg_folder as string) & "jctrans.c" To Segment 1 + Add Files (ijg_folder as string) & "jcparam.c" To Segment 1 + Add Files (ijg_folder as string) & "jdatadst.c" To Segment 1 + Add Files (ijg_folder as string) & "jcinit.c" To Segment 1 + Add Files (ijg_folder as string) & "jcmaster.c" To Segment 1 + Add Files (ijg_folder as string) & "jcmarker.c" To Segment 1 + Add Files (ijg_folder as string) & "jcmainct.c" To Segment 1 + Add Files (ijg_folder as string) & "jcprepct.c" To Segment 1 + Add Files (ijg_folder as string) & "jccoefct.c" To Segment 1 + Add Files (ijg_folder as string) & "jccolor.c" To Segment 1 + Add Files (ijg_folder as string) & "jcsample.c" To Segment 1 + Add Files (ijg_folder as string) & "jchuff.c" To Segment 1 + Add Files (ijg_folder as string) & "jcphuff.c" To Segment 1 + Add Files (ijg_folder as string) & "jcdctmgr.c" To Segment 1 + Add Files (ijg_folder as string) & "jfdctfst.c" To Segment 1 + Add Files (ijg_folder as string) & "jfdctflt.c" To Segment 1 + Add Files (ijg_folder as string) & "jfdctint.c" To Segment 1 + Add Files (ijg_folder as string) & "jdapimin.c" To Segment 1 + Add Files (ijg_folder as string) & "jdapistd.c" To Segment 1 + Add Files (ijg_folder as string) & "jdtrans.c" To Segment 1 + Add Files (ijg_folder as string) & "jdatasrc.c" To Segment 1 + Add Files (ijg_folder as string) & "jdmaster.c" To Segment 1 + Add Files (ijg_folder as string) & "jdinput.c" To Segment 1 + Add Files (ijg_folder as string) & "jdmarker.c" To Segment 1 + Add Files (ijg_folder as string) & "jdhuff.c" To Segment 1 + Add Files (ijg_folder as string) & "jdphuff.c" To Segment 1 + Add Files (ijg_folder as string) & "jdmainct.c" To Segment 1 + Add Files (ijg_folder as string) & "jdcoefct.c" To Segment 1 + Add Files (ijg_folder as string) & "jdpostct.c" To Segment 1 + Add Files (ijg_folder as string) & "jddctmgr.c" To Segment 1 + Add Files (ijg_folder as string) & "jidctfst.c" To Segment 1 + Add Files (ijg_folder as string) & "jidctflt.c" To Segment 1 + Add Files (ijg_folder as string) & "jidctint.c" To Segment 1 + Add Files (ijg_folder as string) & "jidctred.c" To Segment 1 + Add Files (ijg_folder as string) & "jdsample.c" To Segment 1 + Add Files (ijg_folder as string) & "jdcolor.c" To Segment 1 + Add Files (ijg_folder as string) & "jquant1.c" To Segment 1 + Add Files (ijg_folder as string) & "jquant2.c" To Segment 1 + Add Files (ijg_folder as string) & "jdmerge.c" To Segment 1 + Add Files (ijg_folder as string) & "jcomapi.c" To Segment 1 + Add Files (ijg_folder as string) & "jutils.c" To Segment 1 + Add Files (ijg_folder as string) & "jerror.c" To Segment 1 + Add Files (ijg_folder as string) & "jmemmgr.c" To Segment 1 + Add Files (ijg_folder as string) & "jmemmac.c" To Segment 1 + + -- compile and link the library + + Make Project + Close Project + + -- create cjpeg project + + activate + Create Project (ijg_folder as string) & "cjpeg.proj" + Set Preferences of panel "Target Settings" to {Target Name:"cjpeg"} + Set Preferences of panel "PPC Project" to {File Name:"cjpeg"} + Set Preferences of panel "Target Settings" to {Linker:"MacOS PPC Linker"} + Set Preferences of panel "C/C++ Compiler" to {ANSI Strict:true} + Set Preferences of panel "C/C++ Compiler" to {Enums Always Ints:true} + Set Preferences of panel "PPC Codegen" to {Struct Alignment:PowerPC} + Set Preferences of panel "PPC Linker" to {Generate SYM File:false} + + Add Files (ijg_folder as string) & "cjpeg.c" To Segment 1 + Add Files (ijg_folder as string) & "rdppm.c" To Segment 1 + Add Files (ijg_folder as string) & "rdgif.c" To Segment 1 + Add Files (ijg_folder as string) & "rdtarga.c" To Segment 1 + Add Files (ijg_folder as string) & "rdrle.c" To Segment 1 + Add Files (ijg_folder as string) & "rdbmp.c" To Segment 1 + Add Files (ijg_folder as string) & "rdswitch.c" To Segment 1 + Add Files (ijg_folder as string) & "cdjpeg.c" To Segment 1 + + Add Files (ijg_folder as string) & "libjpeg" To Segment 2 + + Add Files (cw_folder as string) & "Metrowerks CodeWarrior:Metrowerks Standard Library:MSL C:Bin:MSL C.PPC.Lib" To Segment 3 + Add Files (cw_folder as string) & "Metrowerks CodeWarrior:Metrowerks Standard Library:MSL C:Bin:MSL SIOUX.PPC.Lib" To Segment 3 + Add Files (cw_folder as string) & "Metrowerks CodeWarrior:MacOS Support:Libraries:Runtime:Runtime PPC:MSL RuntimePPC.Lib" To Segment 3 + + Add Files (cw_folder as string) & "Metrowerks CodeWarrior:MacOS Support:Libraries:MacOS Common:InterfaceLib" To Segment 4 + Add Files (cw_folder as string) & "Metrowerks CodeWarrior:MacOS Support:Libraries:MacOS Common:MathLib" To Segment 4 + + -- compile and link cjpeg + + Make Project + Close Project + + -- create djpeg project + + activate + Create Project (ijg_folder as string) & "djpeg.proj" + Set Preferences of panel "Target Settings" to {Target Name:"djpeg"} + Set Preferences of panel "PPC Project" to {File Name:"djpeg"} + Set Preferences of panel "Target Settings" to {Linker:"MacOS PPC Linker"} + Set Preferences of panel "C/C++ Compiler" to {ANSI Strict:true} + Set Preferences of panel "C/C++ Compiler" to {Enums Always Ints:true} + Set Preferences of panel "PPC Codegen" to {Struct Alignment:PowerPC} + Set Preferences of panel "PPC Linker" to {Generate SYM File:false} + + Add Files (ijg_folder as string) & "djpeg.c" To Segment 1 + Add Files (ijg_folder as string) & "wrppm.c" To Segment 1 + Add Files (ijg_folder as string) & "wrgif.c" To Segment 1 + Add Files (ijg_folder as string) & "wrtarga.c" To Segment 1 + Add Files (ijg_folder as string) & "wrrle.c" To Segment 1 + Add Files (ijg_folder as string) & "wrbmp.c" To Segment 1 + Add Files (ijg_folder as string) & "rdcolmap.c" To Segment 1 + Add Files (ijg_folder as string) & "cdjpeg.c" To Segment 1 + + Add Files (ijg_folder as string) & "libjpeg" To Segment 2 + + Add Files (cw_folder as string) & "Metrowerks CodeWarrior:Metrowerks Standard Library:MSL C:Bin:MSL C.PPC.Lib" To Segment 3 + Add Files (cw_folder as string) & "Metrowerks CodeWarrior:Metrowerks Standard Library:MSL C:Bin:MSL SIOUX.PPC.Lib" To Segment 3 + Add Files (cw_folder as string) & "Metrowerks CodeWarrior:MacOS Support:Libraries:Runtime:Runtime PPC:MSL RuntimePPC.Lib" To Segment 3 + + Add Files (cw_folder as string) & "Metrowerks CodeWarrior:MacOS Support:Libraries:MacOS Common:InterfaceLib" To Segment 4 + Add Files (cw_folder as string) & "Metrowerks CodeWarrior:MacOS Support:Libraries:MacOS Common:MathLib" To Segment 4 + + -- compile and link djpeg + + Make Project + Close Project + + -- create jpegtran project + + activate + Create Project (ijg_folder as string) & "jpegtran.proj" + Set Preferences of panel "Target Settings" to {Target Name:"jpegtran"} + Set Preferences of panel "PPC Project" to {File Name:"jpegtran"} + Set Preferences of panel "Target Settings" to {Linker:"MacOS PPC Linker"} + Set Preferences of panel "C/C++ Compiler" to {ANSI Strict:true} + Set Preferences of panel "C/C++ Compiler" to {Enums Always Ints:true} + Set Preferences of panel "PPC Codegen" to {Struct Alignment:PowerPC} + Set Preferences of panel "PPC Linker" to {Generate SYM File:false} + + Add Files (ijg_folder as string) & "jpegtran.c" To Segment 1 + Add Files (ijg_folder as string) & "rdswitch.c" To Segment 1 + Add Files (ijg_folder as string) & "cdjpeg.c" To Segment 1 + Add Files (ijg_folder as string) & "transupp.c" To Segment 1 + + Add Files (ijg_folder as string) & "libjpeg" To Segment 2 + + Add Files (cw_folder as string) & "Metrowerks CodeWarrior:Metrowerks Standard Library:MSL C:Bin:MSL C.PPC.Lib" To Segment 3 + Add Files (cw_folder as string) & "Metrowerks CodeWarrior:Metrowerks Standard Library:MSL C:Bin:MSL SIOUX.PPC.Lib" To Segment 3 + Add Files (cw_folder as string) & "Metrowerks CodeWarrior:MacOS Support:Libraries:Runtime:Runtime PPC:MSL RuntimePPC.Lib" To Segment 3 + + Add Files (cw_folder as string) & "Metrowerks CodeWarrior:MacOS Support:Libraries:MacOS Common:InterfaceLib" To Segment 4 + Add Files (cw_folder as string) & "Metrowerks CodeWarrior:MacOS Support:Libraries:MacOS Common:MathLib" To Segment 4 + + -- compile and link jpegtran + + Make Project + Close Project + + quit + + end timeout + end tell +end run diff --git a/Engine/lib/ljpeg/extras/makljpeg.st b/Engine/lib/ljpeg/extras/makljpeg.st new file mode 100644 index 000000000..813493ea8 --- /dev/null +++ b/Engine/lib/ljpeg/extras/makljpeg.st @@ -0,0 +1,70 @@ +; Project file for Independent JPEG Group's software +; +; This project file is for Atari ST/STE/TT systems using Pure C or Turbo C. +; Thanks to Frank Moehle (Frank.Moehle@arbi.informatik.uni-oldenburg.de), +; Dr. B. Setzepfandt (bernd@gina.uni-muenster.de), +; and Guido Vollbeding (guivol@esc.de). +; +; To use this file, rename it to libjpeg.prj. +; Read installation instructions before trying to make the program! +; +; +; * * * Output file * * * +libjpeg.lib +; +; * * * COMPILER OPTIONS * * * +.C[-P] ; absolute calls +.C[-M] ; and no string merging, folks +.C[-w-cln] ; no "constant is long" warnings +.C[-w-par] ; no "parameter xxxx unused" +.C[-w-rch] ; no "unreachable code" +.C[-wsig] ; warn if significant digits may be lost +.L[-J] ; link new Obj-format (so we get a library) += +; * * * * List of modules * * * * +jcapimin.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jcapistd.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jccoefct.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jccolor.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jcdctmgr.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h,jdct.h) +jchuff.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h,jchuff.h) +jcinit.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jcmainct.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jcmarker.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jcmaster.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jcomapi.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jcparam.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jcphuff.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h,jchuff.h) +jcprepct.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jcsample.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jctrans.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jdapimin.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jdapistd.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jdatadst.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jerror.h) +jdatasrc.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jerror.h) +jdcoefct.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jdcolor.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jddctmgr.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h,jdct.h) +jdhuff.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h,jdhuff.h) +jdinput.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jdmainct.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jdmarker.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jdmaster.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jdmerge.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jdphuff.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h,jdhuff.h) +jdpostct.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jdsample.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jdtrans.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jerror.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jversion.h,jerror.h) +jfdctflt.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h,jdct.h) +jfdctfst.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h,jdct.h) +jfdctint.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h,jdct.h) +jidctflt.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h,jdct.h) +jidctfst.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h,jdct.h) +jidctint.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h,jdct.h) +jidctred.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h,jdct.h) +jquant1.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jquant2.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jutils.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h) +jmemmgr.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h,jmemsys.h) +jmemansi.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h,jmemsys.h) diff --git a/Engine/lib/ljpeg/extras/maktjpeg.st b/Engine/lib/ljpeg/extras/maktjpeg.st new file mode 100644 index 000000000..31f4d169c --- /dev/null +++ b/Engine/lib/ljpeg/extras/maktjpeg.st @@ -0,0 +1,32 @@ +; Project file for Independent JPEG Group's software +; +; This project file is for Atari ST/STE/TT systems using Pure C or Turbo C. +; Thanks to Frank Moehle (Frank.Moehle@arbi.informatik.uni-oldenburg.de), +; Dr. B. Setzepfandt (bernd@gina.uni-muenster.de), +; and Guido Vollbeding (guivol@esc.de). +; +; To use this file, rename it to jpegtran.prj. +; If you are using Turbo C, change filenames beginning with "pc..." to "tc..." +; Read installation instructions before trying to make the program! +; +; +; * * * Output file * * * +jpegtran.ttp +; +; * * * COMPILER OPTIONS * * * +.C[-P] ; absolute calls +.C[-M] ; and no string merging, folks +.C[-w-cln] ; no "constant is long" warnings +.C[-w-par] ; no "parameter xxxx unused" +.C[-w-rch] ; no "unreachable code" +.C[-wsig] ; warn if significant digits may be lost += +; * * * * List of modules * * * * +pcstart.o +jpegtran.c (cdjpeg.h,jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jerror.h,cderror.h,transupp.h,jversion.h) +cdjpeg.c (cdjpeg.h,jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jerror.h,cderror.h) +rdswitch.c (cdjpeg.h,jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jerror.h,cderror.h) +transupp.c (jinclude.h,jconfig.h,jpeglib.h,jmorecfg.h,jpegint.h,jerror.h,transupp.h) +libjpeg.lib ; built by libjpeg.prj +pcstdlib.lib ; standard library +pcextlib.lib ; extended library diff --git a/Engine/lib/ljpeg/extras/makvms.opt b/Engine/lib/ljpeg/extras/makvms.opt new file mode 100644 index 000000000..675e8fe98 --- /dev/null +++ b/Engine/lib/ljpeg/extras/makvms.opt @@ -0,0 +1,4 @@ +! A pointer to the VAX/VMS C Run-Time Shareable Library. +! This file is needed by makefile.mms and makefile.vms, +! but only for the older VAX C compiler. DEC C does not need it. +Sys$Library:VAXCRTL.EXE /Share diff --git a/Engine/lib/ljpeg/extras/rdbmp.c b/Engine/lib/ljpeg/extras/rdbmp.c new file mode 100644 index 000000000..b05fe2ac4 --- /dev/null +++ b/Engine/lib/ljpeg/extras/rdbmp.c @@ -0,0 +1,439 @@ +/* + * rdbmp.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to read input images in Microsoft "BMP" + * format (MS Windows 3.x, OS/2 1.x, and OS/2 2.x flavors). + * Currently, only 8-bit and 24-bit images are supported, not 1-bit or + * 4-bit (feeding such low-depth images into JPEG would be silly anyway). + * Also, we don't support RLE-compressed files. + * + * These routines may need modification for non-Unix environments or + * specialized applications. As they stand, they assume input from + * an ordinary stdio stream. They further assume that reading begins + * at the start of the file; start_input may need work if the + * user interface has already read some data (e.g., to determine that + * the file is indeed BMP format). + * + * This code contributed by James Arthur Boucher. + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + +#ifdef BMP_SUPPORTED + + +/* Macros to deal with unsigned chars as efficiently as compiler allows */ + +#ifdef HAVE_UNSIGNED_CHAR +typedef unsigned char U_CHAR; +#define UCH(x) ((int) (x)) +#else /* !HAVE_UNSIGNED_CHAR */ +#ifdef CHAR_IS_UNSIGNED +typedef char U_CHAR; +#define UCH(x) ((int) (x)) +#else +typedef char U_CHAR; +#define UCH(x) ((int) (x) & 0xFF) +#endif +#endif /* HAVE_UNSIGNED_CHAR */ + + +#define ReadOK(file,buffer,len) (JFREAD(file,buffer,len) == ((size_t) (len))) + + +/* Private version of data source object */ + +typedef struct _bmp_source_struct * bmp_source_ptr; + +typedef struct _bmp_source_struct { + struct cjpeg_source_struct pub; /* public fields */ + + j_compress_ptr cinfo; /* back link saves passing separate parm */ + + JSAMPARRAY colormap; /* BMP colormap (converted to my format) */ + + jvirt_sarray_ptr whole_image; /* Needed to reverse row order */ + JDIMENSION source_row; /* Current source row number */ + JDIMENSION row_width; /* Physical width of scanlines in file */ + + int bits_per_pixel; /* remembers 8- or 24-bit format */ +} bmp_source_struct; + + +LOCAL(int) +read_byte (bmp_source_ptr sinfo) +/* Read next byte from BMP file */ +{ + register FILE *infile = sinfo->pub.input_file; + register int c; + + if ((c = getc(infile)) == EOF) + ERREXIT(sinfo->cinfo, JERR_INPUT_EOF); + return c; +} + + +LOCAL(void) +read_colormap (bmp_source_ptr sinfo, int cmaplen, int mapentrysize) +/* Read the colormap from a BMP file */ +{ + int i; + + switch (mapentrysize) { + case 3: + /* BGR format (occurs in OS/2 files) */ + for (i = 0; i < cmaplen; i++) { + sinfo->colormap[2][i] = (JSAMPLE) read_byte(sinfo); + sinfo->colormap[1][i] = (JSAMPLE) read_byte(sinfo); + sinfo->colormap[0][i] = (JSAMPLE) read_byte(sinfo); + } + break; + case 4: + /* BGR0 format (occurs in MS Windows files) */ + for (i = 0; i < cmaplen; i++) { + sinfo->colormap[2][i] = (JSAMPLE) read_byte(sinfo); + sinfo->colormap[1][i] = (JSAMPLE) read_byte(sinfo); + sinfo->colormap[0][i] = (JSAMPLE) read_byte(sinfo); + (void) read_byte(sinfo); + } + break; + default: + ERREXIT(sinfo->cinfo, JERR_BMP_BADCMAP); + break; + } +} + + +/* + * Read one row of pixels. + * The image has been read into the whole_image array, but is otherwise + * unprocessed. We must read it out in top-to-bottom row order, and if + * it is an 8-bit image, we must expand colormapped pixels to 24bit format. + */ + +METHODDEF(JDIMENSION) +get_8bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading 8-bit colormap indexes */ +{ + bmp_source_ptr source = (bmp_source_ptr) sinfo; + register JSAMPARRAY colormap = source->colormap; + JSAMPARRAY image_ptr; + register int t; + register JSAMPROW inptr, outptr; + register JDIMENSION col; + + /* Fetch next row from virtual array */ + source->source_row--; + image_ptr = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, source->whole_image, + source->source_row, (JDIMENSION) 1, FALSE); + + /* Expand the colormap indexes to real data */ + inptr = image_ptr[0]; + outptr = source->pub.buffer[0]; + for (col = cinfo->image_width; col > 0; col--) { + t = GETJSAMPLE(*inptr++); + *outptr++ = colormap[0][t]; /* can omit GETJSAMPLE() safely */ + *outptr++ = colormap[1][t]; + *outptr++ = colormap[2][t]; + } + + return 1; +} + + +METHODDEF(JDIMENSION) +get_24bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading 24-bit pixels */ +{ + bmp_source_ptr source = (bmp_source_ptr) sinfo; + JSAMPARRAY image_ptr; + register JSAMPROW inptr, outptr; + register JDIMENSION col; + + /* Fetch next row from virtual array */ + source->source_row--; + image_ptr = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, source->whole_image, + source->source_row, (JDIMENSION) 1, FALSE); + + /* Transfer data. Note source values are in BGR order + * (even though Microsoft's own documents say the opposite). + */ + inptr = image_ptr[0]; + outptr = source->pub.buffer[0]; + for (col = cinfo->image_width; col > 0; col--) { + outptr[2] = *inptr++; /* can omit GETJSAMPLE() safely */ + outptr[1] = *inptr++; + outptr[0] = *inptr++; + outptr += 3; + } + + return 1; +} + + +/* + * This method loads the image into whole_image during the first call on + * get_pixel_rows. The get_pixel_rows pointer is then adjusted to call + * get_8bit_row or get_24bit_row on subsequent calls. + */ + +METHODDEF(JDIMENSION) +preload_image (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + bmp_source_ptr source = (bmp_source_ptr) sinfo; + register FILE *infile = source->pub.input_file; + register int c; + register JSAMPROW out_ptr; + JSAMPARRAY image_ptr; + JDIMENSION row, col; + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; + + /* Read the data into a virtual array in input-file row order. */ + for (row = 0; row < cinfo->image_height; row++) { + if (progress != NULL) { + progress->pub.pass_counter = (long) row; + progress->pub.pass_limit = (long) cinfo->image_height; + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + } + image_ptr = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, source->whole_image, + row, (JDIMENSION) 1, TRUE); + out_ptr = image_ptr[0]; + for (col = source->row_width; col > 0; col--) { + /* inline copy of read_byte() for speed */ + if ((c = getc(infile)) == EOF) + ERREXIT(cinfo, JERR_INPUT_EOF); + *out_ptr++ = (JSAMPLE) c; + } + } + if (progress != NULL) + progress->completed_extra_passes++; + + /* Set up to read from the virtual array in top-to-bottom order */ + switch (source->bits_per_pixel) { + case 8: + source->pub.get_pixel_rows = get_8bit_row; + break; + case 24: + source->pub.get_pixel_rows = get_24bit_row; + break; + default: + ERREXIT(cinfo, JERR_BMP_BADDEPTH); + } + source->source_row = cinfo->image_height; + + /* And read the first row */ + return (*source->pub.get_pixel_rows) (cinfo, sinfo); +} + + +/* + * Read the file header; return image size and component count. + */ + +METHODDEF(void) +start_input_bmp (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + bmp_source_ptr source = (bmp_source_ptr) sinfo; + U_CHAR bmpfileheader[14]; + U_CHAR bmpinfoheader[64]; +#define GET_2B(array,offset) ((unsigned int) UCH(array[offset]) + \ + (((unsigned int) UCH(array[offset+1])) << 8)) +#define GET_4B(array,offset) ((INT32) UCH(array[offset]) + \ + (((INT32) UCH(array[offset+1])) << 8) + \ + (((INT32) UCH(array[offset+2])) << 16) + \ + (((INT32) UCH(array[offset+3])) << 24)) + INT32 bfOffBits; + INT32 headerSize; + INT32 biWidth = 0; /* initialize to avoid compiler warning */ + INT32 biHeight = 0; + unsigned int biPlanes; + INT32 biCompression; + INT32 biXPelsPerMeter,biYPelsPerMeter; + INT32 biClrUsed = 0; + int mapentrysize = 0; /* 0 indicates no colormap */ + INT32 bPad; + JDIMENSION row_width; + + /* Read and verify the bitmap file header */ + if (! ReadOK(source->pub.input_file, bmpfileheader, 14)) + ERREXIT(cinfo, JERR_INPUT_EOF); + if (GET_2B(bmpfileheader,0) != 0x4D42) /* 'BM' */ + ERREXIT(cinfo, JERR_BMP_NOT); + bfOffBits = (INT32) GET_4B(bmpfileheader,10); + /* We ignore the remaining fileheader fields */ + + /* The infoheader might be 12 bytes (OS/2 1.x), 40 bytes (Windows), + * or 64 bytes (OS/2 2.x). Check the first 4 bytes to find out which. + */ + if (! ReadOK(source->pub.input_file, bmpinfoheader, 4)) + ERREXIT(cinfo, JERR_INPUT_EOF); + headerSize = (INT32) GET_4B(bmpinfoheader,0); + if (headerSize < 12 || headerSize > 64) + ERREXIT(cinfo, JERR_BMP_BADHEADER); + if (! ReadOK(source->pub.input_file, bmpinfoheader+4, headerSize-4)) + ERREXIT(cinfo, JERR_INPUT_EOF); + + switch ((int) headerSize) { + case 12: + /* Decode OS/2 1.x header (Microsoft calls this a BITMAPCOREHEADER) */ + biWidth = (INT32) GET_2B(bmpinfoheader,4); + biHeight = (INT32) GET_2B(bmpinfoheader,6); + biPlanes = GET_2B(bmpinfoheader,8); + source->bits_per_pixel = (int) GET_2B(bmpinfoheader,10); + + switch (source->bits_per_pixel) { + case 8: /* colormapped image */ + mapentrysize = 3; /* OS/2 uses RGBTRIPLE colormap */ + TRACEMS2(cinfo, 1, JTRC_BMP_OS2_MAPPED, (int) biWidth, (int) biHeight); + break; + case 24: /* RGB image */ + TRACEMS2(cinfo, 1, JTRC_BMP_OS2, (int) biWidth, (int) biHeight); + break; + default: + ERREXIT(cinfo, JERR_BMP_BADDEPTH); + break; + } + if (biPlanes != 1) + ERREXIT(cinfo, JERR_BMP_BADPLANES); + break; + case 40: + case 64: + /* Decode Windows 3.x header (Microsoft calls this a BITMAPINFOHEADER) */ + /* or OS/2 2.x header, which has additional fields that we ignore */ + biWidth = GET_4B(bmpinfoheader,4); + biHeight = GET_4B(bmpinfoheader,8); + biPlanes = GET_2B(bmpinfoheader,12); + source->bits_per_pixel = (int) GET_2B(bmpinfoheader,14); + biCompression = GET_4B(bmpinfoheader,16); + biXPelsPerMeter = GET_4B(bmpinfoheader,24); + biYPelsPerMeter = GET_4B(bmpinfoheader,28); + biClrUsed = GET_4B(bmpinfoheader,32); + /* biSizeImage, biClrImportant fields are ignored */ + + switch (source->bits_per_pixel) { + case 8: /* colormapped image */ + mapentrysize = 4; /* Windows uses RGBQUAD colormap */ + TRACEMS2(cinfo, 1, JTRC_BMP_MAPPED, (int) biWidth, (int) biHeight); + break; + case 24: /* RGB image */ + TRACEMS2(cinfo, 1, JTRC_BMP, (int) biWidth, (int) biHeight); + break; + default: + ERREXIT(cinfo, JERR_BMP_BADDEPTH); + break; + } + if (biPlanes != 1) + ERREXIT(cinfo, JERR_BMP_BADPLANES); + if (biCompression != 0) + ERREXIT(cinfo, JERR_BMP_COMPRESSED); + + if (biXPelsPerMeter > 0 && biYPelsPerMeter > 0) { + /* Set JFIF density parameters from the BMP data */ + cinfo->X_density = (UINT16) (biXPelsPerMeter/100); /* 100 cm per meter */ + cinfo->Y_density = (UINT16) (biYPelsPerMeter/100); + cinfo->density_unit = 2; /* dots/cm */ + } + break; + default: + ERREXIT(cinfo, JERR_BMP_BADHEADER); + break; + } + + /* Compute distance to bitmap data --- will adjust for colormap below */ + bPad = bfOffBits - (headerSize + 14); + + /* Read the colormap, if any */ + if (mapentrysize > 0) { + if (biClrUsed <= 0) + biClrUsed = 256; /* assume it's 256 */ + else if (biClrUsed > 256) + ERREXIT(cinfo, JERR_BMP_BADCMAP); + /* Allocate space to store the colormap */ + source->colormap = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) biClrUsed, (JDIMENSION) 3); + /* and read it from the file */ + read_colormap(source, (int) biClrUsed, mapentrysize); + /* account for size of colormap */ + bPad -= biClrUsed * mapentrysize; + } + + /* Skip any remaining pad bytes */ + if (bPad < 0) /* incorrect bfOffBits value? */ + ERREXIT(cinfo, JERR_BMP_BADHEADER); + while (--bPad >= 0) { + (void) read_byte(source); + } + + /* Compute row width in file, including padding to 4-byte boundary */ + if (source->bits_per_pixel == 24) + row_width = (JDIMENSION) (biWidth * 3); + else + row_width = (JDIMENSION) biWidth; + while ((row_width & 3) != 0) row_width++; + source->row_width = row_width; + + /* Allocate space for inversion array, prepare for preload pass */ + source->whole_image = (*cinfo->mem->request_virt_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + row_width, (JDIMENSION) biHeight, (JDIMENSION) 1); + source->pub.get_pixel_rows = preload_image; + if (cinfo->progress != NULL) { + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; + progress->total_extra_passes++; /* count file input as separate pass */ + } + + /* Allocate one-row buffer for returned data */ + source->pub.buffer = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) (biWidth * 3), (JDIMENSION) 1); + source->pub.buffer_height = 1; + + cinfo->in_color_space = JCS_RGB; + cinfo->input_components = 3; + cinfo->data_precision = 8; + cinfo->image_width = (JDIMENSION) biWidth; + cinfo->image_height = (JDIMENSION) biHeight; +} + + +/* + * Finish up at the end of the file. + */ + +METHODDEF(void) +finish_input_bmp (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + /* no work */ +} + + +/* + * The module selection routine for BMP format input. + */ + +GLOBAL(cjpeg_source_ptr) +jinit_read_bmp (j_compress_ptr cinfo) +{ + bmp_source_ptr source; + + /* Create module interface object */ + source = (bmp_source_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(bmp_source_struct)); + source->cinfo = cinfo; /* make back link for subroutines */ + /* Fill in method ptrs, except get_pixel_rows which start_input sets */ + source->pub.start_input = start_input_bmp; + source->pub.finish_input = finish_input_bmp; + + return (cjpeg_source_ptr) source; +} + +#endif /* BMP_SUPPORTED */ diff --git a/Engine/lib/ljpeg/extras/rdcolmap.c b/Engine/lib/ljpeg/extras/rdcolmap.c new file mode 100644 index 000000000..42b343763 --- /dev/null +++ b/Engine/lib/ljpeg/extras/rdcolmap.c @@ -0,0 +1,253 @@ +/* + * rdcolmap.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file implements djpeg's "-map file" switch. It reads a source image + * and constructs a colormap to be supplied to the JPEG decompressor. + * + * Currently, these file formats are supported for the map file: + * GIF: the contents of the GIF's global colormap are used. + * PPM (either text or raw flavor): the entire file is read and + * each unique pixel value is entered in the map. + * Note that reading a large PPM file will be horrendously slow. + * Typically, a PPM-format map file should contain just one pixel + * of each desired color. Such a file can be extracted from an + * ordinary image PPM file with ppmtomap(1). + * + * Rescaling a PPM that has a maxval unequal to MAXJSAMPLE is not + * currently implemented. + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + +#ifdef QUANT_2PASS_SUPPORTED /* otherwise can't quantize to supplied map */ + +/* Portions of this code are based on the PBMPLUS library, which is: +** +** Copyright (C) 1988 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + + +/* + * Add a (potentially) new color to the color map. + */ + +LOCAL(void) +add_map_entry (j_decompress_ptr cinfo, int R, int G, int B) +{ + JSAMPROW colormap0 = cinfo->colormap[0]; + JSAMPROW colormap1 = cinfo->colormap[1]; + JSAMPROW colormap2 = cinfo->colormap[2]; + int ncolors = cinfo->actual_number_of_colors; + int index; + + /* Check for duplicate color. */ + for (index = 0; index < ncolors; index++) { + if (GETJSAMPLE(colormap0[index]) == R && + GETJSAMPLE(colormap1[index]) == G && + GETJSAMPLE(colormap2[index]) == B) + return; /* color is already in map */ + } + + /* Check for map overflow. */ + if (ncolors >= (MAXJSAMPLE+1)) + ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, (MAXJSAMPLE+1)); + + /* OK, add color to map. */ + colormap0[ncolors] = (JSAMPLE) R; + colormap1[ncolors] = (JSAMPLE) G; + colormap2[ncolors] = (JSAMPLE) B; + cinfo->actual_number_of_colors++; +} + + +/* + * Extract color map from a GIF file. + */ + +LOCAL(void) +read_gif_map (j_decompress_ptr cinfo, FILE * infile) +{ + int header[13]; + int i, colormaplen; + int R, G, B; + + /* Initial 'G' has already been read by read_color_map */ + /* Read the rest of the GIF header and logical screen descriptor */ + for (i = 1; i < 13; i++) { + if ((header[i] = getc(infile)) == EOF) + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + } + + /* Verify GIF Header */ + if (header[1] != 'I' || header[2] != 'F') + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + + /* There must be a global color map. */ + if ((header[10] & 0x80) == 0) + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + + /* OK, fetch it. */ + colormaplen = 2 << (header[10] & 0x07); + + for (i = 0; i < colormaplen; i++) { + R = getc(infile); + G = getc(infile); + B = getc(infile); + if (R == EOF || G == EOF || B == EOF) + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + add_map_entry(cinfo, + R << (BITS_IN_JSAMPLE-8), + G << (BITS_IN_JSAMPLE-8), + B << (BITS_IN_JSAMPLE-8)); + } +} + + +/* Support routines for reading PPM */ + + +LOCAL(int) +pbm_getc (FILE * infile) +/* Read next char, skipping over any comments */ +/* A comment/newline sequence is returned as a newline */ +{ + register int ch; + + ch = getc(infile); + if (ch == '#') { + do { + ch = getc(infile); + } while (ch != '\n' && ch != EOF); + } + return ch; +} + + +LOCAL(unsigned int) +read_pbm_integer (j_decompress_ptr cinfo, FILE * infile) +/* Read an unsigned decimal integer from the PPM file */ +/* Swallows one trailing character after the integer */ +/* Note that on a 16-bit-int machine, only values up to 64k can be read. */ +/* This should not be a problem in practice. */ +{ + register int ch; + register unsigned int val; + + /* Skip any leading whitespace */ + do { + ch = pbm_getc(infile); + if (ch == EOF) + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + } while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'); + + if (ch < '0' || ch > '9') + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + + val = ch - '0'; + while ((ch = pbm_getc(infile)) >= '0' && ch <= '9') { + val *= 10; + val += ch - '0'; + } + return val; +} + + +/* + * Extract color map from a PPM file. + */ + +LOCAL(void) +read_ppm_map (j_decompress_ptr cinfo, FILE * infile) +{ + int c; + unsigned int w, h, maxval, row, col; + int R, G, B; + + /* Initial 'P' has already been read by read_color_map */ + c = getc(infile); /* save format discriminator for a sec */ + + /* while we fetch the remaining header info */ + w = read_pbm_integer(cinfo, infile); + h = read_pbm_integer(cinfo, infile); + maxval = read_pbm_integer(cinfo, infile); + + if (w <= 0 || h <= 0 || maxval <= 0) /* error check */ + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + + /* For now, we don't support rescaling from an unusual maxval. */ + if (maxval != (unsigned int) MAXJSAMPLE) + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + + switch (c) { + case '3': /* it's a text-format PPM file */ + for (row = 0; row < h; row++) { + for (col = 0; col < w; col++) { + R = read_pbm_integer(cinfo, infile); + G = read_pbm_integer(cinfo, infile); + B = read_pbm_integer(cinfo, infile); + add_map_entry(cinfo, R, G, B); + } + } + break; + + case '6': /* it's a raw-format PPM file */ + for (row = 0; row < h; row++) { + for (col = 0; col < w; col++) { + R = getc(infile); + G = getc(infile); + B = getc(infile); + if (R == EOF || G == EOF || B == EOF) + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + add_map_entry(cinfo, R, G, B); + } + } + break; + + default: + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + break; + } +} + + +/* + * Main entry point from djpeg.c. + * Input: opened input file (from file name argument on command line). + * Output: colormap and actual_number_of_colors fields are set in cinfo. + */ + +GLOBAL(void) +read_color_map (j_decompress_ptr cinfo, FILE * infile) +{ + /* Allocate space for a color map of maximum supported size. */ + cinfo->colormap = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) (MAXJSAMPLE+1), (JDIMENSION) 3); + cinfo->actual_number_of_colors = 0; /* initialize map to empty */ + + /* Read first byte to determine file format */ + switch (getc(infile)) { + case 'G': + read_gif_map(cinfo, infile); + break; + case 'P': + read_ppm_map(cinfo, infile); + break; + default: + ERREXIT(cinfo, JERR_BAD_CMAP_FILE); + break; + } +} + +#endif /* QUANT_2PASS_SUPPORTED */ diff --git a/Engine/lib/ljpeg/extras/rdgif.c b/Engine/lib/ljpeg/extras/rdgif.c new file mode 100644 index 000000000..b27c1675d --- /dev/null +++ b/Engine/lib/ljpeg/extras/rdgif.c @@ -0,0 +1,38 @@ +/* + * rdgif.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to read input images in GIF format. + * + ***************************************************************************** + * NOTE: to avoid entanglements with Unisys' patent on LZW compression, * + * the ability to read GIF files has been removed from the IJG distribution. * + * Sorry about that. * + ***************************************************************************** + * + * We are required to state that + * "The Graphics Interchange Format(c) is the Copyright property of + * CompuServe Incorporated. GIF(sm) is a Service Mark property of + * CompuServe Incorporated." + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + +#ifdef GIF_SUPPORTED + +/* + * The module selection routine for GIF format input. + */ + +GLOBAL(cjpeg_source_ptr) +jinit_read_gif (j_compress_ptr cinfo) +{ + fprintf(stderr, "GIF input is unsupported for legal reasons. Sorry.\n"); + exit(EXIT_FAILURE); + return NULL; /* keep compiler happy */ +} + +#endif /* GIF_SUPPORTED */ diff --git a/Engine/lib/ljpeg/extras/rdjpgcom.1 b/Engine/lib/ljpeg/extras/rdjpgcom.1 new file mode 100644 index 000000000..2bba04e1a --- /dev/null +++ b/Engine/lib/ljpeg/extras/rdjpgcom.1 @@ -0,0 +1,54 @@ +.TH RDJPGCOM 1 "11 October 1997" +.SH NAME +rdjpgcom \- display text comments from a JPEG file +.SH SYNOPSIS +.B rdjpgcom +[ +.B \-verbose +] +[ +.I filename +] +.LP +.SH DESCRIPTION +.LP +.B rdjpgcom +reads the named JPEG/JFIF file, or the standard input if no file is named, +and prints any text comments found in the file on the standard output. +.PP +The JPEG standard allows "comment" (COM) blocks to occur within a JPEG file. +Although the standard doesn't actually define what COM blocks are for, they +are widely used to hold user-supplied text strings. This lets you add +annotations, titles, index terms, etc to your JPEG files, and later retrieve +them as text. COM blocks do not interfere with the image stored in the JPEG +file. The maximum size of a COM block is 64K, but you can have as many of +them as you like in one JPEG file. +.SH OPTIONS +.TP +.B \-verbose +Causes +.B rdjpgcom +to also display the JPEG image dimensions. +.PP +Switch names may be abbreviated, and are not case sensitive. +.SH HINTS +.B rdjpgcom +does not depend on the IJG JPEG library. Its source code is intended as an +illustration of the minimum amount of code required to parse a JPEG file +header correctly. +.PP +In +.B \-verbose +mode, +.B rdjpgcom +will also attempt to print the contents of any "APP12" markers as text. +Some digital cameras produce APP12 markers containing useful textual +information. If you like, you can modify the source code to print +other APPn marker types as well. +.SH SEE ALSO +.BR cjpeg (1), +.BR djpeg (1), +.BR jpegtran (1), +.BR wrjpgcom (1) +.SH AUTHOR +Independent JPEG Group diff --git a/Engine/lib/ljpeg/extras/rdjpgcom.c b/Engine/lib/ljpeg/extras/rdjpgcom.c new file mode 100644 index 000000000..ffe6fc621 --- /dev/null +++ b/Engine/lib/ljpeg/extras/rdjpgcom.c @@ -0,0 +1,496 @@ +/* + * rdjpgcom.c + * + * Copyright (C) 1994-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a very simple stand-alone application that displays + * the text in COM (comment) markers in a JFIF file. + * This may be useful as an example of the minimum logic needed to parse + * JPEG markers. + */ + +#define JPEG_CJPEG_DJPEG /* to get the command-line config symbols */ +#include "jinclude.h" /* get auto-config symbols, */ + +#include /* to declare isupper(), tolower() */ +#ifdef USE_SETMODE +#include /* to declare setmode()'s parameter macros */ +/* If you have setmode() but not , just delete this line: */ +#include /* to declare setmode() */ +#endif + +#ifdef USE_CCOMMAND /* command-line reader for Macintosh */ +#ifdef __MWERKS__ +#include /* Metrowerks needs this */ +#include /* ... and this */ +#endif +#ifdef THINK_C +#include /* Think declares it here */ +#endif +#endif + +#ifdef DONT_USE_B_MODE /* define mode parameters for fopen() */ +#define READ_BINARY "r" +#else +#ifdef VMS /* VMS is very nonstandard */ +#define READ_BINARY "rb", "ctx=stm" +#else /* standard ANSI-compliant case */ +#define READ_BINARY "rb" +#endif +#endif + +#ifndef EXIT_FAILURE /* define exit() codes if not provided */ +#define EXIT_FAILURE 1 +#endif +#ifndef EXIT_SUCCESS +#ifdef VMS +#define EXIT_SUCCESS 1 /* VMS is very nonstandard */ +#else +#define EXIT_SUCCESS 0 +#endif +#endif + + +/* + * These macros are used to read the input file. + * To reuse this code in another application, you might need to change these. + */ + +static FILE * infile; /* input JPEG file */ + +/* Return next input byte, or EOF if no more */ +#define NEXTBYTE() getc(infile) + + +/* Error exit handler */ +#define ERREXIT(msg) (fprintf(stderr, "%s\n", msg), exit(EXIT_FAILURE)) + + +/* Read one byte, testing for EOF */ +static int +read_1_byte (void) +{ + int c; + + c = NEXTBYTE(); + if (c == EOF) + ERREXIT("Premature EOF in JPEG file"); + return c; +} + +/* Read 2 bytes, convert to unsigned int */ +/* All 2-byte quantities in JPEG markers are MSB first */ +static unsigned int +read_2_bytes (void) +{ + int c1, c2; + + c1 = NEXTBYTE(); + if (c1 == EOF) + ERREXIT("Premature EOF in JPEG file"); + c2 = NEXTBYTE(); + if (c2 == EOF) + ERREXIT("Premature EOF in JPEG file"); + return (((unsigned int) c1) << 8) + ((unsigned int) c2); +} + + +/* + * JPEG markers consist of one or more 0xFF bytes, followed by a marker + * code byte (which is not an FF). Here are the marker codes of interest + * in this program. (See jdmarker.c for a more complete list.) + */ + +#define M_SOF0 0xC0 /* Start Of Frame N */ +#define M_SOF1 0xC1 /* N indicates which compression process */ +#define M_SOF2 0xC2 /* Only SOF0-SOF2 are now in common use */ +#define M_SOF3 0xC3 +#define M_SOF5 0xC5 /* NB: codes C4 and CC are NOT SOF markers */ +#define M_SOF6 0xC6 +#define M_SOF7 0xC7 +#define M_SOF9 0xC9 +#define M_SOF10 0xCA +#define M_SOF11 0xCB +#define M_SOF13 0xCD +#define M_SOF14 0xCE +#define M_SOF15 0xCF +#define M_SOI 0xD8 /* Start Of Image (beginning of datastream) */ +#define M_EOI 0xD9 /* End Of Image (end of datastream) */ +#define M_SOS 0xDA /* Start Of Scan (begins compressed data) */ +#define M_APP0 0xE0 /* Application-specific marker, type N */ +#define M_APP12 0xEC /* (we don't bother to list all 16 APPn's) */ +#define M_COM 0xFE /* COMment */ + + +/* + * Find the next JPEG marker and return its marker code. + * We expect at least one FF byte, possibly more if the compressor used FFs + * to pad the file. + * There could also be non-FF garbage between markers. The treatment of such + * garbage is unspecified; we choose to skip over it but emit a warning msg. + * NB: this routine must not be used after seeing SOS marker, since it will + * not deal correctly with FF/00 sequences in the compressed image data... + */ + +static int +next_marker (void) +{ + int c; + int discarded_bytes = 0; + + /* Find 0xFF byte; count and skip any non-FFs. */ + c = read_1_byte(); + while (c != 0xFF) { + discarded_bytes++; + c = read_1_byte(); + } + /* Get marker code byte, swallowing any duplicate FF bytes. Extra FFs + * are legal as pad bytes, so don't count them in discarded_bytes. + */ + do { + c = read_1_byte(); + } while (c == 0xFF); + + if (discarded_bytes != 0) { + fprintf(stderr, "Warning: garbage data found in JPEG file\n"); + } + + return c; +} + + +/* + * Read the initial marker, which should be SOI. + * For a JFIF file, the first two bytes of the file should be literally + * 0xFF M_SOI. To be more general, we could use next_marker, but if the + * input file weren't actually JPEG at all, next_marker might read the whole + * file and then return a misleading error message... + */ + +static int +first_marker (void) +{ + int c1, c2; + + c1 = NEXTBYTE(); + c2 = NEXTBYTE(); + if (c1 != 0xFF || c2 != M_SOI) + ERREXIT("Not a JPEG file"); + return c2; +} + + +/* + * Most types of marker are followed by a variable-length parameter segment. + * This routine skips over the parameters for any marker we don't otherwise + * want to process. + * Note that we MUST skip the parameter segment explicitly in order not to + * be fooled by 0xFF bytes that might appear within the parameter segment; + * such bytes do NOT introduce new markers. + */ + +static void +skip_variable (void) +/* Skip over an unknown or uninteresting variable-length marker */ +{ + unsigned int length; + + /* Get the marker parameter length count */ + length = read_2_bytes(); + /* Length includes itself, so must be at least 2 */ + if (length < 2) + ERREXIT("Erroneous JPEG marker length"); + length -= 2; + /* Skip over the remaining bytes */ + while (length > 0) { + (void) read_1_byte(); + length--; + } +} + + +/* + * Process a COM marker. + * We want to print out the marker contents as legible text; + * we must guard against non-text junk and varying newline representations. + */ + +static void +process_COM (void) +{ + unsigned int length; + int ch; + int lastch = 0; + + /* Get the marker parameter length count */ + length = read_2_bytes(); + /* Length includes itself, so must be at least 2 */ + if (length < 2) + ERREXIT("Erroneous JPEG marker length"); + length -= 2; + + while (length > 0) { + ch = read_1_byte(); + /* Emit the character in a readable form. + * Nonprintables are converted to \nnn form, + * while \ is converted to \\. + * Newlines in CR, CR/LF, or LF form will be printed as one newline. + */ + if (ch == '\r') { + printf("\n"); + } else if (ch == '\n') { + if (lastch != '\r') + printf("\n"); + } else if (ch == '\\') { + printf("\\\\"); + } else if (isprint(ch)) { + putc(ch, stdout); + } else { + printf("\\%03o", ch); + } + lastch = ch; + length--; + } + printf("\n"); +} + + +/* + * Process a SOFn marker. + * This code is only needed if you want to know the image dimensions... + */ + +static void +process_SOFn (int marker) +{ + unsigned int length; + unsigned int image_height, image_width; + int data_precision, num_components; + const char * process; + int ci; + + length = read_2_bytes(); /* usual parameter length count */ + + data_precision = read_1_byte(); + image_height = read_2_bytes(); + image_width = read_2_bytes(); + num_components = read_1_byte(); + + switch (marker) { + case M_SOF0: process = "Baseline"; break; + case M_SOF1: process = "Extended sequential"; break; + case M_SOF2: process = "Progressive"; break; + case M_SOF3: process = "Lossless"; break; + case M_SOF5: process = "Differential sequential"; break; + case M_SOF6: process = "Differential progressive"; break; + case M_SOF7: process = "Differential lossless"; break; + case M_SOF9: process = "Extended sequential, arithmetic coding"; break; + case M_SOF10: process = "Progressive, arithmetic coding"; break; + case M_SOF11: process = "Lossless, arithmetic coding"; break; + case M_SOF13: process = "Differential sequential, arithmetic coding"; break; + case M_SOF14: process = "Differential progressive, arithmetic coding"; break; + case M_SOF15: process = "Differential lossless, arithmetic coding"; break; + default: process = "Unknown"; break; + } + + printf("JPEG image is %uw * %uh, %d color components, %d bits per sample\n", + image_width, image_height, num_components, data_precision); + printf("JPEG process: %s\n", process); + + if (length != (unsigned int) (8 + num_components * 3)) + ERREXIT("Bogus SOF marker length"); + + for (ci = 0; ci < num_components; ci++) { + (void) read_1_byte(); /* Component ID code */ + (void) read_1_byte(); /* H, V sampling factors */ + (void) read_1_byte(); /* Quantization table number */ + } +} + + +/* + * Parse the marker stream until SOS or EOI is seen; + * display any COM markers. + * While the companion program wrjpgcom will always insert COM markers before + * SOFn, other implementations might not, so we scan to SOS before stopping. + * If we were only interested in the image dimensions, we would stop at SOFn. + * (Conversely, if we only cared about COM markers, there would be no need + * for special code to handle SOFn; we could treat it like other markers.) + */ + +static int +scan_JPEG_header (int verbose) +{ + int marker; + + /* Expect SOI at start of file */ + if (first_marker() != M_SOI) + ERREXIT("Expected SOI marker first"); + + /* Scan miscellaneous markers until we reach SOS. */ + for (;;) { + marker = next_marker(); + switch (marker) { + /* Note that marker codes 0xC4, 0xC8, 0xCC are not, and must not be, + * treated as SOFn. C4 in particular is actually DHT. + */ + case M_SOF0: /* Baseline */ + case M_SOF1: /* Extended sequential, Huffman */ + case M_SOF2: /* Progressive, Huffman */ + case M_SOF3: /* Lossless, Huffman */ + case M_SOF5: /* Differential sequential, Huffman */ + case M_SOF6: /* Differential progressive, Huffman */ + case M_SOF7: /* Differential lossless, Huffman */ + case M_SOF9: /* Extended sequential, arithmetic */ + case M_SOF10: /* Progressive, arithmetic */ + case M_SOF11: /* Lossless, arithmetic */ + case M_SOF13: /* Differential sequential, arithmetic */ + case M_SOF14: /* Differential progressive, arithmetic */ + case M_SOF15: /* Differential lossless, arithmetic */ + if (verbose) + process_SOFn(marker); + else + skip_variable(); + break; + + case M_SOS: /* stop before hitting compressed data */ + return marker; + + case M_EOI: /* in case it's a tables-only JPEG stream */ + return marker; + + case M_COM: + process_COM(); + break; + + case M_APP12: + /* Some digital camera makers put useful textual information into + * APP12 markers, so we print those out too when in -verbose mode. + */ + if (verbose) { + printf("APP12 contains:\n"); + process_COM(); + } else + skip_variable(); + break; + + default: /* Anything else just gets skipped */ + skip_variable(); /* we assume it has a parameter count... */ + break; + } + } /* end loop */ +} + + +/* Command line parsing code */ + +static const char * progname; /* program name for error messages */ + + +static void +usage (void) +/* complain about bad command line */ +{ + fprintf(stderr, "rdjpgcom displays any textual comments in a JPEG file.\n"); + + fprintf(stderr, "Usage: %s [switches] [inputfile]\n", progname); + + fprintf(stderr, "Switches (names may be abbreviated):\n"); + fprintf(stderr, " -verbose Also display dimensions of JPEG image\n"); + + exit(EXIT_FAILURE); +} + + +static int +keymatch (char * arg, const char * keyword, int minchars) +/* Case-insensitive matching of (possibly abbreviated) keyword switches. */ +/* keyword is the constant keyword (must be lower case already), */ +/* minchars is length of minimum legal abbreviation. */ +{ + register int ca, ck; + register int nmatched = 0; + + while ((ca = *arg++) != '\0') { + if ((ck = *keyword++) == '\0') + return 0; /* arg longer than keyword, no good */ + if (isupper(ca)) /* force arg to lcase (assume ck is already) */ + ca = tolower(ca); + if (ca != ck) + return 0; /* no good */ + nmatched++; /* count matched characters */ + } + /* reached end of argument; fail if it's too short for unique abbrev */ + if (nmatched < minchars) + return 0; + return 1; /* A-OK */ +} + + +/* + * The main program. + */ + +int +main (int argc, char **argv) +{ + int argn; + char * arg; + int verbose = 0; + + /* On Mac, fetch a command line. */ +#ifdef USE_CCOMMAND + argc = ccommand(&argv); +#endif + + progname = argv[0]; + if (progname == NULL || progname[0] == 0) + progname = "rdjpgcom"; /* in case C library doesn't provide it */ + + /* Parse switches, if any */ + for (argn = 1; argn < argc; argn++) { + arg = argv[argn]; + if (arg[0] != '-') + break; /* not switch, must be file name */ + arg++; /* advance over '-' */ + if (keymatch(arg, "verbose", 1)) { + verbose++; + } else + usage(); + } + + /* Open the input file. */ + /* Unix style: expect zero or one file name */ + if (argn < argc-1) { + fprintf(stderr, "%s: only one input file\n", progname); + usage(); + } + if (argn < argc) { + if ((infile = fopen(argv[argn], READ_BINARY)) == NULL) { + fprintf(stderr, "%s: can't open %s\n", progname, argv[argn]); + exit(EXIT_FAILURE); + } + } else { + /* default input file is stdin */ +#ifdef USE_SETMODE /* need to hack file mode? */ + setmode(fileno(stdin), O_BINARY); +#endif +#ifdef USE_FDOPEN /* need to re-open in binary mode? */ + if ((infile = fdopen(fileno(stdin), READ_BINARY)) == NULL) { + fprintf(stderr, "%s: can't open stdin\n", progname); + exit(EXIT_FAILURE); + } +#else + infile = stdin; +#endif + } + + /* Scan the JPEG headers. */ + (void) scan_JPEG_header(verbose); + + /* All done. */ + exit(EXIT_SUCCESS); + return 0; /* suppress no-return-value warnings */ +} diff --git a/Engine/lib/ljpeg/extras/rdppm.c b/Engine/lib/ljpeg/extras/rdppm.c new file mode 100644 index 000000000..1df35c1b3 --- /dev/null +++ b/Engine/lib/ljpeg/extras/rdppm.c @@ -0,0 +1,458 @@ +/* + * rdppm.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to read input images in PPM/PGM format. + * The extended 2-byte-per-sample raw PPM/PGM formats are supported. + * The PBMPLUS library is NOT required to compile this software + * (but it is highly useful as a set of PPM image manipulation programs). + * + * These routines may need modification for non-Unix environments or + * specialized applications. As they stand, they assume input from + * an ordinary stdio stream. They further assume that reading begins + * at the start of the file; start_input may need work if the + * user interface has already read some data (e.g., to determine that + * the file is indeed PPM format). + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + +#ifdef PPM_SUPPORTED + + +/* Portions of this code are based on the PBMPLUS library, which is: +** +** Copyright (C) 1988 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +*/ + + +/* Macros to deal with unsigned chars as efficiently as compiler allows */ + +#ifdef HAVE_UNSIGNED_CHAR +typedef unsigned char U_CHAR; +#define UCH(x) ((int) (x)) +#else /* !HAVE_UNSIGNED_CHAR */ +#ifdef CHAR_IS_UNSIGNED +typedef char U_CHAR; +#define UCH(x) ((int) (x)) +#else +typedef char U_CHAR; +#define UCH(x) ((int) (x) & 0xFF) +#endif +#endif /* HAVE_UNSIGNED_CHAR */ + + +#define ReadOK(file,buffer,len) (JFREAD(file,buffer,len) == ((size_t) (len))) + + +/* + * On most systems, reading individual bytes with getc() is drastically less + * efficient than buffering a row at a time with fread(). On PCs, we must + * allocate the buffer in near data space, because we are assuming small-data + * memory model, wherein fread() can't reach far memory. If you need to + * process very wide images on a PC, you might have to compile in large-memory + * model, or else replace fread() with a getc() loop --- which will be much + * slower. + */ + + +/* Private version of data source object */ + +typedef struct { + struct cjpeg_source_struct pub; /* public fields */ + + U_CHAR *iobuffer; /* non-FAR pointer to I/O buffer */ + JSAMPROW pixrow; /* FAR pointer to same */ + size_t buffer_width; /* width of I/O buffer */ + JSAMPLE *rescale; /* => maxval-remapping array, or NULL */ +} ppm_source_struct; + +typedef ppm_source_struct * ppm_source_ptr; + + +LOCAL(int) +pbm_getc (FILE * infile) +/* Read next char, skipping over any comments */ +/* A comment/newline sequence is returned as a newline */ +{ + register int ch; + + ch = getc(infile); + if (ch == '#') { + do { + ch = getc(infile); + } while (ch != '\n' && ch != EOF); + } + return ch; +} + + +LOCAL(unsigned int) +read_pbm_integer (j_compress_ptr cinfo, FILE * infile) +/* Read an unsigned decimal integer from the PPM file */ +/* Swallows one trailing character after the integer */ +/* Note that on a 16-bit-int machine, only values up to 64k can be read. */ +/* This should not be a problem in practice. */ +{ + register int ch; + register unsigned int val; + + /* Skip any leading whitespace */ + do { + ch = pbm_getc(infile); + if (ch == EOF) + ERREXIT(cinfo, JERR_INPUT_EOF); + } while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'); + + if (ch < '0' || ch > '9') + ERREXIT(cinfo, JERR_PPM_NONNUMERIC); + + val = ch - '0'; + while ((ch = pbm_getc(infile)) >= '0' && ch <= '9') { + val *= 10; + val += ch - '0'; + } + return val; +} + + +/* + * Read one row of pixels. + * + * We provide several different versions depending on input file format. + * In all cases, input is scaled to the size of JSAMPLE. + * + * A really fast path is provided for reading byte/sample raw files with + * maxval = MAXJSAMPLE, which is the normal case for 8-bit data. + */ + + +METHODDEF(JDIMENSION) +get_text_gray_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading text-format PGM files with any maxval */ +{ + ppm_source_ptr source = (ppm_source_ptr) sinfo; + FILE * infile = source->pub.input_file; + register JSAMPROW ptr; + register JSAMPLE *rescale = source->rescale; + JDIMENSION col; + + ptr = source->pub.buffer[0]; + for (col = cinfo->image_width; col > 0; col--) { + *ptr++ = rescale[read_pbm_integer(cinfo, infile)]; + } + return 1; +} + + +METHODDEF(JDIMENSION) +get_text_rgb_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading text-format PPM files with any maxval */ +{ + ppm_source_ptr source = (ppm_source_ptr) sinfo; + FILE * infile = source->pub.input_file; + register JSAMPROW ptr; + register JSAMPLE *rescale = source->rescale; + JDIMENSION col; + + ptr = source->pub.buffer[0]; + for (col = cinfo->image_width; col > 0; col--) { + *ptr++ = rescale[read_pbm_integer(cinfo, infile)]; + *ptr++ = rescale[read_pbm_integer(cinfo, infile)]; + *ptr++ = rescale[read_pbm_integer(cinfo, infile)]; + } + return 1; +} + + +METHODDEF(JDIMENSION) +get_scaled_gray_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading raw-byte-format PGM files with any maxval */ +{ + ppm_source_ptr source = (ppm_source_ptr) sinfo; + register JSAMPROW ptr; + register U_CHAR * bufferptr; + register JSAMPLE *rescale = source->rescale; + JDIMENSION col; + + if (! ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width)) + ERREXIT(cinfo, JERR_INPUT_EOF); + ptr = source->pub.buffer[0]; + bufferptr = source->iobuffer; + for (col = cinfo->image_width; col > 0; col--) { + *ptr++ = rescale[UCH(*bufferptr++)]; + } + return 1; +} + + +METHODDEF(JDIMENSION) +get_scaled_rgb_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading raw-byte-format PPM files with any maxval */ +{ + ppm_source_ptr source = (ppm_source_ptr) sinfo; + register JSAMPROW ptr; + register U_CHAR * bufferptr; + register JSAMPLE *rescale = source->rescale; + JDIMENSION col; + + if (! ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width)) + ERREXIT(cinfo, JERR_INPUT_EOF); + ptr = source->pub.buffer[0]; + bufferptr = source->iobuffer; + for (col = cinfo->image_width; col > 0; col--) { + *ptr++ = rescale[UCH(*bufferptr++)]; + *ptr++ = rescale[UCH(*bufferptr++)]; + *ptr++ = rescale[UCH(*bufferptr++)]; + } + return 1; +} + + +METHODDEF(JDIMENSION) +get_raw_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading raw-byte-format files with maxval = MAXJSAMPLE. + * In this case we just read right into the JSAMPLE buffer! + * Note that same code works for PPM and PGM files. + */ +{ + ppm_source_ptr source = (ppm_source_ptr) sinfo; + + if (! ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width)) + ERREXIT(cinfo, JERR_INPUT_EOF); + return 1; +} + + +METHODDEF(JDIMENSION) +get_word_gray_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading raw-word-format PGM files with any maxval */ +{ + ppm_source_ptr source = (ppm_source_ptr) sinfo; + register JSAMPROW ptr; + register U_CHAR * bufferptr; + register JSAMPLE *rescale = source->rescale; + JDIMENSION col; + + if (! ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width)) + ERREXIT(cinfo, JERR_INPUT_EOF); + ptr = source->pub.buffer[0]; + bufferptr = source->iobuffer; + for (col = cinfo->image_width; col > 0; col--) { + register int temp; + temp = UCH(*bufferptr++); + temp |= UCH(*bufferptr++) << 8; + *ptr++ = rescale[temp]; + } + return 1; +} + + +METHODDEF(JDIMENSION) +get_word_rgb_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading raw-word-format PPM files with any maxval */ +{ + ppm_source_ptr source = (ppm_source_ptr) sinfo; + register JSAMPROW ptr; + register U_CHAR * bufferptr; + register JSAMPLE *rescale = source->rescale; + JDIMENSION col; + + if (! ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width)) + ERREXIT(cinfo, JERR_INPUT_EOF); + ptr = source->pub.buffer[0]; + bufferptr = source->iobuffer; + for (col = cinfo->image_width; col > 0; col--) { + register int temp; + temp = UCH(*bufferptr++); + temp |= UCH(*bufferptr++) << 8; + *ptr++ = rescale[temp]; + temp = UCH(*bufferptr++); + temp |= UCH(*bufferptr++) << 8; + *ptr++ = rescale[temp]; + temp = UCH(*bufferptr++); + temp |= UCH(*bufferptr++) << 8; + *ptr++ = rescale[temp]; + } + return 1; +} + + +/* + * Read the file header; return image size and component count. + */ + +METHODDEF(void) +start_input_ppm (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + ppm_source_ptr source = (ppm_source_ptr) sinfo; + int c; + unsigned int w, h, maxval; + boolean need_iobuffer, use_raw_buffer, need_rescale; + + if (getc(source->pub.input_file) != 'P') + ERREXIT(cinfo, JERR_PPM_NOT); + + c = getc(source->pub.input_file); /* subformat discriminator character */ + + /* detect unsupported variants (ie, PBM) before trying to read header */ + switch (c) { + case '2': /* it's a text-format PGM file */ + case '3': /* it's a text-format PPM file */ + case '5': /* it's a raw-format PGM file */ + case '6': /* it's a raw-format PPM file */ + break; + default: + ERREXIT(cinfo, JERR_PPM_NOT); + break; + } + + /* fetch the remaining header info */ + w = read_pbm_integer(cinfo, source->pub.input_file); + h = read_pbm_integer(cinfo, source->pub.input_file); + maxval = read_pbm_integer(cinfo, source->pub.input_file); + + if (w <= 0 || h <= 0 || maxval <= 0) /* error check */ + ERREXIT(cinfo, JERR_PPM_NOT); + + cinfo->data_precision = BITS_IN_JSAMPLE; /* we always rescale data to this */ + cinfo->image_width = (JDIMENSION) w; + cinfo->image_height = (JDIMENSION) h; + + /* initialize flags to most common settings */ + need_iobuffer = TRUE; /* do we need an I/O buffer? */ + use_raw_buffer = FALSE; /* do we map input buffer onto I/O buffer? */ + need_rescale = TRUE; /* do we need a rescale array? */ + + switch (c) { + case '2': /* it's a text-format PGM file */ + cinfo->input_components = 1; + cinfo->in_color_space = JCS_GRAYSCALE; + TRACEMS2(cinfo, 1, JTRC_PGM_TEXT, w, h); + source->pub.get_pixel_rows = get_text_gray_row; + need_iobuffer = FALSE; + break; + + case '3': /* it's a text-format PPM file */ + cinfo->input_components = 3; + cinfo->in_color_space = JCS_RGB; + TRACEMS2(cinfo, 1, JTRC_PPM_TEXT, w, h); + source->pub.get_pixel_rows = get_text_rgb_row; + need_iobuffer = FALSE; + break; + + case '5': /* it's a raw-format PGM file */ + cinfo->input_components = 1; + cinfo->in_color_space = JCS_GRAYSCALE; + TRACEMS2(cinfo, 1, JTRC_PGM, w, h); + if (maxval > 255) { + source->pub.get_pixel_rows = get_word_gray_row; + } else if (maxval == MAXJSAMPLE && SIZEOF(JSAMPLE) == SIZEOF(U_CHAR)) { + source->pub.get_pixel_rows = get_raw_row; + use_raw_buffer = TRUE; + need_rescale = FALSE; + } else { + source->pub.get_pixel_rows = get_scaled_gray_row; + } + break; + + case '6': /* it's a raw-format PPM file */ + cinfo->input_components = 3; + cinfo->in_color_space = JCS_RGB; + TRACEMS2(cinfo, 1, JTRC_PPM, w, h); + if (maxval > 255) { + source->pub.get_pixel_rows = get_word_rgb_row; + } else if (maxval == MAXJSAMPLE && SIZEOF(JSAMPLE) == SIZEOF(U_CHAR)) { + source->pub.get_pixel_rows = get_raw_row; + use_raw_buffer = TRUE; + need_rescale = FALSE; + } else { + source->pub.get_pixel_rows = get_scaled_rgb_row; + } + break; + } + + /* Allocate space for I/O buffer: 1 or 3 bytes or words/pixel. */ + if (need_iobuffer) { + source->buffer_width = (size_t) w * cinfo->input_components * + ((maxval<=255) ? SIZEOF(U_CHAR) : (2*SIZEOF(U_CHAR))); + source->iobuffer = (U_CHAR *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + source->buffer_width); + } + + /* Create compressor input buffer. */ + if (use_raw_buffer) { + /* For unscaled raw-input case, we can just map it onto the I/O buffer. */ + /* Synthesize a JSAMPARRAY pointer structure */ + /* Cast here implies near->far pointer conversion on PCs */ + source->pixrow = (JSAMPROW) source->iobuffer; + source->pub.buffer = & source->pixrow; + source->pub.buffer_height = 1; + } else { + /* Need to translate anyway, so make a separate sample buffer. */ + source->pub.buffer = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) w * cinfo->input_components, (JDIMENSION) 1); + source->pub.buffer_height = 1; + } + + /* Compute the rescaling array if required. */ + if (need_rescale) { + INT32 val, half_maxval; + + /* On 16-bit-int machines we have to be careful of maxval = 65535 */ + source->rescale = (JSAMPLE *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (size_t) (((long) maxval + 1L) * SIZEOF(JSAMPLE))); + half_maxval = maxval / 2; + for (val = 0; val <= (INT32) maxval; val++) { + /* The multiplication here must be done in 32 bits to avoid overflow */ + source->rescale[val] = (JSAMPLE) ((val*MAXJSAMPLE + half_maxval)/maxval); + } + } +} + + +/* + * Finish up at the end of the file. + */ + +METHODDEF(void) +finish_input_ppm (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + /* no work */ +} + + +/* + * The module selection routine for PPM format input. + */ + +GLOBAL(cjpeg_source_ptr) +jinit_read_ppm (j_compress_ptr cinfo) +{ + ppm_source_ptr source; + + /* Create module interface object */ + source = (ppm_source_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(ppm_source_struct)); + /* Fill in method ptrs, except get_pixel_rows which start_input sets */ + source->pub.start_input = start_input_ppm; + source->pub.finish_input = finish_input_ppm; + + return (cjpeg_source_ptr) source; +} + +#endif /* PPM_SUPPORTED */ diff --git a/Engine/lib/ljpeg/extras/rdrle.c b/Engine/lib/ljpeg/extras/rdrle.c new file mode 100644 index 000000000..542bc3749 --- /dev/null +++ b/Engine/lib/ljpeg/extras/rdrle.c @@ -0,0 +1,387 @@ +/* + * rdrle.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to read input images in Utah RLE format. + * The Utah Raster Toolkit library is required (version 3.1 or later). + * + * These routines may need modification for non-Unix environments or + * specialized applications. As they stand, they assume input from + * an ordinary stdio stream. They further assume that reading begins + * at the start of the file; start_input may need work if the + * user interface has already read some data (e.g., to determine that + * the file is indeed RLE format). + * + * Based on code contributed by Mike Lijewski, + * with updates from Robert Hutchinson. + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + +#ifdef RLE_SUPPORTED + +/* rle.h is provided by the Utah Raster Toolkit. */ + +#include + +/* + * We assume that JSAMPLE has the same representation as rle_pixel, + * to wit, "unsigned char". Hence we can't cope with 12- or 16-bit samples. + */ + +#if BITS_IN_JSAMPLE != 8 + Sorry, this code only copes with 8-bit JSAMPLEs. /* deliberate syntax err */ +#endif + +/* + * We support the following types of RLE files: + * + * GRAYSCALE - 8 bits, no colormap + * MAPPEDGRAY - 8 bits, 1 channel colomap + * PSEUDOCOLOR - 8 bits, 3 channel colormap + * TRUECOLOR - 24 bits, 3 channel colormap + * DIRECTCOLOR - 24 bits, no colormap + * + * For now, we ignore any alpha channel in the image. + */ + +typedef enum + { GRAYSCALE, MAPPEDGRAY, PSEUDOCOLOR, TRUECOLOR, DIRECTCOLOR } rle_kind; + + +/* + * Since RLE stores scanlines bottom-to-top, we have to invert the image + * to conform to JPEG's top-to-bottom order. To do this, we read the + * incoming image into a virtual array on the first get_pixel_rows call, + * then fetch the required row from the virtual array on subsequent calls. + */ + +typedef struct _rle_source_struct * rle_source_ptr; + +typedef struct _rle_source_struct { + struct cjpeg_source_struct pub; /* public fields */ + + rle_kind visual; /* actual type of input file */ + jvirt_sarray_ptr image; /* virtual array to hold the image */ + JDIMENSION row; /* current row # in the virtual array */ + rle_hdr header; /* Input file information */ + rle_pixel** rle_row; /* holds a row returned by rle_getrow() */ + +} rle_source_struct; + + +/* + * Read the file header; return image size and component count. + */ + +METHODDEF(void) +start_input_rle (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + rle_source_ptr source = (rle_source_ptr) sinfo; + JDIMENSION width, height; +#ifdef PROGRESS_REPORT + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; +#endif + + /* Use RLE library routine to get the header info */ + source->header = *rle_hdr_init(NULL); + source->header.rle_file = source->pub.input_file; + switch (rle_get_setup(&(source->header))) { + case RLE_SUCCESS: + /* A-OK */ + break; + case RLE_NOT_RLE: + ERREXIT(cinfo, JERR_RLE_NOT); + break; + case RLE_NO_SPACE: + ERREXIT(cinfo, JERR_RLE_MEM); + break; + case RLE_EMPTY: + ERREXIT(cinfo, JERR_RLE_EMPTY); + break; + case RLE_EOF: + ERREXIT(cinfo, JERR_RLE_EOF); + break; + default: + ERREXIT(cinfo, JERR_RLE_BADERROR); + break; + } + + /* Figure out what we have, set private vars and return values accordingly */ + + width = source->header.xmax - source->header.xmin + 1; + height = source->header.ymax - source->header.ymin + 1; + source->header.xmin = 0; /* realign horizontally */ + source->header.xmax = width-1; + + cinfo->image_width = width; + cinfo->image_height = height; + cinfo->data_precision = 8; /* we can only handle 8 bit data */ + + if (source->header.ncolors == 1 && source->header.ncmap == 0) { + source->visual = GRAYSCALE; + TRACEMS2(cinfo, 1, JTRC_RLE_GRAY, width, height); + } else if (source->header.ncolors == 1 && source->header.ncmap == 1) { + source->visual = MAPPEDGRAY; + TRACEMS3(cinfo, 1, JTRC_RLE_MAPGRAY, width, height, + 1 << source->header.cmaplen); + } else if (source->header.ncolors == 1 && source->header.ncmap == 3) { + source->visual = PSEUDOCOLOR; + TRACEMS3(cinfo, 1, JTRC_RLE_MAPPED, width, height, + 1 << source->header.cmaplen); + } else if (source->header.ncolors == 3 && source->header.ncmap == 3) { + source->visual = TRUECOLOR; + TRACEMS3(cinfo, 1, JTRC_RLE_FULLMAP, width, height, + 1 << source->header.cmaplen); + } else if (source->header.ncolors == 3 && source->header.ncmap == 0) { + source->visual = DIRECTCOLOR; + TRACEMS2(cinfo, 1, JTRC_RLE, width, height); + } else + ERREXIT(cinfo, JERR_RLE_UNSUPPORTED); + + if (source->visual == GRAYSCALE || source->visual == MAPPEDGRAY) { + cinfo->in_color_space = JCS_GRAYSCALE; + cinfo->input_components = 1; + } else { + cinfo->in_color_space = JCS_RGB; + cinfo->input_components = 3; + } + + /* + * A place to hold each scanline while it's converted. + * (GRAYSCALE scanlines don't need converting) + */ + if (source->visual != GRAYSCALE) { + source->rle_row = (rle_pixel**) (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) width, (JDIMENSION) cinfo->input_components); + } + + /* request a virtual array to hold the image */ + source->image = (*cinfo->mem->request_virt_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + (JDIMENSION) (width * source->header.ncolors), + (JDIMENSION) height, (JDIMENSION) 1); + +#ifdef PROGRESS_REPORT + if (progress != NULL) { + /* count file input as separate pass */ + progress->total_extra_passes++; + } +#endif + + source->pub.buffer_height = 1; +} + + +/* + * Read one row of pixels. + * Called only after load_image has read the image into the virtual array. + * Used for GRAYSCALE, MAPPEDGRAY, TRUECOLOR, and DIRECTCOLOR images. + */ + +METHODDEF(JDIMENSION) +get_rle_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + rle_source_ptr source = (rle_source_ptr) sinfo; + + source->row--; + source->pub.buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, source->image, source->row, (JDIMENSION) 1, FALSE); + + return 1; +} + +/* + * Read one row of pixels. + * Called only after load_image has read the image into the virtual array. + * Used for PSEUDOCOLOR images. + */ + +METHODDEF(JDIMENSION) +get_pseudocolor_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + rle_source_ptr source = (rle_source_ptr) sinfo; + JSAMPROW src_row, dest_row; + JDIMENSION col; + rle_map *colormap; + int val; + + colormap = source->header.cmap; + dest_row = source->pub.buffer[0]; + source->row--; + src_row = * (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, source->image, source->row, (JDIMENSION) 1, FALSE); + + for (col = cinfo->image_width; col > 0; col--) { + val = GETJSAMPLE(*src_row++); + *dest_row++ = (JSAMPLE) (colormap[val ] >> 8); + *dest_row++ = (JSAMPLE) (colormap[val + 256] >> 8); + *dest_row++ = (JSAMPLE) (colormap[val + 512] >> 8); + } + + return 1; +} + + +/* + * Load the image into a virtual array. We have to do this because RLE + * files start at the lower left while the JPEG standard has them starting + * in the upper left. This is called the first time we want to get a row + * of input. What we do is load the RLE data into the array and then call + * the appropriate routine to read one row from the array. Before returning, + * we set source->pub.get_pixel_rows so that subsequent calls go straight to + * the appropriate row-reading routine. + */ + +METHODDEF(JDIMENSION) +load_image (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + rle_source_ptr source = (rle_source_ptr) sinfo; + JDIMENSION row, col; + JSAMPROW scanline, red_ptr, green_ptr, blue_ptr; + rle_pixel **rle_row; + rle_map *colormap; + char channel; +#ifdef PROGRESS_REPORT + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; +#endif + + colormap = source->header.cmap; + rle_row = source->rle_row; + + /* Read the RLE data into our virtual array. + * We assume here that (a) rle_pixel is represented the same as JSAMPLE, + * and (b) we are not on a machine where FAR pointers differ from regular. + */ + RLE_CLR_BIT(source->header, RLE_ALPHA); /* don't read the alpha channel */ + +#ifdef PROGRESS_REPORT + if (progress != NULL) { + progress->pub.pass_limit = cinfo->image_height; + progress->pub.pass_counter = 0; + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + } +#endif + + switch (source->visual) { + + case GRAYSCALE: + case PSEUDOCOLOR: + for (row = 0; row < cinfo->image_height; row++) { + rle_row = (rle_pixel **) (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, source->image, row, (JDIMENSION) 1, TRUE); + rle_getrow(&source->header, rle_row); +#ifdef PROGRESS_REPORT + if (progress != NULL) { + progress->pub.pass_counter++; + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + } +#endif + } + break; + + case MAPPEDGRAY: + case TRUECOLOR: + for (row = 0; row < cinfo->image_height; row++) { + scanline = * (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, source->image, row, (JDIMENSION) 1, TRUE); + rle_row = source->rle_row; + rle_getrow(&source->header, rle_row); + + for (col = 0; col < cinfo->image_width; col++) { + for (channel = 0; channel < source->header.ncolors; channel++) { + *scanline++ = (JSAMPLE) + (colormap[GETJSAMPLE(rle_row[channel][col]) + 256 * channel] >> 8); + } + } + +#ifdef PROGRESS_REPORT + if (progress != NULL) { + progress->pub.pass_counter++; + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + } +#endif + } + break; + + case DIRECTCOLOR: + for (row = 0; row < cinfo->image_height; row++) { + scanline = * (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, source->image, row, (JDIMENSION) 1, TRUE); + rle_getrow(&source->header, rle_row); + + red_ptr = rle_row[0]; + green_ptr = rle_row[1]; + blue_ptr = rle_row[2]; + + for (col = cinfo->image_width; col > 0; col--) { + *scanline++ = *red_ptr++; + *scanline++ = *green_ptr++; + *scanline++ = *blue_ptr++; + } + +#ifdef PROGRESS_REPORT + if (progress != NULL) { + progress->pub.pass_counter++; + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + } +#endif + } + } + +#ifdef PROGRESS_REPORT + if (progress != NULL) + progress->completed_extra_passes++; +#endif + + /* Set up to call proper row-extraction routine in future */ + if (source->visual == PSEUDOCOLOR) { + source->pub.buffer = source->rle_row; + source->pub.get_pixel_rows = get_pseudocolor_row; + } else { + source->pub.get_pixel_rows = get_rle_row; + } + source->row = cinfo->image_height; + + /* And fetch the topmost (bottommost) row */ + return (*source->pub.get_pixel_rows) (cinfo, sinfo); +} + + +/* + * Finish up at the end of the file. + */ + +METHODDEF(void) +finish_input_rle (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + /* no work */ +} + + +/* + * The module selection routine for RLE format input. + */ + +GLOBAL(cjpeg_source_ptr) +jinit_read_rle (j_compress_ptr cinfo) +{ + rle_source_ptr source; + + /* Create module interface object */ + source = (rle_source_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(rle_source_struct)); + /* Fill in method ptrs */ + source->pub.start_input = start_input_rle; + source->pub.finish_input = finish_input_rle; + source->pub.get_pixel_rows = load_image; + + return (cjpeg_source_ptr) source; +} + +#endif /* RLE_SUPPORTED */ diff --git a/Engine/lib/ljpeg/extras/rdswitch.c b/Engine/lib/ljpeg/extras/rdswitch.c new file mode 100644 index 000000000..4f4bb4f58 --- /dev/null +++ b/Engine/lib/ljpeg/extras/rdswitch.c @@ -0,0 +1,332 @@ +/* + * rdswitch.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to process some of cjpeg's more complicated + * command-line switches. Switches processed here are: + * -qtables file Read quantization tables from text file + * -scans file Read scan script from text file + * -qslots N[,N,...] Set component quantization table selectors + * -sample HxV[,HxV,...] Set component sampling factors + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ +#include /* to declare isdigit(), isspace() */ + + +LOCAL(int) +text_getc (FILE * file) +/* Read next char, skipping over any comments (# to end of line) */ +/* A comment/newline sequence is returned as a newline */ +{ + register int ch; + + ch = getc(file); + if (ch == '#') { + do { + ch = getc(file); + } while (ch != '\n' && ch != EOF); + } + return ch; +} + + +LOCAL(boolean) +read_text_integer (FILE * file, long * result, int * termchar) +/* Read an unsigned decimal integer from a file, store it in result */ +/* Reads one trailing character after the integer; returns it in termchar */ +{ + register int ch; + register long val; + + /* Skip any leading whitespace, detect EOF */ + do { + ch = text_getc(file); + if (ch == EOF) { + *termchar = ch; + return FALSE; + } + } while (isspace(ch)); + + if (! isdigit(ch)) { + *termchar = ch; + return FALSE; + } + + val = ch - '0'; + while ((ch = text_getc(file)) != EOF) { + if (! isdigit(ch)) + break; + val *= 10; + val += ch - '0'; + } + *result = val; + *termchar = ch; + return TRUE; +} + + +GLOBAL(boolean) +read_quant_tables (j_compress_ptr cinfo, char * filename, + int scale_factor, boolean force_baseline) +/* Read a set of quantization tables from the specified file. + * The file is plain ASCII text: decimal numbers with whitespace between. + * Comments preceded by '#' may be included in the file. + * There may be one to NUM_QUANT_TBLS tables in the file, each of 64 values. + * The tables are implicitly numbered 0,1,etc. + * NOTE: does not affect the qslots mapping, which will default to selecting + * table 0 for luminance (or primary) components, 1 for chrominance components. + * You must use -qslots if you want a different component->table mapping. + */ +{ + FILE * fp; + int tblno, i, termchar; + long val; + unsigned int table[DCTSIZE2]; + + if ((fp = fopen(filename, "r")) == NULL) { + fprintf(stderr, "Can't open table file %s\n", filename); + return FALSE; + } + tblno = 0; + + while (read_text_integer(fp, &val, &termchar)) { /* read 1st element of table */ + if (tblno >= NUM_QUANT_TBLS) { + fprintf(stderr, "Too many tables in file %s\n", filename); + fclose(fp); + return FALSE; + } + table[0] = (unsigned int) val; + for (i = 1; i < DCTSIZE2; i++) { + if (! read_text_integer(fp, &val, &termchar)) { + fprintf(stderr, "Invalid table data in file %s\n", filename); + fclose(fp); + return FALSE; + } + table[i] = (unsigned int) val; + } + jpeg_add_quant_table(cinfo, tblno, table, scale_factor, force_baseline); + tblno++; + } + + if (termchar != EOF) { + fprintf(stderr, "Non-numeric data in file %s\n", filename); + fclose(fp); + return FALSE; + } + + fclose(fp); + return TRUE; +} + + +#ifdef C_MULTISCAN_FILES_SUPPORTED + +LOCAL(boolean) +read_scan_integer (FILE * file, long * result, int * termchar) +/* Variant of read_text_integer that always looks for a non-space termchar; + * this simplifies parsing of punctuation in scan scripts. + */ +{ + register int ch; + + if (! read_text_integer(file, result, termchar)) + return FALSE; + ch = *termchar; + while (ch != EOF && isspace(ch)) + ch = text_getc(file); + if (isdigit(ch)) { /* oops, put it back */ + if (ungetc(ch, file) == EOF) + return FALSE; + ch = ' '; + } else { + /* Any separators other than ';' and ':' are ignored; + * this allows user to insert commas, etc, if desired. + */ + if (ch != EOF && ch != ';' && ch != ':') + ch = ' '; + } + *termchar = ch; + return TRUE; +} + + +GLOBAL(boolean) +read_scan_script (j_compress_ptr cinfo, char * filename) +/* Read a scan script from the specified text file. + * Each entry in the file defines one scan to be emitted. + * Entries are separated by semicolons ';'. + * An entry contains one to four component indexes, + * optionally followed by a colon ':' and four progressive-JPEG parameters. + * The component indexes denote which component(s) are to be transmitted + * in the current scan. The first component has index 0. + * Sequential JPEG is used if the progressive-JPEG parameters are omitted. + * The file is free format text: any whitespace may appear between numbers + * and the ':' and ';' punctuation marks. Also, other punctuation (such + * as commas or dashes) can be placed between numbers if desired. + * Comments preceded by '#' may be included in the file. + * Note: we do very little validity checking here; + * jcmaster.c will validate the script parameters. + */ +{ + FILE * fp; + int scanno, ncomps, termchar; + long val; + jpeg_scan_info * scanptr; +#define MAX_SCANS 100 /* quite arbitrary limit */ + jpeg_scan_info scans[MAX_SCANS]; + + if ((fp = fopen(filename, "r")) == NULL) { + fprintf(stderr, "Can't open scan definition file %s\n", filename); + return FALSE; + } + scanptr = scans; + scanno = 0; + + while (read_scan_integer(fp, &val, &termchar)) { + if (scanno >= MAX_SCANS) { + fprintf(stderr, "Too many scans defined in file %s\n", filename); + fclose(fp); + return FALSE; + } + scanptr->component_index[0] = (int) val; + ncomps = 1; + while (termchar == ' ') { + if (ncomps >= MAX_COMPS_IN_SCAN) { + fprintf(stderr, "Too many components in one scan in file %s\n", + filename); + fclose(fp); + return FALSE; + } + if (! read_scan_integer(fp, &val, &termchar)) + goto bogus; + scanptr->component_index[ncomps] = (int) val; + ncomps++; + } + scanptr->comps_in_scan = ncomps; + if (termchar == ':') { + if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ') + goto bogus; + scanptr->Ss = (int) val; + if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ') + goto bogus; + scanptr->Se = (int) val; + if (! read_scan_integer(fp, &val, &termchar) || termchar != ' ') + goto bogus; + scanptr->Ah = (int) val; + if (! read_scan_integer(fp, &val, &termchar)) + goto bogus; + scanptr->Al = (int) val; + } else { + /* set non-progressive parameters */ + scanptr->Ss = 0; + scanptr->Se = DCTSIZE2-1; + scanptr->Ah = 0; + scanptr->Al = 0; + } + if (termchar != ';' && termchar != EOF) { +bogus: + fprintf(stderr, "Invalid scan entry format in file %s\n", filename); + fclose(fp); + return FALSE; + } + scanptr++, scanno++; + } + + if (termchar != EOF) { + fprintf(stderr, "Non-numeric data in file %s\n", filename); + fclose(fp); + return FALSE; + } + + if (scanno > 0) { + /* Stash completed scan list in cinfo structure. + * NOTE: for cjpeg's use, JPOOL_IMAGE is the right lifetime for this data, + * but if you want to compress multiple images you'd want JPOOL_PERMANENT. + */ + scanptr = (jpeg_scan_info *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + scanno * SIZEOF(jpeg_scan_info)); + MEMCOPY(scanptr, scans, scanno * SIZEOF(jpeg_scan_info)); + cinfo->scan_info = scanptr; + cinfo->num_scans = scanno; + } + + fclose(fp); + return TRUE; +} + +#endif /* C_MULTISCAN_FILES_SUPPORTED */ + + +GLOBAL(boolean) +set_quant_slots (j_compress_ptr cinfo, char *arg) +/* Process a quantization-table-selectors parameter string, of the form + * N[,N,...] + * If there are more components than parameters, the last value is replicated. + */ +{ + int val = 0; /* default table # */ + int ci; + char ch; + + for (ci = 0; ci < MAX_COMPONENTS; ci++) { + if (*arg) { + ch = ','; /* if not set by sscanf, will be ',' */ + if (sscanf(arg, "%d%c", &val, &ch) < 1) + return FALSE; + if (ch != ',') /* syntax check */ + return FALSE; + if (val < 0 || val >= NUM_QUANT_TBLS) { + fprintf(stderr, "JPEG quantization tables are numbered 0..%d\n", + NUM_QUANT_TBLS-1); + return FALSE; + } + cinfo->comp_info[ci].quant_tbl_no = val; + while (*arg && *arg++ != ',') /* advance to next segment of arg string */ + ; + } else { + /* reached end of parameter, set remaining components to last table */ + cinfo->comp_info[ci].quant_tbl_no = val; + } + } + return TRUE; +} + + +GLOBAL(boolean) +set_sample_factors (j_compress_ptr cinfo, char *arg) +/* Process a sample-factors parameter string, of the form + * HxV[,HxV,...] + * If there are more components than parameters, "1x1" is assumed for the rest. + */ +{ + int ci, val1, val2; + char ch1, ch2; + + for (ci = 0; ci < MAX_COMPONENTS; ci++) { + if (*arg) { + ch2 = ','; /* if not set by sscanf, will be ',' */ + if (sscanf(arg, "%d%c%d%c", &val1, &ch1, &val2, &ch2) < 3) + return FALSE; + if ((ch1 != 'x' && ch1 != 'X') || ch2 != ',') /* syntax check */ + return FALSE; + if (val1 <= 0 || val1 > 4 || val2 <= 0 || val2 > 4) { + fprintf(stderr, "JPEG sampling factors must be 1..4\n"); + return FALSE; + } + cinfo->comp_info[ci].h_samp_factor = val1; + cinfo->comp_info[ci].v_samp_factor = val2; + while (*arg && *arg++ != ',') /* advance to next segment of arg string */ + ; + } else { + /* reached end of parameter, set remaining components to 1x1 sampling */ + cinfo->comp_info[ci].h_samp_factor = 1; + cinfo->comp_info[ci].v_samp_factor = 1; + } + } + return TRUE; +} diff --git a/Engine/lib/ljpeg/extras/rdtarga.c b/Engine/lib/ljpeg/extras/rdtarga.c new file mode 100644 index 000000000..4c2cd2673 --- /dev/null +++ b/Engine/lib/ljpeg/extras/rdtarga.c @@ -0,0 +1,500 @@ +/* + * rdtarga.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to read input images in Targa format. + * + * These routines may need modification for non-Unix environments or + * specialized applications. As they stand, they assume input from + * an ordinary stdio stream. They further assume that reading begins + * at the start of the file; start_input may need work if the + * user interface has already read some data (e.g., to determine that + * the file is indeed Targa format). + * + * Based on code contributed by Lee Daniel Crocker. + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + +#ifdef TARGA_SUPPORTED + + +/* Macros to deal with unsigned chars as efficiently as compiler allows */ + +#ifdef HAVE_UNSIGNED_CHAR +typedef unsigned char U_CHAR; +#define UCH(x) ((int) (x)) +#else /* !HAVE_UNSIGNED_CHAR */ +#ifdef CHAR_IS_UNSIGNED +typedef char U_CHAR; +#define UCH(x) ((int) (x)) +#else +typedef char U_CHAR; +#define UCH(x) ((int) (x) & 0xFF) +#endif +#endif /* HAVE_UNSIGNED_CHAR */ + + +#define ReadOK(file,buffer,len) (JFREAD(file,buffer,len) == ((size_t) (len))) + + +/* Private version of data source object */ + +typedef struct _tga_source_struct * tga_source_ptr; + +typedef struct _tga_source_struct { + struct cjpeg_source_struct pub; /* public fields */ + + j_compress_ptr cinfo; /* back link saves passing separate parm */ + + JSAMPARRAY colormap; /* Targa colormap (converted to my format) */ + + jvirt_sarray_ptr whole_image; /* Needed if funny input row order */ + JDIMENSION current_row; /* Current logical row number to read */ + + /* Pointer to routine to extract next Targa pixel from input file */ + JMETHOD(void, read_pixel, (tga_source_ptr sinfo)); + + /* Result of read_pixel is delivered here: */ + U_CHAR tga_pixel[4]; + + int pixel_size; /* Bytes per Targa pixel (1 to 4) */ + + /* State info for reading RLE-coded pixels; both counts must be init to 0 */ + int block_count; /* # of pixels remaining in RLE block */ + int dup_pixel_count; /* # of times to duplicate previous pixel */ + + /* This saves the correct pixel-row-expansion method for preload_image */ + JMETHOD(JDIMENSION, get_pixel_rows, (j_compress_ptr cinfo, + cjpeg_source_ptr sinfo)); +} tga_source_struct; + + +/* For expanding 5-bit pixel values to 8-bit with best rounding */ + +static const UINT8 c5to8bits[32] = { + 0, 8, 16, 25, 33, 41, 49, 58, + 66, 74, 82, 90, 99, 107, 115, 123, + 132, 140, 148, 156, 165, 173, 181, 189, + 197, 206, 214, 222, 230, 239, 247, 255 +}; + + + +LOCAL(int) +read_byte (tga_source_ptr sinfo) +/* Read next byte from Targa file */ +{ + register FILE *infile = sinfo->pub.input_file; + register int c; + + if ((c = getc(infile)) == EOF) + ERREXIT(sinfo->cinfo, JERR_INPUT_EOF); + return c; +} + + +LOCAL(void) +read_colormap (tga_source_ptr sinfo, int cmaplen, int mapentrysize) +/* Read the colormap from a Targa file */ +{ + int i; + + /* Presently only handles 24-bit BGR format */ + if (mapentrysize != 24) + ERREXIT(sinfo->cinfo, JERR_TGA_BADCMAP); + + for (i = 0; i < cmaplen; i++) { + sinfo->colormap[2][i] = (JSAMPLE) read_byte(sinfo); + sinfo->colormap[1][i] = (JSAMPLE) read_byte(sinfo); + sinfo->colormap[0][i] = (JSAMPLE) read_byte(sinfo); + } +} + + +/* + * read_pixel methods: get a single pixel from Targa file into tga_pixel[] + */ + +METHODDEF(void) +read_non_rle_pixel (tga_source_ptr sinfo) +/* Read one Targa pixel from the input file; no RLE expansion */ +{ + register FILE *infile = sinfo->pub.input_file; + register int i; + + for (i = 0; i < sinfo->pixel_size; i++) { + sinfo->tga_pixel[i] = (U_CHAR) getc(infile); + } +} + + +METHODDEF(void) +read_rle_pixel (tga_source_ptr sinfo) +/* Read one Targa pixel from the input file, expanding RLE data as needed */ +{ + register FILE *infile = sinfo->pub.input_file; + register int i; + + /* Duplicate previously read pixel? */ + if (sinfo->dup_pixel_count > 0) { + sinfo->dup_pixel_count--; + return; + } + + /* Time to read RLE block header? */ + if (--sinfo->block_count < 0) { /* decrement pixels remaining in block */ + i = read_byte(sinfo); + if (i & 0x80) { /* Start of duplicate-pixel block? */ + sinfo->dup_pixel_count = i & 0x7F; /* number of dups after this one */ + sinfo->block_count = 0; /* then read new block header */ + } else { + sinfo->block_count = i & 0x7F; /* number of pixels after this one */ + } + } + + /* Read next pixel */ + for (i = 0; i < sinfo->pixel_size; i++) { + sinfo->tga_pixel[i] = (U_CHAR) getc(infile); + } +} + + +/* + * Read one row of pixels. + * + * We provide several different versions depending on input file format. + */ + + +METHODDEF(JDIMENSION) +get_8bit_gray_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading 8-bit grayscale pixels */ +{ + tga_source_ptr source = (tga_source_ptr) sinfo; + register JSAMPROW ptr; + register JDIMENSION col; + + ptr = source->pub.buffer[0]; + for (col = cinfo->image_width; col > 0; col--) { + (*source->read_pixel) (source); /* Load next pixel into tga_pixel */ + *ptr++ = (JSAMPLE) UCH(source->tga_pixel[0]); + } + return 1; +} + +METHODDEF(JDIMENSION) +get_8bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading 8-bit colormap indexes */ +{ + tga_source_ptr source = (tga_source_ptr) sinfo; + register int t; + register JSAMPROW ptr; + register JDIMENSION col; + register JSAMPARRAY colormap = source->colormap; + + ptr = source->pub.buffer[0]; + for (col = cinfo->image_width; col > 0; col--) { + (*source->read_pixel) (source); /* Load next pixel into tga_pixel */ + t = UCH(source->tga_pixel[0]); + *ptr++ = colormap[0][t]; + *ptr++ = colormap[1][t]; + *ptr++ = colormap[2][t]; + } + return 1; +} + +METHODDEF(JDIMENSION) +get_16bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading 16-bit pixels */ +{ + tga_source_ptr source = (tga_source_ptr) sinfo; + register int t; + register JSAMPROW ptr; + register JDIMENSION col; + + ptr = source->pub.buffer[0]; + for (col = cinfo->image_width; col > 0; col--) { + (*source->read_pixel) (source); /* Load next pixel into tga_pixel */ + t = UCH(source->tga_pixel[0]); + t += UCH(source->tga_pixel[1]) << 8; + /* We expand 5 bit data to 8 bit sample width. + * The format of the 16-bit (LSB first) input word is + * xRRRRRGGGGGBBBBB + */ + ptr[2] = (JSAMPLE) c5to8bits[t & 0x1F]; + t >>= 5; + ptr[1] = (JSAMPLE) c5to8bits[t & 0x1F]; + t >>= 5; + ptr[0] = (JSAMPLE) c5to8bits[t & 0x1F]; + ptr += 3; + } + return 1; +} + +METHODDEF(JDIMENSION) +get_24bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading 24-bit pixels */ +{ + tga_source_ptr source = (tga_source_ptr) sinfo; + register JSAMPROW ptr; + register JDIMENSION col; + + ptr = source->pub.buffer[0]; + for (col = cinfo->image_width; col > 0; col--) { + (*source->read_pixel) (source); /* Load next pixel into tga_pixel */ + *ptr++ = (JSAMPLE) UCH(source->tga_pixel[2]); /* change BGR to RGB order */ + *ptr++ = (JSAMPLE) UCH(source->tga_pixel[1]); + *ptr++ = (JSAMPLE) UCH(source->tga_pixel[0]); + } + return 1; +} + +/* + * Targa also defines a 32-bit pixel format with order B,G,R,A. + * We presently ignore the attribute byte, so the code for reading + * these pixels is identical to the 24-bit routine above. + * This works because the actual pixel length is only known to read_pixel. + */ + +#define get_32bit_row get_24bit_row + + +/* + * This method is for re-reading the input data in standard top-down + * row order. The entire image has already been read into whole_image + * with proper conversion of pixel format, but it's in a funny row order. + */ + +METHODDEF(JDIMENSION) +get_memory_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + tga_source_ptr source = (tga_source_ptr) sinfo; + JDIMENSION source_row; + + /* Compute row of source that maps to current_row of normal order */ + /* For now, assume image is bottom-up and not interlaced. */ + /* NEEDS WORK to support interlaced images! */ + source_row = cinfo->image_height - source->current_row - 1; + + /* Fetch that row from virtual array */ + source->pub.buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, source->whole_image, + source_row, (JDIMENSION) 1, FALSE); + + source->current_row++; + return 1; +} + + +/* + * This method loads the image into whole_image during the first call on + * get_pixel_rows. The get_pixel_rows pointer is then adjusted to call + * get_memory_row on subsequent calls. + */ + +METHODDEF(JDIMENSION) +preload_image (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + tga_source_ptr source = (tga_source_ptr) sinfo; + JDIMENSION row; + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; + + /* Read the data into a virtual array in input-file row order. */ + for (row = 0; row < cinfo->image_height; row++) { + if (progress != NULL) { + progress->pub.pass_counter = (long) row; + progress->pub.pass_limit = (long) cinfo->image_height; + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + } + source->pub.buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, source->whole_image, row, (JDIMENSION) 1, TRUE); + (*source->get_pixel_rows) (cinfo, sinfo); + } + if (progress != NULL) + progress->completed_extra_passes++; + + /* Set up to read from the virtual array in unscrambled order */ + source->pub.get_pixel_rows = get_memory_row; + source->current_row = 0; + /* And read the first row */ + return get_memory_row(cinfo, sinfo); +} + + +/* + * Read the file header; return image size and component count. + */ + +METHODDEF(void) +start_input_tga (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + tga_source_ptr source = (tga_source_ptr) sinfo; + U_CHAR targaheader[18]; + int idlen, cmaptype, subtype, flags, interlace_type, components; + unsigned int width, height, maplen; + boolean is_bottom_up; + +#define GET_2B(offset) ((unsigned int) UCH(targaheader[offset]) + \ + (((unsigned int) UCH(targaheader[offset+1])) << 8)) + + if (! ReadOK(source->pub.input_file, targaheader, 18)) + ERREXIT(cinfo, JERR_INPUT_EOF); + + /* Pretend "15-bit" pixels are 16-bit --- we ignore attribute bit anyway */ + if (targaheader[16] == 15) + targaheader[16] = 16; + + idlen = UCH(targaheader[0]); + cmaptype = UCH(targaheader[1]); + subtype = UCH(targaheader[2]); + maplen = GET_2B(5); + width = GET_2B(12); + height = GET_2B(14); + source->pixel_size = UCH(targaheader[16]) >> 3; + flags = UCH(targaheader[17]); /* Image Descriptor byte */ + + is_bottom_up = ((flags & 0x20) == 0); /* bit 5 set => top-down */ + interlace_type = flags >> 6; /* bits 6/7 are interlace code */ + + if (cmaptype > 1 || /* cmaptype must be 0 or 1 */ + source->pixel_size < 1 || source->pixel_size > 4 || + (UCH(targaheader[16]) & 7) != 0 || /* bits/pixel must be multiple of 8 */ + interlace_type != 0) /* currently don't allow interlaced image */ + ERREXIT(cinfo, JERR_TGA_BADPARMS); + + if (subtype > 8) { + /* It's an RLE-coded file */ + source->read_pixel = read_rle_pixel; + source->block_count = source->dup_pixel_count = 0; + subtype -= 8; + } else { + /* Non-RLE file */ + source->read_pixel = read_non_rle_pixel; + } + + /* Now should have subtype 1, 2, or 3 */ + components = 3; /* until proven different */ + cinfo->in_color_space = JCS_RGB; + + switch (subtype) { + case 1: /* Colormapped image */ + if (source->pixel_size == 1 && cmaptype == 1) + source->get_pixel_rows = get_8bit_row; + else + ERREXIT(cinfo, JERR_TGA_BADPARMS); + TRACEMS2(cinfo, 1, JTRC_TGA_MAPPED, width, height); + break; + case 2: /* RGB image */ + switch (source->pixel_size) { + case 2: + source->get_pixel_rows = get_16bit_row; + break; + case 3: + source->get_pixel_rows = get_24bit_row; + break; + case 4: + source->get_pixel_rows = get_32bit_row; + break; + default: + ERREXIT(cinfo, JERR_TGA_BADPARMS); + break; + } + TRACEMS2(cinfo, 1, JTRC_TGA, width, height); + break; + case 3: /* Grayscale image */ + components = 1; + cinfo->in_color_space = JCS_GRAYSCALE; + if (source->pixel_size == 1) + source->get_pixel_rows = get_8bit_gray_row; + else + ERREXIT(cinfo, JERR_TGA_BADPARMS); + TRACEMS2(cinfo, 1, JTRC_TGA_GRAY, width, height); + break; + default: + ERREXIT(cinfo, JERR_TGA_BADPARMS); + break; + } + + if (is_bottom_up) { + /* Create a virtual array to buffer the upside-down image. */ + source->whole_image = (*cinfo->mem->request_virt_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + (JDIMENSION) width * components, (JDIMENSION) height, (JDIMENSION) 1); + if (cinfo->progress != NULL) { + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; + progress->total_extra_passes++; /* count file input as separate pass */ + } + /* source->pub.buffer will point to the virtual array. */ + source->pub.buffer_height = 1; /* in case anyone looks at it */ + source->pub.get_pixel_rows = preload_image; + } else { + /* Don't need a virtual array, but do need a one-row input buffer. */ + source->whole_image = NULL; + source->pub.buffer = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) width * components, (JDIMENSION) 1); + source->pub.buffer_height = 1; + source->pub.get_pixel_rows = source->get_pixel_rows; + } + + while (idlen--) /* Throw away ID field */ + (void) read_byte(source); + + if (maplen > 0) { + if (maplen > 256 || GET_2B(3) != 0) + ERREXIT(cinfo, JERR_TGA_BADCMAP); + /* Allocate space to store the colormap */ + source->colormap = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, (JDIMENSION) maplen, (JDIMENSION) 3); + /* and read it from the file */ + read_colormap(source, (int) maplen, UCH(targaheader[7])); + } else { + if (cmaptype) /* but you promised a cmap! */ + ERREXIT(cinfo, JERR_TGA_BADPARMS); + source->colormap = NULL; + } + + cinfo->input_components = components; + cinfo->data_precision = 8; + cinfo->image_width = width; + cinfo->image_height = height; +} + + +/* + * Finish up at the end of the file. + */ + +METHODDEF(void) +finish_input_tga (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + /* no work */ +} + + +/* + * The module selection routine for Targa format input. + */ + +GLOBAL(cjpeg_source_ptr) +jinit_read_targa (j_compress_ptr cinfo) +{ + tga_source_ptr source; + + /* Create module interface object */ + source = (tga_source_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(tga_source_struct)); + source->cinfo = cinfo; /* make back link for subroutines */ + /* Fill in method ptrs, except get_pixel_rows which start_input sets */ + source->pub.start_input = start_input_tga; + source->pub.finish_input = finish_input_tga; + + return (cjpeg_source_ptr) source; +} + +#endif /* TARGA_SUPPORTED */ diff --git a/Engine/lib/ljpeg/extras/structure.doc b/Engine/lib/ljpeg/extras/structure.doc new file mode 100644 index 000000000..51c9def7e --- /dev/null +++ b/Engine/lib/ljpeg/extras/structure.doc @@ -0,0 +1,948 @@ +IJG JPEG LIBRARY: SYSTEM ARCHITECTURE + +Copyright (C) 1991-1995, Thomas G. Lane. +This file is part of the Independent JPEG Group's software. +For conditions of distribution and use, see the accompanying README file. + + +This file provides an overview of the architecture of the IJG JPEG software; +that is, the functions of the various modules in the system and the interfaces +between modules. For more precise details about any data structure or calling +convention, see the include files and comments in the source code. + +We assume that the reader is already somewhat familiar with the JPEG standard. +The README file includes references for learning about JPEG. The file +libjpeg.doc describes the library from the viewpoint of an application +programmer using the library; it's best to read that file before this one. +Also, the file coderules.doc describes the coding style conventions we use. + +In this document, JPEG-specific terminology follows the JPEG standard: + A "component" means a color channel, e.g., Red or Luminance. + A "sample" is a single component value (i.e., one number in the image data). + A "coefficient" is a frequency coefficient (a DCT transform output number). + A "block" is an 8x8 group of samples or coefficients. + An "MCU" (minimum coded unit) is an interleaved set of blocks of size + determined by the sampling factors, or a single block in a + noninterleaved scan. +We do not use the terms "pixel" and "sample" interchangeably. When we say +pixel, we mean an element of the full-size image, while a sample is an element +of the downsampled image. Thus the number of samples may vary across +components while the number of pixels does not. (This terminology is not used +rigorously throughout the code, but it is used in places where confusion would +otherwise result.) + + +*** System features *** + +The IJG distribution contains two parts: + * A subroutine library for JPEG compression and decompression. + * cjpeg/djpeg, two sample applications that use the library to transform + JFIF JPEG files to and from several other image formats. +cjpeg/djpeg are of no great intellectual complexity: they merely add a simple +command-line user interface and I/O routines for several uncompressed image +formats. This document concentrates on the library itself. + +We desire the library to be capable of supporting all JPEG baseline, extended +sequential, and progressive DCT processes. Hierarchical processes are not +supported. + +The library does not support the lossless (spatial) JPEG process. Lossless +JPEG shares little or no code with lossy JPEG, and would normally be used +without the extensive pre- and post-processing provided by this library. +We feel that lossless JPEG is better handled by a separate library. + +Within these limits, any set of compression parameters allowed by the JPEG +spec should be readable for decompression. (We can be more restrictive about +what formats we can generate.) Although the system design allows for all +parameter values, some uncommon settings are not yet implemented and may +never be; nonintegral sampling ratios are the prime example. Furthermore, +we treat 8-bit vs. 12-bit data precision as a compile-time switch, not a +run-time option, because most machines can store 8-bit pixels much more +compactly than 12-bit. + +For legal reasons, JPEG arithmetic coding is not currently supported, but +extending the library to include it would be straightforward. + +By itself, the library handles only interchange JPEG datastreams --- in +particular the widely used JFIF file format. The library can be used by +surrounding code to process interchange or abbreviated JPEG datastreams that +are embedded in more complex file formats. (For example, libtiff uses this +library to implement JPEG compression within the TIFF file format.) + +The library includes a substantial amount of code that is not covered by the +JPEG standard but is necessary for typical applications of JPEG. These +functions preprocess the image before JPEG compression or postprocess it after +decompression. They include colorspace conversion, downsampling/upsampling, +and color quantization. This code can be omitted if not needed. + +A wide range of quality vs. speed tradeoffs are possible in JPEG processing, +and even more so in decompression postprocessing. The decompression library +provides multiple implementations that cover most of the useful tradeoffs, +ranging from very-high-quality down to fast-preview operation. On the +compression side we have generally not provided low-quality choices, since +compression is normally less time-critical. It should be understood that the +low-quality modes may not meet the JPEG standard's accuracy requirements; +nonetheless, they are useful for viewers. + + +*** Portability issues *** + +Portability is an essential requirement for the library. The key portability +issues that show up at the level of system architecture are: + +1. Memory usage. We want the code to be able to run on PC-class machines +with limited memory. Images should therefore be processed sequentially (in +strips), to avoid holding the whole image in memory at once. Where a +full-image buffer is necessary, we should be able to use either virtual memory +or temporary files. + +2. Near/far pointer distinction. To run efficiently on 80x86 machines, the +code should distinguish "small" objects (kept in near data space) from +"large" ones (kept in far data space). This is an annoying restriction, but +fortunately it does not impact code quality for less brain-damaged machines, +and the source code clutter turns out to be minimal with sufficient use of +pointer typedefs. + +3. Data precision. We assume that "char" is at least 8 bits, "short" and +"int" at least 16, "long" at least 32. The code will work fine with larger +data sizes, although memory may be used inefficiently in some cases. However, +the JPEG compressed datastream must ultimately appear on external storage as a +sequence of 8-bit bytes if it is to conform to the standard. This may pose a +problem on machines where char is wider than 8 bits. The library represents +compressed data as an array of values of typedef JOCTET. If no data type +exactly 8 bits wide is available, custom data source and data destination +modules must be written to unpack and pack the chosen JOCTET datatype into +8-bit external representation. + + +*** System overview *** + +The compressor and decompressor are each divided into two main sections: +the JPEG compressor or decompressor proper, and the preprocessing or +postprocessing functions. The interface between these two sections is the +image data that the official JPEG spec regards as its input or output: this +data is in the colorspace to be used for compression, and it is downsampled +to the sampling factors to be used. The preprocessing and postprocessing +steps are responsible for converting a normal image representation to or from +this form. (Those few applications that want to deal with YCbCr downsampled +data can skip the preprocessing or postprocessing step.) + +Looking more closely, the compressor library contains the following main +elements: + + Preprocessing: + * Color space conversion (e.g., RGB to YCbCr). + * Edge expansion and downsampling. Optionally, this step can do simple + smoothing --- this is often helpful for low-quality source data. + JPEG proper: + * MCU assembly, DCT, quantization. + * Entropy coding (sequential or progressive, Huffman or arithmetic). + +In addition to these modules we need overall control, marker generation, +and support code (memory management & error handling). There is also a +module responsible for physically writing the output data --- typically +this is just an interface to fwrite(), but some applications may need to +do something else with the data. + +The decompressor library contains the following main elements: + + JPEG proper: + * Entropy decoding (sequential or progressive, Huffman or arithmetic). + * Dequantization, inverse DCT, MCU disassembly. + Postprocessing: + * Upsampling. Optionally, this step may be able to do more general + rescaling of the image. + * Color space conversion (e.g., YCbCr to RGB). This step may also + provide gamma adjustment [ currently it does not ]. + * Optional color quantization (e.g., reduction to 256 colors). + * Optional color precision reduction (e.g., 24-bit to 15-bit color). + [This feature is not currently implemented.] + +We also need overall control, marker parsing, and a data source module. +The support code (memory management & error handling) can be shared with +the compression half of the library. + +There may be several implementations of each of these elements, particularly +in the decompressor, where a wide range of speed/quality tradeoffs is very +useful. It must be understood that some of the best speedups involve +merging adjacent steps in the pipeline. For example, upsampling, color space +conversion, and color quantization might all be done at once when using a +low-quality ordered-dither technique. The system architecture is designed to +allow such merging where appropriate. + + +Note: it is convenient to regard edge expansion (padding to block boundaries) +as a preprocessing/postprocessing function, even though the JPEG spec includes +it in compression/decompression. We do this because downsampling/upsampling +can be simplified a little if they work on padded data: it's not necessary to +have special cases at the right and bottom edges. Therefore the interface +buffer is always an integral number of blocks wide and high, and we expect +compression preprocessing to pad the source data properly. Padding will occur +only to the next block (8-sample) boundary. In an interleaved-scan situation, +additional dummy blocks may be used to fill out MCUs, but the MCU assembly and +disassembly logic will create or discard these blocks internally. (This is +advantageous for speed reasons, since we avoid DCTing the dummy blocks. +It also permits a small reduction in file size, because the compressor can +choose dummy block contents so as to minimize their size in compressed form. +Finally, it makes the interface buffer specification independent of whether +the file is actually interleaved or not.) Applications that wish to deal +directly with the downsampled data must provide similar buffering and padding +for odd-sized images. + + +*** Poor man's object-oriented programming *** + +It should be clear by now that we have a lot of quasi-independent processing +steps, many of which have several possible behaviors. To avoid cluttering the +code with lots of switch statements, we use a simple form of object-style +programming to separate out the different possibilities. + +For example, two different color quantization algorithms could be implemented +as two separate modules that present the same external interface; at runtime, +the calling code will access the proper module indirectly through an "object". + +We can get the limited features we need while staying within portable C. +The basic tool is a function pointer. An "object" is just a struct +containing one or more function pointer fields, each of which corresponds to +a method name in real object-oriented languages. During initialization we +fill in the function pointers with references to whichever module we have +determined we need to use in this run. Then invocation of the module is done +by indirecting through a function pointer; on most machines this is no more +expensive than a switch statement, which would be the only other way of +making the required run-time choice. The really significant benefit, of +course, is keeping the source code clean and well structured. + +We can also arrange to have private storage that varies between different +implementations of the same kind of object. We do this by making all the +module-specific object structs be separately allocated entities, which will +be accessed via pointers in the master compression or decompression struct. +The "public" fields or methods for a given kind of object are specified by +a commonly known struct. But a module's initialization code can allocate +a larger struct that contains the common struct as its first member, plus +additional private fields. With appropriate pointer casting, the module's +internal functions can access these private fields. (For a simple example, +see jdatadst.c, which implements the external interface specified by struct +jpeg_destination_mgr, but adds extra fields.) + +(Of course this would all be a lot easier if we were using C++, but we are +not yet prepared to assume that everyone has a C++ compiler.) + +An important benefit of this scheme is that it is easy to provide multiple +versions of any method, each tuned to a particular case. While a lot of +precalculation might be done to select an optimal implementation of a method, +the cost per invocation is constant. For example, the upsampling step might +have a "generic" method, plus one or more "hardwired" methods for the most +popular sampling factors; the hardwired methods would be faster because they'd +use straight-line code instead of for-loops. The cost to determine which +method to use is paid only once, at startup, and the selection criteria are +hidden from the callers of the method. + +This plan differs a little bit from usual object-oriented structures, in that +only one instance of each object class will exist during execution. The +reason for having the class structure is that on different runs we may create +different instances (choose to execute different modules). You can think of +the term "method" as denoting the common interface presented by a particular +set of interchangeable functions, and "object" as denoting a group of related +methods, or the total shared interface behavior of a group of modules. + + +*** Overall control structure *** + +We previously mentioned the need for overall control logic in the compression +and decompression libraries. In IJG implementations prior to v5, overall +control was mostly provided by "pipeline control" modules, which proved to be +large, unwieldy, and hard to understand. To improve the situation, the +control logic has been subdivided into multiple modules. The control modules +consist of: + +1. Master control for module selection and initialization. This has two +responsibilities: + + 1A. Startup initialization at the beginning of image processing. + The individual processing modules to be used in this run are selected + and given initialization calls. + + 1B. Per-pass control. This determines how many passes will be performed + and calls each active processing module to configure itself + appropriately at the beginning of each pass. End-of-pass processing, + where necessary, is also invoked from the master control module. + + Method selection is partially distributed, in that a particular processing + module may contain several possible implementations of a particular method, + which it will select among when given its initialization call. The master + control code need only be concerned with decisions that affect more than + one module. + +2. Data buffering control. A separate control module exists for each + inter-processing-step data buffer. This module is responsible for + invoking the processing steps that write or read that data buffer. + +Each buffer controller sees the world as follows: + +input data => processing step A => buffer => processing step B => output data + | | | + ------------------ controller ------------------ + +The controller knows the dataflow requirements of steps A and B: how much data +they want to accept in one chunk and how much they output in one chunk. Its +function is to manage its buffer and call A and B at the proper times. + +A data buffer control module may itself be viewed as a processing step by a +higher-level control module; thus the control modules form a binary tree with +elementary processing steps at the leaves of the tree. + +The control modules are objects. A considerable amount of flexibility can +be had by replacing implementations of a control module. For example: +* Merging of adjacent steps in the pipeline is done by replacing a control + module and its pair of processing-step modules with a single processing- + step module. (Hence the possible merges are determined by the tree of + control modules.) +* In some processing modes, a given interstep buffer need only be a "strip" + buffer large enough to accommodate the desired data chunk sizes. In other + modes, a full-image buffer is needed and several passes are required. + The control module determines which kind of buffer is used and manipulates + virtual array buffers as needed. One or both processing steps may be + unaware of the multi-pass behavior. + +In theory, we might be able to make all of the data buffer controllers +interchangeable and provide just one set of implementations for all. In +practice, each one contains considerable special-case processing for its +particular job. The buffer controller concept should be regarded as an +overall system structuring principle, not as a complete description of the +task performed by any one controller. + + +*** Compression object structure *** + +Here is a sketch of the logical structure of the JPEG compression library: + + |-- Colorspace conversion + |-- Preprocessing controller --| + | |-- Downsampling +Main controller --| + | |-- Forward DCT, quantize + |-- Coefficient controller --| + |-- Entropy encoding + +This sketch also describes the flow of control (subroutine calls) during +typical image data processing. Each of the components shown in the diagram is +an "object" which may have several different implementations available. One +or more source code files contain the actual implementation(s) of each object. + +The objects shown above are: + +* Main controller: buffer controller for the subsampled-data buffer, which + holds the preprocessed input data. This controller invokes preprocessing to + fill the subsampled-data buffer, and JPEG compression to empty it. There is + usually no need for a full-image buffer here; a strip buffer is adequate. + +* Preprocessing controller: buffer controller for the downsampling input data + buffer, which lies between colorspace conversion and downsampling. Note + that a unified conversion/downsampling module would probably replace this + controller entirely. + +* Colorspace conversion: converts application image data into the desired + JPEG color space; also changes the data from pixel-interleaved layout to + separate component planes. Processes one pixel row at a time. + +* Downsampling: performs reduction of chroma components as required. + Optionally may perform pixel-level smoothing as well. Processes a "row + group" at a time, where a row group is defined as Vmax pixel rows of each + component before downsampling, and Vk sample rows afterwards (remember Vk + differs across components). Some downsampling or smoothing algorithms may + require context rows above and below the current row group; the + preprocessing controller is responsible for supplying these rows via proper + buffering. The downsampler is responsible for edge expansion at the right + edge (i.e., extending each sample row to a multiple of 8 samples); but the + preprocessing controller is responsible for vertical edge expansion (i.e., + duplicating the bottom sample row as needed to make a multiple of 8 rows). + +* Coefficient controller: buffer controller for the DCT-coefficient data. + This controller handles MCU assembly, including insertion of dummy DCT + blocks when needed at the right or bottom edge. When performing + Huffman-code optimization or emitting a multiscan JPEG file, this + controller is responsible for buffering the full image. The equivalent of + one fully interleaved MCU row of subsampled data is processed per call, + even when the JPEG file is noninterleaved. + +* Forward DCT and quantization: Perform DCT, quantize, and emit coefficients. + Works on one or more DCT blocks at a time. (Note: the coefficients are now + emitted in normal array order, which the entropy encoder is expected to + convert to zigzag order as necessary. Prior versions of the IJG code did + the conversion to zigzag order within the quantization step.) + +* Entropy encoding: Perform Huffman or arithmetic entropy coding and emit the + coded data to the data destination module. Works on one MCU per call. + For progressive JPEG, the same DCT blocks are fed to the entropy coder + during each pass, and the coder must emit the appropriate subset of + coefficients. + +In addition to the above objects, the compression library includes these +objects: + +* Master control: determines the number of passes required, controls overall + and per-pass initialization of the other modules. + +* Marker writing: generates JPEG markers (except for RSTn, which is emitted + by the entropy encoder when needed). + +* Data destination manager: writes the output JPEG datastream to its final + destination (e.g., a file). The destination manager supplied with the + library knows how to write to a stdio stream; for other behaviors, the + surrounding application may provide its own destination manager. + +* Memory manager: allocates and releases memory, controls virtual arrays + (with backing store management, where required). + +* Error handler: performs formatting and output of error and trace messages; + determines handling of nonfatal errors. The surrounding application may + override some or all of this object's methods to change error handling. + +* Progress monitor: supports output of "percent-done" progress reports. + This object represents an optional callback to the surrounding application: + if wanted, it must be supplied by the application. + +The error handler, destination manager, and progress monitor objects are +defined as separate objects in order to simplify application-specific +customization of the JPEG library. A surrounding application may override +individual methods or supply its own all-new implementation of one of these +objects. The object interfaces for these objects are therefore treated as +part of the application interface of the library, whereas the other objects +are internal to the library. + +The error handler and memory manager are shared by JPEG compression and +decompression; the progress monitor, if used, may be shared as well. + + +*** Decompression object structure *** + +Here is a sketch of the logical structure of the JPEG decompression library: + + |-- Entropy decoding + |-- Coefficient controller --| + | |-- Dequantize, Inverse DCT +Main controller --| + | |-- Upsampling + |-- Postprocessing controller --| |-- Colorspace conversion + |-- Color quantization + |-- Color precision reduction + +As before, this diagram also represents typical control flow. The objects +shown are: + +* Main controller: buffer controller for the subsampled-data buffer, which + holds the output of JPEG decompression proper. This controller's primary + task is to feed the postprocessing procedure. Some upsampling algorithms + may require context rows above and below the current row group; when this + is true, the main controller is responsible for managing its buffer so as + to make context rows available. In the current design, the main buffer is + always a strip buffer; a full-image buffer is never required. + +* Coefficient controller: buffer controller for the DCT-coefficient data. + This controller handles MCU disassembly, including deletion of any dummy + DCT blocks at the right or bottom edge. When reading a multiscan JPEG + file, this controller is responsible for buffering the full image. + (Buffering DCT coefficients, rather than samples, is necessary to support + progressive JPEG.) The equivalent of one fully interleaved MCU row of + subsampled data is processed per call, even when the source JPEG file is + noninterleaved. + +* Entropy decoding: Read coded data from the data source module and perform + Huffman or arithmetic entropy decoding. Works on one MCU per call. + For progressive JPEG decoding, the coefficient controller supplies the prior + coefficients of each MCU (initially all zeroes), which the entropy decoder + modifies in each scan. + +* Dequantization and inverse DCT: like it says. Note that the coefficients + buffered by the coefficient controller have NOT been dequantized; we + merge dequantization and inverse DCT into a single step for speed reasons. + When scaled-down output is asked for, simplified DCT algorithms may be used + that emit only 1x1, 2x2, or 4x4 samples per DCT block, not the full 8x8. + Works on one DCT block at a time. + +* Postprocessing controller: buffer controller for the color quantization + input buffer, when quantization is in use. (Without quantization, this + controller just calls the upsampler.) For two-pass quantization, this + controller is responsible for buffering the full-image data. + +* Upsampling: restores chroma components to full size. (May support more + general output rescaling, too. Note that if undersized DCT outputs have + been emitted by the DCT module, this module must adjust so that properly + sized outputs are created.) Works on one row group at a time. This module + also calls the color conversion module, so its top level is effectively a + buffer controller for the upsampling->color conversion buffer. However, in + all but the highest-quality operating modes, upsampling and color + conversion are likely to be merged into a single step. + +* Colorspace conversion: convert from JPEG color space to output color space, + and change data layout from separate component planes to pixel-interleaved. + Works on one pixel row at a time. + +* Color quantization: reduce the data to colormapped form, using either an + externally specified colormap or an internally generated one. This module + is not used for full-color output. Works on one pixel row at a time; may + require two passes to generate a color map. Note that the output will + always be a single component representing colormap indexes. In the current + design, the output values are JSAMPLEs, so an 8-bit compilation cannot + quantize to more than 256 colors. This is unlikely to be a problem in + practice. + +* Color reduction: this module handles color precision reduction, e.g., + generating 15-bit color (5 bits/primary) from JPEG's 24-bit output. + Not quite clear yet how this should be handled... should we merge it with + colorspace conversion??? + +Note that some high-speed operating modes might condense the entire +postprocessing sequence to a single module (upsample, color convert, and +quantize in one step). + +In addition to the above objects, the decompression library includes these +objects: + +* Master control: determines the number of passes required, controls overall + and per-pass initialization of the other modules. This is subdivided into + input and output control: jdinput.c controls only input-side processing, + while jdmaster.c handles overall initialization and output-side control. + +* Marker reading: decodes JPEG markers (except for RSTn). + +* Data source manager: supplies the input JPEG datastream. The source + manager supplied with the library knows how to read from a stdio stream; + for other behaviors, the surrounding application may provide its own source + manager. + +* Memory manager: same as for compression library. + +* Error handler: same as for compression library. + +* Progress monitor: same as for compression library. + +As with compression, the data source manager, error handler, and progress +monitor are candidates for replacement by a surrounding application. + + +*** Decompression input and output separation *** + +To support efficient incremental display of progressive JPEG files, the +decompressor is divided into two sections that can run independently: + +1. Data input includes marker parsing, entropy decoding, and input into the + coefficient controller's DCT coefficient buffer. Note that this + processing is relatively cheap and fast. + +2. Data output reads from the DCT coefficient buffer and performs the IDCT + and all postprocessing steps. + +For a progressive JPEG file, the data input processing is allowed to get +arbitrarily far ahead of the data output processing. (This occurs only +if the application calls jpeg_consume_input(); otherwise input and output +run in lockstep, since the input section is called only when the output +section needs more data.) In this way the application can avoid making +extra display passes when data is arriving faster than the display pass +can run. Furthermore, it is possible to abort an output pass without +losing anything, since the coefficient buffer is read-only as far as the +output section is concerned. See libjpeg.doc for more detail. + +A full-image coefficient array is only created if the JPEG file has multiple +scans (or if the application specifies buffered-image mode anyway). When +reading a single-scan file, the coefficient controller normally creates only +a one-MCU buffer, so input and output processing must run in lockstep in this +case. jpeg_consume_input() is effectively a no-op in this situation. + +The main impact of dividing the decompressor in this fashion is that we must +be very careful with shared variables in the cinfo data structure. Each +variable that can change during the course of decompression must be +classified as belonging to data input or data output, and each section must +look only at its own variables. For example, the data output section may not +depend on any of the variables that describe the current scan in the JPEG +file, because these may change as the data input section advances into a new +scan. + +The progress monitor is (somewhat arbitrarily) defined to treat input of the +file as one pass when buffered-image mode is not used, and to ignore data +input work completely when buffered-image mode is used. Note that the +library has no reliable way to predict the number of passes when dealing +with a progressive JPEG file, nor can it predict the number of output passes +in buffered-image mode. So the work estimate is inherently bogus anyway. + +No comparable division is currently made in the compression library, because +there isn't any real need for it. + + +*** Data formats *** + +Arrays of pixel sample values use the following data structure: + + typedef something JSAMPLE; a pixel component value, 0..MAXJSAMPLE + typedef JSAMPLE *JSAMPROW; ptr to a row of samples + typedef JSAMPROW *JSAMPARRAY; ptr to a list of rows + typedef JSAMPARRAY *JSAMPIMAGE; ptr to a list of color-component arrays + +The basic element type JSAMPLE will typically be one of unsigned char, +(signed) char, or short. Short will be used if samples wider than 8 bits are +to be supported (this is a compile-time option). Otherwise, unsigned char is +used if possible. If the compiler only supports signed chars, then it is +necessary to mask off the value when reading. Thus, all reads of JSAMPLE +values must be coded as "GETJSAMPLE(value)", where the macro will be defined +as "((value) & 0xFF)" on signed-char machines and "((int) (value))" elsewhere. + +With these conventions, JSAMPLE values can be assumed to be >= 0. This helps +simplify correct rounding during downsampling, etc. The JPEG standard's +specification that sample values run from -128..127 is accommodated by +subtracting 128 just as the sample value is copied into the source array for +the DCT step (this will be an array of signed ints). Similarly, during +decompression the output of the IDCT step will be immediately shifted back to +0..255. (NB: different values are required when 12-bit samples are in use. +The code is written in terms of MAXJSAMPLE and CENTERJSAMPLE, which will be +defined as 255 and 128 respectively in an 8-bit implementation, and as 4095 +and 2048 in a 12-bit implementation.) + +We use a pointer per row, rather than a two-dimensional JSAMPLE array. This +choice costs only a small amount of memory and has several benefits: +* Code using the data structure doesn't need to know the allocated width of + the rows. This simplifies edge expansion/compression, since we can work + in an array that's wider than the logical picture width. +* Indexing doesn't require multiplication; this is a performance win on many + machines. +* Arrays with more than 64K total elements can be supported even on machines + where malloc() cannot allocate chunks larger than 64K. +* The rows forming a component array may be allocated at different times + without extra copying. This trick allows some speedups in smoothing steps + that need access to the previous and next rows. + +Note that each color component is stored in a separate array; we don't use the +traditional layout in which the components of a pixel are stored together. +This simplifies coding of modules that work on each component independently, +because they don't need to know how many components there are. Furthermore, +we can read or write each component to a temporary file independently, which +is helpful when dealing with noninterleaved JPEG files. + +In general, a specific sample value is accessed by code such as + GETJSAMPLE(image[colorcomponent][row][col]) +where col is measured from the image left edge, but row is measured from the +first sample row currently in memory. Either of the first two indexings can +be precomputed by copying the relevant pointer. + + +Since most image-processing applications prefer to work on images in which +the components of a pixel are stored together, the data passed to or from the +surrounding application uses the traditional convention: a single pixel is +represented by N consecutive JSAMPLE values, and an image row is an array of +(# of color components)*(image width) JSAMPLEs. One or more rows of data can +be represented by a pointer of type JSAMPARRAY in this scheme. This scheme is +converted to component-wise storage inside the JPEG library. (Applications +that want to skip JPEG preprocessing or postprocessing will have to contend +with component-wise storage.) + + +Arrays of DCT-coefficient values use the following data structure: + + typedef short JCOEF; a 16-bit signed integer + typedef JCOEF JBLOCK[DCTSIZE2]; an 8x8 block of coefficients + typedef JBLOCK *JBLOCKROW; ptr to one horizontal row of 8x8 blocks + typedef JBLOCKROW *JBLOCKARRAY; ptr to a list of such rows + typedef JBLOCKARRAY *JBLOCKIMAGE; ptr to a list of color component arrays + +The underlying type is at least a 16-bit signed integer; while "short" is big +enough on all machines of interest, on some machines it is preferable to use +"int" for speed reasons, despite the storage cost. Coefficients are grouped +into 8x8 blocks (but we always use #defines DCTSIZE and DCTSIZE2 rather than +"8" and "64"). + +The contents of a coefficient block may be in either "natural" or zigzagged +order, and may be true values or divided by the quantization coefficients, +depending on where the block is in the processing pipeline. In the current +library, coefficient blocks are kept in natural order everywhere; the entropy +codecs zigzag or dezigzag the data as it is written or read. The blocks +contain quantized coefficients everywhere outside the DCT/IDCT subsystems. +(This latter decision may need to be revisited to support variable +quantization a la JPEG Part 3.) + +Notice that the allocation unit is now a row of 8x8 blocks, corresponding to +eight rows of samples. Otherwise the structure is much the same as for +samples, and for the same reasons. + +On machines where malloc() can't handle a request bigger than 64Kb, this data +structure limits us to rows of less than 512 JBLOCKs, or a picture width of +4000+ pixels. This seems an acceptable restriction. + + +On 80x86 machines, the bottom-level pointer types (JSAMPROW and JBLOCKROW) +must be declared as "far" pointers, but the upper levels can be "near" +(implying that the pointer lists are allocated in the DS segment). +We use a #define symbol FAR, which expands to the "far" keyword when +compiling on 80x86 machines and to nothing elsewhere. + + +*** Suspendable processing *** + +In some applications it is desirable to use the JPEG library as an +incremental, memory-to-memory filter. In this situation the data source or +destination may be a limited-size buffer, and we can't rely on being able to +empty or refill the buffer at arbitrary times. Instead the application would +like to have control return from the library at buffer overflow/underrun, and +then resume compression or decompression at a later time. + +This scenario is supported for simple cases. (For anything more complex, we +recommend that the application "bite the bullet" and develop real multitasking +capability.) The libjpeg.doc file goes into more detail about the usage and +limitations of this capability; here we address the implications for library +structure. + +The essence of the problem is that the entropy codec (coder or decoder) must +be prepared to stop at arbitrary times. In turn, the controllers that call +the entropy codec must be able to stop before having produced or consumed all +the data that they normally would handle in one call. That part is reasonably +straightforward: we make the controller call interfaces include "progress +counters" which indicate the number of data chunks successfully processed, and +we require callers to test the counter rather than just assume all of the data +was processed. + +Rather than trying to restart at an arbitrary point, the current Huffman +codecs are designed to restart at the beginning of the current MCU after a +suspension due to buffer overflow/underrun. At the start of each call, the +codec's internal state is loaded from permanent storage (in the JPEG object +structures) into local variables. On successful completion of the MCU, the +permanent state is updated. (This copying is not very expensive, and may even +lead to *improved* performance if the local variables can be registerized.) +If a suspension occurs, the codec simply returns without updating the state, +thus effectively reverting to the start of the MCU. Note that this implies +leaving some data unprocessed in the source/destination buffer (ie, the +compressed partial MCU). The data source/destination module interfaces are +specified so as to make this possible. This also implies that the data buffer +must be large enough to hold a worst-case compressed MCU; a couple thousand +bytes should be enough. + +In a successive-approximation AC refinement scan, the progressive Huffman +decoder has to be able to undo assignments of newly nonzero coefficients if it +suspends before the MCU is complete, since decoding requires distinguishing +previously-zero and previously-nonzero coefficients. This is a bit tedious +but probably won't have much effect on performance. Other variants of Huffman +decoding need not worry about this, since they will just store the same values +again if forced to repeat the MCU. + +This approach would probably not work for an arithmetic codec, since its +modifiable state is quite large and couldn't be copied cheaply. Instead it +would have to suspend and resume exactly at the point of the buffer end. + +The JPEG marker reader is designed to cope with suspension at an arbitrary +point. It does so by backing up to the start of the marker parameter segment, +so the data buffer must be big enough to hold the largest marker of interest. +Again, a couple KB should be adequate. (A special "skip" convention is used +to bypass COM and APPn markers, so these can be larger than the buffer size +without causing problems; otherwise a 64K buffer would be needed in the worst +case.) + +The JPEG marker writer currently does *not* cope with suspension. I feel that +this is not necessary; it is much easier simply to require the application to +ensure there is enough buffer space before starting. (An empty 2K buffer is +more than sufficient for the header markers; and ensuring there are a dozen or +two bytes available before calling jpeg_finish_compress() will suffice for the +trailer.) This would not work for writing multi-scan JPEG files, but +we simply do not intend to support that capability with suspension. + + +*** Memory manager services *** + +The JPEG library's memory manager controls allocation and deallocation of +memory, and it manages large "virtual" data arrays on machines where the +operating system does not provide virtual memory. Note that the same +memory manager serves both compression and decompression operations. + +In all cases, allocated objects are tied to a particular compression or +decompression master record, and they will be released when that master +record is destroyed. + +The memory manager does not provide explicit deallocation of objects. +Instead, objects are created in "pools" of free storage, and a whole pool +can be freed at once. This approach helps prevent storage-leak bugs, and +it speeds up operations whenever malloc/free are slow (as they often are). +The pools can be regarded as lifetime identifiers for objects. Two +pools/lifetimes are defined: + * JPOOL_PERMANENT lasts until master record is destroyed + * JPOOL_IMAGE lasts until done with image (JPEG datastream) +Permanent lifetime is used for parameters and tables that should be carried +across from one datastream to another; this includes all application-visible +parameters. Image lifetime is used for everything else. (A third lifetime, +JPOOL_PASS = one processing pass, was originally planned. However it was +dropped as not being worthwhile. The actual usage patterns are such that the +peak memory usage would be about the same anyway; and having per-pass storage +substantially complicates the virtual memory allocation rules --- see below.) + +The memory manager deals with three kinds of object: +1. "Small" objects. Typically these require no more than 10K-20K total. +2. "Large" objects. These may require tens to hundreds of K depending on + image size. Semantically they behave the same as small objects, but we + distinguish them for two reasons: + * On MS-DOS machines, large objects are referenced by FAR pointers, + small objects by NEAR pointers. + * Pool allocation heuristics may differ for large and small objects. + Note that individual "large" objects cannot exceed the size allowed by + type size_t, which may be 64K or less on some machines. +3. "Virtual" objects. These are large 2-D arrays of JSAMPLEs or JBLOCKs + (typically large enough for the entire image being processed). The + memory manager provides stripwise access to these arrays. On machines + without virtual memory, the rest of the array may be swapped out to a + temporary file. + +(Note: JSAMPARRAY and JBLOCKARRAY data structures are a combination of large +objects for the data proper and small objects for the row pointers. For +convenience and speed, the memory manager provides single routines to create +these structures. Similarly, virtual arrays include a small control block +and a JSAMPARRAY or JBLOCKARRAY working buffer, all created with one call.) + +In the present implementation, virtual arrays are only permitted to have image +lifespan. (Permanent lifespan would not be reasonable, and pass lifespan is +not very useful since a virtual array's raison d'etre is to store data for +multiple passes through the image.) We also expect that only "small" objects +will be given permanent lifespan, though this restriction is not required by +the memory manager. + +In a non-virtual-memory machine, some performance benefit can be gained by +making the in-memory buffers for virtual arrays be as large as possible. +(For small images, the buffers might fit entirely in memory, so blind +swapping would be very wasteful.) The memory manager will adjust the height +of the buffers to fit within a prespecified maximum memory usage. In order +to do this in a reasonably optimal fashion, the manager needs to allocate all +of the virtual arrays at once. Therefore, there isn't a one-step allocation +routine for virtual arrays; instead, there is a "request" routine that simply +allocates the control block, and a "realize" routine (called just once) that +determines space allocation and creates all of the actual buffers. The +realize routine must allow for space occupied by non-virtual large objects. +(We don't bother to factor in the space needed for small objects, on the +grounds that it isn't worth the trouble.) + +To support all this, we establish the following protocol for doing business +with the memory manager: + 1. Modules must request virtual arrays (which may have only image lifespan) + during the initial setup phase, i.e., in their jinit_xxx routines. + 2. All "large" objects (including JSAMPARRAYs and JBLOCKARRAYs) must also be + allocated during initial setup. + 3. realize_virt_arrays will be called at the completion of initial setup. + The above conventions ensure that sufficient information is available + for it to choose a good size for virtual array buffers. +Small objects of any lifespan may be allocated at any time. We expect that +the total space used for small objects will be small enough to be negligible +in the realize_virt_arrays computation. + +In a virtual-memory machine, we simply pretend that the available space is +infinite, thus causing realize_virt_arrays to decide that it can allocate all +the virtual arrays as full-size in-memory buffers. The overhead of the +virtual-array access protocol is very small when no swapping occurs. + +A virtual array can be specified to be "pre-zeroed"; when this flag is set, +never-yet-written sections of the array are set to zero before being made +available to the caller. If this flag is not set, never-written sections +of the array contain garbage. (This feature exists primarily because the +equivalent logic would otherwise be needed in jdcoefct.c for progressive +JPEG mode; we may as well make it available for possible other uses.) + +The first write pass on a virtual array is required to occur in top-to-bottom +order; read passes, as well as any write passes after the first one, may +access the array in any order. This restriction exists partly to simplify +the virtual array control logic, and partly because some file systems may not +support seeking beyond the current end-of-file in a temporary file. The main +implication of this restriction is that rearrangement of rows (such as +converting top-to-bottom data order to bottom-to-top) must be handled while +reading data out of the virtual array, not while putting it in. + + +*** Memory manager internal structure *** + +To isolate system dependencies as much as possible, we have broken the +memory manager into two parts. There is a reasonably system-independent +"front end" (jmemmgr.c) and a "back end" that contains only the code +likely to change across systems. All of the memory management methods +outlined above are implemented by the front end. The back end provides +the following routines for use by the front end (none of these routines +are known to the rest of the JPEG code): + +jpeg_mem_init, jpeg_mem_term system-dependent initialization/shutdown + +jpeg_get_small, jpeg_free_small interface to malloc and free library routines + (or their equivalents) + +jpeg_get_large, jpeg_free_large interface to FAR malloc/free in MSDOS machines; + else usually the same as + jpeg_get_small/jpeg_free_small + +jpeg_mem_available estimate available memory + +jpeg_open_backing_store create a backing-store object + +read_backing_store, manipulate a backing-store object +write_backing_store, +close_backing_store + +On some systems there will be more than one type of backing-store object +(specifically, in MS-DOS a backing store file might be an area of extended +memory as well as a disk file). jpeg_open_backing_store is responsible for +choosing how to implement a given object. The read/write/close routines +are method pointers in the structure that describes a given object; this +lets them be different for different object types. + +It may be necessary to ensure that backing store objects are explicitly +released upon abnormal program termination. For example, MS-DOS won't free +extended memory by itself. To support this, we will expect the main program +or surrounding application to arrange to call self_destruct (typically via +jpeg_destroy) upon abnormal termination. This may require a SIGINT signal +handler or equivalent. We don't want to have the back end module install its +own signal handler, because that would pre-empt the surrounding application's +ability to control signal handling. + +The IJG distribution includes several memory manager back end implementations. +Usually the same back end should be suitable for all applications on a given +system, but it is possible for an application to supply its own back end at +need. + + +*** Implications of DNL marker *** + +Some JPEG files may use a DNL marker to postpone definition of the image +height (this would be useful for a fax-like scanner's output, for instance). +In these files the SOF marker claims the image height is 0, and you only +find out the true image height at the end of the first scan. + +We could read these files as follows: +1. Upon seeing zero image height, replace it by 65535 (the maximum allowed). +2. When the DNL is found, update the image height in the global image + descriptor. +This implies that control modules must avoid making copies of the image +height, and must re-test for termination after each MCU row. This would +be easy enough to do. + +In cases where image-size data structures are allocated, this approach will +result in very inefficient use of virtual memory or much-larger-than-necessary +temporary files. This seems acceptable for something that probably won't be a +mainstream usage. People might have to forgo use of memory-hogging options +(such as two-pass color quantization or noninterleaved JPEG files) if they +want efficient conversion of such files. (One could improve efficiency by +demanding a user-supplied upper bound for the height, less than 65536; in most +cases it could be much less.) + +The standard also permits the SOF marker to overestimate the image height, +with a DNL to give the true, smaller height at the end of the first scan. +This would solve the space problems if the overestimate wasn't too great. +However, it implies that you don't even know whether DNL will be used. + +This leads to a couple of very serious objections: +1. Testing for a DNL marker must occur in the inner loop of the decompressor's + Huffman decoder; this implies a speed penalty whether the feature is used + or not. +2. There is no way to hide the last-minute change in image height from an + application using the decoder. Thus *every* application using the IJG + library would suffer a complexity penalty whether it cared about DNL or + not. +We currently do not support DNL because of these problems. + +A different approach is to insist that DNL-using files be preprocessed by a +separate program that reads ahead to the DNL, then goes back and fixes the SOF +marker. This is a much simpler solution and is probably far more efficient. +Even if one wants piped input, buffering the first scan of the JPEG file needs +a lot smaller temp file than is implied by the maximum-height method. For +this approach we'd simply treat DNL as a no-op in the decompressor (at most, +check that it matches the SOF image height). + +We will not worry about making the compressor capable of outputting DNL. +Something similar to the first scheme above could be applied if anyone ever +wants to make that work. diff --git a/Engine/lib/ljpeg/extras/testimg.ppm b/Engine/lib/ljpeg/extras/testimg.ppm new file mode 100644 index 000000000..72787a91f --- /dev/null +++ b/Engine/lib/ljpeg/extras/testimg.ppm @@ -0,0 +1,4 @@ +P6 +227 149 +255 +0/-0/-10.21/51.51.62/62/83/83/:3-:3-:3-:3-:3-:3-:2/91.91.80-80-91.91.:2/80-80-80-80-80-80-80-80-6.+6.+6.+5-*5-*4,)4,)4,)4,)4,)4,)4,)4,)4,)4,)2-)/*$/,%/,%0-&1.'2/(30)30)63,63,74-85.85.96/:70:7.A:0B<0D>2F@4IA4JB5KC6KC6MD5MD5OC3NB2OC3OC3PD4RE5R?1Y?2b@4nB5}E6‹H8™G9ŁF7ŻG:¸G9ľE:ĹG;ÇG>ĘG?ËH@ĐE@çFLíCLëDKëEIîCIďBDń>Bô=Ař;A÷:@ô:?đ×?<ËA7»=/µ@.µ@.´?-´?-ł@-˛?-Ż@-­@,ŞA,¦A-˘B,ź@*›A)@*–A,”>-’?/’?/‘>.‘>,=+’<+’<+”?+”?+”=*”=*”=*•>+–?,–@/–?6•>5—=2ź?1©B3łD3ĽD4żD4ą?0µA2¬F8žH;‡H9oA2T8*C3&=5295495473271160050-50-72/72/72/61.61-50,50,41,//-.0-//-//-0/-0/-2.-2.-5,-4+,4*+3)*7(+=.1E69P:0U?1^A3jC4xD6„E4’E5śC3§C4ŻA4µA4ĽB7ŔD:ÄE<ĹF=ÍC@áEIçBIčCIęDHíDGďBDó@Cö?Cř;A÷:@ô:?đŐ@<Ę@6ą>/µ@.´?-´?-´?-˛?,°?-Ż@-­@,©@+¦A-ˇA+ź@*›A)@*–A,”>-’?/‘>.‘>.‘>,=+’<+’<+”?+“>*”=*”=*”=*•>+–?,–@/”@5•>5>3 >1«A3µD4˝C4żD5»A2·C6¬F8śI;…G:l@3S9*B4)>63:6595484382271161.61.72/72/72/61.61-50,50,41,//-.0-//-//-0/-0/-2.-2.-3--5,-4*+3)*5)+<-0C47N8:d>=vEA†JIŹNLšTV¤ajĄl}ťrŽ‘{˘†€®…ą{„»ou©[[ŤQHuOCiOFeOG_PH_RN_[Yfnot‡”™•™ž—š ”™ťŹŹ‘~ojkY][LVSJXSZVRaXQa/.,/.,0/-10.40-40-51.51.72.72.72.72.92,92,92,92,91.80.7/-7/-7/-7/-80.91/80.80.80.80.80.80.80.80.6.,5-+5-+5-+4,*4,*4,*4,*5-+5-+5-+5-+5-+5-+5-+3.*2-'1.'2/(30)30)41*41*52+63,63,63,74-85.96/96/:7.?8.@:.B<0D>2G?4H@5H@3H@3I@1I@1K?1K?1K?/L@0MA1NB2MA1QA1YB2dC2qC3|C2‡B2’A0<- :+§;.Ż=2µ@6şD:żF=ĹD>ŮCEá@FăBGçBFęDFđCEôADř?Dú;@ů:?ő;@đ=@č@@ÜA=Ń@;Ć@5·=.ł@-ł@-˛?,˛?-°?-Ż>,­@,Ş?-§@-Ą@,ˇA+ťA,š?*@*•@+”>-‘>.‘>.‘>.=+=+=+=+‘>,‘>,’<+’<+“=,“=,”?+•?.•A6–?5š>3Ł>2ŻA4ąC5żD5ÁC5ŔD8¸F;®I=™J=G;h@4Q:,B5,?74=77<66:4494183072/72/62/62/62/51.52-41,41,21,.0-,1-.0-.0-//-//-0/-2.-5//4..5,-4*+4*+9-/>24I56[97l?9|E@†IDOM[`›fv”mŚwž}­}‚ąu~·fm¤TV‰MEvLAkMAeOFcQHcMH^NK\[[eqty…‰‡Ś†ŠŹŤ…†Š|xzlfiXZ[MVSLZU[ZT`[S`.-+/.,/.,0/-3/,40-40-40-61-61-61-61-81+81+81+81+7/-7/-6.,6.,6.,6.,7/-7/-80.80.80.80.80.80.80.80.5-+5-+5-+4,*4,*4,*3+)3+)6.,6.,6.,6.,6.,6.,6.,4/,30+30)30)41*41*52+52+52+52+52+63,74-85.85.96/96->7-?9-@:.B<0E=2E=2F>1F>1G=1G>/H<.I=/I=/J>.L@0JA0KD2NE4UD4^D3iD2sB1~A/†?-Ś9)”9'ť9*Ł<-¬@3łE8¸H<ÁF>ŇDCÚACŢBCâDCçCDěBCó@C÷?Aú;@ů:?ő;@î>@ĺA@ÚB=Í@9Â@3µ=-°@,°@,°@,Ż>,®?,®?,¬?+©@-¦?,Ł@+ @*ś@+@*–@)”?*‘>,‘>.‘>.=-=+=+Ź<*Ź<*=+=+Ź<*Ź<*’<+‘>,”>-’?-•A6–?5ś>2¦@4˛B6ĽC8ÁC7ÂB7ÂF<şJ?¬L@—K>|F:b@4L:.A7-@85>96=77<74:5294183083062/62/62/32.52-21,21,12--2.-2./1./1.00.00.10.10.5106005//5,-4+,6,-:01D22T71c;3qA7{E;‚HD‰RUŤ_l‹i‚s}y«x}µowµ`f˘QR‹LEyL@pL@hPEgQFfLC^GBVMLZ^^fjnquyxx}wz€vwzokoa`bTWYLTTL]WY]V]]V^------.-+/.,0/-10.3/,40-3/,3/,4/+4/+4/+4/+6/)6/)4/,4/,3.+3.+3.+3.+4/,4/,50-50-50-50-50-50-50-50-3.+3.+2-*2-*2-*1,)1,)1,)4/,4/,4/,4/,4/,4/,4/,4/,41,41,41,41,41,52-52-52-52-52-63.63.74/85096196/<5-=6,?8.@9/B:/C;0C;0C;.D:.D:.F:.G;-H<.I=/J>0I@1JG6MH5RG5YF5bE3jD1uB/}>,‚;)‹:)“:*š=,ŁB2¬F8˛J=»I?ĚGBÔDCŘDBÝEBâBBéAAđ=@ô<>ů:?ř:<ô<>í?>áB>ÓC:ĹA5ą?0˛?-®?,®?,®?,­>-¬>-¬>-Ş?-¨>.¤?- ?,ž?+š?,—?+•>*“>*‘>,?.Ź>-Ź>-Ź>-Ž=,Ž=,Ž=,Ž=,Ž=,Ž=,Ž=,Ź<,Ź>-‘>.?.”B4—A4ť@1¨@3¶A7żC9ĹB8ÄA7ľC;·H?¦LCJ@tE;Z>2E9-<5+@93@85?75>63=52<4194083/62/43/43/23.32.12-12-02--2.,2.-2.-2./1./1.00.10.3205105104..2,,4+,7./=/.N5.Y9.e=1n@3sB;yKK€Zeg€p—zxŞu{·ks´_d¦TT”OGLBwNAmNBhMAeJA`GBYGFXKKWMPU]cc`fbbia`f\Z`TW[MUXMXXP^YV`WX`WZ,,,,,,.-+.-+/.,0/-3/,3/,2.+2.+3.*3.*3.*3.*5.(5-*3.+3.+2-*1,)1,)2-*3.+3.+3.+3.+3.+3.+3.+3.+3.+3.+2-*2-*2-*2-*1,)1,)1,)0+(3.+3.+3.+3.+3.+3.+3.+3.+41,41,41,41,41,41,41,41,41,52-52-63.74/85085085.;4,<5+=6,>7-@7.A9.A9.A9.C9/C9-E9-F:.G;/H<.J>0HA1JG6IH6NG5VF6\E3dC2n@0v>-{<+‚;)Ś;*”=,ś@1ŁF5ŞJ:´J=ÄH@ĚEAŃFAÖE@ŢCAä@>ě>?ń:<÷;<ô:;đ<=é@=ÜC=ÍC8ľ@2±>,®?,«@,Ş?+Ş?+Ş?-©>,©>,¨?,Ą>-˘?, ?,›>,—?+•>*“>)‘?*Ź>+Ź>-Ź>-Ź>-Ž=,Ž=,Ž=,Ť<+Ž=,‹<+Ť<+‹<+‹<-Ś=,Ť>/Ž?.”B4—A2 @2¬B5ąC9ÂC:ĹB:ÂA;şA9±I@ŁNGŤNEoG=S?4A;/96-@93A75?74>63<4194083/74/43/43/34/23.23.02-02-.3--3/-3/.3/.3/02/02/11/11/21/32032040/2.-1-,4..8.,G4-O4)X8+`<0e?6mGFyYd‚k…€uź||˛w|Ľnu»dhŻ[[ˇRMŹLB~OArL@hI=cH>`HB^ECX@BO;@FBGJDMJJQJJQIIQFKQEOUIVWO^YS`YS`XU++++++,,,---/.,/.,0/-0/-1-*1-*1-*1-*2-)2-)2-)2-)2-*2,,1++1++1++1++2,,2,,1++1++1++1++1++1++1++1++2,,2,,2,,1++1++1++0**0**3--3--3--3--3--3--3--3.+41,41,41,30+30+30+30+30+41,41,52-63.74/74/85085.:3+;4,<5-=6.?6/?6-?6-?7,B8.B8.E8/E9-G;/H<0J>2H@3HE6GF4KE5QD4XC2_B2f?.n=,v=,|:*…9)Ś;*“=.›B2˘F7¬F8»F<ÂF>ÉF>ĐE>ŮD@âC?ę@@đ>>ň::ń;:ě<<äA<ÖC;ĆD6µ@/Ş=)Ş?-©@-©@-¨?,¨>.¨>.§=-Ą>-Ł=. ?.ž?-š?-–?,“>*‘?*Ź>)Ź>+Ť>-Ť>-Ś=,Ś=.Ś=.‹<-‹<-‹<-Š=-Š;,‰<,Š.‹=0Ś?/’C2B1ˇA1®B6ĽC:ÂC<ÄC=ŔC=ąFA˛QJĄXRŹXQsRIWI>CC793@72>71=60:5/94.83/63.43.43.34/13.13..3-.3-.3--3/-3/-3/-3/.3/.3/02/02/00.11/22021/0/-/.,2.-4/,?0+D0)K3)T8-Z<4dFFu]jsŤ‰€«…„ľ~ÇtzĆmp˝ceŻVSšLC‚K?qI_FB]DBW?AN;?H:BE>HGDMHGQIGQHJRGNVKUXM^ZOaYNaXO++++++,,,,,,.,-/.,0/-0/-1-*1-*1-*1-*2-)2-)2-)2-*2,,1++1++0**0**1++1++2,,0**0**0**0**0**0**0**0**2,,2,,2,,1++1++0**0**0**2,,2,,2,,2,,2,,2,,2,,2,,3/,30+30+30+30+30+30+30+41,41,52-63.63.74/85085092,:3+;4,<5->5.>5.>5.>5,B8/B8.E8/E8/G:1I=1J>2I?3FC4FC4JB5OA4TA2\@2b>0j<-q<.w9*}8)…8(Ś:,•=/›B4¤B5˛F:şE;ÁF>ĘG?ÔG@ŢFAçCAîB@í;;ë;;ç>;ßB;ŃD:żD4Ż@-¤>(¦A-¦A-Ą@,Ą@.Ą@.¤?-¤?-¤>/˘>.ź@.ś?.?-•>+‘?*Ź>)Ź>+Ť>-Ť>-Ť>-Ś=,Ś=.‹<-‹<-Š=-Š=-<.<.<./>1‹?1‘D2–C1˘B4­C6şC;ÁD>ÁD>»EAąPL˛[TĄe\‘f]u_T[UIGMACI?<92?82>71;6094.74-63.43.43.34.24/13./4..3-.3-.3--3/-3/-3/-3/.3/.3/02/02///-00.22022010.0/-0/-3/,8,,;,)C0*K70S<6^IHtbn‡z”Š¶ŚŤĆ„Ďz€ĚrxĆik¶WWźID„E=nG^CAY@CV@DP>EKGQRKWUQ^WU`XS_UR^TT^SY_S^[LaZJaZJ,-/,-/,-/,.-------.-+.-+/.,/.,1-*0,)0,)0,)/+(/+(/+*/+*/+*/+*/+*/+*/+*0,+/+*/+*/+*/+*/+*/+*/+*/+*/+*/+*/+*0,+0,+1-,1-,2.-1-,1-,1-,1-,1-,1-,1-,1-,0,)1-*2.+3/,3/,3/,3/,3/,3/,3/,3/,3/,40-51.62/74/80-92,:3-;4.=4/>5.>5.>5.?4.?5,B5-C6.D7/F90G:1F<2G?4H@5J@6P?5T>3X<1^90b6+m9.t8-|8+9,Ť;/“=0?1ž>0§A3­A4µC8ľE:ĘG=ÔG>ŢE?ĺC@č@?ęBAćDAÚE>ČD8·B1ŞA.˘B,˘A.ˇ@-˘?,˘>.ˇ=-ˇ=-˘>. ?/ś<,š=,<-•>-“=,=+Ž=*Ś>*‹<+‹<+Š=-‰<,‰<,‰<,:-‡;-‰=/†3@2‰A5‹A4‘E5—D4ŁE9±I>şG@»D>»EA¸MG´ZRŻf_Łqf‘sh~rdjj^V^SIQFLLBJF=B>5<8/95,74+63,33+43.34.14-14-02-/1,,1+,1+-2.-2.-2.-2./1./1./1./1.02/02/11/11/11/11/11/40/4+0;/3A32C4/J;6]OOymy‹…ź“»”–ϕىÓtzĆjn·_b§Z[”LItHBdA>]>>X?BUAIVLU\U`bbqnn}xv†|rulyoguh_k_W_P]\Hb\Fc]G,-/,-/,-/,-/------.,-.-+/.,.-+0,)0,)/+(/+(/+(/+(.*).*).*).*)/+*/+*/+*/+*/+*/+*/+*/+*/+*/+*/+*/+*/+*/+*/+*0,+0,+0,+1-,1-,1-,1-,1-,1-,1-,1-,1-,1-,0,+0,)1-*2.+3/,3/,2.+2.+2.+2.+2.+2.+3/,40-51.62/80.91.:2/;4.=4/>50>50=4-?4.?4.A4.B5-C6.E80G:2H;3H>5H>5L=6O>6Q=4V;2Z90_7/h70p7.w7-9-‰9.<1–<1›=1˘@3§A3¬B5´D8ľE:ÉF<ÔE=ŰD=ßD@ŕE@ŢFAÔG>ÄF8˛C2ĄB-žB-źC.žB-ź@.ž?-ž?-ž?-ž>.ž>.š=,™>,–=-”=,=+Ź>+Ś>*Ś=,‹<+Š=+‰<,‰<,‰<,‡;+‡;-…<-†2†@6‡A7B6“G9—E7ŁG<ŻJ@¸IB¸GA·LFłTN±d\Şqfź~oo|mmseZfZNXMLNAKI=EC7@=4=:188.44,11)23-23-03,/2+/1,.0+.0+.0+/1.-2./1./1./1./1./1./1.02/02/11/11/11/11/11/2015+49-7<23?53H?:^VTxr|Šž““ą”—Ę–Ô„‹ĎyÂqy¸kt­hnž\_€XZqSUjRWjT^hZgmfvvr‚t~’‡•‰~Žv†yr€qfteZeT[ZE`Z@b\D,-/,-/,-/,-/,-/,.-------.-+.-+.-+-,*/+(.*'.*'.*',+),*+,*+,*++)*+)*+)*+)*-+,-+,-+,-+,-+,-+,-+,-+,,*+,*+-+,-+,-+,.,-.,-.,-.,-.,-.,-/-./-./-./-./.,0,+0,+1-,2.-2.-2.-2.-1-,1-,1-,1-,1-,2.-3/.40/51.80.91.:2/;30=31=4/=4/=4/?40?4.A4.A4.C60D71F93G:4H;5J;6K<7N=6P;6S:5V72[6.c60k6.t5,}7/‡9/Ť;0”<0–<1ť?3 @4˘@3¨@3±C6şD8ĹE:ÍD:ŐF@×H@ÔIBĚI?ľE:®C3ˇB.śA.ťB/śA.ś?.›>-›>-›>-›>/›?0<-–=-”<.“=.Ź>-Ś=,‹<+Š=+‰<*‰<*‰<,‡;+‡;-…<-„:-;-„<0‚<0‚<2‚>3?4…A8‡C:D9”J=—H; H>¬KD˛KFłLG˛SM®`V­sg¦qťŚz‘Ž{‚‰ws€ocqbXcUNRDMN@HI;DD8@@49;.46+/1&01)01)/0*/0*./*./*//-//-//-.0-//-//-//-//-//-//-00.00.00.00.00.00.00.1/26+97+98/4;63HE>_^Yzz|‹Ťš”±’—żŹ•Ç„ŽĂ}‰»{‰¶|‹˛}Ś«}šwŹq~‡n}‚n~~o‚~yŽ…™Ť‹ˇ”‘¨Ą”ťŠ–|Ť{j{i\hTXX@]Y<_[@-.0-.0-.0-.0-.0-.0.......-+.-+-,*-,*.*'.*'.*'.*),*++)*+)*+)*+)**()*()*(),*+,*+,*+,*+,*+,*+,*+,*+,*+,*+,*+,*+,*+-+,-+,-+,-+,.,-.,-.,-.,-/-./-./-./+*0,+1-,1-,1-,1-,0,+0,+0,+0,+0,+0,+1-,2.-3/.40/91/:20;31<41=31=31=31=4/>3/>3/@2/@3-A4.C60D71E82F93H94I:5J;6L:6N94Q83T50^72e60o6/x8/‚90Š:/<1”>1™?4›?4›?2ź?1ĄA2®B5¸D7żE:ÇG>ËH@ĘJAĂI>¶F:ŞB5žA0™@.™A-™A-?-—>,—>,™>,™=.—>.•<,”=,“=.=-Ž=,Š=+Š=-‰<,;);)‡;+‡;+„;,„;,;-€;,;/€<1€<1>5@7C:‡D§JC®LI®QL­]VŞj^§€oˇŹyšš‚Ź›†•€z‹xm{jbm]SZJQUFKO@EI:@D6;=057,13(01)/0(./).-(.-).-)/.,0/-/.,/.,/.,/.,/.,..,/.,..,0/-//-0/-//-//-//-//-2-17,:6*83-1961HJ?bfX{€z‹“Ť“ŁŹ–°Ś•¶˛Ż…™˛Ť¤¶’¨ł“¨«¤ŁŚ ž‡ž‚™Ź}‰ś‹Ł”°š±›”­—‹ŁŤ…›„’|k|iXfOSV;ZV9^Z=+/2+/2+/2+/2-.0-.0......------.-+-,*-,*,+),+),+),*+,*+,*++)*+)**()*()*(),*+,*+,*+,*+,*+,*+,*+,*+,*+,*+,*+,*+,*++)*+)*+)*,*+-+,-+,.,-.,-/-./-./-./+*0,+0,+0,+0,+0,+/+*.*)/+*/+*/+*/+*0,+1-,3/.40/901:20;31<42=32=31<20<20=20=2.?1.?1.@2/A30B5/C60D63C84D95E:6G96H94K84N50X72_60i70r7/}:1†<1Ś>2>2—@6—?5—?5—?3ś@3ŁC5«C6łE8şE;˝G=ľH>ąG=°D8¦A5›?0—@/—@-—@-–?.•>-”=,•<,–=/–=/•<.“;-’0->0-?1.@2/A4.?4.@51@72@93A:4B94C84F74H5/Q51X5/a6/l8-u9.€0‹=1“?5•>5“?5“?4•B4šB4 C4ĄD4¬B5°D8´E:±E;ŞB7ˇ?4š>1–>0•?.”?+“=,“=,“<+“<+”<.”<.“;-’<-Ź<.Ť;-Š;,‰<,‡;+‡;+†:*†:*…9):):+9*9-9-€<1<3?6€A8‚C<…F?IBŠICŽJAG>—HCźNJ§VR§`Z©pe¤rś”}–ˇŹ¨Š§ „~–~yup{jdp\]iSR^JJS@BK:>E5:@29<134,22*1.)/,'/))0**1++2,,1++1++1++1++1++0,+0,+0,+1-,1-,1-,1-,/.,/.,/.,2,.8*75(13+(56&EK1\gGu‚d†“yޛБž”źš‹žŤĄ——´ž˘ĹĄ©Î­ĄÉ­¦Ę°§Ë±¤Č¬—ľź‹ł‘°ŤŤ˛Ž˛Ž˛ŹŤ¬Š„ˇ‚€™{wŽrdx]Q_ENR7QQ5SR6,03,03,03,03./1./1./1./1/////////////.,/.,.-+.-+/-..,-.,--+,,*++)*+)**()+)*+)*+)*+)*+)*+)*+)*+)*+)*+)*+)**()*()*())'()'(+)*+)*,*+-+,.,-.,-/-./-./+*/+*/+*/+*/+*.*)-)(,('0,+0,+0,+0,+1-,2.-40/40/:12;23;23<34=32<21<21;10<1/<1/>0/=/.>0->0-?1.>3/=60;60;83<94=:5>93@72C60G4.O4+Y4+d5+n8,x:-;.…;.Ź<4‘<5>3Ź@3A2“B1—A2™@0 >1¦@4ŞB7ŞB9Ą@6ź>5—=2•?2’?-’?-‘>,=+‘;*‘;*’<-’<-‘;.‘;.Ž;-Ť;-Š;.:-†:,„;,…9)…9):)‚9(‚9*9*~8,~8,€<1€=4€@7B9„E>…IAKFŚMFŚIAŽH@JFˇSO¨]X©hb©{n¤Žy™ś’§†‰¬‹‚Ş€ˇ„™€~‹wxnjxadr[ZhQQ]IITCCL;>D60-?1.=2.=4-=6.;819919:29:2;81?6/C2+J1,T2)^3*g7+o9-u=.z2˘B6ˇC7žB7™?4–>2”>1”>/“=.=-Ź<,Ž=,Ś=,Ś=.Ť>/Ž<.Ž<.‹2>5€@6‚C:G=…JB‡NE‹OGŚICŽID“PJ\Tžg`ťuiž‡už€”ˇ…ŹŞ‰‰¬‹¨‡€ˇ„~›zytoi}bby]\pUUgOO\HGRAAH8=A388.85.7/,3+)2()2()3)*4*+0*,/+*0*,0**0*,0*,2),2),3*-1+-1+-1+-0,-0,-0,-1+/4)/4*+4-%46!?F%T`8m|Qg‰™tŤžzŤˇ~ˇzЧ{‘¶‚›ÄźËźÉ™žÇťťĆšÁ•Ś·Š‚­€…®‚ڵ‰„«‰®…‰©‚~śxvŹolfZnSJZ?GM3FJ1DF./0+.0+/0+01,01,01,12-21-32.43/43/62/51.41,3/,4/,50-50-4/,3.+2,,1-,0,+0,+.,-.,---/--/,,.++-*).))+.)/.)-/(/.)-/)-.)-.*+.*+/+*/+*-,*.-)--+-.)./*./*------.,-/-./.,0/-2.+2.+2-*4,)5-*6.+8/*:1,:1*;2+=4-=4->50>50>50=4/<3.;2-=2.<1-<1/;0.=/.>0/>0/@1.A0)@2);4*77-39-28.56.:3-?-)F*'L)'S*&Z/(`5,d<0k@0yA0@/†A1‹B3Ź@3“=0“;/’8-“;/“=.”A1•B2”C2’A0‘>.‘;,—;0—:2’;1Ś=0?0„?/‚?.>.@1?0Š>1Š<0‰;/:.†:-;-{9+~@3w<.q7)w>-w>-v;-}?2{;/‚@4‡E9‰I?LA†MB„PC‡NE‘KI—SP”bY‘paŚ}j‰‰q‰–|Šž‚Ť¤‡Ť¦‰Ž§ŠŤ§Ś‡¤~ź‚uš{o—uiŤib„_[zXZsUZnSWeNPWEJK=C?6@93;0.6**3')3'+3'+1&*,*-)+***,*(),'+.(,1(-2'-3(.3(.3(02(00)10)1/*1/*0/)-1++0-(//#5,>5,>5.>5.>5.=4-<3.<3.=2.<1-;0.;0.=/.=/.>0/@1.C0*C0)A2+>4+:6-95,:3-<1-?-+D*)I*(N+'T/)Z5-_:1e>/pA/xA-€A0@2Ź?4‘=3“:2“;1Ť9.‹.Ś@0ŤA1ŤA1ŽA/‘?1•;2•;2<2‹=0†>0@/?/‚?.…@1†>/‰=0<.‡;.„;,‚:,€;,x8,x<1s9-n9+s>.s>.r8*u:,}=1A5‰E:‹I=‰K@†NA„PC‡NE‘JH—SR’f]Śwf„†p€“wž„Ą†§Ť§Š§Ť§ŤŠ¤‰źwś}ove’i`‹`Y‚ZY{X\wXZnSSaJNUCFH;C@7<737/-3*+2)*1(+.(*,*+**,+),+),-(,/)-2(02(02(02(02(10)1/*1-*3-*3-*12-12.-0,)--%8:,SXBox]€‹i‡–o†™l†žnŁp†¤p„ĄpŠ­w“¶€™ą‡–¶„“łŚ®{†«x‚¨w‚¨y¨|¦|‚¤ť{x’uqnh{eYjWMZHEL<@D5;=/12,12,12,12,23-23-43.43.54/54/85085085074/72.61-80-80-7/,6.+4/,3.+2,,1-,1-./-.0.1/-0--/,,.+*/+)./(/1'//)-/)-/)-/)-.*+.*+0,+0,+/.*/.*/.)0/*0/*0/+//-///0./0./0/-/.,1-*2-*6.+70*90+:1*<1+=3*>4+?5,?6-?6-@7.@7.?6/>5.=4-=4-=2.<1-;0.;0.;0.;0.<1/?1.C2+E0+H/+K--L,/K+.I*/E+.A-,@.*A.(E.(M.)X1*b3-g5,j:,o;-w;0=4‡<6Ś<5Ť:4Ť:4Š<2‡=2†>2…?3†A2‰A3ŚB5ŹC5Ž?2Ź=/Ś>1Š>.‡>/…>,†=.<,‰;.‰<,:-†:*;,<)~=+{<+}=1z<1v:/u;/x@1x@1v<.v;-?5‚B8‰F=‹H?‰JA‡KA‡NCŠNF‘JHTQ–f\ve‡…n‚‘tť{¤…¨‡Ş‰ŤŞŚ©ŚŽĄ‰źš}yxi–ma’e\‹a\†`]‚`[|]UrVPhPFYEBP?:D93:2.3,*/)*,)**(0'(1&*1&*1&*0'*1(+0*.0+//*.-+.,+0+,0)-0(-1(-1)-01/23/..*'**"57*PUAmv[|Ši€k~“j™jźn€ n~ m€Ąq‡¬xŤŻ|‹­zŞx„©v§v§u¦w¦y¤y€˘}}›yw‘tp‡mh|cYkUMZHDH9=?299-23-23-34.34.34.45/54/54/650761961:72:72961940940:2/91.91.80-50-4/,4..4..3/03/01/01/20.1..0--/-+.0)02).0*.0*.0*.0*,/+,/+*1-,1-,0/+0/+10+10+10+10,11/1111/010.10.2.+3.+3.*92,92,;2+<3,>4+@6-@6-A7.B8/A8/B90A8/A81@70>5.>5.=2.=2.<1/;0.;0.<1/=20=2.B3.E2.L..R+0V'/U&.P'/I).C/.<1+;2)?2)G0(R/)_.*d/)i9/k;/u<3}<6…;8‰;9Š;7:6‰>8†@8†B9…B9…C7†B7‡A7‰A5@1A/‡@.‡@.‡>-<,;+‰:+Š8*9*‡:*:)<){=(x>(x>*‚>1{7.z7.z<1v;-w=/|A3{@2€B7E:†H=J?†J@‡MBODŠQHŚSJŽ\QŽh[‹tb…g€‹m~–vž|§„©‡‡Ş‰‹ŞŠ‹¦‡‡ ‚š~}yq•oi”ifŹgfŚeg‹gd†e_~_Zw[PhPK^JBP?8D62:/.4*,/(+*%2&&5%(4%(2&(1'(/)+/+,.,-+++*+-*+-(,-(,/',/',/*+-.*+1++0+'.+"88,PUAiqYv„c{ŚhyŹh|–iśl}źmyžkz mĄr‚§t€Ąq}Łp}Łp}Ąs~Ąv~Ąx}¤x|ˇx}źzz™wuqn‡je|bWkRN[GDF9?=1:8,45/45/45/560560560761761872872;83<94<94<94<73<73<41<41;30;3083072/61.61.5106216213122011/00./1-.2,04+.4+.4+.2,.2,,2,,2,,3/.3/,3/,3/,21,21,32-32.32032032032051051.61.61-;4.<5/=4-?6-A7.B8/E8/C9/D:1D:1E;2D:1C90B8/@5/@5/>3/>3/=2.=2.=20=20>31>31@51E31M02T,4X)3W(2R)1K,1B30:6-77+:6*B4)M2)X/)^/)f:1j;3s<7z=:‚<:†<;‡;;†::‚;7>8A8B9C:C:…@9„@7‚C2‚C1B0„?/†=.<.‰:-‹9-‰7+‡8+…9+‚:+~=+x>*v?*x>*9-|/'‚8/„>4w4+s5*}A6}C7E:€G<‚I>J?„KB†MD‡QG…WJ|aNzjQ€pYu]‚|b†i†“uť|Ł~§‚‚Ş…‚¬†©„Ą€{ž}z™wz’pzlxŤlwŚkuŤmr‰lm„gkd`rX[iRR[HHL=@@4;7,70&5*$6('5''3''1'&.)&+*(++)+-*',('+*)+*)+***,+),+),-)*,#$2*'50*86*BC5UZFfpWn}^teqŠbuexiw›ks™htšiwźkz˘nx lwźmx nyŁs{Ąu{Ąw|Łwyžuzśww–tsŽom†hd{_WkPN[GCC7>:195,560560671671782782872872983:94=:5>;6>;6>;6>95>95?74?74>63=52;63:5294194184184395484373243151240/6-06-.6-06-.4..4..4..4/,40-40-40-51.32-32-43.43.43/431542540841850940:5/=60>7/@7.A8/C90D:0G:1H;2F<3F<3F<3F<3E;2C90B71A60@51@51?40>3/>31?42@53?53@72C52I35P16T/6S.5P05J22C52=90<:-=:+C7)I6(Q3)W2)]2+d3,l50v64}77‚8777‚66~75}:4}<6}>5€?9@8†?9…A8€B3€C1B1@0…=/‡;.:.‡9-…9,9,‚:,<,|=,y>,x?,|=,‡5*‹2,ˇLE§XQ‹A8|90‚F;€K=yH:zJ<{M>|N?}OBQE‡UJ‚_LrkOosR|uX‡w]Źybiź‹s ”z––zŹť|‹Ł¨…§‚€¤~{źy~™v‘p…ŽoŽp€Źpp{ŽpwŚms‡kj~bfv\_hSV[GOM>GA3@6*=0';,'9+(6+'3+(/,',-'+.').().(+-(-,*/+*3)*4(*7'*7'(3($<3,E>4IG8QR@]bKgqVjyZn‚]k„\l‰]p‘bq•eo•do—eršfuźmrśjq›itžnx˘rz¤vy˘vyźvvštw™vu”roŤkj…fc|^UlON\ECC7@91;4,671782782782893893983:94:94;:5>;6?<7?<7?<7@;7@;7B:7B:7A96@85=84=84<73<73<73<74<74<74;639529338308/09/.8/080.80.80.61.61-61-61-52-52-63.63.74/74/540540841952:63<94=84@93@70A8/C90D:0G:1H<0I=1I=1J=4J=4J=4I<3F<3D:1B8/A7.A81@70@72?61?61@72@72A83?74@85B86D97G96H96H96H94E80E8/E9-E9+G9,I9*K9+Q7*Z/&d/'n3-z6398‰;9‹;:‹=;‹A>‡@:‚=6<3:3‚<4=7‰@7A5B3‚@2?2=1„<0;/€/|>/}>/=/†9/1+˘<8ÍlfŮ~y­ZTŚC(+C&+E$+C&(F5-LC4VQ>[YD`bJgmQiwVj{Wl‚[g‚Wf†WlŽ\o”an”an–bršfsťko›jo™irśnw ty˘xxžwu›vs—su—vs’rn‹li„cb{[TmMM]CGH:E<5@707827828938938939:4:94:94;:5<;6?<7@=8@=8@=8A<8A<8C;8C;8C;8B:7?:6>95>95=84>95>95>95>95=85<73:51;30:0.:0.91.91.91/91.91.72.61-61-63.63.63.74/74/74/540651952;83<94?:4B;5B;3A8/B:/C9/E;/H<0I=1J>2J>2K>5K>5J=4J=4F<3E;2C90B8/B92B92A83@72@72A83B94A:4?82@93B;5D=7F=6G<6K<5N;4M6.N6,Q6+Q6+Q7*P9+P9)V6'f6*r6,~;3‰@9•D@›HDźJGźLF QJ™LDŽD;…;0„7-…7-Ś91Ť=4>5‡>7†=6…<5…<5=4}=3z>3x@3vA3x@3z>3<3:373‘31(&ł=;ě|zý•’Ĺhc–G@‰K@xH:nMhV>lX@t[E`L€hPysQ„wUžt\´l^É__ÓV\ŮQ[×T\äouŮzx̆|ŔŹ~µ•€«•}Ł’xźŹvśŠrśŠt™Ťu•Źu‘‘u‹’s†‘s‚‘r|‹ny†ju{amoWgbN_TBUE5R/(9/&7.'6/'81):/)=.+A,+F)+H(+K'+H)'TB4YQ9B?:D?;D?;E@8E>8D=7B;5B;5B;5B;5B:7B;5A:4A:4A83A83A83@72@64>71>71>71<71<71;60:5/85.85.74/74/961961961:70<71=82A:2B;1C:1D<1F<2J>2K?3L@2N@3N@5N@7N@7L?6K>5I<3H;2E;2E;2B90A8/@91?80?80?80@93@93<5/MD=M@:K:3T@9R62O0+a<6\3-`5.`4+^/'^/%f6,m=1q=0‰I=ŹG;—I?źKAźF>›>7š=6žD;¦ND¤PE¨VJ«YKĄOB™A5—;0›>6“98‘98Ś65†52†84„?8|@6r>1rB4oA2q=0v:0‚72Ś43’-1š',ČHIčbaĹEDÍWUáyv«VO†F<„VFmR=i[AibFjeHj`En^D|dLŤjT’bL˛m]ŃnhŰX]ŕ;Lç-Eň+Hő0Lö@YěI\ä[eĺnrâ{|Ű€}Ű؉‚Ăv˝wş„x¶†x˛x­Šw¨‹yĄŚxš€o™~mxi“oaŤbY„UM{IBxA>u:9:49:49:49:4:;5;<6>=8?>9>=8>=8A>9A>9B?:C@;D?;D?;FA=E@8E>8E>8E>8E>8D=5C<6C<4D;4C:1B92B92B92B92A83A83@93@93>71<71;60;6096/85.74/74/96196/96/:70<71>:1A:0B<0C;0E;/I=1J>0L@2L@2N@3N@3N@5N@7M?6J=4I<3H;2E;2E;2C:1B90@91@91@91@91A:4B92D93I81L/+V.,j76u99{;;…ECD?„H@…IA†G>ŽIB™NHˇPL˘OG“D7”B4šB6źC8 >3ś7-ś7-ź=0ŁC5›?0™@0 E3¤G6§F6®H:¶NE´LMŻHLĄ@D™9;‘98Ť>:@8x>3o>0zI;…LA„?8„,+’).ł9DŇLWÜKNßMMĘ>=Ŕ@?Đ`\łYQ„@5‚TDyaIe[@^]?ihIslOvgJ~dK–cNĽdXÖ_[ćUZëANň,Eţ%D˙&H˙'I˙.Nů3Nň6UE8[O5cZ9gaAifEnrOz‚]z‰bt†^r‰_p‰_p‹`qŽ`u’dw”dw”dw”fs’ix–rz—xy–x~}ť„…ť‡›…™z’zr‰om„hhd`y[TmMNaERSAVPBUOC8938938939:4;<6<=7?>9@?:@?:@?:C@;C@;C@;C@;D?;D?;FA;FA;FA;E@:E@:E@:G@8F?7JA:I@9H?8G>5F=6E<3F<3E;1D:1D:1D:1D:1D;4D;4D;4C<4?80?80<71;60:5/:5/94.94.96/96/:5/;7.>7/@9/B90C;0E;1F<0I=1K?1M?2M@0NA1M@0P?5M?4L>3K=2I<3H;2E;1D:0C:1C:1A:0A:0?;2@<3@<3D;4UD—I?–LAśNB¬RJľURĚOSÄLKĄ@4ťB0 @0Ą?1Ą;.Ł7+¤8+§=/«E6ˇ>+7$ť:'Ą>-¬A/˛C2¶D:§02Ż:BąDL˝LRşMR­KL–A>‚71{8/v6,x2*„1-ś37ą=GŐGWčM[âFIĎ53Đ:9˝31ČNKĆc]”J?Ś[J}cLj_CgdEtoOpQhJŤhN°hYäbbőQZôDQö7Iý1J˙2N˙/M˙*H˙2Oü0K÷1Jő5Lň4Mđ2Kó3Ló7Pň@XďC[îF]íIaëNcęQečUhčVkĺSjäRiăOiäNiçNlëPoňSsóVuţh…ńa|ĺ[tŐUlÍYlÉdr¨R]r-2^&'Y0,W:4Q?5B:-:8)>B1LN9VO3aV6e_?heDquT†e~ŚiwdxŚiu‹er‹dr‹cvŚeyŹhzizj€•v„›~Šž…Ť Š’Ł—¨–©™–¦™‘˘’‡†zŤypmg~b^wYTmMPcGSXDXUDYVG7827828939:4;<6=>8@?:A@;BA5G=3F<2E;1D:0D:0E;1E;2D;2E<3E<5E<5@91?80=82<71;60:5/:5/:5/96/96-;7.;7,?8.@:.C;0D0K?/M@0M@0M@/M@0O?2O>4L>3K=2J<3G:1E;1D:0D;2D;2B;1@<1@<3A=4B>5F<3S81[*&‰:?ľT`ŮTećUfăUcËJOź30–:/‘>0™A3®F=ĹIGŐBHÍ>@±B7§F5©B3¬@3Ż?3°>3łB4´D6˛G5©B/Ł<)¤;(©<(«:(®6&­3(¶97ş9=˝7>Ľ7<ż^ő@ařCfö@fő?eő?gř?hüCm˙Er˙Is˙Ou˙QuţUvňSqçTnĺaxÖfv·Wb}15j0/X1*P8,G>/>A.;D/?G/PK._T8g`CokN||`Žr‡‘v}‹qzŤq|Źs|ŹszŤozŠm}Śo’u—z•ŁŠś©•Ł®ť¦±ˇ¬´§±ą®°·°«µ­ˇ«˘” ”‘‚s„qh|c]tXSlNPdHRZCWYDYZH671671782893:;5<=7?>9@?:BA/K?1M@0M@0NA0O?/O?0O?2N=3N=3J<1I;0G;/D:0D:0D<1D<1B;1@<1A=2A?3B?6K=4\5.w32­LSŰ]ićM_ĺ@Q×6E·&+¨1)ť7(’:&—<)¬@3ĂD=Ô>?Í;;±?5¨B4Ş@2­?2˛@5¸C9ĽH;»J<¬>/Ş?/¨=+¦;)Ş9)°:,·;/Ľ<1ĘFAÎDAĐ>?Đ79Ö7<ŢBEâLNÝROÄC>ĆKCÍTLÔSMŮKJŰ?Bŕ5>á27Ű4.Ú7.Đ1+Ď:4ż84ł@9µ[P–UC{N9‚bI†kP„`F’[F­fTËrdçmhęEKň:Dę9Cç=8@?:@?:C@;DAGB>GD=GD=HC=ID>IE2I=1G=1G=1F<0F<0E;1E;1D;2C:1A:0A:0@91@91=82<71;60;60;7.;7.;7,<8-?9-A;/D0L?/M@0NA0NA0O?/O?/O?2N=3M<2L;1I;0G;/F90C9/C;0B<0B;1@<1@>2A?3B?6N;4m84—FEÄY_ŰWbßCQŢ8FÔ3;˝++­1'ˇ8%:!—9 ¤;&¶>.Ă:2ľ71§7,˘:-Ą9-§7,Ż:1¸B8şE;·E:®>2­?0§<,¤6'Ş6)¶>0ĂD;ĘG=ľ8-Î@6áD?ěBBô=Aó9>ë27Ţ.0Ű75âGBčSLĺNGÜ=9Ů10ă/2ę67Ů1(ŢŇA>ŇGBÝOMëWWíNRďDMđ:Gö5F÷2Fř.Dű1Gţ7Nţ8Sý8Tý8Vţ9W˙;\˙>a˙Ae˙Ckţ;eýjý?mú>nř>oô=mö:j˙Hr˙;a˙?d˙Kn˙Kn÷NmďUoăZnčr‚ÂajILt:8\6-P9+PC2UM:^QAreU|n•Ť‚źť‘¦§źˇ¦ź–ś•ž›ž§¤¦Ż¬¦¬¨˘§Ł¦¨Łµ´°ÂÁĽĚÉŔŃÎĹŐĐĚŐĐÍÔÎÎŐĎŃŃËĎËĆĘż˝Ŕ°˛Żź€Ś~m~k`t[WnRVjNS`FV`EX`H560560560560671893:94;:5=<7>=8A>9C@;DAHC?HC?GD=HE>ID>ID>JF=JF=MF2@@4P91r1/¨JKÁSV»>D˝06Â03Á//ş1)©1!Ł9#ś=!™;›< ˇ=#Ą;%¤6%ž6) 8-˘6,Ą4,­81·@:¸C<˛@6±B7­A4¨-Ď>-Ů5+á*&í"%ú%+˙*1˙.4í)*ç.+á3,ß4,ŕ3,ă2,é0-ç2+Ú2%Ö6&Đ2&Ď7,Ĺ6.˝>7Č`UŇq¦eSŹVBŠM:ŁWIČf[ßd_çRTčCIőFMí@Dß??Ô@<É@8Ć@7ŃFAăMLďJNô@Ků9Hý7Hü5Hř3E÷6Gú;Mú8Pü7Rű6Rý6U˙6X˙9]˙;a˙k˙?m˙@q˙Bt˙BuţBuüBtůBp˙FmúAaţEe˙KjţKk˙Vt˙_ző]tňj~ćp~Óow®^aCAg6/hB7sUJye\Ś}vŁ–­Łˇ˛®«¶¶¶˛¶ąŞŻł°·˝¶˝Ĺ˝ÁĘŔĂĘĂÂĘÉĆÍŐÎÖÝ×ŰâÚŘçÝŰéÝÝćÚÜĺŘßä×ŕŕÔŢŮĎŘĘĹËşşş˘§Ł’‡t‚qexb\sW[oS[jM\iK]gL561561561560561671872983<;6=<7@=8B?:DAHE>HE>HE2G=1E=2D;2C:1C:3A:2A:4@93?74<73<71>7/>7/<8/=90>:1A:2C<2D=3H@5I?3L@2MA1NA0P@0O@-O@-L?/L>1M=0L;1J91I81E80C90@9/>:/;;/<<0=>0>?1A?0S8-‡<7Ä\[ĘZY®86«.*˛1+®.%­2#¬9&¬B*ŞF,¦D'ŁA&ŁA&˘<#ź9# 9*Ł;0¦:0¦7.­<4¶C<·E;°@5«=0¨Dá;=Ö<<Í@9Á>4¶8,ą6.Ä94âHHé@Eđ;Dö:Hú:Iř8G÷;Iű?NţAU˙?Vţi˙>l˙>o˙>pý>qú@rř@t÷Aq÷Cj˙Mm˙Ss÷Jhë@`ýTs˙g…˙e‚÷]wď_xçh{Üp}Ćqx§ggŤ^X{ZQ—~wŞ™’ľ°­ÄĽşĆÂĂČÇĚÇÇĎĂĂĎËĘŘĚĘŘŃĘÚ×ÎßßÔäćŰéęŕëíăëďĺćňčćôččđäćîáčíŕéęÝçäŘâŃĘŃÁżÂ««©‘–Źz†xl{hbu_`rXbpVboScmT21/320431651875984984984;:5<;6==5??7AA9CC9EE;EE;HH@HH>HHLC4J@4KA5MC9LB8F=4B90E<5C:5@93?74@85?75<74<42C:5B94A96>95?:7>:7?;8@<9B?:D@7G@6J@4L@0O@-O@+L?,G@.F?/I;0K81M53K65J88F;9B?:6904,«=,­<,ł=/˝C4ÄF8Ŕ>1ş2&Ç9+Ě8*Đ8+Ö8-Ű7-â5.é3/î1-ń.,ô.-ó0,ń1,ę5*ă7)Ý:)Ű:(Ü9&Ô3Đ3 Ď8'Ć7'ľ6(ÂB5ĎSIČNCĎSKá\Wî]ZđPRí>Cň9?÷?GęAFßCDŐAAČ?9»<3±;/°HH>HH9H?:F=8B;5D:8A96?74?74@85@85=85;62=4/=4/=52<74=96>:9=<:>=9B?:D?9G@6J@4M@0O@-P?+L@*B?,B?.F<0H:1J65I56F35@65@<9:=6;>5@@4E<-J9)^B4€L>®QB«;- 2#ˇ6&Ł:'ž7$ž9%Ł>*ˇ;%§<(®?+ł@-¶=,·9*¸6(µ7)Ş9)Ą:*¤9)Ą;+¨>.«A1«B/«@.®?,«:(±<+żE6ĆH9Á?1Ľ8+Ŕ6)Ë7+Ď7,Đ9.Ń;-Ô8·;1®:-Ş<-«=.¶E5ĆL?ÔNEÜGCă?>ď?Bú?Fţ@JřCJ÷CLúDP˙FTýBSů>Sü>X˙Ba˙Fh˙Ciü?iů@jůBpüFvţJz˙K{˙Jz˙HtőKpńQső\zů_{űXwüNqýEkúDlďEiÚKgČ_p±nukLI~t°–‰Ě˛ĄäÓÉěăÜëçćęéîęçňíâóöăůűáú˙âý˙ç˙˙ě˙˙ňţ˙öţ˙ůűţú÷ýúőü÷ôúőňřďđôëîńčíďćëćÝŕÜÖŘĘĆĹł˛®žˇšŽ“ډʅʕ‡‰€†Ś~/.,0/-10.21/43/540762761:94::2<<4>>6@@6BB8CC9DD:IF=IG;JH;LI8MJ7NL7NL7OL9KI:NKBTRS_^fihxmm…lkŠkh`YiYQ\OGRH@KH>GG=EE93>:1=9083-94.;60<92=:3>;4?<5@<3E>4G?4I?3L@0O?0P?/P?-L?,<;&:=(?<+A;-B71A62>42;30=84B;5H94N2.Y,)l/.‡;= FEŁ;0˘2$ź1 ¤9'Ą<)ž7$ž8"˘<&¤;&Ş=)±A-¶A/ą>.»<-ľ:-ľ-­>-­<,˛?-°8(ą>.ËL=ËG:Ľ6*ş2&É9.Đ6,Ô6+Ô8,Ö:.Ř:/Ü8.ă6/č3,đ0-ô.+ô.+đ1)ç2'Ý6&Ô8"Ň7!Ý8$Ü5#Ú9'Ú>/Ó=.Č6)Ĺ9,ËA7É<5Í>8ŮEAĺKIęJJéCCě?AëCCŢCAŐD?ÍB=Â=4¶:.®8*«:*¬;+¬8)ŔD8ÔNEÝIEŕ@@č>A÷BI˙HPôAGô@IůCOýGTţEUú@Uű?XţA_˙Df˙Ci˙Ck˙Fp˙Iw˙K{˙L|˙Jz˙Fx˙Hw˙KtúNt˙Z|˙a€ýUvńCdűIk˙TvÜ?\ÍI`Ř{…¸||aC;Ź|n®ŽŐł§óŰŃűěĺúňđů÷ú÷ôýôěűúë˙˙ę˙˙ě˙˙î˙˙ó˙˙÷˙˙ű˙˙ýü˙ţű˙ýů˙úř˙řö˙őöýóôűđôůđóđçęĺßáÓĎÎľ˝ą­®¨˘ĄžťŁ™ťŁ—Ł«žž¦—›Ł–/.,/.,0/-10,21-32.54/650880991;;3==5??5AA7BB8CC9HEQPL^]bmlzzz’‚ˇ€‚¨€€¦{u—rkŠe_{YSmTLdMEZG@PB;2?=1@<1?;/A;-F?/H?.K?/M@/O?0O?/P>0L?/@=*?>,@=.?;/?;2>93=:5:94<94D95M51V-+j)-„28ž8C«>C¦7.Ą7(Ą:(Ş?-¨?, 9&ˇ8#¤;&Ą:&«<)ł>,ą@/˝>/Á=0Ĺ=1Ä>2˝?3¶=2˛9.®8*Ż9+°:,˛:,ł9*¶;,ą;-ÄB4ËE9Č>3ľ1'Ŕ2(Í9/Ö5+Ř4*Ú6-Ű7-Ţ7.á6,ć3,ě1*ó0,ô.+ô/)ď0(ä2&Ű4$Ň6 Đ5×2Ú0Ú4$Ü<.Ö=/Đ9.Í=2ŇD:Ä5-Ĺ60Ę;5ŘD@âMIĺKIŢC?Ö=8Ó@9ÎA8Ç>4ľVádtţź§ÓŹŽd92lJ>ĄynĐĄśőÔË˙éă˙ôň˙ý˙˙ü˙ůó˙üď˙˙ď˙˙đ˙˙ň˙˙ô˙˙÷˙˙űý˙üű˙ýú˙üř˙úř˙ů÷˙ö÷˙őö˙ôřţó÷öëďěăäÚŐŇÉĆÁşą´ł´¬±´©°¶Ş¶ľł°¸­¬´©10,10,0/+0/+10,21,43.54/77/880991;;3==3??5AA7DA8IE:LF:NH:PJ:RK9RM:SL:QK=OJDVTUfdqwxŤ††¨ŤąŹ“ĂŹĆŽ‹Ŕ‡¶{w©pmšidŤ_[~UPnNJaKGXEBM?=B;7895296/85,85,671783891;;1>5L?6K>5F<3B92=82:946;47<59<5>:1H4-Y2-w78”?D¦>E§9<Ş;0Ş<+«@.¬A/©@-Ą<)¦;'«>*§8$­:'µ<+Ľ=.Á<-Ĺ;.Ę<0Ę=3Ĺ?6ľ>3ş:/·7,·7,¸8+ş8+ş8+»7+ÇA5ÍC8Ĺ8.Á1&Ć3)Ë7-Ď5+Ű4+ß3)á4-â6,ă5,ç5+ě1*đ/*ő/,ö/*ň/)ě0'â2#Ů4!Đ5Ď4Ö1Ů/Ř2"Ř6)×9-Ô:0Ö?6ŘE=ÖF>Í@7É<5Ë@9ÖKDŮNGŇE>Ĺ;1Ĺ=1Â<0ŔCňBLűIU˙JYýFXúCYűC]ű@_űBd˙El˙Js˙Nz˙O|˙LyţIv˙Jw˙T˙T}ýJqůIm˙St˙VtřNiëD^äF]ęXk˙–˘˙µĽóťžŽHFk.)¦kcÍ–ŹóČÁ˙ăŢ˙ńđ˙üý˙ý˙ü÷ţţö˙˙ö˙˙÷˙˙ř˙˙ú˙˙ü˙˙ýý˙ýú˙üř˙űř˙ůř˙ř÷˙öř˙ő÷˙ôř˙ôřýńóóéęäÜÚÖŃÍÍĘĂÉÉżĘĘŔĘÍÂÉĐȾȿ·Á¸65143.32.10+10+21,32-43.66.77/880::2<<2>>4@@6B@4JD8ME8OH8RK;TK:TM;SL*F@*G?*H@+IA,IB0IA4HB6HA9G@:K=?ž51§;/Ş<+Ş?-Ş?-©>,©>*¬?+°?-«8%˛9(ą:+ż9-Ă9,Č:.Í:0Ď<4Ë>5Ć;4Â91Á8.Á8.Â9/Â8.Ă6,Ĺ7-ŃC9Đ@7Ă0&Ă,#Đ7/Ö<4Ó2*ŕ3,ä2(ç2+č3*ę3+í2+đ/*ó-*ö/*ő.)ń0)ě1(á4$Ú5"Ń6 Đ5Ř7#Ů3#Ö3$Ő3&Ő5)Ô8,Ô<1Ö?6ěYQáRJÓHAĚC;ĘE<ĚI?ČE;Ŕ>1Ľ:*Ľ;(Ľ:*ą:)·:(¸:+Ľ>0żA3ÁA6Á<3Ĺ<6ĎA=×EEÝGHäIMëLPčBFę@CđCIůKTţNYüJZüF\üF_řA_ůBaýEi˙Jp˙Nw˙Oz˙Nx˙Lv˙Q{˙RyţOvýOt˙Vy˙]{˙SoîD^úTlîQd÷dt˙’ž˙‹•ô‰Ç`d§MMµjgÎŹŠďĽ¸˙ŢŮ˙ďî˙úů˙üţýű˙ýú˙ţű˙˙ű˙˙ű˙˙ü˙˙űý˙űű˙űř˙űř˙üů˙űú˙űú˙řú˙öř˙ó÷ýńóůíďňććçÝŰßŘŇŰ×ÎÝŰĎŕŢŇßáÖÜăŰĎŘÓĹÎÉ<94;8185052+41*41*52+63,74-85.96/;81=:1?<3A>5C?4JB7MC7PG8SJ;WK;UL=UK?SJCSJK]Wcnl‚‚‚¦‘•Ĺš Úź¦ę˘©ńˇĄďž ë™›ĺ•Ý”•֑ʉ‰˝‚±zyˇrq‘fc~XVkPN\IGRFCLCBJ??K==G;:@;9:<94?;/C=-E@,F@*FA+EB/EC4CC7BC;AC>@ACBLCANEEOCHNAIL>HI>GDCHAA=2L:.gF7OAŤI<Ž?2”8)Ą>/©>,«>*¬?+­@,Ż@-°?-±<*±8'¶8)˝9*Ă9,Č8-Ě8.Ň91Ô;5Ď:4Í:3Ë81Ë81Ě92Í:2Ě70Ë6/Ó<5Ő>5Ň91Ě2(Î4*Ř;2Ú<3×3*ä1*é1'ě1*î2)đ1)ň/)ô-(ö,(÷-)ô/)đ1)é3(â5'Ů6%Ň7!Ď7 Ô9%Ő8%Ó6%Ń4%Ň4(Ó7+Ň8.Đ7/ăNGčXPçZSÚQIĚG>ÄD9ż@7ą>/ą<*ą<&ą<(ą:'¸9(ş;*Ŕ>0ĂA4ÉD;Ć@7É@:ŃFCŐIHÖHGŘGJßIKćFHčBDíDIőLSüQZűO]űL]űK`öD^÷DaůFfýIl˙Nu˙Ox˙Ox˙Pw÷VxóUvôTvüYx˙]{˙ZwűQkňI`ýWköUgęM^ö^mâKZćTaĺTa×XaÁefČ~ĺ«§˙ÔŃ˙ëč˙óň˙řů˙ţ˙űü˙űü˙űü˙üüţüúýýůúý÷÷ü÷ô˙ú÷˙űř˙üű˙űú˙řř˙óóýîńúëîđáäéÝÝäŮŐâŮŇćßŐëçŰńíáńńĺěóěŰćâÎŮŐ@=6=:3:7074-52+52+52+63,74-74-96/;81=:1?<3@=4B>3JB7MC7RF8VJ:WK;XL?:6@<1A>/C@/CB0BC3BD7?D=>D@CVCIaLRhU\o\br`dobbjd`afXWaJDlG>SE•XF“J7‘@+™>+¨A.®@/®A-Ż@-°A.´A/˛=+°7&µ7(Ľ8+Â8+Č8-Í6-Ń7/Ő81Ř;4Ň72Đ72Đ72Ń82Ô94Ô94Ó83Ó6/ŢA:Ő8/Ń3*Ö8/Ţ=5Ţ=5Ú91Ü5,ç2)ë0'ď0(ń0)ô/)÷-)÷,(÷,(÷-)ô/)î2)é4)á5'Ú7&Ô7$Ď8#Î7"Ď8%Î7$Ď6&Đ7)Ň9+Ń7+Î4*Ë4+ăNGód\ë`YŐOFÄA7ş;2µ9-¸>)¸>'ą<&ą;%ş9&˝9*Ŕ2ĘD9ĘA9ÎE?ŐKHÓLIÎGDÍEEÖHGăIIćCDčEHňMSřSZůR\÷O^úOaôH^őG`řGdűIi˙Nr˙Qx˙Ry˙TzđUuďZwů^}ý^|ůUpőMgůOi˙Yn˙[m˙ctđM^÷TeôO`üUgőL_äR_Ŕ^_»vqלúÉĹ˙ĺă˙íë˙ôô˙˙ýü˙˙ű˙˙ű˙˙ű˙ţýýýýüúţůöýřő˙řő˙ůö˙úů˙úů˙ööüđđřéěôććëÝÝčÚŮäŮÓčŢŐđčÝůóĺ˙úěţţňôúöáëęÓÝÜB>5A=4@<3>:1<8/:6-84+73*62)62)73*84+;60>93A<6E>6I@7MC7RF8UI9WJ:XK;ZLA[NHTIMXR`gd|~§Ť’Ę— ăžŞöĄ°˙Şł˙«˛˙¬´˙¬˛üޱůŞŻó¨¬ě©«čźˇÚ™šŇŤŤÁ±trˇhe’]Z‡XR~XQzYQvVOnMH_C?M?:@?;:B?8>>4@B5BE:?D=:CB>FH?LUDVnQfarŽisŽtvŹxŤ„o€€^lŠ[c’WYžWU¦VM¦M?ź@.ś9$ :$§<(¬=*®?,±@.ł@-´?-¶=,ą;,ş6'ľ6(Ä6*Ë7-Ń7-Ö8/Ú91Ú83×84Ř95Ů:6Ř93Ř61Ř61Ú83Ý:3ćC<ŕ=6Ú70ă@7ţ[R˙lc˙]Tç?6ĺ3)í2)ń2*ô/)ô*&ő(%ř(&ř+(÷-+ó0,í2+ă1'Ú0#Ô1"Ô4$Ó:(Ě;&Ę<(Í<)Ď<*Ň:,Ň9+Ň8,Ń7-Ú@8Ď81ÜGAôc^ĺXQËB<Ĺ@;·4*·:&¶<$ą<&ş<&˝:(ż:+Ä<0Ć>2Ç>4ĚC;ŮPJßXRÔOJĂ?:Ŕ?:ĎHDŢHGâGEćJKďPTńRWďNVíKXńL\ůRfřOf÷Ke÷JfüMl˙Qs˙RvýRvôWvîXuőXuű[w˙\x˙Zs˙Wn˙Vk˙buýVg˙\m˙[lńFXôDX˙QeîVeĆfgŞieĘŹ‹ç¶˛řŇĎ˙ëçýďîüř÷ůýüů˙˙ů˙˙ú˙ţüţý˙ţü˙űřţöôţůő˙ű÷˙ůö˙öô˙öö˙őőöččćŘŘăŐÔŢĐÍáÖĐńçŢüôçţřč˙üé˙˙óú˙ůčńđŘáŕC?6B>3A=4?;2=90;7.:6-95,73*73*73*84+:5/=82@;5D=5H?6LB6QE7TH8VI8WJ9YK>ZMEUJNWQ_eby{¤ŠŹÉ”ťâ›¦őŁ®ţ«´˙¬µ˙®¶˙Ż·˙°·˙ݶüŻ´řݞő©Şë˘ŁăŘŚŠÉ€»uq®ie c^`WŽ`V]TVPtMIbFCTBAIAAC<<:AB=FEACD?A?@DBEOMRWVdch†quš}˘‡}ź’}ś {šĄpФ`u¬Yi¶VaşQU¸HF°@4¬;)¬<&­='¨8$¨7%«8&®9(°8'˛9(µ7(¸6(»3%Â4(Ę7-Ó:2Ů<3Ţ=5ß<5ß<5á>9ß<7ß<7á>9ĺB=čC=çB<ćA;ĺ@:ćB9ä@7Ü8/Ő1(Ř6+ëI>˙[Oń@6î6,ę+#î)#ř-)˙/-ţ,+ö((ú0.ő4/ď61ç6.Ţ3)Ö2&Ô2%Ď6&Ë:'Č;'Ę<(Í:(Ď9*Đ7)Ń5)Đ4(Ó9/Ď5-Ô=6ĺPJĺTQŰLHÍB?·1(Ľ;(»=&˝<'ľ;'ľ9(Ŕ8(Â:,Ä:/Č>4ËB8ŐOFÜWPŇRIĂD=żC;ĚHCŰLHßJFĺMLëSRíTWëPVéNVěOZ÷VfřUhůSiúRküRm˙Sq˙St˙RuňMmřUt˙\z˙_{˙\v˙UoűRiűReüUf÷Rb˙Zh˙ZhóN^řScţYißS^¶`_—^W¶}Ů©ĄîČĹýáŢůééýřőúüůů˙˙ř˙˙÷˙ýúţý˙˙ý˙űű˙÷őýřôţůőţůőţöó˙÷ö˙őőöęęęÜŰäÖÓÝĐĘßŐĚďĺŰüőĺ˙ůç˙ýé˙˙ďú˙řčńîŰáßEA6EA5C?4A=2?;0=9.<8-;7,84+84+73*84+:5/=82?:4C<4G>5JB7ND8RF6TG6WG7YIBZEJpW]‚csr—Ąz§®«˛{ُvśŔp“ÂcŔTnÂG\ĐK\ŃHPÄ<<ş5.¸9*µ<'°<%°;'°;)˛:)´;*·<,»<-Ŕ<-Ä<.Ä6*Ë8.Ó<1Ů?5ßA8á>7ŕ<3ß:4Ü71Ů4.Ř3-Ţ93ćA;ěE?éB<ä=5ă<4Ű4,Ř4+Ű7-Ô2'Ě, Ö6*éG:őK>ň@6ď4-đ-)ö,*ú,,ü,,ř*,đ*)ě/+č2.ă5.Ü4+Ř1(Ő1'Ń3'Í7(Ë:)Í:(Đ:)Ň;*Ó:*Ô8+Ô8,Ń4+Ö<4Ő:5Ő<7ĺONíYWŮEEÂ3/Ŕ;,ż<(Ŕ;*ż:)Á9)Ŕ8(Ŕ8*Ŕ8,Ć=3ÇA6ĎJAŘUKŇRGĆG>ŔD:ÇG>ÖKDŘICÝLIäSPĺSTâPSâMSĺNWňXdöZhý[pţZrüVnúRlűQl˙QpýGm˙Pv˙Z}˙[{˙VrýTkţWk˙\műYhřZhűamö`kí]gďfněfmÉ\_ ]W{OFmfÁ”Ýş¶đÔŃôŕß˙őôůů÷ů˙ýů˙˙÷˙ýúţý˙˙˙˙ý˙˙řůüůôüůňüůôýřô˙ůö˙řöůîěďäâĺÚÖŰĐĘÜŇČîäŘýöä˙űč˙ýç˙˙íúýôęđěŢăßIE9HD8FB6D@4B>3@<1?;0>:/:6-95,95,95,:5/<71>93@;5E>6I@7LD7OF5RE4UE5WG8WI>XMKXNW^Zqpp–€†şŚ–Ô•ˇéť©ő¨°ů©±ů­łý°·˙±·˙˛ą˙´¸˙´¸˙ݱü«­řĄ§ňźžę—–⌋ׂÍ}yĹum¶pgŞf`ž_ZYX„QQuGGcA>Q=5DH8BT=C`@EnCJ€OU–]d«fxżkŹÉnšËoÉiŹČ`ÉWxĆIgÄ;UĆ2HŮAPŕEMŃ;<Ć71Ä?0˝@,±:$­6"­5$®5$ł5&¶7(Ľ8+Ă;-Č/Ű?0Ü@3Ü@3Ň6*ćLBĺJEÎ50ßGFóZ\ßIKŐA?Č2ČF9ĎOBÎPDĆH<żC7ÁC7ÎH?ŃF?ŐJEŰPKÝROŮMLÚKMÜKPëX`ó[güaq˙auúXmőOgőMg˙Nk˙Ou˙Ry˙Uw˙TqüTmý[p˙cu˙jx˙guúboőamídlăflŮhjÎghµfaŤ^TeG<^U©Í«©ĺÇĹďÚŮ˙óóůřöůýüř˙˙÷˙˙ű˙˙˙ţ˙˙ü˙˙ůúţűöűűóűúőţűö˙ýů˙űřţôňříéęßŮŢÔËÝÓÉíćÖ˙řĺ˙ţč˙˙ć˙˙ëřúďęďčăćßNH:MG9LF8JD6HB6F@4D=3C<2?80>7/=6.=6.=60>71@93?:4C>8EA8KC8NE6QD3RE2VF6VH;ZMGVMR[Whlk‹~‚±Š“Î’źăš¦î¦®÷©Żů­°ýŻłý°ł˙°´ţ°ł˙Żłý«®ű¨«ř¤§ô ˘ďšśé“•⊌ن…ŃÉ{u»mk¬dež_a’Y[„RQsNHbWFY_CQmBLEMMT¬U]Ľ[bĘZhŮRpâRußTuŮPlŇJbÍBWÇ8JĹ.?Ô8EăCKćGKŘ>>Í>6ĚF:ĂH6¶?+·>-¸=-ş<-˝>/Ă?0ĘB4ĐD7ŐE:ŮE9ÚB7Ű>5Ů;2Ů6-Ů5,Ú3+×3*Ô1*×4-Ú7.Ý90ß80Ţ7/Ý6.Ü5,Ö/&Ů5+×7+Ď2#Í1"Ň9)Ő>-Ô;)Ř<-ćD7řJAýD?ů64ő*-ö*-÷/2÷67í55â30Ú1,×0*×/,Ů0-Ů0+ă81ä91â:1â;2ŕ<0ß=0Ü?0Ú>1Ń7+ďWLřaZÓ;6Ń;:ćPQÜFHćPQĎ<4Ę:/Ć8,Ĺ9,Ä:-Ä<.Ă;-Ŕ;,ľ:-ľ<.ĂA3ÇH9ÇI;ÂD6ľ@2Ľ>0ÉF<ĘE<ÎIBŐPI×RMŐNKÓKK×LOéZ`đ^hüfr˙hxü]qňPeóMeýNk˙Vz˙Tx˙RrřTmö]qűhxűjw÷erüetó_mě`kękrßruĂjfŞd\žla}fVXH9kXJ”|r˝ śŢÂÁîÖÖţîďűőőűűűű˙˙ů˙˙ýţ˙˙ţ˙˙ű˙˙úý˙ţůűţőűüö˙ţů˙˙ú˙ţú˙űő˙÷ňńčáäÜŃáŮĚđé×˙úä˙˙ć˙ţĺ˙˙ę÷ůëíđçčéáQK=PJ1ĂE6ĂE6ĹF7ÉE8ÎF8ŃG:×G<ÚG=Ö>3Ő;1Ö8/Ő4,Ö3*Ř4+Ű4,Ú6-Ű81Ý<4ŕ=4ß<3Ý90Ü5-Ü5,Ű7-Ň.$Ý;0Ú=.Í1"Ć-Î7&Ň=)Í:&Ě9'Ô8)â:/ó=9˙@@˙<>ţ37ó,/ę,.á+*Ř*)Ô+(×/,Ţ44ä88é99ď75đ74î73č71ă7-Ü6*×5(Ń5&Í5(çPE˙mcŰHAČ42×CCÓ>BęVVŮA<Ň>4Ë8.Ć6+Ć:-Ć/Á=.»9+»<-ľ?0ŔA2żA2˝@.Ľ=.»<-ĂC6ÄD9ČH?ĐPGÓRLĐOIŇNLŐONé^cîagűit˙nz˙duőVjőQi˙Uo˙Wv˙VtúXpń^pônyöw€íksŢXań`mń`měboďs}ěŠĚ|Şth™yjskXON:_VGqgŻ–’ÜŔżďŐŘüéëţôőýűüýţ˙úţ˙ţý˙˙ý˙˙ű˙˙ůţ˙˙űů˙őúýö˙˙ú˙˙ú˙˙ř˙ý÷˙ţöůđçíĺÚéáÔôďÜ˙űĺ˙ţĺ˙˙ă˙˙čůúęňôçîđĺSM=RL4D=3B;3A:2@93A:4A96@;7A@;CC;IE9MG7OG2RF0UF1UH5WK?RHFXR^jg‚}§‰“Ä’žÚ˘ç¤§ö¦§ú«©ü«¬ţ¬­˙­®ţ¬®ű¬®ű¨¬ö¦Şô¤¨ń٧đˇ¨î §íť¤č›˘ć™žâ’šŰ‹“҆ŹĘŤÂ„·¨yšŹmšcz©Xi»Q_ĎMYŕIRčAIę=Aé=;ă>8ÝC9ÖH:ÎI8ĹF3ÂC0Ĺ@1äTIŕG?Ú;7Ř64Ř88Ř:;Ń98Č74Ŕ:/ľ<.ż;.Ŕ:.Â8+Ă7*Ć4'Ć2&Î4*Đ3*Ń3(Ô3)Ö3*Ů5,Ý6.Ü8/Ţ=3Ű=2Ü;1Ű8/Ű7-Ú6,Ř4*×3)Ő3&Ó3%Đ4%Ď6&Ň=)Ń>*Ę9$Ŕ2Č:&Ę4%Ő1'ç51ú<<˙=@˙7=÷37ę-1á-.Ů--×/.Ü43ă9:é;=đ9=ö26ř03ô01í1/ä1*Ţ2(Ő1%Ď3$É1$Ń=1új_äTLČ95Đ@?Ë:=äRSäJHÜC=Ń:3É6.Ć8.Ĺ;0Â<0ż=/ş;*Ľ?-˝@.Ľ?-ş;*ş;(˝;+ľ<,Ľ>0Ľ>2ŔD8ČLBĚPHĘNFĚLIĐNLčaeěafřhr˙o{˙hx÷Zk÷UjţZr˙Ysű]tîarćjtë}€ń‡‰čwyÚ_dńgtţm|óeuëlwö‘•ě˘źÄ”Š˘Ť|hjUHQSK>PJ>ęDFĺ@DŮ8=Î65ÍB;ČD8ÉC8ÇA5Ć>2Ă9,Â6)Â2'Ć2(Ë3(Î4*Ó5,Ř5.Ů4.Ű4.Ű4,äB7Ý=1Ů7,Ú6,ß9-ß9-Ű5)Ő1%Ř8(Í2 Ę1!Ď9(Î8'Ç4"Ę7%ÔC0Č7&Î8*×6,Ţ5.ć3/î53ö:9ű?>˙LKúHFňBBě>=ę<=ç7:ĺ26ć,1ô+1ř)/ô,.ď/.č1+ŕ4*Ů5)Ó7(Ć0!Ŕ.ôdYë]SĎ@:ÔDCË;;ßMMęRQŕHEÓ;6É6/Ć8.Ć9/Ă;-ľ:+ľ<,Á@-ŔA.»<)·8%¸9&˝<)ľ?.µ7)ł7+¸>1ÁG:ĹK@ĹIAČIBËJEĺa_ć^`ńdjţnwţkuó]iđXeř]mú^sőbtčdoălrě~ůŤ‹ö†…éqsřry˙y†őaqŕXfö‘˙´´Đ®˘ ›‡]jPDR9QT?jbU›ÚŔżňŘŮöŕă˙ô÷˙úý˙ţ˙ýü˙ţü˙˙ý˙˙ű˙ţůýű˙úô˙ňňţňů˙ôü˙öűýň˙ýô˙˙ô˙˙óűőç÷ńáţůĺ˙ýçţüăţýá˙˙ę˙˙ńýýóüüňXO@WP@WN?UN>TK2G?2H>2F@4GC:GE9JG8NI6RJ5UJ4UJ4VK9XNDULMZTbkh{~§‰ŹĂ‘™×źç٤öĄ˘ů˘˘üˇ˘ü ¤˙˘¨˙Ł«˙¤­üˇŞőˇ©ńˇ¨ěĄ¨íŞ©ď­©ó±©ö¬©ř ©ř—§ň’ˇâ–źÖ¦ŁÎ¶žľą…ś¸gzżL]ŃERß>Mç;Gç?ß<=ÝEBÉ:4ĘD9ĘF:ş1)ŰJEčJIéCEë>Bč;?ćÚ<=Ô<;ĎB;Č?7ĘD;ŃMAÍK>ż=0¸6)˝9,ż7+Ă6,Č5-Ë2*Ń/*Ô/+Ű2/Ţ5.çA5ß9+Ů1$Ý3&ĺ9+ç;-ß8&Ő2Ď2Ě4Ę5!Ç4"Ç4"Ę4%Í5'Ď5)Ę0&Ď2)Ń5)Ň6*Ň4(Ő3&×3'Ü4)ŕ2)ć3.ë52ë33é/2ç-2č-4í.5ö-3ö+/ď+,ě/-ę5.ă9,Ů7(Đ4%Ă-Ä3"çXHéYNË;3ßNIĹ41ÚHHáOPÜJJÔC@Ě;6Č80Ć8,Ç9+Č<+Ĺ<*Â;(ľ9&Ľ;(»<)ş=)ş=+·<,´8,µ;0ą=1»?3»?3ľ@4Ŕ@5ÄA7ďjaőpięc_ômjűqqěadôgm˙py˙lzűhxđdoébiëdjîlnîqoîqoűy{˙}…ůZlücwßbpű¬Ż¶Şš}‘u]rQJY:LR8snZĄ•Ë·°íŐÓ˙îđ˙ö÷˙÷űýřüüüţţ˙˙ţ˙˙˙ţ˙ű˙ţđ˙ôĺţéćűęď˙îř˙öü˙ôţ˙ó˙ţń˙ýń˙űî˙úę˙ůć˙ůă˙ůá˙ůáüúĺ˙˙ő˙˙ű˙˙űYPAXO@XO@VM>UL=TKĚLAÁC5¸:,·9+ą7*ż7+Ć6-Ë4-Ń2.Ř3/á53ä84ä>2á;-Ţ6)ß5&á5'á5'Ú5"Ó4Ń9$Ë9"Ç9%Ç:&É<+Ë<,Ě8,Î4*Ő3.Ö5-Ó7+Ď7)É6&Ç4"Ë4!Ď4"Ř2$Ţ2&ă0+ć.,ć,-ć,/ć-2č.3ě+.í,-ě0.č2.ŕ4*Ű5'Ő8'Ň9'Ő?.»*ÖD5Đ>1ŮF>ëVPÚB?ŘBAçUVâRRÜKHÔC>Í=5É9.Č9+Ç8(Č;*Ä;(ż:'˝<)˝>+ş?-ą@/¶>.˛:,´;0·=0¸>1ş?0˝?1ÁB3ĹC5ëi\ňmdçb[đkfőolčbańklütx˙q~˙o|öirí]fęY`ńbf÷qpţzxűvw˙x~ůRdţ[pä]nö¦©©¦“jŚkZtON_;SZ;us\©ťŤÖĸöáÜ˙őń˙úú˙úű˙űüüüüúţýű˙ţúţ˙ő˙úí˙óá˙éáýćé˙ëó˙ńř˙ňý˙ň˙˙ń˙ýď˙ýí˙úé˙öĺűóŕúňÝüôßü÷äýúóüüú˙˙ý\PB[OA[OAYM?XL>WK=VJDă=?ĺ==ĺAá>CŕBč;?ă9<Ü68Ö66Ń96Ě;6ŐHAÄ;1ľ5+ĹA5ÍK>ĆH:»=/·8)ş6)ż7)Ç7,Ď6.×50Ţ71ć95é=9×3*×5*Ú6*Ü6*Ý5*Ý7)Ú:*×>,Ĺ4!ż4ą4!ą6"Ľ8)˝8)˝3(Ŕ-%ă@;ĺ>8Ţ=3Ő<.Ě9)Č7$Č7"Ě7#Ň7%Ő3$×/&Ů.'Ű.*Ü.-Ű//Ü./Ű,)ß0+ŕ5-Ü5,Ő3&Đ4%Đ9&Ń>*ÔA/Â1 çSEŇ;0ÚA;ŢC?čJIđTUíUTçSQăOMÝJCÖC;Ď<2Ę8+Ĺ6&Ä7&Ŕ7%Ľ7&ą:'ą<*·>-µ=,˛=,°:,±;/´<.µ=/·=.ą?0ľC4ĂE7Ü\Qęg]äaYđlgöpmça`ěfgójqŕR^ď^kőhqňemń`gôeiůqqţxwútu˙nuóJ]řRhä[mńśˇŁśŠa€`UoHOd=W`Aww]­Ą’áŃÂ˙îć˙řń˙üř˙ţúţ˙úű˙üű˙ýű˙ýů˙űó˙öč˙îÜţăÚřŢáůáčűĺďüčöýëýţěýúé˙úę˙÷ćúńŕôčŘňçŐöëŮřńáţúńýüř˙ţű\PB\PB[OAZN@YM?YM?XL>XL>UI;TH:SG9RF8RF8RF8RF8PF:LE;KGEç=Fă?FÖ:>Ń?@ŃEDĆA<ş61Ĺ>:ęZYÝABă@Cć=@ă:=Ű89Ó97Đ<8ÎA8ĎE;Â:.»2(Ŕ/¸9(˝8)Â9)Ë8.Ň:/Ů80ŕ91č;7ë>8Ř1+Ů2*Ü3,Ý5,ŕ5-ß7.Ű9.Ö=/É7(Ć:)Á<+ľ<,ż;.Á;0Ĺ<6Î95ëC@í@<ä@7Ů=1Ď9*Ç9%Č:&É;%Ď<(Đ7'Ď1%Ń1%Ô1(Ö3,Ô1,Ń/*Ó2*Ó5,Ó7+Đ7)Í5'Ę7%Ě;(Ě>*Ě;(Ď<,ô^PŕF<Ň3/Ň.,ć@@űWX˙us˙ro˙jgö^YçPGŐA7Ç5(˝. Ä8'Ŕ8(»8&¸;)·<,µ=-˛=,°<-Ż;.­<.°.ĽB3ŔF7ËMAß`Wâa[ôplřtrć`aä]ać]dÖHTçYeógrôgoöelükpţsvţvvűpsűenńDXôKbĺXkë’– •_zYQlCSh?\gEy|_°Ş”çÜĘ˙őč˙ůđ˙űńţţôý˙÷ű˙úů˙úö˙řňüóęűëÚřŢÎóŇĘëĚĎěÎ×îŇŕđÖéóŰň÷áöőáűöăüőăőěŰíáŃéÝÍíáŃńčŮüőíü÷ńţůó\PB\PB[OA[OAZN@YM?YM?YM?WK=VJ@Đ@?ĐIEż>8ł2,ČC<ŕUPÜDCáACă?@á>?Ú?=Ô@<ŃD;ĎF<Ç=2Ŕ8*˝5)˝9*ż=-ż=-˝;+˝<)Â:*Ç;*Ď;/Ő;/Ű8/ŕ8/ç83é:5ć95ä52ä20ć21ç32ć40Ý2+Ô0'Ë1'Ë7-Č:0Ä7.Á4-Ă40Ě:;Ý?@ë8;í76ä71Ř5,Ě4&Ć5"Ä7#Ĺ:%Ë=)Ë8&Ě4&Î5'Ô8,Ô:0Ó9/Đ8-Đ>1Ę;-Ĺ8'Ä7%Ć9'Ç<)Ç<)Ç:(Í>-Í:*Ř>2áC:Ô/-ä::ŕ24ß56×53Ö;6ŢC>ĺKCęQIęSHčTHäUGË?0Ä?.ľ<,¸=-·>-´?.°?/®>0¬<.¬<.¬<.¬=,­<,±=.·A3şD6ĆLAŢbXâc]đnlôrrĺ`cĺ`eç`g÷kvűoz÷kví`hđ_fýlq˙x{˙y|úmsř^jóAWôC]čUhć‡ŤŁ“g]YtI^uIhwPfł˛–čâĚ˙úé˙ýí˙ţďý˙ňű˙ôř˙ôô˙ńíűęäňáŘî×Ĺčǻ伺޺żŢĽÇŕŔÍâĂŘćĚăëÓěîŘôńŢ÷ňßóęŮěŕŇčÚÍęÜĎďáÖňčßńčáňéâ[OA[OA[OAZN@ZN@YM?YM?YM?XL>XL>WK=VJQI>QI>SK>UL;XM9XM7YL9XM;WK?ULGYSWe^nnjut–~}Ą„…±†‰´‡‰ąŠ‹Á‹ŤČŤŽĎŽŽÔŹŹŐŹÓ”Ó‹ĘŔ|¸u|˛ov­io«ljś€l“cy®YvËPoăBdđ7Vö4L÷8Hď@EĺFBÝJCŰJEáGGčCJň=Nđ>LäCI×CCĚA>ÎIDş;4°4,ÉI@ŐLFŘBAÚ?=Ű;;Ú<;Ř@=ÔE?ÍG<ËG:Ŕ8*Á8(Ŕ8*ľ9(Ľ9'»8&˝:(Ŕ;(Ä;)Ę=,Ň<.×;.Ü8.á6.ä6/č50é32č./é,0î02ő47ő77î45ć21Ú.*×4/Ő62Ń32Ď/1Ň/4Ű6=ę:Dí06î02ć3/Ü3,Ń5)Ę7'Č9(É<(Ć8$É6$Î6(Ó:,Ú>2ÝA5ÝA5ŘA6ÓG8ÉA1Â;(Ŕ9&Ă<)Ć=*Ĺ<*Ĺ8&Ě:+Í7)Í0'ćC<Ű2/ď?Aă/2Ú*,Ő1/Ň5.Ő81Ö90Ň8.Ě5*Ć2&Ŕ1#ÎE5ĆA2Ľ=.µ:*±9)Ż9+«:,Ş:,«;/«=0¬>/¬<.«<+­<,˛>/µA2ÉSGŕg^ŕc_ęjiînoćchđjqőoxúq{ýt~ömuîbködn˙rz˙x~˙quúipřWfůAYô@[ęQfá~¬—†yŹkm]o‰ZyŠ`Ť—r¶·ßŢÂůôŢüúĺ˙˙ďű˙ďő˙ďď˙ëçúäÜďŮĐăÍĹŢÁ°Ö­¨Ó¦ŞĐ§°Ó«¶Ô®ĽÖ±ÇÚşŇßÁŕćĚčęÔňíÚňéŘîâÔěŰŃęŮĎěŰÓéÚÓćŮŃĺŘĐ[N>[N>[N>[N>ZM=ZM=ZM=ZM=YLUL=XLNÝAEÔFDČA=ÉHB´:/±7,ÎOFÉB<Î=8Đ64Ď10Đ51Ó>8ĎE;ČF9ĂD5ľ9(Ă:(Â:*ż:'˝8%ľ9&ż:'Á:&Ä9&Ë:)Ó:,Ů9+Ý7+á5+ä3+ę3-ë*+ń*-ř-3ü/4˙17˙37˙38ý58ů8;ő8<ň9>ň9Aô9D÷7Fű6Hţ5Eô+5ô-2í12ă4/Ů6-Đ8*Î;+Î=,Ë5&Ń8*Ů;/ß=2â>4ä=4ĺ>5ßA6ŮJ<ĎG7ÉA1Ä<,Â9)Â9'Ć8*Č9)É2'Ó9/á@8ëD>Ţ0/ŕ..ë46č66Ř3-Ő7.×90×:1Ő;1Ň;0Í:0É;/ÖL?ĚF:ż@1´9*°6)¬6(«7*©8*¬;-­=/®>0­=/«<+«<+­<.±=0ŔKAÚdZŢc^čkiđosęinőrzüvîfp÷oyůryöjsţlv˙w˙v}ůhoůcn÷RbţB[÷ZM=ZM=ZM=ZM=ZM=YLVJ>XL>YN5É83Ç/,Ĺ*&Č/*Í:3ĚC9ĂC6Ľ>/Ŕ;*Ä;)Ă<)Á:'Â;(Ă<)Â;'Ă8%Ä5$Ę7%Ô8)Ů7*Ý5*á3*ĺ3)ę2*ő33˙58˙8<˙4:˙-2˙(-˙(-ţ+1ü-3ö)0ň&1÷)6˙,?˙*Aü 9ó/ö"0ó(.í,1ć0/Ú1,Ô3+Ń5)Đ6*Ô6+Ý90ć=6ë>7ë:4č71č50â92ßH=×K<ÓE7Ë=/Ä6(Â3%Č6)Ď8-Î4*Ô6-ćC<ß82ŕ21Ň ě89ĺ63Ő1(Ň4)Ň4)Ď5)Î6+Ę6*Č5+Ĺ7+ăYNŘRFĘH;ľ@2·;/µ;.µ<1µ<1Ż9-±;/±=0±=.Ż;,­9*­9,®:-±<2ĐYQÜb]ěppôvyîmrőq|ör}˙z˙‰˙y€ôhq÷ep˙q|˙u˙ktř^jöOa˙A^÷8WéG^Űntşť‹™©„‚žn~šg—hŤśqˇ©‚´ş–ČÍ­ŐÜ˝ĚŮ»ÁÔ´˛Ě©§ÂźžĽš¶–˛Ś±†‰ł†µ~Ťą„•Ŕ‹śÂŹźÄ‘¨Ĺ™˛ČˇľÎŞÉҵÚŮÄćßÍęŢŇéÖĎáĚÉŰĆĂÔÁ»Î˝µČ·°ZM=ZM=ZM=ZM=ZM=ZM=ZM=ZM=XK;XK;XK;YLYM=WMCWLHXNOZPX]Ub`Yia\rjfokŽsp›vq§{uł~ÁŠÉŠÇŔŠ~Ľy˛r¨†r§Ťt«‡lن\„HL¤B9¶A:ÍC@Ţ@?č:;ô<>˙DE÷><ó=<ě>=ę@AëBIęCMëANăALĐ>>Ĺ@9ľ?6·>3°,ş7%Á:'Ă<)Â;(Ŕ9%Â9&Ĺ:'Ç:(Ë:'Đ9(Ö6(Ř2$Ú."ŕ/%ě7.ő=5ő82÷40÷0-ř**ű&(ű%'ű%'ű%'ű&*÷!)ř".˙'7˙(>˙!;˙6ý1˙'8ď#,ć#+ă+-Ý--Ô+(Ň-)Ü41â62č64î66ň65ň12ď./î,,ĺ/,Ü92Ń:1Ň91ěSK»"Ä+#ßF>Č.&Í.(Ţ<7čE@â:7Ř/*Ú.*â51ĺ<7ß>4Ő<.Ď5)Ë3&Ě5*Í;.Ë;0Ĺ8.ěbXŕZOČC:ą6,ş:/¸8-±3'´6*°0%°2&®2&Ż4%°6'˛:*´<.ł=1°:0şC;Ö\Wěppńsvöx|űyőq|˙|„ýw€út}üs{˙r~˙r}˙mx˙gsü`nôI\˙>\˙>[äAVÔek´•€’ˇzťm}™f~”c‡—j”źwž¦ ¨ś¨„’ŁŠŁ|€źvzšqyšo|ťr v¤u‚­wły‹¸‘Ľ„“ľ†—ľ‡śľŚ˘˝§Ľ“Ż˝šľÂ§ĎĚąßÓÇăŇĘÜÇÄÔżĽĚş¶Á˛«ąŞĄZM=ZM=ZM=ZM=ZM=ZM=ZM=ZM=XK;XK;XK;YL]P@[OC[NFYNJZPQ\SX_U^_Wfd\tibng‘qjťxp¬€yĽ‡€ÄŠÄŹ€Á~şŤt«Śn˘—q¤ˇu¨šjž™W{ť=AŞ5+±5+Â<3Ó@8Ý>8č?:óA=ő=;ő;<ô;@ń>Bî@IčAIâ@KÚAFÇ<9˝=4¶<1±;/«:,®:+˝A5ĐJAÚG@ÜA=Ô63Đ51Ő@9ÍC8»9+˛5#˝8'Â;(Ä=*Ä=*Ä;(Ć;(Ç:(Ę9(Ď9(Ď6&Ô2%Ú2%ă5*ę8.ď80đ91ç1&ç/%ę.%ď,&ö+'ű+)ţ,+˙-.ü*-ú%+ú$.˙'7˙$;˙7˙3ü0ő0ö.9ő3<ĺ*1Ű(,Ü.0Ţ02Ű+.ń:>ô7=÷4:ö26ö/4ř03ů25ô87Ű2-Ú;5×82äE?äE?Î/+Ń2.Á"Ô51á?:čE@ŕ=8Ř3-Ů2,Ý60Ý:1Ô:.Ď9*É5'Ç5(Č9+Ę<0Ę<2Ć9/ícYáXNÉ@8ş4+Â91Ă:0ż6,Â9/ż5+ľ4*˝4*»5)¸6)µ5(´6(°6)´;0»B9ÓYTčljďqtöx|ţ|„řv€ţ|„üyův~ţu˙s˙p}˙kwüdqý_nöI]˙>\˙>\éCYÓeh®Žw‰oz•bu’\xŽ]cŠ•kŤp‹•p…”mp‡]l‰]h‰\gŤ^kŹ_q•ey›izźk¬t„±vŠ·|Ť»}Ľ“Ľ‚–˝†›»‰›µ†ˇł‹­¶—ľ˝¨ĎĆ·ŘÇżÖÁĽĎĽ¸Ć·´ş¬©˛¤Ł\L<\L<\L<\L<\L<\L<\L<\Lů:Aö=Eď@GćAHŮ@EĎ@BŔ;6¸90±9+­9*©8(­9*»?3ĎF<ŮD>×96Ů74Ú;7ÜC=ÝOEÎH<µ3#ľ9(Á:'Ä;)Ć=+É=,Č<+É:)Ě9)Ď7)Ď2#Ó/#ß4*î=3ô?6ń91é4+ŕ4&ß5&â2%ĺ/$ë,$î+%ń+(ň+(ó+-đ(+ň&/ř(6ţ%:ý7ü3ř3ô%7ň0;ó5Aň8Cđ;Dđä,6đ2<ó/;ö.9ö-7ő.3ő.1ó/1í42Ü0,â=9Ú64ß;9˙mjĺCAŃ/-Î/,Ů:7ß@<ŕA;Ű<6Ő7.Ö5-Ö5+Ň6*Č6'Ă6%Â6'Ä8)Ĺ;.Ć<1Ć<1Ĺ;1ë`YáTMÉ<3ż0(Ë;3Ń>6Î;3Ň=6Ř=8×<7Ô=6Ď<2É;1Ă9.Ľ6*¶6)ą;/Ľ@6ĎTMăgeíorřz~˙ű|ű|űyűx€˙w˙s˙n}˙gvű`pü[mőG^˙\ěFZÎ`až~ev…Zi„Qh…OmRt…X{]}^x„\q‚X`zM_Pa…UeŤYn•`uśg} j~Łm¬r„˛t¶x‹ąy‹ąyŤąz‘ş~”ş“±•¬€›©†©­’şµ˘Çą®Ë¸˛Č¶´¸¬¬Şˇ˘ —š[K;[K;[K;[K;[K;[K;[K;[K;ZJ:ZJ:ZJ:[K;[K;\L<\L<\L<]K=]K=]L<^M=^M;^O<^O<^O<^Q@^Q@]OB[OC\NE[NF\OI]OO^P_bTmdY{i^‰pgś|t­…{·{ł™…şĄ…´Ż€Ş¸zˇĆxźŃuśĚcŚČMlÜFRßA@ŐA=ĎE;ĚH;ÍG;ÓE9ŢA:î@Aő0ÎD:Ő<7Ő31ŕ;9ĺA?áD?čSLßUJż:+Á9)ľ7$Ŕ7%Ä;)É=,Ę=,Ë9*Î8)Í4&Ň2&Ů2)ĺ9/ń>7ő>6đ5.ä/&ŕ6)Ţ6)ŕ4*ă1'ç.)ę-)ě*(ě**ę),č',ë'1ň)9ö&<÷!;ű=ű%Aű6Hä(6ç.<˙P\˙`l˙P\ň;ĺGDŰ=:Ú<9Ő:5Ň80Ń7-Đ6*Í4&Ć3#ľ5#ą6$»8&ż;,Ŕ9é=9ć?9á@8Ů?5Đ<2Č:.Á9-»9,»=1ËNHŕc_ěnoú|˙„ý~…řy€řy€üy˙x‚˙t‚˙n}ýetů^n÷VhóE\ý8V˙^N>\O?^NA]OB^PE^OJbNYbQdcTqdZ}le‘xr˘w¨v¤”}§¬†«Â‰©Î‚žÚw”ál‰ŰXwÚD_óBTúDPďJPáIHĐE>ËE<ĐF<ŮD>čBBđ@Cő@GőBHíDGßCDĎA=Â?7ż@7µ=/®:+«:*Ş9)®8*»;.Ě?6áFAâ=;îBBëAAŢ;6ăJBćXLŐK>Č@2Ŕ8(Ľ3#Á8(Č<-É:*Ë7)Đ8+Î0%×3)ŕ8/é;4í:5í60ë0+ĺ,'ă0,ă1-ç10ě31ó25ö37÷48÷6;ě-5ę,6í.=ń0Có-Fö)Gý+N˙3Rô9Lĺ3?đ@M˙_l˙lz˙WfőCSő>Pî3Dí3Aí3>é6<ă99Ű83Î4*Ç/$Ë2*Ç.(Đ72ňYTö\ZňXVŰA?âHFÜB@Ô<7Î70Í6-Ď8-Ď9+Ę7'Â5#ą8#ł9$µ:(¸=-ą;,¶6)Ľ7.Ć=5ŰNGŮEAÎ50Đ1-á>9ç@:ă81ç51ë20î21ë52ç83á:2Ř:/Đ9.Č:.ľ6*ş8+ČIBÜ_Yënlű}€˙…‰ýőv}öw~üy˙x‚˙s˙k}ýdvů^pôSeôF]ý8Vţ=ZďL]ŔTRz_BWhŃC?ÖEBăEFčBDîAEíCFçDEÚEAËB:ż?4»?3±;-©8(¨9(Ş9)°8*Ľ:-Ę;3čIEé??ë=>ĺ78Ú2/ŢA:îZPôh[ÚPCÉA3ľ5%Â6'Ç9+Č9+Ë7+Ň8,Ń0&Ú6-ć;4é;4ę40č/,ę-+ë--ó49ő3;ř3=ü3=˙1>ţ0=ý1=ů1>ő0Aň1Bô4Kő5Nő0Nö-O˙1X˙>_ć3HůO\˙an˙_n˙WfűP`ôDXé8JóAQę:GÝ2:Ň/2Ę2-Â8-˝;+Ľ:*Ä:/Ŕ2(ŮJBúkcÝLGáPKĎ;9Ď;9ŕLJÔ@<Ę70Ę7-Î-±9)Ż3'ş:/ĘD;ÖGAŘC=Ô72×2.ç;7ě:6č2.í2-ň,-ô,,ň./ď31č71â:1Ú<1Ň>2Â6)ş4)ĹB:×XRçjhú|}˙†‰ţ€„őv}÷xýz‚˙x‚˙q˙i{űbtř]oňQc÷I`˙:Xý?[đO_şQNqX:Sf8]uEa|InSv†Y{]~‹`g‚—l€śl}źm|¤o}¨p€©o‚Şn©l…©l«q®q°r†łr„łoµp…·r‰¸t‹µv‰¬tŽĄwš¨„°´™ČĂŻŘĚľÚŃĚÂÂĚ«±Á›ˇ±ZK8ZK8ZK8ZK8ZK8ZK8ZK8ZK8ZK8ZK8ZK8[L9[L9\M:\M:]L:^K<`J<^K<_L=^M;_N<^O<^O<^O:_P;`O;aP>aP>aP>aPÉ@8ľ>3¸>1Ż9+¨7'©:)­<*´<,Á=1Ď?7ß=:ĺ99ă03â/2ŕ42Ű94ćMEülaňh[ŮQCĆÍD>Á63É;7äUQŐF@É91Č8-É:,Č9(Č9(Ä=)±?'¦<&¨;&¬=*«7(¬3(Ľ=4ÓLFÖGAÝE@Ű96Ü30é73ě51ë0+ô1-ý,/˙+-ú./ô1/í4/ć81ß;1Ř>2É9.˝3(Ŕ;2ĎNHáa^÷yz˙Š˙‡ůzű|˙{†˙x„˙n~ţew÷^pőZlëL`őIaü9Wű=YîM]˛JGhQ1Qd6\tDf~Nu[Žc„h†’j—n‰žs¤t„¦t§r}¨p¨n©m©l…©l…¨n‡«n…­o…Żo°l€˛k‚¶n…·r‡łt‡­tŤ§wś¬…˛ąšČĆŻ×νŮŇĚ»żĘ¤¬ż’š­[J8ZK8[J8ZK8[J8ZK8[J8ZK8[J8ZK8[J8[L9\K9\M:]L:]L:^K<^K<^K<^M=^M=^O<^O<\O<`Q>aR=aR?bQ?bQ?aP>aO;bL>hOKgNRbP\cWkjfuv”}|śx—‘z–«•Ć„’Ú}…đuzţlm˙Y[ýDLţ/I˙5R˙4˝=0µ?1¬;+©8(­<,´?.»@1ĘA7ŘC=Ů74á85Ţ..â30ć:6Ó0'Ď5)ëUG˙wjë[PŃC7Ę<0Ë;0É7*Í6+Ô:0Ú70ß82ĺ63ç32ę01í12ô36ý4<ţ%6˙#9˙"9˙"9˙!8˙6ý5ř6ď2î9đ%Bď(Gî&Jń'M˙3\˙Eh˙bu˙`l÷WcńQ]ňP]ďMZâ@MÔ2=Ú=FĚ7;»/.®/(¦5'˘;(›?&ś>%Ş9'˛8+ŇVJ»;2Ä?8ľ41˝2/ÎC@ĺWSŐHAČ91Ä6*Ĺ6(Ä5%Ć5$Ă:(±<(§:&¨7%­9*¬4&Ż1%Â?5ŰRJŮIAáHBŢ;6Ţ2.ę41í1/ď-+ü22ţ,/ý+.ů+-ó++í-*ć1*ß4,Ö8,Ń=1ż2(Ľ7.ČIBŮ\Xóww˙‰‹˙‡Šű€…ţ†˙}˙v„˙j|ţ_s÷XlöTięD\őD^ő6Uń:VŕLZĄHC^K-Mc5Uo?b}JtŚ\€–g‡l›nŠ r‹Ąvڬz†«wŞr~§m|Ąi}ĄgĄg‚¦i‚Ąk‚¨m«m‚¬l®h~°iłj„¶o‰¶s‰Żrެx™°„­»żĹ©ËʵËͶ˝ĹžŞş‹—§]K7\K7]K7\K7]K7\K7]K7\K7^L8]L8^L8]L8^L8]L8^L8]L:]J<]K=^L>\L<\L<[N=]P?^SA`SB_R?`P@bQAcRBdQBdNAdLBjMIhKMdPYf[lnk†w{ž€…­‰˛Ź©Ą‡©ľ Đ‚’ây~đsqújb˙a^ůJWń>Që:Lî?NďCQę@Kć9Ę=4Á;0¸:,§6&ŻA0­<,¬6(żA5ĚF=Ě=5Đ72Ú85Ü71Ţ5.Ţ6-Ţ8*Ý<*×:'Ó6%Ů;0đSLňZOŮE9Ç8*Í>.ÔB3Ň:-Ô3+đGBá//î5:ó6=î,7˙BMń'3ú&2˙)2˙(2ů'ý"*˙'/ţ'/đ&đ)ů+7ę .ě&7ń-Aç#;˙Xrç-D˙]l˙_iţZcůU\őPWëHMÝ?@Í84Ë>7ÄA7·?1¦7&™2!™6#š;'ź<)§8-®7/ÂE?ż;7˝31Ä64Ĺ54ÔDCŰLHÚKEŃD;Č:0Ä4)Ç5(Ë4)Ĺ5*Ľ1ą=3Ö_Yésq˙ŹŹű„†ű‡Š˙„Ś˙x˙h}˙_w˙[u˙UořLfřEcń:Yô;[ć?YŰ]i‹@;VF-L^6YuBg†Mw–]}ścťg€ˇj¦n‚¨o€§n¦m~Ąl}Ąi|¤h}ŁfŁeŁfĄl¦m«l¬k~­g®f±głi‡µm‹´rŤ°vŽ®|™´‰§˝™±Á¤˛Á®¦˛°Ťžz…‹]K7]K7]K7]K7]K7]K7]K7]K7^L8^L8^L8^L8^L8^L8^L8^K:\K;^L>_M?^N>[N=[N=\Q?^RB_SC]QA]OB_OB`NDbPFcOHeNHiMJgNQeS_g_tnny~¨€‡»‡ŠÁž•ĘŁ‹»«ŁąxÓyę|{ôvjöi`ř]aóOZěFRňHSřIVőEPď?Jë;Eä9?ŕ:<Ú::Ő=8Î?7Ç>4Ŕ/Ç8(Ę8)Ó;.Ţ;4öJFä01đ6;ö6Ań.<˙@Nď'4ó$,÷%(ű),ý+,ü*+ř((ö((÷+,ď$'í%(ę%,ý;DŮ'đ4CüARŘ&6˙ju˙bjţZaüW]ôOSĺBC×<8Ń>6ľ8,żD5ąH6©>,™/)Ź(’(ž-%°:6ŃTPÎHGĂ54Ć45Ń==ëWUŘGBÖG?ĐA9Ç9/Ć3)Ę6,Đ7/Î70Ç=3Â91Â5.Ă2-É4.Ń83Ř?9ŢE=äJ@ŢA8Ú6-Ý0*ç0,ň21ű03ű03ô02ó12ô01ó/0ó0.î1-ç4-ß9-×=1ż3$ľaP@`P@]P?\O>[O?[RC[QE[QG\OG\OI_PMcQOfTTgUUgUUgV\h]nli†svŁ|ş‚ŠËŠŤÔ‘× ŠĘŞ‚´ą}źĎ€“䄆îvósjűeg÷S\íGQđFOöHR÷EQňAKî=Eä7=ŕ7:Ů99Ô<7Í@7ĹA4ľ?0¶>-­>-Ş<+«:,¸B6ÇH?Ŕ;2»0)Ę:2Í6/Đ6.Ő7,Ř8,Ů7(Ů8&Ü9&ŕ:*Ý4-Ű4.âA9éOCŢH:Č5%Č2#Ů@2â>5őHDč13ň5;ů7@ř4@˙ESř2?đ)0ë#&ě$'ů13ő-/ď''ě&%ő12ë)*ö8:č+1â)1ë3=˙R^ď=K˙[gţ^fńV\îOTđMRęEIÝ:;Ő74Ô?9Ŕ6,Á?2ĽB5ł=1¬7-Ą3)ˇ,#ś' š%©/*ÇECÂ::Ľ,,Č35×ABô^]Đ?:ŇC;ĐA9Ë=3Ě9/Ô=4Ú@8Ű@;Ű@<Ř=;Ý>;ăA?ä>>ŕ::ă?=ëIDŕB9Ý<4Ů5,Ý2+ç1-đ31ő12ř02ô02ô02ô01ó/0ó0.î1-ç4-ß9-Ř>2Ŕ4%ş8+µ;0Đ[Tízw˙“’ű‡Šú‰ý~‰˙r‚˙dy˙[t˙Tq˙Nm˙Fgű=_ô=\îEbßTg˘AHp;3SH2Qa<]xEf‡Nr“Zu^tš_xžc{Łg{Łgy˘fy˘fyˇcx byźby a| bz cz˘f{¤h{§h{¨e{©a{©`}«`®`‡˛jеn‹˛s‰¬t†¦w‚žuy“no†lSeYAPM2A>^M9^M9^M9^M9^M9^M9^M9^M9^M9^M9^M9^M9^M9^M9^M9^M9^L8`N:aP<`Q>]P?ZN>YOCXPEXOH[QO_UTbXYeX_iYcl\fk^glaildqok‚rtšy~µ†Ę…ŚÚŚŽáŽ‡×ź‹Ó­ŠÄł€©˝yŽĚ{Ţzň‚w˙sr˙cgůU\ôMTőGPôCMň>Gď-±=0ĽF:ĚRGÇH?ş5.ş1)Č91Í81Ó;0Ů=1Ű;-Ů6'Ü6&ŕ8+ŕ5-Ű2+ŕ=4ěNBăK=Ň:,Ń8*ŕB6ß7.đ>:é/0đ16÷4<ü8B˙NZ˙LUý>Eň38â$&í/1ę,,đ22č**ë/.ň::Ńć37Ý,2č9@đEM˙XcóMWŘ=CÚADáBFá>AŢ9=Ü89Ř88Ó97É83Â91˝:2ŔA:ÍNHÖVSĐNNĂC@˛72¶95ĎKIŮMLăQRďYZęRQńYVÉ51Î>6ŇB9Đ@7Ó?5ŮB9ŕC<â@;é?@ç8=ě9=ň?Cď:?č58ě>=öMJŢ93Ű60Ú3+Ţ3,ĺ4.ě30ň21ô01ô02ö/2ö01ő/0ő/.đ0-é4-ŕ8-Ů?3Â6'·5(ł9.ËVOď|y˙“’ü‹ý‰ţz‡ýoücw˙Xq˙Ol˙Fh˙@cű9\ô?^éKdŘZh…35g;0WO8WgCa{Kj‰Ps’Yt•\u^wťbyˇez˘fw dv awźavž`xź`xź`{źaz cz˘fzŁgz§fz§bz¨`z¨]|«]~­_†˛g‹´nŚłtŠ­s†¦u€ťqtŽii€dQcUDSL8G@^M;^M;^M;^M;^M;^M;^M;^M;^M;^M;^M;^M;^M;^M;^M;^M9]K5_M5`P9_P;\O>ZN@XOFXQKYPQ`W\g`hnfsshysg{sg}qh}ol}pq†rw—v}«z‚Á‡Ň„ŠŕŠ‹ĺ“ŤăťŚÚ „żžuź l‚¬nqĽvlŇviňsm˙ll˙giţ^`űRWőFMď3Ű=1Ú6*Ü4'â6*ŕ3,â70ä=5ĺE9âF9ŢB3ŢA2â>2Ű0&ę72ě0/ď.1ń.4ú7?˙PX˙]d˙Z_˙OSá.1ă03ŕ,-÷EEč66Ü,,Ű-.ë>@Ü25Ů37˙bhŕ=BË*2Ę-4Á&*Ő;=á@EŰ6:Ů37ß9=Ü7;Í/0Ě43Í;;ŐGFŢRSě`cőilôekę^aÄ@>ĆE@ĺ^[řhg˙op˙jjĺKK×=;É40Ń>7×D<ÖC;Ő>5Ő<4Ř:1Ü41î5;ń-7đ,6ň.8ď,4ë,3ó9<ţJKß3/Ü3.Ú3+Ü3,ă5.ę40ď4/ň21ô02ö/2÷/1ö.0ő/.đ0-ę3-â7-Ů?3Ć8*´2%˛8-ĂNGď|y˙’‘˙Ś˙‰˙w…úl|řatűTnýHg˙>b˙:_ú6\óAaŕNeÂWap/+_?0[W>^lIe}Mk‰St’\u–_v™_xžcyˇcx buź`uź`uť_vž_wž_xź`{źazˇbz˘d{Ąe|¦d|§`y§^y¨Z{Ş\}¬^€¬a…Żg‡®m‡Şp…Ąs r{•nrŠjexe[k^RbU_N<_N<_N<_N<_N<_N<_N<_N<_N<_N<_N<_N<_N<_N<_N<_N:^L4_N4`N8^O:[N>YOEZSMZTT_ZahbprmzuŚ{u‘{q“zp“vp’qt“rxšt}¨w¶zÄ~†Ď„‰Ú‹ŚŢ–ŹÜ™ŠÍ›…¶˘ŁŞ„‘«~yźl[šQ>ĽMBÔQIâWRëWUńSRńKMí>Cĺ4:é9<ć9;ŕ;9Ř=8Ó@8ĘB6ŔA0ş?/ł9,şA6«5+Ł.$ľI?Ůg\ŮdZËUI¸<2¸6)Ŕ3)Î:0Ů;0Ú6,Ţ3)ä6-ß1*ä71ă;2Ý9/Ţ>0ăF5äB3ß9+ß1(é4-đ31ď-.í*.ô17˙EJ˙V[˙]b˙\aâ9<ă9<Ő+,ôJKâ89×//Ű57Ě()Ě'+řXZÝ>BŐ9=Ń6:ş#Ä(+Ü<>ćAEÝ49Ů,2Ţ17Ů05Í(.Ň37äKNöaeűhnůcl÷`iú`lřbkÖLLŐNJę\ZęVTçMMăHFĎ42Î3/ŮA<ŕKDäOHßJCŮ@8×:3×6.Ü.-ő2:ű-:ř*7ő'4ő)4ř0:˙>C˙JJŕ1.Ţ3,Ü3,Ý5,á4-ç4/í4/đ3/ô02÷/2ů.1ř-0ö..ń/-ë2-â7-Ů=1É;-ł1$±7,şC=ěyv˙ŤŤ˙Ť˙|˙qöhxö_růRlűDc˙9^˙6]ú6\đFcŃN`˘IMe5+VD0[Y@^lIe|NkRt‘[w•_x™`{ždz cvž`s›]rś\s›\tś]vť^wž_{źazˇbyˇbz¤b|§b{¦_x¦[x§Yz©Y{ŞZ~Ş_­e…¬k‡©m‰§s‰Ąu„ťs|”ru‰pnlfwe_P=_P=_P=_P=_P=_P=_P=_P=^O<^O<^O<^O<^O<^O<^O<_N:aP6aP6aO9^O<[OA[RK\VV_Zakgvso†}z—‚~ˇ|Ą}wŁ{uĄxv§sy©s}°x¸{„ż~†Ä„‰ÉŤŽÎ”“Íš“Ç •˝«›µĽ¦˛É­©Ĺ¦”ŞmśfJśE2ŻA4¶@4ÂB9ÖHDčNNîJKě?Cë=?é;=ä<;ß=:Ů@8Đ@5Č>1Á=0˝:0ľ?6Ż4,§1'»I?ŃcVÖh[ŐeWĂOBş>2ş4)Ç7,Ö90Ů5,ß2+ç60â0,ă4/â7/Ţ7.ŕ>1ĺC4ä>0Ţ4%ć4*é0+đ3/đ..ď-.ň14ó78öBEöMRüY^éFKöQWŘ37ëFJÝ8<ćAEÚ7:Î.0Ń05äHKŇ7;Đ7:ą#%Ő;=Ű8;ĺ;>ë˙0=ú*7ü.:˙;E˙CHűABă1/ß4-Ý4-Ý5,â4-ć3,í4/ń40ô02÷/2ů.1ř-0ö..ń/-ë2-ă7-Ř:/Í>0µ1%±7,˛;5ésq˙ŠŠ˙‡Ť˙v†ţi}óató[p÷PjúCb˙7`˙7aů;_ěLfżO[~86]=0NF1VX@ZeEcxMj…RrŽ[v”^yša{ždxžas›]pYp™WqšXqšXt›\uś]yť_xź`xˇ_y˘^zĄ^y¤\x¤Yx¤WyĄV{§X©_‚¬d„«h†¨l‡Ąo†˘q€šmx‘jq…ij~cbv]_P=_P=_P=_P=_P=_P=_P=_P=^O<^O<^O<^O<^O<^O<^O<_N:cQ9cQ9aP<]P@ZPG\TR_Zaa_mtrŠyx€¨°~~°yyŻyu°wwµw|ľx€Á}…ĂąŠÁŤŹż—–ľ ťĽ¬§»¶¬µľł±Č¸«Đľ¦ÖÁ˘Ń»–ЬŻoT®\G¤G5¨>0ÂG?ŕWQďWVńNOé@Cę=?ć<=â=;ß=:×=5Ń:1É90Á80Á<5ą:3¸=5ÁOEÇXMÇ]OŃeXÓcUÂL>Ľ:-Ĺ8.Ň91×4-ß2,ç60ć40á2-ŕ5.ă<3çC7ćC4â:-ß3%ę5,ć.&đ0-ď/,đ31ń54ç-.â24çBF÷X]öW\˙ouĺDJëHMŕ@Ć02Ö:=ňHKî9>đ7?ř=Fň7Bč-8ô>J˙]g˙mw˙jr˙cn÷]gú^k˙ao˙]nůYeň\]ăPIâJGŐ74Ň/0Ů54Ň0.Ř95ßD?ăJDâIAŮ@8Ó6/Ö4/Ü71č66ę'/ü0<ţ2=÷+6ů0:˙>Eý?Aě44â3.ŕ5.Ţ6-ß4,á3*č3,ď4/ô41ö01÷/2ů.1ř-0ř..ň/-ë2-ă7-×9.Ń?2¶0%˛6,°61čpo˙‡˙†Ž˙p‚ücxń\pôXn÷OiűBb˙9a˙`P@`P@_O?_O?_O?aQAbRB_O?]M=]M=aQAcSCbRB`Q>dS?aP>]P@^QI[QPZSZeapsq‰yxšz|Ą~€±€ą}şxz·rs´lo¶twČv{̀ʋŤĆ–—䤾˛°»Ľą´ËĆłÓ̰ŮϬŰϧÜĎŁÝĐŁŮËśŮÂѨ¸€g ^FËzgÖue˝H?×QN÷ccéKLĺ?Aâ89ä::â:9Ü75Ů61Ö;6Ë81Ŕ5.ÇB;»<3żI?ĆXKąOAÎdVŃcTĚXIĹG9Ä:/Í6-Ř7/á6/ă4/ß0-ß2,ŕ70ä=4ŕ<0Ý7)ß5(ĺ7,ä/$ę/&ě/)î1-î20ë40č51â64×79ćJNőY]íNS×8=×6<ůX`˙~„˙~†˙ou˙go˙ip˙flő\ańX[űZ_˙UZ˙DKü8Bű7Aö2>î+9ě0>ń=H˙S_˙`i˙gr˙epţbm˙cn˙`oý]gíSSßF@Ö85Ö41×11×/.Ř31Ű94áD=ÜB:Ů?7Ö<4×90Ű81ŕ93ć66đ7<ó4;ó4;ö4<ő6;ň59ë33ă1/Ţ3,Ţ5.ß7.ŕ5+ä3+é2,ď2,ő20÷12ř03ú/2ů.1ů//ó0.ě3.ĺ7.ŕ?5Ě8,ż6,Ż/&¸;7ŇXW˙šţ{˙m€˙e|ř\rđPhôGeüCe˙;dů9`őNlÓRd‰=?P, C;&CG.KM5Y_CZjEcxMqZx’bx–`t•\r–YqYl•Sm–Rn—SoTr™Xt›Zuś]uś[t›XtťWv XwˇWv˘Wv˘UuˇTt S{Ą[{Ł]zž^u—[m‹Ub~MXrCSj>AW0?U/`P@`P@`P@_O?`P@aQAbRB`P@_O?`P@bRBcSCbRB`P@fVG_QF\OG^TSaZad`omkvw–wy˘{}®}¸x|ąquµkp´lo¶nqľuvĐ|~Ő‰‰ÓΨ§Ç¸¶ÁÉĹşÓαŰתâŰĄçŢĄćݢäۤâŘŁŰŃ ÔÇ›ÓĽš»ś€©}dĹ‹wĘ}m´VJŇa[örpěZ[çKLă?@ç=>ë>@ç;;â66Ű64Ö=8É83Ę?8ą6.¸>3˝K@¶H;Č]MÝo`Ô`QĆH:˝5)Ä0&Ô6-Ü5-Ű0)â51ĺ95ä=7ŕ<3Ü8.Ű5)ß4*ă5*ě7.í5+í2+ë0+ç0,ă2,á4.Ý52Ö87Ř>@äJLóW[ú^bü\dţ]e˙aiôS[ďNVńQY˙_g˙jq˙flőZ`óNTí4:ń,5ô*6÷-9ń'5ĺ!-ć'6í7CţPZ˙Zd˙bl˙dm˙en˙cmý[hőQZĺEEŢ<7Ű64Ţ63ß55Ü30Ú2/Ű62ŕ>9Ű>7Ř;4×:3Ű81ß82ĺ95ç:6é9;ę7:ë7:î79î79ě65ĺ31ß2,Ü3,Ý6-ß7,â6,ĺ2+ę1,ň1,ő20÷12÷03ú/2ů.1÷//ó0.ě3.ĺ7.ß<3Ô=2Â8.¶1*·74ŘZ[˙“űs}˙fz˙^vůWo÷OiůFfű?bü^QA^QA^QA^QA_RB`SCaTD`SC_RB`SCaTDaTD`SC^PCbUM\QM[QRcYbkdtqm„vu•y{¤y{¬y~¶x|»nsµei°`gŻjn¸ruĆ}}ׇ„Ý—”Ű©§Ř»ąĎËĘČÜŘżĺá´éäŞíč¦đéĄíĺ¤çá§âۧ×ϡËĂś¸Ż©››k¨p§o`śRGş]UÝmi÷uuď__äJLĺ@DëADě>@č8:â66ß=:Ô;6ÔA:Â70·7,·>3®=/¶F8Űj\ßi[Ů[MĘB6Ĺ2(Ń4+Ů6-Ů2,Ţ52éA>ěGCŕ=6Ő2)Ő1'Ú2'Ţ0%ć4*č1)ç.)ć/)ă2,â7/á=4ŕB9Ć-(Ę43ÜDCđVXú^b÷ZaóS[ńQ[čFQęHSďOYü\f˙mv˙ntôY_ă>Dé.5ô-4ű2<ü2>ő+9í'4ö6E˙KW÷ISőPWřW_ţ^f˙ck˙`i÷RYéBIŢ97Ţ71ă75é<8ë;;ć95á51Ý60Ý:3Ů;2×90×90Ü71á83ç:6é<8ă99ă99ĺ97ç98č88ć74á40Ű2+Ú6-Ű7-Ţ8,â6,ç2)ë0)ó0,÷10ő12ő13ř02÷/1÷//ň0.î3.ç6.Ý90ÜD9Ç80»4.µ/.ä_b˙řlwţbxţWsúPmţLl˙Cfö:]ó;_ěIfÔSgťDJl;4O>,=<':?(ED/FH0Q\U'B\/E_2Ic6WJ:ZM=\O?^QA]P@^QA_RBaTD`SC`SC`SCaTDaTD`SC_RB\PDZOMZQVbXcjcsqm†vu•xzŁz|­w|´sy·mr´biŻ]d¬ag±lrľx{ʉ‰ß•‘夡䶵áÇÄŮŐŃÎáÝÄçäąëćŻîę­îč¬éâ«ăŰŞÚÓ©Ë¡»¶™ĄĄŤ›™„“vq™rc›bWłi`Ďrmű‹‰őutę[]ćLNčCGí@Děô@?ď=;ć95ŕ72Ý82Ú91Ů80Ů80Ü71ă73č96é:7â:7ŕ;9ă:7ć:8ç:6ĺ84ŕ5.Ú3+Ú6,Ű7-Ţ8,â6,ç2)ë0)ň/+ö0/ô22ô23÷12ö01ö0/ň0.î3.ç6.Ü8/ŕF<É91ľ3.¶,,îfj˙y€řgt˙byýTqţKk˙Gj˙>cň6Yë=^ăQhÄXeBBgC7[N;GF1AC-JH3EE-LU6Sc>btLlUp‹XnŤRkŤPiŽKgŹIhJh‘Kj“Mk”Pm–To–Wp—Vt›Xs›Uq™So—Pj“MgJeŽHdŤG`‡D]AWy=Rq8Mi6Lf6Ne7Of8Lf7Oi9Sm>UH8WJ:[N>\O?]P@]P@_RBaTD_RB`SCaTDaTD`SC_RB_RB^QIVMR_Xhnf{tp‰xt—xwˇxz«y{´sy·jrł`g­[c«_g°jr»v|Ć€„Γ•ŕžžä­®ç»˝ćËÉßÔÓŘÝŰÎáßĆĺáľćâĽäßąŕ×¶ÚеÎƱľµ¦­§›šŚŚŽ€‰…yŠ|q‘vk›si¨meşoi扄î~ósrńedńWYđMNîDEě@@ă:7ŕ;7äB=ŢE?Ě<3Ć=3ÄB5Ż1#®0"ÇI;ÜXKÖN@Ě=/Î8*Đ6*Ń3*Ö42őUU˙khú]XăD>Ü;3ß;2ă80â4-ă2,â1+Ý0)Ô0'Í/$Ć/$Â0#Ĺ9,Ŕ4'Á3)Ë:5×CAŰEGŰBGÚ@HŕDOëOZňVaňVaú^i˙goóYaáBGę:<í57í38ď28ń3=÷9C˙HS˙V_ä6?Ý4;ß6;ëADůJOüIMô;Aé13č51ë95ď<8ň<9ó;9đ:7é73â70ß;2Ü;1Ű:0Ü90ß82ä73č64é75ć:8ä;8ć:8č:9č;7ć95á6/Ü3,Ű7-Ü8.ß9-á7*ĺ3)é1)ń0)ô1-ó32ń33ô22ô01ô1/ň0.î3.ç6.Ţ7/ŕC:Ę70Ŕ1-Ŕ13÷jp˙qzúftű]túPm˙Cf˙=d˙9cő8^ćEdŮZm«V[wE>dJ=aYFTO;KI4PI6GE.GP3L\8ZlFf|Nm…SmŠRlŚMhŤJfŽHgŹHg‘Ii’Lj“Ol•Sn•Vo–Ut™VsUn•Rj’LfŹKcŚHaŠF_FZBX|?St;Pn8Ok:Rl=Wn@ZqCUo?Vq>WqARE5UH8XK;ZM=[N>\O?^QA`SC`SCaTDaTDaTD`SC`SCbUEaVP^Vcje|yu}yšzyˇxy©vy°sw´jrłcj°[c«]e®hpąv~LJьәťÝŁĄŕŻłă»żĺĆÇăĎÎŢÔÓŘ×ÖŃŮ×ËŮÖÇ×ŃĹŇÉŔĚĂľĹąą´¨¬ŁšťŠ‹†x{tyvquleleŤogŽa[ť`[ŔsmŘyuđ{ü|y˙rpýccöSTđHGďC?ę>:á<6ŕA;Ń:1ÓC:ÝTJÂ>2¸6)˝9,Â=.Č?/ĎB1×C5Ö@1Ń7-Đ2/ęLMú_]ňWSßD?Ů:4Ű81ŕ70ć93ç92ç92â:1Ú9/Ń9,Ę8+Ä8)°(¸3"Č@2ÓI>ŇD@Ë;:Ě7;Ď:@Ń7AâHRíV_đYbő^g÷`ięU[ÜAEĺ@>ě?;ńACúGJ˙LT˙QY˙T]˙V^á4:Ú/5Ü/3ç7:ô=Ař;?÷48ó12í42ď95đ86í42ë20ę20ç40á4.ă<4ŕ=4ß<3Ţ;2á83ä73č43č43č88ç98č88ë99ë97é75â5/Ţ3,Ű7-Ü8,Ý:+ß7*ă2(ç0(ď0(ň1,ń42đ43ó32ó11ó1/ń1.ě3.ç6.ŕ91Ů<3Ě71Á0-Ń@C˙nuýkuůbqđRiőHf˙;a˙4^˙6bű>däNiĘ\i‹GFjF:]L<]VCXQ>RJ7OG4ID.DJ.GU2Sc>^sHgMj‡OjŠKgŚGgŚFfŽGfHgJi’Nk”Rm”Um”Uq•Uo”Qj‘NgŽKcŚH`‰E_F^†G\‚E[~DWxAVr?Uo@YpB]rG^uG[sCYtAXs@NB2QE5UI9WK;XL\P@^RBaUEbVFbVFaUE_SCaUEfZJh]YngwupŽ|x™{y wx¦vw­puŻjoŻah¬^f®_g°em¶pxŔ}…͉؏—Ř™ Öˇ¨ÖŞ±Ű´ąßşżßŔÁÝĂĂŰÇĆŘĆĂÔĹÁĐÁ»ÉĽłÄ»°Áµ¨Ľ¦Ż–‹ś…Śqqqtpopfewhe†nj„_Y•d_›ZTąf`Ůsnđzv˙{w˙ws˙hfüZWţRNőHBć=6çD=Ř;4ŰG=ę\RĹ;0Ä<0˝7+ľ6(Ç>.ŃD3ÔB3ÔA1×A3Ň86Ř>>ÝCCÜB@Ö=7Ő81Ř7/Ţ7/ĺ:3ć93ĺ:3â;3Ű>5ŐA5ŃE8ĚG6®-Ľ;%ÍJ8ŃL=Ć=5»0+Ŕ00Ę9>×AJáKTëU`đZeđ]eěYaßLRÔ>@Ď4/Ř7/ŕ;9ę@@ňCHőDJńBIî?Fâ5;ß26ă36ď8<ő79ř14ű/2ü14ď20đ95đ74ě0.č,*ç0,ć3.ŕ3,ä=5ŕ=4ŕ=4ŕ<3ă:5ć95ę65ě65ě57ě57í57î68î66ë54ä2.Ţ1+Ţ7.Ý9-Ý:+ß7*á4&ć1&í1(ń1,đ42î53ń42ň21ó1/ń1.ě3.ç6.â;3Ň4+Ň:5Ć20çUX˙pxůcnňZiçI`óEf˙8b˙/]˙5b˙BhÜLe±PYj5/^G7XK:UN;[P>VI8M@0JC0@F,AO.K[6Xj@bxGfJhIf‹FeŠDc‹BcŤEeŽHgLh‘Oi‘Rk’Sn’RlPhŹNd‹JaŠH_F_‡H_‡H_…H_‚H]~G]yF]wH`wKcxOczN]uC[vAZu@JC3MF6QH9TK.ŃE6Ë>-Á2"Ę7'ŰE7ŢE?Ó97Ě3.Í50Ô;5×=5Ý<4á=4ä;4ä92Ţ5.×4+Ď5)Ę8+Ĺ<,Â?-ÔS>ČG2˝<)ľ:-Ă=2Ä;5Â74Â43ÜKNŢMRáPUĺTYčY]ĺVXÜMOÓD@É5+Ń7+Ř7/Ý52â24ĺ26ć18ć.6ě4>ě3;ó5?ű9Bý4>ú+5˙(3˙/9ř-3ů57ů59đ./ë*+é0.č51â5/ă<4ŕ=4ŕ=4á=4ä;6ç:6ë76î66ě46ë35ě25í34í55ę41ă1-Ű0(Ţ8,Ý:+Ý:+Ţ8(ŕ4&ă3$é3&ě4*î50î52đ42đ3/ň2/ď2,ě4,ç6.ä=4Í/&×>9Ě35ůbg˙oy÷[iđNcęC]ůEh˙:e˙0]˙6aöGhÉJ]“CFR,#VI8SL:OH6[O?UI9F?-HF1>B'@J(IU1Sd:_rEe}Ig„JeHcCa‰BbŠCcŤEeŽHfŹMhŹPiQlPkMfŤJcŠIaG`‡F`‡H`†I^G^F_}G`|Ia{Kd{Mf|Nf~N]xC]z@^{CDB6FD8JF;PG>RH>WI>]M>_O?`Q>aR?`SB_VGbYRgb_ojnso~wq“zt |y¨yw©pp¦ggŁ`a˘\_¤`b­fhµopŔyzĚ„‚ÖŤ‹ß–’眙栞ݡˇ×ŁŁŮ¤¤Ú¦ŁŘĄ˘×ĄˇÔ¤ ÓĄžŃŁťÍ˘šÉ˘™Čź–Ă™Ť»Ž‚°|žrn}igld^`fZZkYUrWP}WNŚWO•RI¨ZPµXPÁZSŘkděyrů‚|˙‹†ůuq˙usí_]ßPLŮEAďZTĚ71Ĺ2*Ä6*Ă7(Ŕ7'Ă7&Ć9(Ę;+Đ<.Ő?1Ň8.Ö<2×?4Ó;0Í5(Í3'Ř:/â@5ŕ90á90Ţ7.×7+Ď7)É:*Ć?,ÄA/Â=.Ć@4ËE:ÍG<ĚC;Ĺ<4»2,¶+&Ç<7ĐEBŮOLŕVSăYVáXRŘOIĐC:Ě;*Ó:(Ü8,â5.č./ě*2đ(3ô'6ú->ú+?ű*?ý)?˙(?˙&?˙$<˙$<˙$8˙'9˙+<ţ,9ó+5í.5č45ç;9Ý84Ý:3Ţ;4ŕ;5ă:5ć95é73ë54ë35ě46ë54é54ç53â5/Ý5,Ű5)Ü8,Ű9*Ü9(Ţ9&ß8&â7%ă6%ć4&ě5-í4/đ50ń4.ń5,í5+ę5*ĺ7,ŕ90Ř7/Ő:6Ű?B˙mw˙bsűSjőIc˙Kjö6[˙6_˙cçNjŻIV^$"D, ?:'HE4PMC/?G/BH&EK%KS.S]8[kDavKd~NdJb†F`Ba‰@c‹BeŤFhJlPm‘Ql‘LiŽHgŚGf‹HfŠJe‰Ic…HaGdJdIbGc~GdJdHe~Ga}C[{<\=_‚B=?4?A6FC[K>]M=`O;`Q<^SA^VIc[Xhcinlyso†tp•vpžtp˘pm˘gfźaaź]^ˇ\\¤ed°kjştrĹ}{φ‚ŮŤ‰ŕ”Žć“ĺ›–ŮśŐť™Öžš×žÖť—Őś–Ô›•Ńž–Ńś”Íś“Ę›’ǚ牻®‚xťtk|mdif]^f[YiZSoWM{UJŠVKšVK°\RşYRÁXRÓfaŕqjăvoí~wý‡…˙‡…÷usîgdăUSëZUÇ2,Â/%Ĺ7)Ă7&Ŕ7%Á8&Ä8'Č;*Ď;-Ó=/Ő9,Ő9,Ó:,Ň:,Ń9+Ń9+Ô8+Ř8,ß;1ŕ90Ü8.Ö8,Ď9*Č9(Á:&ľ9&ľ6*Á80Ć=5É@8ĘA9Č?7Ĺ<4Â91ż4-Ç>6ŃH@ŘOGÚQI×NFĚC;Ă7*Ę7%Ó8$Ý7)ĺ4,í//ô+3ů)7ý(:˙+@˙*A˙)A˙&A˙#@˙!<˙=˙:˙:˙ 9˙%;˙);÷-9đ19ę7:ĺ;;Ü94Ü:5Ţ;4ŕ;5ă:5ć95é73ë52ë35ě46ë54č64ä71ß6/Ü5,Ů5)Ű9*Ű9*Ü9(Ü9&Ţ9&ß8%á8%ă6%ç5+ę3+ë4,ë5*ë5*ę6(ä7)á7*ß;1Ő4,Ó54óTY˙es˙[pőGb˙Ih˙Ad˙;`ý3[ů;aňMmÍLb‰8?S*$A5'69(;<,B@1DA0@?-=B.>G,=CKP'\b_„?b‡B69.:<1??7DA:KB;OE/Ô>/Î6(Ë/"Ú:.Ú8-Ů7,Ö8,Đ:+É:)Â9&ľ7$˝5)Ŕ6,Â8.Ä:0Ç=3Ę@6ËA7ĚB8Ç:1Ę@6ĎE;ĐF<ŇH>ŃG=Ę@6Ĺ7+Ě6%Ö6&ß7,ç4/ń03ö-5ű+9˙*<ü';ü'=ű%=ů#;ű!:ú7ü7ţ6˙8˙6˙$8ý*;÷2<ń6=ë8;ä::Ü94Ü:5Ţ;4ŕ;5ĺ95ç85é73ë52í36î47í55ę65ĺ61á6/Ü5,Ř4(Ú8)Ů9)Ú9'Ű8%Ý8%Ţ7$ŕ7$á6%á5'â4)ă5*ă6(ă6(á7(Ţ6)Ű7+Ý<2Đ1+Ř88˙kr˙]nţPiń<[˙Hl˙7]˙>eů5[î>bćVqŞ?Qa#(L/);7+07'37&9<)@=*A<(@>)BB&?FXa4s~T€Śdw‡`hSa}La€G`C`…?a‰@cŚ@hŽEjŹJkMl‘Nn“Mk‘HhŤHgŚGh‹Kh‹KfLd†Je„KdJc€FdGf‚Hf‚GeFb€Bc†DeŠEhŤH25*58-;<4A>7G@:KB;RE.Ń>.Ë8(Ç/!Ň6)Ó5)Ň4(Ň6)Ń9+Í<+Č=*Ä;)Ä:-Ä:/Ć90Ć9/Ç:1Č;1É<3Ë=3ÓD<ÓE;ĐA9Ë=3Ë<4ĎA7Ď@8Î;1Î5'Ů5)â5.ë31ň/5÷+6ů)7ü):ů&9÷&;÷&;ř'<ű&<ý%:˙$:˙#:˙"7ţ#7ú%7ů,;÷5>ň9?č8:ŕ87Ü94Ü:5Ţ;4ŕ;5ĺ95ç85é73ë54î47î47î66ę65ć72á6/Ü5,×5*Ř8*Ř8(Ř8(Ů8&Ú7&Ü7$Ý6$Ý6$Ű5%Ü6(Ý7'Ü9(Ü9(Ű:(Ů9)×:+×:1Đ3.ëHK˙oy˙VjřD_ř<_˙Ag˙3\˙8bř>cčMlĹOe€0=J C1-11'.6'4=,=B.B@+B<$E>$HE$U\0p~MŹźr—¨|mj„W_{J]~E_Ca†@bŠAfŹCkJl‘Lm’Om’Oo”Nl’IiŽIhŤHiŚLiŚLhŠNg‰Mf…LdJdGe‚HgHh„If‚Gd‚DgŠHiŽIl‘L-3'36+891<;6B=9G@:MD=QG=SI=SK>UNDZUQa^eigumklkŠjhb`ŹXVPP†QQŤXYš`cŞfj´orÁuxÉ|Đ‚…Ö†‡Ů‡ŘŠ‰Ů‹ŠÖ‹ŚĎ‹ŠĚŠ‰Í‰‡Đ‰…фъщ‚ĐŚ„Ď‹„ČŤ…ÁŤ†şŤ†°‡Ą~w–m…„Yj€SZnVTc\RZaOZ_HeZDxUB’LD¸TRĐSWŰRYĺ^dčejăklćuqäunâoh÷~v˙‚{˙‡~ôl`É?2Á8(Ŕ;(ż='ż='Ŕ<'Â;'Ĺ:'Ç8(Í7)Ř:/Ů8.Ň8,Ď9+Ě:+É:*Č9)É7(Ď9+Ń7+Ň8,Ń7+Ď7*Ě8*Ë<,Ë>-Ę<0É=0Ę<2É;/Č:0Ć8,Ĺ7-Ĺ5*Đ@7Đ@5Í=4É9.Ë80Î;1Í:2Ě5,Đ/%Ů0)ä20ě25ô/8ů-9ű+;ü,<ü-?ű.?ü/@ü/@ţ/A˙/?˙-?˙.@˙)9ü)8÷+7ő0:ń6=í8=ă77Ü43Ü94Ü:5Ţ;6â:7ĺ95ç85ę65ě44ń48ń48ď56í76ć72á6/Ú6,×5*Ř8*Ö9(Ö9(Ř9&Ř9&Ů8&Ú7&Ů8&Ő8%Ő:&Ö;'Ő=(Ô=(Ô=*Ô=,Ô<.Ń7/Ú;8˙^e˙aq˙Pi÷<[˙Ai˙4_˙4`ű1YôKlŰYq•>NZ%-C),:2/-0)08+7@/:B+<<"A<UH(`[5{…SŁlŞľ‹¨ľŤŽĄwo‹[aK^F`„DcBeŤDj“Go”Np•Po“So“So”Nl’IiŽIgŚGiŚLiŚLi‹OhŠNf…Je„IdHe„Ig…Ih†Hg…Ge…DiŽIi‘Kl”N+1%.4*470893>:7B=:HA;KD1Î8*Č4&Ę8)Ë<,É:,Č:,É;/É;-Ę:/Č9+Č5+Ç5(Ę6,Ď;/Ň>4Ó?3Ö?6Ő>3Î7.É,#Ň+%Ü-*ç02đ39ö1;ú0<ý0?ý0?ů0@÷1@÷1@ö0=÷/<÷-9ú,9ú*7ý/<ů/;ő0:đ3:í6;ć69ß55Ů42Ü94Ý:5ß:6â:7ĺ95ç85ę65ě44ň59ň59đ67í76ć74ß6/Ú6-Ö6*Ő7+Ô8)Ô8)Ô9'Ő8'Ő8%×7'Ô9%Đ9$Î<%Ď=&Ď?'Î@(Î@*Đ?.Ň;0Ń61ëHI˙fr˙Od˙Hfű7[˙Amý/]˙7aň4XăPjĽUfm19E(*E697325406906=-3;#38CCja:…Tź®w©Á‡µÎ—ŻÇ“’®}v”be†Q`‚F`…BcBgŹFm–JrOr—Rq•Up”To”Nl‘KhŤHf‹Fh‹KiŚLi‹Ni‹Og†Kf…Je„If…IhIi‰Hi‡Gf†CjŹJk“Mn–P(0#+1'/4-350764<85@=8C>8IEů4>ů1>ú0@ř/?ň.<ď/<í/9í07ď-5ď,2ň+2ń*1ő2:ő3;ň5;ě59ĺ57á55Ţ65Ü75Ü86Ý97ß:6ă:7ć87é77ę67ě46ô5:ô5:ń7:î87ć74ŕ70Ú6-Ô6*Ó7*Ń8*Đ8*Đ9(Đ9(Đ9&Ń8(Đ9&Ë9"Č:"Č>$Ç?%Č@(Č@*Ě=-Ď;1×96üU\˙^q˙Gc˙?a˙:a˙7gţ3bű8`íFdĹNa‰@IS12A64@:<:46?56<7179+4="9CSZ.‚‚P˘§q±ĹŠŻËŽ°Ě’¦ĂŤ’®}{™egQ_E^@c‰@iŹDo•Js™PsSq•Uo“Sn“NkJgŚGeŠEgŠJh‹Ki‹Ni‹NfKd†Id†IeHgŠJh‹Ii‰Hf‰Ej’Ll–No™Q&.!(0%-2+13.331764:95<;6GD=DD*ľ;'Ľ7$Ŕ4#ŃB2ăOAäN@ŘB4Ë7)Ë7)Đ>/Ę;+É:*Ç8(Ć7'Č6'Ę6(Î8*Đ8+Ó;.Ő;/Ő9-Ň6*Ő7,Ú<1Ý<2Ü71č88ń8=÷:Aű9Bú4Aő/<ň,;ď,:í/;ę19ę38ë48í57ń56ô36ó57đ37ď6;í9<é69â45Ý33ß76ŕ;9Ý86Ţ97á96ă:7ć87é77ę67ě46ô5:ô5:ń7:ě89ć95ß82Ř7/Ó7+Đ8+Í:*Í9+Ě;*Í:*Í:(Í:*Ě;(Ĺ9"Â:"Ă=$Â>%Ă?(Ć?+Ę<.Ď81ß<89?48E26C41=:)>E&FU*bs?›a­˝±Ëګɋ¦Ă‹ťş„ެz{™ef‡PZ|@^?a‡>hŽCo•JrOr—Rp”Tn’Rn“NjŹIf‹Fd‰Df‰Ih‹Ki‹Ni‹NfKe‡Jd‡GeFgŠHh‹Gh‹GeŠDk“Lm—OpšR(0!'/"(-&).(.0-3317839:4==5?@8DE@JLKORYVXeZ\s]^}YX‚[Y‹^]–eeŁnn¶wxČŘ„…⌌đŠŠě†‡ä‚‚Ü€€Ö‚€Ó‚΀ȄÇË„„ΆĐÓ‹„ŇŚ„ĎŠ€ĹŠ€Ľ‰€Ż‡}ź€xŤwozkegf]Xm[Qm[Mi\Lf^Kd_IeaHhaGo_FoP;…UAĄ]OżaYŃYXÝQTîS[ř\`ůhećYPÔD<Î70Ń7/×:1Ö=/Ô;+Ď9(Í:&Ď9(Ń:)Ô8)Ř8*Ü5,Ü5,Đ/%Í3)Ě8,Ę>/Ĺ@/Á@-ľ=*Ľ9'Â9)ľ0"éWJßK?Ă/#Ń=1Î2Ü<0Ů7,Ú8-â>4ěC<őBEň9?đ2<ö4?ö2@ń-;í-:í2=ě7>ć5;ä68ĺ78ë99đ:9ń78ď77ň;=î<<ę::ć87á85ß74Ü75Ü75Ţ97á98ă99ć::é9;ě9<í9<ď8<ô7;ó6:đ8:í9:ç;9ŕ;5×90Ď7,Ę8+Č9)Ç9+Ć:)Ç:)Ç:(Č;*Ç<)Á9#Ä='ľ:#¶4Ľ9%ÉD3Ď?6Ę1,˙^e˙Vg˙Ib˙<\ř1X÷0Yű6cőBiçYo¬FQo33N3,B?6>C<@78@-1L/3K2.A9&DJ(Zn=~š_ž¸w¨Ĺ‚¬ĘŚźľ‚¶€”˛~‚ nf„RXvBY{?]€>c‡AiŹDn”Im•Ll”Nk’Ol‘NiŽIfŠDc†Bc†Bf‰Ih‹KfŠLdJg‹MhŚNg‹Kf‹HeŠEf‹EjŹIk“Jq›QržSt U*0")/#).').(-/,130561782;<4>?7AD=HJGOPTSUaXYkZ\u\Z^]‰db”kj¤utş~·‡ß‹ŚéŤŤďŚŚđ‰‰ë…„ ؀~Ҁς‚Ě…†É†‡Č‡Ë‰Î‹ĎŚ†ÎŤ†ĚŽ…ČŠ€Ľ‰±†|ˇ‚x‘{s€tknlb`e]Re`Me^Kf^Kh]Ki\Ij^Hk_Gn^Ew_G[C’YF­ZLÇVPŢRSőQX˙X_đUSăLEŮ>9Ő60Ů6/Ü90Ű9,Ö9(Ó8&Ń8&Ń8(Ô8)×7)Ü6*ß4,Ý5,Î0'É5)Č9+Ć=-ÄA/Á@-ż<*ľ9(Ä8)Ë<.äREÜH<Č4(Ě:-Č:,Â4&Ę=,Ë<+Ę;*Ę;*Ë:)Ě9)Î8)Ď6(Ó7*Ö8,Ů7,×3)Ő1'Ř1(Ţ7.ç<5řDGń8>í/9ď/:đ0=î.;ë1<ë6?ä5:Ü36Ř22ß:8îEBřIFřDCó?>é:7ç98ĺ97â96á85ŕ85á98á98ŕ87â88ĺ99ç9:é9;ě9<í9<î7;ó6:ň59î68ę88ä;8Ü:5Ó9/Ë7+Ć8*Ă:*Â:*Ŕ;*Ŕ;*Á<)Á<+Á<)˝9$Á?)»:%ł5ą:'Á;/Ę:2Ř<=˙^g˙Pd˙A[ů8Wö6[ő;`ô>dăIe«?L?>_:2G;/:>08>2F<:R>=N0.S8/VK5ciExŹ[Ť°pťÂ}ˇĆšą}šą€š¶Ť©xrŽ^YuETp=\{B_@b†@iŤCm“Hl”Kk“LiMiMgŚGe‰Cc†Bc†DgŠJiŚLhŚNg‹MiŤOiŤMiŽKgŚGgŚFfŽEl’Im•LpśQoťRt U-1"-1#,/&,.)//-11/34/45-9;0<>1@B7EHAKMLQRWUUaVWi\Yt^]fdŚpn zx·…ÎŤ‹ßęŽďŽŚíŠ‰ç‡„ßÓ|Ë}Č€Æ…ÇĆŠŠĘŚŠÉŚŠÉŚĹŚ„ż‹‚·‰€­†|ˇ€vŹ{p€ujpndbi_Vc]M_aL_aKc^Jg\Jl[In\Ho]Gq]EwaIxX?‡S=ŁVDÇXOăUSřOTţMSâ><Ţ<7Ý84Ţ71á6/á6.Ý7+Ú7(Ô7&Ó8$Ó8&Ő8'Ř6)Ý5*ŕ3,Ý5,Ë3(Ä6(Â9)Á<+Â?-Â?-Á<+Ŕ8(Ă5'ÜJ=ÚF:ŐA5Îĺ84â62ŕ72ŕ72ŕ72á85â96ä;8ä;8á77ä88ć8:č8;ë8<ë8<ë8<ě8;ď58î47ě57ç77â:7Ů:4Ď8/Ç7,Â8+ż:+˝;+»<+»<+»<)»<+»<)¸9&˝@*ł9$±6$¶;+µ0'Ă40ěPT˙[i˙J`ő:Uđ7Vń@]ďHfçHdÍNa‚89e?6R@2DA09=,57)>6+K9/E-#R=,g]B_’«t—»{–Áz–ż{”łxś¸…š´…€šm\uKHb5Nh8[xB_}?c@hŚDl’Gj’Ih’JgLfŹKd‰Dd‡Cc†DeFh‹KkŽNkŹOjŽNiOiOiMhJgŹHh’Hl”Ko™MsˇVsŁWv¤Y24&04&01)01+12-23.34.46+9;-;>-?A3DF9IJDMONRQWSS]XVd\Zoda~nl”zw¬„‚ĂŚ‰Ö‘Źă”‘ę“뎋≅قĚ|Ă|ż€~˝„‚Á†…Á‰ÂŤŠĂ‹ż‰…¸‡®…}¤…{ť€vŽyn~rgmmaak^Vi[Pc\J]`K]aJc^Jh[JmZKqZJtZIv[FsWAxR;ŠQ=ŞYFÍ[PăTPđFIń>AŰ2/Ü5/á51ă60ă5.á5+ŕ6)Ű8)Ö7$Ó8$Ń8&Ô9'Ř6)Ü6*ŕ3,Ú6-Č6)˝8'Ľ7&˝:(ż>+Â?-Ă;+Ä8)Ć4'éUIÓ<1Ě8,ŃB4ľ5%Ŕ=+Ŕ=+Ŕ8(Ä8'Ĺ9(Ç:)Č9)Ë7)Ě6(Í3'Ň6*Ô3)Ő2)Ű4,ă:3é>7ě?9ě=:č45ě59ď6;î5;ę2:ć39ä7=â<@óUVübb˙mj˙idóVQäA<ß63Ý4/ß61Ţ71Ţ71ß82á85â96ĺ97ć:8ĺ78ć89č8;é9<ë8<ë8<ë8<ě7<î5:î68ę67ĺ97ß<7Ö<4Ě9/Ĺ9,ľ9*ş;*¶;)µ<)µ<+´=)ł;*ł<(ł:'·@,­8&­8'ł;-«+"Ă54ý`g˙TdúG\đ-=@-@C.DG4IK>MNFQPNRRRVUSYX]a_lkius›€}˛†ÇŚŠÓ•’ᔑâŤÜ‹‡Ó„Ä}ş|µ|ł~·„ş‡„ą‰…·Š„˛†¨z›€xs‡znzrfjm_^j[Ti[PjZKfZJb_Lb_Le^Li\Kn[Lr[Kw\K{ZG~UA†R=›RA¶VFÎRHÚG@â88ĺ33Ý1-ß3/â5/ă5.á3,ß3)Ý5(Ü9(Ó8$Đ9$Ď:&Ń:'Ô8)Ů7*Ý5,Ř7-Ä;+ą:)µ6%·8'˝;+Â=.Ç;.Ç7,Đ90ő\TÖ=5É5+ĎA5¸3$·:(Ľ?+ż:)Ă:*Ć:+Č:,Ę;-Ě:-Ď8-Ń7-Đ3*Ň1)Ö1+Ţ71ĺ<7ę>:č;7ć74é75ď;<đë8>č7=ę7;î7;î79ę7:ä::ß=:Ö=7Ë;2Ă;/ş8*µ:*ł;*Ż<)Ż<*¬<(¬;)¬;)Ş;(¬=*¦8'§9*Ş8-¬/)Ë@C˙hrôM^óH[íDYĺH[ŮO^ÉT]·TWśXUdM?OL9LG4JE1FD-?B'=D#>H#KV.\g.==1==3==5;<4;=2;=/?B/@D-CG.FJ3KL:NOAQQIRRHYWJZZN_^\gerqn‰{yˇ€·‡…ÄŹŚŃŹŚÓŤŠĎŠ…Çş~yŻzw¦yvĄ{v¬|y®€|®}©}Łx—|t‹zrvksrffm_\j\Sk[NlZLl[Kk[Ki]Mg^Mi]Mj]Mn]Ms^Mz]MZI“`O›VG§N@¸J=Ĺ@7Ď6.Ř1+Ţ1+á51á6/â5.â4-ŕ2)Ţ4'Ü6&Ú;(Ń9$Í;$Ě;&Î;'Ń:)Ő9*Ů7,Ó9-Á>,´;(°5#˛5#»9)Ä<.É;/Ë7-Ů?7ř[TăIAÎ:0Ě@3¸6&¬3 µ<)Ľ9'ż7'Ŕ7'Ă7(Ĺ7)É7*Î7,Ń7-Ů<3Ű:2Ţ93ĺ<7ę>:ę=9ç85ă41č96ë<9ě::č8:ě>@řPP˙bc˙ppůheâTPĘ=6Ŕ1+É40Ö;7ß=:ŕ;7â;5á:2â94á83ă73ä73ĺ55ć66é69é69ę7;ë8<ë8>ę7=č7=é6:ď8<ě89č8:ă;:Ý>:Ó>7É<2Ŕ8ŰTXűepîK\ďI]éJ\ŰM[ÉQZ´VV ZRŠ`RPI7HM9NI5H@+;567>O%Lf6lŠTw”\„›e‰šdŤ›fŁk”­t°|•mn}\Q`Cg{HiFl‹Gm‘Gj“Gf’GbGcŽGhŤJlŹMo’Pn“PlŹMiŽKjŹJj’Ls›TsťUsťSržQržOs˘Rw¦Uz«Zv¦ZuĄ[sŁYRK;RK;RJ=RJ=RJ=RJ=RJ=RK;UNXQ?YR@[TD\UE\UEb[HaZHaYLbYRe][kaiqftuj{zn„|ps‹s‰|p„uj{mbre]jc`k`_g_\c_Z^^ZY^YUaZRbZOe[Og[Ki\Kk\Ik\Gm]Fm]Fj^HicMqfTqZJuOB‹WJś\PŁYL¬ZLĄN=¦E4§<,±7(Â8-Ń80Ü41Ý1-ß6/Ţ7.Ţ7.Ţ7.Ţ7.Ű7+Ř6)×7)Ő8)Ô8)Ň9)Ň9)Ň9+Ň9+Ô8+Í;,˝;+¶>-®9(©1 «2!·9*ŔÔH9Â=,·2#ÝPFŘJ@ÔF<ÔD;ÚG?ŢG@ŕC>ŕ<:ŕ74ć87ě::ě::ç77â64ŕ93Ü=7ŮF>ĎB9Ä:/Ľ4(·1&ą5)ż;/Ă?3Ă:0É<3Ň?7Ú?:â=;ç;9ě89í9:ç7:ĺ8:ĺ8:ĺ99ĺ97ĺ97ĺ95ĺ95ŕ40á51ä65ĺ76ć89ç9:ç9:ç9;ë;>ć9;ß99Ü=:ÖA=ÍB;ÁA6·?1©:)Ł<+ =*›<(™:&–:%—8&‘8&A0‹<-‡/#‘/&ĽLJämoçloÍVZ˝LNµNOŞQM˘UO›XO—]R•aTŠjSWZ/:M7FTa)~ŽP›°oźĽyś˝x’µs~ťah€L\mCYdBT]BHP9?D0:=,@C2<@1:>0]sBgHpŹKo“Kl’Gj’Ii“KgJlPm‘So“Sl“Pm’Mk“Jm–JmIqśLqžMrźNrˇPtŁRv§UyŞYzŞ^y¦cm™ZbŽOWN?WN?WN?WN?WN?WN?WN?WN?XO@YPAYPA[RC\SD]TE^UF_VGd[JcZIcYMcYOf[Uj_]nbdpdhreltgpvirvirsfmm`gg[_bXY^[VZZRYVOWTKXTIXTH\VH^WGcZKcZIf[Ig\Hi]Gj^Fk_Gh`IcbMngTv\MSFšVM«VO°RJ·QEŻH9¬C0¬=,±9)ľ8,Ë80Ő62Ů40Ý6.Ü8.Ü8.Ü8.Ű7-Ú8-Ů7,×7+×7+Ő7+Ő7+Ő7+Ô8+Ô8+Ô8+Î:,ľ<.¶>.®9(¦1 §2 ˛:)ą>.ľ<.Ć:-Ç4*Ë4)Ň;0ÜE:áOBßQCÝOCŰF?Ř@;Ó<5Ň;4Ö=7Ú=8Ü86Ý33ç77ë78î79ë78ç77ĺ97â=9ŰB:ĘA7ż?4¸:,±6'Ż5&˛8)¸=-ż@1ĹA4Í@6×@7ß=8ă:7é77ď56ń7:é6:ç6<ç7:ć89ć87ĺ95ĺ95ă:5â92ă:3ă:5ä;6ä;8ä;8ĺ99ĺ99ä:;ă;;ß<=Ů?=ĐA;Ä?6·=2¬;-Ą;+ =*ś=+—<)–;(’:&“8%Ś:%€9'€8)‰5*ť<5żQP×eeŃ^a·MM¬NL¤TMťYP\Q—^S—^S_VŚfQ`_/Sg*^r3zO™ŻnĄľ|™˛p bj‚HauBTe;JX7FP7?H59?158-:^tEgHqMp”Ln’Hk”Hk“Lh‘Km‘Qn’Tm”Sm”Qk“Lk”Hl—HmHpťJpťJp Ls˘Qu¦Tx©Xx©Xx¨^m—Xb‹QY‚H]TE]TE]TE]TE]TE]TE]TE]TE[RC\SD]TE^UF_VG`WHaXIaXGe]Je]Hd[Jd[Le[Qf[Uh]Yh][i]]j^^k__k__i^\f[YbWS_VQZVMWTKUQHRNCQMBSOCWQCXRB^WG^WEaYFc[Fg\Hi^Hk`JhbLbaMngUy_PXN˘[U±VQ˛IE¸E@ą@7¸>1·;/¸8+Ľ8+Ă9,É;/Ń:/Ů8.Ű7-Ű7-Ű9.Ú8-Ů7,Ů7,×7+×7+Ő7+Ő7+Ô8+Ô8+Ô8+Ň9+Í;,Ŕ>0¶>.­:(Ł2 Ł3Ş:&ł>,ą>.Ä?0Ä8+Ĺ2(Č4*Đ<0ŘF9ÜM?âNDŮ:6Ř43Ô20Ö42Ű97á=<ć<<é;<í9<î5:ë27é26č58ĺ99á<:Ů@:ľ8-µ:+±6'®4%­5%°8(µ<+»=.ČD5ĎC6ÖB8Ü=7â94č64í55ď79ę69é69é69ç77ç77ć95ĺ95ĺ:3ă:3ä;4ä;6ă<6ă;8â:9á99Ţ88Ý9:Ü<<Ú@>ÓB=Č?9ą:1­7+Ą7(ˇ;,ť<+™<+”;)“:(9&7%Š9&z6#y6%9, F=ąSOŔWT¸PO«IF˘MHśSL™YP—^S—aW—aW_V‹ePsrFzŤV“¦n¤ą€«Ŕ‡žł{|[[oaXIaXIaXIaXIaXIaXIaXIaXI_VG`WH`WHaXIbYJcZKd[Ld[Je]Hf^Gf^Ig_Lg^Of\Pf\Rf\Sh]Wh]Wh]Wh^Ug]Te[QdZPc[N]YMZVJVRFRNBPL@PM>TN@UO?WQAYTA[VC]XDaZGd]Jf_LfaMdcQleUv\OŠ[Q©b\¸ZX¶FDą<:Ă<8Ć;6Ä:0Â8-˝8)Ľ:*ľ?.Ç?/Ó9-Ů7,Ú8-Ů9-Ú8-Ř8,Ř8,Ő7+Ő7+Ô8+Ô8+Ô8+Ô8+Ô8+Ň9+Í;,Á?1¶>.¬9'˘3 ť3˘8"¬=)µ@.Ľ?-ż:+Ă7*Ĺ5*Ë7+Î:.Îî7<ě38é06ć25ĺ58á77Ú65Î61·5(Ż7'®6&¬7&¬7&®9(˛:)µ:*ĂA1ĘB4ÔA7Ü?8â;5ć95ë76î87í68ë78ë78ę86ę86č94ç:4ć;4â92á:2ß:4ŕ;7Ţ:8Ü:8Ű97Ů99Ö<:Ő@<ŇC=ĘA;»;2Ż6+¤6'ž7(ś;*;)”;)’;(Ź:&Ť8$Ś7#‡9%€=*v5#5'™E:ŞPH¬MG©LG¦QLťPJUL”ZO”^R•aV•aV—aWŤgT‰…_™©x­˝Ž¦¸ˇts†YN`8/A->3B%:F.=F3;A3:=2:=4<=599-?@2<>39<1<@29B/?L2PbNK:NK:NK:PMTQ@VS@XWC[ZF]\H^]Kb`Qf^QmWJ†[R¬icľc`ÁMMČBCĐ;=Ô89Ó84Ě70Á9+ş=)µC+ĽB+Î;+×7)Ř8*×:+Ů9+Ö9*Ö9*Ô8)Ô8)Ô8)Ô8)Ň9)Ň9)Ň9)Ň9)Í;,Á?1¶>0«:(ˇ6"š4›7 Ł=&ŻB-ł;*ş;,Â:,Ç9-Ë8.Î:0Đ<0Ů:4é9<ě4<ć39ă28â38á48â38â25é6<é49ç4:ç7:ă9<Ű76Đ21Ŕ0(´6(­:(­:(«:(«:(«:(¬9&Ż7&¸:+Á;/Î>3Ř?7ŕ=8ä;8č96ě87î66î66î66í74ę84č:3ç:3ä<3á:2ß;2ß<5Ü=7Ű=:Ů><×=;Ô>=Ď@<ËB<ĹB:ą=5­7-Ł5(ž7(š:*–;)“<)‘;*Ť:(Š9&‰8%7$8%@-u4"{3%“G:¤QIˇNFžNGˇWNUL’XLZNŤ\NŹ]R”`U—aWŹhWŤ‰fĄzť©€Žj]kHDS42@&$2-:&0;*5=.9?3?1;=28;0;?18A.>K1PbÜ87Ô70Ç;.Ľ?+±C*µA(Ę;*Ô7(Ő8)Ő9*Ö9*Ő9*Ő9*Ô8)Ô8)Ň9)Ň9)Ň9)Ň9)Ň9)Ň9)Í;,Á?1µ=/«<)˘9$™6•5›;"§A*˛B.ą@/ż;,Ă7*Ĺ2(Ë4+Ň;0ß=8ę5:î3<ë6=é8>ç:>ä;>ă:=â9<ß58á7:â9>á=>Ű=>Ń96Ă2/¶0'Ż9+«>*«>*©>*Ş=)©<(©:'«8%Ż6%ą7)Ĺ;0Đ=3Ů<5ß<7ä;8č;7î87ď75ď75î85ë:4é;4ç<4ä=4â>5ŕ?7ŢA:ÚA;ŮA>ŐA?Ń@=ÍB?Ă?:»@8ł=3Ş8-ˇ5(›5'8(•<*‘<(Ť<)Š<(:&†9'„7%6$6%}:)t3!~9*—OAŁYNśRG•OEVJ•YN[MŽZMŚ[MŽ\Q“_T™`W‘gW~xXy^r|ZU`B;E,/;%0;*0<.3=24<16<2?1:<17:/:>07@-=J0Oa;bxIk‡Lu”Qu™QtNqšNršSpšRršQsśPr›Op™Kn™Jp›LsžOvˇRyĄX{§Zz¨]x¦]qźWg”O]ŠEX‚B`‡NY€ISzCg^Og^Og^Og^Og^Og^Og^Og^Og^Og^Og^Oh_Ph_Ph_Ph_Pi`OjaPjbOjbOjbOi`Oi`OiaNiaNiaNh`Kh`Kh`Ih`IhaGhaGhaGf_Le^Kb[I\WDWR?PM:LI8IH6IH6GH6GJ9GK:HL;IMě;Aę@CćAEŕBCÜ@AŮ??Í12Đ66Ó:<Ń=;É;9ľ71µ4.­4)Ş<-Ą>+Ą>+Ą@,Ą>+¤>(¦;'©:'Ş7%˛7'Ľ8+Ĺ8.Ě70Ô94Ü=9ć>;é:7ě95ě95ë:4é;4č;4ĺ=4â>4ŕB7ÝC9ÚC<ŐB;ĎA=Ę?<Ç=;ż>9±<3©:/˘8+ś6(7'”8)‘:)Ť:(‹=)‰<*‡<):'‚9(€7&6%}6$x3#x5%…B2UEťWK”PCPD•ZL“YMZN\Q\Q’[T•\U[VŽbUkbEV`=JS4=G,6?*2=,4>35?66=68?8=B;CF=EG/;9*78*=>0:<17:/:>06?,=J0N`:awHk‡Lu”Qu™QtNr›OršSpšPq›OrťNqśMp›LošKrťNuˇTyĄX«`~©azĄ]qśUf‘L_‰G]‡E^Ib‰PY€ISzCh_Ph_Ph_Ph_Ph_Ph_Ph_Ph_Pg^Oh_Ph_Ph_Ph_Ph_Pi`Qi`QmdUlcTjaRi`Qh_Pg^Og^Mh`MiaNiaLiaLiaLh`Ih`IhaGh`IgaKf_Le^Kb[H\WDUR?PMSQDSPAXLÂYSŮROŕDEŕ::Ü75Ő;3Ę>/ş=)µ8"É:)Ď8'Đ9(Ň;*Ň;*Ň;*Ň;*Ň;*Ń:)Ń:)Ń:)Ń:)Ń:)Ń:)Ń:)Í;,Á;0¶:.­<,§>+™9#Ź3“7ź?'Ł9#°;)Ŕ>0Ć<1Ě8.Đ7/Ů<5ă?=ě=Bî?DéCEâDCŘ@?Ę;7Ŕ50»1.ą/,Ľ51ľ:6»<6ł:2Ş8.¤8,ˇ:+ˇ=-ź>+ ?,ˇ@-ˇ@-ˇA+Ł>*¦=*©;*­:(˛7(ą5)ż5+É90Ô?9ŕC>ä=7č;5č;5ç<4ĺ<5ä=4á>5Ţ@5ÜD9ŘD:ŇC;ĘA9Ä?:Ľ;6¸85°93˘7-™8(•5'“6'‘:)Ź<*‹:)…8&=*…<+<*€<)~:'|8%{6&{6&y1#€8*ŚG8–SC’PB‹L=RE™_S‘WL’YN”[R–]T–]V—\V–YTŚ^QaX;EM(6@8A&>G26BE*;:&78(=>09;069.:>06?,F7GK=IK=AE4KJ6\Q;aE/J9Ŕj]Üg`ăUSäFEĺA@ŕA=Ő>5É=0Ć=-Ć4%Ë5&Ě6'Í7(Í7(Î8)Ń;,Ó=.Ň<-Ň<-Ň<-Ň<-Ń;,Ń;,Ń;,Î<-»2(¶7.±;/¨:)š7"”5•7ś9"®A-ł;+ş6)Á4*Í6/×<7âC?ęHEčDEčEHÝCCÉ:6»61´80®8.©4+«9/©9.¤8. 8-›9,–:+“<+’<+–=+=*™>+š?,›@-ťA, A-Ł@-˘;*Ą:(«7(°6)¶6+ľ8-Ä;3Ě<4Ú=6ŕ=6â?8âA7âA9ŢB6Ú@6×@5ŘH=ŇE;ÇA8»;2˛5/Ş3-¦1*ź2+™9-’;*‘9+Ť:*‹:);)‡;+…<+€9'}8(|9(|9(|9({8'y6&x3$w- ‹A4—OC“MAŽL@‘QE“UJŽRG\R\R‘UMTL—ZU•XS”WT“gZ]T5IQ*AK(BK,>H/7@+2=-4?14<1HPCZ^PW[JMO:DF.AA'==%:;)>?1;=2:=2;?14=*;H.PbF7>F7=E6@F8>E5>F1HL5MI0U?'Q:ĽsbËcXŕ_ZëUTçGGâ@>ŕA=Ř?9Î:0Í9+Î=,Ď=.Ď>-Ď;-Î;+Í9+Í:*Î:,Đ:+Đ:,Đ:+Đ:,Đ:+Đ:,Í;.É?5ĹB:ĽB7®=/ź6#–1–1›2¨5#´6(Ă;/ŃA8ŢE?ćIDéJFęKHŢCAŰEDĐB>Ľ<3Ż9/©=0Ą?1ź=0ź?1ś>2—>0”>/Ź=/Š>.‡@.?,Ž=*‘;*’<+“=,”?+•>+—>,™>,ź@.ˇ=-¤:,¨8,¬8+±8-¶:0Ľ9/Ę<2Đ<2Ó?5ŐA7×C9ÖC9ÔD9ŃE8ĘB6ĂA4ą=3°:0©6/¤5.ź4,™7,’:.Ś=,Š;,;+†:*„;*<*<,~;*|;){9)y:)x9(w8'u6'w4$€4'D7NC‘K?ŚJ>ŹQD’TIRG“SJXOUO—VPś]X”WRŽSOŠcT]W5Xc9_jBZfBIT66C)4@*0:<19<1<@25>+;H.PbMKD63Ĺ?3ĹA4ĂA3ÁA4¶;,˛:,Ş8-Ł7+ž6+›6,7.”:/Ť;/<,‡;-„;,‚:+€;+€;+<,};+z;*z;,w9*v8)u7(t6)v4&;1”F<–NBŽJ=H<ŤOB‘SH’RI’OI™TO™SQšVSš]Z“YUŤ[TŤl[}X~‰^€‹alxRLX46D#=J.LZ@eqYeoW]eMPU>EI0?C(=@#:<$89';<.8:/9<1=A36?,MK4Ń>4Ň>4Ň>4Ó?5Ó?5Ó?5Ó>7Ó?;ŃB>ËD>ĆF=ÂF<ĂI>ÇM@ÎPDŮSHÝPGâMGäIEäGBâC?ŕA=ŰB=ŐFBÉD?ş?7«=0ź>.šC0•G3”I6ŤF4ŚE3C3…B1€A0}@.zA.|?-‚:,†8,…9+†:,†:,‡;-‡;+;+‰:+‹:)Ž8)8*•9,™;/ť=/˘<0Ş:/­8.Ż9/°:.°:.°:,Ż9+®8*¨7)Ą7(ž6)›6*•7+’8-‘9/Ž<0<.…<-;,9*:*~9*}:*};+|:,z;,y;,w9*s8*q6(r7)u5)ŚD8’H=‘K?ŠH:‡I<‹OD“TK—TL–QL›TPQO”SO–\X_Xh^—~j””p…’fu‚WYh?CR+?O*P`B)=A(;='78(:;-79.9<1=A37@-NKB1=I1?G/LG1S>+g=-•ZL­_Sś>4ž3+¶?9ŘXUóigödeëRUćJNčNPŮIAÓF<Í@6Ë>4Î@6ŇD:ŐG=×G>ÚJAÚJAÝJBÝJBŢKCŢKCŕKDŕKEëSRčTTăSRÜROŘSLŘSLŰULŕUNŕLHáIFăEBâC@äB@âC@äEBŕFDŮJFĘE@ą>7¬=2ˇ?2B1’E3G4ŚE3C3‡B3@0?1~?0z?/|>/€:.9.9.9.;-;-;-;-9*‚9*…9+‡:*‰:+Ť;-Ź<.“;/›;/ž9-ź9- :,ˇ9,˘8+˘8*˘8*ź8)ś8)8(”8)Ź9*Ś:,‰:-‡;-„:-‚:,9+€8*}8)}7+|8+{9+z8,y9-x:-v8+q5*o5)q7+t9+ŚH;ŤI<‰I=„I;…K?ŚRF“WMXOQMśUQRP“VQ’`YbWg[†zdnrQZi@JY0AR(IZ0Wi?dvNj|VXiEL[:?L.:D)C-B47@-;H.L^8_uFi…Jt“PtPtNr›Os›TpśQnťMmžLpˇPw¨Y|Şaz¨`rťXi“QZA\„EaIeŚMgŽMgŽKhŤJfŠLc†PY{IRtBi`Qi`Qi`Qi`Qi`Qi`Qi`Qi`QkbSkbSkbSkbSkbSkbSkbSkaUlbYlbYlbYlbVlbVlcRlcRldQldQldQlcRlcTlbVlbXlbXlbXocUnbRkbQlcRlcRkbQg`Pd]M^WGYSCSM?MJ;KH9GE6EB3?B/;H.?H-OC-a=-E:«XR´NJž,+˛:9ą=;ÓSRňpnţvvńehëX^đ^_ăUQÚPFÔG@ĐC:ŃD=ÔG>ÖGAÖG?ŮHCÚJBÜHDÝJCŢJFŢKDŕKGŕKGâHHáGIÜHHŮGGÖHD×HDŘGBŰFBŕBAäABćBAčBBçCBäB@ßA>ŰA?ŰJGĘC?ą<6®>3˘@3A0A0ŹC3ŠA2@1‡?1…=1‚<0€<1=1=1;1;1:3;1;1€.™9)–9(“:*:+Š;,‡;+„;,‚:+‚:,9+7)~6({5){5){5)z6)x6*x8,w9,s8*p4)o5)r:-v<0‹M@‰K<„J<‚M=…QC‹VH’XM—WN“NI™TO—VR•\Ue\{^PfVF`^GEO->N)?O(J\2_rEk~QexKXj@M_7@Q-6E&7D*=F1=D4:>07;-8:,:;-68-8;0>B47@-9F,I[5[qBeFr‘Ns—Os—Mr›Os›TpśQmśLo Os¤Ux¨\z¨`s [i“Q`ŠHYB^†GeŚMgŽMfŤJeŤGgŚGhŚLa„NXzHQsAi`Qi`Qi`Qi`Qi`Qi`Qi`Qi`QkbSkbSkbSkbSkbSkbSkbSkbSlbXlbVlbVlcRlcRldOldOldMldMldMldOldQlcRlcTlbVlbVocUmaSkbSlcTmdUlcTi`Qf_Ob[K^WGXQARLĐB>Í@9ŇC?ŇC=ÔC@ŐD?×CAŘD@ŮECÚDCÝADÝADŢDFÝGHßIHŕJIăIIĺGHçACęADěBEěBEçAAâ@>Ű=:Ő=:ÚFFÉ=<ą83°;2¦@4?1‘>0Ź@1ŠG]/AV-7H$1@!6B*=F5PK8MH5ID1DC.@F*DD([B,TGŻ\X«>A¬/3ÎINĆ?EŔ;>»;<ÄFGŘZ[ăefŰY[ĚGHŃJGĐEBÍB?ĐB>ÔFBÔFBĐB>É>7ĎD=ĎD=ŇD@ÔEAÖEBŮECÚDEÝDFŢ?Cß@DŢBEßEEáGGŕFFŕBCá>?čBDé@Cč>Aç=>ä>>ăA?áC@ßECÖBBĹ98¶50°;4¦?6š>3=/Ť?2)=C5ă?>â@=â@=ŢC?ÓB?Ĺ>:¶93Ş70ź:0—=2Ž>3†>2?2?3=2;3„93†938192…;0„<.„<.;-;/€:.€:.€:.~:-~:-~:-~:-};/};/};/};/}90}90~80~8.€7.€7.€7.~8,€8,~8,}9,|8-{9-z8,x8,x8,x8.x8.w7-v8-v6,t6+s5*q5+m1'r6,n4)m3(o6+m4)q8-I=…SH…SJ†RGRH‹PHQJ”QK•RL“TMŤTK‰[N‚cQo_HYU:MV7O_;j~YbxQYoHUjCRg@Mb;IY5CR17D&4>#2:#6;'<>0@B5@@6<>358-39-4:.7:/9=/;?.=B,%Q?)kP=XG„NBE=“D?±WV¸TT»QQŔPOŔNMÁMMÂLJĂKJĐTTČHGÂ=>Ä=:ĐB@ÓEAĎB;Ç>4ĘD9ÉE9ËF=ÍG>ÔFBŘDDÜAEÝAEáBFáCDáCDâDEâDCáFDáFDáFDŕEAßD@ŕB?ßA>ŕA=ŕA=ŕA=ŢC?ÔE?ČC<ş>6¬:0 90”:/Š<0‚<0€A2~@3~>4<3:3†9181‰90‡;.…<-„;,„;,;-‚:,‚:,‚:,9-9-9-9-~:/~:/~:/~:/}90}90}90}90}90}90|90|90|90z:0z:0y9/y9/y9/w9.w9.w8/w8/t8.s7-r6,r6,p6+p6+k1&q7,m4)l3(o6+l3(p7,~H>„RI…SLQJ‹PJQL”PM•QN”SOŠOG…SH€[K{ePsiPhkLfqQgxT^tMXnGPf?La:K_:K\:GV7CP4>2;=04:03;05;16<0:A,>D*Q[9^mDkRpŚRp“Sq™RsťSsžVxź^|˘exžak‘T`†I]F]F\‚E`†Ia‡Jc‰Le‹NdŠMbK`†I^„I]LRtBIk9i`Qi`Qi`QjaRjaRkbSkbSkbSjaRjaRjaRjaRjaRjaRjaRjaRkbSkbSkbSkbSkbSkbSkbSkbSlcTlcTlcTlcTlcTlcTlcTnbTqdTo_Pm]Pm_RqeYog\ldYhbVfbWpl`{xi~{jyizubpiVe`JTT(MB.S@/U8(]7*l=3n70u91~;5‡@:’IB›PJĄXP­ZR»^YşSN·GEĽB?ČDBŃFCÓD@Ď@:ĐA;ĐC<ŇE>ÔG@ÖGCÚFFŰEFŢDFßCDßCDßCDßCDŕDEßEEßECßECŢDBŢDBÜD?ŰC>ÜC=ÜC=ÜC=ŰC>ÖE@ÎE?ĂB<ł>5˘:1”8-9,;-‚@2@1~>2=1:1†91‡81:0‡;.„;,„;,„;,‚:,‚:,€:.€:.9-9-~:/~:/~:/~:/~:/~:/}90}90}90}90|90{8/{8/{8/{;1{;1z:0y;0x:/x:/x:/x:/t8.t8.s7-s7-r6,q5+o5*o5*j1&o6+m4)k2'l6*i3'm7+}G=‡PI‡PI‹PJŽQL‘RM’SN”UP‘VPŚWO‡[P~^OtbNoiQorUlwYfwUQeBK_:EY6DU3EV6GV9GT8ER8?I0*7;*:<.<>0;=/9;.6<25=26<26<0:0=@-=C)MW5Zi@f|MlNn‘Qq™RuźUtźWzˇ`wť`n”Wc‰L\‚E\‚E\‚EZ€C_…Ha‡JbKdŠMc‰La‡J_…H]H[}JPr@Gi7i`Qi`Qi`QjaRjaRkbSkbSkbSjaRjaRjaRjaRjaRjaRjaRjaRkbSkbSkbSkbSkbSkbSkbSkbSlcTlcTlcTlcTlcTlcTlcTnbTrdWoaTm_Rk_SkaWlcZle[jf]jf]xti„x‘Ť•Ź’Ś~‰‚r}zg`cNOT>DG2FE1HC0F=,L<,VC4P9+R6*V4*Y5)\6+a9/e=1m=1ŠKBťSJ®UO¶NKĽFDĆABŇDCŘFFÔ@>ŐA?ÖB>ÖE@ŮECÚFDŰEDÝEDŢDDßCDßCDŢDDŢDDÝEDÝEBŢFCŰFBÚEAŮD>ŘE>×D<×D<ŮD=ŘE>ÔD<ÓF?ÉF>şA8§;18,Ś9+…<-@0@1~?0=/:1„:/‡9/:.„:-„;,„;,:+‚:,‚:,9-9-9-9-~:/~:/~:/~:/~:/~:/}90|90}90{8/{8/{8/{8/x8.{;1y;0y;0y;0x:/x:/x:/v:/s7-s7-s7-r6,q5+o5*o5*m4)j1&o6+l3(h2&k5)h2&l6*|F<ŠOGŚOJŤPKŹRMSN‘VPWPŚZQ‰]R…aUy_PiZGd_IdhO\fKN]@EU8AQ4=M0G2:A/9=,9=.9=.8<-7;-7=36=56;46<2:<1;=/bxIj†Ln‘Qs›TwˇWx˘Z{˘ar[e‹N\‚EZ€C\‚E\‚EZ€C_…H`†Ia‡JbKa‡J`†I^„G]HY{HNp>Eg5haQhaQhaQibRibRjcSjcSjcSibRibRibRibRibRibRibRibRkbSkbSkbSkbSkbSkbSkbSkbSlcTlcTlcTlcTlcTlcTlcTlcTrfXth\sg[mcYjaXle]snhzwp~wŤ† ť”¨Ąś«§ś¨¤™Łť‘™–‡z{kcfSOR?GJ7EH5BE2BE2EH5KL0A9,A7+@6*F4(S5*qB8ŚPHźPKŻIGżGIĚEIÓBEÖ@BŮ@BÚBAŰCBÚEAÚEAÜDAÜDAŢDDŢDFŢDFŢDFŢDDÝEDÝEDÚDCÚFB×FAÖF>ÔE=ÔE=ÓF=ÔE=ŐF>ÔA:ÓC;ÎE=ÁB9®>3ž9-‘;.Š.€=-;/„:-†:-‡;.„:-;-‚:,‚:,‚:,9+9-9-~:/~:/~:/~:/}:1}:1}:1}:1|91z:1|91y90y90x8/x8/v7.y:1y:1x90v:0v:0u9/u9/t:/r7/q6.q6.p5-o4,n3+n3+m4+j1(m7-j4*h2(j4*f2'j6+}D;ŤNGŹNHQJTLUOŽWPŠYR[Q{YMv\OkZJ]UBYXDY^HOYA?K3:H/7E.4B+5A+8D.=I3CL7EN9BK6?H5.5<,4:,5<45<56;56;49;0:-;@)GP1Ra:`vGi…Ko’RuťVyŁYyŁ[xź`n‘W`IZ}C[~D^G^G]€F`Ia„Jb…Kb…Ka„J`I_‚H^IW|ILp@Bf6haQhaQhaQibRibRjcSjcSjcSibRibRibRibRibRibRibRibRkbSkbSkbSkbSkbSkbSkbSkbSlcTlcTlcTlcTlcTlcTlcTlcTkaUndZpf\lcZkd\rmg„|‘ڧ٠µ±®ÄÁĽÉĆÁÉÄľĹŔşŔą±ą˛¨¤śŹ‚rfcTPQ?FI8>E3;D1:F28E38E38C2:B34(P:/e?6€EAˇPOżX[ĘQVĘCIŐFJ×EFÚFFŰEDÝEBŢDBÝD?ŢCAŢDDŢDFŢDFŢDFÜDCÜDCÜDCÚDCŮEA×FAŐF@ÓF=ŇE<ĐF<ŃG=ÔE=Ô?8Ő@9ĐC:ĆC9¶A7Ą=0–=/Ś=.†>/‚?/>.€=-<-;-„:-…;.‚:,‚:,‚:,‚:,9+:+9-~8,~:/~:/~:/~:/}:1}:1}:1}:1z:1z:1y90y90x8/v7.v7.t8.w8/u9/u9/s9.t8.s9.r8-r8-q6.q6.p5-o4,n3+m4+l3*j4*h2(l6,i3)f2'h4)e1&i5*{B9ŽMG’MH‘PJŹTLŤVO‡XNYO|\Qs\Nk[L`WFVSBPSBJQ?@I64@,3<+2;*09(09(2;*5>-9B1G4:F2:C25@/3>-1<,3:33954954928919;.;>-;@*CL/O^7]sDi„Mp“SwźYyŁYwˇYqYf‰OZ}CX{A\E]€F]€F^G`I`I`I`I`I_‚H^G]€JV{HJn@Ae7haQhaQhaQibRibRjcSjcSjcSibRibRibRibRibRibRibRibRkbSkbSkbSkbSkbSkbSkbSkbSlcTlcTlcTlcTlcTlcTlcTlbVj`VjaXkdZkg^upj…€źžš°°®ĂÂŔÓŇĐâáßçćäçăŕăŢÚŢÖÓŮÎČÎĽ˛¸Ą——‡zym]_YIKL:@E1;D/>J6=H7=F5;C4;>5:;3983880==3C<2N71a84HHŻ]_Ĺ`dĘWZÍQSÓNO×MKŰKJŢHGáFDăDAâBBßCDŢDFŢDFÝCEÝCEÜBDÜBBŰCBÚDCŘD@ŐD?ÓF?ŇE<ĐF<ĐG=ÔE=Ů@:Ů@:ŃB:ČC:ĽC:¬@4™;/Ś9+‰=/>.>-€=,<,;,;,„<-‚:,‚:,‚:,9+9-9-~8.}9.~:/~:/}:1}:1}:1}:1}:1}:1z:1y:1x90x90w8/t8.s7-r8-t8.s9.r8-q8-r8-p7,p7,p7,o6-o6-n5,n5,m4+l3*k2)i3)g1'k7,h4)e1&h4)b0%f4)zA8MG’MH‘PJŽULXNYOxZOr\Nk^N^WGRPAKN=CJ:9B12:+08)17+17-06,/5+/5)08+2:+4<-9D4;F5=H7-.800621622717829;0:<.:?)?H+KY5[qChLq“Vwž[wˇYsśVgŤP^IUx@WzB[~F[~F[~F]€H_‚J_‚J_‚J_‚J^I^I]€H]€JUvIJk@@a6haQhaQhaQibRibRjcSjcSjcSibRibRibRibRibRibRibRibRkbSkbSkbSkbSkbSkbSkbSkbSlcTlcTlcTlcTlcTlcTlcTlbVpg^ng_lgatqj‡„ˇ śĽĽşÎĐĎĎĎĎŕŕŕňňňúřůű÷ö÷óđńěéđâßčĐĆÝÁµÁ«ť˘‘}ub^\GKM7CH2AI2AI4@G5BD7AA7B=9C97?:69<5>?7B71J.+d66ŹQRłgiĆnmŔ\\ÇZWĐVS×QNÜLKâHHćCDĺBCßCFÝDFÝDFÝCEÝCEÜBDÜBBÜBBÜDCŮCB×C?ÓD>ŇE>ĐF<ĐF<ÔE=ÜC=ŰA9ŇB:ĘD;żE:°A6ś:-‹5&ŠD69A208+/7*6<247058157247025.06,08-19,6A3:E5=J9>K9-,6..400511606718:/:<.:?+WzB[~FY|DY|D\G_‚J_‚J^I^I^I]€H]€H]LStIHhA?]7icSicSicSicSicSicSicSicSicSicSicSicSicSicSicSjcSkbSkbSkbSlcTlcTmdUmdUmdUmdUmdUmdUmdUmdUmdUmdUldWibZgd]vsn“’ŽŻŻ­ÄĆĹÚÜŰęîďóôöö÷ůüüţ˙˙˙˙˙ý˙űúü÷ôýňě÷áÔňŘÇŕĘµĆ¶źŞ ‡„ibbFEH-DH/CG0FE1GD5F<3C60F42H:9>=9>?:B:7I75cGF’jjĹ“’č­©Ň‹‡ÇtnŔ]XĆSPŐONßJLçFKéFKßEGßIKÚDFÔ;>Ř>@áGIŕDGÖ:=Ú@@Ú@@ŮA@ÖB>ÔC>ŃB<ĐA;ŃA9ŢC>ŰA9Ó@9ĘA9ľB8°@5ź:.8*Ś=0‡>/>.>-€=,<+=*<,:+9-9-~8,~8.|8-|8-|8-|90|90|90|90z:1z:1z:1z:1y=5w<4w;3u:2t91q8/p7.p7.o6-m7-m7-l8-m7-l8-m7-m7-i2+m6/p92o81k4-g2*g2*h3+e0(i7.d2)]-#`0&_/%b2(s>6ŚOJŹPKQJRJ}YMv_QfYITN>DE5>E5:C25@04&DR1WlCh‚Rs”]wž_r›Wk”R[~DXyDTu@Tu@WxCZ{F[|GZ{F^JbNbN^J\}H_€K`L^~MTsJFd@2I:3L95K<9?:7BA?MHE]RPyjgŁŽ‹Î˛®čĹż˙×Ń쳪͂|Ŕc^ĘVVŘRSßHMÚADÖDEŐEEŘHHÜJKŰEFŐ<>Ö<>ŰACŮ??Ů??ŮA@×A@ÖB>ÓB=ŇA<Ó@9ÜA<Ű@;Ó@9ËB:żC;±A6 ;1’8-Ś=0‡>/>.>-€=,<+<,<,:+9-9-~8,}9.|8-|8-|8-|90|90|90|90z:1{;2{;2z;2w<4u<3u:2s:1r90n8.o6-m7-m7-l8-l8-l8-l8-j8-l8-l8-k6.n70o81n91l7/i4,g2*e3*c1(d4*_/%^.$b2(^.$`0&p>5JE‡RL‡XR~XOrVKdRDQJ:>?/:A16A05@03>04<14<15;17:379477577557246116//6./7,.9+1<.4A08E49H58G44E24B1.8/.5..3--2+23+46+8:-9=,7?(DQ3WlEh‚Ut•`wťbpYgŹPY|DWxCTu@Tu@VwBYzEYzEYzE[|G_€K_€K\}H[|G^J^J^{MSnKF_A9R4icSicSicSicSicSicSicSicSicSicSicSicSicSicSicSicSkbSkbSkbSlcTlcTmdUmdUmdUmdUmdUmdUmdUmdUneVneVlfZed_y{x—™˛¶·ÍŃŇäéěôůüú˙˙ű˙˙ü˙˙ýţ˙ţţţ˙ţü˙˙ű˙˙ú˙˙ôţüçúůÝďđŃáĺÄŇŘ´ĽÄź ¨‡‘l`gEQX7DG,DC.JD4LD7KA8I>:C;9SJKia_wvś’ľłŻŮÎČęÜÓţčÝ˙óé˙äŰđ©ŁĂecµBEÇDIÚRVŰUTŃMKĘDAÍCAÔFE×EFŘDDŮCDÖ=?×>@Ř@?Ö@?ÖB@ÓB?ÓB=ÔA:Ů@:Ů@:ŇB:ËE<ŔD<˛B7ˇ<2“9.Ž<0‰=0>/>.=-~<,€=-€=-9-9-~:/}9.}9.|8-{8/{8/}:1}:1{;2{;2{;2{;2z;4x<4t;2q;1r90o9/n8.l8-l6,k7,l8-j8-j8-j8-j8-i9-j8-j8-n91m80m80m80m80i7.f4+c1(d2)a1']-#_0&c4*\-#_0&sD<}PJVP~YQpTI^J?OC7?<-46(2;*0;+1<.2=/4<14<15;17:379479668357257227007/08-.9+0;-2?.6C27E48G48G49F54>33:2/4-,2(/0(13(57*6:)6>'DQ5YmJk„Zw—fwśfl“Z_‡KWxCWuCTr@Tr@VtBXvDYwEYwEZxF]{I_}K]{I]{I_}K^|JZwKPgJAW@3I2icSicSicSicSicSicSicSicSicSicSicSicSicSicSicSicSjcSkbSkbSlcTlcTmdUmdUmdUmdUmdUmdUneVneVneVneVmg[jkf‡Ł§¨ąľÁĐŐŘĺíďóűýőý˙ű˙˙ű˙˙ü˙˙üţűýţůţýř˙ţ÷˙˙óřýćôýŢî÷ŘćňÎÝéĂÎÚ´¶Á™ˇ«†€‡efmLMQ6FH0HJ5KI:HD9D@7OGDj`_‹€¤š™·Ż¬ËĆÂŢŰÔęćÝúńč˙űń˙÷í˙ÝŐű¶±ĺŹŽĘcd°@?ĂPKÉTMÉPHĂF@ČGBÔMJÖKHĐ@?Ň>>Ó??Ő?>ŐA?ŐA?ÓB?ÓB=ÓB=Ö>9Ő@:ŇC=ĚE?ŔE>˛A9˘=5•;2Ž<1‰=0>/>.=/~<,€=-€=-9-9-~:/}9.}9.|8-{8/{8/~;2~;2|<3{;2{;2{;2z;4x<4r90o9/o9/m9.l8-k7,j6+h6+i7,i7,i7,h8,h8,h8,h8,h8,m;2l7/j5-i7.j8/j8/f4+a1'e5+a2(].$a2(b3)Y* a2(yNE\VvWRiNGWC:I<3@:.:;-8<.08)/:*3;.4Đ@?ŃA@ŇC?ŃC?ŃC?ŇC=Ó?;ŇA<ĐE@ÉE@˝C>Ż@9 >5•=3Ž<1‰<2=1=0=1~<.=/=/~:/~:/~:/}9.|90{8/{8/{8/|<3|<3|<3{;2z;4y:3y:3w;3n70m80l7/l7/k6.h6-h6-h6-i7.h8.h8.g8.g8.g8.g8.h8.k92h6/f4-g5.i70h70e4-b1*c2+b3+_0(]1(]1(V*!b6-{ULtYRaNHN?8A7.=6,;9-9;.9<139-3;.5;/6<06<07:/58-47.69049238139/39/2:/19.19,.9+.9+/:,0;-3>.6C2:E5Qn>Qn>Qn>Qn>Qn>Sp@Sp@WtD[xH]zJ]zJ_|L_|LZwGRlECU?6D7(6)gdSgdSgdSgdSgdSgdSgdSgdSgdSgdSgdSgdSgdSgdSgdSicSjcSkbSkbSlcTlcTmdUmdUmdUmdUmdUneVneVofWpgXpgXng]lnmz‚…“š «µ·ÄÎĐÜćčí÷ůôţ˙őţýö˙üú˙úű˙ůűţóřúíőöčńôăôýęôýčôüĺóůßňöŰéěÍŘ׹ĹÄĄ±°’ŹŽregOMP;GK:FM=GOBKQG{{sźš”Ĺľ¸ŘÎĚßÓÓçŰŰóëéü÷ôđďęüüô˙˙ö˙ýó˙ůď˙óę˙çŢ˙×ËŘ“¸eSŁJ:´TF˝WIµF;·@8ČKEÇC>ĘC?ĚB?ÎC@ÎC>ÎC>ÍD>ÍB=ŃB<ŇC?ÎDAĹD?¸A=Ş=8ž;5•<4Ž;3‰<2=3=2=1~<0=1=/~:/~:/~:/}9.|90{8/{8/{8/|<3{;2{;2z:1y:3x92w81u91n70l7/l7/j8/i7.i7.h6-g7-g7-g7-f7-f7-f7-f7-f7-f7-h70h6/g5.g5.g6/h70g6/f5.a2*e6.^2)\0'`4+a5,i=4uSJXG@E>6<5/95,;8/8:/57,36+69.5;/7:/69.69.58-57,36+28,19,28.19,19.19,19,19,.9+.9+.9+/:,0;+3>.6A17B2ex\uŤmt’lf‰_RwKBh9KkVsCZwG]zJ^{K_|L_|LVsCNg@ÉD=ČC<ÉB<ŃC?ĐE@ĚEAÂC=ł=9¦;5ś;5•<6Ś<3‡=4…<5=4<3~<0=1=1~:/~:/~:/}9.|90{8/{8/{8/{;2{;2y:3x92w81v70v70s7/n70j8/j8/j8/j8/h8.h8.h8.g7-f7-f7-f7-d8-d8-d8-f7-g6/h70i81i81h70i81j;3l=5g80k<4b6-a5,oC:xLCtI@nMDC9056.45/8918;247.14+25,58-58-57,46+46+46+46)37)/7(.9)08+.9).9+.9)08)08)19*08)08+/7*08+19,3;.4<-9B1:C0;B0:A1:<.68*24&/3$29'IU?cu[m„gf„bWyTInE?d8Hg;Li;Nk=Nk=Li;Li;Mj
      UrDYvH[xJ]zL^{M\yKQn@G_;6B4+4/#)%gdSgdSgdSgdSgdSgdSgdSgdSgdSgdSgdSgdSgdSgdSgdSicSjcSjcSjbUlbVlbVmcWmdUmdUmdUleUmfVngWogZnh\oh^mjekormtzŠĄ®łĂĚŃŐßáćîńôüţôýú÷ýůůţ÷ú˙ôü˙ňü˙ńü˙îýţđűřďţúń˙ůď˙úí˙ůé˙ňßďŕËÝηÁ˛›˘—|ubb_NXZL]cWmwn|†}¦¬˘ľŔµÚŐĎîäâüîî˙ôô˙ô÷ţôő˙ůůűúřűýřü˙úř˙őôüń÷ýń˙˙ń˙ůč˙ôă˙Ě»˝p›QD©SFµSH˛C:ŔJ@ÄH@ÇG>ÉF>ČE=ÇD<ĆC;ĆC;ÍG>ĚG>ÇG>ĽC;­>5ź:2—:3‘>6Š=5†=4=5=4€<3;0;1;1~:/~:/~:1}90|90{8/{8/{8/z:1z:1x92w81v70u6/u6/q6.m80k90j8/j8/i9/i9/i9/h8.g7-f7-g7-f7-f7-f7-f7-f7-f5.j81m;4k:3j92j:0m>4oC8l@5oD;d<2c=2zVJ†dZyWMbLA>:13814927<54:0/5+25,9<336+25*24)03(13(25*46+47,.6),7)/7*-8*/7*/7*/7*/7*19,08+08-/7,/7,/7,08-08+7?09B14*00!''heVheVheVheVheVheVheVheVheVheVheVheVheVheVheVheVjdVjdVjdXldYlcZmeZmeXmfVg`NjeRnkXol]he\feasqr‚†ŠŹ“–žˇ«ł¶ĂČËŘÝŕęîďö÷ůůűúüţýţţü˙˙úýţöúýňůüńúýňűţóůúňúúňůůíüúëţüçů÷ŢéĺĘŐÔ¶şą›|qtY_dMbkXr}l„‚‘›´ą˛ËĚÇăâŢňîë÷óňű÷öýůřüř÷˙ţüţţüţţüý˙úý˙úý˙úý˙úý˙úúü÷˙˙ú˙űö˙ęĺا Ł`X›D=µNEżMCÂF<ÄD9ĘE<ÎI@ÎKAËH@ÇG<ĆG8ĂH8˝J8łH8¦E5šB4‘B5‹C5†B5„B6‚@4‚>3‚<2„:1†91†9191~:1|91{80z7/z7/w7.v6-w7.w7.w8/w8/v7.u6-q5+n3+l7/i81i81h70h70g6/g6/g6/f5.g6/i70i81h70g6/e4-e3,h3-j5/m80k90j:0i;.i=0kA3gB2jI:dH:v_O~k\ŠxjŚoRJ=79.4:04:039/39/39/28.28.17-17-36-06,25,/5+14+/5+/4-.5-/4-.5-/4-/4-/4-/4-05./4-/4-.3,.3,/4-/4-/6.1;23=26@58B79D67B45@03>-4?.DQ=WeN[kQPbHEX<@T8BW6Ic>Ke>Kf=Kf=JeOj?UpGYtK\vO]wP]wRWqNHb?;O6&1+&+.$),heVheVheVheVheVheVheVheVheVheVheVheVheVheVheVheVheVheVjcYkdZlc\md[meZmfVidQjhSmjWjk[gg_lll~„“š¨Żµ¸ÂÄŃÖŮâçęňóőűüţ˙ţ˙˙ţ˙ţýű˙ţú˙˙úţ˙ůűüôřűňöüň÷ýó÷úńřúďřúí÷úéůűĺňőÚßâĂĚĐŻ´¸—‘–vkqUYaI_kUtoŚŠť§śżÁĽÓÓŃççĺóóń÷÷őűűůüüúűűůţţüţţüţţüţţüţţüţţüţţüű˙ţő˙˙ő˙˙ţ˙˙˙űú˙íęđżşÂ~uźKAŻM@ąK>ÂL@ĹK>ĂE9Ľ=4ľ?6ĂE9ÄF7ĂJ7˝L:łJ7¦F6™C2‘C6ŤE7…C7‚B8A7>5‚<4:3†91„93~92}:2z:1z:1y90x8/v7.v7.u6-v7.t8.t8.t8.r6,o5*m4+l7/i81j81h70i70h70g6/g6/e3,e3,g5.h6/j81j81j81k92k60l71k90k;1k;/j>1j@0iD2gH6kP=gRAufSueŚ…s†rKL<69.39/39/39/28.28.28.17-17-17-17-06,06,/5+/5+/5+/4./4./4./4./4./4./4./4.05//4..3-.3-.3-.3-/4./6/.80/:21=34@66B66B66B45B16C1CP>P]IR`IIW@AO6BP7EU:Jb@Jd?Ke@Ke>Ic6I5&1-',0$)-heVheVheVheVheVheVheVheVheVheVheVheVheVheVheVheVheVheVjcYkdZlc\md[meZmfVmhUkiTmjWjk[kkcwww‘’—§¬˛żĆĚŇÜŢěńô÷ü˙ţ˙˙ţ˙˙˙ţ˙˙ţ˙ţýű˙ţú˙˙úýţřúűóőřďń÷ëń÷ëô÷ěőůëöřęőřĺő÷áîîÔŰŰżÉĘ«°±’“–yuy`kpZt{iź§ś®µ­ÍĎĘŢŢÜďďířřöűűůţţüţţüýýűţţüţţüţţüţţüţţüţţüţţüý˙ţű˙˙ű˙˙˙˙˙˙űů˙úô˙÷íýŃĆŘž”L=ĄRB©L;§@1·G;ĎYMĎUJĽ@4ÄD7ĆH:ĂK=ąI;ŞB7ť?3—@7“E;‰@9„A9A8?6~>5}=4<4}=4z;4x<4w;3w;3u:2t91t91t91r90r90r90r90o9/n8.l6,k5+l7/j81l71j81k60i70i70h6/g5.g5.f5.g6/h70i81k:3k:3l;4j;3i:0h<1k?2jC4iD2fE2fI7lVAjYGujV†m‘Ž{€mEF658-28.28.28.17-17-17-06,17-17-17-06,06,/5+/5+/5+/4./4./4./4./4./4./4./4./4./4..3--2,-2,.3-/4..5.+5-+6.-9//;12>24@46B47D38E3?L:ER>CQ:;I28F->L3DT9J_@Jb@LdBKc?Ia=G`9F_8G`9RkDXpL]uS^vVZqTPgK=T8.A.&1-',0%*.heVheVheVheVheVheVheVheVheVheVheVheVheVheVheVheVheVheVjcYkdZlc\md[meZmfVniVljUlkWlm]pqi€‚žˇ¦ąľÄÄËŃŰĺçőúýű˙˙üý˙üý˙ţüýüűů˙ţü˙˙ű˙ţůűüôőöîîńćéďăçíßíńâđôĺńôăńôßňńÜęęĐŘŘĽÇÇ«˛±•ˇˇ‰‘“}“€śž‘Ş­˘¶»´ŔĹżÜÜÚééçööôűűůýýű˙˙ý˙˙ýýýűţţüţţüţţüţţüţţüţţüţţüţţţűűý˙ţ˙˙ţ˙÷ňďüóě˙üó˙űí˙ôäěñąp•P@˘O?¸XJ·M?µE9ŔG<ÁA6ČE;ĹI?˝G=Ż@9ˇ<4š=6–A<Ť?;‡@<@:}@;z?9y@9x?8x?8v=4v=4v=4u<3s=3r<2r<2q;1o;0o;0o;0n:/k9.j8-j8-i7.m82n72m61l71l71k60i70i70l:3j92h70f7/f7/f7/h91f:1j?6f>4e=1f>2hC3iE5fG3cG2cJ6hV@i\IskV…‚oŹŽzpq_9=,47,28.17-17-17-06,06,06,17-17-17-06,06,/5+/5+/5+.3-.3-.3-.3-.3-.3-.3-.3-.3-.3--2,-2,-2,-2,.3--4-*4,)4,*6,+7-.:.1=14@25A36C2:G5N4DXż@7ĆC;ĹF?żD=±>;¦:7ž:8™>;‘=;Š?<„@=}@=xA6s>6r=5r=5r=5p>5o=4n>4m=3l<2k;1j;1i:0i:0j:0n72p62p62m61m61l71i70i70l;4k:3h91e90e90e90e:1d<2fB6cA5cC4dD5dG5dI6bI3_I2^L6eV?jbMrmW„o„‡r[`L3:(28,28.17-17-17-06,06,06,17-17-17-06,06,/5+/5+/5+.3-.3-.3-.3-.3-.3-.3-.3--2,-2,,1+,1+,1+,1+-2,,3,,6.+5-*4+*4++5,.8-0;-2=/2=-5@/6A05A-1=)1=)5A-8F/=O5@T8FY=H\@J]?I^=K^>K`?TgG[pQcvZauZYkSJ]G6G4&4%$-*$),"'*heVheVheVheVheVheVheVheVheVheVheVheVheVheVheVheVheVheVjcYkdZlc\md[meZlfVjgTkjUlnYorasvm…„ꤍ¸żÇÖßäí÷ůű˙˙ű˙˙űü˙ţ˙˙˙ţ˙ţýű˙˙ű˙˙úüüôóőęëíâăçŮÝăŐÜăŃäéÓçěŐéíÖęěÔëëÓčćĎßÚÄÓθÓÎşĚƶËøĐÉÁ×ŇÎŕŰŘęćĺđďíóňđůů÷ýýűýýűýýű˙˙ýţţüüüúţţüţţüţţüţţüţţüţţüţţü˙ýü˙űü˙řů˙ţýţ˙ýů˙ýô˙řď˙ő÷˙öđ÷ç˙˙í˙îŰͧ”¤kX˘[GŞWE­L;ąD:ŔD<ŔE>ĽE?´A>Ş=:˘:9š;9•=<Ť?=…?=}@;wB5o?5o?5o@6n?5m>4k<2h<1h<1h<1l<2o83q62p62p62m61l71j81i81h70g80e90e:1d<2e?4f@5dB6^B4aG8cL:dM;cM8`K6]K3]M4]Q9bX?mhRss[€nv{eFO:4=*39-28.28.28.17-17-17-06,17-17-17-06,06,/5+/5+/5+-2,-2,-2,-2,-2,-2,-2,-2,-2,,1++0*+0*+0*+0*,1+,3,.5.,6.*4+*4+*4++5,-7,.9+-8*0;+4?/5@/4?.3?+3>-3@,7F/9K3@O8BT:GW¸J=ąI>¸I@´G@ŻD>Ą=:ś:7–=9Ź>:?9€A:xA:tD:pF:pE5j>5n=6o83r73p62p62n72l71j81i81d8/d90c;1d>3d@4cA5cA5_C5ZE4_NnlUtv^|kcmU2>(6B.3;.39/39/39/28.28.28.17-17-17-17-06,06,/5+/5+/5+-2,-2,-2,-2,-2,-2,-2,-2,,1+,1++0**/)*/)+0*,1++2+-4-+5-*4,*4,+5,+5,,6+-7,,6+/:,2=/6A17B27B15@04?.2?-4C.:G3=L5AO8DT:JX?L\BTbI\kTcqZ^mXUbPDRA2>0#/%&/,&,,$**heVheVheVheVheVheVheVheVheVheVheVheVheVheVheVheVjdVjdVjcYkdZlc\md[lfZlfVolYmlWjlWgkZflbr{xŽš¨±¸ĘÓŘăěńřý˙÷ü˙úű˙ţ˙˙˙ţ˙úůő˙˙úýýőřřîđňĺéëÝĺéŘăęŘĺíÖćěŇéíŇęíŇččÎčćĎčăĎáÚČŘĐĂŘÎÄÝŇĚęßŰříë˙ôö˙őů˙őű˙ůüţüý˙˙ý˙˙ýţţüţţü˙˙ý˙˙ýýýűţţüţţüţţüţţüţţüţţüţţü˙ţú˙ýű˙ýűýüúůýüř˙˙ő˙˙ó˙˙íýúó˙űű˙ú˙˙ô˙öć˙óß˙ăÍ×­•©oY¨WD®Q@¬O>ŻOA±OB­K@¤C<™<5•>7Ź?6?8A7{B7uD6qE8qE8p?8q?8s>8q?8q?8q?8q?8p?8sB;qB:o@8m>6l=5j>5m>6o>7o83q73o83m82m82i81h91f:1f;2d>3d@4bB5`C5]A3\@2WB1TG4[S>f^Gi^HbX?ZS9ZS7\W:ZV;XW;kmUsw^u}fUaI&28D03;.4:04:039/39/39/28.28.17-17-17-06,06,/5+/5+/5+-2,-2,-2,-2,-2,-2,-2,-2,,1++0*+0**/)*/)+0*+0*,1++2+)3+*4,+5-+5,,6--7.-7,,6+.8-2<16A39D69D47B46A10;+2?-5B09F22=/x=/t?/sB3tD6r=5r=7t=8s>8u@:u@:t?9q?8sA:q@9q@9q@9p?8n?7o>7o>7k92m82l:3l;4l;4h<3g<3c=2c?3^>1dG9cG9T=-N9(M8'@1WQ;PN7KI0OK2]Y>ieJeaDVU7XX<]`CosZz€fdlU=I1-9#2>*4-1=)2>*9E/BN8IU?O[GVbN]hWYdTLWI/u@8s>8u>9u@:t?9o=6p>7sA:q?8p?8p?8n?7o>7m>6n=6m>6k<4l=5m>6k?6j?6gA6eA5bB5dG9[A2^G7^I8N=+F7$G8%>6!LL4GK2DG,DD*MM1\[?baC`aBYY=dgJsw^sy_X`I:D,/;%7C/6>16<26<26<25;15;15;15;15;15;15;14:04:039/39/39/27127127116016016005/05/.3-.3--2,-2,,1+,1++0*+0**1**1*)0))0))0)*1*+2*,3+.5--4,.5-07/4;39A6?F>BJ?;F8:E57B14?.3?+6B.)r>)s@+tD0vE4q?4n<3q<4tB9sA8o?5qA7xH>n>4m>4m>4k?4l=3j>3l=3j>3g?5gA6gA6gC7eC7cC6`C5^D5bK;UB1ZI7`S@RG3C;&E=(FA+AE,BH.AE*<@%?B%NN2_`AijKaaEnqTvzagmSJO94<%4=(=F38>28>48>48>47=37=37=36<28>48>47=37=37=36<26<26<25:449349349338238238227105/05//4..3--2,,1++0*+0*).().().().().(*/)+0*,1+160/4.,1+,1+/4.6;4=BK:4.8/-4--4-,3,gdUgdUheVheVheVheVifWifWifWifWifWifWifWifWifWifWkeWkeWlcZmd[md]mf\mg[jiWkmXimVjoYjs`hthm|w¦łążĘĐÓÜăëđööű˙üý˙ý˙ţ˙˙ý˙ţůţţöýýńúűí÷úçô÷âîóÜĺíŐâčÎŰŕŔ×Ú»ÖÖĽŮ×ÂÝ×ÇŢ×ÍĺŰŮěŕŕřěđűđö˙őű˙ůţ˙úý˙űű˙űű˙ýü˙ţüţţüţţüţţüţţüţţüţţüţţüţţüţţüţţüţţüţţüţţüţţüý˙üř˙ţř˙ţű˙ţţţţ˙üţ˙ú˙˙ů˙˙řţ˙úý˙ůü˙ůů˙ýú˙˙ř˙˙řű˙őüţđ˙˙í˙üé˙ńß˙đÝ˙ďÚńŇ˝´z{R>tJ4uH3vI4zM8}P;yL7nA.e7'{M@sD:qB8sD:oC8g;0d8-g=1i?3i?3i?3g?3f>2f>2f>2e?2b@4bB5`C5_C5_C5[D4ZE4WD3XI6OB/\T?oiSc^HMK4IG0IK3>D*?H-@F*48>47=37=37=39?58>48>48>48>48>48>48>47<67<67<66;56;56;55:45:438238216005//4.-2,,1+,1++0*+0**/)*/)+0*,1+-2,-2,05/.3-+0*+0*-2.2718=9;B:@K=@M;@M;=J68E13@,1?(0>'4.:0,6..5.-4-gdUgdUgdUheVheVifWifWifWifWifWifWifWifWifWifWifWkeWldWlcZmd[md]mf\mg[jiWkmXinWiqZgs_drejyt…••ٞ·˝ČÎŇŰâęďőôůýűü˙ý˙ţ˙˙ý˙˙ú˙˙ř˙˙óţ˙ńűţë÷úĺđőŢćîÖâčĚŮÜżŐŐąŇĐąÔŃľÚÔČŕŮŃęŕßôéíţóů˙÷ü˙ű˙˙ý˙˙ţ˙˙ţü˙ţú˙ţúţţüţţüţţüţţüţţüţţüţţüţţüţţüţţüţţüţţüţţüţţüţţüý˙ţú˙˙ú˙˙ű˙˙ţţţ˙ýţ˙üü˙űü˙űú˙úř˙ů÷˙ú÷˙ýř˙˙ű˙˙űý˙úýţůůôî˙ţö˙ýô˙üó˙űî˙÷çűćŐéŇŔĎ´ˇ¶š…”uawVCjI6lI6rM;uP>mF7iB3gB2jE5nI9nJ:oK;oK;eA1cB1cB1cB1cB1cB1bC1`C3]F6\G6\G6ZG6ZG6WH5TG4RG3PH3NH2fdM~~frrZVX@JL4GM3A.:>-;>39?59?58>48>47=37=37=38>48>48>48>48>48>48>48>49>89>89>88=78=78=77<67<66;55:449338227105//4./4.-2.-2.,1-+0,+0,,1--2.-2.,1-,1-,1-,1-.210513764;4;G9=L9@O<@O:8F71?2-9-,6--4,,3+fcTfcTgdUheVheVifWjgXjgXifWifWifWifWifWifWifWifWldWldWlcZmd[md]mf\mg[jiWkmXinWiqZgs_bpcgxr‚””ٞ·ŔËŃÔÝäëđöőúţűü˙ý˙ţ˙˙ý˙˙ú˙˙ř˙˙ô˙˙ó˙˙ďúýčň÷ŕçđŐâčĚ×Ú˝ŃѵÍË´ÎËşÖĎĹŢ×Ńěâă÷îó˙öţ˙ú˙˙ý˙˙ţ˙˙˙ýţ˙úţ˙ůţ˙úţ˙úţţüţţüţţüţţüţţüţţüţţüţţüţţüţţüţţüţţüţţüţţüţţţýţ˙ýţ˙ýţ˙ý˙ţý˙üţ˙úţ˙ů˙˙÷ýýóýýőţýřţýů˙ţüţţţ˙ý˙˙ţ˙ţů˙˙ü˙˙űüűöó˙řň˙ýô˙ţń˙ýí˙úé˙ňŕ÷äÓçҿһ©ąźŽź„sŹrb|]K}^LtUCaD2W:(Y<*[>,X=*`E2`E2`E2_F2`G3`G3`G3]H3\K7\M:\M:YL9XM9UM8RL6QL6NL5QQ9ikS|€gmqXSY?FL2>G,48>48>48>47=37=38>48>48>48>49?59?5:?9:?9:?9:?99>89>89>88=78=78=77<66;55:4493382382/40.3/-2.,1-+0,+0,+0,+0,+0,,1--10.21/32/32/32-4-3?17F3=L7@O:?N7M:MJ9BC3=@5;A7;A7;A7:@6:@6:@69?58>48>48>49?59?5:@6:@6:@6;@:;@:;@::?9:?99>89>89>8:?9:?99>89>88=78=77<67<6495273162/40-2.,1-+/.+/.,0/,0/-10.21.23.23-12+1-+9*/@-6H2;M7=O7A6>A6>A6=@7=@79?59?59?5:@6:@6:B7:B7:B7;@9;@9;@::?8:?99>79>89>7;@:;@9;@:;@9:?9:?8:?9:?98?87=94;4382160/4..3/.3/.21.21,2.,2.+2++2*,4))6%$6)=!2F+8L1;O6;M5;M5;M58J29K3;M5H&DK*FM+EL*PV4PT3KN/HK,TT8bbFccIZZ@[Y@\ZA][B][B[Y@XV=US:SQ8HF1GD1DA0A?0@>1>>2??5>@5;>59?59?59A69A48C58C58C5:B79A69@88@58?79A69@8:B7:A9:B79@8:B7;B:E=8B:8B:7A88@56>14O<>P::L6@R8EW=FY=DX=@T;5H2+<),6+,3++2*ZgM[hN\hP]iQbjSckTglXhkXghVghVifWifWkeWlfXmeZmgYmgYkhYkg[kg\jf]jf[hfZefVhiWglVfo\erajwnzЉś«˛şČŃĐŮŕáéěň÷űřţţůţúú˙řűţóůýďřůë÷řćőôâíěŘéćÓĺâĎÜ×ÄŇ˹ʿ­Á´¤ą­ˇĽŻ¦Á¶˛ËÁŔŰĐÖćßćűóţý÷˙ţű˙˙ţ˙ţý˙üý˙üý˙üý˙ţţţţţţţţţţţţţţţţţţ˙ţü˙ţü˙ţü˙ţü˙ýú˙üů˙üů˙ýú˙ýů˙ţú˙ţý˙ýü˙üý˙ýţ˙ţ˙˙ţ˙˙ýţýűü˙ţý˙ţű˙üú˙řóříçňĺÝöčßűđę˙úý˙ü˙˙ýţ˙˙ý˙˙űý˙üý˙ţüý˙ýţ˙ýţ˙˙ý˙˙ţü˙ţő˙˙î˙ţč˙˙äúöÝýűâäŕÇ‹‡lRM0SN0QK+QK+UO/UO/TO1TO1SO2SO2QP4QP4QM2PO3PN5NQ6MO7JP6HM6EM5HQ6EN1BK.?I'BI(FM+JP,KQ-NR/PT1VZ7^bA_bCY\?TWWU>XV=YW>XV=XW;WV:XT9QK3OI3LG4ID1DA2B@3A?3>@5<=59?59?59A67B48C57D37D3:B79A69A68@58@59A69A6:B7:B7:B7:B7:B7;C874>63=40>-3C)9M*BY/Ga1Op;Ln;Li=Fa>BY?F;J@?I>>I9?G8>F7>D8=B;>E>9C;6@74B19I/BV3Pg=XrB^J[}JXuIMhECZ@9O:6G54E2XR6:@6:@69A67B47B46C26C2;C8:B79A69A69A69A6:B7;C8;C8:B7:B7;C8;C8=E:>F;=G<=G?>H=>I9?G8>F7>D8=B;=D=9C;6@74B19I/DX5Ri?[uEZ{FWyFTqEIdA=T:2H3/@.->+6H2@R8L_CReGSgKOcG@W=6I30:/07/-4,YgMZhN\hP^jRblTemVinZjo[mn\lm[mk\mj[nhZnhZoi]oi[liZkhYjfZieZie\ieZig[ghXghVchRajWerao|s{‹Šź¦˘°ąÁËÔÔÝäéńôňúüôúřńöđíđçčěŢâăŐÚŰÉŇŃżĚɶÉıþ«ż¸¦Ľł˘Ęľ®ÓŸÜĐÄćŮŃíâŕöěí˙ôú˙ů˙˙÷˙˙ú˙˙ü˙ţý˙ýü˙űüţüţýţ˙˙ţţţţţţţţţţţţţţţţţţ˙ţü˙ţüţýűţýű˙ýú˙ýú˙ýú˙űř˙úöţůő˙ýü˙űúýůúţúűţüý˙ýţ˙ţ˙˙ýţ˙üű˙ýú˙üú˙řó÷ěćňĺÝřęá˙ôî˙úý˙ü˙˙ýţ˙˙ý˙˙űý˙úý˙ţüý˙üý˙ýţ˙˙ţ˙˙˙ý˙ţő˙ýě˙űăţúß˙üć˙˙éçâĚŚoRL2TO2TM0UN1TN.UO/VQ3VQ3UQ4SO2ON2NM1PL1NM1NL3KN3LN6KQ7KP9JR:LU:IR5JS6OY7SZ9T[9]c?fnG‚’c’¦sź±›Ş’ˇz”pr{\\bFLP7KM5LK6KI4LJ5PK5TN8VP:WQ;VP:SN;QL9LI:IG:GE9CE:=>6:@6:@69A67B47B46C26C2;C8;C8:B79A69A6:B7;C8;C8;C8;C8;C8;C8F;=G<=G?=G<=H8>F7=E6=C7=B;:A:7A95?64B19I/BV3Ne;Uo?Tu@RtAPmAE`=9P6-C.+<*+<)1C-image_width / (dstinfo->max_h_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + for (blk_y = 0; blk_y < compptr->height_in_blocks; + blk_y += compptr->v_samp_factor) { + buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (blk_x = 0; blk_x * 2 < comp_width; blk_x++) { + ptr1 = buffer[offset_y][blk_x]; + ptr2 = buffer[offset_y][comp_width - blk_x - 1]; + /* this unrolled loop doesn't need to know which row it's on... */ + for (k = 0; k < DCTSIZE2; k += 2) { + temp1 = *ptr1; /* swap even column */ + temp2 = *ptr2; + *ptr1++ = temp2; + *ptr2++ = temp1; + temp1 = *ptr1; /* swap odd column with sign change */ + temp2 = *ptr2; + *ptr1++ = -temp2; + *ptr2++ = -temp1; + } + } + } + } + } +} + + +LOCAL(void) +do_flip_v (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Vertical flip */ +{ + JDIMENSION MCU_rows, comp_height, dst_blk_x, dst_blk_y; + int ci, i, j, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JBLOCKROW src_row_ptr, dst_row_ptr; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* We output into a separate array because we can't touch different + * rows of the source virtual array simultaneously. Otherwise, this + * is a pretty straightforward analog of horizontal flip. + * Within a DCT block, vertical mirroring is done by changing the signs + * of odd-numbered rows. + * Partial iMCUs at the bottom edge are copied verbatim. + */ + MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_height = MCU_rows * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + if (dst_blk_y < comp_height) { + /* Row is within the mirrorable area. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + comp_height - dst_blk_y - (JDIMENSION) compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } else { + /* Bottom-edge blocks will be copied verbatim. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + if (dst_blk_y < comp_height) { + /* Row is within the mirrorable area. */ + dst_row_ptr = dst_buffer[offset_y]; + src_row_ptr = src_buffer[compptr->v_samp_factor - offset_y - 1]; + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[dst_blk_x]; + for (i = 0; i < DCTSIZE; i += 2) { + /* copy even row */ + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = *src_ptr++; + /* copy odd row with sign change */ + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = - *src_ptr++; + } + } + } else { + /* Just copy row verbatim. */ + jcopy_block_row(src_buffer[offset_y], dst_buffer[offset_y], + compptr->width_in_blocks); + } + } + } + } +} + + +LOCAL(void) +do_transpose (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Transpose source into destination */ +{ + JDIMENSION dst_blk_x, dst_blk_y; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* Transposing pixels within a block just requires transposing the + * DCT coefficients. + * Partial iMCUs at the edges require no special treatment; we simply + * process all the available DCT blocks for every component. + */ + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, + (JDIMENSION) compptr->h_samp_factor, FALSE); + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + src_ptr = src_buffer[offset_x][dst_blk_y + offset_y]; + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } +} + + +LOCAL(void) +do_rot_90 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* 90 degree rotation is equivalent to + * 1. Transposing the image; + * 2. Horizontal mirroring. + * These two steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_cols, comp_width, dst_blk_x, dst_blk_y; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* Because of the horizontal mirror step, we can't process partial iMCUs + * at the (output) right edge properly. They just get transposed and + * not mirrored. + */ + MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, + (JDIMENSION) compptr->h_samp_factor, FALSE); + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + src_ptr = src_buffer[offset_x][dst_blk_y + offset_y]; + if (dst_blk_x < comp_width) { + /* Block is within the mirrorable area. */ + dst_ptr = dst_buffer[offset_y] + [comp_width - dst_blk_x - offset_x - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + i++; + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } else { + /* Edge blocks are transposed but not mirrored. */ + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } + } +} + + +LOCAL(void) +do_rot_270 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* 270 degree rotation is equivalent to + * 1. Horizontal mirroring; + * 2. Transposing the image. + * These two steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_rows, comp_height, dst_blk_x, dst_blk_y; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + /* Because of the horizontal mirror step, we can't process partial iMCUs + * at the (output) bottom edge properly. They just get transposed and + * not mirrored. + */ + MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_height = MCU_rows * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, + (JDIMENSION) compptr->h_samp_factor, FALSE); + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + if (dst_blk_y < comp_height) { + /* Block is within the mirrorable area. */ + src_ptr = src_buffer[offset_x] + [comp_height - dst_blk_y - offset_y - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } + } else { + /* Edge blocks are transposed but not mirrored. */ + src_ptr = src_buffer[offset_x][dst_blk_y + offset_y]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } + } +} + + +LOCAL(void) +do_rot_180 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* 180 degree rotation is equivalent to + * 1. Vertical mirroring; + * 2. Horizontal mirroring. + * These two steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_cols, MCU_rows, comp_width, comp_height, dst_blk_x, dst_blk_y; + int ci, i, j, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JBLOCKROW src_row_ptr, dst_row_ptr; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); + MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + comp_height = MCU_rows * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + if (dst_blk_y < comp_height) { + /* Row is within the vertically mirrorable area. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], + comp_height - dst_blk_y - (JDIMENSION) compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } else { + /* Bottom-edge rows are only mirrored horizontally. */ + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + if (dst_blk_y < comp_height) { + /* Row is within the mirrorable area. */ + dst_row_ptr = dst_buffer[offset_y]; + src_row_ptr = src_buffer[compptr->v_samp_factor - offset_y - 1]; + /* Process the blocks that can be mirrored both ways. */ + for (dst_blk_x = 0; dst_blk_x < comp_width; dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[comp_width - dst_blk_x - 1]; + for (i = 0; i < DCTSIZE; i += 2) { + /* For even row, negate every odd column. */ + for (j = 0; j < DCTSIZE; j += 2) { + *dst_ptr++ = *src_ptr++; + *dst_ptr++ = - *src_ptr++; + } + /* For odd row, negate every even column. */ + for (j = 0; j < DCTSIZE; j += 2) { + *dst_ptr++ = - *src_ptr++; + *dst_ptr++ = *src_ptr++; + } + } + } + /* Any remaining right-edge blocks are only mirrored vertically. */ + for (; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[dst_blk_x]; + for (i = 0; i < DCTSIZE; i += 2) { + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = *src_ptr++; + for (j = 0; j < DCTSIZE; j++) + *dst_ptr++ = - *src_ptr++; + } + } + } else { + /* Remaining rows are just mirrored horizontally. */ + dst_row_ptr = dst_buffer[offset_y]; + src_row_ptr = src_buffer[offset_y]; + /* Process the blocks that can be mirrored. */ + for (dst_blk_x = 0; dst_blk_x < comp_width; dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[comp_width - dst_blk_x - 1]; + for (i = 0; i < DCTSIZE2; i += 2) { + *dst_ptr++ = *src_ptr++; + *dst_ptr++ = - *src_ptr++; + } + } + /* Any remaining right-edge blocks are only copied. */ + for (; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) { + dst_ptr = dst_row_ptr[dst_blk_x]; + src_ptr = src_row_ptr[dst_blk_x]; + for (i = 0; i < DCTSIZE2; i++) + *dst_ptr++ = *src_ptr++; + } + } + } + } + } +} + + +LOCAL(void) +do_transverse (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jvirt_barray_ptr *dst_coef_arrays) +/* Transverse transpose is equivalent to + * 1. 180 degree rotation; + * 2. Transposition; + * or + * 1. Horizontal mirroring; + * 2. Transposition; + * 3. Horizontal mirroring. + * These steps are merged into a single processing routine. + */ +{ + JDIMENSION MCU_cols, MCU_rows, comp_width, comp_height, dst_blk_x, dst_blk_y; + int ci, i, j, offset_x, offset_y; + JBLOCKARRAY src_buffer, dst_buffer; + JCOEFPTR src_ptr, dst_ptr; + jpeg_component_info *compptr; + + MCU_cols = dstinfo->image_width / (dstinfo->max_h_samp_factor * DCTSIZE); + MCU_rows = dstinfo->image_height / (dstinfo->max_v_samp_factor * DCTSIZE); + + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + comp_width = MCU_cols * compptr->h_samp_factor; + comp_height = MCU_rows * compptr->v_samp_factor; + for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks; + dst_blk_y += compptr->v_samp_factor) { + dst_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y, + (JDIMENSION) compptr->v_samp_factor, TRUE); + for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) { + for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; + dst_blk_x += compptr->h_samp_factor) { + src_buffer = (*srcinfo->mem->access_virt_barray) + ((j_common_ptr) srcinfo, src_coef_arrays[ci], dst_blk_x, + (JDIMENSION) compptr->h_samp_factor, FALSE); + for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) { + if (dst_blk_y < comp_height) { + src_ptr = src_buffer[offset_x] + [comp_height - dst_blk_y - offset_y - 1]; + if (dst_blk_x < comp_width) { + /* Block is within the mirrorable area. */ + dst_ptr = dst_buffer[offset_y] + [comp_width - dst_blk_x - offset_x - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + i++; + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } else { + /* Right-edge blocks are mirrored in y only */ + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) { + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + j++; + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } + } + } else { + src_ptr = src_buffer[offset_x][dst_blk_y + offset_y]; + if (dst_blk_x < comp_width) { + /* Bottom-edge blocks are mirrored in x only */ + dst_ptr = dst_buffer[offset_y] + [comp_width - dst_blk_x - offset_x - 1]; + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + i++; + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j]; + } + } else { + /* At lower right corner, just transpose, no mirroring */ + dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x]; + for (i = 0; i < DCTSIZE; i++) + for (j = 0; j < DCTSIZE; j++) + dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j]; + } + } + } + } + } + } + } +} + + +/* Request any required workspace. + * + * We allocate the workspace virtual arrays from the source decompression + * object, so that all the arrays (both the original data and the workspace) + * will be taken into account while making memory management decisions. + * Hence, this routine must be called after jpeg_read_header (which reads + * the image dimensions) and before jpeg_read_coefficients (which realizes + * the source's virtual arrays). + */ + +GLOBAL(void) +jtransform_request_workspace (j_decompress_ptr srcinfo, + jpeg_transform_info *info) +{ + jvirt_barray_ptr *coef_arrays = NULL; + jpeg_component_info *compptr; + int ci; + + if (info->force_grayscale && + srcinfo->jpeg_color_space == JCS_YCbCr && + srcinfo->num_components == 3) { + /* We'll only process the first component */ + info->num_components = 1; + } else { + /* Process all the components */ + info->num_components = srcinfo->num_components; + } + + switch (info->transform) { + case JXFORM_NONE: + case JXFORM_FLIP_H: + /* Don't need a workspace array */ + break; + case JXFORM_FLIP_V: + case JXFORM_ROT_180: + /* Need workspace arrays having same dimensions as source image. + * Note that we allocate arrays padded out to the next iMCU boundary, + * so that transform routines need not worry about missing edge blocks. + */ + coef_arrays = (jvirt_barray_ptr *) + (*srcinfo->mem->alloc_small) ((j_common_ptr) srcinfo, JPOOL_IMAGE, + SIZEOF(jvirt_barray_ptr) * info->num_components); + for (ci = 0; ci < info->num_components; ci++) { + compptr = srcinfo->comp_info + ci; + coef_arrays[ci] = (*srcinfo->mem->request_virt_barray) + ((j_common_ptr) srcinfo, JPOOL_IMAGE, FALSE, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + (long) compptr->h_samp_factor), + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + (long) compptr->v_samp_factor), + (JDIMENSION) compptr->v_samp_factor); + } + break; + case JXFORM_TRANSPOSE: + case JXFORM_TRANSVERSE: + case JXFORM_ROT_90: + case JXFORM_ROT_270: + /* Need workspace arrays having transposed dimensions. + * Note that we allocate arrays padded out to the next iMCU boundary, + * so that transform routines need not worry about missing edge blocks. + */ + coef_arrays = (jvirt_barray_ptr *) + (*srcinfo->mem->alloc_small) ((j_common_ptr) srcinfo, JPOOL_IMAGE, + SIZEOF(jvirt_barray_ptr) * info->num_components); + for (ci = 0; ci < info->num_components; ci++) { + compptr = srcinfo->comp_info + ci; + coef_arrays[ci] = (*srcinfo->mem->request_virt_barray) + ((j_common_ptr) srcinfo, JPOOL_IMAGE, FALSE, + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + (long) compptr->v_samp_factor), + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + (long) compptr->h_samp_factor), + (JDIMENSION) compptr->h_samp_factor); + } + break; + } + info->workspace_coef_arrays = coef_arrays; +} + + +/* Transpose destination image parameters */ + +LOCAL(void) +transpose_critical_parameters (j_compress_ptr dstinfo) +{ + int tblno, i, j, ci, itemp; + jpeg_component_info *compptr; + JQUANT_TBL *qtblptr; + JDIMENSION dtemp; + UINT16 qtemp; + + /* Transpose basic image dimensions */ + dtemp = dstinfo->image_width; + dstinfo->image_width = dstinfo->image_height; + dstinfo->image_height = dtemp; + + /* Transpose sampling factors */ + for (ci = 0; ci < dstinfo->num_components; ci++) { + compptr = dstinfo->comp_info + ci; + itemp = compptr->h_samp_factor; + compptr->h_samp_factor = compptr->v_samp_factor; + compptr->v_samp_factor = itemp; + } + + /* Transpose quantization tables */ + for (tblno = 0; tblno < NUM_QUANT_TBLS; tblno++) { + qtblptr = dstinfo->quant_tbl_ptrs[tblno]; + if (qtblptr != NULL) { + for (i = 0; i < DCTSIZE; i++) { + for (j = 0; j < i; j++) { + qtemp = qtblptr->quantval[i*DCTSIZE+j]; + qtblptr->quantval[i*DCTSIZE+j] = qtblptr->quantval[j*DCTSIZE+i]; + qtblptr->quantval[j*DCTSIZE+i] = qtemp; + } + } + } + } +} + + +/* Trim off any partial iMCUs on the indicated destination edge */ + +LOCAL(void) +trim_right_edge (j_compress_ptr dstinfo) +{ + int ci, max_h_samp_factor; + JDIMENSION MCU_cols; + + /* We have to compute max_h_samp_factor ourselves, + * because it hasn't been set yet in the destination + * (and we don't want to use the source's value). + */ + max_h_samp_factor = 1; + for (ci = 0; ci < dstinfo->num_components; ci++) { + int h_samp_factor = dstinfo->comp_info[ci].h_samp_factor; + max_h_samp_factor = MAX(max_h_samp_factor, h_samp_factor); + } + MCU_cols = dstinfo->image_width / (max_h_samp_factor * DCTSIZE); + if (MCU_cols > 0) /* can't trim to 0 pixels */ + dstinfo->image_width = MCU_cols * (max_h_samp_factor * DCTSIZE); +} + +LOCAL(void) +trim_bottom_edge (j_compress_ptr dstinfo) +{ + int ci, max_v_samp_factor; + JDIMENSION MCU_rows; + + /* We have to compute max_v_samp_factor ourselves, + * because it hasn't been set yet in the destination + * (and we don't want to use the source's value). + */ + max_v_samp_factor = 1; + for (ci = 0; ci < dstinfo->num_components; ci++) { + int v_samp_factor = dstinfo->comp_info[ci].v_samp_factor; + max_v_samp_factor = MAX(max_v_samp_factor, v_samp_factor); + } + MCU_rows = dstinfo->image_height / (max_v_samp_factor * DCTSIZE); + if (MCU_rows > 0) /* can't trim to 0 pixels */ + dstinfo->image_height = MCU_rows * (max_v_samp_factor * DCTSIZE); +} + + +/* Adjust output image parameters as needed. + * + * This must be called after jpeg_copy_critical_parameters() + * and before jpeg_write_coefficients(). + * + * The return value is the set of virtual coefficient arrays to be written + * (either the ones allocated by jtransform_request_workspace, or the + * original source data arrays). The caller will need to pass this value + * to jpeg_write_coefficients(). + */ + +GLOBAL(jvirt_barray_ptr *) +jtransform_adjust_parameters (j_decompress_ptr srcinfo, + j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info) +{ + /* If force-to-grayscale is requested, adjust destination parameters */ + if (info->force_grayscale) { + /* We use jpeg_set_colorspace to make sure subsidiary settings get fixed + * properly. Among other things, the target h_samp_factor & v_samp_factor + * will get set to 1, which typically won't match the source. + * In fact we do this even if the source is already grayscale; that + * provides an easy way of coercing a grayscale JPEG with funny sampling + * factors to the customary 1,1. (Some decoders fail on other factors.) + */ + if ((dstinfo->jpeg_color_space == JCS_YCbCr && + dstinfo->num_components == 3) || + (dstinfo->jpeg_color_space == JCS_GRAYSCALE && + dstinfo->num_components == 1)) { + /* We have to preserve the source's quantization table number. */ + int sv_quant_tbl_no = dstinfo->comp_info[0].quant_tbl_no; + jpeg_set_colorspace(dstinfo, JCS_GRAYSCALE); + dstinfo->comp_info[0].quant_tbl_no = sv_quant_tbl_no; + } else { + /* Sorry, can't do it */ + ERREXIT(dstinfo, JERR_CONVERSION_NOTIMPL); + } + } + + /* Correct the destination's image dimensions etc if necessary */ + switch (info->transform) { + case JXFORM_NONE: + /* Nothing to do */ + break; + case JXFORM_FLIP_H: + if (info->trim) + trim_right_edge(dstinfo); + break; + case JXFORM_FLIP_V: + if (info->trim) + trim_bottom_edge(dstinfo); + break; + case JXFORM_TRANSPOSE: + transpose_critical_parameters(dstinfo); + /* transpose does NOT have to trim anything */ + break; + case JXFORM_TRANSVERSE: + transpose_critical_parameters(dstinfo); + if (info->trim) { + trim_right_edge(dstinfo); + trim_bottom_edge(dstinfo); + } + break; + case JXFORM_ROT_90: + transpose_critical_parameters(dstinfo); + if (info->trim) + trim_right_edge(dstinfo); + break; + case JXFORM_ROT_180: + if (info->trim) { + trim_right_edge(dstinfo); + trim_bottom_edge(dstinfo); + } + break; + case JXFORM_ROT_270: + transpose_critical_parameters(dstinfo); + if (info->trim) + trim_bottom_edge(dstinfo); + break; + } + + /* Return the appropriate output data set */ + if (info->workspace_coef_arrays != NULL) + return info->workspace_coef_arrays; + return src_coef_arrays; +} + + +/* Execute the actual transformation, if any. + * + * This must be called *after* jpeg_write_coefficients, because it depends + * on jpeg_write_coefficients to have computed subsidiary values such as + * the per-component width and height fields in the destination object. + * + * Note that some transformations will modify the source data arrays! + */ + +GLOBAL(void) +jtransform_execute_transformation (j_decompress_ptr srcinfo, + j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info) +{ + jvirt_barray_ptr *dst_coef_arrays = info->workspace_coef_arrays; + + switch (info->transform) { + case JXFORM_NONE: + break; + case JXFORM_FLIP_H: + do_flip_h(srcinfo, dstinfo, src_coef_arrays); + break; + case JXFORM_FLIP_V: + do_flip_v(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_TRANSPOSE: + do_transpose(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_TRANSVERSE: + do_transverse(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_90: + do_rot_90(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_180: + do_rot_180(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + case JXFORM_ROT_270: + do_rot_270(srcinfo, dstinfo, src_coef_arrays, dst_coef_arrays); + break; + } +} + +#endif /* TRANSFORMS_SUPPORTED */ + + +/* Setup decompression object to save desired markers in memory. + * This must be called before jpeg_read_header() to have the desired effect. + */ + +GLOBAL(void) +jcopy_markers_setup (j_decompress_ptr srcinfo, JCOPY_OPTION option) +{ +#ifdef SAVE_MARKERS_SUPPORTED + int m; + + /* Save comments except under NONE option */ + if (option != JCOPYOPT_NONE) { + jpeg_save_markers(srcinfo, JPEG_COM, 0xFFFF); + } + /* Save all types of APPn markers iff ALL option */ + if (option == JCOPYOPT_ALL) { + for (m = 0; m < 16; m++) + jpeg_save_markers(srcinfo, JPEG_APP0 + m, 0xFFFF); + } +#endif /* SAVE_MARKERS_SUPPORTED */ +} + +/* Copy markers saved in the given source object to the destination object. + * This should be called just after jpeg_start_compress() or + * jpeg_write_coefficients(). + * Note that those routines will have written the SOI, and also the + * JFIF APP0 or Adobe APP14 markers if selected. + */ + +GLOBAL(void) +jcopy_markers_execute (j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JCOPY_OPTION option) +{ + jpeg_saved_marker_ptr marker; + + /* In the current implementation, we don't actually need to examine the + * option flag here; we just copy everything that got saved. + * But to avoid confusion, we do not output JFIF and Adobe APP14 markers + * if the encoder library already wrote one. + */ + for (marker = srcinfo->marker_list; marker != NULL; marker = marker->next) { + if (dstinfo->write_JFIF_header && + marker->marker == JPEG_APP0 && + marker->data_length >= 5 && + GETJOCTET(marker->data[0]) == 0x4A && + GETJOCTET(marker->data[1]) == 0x46 && + GETJOCTET(marker->data[2]) == 0x49 && + GETJOCTET(marker->data[3]) == 0x46 && + GETJOCTET(marker->data[4]) == 0) + continue; /* reject duplicate JFIF */ + if (dstinfo->write_Adobe_marker && + marker->marker == JPEG_APP0+14 && + marker->data_length >= 5 && + GETJOCTET(marker->data[0]) == 0x41 && + GETJOCTET(marker->data[1]) == 0x64 && + GETJOCTET(marker->data[2]) == 0x6F && + GETJOCTET(marker->data[3]) == 0x62 && + GETJOCTET(marker->data[4]) == 0x65) + continue; /* reject duplicate Adobe */ +#ifdef NEED_FAR_POINTERS + /* We could use jpeg_write_marker if the data weren't FAR... */ + { + unsigned int i; + jpeg_write_m_header(dstinfo, marker->marker, marker->data_length); + for (i = 0; i < marker->data_length; i++) + jpeg_write_m_byte(dstinfo, marker->data[i]); + } +#else + jpeg_write_marker(dstinfo, marker->marker, + marker->data, marker->data_length); +#endif + } +} diff --git a/Engine/lib/ljpeg/extras/transupp.h b/Engine/lib/ljpeg/extras/transupp.h new file mode 100644 index 000000000..5c2d32aff --- /dev/null +++ b/Engine/lib/ljpeg/extras/transupp.h @@ -0,0 +1,135 @@ +/* + * transupp.h + * + * Copyright (C) 1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains declarations for image transformation routines and + * other utility code used by the jpegtran sample application. These are + * NOT part of the core JPEG library. But we keep these routines separate + * from jpegtran.c to ease the task of maintaining jpegtran-like programs + * that have other user interfaces. + * + * NOTE: all the routines declared here have very specific requirements + * about when they are to be executed during the reading and writing of the + * source and destination files. See the comments in transupp.c, or see + * jpegtran.c for an example of correct usage. + */ + +/* If you happen not to want the image transform support, disable it here */ +#ifndef TRANSFORMS_SUPPORTED +#define TRANSFORMS_SUPPORTED 1 /* 0 disables transform code */ +#endif + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jtransform_request_workspace jTrRequest +#define jtransform_adjust_parameters jTrAdjust +#define jtransform_execute_transformation jTrExec +#define jcopy_markers_setup jCMrkSetup +#define jcopy_markers_execute jCMrkExec +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* + * Codes for supported types of image transformations. + */ + +typedef enum { + JXFORM_NONE, /* no transformation */ + JXFORM_FLIP_H, /* horizontal flip */ + JXFORM_FLIP_V, /* vertical flip */ + JXFORM_TRANSPOSE, /* transpose across UL-to-LR axis */ + JXFORM_TRANSVERSE, /* transpose across UR-to-LL axis */ + JXFORM_ROT_90, /* 90-degree clockwise rotation */ + JXFORM_ROT_180, /* 180-degree rotation */ + JXFORM_ROT_270 /* 270-degree clockwise (or 90 ccw) */ +} JXFORM_CODE; + +/* + * Although rotating and flipping data expressed as DCT coefficients is not + * hard, there is an asymmetry in the JPEG format specification for images + * whose dimensions aren't multiples of the iMCU size. The right and bottom + * image edges are padded out to the next iMCU boundary with junk data; but + * no padding is possible at the top and left edges. If we were to flip + * the whole image including the pad data, then pad garbage would become + * visible at the top and/or left, and real pixels would disappear into the + * pad margins --- perhaps permanently, since encoders & decoders may not + * bother to preserve DCT blocks that appear to be completely outside the + * nominal image area. So, we have to exclude any partial iMCUs from the + * basic transformation. + * + * Transpose is the only transformation that can handle partial iMCUs at the + * right and bottom edges completely cleanly. flip_h can flip partial iMCUs + * at the bottom, but leaves any partial iMCUs at the right edge untouched. + * Similarly flip_v leaves any partial iMCUs at the bottom edge untouched. + * The other transforms are defined as combinations of these basic transforms + * and process edge blocks in a way that preserves the equivalence. + * + * The "trim" option causes untransformable partial iMCUs to be dropped; + * this is not strictly lossless, but it usually gives the best-looking + * result for odd-size images. Note that when this option is active, + * the expected mathematical equivalences between the transforms may not hold. + * (For example, -rot 270 -trim trims only the bottom edge, but -rot 90 -trim + * followed by -rot 180 -trim trims both edges.) + * + * We also offer a "force to grayscale" option, which simply discards the + * chrominance channels of a YCbCr image. This is lossless in the sense that + * the luminance channel is preserved exactly. It's not the same kind of + * thing as the rotate/flip transformations, but it's convenient to handle it + * as part of this package, mainly because the transformation routines have to + * be aware of the option to know how many components to work on. + */ + +typedef struct { + /* Options: set by caller */ + JXFORM_CODE transform; /* image transform operator */ + boolean trim; /* if TRUE, trim partial MCUs as needed */ + boolean force_grayscale; /* if TRUE, convert color image to grayscale */ + + /* Internal workspace: caller should not touch these */ + int num_components; /* # of components in workspace */ + jvirt_barray_ptr * workspace_coef_arrays; /* workspace for transformations */ +} jpeg_transform_info; + + +#if TRANSFORMS_SUPPORTED + +/* Request any required workspace */ +EXTERN(void) jtransform_request_workspace + JPP((j_decompress_ptr srcinfo, jpeg_transform_info *info)); +/* Adjust output image parameters */ +EXTERN(jvirt_barray_ptr *) jtransform_adjust_parameters + JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info)); +/* Execute the actual transformation, if any */ +EXTERN(void) jtransform_execute_transformation + JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + jvirt_barray_ptr *src_coef_arrays, + jpeg_transform_info *info)); + +#endif /* TRANSFORMS_SUPPORTED */ + + +/* + * Support for copying optional markers from source to destination file. + */ + +typedef enum { + JCOPYOPT_NONE, /* copy no optional markers */ + JCOPYOPT_COMMENTS, /* copy only comment (COM) markers */ + JCOPYOPT_ALL /* copy all optional markers */ +} JCOPY_OPTION; + +#define JCOPYOPT_DEFAULT JCOPYOPT_COMMENTS /* recommended default */ + +/* Setup decompression object to save desired markers in memory */ +EXTERN(void) jcopy_markers_setup + JPP((j_decompress_ptr srcinfo, JCOPY_OPTION option)); +/* Copy markers saved in the given source object to the destination object */ +EXTERN(void) jcopy_markers_execute + JPP((j_decompress_ptr srcinfo, j_compress_ptr dstinfo, + JCOPY_OPTION option)); diff --git a/Engine/lib/ljpeg/extras/usage.doc b/Engine/lib/ljpeg/extras/usage.doc new file mode 100644 index 000000000..8c4970af0 --- /dev/null +++ b/Engine/lib/ljpeg/extras/usage.doc @@ -0,0 +1,562 @@ +USAGE instructions for the Independent JPEG Group's JPEG software +================================================================= + +This file describes usage of the JPEG conversion programs cjpeg and djpeg, +as well as the utility programs jpegtran, rdjpgcom and wrjpgcom. (See +the other documentation files if you wish to use the JPEG library within +your own programs.) + +If you are on a Unix machine you may prefer to read the Unix-style manual +pages in files cjpeg.1, djpeg.1, jpegtran.1, rdjpgcom.1, wrjpgcom.1. + + +INTRODUCTION + +These programs implement JPEG image compression and decompression. JPEG +(pronounced "jay-peg") is a standardized compression method for full-color +and gray-scale images. JPEG is designed to handle "real-world" scenes, +for example scanned photographs. Cartoons, line drawings, and other +non-realistic images are not JPEG's strong suit; on that sort of material +you may get poor image quality and/or little compression. + +JPEG is lossy, meaning that the output image is not necessarily identical to +the input image. Hence you should not use JPEG if you have to have identical +output bits. However, on typical real-world images, very good compression +levels can be obtained with no visible change, and amazingly high compression +is possible if you can tolerate a low-quality image. You can trade off image +quality against file size by adjusting the compressor's "quality" setting. + + +GENERAL USAGE + +We provide two programs, cjpeg to compress an image file into JPEG format, +and djpeg to decompress a JPEG file back into a conventional image format. + +On Unix-like systems, you say: + cjpeg [switches] [imagefile] >jpegfile +or + djpeg [switches] [jpegfile] >imagefile +The programs read the specified input file, or standard input if none is +named. They always write to standard output (with trace/error messages to +standard error). These conventions are handy for piping images between +programs. + +On most non-Unix systems, you say: + cjpeg [switches] imagefile jpegfile +or + djpeg [switches] jpegfile imagefile +i.e., both the input and output files are named on the command line. This +style is a little more foolproof, and it loses no functionality if you don't +have pipes. (You can get this style on Unix too, if you prefer, by defining +TWO_FILE_COMMANDLINE when you compile the programs; see install.doc.) + +You can also say: + cjpeg [switches] -outfile jpegfile imagefile +or + djpeg [switches] -outfile imagefile jpegfile +This syntax works on all systems, so it is useful for scripts. + +The currently supported image file formats are: PPM (PBMPLUS color format), +PGM (PBMPLUS gray-scale format), BMP, Targa, and RLE (Utah Raster Toolkit +format). (RLE is supported only if the URT library is available.) +cjpeg recognizes the input image format automatically, with the exception +of some Targa-format files. You have to tell djpeg which format to generate. + +JPEG files are in the defacto standard JFIF file format. There are other, +less widely used JPEG-based file formats, but we don't support them. + +All switch names may be abbreviated; for example, -grayscale may be written +-gray or -gr. Most of the "basic" switches can be abbreviated to as little as +one letter. Upper and lower case are equivalent (-BMP is the same as -bmp). +British spellings are also accepted (e.g., -greyscale), though for brevity +these are not mentioned below. + + +CJPEG DETAILS + +The basic command line switches for cjpeg are: + + -quality N Scale quantization tables to adjust image quality. + Quality is 0 (worst) to 100 (best); default is 75. + (See below for more info.) + + -grayscale Create monochrome JPEG file from color input. + Be sure to use this switch when compressing a grayscale + BMP file, because cjpeg isn't bright enough to notice + whether a BMP file uses only shades of gray. By + saying -grayscale, you'll get a smaller JPEG file that + takes less time to process. + + -optimize Perform optimization of entropy encoding parameters. + Without this, default encoding parameters are used. + -optimize usually makes the JPEG file a little smaller, + but cjpeg runs somewhat slower and needs much more + memory. Image quality and speed of decompression are + unaffected by -optimize. + + -progressive Create progressive JPEG file (see below). + + -targa Input file is Targa format. Targa files that contain + an "identification" field will not be automatically + recognized by cjpeg; for such files you must specify + -targa to make cjpeg treat the input as Targa format. + For most Targa files, you won't need this switch. + +The -quality switch lets you trade off compressed file size against quality of +the reconstructed image: the higher the quality setting, the larger the JPEG +file, and the closer the output image will be to the original input. Normally +you want to use the lowest quality setting (smallest file) that decompresses +into something visually indistinguishable from the original image. For this +purpose the quality setting should be between 50 and 95; the default of 75 is +often about right. If you see defects at -quality 75, then go up 5 or 10 +counts at a time until you are happy with the output image. (The optimal +setting will vary from one image to another.) + +-quality 100 will generate a quantization table of all 1's, minimizing loss +in the quantization step (but there is still information loss in subsampling, +as well as roundoff error). This setting is mainly of interest for +experimental purposes. Quality values above about 95 are NOT recommended for +normal use; the compressed file size goes up dramatically for hardly any gain +in output image quality. + +In the other direction, quality values below 50 will produce very small files +of low image quality. Settings around 5 to 10 might be useful in preparing an +index of a large image library, for example. Try -quality 2 (or so) for some +amusing Cubist effects. (Note: quality values below about 25 generate 2-byte +quantization tables, which are considered optional in the JPEG standard. +cjpeg emits a warning message when you give such a quality value, because some +other JPEG programs may be unable to decode the resulting file. Use -baseline +if you need to ensure compatibility at low quality values.) + +The -progressive switch creates a "progressive JPEG" file. In this type of +JPEG file, the data is stored in multiple scans of increasing quality. If the +file is being transmitted over a slow communications link, the decoder can use +the first scan to display a low-quality image very quickly, and can then +improve the display with each subsequent scan. The final image is exactly +equivalent to a standard JPEG file of the same quality setting, and the total +file size is about the same --- often a little smaller. CAUTION: progressive +JPEG is not yet widely implemented, so many decoders will be unable to view a +progressive JPEG file at all. + +Switches for advanced users: + + -dct int Use integer DCT method (default). + -dct fast Use fast integer DCT (less accurate). + -dct float Use floating-point DCT method. + The float method is very slightly more accurate than + the int method, but is much slower unless your machine + has very fast floating-point hardware. Also note that + results of the floating-point method may vary slightly + across machines, while the integer methods should give + the same results everywhere. The fast integer method + is much less accurate than the other two. + + -restart N Emit a JPEG restart marker every N MCU rows, or every + N MCU blocks if "B" is attached to the number. + -restart 0 (the default) means no restart markers. + + -smooth N Smooth the input image to eliminate dithering noise. + N, ranging from 1 to 100, indicates the strength of + smoothing. 0 (the default) means no smoothing. + + -maxmemory N Set limit for amount of memory to use in processing + large images. Value is in thousands of bytes, or + millions of bytes if "M" is attached to the number. + For example, -max 4m selects 4000000 bytes. If more + space is needed, temporary files will be used. + + -verbose Enable debug printout. More -v's give more printout. + or -debug Also, version information is printed at startup. + +The -restart option inserts extra markers that allow a JPEG decoder to +resynchronize after a transmission error. Without restart markers, any damage +to a compressed file will usually ruin the image from the point of the error +to the end of the image; with restart markers, the damage is usually confined +to the portion of the image up to the next restart marker. Of course, the +restart markers occupy extra space. We recommend -restart 1 for images that +will be transmitted across unreliable networks such as Usenet. + +The -smooth option filters the input to eliminate fine-scale noise. This is +often useful when converting dithered images to JPEG: a moderate smoothing +factor of 10 to 50 gets rid of dithering patterns in the input file, resulting +in a smaller JPEG file and a better-looking image. Too large a smoothing +factor will visibly blur the image, however. + +Switches for wizards: + + -baseline Force baseline-compatible quantization tables to be + generated. This clamps quantization values to 8 bits + even at low quality settings. (This switch is poorly + named, since it does not ensure that the output is + actually baseline JPEG. For example, you can use + -baseline and -progressive together.) + + -qtables file Use the quantization tables given in the specified + text file. + + -qslots N[,...] Select which quantization table to use for each color + component. + + -sample HxV[,...] Set JPEG sampling factors for each color component. + + -scans file Use the scan script given in the specified text file. + +The "wizard" switches are intended for experimentation with JPEG. If you +don't know what you are doing, DON'T USE THEM. These switches are documented +further in the file wizard.doc. + + +DJPEG DETAILS + +The basic command line switches for djpeg are: + + -colors N Reduce image to at most N colors. This reduces the + or -quantize N number of colors used in the output image, so that it + can be displayed on a colormapped display or stored in + a colormapped file format. For example, if you have + an 8-bit display, you'd need to reduce to 256 or fewer + colors. (-colors is the recommended name, -quantize + is provided only for backwards compatibility.) + + -fast Select recommended processing options for fast, low + quality output. (The default options are chosen for + highest quality output.) Currently, this is equivalent + to "-dct fast -nosmooth -onepass -dither ordered". + + -grayscale Force gray-scale output even if JPEG file is color. + Useful for viewing on monochrome displays; also, + djpeg runs noticeably faster in this mode. + + -scale M/N Scale the output image by a factor M/N. Currently + the scale factor must be 1/1, 1/2, 1/4, or 1/8. + Scaling is handy if the image is larger than your + screen; also, djpeg runs much faster when scaling + down the output. + + -bmp Select BMP output format (Windows flavor). 8-bit + colormapped format is emitted if -colors or -grayscale + is specified, or if the JPEG file is gray-scale; + otherwise, 24-bit full-color format is emitted. + + -gif Select GIF output format. Since GIF does not support + more than 256 colors, -colors 256 is assumed (unless + you specify a smaller number of colors). If you + specify -fast, the default number of colors is 216. + + -os2 Select BMP output format (OS/2 1.x flavor). 8-bit + colormapped format is emitted if -colors or -grayscale + is specified, or if the JPEG file is gray-scale; + otherwise, 24-bit full-color format is emitted. + + -pnm Select PBMPLUS (PPM/PGM) output format (this is the + default format). PGM is emitted if the JPEG file is + gray-scale or if -grayscale is specified; otherwise + PPM is emitted. + + -rle Select RLE output format. (Requires URT library.) + + -targa Select Targa output format. Gray-scale format is + emitted if the JPEG file is gray-scale or if + -grayscale is specified; otherwise, colormapped format + is emitted if -colors is specified; otherwise, 24-bit + full-color format is emitted. + +Switches for advanced users: + + -dct int Use integer DCT method (default). + -dct fast Use fast integer DCT (less accurate). + -dct float Use floating-point DCT method. + The float method is very slightly more accurate than + the int method, but is much slower unless your machine + has very fast floating-point hardware. Also note that + results of the floating-point method may vary slightly + across machines, while the integer methods should give + the same results everywhere. The fast integer method + is much less accurate than the other two. + + -dither fs Use Floyd-Steinberg dithering in color quantization. + -dither ordered Use ordered dithering in color quantization. + -dither none Do not use dithering in color quantization. + By default, Floyd-Steinberg dithering is applied when + quantizing colors; this is slow but usually produces + the best results. Ordered dither is a compromise + between speed and quality; no dithering is fast but + usually looks awful. Note that these switches have + no effect unless color quantization is being done. + Ordered dither is only available in -onepass mode. + + -map FILE Quantize to the colors used in the specified image + file. This is useful for producing multiple files + with identical color maps, or for forcing a predefined + set of colors to be used. The FILE must be a GIF + or PPM file. This option overrides -colors and + -onepass. + + -nosmooth Use a faster, lower-quality upsampling routine. + + -onepass Use one-pass instead of two-pass color quantization. + The one-pass method is faster and needs less memory, + but it produces a lower-quality image. -onepass is + ignored unless you also say -colors N. Also, + the one-pass method is always used for gray-scale + output (the two-pass method is no improvement then). + + -maxmemory N Set limit for amount of memory to use in processing + large images. Value is in thousands of bytes, or + millions of bytes if "M" is attached to the number. + For example, -max 4m selects 4000000 bytes. If more + space is needed, temporary files will be used. + + -verbose Enable debug printout. More -v's give more printout. + or -debug Also, version information is printed at startup. + + +HINTS FOR CJPEG + +Color GIF files are not the ideal input for JPEG; JPEG is really intended for +compressing full-color (24-bit) images. In particular, don't try to convert +cartoons, line drawings, and other images that have only a few distinct +colors. GIF works great on these, JPEG does not. If you want to convert a +GIF to JPEG, you should experiment with cjpeg's -quality and -smooth options +to get a satisfactory conversion. -smooth 10 or so is often helpful. + +Avoid running an image through a series of JPEG compression/decompression +cycles. Image quality loss will accumulate; after ten or so cycles the image +may be noticeably worse than it was after one cycle. It's best to use a +lossless format while manipulating an image, then convert to JPEG format when +you are ready to file the image away. + +The -optimize option to cjpeg is worth using when you are making a "final" +version for posting or archiving. It's also a win when you are using low +quality settings to make very small JPEG files; the percentage improvement +is often a lot more than it is on larger files. (At present, -optimize +mode is always selected when generating progressive JPEG files.) + +GIF input files are no longer supported, to avoid the Unisys LZW patent. +Use a Unisys-licensed program if you need to read a GIF file. (Conversion +of GIF files to JPEG is usually a bad idea anyway.) + + +HINTS FOR DJPEG + +To get a quick preview of an image, use the -grayscale and/or -scale switches. +"-grayscale -scale 1/8" is the fastest case. + +Several options are available that trade off image quality to gain speed. +"-fast" turns on the recommended settings. + +"-dct fast" and/or "-nosmooth" gain speed at a small sacrifice in quality. +When producing a color-quantized image, "-onepass -dither ordered" is fast but +much lower quality than the default behavior. "-dither none" may give +acceptable results in two-pass mode, but is seldom tolerable in one-pass mode. + +If you are fortunate enough to have very fast floating point hardware, +"-dct float" may be even faster than "-dct fast". But on most machines +"-dct float" is slower than "-dct int"; in this case it is not worth using, +because its theoretical accuracy advantage is too small to be significant +in practice. + +Two-pass color quantization requires a good deal of memory; on MS-DOS machines +it may run out of memory even with -maxmemory 0. In that case you can still +decompress, with some loss of image quality, by specifying -onepass for +one-pass quantization. + +To avoid the Unisys LZW patent, djpeg produces uncompressed GIF files. These +are larger than they should be, but are readable by standard GIF decoders. + + +HINTS FOR BOTH PROGRAMS + +If more space is needed than will fit in the available main memory (as +determined by -maxmemory), temporary files will be used. (MS-DOS versions +will try to get extended or expanded memory first.) The temporary files are +often rather large: in typical cases they occupy three bytes per pixel, for +example 3*800*600 = 1.44Mb for an 800x600 image. If you don't have enough +free disk space, leave out -progressive and -optimize (for cjpeg) or specify +-onepass (for djpeg). + +On MS-DOS, the temporary files are created in the directory named by the TMP +or TEMP environment variable, or in the current directory if neither of those +exist. Amiga implementations put the temp files in the directory named by +JPEGTMP:, so be sure to assign JPEGTMP: to a disk partition with adequate free +space. + +The default memory usage limit (-maxmemory) is set when the software is +compiled. If you get an "insufficient memory" error, try specifying a smaller +-maxmemory value, even -maxmemory 0 to use the absolute minimum space. You +may want to recompile with a smaller default value if this happens often. + +On machines that have "environment" variables, you can define the environment +variable JPEGMEM to set the default memory limit. The value is specified as +described for the -maxmemory switch. JPEGMEM overrides the default value +specified when the program was compiled, and itself is overridden by an +explicit -maxmemory switch. + +On MS-DOS machines, -maxmemory is the amount of main (conventional) memory to +use. (Extended or expanded memory is also used if available.) Most +DOS-specific versions of this software do their own memory space estimation +and do not need you to specify -maxmemory. + + +JPEGTRAN + +jpegtran performs various useful transformations of JPEG files. +It can translate the coded representation from one variant of JPEG to another, +for example from baseline JPEG to progressive JPEG or vice versa. It can also +perform some rearrangements of the image data, for example turning an image +from landscape to portrait format by rotation. + +jpegtran works by rearranging the compressed data (DCT coefficients), without +ever fully decoding the image. Therefore, its transformations are lossless: +there is no image degradation at all, which would not be true if you used +djpeg followed by cjpeg to accomplish the same conversion. But by the same +token, jpegtran cannot perform lossy operations such as changing the image +quality. + +jpegtran uses a command line syntax similar to cjpeg or djpeg. +On Unix-like systems, you say: + jpegtran [switches] [inputfile] >outputfile +On most non-Unix systems, you say: + jpegtran [switches] inputfile outputfile +where both the input and output files are JPEG files. + +To specify the coded JPEG representation used in the output file, +jpegtran accepts a subset of the switches recognized by cjpeg: + -optimize Perform optimization of entropy encoding parameters. + -progressive Create progressive JPEG file. + -restart N Emit a JPEG restart marker every N MCU rows, or every + N MCU blocks if "B" is attached to the number. + -scans file Use the scan script given in the specified text file. +See the previous discussion of cjpeg for more details about these switches. +If you specify none of these switches, you get a plain baseline-JPEG output +file. The quality setting and so forth are determined by the input file. + +The image can be losslessly transformed by giving one of these switches: + -flip horizontal Mirror image horizontally (left-right). + -flip vertical Mirror image vertically (top-bottom). + -rotate 90 Rotate image 90 degrees clockwise. + -rotate 180 Rotate image 180 degrees. + -rotate 270 Rotate image 270 degrees clockwise (or 90 ccw). + -transpose Transpose image (across UL-to-LR axis). + -transverse Transverse transpose (across UR-to-LL axis). + +The transpose transformation has no restrictions regarding image dimensions. +The other transformations operate rather oddly if the image dimensions are not +a multiple of the iMCU size (usually 8 or 16 pixels), because they can only +transform complete blocks of DCT coefficient data in the desired way. + +jpegtran's default behavior when transforming an odd-size image is designed +to preserve exact reversibility and mathematical consistency of the +transformation set. As stated, transpose is able to flip the entire image +area. Horizontal mirroring leaves any partial iMCU column at the right edge +untouched, but is able to flip all rows of the image. Similarly, vertical +mirroring leaves any partial iMCU row at the bottom edge untouched, but is +able to flip all columns. The other transforms can be built up as sequences +of transpose and flip operations; for consistency, their actions on edge +pixels are defined to be the same as the end result of the corresponding +transpose-and-flip sequence. + +For practical use, you may prefer to discard any untransformable edge pixels +rather than having a strange-looking strip along the right and/or bottom edges +of a transformed image. To do this, add the -trim switch: + -trim Drop non-transformable edge blocks. +Obviously, a transformation with -trim is not reversible, so strictly speaking +jpegtran with this switch is not lossless. Also, the expected mathematical +equivalences between the transformations no longer hold. For example, +"-rot 270 -trim" trims only the bottom edge, but "-rot 90 -trim" followed by +"-rot 180 -trim" trims both edges. + +Another not-strictly-lossless transformation switch is: + -grayscale Force grayscale output. +This option discards the chrominance channels if the input image is YCbCr +(ie, a standard color JPEG), resulting in a grayscale JPEG file. The +luminance channel is preserved exactly, so this is a better method of reducing +to grayscale than decompression, conversion, and recompression. This switch +is particularly handy for fixing a monochrome picture that was mistakenly +encoded as a color JPEG. (In such a case, the space savings from getting rid +of the near-empty chroma channels won't be large; but the decoding time for +a grayscale JPEG is substantially less than that for a color JPEG.) + +jpegtran also recognizes these switches that control what to do with "extra" +markers, such as comment blocks: + -copy none Copy no extra markers from source file. This setting + suppresses all comments and other excess baggage + present in the source file. + -copy comments Copy only comment markers. This setting copies + comments from the source file, but discards + any other inessential data. + -copy all Copy all extra markers. This setting preserves + miscellaneous markers found in the source file, such + as JFIF thumbnails and Photoshop settings. In some + files these extra markers can be sizable. +The default behavior is -copy comments. (Note: in IJG releases v6 and v6a, +jpegtran always did the equivalent of -copy none.) + +Additional switches recognized by jpegtran are: + -outfile filename + -maxmemory N + -verbose + -debug +These work the same as in cjpeg or djpeg. + + +THE COMMENT UTILITIES + +The JPEG standard allows "comment" (COM) blocks to occur within a JPEG file. +Although the standard doesn't actually define what COM blocks are for, they +are widely used to hold user-supplied text strings. This lets you add +annotations, titles, index terms, etc to your JPEG files, and later retrieve +them as text. COM blocks do not interfere with the image stored in the JPEG +file. The maximum size of a COM block is 64K, but you can have as many of +them as you like in one JPEG file. + +We provide two utility programs to display COM block contents and add COM +blocks to a JPEG file. + +rdjpgcom searches a JPEG file and prints the contents of any COM blocks on +standard output. The command line syntax is + rdjpgcom [-verbose] [inputfilename] +The switch "-verbose" (or just "-v") causes rdjpgcom to also display the JPEG +image dimensions. If you omit the input file name from the command line, +the JPEG file is read from standard input. (This may not work on some +operating systems, if binary data can't be read from stdin.) + +wrjpgcom adds a COM block, containing text you provide, to a JPEG file. +Ordinarily, the COM block is added after any existing COM blocks, but you +can delete the old COM blocks if you wish. wrjpgcom produces a new JPEG +file; it does not modify the input file. DO NOT try to overwrite the input +file by directing wrjpgcom's output back into it; on most systems this will +just destroy your file. + +The command line syntax for wrjpgcom is similar to cjpeg's. On Unix-like +systems, it is + wrjpgcom [switches] [inputfilename] +The output file is written to standard output. The input file comes from +the named file, or from standard input if no input file is named. + +On most non-Unix systems, the syntax is + wrjpgcom [switches] inputfilename outputfilename +where both input and output file names must be given explicitly. + +wrjpgcom understands three switches: + -replace Delete any existing COM blocks from the file. + -comment "Comment text" Supply new COM text on command line. + -cfile name Read text for new COM block from named file. +(Switch names can be abbreviated.) If you have only one line of comment text +to add, you can provide it on the command line with -comment. The comment +text must be surrounded with quotes so that it is treated as a single +argument. Longer comments can be read from a text file. + +If you give neither -comment nor -cfile, then wrjpgcom will read the comment +text from standard input. (In this case an input image file name MUST be +supplied, so that the source JPEG file comes from somewhere else.) You can +enter multiple lines, up to 64KB worth. Type an end-of-file indicator +(usually control-D or control-Z) to terminate the comment text entry. + +wrjpgcom will not add a COM block if the provided comment string is empty. +Therefore -replace -comment "" can be used to delete all COM blocks from a +file. + +These utility programs do not depend on the IJG JPEG library. In +particular, the source code for rdjpgcom is intended as an illustration of +the minimum amount of code required to parse a JPEG file header correctly. diff --git a/Engine/lib/ljpeg/extras/wizard.doc b/Engine/lib/ljpeg/extras/wizard.doc new file mode 100644 index 000000000..54170b227 --- /dev/null +++ b/Engine/lib/ljpeg/extras/wizard.doc @@ -0,0 +1,211 @@ +Advanced usage instructions for the Independent JPEG Group's JPEG software +========================================================================== + +This file describes cjpeg's "switches for wizards". + +The "wizard" switches are intended for experimentation with JPEG by persons +who are reasonably knowledgeable about the JPEG standard. If you don't know +what you are doing, DON'T USE THESE SWITCHES. You'll likely produce files +with worse image quality and/or poorer compression than you'd get from the +default settings. Furthermore, these switches must be used with caution +when making files intended for general use, because not all JPEG decoders +will support unusual JPEG parameter settings. + + +Quantization Table Adjustment +----------------------------- + +Ordinarily, cjpeg starts with a default set of tables (the same ones given +as examples in the JPEG standard) and scales them up or down according to +the -quality setting. The details of the scaling algorithm can be found in +jcparam.c. At very low quality settings, some quantization table entries +can get scaled up to values exceeding 255. Although 2-byte quantization +values are supported by the IJG software, this feature is not in baseline +JPEG and is not supported by all implementations. If you need to ensure +wide compatibility of low-quality files, you can constrain the scaled +quantization values to no more than 255 by giving the -baseline switch. +Note that use of -baseline will result in poorer quality for the same file +size, since more bits than necessary are expended on higher AC coefficients. + +You can substitute a different set of quantization values by using the +-qtables switch: + + -qtables file Use the quantization tables given in the named file. + +The specified file should be a text file containing decimal quantization +values. The file should contain one to four tables, each of 64 elements. +The tables are implicitly numbered 0,1,etc. in order of appearance. Table +entries appear in normal array order (NOT in the zigzag order in which they +will be stored in the JPEG file). + +Quantization table files are free format, in that arbitrary whitespace can +appear between numbers. Also, comments can be included: a comment starts +with '#' and extends to the end of the line. Here is an example file that +duplicates the default quantization tables: + + # Quantization tables given in JPEG spec, section K.1 + + # This is table 0 (the luminance table): + 16 11 10 16 24 40 51 61 + 12 12 14 19 26 58 60 55 + 14 13 16 24 40 57 69 56 + 14 17 22 29 51 87 80 62 + 18 22 37 56 68 109 103 77 + 24 35 55 64 81 104 113 92 + 49 64 78 87 103 121 120 101 + 72 92 95 98 112 100 103 99 + + # This is table 1 (the chrominance table): + 17 18 24 47 99 99 99 99 + 18 21 26 66 99 99 99 99 + 24 26 56 99 99 99 99 99 + 47 66 99 99 99 99 99 99 + 99 99 99 99 99 99 99 99 + 99 99 99 99 99 99 99 99 + 99 99 99 99 99 99 99 99 + 99 99 99 99 99 99 99 99 + +If the -qtables switch is used without -quality, then the specified tables +are used exactly as-is. If both -qtables and -quality are used, then the +tables taken from the file are scaled in the same fashion that the default +tables would be scaled for that quality setting. If -baseline appears, then +the quantization values are constrained to the range 1-255. + +By default, cjpeg will use quantization table 0 for luminance components and +table 1 for chrominance components. To override this choice, use the -qslots +switch: + + -qslots N[,...] Select which quantization table to use for + each color component. + +The -qslots switch specifies a quantization table number for each color +component, in the order in which the components appear in the JPEG SOF marker. +For example, to create a separate table for each of Y,Cb,Cr, you could +provide a -qtables file that defines three quantization tables and say +"-qslots 0,1,2". If -qslots gives fewer table numbers than there are color +components, then the last table number is repeated as necessary. + + +Sampling Factor Adjustment +-------------------------- + +By default, cjpeg uses 2:1 horizontal and vertical downsampling when +compressing YCbCr data, and no downsampling for all other color spaces. +You can override this default with the -sample switch: + + -sample HxV[,...] Set JPEG sampling factors for each color + component. + +The -sample switch specifies the JPEG sampling factors for each color +component, in the order in which they appear in the JPEG SOF marker. +If you specify fewer HxV pairs than there are components, the remaining +components are set to 1x1 sampling. For example, the default YCbCr setting +is equivalent to "-sample 2x2,1x1,1x1", which can be abbreviated to +"-sample 2x2". + +There are still some JPEG decoders in existence that support only 2x1 +sampling (also called 4:2:2 sampling). Compatibility with such decoders can +be achieved by specifying "-sample 2x1". This is not recommended unless +really necessary, since it increases file size and encoding/decoding time +with very little quality gain. + + +Multiple Scan / Progression Control +----------------------------------- + +By default, cjpeg emits a single-scan sequential JPEG file. The +-progressive switch generates a progressive JPEG file using a default series +of progression parameters. You can create multiple-scan sequential JPEG +files or progressive JPEG files with custom progression parameters by using +the -scans switch: + + -scans file Use the scan sequence given in the named file. + +The specified file should be a text file containing a "scan script". +The script specifies the contents and ordering of the scans to be emitted. +Each entry in the script defines one scan. A scan definition specifies +the components to be included in the scan, and for progressive JPEG it also +specifies the progression parameters Ss,Se,Ah,Al for the scan. Scan +definitions are separated by semicolons (';'). A semicolon after the last +scan definition is optional. + +Each scan definition contains one to four component indexes, optionally +followed by a colon (':') and the four progressive-JPEG parameters. The +component indexes denote which color component(s) are to be transmitted in +the scan. Components are numbered in the order in which they appear in the +JPEG SOF marker, with the first component being numbered 0. (Note that these +indexes are not the "component ID" codes assigned to the components, just +positional indexes.) + +The progression parameters for each scan are: + Ss Zigzag index of first coefficient included in scan + Se Zigzag index of last coefficient included in scan + Ah Zero for first scan of a coefficient, else Al of prior scan + Al Successive approximation low bit position for scan +If the progression parameters are omitted, the values 0,63,0,0 are used, +producing a sequential JPEG file. cjpeg automatically determines whether +the script represents a progressive or sequential file, by observing whether +Ss and Se values other than 0 and 63 appear. (The -progressive switch is +not needed to specify this; in fact, it is ignored when -scans appears.) +The scan script must meet the JPEG restrictions on progression sequences. +(cjpeg checks that the spec's requirements are obeyed.) + +Scan script files are free format, in that arbitrary whitespace can appear +between numbers and around punctuation. Also, comments can be included: a +comment starts with '#' and extends to the end of the line. For additional +legibility, commas or dashes can be placed between values. (Actually, any +single punctuation character other than ':' or ';' can be inserted.) For +example, the following two scan definitions are equivalent: + 0 1 2: 0 63 0 0; + 0,1,2 : 0-63, 0,0 ; + +Here is an example of a scan script that generates a partially interleaved +sequential JPEG file: + + 0; # Y only in first scan + 1 2; # Cb and Cr in second scan + +Here is an example of a progressive scan script using only spectral selection +(no successive approximation): + + # Interleaved DC scan for Y,Cb,Cr: + 0,1,2: 0-0, 0, 0 ; + # AC scans: + 0: 1-2, 0, 0 ; # First two Y AC coefficients + 0: 3-5, 0, 0 ; # Three more + 1: 1-63, 0, 0 ; # All AC coefficients for Cb + 2: 1-63, 0, 0 ; # All AC coefficients for Cr + 0: 6-9, 0, 0 ; # More Y coefficients + 0: 10-63, 0, 0 ; # Remaining Y coefficients + +Here is an example of a successive-approximation script. This is equivalent +to the default script used by "cjpeg -progressive" for YCbCr images: + + # Initial DC scan for Y,Cb,Cr (lowest bit not sent) + 0,1,2: 0-0, 0, 1 ; + # First AC scan: send first 5 Y AC coefficients, minus 2 lowest bits: + 0: 1-5, 0, 2 ; + # Send all Cr,Cb AC coefficients, minus lowest bit: + # (chroma data is usually too small to be worth subdividing further; + # but note we send Cr first since eye is least sensitive to Cb) + 2: 1-63, 0, 1 ; + 1: 1-63, 0, 1 ; + # Send remaining Y AC coefficients, minus 2 lowest bits: + 0: 6-63, 0, 2 ; + # Send next-to-lowest bit of all Y AC coefficients: + 0: 1-63, 2, 1 ; + # At this point we've sent all but the lowest bit of all coefficients. + # Send lowest bit of DC coefficients + 0,1,2: 0-0, 1, 0 ; + # Send lowest bit of AC coefficients + 2: 1-63, 1, 0 ; + 1: 1-63, 1, 0 ; + # Y AC lowest bit scan is last; it's usually the largest scan + 0: 1-63, 1, 0 ; + +It may be worth pointing out that this script is tuned for quality settings +of around 50 to 75. For lower quality settings, you'd probably want to use +a script with fewer stages of successive approximation (otherwise the +initial scans will be really bad). For higher quality settings, you might +want to use more stages of successive approximation (so that the initial +scans are not too large). diff --git a/Engine/lib/ljpeg/extras/wrbmp.c b/Engine/lib/ljpeg/extras/wrbmp.c new file mode 100644 index 000000000..3283b0f15 --- /dev/null +++ b/Engine/lib/ljpeg/extras/wrbmp.c @@ -0,0 +1,442 @@ +/* + * wrbmp.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to write output images in Microsoft "BMP" + * format (MS Windows 3.x and OS/2 1.x flavors). + * Either 8-bit colormapped or 24-bit full-color format can be written. + * No compression is supported. + * + * These routines may need modification for non-Unix environments or + * specialized applications. As they stand, they assume output to + * an ordinary stdio stream. + * + * This code contributed by James Arthur Boucher. + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + +#ifdef BMP_SUPPORTED + + +/* + * To support 12-bit JPEG data, we'd have to scale output down to 8 bits. + * This is not yet implemented. + */ + +#if BITS_IN_JSAMPLE != 8 + Sorry, this code only copes with 8-bit JSAMPLEs. /* deliberate syntax err */ +#endif + +/* + * Since BMP stores scanlines bottom-to-top, we have to invert the image + * from JPEG's top-to-bottom order. To do this, we save the outgoing data + * in a virtual array during put_pixel_row calls, then actually emit the + * BMP file during finish_output. The virtual array contains one JSAMPLE per + * pixel if the output is grayscale or colormapped, three if it is full color. + */ + +/* Private version of data destination object */ + +typedef struct { + struct djpeg_dest_struct pub; /* public fields */ + + boolean is_os2; /* saves the OS2 format request flag */ + + jvirt_sarray_ptr whole_image; /* needed to reverse row order */ + JDIMENSION data_width; /* JSAMPLEs per row */ + JDIMENSION row_width; /* physical width of one row in the BMP file */ + int pad_bytes; /* number of padding bytes needed per row */ + JDIMENSION cur_output_row; /* next row# to write to virtual array */ +} bmp_dest_struct; + +typedef bmp_dest_struct * bmp_dest_ptr; + + +/* Forward declarations */ +LOCAL(void) write_colormap + JPP((j_decompress_ptr cinfo, bmp_dest_ptr dest, + int map_colors, int map_entry_size)); + + +/* + * Write some pixel data. + * In this module rows_supplied will always be 1. + */ + +METHODDEF(void) +put_pixel_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + JDIMENSION rows_supplied) +/* This version is for writing 24-bit pixels */ +{ + bmp_dest_ptr dest = (bmp_dest_ptr) dinfo; + JSAMPARRAY image_ptr; + register JSAMPROW inptr, outptr; + register JDIMENSION col; + int pad; + + /* Access next row in virtual array */ + image_ptr = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, dest->whole_image, + dest->cur_output_row, (JDIMENSION) 1, TRUE); + dest->cur_output_row++; + + /* Transfer data. Note destination values must be in BGR order + * (even though Microsoft's own documents say the opposite). + */ + inptr = dest->pub.buffer[0]; + outptr = image_ptr[0]; + for (col = cinfo->output_width; col > 0; col--) { + outptr[2] = *inptr++; /* can omit GETJSAMPLE() safely */ + outptr[1] = *inptr++; + outptr[0] = *inptr++; + outptr += 3; + } + + /* Zero out the pad bytes. */ + pad = dest->pad_bytes; + while (--pad >= 0) + *outptr++ = 0; +} + +METHODDEF(void) +put_gray_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + JDIMENSION rows_supplied) +/* This version is for grayscale OR quantized color output */ +{ + bmp_dest_ptr dest = (bmp_dest_ptr) dinfo; + JSAMPARRAY image_ptr; + register JSAMPROW inptr, outptr; + register JDIMENSION col; + int pad; + + /* Access next row in virtual array */ + image_ptr = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, dest->whole_image, + dest->cur_output_row, (JDIMENSION) 1, TRUE); + dest->cur_output_row++; + + /* Transfer data. */ + inptr = dest->pub.buffer[0]; + outptr = image_ptr[0]; + for (col = cinfo->output_width; col > 0; col--) { + *outptr++ = *inptr++; /* can omit GETJSAMPLE() safely */ + } + + /* Zero out the pad bytes. */ + pad = dest->pad_bytes; + while (--pad >= 0) + *outptr++ = 0; +} + + +/* + * Startup: normally writes the file header. + * In this module we may as well postpone everything until finish_output. + */ + +METHODDEF(void) +start_output_bmp (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) +{ + /* no work here */ +} + + +/* + * Finish up at the end of the file. + * + * Here is where we really output the BMP file. + * + * First, routines to write the Windows and OS/2 variants of the file header. + */ + +LOCAL(void) +write_bmp_header (j_decompress_ptr cinfo, bmp_dest_ptr dest) +/* Write a Windows-style BMP file header, including colormap if needed */ +{ + char bmpfileheader[14]; + char bmpinfoheader[40]; +#define PUT_2B(array,offset,value) \ + (array[offset] = (char) ((value) & 0xFF), \ + array[offset+1] = (char) (((value) >> 8) & 0xFF)) +#define PUT_4B(array,offset,value) \ + (array[offset] = (char) ((value) & 0xFF), \ + array[offset+1] = (char) (((value) >> 8) & 0xFF), \ + array[offset+2] = (char) (((value) >> 16) & 0xFF), \ + array[offset+3] = (char) (((value) >> 24) & 0xFF)) + INT32 headersize, bfSize; + int bits_per_pixel, cmap_entries; + + /* Compute colormap size and total file size */ + if (cinfo->out_color_space == JCS_RGB) { + if (cinfo->quantize_colors) { + /* Colormapped RGB */ + bits_per_pixel = 8; + cmap_entries = 256; + } else { + /* Unquantized, full color RGB */ + bits_per_pixel = 24; + cmap_entries = 0; + } + } else { + /* Grayscale output. We need to fake a 256-entry colormap. */ + bits_per_pixel = 8; + cmap_entries = 256; + } + /* File size */ + headersize = 14 + 40 + cmap_entries * 4; /* Header and colormap */ + bfSize = headersize + (INT32) dest->row_width * (INT32) cinfo->output_height; + + /* Set unused fields of header to 0 */ + MEMZERO(bmpfileheader, SIZEOF(bmpfileheader)); + MEMZERO(bmpinfoheader, SIZEOF(bmpinfoheader)); + + /* Fill the file header */ + bmpfileheader[0] = 0x42; /* first 2 bytes are ASCII 'B', 'M' */ + bmpfileheader[1] = 0x4D; + PUT_4B(bmpfileheader, 2, bfSize); /* bfSize */ + /* we leave bfReserved1 & bfReserved2 = 0 */ + PUT_4B(bmpfileheader, 10, headersize); /* bfOffBits */ + + /* Fill the info header (Microsoft calls this a BITMAPINFOHEADER) */ + PUT_2B(bmpinfoheader, 0, 40); /* biSize */ + PUT_4B(bmpinfoheader, 4, cinfo->output_width); /* biWidth */ + PUT_4B(bmpinfoheader, 8, cinfo->output_height); /* biHeight */ + PUT_2B(bmpinfoheader, 12, 1); /* biPlanes - must be 1 */ + PUT_2B(bmpinfoheader, 14, bits_per_pixel); /* biBitCount */ + /* we leave biCompression = 0, for none */ + /* we leave biSizeImage = 0; this is correct for uncompressed data */ + if (cinfo->density_unit == 2) { /* if have density in dots/cm, then */ + PUT_4B(bmpinfoheader, 24, (INT32) (cinfo->X_density*100)); /* XPels/M */ + PUT_4B(bmpinfoheader, 28, (INT32) (cinfo->Y_density*100)); /* XPels/M */ + } + PUT_2B(bmpinfoheader, 32, cmap_entries); /* biClrUsed */ + /* we leave biClrImportant = 0 */ + + if (JFWRITE(dest->pub.output_file, bmpfileheader, 14) != (size_t) 14) + ERREXIT(cinfo, JERR_FILE_WRITE); + if (JFWRITE(dest->pub.output_file, bmpinfoheader, 40) != (size_t) 40) + ERREXIT(cinfo, JERR_FILE_WRITE); + + if (cmap_entries > 0) + write_colormap(cinfo, dest, cmap_entries, 4); +} + + +LOCAL(void) +write_os2_header (j_decompress_ptr cinfo, bmp_dest_ptr dest) +/* Write an OS2-style BMP file header, including colormap if needed */ +{ + char bmpfileheader[14]; + char bmpcoreheader[12]; + INT32 headersize, bfSize; + int bits_per_pixel, cmap_entries; + + /* Compute colormap size and total file size */ + if (cinfo->out_color_space == JCS_RGB) { + if (cinfo->quantize_colors) { + /* Colormapped RGB */ + bits_per_pixel = 8; + cmap_entries = 256; + } else { + /* Unquantized, full color RGB */ + bits_per_pixel = 24; + cmap_entries = 0; + } + } else { + /* Grayscale output. We need to fake a 256-entry colormap. */ + bits_per_pixel = 8; + cmap_entries = 256; + } + /* File size */ + headersize = 14 + 12 + cmap_entries * 3; /* Header and colormap */ + bfSize = headersize + (INT32) dest->row_width * (INT32) cinfo->output_height; + + /* Set unused fields of header to 0 */ + MEMZERO(bmpfileheader, SIZEOF(bmpfileheader)); + MEMZERO(bmpcoreheader, SIZEOF(bmpcoreheader)); + + /* Fill the file header */ + bmpfileheader[0] = 0x42; /* first 2 bytes are ASCII 'B', 'M' */ + bmpfileheader[1] = 0x4D; + PUT_4B(bmpfileheader, 2, bfSize); /* bfSize */ + /* we leave bfReserved1 & bfReserved2 = 0 */ + PUT_4B(bmpfileheader, 10, headersize); /* bfOffBits */ + + /* Fill the info header (Microsoft calls this a BITMAPCOREHEADER) */ + PUT_2B(bmpcoreheader, 0, 12); /* bcSize */ + PUT_2B(bmpcoreheader, 4, cinfo->output_width); /* bcWidth */ + PUT_2B(bmpcoreheader, 6, cinfo->output_height); /* bcHeight */ + PUT_2B(bmpcoreheader, 8, 1); /* bcPlanes - must be 1 */ + PUT_2B(bmpcoreheader, 10, bits_per_pixel); /* bcBitCount */ + + if (JFWRITE(dest->pub.output_file, bmpfileheader, 14) != (size_t) 14) + ERREXIT(cinfo, JERR_FILE_WRITE); + if (JFWRITE(dest->pub.output_file, bmpcoreheader, 12) != (size_t) 12) + ERREXIT(cinfo, JERR_FILE_WRITE); + + if (cmap_entries > 0) + write_colormap(cinfo, dest, cmap_entries, 3); +} + + +/* + * Write the colormap. + * Windows uses BGR0 map entries; OS/2 uses BGR entries. + */ + +LOCAL(void) +write_colormap (j_decompress_ptr cinfo, bmp_dest_ptr dest, + int map_colors, int map_entry_size) +{ + JSAMPARRAY colormap = cinfo->colormap; + int num_colors = cinfo->actual_number_of_colors; + FILE * outfile = dest->pub.output_file; + int i; + + if (colormap != NULL) { + if (cinfo->out_color_components == 3) { + /* Normal case with RGB colormap */ + for (i = 0; i < num_colors; i++) { + putc(GETJSAMPLE(colormap[2][i]), outfile); + putc(GETJSAMPLE(colormap[1][i]), outfile); + putc(GETJSAMPLE(colormap[0][i]), outfile); + if (map_entry_size == 4) + putc(0, outfile); + } + } else { + /* Grayscale colormap (only happens with grayscale quantization) */ + for (i = 0; i < num_colors; i++) { + putc(GETJSAMPLE(colormap[0][i]), outfile); + putc(GETJSAMPLE(colormap[0][i]), outfile); + putc(GETJSAMPLE(colormap[0][i]), outfile); + if (map_entry_size == 4) + putc(0, outfile); + } + } + } else { + /* If no colormap, must be grayscale data. Generate a linear "map". */ + for (i = 0; i < 256; i++) { + putc(i, outfile); + putc(i, outfile); + putc(i, outfile); + if (map_entry_size == 4) + putc(0, outfile); + } + } + /* Pad colormap with zeros to ensure specified number of colormap entries */ + if (i > map_colors) + ERREXIT1(cinfo, JERR_TOO_MANY_COLORS, i); + for (; i < map_colors; i++) { + putc(0, outfile); + putc(0, outfile); + putc(0, outfile); + if (map_entry_size == 4) + putc(0, outfile); + } +} + + +METHODDEF(void) +finish_output_bmp (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) +{ + bmp_dest_ptr dest = (bmp_dest_ptr) dinfo; + register FILE * outfile = dest->pub.output_file; + JSAMPARRAY image_ptr; + register JSAMPROW data_ptr; + JDIMENSION row; + register JDIMENSION col; + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; + + /* Write the header and colormap */ + if (dest->is_os2) + write_os2_header(cinfo, dest); + else + write_bmp_header(cinfo, dest); + + /* Write the file body from our virtual array */ + for (row = cinfo->output_height; row > 0; row--) { + if (progress != NULL) { + progress->pub.pass_counter = (long) (cinfo->output_height - row); + progress->pub.pass_limit = (long) cinfo->output_height; + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + } + image_ptr = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, dest->whole_image, row-1, (JDIMENSION) 1, FALSE); + data_ptr = image_ptr[0]; + for (col = dest->row_width; col > 0; col--) { + putc(GETJSAMPLE(*data_ptr), outfile); + data_ptr++; + } + } + if (progress != NULL) + progress->completed_extra_passes++; + + /* Make sure we wrote the output file OK */ + fflush(outfile); + if (ferror(outfile)) + ERREXIT(cinfo, JERR_FILE_WRITE); +} + + +/* + * The module selection routine for BMP format output. + */ + +GLOBAL(djpeg_dest_ptr) +jinit_write_bmp (j_decompress_ptr cinfo, boolean is_os2) +{ + bmp_dest_ptr dest; + JDIMENSION row_width; + + /* Create module interface object, fill in method pointers */ + dest = (bmp_dest_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(bmp_dest_struct)); + dest->pub.start_output = start_output_bmp; + dest->pub.finish_output = finish_output_bmp; + dest->is_os2 = is_os2; + + if (cinfo->out_color_space == JCS_GRAYSCALE) { + dest->pub.put_pixel_rows = put_gray_rows; + } else if (cinfo->out_color_space == JCS_RGB) { + if (cinfo->quantize_colors) + dest->pub.put_pixel_rows = put_gray_rows; + else + dest->pub.put_pixel_rows = put_pixel_rows; + } else { + ERREXIT(cinfo, JERR_BMP_COLORSPACE); + } + + /* Calculate output image dimensions so we can allocate space */ + jpeg_calc_output_dimensions(cinfo); + + /* Determine width of rows in the BMP file (padded to 4-byte boundary). */ + row_width = cinfo->output_width * cinfo->output_components; + dest->data_width = row_width; + while ((row_width & 3) != 0) row_width++; + dest->row_width = row_width; + dest->pad_bytes = (int) (row_width - dest->data_width); + + /* Allocate space for inversion array, prepare for write pass */ + dest->whole_image = (*cinfo->mem->request_virt_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + row_width, cinfo->output_height, (JDIMENSION) 1); + dest->cur_output_row = 0; + if (cinfo->progress != NULL) { + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; + progress->total_extra_passes++; /* count file input as separate pass */ + } + + /* Create decompressor output buffer. */ + dest->pub.buffer = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, row_width, (JDIMENSION) 1); + dest->pub.buffer_height = 1; + + return (djpeg_dest_ptr) dest; +} + +#endif /* BMP_SUPPORTED */ diff --git a/Engine/lib/ljpeg/extras/wrgif.c b/Engine/lib/ljpeg/extras/wrgif.c new file mode 100644 index 000000000..5fe832839 --- /dev/null +++ b/Engine/lib/ljpeg/extras/wrgif.c @@ -0,0 +1,399 @@ +/* + * wrgif.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to write output images in GIF format. + * + ************************************************************************** + * NOTE: to avoid entanglements with Unisys' patent on LZW compression, * + * this code has been modified to output "uncompressed GIF" files. * + * There is no trace of the LZW algorithm in this file. * + ************************************************************************** + * + * These routines may need modification for non-Unix environments or + * specialized applications. As they stand, they assume output to + * an ordinary stdio stream. + */ + +/* + * This code is loosely based on ppmtogif from the PBMPLUS distribution + * of Feb. 1991. That file contains the following copyright notice: + * Based on GIFENCODE by David Rowley . + * Lempel-Ziv compression based on "compress" by Spencer W. Thomas et al. + * Copyright (C) 1989 by Jef Poskanzer. + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose and without fee is hereby granted, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation. This software is provided "as is" without express or + * implied warranty. + * + * We are also required to state that + * "The Graphics Interchange Format(c) is the Copyright property of + * CompuServe Incorporated. GIF(sm) is a Service Mark property of + * CompuServe Incorporated." + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + +#ifdef GIF_SUPPORTED + + +/* Private version of data destination object */ + +typedef struct { + struct djpeg_dest_struct pub; /* public fields */ + + j_decompress_ptr cinfo; /* back link saves passing separate parm */ + + /* State for packing variable-width codes into a bitstream */ + int n_bits; /* current number of bits/code */ + int maxcode; /* maximum code, given n_bits */ + INT32 cur_accum; /* holds bits not yet output */ + int cur_bits; /* # of bits in cur_accum */ + + /* State for GIF code assignment */ + int ClearCode; /* clear code (doesn't change) */ + int EOFCode; /* EOF code (ditto) */ + int code_counter; /* counts output symbols */ + + /* GIF data packet construction buffer */ + int bytesinpkt; /* # of bytes in current packet */ + char packetbuf[256]; /* workspace for accumulating packet */ + +} gif_dest_struct; + +typedef gif_dest_struct * gif_dest_ptr; + +/* Largest value that will fit in N bits */ +#define MAXCODE(n_bits) ((1 << (n_bits)) - 1) + + +/* + * Routines to package finished data bytes into GIF data blocks. + * A data block consists of a count byte (1..255) and that many data bytes. + */ + +LOCAL(void) +flush_packet (gif_dest_ptr dinfo) +/* flush any accumulated data */ +{ + if (dinfo->bytesinpkt > 0) { /* never write zero-length packet */ + dinfo->packetbuf[0] = (char) dinfo->bytesinpkt++; + if (JFWRITE(dinfo->pub.output_file, dinfo->packetbuf, dinfo->bytesinpkt) + != (size_t) dinfo->bytesinpkt) + ERREXIT(dinfo->cinfo, JERR_FILE_WRITE); + dinfo->bytesinpkt = 0; + } +} + + +/* Add a character to current packet; flush to disk if necessary */ +#define CHAR_OUT(dinfo,c) \ + { (dinfo)->packetbuf[++(dinfo)->bytesinpkt] = (char) (c); \ + if ((dinfo)->bytesinpkt >= 255) \ + flush_packet(dinfo); \ + } + + +/* Routine to convert variable-width codes into a byte stream */ + +LOCAL(void) +output (gif_dest_ptr dinfo, int code) +/* Emit a code of n_bits bits */ +/* Uses cur_accum and cur_bits to reblock into 8-bit bytes */ +{ + dinfo->cur_accum |= ((INT32) code) << dinfo->cur_bits; + dinfo->cur_bits += dinfo->n_bits; + + while (dinfo->cur_bits >= 8) { + CHAR_OUT(dinfo, dinfo->cur_accum & 0xFF); + dinfo->cur_accum >>= 8; + dinfo->cur_bits -= 8; + } +} + + +/* The pseudo-compression algorithm. + * + * In this module we simply output each pixel value as a separate symbol; + * thus, no compression occurs. In fact, there is expansion of one bit per + * pixel, because we use a symbol width one bit wider than the pixel width. + * + * GIF ordinarily uses variable-width symbols, and the decoder will expect + * to ratchet up the symbol width after a fixed number of symbols. + * To simplify the logic and keep the expansion penalty down, we emit a + * GIF Clear code to reset the decoder just before the width would ratchet up. + * Thus, all the symbols in the output file will have the same bit width. + * Note that emitting the Clear codes at the right times is a mere matter of + * counting output symbols and is in no way dependent on the LZW patent. + * + * With a small basic pixel width (low color count), Clear codes will be + * needed very frequently, causing the file to expand even more. So this + * simplistic approach wouldn't work too well on bilevel images, for example. + * But for output of JPEG conversions the pixel width will usually be 8 bits + * (129 to 256 colors), so the overhead added by Clear symbols is only about + * one symbol in every 256. + */ + +LOCAL(void) +compress_init (gif_dest_ptr dinfo, int i_bits) +/* Initialize pseudo-compressor */ +{ + /* init all the state variables */ + dinfo->n_bits = i_bits; + dinfo->maxcode = MAXCODE(dinfo->n_bits); + dinfo->ClearCode = (1 << (i_bits - 1)); + dinfo->EOFCode = dinfo->ClearCode + 1; + dinfo->code_counter = dinfo->ClearCode + 2; + /* init output buffering vars */ + dinfo->bytesinpkt = 0; + dinfo->cur_accum = 0; + dinfo->cur_bits = 0; + /* GIF specifies an initial Clear code */ + output(dinfo, dinfo->ClearCode); +} + + +LOCAL(void) +compress_pixel (gif_dest_ptr dinfo, int c) +/* Accept and "compress" one pixel value. + * The given value must be less than n_bits wide. + */ +{ + /* Output the given pixel value as a symbol. */ + output(dinfo, c); + /* Issue Clear codes often enough to keep the reader from ratcheting up + * its symbol size. + */ + if (dinfo->code_counter < dinfo->maxcode) { + dinfo->code_counter++; + } else { + output(dinfo, dinfo->ClearCode); + dinfo->code_counter = dinfo->ClearCode + 2; /* reset the counter */ + } +} + + +LOCAL(void) +compress_term (gif_dest_ptr dinfo) +/* Clean up at end */ +{ + /* Send an EOF code */ + output(dinfo, dinfo->EOFCode); + /* Flush the bit-packing buffer */ + if (dinfo->cur_bits > 0) { + CHAR_OUT(dinfo, dinfo->cur_accum & 0xFF); + } + /* Flush the packet buffer */ + flush_packet(dinfo); +} + + +/* GIF header construction */ + + +LOCAL(void) +put_word (gif_dest_ptr dinfo, unsigned int w) +/* Emit a 16-bit word, LSB first */ +{ + putc(w & 0xFF, dinfo->pub.output_file); + putc((w >> 8) & 0xFF, dinfo->pub.output_file); +} + + +LOCAL(void) +put_3bytes (gif_dest_ptr dinfo, int val) +/* Emit 3 copies of same byte value --- handy subr for colormap construction */ +{ + putc(val, dinfo->pub.output_file); + putc(val, dinfo->pub.output_file); + putc(val, dinfo->pub.output_file); +} + + +LOCAL(void) +emit_header (gif_dest_ptr dinfo, int num_colors, JSAMPARRAY colormap) +/* Output the GIF file header, including color map */ +/* If colormap==NULL, synthesize a gray-scale colormap */ +{ + int BitsPerPixel, ColorMapSize, InitCodeSize, FlagByte; + int cshift = dinfo->cinfo->data_precision - 8; + int i; + + if (num_colors > 256) + ERREXIT1(dinfo->cinfo, JERR_TOO_MANY_COLORS, num_colors); + /* Compute bits/pixel and related values */ + BitsPerPixel = 1; + while (num_colors > (1 << BitsPerPixel)) + BitsPerPixel++; + ColorMapSize = 1 << BitsPerPixel; + if (BitsPerPixel <= 1) + InitCodeSize = 2; + else + InitCodeSize = BitsPerPixel; + /* + * Write the GIF header. + * Note that we generate a plain GIF87 header for maximum compatibility. + */ + putc('G', dinfo->pub.output_file); + putc('I', dinfo->pub.output_file); + putc('F', dinfo->pub.output_file); + putc('8', dinfo->pub.output_file); + putc('7', dinfo->pub.output_file); + putc('a', dinfo->pub.output_file); + /* Write the Logical Screen Descriptor */ + put_word(dinfo, (unsigned int) dinfo->cinfo->output_width); + put_word(dinfo, (unsigned int) dinfo->cinfo->output_height); + FlagByte = 0x80; /* Yes, there is a global color table */ + FlagByte |= (BitsPerPixel-1) << 4; /* color resolution */ + FlagByte |= (BitsPerPixel-1); /* size of global color table */ + putc(FlagByte, dinfo->pub.output_file); + putc(0, dinfo->pub.output_file); /* Background color index */ + putc(0, dinfo->pub.output_file); /* Reserved (aspect ratio in GIF89) */ + /* Write the Global Color Map */ + /* If the color map is more than 8 bits precision, */ + /* we reduce it to 8 bits by shifting */ + for (i=0; i < ColorMapSize; i++) { + if (i < num_colors) { + if (colormap != NULL) { + if (dinfo->cinfo->out_color_space == JCS_RGB) { + /* Normal case: RGB color map */ + putc(GETJSAMPLE(colormap[0][i]) >> cshift, dinfo->pub.output_file); + putc(GETJSAMPLE(colormap[1][i]) >> cshift, dinfo->pub.output_file); + putc(GETJSAMPLE(colormap[2][i]) >> cshift, dinfo->pub.output_file); + } else { + /* Grayscale "color map": possible if quantizing grayscale image */ + put_3bytes(dinfo, GETJSAMPLE(colormap[0][i]) >> cshift); + } + } else { + /* Create a gray-scale map of num_colors values, range 0..255 */ + put_3bytes(dinfo, (i * 255 + (num_colors-1)/2) / (num_colors-1)); + } + } else { + /* fill out the map to a power of 2 */ + put_3bytes(dinfo, 0); + } + } + /* Write image separator and Image Descriptor */ + putc(',', dinfo->pub.output_file); /* separator */ + put_word(dinfo, 0); /* left/top offset */ + put_word(dinfo, 0); + put_word(dinfo, (unsigned int) dinfo->cinfo->output_width); /* image size */ + put_word(dinfo, (unsigned int) dinfo->cinfo->output_height); + /* flag byte: not interlaced, no local color map */ + putc(0x00, dinfo->pub.output_file); + /* Write Initial Code Size byte */ + putc(InitCodeSize, dinfo->pub.output_file); + + /* Initialize for "compression" of image data */ + compress_init(dinfo, InitCodeSize+1); +} + + +/* + * Startup: write the file header. + */ + +METHODDEF(void) +start_output_gif (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) +{ + gif_dest_ptr dest = (gif_dest_ptr) dinfo; + + if (cinfo->quantize_colors) + emit_header(dest, cinfo->actual_number_of_colors, cinfo->colormap); + else + emit_header(dest, 256, (JSAMPARRAY) NULL); +} + + +/* + * Write some pixel data. + * In this module rows_supplied will always be 1. + */ + +METHODDEF(void) +put_pixel_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + JDIMENSION rows_supplied) +{ + gif_dest_ptr dest = (gif_dest_ptr) dinfo; + register JSAMPROW ptr; + register JDIMENSION col; + + ptr = dest->pub.buffer[0]; + for (col = cinfo->output_width; col > 0; col--) { + compress_pixel(dest, GETJSAMPLE(*ptr++)); + } +} + + +/* + * Finish up at the end of the file. + */ + +METHODDEF(void) +finish_output_gif (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) +{ + gif_dest_ptr dest = (gif_dest_ptr) dinfo; + + /* Flush "compression" mechanism */ + compress_term(dest); + /* Write a zero-length data block to end the series */ + putc(0, dest->pub.output_file); + /* Write the GIF terminator mark */ + putc(';', dest->pub.output_file); + /* Make sure we wrote the output file OK */ + fflush(dest->pub.output_file); + if (ferror(dest->pub.output_file)) + ERREXIT(cinfo, JERR_FILE_WRITE); +} + + +/* + * The module selection routine for GIF format output. + */ + +GLOBAL(djpeg_dest_ptr) +jinit_write_gif (j_decompress_ptr cinfo) +{ + gif_dest_ptr dest; + + /* Create module interface object, fill in method pointers */ + dest = (gif_dest_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(gif_dest_struct)); + dest->cinfo = cinfo; /* make back link for subroutines */ + dest->pub.start_output = start_output_gif; + dest->pub.put_pixel_rows = put_pixel_rows; + dest->pub.finish_output = finish_output_gif; + + if (cinfo->out_color_space != JCS_GRAYSCALE && + cinfo->out_color_space != JCS_RGB) + ERREXIT(cinfo, JERR_GIF_COLORSPACE); + + /* Force quantization if color or if > 8 bits input */ + if (cinfo->out_color_space != JCS_GRAYSCALE || cinfo->data_precision > 8) { + /* Force quantization to at most 256 colors */ + cinfo->quantize_colors = TRUE; + if (cinfo->desired_number_of_colors > 256) + cinfo->desired_number_of_colors = 256; + } + + /* Calculate output image dimensions so we can allocate space */ + jpeg_calc_output_dimensions(cinfo); + + if (cinfo->output_components != 1) /* safety check: just one component? */ + ERREXIT(cinfo, JERR_GIF_BUG); + + /* Create decompressor output buffer. */ + dest->pub.buffer = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, cinfo->output_width, (JDIMENSION) 1); + dest->pub.buffer_height = 1; + + return (djpeg_dest_ptr) dest; +} + +#endif /* GIF_SUPPORTED */ diff --git a/Engine/lib/ljpeg/extras/wrjpgcom.1 b/Engine/lib/ljpeg/extras/wrjpgcom.1 new file mode 100644 index 000000000..d419a9999 --- /dev/null +++ b/Engine/lib/ljpeg/extras/wrjpgcom.1 @@ -0,0 +1,103 @@ +.TH WRJPGCOM 1 "15 June 1995" +.SH NAME +wrjpgcom \- insert text comments into a JPEG file +.SH SYNOPSIS +.B wrjpgcom +[ +.B \-replace +] +[ +.BI \-comment " text" +] +[ +.BI \-cfile " name" +] +[ +.I filename +] +.LP +.SH DESCRIPTION +.LP +.B wrjpgcom +reads the named JPEG/JFIF file, or the standard input if no file is named, +and generates a new JPEG/JFIF file on standard output. A comment block is +added to the file. +.PP +The JPEG standard allows "comment" (COM) blocks to occur within a JPEG file. +Although the standard doesn't actually define what COM blocks are for, they +are widely used to hold user-supplied text strings. This lets you add +annotations, titles, index terms, etc to your JPEG files, and later retrieve +them as text. COM blocks do not interfere with the image stored in the JPEG +file. The maximum size of a COM block is 64K, but you can have as many of +them as you like in one JPEG file. +.PP +.B wrjpgcom +adds a COM block, containing text you provide, to a JPEG file. +Ordinarily, the COM block is added after any existing COM blocks; but you +can delete the old COM blocks if you wish. +.SH OPTIONS +Switch names may be abbreviated, and are not case sensitive. +.TP +.B \-replace +Delete any existing COM blocks from the file. +.TP +.BI \-comment " text" +Supply text for new COM block on command line. +.TP +.BI \-cfile " name" +Read text for new COM block from named file. +.PP +If you have only one line of comment text to add, you can provide it on the +command line with +.BR \-comment . +The comment text must be surrounded with quotes so that it is treated as a +single argument. Longer comments can be read from a text file. +.PP +If you give neither +.B \-comment +nor +.BR \-cfile , +then +.B wrjpgcom +will read the comment text from standard input. (In this case an input image +file name MUST be supplied, so that the source JPEG file comes from somewhere +else.) You can enter multiple lines, up to 64KB worth. Type an end-of-file +indicator (usually control-D) to terminate the comment text entry. +.PP +.B wrjpgcom +will not add a COM block if the provided comment string is empty. Therefore +\fB\-replace \-comment ""\fR can be used to delete all COM blocks from a file. +.SH EXAMPLES +.LP +Add a short comment to in.jpg, producing out.jpg: +.IP +.B wrjpgcom \-c +\fI"View of my back yard" in.jpg +.B > +.I out.jpg +.PP +Attach a long comment previously stored in comment.txt: +.IP +.B wrjpgcom +.I in.jpg +.B < +.I comment.txt +.B > +.I out.jpg +.PP +or equivalently +.IP +.B wrjpgcom +.B -cfile +.I comment.txt +.B < +.I in.jpg +.B > +.I out.jpg +.SH SEE ALSO +.BR cjpeg (1), +.BR djpeg (1), +.BR jpegtran (1), +.BR rdjpgcom (1) +.SH AUTHOR +Independent JPEG Group diff --git a/Engine/lib/ljpeg/extras/wrjpgcom.c b/Engine/lib/ljpeg/extras/wrjpgcom.c new file mode 100644 index 000000000..8c04b0551 --- /dev/null +++ b/Engine/lib/ljpeg/extras/wrjpgcom.c @@ -0,0 +1,583 @@ +/* + * wrjpgcom.c + * + * Copyright (C) 1994-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a very simple stand-alone application that inserts + * user-supplied text as a COM (comment) marker in a JFIF file. + * This may be useful as an example of the minimum logic needed to parse + * JPEG markers. + */ + +#define JPEG_CJPEG_DJPEG /* to get the command-line config symbols */ +#include "jinclude.h" /* get auto-config symbols, */ + +#ifndef HAVE_STDLIB_H /* should declare malloc() */ +extern void * malloc (); +#endif +#include /* to declare isupper(), tolower() */ +#ifdef USE_SETMODE +#include /* to declare setmode()'s parameter macros */ +/* If you have setmode() but not , just delete this line: */ +#include /* to declare setmode() */ +#endif + +#ifdef USE_CCOMMAND /* command-line reader for Macintosh */ +#ifdef __MWERKS__ +#include /* Metrowerks needs this */ +#include /* ... and this */ +#endif +#ifdef THINK_C +#include /* Think declares it here */ +#endif +#endif + +#ifdef DONT_USE_B_MODE /* define mode parameters for fopen() */ +#define READ_BINARY "r" +#define WRITE_BINARY "w" +#else +#ifdef VMS /* VMS is very nonstandard */ +#define READ_BINARY "rb", "ctx=stm" +#define WRITE_BINARY "wb", "ctx=stm" +#else /* standard ANSI-compliant case */ +#define READ_BINARY "rb" +#define WRITE_BINARY "wb" +#endif +#endif + +#ifndef EXIT_FAILURE /* define exit() codes if not provided */ +#define EXIT_FAILURE 1 +#endif +#ifndef EXIT_SUCCESS +#ifdef VMS +#define EXIT_SUCCESS 1 /* VMS is very nonstandard */ +#else +#define EXIT_SUCCESS 0 +#endif +#endif + +/* Reduce this value if your malloc() can't allocate blocks up to 64K. + * On DOS, compiling in large model is usually a better solution. + */ + +#ifndef MAX_COM_LENGTH +#define MAX_COM_LENGTH 65000L /* must be <= 65533 in any case */ +#endif + + +/* + * These macros are used to read the input file and write the output file. + * To reuse this code in another application, you might need to change these. + */ + +static FILE * infile; /* input JPEG file */ + +/* Return next input byte, or EOF if no more */ +#define NEXTBYTE() getc(infile) + +static FILE * outfile; /* output JPEG file */ + +/* Emit an output byte */ +#define PUTBYTE(x) putc((x), outfile) + + +/* Error exit handler */ +#define ERREXIT(msg) (fprintf(stderr, "%s\n", msg), exit(EXIT_FAILURE)) + + +/* Read one byte, testing for EOF */ +static int +read_1_byte (void) +{ + int c; + + c = NEXTBYTE(); + if (c == EOF) + ERREXIT("Premature EOF in JPEG file"); + return c; +} + +/* Read 2 bytes, convert to unsigned int */ +/* All 2-byte quantities in JPEG markers are MSB first */ +static unsigned int +read_2_bytes (void) +{ + int c1, c2; + + c1 = NEXTBYTE(); + if (c1 == EOF) + ERREXIT("Premature EOF in JPEG file"); + c2 = NEXTBYTE(); + if (c2 == EOF) + ERREXIT("Premature EOF in JPEG file"); + return (((unsigned int) c1) << 8) + ((unsigned int) c2); +} + + +/* Routines to write data to output file */ + +static void +write_1_byte (int c) +{ + PUTBYTE(c); +} + +static void +write_2_bytes (unsigned int val) +{ + PUTBYTE((val >> 8) & 0xFF); + PUTBYTE(val & 0xFF); +} + +static void +write_marker (int marker) +{ + PUTBYTE(0xFF); + PUTBYTE(marker); +} + +static void +copy_rest_of_file (void) +{ + int c; + + while ((c = NEXTBYTE()) != EOF) + PUTBYTE(c); +} + + +/* + * JPEG markers consist of one or more 0xFF bytes, followed by a marker + * code byte (which is not an FF). Here are the marker codes of interest + * in this program. (See jdmarker.c for a more complete list.) + */ + +#define M_SOF0 0xC0 /* Start Of Frame N */ +#define M_SOF1 0xC1 /* N indicates which compression process */ +#define M_SOF2 0xC2 /* Only SOF0-SOF2 are now in common use */ +#define M_SOF3 0xC3 +#define M_SOF5 0xC5 /* NB: codes C4 and CC are NOT SOF markers */ +#define M_SOF6 0xC6 +#define M_SOF7 0xC7 +#define M_SOF9 0xC9 +#define M_SOF10 0xCA +#define M_SOF11 0xCB +#define M_SOF13 0xCD +#define M_SOF14 0xCE +#define M_SOF15 0xCF +#define M_SOI 0xD8 /* Start Of Image (beginning of datastream) */ +#define M_EOI 0xD9 /* End Of Image (end of datastream) */ +#define M_SOS 0xDA /* Start Of Scan (begins compressed data) */ +#define M_COM 0xFE /* COMment */ + + +/* + * Find the next JPEG marker and return its marker code. + * We expect at least one FF byte, possibly more if the compressor used FFs + * to pad the file. (Padding FFs will NOT be replicated in the output file.) + * There could also be non-FF garbage between markers. The treatment of such + * garbage is unspecified; we choose to skip over it but emit a warning msg. + * NB: this routine must not be used after seeing SOS marker, since it will + * not deal correctly with FF/00 sequences in the compressed image data... + */ + +static int +next_marker (void) +{ + int c; + int discarded_bytes = 0; + + /* Find 0xFF byte; count and skip any non-FFs. */ + c = read_1_byte(); + while (c != 0xFF) { + discarded_bytes++; + c = read_1_byte(); + } + /* Get marker code byte, swallowing any duplicate FF bytes. Extra FFs + * are legal as pad bytes, so don't count them in discarded_bytes. + */ + do { + c = read_1_byte(); + } while (c == 0xFF); + + if (discarded_bytes != 0) { + fprintf(stderr, "Warning: garbage data found in JPEG file\n"); + } + + return c; +} + + +/* + * Read the initial marker, which should be SOI. + * For a JFIF file, the first two bytes of the file should be literally + * 0xFF M_SOI. To be more general, we could use next_marker, but if the + * input file weren't actually JPEG at all, next_marker might read the whole + * file and then return a misleading error message... + */ + +static int +first_marker (void) +{ + int c1, c2; + + c1 = NEXTBYTE(); + c2 = NEXTBYTE(); + if (c1 != 0xFF || c2 != M_SOI) + ERREXIT("Not a JPEG file"); + return c2; +} + + +/* + * Most types of marker are followed by a variable-length parameter segment. + * This routine skips over the parameters for any marker we don't otherwise + * want to process. + * Note that we MUST skip the parameter segment explicitly in order not to + * be fooled by 0xFF bytes that might appear within the parameter segment; + * such bytes do NOT introduce new markers. + */ + +static void +copy_variable (void) +/* Copy an unknown or uninteresting variable-length marker */ +{ + unsigned int length; + + /* Get the marker parameter length count */ + length = read_2_bytes(); + write_2_bytes(length); + /* Length includes itself, so must be at least 2 */ + if (length < 2) + ERREXIT("Erroneous JPEG marker length"); + length -= 2; + /* Skip over the remaining bytes */ + while (length > 0) { + write_1_byte(read_1_byte()); + length--; + } +} + +static void +skip_variable (void) +/* Skip over an unknown or uninteresting variable-length marker */ +{ + unsigned int length; + + /* Get the marker parameter length count */ + length = read_2_bytes(); + /* Length includes itself, so must be at least 2 */ + if (length < 2) + ERREXIT("Erroneous JPEG marker length"); + length -= 2; + /* Skip over the remaining bytes */ + while (length > 0) { + (void) read_1_byte(); + length--; + } +} + + +/* + * Parse the marker stream until SOFn or EOI is seen; + * copy data to output, but discard COM markers unless keep_COM is true. + */ + +static int +scan_JPEG_header (int keep_COM) +{ + int marker; + + /* Expect SOI at start of file */ + if (first_marker() != M_SOI) + ERREXIT("Expected SOI marker first"); + write_marker(M_SOI); + + /* Scan miscellaneous markers until we reach SOFn. */ + for (;;) { + marker = next_marker(); + switch (marker) { + /* Note that marker codes 0xC4, 0xC8, 0xCC are not, and must not be, + * treated as SOFn. C4 in particular is actually DHT. + */ + case M_SOF0: /* Baseline */ + case M_SOF1: /* Extended sequential, Huffman */ + case M_SOF2: /* Progressive, Huffman */ + case M_SOF3: /* Lossless, Huffman */ + case M_SOF5: /* Differential sequential, Huffman */ + case M_SOF6: /* Differential progressive, Huffman */ + case M_SOF7: /* Differential lossless, Huffman */ + case M_SOF9: /* Extended sequential, arithmetic */ + case M_SOF10: /* Progressive, arithmetic */ + case M_SOF11: /* Lossless, arithmetic */ + case M_SOF13: /* Differential sequential, arithmetic */ + case M_SOF14: /* Differential progressive, arithmetic */ + case M_SOF15: /* Differential lossless, arithmetic */ + return marker; + + case M_SOS: /* should not see compressed data before SOF */ + ERREXIT("SOS without prior SOFn"); + break; + + case M_EOI: /* in case it's a tables-only JPEG stream */ + return marker; + + case M_COM: /* Existing COM: conditionally discard */ + if (keep_COM) { + write_marker(marker); + copy_variable(); + } else { + skip_variable(); + } + break; + + default: /* Anything else just gets copied */ + write_marker(marker); + copy_variable(); /* we assume it has a parameter count... */ + break; + } + } /* end loop */ +} + + +/* Command line parsing code */ + +static const char * progname; /* program name for error messages */ + + +static void +usage (void) +/* complain about bad command line */ +{ + fprintf(stderr, "wrjpgcom inserts a textual comment in a JPEG file.\n"); + fprintf(stderr, "You can add to or replace any existing comment(s).\n"); + + fprintf(stderr, "Usage: %s [switches] ", progname); +#ifdef TWO_FILE_COMMANDLINE + fprintf(stderr, "inputfile outputfile\n"); +#else + fprintf(stderr, "[inputfile]\n"); +#endif + + fprintf(stderr, "Switches (names may be abbreviated):\n"); + fprintf(stderr, " -replace Delete any existing comments\n"); + fprintf(stderr, " -comment \"text\" Insert comment with given text\n"); + fprintf(stderr, " -cfile name Read comment from named file\n"); + fprintf(stderr, "Notice that you must put quotes around the comment text\n"); + fprintf(stderr, "when you use -comment.\n"); + fprintf(stderr, "If you do not give either -comment or -cfile on the command line,\n"); + fprintf(stderr, "then the comment text is read from standard input.\n"); + fprintf(stderr, "It can be multiple lines, up to %u characters total.\n", + (unsigned int) MAX_COM_LENGTH); +#ifndef TWO_FILE_COMMANDLINE + fprintf(stderr, "You must specify an input JPEG file name when supplying\n"); + fprintf(stderr, "comment text from standard input.\n"); +#endif + + exit(EXIT_FAILURE); +} + + +static int +keymatch (char * arg, const char * keyword, int minchars) +/* Case-insensitive matching of (possibly abbreviated) keyword switches. */ +/* keyword is the constant keyword (must be lower case already), */ +/* minchars is length of minimum legal abbreviation. */ +{ + register int ca, ck; + register int nmatched = 0; + + while ((ca = *arg++) != '\0') { + if ((ck = *keyword++) == '\0') + return 0; /* arg longer than keyword, no good */ + if (isupper(ca)) /* force arg to lcase (assume ck is already) */ + ca = tolower(ca); + if (ca != ck) + return 0; /* no good */ + nmatched++; /* count matched characters */ + } + /* reached end of argument; fail if it's too short for unique abbrev */ + if (nmatched < minchars) + return 0; + return 1; /* A-OK */ +} + + +/* + * The main program. + */ + +int +main (int argc, char **argv) +{ + int argn; + char * arg; + int keep_COM = 1; + char * comment_arg = NULL; + FILE * comment_file = NULL; + unsigned int comment_length = 0; + int marker; + + /* On Mac, fetch a command line. */ +#ifdef USE_CCOMMAND + argc = ccommand(&argv); +#endif + + progname = argv[0]; + if (progname == NULL || progname[0] == 0) + progname = "wrjpgcom"; /* in case C library doesn't provide it */ + + /* Parse switches, if any */ + for (argn = 1; argn < argc; argn++) { + arg = argv[argn]; + if (arg[0] != '-') + break; /* not switch, must be file name */ + arg++; /* advance over '-' */ + if (keymatch(arg, "replace", 1)) { + keep_COM = 0; + } else if (keymatch(arg, "cfile", 2)) { + if (++argn >= argc) usage(); + if ((comment_file = fopen(argv[argn], "r")) == NULL) { + fprintf(stderr, "%s: can't open %s\n", progname, argv[argn]); + exit(EXIT_FAILURE); + } + } else if (keymatch(arg, "comment", 1)) { + if (++argn >= argc) usage(); + comment_arg = argv[argn]; + /* If the comment text starts with '"', then we are probably running + * under MS-DOG and must parse out the quoted string ourselves. Sigh. + */ + if (comment_arg[0] == '"') { + comment_arg = (char *) malloc((size_t) MAX_COM_LENGTH); + if (comment_arg == NULL) + ERREXIT("Insufficient memory"); + strcpy(comment_arg, argv[argn]+1); + for (;;) { + comment_length = (unsigned int) strlen(comment_arg); + if (comment_length > 0 && comment_arg[comment_length-1] == '"') { + comment_arg[comment_length-1] = '\0'; /* zap terminating quote */ + break; + } + if (++argn >= argc) + ERREXIT("Missing ending quote mark"); + strcat(comment_arg, " "); + strcat(comment_arg, argv[argn]); + } + } + comment_length = (unsigned int) strlen(comment_arg); + } else + usage(); + } + + /* Cannot use both -comment and -cfile. */ + if (comment_arg != NULL && comment_file != NULL) + usage(); + /* If there is neither -comment nor -cfile, we will read the comment text + * from stdin; in this case there MUST be an input JPEG file name. + */ + if (comment_arg == NULL && comment_file == NULL && argn >= argc) + usage(); + + /* Open the input file. */ + if (argn < argc) { + if ((infile = fopen(argv[argn], READ_BINARY)) == NULL) { + fprintf(stderr, "%s: can't open %s\n", progname, argv[argn]); + exit(EXIT_FAILURE); + } + } else { + /* default input file is stdin */ +#ifdef USE_SETMODE /* need to hack file mode? */ + setmode(fileno(stdin), O_BINARY); +#endif +#ifdef USE_FDOPEN /* need to re-open in binary mode? */ + if ((infile = fdopen(fileno(stdin), READ_BINARY)) == NULL) { + fprintf(stderr, "%s: can't open stdin\n", progname); + exit(EXIT_FAILURE); + } +#else + infile = stdin; +#endif + } + + /* Open the output file. */ +#ifdef TWO_FILE_COMMANDLINE + /* Must have explicit output file name */ + if (argn != argc-2) { + fprintf(stderr, "%s: must name one input and one output file\n", + progname); + usage(); + } + if ((outfile = fopen(argv[argn+1], WRITE_BINARY)) == NULL) { + fprintf(stderr, "%s: can't open %s\n", progname, argv[argn+1]); + exit(EXIT_FAILURE); + } +#else + /* Unix style: expect zero or one file name */ + if (argn < argc-1) { + fprintf(stderr, "%s: only one input file\n", progname); + usage(); + } + /* default output file is stdout */ +#ifdef USE_SETMODE /* need to hack file mode? */ + setmode(fileno(stdout), O_BINARY); +#endif +#ifdef USE_FDOPEN /* need to re-open in binary mode? */ + if ((outfile = fdopen(fileno(stdout), WRITE_BINARY)) == NULL) { + fprintf(stderr, "%s: can't open stdout\n", progname); + exit(EXIT_FAILURE); + } +#else + outfile = stdout; +#endif +#endif /* TWO_FILE_COMMANDLINE */ + + /* Collect comment text from comment_file or stdin, if necessary */ + if (comment_arg == NULL) { + FILE * src_file; + int c; + + comment_arg = (char *) malloc((size_t) MAX_COM_LENGTH); + if (comment_arg == NULL) + ERREXIT("Insufficient memory"); + comment_length = 0; + src_file = (comment_file != NULL ? comment_file : stdin); + while ((c = getc(src_file)) != EOF) { + if (comment_length >= (unsigned int) MAX_COM_LENGTH) { + fprintf(stderr, "Comment text may not exceed %u bytes\n", + (unsigned int) MAX_COM_LENGTH); + exit(EXIT_FAILURE); + } + comment_arg[comment_length++] = (char) c; + } + if (comment_file != NULL) + fclose(comment_file); + } + + /* Copy JPEG headers until SOFn marker; + * we will insert the new comment marker just before SOFn. + * This (a) causes the new comment to appear after, rather than before, + * existing comments; and (b) ensures that comments come after any JFIF + * or JFXX markers, as required by the JFIF specification. + */ + marker = scan_JPEG_header(keep_COM); + /* Insert the new COM marker, but only if nonempty text has been supplied */ + if (comment_length > 0) { + write_marker(M_COM); + write_2_bytes(comment_length + 2); + while (comment_length > 0) { + write_1_byte(*comment_arg++); + comment_length--; + } + } + /* Duplicate the remainder of the source file. + * Note that any COM markers occuring after SOF will not be touched. + */ + write_marker(marker); + copy_rest_of_file(); + + /* All done. */ + exit(EXIT_SUCCESS); + return 0; /* suppress no-return-value warnings */ +} diff --git a/Engine/lib/ljpeg/extras/wrppm.c b/Engine/lib/ljpeg/extras/wrppm.c new file mode 100644 index 000000000..6c6d90881 --- /dev/null +++ b/Engine/lib/ljpeg/extras/wrppm.c @@ -0,0 +1,268 @@ +/* + * wrppm.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to write output images in PPM/PGM format. + * The extended 2-byte-per-sample raw PPM/PGM formats are supported. + * The PBMPLUS library is NOT required to compile this software + * (but it is highly useful as a set of PPM image manipulation programs). + * + * These routines may need modification for non-Unix environments or + * specialized applications. As they stand, they assume output to + * an ordinary stdio stream. + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + +#ifdef PPM_SUPPORTED + + +/* + * For 12-bit JPEG data, we either downscale the values to 8 bits + * (to write standard byte-per-sample PPM/PGM files), or output + * nonstandard word-per-sample PPM/PGM files. Downscaling is done + * if PPM_NORAWWORD is defined (this can be done in the Makefile + * or in jconfig.h). + * (When the core library supports data precision reduction, a cleaner + * implementation will be to ask for that instead.) + */ + +#if BITS_IN_JSAMPLE == 8 +#define PUTPPMSAMPLE(ptr,v) *ptr++ = (char) (v) +#define BYTESPERSAMPLE 1 +#define PPM_MAXVAL 255 +#else +#ifdef PPM_NORAWWORD +#define PUTPPMSAMPLE(ptr,v) *ptr++ = (char) ((v) >> (BITS_IN_JSAMPLE-8)) +#define BYTESPERSAMPLE 1 +#define PPM_MAXVAL 255 +#else +/* The word-per-sample format always puts the LSB first. */ +#define PUTPPMSAMPLE(ptr,v) \ + { register int val_ = v; \ + *ptr++ = (char) (val_ & 0xFF); \ + *ptr++ = (char) ((val_ >> 8) & 0xFF); \ + } +#define BYTESPERSAMPLE 2 +#define PPM_MAXVAL ((1<pub.output_file, dest->iobuffer, dest->buffer_width); +} + + +/* + * This code is used when we have to copy the data and apply a pixel + * format translation. Typically this only happens in 12-bit mode. + */ + +METHODDEF(void) +copy_pixel_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + JDIMENSION rows_supplied) +{ + ppm_dest_ptr dest = (ppm_dest_ptr) dinfo; + register char * bufferptr; + register JSAMPROW ptr; + register JDIMENSION col; + + ptr = dest->pub.buffer[0]; + bufferptr = dest->iobuffer; + for (col = dest->samples_per_row; col > 0; col--) { + PUTPPMSAMPLE(bufferptr, GETJSAMPLE(*ptr++)); + } + (void) JFWRITE(dest->pub.output_file, dest->iobuffer, dest->buffer_width); +} + + +/* + * Write some pixel data when color quantization is in effect. + * We have to demap the color index values to straight data. + */ + +METHODDEF(void) +put_demapped_rgb (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + JDIMENSION rows_supplied) +{ + ppm_dest_ptr dest = (ppm_dest_ptr) dinfo; + register char * bufferptr; + register int pixval; + register JSAMPROW ptr; + register JSAMPROW color_map0 = cinfo->colormap[0]; + register JSAMPROW color_map1 = cinfo->colormap[1]; + register JSAMPROW color_map2 = cinfo->colormap[2]; + register JDIMENSION col; + + ptr = dest->pub.buffer[0]; + bufferptr = dest->iobuffer; + for (col = cinfo->output_width; col > 0; col--) { + pixval = GETJSAMPLE(*ptr++); + PUTPPMSAMPLE(bufferptr, GETJSAMPLE(color_map0[pixval])); + PUTPPMSAMPLE(bufferptr, GETJSAMPLE(color_map1[pixval])); + PUTPPMSAMPLE(bufferptr, GETJSAMPLE(color_map2[pixval])); + } + (void) JFWRITE(dest->pub.output_file, dest->iobuffer, dest->buffer_width); +} + + +METHODDEF(void) +put_demapped_gray (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + JDIMENSION rows_supplied) +{ + ppm_dest_ptr dest = (ppm_dest_ptr) dinfo; + register char * bufferptr; + register JSAMPROW ptr; + register JSAMPROW color_map = cinfo->colormap[0]; + register JDIMENSION col; + + ptr = dest->pub.buffer[0]; + bufferptr = dest->iobuffer; + for (col = cinfo->output_width; col > 0; col--) { + PUTPPMSAMPLE(bufferptr, GETJSAMPLE(color_map[GETJSAMPLE(*ptr++)])); + } + (void) JFWRITE(dest->pub.output_file, dest->iobuffer, dest->buffer_width); +} + + +/* + * Startup: write the file header. + */ + +METHODDEF(void) +start_output_ppm (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) +{ + ppm_dest_ptr dest = (ppm_dest_ptr) dinfo; + + /* Emit file header */ + switch (cinfo->out_color_space) { + case JCS_GRAYSCALE: + /* emit header for raw PGM format */ + fprintf(dest->pub.output_file, "P5\n%ld %ld\n%d\n", + (long) cinfo->output_width, (long) cinfo->output_height, + PPM_MAXVAL); + break; + case JCS_RGB: + /* emit header for raw PPM format */ + fprintf(dest->pub.output_file, "P6\n%ld %ld\n%d\n", + (long) cinfo->output_width, (long) cinfo->output_height, + PPM_MAXVAL); + break; + default: + ERREXIT(cinfo, JERR_PPM_COLORSPACE); + } +} + + +/* + * Finish up at the end of the file. + */ + +METHODDEF(void) +finish_output_ppm (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) +{ + /* Make sure we wrote the output file OK */ + fflush(dinfo->output_file); + if (ferror(dinfo->output_file)) + ERREXIT(cinfo, JERR_FILE_WRITE); +} + + +/* + * The module selection routine for PPM format output. + */ + +GLOBAL(djpeg_dest_ptr) +jinit_write_ppm (j_decompress_ptr cinfo) +{ + ppm_dest_ptr dest; + + /* Create module interface object, fill in method pointers */ + dest = (ppm_dest_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(ppm_dest_struct)); + dest->pub.start_output = start_output_ppm; + dest->pub.finish_output = finish_output_ppm; + + /* Calculate output image dimensions so we can allocate space */ + jpeg_calc_output_dimensions(cinfo); + + /* Create physical I/O buffer. Note we make this near on a PC. */ + dest->samples_per_row = cinfo->output_width * cinfo->out_color_components; + dest->buffer_width = dest->samples_per_row * (BYTESPERSAMPLE * SIZEOF(char)); + dest->iobuffer = (char *) (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_IMAGE, dest->buffer_width); + + if (cinfo->quantize_colors || BITS_IN_JSAMPLE != 8 || + SIZEOF(JSAMPLE) != SIZEOF(char)) { + /* When quantizing, we need an output buffer for colormap indexes + * that's separate from the physical I/O buffer. We also need a + * separate buffer if pixel format translation must take place. + */ + dest->pub.buffer = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->output_width * cinfo->output_components, (JDIMENSION) 1); + dest->pub.buffer_height = 1; + if (! cinfo->quantize_colors) + dest->pub.put_pixel_rows = copy_pixel_rows; + else if (cinfo->out_color_space == JCS_GRAYSCALE) + dest->pub.put_pixel_rows = put_demapped_gray; + else + dest->pub.put_pixel_rows = put_demapped_rgb; + } else { + /* We will fwrite() directly from decompressor output buffer. */ + /* Synthesize a JSAMPARRAY pointer structure */ + /* Cast here implies near->far pointer conversion on PCs */ + dest->pixrow = (JSAMPROW) dest->iobuffer; + dest->pub.buffer = & dest->pixrow; + dest->pub.buffer_height = 1; + dest->pub.put_pixel_rows = put_pixel_rows; + } + + return (djpeg_dest_ptr) dest; +} + +#endif /* PPM_SUPPORTED */ diff --git a/Engine/lib/ljpeg/extras/wrrle.c b/Engine/lib/ljpeg/extras/wrrle.c new file mode 100644 index 000000000..a4e73372d --- /dev/null +++ b/Engine/lib/ljpeg/extras/wrrle.c @@ -0,0 +1,305 @@ +/* + * wrrle.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to write output images in RLE format. + * The Utah Raster Toolkit library is required (version 3.1 or later). + * + * These routines may need modification for non-Unix environments or + * specialized applications. As they stand, they assume output to + * an ordinary stdio stream. + * + * Based on code contributed by Mike Lijewski, + * with updates from Robert Hutchinson. + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + +#ifdef RLE_SUPPORTED + +/* rle.h is provided by the Utah Raster Toolkit. */ + +#include + +/* + * We assume that JSAMPLE has the same representation as rle_pixel, + * to wit, "unsigned char". Hence we can't cope with 12- or 16-bit samples. + */ + +#if BITS_IN_JSAMPLE != 8 + Sorry, this code only copes with 8-bit JSAMPLEs. /* deliberate syntax err */ +#endif + + +/* + * Since RLE stores scanlines bottom-to-top, we have to invert the image + * from JPEG's top-to-bottom order. To do this, we save the outgoing data + * in a virtual array during put_pixel_row calls, then actually emit the + * RLE file during finish_output. + */ + + +/* + * For now, if we emit an RLE color map then it is always 256 entries long, + * though not all of the entries need be used. + */ + +#define CMAPBITS 8 +#define CMAPLENGTH (1<<(CMAPBITS)) + +typedef struct { + struct djpeg_dest_struct pub; /* public fields */ + + jvirt_sarray_ptr image; /* virtual array to store the output image */ + rle_map *colormap; /* RLE-style color map, or NULL if none */ + rle_pixel **rle_row; /* To pass rows to rle_putrow() */ + +} rle_dest_struct; + +typedef rle_dest_struct * rle_dest_ptr; + +/* Forward declarations */ +METHODDEF(void) rle_put_pixel_rows + JPP((j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + JDIMENSION rows_supplied)); + + +/* + * Write the file header. + * + * In this module it's easier to wait till finish_output to write anything. + */ + +METHODDEF(void) +start_output_rle (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) +{ + rle_dest_ptr dest = (rle_dest_ptr) dinfo; + size_t cmapsize; + int i, ci; +#ifdef PROGRESS_REPORT + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; +#endif + + /* + * Make sure the image can be stored in RLE format. + * + * - RLE stores image dimensions as *signed* 16 bit integers. JPEG + * uses unsigned, so we have to check the width. + * + * - Colorspace is expected to be grayscale or RGB. + * + * - The number of channels (components) is expected to be 1 (grayscale/ + * pseudocolor) or 3 (truecolor/directcolor). + * (could be 2 or 4 if using an alpha channel, but we aren't) + */ + + if (cinfo->output_width > 32767 || cinfo->output_height > 32767) + ERREXIT2(cinfo, JERR_RLE_DIMENSIONS, cinfo->output_width, + cinfo->output_height); + + if (cinfo->out_color_space != JCS_GRAYSCALE && + cinfo->out_color_space != JCS_RGB) + ERREXIT(cinfo, JERR_RLE_COLORSPACE); + + if (cinfo->output_components != 1 && cinfo->output_components != 3) + ERREXIT1(cinfo, JERR_RLE_TOOMANYCHANNELS, cinfo->num_components); + + /* Convert colormap, if any, to RLE format. */ + + dest->colormap = NULL; + + if (cinfo->quantize_colors) { + /* Allocate storage for RLE-style cmap, zero any extra entries */ + cmapsize = cinfo->out_color_components * CMAPLENGTH * SIZEOF(rle_map); + dest->colormap = (rle_map *) (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_IMAGE, cmapsize); + MEMZERO(dest->colormap, cmapsize); + + /* Save away data in RLE format --- note 8-bit left shift! */ + /* Shifting would need adjustment for JSAMPLEs wider than 8 bits. */ + for (ci = 0; ci < cinfo->out_color_components; ci++) { + for (i = 0; i < cinfo->actual_number_of_colors; i++) { + dest->colormap[ci * CMAPLENGTH + i] = + GETJSAMPLE(cinfo->colormap[ci][i]) << 8; + } + } + } + + /* Set the output buffer to the first row */ + dest->pub.buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, dest->image, (JDIMENSION) 0, (JDIMENSION) 1, TRUE); + dest->pub.buffer_height = 1; + + dest->pub.put_pixel_rows = rle_put_pixel_rows; + +#ifdef PROGRESS_REPORT + if (progress != NULL) { + progress->total_extra_passes++; /* count file writing as separate pass */ + } +#endif +} + + +/* + * Write some pixel data. + * + * This routine just saves the data away in a virtual array. + */ + +METHODDEF(void) +rle_put_pixel_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + JDIMENSION rows_supplied) +{ + rle_dest_ptr dest = (rle_dest_ptr) dinfo; + + if (cinfo->output_scanline < cinfo->output_height) { + dest->pub.buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, dest->image, + cinfo->output_scanline, (JDIMENSION) 1, TRUE); + } +} + +/* + * Finish up at the end of the file. + * + * Here is where we really output the RLE file. + */ + +METHODDEF(void) +finish_output_rle (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) +{ + rle_dest_ptr dest = (rle_dest_ptr) dinfo; + rle_hdr header; /* Output file information */ + rle_pixel **rle_row, *red, *green, *blue; + JSAMPROW output_row; + char cmapcomment[80]; + int row, col; + int ci; +#ifdef PROGRESS_REPORT + cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress; +#endif + + /* Initialize the header info */ + header = *rle_hdr_init(NULL); + header.rle_file = dest->pub.output_file; + header.xmin = 0; + header.xmax = cinfo->output_width - 1; + header.ymin = 0; + header.ymax = cinfo->output_height - 1; + header.alpha = 0; + header.ncolors = cinfo->output_components; + for (ci = 0; ci < cinfo->output_components; ci++) { + RLE_SET_BIT(header, ci); + } + if (cinfo->quantize_colors) { + header.ncmap = cinfo->out_color_components; + header.cmaplen = CMAPBITS; + header.cmap = dest->colormap; + /* Add a comment to the output image with the true colormap length. */ + sprintf(cmapcomment, "color_map_length=%d", cinfo->actual_number_of_colors); + rle_putcom(cmapcomment, &header); + } + + /* Emit the RLE header and color map (if any) */ + rle_put_setup(&header); + + /* Now output the RLE data from our virtual array. + * We assume here that (a) rle_pixel is represented the same as JSAMPLE, + * and (b) we are not on a machine where FAR pointers differ from regular. + */ + +#ifdef PROGRESS_REPORT + if (progress != NULL) { + progress->pub.pass_limit = cinfo->output_height; + progress->pub.pass_counter = 0; + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + } +#endif + + if (cinfo->output_components == 1) { + for (row = cinfo->output_height-1; row >= 0; row--) { + rle_row = (rle_pixel **) (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, dest->image, + (JDIMENSION) row, (JDIMENSION) 1, FALSE); + rle_putrow(rle_row, (int) cinfo->output_width, &header); +#ifdef PROGRESS_REPORT + if (progress != NULL) { + progress->pub.pass_counter++; + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + } +#endif + } + } else { + for (row = cinfo->output_height-1; row >= 0; row--) { + rle_row = (rle_pixel **) dest->rle_row; + output_row = * (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, dest->image, + (JDIMENSION) row, (JDIMENSION) 1, FALSE); + red = rle_row[0]; + green = rle_row[1]; + blue = rle_row[2]; + for (col = cinfo->output_width; col > 0; col--) { + *red++ = GETJSAMPLE(*output_row++); + *green++ = GETJSAMPLE(*output_row++); + *blue++ = GETJSAMPLE(*output_row++); + } + rle_putrow(rle_row, (int) cinfo->output_width, &header); +#ifdef PROGRESS_REPORT + if (progress != NULL) { + progress->pub.pass_counter++; + (*progress->pub.progress_monitor) ((j_common_ptr) cinfo); + } +#endif + } + } + +#ifdef PROGRESS_REPORT + if (progress != NULL) + progress->completed_extra_passes++; +#endif + + /* Emit file trailer */ + rle_puteof(&header); + fflush(dest->pub.output_file); + if (ferror(dest->pub.output_file)) + ERREXIT(cinfo, JERR_FILE_WRITE); +} + + +/* + * The module selection routine for RLE format output. + */ + +GLOBAL(djpeg_dest_ptr) +jinit_write_rle (j_decompress_ptr cinfo) +{ + rle_dest_ptr dest; + + /* Create module interface object, fill in method pointers */ + dest = (rle_dest_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(rle_dest_struct)); + dest->pub.start_output = start_output_rle; + dest->pub.finish_output = finish_output_rle; + + /* Calculate output image dimensions so we can allocate space */ + jpeg_calc_output_dimensions(cinfo); + + /* Allocate a work array for output to the RLE library. */ + dest->rle_row = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->output_width, (JDIMENSION) cinfo->output_components); + + /* Allocate a virtual array to hold the image. */ + dest->image = (*cinfo->mem->request_virt_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + (JDIMENSION) (cinfo->output_width * cinfo->output_components), + cinfo->output_height, (JDIMENSION) 1); + + return (djpeg_dest_ptr) dest; +} + +#endif /* RLE_SUPPORTED */ diff --git a/Engine/lib/ljpeg/extras/wrtarga.c b/Engine/lib/ljpeg/extras/wrtarga.c new file mode 100644 index 000000000..cf104d2de --- /dev/null +++ b/Engine/lib/ljpeg/extras/wrtarga.c @@ -0,0 +1,253 @@ +/* + * wrtarga.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to write output images in Targa format. + * + * These routines may need modification for non-Unix environments or + * specialized applications. As they stand, they assume output to + * an ordinary stdio stream. + * + * Based on code contributed by Lee Daniel Crocker. + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ + +#ifdef TARGA_SUPPORTED + + +/* + * To support 12-bit JPEG data, we'd have to scale output down to 8 bits. + * This is not yet implemented. + */ + +#if BITS_IN_JSAMPLE != 8 + Sorry, this code only copes with 8-bit JSAMPLEs. /* deliberate syntax err */ +#endif + +/* + * The output buffer needs to be writable by fwrite(). On PCs, we must + * allocate the buffer in near data space, because we are assuming small-data + * memory model, wherein fwrite() can't reach far memory. If you need to + * process very wide images on a PC, you might have to compile in large-memory + * model, or else replace fwrite() with a putc() loop --- which will be much + * slower. + */ + + +/* Private version of data destination object */ + +typedef struct { + struct djpeg_dest_struct pub; /* public fields */ + + char *iobuffer; /* physical I/O buffer */ + JDIMENSION buffer_width; /* width of one row */ +} tga_dest_struct; + +typedef tga_dest_struct * tga_dest_ptr; + + +LOCAL(void) +write_header (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, int num_colors) +/* Create and write a Targa header */ +{ + char targaheader[18]; + + /* Set unused fields of header to 0 */ + MEMZERO(targaheader, SIZEOF(targaheader)); + + if (num_colors > 0) { + targaheader[1] = 1; /* color map type 1 */ + targaheader[5] = (char) (num_colors & 0xFF); + targaheader[6] = (char) (num_colors >> 8); + targaheader[7] = 24; /* 24 bits per cmap entry */ + } + + targaheader[12] = (char) (cinfo->output_width & 0xFF); + targaheader[13] = (char) (cinfo->output_width >> 8); + targaheader[14] = (char) (cinfo->output_height & 0xFF); + targaheader[15] = (char) (cinfo->output_height >> 8); + targaheader[17] = 0x20; /* Top-down, non-interlaced */ + + if (cinfo->out_color_space == JCS_GRAYSCALE) { + targaheader[2] = 3; /* image type = uncompressed gray-scale */ + targaheader[16] = 8; /* bits per pixel */ + } else { /* must be RGB */ + if (num_colors > 0) { + targaheader[2] = 1; /* image type = colormapped RGB */ + targaheader[16] = 8; + } else { + targaheader[2] = 2; /* image type = uncompressed RGB */ + targaheader[16] = 24; + } + } + + if (JFWRITE(dinfo->output_file, targaheader, 18) != (size_t) 18) + ERREXIT(cinfo, JERR_FILE_WRITE); +} + + +/* + * Write some pixel data. + * In this module rows_supplied will always be 1. + */ + +METHODDEF(void) +put_pixel_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + JDIMENSION rows_supplied) +/* used for unquantized full-color output */ +{ + tga_dest_ptr dest = (tga_dest_ptr) dinfo; + register JSAMPROW inptr; + register char * outptr; + register JDIMENSION col; + + inptr = dest->pub.buffer[0]; + outptr = dest->iobuffer; + for (col = cinfo->output_width; col > 0; col--) { + outptr[0] = (char) GETJSAMPLE(inptr[2]); /* RGB to BGR order */ + outptr[1] = (char) GETJSAMPLE(inptr[1]); + outptr[2] = (char) GETJSAMPLE(inptr[0]); + inptr += 3, outptr += 3; + } + (void) JFWRITE(dest->pub.output_file, dest->iobuffer, dest->buffer_width); +} + +METHODDEF(void) +put_gray_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + JDIMENSION rows_supplied) +/* used for grayscale OR quantized color output */ +{ + tga_dest_ptr dest = (tga_dest_ptr) dinfo; + register JSAMPROW inptr; + register char * outptr; + register JDIMENSION col; + + inptr = dest->pub.buffer[0]; + outptr = dest->iobuffer; + for (col = cinfo->output_width; col > 0; col--) { + *outptr++ = (char) GETJSAMPLE(*inptr++); + } + (void) JFWRITE(dest->pub.output_file, dest->iobuffer, dest->buffer_width); +} + + +/* + * Write some demapped pixel data when color quantization is in effect. + * For Targa, this is only applied to grayscale data. + */ + +METHODDEF(void) +put_demapped_gray (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, + JDIMENSION rows_supplied) +{ + tga_dest_ptr dest = (tga_dest_ptr) dinfo; + register JSAMPROW inptr; + register char * outptr; + register JSAMPROW color_map0 = cinfo->colormap[0]; + register JDIMENSION col; + + inptr = dest->pub.buffer[0]; + outptr = dest->iobuffer; + for (col = cinfo->output_width; col > 0; col--) { + *outptr++ = (char) GETJSAMPLE(color_map0[GETJSAMPLE(*inptr++)]); + } + (void) JFWRITE(dest->pub.output_file, dest->iobuffer, dest->buffer_width); +} + + +/* + * Startup: write the file header. + */ + +METHODDEF(void) +start_output_tga (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) +{ + tga_dest_ptr dest = (tga_dest_ptr) dinfo; + int num_colors, i; + FILE *outfile; + + if (cinfo->out_color_space == JCS_GRAYSCALE) { + /* Targa doesn't have a mapped grayscale format, so we will */ + /* demap quantized gray output. Never emit a colormap. */ + write_header(cinfo, dinfo, 0); + if (cinfo->quantize_colors) + dest->pub.put_pixel_rows = put_demapped_gray; + else + dest->pub.put_pixel_rows = put_gray_rows; + } else if (cinfo->out_color_space == JCS_RGB) { + if (cinfo->quantize_colors) { + /* We only support 8-bit colormap indexes, so only 256 colors */ + num_colors = cinfo->actual_number_of_colors; + if (num_colors > 256) + ERREXIT1(cinfo, JERR_TOO_MANY_COLORS, num_colors); + write_header(cinfo, dinfo, num_colors); + /* Write the colormap. Note Targa uses BGR byte order */ + outfile = dest->pub.output_file; + for (i = 0; i < num_colors; i++) { + putc(GETJSAMPLE(cinfo->colormap[2][i]), outfile); + putc(GETJSAMPLE(cinfo->colormap[1][i]), outfile); + putc(GETJSAMPLE(cinfo->colormap[0][i]), outfile); + } + dest->pub.put_pixel_rows = put_gray_rows; + } else { + write_header(cinfo, dinfo, 0); + dest->pub.put_pixel_rows = put_pixel_rows; + } + } else { + ERREXIT(cinfo, JERR_TGA_COLORSPACE); + } +} + + +/* + * Finish up at the end of the file. + */ + +METHODDEF(void) +finish_output_tga (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo) +{ + /* Make sure we wrote the output file OK */ + fflush(dinfo->output_file); + if (ferror(dinfo->output_file)) + ERREXIT(cinfo, JERR_FILE_WRITE); +} + + +/* + * The module selection routine for Targa format output. + */ + +GLOBAL(djpeg_dest_ptr) +jinit_write_targa (j_decompress_ptr cinfo) +{ + tga_dest_ptr dest; + + /* Create module interface object, fill in method pointers */ + dest = (tga_dest_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(tga_dest_struct)); + dest->pub.start_output = start_output_tga; + dest->pub.finish_output = finish_output_tga; + + /* Calculate output image dimensions so we can allocate space */ + jpeg_calc_output_dimensions(cinfo); + + /* Create I/O buffer. Note we make this near on a PC. */ + dest->buffer_width = cinfo->output_width * cinfo->output_components; + dest->iobuffer = (char *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (size_t) (dest->buffer_width * SIZEOF(char))); + + /* Create decompressor output buffer. */ + dest->pub.buffer = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, dest->buffer_width, (JDIMENSION) 1); + dest->pub.buffer_height = 1; + + return (djpeg_dest_ptr) dest; +} + +#endif /* TARGA_SUPPORTED */ diff --git a/Engine/lib/ljpeg/jcapimin.c b/Engine/lib/ljpeg/jcapimin.c new file mode 100644 index 000000000..bdcb7baf8 --- /dev/null +++ b/Engine/lib/ljpeg/jcapimin.c @@ -0,0 +1,280 @@ +/* + * jcapimin.c + * + * Copyright (C) 1994-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains application interface code for the compression half + * of the JPEG library. These are the "minimum" API routines that may be + * needed in either the normal full-compression case or the transcoding-only + * case. + * + * Most of the routines intended to be called directly by an application + * are in this file or in jcapistd.c. But also see jcparam.c for + * parameter-setup helper routines, jcomapi.c for routines shared by + * compression and decompression, and jctrans.c for the transcoding case. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * Initialization of a JPEG compression object. + * The error manager must already be set up (in case memory manager fails). + */ + +GLOBAL(void) +jpeg_CreateCompress (j_compress_ptr cinfo, int version, jpeg_size_t structsize) +{ + int i; + + /* Guard against version mismatches between library and caller. */ + cinfo->mem = NULL; /* so jpeg_destroy knows mem mgr not called */ + if (version != JPEG_LIB_VERSION) + ERREXIT2(cinfo, JERR_BAD_LIB_VERSION, JPEG_LIB_VERSION, version); + if (structsize != SIZEOF(struct jpeg_compress_struct)) + ERREXIT2(cinfo, JERR_BAD_STRUCT_SIZE, + (int) SIZEOF(struct jpeg_compress_struct), (int) structsize); + + /* For debugging purposes, we zero the whole master structure. + * But the application has already set the err pointer, and may have set + * client_data, so we have to save and restore those fields. + * Note: if application hasn't set client_data, tools like Purify may + * complain here. + */ + { + struct jpeg_error_mgr * err = cinfo->err; + void * client_data = cinfo->client_data; /* ignore Purify complaint here */ + MEMZERO(cinfo, SIZEOF(struct jpeg_compress_struct)); + cinfo->err = err; + cinfo->client_data = client_data; + } + cinfo->is_decompressor = FALSE; + + /* Initialize a memory manager instance for this object */ + jinit_memory_mgr((j_common_ptr) cinfo); + + /* Zero out pointers to permanent structures. */ + cinfo->progress = NULL; + cinfo->dest = NULL; + + cinfo->comp_info = NULL; + + for (i = 0; i < NUM_QUANT_TBLS; i++) + cinfo->quant_tbl_ptrs[i] = NULL; + + for (i = 0; i < NUM_HUFF_TBLS; i++) { + cinfo->dc_huff_tbl_ptrs[i] = NULL; + cinfo->ac_huff_tbl_ptrs[i] = NULL; + } + + cinfo->script_space = NULL; + + cinfo->input_gamma = 1.0; /* in case application forgets */ + + /* OK, I'm ready */ + cinfo->global_state = CSTATE_START; +} + + +/* + * Destruction of a JPEG compression object + */ + +GLOBAL(void) +jpeg_destroy_compress (j_compress_ptr cinfo) +{ + jpeg_destroy((j_common_ptr) cinfo); /* use common routine */ +} + + +/* + * Abort processing of a JPEG compression operation, + * but don't destroy the object itself. + */ + +GLOBAL(void) +jpeg_abort_compress (j_compress_ptr cinfo) +{ + jpeg_abort((j_common_ptr) cinfo); /* use common routine */ +} + + +/* + * Forcibly suppress or un-suppress all quantization and Huffman tables. + * Marks all currently defined tables as already written (if suppress) + * or not written (if !suppress). This will control whether they get emitted + * by a subsequent jpeg_start_compress call. + * + * This routine is exported for use by applications that want to produce + * abbreviated JPEG datastreams. It logically belongs in jcparam.c, but + * since it is called by jpeg_start_compress, we put it here --- otherwise + * jcparam.o would be linked whether the application used it or not. + */ + +GLOBAL(void) +jpeg_suppress_tables (j_compress_ptr cinfo, boolean suppress) +{ + int i; + JQUANT_TBL * qtbl; + JHUFF_TBL * htbl; + + for (i = 0; i < NUM_QUANT_TBLS; i++) { + if ((qtbl = cinfo->quant_tbl_ptrs[i]) != NULL) + qtbl->sent_table = suppress; + } + + for (i = 0; i < NUM_HUFF_TBLS; i++) { + if ((htbl = cinfo->dc_huff_tbl_ptrs[i]) != NULL) + htbl->sent_table = suppress; + if ((htbl = cinfo->ac_huff_tbl_ptrs[i]) != NULL) + htbl->sent_table = suppress; + } +} + + +/* + * Finish JPEG compression. + * + * If a multipass operating mode was selected, this may do a great deal of + * work including most of the actual output. + */ + +GLOBAL(void) +jpeg_finish_compress (j_compress_ptr cinfo) +{ + JDIMENSION iMCU_row; + + if (cinfo->global_state == CSTATE_SCANNING || + cinfo->global_state == CSTATE_RAW_OK) { + /* Terminate first pass */ + if (cinfo->next_scanline < cinfo->image_height) + ERREXIT(cinfo, JERR_TOO_LITTLE_DATA); + (*cinfo->master->finish_pass) (cinfo); + } else if (cinfo->global_state != CSTATE_WRCOEFS) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + /* Perform any remaining passes */ + while (! cinfo->master->is_last_pass) { + (*cinfo->master->prepare_for_pass) (cinfo); + for (iMCU_row = 0; iMCU_row < cinfo->total_iMCU_rows; iMCU_row++) { + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) iMCU_row; + cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + /* We bypass the main controller and invoke coef controller directly; + * all work is being done from the coefficient buffer. + */ + if (! (*cinfo->coef->compress_data) (cinfo, (JSAMPIMAGE) NULL)) + ERREXIT(cinfo, JERR_CANT_SUSPEND); + } + (*cinfo->master->finish_pass) (cinfo); + } + /* Write EOI, do final cleanup */ + (*cinfo->marker->write_file_trailer) (cinfo); + (*cinfo->dest->term_destination) (cinfo); + /* We can use jpeg_abort to release memory and reset global_state */ + jpeg_abort((j_common_ptr) cinfo); +} + + +/* + * Write a special marker. + * This is only recommended for writing COM or APPn markers. + * Must be called after jpeg_start_compress() and before + * first call to jpeg_write_scanlines() or jpeg_write_raw_data(). + */ + +GLOBAL(void) +jpeg_write_marker (j_compress_ptr cinfo, int marker, + const JOCTET *dataptr, unsigned int datalen) +{ + JMETHOD(void, write_marker_byte, (j_compress_ptr info, int val)); + + if (cinfo->next_scanline != 0 || + (cinfo->global_state != CSTATE_SCANNING && + cinfo->global_state != CSTATE_RAW_OK && + cinfo->global_state != CSTATE_WRCOEFS)) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + (*cinfo->marker->write_marker_header) (cinfo, marker, datalen); + write_marker_byte = cinfo->marker->write_marker_byte; /* copy for speed */ + while (datalen--) { + (*write_marker_byte) (cinfo, *dataptr); + dataptr++; + } +} + +/* Same, but piecemeal. */ + +GLOBAL(void) +jpeg_write_m_header (j_compress_ptr cinfo, int marker, unsigned int datalen) +{ + if (cinfo->next_scanline != 0 || + (cinfo->global_state != CSTATE_SCANNING && + cinfo->global_state != CSTATE_RAW_OK && + cinfo->global_state != CSTATE_WRCOEFS)) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + (*cinfo->marker->write_marker_header) (cinfo, marker, datalen); +} + +GLOBAL(void) +jpeg_write_m_byte (j_compress_ptr cinfo, int val) +{ + (*cinfo->marker->write_marker_byte) (cinfo, val); +} + + +/* + * Alternate compression function: just write an abbreviated table file. + * Before calling this, all parameters and a data destination must be set up. + * + * To produce a pair of files containing abbreviated tables and abbreviated + * image data, one would proceed as follows: + * + * initialize JPEG object + * set JPEG parameters + * set destination to table file + * jpeg_write_tables(cinfo); + * set destination to image file + * jpeg_start_compress(cinfo, FALSE); + * write data... + * jpeg_finish_compress(cinfo); + * + * jpeg_write_tables has the side effect of marking all tables written + * (same as jpeg_suppress_tables(..., TRUE)). Thus a subsequent start_compress + * will not re-emit the tables unless it is passed write_all_tables=TRUE. + */ + +GLOBAL(void) +jpeg_write_tables (j_compress_ptr cinfo) +{ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + /* (Re)initialize error mgr and destination modules */ + (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo); + (*cinfo->dest->init_destination) (cinfo); + /* Initialize the marker writer ... bit of a crock to do it here. */ + jinit_marker_writer(cinfo); + /* Write them tables! */ + (*cinfo->marker->write_tables_only) (cinfo); + /* And clean up. */ + (*cinfo->dest->term_destination) (cinfo); + /* + * In library releases up through v6a, we called jpeg_abort() here to free + * any working memory allocated by the destination manager and marker + * writer. Some applications had a problem with that: they allocated space + * of their own from the library memory manager, and didn't want it to go + * away during write_tables. So now we do nothing. This will cause a + * memory leak if an app calls write_tables repeatedly without doing a full + * compression cycle or otherwise resetting the JPEG object. However, that + * seems less bad than unexpectedly freeing memory in the normal case. + * An app that prefers the old behavior can call jpeg_abort for itself after + * each call to jpeg_write_tables(). + */ +} diff --git a/Engine/lib/ljpeg/jcapistd.c b/Engine/lib/ljpeg/jcapistd.c new file mode 100644 index 000000000..c0320b1b1 --- /dev/null +++ b/Engine/lib/ljpeg/jcapistd.c @@ -0,0 +1,161 @@ +/* + * jcapistd.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains application interface code for the compression half + * of the JPEG library. These are the "standard" API routines that are + * used in the normal full-compression case. They are not used by a + * transcoding-only application. Note that if an application links in + * jpeg_start_compress, it will end up linking in the entire compressor. + * We thus must separate this file from jcapimin.c to avoid linking the + * whole compression library into a transcoder. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * Compression initialization. + * Before calling this, all parameters and a data destination must be set up. + * + * We require a write_all_tables parameter as a failsafe check when writing + * multiple datastreams from the same compression object. Since prior runs + * will have left all the tables marked sent_table=TRUE, a subsequent run + * would emit an abbreviated stream (no tables) by default. This may be what + * is wanted, but for safety's sake it should not be the default behavior: + * programmers should have to make a deliberate choice to emit abbreviated + * images. Therefore the documentation and examples should encourage people + * to pass write_all_tables=TRUE; then it will take active thought to do the + * wrong thing. + */ + +GLOBAL(void) +jpeg_start_compress (j_compress_ptr cinfo, boolean write_all_tables) +{ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + if (write_all_tables) + jpeg_suppress_tables(cinfo, FALSE); /* mark all tables to be written */ + + /* (Re)initialize error mgr and destination modules */ + (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo); + (*cinfo->dest->init_destination) (cinfo); + /* Perform master selection of active modules */ + jinit_compress_master(cinfo); + /* Set up for the first pass */ + (*cinfo->master->prepare_for_pass) (cinfo); + /* Ready for application to drive first pass through jpeg_write_scanlines + * or jpeg_write_raw_data. + */ + cinfo->next_scanline = 0; + cinfo->global_state = (cinfo->raw_data_in ? CSTATE_RAW_OK : CSTATE_SCANNING); +} + + +/* + * Write some scanlines of data to the JPEG compressor. + * + * The return value will be the number of lines actually written. + * This should be less than the supplied num_lines only in case that + * the data destination module has requested suspension of the compressor, + * or if more than image_height scanlines are passed in. + * + * Note: we warn about excess calls to jpeg_write_scanlines() since + * this likely signals an application programmer error. However, + * excess scanlines passed in the last valid call are *silently* ignored, + * so that the application need not adjust num_lines for end-of-image + * when using a multiple-scanline buffer. + */ + +GLOBAL(JDIMENSION) +jpeg_write_scanlines (j_compress_ptr cinfo, JSAMPARRAY scanlines, + JDIMENSION num_lines) +{ + JDIMENSION row_ctr, rows_left; + + if (cinfo->global_state != CSTATE_SCANNING) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + if (cinfo->next_scanline >= cinfo->image_height) + WARNMS(cinfo, JWRN_TOO_MUCH_DATA); + + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) cinfo->next_scanline; + cinfo->progress->pass_limit = (long) cinfo->image_height; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + + /* Give master control module another chance if this is first call to + * jpeg_write_scanlines. This lets output of the frame/scan headers be + * delayed so that application can write COM, etc, markers between + * jpeg_start_compress and jpeg_write_scanlines. + */ + if (cinfo->master->call_pass_startup) + (*cinfo->master->pass_startup) (cinfo); + + /* Ignore any extra scanlines at bottom of image. */ + rows_left = cinfo->image_height - cinfo->next_scanline; + if (num_lines > rows_left) + num_lines = rows_left; + + row_ctr = 0; + (*cinfo->main->process_data) (cinfo, scanlines, &row_ctr, num_lines); + cinfo->next_scanline += row_ctr; + return row_ctr; +} + + +/* + * Alternate entry point to write raw data. + * Processes exactly one iMCU row per call, unless suspended. + */ + +GLOBAL(JDIMENSION) +jpeg_write_raw_data (j_compress_ptr cinfo, JSAMPIMAGE data, + JDIMENSION num_lines) +{ + JDIMENSION lines_per_iMCU_row; + + if (cinfo->global_state != CSTATE_RAW_OK) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + if (cinfo->next_scanline >= cinfo->image_height) { + WARNMS(cinfo, JWRN_TOO_MUCH_DATA); + return 0; + } + + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) cinfo->next_scanline; + cinfo->progress->pass_limit = (long) cinfo->image_height; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + + /* Give master control module another chance if this is first call to + * jpeg_write_raw_data. This lets output of the frame/scan headers be + * delayed so that application can write COM, etc, markers between + * jpeg_start_compress and jpeg_write_raw_data. + */ + if (cinfo->master->call_pass_startup) + (*cinfo->master->pass_startup) (cinfo); + + /* Verify that at least one iMCU row has been passed. */ + lines_per_iMCU_row = cinfo->max_v_samp_factor * DCTSIZE; + if (num_lines < lines_per_iMCU_row) + ERREXIT(cinfo, JERR_BUFFER_SIZE); + + /* Directly compress the row. */ + if (! (*cinfo->coef->compress_data) (cinfo, data)) { + /* If compressor did not consume the whole row, suspend processing. */ + return 0; + } + + /* OK, we processed one iMCU row. */ + cinfo->next_scanline += lines_per_iMCU_row; + return lines_per_iMCU_row; +} diff --git a/Engine/lib/ljpeg/jccoefct.c b/Engine/lib/ljpeg/jccoefct.c new file mode 100644 index 000000000..1963ddb61 --- /dev/null +++ b/Engine/lib/ljpeg/jccoefct.c @@ -0,0 +1,449 @@ +/* + * jccoefct.c + * + * Copyright (C) 1994-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the coefficient buffer controller for compression. + * This controller is the top level of the JPEG compressor proper. + * The coefficient buffer lies between forward-DCT and entropy encoding steps. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* We use a full-image coefficient buffer when doing Huffman optimization, + * and also for writing multiple-scan JPEG files. In all cases, the DCT + * step is run during the first pass, and subsequent passes need only read + * the buffered coefficients. + */ +#ifdef ENTROPY_OPT_SUPPORTED +#define FULL_COEF_BUFFER_SUPPORTED +#else +#ifdef C_MULTISCAN_FILES_SUPPORTED +#define FULL_COEF_BUFFER_SUPPORTED +#endif +#endif + + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_c_coef_controller pub; /* public fields */ + + JDIMENSION iMCU_row_num; /* iMCU row # within image */ + JDIMENSION mcu_ctr; /* counts MCUs processed in current row */ + int MCU_vert_offset; /* counts MCU rows within iMCU row */ + int MCU_rows_per_iMCU_row; /* number of such rows needed */ + + /* For single-pass compression, it's sufficient to buffer just one MCU + * (although this may prove a bit slow in practice). We allocate a + * workspace of C_MAX_BLOCKS_IN_MCU coefficient blocks, and reuse it for each + * MCU constructed and sent. (On 80x86, the workspace is FAR even though + * it's not really very big; this is to keep the module interfaces unchanged + * when a large coefficient buffer is necessary.) + * In multi-pass modes, this array points to the current MCU's blocks + * within the virtual arrays. + */ + JBLOCKROW MCU_buffer[C_MAX_BLOCKS_IN_MCU]; + + /* In multi-pass modes, we need a virtual block array for each component. */ + jvirt_barray_ptr whole_image[MAX_COMPONENTS]; +} my_coef_controller; + +typedef my_coef_controller * my_coef_ptr; + + +/* Forward declarations */ +METHODDEF(boolean) compress_data + JPP((j_compress_ptr cinfo, JSAMPIMAGE input_buf)); +#ifdef FULL_COEF_BUFFER_SUPPORTED +METHODDEF(boolean) compress_first_pass + JPP((j_compress_ptr cinfo, JSAMPIMAGE input_buf)); +METHODDEF(boolean) compress_output + JPP((j_compress_ptr cinfo, JSAMPIMAGE input_buf)); +#endif + + +LOCAL(void) +start_iMCU_row (j_compress_ptr cinfo) +/* Reset within-iMCU-row counters for a new row */ +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + /* In an interleaved scan, an MCU row is the same as an iMCU row. + * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. + * But at the bottom of the image, process only what's left. + */ + if (cinfo->comps_in_scan > 1) { + coef->MCU_rows_per_iMCU_row = 1; + } else { + if (coef->iMCU_row_num < (cinfo->total_iMCU_rows-1)) + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor; + else + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height; + } + + coef->mcu_ctr = 0; + coef->MCU_vert_offset = 0; +} + + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_coef (j_compress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + coef->iMCU_row_num = 0; + start_iMCU_row(cinfo); + + switch (pass_mode) { + case JBUF_PASS_THRU: + if (coef->whole_image[0] != NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + coef->pub.compress_data = compress_data; + break; +#ifdef FULL_COEF_BUFFER_SUPPORTED + case JBUF_SAVE_AND_PASS: + if (coef->whole_image[0] == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + coef->pub.compress_data = compress_first_pass; + break; + case JBUF_CRANK_DEST: + if (coef->whole_image[0] == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + coef->pub.compress_data = compress_output; + break; +#endif + default: + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + break; + } +} + + +/* + * Process some data in the single-pass case. + * We process the equivalent of one fully interleaved MCU row ("iMCU" row) + * per call, ie, v_samp_factor block rows for each component in the image. + * Returns TRUE if the iMCU row is completed, FALSE if suspended. + * + * NB: input_buf contains a plane for each component in image, + * which we index according to the component's SOF position. + */ + +METHODDEF(boolean) +compress_data (j_compress_ptr cinfo, JSAMPIMAGE input_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + int blkn, bi, ci, yindex, yoffset, blockcnt; + JDIMENSION ypos, xpos; + jpeg_component_info *compptr; + + /* Loop to write as much as one whole iMCU row */ + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + yoffset++) { + for (MCU_col_num = coef->mcu_ctr; MCU_col_num <= last_MCU_col; + MCU_col_num++) { + /* Determine where data comes from in input_buf and do the DCT thing. + * Each call on forward_DCT processes a horizontal row of DCT blocks + * as wide as an MCU; we rely on having allocated the MCU_buffer[] blocks + * sequentially. Dummy blocks at the right or bottom edge are filled in + * specially. The data in them does not matter for image reconstruction, + * so we fill them with values that will encode to the smallest amount of + * data, viz: all zeroes in the AC entries, DC entries equal to previous + * block's DC value. (Thanks to Thomas Kinsman for this idea.) + */ + blkn = 0; + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + blockcnt = (MCU_col_num < last_MCU_col) ? compptr->MCU_width + : compptr->last_col_width; + xpos = MCU_col_num * compptr->MCU_sample_width; + ypos = yoffset * DCTSIZE; /* ypos == (yoffset+yindex) * DCTSIZE */ + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + if (coef->iMCU_row_num < last_iMCU_row || + yoffset+yindex < compptr->last_row_height) { + (*cinfo->fdct->forward_DCT) (cinfo, compptr, + input_buf[compptr->component_index], + coef->MCU_buffer[blkn], + ypos, xpos, (JDIMENSION) blockcnt); + if (blockcnt < compptr->MCU_width) { + /* Create some dummy blocks at the right edge of the image. */ + jzero_far((void FAR *) coef->MCU_buffer[blkn + blockcnt], + (compptr->MCU_width - blockcnt) * SIZEOF(JBLOCK)); + for (bi = blockcnt; bi < compptr->MCU_width; bi++) { + coef->MCU_buffer[blkn+bi][0][0] = coef->MCU_buffer[blkn+bi-1][0][0]; + } + } + } else { + /* Create a row of dummy blocks at the bottom of the image. */ + jzero_far((void FAR *) coef->MCU_buffer[blkn], + compptr->MCU_width * SIZEOF(JBLOCK)); + for (bi = 0; bi < compptr->MCU_width; bi++) { + coef->MCU_buffer[blkn+bi][0][0] = coef->MCU_buffer[blkn-1][0][0]; + } + } + blkn += compptr->MCU_width; + ypos += DCTSIZE; + } + } + /* Try to write the MCU. In event of a suspension failure, we will + * re-DCT the MCU on restart (a bit inefficient, could be fixed...) + */ + if (! (*cinfo->entropy->encode_mcu) (cinfo, coef->MCU_buffer)) { + /* Suspension forced; update state counters and exit */ + coef->MCU_vert_offset = yoffset; + coef->mcu_ctr = MCU_col_num; + return FALSE; + } + } + /* Completed an MCU row, but perhaps not an iMCU row */ + coef->mcu_ctr = 0; + } + /* Completed the iMCU row, advance counters for next one */ + coef->iMCU_row_num++; + start_iMCU_row(cinfo); + return TRUE; +} + + +#ifdef FULL_COEF_BUFFER_SUPPORTED + +/* + * Process some data in the first pass of a multi-pass case. + * We process the equivalent of one fully interleaved MCU row ("iMCU" row) + * per call, ie, v_samp_factor block rows for each component in the image. + * This amount of data is read from the source buffer, DCT'd and quantized, + * and saved into the virtual arrays. We also generate suitable dummy blocks + * as needed at the right and lower edges. (The dummy blocks are constructed + * in the virtual arrays, which have been padded appropriately.) This makes + * it possible for subsequent passes not to worry about real vs. dummy blocks. + * + * We must also emit the data to the entropy encoder. This is conveniently + * done by calling compress_output() after we've loaded the current strip + * of the virtual arrays. + * + * NB: input_buf contains a plane for each component in image. All + * components are DCT'd and loaded into the virtual arrays in this pass. + * However, it may be that only a subset of the components are emitted to + * the entropy encoder during this first pass; be careful about looking + * at the scan-dependent variables (MCU dimensions, etc). + */ + +METHODDEF(boolean) +compress_first_pass (j_compress_ptr cinfo, JSAMPIMAGE input_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + JDIMENSION blocks_across, MCUs_across, MCUindex; + int bi, ci, h_samp_factor, block_row, block_rows, ndummy; + JCOEF lastDC; + jpeg_component_info *compptr; + JBLOCKARRAY buffer; + JBLOCKROW thisblockrow, lastblockrow; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Align the virtual buffer for this component. */ + buffer = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[ci], + coef->iMCU_row_num * compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, TRUE); + /* Count non-dummy DCT block rows in this iMCU row. */ + if (coef->iMCU_row_num < last_iMCU_row) + block_rows = compptr->v_samp_factor; + else { + /* NB: can't use last_row_height here, since may not be set! */ + block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + if (block_rows == 0) block_rows = compptr->v_samp_factor; + } + blocks_across = compptr->width_in_blocks; + h_samp_factor = compptr->h_samp_factor; + /* Count number of dummy blocks to be added at the right margin. */ + ndummy = (int) (blocks_across % h_samp_factor); + if (ndummy > 0) + ndummy = h_samp_factor - ndummy; + /* Perform DCT for all non-dummy blocks in this iMCU row. Each call + * on forward_DCT processes a complete horizontal row of DCT blocks. + */ + for (block_row = 0; block_row < block_rows; block_row++) { + thisblockrow = buffer[block_row]; + (*cinfo->fdct->forward_DCT) (cinfo, compptr, + input_buf[ci], thisblockrow, + (JDIMENSION) (block_row * DCTSIZE), + (JDIMENSION) 0, blocks_across); + if (ndummy > 0) { + /* Create dummy blocks at the right edge of the image. */ + thisblockrow += blocks_across; /* => first dummy block */ + jzero_far((void FAR *) thisblockrow, ndummy * SIZEOF(JBLOCK)); + lastDC = thisblockrow[-1][0]; + for (bi = 0; bi < ndummy; bi++) { + thisblockrow[bi][0] = lastDC; + } + } + } + /* If at end of image, create dummy block rows as needed. + * The tricky part here is that within each MCU, we want the DC values + * of the dummy blocks to match the last real block's DC value. + * This squeezes a few more bytes out of the resulting file... + */ + if (coef->iMCU_row_num == last_iMCU_row) { + blocks_across += ndummy; /* include lower right corner */ + MCUs_across = blocks_across / h_samp_factor; + for (block_row = block_rows; block_row < compptr->v_samp_factor; + block_row++) { + thisblockrow = buffer[block_row]; + lastblockrow = buffer[block_row-1]; + jzero_far((void FAR *) thisblockrow, + (size_t) (blocks_across * SIZEOF(JBLOCK))); + for (MCUindex = 0; MCUindex < MCUs_across; MCUindex++) { + lastDC = lastblockrow[h_samp_factor-1][0]; + for (bi = 0; bi < h_samp_factor; bi++) { + thisblockrow[bi][0] = lastDC; + } + thisblockrow += h_samp_factor; /* advance to next MCU in row */ + lastblockrow += h_samp_factor; + } + } + } + } + /* NB: compress_output will increment iMCU_row_num if successful. + * A suspension return will result in redoing all the work above next time. + */ + + /* Emit data to the entropy encoder, sharing code with subsequent passes */ + return compress_output(cinfo, input_buf); +} + + +/* + * Process some data in subsequent passes of a multi-pass case. + * We process the equivalent of one fully interleaved MCU row ("iMCU" row) + * per call, ie, v_samp_factor block rows for each component in the scan. + * The data is obtained from the virtual arrays and fed to the entropy coder. + * Returns TRUE if the iMCU row is completed, FALSE if suspended. + * + * NB: input_buf is ignored; it is likely to be a NULL pointer. + */ + +METHODDEF(boolean) +compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + int blkn, ci, xindex, yindex, yoffset; + JDIMENSION start_col; + JBLOCKARRAY buffer[MAX_COMPS_IN_SCAN]; + JBLOCKROW buffer_ptr; + jpeg_component_info *compptr; + + /* Align the virtual buffers for the components used in this scan. + * NB: during first pass, this is safe only because the buffers will + * already be aligned properly, so jmemmgr.c won't need to do any I/O. + */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + buffer[ci] = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[compptr->component_index], + coef->iMCU_row_num * compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } + + /* Loop to process one whole iMCU row */ + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + yoffset++) { + for (MCU_col_num = coef->mcu_ctr; MCU_col_num < cinfo->MCUs_per_row; + MCU_col_num++) { + /* Construct list of pointers to DCT blocks belonging to this MCU */ + blkn = 0; /* index of current DCT block within MCU */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + start_col = MCU_col_num * compptr->MCU_width; + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + buffer_ptr = buffer[ci][yindex+yoffset] + start_col; + for (xindex = 0; xindex < compptr->MCU_width; xindex++) { + coef->MCU_buffer[blkn++] = buffer_ptr++; + } + } + } + /* Try to write the MCU. */ + if (! (*cinfo->entropy->encode_mcu) (cinfo, coef->MCU_buffer)) { + /* Suspension forced; update state counters and exit */ + coef->MCU_vert_offset = yoffset; + coef->mcu_ctr = MCU_col_num; + return FALSE; + } + } + /* Completed an MCU row, but perhaps not an iMCU row */ + coef->mcu_ctr = 0; + } + /* Completed the iMCU row, advance counters for next one */ + coef->iMCU_row_num++; + start_iMCU_row(cinfo); + return TRUE; +} + +#endif /* FULL_COEF_BUFFER_SUPPORTED */ + + +/* + * Initialize coefficient buffer controller. + */ + +GLOBAL(void) +jinit_c_coef_controller (j_compress_ptr cinfo, boolean need_full_buffer) +{ + my_coef_ptr coef; + + coef = (my_coef_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_coef_controller)); + cinfo->coef = (struct jpeg_c_coef_controller *) coef; + coef->pub.start_pass = start_pass_coef; + + /* Create the coefficient buffer. */ + if (need_full_buffer) { +#ifdef FULL_COEF_BUFFER_SUPPORTED + /* Allocate a full-image virtual array for each component, */ + /* padded to a multiple of samp_factor DCT blocks in each direction. */ + int ci; + jpeg_component_info *compptr; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + coef->whole_image[ci] = (*cinfo->mem->request_virt_barray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + (long) compptr->h_samp_factor), + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + (long) compptr->v_samp_factor), + (JDIMENSION) compptr->v_samp_factor); + } +#else + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); +#endif + } else { + /* We only need a single-MCU buffer. */ + JBLOCKROW buffer; + int i; + + buffer = (JBLOCKROW) + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + for (i = 0; i < C_MAX_BLOCKS_IN_MCU; i++) { + coef->MCU_buffer[i] = buffer + i; + } + coef->whole_image[0] = NULL; /* flag for no virtual arrays */ + } +} diff --git a/Engine/lib/ljpeg/jccolor.c b/Engine/lib/ljpeg/jccolor.c new file mode 100644 index 000000000..0a8a4b5d1 --- /dev/null +++ b/Engine/lib/ljpeg/jccolor.c @@ -0,0 +1,459 @@ +/* + * jccolor.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains input colorspace conversion routines. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Private subobject */ + +typedef struct { + struct jpeg_color_converter pub; /* public fields */ + + /* Private state for RGB->YCC conversion */ + INT32 * rgb_ycc_tab; /* => table for RGB to YCbCr conversion */ +} my_color_converter; + +typedef my_color_converter * my_cconvert_ptr; + + +/**************** RGB -> YCbCr conversion: most common case **************/ + +/* + * YCbCr is defined per CCIR 601-1, except that Cb and Cr are + * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. + * The conversion equations to be implemented are therefore + * Y = 0.29900 * R + 0.58700 * G + 0.11400 * B + * Cb = -0.16874 * R - 0.33126 * G + 0.50000 * B + CENTERJSAMPLE + * Cr = 0.50000 * R - 0.41869 * G - 0.08131 * B + CENTERJSAMPLE + * (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.) + * Note: older versions of the IJG code used a zero offset of MAXJSAMPLE/2, + * rather than CENTERJSAMPLE, for Cb and Cr. This gave equal positive and + * negative swings for Cb/Cr, but meant that grayscale values (Cb=Cr=0) + * were not represented exactly. Now we sacrifice exact representation of + * maximum red and maximum blue in order to get exact grayscales. + * + * To avoid floating-point arithmetic, we represent the fractional constants + * as integers scaled up by 2^16 (about 4 digits precision); we have to divide + * the products by 2^16, with appropriate rounding, to get the correct answer. + * + * For even more speed, we avoid doing any multiplications in the inner loop + * by precalculating the constants times R,G,B for all possible values. + * For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table); + * for 12-bit samples it is still acceptable. It's not very reasonable for + * 16-bit samples, but if you want lossless storage you shouldn't be changing + * colorspace anyway. + * The CENTERJSAMPLE offsets and the rounding fudge-factor of 0.5 are included + * in the tables to save adding them separately in the inner loop. + */ + +#define SCALEBITS 16 /* speediest right-shift on some machines */ +#define CBCR_OFFSET ((INT32) CENTERJSAMPLE << SCALEBITS) +#define ONE_HALF ((INT32) 1 << (SCALEBITS-1)) +#define FIX(x) ((INT32) ((x) * (1L< Y section */ +#define G_Y_OFF (1*(MAXJSAMPLE+1)) /* offset to G => Y section */ +#define B_Y_OFF (2*(MAXJSAMPLE+1)) /* etc. */ +#define R_CB_OFF (3*(MAXJSAMPLE+1)) +#define G_CB_OFF (4*(MAXJSAMPLE+1)) +#define B_CB_OFF (5*(MAXJSAMPLE+1)) +#define R_CR_OFF B_CB_OFF /* B=>Cb, R=>Cr are the same */ +#define G_CR_OFF (6*(MAXJSAMPLE+1)) +#define B_CR_OFF (7*(MAXJSAMPLE+1)) +#define TABLE_SIZE (8*(MAXJSAMPLE+1)) + + +/* + * Initialize for RGB->YCC colorspace conversion. + */ + +METHODDEF(void) +rgb_ycc_start (j_compress_ptr cinfo) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + INT32 * rgb_ycc_tab; + INT32 i; + + /* Allocate and fill in the conversion tables. */ + cconvert->rgb_ycc_tab = rgb_ycc_tab = (INT32 *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (TABLE_SIZE * SIZEOF(INT32))); + + for (i = 0; i <= MAXJSAMPLE; i++) { + rgb_ycc_tab[i+R_Y_OFF] = FIX(0.29900) * i; + rgb_ycc_tab[i+G_Y_OFF] = FIX(0.58700) * i; + rgb_ycc_tab[i+B_Y_OFF] = FIX(0.11400) * i + ONE_HALF; + rgb_ycc_tab[i+R_CB_OFF] = (-FIX(0.16874)) * i; + rgb_ycc_tab[i+G_CB_OFF] = (-FIX(0.33126)) * i; + /* We use a rounding fudge-factor of 0.5-epsilon for Cb and Cr. + * This ensures that the maximum output will round to MAXJSAMPLE + * not MAXJSAMPLE+1, and thus that we don't have to range-limit. + */ + rgb_ycc_tab[i+B_CB_OFF] = FIX(0.50000) * i + CBCR_OFFSET + ONE_HALF-1; +/* B=>Cb and R=>Cr tables are the same + rgb_ycc_tab[i+R_CR_OFF] = FIX(0.50000) * i + CBCR_OFFSET + ONE_HALF-1; +*/ + rgb_ycc_tab[i+G_CR_OFF] = (-FIX(0.41869)) * i; + rgb_ycc_tab[i+B_CR_OFF] = (-FIX(0.08131)) * i; + } +} + + +/* + * Convert some rows of samples to the JPEG colorspace. + * + * Note that we change from the application's interleaved-pixel format + * to our internal noninterleaved, one-plane-per-component format. + * The input buffer is therefore three times as wide as the output buffer. + * + * A starting row offset is provided only for the output buffer. The caller + * can easily adjust the passed input_buf value to accommodate any row + * offset required on that side. + */ + +METHODDEF(void) +rgb_ycc_convert (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + register int r, g, b; + register INT32 * ctab = cconvert->rgb_ycc_tab; + register JSAMPROW inptr; + register JSAMPROW outptr0, outptr1, outptr2; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->image_width; + + while (--num_rows >= 0) { + inptr = *input_buf++; + outptr0 = output_buf[0][output_row]; + outptr1 = output_buf[1][output_row]; + outptr2 = output_buf[2][output_row]; + output_row++; + for (col = 0; col < num_cols; col++) { + r = GETJSAMPLE(inptr[RGB_RED]); + g = GETJSAMPLE(inptr[RGB_GREEN]); + b = GETJSAMPLE(inptr[RGB_BLUE]); + inptr += RGB_PIXELSIZE; + /* If the inputs are 0..MAXJSAMPLE, the outputs of these equations + * must be too; we do not need an explicit range-limiting operation. + * Hence the value being shifted is never negative, and we don't + * need the general RIGHT_SHIFT macro. + */ + /* Y */ + outptr0[col] = (JSAMPLE) + ((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF]) + >> SCALEBITS); + /* Cb */ + outptr1[col] = (JSAMPLE) + ((ctab[r+R_CB_OFF] + ctab[g+G_CB_OFF] + ctab[b+B_CB_OFF]) + >> SCALEBITS); + /* Cr */ + outptr2[col] = (JSAMPLE) + ((ctab[r+R_CR_OFF] + ctab[g+G_CR_OFF] + ctab[b+B_CR_OFF]) + >> SCALEBITS); + } + } +} + + +/**************** Cases other than RGB -> YCbCr **************/ + + +/* + * Convert some rows of samples to the JPEG colorspace. + * This version handles RGB->grayscale conversion, which is the same + * as the RGB->Y portion of RGB->YCbCr. + * We assume rgb_ycc_start has been called (we only use the Y tables). + */ + +METHODDEF(void) +rgb_gray_convert (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + register int r, g, b; + register INT32 * ctab = cconvert->rgb_ycc_tab; + register JSAMPROW inptr; + register JSAMPROW outptr; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->image_width; + + while (--num_rows >= 0) { + inptr = *input_buf++; + outptr = output_buf[0][output_row]; + output_row++; + for (col = 0; col < num_cols; col++) { + r = GETJSAMPLE(inptr[RGB_RED]); + g = GETJSAMPLE(inptr[RGB_GREEN]); + b = GETJSAMPLE(inptr[RGB_BLUE]); + inptr += RGB_PIXELSIZE; + /* Y */ + outptr[col] = (JSAMPLE) + ((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF]) + >> SCALEBITS); + } + } +} + + +/* + * Convert some rows of samples to the JPEG colorspace. + * This version handles Adobe-style CMYK->YCCK conversion, + * where we convert R=1-C, G=1-M, and B=1-Y to YCbCr using the same + * conversion as above, while passing K (black) unchanged. + * We assume rgb_ycc_start has been called. + */ + +METHODDEF(void) +cmyk_ycck_convert (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + register int r, g, b; + register INT32 * ctab = cconvert->rgb_ycc_tab; + register JSAMPROW inptr; + register JSAMPROW outptr0, outptr1, outptr2, outptr3; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->image_width; + + while (--num_rows >= 0) { + inptr = *input_buf++; + outptr0 = output_buf[0][output_row]; + outptr1 = output_buf[1][output_row]; + outptr2 = output_buf[2][output_row]; + outptr3 = output_buf[3][output_row]; + output_row++; + for (col = 0; col < num_cols; col++) { + r = MAXJSAMPLE - GETJSAMPLE(inptr[0]); + g = MAXJSAMPLE - GETJSAMPLE(inptr[1]); + b = MAXJSAMPLE - GETJSAMPLE(inptr[2]); + /* K passes through as-is */ + outptr3[col] = inptr[3]; /* don't need GETJSAMPLE here */ + inptr += 4; + /* If the inputs are 0..MAXJSAMPLE, the outputs of these equations + * must be too; we do not need an explicit range-limiting operation. + * Hence the value being shifted is never negative, and we don't + * need the general RIGHT_SHIFT macro. + */ + /* Y */ + outptr0[col] = (JSAMPLE) + ((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF]) + >> SCALEBITS); + /* Cb */ + outptr1[col] = (JSAMPLE) + ((ctab[r+R_CB_OFF] + ctab[g+G_CB_OFF] + ctab[b+B_CB_OFF]) + >> SCALEBITS); + /* Cr */ + outptr2[col] = (JSAMPLE) + ((ctab[r+R_CR_OFF] + ctab[g+G_CR_OFF] + ctab[b+B_CR_OFF]) + >> SCALEBITS); + } + } +} + + +/* + * Convert some rows of samples to the JPEG colorspace. + * This version handles grayscale output with no conversion. + * The source can be either plain grayscale or YCbCr (since Y == gray). + */ + +METHODDEF(void) +grayscale_convert (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows) +{ + register JSAMPROW inptr; + register JSAMPROW outptr; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->image_width; + int instride = cinfo->input_components; + + while (--num_rows >= 0) { + inptr = *input_buf++; + outptr = output_buf[0][output_row]; + output_row++; + for (col = 0; col < num_cols; col++) { + outptr[col] = inptr[0]; /* don't need GETJSAMPLE() here */ + inptr += instride; + } + } +} + + +/* + * Convert some rows of samples to the JPEG colorspace. + * This version handles multi-component colorspaces without conversion. + * We assume input_components == num_components. + */ + +METHODDEF(void) +null_convert (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows) +{ + register JSAMPROW inptr; + register JSAMPROW outptr; + register JDIMENSION col; + register int ci; + int nc = cinfo->num_components; + JDIMENSION num_cols = cinfo->image_width; + + while (--num_rows >= 0) { + /* It seems fastest to make a separate pass for each component. */ + for (ci = 0; ci < nc; ci++) { + inptr = *input_buf; + outptr = output_buf[ci][output_row]; + for (col = 0; col < num_cols; col++) { + outptr[col] = inptr[ci]; /* don't need GETJSAMPLE() here */ + inptr += nc; + } + } + input_buf++; + output_row++; + } +} + + +/* + * Empty method for start_pass. + */ + +METHODDEF(void) +null_method (j_compress_ptr cinfo) +{ + /* no work needed */ +} + + +/* + * Module initialization routine for input colorspace conversion. + */ + +GLOBAL(void) +jinit_color_converter (j_compress_ptr cinfo) +{ + my_cconvert_ptr cconvert; + + cconvert = (my_cconvert_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_color_converter)); + cinfo->cconvert = (struct jpeg_color_converter *) cconvert; + /* set start_pass to null method until we find out differently */ + cconvert->pub.start_pass = null_method; + + /* Make sure input_components agrees with in_color_space */ + switch (cinfo->in_color_space) { + case JCS_GRAYSCALE: + if (cinfo->input_components != 1) + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + break; + + case JCS_RGB: +#if RGB_PIXELSIZE != 3 + if (cinfo->input_components != RGB_PIXELSIZE) + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + break; +#endif /* else share code with YCbCr */ + + case JCS_YCbCr: + if (cinfo->input_components != 3) + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + break; + + case JCS_CMYK: + case JCS_YCCK: + if (cinfo->input_components != 4) + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + break; + + default: /* JCS_UNKNOWN can be anything */ + if (cinfo->input_components < 1) + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + break; + } + + /* Check num_components, set conversion method based on requested space */ + switch (cinfo->jpeg_color_space) { + case JCS_GRAYSCALE: + if (cinfo->num_components != 1) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + if (cinfo->in_color_space == JCS_GRAYSCALE) + cconvert->pub.color_convert = grayscale_convert; + else if (cinfo->in_color_space == JCS_RGB) { + cconvert->pub.start_pass = rgb_ycc_start; + cconvert->pub.color_convert = rgb_gray_convert; + } else if (cinfo->in_color_space == JCS_YCbCr) + cconvert->pub.color_convert = grayscale_convert; + else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_RGB: + if (cinfo->num_components != 3) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + if (cinfo->in_color_space == JCS_RGB && RGB_PIXELSIZE == 3) + cconvert->pub.color_convert = null_convert; + else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_YCbCr: + if (cinfo->num_components != 3) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + if (cinfo->in_color_space == JCS_RGB) { + cconvert->pub.start_pass = rgb_ycc_start; + cconvert->pub.color_convert = rgb_ycc_convert; + } else if (cinfo->in_color_space == JCS_YCbCr) + cconvert->pub.color_convert = null_convert; + else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_CMYK: + if (cinfo->num_components != 4) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + if (cinfo->in_color_space == JCS_CMYK) + cconvert->pub.color_convert = null_convert; + else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_YCCK: + if (cinfo->num_components != 4) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + if (cinfo->in_color_space == JCS_CMYK) { + cconvert->pub.start_pass = rgb_ycc_start; + cconvert->pub.color_convert = cmyk_ycck_convert; + } else if (cinfo->in_color_space == JCS_YCCK) + cconvert->pub.color_convert = null_convert; + else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + default: /* allow null conversion of JCS_UNKNOWN */ + if (cinfo->jpeg_color_space != cinfo->in_color_space || + cinfo->num_components != cinfo->input_components) + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + cconvert->pub.color_convert = null_convert; + break; + } +} diff --git a/Engine/lib/ljpeg/jcdctmgr.c b/Engine/lib/ljpeg/jcdctmgr.c new file mode 100644 index 000000000..61fa79b9e --- /dev/null +++ b/Engine/lib/ljpeg/jcdctmgr.c @@ -0,0 +1,387 @@ +/* + * jcdctmgr.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the forward-DCT management logic. + * This code selects a particular DCT implementation to be used, + * and it performs related housekeeping chores including coefficient + * quantization. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + + +/* Private subobject for this module */ + +typedef struct { + struct jpeg_forward_dct pub; /* public fields */ + + /* Pointer to the DCT routine actually in use */ + forward_DCT_method_ptr do_dct; + + /* The actual post-DCT divisors --- not identical to the quant table + * entries, because of scaling (especially for an unnormalized DCT). + * Each table is given in normal array order. + */ + DCTELEM * divisors[NUM_QUANT_TBLS]; + +#ifdef DCT_FLOAT_SUPPORTED + /* Same as above for the floating-point case. */ + float_DCT_method_ptr do_float_dct; + FAST_FLOAT * float_divisors[NUM_QUANT_TBLS]; +#endif +} my_fdct_controller; + +typedef my_fdct_controller * my_fdct_ptr; + + +/* + * Initialize for a processing pass. + * Verify that all referenced Q-tables are present, and set up + * the divisor table for each one. + * In the current implementation, DCT of all components is done during + * the first pass, even if only some components will be output in the + * first scan. Hence all components should be examined here. + */ + +METHODDEF(void) +start_pass_fdctmgr (j_compress_ptr cinfo) +{ + my_fdct_ptr fdct = (my_fdct_ptr) cinfo->fdct; + int ci, qtblno, i; + jpeg_component_info *compptr; + JQUANT_TBL * qtbl; + DCTELEM * dtbl; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + qtblno = compptr->quant_tbl_no; + /* Make sure specified quantization table is present */ + if (qtblno < 0 || qtblno >= NUM_QUANT_TBLS || + cinfo->quant_tbl_ptrs[qtblno] == NULL) + ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, qtblno); + qtbl = cinfo->quant_tbl_ptrs[qtblno]; + /* Compute divisors for this quant table */ + /* We may do this more than once for same table, but it's not a big deal */ + switch (cinfo->dct_method) { +#ifdef DCT_ISLOW_SUPPORTED + case JDCT_ISLOW: + /* For LL&M IDCT method, divisors are equal to raw quantization + * coefficients multiplied by 8 (to counteract scaling). + */ + if (fdct->divisors[qtblno] == NULL) { + fdct->divisors[qtblno] = (DCTELEM *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + DCTSIZE2 * SIZEOF(DCTELEM)); + } + dtbl = fdct->divisors[qtblno]; + for (i = 0; i < DCTSIZE2; i++) { + dtbl[i] = ((DCTELEM) qtbl->quantval[i]) << 3; + } + break; +#endif +#ifdef DCT_IFAST_SUPPORTED + case JDCT_IFAST: + { + /* For AA&N IDCT method, divisors are equal to quantization + * coefficients scaled by scalefactor[row]*scalefactor[col], where + * scalefactor[0] = 1 + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + * We apply a further scale factor of 8. + */ +#define CONST_BITS 14 + static const INT16 aanscales[DCTSIZE2] = { + /* precomputed values scaled up by 14 bits */ + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + 22725, 31521, 29692, 26722, 22725, 17855, 12299, 6270, + 21407, 29692, 27969, 25172, 21407, 16819, 11585, 5906, + 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + 12873, 17855, 16819, 15137, 12873, 10114, 6967, 3552, + 8867, 12299, 11585, 10426, 8867, 6967, 4799, 2446, + 4520, 6270, 5906, 5315, 4520, 3552, 2446, 1247 + }; + SHIFT_TEMPS + + if (fdct->divisors[qtblno] == NULL) { + fdct->divisors[qtblno] = (DCTELEM *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + DCTSIZE2 * SIZEOF(DCTELEM)); + } + dtbl = fdct->divisors[qtblno]; + for (i = 0; i < DCTSIZE2; i++) { + dtbl[i] = (DCTELEM) + DESCALE(MULTIPLY16V16((INT32) qtbl->quantval[i], + (INT32) aanscales[i]), + CONST_BITS-3); + } + } + break; +#endif +#ifdef DCT_FLOAT_SUPPORTED + case JDCT_FLOAT: + { + /* For float AA&N IDCT method, divisors are equal to quantization + * coefficients scaled by scalefactor[row]*scalefactor[col], where + * scalefactor[0] = 1 + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + * We apply a further scale factor of 8. + * What's actually stored is 1/divisor so that the inner loop can + * use a multiplication rather than a division. + */ + FAST_FLOAT * fdtbl; + int row, col; + static const double aanscalefactor[DCTSIZE] = { + 1.0, 1.387039845, 1.306562965, 1.175875602, + 1.0, 0.785694958, 0.541196100, 0.275899379 + }; + + if (fdct->float_divisors[qtblno] == NULL) { + fdct->float_divisors[qtblno] = (FAST_FLOAT *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + DCTSIZE2 * SIZEOF(FAST_FLOAT)); + } + fdtbl = fdct->float_divisors[qtblno]; + i = 0; + for (row = 0; row < DCTSIZE; row++) { + for (col = 0; col < DCTSIZE; col++) { + fdtbl[i] = (FAST_FLOAT) + (1.0 / (((double) qtbl->quantval[i] * + aanscalefactor[row] * aanscalefactor[col] * 8.0))); + i++; + } + } + } + break; +#endif + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + break; + } + } +} + + +/* + * Perform forward DCT on one or more blocks of a component. + * + * The input samples are taken from the sample_data[] array starting at + * position start_row/start_col, and moving to the right for any additional + * blocks. The quantized coefficients are returned in coef_blocks[]. + */ + +METHODDEF(void) +forward_DCT (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY sample_data, JBLOCKROW coef_blocks, + JDIMENSION start_row, JDIMENSION start_col, + JDIMENSION num_blocks) +/* This version is used for integer DCT implementations. */ +{ + /* This routine is heavily used, so it's worth coding it tightly. */ + my_fdct_ptr fdct = (my_fdct_ptr) cinfo->fdct; + forward_DCT_method_ptr do_dct = fdct->do_dct; + DCTELEM * divisors = fdct->divisors[compptr->quant_tbl_no]; + DCTELEM workspace[DCTSIZE2]; /* work area for FDCT subroutine */ + JDIMENSION bi; + + sample_data += start_row; /* fold in the vertical offset once */ + + for (bi = 0; bi < num_blocks; bi++, start_col += DCTSIZE) { + /* Load data into workspace, applying unsigned->signed conversion */ + { register DCTELEM *workspaceptr; + register JSAMPROW elemptr; + register int elemr; + + workspaceptr = workspace; + for (elemr = 0; elemr < DCTSIZE; elemr++) { + elemptr = sample_data[elemr] + start_col; +#if DCTSIZE == 8 /* unroll the inner loop */ + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; +#else + { register int elemc; + for (elemc = DCTSIZE; elemc > 0; elemc--) { + *workspaceptr++ = GETJSAMPLE(*elemptr++) - CENTERJSAMPLE; + } + } +#endif + } + } + + /* Perform the DCT */ + (*do_dct) (workspace); + + /* Quantize/descale the coefficients, and store into coef_blocks[] */ + { register DCTELEM temp, qval; + register int i; + register JCOEFPTR output_ptr = coef_blocks[bi]; + + for (i = 0; i < DCTSIZE2; i++) { + qval = divisors[i]; + temp = workspace[i]; + /* Divide the coefficient value by qval, ensuring proper rounding. + * Since C does not specify the direction of rounding for negative + * quotients, we have to force the dividend positive for portability. + * + * In most files, at least half of the output values will be zero + * (at default quantization settings, more like three-quarters...) + * so we should ensure that this case is fast. On many machines, + * a comparison is enough cheaper than a divide to make a special test + * a win. Since both inputs will be nonnegative, we need only test + * for a < b to discover whether a/b is 0. + * If your machine's division is fast enough, define FAST_DIVIDE. + */ +#ifdef FAST_DIVIDE +#define DIVIDE_BY(a,b) a /= b +#else +#define DIVIDE_BY(a,b) if (a >= b) a /= b; else a = 0 +#endif + if (temp < 0) { + temp = -temp; + temp += qval>>1; /* for rounding */ + DIVIDE_BY(temp, qval); + temp = -temp; + } else { + temp += qval>>1; /* for rounding */ + DIVIDE_BY(temp, qval); + } + output_ptr[i] = (JCOEF) temp; + } + } + } +} + + +#ifdef DCT_FLOAT_SUPPORTED + +METHODDEF(void) +forward_DCT_float (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY sample_data, JBLOCKROW coef_blocks, + JDIMENSION start_row, JDIMENSION start_col, + JDIMENSION num_blocks) +/* This version is used for floating-point DCT implementations. */ +{ + /* This routine is heavily used, so it's worth coding it tightly. */ + my_fdct_ptr fdct = (my_fdct_ptr) cinfo->fdct; + float_DCT_method_ptr do_dct = fdct->do_float_dct; + FAST_FLOAT * divisors = fdct->float_divisors[compptr->quant_tbl_no]; + FAST_FLOAT workspace[DCTSIZE2]; /* work area for FDCT subroutine */ + JDIMENSION bi; + + sample_data += start_row; /* fold in the vertical offset once */ + + for (bi = 0; bi < num_blocks; bi++, start_col += DCTSIZE) { + /* Load data into workspace, applying unsigned->signed conversion */ + { register FAST_FLOAT *workspaceptr; + register JSAMPROW elemptr; + register int elemr; + + workspaceptr = workspace; + for (elemr = 0; elemr < DCTSIZE; elemr++) { + elemptr = sample_data[elemr] + start_col; +#if DCTSIZE == 8 /* unroll the inner loop */ + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + *workspaceptr++ = (FAST_FLOAT)(GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); +#else + { register int elemc; + for (elemc = DCTSIZE; elemc > 0; elemc--) { + *workspaceptr++ = (FAST_FLOAT) + (GETJSAMPLE(*elemptr++) - CENTERJSAMPLE); + } + } +#endif + } + } + + /* Perform the DCT */ + (*do_dct) (workspace); + + /* Quantize/descale the coefficients, and store into coef_blocks[] */ + { register FAST_FLOAT temp; + register int i; + register JCOEFPTR output_ptr = coef_blocks[bi]; + + for (i = 0; i < DCTSIZE2; i++) { + /* Apply the quantization and scaling factor */ + temp = workspace[i] * divisors[i]; + /* Round to nearest integer. + * Since C does not specify the direction of rounding for negative + * quotients, we have to force the dividend positive for portability. + * The maximum coefficient size is +-16K (for 12-bit data), so this + * code should work for either 16-bit or 32-bit ints. + */ + output_ptr[i] = (JCOEF) ((int) (temp + (FAST_FLOAT) 16384.5) - 16384); + } + } + } +} + +#endif /* DCT_FLOAT_SUPPORTED */ + + +/* + * Initialize FDCT manager. + */ + +GLOBAL(void) +jinit_forward_dct (j_compress_ptr cinfo) +{ + my_fdct_ptr fdct; + int i; + + fdct = (my_fdct_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_fdct_controller)); + cinfo->fdct = (struct jpeg_forward_dct *) fdct; + fdct->pub.start_pass = start_pass_fdctmgr; + + switch (cinfo->dct_method) { +#ifdef DCT_ISLOW_SUPPORTED + case JDCT_ISLOW: + fdct->pub.forward_DCT = forward_DCT; + fdct->do_dct = jpeg_fdct_islow; + break; +#endif +#ifdef DCT_IFAST_SUPPORTED + case JDCT_IFAST: + fdct->pub.forward_DCT = forward_DCT; + fdct->do_dct = jpeg_fdct_ifast; + break; +#endif +#ifdef DCT_FLOAT_SUPPORTED + case JDCT_FLOAT: + fdct->pub.forward_DCT = forward_DCT_float; + fdct->do_float_dct = jpeg_fdct_float; + break; +#endif + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + break; + } + + /* Mark divisor tables unallocated */ + for (i = 0; i < NUM_QUANT_TBLS; i++) { + fdct->divisors[i] = NULL; +#ifdef DCT_FLOAT_SUPPORTED + fdct->float_divisors[i] = NULL; +#endif + } +} diff --git a/Engine/lib/ljpeg/jchuff.c b/Engine/lib/ljpeg/jchuff.c new file mode 100644 index 000000000..f23525054 --- /dev/null +++ b/Engine/lib/ljpeg/jchuff.c @@ -0,0 +1,909 @@ +/* + * jchuff.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains Huffman entropy encoding routines. + * + * Much of the complexity here has to do with supporting output suspension. + * If the data destination module demands suspension, we want to be able to + * back up to the start of the current MCU. To do this, we copy state + * variables into local working storage, and update them back to the + * permanent JPEG objects only upon successful completion of an MCU. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jchuff.h" /* Declarations shared with jcphuff.c */ + + +/* Expanded entropy encoder object for Huffman encoding. + * + * The savable_state subrecord contains fields that change within an MCU, + * but must not be updated permanently until we complete the MCU. + */ + +typedef struct { + INT32 put_buffer; /* current bit-accumulation buffer */ + int put_bits; /* # of bits now in it */ + int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ +} savable_state; + +/* This macro is to work around compilers with missing or broken + * structure assignment. You'll need to fix this code if you have + * such a compiler and you change MAX_COMPS_IN_SCAN. + */ + +#ifndef NO_STRUCT_ASSIGN +#define ASSIGN_STATE(dest,src) ((dest) = (src)) +#else +#if MAX_COMPS_IN_SCAN == 4 +#define ASSIGN_STATE(dest,src) \ + ((dest).put_buffer = (src).put_buffer, \ + (dest).put_bits = (src).put_bits, \ + (dest).last_dc_val[0] = (src).last_dc_val[0], \ + (dest).last_dc_val[1] = (src).last_dc_val[1], \ + (dest).last_dc_val[2] = (src).last_dc_val[2], \ + (dest).last_dc_val[3] = (src).last_dc_val[3]) +#endif +#endif + + +typedef struct { + struct jpeg_entropy_encoder pub; /* public fields */ + + savable_state saved; /* Bit buffer & DC state at start of MCU */ + + /* These fields are NOT loaded into local working state. */ + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + int next_restart_num; /* next restart number to write (0-7) */ + + /* Pointers to derived tables (these workspaces have image lifespan) */ + c_derived_tbl * dc_derived_tbls[NUM_HUFF_TBLS]; + c_derived_tbl * ac_derived_tbls[NUM_HUFF_TBLS]; + +#ifdef ENTROPY_OPT_SUPPORTED /* Statistics tables for optimization */ + long * dc_count_ptrs[NUM_HUFF_TBLS]; + long * ac_count_ptrs[NUM_HUFF_TBLS]; +#endif +} huff_entropy_encoder; + +typedef huff_entropy_encoder * huff_entropy_ptr; + +/* Working state while writing an MCU. + * This struct contains all the fields that are needed by subroutines. + */ + +typedef struct { + JOCTET * next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + savable_state cur; /* Current bit buffer & DC state */ + j_compress_ptr cinfo; /* dump_buffer needs access to this */ +} working_state; + + +/* Forward declarations */ +METHODDEF(boolean) encode_mcu_huff JPP((j_compress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(void) finish_pass_huff JPP((j_compress_ptr cinfo)); +#ifdef ENTROPY_OPT_SUPPORTED +METHODDEF(boolean) encode_mcu_gather JPP((j_compress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(void) finish_pass_gather JPP((j_compress_ptr cinfo)); +#endif + + +/* + * Initialize for a Huffman-compressed scan. + * If gather_statistics is TRUE, we do not output anything during the scan, + * just count the Huffman symbols used and generate Huffman code tables. + */ + +METHODDEF(void) +start_pass_huff (j_compress_ptr cinfo, boolean gather_statistics) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci, dctbl, actbl; + jpeg_component_info * compptr; + + if (gather_statistics) { +#ifdef ENTROPY_OPT_SUPPORTED + entropy->pub.encode_mcu = encode_mcu_gather; + entropy->pub.finish_pass = finish_pass_gather; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + entropy->pub.encode_mcu = encode_mcu_huff; + entropy->pub.finish_pass = finish_pass_huff; + } + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + dctbl = compptr->dc_tbl_no; + actbl = compptr->ac_tbl_no; + if (gather_statistics) { +#ifdef ENTROPY_OPT_SUPPORTED + /* Check for invalid table indexes */ + /* (make_c_derived_tbl does this in the other path) */ + if (dctbl < 0 || dctbl >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, dctbl); + if (actbl < 0 || actbl >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, actbl); + /* Allocate and zero the statistics tables */ + /* Note that jpeg_gen_optimal_table expects 257 entries in each table! */ + if (entropy->dc_count_ptrs[dctbl] == NULL) + entropy->dc_count_ptrs[dctbl] = (long *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + 257 * SIZEOF(long)); + MEMZERO(entropy->dc_count_ptrs[dctbl], 257 * SIZEOF(long)); + if (entropy->ac_count_ptrs[actbl] == NULL) + entropy->ac_count_ptrs[actbl] = (long *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + 257 * SIZEOF(long)); + MEMZERO(entropy->ac_count_ptrs[actbl], 257 * SIZEOF(long)); +#endif + } else { + /* Compute derived values for Huffman tables */ + /* We may do this more than once for a table, but it's not expensive */ + jpeg_make_c_derived_tbl(cinfo, TRUE, dctbl, + & entropy->dc_derived_tbls[dctbl]); + jpeg_make_c_derived_tbl(cinfo, FALSE, actbl, + & entropy->ac_derived_tbls[actbl]); + } + /* Initialize DC predictions to 0 */ + entropy->saved.last_dc_val[ci] = 0; + } + + /* Initialize bit buffer to empty */ + entropy->saved.put_buffer = 0; + entropy->saved.put_bits = 0; + + /* Initialize restart stuff */ + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num = 0; +} + + +/* + * Compute the derived values for a Huffman table. + * This routine also performs some validation checks on the table. + * + * Note this is also used by jcphuff.c. + */ + +GLOBAL(void) +jpeg_make_c_derived_tbl (j_compress_ptr cinfo, boolean isDC, int tblno, + c_derived_tbl ** pdtbl) +{ + JHUFF_TBL *htbl; + c_derived_tbl *dtbl; + int p, i, l, lastp, si, maxsymbol; + char huffsize[257]; + unsigned int huffcode[257]; + unsigned int code; + + /* Note that huffsize[] and huffcode[] are filled in code-length order, + * paralleling the order of the symbols themselves in htbl->huffval[]. + */ + + /* Find the input Huffman table */ + if (tblno < 0 || tblno >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tblno); + htbl = + isDC ? cinfo->dc_huff_tbl_ptrs[tblno] : cinfo->ac_huff_tbl_ptrs[tblno]; + if (htbl == NULL) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tblno); + + /* Allocate a workspace if we haven't already done so. */ + if (*pdtbl == NULL) + *pdtbl = (c_derived_tbl *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(c_derived_tbl)); + dtbl = *pdtbl; + + /* Figure C.1: make table of Huffman code length for each symbol */ + + p = 0; + for (l = 1; l <= 16; l++) { + i = (int) htbl->bits[l]; + if (i < 0 || p + i > 256) /* protect against table overrun */ + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + while (i--) + huffsize[p++] = (char) l; + } + huffsize[p] = 0; + lastp = p; + + /* Figure C.2: generate the codes themselves */ + /* We also validate that the counts represent a legal Huffman code tree. */ + + code = 0; + si = huffsize[0]; + p = 0; + while (huffsize[p]) { + while (((int) huffsize[p]) == si) { + huffcode[p++] = code; + code++; + } + /* code is now 1 more than the last code used for codelength si; but + * it must still fit in si bits, since no code is allowed to be all ones. + */ + if (((INT32) code) >= (((INT32) 1) << si)) + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + code <<= 1; + si++; + } + + /* Figure C.3: generate encoding tables */ + /* These are code and size indexed by symbol value */ + + /* Set all codeless symbols to have code length 0; + * this lets us detect duplicate VAL entries here, and later + * allows emit_bits to detect any attempt to emit such symbols. + */ + MEMZERO(dtbl->ehufsi, SIZEOF(dtbl->ehufsi)); + + /* This is also a convenient place to check for out-of-range + * and duplicated VAL entries. We allow 0..255 for AC symbols + * but only 0..15 for DC. (We could constrain them further + * based on data depth and mode, but this seems enough.) + */ + maxsymbol = isDC ? 15 : 255; + + for (p = 0; p < lastp; p++) { + i = htbl->huffval[p]; + if (i < 0 || i > maxsymbol || dtbl->ehufsi[i]) + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + dtbl->ehufco[i] = huffcode[p]; + dtbl->ehufsi[i] = huffsize[p]; + } +} + + +/* Outputting bytes to the file */ + +/* Emit a byte, taking 'action' if must suspend. */ +#define emit_byte(state,val,action) \ + { *(state)->next_output_byte++ = (JOCTET) (val); \ + if (--(state)->free_in_buffer == 0) \ + if (! dump_buffer(state)) \ + { action; } } + + +LOCAL(boolean) +dump_buffer (working_state * state) +/* Empty the output buffer; return TRUE if successful, FALSE if must suspend */ +{ + struct jpeg_destination_mgr * dest = state->cinfo->dest; + + if (! (*dest->empty_output_buffer) (state->cinfo)) + return FALSE; + /* After a successful buffer dump, must reset buffer pointers */ + state->next_output_byte = dest->next_output_byte; + state->free_in_buffer = dest->free_in_buffer; + return TRUE; +} + + +/* Outputting bits to the file */ + +/* Only the right 24 bits of put_buffer are used; the valid bits are + * left-justified in this part. At most 16 bits can be passed to emit_bits + * in one call, and we never retain more than 7 bits in put_buffer + * between calls, so 24 bits are sufficient. + */ + +INLINE +LOCAL(boolean) +emit_bits (working_state * state, unsigned int code, int size) +/* Emit some bits; return TRUE if successful, FALSE if must suspend */ +{ + /* This routine is heavily used, so it's worth coding tightly. */ + register INT32 put_buffer = (INT32) code; + register int put_bits = state->cur.put_bits; + + /* if size is 0, caller used an invalid Huffman table entry */ + if (size == 0) + ERREXIT(state->cinfo, JERR_HUFF_MISSING_CODE); + + put_buffer &= (((INT32) 1)<cur.put_buffer; /* and merge with old buffer contents */ + + while (put_bits >= 8) { + int c = (int) ((put_buffer >> 16) & 0xFF); + + emit_byte(state, c, return FALSE); + if (c == 0xFF) { /* need to stuff a zero byte? */ + emit_byte(state, 0, return FALSE); + } + put_buffer <<= 8; + put_bits -= 8; + } + + state->cur.put_buffer = put_buffer; /* update state variables */ + state->cur.put_bits = put_bits; + + return TRUE; +} + + +LOCAL(boolean) +flush_bits (working_state * state) +{ + if (! emit_bits(state, 0x7F, 7)) /* fill any partial byte with ones */ + return FALSE; + state->cur.put_buffer = 0; /* and reset bit-buffer to empty */ + state->cur.put_bits = 0; + return TRUE; +} + + +/* Encode a single block's worth of coefficients */ + +LOCAL(boolean) +encode_one_block (working_state * state, JCOEFPTR block, int last_dc_val, + c_derived_tbl *dctbl, c_derived_tbl *actbl) +{ + register int temp, temp2; + register int nbits; + register int k, r, i; + + /* Encode the DC coefficient difference per section F.1.2.1 */ + + temp = temp2 = block[0] - last_dc_val; + + if (temp < 0) { + temp = -temp; /* temp is abs value of input */ + /* For a negative input, want temp2 = bitwise complement of abs(input) */ + /* This code assumes we are on a two's complement machine */ + temp2--; + } + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 0; + while (temp) { + nbits++; + temp >>= 1; + } + /* Check for out-of-range coefficient values. + * Since we're encoding a difference, the range limit is twice as much. + */ + if (nbits > MAX_COEF_BITS+1) + ERREXIT(state->cinfo, JERR_BAD_DCT_COEF); + + /* Emit the Huffman-coded symbol for the number of bits */ + if (! emit_bits(state, dctbl->ehufco[nbits], dctbl->ehufsi[nbits])) + return FALSE; + + /* Emit that number of bits of the value, if positive, */ + /* or the complement of its magnitude, if negative. */ + if (nbits) /* emit_bits rejects calls with size 0 */ + if (! emit_bits(state, (unsigned int) temp2, nbits)) + return FALSE; + + /* Encode the AC coefficients per section F.1.2.2 */ + + r = 0; /* r = run length of zeros */ + + for (k = 1; k < DCTSIZE2; k++) { + if ((temp = block[jpeg_natural_order[k]]) == 0) { + r++; + } else { + /* if run length > 15, must emit special run-length-16 codes (0xF0) */ + while (r > 15) { + if (! emit_bits(state, actbl->ehufco[0xF0], actbl->ehufsi[0xF0])) + return FALSE; + r -= 16; + } + + temp2 = temp; + if (temp < 0) { + temp = -temp; /* temp is abs value of input */ + /* This code assumes we are on a two's complement machine */ + temp2--; + } + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 1; /* there must be at least one 1 bit */ + while ((temp >>= 1)) + nbits++; + /* Check for out-of-range coefficient values */ + if (nbits > MAX_COEF_BITS) + ERREXIT(state->cinfo, JERR_BAD_DCT_COEF); + + /* Emit Huffman symbol for run length / number of bits */ + i = (r << 4) + nbits; + if (! emit_bits(state, actbl->ehufco[i], actbl->ehufsi[i])) + return FALSE; + + /* Emit that number of bits of the value, if positive, */ + /* or the complement of its magnitude, if negative. */ + if (! emit_bits(state, (unsigned int) temp2, nbits)) + return FALSE; + + r = 0; + } + } + + /* If the last coef(s) were zero, emit an end-of-block code */ + if (r > 0) + if (! emit_bits(state, actbl->ehufco[0], actbl->ehufsi[0])) + return FALSE; + + return TRUE; +} + + +/* + * Emit a restart marker & resynchronize predictions. + */ + +LOCAL(boolean) +emit_restart (working_state * state, int restart_num) +{ + int ci; + + if (! flush_bits(state)) + return FALSE; + + emit_byte(state, 0xFF, return FALSE); + emit_byte(state, JPEG_RST0 + restart_num, return FALSE); + + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < state->cinfo->comps_in_scan; ci++) + state->cur.last_dc_val[ci] = 0; + + /* The restart counter is not updated until we successfully write the MCU. */ + + return TRUE; +} + + +/* + * Encode and output one MCU's worth of Huffman-compressed coefficients. + */ + +METHODDEF(boolean) +encode_mcu_huff (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + working_state state; + int blkn, ci; + jpeg_component_info * compptr; + + /* Load up working state */ + state.next_output_byte = cinfo->dest->next_output_byte; + state.free_in_buffer = cinfo->dest->free_in_buffer; + ASSIGN_STATE(state.cur, entropy->saved); + state.cinfo = cinfo; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! emit_restart(&state, entropy->next_restart_num)) + return FALSE; + } + + /* Encode the MCU data blocks */ + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + if (! encode_one_block(&state, + MCU_data[blkn][0], state.cur.last_dc_val[ci], + entropy->dc_derived_tbls[compptr->dc_tbl_no], + entropy->ac_derived_tbls[compptr->ac_tbl_no])) + return FALSE; + /* Update last_dc_val */ + state.cur.last_dc_val[ci] = MCU_data[blkn][0][0]; + } + + /* Completed MCU, so update state */ + cinfo->dest->next_output_byte = state.next_output_byte; + cinfo->dest->free_in_buffer = state.free_in_buffer; + ASSIGN_STATE(entropy->saved, state.cur); + + /* Update restart-interval state too */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + return TRUE; +} + + +/* + * Finish up at the end of a Huffman-compressed scan. + */ + +METHODDEF(void) +finish_pass_huff (j_compress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + working_state state; + + /* Load up working state ... flush_bits needs it */ + state.next_output_byte = cinfo->dest->next_output_byte; + state.free_in_buffer = cinfo->dest->free_in_buffer; + ASSIGN_STATE(state.cur, entropy->saved); + state.cinfo = cinfo; + + /* Flush out the last data */ + if (! flush_bits(&state)) + ERREXIT(cinfo, JERR_CANT_SUSPEND); + + /* Update state */ + cinfo->dest->next_output_byte = state.next_output_byte; + cinfo->dest->free_in_buffer = state.free_in_buffer; + ASSIGN_STATE(entropy->saved, state.cur); +} + + +/* + * Huffman coding optimization. + * + * We first scan the supplied data and count the number of uses of each symbol + * that is to be Huffman-coded. (This process MUST agree with the code above.) + * Then we build a Huffman coding tree for the observed counts. + * Symbols which are not needed at all for the particular image are not + * assigned any code, which saves space in the DHT marker as well as in + * the compressed data. + */ + +#ifdef ENTROPY_OPT_SUPPORTED + + +/* Process a single block's worth of coefficients */ + +LOCAL(void) +htest_one_block (j_compress_ptr cinfo, JCOEFPTR block, int last_dc_val, + long dc_counts[], long ac_counts[]) +{ + register int temp; + register int nbits; + register int k, r; + + /* Encode the DC coefficient difference per section F.1.2.1 */ + + temp = block[0] - last_dc_val; + if (temp < 0) + temp = -temp; + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 0; + while (temp) { + nbits++; + temp >>= 1; + } + /* Check for out-of-range coefficient values. + * Since we're encoding a difference, the range limit is twice as much. + */ + if (nbits > MAX_COEF_BITS+1) + ERREXIT(cinfo, JERR_BAD_DCT_COEF); + + /* Count the Huffman symbol for the number of bits */ + dc_counts[nbits]++; + + /* Encode the AC coefficients per section F.1.2.2 */ + + r = 0; /* r = run length of zeros */ + + for (k = 1; k < DCTSIZE2; k++) { + if ((temp = block[jpeg_natural_order[k]]) == 0) { + r++; + } else { + /* if run length > 15, must emit special run-length-16 codes (0xF0) */ + while (r > 15) { + ac_counts[0xF0]++; + r -= 16; + } + + /* Find the number of bits needed for the magnitude of the coefficient */ + if (temp < 0) + temp = -temp; + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 1; /* there must be at least one 1 bit */ + while ((temp >>= 1)) + nbits++; + /* Check for out-of-range coefficient values */ + if (nbits > MAX_COEF_BITS) + ERREXIT(cinfo, JERR_BAD_DCT_COEF); + + /* Count Huffman symbol for run length / number of bits */ + ac_counts[(r << 4) + nbits]++; + + r = 0; + } + } + + /* If the last coef(s) were zero, emit an end-of-block code */ + if (r > 0) + ac_counts[0]++; +} + + +/* + * Trial-encode one MCU's worth of Huffman-compressed coefficients. + * No data is actually output, so no suspension return is possible. + */ + +METHODDEF(boolean) +encode_mcu_gather (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int blkn, ci; + jpeg_component_info * compptr; + + /* Take care of restart intervals if needed */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) + entropy->saved.last_dc_val[ci] = 0; + /* Update restart state */ + entropy->restarts_to_go = cinfo->restart_interval; + } + entropy->restarts_to_go--; + } + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + htest_one_block(cinfo, MCU_data[blkn][0], entropy->saved.last_dc_val[ci], + entropy->dc_count_ptrs[compptr->dc_tbl_no], + entropy->ac_count_ptrs[compptr->ac_tbl_no]); + entropy->saved.last_dc_val[ci] = MCU_data[blkn][0][0]; + } + + return TRUE; +} + + +/* + * Generate the best Huffman code table for the given counts, fill htbl. + * Note this is also used by jcphuff.c. + * + * The JPEG standard requires that no symbol be assigned a codeword of all + * one bits (so that padding bits added at the end of a compressed segment + * can't look like a valid code). Because of the canonical ordering of + * codewords, this just means that there must be an unused slot in the + * longest codeword length category. Section K.2 of the JPEG spec suggests + * reserving such a slot by pretending that symbol 256 is a valid symbol + * with count 1. In theory that's not optimal; giving it count zero but + * including it in the symbol set anyway should give a better Huffman code. + * But the theoretically better code actually seems to come out worse in + * practice, because it produces more all-ones bytes (which incur stuffed + * zero bytes in the final file). In any case the difference is tiny. + * + * The JPEG standard requires Huffman codes to be no more than 16 bits long. + * If some symbols have a very small but nonzero probability, the Huffman tree + * must be adjusted to meet the code length restriction. We currently use + * the adjustment method suggested in JPEG section K.2. This method is *not* + * optimal; it may not choose the best possible limited-length code. But + * typically only very-low-frequency symbols will be given less-than-optimal + * lengths, so the code is almost optimal. Experimental comparisons against + * an optimal limited-length-code algorithm indicate that the difference is + * microscopic --- usually less than a hundredth of a percent of total size. + * So the extra complexity of an optimal algorithm doesn't seem worthwhile. + */ + +GLOBAL(void) +jpeg_gen_optimal_table (j_compress_ptr cinfo, JHUFF_TBL * htbl, long freq[]) +{ +#define MAX_CLEN 32 /* assumed maximum initial code length */ + UINT8 bits[MAX_CLEN+1]; /* bits[k] = # of symbols with code length k */ + int codesize[257]; /* codesize[k] = code length of symbol k */ + int others[257]; /* next symbol in current branch of tree */ + int c1, c2; + int p, i, j; + long v; + + /* This algorithm is explained in section K.2 of the JPEG standard */ + + MEMZERO(bits, SIZEOF(bits)); + MEMZERO(codesize, SIZEOF(codesize)); + for (i = 0; i < 257; i++) + others[i] = -1; /* init links to empty */ + + freq[256] = 1; /* make sure 256 has a nonzero count */ + /* Including the pseudo-symbol 256 in the Huffman procedure guarantees + * that no real symbol is given code-value of all ones, because 256 + * will be placed last in the largest codeword category. + */ + + /* Huffman's basic algorithm to assign optimal code lengths to symbols */ + + for (;;) { + /* Find the smallest nonzero frequency, set c1 = its symbol */ + /* In case of ties, take the larger symbol number */ + c1 = -1; + v = 1000000000L; + for (i = 0; i <= 256; i++) { + if (freq[i] && freq[i] <= v) { + v = freq[i]; + c1 = i; + } + } + + /* Find the next smallest nonzero frequency, set c2 = its symbol */ + /* In case of ties, take the larger symbol number */ + c2 = -1; + v = 1000000000L; + for (i = 0; i <= 256; i++) { + if (freq[i] && freq[i] <= v && i != c1) { + v = freq[i]; + c2 = i; + } + } + + /* Done if we've merged everything into one frequency */ + if (c2 < 0) + break; + + /* Else merge the two counts/trees */ + freq[c1] += freq[c2]; + freq[c2] = 0; + + /* Increment the codesize of everything in c1's tree branch */ + codesize[c1]++; + while (others[c1] >= 0) { + c1 = others[c1]; + codesize[c1]++; + } + + others[c1] = c2; /* chain c2 onto c1's tree branch */ + + /* Increment the codesize of everything in c2's tree branch */ + codesize[c2]++; + while (others[c2] >= 0) { + c2 = others[c2]; + codesize[c2]++; + } + } + + /* Now count the number of symbols of each code length */ + for (i = 0; i <= 256; i++) { + if (codesize[i]) { + /* The JPEG standard seems to think that this can't happen, */ + /* but I'm paranoid... */ + if (codesize[i] > MAX_CLEN) + ERREXIT(cinfo, JERR_HUFF_CLEN_OVERFLOW); + + bits[codesize[i]]++; + } + } + + /* JPEG doesn't allow symbols with code lengths over 16 bits, so if the pure + * Huffman procedure assigned any such lengths, we must adjust the coding. + * Here is what the JPEG spec says about how this next bit works: + * Since symbols are paired for the longest Huffman code, the symbols are + * removed from this length category two at a time. The prefix for the pair + * (which is one bit shorter) is allocated to one of the pair; then, + * skipping the BITS entry for that prefix length, a code word from the next + * shortest nonzero BITS entry is converted into a prefix for two code words + * one bit longer. + */ + + for (i = MAX_CLEN; i > 16; i--) { + while (bits[i] > 0) { + j = i - 2; /* find length of new prefix to be used */ + while (bits[j] == 0) + j--; + + bits[i] -= 2; /* remove two symbols */ + bits[i-1]++; /* one goes in this length */ + bits[j+1] += 2; /* two new symbols in this length */ + bits[j]--; /* symbol of this length is now a prefix */ + } + } + + /* Remove the count for the pseudo-symbol 256 from the largest codelength */ + while (bits[i] == 0) /* find largest codelength still in use */ + i--; + bits[i]--; + + /* Return final symbol counts (only for lengths 0..16) */ + MEMCOPY(htbl->bits, bits, SIZEOF(htbl->bits)); + + /* Return a list of the symbols sorted by code length */ + /* It's not real clear to me why we don't need to consider the codelength + * changes made above, but the JPEG spec seems to think this works. + */ + p = 0; + for (i = 1; i <= MAX_CLEN; i++) { + for (j = 0; j <= 255; j++) { + if (codesize[j] == i) { + htbl->huffval[p] = (UINT8) j; + p++; + } + } + } + + /* Set sent_table FALSE so updated table will be written to JPEG file. */ + htbl->sent_table = FALSE; +} + + +/* + * Finish up a statistics-gathering pass and create the new Huffman tables. + */ + +METHODDEF(void) +finish_pass_gather (j_compress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci, dctbl, actbl; + jpeg_component_info * compptr; + JHUFF_TBL **htblptr; + boolean did_dc[NUM_HUFF_TBLS]; + boolean did_ac[NUM_HUFF_TBLS]; + + /* It's important not to apply jpeg_gen_optimal_table more than once + * per table, because it clobbers the input frequency counts! + */ + MEMZERO(did_dc, SIZEOF(did_dc)); + MEMZERO(did_ac, SIZEOF(did_ac)); + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + dctbl = compptr->dc_tbl_no; + actbl = compptr->ac_tbl_no; + if (! did_dc[dctbl]) { + htblptr = & cinfo->dc_huff_tbl_ptrs[dctbl]; + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + jpeg_gen_optimal_table(cinfo, *htblptr, entropy->dc_count_ptrs[dctbl]); + did_dc[dctbl] = TRUE; + } + if (! did_ac[actbl]) { + htblptr = & cinfo->ac_huff_tbl_ptrs[actbl]; + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + jpeg_gen_optimal_table(cinfo, *htblptr, entropy->ac_count_ptrs[actbl]); + did_ac[actbl] = TRUE; + } + } +} + + +#endif /* ENTROPY_OPT_SUPPORTED */ + + +/* + * Module initialization routine for Huffman entropy encoding. + */ + +GLOBAL(void) +jinit_huff_encoder (j_compress_ptr cinfo) +{ + huff_entropy_ptr entropy; + int i; + + entropy = (huff_entropy_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(huff_entropy_encoder)); + cinfo->entropy = (struct jpeg_entropy_encoder *) entropy; + entropy->pub.start_pass = start_pass_huff; + + /* Mark tables unallocated */ + for (i = 0; i < NUM_HUFF_TBLS; i++) { + entropy->dc_derived_tbls[i] = entropy->ac_derived_tbls[i] = NULL; +#ifdef ENTROPY_OPT_SUPPORTED + entropy->dc_count_ptrs[i] = entropy->ac_count_ptrs[i] = NULL; +#endif + } +} diff --git a/Engine/lib/ljpeg/jchuff.h b/Engine/lib/ljpeg/jchuff.h new file mode 100644 index 000000000..a9599fc1e --- /dev/null +++ b/Engine/lib/ljpeg/jchuff.h @@ -0,0 +1,47 @@ +/* + * jchuff.h + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains declarations for Huffman entropy encoding routines + * that are shared between the sequential encoder (jchuff.c) and the + * progressive encoder (jcphuff.c). No other modules need to see these. + */ + +/* The legal range of a DCT coefficient is + * -1024 .. +1023 for 8-bit data; + * -16384 .. +16383 for 12-bit data. + * Hence the magnitude should always fit in 10 or 14 bits respectively. + */ + +#if BITS_IN_JSAMPLE == 8 +#define MAX_COEF_BITS 10 +#else +#define MAX_COEF_BITS 14 +#endif + +/* Derived data constructed for each Huffman table */ + +typedef struct { + unsigned int ehufco[256]; /* code for each symbol */ + char ehufsi[256]; /* length of code for each symbol */ + /* If no code has been allocated for a symbol S, ehufsi[S] contains 0 */ +} c_derived_tbl; + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_make_c_derived_tbl jMkCDerived +#define jpeg_gen_optimal_table jGenOptTbl +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + +/* Expand a Huffman table definition into the derived format */ +EXTERN(void) jpeg_make_c_derived_tbl + JPP((j_compress_ptr cinfo, boolean isDC, int tblno, + c_derived_tbl ** pdtbl)); + +/* Generate an optimal table definition given the specified counts */ +EXTERN(void) jpeg_gen_optimal_table + JPP((j_compress_ptr cinfo, JHUFF_TBL * htbl, long freq[])); diff --git a/Engine/lib/ljpeg/jcinit.c b/Engine/lib/ljpeg/jcinit.c new file mode 100644 index 000000000..5efffe331 --- /dev/null +++ b/Engine/lib/ljpeg/jcinit.c @@ -0,0 +1,72 @@ +/* + * jcinit.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains initialization logic for the JPEG compressor. + * This routine is in charge of selecting the modules to be executed and + * making an initialization call to each one. + * + * Logically, this code belongs in jcmaster.c. It's split out because + * linking this routine implies linking the entire compression library. + * For a transcoding-only application, we want to be able to use jcmaster.c + * without linking in the whole library. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * Master selection of compression modules. + * This is done once at the start of processing an image. We determine + * which modules will be used and give them appropriate initialization calls. + */ + +GLOBAL(void) +jinit_compress_master (j_compress_ptr cinfo) +{ + /* Initialize master control (includes parameter checking/processing) */ + jinit_c_master_control(cinfo, FALSE /* full compression */); + + /* Preprocessing */ + if (! cinfo->raw_data_in) { + jinit_color_converter(cinfo); + jinit_downsampler(cinfo); + jinit_c_prep_controller(cinfo, FALSE /* never need full buffer here */); + } + /* Forward DCT */ + jinit_forward_dct(cinfo); + /* Entropy encoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + if (cinfo->progressive_mode) { +#ifdef C_PROGRESSIVE_SUPPORTED + jinit_phuff_encoder(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else + jinit_huff_encoder(cinfo); + } + + /* Need a full-image coefficient buffer in any multi-pass mode. */ + jinit_c_coef_controller(cinfo, + (boolean) (cinfo->num_scans > 1 || cinfo->optimize_coding)); + jinit_c_main_controller(cinfo, FALSE /* never need full buffer here */); + + jinit_marker_writer(cinfo); + + /* We can now tell the memory manager to allocate virtual arrays. */ + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); + + /* Write the datastream header (SOI) immediately. + * Frame and scan headers are postponed till later. + * This lets application insert special markers after the SOI. + */ + (*cinfo->marker->write_file_header) (cinfo); +} diff --git a/Engine/lib/ljpeg/jcmainct.c b/Engine/lib/ljpeg/jcmainct.c new file mode 100644 index 000000000..e0279a7e0 --- /dev/null +++ b/Engine/lib/ljpeg/jcmainct.c @@ -0,0 +1,293 @@ +/* + * jcmainct.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the main buffer controller for compression. + * The main buffer lies between the pre-processor and the JPEG + * compressor proper; it holds downsampled data in the JPEG colorspace. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Note: currently, there is no operating mode in which a full-image buffer + * is needed at this step. If there were, that mode could not be used with + * "raw data" input, since this module is bypassed in that case. However, + * we've left the code here for possible use in special applications. + */ +#undef FULL_MAIN_BUFFER_SUPPORTED + + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_c_main_controller pub; /* public fields */ + + JDIMENSION cur_iMCU_row; /* number of current iMCU row */ + JDIMENSION rowgroup_ctr; /* counts row groups received in iMCU row */ + boolean suspended; /* remember if we suspended output */ + J_BUF_MODE pass_mode; /* current operating mode */ + + /* If using just a strip buffer, this points to the entire set of buffers + * (we allocate one for each component). In the full-image case, this + * points to the currently accessible strips of the virtual arrays. + */ + JSAMPARRAY buffer[MAX_COMPONENTS]; + +#ifdef FULL_MAIN_BUFFER_SUPPORTED + /* If using full-image storage, this array holds pointers to virtual-array + * control blocks for each component. Unused if not full-image storage. + */ + jvirt_sarray_ptr whole_image[MAX_COMPONENTS]; +#endif +} my_main_controller; + +typedef my_main_controller * my_main_ptr; + + +/* Forward declarations */ +METHODDEF(void) process_data_simple_main + JPP((j_compress_ptr cinfo, JSAMPARRAY input_buf, + JDIMENSION *in_row_ctr, JDIMENSION in_rows_avail)); +#ifdef FULL_MAIN_BUFFER_SUPPORTED +METHODDEF(void) process_data_buffer_main + JPP((j_compress_ptr cinfo, JSAMPARRAY input_buf, + JDIMENSION *in_row_ctr, JDIMENSION in_rows_avail)); +#endif + + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_main (j_compress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + + /* Do nothing in raw-data mode. */ + if (cinfo->raw_data_in) + return; + + main->cur_iMCU_row = 0; /* initialize counters */ + main->rowgroup_ctr = 0; + main->suspended = FALSE; + main->pass_mode = pass_mode; /* save mode for use by process_data */ + + switch (pass_mode) { + case JBUF_PASS_THRU: +#ifdef FULL_MAIN_BUFFER_SUPPORTED + if (main->whole_image[0] != NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); +#endif + main->pub.process_data = process_data_simple_main; + break; +#ifdef FULL_MAIN_BUFFER_SUPPORTED + case JBUF_SAVE_SOURCE: + case JBUF_CRANK_DEST: + case JBUF_SAVE_AND_PASS: + if (main->whole_image[0] == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + main->pub.process_data = process_data_buffer_main; + break; +#endif + default: + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + break; + } +} + + +/* + * Process some data. + * This routine handles the simple pass-through mode, + * where we have only a strip buffer. + */ + +METHODDEF(void) +process_data_simple_main (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail) +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + + while (main->cur_iMCU_row < cinfo->total_iMCU_rows) { + /* Read input data if we haven't filled the main buffer yet */ + if (main->rowgroup_ctr < DCTSIZE) + (*cinfo->prep->pre_process_data) (cinfo, + input_buf, in_row_ctr, in_rows_avail, + main->buffer, &main->rowgroup_ctr, + (JDIMENSION) DCTSIZE); + + /* If we don't have a full iMCU row buffered, return to application for + * more data. Note that preprocessor will always pad to fill the iMCU row + * at the bottom of the image. + */ + if (main->rowgroup_ctr != DCTSIZE) + return; + + /* Send the completed row to the compressor */ + if (! (*cinfo->coef->compress_data) (cinfo, main->buffer)) { + /* If compressor did not consume the whole row, then we must need to + * suspend processing and return to the application. In this situation + * we pretend we didn't yet consume the last input row; otherwise, if + * it happened to be the last row of the image, the application would + * think we were done. + */ + if (! main->suspended) { + (*in_row_ctr)--; + main->suspended = TRUE; + } + return; + } + /* We did finish the row. Undo our little suspension hack if a previous + * call suspended; then mark the main buffer empty. + */ + if (main->suspended) { + (*in_row_ctr)++; + main->suspended = FALSE; + } + main->rowgroup_ctr = 0; + main->cur_iMCU_row++; + } +} + + +#ifdef FULL_MAIN_BUFFER_SUPPORTED + +/* + * Process some data. + * This routine handles all of the modes that use a full-size buffer. + */ + +METHODDEF(void) +process_data_buffer_main (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail) +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + int ci; + jpeg_component_info *compptr; + boolean writing = (main->pass_mode != JBUF_CRANK_DEST); + + while (main->cur_iMCU_row < cinfo->total_iMCU_rows) { + /* Realign the virtual buffers if at the start of an iMCU row. */ + if (main->rowgroup_ctr == 0) { + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + main->buffer[ci] = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, main->whole_image[ci], + main->cur_iMCU_row * (compptr->v_samp_factor * DCTSIZE), + (JDIMENSION) (compptr->v_samp_factor * DCTSIZE), writing); + } + /* In a read pass, pretend we just read some source data. */ + if (! writing) { + *in_row_ctr += cinfo->max_v_samp_factor * DCTSIZE; + main->rowgroup_ctr = DCTSIZE; + } + } + + /* If a write pass, read input data until the current iMCU row is full. */ + /* Note: preprocessor will pad if necessary to fill the last iMCU row. */ + if (writing) { + (*cinfo->prep->pre_process_data) (cinfo, + input_buf, in_row_ctr, in_rows_avail, + main->buffer, &main->rowgroup_ctr, + (JDIMENSION) DCTSIZE); + /* Return to application if we need more data to fill the iMCU row. */ + if (main->rowgroup_ctr < DCTSIZE) + return; + } + + /* Emit data, unless this is a sink-only pass. */ + if (main->pass_mode != JBUF_SAVE_SOURCE) { + if (! (*cinfo->coef->compress_data) (cinfo, main->buffer)) { + /* If compressor did not consume the whole row, then we must need to + * suspend processing and return to the application. In this situation + * we pretend we didn't yet consume the last input row; otherwise, if + * it happened to be the last row of the image, the application would + * think we were done. + */ + if (! main->suspended) { + (*in_row_ctr)--; + main->suspended = TRUE; + } + return; + } + /* We did finish the row. Undo our little suspension hack if a previous + * call suspended; then mark the main buffer empty. + */ + if (main->suspended) { + (*in_row_ctr)++; + main->suspended = FALSE; + } + } + + /* If get here, we are done with this iMCU row. Mark buffer empty. */ + main->rowgroup_ctr = 0; + main->cur_iMCU_row++; + } +} + +#endif /* FULL_MAIN_BUFFER_SUPPORTED */ + + +/* + * Initialize main buffer controller. + */ + +GLOBAL(void) +jinit_c_main_controller (j_compress_ptr cinfo, boolean need_full_buffer) +{ + my_main_ptr main; + int ci; + jpeg_component_info *compptr; + + main = (my_main_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_main_controller)); + cinfo->main = (struct jpeg_c_main_controller *) main; + main->pub.start_pass = start_pass_main; + + /* We don't need to create a buffer in raw-data mode. */ + if (cinfo->raw_data_in) + return; + + /* Create the buffer. It holds downsampled data, so each component + * may be of a different size. + */ + if (need_full_buffer) { +#ifdef FULL_MAIN_BUFFER_SUPPORTED + /* Allocate a full-image virtual array for each component */ + /* Note we pad the bottom to a multiple of the iMCU height */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + main->whole_image[ci] = (*cinfo->mem->request_virt_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + compptr->width_in_blocks * DCTSIZE, + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + (long) compptr->v_samp_factor) * DCTSIZE, + (JDIMENSION) (compptr->v_samp_factor * DCTSIZE)); + } +#else + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); +#endif + } else { +#ifdef FULL_MAIN_BUFFER_SUPPORTED + main->whole_image[0] = NULL; /* flag for no virtual arrays */ +#endif + /* Allocate a strip buffer for each component */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + main->buffer[ci] = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + compptr->width_in_blocks * DCTSIZE, + (JDIMENSION) (compptr->v_samp_factor * DCTSIZE)); + } + } +} diff --git a/Engine/lib/ljpeg/jcmarker.c b/Engine/lib/ljpeg/jcmarker.c new file mode 100644 index 000000000..3d1e6c6d5 --- /dev/null +++ b/Engine/lib/ljpeg/jcmarker.c @@ -0,0 +1,664 @@ +/* + * jcmarker.c + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to write JPEG datastream markers. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +typedef enum { /* JPEG marker codes */ + M_SOF0 = 0xc0, + M_SOF1 = 0xc1, + M_SOF2 = 0xc2, + M_SOF3 = 0xc3, + + M_SOF5 = 0xc5, + M_SOF6 = 0xc6, + M_SOF7 = 0xc7, + + M_JPG = 0xc8, + M_SOF9 = 0xc9, + M_SOF10 = 0xca, + M_SOF11 = 0xcb, + + M_SOF13 = 0xcd, + M_SOF14 = 0xce, + M_SOF15 = 0xcf, + + M_DHT = 0xc4, + + M_DAC = 0xcc, + + M_RST0 = 0xd0, + M_RST1 = 0xd1, + M_RST2 = 0xd2, + M_RST3 = 0xd3, + M_RST4 = 0xd4, + M_RST5 = 0xd5, + M_RST6 = 0xd6, + M_RST7 = 0xd7, + + M_SOI = 0xd8, + M_EOI = 0xd9, + M_SOS = 0xda, + M_DQT = 0xdb, + M_DNL = 0xdc, + M_DRI = 0xdd, + M_DHP = 0xde, + M_EXP = 0xdf, + + M_APP0 = 0xe0, + M_APP1 = 0xe1, + M_APP2 = 0xe2, + M_APP3 = 0xe3, + M_APP4 = 0xe4, + M_APP5 = 0xe5, + M_APP6 = 0xe6, + M_APP7 = 0xe7, + M_APP8 = 0xe8, + M_APP9 = 0xe9, + M_APP10 = 0xea, + M_APP11 = 0xeb, + M_APP12 = 0xec, + M_APP13 = 0xed, + M_APP14 = 0xee, + M_APP15 = 0xef, + + M_JPG0 = 0xf0, + M_JPG13 = 0xfd, + M_COM = 0xfe, + + M_TEM = 0x01, + + M_ERROR = 0x100 +} JPEG_MARKER; + + +/* Private state */ + +typedef struct { + struct jpeg_marker_writer pub; /* public fields */ + + unsigned int last_restart_interval; /* last DRI value emitted; 0 after SOI */ +} my_marker_writer; + +typedef my_marker_writer * my_marker_ptr; + + +/* + * Basic output routines. + * + * Note that we do not support suspension while writing a marker. + * Therefore, an application using suspension must ensure that there is + * enough buffer space for the initial markers (typ. 600-700 bytes) before + * calling jpeg_start_compress, and enough space to write the trailing EOI + * (a few bytes) before calling jpeg_finish_compress. Multipass compression + * modes are not supported at all with suspension, so those two are the only + * points where markers will be written. + */ + +LOCAL(void) +emit_byte (j_compress_ptr cinfo, int val) +/* Emit a byte */ +{ + struct jpeg_destination_mgr * dest = cinfo->dest; + + *(dest->next_output_byte)++ = (JOCTET) val; + if (--dest->free_in_buffer == 0) { + if (! (*dest->empty_output_buffer) (cinfo)) + ERREXIT(cinfo, JERR_CANT_SUSPEND); + } +} + + +LOCAL(void) +emit_marker (j_compress_ptr cinfo, JPEG_MARKER mark) +/* Emit a marker code */ +{ + emit_byte(cinfo, 0xFF); + emit_byte(cinfo, (int) mark); +} + + +LOCAL(void) +emit_2bytes (j_compress_ptr cinfo, int value) +/* Emit a 2-byte integer; these are always MSB first in JPEG files */ +{ + emit_byte(cinfo, (value >> 8) & 0xFF); + emit_byte(cinfo, value & 0xFF); +} + + +/* + * Routines to write specific marker types. + */ + +LOCAL(int) +emit_dqt (j_compress_ptr cinfo, int index) +/* Emit a DQT marker */ +/* Returns the precision used (0 = 8bits, 1 = 16bits) for baseline checking */ +{ + JQUANT_TBL * qtbl = cinfo->quant_tbl_ptrs[index]; + int prec; + int i; + + if (qtbl == NULL) + ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, index); + + prec = 0; + for (i = 0; i < DCTSIZE2; i++) { + if (qtbl->quantval[i] > 255) + prec = 1; + } + + if (! qtbl->sent_table) { + emit_marker(cinfo, M_DQT); + + emit_2bytes(cinfo, prec ? DCTSIZE2*2 + 1 + 2 : DCTSIZE2 + 1 + 2); + + emit_byte(cinfo, index + (prec<<4)); + + for (i = 0; i < DCTSIZE2; i++) { + /* The table entries must be emitted in zigzag order. */ + unsigned int qval = qtbl->quantval[jpeg_natural_order[i]]; + if (prec) + emit_byte(cinfo, (int) (qval >> 8)); + emit_byte(cinfo, (int) (qval & 0xFF)); + } + + qtbl->sent_table = TRUE; + } + + return prec; +} + + +LOCAL(void) +emit_dht (j_compress_ptr cinfo, int index, boolean is_ac) +/* Emit a DHT marker */ +{ + JHUFF_TBL * htbl; + int length, i; + + if (is_ac) { + htbl = cinfo->ac_huff_tbl_ptrs[index]; + index += 0x10; /* output index has AC bit set */ + } else { + htbl = cinfo->dc_huff_tbl_ptrs[index]; + } + + if (htbl == NULL) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, index); + + if (! htbl->sent_table) { + emit_marker(cinfo, M_DHT); + + length = 0; + for (i = 1; i <= 16; i++) + length += htbl->bits[i]; + + emit_2bytes(cinfo, length + 2 + 1 + 16); + emit_byte(cinfo, index); + + for (i = 1; i <= 16; i++) + emit_byte(cinfo, htbl->bits[i]); + + for (i = 0; i < length; i++) + emit_byte(cinfo, htbl->huffval[i]); + + htbl->sent_table = TRUE; + } +} + + +LOCAL(void) +emit_dac (j_compress_ptr cinfo) +/* Emit a DAC marker */ +/* Since the useful info is so small, we want to emit all the tables in */ +/* one DAC marker. Therefore this routine does its own scan of the table. */ +{ +#ifdef C_ARITH_CODING_SUPPORTED + char dc_in_use[NUM_ARITH_TBLS]; + char ac_in_use[NUM_ARITH_TBLS]; + int length, i; + jpeg_component_info *compptr; + + for (i = 0; i < NUM_ARITH_TBLS; i++) + dc_in_use[i] = ac_in_use[i] = 0; + + for (i = 0; i < cinfo->comps_in_scan; i++) { + compptr = cinfo->cur_comp_info[i]; + dc_in_use[compptr->dc_tbl_no] = 1; + ac_in_use[compptr->ac_tbl_no] = 1; + } + + length = 0; + for (i = 0; i < NUM_ARITH_TBLS; i++) + length += dc_in_use[i] + ac_in_use[i]; + + emit_marker(cinfo, M_DAC); + + emit_2bytes(cinfo, length*2 + 2); + + for (i = 0; i < NUM_ARITH_TBLS; i++) { + if (dc_in_use[i]) { + emit_byte(cinfo, i); + emit_byte(cinfo, cinfo->arith_dc_L[i] + (cinfo->arith_dc_U[i]<<4)); + } + if (ac_in_use[i]) { + emit_byte(cinfo, i + 0x10); + emit_byte(cinfo, cinfo->arith_ac_K[i]); + } + } +#endif /* C_ARITH_CODING_SUPPORTED */ +} + + +LOCAL(void) +emit_dri (j_compress_ptr cinfo) +/* Emit a DRI marker */ +{ + emit_marker(cinfo, M_DRI); + + emit_2bytes(cinfo, 4); /* fixed length */ + + emit_2bytes(cinfo, (int) cinfo->restart_interval); +} + + +LOCAL(void) +emit_sof (j_compress_ptr cinfo, JPEG_MARKER code) +/* Emit a SOF marker */ +{ + int ci; + jpeg_component_info *compptr; + + emit_marker(cinfo, code); + + emit_2bytes(cinfo, 3 * cinfo->num_components + 2 + 5 + 1); /* length */ + + /* Make sure image isn't bigger than SOF field can handle */ + if ((long) cinfo->image_height > 65535L || + (long) cinfo->image_width > 65535L) + ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) 65535); + + emit_byte(cinfo, cinfo->data_precision); + emit_2bytes(cinfo, (int) cinfo->image_height); + emit_2bytes(cinfo, (int) cinfo->image_width); + + emit_byte(cinfo, cinfo->num_components); + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + emit_byte(cinfo, compptr->component_id); + emit_byte(cinfo, (compptr->h_samp_factor << 4) + compptr->v_samp_factor); + emit_byte(cinfo, compptr->quant_tbl_no); + } +} + + +LOCAL(void) +emit_sos (j_compress_ptr cinfo) +/* Emit a SOS marker */ +{ + int i, td, ta; + jpeg_component_info *compptr; + + emit_marker(cinfo, M_SOS); + + emit_2bytes(cinfo, 2 * cinfo->comps_in_scan + 2 + 1 + 3); /* length */ + + emit_byte(cinfo, cinfo->comps_in_scan); + + for (i = 0; i < cinfo->comps_in_scan; i++) { + compptr = cinfo->cur_comp_info[i]; + emit_byte(cinfo, compptr->component_id); + td = compptr->dc_tbl_no; + ta = compptr->ac_tbl_no; + if (cinfo->progressive_mode) { + /* Progressive mode: only DC or only AC tables are used in one scan; + * furthermore, Huffman coding of DC refinement uses no table at all. + * We emit 0 for unused field(s); this is recommended by the P&M text + * but does not seem to be specified in the standard. + */ + if (cinfo->Ss == 0) { + ta = 0; /* DC scan */ + if (cinfo->Ah != 0 && !cinfo->arith_code) + td = 0; /* no DC table either */ + } else { + td = 0; /* AC scan */ + } + } + emit_byte(cinfo, (td << 4) + ta); + } + + emit_byte(cinfo, cinfo->Ss); + emit_byte(cinfo, cinfo->Se); + emit_byte(cinfo, (cinfo->Ah << 4) + cinfo->Al); +} + + +LOCAL(void) +emit_jfif_app0 (j_compress_ptr cinfo) +/* Emit a JFIF-compliant APP0 marker */ +{ + /* + * Length of APP0 block (2 bytes) + * Block ID (4 bytes - ASCII "JFIF") + * Zero byte (1 byte to terminate the ID string) + * Version Major, Minor (2 bytes - major first) + * Units (1 byte - 0x00 = none, 0x01 = inch, 0x02 = cm) + * Xdpu (2 bytes - dots per unit horizontal) + * Ydpu (2 bytes - dots per unit vertical) + * Thumbnail X size (1 byte) + * Thumbnail Y size (1 byte) + */ + + emit_marker(cinfo, M_APP0); + + emit_2bytes(cinfo, 2 + 4 + 1 + 2 + 1 + 2 + 2 + 1 + 1); /* length */ + + emit_byte(cinfo, 0x4A); /* Identifier: ASCII "JFIF" */ + emit_byte(cinfo, 0x46); + emit_byte(cinfo, 0x49); + emit_byte(cinfo, 0x46); + emit_byte(cinfo, 0); + emit_byte(cinfo, cinfo->JFIF_major_version); /* Version fields */ + emit_byte(cinfo, cinfo->JFIF_minor_version); + emit_byte(cinfo, cinfo->density_unit); /* Pixel size information */ + emit_2bytes(cinfo, (int) cinfo->X_density); + emit_2bytes(cinfo, (int) cinfo->Y_density); + emit_byte(cinfo, 0); /* No thumbnail image */ + emit_byte(cinfo, 0); +} + + +LOCAL(void) +emit_adobe_app14 (j_compress_ptr cinfo) +/* Emit an Adobe APP14 marker */ +{ + /* + * Length of APP14 block (2 bytes) + * Block ID (5 bytes - ASCII "Adobe") + * Version Number (2 bytes - currently 100) + * Flags0 (2 bytes - currently 0) + * Flags1 (2 bytes - currently 0) + * Color transform (1 byte) + * + * Although Adobe TN 5116 mentions Version = 101, all the Adobe files + * now in circulation seem to use Version = 100, so that's what we write. + * + * We write the color transform byte as 1 if the JPEG color space is + * YCbCr, 2 if it's YCCK, 0 otherwise. Adobe's definition has to do with + * whether the encoder performed a transformation, which is pretty useless. + */ + + emit_marker(cinfo, M_APP14); + + emit_2bytes(cinfo, 2 + 5 + 2 + 2 + 2 + 1); /* length */ + + emit_byte(cinfo, 0x41); /* Identifier: ASCII "Adobe" */ + emit_byte(cinfo, 0x64); + emit_byte(cinfo, 0x6F); + emit_byte(cinfo, 0x62); + emit_byte(cinfo, 0x65); + emit_2bytes(cinfo, 100); /* Version */ + emit_2bytes(cinfo, 0); /* Flags0 */ + emit_2bytes(cinfo, 0); /* Flags1 */ + switch (cinfo->jpeg_color_space) { + case JCS_YCbCr: + emit_byte(cinfo, 1); /* Color transform = 1 */ + break; + case JCS_YCCK: + emit_byte(cinfo, 2); /* Color transform = 2 */ + break; + default: + emit_byte(cinfo, 0); /* Color transform = 0 */ + break; + } +} + + +/* + * These routines allow writing an arbitrary marker with parameters. + * The only intended use is to emit COM or APPn markers after calling + * write_file_header and before calling write_frame_header. + * Other uses are not guaranteed to produce desirable results. + * Counting the parameter bytes properly is the caller's responsibility. + */ + +METHODDEF(void) +write_marker_header (j_compress_ptr cinfo, int marker, unsigned int datalen) +/* Emit an arbitrary marker header */ +{ + if (datalen > (unsigned int) 65533) /* safety check */ + ERREXIT(cinfo, JERR_BAD_LENGTH); + + emit_marker(cinfo, (JPEG_MARKER) marker); + + emit_2bytes(cinfo, (int) (datalen + 2)); /* total length */ +} + +METHODDEF(void) +write_marker_byte (j_compress_ptr cinfo, int val) +/* Emit one byte of marker parameters following write_marker_header */ +{ + emit_byte(cinfo, val); +} + + +/* + * Write datastream header. + * This consists of an SOI and optional APPn markers. + * We recommend use of the JFIF marker, but not the Adobe marker, + * when using YCbCr or grayscale data. The JFIF marker should NOT + * be used for any other JPEG colorspace. The Adobe marker is helpful + * to distinguish RGB, CMYK, and YCCK colorspaces. + * Note that an application can write additional header markers after + * jpeg_start_compress returns. + */ + +METHODDEF(void) +write_file_header (j_compress_ptr cinfo) +{ + my_marker_ptr marker = (my_marker_ptr) cinfo->marker; + + emit_marker(cinfo, M_SOI); /* first the SOI */ + + /* SOI is defined to reset restart interval to 0 */ + marker->last_restart_interval = 0; + + if (cinfo->write_JFIF_header) /* next an optional JFIF APP0 */ + emit_jfif_app0(cinfo); + if (cinfo->write_Adobe_marker) /* next an optional Adobe APP14 */ + emit_adobe_app14(cinfo); +} + + +/* + * Write frame header. + * This consists of DQT and SOFn markers. + * Note that we do not emit the SOF until we have emitted the DQT(s). + * This avoids compatibility problems with incorrect implementations that + * try to error-check the quant table numbers as soon as they see the SOF. + */ + +METHODDEF(void) +write_frame_header (j_compress_ptr cinfo) +{ + int ci, prec; + boolean is_baseline; + jpeg_component_info *compptr; + + /* Emit DQT for each quantization table. + * Note that emit_dqt() suppresses any duplicate tables. + */ + prec = 0; + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + prec += emit_dqt(cinfo, compptr->quant_tbl_no); + } + /* now prec is nonzero iff there are any 16-bit quant tables. */ + + /* Check for a non-baseline specification. + * Note we assume that Huffman table numbers won't be changed later. + */ + if (cinfo->arith_code || cinfo->progressive_mode || + cinfo->data_precision != 8) { + is_baseline = FALSE; + } else { + is_baseline = TRUE; + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + if (compptr->dc_tbl_no > 1 || compptr->ac_tbl_no > 1) + is_baseline = FALSE; + } + if (prec && is_baseline) { + is_baseline = FALSE; + /* If it's baseline except for quantizer size, warn the user */ + TRACEMS(cinfo, 0, JTRC_16BIT_TABLES); + } + } + + /* Emit the proper SOF marker */ + if (cinfo->arith_code) { + emit_sof(cinfo, M_SOF9); /* SOF code for arithmetic coding */ + } else { + if (cinfo->progressive_mode) + emit_sof(cinfo, M_SOF2); /* SOF code for progressive Huffman */ + else if (is_baseline) + emit_sof(cinfo, M_SOF0); /* SOF code for baseline implementation */ + else + emit_sof(cinfo, M_SOF1); /* SOF code for non-baseline Huffman file */ + } +} + + +/* + * Write scan header. + * This consists of DHT or DAC markers, optional DRI, and SOS. + * Compressed data will be written following the SOS. + */ + +METHODDEF(void) +write_scan_header (j_compress_ptr cinfo) +{ + my_marker_ptr marker = (my_marker_ptr) cinfo->marker; + int i; + jpeg_component_info *compptr; + + if (cinfo->arith_code) { + /* Emit arith conditioning info. We may have some duplication + * if the file has multiple scans, but it's so small it's hardly + * worth worrying about. + */ + emit_dac(cinfo); + } else { + /* Emit Huffman tables. + * Note that emit_dht() suppresses any duplicate tables. + */ + for (i = 0; i < cinfo->comps_in_scan; i++) { + compptr = cinfo->cur_comp_info[i]; + if (cinfo->progressive_mode) { + /* Progressive mode: only DC or only AC tables are used in one scan */ + if (cinfo->Ss == 0) { + if (cinfo->Ah == 0) /* DC needs no table for refinement scan */ + emit_dht(cinfo, compptr->dc_tbl_no, FALSE); + } else { + emit_dht(cinfo, compptr->ac_tbl_no, TRUE); + } + } else { + /* Sequential mode: need both DC and AC tables */ + emit_dht(cinfo, compptr->dc_tbl_no, FALSE); + emit_dht(cinfo, compptr->ac_tbl_no, TRUE); + } + } + } + + /* Emit DRI if required --- note that DRI value could change for each scan. + * We avoid wasting space with unnecessary DRIs, however. + */ + if (cinfo->restart_interval != marker->last_restart_interval) { + emit_dri(cinfo); + marker->last_restart_interval = cinfo->restart_interval; + } + + emit_sos(cinfo); +} + + +/* + * Write datastream trailer. + */ + +METHODDEF(void) +write_file_trailer (j_compress_ptr cinfo) +{ + emit_marker(cinfo, M_EOI); +} + + +/* + * Write an abbreviated table-specification datastream. + * This consists of SOI, DQT and DHT tables, and EOI. + * Any table that is defined and not marked sent_table = TRUE will be + * emitted. Note that all tables will be marked sent_table = TRUE at exit. + */ + +METHODDEF(void) +write_tables_only (j_compress_ptr cinfo) +{ + int i; + + emit_marker(cinfo, M_SOI); + + for (i = 0; i < NUM_QUANT_TBLS; i++) { + if (cinfo->quant_tbl_ptrs[i] != NULL) + (void) emit_dqt(cinfo, i); + } + + if (! cinfo->arith_code) { + for (i = 0; i < NUM_HUFF_TBLS; i++) { + if (cinfo->dc_huff_tbl_ptrs[i] != NULL) + emit_dht(cinfo, i, FALSE); + if (cinfo->ac_huff_tbl_ptrs[i] != NULL) + emit_dht(cinfo, i, TRUE); + } + } + + emit_marker(cinfo, M_EOI); +} + + +/* + * Initialize the marker writer module. + */ + +GLOBAL(void) +jinit_marker_writer (j_compress_ptr cinfo) +{ + my_marker_ptr marker; + + /* Create the subobject */ + marker = (my_marker_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_marker_writer)); + cinfo->marker = (struct jpeg_marker_writer *) marker; + /* Initialize method pointers */ + marker->pub.write_file_header = write_file_header; + marker->pub.write_frame_header = write_frame_header; + marker->pub.write_scan_header = write_scan_header; + marker->pub.write_file_trailer = write_file_trailer; + marker->pub.write_tables_only = write_tables_only; + marker->pub.write_marker_header = write_marker_header; + marker->pub.write_marker_byte = write_marker_byte; + /* Initialize private state */ + marker->last_restart_interval = 0; +} diff --git a/Engine/lib/ljpeg/jcmaster.c b/Engine/lib/ljpeg/jcmaster.c new file mode 100644 index 000000000..aab4020b8 --- /dev/null +++ b/Engine/lib/ljpeg/jcmaster.c @@ -0,0 +1,590 @@ +/* + * jcmaster.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains master control logic for the JPEG compressor. + * These routines are concerned with parameter validation, initial setup, + * and inter-pass control (determining the number of passes and the work + * to be done in each pass). + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Private state */ + +typedef enum { + main_pass, /* input data, also do first output step */ + huff_opt_pass, /* Huffman code optimization pass */ + output_pass /* data output pass */ +} c_pass_type; + +typedef struct { + struct jpeg_comp_master pub; /* public fields */ + + c_pass_type pass_type; /* the type of the current pass */ + + int pass_number; /* # of passes completed */ + int total_passes; /* total # of passes needed */ + + int scan_number; /* current index in scan_info[] */ +} my_comp_master; + +typedef my_comp_master * my_master_ptr; + + +/* + * Support routines that do various essential calculations. + */ + +LOCAL(void) +initial_setup (j_compress_ptr cinfo) +/* Do computations that are needed before master selection phase */ +{ + int ci; + jpeg_component_info *compptr; + long samplesperrow; + JDIMENSION jd_samplesperrow; + + /* Sanity check on image dimensions */ + if (cinfo->image_height <= 0 || cinfo->image_width <= 0 + || cinfo->num_components <= 0 || cinfo->input_components <= 0) + ERREXIT(cinfo, JERR_EMPTY_IMAGE); + + /* Make sure image isn't bigger than I can handle */ + if ((long) cinfo->image_height > (long) JPEG_MAX_DIMENSION || + (long) cinfo->image_width > (long) JPEG_MAX_DIMENSION) + ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) JPEG_MAX_DIMENSION); + + /* Width of an input scanline must be representable as JDIMENSION. */ + samplesperrow = (long) cinfo->image_width * (long) cinfo->input_components; + jd_samplesperrow = (JDIMENSION) samplesperrow; + if ((long) jd_samplesperrow != samplesperrow) + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + + /* For now, precision must match compiled-in value... */ + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + + /* Check that number of components won't exceed internal array sizes */ + if (cinfo->num_components > MAX_COMPONENTS) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components, + MAX_COMPONENTS); + + /* Compute maximum sampling factors; check factor validity */ + cinfo->max_h_samp_factor = 1; + cinfo->max_v_samp_factor = 1; + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + if (compptr->h_samp_factor<=0 || compptr->h_samp_factor>MAX_SAMP_FACTOR || + compptr->v_samp_factor<=0 || compptr->v_samp_factor>MAX_SAMP_FACTOR) + ERREXIT(cinfo, JERR_BAD_SAMPLING); + cinfo->max_h_samp_factor = MAX(cinfo->max_h_samp_factor, + compptr->h_samp_factor); + cinfo->max_v_samp_factor = MAX(cinfo->max_v_samp_factor, + compptr->v_samp_factor); + } + + /* Compute dimensions of components */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Fill in the correct component_index value; don't rely on application */ + compptr->component_index = ci; + /* For compression, we never do DCT scaling. */ + compptr->DCT_scaled_size = DCTSIZE; + /* Size in DCT blocks */ + compptr->width_in_blocks = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, + (long) (cinfo->max_h_samp_factor * DCTSIZE)); + compptr->height_in_blocks = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, + (long) (cinfo->max_v_samp_factor * DCTSIZE)); + /* Size in samples */ + compptr->downsampled_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, + (long) cinfo->max_h_samp_factor); + compptr->downsampled_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, + (long) cinfo->max_v_samp_factor); + /* Mark component needed (this flag isn't actually used for compression) */ + compptr->component_needed = TRUE; + } + + /* Compute number of fully interleaved MCU rows (number of times that + * main controller will call coefficient controller). + */ + cinfo->total_iMCU_rows = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, + (long) (cinfo->max_v_samp_factor*DCTSIZE)); +} + + +#ifdef C_MULTISCAN_FILES_SUPPORTED + +LOCAL(void) +validate_script (j_compress_ptr cinfo) +/* Verify that the scan script in cinfo->scan_info[] is valid; also + * determine whether it uses progressive JPEG, and set cinfo->progressive_mode. + */ +{ + const jpeg_scan_info * scanptr; + int scanno, ncomps, ci, coefi, thisi; + int Ss, Se, Ah, Al; + boolean component_sent[MAX_COMPONENTS]; +#ifdef C_PROGRESSIVE_SUPPORTED + int * last_bitpos_ptr; + int last_bitpos[MAX_COMPONENTS][DCTSIZE2]; + /* -1 until that coefficient has been seen; then last Al for it */ +#endif + + if (cinfo->num_scans <= 0) + ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, 0); + + /* For sequential JPEG, all scans must have Ss=0, Se=DCTSIZE2-1; + * for progressive JPEG, no scan can have this. + */ + scanptr = cinfo->scan_info; + if (scanptr->Ss != 0 || scanptr->Se != DCTSIZE2-1) { +#ifdef C_PROGRESSIVE_SUPPORTED + cinfo->progressive_mode = TRUE; + last_bitpos_ptr = & last_bitpos[0][0]; + for (ci = 0; ci < cinfo->num_components; ci++) + for (coefi = 0; coefi < DCTSIZE2; coefi++) + *last_bitpos_ptr++ = -1; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + cinfo->progressive_mode = FALSE; + for (ci = 0; ci < cinfo->num_components; ci++) + component_sent[ci] = FALSE; + } + + for (scanno = 1; scanno <= cinfo->num_scans; scanptr++, scanno++) { + /* Validate component indexes */ + ncomps = scanptr->comps_in_scan; + if (ncomps <= 0 || ncomps > MAX_COMPS_IN_SCAN) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, ncomps, MAX_COMPS_IN_SCAN); + for (ci = 0; ci < ncomps; ci++) { + thisi = scanptr->component_index[ci]; + if (thisi < 0 || thisi >= cinfo->num_components) + ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, scanno); + /* Components must appear in SOF order within each scan */ + if (ci > 0 && thisi <= scanptr->component_index[ci-1]) + ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, scanno); + } + /* Validate progression parameters */ + Ss = scanptr->Ss; + Se = scanptr->Se; + Ah = scanptr->Ah; + Al = scanptr->Al; + if (cinfo->progressive_mode) { +#ifdef C_PROGRESSIVE_SUPPORTED + /* The JPEG spec simply gives the ranges 0..13 for Ah and Al, but that + * seems wrong: the upper bound ought to depend on data precision. + * Perhaps they really meant 0..N+1 for N-bit precision. + * Here we allow 0..10 for 8-bit data; Al larger than 10 results in + * out-of-range reconstructed DC values during the first DC scan, + * which might cause problems for some decoders. + */ +#if BITS_IN_JSAMPLE == 8 +#define MAX_AH_AL 10 +#else +#define MAX_AH_AL 13 +#endif + if (Ss < 0 || Ss >= DCTSIZE2 || Se < Ss || Se >= DCTSIZE2 || + Ah < 0 || Ah > MAX_AH_AL || Al < 0 || Al > MAX_AH_AL) + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + if (Ss == 0) { + if (Se != 0) /* DC and AC together not OK */ + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + } else { + if (ncomps != 1) /* AC scans must be for only one component */ + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + } + for (ci = 0; ci < ncomps; ci++) { + last_bitpos_ptr = & last_bitpos[scanptr->component_index[ci]][0]; + if (Ss != 0 && last_bitpos_ptr[0] < 0) /* AC without prior DC scan */ + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + for (coefi = Ss; coefi <= Se; coefi++) { + if (last_bitpos_ptr[coefi] < 0) { + /* first scan of this coefficient */ + if (Ah != 0) + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + } else { + /* not first scan */ + if (Ah != last_bitpos_ptr[coefi] || Al != Ah-1) + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + } + last_bitpos_ptr[coefi] = Al; + } + } +#endif + } else { + /* For sequential JPEG, all progression parameters must be these: */ + if (Ss != 0 || Se != DCTSIZE2-1 || Ah != 0 || Al != 0) + ERREXIT1(cinfo, JERR_BAD_PROG_SCRIPT, scanno); + /* Make sure components are not sent twice */ + for (ci = 0; ci < ncomps; ci++) { + thisi = scanptr->component_index[ci]; + if (component_sent[thisi]) + ERREXIT1(cinfo, JERR_BAD_SCAN_SCRIPT, scanno); + component_sent[thisi] = TRUE; + } + } + } + + /* Now verify that everything got sent. */ + if (cinfo->progressive_mode) { +#ifdef C_PROGRESSIVE_SUPPORTED + /* For progressive mode, we only check that at least some DC data + * got sent for each component; the spec does not require that all bits + * of all coefficients be transmitted. Would it be wiser to enforce + * transmission of all coefficient bits?? + */ + for (ci = 0; ci < cinfo->num_components; ci++) { + if (last_bitpos[ci][0] < 0) + ERREXIT(cinfo, JERR_MISSING_DATA); + } +#endif + } else { + for (ci = 0; ci < cinfo->num_components; ci++) { + if (! component_sent[ci]) + ERREXIT(cinfo, JERR_MISSING_DATA); + } + } +} + +#endif /* C_MULTISCAN_FILES_SUPPORTED */ + + +LOCAL(void) +select_scan_parameters (j_compress_ptr cinfo) +/* Set up the scan parameters for the current scan */ +{ + int ci; + +#ifdef C_MULTISCAN_FILES_SUPPORTED + if (cinfo->scan_info != NULL) { + /* Prepare for current scan --- the script is already validated */ + my_master_ptr master = (my_master_ptr) cinfo->master; + const jpeg_scan_info * scanptr = cinfo->scan_info + master->scan_number; + + cinfo->comps_in_scan = scanptr->comps_in_scan; + for (ci = 0; ci < scanptr->comps_in_scan; ci++) { + cinfo->cur_comp_info[ci] = + &cinfo->comp_info[scanptr->component_index[ci]]; + } + cinfo->Ss = scanptr->Ss; + cinfo->Se = scanptr->Se; + cinfo->Ah = scanptr->Ah; + cinfo->Al = scanptr->Al; + } + else +#endif + { + /* Prepare for single sequential-JPEG scan containing all components */ + if (cinfo->num_components > MAX_COMPS_IN_SCAN) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components, + MAX_COMPS_IN_SCAN); + cinfo->comps_in_scan = cinfo->num_components; + for (ci = 0; ci < cinfo->num_components; ci++) { + cinfo->cur_comp_info[ci] = &cinfo->comp_info[ci]; + } + cinfo->Ss = 0; + cinfo->Se = DCTSIZE2-1; + cinfo->Ah = 0; + cinfo->Al = 0; + } +} + + +LOCAL(void) +per_scan_setup (j_compress_ptr cinfo) +/* Do computations that are needed before processing a JPEG scan */ +/* cinfo->comps_in_scan and cinfo->cur_comp_info[] are already set */ +{ + int ci, mcublks, tmp; + jpeg_component_info *compptr; + + if (cinfo->comps_in_scan == 1) { + + /* Noninterleaved (single-component) scan */ + compptr = cinfo->cur_comp_info[0]; + + /* Overall image size in MCUs */ + cinfo->MCUs_per_row = compptr->width_in_blocks; + cinfo->MCU_rows_in_scan = compptr->height_in_blocks; + + /* For noninterleaved scan, always one block per MCU */ + compptr->MCU_width = 1; + compptr->MCU_height = 1; + compptr->MCU_blocks = 1; + compptr->MCU_sample_width = DCTSIZE; + compptr->last_col_width = 1; + /* For noninterleaved scans, it is convenient to define last_row_height + * as the number of block rows present in the last iMCU row. + */ + tmp = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + if (tmp == 0) tmp = compptr->v_samp_factor; + compptr->last_row_height = tmp; + + /* Prepare array describing MCU composition */ + cinfo->blocks_in_MCU = 1; + cinfo->MCU_membership[0] = 0; + + } else { + + /* Interleaved (multi-component) scan */ + if (cinfo->comps_in_scan <= 0 || cinfo->comps_in_scan > MAX_COMPS_IN_SCAN) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->comps_in_scan, + MAX_COMPS_IN_SCAN); + + /* Overall image size in MCUs */ + cinfo->MCUs_per_row = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, + (long) (cinfo->max_h_samp_factor*DCTSIZE)); + cinfo->MCU_rows_in_scan = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, + (long) (cinfo->max_v_samp_factor*DCTSIZE)); + + cinfo->blocks_in_MCU = 0; + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* Sampling factors give # of blocks of component in each MCU */ + compptr->MCU_width = compptr->h_samp_factor; + compptr->MCU_height = compptr->v_samp_factor; + compptr->MCU_blocks = compptr->MCU_width * compptr->MCU_height; + compptr->MCU_sample_width = compptr->MCU_width * DCTSIZE; + /* Figure number of non-dummy blocks in last MCU column & row */ + tmp = (int) (compptr->width_in_blocks % compptr->MCU_width); + if (tmp == 0) tmp = compptr->MCU_width; + compptr->last_col_width = tmp; + tmp = (int) (compptr->height_in_blocks % compptr->MCU_height); + if (tmp == 0) tmp = compptr->MCU_height; + compptr->last_row_height = tmp; + /* Prepare array describing MCU composition */ + mcublks = compptr->MCU_blocks; + if (cinfo->blocks_in_MCU + mcublks > C_MAX_BLOCKS_IN_MCU) + ERREXIT(cinfo, JERR_BAD_MCU_SIZE); + while (mcublks-- > 0) { + cinfo->MCU_membership[cinfo->blocks_in_MCU++] = ci; + } + } + + } + + /* Convert restart specified in rows to actual MCU count. */ + /* Note that count must fit in 16 bits, so we provide limiting. */ + if (cinfo->restart_in_rows > 0) { + long nominal = (long) cinfo->restart_in_rows * (long) cinfo->MCUs_per_row; + cinfo->restart_interval = (unsigned int) MIN(nominal, 65535L); + } +} + + +/* + * Per-pass setup. + * This is called at the beginning of each pass. We determine which modules + * will be active during this pass and give them appropriate start_pass calls. + * We also set is_last_pass to indicate whether any more passes will be + * required. + */ + +METHODDEF(void) +prepare_for_pass (j_compress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + + switch (master->pass_type) { + case main_pass: + /* Initial pass: will collect input data, and do either Huffman + * optimization or data output for the first scan. + */ + select_scan_parameters(cinfo); + per_scan_setup(cinfo); + if (! cinfo->raw_data_in) { + (*cinfo->cconvert->start_pass) (cinfo); + (*cinfo->downsample->start_pass) (cinfo); + (*cinfo->prep->start_pass) (cinfo, JBUF_PASS_THRU); + } + (*cinfo->fdct->start_pass) (cinfo); + (*cinfo->entropy->start_pass) (cinfo, cinfo->optimize_coding); + (*cinfo->coef->start_pass) (cinfo, + (master->total_passes > 1 ? + JBUF_SAVE_AND_PASS : JBUF_PASS_THRU)); + (*cinfo->main->start_pass) (cinfo, JBUF_PASS_THRU); + if (cinfo->optimize_coding) { + /* No immediate data output; postpone writing frame/scan headers */ + master->pub.call_pass_startup = FALSE; + } else { + /* Will write frame/scan headers at first jpeg_write_scanlines call */ + master->pub.call_pass_startup = TRUE; + } + break; +#ifdef ENTROPY_OPT_SUPPORTED + case huff_opt_pass: + /* Do Huffman optimization for a scan after the first one. */ + select_scan_parameters(cinfo); + per_scan_setup(cinfo); + if (cinfo->Ss != 0 || cinfo->Ah == 0 || cinfo->arith_code) { + (*cinfo->entropy->start_pass) (cinfo, TRUE); + (*cinfo->coef->start_pass) (cinfo, JBUF_CRANK_DEST); + master->pub.call_pass_startup = FALSE; + break; + } + /* Special case: Huffman DC refinement scans need no Huffman table + * and therefore we can skip the optimization pass for them. + */ + master->pass_type = output_pass; + master->pass_number++; + /*FALLTHROUGH*/ +#endif + case output_pass: + /* Do a data-output pass. */ + /* We need not repeat per-scan setup if prior optimization pass did it. */ + if (! cinfo->optimize_coding) { + select_scan_parameters(cinfo); + per_scan_setup(cinfo); + } + (*cinfo->entropy->start_pass) (cinfo, FALSE); + (*cinfo->coef->start_pass) (cinfo, JBUF_CRANK_DEST); + /* We emit frame/scan headers now */ + if (master->scan_number == 0) + (*cinfo->marker->write_frame_header) (cinfo); + (*cinfo->marker->write_scan_header) (cinfo); + master->pub.call_pass_startup = FALSE; + break; + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + } + + master->pub.is_last_pass = (master->pass_number == master->total_passes-1); + + /* Set up progress monitor's pass info if present */ + if (cinfo->progress != NULL) { + cinfo->progress->completed_passes = master->pass_number; + cinfo->progress->total_passes = master->total_passes; + } +} + + +/* + * Special start-of-pass hook. + * This is called by jpeg_write_scanlines if call_pass_startup is TRUE. + * In single-pass processing, we need this hook because we don't want to + * write frame/scan headers during jpeg_start_compress; we want to let the + * application write COM markers etc. between jpeg_start_compress and the + * jpeg_write_scanlines loop. + * In multi-pass processing, this routine is not used. + */ + +METHODDEF(void) +pass_startup (j_compress_ptr cinfo) +{ + cinfo->master->call_pass_startup = FALSE; /* reset flag so call only once */ + + (*cinfo->marker->write_frame_header) (cinfo); + (*cinfo->marker->write_scan_header) (cinfo); +} + + +/* + * Finish up at end of pass. + */ + +METHODDEF(void) +finish_pass_master (j_compress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + + /* The entropy coder always needs an end-of-pass call, + * either to analyze statistics or to flush its output buffer. + */ + (*cinfo->entropy->finish_pass) (cinfo); + + /* Update state for next pass */ + switch (master->pass_type) { + case main_pass: + /* next pass is either output of scan 0 (after optimization) + * or output of scan 1 (if no optimization). + */ + master->pass_type = output_pass; + if (! cinfo->optimize_coding) + master->scan_number++; + break; + case huff_opt_pass: + /* next pass is always output of current scan */ + master->pass_type = output_pass; + break; + case output_pass: + /* next pass is either optimization or output of next scan */ + if (cinfo->optimize_coding) + master->pass_type = huff_opt_pass; + master->scan_number++; + break; + } + + master->pass_number++; +} + + +/* + * Initialize master compression control. + */ + +GLOBAL(void) +jinit_c_master_control (j_compress_ptr cinfo, boolean transcode_only) +{ + my_master_ptr master; + + master = (my_master_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_comp_master)); + cinfo->master = (struct jpeg_comp_master *) master; + master->pub.prepare_for_pass = prepare_for_pass; + master->pub.pass_startup = pass_startup; + master->pub.finish_pass = finish_pass_master; + master->pub.is_last_pass = FALSE; + + /* Validate parameters, determine derived values */ + initial_setup(cinfo); + + if (cinfo->scan_info != NULL) { +#ifdef C_MULTISCAN_FILES_SUPPORTED + validate_script(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + cinfo->progressive_mode = FALSE; + cinfo->num_scans = 1; + } + + if (cinfo->progressive_mode) /* TEMPORARY HACK ??? */ + cinfo->optimize_coding = TRUE; /* assume default tables no good for progressive mode */ + + /* Initialize my private state */ + if (transcode_only) { + /* no main pass in transcoding */ + if (cinfo->optimize_coding) + master->pass_type = huff_opt_pass; + else + master->pass_type = output_pass; + } else { + /* for normal compression, first pass is always this type: */ + master->pass_type = main_pass; + } + master->scan_number = 0; + master->pass_number = 0; + if (cinfo->optimize_coding) + master->total_passes = cinfo->num_scans * 2; + else + master->total_passes = cinfo->num_scans; +} diff --git a/Engine/lib/ljpeg/jcomapi.c b/Engine/lib/ljpeg/jcomapi.c new file mode 100644 index 000000000..9b1fa7568 --- /dev/null +++ b/Engine/lib/ljpeg/jcomapi.c @@ -0,0 +1,106 @@ +/* + * jcomapi.c + * + * Copyright (C) 1994-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains application interface routines that are used for both + * compression and decompression. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * Abort processing of a JPEG compression or decompression operation, + * but don't destroy the object itself. + * + * For this, we merely clean up all the nonpermanent memory pools. + * Note that temp files (virtual arrays) are not allowed to belong to + * the permanent pool, so we will be able to close all temp files here. + * Closing a data source or destination, if necessary, is the application's + * responsibility. + */ + +GLOBAL(void) +jpeg_abort (j_common_ptr cinfo) +{ + int pool; + + /* Do nothing if called on a not-initialized or destroyed JPEG object. */ + if (cinfo->mem == NULL) + return; + + /* Releasing pools in reverse order might help avoid fragmentation + * with some (brain-damaged) malloc libraries. + */ + for (pool = JPOOL_NUMPOOLS-1; pool > JPOOL_PERMANENT; pool--) { + (*cinfo->mem->free_pool) (cinfo, pool); + } + + /* Reset overall state for possible reuse of object */ + if (cinfo->is_decompressor) { + cinfo->global_state = DSTATE_START; + /* Try to keep application from accessing now-deleted marker list. + * A bit kludgy to do it here, but this is the most central place. + */ + ((j_decompress_ptr) cinfo)->marker_list = NULL; + } else { + cinfo->global_state = CSTATE_START; + } +} + + +/* + * Destruction of a JPEG object. + * + * Everything gets deallocated except the master jpeg_compress_struct itself + * and the error manager struct. Both of these are supplied by the application + * and must be freed, if necessary, by the application. (Often they are on + * the stack and so don't need to be freed anyway.) + * Closing a data source or destination, if necessary, is the application's + * responsibility. + */ + +GLOBAL(void) +jpeg_destroy (j_common_ptr cinfo) +{ + /* We need only tell the memory manager to release everything. */ + /* NB: mem pointer is NULL if memory mgr failed to initialize. */ + if (cinfo->mem != NULL) + (*cinfo->mem->self_destruct) (cinfo); + cinfo->mem = NULL; /* be safe if jpeg_destroy is called twice */ + cinfo->global_state = 0; /* mark it destroyed */ +} + + +/* + * Convenience routines for allocating quantization and Huffman tables. + * (Would jutils.c be a more reasonable place to put these?) + */ + +GLOBAL(JQUANT_TBL *) +jpeg_alloc_quant_table (j_common_ptr cinfo) +{ + JQUANT_TBL *tbl; + + tbl = (JQUANT_TBL *) + (*cinfo->mem->alloc_small) (cinfo, JPOOL_PERMANENT, SIZEOF(JQUANT_TBL)); + tbl->sent_table = FALSE; /* make sure this is false in any new table */ + return tbl; +} + + +GLOBAL(JHUFF_TBL *) +jpeg_alloc_huff_table (j_common_ptr cinfo) +{ + JHUFF_TBL *tbl; + + tbl = (JHUFF_TBL *) + (*cinfo->mem->alloc_small) (cinfo, JPOOL_PERMANENT, SIZEOF(JHUFF_TBL)); + tbl->sent_table = FALSE; /* make sure this is false in any new table */ + return tbl; +} diff --git a/Engine/lib/ljpeg/jconfig.cw.mac.h b/Engine/lib/ljpeg/jconfig.cw.mac.h new file mode 100644 index 000000000..c906c5d02 --- /dev/null +++ b/Engine/lib/ljpeg/jconfig.cw.mac.h @@ -0,0 +1,160 @@ +/* + * jconfig.doc + * + * Copyright (C) 1991-1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file documents the configuration options that are required to + * customize the JPEG software for a particular system. + * + * The actual configuration options for a particular installation are stored + * in jconfig.h. On many machines, jconfig.h can be generated automatically + * or copied from one of the "canned" jconfig files that we supply. But if + * you need to generate a jconfig.h file by hand, this file tells you how. + * + */ + +// disable the unused arument warning +#pragma warn_unusedarg off + +#define jpeg_size_t unsigned long + +// apparently, if we use the Mac memmgr instead of ansi or something, we need to define this here. +#undef USE_MAC_MEMMGR + +/* + * These symbols indicate the properties of your machine or compiler. + * #define the symbol if yes, #undef it if no. + */ + +/* Does your compiler support function prototypes? + * (If not, you also need to use ansi2knr, see install.doc) + */ +#define HAVE_PROTOTYPES + +/* Does your compiler support the declaration "unsigned char" ? + * How about "unsigned short" ? + */ +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT + +/* Define "void" as "char" if your compiler doesn't know about type void. + * NOTE: be sure to define void such that "void *" represents the most general + * pointer type, e.g., that returned by malloc(). + */ +/* #define void char */ + +/* Define "const" as empty if your compiler doesn't know the "const" keyword. + */ +/* #define const */ + +/* Define this if an ordinary "char" type is unsigned. + * If you're not sure, leaving it undefined will work at some cost in speed. + * If you defined HAVE_UNSIGNED_CHAR then the speed difference is minimal. + */ +#undef CHAR_IS_UNSIGNED + +/* Define this if your system has an ANSI-conforming file. + */ +#define HAVE_STDDEF_H + +/* Define this if your system has an ANSI-conforming file. + */ +#define HAVE_STDLIB_H + +/* Define this if your system does not have an ANSI/SysV , + * but does have a BSD-style . + */ +#undef NEED_BSD_STRINGS + +/* Define this if your system does not provide typedef size_t in any of the + * ANSI-standard places (stddef.h, stdlib.h, or stdio.h), but places it in + * instead. + */ +#undef NEED_SYS_TYPES_H + +/* For 80x86 machines, you need to define NEED_FAR_POINTERS, + * unless you are using a large-data memory model or 80386 flat-memory mode. + * On less brain-damaged CPUs this symbol must not be defined. + * (Defining this symbol causes large data structures to be referenced through + * "far" pointers and to be allocated with a special version of malloc.) + */ +#undef NEED_FAR_POINTERS + +/* Define this if your linker needs global names to be unique in less + * than the first 15 characters. + */ +#undef NEED_SHORT_EXTERNAL_NAMES + +/* Although a real ANSI C compiler can deal perfectly well with pointers to + * unspecified structures (see "incomplete types" in the spec), a few pre-ANSI + * and pseudo-ANSI compilers get confused. To keep one of these bozos happy, + * define INCOMPLETE_TYPES_BROKEN. This is not recommended unless you + * actually get "missing structure definition" warnings or errors while + * compiling the JPEG code. + */ +#undef INCOMPLETE_TYPES_BROKEN + + +/* + * The following options affect code selection within the JPEG library, + * but they don't need to be visible to applications using the library. + * To minimize application namespace pollution, the symbols won't be + * defined unless JPEG_INTERNALS has been defined. + */ + +#ifdef JPEG_INTERNALS + +/* Define this if your compiler implements ">>" on signed values as a logical + * (unsigned) shift; leave it undefined if ">>" is a signed (arithmetic) shift, + * which is the normal and rational definition. + */ +#undef RIGHT_SHIFT_IS_UNSIGNED + + +#endif /* JPEG_INTERNALS */ + + +/* + * The remaining options do not affect the JPEG library proper, + * but only the sample applications cjpeg/djpeg (see cjpeg.c, djpeg.c). + * Other applications can ignore these. + */ + +#ifdef JPEG_CJPEG_DJPEG + +/* These defines indicate which image (non-JPEG) file formats are allowed. */ + +#undef BMP_SUPPORTED /* BMP image file format */ +#undef GIF_SUPPORTED /* GIF image file format */ +#undef PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +#undef RLE_SUPPORTED /* Utah RLE image file format */ +#undef TARGA_SUPPORTED /* Targa image file format */ + +/* Define this if you want to name both input and output files on the command + * line, rather than using stdout and optionally stdin. You MUST do this if + * your system can't cope with binary I/O to stdin/stdout. See comments at + * head of cjpeg.c or djpeg.c. + */ +#undef TWO_FILE_COMMANDLINE + +/* Define this if your system needs explicit cleanup of temporary files. + * This is crucial under MS-DOS, where the temporary "files" may be areas + * of extended memory; on most other systems it's not as important. + */ +#undef NEED_SIGNAL_CATCHER + +/* By default, we open image files with fopen(...,"rb") or fopen(...,"wb"). + * This is necessary on systems that distinguish text files from binary files, + * and is harmless on most systems that don't. If you have one of the rare + * systems that complains about the "b" spec, define this symbol. + */ +#undef DONT_USE_B_MODE + +/* Define this if you want percent-done progress reports from cjpeg/djpeg. + */ +#undef PROGRESS_REPORT + + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/Engine/lib/ljpeg/jconfig.cw.win.h b/Engine/lib/ljpeg/jconfig.cw.win.h new file mode 100644 index 000000000..472764b38 --- /dev/null +++ b/Engine/lib/ljpeg/jconfig.cw.win.h @@ -0,0 +1,157 @@ +/* + * jconfig.doc + * + * Copyright (C) 1991-1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file documents the configuration options that are required to + * customize the JPEG software for a particular system. + * + * The actual configuration options for a particular installation are stored + * in jconfig.h. On many machines, jconfig.h can be generated automatically + * or copied from one of the "canned" jconfig files that we supply. But if + * you need to generate a jconfig.h file by hand, this file tells you how. + * + */ + +// disable the unused arument warning +#pragma warn_unusedarg off + +#define jpeg_size_t unsigned long + +/* + * These symbols indicate the properties of your machine or compiler. + * #define the symbol if yes, #undef it if no. + */ + +/* Does your compiler support function prototypes? + * (If not, you also need to use ansi2knr, see install.doc) + */ +#define HAVE_PROTOTYPES + +/* Does your compiler support the declaration "unsigned char" ? + * How about "unsigned short" ? + */ +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT + +/* Define "void" as "char" if your compiler doesn't know about type void. + * NOTE: be sure to define void such that "void *" represents the most general + * pointer type, e.g., that returned by malloc(). + */ +/* #define void char */ + +/* Define "const" as empty if your compiler doesn't know the "const" keyword. + */ +/* #define const */ + +/* Define this if an ordinary "char" type is unsigned. + * If you're not sure, leaving it undefined will work at some cost in speed. + * If you defined HAVE_UNSIGNED_CHAR then the speed difference is minimal. + */ +#undef CHAR_IS_UNSIGNED + +/* Define this if your system has an ANSI-conforming file. + */ +#define HAVE_STDDEF_H + +/* Define this if your system has an ANSI-conforming file. + */ +#define HAVE_STDLIB_H + +/* Define this if your system does not have an ANSI/SysV , + * but does have a BSD-style . + */ +#undef NEED_BSD_STRINGS + +/* Define this if your system does not provide typedef size_t in any of the + * ANSI-standard places (stddef.h, stdlib.h, or stdio.h), but places it in + * instead. + */ +#undef NEED_SYS_TYPES_H + +/* For 80x86 machines, you need to define NEED_FAR_POINTERS, + * unless you are using a large-data memory model or 80386 flat-memory mode. + * On less brain-damaged CPUs this symbol must not be defined. + * (Defining this symbol causes large data structures to be referenced through + * "far" pointers and to be allocated with a special version of malloc.) + */ +#undef NEED_FAR_POINTERS + +/* Define this if your linker needs global names to be unique in less + * than the first 15 characters. + */ +#undef NEED_SHORT_EXTERNAL_NAMES + +/* Although a real ANSI C compiler can deal perfectly well with pointers to + * unspecified structures (see "incomplete types" in the spec), a few pre-ANSI + * and pseudo-ANSI compilers get confused. To keep one of these bozos happy, + * define INCOMPLETE_TYPES_BROKEN. This is not recommended unless you + * actually get "missing structure definition" warnings or errors while + * compiling the JPEG code. + */ +#undef INCOMPLETE_TYPES_BROKEN + + +/* + * The following options affect code selection within the JPEG library, + * but they don't need to be visible to applications using the library. + * To minimize application namespace pollution, the symbols won't be + * defined unless JPEG_INTERNALS has been defined. + */ + +#ifdef JPEG_INTERNALS + +/* Define this if your compiler implements ">>" on signed values as a logical + * (unsigned) shift; leave it undefined if ">>" is a signed (arithmetic) shift, + * which is the normal and rational definition. + */ +#undef RIGHT_SHIFT_IS_UNSIGNED + + +#endif /* JPEG_INTERNALS */ + + +/* + * The remaining options do not affect the JPEG library proper, + * but only the sample applications cjpeg/djpeg (see cjpeg.c, djpeg.c). + * Other applications can ignore these. + */ + +#ifdef JPEG_CJPEG_DJPEG + +/* These defines indicate which image (non-JPEG) file formats are allowed. */ + +#undef BMP_SUPPORTED /* BMP image file format */ +#undef GIF_SUPPORTED /* GIF image file format */ +#undef PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +#undef RLE_SUPPORTED /* Utah RLE image file format */ +#undef TARGA_SUPPORTED /* Targa image file format */ + +/* Define this if you want to name both input and output files on the command + * line, rather than using stdout and optionally stdin. You MUST do this if + * your system can't cope with binary I/O to stdin/stdout. See comments at + * head of cjpeg.c or djpeg.c. + */ +#undef TWO_FILE_COMMANDLINE + +/* Define this if your system needs explicit cleanup of temporary files. + * This is crucial under MS-DOS, where the temporary "files" may be areas + * of extended memory; on most other systems it's not as important. + */ +#undef NEED_SIGNAL_CATCHER + +/* By default, we open image files with fopen(...,"rb") or fopen(...,"wb"). + * This is necessary on systems that distinguish text files from binary files, + * and is harmless on most systems that don't. If you have one of the rare + * systems that complains about the "b" spec, define this symbol. + */ +#undef DONT_USE_B_MODE + +/* Define this if you want percent-done progress reports from cjpeg/djpeg. + */ +#undef PROGRESS_REPORT + + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/Engine/lib/ljpeg/jconfig.gcc.linux.h b/Engine/lib/ljpeg/jconfig.gcc.linux.h new file mode 100644 index 000000000..fe5d32bb4 --- /dev/null +++ b/Engine/lib/ljpeg/jconfig.gcc.linux.h @@ -0,0 +1,101 @@ +#ifndef __JCONFIG_GCC_LINUX_H +#define __JCONFIG_GCC_LINUX_H + +#include + +#define jpeg_size_t size_t + +/* + * These symbols indicate the properties of your machine or compiler. + * #define the symbol if yes, #undef it if no. + */ + +/* Does your compiler support function prototypes? + * (If not, you also need to use ansi2knr, see install.doc) + */ +#define HAVE_PROTOTYPES + +/* Does your compiler support the declaration "unsigned char" ? + * How about "unsigned short" ? + */ +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT + +/* Define "void" as "char" if your compiler doesn't know about type void. + * NOTE: be sure to define void such that "void *" represents the most general + * pointer type, e.g., that returned by malloc(). + */ +/* #define void char */ + +/* Define "const" as empty if your compiler doesn't know the "const" keyword. + */ +/* #define const */ + +/* Define this if an ordinary "char" type is unsigned. + * If you're not sure, leaving it undefined will work at some cost in speed. + * If you defined HAVE_UNSIGNED_CHAR then the speed difference is minimal. + */ +#undef CHAR_IS_UNSIGNED + +/* Define this if your system has an ANSI-conforming file. + */ +#define HAVE_STDDEF_H + +/* Define this if your system has an ANSI-conforming file. + */ +#define HAVE_STDLIB_H + +/* Define this if your system does not have an ANSI/SysV , + * but does have a BSD-style . + */ +#undef NEED_BSD_STRINGS + +/* Define this if your system does not provide typedef size_t in any of the + * ANSI-standard places (stddef.h, stdlib.h, or stdio.h), but places it in + * instead. + */ +#undef NEED_SYS_TYPES_H + +/* For 80x86 machines, you need to define NEED_FAR_POINTERS, + * unless you are using a large-data memory model or 80386 flat-memory mode. + * On less brain-damaged CPUs this symbol must not be defined. + * (Defining this symbol causes large data structures to be referenced through + * "far" pointers and to be allocated with a special version of malloc.) + */ +#undef NEED_FAR_POINTERS + +/* Define this if your linker needs global names to be unique in less + * than the first 15 characters. + */ +#undef NEED_SHORT_EXTERNAL_NAMES + +/* Although a real ANSI C compiler can deal perfectly well with pointers to + * unspecified structures (see "incomplete types" in the spec), a few pre-ANSI + * and pseudo-ANSI compilers get confused. To keep one of these bozos happy, + * define INCOMPLETE_TYPES_BROKEN. This is not recommended unless you + * actually get "missing structure definition" warnings or errors while + * compiling the JPEG code. + */ +#undef INCOMPLETE_TYPES_BROKEN + + +/* + * The following options affect code selection within the JPEG library, + * but they don't need to be visible to applications using the library. + * To minimize application namespace pollution, the symbols won't be + * defined unless JPEG_INTERNALS has been defined. + */ + +#ifdef JPEG_INTERNALS + +/* Define this if your compiler implements ">>" on signed values as a logical + * (unsigned) shift; leave it undefined if ">>" is a signed (arithmetic) shift, + * which is the normal and rational definition. + */ +#undef RIGHT_SHIFT_IS_UNSIGNED + + +#endif /* JPEG_INTERNALS */ + + +#endif diff --git a/Engine/lib/ljpeg/jconfig.h b/Engine/lib/ljpeg/jconfig.h new file mode 100644 index 000000000..3d7cb73d8 --- /dev/null +++ b/Engine/lib/ljpeg/jconfig.h @@ -0,0 +1,39 @@ +//-------------------------------------------------------------------------- +// +// +// +//-------------------------------------------------------------------------- + + +#ifndef _H_JCONFIG_ +#define _H_JCONFIG_ + + +#if (defined(__MWERKS__) && !defined(macintosh)) + #include "jconfig.cw.win.h" + #define JCONFIG_INCLUDED +#endif + +#if (defined(__MWERKS__) && defined(macintosh)) || defined(__APPLE__) + #include "jconfig.cw.mac.h" + #define JCONFIG_INCLUDED +#endif + +#if (defined(_MSC_VER) && !defined(__MWERKS__) && !defined(macintosh)) + #include "jconfig.vc.win.h" + #define JCONFIG_INCLUDED +#endif + +#if (( __GNUC__ >= 2 ) && (defined (__CYGWIN32__) || defined (__linux__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__WIN32__) || defined(SN_TARGET_PS3)) ) + #include "jconfig.gcc.linux.h" + #define JCONFIG_INCLUDED +#endif + + +#ifndef JCONFIG_INCLUDED +#error No jconfig.h file was included! +#endif + +#undef JCONFIG_INCLUDED + +#endif // _H_JCONFIG_ diff --git a/Engine/lib/ljpeg/jconfig.vc.win.h b/Engine/lib/ljpeg/jconfig.vc.win.h new file mode 100644 index 000000000..de686f047 --- /dev/null +++ b/Engine/lib/ljpeg/jconfig.vc.win.h @@ -0,0 +1,154 @@ +/* + * jconfig.doc + * + * Copyright (C) 1991-1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file documents the configuration options that are required to + * customize the JPEG software for a particular system. + * + * The actual configuration options for a particular installation are stored + * in jconfig.h. On many machines, jconfig.h can be generated automatically + * or copied from one of the "canned" jconfig files that we supply. But if + * you need to generate a jconfig.h file by hand, this file tells you how. + * + */ + +#define jpeg_size_t unsigned long + +/* + * These symbols indicate the properties of your machine or compiler. + * #define the symbol if yes, #undef it if no. + */ + +/* Does your compiler support function prototypes? + * (If not, you also need to use ansi2knr, see install.doc) + */ +#define HAVE_PROTOTYPES + +/* Does your compiler support the declaration "unsigned char" ? + * How about "unsigned short" ? + */ +#define HAVE_UNSIGNED_CHAR +#define HAVE_UNSIGNED_SHORT + +/* Define "void" as "char" if your compiler doesn't know about type void. + * NOTE: be sure to define void such that "void *" represents the most general + * pointer type, e.g., that returned by malloc(). + */ +/* #define void char */ + +/* Define "const" as empty if your compiler doesn't know the "const" keyword. + */ +/* #define const */ + +/* Define this if an ordinary "char" type is unsigned. + * If you're not sure, leaving it undefined will work at some cost in speed. + * If you defined HAVE_UNSIGNED_CHAR then the speed difference is minimal. + */ +#undef CHAR_IS_UNSIGNED + +/* Define this if your system has an ANSI-conforming file. + */ +#define HAVE_STDDEF_H + +/* Define this if your system has an ANSI-conforming file. + */ +#define HAVE_STDLIB_H + +/* Define this if your system does not have an ANSI/SysV , + * but does have a BSD-style . + */ +#undef NEED_BSD_STRINGS + +/* Define this if your system does not provide typedef size_t in any of the + * ANSI-standard places (stddef.h, stdlib.h, or stdio.h), but places it in + * instead. + */ +#undef NEED_SYS_TYPES_H + +/* For 80x86 machines, you need to define NEED_FAR_POINTERS, + * unless you are using a large-data memory model or 80386 flat-memory mode. + * On less brain-damaged CPUs this symbol must not be defined. + * (Defining this symbol causes large data structures to be referenced through + * "far" pointers and to be allocated with a special version of malloc.) + */ +#undef NEED_FAR_POINTERS + +/* Define this if your linker needs global names to be unique in less + * than the first 15 characters. + */ +#undef NEED_SHORT_EXTERNAL_NAMES + +/* Although a real ANSI C compiler can deal perfectly well with pointers to + * unspecified structures (see "incomplete types" in the spec), a few pre-ANSI + * and pseudo-ANSI compilers get confused. To keep one of these bozos happy, + * define INCOMPLETE_TYPES_BROKEN. This is not recommended unless you + * actually get "missing structure definition" warnings or errors while + * compiling the JPEG code. + */ +#undef INCOMPLETE_TYPES_BROKEN + + +/* + * The following options affect code selection within the JPEG library, + * but they don't need to be visible to applications using the library. + * To minimize application namespace pollution, the symbols won't be + * defined unless JPEG_INTERNALS has been defined. + */ + +#ifdef JPEG_INTERNALS + +/* Define this if your compiler implements ">>" on signed values as a logical + * (unsigned) shift; leave it undefined if ">>" is a signed (arithmetic) shift, + * which is the normal and rational definition. + */ +#undef RIGHT_SHIFT_IS_UNSIGNED + + +#endif /* JPEG_INTERNALS */ + + +/* + * The remaining options do not affect the JPEG library proper, + * but only the sample applications cjpeg/djpeg (see cjpeg.c, djpeg.c). + * Other applications can ignore these. + */ + +#ifdef JPEG_CJPEG_DJPEG + +/* These defines indicate which image (non-JPEG) file formats are allowed. */ + +#undef BMP_SUPPORTED /* BMP image file format */ +#undef GIF_SUPPORTED /* GIF image file format */ +#undef PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */ +#undef RLE_SUPPORTED /* Utah RLE image file format */ +#undef TARGA_SUPPORTED /* Targa image file format */ + +/* Define this if you want to name both input and output files on the command + * line, rather than using stdout and optionally stdin. You MUST do this if + * your system can't cope with binary I/O to stdin/stdout. See comments at + * head of cjpeg.c or djpeg.c. + */ +#undef TWO_FILE_COMMANDLINE + +/* Define this if your system needs explicit cleanup of temporary files. + * This is crucial under MS-DOS, where the temporary "files" may be areas + * of extended memory; on most other systems it's not as important. + */ +#undef NEED_SIGNAL_CATCHER + +/* By default, we open image files with fopen(...,"rb") or fopen(...,"wb"). + * This is necessary on systems that distinguish text files from binary files, + * and is harmless on most systems that don't. If you have one of the rare + * systems that complains about the "b" spec, define this symbol. + */ +#undef DONT_USE_B_MODE + +/* Define this if you want percent-done progress reports from cjpeg/djpeg. + */ +#undef PROGRESS_REPORT + + +#endif /* JPEG_CJPEG_DJPEG */ diff --git a/Engine/lib/ljpeg/jcparam.c b/Engine/lib/ljpeg/jcparam.c new file mode 100644 index 000000000..6fc48f536 --- /dev/null +++ b/Engine/lib/ljpeg/jcparam.c @@ -0,0 +1,610 @@ +/* + * jcparam.c + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains optional default-setting code for the JPEG compressor. + * Applications do not have to use this file, but those that don't use it + * must know a lot more about the innards of the JPEG code. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * Quantization table setup routines + */ + +GLOBAL(void) +jpeg_add_quant_table (j_compress_ptr cinfo, int which_tbl, + const unsigned int *basic_table, + int scale_factor, boolean force_baseline) +/* Define a quantization table equal to the basic_table times + * a scale factor (given as a percentage). + * If force_baseline is TRUE, the computed quantization table entries + * are limited to 1..255 for JPEG baseline compatibility. + */ +{ + JQUANT_TBL ** qtblptr; + int i; + long temp; + + /* Safety check to ensure start_compress not called yet. */ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + if (which_tbl < 0 || which_tbl >= NUM_QUANT_TBLS) + ERREXIT1(cinfo, JERR_DQT_INDEX, which_tbl); + + qtblptr = & cinfo->quant_tbl_ptrs[which_tbl]; + + if (*qtblptr == NULL) + *qtblptr = jpeg_alloc_quant_table((j_common_ptr) cinfo); + + for (i = 0; i < DCTSIZE2; i++) { + temp = ((long) basic_table[i] * scale_factor + 50L) / 100L; + /* limit the values to the valid range */ + if (temp <= 0L) temp = 1L; + if (temp > 32767L) temp = 32767L; /* max quantizer needed for 12 bits */ + if (force_baseline && temp > 255L) + temp = 255L; /* limit to baseline range if requested */ + (*qtblptr)->quantval[i] = (UINT16) temp; + } + + /* Initialize sent_table FALSE so table will be written to JPEG file. */ + (*qtblptr)->sent_table = FALSE; +} + + +GLOBAL(void) +jpeg_set_linear_quality (j_compress_ptr cinfo, int scale_factor, + boolean force_baseline) +/* Set or change the 'quality' (quantization) setting, using default tables + * and a straight percentage-scaling quality scale. In most cases it's better + * to use jpeg_set_quality (below); this entry point is provided for + * applications that insist on a linear percentage scaling. + */ +{ + /* These are the sample quantization tables given in JPEG spec section K.1. + * The spec says that the values given produce "good" quality, and + * when divided by 2, "very good" quality. + */ + static const unsigned int std_luminance_quant_tbl[DCTSIZE2] = { + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, + 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, + 72, 92, 95, 98, 112, 100, 103, 99 + }; + static const unsigned int std_chrominance_quant_tbl[DCTSIZE2] = { + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 + }; + + /* Set up two quantization tables using the specified scaling */ + jpeg_add_quant_table(cinfo, 0, std_luminance_quant_tbl, + scale_factor, force_baseline); + jpeg_add_quant_table(cinfo, 1, std_chrominance_quant_tbl, + scale_factor, force_baseline); +} + + +GLOBAL(int) +jpeg_quality_scaling (int quality) +/* Convert a user-specified quality rating to a percentage scaling factor + * for an underlying quantization table, using our recommended scaling curve. + * The input 'quality' factor should be 0 (terrible) to 100 (very good). + */ +{ + /* Safety limit on quality factor. Convert 0 to 1 to avoid zero divide. */ + if (quality <= 0) quality = 1; + if (quality > 100) quality = 100; + + /* The basic table is used as-is (scaling 100) for a quality of 50. + * Qualities 50..100 are converted to scaling percentage 200 - 2*Q; + * note that at Q=100 the scaling is 0, which will cause jpeg_add_quant_table + * to make all the table entries 1 (hence, minimum quantization loss). + * Qualities 1..50 are converted to scaling percentage 5000/Q. + */ + if (quality < 50) + quality = 5000 / quality; + else + quality = 200 - quality*2; + + return quality; +} + + +GLOBAL(void) +jpeg_set_quality (j_compress_ptr cinfo, int quality, boolean force_baseline) +/* Set or change the 'quality' (quantization) setting, using default tables. + * This is the standard quality-adjusting entry point for typical user + * interfaces; only those who want detailed control over quantization tables + * would use the preceding three routines directly. + */ +{ + /* Convert user 0-100 rating to percentage scaling */ + quality = jpeg_quality_scaling(quality); + + /* Set up standard quality tables */ + jpeg_set_linear_quality(cinfo, quality, force_baseline); +} + + +/* + * Huffman table setup routines + */ + +LOCAL(void) +add_huff_table (j_compress_ptr cinfo, + JHUFF_TBL **htblptr, const UINT8 *bits, const UINT8 *val) +/* Define a Huffman table */ +{ + int nsymbols, len; + + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + + /* Copy the number-of-symbols-of-each-code-length counts */ + MEMCOPY((*htblptr)->bits, bits, SIZEOF((*htblptr)->bits)); + + /* Validate the counts. We do this here mainly so we can copy the right + * number of symbols from the val[] array, without risking marching off + * the end of memory. jchuff.c will do a more thorough test later. + */ + nsymbols = 0; + for (len = 1; len <= 16; len++) + nsymbols += bits[len]; + if (nsymbols < 1 || nsymbols > 256) + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + + MEMCOPY((*htblptr)->huffval, val, nsymbols * SIZEOF(UINT8)); + + /* Initialize sent_table FALSE so table will be written to JPEG file. */ + (*htblptr)->sent_table = FALSE; +} + + +LOCAL(void) +std_huff_tables (j_compress_ptr cinfo) +/* Set up the standard Huffman tables (cf. JPEG standard section K.3) */ +/* IMPORTANT: these are only valid for 8-bit data precision! */ +{ + static const UINT8 bits_dc_luminance[17] = + { /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }; + static const UINT8 val_dc_luminance[] = + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; + + static const UINT8 bits_dc_chrominance[17] = + { /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }; + static const UINT8 val_dc_chrominance[] = + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }; + + static const UINT8 bits_ac_luminance[17] = + { /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d }; + static const UINT8 val_ac_luminance[] = + { 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, + 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa }; + + static const UINT8 bits_ac_chrominance[17] = + { /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 }; + static const UINT8 val_ac_chrominance[] = + { 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, + 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, + 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, + 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, + 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, + 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, + 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, + 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa }; + + add_huff_table(cinfo, &cinfo->dc_huff_tbl_ptrs[0], + bits_dc_luminance, val_dc_luminance); + add_huff_table(cinfo, &cinfo->ac_huff_tbl_ptrs[0], + bits_ac_luminance, val_ac_luminance); + add_huff_table(cinfo, &cinfo->dc_huff_tbl_ptrs[1], + bits_dc_chrominance, val_dc_chrominance); + add_huff_table(cinfo, &cinfo->ac_huff_tbl_ptrs[1], + bits_ac_chrominance, val_ac_chrominance); +} + + +/* + * Default parameter setup for compression. + * + * Applications that don't choose to use this routine must do their + * own setup of all these parameters. Alternately, you can call this + * to establish defaults and then alter parameters selectively. This + * is the recommended approach since, if we add any new parameters, + * your code will still work (they'll be set to reasonable defaults). + */ + +GLOBAL(void) +jpeg_set_defaults (j_compress_ptr cinfo) +{ + int i; + + /* Safety check to ensure start_compress not called yet. */ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + /* Allocate comp_info array large enough for maximum component count. + * Array is made permanent in case application wants to compress + * multiple images at same param settings. + */ + if (cinfo->comp_info == NULL) + cinfo->comp_info = (jpeg_component_info *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + MAX_COMPONENTS * SIZEOF(jpeg_component_info)); + + /* Initialize everything not dependent on the color space */ + + cinfo->data_precision = BITS_IN_JSAMPLE; + /* Set up two quantization tables using default quality of 75 */ + jpeg_set_quality(cinfo, 75, TRUE); + /* Set up two Huffman tables */ + std_huff_tables(cinfo); + + /* Initialize default arithmetic coding conditioning */ + for (i = 0; i < NUM_ARITH_TBLS; i++) { + cinfo->arith_dc_L[i] = 0; + cinfo->arith_dc_U[i] = 1; + cinfo->arith_ac_K[i] = 5; + } + + /* Default is no multiple-scan output */ + cinfo->scan_info = NULL; + cinfo->num_scans = 0; + + /* Expect normal source image, not raw downsampled data */ + cinfo->raw_data_in = FALSE; + + /* Use Huffman coding, not arithmetic coding, by default */ + cinfo->arith_code = FALSE; + + /* By default, don't do extra passes to optimize entropy coding */ + cinfo->optimize_coding = FALSE; + /* The standard Huffman tables are only valid for 8-bit data precision. + * If the precision is higher, force optimization on so that usable + * tables will be computed. This test can be removed if default tables + * are supplied that are valid for the desired precision. + */ + if (cinfo->data_precision > 8) + cinfo->optimize_coding = TRUE; + + /* By default, use the simpler non-cosited sampling alignment */ + cinfo->CCIR601_sampling = FALSE; + + /* No input smoothing */ + cinfo->smoothing_factor = 0; + + /* DCT algorithm preference */ + cinfo->dct_method = JDCT_DEFAULT; + + /* No restart markers */ + cinfo->restart_interval = 0; + cinfo->restart_in_rows = 0; + + /* Fill in default JFIF marker parameters. Note that whether the marker + * will actually be written is determined by jpeg_set_colorspace. + * + * By default, the library emits JFIF version code 1.01. + * An application that wants to emit JFIF 1.02 extension markers should set + * JFIF_minor_version to 2. We could probably get away with just defaulting + * to 1.02, but there may still be some decoders in use that will complain + * about that; saying 1.01 should minimize compatibility problems. + */ + cinfo->JFIF_major_version = 1; /* Default JFIF version = 1.01 */ + cinfo->JFIF_minor_version = 1; + cinfo->density_unit = 0; /* Pixel size is unknown by default */ + cinfo->X_density = 1; /* Pixel aspect ratio is square by default */ + cinfo->Y_density = 1; + + /* Choose JPEG colorspace based on input space, set defaults accordingly */ + + jpeg_default_colorspace(cinfo); +} + + +/* + * Select an appropriate JPEG colorspace for in_color_space. + */ + +GLOBAL(void) +jpeg_default_colorspace (j_compress_ptr cinfo) +{ + switch (cinfo->in_color_space) { + case JCS_GRAYSCALE: + jpeg_set_colorspace(cinfo, JCS_GRAYSCALE); + break; + case JCS_RGB: + jpeg_set_colorspace(cinfo, JCS_YCbCr); + break; + case JCS_YCbCr: + jpeg_set_colorspace(cinfo, JCS_YCbCr); + break; + case JCS_CMYK: + jpeg_set_colorspace(cinfo, JCS_CMYK); /* By default, no translation */ + break; + case JCS_YCCK: + jpeg_set_colorspace(cinfo, JCS_YCCK); + break; + case JCS_UNKNOWN: + jpeg_set_colorspace(cinfo, JCS_UNKNOWN); + break; + default: + ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE); + } +} + + +/* + * Set the JPEG colorspace, and choose colorspace-dependent default values. + */ + +GLOBAL(void) +jpeg_set_colorspace (j_compress_ptr cinfo, J_COLOR_SPACE colorspace) +{ + jpeg_component_info * compptr; + int ci; + +#define SET_COMP(index,id,hsamp,vsamp,quant,dctbl,actbl) \ + (compptr = &cinfo->comp_info[index], \ + compptr->component_id = (id), \ + compptr->h_samp_factor = (hsamp), \ + compptr->v_samp_factor = (vsamp), \ + compptr->quant_tbl_no = (quant), \ + compptr->dc_tbl_no = (dctbl), \ + compptr->ac_tbl_no = (actbl) ) + + /* Safety check to ensure start_compress not called yet. */ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + /* For all colorspaces, we use Q and Huff tables 0 for luminance components, + * tables 1 for chrominance components. + */ + + cinfo->jpeg_color_space = colorspace; + + cinfo->write_JFIF_header = FALSE; /* No marker for non-JFIF colorspaces */ + cinfo->write_Adobe_marker = FALSE; /* write no Adobe marker by default */ + + switch (colorspace) { + case JCS_GRAYSCALE: + cinfo->write_JFIF_header = TRUE; /* Write a JFIF marker */ + cinfo->num_components = 1; + /* JFIF specifies component ID 1 */ + SET_COMP(0, 1, 1,1, 0, 0,0); + break; + case JCS_RGB: + cinfo->write_Adobe_marker = TRUE; /* write Adobe marker to flag RGB */ + cinfo->num_components = 3; + SET_COMP(0, 0x52 /* 'R' */, 1,1, 0, 0,0); + SET_COMP(1, 0x47 /* 'G' */, 1,1, 0, 0,0); + SET_COMP(2, 0x42 /* 'B' */, 1,1, 0, 0,0); + break; + case JCS_YCbCr: + cinfo->write_JFIF_header = TRUE; /* Write a JFIF marker */ + cinfo->num_components = 3; + /* JFIF specifies component IDs 1,2,3 */ + /* We default to 2x2 subsamples of chrominance */ + SET_COMP(0, 1, 2,2, 0, 0,0); + SET_COMP(1, 2, 1,1, 1, 1,1); + SET_COMP(2, 3, 1,1, 1, 1,1); + break; + case JCS_CMYK: + cinfo->write_Adobe_marker = TRUE; /* write Adobe marker to flag CMYK */ + cinfo->num_components = 4; + SET_COMP(0, 0x43 /* 'C' */, 1,1, 0, 0,0); + SET_COMP(1, 0x4D /* 'M' */, 1,1, 0, 0,0); + SET_COMP(2, 0x59 /* 'Y' */, 1,1, 0, 0,0); + SET_COMP(3, 0x4B /* 'K' */, 1,1, 0, 0,0); + break; + case JCS_YCCK: + cinfo->write_Adobe_marker = TRUE; /* write Adobe marker to flag YCCK */ + cinfo->num_components = 4; + SET_COMP(0, 1, 2,2, 0, 0,0); + SET_COMP(1, 2, 1,1, 1, 1,1); + SET_COMP(2, 3, 1,1, 1, 1,1); + SET_COMP(3, 4, 2,2, 0, 0,0); + break; + case JCS_UNKNOWN: + cinfo->num_components = cinfo->input_components; + if (cinfo->num_components < 1 || cinfo->num_components > MAX_COMPONENTS) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components, + MAX_COMPONENTS); + for (ci = 0; ci < cinfo->num_components; ci++) { + SET_COMP(ci, ci, 1,1, 0, 0,0); + } + break; + default: + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + } +} + + +#ifdef C_PROGRESSIVE_SUPPORTED + +LOCAL(jpeg_scan_info *) +fill_a_scan (jpeg_scan_info * scanptr, int ci, + int Ss, int Se, int Ah, int Al) +/* Support routine: generate one scan for specified component */ +{ + scanptr->comps_in_scan = 1; + scanptr->component_index[0] = ci; + scanptr->Ss = Ss; + scanptr->Se = Se; + scanptr->Ah = Ah; + scanptr->Al = Al; + scanptr++; + return scanptr; +} + +LOCAL(jpeg_scan_info *) +fill_scans (jpeg_scan_info * scanptr, int ncomps, + int Ss, int Se, int Ah, int Al) +/* Support routine: generate one scan for each component */ +{ + int ci; + + for (ci = 0; ci < ncomps; ci++) { + scanptr->comps_in_scan = 1; + scanptr->component_index[0] = ci; + scanptr->Ss = Ss; + scanptr->Se = Se; + scanptr->Ah = Ah; + scanptr->Al = Al; + scanptr++; + } + return scanptr; +} + +LOCAL(jpeg_scan_info *) +fill_dc_scans (jpeg_scan_info * scanptr, int ncomps, int Ah, int Al) +/* Support routine: generate interleaved DC scan if possible, else N scans */ +{ + int ci; + + if (ncomps <= MAX_COMPS_IN_SCAN) { + /* Single interleaved DC scan */ + scanptr->comps_in_scan = ncomps; + for (ci = 0; ci < ncomps; ci++) + scanptr->component_index[ci] = ci; + scanptr->Ss = scanptr->Se = 0; + scanptr->Ah = Ah; + scanptr->Al = Al; + scanptr++; + } else { + /* Noninterleaved DC scan for each component */ + scanptr = fill_scans(scanptr, ncomps, 0, 0, Ah, Al); + } + return scanptr; +} + + +/* + * Create a recommended progressive-JPEG script. + * cinfo->num_components and cinfo->jpeg_color_space must be correct. + */ + +GLOBAL(void) +jpeg_simple_progression (j_compress_ptr cinfo) +{ + int ncomps = cinfo->num_components; + int nscans; + jpeg_scan_info * scanptr; + + /* Safety check to ensure start_compress not called yet. */ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + /* Figure space needed for script. Calculation must match code below! */ + if (ncomps == 3 && cinfo->jpeg_color_space == JCS_YCbCr) { + /* Custom script for YCbCr color images. */ + nscans = 10; + } else { + /* All-purpose script for other color spaces. */ + if (ncomps > MAX_COMPS_IN_SCAN) + nscans = 6 * ncomps; /* 2 DC + 4 AC scans per component */ + else + nscans = 2 + 4 * ncomps; /* 2 DC scans; 4 AC scans per component */ + } + + /* Allocate space for script. + * We need to put it in the permanent pool in case the application performs + * multiple compressions without changing the settings. To avoid a memory + * leak if jpeg_simple_progression is called repeatedly for the same JPEG + * object, we try to re-use previously allocated space, and we allocate + * enough space to handle YCbCr even if initially asked for grayscale. + */ + if (cinfo->script_space == NULL || cinfo->script_space_size < nscans) { + cinfo->script_space_size = MAX(nscans, 10); + cinfo->script_space = (jpeg_scan_info *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + cinfo->script_space_size * SIZEOF(jpeg_scan_info)); + } + scanptr = cinfo->script_space; + cinfo->scan_info = scanptr; + cinfo->num_scans = nscans; + + if (ncomps == 3 && cinfo->jpeg_color_space == JCS_YCbCr) { + /* Custom script for YCbCr color images. */ + /* Initial DC scan */ + scanptr = fill_dc_scans(scanptr, ncomps, 0, 1); + /* Initial AC scan: get some luma data out in a hurry */ + scanptr = fill_a_scan(scanptr, 0, 1, 5, 0, 2); + /* Chroma data is too small to be worth expending many scans on */ + scanptr = fill_a_scan(scanptr, 2, 1, 63, 0, 1); + scanptr = fill_a_scan(scanptr, 1, 1, 63, 0, 1); + /* Complete spectral selection for luma AC */ + scanptr = fill_a_scan(scanptr, 0, 6, 63, 0, 2); + /* Refine next bit of luma AC */ + scanptr = fill_a_scan(scanptr, 0, 1, 63, 2, 1); + /* Finish DC successive approximation */ + scanptr = fill_dc_scans(scanptr, ncomps, 1, 0); + /* Finish AC successive approximation */ + scanptr = fill_a_scan(scanptr, 2, 1, 63, 1, 0); + scanptr = fill_a_scan(scanptr, 1, 1, 63, 1, 0); + /* Luma bottom bit comes last since it's usually largest scan */ + scanptr = fill_a_scan(scanptr, 0, 1, 63, 1, 0); + } else { + /* All-purpose script for other color spaces. */ + /* Successive approximation first pass */ + scanptr = fill_dc_scans(scanptr, ncomps, 0, 1); + scanptr = fill_scans(scanptr, ncomps, 1, 5, 0, 2); + scanptr = fill_scans(scanptr, ncomps, 6, 63, 0, 2); + /* Successive approximation second pass */ + scanptr = fill_scans(scanptr, ncomps, 1, 63, 2, 1); + /* Successive approximation final pass */ + scanptr = fill_dc_scans(scanptr, ncomps, 1, 0); + scanptr = fill_scans(scanptr, ncomps, 1, 63, 1, 0); + } +} + +#endif /* C_PROGRESSIVE_SUPPORTED */ diff --git a/Engine/lib/ljpeg/jcphuff.c b/Engine/lib/ljpeg/jcphuff.c new file mode 100644 index 000000000..07f9178b0 --- /dev/null +++ b/Engine/lib/ljpeg/jcphuff.c @@ -0,0 +1,833 @@ +/* + * jcphuff.c + * + * Copyright (C) 1995-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains Huffman entropy encoding routines for progressive JPEG. + * + * We do not support output suspension in this module, since the library + * currently does not allow multiple-scan files to be written with output + * suspension. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jchuff.h" /* Declarations shared with jchuff.c */ + +#ifdef C_PROGRESSIVE_SUPPORTED + +/* Expanded entropy encoder object for progressive Huffman encoding. */ + +typedef struct { + struct jpeg_entropy_encoder pub; /* public fields */ + + /* Mode flag: TRUE for optimization, FALSE for actual data output */ + boolean gather_statistics; + + /* Bit-level coding status. + * next_output_byte/free_in_buffer are local copies of cinfo->dest fields. + */ + JOCTET * next_output_byte; /* => next byte to write in buffer */ + size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + INT32 put_buffer; /* current bit-accumulation buffer */ + int put_bits; /* # of bits now in it */ + j_compress_ptr cinfo; /* link to cinfo (needed for dump_buffer) */ + + /* Coding status for DC components */ + int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ + + /* Coding status for AC components */ + int ac_tbl_no; /* the table number of the single component */ + unsigned int EOBRUN; /* run length of EOBs */ + unsigned int BE; /* # of buffered correction bits before MCU */ + char * bit_buffer; /* buffer for correction bits (1 per char) */ + /* packing correction bits tightly would save some space but cost time... */ + + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + int next_restart_num; /* next restart number to write (0-7) */ + + /* Pointers to derived tables (these workspaces have image lifespan). + * Since any one scan codes only DC or only AC, we only need one set + * of tables, not one for DC and one for AC. + */ + c_derived_tbl * derived_tbls[NUM_HUFF_TBLS]; + + /* Statistics tables for optimization; again, one set is enough */ + long * count_ptrs[NUM_HUFF_TBLS]; +} phuff_entropy_encoder; + +typedef phuff_entropy_encoder * phuff_entropy_ptr; + +/* MAX_CORR_BITS is the number of bits the AC refinement correction-bit + * buffer can hold. Larger sizes may slightly improve compression, but + * 1000 is already well into the realm of overkill. + * The minimum safe size is 64 bits. + */ + +#define MAX_CORR_BITS 1000 /* Max # of correction bits I can buffer */ + +/* IRIGHT_SHIFT is like RIGHT_SHIFT, but works on int rather than INT32. + * We assume that int right shift is unsigned if INT32 right shift is, + * which should be safe. + */ + +#ifdef RIGHT_SHIFT_IS_UNSIGNED +#define ISHIFT_TEMPS int ishift_temp; +#define IRIGHT_SHIFT(x,shft) \ + ((ishift_temp = (x)) < 0 ? \ + (ishift_temp >> (shft)) | ((~0) << (16-(shft))) : \ + (ishift_temp >> (shft))) +#else +#define ISHIFT_TEMPS +#define IRIGHT_SHIFT(x,shft) ((x) >> (shft)) +#endif + +/* Forward declarations */ +METHODDEF(boolean) encode_mcu_DC_first JPP((j_compress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(boolean) encode_mcu_AC_first JPP((j_compress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(boolean) encode_mcu_DC_refine JPP((j_compress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(boolean) encode_mcu_AC_refine JPP((j_compress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(void) finish_pass_phuff JPP((j_compress_ptr cinfo)); +METHODDEF(void) finish_pass_gather_phuff JPP((j_compress_ptr cinfo)); + + +/* + * Initialize for a Huffman-compressed scan using progressive JPEG. + */ + +METHODDEF(void) +start_pass_phuff (j_compress_ptr cinfo, boolean gather_statistics) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + boolean is_DC_band; + int ci, tbl; + jpeg_component_info * compptr; + + entropy->cinfo = cinfo; + entropy->gather_statistics = gather_statistics; + + is_DC_band = (cinfo->Ss == 0); + + /* We assume jcmaster.c already validated the scan parameters. */ + + /* Select execution routines */ + if (cinfo->Ah == 0) { + if (is_DC_band) + entropy->pub.encode_mcu = encode_mcu_DC_first; + else + entropy->pub.encode_mcu = encode_mcu_AC_first; + } else { + if (is_DC_band) + entropy->pub.encode_mcu = encode_mcu_DC_refine; + else { + entropy->pub.encode_mcu = encode_mcu_AC_refine; + /* AC refinement needs a correction bit buffer */ + if (entropy->bit_buffer == NULL) + entropy->bit_buffer = (char *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + MAX_CORR_BITS * SIZEOF(char)); + } + } + if (gather_statistics) + entropy->pub.finish_pass = finish_pass_gather_phuff; + else + entropy->pub.finish_pass = finish_pass_phuff; + + /* Only DC coefficients may be interleaved, so cinfo->comps_in_scan = 1 + * for AC coefficients. + */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* Initialize DC predictions to 0 */ + entropy->last_dc_val[ci] = 0; + /* Get table index */ + if (is_DC_band) { + if (cinfo->Ah != 0) /* DC refinement needs no table */ + continue; + tbl = compptr->dc_tbl_no; + } else { + entropy->ac_tbl_no = tbl = compptr->ac_tbl_no; + } + if (gather_statistics) { + /* Check for invalid table index */ + /* (make_c_derived_tbl does this in the other path) */ + if (tbl < 0 || tbl >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tbl); + /* Allocate and zero the statistics tables */ + /* Note that jpeg_gen_optimal_table expects 257 entries in each table! */ + if (entropy->count_ptrs[tbl] == NULL) + entropy->count_ptrs[tbl] = (long *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + 257 * SIZEOF(long)); + MEMZERO(entropy->count_ptrs[tbl], 257 * SIZEOF(long)); + } else { + /* Compute derived values for Huffman table */ + /* We may do this more than once for a table, but it's not expensive */ + jpeg_make_c_derived_tbl(cinfo, is_DC_band, tbl, + & entropy->derived_tbls[tbl]); + } + } + + /* Initialize AC stuff */ + entropy->EOBRUN = 0; + entropy->BE = 0; + + /* Initialize bit buffer to empty */ + entropy->put_buffer = 0; + entropy->put_bits = 0; + + /* Initialize restart stuff */ + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num = 0; +} + + +/* Outputting bytes to the file. + * NB: these must be called only when actually outputting, + * that is, entropy->gather_statistics == FALSE. + */ + +/* Emit a byte */ +#define emit_byte(entropy,val) \ + { *(entropy)->next_output_byte++ = (JOCTET) (val); \ + if (--(entropy)->free_in_buffer == 0) \ + dump_buffer(entropy); } + + +LOCAL(void) +dump_buffer (phuff_entropy_ptr entropy) +/* Empty the output buffer; we do not support suspension in this module. */ +{ + struct jpeg_destination_mgr * dest = entropy->cinfo->dest; + + if (! (*dest->empty_output_buffer) (entropy->cinfo)) + ERREXIT(entropy->cinfo, JERR_CANT_SUSPEND); + /* After a successful buffer dump, must reset buffer pointers */ + entropy->next_output_byte = dest->next_output_byte; + entropy->free_in_buffer = dest->free_in_buffer; +} + + +/* Outputting bits to the file */ + +/* Only the right 24 bits of put_buffer are used; the valid bits are + * left-justified in this part. At most 16 bits can be passed to emit_bits + * in one call, and we never retain more than 7 bits in put_buffer + * between calls, so 24 bits are sufficient. + */ + +INLINE +LOCAL(void) +emit_bits (phuff_entropy_ptr entropy, unsigned int code, int size) +/* Emit some bits, unless we are in gather mode */ +{ + /* This routine is heavily used, so it's worth coding tightly. */ + register INT32 put_buffer = (INT32) code; + register int put_bits = entropy->put_bits; + + /* if size is 0, caller used an invalid Huffman table entry */ + if (size == 0) + ERREXIT(entropy->cinfo, JERR_HUFF_MISSING_CODE); + + if (entropy->gather_statistics) + return; /* do nothing if we're only getting stats */ + + put_buffer &= (((INT32) 1)<put_buffer; /* and merge with old buffer contents */ + + while (put_bits >= 8) { + int c = (int) ((put_buffer >> 16) & 0xFF); + + emit_byte(entropy, c); + if (c == 0xFF) { /* need to stuff a zero byte? */ + emit_byte(entropy, 0); + } + put_buffer <<= 8; + put_bits -= 8; + } + + entropy->put_buffer = put_buffer; /* update variables */ + entropy->put_bits = put_bits; +} + + +LOCAL(void) +flush_bits (phuff_entropy_ptr entropy) +{ + emit_bits(entropy, 0x7F, 7); /* fill any partial byte with ones */ + entropy->put_buffer = 0; /* and reset bit-buffer to empty */ + entropy->put_bits = 0; +} + + +/* + * Emit (or just count) a Huffman symbol. + */ + +INLINE +LOCAL(void) +emit_symbol (phuff_entropy_ptr entropy, int tbl_no, int symbol) +{ + if (entropy->gather_statistics) + entropy->count_ptrs[tbl_no][symbol]++; + else { + c_derived_tbl * tbl = entropy->derived_tbls[tbl_no]; + emit_bits(entropy, tbl->ehufco[symbol], tbl->ehufsi[symbol]); + } +} + + +/* + * Emit bits from a correction bit buffer. + */ + +LOCAL(void) +emit_buffered_bits (phuff_entropy_ptr entropy, char * bufstart, + unsigned int nbits) +{ + if (entropy->gather_statistics) + return; /* no real work */ + + while (nbits > 0) { + emit_bits(entropy, (unsigned int) (*bufstart), 1); + bufstart++; + nbits--; + } +} + + +/* + * Emit any pending EOBRUN symbol. + */ + +LOCAL(void) +emit_eobrun (phuff_entropy_ptr entropy) +{ + register int temp, nbits; + + if (entropy->EOBRUN > 0) { /* if there is any pending EOBRUN */ + temp = entropy->EOBRUN; + nbits = 0; + while ((temp >>= 1)) + nbits++; + /* safety check: shouldn't happen given limited correction-bit buffer */ + if (nbits > 14) + ERREXIT(entropy->cinfo, JERR_HUFF_MISSING_CODE); + + emit_symbol(entropy, entropy->ac_tbl_no, nbits << 4); + if (nbits) + emit_bits(entropy, entropy->EOBRUN, nbits); + + entropy->EOBRUN = 0; + + /* Emit any buffered correction bits */ + emit_buffered_bits(entropy, entropy->bit_buffer, entropy->BE); + entropy->BE = 0; + } +} + + +/* + * Emit a restart marker & resynchronize predictions. + */ + +LOCAL(void) +emit_restart (phuff_entropy_ptr entropy, int restart_num) +{ + int ci; + + emit_eobrun(entropy); + + if (! entropy->gather_statistics) { + flush_bits(entropy); + emit_byte(entropy, 0xFF); + emit_byte(entropy, JPEG_RST0 + restart_num); + } + + if (entropy->cinfo->Ss == 0) { + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < entropy->cinfo->comps_in_scan; ci++) + entropy->last_dc_val[ci] = 0; + } else { + /* Re-initialize all AC-related fields to 0 */ + entropy->EOBRUN = 0; + entropy->BE = 0; + } +} + + +/* + * MCU encoding for DC initial scan (either spectral selection, + * or first pass of successive approximation). + */ + +METHODDEF(boolean) +encode_mcu_DC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + register int temp, temp2; + register int nbits; + int blkn, ci; + int Al = cinfo->Al; + JBLOCKROW block; + jpeg_component_info * compptr; + ISHIFT_TEMPS + + entropy->next_output_byte = cinfo->dest->next_output_byte; + entropy->free_in_buffer = cinfo->dest->free_in_buffer; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) + if (entropy->restarts_to_go == 0) + emit_restart(entropy, entropy->next_restart_num); + + /* Encode the MCU data blocks */ + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + block = MCU_data[blkn]; + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + + /* Compute the DC value after the required point transform by Al. + * This is simply an arithmetic right shift. + */ + temp2 = IRIGHT_SHIFT((int) ((*block)[0]), Al); + + /* DC differences are figured on the point-transformed values. */ + temp = temp2 - entropy->last_dc_val[ci]; + entropy->last_dc_val[ci] = temp2; + + /* Encode the DC coefficient difference per section G.1.2.1 */ + temp2 = temp; + if (temp < 0) { + temp = -temp; /* temp is abs value of input */ + /* For a negative input, want temp2 = bitwise complement of abs(input) */ + /* This code assumes we are on a two's complement machine */ + temp2--; + } + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 0; + while (temp) { + nbits++; + temp >>= 1; + } + /* Check for out-of-range coefficient values. + * Since we're encoding a difference, the range limit is twice as much. + */ + if (nbits > MAX_COEF_BITS+1) + ERREXIT(cinfo, JERR_BAD_DCT_COEF); + + /* Count/emit the Huffman-coded symbol for the number of bits */ + emit_symbol(entropy, compptr->dc_tbl_no, nbits); + + /* Emit that number of bits of the value, if positive, */ + /* or the complement of its magnitude, if negative. */ + if (nbits) /* emit_bits rejects calls with size 0 */ + emit_bits(entropy, (unsigned int) temp2, nbits); + } + + cinfo->dest->next_output_byte = entropy->next_output_byte; + cinfo->dest->free_in_buffer = entropy->free_in_buffer; + + /* Update restart-interval state too */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + return TRUE; +} + + +/* + * MCU encoding for AC initial scan (either spectral selection, + * or first pass of successive approximation). + */ + +METHODDEF(boolean) +encode_mcu_AC_first (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + register int temp, temp2; + register int nbits; + register int r, k; + int Se = cinfo->Se; + int Al = cinfo->Al; + JBLOCKROW block; + + entropy->next_output_byte = cinfo->dest->next_output_byte; + entropy->free_in_buffer = cinfo->dest->free_in_buffer; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) + if (entropy->restarts_to_go == 0) + emit_restart(entropy, entropy->next_restart_num); + + /* Encode the MCU data block */ + block = MCU_data[0]; + + /* Encode the AC coefficients per section G.1.2.2, fig. G.3 */ + + r = 0; /* r = run length of zeros */ + + for (k = cinfo->Ss; k <= Se; k++) { + if ((temp = (*block)[jpeg_natural_order[k]]) == 0) { + r++; + continue; + } + /* We must apply the point transform by Al. For AC coefficients this + * is an integer division with rounding towards 0. To do this portably + * in C, we shift after obtaining the absolute value; so the code is + * interwoven with finding the abs value (temp) and output bits (temp2). + */ + if (temp < 0) { + temp = -temp; /* temp is abs value of input */ + temp >>= Al; /* apply the point transform */ + /* For a negative coef, want temp2 = bitwise complement of abs(coef) */ + temp2 = ~temp; + } else { + temp >>= Al; /* apply the point transform */ + temp2 = temp; + } + /* Watch out for case that nonzero coef is zero after point transform */ + if (temp == 0) { + r++; + continue; + } + + /* Emit any pending EOBRUN */ + if (entropy->EOBRUN > 0) + emit_eobrun(entropy); + /* if run length > 15, must emit special run-length-16 codes (0xF0) */ + while (r > 15) { + emit_symbol(entropy, entropy->ac_tbl_no, 0xF0); + r -= 16; + } + + /* Find the number of bits needed for the magnitude of the coefficient */ + nbits = 1; /* there must be at least one 1 bit */ + while ((temp >>= 1)) + nbits++; + /* Check for out-of-range coefficient values */ + if (nbits > MAX_COEF_BITS) + ERREXIT(cinfo, JERR_BAD_DCT_COEF); + + /* Count/emit Huffman symbol for run length / number of bits */ + emit_symbol(entropy, entropy->ac_tbl_no, (r << 4) + nbits); + + /* Emit that number of bits of the value, if positive, */ + /* or the complement of its magnitude, if negative. */ + emit_bits(entropy, (unsigned int) temp2, nbits); + + r = 0; /* reset zero run length */ + } + + if (r > 0) { /* If there are trailing zeroes, */ + entropy->EOBRUN++; /* count an EOB */ + if (entropy->EOBRUN == 0x7FFF) + emit_eobrun(entropy); /* force it out to avoid overflow */ + } + + cinfo->dest->next_output_byte = entropy->next_output_byte; + cinfo->dest->free_in_buffer = entropy->free_in_buffer; + + /* Update restart-interval state too */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + return TRUE; +} + + +/* + * MCU encoding for DC successive approximation refinement scan. + * Note: we assume such scans can be multi-component, although the spec + * is not very clear on the point. + */ + +METHODDEF(boolean) +encode_mcu_DC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + register int temp; + int blkn; + int Al = cinfo->Al; + JBLOCKROW block; + + entropy->next_output_byte = cinfo->dest->next_output_byte; + entropy->free_in_buffer = cinfo->dest->free_in_buffer; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) + if (entropy->restarts_to_go == 0) + emit_restart(entropy, entropy->next_restart_num); + + /* Encode the MCU data blocks */ + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + block = MCU_data[blkn]; + + /* We simply emit the Al'th bit of the DC coefficient value. */ + temp = (*block)[0]; + emit_bits(entropy, (unsigned int) (temp >> Al), 1); + } + + cinfo->dest->next_output_byte = entropy->next_output_byte; + cinfo->dest->free_in_buffer = entropy->free_in_buffer; + + /* Update restart-interval state too */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + return TRUE; +} + + +/* + * MCU encoding for AC successive approximation refinement scan. + */ + +METHODDEF(boolean) +encode_mcu_AC_refine (j_compress_ptr cinfo, JBLOCKROW *MCU_data) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + register int temp; + register int r, k; + int EOB; + char *BR_buffer; + unsigned int BR; + int Se = cinfo->Se; + int Al = cinfo->Al; + JBLOCKROW block; + int absvalues[DCTSIZE2]; + + entropy->next_output_byte = cinfo->dest->next_output_byte; + entropy->free_in_buffer = cinfo->dest->free_in_buffer; + + /* Emit restart marker if needed */ + if (cinfo->restart_interval) + if (entropy->restarts_to_go == 0) + emit_restart(entropy, entropy->next_restart_num); + + /* Encode the MCU data block */ + block = MCU_data[0]; + + /* It is convenient to make a pre-pass to determine the transformed + * coefficients' absolute values and the EOB position. + */ + EOB = 0; + for (k = cinfo->Ss; k <= Se; k++) { + temp = (*block)[jpeg_natural_order[k]]; + /* We must apply the point transform by Al. For AC coefficients this + * is an integer division with rounding towards 0. To do this portably + * in C, we shift after obtaining the absolute value. + */ + if (temp < 0) + temp = -temp; /* temp is abs value of input */ + temp >>= Al; /* apply the point transform */ + absvalues[k] = temp; /* save abs value for main pass */ + if (temp == 1) + EOB = k; /* EOB = index of last newly-nonzero coef */ + } + + /* Encode the AC coefficients per section G.1.2.3, fig. G.7 */ + + r = 0; /* r = run length of zeros */ + BR = 0; /* BR = count of buffered bits added now */ + BR_buffer = entropy->bit_buffer + entropy->BE; /* Append bits to buffer */ + + for (k = cinfo->Ss; k <= Se; k++) { + if ((temp = absvalues[k]) == 0) { + r++; + continue; + } + + /* Emit any required ZRLs, but not if they can be folded into EOB */ + while (r > 15 && k <= EOB) { + /* emit any pending EOBRUN and the BE correction bits */ + emit_eobrun(entropy); + /* Emit ZRL */ + emit_symbol(entropy, entropy->ac_tbl_no, 0xF0); + r -= 16; + /* Emit buffered correction bits that must be associated with ZRL */ + emit_buffered_bits(entropy, BR_buffer, BR); + BR_buffer = entropy->bit_buffer; /* BE bits are gone now */ + BR = 0; + } + + /* If the coef was previously nonzero, it only needs a correction bit. + * NOTE: a straight translation of the spec's figure G.7 would suggest + * that we also need to test r > 15. But if r > 15, we can only get here + * if k > EOB, which implies that this coefficient is not 1. + */ + if (temp > 1) { + /* The correction bit is the next bit of the absolute value. */ + BR_buffer[BR++] = (char) (temp & 1); + continue; + } + + /* Emit any pending EOBRUN and the BE correction bits */ + emit_eobrun(entropy); + + /* Count/emit Huffman symbol for run length / number of bits */ + emit_symbol(entropy, entropy->ac_tbl_no, (r << 4) + 1); + + /* Emit output bit for newly-nonzero coef */ + temp = ((*block)[jpeg_natural_order[k]] < 0) ? 0 : 1; + emit_bits(entropy, (unsigned int) temp, 1); + + /* Emit buffered correction bits that must be associated with this code */ + emit_buffered_bits(entropy, BR_buffer, BR); + BR_buffer = entropy->bit_buffer; /* BE bits are gone now */ + BR = 0; + r = 0; /* reset zero run length */ + } + + if (r > 0 || BR > 0) { /* If there are trailing zeroes, */ + entropy->EOBRUN++; /* count an EOB */ + entropy->BE += BR; /* concat my correction bits to older ones */ + /* We force out the EOB if we risk either: + * 1. overflow of the EOB counter; + * 2. overflow of the correction bit buffer during the next MCU. + */ + if (entropy->EOBRUN == 0x7FFF || entropy->BE > (MAX_CORR_BITS-DCTSIZE2+1)) + emit_eobrun(entropy); + } + + cinfo->dest->next_output_byte = entropy->next_output_byte; + cinfo->dest->free_in_buffer = entropy->free_in_buffer; + + /* Update restart-interval state too */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) { + entropy->restarts_to_go = cinfo->restart_interval; + entropy->next_restart_num++; + entropy->next_restart_num &= 7; + } + entropy->restarts_to_go--; + } + + return TRUE; +} + + +/* + * Finish up at the end of a Huffman-compressed progressive scan. + */ + +METHODDEF(void) +finish_pass_phuff (j_compress_ptr cinfo) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + + entropy->next_output_byte = cinfo->dest->next_output_byte; + entropy->free_in_buffer = cinfo->dest->free_in_buffer; + + /* Flush out any buffered data */ + emit_eobrun(entropy); + flush_bits(entropy); + + cinfo->dest->next_output_byte = entropy->next_output_byte; + cinfo->dest->free_in_buffer = entropy->free_in_buffer; +} + + +/* + * Finish up a statistics-gathering pass and create the new Huffman tables. + */ + +METHODDEF(void) +finish_pass_gather_phuff (j_compress_ptr cinfo) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + boolean is_DC_band; + int ci, tbl; + jpeg_component_info * compptr; + JHUFF_TBL **htblptr; + boolean did[NUM_HUFF_TBLS]; + + /* Flush out buffered data (all we care about is counting the EOB symbol) */ + emit_eobrun(entropy); + + is_DC_band = (cinfo->Ss == 0); + + /* It's important not to apply jpeg_gen_optimal_table more than once + * per table, because it clobbers the input frequency counts! + */ + MEMZERO(did, SIZEOF(did)); + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + if (is_DC_band) { + if (cinfo->Ah != 0) /* DC refinement needs no table */ + continue; + tbl = compptr->dc_tbl_no; + } else { + tbl = compptr->ac_tbl_no; + } + if (! did[tbl]) { + if (is_DC_band) + htblptr = & cinfo->dc_huff_tbl_ptrs[tbl]; + else + htblptr = & cinfo->ac_huff_tbl_ptrs[tbl]; + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + jpeg_gen_optimal_table(cinfo, *htblptr, entropy->count_ptrs[tbl]); + did[tbl] = TRUE; + } + } +} + + +/* + * Module initialization routine for progressive Huffman entropy encoding. + */ + +GLOBAL(void) +jinit_phuff_encoder (j_compress_ptr cinfo) +{ + phuff_entropy_ptr entropy; + int i; + + entropy = (phuff_entropy_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(phuff_entropy_encoder)); + cinfo->entropy = (struct jpeg_entropy_encoder *) entropy; + entropy->pub.start_pass = start_pass_phuff; + + /* Mark tables unallocated */ + for (i = 0; i < NUM_HUFF_TBLS; i++) { + entropy->derived_tbls[i] = NULL; + entropy->count_ptrs[i] = NULL; + } + entropy->bit_buffer = NULL; /* needed only in AC refinement scan */ +} + +#endif /* C_PROGRESSIVE_SUPPORTED */ diff --git a/Engine/lib/ljpeg/jcprepct.c b/Engine/lib/ljpeg/jcprepct.c new file mode 100644 index 000000000..fa93333db --- /dev/null +++ b/Engine/lib/ljpeg/jcprepct.c @@ -0,0 +1,354 @@ +/* + * jcprepct.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the compression preprocessing controller. + * This controller manages the color conversion, downsampling, + * and edge expansion steps. + * + * Most of the complexity here is associated with buffering input rows + * as required by the downsampler. See the comments at the head of + * jcsample.c for the downsampler's needs. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* At present, jcsample.c can request context rows only for smoothing. + * In the future, we might also need context rows for CCIR601 sampling + * or other more-complex downsampling procedures. The code to support + * context rows should be compiled only if needed. + */ +#ifdef INPUT_SMOOTHING_SUPPORTED +#define CONTEXT_ROWS_SUPPORTED +#endif + + +/* + * For the simple (no-context-row) case, we just need to buffer one + * row group's worth of pixels for the downsampling step. At the bottom of + * the image, we pad to a full row group by replicating the last pixel row. + * The downsampler's last output row is then replicated if needed to pad + * out to a full iMCU row. + * + * When providing context rows, we must buffer three row groups' worth of + * pixels. Three row groups are physically allocated, but the row pointer + * arrays are made five row groups high, with the extra pointers above and + * below "wrapping around" to point to the last and first real row groups. + * This allows the downsampler to access the proper context rows. + * At the top and bottom of the image, we create dummy context rows by + * copying the first or last real pixel row. This copying could be avoided + * by pointer hacking as is done in jdmainct.c, but it doesn't seem worth the + * trouble on the compression side. + */ + + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_c_prep_controller pub; /* public fields */ + + /* Downsampling input buffer. This buffer holds color-converted data + * until we have enough to do a downsample step. + */ + JSAMPARRAY color_buf[MAX_COMPONENTS]; + + JDIMENSION rows_to_go; /* counts rows remaining in source image */ + int next_buf_row; /* index of next row to store in color_buf */ + +#ifdef CONTEXT_ROWS_SUPPORTED /* only needed for context case */ + int this_row_group; /* starting row index of group to process */ + int next_buf_stop; /* downsample when we reach this index */ +#endif +} my_prep_controller; + +typedef my_prep_controller * my_prep_ptr; + + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_prep (j_compress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_prep_ptr prep = (my_prep_ptr) cinfo->prep; + + if (pass_mode != JBUF_PASS_THRU) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + /* Initialize total-height counter for detecting bottom of image */ + prep->rows_to_go = cinfo->image_height; + /* Mark the conversion buffer empty */ + prep->next_buf_row = 0; +#ifdef CONTEXT_ROWS_SUPPORTED + /* Preset additional state variables for context mode. + * These aren't used in non-context mode, so we needn't test which mode. + */ + prep->this_row_group = 0; + /* Set next_buf_stop to stop after two row groups have been read in. */ + prep->next_buf_stop = 2 * cinfo->max_v_samp_factor; +#endif +} + + +/* + * Expand an image vertically from height input_rows to height output_rows, + * by duplicating the bottom row. + */ + +LOCAL(void) +expand_bottom_edge (JSAMPARRAY image_data, JDIMENSION num_cols, + int input_rows, int output_rows) +{ + register int row; + + for (row = input_rows; row < output_rows; row++) { + jcopy_sample_rows(image_data, input_rows-1, image_data, row, + 1, num_cols); + } +} + + +/* + * Process some data in the simple no-context case. + * + * Preprocessor output data is counted in "row groups". A row group + * is defined to be v_samp_factor sample rows of each component. + * Downsampling will produce this much data from each max_v_samp_factor + * input rows. + */ + +METHODDEF(void) +pre_process_data (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail, + JSAMPIMAGE output_buf, JDIMENSION *out_row_group_ctr, + JDIMENSION out_row_groups_avail) +{ + my_prep_ptr prep = (my_prep_ptr) cinfo->prep; + int numrows, ci; + JDIMENSION inrows; + jpeg_component_info * compptr; + + while (*in_row_ctr < in_rows_avail && + *out_row_group_ctr < out_row_groups_avail) { + /* Do color conversion to fill the conversion buffer. */ + inrows = in_rows_avail - *in_row_ctr; + numrows = cinfo->max_v_samp_factor - prep->next_buf_row; + numrows = (int) MIN((JDIMENSION) numrows, inrows); + (*cinfo->cconvert->color_convert) (cinfo, input_buf + *in_row_ctr, + prep->color_buf, + (JDIMENSION) prep->next_buf_row, + numrows); + *in_row_ctr += numrows; + prep->next_buf_row += numrows; + prep->rows_to_go -= numrows; + /* If at bottom of image, pad to fill the conversion buffer. */ + if (prep->rows_to_go == 0 && + prep->next_buf_row < cinfo->max_v_samp_factor) { + for (ci = 0; ci < cinfo->num_components; ci++) { + expand_bottom_edge(prep->color_buf[ci], cinfo->image_width, + prep->next_buf_row, cinfo->max_v_samp_factor); + } + prep->next_buf_row = cinfo->max_v_samp_factor; + } + /* If we've filled the conversion buffer, empty it. */ + if (prep->next_buf_row == cinfo->max_v_samp_factor) { + (*cinfo->downsample->downsample) (cinfo, + prep->color_buf, (JDIMENSION) 0, + output_buf, *out_row_group_ctr); + prep->next_buf_row = 0; + (*out_row_group_ctr)++; + } + /* If at bottom of image, pad the output to a full iMCU height. + * Note we assume the caller is providing a one-iMCU-height output buffer! + */ + if (prep->rows_to_go == 0 && + *out_row_group_ctr < out_row_groups_avail) { + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + expand_bottom_edge(output_buf[ci], + compptr->width_in_blocks * DCTSIZE, + (int) (*out_row_group_ctr * compptr->v_samp_factor), + (int) (out_row_groups_avail * compptr->v_samp_factor)); + } + *out_row_group_ctr = out_row_groups_avail; + break; /* can exit outer loop without test */ + } + } +} + + +#ifdef CONTEXT_ROWS_SUPPORTED + +/* + * Process some data in the context case. + */ + +METHODDEF(void) +pre_process_context (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail, + JSAMPIMAGE output_buf, JDIMENSION *out_row_group_ctr, + JDIMENSION out_row_groups_avail) +{ + my_prep_ptr prep = (my_prep_ptr) cinfo->prep; + int numrows, ci; + int buf_height = cinfo->max_v_samp_factor * 3; + JDIMENSION inrows; + + while (*out_row_group_ctr < out_row_groups_avail) { + if (*in_row_ctr < in_rows_avail) { + /* Do color conversion to fill the conversion buffer. */ + inrows = in_rows_avail - *in_row_ctr; + numrows = prep->next_buf_stop - prep->next_buf_row; + numrows = (int) MIN((JDIMENSION) numrows, inrows); + (*cinfo->cconvert->color_convert) (cinfo, input_buf + *in_row_ctr, + prep->color_buf, + (JDIMENSION) prep->next_buf_row, + numrows); + /* Pad at top of image, if first time through */ + if (prep->rows_to_go == cinfo->image_height) { + for (ci = 0; ci < cinfo->num_components; ci++) { + int row; + for (row = 1; row <= cinfo->max_v_samp_factor; row++) { + jcopy_sample_rows(prep->color_buf[ci], 0, + prep->color_buf[ci], -row, + 1, cinfo->image_width); + } + } + } + *in_row_ctr += numrows; + prep->next_buf_row += numrows; + prep->rows_to_go -= numrows; + } else { + /* Return for more data, unless we are at the bottom of the image. */ + if (prep->rows_to_go != 0) + break; + /* When at bottom of image, pad to fill the conversion buffer. */ + if (prep->next_buf_row < prep->next_buf_stop) { + for (ci = 0; ci < cinfo->num_components; ci++) { + expand_bottom_edge(prep->color_buf[ci], cinfo->image_width, + prep->next_buf_row, prep->next_buf_stop); + } + prep->next_buf_row = prep->next_buf_stop; + } + } + /* If we've gotten enough data, downsample a row group. */ + if (prep->next_buf_row == prep->next_buf_stop) { + (*cinfo->downsample->downsample) (cinfo, + prep->color_buf, + (JDIMENSION) prep->this_row_group, + output_buf, *out_row_group_ctr); + (*out_row_group_ctr)++; + /* Advance pointers with wraparound as necessary. */ + prep->this_row_group += cinfo->max_v_samp_factor; + if (prep->this_row_group >= buf_height) + prep->this_row_group = 0; + if (prep->next_buf_row >= buf_height) + prep->next_buf_row = 0; + prep->next_buf_stop = prep->next_buf_row + cinfo->max_v_samp_factor; + } + } +} + + +/* + * Create the wrapped-around downsampling input buffer needed for context mode. + */ + +LOCAL(void) +create_context_buffer (j_compress_ptr cinfo) +{ + my_prep_ptr prep = (my_prep_ptr) cinfo->prep; + int rgroup_height = cinfo->max_v_samp_factor; + int ci, i; + jpeg_component_info * compptr; + JSAMPARRAY true_buffer, fake_buffer; + + /* Grab enough space for fake row pointers for all the components; + * we need five row groups' worth of pointers for each component. + */ + fake_buffer = (JSAMPARRAY) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (cinfo->num_components * 5 * rgroup_height) * + SIZEOF(JSAMPROW)); + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Allocate the actual buffer space (3 row groups) for this component. + * We make the buffer wide enough to allow the downsampler to edge-expand + * horizontally within the buffer, if it so chooses. + */ + true_buffer = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) (((long) compptr->width_in_blocks * DCTSIZE * + cinfo->max_h_samp_factor) / compptr->h_samp_factor), + (JDIMENSION) (3 * rgroup_height)); + /* Copy true buffer row pointers into the middle of the fake row array */ + MEMCOPY(fake_buffer + rgroup_height, true_buffer, + 3 * rgroup_height * SIZEOF(JSAMPROW)); + /* Fill in the above and below wraparound pointers */ + for (i = 0; i < rgroup_height; i++) { + fake_buffer[i] = true_buffer[2 * rgroup_height + i]; + fake_buffer[4 * rgroup_height + i] = true_buffer[i]; + } + prep->color_buf[ci] = fake_buffer + rgroup_height; + fake_buffer += 5 * rgroup_height; /* point to space for next component */ + } +} + +#endif /* CONTEXT_ROWS_SUPPORTED */ + + +/* + * Initialize preprocessing controller. + */ + +GLOBAL(void) +jinit_c_prep_controller (j_compress_ptr cinfo, boolean need_full_buffer) +{ + my_prep_ptr prep; + int ci; + jpeg_component_info * compptr; + + if (need_full_buffer) /* safety check */ + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + prep = (my_prep_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_prep_controller)); + cinfo->prep = (struct jpeg_c_prep_controller *) prep; + prep->pub.start_pass = start_pass_prep; + + /* Allocate the color conversion buffer. + * We make the buffer wide enough to allow the downsampler to edge-expand + * horizontally within the buffer, if it so chooses. + */ + if (cinfo->downsample->need_context_rows) { + /* Set up to provide context rows */ +#ifdef CONTEXT_ROWS_SUPPORTED + prep->pub.pre_process_data = pre_process_context; + create_context_buffer(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + /* No context, just make it tall enough for one row group */ + prep->pub.pre_process_data = pre_process_data; + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + prep->color_buf[ci] = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) (((long) compptr->width_in_blocks * DCTSIZE * + cinfo->max_h_samp_factor) / compptr->h_samp_factor), + (JDIMENSION) cinfo->max_v_samp_factor); + } + } +} diff --git a/Engine/lib/ljpeg/jcsample.c b/Engine/lib/ljpeg/jcsample.c new file mode 100644 index 000000000..212ec8757 --- /dev/null +++ b/Engine/lib/ljpeg/jcsample.c @@ -0,0 +1,519 @@ +/* + * jcsample.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains downsampling routines. + * + * Downsampling input data is counted in "row groups". A row group + * is defined to be max_v_samp_factor pixel rows of each component, + * from which the downsampler produces v_samp_factor sample rows. + * A single row group is processed in each call to the downsampler module. + * + * The downsampler is responsible for edge-expansion of its output data + * to fill an integral number of DCT blocks horizontally. The source buffer + * may be modified if it is helpful for this purpose (the source buffer is + * allocated wide enough to correspond to the desired output width). + * The caller (the prep controller) is responsible for vertical padding. + * + * The downsampler may request "context rows" by setting need_context_rows + * during startup. In this case, the input arrays will contain at least + * one row group's worth of pixels above and below the passed-in data; + * the caller will create dummy rows at image top and bottom by replicating + * the first or last real pixel row. + * + * An excellent reference for image resampling is + * Digital Image Warping, George Wolberg, 1990. + * Pub. by IEEE Computer Society Press, Los Alamitos, CA. ISBN 0-8186-8944-7. + * + * The downsampling algorithm used here is a simple average of the source + * pixels covered by the output pixel. The hi-falutin sampling literature + * refers to this as a "box filter". In general the characteristics of a box + * filter are not very good, but for the specific cases we normally use (1:1 + * and 2:1 ratios) the box is equivalent to a "triangle filter" which is not + * nearly so bad. If you intend to use other sampling ratios, you'd be well + * advised to improve this code. + * + * A simple input-smoothing capability is provided. This is mainly intended + * for cleaning up color-dithered GIF input files (if you find it inadequate, + * we suggest using an external filtering program such as pnmconvol). When + * enabled, each input pixel P is replaced by a weighted sum of itself and its + * eight neighbors. P's weight is 1-8*SF and each neighbor's weight is SF, + * where SF = (smoothing_factor / 1024). + * Currently, smoothing is only supported for 2h2v sampling factors. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Pointer to routine to downsample a single component */ +typedef JMETHOD(void, downsample1_ptr, + (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data)); + +/* Private subobject */ + +typedef struct { + struct jpeg_downsampler pub; /* public fields */ + + /* Downsampling method pointers, one per component */ + downsample1_ptr methods[MAX_COMPONENTS]; +} my_downsampler; + +typedef my_downsampler * my_downsample_ptr; + + +/* + * Initialize for a downsampling pass. + */ + +METHODDEF(void) +start_pass_downsample (j_compress_ptr cinfo) +{ + /* no work for now */ +} + + +/* + * Expand a component horizontally from width input_cols to width output_cols, + * by duplicating the rightmost samples. + */ + +LOCAL(void) +expand_right_edge (JSAMPARRAY image_data, int num_rows, + JDIMENSION input_cols, JDIMENSION output_cols) +{ + register JSAMPROW ptr; + register JSAMPLE pixval; + register int count; + int row; + int numcols = (int) (output_cols - input_cols); + + if (numcols > 0) { + for (row = 0; row < num_rows; row++) { + ptr = image_data[row] + input_cols; + pixval = ptr[-1]; /* don't need GETJSAMPLE() here */ + for (count = numcols; count > 0; count--) + *ptr++ = pixval; + } + } +} + + +/* + * Do downsampling for a whole row group (all components). + * + * In this version we simply downsample each component independently. + */ + +METHODDEF(void) +sep_downsample (j_compress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION in_row_index, + JSAMPIMAGE output_buf, JDIMENSION out_row_group_index) +{ + my_downsample_ptr downsample = (my_downsample_ptr) cinfo->downsample; + int ci; + jpeg_component_info * compptr; + JSAMPARRAY in_ptr, out_ptr; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + in_ptr = input_buf[ci] + in_row_index; + out_ptr = output_buf[ci] + (out_row_group_index * compptr->v_samp_factor); + (*downsample->methods[ci]) (cinfo, compptr, in_ptr, out_ptr); + } +} + + +/* + * Downsample pixel values of a single component. + * One row group is processed per call. + * This version handles arbitrary integral sampling ratios, without smoothing. + * Note that this version is not actually used for customary sampling ratios. + */ + +METHODDEF(void) +int_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data) +{ + int inrow, outrow, h_expand, v_expand, numpix, numpix2, h, v; + JDIMENSION outcol, outcol_h; /* outcol_h == outcol*h_expand */ + JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; + JSAMPROW inptr, outptr; + INT32 outvalue; + + h_expand = cinfo->max_h_samp_factor / compptr->h_samp_factor; + v_expand = cinfo->max_v_samp_factor / compptr->v_samp_factor; + numpix = h_expand * v_expand; + numpix2 = numpix/2; + + /* Expand input data enough to let all the output samples be generated + * by the standard loop. Special-casing padded output would be more + * efficient. + */ + expand_right_edge(input_data, cinfo->max_v_samp_factor, + cinfo->image_width, output_cols * h_expand); + + inrow = 0; + for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) { + outptr = output_data[outrow]; + for (outcol = 0, outcol_h = 0; outcol < output_cols; + outcol++, outcol_h += h_expand) { + outvalue = 0; + for (v = 0; v < v_expand; v++) { + inptr = input_data[inrow+v] + outcol_h; + for (h = 0; h < h_expand; h++) { + outvalue += (INT32) GETJSAMPLE(*inptr++); + } + } + *outptr++ = (JSAMPLE) ((outvalue + numpix2) / numpix); + } + inrow += v_expand; + } +} + + +/* + * Downsample pixel values of a single component. + * This version handles the special case of a full-size component, + * without smoothing. + */ + +METHODDEF(void) +fullsize_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data) +{ + /* Copy the data */ + jcopy_sample_rows(input_data, 0, output_data, 0, + cinfo->max_v_samp_factor, cinfo->image_width); + /* Edge-expand */ + expand_right_edge(output_data, cinfo->max_v_samp_factor, + cinfo->image_width, compptr->width_in_blocks * DCTSIZE); +} + + +/* + * Downsample pixel values of a single component. + * This version handles the common case of 2:1 horizontal and 1:1 vertical, + * without smoothing. + * + * A note about the "bias" calculations: when rounding fractional values to + * integer, we do not want to always round 0.5 up to the next integer. + * If we did that, we'd introduce a noticeable bias towards larger values. + * Instead, this code is arranged so that 0.5 will be rounded up or down at + * alternate pixel locations (a simple ordered dither pattern). + */ + +METHODDEF(void) +h2v1_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data) +{ + int outrow; + JDIMENSION outcol; + JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; + register JSAMPROW inptr, outptr; + register int bias; + + /* Expand input data enough to let all the output samples be generated + * by the standard loop. Special-casing padded output would be more + * efficient. + */ + expand_right_edge(input_data, cinfo->max_v_samp_factor, + cinfo->image_width, output_cols * 2); + + for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) { + outptr = output_data[outrow]; + inptr = input_data[outrow]; + bias = 0; /* bias = 0,1,0,1,... for successive samples */ + for (outcol = 0; outcol < output_cols; outcol++) { + *outptr++ = (JSAMPLE) ((GETJSAMPLE(*inptr) + GETJSAMPLE(inptr[1]) + + bias) >> 1); + bias ^= 1; /* 0=>1, 1=>0 */ + inptr += 2; + } + } +} + + +/* + * Downsample pixel values of a single component. + * This version handles the standard case of 2:1 horizontal and 2:1 vertical, + * without smoothing. + */ + +METHODDEF(void) +h2v2_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data) +{ + int inrow, outrow; + JDIMENSION outcol; + JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; + register JSAMPROW inptr0, inptr1, outptr; + register int bias; + + /* Expand input data enough to let all the output samples be generated + * by the standard loop. Special-casing padded output would be more + * efficient. + */ + expand_right_edge(input_data, cinfo->max_v_samp_factor, + cinfo->image_width, output_cols * 2); + + inrow = 0; + for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) { + outptr = output_data[outrow]; + inptr0 = input_data[inrow]; + inptr1 = input_data[inrow+1]; + bias = 1; /* bias = 1,2,1,2,... for successive samples */ + for (outcol = 0; outcol < output_cols; outcol++) { + *outptr++ = (JSAMPLE) ((GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) + + GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1]) + + bias) >> 2); + bias ^= 3; /* 1=>2, 2=>1 */ + inptr0 += 2; inptr1 += 2; + } + inrow += 2; + } +} + + +#ifdef INPUT_SMOOTHING_SUPPORTED + +/* + * Downsample pixel values of a single component. + * This version handles the standard case of 2:1 horizontal and 2:1 vertical, + * with smoothing. One row of context is required. + */ + +METHODDEF(void) +h2v2_smooth_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data) +{ + int inrow, outrow; + JDIMENSION colctr; + JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; + register JSAMPROW inptr0, inptr1, above_ptr, below_ptr, outptr; + INT32 membersum, neighsum, memberscale, neighscale; + + /* Expand input data enough to let all the output samples be generated + * by the standard loop. Special-casing padded output would be more + * efficient. + */ + expand_right_edge(input_data - 1, cinfo->max_v_samp_factor + 2, + cinfo->image_width, output_cols * 2); + + /* We don't bother to form the individual "smoothed" input pixel values; + * we can directly compute the output which is the average of the four + * smoothed values. Each of the four member pixels contributes a fraction + * (1-8*SF) to its own smoothed image and a fraction SF to each of the three + * other smoothed pixels, therefore a total fraction (1-5*SF)/4 to the final + * output. The four corner-adjacent neighbor pixels contribute a fraction + * SF to just one smoothed pixel, or SF/4 to the final output; while the + * eight edge-adjacent neighbors contribute SF to each of two smoothed + * pixels, or SF/2 overall. In order to use integer arithmetic, these + * factors are scaled by 2^16 = 65536. + * Also recall that SF = smoothing_factor / 1024. + */ + + memberscale = 16384 - cinfo->smoothing_factor * 80; /* scaled (1-5*SF)/4 */ + neighscale = cinfo->smoothing_factor * 16; /* scaled SF/4 */ + + inrow = 0; + for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) { + outptr = output_data[outrow]; + inptr0 = input_data[inrow]; + inptr1 = input_data[inrow+1]; + above_ptr = input_data[inrow-1]; + below_ptr = input_data[inrow+2]; + + /* Special case for first column: pretend column -1 is same as column 0 */ + membersum = GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) + + GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1]); + neighsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[1]) + + GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[1]) + + GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[2]) + + GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[2]); + neighsum += neighsum; + neighsum += GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[2]) + + GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[2]); + membersum = membersum * memberscale + neighsum * neighscale; + *outptr++ = (JSAMPLE) ((membersum + 32768) >> 16); + inptr0 += 2; inptr1 += 2; above_ptr += 2; below_ptr += 2; + + for (colctr = output_cols - 2; colctr > 0; colctr--) { + /* sum of pixels directly mapped to this output element */ + membersum = GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) + + GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1]); + /* sum of edge-neighbor pixels */ + neighsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[1]) + + GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[1]) + + GETJSAMPLE(inptr0[-1]) + GETJSAMPLE(inptr0[2]) + + GETJSAMPLE(inptr1[-1]) + GETJSAMPLE(inptr1[2]); + /* The edge-neighbors count twice as much as corner-neighbors */ + neighsum += neighsum; + /* Add in the corner-neighbors */ + neighsum += GETJSAMPLE(above_ptr[-1]) + GETJSAMPLE(above_ptr[2]) + + GETJSAMPLE(below_ptr[-1]) + GETJSAMPLE(below_ptr[2]); + /* form final output scaled up by 2^16 */ + membersum = membersum * memberscale + neighsum * neighscale; + /* round, descale and output it */ + *outptr++ = (JSAMPLE) ((membersum + 32768) >> 16); + inptr0 += 2; inptr1 += 2; above_ptr += 2; below_ptr += 2; + } + + /* Special case for last column */ + membersum = GETJSAMPLE(*inptr0) + GETJSAMPLE(inptr0[1]) + + GETJSAMPLE(*inptr1) + GETJSAMPLE(inptr1[1]); + neighsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(above_ptr[1]) + + GETJSAMPLE(*below_ptr) + GETJSAMPLE(below_ptr[1]) + + GETJSAMPLE(inptr0[-1]) + GETJSAMPLE(inptr0[1]) + + GETJSAMPLE(inptr1[-1]) + GETJSAMPLE(inptr1[1]); + neighsum += neighsum; + neighsum += GETJSAMPLE(above_ptr[-1]) + GETJSAMPLE(above_ptr[1]) + + GETJSAMPLE(below_ptr[-1]) + GETJSAMPLE(below_ptr[1]); + membersum = membersum * memberscale + neighsum * neighscale; + *outptr = (JSAMPLE) ((membersum + 32768) >> 16); + + inrow += 2; + } +} + + +/* + * Downsample pixel values of a single component. + * This version handles the special case of a full-size component, + * with smoothing. One row of context is required. + */ + +METHODDEF(void) +fullsize_smooth_downsample (j_compress_ptr cinfo, jpeg_component_info *compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data) +{ + int outrow; + JDIMENSION colctr; + JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE; + register JSAMPROW inptr, above_ptr, below_ptr, outptr; + INT32 membersum, neighsum, memberscale, neighscale; + int colsum, lastcolsum, nextcolsum; + + /* Expand input data enough to let all the output samples be generated + * by the standard loop. Special-casing padded output would be more + * efficient. + */ + expand_right_edge(input_data - 1, cinfo->max_v_samp_factor + 2, + cinfo->image_width, output_cols); + + /* Each of the eight neighbor pixels contributes a fraction SF to the + * smoothed pixel, while the main pixel contributes (1-8*SF). In order + * to use integer arithmetic, these factors are multiplied by 2^16 = 65536. + * Also recall that SF = smoothing_factor / 1024. + */ + + memberscale = 65536L - cinfo->smoothing_factor * 512L; /* scaled 1-8*SF */ + neighscale = cinfo->smoothing_factor * 64; /* scaled SF */ + + for (outrow = 0; outrow < compptr->v_samp_factor; outrow++) { + outptr = output_data[outrow]; + inptr = input_data[outrow]; + above_ptr = input_data[outrow-1]; + below_ptr = input_data[outrow+1]; + + /* Special case for first column */ + colsum = GETJSAMPLE(*above_ptr++) + GETJSAMPLE(*below_ptr++) + + GETJSAMPLE(*inptr); + membersum = GETJSAMPLE(*inptr++); + nextcolsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(*below_ptr) + + GETJSAMPLE(*inptr); + neighsum = colsum + (colsum - membersum) + nextcolsum; + membersum = membersum * memberscale + neighsum * neighscale; + *outptr++ = (JSAMPLE) ((membersum + 32768) >> 16); + lastcolsum = colsum; colsum = nextcolsum; + + for (colctr = output_cols - 2; colctr > 0; colctr--) { + membersum = GETJSAMPLE(*inptr++); + above_ptr++; below_ptr++; + nextcolsum = GETJSAMPLE(*above_ptr) + GETJSAMPLE(*below_ptr) + + GETJSAMPLE(*inptr); + neighsum = lastcolsum + (colsum - membersum) + nextcolsum; + membersum = membersum * memberscale + neighsum * neighscale; + *outptr++ = (JSAMPLE) ((membersum + 32768) >> 16); + lastcolsum = colsum; colsum = nextcolsum; + } + + /* Special case for last column */ + membersum = GETJSAMPLE(*inptr); + neighsum = lastcolsum + (colsum - membersum) + colsum; + membersum = membersum * memberscale + neighsum * neighscale; + *outptr = (JSAMPLE) ((membersum + 32768) >> 16); + + } +} + +#endif /* INPUT_SMOOTHING_SUPPORTED */ + + +/* + * Module initialization routine for downsampling. + * Note that we must select a routine for each component. + */ + +GLOBAL(void) +jinit_downsampler (j_compress_ptr cinfo) +{ + my_downsample_ptr downsample; + int ci; + jpeg_component_info * compptr; + boolean smoothok = TRUE; + + downsample = (my_downsample_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_downsampler)); + cinfo->downsample = (struct jpeg_downsampler *) downsample; + downsample->pub.start_pass = start_pass_downsample; + downsample->pub.downsample = sep_downsample; + downsample->pub.need_context_rows = FALSE; + + if (cinfo->CCIR601_sampling) + ERREXIT(cinfo, JERR_CCIR601_NOTIMPL); + + /* Verify we can handle the sampling factors, and set up method pointers */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + if (compptr->h_samp_factor == cinfo->max_h_samp_factor && + compptr->v_samp_factor == cinfo->max_v_samp_factor) { +#ifdef INPUT_SMOOTHING_SUPPORTED + if (cinfo->smoothing_factor) { + downsample->methods[ci] = fullsize_smooth_downsample; + downsample->pub.need_context_rows = TRUE; + } else +#endif + downsample->methods[ci] = fullsize_downsample; + } else if (compptr->h_samp_factor * 2 == cinfo->max_h_samp_factor && + compptr->v_samp_factor == cinfo->max_v_samp_factor) { + smoothok = FALSE; + downsample->methods[ci] = h2v1_downsample; + } else if (compptr->h_samp_factor * 2 == cinfo->max_h_samp_factor && + compptr->v_samp_factor * 2 == cinfo->max_v_samp_factor) { +#ifdef INPUT_SMOOTHING_SUPPORTED + if (cinfo->smoothing_factor) { + downsample->methods[ci] = h2v2_smooth_downsample; + downsample->pub.need_context_rows = TRUE; + } else +#endif + downsample->methods[ci] = h2v2_downsample; + } else if ((cinfo->max_h_samp_factor % compptr->h_samp_factor) == 0 && + (cinfo->max_v_samp_factor % compptr->v_samp_factor) == 0) { + smoothok = FALSE; + downsample->methods[ci] = int_downsample; + } else + ERREXIT(cinfo, JERR_FRACT_SAMPLE_NOTIMPL); + } + +#ifdef INPUT_SMOOTHING_SUPPORTED + if (cinfo->smoothing_factor && !smoothok) + TRACEMS(cinfo, 0, JTRC_SMOOTH_NOTIMPL); +#endif +} diff --git a/Engine/lib/ljpeg/jctrans.c b/Engine/lib/ljpeg/jctrans.c new file mode 100644 index 000000000..0e6d70769 --- /dev/null +++ b/Engine/lib/ljpeg/jctrans.c @@ -0,0 +1,388 @@ +/* + * jctrans.c + * + * Copyright (C) 1995-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains library routines for transcoding compression, + * that is, writing raw DCT coefficient arrays to an output JPEG file. + * The routines in jcapimin.c will also be needed by a transcoder. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Forward declarations */ +LOCAL(void) transencode_master_selection + JPP((j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays)); +LOCAL(void) transencode_coef_controller + JPP((j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays)); + + +/* + * Compression initialization for writing raw-coefficient data. + * Before calling this, all parameters and a data destination must be set up. + * Call jpeg_finish_compress() to actually write the data. + * + * The number of passed virtual arrays must match cinfo->num_components. + * Note that the virtual arrays need not be filled or even realized at + * the time write_coefficients is called; indeed, if the virtual arrays + * were requested from this compression object's memory manager, they + * typically will be realized during this routine and filled afterwards. + */ + +GLOBAL(void) +jpeg_write_coefficients (j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays) +{ + if (cinfo->global_state != CSTATE_START) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + /* Mark all tables to be written */ + jpeg_suppress_tables(cinfo, FALSE); + /* (Re)initialize error mgr and destination modules */ + (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo); + (*cinfo->dest->init_destination) (cinfo); + /* Perform master selection of active modules */ + transencode_master_selection(cinfo, coef_arrays); + /* Wait for jpeg_finish_compress() call */ + cinfo->next_scanline = 0; /* so jpeg_write_marker works */ + cinfo->global_state = CSTATE_WRCOEFS; +} + + +/* + * Initialize the compression object with default parameters, + * then copy from the source object all parameters needed for lossless + * transcoding. Parameters that can be varied without loss (such as + * scan script and Huffman optimization) are left in their default states. + */ + +GLOBAL(void) +jpeg_copy_critical_parameters (j_decompress_ptr srcinfo, + j_compress_ptr dstinfo) +{ + JQUANT_TBL ** qtblptr; + jpeg_component_info *incomp, *outcomp; + JQUANT_TBL *c_quant, *slot_quant; + int tblno, ci, coefi; + + /* Safety check to ensure start_compress not called yet. */ + if (dstinfo->global_state != CSTATE_START) + ERREXIT1(dstinfo, JERR_BAD_STATE, dstinfo->global_state); + /* Copy fundamental image dimensions */ + dstinfo->image_width = srcinfo->image_width; + dstinfo->image_height = srcinfo->image_height; + dstinfo->input_components = srcinfo->num_components; + dstinfo->in_color_space = srcinfo->jpeg_color_space; + /* Initialize all parameters to default values */ + jpeg_set_defaults(dstinfo); + /* jpeg_set_defaults may choose wrong colorspace, eg YCbCr if input is RGB. + * Fix it to get the right header markers for the image colorspace. + */ + jpeg_set_colorspace(dstinfo, srcinfo->jpeg_color_space); + dstinfo->data_precision = srcinfo->data_precision; + dstinfo->CCIR601_sampling = srcinfo->CCIR601_sampling; + /* Copy the source's quantization tables. */ + for (tblno = 0; tblno < NUM_QUANT_TBLS; tblno++) { + if (srcinfo->quant_tbl_ptrs[tblno] != NULL) { + qtblptr = & dstinfo->quant_tbl_ptrs[tblno]; + if (*qtblptr == NULL) + *qtblptr = jpeg_alloc_quant_table((j_common_ptr) dstinfo); + MEMCOPY((*qtblptr)->quantval, + srcinfo->quant_tbl_ptrs[tblno]->quantval, + SIZEOF((*qtblptr)->quantval)); + (*qtblptr)->sent_table = FALSE; + } + } + /* Copy the source's per-component info. + * Note we assume jpeg_set_defaults has allocated the dest comp_info array. + */ + dstinfo->num_components = srcinfo->num_components; + if (dstinfo->num_components < 1 || dstinfo->num_components > MAX_COMPONENTS) + ERREXIT2(dstinfo, JERR_COMPONENT_COUNT, dstinfo->num_components, + MAX_COMPONENTS); + for (ci = 0, incomp = srcinfo->comp_info, outcomp = dstinfo->comp_info; + ci < dstinfo->num_components; ci++, incomp++, outcomp++) { + outcomp->component_id = incomp->component_id; + outcomp->h_samp_factor = incomp->h_samp_factor; + outcomp->v_samp_factor = incomp->v_samp_factor; + outcomp->quant_tbl_no = incomp->quant_tbl_no; + /* Make sure saved quantization table for component matches the qtable + * slot. If not, the input file re-used this qtable slot. + * IJG encoder currently cannot duplicate this. + */ + tblno = outcomp->quant_tbl_no; + if (tblno < 0 || tblno >= NUM_QUANT_TBLS || + srcinfo->quant_tbl_ptrs[tblno] == NULL) + ERREXIT1(dstinfo, JERR_NO_QUANT_TABLE, tblno); + slot_quant = srcinfo->quant_tbl_ptrs[tblno]; + c_quant = incomp->quant_table; + if (c_quant != NULL) { + for (coefi = 0; coefi < DCTSIZE2; coefi++) { + if (c_quant->quantval[coefi] != slot_quant->quantval[coefi]) + ERREXIT1(dstinfo, JERR_MISMATCHED_QUANT_TABLE, tblno); + } + } + /* Note: we do not copy the source's Huffman table assignments; + * instead we rely on jpeg_set_colorspace to have made a suitable choice. + */ + } + /* Also copy JFIF version and resolution information, if available. + * Strictly speaking this isn't "critical" info, but it's nearly + * always appropriate to copy it if available. In particular, + * if the application chooses to copy JFIF 1.02 extension markers from + * the source file, we need to copy the version to make sure we don't + * emit a file that has 1.02 extensions but a claimed version of 1.01. + * We will *not*, however, copy version info from mislabeled "2.01" files. + */ + if (srcinfo->saw_JFIF_marker) { + if (srcinfo->JFIF_major_version == 1) { + dstinfo->JFIF_major_version = srcinfo->JFIF_major_version; + dstinfo->JFIF_minor_version = srcinfo->JFIF_minor_version; + } + dstinfo->density_unit = srcinfo->density_unit; + dstinfo->X_density = srcinfo->X_density; + dstinfo->Y_density = srcinfo->Y_density; + } +} + + +/* + * Master selection of compression modules for transcoding. + * This substitutes for jcinit.c's initialization of the full compressor. + */ + +LOCAL(void) +transencode_master_selection (j_compress_ptr cinfo, + jvirt_barray_ptr * coef_arrays) +{ + /* Although we don't actually use input_components for transcoding, + * jcmaster.c's initial_setup will complain if input_components is 0. + */ + cinfo->input_components = 1; + /* Initialize master control (includes parameter checking/processing) */ + jinit_c_master_control(cinfo, TRUE /* transcode only */); + + /* Entropy encoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + if (cinfo->progressive_mode) { +#ifdef C_PROGRESSIVE_SUPPORTED + jinit_phuff_encoder(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else + jinit_huff_encoder(cinfo); + } + + /* We need a special coefficient buffer controller. */ + transencode_coef_controller(cinfo, coef_arrays); + + jinit_marker_writer(cinfo); + + /* We can now tell the memory manager to allocate virtual arrays. */ + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); + + /* Write the datastream header (SOI, JFIF) immediately. + * Frame and scan headers are postponed till later. + * This lets application insert special markers after the SOI. + */ + (*cinfo->marker->write_file_header) (cinfo); +} + + +/* + * The rest of this file is a special implementation of the coefficient + * buffer controller. This is similar to jccoefct.c, but it handles only + * output from presupplied virtual arrays. Furthermore, we generate any + * dummy padding blocks on-the-fly rather than expecting them to be present + * in the arrays. + */ + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_c_coef_controller pub; /* public fields */ + + JDIMENSION iMCU_row_num; /* iMCU row # within image */ + JDIMENSION mcu_ctr; /* counts MCUs processed in current row */ + int MCU_vert_offset; /* counts MCU rows within iMCU row */ + int MCU_rows_per_iMCU_row; /* number of such rows needed */ + + /* Virtual block array for each component. */ + jvirt_barray_ptr * whole_image; + + /* Workspace for constructing dummy blocks at right/bottom edges. */ + JBLOCKROW dummy_buffer[C_MAX_BLOCKS_IN_MCU]; +} my_coef_controller; + +typedef my_coef_controller * my_coef_ptr; + + +LOCAL(void) +start_iMCU_row (j_compress_ptr cinfo) +/* Reset within-iMCU-row counters for a new row */ +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + /* In an interleaved scan, an MCU row is the same as an iMCU row. + * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. + * But at the bottom of the image, process only what's left. + */ + if (cinfo->comps_in_scan > 1) { + coef->MCU_rows_per_iMCU_row = 1; + } else { + if (coef->iMCU_row_num < (cinfo->total_iMCU_rows-1)) + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor; + else + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height; + } + + coef->mcu_ctr = 0; + coef->MCU_vert_offset = 0; +} + + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_coef (j_compress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + if (pass_mode != JBUF_CRANK_DEST) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + coef->iMCU_row_num = 0; + start_iMCU_row(cinfo); +} + + +/* + * Process some data. + * We process the equivalent of one fully interleaved MCU row ("iMCU" row) + * per call, ie, v_samp_factor block rows for each component in the scan. + * The data is obtained from the virtual arrays and fed to the entropy coder. + * Returns TRUE if the iMCU row is completed, FALSE if suspended. + * + * NB: input_buf is ignored; it is likely to be a NULL pointer. + */ + +METHODDEF(boolean) +compress_output (j_compress_ptr cinfo, JSAMPIMAGE input_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + int blkn, ci, xindex, yindex, yoffset, blockcnt; + JDIMENSION start_col; + JBLOCKARRAY buffer[MAX_COMPS_IN_SCAN]; + JBLOCKROW MCU_buffer[C_MAX_BLOCKS_IN_MCU]; + JBLOCKROW buffer_ptr; + jpeg_component_info *compptr; + + /* Align the virtual buffers for the components used in this scan. */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + buffer[ci] = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[compptr->component_index], + coef->iMCU_row_num * compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, FALSE); + } + + /* Loop to process one whole iMCU row */ + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + yoffset++) { + for (MCU_col_num = coef->mcu_ctr; MCU_col_num < cinfo->MCUs_per_row; + MCU_col_num++) { + /* Construct list of pointers to DCT blocks belonging to this MCU */ + blkn = 0; /* index of current DCT block within MCU */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + start_col = MCU_col_num * compptr->MCU_width; + blockcnt = (MCU_col_num < last_MCU_col) ? compptr->MCU_width + : compptr->last_col_width; + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + if (coef->iMCU_row_num < last_iMCU_row || + yindex+yoffset < compptr->last_row_height) { + /* Fill in pointers to real blocks in this row */ + buffer_ptr = buffer[ci][yindex+yoffset] + start_col; + for (xindex = 0; xindex < blockcnt; xindex++) + MCU_buffer[blkn++] = buffer_ptr++; + } else { + /* At bottom of image, need a whole row of dummy blocks */ + xindex = 0; + } + /* Fill in any dummy blocks needed in this row. + * Dummy blocks are filled in the same way as in jccoefct.c: + * all zeroes in the AC entries, DC entries equal to previous + * block's DC value. The init routine has already zeroed the + * AC entries, so we need only set the DC entries correctly. + */ + for (; xindex < compptr->MCU_width; xindex++) { + MCU_buffer[blkn] = coef->dummy_buffer[blkn]; + MCU_buffer[blkn][0][0] = MCU_buffer[blkn-1][0][0]; + blkn++; + } + } + } + /* Try to write the MCU. */ + if (! (*cinfo->entropy->encode_mcu) (cinfo, MCU_buffer)) { + /* Suspension forced; update state counters and exit */ + coef->MCU_vert_offset = yoffset; + coef->mcu_ctr = MCU_col_num; + return FALSE; + } + } + /* Completed an MCU row, but perhaps not an iMCU row */ + coef->mcu_ctr = 0; + } + /* Completed the iMCU row, advance counters for next one */ + coef->iMCU_row_num++; + start_iMCU_row(cinfo); + return TRUE; +} + + +/* + * Initialize coefficient buffer controller. + * + * Each passed coefficient array must be the right size for that + * coefficient: width_in_blocks wide and height_in_blocks high, + * with unitheight at least v_samp_factor. + */ + +LOCAL(void) +transencode_coef_controller (j_compress_ptr cinfo, + jvirt_barray_ptr * coef_arrays) +{ + my_coef_ptr coef; + JBLOCKROW buffer; + int i; + + coef = (my_coef_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_coef_controller)); + cinfo->coef = (struct jpeg_c_coef_controller *) coef; + coef->pub.start_pass = start_pass_coef; + coef->pub.compress_data = compress_output; + + /* Save pointer to virtual arrays */ + coef->whole_image = coef_arrays; + + /* Allocate and pre-zero space for dummy DCT blocks. */ + buffer = (JBLOCKROW) + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + jzero_far((void FAR *) buffer, C_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + for (i = 0; i < C_MAX_BLOCKS_IN_MCU; i++) { + coef->dummy_buffer[i] = buffer + i; + } +} diff --git a/Engine/lib/ljpeg/jdapimin.c b/Engine/lib/ljpeg/jdapimin.c new file mode 100644 index 000000000..a06b97c47 --- /dev/null +++ b/Engine/lib/ljpeg/jdapimin.c @@ -0,0 +1,395 @@ +/* + * jdapimin.c + * + * Copyright (C) 1994-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains application interface code for the decompression half + * of the JPEG library. These are the "minimum" API routines that may be + * needed in either the normal full-decompression case or the + * transcoding-only case. + * + * Most of the routines intended to be called directly by an application + * are in this file or in jdapistd.c. But also see jcomapi.c for routines + * shared by compression and decompression, and jdtrans.c for the transcoding + * case. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * Initialization of a JPEG decompression object. + * The error manager must already be set up (in case memory manager fails). + */ + +GLOBAL(void) +jpeg_CreateDecompress (j_decompress_ptr cinfo, int version, jpeg_size_t structsize) +{ + int i; + + /* Guard against version mismatches between library and caller. */ + cinfo->mem = NULL; /* so jpeg_destroy knows mem mgr not called */ + if (version != JPEG_LIB_VERSION) + ERREXIT2(cinfo, JERR_BAD_LIB_VERSION, JPEG_LIB_VERSION, version); + if (structsize != SIZEOF(struct jpeg_decompress_struct)) + ERREXIT2(cinfo, JERR_BAD_STRUCT_SIZE, + (int) SIZEOF(struct jpeg_decompress_struct), (int) structsize); + + /* For debugging purposes, we zero the whole master structure. + * But the application has already set the err pointer, and may have set + * client_data, so we have to save and restore those fields. + * Note: if application hasn't set client_data, tools like Purify may + * complain here. + */ + { + struct jpeg_error_mgr * err = cinfo->err; + void * client_data = cinfo->client_data; /* ignore Purify complaint here */ + MEMZERO(cinfo, SIZEOF(struct jpeg_decompress_struct)); + cinfo->err = err; + cinfo->client_data = client_data; + } + cinfo->is_decompressor = TRUE; + + /* Initialize a memory manager instance for this object */ + jinit_memory_mgr((j_common_ptr) cinfo); + + /* Zero out pointers to permanent structures. */ + cinfo->progress = NULL; + cinfo->src = NULL; + + for (i = 0; i < NUM_QUANT_TBLS; i++) + cinfo->quant_tbl_ptrs[i] = NULL; + + for (i = 0; i < NUM_HUFF_TBLS; i++) { + cinfo->dc_huff_tbl_ptrs[i] = NULL; + cinfo->ac_huff_tbl_ptrs[i] = NULL; + } + + /* Initialize marker processor so application can override methods + * for COM, APPn markers before calling jpeg_read_header. + */ + cinfo->marker_list = NULL; + jinit_marker_reader(cinfo); + + /* And initialize the overall input controller. */ + jinit_input_controller(cinfo); + + /* OK, I'm ready */ + cinfo->global_state = DSTATE_START; +} + + +/* + * Destruction of a JPEG decompression object + */ + +GLOBAL(void) +jpeg_destroy_decompress (j_decompress_ptr cinfo) +{ + jpeg_destroy((j_common_ptr) cinfo); /* use common routine */ +} + + +/* + * Abort processing of a JPEG decompression operation, + * but don't destroy the object itself. + */ + +GLOBAL(void) +jpeg_abort_decompress (j_decompress_ptr cinfo) +{ + jpeg_abort((j_common_ptr) cinfo); /* use common routine */ +} + + +/* + * Set default decompression parameters. + */ + +LOCAL(void) +default_decompress_parms (j_decompress_ptr cinfo) +{ + /* Guess the input colorspace, and set output colorspace accordingly. */ + /* (Wish JPEG committee had provided a real way to specify this...) */ + /* Note application may override our guesses. */ + switch (cinfo->num_components) { + case 1: + cinfo->jpeg_color_space = JCS_GRAYSCALE; + cinfo->out_color_space = JCS_GRAYSCALE; + break; + + case 3: + if (cinfo->saw_JFIF_marker) { + cinfo->jpeg_color_space = JCS_YCbCr; /* JFIF implies YCbCr */ + } else if (cinfo->saw_Adobe_marker) { + switch (cinfo->Adobe_transform) { + case 0: + cinfo->jpeg_color_space = JCS_RGB; + break; + case 1: + cinfo->jpeg_color_space = JCS_YCbCr; + break; + default: + WARNMS1(cinfo, JWRN_ADOBE_XFORM, cinfo->Adobe_transform); + cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */ + break; + } + } else { + /* Saw no special markers, try to guess from the component IDs */ + int cid0 = cinfo->comp_info[0].component_id; + int cid1 = cinfo->comp_info[1].component_id; + int cid2 = cinfo->comp_info[2].component_id; + + if (cid0 == 1 && cid1 == 2 && cid2 == 3) + cinfo->jpeg_color_space = JCS_YCbCr; /* assume JFIF w/out marker */ + else if (cid0 == 82 && cid1 == 71 && cid2 == 66) + cinfo->jpeg_color_space = JCS_RGB; /* ASCII 'R', 'G', 'B' */ + else { + TRACEMS3(cinfo, 1, JTRC_UNKNOWN_IDS, cid0, cid1, cid2); + cinfo->jpeg_color_space = JCS_YCbCr; /* assume it's YCbCr */ + } + } + /* Always guess RGB is proper output colorspace. */ + cinfo->out_color_space = JCS_RGB; + break; + + case 4: + if (cinfo->saw_Adobe_marker) { + switch (cinfo->Adobe_transform) { + case 0: + cinfo->jpeg_color_space = JCS_CMYK; + break; + case 2: + cinfo->jpeg_color_space = JCS_YCCK; + break; + default: + WARNMS1(cinfo, JWRN_ADOBE_XFORM, cinfo->Adobe_transform); + cinfo->jpeg_color_space = JCS_YCCK; /* assume it's YCCK */ + break; + } + } else { + /* No special markers, assume straight CMYK. */ + cinfo->jpeg_color_space = JCS_CMYK; + } + cinfo->out_color_space = JCS_CMYK; + break; + + default: + cinfo->jpeg_color_space = JCS_UNKNOWN; + cinfo->out_color_space = JCS_UNKNOWN; + break; + } + + /* Set defaults for other decompression parameters. */ + cinfo->scale_num = 1; /* 1:1 scaling */ + cinfo->scale_denom = 1; + cinfo->output_gamma = 1.0; + cinfo->buffered_image = FALSE; + cinfo->raw_data_out = FALSE; + cinfo->dct_method = JDCT_DEFAULT; + cinfo->do_fancy_upsampling = TRUE; + cinfo->do_block_smoothing = TRUE; + cinfo->quantize_colors = FALSE; + /* We set these in case application only sets quantize_colors. */ + cinfo->dither_mode = JDITHER_FS; +#ifdef QUANT_2PASS_SUPPORTED + cinfo->two_pass_quantize = TRUE; +#else + cinfo->two_pass_quantize = FALSE; +#endif + cinfo->desired_number_of_colors = 256; + cinfo->colormap = NULL; + /* Initialize for no mode change in buffered-image mode. */ + cinfo->enable_1pass_quant = FALSE; + cinfo->enable_external_quant = FALSE; + cinfo->enable_2pass_quant = FALSE; +} + + +/* + * Decompression startup: read start of JPEG datastream to see what's there. + * Need only initialize JPEG object and supply a data source before calling. + * + * This routine will read as far as the first SOS marker (ie, actual start of + * compressed data), and will save all tables and parameters in the JPEG + * object. It will also initialize the decompression parameters to default + * values, and finally return JPEG_HEADER_OK. On return, the application may + * adjust the decompression parameters and then call jpeg_start_decompress. + * (Or, if the application only wanted to determine the image parameters, + * the data need not be decompressed. In that case, call jpeg_abort or + * jpeg_destroy to release any temporary space.) + * If an abbreviated (tables only) datastream is presented, the routine will + * return JPEG_HEADER_TABLES_ONLY upon reaching EOI. The application may then + * re-use the JPEG object to read the abbreviated image datastream(s). + * It is unnecessary (but OK) to call jpeg_abort in this case. + * The JPEG_SUSPENDED return code only occurs if the data source module + * requests suspension of the decompressor. In this case the application + * should load more source data and then re-call jpeg_read_header to resume + * processing. + * If a non-suspending data source is used and require_image is TRUE, then the + * return code need not be inspected since only JPEG_HEADER_OK is possible. + * + * This routine is now just a front end to jpeg_consume_input, with some + * extra error checking. + */ + +GLOBAL(int) +jpeg_read_header (j_decompress_ptr cinfo, boolean require_image) +{ + int retcode; + + if (cinfo->global_state != DSTATE_START && + cinfo->global_state != DSTATE_INHEADER) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + retcode = jpeg_consume_input(cinfo); + + switch (retcode) { + case JPEG_REACHED_SOS: + retcode = JPEG_HEADER_OK; + break; + case JPEG_REACHED_EOI: + if (require_image) /* Complain if application wanted an image */ + ERREXIT(cinfo, JERR_NO_IMAGE); + /* Reset to start state; it would be safer to require the application to + * call jpeg_abort, but we can't change it now for compatibility reasons. + * A side effect is to free any temporary memory (there shouldn't be any). + */ + jpeg_abort((j_common_ptr) cinfo); /* sets state = DSTATE_START */ + retcode = JPEG_HEADER_TABLES_ONLY; + break; + case JPEG_SUSPENDED: + /* no work */ + break; + } + + return retcode; +} + + +/* + * Consume data in advance of what the decompressor requires. + * This can be called at any time once the decompressor object has + * been created and a data source has been set up. + * + * This routine is essentially a state machine that handles a couple + * of critical state-transition actions, namely initial setup and + * transition from header scanning to ready-for-start_decompress. + * All the actual input is done via the input controller's consume_input + * method. + */ + +GLOBAL(int) +jpeg_consume_input (j_decompress_ptr cinfo) +{ + int retcode = JPEG_SUSPENDED; + + /* NB: every possible DSTATE value should be listed in this switch */ + switch (cinfo->global_state) { + case DSTATE_START: + /* Start-of-datastream actions: reset appropriate modules */ + (*cinfo->inputctl->reset_input_controller) (cinfo); + /* Initialize application's data source module */ + (*cinfo->src->init_source) (cinfo); + cinfo->global_state = DSTATE_INHEADER; + /*FALLTHROUGH*/ + case DSTATE_INHEADER: + retcode = (*cinfo->inputctl->consume_input) (cinfo); + if (retcode == JPEG_REACHED_SOS) { /* Found SOS, prepare to decompress */ + /* Set up default parameters based on header data */ + default_decompress_parms(cinfo); + /* Set global state: ready for start_decompress */ + cinfo->global_state = DSTATE_READY; + } + break; + case DSTATE_READY: + /* Can't advance past first SOS until start_decompress is called */ + retcode = JPEG_REACHED_SOS; + break; + case DSTATE_PRELOAD: + case DSTATE_PRESCAN: + case DSTATE_SCANNING: + case DSTATE_RAW_OK: + case DSTATE_BUFIMAGE: + case DSTATE_BUFPOST: + case DSTATE_STOPPING: + retcode = (*cinfo->inputctl->consume_input) (cinfo); + break; + default: + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + } + return retcode; +} + + +/* + * Have we finished reading the input file? + */ + +GLOBAL(boolean) +jpeg_input_complete (j_decompress_ptr cinfo) +{ + /* Check for valid jpeg object */ + if (cinfo->global_state < DSTATE_START || + cinfo->global_state > DSTATE_STOPPING) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + return cinfo->inputctl->eoi_reached; +} + + +/* + * Is there more than one scan? + */ + +GLOBAL(boolean) +jpeg_has_multiple_scans (j_decompress_ptr cinfo) +{ + /* Only valid after jpeg_read_header completes */ + if (cinfo->global_state < DSTATE_READY || + cinfo->global_state > DSTATE_STOPPING) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + return cinfo->inputctl->has_multiple_scans; +} + + +/* + * Finish JPEG decompression. + * + * This will normally just verify the file trailer and release temp storage. + * + * Returns FALSE if suspended. The return value need be inspected only if + * a suspending data source is used. + */ + +GLOBAL(boolean) +jpeg_finish_decompress (j_decompress_ptr cinfo) +{ + if ((cinfo->global_state == DSTATE_SCANNING || + cinfo->global_state == DSTATE_RAW_OK) && ! cinfo->buffered_image) { + /* Terminate final pass of non-buffered mode */ + if (cinfo->output_scanline < cinfo->output_height) + ERREXIT(cinfo, JERR_TOO_LITTLE_DATA); + (*cinfo->master->finish_output_pass) (cinfo); + cinfo->global_state = DSTATE_STOPPING; + } else if (cinfo->global_state == DSTATE_BUFIMAGE) { + /* Finishing after a buffered-image operation */ + cinfo->global_state = DSTATE_STOPPING; + } else if (cinfo->global_state != DSTATE_STOPPING) { + /* STOPPING = repeat call after a suspension, anything else is error */ + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + } + /* Read until EOI */ + while (! cinfo->inputctl->eoi_reached) { + if ((*cinfo->inputctl->consume_input) (cinfo) == JPEG_SUSPENDED) + return FALSE; /* Suspend, come back later */ + } + /* Do final cleanup */ + (*cinfo->src->term_source) (cinfo); + /* We can use jpeg_abort to release memory and reset global_state */ + jpeg_abort((j_common_ptr) cinfo); + return TRUE; +} diff --git a/Engine/lib/ljpeg/jdapistd.c b/Engine/lib/ljpeg/jdapistd.c new file mode 100644 index 000000000..c8e3fa0c3 --- /dev/null +++ b/Engine/lib/ljpeg/jdapistd.c @@ -0,0 +1,275 @@ +/* + * jdapistd.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains application interface code for the decompression half + * of the JPEG library. These are the "standard" API routines that are + * used in the normal full-decompression case. They are not used by a + * transcoding-only application. Note that if an application links in + * jpeg_start_decompress, it will end up linking in the entire decompressor. + * We thus must separate this file from jdapimin.c to avoid linking the + * whole decompression library into a transcoder. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Forward declarations */ +LOCAL(boolean) output_pass_setup JPP((j_decompress_ptr cinfo)); + + +/* + * Decompression initialization. + * jpeg_read_header must be completed before calling this. + * + * If a multipass operating mode was selected, this will do all but the + * last pass, and thus may take a great deal of time. + * + * Returns FALSE if suspended. The return value need be inspected only if + * a suspending data source is used. + */ + +GLOBAL(boolean) +jpeg_start_decompress (j_decompress_ptr cinfo) +{ + if (cinfo->global_state == DSTATE_READY) { + /* First call: initialize master control, select active modules */ + jinit_master_decompress(cinfo); + if (cinfo->buffered_image) { + /* No more work here; expecting jpeg_start_output next */ + cinfo->global_state = DSTATE_BUFIMAGE; + return TRUE; + } + cinfo->global_state = DSTATE_PRELOAD; + } + if (cinfo->global_state == DSTATE_PRELOAD) { + /* If file has multiple scans, absorb them all into the coef buffer */ + if (cinfo->inputctl->has_multiple_scans) { +#ifdef D_MULTISCAN_FILES_SUPPORTED + for (;;) { + int retcode; + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + /* Absorb some more input */ + retcode = (*cinfo->inputctl->consume_input) (cinfo); + if (retcode == JPEG_SUSPENDED) + return FALSE; + if (retcode == JPEG_REACHED_EOI) + break; + /* Advance progress counter if appropriate */ + if (cinfo->progress != NULL && + (retcode == JPEG_ROW_COMPLETED || retcode == JPEG_REACHED_SOS)) { + if (++cinfo->progress->pass_counter >= cinfo->progress->pass_limit) { + /* jdmaster underestimated number of scans; ratchet up one scan */ + cinfo->progress->pass_limit += (long) cinfo->total_iMCU_rows; + } + } + } +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + } + cinfo->output_scan_number = cinfo->input_scan_number; + } else if (cinfo->global_state != DSTATE_PRESCAN) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + /* Perform any dummy output passes, and set up for the final pass */ + return output_pass_setup(cinfo); +} + + +/* + * Set up for an output pass, and perform any dummy pass(es) needed. + * Common subroutine for jpeg_start_decompress and jpeg_start_output. + * Entry: global_state = DSTATE_PRESCAN only if previously suspended. + * Exit: If done, returns TRUE and sets global_state for proper output mode. + * If suspended, returns FALSE and sets global_state = DSTATE_PRESCAN. + */ + +LOCAL(boolean) +output_pass_setup (j_decompress_ptr cinfo) +{ + if (cinfo->global_state != DSTATE_PRESCAN) { + /* First call: do pass setup */ + (*cinfo->master->prepare_for_output_pass) (cinfo); + cinfo->output_scanline = 0; + cinfo->global_state = DSTATE_PRESCAN; + } + /* Loop over any required dummy passes */ + while (cinfo->master->is_dummy_pass) { +#ifdef QUANT_2PASS_SUPPORTED + /* Crank through the dummy pass */ + while (cinfo->output_scanline < cinfo->output_height) { + JDIMENSION last_scanline; + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) cinfo->output_scanline; + cinfo->progress->pass_limit = (long) cinfo->output_height; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + /* Process some data */ + last_scanline = cinfo->output_scanline; + (*cinfo->main->process_data) (cinfo, (JSAMPARRAY) NULL, + &cinfo->output_scanline, (JDIMENSION) 0); + if (cinfo->output_scanline == last_scanline) + return FALSE; /* No progress made, must suspend */ + } + /* Finish up dummy pass, and set up for another one */ + (*cinfo->master->finish_output_pass) (cinfo); + (*cinfo->master->prepare_for_output_pass) (cinfo); + cinfo->output_scanline = 0; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif /* QUANT_2PASS_SUPPORTED */ + } + /* Ready for application to drive output pass through + * jpeg_read_scanlines or jpeg_read_raw_data. + */ + cinfo->global_state = cinfo->raw_data_out ? DSTATE_RAW_OK : DSTATE_SCANNING; + return TRUE; +} + + +/* + * Read some scanlines of data from the JPEG decompressor. + * + * The return value will be the number of lines actually read. + * This may be less than the number requested in several cases, + * including bottom of image, data source suspension, and operating + * modes that emit multiple scanlines at a time. + * + * Note: we warn about excess calls to jpeg_read_scanlines() since + * this likely signals an application programmer error. However, + * an oversize buffer (max_lines > scanlines remaining) is not an error. + */ + +GLOBAL(JDIMENSION) +jpeg_read_scanlines (j_decompress_ptr cinfo, JSAMPARRAY scanlines, + JDIMENSION max_lines) +{ + JDIMENSION row_ctr; + + if (cinfo->global_state != DSTATE_SCANNING) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + if (cinfo->output_scanline >= cinfo->output_height) { + WARNMS(cinfo, JWRN_TOO_MUCH_DATA); + return 0; + } + + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) cinfo->output_scanline; + cinfo->progress->pass_limit = (long) cinfo->output_height; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + + /* Process some data */ + row_ctr = 0; + (*cinfo->main->process_data) (cinfo, scanlines, &row_ctr, max_lines); + cinfo->output_scanline += row_ctr; + return row_ctr; +} + + +/* + * Alternate entry point to read raw data. + * Processes exactly one iMCU row per call, unless suspended. + */ + +GLOBAL(JDIMENSION) +jpeg_read_raw_data (j_decompress_ptr cinfo, JSAMPIMAGE data, + JDIMENSION max_lines) +{ + JDIMENSION lines_per_iMCU_row; + + if (cinfo->global_state != DSTATE_RAW_OK) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + if (cinfo->output_scanline >= cinfo->output_height) { + WARNMS(cinfo, JWRN_TOO_MUCH_DATA); + return 0; + } + + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) { + cinfo->progress->pass_counter = (long) cinfo->output_scanline; + cinfo->progress->pass_limit = (long) cinfo->output_height; + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + } + + /* Verify that at least one iMCU row can be returned. */ + lines_per_iMCU_row = cinfo->max_v_samp_factor * cinfo->min_DCT_scaled_size; + if (max_lines < lines_per_iMCU_row) + ERREXIT(cinfo, JERR_BUFFER_SIZE); + + /* Decompress directly into user's buffer. */ + if (! (*cinfo->coef->decompress_data) (cinfo, data)) + return 0; /* suspension forced, can do nothing more */ + + /* OK, we processed one iMCU row. */ + cinfo->output_scanline += lines_per_iMCU_row; + return lines_per_iMCU_row; +} + + +/* Additional entry points for buffered-image mode. */ + +#ifdef D_MULTISCAN_FILES_SUPPORTED + +/* + * Initialize for an output pass in buffered-image mode. + */ + +GLOBAL(boolean) +jpeg_start_output (j_decompress_ptr cinfo, int scan_number) +{ + if (cinfo->global_state != DSTATE_BUFIMAGE && + cinfo->global_state != DSTATE_PRESCAN) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + /* Limit scan number to valid range */ + if (scan_number <= 0) + scan_number = 1; + if (cinfo->inputctl->eoi_reached && + scan_number > cinfo->input_scan_number) + scan_number = cinfo->input_scan_number; + cinfo->output_scan_number = scan_number; + /* Perform any dummy output passes, and set up for the real pass */ + return output_pass_setup(cinfo); +} + + +/* + * Finish up after an output pass in buffered-image mode. + * + * Returns FALSE if suspended. The return value need be inspected only if + * a suspending data source is used. + */ + +GLOBAL(boolean) +jpeg_finish_output (j_decompress_ptr cinfo) +{ + if ((cinfo->global_state == DSTATE_SCANNING || + cinfo->global_state == DSTATE_RAW_OK) && cinfo->buffered_image) { + /* Terminate this pass. */ + /* We do not require the whole pass to have been completed. */ + (*cinfo->master->finish_output_pass) (cinfo); + cinfo->global_state = DSTATE_BUFPOST; + } else if (cinfo->global_state != DSTATE_BUFPOST) { + /* BUFPOST = repeat call after a suspension, anything else is error */ + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + } + /* Read markers looking for SOS or EOI */ + while (cinfo->input_scan_number <= cinfo->output_scan_number && + ! cinfo->inputctl->eoi_reached) { + if ((*cinfo->inputctl->consume_input) (cinfo) == JPEG_SUSPENDED) + return FALSE; /* Suspend, come back later */ + } + cinfo->global_state = DSTATE_BUFIMAGE; + return TRUE; +} + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ diff --git a/Engine/lib/ljpeg/jdatadst.c b/Engine/lib/ljpeg/jdatadst.c new file mode 100644 index 000000000..60907b92f --- /dev/null +++ b/Engine/lib/ljpeg/jdatadst.c @@ -0,0 +1,159 @@ +/* + * jdatadst.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains compression data destination routines for the case of + * emitting JPEG data to a file (or any stdio stream). While these routines + * are sufficient for most applications, some will want to use a different + * destination manager. + * IMPORTANT: we assume that fwrite() will correctly transcribe an array of + * JOCTETs into 8-bit-wide elements on external storage. If char is wider + * than 8 bits on your machine, you may need to do some tweaking. + */ + +/* this is not a core library module, so it doesn't define JPEG_INTERNALS */ +#include "jinclude.h" +#include "jpeglib.h" +#include "jerror.h" + + +int (*JFREAD)(void *client_data, unsigned char*, int) = NULL; +int (*JFWRITE)(void *client_data, unsigned char*, int) = NULL; +int (*JFFLUSH)(void *client_data) = NULL; +int (*JFERROR)(void *client_data) = NULL; + + + + +/* Expanded data destination object for stdio output */ + +typedef struct { + struct jpeg_destination_mgr pub; /* public fields */ + +// FILE * outfile; /* target stream */ + JOCTET * buffer; /* start of buffer */ +} my_destination_mgr; + +typedef my_destination_mgr * my_dest_ptr; + +#define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */ + + +/* + * Initialize destination --- called by jpeg_start_compress + * before any data is actually written. + */ + +METHODDEF(void) +init_destination (j_compress_ptr cinfo) +{ + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + + /* Allocate the output buffer --- it will be released when done with image */ + dest->buffer = (JOCTET *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + OUTPUT_BUF_SIZE * SIZEOF(JOCTET)); + + dest->pub.next_output_byte = dest->buffer; + dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; +} + + +/* + * Empty the output buffer --- called whenever buffer fills up. + * + * In typical applications, this should write the entire output buffer + * (ignoring the current state of next_output_byte & free_in_buffer), + * reset the pointer & count to the start of the buffer, and return TRUE + * indicating that the buffer has been dumped. + * + * In applications that need to be able to suspend compression due to output + * overrun, a FALSE return indicates that the buffer cannot be emptied now. + * In this situation, the compressor will return to its caller (possibly with + * an indication that it has not accepted all the supplied scanlines). The + * application should resume compression after it has made more room in the + * output buffer. Note that there are substantial restrictions on the use of + * suspension --- see the documentation. + * + * When suspending, the compressor will back up to a convenient restart point + * (typically the start of the current MCU). next_output_byte & free_in_buffer + * indicate where the restart point will be if the current call returns FALSE. + * Data beyond this point will be regenerated after resumption, so do not + * write it out when emptying the buffer externally. + */ + +METHODDEF(boolean) +empty_output_buffer (j_compress_ptr cinfo) +{ + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + + if (JFWRITE(cinfo->client_data, dest->buffer, OUTPUT_BUF_SIZE) != + (jpeg_size_t) OUTPUT_BUF_SIZE) + ERREXIT(cinfo, JERR_FILE_WRITE); + + dest->pub.next_output_byte = dest->buffer; + dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; + + return TRUE; +} + + +/* + * Terminate destination --- called by jpeg_finish_compress + * after all data has been written. Usually needs to flush buffer. + * + * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding + * application must deal with any cleanup that should happen even + * for error exit. + */ + +METHODDEF(void) +term_destination (j_compress_ptr cinfo) +{ + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + jpeg_size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer; + + /* Write any data remaining in the buffer */ + if (datacount > 0) { + if ((jpeg_size_t)JFWRITE(cinfo->client_data, dest->buffer, datacount) != (jpeg_size_t)datacount) + ERREXIT(cinfo, JERR_FILE_WRITE); + } + JFFLUSH(cinfo->client_data); + /* Make sure we wrote the output file OK */ + if (JFERROR(cinfo->client_data)) + ERREXIT(cinfo, JERR_FILE_WRITE); +} + + +/* + * Prepare for output to a stdio stream. + * The caller must have already opened the stream, and is responsible + * for closing it after finishing compression. + */ + +GLOBAL(void) +jpeg_stdio_dest (j_compress_ptr cinfo) +{ + my_dest_ptr dest; + + /* The destination object is made permanent so that multiple JPEG images + * can be written to the same file without re-executing jpeg_stdio_dest. + * This makes it dangerous to use this manager and a different destination + * manager serially with the same JPEG object, because their private object + * sizes may be different. Caveat programmer. + */ + if (cinfo->dest == NULL) { /* first time for this JPEG object? */ + cinfo->dest = (struct jpeg_destination_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(my_destination_mgr)); + } + + dest = (my_dest_ptr) cinfo->dest; + dest->pub.init_destination = init_destination; + dest->pub.empty_output_buffer = empty_output_buffer; + dest->pub.term_destination = term_destination; + //dest->outfile = outfile; +} diff --git a/Engine/lib/ljpeg/jdatasrc.c b/Engine/lib/ljpeg/jdatasrc.c new file mode 100644 index 000000000..3ebd8b1c0 --- /dev/null +++ b/Engine/lib/ljpeg/jdatasrc.c @@ -0,0 +1,212 @@ +/* + * jdatasrc.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains decompression data source routines for the case of + * reading JPEG data from a file (or any stdio stream). While these routines + * are sufficient for most applications, some will want to use a different + * source manager. + * IMPORTANT: we assume that fread() will correctly transcribe an array of + * JOCTETs from 8-bit-wide elements on external storage. If char is wider + * than 8 bits on your machine, you may need to do some tweaking. + */ + +/* this is not a core library module, so it doesn't define JPEG_INTERNALS */ +#include "jinclude.h" +#include "jpeglib.h" +#include "jerror.h" + + +/* Expanded data source object for stdio input */ + +typedef struct { + struct jpeg_source_mgr pub; /* public fields */ + +// FILE * infile; /* source stream */ + JOCTET * buffer; /* start of buffer */ + boolean start_of_file; /* have we gotten any data yet? */ +} my_source_mgr; + +typedef my_source_mgr * my_src_ptr; + +#define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */ + + +/* + * Initialize source --- called by jpeg_read_header + * before any data is actually read. + */ + +METHODDEF(void) +init_source (j_decompress_ptr cinfo) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + /* We reset the empty-input-file flag for each image, + * but we don't clear the input buffer. + * This is correct behavior for reading a series of images from one source. + */ + src->start_of_file = TRUE; +} + + +/* + * Fill the input buffer --- called whenever buffer is emptied. + * + * In typical applications, this should read fresh data into the buffer + * (ignoring the current state of next_input_byte & bytes_in_buffer), + * reset the pointer & count to the start of the buffer, and return TRUE + * indicating that the buffer has been reloaded. It is not necessary to + * fill the buffer entirely, only to obtain at least one more byte. + * + * There is no such thing as an EOF return. If the end of the file has been + * reached, the routine has a choice of ERREXIT() or inserting fake data into + * the buffer. In most cases, generating a warning message and inserting a + * fake EOI marker is the best course of action --- this will allow the + * decompressor to output however much of the image is there. However, + * the resulting error message is misleading if the real problem is an empty + * input file, so we handle that case specially. + * + * In applications that need to be able to suspend compression due to input + * not being available yet, a FALSE return indicates that no more data can be + * obtained right now, but more may be forthcoming later. In this situation, + * the decompressor will return to its caller (with an indication of the + * number of scanlines it has read, if any). The application should resume + * decompression after it has loaded more data into the input buffer. Note + * that there are substantial restrictions on the use of suspension --- see + * the documentation. + * + * When suspending, the decompressor will back up to a convenient restart point + * (typically the start of the current MCU). next_input_byte & bytes_in_buffer + * indicate where the restart point will be if the current call returns FALSE. + * Data beyond this point must be rescanned after resumption, so move it to + * the front of the buffer rather than discarding it. + */ + +METHODDEF(boolean) +fill_input_buffer (j_decompress_ptr cinfo) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + jpeg_size_t nbytes; + + nbytes = JFREAD(cinfo->client_data, src->buffer, INPUT_BUF_SIZE); + + if (nbytes <= 0) { + if (src->start_of_file) /* Treat empty input file as fatal error */ + ERREXIT(cinfo, JERR_INPUT_EMPTY); + WARNMS(cinfo, JWRN_JPEG_EOF); + /* Insert a fake EOI marker */ + src->buffer[0] = (JOCTET) 0xFF; + src->buffer[1] = (JOCTET) JPEG_EOI; + nbytes = 2; + } + + src->pub.next_input_byte = src->buffer; + src->pub.bytes_in_buffer = nbytes; + src->start_of_file = FALSE; + + return TRUE; +} + + +/* + * Skip data --- used to skip over a potentially large amount of + * uninteresting data (such as an APPn marker). + * + * Writers of suspendable-input applications must note that skip_input_data + * is not granted the right to give a suspension return. If the skip extends + * beyond the data currently in the buffer, the buffer can be marked empty so + * that the next read will cause a fill_input_buffer call that can suspend. + * Arranging for additional bytes to be discarded before reloading the input + * buffer is the application writer's problem. + */ + +METHODDEF(void) +skip_input_data (j_decompress_ptr cinfo, long num_bytes) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + /* Just a dumb implementation for now. Could use fseek() except + * it doesn't work on pipes. Not clear that being smart is worth + * any trouble anyway --- large skips are infrequent. + */ + if (num_bytes > 0) { + while (num_bytes > (long) src->pub.bytes_in_buffer) { + num_bytes -= (long) src->pub.bytes_in_buffer; + (void) fill_input_buffer(cinfo); + /* note we assume that fill_input_buffer will never return FALSE, + * so suspension need not be handled. + */ + } + src->pub.next_input_byte += (jpeg_size_t) num_bytes; + src->pub.bytes_in_buffer -= (jpeg_size_t) num_bytes; + } +} + + +/* + * An additional method that can be provided by data source modules is the + * resync_to_restart method for error recovery in the presence of RST markers. + * For the moment, this source module just uses the default resync method + * provided by the JPEG library. That method assumes that no backtracking + * is possible. + */ + + +/* + * Terminate source --- called by jpeg_finish_decompress + * after all data has been read. Often a no-op. + * + * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding + * application must deal with any cleanup that should happen even + * for error exit. + */ + +METHODDEF(void) +term_source (j_decompress_ptr cinfo) +{ + /* no work necessary here */ +} + + +/* + * Prepare for input from a stdio stream. + * The caller must have already opened the stream, and is responsible + * for closing it after finishing decompression. + */ + +GLOBAL(void) +jpeg_stdio_src (j_decompress_ptr cinfo) +{ + my_src_ptr src; + + /* The source object and input buffer are made permanent so that a series + * of JPEG images can be read from the same file by calling jpeg_stdio_src + * only before the first one. (If we discarded the buffer at the end of + * one image, we'd likely lose the start of the next one.) + * This makes it unsafe to use this manager and a different source + * manager serially with the same JPEG object. Caveat programmer. + */ + if (cinfo->src == NULL) { /* first time for this JPEG object? */ + cinfo->src = (struct jpeg_source_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(my_source_mgr)); + src = (my_src_ptr) cinfo->src; + src->buffer = (JOCTET *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + INPUT_BUF_SIZE * SIZEOF(JOCTET)); + } + + src = (my_src_ptr) cinfo->src; + src->pub.init_source = init_source; + src->pub.fill_input_buffer = fill_input_buffer; + src->pub.skip_input_data = skip_input_data; + src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */ + src->pub.term_source = term_source; +// src->infile = infile; + src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ + src->pub.next_input_byte = NULL; /* until buffer loaded */ +} diff --git a/Engine/lib/ljpeg/jdcoefct.c b/Engine/lib/ljpeg/jdcoefct.c new file mode 100644 index 000000000..4938d20fc --- /dev/null +++ b/Engine/lib/ljpeg/jdcoefct.c @@ -0,0 +1,736 @@ +/* + * jdcoefct.c + * + * Copyright (C) 1994-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the coefficient buffer controller for decompression. + * This controller is the top level of the JPEG decompressor proper. + * The coefficient buffer lies between entropy decoding and inverse-DCT steps. + * + * In buffered-image mode, this controller is the interface between + * input-oriented processing and output-oriented processing. + * Also, the input side (only) is used when reading a file for transcoding. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + +/* Block smoothing is only applicable for progressive JPEG, so: */ +#ifndef D_PROGRESSIVE_SUPPORTED +#undef BLOCK_SMOOTHING_SUPPORTED +#endif + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_d_coef_controller pub; /* public fields */ + + /* These variables keep track of the current location of the input side. */ + /* cinfo->input_iMCU_row is also used for this. */ + JDIMENSION MCU_ctr; /* counts MCUs processed in current row */ + int MCU_vert_offset; /* counts MCU rows within iMCU row */ + int MCU_rows_per_iMCU_row; /* number of such rows needed */ + + /* The output side's location is represented by cinfo->output_iMCU_row. */ + + /* In single-pass modes, it's sufficient to buffer just one MCU. + * We allocate a workspace of D_MAX_BLOCKS_IN_MCU coefficient blocks, + * and let the entropy decoder write into that workspace each time. + * (On 80x86, the workspace is FAR even though it's not really very big; + * this is to keep the module interfaces unchanged when a large coefficient + * buffer is necessary.) + * In multi-pass modes, this array points to the current MCU's blocks + * within the virtual arrays; it is used only by the input side. + */ + JBLOCKROW MCU_buffer[D_MAX_BLOCKS_IN_MCU]; + +#ifdef D_MULTISCAN_FILES_SUPPORTED + /* In multi-pass modes, we need a virtual block array for each component. */ + jvirt_barray_ptr whole_image[MAX_COMPONENTS]; +#endif + +#ifdef BLOCK_SMOOTHING_SUPPORTED + /* When doing block smoothing, we latch coefficient Al values here */ + int * coef_bits_latch; +#define SAVED_COEFS 6 /* we save coef_bits[0..5] */ +#endif +} my_coef_controller; + +typedef my_coef_controller * my_coef_ptr; + +/* Forward declarations */ +METHODDEF(int) decompress_onepass + JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf)); +#ifdef D_MULTISCAN_FILES_SUPPORTED +METHODDEF(int) decompress_data + JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf)); +#endif +#ifdef BLOCK_SMOOTHING_SUPPORTED +LOCAL(boolean) smoothing_ok JPP((j_decompress_ptr cinfo)); +METHODDEF(int) decompress_smooth_data + JPP((j_decompress_ptr cinfo, JSAMPIMAGE output_buf)); +#endif + + +LOCAL(void) +start_iMCU_row (j_decompress_ptr cinfo) +/* Reset within-iMCU-row counters for a new row (input side) */ +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + /* In an interleaved scan, an MCU row is the same as an iMCU row. + * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. + * But at the bottom of the image, process only what's left. + */ + if (cinfo->comps_in_scan > 1) { + coef->MCU_rows_per_iMCU_row = 1; + } else { + if (cinfo->input_iMCU_row < (cinfo->total_iMCU_rows-1)) + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor; + else + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height; + } + + coef->MCU_ctr = 0; + coef->MCU_vert_offset = 0; +} + + +/* + * Initialize for an input processing pass. + */ + +METHODDEF(void) +start_input_pass (j_decompress_ptr cinfo) +{ + cinfo->input_iMCU_row = 0; + start_iMCU_row(cinfo); +} + + +/* + * Initialize for an output processing pass. + */ + +METHODDEF(void) +start_output_pass (j_decompress_ptr cinfo) +{ +#ifdef BLOCK_SMOOTHING_SUPPORTED + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + /* If multipass, check to see whether to use block smoothing on this pass */ + if (coef->pub.coef_arrays != NULL) { + if (cinfo->do_block_smoothing && smoothing_ok(cinfo)) + coef->pub.decompress_data = decompress_smooth_data; + else + coef->pub.decompress_data = decompress_data; + } +#endif + cinfo->output_iMCU_row = 0; +} + + +/* + * Decompress and return some data in the single-pass case. + * Always attempts to emit one fully interleaved MCU row ("iMCU" row). + * Input and output must run in lockstep since we have only a one-MCU buffer. + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + * + * NB: output_buf contains a plane for each component in image, + * which we index according to the component's SOF position. + */ + +METHODDEF(int) +decompress_onepass (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + JDIMENSION last_MCU_col = cinfo->MCUs_per_row - 1; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + int blkn, ci, xindex, yindex, yoffset, useful_width; + JSAMPARRAY output_ptr; + JDIMENSION start_col, output_col; + jpeg_component_info *compptr; + inverse_DCT_method_ptr inverse_DCT; + + /* Loop to process as much as one whole iMCU row */ + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + yoffset++) { + for (MCU_col_num = coef->MCU_ctr; MCU_col_num <= last_MCU_col; + MCU_col_num++) { + /* Try to fetch an MCU. Entropy decoder expects buffer to be zeroed. */ + jzero_far((void FAR *) coef->MCU_buffer[0], + (size_t) (cinfo->blocks_in_MCU * SIZEOF(JBLOCK))); + if (! (*cinfo->entropy->decode_mcu) (cinfo, coef->MCU_buffer)) { + /* Suspension forced; update state counters and exit */ + coef->MCU_vert_offset = yoffset; + coef->MCU_ctr = MCU_col_num; + return JPEG_SUSPENDED; + } + /* Determine where data should go in output_buf and do the IDCT thing. + * We skip dummy blocks at the right and bottom edges (but blkn gets + * incremented past them!). Note the inner loop relies on having + * allocated the MCU_buffer[] blocks sequentially. + */ + blkn = 0; /* index of current DCT block within MCU */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* Don't bother to IDCT an uninteresting component. */ + if (! compptr->component_needed) { + blkn += compptr->MCU_blocks; + continue; + } + inverse_DCT = cinfo->idct->inverse_DCT[compptr->component_index]; + useful_width = (MCU_col_num < last_MCU_col) ? compptr->MCU_width + : compptr->last_col_width; + output_ptr = output_buf[compptr->component_index] + + yoffset * compptr->DCT_scaled_size; + start_col = MCU_col_num * compptr->MCU_sample_width; + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + if (cinfo->input_iMCU_row < last_iMCU_row || + yoffset+yindex < compptr->last_row_height) { + output_col = start_col; + for (xindex = 0; xindex < useful_width; xindex++) { + (*inverse_DCT) (cinfo, compptr, + (JCOEFPTR) coef->MCU_buffer[blkn+xindex], + output_ptr, output_col); + output_col += compptr->DCT_scaled_size; + } + } + blkn += compptr->MCU_width; + output_ptr += compptr->DCT_scaled_size; + } + } + } + /* Completed an MCU row, but perhaps not an iMCU row */ + coef->MCU_ctr = 0; + } + /* Completed the iMCU row, advance counters for next one */ + cinfo->output_iMCU_row++; + if (++(cinfo->input_iMCU_row) < cinfo->total_iMCU_rows) { + start_iMCU_row(cinfo); + return JPEG_ROW_COMPLETED; + } + /* Completed the scan */ + (*cinfo->inputctl->finish_input_pass) (cinfo); + return JPEG_SCAN_COMPLETED; +} + + +/* + * Dummy consume-input routine for single-pass operation. + */ + +METHODDEF(int) +dummy_consume_data (j_decompress_ptr cinfo) +{ + return JPEG_SUSPENDED; /* Always indicate nothing was done */ +} + + +#ifdef D_MULTISCAN_FILES_SUPPORTED + +/* + * Consume input data and store it in the full-image coefficient buffer. + * We read as much as one fully interleaved MCU row ("iMCU" row) per call, + * ie, v_samp_factor block rows for each component in the scan. + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + */ + +METHODDEF(int) +consume_data (j_decompress_ptr cinfo) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION MCU_col_num; /* index of current MCU within row */ + int blkn, ci, xindex, yindex, yoffset; + JDIMENSION start_col; + JBLOCKARRAY buffer[MAX_COMPS_IN_SCAN]; + JBLOCKROW buffer_ptr; + jpeg_component_info *compptr; + + /* Align the virtual buffers for the components used in this scan. */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + buffer[ci] = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[compptr->component_index], + cinfo->input_iMCU_row * compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, TRUE); + /* Note: entropy decoder expects buffer to be zeroed, + * but this is handled automatically by the memory manager + * because we requested a pre-zeroed array. + */ + } + + /* Loop to process one whole iMCU row */ + for (yoffset = coef->MCU_vert_offset; yoffset < coef->MCU_rows_per_iMCU_row; + yoffset++) { + for (MCU_col_num = coef->MCU_ctr; MCU_col_num < cinfo->MCUs_per_row; + MCU_col_num++) { + /* Construct list of pointers to DCT blocks belonging to this MCU */ + blkn = 0; /* index of current DCT block within MCU */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + start_col = MCU_col_num * compptr->MCU_width; + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + buffer_ptr = buffer[ci][yindex+yoffset] + start_col; + for (xindex = 0; xindex < compptr->MCU_width; xindex++) { + coef->MCU_buffer[blkn++] = buffer_ptr++; + } + } + } + /* Try to fetch the MCU. */ + if (! (*cinfo->entropy->decode_mcu) (cinfo, coef->MCU_buffer)) { + /* Suspension forced; update state counters and exit */ + coef->MCU_vert_offset = yoffset; + coef->MCU_ctr = MCU_col_num; + return JPEG_SUSPENDED; + } + } + /* Completed an MCU row, but perhaps not an iMCU row */ + coef->MCU_ctr = 0; + } + /* Completed the iMCU row, advance counters for next one */ + if (++(cinfo->input_iMCU_row) < cinfo->total_iMCU_rows) { + start_iMCU_row(cinfo); + return JPEG_ROW_COMPLETED; + } + /* Completed the scan */ + (*cinfo->inputctl->finish_input_pass) (cinfo); + return JPEG_SCAN_COMPLETED; +} + + +/* + * Decompress and return some data in the multi-pass case. + * Always attempts to emit one fully interleaved MCU row ("iMCU" row). + * Return value is JPEG_ROW_COMPLETED, JPEG_SCAN_COMPLETED, or JPEG_SUSPENDED. + * + * NB: output_buf contains a plane for each component in image. + */ + +METHODDEF(int) +decompress_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + JDIMENSION block_num; + int ci, block_row, block_rows; + JBLOCKARRAY buffer; + JBLOCKROW buffer_ptr; + JSAMPARRAY output_ptr; + JDIMENSION output_col; + jpeg_component_info *compptr; + inverse_DCT_method_ptr inverse_DCT; + + /* Force some input to be done if we are getting ahead of the input. */ + while (cinfo->input_scan_number < cinfo->output_scan_number || + (cinfo->input_scan_number == cinfo->output_scan_number && + cinfo->input_iMCU_row <= cinfo->output_iMCU_row)) { + if ((*cinfo->inputctl->consume_input)(cinfo) == JPEG_SUSPENDED) + return JPEG_SUSPENDED; + } + + /* OK, output from the virtual arrays. */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Don't bother to IDCT an uninteresting component. */ + if (! compptr->component_needed) + continue; + /* Align the virtual buffer for this component. */ + buffer = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[ci], + cinfo->output_iMCU_row * compptr->v_samp_factor, + (JDIMENSION) compptr->v_samp_factor, FALSE); + /* Count non-dummy DCT block rows in this iMCU row. */ + if (cinfo->output_iMCU_row < last_iMCU_row) + block_rows = compptr->v_samp_factor; + else { + /* NB: can't use last_row_height here; it is input-side-dependent! */ + block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + if (block_rows == 0) block_rows = compptr->v_samp_factor; + } + inverse_DCT = cinfo->idct->inverse_DCT[ci]; + output_ptr = output_buf[ci]; + /* Loop over all DCT blocks to be processed. */ + for (block_row = 0; block_row < block_rows; block_row++) { + buffer_ptr = buffer[block_row]; + output_col = 0; + for (block_num = 0; block_num < compptr->width_in_blocks; block_num++) { + (*inverse_DCT) (cinfo, compptr, (JCOEFPTR) buffer_ptr, + output_ptr, output_col); + buffer_ptr++; + output_col += compptr->DCT_scaled_size; + } + output_ptr += compptr->DCT_scaled_size; + } + } + + if (++(cinfo->output_iMCU_row) < cinfo->total_iMCU_rows) + return JPEG_ROW_COMPLETED; + return JPEG_SCAN_COMPLETED; +} + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + + +#ifdef BLOCK_SMOOTHING_SUPPORTED + +/* + * This code applies interblock smoothing as described by section K.8 + * of the JPEG standard: the first 5 AC coefficients are estimated from + * the DC values of a DCT block and its 8 neighboring blocks. + * We apply smoothing only for progressive JPEG decoding, and only if + * the coefficients it can estimate are not yet known to full precision. + */ + +/* Natural-order array positions of the first 5 zigzag-order coefficients */ +#define Q01_POS 1 +#define Q10_POS 8 +#define Q20_POS 16 +#define Q11_POS 9 +#define Q02_POS 2 + +/* + * Determine whether block smoothing is applicable and safe. + * We also latch the current states of the coef_bits[] entries for the + * AC coefficients; otherwise, if the input side of the decompressor + * advances into a new scan, we might think the coefficients are known + * more accurately than they really are. + */ + +LOCAL(boolean) +smoothing_ok (j_decompress_ptr cinfo) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + boolean smoothing_useful = FALSE; + int ci, coefi; + jpeg_component_info *compptr; + JQUANT_TBL * qtable; + int * coef_bits; + int * coef_bits_latch; + + if (! cinfo->progressive_mode || cinfo->coef_bits == NULL) + return FALSE; + + /* Allocate latch area if not already done */ + if (coef->coef_bits_latch == NULL) + coef->coef_bits_latch = (int *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->num_components * + (SAVED_COEFS * SIZEOF(int))); + coef_bits_latch = coef->coef_bits_latch; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* All components' quantization values must already be latched. */ + if ((qtable = compptr->quant_table) == NULL) + return FALSE; + /* Verify DC & first 5 AC quantizers are nonzero to avoid zero-divide. */ + if (qtable->quantval[0] == 0 || + qtable->quantval[Q01_POS] == 0 || + qtable->quantval[Q10_POS] == 0 || + qtable->quantval[Q20_POS] == 0 || + qtable->quantval[Q11_POS] == 0 || + qtable->quantval[Q02_POS] == 0) + return FALSE; + /* DC values must be at least partly known for all components. */ + coef_bits = cinfo->coef_bits[ci]; + if (coef_bits[0] < 0) + return FALSE; + /* Block smoothing is helpful if some AC coefficients remain inaccurate. */ + for (coefi = 1; coefi <= 5; coefi++) { + coef_bits_latch[coefi] = coef_bits[coefi]; + if (coef_bits[coefi] != 0) + smoothing_useful = TRUE; + } + coef_bits_latch += SAVED_COEFS; + } + + return smoothing_useful; +} + + +/* + * Variant of decompress_data for use when doing block smoothing. + */ + +METHODDEF(int) +decompress_smooth_data (j_decompress_ptr cinfo, JSAMPIMAGE output_buf) +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + JDIMENSION last_iMCU_row = cinfo->total_iMCU_rows - 1; + JDIMENSION block_num, last_block_column; + int ci, block_row, block_rows, access_rows; + JBLOCKARRAY buffer; + JBLOCKROW buffer_ptr, prev_block_row, next_block_row; + JSAMPARRAY output_ptr; + JDIMENSION output_col; + jpeg_component_info *compptr; + inverse_DCT_method_ptr inverse_DCT; + boolean first_row, last_row; + JBLOCK workspace; + int *coef_bits; + JQUANT_TBL *quanttbl; + INT32 Q00,Q01,Q02,Q10,Q11,Q20, num; + int DC1,DC2,DC3,DC4,DC5,DC6,DC7,DC8,DC9; + int Al, pred; + + /* Force some input to be done if we are getting ahead of the input. */ + while (cinfo->input_scan_number <= cinfo->output_scan_number && + ! cinfo->inputctl->eoi_reached) { + if (cinfo->input_scan_number == cinfo->output_scan_number) { + /* If input is working on current scan, we ordinarily want it to + * have completed the current row. But if input scan is DC, + * we want it to keep one row ahead so that next block row's DC + * values are up to date. + */ + JDIMENSION delta = (cinfo->Ss == 0) ? 1 : 0; + if (cinfo->input_iMCU_row > cinfo->output_iMCU_row+delta) + break; + } + if ((*cinfo->inputctl->consume_input)(cinfo) == JPEG_SUSPENDED) + return JPEG_SUSPENDED; + } + + /* OK, output from the virtual arrays. */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Don't bother to IDCT an uninteresting component. */ + if (! compptr->component_needed) + continue; + /* Count non-dummy DCT block rows in this iMCU row. */ + if (cinfo->output_iMCU_row < last_iMCU_row) { + block_rows = compptr->v_samp_factor; + access_rows = block_rows * 2; /* this and next iMCU row */ + last_row = FALSE; + } else { + /* NB: can't use last_row_height here; it is input-side-dependent! */ + block_rows = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + if (block_rows == 0) block_rows = compptr->v_samp_factor; + access_rows = block_rows; /* this iMCU row only */ + last_row = TRUE; + } + /* Align the virtual buffer for this component. */ + if (cinfo->output_iMCU_row > 0) { + access_rows += compptr->v_samp_factor; /* prior iMCU row too */ + buffer = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[ci], + (cinfo->output_iMCU_row - 1) * compptr->v_samp_factor, + (JDIMENSION) access_rows, FALSE); + buffer += compptr->v_samp_factor; /* point to current iMCU row */ + first_row = FALSE; + } else { + buffer = (*cinfo->mem->access_virt_barray) + ((j_common_ptr) cinfo, coef->whole_image[ci], + (JDIMENSION) 0, (JDIMENSION) access_rows, FALSE); + first_row = TRUE; + } + /* Fetch component-dependent info */ + coef_bits = coef->coef_bits_latch + (ci * SAVED_COEFS); + quanttbl = compptr->quant_table; + Q00 = quanttbl->quantval[0]; + Q01 = quanttbl->quantval[Q01_POS]; + Q10 = quanttbl->quantval[Q10_POS]; + Q20 = quanttbl->quantval[Q20_POS]; + Q11 = quanttbl->quantval[Q11_POS]; + Q02 = quanttbl->quantval[Q02_POS]; + inverse_DCT = cinfo->idct->inverse_DCT[ci]; + output_ptr = output_buf[ci]; + /* Loop over all DCT blocks to be processed. */ + for (block_row = 0; block_row < block_rows; block_row++) { + buffer_ptr = buffer[block_row]; + if (first_row && block_row == 0) + prev_block_row = buffer_ptr; + else + prev_block_row = buffer[block_row-1]; + if (last_row && block_row == block_rows-1) + next_block_row = buffer_ptr; + else + next_block_row = buffer[block_row+1]; + /* We fetch the surrounding DC values using a sliding-register approach. + * Initialize all nine here so as to do the right thing on narrow pics. + */ + DC1 = DC2 = DC3 = (int) prev_block_row[0][0]; + DC4 = DC5 = DC6 = (int) buffer_ptr[0][0]; + DC7 = DC8 = DC9 = (int) next_block_row[0][0]; + output_col = 0; + last_block_column = compptr->width_in_blocks - 1; + for (block_num = 0; block_num <= last_block_column; block_num++) { + /* Fetch current DCT block into workspace so we can modify it. */ + jcopy_block_row(buffer_ptr, (JBLOCKROW) workspace, (JDIMENSION) 1); + /* Update DC values */ + if (block_num < last_block_column) { + DC3 = (int) prev_block_row[1][0]; + DC6 = (int) buffer_ptr[1][0]; + DC9 = (int) next_block_row[1][0]; + } + /* Compute coefficient estimates per K.8. + * An estimate is applied only if coefficient is still zero, + * and is not known to be fully accurate. + */ + /* AC01 */ + if ((Al=coef_bits[1]) != 0 && workspace[1] == 0) { + num = 36 * Q00 * (DC4 - DC6); + if (num >= 0) { + pred = (int) (((Q01<<7) + num) / (Q01<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + pred = (int) (((Q10<<7) + num) / (Q10<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + pred = (int) (((Q20<<7) + num) / (Q20<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + pred = (int) (((Q11<<7) + num) / (Q11<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<= 0) { + pred = (int) (((Q02<<7) + num) / (Q02<<8)); + if (Al > 0 && pred >= (1< 0 && pred >= (1<DCT_scaled_size; + } + output_ptr += compptr->DCT_scaled_size; + } + } + + if (++(cinfo->output_iMCU_row) < cinfo->total_iMCU_rows) + return JPEG_ROW_COMPLETED; + return JPEG_SCAN_COMPLETED; +} + +#endif /* BLOCK_SMOOTHING_SUPPORTED */ + + +/* + * Initialize coefficient buffer controller. + */ + +GLOBAL(void) +jinit_d_coef_controller (j_decompress_ptr cinfo, boolean need_full_buffer) +{ + my_coef_ptr coef; + + coef = (my_coef_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_coef_controller)); + cinfo->coef = (struct jpeg_d_coef_controller *) coef; + coef->pub.start_input_pass = start_input_pass; + coef->pub.start_output_pass = start_output_pass; +#ifdef BLOCK_SMOOTHING_SUPPORTED + coef->coef_bits_latch = NULL; +#endif + + /* Create the coefficient buffer. */ + if (need_full_buffer) { +#ifdef D_MULTISCAN_FILES_SUPPORTED + /* Allocate a full-image virtual array for each component, */ + /* padded to a multiple of samp_factor DCT blocks in each direction. */ + /* Note we ask for a pre-zeroed array. */ + int ci, access_rows; + jpeg_component_info *compptr; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + access_rows = compptr->v_samp_factor; +#ifdef BLOCK_SMOOTHING_SUPPORTED + /* If block smoothing could be used, need a bigger window */ + if (cinfo->progressive_mode) + access_rows *= 3; +#endif + coef->whole_image[ci] = (*cinfo->mem->request_virt_barray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, TRUE, + (JDIMENSION) jround_up((long) compptr->width_in_blocks, + (long) compptr->h_samp_factor), + (JDIMENSION) jround_up((long) compptr->height_in_blocks, + (long) compptr->v_samp_factor), + (JDIMENSION) access_rows); + } + coef->pub.consume_data = consume_data; + coef->pub.decompress_data = decompress_data; + coef->pub.coef_arrays = coef->whole_image; /* link to virtual arrays */ +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + /* We only need a single-MCU buffer. */ + JBLOCKROW buffer; + int i; + + buffer = (JBLOCKROW) + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + D_MAX_BLOCKS_IN_MCU * SIZEOF(JBLOCK)); + for (i = 0; i < D_MAX_BLOCKS_IN_MCU; i++) { + coef->MCU_buffer[i] = buffer + i; + } + coef->pub.consume_data = dummy_consume_data; + coef->pub.decompress_data = decompress_onepass; + coef->pub.coef_arrays = NULL; /* flag for no virtual arrays */ + } +} diff --git a/Engine/lib/ljpeg/jdcolor.c b/Engine/lib/ljpeg/jdcolor.c new file mode 100644 index 000000000..6c04dfe8a --- /dev/null +++ b/Engine/lib/ljpeg/jdcolor.c @@ -0,0 +1,396 @@ +/* + * jdcolor.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains output colorspace conversion routines. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Private subobject */ + +typedef struct { + struct jpeg_color_deconverter pub; /* public fields */ + + /* Private state for YCC->RGB conversion */ + int * Cr_r_tab; /* => table for Cr to R conversion */ + int * Cb_b_tab; /* => table for Cb to B conversion */ + INT32 * Cr_g_tab; /* => table for Cr to G conversion */ + INT32 * Cb_g_tab; /* => table for Cb to G conversion */ +} my_color_deconverter; + +typedef my_color_deconverter * my_cconvert_ptr; + + +/**************** YCbCr -> RGB conversion: most common case **************/ + +/* + * YCbCr is defined per CCIR 601-1, except that Cb and Cr are + * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. + * The conversion equations to be implemented are therefore + * R = Y + 1.40200 * Cr + * G = Y - 0.34414 * Cb - 0.71414 * Cr + * B = Y + 1.77200 * Cb + * where Cb and Cr represent the incoming values less CENTERJSAMPLE. + * (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.) + * + * To avoid floating-point arithmetic, we represent the fractional constants + * as integers scaled up by 2^16 (about 4 digits precision); we have to divide + * the products by 2^16, with appropriate rounding, to get the correct answer. + * Notice that Y, being an integral input, does not contribute any fraction + * so it need not participate in the rounding. + * + * For even more speed, we avoid doing any multiplications in the inner loop + * by precalculating the constants times Cb and Cr for all possible values. + * For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table); + * for 12-bit samples it is still acceptable. It's not very reasonable for + * 16-bit samples, but if you want lossless storage you shouldn't be changing + * colorspace anyway. + * The Cr=>R and Cb=>B values can be rounded to integers in advance; the + * values for the G calculation are left scaled up, since we must add them + * together before rounding. + */ + +#define SCALEBITS 16 /* speediest right-shift on some machines */ +#define ONE_HALF ((INT32) 1 << (SCALEBITS-1)) +#define FIX(x) ((INT32) ((x) * (1L<RGB colorspace conversion. + */ + +LOCAL(void) +build_ycc_rgb_table (j_decompress_ptr cinfo) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + int i; + INT32 x; + SHIFT_TEMPS + + cconvert->Cr_r_tab = (int *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(int)); + cconvert->Cb_b_tab = (int *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(int)); + cconvert->Cr_g_tab = (INT32 *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(INT32)); + cconvert->Cb_g_tab = (INT32 *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(INT32)); + + for (i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) { + /* i is the actual input pixel value, in the range 0..MAXJSAMPLE */ + /* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */ + /* Cr=>R value is nearest int to 1.40200 * x */ + cconvert->Cr_r_tab[i] = (int) + RIGHT_SHIFT(FIX(1.40200) * x + ONE_HALF, SCALEBITS); + /* Cb=>B value is nearest int to 1.77200 * x */ + cconvert->Cb_b_tab[i] = (int) + RIGHT_SHIFT(FIX(1.77200) * x + ONE_HALF, SCALEBITS); + /* Cr=>G value is scaled-up -0.71414 * x */ + cconvert->Cr_g_tab[i] = (- FIX(0.71414)) * x; + /* Cb=>G value is scaled-up -0.34414 * x */ + /* We also add in ONE_HALF so that need not do it in inner loop */ + cconvert->Cb_g_tab[i] = (- FIX(0.34414)) * x + ONE_HALF; + } +} + + +/* + * Convert some rows of samples to the output colorspace. + * + * Note that we change from noninterleaved, one-plane-per-component format + * to interleaved-pixel format. The output buffer is therefore three times + * as wide as the input buffer. + * A starting row offset is provided only for the input buffer. The caller + * can easily adjust the passed output_buf value to accommodate any row + * offset required on that side. + */ + +METHODDEF(void) +ycc_rgb_convert (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + register int y, cb, cr; + register JSAMPROW outptr; + register JSAMPROW inptr0, inptr1, inptr2; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->output_width; + /* copy these pointers into registers if possible */ + register JSAMPLE * range_limit = cinfo->sample_range_limit; + register int * Crrtab = cconvert->Cr_r_tab; + register int * Cbbtab = cconvert->Cb_b_tab; + register INT32 * Crgtab = cconvert->Cr_g_tab; + register INT32 * Cbgtab = cconvert->Cb_g_tab; + SHIFT_TEMPS + + while (--num_rows >= 0) { + inptr0 = input_buf[0][input_row]; + inptr1 = input_buf[1][input_row]; + inptr2 = input_buf[2][input_row]; + input_row++; + outptr = *output_buf++; + for (col = 0; col < num_cols; col++) { + y = GETJSAMPLE(inptr0[col]); + cb = GETJSAMPLE(inptr1[col]); + cr = GETJSAMPLE(inptr2[col]); + /* Range-limiting is essential due to noise introduced by DCT losses. */ + outptr[RGB_RED] = range_limit[y + Crrtab[cr]]; + outptr[RGB_GREEN] = range_limit[y + + ((int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], + SCALEBITS))]; + outptr[RGB_BLUE] = range_limit[y + Cbbtab[cb]]; + outptr += RGB_PIXELSIZE; + } + } +} + + +/**************** Cases other than YCbCr -> RGB **************/ + + +/* + * Color conversion for no colorspace change: just copy the data, + * converting from separate-planes to interleaved representation. + */ + +METHODDEF(void) +null_convert (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + register JSAMPROW inptr, outptr; + register JDIMENSION count; + register int num_components = cinfo->num_components; + JDIMENSION num_cols = cinfo->output_width; + int ci; + + while (--num_rows >= 0) { + for (ci = 0; ci < num_components; ci++) { + inptr = input_buf[ci][input_row]; + outptr = output_buf[0] + ci; + for (count = num_cols; count > 0; count--) { + *outptr = *inptr++; /* needn't bother with GETJSAMPLE() here */ + outptr += num_components; + } + } + input_row++; + output_buf++; + } +} + + +/* + * Color conversion for grayscale: just copy the data. + * This also works for YCbCr -> grayscale conversion, in which + * we just copy the Y (luminance) component and ignore chrominance. + */ + +METHODDEF(void) +grayscale_convert (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + jcopy_sample_rows(input_buf[0], (int) input_row, output_buf, 0, + num_rows, cinfo->output_width); +} + + +/* + * Convert grayscale to RGB: just duplicate the graylevel three times. + * This is provided to support applications that don't want to cope + * with grayscale as a separate case. + */ + +METHODDEF(void) +gray_rgb_convert (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + register JSAMPROW inptr, outptr; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->output_width; + + while (--num_rows >= 0) { + inptr = input_buf[0][input_row++]; + outptr = *output_buf++; + for (col = 0; col < num_cols; col++) { + /* We can dispense with GETJSAMPLE() here */ + outptr[RGB_RED] = outptr[RGB_GREEN] = outptr[RGB_BLUE] = inptr[col]; + outptr += RGB_PIXELSIZE; + } + } +} + + +/* + * Adobe-style YCCK->CMYK conversion. + * We convert YCbCr to R=1-C, G=1-M, and B=1-Y using the same + * conversion as above, while passing K (black) unchanged. + * We assume build_ycc_rgb_table has been called. + */ + +METHODDEF(void) +ycck_cmyk_convert (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows) +{ + my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; + register int y, cb, cr; + register JSAMPROW outptr; + register JSAMPROW inptr0, inptr1, inptr2, inptr3; + register JDIMENSION col; + JDIMENSION num_cols = cinfo->output_width; + /* copy these pointers into registers if possible */ + register JSAMPLE * range_limit = cinfo->sample_range_limit; + register int * Crrtab = cconvert->Cr_r_tab; + register int * Cbbtab = cconvert->Cb_b_tab; + register INT32 * Crgtab = cconvert->Cr_g_tab; + register INT32 * Cbgtab = cconvert->Cb_g_tab; + SHIFT_TEMPS + + while (--num_rows >= 0) { + inptr0 = input_buf[0][input_row]; + inptr1 = input_buf[1][input_row]; + inptr2 = input_buf[2][input_row]; + inptr3 = input_buf[3][input_row]; + input_row++; + outptr = *output_buf++; + for (col = 0; col < num_cols; col++) { + y = GETJSAMPLE(inptr0[col]); + cb = GETJSAMPLE(inptr1[col]); + cr = GETJSAMPLE(inptr2[col]); + /* Range-limiting is essential due to noise introduced by DCT losses. */ + outptr[0] = range_limit[MAXJSAMPLE - (y + Crrtab[cr])]; /* red */ + outptr[1] = range_limit[MAXJSAMPLE - (y + /* green */ + ((int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], + SCALEBITS)))]; + outptr[2] = range_limit[MAXJSAMPLE - (y + Cbbtab[cb])]; /* blue */ + /* K passes through unchanged */ + outptr[3] = inptr3[col]; /* don't need GETJSAMPLE here */ + outptr += 4; + } + } +} + + +/* + * Empty method for start_pass. + */ + +METHODDEF(void) +start_pass_dcolor (j_decompress_ptr cinfo) +{ + /* no work needed */ +} + + +/* + * Module initialization routine for output colorspace conversion. + */ + +GLOBAL(void) +jinit_color_deconverter (j_decompress_ptr cinfo) +{ + my_cconvert_ptr cconvert; + int ci; + + cconvert = (my_cconvert_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_color_deconverter)); + cinfo->cconvert = (struct jpeg_color_deconverter *) cconvert; + cconvert->pub.start_pass = start_pass_dcolor; + + /* Make sure num_components agrees with jpeg_color_space */ + switch (cinfo->jpeg_color_space) { + case JCS_GRAYSCALE: + if (cinfo->num_components != 1) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + break; + + case JCS_RGB: + case JCS_YCbCr: + if (cinfo->num_components != 3) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + break; + + case JCS_CMYK: + case JCS_YCCK: + if (cinfo->num_components != 4) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + break; + + default: /* JCS_UNKNOWN can be anything */ + if (cinfo->num_components < 1) + ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); + break; + } + + /* Set out_color_components and conversion method based on requested space. + * Also clear the component_needed flags for any unused components, + * so that earlier pipeline stages can avoid useless computation. + */ + + switch (cinfo->out_color_space) { + case JCS_GRAYSCALE: + cinfo->out_color_components = 1; + if (cinfo->jpeg_color_space == JCS_GRAYSCALE || + cinfo->jpeg_color_space == JCS_YCbCr) { + cconvert->pub.color_convert = grayscale_convert; + /* For color->grayscale conversion, only the Y (0) component is needed */ + for (ci = 1; ci < cinfo->num_components; ci++) + cinfo->comp_info[ci].component_needed = FALSE; + } else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_RGB: + cinfo->out_color_components = RGB_PIXELSIZE; + if (cinfo->jpeg_color_space == JCS_YCbCr) { + cconvert->pub.color_convert = ycc_rgb_convert; + build_ycc_rgb_table(cinfo); + } else if (cinfo->jpeg_color_space == JCS_GRAYSCALE) { + cconvert->pub.color_convert = gray_rgb_convert; + } else if (cinfo->jpeg_color_space == JCS_RGB && RGB_PIXELSIZE == 3) { + cconvert->pub.color_convert = null_convert; + } else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + case JCS_CMYK: + cinfo->out_color_components = 4; + if (cinfo->jpeg_color_space == JCS_YCCK) { + cconvert->pub.color_convert = ycck_cmyk_convert; + build_ycc_rgb_table(cinfo); + } else if (cinfo->jpeg_color_space == JCS_CMYK) { + cconvert->pub.color_convert = null_convert; + } else + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + + default: + /* Permit null conversion to same output space */ + if (cinfo->out_color_space == cinfo->jpeg_color_space) { + cinfo->out_color_components = cinfo->num_components; + cconvert->pub.color_convert = null_convert; + } else /* unsupported non-null conversion */ + ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); + break; + } + + if (cinfo->quantize_colors) + cinfo->output_components = 1; /* single colormapped output component */ + else + cinfo->output_components = cinfo->out_color_components; +} diff --git a/Engine/lib/ljpeg/jdct.h b/Engine/lib/ljpeg/jdct.h new file mode 100644 index 000000000..04192a266 --- /dev/null +++ b/Engine/lib/ljpeg/jdct.h @@ -0,0 +1,176 @@ +/* + * jdct.h + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This include file contains common declarations for the forward and + * inverse DCT modules. These declarations are private to the DCT managers + * (jcdctmgr.c, jddctmgr.c) and the individual DCT algorithms. + * The individual DCT algorithms are kept in separate files to ease + * machine-dependent tuning (e.g., assembly coding). + */ + + +/* + * A forward DCT routine is given a pointer to a work area of type DCTELEM[]; + * the DCT is to be performed in-place in that buffer. Type DCTELEM is int + * for 8-bit samples, INT32 for 12-bit samples. (NOTE: Floating-point DCT + * implementations use an array of type FAST_FLOAT, instead.) + * The DCT inputs are expected to be signed (range +-CENTERJSAMPLE). + * The DCT outputs are returned scaled up by a factor of 8; they therefore + * have a range of +-8K for 8-bit data, +-128K for 12-bit data. This + * convention improves accuracy in integer implementations and saves some + * work in floating-point ones. + * Quantization of the output coefficients is done by jcdctmgr.c. + */ + +#if BITS_IN_JSAMPLE == 8 +typedef int DCTELEM; /* 16 or 32 bits is fine */ +#else +typedef INT32 DCTELEM; /* must have 32 bits */ +#endif + +typedef JMETHOD(void, forward_DCT_method_ptr, (DCTELEM * data)); +typedef JMETHOD(void, float_DCT_method_ptr, (FAST_FLOAT * data)); + + +/* + * An inverse DCT routine is given a pointer to the input JBLOCK and a pointer + * to an output sample array. The routine must dequantize the input data as + * well as perform the IDCT; for dequantization, it uses the multiplier table + * pointed to by compptr->dct_table. The output data is to be placed into the + * sample array starting at a specified column. (Any row offset needed will + * be applied to the array pointer before it is passed to the IDCT code.) + * Note that the number of samples emitted by the IDCT routine is + * DCT_scaled_size * DCT_scaled_size. + */ + +/* typedef inverse_DCT_method_ptr is declared in jpegint.h */ + +/* + * Each IDCT routine has its own ideas about the best dct_table element type. + */ + +typedef MULTIPLIER ISLOW_MULT_TYPE; /* short or int, whichever is faster */ +#if BITS_IN_JSAMPLE == 8 +typedef MULTIPLIER IFAST_MULT_TYPE; /* 16 bits is OK, use short if faster */ +#define IFAST_SCALE_BITS 2 /* fractional bits in scale factors */ +#else +typedef INT32 IFAST_MULT_TYPE; /* need 32 bits for scaled quantizers */ +#define IFAST_SCALE_BITS 13 /* fractional bits in scale factors */ +#endif +typedef FAST_FLOAT FLOAT_MULT_TYPE; /* preferred floating type */ + + +/* + * Each IDCT routine is responsible for range-limiting its results and + * converting them to unsigned form (0..MAXJSAMPLE). The raw outputs could + * be quite far out of range if the input data is corrupt, so a bulletproof + * range-limiting step is required. We use a mask-and-table-lookup method + * to do the combined operations quickly. See the comments with + * prepare_range_limit_table (in jdmaster.c) for more info. + */ + +#define IDCT_range_limit(cinfo) ((cinfo)->sample_range_limit + CENTERJSAMPLE) + +#define RANGE_MASK (MAXJSAMPLE * 4 + 3) /* 2 bits wider than legal samples */ + + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_fdct_islow jFDislow +#define jpeg_fdct_ifast jFDifast +#define jpeg_fdct_float jFDfloat +#define jpeg_idct_islow jRDislow +#define jpeg_idct_ifast jRDifast +#define jpeg_idct_float jRDfloat +#define jpeg_idct_4x4 jRD4x4 +#define jpeg_idct_2x2 jRD2x2 +#define jpeg_idct_1x1 jRD1x1 +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + +/* Extern declarations for the forward and inverse DCT routines. */ + +EXTERN(void) jpeg_fdct_islow JPP((DCTELEM * data)); +EXTERN(void) jpeg_fdct_ifast JPP((DCTELEM * data)); +EXTERN(void) jpeg_fdct_float JPP((FAST_FLOAT * data)); + +EXTERN(void) jpeg_idct_islow + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_ifast + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_float + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_4x4 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_2x2 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); +EXTERN(void) jpeg_idct_1x1 + JPP((j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col)); + + +/* + * Macros for handling fixed-point arithmetic; these are used by many + * but not all of the DCT/IDCT modules. + * + * All values are expected to be of type INT32. + * Fractional constants are scaled left by CONST_BITS bits. + * CONST_BITS is defined within each module using these macros, + * and may differ from one module to the next. + */ + +#define ONE ((INT32) 1) +#define CONST_SCALE (ONE << CONST_BITS) + +/* Convert a positive real constant to an integer scaled by CONST_SCALE. + * Caution: some C compilers fail to reduce "FIX(constant)" at compile time, + * thus causing a lot of useless floating-point operations at run time. + */ + +#define FIX(x) ((INT32) ((x) * CONST_SCALE + 0.5)) + +/* Descale and correctly round an INT32 value that's scaled by N bits. + * We assume RIGHT_SHIFT rounds towards minus infinity, so adding + * the fudge factor is correct for either sign of X. + */ + +#define DESCALE(x,n) RIGHT_SHIFT((x) + (ONE << ((n)-1)), n) + +/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result. + * This macro is used only when the two inputs will actually be no more than + * 16 bits wide, so that a 16x16->32 bit multiply can be used instead of a + * full 32x32 multiply. This provides a useful speedup on many machines. + * Unfortunately there is no way to specify a 16x16->32 multiply portably + * in C, but some C compilers will do the right thing if you provide the + * correct combination of casts. + */ + +#ifdef SHORTxSHORT_32 /* may work if 'int' is 32 bits */ +#define MULTIPLY16C16(var,const) (((INT16) (var)) * ((INT16) (const))) +#endif +#ifdef SHORTxLCONST_32 /* known to work with Microsoft C 6.0 */ +#define MULTIPLY16C16(var,const) (((INT16) (var)) * ((INT32) (const))) +#endif + +#ifndef MULTIPLY16C16 /* default definition */ +#define MULTIPLY16C16(var,const) ((var) * (const)) +#endif + +/* Same except both inputs are variables. */ + +#ifdef SHORTxSHORT_32 /* may work if 'int' is 32 bits */ +#define MULTIPLY16V16(var1,var2) (((INT16) (var1)) * ((INT16) (var2))) +#endif + +#ifndef MULTIPLY16V16 /* default definition */ +#define MULTIPLY16V16(var1,var2) ((var1) * (var2)) +#endif diff --git a/Engine/lib/ljpeg/jddctmgr.c b/Engine/lib/ljpeg/jddctmgr.c new file mode 100644 index 000000000..bbf8d0e92 --- /dev/null +++ b/Engine/lib/ljpeg/jddctmgr.c @@ -0,0 +1,269 @@ +/* + * jddctmgr.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the inverse-DCT management logic. + * This code selects a particular IDCT implementation to be used, + * and it performs related housekeeping chores. No code in this file + * is executed per IDCT step, only during output pass setup. + * + * Note that the IDCT routines are responsible for performing coefficient + * dequantization as well as the IDCT proper. This module sets up the + * dequantization multiplier table needed by the IDCT routine. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + + +/* + * The decompressor input side (jdinput.c) saves away the appropriate + * quantization table for each component at the start of the first scan + * involving that component. (This is necessary in order to correctly + * decode files that reuse Q-table slots.) + * When we are ready to make an output pass, the saved Q-table is converted + * to a multiplier table that will actually be used by the IDCT routine. + * The multiplier table contents are IDCT-method-dependent. To support + * application changes in IDCT method between scans, we can remake the + * multiplier tables if necessary. + * In buffered-image mode, the first output pass may occur before any data + * has been seen for some components, and thus before their Q-tables have + * been saved away. To handle this case, multiplier tables are preset + * to zeroes; the result of the IDCT will be a neutral gray level. + */ + + +/* Private subobject for this module */ + +typedef struct { + struct jpeg_inverse_dct pub; /* public fields */ + + /* This array contains the IDCT method code that each multiplier table + * is currently set up for, or -1 if it's not yet set up. + * The actual multiplier tables are pointed to by dct_table in the + * per-component comp_info structures. + */ + int cur_method[MAX_COMPONENTS]; +} my_idct_controller; + +typedef my_idct_controller * my_idct_ptr; + + +/* Allocated multiplier tables: big enough for any supported variant */ + +typedef union { + ISLOW_MULT_TYPE islow_array[DCTSIZE2]; +#ifdef DCT_IFAST_SUPPORTED + IFAST_MULT_TYPE ifast_array[DCTSIZE2]; +#endif +#ifdef DCT_FLOAT_SUPPORTED + FLOAT_MULT_TYPE float_array[DCTSIZE2]; +#endif +} multiplier_table; + + +/* The current scaled-IDCT routines require ISLOW-style multiplier tables, + * so be sure to compile that code if either ISLOW or SCALING is requested. + */ +#ifdef DCT_ISLOW_SUPPORTED +#define PROVIDE_ISLOW_TABLES +#else +#ifdef IDCT_SCALING_SUPPORTED +#define PROVIDE_ISLOW_TABLES +#endif +#endif + + +/* + * Prepare for an output pass. + * Here we select the proper IDCT routine for each component and build + * a matching multiplier table. + */ + +METHODDEF(void) +start_pass (j_decompress_ptr cinfo) +{ + my_idct_ptr idct = (my_idct_ptr) cinfo->idct; + int ci, i; + jpeg_component_info *compptr; + int method = 0; + inverse_DCT_method_ptr method_ptr = NULL; + JQUANT_TBL * qtbl; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Select the proper IDCT routine for this component's scaling */ + switch (compptr->DCT_scaled_size) { +#ifdef IDCT_SCALING_SUPPORTED + case 1: + method_ptr = jpeg_idct_1x1; + method = JDCT_ISLOW; /* jidctred uses islow-style table */ + break; + case 2: + method_ptr = jpeg_idct_2x2; + method = JDCT_ISLOW; /* jidctred uses islow-style table */ + break; + case 4: + method_ptr = jpeg_idct_4x4; + method = JDCT_ISLOW; /* jidctred uses islow-style table */ + break; +#endif + case DCTSIZE: + switch (cinfo->dct_method) { +#ifdef DCT_ISLOW_SUPPORTED + case JDCT_ISLOW: + method_ptr = jpeg_idct_islow; + method = JDCT_ISLOW; + break; +#endif +#ifdef DCT_IFAST_SUPPORTED + case JDCT_IFAST: + method_ptr = jpeg_idct_ifast; + method = JDCT_IFAST; + break; +#endif +#ifdef DCT_FLOAT_SUPPORTED + case JDCT_FLOAT: + method_ptr = jpeg_idct_float; + method = JDCT_FLOAT; + break; +#endif + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + break; + } + break; + default: + ERREXIT1(cinfo, JERR_BAD_DCTSIZE, compptr->DCT_scaled_size); + break; + } + idct->pub.inverse_DCT[ci] = method_ptr; + /* Create multiplier table from quant table. + * However, we can skip this if the component is uninteresting + * or if we already built the table. Also, if no quant table + * has yet been saved for the component, we leave the + * multiplier table all-zero; we'll be reading zeroes from the + * coefficient controller's buffer anyway. + */ + if (! compptr->component_needed || idct->cur_method[ci] == method) + continue; + qtbl = compptr->quant_table; + if (qtbl == NULL) /* happens if no data yet for component */ + continue; + idct->cur_method[ci] = method; + switch (method) { +#ifdef PROVIDE_ISLOW_TABLES + case JDCT_ISLOW: + { + /* For LL&M IDCT method, multipliers are equal to raw quantization + * coefficients, but are stored as ints to ensure access efficiency. + */ + ISLOW_MULT_TYPE * ismtbl = (ISLOW_MULT_TYPE *) compptr->dct_table; + for (i = 0; i < DCTSIZE2; i++) { + ismtbl[i] = (ISLOW_MULT_TYPE) qtbl->quantval[i]; + } + } + break; +#endif +#ifdef DCT_IFAST_SUPPORTED + case JDCT_IFAST: + { + /* For AA&N IDCT method, multipliers are equal to quantization + * coefficients scaled by scalefactor[row]*scalefactor[col], where + * scalefactor[0] = 1 + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + * For integer operation, the multiplier table is to be scaled by + * IFAST_SCALE_BITS. + */ + IFAST_MULT_TYPE * ifmtbl = (IFAST_MULT_TYPE *) compptr->dct_table; +#define CONST_BITS 14 + static const INT16 aanscales[DCTSIZE2] = { + /* precomputed values scaled up by 14 bits */ + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + 22725, 31521, 29692, 26722, 22725, 17855, 12299, 6270, + 21407, 29692, 27969, 25172, 21407, 16819, 11585, 5906, + 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, + 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, + 12873, 17855, 16819, 15137, 12873, 10114, 6967, 3552, + 8867, 12299, 11585, 10426, 8867, 6967, 4799, 2446, + 4520, 6270, 5906, 5315, 4520, 3552, 2446, 1247 + }; + SHIFT_TEMPS + + for (i = 0; i < DCTSIZE2; i++) { + ifmtbl[i] = (IFAST_MULT_TYPE) + DESCALE(MULTIPLY16V16((INT32) qtbl->quantval[i], + (INT32) aanscales[i]), + CONST_BITS-IFAST_SCALE_BITS); + } + } + break; +#endif +#ifdef DCT_FLOAT_SUPPORTED + case JDCT_FLOAT: + { + /* For float AA&N IDCT method, multipliers are equal to quantization + * coefficients scaled by scalefactor[row]*scalefactor[col], where + * scalefactor[0] = 1 + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + */ + FLOAT_MULT_TYPE * fmtbl = (FLOAT_MULT_TYPE *) compptr->dct_table; + int row, col; + static const double aanscalefactor[DCTSIZE] = { + 1.0, 1.387039845, 1.306562965, 1.175875602, + 1.0, 0.785694958, 0.541196100, 0.275899379 + }; + + i = 0; + for (row = 0; row < DCTSIZE; row++) { + for (col = 0; col < DCTSIZE; col++) { + fmtbl[i] = (FLOAT_MULT_TYPE) + ((double) qtbl->quantval[i] * + aanscalefactor[row] * aanscalefactor[col]); + i++; + } + } + } + break; +#endif + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + break; + } + } +} + + +/* + * Initialize IDCT manager. + */ + +GLOBAL(void) +jinit_inverse_dct (j_decompress_ptr cinfo) +{ + my_idct_ptr idct; + int ci; + jpeg_component_info *compptr; + + idct = (my_idct_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_idct_controller)); + cinfo->idct = (struct jpeg_inverse_dct *) idct; + idct->pub.start_pass = start_pass; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Allocate and pre-zero a multiplier table for each component */ + compptr->dct_table = + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(multiplier_table)); + MEMZERO(compptr->dct_table, SIZEOF(multiplier_table)); + /* Mark multiplier table not yet set up for any method */ + idct->cur_method[ci] = -1; + } +} diff --git a/Engine/lib/ljpeg/jdhuff.c b/Engine/lib/ljpeg/jdhuff.c new file mode 100644 index 000000000..b5ba39f73 --- /dev/null +++ b/Engine/lib/ljpeg/jdhuff.c @@ -0,0 +1,651 @@ +/* + * jdhuff.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains Huffman entropy decoding routines. + * + * Much of the complexity here has to do with supporting input suspension. + * If the data source module demands suspension, we want to be able to back + * up to the start of the current MCU. To do this, we copy state variables + * into local working storage, and update them back to the permanent + * storage only upon successful completion of an MCU. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdhuff.h" /* Declarations shared with jdphuff.c */ + + +/* + * Expanded entropy decoder object for Huffman decoding. + * + * The savable_state subrecord contains fields that change within an MCU, + * but must not be updated permanently until we complete the MCU. + */ + +typedef struct { + int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ +} savable_state; + +/* This macro is to work around compilers with missing or broken + * structure assignment. You'll need to fix this code if you have + * such a compiler and you change MAX_COMPS_IN_SCAN. + */ + +#ifndef NO_STRUCT_ASSIGN +#define ASSIGN_STATE(dest,src) ((dest) = (src)) +#else +#if MAX_COMPS_IN_SCAN == 4 +#define ASSIGN_STATE(dest,src) \ + ((dest).last_dc_val[0] = (src).last_dc_val[0], \ + (dest).last_dc_val[1] = (src).last_dc_val[1], \ + (dest).last_dc_val[2] = (src).last_dc_val[2], \ + (dest).last_dc_val[3] = (src).last_dc_val[3]) +#endif +#endif + + +typedef struct { + struct jpeg_entropy_decoder pub; /* public fields */ + + /* These fields are loaded into local variables at start of each MCU. + * In case of suspension, we exit WITHOUT updating them. + */ + bitread_perm_state bitstate; /* Bit buffer at start of MCU */ + savable_state saved; /* Other state at start of MCU */ + + /* These fields are NOT loaded into local working state. */ + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + + /* Pointers to derived tables (these workspaces have image lifespan) */ + d_derived_tbl * dc_derived_tbls[NUM_HUFF_TBLS]; + d_derived_tbl * ac_derived_tbls[NUM_HUFF_TBLS]; + + /* Precalculated info set up by start_pass for use in decode_mcu: */ + + /* Pointers to derived tables to be used for each block within an MCU */ + d_derived_tbl * dc_cur_tbls[D_MAX_BLOCKS_IN_MCU]; + d_derived_tbl * ac_cur_tbls[D_MAX_BLOCKS_IN_MCU]; + /* Whether we care about the DC and AC coefficient values for each block */ + boolean dc_needed[D_MAX_BLOCKS_IN_MCU]; + boolean ac_needed[D_MAX_BLOCKS_IN_MCU]; +} huff_entropy_decoder; + +typedef huff_entropy_decoder * huff_entropy_ptr; + + +/* + * Initialize for a Huffman-compressed scan. + */ + +METHODDEF(void) +start_pass_huff_decoder (j_decompress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci, blkn, dctbl, actbl; + jpeg_component_info * compptr; + + /* Check that the scan parameters Ss, Se, Ah/Al are OK for sequential JPEG. + * This ought to be an error condition, but we make it a warning because + * there are some baseline files out there with all zeroes in these bytes. + */ + if (cinfo->Ss != 0 || cinfo->Se != DCTSIZE2-1 || + cinfo->Ah != 0 || cinfo->Al != 0) + WARNMS(cinfo, JWRN_NOT_SEQUENTIAL); + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + dctbl = compptr->dc_tbl_no; + actbl = compptr->ac_tbl_no; + /* Compute derived values for Huffman tables */ + /* We may do this more than once for a table, but it's not expensive */ + jpeg_make_d_derived_tbl(cinfo, TRUE, dctbl, + & entropy->dc_derived_tbls[dctbl]); + jpeg_make_d_derived_tbl(cinfo, FALSE, actbl, + & entropy->ac_derived_tbls[actbl]); + /* Initialize DC predictions to 0 */ + entropy->saved.last_dc_val[ci] = 0; + } + + /* Precalculate decoding info for each block in an MCU of this scan */ + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + /* Precalculate which table to use for each block */ + entropy->dc_cur_tbls[blkn] = entropy->dc_derived_tbls[compptr->dc_tbl_no]; + entropy->ac_cur_tbls[blkn] = entropy->ac_derived_tbls[compptr->ac_tbl_no]; + /* Decide whether we really care about the coefficient values */ + if (compptr->component_needed) { + entropy->dc_needed[blkn] = TRUE; + /* we don't need the ACs if producing a 1/8th-size image */ + entropy->ac_needed[blkn] = (compptr->DCT_scaled_size > 1); + } else { + entropy->dc_needed[blkn] = entropy->ac_needed[blkn] = FALSE; + } + } + + /* Initialize bitread state variables */ + entropy->bitstate.bits_left = 0; + entropy->bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */ + entropy->pub.insufficient_data = FALSE; + + /* Initialize restart counter */ + entropy->restarts_to_go = cinfo->restart_interval; +} + + +/* + * Compute the derived values for a Huffman table. + * This routine also performs some validation checks on the table. + * + * Note this is also used by jdphuff.c. + */ + +GLOBAL(void) +jpeg_make_d_derived_tbl (j_decompress_ptr cinfo, boolean isDC, int tblno, + d_derived_tbl ** pdtbl) +{ + JHUFF_TBL *htbl; + d_derived_tbl *dtbl; + int p, i, l, si, numsymbols; + int lookbits, ctr; + char huffsize[257]; + unsigned int huffcode[257]; + unsigned int code; + + /* Note that huffsize[] and huffcode[] are filled in code-length order, + * paralleling the order of the symbols themselves in htbl->huffval[]. + */ + + /* Find the input Huffman table */ + if (tblno < 0 || tblno >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tblno); + htbl = + isDC ? cinfo->dc_huff_tbl_ptrs[tblno] : cinfo->ac_huff_tbl_ptrs[tblno]; + if (htbl == NULL) + ERREXIT1(cinfo, JERR_NO_HUFF_TABLE, tblno); + + /* Allocate a workspace if we haven't already done so. */ + if (*pdtbl == NULL) + *pdtbl = (d_derived_tbl *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(d_derived_tbl)); + dtbl = *pdtbl; + dtbl->pub = htbl; /* fill in back link */ + + /* Figure C.1: make table of Huffman code length for each symbol */ + + p = 0; + for (l = 1; l <= 16; l++) { + i = (int) htbl->bits[l]; + if (i < 0 || p + i > 256) /* protect against table overrun */ + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + while (i--) + huffsize[p++] = (char) l; + } + huffsize[p] = 0; + numsymbols = p; + + /* Figure C.2: generate the codes themselves */ + /* We also validate that the counts represent a legal Huffman code tree. */ + + code = 0; + si = huffsize[0]; + p = 0; + while (huffsize[p]) { + while (((int) huffsize[p]) == si) { + huffcode[p++] = code; + code++; + } + /* code is now 1 more than the last code used for codelength si; but + * it must still fit in si bits, since no code is allowed to be all ones. + */ + if (((INT32) code) >= (((INT32) 1) << si)) + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + code <<= 1; + si++; + } + + /* Figure F.15: generate decoding tables for bit-sequential decoding */ + + p = 0; + for (l = 1; l <= 16; l++) { + if (htbl->bits[l]) { + /* valoffset[l] = huffval[] index of 1st symbol of code length l, + * minus the minimum code of length l + */ + dtbl->valoffset[l] = (INT32) p - (INT32) huffcode[p]; + p += htbl->bits[l]; + dtbl->maxcode[l] = huffcode[p-1]; /* maximum code of length l */ + } else { + dtbl->maxcode[l] = -1; /* -1 if no codes of this length */ + } + } + dtbl->maxcode[17] = 0xFFFFFL; /* ensures jpeg_huff_decode terminates */ + + /* Compute lookahead tables to speed up decoding. + * First we set all the table entries to 0, indicating "too long"; + * then we iterate through the Huffman codes that are short enough and + * fill in all the entries that correspond to bit sequences starting + * with that code. + */ + + MEMZERO(dtbl->look_nbits, SIZEOF(dtbl->look_nbits)); + + p = 0; + for (l = 1; l <= HUFF_LOOKAHEAD; l++) { + for (i = 1; i <= (int) htbl->bits[l]; i++, p++) { + /* l = current code's length, p = its index in huffcode[] & huffval[]. */ + /* Generate left-justified code followed by all possible bit sequences */ + lookbits = huffcode[p] << (HUFF_LOOKAHEAD-l); + for (ctr = 1 << (HUFF_LOOKAHEAD-l); ctr > 0; ctr--) { + dtbl->look_nbits[lookbits] = l; + dtbl->look_sym[lookbits] = htbl->huffval[p]; + lookbits++; + } + } + } + + /* Validate symbols as being reasonable. + * For AC tables, we make no check, but accept all byte values 0..255. + * For DC tables, we require the symbols to be in range 0..15. + * (Tighter bounds could be applied depending on the data depth and mode, + * but this is sufficient to ensure safe decoding.) + */ + if (isDC) { + for (i = 0; i < numsymbols; i++) { + int sym = htbl->huffval[i]; + if (sym < 0 || sym > 15) + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + } + } +} + + +/* + * Out-of-line code for bit fetching (shared with jdphuff.c). + * See jdhuff.h for info about usage. + * Note: current values of get_buffer and bits_left are passed as parameters, + * but are returned in the corresponding fields of the state struct. + * + * On most machines MIN_GET_BITS should be 25 to allow the full 32-bit width + * of get_buffer to be used. (On machines with wider words, an even larger + * buffer could be used.) However, on some machines 32-bit shifts are + * quite slow and take time proportional to the number of places shifted. + * (This is true with most PC compilers, for instance.) In this case it may + * be a win to set MIN_GET_BITS to the minimum value of 15. This reduces the + * average shift distance at the cost of more calls to jpeg_fill_bit_buffer. + */ + +#ifdef SLOW_SHIFT_32 +#define MIN_GET_BITS 15 /* minimum allowable value */ +#else +#define MIN_GET_BITS (BIT_BUF_SIZE-7) +#endif + + +GLOBAL(boolean) +jpeg_fill_bit_buffer (bitread_working_state * state, + register bit_buf_type get_buffer, register int bits_left, + int nbits) +/* Load up the bit buffer to a depth of at least nbits */ +{ + /* Copy heavily used state fields into locals (hopefully registers) */ + register const JOCTET * next_input_byte = state->next_input_byte; + register size_t bytes_in_buffer = state->bytes_in_buffer; + j_decompress_ptr cinfo = state->cinfo; + + /* Attempt to load at least MIN_GET_BITS bits into get_buffer. */ + /* (It is assumed that no request will be for more than that many bits.) */ + /* We fail to do so only if we hit a marker or are forced to suspend. */ + + if (cinfo->unread_marker == 0) { /* cannot advance past a marker */ + while (bits_left < MIN_GET_BITS) { + register int c; + + /* Attempt to read a byte */ + if (bytes_in_buffer == 0) { + if (! (*cinfo->src->fill_input_buffer) (cinfo)) + return FALSE; + next_input_byte = cinfo->src->next_input_byte; + bytes_in_buffer = cinfo->src->bytes_in_buffer; + } + bytes_in_buffer--; + c = GETJOCTET(*next_input_byte++); + + /* If it's 0xFF, check and discard stuffed zero byte */ + if (c == 0xFF) { + /* Loop here to discard any padding FF's on terminating marker, + * so that we can save a valid unread_marker value. NOTE: we will + * accept multiple FF's followed by a 0 as meaning a single FF data + * byte. This data pattern is not valid according to the standard. + */ + do { + if (bytes_in_buffer == 0) { + if (! (*cinfo->src->fill_input_buffer) (cinfo)) + return FALSE; + next_input_byte = cinfo->src->next_input_byte; + bytes_in_buffer = cinfo->src->bytes_in_buffer; + } + bytes_in_buffer--; + c = GETJOCTET(*next_input_byte++); + } while (c == 0xFF); + + if (c == 0) { + /* Found FF/00, which represents an FF data byte */ + c = 0xFF; + } else { + /* Oops, it's actually a marker indicating end of compressed data. + * Save the marker code for later use. + * Fine point: it might appear that we should save the marker into + * bitread working state, not straight into permanent state. But + * once we have hit a marker, we cannot need to suspend within the + * current MCU, because we will read no more bytes from the data + * source. So it is OK to update permanent state right away. + */ + cinfo->unread_marker = c; + /* See if we need to insert some fake zero bits. */ + goto no_more_bytes; + } + } + + /* OK, load c into get_buffer */ + get_buffer = (get_buffer << 8) | c; + bits_left += 8; + } /* end while */ + } else { + no_more_bytes: + /* We get here if we've read the marker that terminates the compressed + * data segment. There should be enough bits in the buffer register + * to satisfy the request; if so, no problem. + */ + if (nbits > bits_left) { + /* Uh-oh. Report corrupted data to user and stuff zeroes into + * the data stream, so that we can produce some kind of image. + * We use a nonvolatile flag to ensure that only one warning message + * appears per data segment. + */ + if (! cinfo->entropy->insufficient_data) { + WARNMS(cinfo, JWRN_HIT_MARKER); + cinfo->entropy->insufficient_data = TRUE; + } + /* Fill the buffer with zero bits */ + get_buffer <<= MIN_GET_BITS - bits_left; + bits_left = MIN_GET_BITS; + } + } + + /* Unload the local registers */ + state->next_input_byte = next_input_byte; + state->bytes_in_buffer = bytes_in_buffer; + state->get_buffer = get_buffer; + state->bits_left = bits_left; + + return TRUE; +} + + +/* + * Out-of-line code for Huffman code decoding. + * See jdhuff.h for info about usage. + */ + +GLOBAL(int) +jpeg_huff_decode (bitread_working_state * state, + register bit_buf_type get_buffer, register int bits_left, + d_derived_tbl * htbl, int min_bits) +{ + register int l = min_bits; + register INT32 code; + + /* HUFF_DECODE has determined that the code is at least min_bits */ + /* bits long, so fetch that many bits in one swoop. */ + + CHECK_BIT_BUFFER(*state, l, return -1); + code = GET_BITS(l); + + /* Collect the rest of the Huffman code one bit at a time. */ + /* This is per Figure F.16 in the JPEG spec. */ + + while (code > htbl->maxcode[l]) { + code <<= 1; + CHECK_BIT_BUFFER(*state, 1, return -1); + code |= GET_BITS(1); + l++; + } + + /* Unload the local registers */ + state->get_buffer = get_buffer; + state->bits_left = bits_left; + + /* With garbage input we may reach the sentinel value l = 17. */ + + if (l > 16) { + WARNMS(state->cinfo, JWRN_HUFF_BAD_CODE); + return 0; /* fake a zero as the safest result */ + } + + return htbl->pub->huffval[ (int) (code + htbl->valoffset[l]) ]; +} + + +/* + * Figure F.12: extend sign bit. + * On some machines, a shift and add will be faster than a table lookup. + */ + +#ifdef AVOID_TABLES + +#define HUFF_EXTEND(x,s) ((x) < (1<<((s)-1)) ? (x) + (((-1)<<(s)) + 1) : (x)) + +#else + +#define HUFF_EXTEND(x,s) ((x) < extend_test[s] ? (x) + extend_offset[s] : (x)) + +static const int extend_test[16] = /* entry n is 2**(n-1) */ + { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, + 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 }; + +static const int extend_offset[16] = /* entry n is (-1 << n) + 1 */ + { 0, ((-1)<<1) + 1, ((-1)<<2) + 1, ((-1)<<3) + 1, ((-1)<<4) + 1, + ((-1)<<5) + 1, ((-1)<<6) + 1, ((-1)<<7) + 1, ((-1)<<8) + 1, + ((-1)<<9) + 1, ((-1)<<10) + 1, ((-1)<<11) + 1, ((-1)<<12) + 1, + ((-1)<<13) + 1, ((-1)<<14) + 1, ((-1)<<15) + 1 }; + +#endif /* AVOID_TABLES */ + + +/* + * Check for a restart marker & resynchronize decoder. + * Returns FALSE if must suspend. + */ + +LOCAL(boolean) +process_restart (j_decompress_ptr cinfo) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int ci; + + /* Throw away any unused bits remaining in bit buffer; */ + /* include any full bytes in next_marker's count of discarded bytes */ + cinfo->marker->discarded_bytes += entropy->bitstate.bits_left / 8; + entropy->bitstate.bits_left = 0; + + /* Advance past the RSTn marker */ + if (! (*cinfo->marker->read_restart_marker) (cinfo)) + return FALSE; + + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) + entropy->saved.last_dc_val[ci] = 0; + + /* Reset restart counter */ + entropy->restarts_to_go = cinfo->restart_interval; + + /* Reset out-of-data flag, unless read_restart_marker left us smack up + * against a marker. In that case we will end up treating the next data + * segment as empty, and we can avoid producing bogus output pixels by + * leaving the flag set. + */ + if (cinfo->unread_marker == 0) + entropy->pub.insufficient_data = FALSE; + + return TRUE; +} + + +/* + * Decode and return one MCU's worth of Huffman-compressed coefficients. + * The coefficients are reordered from zigzag order into natural array order, + * but are not dequantized. + * + * The i'th block of the MCU is stored into the block pointed to by + * MCU_data[i]. WE ASSUME THIS AREA HAS BEEN ZEROED BY THE CALLER. + * (Wholesale zeroing is usually a little faster than retail...) + * + * Returns FALSE if data source requested suspension. In that case no + * changes have been made to permanent state. (Exception: some output + * coefficients may already have been assigned. This is harmless for + * this module, since we'll just re-assign them on the next call.) + */ + +METHODDEF(boolean) +decode_mcu (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; + int blkn; + BITREAD_STATE_VARS; + savable_state state; + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! process_restart(cinfo)) + return FALSE; + } + + /* If we've run out of data, just leave the MCU set to zeroes. + * This way, we return uniform gray for the remainder of the segment. + */ + if (! entropy->pub.insufficient_data) { + + /* Load up working state */ + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(state, entropy->saved); + + /* Outer loop handles each block in the MCU */ + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + JBLOCKROW block = MCU_data[blkn]; + d_derived_tbl * dctbl = entropy->dc_cur_tbls[blkn]; + d_derived_tbl * actbl = entropy->ac_cur_tbls[blkn]; + register int s, k, r; + + /* Decode a single block's worth of coefficients */ + + /* Section F.2.2.1: decode the DC coefficient difference */ + HUFF_DECODE(s, br_state, dctbl, return FALSE, label1); + if (s) { + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + } + + if (entropy->dc_needed[blkn]) { + /* Convert DC difference to actual value, update last_dc_val */ + int ci = cinfo->MCU_membership[blkn]; + s += state.last_dc_val[ci]; + state.last_dc_val[ci] = s; + /* Output the DC coefficient (assumes jpeg_natural_order[0] = 0) */ + (*block)[0] = (JCOEF) s; + } + + if (entropy->ac_needed[blkn]) { + + /* Section F.2.2.2: decode the AC coefficients */ + /* Since zeroes are skipped, output area must be cleared beforehand */ + for (k = 1; k < DCTSIZE2; k++) { + HUFF_DECODE(s, br_state, actbl, return FALSE, label2); + + r = s >> 4; + s &= 15; + + if (s) { + k += r; + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + /* Output coefficient in natural (dezigzagged) order. + * Note: the extra entries in jpeg_natural_order[] will save us + * if k >= DCTSIZE2, which could happen if the data is corrupted. + */ + (*block)[jpeg_natural_order[k]] = (JCOEF) s; + } else { + if (r != 15) + break; + k += 15; + } + } + + } else { + + /* Section F.2.2.2: decode the AC coefficients */ + /* In this path we just discard the values */ + for (k = 1; k < DCTSIZE2; k++) { + HUFF_DECODE(s, br_state, actbl, return FALSE, label3); + + r = s >> 4; + s &= 15; + + if (s) { + k += r; + CHECK_BIT_BUFFER(br_state, s, return FALSE); + DROP_BITS(s); + } else { + if (r != 15) + break; + k += 15; + } + } + + } + } + + /* Completed MCU, so update state */ + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(entropy->saved, state); + } + + /* Account for restart interval (no-op if not using restarts) */ + entropy->restarts_to_go--; + + return TRUE; +} + + +/* + * Module initialization routine for Huffman entropy decoding. + */ + +GLOBAL(void) +jinit_huff_decoder (j_decompress_ptr cinfo) +{ + huff_entropy_ptr entropy; + int i; + + entropy = (huff_entropy_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(huff_entropy_decoder)); + cinfo->entropy = (struct jpeg_entropy_decoder *) entropy; + entropy->pub.start_pass = start_pass_huff_decoder; + entropy->pub.decode_mcu = decode_mcu; + + /* Mark tables unallocated */ + for (i = 0; i < NUM_HUFF_TBLS; i++) { + entropy->dc_derived_tbls[i] = entropy->ac_derived_tbls[i] = NULL; + } +} diff --git a/Engine/lib/ljpeg/jdhuff.h b/Engine/lib/ljpeg/jdhuff.h new file mode 100644 index 000000000..ae19b6caf --- /dev/null +++ b/Engine/lib/ljpeg/jdhuff.h @@ -0,0 +1,201 @@ +/* + * jdhuff.h + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains declarations for Huffman entropy decoding routines + * that are shared between the sequential decoder (jdhuff.c) and the + * progressive decoder (jdphuff.c). No other modules need to see these. + */ + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_make_d_derived_tbl jMkDDerived +#define jpeg_fill_bit_buffer jFilBitBuf +#define jpeg_huff_decode jHufDecode +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Derived data constructed for each Huffman table */ + +#define HUFF_LOOKAHEAD 8 /* # of bits of lookahead */ + +typedef struct { + /* Basic tables: (element [0] of each array is unused) */ + INT32 maxcode[18]; /* largest code of length k (-1 if none) */ + /* (maxcode[17] is a sentinel to ensure jpeg_huff_decode terminates) */ + INT32 valoffset[17]; /* huffval[] offset for codes of length k */ + /* valoffset[k] = huffval[] index of 1st symbol of code length k, less + * the smallest code of length k; so given a code of length k, the + * corresponding symbol is huffval[code + valoffset[k]] + */ + + /* Link to public Huffman table (needed only in jpeg_huff_decode) */ + JHUFF_TBL *pub; + + /* Lookahead tables: indexed by the next HUFF_LOOKAHEAD bits of + * the input data stream. If the next Huffman code is no more + * than HUFF_LOOKAHEAD bits long, we can obtain its length and + * the corresponding symbol directly from these tables. + */ + int look_nbits[1< 32 bits on your machine, and shifting/masking longs is + * reasonably fast, making bit_buf_type be long and setting BIT_BUF_SIZE + * appropriately should be a win. Unfortunately we can't define the size + * with something like #define BIT_BUF_SIZE (sizeof(bit_buf_type)*8) + * because not all machines measure sizeof in 8-bit bytes. + */ + +typedef struct { /* Bitreading state saved across MCUs */ + bit_buf_type get_buffer; /* current bit-extraction buffer */ + int bits_left; /* # of unused bits in it */ +} bitread_perm_state; + +typedef struct { /* Bitreading working state within an MCU */ + /* Current data source location */ + /* We need a copy, rather than munging the original, in case of suspension */ + const JOCTET * next_input_byte; /* => next byte to read from source */ + size_t bytes_in_buffer; /* # of bytes remaining in source buffer */ + /* Bit input buffer --- note these values are kept in register variables, + * not in this struct, inside the inner loops. + */ + bit_buf_type get_buffer; /* current bit-extraction buffer */ + int bits_left; /* # of unused bits in it */ + /* Pointer needed by jpeg_fill_bit_buffer. */ + j_decompress_ptr cinfo; /* back link to decompress master record */ +} bitread_working_state; + +/* Macros to declare and load/save bitread local variables. */ +#define BITREAD_STATE_VARS \ + register bit_buf_type get_buffer; \ + register int bits_left; \ + bitread_working_state br_state + +#define BITREAD_LOAD_STATE(cinfop,permstate) \ + br_state.cinfo = cinfop; \ + br_state.next_input_byte = cinfop->src->next_input_byte; \ + br_state.bytes_in_buffer = cinfop->src->bytes_in_buffer; \ + get_buffer = permstate.get_buffer; \ + bits_left = permstate.bits_left; + +#define BITREAD_SAVE_STATE(cinfop,permstate) \ + cinfop->src->next_input_byte = br_state.next_input_byte; \ + cinfop->src->bytes_in_buffer = br_state.bytes_in_buffer; \ + permstate.get_buffer = get_buffer; \ + permstate.bits_left = bits_left + +/* + * These macros provide the in-line portion of bit fetching. + * Use CHECK_BIT_BUFFER to ensure there are N bits in get_buffer + * before using GET_BITS, PEEK_BITS, or DROP_BITS. + * The variables get_buffer and bits_left are assumed to be locals, + * but the state struct might not be (jpeg_huff_decode needs this). + * CHECK_BIT_BUFFER(state,n,action); + * Ensure there are N bits in get_buffer; if suspend, take action. + * val = GET_BITS(n); + * Fetch next N bits. + * val = PEEK_BITS(n); + * Fetch next N bits without removing them from the buffer. + * DROP_BITS(n); + * Discard next N bits. + * The value N should be a simple variable, not an expression, because it + * is evaluated multiple times. + */ + +#define CHECK_BIT_BUFFER(state,nbits,action) \ + { if (bits_left < (nbits)) { \ + if (! jpeg_fill_bit_buffer(&(state),get_buffer,bits_left,nbits)) \ + { action; } \ + get_buffer = (state).get_buffer; bits_left = (state).bits_left; } } + +#define GET_BITS(nbits) \ + (((int) (get_buffer >> (bits_left -= (nbits)))) & ((1<<(nbits))-1)) + +#define PEEK_BITS(nbits) \ + (((int) (get_buffer >> (bits_left - (nbits)))) & ((1<<(nbits))-1)) + +#define DROP_BITS(nbits) \ + (bits_left -= (nbits)) + +/* Load up the bit buffer to a depth of at least nbits */ +EXTERN(boolean) jpeg_fill_bit_buffer + JPP((bitread_working_state * state, register bit_buf_type get_buffer, + register int bits_left, int nbits)); + + +/* + * Code for extracting next Huffman-coded symbol from input bit stream. + * Again, this is time-critical and we make the main paths be macros. + * + * We use a lookahead table to process codes of up to HUFF_LOOKAHEAD bits + * without looping. Usually, more than 95% of the Huffman codes will be 8 + * or fewer bits long. The few overlength codes are handled with a loop, + * which need not be inline code. + * + * Notes about the HUFF_DECODE macro: + * 1. Near the end of the data segment, we may fail to get enough bits + * for a lookahead. In that case, we do it the hard way. + * 2. If the lookahead table contains no entry, the next code must be + * more than HUFF_LOOKAHEAD bits long. + * 3. jpeg_huff_decode returns -1 if forced to suspend. + */ + +#define HUFF_DECODE(result,state,htbl,failaction,slowlabel) \ +{ register int nb, look; \ + if (bits_left < HUFF_LOOKAHEAD) { \ + if (! jpeg_fill_bit_buffer(&state,get_buffer,bits_left, 0)) {failaction;} \ + get_buffer = state.get_buffer; bits_left = state.bits_left; \ + if (bits_left < HUFF_LOOKAHEAD) { \ + nb = 1; goto slowlabel; \ + } \ + } \ + look = PEEK_BITS(HUFF_LOOKAHEAD); \ + if ((nb = htbl->look_nbits[look]) != 0) { \ + DROP_BITS(nb); \ + result = htbl->look_sym[look]; \ + } else { \ + nb = HUFF_LOOKAHEAD+1; \ +slowlabel: \ + if ((result=jpeg_huff_decode(&state,get_buffer,bits_left,htbl,nb)) < 0) \ + { failaction; } \ + get_buffer = state.get_buffer; bits_left = state.bits_left; \ + } \ +} + +/* Out-of-line case for Huffman code fetching */ +EXTERN(int) jpeg_huff_decode + JPP((bitread_working_state * state, register bit_buf_type get_buffer, + register int bits_left, d_derived_tbl * htbl, int min_bits)); diff --git a/Engine/lib/ljpeg/jdinput.c b/Engine/lib/ljpeg/jdinput.c new file mode 100644 index 000000000..0c2ac8f12 --- /dev/null +++ b/Engine/lib/ljpeg/jdinput.c @@ -0,0 +1,381 @@ +/* + * jdinput.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains input control logic for the JPEG decompressor. + * These routines are concerned with controlling the decompressor's input + * processing (marker reading and coefficient decoding). The actual input + * reading is done in jdmarker.c, jdhuff.c, and jdphuff.c. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Private state */ + +typedef struct { + struct jpeg_input_controller pub; /* public fields */ + + boolean inheaders; /* TRUE until first SOS is reached */ +} my_input_controller; + +typedef my_input_controller * my_inputctl_ptr; + + +/* Forward declarations */ +METHODDEF(int) consume_markers JPP((j_decompress_ptr cinfo)); + + +/* + * Routines to calculate various quantities related to the size of the image. + */ + +LOCAL(void) +initial_setup (j_decompress_ptr cinfo) +/* Called once, when first SOS marker is reached */ +{ + int ci; + jpeg_component_info *compptr; + + /* Make sure image isn't bigger than I can handle */ + if ((long) cinfo->image_height > (long) JPEG_MAX_DIMENSION || + (long) cinfo->image_width > (long) JPEG_MAX_DIMENSION) + ERREXIT1(cinfo, JERR_IMAGE_TOO_BIG, (unsigned int) JPEG_MAX_DIMENSION); + + /* For now, precision must match compiled-in value... */ + if (cinfo->data_precision != BITS_IN_JSAMPLE) + ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision); + + /* Check that number of components won't exceed internal array sizes */ + if (cinfo->num_components > MAX_COMPONENTS) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->num_components, + MAX_COMPONENTS); + + /* Compute maximum sampling factors; check factor validity */ + cinfo->max_h_samp_factor = 1; + cinfo->max_v_samp_factor = 1; + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + if (compptr->h_samp_factor<=0 || compptr->h_samp_factor>MAX_SAMP_FACTOR || + compptr->v_samp_factor<=0 || compptr->v_samp_factor>MAX_SAMP_FACTOR) + ERREXIT(cinfo, JERR_BAD_SAMPLING); + cinfo->max_h_samp_factor = MAX(cinfo->max_h_samp_factor, + compptr->h_samp_factor); + cinfo->max_v_samp_factor = MAX(cinfo->max_v_samp_factor, + compptr->v_samp_factor); + } + + /* We initialize DCT_scaled_size and min_DCT_scaled_size to DCTSIZE. + * In the full decompressor, this will be overridden by jdmaster.c; + * but in the transcoder, jdmaster.c is not used, so we must do it here. + */ + cinfo->min_DCT_scaled_size = DCTSIZE; + + /* Compute dimensions of components */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + compptr->DCT_scaled_size = DCTSIZE; + /* Size in DCT blocks */ + compptr->width_in_blocks = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, + (long) (cinfo->max_h_samp_factor * DCTSIZE)); + compptr->height_in_blocks = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, + (long) (cinfo->max_v_samp_factor * DCTSIZE)); + /* downsampled_width and downsampled_height will also be overridden by + * jdmaster.c if we are doing full decompression. The transcoder library + * doesn't use these values, but the calling application might. + */ + /* Size in samples */ + compptr->downsampled_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * (long) compptr->h_samp_factor, + (long) cinfo->max_h_samp_factor); + compptr->downsampled_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, + (long) cinfo->max_v_samp_factor); + /* Mark component needed, until color conversion says otherwise */ + compptr->component_needed = TRUE; + /* Mark no quantization table yet saved for component */ + compptr->quant_table = NULL; + } + + /* Compute number of fully interleaved MCU rows. */ + cinfo->total_iMCU_rows = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, + (long) (cinfo->max_v_samp_factor*DCTSIZE)); + + /* Decide whether file contains multiple scans */ + if (cinfo->comps_in_scan < cinfo->num_components || cinfo->progressive_mode) + cinfo->inputctl->has_multiple_scans = TRUE; + else + cinfo->inputctl->has_multiple_scans = FALSE; +} + + +LOCAL(void) +per_scan_setup (j_decompress_ptr cinfo) +/* Do computations that are needed before processing a JPEG scan */ +/* cinfo->comps_in_scan and cinfo->cur_comp_info[] were set from SOS marker */ +{ + int ci, mcublks, tmp; + jpeg_component_info *compptr; + + if (cinfo->comps_in_scan == 1) { + + /* Noninterleaved (single-component) scan */ + compptr = cinfo->cur_comp_info[0]; + + /* Overall image size in MCUs */ + cinfo->MCUs_per_row = compptr->width_in_blocks; + cinfo->MCU_rows_in_scan = compptr->height_in_blocks; + + /* For noninterleaved scan, always one block per MCU */ + compptr->MCU_width = 1; + compptr->MCU_height = 1; + compptr->MCU_blocks = 1; + compptr->MCU_sample_width = compptr->DCT_scaled_size; + compptr->last_col_width = 1; + /* For noninterleaved scans, it is convenient to define last_row_height + * as the number of block rows present in the last iMCU row. + */ + tmp = (int) (compptr->height_in_blocks % compptr->v_samp_factor); + if (tmp == 0) tmp = compptr->v_samp_factor; + compptr->last_row_height = tmp; + + /* Prepare array describing MCU composition */ + cinfo->blocks_in_MCU = 1; + cinfo->MCU_membership[0] = 0; + + } else { + + /* Interleaved (multi-component) scan */ + if (cinfo->comps_in_scan <= 0 || cinfo->comps_in_scan > MAX_COMPS_IN_SCAN) + ERREXIT2(cinfo, JERR_COMPONENT_COUNT, cinfo->comps_in_scan, + MAX_COMPS_IN_SCAN); + + /* Overall image size in MCUs */ + cinfo->MCUs_per_row = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, + (long) (cinfo->max_h_samp_factor*DCTSIZE)); + cinfo->MCU_rows_in_scan = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, + (long) (cinfo->max_v_samp_factor*DCTSIZE)); + + cinfo->blocks_in_MCU = 0; + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* Sampling factors give # of blocks of component in each MCU */ + compptr->MCU_width = compptr->h_samp_factor; + compptr->MCU_height = compptr->v_samp_factor; + compptr->MCU_blocks = compptr->MCU_width * compptr->MCU_height; + compptr->MCU_sample_width = compptr->MCU_width * compptr->DCT_scaled_size; + /* Figure number of non-dummy blocks in last MCU column & row */ + tmp = (int) (compptr->width_in_blocks % compptr->MCU_width); + if (tmp == 0) tmp = compptr->MCU_width; + compptr->last_col_width = tmp; + tmp = (int) (compptr->height_in_blocks % compptr->MCU_height); + if (tmp == 0) tmp = compptr->MCU_height; + compptr->last_row_height = tmp; + /* Prepare array describing MCU composition */ + mcublks = compptr->MCU_blocks; + if (cinfo->blocks_in_MCU + mcublks > D_MAX_BLOCKS_IN_MCU) + ERREXIT(cinfo, JERR_BAD_MCU_SIZE); + while (mcublks-- > 0) { + cinfo->MCU_membership[cinfo->blocks_in_MCU++] = ci; + } + } + + } +} + + +/* + * Save away a copy of the Q-table referenced by each component present + * in the current scan, unless already saved during a prior scan. + * + * In a multiple-scan JPEG file, the encoder could assign different components + * the same Q-table slot number, but change table definitions between scans + * so that each component uses a different Q-table. (The IJG encoder is not + * currently capable of doing this, but other encoders might.) Since we want + * to be able to dequantize all the components at the end of the file, this + * means that we have to save away the table actually used for each component. + * We do this by copying the table at the start of the first scan containing + * the component. + * The JPEG spec prohibits the encoder from changing the contents of a Q-table + * slot between scans of a component using that slot. If the encoder does so + * anyway, this decoder will simply use the Q-table values that were current + * at the start of the first scan for the component. + * + * The decompressor output side looks only at the saved quant tables, + * not at the current Q-table slots. + */ + +LOCAL(void) +latch_quant_tables (j_decompress_ptr cinfo) +{ + int ci, qtblno; + jpeg_component_info *compptr; + JQUANT_TBL * qtbl; + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* No work if we already saved Q-table for this component */ + if (compptr->quant_table != NULL) + continue; + /* Make sure specified quantization table is present */ + qtblno = compptr->quant_tbl_no; + if (qtblno < 0 || qtblno >= NUM_QUANT_TBLS || + cinfo->quant_tbl_ptrs[qtblno] == NULL) + ERREXIT1(cinfo, JERR_NO_QUANT_TABLE, qtblno); + /* OK, save away the quantization table */ + qtbl = (JQUANT_TBL *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(JQUANT_TBL)); + MEMCOPY(qtbl, cinfo->quant_tbl_ptrs[qtblno], SIZEOF(JQUANT_TBL)); + compptr->quant_table = qtbl; + } +} + + +/* + * Initialize the input modules to read a scan of compressed data. + * The first call to this is done by jdmaster.c after initializing + * the entire decompressor (during jpeg_start_decompress). + * Subsequent calls come from consume_markers, below. + */ + +METHODDEF(void) +start_input_pass (j_decompress_ptr cinfo) +{ + per_scan_setup(cinfo); + latch_quant_tables(cinfo); + (*cinfo->entropy->start_pass) (cinfo); + (*cinfo->coef->start_input_pass) (cinfo); + cinfo->inputctl->consume_input = cinfo->coef->consume_data; +} + + +/* + * Finish up after inputting a compressed-data scan. + * This is called by the coefficient controller after it's read all + * the expected data of the scan. + */ + +METHODDEF(void) +finish_input_pass (j_decompress_ptr cinfo) +{ + cinfo->inputctl->consume_input = consume_markers; +} + + +/* + * Read JPEG markers before, between, or after compressed-data scans. + * Change state as necessary when a new scan is reached. + * Return value is JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + * + * The consume_input method pointer points either here or to the + * coefficient controller's consume_data routine, depending on whether + * we are reading a compressed data segment or inter-segment markers. + */ + +METHODDEF(int) +consume_markers (j_decompress_ptr cinfo) +{ + my_inputctl_ptr inputctl = (my_inputctl_ptr) cinfo->inputctl; + int val; + + if (inputctl->pub.eoi_reached) /* After hitting EOI, read no further */ + return JPEG_REACHED_EOI; + + val = (*cinfo->marker->read_markers) (cinfo); + + switch (val) { + case JPEG_REACHED_SOS: /* Found SOS */ + if (inputctl->inheaders) { /* 1st SOS */ + initial_setup(cinfo); + inputctl->inheaders = FALSE; + /* Note: start_input_pass must be called by jdmaster.c + * before any more input can be consumed. jdapimin.c is + * responsible for enforcing this sequencing. + */ + } else { /* 2nd or later SOS marker */ + if (! inputctl->pub.has_multiple_scans) + ERREXIT(cinfo, JERR_EOI_EXPECTED); /* Oops, I wasn't expecting this! */ + start_input_pass(cinfo); + } + break; + case JPEG_REACHED_EOI: /* Found EOI */ + inputctl->pub.eoi_reached = TRUE; + if (inputctl->inheaders) { /* Tables-only datastream, apparently */ + if (cinfo->marker->saw_SOF) + ERREXIT(cinfo, JERR_SOF_NO_SOS); + } else { + /* Prevent infinite loop in coef ctlr's decompress_data routine + * if user set output_scan_number larger than number of scans. + */ + if (cinfo->output_scan_number > cinfo->input_scan_number) + cinfo->output_scan_number = cinfo->input_scan_number; + } + break; + case JPEG_SUSPENDED: + break; + } + + return val; +} + + +/* + * Reset state to begin a fresh datastream. + */ + +METHODDEF(void) +reset_input_controller (j_decompress_ptr cinfo) +{ + my_inputctl_ptr inputctl = (my_inputctl_ptr) cinfo->inputctl; + + inputctl->pub.consume_input = consume_markers; + inputctl->pub.has_multiple_scans = FALSE; /* "unknown" would be better */ + inputctl->pub.eoi_reached = FALSE; + inputctl->inheaders = TRUE; + /* Reset other modules */ + (*cinfo->err->reset_error_mgr) ((j_common_ptr) cinfo); + (*cinfo->marker->reset_marker_reader) (cinfo); + /* Reset progression state -- would be cleaner if entropy decoder did this */ + cinfo->coef_bits = NULL; +} + + +/* + * Initialize the input controller module. + * This is called only once, when the decompression object is created. + */ + +GLOBAL(void) +jinit_input_controller (j_decompress_ptr cinfo) +{ + my_inputctl_ptr inputctl; + + /* Create subobject in permanent pool */ + inputctl = (my_inputctl_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(my_input_controller)); + cinfo->inputctl = (struct jpeg_input_controller *) inputctl; + /* Initialize method pointers */ + inputctl->pub.consume_input = consume_markers; + inputctl->pub.reset_input_controller = reset_input_controller; + inputctl->pub.start_input_pass = start_input_pass; + inputctl->pub.finish_input_pass = finish_input_pass; + /* Initialize state: can't use reset_input_controller since we don't + * want to try to reset other modules yet. + */ + inputctl->pub.has_multiple_scans = FALSE; /* "unknown" would be better */ + inputctl->pub.eoi_reached = FALSE; + inputctl->inheaders = TRUE; +} diff --git a/Engine/lib/ljpeg/jdmainct.c b/Engine/lib/ljpeg/jdmainct.c new file mode 100644 index 000000000..13c956f5d --- /dev/null +++ b/Engine/lib/ljpeg/jdmainct.c @@ -0,0 +1,512 @@ +/* + * jdmainct.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the main buffer controller for decompression. + * The main buffer lies between the JPEG decompressor proper and the + * post-processor; it holds downsampled data in the JPEG colorspace. + * + * Note that this code is bypassed in raw-data mode, since the application + * supplies the equivalent of the main buffer in that case. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * In the current system design, the main buffer need never be a full-image + * buffer; any full-height buffers will be found inside the coefficient or + * postprocessing controllers. Nonetheless, the main controller is not + * trivial. Its responsibility is to provide context rows for upsampling/ + * rescaling, and doing this in an efficient fashion is a bit tricky. + * + * Postprocessor input data is counted in "row groups". A row group + * is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size) + * sample rows of each component. (We require DCT_scaled_size values to be + * chosen such that these numbers are integers. In practice DCT_scaled_size + * values will likely be powers of two, so we actually have the stronger + * condition that DCT_scaled_size / min_DCT_scaled_size is an integer.) + * Upsampling will typically produce max_v_samp_factor pixel rows from each + * row group (times any additional scale factor that the upsampler is + * applying). + * + * The coefficient controller will deliver data to us one iMCU row at a time; + * each iMCU row contains v_samp_factor * DCT_scaled_size sample rows, or + * exactly min_DCT_scaled_size row groups. (This amount of data corresponds + * to one row of MCUs when the image is fully interleaved.) Note that the + * number of sample rows varies across components, but the number of row + * groups does not. Some garbage sample rows may be included in the last iMCU + * row at the bottom of the image. + * + * Depending on the vertical scaling algorithm used, the upsampler may need + * access to the sample row(s) above and below its current input row group. + * The upsampler is required to set need_context_rows TRUE at global selection + * time if so. When need_context_rows is FALSE, this controller can simply + * obtain one iMCU row at a time from the coefficient controller and dole it + * out as row groups to the postprocessor. + * + * When need_context_rows is TRUE, this controller guarantees that the buffer + * passed to postprocessing contains at least one row group's worth of samples + * above and below the row group(s) being processed. Note that the context + * rows "above" the first passed row group appear at negative row offsets in + * the passed buffer. At the top and bottom of the image, the required + * context rows are manufactured by duplicating the first or last real sample + * row; this avoids having special cases in the upsampling inner loops. + * + * The amount of context is fixed at one row group just because that's a + * convenient number for this controller to work with. The existing + * upsamplers really only need one sample row of context. An upsampler + * supporting arbitrary output rescaling might wish for more than one row + * group of context when shrinking the image; tough, we don't handle that. + * (This is justified by the assumption that downsizing will be handled mostly + * by adjusting the DCT_scaled_size values, so that the actual scale factor at + * the upsample step needn't be much less than one.) + * + * To provide the desired context, we have to retain the last two row groups + * of one iMCU row while reading in the next iMCU row. (The last row group + * can't be processed until we have another row group for its below-context, + * and so we have to save the next-to-last group too for its above-context.) + * We could do this most simply by copying data around in our buffer, but + * that'd be very slow. We can avoid copying any data by creating a rather + * strange pointer structure. Here's how it works. We allocate a workspace + * consisting of M+2 row groups (where M = min_DCT_scaled_size is the number + * of row groups per iMCU row). We create two sets of redundant pointers to + * the workspace. Labeling the physical row groups 0 to M+1, the synthesized + * pointer lists look like this: + * M+1 M-1 + * master pointer --> 0 master pointer --> 0 + * 1 1 + * ... ... + * M-3 M-3 + * M-2 M + * M-1 M+1 + * M M-2 + * M+1 M-1 + * 0 0 + * We read alternate iMCU rows using each master pointer; thus the last two + * row groups of the previous iMCU row remain un-overwritten in the workspace. + * The pointer lists are set up so that the required context rows appear to + * be adjacent to the proper places when we pass the pointer lists to the + * upsampler. + * + * The above pictures describe the normal state of the pointer lists. + * At top and bottom of the image, we diddle the pointer lists to duplicate + * the first or last sample row as necessary (this is cheaper than copying + * sample rows around). + * + * This scheme breaks down if M < 2, ie, min_DCT_scaled_size is 1. In that + * situation each iMCU row provides only one row group so the buffering logic + * must be different (eg, we must read two iMCU rows before we can emit the + * first row group). For now, we simply do not support providing context + * rows when min_DCT_scaled_size is 1. That combination seems unlikely to + * be worth providing --- if someone wants a 1/8th-size preview, they probably + * want it quick and dirty, so a context-free upsampler is sufficient. + */ + + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_d_main_controller pub; /* public fields */ + + /* Pointer to allocated workspace (M or M+2 row groups). */ + JSAMPARRAY buffer[MAX_COMPONENTS]; + + boolean buffer_full; /* Have we gotten an iMCU row from decoder? */ + JDIMENSION rowgroup_ctr; /* counts row groups output to postprocessor */ + + /* Remaining fields are only used in the context case. */ + + /* These are the master pointers to the funny-order pointer lists. */ + JSAMPIMAGE xbuffer[2]; /* pointers to weird pointer lists */ + + int whichptr; /* indicates which pointer set is now in use */ + int context_state; /* process_data state machine status */ + JDIMENSION rowgroups_avail; /* row groups available to postprocessor */ + JDIMENSION iMCU_row_ctr; /* counts iMCU rows to detect image top/bot */ +} my_main_controller; + +typedef my_main_controller * my_main_ptr; + +/* context_state values: */ +#define CTX_PREPARE_FOR_IMCU 0 /* need to prepare for MCU row */ +#define CTX_PROCESS_IMCU 1 /* feeding iMCU to postprocessor */ +#define CTX_POSTPONED_ROW 2 /* feeding postponed row group */ + + +/* Forward declarations */ +METHODDEF(void) process_data_simple_main + JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)); +METHODDEF(void) process_data_context_main + JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)); +#ifdef QUANT_2PASS_SUPPORTED +METHODDEF(void) process_data_crank_post + JPP((j_decompress_ptr cinfo, JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail)); +#endif + + +LOCAL(void) +alloc_funny_pointers (j_decompress_ptr cinfo) +/* Allocate space for the funny pointer lists. + * This is done only once, not once per pass. + */ +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + int ci, rgroup; + int M = cinfo->min_DCT_scaled_size; + jpeg_component_info *compptr; + JSAMPARRAY xbuf; + + /* Get top-level space for component array pointers. + * We alloc both arrays with one call to save a few cycles. + */ + main->xbuffer[0] = (JSAMPIMAGE) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->num_components * 2 * SIZEOF(JSAMPARRAY)); + main->xbuffer[1] = main->xbuffer[0] + cinfo->num_components; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + /* Get space for pointer lists --- M+4 row groups in each list. + * We alloc both pointer lists with one call to save a few cycles. + */ + xbuf = (JSAMPARRAY) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + 2 * (rgroup * (M + 4)) * SIZEOF(JSAMPROW)); + xbuf += rgroup; /* want one row group at negative offsets */ + main->xbuffer[0][ci] = xbuf; + xbuf += rgroup * (M + 4); + main->xbuffer[1][ci] = xbuf; + } +} + + +LOCAL(void) +make_funny_pointers (j_decompress_ptr cinfo) +/* Create the funny pointer lists discussed in the comments above. + * The actual workspace is already allocated (in main->buffer), + * and the space for the pointer lists is allocated too. + * This routine just fills in the curiously ordered lists. + * This will be repeated at the beginning of each pass. + */ +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + int ci, i, rgroup; + int M = cinfo->min_DCT_scaled_size; + jpeg_component_info *compptr; + JSAMPARRAY buf, xbuf0, xbuf1; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + xbuf0 = main->xbuffer[0][ci]; + xbuf1 = main->xbuffer[1][ci]; + /* First copy the workspace pointers as-is */ + buf = main->buffer[ci]; + for (i = 0; i < rgroup * (M + 2); i++) { + xbuf0[i] = xbuf1[i] = buf[i]; + } + /* In the second list, put the last four row groups in swapped order */ + for (i = 0; i < rgroup * 2; i++) { + xbuf1[rgroup*(M-2) + i] = buf[rgroup*M + i]; + xbuf1[rgroup*M + i] = buf[rgroup*(M-2) + i]; + } + /* The wraparound pointers at top and bottom will be filled later + * (see set_wraparound_pointers, below). Initially we want the "above" + * pointers to duplicate the first actual data line. This only needs + * to happen in xbuffer[0]. + */ + for (i = 0; i < rgroup; i++) { + xbuf0[i - rgroup] = xbuf0[0]; + } + } +} + + +LOCAL(void) +set_wraparound_pointers (j_decompress_ptr cinfo) +/* Set up the "wraparound" pointers at top and bottom of the pointer lists. + * This changes the pointer list state from top-of-image to the normal state. + */ +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + int ci, i, rgroup; + int M = cinfo->min_DCT_scaled_size; + jpeg_component_info *compptr; + JSAMPARRAY xbuf0, xbuf1; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + xbuf0 = main->xbuffer[0][ci]; + xbuf1 = main->xbuffer[1][ci]; + for (i = 0; i < rgroup; i++) { + xbuf0[i - rgroup] = xbuf0[rgroup*(M+1) + i]; + xbuf1[i - rgroup] = xbuf1[rgroup*(M+1) + i]; + xbuf0[rgroup*(M+2) + i] = xbuf0[i]; + xbuf1[rgroup*(M+2) + i] = xbuf1[i]; + } + } +} + + +LOCAL(void) +set_bottom_pointers (j_decompress_ptr cinfo) +/* Change the pointer lists to duplicate the last sample row at the bottom + * of the image. whichptr indicates which xbuffer holds the final iMCU row. + * Also sets rowgroups_avail to indicate number of nondummy row groups in row. + */ +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + int ci, i, rgroup, iMCUheight, rows_left; + jpeg_component_info *compptr; + JSAMPARRAY xbuf; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Count sample rows in one iMCU row and in one row group */ + iMCUheight = compptr->v_samp_factor * compptr->DCT_scaled_size; + rgroup = iMCUheight / cinfo->min_DCT_scaled_size; + /* Count nondummy sample rows remaining for this component */ + rows_left = (int) (compptr->downsampled_height % (JDIMENSION) iMCUheight); + if (rows_left == 0) rows_left = iMCUheight; + /* Count nondummy row groups. Should get same answer for each component, + * so we need only do it once. + */ + if (ci == 0) { + main->rowgroups_avail = (JDIMENSION) ((rows_left-1) / rgroup + 1); + } + /* Duplicate the last real sample row rgroup*2 times; this pads out the + * last partial rowgroup and ensures at least one full rowgroup of context. + */ + xbuf = main->xbuffer[main->whichptr][ci]; + for (i = 0; i < rgroup * 2; i++) { + xbuf[rows_left + i] = xbuf[rows_left-1]; + } + } +} + + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_main (j_decompress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + + switch (pass_mode) { + case JBUF_PASS_THRU: + if (cinfo->upsample->need_context_rows) { + main->pub.process_data = process_data_context_main; + make_funny_pointers(cinfo); /* Create the xbuffer[] lists */ + main->whichptr = 0; /* Read first iMCU row into xbuffer[0] */ + main->context_state = CTX_PREPARE_FOR_IMCU; + main->iMCU_row_ctr = 0; + } else { + /* Simple case with no context needed */ + main->pub.process_data = process_data_simple_main; + } + main->buffer_full = FALSE; /* Mark buffer empty */ + main->rowgroup_ctr = 0; + break; +#ifdef QUANT_2PASS_SUPPORTED + case JBUF_CRANK_DEST: + /* For last pass of 2-pass quantization, just crank the postprocessor */ + main->pub.process_data = process_data_crank_post; + break; +#endif + default: + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + break; + } +} + + +/* + * Process some data. + * This handles the simple case where no context is required. + */ + +METHODDEF(void) +process_data_simple_main (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + JDIMENSION rowgroups_avail; + + /* Read input data if we haven't filled the main buffer yet */ + if (! main->buffer_full) { + if (! (*cinfo->coef->decompress_data) (cinfo, main->buffer)) + return; /* suspension forced, can do nothing more */ + main->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ + } + + /* There are always min_DCT_scaled_size row groups in an iMCU row. */ + rowgroups_avail = (JDIMENSION) cinfo->min_DCT_scaled_size; + /* Note: at the bottom of the image, we may pass extra garbage row groups + * to the postprocessor. The postprocessor has to check for bottom + * of image anyway (at row resolution), so no point in us doing it too. + */ + + /* Feed the postprocessor */ + (*cinfo->post->post_process_data) (cinfo, main->buffer, + &main->rowgroup_ctr, rowgroups_avail, + output_buf, out_row_ctr, out_rows_avail); + + /* Has postprocessor consumed all the data yet? If so, mark buffer empty */ + if (main->rowgroup_ctr >= rowgroups_avail) { + main->buffer_full = FALSE; + main->rowgroup_ctr = 0; + } +} + + +/* + * Process some data. + * This handles the case where context rows must be provided. + */ + +METHODDEF(void) +process_data_context_main (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_main_ptr main = (my_main_ptr) cinfo->main; + + /* Read input data if we haven't filled the main buffer yet */ + if (! main->buffer_full) { + if (! (*cinfo->coef->decompress_data) (cinfo, + main->xbuffer[main->whichptr])) + return; /* suspension forced, can do nothing more */ + main->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ + main->iMCU_row_ctr++; /* count rows received */ + } + + /* Postprocessor typically will not swallow all the input data it is handed + * in one call (due to filling the output buffer first). Must be prepared + * to exit and restart. This switch lets us keep track of how far we got. + * Note that each case falls through to the next on successful completion. + */ + switch (main->context_state) { + case CTX_POSTPONED_ROW: + /* Call postprocessor using previously set pointers for postponed row */ + (*cinfo->post->post_process_data) (cinfo, main->xbuffer[main->whichptr], + &main->rowgroup_ctr, main->rowgroups_avail, + output_buf, out_row_ctr, out_rows_avail); + if (main->rowgroup_ctr < main->rowgroups_avail) + return; /* Need to suspend */ + main->context_state = CTX_PREPARE_FOR_IMCU; + if (*out_row_ctr >= out_rows_avail) + return; /* Postprocessor exactly filled output buf */ + /*FALLTHROUGH*/ + case CTX_PREPARE_FOR_IMCU: + /* Prepare to process first M-1 row groups of this iMCU row */ + main->rowgroup_ctr = 0; + main->rowgroups_avail = (JDIMENSION) (cinfo->min_DCT_scaled_size - 1); + /* Check for bottom of image: if so, tweak pointers to "duplicate" + * the last sample row, and adjust rowgroups_avail to ignore padding rows. + */ + if (main->iMCU_row_ctr == cinfo->total_iMCU_rows) + set_bottom_pointers(cinfo); + main->context_state = CTX_PROCESS_IMCU; + /*FALLTHROUGH*/ + case CTX_PROCESS_IMCU: + /* Call postprocessor using previously set pointers */ + (*cinfo->post->post_process_data) (cinfo, main->xbuffer[main->whichptr], + &main->rowgroup_ctr, main->rowgroups_avail, + output_buf, out_row_ctr, out_rows_avail); + if (main->rowgroup_ctr < main->rowgroups_avail) + return; /* Need to suspend */ + /* After the first iMCU, change wraparound pointers to normal state */ + if (main->iMCU_row_ctr == 1) + set_wraparound_pointers(cinfo); + /* Prepare to load new iMCU row using other xbuffer list */ + main->whichptr ^= 1; /* 0=>1 or 1=>0 */ + main->buffer_full = FALSE; + /* Still need to process last row group of this iMCU row, */ + /* which is saved at index M+1 of the other xbuffer */ + main->rowgroup_ctr = (JDIMENSION) (cinfo->min_DCT_scaled_size + 1); + main->rowgroups_avail = (JDIMENSION) (cinfo->min_DCT_scaled_size + 2); + main->context_state = CTX_POSTPONED_ROW; + } +} + + +/* + * Process some data. + * Final pass of two-pass quantization: just call the postprocessor. + * Source data will be the postprocessor controller's internal buffer. + */ + +#ifdef QUANT_2PASS_SUPPORTED + +METHODDEF(void) +process_data_crank_post (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + (*cinfo->post->post_process_data) (cinfo, (JSAMPIMAGE) NULL, + (JDIMENSION *) NULL, (JDIMENSION) 0, + output_buf, out_row_ctr, out_rows_avail); +} + +#endif /* QUANT_2PASS_SUPPORTED */ + + +/* + * Initialize main buffer controller. + */ + +GLOBAL(void) +jinit_d_main_controller (j_decompress_ptr cinfo, boolean need_full_buffer) +{ + my_main_ptr main; + int ci, rgroup, ngroups; + jpeg_component_info *compptr; + + main = (my_main_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_main_controller)); + cinfo->main = (struct jpeg_d_main_controller *) main; + main->pub.start_pass = start_pass_main; + + if (need_full_buffer) /* shouldn't happen */ + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + + /* Allocate the workspace. + * ngroups is the number of row groups we need. + */ + if (cinfo->upsample->need_context_rows) { + if (cinfo->min_DCT_scaled_size < 2) /* unsupported, see comments above */ + ERREXIT(cinfo, JERR_NOTIMPL); + alloc_funny_pointers(cinfo); /* Alloc space for xbuffer[] lists */ + ngroups = cinfo->min_DCT_scaled_size + 2; + } else { + ngroups = cinfo->min_DCT_scaled_size; + } + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + rgroup = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; /* height of a row group of component */ + main->buffer[ci] = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + compptr->width_in_blocks * compptr->DCT_scaled_size, + (JDIMENSION) (rgroup * ngroups)); + } +} diff --git a/Engine/lib/ljpeg/jdmarker.c b/Engine/lib/ljpeg/jdmarker.c new file mode 100644 index 000000000..f4cca8cc8 --- /dev/null +++ b/Engine/lib/ljpeg/jdmarker.c @@ -0,0 +1,1360 @@ +/* + * jdmarker.c + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains routines to decode JPEG datastream markers. + * Most of the complexity arises from our desire to support input + * suspension: if not all of the data for a marker is available, + * we must exit back to the application. On resumption, we reprocess + * the marker. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +typedef enum { /* JPEG marker codes */ + M_SOF0 = 0xc0, + M_SOF1 = 0xc1, + M_SOF2 = 0xc2, + M_SOF3 = 0xc3, + + M_SOF5 = 0xc5, + M_SOF6 = 0xc6, + M_SOF7 = 0xc7, + + M_JPG = 0xc8, + M_SOF9 = 0xc9, + M_SOF10 = 0xca, + M_SOF11 = 0xcb, + + M_SOF13 = 0xcd, + M_SOF14 = 0xce, + M_SOF15 = 0xcf, + + M_DHT = 0xc4, + + M_DAC = 0xcc, + + M_RST0 = 0xd0, + M_RST1 = 0xd1, + M_RST2 = 0xd2, + M_RST3 = 0xd3, + M_RST4 = 0xd4, + M_RST5 = 0xd5, + M_RST6 = 0xd6, + M_RST7 = 0xd7, + + M_SOI = 0xd8, + M_EOI = 0xd9, + M_SOS = 0xda, + M_DQT = 0xdb, + M_DNL = 0xdc, + M_DRI = 0xdd, + M_DHP = 0xde, + M_EXP = 0xdf, + + M_APP0 = 0xe0, + M_APP1 = 0xe1, + M_APP2 = 0xe2, + M_APP3 = 0xe3, + M_APP4 = 0xe4, + M_APP5 = 0xe5, + M_APP6 = 0xe6, + M_APP7 = 0xe7, + M_APP8 = 0xe8, + M_APP9 = 0xe9, + M_APP10 = 0xea, + M_APP11 = 0xeb, + M_APP12 = 0xec, + M_APP13 = 0xed, + M_APP14 = 0xee, + M_APP15 = 0xef, + + M_JPG0 = 0xf0, + M_JPG13 = 0xfd, + M_COM = 0xfe, + + M_TEM = 0x01, + + M_ERROR = 0x100 +} JPEG_MARKER; + + +/* Private state */ + +typedef struct { + struct jpeg_marker_reader pub; /* public fields */ + + /* Application-overridable marker processing methods */ + jpeg_marker_parser_method process_COM; + jpeg_marker_parser_method process_APPn[16]; + + /* Limit on marker data length to save for each marker type */ + unsigned int length_limit_COM; + unsigned int length_limit_APPn[16]; + + /* Status of COM/APPn marker saving */ + jpeg_saved_marker_ptr cur_marker; /* NULL if not processing a marker */ + unsigned int bytes_read; /* data bytes read so far in marker */ + /* Note: cur_marker is not linked into marker_list until it's all read. */ +} my_marker_reader; + +typedef my_marker_reader * my_marker_ptr; + + +/* + * Macros for fetching data from the data source module. + * + * At all times, cinfo->src->next_input_byte and ->bytes_in_buffer reflect + * the current restart point; we update them only when we have reached a + * suitable place to restart if a suspension occurs. + */ + +/* Declare and initialize local copies of input pointer/count */ +#define INPUT_VARS(cinfo) \ + struct jpeg_source_mgr * datasrc = (cinfo)->src; \ + const JOCTET * next_input_byte = datasrc->next_input_byte; \ + size_t bytes_in_buffer = datasrc->bytes_in_buffer + +/* Unload the local copies --- do this only at a restart boundary */ +#define INPUT_SYNC(cinfo) \ + ( datasrc->next_input_byte = next_input_byte, \ + datasrc->bytes_in_buffer = bytes_in_buffer ) + +/* Reload the local copies --- used only in MAKE_BYTE_AVAIL */ +#define INPUT_RELOAD(cinfo) \ + ( next_input_byte = datasrc->next_input_byte, \ + bytes_in_buffer = datasrc->bytes_in_buffer ) + +/* Internal macro for INPUT_BYTE and INPUT_2BYTES: make a byte available. + * Note we do *not* do INPUT_SYNC before calling fill_input_buffer, + * but we must reload the local copies after a successful fill. + */ +#define MAKE_BYTE_AVAIL(cinfo,action) \ + if (bytes_in_buffer == 0) { \ + if (! (*datasrc->fill_input_buffer) (cinfo)) \ + { action; } \ + INPUT_RELOAD(cinfo); \ + } + +/* Read a byte into variable V. + * If must suspend, take the specified action (typically "return FALSE"). + */ +#define INPUT_BYTE(cinfo,V,action) \ + MAKESTMT( MAKE_BYTE_AVAIL(cinfo,action); \ + bytes_in_buffer--; \ + V = GETJOCTET(*next_input_byte++); ) + +/* As above, but read two bytes interpreted as an unsigned 16-bit integer. + * V should be declared unsigned int or perhaps INT32. + */ +#define INPUT_2BYTES(cinfo,V,action) \ + MAKESTMT( MAKE_BYTE_AVAIL(cinfo,action); \ + bytes_in_buffer--; \ + V = ((unsigned int) GETJOCTET(*next_input_byte++)) << 8; \ + MAKE_BYTE_AVAIL(cinfo,action); \ + bytes_in_buffer--; \ + V += GETJOCTET(*next_input_byte++); ) + + +/* + * Routines to process JPEG markers. + * + * Entry condition: JPEG marker itself has been read and its code saved + * in cinfo->unread_marker; input restart point is just after the marker. + * + * Exit: if return TRUE, have read and processed any parameters, and have + * updated the restart point to point after the parameters. + * If return FALSE, was forced to suspend before reaching end of + * marker parameters; restart point has not been moved. Same routine + * will be called again after application supplies more input data. + * + * This approach to suspension assumes that all of a marker's parameters + * can fit into a single input bufferload. This should hold for "normal" + * markers. Some COM/APPn markers might have large parameter segments + * that might not fit. If we are simply dropping such a marker, we use + * skip_input_data to get past it, and thereby put the problem on the + * source manager's shoulders. If we are saving the marker's contents + * into memory, we use a slightly different convention: when forced to + * suspend, the marker processor updates the restart point to the end of + * what it's consumed (ie, the end of the buffer) before returning FALSE. + * On resumption, cinfo->unread_marker still contains the marker code, + * but the data source will point to the next chunk of marker data. + * The marker processor must retain internal state to deal with this. + * + * Note that we don't bother to avoid duplicate trace messages if a + * suspension occurs within marker parameters. Other side effects + * require more care. + */ + + +LOCAL(boolean) +get_soi (j_decompress_ptr cinfo) +/* Process an SOI marker */ +{ + int i; + + TRACEMS(cinfo, 1, JTRC_SOI); + + if (cinfo->marker->saw_SOI) + ERREXIT(cinfo, JERR_SOI_DUPLICATE); + + /* Reset all parameters that are defined to be reset by SOI */ + + for (i = 0; i < NUM_ARITH_TBLS; i++) { + cinfo->arith_dc_L[i] = 0; + cinfo->arith_dc_U[i] = 1; + cinfo->arith_ac_K[i] = 5; + } + cinfo->restart_interval = 0; + + /* Set initial assumptions for colorspace etc */ + + cinfo->jpeg_color_space = JCS_UNKNOWN; + cinfo->CCIR601_sampling = FALSE; /* Assume non-CCIR sampling??? */ + + cinfo->saw_JFIF_marker = FALSE; + cinfo->JFIF_major_version = 1; /* set default JFIF APP0 values */ + cinfo->JFIF_minor_version = 1; + cinfo->density_unit = 0; + cinfo->X_density = 1; + cinfo->Y_density = 1; + cinfo->saw_Adobe_marker = FALSE; + cinfo->Adobe_transform = 0; + + cinfo->marker->saw_SOI = TRUE; + + return TRUE; +} + + +LOCAL(boolean) +get_sof (j_decompress_ptr cinfo, boolean is_prog, boolean is_arith) +/* Process a SOFn marker */ +{ + INT32 length; + int c, ci; + jpeg_component_info * compptr; + INPUT_VARS(cinfo); + + cinfo->progressive_mode = is_prog; + cinfo->arith_code = is_arith; + + INPUT_2BYTES(cinfo, length, return FALSE); + + INPUT_BYTE(cinfo, cinfo->data_precision, return FALSE); + INPUT_2BYTES(cinfo, cinfo->image_height, return FALSE); + INPUT_2BYTES(cinfo, cinfo->image_width, return FALSE); + INPUT_BYTE(cinfo, cinfo->num_components, return FALSE); + + length -= 8; + + TRACEMS4(cinfo, 1, JTRC_SOF, cinfo->unread_marker, + (int) cinfo->image_width, (int) cinfo->image_height, + cinfo->num_components); + + if (cinfo->marker->saw_SOF) + ERREXIT(cinfo, JERR_SOF_DUPLICATE); + + /* We don't support files in which the image height is initially specified */ + /* as 0 and is later redefined by DNL. As long as we have to check that, */ + /* might as well have a general sanity check. */ + if (cinfo->image_height <= 0 || cinfo->image_width <= 0 + || cinfo->num_components <= 0) + ERREXIT(cinfo, JERR_EMPTY_IMAGE); + + if (length != (cinfo->num_components * 3)) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + if (cinfo->comp_info == NULL) /* do only once, even if suspend */ + cinfo->comp_info = (jpeg_component_info *) (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->num_components * SIZEOF(jpeg_component_info)); + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + compptr->component_index = ci; + INPUT_BYTE(cinfo, compptr->component_id, return FALSE); + INPUT_BYTE(cinfo, c, return FALSE); + compptr->h_samp_factor = (c >> 4) & 15; + compptr->v_samp_factor = (c ) & 15; + INPUT_BYTE(cinfo, compptr->quant_tbl_no, return FALSE); + + TRACEMS4(cinfo, 1, JTRC_SOF_COMPONENT, + compptr->component_id, compptr->h_samp_factor, + compptr->v_samp_factor, compptr->quant_tbl_no); + } + + cinfo->marker->saw_SOF = TRUE; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +LOCAL(boolean) +get_sos (j_decompress_ptr cinfo) +/* Process a SOS marker */ +{ + INT32 length; + int i, ci, n, c, cc; + jpeg_component_info * compptr; + INPUT_VARS(cinfo); + + if (! cinfo->marker->saw_SOF) + ERREXIT(cinfo, JERR_SOS_NO_SOF); + + INPUT_2BYTES(cinfo, length, return FALSE); + + INPUT_BYTE(cinfo, n, return FALSE); /* Number of components */ + + TRACEMS1(cinfo, 1, JTRC_SOS, n); + + if (length != (n * 2 + 6) || n < 1 || n > MAX_COMPS_IN_SCAN) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + cinfo->comps_in_scan = n; + + /* Collect the component-spec parameters */ + + for (i = 0; i < n; i++) { + INPUT_BYTE(cinfo, cc, return FALSE); + INPUT_BYTE(cinfo, c, return FALSE); + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + if (cc == compptr->component_id) + goto id_found; + } + + ERREXIT1(cinfo, JERR_BAD_COMPONENT_ID, cc); + + id_found: + + cinfo->cur_comp_info[i] = compptr; + compptr->dc_tbl_no = (c >> 4) & 15; + compptr->ac_tbl_no = (c ) & 15; + + TRACEMS3(cinfo, 1, JTRC_SOS_COMPONENT, cc, + compptr->dc_tbl_no, compptr->ac_tbl_no); + } + + /* Collect the additional scan parameters Ss, Se, Ah/Al. */ + INPUT_BYTE(cinfo, c, return FALSE); + cinfo->Ss = c; + INPUT_BYTE(cinfo, c, return FALSE); + cinfo->Se = c; + INPUT_BYTE(cinfo, c, return FALSE); + cinfo->Ah = (c >> 4) & 15; + cinfo->Al = (c ) & 15; + + TRACEMS4(cinfo, 1, JTRC_SOS_PARAMS, cinfo->Ss, cinfo->Se, + cinfo->Ah, cinfo->Al); + + /* Prepare to scan data & restart markers */ + cinfo->marker->next_restart_num = 0; + + /* Count another SOS marker */ + cinfo->input_scan_number++; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +#ifdef D_ARITH_CODING_SUPPORTED + +LOCAL(boolean) +get_dac (j_decompress_ptr cinfo) +/* Process a DAC marker */ +{ + INT32 length; + int index, val; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + while (length > 0) { + INPUT_BYTE(cinfo, index, return FALSE); + INPUT_BYTE(cinfo, val, return FALSE); + + length -= 2; + + TRACEMS2(cinfo, 1, JTRC_DAC, index, val); + + if (index < 0 || index >= (2*NUM_ARITH_TBLS)) + ERREXIT1(cinfo, JERR_DAC_INDEX, index); + + if (index >= NUM_ARITH_TBLS) { /* define AC table */ + cinfo->arith_ac_K[index-NUM_ARITH_TBLS] = (UINT8) val; + } else { /* define DC table */ + cinfo->arith_dc_L[index] = (UINT8) (val & 0x0F); + cinfo->arith_dc_U[index] = (UINT8) (val >> 4); + if (cinfo->arith_dc_L[index] > cinfo->arith_dc_U[index]) + ERREXIT1(cinfo, JERR_DAC_VALUE, val); + } + } + + if (length != 0) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + INPUT_SYNC(cinfo); + return TRUE; +} + +#else /* ! D_ARITH_CODING_SUPPORTED */ + +#define get_dac(cinfo) skip_variable(cinfo) + +#endif /* D_ARITH_CODING_SUPPORTED */ + + +LOCAL(boolean) +get_dht (j_decompress_ptr cinfo) +/* Process a DHT marker */ +{ + INT32 length; + UINT8 bits[17]; + UINT8 huffval[256]; + int i, index, count; + JHUFF_TBL **htblptr; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + while (length > 16) { + INPUT_BYTE(cinfo, index, return FALSE); + + TRACEMS1(cinfo, 1, JTRC_DHT, index); + + bits[0] = 0; + count = 0; + for (i = 1; i <= 16; i++) { + INPUT_BYTE(cinfo, bits[i], return FALSE); + count += bits[i]; + } + + length -= 1 + 16; + + TRACEMS8(cinfo, 2, JTRC_HUFFBITS, + bits[1], bits[2], bits[3], bits[4], + bits[5], bits[6], bits[7], bits[8]); + TRACEMS8(cinfo, 2, JTRC_HUFFBITS, + bits[9], bits[10], bits[11], bits[12], + bits[13], bits[14], bits[15], bits[16]); + + /* Here we just do minimal validation of the counts to avoid walking + * off the end of our table space. jdhuff.c will check more carefully. + */ + if (count > 256 || ((INT32) count) > length) + ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); + + for (i = 0; i < count; i++) + INPUT_BYTE(cinfo, huffval[i], return FALSE); + + length -= count; + + if (index & 0x10) { /* AC table definition */ + index -= 0x10; + htblptr = &cinfo->ac_huff_tbl_ptrs[index]; + } else { /* DC table definition */ + htblptr = &cinfo->dc_huff_tbl_ptrs[index]; + } + + if (index < 0 || index >= NUM_HUFF_TBLS) + ERREXIT1(cinfo, JERR_DHT_INDEX, index); + + if (*htblptr == NULL) + *htblptr = jpeg_alloc_huff_table((j_common_ptr) cinfo); + + MEMCOPY((*htblptr)->bits, bits, SIZEOF((*htblptr)->bits)); + MEMCOPY((*htblptr)->huffval, huffval, SIZEOF((*htblptr)->huffval)); + } + + if (length != 0) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + INPUT_SYNC(cinfo); + return TRUE; +} + + +LOCAL(boolean) +get_dqt (j_decompress_ptr cinfo) +/* Process a DQT marker */ +{ + INT32 length; + int n, i, prec; + unsigned int tmp; + JQUANT_TBL *quant_ptr; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + while (length > 0) { + INPUT_BYTE(cinfo, n, return FALSE); + prec = n >> 4; + n &= 0x0F; + + TRACEMS2(cinfo, 1, JTRC_DQT, n, prec); + + if (n >= NUM_QUANT_TBLS) + ERREXIT1(cinfo, JERR_DQT_INDEX, n); + + if (cinfo->quant_tbl_ptrs[n] == NULL) + cinfo->quant_tbl_ptrs[n] = jpeg_alloc_quant_table((j_common_ptr) cinfo); + quant_ptr = cinfo->quant_tbl_ptrs[n]; + + for (i = 0; i < DCTSIZE2; i++) { + if (prec) + INPUT_2BYTES(cinfo, tmp, return FALSE); + else + INPUT_BYTE(cinfo, tmp, return FALSE); + /* We convert the zigzag-order table to natural array order. */ + quant_ptr->quantval[jpeg_natural_order[i]] = (UINT16) tmp; + } + + if (cinfo->err->trace_level >= 2) { + for (i = 0; i < DCTSIZE2; i += 8) { + TRACEMS8(cinfo, 2, JTRC_QUANTVALS, + quant_ptr->quantval[i], quant_ptr->quantval[i+1], + quant_ptr->quantval[i+2], quant_ptr->quantval[i+3], + quant_ptr->quantval[i+4], quant_ptr->quantval[i+5], + quant_ptr->quantval[i+6], quant_ptr->quantval[i+7]); + } + } + + length -= DCTSIZE2+1; + if (prec) length -= DCTSIZE2; + } + + if (length != 0) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + INPUT_SYNC(cinfo); + return TRUE; +} + + +LOCAL(boolean) +get_dri (j_decompress_ptr cinfo) +/* Process a DRI marker */ +{ + INT32 length; + unsigned int tmp; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + + if (length != 4) + ERREXIT(cinfo, JERR_BAD_LENGTH); + + INPUT_2BYTES(cinfo, tmp, return FALSE); + + TRACEMS1(cinfo, 1, JTRC_DRI, tmp); + + cinfo->restart_interval = tmp; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +/* + * Routines for processing APPn and COM markers. + * These are either saved in memory or discarded, per application request. + * APP0 and APP14 are specially checked to see if they are + * JFIF and Adobe markers, respectively. + */ + +#define APP0_DATA_LEN 14 /* Length of interesting data in APP0 */ +#define APP14_DATA_LEN 12 /* Length of interesting data in APP14 */ +#define APPN_DATA_LEN 14 /* Must be the largest of the above!! */ + + +LOCAL(void) +examine_app0 (j_decompress_ptr cinfo, JOCTET FAR * data, + unsigned int datalen, INT32 remaining) +/* Examine first few bytes from an APP0. + * Take appropriate action if it is a JFIF marker. + * datalen is # of bytes at data[], remaining is length of rest of marker data. + */ +{ + INT32 totallen = (INT32) datalen + remaining; + + if (datalen >= APP0_DATA_LEN && + GETJOCTET(data[0]) == 0x4A && + GETJOCTET(data[1]) == 0x46 && + GETJOCTET(data[2]) == 0x49 && + GETJOCTET(data[3]) == 0x46 && + GETJOCTET(data[4]) == 0) { + /* Found JFIF APP0 marker: save info */ + cinfo->saw_JFIF_marker = TRUE; + cinfo->JFIF_major_version = GETJOCTET(data[5]); + cinfo->JFIF_minor_version = GETJOCTET(data[6]); + cinfo->density_unit = GETJOCTET(data[7]); + cinfo->X_density = (GETJOCTET(data[8]) << 8) + GETJOCTET(data[9]); + cinfo->Y_density = (GETJOCTET(data[10]) << 8) + GETJOCTET(data[11]); + /* Check version. + * Major version must be 1, anything else signals an incompatible change. + * (We used to treat this as an error, but now it's a nonfatal warning, + * because some bozo at Hijaak couldn't read the spec.) + * Minor version should be 0..2, but process anyway if newer. + */ + if (cinfo->JFIF_major_version != 1) + WARNMS2(cinfo, JWRN_JFIF_MAJOR, + cinfo->JFIF_major_version, cinfo->JFIF_minor_version); + /* Generate trace messages */ + TRACEMS5(cinfo, 1, JTRC_JFIF, + cinfo->JFIF_major_version, cinfo->JFIF_minor_version, + cinfo->X_density, cinfo->Y_density, cinfo->density_unit); + /* Validate thumbnail dimensions and issue appropriate messages */ + if (GETJOCTET(data[12]) | GETJOCTET(data[13])) + TRACEMS2(cinfo, 1, JTRC_JFIF_THUMBNAIL, + GETJOCTET(data[12]), GETJOCTET(data[13])); + totallen -= APP0_DATA_LEN; + if (totallen != + ((INT32)GETJOCTET(data[12]) * (INT32)GETJOCTET(data[13]) * (INT32) 3)) + TRACEMS1(cinfo, 1, JTRC_JFIF_BADTHUMBNAILSIZE, (int) totallen); + } else if (datalen >= 6 && + GETJOCTET(data[0]) == 0x4A && + GETJOCTET(data[1]) == 0x46 && + GETJOCTET(data[2]) == 0x58 && + GETJOCTET(data[3]) == 0x58 && + GETJOCTET(data[4]) == 0) { + /* Found JFIF "JFXX" extension APP0 marker */ + /* The library doesn't actually do anything with these, + * but we try to produce a helpful trace message. + */ + switch (GETJOCTET(data[5])) { + case 0x10: + TRACEMS1(cinfo, 1, JTRC_THUMB_JPEG, (int) totallen); + break; + case 0x11: + TRACEMS1(cinfo, 1, JTRC_THUMB_PALETTE, (int) totallen); + break; + case 0x13: + TRACEMS1(cinfo, 1, JTRC_THUMB_RGB, (int) totallen); + break; + default: + TRACEMS2(cinfo, 1, JTRC_JFIF_EXTENSION, + GETJOCTET(data[5]), (int) totallen); + break; + } + } else { + /* Start of APP0 does not match "JFIF" or "JFXX", or too short */ + TRACEMS1(cinfo, 1, JTRC_APP0, (int) totallen); + } +} + + +LOCAL(void) +examine_app14 (j_decompress_ptr cinfo, JOCTET FAR * data, + unsigned int datalen, INT32 remaining) +/* Examine first few bytes from an APP14. + * Take appropriate action if it is an Adobe marker. + * datalen is # of bytes at data[], remaining is length of rest of marker data. + */ +{ + unsigned int version, flags0, flags1, transform; + + if (datalen >= APP14_DATA_LEN && + GETJOCTET(data[0]) == 0x41 && + GETJOCTET(data[1]) == 0x64 && + GETJOCTET(data[2]) == 0x6F && + GETJOCTET(data[3]) == 0x62 && + GETJOCTET(data[4]) == 0x65) { + /* Found Adobe APP14 marker */ + version = (GETJOCTET(data[5]) << 8) + GETJOCTET(data[6]); + flags0 = (GETJOCTET(data[7]) << 8) + GETJOCTET(data[8]); + flags1 = (GETJOCTET(data[9]) << 8) + GETJOCTET(data[10]); + transform = GETJOCTET(data[11]); + TRACEMS4(cinfo, 1, JTRC_ADOBE, version, flags0, flags1, transform); + cinfo->saw_Adobe_marker = TRUE; + cinfo->Adobe_transform = (UINT8) transform; + } else { + /* Start of APP14 does not match "Adobe", or too short */ + TRACEMS1(cinfo, 1, JTRC_APP14, (int) (datalen + remaining)); + } +} + + +METHODDEF(boolean) +get_interesting_appn (j_decompress_ptr cinfo) +/* Process an APP0 or APP14 marker without saving it */ +{ + INT32 length; + JOCTET b[APPN_DATA_LEN]; + unsigned int i, numtoread; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + /* get the interesting part of the marker data */ + if (length >= APPN_DATA_LEN) + numtoread = APPN_DATA_LEN; + else if (length > 0) + numtoread = (unsigned int) length; + else + numtoread = 0; + for (i = 0; i < numtoread; i++) + INPUT_BYTE(cinfo, b[i], return FALSE); + length -= numtoread; + + /* process it */ + switch (cinfo->unread_marker) { + case M_APP0: + examine_app0(cinfo, (JOCTET FAR *) b, numtoread, length); + break; + case M_APP14: + examine_app14(cinfo, (JOCTET FAR *) b, numtoread, length); + break; + default: + /* can't get here unless jpeg_save_markers chooses wrong processor */ + ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, cinfo->unread_marker); + break; + } + + /* skip any remaining data -- could be lots */ + INPUT_SYNC(cinfo); + if (length > 0) + (*cinfo->src->skip_input_data) (cinfo, (long) length); + + return TRUE; +} + + +#ifdef SAVE_MARKERS_SUPPORTED + +METHODDEF(boolean) +save_marker (j_decompress_ptr cinfo) +/* Save an APPn or COM marker into the marker list */ +{ + my_marker_ptr marker = (my_marker_ptr) cinfo->marker; + jpeg_saved_marker_ptr cur_marker = marker->cur_marker; + unsigned int bytes_read, data_length; + JOCTET FAR * data; + INT32 length = 0; + INPUT_VARS(cinfo); + + if (cur_marker == NULL) { + /* begin reading a marker */ + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + if (length >= 0) { /* watch out for bogus length word */ + /* figure out how much we want to save */ + unsigned int limit; + if (cinfo->unread_marker == (int) M_COM) + limit = marker->length_limit_COM; + else + limit = marker->length_limit_APPn[cinfo->unread_marker - (int) M_APP0]; + if ((unsigned int) length < limit) + limit = (unsigned int) length; + /* allocate and initialize the marker item */ + cur_marker = (jpeg_saved_marker_ptr) + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(struct jpeg_marker_struct) + limit); + cur_marker->next = NULL; + cur_marker->marker = (UINT8) cinfo->unread_marker; + cur_marker->original_length = (unsigned int) length; + cur_marker->data_length = limit; + /* data area is just beyond the jpeg_marker_struct */ + data = cur_marker->data = (JOCTET FAR *) (cur_marker + 1); + marker->cur_marker = cur_marker; + marker->bytes_read = 0; + bytes_read = 0; + data_length = limit; + } else { + /* deal with bogus length word */ + bytes_read = data_length = 0; + data = NULL; + } + } else { + /* resume reading a marker */ + bytes_read = marker->bytes_read; + data_length = cur_marker->data_length; + data = cur_marker->data + bytes_read; + } + + while (bytes_read < data_length) { + INPUT_SYNC(cinfo); /* move the restart point to here */ + marker->bytes_read = bytes_read; + /* If there's not at least one byte in buffer, suspend */ + MAKE_BYTE_AVAIL(cinfo, return FALSE); + /* Copy bytes with reasonable rapidity */ + while (bytes_read < data_length && bytes_in_buffer > 0) { + *data++ = *next_input_byte++; + bytes_in_buffer--; + bytes_read++; + } + } + + /* Done reading what we want to read */ + if (cur_marker != NULL) { /* will be NULL if bogus length word */ + /* Add new marker to end of list */ + if (cinfo->marker_list == NULL) { + cinfo->marker_list = cur_marker; + } else { + jpeg_saved_marker_ptr prev = cinfo->marker_list; + while (prev->next != NULL) + prev = prev->next; + prev->next = cur_marker; + } + /* Reset pointer & calc remaining data length */ + data = cur_marker->data; + length = cur_marker->original_length - data_length; + } + /* Reset to initial state for next marker */ + marker->cur_marker = NULL; + + /* Process the marker if interesting; else just make a generic trace msg */ + switch (cinfo->unread_marker) { + case M_APP0: + examine_app0(cinfo, data, data_length, length); + break; + case M_APP14: + examine_app14(cinfo, data, data_length, length); + break; + default: + TRACEMS2(cinfo, 1, JTRC_MISC_MARKER, cinfo->unread_marker, + (int) (data_length + length)); + break; + } + + /* skip any remaining data -- could be lots */ + INPUT_SYNC(cinfo); /* do before skip_input_data */ + if (length > 0) + (*cinfo->src->skip_input_data) (cinfo, (long) length); + + return TRUE; +} + +#endif /* SAVE_MARKERS_SUPPORTED */ + + +METHODDEF(boolean) +skip_variable (j_decompress_ptr cinfo) +/* Skip over an unknown or uninteresting variable-length marker */ +{ + INT32 length; + INPUT_VARS(cinfo); + + INPUT_2BYTES(cinfo, length, return FALSE); + length -= 2; + + TRACEMS2(cinfo, 1, JTRC_MISC_MARKER, cinfo->unread_marker, (int) length); + + INPUT_SYNC(cinfo); /* do before skip_input_data */ + if (length > 0) + (*cinfo->src->skip_input_data) (cinfo, (long) length); + + return TRUE; +} + + +/* + * Find the next JPEG marker, save it in cinfo->unread_marker. + * Returns FALSE if had to suspend before reaching a marker; + * in that case cinfo->unread_marker is unchanged. + * + * Note that the result might not be a valid marker code, + * but it will never be 0 or FF. + */ + +LOCAL(boolean) +next_marker (j_decompress_ptr cinfo) +{ + int c; + INPUT_VARS(cinfo); + + for (;;) { + INPUT_BYTE(cinfo, c, return FALSE); + /* Skip any non-FF bytes. + * This may look a bit inefficient, but it will not occur in a valid file. + * We sync after each discarded byte so that a suspending data source + * can discard the byte from its buffer. + */ + while (c != 0xFF) { + cinfo->marker->discarded_bytes++; + INPUT_SYNC(cinfo); + INPUT_BYTE(cinfo, c, return FALSE); + } + /* This loop swallows any duplicate FF bytes. Extra FFs are legal as + * pad bytes, so don't count them in discarded_bytes. We assume there + * will not be so many consecutive FF bytes as to overflow a suspending + * data source's input buffer. + */ + do { + INPUT_BYTE(cinfo, c, return FALSE); + } while (c == 0xFF); + if (c != 0) + break; /* found a valid marker, exit loop */ + /* Reach here if we found a stuffed-zero data sequence (FF/00). + * Discard it and loop back to try again. + */ + cinfo->marker->discarded_bytes += 2; + INPUT_SYNC(cinfo); + } + + if (cinfo->marker->discarded_bytes != 0) { + WARNMS2(cinfo, JWRN_EXTRANEOUS_DATA, cinfo->marker->discarded_bytes, c); + cinfo->marker->discarded_bytes = 0; + } + + cinfo->unread_marker = c; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +LOCAL(boolean) +first_marker (j_decompress_ptr cinfo) +/* Like next_marker, but used to obtain the initial SOI marker. */ +/* For this marker, we do not allow preceding garbage or fill; otherwise, + * we might well scan an entire input file before realizing it ain't JPEG. + * If an application wants to process non-JFIF files, it must seek to the + * SOI before calling the JPEG library. + */ +{ + int c, c2; + INPUT_VARS(cinfo); + + INPUT_BYTE(cinfo, c, return FALSE); + INPUT_BYTE(cinfo, c2, return FALSE); + if (c != 0xFF || c2 != (int) M_SOI) + ERREXIT2(cinfo, JERR_NO_SOI, c, c2); + + cinfo->unread_marker = c2; + + INPUT_SYNC(cinfo); + return TRUE; +} + + +/* + * Read markers until SOS or EOI. + * + * Returns same codes as are defined for jpeg_consume_input: + * JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + */ + +METHODDEF(int) +read_markers (j_decompress_ptr cinfo) +{ + /* Outer loop repeats once for each marker. */ + for (;;) { + /* Collect the marker proper, unless we already did. */ + /* NB: first_marker() enforces the requirement that SOI appear first. */ + if (cinfo->unread_marker == 0) { + if (! cinfo->marker->saw_SOI) { + if (! first_marker(cinfo)) + return JPEG_SUSPENDED; + } else { + if (! next_marker(cinfo)) + return JPEG_SUSPENDED; + } + } + /* At this point cinfo->unread_marker contains the marker code and the + * input point is just past the marker proper, but before any parameters. + * A suspension will cause us to return with this state still true. + */ + switch (cinfo->unread_marker) { + case M_SOI: + if (! get_soi(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_SOF0: /* Baseline */ + case M_SOF1: /* Extended sequential, Huffman */ + if (! get_sof(cinfo, FALSE, FALSE)) + return JPEG_SUSPENDED; + break; + + case M_SOF2: /* Progressive, Huffman */ + if (! get_sof(cinfo, TRUE, FALSE)) + return JPEG_SUSPENDED; + break; + + case M_SOF9: /* Extended sequential, arithmetic */ + if (! get_sof(cinfo, FALSE, TRUE)) + return JPEG_SUSPENDED; + break; + + case M_SOF10: /* Progressive, arithmetic */ + if (! get_sof(cinfo, TRUE, TRUE)) + return JPEG_SUSPENDED; + break; + + /* Currently unsupported SOFn types */ + case M_SOF3: /* Lossless, Huffman */ + case M_SOF5: /* Differential sequential, Huffman */ + case M_SOF6: /* Differential progressive, Huffman */ + case M_SOF7: /* Differential lossless, Huffman */ + case M_JPG: /* Reserved for JPEG extensions */ + case M_SOF11: /* Lossless, arithmetic */ + case M_SOF13: /* Differential sequential, arithmetic */ + case M_SOF14: /* Differential progressive, arithmetic */ + case M_SOF15: /* Differential lossless, arithmetic */ + ERREXIT1(cinfo, JERR_SOF_UNSUPPORTED, cinfo->unread_marker); + break; + + case M_SOS: + if (! get_sos(cinfo)) + return JPEG_SUSPENDED; + cinfo->unread_marker = 0; /* processed the marker */ + return JPEG_REACHED_SOS; + + case M_EOI: + TRACEMS(cinfo, 1, JTRC_EOI); + cinfo->unread_marker = 0; /* processed the marker */ + return JPEG_REACHED_EOI; + + case M_DAC: + if (! get_dac(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_DHT: + if (! get_dht(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_DQT: + if (! get_dqt(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_DRI: + if (! get_dri(cinfo)) + return JPEG_SUSPENDED; + break; + + case M_APP0: + case M_APP1: + case M_APP2: + case M_APP3: + case M_APP4: + case M_APP5: + case M_APP6: + case M_APP7: + case M_APP8: + case M_APP9: + case M_APP10: + case M_APP11: + case M_APP12: + case M_APP13: + case M_APP14: + case M_APP15: + if (! (*((my_marker_ptr) cinfo->marker)->process_APPn[ + cinfo->unread_marker - (int) M_APP0]) (cinfo)) + return JPEG_SUSPENDED; + break; + + case M_COM: + if (! (*((my_marker_ptr) cinfo->marker)->process_COM) (cinfo)) + return JPEG_SUSPENDED; + break; + + case M_RST0: /* these are all parameterless */ + case M_RST1: + case M_RST2: + case M_RST3: + case M_RST4: + case M_RST5: + case M_RST6: + case M_RST7: + case M_TEM: + TRACEMS1(cinfo, 1, JTRC_PARMLESS_MARKER, cinfo->unread_marker); + break; + + case M_DNL: /* Ignore DNL ... perhaps the wrong thing */ + if (! skip_variable(cinfo)) + return JPEG_SUSPENDED; + break; + + default: /* must be DHP, EXP, JPGn, or RESn */ + /* For now, we treat the reserved markers as fatal errors since they are + * likely to be used to signal incompatible JPEG Part 3 extensions. + * Once the JPEG 3 version-number marker is well defined, this code + * ought to change! + */ + ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, cinfo->unread_marker); + break; + } + /* Successfully processed marker, so reset state variable */ + cinfo->unread_marker = 0; + } /* end loop */ +} + + +/* + * Read a restart marker, which is expected to appear next in the datastream; + * if the marker is not there, take appropriate recovery action. + * Returns FALSE if suspension is required. + * + * This is called by the entropy decoder after it has read an appropriate + * number of MCUs. cinfo->unread_marker may be nonzero if the entropy decoder + * has already read a marker from the data source. Under normal conditions + * cinfo->unread_marker will be reset to 0 before returning; if not reset, + * it holds a marker which the decoder will be unable to read past. + */ + +METHODDEF(boolean) +read_restart_marker (j_decompress_ptr cinfo) +{ + /* Obtain a marker unless we already did. */ + /* Note that next_marker will complain if it skips any data. */ + if (cinfo->unread_marker == 0) { + if (! next_marker(cinfo)) + return FALSE; + } + + if (cinfo->unread_marker == + ((int) M_RST0 + cinfo->marker->next_restart_num)) { + /* Normal case --- swallow the marker and let entropy decoder continue */ + TRACEMS1(cinfo, 3, JTRC_RST, cinfo->marker->next_restart_num); + cinfo->unread_marker = 0; + } else { + /* Uh-oh, the restart markers have been messed up. */ + /* Let the data source manager determine how to resync. */ + if (! (*cinfo->src->resync_to_restart) (cinfo, + cinfo->marker->next_restart_num)) + return FALSE; + } + + /* Update next-restart state */ + cinfo->marker->next_restart_num = (cinfo->marker->next_restart_num + 1) & 7; + + return TRUE; +} + + +/* + * This is the default resync_to_restart method for data source managers + * to use if they don't have any better approach. Some data source managers + * may be able to back up, or may have additional knowledge about the data + * which permits a more intelligent recovery strategy; such managers would + * presumably supply their own resync method. + * + * read_restart_marker calls resync_to_restart if it finds a marker other than + * the restart marker it was expecting. (This code is *not* used unless + * a nonzero restart interval has been declared.) cinfo->unread_marker is + * the marker code actually found (might be anything, except 0 or FF). + * The desired restart marker number (0..7) is passed as a parameter. + * This routine is supposed to apply whatever error recovery strategy seems + * appropriate in order to position the input stream to the next data segment. + * Note that cinfo->unread_marker is treated as a marker appearing before + * the current data-source input point; usually it should be reset to zero + * before returning. + * Returns FALSE if suspension is required. + * + * This implementation is substantially constrained by wanting to treat the + * input as a data stream; this means we can't back up. Therefore, we have + * only the following actions to work with: + * 1. Simply discard the marker and let the entropy decoder resume at next + * byte of file. + * 2. Read forward until we find another marker, discarding intervening + * data. (In theory we could look ahead within the current bufferload, + * without having to discard data if we don't find the desired marker. + * This idea is not implemented here, in part because it makes behavior + * dependent on buffer size and chance buffer-boundary positions.) + * 3. Leave the marker unread (by failing to zero cinfo->unread_marker). + * This will cause the entropy decoder to process an empty data segment, + * inserting dummy zeroes, and then we will reprocess the marker. + * + * #2 is appropriate if we think the desired marker lies ahead, while #3 is + * appropriate if the found marker is a future restart marker (indicating + * that we have missed the desired restart marker, probably because it got + * corrupted). + * We apply #2 or #3 if the found marker is a restart marker no more than + * two counts behind or ahead of the expected one. We also apply #2 if the + * found marker is not a legal JPEG marker code (it's certainly bogus data). + * If the found marker is a restart marker more than 2 counts away, we do #1 + * (too much risk that the marker is erroneous; with luck we will be able to + * resync at some future point). + * For any valid non-restart JPEG marker, we apply #3. This keeps us from + * overrunning the end of a scan. An implementation limited to single-scan + * files might find it better to apply #2 for markers other than EOI, since + * any other marker would have to be bogus data in that case. + */ + +GLOBAL(boolean) +jpeg_resync_to_restart (j_decompress_ptr cinfo, int desired) +{ + int marker = cinfo->unread_marker; + int action = 1; + + /* Always put up a warning. */ + WARNMS2(cinfo, JWRN_MUST_RESYNC, marker, desired); + + /* Outer loop handles repeated decision after scanning forward. */ + for (;;) { + if (marker < (int) M_SOF0) + action = 2; /* invalid marker */ + else if (marker < (int) M_RST0 || marker > (int) M_RST7) + action = 3; /* valid non-restart marker */ + else { + if (marker == ((int) M_RST0 + ((desired+1) & 7)) || + marker == ((int) M_RST0 + ((desired+2) & 7))) + action = 3; /* one of the next two expected restarts */ + else if (marker == ((int) M_RST0 + ((desired-1) & 7)) || + marker == ((int) M_RST0 + ((desired-2) & 7))) + action = 2; /* a prior restart, so advance */ + else + action = 1; /* desired restart or too far away */ + } + TRACEMS2(cinfo, 4, JTRC_RECOVERY_ACTION, marker, action); + switch (action) { + case 1: + /* Discard marker and let entropy decoder resume processing. */ + cinfo->unread_marker = 0; + return TRUE; + case 2: + /* Scan to the next marker, and repeat the decision loop. */ + if (! next_marker(cinfo)) + return FALSE; + marker = cinfo->unread_marker; + break; + case 3: + /* Return without advancing past this marker. */ + /* Entropy decoder will be forced to process an empty segment. */ + return TRUE; + } + } /* end loop */ +} + + +/* + * Reset marker processing state to begin a fresh datastream. + */ + +METHODDEF(void) +reset_marker_reader (j_decompress_ptr cinfo) +{ + my_marker_ptr marker = (my_marker_ptr) cinfo->marker; + + cinfo->comp_info = NULL; /* until allocated by get_sof */ + cinfo->input_scan_number = 0; /* no SOS seen yet */ + cinfo->unread_marker = 0; /* no pending marker */ + marker->pub.saw_SOI = FALSE; /* set internal state too */ + marker->pub.saw_SOF = FALSE; + marker->pub.discarded_bytes = 0; + marker->cur_marker = NULL; +} + + +/* + * Initialize the marker reader module. + * This is called only once, when the decompression object is created. + */ + +GLOBAL(void) +jinit_marker_reader (j_decompress_ptr cinfo) +{ + my_marker_ptr marker; + int i; + + /* Create subobject in permanent pool */ + marker = (my_marker_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + SIZEOF(my_marker_reader)); + cinfo->marker = (struct jpeg_marker_reader *) marker; + /* Initialize public method pointers */ + marker->pub.reset_marker_reader = reset_marker_reader; + marker->pub.read_markers = read_markers; + marker->pub.read_restart_marker = read_restart_marker; + /* Initialize COM/APPn processing. + * By default, we examine and then discard APP0 and APP14, + * but simply discard COM and all other APPn. + */ + marker->process_COM = skip_variable; + marker->length_limit_COM = 0; + for (i = 0; i < 16; i++) { + marker->process_APPn[i] = skip_variable; + marker->length_limit_APPn[i] = 0; + } + marker->process_APPn[0] = get_interesting_appn; + marker->process_APPn[14] = get_interesting_appn; + /* Reset marker processing state */ + reset_marker_reader(cinfo); +} + + +/* + * Control saving of COM and APPn markers into marker_list. + */ + +#ifdef SAVE_MARKERS_SUPPORTED + +GLOBAL(void) +jpeg_save_markers (j_decompress_ptr cinfo, int marker_code, + unsigned int length_limit) +{ + my_marker_ptr marker = (my_marker_ptr) cinfo->marker; + long maxlength; + jpeg_marker_parser_method processor; + + /* Length limit mustn't be larger than what we can allocate + * (should only be a concern in a 16-bit environment). + */ + maxlength = cinfo->mem->max_alloc_chunk - SIZEOF(struct jpeg_marker_struct); + if (((long) length_limit) > maxlength) + length_limit = (unsigned int) maxlength; + + /* Choose processor routine to use. + * APP0/APP14 have special requirements. + */ + if (length_limit) { + processor = save_marker; + /* If saving APP0/APP14, save at least enough for our internal use. */ + if (marker_code == (int) M_APP0 && length_limit < APP0_DATA_LEN) + length_limit = APP0_DATA_LEN; + else if (marker_code == (int) M_APP14 && length_limit < APP14_DATA_LEN) + length_limit = APP14_DATA_LEN; + } else { + processor = skip_variable; + /* If discarding APP0/APP14, use our regular on-the-fly processor. */ + if (marker_code == (int) M_APP0 || marker_code == (int) M_APP14) + processor = get_interesting_appn; + } + + if (marker_code == (int) M_COM) { + marker->process_COM = processor; + marker->length_limit_COM = length_limit; + } else if (marker_code >= (int) M_APP0 && marker_code <= (int) M_APP15) { + marker->process_APPn[marker_code - (int) M_APP0] = processor; + marker->length_limit_APPn[marker_code - (int) M_APP0] = length_limit; + } else + ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, marker_code); +} + +#endif /* SAVE_MARKERS_SUPPORTED */ + + +/* + * Install a special processing method for COM or APPn markers. + */ + +GLOBAL(void) +jpeg_set_marker_processor (j_decompress_ptr cinfo, int marker_code, + jpeg_marker_parser_method routine) +{ + my_marker_ptr marker = (my_marker_ptr) cinfo->marker; + + if (marker_code == (int) M_COM) + marker->process_COM = routine; + else if (marker_code >= (int) M_APP0 && marker_code <= (int) M_APP15) + marker->process_APPn[marker_code - (int) M_APP0] = routine; + else + ERREXIT1(cinfo, JERR_UNKNOWN_MARKER, marker_code); +} diff --git a/Engine/lib/ljpeg/jdmaster.c b/Engine/lib/ljpeg/jdmaster.c new file mode 100644 index 000000000..2802c5b7b --- /dev/null +++ b/Engine/lib/ljpeg/jdmaster.c @@ -0,0 +1,557 @@ +/* + * jdmaster.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains master control logic for the JPEG decompressor. + * These routines are concerned with selecting the modules to be executed + * and with determining the number of passes and the work to be done in each + * pass. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Private state */ + +typedef struct { + struct jpeg_decomp_master pub; /* public fields */ + + int pass_number; /* # of passes completed */ + + boolean using_merged_upsample; /* TRUE if using merged upsample/cconvert */ + + /* Saved references to initialized quantizer modules, + * in case we need to switch modes. + */ + struct jpeg_color_quantizer * quantizer_1pass; + struct jpeg_color_quantizer * quantizer_2pass; +} my_decomp_master; + +typedef my_decomp_master * my_master_ptr; + + +/* + * Determine whether merged upsample/color conversion should be used. + * CRUCIAL: this must match the actual capabilities of jdmerge.c! + */ + +LOCAL(boolean) +use_merged_upsample (j_decompress_ptr cinfo) +{ +#ifdef UPSAMPLE_MERGING_SUPPORTED + /* Merging is the equivalent of plain box-filter upsampling */ + if (cinfo->do_fancy_upsampling || cinfo->CCIR601_sampling) + return FALSE; + /* jdmerge.c only supports YCC=>RGB color conversion */ + if (cinfo->jpeg_color_space != JCS_YCbCr || cinfo->num_components != 3 || + cinfo->out_color_space != JCS_RGB || + cinfo->out_color_components != RGB_PIXELSIZE) + return FALSE; + /* and it only handles 2h1v or 2h2v sampling ratios */ + if (cinfo->comp_info[0].h_samp_factor != 2 || + cinfo->comp_info[1].h_samp_factor != 1 || + cinfo->comp_info[2].h_samp_factor != 1 || + cinfo->comp_info[0].v_samp_factor > 2 || + cinfo->comp_info[1].v_samp_factor != 1 || + cinfo->comp_info[2].v_samp_factor != 1) + return FALSE; + /* furthermore, it doesn't work if we've scaled the IDCTs differently */ + if (cinfo->comp_info[0].DCT_scaled_size != cinfo->min_DCT_scaled_size || + cinfo->comp_info[1].DCT_scaled_size != cinfo->min_DCT_scaled_size || + cinfo->comp_info[2].DCT_scaled_size != cinfo->min_DCT_scaled_size) + return FALSE; + /* ??? also need to test for upsample-time rescaling, when & if supported */ + return TRUE; /* by golly, it'll work... */ +#else + return FALSE; +#endif +} + + +/* + * Compute output image dimensions and related values. + * NOTE: this is exported for possible use by application. + * Hence it mustn't do anything that can't be done twice. + * Also note that it may be called before the master module is initialized! + */ + +GLOBAL(void) +jpeg_calc_output_dimensions (j_decompress_ptr cinfo) +/* Do computations that are needed before master selection phase */ +{ +#ifdef IDCT_SCALING_SUPPORTED + int ci; + jpeg_component_info *compptr; +#endif + + /* Prevent application from calling me at wrong times */ + if (cinfo->global_state != DSTATE_READY) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + +#ifdef IDCT_SCALING_SUPPORTED + + /* Compute actual output image dimensions and DCT scaling choices. */ + if (cinfo->scale_num * 8 <= cinfo->scale_denom) { + /* Provide 1/8 scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, 8L); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, 8L); + cinfo->min_DCT_scaled_size = 1; + } else if (cinfo->scale_num * 4 <= cinfo->scale_denom) { + /* Provide 1/4 scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, 4L); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, 4L); + cinfo->min_DCT_scaled_size = 2; + } else if (cinfo->scale_num * 2 <= cinfo->scale_denom) { + /* Provide 1/2 scaling */ + cinfo->output_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width, 2L); + cinfo->output_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height, 2L); + cinfo->min_DCT_scaled_size = 4; + } else { + /* Provide 1/1 scaling */ + cinfo->output_width = cinfo->image_width; + cinfo->output_height = cinfo->image_height; + cinfo->min_DCT_scaled_size = DCTSIZE; + } + /* In selecting the actual DCT scaling for each component, we try to + * scale up the chroma components via IDCT scaling rather than upsampling. + * This saves time if the upsampler gets to use 1:1 scaling. + * Note this code assumes that the supported DCT scalings are powers of 2. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + int ssize = cinfo->min_DCT_scaled_size; + while (ssize < DCTSIZE && + (compptr->h_samp_factor * ssize * 2 <= + cinfo->max_h_samp_factor * cinfo->min_DCT_scaled_size) && + (compptr->v_samp_factor * ssize * 2 <= + cinfo->max_v_samp_factor * cinfo->min_DCT_scaled_size)) { + ssize = ssize * 2; + } + compptr->DCT_scaled_size = ssize; + } + + /* Recompute downsampled dimensions of components; + * application needs to know these if using raw downsampled data. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Size in samples, after IDCT scaling */ + compptr->downsampled_width = (JDIMENSION) + jdiv_round_up((long) cinfo->image_width * + (long) (compptr->h_samp_factor * compptr->DCT_scaled_size), + (long) (cinfo->max_h_samp_factor * DCTSIZE)); + compptr->downsampled_height = (JDIMENSION) + jdiv_round_up((long) cinfo->image_height * + (long) (compptr->v_samp_factor * compptr->DCT_scaled_size), + (long) (cinfo->max_v_samp_factor * DCTSIZE)); + } + +#else /* !IDCT_SCALING_SUPPORTED */ + + /* Hardwire it to "no scaling" */ + cinfo->output_width = cinfo->image_width; + cinfo->output_height = cinfo->image_height; + /* jdinput.c has already initialized DCT_scaled_size to DCTSIZE, + * and has computed unscaled downsampled_width and downsampled_height. + */ + +#endif /* IDCT_SCALING_SUPPORTED */ + + /* Report number of components in selected colorspace. */ + /* Probably this should be in the color conversion module... */ + switch (cinfo->out_color_space) { + case JCS_GRAYSCALE: + cinfo->out_color_components = 1; + break; + case JCS_RGB: +#if RGB_PIXELSIZE != 3 + cinfo->out_color_components = RGB_PIXELSIZE; + break; +#endif /* else share code with YCbCr */ + case JCS_YCbCr: + cinfo->out_color_components = 3; + break; + case JCS_CMYK: + case JCS_YCCK: + cinfo->out_color_components = 4; + break; + default: /* else must be same colorspace as in file */ + cinfo->out_color_components = cinfo->num_components; + break; + } + cinfo->output_components = (cinfo->quantize_colors ? 1 : + cinfo->out_color_components); + + /* See if upsampler will want to emit more than one row at a time */ + if (use_merged_upsample(cinfo)) + cinfo->rec_outbuf_height = cinfo->max_v_samp_factor; + else + cinfo->rec_outbuf_height = 1; +} + + +/* + * Several decompression processes need to range-limit values to the range + * 0..MAXJSAMPLE; the input value may fall somewhat outside this range + * due to noise introduced by quantization, roundoff error, etc. These + * processes are inner loops and need to be as fast as possible. On most + * machines, particularly CPUs with pipelines or instruction prefetch, + * a (subscript-check-less) C table lookup + * x = sample_range_limit[x]; + * is faster than explicit tests + * if (x < 0) x = 0; + * else if (x > MAXJSAMPLE) x = MAXJSAMPLE; + * These processes all use a common table prepared by the routine below. + * + * For most steps we can mathematically guarantee that the initial value + * of x is within MAXJSAMPLE+1 of the legal range, so a table running from + * -(MAXJSAMPLE+1) to 2*MAXJSAMPLE+1 is sufficient. But for the initial + * limiting step (just after the IDCT), a wildly out-of-range value is + * possible if the input data is corrupt. To avoid any chance of indexing + * off the end of memory and getting a bad-pointer trap, we perform the + * post-IDCT limiting thus: + * x = range_limit[x & MASK]; + * where MASK is 2 bits wider than legal sample data, ie 10 bits for 8-bit + * samples. Under normal circumstances this is more than enough range and + * a correct output will be generated; with bogus input data the mask will + * cause wraparound, and we will safely generate a bogus-but-in-range output. + * For the post-IDCT step, we want to convert the data from signed to unsigned + * representation by adding CENTERJSAMPLE at the same time that we limit it. + * So the post-IDCT limiting table ends up looking like this: + * CENTERJSAMPLE,CENTERJSAMPLE+1,...,MAXJSAMPLE, + * MAXJSAMPLE (repeat 2*(MAXJSAMPLE+1)-CENTERJSAMPLE times), + * 0 (repeat 2*(MAXJSAMPLE+1)-CENTERJSAMPLE times), + * 0,1,...,CENTERJSAMPLE-1 + * Negative inputs select values from the upper half of the table after + * masking. + * + * We can save some space by overlapping the start of the post-IDCT table + * with the simpler range limiting table. The post-IDCT table begins at + * sample_range_limit + CENTERJSAMPLE. + * + * Note that the table is allocated in near data space on PCs; it's small + * enough and used often enough to justify this. + */ + +LOCAL(void) +prepare_range_limit_table (j_decompress_ptr cinfo) +/* Allocate and fill in the sample_range_limit table */ +{ + JSAMPLE * table; + int i; + + table = (JSAMPLE *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (5 * (MAXJSAMPLE+1) + CENTERJSAMPLE) * SIZEOF(JSAMPLE)); + table += (MAXJSAMPLE+1); /* allow negative subscripts of simple table */ + cinfo->sample_range_limit = table; + /* First segment of "simple" table: limit[x] = 0 for x < 0 */ + MEMZERO(table - (MAXJSAMPLE+1), (MAXJSAMPLE+1) * SIZEOF(JSAMPLE)); + /* Main part of "simple" table: limit[x] = x */ + for (i = 0; i <= MAXJSAMPLE; i++) + table[i] = (JSAMPLE) i; + table += CENTERJSAMPLE; /* Point to where post-IDCT table starts */ + /* End of simple table, rest of first half of post-IDCT table */ + for (i = CENTERJSAMPLE; i < 2*(MAXJSAMPLE+1); i++) + table[i] = MAXJSAMPLE; + /* Second half of post-IDCT table */ + MEMZERO(table + (2 * (MAXJSAMPLE+1)), + (2 * (MAXJSAMPLE+1) - CENTERJSAMPLE) * SIZEOF(JSAMPLE)); + MEMCOPY(table + (4 * (MAXJSAMPLE+1) - CENTERJSAMPLE), + cinfo->sample_range_limit, CENTERJSAMPLE * SIZEOF(JSAMPLE)); +} + + +/* + * Master selection of decompression modules. + * This is done once at jpeg_start_decompress time. We determine + * which modules will be used and give them appropriate initialization calls. + * We also initialize the decompressor input side to begin consuming data. + * + * Since jpeg_read_header has finished, we know what is in the SOF + * and (first) SOS markers. We also have all the application parameter + * settings. + */ + +LOCAL(void) +master_selection (j_decompress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + boolean use_c_buffer; + long samplesperrow; + JDIMENSION jd_samplesperrow; + + /* Initialize dimensions and other stuff */ + jpeg_calc_output_dimensions(cinfo); + prepare_range_limit_table(cinfo); + + /* Width of an output scanline must be representable as JDIMENSION. */ + samplesperrow = (long) cinfo->output_width * (long) cinfo->out_color_components; + jd_samplesperrow = (JDIMENSION) samplesperrow; + if ((long) jd_samplesperrow != samplesperrow) + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + + /* Initialize my private state */ + master->pass_number = 0; + master->using_merged_upsample = use_merged_upsample(cinfo); + + /* Color quantizer selection */ + master->quantizer_1pass = NULL; + master->quantizer_2pass = NULL; + /* No mode changes if not using buffered-image mode. */ + if (! cinfo->quantize_colors || ! cinfo->buffered_image) { + cinfo->enable_1pass_quant = FALSE; + cinfo->enable_external_quant = FALSE; + cinfo->enable_2pass_quant = FALSE; + } + if (cinfo->quantize_colors) { + if (cinfo->raw_data_out) + ERREXIT(cinfo, JERR_NOTIMPL); + /* 2-pass quantizer only works in 3-component color space. */ + if (cinfo->out_color_components != 3) { + cinfo->enable_1pass_quant = TRUE; + cinfo->enable_external_quant = FALSE; + cinfo->enable_2pass_quant = FALSE; + cinfo->colormap = NULL; + } else if (cinfo->colormap != NULL) { + cinfo->enable_external_quant = TRUE; + } else if (cinfo->two_pass_quantize) { + cinfo->enable_2pass_quant = TRUE; + } else { + cinfo->enable_1pass_quant = TRUE; + } + + if (cinfo->enable_1pass_quant) { +#ifdef QUANT_1PASS_SUPPORTED + jinit_1pass_quantizer(cinfo); + master->quantizer_1pass = cinfo->cquantize; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } + + /* We use the 2-pass code to map to external colormaps. */ + if (cinfo->enable_2pass_quant || cinfo->enable_external_quant) { +#ifdef QUANT_2PASS_SUPPORTED + jinit_2pass_quantizer(cinfo); + master->quantizer_2pass = cinfo->cquantize; +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } + /* If both quantizers are initialized, the 2-pass one is left active; + * this is necessary for starting with quantization to an external map. + */ + } + + /* Post-processing: in particular, color conversion first */ + if (! cinfo->raw_data_out) { + if (master->using_merged_upsample) { +#ifdef UPSAMPLE_MERGING_SUPPORTED + jinit_merged_upsampler(cinfo); /* does color conversion too */ +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else { + jinit_color_deconverter(cinfo); + jinit_upsampler(cinfo); + } + jinit_d_post_controller(cinfo, cinfo->enable_2pass_quant); + } + /* Inverse DCT */ + jinit_inverse_dct(cinfo); + /* Entropy decoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + if (cinfo->progressive_mode) { +#ifdef D_PROGRESSIVE_SUPPORTED + jinit_phuff_decoder(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else + jinit_huff_decoder(cinfo); + } + + /* Initialize principal buffer controllers. */ + use_c_buffer = cinfo->inputctl->has_multiple_scans || cinfo->buffered_image; + jinit_d_coef_controller(cinfo, use_c_buffer); + + if (! cinfo->raw_data_out) + jinit_d_main_controller(cinfo, FALSE /* never need full buffer here */); + + /* We can now tell the memory manager to allocate virtual arrays. */ + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); + + /* Initialize input side of decompressor to consume first scan. */ + (*cinfo->inputctl->start_input_pass) (cinfo); + +#ifdef D_MULTISCAN_FILES_SUPPORTED + /* If jpeg_start_decompress will read the whole file, initialize + * progress monitoring appropriately. The input step is counted + * as one pass. + */ + if (cinfo->progress != NULL && ! cinfo->buffered_image && + cinfo->inputctl->has_multiple_scans) { + int nscans; + /* Estimate number of scans to set pass_limit. */ + if (cinfo->progressive_mode) { + /* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */ + nscans = 2 + 3 * cinfo->num_components; + } else { + /* For a nonprogressive multiscan file, estimate 1 scan per component. */ + nscans = cinfo->num_components; + } + cinfo->progress->pass_counter = 0L; + cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows * nscans; + cinfo->progress->completed_passes = 0; + cinfo->progress->total_passes = (cinfo->enable_2pass_quant ? 3 : 2); + /* Count the input pass as done */ + master->pass_number++; + } +#endif /* D_MULTISCAN_FILES_SUPPORTED */ +} + + +/* + * Per-pass setup. + * This is called at the beginning of each output pass. We determine which + * modules will be active during this pass and give them appropriate + * start_pass calls. We also set is_dummy_pass to indicate whether this + * is a "real" output pass or a dummy pass for color quantization. + * (In the latter case, jdapistd.c will crank the pass to completion.) + */ + +METHODDEF(void) +prepare_for_output_pass (j_decompress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + + if (master->pub.is_dummy_pass) { +#ifdef QUANT_2PASS_SUPPORTED + /* Final pass of 2-pass quantization */ + master->pub.is_dummy_pass = FALSE; + (*cinfo->cquantize->start_pass) (cinfo, FALSE); + (*cinfo->post->start_pass) (cinfo, JBUF_CRANK_DEST); + (*cinfo->main->start_pass) (cinfo, JBUF_CRANK_DEST); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif /* QUANT_2PASS_SUPPORTED */ + } else { + if (cinfo->quantize_colors && cinfo->colormap == NULL) { + /* Select new quantization method */ + if (cinfo->two_pass_quantize && cinfo->enable_2pass_quant) { + cinfo->cquantize = master->quantizer_2pass; + master->pub.is_dummy_pass = TRUE; + } else if (cinfo->enable_1pass_quant) { + cinfo->cquantize = master->quantizer_1pass; + } else { + ERREXIT(cinfo, JERR_MODE_CHANGE); + } + } + (*cinfo->idct->start_pass) (cinfo); + (*cinfo->coef->start_output_pass) (cinfo); + if (! cinfo->raw_data_out) { + if (! master->using_merged_upsample) + (*cinfo->cconvert->start_pass) (cinfo); + (*cinfo->upsample->start_pass) (cinfo); + if (cinfo->quantize_colors) + (*cinfo->cquantize->start_pass) (cinfo, master->pub.is_dummy_pass); + (*cinfo->post->start_pass) (cinfo, + (master->pub.is_dummy_pass ? JBUF_SAVE_AND_PASS : JBUF_PASS_THRU)); + (*cinfo->main->start_pass) (cinfo, JBUF_PASS_THRU); + } + } + + /* Set up progress monitor's pass info if present */ + if (cinfo->progress != NULL) { + cinfo->progress->completed_passes = master->pass_number; + cinfo->progress->total_passes = master->pass_number + + (master->pub.is_dummy_pass ? 2 : 1); + /* In buffered-image mode, we assume one more output pass if EOI not + * yet reached, but no more passes if EOI has been reached. + */ + if (cinfo->buffered_image && ! cinfo->inputctl->eoi_reached) { + cinfo->progress->total_passes += (cinfo->enable_2pass_quant ? 2 : 1); + } + } +} + + +/* + * Finish up at end of an output pass. + */ + +METHODDEF(void) +finish_output_pass (j_decompress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + + if (cinfo->quantize_colors) + (*cinfo->cquantize->finish_pass) (cinfo); + master->pass_number++; +} + + +#ifdef D_MULTISCAN_FILES_SUPPORTED + +/* + * Switch to a new external colormap between output passes. + */ + +GLOBAL(void) +jpeg_new_colormap (j_decompress_ptr cinfo) +{ + my_master_ptr master = (my_master_ptr) cinfo->master; + + /* Prevent application from calling me at wrong times */ + if (cinfo->global_state != DSTATE_BUFIMAGE) + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + + if (cinfo->quantize_colors && cinfo->enable_external_quant && + cinfo->colormap != NULL) { + /* Select 2-pass quantizer for external colormap use */ + cinfo->cquantize = master->quantizer_2pass; + /* Notify quantizer of colormap change */ + (*cinfo->cquantize->new_color_map) (cinfo); + master->pub.is_dummy_pass = FALSE; /* just in case */ + } else + ERREXIT(cinfo, JERR_MODE_CHANGE); +} + +#endif /* D_MULTISCAN_FILES_SUPPORTED */ + + +/* + * Initialize master decompression control and select active modules. + * This is performed at the start of jpeg_start_decompress. + */ + +GLOBAL(void) +jinit_master_decompress (j_decompress_ptr cinfo) +{ + my_master_ptr master; + + master = (my_master_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_decomp_master)); + cinfo->master = (struct jpeg_decomp_master *) master; + master->pub.prepare_for_output_pass = prepare_for_output_pass; + master->pub.finish_output_pass = finish_output_pass; + + master->pub.is_dummy_pass = FALSE; + + master_selection(cinfo); +} diff --git a/Engine/lib/ljpeg/jdmerge.c b/Engine/lib/ljpeg/jdmerge.c new file mode 100644 index 000000000..37444468c --- /dev/null +++ b/Engine/lib/ljpeg/jdmerge.c @@ -0,0 +1,400 @@ +/* + * jdmerge.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains code for merged upsampling/color conversion. + * + * This file combines functions from jdsample.c and jdcolor.c; + * read those files first to understand what's going on. + * + * When the chroma components are to be upsampled by simple replication + * (ie, box filtering), we can save some work in color conversion by + * calculating all the output pixels corresponding to a pair of chroma + * samples at one time. In the conversion equations + * R = Y + K1 * Cr + * G = Y + K2 * Cb + K3 * Cr + * B = Y + K4 * Cb + * only the Y term varies among the group of pixels corresponding to a pair + * of chroma samples, so the rest of the terms can be calculated just once. + * At typical sampling ratios, this eliminates half or three-quarters of the + * multiplications needed for color conversion. + * + * This file currently provides implementations for the following cases: + * YCbCr => RGB color conversion only. + * Sampling ratios of 2h1v or 2h2v. + * No scaling needed at upsample time. + * Corner-aligned (non-CCIR601) sampling alignment. + * Other special cases could be added, but in most applications these are + * the only common cases. (For uncommon cases we fall back on the more + * general code in jdsample.c and jdcolor.c.) + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + +#ifdef UPSAMPLE_MERGING_SUPPORTED + + +/* Private subobject */ + +typedef struct { + struct jpeg_upsampler pub; /* public fields */ + + /* Pointer to routine to do actual upsampling/conversion of one row group */ + JMETHOD(void, upmethod, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, + JSAMPARRAY output_buf)); + + /* Private state for YCC->RGB conversion */ + int * Cr_r_tab; /* => table for Cr to R conversion */ + int * Cb_b_tab; /* => table for Cb to B conversion */ + INT32 * Cr_g_tab; /* => table for Cr to G conversion */ + INT32 * Cb_g_tab; /* => table for Cb to G conversion */ + + /* For 2:1 vertical sampling, we produce two output rows at a time. + * We need a "spare" row buffer to hold the second output row if the + * application provides just a one-row buffer; we also use the spare + * to discard the dummy last row if the image height is odd. + */ + JSAMPROW spare_row; + boolean spare_full; /* T if spare buffer is occupied */ + + JDIMENSION out_row_width; /* samples per output row */ + JDIMENSION rows_to_go; /* counts rows remaining in image */ +} my_upsampler; + +typedef my_upsampler * my_upsample_ptr; + +#define SCALEBITS 16 /* speediest right-shift on some machines */ +#define ONE_HALF ((INT32) 1 << (SCALEBITS-1)) +#define FIX(x) ((INT32) ((x) * (1L<RGB colorspace conversion. + * This is taken directly from jdcolor.c; see that file for more info. + */ + +LOCAL(void) +build_ycc_rgb_table (j_decompress_ptr cinfo) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + int i; + INT32 x; + SHIFT_TEMPS + + upsample->Cr_r_tab = (int *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(int)); + upsample->Cb_b_tab = (int *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(int)); + upsample->Cr_g_tab = (INT32 *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(INT32)); + upsample->Cb_g_tab = (INT32 *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (MAXJSAMPLE+1) * SIZEOF(INT32)); + + for (i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) { + /* i is the actual input pixel value, in the range 0..MAXJSAMPLE */ + /* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */ + /* Cr=>R value is nearest int to 1.40200 * x */ + upsample->Cr_r_tab[i] = (int) + RIGHT_SHIFT(FIX(1.40200) * x + ONE_HALF, SCALEBITS); + /* Cb=>B value is nearest int to 1.77200 * x */ + upsample->Cb_b_tab[i] = (int) + RIGHT_SHIFT(FIX(1.77200) * x + ONE_HALF, SCALEBITS); + /* Cr=>G value is scaled-up -0.71414 * x */ + upsample->Cr_g_tab[i] = (- FIX(0.71414)) * x; + /* Cb=>G value is scaled-up -0.34414 * x */ + /* We also add in ONE_HALF so that need not do it in inner loop */ + upsample->Cb_g_tab[i] = (- FIX(0.34414)) * x + ONE_HALF; + } +} + + +/* + * Initialize for an upsampling pass. + */ + +METHODDEF(void) +start_pass_merged_upsample (j_decompress_ptr cinfo) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + + /* Mark the spare buffer empty */ + upsample->spare_full = FALSE; + /* Initialize total-height counter for detecting bottom of image */ + upsample->rows_to_go = cinfo->output_height; +} + + +/* + * Control routine to do upsampling (and color conversion). + * + * The control routine just handles the row buffering considerations. + */ + +METHODDEF(void) +merged_2v_upsample (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +/* 2:1 vertical sampling case: may need a spare row. */ +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + JSAMPROW work_ptrs[2]; + JDIMENSION num_rows; /* number of rows returned to caller */ + + if (upsample->spare_full) { + /* If we have a spare row saved from a previous cycle, just return it. */ + jcopy_sample_rows(& upsample->spare_row, 0, output_buf + *out_row_ctr, 0, + 1, upsample->out_row_width); + num_rows = 1; + upsample->spare_full = FALSE; + } else { + /* Figure number of rows to return to caller. */ + num_rows = 2; + /* Not more than the distance to the end of the image. */ + if (num_rows > upsample->rows_to_go) + num_rows = upsample->rows_to_go; + /* And not more than what the client can accept: */ + out_rows_avail -= *out_row_ctr; + if (num_rows > out_rows_avail) + num_rows = out_rows_avail; + /* Create output pointer array for upsampler. */ + work_ptrs[0] = output_buf[*out_row_ctr]; + if (num_rows > 1) { + work_ptrs[1] = output_buf[*out_row_ctr + 1]; + } else { + work_ptrs[1] = upsample->spare_row; + upsample->spare_full = TRUE; + } + /* Now do the upsampling. */ + (*upsample->upmethod) (cinfo, input_buf, *in_row_group_ctr, work_ptrs); + } + + /* Adjust counts */ + *out_row_ctr += num_rows; + upsample->rows_to_go -= num_rows; + /* When the buffer is emptied, declare this input row group consumed */ + if (! upsample->spare_full) + (*in_row_group_ctr)++; +} + + +METHODDEF(void) +merged_1v_upsample (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +/* 1:1 vertical sampling case: much easier, never need a spare row. */ +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + + /* Just do the upsampling. */ + (*upsample->upmethod) (cinfo, input_buf, *in_row_group_ctr, + output_buf + *out_row_ctr); + /* Adjust counts */ + (*out_row_ctr)++; + (*in_row_group_ctr)++; +} + + +/* + * These are the routines invoked by the control routines to do + * the actual upsampling/conversion. One row group is processed per call. + * + * Note: since we may be writing directly into application-supplied buffers, + * we have to be honest about the output width; we can't assume the buffer + * has been rounded up to an even width. + */ + + +/* + * Upsample and color convert for the case of 2:1 horizontal and 1:1 vertical. + */ + +METHODDEF(void) +h2v1_merged_upsample (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, + JSAMPARRAY output_buf) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + register int y, cred, cgreen, cblue; + int cb, cr; + register JSAMPROW outptr; + JSAMPROW inptr0, inptr1, inptr2; + JDIMENSION col; + /* copy these pointers into registers if possible */ + register JSAMPLE * range_limit = cinfo->sample_range_limit; + int * Crrtab = upsample->Cr_r_tab; + int * Cbbtab = upsample->Cb_b_tab; + INT32 * Crgtab = upsample->Cr_g_tab; + INT32 * Cbgtab = upsample->Cb_g_tab; + SHIFT_TEMPS + + inptr0 = input_buf[0][in_row_group_ctr]; + inptr1 = input_buf[1][in_row_group_ctr]; + inptr2 = input_buf[2][in_row_group_ctr]; + outptr = output_buf[0]; + /* Loop for each pair of output pixels */ + for (col = cinfo->output_width >> 1; col > 0; col--) { + /* Do the chroma part of the calculation */ + cb = GETJSAMPLE(*inptr1++); + cr = GETJSAMPLE(*inptr2++); + cred = Crrtab[cr]; + cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); + cblue = Cbbtab[cb]; + /* Fetch 2 Y values and emit 2 pixels */ + y = GETJSAMPLE(*inptr0++); + outptr[RGB_RED] = range_limit[y + cred]; + outptr[RGB_GREEN] = range_limit[y + cgreen]; + outptr[RGB_BLUE] = range_limit[y + cblue]; + outptr += RGB_PIXELSIZE; + y = GETJSAMPLE(*inptr0++); + outptr[RGB_RED] = range_limit[y + cred]; + outptr[RGB_GREEN] = range_limit[y + cgreen]; + outptr[RGB_BLUE] = range_limit[y + cblue]; + outptr += RGB_PIXELSIZE; + } + /* If image width is odd, do the last output column separately */ + if (cinfo->output_width & 1) { + cb = GETJSAMPLE(*inptr1); + cr = GETJSAMPLE(*inptr2); + cred = Crrtab[cr]; + cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); + cblue = Cbbtab[cb]; + y = GETJSAMPLE(*inptr0); + outptr[RGB_RED] = range_limit[y + cred]; + outptr[RGB_GREEN] = range_limit[y + cgreen]; + outptr[RGB_BLUE] = range_limit[y + cblue]; + } +} + + +/* + * Upsample and color convert for the case of 2:1 horizontal and 2:1 vertical. + */ + +METHODDEF(void) +h2v2_merged_upsample (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, + JSAMPARRAY output_buf) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + register int y, cred, cgreen, cblue; + int cb, cr; + register JSAMPROW outptr0, outptr1; + JSAMPROW inptr00, inptr01, inptr1, inptr2; + JDIMENSION col; + /* copy these pointers into registers if possible */ + register JSAMPLE * range_limit = cinfo->sample_range_limit; + int * Crrtab = upsample->Cr_r_tab; + int * Cbbtab = upsample->Cb_b_tab; + INT32 * Crgtab = upsample->Cr_g_tab; + INT32 * Cbgtab = upsample->Cb_g_tab; + SHIFT_TEMPS + + inptr00 = input_buf[0][in_row_group_ctr*2]; + inptr01 = input_buf[0][in_row_group_ctr*2 + 1]; + inptr1 = input_buf[1][in_row_group_ctr]; + inptr2 = input_buf[2][in_row_group_ctr]; + outptr0 = output_buf[0]; + outptr1 = output_buf[1]; + /* Loop for each group of output pixels */ + for (col = cinfo->output_width >> 1; col > 0; col--) { + /* Do the chroma part of the calculation */ + cb = GETJSAMPLE(*inptr1++); + cr = GETJSAMPLE(*inptr2++); + cred = Crrtab[cr]; + cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); + cblue = Cbbtab[cb]; + /* Fetch 4 Y values and emit 4 pixels */ + y = GETJSAMPLE(*inptr00++); + outptr0[RGB_RED] = range_limit[y + cred]; + outptr0[RGB_GREEN] = range_limit[y + cgreen]; + outptr0[RGB_BLUE] = range_limit[y + cblue]; + outptr0 += RGB_PIXELSIZE; + y = GETJSAMPLE(*inptr00++); + outptr0[RGB_RED] = range_limit[y + cred]; + outptr0[RGB_GREEN] = range_limit[y + cgreen]; + outptr0[RGB_BLUE] = range_limit[y + cblue]; + outptr0 += RGB_PIXELSIZE; + y = GETJSAMPLE(*inptr01++); + outptr1[RGB_RED] = range_limit[y + cred]; + outptr1[RGB_GREEN] = range_limit[y + cgreen]; + outptr1[RGB_BLUE] = range_limit[y + cblue]; + outptr1 += RGB_PIXELSIZE; + y = GETJSAMPLE(*inptr01++); + outptr1[RGB_RED] = range_limit[y + cred]; + outptr1[RGB_GREEN] = range_limit[y + cgreen]; + outptr1[RGB_BLUE] = range_limit[y + cblue]; + outptr1 += RGB_PIXELSIZE; + } + /* If image width is odd, do the last output column separately */ + if (cinfo->output_width & 1) { + cb = GETJSAMPLE(*inptr1); + cr = GETJSAMPLE(*inptr2); + cred = Crrtab[cr]; + cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); + cblue = Cbbtab[cb]; + y = GETJSAMPLE(*inptr00); + outptr0[RGB_RED] = range_limit[y + cred]; + outptr0[RGB_GREEN] = range_limit[y + cgreen]; + outptr0[RGB_BLUE] = range_limit[y + cblue]; + y = GETJSAMPLE(*inptr01); + outptr1[RGB_RED] = range_limit[y + cred]; + outptr1[RGB_GREEN] = range_limit[y + cgreen]; + outptr1[RGB_BLUE] = range_limit[y + cblue]; + } +} + + +/* + * Module initialization routine for merged upsampling/color conversion. + * + * NB: this is called under the conditions determined by use_merged_upsample() + * in jdmaster.c. That routine MUST correspond to the actual capabilities + * of this module; no safety checks are made here. + */ + +GLOBAL(void) +jinit_merged_upsampler (j_decompress_ptr cinfo) +{ + my_upsample_ptr upsample; + + upsample = (my_upsample_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_upsampler)); + cinfo->upsample = (struct jpeg_upsampler *) upsample; + upsample->pub.start_pass = start_pass_merged_upsample; + upsample->pub.need_context_rows = FALSE; + + upsample->out_row_width = cinfo->output_width * cinfo->out_color_components; + + if (cinfo->max_v_samp_factor == 2) { + upsample->pub.upsample = merged_2v_upsample; + upsample->upmethod = h2v2_merged_upsample; + /* Allocate a spare row buffer */ + upsample->spare_row = (JSAMPROW) + (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, + (size_t) (upsample->out_row_width * SIZEOF(JSAMPLE))); + } else { + upsample->pub.upsample = merged_1v_upsample; + upsample->upmethod = h2v1_merged_upsample; + /* No spare row needed */ + upsample->spare_row = NULL; + } + + build_ycc_rgb_table(cinfo); +} + +#endif /* UPSAMPLE_MERGING_SUPPORTED */ diff --git a/Engine/lib/ljpeg/jdphuff.c b/Engine/lib/ljpeg/jdphuff.c new file mode 100644 index 000000000..226780994 --- /dev/null +++ b/Engine/lib/ljpeg/jdphuff.c @@ -0,0 +1,668 @@ +/* + * jdphuff.c + * + * Copyright (C) 1995-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains Huffman entropy decoding routines for progressive JPEG. + * + * Much of the complexity here has to do with supporting input suspension. + * If the data source module demands suspension, we want to be able to back + * up to the start of the current MCU. To do this, we copy state variables + * into local working storage, and update them back to the permanent + * storage only upon successful completion of an MCU. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdhuff.h" /* Declarations shared with jdhuff.c */ + + +#ifdef D_PROGRESSIVE_SUPPORTED + +/* + * Expanded entropy decoder object for progressive Huffman decoding. + * + * The savable_state subrecord contains fields that change within an MCU, + * but must not be updated permanently until we complete the MCU. + */ + +typedef struct { + unsigned int EOBRUN; /* remaining EOBs in EOBRUN */ + int last_dc_val[MAX_COMPS_IN_SCAN]; /* last DC coef for each component */ +} savable_state; + +/* This macro is to work around compilers with missing or broken + * structure assignment. You'll need to fix this code if you have + * such a compiler and you change MAX_COMPS_IN_SCAN. + */ + +#ifndef NO_STRUCT_ASSIGN +#define ASSIGN_STATE(dest,src) ((dest) = (src)) +#else +#if MAX_COMPS_IN_SCAN == 4 +#define ASSIGN_STATE(dest,src) \ + ((dest).EOBRUN = (src).EOBRUN, \ + (dest).last_dc_val[0] = (src).last_dc_val[0], \ + (dest).last_dc_val[1] = (src).last_dc_val[1], \ + (dest).last_dc_val[2] = (src).last_dc_val[2], \ + (dest).last_dc_val[3] = (src).last_dc_val[3]) +#endif +#endif + + +typedef struct { + struct jpeg_entropy_decoder pub; /* public fields */ + + /* These fields are loaded into local variables at start of each MCU. + * In case of suspension, we exit WITHOUT updating them. + */ + bitread_perm_state bitstate; /* Bit buffer at start of MCU */ + savable_state saved; /* Other state at start of MCU */ + + /* These fields are NOT loaded into local working state. */ + unsigned int restarts_to_go; /* MCUs left in this restart interval */ + + /* Pointers to derived tables (these workspaces have image lifespan) */ + d_derived_tbl * derived_tbls[NUM_HUFF_TBLS]; + + d_derived_tbl * ac_derived_tbl; /* active table during an AC scan */ +} phuff_entropy_decoder; + +typedef phuff_entropy_decoder * phuff_entropy_ptr; + +/* Forward declarations */ +METHODDEF(boolean) decode_mcu_DC_first JPP((j_decompress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(boolean) decode_mcu_AC_first JPP((j_decompress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(boolean) decode_mcu_DC_refine JPP((j_decompress_ptr cinfo, + JBLOCKROW *MCU_data)); +METHODDEF(boolean) decode_mcu_AC_refine JPP((j_decompress_ptr cinfo, + JBLOCKROW *MCU_data)); + + +/* + * Initialize for a Huffman-compressed scan. + */ + +METHODDEF(void) +start_pass_phuff_decoder (j_decompress_ptr cinfo) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + boolean is_DC_band, bad; + int ci, coefi, tbl; + int *coef_bit_ptr; + jpeg_component_info * compptr; + + is_DC_band = (cinfo->Ss == 0); + + /* Validate scan parameters */ + bad = FALSE; + if (is_DC_band) { + if (cinfo->Se != 0) + bad = TRUE; + } else { + /* need not check Ss/Se < 0 since they came from unsigned bytes */ + if (cinfo->Ss > cinfo->Se || cinfo->Se >= DCTSIZE2) + bad = TRUE; + /* AC scans may have only one component */ + if (cinfo->comps_in_scan != 1) + bad = TRUE; + } + if (cinfo->Ah != 0) { + /* Successive approximation refinement scan: must have Al = Ah-1. */ + if (cinfo->Al != cinfo->Ah-1) + bad = TRUE; + } + if (cinfo->Al > 13) /* need not check for < 0 */ + bad = TRUE; + /* Arguably the maximum Al value should be less than 13 for 8-bit precision, + * but the spec doesn't say so, and we try to be liberal about what we + * accept. Note: large Al values could result in out-of-range DC + * coefficients during early scans, leading to bizarre displays due to + * overflows in the IDCT math. But we won't crash. + */ + if (bad) + ERREXIT4(cinfo, JERR_BAD_PROGRESSION, + cinfo->Ss, cinfo->Se, cinfo->Ah, cinfo->Al); + /* Update progression status, and verify that scan order is legal. + * Note that inter-scan inconsistencies are treated as warnings + * not fatal errors ... not clear if this is right way to behave. + */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + int cindex = cinfo->cur_comp_info[ci]->component_index; + coef_bit_ptr = & cinfo->coef_bits[cindex][0]; + if (!is_DC_band && coef_bit_ptr[0] < 0) /* AC without prior DC scan */ + WARNMS2(cinfo, JWRN_BOGUS_PROGRESSION, cindex, 0); + for (coefi = cinfo->Ss; coefi <= cinfo->Se; coefi++) { + int expected = (coef_bit_ptr[coefi] < 0) ? 0 : coef_bit_ptr[coefi]; + if (cinfo->Ah != expected) + WARNMS2(cinfo, JWRN_BOGUS_PROGRESSION, cindex, coefi); + coef_bit_ptr[coefi] = cinfo->Al; + } + } + + /* Select MCU decoding routine */ + if (cinfo->Ah == 0) { + if (is_DC_band) + entropy->pub.decode_mcu = decode_mcu_DC_first; + else + entropy->pub.decode_mcu = decode_mcu_AC_first; + } else { + if (is_DC_band) + entropy->pub.decode_mcu = decode_mcu_DC_refine; + else + entropy->pub.decode_mcu = decode_mcu_AC_refine; + } + + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* Make sure requested tables are present, and compute derived tables. + * We may build same derived table more than once, but it's not expensive. + */ + if (is_DC_band) { + if (cinfo->Ah == 0) { /* DC refinement needs no table */ + tbl = compptr->dc_tbl_no; + jpeg_make_d_derived_tbl(cinfo, TRUE, tbl, + & entropy->derived_tbls[tbl]); + } + } else { + tbl = compptr->ac_tbl_no; + jpeg_make_d_derived_tbl(cinfo, FALSE, tbl, + & entropy->derived_tbls[tbl]); + /* remember the single active table */ + entropy->ac_derived_tbl = entropy->derived_tbls[tbl]; + } + /* Initialize DC predictions to 0 */ + entropy->saved.last_dc_val[ci] = 0; + } + + /* Initialize bitread state variables */ + entropy->bitstate.bits_left = 0; + entropy->bitstate.get_buffer = 0; /* unnecessary, but keeps Purify quiet */ + entropy->pub.insufficient_data = FALSE; + + /* Initialize private state variables */ + entropy->saved.EOBRUN = 0; + + /* Initialize restart counter */ + entropy->restarts_to_go = cinfo->restart_interval; +} + + +/* + * Figure F.12: extend sign bit. + * On some machines, a shift and add will be faster than a table lookup. + */ + +#ifdef AVOID_TABLES + +#define HUFF_EXTEND(x,s) ((x) < (1<<((s)-1)) ? (x) + (((-1)<<(s)) + 1) : (x)) + +#else + +#define HUFF_EXTEND(x,s) ((x) < extend_test[s] ? (x) + extend_offset[s] : (x)) + +static const int extend_test[16] = /* entry n is 2**(n-1) */ + { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, + 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 }; + +static const int extend_offset[16] = /* entry n is (-1 << n) + 1 */ + { 0, ((-1)<<1) + 1, ((-1)<<2) + 1, ((-1)<<3) + 1, ((-1)<<4) + 1, + ((-1)<<5) + 1, ((-1)<<6) + 1, ((-1)<<7) + 1, ((-1)<<8) + 1, + ((-1)<<9) + 1, ((-1)<<10) + 1, ((-1)<<11) + 1, ((-1)<<12) + 1, + ((-1)<<13) + 1, ((-1)<<14) + 1, ((-1)<<15) + 1 }; + +#endif /* AVOID_TABLES */ + + +/* + * Check for a restart marker & resynchronize decoder. + * Returns FALSE if must suspend. + */ + +LOCAL(boolean) +process_restart (j_decompress_ptr cinfo) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + int ci; + + /* Throw away any unused bits remaining in bit buffer; */ + /* include any full bytes in next_marker's count of discarded bytes */ + cinfo->marker->discarded_bytes += entropy->bitstate.bits_left / 8; + entropy->bitstate.bits_left = 0; + + /* Advance past the RSTn marker */ + if (! (*cinfo->marker->read_restart_marker) (cinfo)) + return FALSE; + + /* Re-initialize DC predictions to 0 */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) + entropy->saved.last_dc_val[ci] = 0; + /* Re-init EOB run count, too */ + entropy->saved.EOBRUN = 0; + + /* Reset restart counter */ + entropy->restarts_to_go = cinfo->restart_interval; + + /* Reset out-of-data flag, unless read_restart_marker left us smack up + * against a marker. In that case we will end up treating the next data + * segment as empty, and we can avoid producing bogus output pixels by + * leaving the flag set. + */ + if (cinfo->unread_marker == 0) + entropy->pub.insufficient_data = FALSE; + + return TRUE; +} + + +/* + * Huffman MCU decoding. + * Each of these routines decodes and returns one MCU's worth of + * Huffman-compressed coefficients. + * The coefficients are reordered from zigzag order into natural array order, + * but are not dequantized. + * + * The i'th block of the MCU is stored into the block pointed to by + * MCU_data[i]. WE ASSUME THIS AREA IS INITIALLY ZEROED BY THE CALLER. + * + * We return FALSE if data source requested suspension. In that case no + * changes have been made to permanent state. (Exception: some output + * coefficients may already have been assigned. This is harmless for + * spectral selection, since we'll just re-assign them on the next call. + * Successive approximation AC refinement has to be more careful, however.) + */ + +/* + * MCU decoding for DC initial scan (either spectral selection, + * or first pass of successive approximation). + */ + +METHODDEF(boolean) +decode_mcu_DC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + int Al = cinfo->Al; + register int s, r; + int blkn, ci; + JBLOCKROW block; + BITREAD_STATE_VARS; + savable_state state; + d_derived_tbl * tbl; + jpeg_component_info * compptr; + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! process_restart(cinfo)) + return FALSE; + } + + /* If we've run out of data, just leave the MCU set to zeroes. + * This way, we return uniform gray for the remainder of the segment. + */ + if (! entropy->pub.insufficient_data) { + + /* Load up working state */ + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(state, entropy->saved); + + /* Outer loop handles each block in the MCU */ + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + block = MCU_data[blkn]; + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + tbl = entropy->derived_tbls[compptr->dc_tbl_no]; + + /* Decode a single block's worth of coefficients */ + + /* Section F.2.2.1: decode the DC coefficient difference */ + HUFF_DECODE(s, br_state, tbl, return FALSE, label1); + if (s) { + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + } + + /* Convert DC difference to actual value, update last_dc_val */ + s += state.last_dc_val[ci]; + state.last_dc_val[ci] = s; + /* Scale and output the coefficient (assumes jpeg_natural_order[0]=0) */ + (*block)[0] = (JCOEF) (s << Al); + } + + /* Completed MCU, so update state */ + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + ASSIGN_STATE(entropy->saved, state); + } + + /* Account for restart interval (no-op if not using restarts) */ + entropy->restarts_to_go--; + + return TRUE; +} + + +/* + * MCU decoding for AC initial scan (either spectral selection, + * or first pass of successive approximation). + */ + +METHODDEF(boolean) +decode_mcu_AC_first (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + int Se = cinfo->Se; + int Al = cinfo->Al; + register int s, k, r; + unsigned int EOBRUN; + JBLOCKROW block; + BITREAD_STATE_VARS; + d_derived_tbl * tbl; + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! process_restart(cinfo)) + return FALSE; + } + + /* If we've run out of data, just leave the MCU set to zeroes. + * This way, we return uniform gray for the remainder of the segment. + */ + if (! entropy->pub.insufficient_data) { + + /* Load up working state. + * We can avoid loading/saving bitread state if in an EOB run. + */ + EOBRUN = entropy->saved.EOBRUN; /* only part of saved state we need */ + + /* There is always only one block per MCU */ + + if (EOBRUN > 0) /* if it's a band of zeroes... */ + EOBRUN--; /* ...process it now (we do nothing) */ + else { + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + block = MCU_data[0]; + tbl = entropy->ac_derived_tbl; + + for (k = cinfo->Ss; k <= Se; k++) { + HUFF_DECODE(s, br_state, tbl, return FALSE, label2); + r = s >> 4; + s &= 15; + if (s) { + k += r; + CHECK_BIT_BUFFER(br_state, s, return FALSE); + r = GET_BITS(s); + s = HUFF_EXTEND(r, s); + /* Scale and output coefficient in natural (dezigzagged) order */ + (*block)[jpeg_natural_order[k]] = (JCOEF) (s << Al); + } else { + if (r == 15) { /* ZRL */ + k += 15; /* skip 15 zeroes in band */ + } else { /* EOBr, run length is 2^r + appended bits */ + EOBRUN = 1 << r; + if (r) { /* EOBr, r > 0 */ + CHECK_BIT_BUFFER(br_state, r, return FALSE); + r = GET_BITS(r); + EOBRUN += r; + } + EOBRUN--; /* this band is processed at this moment */ + break; /* force end-of-band */ + } + } + } + + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + } + + /* Completed MCU, so update state */ + entropy->saved.EOBRUN = EOBRUN; /* only part of saved state we need */ + } + + /* Account for restart interval (no-op if not using restarts) */ + entropy->restarts_to_go--; + + return TRUE; +} + + +/* + * MCU decoding for DC successive approximation refinement scan. + * Note: we assume such scans can be multi-component, although the spec + * is not very clear on the point. + */ + +METHODDEF(boolean) +decode_mcu_DC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + int p1 = 1 << cinfo->Al; /* 1 in the bit position being coded */ + int blkn; + JBLOCKROW block; + BITREAD_STATE_VARS; + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! process_restart(cinfo)) + return FALSE; + } + + /* Not worth the cycles to check insufficient_data here, + * since we will not change the data anyway if we read zeroes. + */ + + /* Load up working state */ + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + + /* Outer loop handles each block in the MCU */ + + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + block = MCU_data[blkn]; + + /* Encoded data is simply the next bit of the two's-complement DC value */ + CHECK_BIT_BUFFER(br_state, 1, return FALSE); + if (GET_BITS(1)) + (*block)[0] |= p1; + /* Note: since we use |=, repeating the assignment later is safe */ + } + + /* Completed MCU, so update state */ + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + + /* Account for restart interval (no-op if not using restarts) */ + entropy->restarts_to_go--; + + return TRUE; +} + + +/* + * MCU decoding for AC successive approximation refinement scan. + */ + +METHODDEF(boolean) +decode_mcu_AC_refine (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) +{ + phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; + int Se = cinfo->Se; + int p1 = 1 << cinfo->Al; /* 1 in the bit position being coded */ + int m1 = (-1) << cinfo->Al; /* -1 in the bit position being coded */ + register int s, k, r; + unsigned int EOBRUN; + JBLOCKROW block; + JCOEFPTR thiscoef; + BITREAD_STATE_VARS; + d_derived_tbl * tbl; + int num_newnz; + int newnz_pos[DCTSIZE2]; + + /* Process restart marker if needed; may have to suspend */ + if (cinfo->restart_interval) { + if (entropy->restarts_to_go == 0) + if (! process_restart(cinfo)) + return FALSE; + } + + /* If we've run out of data, don't modify the MCU. + */ + if (! entropy->pub.insufficient_data) { + + /* Load up working state */ + BITREAD_LOAD_STATE(cinfo,entropy->bitstate); + EOBRUN = entropy->saved.EOBRUN; /* only part of saved state we need */ + + /* There is always only one block per MCU */ + block = MCU_data[0]; + tbl = entropy->ac_derived_tbl; + + /* If we are forced to suspend, we must undo the assignments to any newly + * nonzero coefficients in the block, because otherwise we'd get confused + * next time about which coefficients were already nonzero. + * But we need not undo addition of bits to already-nonzero coefficients; + * instead, we can test the current bit to see if we already did it. + */ + num_newnz = 0; + + /* initialize coefficient loop counter to start of band */ + k = cinfo->Ss; + + if (EOBRUN == 0) { + for (; k <= Se; k++) { + HUFF_DECODE(s, br_state, tbl, goto undoit, label3); + r = s >> 4; + s &= 15; + if (s) { + if (s != 1) /* size of new coef should always be 1 */ + WARNMS(cinfo, JWRN_HUFF_BAD_CODE); + CHECK_BIT_BUFFER(br_state, 1, goto undoit); + if (GET_BITS(1)) + s = p1; /* newly nonzero coef is positive */ + else + s = m1; /* newly nonzero coef is negative */ + } else { + if (r != 15) { + EOBRUN = 1 << r; /* EOBr, run length is 2^r + appended bits */ + if (r) { + CHECK_BIT_BUFFER(br_state, r, goto undoit); + r = GET_BITS(r); + EOBRUN += r; + } + break; /* rest of block is handled by EOB logic */ + } + /* note s = 0 for processing ZRL */ + } + /* Advance over already-nonzero coefs and r still-zero coefs, + * appending correction bits to the nonzeroes. A correction bit is 1 + * if the absolute value of the coefficient must be increased. + */ + do { + thiscoef = *block + jpeg_natural_order[k]; + if (*thiscoef != 0) { + CHECK_BIT_BUFFER(br_state, 1, goto undoit); + if (GET_BITS(1)) { + if ((*thiscoef & p1) == 0) { /* do nothing if already set it */ + if (*thiscoef >= 0) + *thiscoef += p1; + else + *thiscoef += m1; + } + } + } else { + if (--r < 0) + break; /* reached target zero coefficient */ + } + k++; + } while (k <= Se); + if (s) { + int pos = jpeg_natural_order[k]; + /* Output newly nonzero coefficient */ + (*block)[pos] = (JCOEF) s; + /* Remember its position in case we have to suspend */ + newnz_pos[num_newnz++] = pos; + } + } + } + + if (EOBRUN > 0) { + /* Scan any remaining coefficient positions after the end-of-band + * (the last newly nonzero coefficient, if any). Append a correction + * bit to each already-nonzero coefficient. A correction bit is 1 + * if the absolute value of the coefficient must be increased. + */ + for (; k <= Se; k++) { + thiscoef = *block + jpeg_natural_order[k]; + if (*thiscoef != 0) { + CHECK_BIT_BUFFER(br_state, 1, goto undoit); + if (GET_BITS(1)) { + if ((*thiscoef & p1) == 0) { /* do nothing if already changed it */ + if (*thiscoef >= 0) + *thiscoef += p1; + else + *thiscoef += m1; + } + } + } + } + /* Count one block completed in EOB run */ + EOBRUN--; + } + + /* Completed MCU, so update state */ + BITREAD_SAVE_STATE(cinfo,entropy->bitstate); + entropy->saved.EOBRUN = EOBRUN; /* only part of saved state we need */ + } + + /* Account for restart interval (no-op if not using restarts) */ + entropy->restarts_to_go--; + + return TRUE; + +undoit: + /* Re-zero any output coefficients that we made newly nonzero */ + while (num_newnz > 0) + (*block)[newnz_pos[--num_newnz]] = 0; + + return FALSE; +} + + +/* + * Module initialization routine for progressive Huffman entropy decoding. + */ + +GLOBAL(void) +jinit_phuff_decoder (j_decompress_ptr cinfo) +{ + phuff_entropy_ptr entropy; + int *coef_bit_ptr; + int ci, i; + + entropy = (phuff_entropy_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(phuff_entropy_decoder)); + cinfo->entropy = (struct jpeg_entropy_decoder *) entropy; + entropy->pub.start_pass = start_pass_phuff_decoder; + + /* Mark derived tables unallocated */ + for (i = 0; i < NUM_HUFF_TBLS; i++) { + entropy->derived_tbls[i] = NULL; + } + + /* Create progression status table */ + cinfo->coef_bits = (int (*)[DCTSIZE2]) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->num_components*DCTSIZE2*SIZEOF(int)); + coef_bit_ptr = & cinfo->coef_bits[0][0]; + for (ci = 0; ci < cinfo->num_components; ci++) + for (i = 0; i < DCTSIZE2; i++) + *coef_bit_ptr++ = -1; +} + +#endif /* D_PROGRESSIVE_SUPPORTED */ diff --git a/Engine/lib/ljpeg/jdpostct.c b/Engine/lib/ljpeg/jdpostct.c new file mode 100644 index 000000000..571563d72 --- /dev/null +++ b/Engine/lib/ljpeg/jdpostct.c @@ -0,0 +1,290 @@ +/* + * jdpostct.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the decompression postprocessing controller. + * This controller manages the upsampling, color conversion, and color + * quantization/reduction steps; specifically, it controls the buffering + * between upsample/color conversion and color quantization/reduction. + * + * If no color quantization/reduction is required, then this module has no + * work to do, and it just hands off to the upsample/color conversion code. + * An integrated upsample/convert/quantize process would replace this module + * entirely. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_d_post_controller pub; /* public fields */ + + /* Color quantization source buffer: this holds output data from + * the upsample/color conversion step to be passed to the quantizer. + * For two-pass color quantization, we need a full-image buffer; + * for one-pass operation, a strip buffer is sufficient. + */ + jvirt_sarray_ptr whole_image; /* virtual array, or NULL if one-pass */ + JSAMPARRAY buffer; /* strip buffer, or current strip of virtual */ + JDIMENSION strip_height; /* buffer size in rows */ + /* for two-pass mode only: */ + JDIMENSION starting_row; /* row # of first row in current strip */ + JDIMENSION next_row; /* index of next row to fill/empty in strip */ +} my_post_controller; + +typedef my_post_controller * my_post_ptr; + + +/* Forward declarations */ +METHODDEF(void) post_process_1pass + JPP((j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +#ifdef QUANT_2PASS_SUPPORTED +METHODDEF(void) post_process_prepass + JPP((j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +METHODDEF(void) post_process_2pass + JPP((j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +#endif + + +/* + * Initialize for a processing pass. + */ + +METHODDEF(void) +start_pass_dpost (j_decompress_ptr cinfo, J_BUF_MODE pass_mode) +{ + my_post_ptr post = (my_post_ptr) cinfo->post; + + switch (pass_mode) { + case JBUF_PASS_THRU: + if (cinfo->quantize_colors) { + /* Single-pass processing with color quantization. */ + post->pub.post_process_data = post_process_1pass; + /* We could be doing buffered-image output before starting a 2-pass + * color quantization; in that case, jinit_d_post_controller did not + * allocate a strip buffer. Use the virtual-array buffer as workspace. + */ + if (post->buffer == NULL) { + post->buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, post->whole_image, + (JDIMENSION) 0, post->strip_height, TRUE); + } + } else { + /* For single-pass processing without color quantization, + * I have no work to do; just call the upsampler directly. + */ + post->pub.post_process_data = cinfo->upsample->upsample; + } + break; +#ifdef QUANT_2PASS_SUPPORTED + case JBUF_SAVE_AND_PASS: + /* First pass of 2-pass quantization */ + if (post->whole_image == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + post->pub.post_process_data = post_process_prepass; + break; + case JBUF_CRANK_DEST: + /* Second pass of 2-pass quantization */ + if (post->whole_image == NULL) + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + post->pub.post_process_data = post_process_2pass; + break; +#endif /* QUANT_2PASS_SUPPORTED */ + default: + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); + break; + } + post->starting_row = post->next_row = 0; +} + + +/* + * Process some data in the one-pass (strip buffer) case. + * This is used for color precision reduction as well as one-pass quantization. + */ + +METHODDEF(void) +post_process_1pass (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_post_ptr post = (my_post_ptr) cinfo->post; + JDIMENSION num_rows, max_rows; + + /* Fill the buffer, but not more than what we can dump out in one go. */ + /* Note we rely on the upsampler to detect bottom of image. */ + max_rows = out_rows_avail - *out_row_ctr; + if (max_rows > post->strip_height) + max_rows = post->strip_height; + num_rows = 0; + (*cinfo->upsample->upsample) (cinfo, + input_buf, in_row_group_ctr, in_row_groups_avail, + post->buffer, &num_rows, max_rows); + /* Quantize and emit data. */ + (*cinfo->cquantize->color_quantize) (cinfo, + post->buffer, output_buf + *out_row_ctr, (int) num_rows); + *out_row_ctr += num_rows; +} + + +#ifdef QUANT_2PASS_SUPPORTED + +/* + * Process some data in the first pass of 2-pass quantization. + */ + +METHODDEF(void) +post_process_prepass (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_post_ptr post = (my_post_ptr) cinfo->post; + JDIMENSION old_next_row, num_rows; + + /* Reposition virtual buffer if at start of strip. */ + if (post->next_row == 0) { + post->buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, post->whole_image, + post->starting_row, post->strip_height, TRUE); + } + + /* Upsample some data (up to a strip height's worth). */ + old_next_row = post->next_row; + (*cinfo->upsample->upsample) (cinfo, + input_buf, in_row_group_ctr, in_row_groups_avail, + post->buffer, &post->next_row, post->strip_height); + + /* Allow quantizer to scan new data. No data is emitted, */ + /* but we advance out_row_ctr so outer loop can tell when we're done. */ + if (post->next_row > old_next_row) { + num_rows = post->next_row - old_next_row; + (*cinfo->cquantize->color_quantize) (cinfo, post->buffer + old_next_row, + (JSAMPARRAY) NULL, (int) num_rows); + *out_row_ctr += num_rows; + } + + /* Advance if we filled the strip. */ + if (post->next_row >= post->strip_height) { + post->starting_row += post->strip_height; + post->next_row = 0; + } +} + + +/* + * Process some data in the second pass of 2-pass quantization. + */ + +METHODDEF(void) +post_process_2pass (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_post_ptr post = (my_post_ptr) cinfo->post; + JDIMENSION num_rows, max_rows; + + /* Reposition virtual buffer if at start of strip. */ + if (post->next_row == 0) { + post->buffer = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, post->whole_image, + post->starting_row, post->strip_height, FALSE); + } + + /* Determine number of rows to emit. */ + num_rows = post->strip_height - post->next_row; /* available in strip */ + max_rows = out_rows_avail - *out_row_ctr; /* available in output area */ + if (num_rows > max_rows) + num_rows = max_rows; + /* We have to check bottom of image here, can't depend on upsampler. */ + max_rows = cinfo->output_height - post->starting_row; + if (num_rows > max_rows) + num_rows = max_rows; + + /* Quantize and emit data. */ + (*cinfo->cquantize->color_quantize) (cinfo, + post->buffer + post->next_row, output_buf + *out_row_ctr, + (int) num_rows); + *out_row_ctr += num_rows; + + /* Advance if we filled the strip. */ + post->next_row += num_rows; + if (post->next_row >= post->strip_height) { + post->starting_row += post->strip_height; + post->next_row = 0; + } +} + +#endif /* QUANT_2PASS_SUPPORTED */ + + +/* + * Initialize postprocessing controller. + */ + +GLOBAL(void) +jinit_d_post_controller (j_decompress_ptr cinfo, boolean need_full_buffer) +{ + my_post_ptr post; + + post = (my_post_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_post_controller)); + cinfo->post = (struct jpeg_d_post_controller *) post; + post->pub.start_pass = start_pass_dpost; + post->whole_image = NULL; /* flag for no virtual arrays */ + post->buffer = NULL; /* flag for no strip buffer */ + + /* Create the quantization buffer, if needed */ + if (cinfo->quantize_colors) { + /* The buffer strip height is max_v_samp_factor, which is typically + * an efficient number of rows for upsampling to return. + * (In the presence of output rescaling, we might want to be smarter?) + */ + post->strip_height = (JDIMENSION) cinfo->max_v_samp_factor; + if (need_full_buffer) { + /* Two-pass color quantization: need full-image storage. */ + /* We round up the number of rows to a multiple of the strip height. */ +#ifdef QUANT_2PASS_SUPPORTED + post->whole_image = (*cinfo->mem->request_virt_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + cinfo->output_width * cinfo->out_color_components, + (JDIMENSION) jround_up((long) cinfo->output_height, + (long) post->strip_height), + post->strip_height); +#else + ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); +#endif /* QUANT_2PASS_SUPPORTED */ + } else { + /* One-pass color quantization: just make a strip buffer. */ + post->buffer = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + cinfo->output_width * cinfo->out_color_components, + post->strip_height); + } + } +} diff --git a/Engine/lib/ljpeg/jdsample.c b/Engine/lib/ljpeg/jdsample.c new file mode 100644 index 000000000..80ffefb2a --- /dev/null +++ b/Engine/lib/ljpeg/jdsample.c @@ -0,0 +1,478 @@ +/* + * jdsample.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains upsampling routines. + * + * Upsampling input data is counted in "row groups". A row group + * is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size) + * sample rows of each component. Upsampling will normally produce + * max_v_samp_factor pixel rows from each row group (but this could vary + * if the upsampler is applying a scale factor of its own). + * + * An excellent reference for image resampling is + * Digital Image Warping, George Wolberg, 1990. + * Pub. by IEEE Computer Society Press, Los Alamitos, CA. ISBN 0-8186-8944-7. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Pointer to routine to upsample a single component */ +typedef JMETHOD(void, upsample1_ptr, + (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr)); + +/* Private subobject */ + +typedef struct { + struct jpeg_upsampler pub; /* public fields */ + + /* Color conversion buffer. When using separate upsampling and color + * conversion steps, this buffer holds one upsampled row group until it + * has been color converted and output. + * Note: we do not allocate any storage for component(s) which are full-size, + * ie do not need rescaling. The corresponding entry of color_buf[] is + * simply set to point to the input data array, thereby avoiding copying. + */ + JSAMPARRAY color_buf[MAX_COMPONENTS]; + + /* Per-component upsampling method pointers */ + upsample1_ptr methods[MAX_COMPONENTS]; + + int next_row_out; /* counts rows emitted from color_buf */ + JDIMENSION rows_to_go; /* counts rows remaining in image */ + + /* Height of an input row group for each component. */ + int rowgroup_height[MAX_COMPONENTS]; + + /* These arrays save pixel expansion factors so that int_expand need not + * recompute them each time. They are unused for other upsampling methods. + */ + UINT8 h_expand[MAX_COMPONENTS]; + UINT8 v_expand[MAX_COMPONENTS]; +} my_upsampler; + +typedef my_upsampler * my_upsample_ptr; + + +/* + * Initialize for an upsampling pass. + */ + +METHODDEF(void) +start_pass_upsample (j_decompress_ptr cinfo) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + + /* Mark the conversion buffer empty */ + upsample->next_row_out = cinfo->max_v_samp_factor; + /* Initialize total-height counter for detecting bottom of image */ + upsample->rows_to_go = cinfo->output_height; +} + + +/* + * Control routine to do upsampling (and color conversion). + * + * In this version we upsample each component independently. + * We upsample one row group into the conversion buffer, then apply + * color conversion a row at a time. + */ + +METHODDEF(void) +sep_upsample (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + int ci; + jpeg_component_info * compptr; + JDIMENSION num_rows; + + /* Fill the conversion buffer, if it's empty */ + if (upsample->next_row_out >= cinfo->max_v_samp_factor) { + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Invoke per-component upsample method. Notice we pass a POINTER + * to color_buf[ci], so that fullsize_upsample can change it. + */ + (*upsample->methods[ci]) (cinfo, compptr, + input_buf[ci] + (*in_row_group_ctr * upsample->rowgroup_height[ci]), + upsample->color_buf + ci); + } + upsample->next_row_out = 0; + } + + /* Color-convert and emit rows */ + + /* How many we have in the buffer: */ + num_rows = (JDIMENSION) (cinfo->max_v_samp_factor - upsample->next_row_out); + /* Not more than the distance to the end of the image. Need this test + * in case the image height is not a multiple of max_v_samp_factor: + */ + if (num_rows > upsample->rows_to_go) + num_rows = upsample->rows_to_go; + /* And not more than what the client can accept: */ + out_rows_avail -= *out_row_ctr; + if (num_rows > out_rows_avail) + num_rows = out_rows_avail; + + (*cinfo->cconvert->color_convert) (cinfo, upsample->color_buf, + (JDIMENSION) upsample->next_row_out, + output_buf + *out_row_ctr, + (int) num_rows); + + /* Adjust counts */ + *out_row_ctr += num_rows; + upsample->rows_to_go -= num_rows; + upsample->next_row_out += num_rows; + /* When the buffer is emptied, declare this input row group consumed */ + if (upsample->next_row_out >= cinfo->max_v_samp_factor) + (*in_row_group_ctr)++; +} + + +/* + * These are the routines invoked by sep_upsample to upsample pixel values + * of a single component. One row group is processed per call. + */ + + +/* + * For full-size components, we just make color_buf[ci] point at the + * input buffer, and thus avoid copying any data. Note that this is + * safe only because sep_upsample doesn't declare the input row group + * "consumed" until we are done color converting and emitting it. + */ + +METHODDEF(void) +fullsize_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + *output_data_ptr = input_data; +} + + +/* + * This is a no-op version used for "uninteresting" components. + * These components will not be referenced by color conversion. + */ + +METHODDEF(void) +noop_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + *output_data_ptr = NULL; /* safety check */ +} + + +/* + * This version handles any integral sampling ratios. + * This is not used for typical JPEG files, so it need not be fast. + * Nor, for that matter, is it particularly accurate: the algorithm is + * simple replication of the input pixel onto the corresponding output + * pixels. The hi-falutin sampling literature refers to this as a + * "box filter". A box filter tends to introduce visible artifacts, + * so if you are actually going to use 3:1 or 4:1 sampling ratios + * you would be well advised to improve this code. + */ + +METHODDEF(void) +int_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr, outptr; + register JSAMPLE invalue; + register int h; + JSAMPROW outend; + int h_expand, v_expand; + int inrow, outrow; + + h_expand = upsample->h_expand[compptr->component_index]; + v_expand = upsample->v_expand[compptr->component_index]; + + inrow = outrow = 0; + while (outrow < cinfo->max_v_samp_factor) { + /* Generate one output row with proper horizontal expansion */ + inptr = input_data[inrow]; + outptr = output_data[outrow]; + outend = outptr + cinfo->output_width; + while (outptr < outend) { + invalue = *inptr++; /* don't need GETJSAMPLE() here */ + for (h = h_expand; h > 0; h--) { + *outptr++ = invalue; + } + } + /* Generate any additional output rows by duplicating the first one */ + if (v_expand > 1) { + jcopy_sample_rows(output_data, outrow, output_data, outrow+1, + v_expand-1, cinfo->output_width); + } + inrow++; + outrow += v_expand; + } +} + + +/* + * Fast processing for the common case of 2:1 horizontal and 1:1 vertical. + * It's still a box filter. + */ + +METHODDEF(void) +h2v1_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr, outptr; + register JSAMPLE invalue; + JSAMPROW outend; + int inrow; + + for (inrow = 0; inrow < cinfo->max_v_samp_factor; inrow++) { + inptr = input_data[inrow]; + outptr = output_data[inrow]; + outend = outptr + cinfo->output_width; + while (outptr < outend) { + invalue = *inptr++; /* don't need GETJSAMPLE() here */ + *outptr++ = invalue; + *outptr++ = invalue; + } + } +} + + +/* + * Fast processing for the common case of 2:1 horizontal and 2:1 vertical. + * It's still a box filter. + */ + +METHODDEF(void) +h2v2_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr, outptr; + register JSAMPLE invalue; + JSAMPROW outend; + int inrow, outrow; + + inrow = outrow = 0; + while (outrow < cinfo->max_v_samp_factor) { + inptr = input_data[inrow]; + outptr = output_data[outrow]; + outend = outptr + cinfo->output_width; + while (outptr < outend) { + invalue = *inptr++; /* don't need GETJSAMPLE() here */ + *outptr++ = invalue; + *outptr++ = invalue; + } + jcopy_sample_rows(output_data, outrow, output_data, outrow+1, + 1, cinfo->output_width); + inrow++; + outrow += 2; + } +} + + +/* + * Fancy processing for the common case of 2:1 horizontal and 1:1 vertical. + * + * The upsampling algorithm is linear interpolation between pixel centers, + * also known as a "triangle filter". This is a good compromise between + * speed and visual quality. The centers of the output pixels are 1/4 and 3/4 + * of the way between input pixel centers. + * + * A note about the "bias" calculations: when rounding fractional values to + * integer, we do not want to always round 0.5 up to the next integer. + * If we did that, we'd introduce a noticeable bias towards larger values. + * Instead, this code is arranged so that 0.5 will be rounded up or down at + * alternate pixel locations (a simple ordered dither pattern). + */ + +METHODDEF(void) +h2v1_fancy_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr, outptr; + register int invalue; + register JDIMENSION colctr; + int inrow; + + for (inrow = 0; inrow < cinfo->max_v_samp_factor; inrow++) { + inptr = input_data[inrow]; + outptr = output_data[inrow]; + /* Special case for first column */ + invalue = GETJSAMPLE(*inptr++); + *outptr++ = (JSAMPLE) invalue; + *outptr++ = (JSAMPLE) ((invalue * 3 + GETJSAMPLE(*inptr) + 2) >> 2); + + for (colctr = compptr->downsampled_width - 2; colctr > 0; colctr--) { + /* General case: 3/4 * nearer pixel + 1/4 * further pixel */ + invalue = GETJSAMPLE(*inptr++) * 3; + *outptr++ = (JSAMPLE) ((invalue + GETJSAMPLE(inptr[-2]) + 1) >> 2); + *outptr++ = (JSAMPLE) ((invalue + GETJSAMPLE(*inptr) + 2) >> 2); + } + + /* Special case for last column */ + invalue = GETJSAMPLE(*inptr); + *outptr++ = (JSAMPLE) ((invalue * 3 + GETJSAMPLE(inptr[-1]) + 1) >> 2); + *outptr++ = (JSAMPLE) invalue; + } +} + + +/* + * Fancy processing for the common case of 2:1 horizontal and 2:1 vertical. + * Again a triangle filter; see comments for h2v1 case, above. + * + * It is OK for us to reference the adjacent input rows because we demanded + * context from the main buffer controller (see initialization code). + */ + +METHODDEF(void) +h2v2_fancy_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +{ + JSAMPARRAY output_data = *output_data_ptr; + register JSAMPROW inptr0, inptr1, outptr; +#if BITS_IN_JSAMPLE == 8 + register int thiscolsum, lastcolsum, nextcolsum; +#else + register INT32 thiscolsum, lastcolsum, nextcolsum; +#endif + register JDIMENSION colctr; + int inrow, outrow, v; + + inrow = outrow = 0; + while (outrow < cinfo->max_v_samp_factor) { + for (v = 0; v < 2; v++) { + /* inptr0 points to nearest input row, inptr1 points to next nearest */ + inptr0 = input_data[inrow]; + if (v == 0) /* next nearest is row above */ + inptr1 = input_data[inrow-1]; + else /* next nearest is row below */ + inptr1 = input_data[inrow+1]; + outptr = output_data[outrow++]; + + /* Special case for first column */ + thiscolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++); + nextcolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++); + *outptr++ = (JSAMPLE) ((thiscolsum * 4 + 8) >> 4); + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + nextcolsum + 7) >> 4); + lastcolsum = thiscolsum; thiscolsum = nextcolsum; + + for (colctr = compptr->downsampled_width - 2; colctr > 0; colctr--) { + /* General case: 3/4 * nearer pixel + 1/4 * further pixel in each */ + /* dimension, thus 9/16, 3/16, 3/16, 1/16 overall */ + nextcolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++); + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + lastcolsum + 8) >> 4); + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + nextcolsum + 7) >> 4); + lastcolsum = thiscolsum; thiscolsum = nextcolsum; + } + + /* Special case for last column */ + *outptr++ = (JSAMPLE) ((thiscolsum * 3 + lastcolsum + 8) >> 4); + *outptr++ = (JSAMPLE) ((thiscolsum * 4 + 7) >> 4); + } + inrow++; + } +} + + +/* + * Module initialization routine for upsampling. + */ + +GLOBAL(void) +jinit_upsampler (j_decompress_ptr cinfo) +{ + my_upsample_ptr upsample; + int ci; + jpeg_component_info * compptr; + boolean need_buffer, do_fancy; + int h_in_group, v_in_group, h_out_group, v_out_group; + + upsample = (my_upsample_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_upsampler)); + cinfo->upsample = (struct jpeg_upsampler *) upsample; + upsample->pub.start_pass = start_pass_upsample; + upsample->pub.upsample = sep_upsample; + upsample->pub.need_context_rows = FALSE; /* until we find out differently */ + + if (cinfo->CCIR601_sampling) /* this isn't supported */ + ERREXIT(cinfo, JERR_CCIR601_NOTIMPL); + + /* jdmainct.c doesn't support context rows when min_DCT_scaled_size = 1, + * so don't ask for it. + */ + do_fancy = cinfo->do_fancy_upsampling && cinfo->min_DCT_scaled_size > 1; + + /* Verify we can handle the sampling factors, select per-component methods, + * and create storage as needed. + */ + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + /* Compute size of an "input group" after IDCT scaling. This many samples + * are to be converted to max_h_samp_factor * max_v_samp_factor pixels. + */ + h_in_group = (compptr->h_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; + v_in_group = (compptr->v_samp_factor * compptr->DCT_scaled_size) / + cinfo->min_DCT_scaled_size; + h_out_group = cinfo->max_h_samp_factor; + v_out_group = cinfo->max_v_samp_factor; + upsample->rowgroup_height[ci] = v_in_group; /* save for use later */ + need_buffer = TRUE; + if (! compptr->component_needed) { + /* Don't bother to upsample an uninteresting component. */ + upsample->methods[ci] = noop_upsample; + need_buffer = FALSE; + } else if (h_in_group == h_out_group && v_in_group == v_out_group) { + /* Fullsize components can be processed without any work. */ + upsample->methods[ci] = fullsize_upsample; + need_buffer = FALSE; + } else if (h_in_group * 2 == h_out_group && + v_in_group == v_out_group) { + /* Special cases for 2h1v upsampling */ + if (do_fancy && compptr->downsampled_width > 2) + upsample->methods[ci] = h2v1_fancy_upsample; + else + upsample->methods[ci] = h2v1_upsample; + } else if (h_in_group * 2 == h_out_group && + v_in_group * 2 == v_out_group) { + /* Special cases for 2h2v upsampling */ + if (do_fancy && compptr->downsampled_width > 2) { + upsample->methods[ci] = h2v2_fancy_upsample; + upsample->pub.need_context_rows = TRUE; + } else + upsample->methods[ci] = h2v2_upsample; + } else if ((h_out_group % h_in_group) == 0 && + (v_out_group % v_in_group) == 0) { + /* Generic integral-factors upsampling method */ + upsample->methods[ci] = int_upsample; + upsample->h_expand[ci] = (UINT8) (h_out_group / h_in_group); + upsample->v_expand[ci] = (UINT8) (v_out_group / v_in_group); + } else + ERREXIT(cinfo, JERR_FRACT_SAMPLE_NOTIMPL); + if (need_buffer) { + upsample->color_buf[ci] = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) jround_up((long) cinfo->output_width, + (long) cinfo->max_h_samp_factor), + (JDIMENSION) cinfo->max_v_samp_factor); + } + } +} diff --git a/Engine/lib/ljpeg/jdtrans.c b/Engine/lib/ljpeg/jdtrans.c new file mode 100644 index 000000000..6c0ab715d --- /dev/null +++ b/Engine/lib/ljpeg/jdtrans.c @@ -0,0 +1,143 @@ +/* + * jdtrans.c + * + * Copyright (C) 1995-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains library routines for transcoding decompression, + * that is, reading raw DCT coefficient arrays from an input JPEG file. + * The routines in jdapimin.c will also be needed by a transcoder. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* Forward declarations */ +LOCAL(void) transdecode_master_selection JPP((j_decompress_ptr cinfo)); + + +/* + * Read the coefficient arrays from a JPEG file. + * jpeg_read_header must be completed before calling this. + * + * The entire image is read into a set of virtual coefficient-block arrays, + * one per component. The return value is a pointer to the array of + * virtual-array descriptors. These can be manipulated directly via the + * JPEG memory manager, or handed off to jpeg_write_coefficients(). + * To release the memory occupied by the virtual arrays, call + * jpeg_finish_decompress() when done with the data. + * + * An alternative usage is to simply obtain access to the coefficient arrays + * during a buffered-image-mode decompression operation. This is allowed + * after any jpeg_finish_output() call. The arrays can be accessed until + * jpeg_finish_decompress() is called. (Note that any call to the library + * may reposition the arrays, so don't rely on access_virt_barray() results + * to stay valid across library calls.) + * + * Returns NULL if suspended. This case need be checked only if + * a suspending data source is used. + */ + +GLOBAL(jvirt_barray_ptr *) +jpeg_read_coefficients (j_decompress_ptr cinfo) +{ + if (cinfo->global_state == DSTATE_READY) { + /* First call: initialize active modules */ + transdecode_master_selection(cinfo); + cinfo->global_state = DSTATE_RDCOEFS; + } + if (cinfo->global_state == DSTATE_RDCOEFS) { + /* Absorb whole file into the coef buffer */ + for (;;) { + int retcode; + /* Call progress monitor hook if present */ + if (cinfo->progress != NULL) + (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); + /* Absorb some more input */ + retcode = (*cinfo->inputctl->consume_input) (cinfo); + if (retcode == JPEG_SUSPENDED) + return NULL; + if (retcode == JPEG_REACHED_EOI) + break; + /* Advance progress counter if appropriate */ + if (cinfo->progress != NULL && + (retcode == JPEG_ROW_COMPLETED || retcode == JPEG_REACHED_SOS)) { + if (++cinfo->progress->pass_counter >= cinfo->progress->pass_limit) { + /* startup underestimated number of scans; ratchet up one scan */ + cinfo->progress->pass_limit += (long) cinfo->total_iMCU_rows; + } + } + } + /* Set state so that jpeg_finish_decompress does the right thing */ + cinfo->global_state = DSTATE_STOPPING; + } + /* At this point we should be in state DSTATE_STOPPING if being used + * standalone, or in state DSTATE_BUFIMAGE if being invoked to get access + * to the coefficients during a full buffered-image-mode decompression. + */ + if ((cinfo->global_state == DSTATE_STOPPING || + cinfo->global_state == DSTATE_BUFIMAGE) && cinfo->buffered_image) { + return cinfo->coef->coef_arrays; + } + /* Oops, improper usage */ + ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); + return NULL; /* keep compiler happy */ +} + + +/* + * Master selection of decompression modules for transcoding. + * This substitutes for jdmaster.c's initialization of the full decompressor. + */ + +LOCAL(void) +transdecode_master_selection (j_decompress_ptr cinfo) +{ + /* This is effectively a buffered-image operation. */ + cinfo->buffered_image = TRUE; + + /* Entropy decoding: either Huffman or arithmetic coding. */ + if (cinfo->arith_code) { + ERREXIT(cinfo, JERR_ARITH_NOTIMPL); + } else { + if (cinfo->progressive_mode) { +#ifdef D_PROGRESSIVE_SUPPORTED + jinit_phuff_decoder(cinfo); +#else + ERREXIT(cinfo, JERR_NOT_COMPILED); +#endif + } else + jinit_huff_decoder(cinfo); + } + + /* Always get a full-image coefficient buffer. */ + jinit_d_coef_controller(cinfo, TRUE); + + /* We can now tell the memory manager to allocate virtual arrays. */ + (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); + + /* Initialize input side of decompressor to consume first scan. */ + (*cinfo->inputctl->start_input_pass) (cinfo); + + /* Initialize progress monitoring. */ + if (cinfo->progress != NULL) { + int nscans; + /* Estimate number of scans to set pass_limit. */ + if (cinfo->progressive_mode) { + /* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */ + nscans = 2 + 3 * cinfo->num_components; + } else if (cinfo->inputctl->has_multiple_scans) { + /* For a nonprogressive multiscan file, estimate 1 scan per component. */ + nscans = cinfo->num_components; + } else { + nscans = 1; + } + cinfo->progress->pass_counter = 0L; + cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows * nscans; + cinfo->progress->completed_passes = 0; + cinfo->progress->total_passes = 1; + } +} diff --git a/Engine/lib/ljpeg/jerror.c b/Engine/lib/ljpeg/jerror.c new file mode 100644 index 000000000..3da7be86a --- /dev/null +++ b/Engine/lib/ljpeg/jerror.c @@ -0,0 +1,252 @@ +/* + * jerror.c + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains simple error-reporting and trace-message routines. + * These are suitable for Unix-like systems and others where writing to + * stderr is the right thing to do. Many applications will want to replace + * some or all of these routines. + * + * If you define USE_WINDOWS_MESSAGEBOX in jconfig.h or in the makefile, + * you get a Windows-specific hack to display error messages in a dialog box. + * It ain't much, but it beats dropping error messages into the bit bucket, + * which is what happens to output to stderr under most Windows C compilers. + * + * These routines are used by both the compression and decompression code. + */ + +/* this is not a core library module, so it doesn't define JPEG_INTERNALS */ +#include "jinclude.h" +#include "jpeglib.h" +#include "jversion.h" +#include "jerror.h" + +#ifdef USE_WINDOWS_MESSAGEBOX +#include +#endif + +#ifndef EXIT_FAILURE /* define exit() codes if not provided */ +#define EXIT_FAILURE 1 +#endif + + +/* + * Create the message string table. + * We do this from the master message list in jerror.h by re-reading + * jerror.h with a suitable definition for macro JMESSAGE. + * The message table is made an external symbol just in case any applications + * want to refer to it directly. + */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_std_message_table jMsgTable +#endif + +#define JMESSAGE(code,string) string , + +const char * const jpeg_std_message_table[] = { +#include "jerror.h" + NULL +}; + + +/* + * Error exit handler: must not return to caller. + * + * Applications may override this if they want to get control back after + * an error. Typically one would longjmp somewhere instead of exiting. + * The setjmp buffer can be made a private field within an expanded error + * handler object. Note that the info needed to generate an error message + * is stored in the error object, so you can generate the message now or + * later, at your convenience. + * You should make sure that the JPEG object is cleaned up (with jpeg_abort + * or jpeg_destroy) at some point. + */ + +METHODDEF(void) +error_exit (j_common_ptr cinfo) +{ + /* Always display the message */ + (*cinfo->err->output_message) (cinfo); + + /* Let the memory manager delete any temp files before we die */ + jpeg_destroy(cinfo); + + exit(EXIT_FAILURE); +} + + +/* + * Actual output of an error or trace message. + * Applications may override this method to send JPEG messages somewhere + * other than stderr. + * + * On Windows, printing to stderr is generally completely useless, + * so we provide optional code to produce an error-dialog popup. + * Most Windows applications will still prefer to override this routine, + * but if they don't, it'll do something at least marginally useful. + * + * NOTE: to use the library in an environment that doesn't support the + * C stdio library, you may have to delete the call to fprintf() entirely, + * not just not use this routine. + */ + +METHODDEF(void) +output_message (j_common_ptr cinfo) +{ + char buffer[JMSG_LENGTH_MAX]; + + /* Create the message */ + (*cinfo->err->format_message) (cinfo, buffer); + +#ifdef USE_WINDOWS_MESSAGEBOX + /* Display it in a message dialog box */ + MessageBox(GetActiveWindow(), buffer, "JPEG Library Error", + MB_OK | MB_ICONERROR); +#else + /* Send it to stderr, adding a newline */ + fprintf(stderr, "%s\n", buffer); +#endif +} + + +/* + * Decide whether to emit a trace or warning message. + * msg_level is one of: + * -1: recoverable corrupt-data warning, may want to abort. + * 0: important advisory messages (always display to user). + * 1: first level of tracing detail. + * 2,3,...: successively more detailed tracing messages. + * An application might override this method if it wanted to abort on warnings + * or change the policy about which messages to display. + */ + +METHODDEF(void) +emit_message (j_common_ptr cinfo, int msg_level) +{ + struct jpeg_error_mgr * err = cinfo->err; + + if (msg_level < 0) { + /* It's a warning message. Since corrupt files may generate many warnings, + * the policy implemented here is to show only the first warning, + * unless trace_level >= 3. + */ + if (err->num_warnings == 0 || err->trace_level >= 3) + (*err->output_message) (cinfo); + /* Always count warnings in num_warnings. */ + err->num_warnings++; + } else { + /* It's a trace message. Show it if trace_level >= msg_level. */ + if (err->trace_level >= msg_level) + (*err->output_message) (cinfo); + } +} + + +/* + * Format a message string for the most recent JPEG error or message. + * The message is stored into buffer, which should be at least JMSG_LENGTH_MAX + * characters. Note that no '\n' character is added to the string. + * Few applications should need to override this method. + */ + +METHODDEF(void) +format_message (j_common_ptr cinfo, char * buffer) +{ + struct jpeg_error_mgr * err = cinfo->err; + int msg_code = err->msg_code; + const char * msgtext = NULL; + const char * msgptr; + char ch; + boolean isstring; + + /* Look up message string in proper table */ + if (msg_code > 0 && msg_code <= err->last_jpeg_message) { + msgtext = err->jpeg_message_table[msg_code]; + } else if (err->addon_message_table != NULL && + msg_code >= err->first_addon_message && + msg_code <= err->last_addon_message) { + msgtext = err->addon_message_table[msg_code - err->first_addon_message]; + } + + /* Defend against bogus message number */ + if (msgtext == NULL) { + err->msg_parm.i[0] = msg_code; + msgtext = err->jpeg_message_table[0]; + } + + /* Check for string parameter, as indicated by %s in the message text */ + isstring = FALSE; + msgptr = msgtext; + while ((ch = *msgptr++) != '\0') { + if (ch == '%') { + if (*msgptr == 's') isstring = TRUE; + break; + } + } + + /* Format the message into the passed buffer */ + if (isstring) + sprintf(buffer, msgtext, err->msg_parm.s); + else + sprintf(buffer, msgtext, + err->msg_parm.i[0], err->msg_parm.i[1], + err->msg_parm.i[2], err->msg_parm.i[3], + err->msg_parm.i[4], err->msg_parm.i[5], + err->msg_parm.i[6], err->msg_parm.i[7]); +} + + +/* + * Reset error state variables at start of a new image. + * This is called during compression startup to reset trace/error + * processing to default state, without losing any application-specific + * method pointers. An application might possibly want to override + * this method if it has additional error processing state. + */ + +METHODDEF(void) +reset_error_mgr (j_common_ptr cinfo) +{ + cinfo->err->num_warnings = 0; + /* trace_level is not reset since it is an application-supplied parameter */ + cinfo->err->msg_code = 0; /* may be useful as a flag for "no error" */ +} + + +/* + * Fill in the standard error-handling methods in a jpeg_error_mgr object. + * Typical call is: + * struct jpeg_compress_struct cinfo; + * struct jpeg_error_mgr err; + * + * cinfo.err = jpeg_std_error(&err); + * after which the application may override some of the methods. + */ + +GLOBAL(struct jpeg_error_mgr *) +jpeg_std_error (struct jpeg_error_mgr * err) +{ + err->error_exit = error_exit; + err->emit_message = emit_message; + err->output_message = output_message; + err->format_message = format_message; + err->reset_error_mgr = reset_error_mgr; + + err->trace_level = 0; /* default = no tracing */ + err->num_warnings = 0; /* no warnings emitted yet */ + err->msg_code = 0; /* may be useful as a flag for "no error" */ + + /* Initialize message table pointers */ + err->jpeg_message_table = jpeg_std_message_table; + err->last_jpeg_message = (int) JMSG_LASTMSGCODE - 1; + + err->addon_message_table = NULL; + err->first_addon_message = 0; /* for safety */ + err->last_addon_message = 0; + + return err; +} diff --git a/Engine/lib/ljpeg/jerror.h b/Engine/lib/ljpeg/jerror.h new file mode 100644 index 000000000..fc2fffeac --- /dev/null +++ b/Engine/lib/ljpeg/jerror.h @@ -0,0 +1,291 @@ +/* + * jerror.h + * + * Copyright (C) 1994-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the error and message codes for the JPEG library. + * Edit this file to add new codes, or to translate the message strings to + * some other language. + * A set of error-reporting macros are defined too. Some applications using + * the JPEG library may wish to include this file to get the error codes + * and/or the macros. + */ + +/* + * To define the enum list of message codes, include this file without + * defining macro JMESSAGE. To create a message string table, include it + * again with a suitable JMESSAGE definition (see jerror.c for an example). + */ +#ifndef JMESSAGE +#ifndef JERROR_H +/* First time through, define the enum list */ +#define JMAKE_ENUM_LIST +#else +/* Repeated inclusions of this file are no-ops unless JMESSAGE is defined */ +#define JMESSAGE(code,string) +#endif /* JERROR_H */ +#endif /* JMESSAGE */ + +#ifdef JMAKE_ENUM_LIST + +typedef enum { + +#define JMESSAGE(code,string) code , + +#endif /* JMAKE_ENUM_LIST */ + +JMESSAGE(JMSG_NOMESSAGE, "Bogus message code %d") /* Must be first entry! */ + +/* For maintenance convenience, list is alphabetical by message code name */ +JMESSAGE(JERR_ARITH_NOTIMPL, + "Sorry, there are legal restrictions on arithmetic coding") +JMESSAGE(JERR_BAD_ALIGN_TYPE, "ALIGN_TYPE is wrong, please fix") +JMESSAGE(JERR_BAD_ALLOC_CHUNK, "MAX_ALLOC_CHUNK is wrong, please fix") +JMESSAGE(JERR_BAD_BUFFER_MODE, "Bogus buffer control mode") +JMESSAGE(JERR_BAD_COMPONENT_ID, "Invalid component ID %d in SOS") +JMESSAGE(JERR_BAD_DCT_COEF, "DCT coefficient out of range") +JMESSAGE(JERR_BAD_DCTSIZE, "IDCT output block size %d not supported") +JMESSAGE(JERR_BAD_HUFF_TABLE, "Bogus Huffman table definition") +JMESSAGE(JERR_BAD_IN_COLORSPACE, "Bogus input colorspace") +JMESSAGE(JERR_BAD_J_COLORSPACE, "Bogus JPEG colorspace") +JMESSAGE(JERR_BAD_LENGTH, "Bogus marker length") +JMESSAGE(JERR_BAD_LIB_VERSION, + "Wrong JPEG library version: library is %d, caller expects %d") +JMESSAGE(JERR_BAD_MCU_SIZE, "Sampling factors too large for interleaved scan") +JMESSAGE(JERR_BAD_POOL_ID, "Invalid memory pool code %d") +JMESSAGE(JERR_BAD_PRECISION, "Unsupported JPEG data precision %d") +JMESSAGE(JERR_BAD_PROGRESSION, + "Invalid progressive parameters Ss=%d Se=%d Ah=%d Al=%d") +JMESSAGE(JERR_BAD_PROG_SCRIPT, + "Invalid progressive parameters at scan script entry %d") +JMESSAGE(JERR_BAD_SAMPLING, "Bogus sampling factors") +JMESSAGE(JERR_BAD_SCAN_SCRIPT, "Invalid scan script at entry %d") +JMESSAGE(JERR_BAD_STATE, "Improper call to JPEG library in state %d") +JMESSAGE(JERR_BAD_STRUCT_SIZE, + "JPEG parameter struct mismatch: library thinks size is %u, caller expects %u") +JMESSAGE(JERR_BAD_VIRTUAL_ACCESS, "Bogus virtual array access") +JMESSAGE(JERR_BUFFER_SIZE, "Buffer passed to JPEG library is too small") +JMESSAGE(JERR_CANT_SUSPEND, "Suspension not allowed here") +JMESSAGE(JERR_CCIR601_NOTIMPL, "CCIR601 sampling not implemented yet") +JMESSAGE(JERR_COMPONENT_COUNT, "Too many color components: %d, max %d") +JMESSAGE(JERR_CONVERSION_NOTIMPL, "Unsupported color conversion request") +JMESSAGE(JERR_DAC_INDEX, "Bogus DAC index %d") +JMESSAGE(JERR_DAC_VALUE, "Bogus DAC value 0x%x") +JMESSAGE(JERR_DHT_INDEX, "Bogus DHT index %d") +JMESSAGE(JERR_DQT_INDEX, "Bogus DQT index %d") +JMESSAGE(JERR_EMPTY_IMAGE, "Empty JPEG image (DNL not supported)") +JMESSAGE(JERR_EMS_READ, "Read from EMS failed") +JMESSAGE(JERR_EMS_WRITE, "Write to EMS failed") +JMESSAGE(JERR_EOI_EXPECTED, "Didn't expect more than one scan") +JMESSAGE(JERR_FILE_READ, "Input file read error") +JMESSAGE(JERR_FILE_WRITE, "Output file write error --- out of disk space?") +JMESSAGE(JERR_FRACT_SAMPLE_NOTIMPL, "Fractional sampling not implemented yet") +JMESSAGE(JERR_HUFF_CLEN_OVERFLOW, "Huffman code size table overflow") +JMESSAGE(JERR_HUFF_MISSING_CODE, "Missing Huffman code table entry") +JMESSAGE(JERR_IMAGE_TOO_BIG, "Maximum supported image dimension is %u pixels") +JMESSAGE(JERR_INPUT_EMPTY, "Empty input file") +JMESSAGE(JERR_INPUT_EOF, "Premature end of input file") +JMESSAGE(JERR_MISMATCHED_QUANT_TABLE, + "Cannot transcode due to multiple use of quantization table %d") +JMESSAGE(JERR_MISSING_DATA, "Scan script does not transmit all data") +JMESSAGE(JERR_MODE_CHANGE, "Invalid color quantization mode change") +JMESSAGE(JERR_NOTIMPL, "Not implemented yet") +JMESSAGE(JERR_NOT_COMPILED, "Requested feature was omitted at compile time") +JMESSAGE(JERR_NO_BACKING_STORE, "Backing store not supported") +JMESSAGE(JERR_NO_HUFF_TABLE, "Huffman table 0x%02x was not defined") +JMESSAGE(JERR_NO_IMAGE, "JPEG datastream contains no image") +JMESSAGE(JERR_NO_QUANT_TABLE, "Quantization table 0x%02x was not defined") +JMESSAGE(JERR_NO_SOI, "Not a JPEG file: starts with 0x%02x 0x%02x") +JMESSAGE(JERR_OUT_OF_MEMORY, "Insufficient memory (case %d)") +JMESSAGE(JERR_QUANT_COMPONENTS, + "Cannot quantize more than %d color components") +JMESSAGE(JERR_QUANT_FEW_COLORS, "Cannot quantize to fewer than %d colors") +JMESSAGE(JERR_QUANT_MANY_COLORS, "Cannot quantize to more than %d colors") +JMESSAGE(JERR_SOF_DUPLICATE, "Invalid JPEG file structure: two SOF markers") +JMESSAGE(JERR_SOF_NO_SOS, "Invalid JPEG file structure: missing SOS marker") +JMESSAGE(JERR_SOF_UNSUPPORTED, "Unsupported JPEG process: SOF type 0x%02x") +JMESSAGE(JERR_SOI_DUPLICATE, "Invalid JPEG file structure: two SOI markers") +JMESSAGE(JERR_SOS_NO_SOF, "Invalid JPEG file structure: SOS before SOF") +JMESSAGE(JERR_TFILE_CREATE, "Failed to create temporary file %s") +JMESSAGE(JERR_TFILE_READ, "Read failed on temporary file") +JMESSAGE(JERR_TFILE_SEEK, "Seek failed on temporary file") +JMESSAGE(JERR_TFILE_WRITE, + "Write failed on temporary file --- out of disk space?") +JMESSAGE(JERR_TOO_LITTLE_DATA, "Application transferred too few scanlines") +JMESSAGE(JERR_UNKNOWN_MARKER, "Unsupported marker type 0x%02x") +JMESSAGE(JERR_VIRTUAL_BUG, "Virtual array controller messed up") +JMESSAGE(JERR_WIDTH_OVERFLOW, "Image too wide for this implementation") +JMESSAGE(JERR_XMS_READ, "Read from XMS failed") +JMESSAGE(JERR_XMS_WRITE, "Write to XMS failed") +JMESSAGE(JMSG_COPYRIGHT, JCOPYRIGHT) +JMESSAGE(JMSG_VERSION, JVERSION) +JMESSAGE(JTRC_16BIT_TABLES, + "Caution: quantization tables are too coarse for baseline JPEG") +JMESSAGE(JTRC_ADOBE, + "Adobe APP14 marker: version %d, flags 0x%04x 0x%04x, transform %d") +JMESSAGE(JTRC_APP0, "Unknown APP0 marker (not JFIF), length %u") +JMESSAGE(JTRC_APP14, "Unknown APP14 marker (not Adobe), length %u") +JMESSAGE(JTRC_DAC, "Define Arithmetic Table 0x%02x: 0x%02x") +JMESSAGE(JTRC_DHT, "Define Huffman Table 0x%02x") +JMESSAGE(JTRC_DQT, "Define Quantization Table %d precision %d") +JMESSAGE(JTRC_DRI, "Define Restart Interval %u") +JMESSAGE(JTRC_EMS_CLOSE, "Freed EMS handle %u") +JMESSAGE(JTRC_EMS_OPEN, "Obtained EMS handle %u") +JMESSAGE(JTRC_EOI, "End Of Image") +JMESSAGE(JTRC_HUFFBITS, " %3d %3d %3d %3d %3d %3d %3d %3d") +JMESSAGE(JTRC_JFIF, "JFIF APP0 marker: version %d.%02d, density %dx%d %d") +JMESSAGE(JTRC_JFIF_BADTHUMBNAILSIZE, + "Warning: thumbnail image size does not match data length %u") +JMESSAGE(JTRC_JFIF_EXTENSION, + "JFIF extension marker: type 0x%02x, length %u") +JMESSAGE(JTRC_JFIF_THUMBNAIL, " with %d x %d thumbnail image") +JMESSAGE(JTRC_MISC_MARKER, "Miscellaneous marker 0x%02x, length %u") +JMESSAGE(JTRC_PARMLESS_MARKER, "Unexpected marker 0x%02x") +JMESSAGE(JTRC_QUANTVALS, " %4u %4u %4u %4u %4u %4u %4u %4u") +JMESSAGE(JTRC_QUANT_3_NCOLORS, "Quantizing to %d = %d*%d*%d colors") +JMESSAGE(JTRC_QUANT_NCOLORS, "Quantizing to %d colors") +JMESSAGE(JTRC_QUANT_SELECTED, "Selected %d colors for quantization") +JMESSAGE(JTRC_RECOVERY_ACTION, "At marker 0x%02x, recovery action %d") +JMESSAGE(JTRC_RST, "RST%d") +JMESSAGE(JTRC_SMOOTH_NOTIMPL, + "Smoothing not supported with nonstandard sampling ratios") +JMESSAGE(JTRC_SOF, "Start Of Frame 0x%02x: width=%u, height=%u, components=%d") +JMESSAGE(JTRC_SOF_COMPONENT, " Component %d: %dhx%dv q=%d") +JMESSAGE(JTRC_SOI, "Start of Image") +JMESSAGE(JTRC_SOS, "Start Of Scan: %d components") +JMESSAGE(JTRC_SOS_COMPONENT, " Component %d: dc=%d ac=%d") +JMESSAGE(JTRC_SOS_PARAMS, " Ss=%d, Se=%d, Ah=%d, Al=%d") +JMESSAGE(JTRC_TFILE_CLOSE, "Closed temporary file %s") +JMESSAGE(JTRC_TFILE_OPEN, "Opened temporary file %s") +JMESSAGE(JTRC_THUMB_JPEG, + "JFIF extension marker: JPEG-compressed thumbnail image, length %u") +JMESSAGE(JTRC_THUMB_PALETTE, + "JFIF extension marker: palette thumbnail image, length %u") +JMESSAGE(JTRC_THUMB_RGB, + "JFIF extension marker: RGB thumbnail image, length %u") +JMESSAGE(JTRC_UNKNOWN_IDS, + "Unrecognized component IDs %d %d %d, assuming YCbCr") +JMESSAGE(JTRC_XMS_CLOSE, "Freed XMS handle %u") +JMESSAGE(JTRC_XMS_OPEN, "Obtained XMS handle %u") +JMESSAGE(JWRN_ADOBE_XFORM, "Unknown Adobe color transform code %d") +JMESSAGE(JWRN_BOGUS_PROGRESSION, + "Inconsistent progression sequence for component %d coefficient %d") +JMESSAGE(JWRN_EXTRANEOUS_DATA, + "Corrupt JPEG data: %u extraneous bytes before marker 0x%02x") +JMESSAGE(JWRN_HIT_MARKER, "Corrupt JPEG data: premature end of data segment") +JMESSAGE(JWRN_HUFF_BAD_CODE, "Corrupt JPEG data: bad Huffman code") +JMESSAGE(JWRN_JFIF_MAJOR, "Warning: unknown JFIF revision number %d.%02d") +JMESSAGE(JWRN_JPEG_EOF, "Premature end of JPEG file") +JMESSAGE(JWRN_MUST_RESYNC, + "Corrupt JPEG data: found marker 0x%02x instead of RST%d") +JMESSAGE(JWRN_NOT_SEQUENTIAL, "Invalid SOS parameters for sequential JPEG") +JMESSAGE(JWRN_TOO_MUCH_DATA, "Application transferred too many scanlines") + +#ifdef JMAKE_ENUM_LIST + + JMSG_LASTMSGCODE +} J_MESSAGE_CODE; + +#undef JMAKE_ENUM_LIST +#endif /* JMAKE_ENUM_LIST */ + +/* Zap JMESSAGE macro so that future re-inclusions do nothing by default */ +#undef JMESSAGE + + +#ifndef JERROR_H +#define JERROR_H + +/* Macros to simplify using the error and trace message stuff */ +/* The first parameter is either type of cinfo pointer */ + +/* Fatal errors (print message and exit) */ +#define ERREXIT(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT3(cinfo,code,p1,p2,p3) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXIT4(cinfo,code,p1,p2,p3,p4) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (cinfo)->err->msg_parm.i[2] = (p3), \ + (cinfo)->err->msg_parm.i[3] = (p4), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) +#define ERREXITS(cinfo,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->error_exit) ((j_common_ptr) (cinfo))) + +#define MAKESTMT(stuff) do { stuff } while (0) + +/* Nonfatal errors (we can keep going, but the data is probably corrupt) */ +#define WARNMS(cinfo,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS1(cinfo,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) +#define WARNMS2(cinfo,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), -1)) + +/* Informational/debugging messages */ +#define TRACEMS(cinfo,lvl,code) \ + ((cinfo)->err->msg_code = (code), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS1(cinfo,lvl,code,p1) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS2(cinfo,lvl,code,p1,p2) \ + ((cinfo)->err->msg_code = (code), \ + (cinfo)->err->msg_parm.i[0] = (p1), \ + (cinfo)->err->msg_parm.i[1] = (p2), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) +#define TRACEMS3(cinfo,lvl,code,p1,p2,p3) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS4(cinfo,lvl,code,p1,p2,p3,p4) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS5(cinfo,lvl,code,p1,p2,p3,p4,p5) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMS8(cinfo,lvl,code,p1,p2,p3,p4,p5,p6,p7,p8) \ + MAKESTMT(int * _mp = (cinfo)->err->msg_parm.i; \ + _mp[0] = (p1); _mp[1] = (p2); _mp[2] = (p3); _mp[3] = (p4); \ + _mp[4] = (p5); _mp[5] = (p6); _mp[6] = (p7); _mp[7] = (p8); \ + (cinfo)->err->msg_code = (code); \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl)); ) +#define TRACEMSS(cinfo,lvl,code,str) \ + ((cinfo)->err->msg_code = (code), \ + strncpy((cinfo)->err->msg_parm.s, (str), JMSG_STR_PARM_MAX), \ + (*(cinfo)->err->emit_message) ((j_common_ptr) (cinfo), (lvl))) + +#endif /* JERROR_H */ diff --git a/Engine/lib/ljpeg/jfdctflt.c b/Engine/lib/ljpeg/jfdctflt.c new file mode 100644 index 000000000..79d7a0078 --- /dev/null +++ b/Engine/lib/ljpeg/jfdctflt.c @@ -0,0 +1,168 @@ +/* + * jfdctflt.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a floating-point implementation of the + * forward DCT (Discrete Cosine Transform). + * + * This implementation should be more accurate than either of the integer + * DCT implementations. However, it may not give the same results on all + * machines because of differences in roundoff behavior. Speed will depend + * on the hardware's floating point capacity. + * + * A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT + * on each column. Direct algorithms are also available, but they are + * much more complex and seem not to be any faster when reduced to code. + * + * This implementation is based on Arai, Agui, and Nakajima's algorithm for + * scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in + * Japanese, but the algorithm is described in the Pennebaker & Mitchell + * JPEG textbook (see REFERENCES section in file README). The following code + * is based directly on figure 4-8 in P&M. + * While an 8-point DCT cannot be done in less than 11 multiplies, it is + * possible to arrange the computation so that many of the multiplies are + * simple scalings of the final outputs. These multiplies can then be + * folded into the multiplications or divisions by the JPEG quantization + * table entries. The AA&N method leaves only 5 multiplies and 29 adds + * to be done in the DCT itself. + * The primary disadvantage of this method is that with a fixed-point + * implementation, accuracy is lost due to imprecise representation of the + * scaled quantization values. However, that problem does not arise if + * we use floating point arithmetic. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + +#ifdef DCT_FLOAT_SUPPORTED + + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + + +/* + * Perform the forward DCT on one block of samples. + */ + +GLOBAL(void) +jpeg_fdct_float (FAST_FLOAT * data) +{ + FAST_FLOAT tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + FAST_FLOAT tmp10, tmp11, tmp12, tmp13; + FAST_FLOAT z1, z2, z3, z4, z5, z11, z13; + FAST_FLOAT *dataptr; + int ctr; + + /* Pass 1: process rows. */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + tmp0 = dataptr[0] + dataptr[7]; + tmp7 = dataptr[0] - dataptr[7]; + tmp1 = dataptr[1] + dataptr[6]; + tmp6 = dataptr[1] - dataptr[6]; + tmp2 = dataptr[2] + dataptr[5]; + tmp5 = dataptr[2] - dataptr[5]; + tmp3 = dataptr[3] + dataptr[4]; + tmp4 = dataptr[3] - dataptr[4]; + + /* Even part */ + + tmp10 = tmp0 + tmp3; /* phase 2 */ + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + dataptr[0] = tmp10 + tmp11; /* phase 3 */ + dataptr[4] = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * ((FAST_FLOAT) 0.707106781); /* c4 */ + dataptr[2] = tmp13 + z1; /* phase 5 */ + dataptr[6] = tmp13 - z1; + + /* Odd part */ + + tmp10 = tmp4 + tmp5; /* phase 2 */ + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + z5 = (tmp10 - tmp12) * ((FAST_FLOAT) 0.382683433); /* c6 */ + z2 = ((FAST_FLOAT) 0.541196100) * tmp10 + z5; /* c2-c6 */ + z4 = ((FAST_FLOAT) 1.306562965) * tmp12 + z5; /* c2+c6 */ + z3 = tmp11 * ((FAST_FLOAT) 0.707106781); /* c4 */ + + z11 = tmp7 + z3; /* phase 5 */ + z13 = tmp7 - z3; + + dataptr[5] = z13 + z2; /* phase 6 */ + dataptr[3] = z13 - z2; + dataptr[1] = z11 + z4; + dataptr[7] = z11 - z4; + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*7]; + tmp7 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*7]; + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*6]; + tmp6 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*6]; + tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*5]; + tmp5 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*5]; + tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*4]; + tmp4 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*4]; + + /* Even part */ + + tmp10 = tmp0 + tmp3; /* phase 2 */ + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + dataptr[DCTSIZE*0] = tmp10 + tmp11; /* phase 3 */ + dataptr[DCTSIZE*4] = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * ((FAST_FLOAT) 0.707106781); /* c4 */ + dataptr[DCTSIZE*2] = tmp13 + z1; /* phase 5 */ + dataptr[DCTSIZE*6] = tmp13 - z1; + + /* Odd part */ + + tmp10 = tmp4 + tmp5; /* phase 2 */ + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + z5 = (tmp10 - tmp12) * ((FAST_FLOAT) 0.382683433); /* c6 */ + z2 = ((FAST_FLOAT) 0.541196100) * tmp10 + z5; /* c2-c6 */ + z4 = ((FAST_FLOAT) 1.306562965) * tmp12 + z5; /* c2+c6 */ + z3 = tmp11 * ((FAST_FLOAT) 0.707106781); /* c4 */ + + z11 = tmp7 + z3; /* phase 5 */ + z13 = tmp7 - z3; + + dataptr[DCTSIZE*5] = z13 + z2; /* phase 6 */ + dataptr[DCTSIZE*3] = z13 - z2; + dataptr[DCTSIZE*1] = z11 + z4; + dataptr[DCTSIZE*7] = z11 - z4; + + dataptr++; /* advance pointer to next column */ + } +} + +#endif /* DCT_FLOAT_SUPPORTED */ diff --git a/Engine/lib/ljpeg/jfdctfst.c b/Engine/lib/ljpeg/jfdctfst.c new file mode 100644 index 000000000..ccb378a3b --- /dev/null +++ b/Engine/lib/ljpeg/jfdctfst.c @@ -0,0 +1,224 @@ +/* + * jfdctfst.c + * + * Copyright (C) 1994-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a fast, not so accurate integer implementation of the + * forward DCT (Discrete Cosine Transform). + * + * A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT + * on each column. Direct algorithms are also available, but they are + * much more complex and seem not to be any faster when reduced to code. + * + * This implementation is based on Arai, Agui, and Nakajima's algorithm for + * scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in + * Japanese, but the algorithm is described in the Pennebaker & Mitchell + * JPEG textbook (see REFERENCES section in file README). The following code + * is based directly on figure 4-8 in P&M. + * While an 8-point DCT cannot be done in less than 11 multiplies, it is + * possible to arrange the computation so that many of the multiplies are + * simple scalings of the final outputs. These multiplies can then be + * folded into the multiplications or divisions by the JPEG quantization + * table entries. The AA&N method leaves only 5 multiplies and 29 adds + * to be done in the DCT itself. + * The primary disadvantage of this method is that with fixed-point math, + * accuracy is lost due to imprecise representation of the scaled + * quantization values. The smaller the quantization table entry, the less + * precise the scaled value, so this implementation does worse with high- + * quality-setting files than with low-quality ones. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + +#ifdef DCT_IFAST_SUPPORTED + + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + + +/* Scaling decisions are generally the same as in the LL&M algorithm; + * see jfdctint.c for more details. However, we choose to descale + * (right shift) multiplication products as soon as they are formed, + * rather than carrying additional fractional bits into subsequent additions. + * This compromises accuracy slightly, but it lets us save a few shifts. + * More importantly, 16-bit arithmetic is then adequate (for 8-bit samples) + * everywhere except in the multiplications proper; this saves a good deal + * of work on 16-bit-int machines. + * + * Again to save a few shifts, the intermediate results between pass 1 and + * pass 2 are not upscaled, but are represented only to integral precision. + * + * A final compromise is to represent the multiplicative constants to only + * 8 fractional bits, rather than 13. This saves some shifting work on some + * machines, and may also reduce the cost of multiplication (since there + * are fewer one-bits in the constants). + */ + +#define CONST_BITS 8 + + +/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus + * causing a lot of useless floating-point operations at run time. + * To get around this we use the following pre-calculated constants. + * If you change CONST_BITS you may want to add appropriate values. + * (With a reasonable C compiler, you can just rely on the FIX() macro...) + */ + +#if CONST_BITS == 8 +#define FIX_0_382683433 ((INT32) 98) /* FIX(0.382683433) */ +#define FIX_0_541196100 ((INT32) 139) /* FIX(0.541196100) */ +#define FIX_0_707106781 ((INT32) 181) /* FIX(0.707106781) */ +#define FIX_1_306562965 ((INT32) 334) /* FIX(1.306562965) */ +#else +#define FIX_0_382683433 FIX(0.382683433) +#define FIX_0_541196100 FIX(0.541196100) +#define FIX_0_707106781 FIX(0.707106781) +#define FIX_1_306562965 FIX(1.306562965) +#endif + + +/* We can gain a little more speed, with a further compromise in accuracy, + * by omitting the addition in a descaling shift. This yields an incorrectly + * rounded result half the time... + */ + +#ifndef USE_ACCURATE_ROUNDING +#undef DESCALE +#define DESCALE(x,n) RIGHT_SHIFT(x, n) +#endif + + +/* Multiply a DCTELEM variable by an INT32 constant, and immediately + * descale to yield a DCTELEM result. + */ + +#define MULTIPLY(var,const) ((DCTELEM) DESCALE((var) * (const), CONST_BITS)) + + +/* + * Perform the forward DCT on one block of samples. + */ + +GLOBAL(void) +jpeg_fdct_ifast (DCTELEM * data) +{ + DCTELEM tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + DCTELEM tmp10, tmp11, tmp12, tmp13; + DCTELEM z1, z2, z3, z4, z5, z11, z13; + DCTELEM *dataptr; + int ctr; + SHIFT_TEMPS + + /* Pass 1: process rows. */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + tmp0 = dataptr[0] + dataptr[7]; + tmp7 = dataptr[0] - dataptr[7]; + tmp1 = dataptr[1] + dataptr[6]; + tmp6 = dataptr[1] - dataptr[6]; + tmp2 = dataptr[2] + dataptr[5]; + tmp5 = dataptr[2] - dataptr[5]; + tmp3 = dataptr[3] + dataptr[4]; + tmp4 = dataptr[3] - dataptr[4]; + + /* Even part */ + + tmp10 = tmp0 + tmp3; /* phase 2 */ + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + dataptr[0] = tmp10 + tmp11; /* phase 3 */ + dataptr[4] = tmp10 - tmp11; + + z1 = MULTIPLY(tmp12 + tmp13, FIX_0_707106781); /* c4 */ + dataptr[2] = tmp13 + z1; /* phase 5 */ + dataptr[6] = tmp13 - z1; + + /* Odd part */ + + tmp10 = tmp4 + tmp5; /* phase 2 */ + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + z5 = MULTIPLY(tmp10 - tmp12, FIX_0_382683433); /* c6 */ + z2 = MULTIPLY(tmp10, FIX_0_541196100) + z5; /* c2-c6 */ + z4 = MULTIPLY(tmp12, FIX_1_306562965) + z5; /* c2+c6 */ + z3 = MULTIPLY(tmp11, FIX_0_707106781); /* c4 */ + + z11 = tmp7 + z3; /* phase 5 */ + z13 = tmp7 - z3; + + dataptr[5] = z13 + z2; /* phase 6 */ + dataptr[3] = z13 - z2; + dataptr[1] = z11 + z4; + dataptr[7] = z11 - z4; + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*7]; + tmp7 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*7]; + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*6]; + tmp6 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*6]; + tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*5]; + tmp5 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*5]; + tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*4]; + tmp4 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*4]; + + /* Even part */ + + tmp10 = tmp0 + tmp3; /* phase 2 */ + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + dataptr[DCTSIZE*0] = tmp10 + tmp11; /* phase 3 */ + dataptr[DCTSIZE*4] = tmp10 - tmp11; + + z1 = MULTIPLY(tmp12 + tmp13, FIX_0_707106781); /* c4 */ + dataptr[DCTSIZE*2] = tmp13 + z1; /* phase 5 */ + dataptr[DCTSIZE*6] = tmp13 - z1; + + /* Odd part */ + + tmp10 = tmp4 + tmp5; /* phase 2 */ + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + z5 = MULTIPLY(tmp10 - tmp12, FIX_0_382683433); /* c6 */ + z2 = MULTIPLY(tmp10, FIX_0_541196100) + z5; /* c2-c6 */ + z4 = MULTIPLY(tmp12, FIX_1_306562965) + z5; /* c2+c6 */ + z3 = MULTIPLY(tmp11, FIX_0_707106781); /* c4 */ + + z11 = tmp7 + z3; /* phase 5 */ + z13 = tmp7 - z3; + + dataptr[DCTSIZE*5] = z13 + z2; /* phase 6 */ + dataptr[DCTSIZE*3] = z13 - z2; + dataptr[DCTSIZE*1] = z11 + z4; + dataptr[DCTSIZE*7] = z11 - z4; + + dataptr++; /* advance pointer to next column */ + } +} + +#endif /* DCT_IFAST_SUPPORTED */ diff --git a/Engine/lib/ljpeg/jfdctint.c b/Engine/lib/ljpeg/jfdctint.c new file mode 100644 index 000000000..0a78b64ae --- /dev/null +++ b/Engine/lib/ljpeg/jfdctint.c @@ -0,0 +1,283 @@ +/* + * jfdctint.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a slow-but-accurate integer implementation of the + * forward DCT (Discrete Cosine Transform). + * + * A 2-D DCT can be done by 1-D DCT on each row followed by 1-D DCT + * on each column. Direct algorithms are also available, but they are + * much more complex and seem not to be any faster when reduced to code. + * + * This implementation is based on an algorithm described in + * C. Loeffler, A. Ligtenberg and G. Moschytz, "Practical Fast 1-D DCT + * Algorithms with 11 Multiplications", Proc. Int'l. Conf. on Acoustics, + * Speech, and Signal Processing 1989 (ICASSP '89), pp. 988-991. + * The primary algorithm described there uses 11 multiplies and 29 adds. + * We use their alternate method with 12 multiplies and 32 adds. + * The advantage of this method is that no data path contains more than one + * multiplication; this allows a very simple and accurate implementation in + * scaled fixed-point arithmetic, with a minimal number of shifts. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + +#ifdef DCT_ISLOW_SUPPORTED + + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + + +/* + * The poop on this scaling stuff is as follows: + * + * Each 1-D DCT step produces outputs which are a factor of sqrt(N) + * larger than the true DCT outputs. The final outputs are therefore + * a factor of N larger than desired; since N=8 this can be cured by + * a simple right shift at the end of the algorithm. The advantage of + * this arrangement is that we save two multiplications per 1-D DCT, + * because the y0 and y4 outputs need not be divided by sqrt(N). + * In the IJG code, this factor of 8 is removed by the quantization step + * (in jcdctmgr.c), NOT in this module. + * + * We have to do addition and subtraction of the integer inputs, which + * is no problem, and multiplication by fractional constants, which is + * a problem to do in integer arithmetic. We multiply all the constants + * by CONST_SCALE and convert them to integer constants (thus retaining + * CONST_BITS bits of precision in the constants). After doing a + * multiplication we have to divide the product by CONST_SCALE, with proper + * rounding, to produce the correct output. This division can be done + * cheaply as a right shift of CONST_BITS bits. We postpone shifting + * as long as possible so that partial sums can be added together with + * full fractional precision. + * + * The outputs of the first pass are scaled up by PASS1_BITS bits so that + * they are represented to better-than-integral precision. These outputs + * require BITS_IN_JSAMPLE + PASS1_BITS + 3 bits; this fits in a 16-bit word + * with the recommended scaling. (For 12-bit sample data, the intermediate + * array is INT32 anyway.) + * + * To avoid overflow of the 32-bit intermediate results in pass 2, we must + * have BITS_IN_JSAMPLE + CONST_BITS + PASS1_BITS <= 26. Error analysis + * shows that the values given below are the most effective. + */ + +#if BITS_IN_JSAMPLE == 8 +#define CONST_BITS 13 +#define PASS1_BITS 2 +#else +#define CONST_BITS 13 +#define PASS1_BITS 1 /* lose a little precision to avoid overflow */ +#endif + +/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus + * causing a lot of useless floating-point operations at run time. + * To get around this we use the following pre-calculated constants. + * If you change CONST_BITS you may want to add appropriate values. + * (With a reasonable C compiler, you can just rely on the FIX() macro...) + */ + +#if CONST_BITS == 13 +#define FIX_0_298631336 ((INT32) 2446) /* FIX(0.298631336) */ +#define FIX_0_390180644 ((INT32) 3196) /* FIX(0.390180644) */ +#define FIX_0_541196100 ((INT32) 4433) /* FIX(0.541196100) */ +#define FIX_0_765366865 ((INT32) 6270) /* FIX(0.765366865) */ +#define FIX_0_899976223 ((INT32) 7373) /* FIX(0.899976223) */ +#define FIX_1_175875602 ((INT32) 9633) /* FIX(1.175875602) */ +#define FIX_1_501321110 ((INT32) 12299) /* FIX(1.501321110) */ +#define FIX_1_847759065 ((INT32) 15137) /* FIX(1.847759065) */ +#define FIX_1_961570560 ((INT32) 16069) /* FIX(1.961570560) */ +#define FIX_2_053119869 ((INT32) 16819) /* FIX(2.053119869) */ +#define FIX_2_562915447 ((INT32) 20995) /* FIX(2.562915447) */ +#define FIX_3_072711026 ((INT32) 25172) /* FIX(3.072711026) */ +#else +#define FIX_0_298631336 FIX(0.298631336) +#define FIX_0_390180644 FIX(0.390180644) +#define FIX_0_541196100 FIX(0.541196100) +#define FIX_0_765366865 FIX(0.765366865) +#define FIX_0_899976223 FIX(0.899976223) +#define FIX_1_175875602 FIX(1.175875602) +#define FIX_1_501321110 FIX(1.501321110) +#define FIX_1_847759065 FIX(1.847759065) +#define FIX_1_961570560 FIX(1.961570560) +#define FIX_2_053119869 FIX(2.053119869) +#define FIX_2_562915447 FIX(2.562915447) +#define FIX_3_072711026 FIX(3.072711026) +#endif + + +/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result. + * For 8-bit samples with the recommended scaling, all the variable + * and constant values involved are no more than 16 bits wide, so a + * 16x16->32 bit multiply can be used instead of a full 32x32 multiply. + * For 12-bit samples, a full 32-bit multiplication will be needed. + */ + +#if BITS_IN_JSAMPLE == 8 +#define MULTIPLY(var,const) MULTIPLY16C16(var,const) +#else +#define MULTIPLY(var,const) ((var) * (const)) +#endif + + +/* + * Perform the forward DCT on one block of samples. + */ + +GLOBAL(void) +jpeg_fdct_islow (DCTELEM * data) +{ + INT32 tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + INT32 tmp10, tmp11, tmp12, tmp13; + INT32 z1, z2, z3, z4, z5; + DCTELEM *dataptr; + int ctr; + SHIFT_TEMPS + + /* Pass 1: process rows. */ + /* Note results are scaled up by sqrt(8) compared to a true DCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + tmp0 = dataptr[0] + dataptr[7]; + tmp7 = dataptr[0] - dataptr[7]; + tmp1 = dataptr[1] + dataptr[6]; + tmp6 = dataptr[1] - dataptr[6]; + tmp2 = dataptr[2] + dataptr[5]; + tmp5 = dataptr[2] - dataptr[5]; + tmp3 = dataptr[3] + dataptr[4]; + tmp4 = dataptr[3] - dataptr[4]; + + /* Even part per LL&M figure 1 --- note that published figure is faulty; + * rotator "sqrt(2)*c1" should be "sqrt(2)*c6". + */ + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + dataptr[0] = (DCTELEM) ((tmp10 + tmp11) << PASS1_BITS); + dataptr[4] = (DCTELEM) ((tmp10 - tmp11) << PASS1_BITS); + + z1 = MULTIPLY(tmp12 + tmp13, FIX_0_541196100); + dataptr[2] = (DCTELEM) DESCALE(z1 + MULTIPLY(tmp13, FIX_0_765366865), + CONST_BITS-PASS1_BITS); + dataptr[6] = (DCTELEM) DESCALE(z1 + MULTIPLY(tmp12, - FIX_1_847759065), + CONST_BITS-PASS1_BITS); + + /* Odd part per figure 8 --- note paper omits factor of sqrt(2). + * cK represents cos(K*pi/16). + * i0..i3 in the paper are tmp4..tmp7 here. + */ + + z1 = tmp4 + tmp7; + z2 = tmp5 + tmp6; + z3 = tmp4 + tmp6; + z4 = tmp5 + tmp7; + z5 = MULTIPLY(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */ + + tmp4 = MULTIPLY(tmp4, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ + tmp5 = MULTIPLY(tmp5, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ + tmp6 = MULTIPLY(tmp6, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ + tmp7 = MULTIPLY(tmp7, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ + z1 = MULTIPLY(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ + z2 = MULTIPLY(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ + z3 = MULTIPLY(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ + z4 = MULTIPLY(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ + + z3 += z5; + z4 += z5; + + dataptr[7] = (DCTELEM) DESCALE(tmp4 + z1 + z3, CONST_BITS-PASS1_BITS); + dataptr[5] = (DCTELEM) DESCALE(tmp5 + z2 + z4, CONST_BITS-PASS1_BITS); + dataptr[3] = (DCTELEM) DESCALE(tmp6 + z2 + z3, CONST_BITS-PASS1_BITS); + dataptr[1] = (DCTELEM) DESCALE(tmp7 + z1 + z4, CONST_BITS-PASS1_BITS); + + dataptr += DCTSIZE; /* advance pointer to next row */ + } + + /* Pass 2: process columns. + * We remove the PASS1_BITS scaling, but leave the results scaled up + * by an overall factor of 8. + */ + + dataptr = data; + for (ctr = DCTSIZE-1; ctr >= 0; ctr--) { + tmp0 = dataptr[DCTSIZE*0] + dataptr[DCTSIZE*7]; + tmp7 = dataptr[DCTSIZE*0] - dataptr[DCTSIZE*7]; + tmp1 = dataptr[DCTSIZE*1] + dataptr[DCTSIZE*6]; + tmp6 = dataptr[DCTSIZE*1] - dataptr[DCTSIZE*6]; + tmp2 = dataptr[DCTSIZE*2] + dataptr[DCTSIZE*5]; + tmp5 = dataptr[DCTSIZE*2] - dataptr[DCTSIZE*5]; + tmp3 = dataptr[DCTSIZE*3] + dataptr[DCTSIZE*4]; + tmp4 = dataptr[DCTSIZE*3] - dataptr[DCTSIZE*4]; + + /* Even part per LL&M figure 1 --- note that published figure is faulty; + * rotator "sqrt(2)*c1" should be "sqrt(2)*c6". + */ + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + dataptr[DCTSIZE*0] = (DCTELEM) DESCALE(tmp10 + tmp11, PASS1_BITS); + dataptr[DCTSIZE*4] = (DCTELEM) DESCALE(tmp10 - tmp11, PASS1_BITS); + + z1 = MULTIPLY(tmp12 + tmp13, FIX_0_541196100); + dataptr[DCTSIZE*2] = (DCTELEM) DESCALE(z1 + MULTIPLY(tmp13, FIX_0_765366865), + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*6] = (DCTELEM) DESCALE(z1 + MULTIPLY(tmp12, - FIX_1_847759065), + CONST_BITS+PASS1_BITS); + + /* Odd part per figure 8 --- note paper omits factor of sqrt(2). + * cK represents cos(K*pi/16). + * i0..i3 in the paper are tmp4..tmp7 here. + */ + + z1 = tmp4 + tmp7; + z2 = tmp5 + tmp6; + z3 = tmp4 + tmp6; + z4 = tmp5 + tmp7; + z5 = MULTIPLY(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */ + + tmp4 = MULTIPLY(tmp4, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ + tmp5 = MULTIPLY(tmp5, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ + tmp6 = MULTIPLY(tmp6, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ + tmp7 = MULTIPLY(tmp7, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ + z1 = MULTIPLY(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ + z2 = MULTIPLY(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ + z3 = MULTIPLY(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ + z4 = MULTIPLY(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ + + z3 += z5; + z4 += z5; + + dataptr[DCTSIZE*7] = (DCTELEM) DESCALE(tmp4 + z1 + z3, + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*5] = (DCTELEM) DESCALE(tmp5 + z2 + z4, + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*3] = (DCTELEM) DESCALE(tmp6 + z2 + z3, + CONST_BITS+PASS1_BITS); + dataptr[DCTSIZE*1] = (DCTELEM) DESCALE(tmp7 + z1 + z4, + CONST_BITS+PASS1_BITS); + + dataptr++; /* advance pointer to next column */ + } +} + +#endif /* DCT_ISLOW_SUPPORTED */ diff --git a/Engine/lib/ljpeg/jidctflt.c b/Engine/lib/ljpeg/jidctflt.c new file mode 100644 index 000000000..0188ce3df --- /dev/null +++ b/Engine/lib/ljpeg/jidctflt.c @@ -0,0 +1,242 @@ +/* + * jidctflt.c + * + * Copyright (C) 1994-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a floating-point implementation of the + * inverse DCT (Discrete Cosine Transform). In the IJG code, this routine + * must also perform dequantization of the input coefficients. + * + * This implementation should be more accurate than either of the integer + * IDCT implementations. However, it may not give the same results on all + * machines because of differences in roundoff behavior. Speed will depend + * on the hardware's floating point capacity. + * + * A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT + * on each row (or vice versa, but it's more convenient to emit a row at + * a time). Direct algorithms are also available, but they are much more + * complex and seem not to be any faster when reduced to code. + * + * This implementation is based on Arai, Agui, and Nakajima's algorithm for + * scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in + * Japanese, but the algorithm is described in the Pennebaker & Mitchell + * JPEG textbook (see REFERENCES section in file README). The following code + * is based directly on figure 4-8 in P&M. + * While an 8-point DCT cannot be done in less than 11 multiplies, it is + * possible to arrange the computation so that many of the multiplies are + * simple scalings of the final outputs. These multiplies can then be + * folded into the multiplications or divisions by the JPEG quantization + * table entries. The AA&N method leaves only 5 multiplies and 29 adds + * to be done in the DCT itself. + * The primary disadvantage of this method is that with a fixed-point + * implementation, accuracy is lost due to imprecise representation of the + * scaled quantization values. However, that problem does not arise if + * we use floating point arithmetic. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + +#ifdef DCT_FLOAT_SUPPORTED + + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + + +/* Dequantize a coefficient by multiplying it by the multiplier-table + * entry; produce a float result. + */ + +#define DEQUANTIZE(coef,quantval) (((FAST_FLOAT) (coef)) * (quantval)) + + +/* + * Perform dequantization and inverse DCT on one block of coefficients. + */ + +GLOBAL(void) +jpeg_idct_float (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + FAST_FLOAT tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + FAST_FLOAT tmp10, tmp11, tmp12, tmp13; + FAST_FLOAT z5, z10, z11, z12, z13; + JCOEFPTR inptr; + FLOAT_MULT_TYPE * quantptr; + FAST_FLOAT * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + FAST_FLOAT workspace[DCTSIZE2]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (FLOAT_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = DCTSIZE; ctr > 0; ctr--) { + /* Due to quantization, we will usually find that many of the input + * coefficients are zero, especially the AC terms. We can exploit this + * by short-circuiting the IDCT calculation for any column in which all + * the AC terms are zero. In that case each output is equal to the + * DC coefficient (with scale factor as needed). + * With typical images and quantization tables, half or more of the + * column DCT calculations can be simplified this way. + */ + + if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 && + inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*4] == 0 && + inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*6] == 0 && + inptr[DCTSIZE*7] == 0) { + /* AC terms all zero */ + FAST_FLOAT dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + + wsptr[DCTSIZE*0] = dcval; + wsptr[DCTSIZE*1] = dcval; + wsptr[DCTSIZE*2] = dcval; + wsptr[DCTSIZE*3] = dcval; + wsptr[DCTSIZE*4] = dcval; + wsptr[DCTSIZE*5] = dcval; + wsptr[DCTSIZE*6] = dcval; + wsptr[DCTSIZE*7] = dcval; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + continue; + } + + /* Even part */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + tmp2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + tmp3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + tmp10 = tmp0 + tmp2; /* phase 3 */ + tmp11 = tmp0 - tmp2; + + tmp13 = tmp1 + tmp3; /* phases 5-3 */ + tmp12 = (tmp1 - tmp3) * ((FAST_FLOAT) 1.414213562) - tmp13; /* 2*c4 */ + + tmp0 = tmp10 + tmp13; /* phase 2 */ + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + /* Odd part */ + + tmp4 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + tmp5 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + tmp6 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + tmp7 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + z13 = tmp6 + tmp5; /* phase 6 */ + z10 = tmp6 - tmp5; + z11 = tmp4 + tmp7; + z12 = tmp4 - tmp7; + + tmp7 = z11 + z13; /* phase 5 */ + tmp11 = (z11 - z13) * ((FAST_FLOAT) 1.414213562); /* 2*c4 */ + + z5 = (z10 + z12) * ((FAST_FLOAT) 1.847759065); /* 2*c2 */ + tmp10 = ((FAST_FLOAT) 1.082392200) * z12 - z5; /* 2*(c2-c6) */ + tmp12 = ((FAST_FLOAT) -2.613125930) * z10 + z5; /* -2*(c2+c6) */ + + tmp6 = tmp12 - tmp7; /* phase 2 */ + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + + wsptr[DCTSIZE*0] = tmp0 + tmp7; + wsptr[DCTSIZE*7] = tmp0 - tmp7; + wsptr[DCTSIZE*1] = tmp1 + tmp6; + wsptr[DCTSIZE*6] = tmp1 - tmp6; + wsptr[DCTSIZE*2] = tmp2 + tmp5; + wsptr[DCTSIZE*5] = tmp2 - tmp5; + wsptr[DCTSIZE*4] = tmp3 + tmp4; + wsptr[DCTSIZE*3] = tmp3 - tmp4; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + } + + /* Pass 2: process rows from work array, store into output array. */ + /* Note that we must descale the results by a factor of 8 == 2**3. */ + + wsptr = workspace; + for (ctr = 0; ctr < DCTSIZE; ctr++) { + outptr = output_buf[ctr] + output_col; + /* Rows of zeroes can be exploited in the same way as we did with columns. + * However, the column calculation has created many nonzero AC terms, so + * the simplification applies less often (typically 5% to 10% of the time). + * And testing floats for zero is relatively expensive, so we don't bother. + */ + + /* Even part */ + + tmp10 = wsptr[0] + wsptr[4]; + tmp11 = wsptr[0] - wsptr[4]; + + tmp13 = wsptr[2] + wsptr[6]; + tmp12 = (wsptr[2] - wsptr[6]) * ((FAST_FLOAT) 1.414213562) - tmp13; + + tmp0 = tmp10 + tmp13; + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + /* Odd part */ + + z13 = wsptr[5] + wsptr[3]; + z10 = wsptr[5] - wsptr[3]; + z11 = wsptr[1] + wsptr[7]; + z12 = wsptr[1] - wsptr[7]; + + tmp7 = z11 + z13; + tmp11 = (z11 - z13) * ((FAST_FLOAT) 1.414213562); + + z5 = (z10 + z12) * ((FAST_FLOAT) 1.847759065); /* 2*c2 */ + tmp10 = ((FAST_FLOAT) 1.082392200) * z12 - z5; /* 2*(c2-c6) */ + tmp12 = ((FAST_FLOAT) -2.613125930) * z10 + z5; /* -2*(c2+c6) */ + + tmp6 = tmp12 - tmp7; + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + + /* Final output stage: scale down by a factor of 8 and range-limit */ + + outptr[0] = range_limit[(int) DESCALE((INT32) (tmp0 + tmp7), 3) + & RANGE_MASK]; + outptr[7] = range_limit[(int) DESCALE((INT32) (tmp0 - tmp7), 3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) DESCALE((INT32) (tmp1 + tmp6), 3) + & RANGE_MASK]; + outptr[6] = range_limit[(int) DESCALE((INT32) (tmp1 - tmp6), 3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) DESCALE((INT32) (tmp2 + tmp5), 3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) DESCALE((INT32) (tmp2 - tmp5), 3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) DESCALE((INT32) (tmp3 + tmp4), 3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) DESCALE((INT32) (tmp3 - tmp4), 3) + & RANGE_MASK]; + + wsptr += DCTSIZE; /* advance pointer to next row */ + } +} + +#endif /* DCT_FLOAT_SUPPORTED */ diff --git a/Engine/lib/ljpeg/jidctfst.c b/Engine/lib/ljpeg/jidctfst.c new file mode 100644 index 000000000..dba4216fb --- /dev/null +++ b/Engine/lib/ljpeg/jidctfst.c @@ -0,0 +1,368 @@ +/* + * jidctfst.c + * + * Copyright (C) 1994-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a fast, not so accurate integer implementation of the + * inverse DCT (Discrete Cosine Transform). In the IJG code, this routine + * must also perform dequantization of the input coefficients. + * + * A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT + * on each row (or vice versa, but it's more convenient to emit a row at + * a time). Direct algorithms are also available, but they are much more + * complex and seem not to be any faster when reduced to code. + * + * This implementation is based on Arai, Agui, and Nakajima's algorithm for + * scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in + * Japanese, but the algorithm is described in the Pennebaker & Mitchell + * JPEG textbook (see REFERENCES section in file README). The following code + * is based directly on figure 4-8 in P&M. + * While an 8-point DCT cannot be done in less than 11 multiplies, it is + * possible to arrange the computation so that many of the multiplies are + * simple scalings of the final outputs. These multiplies can then be + * folded into the multiplications or divisions by the JPEG quantization + * table entries. The AA&N method leaves only 5 multiplies and 29 adds + * to be done in the DCT itself. + * The primary disadvantage of this method is that with fixed-point math, + * accuracy is lost due to imprecise representation of the scaled + * quantization values. The smaller the quantization table entry, the less + * precise the scaled value, so this implementation does worse with high- + * quality-setting files than with low-quality ones. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + +#ifdef DCT_IFAST_SUPPORTED + + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + + +/* Scaling decisions are generally the same as in the LL&M algorithm; + * see jidctint.c for more details. However, we choose to descale + * (right shift) multiplication products as soon as they are formed, + * rather than carrying additional fractional bits into subsequent additions. + * This compromises accuracy slightly, but it lets us save a few shifts. + * More importantly, 16-bit arithmetic is then adequate (for 8-bit samples) + * everywhere except in the multiplications proper; this saves a good deal + * of work on 16-bit-int machines. + * + * The dequantized coefficients are not integers because the AA&N scaling + * factors have been incorporated. We represent them scaled up by PASS1_BITS, + * so that the first and second IDCT rounds have the same input scaling. + * For 8-bit JSAMPLEs, we choose IFAST_SCALE_BITS = PASS1_BITS so as to + * avoid a descaling shift; this compromises accuracy rather drastically + * for small quantization table entries, but it saves a lot of shifts. + * For 12-bit JSAMPLEs, there's no hope of using 16x16 multiplies anyway, + * so we use a much larger scaling factor to preserve accuracy. + * + * A final compromise is to represent the multiplicative constants to only + * 8 fractional bits, rather than 13. This saves some shifting work on some + * machines, and may also reduce the cost of multiplication (since there + * are fewer one-bits in the constants). + */ + +#if BITS_IN_JSAMPLE == 8 +#define CONST_BITS 8 +#define PASS1_BITS 2 +#else +#define CONST_BITS 8 +#define PASS1_BITS 1 /* lose a little precision to avoid overflow */ +#endif + +/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus + * causing a lot of useless floating-point operations at run time. + * To get around this we use the following pre-calculated constants. + * If you change CONST_BITS you may want to add appropriate values. + * (With a reasonable C compiler, you can just rely on the FIX() macro...) + */ + +#if CONST_BITS == 8 +#define FIX_1_082392200 ((INT32) 277) /* FIX(1.082392200) */ +#define FIX_1_414213562 ((INT32) 362) /* FIX(1.414213562) */ +#define FIX_1_847759065 ((INT32) 473) /* FIX(1.847759065) */ +#define FIX_2_613125930 ((INT32) 669) /* FIX(2.613125930) */ +#else +#define FIX_1_082392200 FIX(1.082392200) +#define FIX_1_414213562 FIX(1.414213562) +#define FIX_1_847759065 FIX(1.847759065) +#define FIX_2_613125930 FIX(2.613125930) +#endif + + +/* We can gain a little more speed, with a further compromise in accuracy, + * by omitting the addition in a descaling shift. This yields an incorrectly + * rounded result half the time... + */ + +#ifndef USE_ACCURATE_ROUNDING +#undef DESCALE +#define DESCALE(x,n) RIGHT_SHIFT(x, n) +#endif + + +/* Multiply a DCTELEM variable by an INT32 constant, and immediately + * descale to yield a DCTELEM result. + */ + +#define MULTIPLY(var,const) ((DCTELEM) DESCALE((var) * (const), CONST_BITS)) + + +/* Dequantize a coefficient by multiplying it by the multiplier-table + * entry; produce a DCTELEM result. For 8-bit data a 16x16->16 + * multiplication will do. For 12-bit data, the multiplier table is + * declared INT32, so a 32-bit multiply will be used. + */ + +#if BITS_IN_JSAMPLE == 8 +#define DEQUANTIZE(coef,quantval) (((IFAST_MULT_TYPE) (coef)) * (quantval)) +#else +#define DEQUANTIZE(coef,quantval) \ + DESCALE((coef)*(quantval), IFAST_SCALE_BITS-PASS1_BITS) +#endif + + +/* Like DESCALE, but applies to a DCTELEM and produces an int. + * We assume that int right shift is unsigned if INT32 right shift is. + */ + +#ifdef RIGHT_SHIFT_IS_UNSIGNED +#define ISHIFT_TEMPS DCTELEM ishift_temp; +#if BITS_IN_JSAMPLE == 8 +#define DCTELEMBITS 16 /* DCTELEM may be 16 or 32 bits */ +#else +#define DCTELEMBITS 32 /* DCTELEM must be 32 bits */ +#endif +#define IRIGHT_SHIFT(x,shft) \ + ((ishift_temp = (x)) < 0 ? \ + (ishift_temp >> (shft)) | ((~((DCTELEM) 0)) << (DCTELEMBITS-(shft))) : \ + (ishift_temp >> (shft))) +#else +#define ISHIFT_TEMPS +#define IRIGHT_SHIFT(x,shft) ((x) >> (shft)) +#endif + +#ifdef USE_ACCURATE_ROUNDING +#define IDESCALE(x,n) ((int) IRIGHT_SHIFT((x) + (1 << ((n)-1)), n)) +#else +#define IDESCALE(x,n) ((int) IRIGHT_SHIFT(x, n)) +#endif + + +/* + * Perform dequantization and inverse DCT on one block of coefficients. + */ + +GLOBAL(void) +jpeg_idct_ifast (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + DCTELEM tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + DCTELEM tmp10, tmp11, tmp12, tmp13; + DCTELEM z5, z10, z11, z12, z13; + JCOEFPTR inptr; + IFAST_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[DCTSIZE2]; /* buffers data between passes */ + SHIFT_TEMPS /* for DESCALE */ + ISHIFT_TEMPS /* for IDESCALE */ + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (IFAST_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = DCTSIZE; ctr > 0; ctr--) { + /* Due to quantization, we will usually find that many of the input + * coefficients are zero, especially the AC terms. We can exploit this + * by short-circuiting the IDCT calculation for any column in which all + * the AC terms are zero. In that case each output is equal to the + * DC coefficient (with scale factor as needed). + * With typical images and quantization tables, half or more of the + * column DCT calculations can be simplified this way. + */ + + if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 && + inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*4] == 0 && + inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*6] == 0 && + inptr[DCTSIZE*7] == 0) { + /* AC terms all zero */ + int dcval = (int) DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + + wsptr[DCTSIZE*0] = dcval; + wsptr[DCTSIZE*1] = dcval; + wsptr[DCTSIZE*2] = dcval; + wsptr[DCTSIZE*3] = dcval; + wsptr[DCTSIZE*4] = dcval; + wsptr[DCTSIZE*5] = dcval; + wsptr[DCTSIZE*6] = dcval; + wsptr[DCTSIZE*7] = dcval; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + continue; + } + + /* Even part */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + tmp2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + tmp3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + tmp10 = tmp0 + tmp2; /* phase 3 */ + tmp11 = tmp0 - tmp2; + + tmp13 = tmp1 + tmp3; /* phases 5-3 */ + tmp12 = MULTIPLY(tmp1 - tmp3, FIX_1_414213562) - tmp13; /* 2*c4 */ + + tmp0 = tmp10 + tmp13; /* phase 2 */ + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + /* Odd part */ + + tmp4 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + tmp5 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + tmp6 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + tmp7 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + + z13 = tmp6 + tmp5; /* phase 6 */ + z10 = tmp6 - tmp5; + z11 = tmp4 + tmp7; + z12 = tmp4 - tmp7; + + tmp7 = z11 + z13; /* phase 5 */ + tmp11 = MULTIPLY(z11 - z13, FIX_1_414213562); /* 2*c4 */ + + z5 = MULTIPLY(z10 + z12, FIX_1_847759065); /* 2*c2 */ + tmp10 = MULTIPLY(z12, FIX_1_082392200) - z5; /* 2*(c2-c6) */ + tmp12 = MULTIPLY(z10, - FIX_2_613125930) + z5; /* -2*(c2+c6) */ + + tmp6 = tmp12 - tmp7; /* phase 2 */ + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + + wsptr[DCTSIZE*0] = (int) (tmp0 + tmp7); + wsptr[DCTSIZE*7] = (int) (tmp0 - tmp7); + wsptr[DCTSIZE*1] = (int) (tmp1 + tmp6); + wsptr[DCTSIZE*6] = (int) (tmp1 - tmp6); + wsptr[DCTSIZE*2] = (int) (tmp2 + tmp5); + wsptr[DCTSIZE*5] = (int) (tmp2 - tmp5); + wsptr[DCTSIZE*4] = (int) (tmp3 + tmp4); + wsptr[DCTSIZE*3] = (int) (tmp3 - tmp4); + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + } + + /* Pass 2: process rows from work array, store into output array. */ + /* Note that we must descale the results by a factor of 8 == 2**3, */ + /* and also undo the PASS1_BITS scaling. */ + + wsptr = workspace; + for (ctr = 0; ctr < DCTSIZE; ctr++) { + outptr = output_buf[ctr] + output_col; + /* Rows of zeroes can be exploited in the same way as we did with columns. + * However, the column calculation has created many nonzero AC terms, so + * the simplification applies less often (typically 5% to 10% of the time). + * On machines with very fast multiplication, it's possible that the + * test takes more time than it's worth. In that case this section + * may be commented out. + */ + +#ifndef NO_ZERO_ROW_TEST + if (wsptr[1] == 0 && wsptr[2] == 0 && wsptr[3] == 0 && wsptr[4] == 0 && + wsptr[5] == 0 && wsptr[6] == 0 && wsptr[7] == 0) { + /* AC terms all zero */ + JSAMPLE dcval = range_limit[IDESCALE(wsptr[0], PASS1_BITS+3) + & RANGE_MASK]; + + outptr[0] = dcval; + outptr[1] = dcval; + outptr[2] = dcval; + outptr[3] = dcval; + outptr[4] = dcval; + outptr[5] = dcval; + outptr[6] = dcval; + outptr[7] = dcval; + + wsptr += DCTSIZE; /* advance pointer to next row */ + continue; + } +#endif + + /* Even part */ + + tmp10 = ((DCTELEM) wsptr[0] + (DCTELEM) wsptr[4]); + tmp11 = ((DCTELEM) wsptr[0] - (DCTELEM) wsptr[4]); + + tmp13 = ((DCTELEM) wsptr[2] + (DCTELEM) wsptr[6]); + tmp12 = MULTIPLY((DCTELEM) wsptr[2] - (DCTELEM) wsptr[6], FIX_1_414213562) + - tmp13; + + tmp0 = tmp10 + tmp13; + tmp3 = tmp10 - tmp13; + tmp1 = tmp11 + tmp12; + tmp2 = tmp11 - tmp12; + + /* Odd part */ + + z13 = (DCTELEM) wsptr[5] + (DCTELEM) wsptr[3]; + z10 = (DCTELEM) wsptr[5] - (DCTELEM) wsptr[3]; + z11 = (DCTELEM) wsptr[1] + (DCTELEM) wsptr[7]; + z12 = (DCTELEM) wsptr[1] - (DCTELEM) wsptr[7]; + + tmp7 = z11 + z13; /* phase 5 */ + tmp11 = MULTIPLY(z11 - z13, FIX_1_414213562); /* 2*c4 */ + + z5 = MULTIPLY(z10 + z12, FIX_1_847759065); /* 2*c2 */ + tmp10 = MULTIPLY(z12, FIX_1_082392200) - z5; /* 2*(c2-c6) */ + tmp12 = MULTIPLY(z10, - FIX_2_613125930) + z5; /* -2*(c2+c6) */ + + tmp6 = tmp12 - tmp7; /* phase 2 */ + tmp5 = tmp11 - tmp6; + tmp4 = tmp10 + tmp5; + + /* Final output stage: scale down by a factor of 8 and range-limit */ + + outptr[0] = range_limit[IDESCALE(tmp0 + tmp7, PASS1_BITS+3) + & RANGE_MASK]; + outptr[7] = range_limit[IDESCALE(tmp0 - tmp7, PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[IDESCALE(tmp1 + tmp6, PASS1_BITS+3) + & RANGE_MASK]; + outptr[6] = range_limit[IDESCALE(tmp1 - tmp6, PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[IDESCALE(tmp2 + tmp5, PASS1_BITS+3) + & RANGE_MASK]; + outptr[5] = range_limit[IDESCALE(tmp2 - tmp5, PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[IDESCALE(tmp3 + tmp4, PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[IDESCALE(tmp3 - tmp4, PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += DCTSIZE; /* advance pointer to next row */ + } +} + +#endif /* DCT_IFAST_SUPPORTED */ diff --git a/Engine/lib/ljpeg/jidctint.c b/Engine/lib/ljpeg/jidctint.c new file mode 100644 index 000000000..a72b3207c --- /dev/null +++ b/Engine/lib/ljpeg/jidctint.c @@ -0,0 +1,389 @@ +/* + * jidctint.c + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a slow-but-accurate integer implementation of the + * inverse DCT (Discrete Cosine Transform). In the IJG code, this routine + * must also perform dequantization of the input coefficients. + * + * A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT + * on each row (or vice versa, but it's more convenient to emit a row at + * a time). Direct algorithms are also available, but they are much more + * complex and seem not to be any faster when reduced to code. + * + * This implementation is based on an algorithm described in + * C. Loeffler, A. Ligtenberg and G. Moschytz, "Practical Fast 1-D DCT + * Algorithms with 11 Multiplications", Proc. Int'l. Conf. on Acoustics, + * Speech, and Signal Processing 1989 (ICASSP '89), pp. 988-991. + * The primary algorithm described there uses 11 multiplies and 29 adds. + * We use their alternate method with 12 multiplies and 32 adds. + * The advantage of this method is that no data path contains more than one + * multiplication; this allows a very simple and accurate implementation in + * scaled fixed-point arithmetic, with a minimal number of shifts. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + +#ifdef DCT_ISLOW_SUPPORTED + + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + + +/* + * The poop on this scaling stuff is as follows: + * + * Each 1-D IDCT step produces outputs which are a factor of sqrt(N) + * larger than the true IDCT outputs. The final outputs are therefore + * a factor of N larger than desired; since N=8 this can be cured by + * a simple right shift at the end of the algorithm. The advantage of + * this arrangement is that we save two multiplications per 1-D IDCT, + * because the y0 and y4 inputs need not be divided by sqrt(N). + * + * We have to do addition and subtraction of the integer inputs, which + * is no problem, and multiplication by fractional constants, which is + * a problem to do in integer arithmetic. We multiply all the constants + * by CONST_SCALE and convert them to integer constants (thus retaining + * CONST_BITS bits of precision in the constants). After doing a + * multiplication we have to divide the product by CONST_SCALE, with proper + * rounding, to produce the correct output. This division can be done + * cheaply as a right shift of CONST_BITS bits. We postpone shifting + * as long as possible so that partial sums can be added together with + * full fractional precision. + * + * The outputs of the first pass are scaled up by PASS1_BITS bits so that + * they are represented to better-than-integral precision. These outputs + * require BITS_IN_JSAMPLE + PASS1_BITS + 3 bits; this fits in a 16-bit word + * with the recommended scaling. (To scale up 12-bit sample data further, an + * intermediate INT32 array would be needed.) + * + * To avoid overflow of the 32-bit intermediate results in pass 2, we must + * have BITS_IN_JSAMPLE + CONST_BITS + PASS1_BITS <= 26. Error analysis + * shows that the values given below are the most effective. + */ + +#if BITS_IN_JSAMPLE == 8 +#define CONST_BITS 13 +#define PASS1_BITS 2 +#else +#define CONST_BITS 13 +#define PASS1_BITS 1 /* lose a little precision to avoid overflow */ +#endif + +/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus + * causing a lot of useless floating-point operations at run time. + * To get around this we use the following pre-calculated constants. + * If you change CONST_BITS you may want to add appropriate values. + * (With a reasonable C compiler, you can just rely on the FIX() macro...) + */ + +#if CONST_BITS == 13 +#define FIX_0_298631336 ((INT32) 2446) /* FIX(0.298631336) */ +#define FIX_0_390180644 ((INT32) 3196) /* FIX(0.390180644) */ +#define FIX_0_541196100 ((INT32) 4433) /* FIX(0.541196100) */ +#define FIX_0_765366865 ((INT32) 6270) /* FIX(0.765366865) */ +#define FIX_0_899976223 ((INT32) 7373) /* FIX(0.899976223) */ +#define FIX_1_175875602 ((INT32) 9633) /* FIX(1.175875602) */ +#define FIX_1_501321110 ((INT32) 12299) /* FIX(1.501321110) */ +#define FIX_1_847759065 ((INT32) 15137) /* FIX(1.847759065) */ +#define FIX_1_961570560 ((INT32) 16069) /* FIX(1.961570560) */ +#define FIX_2_053119869 ((INT32) 16819) /* FIX(2.053119869) */ +#define FIX_2_562915447 ((INT32) 20995) /* FIX(2.562915447) */ +#define FIX_3_072711026 ((INT32) 25172) /* FIX(3.072711026) */ +#else +#define FIX_0_298631336 FIX(0.298631336) +#define FIX_0_390180644 FIX(0.390180644) +#define FIX_0_541196100 FIX(0.541196100) +#define FIX_0_765366865 FIX(0.765366865) +#define FIX_0_899976223 FIX(0.899976223) +#define FIX_1_175875602 FIX(1.175875602) +#define FIX_1_501321110 FIX(1.501321110) +#define FIX_1_847759065 FIX(1.847759065) +#define FIX_1_961570560 FIX(1.961570560) +#define FIX_2_053119869 FIX(2.053119869) +#define FIX_2_562915447 FIX(2.562915447) +#define FIX_3_072711026 FIX(3.072711026) +#endif + + +/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result. + * For 8-bit samples with the recommended scaling, all the variable + * and constant values involved are no more than 16 bits wide, so a + * 16x16->32 bit multiply can be used instead of a full 32x32 multiply. + * For 12-bit samples, a full 32-bit multiplication will be needed. + */ + +#if BITS_IN_JSAMPLE == 8 +#define MULTIPLY(var,const) MULTIPLY16C16(var,const) +#else +#define MULTIPLY(var,const) ((var) * (const)) +#endif + + +/* Dequantize a coefficient by multiplying it by the multiplier-table + * entry; produce an int result. In this module, both inputs and result + * are 16 bits or less, so either int or short multiply will work. + */ + +#define DEQUANTIZE(coef,quantval) (((ISLOW_MULT_TYPE) (coef)) * (quantval)) + + +/* + * Perform dequantization and inverse DCT on one block of coefficients. + */ + +GLOBAL(void) +jpeg_idct_islow (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp1, tmp2, tmp3; + INT32 tmp10, tmp11, tmp12, tmp13; + INT32 z1, z2, z3, z4, z5; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[DCTSIZE2]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + /* Note results are scaled up by sqrt(8) compared to a true IDCT; */ + /* furthermore, we scale the results by 2**PASS1_BITS. */ + + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = DCTSIZE; ctr > 0; ctr--) { + /* Due to quantization, we will usually find that many of the input + * coefficients are zero, especially the AC terms. We can exploit this + * by short-circuiting the IDCT calculation for any column in which all + * the AC terms are zero. In that case each output is equal to the + * DC coefficient (with scale factor as needed). + * With typical images and quantization tables, half or more of the + * column DCT calculations can be simplified this way. + */ + + if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 && + inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*4] == 0 && + inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*6] == 0 && + inptr[DCTSIZE*7] == 0) { + /* AC terms all zero */ + int dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]) << PASS1_BITS; + + wsptr[DCTSIZE*0] = dcval; + wsptr[DCTSIZE*1] = dcval; + wsptr[DCTSIZE*2] = dcval; + wsptr[DCTSIZE*3] = dcval; + wsptr[DCTSIZE*4] = dcval; + wsptr[DCTSIZE*5] = dcval; + wsptr[DCTSIZE*6] = dcval; + wsptr[DCTSIZE*7] = dcval; + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + continue; + } + + /* Even part: reverse the even part of the forward DCT. */ + /* The rotator is sqrt(2)*c(-6). */ + + z2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + z3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + z1 = MULTIPLY(z2 + z3, FIX_0_541196100); + tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065); + tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865); + + z2 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + z3 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); + + tmp0 = (z2 + z3) << CONST_BITS; + tmp1 = (z2 - z3) << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + /* Odd part per figure 8; the matrix is unitary and hence its + * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. + */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + tmp1 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + tmp2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + tmp3 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + + z1 = tmp0 + tmp3; + z2 = tmp1 + tmp2; + z3 = tmp0 + tmp2; + z4 = tmp1 + tmp3; + z5 = MULTIPLY(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */ + + tmp0 = MULTIPLY(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ + tmp1 = MULTIPLY(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ + tmp2 = MULTIPLY(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ + tmp3 = MULTIPLY(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ + z1 = MULTIPLY(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ + z2 = MULTIPLY(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ + z3 = MULTIPLY(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ + z4 = MULTIPLY(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 += z2 + z4; + tmp2 += z2 + z3; + tmp3 += z1 + z4; + + /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ + + wsptr[DCTSIZE*0] = (int) DESCALE(tmp10 + tmp3, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*7] = (int) DESCALE(tmp10 - tmp3, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*1] = (int) DESCALE(tmp11 + tmp2, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*6] = (int) DESCALE(tmp11 - tmp2, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*2] = (int) DESCALE(tmp12 + tmp1, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*5] = (int) DESCALE(tmp12 - tmp1, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*3] = (int) DESCALE(tmp13 + tmp0, CONST_BITS-PASS1_BITS); + wsptr[DCTSIZE*4] = (int) DESCALE(tmp13 - tmp0, CONST_BITS-PASS1_BITS); + + inptr++; /* advance pointers to next column */ + quantptr++; + wsptr++; + } + + /* Pass 2: process rows from work array, store into output array. */ + /* Note that we must descale the results by a factor of 8 == 2**3, */ + /* and also undo the PASS1_BITS scaling. */ + + wsptr = workspace; + for (ctr = 0; ctr < DCTSIZE; ctr++) { + outptr = output_buf[ctr] + output_col; + /* Rows of zeroes can be exploited in the same way as we did with columns. + * However, the column calculation has created many nonzero AC terms, so + * the simplification applies less often (typically 5% to 10% of the time). + * On machines with very fast multiplication, it's possible that the + * test takes more time than it's worth. In that case this section + * may be commented out. + */ + +#ifndef NO_ZERO_ROW_TEST + if (wsptr[1] == 0 && wsptr[2] == 0 && wsptr[3] == 0 && wsptr[4] == 0 && + wsptr[5] == 0 && wsptr[6] == 0 && wsptr[7] == 0) { + /* AC terms all zero */ + JSAMPLE dcval = range_limit[(int) DESCALE((INT32) wsptr[0], PASS1_BITS+3) + & RANGE_MASK]; + + outptr[0] = dcval; + outptr[1] = dcval; + outptr[2] = dcval; + outptr[3] = dcval; + outptr[4] = dcval; + outptr[5] = dcval; + outptr[6] = dcval; + outptr[7] = dcval; + + wsptr += DCTSIZE; /* advance pointer to next row */ + continue; + } +#endif + + /* Even part: reverse the even part of the forward DCT. */ + /* The rotator is sqrt(2)*c(-6). */ + + z2 = (INT32) wsptr[2]; + z3 = (INT32) wsptr[6]; + + z1 = MULTIPLY(z2 + z3, FIX_0_541196100); + tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065); + tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865); + + tmp0 = ((INT32) wsptr[0] + (INT32) wsptr[4]) << CONST_BITS; + tmp1 = ((INT32) wsptr[0] - (INT32) wsptr[4]) << CONST_BITS; + + tmp10 = tmp0 + tmp3; + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + /* Odd part per figure 8; the matrix is unitary and hence its + * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. + */ + + tmp0 = (INT32) wsptr[7]; + tmp1 = (INT32) wsptr[5]; + tmp2 = (INT32) wsptr[3]; + tmp3 = (INT32) wsptr[1]; + + z1 = tmp0 + tmp3; + z2 = tmp1 + tmp2; + z3 = tmp0 + tmp2; + z4 = tmp1 + tmp3; + z5 = MULTIPLY(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */ + + tmp0 = MULTIPLY(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ + tmp1 = MULTIPLY(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ + tmp2 = MULTIPLY(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ + tmp3 = MULTIPLY(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ + z1 = MULTIPLY(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ + z2 = MULTIPLY(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ + z3 = MULTIPLY(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ + z4 = MULTIPLY(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ + + z3 += z5; + z4 += z5; + + tmp0 += z1 + z3; + tmp1 += z2 + z4; + tmp2 += z2 + z3; + tmp3 += z1 + z4; + + /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ + + outptr[0] = range_limit[(int) DESCALE(tmp10 + tmp3, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[7] = range_limit[(int) DESCALE(tmp10 - tmp3, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[1] = range_limit[(int) DESCALE(tmp11 + tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[6] = range_limit[(int) DESCALE(tmp11 - tmp2, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[2] = range_limit[(int) DESCALE(tmp12 + tmp1, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[5] = range_limit[(int) DESCALE(tmp12 - tmp1, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[3] = range_limit[(int) DESCALE(tmp13 + tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + outptr[4] = range_limit[(int) DESCALE(tmp13 - tmp0, + CONST_BITS+PASS1_BITS+3) + & RANGE_MASK]; + + wsptr += DCTSIZE; /* advance pointer to next row */ + } +} + +#endif /* DCT_ISLOW_SUPPORTED */ diff --git a/Engine/lib/ljpeg/jidctred.c b/Engine/lib/ljpeg/jidctred.c new file mode 100644 index 000000000..421f3c7ca --- /dev/null +++ b/Engine/lib/ljpeg/jidctred.c @@ -0,0 +1,398 @@ +/* + * jidctred.c + * + * Copyright (C) 1994-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains inverse-DCT routines that produce reduced-size output: + * either 4x4, 2x2, or 1x1 pixels from an 8x8 DCT block. + * + * The implementation is based on the Loeffler, Ligtenberg and Moschytz (LL&M) + * algorithm used in jidctint.c. We simply replace each 8-to-8 1-D IDCT step + * with an 8-to-4 step that produces the four averages of two adjacent outputs + * (or an 8-to-2 step producing two averages of four outputs, for 2x2 output). + * These steps were derived by computing the corresponding values at the end + * of the normal LL&M code, then simplifying as much as possible. + * + * 1x1 is trivial: just take the DC coefficient divided by 8. + * + * See jidctint.c for additional comments. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jdct.h" /* Private declarations for DCT subsystem */ + +#ifdef IDCT_SCALING_SUPPORTED + + +/* + * This module is specialized to the case DCTSIZE = 8. + */ + +#if DCTSIZE != 8 + Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ +#endif + + +/* Scaling is the same as in jidctint.c. */ + +#if BITS_IN_JSAMPLE == 8 +#define CONST_BITS 13 +#define PASS1_BITS 2 +#else +#define CONST_BITS 13 +#define PASS1_BITS 1 /* lose a little precision to avoid overflow */ +#endif + +/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus + * causing a lot of useless floating-point operations at run time. + * To get around this we use the following pre-calculated constants. + * If you change CONST_BITS you may want to add appropriate values. + * (With a reasonable C compiler, you can just rely on the FIX() macro...) + */ + +#if CONST_BITS == 13 +#define FIX_0_211164243 ((INT32) 1730) /* FIX(0.211164243) */ +#define FIX_0_509795579 ((INT32) 4176) /* FIX(0.509795579) */ +#define FIX_0_601344887 ((INT32) 4926) /* FIX(0.601344887) */ +#define FIX_0_720959822 ((INT32) 5906) /* FIX(0.720959822) */ +#define FIX_0_765366865 ((INT32) 6270) /* FIX(0.765366865) */ +#define FIX_0_850430095 ((INT32) 6967) /* FIX(0.850430095) */ +#define FIX_0_899976223 ((INT32) 7373) /* FIX(0.899976223) */ +#define FIX_1_061594337 ((INT32) 8697) /* FIX(1.061594337) */ +#define FIX_1_272758580 ((INT32) 10426) /* FIX(1.272758580) */ +#define FIX_1_451774981 ((INT32) 11893) /* FIX(1.451774981) */ +#define FIX_1_847759065 ((INT32) 15137) /* FIX(1.847759065) */ +#define FIX_2_172734803 ((INT32) 17799) /* FIX(2.172734803) */ +#define FIX_2_562915447 ((INT32) 20995) /* FIX(2.562915447) */ +#define FIX_3_624509785 ((INT32) 29692) /* FIX(3.624509785) */ +#else +#define FIX_0_211164243 FIX(0.211164243) +#define FIX_0_509795579 FIX(0.509795579) +#define FIX_0_601344887 FIX(0.601344887) +#define FIX_0_720959822 FIX(0.720959822) +#define FIX_0_765366865 FIX(0.765366865) +#define FIX_0_850430095 FIX(0.850430095) +#define FIX_0_899976223 FIX(0.899976223) +#define FIX_1_061594337 FIX(1.061594337) +#define FIX_1_272758580 FIX(1.272758580) +#define FIX_1_451774981 FIX(1.451774981) +#define FIX_1_847759065 FIX(1.847759065) +#define FIX_2_172734803 FIX(2.172734803) +#define FIX_2_562915447 FIX(2.562915447) +#define FIX_3_624509785 FIX(3.624509785) +#endif + + +/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result. + * For 8-bit samples with the recommended scaling, all the variable + * and constant values involved are no more than 16 bits wide, so a + * 16x16->32 bit multiply can be used instead of a full 32x32 multiply. + * For 12-bit samples, a full 32-bit multiplication will be needed. + */ + +#if BITS_IN_JSAMPLE == 8 +#define MULTIPLY(var,const) MULTIPLY16C16(var,const) +#else +#define MULTIPLY(var,const) ((var) * (const)) +#endif + + +/* Dequantize a coefficient by multiplying it by the multiplier-table + * entry; produce an int result. In this module, both inputs and result + * are 16 bits or less, so either int or short multiply will work. + */ + +#define DEQUANTIZE(coef,quantval) (((ISLOW_MULT_TYPE) (coef)) * (quantval)) + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a reduced-size 4x4 output block. + */ + +GLOBAL(void) +jpeg_idct_4x4 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp2, tmp10, tmp12; + INT32 z1, z2, z3, z4; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[DCTSIZE*4]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = DCTSIZE; ctr > 0; inptr++, quantptr++, wsptr++, ctr--) { + /* Don't bother to process column 4, because second pass won't use it */ + if (ctr == DCTSIZE-4) + continue; + if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 && + inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*5] == 0 && + inptr[DCTSIZE*6] == 0 && inptr[DCTSIZE*7] == 0) { + /* AC terms all zero; we need not examine term 4 for 4x4 output */ + int dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]) << PASS1_BITS; + + wsptr[DCTSIZE*0] = dcval; + wsptr[DCTSIZE*1] = dcval; + wsptr[DCTSIZE*2] = dcval; + wsptr[DCTSIZE*3] = dcval; + + continue; + } + + /* Even part */ + + tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp0 <<= (CONST_BITS+1); + + z2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); + z3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); + + tmp2 = MULTIPLY(z2, FIX_1_847759065) + MULTIPLY(z3, - FIX_0_765366865); + + tmp10 = tmp0 + tmp2; + tmp12 = tmp0 - tmp2; + + /* Odd part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + z2 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + z3 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + z4 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + + tmp0 = MULTIPLY(z1, - FIX_0_211164243) /* sqrt(2) * (c3-c1) */ + + MULTIPLY(z2, FIX_1_451774981) /* sqrt(2) * (c3+c7) */ + + MULTIPLY(z3, - FIX_2_172734803) /* sqrt(2) * (-c1-c5) */ + + MULTIPLY(z4, FIX_1_061594337); /* sqrt(2) * (c5+c7) */ + + tmp2 = MULTIPLY(z1, - FIX_0_509795579) /* sqrt(2) * (c7-c5) */ + + MULTIPLY(z2, - FIX_0_601344887) /* sqrt(2) * (c5-c1) */ + + MULTIPLY(z3, FIX_0_899976223) /* sqrt(2) * (c3-c7) */ + + MULTIPLY(z4, FIX_2_562915447); /* sqrt(2) * (c1+c3) */ + + /* Final output stage */ + + wsptr[DCTSIZE*0] = (int) DESCALE(tmp10 + tmp2, CONST_BITS-PASS1_BITS+1); + wsptr[DCTSIZE*3] = (int) DESCALE(tmp10 - tmp2, CONST_BITS-PASS1_BITS+1); + wsptr[DCTSIZE*1] = (int) DESCALE(tmp12 + tmp0, CONST_BITS-PASS1_BITS+1); + wsptr[DCTSIZE*2] = (int) DESCALE(tmp12 - tmp0, CONST_BITS-PASS1_BITS+1); + } + + /* Pass 2: process 4 rows from work array, store into output array. */ + + wsptr = workspace; + for (ctr = 0; ctr < 4; ctr++) { + outptr = output_buf[ctr] + output_col; + /* It's not clear whether a zero row test is worthwhile here ... */ + +#ifndef NO_ZERO_ROW_TEST + if (wsptr[1] == 0 && wsptr[2] == 0 && wsptr[3] == 0 && + wsptr[5] == 0 && wsptr[6] == 0 && wsptr[7] == 0) { + /* AC terms all zero */ + JSAMPLE dcval = range_limit[(int) DESCALE((INT32) wsptr[0], PASS1_BITS+3) + & RANGE_MASK]; + + outptr[0] = dcval; + outptr[1] = dcval; + outptr[2] = dcval; + outptr[3] = dcval; + + wsptr += DCTSIZE; /* advance pointer to next row */ + continue; + } +#endif + + /* Even part */ + + tmp0 = ((INT32) wsptr[0]) << (CONST_BITS+1); + + tmp2 = MULTIPLY((INT32) wsptr[2], FIX_1_847759065) + + MULTIPLY((INT32) wsptr[6], - FIX_0_765366865); + + tmp10 = tmp0 + tmp2; + tmp12 = tmp0 - tmp2; + + /* Odd part */ + + z1 = (INT32) wsptr[7]; + z2 = (INT32) wsptr[5]; + z3 = (INT32) wsptr[3]; + z4 = (INT32) wsptr[1]; + + tmp0 = MULTIPLY(z1, - FIX_0_211164243) /* sqrt(2) * (c3-c1) */ + + MULTIPLY(z2, FIX_1_451774981) /* sqrt(2) * (c3+c7) */ + + MULTIPLY(z3, - FIX_2_172734803) /* sqrt(2) * (-c1-c5) */ + + MULTIPLY(z4, FIX_1_061594337); /* sqrt(2) * (c5+c7) */ + + tmp2 = MULTIPLY(z1, - FIX_0_509795579) /* sqrt(2) * (c7-c5) */ + + MULTIPLY(z2, - FIX_0_601344887) /* sqrt(2) * (c5-c1) */ + + MULTIPLY(z3, FIX_0_899976223) /* sqrt(2) * (c3-c7) */ + + MULTIPLY(z4, FIX_2_562915447); /* sqrt(2) * (c1+c3) */ + + /* Final output stage */ + + outptr[0] = range_limit[(int) DESCALE(tmp10 + tmp2, + CONST_BITS+PASS1_BITS+3+1) + & RANGE_MASK]; + outptr[3] = range_limit[(int) DESCALE(tmp10 - tmp2, + CONST_BITS+PASS1_BITS+3+1) + & RANGE_MASK]; + outptr[1] = range_limit[(int) DESCALE(tmp12 + tmp0, + CONST_BITS+PASS1_BITS+3+1) + & RANGE_MASK]; + outptr[2] = range_limit[(int) DESCALE(tmp12 - tmp0, + CONST_BITS+PASS1_BITS+3+1) + & RANGE_MASK]; + + wsptr += DCTSIZE; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a reduced-size 2x2 output block. + */ + +GLOBAL(void) +jpeg_idct_2x2 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + INT32 tmp0, tmp10, z1; + JCOEFPTR inptr; + ISLOW_MULT_TYPE * quantptr; + int * wsptr; + JSAMPROW outptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + int ctr; + int workspace[DCTSIZE*2]; /* buffers data between passes */ + SHIFT_TEMPS + + /* Pass 1: process columns from input, store into work array. */ + + inptr = coef_block; + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + wsptr = workspace; + for (ctr = DCTSIZE; ctr > 0; inptr++, quantptr++, wsptr++, ctr--) { + /* Don't bother to process columns 2,4,6 */ + if (ctr == DCTSIZE-2 || ctr == DCTSIZE-4 || ctr == DCTSIZE-6) + continue; + if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*3] == 0 && + inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*7] == 0) { + /* AC terms all zero; we need not examine terms 2,4,6 for 2x2 output */ + int dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]) << PASS1_BITS; + + wsptr[DCTSIZE*0] = dcval; + wsptr[DCTSIZE*1] = dcval; + + continue; + } + + /* Even part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); + tmp10 = z1 << (CONST_BITS+2); + + /* Odd part */ + + z1 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); + tmp0 = MULTIPLY(z1, - FIX_0_720959822); /* sqrt(2) * (c7-c5+c3-c1) */ + z1 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); + tmp0 += MULTIPLY(z1, FIX_0_850430095); /* sqrt(2) * (-c1+c3+c5+c7) */ + z1 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); + tmp0 += MULTIPLY(z1, - FIX_1_272758580); /* sqrt(2) * (-c1+c3-c5-c7) */ + z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); + tmp0 += MULTIPLY(z1, FIX_3_624509785); /* sqrt(2) * (c1+c3+c5+c7) */ + + /* Final output stage */ + + wsptr[DCTSIZE*0] = (int) DESCALE(tmp10 + tmp0, CONST_BITS-PASS1_BITS+2); + wsptr[DCTSIZE*1] = (int) DESCALE(tmp10 - tmp0, CONST_BITS-PASS1_BITS+2); + } + + /* Pass 2: process 2 rows from work array, store into output array. */ + + wsptr = workspace; + for (ctr = 0; ctr < 2; ctr++) { + outptr = output_buf[ctr] + output_col; + /* It's not clear whether a zero row test is worthwhile here ... */ + +#ifndef NO_ZERO_ROW_TEST + if (wsptr[1] == 0 && wsptr[3] == 0 && wsptr[5] == 0 && wsptr[7] == 0) { + /* AC terms all zero */ + JSAMPLE dcval = range_limit[(int) DESCALE((INT32) wsptr[0], PASS1_BITS+3) + & RANGE_MASK]; + + outptr[0] = dcval; + outptr[1] = dcval; + + wsptr += DCTSIZE; /* advance pointer to next row */ + continue; + } +#endif + + /* Even part */ + + tmp10 = ((INT32) wsptr[0]) << (CONST_BITS+2); + + /* Odd part */ + + tmp0 = MULTIPLY((INT32) wsptr[7], - FIX_0_720959822) /* sqrt(2) * (c7-c5+c3-c1) */ + + MULTIPLY((INT32) wsptr[5], FIX_0_850430095) /* sqrt(2) * (-c1+c3+c5+c7) */ + + MULTIPLY((INT32) wsptr[3], - FIX_1_272758580) /* sqrt(2) * (-c1+c3-c5-c7) */ + + MULTIPLY((INT32) wsptr[1], FIX_3_624509785); /* sqrt(2) * (c1+c3+c5+c7) */ + + /* Final output stage */ + + outptr[0] = range_limit[(int) DESCALE(tmp10 + tmp0, + CONST_BITS+PASS1_BITS+3+2) + & RANGE_MASK]; + outptr[1] = range_limit[(int) DESCALE(tmp10 - tmp0, + CONST_BITS+PASS1_BITS+3+2) + & RANGE_MASK]; + + wsptr += DCTSIZE; /* advance pointer to next row */ + } +} + + +/* + * Perform dequantization and inverse DCT on one block of coefficients, + * producing a reduced-size 1x1 output block. + */ + +GLOBAL(void) +jpeg_idct_1x1 (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col) +{ + int dcval; + ISLOW_MULT_TYPE * quantptr; + JSAMPLE *range_limit = IDCT_range_limit(cinfo); + SHIFT_TEMPS + + /* We hardly need an inverse DCT routine for this: just take the + * average pixel value, which is one-eighth of the DC coefficient. + */ + quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; + dcval = DEQUANTIZE(coef_block[0], quantptr[0]); + dcval = (int) DESCALE((INT32) dcval, 3); + + output_buf[0][output_col] = range_limit[dcval & RANGE_MASK]; +} + +#endif /* IDCT_SCALING_SUPPORTED */ diff --git a/Engine/lib/ljpeg/jinclude.h b/Engine/lib/ljpeg/jinclude.h new file mode 100644 index 000000000..042574348 --- /dev/null +++ b/Engine/lib/ljpeg/jinclude.h @@ -0,0 +1,91 @@ +/* + * jinclude.h + * + * Copyright (C) 1991-1994, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file exists to provide a single place to fix any problems with + * including the wrong system include files. (Common problems are taken + * care of by the standard jconfig symbols, but on really weird systems + * you may have to edit this file.) + * + * NOTE: this file is NOT intended to be included by applications using the + * JPEG library. Most applications need only include jpeglib.h. + */ + + +/* Include auto-config file to find out which system include files we need. */ + +#include "jconfig.h" /* auto configuration options */ +#define JCONFIG_INCLUDED /* so that jpeglib.h doesn't do it again */ + +/* + * We need the NULL macro and size_t typedef. + * On an ANSI-conforming system it is sufficient to include . + * Otherwise, we get them from or ; we may have to + * pull in as well. + * Note that the core JPEG library does not require ; + * only the default error handler and data source/destination modules do. + * But we must pull it in because of the references to FILE in jpeglib.h. + * You can remove those references if you want to compile without . + */ + +#ifdef HAVE_STDDEF_H +#include +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef NEED_SYS_TYPES_H +#include +#endif + +#include + +/* + * We need memory copying and zeroing functions, plus strncpy(). + * ANSI and System V implementations declare these in . + * BSD doesn't have the mem() functions, but it does have bcopy()/bzero(). + * Some systems may declare memset and memcpy in . + * + * NOTE: we assume the size parameters to these functions are of type size_t. + * Change the casts in these macros if not! + */ + +#ifdef NEED_BSD_STRINGS + +#include +#define MEMZERO(target,size) bzero((void *)(target), (jpeg_size_t)(size)) +#define MEMCOPY(dest,src,size) bcopy((const void *)(src), (void *)(dest), (jpeg_size_t)(size)) + +#else /* not BSD, assume ANSI/SysV string lib */ + +#include +#define MEMZERO(target,size) memset((void *)(target), 0, (jpeg_size_t)(size)) +#define MEMCOPY(dest,src,size) memcpy((void *)(dest), (const void *)(src), (jpeg_size_t)(size)) + +#endif + +/* + * In ANSI C, and indeed any rational implementation, size_t is also the + * type returned by sizeof(). However, it seems there are some irrational + * implementations out there, in which sizeof() returns an int even though + * size_t is defined as long or unsigned long. To ensure consistent results + * we always use this SIZEOF() macro in place of using sizeof() directly. + */ + +#define SIZEOF(object) ((jpeg_size_t) sizeof(object)) + +/* + * The modules that use fread() and fwrite() always invoke them through + * these macros. On some systems you may need to twiddle the argument casts. + * CAUTION: argument order is different from underlying functions! + */ + +//#define JFREAD(file,buf,sizeofbuf) \ +// ((size_t) fread((void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) +//#define JFWRITE(file,buf,sizeofbuf) \ +// ((size_t) fwrite((const void *) (buf), (size_t) 1, (size_t) (sizeofbuf), (file))) diff --git a/Engine/lib/ljpeg/jmemmgr.c b/Engine/lib/ljpeg/jmemmgr.c new file mode 100644 index 000000000..562924d98 --- /dev/null +++ b/Engine/lib/ljpeg/jmemmgr.c @@ -0,0 +1,1118 @@ +/* + * jmemmgr.c + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains the JPEG system-independent memory management + * routines. This code is usable across a wide variety of machines; most + * of the system dependencies have been isolated in a separate file. + * The major functions provided here are: + * * pool-based allocation and freeing of memory; + * * policy decisions about how to divide available memory among the + * virtual arrays; + * * control logic for swapping virtual arrays between main memory and + * backing storage. + * The separate system-dependent file provides the actual backing-storage + * access code, and it contains the policy decision about how much total + * main memory to use. + * This file is system-dependent in the sense that some of its functions + * are unnecessary in some systems. For example, if there is enough virtual + * memory so that backing storage will never be used, much of the virtual + * array control logic could be removed. (Of course, if you have that much + * memory then you shouldn't care about a little bit of unused code...) + */ + +#define JPEG_INTERNALS +#define AM_MEMORY_MANAGER /* we define jvirt_Xarray_control structs */ +#include "jinclude.h" +#include "jpeglib.h" +#include "jmemsys.h" /* import the system-dependent declarations */ + +#ifndef NO_GETENV +#ifndef HAVE_STDLIB_H /* should declare getenv() */ +extern char * getenv JPP((const char * name)); +#endif +#endif + + +/* + * Some important notes: + * The allocation routines provided here must never return NULL. + * They should exit to error_exit if unsuccessful. + * + * It's not a good idea to try to merge the sarray and barray routines, + * even though they are textually almost the same, because samples are + * usually stored as bytes while coefficients are shorts or ints. Thus, + * in machines where byte pointers have a different representation from + * word pointers, the resulting machine code could not be the same. + */ + + +/* + * Many machines require storage alignment: longs must start on 4-byte + * boundaries, doubles on 8-byte boundaries, etc. On such machines, malloc() + * always returns pointers that are multiples of the worst-case alignment + * requirement, and we had better do so too. + * There isn't any really portable way to determine the worst-case alignment + * requirement. This module assumes that the alignment requirement is + * multiples of sizeof(ALIGN_TYPE). + * By default, we define ALIGN_TYPE as double. This is necessary on some + * workstations (where doubles really do need 8-byte alignment) and will work + * fine on nearly everything. If your machine has lesser alignment needs, + * you can save a few bytes by making ALIGN_TYPE smaller. + * The only place I know of where this will NOT work is certain Macintosh + * 680x0 compilers that define double as a 10-byte IEEE extended float. + * Doing 10-byte alignment is counterproductive because longwords won't be + * aligned well. Put "#define ALIGN_TYPE long" in jconfig.h if you have + * such a compiler. + */ + +#ifndef ALIGN_TYPE /* so can override from jconfig.h */ +#define ALIGN_TYPE double +#endif + + +/* + * We allocate objects from "pools", where each pool is gotten with a single + * request to jpeg_get_small() or jpeg_get_large(). There is no per-object + * overhead within a pool, except for alignment padding. Each pool has a + * header with a link to the next pool of the same class. + * Small and large pool headers are identical except that the latter's + * link pointer must be FAR on 80x86 machines. + * Notice that the "real" header fields are union'ed with a dummy ALIGN_TYPE + * field. This forces the compiler to make SIZEOF(small_pool_hdr) a multiple + * of the alignment requirement of ALIGN_TYPE. + */ + +typedef union small_pool_struct * small_pool_ptr; + +typedef union small_pool_struct { + struct { + small_pool_ptr next; /* next in list of pools */ + jpeg_size_t bytes_used; /* how many bytes already used within pool */ + jpeg_size_t bytes_left; /* bytes still available in this pool */ + } hdr; + ALIGN_TYPE dummy; /* included in union to ensure alignment */ +} small_pool_hdr; + +typedef union large_pool_struct FAR * large_pool_ptr; + +typedef union large_pool_struct { + struct { + large_pool_ptr next; /* next in list of pools */ + jpeg_size_t bytes_used; /* how many bytes already used within pool */ + jpeg_size_t bytes_left; /* bytes still available in this pool */ + } hdr; + ALIGN_TYPE dummy; /* included in union to ensure alignment */ +} large_pool_hdr; + + +/* + * Here is the full definition of a memory manager object. + */ + +typedef struct { + struct jpeg_memory_mgr pub; /* public fields */ + + /* Each pool identifier (lifetime class) names a linked list of pools. */ + small_pool_ptr small_list[JPOOL_NUMPOOLS]; + large_pool_ptr large_list[JPOOL_NUMPOOLS]; + + /* Since we only have one lifetime class of virtual arrays, only one + * linked list is necessary (for each datatype). Note that the virtual + * array control blocks being linked together are actually stored somewhere + * in the small-pool list. + */ + jvirt_sarray_ptr virt_sarray_list; + jvirt_barray_ptr virt_barray_list; + + /* This counts total space obtained from jpeg_get_small/large */ + long total_space_allocated; + + /* alloc_sarray and alloc_barray set this value for use by virtual + * array routines. + */ + JDIMENSION last_rowsperchunk; /* from most recent alloc_sarray/barray */ +} my_memory_mgr; + +typedef my_memory_mgr * my_mem_ptr; + + +/* + * The control blocks for virtual arrays. + * Note that these blocks are allocated in the "small" pool area. + * System-dependent info for the associated backing store (if any) is hidden + * inside the backing_store_info struct. + */ + +struct jvirt_sarray_control { + JSAMPARRAY mem_buffer; /* => the in-memory buffer */ + JDIMENSION rows_in_array; /* total virtual array height */ + JDIMENSION samplesperrow; /* width of array (and of memory buffer) */ + JDIMENSION maxaccess; /* max rows accessed by access_virt_sarray */ + JDIMENSION rows_in_mem; /* height of memory buffer */ + JDIMENSION rowsperchunk; /* allocation chunk size in mem_buffer */ + JDIMENSION cur_start_row; /* first logical row # in the buffer */ + JDIMENSION first_undef_row; /* row # of first uninitialized row */ + boolean pre_zero; /* pre-zero mode requested? */ + boolean dirty; /* do current buffer contents need written? */ + boolean b_s_open; /* is backing-store data valid? */ + jvirt_sarray_ptr next; /* link to next virtual sarray control block */ + backing_store_info b_s_info; /* System-dependent control info */ +}; + +struct jvirt_barray_control { + JBLOCKARRAY mem_buffer; /* => the in-memory buffer */ + JDIMENSION rows_in_array; /* total virtual array height */ + JDIMENSION blocksperrow; /* width of array (and of memory buffer) */ + JDIMENSION maxaccess; /* max rows accessed by access_virt_barray */ + JDIMENSION rows_in_mem; /* height of memory buffer */ + JDIMENSION rowsperchunk; /* allocation chunk size in mem_buffer */ + JDIMENSION cur_start_row; /* first logical row # in the buffer */ + JDIMENSION first_undef_row; /* row # of first uninitialized row */ + boolean pre_zero; /* pre-zero mode requested? */ + boolean dirty; /* do current buffer contents need written? */ + boolean b_s_open; /* is backing-store data valid? */ + jvirt_barray_ptr next; /* link to next virtual barray control block */ + backing_store_info b_s_info; /* System-dependent control info */ +}; + + +#ifdef MEM_STATS /* optional extra stuff for statistics */ + +LOCAL(void) +print_mem_stats (j_common_ptr cinfo, int pool_id) +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + small_pool_ptr shdr_ptr; + large_pool_ptr lhdr_ptr; + + /* Since this is only a debugging stub, we can cheat a little by using + * fprintf directly rather than going through the trace message code. + * This is helpful because message parm array can't handle longs. + */ + fprintf(stderr, "Freeing pool %d, total space = %ld\n", + pool_id, mem->total_space_allocated); + + for (lhdr_ptr = mem->large_list[pool_id]; lhdr_ptr != NULL; + lhdr_ptr = lhdr_ptr->hdr.next) { + fprintf(stderr, " Large chunk used %ld\n", + (long) lhdr_ptr->hdr.bytes_used); + } + + for (shdr_ptr = mem->small_list[pool_id]; shdr_ptr != NULL; + shdr_ptr = shdr_ptr->hdr.next) { + fprintf(stderr, " Small chunk used %ld free %ld\n", + (long) shdr_ptr->hdr.bytes_used, + (long) shdr_ptr->hdr.bytes_left); + } +} + +#endif /* MEM_STATS */ + + +LOCAL(void) +out_of_memory (j_common_ptr cinfo, int which) +/* Report an out-of-memory error and stop execution */ +/* If we compiled MEM_STATS support, report alloc requests before dying */ +{ +#ifdef MEM_STATS + cinfo->err->trace_level = 2; /* force self_destruct to report stats */ +#endif + ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, which); +} + + +/* + * Allocation of "small" objects. + * + * For these, we use pooled storage. When a new pool must be created, + * we try to get enough space for the current request plus a "slop" factor, + * where the slop will be the amount of leftover space in the new pool. + * The speed vs. space tradeoff is largely determined by the slop values. + * A different slop value is provided for each pool class (lifetime), + * and we also distinguish the first pool of a class from later ones. + * NOTE: the values given work fairly well on both 16- and 32-bit-int + * machines, but may be too small if longs are 64 bits or more. + */ + +static const jpeg_size_t first_pool_slop[JPOOL_NUMPOOLS] = +{ + 1600, /* first PERMANENT pool */ + 16000 /* first IMAGE pool */ +}; + +static const jpeg_size_t extra_pool_slop[JPOOL_NUMPOOLS] = +{ + 0, /* additional PERMANENT pools */ + 5000 /* additional IMAGE pools */ +}; + +#define MIN_SLOP 50 /* greater than 0 to avoid futile looping */ + + +METHODDEF(void *) +alloc_small (j_common_ptr cinfo, int pool_id, jpeg_size_t sizeofobject) +/* Allocate a "small" object */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + small_pool_ptr hdr_ptr, prev_hdr_ptr; + char * data_ptr; + jpeg_size_t odd_bytes, min_request, slop; + + /* Check for unsatisfiable request (do now to ensure no overflow below) */ + if (sizeofobject > (jpeg_size_t) (MAX_ALLOC_CHUNK-SIZEOF(small_pool_hdr))) + out_of_memory(cinfo, 1); /* request exceeds malloc's ability */ + + /* Round up the requested size to a multiple of SIZEOF(ALIGN_TYPE) */ + odd_bytes = sizeofobject % SIZEOF(ALIGN_TYPE); + if (odd_bytes > 0) + sizeofobject += SIZEOF(ALIGN_TYPE) - odd_bytes; + + /* See if space is available in any existing pool */ + if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + prev_hdr_ptr = NULL; + hdr_ptr = mem->small_list[pool_id]; + while (hdr_ptr != NULL) { + if (hdr_ptr->hdr.bytes_left >= sizeofobject) + break; /* found pool with enough space */ + prev_hdr_ptr = hdr_ptr; + hdr_ptr = hdr_ptr->hdr.next; + } + + /* Time to make a new pool? */ + if (hdr_ptr == NULL) { + /* min_request is what we need now, slop is what will be leftover */ + min_request = sizeofobject + SIZEOF(small_pool_hdr); + if (prev_hdr_ptr == NULL) /* first pool in class? */ + slop = first_pool_slop[pool_id]; + else + slop = extra_pool_slop[pool_id]; + /* Don't ask for more than MAX_ALLOC_CHUNK */ + if (slop > (jpeg_size_t) (MAX_ALLOC_CHUNK-min_request)) + slop = (jpeg_size_t) (MAX_ALLOC_CHUNK-min_request); + /* Try to get space, if fail reduce slop and try again */ + for (;;) { + hdr_ptr = (small_pool_ptr) jpeg_get_small(cinfo, min_request + slop); + if (hdr_ptr != NULL) + break; + slop /= 2; + if (slop < MIN_SLOP) /* give up when it gets real small */ + out_of_memory(cinfo, 2); /* jpeg_get_small failed */ + } + mem->total_space_allocated += min_request + slop; + /* Success, initialize the new pool header and add to end of list */ + hdr_ptr->hdr.next = NULL; + hdr_ptr->hdr.bytes_used = 0; + hdr_ptr->hdr.bytes_left = sizeofobject + slop; + if (prev_hdr_ptr == NULL) /* first pool in class? */ + mem->small_list[pool_id] = hdr_ptr; + else + prev_hdr_ptr->hdr.next = hdr_ptr; + } + + /* OK, allocate the object from the current pool */ + data_ptr = (char *) (hdr_ptr + 1); /* point to first data byte in pool */ + data_ptr += hdr_ptr->hdr.bytes_used; /* point to place for object */ + hdr_ptr->hdr.bytes_used += sizeofobject; + hdr_ptr->hdr.bytes_left -= sizeofobject; + + return (void *) data_ptr; +} + + +/* + * Allocation of "large" objects. + * + * The external semantics of these are the same as "small" objects, + * except that FAR pointers are used on 80x86. However the pool + * management heuristics are quite different. We assume that each + * request is large enough that it may as well be passed directly to + * jpeg_get_large; the pool management just links everything together + * so that we can free it all on demand. + * Note: the major use of "large" objects is in JSAMPARRAY and JBLOCKARRAY + * structures. The routines that create these structures (see below) + * deliberately bunch rows together to ensure a large request size. + */ + +METHODDEF(void FAR *) +alloc_large (j_common_ptr cinfo, int pool_id, jpeg_size_t sizeofobject) +/* Allocate a "large" object */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + large_pool_ptr hdr_ptr; + jpeg_size_t odd_bytes; + + /* Check for unsatisfiable request (do now to ensure no overflow below) */ + if (sizeofobject > (jpeg_size_t) (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr))) + out_of_memory(cinfo, 3); /* request exceeds malloc's ability */ + + /* Round up the requested size to a multiple of SIZEOF(ALIGN_TYPE) */ + odd_bytes = sizeofobject % SIZEOF(ALIGN_TYPE); + if (odd_bytes > 0) + sizeofobject += SIZEOF(ALIGN_TYPE) - odd_bytes; + + /* Always make a new pool */ + if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + hdr_ptr = (large_pool_ptr) jpeg_get_large(cinfo, sizeofobject + + SIZEOF(large_pool_hdr)); + if (hdr_ptr == NULL) + out_of_memory(cinfo, 4); /* jpeg_get_large failed */ + mem->total_space_allocated += sizeofobject + SIZEOF(large_pool_hdr); + + /* Success, initialize the new pool header and add to list */ + hdr_ptr->hdr.next = mem->large_list[pool_id]; + /* We maintain space counts in each pool header for statistical purposes, + * even though they are not needed for allocation. + */ + hdr_ptr->hdr.bytes_used = sizeofobject; + hdr_ptr->hdr.bytes_left = 0; + mem->large_list[pool_id] = hdr_ptr; + + return (void FAR *) (hdr_ptr + 1); /* point to first data byte in pool */ +} + + +/* + * Creation of 2-D sample arrays. + * The pointers are in near heap, the samples themselves in FAR heap. + * + * To minimize allocation overhead and to allow I/O of large contiguous + * blocks, we allocate the sample rows in groups of as many rows as possible + * without exceeding MAX_ALLOC_CHUNK total bytes per allocation request. + * NB: the virtual array control routines, later in this file, know about + * this chunking of rows. The rowsperchunk value is left in the mem manager + * object so that it can be saved away if this sarray is the workspace for + * a virtual array. + */ + +METHODDEF(JSAMPARRAY) +alloc_sarray (j_common_ptr cinfo, int pool_id, + JDIMENSION samplesperrow, JDIMENSION numrows) +/* Allocate a 2-D sample array */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + JSAMPARRAY result; + JSAMPROW workspace; + JDIMENSION rowsperchunk, currow, i; + long ltemp; + + /* Calculate max # of rows allowed in one allocation chunk */ + ltemp = (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr)) / + ((long) samplesperrow * SIZEOF(JSAMPLE)); + if (ltemp <= 0) + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + if (ltemp < (long) numrows) + rowsperchunk = (JDIMENSION) ltemp; + else + rowsperchunk = numrows; + mem->last_rowsperchunk = rowsperchunk; + + /* Get space for row pointers (small object) */ + result = (JSAMPARRAY) alloc_small(cinfo, pool_id, + (jpeg_size_t) (numrows * SIZEOF(JSAMPROW))); + + /* Get the rows themselves (large objects) */ + currow = 0; + while (currow < numrows) { + rowsperchunk = MIN(rowsperchunk, numrows - currow); + workspace = (JSAMPROW) alloc_large(cinfo, pool_id, + (jpeg_size_t) ((jpeg_size_t) rowsperchunk * (jpeg_size_t) samplesperrow + * SIZEOF(JSAMPLE))); + for (i = rowsperchunk; i > 0; i--) { + result[currow++] = workspace; + workspace += samplesperrow; + } + } + + return result; +} + + +/* + * Creation of 2-D coefficient-block arrays. + * This is essentially the same as the code for sample arrays, above. + */ + +METHODDEF(JBLOCKARRAY) +alloc_barray (j_common_ptr cinfo, int pool_id, + JDIMENSION blocksperrow, JDIMENSION numrows) +/* Allocate a 2-D coefficient-block array */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + JBLOCKARRAY result; + JBLOCKROW workspace; + JDIMENSION rowsperchunk, currow, i; + long ltemp; + + /* Calculate max # of rows allowed in one allocation chunk */ + ltemp = (MAX_ALLOC_CHUNK-SIZEOF(large_pool_hdr)) / + ((long) blocksperrow * SIZEOF(JBLOCK)); + if (ltemp <= 0) + ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); + if (ltemp < (long) numrows) + rowsperchunk = (JDIMENSION) ltemp; + else + rowsperchunk = numrows; + mem->last_rowsperchunk = rowsperchunk; + + /* Get space for row pointers (small object) */ + result = (JBLOCKARRAY) alloc_small(cinfo, pool_id, + (jpeg_size_t) (numrows * SIZEOF(JBLOCKROW))); + + /* Get the rows themselves (large objects) */ + currow = 0; + while (currow < numrows) { + rowsperchunk = MIN(rowsperchunk, numrows - currow); + workspace = (JBLOCKROW) alloc_large(cinfo, pool_id, + (jpeg_size_t) ((jpeg_size_t) rowsperchunk * (jpeg_size_t) blocksperrow + * SIZEOF(JBLOCK))); + for (i = rowsperchunk; i > 0; i--) { + result[currow++] = workspace; + workspace += blocksperrow; + } + } + + return result; +} + + +/* + * About virtual array management: + * + * The above "normal" array routines are only used to allocate strip buffers + * (as wide as the image, but just a few rows high). Full-image-sized buffers + * are handled as "virtual" arrays. The array is still accessed a strip at a + * time, but the memory manager must save the whole array for repeated + * accesses. The intended implementation is that there is a strip buffer in + * memory (as high as is possible given the desired memory limit), plus a + * backing file that holds the rest of the array. + * + * The request_virt_array routines are told the total size of the image and + * the maximum number of rows that will be accessed at once. The in-memory + * buffer must be at least as large as the maxaccess value. + * + * The request routines create control blocks but not the in-memory buffers. + * That is postponed until realize_virt_arrays is called. At that time the + * total amount of space needed is known (approximately, anyway), so free + * memory can be divided up fairly. + * + * The access_virt_array routines are responsible for making a specific strip + * area accessible (after reading or writing the backing file, if necessary). + * Note that the access routines are told whether the caller intends to modify + * the accessed strip; during a read-only pass this saves having to rewrite + * data to disk. The access routines are also responsible for pre-zeroing + * any newly accessed rows, if pre-zeroing was requested. + * + * In current usage, the access requests are usually for nonoverlapping + * strips; that is, successive access start_row numbers differ by exactly + * num_rows = maxaccess. This means we can get good performance with simple + * buffer dump/reload logic, by making the in-memory buffer be a multiple + * of the access height; then there will never be accesses across bufferload + * boundaries. The code will still work with overlapping access requests, + * but it doesn't handle bufferload overlaps very efficiently. + */ + + +METHODDEF(jvirt_sarray_ptr) +request_virt_sarray (j_common_ptr cinfo, int pool_id, boolean pre_zero, + JDIMENSION samplesperrow, JDIMENSION numrows, + JDIMENSION maxaccess) +/* Request a virtual 2-D sample array */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + jvirt_sarray_ptr result; + + /* Only IMAGE-lifetime virtual arrays are currently supported */ + if (pool_id != JPOOL_IMAGE) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + /* get control block */ + result = (jvirt_sarray_ptr) alloc_small(cinfo, pool_id, + SIZEOF(struct jvirt_sarray_control)); + + result->mem_buffer = NULL; /* marks array not yet realized */ + result->rows_in_array = numrows; + result->samplesperrow = samplesperrow; + result->maxaccess = maxaccess; + result->pre_zero = pre_zero; + result->b_s_open = FALSE; /* no associated backing-store object */ + result->next = mem->virt_sarray_list; /* add to list of virtual arrays */ + mem->virt_sarray_list = result; + + return result; +} + + +METHODDEF(jvirt_barray_ptr) +request_virt_barray (j_common_ptr cinfo, int pool_id, boolean pre_zero, + JDIMENSION blocksperrow, JDIMENSION numrows, + JDIMENSION maxaccess) +/* Request a virtual 2-D coefficient-block array */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + jvirt_barray_ptr result; + + /* Only IMAGE-lifetime virtual arrays are currently supported */ + if (pool_id != JPOOL_IMAGE) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + + /* get control block */ + result = (jvirt_barray_ptr) alloc_small(cinfo, pool_id, + SIZEOF(struct jvirt_barray_control)); + + result->mem_buffer = NULL; /* marks array not yet realized */ + result->rows_in_array = numrows; + result->blocksperrow = blocksperrow; + result->maxaccess = maxaccess; + result->pre_zero = pre_zero; + result->b_s_open = FALSE; /* no associated backing-store object */ + result->next = mem->virt_barray_list; /* add to list of virtual arrays */ + mem->virt_barray_list = result; + + return result; +} + + +METHODDEF(void) +realize_virt_arrays (j_common_ptr cinfo) +/* Allocate the in-memory buffers for any unrealized virtual arrays */ +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + long space_per_minheight, maximum_space, avail_mem; + long minheights, max_minheights; + jvirt_sarray_ptr sptr; + jvirt_barray_ptr bptr; + + /* Compute the minimum space needed (maxaccess rows in each buffer) + * and the maximum space needed (full image height in each buffer). + * These may be of use to the system-dependent jpeg_mem_available routine. + */ + space_per_minheight = 0; + maximum_space = 0; + for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) { + if (sptr->mem_buffer == NULL) { /* if not realized yet */ + space_per_minheight += (long) sptr->maxaccess * + (long) sptr->samplesperrow * SIZEOF(JSAMPLE); + maximum_space += (long) sptr->rows_in_array * + (long) sptr->samplesperrow * SIZEOF(JSAMPLE); + } + } + for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) { + if (bptr->mem_buffer == NULL) { /* if not realized yet */ + space_per_minheight += (long) bptr->maxaccess * + (long) bptr->blocksperrow * SIZEOF(JBLOCK); + maximum_space += (long) bptr->rows_in_array * + (long) bptr->blocksperrow * SIZEOF(JBLOCK); + } + } + + if (space_per_minheight <= 0) + return; /* no unrealized arrays, no work */ + + /* Determine amount of memory to actually use; this is system-dependent. */ + avail_mem = jpeg_mem_available(cinfo, space_per_minheight, maximum_space, + mem->total_space_allocated); + + /* If the maximum space needed is available, make all the buffers full + * height; otherwise parcel it out with the same number of minheights + * in each buffer. + */ + if (avail_mem >= maximum_space) + max_minheights = 1000000000L; + else { + max_minheights = avail_mem / space_per_minheight; + /* If there doesn't seem to be enough space, try to get the minimum + * anyway. This allows a "stub" implementation of jpeg_mem_available(). + */ + if (max_minheights <= 0) + max_minheights = 1; + } + + /* Allocate the in-memory buffers and initialize backing store as needed. */ + + for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) { + if (sptr->mem_buffer == NULL) { /* if not realized yet */ + minheights = ((long) sptr->rows_in_array - 1L) / sptr->maxaccess + 1L; + if (minheights <= max_minheights) { + /* This buffer fits in memory */ + sptr->rows_in_mem = sptr->rows_in_array; + } else { + /* It doesn't fit in memory, create backing store. */ + sptr->rows_in_mem = (JDIMENSION) (max_minheights * sptr->maxaccess); + jpeg_open_backing_store(cinfo, & sptr->b_s_info, + (long) sptr->rows_in_array * + (long) sptr->samplesperrow * + (long) SIZEOF(JSAMPLE)); + sptr->b_s_open = TRUE; + } + sptr->mem_buffer = alloc_sarray(cinfo, JPOOL_IMAGE, + sptr->samplesperrow, sptr->rows_in_mem); + sptr->rowsperchunk = mem->last_rowsperchunk; + sptr->cur_start_row = 0; + sptr->first_undef_row = 0; + sptr->dirty = FALSE; + } + } + + for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) { + if (bptr->mem_buffer == NULL) { /* if not realized yet */ + minheights = ((long) bptr->rows_in_array - 1L) / bptr->maxaccess + 1L; + if (minheights <= max_minheights) { + /* This buffer fits in memory */ + bptr->rows_in_mem = bptr->rows_in_array; + } else { + /* It doesn't fit in memory, create backing store. */ + bptr->rows_in_mem = (JDIMENSION) (max_minheights * bptr->maxaccess); + jpeg_open_backing_store(cinfo, & bptr->b_s_info, + (long) bptr->rows_in_array * + (long) bptr->blocksperrow * + (long) SIZEOF(JBLOCK)); + bptr->b_s_open = TRUE; + } + bptr->mem_buffer = alloc_barray(cinfo, JPOOL_IMAGE, + bptr->blocksperrow, bptr->rows_in_mem); + bptr->rowsperchunk = mem->last_rowsperchunk; + bptr->cur_start_row = 0; + bptr->first_undef_row = 0; + bptr->dirty = FALSE; + } + } +} + + +LOCAL(void) +do_sarray_io (j_common_ptr cinfo, jvirt_sarray_ptr ptr, boolean writing) +/* Do backing store read or write of a virtual sample array */ +{ + long bytesperrow, file_offset, byte_count, rows, thisrow, i; + + bytesperrow = (long) ptr->samplesperrow * SIZEOF(JSAMPLE); + file_offset = ptr->cur_start_row * bytesperrow; + /* Loop to read or write each allocation chunk in mem_buffer */ + for (i = 0; i < (long) ptr->rows_in_mem; i += ptr->rowsperchunk) { + /* One chunk, but check for short chunk at end of buffer */ + rows = MIN((long) ptr->rowsperchunk, (long) ptr->rows_in_mem - i); + /* Transfer no more than is currently defined */ + thisrow = (long) ptr->cur_start_row + i; + rows = MIN(rows, (long) ptr->first_undef_row - thisrow); + /* Transfer no more than fits in file */ + rows = MIN(rows, (long) ptr->rows_in_array - thisrow); + if (rows <= 0) /* this chunk might be past end of file! */ + break; + byte_count = rows * bytesperrow; + if (writing) + (*ptr->b_s_info.write_backing_store) (cinfo, & ptr->b_s_info, + (void FAR *) ptr->mem_buffer[i], + file_offset, byte_count); + else + (*ptr->b_s_info.read_backing_store) (cinfo, & ptr->b_s_info, + (void FAR *) ptr->mem_buffer[i], + file_offset, byte_count); + file_offset += byte_count; + } +} + + +LOCAL(void) +do_barray_io (j_common_ptr cinfo, jvirt_barray_ptr ptr, boolean writing) +/* Do backing store read or write of a virtual coefficient-block array */ +{ + long bytesperrow, file_offset, byte_count, rows, thisrow, i; + + bytesperrow = (long) ptr->blocksperrow * SIZEOF(JBLOCK); + file_offset = ptr->cur_start_row * bytesperrow; + /* Loop to read or write each allocation chunk in mem_buffer */ + for (i = 0; i < (long) ptr->rows_in_mem; i += ptr->rowsperchunk) { + /* One chunk, but check for short chunk at end of buffer */ + rows = MIN((long) ptr->rowsperchunk, (long) ptr->rows_in_mem - i); + /* Transfer no more than is currently defined */ + thisrow = (long) ptr->cur_start_row + i; + rows = MIN(rows, (long) ptr->first_undef_row - thisrow); + /* Transfer no more than fits in file */ + rows = MIN(rows, (long) ptr->rows_in_array - thisrow); + if (rows <= 0) /* this chunk might be past end of file! */ + break; + byte_count = rows * bytesperrow; + if (writing) + (*ptr->b_s_info.write_backing_store) (cinfo, & ptr->b_s_info, + (void FAR *) ptr->mem_buffer[i], + file_offset, byte_count); + else + (*ptr->b_s_info.read_backing_store) (cinfo, & ptr->b_s_info, + (void FAR *) ptr->mem_buffer[i], + file_offset, byte_count); + file_offset += byte_count; + } +} + + +METHODDEF(JSAMPARRAY) +access_virt_sarray (j_common_ptr cinfo, jvirt_sarray_ptr ptr, + JDIMENSION start_row, JDIMENSION num_rows, + boolean writable) +/* Access the part of a virtual sample array starting at start_row */ +/* and extending for num_rows rows. writable is true if */ +/* caller intends to modify the accessed area. */ +{ + JDIMENSION end_row = start_row + num_rows; + JDIMENSION undef_row; + + /* debugging check */ + if (end_row > ptr->rows_in_array || num_rows > ptr->maxaccess || + ptr->mem_buffer == NULL) + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + + /* Make the desired part of the virtual array accessible */ + if (start_row < ptr->cur_start_row || + end_row > ptr->cur_start_row+ptr->rows_in_mem) { + if (! ptr->b_s_open) + ERREXIT(cinfo, JERR_VIRTUAL_BUG); + /* Flush old buffer contents if necessary */ + if (ptr->dirty) { + do_sarray_io(cinfo, ptr, TRUE); + ptr->dirty = FALSE; + } + /* Decide what part of virtual array to access. + * Algorithm: if target address > current window, assume forward scan, + * load starting at target address. If target address < current window, + * assume backward scan, load so that target area is top of window. + * Note that when switching from forward write to forward read, will have + * start_row = 0, so the limiting case applies and we load from 0 anyway. + */ + if (start_row > ptr->cur_start_row) { + ptr->cur_start_row = start_row; + } else { + /* use long arithmetic here to avoid overflow & unsigned problems */ + long ltemp; + + ltemp = (long) end_row - (long) ptr->rows_in_mem; + if (ltemp < 0) + ltemp = 0; /* don't fall off front end of file */ + ptr->cur_start_row = (JDIMENSION) ltemp; + } + /* Read in the selected part of the array. + * During the initial write pass, we will do no actual read + * because the selected part is all undefined. + */ + do_sarray_io(cinfo, ptr, FALSE); + } + /* Ensure the accessed part of the array is defined; prezero if needed. + * To improve locality of access, we only prezero the part of the array + * that the caller is about to access, not the entire in-memory array. + */ + if (ptr->first_undef_row < end_row) { + if (ptr->first_undef_row < start_row) { + if (writable) /* writer skipped over a section of array */ + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + undef_row = start_row; /* but reader is allowed to read ahead */ + } else { + undef_row = ptr->first_undef_row; + } + if (writable) + ptr->first_undef_row = end_row; + if (ptr->pre_zero) { + jpeg_size_t bytesperrow = (jpeg_size_t) ptr->samplesperrow * SIZEOF(JSAMPLE); + undef_row -= ptr->cur_start_row; /* make indexes relative to buffer */ + end_row -= ptr->cur_start_row; + while (undef_row < end_row) { + jzero_far((void FAR *) ptr->mem_buffer[undef_row], bytesperrow); + undef_row++; + } + } else { + if (! writable) /* reader looking at undefined data */ + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + } + } + /* Flag the buffer dirty if caller will write in it */ + if (writable) + ptr->dirty = TRUE; + /* Return address of proper part of the buffer */ + return ptr->mem_buffer + (start_row - ptr->cur_start_row); +} + + +METHODDEF(JBLOCKARRAY) +access_virt_barray (j_common_ptr cinfo, jvirt_barray_ptr ptr, + JDIMENSION start_row, JDIMENSION num_rows, + boolean writable) +/* Access the part of a virtual block array starting at start_row */ +/* and extending for num_rows rows. writable is true if */ +/* caller intends to modify the accessed area. */ +{ + JDIMENSION end_row = start_row + num_rows; + JDIMENSION undef_row; + + /* debugging check */ + if (end_row > ptr->rows_in_array || num_rows > ptr->maxaccess || + ptr->mem_buffer == NULL) + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + + /* Make the desired part of the virtual array accessible */ + if (start_row < ptr->cur_start_row || + end_row > ptr->cur_start_row+ptr->rows_in_mem) { + if (! ptr->b_s_open) + ERREXIT(cinfo, JERR_VIRTUAL_BUG); + /* Flush old buffer contents if necessary */ + if (ptr->dirty) { + do_barray_io(cinfo, ptr, TRUE); + ptr->dirty = FALSE; + } + /* Decide what part of virtual array to access. + * Algorithm: if target address > current window, assume forward scan, + * load starting at target address. If target address < current window, + * assume backward scan, load so that target area is top of window. + * Note that when switching from forward write to forward read, will have + * start_row = 0, so the limiting case applies and we load from 0 anyway. + */ + if (start_row > ptr->cur_start_row) { + ptr->cur_start_row = start_row; + } else { + /* use long arithmetic here to avoid overflow & unsigned problems */ + long ltemp; + + ltemp = (long) end_row - (long) ptr->rows_in_mem; + if (ltemp < 0) + ltemp = 0; /* don't fall off front end of file */ + ptr->cur_start_row = (JDIMENSION) ltemp; + } + /* Read in the selected part of the array. + * During the initial write pass, we will do no actual read + * because the selected part is all undefined. + */ + do_barray_io(cinfo, ptr, FALSE); + } + /* Ensure the accessed part of the array is defined; prezero if needed. + * To improve locality of access, we only prezero the part of the array + * that the caller is about to access, not the entire in-memory array. + */ + if (ptr->first_undef_row < end_row) { + if (ptr->first_undef_row < start_row) { + if (writable) /* writer skipped over a section of array */ + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + undef_row = start_row; /* but reader is allowed to read ahead */ + } else { + undef_row = ptr->first_undef_row; + } + if (writable) + ptr->first_undef_row = end_row; + if (ptr->pre_zero) { + jpeg_size_t bytesperrow = (jpeg_size_t) ptr->blocksperrow * SIZEOF(JBLOCK); + undef_row -= ptr->cur_start_row; /* make indexes relative to buffer */ + end_row -= ptr->cur_start_row; + while (undef_row < end_row) { + jzero_far((void FAR *) ptr->mem_buffer[undef_row], bytesperrow); + undef_row++; + } + } else { + if (! writable) /* reader looking at undefined data */ + ERREXIT(cinfo, JERR_BAD_VIRTUAL_ACCESS); + } + } + /* Flag the buffer dirty if caller will write in it */ + if (writable) + ptr->dirty = TRUE; + /* Return address of proper part of the buffer */ + return ptr->mem_buffer + (start_row - ptr->cur_start_row); +} + + +/* + * Release all objects belonging to a specified pool. + */ + +METHODDEF(void) +free_pool (j_common_ptr cinfo, int pool_id) +{ + my_mem_ptr mem = (my_mem_ptr) cinfo->mem; + small_pool_ptr shdr_ptr; + large_pool_ptr lhdr_ptr; + jpeg_size_t space_freed; + + if (pool_id < 0 || pool_id >= JPOOL_NUMPOOLS) + ERREXIT1(cinfo, JERR_BAD_POOL_ID, pool_id); /* safety check */ + +#ifdef MEM_STATS + if (cinfo->err->trace_level > 1) + print_mem_stats(cinfo, pool_id); /* print pool's memory usage statistics */ +#endif + + /* If freeing IMAGE pool, close any virtual arrays first */ + if (pool_id == JPOOL_IMAGE) { + jvirt_sarray_ptr sptr; + jvirt_barray_ptr bptr; + + for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) { + if (sptr->b_s_open) { /* there may be no backing store */ + sptr->b_s_open = FALSE; /* prevent recursive close if error */ + (*sptr->b_s_info.close_backing_store) (cinfo, & sptr->b_s_info); + } + } + mem->virt_sarray_list = NULL; + for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) { + if (bptr->b_s_open) { /* there may be no backing store */ + bptr->b_s_open = FALSE; /* prevent recursive close if error */ + (*bptr->b_s_info.close_backing_store) (cinfo, & bptr->b_s_info); + } + } + mem->virt_barray_list = NULL; + } + + /* Release large objects */ + lhdr_ptr = mem->large_list[pool_id]; + mem->large_list[pool_id] = NULL; + + while (lhdr_ptr != NULL) { + large_pool_ptr next_lhdr_ptr = lhdr_ptr->hdr.next; + space_freed = lhdr_ptr->hdr.bytes_used + + lhdr_ptr->hdr.bytes_left + + SIZEOF(large_pool_hdr); + jpeg_free_large(cinfo, (void FAR *) lhdr_ptr, space_freed); + mem->total_space_allocated -= space_freed; + lhdr_ptr = next_lhdr_ptr; + } + + /* Release small objects */ + shdr_ptr = mem->small_list[pool_id]; + mem->small_list[pool_id] = NULL; + + while (shdr_ptr != NULL) { + small_pool_ptr next_shdr_ptr = shdr_ptr->hdr.next; + space_freed = shdr_ptr->hdr.bytes_used + + shdr_ptr->hdr.bytes_left + + SIZEOF(small_pool_hdr); + jpeg_free_small(cinfo, (void *) shdr_ptr, space_freed); + mem->total_space_allocated -= space_freed; + shdr_ptr = next_shdr_ptr; + } +} + + +/* + * Close up shop entirely. + * Note that this cannot be called unless cinfo->mem is non-NULL. + */ + +METHODDEF(void) +self_destruct (j_common_ptr cinfo) +{ + int pool; + + /* Close all backing store, release all memory. + * Releasing pools in reverse order might help avoid fragmentation + * with some (brain-damaged) malloc libraries. + */ + for (pool = JPOOL_NUMPOOLS-1; pool >= JPOOL_PERMANENT; pool--) { + free_pool(cinfo, pool); + } + + /* Release the memory manager control block too. */ + jpeg_free_small(cinfo, (void *) cinfo->mem, SIZEOF(my_memory_mgr)); + cinfo->mem = NULL; /* ensures I will be called only once */ + + jpeg_mem_term(cinfo); /* system-dependent cleanup */ +} + + +/* + * Memory manager initialization. + * When this is called, only the error manager pointer is valid in cinfo! + */ + +GLOBAL(void) +jinit_memory_mgr (j_common_ptr cinfo) +{ + my_mem_ptr mem; + long max_to_use; + int pool; + jpeg_size_t test_mac; + + cinfo->mem = NULL; /* for safety if init fails */ + + /* Check for configuration errors. + * SIZEOF(ALIGN_TYPE) should be a power of 2; otherwise, it probably + * doesn't reflect any real hardware alignment requirement. + * The test is a little tricky: for X>0, X and X-1 have no one-bits + * in common if and only if X is a power of 2, ie has only one one-bit. + * Some compilers may give an "unreachable code" warning here; ignore it. + */ + if ((SIZEOF(ALIGN_TYPE) & (SIZEOF(ALIGN_TYPE)-1)) != 0) + ERREXIT(cinfo, JERR_BAD_ALIGN_TYPE); + /* MAX_ALLOC_CHUNK must be representable as type jpeg_size_t, and must be + * a multiple of SIZEOF(ALIGN_TYPE). + * Again, an "unreachable code" warning may be ignored here. + * But a "constant too large" warning means you need to fix MAX_ALLOC_CHUNK. + */ + test_mac = (jpeg_size_t) MAX_ALLOC_CHUNK; + if ((long) test_mac != MAX_ALLOC_CHUNK || + (MAX_ALLOC_CHUNK % SIZEOF(ALIGN_TYPE)) != 0) + ERREXIT(cinfo, JERR_BAD_ALLOC_CHUNK); + + max_to_use = jpeg_mem_init(cinfo); /* system-dependent initialization */ + + /* Attempt to allocate memory manager's control block */ + mem = (my_mem_ptr) jpeg_get_small(cinfo, SIZEOF(my_memory_mgr)); + + if (mem == NULL) { + jpeg_mem_term(cinfo); /* system-dependent cleanup */ + ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 0); + } + + /* OK, fill in the method pointers */ + mem->pub.alloc_small = alloc_small; + mem->pub.alloc_large = alloc_large; + mem->pub.alloc_sarray = alloc_sarray; + mem->pub.alloc_barray = alloc_barray; + mem->pub.request_virt_sarray = request_virt_sarray; + mem->pub.request_virt_barray = request_virt_barray; + mem->pub.realize_virt_arrays = realize_virt_arrays; + mem->pub.access_virt_sarray = access_virt_sarray; + mem->pub.access_virt_barray = access_virt_barray; + mem->pub.free_pool = free_pool; + mem->pub.self_destruct = self_destruct; + + /* Make MAX_ALLOC_CHUNK accessible to other modules */ + mem->pub.max_alloc_chunk = MAX_ALLOC_CHUNK; + + /* Initialize working state */ + mem->pub.max_memory_to_use = max_to_use; + + for (pool = JPOOL_NUMPOOLS-1; pool >= JPOOL_PERMANENT; pool--) { + mem->small_list[pool] = NULL; + mem->large_list[pool] = NULL; + } + mem->virt_sarray_list = NULL; + mem->virt_barray_list = NULL; + + mem->total_space_allocated = SIZEOF(my_memory_mgr); + + /* Declare ourselves open for business */ + cinfo->mem = & mem->pub; + + /* Check for an environment variable JPEGMEM; if found, override the + * default max_memory setting from jpeg_mem_init. Note that the + * surrounding application may again override this value. + * If your system doesn't support getenv(), define NO_GETENV to disable + * this feature. + */ +#ifndef NO_GETENV + { char * memenv; + + if ((memenv = getenv("JPEGMEM")) != NULL) { + char ch = 'x'; + + if (sscanf(memenv, "%ld%c", &max_to_use, &ch) > 0) { + if (ch == 'm' || ch == 'M') + max_to_use *= 1000L; + mem->pub.max_memory_to_use = max_to_use * 1000L; + } + } + } +#endif + +} diff --git a/Engine/lib/ljpeg/jmemnobs.c b/Engine/lib/ljpeg/jmemnobs.c new file mode 100644 index 000000000..f5cdbd7dc --- /dev/null +++ b/Engine/lib/ljpeg/jmemnobs.c @@ -0,0 +1,109 @@ +/* + * jmemnobs.c + * + * Copyright (C) 1992-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file provides a really simple implementation of the system- + * dependent portion of the JPEG memory manager. This implementation + * assumes that no backing-store files are needed: all required space + * can be obtained from malloc(). + * This is very portable in the sense that it'll compile on almost anything, + * but you'd better have lots of main memory (or virtual memory) if you want + * to process big images. + * Note that the max_memory_to_use option is ignored by this implementation. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jmemsys.h" /* import the system-dependent declarations */ + +#ifndef HAVE_STDLIB_H /* should declare malloc(),free() */ +extern void * malloc JPP((jpeg_size_t size)); +extern void free JPP((void *ptr)); +#endif + + +/* + * Memory allocation and freeing are controlled by the regular library + * routines malloc() and free(). + */ + +GLOBAL(void *) +jpeg_get_small (j_common_ptr cinfo, jpeg_size_t sizeofobject) +{ + return (void *) malloc(sizeofobject); +} + +GLOBAL(void) +jpeg_free_small (j_common_ptr cinfo, void * object, jpeg_size_t sizeofobject) +{ + free(object); +} + + +/* + * "Large" objects are treated the same as "small" ones. + * NB: although we include FAR keywords in the routine declarations, + * this file won't actually work in 80x86 small/medium model; at least, + * you probably won't be able to process useful-size images in only 64KB. + */ + +GLOBAL(void FAR *) +jpeg_get_large (j_common_ptr cinfo, jpeg_size_t sizeofobject) +{ + return (void FAR *) malloc(sizeofobject); +} + +GLOBAL(void) +jpeg_free_large (j_common_ptr cinfo, void FAR * object, jpeg_size_t sizeofobject) +{ + free(object); +} + + +/* + * This routine computes the total memory space available for allocation. + * Here we always say, "we got all you want bud!" + */ + +GLOBAL(long) +jpeg_mem_available (j_common_ptr cinfo, long min_bytes_needed, + long max_bytes_needed, long already_allocated) +{ + return max_bytes_needed; +} + + +/* + * Backing store (temporary file) management. + * Since jpeg_mem_available always promised the moon, + * this should never be called and we can just error out. + */ + +GLOBAL(void) +jpeg_open_backing_store (j_common_ptr cinfo, backing_store_ptr info, + long total_bytes_needed) +{ + ERREXIT(cinfo, JERR_NO_BACKING_STORE); +} + + +/* + * These routines take care of any system-dependent initialization and + * cleanup required. Here, there isn't any. + */ + +GLOBAL(long) +jpeg_mem_init (j_common_ptr cinfo) +{ + return 0; /* just set max_memory_to_use to 0 */ +} + +GLOBAL(void) +jpeg_mem_term (j_common_ptr cinfo) +{ + /* no work */ +} diff --git a/Engine/lib/ljpeg/jmemsys.h b/Engine/lib/ljpeg/jmemsys.h new file mode 100644 index 000000000..249c9fe94 --- /dev/null +++ b/Engine/lib/ljpeg/jmemsys.h @@ -0,0 +1,198 @@ +/* + * jmemsys.h + * + * Copyright (C) 1992-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This include file defines the interface between the system-independent + * and system-dependent portions of the JPEG memory manager. No other + * modules need include it. (The system-independent portion is jmemmgr.c; + * there are several different versions of the system-dependent portion.) + * + * This file works as-is for the system-dependent memory managers supplied + * in the IJG distribution. You may need to modify it if you write a + * custom memory manager. If system-dependent changes are needed in + * this file, the best method is to #ifdef them based on a configuration + * symbol supplied in jconfig.h, as we have done with USE_MSDOS_MEMMGR + * and USE_MAC_MEMMGR. + */ + + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_get_small jGetSmall +#define jpeg_free_small jFreeSmall +#define jpeg_get_large jGetLarge +#define jpeg_free_large jFreeLarge +#define jpeg_mem_available jMemAvail +#define jpeg_open_backing_store jOpenBackStore +#define jpeg_mem_init jMemInit +#define jpeg_mem_term jMemTerm +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* + * These two functions are used to allocate and release small chunks of + * memory. (Typically the total amount requested through jpeg_get_small is + * no more than 20K or so; this will be requested in chunks of a few K each.) + * Behavior should be the same as for the standard library functions malloc + * and free; in particular, jpeg_get_small must return NULL on failure. + * On most systems, these ARE malloc and free. jpeg_free_small is passed the + * size of the object being freed, just in case it's needed. + * On an 80x86 machine using small-data memory model, these manage near heap. + */ + +EXTERN(void *) jpeg_get_small JPP((j_common_ptr cinfo, jpeg_size_t sizeofobject)); +EXTERN(void) jpeg_free_small JPP((j_common_ptr cinfo, void * object, + jpeg_size_t sizeofobject)); + +/* + * These two functions are used to allocate and release large chunks of + * memory (up to the total free space designated by jpeg_mem_available). + * The interface is the same as above, except that on an 80x86 machine, + * far pointers are used. On most other machines these are identical to + * the jpeg_get/free_small routines; but we keep them separate anyway, + * in case a different allocation strategy is desirable for large chunks. + */ + +EXTERN(void FAR *) jpeg_get_large JPP((j_common_ptr cinfo, + jpeg_size_t sizeofobject)); +EXTERN(void) jpeg_free_large JPP((j_common_ptr cinfo, void FAR * object, + jpeg_size_t sizeofobject)); + +/* + * The macro MAX_ALLOC_CHUNK designates the maximum number of bytes that may + * be requested in a single call to jpeg_get_large (and jpeg_get_small for that + * matter, but that case should never come into play). This macro is needed + * to model the 64Kb-segment-size limit of far addressing on 80x86 machines. + * On those machines, we expect that jconfig.h will provide a proper value. + * On machines with 32-bit flat address spaces, any large constant may be used. + * + * NB: jmemmgr.c expects that MAX_ALLOC_CHUNK will be representable as type + * size_t and will be a multiple of sizeof(align_type). + */ + +#ifndef MAX_ALLOC_CHUNK /* may be overridden in jconfig.h */ +#define MAX_ALLOC_CHUNK 1000000000L +#endif + +/* + * This routine computes the total space still available for allocation by + * jpeg_get_large. If more space than this is needed, backing store will be + * used. NOTE: any memory already allocated must not be counted. + * + * There is a minimum space requirement, corresponding to the minimum + * feasible buffer sizes; jmemmgr.c will request that much space even if + * jpeg_mem_available returns zero. The maximum space needed, enough to hold + * all working storage in memory, is also passed in case it is useful. + * Finally, the total space already allocated is passed. If no better + * method is available, cinfo->mem->max_memory_to_use - already_allocated + * is often a suitable calculation. + * + * It is OK for jpeg_mem_available to underestimate the space available + * (that'll just lead to more backing-store access than is really necessary). + * However, an overestimate will lead to failure. Hence it's wise to subtract + * a slop factor from the true available space. 5% should be enough. + * + * On machines with lots of virtual memory, any large constant may be returned. + * Conversely, zero may be returned to always use the minimum amount of memory. + */ + +EXTERN(long) jpeg_mem_available JPP((j_common_ptr cinfo, + long min_bytes_needed, + long max_bytes_needed, + long already_allocated)); + + +/* + * This structure holds whatever state is needed to access a single + * backing-store object. The read/write/close method pointers are called + * by jmemmgr.c to manipulate the backing-store object; all other fields + * are private to the system-dependent backing store routines. + */ + +#define TEMP_NAME_LENGTH 64 /* max length of a temporary file's name */ + + +#ifdef USE_MSDOS_MEMMGR /* DOS-specific junk */ + +typedef unsigned short XMSH; /* type of extended-memory handles */ +typedef unsigned short EMSH; /* type of expanded-memory handles */ + +typedef union { + short file_handle; /* DOS file handle if it's a temp file */ + XMSH xms_handle; /* handle if it's a chunk of XMS */ + EMSH ems_handle; /* handle if it's a chunk of EMS */ +} handle_union; + +#endif /* USE_MSDOS_MEMMGR */ + +#ifdef USE_MAC_MEMMGR /* Mac-specific junk */ +#include +#endif /* USE_MAC_MEMMGR */ + + +typedef struct backing_store_struct * backing_store_ptr; + +typedef struct backing_store_struct { + /* Methods for reading/writing/closing this backing-store object */ + JMETHOD(void, read_backing_store, (j_common_ptr cinfo, + backing_store_ptr info, + void FAR * buffer_address, + long file_offset, long byte_count)); + JMETHOD(void, write_backing_store, (j_common_ptr cinfo, + backing_store_ptr info, + void FAR * buffer_address, + long file_offset, long byte_count)); + JMETHOD(void, close_backing_store, (j_common_ptr cinfo, + backing_store_ptr info)); + + /* Private fields for system-dependent backing-store management */ +#ifdef USE_MSDOS_MEMMGR + /* For the MS-DOS manager (jmemdos.c), we need: */ + handle_union handle; /* reference to backing-store storage object */ + char temp_name[TEMP_NAME_LENGTH]; /* name if it's a file */ +#else +#ifdef USE_MAC_MEMMGR + /* For the Mac manager (jmemmac.c), we need: */ + short temp_file; /* file reference number to temp file */ + FSSpec tempSpec; /* the FSSpec for the temp file */ + char temp_name[TEMP_NAME_LENGTH]; /* name if it's a file */ +#else + /* For a typical implementation with temp files, we need: */ + FILE * temp_file; /* stdio reference to temp file */ + char temp_name[TEMP_NAME_LENGTH]; /* name of temp file */ +#endif +#endif +} backing_store_info; + + +/* + * Initial opening of a backing-store object. This must fill in the + * read/write/close pointers in the object. The read/write routines + * may take an error exit if the specified maximum file size is exceeded. + * (If jpeg_mem_available always returns a large value, this routine can + * just take an error exit.) + */ + +EXTERN(void) jpeg_open_backing_store JPP((j_common_ptr cinfo, + backing_store_ptr info, + long total_bytes_needed)); + + +/* + * These routines take care of any system-dependent initialization and + * cleanup required. jpeg_mem_init will be called before anything is + * allocated (and, therefore, nothing in cinfo is of use except the error + * manager pointer). It should return a suitable default value for + * max_memory_to_use; this may subsequently be overridden by the surrounding + * application. (Note that max_memory_to_use is only important if + * jpeg_mem_available chooses to consult it ... no one else will.) + * jpeg_mem_term may assume that all requested memory has been freed and that + * all opened backing-store objects have been closed. + */ + +EXTERN(long) jpeg_mem_init JPP((j_common_ptr cinfo)); +EXTERN(void) jpeg_mem_term JPP((j_common_ptr cinfo)); diff --git a/Engine/lib/ljpeg/jmorecfg.h b/Engine/lib/ljpeg/jmorecfg.h new file mode 100644 index 000000000..54a7d1c44 --- /dev/null +++ b/Engine/lib/ljpeg/jmorecfg.h @@ -0,0 +1,363 @@ +/* + * jmorecfg.h + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains additional configuration options that customize the + * JPEG software for special applications or support machine-dependent + * optimizations. Most users will not need to touch this file. + */ + + +/* + * Define BITS_IN_JSAMPLE as either + * 8 for 8-bit sample values (the usual setting) + * 12 for 12-bit sample values + * Only 8 and 12 are legal data precisions for lossy JPEG according to the + * JPEG standard, and the IJG code does not support anything else! + * We do not support run-time selection of data precision, sorry. + */ + +#define BITS_IN_JSAMPLE 8 /* use 8 or 12 */ + + +/* + * Maximum number of components (color channels) allowed in JPEG image. + * To meet the letter of the JPEG spec, set this to 255. However, darn + * few applications need more than 4 channels (maybe 5 for CMYK + alpha + * mask). We recommend 10 as a reasonable compromise; use 4 if you are + * really short on memory. (Each allowed component costs a hundred or so + * bytes of storage, whether actually used in an image or not.) + */ + +#define MAX_COMPONENTS 10 /* maximum number of image components */ + + +/* + * Basic data types. + * You may need to change these if you have a machine with unusual data + * type sizes; for example, "char" not 8 bits, "short" not 16 bits, + * or "long" not 32 bits. We don't care whether "int" is 16 or 32 bits, + * but it had better be at least 16. + */ + +/* Representation of a single sample (pixel element value). + * We frequently allocate large arrays of these, so it's important to keep + * them small. But if you have memory to burn and access to char or short + * arrays is very slow on your hardware, you might want to change these. + */ + +#if BITS_IN_JSAMPLE == 8 +/* JSAMPLE should be the smallest type that will hold the values 0..255. + * You can use a signed char by having GETJSAMPLE mask it with 0xFF. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JSAMPLE; +#ifdef CHAR_IS_UNSIGNED +#define GETJSAMPLE(value) ((int) (value)) +#else +#define GETJSAMPLE(value) ((int) (value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + +#define MAXJSAMPLE 255 +#define CENTERJSAMPLE 128 + +#endif /* BITS_IN_JSAMPLE == 8 */ + + +#if BITS_IN_JSAMPLE == 12 +/* JSAMPLE should be the smallest type that will hold the values 0..4095. + * On nearly all machines "short" will do nicely. + */ + +typedef short JSAMPLE; +#define GETJSAMPLE(value) ((int) (value)) + +#define MAXJSAMPLE 4095 +#define CENTERJSAMPLE 2048 + +#endif /* BITS_IN_JSAMPLE == 12 */ + + +/* Representation of a DCT frequency coefficient. + * This should be a signed value of at least 16 bits; "short" is usually OK. + * Again, we allocate large arrays of these, but you can change to int + * if you have memory to burn and "short" is really slow. + */ + +typedef short JCOEF; + + +/* Compressed datastreams are represented as arrays of JOCTET. + * These must be EXACTLY 8 bits wide, at least once they are written to + * external storage. Note that when using the stdio data source/destination + * managers, this is also the data type passed to fread/fwrite. + */ + +#ifdef HAVE_UNSIGNED_CHAR + +typedef unsigned char JOCTET; +#define GETJOCTET(value) (value) + +#else /* not HAVE_UNSIGNED_CHAR */ + +typedef char JOCTET; +#ifdef CHAR_IS_UNSIGNED +#define GETJOCTET(value) (value) +#else +#define GETJOCTET(value) ((value) & 0xFF) +#endif /* CHAR_IS_UNSIGNED */ + +#endif /* HAVE_UNSIGNED_CHAR */ + + +/* These typedefs are used for various table entries and so forth. + * They must be at least as wide as specified; but making them too big + * won't cost a huge amount of memory, so we don't provide special + * extraction code like we did for JSAMPLE. (In other words, these + * typedefs live at a different point on the speed/space tradeoff curve.) + */ + +/* UINT8 must hold at least the values 0..255. */ + +#ifdef HAVE_UNSIGNED_CHAR +typedef unsigned char UINT8; +#else /* not HAVE_UNSIGNED_CHAR */ +#ifdef CHAR_IS_UNSIGNED +typedef char UINT8; +#else /* not CHAR_IS_UNSIGNED */ +typedef short UINT8; +#endif /* CHAR_IS_UNSIGNED */ +#endif /* HAVE_UNSIGNED_CHAR */ + +/* UINT16 must hold at least the values 0..65535. */ + +#ifdef HAVE_UNSIGNED_SHORT +typedef unsigned short UINT16; +#else /* not HAVE_UNSIGNED_SHORT */ +typedef unsigned int UINT16; +#endif /* HAVE_UNSIGNED_SHORT */ + +/* INT16 must hold at least the values -32768..32767. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT16 */ +typedef short INT16; +#endif + +/* INT32 must hold at least signed 32-bit values. */ + +#ifndef XMD_H /* X11/xmd.h correctly defines INT32 */ +typedef long INT32; +#endif + +/* Datatype used for image dimensions. The JPEG standard only supports + * images up to 64K*64K due to 16-bit fields in SOF markers. Therefore + * "unsigned int" is sufficient on all machines. However, if you need to + * handle larger images and you don't mind deviating from the spec, you + * can change this datatype. + */ + +typedef unsigned int JDIMENSION; + +#define JPEG_MAX_DIMENSION 65500L /* a tad under 64K to prevent overflows */ + + +/* These macros are used in all function definitions and extern declarations. + * You could modify them if you need to change function linkage conventions; + * in particular, you'll need to do that to make the library a Windows DLL. + * Another application is to make all functions global for use with debuggers + * or code profilers that require it. + */ + +/* a function called through method pointers: */ +#define METHODDEF(type) static type +/* a function used only in its module: */ +#define LOCAL(type) static type +/* a function referenced thru EXTERNs: */ +#define GLOBAL(type) type +/* a reference to a GLOBAL function: */ +#define EXTERN(type) extern type + + +/* This macro is used to declare a "method", that is, a function pointer. + * We want to supply prototype parameters if the compiler can cope. + * Note that the arglist parameter must be parenthesized! + * Again, you can customize this if you need special linkage keywords. + */ + +#ifdef HAVE_PROTOTYPES +#define JMETHOD(type,methodname,arglist) type (*methodname) arglist +#else +#define JMETHOD(type,methodname,arglist) type (*methodname) () +#endif + + +/* Here is the pseudo-keyword for declaring pointers that must be "far" + * on 80x86 machines. Most of the specialized coding for 80x86 is handled + * by just saying "FAR *" where such a pointer is needed. In a few places + * explicit coding is needed; see uses of the NEED_FAR_POINTERS symbol. + */ + +#ifdef NEED_FAR_POINTERS +#define FAR far +#else +#define FAR +#endif + + +/* + * On a few systems, type boolean and/or its values FALSE, TRUE may appear + * in standard header files. Or you may have conflicts with application- + * specific header files that you want to include together with these files. + * Defining HAVE_BOOLEAN before including jpeglib.h should make it work. + */ + +#ifndef HAVE_BOOLEAN +typedef int boolean; +#endif +#ifndef FALSE /* in case these macros already exist */ +#define FALSE 0 /* values of boolean */ +#endif +#ifndef TRUE +#define TRUE 1 +#endif + + +/* + * The remaining options affect code selection within the JPEG library, + * but they don't need to be visible to most applications using the library. + * To minimize application namespace pollution, the symbols won't be + * defined unless JPEG_INTERNALS or JPEG_INTERNAL_OPTIONS has been defined. + */ + +#ifdef JPEG_INTERNALS +#define JPEG_INTERNAL_OPTIONS +#endif + +#ifdef JPEG_INTERNAL_OPTIONS + + +/* + * These defines indicate whether to include various optional functions. + * Undefining some of these symbols will produce a smaller but less capable + * library. Note that you can leave certain source files out of the + * compilation/linking process if you've #undef'd the corresponding symbols. + * (You may HAVE to do that if your compiler doesn't like null source files.) + */ + +/* Arithmetic coding is unsupported for legal reasons. Complaints to IBM. */ + +/* Capability options common to encoder and decoder: */ + +#define DCT_ISLOW_SUPPORTED /* slow but accurate integer algorithm */ +#define DCT_IFAST_SUPPORTED /* faster, less accurate integer method */ +#define DCT_FLOAT_SUPPORTED /* floating-point: accurate, fast on fast HW */ + +/* Encoder capability options: */ + +#undef C_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define C_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define C_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define ENTROPY_OPT_SUPPORTED /* Optimization of entropy coding parms? */ +/* Note: if you selected 12-bit data precision, it is dangerous to turn off + * ENTROPY_OPT_SUPPORTED. The standard Huffman tables are only good for 8-bit + * precision, so jchuff.c normally uses entropy optimization to compute + * usable tables for higher precision. If you don't want to do optimization, + * you'll have to supply different default Huffman tables. + * The exact same statements apply for progressive JPEG: the default tables + * don't work for progressive mode. (This may get fixed, however.) + */ +#define INPUT_SMOOTHING_SUPPORTED /* Input image smoothing option? */ + +/* Decoder capability options: */ + +#undef D_ARITH_CODING_SUPPORTED /* Arithmetic coding back end? */ +#define D_MULTISCAN_FILES_SUPPORTED /* Multiple-scan JPEG files? */ +#define D_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ +#define SAVE_MARKERS_SUPPORTED /* jpeg_save_markers() needed? */ +#define BLOCK_SMOOTHING_SUPPORTED /* Block smoothing? (Progressive only) */ +#define IDCT_SCALING_SUPPORTED /* Output rescaling via IDCT? */ +#undef UPSAMPLE_SCALING_SUPPORTED /* Output rescaling at upsample stage? */ +#define UPSAMPLE_MERGING_SUPPORTED /* Fast path for sloppy upsampling? */ +#define QUANT_1PASS_SUPPORTED /* 1-pass color quantization? */ +#define QUANT_2PASS_SUPPORTED /* 2-pass color quantization? */ + +/* more capability options later, no doubt */ + + +/* + * Ordering of RGB data in scanlines passed to or from the application. + * If your application wants to deal with data in the order B,G,R, just + * change these macros. You can also deal with formats such as R,G,B,X + * (one extra byte per pixel) by changing RGB_PIXELSIZE. Note that changing + * the offsets will also change the order in which colormap data is organized. + * RESTRICTIONS: + * 1. The sample applications cjpeg,djpeg do NOT support modified RGB formats. + * 2. These macros only affect RGB<=>YCbCr color conversion, so they are not + * useful if you are using JPEG color spaces other than YCbCr or grayscale. + * 3. The color quantizer modules will not behave desirably if RGB_PIXELSIZE + * is not 3 (they don't understand about dummy color components!). So you + * can't use color quantization if you change that value. + */ + +#define RGB_RED 0 /* Offset of Red in an RGB scanline element */ +#define RGB_GREEN 1 /* Offset of Green */ +#define RGB_BLUE 2 /* Offset of Blue */ +#define RGB_PIXELSIZE 3 /* JSAMPLEs per RGB scanline element */ + + +/* Definitions for speed-related optimizations. */ + + +/* If your compiler supports inline functions, define INLINE + * as the inline keyword; otherwise define it as empty. + */ + +#ifndef INLINE +#ifdef __GNUC__ /* for instance, GNU C knows about inline */ +#define INLINE __inline__ +#endif +#ifndef INLINE +#define INLINE /* default is to define it as empty */ +#endif +#endif + + +/* On some machines (notably 68000 series) "int" is 32 bits, but multiplying + * two 16-bit shorts is faster than multiplying two ints. Define MULTIPLIER + * as short on such a machine. MULTIPLIER must be at least 16 bits wide. + */ + +#ifndef MULTIPLIER +#define MULTIPLIER int /* type for fastest integer multiply */ +#endif + + +/* FAST_FLOAT should be either float or double, whichever is done faster + * by your compiler. (Note that this type is only used in the floating point + * DCT routines, so it only matters if you've defined DCT_FLOAT_SUPPORTED.) + * Typically, float is faster in ANSI C compilers, while double is faster in + * pre-ANSI compilers (because they insist on converting to double anyway). + * The code below therefore chooses float if we have ANSI-style prototypes. + */ + +#ifndef FAST_FLOAT +#ifdef HAVE_PROTOTYPES +#define FAST_FLOAT float +#else +#define FAST_FLOAT double +#endif +#endif + +#endif /* JPEG_INTERNAL_OPTIONS */ diff --git a/Engine/lib/ljpeg/jpegint.h b/Engine/lib/ljpeg/jpegint.h new file mode 100644 index 000000000..95b00d405 --- /dev/null +++ b/Engine/lib/ljpeg/jpegint.h @@ -0,0 +1,392 @@ +/* + * jpegint.h + * + * Copyright (C) 1991-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file provides common declarations for the various JPEG modules. + * These declarations are considered internal to the JPEG library; most + * applications using the library shouldn't need to include this file. + */ + + +/* Declarations for both compression & decompression */ + +typedef enum { /* Operating modes for buffer controllers */ + JBUF_PASS_THRU, /* Plain stripwise operation */ + /* Remaining modes require a full-image buffer to have been created */ + JBUF_SAVE_SOURCE, /* Run source subobject only, save output */ + JBUF_CRANK_DEST, /* Run dest subobject only, using saved data */ + JBUF_SAVE_AND_PASS /* Run both subobjects, save output */ +} J_BUF_MODE; + +/* Values of global_state field (jdapi.c has some dependencies on ordering!) */ +#define CSTATE_START 100 /* after create_compress */ +#define CSTATE_SCANNING 101 /* start_compress done, write_scanlines OK */ +#define CSTATE_RAW_OK 102 /* start_compress done, write_raw_data OK */ +#define CSTATE_WRCOEFS 103 /* jpeg_write_coefficients done */ +#define DSTATE_START 200 /* after create_decompress */ +#define DSTATE_INHEADER 201 /* reading header markers, no SOS yet */ +#define DSTATE_READY 202 /* found SOS, ready for start_decompress */ +#define DSTATE_PRELOAD 203 /* reading multiscan file in start_decompress*/ +#define DSTATE_PRESCAN 204 /* performing dummy pass for 2-pass quant */ +#define DSTATE_SCANNING 205 /* start_decompress done, read_scanlines OK */ +#define DSTATE_RAW_OK 206 /* start_decompress done, read_raw_data OK */ +#define DSTATE_BUFIMAGE 207 /* expecting jpeg_start_output */ +#define DSTATE_BUFPOST 208 /* looking for SOS/EOI in jpeg_finish_output */ +#define DSTATE_RDCOEFS 209 /* reading file in jpeg_read_coefficients */ +#define DSTATE_STOPPING 210 /* looking for EOI in jpeg_finish_decompress */ + + +/* Declarations for compression modules */ + +/* Master control module */ +struct jpeg_comp_master { + JMETHOD(void, prepare_for_pass, (j_compress_ptr cinfo)); + JMETHOD(void, pass_startup, (j_compress_ptr cinfo)); + JMETHOD(void, finish_pass, (j_compress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean call_pass_startup; /* True if pass_startup must be called */ + boolean is_last_pass; /* True during last pass */ +}; + +/* Main buffer control (downsampled-data buffer) */ +struct jpeg_c_main_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, process_data, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail)); +}; + +/* Compression preprocessing (downsampling input buffer control) */ +struct jpeg_c_prep_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, pre_process_data, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, + JDIMENSION *in_row_ctr, + JDIMENSION in_rows_avail, + JSAMPIMAGE output_buf, + JDIMENSION *out_row_group_ctr, + JDIMENSION out_row_groups_avail)); +}; + +/* Coefficient buffer control */ +struct jpeg_c_coef_controller { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(boolean, compress_data, (j_compress_ptr cinfo, + JSAMPIMAGE input_buf)); +}; + +/* Colorspace conversion */ +struct jpeg_color_converter { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + JMETHOD(void, color_convert, (j_compress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPIMAGE output_buf, + JDIMENSION output_row, int num_rows)); +}; + +/* Downsampling */ +struct jpeg_downsampler { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + JMETHOD(void, downsample, (j_compress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION in_row_index, + JSAMPIMAGE output_buf, + JDIMENSION out_row_group_index)); + + boolean need_context_rows; /* TRUE if need rows above & below */ +}; + +/* Forward DCT (also controls coefficient quantization) */ +struct jpeg_forward_dct { + JMETHOD(void, start_pass, (j_compress_ptr cinfo)); + /* perhaps this should be an array??? */ + JMETHOD(void, forward_DCT, (j_compress_ptr cinfo, + jpeg_component_info * compptr, + JSAMPARRAY sample_data, JBLOCKROW coef_blocks, + JDIMENSION start_row, JDIMENSION start_col, + JDIMENSION num_blocks)); +}; + +/* Entropy encoding */ +struct jpeg_entropy_encoder { + JMETHOD(void, start_pass, (j_compress_ptr cinfo, boolean gather_statistics)); + JMETHOD(boolean, encode_mcu, (j_compress_ptr cinfo, JBLOCKROW *MCU_data)); + JMETHOD(void, finish_pass, (j_compress_ptr cinfo)); +}; + +/* Marker writing */ +struct jpeg_marker_writer { + JMETHOD(void, write_file_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_frame_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_scan_header, (j_compress_ptr cinfo)); + JMETHOD(void, write_file_trailer, (j_compress_ptr cinfo)); + JMETHOD(void, write_tables_only, (j_compress_ptr cinfo)); + /* These routines are exported to allow insertion of extra markers */ + /* Probably only COM and APPn markers should be written this way */ + JMETHOD(void, write_marker_header, (j_compress_ptr cinfo, int marker, + unsigned int datalen)); + JMETHOD(void, write_marker_byte, (j_compress_ptr cinfo, int val)); +}; + + +/* Declarations for decompression modules */ + +/* Master control module */ +struct jpeg_decomp_master { + JMETHOD(void, prepare_for_output_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, finish_output_pass, (j_decompress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean is_dummy_pass; /* True during 1st pass for 2-pass quant */ +}; + +/* Input control module */ +struct jpeg_input_controller { + JMETHOD(int, consume_input, (j_decompress_ptr cinfo)); + JMETHOD(void, reset_input_controller, (j_decompress_ptr cinfo)); + JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, finish_input_pass, (j_decompress_ptr cinfo)); + + /* State variables made visible to other modules */ + boolean has_multiple_scans; /* True if file has multiple scans */ + boolean eoi_reached; /* True when EOI has been consumed */ +}; + +/* Main buffer control (downsampled-data buffer) */ +struct jpeg_d_main_controller { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, process_data, (j_decompress_ptr cinfo, + JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +}; + +/* Coefficient buffer control */ +struct jpeg_d_coef_controller { + JMETHOD(void, start_input_pass, (j_decompress_ptr cinfo)); + JMETHOD(int, consume_data, (j_decompress_ptr cinfo)); + JMETHOD(void, start_output_pass, (j_decompress_ptr cinfo)); + JMETHOD(int, decompress_data, (j_decompress_ptr cinfo, + JSAMPIMAGE output_buf)); + /* Pointer to array of coefficient virtual arrays, or NULL if none */ + jvirt_barray_ptr *coef_arrays; +}; + +/* Decompression postprocessing (color quantization buffer control) */ +struct jpeg_d_post_controller { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, J_BUF_MODE pass_mode)); + JMETHOD(void, post_process_data, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, + JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); +}; + +/* Marker reading & parsing */ +struct jpeg_marker_reader { + JMETHOD(void, reset_marker_reader, (j_decompress_ptr cinfo)); + /* Read markers until SOS or EOI. + * Returns same codes as are defined for jpeg_consume_input: + * JPEG_SUSPENDED, JPEG_REACHED_SOS, or JPEG_REACHED_EOI. + */ + JMETHOD(int, read_markers, (j_decompress_ptr cinfo)); + /* Read a restart marker --- exported for use by entropy decoder only */ + jpeg_marker_parser_method read_restart_marker; + + /* State of marker reader --- nominally internal, but applications + * supplying COM or APPn handlers might like to know the state. + */ + boolean saw_SOI; /* found SOI? */ + boolean saw_SOF; /* found SOF? */ + int next_restart_num; /* next restart number expected (0-7) */ + unsigned int discarded_bytes; /* # of bytes skipped looking for a marker */ +}; + +/* Entropy decoding */ +struct jpeg_entropy_decoder { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(boolean, decode_mcu, (j_decompress_ptr cinfo, + JBLOCKROW *MCU_data)); + + /* This is here to share code between baseline and progressive decoders; */ + /* other modules probably should not use it */ + boolean insufficient_data; /* set TRUE after emitting warning */ +}; + +/* Inverse DCT (also performs dequantization) */ +typedef JMETHOD(void, inverse_DCT_method_ptr, + (j_decompress_ptr cinfo, jpeg_component_info * compptr, + JCOEFPTR coef_block, + JSAMPARRAY output_buf, JDIMENSION output_col)); + +struct jpeg_inverse_dct { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + /* It is useful to allow each component to have a separate IDCT method. */ + inverse_DCT_method_ptr inverse_DCT[MAX_COMPONENTS]; +}; + +/* Upsampling (note that upsampler must also call color converter) */ +struct jpeg_upsampler { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, upsample, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, + JDIMENSION *in_row_group_ctr, + JDIMENSION in_row_groups_avail, + JSAMPARRAY output_buf, + JDIMENSION *out_row_ctr, + JDIMENSION out_rows_avail)); + + boolean need_context_rows; /* TRUE if need rows above & below */ +}; + +/* Colorspace conversion */ +struct jpeg_color_deconverter { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, color_convert, (j_decompress_ptr cinfo, + JSAMPIMAGE input_buf, JDIMENSION input_row, + JSAMPARRAY output_buf, int num_rows)); +}; + +/* Color quantization or color precision reduction */ +struct jpeg_color_quantizer { + JMETHOD(void, start_pass, (j_decompress_ptr cinfo, boolean is_pre_scan)); + JMETHOD(void, color_quantize, (j_decompress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPARRAY output_buf, + int num_rows)); + JMETHOD(void, finish_pass, (j_decompress_ptr cinfo)); + JMETHOD(void, new_color_map, (j_decompress_ptr cinfo)); +}; + + +/* Miscellaneous useful macros */ + +#undef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#undef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + + +/* We assume that right shift corresponds to signed division by 2 with + * rounding towards minus infinity. This is correct for typical "arithmetic + * shift" instructions that shift in copies of the sign bit. But some + * C compilers implement >> with an unsigned shift. For these machines you + * must define RIGHT_SHIFT_IS_UNSIGNED. + * RIGHT_SHIFT provides a proper signed right shift of an INT32 quantity. + * It is only applied with constant shift counts. SHIFT_TEMPS must be + * included in the variables of any routine using RIGHT_SHIFT. + */ + +#ifdef RIGHT_SHIFT_IS_UNSIGNED +#define SHIFT_TEMPS INT32 shift_temp; +#define RIGHT_SHIFT(x,shft) \ + ((shift_temp = (x)) < 0 ? \ + (shift_temp >> (shft)) | ((~((INT32) 0)) << (32-(shft))) : \ + (shift_temp >> (shft))) +#else +#define SHIFT_TEMPS +#define RIGHT_SHIFT(x,shft) ((x) >> (shft)) +#endif + + +/* Short forms of external names for systems with brain-damaged linkers. */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jinit_compress_master jICompress +#define jinit_c_master_control jICMaster +#define jinit_c_main_controller jICMainC +#define jinit_c_prep_controller jICPrepC +#define jinit_c_coef_controller jICCoefC +#define jinit_color_converter jICColor +#define jinit_downsampler jIDownsampler +#define jinit_forward_dct jIFDCT +#define jinit_huff_encoder jIHEncoder +#define jinit_phuff_encoder jIPHEncoder +#define jinit_marker_writer jIMWriter +#define jinit_master_decompress jIDMaster +#define jinit_d_main_controller jIDMainC +#define jinit_d_coef_controller jIDCoefC +#define jinit_d_post_controller jIDPostC +#define jinit_input_controller jIInCtlr +#define jinit_marker_reader jIMReader +#define jinit_huff_decoder jIHDecoder +#define jinit_phuff_decoder jIPHDecoder +#define jinit_inverse_dct jIIDCT +#define jinit_upsampler jIUpsampler +#define jinit_color_deconverter jIDColor +#define jinit_1pass_quantizer jI1Quant +#define jinit_2pass_quantizer jI2Quant +#define jinit_merged_upsampler jIMUpsampler +#define jinit_memory_mgr jIMemMgr +#define jdiv_round_up jDivRound +#define jround_up jRound +#define jcopy_sample_rows jCopySamples +#define jcopy_block_row jCopyBlocks +#define jzero_far jZeroFar +#define jpeg_zigzag_order jZIGTable +#define jpeg_natural_order jZAGTable +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Compression module initialization routines */ +EXTERN(void) jinit_compress_master JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_c_master_control JPP((j_compress_ptr cinfo, + boolean transcode_only)); +EXTERN(void) jinit_c_main_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_c_prep_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_c_coef_controller JPP((j_compress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_color_converter JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_downsampler JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_forward_dct JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_huff_encoder JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_phuff_encoder JPP((j_compress_ptr cinfo)); +EXTERN(void) jinit_marker_writer JPP((j_compress_ptr cinfo)); +/* Decompression module initialization routines */ +EXTERN(void) jinit_master_decompress JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_d_main_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_d_coef_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_d_post_controller JPP((j_decompress_ptr cinfo, + boolean need_full_buffer)); +EXTERN(void) jinit_input_controller JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_marker_reader JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_huff_decoder JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_phuff_decoder JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_inverse_dct JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_upsampler JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_color_deconverter JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_1pass_quantizer JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_2pass_quantizer JPP((j_decompress_ptr cinfo)); +EXTERN(void) jinit_merged_upsampler JPP((j_decompress_ptr cinfo)); +/* Memory manager initialization */ +EXTERN(void) jinit_memory_mgr JPP((j_common_ptr cinfo)); + +/* Utility routines in jutils.c */ +EXTERN(long) jdiv_round_up JPP((long a, long b)); +EXTERN(long) jround_up JPP((long a, long b)); +EXTERN(void) jcopy_sample_rows JPP((JSAMPARRAY input_array, int source_row, + JSAMPARRAY output_array, int dest_row, + int num_rows, JDIMENSION num_cols)); +EXTERN(void) jcopy_block_row JPP((JBLOCKROW input_row, JBLOCKROW output_row, + JDIMENSION num_blocks)); +EXTERN(void) jzero_far JPP((void FAR * target, size_t bytestozero)); +/* Constant tables in jutils.c */ +#if 0 /* This table is not actually needed in v6a */ +extern const int jpeg_zigzag_order[]; /* natural coef order to zigzag order */ +#endif +extern const int jpeg_natural_order[]; /* zigzag coef order to natural order */ + +/* Suppress undefined-structure complaints if necessary. */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef AM_MEMORY_MANAGER /* only jmemmgr.c defines these */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +#endif +#endif /* INCOMPLETE_TYPES_BROKEN */ diff --git a/Engine/lib/ljpeg/jpeglib.h b/Engine/lib/ljpeg/jpeglib.h new file mode 100644 index 000000000..1a5924de4 --- /dev/null +++ b/Engine/lib/ljpeg/jpeglib.h @@ -0,0 +1,1112 @@ +/* + * jpeglib.h + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file defines the application interface for the JPEG library. + * Most applications using the library need only include this file, + * and perhaps jerror.h if they want to know the exact error codes. + */ + +#ifndef JPEGLIB_H +#define JPEGLIB_H + +/* + * First we include the configuration files that record how this + * installation of the JPEG library is set up. jconfig.h can be + * generated automatically for many systems. jmorecfg.h contains + * manual configuration options that most people need not worry about. + */ + +#ifndef JCONFIG_INCLUDED /* in case jinclude.h already did */ +#include "jconfig.h" /* widely used configuration options */ +#endif +#include "jmorecfg.h" /* seldom changed options */ + +/* Inhibit C++ name-mangling for libpng functions but not for system calls. */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/* Version ID for the JPEG library. + * Might be useful for tests like "#if JPEG_LIB_VERSION >= 60". + */ + +#define JPEG_LIB_VERSION 62 /* Version 6b */ + + +/* Various constants determining the sizes of things. + * All of these are specified by the JPEG standard, so don't change them + * if you want to be compatible. + */ + +#define DCTSIZE 8 /* The basic DCT block is 8x8 samples */ +#define DCTSIZE2 64 /* DCTSIZE squared; # of elements in a block */ +#define NUM_QUANT_TBLS 4 /* Quantization tables are numbered 0..3 */ +#define NUM_HUFF_TBLS 4 /* Huffman tables are numbered 0..3 */ +#define NUM_ARITH_TBLS 16 /* Arith-coding tables are numbered 0..15 */ +#define MAX_COMPS_IN_SCAN 4 /* JPEG limit on # of components in one scan */ +#define MAX_SAMP_FACTOR 4 /* JPEG limit on sampling factors */ +/* Unfortunately, some bozo at Adobe saw no reason to be bound by the standard; + * the PostScript DCT filter can emit files with many more than 10 blocks/MCU. + * If you happen to run across such a file, you can up D_MAX_BLOCKS_IN_MCU + * to handle it. We even let you do this from the jconfig.h file. However, + * we strongly discourage changing C_MAX_BLOCKS_IN_MCU; just because Adobe + * sometimes emits noncompliant files doesn't mean you should too. + */ +#define C_MAX_BLOCKS_IN_MCU 10 /* compressor's limit on blocks per MCU */ +#ifndef D_MAX_BLOCKS_IN_MCU +#define D_MAX_BLOCKS_IN_MCU 10 /* decompressor's limit on blocks per MCU */ +#endif + + +/* Data structures for images (arrays of samples and of DCT coefficients). + * On 80x86 machines, the image arrays are too big for near pointers, + * but the pointer arrays can fit in near memory. + */ + +typedef JSAMPLE FAR *JSAMPROW; /* ptr to one image row of pixel samples. */ +typedef JSAMPROW *JSAMPARRAY; /* ptr to some rows (a 2-D sample array) */ +typedef JSAMPARRAY *JSAMPIMAGE; /* a 3-D sample array: top index is color */ + +typedef JCOEF JBLOCK[DCTSIZE2]; /* one block of coefficients */ +typedef JBLOCK FAR *JBLOCKROW; /* pointer to one row of coefficient blocks */ +typedef JBLOCKROW *JBLOCKARRAY; /* a 2-D array of coefficient blocks */ +typedef JBLOCKARRAY *JBLOCKIMAGE; /* a 3-D array of coefficient blocks */ + +typedef JCOEF FAR *JCOEFPTR; /* useful in a couple of places */ + + +/* Types for JPEG compression parameters and working tables. */ + + +/* DCT coefficient quantization tables. */ + +typedef struct { + /* This array gives the coefficient quantizers in natural array order + * (not the zigzag order in which they are stored in a JPEG DQT marker). + * CAUTION: IJG versions prior to v6a kept this array in zigzag order. + */ + UINT16 quantval[DCTSIZE2]; /* quantization step for each coefficient */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JQUANT_TBL; + + +/* Huffman coding tables. */ + +typedef struct { + /* These two fields directly represent the contents of a JPEG DHT marker */ + UINT8 bits[17]; /* bits[k] = # of symbols with codes of */ + /* length k bits; bits[0] is unused */ + UINT8 huffval[256]; /* The symbols, in order of incr code length */ + /* This field is used only during compression. It's initialized FALSE when + * the table is created, and set TRUE when it's been output to the file. + * You could suppress output of a table by setting this to TRUE. + * (See jpeg_suppress_tables for an example.) + */ + boolean sent_table; /* TRUE when table has been output */ +} JHUFF_TBL; + + +/* Basic info about one component (color channel). */ + +typedef struct { + /* These values are fixed over the whole image. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOF marker. */ + int component_id; /* identifier for this component (0..255) */ + int component_index; /* its index in SOF or cinfo->comp_info[] */ + int h_samp_factor; /* horizontal sampling factor (1..4) */ + int v_samp_factor; /* vertical sampling factor (1..4) */ + int quant_tbl_no; /* quantization table selector (0..3) */ + /* These values may vary between scans. */ + /* For compression, they must be supplied by parameter setup; */ + /* for decompression, they are read from the SOS marker. */ + /* The decompressor output side may not use these variables. */ + int dc_tbl_no; /* DC entropy table selector (0..3) */ + int ac_tbl_no; /* AC entropy table selector (0..3) */ + + /* Remaining fields should be treated as private by applications. */ + + /* These values are computed during compression or decompression startup: */ + /* Component's size in DCT blocks. + * Any dummy blocks added to complete an MCU are not counted; therefore + * these values do not depend on whether a scan is interleaved or not. + */ + JDIMENSION width_in_blocks; + JDIMENSION height_in_blocks; + /* Size of a DCT block in samples. Always DCTSIZE for compression. + * For decompression this is the size of the output from one DCT block, + * reflecting any scaling we choose to apply during the IDCT step. + * Values of 1,2,4,8 are likely to be supported. Note that different + * components may receive different IDCT scalings. + */ + int DCT_scaled_size; + /* The downsampled dimensions are the component's actual, unpadded number + * of samples at the main buffer (preprocessing/compression interface), thus + * downsampled_width = ceil(image_width * Hi/Hmax) + * and similarly for height. For decompression, IDCT scaling is included, so + * downsampled_width = ceil(image_width * Hi/Hmax * DCT_scaled_size/DCTSIZE) + */ + JDIMENSION downsampled_width; /* actual width in samples */ + JDIMENSION downsampled_height; /* actual height in samples */ + /* This flag is used only for decompression. In cases where some of the + * components will be ignored (eg grayscale output from YCbCr image), + * we can skip most computations for the unused components. + */ + boolean component_needed; /* do we need the value of this component? */ + + /* These values are computed before starting a scan of the component. */ + /* The decompressor output side may not use these variables. */ + int MCU_width; /* number of blocks per MCU, horizontally */ + int MCU_height; /* number of blocks per MCU, vertically */ + int MCU_blocks; /* MCU_width * MCU_height */ + int MCU_sample_width; /* MCU width in samples, MCU_width*DCT_scaled_size */ + int last_col_width; /* # of non-dummy blocks across in last MCU */ + int last_row_height; /* # of non-dummy blocks down in last MCU */ + + /* Saved quantization table for component; NULL if none yet saved. + * See jdinput.c comments about the need for this information. + * This field is currently used only for decompression. + */ + JQUANT_TBL * quant_table; + + /* Private per-component storage for DCT or IDCT subsystem. */ + void * dct_table; +} jpeg_component_info; + + +/* The script for encoding a multiple-scan file is an array of these: */ + +typedef struct { + int comps_in_scan; /* number of components encoded in this scan */ + int component_index[MAX_COMPS_IN_SCAN]; /* their SOF/comp_info[] indexes */ + int Ss, Se; /* progressive JPEG spectral selection parms */ + int Ah, Al; /* progressive JPEG successive approx. parms */ +} jpeg_scan_info; + +/* The decompressor can save APPn and COM markers in a list of these: */ + +typedef struct jpeg_marker_struct FAR * jpeg_saved_marker_ptr; + +struct jpeg_marker_struct { + jpeg_saved_marker_ptr next; /* next in list, or NULL */ + UINT8 marker; /* marker code: JPEG_COM, or JPEG_APP0+n */ + unsigned int original_length; /* # bytes of data in the file */ + unsigned int data_length; /* # bytes of data saved at data[] */ + JOCTET FAR * data; /* the data contained in the marker */ + /* the marker length word is not counted in data_length or original_length */ +}; + +/* Known color spaces. */ + +typedef enum { + JCS_UNKNOWN, /* error/unspecified */ + JCS_GRAYSCALE, /* monochrome */ + JCS_RGB, /* red/green/blue */ + JCS_YCbCr, /* Y/Cb/Cr (also known as YUV) */ + JCS_CMYK, /* C/M/Y/K */ + JCS_YCCK /* Y/Cb/Cr/K */ +} J_COLOR_SPACE; + +/* DCT/IDCT algorithm options. */ + +typedef enum { + JDCT_ISLOW, /* slow but accurate integer algorithm */ + JDCT_IFAST, /* faster, less accurate integer method */ + JDCT_FLOAT /* floating-point: accurate, fast on fast HW */ +} J_DCT_METHOD; + +#ifndef JDCT_DEFAULT /* may be overridden in jconfig.h */ +#define JDCT_DEFAULT JDCT_ISLOW +#endif +#ifndef JDCT_FASTEST /* may be overridden in jconfig.h */ +#define JDCT_FASTEST JDCT_IFAST +#endif + +/* Dithering options for decompression. */ + +typedef enum { + JDITHER_NONE, /* no dithering */ + JDITHER_ORDERED, /* simple ordered dither */ + JDITHER_FS /* Floyd-Steinberg error diffusion dither */ +} J_DITHER_MODE; + + +/* Common fields between JPEG compression and decompression master structs. */ + +#define jpeg_common_fields \ + struct jpeg_error_mgr * err; /* Error handler module */\ + struct jpeg_memory_mgr * mem; /* Memory manager module */\ + struct jpeg_progress_mgr * progress; /* Progress monitor, or NULL if none */\ + void * client_data; /* Available for use by application */\ + boolean is_decompressor; /* So common code can tell which is which */\ + int global_state /* For checking call sequence validity */ + +/* Routines that are to be used by both halves of the library are declared + * to receive a pointer to this structure. There are no actual instances of + * jpeg_common_struct, only of jpeg_compress_struct and jpeg_decompress_struct. + */ +struct jpeg_common_struct { + jpeg_common_fields; /* Fields common to both master struct types */ + /* Additional fields follow in an actual jpeg_compress_struct or + * jpeg_decompress_struct. All three structs must agree on these + * initial fields! (This would be a lot cleaner in C++.) + */ +}; + +typedef struct jpeg_common_struct * j_common_ptr; +typedef struct jpeg_compress_struct * j_compress_ptr; +typedef struct jpeg_decompress_struct * j_decompress_ptr; + + +/* Master record for a compression instance */ + +struct jpeg_compress_struct { + jpeg_common_fields; /* Fields shared with jpeg_decompress_struct */ + + /* Destination for compressed data */ + struct jpeg_destination_mgr * dest; + + /* Description of source image --- these fields must be filled in by + * outer application before starting compression. in_color_space must + * be correct before you can even call jpeg_set_defaults(). + */ + + JDIMENSION image_width; /* input image width */ + JDIMENSION image_height; /* input image height */ + int input_components; /* # of color components in input image */ + J_COLOR_SPACE in_color_space; /* colorspace of input image */ + + double input_gamma; /* image gamma of input image */ + + /* Compression parameters --- these fields must be set before calling + * jpeg_start_compress(). We recommend calling jpeg_set_defaults() to + * initialize everything to reasonable defaults, then changing anything + * the application specifically wants to change. That way you won't get + * burnt when new parameters are added. Also note that there are several + * helper routines to simplify changing parameters. + */ + + int data_precision; /* bits of precision in image data */ + + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + int num_scans; /* # of entries in scan_info array */ + const jpeg_scan_info * scan_info; /* script for multi-scan file, or NULL */ + /* The default value of scan_info is NULL, which causes a single-scan + * sequential JPEG file to be emitted. To create a multi-scan file, + * set num_scans and scan_info to point to an array of scan definitions. + */ + + boolean raw_data_in; /* TRUE=caller supplies downsampled data */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + boolean optimize_coding; /* TRUE=optimize entropy encoding parms */ + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + int smoothing_factor; /* 1..100, or 0 for no input smoothing */ + J_DCT_METHOD dct_method; /* DCT algorithm selector */ + + /* The restart interval can be specified in absolute MCUs by setting + * restart_interval, or in MCU rows by setting restart_in_rows + * (in which case the correct restart_interval will be figured + * for each scan). + */ + unsigned int restart_interval; /* MCUs per restart, or 0 for no restart */ + int restart_in_rows; /* if > 0, MCU rows per restart interval */ + + /* Parameters controlling emission of special markers. */ + + boolean write_JFIF_header; /* should a JFIF marker be written? */ + UINT8 JFIF_major_version; /* What to write for the JFIF version number */ + UINT8 JFIF_minor_version; + /* These three values are not used by the JPEG code, merely copied */ + /* into the JFIF APP0 marker. density_unit can be 0 for unknown, */ + /* 1 for dots/inch, or 2 for dots/cm. Note that the pixel aspect */ + /* ratio is defined by X_density/Y_density even when density_unit=0. */ + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean write_Adobe_marker; /* should an Adobe marker be written? */ + + /* State variable: index of next scanline to be written to + * jpeg_write_scanlines(). Application may use this to control its + * processing loop, e.g., "while (next_scanline < image_height)". + */ + + JDIMENSION next_scanline; /* 0 .. image_height-1 */ + + /* Remaining fields are known throughout compressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during compression startup + */ + boolean progressive_mode; /* TRUE if scan script uses progressive mode */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows to be input to coef ctlr */ + /* The coefficient controller receives data in units of MCU rows as defined + * for fully interleaved scans (whether the JPEG file is interleaved or not). + * There are v_samp_factor * DCTSIZE sample rows of each component in an + * "iMCU" (interleaved MCU) row. + */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[C_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + /* + * Links to compression subobjects (methods and private variables of modules) + */ + struct jpeg_comp_master * master; + struct jpeg_c_main_controller * main; + struct jpeg_c_prep_controller * prep; + struct jpeg_c_coef_controller * coef; + struct jpeg_marker_writer * marker; + struct jpeg_color_converter * cconvert; + struct jpeg_downsampler * downsample; + struct jpeg_forward_dct * fdct; + struct jpeg_entropy_encoder * entropy; + jpeg_scan_info * script_space; /* workspace for jpeg_simple_progression */ + int script_space_size; +}; + + +/* Master record for a decompression instance */ + +struct jpeg_decompress_struct { + jpeg_common_fields; /* Fields shared with jpeg_compress_struct */ + + /* Source of compressed data */ + struct jpeg_source_mgr * src; + + /* Basic description of image --- filled in by jpeg_read_header(). */ + /* Application may inspect these values to decide how to process image. */ + + JDIMENSION image_width; /* nominal image width (from SOF marker) */ + JDIMENSION image_height; /* nominal image height */ + int num_components; /* # of color components in JPEG image */ + J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ + + /* Decompression processing parameters --- these fields must be set before + * calling jpeg_start_decompress(). Note that jpeg_read_header() initializes + * them to default values. + */ + + J_COLOR_SPACE out_color_space; /* colorspace for output */ + + unsigned int scale_num, scale_denom; /* fraction by which to scale image */ + + double output_gamma; /* image gamma wanted in output */ + + boolean buffered_image; /* TRUE=multiple output passes */ + boolean raw_data_out; /* TRUE=downsampled data wanted */ + + J_DCT_METHOD dct_method; /* IDCT algorithm selector */ + boolean do_fancy_upsampling; /* TRUE=apply fancy upsampling */ + boolean do_block_smoothing; /* TRUE=apply interblock smoothing */ + + boolean quantize_colors; /* TRUE=colormapped output wanted */ + /* the following are ignored if not quantize_colors: */ + J_DITHER_MODE dither_mode; /* type of color dithering to use */ + boolean two_pass_quantize; /* TRUE=use two-pass color quantization */ + int desired_number_of_colors; /* max # colors to use in created colormap */ + /* these are significant only in buffered-image mode: */ + boolean enable_1pass_quant; /* enable future use of 1-pass quantizer */ + boolean enable_external_quant;/* enable future use of external colormap */ + boolean enable_2pass_quant; /* enable future use of 2-pass quantizer */ + + /* Description of actual output image that will be returned to application. + * These fields are computed by jpeg_start_decompress(). + * You can also use jpeg_calc_output_dimensions() to determine these values + * in advance of calling jpeg_start_decompress(). + */ + + JDIMENSION output_width; /* scaled image width */ + JDIMENSION output_height; /* scaled image height */ + int out_color_components; /* # of color components in out_color_space */ + int output_components; /* # of color components returned */ + /* output_components is 1 (a colormap index) when quantizing colors; + * otherwise it equals out_color_components. + */ + int rec_outbuf_height; /* min recommended height of scanline buffer */ + /* If the buffer passed to jpeg_read_scanlines() is less than this many rows + * high, space and time will be wasted due to unnecessary data copying. + * Usually rec_outbuf_height will be 1 or 2, at most 4. + */ + + /* When quantizing colors, the output colormap is described by these fields. + * The application can supply a colormap by setting colormap non-NULL before + * calling jpeg_start_decompress; otherwise a colormap is created during + * jpeg_start_decompress or jpeg_start_output. + * The map has out_color_components rows and actual_number_of_colors columns. + */ + int actual_number_of_colors; /* number of entries in use */ + JSAMPARRAY colormap; /* The color map as a 2-D pixel array */ + + /* State variables: these variables indicate the progress of decompression. + * The application may examine these but must not modify them. + */ + + /* Row index of next scanline to be read from jpeg_read_scanlines(). + * Application may use this to control its processing loop, e.g., + * "while (output_scanline < output_height)". + */ + JDIMENSION output_scanline; /* 0 .. output_height-1 */ + + /* Current input scan number and number of iMCU rows completed in scan. + * These indicate the progress of the decompressor input side. + */ + int input_scan_number; /* Number of SOS markers seen so far */ + JDIMENSION input_iMCU_row; /* Number of iMCU rows completed */ + + /* The "output scan number" is the notional scan being displayed by the + * output side. The decompressor will not allow output scan/row number + * to get ahead of input scan/row, but it can fall arbitrarily far behind. + */ + int output_scan_number; /* Nominal scan number being displayed */ + JDIMENSION output_iMCU_row; /* Number of iMCU rows read */ + + /* Current progression status. coef_bits[c][i] indicates the precision + * with which component c's DCT coefficient i (in zigzag order) is known. + * It is -1 when no data has yet been received, otherwise it is the point + * transform (shift) value for the most recent scan of the coefficient + * (thus, 0 at completion of the progression). + * This pointer is NULL when reading a non-progressive file. + */ + int (*coef_bits)[DCTSIZE2]; /* -1 or current Al value for each coef */ + + /* Internal JPEG parameters --- the application usually need not look at + * these fields. Note that the decompressor output side may not use + * any parameters that can change between scans. + */ + + /* Quantization and Huffman tables are carried forward across input + * datastreams when processing abbreviated JPEG datastreams. + */ + + JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + /* ptrs to coefficient quantization tables, or NULL if not defined */ + + JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + /* ptrs to Huffman coding tables, or NULL if not defined */ + + /* These parameters are never carried across datastreams, since they + * are given in SOF/SOS markers or defined to be reset by SOI. + */ + + int data_precision; /* bits of precision in image data */ + + jpeg_component_info * comp_info; + /* comp_info[i] describes component that appears i'th in SOF */ + + boolean progressive_mode; /* TRUE if SOFn specifies progressive mode */ + boolean arith_code; /* TRUE=arithmetic coding, FALSE=Huffman */ + + UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ + UINT8 arith_dc_U[NUM_ARITH_TBLS]; /* U values for DC arith-coding tables */ + UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ + + unsigned int restart_interval; /* MCUs per restart interval, or 0 for no restart */ + + /* These fields record data obtained from optional markers recognized by + * the JPEG library. + */ + boolean saw_JFIF_marker; /* TRUE iff a JFIF APP0 marker was found */ + /* Data copied from JFIF marker; only valid if saw_JFIF_marker is TRUE: */ + UINT8 JFIF_major_version; /* JFIF version number */ + UINT8 JFIF_minor_version; + UINT8 density_unit; /* JFIF code for pixel size units */ + UINT16 X_density; /* Horizontal pixel density */ + UINT16 Y_density; /* Vertical pixel density */ + boolean saw_Adobe_marker; /* TRUE iff an Adobe APP14 marker was found */ + UINT8 Adobe_transform; /* Color transform code from Adobe marker */ + + boolean CCIR601_sampling; /* TRUE=first samples are cosited */ + + /* Aside from the specific data retained from APPn markers known to the + * library, the uninterpreted contents of any or all APPn and COM markers + * can be saved in a list for examination by the application. + */ + jpeg_saved_marker_ptr marker_list; /* Head of list of saved markers */ + + /* Remaining fields are known throughout decompressor, but generally + * should not be touched by a surrounding application. + */ + + /* + * These fields are computed during decompression startup + */ + int max_h_samp_factor; /* largest h_samp_factor */ + int max_v_samp_factor; /* largest v_samp_factor */ + + int min_DCT_scaled_size; /* smallest DCT_scaled_size of any component */ + + JDIMENSION total_iMCU_rows; /* # of iMCU rows in image */ + /* The coefficient controller's input and output progress is measured in + * units of "iMCU" (interleaved MCU) rows. These are the same as MCU rows + * in fully interleaved JPEG scans, but are used whether the scan is + * interleaved or not. We define an iMCU row as v_samp_factor DCT block + * rows of each component. Therefore, the IDCT output contains + * v_samp_factor*DCT_scaled_size sample rows of a component per iMCU row. + */ + + JSAMPLE * sample_range_limit; /* table for fast range-limiting */ + + /* + * These fields are valid during any one scan. + * They describe the components and MCUs actually appearing in the scan. + * Note that the decompressor output side must not use these fields. + */ + int comps_in_scan; /* # of JPEG components in this scan */ + jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + /* *cur_comp_info[i] describes component that appears i'th in SOS */ + + JDIMENSION MCUs_per_row; /* # of MCUs across the image */ + JDIMENSION MCU_rows_in_scan; /* # of MCU rows in the image */ + + int blocks_in_MCU; /* # of DCT blocks per MCU */ + int MCU_membership[D_MAX_BLOCKS_IN_MCU]; + /* MCU_membership[i] is index in cur_comp_info of component owning */ + /* i'th block in an MCU */ + + int Ss, Se, Ah, Al; /* progressive JPEG parameters for scan */ + + /* This field is shared between entropy decoder and marker parser. + * It is either zero or the code of a JPEG marker that has been + * read from the data source, but has not yet been processed. + */ + int unread_marker; + + /* + * Links to decompression subobjects (methods, private variables of modules) + */ + struct jpeg_decomp_master * master; + struct jpeg_d_main_controller * main; + struct jpeg_d_coef_controller * coef; + struct jpeg_d_post_controller * post; + struct jpeg_input_controller * inputctl; + struct jpeg_marker_reader * marker; + struct jpeg_entropy_decoder * entropy; + struct jpeg_inverse_dct * idct; + struct jpeg_upsampler * upsample; + struct jpeg_color_deconverter * cconvert; + struct jpeg_color_quantizer * cquantize; +}; + + +/* "Object" declarations for JPEG modules that may be supplied or called + * directly by the surrounding application. + * As with all objects in the JPEG library, these structs only define the + * publicly visible methods and state variables of a module. Additional + * private fields may exist after the public ones. + */ + + +/* Error handler object */ + +struct jpeg_error_mgr { + /* Error exit handler: does not return to caller */ + JMETHOD(void, error_exit, (j_common_ptr cinfo)); + /* Conditionally emit a trace or warning message */ + JMETHOD(void, emit_message, (j_common_ptr cinfo, int msg_level)); + /* Routine that actually outputs a trace or error message */ + JMETHOD(void, output_message, (j_common_ptr cinfo)); + /* Format a message string for the most recent JPEG error or message */ + JMETHOD(void, format_message, (j_common_ptr cinfo, char * buffer)); +#define JMSG_LENGTH_MAX 200 /* recommended size of format_message buffer */ + /* Reset error state variables at start of a new image */ + JMETHOD(void, reset_error_mgr, (j_common_ptr cinfo)); + + /* The message ID code and any parameters are saved here. + * A message can have one string parameter or up to 8 int parameters. + */ + int msg_code; +#define JMSG_STR_PARM_MAX 80 + union { + int i[8]; + char s[JMSG_STR_PARM_MAX]; + } msg_parm; + + /* Standard state variables for error facility */ + + int trace_level; /* max msg_level that will be displayed */ + + /* For recoverable corrupt-data errors, we emit a warning message, + * but keep going unless emit_message chooses to abort. emit_message + * should count warnings in num_warnings. The surrounding application + * can check for bad data by seeing if num_warnings is nonzero at the + * end of processing. + */ + long num_warnings; /* number of corrupt-data warnings */ + + /* These fields point to the table(s) of error message strings. + * An application can change the table pointer to switch to a different + * message list (typically, to change the language in which errors are + * reported). Some applications may wish to add additional error codes + * that will be handled by the JPEG library error mechanism; the second + * table pointer is used for this purpose. + * + * First table includes all errors generated by JPEG library itself. + * Error code 0 is reserved for a "no such error string" message. + */ + const char * const * jpeg_message_table; /* Library errors */ + int last_jpeg_message; /* Table contains strings 0..last_jpeg_message */ + /* Second table can be added by application (see cjpeg/djpeg for example). + * It contains strings numbered first_addon_message..last_addon_message. + */ + const char * const * addon_message_table; /* Non-library errors */ + int first_addon_message; /* code for first string in addon table */ + int last_addon_message; /* code for last string in addon table */ +}; + + +/* Progress monitor object */ + +struct jpeg_progress_mgr { + JMETHOD(void, progress_monitor, (j_common_ptr cinfo)); + + long pass_counter; /* work units completed in this pass */ + long pass_limit; /* total number of work units in this pass */ + int completed_passes; /* passes completed so far */ + int total_passes; /* total number of passes expected */ +}; + + +/* Data destination object for compression */ + +struct jpeg_destination_mgr { + JOCTET * next_output_byte; /* => next byte to write in buffer */ + jpeg_size_t free_in_buffer; /* # of byte spaces remaining in buffer */ + + JMETHOD(void, init_destination, (j_compress_ptr cinfo)); + JMETHOD(boolean, empty_output_buffer, (j_compress_ptr cinfo)); + JMETHOD(void, term_destination, (j_compress_ptr cinfo)); +}; + + +/* Data source object for decompression */ + +struct jpeg_source_mgr { + const JOCTET * next_input_byte; /* => next byte to read from buffer */ + jpeg_size_t bytes_in_buffer; /* # of bytes remaining in buffer */ + + JMETHOD(void, init_source, (j_decompress_ptr cinfo)); + JMETHOD(boolean, fill_input_buffer, (j_decompress_ptr cinfo)); + JMETHOD(void, skip_input_data, (j_decompress_ptr cinfo, long num_bytes)); + JMETHOD(boolean, resync_to_restart, (j_decompress_ptr cinfo, int desired)); + JMETHOD(void, term_source, (j_decompress_ptr cinfo)); +}; + + +/* Memory manager object. + * Allocates "small" objects (a few K total), "large" objects (tens of K), + * and "really big" objects (virtual arrays with backing store if needed). + * The memory manager does not allow individual objects to be freed; rather, + * each created object is assigned to a pool, and whole pools can be freed + * at once. This is faster and more convenient than remembering exactly what + * to free, especially where malloc()/free() are not too speedy. + * NB: alloc routines never return NULL. They exit to error_exit if not + * successful. + */ + +#define JPOOL_PERMANENT 0 /* lasts until master record is destroyed */ +#define JPOOL_IMAGE 1 /* lasts until done with image/datastream */ +#define JPOOL_NUMPOOLS 2 + +typedef struct jvirt_sarray_control * jvirt_sarray_ptr; +typedef struct jvirt_barray_control * jvirt_barray_ptr; + + +struct jpeg_memory_mgr { + /* Method pointers */ + JMETHOD(void *, alloc_small, (j_common_ptr cinfo, int pool_id, + jpeg_size_t sizeofobject)); + JMETHOD(void FAR *, alloc_large, (j_common_ptr cinfo, int pool_id, + jpeg_size_t sizeofobject)); + JMETHOD(JSAMPARRAY, alloc_sarray, (j_common_ptr cinfo, int pool_id, + JDIMENSION samplesperrow, + JDIMENSION numrows)); + JMETHOD(JBLOCKARRAY, alloc_barray, (j_common_ptr cinfo, int pool_id, + JDIMENSION blocksperrow, + JDIMENSION numrows)); + JMETHOD(jvirt_sarray_ptr, request_virt_sarray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION samplesperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(jvirt_barray_ptr, request_virt_barray, (j_common_ptr cinfo, + int pool_id, + boolean pre_zero, + JDIMENSION blocksperrow, + JDIMENSION numrows, + JDIMENSION maxaccess)); + JMETHOD(void, realize_virt_arrays, (j_common_ptr cinfo)); + JMETHOD(JSAMPARRAY, access_virt_sarray, (j_common_ptr cinfo, + jvirt_sarray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(JBLOCKARRAY, access_virt_barray, (j_common_ptr cinfo, + jvirt_barray_ptr ptr, + JDIMENSION start_row, + JDIMENSION num_rows, + boolean writable)); + JMETHOD(void, free_pool, (j_common_ptr cinfo, int pool_id)); + JMETHOD(void, self_destruct, (j_common_ptr cinfo)); + + /* Limit on memory allocation for this JPEG object. (Note that this is + * merely advisory, not a guaranteed maximum; it only affects the space + * used for virtual-array buffers.) May be changed by outer application + * after creating the JPEG object. + */ + long max_memory_to_use; + + /* Maximum allocation request accepted by alloc_large. */ + long max_alloc_chunk; +}; + + +/* Routine signature for application-supplied marker processing methods. + * Need not pass marker code since it is stored in cinfo->unread_marker. + */ +typedef JMETHOD(boolean, jpeg_marker_parser_method, (j_decompress_ptr cinfo)); + + +/* Declarations for routines called by application. + * The JPP macro hides prototype parameters from compilers that can't cope. + * Note JPP requires double parentheses. + */ + +#ifdef HAVE_PROTOTYPES +#define JPP(arglist) arglist +#else +#define JPP(arglist) () +#endif + + +/* Short forms of external names for systems with brain-damaged linkers. + * We shorten external names to be unique in the first six letters, which + * is good enough for all known systems. + * (If your compiler itself needs names to be unique in less than 15 + * characters, you are out of luck. Get a better compiler.) + */ + +#ifdef NEED_SHORT_EXTERNAL_NAMES +#define jpeg_std_error jStdError +#define jpeg_CreateCompress jCreaCompress +#define jpeg_CreateDecompress jCreaDecompress +#define jpeg_destroy_compress jDestCompress +#define jpeg_destroy_decompress jDestDecompress +#define jpeg_stdio_dest jStdDest +#define jpeg_stdio_src jStdSrc +#define jpeg_set_defaults jSetDefaults +#define jpeg_set_colorspace jSetColorspace +#define jpeg_default_colorspace jDefColorspace +#define jpeg_set_quality jSetQuality +#define jpeg_set_linear_quality jSetLQuality +#define jpeg_add_quant_table jAddQuantTable +#define jpeg_quality_scaling jQualityScaling +#define jpeg_simple_progression jSimProgress +#define jpeg_suppress_tables jSuppressTables +#define jpeg_alloc_quant_table jAlcQTable +#define jpeg_alloc_huff_table jAlcHTable +#define jpeg_start_compress jStrtCompress +#define jpeg_write_scanlines jWrtScanlines +#define jpeg_finish_compress jFinCompress +#define jpeg_write_raw_data jWrtRawData +#define jpeg_write_marker jWrtMarker +#define jpeg_write_m_header jWrtMHeader +#define jpeg_write_m_byte jWrtMByte +#define jpeg_write_tables jWrtTables +#define jpeg_read_header jReadHeader +#define jpeg_start_decompress jStrtDecompress +#define jpeg_read_scanlines jReadScanlines +#define jpeg_finish_decompress jFinDecompress +#define jpeg_read_raw_data jReadRawData +#define jpeg_has_multiple_scans jHasMultScn +#define jpeg_start_output jStrtOutput +#define jpeg_finish_output jFinOutput +#define jpeg_input_complete jInComplete +#define jpeg_new_colormap jNewCMap +#define jpeg_consume_input jConsumeInput +#define jpeg_calc_output_dimensions jCalcDimensions +#define jpeg_save_markers jSaveMarkers +#define jpeg_set_marker_processor jSetMarker +#define jpeg_read_coefficients jReadCoefs +#define jpeg_write_coefficients jWrtCoefs +#define jpeg_copy_critical_parameters jCopyCrit +#define jpeg_abort_compress jAbrtCompress +#define jpeg_abort_decompress jAbrtDecompress +#define jpeg_abort jAbort +#define jpeg_destroy jDestroy +#define jpeg_resync_to_restart jResyncRestart +#endif /* NEED_SHORT_EXTERNAL_NAMES */ + + +/* Default error-management setup */ +EXTERN(struct jpeg_error_mgr *) jpeg_std_error + JPP((struct jpeg_error_mgr * err)); + +/* Initialization of JPEG compression objects. + * jpeg_create_compress() and jpeg_create_decompress() are the exported + * names that applications should call. These expand to calls on + * jpeg_CreateCompress and jpeg_CreateDecompress with additional information + * passed for version mismatch checking. + * NB: you must set up the error-manager BEFORE calling jpeg_create_xxx. + */ +#define jpeg_create_compress(cinfo) \ + jpeg_CreateCompress((cinfo), JPEG_LIB_VERSION, \ + (jpeg_size_t) sizeof(struct jpeg_compress_struct)) +#define jpeg_create_decompress(cinfo) \ + jpeg_CreateDecompress((cinfo), JPEG_LIB_VERSION, \ + (jpeg_size_t) sizeof(struct jpeg_decompress_struct)) +EXTERN(void) jpeg_CreateCompress JPP((j_compress_ptr cinfo, + int version, jpeg_size_t structsize)); +EXTERN(void) jpeg_CreateDecompress JPP((j_decompress_ptr cinfo, + int version, jpeg_size_t structsize)); +/* Destruction of JPEG compression objects */ +EXTERN(void) jpeg_destroy_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_destroy_decompress JPP((j_decompress_ptr cinfo)); + +/* Standard data source and destination managers: stdio streams. */ +/* Caller is responsible for opening the file before and closing after. */ +EXTERN(void) jpeg_stdio_dest JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_stdio_src JPP((j_decompress_ptr cinfo)); + +/* Default parameter setup for compression */ +EXTERN(void) jpeg_set_defaults JPP((j_compress_ptr cinfo)); +/* Compression parameter setup aids */ +EXTERN(void) jpeg_set_colorspace JPP((j_compress_ptr cinfo, + J_COLOR_SPACE colorspace)); +EXTERN(void) jpeg_default_colorspace JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_set_quality JPP((j_compress_ptr cinfo, int quality, + boolean force_baseline)); +EXTERN(void) jpeg_set_linear_quality JPP((j_compress_ptr cinfo, + int scale_factor, + boolean force_baseline)); +EXTERN(void) jpeg_add_quant_table JPP((j_compress_ptr cinfo, int which_tbl, + const unsigned int *basic_table, + int scale_factor, + boolean force_baseline)); +EXTERN(int) jpeg_quality_scaling JPP((int quality)); +EXTERN(void) jpeg_simple_progression JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_suppress_tables JPP((j_compress_ptr cinfo, + boolean suppress)); +EXTERN(JQUANT_TBL *) jpeg_alloc_quant_table JPP((j_common_ptr cinfo)); +EXTERN(JHUFF_TBL *) jpeg_alloc_huff_table JPP((j_common_ptr cinfo)); + +/* Main entry points for compression */ +EXTERN(void) jpeg_start_compress JPP((j_compress_ptr cinfo, + boolean write_all_tables)); +EXTERN(JDIMENSION) jpeg_write_scanlines JPP((j_compress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION num_lines)); +EXTERN(void) jpeg_finish_compress JPP((j_compress_ptr cinfo)); + +/* Replaces jpeg_write_scanlines when writing raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_write_raw_data JPP((j_compress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION num_lines)); + +/* Write a special marker. See libjpeg.doc concerning safe usage. */ +EXTERN(void) jpeg_write_marker + JPP((j_compress_ptr cinfo, int marker, + const JOCTET * dataptr, unsigned int datalen)); +/* Same, but piecemeal. */ +EXTERN(void) jpeg_write_m_header + JPP((j_compress_ptr cinfo, int marker, unsigned int datalen)); +EXTERN(void) jpeg_write_m_byte + JPP((j_compress_ptr cinfo, int val)); + +/* Alternate compression function: just write an abbreviated table file */ +EXTERN(void) jpeg_write_tables JPP((j_compress_ptr cinfo)); + +/* Decompression startup: read start of JPEG datastream to see what's there */ +EXTERN(int) jpeg_read_header JPP((j_decompress_ptr cinfo, + boolean require_image)); +/* Return value is one of: */ +#define JPEG_SUSPENDED 0 /* Suspended due to lack of input data */ +#define JPEG_HEADER_OK 1 /* Found valid image datastream */ +#define JPEG_HEADER_TABLES_ONLY 2 /* Found valid table-specs-only datastream */ +/* If you pass require_image = TRUE (normal case), you need not check for + * a TABLES_ONLY return code; an abbreviated file will cause an error exit. + * JPEG_SUSPENDED is only possible if you use a data source module that can + * give a suspension return (the stdio source module doesn't). + */ + +/* Main entry points for decompression */ +EXTERN(boolean) jpeg_start_decompress JPP((j_decompress_ptr cinfo)); +EXTERN(JDIMENSION) jpeg_read_scanlines JPP((j_decompress_ptr cinfo, + JSAMPARRAY scanlines, + JDIMENSION max_lines)); +EXTERN(boolean) jpeg_finish_decompress JPP((j_decompress_ptr cinfo)); + +/* Replaces jpeg_read_scanlines when reading raw downsampled data. */ +EXTERN(JDIMENSION) jpeg_read_raw_data JPP((j_decompress_ptr cinfo, + JSAMPIMAGE data, + JDIMENSION max_lines)); + +/* Additional entry points for buffered-image mode. */ +EXTERN(boolean) jpeg_has_multiple_scans JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_start_output JPP((j_decompress_ptr cinfo, + int scan_number)); +EXTERN(boolean) jpeg_finish_output JPP((j_decompress_ptr cinfo)); +EXTERN(boolean) jpeg_input_complete JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_new_colormap JPP((j_decompress_ptr cinfo)); +EXTERN(int) jpeg_consume_input JPP((j_decompress_ptr cinfo)); +/* Return value is one of: */ +/* #define JPEG_SUSPENDED 0 Suspended due to lack of input data */ +#define JPEG_REACHED_SOS 1 /* Reached start of new scan */ +#define JPEG_REACHED_EOI 2 /* Reached end of image */ +#define JPEG_ROW_COMPLETED 3 /* Completed one iMCU row */ +#define JPEG_SCAN_COMPLETED 4 /* Completed last iMCU row of a scan */ + +/* Precalculate output dimensions for current decompression parameters. */ +EXTERN(void) jpeg_calc_output_dimensions JPP((j_decompress_ptr cinfo)); + +/* Control saving of COM and APPn markers into marker_list. */ +EXTERN(void) jpeg_save_markers + JPP((j_decompress_ptr cinfo, int marker_code, + unsigned int length_limit)); + +/* Install a special processing method for COM or APPn markers. */ +EXTERN(void) jpeg_set_marker_processor + JPP((j_decompress_ptr cinfo, int marker_code, + jpeg_marker_parser_method routine)); + +/* Read or write raw DCT coefficients --- useful for lossless transcoding. */ +EXTERN(jvirt_barray_ptr *) jpeg_read_coefficients JPP((j_decompress_ptr cinfo)); +EXTERN(void) jpeg_write_coefficients JPP((j_compress_ptr cinfo, + jvirt_barray_ptr * coef_arrays)); +EXTERN(void) jpeg_copy_critical_parameters JPP((j_decompress_ptr srcinfo, + j_compress_ptr dstinfo)); + +/* If you choose to abort compression or decompression before completing + * jpeg_finish_(de)compress, then you need to clean up to release memory, + * temporary files, etc. You can just call jpeg_destroy_(de)compress + * if you're done with the JPEG object, but if you want to clean it up and + * reuse it, call this: + */ +EXTERN(void) jpeg_abort_compress JPP((j_compress_ptr cinfo)); +EXTERN(void) jpeg_abort_decompress JPP((j_decompress_ptr cinfo)); + +/* Generic versions of jpeg_abort and jpeg_destroy that work on either + * flavor of JPEG object. These may be more convenient in some places. + */ +EXTERN(void) jpeg_abort JPP((j_common_ptr cinfo)); +EXTERN(void) jpeg_destroy JPP((j_common_ptr cinfo)); + +/* Default restart-marker-resync procedure for use by data source modules */ +EXTERN(boolean) jpeg_resync_to_restart JPP((j_decompress_ptr cinfo, + int desired)); + + +extern int (*JFREAD)(void *client_data, unsigned char*, int); +extern int (*JFWRITE)(void *client_data, unsigned char*, int); +extern int (*JFFLUSH)(void *client_data); +extern int (*JFERROR)(void *client_data); + + +/* These marker codes are exported since applications and data source modules + * are likely to want to use them. + */ + +#define JPEG_RST0 0xD0 /* RST0 marker code */ +#define JPEG_EOI 0xD9 /* EOI marker code */ +#define JPEG_APP0 0xE0 /* APP0 marker code */ +#define JPEG_COM 0xFE /* COM marker code */ + + +/* If we have a brain-damaged compiler that emits warnings (or worse, errors) + * for structure definitions that are never filled in, keep it quiet by + * supplying dummy definitions for the various substructures. + */ + +#ifdef INCOMPLETE_TYPES_BROKEN +#ifndef JPEG_INTERNALS /* will be defined in jpegint.h */ +struct jvirt_sarray_control { long dummy; }; +struct jvirt_barray_control { long dummy; }; +struct jpeg_comp_master { long dummy; }; +struct jpeg_c_main_controller { long dummy; }; +struct jpeg_c_prep_controller { long dummy; }; +struct jpeg_c_coef_controller { long dummy; }; +struct jpeg_marker_writer { long dummy; }; +struct jpeg_color_converter { long dummy; }; +struct jpeg_downsampler { long dummy; }; +struct jpeg_forward_dct { long dummy; }; +struct jpeg_entropy_encoder { long dummy; }; +struct jpeg_decomp_master { long dummy; }; +struct jpeg_d_main_controller { long dummy; }; +struct jpeg_d_coef_controller { long dummy; }; +struct jpeg_d_post_controller { long dummy; }; +struct jpeg_input_controller { long dummy; }; +struct jpeg_marker_reader { long dummy; }; +struct jpeg_entropy_decoder { long dummy; }; +struct jpeg_inverse_dct { long dummy; }; +struct jpeg_upsampler { long dummy; }; +struct jpeg_color_deconverter { long dummy; }; +struct jpeg_color_quantizer { long dummy; }; +#endif /* JPEG_INTERNALS */ +#endif /* INCOMPLETE_TYPES_BROKEN */ + + +/* + * The JPEG library modules define JPEG_INTERNALS before including this file. + * The internal structure declarations are read only when that is true. + * Applications using the library should not include jpegint.h, but may wish + * to include jerror.h. + */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#ifdef JPEG_INTERNALS +#include "jpegint.h" /* fetch private declarations */ +#include "jerror.h" /* fetch error codes too */ +#endif + +#endif /* JPEGLIB_H */ diff --git a/Engine/lib/ljpeg/jpegtran/jpegtran.c b/Engine/lib/ljpeg/jpegtran/jpegtran.c new file mode 100644 index 000000000..599930088 --- /dev/null +++ b/Engine/lib/ljpeg/jpegtran/jpegtran.c @@ -0,0 +1,506 @@ +#if 0 +/* + * jpegtran.c + * + * Copyright (C) 1995-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains a command-line user interface for JPEG transcoding. + * It is very similar to cjpeg.c, but provides lossless transcoding between + * different JPEG file formats. It also provides some lossless and sort-of- + * lossless transformations of JPEG data. + */ + +#include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ +#include "transupp.h" /* Support routines for jpegtran */ +#include "jversion.h" /* for version message */ + +#ifdef USE_CCOMMAND /* command-line reader for Macintosh */ +#ifdef __MWERKS__ +#include /* Metrowerks needs this */ +#include /* ... and this */ +#endif +#ifdef THINK_C +#include /* Think declares it here */ +#endif +#endif + + +/* + * Argument-parsing code. + * The switch parser is designed to be useful with DOS-style command line + * syntax, ie, intermixed switches and file names, where only the switches + * to the left of a given file name affect processing of that file. + * The main program in this file doesn't actually use this capability... + */ + + +static const char * progname; /* program name for error messages */ +static char * outfilename; /* for -outfile switch */ +static JCOPY_OPTION copyoption; /* -copy switch */ +static jpeg_transform_info transformoption; /* image transformation options */ + + +LOCAL(void) +usage (void) +/* complain about bad command line */ +{ + fprintf(stderr, "usage: %s [switches] ", progname); +#ifdef TWO_FILE_COMMANDLINE + fprintf(stderr, "inputfile outputfile\n"); +#else + fprintf(stderr, "[inputfile]\n"); +#endif + + fprintf(stderr, "Switches (names may be abbreviated):\n"); + fprintf(stderr, " -copy none Copy no extra markers from source file\n"); + fprintf(stderr, " -copy comments Copy only comment markers (default)\n"); + fprintf(stderr, " -copy all Copy all extra markers\n"); +#ifdef ENTROPY_OPT_SUPPORTED + fprintf(stderr, " -optimize Optimize Huffman table (smaller file, but slow compression)\n"); +#endif +#ifdef C_PROGRESSIVE_SUPPORTED + fprintf(stderr, " -progressive Create progressive JPEG file\n"); +#endif +#if TRANSFORMS_SUPPORTED + fprintf(stderr, "Switches for modifying the image:\n"); + fprintf(stderr, " -grayscale Reduce to grayscale (omit color data)\n"); + fprintf(stderr, " -flip [horizontal|vertical] Mirror image (left-right or top-bottom)\n"); + fprintf(stderr, " -rotate [90|180|270] Rotate image (degrees clockwise)\n"); + fprintf(stderr, " -transpose Transpose image\n"); + fprintf(stderr, " -transverse Transverse transpose image\n"); + fprintf(stderr, " -trim Drop non-transformable edge blocks\n"); +#endif /* TRANSFORMS_SUPPORTED */ + fprintf(stderr, "Switches for advanced users:\n"); + fprintf(stderr, " -restart N Set restart interval in rows, or in blocks with B\n"); + fprintf(stderr, " -maxmemory N Maximum memory to use (in kbytes)\n"); + fprintf(stderr, " -outfile name Specify name for output file\n"); + fprintf(stderr, " -verbose or -debug Emit debug output\n"); + fprintf(stderr, "Switches for wizards:\n"); +#ifdef C_ARITH_CODING_SUPPORTED + fprintf(stderr, " -arithmetic Use arithmetic coding\n"); +#endif +#ifdef C_MULTISCAN_FILES_SUPPORTED + fprintf(stderr, " -scans file Create multi-scan JPEG per script file\n"); +#endif + exit(EXIT_FAILURE); +} + + +LOCAL(void) +select_transform (JXFORM_CODE transform) +/* Silly little routine to detect multiple transform options, + * which we can't handle. + */ +{ +#if TRANSFORMS_SUPPORTED + if (transformoption.transform == JXFORM_NONE || + transformoption.transform == transform) { + transformoption.transform = transform; + } else { + fprintf(stderr, "%s: can only do one image transformation at a time\n", + progname); + usage(); + } +#else + fprintf(stderr, "%s: sorry, image transformation was not compiled\n", + progname); + exit(EXIT_FAILURE); +#endif +} + + +LOCAL(int) +parse_switches (j_compress_ptr cinfo, int argc, char **argv, + int last_file_arg_seen, boolean for_real) +/* Parse optional switches. + * Returns argv[] index of first file-name argument (== argc if none). + * Any file names with indexes <= last_file_arg_seen are ignored; + * they have presumably been processed in a previous iteration. + * (Pass 0 for last_file_arg_seen on the first or only iteration.) + * for_real is FALSE on the first (dummy) pass; we may skip any expensive + * processing. + */ +{ + int argn; + char * arg; + boolean simple_progressive; + char * scansarg = NULL; /* saves -scans parm if any */ + + /* Set up default JPEG parameters. */ + simple_progressive = FALSE; + outfilename = NULL; + copyoption = JCOPYOPT_DEFAULT; + transformoption.transform = JXFORM_NONE; + transformoption.trim = FALSE; + transformoption.force_grayscale = FALSE; + cinfo->err->trace_level = 0; + + /* Scan command line options, adjust parameters */ + + for (argn = 1; argn < argc; argn++) { + arg = argv[argn]; + if (*arg != '-') { + /* Not a switch, must be a file name argument */ + if (argn <= last_file_arg_seen) { + outfilename = NULL; /* -outfile applies to just one input file */ + continue; /* ignore this name if previously processed */ + } + break; /* else done parsing switches */ + } + arg++; /* advance past switch marker character */ + + if (keymatch(arg, "arithmetic", 1)) { + /* Use arithmetic coding. */ +#ifdef C_ARITH_CODING_SUPPORTED + cinfo->arith_code = TRUE; +#else + fprintf(stderr, "%s: sorry, arithmetic coding not supported\n", + progname); + exit(EXIT_FAILURE); +#endif + + } else if (keymatch(arg, "copy", 1)) { + /* Select which extra markers to copy. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + if (keymatch(argv[argn], "none", 1)) { + copyoption = JCOPYOPT_NONE; + } else if (keymatch(argv[argn], "comments", 1)) { + copyoption = JCOPYOPT_COMMENTS; + } else if (keymatch(argv[argn], "all", 1)) { + copyoption = JCOPYOPT_ALL; + } else + usage(); + + } else if (keymatch(arg, "debug", 1) || keymatch(arg, "verbose", 1)) { + /* Enable debug printouts. */ + /* On first -d, print version identification */ + static boolean printed_version = FALSE; + + if (! printed_version) { + fprintf(stderr, "Independent JPEG Group's JPEGTRAN, version %s\n%s\n", + JVERSION, JCOPYRIGHT); + printed_version = TRUE; + } + cinfo->err->trace_level++; + + } else if (keymatch(arg, "flip", 1)) { + /* Mirror left-right or top-bottom. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + if (keymatch(argv[argn], "horizontal", 1)) + select_transform(JXFORM_FLIP_H); + else if (keymatch(argv[argn], "vertical", 1)) + select_transform(JXFORM_FLIP_V); + else + usage(); + + } else if (keymatch(arg, "grayscale", 1) || keymatch(arg, "greyscale",1)) { + /* Force to grayscale. */ +#if TRANSFORMS_SUPPORTED + transformoption.force_grayscale = TRUE; +#else + select_transform(JXFORM_NONE); /* force an error */ +#endif + + } else if (keymatch(arg, "maxmemory", 3)) { + /* Maximum memory in Kb (or Mb with 'm'). */ + long lval; + char ch = 'x'; + + if (++argn >= argc) /* advance to next argument */ + usage(); + if (sscanf(argv[argn], "%ld%c", &lval, &ch) < 1) + usage(); + if (ch == 'm' || ch == 'M') + lval *= 1000L; + cinfo->mem->max_memory_to_use = lval * 1000L; + + } else if (keymatch(arg, "optimize", 1) || keymatch(arg, "optimise", 1)) { + /* Enable entropy parm optimization. */ +#ifdef ENTROPY_OPT_SUPPORTED + cinfo->optimize_coding = TRUE; +#else + fprintf(stderr, "%s: sorry, entropy optimization was not compiled\n", + progname); + exit(EXIT_FAILURE); +#endif + + } else if (keymatch(arg, "outfile", 4)) { + /* Set output file name. */ + if (++argn >= argc) /* advance to next argument */ + usage(); + outfilename = argv[argn]; /* save it away for later use */ + + } else if (keymatch(arg, "progressive", 1)) { + /* Select simple progressive mode. */ +#ifdef C_PROGRESSIVE_SUPPORTED + simple_progressive = TRUE; + /* We must postpone execution until num_components is known. */ +#else + fprintf(stderr, "%s: sorry, progressive output was not compiled\n", + progname); + exit(EXIT_FAILURE); +#endif + + } else if (keymatch(arg, "restart", 1)) { + /* Restart interval in MCU rows (or in MCUs with 'b'). */ + long lval; + char ch = 'x'; + + if (++argn >= argc) /* advance to next argument */ + usage(); + if (sscanf(argv[argn], "%ld%c", &lval, &ch) < 1) + usage(); + if (lval < 0 || lval > 65535L) + usage(); + if (ch == 'b' || ch == 'B') { + cinfo->restart_interval = (unsigned int) lval; + cinfo->restart_in_rows = 0; /* else prior '-restart n' overrides me */ + } else { + cinfo->restart_in_rows = (int) lval; + /* restart_interval will be computed during startup */ + } + + } else if (keymatch(arg, "rotate", 2)) { + /* Rotate 90, 180, or 270 degrees (measured clockwise). */ + if (++argn >= argc) /* advance to next argument */ + usage(); + if (keymatch(argv[argn], "90", 2)) + select_transform(JXFORM_ROT_90); + else if (keymatch(argv[argn], "180", 3)) + select_transform(JXFORM_ROT_180); + else if (keymatch(argv[argn], "270", 3)) + select_transform(JXFORM_ROT_270); + else + usage(); + + } else if (keymatch(arg, "scans", 1)) { + /* Set scan script. */ +#ifdef C_MULTISCAN_FILES_SUPPORTED + if (++argn >= argc) /* advance to next argument */ + usage(); + scansarg = argv[argn]; + /* We must postpone reading the file in case -progressive appears. */ +#else + fprintf(stderr, "%s: sorry, multi-scan output was not compiled\n", + progname); + exit(EXIT_FAILURE); +#endif + + } else if (keymatch(arg, "transpose", 1)) { + /* Transpose (across UL-to-LR axis). */ + select_transform(JXFORM_TRANSPOSE); + + } else if (keymatch(arg, "transverse", 6)) { + /* Transverse transpose (across UR-to-LL axis). */ + select_transform(JXFORM_TRANSVERSE); + + } else if (keymatch(arg, "trim", 3)) { + /* Trim off any partial edge MCUs that the transform can't handle. */ + transformoption.trim = TRUE; + + } else { + usage(); /* bogus switch */ + } + } + + /* Post-switch-scanning cleanup */ + + if (for_real) { + +#ifdef C_PROGRESSIVE_SUPPORTED + if (simple_progressive) /* process -progressive; -scans can override */ + jpeg_simple_progression(cinfo); +#endif + +#ifdef C_MULTISCAN_FILES_SUPPORTED + if (scansarg != NULL) /* process -scans if it was present */ + if (! read_scan_script(cinfo, scansarg)) + usage(); +#endif + } + + return argn; /* return index of next arg (file name) */ +} + + +/* + * The main program. + */ + +int +main (int argc, char **argv) +{ + struct jpeg_decompress_struct srcinfo; + struct jpeg_compress_struct dstinfo; + struct jpeg_error_mgr jsrcerr, jdsterr; +#ifdef PROGRESS_REPORT + struct cdjpeg_progress_mgr progress; +#endif + jvirt_barray_ptr * src_coef_arrays; + jvirt_barray_ptr * dst_coef_arrays; + int file_index; + FILE * input_file; + FILE * output_file; + + /* On Mac, fetch a command line. */ +#ifdef USE_CCOMMAND + argc = ccommand(&argv); +#endif + + progname = argv[0]; + if (progname == NULL || progname[0] == 0) + progname = "jpegtran"; /* in case C library doesn't provide it */ + + /* Initialize the JPEG decompression object with default error handling. */ + srcinfo.err = jpeg_std_error(&jsrcerr); + jpeg_create_decompress(&srcinfo); + /* Initialize the JPEG compression object with default error handling. */ + dstinfo.err = jpeg_std_error(&jdsterr); + jpeg_create_compress(&dstinfo); + + /* Now safe to enable signal catcher. + * Note: we assume only the decompression object will have virtual arrays. + */ +#ifdef NEED_SIGNAL_CATCHER + enable_signal_catcher((j_common_ptr) &srcinfo); +#endif + + /* Scan command line to find file names. + * It is convenient to use just one switch-parsing routine, but the switch + * values read here are mostly ignored; we will rescan the switches after + * opening the input file. Also note that most of the switches affect the + * destination JPEG object, so we parse into that and then copy over what + * needs to affects the source too. + */ + + file_index = parse_switches(&dstinfo, argc, argv, 0, FALSE); + jsrcerr.trace_level = jdsterr.trace_level; + srcinfo.mem->max_memory_to_use = dstinfo.mem->max_memory_to_use; + +#ifdef TWO_FILE_COMMANDLINE + /* Must have either -outfile switch or explicit output file name */ + if (outfilename == NULL) { + if (file_index != argc-2) { + fprintf(stderr, "%s: must name one input and one output file\n", + progname); + usage(); + } + outfilename = argv[file_index+1]; + } else { + if (file_index != argc-1) { + fprintf(stderr, "%s: must name one input and one output file\n", + progname); + usage(); + } + } +#else + /* Unix style: expect zero or one file name */ + if (file_index < argc-1) { + fprintf(stderr, "%s: only one input file\n", progname); + usage(); + } +#endif /* TWO_FILE_COMMANDLINE */ + + /* Open the input file. */ + if (file_index < argc) { + if ((input_file = fopen(argv[file_index], READ_BINARY)) == NULL) { + fprintf(stderr, "%s: can't open %s\n", progname, argv[file_index]); + exit(EXIT_FAILURE); + } + } else { + /* default input file is stdin */ + input_file = read_stdin(); + } + + /* Open the output file. */ + if (outfilename != NULL) { + if ((output_file = fopen(outfilename, WRITE_BINARY)) == NULL) { + fprintf(stderr, "%s: can't open %s\n", progname, outfilename); + exit(EXIT_FAILURE); + } + } else { + /* default output file is stdout */ + output_file = write_stdout(); + } + +#ifdef PROGRESS_REPORT + start_progress_monitor((j_common_ptr) &dstinfo, &progress); +#endif + + /* Specify data source for decompression */ + jpeg_stdio_src(&srcinfo, input_file); + + /* Enable saving of extra markers that we want to copy */ + jcopy_markers_setup(&srcinfo, copyoption); + + /* Read file header */ + (void) jpeg_read_header(&srcinfo, TRUE); + + /* Any space needed by a transform option must be requested before + * jpeg_read_coefficients so that memory allocation will be done right. + */ +#if TRANSFORMS_SUPPORTED + jtransform_request_workspace(&srcinfo, &transformoption); +#endif + + /* Read source file as DCT coefficients */ + src_coef_arrays = jpeg_read_coefficients(&srcinfo); + + /* Initialize destination compression parameters from source values */ + jpeg_copy_critical_parameters(&srcinfo, &dstinfo); + + /* Adjust destination parameters if required by transform options; + * also find out which set of coefficient arrays will hold the output. + */ +#if TRANSFORMS_SUPPORTED + dst_coef_arrays = jtransform_adjust_parameters(&srcinfo, &dstinfo, + src_coef_arrays, + &transformoption); +#else + dst_coef_arrays = src_coef_arrays; +#endif + + /* Adjust default compression parameters by re-parsing the options */ + file_index = parse_switches(&dstinfo, argc, argv, 0, TRUE); + + /* Specify data destination for compression */ + jpeg_stdio_dest(&dstinfo, output_file); + + /* Start compressor (note no image data is actually written here) */ + jpeg_write_coefficients(&dstinfo, dst_coef_arrays); + + /* Copy to the output file any extra markers that we want to preserve */ + jcopy_markers_execute(&srcinfo, &dstinfo, copyoption); + + /* Execute image transformation, if any */ +#if TRANSFORMS_SUPPORTED + jtransform_execute_transformation(&srcinfo, &dstinfo, + src_coef_arrays, + &transformoption); +#endif + + /* Finish compression and release memory */ + jpeg_finish_compress(&dstinfo); + jpeg_destroy_compress(&dstinfo); + (void) jpeg_finish_decompress(&srcinfo); + jpeg_destroy_decompress(&srcinfo); + + /* Close files, if we opened them */ + if (input_file != stdin) + fclose(input_file); + if (output_file != stdout) + fclose(output_file); + +#ifdef PROGRESS_REPORT + end_progress_monitor((j_common_ptr) &dstinfo); +#endif + + /* All done. */ + exit(jsrcerr.num_warnings + jdsterr.num_warnings ?EXIT_WARNING:EXIT_SUCCESS); + return 0; /* suppress no-return-value warnings */ +} +#endif \ No newline at end of file diff --git a/Engine/lib/ljpeg/jquant1.c b/Engine/lib/ljpeg/jquant1.c new file mode 100644 index 000000000..b2f96aa15 --- /dev/null +++ b/Engine/lib/ljpeg/jquant1.c @@ -0,0 +1,856 @@ +/* + * jquant1.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains 1-pass color quantization (color mapping) routines. + * These routines provide mapping to a fixed color map using equally spaced + * color values. Optional Floyd-Steinberg or ordered dithering is available. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + +#ifdef QUANT_1PASS_SUPPORTED + + +/* + * The main purpose of 1-pass quantization is to provide a fast, if not very + * high quality, colormapped output capability. A 2-pass quantizer usually + * gives better visual quality; however, for quantized grayscale output this + * quantizer is perfectly adequate. Dithering is highly recommended with this + * quantizer, though you can turn it off if you really want to. + * + * In 1-pass quantization the colormap must be chosen in advance of seeing the + * image. We use a map consisting of all combinations of Ncolors[i] color + * values for the i'th component. The Ncolors[] values are chosen so that + * their product, the total number of colors, is no more than that requested. + * (In most cases, the product will be somewhat less.) + * + * Since the colormap is orthogonal, the representative value for each color + * component can be determined without considering the other components; + * then these indexes can be combined into a colormap index by a standard + * N-dimensional-array-subscript calculation. Most of the arithmetic involved + * can be precalculated and stored in the lookup table colorindex[]. + * colorindex[i][j] maps pixel value j in component i to the nearest + * representative value (grid plane) for that component; this index is + * multiplied by the array stride for component i, so that the + * index of the colormap entry closest to a given pixel value is just + * sum( colorindex[component-number][pixel-component-value] ) + * Aside from being fast, this scheme allows for variable spacing between + * representative values with no additional lookup cost. + * + * If gamma correction has been applied in color conversion, it might be wise + * to adjust the color grid spacing so that the representative colors are + * equidistant in linear space. At this writing, gamma correction is not + * implemented by jdcolor, so nothing is done here. + */ + + +/* Declarations for ordered dithering. + * + * We use a standard 16x16 ordered dither array. The basic concept of ordered + * dithering is described in many references, for instance Dale Schumacher's + * chapter II.2 of Graphics Gems II (James Arvo, ed. Academic Press, 1991). + * In place of Schumacher's comparisons against a "threshold" value, we add a + * "dither" value to the input pixel and then round the result to the nearest + * output value. The dither value is equivalent to (0.5 - threshold) times + * the distance between output values. For ordered dithering, we assume that + * the output colors are equally spaced; if not, results will probably be + * worse, since the dither may be too much or too little at a given point. + * + * The normal calculation would be to form pixel value + dither, range-limit + * this to 0..MAXJSAMPLE, and then index into the colorindex table as usual. + * We can skip the separate range-limiting step by extending the colorindex + * table in both directions. + */ + +#define ODITHER_SIZE 16 /* dimension of dither matrix */ +/* NB: if ODITHER_SIZE is not a power of 2, ODITHER_MASK uses will break */ +#define ODITHER_CELLS (ODITHER_SIZE*ODITHER_SIZE) /* # cells in matrix */ +#define ODITHER_MASK (ODITHER_SIZE-1) /* mask for wrapping around counters */ + +typedef int ODITHER_MATRIX[ODITHER_SIZE][ODITHER_SIZE]; +typedef int (*ODITHER_MATRIX_PTR)[ODITHER_SIZE]; + +static const UINT8 base_dither_matrix[ODITHER_SIZE][ODITHER_SIZE] = { + /* Bayer's order-4 dither array. Generated by the code given in + * Stephen Hawley's article "Ordered Dithering" in Graphics Gems I. + * The values in this array must range from 0 to ODITHER_CELLS-1. + */ + { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 }, + { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 }, + { 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 }, + { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 }, + { 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 }, + { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 }, + { 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 }, + { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 }, + { 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 }, + { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 }, + { 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 }, + { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 }, + { 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 }, + { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 }, + { 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 }, + { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 } +}; + + +/* Declarations for Floyd-Steinberg dithering. + * + * Errors are accumulated into the array fserrors[], at a resolution of + * 1/16th of a pixel count. The error at a given pixel is propagated + * to its not-yet-processed neighbors using the standard F-S fractions, + * ... (here) 7/16 + * 3/16 5/16 1/16 + * We work left-to-right on even rows, right-to-left on odd rows. + * + * We can get away with a single array (holding one row's worth of errors) + * by using it to store the current row's errors at pixel columns not yet + * processed, but the next row's errors at columns already processed. We + * need only a few extra variables to hold the errors immediately around the + * current column. (If we are lucky, those variables are in registers, but + * even if not, they're probably cheaper to access than array elements are.) + * + * The fserrors[] array is indexed [component#][position]. + * We provide (#columns + 2) entries per component; the extra entry at each + * end saves us from special-casing the first and last pixels. + * + * Note: on a wide image, we might not have enough room in a PC's near data + * segment to hold the error array; so it is allocated with alloc_large. + */ + +#if BITS_IN_JSAMPLE == 8 +typedef INT16 FSERROR; /* 16 bits should be enough */ +typedef int LOCFSERROR; /* use 'int' for calculation temps */ +#else +typedef INT32 FSERROR; /* may need more than 16 bits */ +typedef INT32 LOCFSERROR; /* be sure calculation temps are big enough */ +#endif + +typedef FSERROR FAR *FSERRPTR; /* pointer to error array (in FAR storage!) */ + + +/* Private subobject */ + +#define MAX_Q_COMPS 4 /* max components I can handle */ + +typedef struct { + struct jpeg_color_quantizer pub; /* public fields */ + + /* Initially allocated colormap is saved here */ + JSAMPARRAY sv_colormap; /* The color map as a 2-D pixel array */ + int sv_actual; /* number of entries in use */ + + JSAMPARRAY colorindex; /* Precomputed mapping for speed */ + /* colorindex[i][j] = index of color closest to pixel value j in component i, + * premultiplied as described above. Since colormap indexes must fit into + * JSAMPLEs, the entries of this array will too. + */ + boolean is_padded; /* is the colorindex padded for odither? */ + + int Ncolors[MAX_Q_COMPS]; /* # of values alloced to each component */ + + /* Variables for ordered dithering */ + int row_index; /* cur row's vertical index in dither matrix */ + ODITHER_MATRIX_PTR odither[MAX_Q_COMPS]; /* one dither array per component */ + + /* Variables for Floyd-Steinberg dithering */ + FSERRPTR fserrors[MAX_Q_COMPS]; /* accumulated errors */ + boolean on_odd_row; /* flag to remember which row we are on */ +} my_cquantizer; + +typedef my_cquantizer * my_cquantize_ptr; + + +/* + * Policy-making subroutines for create_colormap and create_colorindex. + * These routines determine the colormap to be used. The rest of the module + * only assumes that the colormap is orthogonal. + * + * * select_ncolors decides how to divvy up the available colors + * among the components. + * * output_value defines the set of representative values for a component. + * * largest_input_value defines the mapping from input values to + * representative values for a component. + * Note that the latter two routines may impose different policies for + * different components, though this is not currently done. + */ + + +LOCAL(int) +select_ncolors (j_decompress_ptr cinfo, int Ncolors[]) +/* Determine allocation of desired colors to components, */ +/* and fill in Ncolors[] array to indicate choice. */ +/* Return value is total number of colors (product of Ncolors[] values). */ +{ + int nc = cinfo->out_color_components; /* number of color components */ + int max_colors = cinfo->desired_number_of_colors; + int total_colors, iroot, i, j; + boolean changed; + long temp; + static const int RGB_order[3] = { RGB_GREEN, RGB_RED, RGB_BLUE }; + + /* We can allocate at least the nc'th root of max_colors per component. */ + /* Compute floor(nc'th root of max_colors). */ + iroot = 1; + do { + iroot++; + temp = iroot; /* set temp = iroot ** nc */ + for (i = 1; i < nc; i++) + temp *= iroot; + } while (temp <= (long) max_colors); /* repeat till iroot exceeds root */ + iroot--; /* now iroot = floor(root) */ + + /* Must have at least 2 color values per component */ + if (iroot < 2) + ERREXIT1(cinfo, JERR_QUANT_FEW_COLORS, (int) temp); + + /* Initialize to iroot color values for each component */ + total_colors = 1; + for (i = 0; i < nc; i++) { + Ncolors[i] = iroot; + total_colors *= iroot; + } + /* We may be able to increment the count for one or more components without + * exceeding max_colors, though we know not all can be incremented. + * Sometimes, the first component can be incremented more than once! + * (Example: for 16 colors, we start at 2*2*2, go to 3*2*2, then 4*2*2.) + * In RGB colorspace, try to increment G first, then R, then B. + */ + do { + changed = FALSE; + for (i = 0; i < nc; i++) { + j = (cinfo->out_color_space == JCS_RGB ? RGB_order[i] : i); + /* calculate new total_colors if Ncolors[j] is incremented */ + temp = total_colors / Ncolors[j]; + temp *= Ncolors[j]+1; /* done in long arith to avoid oflo */ + if (temp > (long) max_colors) + break; /* won't fit, done with this pass */ + Ncolors[j]++; /* OK, apply the increment */ + total_colors = (int) temp; + changed = TRUE; + } + } while (changed); + + return total_colors; +} + + +LOCAL(int) +output_value (j_decompress_ptr cinfo, int ci, int j, int maxj) +/* Return j'th output value, where j will range from 0 to maxj */ +/* The output values must fall in 0..MAXJSAMPLE in increasing order */ +{ + /* We always provide values 0 and MAXJSAMPLE for each component; + * any additional values are equally spaced between these limits. + * (Forcing the upper and lower values to the limits ensures that + * dithering can't produce a color outside the selected gamut.) + */ + return (int) (((INT32) j * MAXJSAMPLE + maxj/2) / maxj); +} + + +LOCAL(int) +largest_input_value (j_decompress_ptr cinfo, int ci, int j, int maxj) +/* Return largest input value that should map to j'th output value */ +/* Must have largest(j=0) >= 0, and largest(j=maxj) >= MAXJSAMPLE */ +{ + /* Breakpoints are halfway between values returned by output_value */ + return (int) (((INT32) (2*j + 1) * MAXJSAMPLE + maxj) / (2*maxj)); +} + + +/* + * Create the colormap. + */ + +LOCAL(void) +create_colormap (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + JSAMPARRAY colormap; /* Created colormap */ + int total_colors; /* Number of distinct output colors */ + int i,j,k, nci, blksize, blkdist, ptr, val; + + /* Select number of colors for each component */ + total_colors = select_ncolors(cinfo, cquantize->Ncolors); + + /* Report selected color counts */ + if (cinfo->out_color_components == 3) + TRACEMS4(cinfo, 1, JTRC_QUANT_3_NCOLORS, + total_colors, cquantize->Ncolors[0], + cquantize->Ncolors[1], cquantize->Ncolors[2]); + else + TRACEMS1(cinfo, 1, JTRC_QUANT_NCOLORS, total_colors); + + /* Allocate and fill in the colormap. */ + /* The colors are ordered in the map in standard row-major order, */ + /* i.e. rightmost (highest-indexed) color changes most rapidly. */ + + colormap = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) total_colors, (JDIMENSION) cinfo->out_color_components); + + /* blksize is number of adjacent repeated entries for a component */ + /* blkdist is distance between groups of identical entries for a component */ + blkdist = total_colors; + + for (i = 0; i < cinfo->out_color_components; i++) { + /* fill in colormap entries for i'th color component */ + nci = cquantize->Ncolors[i]; /* # of distinct values for this color */ + blksize = blkdist / nci; + for (j = 0; j < nci; j++) { + /* Compute j'th output value (out of nci) for component */ + val = output_value(cinfo, i, j, nci-1); + /* Fill in all colormap entries that have this value of this component */ + for (ptr = j * blksize; ptr < total_colors; ptr += blkdist) { + /* fill in blksize entries beginning at ptr */ + for (k = 0; k < blksize; k++) + colormap[i][ptr+k] = (JSAMPLE) val; + } + } + blkdist = blksize; /* blksize of this color is blkdist of next */ + } + + /* Save the colormap in private storage, + * where it will survive color quantization mode changes. + */ + cquantize->sv_colormap = colormap; + cquantize->sv_actual = total_colors; +} + + +/* + * Create the color index table. + */ + +LOCAL(void) +create_colorindex (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + JSAMPROW indexptr; + int i,j,k, nci, blksize, val, pad; + + /* For ordered dither, we pad the color index tables by MAXJSAMPLE in + * each direction (input index values can be -MAXJSAMPLE .. 2*MAXJSAMPLE). + * This is not necessary in the other dithering modes. However, we + * flag whether it was done in case user changes dithering mode. + */ + if (cinfo->dither_mode == JDITHER_ORDERED) { + pad = MAXJSAMPLE*2; + cquantize->is_padded = TRUE; + } else { + pad = 0; + cquantize->is_padded = FALSE; + } + + cquantize->colorindex = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) (MAXJSAMPLE+1 + pad), + (JDIMENSION) cinfo->out_color_components); + + /* blksize is number of adjacent repeated entries for a component */ + blksize = cquantize->sv_actual; + + for (i = 0; i < cinfo->out_color_components; i++) { + /* fill in colorindex entries for i'th color component */ + nci = cquantize->Ncolors[i]; /* # of distinct values for this color */ + blksize = blksize / nci; + + /* adjust colorindex pointers to provide padding at negative indexes. */ + if (pad) + cquantize->colorindex[i] += MAXJSAMPLE; + + /* in loop, val = index of current output value, */ + /* and k = largest j that maps to current val */ + indexptr = cquantize->colorindex[i]; + val = 0; + k = largest_input_value(cinfo, i, 0, nci-1); + for (j = 0; j <= MAXJSAMPLE; j++) { + while (j > k) /* advance val if past boundary */ + k = largest_input_value(cinfo, i, ++val, nci-1); + /* premultiply so that no multiplication needed in main processing */ + indexptr[j] = (JSAMPLE) (val * blksize); + } + /* Pad at both ends if necessary */ + if (pad) + for (j = 1; j <= MAXJSAMPLE; j++) { + indexptr[-j] = indexptr[0]; + indexptr[MAXJSAMPLE+j] = indexptr[MAXJSAMPLE]; + } + } +} + + +/* + * Create an ordered-dither array for a component having ncolors + * distinct output values. + */ + +LOCAL(ODITHER_MATRIX_PTR) +make_odither_array (j_decompress_ptr cinfo, int ncolors) +{ + ODITHER_MATRIX_PTR odither; + int j,k; + INT32 num,den; + + odither = (ODITHER_MATRIX_PTR) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(ODITHER_MATRIX)); + /* The inter-value distance for this color is MAXJSAMPLE/(ncolors-1). + * Hence the dither value for the matrix cell with fill order f + * (f=0..N-1) should be (N-1-2*f)/(2*N) * MAXJSAMPLE/(ncolors-1). + * On 16-bit-int machine, be careful to avoid overflow. + */ + den = 2 * ODITHER_CELLS * ((INT32) (ncolors - 1)); + for (j = 0; j < ODITHER_SIZE; j++) { + for (k = 0; k < ODITHER_SIZE; k++) { + num = ((INT32) (ODITHER_CELLS-1 - 2*((int)base_dither_matrix[j][k]))) + * MAXJSAMPLE; + /* Ensure round towards zero despite C's lack of consistency + * about rounding negative values in integer division... + */ + odither[j][k] = (int) (num<0 ? -((-num)/den) : num/den); + } + } + return odither; +} + + +/* + * Create the ordered-dither tables. + * Components having the same number of representative colors may + * share a dither table. + */ + +LOCAL(void) +create_odither_tables (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + ODITHER_MATRIX_PTR odither; + int i, j, nci; + + for (i = 0; i < cinfo->out_color_components; i++) { + nci = cquantize->Ncolors[i]; /* # of distinct values for this color */ + odither = NULL; /* search for matching prior component */ + for (j = 0; j < i; j++) { + if (nci == cquantize->Ncolors[j]) { + odither = cquantize->odither[j]; + break; + } + } + if (odither == NULL) /* need a new table? */ + odither = make_odither_array(cinfo, nci); + cquantize->odither[i] = odither; + } +} + + +/* + * Map some rows of pixels to the output colormapped representation. + */ + +METHODDEF(void) +color_quantize (j_decompress_ptr cinfo, JSAMPARRAY input_buf, + JSAMPARRAY output_buf, int num_rows) +/* General case, no dithering */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + JSAMPARRAY colorindex = cquantize->colorindex; + register int pixcode, ci; + register JSAMPROW ptrin, ptrout; + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + register int nc = cinfo->out_color_components; + + for (row = 0; row < num_rows; row++) { + ptrin = input_buf[row]; + ptrout = output_buf[row]; + for (col = width; col > 0; col--) { + pixcode = 0; + for (ci = 0; ci < nc; ci++) { + pixcode += GETJSAMPLE(colorindex[ci][GETJSAMPLE(*ptrin++)]); + } + *ptrout++ = (JSAMPLE) pixcode; + } + } +} + + +METHODDEF(void) +color_quantize3 (j_decompress_ptr cinfo, JSAMPARRAY input_buf, + JSAMPARRAY output_buf, int num_rows) +/* Fast path for out_color_components==3, no dithering */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + register int pixcode; + register JSAMPROW ptrin, ptrout; + JSAMPROW colorindex0 = cquantize->colorindex[0]; + JSAMPROW colorindex1 = cquantize->colorindex[1]; + JSAMPROW colorindex2 = cquantize->colorindex[2]; + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + + for (row = 0; row < num_rows; row++) { + ptrin = input_buf[row]; + ptrout = output_buf[row]; + for (col = width; col > 0; col--) { + pixcode = GETJSAMPLE(colorindex0[GETJSAMPLE(*ptrin++)]); + pixcode += GETJSAMPLE(colorindex1[GETJSAMPLE(*ptrin++)]); + pixcode += GETJSAMPLE(colorindex2[GETJSAMPLE(*ptrin++)]); + *ptrout++ = (JSAMPLE) pixcode; + } + } +} + + +METHODDEF(void) +quantize_ord_dither (j_decompress_ptr cinfo, JSAMPARRAY input_buf, + JSAMPARRAY output_buf, int num_rows) +/* General case, with ordered dithering */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + register JSAMPROW input_ptr; + register JSAMPROW output_ptr; + JSAMPROW colorindex_ci; + int * dither; /* points to active row of dither matrix */ + int row_index, col_index; /* current indexes into dither matrix */ + int nc = cinfo->out_color_components; + int ci; + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + + for (row = 0; row < num_rows; row++) { + /* Initialize output values to 0 so can process components separately */ + jzero_far((void FAR *) output_buf[row], + (size_t) (width * SIZEOF(JSAMPLE))); + row_index = cquantize->row_index; + for (ci = 0; ci < nc; ci++) { + input_ptr = input_buf[row] + ci; + output_ptr = output_buf[row]; + colorindex_ci = cquantize->colorindex[ci]; + dither = cquantize->odither[ci][row_index]; + col_index = 0; + + for (col = width; col > 0; col--) { + /* Form pixel value + dither, range-limit to 0..MAXJSAMPLE, + * select output value, accumulate into output code for this pixel. + * Range-limiting need not be done explicitly, as we have extended + * the colorindex table to produce the right answers for out-of-range + * inputs. The maximum dither is +- MAXJSAMPLE; this sets the + * required amount of padding. + */ + *output_ptr += colorindex_ci[GETJSAMPLE(*input_ptr)+dither[col_index]]; + input_ptr += nc; + output_ptr++; + col_index = (col_index + 1) & ODITHER_MASK; + } + } + /* Advance row index for next row */ + row_index = (row_index + 1) & ODITHER_MASK; + cquantize->row_index = row_index; + } +} + + +METHODDEF(void) +quantize3_ord_dither (j_decompress_ptr cinfo, JSAMPARRAY input_buf, + JSAMPARRAY output_buf, int num_rows) +/* Fast path for out_color_components==3, with ordered dithering */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + register int pixcode; + register JSAMPROW input_ptr; + register JSAMPROW output_ptr; + JSAMPROW colorindex0 = cquantize->colorindex[0]; + JSAMPROW colorindex1 = cquantize->colorindex[1]; + JSAMPROW colorindex2 = cquantize->colorindex[2]; + int * dither0; /* points to active row of dither matrix */ + int * dither1; + int * dither2; + int row_index, col_index; /* current indexes into dither matrix */ + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + + for (row = 0; row < num_rows; row++) { + row_index = cquantize->row_index; + input_ptr = input_buf[row]; + output_ptr = output_buf[row]; + dither0 = cquantize->odither[0][row_index]; + dither1 = cquantize->odither[1][row_index]; + dither2 = cquantize->odither[2][row_index]; + col_index = 0; + + for (col = width; col > 0; col--) { + pixcode = GETJSAMPLE(colorindex0[GETJSAMPLE(*input_ptr++) + + dither0[col_index]]); + pixcode += GETJSAMPLE(colorindex1[GETJSAMPLE(*input_ptr++) + + dither1[col_index]]); + pixcode += GETJSAMPLE(colorindex2[GETJSAMPLE(*input_ptr++) + + dither2[col_index]]); + *output_ptr++ = (JSAMPLE) pixcode; + col_index = (col_index + 1) & ODITHER_MASK; + } + row_index = (row_index + 1) & ODITHER_MASK; + cquantize->row_index = row_index; + } +} + + +METHODDEF(void) +quantize_fs_dither (j_decompress_ptr cinfo, JSAMPARRAY input_buf, + JSAMPARRAY output_buf, int num_rows) +/* General case, with Floyd-Steinberg dithering */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + register LOCFSERROR cur; /* current error or pixel value */ + LOCFSERROR belowerr; /* error for pixel below cur */ + LOCFSERROR bpreverr; /* error for below/prev col */ + LOCFSERROR bnexterr; /* error for below/next col */ + LOCFSERROR delta; + register FSERRPTR errorptr; /* => fserrors[] at column before current */ + register JSAMPROW input_ptr; + register JSAMPROW output_ptr; + JSAMPROW colorindex_ci; + JSAMPROW colormap_ci; + int pixcode; + int nc = cinfo->out_color_components; + int dir; /* 1 for left-to-right, -1 for right-to-left */ + int dirnc; /* dir * nc */ + int ci; + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + JSAMPLE *range_limit = cinfo->sample_range_limit; + SHIFT_TEMPS + + for (row = 0; row < num_rows; row++) { + /* Initialize output values to 0 so can process components separately */ + jzero_far((void FAR *) output_buf[row], + (size_t) (width * SIZEOF(JSAMPLE))); + for (ci = 0; ci < nc; ci++) { + input_ptr = input_buf[row] + ci; + output_ptr = output_buf[row]; + if (cquantize->on_odd_row) { + /* work right to left in this row */ + input_ptr += (width-1) * nc; /* so point to rightmost pixel */ + output_ptr += width-1; + dir = -1; + dirnc = -nc; + errorptr = cquantize->fserrors[ci] + (width+1); /* => entry after last column */ + } else { + /* work left to right in this row */ + dir = 1; + dirnc = nc; + errorptr = cquantize->fserrors[ci]; /* => entry before first column */ + } + colorindex_ci = cquantize->colorindex[ci]; + colormap_ci = cquantize->sv_colormap[ci]; + /* Preset error values: no error propagated to first pixel from left */ + cur = 0; + /* and no error propagated to row below yet */ + belowerr = bpreverr = 0; + + for (col = width; col > 0; col--) { + /* cur holds the error propagated from the previous pixel on the + * current line. Add the error propagated from the previous line + * to form the complete error correction term for this pixel, and + * round the error term (which is expressed * 16) to an integer. + * RIGHT_SHIFT rounds towards minus infinity, so adding 8 is correct + * for either sign of the error value. + * Note: errorptr points to *previous* column's array entry. + */ + cur = RIGHT_SHIFT(cur + errorptr[dir] + 8, 4); + /* Form pixel value + error, and range-limit to 0..MAXJSAMPLE. + * The maximum error is +- MAXJSAMPLE; this sets the required size + * of the range_limit array. + */ + cur += GETJSAMPLE(*input_ptr); + cur = GETJSAMPLE(range_limit[cur]); + /* Select output value, accumulate into output code for this pixel */ + pixcode = GETJSAMPLE(colorindex_ci[cur]); + *output_ptr += (JSAMPLE) pixcode; + /* Compute actual representation error at this pixel */ + /* Note: we can do this even though we don't have the final */ + /* pixel code, because the colormap is orthogonal. */ + cur -= GETJSAMPLE(colormap_ci[pixcode]); + /* Compute error fractions to be propagated to adjacent pixels. + * Add these into the running sums, and simultaneously shift the + * next-line error sums left by 1 column. + */ + bnexterr = cur; + delta = cur * 2; + cur += delta; /* form error * 3 */ + errorptr[0] = (FSERROR) (bpreverr + cur); + cur += delta; /* form error * 5 */ + bpreverr = belowerr + cur; + belowerr = bnexterr; + cur += delta; /* form error * 7 */ + /* At this point cur contains the 7/16 error value to be propagated + * to the next pixel on the current line, and all the errors for the + * next line have been shifted over. We are therefore ready to move on. + */ + input_ptr += dirnc; /* advance input ptr to next column */ + output_ptr += dir; /* advance output ptr to next column */ + errorptr += dir; /* advance errorptr to current column */ + } + /* Post-loop cleanup: we must unload the final error value into the + * final fserrors[] entry. Note we need not unload belowerr because + * it is for the dummy column before or after the actual array. + */ + errorptr[0] = (FSERROR) bpreverr; /* unload prev err into array */ + } + cquantize->on_odd_row = (cquantize->on_odd_row ? FALSE : TRUE); + } +} + + +/* + * Allocate workspace for Floyd-Steinberg errors. + */ + +LOCAL(void) +alloc_fs_workspace (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + size_t arraysize; + int i; + + arraysize = (size_t) ((cinfo->output_width + 2) * SIZEOF(FSERROR)); + for (i = 0; i < cinfo->out_color_components; i++) { + cquantize->fserrors[i] = (FSERRPTR) + (*cinfo->mem->alloc_large)((j_common_ptr) cinfo, JPOOL_IMAGE, arraysize); + } +} + + +/* + * Initialize for one-pass color quantization. + */ + +METHODDEF(void) +start_pass_1_quant (j_decompress_ptr cinfo, boolean is_pre_scan) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + size_t arraysize; + int i; + + /* Install my colormap. */ + cinfo->colormap = cquantize->sv_colormap; + cinfo->actual_number_of_colors = cquantize->sv_actual; + + /* Initialize for desired dithering mode. */ + switch (cinfo->dither_mode) { + case JDITHER_NONE: + if (cinfo->out_color_components == 3) + cquantize->pub.color_quantize = color_quantize3; + else + cquantize->pub.color_quantize = color_quantize; + break; + case JDITHER_ORDERED: + if (cinfo->out_color_components == 3) + cquantize->pub.color_quantize = quantize3_ord_dither; + else + cquantize->pub.color_quantize = quantize_ord_dither; + cquantize->row_index = 0; /* initialize state for ordered dither */ + /* If user changed to ordered dither from another mode, + * we must recreate the color index table with padding. + * This will cost extra space, but probably isn't very likely. + */ + if (! cquantize->is_padded) + create_colorindex(cinfo); + /* Create ordered-dither tables if we didn't already. */ + if (cquantize->odither[0] == NULL) + create_odither_tables(cinfo); + break; + case JDITHER_FS: + cquantize->pub.color_quantize = quantize_fs_dither; + cquantize->on_odd_row = FALSE; /* initialize state for F-S dither */ + /* Allocate Floyd-Steinberg workspace if didn't already. */ + if (cquantize->fserrors[0] == NULL) + alloc_fs_workspace(cinfo); + /* Initialize the propagated errors to zero. */ + arraysize = (size_t) ((cinfo->output_width + 2) * SIZEOF(FSERROR)); + for (i = 0; i < cinfo->out_color_components; i++) + jzero_far((void FAR *) cquantize->fserrors[i], arraysize); + break; + default: + ERREXIT(cinfo, JERR_NOT_COMPILED); + break; + } +} + + +/* + * Finish up at the end of the pass. + */ + +METHODDEF(void) +finish_pass_1_quant (j_decompress_ptr cinfo) +{ + /* no work in 1-pass case */ +} + + +/* + * Switch to a new external colormap between output passes. + * Shouldn't get to this module! + */ + +METHODDEF(void) +new_color_map_1_quant (j_decompress_ptr cinfo) +{ + ERREXIT(cinfo, JERR_MODE_CHANGE); +} + + +/* + * Module initialization routine for 1-pass color quantization. + */ + +GLOBAL(void) +jinit_1pass_quantizer (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize; + + cquantize = (my_cquantize_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_cquantizer)); + cinfo->cquantize = (struct jpeg_color_quantizer *) cquantize; + cquantize->pub.start_pass = start_pass_1_quant; + cquantize->pub.finish_pass = finish_pass_1_quant; + cquantize->pub.new_color_map = new_color_map_1_quant; + cquantize->fserrors[0] = NULL; /* Flag FS workspace not allocated */ + cquantize->odither[0] = NULL; /* Also flag odither arrays not allocated */ + + /* Make sure my internal arrays won't overflow */ + if (cinfo->out_color_components > MAX_Q_COMPS) + ERREXIT1(cinfo, JERR_QUANT_COMPONENTS, MAX_Q_COMPS); + /* Make sure colormap indexes can be represented by JSAMPLEs */ + if (cinfo->desired_number_of_colors > (MAXJSAMPLE+1)) + ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, MAXJSAMPLE+1); + + /* Create the colormap and color index table. */ + create_colormap(cinfo); + create_colorindex(cinfo); + + /* Allocate Floyd-Steinberg workspace now if requested. + * We do this now since it is FAR storage and may affect the memory + * manager's space calculations. If the user changes to FS dither + * mode in a later pass, we will allocate the space then, and will + * possibly overrun the max_memory_to_use setting. + */ + if (cinfo->dither_mode == JDITHER_FS) + alloc_fs_workspace(cinfo); +} + +#endif /* QUANT_1PASS_SUPPORTED */ diff --git a/Engine/lib/ljpeg/jquant2.c b/Engine/lib/ljpeg/jquant2.c new file mode 100644 index 000000000..af601e334 --- /dev/null +++ b/Engine/lib/ljpeg/jquant2.c @@ -0,0 +1,1310 @@ +/* + * jquant2.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains 2-pass color quantization (color mapping) routines. + * These routines provide selection of a custom color map for an image, + * followed by mapping of the image to that color map, with optional + * Floyd-Steinberg dithering. + * It is also possible to use just the second pass to map to an arbitrary + * externally-given color map. + * + * Note: ordered dithering is not supported, since there isn't any fast + * way to compute intercolor distances; it's unclear that ordered dither's + * fundamental assumptions even hold with an irregularly spaced color map. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + +#ifdef QUANT_2PASS_SUPPORTED + + +/* + * This module implements the well-known Heckbert paradigm for color + * quantization. Most of the ideas used here can be traced back to + * Heckbert's seminal paper + * Heckbert, Paul. "Color Image Quantization for Frame Buffer Display", + * Proc. SIGGRAPH '82, Computer Graphics v.16 #3 (July 1982), pp 297-304. + * + * In the first pass over the image, we accumulate a histogram showing the + * usage count of each possible color. To keep the histogram to a reasonable + * size, we reduce the precision of the input; typical practice is to retain + * 5 or 6 bits per color, so that 8 or 4 different input values are counted + * in the same histogram cell. + * + * Next, the color-selection step begins with a box representing the whole + * color space, and repeatedly splits the "largest" remaining box until we + * have as many boxes as desired colors. Then the mean color in each + * remaining box becomes one of the possible output colors. + * + * The second pass over the image maps each input pixel to the closest output + * color (optionally after applying a Floyd-Steinberg dithering correction). + * This mapping is logically trivial, but making it go fast enough requires + * considerable care. + * + * Heckbert-style quantizers vary a good deal in their policies for choosing + * the "largest" box and deciding where to cut it. The particular policies + * used here have proved out well in experimental comparisons, but better ones + * may yet be found. + * + * In earlier versions of the IJG code, this module quantized in YCbCr color + * space, processing the raw upsampled data without a color conversion step. + * This allowed the color conversion math to be done only once per colormap + * entry, not once per pixel. However, that optimization precluded other + * useful optimizations (such as merging color conversion with upsampling) + * and it also interfered with desired capabilities such as quantizing to an + * externally-supplied colormap. We have therefore abandoned that approach. + * The present code works in the post-conversion color space, typically RGB. + * + * To improve the visual quality of the results, we actually work in scaled + * RGB space, giving G distances more weight than R, and R in turn more than + * B. To do everything in integer math, we must use integer scale factors. + * The 2/3/1 scale factors used here correspond loosely to the relative + * weights of the colors in the NTSC grayscale equation. + * If you want to use this code to quantize a non-RGB color space, you'll + * probably need to change these scale factors. + */ + +#define R_SCALE 2 /* scale R distances by this much */ +#define G_SCALE 3 /* scale G distances by this much */ +#define B_SCALE 1 /* and B by this much */ + +/* Relabel R/G/B as components 0/1/2, respecting the RGB ordering defined + * in jmorecfg.h. As the code stands, it will do the right thing for R,G,B + * and B,G,R orders. If you define some other weird order in jmorecfg.h, + * you'll get compile errors until you extend this logic. In that case + * you'll probably want to tweak the histogram sizes too. + */ + +#if RGB_RED == 0 +#define C0_SCALE R_SCALE +#endif +#if RGB_BLUE == 0 +#define C0_SCALE B_SCALE +#endif +#if RGB_GREEN == 1 +#define C1_SCALE G_SCALE +#endif +#if RGB_RED == 2 +#define C2_SCALE R_SCALE +#endif +#if RGB_BLUE == 2 +#define C2_SCALE B_SCALE +#endif + + +/* + * First we have the histogram data structure and routines for creating it. + * + * The number of bits of precision can be adjusted by changing these symbols. + * We recommend keeping 6 bits for G and 5 each for R and B. + * If you have plenty of memory and cycles, 6 bits all around gives marginally + * better results; if you are short of memory, 5 bits all around will save + * some space but degrade the results. + * To maintain a fully accurate histogram, we'd need to allocate a "long" + * (preferably unsigned long) for each cell. In practice this is overkill; + * we can get by with 16 bits per cell. Few of the cell counts will overflow, + * and clamping those that do overflow to the maximum value will give close- + * enough results. This reduces the recommended histogram size from 256Kb + * to 128Kb, which is a useful savings on PC-class machines. + * (In the second pass the histogram space is re-used for pixel mapping data; + * in that capacity, each cell must be able to store zero to the number of + * desired colors. 16 bits/cell is plenty for that too.) + * Since the JPEG code is intended to run in small memory model on 80x86 + * machines, we can't just allocate the histogram in one chunk. Instead + * of a true 3-D array, we use a row of pointers to 2-D arrays. Each + * pointer corresponds to a C0 value (typically 2^5 = 32 pointers) and + * each 2-D array has 2^6*2^5 = 2048 or 2^6*2^6 = 4096 entries. Note that + * on 80x86 machines, the pointer row is in near memory but the actual + * arrays are in far memory (same arrangement as we use for image arrays). + */ + +#define MAXNUMCOLORS (MAXJSAMPLE+1) /* maximum size of colormap */ + +/* These will do the right thing for either R,G,B or B,G,R color order, + * but you may not like the results for other color orders. + */ +#define HIST_C0_BITS 5 /* bits of precision in R/B histogram */ +#define HIST_C1_BITS 6 /* bits of precision in G histogram */ +#define HIST_C2_BITS 5 /* bits of precision in B/R histogram */ + +/* Number of elements along histogram axes. */ +#define HIST_C0_ELEMS (1<cquantize; + register JSAMPROW ptr; + register histptr histp; + register hist3d histogram = cquantize->histogram; + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + + for (row = 0; row < num_rows; row++) { + ptr = input_buf[row]; + for (col = width; col > 0; col--) { + /* get pixel value and index into the histogram */ + histp = & histogram[GETJSAMPLE(ptr[0]) >> C0_SHIFT] + [GETJSAMPLE(ptr[1]) >> C1_SHIFT] + [GETJSAMPLE(ptr[2]) >> C2_SHIFT]; + /* increment, check for overflow and undo increment if so. */ + if (++(*histp) <= 0) + (*histp)--; + ptr += 3; + } + } +} + + +/* + * Next we have the really interesting routines: selection of a colormap + * given the completed histogram. + * These routines work with a list of "boxes", each representing a rectangular + * subset of the input color space (to histogram precision). + */ + +typedef struct { + /* The bounds of the box (inclusive); expressed as histogram indexes */ + int c0min, c0max; + int c1min, c1max; + int c2min, c2max; + /* The volume (actually 2-norm) of the box */ + INT32 volume; + /* The number of nonzero histogram cells within this box */ + long colorcount; +} box; + +typedef box * boxptr; + + +LOCAL(boxptr) +find_biggest_color_pop (boxptr boxlist, int numboxes) +/* Find the splittable box with the largest color population */ +/* Returns NULL if no splittable boxes remain */ +{ + register boxptr boxp; + register int i; + register long maxc = 0; + boxptr which = NULL; + + for (i = 0, boxp = boxlist; i < numboxes; i++, boxp++) { + if (boxp->colorcount > maxc && boxp->volume > 0) { + which = boxp; + maxc = boxp->colorcount; + } + } + return which; +} + + +LOCAL(boxptr) +find_biggest_volume (boxptr boxlist, int numboxes) +/* Find the splittable box with the largest (scaled) volume */ +/* Returns NULL if no splittable boxes remain */ +{ + register boxptr boxp; + register int i; + register INT32 maxv = 0; + boxptr which = NULL; + + for (i = 0, boxp = boxlist; i < numboxes; i++, boxp++) { + if (boxp->volume > maxv) { + which = boxp; + maxv = boxp->volume; + } + } + return which; +} + + +LOCAL(void) +update_box (j_decompress_ptr cinfo, boxptr boxp) +/* Shrink the min/max bounds of a box to enclose only nonzero elements, */ +/* and recompute its volume and population */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + hist3d histogram = cquantize->histogram; + histptr histp; + int c0,c1,c2; + int c0min,c0max,c1min,c1max,c2min,c2max; + INT32 dist0,dist1,dist2; + long ccount; + + c0min = boxp->c0min; c0max = boxp->c0max; + c1min = boxp->c1min; c1max = boxp->c1max; + c2min = boxp->c2min; c2max = boxp->c2max; + + if (c0max > c0min) + for (c0 = c0min; c0 <= c0max; c0++) + for (c1 = c1min; c1 <= c1max; c1++) { + histp = & histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++) + if (*histp++ != 0) { + boxp->c0min = c0min = c0; + goto have_c0min; + } + } + have_c0min: + if (c0max > c0min) + for (c0 = c0max; c0 >= c0min; c0--) + for (c1 = c1min; c1 <= c1max; c1++) { + histp = & histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++) + if (*histp++ != 0) { + boxp->c0max = c0max = c0; + goto have_c0max; + } + } + have_c0max: + if (c1max > c1min) + for (c1 = c1min; c1 <= c1max; c1++) + for (c0 = c0min; c0 <= c0max; c0++) { + histp = & histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++) + if (*histp++ != 0) { + boxp->c1min = c1min = c1; + goto have_c1min; + } + } + have_c1min: + if (c1max > c1min) + for (c1 = c1max; c1 >= c1min; c1--) + for (c0 = c0min; c0 <= c0max; c0++) { + histp = & histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++) + if (*histp++ != 0) { + boxp->c1max = c1max = c1; + goto have_c1max; + } + } + have_c1max: + if (c2max > c2min) + for (c2 = c2min; c2 <= c2max; c2++) + for (c0 = c0min; c0 <= c0max; c0++) { + histp = & histogram[c0][c1min][c2]; + for (c1 = c1min; c1 <= c1max; c1++, histp += HIST_C2_ELEMS) + if (*histp != 0) { + boxp->c2min = c2min = c2; + goto have_c2min; + } + } + have_c2min: + if (c2max > c2min) + for (c2 = c2max; c2 >= c2min; c2--) + for (c0 = c0min; c0 <= c0max; c0++) { + histp = & histogram[c0][c1min][c2]; + for (c1 = c1min; c1 <= c1max; c1++, histp += HIST_C2_ELEMS) + if (*histp != 0) { + boxp->c2max = c2max = c2; + goto have_c2max; + } + } + have_c2max: + + /* Update box volume. + * We use 2-norm rather than real volume here; this biases the method + * against making long narrow boxes, and it has the side benefit that + * a box is splittable iff norm > 0. + * Since the differences are expressed in histogram-cell units, + * we have to shift back to JSAMPLE units to get consistent distances; + * after which, we scale according to the selected distance scale factors. + */ + dist0 = ((c0max - c0min) << C0_SHIFT) * C0_SCALE; + dist1 = ((c1max - c1min) << C1_SHIFT) * C1_SCALE; + dist2 = ((c2max - c2min) << C2_SHIFT) * C2_SCALE; + boxp->volume = dist0*dist0 + dist1*dist1 + dist2*dist2; + + /* Now scan remaining volume of box and compute population */ + ccount = 0; + for (c0 = c0min; c0 <= c0max; c0++) + for (c1 = c1min; c1 <= c1max; c1++) { + histp = & histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++, histp++) + if (*histp != 0) { + ccount++; + } + } + boxp->colorcount = ccount; +} + + +LOCAL(int) +median_cut (j_decompress_ptr cinfo, boxptr boxlist, int numboxes, + int desired_colors) +/* Repeatedly select and split the largest box until we have enough boxes */ +{ + int n,lb; + int c0,c1,c2,cmax; + register boxptr b1,b2; + + while (numboxes < desired_colors) { + /* Select box to split. + * Current algorithm: by population for first half, then by volume. + */ + if (numboxes*2 <= desired_colors) { + b1 = find_biggest_color_pop(boxlist, numboxes); + } else { + b1 = find_biggest_volume(boxlist, numboxes); + } + if (b1 == NULL) /* no splittable boxes left! */ + break; + b2 = &boxlist[numboxes]; /* where new box will go */ + /* Copy the color bounds to the new box. */ + b2->c0max = b1->c0max; b2->c1max = b1->c1max; b2->c2max = b1->c2max; + b2->c0min = b1->c0min; b2->c1min = b1->c1min; b2->c2min = b1->c2min; + /* Choose which axis to split the box on. + * Current algorithm: longest scaled axis. + * See notes in update_box about scaling distances. + */ + c0 = ((b1->c0max - b1->c0min) << C0_SHIFT) * C0_SCALE; + c1 = ((b1->c1max - b1->c1min) << C1_SHIFT) * C1_SCALE; + c2 = ((b1->c2max - b1->c2min) << C2_SHIFT) * C2_SCALE; + /* We want to break any ties in favor of green, then red, blue last. + * This code does the right thing for R,G,B or B,G,R color orders only. + */ +#if RGB_RED == 0 + cmax = c1; n = 1; + if (c0 > cmax) { cmax = c0; n = 0; } + if (c2 > cmax) { n = 2; } +#else + cmax = c1; n = 1; + if (c2 > cmax) { cmax = c2; n = 2; } + if (c0 > cmax) { n = 0; } +#endif + /* Choose split point along selected axis, and update box bounds. + * Current algorithm: split at halfway point. + * (Since the box has been shrunk to minimum volume, + * any split will produce two nonempty subboxes.) + * Note that lb value is max for lower box, so must be < old max. + */ + switch (n) { + case 0: + lb = (b1->c0max + b1->c0min) / 2; + b1->c0max = lb; + b2->c0min = lb+1; + break; + case 1: + lb = (b1->c1max + b1->c1min) / 2; + b1->c1max = lb; + b2->c1min = lb+1; + break; + case 2: + lb = (b1->c2max + b1->c2min) / 2; + b1->c2max = lb; + b2->c2min = lb+1; + break; + } + /* Update stats for boxes */ + update_box(cinfo, b1); + update_box(cinfo, b2); + numboxes++; + } + return numboxes; +} + + +LOCAL(void) +compute_color (j_decompress_ptr cinfo, boxptr boxp, int icolor) +/* Compute representative color for a box, put it in colormap[icolor] */ +{ + /* Current algorithm: mean weighted by pixels (not colors) */ + /* Note it is important to get the rounding correct! */ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + hist3d histogram = cquantize->histogram; + histptr histp; + int c0,c1,c2; + int c0min,c0max,c1min,c1max,c2min,c2max; + long count; + long total = 0; + long c0total = 0; + long c1total = 0; + long c2total = 0; + + c0min = boxp->c0min; c0max = boxp->c0max; + c1min = boxp->c1min; c1max = boxp->c1max; + c2min = boxp->c2min; c2max = boxp->c2max; + + for (c0 = c0min; c0 <= c0max; c0++) + for (c1 = c1min; c1 <= c1max; c1++) { + histp = & histogram[c0][c1][c2min]; + for (c2 = c2min; c2 <= c2max; c2++) { + if ((count = *histp++) != 0) { + total += count; + c0total += ((c0 << C0_SHIFT) + ((1<>1)) * count; + c1total += ((c1 << C1_SHIFT) + ((1<>1)) * count; + c2total += ((c2 << C2_SHIFT) + ((1<>1)) * count; + } + } + } + + cinfo->colormap[0][icolor] = (JSAMPLE) ((c0total + (total>>1)) / total); + cinfo->colormap[1][icolor] = (JSAMPLE) ((c1total + (total>>1)) / total); + cinfo->colormap[2][icolor] = (JSAMPLE) ((c2total + (total>>1)) / total); +} + + +LOCAL(void) +select_colors (j_decompress_ptr cinfo, int desired_colors) +/* Master routine for color selection */ +{ + boxptr boxlist; + int numboxes; + int i; + + /* Allocate workspace for box list */ + boxlist = (boxptr) (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_IMAGE, desired_colors * SIZEOF(box)); + /* Initialize one box containing whole space */ + numboxes = 1; + boxlist[0].c0min = 0; + boxlist[0].c0max = MAXJSAMPLE >> C0_SHIFT; + boxlist[0].c1min = 0; + boxlist[0].c1max = MAXJSAMPLE >> C1_SHIFT; + boxlist[0].c2min = 0; + boxlist[0].c2max = MAXJSAMPLE >> C2_SHIFT; + /* Shrink it to actually-used volume and set its statistics */ + update_box(cinfo, & boxlist[0]); + /* Perform median-cut to produce final box list */ + numboxes = median_cut(cinfo, boxlist, numboxes, desired_colors); + /* Compute the representative color for each box, fill colormap */ + for (i = 0; i < numboxes; i++) + compute_color(cinfo, & boxlist[i], i); + cinfo->actual_number_of_colors = numboxes; + TRACEMS1(cinfo, 1, JTRC_QUANT_SELECTED, numboxes); +} + + +/* + * These routines are concerned with the time-critical task of mapping input + * colors to the nearest color in the selected colormap. + * + * We re-use the histogram space as an "inverse color map", essentially a + * cache for the results of nearest-color searches. All colors within a + * histogram cell will be mapped to the same colormap entry, namely the one + * closest to the cell's center. This may not be quite the closest entry to + * the actual input color, but it's almost as good. A zero in the cache + * indicates we haven't found the nearest color for that cell yet; the array + * is cleared to zeroes before starting the mapping pass. When we find the + * nearest color for a cell, its colormap index plus one is recorded in the + * cache for future use. The pass2 scanning routines call fill_inverse_cmap + * when they need to use an unfilled entry in the cache. + * + * Our method of efficiently finding nearest colors is based on the "locally + * sorted search" idea described by Heckbert and on the incremental distance + * calculation described by Spencer W. Thomas in chapter III.1 of Graphics + * Gems II (James Arvo, ed. Academic Press, 1991). Thomas points out that + * the distances from a given colormap entry to each cell of the histogram can + * be computed quickly using an incremental method: the differences between + * distances to adjacent cells themselves differ by a constant. This allows a + * fairly fast implementation of the "brute force" approach of computing the + * distance from every colormap entry to every histogram cell. Unfortunately, + * it needs a work array to hold the best-distance-so-far for each histogram + * cell (because the inner loop has to be over cells, not colormap entries). + * The work array elements have to be INT32s, so the work array would need + * 256Kb at our recommended precision. This is not feasible in DOS machines. + * + * To get around these problems, we apply Thomas' method to compute the + * nearest colors for only the cells within a small subbox of the histogram. + * The work array need be only as big as the subbox, so the memory usage + * problem is solved. Furthermore, we need not fill subboxes that are never + * referenced in pass2; many images use only part of the color gamut, so a + * fair amount of work is saved. An additional advantage of this + * approach is that we can apply Heckbert's locality criterion to quickly + * eliminate colormap entries that are far away from the subbox; typically + * three-fourths of the colormap entries are rejected by Heckbert's criterion, + * and we need not compute their distances to individual cells in the subbox. + * The speed of this approach is heavily influenced by the subbox size: too + * small means too much overhead, too big loses because Heckbert's criterion + * can't eliminate as many colormap entries. Empirically the best subbox + * size seems to be about 1/512th of the histogram (1/8th in each direction). + * + * Thomas' article also describes a refined method which is asymptotically + * faster than the brute-force method, but it is also far more complex and + * cannot efficiently be applied to small subboxes. It is therefore not + * useful for programs intended to be portable to DOS machines. On machines + * with plenty of memory, filling the whole histogram in one shot with Thomas' + * refined method might be faster than the present code --- but then again, + * it might not be any faster, and it's certainly more complicated. + */ + + +/* log2(histogram cells in update box) for each axis; this can be adjusted */ +#define BOX_C0_LOG (HIST_C0_BITS-3) +#define BOX_C1_LOG (HIST_C1_BITS-3) +#define BOX_C2_LOG (HIST_C2_BITS-3) + +#define BOX_C0_ELEMS (1<actual_number_of_colors; + int maxc0, maxc1, maxc2; + int centerc0, centerc1, centerc2; + int i, x, ncolors; + INT32 minmaxdist, min_dist, max_dist, tdist; + INT32 mindist[MAXNUMCOLORS]; /* min distance to colormap entry i */ + + /* Compute true coordinates of update box's upper corner and center. + * Actually we compute the coordinates of the center of the upper-corner + * histogram cell, which are the upper bounds of the volume we care about. + * Note that since ">>" rounds down, the "center" values may be closer to + * min than to max; hence comparisons to them must be "<=", not "<". + */ + maxc0 = minc0 + ((1 << BOX_C0_SHIFT) - (1 << C0_SHIFT)); + centerc0 = (minc0 + maxc0) >> 1; + maxc1 = minc1 + ((1 << BOX_C1_SHIFT) - (1 << C1_SHIFT)); + centerc1 = (minc1 + maxc1) >> 1; + maxc2 = minc2 + ((1 << BOX_C2_SHIFT) - (1 << C2_SHIFT)); + centerc2 = (minc2 + maxc2) >> 1; + + /* For each color in colormap, find: + * 1. its minimum squared-distance to any point in the update box + * (zero if color is within update box); + * 2. its maximum squared-distance to any point in the update box. + * Both of these can be found by considering only the corners of the box. + * We save the minimum distance for each color in mindist[]; + * only the smallest maximum distance is of interest. + */ + minmaxdist = 0x7FFFFFFFL; + + for (i = 0; i < numcolors; i++) { + /* We compute the squared-c0-distance term, then add in the other two. */ + x = GETJSAMPLE(cinfo->colormap[0][i]); + if (x < minc0) { + tdist = (x - minc0) * C0_SCALE; + min_dist = tdist*tdist; + tdist = (x - maxc0) * C0_SCALE; + max_dist = tdist*tdist; + } else if (x > maxc0) { + tdist = (x - maxc0) * C0_SCALE; + min_dist = tdist*tdist; + tdist = (x - minc0) * C0_SCALE; + max_dist = tdist*tdist; + } else { + /* within cell range so no contribution to min_dist */ + min_dist = 0; + if (x <= centerc0) { + tdist = (x - maxc0) * C0_SCALE; + max_dist = tdist*tdist; + } else { + tdist = (x - minc0) * C0_SCALE; + max_dist = tdist*tdist; + } + } + + x = GETJSAMPLE(cinfo->colormap[1][i]); + if (x < minc1) { + tdist = (x - minc1) * C1_SCALE; + min_dist += tdist*tdist; + tdist = (x - maxc1) * C1_SCALE; + max_dist += tdist*tdist; + } else if (x > maxc1) { + tdist = (x - maxc1) * C1_SCALE; + min_dist += tdist*tdist; + tdist = (x - minc1) * C1_SCALE; + max_dist += tdist*tdist; + } else { + /* within cell range so no contribution to min_dist */ + if (x <= centerc1) { + tdist = (x - maxc1) * C1_SCALE; + max_dist += tdist*tdist; + } else { + tdist = (x - minc1) * C1_SCALE; + max_dist += tdist*tdist; + } + } + + x = GETJSAMPLE(cinfo->colormap[2][i]); + if (x < minc2) { + tdist = (x - minc2) * C2_SCALE; + min_dist += tdist*tdist; + tdist = (x - maxc2) * C2_SCALE; + max_dist += tdist*tdist; + } else if (x > maxc2) { + tdist = (x - maxc2) * C2_SCALE; + min_dist += tdist*tdist; + tdist = (x - minc2) * C2_SCALE; + max_dist += tdist*tdist; + } else { + /* within cell range so no contribution to min_dist */ + if (x <= centerc2) { + tdist = (x - maxc2) * C2_SCALE; + max_dist += tdist*tdist; + } else { + tdist = (x - minc2) * C2_SCALE; + max_dist += tdist*tdist; + } + } + + mindist[i] = min_dist; /* save away the results */ + if (max_dist < minmaxdist) + minmaxdist = max_dist; + } + + /* Now we know that no cell in the update box is more than minmaxdist + * away from some colormap entry. Therefore, only colors that are + * within minmaxdist of some part of the box need be considered. + */ + ncolors = 0; + for (i = 0; i < numcolors; i++) { + if (mindist[i] <= minmaxdist) + colorlist[ncolors++] = (JSAMPLE) i; + } + return ncolors; +} + + +LOCAL(void) +find_best_colors (j_decompress_ptr cinfo, int minc0, int minc1, int minc2, + int numcolors, JSAMPLE colorlist[], JSAMPLE bestcolor[]) +/* Find the closest colormap entry for each cell in the update box, + * given the list of candidate colors prepared by find_nearby_colors. + * Return the indexes of the closest entries in the bestcolor[] array. + * This routine uses Thomas' incremental distance calculation method to + * find the distance from a colormap entry to successive cells in the box. + */ +{ + int ic0, ic1, ic2; + int i, icolor; + register INT32 * bptr; /* pointer into bestdist[] array */ + JSAMPLE * cptr; /* pointer into bestcolor[] array */ + INT32 dist0, dist1; /* initial distance values */ + register INT32 dist2; /* current distance in inner loop */ + INT32 xx0, xx1; /* distance increments */ + register INT32 xx2; + INT32 inc0, inc1, inc2; /* initial values for increments */ + /* This array holds the distance to the nearest-so-far color for each cell */ + INT32 bestdist[BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS]; + + /* Initialize best-distance for each cell of the update box */ + bptr = bestdist; + for (i = BOX_C0_ELEMS*BOX_C1_ELEMS*BOX_C2_ELEMS-1; i >= 0; i--) + *bptr++ = 0x7FFFFFFFL; + + /* For each color selected by find_nearby_colors, + * compute its distance to the center of each cell in the box. + * If that's less than best-so-far, update best distance and color number. + */ + + /* Nominal steps between cell centers ("x" in Thomas article) */ +#define STEP_C0 ((1 << C0_SHIFT) * C0_SCALE) +#define STEP_C1 ((1 << C1_SHIFT) * C1_SCALE) +#define STEP_C2 ((1 << C2_SHIFT) * C2_SCALE) + + for (i = 0; i < numcolors; i++) { + icolor = GETJSAMPLE(colorlist[i]); + /* Compute (square of) distance from minc0/c1/c2 to this color */ + inc0 = (minc0 - GETJSAMPLE(cinfo->colormap[0][icolor])) * C0_SCALE; + dist0 = inc0*inc0; + inc1 = (minc1 - GETJSAMPLE(cinfo->colormap[1][icolor])) * C1_SCALE; + dist0 += inc1*inc1; + inc2 = (minc2 - GETJSAMPLE(cinfo->colormap[2][icolor])) * C2_SCALE; + dist0 += inc2*inc2; + /* Form the initial difference increments */ + inc0 = inc0 * (2 * STEP_C0) + STEP_C0 * STEP_C0; + inc1 = inc1 * (2 * STEP_C1) + STEP_C1 * STEP_C1; + inc2 = inc2 * (2 * STEP_C2) + STEP_C2 * STEP_C2; + /* Now loop over all cells in box, updating distance per Thomas method */ + bptr = bestdist; + cptr = bestcolor; + xx0 = inc0; + for (ic0 = BOX_C0_ELEMS-1; ic0 >= 0; ic0--) { + dist1 = dist0; + xx1 = inc1; + for (ic1 = BOX_C1_ELEMS-1; ic1 >= 0; ic1--) { + dist2 = dist1; + xx2 = inc2; + for (ic2 = BOX_C2_ELEMS-1; ic2 >= 0; ic2--) { + if (dist2 < *bptr) { + *bptr = dist2; + *cptr = (JSAMPLE) icolor; + } + dist2 += xx2; + xx2 += 2 * STEP_C2 * STEP_C2; + bptr++; + cptr++; + } + dist1 += xx1; + xx1 += 2 * STEP_C1 * STEP_C1; + } + dist0 += xx0; + xx0 += 2 * STEP_C0 * STEP_C0; + } + } +} + + +LOCAL(void) +fill_inverse_cmap (j_decompress_ptr cinfo, int c0, int c1, int c2) +/* Fill the inverse-colormap entries in the update box that contains */ +/* histogram cell c0/c1/c2. (Only that one cell MUST be filled, but */ +/* we can fill as many others as we wish.) */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + hist3d histogram = cquantize->histogram; + int minc0, minc1, minc2; /* lower left corner of update box */ + int ic0, ic1, ic2; + register JSAMPLE * cptr; /* pointer into bestcolor[] array */ + register histptr cachep; /* pointer into main cache array */ + /* This array lists the candidate colormap indexes. */ + JSAMPLE colorlist[MAXNUMCOLORS]; + int numcolors; /* number of candidate colors */ + /* This array holds the actually closest colormap index for each cell. */ + JSAMPLE bestcolor[BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS]; + + /* Convert cell coordinates to update box ID */ + c0 >>= BOX_C0_LOG; + c1 >>= BOX_C1_LOG; + c2 >>= BOX_C2_LOG; + + /* Compute true coordinates of update box's origin corner. + * Actually we compute the coordinates of the center of the corner + * histogram cell, which are the lower bounds of the volume we care about. + */ + minc0 = (c0 << BOX_C0_SHIFT) + ((1 << C0_SHIFT) >> 1); + minc1 = (c1 << BOX_C1_SHIFT) + ((1 << C1_SHIFT) >> 1); + minc2 = (c2 << BOX_C2_SHIFT) + ((1 << C2_SHIFT) >> 1); + + /* Determine which colormap entries are close enough to be candidates + * for the nearest entry to some cell in the update box. + */ + numcolors = find_nearby_colors(cinfo, minc0, minc1, minc2, colorlist); + + /* Determine the actually nearest colors. */ + find_best_colors(cinfo, minc0, minc1, minc2, numcolors, colorlist, + bestcolor); + + /* Save the best color numbers (plus 1) in the main cache array */ + c0 <<= BOX_C0_LOG; /* convert ID back to base cell indexes */ + c1 <<= BOX_C1_LOG; + c2 <<= BOX_C2_LOG; + cptr = bestcolor; + for (ic0 = 0; ic0 < BOX_C0_ELEMS; ic0++) { + for (ic1 = 0; ic1 < BOX_C1_ELEMS; ic1++) { + cachep = & histogram[c0+ic0][c1+ic1][c2]; + for (ic2 = 0; ic2 < BOX_C2_ELEMS; ic2++) { + *cachep++ = (histcell) (GETJSAMPLE(*cptr++) + 1); + } + } + } +} + + +/* + * Map some rows of pixels to the output colormapped representation. + */ + +METHODDEF(void) +pass2_no_dither (j_decompress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPARRAY output_buf, int num_rows) +/* This version performs no dithering */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + hist3d histogram = cquantize->histogram; + register JSAMPROW inptr, outptr; + register histptr cachep; + register int c0, c1, c2; + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + + for (row = 0; row < num_rows; row++) { + inptr = input_buf[row]; + outptr = output_buf[row]; + for (col = width; col > 0; col--) { + /* get pixel value and index into the cache */ + c0 = GETJSAMPLE(*inptr++) >> C0_SHIFT; + c1 = GETJSAMPLE(*inptr++) >> C1_SHIFT; + c2 = GETJSAMPLE(*inptr++) >> C2_SHIFT; + cachep = & histogram[c0][c1][c2]; + /* If we have not seen this color before, find nearest colormap entry */ + /* and update the cache */ + if (*cachep == 0) + fill_inverse_cmap(cinfo, c0,c1,c2); + /* Now emit the colormap index for this cell */ + *outptr++ = (JSAMPLE) (*cachep - 1); + } + } +} + + +METHODDEF(void) +pass2_fs_dither (j_decompress_ptr cinfo, + JSAMPARRAY input_buf, JSAMPARRAY output_buf, int num_rows) +/* This version performs Floyd-Steinberg dithering */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + hist3d histogram = cquantize->histogram; + register LOCFSERROR cur0, cur1, cur2; /* current error or pixel value */ + LOCFSERROR belowerr0, belowerr1, belowerr2; /* error for pixel below cur */ + LOCFSERROR bpreverr0, bpreverr1, bpreverr2; /* error for below/prev col */ + register FSERRPTR errorptr; /* => fserrors[] at column before current */ + JSAMPROW inptr; /* => current input pixel */ + JSAMPROW outptr; /* => current output pixel */ + histptr cachep; + int dir; /* +1 or -1 depending on direction */ + int dir3; /* 3*dir, for advancing inptr & errorptr */ + int row; + JDIMENSION col; + JDIMENSION width = cinfo->output_width; + JSAMPLE *range_limit = cinfo->sample_range_limit; + int *error_limit = cquantize->error_limiter; + JSAMPROW colormap0 = cinfo->colormap[0]; + JSAMPROW colormap1 = cinfo->colormap[1]; + JSAMPROW colormap2 = cinfo->colormap[2]; + SHIFT_TEMPS + + for (row = 0; row < num_rows; row++) { + inptr = input_buf[row]; + outptr = output_buf[row]; + if (cquantize->on_odd_row) { + /* work right to left in this row */ + inptr += (width-1) * 3; /* so point to rightmost pixel */ + outptr += width-1; + dir = -1; + dir3 = -3; + errorptr = cquantize->fserrors + (width+1)*3; /* => entry after last column */ + cquantize->on_odd_row = FALSE; /* flip for next time */ + } else { + /* work left to right in this row */ + dir = 1; + dir3 = 3; + errorptr = cquantize->fserrors; /* => entry before first real column */ + cquantize->on_odd_row = TRUE; /* flip for next time */ + } + /* Preset error values: no error propagated to first pixel from left */ + cur0 = cur1 = cur2 = 0; + /* and no error propagated to row below yet */ + belowerr0 = belowerr1 = belowerr2 = 0; + bpreverr0 = bpreverr1 = bpreverr2 = 0; + + for (col = width; col > 0; col--) { + /* curN holds the error propagated from the previous pixel on the + * current line. Add the error propagated from the previous line + * to form the complete error correction term for this pixel, and + * round the error term (which is expressed * 16) to an integer. + * RIGHT_SHIFT rounds towards minus infinity, so adding 8 is correct + * for either sign of the error value. + * Note: errorptr points to *previous* column's array entry. + */ + cur0 = RIGHT_SHIFT(cur0 + errorptr[dir3+0] + 8, 4); + cur1 = RIGHT_SHIFT(cur1 + errorptr[dir3+1] + 8, 4); + cur2 = RIGHT_SHIFT(cur2 + errorptr[dir3+2] + 8, 4); + /* Limit the error using transfer function set by init_error_limit. + * See comments with init_error_limit for rationale. + */ + cur0 = error_limit[cur0]; + cur1 = error_limit[cur1]; + cur2 = error_limit[cur2]; + /* Form pixel value + error, and range-limit to 0..MAXJSAMPLE. + * The maximum error is +- MAXJSAMPLE (or less with error limiting); + * this sets the required size of the range_limit array. + */ + cur0 += GETJSAMPLE(inptr[0]); + cur1 += GETJSAMPLE(inptr[1]); + cur2 += GETJSAMPLE(inptr[2]); + cur0 = GETJSAMPLE(range_limit[cur0]); + cur1 = GETJSAMPLE(range_limit[cur1]); + cur2 = GETJSAMPLE(range_limit[cur2]); + /* Index into the cache with adjusted pixel value */ + cachep = & histogram[cur0>>C0_SHIFT][cur1>>C1_SHIFT][cur2>>C2_SHIFT]; + /* If we have not seen this color before, find nearest colormap */ + /* entry and update the cache */ + if (*cachep == 0) + fill_inverse_cmap(cinfo, cur0>>C0_SHIFT,cur1>>C1_SHIFT,cur2>>C2_SHIFT); + /* Now emit the colormap index for this cell */ + { register int pixcode = *cachep - 1; + *outptr = (JSAMPLE) pixcode; + /* Compute representation error for this pixel */ + cur0 -= GETJSAMPLE(colormap0[pixcode]); + cur1 -= GETJSAMPLE(colormap1[pixcode]); + cur2 -= GETJSAMPLE(colormap2[pixcode]); + } + /* Compute error fractions to be propagated to adjacent pixels. + * Add these into the running sums, and simultaneously shift the + * next-line error sums left by 1 column. + */ + { register LOCFSERROR bnexterr, delta; + + bnexterr = cur0; /* Process component 0 */ + delta = cur0 * 2; + cur0 += delta; /* form error * 3 */ + errorptr[0] = (FSERROR) (bpreverr0 + cur0); + cur0 += delta; /* form error * 5 */ + bpreverr0 = belowerr0 + cur0; + belowerr0 = bnexterr; + cur0 += delta; /* form error * 7 */ + bnexterr = cur1; /* Process component 1 */ + delta = cur1 * 2; + cur1 += delta; /* form error * 3 */ + errorptr[1] = (FSERROR) (bpreverr1 + cur1); + cur1 += delta; /* form error * 5 */ + bpreverr1 = belowerr1 + cur1; + belowerr1 = bnexterr; + cur1 += delta; /* form error * 7 */ + bnexterr = cur2; /* Process component 2 */ + delta = cur2 * 2; + cur2 += delta; /* form error * 3 */ + errorptr[2] = (FSERROR) (bpreverr2 + cur2); + cur2 += delta; /* form error * 5 */ + bpreverr2 = belowerr2 + cur2; + belowerr2 = bnexterr; + cur2 += delta; /* form error * 7 */ + } + /* At this point curN contains the 7/16 error value to be propagated + * to the next pixel on the current line, and all the errors for the + * next line have been shifted over. We are therefore ready to move on. + */ + inptr += dir3; /* Advance pixel pointers to next column */ + outptr += dir; + errorptr += dir3; /* advance errorptr to current column */ + } + /* Post-loop cleanup: we must unload the final error values into the + * final fserrors[] entry. Note we need not unload belowerrN because + * it is for the dummy column before or after the actual array. + */ + errorptr[0] = (FSERROR) bpreverr0; /* unload prev errs into array */ + errorptr[1] = (FSERROR) bpreverr1; + errorptr[2] = (FSERROR) bpreverr2; + } +} + + +/* + * Initialize the error-limiting transfer function (lookup table). + * The raw F-S error computation can potentially compute error values of up to + * +- MAXJSAMPLE. But we want the maximum correction applied to a pixel to be + * much less, otherwise obviously wrong pixels will be created. (Typical + * effects include weird fringes at color-area boundaries, isolated bright + * pixels in a dark area, etc.) The standard advice for avoiding this problem + * is to ensure that the "corners" of the color cube are allocated as output + * colors; then repeated errors in the same direction cannot cause cascading + * error buildup. However, that only prevents the error from getting + * completely out of hand; Aaron Giles reports that error limiting improves + * the results even with corner colors allocated. + * A simple clamping of the error values to about +- MAXJSAMPLE/8 works pretty + * well, but the smoother transfer function used below is even better. Thanks + * to Aaron Giles for this idea. + */ + +LOCAL(void) +init_error_limit (j_decompress_ptr cinfo) +/* Allocate and fill in the error_limiter table */ +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + int * table; + int in, out; + + table = (int *) (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_IMAGE, (MAXJSAMPLE*2+1) * SIZEOF(int)); + table += MAXJSAMPLE; /* so can index -MAXJSAMPLE .. +MAXJSAMPLE */ + cquantize->error_limiter = table; + +#define STEPSIZE ((MAXJSAMPLE+1)/16) + /* Map errors 1:1 up to +- MAXJSAMPLE/16 */ + out = 0; + for (in = 0; in < STEPSIZE; in++, out++) { + table[in] = out; table[-in] = -out; + } + /* Map errors 1:2 up to +- 3*MAXJSAMPLE/16 */ + for (; in < STEPSIZE*3; in++, out += (in&1) ? 0 : 1) { + table[in] = out; table[-in] = -out; + } + /* Clamp the rest to final out value (which is (MAXJSAMPLE+1)/8) */ + for (; in <= MAXJSAMPLE; in++) { + table[in] = out; table[-in] = -out; + } +#undef STEPSIZE +} + + +/* + * Finish up at the end of each pass. + */ + +METHODDEF(void) +finish_pass1 (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + + /* Select the representative colors and fill in cinfo->colormap */ + cinfo->colormap = cquantize->sv_colormap; + select_colors(cinfo, cquantize->desired); + /* Force next pass to zero the color index table */ + cquantize->needs_zeroed = TRUE; +} + + +METHODDEF(void) +finish_pass2 (j_decompress_ptr cinfo) +{ + /* no work */ +} + + +/* + * Initialize for each processing pass. + */ + +METHODDEF(void) +start_pass_2_quant (j_decompress_ptr cinfo, boolean is_pre_scan) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + hist3d histogram = cquantize->histogram; + int i; + + /* Only F-S dithering or no dithering is supported. */ + /* If user asks for ordered dither, give him F-S. */ + if (cinfo->dither_mode != JDITHER_NONE) + cinfo->dither_mode = JDITHER_FS; + + if (is_pre_scan) { + /* Set up method pointers */ + cquantize->pub.color_quantize = prescan_quantize; + cquantize->pub.finish_pass = finish_pass1; + cquantize->needs_zeroed = TRUE; /* Always zero histogram */ + } else { + /* Set up method pointers */ + if (cinfo->dither_mode == JDITHER_FS) + cquantize->pub.color_quantize = pass2_fs_dither; + else + cquantize->pub.color_quantize = pass2_no_dither; + cquantize->pub.finish_pass = finish_pass2; + + /* Make sure color count is acceptable */ + i = cinfo->actual_number_of_colors; + if (i < 1) + ERREXIT1(cinfo, JERR_QUANT_FEW_COLORS, 1); + if (i > MAXNUMCOLORS) + ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, MAXNUMCOLORS); + + if (cinfo->dither_mode == JDITHER_FS) { + size_t arraysize = (size_t) ((cinfo->output_width + 2) * + (3 * SIZEOF(FSERROR))); + /* Allocate Floyd-Steinberg workspace if we didn't already. */ + if (cquantize->fserrors == NULL) + cquantize->fserrors = (FSERRPTR) (*cinfo->mem->alloc_large) + ((j_common_ptr) cinfo, JPOOL_IMAGE, arraysize); + /* Initialize the propagated errors to zero. */ + jzero_far((void FAR *) cquantize->fserrors, arraysize); + /* Make the error-limit table if we didn't already. */ + if (cquantize->error_limiter == NULL) + init_error_limit(cinfo); + cquantize->on_odd_row = FALSE; + } + + } + /* Zero the histogram or inverse color map, if necessary */ + if (cquantize->needs_zeroed) { + for (i = 0; i < HIST_C0_ELEMS; i++) { + jzero_far((void FAR *) histogram[i], + HIST_C1_ELEMS*HIST_C2_ELEMS * SIZEOF(histcell)); + } + cquantize->needs_zeroed = FALSE; + } +} + + +/* + * Switch to a new external colormap between output passes. + */ + +METHODDEF(void) +new_color_map_2_quant (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize; + + /* Reset the inverse color map */ + cquantize->needs_zeroed = TRUE; +} + + +/* + * Module initialization routine for 2-pass color quantization. + */ + +GLOBAL(void) +jinit_2pass_quantizer (j_decompress_ptr cinfo) +{ + my_cquantize_ptr cquantize; + int i; + + cquantize = (my_cquantize_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(my_cquantizer)); + cinfo->cquantize = (struct jpeg_color_quantizer *) cquantize; + cquantize->pub.start_pass = start_pass_2_quant; + cquantize->pub.new_color_map = new_color_map_2_quant; + cquantize->fserrors = NULL; /* flag optional arrays not allocated */ + cquantize->error_limiter = NULL; + + /* Make sure jdmaster didn't give me a case I can't handle */ + if (cinfo->out_color_components != 3) + ERREXIT(cinfo, JERR_NOTIMPL); + + /* Allocate the histogram/inverse colormap storage */ + cquantize->histogram = (hist3d) (*cinfo->mem->alloc_small) + ((j_common_ptr) cinfo, JPOOL_IMAGE, HIST_C0_ELEMS * SIZEOF(hist2d)); + for (i = 0; i < HIST_C0_ELEMS; i++) { + cquantize->histogram[i] = (hist2d) (*cinfo->mem->alloc_large) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + HIST_C1_ELEMS*HIST_C2_ELEMS * SIZEOF(histcell)); + } + cquantize->needs_zeroed = TRUE; /* histogram is garbage now */ + + /* Allocate storage for the completed colormap, if required. + * We do this now since it is FAR storage and may affect + * the memory manager's space calculations. + */ + if (cinfo->enable_2pass_quant) { + /* Make sure color count is acceptable */ + int desired = cinfo->desired_number_of_colors; + /* Lower bound on # of colors ... somewhat arbitrary as long as > 0 */ + if (desired < 8) + ERREXIT1(cinfo, JERR_QUANT_FEW_COLORS, 8); + /* Make sure colormap indexes can be represented by JSAMPLEs */ + if (desired > MAXNUMCOLORS) + ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, MAXNUMCOLORS); + cquantize->sv_colormap = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo,JPOOL_IMAGE, (JDIMENSION) desired, (JDIMENSION) 3); + cquantize->desired = desired; + } else + cquantize->sv_colormap = NULL; + + /* Only F-S dithering or no dithering is supported. */ + /* If user asks for ordered dither, give him F-S. */ + if (cinfo->dither_mode != JDITHER_NONE) + cinfo->dither_mode = JDITHER_FS; + + /* Allocate Floyd-Steinberg workspace if necessary. + * This isn't really needed until pass 2, but again it is FAR storage. + * Although we will cope with a later change in dither_mode, + * we do not promise to honor max_memory_to_use if dither_mode changes. + */ + if (cinfo->dither_mode == JDITHER_FS) { + cquantize->fserrors = (FSERRPTR) (*cinfo->mem->alloc_large) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (size_t) ((cinfo->output_width + 2) * (3 * SIZEOF(FSERROR)))); + /* Might as well create the error-limiting table too. */ + init_error_limit(cinfo); + } +} + +#endif /* QUANT_2PASS_SUPPORTED */ diff --git a/Engine/lib/ljpeg/jutils.c b/Engine/lib/ljpeg/jutils.c new file mode 100644 index 000000000..d18a95556 --- /dev/null +++ b/Engine/lib/ljpeg/jutils.c @@ -0,0 +1,179 @@ +/* + * jutils.c + * + * Copyright (C) 1991-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains tables and miscellaneous utility routines needed + * for both compression and decompression. + * Note we prefix all global names with "j" to minimize conflicts with + * a surrounding application. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" + + +/* + * jpeg_zigzag_order[i] is the zigzag-order position of the i'th element + * of a DCT block read in natural order (left to right, top to bottom). + */ + +#if 0 /* This table is not actually needed in v6a */ + +const int jpeg_zigzag_order[DCTSIZE2] = { + 0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63 +}; + +#endif + +/* + * jpeg_natural_order[i] is the natural-order position of the i'th element + * of zigzag order. + * + * When reading corrupted data, the Huffman decoders could attempt + * to reference an entry beyond the end of this array (if the decoded + * zero run length reaches past the end of the block). To prevent + * wild stores without adding an inner-loop test, we put some extra + * "63"s after the real entries. This will cause the extra coefficient + * to be stored in location 63 of the block, not somewhere random. + * The worst case would be a run-length of 15, which means we need 16 + * fake entries. + */ + +const int jpeg_natural_order[DCTSIZE2+16] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + 63, 63, 63, 63, 63, 63, 63, 63, /* extra entries for safety in decoder */ + 63, 63, 63, 63, 63, 63, 63, 63 +}; + + +/* + * Arithmetic utilities + */ + +GLOBAL(long) +jdiv_round_up (long a, long b) +/* Compute a/b rounded up to next integer, ie, ceil(a/b) */ +/* Assumes a >= 0, b > 0 */ +{ + return (a + b - 1L) / b; +} + + +GLOBAL(long) +jround_up (long a, long b) +/* Compute a rounded up to next multiple of b, ie, ceil(a/b)*b */ +/* Assumes a >= 0, b > 0 */ +{ + a += b - 1L; + return a - (a % b); +} + + +/* On normal machines we can apply MEMCOPY() and MEMZERO() to sample arrays + * and coefficient-block arrays. This won't work on 80x86 because the arrays + * are FAR and we're assuming a small-pointer memory model. However, some + * DOS compilers provide far-pointer versions of memcpy() and memset() even + * in the small-model libraries. These will be used if USE_FMEM is defined. + * Otherwise, the routines below do it the hard way. (The performance cost + * is not all that great, because these routines aren't very heavily used.) + */ + +#ifndef NEED_FAR_POINTERS /* normal case, same as regular macros */ +#define FMEMCOPY(dest,src,size) MEMCOPY(dest,src,size) +#define FMEMZERO(target,size) MEMZERO(target,size) +#else /* 80x86 case, define if we can */ +#ifdef USE_FMEM +#define FMEMCOPY(dest,src,size) _fmemcpy((void FAR *)(dest), (const void FAR *)(src), (size_t)(size)) +#define FMEMZERO(target,size) _fmemset((void FAR *)(target), 0, (size_t)(size)) +#endif +#endif + + +GLOBAL(void) +jcopy_sample_rows (JSAMPARRAY input_array, int source_row, + JSAMPARRAY output_array, int dest_row, + int num_rows, JDIMENSION num_cols) +/* Copy some rows of samples from one place to another. + * num_rows rows are copied from input_array[source_row++] + * to output_array[dest_row++]; these areas may overlap for duplication. + * The source and destination arrays must be at least as wide as num_cols. + */ +{ + register JSAMPROW inptr, outptr; +#ifdef FMEMCOPY + register size_t count = (size_t) (num_cols * SIZEOF(JSAMPLE)); +#else + register JDIMENSION count; +#endif + register int row; + + input_array += source_row; + output_array += dest_row; + + for (row = num_rows; row > 0; row--) { + inptr = *input_array++; + outptr = *output_array++; +#ifdef FMEMCOPY + FMEMCOPY(outptr, inptr, count); +#else + for (count = num_cols; count > 0; count--) + *outptr++ = *inptr++; /* needn't bother with GETJSAMPLE() here */ +#endif + } +} + + +GLOBAL(void) +jcopy_block_row (JBLOCKROW input_row, JBLOCKROW output_row, + JDIMENSION num_blocks) +/* Copy a row of coefficient blocks from one place to another. */ +{ +#ifdef FMEMCOPY + FMEMCOPY(output_row, input_row, num_blocks * (DCTSIZE2 * SIZEOF(JCOEF))); +#else + register JCOEFPTR inptr, outptr; + register long count; + + inptr = (JCOEFPTR) input_row; + outptr = (JCOEFPTR) output_row; + for (count = (long) num_blocks * DCTSIZE2; count > 0; count--) { + *outptr++ = *inptr++; + } +#endif +} + + +GLOBAL(void) +jzero_far (void FAR * target, size_t bytestozero) +/* Zero out a chunk of FAR memory. */ +/* This might be sample-array data, block-array data, or alloc_large data. */ +{ +#ifdef FMEMZERO + FMEMZERO(target, bytestozero); +#else + register char FAR * ptr = (char FAR *) target; + register size_t count; + + for (count = bytestozero; count > 0; count--) { + *ptr++ = 0; + } +#endif +} diff --git a/Engine/lib/ljpeg/jversion.h b/Engine/lib/ljpeg/jversion.h new file mode 100644 index 000000000..6472c58d3 --- /dev/null +++ b/Engine/lib/ljpeg/jversion.h @@ -0,0 +1,14 @@ +/* + * jversion.h + * + * Copyright (C) 1991-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file contains software version identification. + */ + + +#define JVERSION "6b 27-Mar-1998" + +#define JCOPYRIGHT "Copyright (C) 1998, Thomas G. Lane" diff --git a/Engine/lib/ljpeg/obsolete/jmemansi.c b/Engine/lib/ljpeg/obsolete/jmemansi.c new file mode 100644 index 000000000..481643468 --- /dev/null +++ b/Engine/lib/ljpeg/obsolete/jmemansi.c @@ -0,0 +1,170 @@ +#if defined(__MACOSX__) +/* + * jmemansi.c + * + * Copyright (C) 1992-1996, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file provides a simple generic implementation of the system- + * dependent portion of the JPEG memory manager. This implementation + * assumes that you have the ANSI-standard library routine tmpfile(). + * Also, the problem of determining the amount of memory available + * is shoved onto the user. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jmemsys.h" /* import the system-dependent declarations */ + +#ifndef HAVE_STDLIB_H /* should declare malloc(),free() */ +extern void * malloc JPP((size_t size)); +extern void free JPP((void *ptr)); +#endif + +#ifndef SEEK_SET /* pre-ANSI systems may not define this; */ +#define SEEK_SET 0 /* if not, assume 0 is correct */ +#endif + + +/* + * Memory allocation and freeing are controlled by the regular library + * routines malloc() and free(). + */ + +GLOBAL(void *) +jpeg_get_small (j_common_ptr cinfo, jpeg_size_t sizeofobject) +{ + return (void *) malloc(sizeofobject); +} + +GLOBAL(void) +jpeg_free_small (j_common_ptr cinfo, void * object, jpeg_size_t sizeofobject) +{ + free(object); +} + + +/* + * "Large" objects are treated the same as "small" ones. + * NB: although we include FAR keywords in the routine declarations, + * this file won't actually work in 80x86 small/medium model; at least, + * you probably won't be able to process useful-size images in only 64KB. + */ + +GLOBAL(void FAR *) +jpeg_get_large (j_common_ptr cinfo, jpeg_size_t sizeofobject) +{ + return (void FAR *) malloc(sizeofobject); +} + +GLOBAL(void) +jpeg_free_large (j_common_ptr cinfo, void FAR * object, jpeg_size_t sizeofobject) +{ + free(object); +} + + +/* + * This routine computes the total memory space available for allocation. + * It's impossible to do this in a portable way; our current solution is + * to make the user tell us (with a default value set at compile time). + * If you can actually get the available space, it's a good idea to subtract + * a slop factor of 5% or so. + */ + +#ifndef DEFAULT_MAX_MEM /* so can override from makefile */ +#define DEFAULT_MAX_MEM 1000000L /* default: one megabyte */ +#endif + +GLOBAL(long) +jpeg_mem_available (j_common_ptr cinfo, long min_bytes_needed, + long max_bytes_needed, long already_allocated) +{ + return cinfo->mem->max_memory_to_use - already_allocated; +} + + +/* + * Backing store (temporary file) management. + * Backing store objects are only used when the value returned by + * jpeg_mem_available is less than the total space needed. You can dispense + * with these routines if you have plenty of virtual memory; see jmemnobs.c. + */ + + +METHODDEF(void) +read_backing_store (j_common_ptr cinfo, backing_store_ptr info, + void FAR * buffer_address, + long file_offset, long byte_count) +{ + if (fseek(info->temp_file, file_offset, SEEK_SET)) + ERREXIT(cinfo, JERR_TFILE_SEEK); + if (JFREAD(info->temp_file, buffer_address, byte_count) + != (size_t) byte_count) + ERREXIT(cinfo, JERR_TFILE_READ); +} + + +METHODDEF(void) +write_backing_store (j_common_ptr cinfo, backing_store_ptr info, + void FAR * buffer_address, + long file_offset, long byte_count) +{ + if (fseek(info->temp_file, file_offset, SEEK_SET)) + ERREXIT(cinfo, JERR_TFILE_SEEK); + if (JFWRITE(info->temp_file, buffer_address, byte_count) + != (size_t) byte_count) + ERREXIT(cinfo, JERR_TFILE_WRITE); +} + + +METHODDEF(void) +close_backing_store (j_common_ptr cinfo, backing_store_ptr info) +{ + fclose(info->temp_file); + /* Since this implementation uses tmpfile() to create the file, + * no explicit file deletion is needed. + */ +} + + +/* + * Initial opening of a backing-store object. + * + * This version uses tmpfile(), which constructs a suitable file name + * behind the scenes. We don't have to use info->temp_name[] at all; + * indeed, we can't even find out the actual name of the temp file. + */ + +GLOBAL(void) +jpeg_open_backing_store (j_common_ptr cinfo, backing_store_ptr info, + long total_bytes_needed) +{ + if ((info->temp_file = tmpfile()) == NULL) + ERREXITS(cinfo, JERR_TFILE_CREATE, ""); + info->read_backing_store = read_backing_store; + info->write_backing_store = write_backing_store; + info->close_backing_store = close_backing_store; +} + + +/* + * These routines take care of any system-dependent initialization and + * cleanup required. + */ + +GLOBAL(long) +jpeg_mem_init (j_common_ptr cinfo) +{ + return DEFAULT_MAX_MEM; /* default for max_memory_to_use */ +} + +GLOBAL(void) +jpeg_mem_term (j_common_ptr cinfo) +{ + /* no work */ +} + +#endif \ No newline at end of file diff --git a/Engine/lib/ljpeg/obsolete/jmemdos.c b/Engine/lib/ljpeg/obsolete/jmemdos.c new file mode 100644 index 000000000..ea3c065c8 --- /dev/null +++ b/Engine/lib/ljpeg/obsolete/jmemdos.c @@ -0,0 +1,640 @@ +#if 0 +/* + * jmemdos.c + * + * Copyright (C) 1992-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file provides an MS-DOS-compatible implementation of the system- + * dependent portion of the JPEG memory manager. Temporary data can be + * stored in extended or expanded memory as well as in regular DOS files. + * + * If you use this file, you must be sure that NEED_FAR_POINTERS is defined + * if you compile in a small-data memory model; it should NOT be defined if + * you use a large-data memory model. This file is not recommended if you + * are using a flat-memory-space 386 environment such as DJGCC or Watcom C. + * Also, this code will NOT work if struct fields are aligned on greater than + * 2-byte boundaries. + * + * Based on code contributed by Ge' Weijers. + */ + +/* + * If you have both extended and expanded memory, you may want to change the + * order in which they are tried in jopen_backing_store. On a 286 machine + * expanded memory is usually faster, since extended memory access involves + * an expensive protected-mode-and-back switch. On 386 and better, extended + * memory is usually faster. As distributed, the code tries extended memory + * first (what? not everyone has a 386? :-). + * + * You can disable use of extended/expanded memory entirely by altering these + * definitions or overriding them from the Makefile (eg, -DEMS_SUPPORTED=0). + */ + +#ifndef XMS_SUPPORTED +#define XMS_SUPPORTED 1 +#endif +#ifndef EMS_SUPPORTED +#define EMS_SUPPORTED 1 +#endif + + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jmemsys.h" /* import the system-dependent declarations */ + +#ifndef HAVE_STDLIB_H /* should declare these */ +extern void * malloc JPP((size_t size)); +extern void free JPP((void *ptr)); +extern char * getenv JPP((const char * name)); +#endif + +#ifdef NEED_FAR_POINTERS + +#ifdef __TURBOC__ +/* These definitions work for Borland C (Turbo C) */ +#include /* need farmalloc(), farfree() */ +#define far_malloc(x) farmalloc(x) +#define far_free(x) farfree(x) +#else +/* These definitions work for Microsoft C and compatible compilers */ +#include /* need _fmalloc(), _ffree() */ +#define far_malloc(x) _fmalloc(x) +#define far_free(x) _ffree(x) +#endif + +#else /* not NEED_FAR_POINTERS */ + +#define far_malloc(x) malloc(x) +#define far_free(x) free(x) + +#endif /* NEED_FAR_POINTERS */ + +#ifdef DONT_USE_B_MODE /* define mode parameters for fopen() */ +#define READ_BINARY "r" +#else +#define READ_BINARY "rb" +#endif + +#ifndef USE_MSDOS_MEMMGR /* make sure user got configuration right */ + You forgot to define USE_MSDOS_MEMMGR in jconfig.h. /* deliberate syntax error */ +#endif + +#if MAX_ALLOC_CHUNK >= 65535L /* make sure jconfig.h got this right */ + MAX_ALLOC_CHUNK should be less than 64K. /* deliberate syntax error */ +#endif + + +/* + * Declarations for assembly-language support routines (see jmemdosa.asm). + * + * The functions are declared "far" as are all their pointer arguments; + * this ensures the assembly source code will work regardless of the + * compiler memory model. We assume "short" is 16 bits, "long" is 32. + */ + +typedef void far * XMSDRIVER; /* actually a pointer to code */ +typedef struct { /* registers for calling XMS driver */ + unsigned short ax, dx, bx; + void far * ds_si; + } XMScontext; +typedef struct { /* registers for calling EMS driver */ + unsigned short ax, dx, bx; + void far * ds_si; + } EMScontext; + +extern short far jdos_open JPP((short far * handle, char far * filename)); +extern short far jdos_close JPP((short handle)); +extern short far jdos_seek JPP((short handle, long offset)); +extern short far jdos_read JPP((short handle, void far * buffer, + unsigned short count)); +extern short far jdos_write JPP((short handle, void far * buffer, + unsigned short count)); +extern void far jxms_getdriver JPP((XMSDRIVER far *)); +extern void far jxms_calldriver JPP((XMSDRIVER, XMScontext far *)); +extern short far jems_available JPP((void)); +extern void far jems_calldriver JPP((EMScontext far *)); + + +/* + * Selection of a file name for a temporary file. + * This is highly system-dependent, and you may want to customize it. + */ + +static int next_file_num; /* to distinguish among several temp files */ + +LOCAL(void) +select_file_name (char * fname) +{ + const char * env; + char * ptr; + FILE * tfile; + + /* Keep generating file names till we find one that's not in use */ + for (;;) { + /* Get temp directory name from environment TMP or TEMP variable; + * if none, use "." + */ + if ((env = (const char *) getenv("TMP")) == NULL) + if ((env = (const char *) getenv("TEMP")) == NULL) + env = "."; + if (*env == '\0') /* null string means "." */ + env = "."; + ptr = fname; /* copy name to fname */ + while (*env != '\0') + *ptr++ = *env++; + if (ptr[-1] != '\\' && ptr[-1] != '/') + *ptr++ = '\\'; /* append backslash if not in env variable */ + /* Append a suitable file name */ + next_file_num++; /* advance counter */ + sprintf(ptr, "JPG%03d.TMP", next_file_num); + /* Probe to see if file name is already in use */ + if ((tfile = fopen(fname, READ_BINARY)) == NULL) + break; + fclose(tfile); /* oops, it's there; close tfile & try again */ + } +} + + +/* + * Near-memory allocation and freeing are controlled by the regular library + * routines malloc() and free(). + */ + +GLOBAL(void *) +jpeg_get_small (j_common_ptr cinfo, size_t sizeofobject) +{ + return (void *) malloc(sizeofobject); +} + +GLOBAL(void) +jpeg_free_small (j_common_ptr cinfo, void * object, size_t sizeofobject) +{ + free(object); +} + + +/* + * "Large" objects are allocated in far memory, if possible + */ + +GLOBAL(void FAR *) +jpeg_get_large (j_common_ptr cinfo, size_t sizeofobject) +{ + return (void FAR *) far_malloc(sizeofobject); +} + +GLOBAL(void) +jpeg_free_large (j_common_ptr cinfo, void FAR * object, size_t sizeofobject) +{ + far_free(object); +} + + +/* + * This routine computes the total memory space available for allocation. + * It's impossible to do this in a portable way; our current solution is + * to make the user tell us (with a default value set at compile time). + * If you can actually get the available space, it's a good idea to subtract + * a slop factor of 5% or so. + */ + +#ifndef DEFAULT_MAX_MEM /* so can override from makefile */ +#define DEFAULT_MAX_MEM 300000L /* for total usage about 450K */ +#endif + +GLOBAL(long) +jpeg_mem_available (j_common_ptr cinfo, long min_bytes_needed, + long max_bytes_needed, long already_allocated) +{ + return cinfo->mem->max_memory_to_use - already_allocated; +} + + +/* + * Backing store (temporary file) management. + * Backing store objects are only used when the value returned by + * jpeg_mem_available is less than the total space needed. You can dispense + * with these routines if you have plenty of virtual memory; see jmemnobs.c. + */ + +/* + * For MS-DOS we support three types of backing storage: + * 1. Conventional DOS files. We access these by direct DOS calls rather + * than via the stdio package. This provides a bit better performance, + * but the real reason is that the buffers to be read or written are FAR. + * The stdio library for small-data memory models can't cope with that. + * 2. Extended memory, accessed per the XMS V2.0 specification. + * 3. Expanded memory, accessed per the LIM/EMS 4.0 specification. + * You'll need copies of those specs to make sense of the related code. + * The specs are available by Internet FTP from the SIMTEL archives + * (oak.oakland.edu and its various mirror sites). See files + * pub/msdos/microsoft/xms20.arc and pub/msdos/info/limems41.zip. + */ + + +/* + * Access methods for a DOS file. + */ + + +METHODDEF(void) +read_file_store (j_common_ptr cinfo, backing_store_ptr info, + void FAR * buffer_address, + long file_offset, long byte_count) +{ + if (jdos_seek(info->handle.file_handle, file_offset)) + ERREXIT(cinfo, JERR_TFILE_SEEK); + /* Since MAX_ALLOC_CHUNK is less than 64K, byte_count will be too. */ + if (byte_count > 65535L) /* safety check */ + ERREXIT(cinfo, JERR_BAD_ALLOC_CHUNK); + if (jdos_read(info->handle.file_handle, buffer_address, + (unsigned short) byte_count)) + ERREXIT(cinfo, JERR_TFILE_READ); +} + + +METHODDEF(void) +write_file_store (j_common_ptr cinfo, backing_store_ptr info, + void FAR * buffer_address, + long file_offset, long byte_count) +{ + if (jdos_seek(info->handle.file_handle, file_offset)) + ERREXIT(cinfo, JERR_TFILE_SEEK); + /* Since MAX_ALLOC_CHUNK is less than 64K, byte_count will be too. */ + if (byte_count > 65535L) /* safety check */ + ERREXIT(cinfo, JERR_BAD_ALLOC_CHUNK); + if (jdos_write(info->handle.file_handle, buffer_address, + (unsigned short) byte_count)) + ERREXIT(cinfo, JERR_TFILE_WRITE); +} + + +METHODDEF(void) +close_file_store (j_common_ptr cinfo, backing_store_ptr info) +{ + jdos_close(info->handle.file_handle); /* close the file */ + remove(info->temp_name); /* delete the file */ +/* If your system doesn't have remove(), try unlink() instead. + * remove() is the ANSI-standard name for this function, but + * unlink() was more common in pre-ANSI systems. + */ + TRACEMSS(cinfo, 1, JTRC_TFILE_CLOSE, info->temp_name); +} + + +LOCAL(boolean) +open_file_store (j_common_ptr cinfo, backing_store_ptr info, + long total_bytes_needed) +{ + short handle; + + select_file_name(info->temp_name); + if (jdos_open((short far *) & handle, (char far *) info->temp_name)) { + /* might as well exit since jpeg_open_backing_store will fail anyway */ + ERREXITS(cinfo, JERR_TFILE_CREATE, info->temp_name); + return FALSE; + } + info->handle.file_handle = handle; + info->read_backing_store = read_file_store; + info->write_backing_store = write_file_store; + info->close_backing_store = close_file_store; + TRACEMSS(cinfo, 1, JTRC_TFILE_OPEN, info->temp_name); + return TRUE; /* succeeded */ +} + + +/* + * Access methods for extended memory. + */ + +#if XMS_SUPPORTED + +static XMSDRIVER xms_driver; /* saved address of XMS driver */ + +typedef union { /* either long offset or real-mode pointer */ + long offset; + void far * ptr; + } XMSPTR; + +typedef struct { /* XMS move specification structure */ + long length; + XMSH src_handle; + XMSPTR src; + XMSH dst_handle; + XMSPTR dst; + } XMSspec; + +#define ODD(X) (((X) & 1L) != 0) + + +METHODDEF(void) +read_xms_store (j_common_ptr cinfo, backing_store_ptr info, + void FAR * buffer_address, + long file_offset, long byte_count) +{ + XMScontext ctx; + XMSspec spec; + char endbuffer[2]; + + /* The XMS driver can't cope with an odd length, so handle the last byte + * specially if byte_count is odd. We don't expect this to be common. + */ + + spec.length = byte_count & (~ 1L); + spec.src_handle = info->handle.xms_handle; + spec.src.offset = file_offset; + spec.dst_handle = 0; + spec.dst.ptr = buffer_address; + + ctx.ds_si = (void far *) & spec; + ctx.ax = 0x0b00; /* EMB move */ + jxms_calldriver(xms_driver, (XMScontext far *) & ctx); + if (ctx.ax != 1) + ERREXIT(cinfo, JERR_XMS_READ); + + if (ODD(byte_count)) { + read_xms_store(cinfo, info, (void FAR *) endbuffer, + file_offset + byte_count - 1L, 2L); + ((char FAR *) buffer_address)[byte_count - 1L] = endbuffer[0]; + } +} + + +METHODDEF(void) +write_xms_store (j_common_ptr cinfo, backing_store_ptr info, + void FAR * buffer_address, + long file_offset, long byte_count) +{ + XMScontext ctx; + XMSspec spec; + char endbuffer[2]; + + /* The XMS driver can't cope with an odd length, so handle the last byte + * specially if byte_count is odd. We don't expect this to be common. + */ + + spec.length = byte_count & (~ 1L); + spec.src_handle = 0; + spec.src.ptr = buffer_address; + spec.dst_handle = info->handle.xms_handle; + spec.dst.offset = file_offset; + + ctx.ds_si = (void far *) & spec; + ctx.ax = 0x0b00; /* EMB move */ + jxms_calldriver(xms_driver, (XMScontext far *) & ctx); + if (ctx.ax != 1) + ERREXIT(cinfo, JERR_XMS_WRITE); + + if (ODD(byte_count)) { + read_xms_store(cinfo, info, (void FAR *) endbuffer, + file_offset + byte_count - 1L, 2L); + endbuffer[0] = ((char FAR *) buffer_address)[byte_count - 1L]; + write_xms_store(cinfo, info, (void FAR *) endbuffer, + file_offset + byte_count - 1L, 2L); + } +} + + +METHODDEF(void) +close_xms_store (j_common_ptr cinfo, backing_store_ptr info) +{ + XMScontext ctx; + + ctx.dx = info->handle.xms_handle; + ctx.ax = 0x0a00; + jxms_calldriver(xms_driver, (XMScontext far *) & ctx); + TRACEMS1(cinfo, 1, JTRC_XMS_CLOSE, info->handle.xms_handle); + /* we ignore any error return from the driver */ +} + + +LOCAL(boolean) +open_xms_store (j_common_ptr cinfo, backing_store_ptr info, + long total_bytes_needed) +{ + XMScontext ctx; + + /* Get address of XMS driver */ + jxms_getdriver((XMSDRIVER far *) & xms_driver); + if (xms_driver == NULL) + return FALSE; /* no driver to be had */ + + /* Get version number, must be >= 2.00 */ + ctx.ax = 0x0000; + jxms_calldriver(xms_driver, (XMScontext far *) & ctx); + if (ctx.ax < (unsigned short) 0x0200) + return FALSE; + + /* Try to get space (expressed in kilobytes) */ + ctx.dx = (unsigned short) ((total_bytes_needed + 1023L) >> 10); + ctx.ax = 0x0900; + jxms_calldriver(xms_driver, (XMScontext far *) & ctx); + if (ctx.ax != 1) + return FALSE; + + /* Succeeded, save the handle and away we go */ + info->handle.xms_handle = ctx.dx; + info->read_backing_store = read_xms_store; + info->write_backing_store = write_xms_store; + info->close_backing_store = close_xms_store; + TRACEMS1(cinfo, 1, JTRC_XMS_OPEN, ctx.dx); + return TRUE; /* succeeded */ +} + +#endif /* XMS_SUPPORTED */ + + +/* + * Access methods for expanded memory. + */ + +#if EMS_SUPPORTED + +/* The EMS move specification structure requires word and long fields aligned + * at odd byte boundaries. Some compilers will align struct fields at even + * byte boundaries. While it's usually possible to force byte alignment, + * that causes an overall performance penalty and may pose problems in merging + * JPEG into a larger application. Instead we accept some rather dirty code + * here. Note this code would fail if the hardware did not allow odd-byte + * word & long accesses, but all 80x86 CPUs do. + */ + +typedef void far * EMSPTR; + +typedef union { /* EMS move specification structure */ + long length; /* It's easy to access first 4 bytes */ + char bytes[18]; /* Misaligned fields in here! */ + } EMSspec; + +/* Macros for accessing misaligned fields */ +#define FIELD_AT(spec,offset,type) (*((type *) &(spec.bytes[offset]))) +#define SRC_TYPE(spec) FIELD_AT(spec,4,char) +#define SRC_HANDLE(spec) FIELD_AT(spec,5,EMSH) +#define SRC_OFFSET(spec) FIELD_AT(spec,7,unsigned short) +#define SRC_PAGE(spec) FIELD_AT(spec,9,unsigned short) +#define SRC_PTR(spec) FIELD_AT(spec,7,EMSPTR) +#define DST_TYPE(spec) FIELD_AT(spec,11,char) +#define DST_HANDLE(spec) FIELD_AT(spec,12,EMSH) +#define DST_OFFSET(spec) FIELD_AT(spec,14,unsigned short) +#define DST_PAGE(spec) FIELD_AT(spec,16,unsigned short) +#define DST_PTR(spec) FIELD_AT(spec,14,EMSPTR) + +#define EMSPAGESIZE 16384L /* gospel, see the EMS specs */ + +#define HIBYTE(W) (((W) >> 8) & 0xFF) +#define LOBYTE(W) ((W) & 0xFF) + + +METHODDEF(void) +read_ems_store (j_common_ptr cinfo, backing_store_ptr info, + void FAR * buffer_address, + long file_offset, long byte_count) +{ + EMScontext ctx; + EMSspec spec; + + spec.length = byte_count; + SRC_TYPE(spec) = 1; + SRC_HANDLE(spec) = info->handle.ems_handle; + SRC_PAGE(spec) = (unsigned short) (file_offset / EMSPAGESIZE); + SRC_OFFSET(spec) = (unsigned short) (file_offset % EMSPAGESIZE); + DST_TYPE(spec) = 0; + DST_HANDLE(spec) = 0; + DST_PTR(spec) = buffer_address; + + ctx.ds_si = (void far *) & spec; + ctx.ax = 0x5700; /* move memory region */ + jems_calldriver((EMScontext far *) & ctx); + if (HIBYTE(ctx.ax) != 0) + ERREXIT(cinfo, JERR_EMS_READ); +} + + +METHODDEF(void) +write_ems_store (j_common_ptr cinfo, backing_store_ptr info, + void FAR * buffer_address, + long file_offset, long byte_count) +{ + EMScontext ctx; + EMSspec spec; + + spec.length = byte_count; + SRC_TYPE(spec) = 0; + SRC_HANDLE(spec) = 0; + SRC_PTR(spec) = buffer_address; + DST_TYPE(spec) = 1; + DST_HANDLE(spec) = info->handle.ems_handle; + DST_PAGE(spec) = (unsigned short) (file_offset / EMSPAGESIZE); + DST_OFFSET(spec) = (unsigned short) (file_offset % EMSPAGESIZE); + + ctx.ds_si = (void far *) & spec; + ctx.ax = 0x5700; /* move memory region */ + jems_calldriver((EMScontext far *) & ctx); + if (HIBYTE(ctx.ax) != 0) + ERREXIT(cinfo, JERR_EMS_WRITE); +} + + +METHODDEF(void) +close_ems_store (j_common_ptr cinfo, backing_store_ptr info) +{ + EMScontext ctx; + + ctx.ax = 0x4500; + ctx.dx = info->handle.ems_handle; + jems_calldriver((EMScontext far *) & ctx); + TRACEMS1(cinfo, 1, JTRC_EMS_CLOSE, info->handle.ems_handle); + /* we ignore any error return from the driver */ +} + + +LOCAL(boolean) +open_ems_store (j_common_ptr cinfo, backing_store_ptr info, + long total_bytes_needed) +{ + EMScontext ctx; + + /* Is EMS driver there? */ + if (! jems_available()) + return FALSE; + + /* Get status, make sure EMS is OK */ + ctx.ax = 0x4000; + jems_calldriver((EMScontext far *) & ctx); + if (HIBYTE(ctx.ax) != 0) + return FALSE; + + /* Get version, must be >= 4.0 */ + ctx.ax = 0x4600; + jems_calldriver((EMScontext far *) & ctx); + if (HIBYTE(ctx.ax) != 0 || LOBYTE(ctx.ax) < 0x40) + return FALSE; + + /* Try to allocate requested space */ + ctx.ax = 0x4300; + ctx.bx = (unsigned short) ((total_bytes_needed + EMSPAGESIZE-1L) / EMSPAGESIZE); + jems_calldriver((EMScontext far *) & ctx); + if (HIBYTE(ctx.ax) != 0) + return FALSE; + + /* Succeeded, save the handle and away we go */ + info->handle.ems_handle = ctx.dx; + info->read_backing_store = read_ems_store; + info->write_backing_store = write_ems_store; + info->close_backing_store = close_ems_store; + TRACEMS1(cinfo, 1, JTRC_EMS_OPEN, ctx.dx); + return TRUE; /* succeeded */ +} + +#endif /* EMS_SUPPORTED */ + + +/* + * Initial opening of a backing-store object. + */ + +GLOBAL(void) +jpeg_open_backing_store (j_common_ptr cinfo, backing_store_ptr info, + long total_bytes_needed) +{ + /* Try extended memory, then expanded memory, then regular file. */ +#if XMS_SUPPORTED + if (open_xms_store(cinfo, info, total_bytes_needed)) + return; +#endif +#if EMS_SUPPORTED + if (open_ems_store(cinfo, info, total_bytes_needed)) + return; +#endif + if (open_file_store(cinfo, info, total_bytes_needed)) + return; + ERREXITS(cinfo, JERR_TFILE_CREATE, ""); +} + + +/* + * These routines take care of any system-dependent initialization and + * cleanup required. + */ + +GLOBAL(long) +jpeg_mem_init (j_common_ptr cinfo) +{ + next_file_num = 0; /* initialize temp file name generator */ + return DEFAULT_MAX_MEM; /* default for max_memory_to_use */ +} + +GLOBAL(void) +jpeg_mem_term (j_common_ptr cinfo) +{ + /* Microsoft C, at least in v6.00A, will not successfully reclaim freed + * blocks of size > 32Kbytes unless we give it a kick in the rear, like so: + */ +#ifdef NEED_FHEAPMIN + _fheapmin(); +#endif +} +#endif \ No newline at end of file diff --git a/Engine/lib/ljpeg/obsolete/jmemmac.c b/Engine/lib/ljpeg/obsolete/jmemmac.c new file mode 100644 index 000000000..8aa0d9477 --- /dev/null +++ b/Engine/lib/ljpeg/obsolete/jmemmac.c @@ -0,0 +1,292 @@ +/* + * jmemmac.c + * + * Copyright (C) 1992-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * jmemmac.c provides an Apple Macintosh implementation of the system- + * dependent portion of the JPEG memory manager. + * + * If you use jmemmac.c, then you must define USE_MAC_MEMMGR in the + * JPEG_INTERNALS part of jconfig.h. + * + * jmemmac.c uses the Macintosh toolbox routines NewPtr and DisposePtr + * instead of malloc and free. It accurately determines the amount of + * memory available by using CompactMem. Notice that if left to its + * own devices, this code can chew up all available space in the + * application's zone, with the exception of the rather small "slop" + * factor computed in jpeg_mem_available(). The application can ensure + * that more space is left over by reducing max_memory_to_use. + * + * Large images are swapped to disk using temporary files and System 7.0+'s + * temporary folder functionality. + * + * Note that jmemmac.c depends on two features of MacOS that were first + * introduced in System 7: FindFolder and the FSSpec-based calls. + * If your application uses jmemmac.c and is run under System 6 or earlier, + * and the jpeg library decides it needs a temporary file, it will abort, + * printing error messages about requiring System 7. (If no temporary files + * are created, it will run fine.) + * + * If you want to use jmemmac.c in an application that might be used with + * System 6 or earlier, then you should remove dependencies on FindFolder + * and the FSSpec calls. You will need to replace FindFolder with some + * other mechanism for finding a place to put temporary files, and you + * should replace the FSSpec calls with their HFS equivalents: + * + * FSpDelete -> HDelete + * FSpGetFInfo -> HGetFInfo + * FSpCreate -> HCreate + * FSpOpenDF -> HOpen *** Note: not HOpenDF *** + * FSMakeFSSpec -> (fill in spec by hand.) + * + * (Use HOpen instead of HOpenDF. HOpen is just a glue-interface to PBHOpen, + * which is on all HFS macs. HOpenDF is a System 7 addition which avoids the + * ages-old problem of names starting with a period.) + * + * Contributed by Sam Bushell (jsam@iagu.on.net) and + * Dan Gildor (gyld@in-touch.com). + */ +#if defined(__APPLE__) + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jmemsys.h" /* import the system-dependent declarations */ + +#ifndef USE_MAC_MEMMGR /* make sure user got configuration right */ + You forgot to define USE_MAC_MEMMGR in jconfig.h. /* deliberate syntax error */ +#endif + +#include /* we use the MacOS memory manager */ +#include /* we use the MacOS File stuff */ +#include /* we use the MacOS HFS stuff */ +#include /* for smSystemScript */ +#include /* we use Gestalt to test for specific functionality */ + +#ifndef TEMP_FILE_NAME /* can override from jconfig.h or Makefile */ +#define TEMP_FILE_NAME "JPG%03d.TMP" +#endif + +static int next_file_num; /* to distinguish among several temp files */ + + +/* + * Memory allocation and freeing are controlled by the MacOS library + * routines NewPtr() and DisposePtr(), which allocate fixed-address + * storage. Unfortunately, the IJG library isn't smart enough to cope + * with relocatable storage. + */ + +GLOBAL(void *) +jpeg_get_small (j_common_ptr cinfo, size_t sizeofobject) +{ + return (void *) NewPtr(sizeofobject); +} + +GLOBAL(void) +jpeg_free_small (j_common_ptr cinfo, void * object, size_t sizeofobject) +{ + DisposePtr((Ptr) object); +} + + +/* + * "Large" objects are treated the same as "small" ones. + * NB: we include FAR keywords in the routine declarations simply for + * consistency with the rest of the IJG code; FAR should expand to empty + * on rational architectures like the Mac. + */ + +GLOBAL(void FAR *) +jpeg_get_large (j_common_ptr cinfo, size_t sizeofobject) +{ + return (void FAR *) NewPtr(sizeofobject); +} + +GLOBAL(void) +jpeg_free_large (j_common_ptr cinfo, void FAR * object, size_t sizeofobject) +{ + DisposePtr((Ptr) object); +} + + +/* + * This routine computes the total memory space available for allocation. + */ + +GLOBAL(long) +jpeg_mem_available (j_common_ptr cinfo, long min_bytes_needed, + long max_bytes_needed, long already_allocated) +{ + long limit = cinfo->mem->max_memory_to_use - already_allocated; + long slop, mem; + + /* Don't ask for more than what application has told us we may use */ + if (max_bytes_needed > limit && limit > 0) + max_bytes_needed = limit; + /* Find whether there's a big enough free block in the heap. + * CompactMem tries to create a contiguous block of the requested size, + * and then returns the size of the largest free block (which could be + * much more or much less than we asked for). + * We add some slop to ensure we don't use up all available memory. + */ + slop = max_bytes_needed / 16 + 32768L; + mem = CompactMem(max_bytes_needed + slop) - slop; + if (mem < 0) + mem = 0; /* sigh, couldn't even get the slop */ + /* Don't take more than the application says we can have */ + if (mem > limit && limit > 0) + mem = limit; + return mem; +} + + +/* + * Backing store (temporary file) management. + * Backing store objects are only used when the value returned by + * jpeg_mem_available is less than the total space needed. You can dispense + * with these routines if you have plenty of virtual memory; see jmemnobs.c. + */ + + +METHODDEF(void) +read_backing_store (j_common_ptr cinfo, backing_store_ptr info, + void FAR * buffer_address, + long file_offset, long byte_count) +{ + long bytes = byte_count; + long retVal; + + if ( SetFPos ( info->temp_file, fsFromStart, file_offset ) != noErr ) + ERREXIT(cinfo, JERR_TFILE_SEEK); + + retVal = FSRead ( info->temp_file, &bytes, + (unsigned char *) buffer_address ); + if ( retVal != noErr || bytes != byte_count ) + ERREXIT(cinfo, JERR_TFILE_READ); +} + + +METHODDEF(void) +write_backing_store (j_common_ptr cinfo, backing_store_ptr info, + void FAR * buffer_address, + long file_offset, long byte_count) +{ + long bytes = byte_count; + long retVal; + + if ( SetFPos ( info->temp_file, fsFromStart, file_offset ) != noErr ) + ERREXIT(cinfo, JERR_TFILE_SEEK); + + retVal = FSWrite ( info->temp_file, &bytes, + (unsigned char *) buffer_address ); + if ( retVal != noErr || bytes != byte_count ) + ERREXIT(cinfo, JERR_TFILE_WRITE); +} + + +METHODDEF(void) +close_backing_store (j_common_ptr cinfo, backing_store_ptr info) +{ + FSClose ( info->temp_file ); + FSpDelete ( &(info->tempSpec) ); +} + + +/* + * Initial opening of a backing-store object. + * + * This version uses FindFolder to find the Temporary Items folder, + * and puts the temporary file in there. + */ + +GLOBAL(void) +jpeg_open_backing_store (j_common_ptr cinfo, backing_store_ptr info, + long total_bytes_needed) +{ + short tmpRef, vRefNum; + long dirID; + FInfo finderInfo; + FSSpec theSpec; + Str255 fName; + OSErr osErr; + long gestaltResponse = 0; + + /* Check that FSSpec calls are available. */ + osErr = Gestalt( gestaltFSAttr, &gestaltResponse ); + if ( ( osErr != noErr ) + || !( gestaltResponse & (1<temp_name, TEMP_FILE_NAME, next_file_num); + strcpy ( (Ptr)fName+1, info->temp_name ); + *fName = strlen (info->temp_name); + osErr = FSMakeFSSpec ( vRefNum, dirID, fName, &theSpec ); + + if ( (osErr = FSpGetFInfo ( &theSpec, &finderInfo ) ) != noErr ) + break; + } + + osErr = FSpCreate ( &theSpec, '????', '????', smSystemScript ); + if ( osErr != noErr ) + ERREXITS(cinfo, JERR_TFILE_CREATE, info->temp_name); + + osErr = FSpOpenDF ( &theSpec, fsRdWrPerm, &(info->temp_file) ); + if ( osErr != noErr ) + ERREXITS(cinfo, JERR_TFILE_CREATE, info->temp_name); + + info->tempSpec = theSpec; + + info->read_backing_store = read_backing_store; + info->write_backing_store = write_backing_store; + info->close_backing_store = close_backing_store; + TRACEMSS(cinfo, 1, JTRC_TFILE_OPEN, info->temp_name); +} + + +/* + * These routines take care of any system-dependent initialization and + * cleanup required. + */ + +GLOBAL(long) +jpeg_mem_init (j_common_ptr cinfo) +{ + next_file_num = 0; + + /* max_memory_to_use will be initialized to FreeMem()'s result; + * the calling application might later reduce it, for example + * to leave room to invoke multiple JPEG objects. + * Note that FreeMem returns the total number of free bytes; + * it may not be possible to allocate a single block of this size. + */ + return FreeMem(); +} + +GLOBAL(void) +jpeg_mem_term (j_common_ptr cinfo) +{ + /* no work */ +} + +#endif \ No newline at end of file diff --git a/Engine/lib/ljpeg/obsolete/jmemname.c b/Engine/lib/ljpeg/obsolete/jmemname.c new file mode 100644 index 000000000..2f4c86980 --- /dev/null +++ b/Engine/lib/ljpeg/obsolete/jmemname.c @@ -0,0 +1,279 @@ +#if 0 +/* + * jmemname.c + * + * Copyright (C) 1992-1997, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * For conditions of distribution and use, see the accompanying README file. + * + * This file provides a generic implementation of the system-dependent + * portion of the JPEG memory manager. This implementation assumes that + * you must explicitly construct a name for each temp file. + * Also, the problem of determining the amount of memory available + * is shoved onto the user. + */ + +#define JPEG_INTERNALS +#include "jinclude.h" +#include "jpeglib.h" +#include "jmemsys.h" /* import the system-dependent declarations */ + +#ifndef HAVE_STDLIB_H /* should declare malloc(),free() */ +extern void * malloc JPP((size_t size)); +extern void free JPP((void *ptr)); +#endif + +#ifndef SEEK_SET /* pre-ANSI systems may not define this; */ +#define SEEK_SET 0 /* if not, assume 0 is correct */ +#endif + +#ifdef DONT_USE_B_MODE /* define mode parameters for fopen() */ +#define READ_BINARY "r" +#define RW_BINARY "w+" +#else +#ifdef VMS /* VMS is very nonstandard */ +#define READ_BINARY "rb", "ctx=stm" +#define RW_BINARY "w+b", "ctx=stm" +#else /* standard ANSI-compliant case */ +#define READ_BINARY "rb" +#define RW_BINARY "w+b" +#endif +#endif + + +/* + * Selection of a file name for a temporary file. + * This is system-dependent! + * + * The code as given is suitable for most Unix systems, and it is easily + * modified for most non-Unix systems. Some notes: + * 1. The temp file is created in the directory named by TEMP_DIRECTORY. + * The default value is /usr/tmp, which is the conventional place for + * creating large temp files on Unix. On other systems you'll probably + * want to change the file location. You can do this by editing the + * #define, or (preferred) by defining TEMP_DIRECTORY in jconfig.h. + * + * 2. If you need to change the file name as well as its location, + * you can override the TEMP_FILE_NAME macro. (Note that this is + * actually a printf format string; it must contain %s and %d.) + * Few people should need to do this. + * + * 3. mktemp() is used to ensure that multiple processes running + * simultaneously won't select the same file names. If your system + * doesn't have mktemp(), define NO_MKTEMP to do it the hard way. + * (If you don't have , also define NO_ERRNO_H.) + * + * 4. You probably want to define NEED_SIGNAL_CATCHER so that cjpeg.c/djpeg.c + * will cause the temp files to be removed if you stop the program early. + */ + +#ifndef TEMP_DIRECTORY /* can override from jconfig.h or Makefile */ +#define TEMP_DIRECTORY "/usr/tmp/" /* recommended setting for Unix */ +#endif + +static int next_file_num; /* to distinguish among several temp files */ + +#ifdef NO_MKTEMP + +#ifndef TEMP_FILE_NAME /* can override from jconfig.h or Makefile */ +#define TEMP_FILE_NAME "%sJPG%03d.TMP" +#endif + +#ifndef NO_ERRNO_H +#include /* to define ENOENT */ +#endif + +/* ANSI C specifies that errno is a macro, but on older systems it's more + * likely to be a plain int variable. And not all versions of errno.h + * bother to declare it, so we have to in order to be most portable. Thus: + */ +#ifndef errno +extern int errno; +#endif + + +LOCAL(void) +select_file_name (char * fname) +{ + FILE * tfile; + + /* Keep generating file names till we find one that's not in use */ + for (;;) { + next_file_num++; /* advance counter */ + sprintf(fname, TEMP_FILE_NAME, TEMP_DIRECTORY, next_file_num); + if ((tfile = fopen(fname, READ_BINARY)) == NULL) { + /* fopen could have failed for a reason other than the file not + * being there; for example, file there but unreadable. + * If isn't available, then we cannot test the cause. + */ +#ifdef ENOENT + if (errno != ENOENT) + continue; +#endif + break; + } + fclose(tfile); /* oops, it's there; close tfile & try again */ + } +} + +#else /* ! NO_MKTEMP */ + +/* Note that mktemp() requires the initial filename to end in six X's */ +#ifndef TEMP_FILE_NAME /* can override from jconfig.h or Makefile */ +#define TEMP_FILE_NAME "%sJPG%dXXXXXX" +#endif + +LOCAL(void) +select_file_name (char * fname) +{ + next_file_num++; /* advance counter */ + sprintf(fname, TEMP_FILE_NAME, TEMP_DIRECTORY, next_file_num); + mktemp(fname); /* make sure file name is unique */ + /* mktemp replaces the trailing XXXXXX with a unique string of characters */ +} + +#endif /* NO_MKTEMP */ + + +/* + * Memory allocation and freeing are controlled by the regular library + * routines malloc() and free(). + */ + +GLOBAL(void *) +jpeg_get_small (j_common_ptr cinfo, size_t sizeofobject) +{ + return (void *) malloc(sizeofobject); +} + +GLOBAL(void) +jpeg_free_small (j_common_ptr cinfo, void * object, size_t sizeofobject) +{ + free(object); +} + + +/* + * "Large" objects are treated the same as "small" ones. + * NB: although we include FAR keywords in the routine declarations, + * this file won't actually work in 80x86 small/medium model; at least, + * you probably won't be able to process useful-size images in only 64KB. + */ + +GLOBAL(void FAR *) +jpeg_get_large (j_common_ptr cinfo, size_t sizeofobject) +{ + return (void FAR *) malloc(sizeofobject); +} + +GLOBAL(void) +jpeg_free_large (j_common_ptr cinfo, void FAR * object, size_t sizeofobject) +{ + free(object); +} + + +/* + * This routine computes the total memory space available for allocation. + * It's impossible to do this in a portable way; our current solution is + * to make the user tell us (with a default value set at compile time). + * If you can actually get the available space, it's a good idea to subtract + * a slop factor of 5% or so. + */ + +#ifndef DEFAULT_MAX_MEM /* so can override from makefile */ +#define DEFAULT_MAX_MEM 1000000L /* default: one megabyte */ +#endif + +GLOBAL(long) +jpeg_mem_available (j_common_ptr cinfo, long min_bytes_needed, + long max_bytes_needed, long already_allocated) +{ + return cinfo->mem->max_memory_to_use - already_allocated; +} + + +/* + * Backing store (temporary file) management. + * Backing store objects are only used when the value returned by + * jpeg_mem_available is less than the total space needed. You can dispense + * with these routines if you have plenty of virtual memory; see jmemnobs.c. + */ + + +METHODDEF(void) +read_backing_store (j_common_ptr cinfo, backing_store_ptr info, + void FAR * buffer_address, + long file_offset, long byte_count) +{ + if (fseek(info->temp_file, file_offset, SEEK_SET)) + ERREXIT(cinfo, JERR_TFILE_SEEK); + if (JFREAD(info->temp_file, buffer_address, byte_count) + != (size_t) byte_count) + ERREXIT(cinfo, JERR_TFILE_READ); +} + + +METHODDEF(void) +write_backing_store (j_common_ptr cinfo, backing_store_ptr info, + void FAR * buffer_address, + long file_offset, long byte_count) +{ + if (fseek(info->temp_file, file_offset, SEEK_SET)) + ERREXIT(cinfo, JERR_TFILE_SEEK); + if (JFWRITE(info->temp_file, buffer_address, byte_count) + != (size_t) byte_count) + ERREXIT(cinfo, JERR_TFILE_WRITE); +} + + +METHODDEF(void) +close_backing_store (j_common_ptr cinfo, backing_store_ptr info) +{ + fclose(info->temp_file); /* close the file */ + unlink(info->temp_name); /* delete the file */ +/* If your system doesn't have unlink(), use remove() instead. + * remove() is the ANSI-standard name for this function, but if + * your system was ANSI you'd be using jmemansi.c, right? + */ + TRACEMSS(cinfo, 1, JTRC_TFILE_CLOSE, info->temp_name); +} + + +/* + * Initial opening of a backing-store object. + */ + +GLOBAL(void) +jpeg_open_backing_store (j_common_ptr cinfo, backing_store_ptr info, + long total_bytes_needed) +{ + select_file_name(info->temp_name); + if ((info->temp_file = fopen(info->temp_name, RW_BINARY)) == NULL) + ERREXITS(cinfo, JERR_TFILE_CREATE, info->temp_name); + info->read_backing_store = read_backing_store; + info->write_backing_store = write_backing_store; + info->close_backing_store = close_backing_store; + TRACEMSS(cinfo, 1, JTRC_TFILE_OPEN, info->temp_name); +} + + +/* + * These routines take care of any system-dependent initialization and + * cleanup required. + */ + +GLOBAL(long) +jpeg_mem_init (j_common_ptr cinfo) +{ + next_file_num = 0; /* initialize temp file name generator */ + return DEFAULT_MAX_MEM; /* default for max_memory_to_use */ +} + +GLOBAL(void) +jpeg_mem_term (j_common_ptr cinfo) +{ + /* no work */ +} + +#endif \ No newline at end of file diff --git a/Engine/lib/lmng/CHANGES b/Engine/lib/lmng/CHANGES new file mode 100644 index 000000000..eeacf3324 --- /dev/null +++ b/Engine/lib/lmng/CHANGES @@ -0,0 +1,1447 @@ +----------------------------------------------------------- + +1.0.10 (Jul 13th 2007) +---------------------- + +in short: + +intermediate CVS + +------------------- + +bugfixes: + +core: +- fixed some compiler-warnings +- fixed display routines called twice for FULL_MNG support in mozlibmngconf.h +- standard windows dll upgraded to zlib 1.2.3 +- fixed problem with CLON object during readdisplay() (thanks Winfried!) +- added typecast to appease the compiler (G R-P) +- added more SKIPCHUNK conditionals (G R-P) +- added MORE MNG_NO_1_2_4BIT_SUPPORT (G R-P) +- added provisional support for anIM(mpNG) proposal +- added provisional support for ANG proposal + +samples: +- xmngview upgraded to 0.6 (thanks Winfried!) + +contrib: + +doc: + +makefiles: + +autoconf: + +----------------------------------------------------------- + +1.0.9 (jan 30th 2005) +--------------------- + +in short: + +New optimizations save over 20KB on footprint. +Also a few bugfixes and several patches. + +Thanks to those sending in their additions and for testing! + +To turn on the optimizations do: + +#DEFINE MNG_OPTIMIZE_CHUNKINITFREE +#DEFINE MNG_OPTIMIZE_OBJCLEANUP +#DEFINE MNG_OPTIMIZE_CHUNKASSIGN +#DEFINE MNG_OPTIMIZE_CHUNKREADER + +(eg. they're not on by default (yet) !) + +------------------- + +bugfixes: +- fixed chunk pushing mechanism +- fixed bug in writing sBIT for indexed color +- fixed PPLT getchunk/putchunk routines +- fixed MNG_NO_1_2_4BIT_SUPPORT for TBBN1G04.PNG +- cleaned up macro-invocations (thanks to D. Airlie) + +core: +- added more SKIPCHUNK conditionals +- replaced MNG_TWEAK_LARGE_FILES with permanent solution +- improved handling of cheap transparency when 16-bit support is disabled +- added some MNG_SUPPORT_WRITE conditionals +- added function to retrieve current FRAM delay +- added MNG_NO_1_2_4BIT_SUPPORT +- added bgr565_a8 canvas-style (thanks to J. Elvander) +- standard windows dll upgraded to zlib 1.2.2 +- added LITTLEENDIAN/BIGENDIAN fixtures (thanks J.Stiles) +- inclusion of zlib/lcms/ijgsrc6b with <> instead of "" +- added conditional MNG_OPTIMIZE_CHUNKINITFREE +- added conditional MNG_OPTIMIZE_OBJCLEANUP +- added conditional MNG_OPTIMIZE_CHUNKASSIGN +- added conditional MNG_OPTIMIZE_CHUNKREADER +- fixed problem with global PLTE/tRNS + +samples: + +contrib: + +doc: +- patched jng & mng manual pages (Thanks Peter Breitenlohner) + +makefiles: + +autoconf: +- patched makefile.am & configure.in (Thanks Peter Breitenlohner) + +----------------------------------------------------------- + +1.0.8 (aug 5th 2004) +-------------------- + +in short: + +added special data-pushing mechanisms and a few other tid-bits + +------------------- + +bugfixes: +- fixed problem with PAST usage where source > dest + +core: +- added missing get-/put-chunk-jdaa +- added CRC existence & checking flags +- added data-push mechanisms for specialized decoders +- some speed optimizations (thanks to John Stiles) +- defend against using undefined closestream function +- defend against using undefined openstream function +- added check for extreme chunk-lengths +- change worst-case iAlphadepth to 1 for standalone PNGs +- added support for 3+byte pixelsize for JPEG's +- added conditional to allow easier writing of large MNG's + +samples: + +contrib: + +doc: + +makefiles: + +autoconf: + +----------------------------------------------------------- + +1.0.7 (March 21st 2004) +----------------------- + +in short: + +A bunch of new canvas-styles, some bug-fixes, upgraded to latest zlib/lcms +and yet more work to please the Mozilla crowd. +Releasing beta's doesn't seem very responsive, and this one's hardly changed +much anyway. I just wanted to bump to a regular version for Mozilla +re-integration. + +------------------- + +bugfixes: +- fixed inclusion of IJNG chunk for non-JNG use (J.S) +- fixed bug in chunk-storage of SHOW chunk (where from == to) +- fixed bug in promote_g8_g8 with 16bit support off + +core: +- added CANVAS_RGB565 and CANVAS_BGR565 (big thanx to Raphael Assenat!!) +- added CANVAS_RGBA565 and CANVAS_BGRA565 ( -- ditto -- ) +- upgraded to zlib 1.2.1 +- upgraded to lcms 1.11 +- added premultiplied alpha canvas' for RGBA, ARGB, ABGR (thx to John Stiles) +- more optimizations with 16bit support off +- put conditionals around openstream/closestream callbacks. +- fixed typo (MNG_SKIPCHUNK_SAVE -> MNG_SKIPCHUNK_nEED) +- fixed some 64-bit platform compiler warnings + +samples: + +contrib: +- fixed mngtree sample (Raphael) +- added 5-6-5 canvas to SDL sample (Raphael) + +doc: + +makefiles: + +autoconf: + +----------------------------------------------------------- + +1.0.6 (oct 19th 2003) +--------------------- + +in short: + +Final release from beta1. No feedback is good feedback I presume, +so here's 1.0.6-final! + + +------------------- + +bugfixes: + +core: + +samples: + +contrib: + +doc: + +makefiles: + +autoconf: + +----------------------------------------------------------- + +1.0.6-beta1 (sep 14th 2003) +--------------------------- + +in short: + +further footprint-reductions +removing email-addresses + +1.0.6 (final) will be out shortly + +------------------- + +bugfixes: + +core: +- added support for reducing the footprint of libmng by macros that optionally + skip unused chunks, remove 16-bit sample support, remove Delta support, and + remove JNG support, to accomodate Mozilla/Firebird. +- further optional removal of unused functions +- added MNG_NO_SUPPORT_FUNCQUERY conditional +- added iPNGdepth member to pData structure +- added conditionals around MAGN chunk support +- added conditionals around non-VLC chunk support +- added conditionals around "mng_display_go*" and other unused functions +- added more conditionals around "promote" functions +- removed email references as appropriate + +samples: + +contrib: + +doc: + +makefiles: + +autoconf: + +----------------------------------------------------------- + +1.0.6-alpha1 (aug 2nd 2003) +--------------------------- + +in short: + +This is mostly in the light of footprint-reduction to please the Mozilla +crew with a leaner and meaner libmng. See bug 18574 if you're interested: +http://bugzilla.mozilla.org/show_bug.cgi?id=18574 + +------------------- + +bugfixes: +- B719420 - fixed several MNG_APP_CMS problems + +core: +- removed some compiler-warnings +- hiding 12-bit JPEG stuff +- fixed problem with infinite loops during readdisplay() +- added size-optimiation COMPOSE routine usage +- added conditionals around canvas update routines +- added MNG_SKIPCHUNK_cHNK footprint optimizations +- added conditionals around some JNG-supporting code +- added conditionals around 16-bit supporting code +- combined init functions into one function +- replaced nested switches with simple init setup function +- added conditionals zlib and jpeg property accessors +- added size-optimization DIV255B8 routine usage +- added conditionals around 8-bit magn routines +- removed conditionals around 8-bit magn routines +- added MNG_NO_16BIT_SUPPORT and MNG_NO_DELTA_PNG conditionals +- reversed many loops to use decrementing counter +- converted some switches to array references +- removed some redundant checks for iRawlen==0 +- optionally use zlib's crc32 function instead of local mng_update_crc +- bugfix empty "if" statement when 16-bit code is enabled +- restored two misplaced #else/#endif blocks +- added conditionals around "mng_display_go*" and other unused functions +- added MNG_NO_LOOP_SIGNALS_SUPPORTED conditional +- fixed duplicate for-loop +- fixed invalid test in promote_imageobject +- added conditionals around PAST chunk support +- fixed "FOOTPRINT_COMPOSEIV" typo (now "FOOTPRINT_DIV") + +samples: + +contrib: +- updated xmngview +- added MSVC project for creating delta-MNGs: makemng (thanks Alex!) +- added MSVC lib-file for use with the standard libmng.dll (again thanks Alex) + +doc: +- updated readme.contrib + +makefiles: + +autoconf: + +----------------------------------------------------------- + +1.0.5 (mar 1st 2003) +-------------------- + +in short: + +Only a small fix for progressive jpeg suspension problem. + +This is the long-awaited final release containing the new 'dynamic MNG' feature +and bringing MNG compliance to near 100%! + +------------------- + +bugfixes: +- B683152 - libjpeg suspension not always honored correctly + +core: + +samples: + +contrib: + +doc: + +makefiles: + +autoconf: + +----------------------------------------------------------- + +1.0.5-rc3 (jan 20th 2003) +------------------------- + +in short: + +Third release-candidate for the upcoming 1.0.5 version. + +Minor bug-fixes and finalizing the accepted proposal (by official vote) for +the TERM/frame_delay changes on mng-list (nov-dec/2002). + +------------------- + +bugfixes: +- B654627 - fixed SEGV when no gettickcount callback (thanks Adam!) +- B664383 - fixed typo (thanks Dimitri) +- B664911 - fixed buffer overflow during init (thanks Alex!) + +core: +- finalized changes in TERM/final_delay to elected proposal (positive vote) + +samples: + +contrib: + +doc: + +makefiles: + +autoconf: + +----------------------------------------------------------- + +1.0.5-rc2 (dec 9th 2002) +------------------------ + +in short: + +Second release-candidate for the upcoming 1.0.5 version. +This contains fixes for a few minor details reported by the loyal testers. +It fixes some issues with the goframe/golayer/gotime processing and related +stuff. And it adds a way to disable playback-caching from within the MNG, +which is very useful for streaming-MNG encoders (such as gserver!). + +------------------- + +bugfixes: + +core: +- fixed layer- & frame-counting during read() +- changed FRAMECOUNT/LAYERCOUNT/PLAYTIME error to warning +- fixed goframe/golayer/gotime processing +- added support for nEED "MNG 1.1" +- added support for nEED "CACHEOFF"; turn playback caching off for streaming MNG +- fixed magnification bug with object 0 +- added support to get totals for frames/layers/playtime after mng_read() +- fixed some issues in init_app_cms() +- fixed goxxxxx() support for zero values + +samples: + +contrib: + +doc: + +makefiles: + +autoconf: + +----------------------------------------------------------- + +1.0.5-rc1 (nov 1st 2002) +------------------------ + +in short: + +First release-candidate for the upcoming 1.0.5 version. +This fixes a few small problems and brings the TERM/MEND processing, with +respect to interframe_delay as per the current discussion on MNG-list, +up-to-date with the latest proposal. + +------------------- + +bugfixes: + +core: +- fixed initialization of pIds in dISC read routine (Thanks Winfried!) +- fixed issue in freeing evNT chunk (Thanks Winfried!) +- fixed clipping-problem with BACK tiling (Thanks Sakura!) +- fixed processing for multiple objects in MAGN (Thanks Sakura!) +- fixed display of visible target of PAST operation (Thanks Sakura!) +- modified TERM/MEND processing for max(1, TERM_delay, interframe_delay) + +samples: + +contrib: +- fixed typo in Makefile for gtk-mng-view sample + +doc: + +makefiles: + +autoconf: + +----------------------------------------------------------- + +1.0.5-b3 (oct 15th 2002) +------------------------ + +in short: + +Fairly quick after beta2, since that introduced a couple of unfortunate +booboo's and wasn't very workable. It also changes the standard configure +script to build a standard shared object similar to what I intended. + +------------------- + +bugfixes: + +core: +- fixed support for condition=2 in TERM chunk +- fixed trace-constants for PAST chunk +- added mng_status_dynamic to supports function + +samples: + +contrib: + +doc: +- small cosmetic changes in man/libmng.3 + +makefiles: + +autoconf: +- fixed configure.in to build a 'standard' SO primarily + +----------------------------------------------------------- + +1.0.5-b2 (oct 9th 2002) +------------------------ + +in short: + +Second beta for next 1.0.5 release. This addresses some minor problems +detected during testing. It adds the proposed change to the MNG spec as +discussed on the "mng-list" recently; eg. Adam's option 4. +And it adds a little function to check at run-time if the lib is a beta or not. + +------------------- + +bugfixes: + +core: +- fixed chunk-storage for evNT chunk +- fixed dropping mix of frozen/unfrozen objects +- fixed problem with cloned objects marked as invalid +- fixed problem cloning frozen object_buffers +- fixed DISC support +- added proposed change in handling of TERM- & interframe-delay +- added another fix for misplaced TERM chunk +- added check for TERM placement during create/write +- completed support for condition=2 in TERM chunk +- added beta version function & constant + +samples: + +contrib: + +doc: + +makefiles: + +autoconf: + +----------------------------------------------------------- + +1.0.5-b1 (sep 24th 2002) +------------------------ + +in short: + +First beta of a large maintenance release. This completes support of the MNG +specification to nearly 100% (PAST, PROM, delta-images, BACK image+tile). +It adds "dynamic" MNG and a few other neat routines as well as fixes several +bugs reported through SourceForge or to me directly. + +------------------- + +bugfixes: +- B575832 - library has wrong patch version number +- B578572 - remove in 1.0.0! +- B578940 - some functions not implemented +- B581625 - large chunks fail with suspension reads +- B597134 - libmng pollutes the linker namespace + +core: +- added sanity check for improbable chunklengths +- removed eMNGma hack (thanks Dimitri!) +- unimplemented functions return an errorcode now +- added test-option for PNG filter method 192 (= levelling) +- added test-option for PNG filter method 193 (= no filtering) + (both are conditional and only for testing purposes!!!) +- completed PROM support +- completed delta-image support +- completed MAGN support (16-bit functions) +- added HLAPI function to copy a chunk from a read MNG to a newly created MNG +- added option for soft-handling of errors (only for repair software!!!) +- fixed some routine inclusion/exclusion for undefined conditionals +- pre-fixed all internal routines with mng_ +- added symbol MNG_LOCAL (= static) to really local functions +- fixed reading of FRAM with just frame_mode and name +- fixed read/write of MAGN chunk +- added event handling for dynamic MNG +- added 'supports' call to check function availability +- fixed copyright notice in the headers of all libmng modules +- fixed LOOP iteration=0 special case +- re-compiled standard Windows dll with lcms-1.0.9 +- added warning for too much IDAT data +- warnings are ignored by default now +- misplaced TERM is now treated as warning +- fixed color-correction for restore-background handling +- optimized restore-background for bKGD cases +- cleaned up some old stuff +- completed support for BACK image & tiling +- completed support for PAST +- added bgrx8 canvas (filler byte) +- fixed reset_object_detail to clear old buffer +- added in-memory color-correction of abstract images +- added compose over/under routines for PAST processing +- added flip & tile routines for PAST processing + +samples: +- Added new BCB sample for fixing invalid JASC Animation Shop files + (shows new copy_chunks function; use of MNG_SOFTERRORS & static linking) + +contrib: +- added xmngview by Winfried Szukalski (Vielen dank!) +- Updated the Delphi mngview sample to handle dynamic MNGs +- Added Kylix example (simplified port of the Delphi mngview sample) + +doc: +- added diff to add MNG&JNG to a systems 'magic' file (Thanks Winfried) +- fixed docs about using mng_display_resume after display_reset + (should read to use mng_display!) + +makefiles: +- added makefile to build a libmng.dll for MingW + (makefile.mingwdll - thanks to Frank Richter!) + +autoconf: +- fixing libtool version-number to be in line with what it should be + +----------------------------------------------------------- + +1.0.4 (Jun 23rd 2002) +--------------------- + +in short: + +Just some small fixes +Standard dll now compiled with zlib 1.1.4 and lcms 1.0.8 + +------------------- + +bugfixes: +- B495442 - invalid returnvalue in mng_get_suspensionmode +- B495443 - incorrect suspend check in read_databuffer +- B526138 - returned IJGSRC6B calling convention to default for MSVC +- B558212 - off by one error +- B557677 - can't find lcms.h + +core: +- fixed possible compile-problem in cleanup_rowproc +- MNG subimage alpha composite wrong for rgba8 images + +samples: + +contrib: + +doc: + +makefiles: +- fixed check for lcms.h in configure.in + +autoconf: + +----------------------------------------------------------- + +1.0.3 (Sep 18th 2001) +--------------------- + +in short: + +Small cosmetic changes. Cleaning up the contributions. +New makefile for mingw32, and new fbcon example. +Major thanks to Greg for helping out with the *nix stuff! +Note that there's also a separate download for ASM programmers now. +Check http://www.libmng.com for details (download/ports&packages page). + +It may be a while for the next release. I'm "off duty" for the next 8 or +so months... + +Gerard + +------------------- + +bugfixes: +- B459058 - wrong include for lcms headers + +core: +- changed inclusion of lcms.h header for Linux platforms (suggested by Greg) +- added get function for last processed BACK chunk + +samples: +- replaced the gtk & sdl viewer apps with updates by Greg Roelofs + +contrib: + +doc: + +makefiles: +- changed makefile.linux & makefile.unix as suggested by Greg Roelofs + (makefile.linux now compiles with lcms by default) +- added makefile.mingw for mingw32 by Benoit Blanchon (thanks Mate!) + +autoconf: + +----------------------------------------------------------- + +1.0.2 (Jul 7th 2001) +-------------------- + +in short: + +Another maintenance release with a few added extra's. + +------------------- + +bugfixes: +- B421427 - writes wrong format in bKGD and tRNS +- B434583 - compiler-warning if MNG_STORE_CHUNKS undefined + +core: +- added optimization option for MNG-video playback +- added processterm callback +- added late binding errorcode (not used internally) +- fixed memory-leak with delta-images (Thanks Michael!) +- added option to turn off progressive refresh for large images + +samples: + +contrib: + +doc: + +makefiles: + +autoconf: + +----------------------------------------------------------- + +1.0.1 (May 2nd 2001) +-------------------- + +in short: + +Maintenance release. +Fixed several memory-leaks with the help of Gregg Kelly, added/fixed some CMS +handling, exported JPEG functions from standard DLL, and some other minor fixes. + +The CMS fix now makes libmng automagically work in MNG_FULL_CMS mode as a +sRGB compliant system. YOU WILL NEED TO CHANGE THIS IF YOU ARE NOT ON AN sRGB +COMPLIANT SYSTEM AND WANT TO USE CMS!!!! +(look in libmng.h for the proper function-calls) + +------------------- + +bugfixes: + +core: +- added MEND processing callback +- fixed first FRAM_MODE=4 timing problem +- added handle status-copy function (use with care) +- exported JPEG functions from standard DLL +- added BGRA8 canvas with premultiplied alpha (contrib by Gregg Kelly) +- fixed problem with display_reset/display_resume (Thanks Gregg!) +- fixed several memory-leaks (Thanks Gregg!) +- fixed reset_rundata to drop all objects (Thanks again, Gregg!) +- fixed problem with cms profile being created multiple times when both + iCCP & cHRM/gAMA are present (And again... Gregg) +- moved mng_clear_cms to libmng_cms +- added "default" sRGB generation (Thanks Marti!) + +samples: + +contrib: + +doc: + +makefiles: + +autoconf: + +----------------------------------------------------------- + +1.0.0 (Feb 6th 2001) +-------------------- + +in short: + +First public release. Finally(!) + +This is the 0.9.5 CVS version, which will never be released, because I feel it +is now ready for a public release. So apart from the version-numbers here and +there, all other changes are listed under 0.9.5. + +This library will work with every MNG/JNG known and available to me. Note that +there are still parts that need to be coded, and that MNG support is around +90-95% (JNG at 100%). It is however compliant with the latest and greatest +MNG 1.0 specification. + +I hope to dedicate a bit more time this year to finish up full support and fill +in the remaining blanks. But this is coming out of my spare time. And extra +help is always appreciated. + +Please enjoy! + +Gerard + +----------------------------------------------------------- + +0.9.5 (no release) +------------------ + +in short: + +intermediate CVS + +------------------- + +bugfixes: +B129681 - fixed compiler warnings SGI/Irix (thanks Dimitri) + +core: +- fixed compiler-warnings Mozilla (thanks Tim) +- fixed timing-problem with switching framing_modes +- fixed some small compiler warnings (thanks Nikki) + +samples: + +contrib: +- fixed library-paths for MSVC DLL project (thanks Chad) + +doc: + +makefiles: +- added makefile for DJGPP (thanks Silvio) + +autoconf: + +----------------------------------------------------------- + +0.9.4 (Jan 19th 2001) +---------------------- + +in short: + +Now that the MNG spec is at 1.0, this should be the last beta. There's a few +small changes to make it inline with the spec, and a couple of bug-fixes. +This is a serious release-candidate for libmng-1.0!! +Please... test test test test!! + +------------------- + +bugfixes: +B123314 - fixed number of TERM related problems +B123322 - fixed unwanted repetition in mng_readdisplay() +B123443 - fixed by Ralph +B124910 - fixed definition for WIN32_LEAN_AND_MEAN (thanks Chad) +B125750 - fixed by Ralph +B125756 - fixed mixup of data- & function-pointers (thanks Dimitri) +B127517 - changed inclusion of the lcms header file for non-windows platforms + +core: +- version numbers +- fixed possible loop in display_resume() (Thanks Vova!) +- fixed unwanted repetition in mng_readdisplay() +- changed inclusion of the lcms header file for non-windows platforms +- changed IHDR filter_method check for PNGs +- moved restore of object 0 to libmng_display +- added restore of object 0 to TERM processing (B123314) +- fixed TERM delay processing (B123314) +- fixed TERM end processing when count = 0 (B123314) +- changed callback convention for MSVC (Thanks Chad) +- fixed mixup of data- & function-pointers (thanks Dimitri) +- added support for "nEED MNG-1.0" +- added errorcode for MAGN methods +- added errorchecking for MAGN methods +- removed "old" MAGN methods 3 & 4 +- added "new" MAGN methods 3, 4 & 5 +- removed test filter-methods 1 & 65 +- set default level-set for filtertype=64 to all zeroes + +samples: + +contrib: +- added GTK mng-view example by Vova Babin +- added MSVC MNGview sample by Nikolaus Brennig +- updated Jason Summer's mngplg to version 0.9.2 + (that's mngplg-0.9.2 based on libmng-0.9.3 !!!) +- rearranged contrib directory slightly +- added MSVC project to build libmng.dll by Chad Austin + +doc: +- added README.dll +- added README.config + +makefiles: +- added a makefile for MS Visual C++ (Thanks to Atsushi Matsuda) + +autoconf: +- fixed configure.in for lcms (FreeBSD port by Mikhail Teterin) +- by default configure includes CMS support if lcms is present + +----------------------------------------------------------- + +0.9.3 (October 29th 2000) +------------------------- + +in short: + +Another beta release. The number of changes in the MNG specification have +resulted in a lot of new code and some changed code. At the same time I saw +no need to withhold some new functionality as it was pretty clear there was +going to be another beta-round. If things go well, I'm going to try to release +libmng 1.0.0 very shortly after this one. + +Many thanks to a lot of people for helping out, sending contributions, making +suggestions and testing this little baby. This would get nowhere without YOU!!! + +- fixed bug 111300/117103 +- added workaround for faulty PhotoShop iCCP chunk +- added MAGN/JDAA chunks +- added support for new filter_types +- added PNG/MNG spec version indicators +- added BCB mngview contribution by Andy Protano +- added BCB mngdump; a GUI-based MNG dumping utility (Andy Protano) +- implemented support for nEED "draft nn" +- implemented app-defined support for bKGD for PNG images +- removed trace-options from default SO/DLL builds (!!!) +- raised initial maximum canvas size to 10000x10000 (!!!) + (an App that wants to protect from overly large images should call + mng_set_maxcanvassize() with appropriate values) +- fixed other assorted stuff + +------------------- + +bugfixes: +B111300 - fixup for improved portability +B117103 - fixed compilation errors on *nix with lcms (thanks Ralph!) + +core: +- fixed compiler-warnings from Mozilla +- added check for simplicity-bits in MHDR +- added workaround for faulty PhotoShop iCCP chunk +- fixed app-supplied background restore +- fixed TERM processing delay of 0 msecs +- fixed write-code for zTXt & iTXt +- fixed read-code for iTXt +- added MAGN chunk +- fixed sRGB precedence for gamma_only corection +- added support for new filter_types +- fixed problem with no refresh after TERM +- fixed DEFI behavior +- fixed inclusion parameters to make the external libs work together +- added export of zlib functions from windows dll +- fixed timing & refresh behavior for single PNG/JNG +- removed trace-options from default SO/DLL builds (!!!) +- fixed MAGN rounding errors (thanks Matthias!) +- fixed small timing problem when FRAM delay = 0 +- fixed simplicity-check in compliance with draft 81/0.98a +- fixed alpha-blending for all alpha-canvasstyles +- added support for alpha-depth prediction +- fixed processing of unknown critical chunks +- removed test-MaGN +- added PNG/MNG spec version indicators +- implemented support for nEED +- added support for JDAA +- added functions to retrieve PNG/JNG specific header-info +- added optional support for bKGD for PNG images +- raised initial maximum canvas size to 10000x10000 +- added support for delta-JNG +- added callback to process non-critical unknown chunks +- fixed support for delta-images during read() / display() +- added closestream() processing for mng_cleanup() +- fixed delta-processing behavior +- added storage for pixel-/alpha-sampledepth for delta's +- implemented delayed delta-processing +- fixed putchunk_plte() to set bEmpty parameter (thanks Ben!) +- added errorcode for delayed delta-processing +- added get/set for bKGD preference setting +- added get function for interlace/progressive display +- fixed bug in empty PLTE handling +- fixed seperate read() & display() processing +- fixed tRNS processing for gray-image < 8-bits + +samples: +- added BCB mngview contribution by Andy Protano + +contrib: +- added BCB mngdump; a GUI-based MNG dumping utility (Andy Protano) + +doc: +- updated RPM spec-file by MATSUURA Takanori +- updated README.contrib + +makefiles: +- fixed some stuff in automake/autoconf/libtool +- fixed auto* for bug B117103 + +----------------------------------------------------------- + +0.9.2 (August 7th 2000) +----------------------- + +in short: + +Third beta release! Last one??? + +!!IMPORTANT!! All file-names are now prefixed with "libmng_" !!IMPORTANT!! + +Many thanks to Albert Chin-A-Young for his contribution of the +autoconf/automake/libtool stuff and to Ralph Giles for helping me +put it in the right places. + +There's a special README.autoconf so please read it! + +- fixed bug 110320/110546/110547/111096 +- added several status retrieval functions +- fixed other small bugs in display processing +- fixed number of small problems and documentation typos +- added autoconf/automake/libtool +- added latest MNG plugin (0.9.0) by Jason Summers + +------------------- + +bugfixes: +B110320 - fixed GCC warning about mix-sized pointer math +B110546 - fixed for improperly returning UNEXPECTEDEOF +B110547 - fixed bug in interlace code +B111096 - fixed large-buffer read-suspension + +core: +- version numbers +- fixed small bugs in display processing +- removed Nextbackxxx fields (no longer used) +- fixed problem with trace-functions improperly wrapped +- put specific code in add_chunk() inside MNG_SUPPORT_WRITE wrapper +- fixed documentation typos +- fixed wrapping of suspension parameters +- added status_xxxx functions +- added trace-codes/-strings for status_xxxxx functions +- changed file-prefixes +- added function to set simplicity field +- added trace-code/-string for updatemngsimplicity +- fixed putchunk_unknown() function + +samples: + +contrib: +- added latest MNG plugin (0.9.0) by Jason Summers + +doc: +- version numbers +- added autoconf readme +- version numbers in RPM stuff + +makefiles: +- fixed for new file-prefix +- added autoconf/automake/libtool + +----------------------------------------------------------- + +0.9.1 (July 26th 2000) +---------------------- + +in short: + +Second beta release. + +Given the enormous amount of bug-reports (not ;-), this will most likely +be one of the last betas. If things remain upright, the first public release +(1.0.0) is fairly eminent in the weeks to come... + +- added SDL mng player by Ralph Giles to contributions +- fixed timing and added internal buffering for I/O-suspension scenarios +- added get routines for internal display-state variables (frame/layer/playtime) +- changed read-processing for improved I/O-suspension (internal buffering) +- fixed several problems with create- & write-support +- added a load of documentation +- lots of small stuff + +------------------- + +bugfixes: + +core: +- fixed mandatory BACK color to be opaque +- changed mng_display_resume to allow to be called after a suspension + return with MNG_NEEDMOREDATA +- changed comments to indicate modified behavior for timer & suspension breaks +- added variables for go_xxxx processing +- implemented support for freeze/reset/resume & go_xxxx +- added trace-codes/-strings for special display processing +- added variables for improved timing support +- added support for improved timing +- added get routines for internal display variables +- added get/set routines for suspensionmode variable +- added trace-code/-string for get/set suspensionmode +- added trace-codes/-strings for get/set display variables +- added support for improved I/O-suspension +- changed read-processing for improved I/O-suspension +- added trace-code/-string for read_databuffer (I/O-suspension) +- added suspendbuffer constants +- changed EOF processing behavior +- fixed TERM delay processing +- changed pre-draft48 frame_mode=3 to frame_mode=1 +- added callbacks for SAVE/SEEK processing +- added trace-codes/-strings for SAVE/SEEK callbacks +- added variable for NEEDSECTIONWAIT breaks +- added trace-codes/-strings for get/set sectionbreaks +- added NEEDSECTIONWAIT error-code/-string +- added macro + routine to set returncode without calling error callback +- added trace-code/-string for special error routine +- changed default readbuffer size from 1024 to 4200 +- added variable for freeze & reset processing +- fixed storage of images during mng_read() +- fixed support for mng_display() after mng_read() +- added error cleanup processing +- fixed support for mng_display_reset() +- fixed suspension-buffering for 32K+ chunks +- added function to set frame-/layer-count & playtime +- added trace-code/-string for updatemngheader +- added error-code/-string for updatemngheader if not a MNG +- fixed creation-code +- fixed writing of signature +- fixed several chunk-writing routines + +samples: +- fixed the libmng.pas module in line with libmng.h + +contrib: +- added the SDL based mngplay viewer by Ralph Giles + +doc: +- extended the RPM contribution by MATSUURA Takanori +- added libmng.txt, a full description of the library and its usage +- added man-pages for mng(5), jng(5) and libmng(3) + +makefiles: + +----------------------------------------------------------- + +0.9.0 (June 30th 2000) +---------------------- + +in short: + +This is the first beta!!! Yippee!!! + +Thanks to all the people who helped to guide me in the right direction. +You know who you are! + +A special thanks to the guys with early implementations, who stood by and +put up with my whims :-) + +changes over 0.5.3: + +- updated mngplg to 0.4.1 (the latest & greatest) +- changed refresh parameters to 'x,y,width,height' + +----------------------------------------------------------- + +0.5.3 (never released) +---------------------- + +in short: + +This is a working version only; the next release will be 0.9.0 (first Beta!) + +There are a few incompatible changes with previous versions. The userdata +variable has been changed from mng_uint32 to mng_ptr to accomodate 64-bit +systems. For the same reason memory allocation size parameters have been +changed to a mng_size_t type which is a typedef of size_t. + +Thanks to Aleks Jakulin for helping to iron out some 64-bit platform issues! + +- implemented the update-region parameters of the refresh callback +- added support for most common delta-image options +- added an animation-speed modifier +- added an image-level parameter for the processtext callback +- updated mngplg to 0.4.0 (supports JNG, full CMS, and other enhancements!) +- fixed a lot of small things +- added support for PPLT chunk +- fixed to support 64-bit platforms + +------------------- + +bugfixes: + +core: +- added processing of color-info on delta-image +- fixed handling of empty SAVE chunk +- fixed display of stored JNG images +- fixed problem with BASI-IEND as object 0 +- changed the version parameters (obviously) +- added update-region parms for refresh calback +- added Needrefresh parameter +- added initialization of update-region for refresh +- added initialization of Needrefresh parameter +- changed progressive-display processing +- added tracecodes for tracing JPEG progression +- added tracing of JPEG calls +- added Deltaimmediate parm for faster delta-processing +- added extra checks for delta-images +- many changes to support delta-images +- optimized some store_xxx routines +- fixed some small things (as precaution) +- fixed possible trouble if IEND display-processing got broken up +- fixed nasty bug with embedded PNG after delta-image +- added processing of PLTE & tRNS for delta-images +- added processing of PLTE/tRNS & color-info for delta-images in the + ani_objects chain +- fixed problem with color-correction for stored images +- added get/set for speedtype to facilitate testing +- added trace-codes & -strings for get/set speedtype +- added speed-modifier to timing routine +- added get-routine of imagelevel for processtext callback +- added trace-code & -string for get imagelevel +- added administration of imagelevel parameter +- added support for PPLT chunk +- added trace-codes & -strings for PPLT chunk processing +- fixed problem with incorrect gamma-correction +- fixed inclusion of IJG read/write code +- fixed problem with 16-bit GA format +- fixed problem with cheap transparency for 4-bit gray +- fixed display_xxxx routines for interlaced images +- added precaution against faulty iCCP chunks from PS +- changed userdata variable to mng_ptr +- added typedef for mng_size_t +- changed size parameter for memory allocation to mng_size_t +- fixed compiler-warning for non-initialized iB variable +- changed definition for 32-bit ints (64-bit platforms) +- changed definition for mng_handle (64-bit platforms) +- swapped refresh parameters +- fixed initialization routine for new mng_handle type +- added inclusion of stdlib.h for abs() +- fixed some 64-bit warnings +- fixed incompatible return-types + +samples: + +contrib: +- updated mngplg to 0.3.0 (supports JNG & full color-correction!) +- updated mngplg to 0.4.0 (Jason is picking up the pace ;-) + +doc: +- added rpm directory with rpm spec-file (contributed by MATSUURA Takanori) + +makefiles: +- changed makefile.linux to reflect versionnr for shared-lib +- changed makefile.linux to depend on mng_conf.h & mng_types.h + +----------------------------------------------------------- + +0.5.2 (June 10th 2000) +---------------------- + +in short: + +This is the third release for developers +Another milestone since JNG is now fully supported +The next release will most likely be numbered 0.9.0 as the first Beta!! + +Fixed bug 106017 & 106019 +Added many constants regarding chunk-property values +Implemented full JNG support +Added all the error- & trace-strings +Added get/set routines for default ZLIB/IJG parameters +Added a generic makefile for Unix platforms (contributed by Tim Rowley) +Added canvasstyle for separate RGB + A canvas (eg. mozilla-style) +Separated configuration-options into a separate file: "mng_conf.h" +Fixed stuff for generic Unix compilation (contributed by Tim Rowley) +Upgraded to lcms1.0.6 (now supports 16-bit endian-peculiarities) +Added a makefile for Linux ELF & fixed some code-issues to go along with gcc +Added support for suspended input-buffer processing +Implemented the display-routines for RGBA/ARGB/BGRA/ABGR canvasstyles +Implemented the application background-restore functionality +Fixed & tested the mngtree Unix-sample (runs on Linux-RH6.2 with libmng.so) +Upgraded mngplg to v0.2.2 (based on the latest code including JNG) +Fixed a lot of other assorted stuff + +------------------- + +bugfixes: +B003(106017) - fixed problem with being proprietary to BCB +B004(106019) - fixed problem when MNG_SUPPORT_WRITE not defined + +core: +- bumped version-numbers up to 0.5.2 (yeah, really) +- fixed support for IJGSRC6B +- cleaned up some code regarding mixed support-options +- complemented constants for chunk-property values +- fixed MNG_UINT_pHYg value +- implemented JNG support +- fixed problem with DEFI clipping +- added error telltale strings & support +- added trace telltale strings & support +- added support for global color-chunks inside TERM/LOOP +- added support for global PLTE,tRNS,bKGD inside TERM/LOOP +- added default IJG compression parameters and such +- moved init of default zlib parms to "mng_hlapi.c" +- added init of default IJG parms +- added support for get/set of zlib/IJG default parms +- added tracestrings for global animation color-chunks +- added tracestrings for get/set of default ZLIB/IJG parms +- added tracestrings for global PLTE,tRNS,bKGD +- added framenr/layernr/playtime to object header +- added initialization of framenr/layernr/playtime +- changed ani_create calls not returning object pointer +- create ani objects always (not just inside TERM/LOOP) +- fixed inconsistancy with freeing global iCCP profile +- fixed minor bugs 16-bit pixel-handling +- added object promotion routine (PROM handling) +- added trace-codes & -strings for image-object promotion +- added trace-codes & -strings for delta-image processing +- added error-codes & -strings for delta-image processing +- added support for delta-image processing +- added ani-object routines for delta-image processing +- added delta-image fields +- added compression/filter/interlace fields to object-buffer for + delta-image processing +- added delta-image row-processing routines +- fixed up punctuation in several files (contributed by Tim Rowley) +- removed useless definition in "mng_chunks.h" (contributed by Tim Rowley) +- fixed pointer confusion in "mng_display.c" (contributed by Tim Rowley) +- fixed inclusion for memcpy (contributed by Tim Rowley) +- added mng_int32p (contributed by Tim Rowley) +- added internal delta-image processing callbacks +- separated configuration-options into "mng_conf.h" +- changed to most likely configuration +- added RGB8_A8 canvasstyle +- added getalphaline callback for RGB8_A8 canvasstyle +- fixed some makeup for Linux gcc compile +- implemented app bkgd restore routines +- implemented RGBA8, ARGB8, BGRA8 & ABGR8 display routines +- added support for RGB8_A8 canvasstyle +- added support for suspended input-buffer processing +- added mng_read_resume HLAPI function to support read-suspension +- fixed timer-handling to run with Mozilla (Tim Rowley) +- fixed alpha-handling for alpha canvasstyles +- fixed some compilation-warnings (contrib Jason Morris) + +samples: +- fixed mngview(delphi) to work with the new core +- synchronized libmng.pas(delphi) with the new libmng.h header +- removed the error- & trace-strings from libmng.pas(delphi) +- fixed mngtree(Unix) to compile on Linux (runs with libmng.so) +- added makefile.linux for mngtree(Unix) (tested on RedHat6.2) + +contrib: +- updated mngplg to 0.2.2 (based on latest code; supports JNG!) + +doc: +- this file obviously +- added Tim Rowley as contributing author +- changed the examples.readme doc +- updated the design-schematics in line with the current code + +makefiles: +- changed the directory to "makefiles" to avoid name-conflicts +- added generic Unix makefile (thanks to Tim Rowley) +- added Linux ELF makefile (tested on RedHat6.2) + +----------------------------------------------------------- + +0.5.1 May 16th 2000 +------------------- + +in short: + +This is the second release for developers +It's a bit of a milestone since all the chunk functionality is in place and +functioning (read, examine, create & write) +This version is incompatible with 0.5.0 since some of the callback prototypes +have changed (should be the last time that happens!) +There are a few more samples and even a real contribution! + +Fixed bug 105795 & 105797 +Fixed a mis-alignment in animation-timing +Added chunk-access functions +Finished all chunk-storage routine-bits +Finished all chunk-write routines +Changed the callback prototypes to allow error-reporting back to the library +Fixed some routines to allow for callback error-reporting +Added version-control functions & constants +Added two functions to set display- & sRGB-profile from memory +Moved CRC table to dynamic structure (for improved thread-safety) +Added SAVE & SEEK save&restore functionality +Finished the application-based CMS-callbacks +Fixed a few BCB specifics +Changed the Win32 DLL and samples to use __stdcall +Did some more assorted little changes +Added 2 BCB samples +Added 1 Unix sample +Added the MNG plugin by Jason Summers in the contrib section +Changed some documents to reflect these changes + +------------------- + +bugfixes: +B001(105795) - fixed wrong lcms call & memory-leak for gammatables +B002(105797) - fixed problem with missing sRGB profile + +core: +- changed chunk iteration function +- added chunk access functions +- added version control constants & functions +- changed strict-ANSI stuff +- added set_outputprofile2 & set_srgbprofile2 +- added empty-chunk put-routines +- added version_dll & VERSION_DLL (for consistency) +- added version control explanatory text & samples +- added iteratechunk callback definition +- improved definitions for DLL support +- added 8-bit palette definition +- added general array definitions +- added MNG_NULL definition +- changed most callback prototypes to allow the app + to report errors during callback processing +- added CRC table to main structure (for thread-safety) +- added iPLTEentries for checking hIST-length +- changed palette definition to exported palette-type +- removed frozen indicator +- added create/write indicators +- added eMNGma hack (will be removed in 1.0.0 !!!) +- added TERM animation object pointer (easier reference) +- added saved-data structure for SAVE/SEEK processing +- added some errorcodes +- added application errorcodes (used with callbacks) +- moved chunk-access errorcodes to severity 5 +- added chunk-access function trace-codes +- changed trace to macro for callback error-reporting +- added save_state & restore_state trace-codes +- put in some extra comments +- fixed layout for sBIT, PPLT +- changed write callback definition +- fixed layout for PPLT again (missed deltatype ?!?) +- cleaned up left-over teststuff in the BACK chunk routine +- changed CRC initialization to use dynamic structure + (wasn't thread-safe the old way !) +- filled in many missing sequence&length checks +- filled in many missing chunk-store snippets +- added checks for running animations +- filled remaining write routines +- fixed read_pplt with regard to deltatype +- added callback error-reporting support +- added pre-draft48 support (short MHDR, frame_mode, LOOP) +- fixed chunk-storage bit in several routines +- supplemented the SAVE & SEEK display processing +- added init of iPLTEcount +- changed calling-convention definition +- changed status-handling of display-routines +- added versioning-control routines +- filled the write routine +- fixed frame_delay misalignment +- added sanity check for frozen status +- changed display_mend to reset state to initial or SAVE +- added save_state and restore_state for SAVE/SEEK/TERM + processing +- added process_save & process_seek routines +- changed and filled iterate-chunk function +- added getchunk functions +- added putchunk functions +- added empty-chunk put-routines +- filled application-based color-management routines +- added creatememprofile +- filled the deflatedata routine +- added cleanup of saved-data (SAVE/SEEK processing) +- moved the actual write_graphic functionality from mng_hlapi.c + to it's appropriate function in the mng_write.c module +- moved standard header includes into mng_types.h + (stdlib/mem for mem-mngmt & math for fp gamma-calc) +- added getimgdata & putimgdata functions + +samples: +- fixed mngview(delphi) to work with the new core +- synchronized libmng.pas(delphi) with the new libmng.h header +- added mngtree(bcb) sample +- added bogus(bcb) sample +- added mngtree(unix) sample + +contrib: +- added mngplg 0.1.0 / a MNG plugin for Win32 by Jason Summers + +doc: +- added this changes.readme file +- changed the samples.readme doc accordingly +- changed the contrib.readme doc accordingly + +----------------------------------------------------------- + +0.5.0 May 1st 2000 +------------------ + +in short: + +This is the first developers release. +It's roughly about 60% done. diff --git a/Engine/lib/lmng/LICENSE b/Engine/lib/lmng/LICENSE new file mode 100644 index 000000000..2b624c00a --- /dev/null +++ b/Engine/lib/lmng/LICENSE @@ -0,0 +1,57 @@ +/* ************************************************************************** */ +/* * * */ +/* * COPYRIGHT NOTICE: * */ +/* * * */ +/* * Copyright (c) 2000-2007 Gerard Juyn (gerard@libmng.com) * */ +/* * [You may insert additional notices after this sentence if you modify * */ +/* * this source] * */ +/* * * */ +/* * For the purposes of this copyright and license, "Contributing Authors" * */ +/* * is defined as the following set of individuals: * */ +/* * * */ +/* * Gerard Juyn * */ +/* * Glenn Randers-Pehrson * */ +/* * * */ +/* * The MNG Library is supplied "AS IS". The Contributing Authors * */ +/* * disclaim all warranties, expressed or implied, including, without * */ +/* * limitation, the warranties of merchantability and of fitness for any * */ +/* * purpose. The Contributing Authors assume no liability for direct, * */ +/* * indirect, incidental, special, exemplary, or consequential damages, * */ +/* * which may result from the use of the MNG Library, even if advised of * */ +/* * the possibility of such damage. * */ +/* * * */ +/* * Permission is hereby granted to use, copy, modify, and distribute this * */ +/* * source code, or portions hereof, for any purpose, without fee, subject * */ +/* * to the following restrictions: * */ +/* * * */ +/* * 1. The origin of this source code must not be misrepresented; * */ +/* * you must not claim that you wrote the original software. * */ +/* * * */ +/* * 2. Altered versions must be plainly marked as such and must not be * */ +/* * misrepresented as being the original source. * */ +/* * * */ +/* * 3. This Copyright notice may not be removed or altered from any source * */ +/* * or altered source distribution. * */ +/* * * */ +/* * The Contributing Authors specifically permit, without fee, and * */ +/* * encourage the use of this source code as a component to supporting * */ +/* * the MNG and JNG file format in commercial products. If you use this * */ +/* * source code in a product, acknowledgment would be highly appreciated. * */ +/* * * */ +/* ************************************************************************** */ +/* * * */ +/* * Parts of this software have been adapted from the libpng package. * */ +/* * Although this library supports all features from the PNG specification * */ +/* * (as MNG descends from it) it does not require the libpng package. * */ +/* * It does require the zlib library and optionally the IJG jpeg library, * */ +/* * and/or the "little-cms" library by Marti Maria (depending on the * */ +/* * inclusion of support for JNG and Full-Color-Management respectively. * */ +/* * * */ +/* * This library's function is primarily to read and display MNG * */ +/* * animations. It is not meant as a full-featured image-editing * */ +/* * component! It does however offer creation and editing functionality * */ +/* * at the chunk level. * */ +/* * (future modifications may include some more support for creation * */ +/* * and or editing) * */ +/* * * */ +/* ************************************************************************** */ diff --git a/Engine/lib/lmng/README b/Engine/lib/lmng/README new file mode 100644 index 000000000..a8e67cad5 --- /dev/null +++ b/Engine/lib/lmng/README @@ -0,0 +1,36 @@ +libmng 1.0.10 +------------- + +Added provisional ANG and anIM support, and made some minor bugfixes. + +libmng 1.0.9 +------------ + +A number of optimizations in the chunk handling and reader/writer code. +This saves over 20KB on binary footprint! + +Also several bugfixes and a couple of patches bring it another step +closer to perfection.... :-) + +See CHANGELOG for details. + + +Y.T. + +Gerard + + +For more information please visit: + +The official libmng web-site: + http://www.libmng.com/ + +Libmng's community on SourceForge: + https://sourceforge.net/project/?group_id=5635 + +The official MNG homepage: + http://www.libpng.org/pub/mng/ + +The official PNG homepage: + http://www.libpng.org/pub/png/ + diff --git a/Engine/lib/lmng/README.autoconf b/Engine/lib/lmng/README.autoconf new file mode 100644 index 000000000..753f7b4fd --- /dev/null +++ b/Engine/lib/lmng/README.autoconf @@ -0,0 +1,213 @@ +********************************************************************** +********************************************************************** + + ***** this is unmaintained ***** + +If you happen to find problems with autoconfiguration and building, +I simply cannot help you. I'm looking for a maintainer that doesn't mind +spending a few minutes every now and then on the next release to make sure +things are still in working order. + +For the moment all autoconf stuff ahs been moved into unmaintained!! + +********************************************************************** +********************************************************************** + + + + +Configuration from CVS +====================== + +If you're using source checked out from CVS, rather than a source +distribution tarball, please be aware that you can use ./autogen.sh in +place of ./configure below. + +Because this is a cross-platform project, the source templates for +the autoconf scripts are sequestered in the 'makefiles' directory. +Running './autogen.sh' will copy them into their conventional places at +the lop level. If you already see the files there, you don't need to +worry about this step. + +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. diff --git a/Engine/lib/lmng/README.config b/Engine/lib/lmng/README.config new file mode 100644 index 000000000..d5cd4541d --- /dev/null +++ b/Engine/lib/lmng/README.config @@ -0,0 +1,104 @@ +Configuration options in libmng +=============================== + +The library is fairly configurable through the use of a number of defines. +Please note however that certain defines are for internal use only. +The following list gives a summary of options that can be used externally to +define the functionality of the library: + +======================================== + +#define MNG_BUILD_DLL + +This is used to indicate that a "standard" DLL should result from compiling +the library. Please note the remarks in README.dll if you intend to work +with the library as a DLL. The purpose of this option is to ensure that +DLL builds have the same set of functions. + +#define MNG_BUILD_SO + +This is used to indicate that a "standard" shared library (SO) should result +from a compilation. The purpose of this option is to ensure that all +shared libraries generated this way will have the same set of functions. + +#define MNG_USE_DLL / #define MNG_USE_SO + +These should be used when including the library header in the compilation +of an application to indicate that the compiler/linker must take the +necessary steps to make the binary executable to use the standard DLL +or shared library (SO). + +#define MNG_SKIP_ZLIB / #define MNG_SKIP_LCMS / #define MNG_SKIP_IJG6B + +Use these in conjunction with MNG_USE_DLL / MNG_USE_SO. This is useful if +you only need the external definitions of the MNG library and not the others, +which will speed up the compilation process. + +#define MNG_SUPPORT_FULL / #define MNG_SUPPORT_LC / #define MNG_SUPPORT_VLC + +These can be used to indicate the level of MNG spec compliance required. +Currently only full MNG compliance is supported. + +#define MNG_SUPPORT_IJG6B + +This can be used to indicate if JNG support is required. This option will +include the IJG JPEG-library. Note that MNG_SUPPORT_FULL will automatically +set this option. Use this only if you need JNG support with MNG-(V)LC. + +#define MNG_FULL_CMS / #define MNG_GAMMA_ONLY / #define MNG_NO_CMS / +#define MNG_APP_CMS + +These indicate the color-correction support level of the library. +If you are on a platform that supports lcms (Little CMS by Marti Maria Saguar) +then it is highly recommended to define MNG_FULL_CMS. +If your platform has it's own CMS then select MNG_APP_CMS and be sure to +include the appropriate callbacks in your app. +In all other cases it is recommended to define MNG_GAMMA_ONLY. + +#define MNG_SUPPORT_READ / #define MNG_SUPPORT_WRITE / +#define MNG_SUPPORT_DISPLAY + +These indicate the high-level support for reading, writing and/or +displaying files. Note that in order to display a file, you'll need to read +it first. (yes, really!) + +#define MNG_STORE_CHUNKS + +This indicates that the library should store chunk-information when reading +a file. This information can then be processed through the +MNG_ITERATE_CHUNKS() function. Note that you must specify this option if +you want to create and write a new file. + +#define MNG_ACCESS_CHUNKS + +This is used to indicate that the app may need access to internally stored +chunk information. MNG_STORE_CHUNKS must be defined as well for this option +to function properly. + +#define MNG_INTERNAL_MEMMNGMT + +You can use this to have the library handle it's own memory allocation and +deallocation through the "standard" memory functions. This option is turned +off by default, which means your app must define the memory callbacks. + +#define MNG_ERROR_TELLTALE + +Set this on to allow human-readable error-messages to be included in the +library and the error function and callback. + +#define MNG_BIGENDIAN_SUPPORTED + +This option should be used to indicate the hardware is based on big endian +integers. + +#define MNG_SUPPORT_TRACE / #define MNG_TRACE_TELLTALE + +These two can be used when debugging an app. You'll need to have the trace +callback setup also. This allows for a rather thorough investigation of the +libraries function paths. + +======================================== + +Any other optional defines you may encounter are for internal use only. +please do not specify them externally. In case of doubt, consult the +support email lists. More info can be found on http://www.libmng.com diff --git a/Engine/lib/lmng/README.contrib b/Engine/lib/lmng/README.contrib new file mode 100644 index 000000000..9b287532a --- /dev/null +++ b/Engine/lib/lmng/README.contrib @@ -0,0 +1,95 @@ +The contrib directory contains contributions made by fellow +enthousiasts. (Check respective web-sites for the latest version) + +---------------------------------------------------------------------- + +mngplg - A Netscape plugin for MNG - by Jason Summers + +http://pobox.com/~jason1/imaging/mngplg/ + +The very first contribution, and what a start! +GIF look out, MNG is on the prowl and ready to swat you like a fly! + +---------------------------------------------------------------------- + +mngplay - An SDL based MNG viewer - by Ralph Giles + +http://snow.ashlu.bc.ca/~giles/mng/ + +Another nice contribution. View MNG files on practically any platform +with this standalone viewer. +Source-code only; Requires SDL library and libmng.so + +(Modified by Greg Roelofs) + +---------------------------------------------------------------------- + +mngview - A BCB port of the Delphi sample - by Andy Protano + +I have added this nice little port to the BCB samples directory. +It adds a nifty progressbar while reading a file. Excellent work! +Requires libmng.dll +(note: this is in the BCB samples directory) + +---------------------------------------------------------------------- + +mngdump - A BCB GUI-based dump utility - by Andy Protano + +Andy has sent me this fully functional MNG dump utility, that gives +detailed information of the contents of any MNG file. +Requires libmng.dll + +---------------------------------------------------------------------- + +mng-view - A GTK-based MNG viewer - by Vova Babin + +Vova has been hacking away with the libmng code and has come up with +this nice little sample how to write a MNG viewer using GTK. +Thanks mate! +Source-code only +Requires GTK+ (1.2 or higher) and libmng (0.9.2 or higher) + +(Modified by Greg Roelofs) + +---------------------------------------------------------------------- + +mngview - Another MNG viewer; this one for MSVC - by Nicholaus Brennig + +A welcome contribution from Nicholaus. Author of SlowView. A very nice +image-handling utility for Windows. A welcome contribution since there +have been numerous questions about linking libmng with MSVC. +Well, look no further. Here it is! + +---------------------------------------------------------------------- + +MSVC libmng project - An MSVC project to build libmng.dll + - by Chad Austin + +Chad has contributed some project-files that you could use to build +libmng.dll with MSVC. Please be sure to read the README file included. + +---------------------------------------------------------------------- + +fbmngplay - A simple fbcon based mng player - by Stefan Reinauer + +Stefan has contributed this little example, based on Ralph's +SDL player. It uses the kernel framebuffer device to display mng +animations through the libmng interface. +(currently for 16-bit buffers only) + +---------------------------------------------------------------------- + +xmngview - Lesstif/Motif standalone player for MNG files + - by Winfried Szukalski + +Winfried contributed this MNG player for X-based systems. +(recently updated) + +---------------------------------------------------------------------- + +makemng - A delta-MNG creation utility for MSVC - by Alex Volkov + +Alex sent me this nice utility that will allow you to create highly +optmized MNGs using the delta-PNG capabilities of MNG. + +---------------------------------------------------------------------- diff --git a/Engine/lib/lmng/README.dll b/Engine/lib/lmng/README.dll new file mode 100644 index 000000000..2f779e81a --- /dev/null +++ b/Engine/lib/lmng/README.dll @@ -0,0 +1,41 @@ +Standard Windows DLL +==================== + +The DLL provided in the BCB/win32dll directory is meant as the sole candidate +for distributions, based on libmng.dll, that install the DLL into the public +Windows system-directory. The outline herein defines the requirements to +which such a distribution must comply. If you cannot comply with these +requirements please install the dll in the same directory as your application +and NOT in the Windows system-directory!!! + + +1) Only the DLL already assembled in the libmng distribution may be used for + other distributions! + +2) Only stable public releases are eligible for distribution! A public release + is one where the y-value of the x.y.z version-code is an even number. + Eg. 1.0.0, 1.2.1, 2.4.7, etc. + +3) The installation program MUST store the DLL in the Windows system-directory! + Eg. C:\WinNT\System32, C:\Windows98\System + (Note: InstallShield users can use the variable) + +3) The installation program MUST flag the file as a shared library! + +4) The installation program MUST NOT install the DLL if a newer version + already exists in the Windows system-directory! The standard DLL provided + contains the Windows-default version-numbering system. PLEASE USE IT!! + DO NOT rely on the date or size of the files. + +5) An uninstall procedure MAY NOT remove the DLL if other applications are + still linked to it! Proper handling as a shared library is imperitive. + +6) TEST IT, TEST IT, TEST IT!!! (I just can't stress this enough) + If you don't have enough time, let someone else test it BEFORE you + distribute! + + +The penalty for violating these rules is inclusion of your name in the list +of endangered but useless species (just below the GIF entry!), and on my +blacklist. YOU HAVE BEEN FOREWARNED! + diff --git a/Engine/lib/lmng/README.examples b/Engine/lib/lmng/README.examples new file mode 100644 index 000000000..1ba94c140 --- /dev/null +++ b/Engine/lib/lmng/README.examples @@ -0,0 +1,48 @@ +The samples are in platform-specific directories. + +!!! contributions are very welcome !!! + + +bcb - Borland C++ Builder (3.0) (found under bcb/xxx) +----------------------------------------------------- + +win32dll - sample project to create a Windows dll. Requires zlib1.2.1, + IJG jpgsrc6b and lcms1.0.14. The directories containing these + libraries must be at the same level as the libmng directory. + So if you're in the directory with this file and the libmng + sources, they should be in ..\zlib , ..\jpgsrc6b and ..\lcms + respectively. + +!!! To run the other Win32 samples you need to copy the libmng.dll + file from here into the sample's directory !!! + +mngtree - sample project to create a little command-line tool that dumps + the chunk-structure of a given file onto stdout. + +bogus - a completely bogus example on how to create a perfectly valid + (though slightly biased) MNG. + +mngview - port of the Delphi mngview sample. contributed by Andy Protano. + see also README.contrib + +mngrepair- an example on how to fix invalid MNG files + uses the new mng_copy_chunks() function and MNG_SOFTERRORS to + 'ignore' certain input-errors. This conditional *MUST* only be used + for exactly this kind of software; eg. repair utilities. + + +delphi - Borland Delphi (3.0+) (found under contrib/delphi/xxx) +--------------------------------------------------------------- + +mngview - sample project for a simple mng-viewer. The general unit in + the delphi directory was translated from libmng.h It can be + used in other projects to access libmng.dll created with the + win32dll example above. + + +unix - Unix (found under contrib/gcc/xxx) +----------------------------------------- + +mngtree - basically a copy of the BCB sample. It includes a makefile for + Linux and it's been tested on RedHat6.2 + diff --git a/Engine/lib/lmng/README.footprint b/Engine/lib/lmng/README.footprint new file mode 100644 index 000000000..34dba7f18 --- /dev/null +++ b/Engine/lib/lmng/README.footprint @@ -0,0 +1,46 @@ +/* + You can use one or more of the following defines to + reduce the size of the compiled library. Define the + SKIPCANVAS macros for any canvas configurations that + your application doesn't use. Define the SKIPCHUNK + macros for any chunks that your application doesn't + process. Define MNG_OPTIMIZE_FOOTPRINT to choose + smaller code size over faster execution and less memory + usage. These macros became available in version 1.0.6. +*/ + +/* eliminate unused features from libmng */ +#define MNG_OPTIMIZE_FOOTPRINT +#define MNG_OPTIMIZE_OBJCLEANUP +#define MNG_OPTIMIZE_CHUNKINITFREE +#define MNG_OPTIMIZE_CHUNKASSIGN +#define MNG_OPTIMIZE_CHUNKREADER + +#define MNG_SKIPCANVAS_ABGR8 +#define MNG_SKIPCANVAS_ARGB8 +#define MNG_SKIPCANVAS_BGR8 +#define MNG_SKIPCANVAS_BGRA8 +#define MNG_SKIPCANVAS_BGRA8_PM +#define MNG_SKIPCANVAS_BGRX8 +#define MNG_SKIPCANVAS_RGBA8 +#define MNG_SKIPCANVAS_BGR565 +#define MNG_SKIPCANVAS_RGB565 +#define MNG_SKIPCANVAS_BGRA565 +#define MNG_SKIPCANVAS_RGBA565 + +#define MNG_SKIPCHUNK_iCCP +#define MNG_SKIPCHUNK_tEXt +#define MNG_SKIPCHUNK_zTXt +#define MNG_SKIPCHUNK_iTXt +#define MNG_SKIPCHUNK_bKGD +#define MNG_SKIPCHUNK_pHYs +#define MNG_SKIPCHUNK_sBIT +#define MNG_SKIPCHUNK_sPLT +#define MNG_SKIPCHUNK_hIST +#define MNG_SKIPCHUNK_tIME +#define MNG_SKIPCHUNK_eXPI +#define MNG_SKIPCHUNK_fPRI +#define MNG_SKIPCHUNK_nEED +#define MNG_SKIPCHUNK_pHYg + + diff --git a/Engine/lib/lmng/README.packaging b/Engine/lib/lmng/README.packaging new file mode 100644 index 000000000..da0db3e7b --- /dev/null +++ b/Engine/lib/lmng/README.packaging @@ -0,0 +1,24 @@ +Packaging Libmng for distribution +--------------------------------- + +These are some notes for those building binaries for distribution. + +We're interested to hear about anywhere libmng is helpful, so let us +know if you're including it with your application or OS. Also, if your +build is publicly accessible, we'd be happy to link to it from +the libmng site. + +However, We respectfully request that you *not* distribute binaries as a +shared library (DLL) with any of the major features disabled. While +there is support for this in terms of #ifdef directives (in +libmng_conf.h) and autoconf switches they are intended for embedded +application and testing. The default compilation options support the +full MNG specification, and we wish to avoid the confusion among +general users that partial support would engender. + + +Platform specific notes: + +We have a basic .spec file for generating rpms. Send us a note if you'd +like to clean it up. + diff --git a/Engine/lib/lmng/bcb/bogus/bogus.bpr b/Engine/lib/lmng/bcb/bogus/bogus.bpr new file mode 100644 index 000000000..50a7a8094 --- /dev/null +++ b/Engine/lib/lmng/bcb/bogus/bogus.bpr @@ -0,0 +1,184 @@ +# --------------------------------------------------------------------------- +!if !$d(BCB) +BCB = $(MAKEDIR)\.. +!endif + +# --------------------------------------------------------------------------- +# IDE SECTION +# --------------------------------------------------------------------------- +# The following section of the project makefile is managed by the BCB IDE. +# It is recommended to use the IDE to change any of the values in this +# section. +# --------------------------------------------------------------------------- + +VERSION = BCB.03 +# --------------------------------------------------------------------------- +PROJECT = bogus.exe +OBJFILES = bogus.obj +RESFILES = bogus.res +RESDEPEN = $(RESFILES) +LIBFILES = ..\win32dll\libmng.lib +LIBRARIES = +SPARELIBS = +PACKAGES = vclx35.bpi VCL35.bpi vcldb35.bpi vcldbx35.bpi bcbsmp35.bpi dclocx35.bpi \ + Qrpt35.bpi +DEFFILE = +# --------------------------------------------------------------------------- +PATHCPP = .; +PATHASM = .; +PATHPAS = .; +PATHRC = .; +DEBUGLIBPATH = $(BCB)\lib\debug +RELEASELIBPATH = $(BCB)\lib\release +# --------------------------------------------------------------------------- +CFLAG1 = -O2 -w -r- -k -y -v -vi- -c -tWC +CFLAG2 = -D_NO_VCL;MNG_SUPPORT_WRITE;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_NO_CMS;MNG_USE_DLL \ + -I..\win32dll;..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms;$(BCB)\include +CFLAG3 = -Tkh30000 +PFLAGS = -D_NO_VCL;MNG_SUPPORT_WRITE;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_NO_CMS;MNG_USE_DLL \ + -U..\win32dll;$(BCB)\lib;$(DEBUGLIBPATH) \ + -I..\win32dll;..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms;$(BCB)\include \ + -$Y -$W -$O- -v -JPHN -M +RFLAGS = -D_NO_VCL;MNG_SUPPORT_WRITE;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_NO_CMS;MNG_USE_DLL \ + -i..\win32dll;..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms;$(BCB)\include +AFLAGS = /i..\win32dll /i..\..\..\libmng-devel /i..\..\..\zlib /i..\..\..\jpgsrc6b \ + /i..\..\..\lcms /i$(BCB)\include /d_NO_VCL /dMNG_SUPPORT_WRITE \ + /dMNG_ACCESS_CHUNKS /dMNG_STORE_CHUNKS /dMNG_NO_CMS /dMNG_USE_DLL /mx /w2 /zd +LFLAGS = -L..\win32dll;$(BCB)\lib;$(DEBUGLIBPATH) -ap -Tpe -x -Gn -v +IFLAGS = +# --------------------------------------------------------------------------- +ALLOBJ = c0x32.obj $(OBJFILES) +ALLRES = $(RESFILES) +ALLLIB = $(LIBFILES) import32.lib cw32mt.lib +# --------------------------------------------------------------------------- +!ifdef IDEOPTIONS + +[Version Info] +IncludeVerInfo=1 +AutoIncBuild=1 +MajorVer=1 +MinorVer=0 +Release=1 +Build=9 +Debug=0 +PreRelease=0 +Special=0 +Private=0 +DLL=0 +Locale=1033 +CodePage=1252 + +[Version Info Keys] +CompanyName= +FileDescription=Executable (Console) +FileVersion=1.0.1.9 +InternalName=bogus +LegalCopyright=copyright (c) 2000,2002 G. Juyn +LegalTrademarks= +OriginalFilename=bogus.exe +ProductName=bogus +ProductVersion=1.0.1 +Comments= + +[Excluded Packages] +C:\Program Files\Borland\CBuilder3\Bin\DbX35.bpl=(untitled) + +[HistoryLists\hlIncludePath] +Count=3 +Item0=..\win32dll;..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms;$(BCB)\include +Item1=..\..\..\libmng-devel;..\..\..\zlib;$(BCB)\include +Item2=..\..\..\libmng;..\..\..\zlib;$(BCB)\include + +[HistoryLists\hlLibraryPath] +Count=2 +Item0=..\win32dll;$(BCB)\lib +Item1=$(BCB)\lib + +[HistoryLists\hlConditionals] +Count=2 +Item0=_NO_VCL;MNG_SUPPORT_WRITE;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_NO_CMS;MNG_USE_DLL +Item1=_NO_VCL;MNG_SUPPORT_WRITE;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_USE_DLL + +[Debugging] +DebugSourceDirs= + +[Parameters] +RunParams= +HostApplication= + +!endif + +# --------------------------------------------------------------------------- +# MAKE SECTION +# --------------------------------------------------------------------------- +# This section of the project file is not used by the BCB IDE. It is for +# the benefit of building from the command-line using the MAKE utility. +# --------------------------------------------------------------------------- + +.autodepend +# --------------------------------------------------------------------------- +!if !$d(BCC32) +BCC32 = bcc32 +!endif + +!if !$d(DCC32) +DCC32 = dcc32 +!endif + +!if !$d(TASM32) +TASM32 = tasm32 +!endif + +!if !$d(LINKER) +LINKER = ilink32 +!endif + +!if !$d(BRCC32) +BRCC32 = brcc32 +!endif +# --------------------------------------------------------------------------- +!if $d(PATHCPP) +.PATH.CPP = $(PATHCPP) +.PATH.C = $(PATHCPP) +!endif + +!if $d(PATHPAS) +.PATH.PAS = $(PATHPAS) +!endif + +!if $d(PATHASM) +.PATH.ASM = $(PATHASM) +!endif + +!if $d(PATHRC) +.PATH.RC = $(PATHRC) +!endif +# --------------------------------------------------------------------------- +$(PROJECT): $(OBJFILES) $(RESDEPEN) $(DEFFILE) + $(BCB)\BIN\$(LINKER) @&&! + $(LFLAGS) + + $(ALLOBJ), + + $(PROJECT),, + + $(ALLLIB), + + $(DEFFILE), + + $(ALLRES) +! +# --------------------------------------------------------------------------- +.pas.hpp: + $(BCB)\BIN\$(DCC32) $(PFLAGS) {$< } + +.pas.obj: + $(BCB)\BIN\$(DCC32) $(PFLAGS) {$< } + +.cpp.obj: + $(BCB)\BIN\$(BCC32) $(CFLAG1) $(CFLAG2) $(CFLAG3) -n$(@D) {$< } + +.c.obj: + $(BCB)\BIN\$(BCC32) $(CFLAG1) $(CFLAG2) $(CFLAG3) -n$(@D) {$< } + +.asm.obj: + $(BCB)\BIN\$(TASM32) $(AFLAGS) $<, $@ + +.rc.res: + $(BCB)\BIN\$(BRCC32) $(RFLAGS) -fo$@ $< +# --------------------------------------------------------------------------- diff --git a/Engine/lib/lmng/bcb/bogus/bogus.cpp b/Engine/lib/lmng/bcb/bogus/bogus.cpp new file mode 100644 index 000000000..626d0d73c --- /dev/null +++ b/Engine/lib/lmng/bcb/bogus/bogus.cpp @@ -0,0 +1,212 @@ +/* ************************************************************************** */ +/* * * */ +/* * COPYRIGHT NOTICE: * */ +/* * * */ +/* * Copyright (c) 2000,2002 Gerard Juyn (gerard@libmng.com) * */ +/* * [You may insert additional notices after this sentence if you modify * */ +/* * this source] * */ +/* * * */ +/* * For the purposes of this copyright and license, "Contributing Authors" * */ +/* * is defined as the following set of individuals: * */ +/* * * */ +/* * Gerard Juyn * */ +/* * (hopefully some more to come...) * */ +/* * * */ +/* * The MNG Library is supplied "AS IS". The Contributing Authors * */ +/* * disclaim all warranties, expressed or implied, including, without * */ +/* * limitation, the warranties of merchantability and of fitness for any * */ +/* * purpose. The Contributing Authors assume no liability for direct, * */ +/* * indirect, incidental, special, exemplary, or consequential damages, * */ +/* * which may result from the use of the MNG Library, even if advised of * */ +/* * the possibility of such damage. * */ +/* * * */ +/* * Permission is hereby granted to use, copy, modify, and distribute this * */ +/* * source code, or portions hereof, for any purpose, without fee, subject * */ +/* * to the following restrictions: * */ +/* * * */ +/* * 1. The origin of this source code must not be misrepresented; * */ +/* * you must not claim that you wrote the original software. * */ +/* * * */ +/* * 2. Altered versions must be plainly marked as such and must not be * */ +/* * misrepresented as being the original source. * */ +/* * * */ +/* * 3. This Copyright notice may not be removed or altered from any source * */ +/* * or altered source distribution. * */ +/* * * */ +/* * The Contributing Authors specifically permit, without fee, and * */ +/* * encourage the use of this source code as a component to supporting * */ +/* * the MNG and JNG file format in commercial products. If you use this * */ +/* * source code in a product, acknowledgment would be highly appreciated. * */ +/* * * */ +/* ************************************************************************** */ +/* * * */ +/* * project : bogus * */ +/* * file : bogus.cpp copyright (c) 2000,2002 G.Juyn * */ +/* * version : 1.0.1 * */ +/* * * */ +/* * purpose : main project file * */ +/* * * */ +/* * author : G.Juyn * */ +/* * web : http://www.3-t.com * */ +/* * email : mailto:info@3-t.com * */ +/* * * */ +/* * comment : bogus is (quite literally) a bogus sample which creates and* */ +/* * writes a totally valid, be it somewhat trivial, MNG-file * */ +/* * * */ +/* * changes : 0.5.3 - 06/26/2000 - G.Juyn * */ +/* * - changed userdata variable to mng_ptr * */ +/* * 0.5.3 - 06/28/2000 - G.Juyn * */ +/* * - changed memory allocation size parameters to mng_size_t * */ +/* * * */ +/* * 1.0.1 - 10/07/2002 - G.Juyn * */ +/* * - fixed copyright notice * */ +/* * - updated MHDR simplicity flag * */ +/* * * */ +/* ************************************************************************** */ + +#pragma hdrstop +#include +#include +#include +#include + +#include "libmng.h" + +/* ************************************************************************** */ + +USERES("bogus.res"); +USELIB("..\win32dll\libmng.lib"); +//--------------------------------------------------------------------------- +typedef struct user_struct { + + FILE *hFile; /* file handle */ + + } userdata; + +typedef userdata * userdatap; + +/* ************************************************************************** */ + +#define MY_DECL __stdcall /* get the right callback convention */ + +/* ************************************************************************** */ + +mng_ptr MY_DECL myalloc (mng_size_t iSize) +{ + return (mng_ptr)calloc (1, iSize); /* duh! */ +} + +/* ************************************************************************** */ + +#pragma argsused +void MY_DECL myfree (mng_ptr pPtr, mng_size_t iSize) +{ + free (pPtr); /* duh! */ + return; +} + +/* ************************************************************************** */ + +#pragma argsused +mng_bool MY_DECL myopenstream (mng_handle hMNG) +{ + return MNG_TRUE; /* already opened in main function */ +} + +/* ************************************************************************** */ + +#pragma argsused +mng_bool MY_DECL myclosestream (mng_handle hMNG) +{ + return MNG_TRUE; /* gets closed in main function */ +} + +/* ************************************************************************** */ + +mng_bool MY_DECL mywritedata (mng_handle hMNG, + mng_ptr pBuf, + mng_uint32 iSize, + mng_uint32 *iWritten) +{ /* get to my file handle */ + userdatap pMydata = (userdatap)mng_get_userdata (hMNG); + /* write it */ + *iWritten = fwrite (pBuf, 1, iSize, pMydata->hFile); + /* iWritten will indicate errors */ + return MNG_TRUE; +} + +/* ************************************************************************** */ + +int makeimage (char * zFilename) +{ + userdatap pMydata; + mng_handle hMNG; + mng_retcode iRC; + /* get a data buffer */ + pMydata = (userdatap)calloc (1, sizeof (userdata)); + + if (pMydata == NULL) /* oke ? */ + { + fprintf (stderr, "Cannot allocate a data buffer.\n"); + return 1; + } + /* can we open the file ? */ + if ((pMydata->hFile = fopen (zFilename, "wb")) == NULL) + { /* error out if we can't */ + fprintf (stderr, "Cannot open output file %s.\n", zFilename); + return 1; + } + /* let's initialize the library */ + hMNG = mng_initialize ((mng_ptr)pMydata, myalloc, myfree, MNG_NULL); + + if (!hMNG) /* did that work out ? */ + { + fprintf (stderr, "Cannot initialize libmng.\n"); + iRC = 1; + } + else + { /* setup callbacks */ + if ( ((iRC = mng_setcb_openstream (hMNG, myopenstream )) != 0) || + ((iRC = mng_setcb_closestream (hMNG, myclosestream)) != 0) || + ((iRC = mng_setcb_writedata (hMNG, mywritedata )) != 0) ) + fprintf (stderr, "Cannot set callbacks for libmng.\n"); + else + { /* create the file in memory */ + if ( ((iRC = mng_create (hMNG) ) != 0) || + ((iRC = mng_putchunk_mhdr (hMNG, 640, 480, 1000, 3, 1, 3, 0x0047) ) != 0) || + ((iRC = mng_putchunk_basi (hMNG, 640, 160, 8, 2, 0, 0, 0, 0xFF, 0x00, 0x00, 0xFF, 1)) != 0) || + ((iRC = mng_putchunk_iend (hMNG) ) != 0) || + ((iRC = mng_putchunk_defi (hMNG, 0, 0, 0, MNG_TRUE, 0, 160, MNG_FALSE, 0, 0, 0, 0 )) != 0) || + ((iRC = mng_putchunk_basi (hMNG, 640, 160, 8, 2, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF, 1)) != 0) || + ((iRC = mng_putchunk_iend (hMNG) ) != 0) || + ((iRC = mng_putchunk_defi (hMNG, 0, 0, 0, MNG_TRUE, 0, 320, MNG_FALSE, 0, 0, 0, 0 )) != 0) || + ((iRC = mng_putchunk_basi (hMNG, 640, 160, 8, 2, 0, 0, 0, 0x00, 0x00, 0xFF, 0xFF, 1)) != 0) || + ((iRC = mng_putchunk_iend (hMNG) ) != 0) || + ((iRC = mng_putchunk_mend (hMNG) ) != 0) ) + fprintf (stderr, "Cannot create the chunks for the image.\n"); + else + { + if ((iRC = mng_write (hMNG)) != 0) + fprintf (stderr, "Cannot write the image.\n"); + + } + } + + mng_cleanup (&hMNG); /* cleanup the library */ + } + + fclose (pMydata->hFile); /* cleanup */ + free (pMydata); + + return iRC; +} + +/* ************************************************************************** */ + +#pragma argsused +int main (int argc, char *argv[]) +{ + return makeimage ("dutch.mng"); +} + +/* ************************************************************************** */ diff --git a/Engine/lib/lmng/bcb/mngrepair/mngrepair.bpr b/Engine/lib/lmng/bcb/mngrepair/mngrepair.bpr new file mode 100644 index 000000000..386da9021 --- /dev/null +++ b/Engine/lib/lmng/bcb/mngrepair/mngrepair.bpr @@ -0,0 +1,235 @@ +# --------------------------------------------------------------------------- +!if !$d(BCB) +BCB = $(MAKEDIR)\.. +!endif + +# --------------------------------------------------------------------------- +# IDE SECTION +# --------------------------------------------------------------------------- +# The following section of the project makefile is managed by the BCB IDE. +# It is recommended to use the IDE to change any of the values in this +# section. +# --------------------------------------------------------------------------- + +VERSION = BCB.03 +# --------------------------------------------------------------------------- +PROJECT = mngrepair.exe +OBJFILES = obj\mngrepair.obj obj\libmng_hlapi.obj obj\libmng_callback_xs.obj \ + obj\libmng_prop_xs.obj obj\libmng_chunk_xs.obj obj\libmng_object_prc.obj \ + obj\libmng_chunk_prc.obj obj\libmng_chunk_io.obj obj\libmng_read.obj \ + obj\libmng_write.obj obj\libmng_display.obj obj\libmng_dither.obj \ + obj\libmng_pixels.obj obj\libmng_filter.obj obj\libmng_error.obj \ + obj\libmng_trace.obj obj\libmng_cms.obj obj\libmng_zlib.obj obj\libmng_jpeg.obj \ + obj\adler32.obj obj\compress.obj obj\crc32.obj obj\deflate.obj obj\infblock.obj \ + obj\infcodes.obj obj\inffast.obj obj\inflate.obj obj\inftrees.obj \ + obj\infutil.obj obj\trees.obj obj\uncompr.obj obj\zutil.obj +RESFILES = mngrepair.res +RESDEPEN = $(RESFILES) +LIBFILES = +LIBRARIES = VCL35.lib +SPARELIBS = VCL35.lib +PACKAGES = vclx35.bpi VCL35.bpi vcldb35.bpi vcldbx35.bpi bcbsmp35.bpi dclocx35.bpi \ + Qrpt35.bpi +DEFFILE = +# --------------------------------------------------------------------------- +PATHCPP = .;..\..;..\..\..\zlib +PATHASM = .; +PATHPAS = .; +PATHRC = .; +DEBUGLIBPATH = $(BCB)\lib\debug +RELEASELIBPATH = $(BCB)\lib\release +# --------------------------------------------------------------------------- +CFLAG1 = -Od -Hc -w -r- -d -k -y -v -vi- -w-par -c -tWC +CFLAG2 = -D_NO_VCL;MNG_SUPPORT_FULL;MNG_SUPPORT_READ;MNG_SUPPORT_WRITE;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_INCLUDE_ZLIB;MNG_NO_CMS;MNG_SOFTERRORS \ + -I..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;$(BCB)\include \ + -H=$(BCB)\lib\vcl35.csm +CFLAG3 = -Tkh30000 -ff -5 -wuse -wucp -wstv -wstu -wsig -wpin -wnod -wnak -wdef -wcln \ + -wbbf -wasm -wamp -wamb +PFLAGS = -D_NO_VCL;MNG_SUPPORT_FULL;MNG_SUPPORT_READ;MNG_SUPPORT_WRITE;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_INCLUDE_ZLIB;MNG_NO_CMS;MNG_SOFTERRORS \ + -N2.\obj -N0.\obj \ + -U..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;$(BCB)\lib;$(RELEASELIBPATH) \ + -I..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;$(BCB)\include -H -W \ + -$Y -$W -$O- -v -JPHN -M +RFLAGS = -D_NO_VCL;MNG_SUPPORT_FULL;MNG_SUPPORT_READ;MNG_SUPPORT_WRITE;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_INCLUDE_ZLIB;MNG_NO_CMS;MNG_SOFTERRORS \ + -i..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;$(BCB)\include +AFLAGS = /i..\..\..\libmng-devel /i..\..\..\zlib /i..\..\..\jpgsrc6b /i$(BCB)\include \ + /d_NO_VCL /dMNG_SUPPORT_FULL /dMNG_SUPPORT_READ /dMNG_SUPPORT_WRITE \ + /dMNG_ACCESS_CHUNKS /dMNG_STORE_CHUNKS /dMNG_INCLUDE_ZLIB /dMNG_NO_CMS \ + /dMNG_SOFTERRORS /mx /w2 /zd +LFLAGS = -L..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;$(BCB)\lib;$(RELEASELIBPATH) \ + -ap -Tpe -x -Gn -wdef -wdpl -v +IFLAGS = +# --------------------------------------------------------------------------- +ALLOBJ = c0x32.obj $(OBJFILES) +ALLRES = $(RESFILES) +ALLLIB = $(LIBFILES) import32.lib cw32mt.lib +# --------------------------------------------------------------------------- +!ifdef IDEOPTIONS + +[Version Info] +IncludeVerInfo=1 +AutoIncBuild=1 +MajorVer=1 +MinorVer=0 +Release=0 +Build=27 +Debug=1 +PreRelease=0 +Special=0 +Private=0 +DLL=0 +Locale=1033 +CodePage=1252 + +[Version Info Keys] +CompanyName= +FileDescription=Executable (Console) +FileVersion=1.0.0.27 +InternalName=mngrepair +LegalCopyright=copyright (c) 2002 G.Juyn +LegalTrademarks= +OriginalFilename=mngrepair.exe +ProductName=mngrepair +ProductVersion=1.0 +Comments= + +[Excluded Packages] +C:\Program Files\Borland\CBuilder3\Bin\DbX35.bpl=(untitled) + +[HistoryLists\hlIncludePath] +Count=9 +Item0=..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;$(BCB)\include +Item1=..\..\..\libmng-devel;..\..;..\..\..\zlib;..\..\..\jpgsrc6b;$(BCB)\include +Item2=..\..;..\..\..\zlib;..\..\..\jpgsrc6b;$(BCB)\include +Item3=..\..\..\libmng-devel;..\..;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms;$(BCB)\include +Item4=..\..;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms;$(BCB)\include +Item5=..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms;$(BCB)\include +Item6=..\..\..\libmng;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms;$(BCB)\include +Item7=..\..\..\libmng;..\..\..\zlib;$(BCB)\include +Item8=..\..\..\libmng;$(BCB)\include + +[HistoryLists\hlLibraryPath] +Count=7 +Item0=..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;$(BCB)\lib +Item1=..\..\..\libmng-devel;..\..;..\..\..\zlib;..\..\..\jpgsrc6b;$(BCB)\lib +Item2=..\..;..\..\..\zlib;..\..\..\jpgsrc6b;$(BCB)\lib +Item3=..\..\..\jpgsrc6b;..\..\..\zlib;..\..\..\libmng-devel;..\win32dll;$(BCB)\lib +Item4=..\win32dll;$(BCB)\lib +Item5=..\..\..\libmng\bcb\win32dll;$(BCB)\lib +Item6=..\..\..\libmng;$(BCB)\lib + +[HistoryLists\hlDebugSourcePath] +Count=1 +Item0=..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b + +[HistoryLists\hlConditionals] +Count=13 +Item0=_NO_VCL;MNG_SUPPORT_FULL;MNG_SUPPORT_READ;MNG_SUPPORT_WRITE;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_INCLUDE_ZLIB;MNG_NO_CMS;MNG_SOFTERRORS +Item1=_NO_VCL;MNG_SUPPORT_FULL;MNG_SUPPORT_READ;MNG_SUPPORT_WRITE;MNG_SUPPORT_DISPLAY;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_INCLUDE_ZLIB;MNG_INCLUDE_IJG6B;MNG_NO_CMS;MNG_DEFINE_JPEG_STDCALL;MNG_SOFTERRORS +Item2=_NO_VCL;MNG_SUPPORT_FULL;MNG_SUPPORT_READ;MNG_SUPPORT_WRITE;MNG_SUPPORT_DISPLAY;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_INCLUDE_ZLIB;MNG_INCLUDE_IJG6B;MNG_NO_CMS;MNG_DEFINE_JPEG_STDCALL;MNG_SOFTERROR +Item3=_NO_VCL;MNG_SUPPORT_FULL;MNG_SUPPORT_READ;MNG_SUPPORT_WRITE;MNG_SUPPORT_DISPLAY;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_INCLUDE_ZLIB;MNG_INCLUDE_IJG6B;MNG_NO_CMS;MNG_DEFINE_JPEG_STDCALL +Item4=_NO_VCL;MNG_SUPPORT_FULL;MNG_SUPPORT_READ;MNG_SUPPORT_WRITE;MNG_SUPPORT_DISPLAY;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_INCLUDE_ZLIB;MNG_INCLUDE_IJG;MNG_NO_CMS;MNG_DEFINE_JPEG_STDCALL +Item5=_NO_VCL;MNG_SUPPORT_FULL;MNG_SUPPORT_READ;MNG_SUPPORT_WRITE;MNG_SUPPORT_DISPLAY;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_NO_CMS;MNG_DEFINE_JPEG_STDCALL +Item6=_NO_VCL;MNG_SUPPORT_READ;MNG_SUPPORT_WRITE;MNG_SUPPORT_DISPLAY;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_NO_CMS;MNG_DEFINE_JPEG_STDCALL;MNG_SUPPORT_IJG6B +Item7=_NO_VCL;MNG_SUPPORT_READ;MNG_SUPPORT_WRITE;MNG_SUPPORT_DISPLAY;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_NO_CMS;MNG_DEFINE_JPEG_STDCALL +Item8=_NO_VCL;MNG_SUPPORT_READ;MNG_SUPPORT_WRITE;MNG_SUPPORT_DISPLAY;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_NO_CMS +Item9=_NO_VCL;MNG_SUPPORT_READ;MNG_SUPPORT_WRITE;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_NO_CMS +Item10=_NO_VCL;MNG_SUPPORT_READ;MNG_SUPPORT_WRITE;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_NO_CMS;MNG_USE_DLL +Item11=_NO_VCL;MNG_SUPPORT_READ;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_NO_CMS;MNG_USE_DLL +Item12=_NO_VCL;MNG_SUPPORT_READ;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_NO_CMS + +[HistoryLists\hlIntOutputDir] +Count=2 +Item0=.\obj +Item1=..\..\..\obj + +[HistoryLists\hlRunParameters] +Count=4 +Item0=roilion02.mng roilion02-fixed.mng +Item1=roilion.mng roilion-fixed.mng +Item2=dutch.mng +Item3=usflag-lc-d63.mng + +[Debugging] +DebugSourceDirs=..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b + +[Parameters] +RunParams=roilion02.mng roilion02-fixed.mng +HostApplication= + +!endif + +# --------------------------------------------------------------------------- +# MAKE SECTION +# --------------------------------------------------------------------------- +# This section of the project file is not used by the BCB IDE. It is for +# the benefit of building from the command-line using the MAKE utility. +# --------------------------------------------------------------------------- + +.autodepend +# --------------------------------------------------------------------------- +!if !$d(BCC32) +BCC32 = bcc32 +!endif + +!if !$d(DCC32) +DCC32 = dcc32 +!endif + +!if !$d(TASM32) +TASM32 = tasm32 +!endif + +!if !$d(LINKER) +LINKER = ilink32 +!endif + +!if !$d(BRCC32) +BRCC32 = brcc32 +!endif +# --------------------------------------------------------------------------- +!if $d(PATHCPP) +.PATH.CPP = $(PATHCPP) +.PATH.C = $(PATHCPP) +!endif + +!if $d(PATHPAS) +.PATH.PAS = $(PATHPAS) +!endif + +!if $d(PATHASM) +.PATH.ASM = $(PATHASM) +!endif + +!if $d(PATHRC) +.PATH.RC = $(PATHRC) +!endif +# --------------------------------------------------------------------------- +$(PROJECT): $(OBJFILES) $(RESDEPEN) $(DEFFILE) + $(BCB)\BIN\$(LINKER) @&&! + $(LFLAGS) + + $(ALLOBJ), + + $(PROJECT),, + + $(ALLLIB), + + $(DEFFILE), + + $(ALLRES) +! +# --------------------------------------------------------------------------- +.pas.hpp: + $(BCB)\BIN\$(DCC32) $(PFLAGS) {$< } + +.pas.obj: + $(BCB)\BIN\$(DCC32) $(PFLAGS) {$< } + +.cpp.obj: + $(BCB)\BIN\$(BCC32) $(CFLAG1) $(CFLAG2) $(CFLAG3) -n$(@D) {$< } + +.c.obj: + $(BCB)\BIN\$(BCC32) $(CFLAG1) $(CFLAG2) $(CFLAG3) -n$(@D) {$< } + +.asm.obj: + $(BCB)\BIN\$(TASM32) $(AFLAGS) $<, $@ + +.rc.res: + $(BCB)\BIN\$(BRCC32) $(RFLAGS) -fo$@ $< +# --------------------------------------------------------------------------- diff --git a/Engine/lib/lmng/bcb/mngrepair/mngrepair.cpp b/Engine/lib/lmng/bcb/mngrepair/mngrepair.cpp new file mode 100644 index 000000000..5f7c6ec19 --- /dev/null +++ b/Engine/lib/lmng/bcb/mngrepair/mngrepair.cpp @@ -0,0 +1,489 @@ +/* ************************************************************************** */ +/* * * */ +/* * COPYRIGHT NOTICE: * */ +/* * * */ +/* * Copyright (c) 2000-2002 Gerard Juyn (gerard@libmng.com) * */ +/* * [You may insert additional notices after this sentence if you modify * */ +/* * this source] * */ +/* * * */ +/* * For the purposes of this copyright and license, "Contributing Authors" * */ +/* * is defined as the following set of individuals: * */ +/* * * */ +/* * Gerard Juyn * */ +/* * (hopefully some more to come...) * */ +/* * * */ +/* * The MNG Library is supplied "AS IS". The Contributing Authors * */ +/* * disclaim all warranties, expressed or implied, including, without * */ +/* * limitation, the warranties of merchantability and of fitness for any * */ +/* * purpose. The Contributing Authors assume no liability for direct, * */ +/* * indirect, incidental, special, exemplary, or consequential damages, * */ +/* * which may result from the use of the MNG Library, even if advised of * */ +/* * the possibility of such damage. * */ +/* * * */ +/* * Permission is hereby granted to use, copy, modify, and distribute this * */ +/* * source code, or portions hereof, for any purpose, without fee, subject * */ +/* * to the following restrictions: * */ +/* * * */ +/* * 1. The origin of this source code must not be misrepresented; * */ +/* * you must not claim that you wrote the original software. * */ +/* * * */ +/* * 2. Altered versions must be plainly marked as such and must not be * */ +/* * misrepresented as being the original source. * */ +/* * * */ +/* * 3. This Copyright notice may not be removed or altered from any source * */ +/* * or altered source distribution. * */ +/* * * */ +/* * The Contributing Authors specifically permit, without fee, and * */ +/* * encourage the use of this source code as a component to supporting * */ +/* * the MNG and JNG file format in commercial products. If you use this * */ +/* * source code in a product, acknowledgment would be highly appreciated. * */ +/* * * */ +/* ************************************************************************** */ +/* * * */ +/* * project : mngrepair * */ +/* * file : mngrepair.cpp copyright (c) 2002 G.Juyn * */ +/* * version : 1.0.0 * */ +/* * * */ +/* * purpose : main project file * */ +/* * * */ +/* * author : G.Juyn * */ +/* * web : http://www.3-t.com * */ +/* * email : mailto:info@3-t.com * */ +/* * * */ +/* * comment : mngrepair iterates tries to fix a couple of 'common' * */ +/* * MNG encoding errors (such as in JASC Animation Shop) * */ +/* * * */ +/* * changes : * */ +/* * * */ +/* ************************************************************************** */ + +#define HAVE_BOOLEAN + +#include +#pragma hdrstop +#include + +#include "libmng.h" + +/* ************************************************************************** */ + +USERES("mngrepair.res"); +USEUNIT("..\..\libmng_hlapi.c"); +USEUNIT("..\..\libmng_callback_xs.c"); +USEUNIT("..\..\libmng_prop_xs.c"); +USEUNIT("..\..\libmng_chunk_xs.c"); +USEUNIT("..\..\libmng_object_prc.c"); +USEUNIT("..\..\libmng_chunk_prc.c"); +USEUNIT("..\..\libmng_chunk_io.c"); +USEUNIT("..\..\libmng_read.c"); +USEUNIT("..\..\libmng_write.c"); +USEUNIT("..\..\libmng_display.c"); +USEUNIT("..\..\libmng_dither.c"); +USEUNIT("..\..\libmng_pixels.c"); +USEUNIT("..\..\libmng_filter.c"); +USEUNIT("..\..\libmng_error.c"); +USEUNIT("..\..\libmng_trace.c"); +USEUNIT("..\..\libmng_cms.c"); +USEUNIT("..\..\libmng_zlib.c"); +USEUNIT("..\..\libmng_jpeg.c"); +USEUNIT("..\..\..\zlib\adler32.c"); +USEUNIT("..\..\..\zlib\compress.c"); +USEUNIT("..\..\..\zlib\crc32.c"); +USEUNIT("..\..\..\zlib\deflate.c"); +USEUNIT("..\..\..\zlib\infblock.c"); +USEUNIT("..\..\..\zlib\infcodes.c"); +USEUNIT("..\..\..\zlib\inffast.c"); +USEUNIT("..\..\..\zlib\inflate.c"); +USEUNIT("..\..\..\zlib\inftrees.c"); +USEUNIT("..\..\..\zlib\infutil.c"); +USEUNIT("..\..\..\zlib\trees.c"); +USEUNIT("..\..\..\zlib\uncompr.c"); +USEUNIT("..\..\..\zlib\zutil.c"); +//--------------------------------------------------------------------------- +typedef struct user_struct { + + FILE *hFileI; /* input file handle */ + FILE *hFileO; /* output file handle */ + mng_handle hHandleI; /* input mng handle */ + mng_handle hHandleO; /* output mng handle */ + mng_bool bHasSAVE; /* indicates a SAVE in the input */ + mng_bool bHasTERM; /* indicates we saved the TERM */ + mng_bool bIsJASC; /* indicates if this is an AS file */ + mng_chunkid iLastchunk; /* last processed chunk */ + mng_retcode iError; /* errorcode from function in callback */ + mng_uint8 iTermaction; /* saved TERM parameters */ + mng_uint8 iIteraction; + mng_uint32 iDelay; + mng_uint32 iItermax; + + } userdata; + +typedef userdata * userdatap; + +/* ************************************************************************** */ + +#define MY_DECL /* get the right callback convention */ + +/* ************************************************************************** */ + +mng_ptr MY_DECL myalloc (mng_size_t iSize) +{ /* duh! */ + return (mng_ptr)calloc (1, (size_t)iSize); +} + +/* ************************************************************************** */ + +#pragma argsused +void MY_DECL myfree (mng_ptr pPtr, mng_size_t iSize) +{ + free (pPtr); /* duh! */ + return; +} + +/* ************************************************************************** */ + +#pragma argsused +mng_bool MY_DECL myopenstream (mng_handle hMNG) +{ + return MNG_TRUE; /* already opened in main function */ +} + +/* ************************************************************************** */ + +#pragma argsused +mng_bool MY_DECL myclosestream (mng_handle hMNG) +{ + return MNG_TRUE; /* gets closed in main function */ +} + +/* ************************************************************************** */ + +mng_bool MY_DECL myreaddata (mng_handle hMNG, + mng_ptr pBuf, + mng_uint32 iSize, + mng_uint32 *iRead) +{ /* get to my file handle */ + userdatap pMydata = (userdatap)mng_get_userdata (hMNG); + /* read it */ + *iRead = fread (pBuf, 1, iSize, pMydata->hFileI); + /* iRead will indicate EOF */ + return MNG_TRUE; +} + +/* ************************************************************************** */ + +mng_bool MY_DECL mywritedata (mng_handle hMNG, + mng_ptr pBuf, + mng_uint32 iSize, + mng_uint32 *iWritten) +{ /* get to my file handle */ + userdatap pMydata = (userdatap)mng_get_userdata (hMNG); + /* write it */ + *iWritten = fwrite (pBuf, 1, iSize, pMydata->hFileO); + /* iWritten will indicate errors */ + return MNG_TRUE; +} + +/* ************************************************************************** */ + +mng_bool MY_DECL myprocesserror (mng_handle hMNG, + mng_int32 iErrorcode, + mng_int8 iSeverity, + mng_chunkid iChunkname, + mng_uint32 iChunkseq, + mng_int32 iExtra1, + mng_int32 iExtra2, + mng_pchar zErrortext) +{ /* sequence error for TERM we ignore ! */ + if ((iErrorcode == MNG_SEQUENCEERROR) && (iChunkname == MNG_UINT_TERM)) + return MNG_TRUE; + + return MNG_FALSE; /* all others get dumped ! */ +} + +/* ************************************************************************** */ + +#pragma argsused +mng_bool MY_DECL myiterchunk (mng_handle hMNG, + mng_handle hChunk, + mng_chunkid iChunktype, + mng_uint32 iChunkseq) +{ + mng_uint32 iWidth; /* temps for IHDR processing */ + mng_uint32 iHeight; + mng_uint8 iBitdepth; + mng_uint8 iColortype; + mng_uint8 iCompression; + mng_uint8 iFilter; + mng_uint8 iInterlace; + + mng_bool bEmpty; /* temps for FRAM processing */ + mng_uint8 iMode; + mng_uint32 iNamesize; + mng_pchar zName; + mng_uint8 iChangedelay; + mng_uint8 iChangetimeout; + mng_uint8 iChangeclipping; + mng_uint8 iChangesyncid; + mng_uint32 iDelay; + mng_uint32 iTimeout; + mng_uint8 iBoundarytype; + mng_int32 iBoundaryl; + mng_int32 iBoundaryr; + mng_int32 iBoundaryt; + mng_int32 iBoundaryb; + mng_uint32 iCount; + mng_uint32p pSyncids; + /* get to my userdata */ + userdatap pMydata = (userdatap)mng_get_userdata (hMNG); + + if (pMydata->hFileO) /* are we writing this time ? */ + { /* do we need to 'forget' the TERM ? */ + if ((iChunktype == MNG_UINT_TERM) && (pMydata->bHasTERM)) + ; + else + { /* fix JASC AS frame_type ? */ + if ((iChunktype == MNG_UINT_FRAM) && (pMydata->bIsJASC)) + { + if ((pMydata->iError = mng_getchunk_fram (hMNG, hChunk, + &bEmpty, &iMode, &iNamesize, &zName, + &iChangedelay, &iChangetimeout, + &iChangeclipping, &iChangesyncid, + &iDelay, &iTimeout, &iBoundarytype, + &iBoundaryl, &iBoundaryr, + &iBoundaryt, &iBoundaryb, + &iCount, &pSyncids)) != 0) + { + fprintf (stderr, "Cannot get FRAM fields.\n"); + return MNG_FALSE; /* stop the process ! */ + } + + if (iMode == 1) /* really ? */ + iMode = 3; + + if ((pMydata->iError = mng_putchunk_fram (pMydata->hHandleO, + bEmpty, iMode, iNamesize, zName, + iChangedelay, iChangetimeout, + iChangeclipping, iChangesyncid, + iDelay, iTimeout, iBoundarytype, + iBoundaryl, iBoundaryr, + iBoundaryt, iBoundaryb, + iCount, pSyncids)) != 0) + { + fprintf (stderr, "Cannot write FRAM chunk.\n"); + return MNG_FALSE; /* stop the process ! */ + } + } + else + { + if ((pMydata->iError = mng_copy_chunk (hMNG, hChunk, pMydata->hHandleO)) != 0) + { + fprintf (stderr, "Cannot copy the chunk.\n"); + return MNG_FALSE; /* stop the process ! */ + } + } + } + /* need to insert TERM in the proper place ? */ + if ((iChunktype == MNG_UINT_MHDR) && (pMydata->bHasTERM)) + { + if ((pMydata->iError = mng_putchunk_term (pMydata->hHandleO, + pMydata->iTermaction, + pMydata->iIteraction, + pMydata->iDelay, + pMydata->iItermax)) != 0) + { + fprintf (stderr, "Cannot write TERM chunk.\n"); + return MNG_FALSE; /* stop the process ! */ + } + } + } + else /* first iteration; just checking stuff */ + { + if (iChunktype == MNG_UINT_SAVE) /* SAVE ? */ + { + pMydata->bHasSAVE = MNG_TRUE; /* we got a SAVE ! */ + pMydata->bIsJASC = MNG_FALSE; /* so it's definitely not an invalid AS file */ + } + else + { /* TERM ? */ + if (iChunktype == MNG_UINT_TERM) + { /* is it in the wrong place ? */ + if ((pMydata->iLastchunk != MNG_UINT_MHDR) || + (pMydata->iLastchunk != MNG_UINT_SAVE) ) + { + pMydata->bHasTERM = MNG_TRUE; + + if ((pMydata->iError = mng_getchunk_term (hMNG, hChunk, + &pMydata->iTermaction, + &pMydata->iIteraction, + &pMydata->iDelay, + &pMydata->iItermax)) != 0) + { + fprintf (stderr, "Cannot get TERM fields.\n"); + return MNG_FALSE; /* stop the process ! */ + } + } + } + else + { /* IHDR ? */ + if (iChunktype == MNG_UINT_IHDR) + { + if ((pMydata->iError = mng_getchunk_ihdr (hMNG, hChunk, + &iWidth, &iHeight, &iBitdepth, + &iColortype, &iCompression, + &iFilter, &iInterlace)) != 0) + { + fprintf (stderr, "Cannot get IHDR fields.\n"); + return MNG_FALSE; /* stop the process ! */ + } + /* is it not a typical JASC AS file */ + if ((iBitdepth != 8) || (iColortype != 6)) + pMydata->bIsJASC = MNG_FALSE; + } + } + } + + pMydata->iLastchunk = iChunktype; + } + + return MNG_TRUE; /* keep'm coming... */ +} + +/* ************************************************************************** */ + +int fixit (char * zFilenameI, + char * zFilenameO) +{ + userdatap pMydata; + mng_retcode iRC; + /* get a data buffer */ + pMydata = (userdatap)calloc (1, sizeof (userdata)); + + if (pMydata == NULL) /* oke ? */ + { + fprintf (stderr, "Cannot allocate a data buffer.\n"); + return 1; + } + + pMydata->hFileO = 0; /* initialize some stuff! */ + pMydata->hHandleI = MNG_NULL; + pMydata->hHandleO = MNG_NULL; + pMydata->bHasSAVE = MNG_FALSE; + pMydata->bHasTERM = MNG_FALSE; + pMydata->bIsJASC = MNG_TRUE; + pMydata->iLastchunk = MNG_UINT_HUH; + pMydata->iTermaction = 0; + pMydata->iIteraction = 0; + pMydata->iDelay = 0; + pMydata->iItermax = 0; + /* can we open the input file ? */ + if ((pMydata->hFileI = fopen (zFilenameI, "rb")) == NULL) + { /* error out if we can't */ + fprintf (stderr, "Cannot open input file %s.\n", zFilenameI); + return 1; + } + /* let's initialize the library */ + pMydata->hHandleI = mng_initialize ((mng_ptr)pMydata, myalloc, myfree, MNG_NULL); + + if (!pMydata->hHandleI) /* did that work out ? */ + { + fprintf (stderr, "Cannot initialize libmng.\n"); + iRC = 1; + } + else + { /* some informatory messages */ + fprintf (stderr, "Compiled with libmng %s.\n", MNG_VERSION_TEXT); + fprintf (stderr, "Running with libmng %s.\n", mng_version_text()); + /* setup callbacks */ + if ( ((iRC = mng_setcb_openstream (pMydata->hHandleI, myopenstream )) != 0) || + ((iRC = mng_setcb_closestream (pMydata->hHandleI, myclosestream )) != 0) || + ((iRC = mng_setcb_readdata (pMydata->hHandleI, myreaddata )) != 0) || + ((iRC = mng_setcb_errorproc (pMydata->hHandleI, myprocesserror)) != 0) ) + fprintf (stderr, "Cannot set callbacks for libmng.\n"); + else + { /* reaad the file into memory */ + if ((iRC = mng_read (pMydata->hHandleI)) != 0) + fprintf (stderr, "Cannot read the input file.\n"); + else + { /* run through the chunk list to get TERM */ + if ((iRC = mng_iterate_chunks (pMydata->hHandleI, 0, myiterchunk)) != 0) + fprintf (stderr, "Cannot iterate the chunks.\n"); + else + { + if (pMydata->iError) /* did the iteration fail somehow ? */ + iRC = pMydata->iError; + else + { /* can we open the output file ? */ + if ((pMydata->hFileO = fopen (zFilenameO, "wb")) == NULL) + { /* error out if we can't */ + fprintf (stderr, "Cannot open output file %s.\n", zFilenameO); + iRC = 1; + } + else + { /* let's initialize the library */ + pMydata->hHandleO = mng_initialize ((mng_ptr)pMydata, myalloc, myfree, MNG_NULL); + + if (!pMydata->hHandleO) /* did that work out ? */ + { + fprintf (stderr, "Cannot initialize libmng.\n"); + iRC = 1; + } + else + { /* setup callbacks */ + if ( ((iRC = mng_setcb_openstream (pMydata->hHandleO, myopenstream )) != 0) || + ((iRC = mng_setcb_closestream (pMydata->hHandleO, myclosestream)) != 0) || + ((iRC = mng_setcb_writedata (pMydata->hHandleO, mywritedata )) != 0) ) + fprintf (stderr, "Cannot set callbacks for libmng.\n"); + else + { + if ((iRC = mng_create (pMydata->hHandleO)) != 0) + fprintf (stderr, "Cannot create a new MNG.\n"); + else + { /* run through the chunk again and create the new file */ + if ((iRC = mng_iterate_chunks (pMydata->hHandleI, 0, myiterchunk)) != 0) + fprintf (stderr, "Cannot iterate the chunks.\n"); + else + { /* did the iteration fail somehow ? */ + if (pMydata->iError) + iRC = pMydata->iError; + else + { /* now write the created new file !! */ + if ((iRC = mng_write (pMydata->hHandleO)) != 0) + fprintf (stderr, "Cannot write the output file.\n"); + } + } + } + } + /* cleanup the library */ + mng_cleanup (&pMydata->hHandleO); + } + /* cleanup output file */ + fclose (pMydata->hFileO); + } + } + } + } + } + + mng_cleanup (&pMydata->hHandleI); /* cleanup the library */ + } + + fclose (pMydata->hFileI); /* cleanup input file and userdata */ + free (pMydata); + + return iRC; +} + +/* ************************************************************************** */ + +int main(int argc, char *argv[]) +{ + if (argc > 2) /* need two (2) parameters ! */ + return fixit (argv[1], argv[2]); + else + fprintf (stdout, "\nUsage: mngrepair \n\n"); + + return 0; +} + +/* ************************************************************************** */ diff --git a/Engine/lib/lmng/bcb/mngrepair/mngrepair.res b/Engine/lib/lmng/bcb/mngrepair/mngrepair.res new file mode 100644 index 000000000..12530c34f Binary files /dev/null and b/Engine/lib/lmng/bcb/mngrepair/mngrepair.res differ diff --git a/Engine/lib/lmng/bcb/mngtree/mngtree.bpr b/Engine/lib/lmng/bcb/mngtree/mngtree.bpr new file mode 100644 index 000000000..656a04130 --- /dev/null +++ b/Engine/lib/lmng/bcb/mngtree/mngtree.bpr @@ -0,0 +1,188 @@ +# --------------------------------------------------------------------------- +!if !$d(BCB) +BCB = $(MAKEDIR)\.. +!endif + +# --------------------------------------------------------------------------- +# IDE SECTION +# --------------------------------------------------------------------------- +# The following section of the project makefile is managed by the BCB IDE. +# It is recommended to use the IDE to change any of the values in this +# section. +# --------------------------------------------------------------------------- + +VERSION = BCB.03 +# --------------------------------------------------------------------------- +PROJECT = mngtree.exe +OBJFILES = mngtree.obj +RESFILES = mngtree.res +RESDEPEN = $(RESFILES) +LIBFILES = ..\win32dll\libmng.lib +LIBRARIES = VCL35.lib +SPARELIBS = VCL35.lib +PACKAGES = vclx35.bpi VCL35.bpi vcldb35.bpi vcldbx35.bpi bcbsmp35.bpi dclocx35.bpi \ + Qrpt35.bpi +DEFFILE = +# --------------------------------------------------------------------------- +PATHCPP = .; +PATHASM = .; +PATHPAS = .; +PATHRC = .; +DEBUGLIBPATH = $(BCB)\lib\debug +RELEASELIBPATH = $(BCB)\lib\release +# --------------------------------------------------------------------------- +CFLAG1 = -Od -w -r- -k -y -v -vi- -c -tWC +CFLAG2 = -D_NO_VCL;MNG_SUPPORT_READ;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_NO_CMS;MNG_USE_DLL \ + -I..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms;$(BCB)\include +CFLAG3 = -Tkh30000 +PFLAGS = -D_NO_VCL;MNG_SUPPORT_READ;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_NO_CMS;MNG_USE_DLL \ + -U..\win32dll;$(BCB)\lib;$(DEBUGLIBPATH) \ + -I..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms;$(BCB)\include \ + -$Y -$W -$O- -v -JPHN -M +RFLAGS = -D_NO_VCL;MNG_SUPPORT_READ;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_NO_CMS;MNG_USE_DLL \ + -i..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms;$(BCB)\include +AFLAGS = /i..\..\..\libmng-devel /i..\..\..\zlib /i..\..\..\jpgsrc6b /i..\..\..\lcms \ + /i$(BCB)\include /d_NO_VCL /dMNG_SUPPORT_READ /dMNG_ACCESS_CHUNKS \ + /dMNG_STORE_CHUNKS /dMNG_NO_CMS /dMNG_USE_DLL /mx /w2 /zd +LFLAGS = -L..\win32dll;$(BCB)\lib;$(DEBUGLIBPATH) -ap -Tpe -x -Gn -v +IFLAGS = +# --------------------------------------------------------------------------- +ALLOBJ = c0x32.obj $(OBJFILES) +ALLRES = $(RESFILES) +ALLLIB = $(LIBFILES) import32.lib cw32mt.lib libmng.lib +# --------------------------------------------------------------------------- +!ifdef IDEOPTIONS + +[Version Info] +IncludeVerInfo=1 +AutoIncBuild=1 +MajorVer=0 +MinorVer=9 +Release=0 +Build=13 +Debug=1 +PreRelease=1 +Special=0 +Private=0 +DLL=0 +Locale=1033 +CodePage=1252 + +[Version Info Keys] +CompanyName= +FileDescription=Executable (Console) +FileVersion=0.9.0.13 +InternalName=mngtree +LegalCopyright=copyright (c) 2000 G.Juyn +LegalTrademarks= +OriginalFilename=mngtree.exe +ProductName=mngtree +ProductVersion=0.9.0 +Comments= + +[HistoryLists\hlIncludePath] +Count=4 +Item0=..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms;$(BCB)\include +Item1=..\..\..\libmng;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms;$(BCB)\include +Item2=..\..\..\libmng;..\..\..\zlib;$(BCB)\include +Item3=..\..\..\libmng;$(BCB)\include + +[HistoryLists\hlLibraryPath] +Count=3 +Item0=..\win32dll;$(BCB)\lib +Item1=..\..\..\libmng\bcb\win32dll;$(BCB)\lib +Item2=..\..\..\libmng;$(BCB)\lib + +[HistoryLists\hlConditionals] +Count=2 +Item0=_NO_VCL;MNG_SUPPORT_READ;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_NO_CMS;MNG_USE_DLL +Item1=_NO_VCL;MNG_SUPPORT_READ;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_NO_CMS + +[HistoryLists\hlRunParameters] +Count=2 +Item0=dutch.mng +Item1=usflag-lc-d63.mng + +[Debugging] +DebugSourceDirs= + +[Parameters] +RunParams=dutch.mng +HostApplication= + +!endif + +# --------------------------------------------------------------------------- +# MAKE SECTION +# --------------------------------------------------------------------------- +# This section of the project file is not used by the BCB IDE. It is for +# the benefit of building from the command-line using the MAKE utility. +# --------------------------------------------------------------------------- + +.autodepend +# --------------------------------------------------------------------------- +!if !$d(BCC32) +BCC32 = bcc32 +!endif + +!if !$d(DCC32) +DCC32 = dcc32 +!endif + +!if !$d(TASM32) +TASM32 = tasm32 +!endif + +!if !$d(LINKER) +LINKER = ilink32 +!endif + +!if !$d(BRCC32) +BRCC32 = brcc32 +!endif +# --------------------------------------------------------------------------- +!if $d(PATHCPP) +.PATH.CPP = $(PATHCPP) +.PATH.C = $(PATHCPP) +!endif + +!if $d(PATHPAS) +.PATH.PAS = $(PATHPAS) +!endif + +!if $d(PATHASM) +.PATH.ASM = $(PATHASM) +!endif + +!if $d(PATHRC) +.PATH.RC = $(PATHRC) +!endif +# --------------------------------------------------------------------------- +$(PROJECT): $(OBJFILES) $(RESDEPEN) $(DEFFILE) + $(BCB)\BIN\$(LINKER) @&&! + $(LFLAGS) + + $(ALLOBJ), + + $(PROJECT),, + + $(ALLLIB), + + $(DEFFILE), + + $(ALLRES) +! +# --------------------------------------------------------------------------- +.pas.hpp: + $(BCB)\BIN\$(DCC32) $(PFLAGS) {$< } + +.pas.obj: + $(BCB)\BIN\$(DCC32) $(PFLAGS) {$< } + +.cpp.obj: + $(BCB)\BIN\$(BCC32) $(CFLAG1) $(CFLAG2) $(CFLAG3) -n$(@D) {$< } + +.c.obj: + $(BCB)\BIN\$(BCC32) $(CFLAG1) $(CFLAG2) $(CFLAG3) -n$(@D) {$< } + +.asm.obj: + $(BCB)\BIN\$(TASM32) $(AFLAGS) $<, $@ + +.rc.res: + $(BCB)\BIN\$(BRCC32) $(RFLAGS) -fo$@ $< +# --------------------------------------------------------------------------- diff --git a/Engine/lib/lmng/bcb/mngtree/mngtree.cpp b/Engine/lib/lmng/bcb/mngtree/mngtree.cpp new file mode 100644 index 000000000..d16c68e9c --- /dev/null +++ b/Engine/lib/lmng/bcb/mngtree/mngtree.cpp @@ -0,0 +1,249 @@ +/* ************************************************************************** */ +/* * * */ +/* * COPYRIGHT NOTICE: * */ +/* * * */ +/* * Copyright (c) 2000 Gerard Juyn (gerard@libmng.com) * */ +/* * [You may insert additional notices after this sentence if you modify * */ +/* * this source] * */ +/* * * */ +/* * For the purposes of this copyright and license, "Contributing Authors" * */ +/* * is defined as the following set of individuals: * */ +/* * * */ +/* * Gerard Juyn * */ +/* * (hopefully some more to come...) * */ +/* * * */ +/* * The MNG Library is supplied "AS IS". The Contributing Authors * */ +/* * disclaim all warranties, expressed or implied, including, without * */ +/* * limitation, the warranties of merchantability and of fitness for any * */ +/* * purpose. The Contributing Authors assume no liability for direct, * */ +/* * indirect, incidental, special, exemplary, or consequential damages, * */ +/* * which may result from the use of the MNG Library, even if advised of * */ +/* * the possibility of such damage. * */ +/* * * */ +/* * Permission is hereby granted to use, copy, modify, and distribute this * */ +/* * source code, or portions hereof, for any purpose, without fee, subject * */ +/* * to the following restrictions: * */ +/* * * */ +/* * 1. The origin of this source code must not be misrepresented; * */ +/* * you must not claim that you wrote the original software. * */ +/* * * */ +/* * 2. Altered versions must be plainly marked as such and must not be * */ +/* * misrepresented as being the original source. * */ +/* * * */ +/* * 3. This Copyright notice may not be removed or altered from any source * */ +/* * or altered source distribution. * */ +/* * * */ +/* * The Contributing Authors specifically permit, without fee, and * */ +/* * encourage the use of this source code as a component to supporting * */ +/* * the MNG and JNG file format in commercial products. If you use this * */ +/* * source code in a product, acknowledgment would be highly appreciated. * */ +/* * * */ +/* ************************************************************************** */ +/* * * */ +/* * project : mngtree * */ +/* * file : mngtree.cpp copyright (c) 2000 G.Juyn * */ +/* * version : 1.0.0 * */ +/* * * */ +/* * purpose : main project file * */ +/* * * */ +/* * author : G.Juyn * */ +/* * web : http://www.3-t.com * */ +/* * email : mailto:info@3-t.com * */ +/* * * */ +/* * comment : mngtree simply dumps the chunk-structure of the supplied * */ +/* * first parameter to stdout (should be a xNG-file) * */ +/* * * */ +/* * changes : 0.5.3 - 06/26/2000 - G.Juyn * */ +/* * - changed userdata variable to mng_ptr * */ +/* * 0.5.3 - 06/28/2000 - G.Juyn * */ +/* * - changed memory allocation size parameters to mng_size_t * */ +/* * * */ +/* ************************************************************************** */ + +#pragma hdrstop +#include +#include +#include +#include + +#include "libmng.h" + +/* ************************************************************************** */ + +USERES("mngtree.res"); +USELIB("..\win32dll\libmng.lib"); +//--------------------------------------------------------------------------- + +/* ************************************************************************** */ + +typedef struct user_struct { + + FILE *hFile; /* file handle */ + int iIndent; /* for nice indented formatting */ + + } userdata; + +typedef userdata * userdatap; + +/* ************************************************************************** */ + +#define MY_DECL __stdcall /* get the right callback convention */ + +/* ************************************************************************** */ + +mng_ptr MY_DECL myalloc (mng_size_t iSize) +{ /* duh! */ + return (mng_ptr)calloc (1, (size_t)iSize); +} + +/* ************************************************************************** */ + +#pragma argsused +void MY_DECL myfree (mng_ptr pPtr, mng_size_t iSize) +{ + free (pPtr); /* duh! */ + return; +} + +/* ************************************************************************** */ + +#pragma argsused +mng_bool MY_DECL myopenstream (mng_handle hMNG) +{ + return MNG_TRUE; /* already opened in main function */ +} + +/* ************************************************************************** */ + +#pragma argsused +mng_bool MY_DECL myclosestream (mng_handle hMNG) +{ + return MNG_TRUE; /* gets closed in main function */ +} + +/* ************************************************************************** */ + +mng_bool MY_DECL myreaddata (mng_handle hMNG, + mng_ptr pBuf, + mng_uint32 iSize, + mng_uint32 *iRead) +{ /* get to my file handle */ + userdatap pMydata = (userdatap)mng_get_userdata (hMNG); + /* read it */ + *iRead = fread (pBuf, 1, iSize, pMydata->hFile); + /* iRead will indicate EOF */ + return MNG_TRUE; +} + +/* ************************************************************************** */ + +#pragma argsused +mng_bool MY_DECL myiterchunk (mng_handle hMNG, + mng_handle hChunk, + mng_chunkid iChunktype, + mng_uint32 iChunkseq) +{ /* get to my file handle */ + userdatap pMydata = (userdatap)mng_get_userdata (hMNG); + char aCh[4]; + char zIndent[80]; + int iX; + /* decode the chunkname */ + aCh[0] = (char)((iChunktype >> 24) & 0xFF); + aCh[1] = (char)((iChunktype >> 16) & 0xFF); + aCh[2] = (char)((iChunktype >> 8) & 0xFF); + aCh[3] = (char)((iChunktype ) & 0xFF); + /* indent less ? */ + if ( (iChunktype == MNG_UINT_MEND) || (iChunktype == MNG_UINT_IEND) || + (iChunktype == MNG_UINT_ENDL) ) + pMydata->iIndent -= 2; + /* this looks ugly; but I haven't + figured out how to do it prettier */ + for (iX = 0; iX < pMydata->iIndent; iX++) + zIndent[iX] = ' '; + zIndent[pMydata->iIndent] = '\0'; + /* print a nicely indented line */ + printf ("%s%c%c%c%c\n", &zIndent, aCh[0], aCh[1], aCh[2], aCh[3]); + /* indent more ? */ + if ( (iChunktype == MNG_UINT_MHDR) || (iChunktype == MNG_UINT_IHDR) || + (iChunktype == MNG_UINT_JHDR) || (iChunktype == MNG_UINT_DHDR) || + (iChunktype == MNG_UINT_BASI) || (iChunktype == MNG_UINT_LOOP) ) + pMydata->iIndent += 2; + + return MNG_TRUE; /* keep'm coming... */ +} + +/* ************************************************************************** */ + +int dumptree (char * zFilename) +{ + userdatap pMydata; + mng_handle hMNG; + mng_retcode iRC; + /* get a data buffer */ + pMydata = (userdatap)calloc (1, sizeof (userdata)); + + if (pMydata == NULL) /* oke ? */ + { + fprintf (stderr, "Cannot allocate a data buffer.\n"); + return 1; + } + /* can we open the file ? */ + if ((pMydata->hFile = fopen (zFilename, "rb")) == NULL) + { /* error out if we can't */ + fprintf (stderr, "Cannot open input file %s.\n", zFilename); + return 1; + } + /* let's initialize the library */ + hMNG = mng_initialize ((mng_ptr)pMydata, myalloc, myfree, MNG_NULL); + + if (!hMNG) /* did that work out ? */ + { + fprintf (stderr, "Cannot initialize libmng.\n"); + iRC = 1; + } + else + { /* setup callbacks */ + if ( ((iRC = mng_setcb_openstream (hMNG, myopenstream )) != 0) || + ((iRC = mng_setcb_closestream (hMNG, myclosestream)) != 0) || + ((iRC = mng_setcb_readdata (hMNG, myreaddata )) != 0) ) + fprintf (stderr, "Cannot set callbacks for libmng.\n"); + else + { /* reaad the file into memory */ + if ((iRC = mng_read (hMNG)) != 0) + fprintf (stderr, "Cannot read the file.\n"); + else + { + pMydata->iIndent = 2; /* start of the indenting at a nice level */ + + printf ("Starting dump of %s.\n\n", zFilename); + /* run through the chunk list */ + if ((iRC = mng_iterate_chunks (hMNG, 0, myiterchunk)) != 0) + fprintf (stderr, "Cannot iterate the chunks.\n"); + + printf ("\nDone.\n"); + } + } + + mng_cleanup (&hMNG); /* cleanup the library */ + } + + fclose (pMydata->hFile); /* cleanup */ + free (pMydata); + + return iRC; +} + +/* ************************************************************************** */ + +#pragma argsused +int main(int argc, char *argv[]) +{ + if (argc > 1) /* need that first parameter ! */ + return dumptree (argv[1]); + else + fprintf (stdout, "\nUsage: mngtree \n\n"); + + return 0; +} + +/* ************************************************************************** */ diff --git a/Engine/lib/lmng/bcb/mngview/MNGI.ICO b/Engine/lib/lmng/bcb/mngview/MNGI.ICO new file mode 100644 index 000000000..1556e5899 Binary files /dev/null and b/Engine/lib/lmng/bcb/mngview/MNGI.ICO differ diff --git a/Engine/lib/lmng/bcb/mngview/Main.cpp b/Engine/lib/lmng/bcb/mngview/Main.cpp new file mode 100644 index 000000000..2bc3b5409 --- /dev/null +++ b/Engine/lib/lmng/bcb/mngview/Main.cpp @@ -0,0 +1,573 @@ +//--------------------------------------------------------------------------- +#include +#pragma hdrstop +#include // for : malloc & free +#include "Main.h" + +/****************************************************************************} +{* For conditions of distribution and use, *} +{* see copyright notice in libmng.pas *} +{****************************************************************************} +{* *} +{* project : libmng *} +{* file : main.pas copyright (c) 2000 G.Juyn *} +{* version : 1.0.1 *} +{* *} +{* purpose : Main form for mngview application *} +{* *} +{* author : G.Juyn *} +{* web : http://www.3-t.com *} +{* email : mailto:info@3-t.com *} +{* *} +{* comment : this is the heart of the mngview applciation *} +{* *} +{* changes : This project is a converted version of "mngview" - AP *} +{* - AP - 15/9/2000 - revisions ... *} +{* - made the callbacks calling convention explicit *} +(* - Moved the defines from "project options" to "main.h" *} +{* - Added Readme.txt to the project - Please READ IT ! *} +(* *} +{* 0.5.1 - 05/02/2000 - G.Juyn *} +{* - added this version block *} +{* - made the initialization part more robust *} +{* eg. program aborts on initialization errors *} +{* - B002(105797) - added check for existence of default sRGB *} +{* profile (now included in distribution) *} +{* - added mng_cleanup to program exit *} +{* 0.5.1 - 05/08/2000 - G.Juyn *} +{* - changed to stdcall convention *} +{* 0.5.1 - 05/11/2000 - G.Juyn *} +{* - changed callback function declarations *} +{* *} +{* 0.5.3 - 06/16/2000 - G.Juyn *} +{* - removed processmessages call from refresh callback *} +{* 0.5.3 - 06/17/2000 - G.Juyn *} +{* - switched "storechunks" off *} +{* 0.5.3 - 06/26/2000 - G.Juyn *} +{* - changed definition of userdata to mng_ptr *} +{* 0.5.3 - 06/28/2000 - G.Juyn *} +{* - changed the default icon to something more appropriate *} +{* - changed definition of memory alloc size to mng_size_t *} +{* 0.5.3 - 06/29/2000 - G.Juyn *} +{* - changed order of refresh parameters *} +{* *} +{* 0.9.0 - 06/30/2000 - G.Juyn *} +{* - changed refresh parameters to 'x,y,width,height' *} +{* *} +{* 0.9.1 - 07/08/2000 - G.Juyn *} +{* - fixed to use returncode constants *} +{* - changed to accomodate MNG_NEEDTIMERWAIT returncode *} +{* 0.9.1 - 07/10/2000 - G.Juyn *} +{* - changed to use suspension-mode *} +{* *} +{* 1.0.1 - 05/02/2000 - G.Juyn *} +{* - removed loading of default sRGB profile (auto in libmng) *} +{* *} +{****************************************************************************/ + +//--------------------------------------------------------------------------- +#pragma package(smart_init) +#pragma resource "*.dfm" +TMainForm *MainForm; + +# define _OR_ | +# define _AND_ & +# define _DIV_ / +# define _NOT_ ! +# define _NIL_ 0 +# define _SHR_ >> +# define _SHL_ << + +// Prototypes for static functions - the LibMng Callbacks. +static mng_ptr __stdcall Memalloc( mng_uint32 iLen ); +static void __stdcall Memfree( mng_ptr iPtr, mng_size_t iLen ); +static mng_bool __stdcall Openstream( mng_handle hHandle ); +static mng_bool __stdcall Closestream( mng_handle hHandle ); +static mng_bool __stdcall Readdata ( mng_handle hHandle, mng_ptr pBuf, + mng_uint32 iBuflen, mng_uint32 *pRead ); +static mng_bool __stdcall ProcessHeader ( mng_handle hHandle, + mng_uint32 iWidth, mng_uint32 iHeight ); +static mng_ptr __stdcall GetCanvasLine ( mng_handle hHandle, + mng_uint32 iLinenr ); +static mng_ptr __stdcall GetAlphaLine( mng_handle hHandle, mng_uint32 iLinenr ); +static mng_bool __stdcall ImageRefresh ( mng_handle hHandle, + mng_uint32 iX, mng_uint32 iY, mng_uint32 iWidth, mng_uint32 iHeight ); +static mng_uint32 __stdcall GetTickCount( mng_handle hHandle ); +static mng_bool __stdcall SetTimer( mng_handle hHandle, mng_uint32 iMsecs ); + +//--------------------------------------------------------------------------- +__fastcall TMainForm::TMainForm(TComponent* Owner) + : TForm(Owner) +{ +} +//--------------------------------------------------------------------------- +static mng_ptr __stdcall Memalloc( mng_uint32 iLen ) +{ +mng_ptr pResult = + + malloc( iLen ); /* get memory from the heap */ + + if( pResult ) /* Added - condition */ + memset( pResult, 0, iLen ); + + return pResult; +} +//--------------------------------------------------------------------------- +static void __stdcall Memfree( mng_ptr iPtr, mng_size_t iLen ) +{ + free( iPtr ); /* free the memory */ + (void)iLen; // Kill compiler warning +} +//--------------------------------------------------------------------------- +static mng_bool __stdcall Openstream( mng_handle hHandle ) +{ +TMainForm *OHForm; + + /* get a fix on our form */ + OHForm = (TMainForm *)mng_get_userdata( hHandle ); + + if( OHForm->OFFile != _NIL_ ) /* free previous stream (if any) */ + OHForm->OFFile->Free(); + + /* open a new stream */ + OHForm->OFFile = new TFileStream( + OHForm->SFFileName, fmOpenRead _OR_ fmShareDenyWrite); + + OHForm->ProgressBar1->Position = 0; /* Added */ + OHForm->ProgressBar1->Min =0; /* Added */ + OHForm->ProgressBar1->Max = OHForm->OFFile->Size; /* Added */ + + return MNG_TRUE; +} +//--------------------------------------------------------------------------- +static mng_bool __stdcall Closestream( mng_handle hHandle ) +{ +TMainForm *OHForm; + + /* get a fix on our form */ + OHForm = (TMainForm *)mng_get_userdata( hHandle ); + + OHForm->OFFile->Free(); /* cleanup the stream */ + OHForm->OFFile = 0; /* don't use it again ! */ + + OHForm->ProgressBar1->Position = 0; /* Added */ + + return MNG_TRUE; +} +//--------------------------------------------------------------------------- +static mng_bool __stdcall Readdata ( mng_handle hHandle, mng_ptr pBuf, + mng_uint32 iBuflen, mng_uint32 *pRead ) +{ +TMainForm *OHForm; +unsigned int IHTicks; +unsigned int IHByte1; +unsigned int IHByte2; +unsigned int IHBytesPerSec ; + + /* get a fix on our form */ + OHForm = (TMainForm *)mng_get_userdata( hHandle ); + + /* are we at EOF ? */ + if( OHForm->OFFile->Position >= OHForm->OFFile->Size ) + { + *pRead = 0; /* indicate so */ + } + else + { + IHBytesPerSec = OHForm->IFBytesPerSec; /* fake a slow connection */ + + if( IHBytesPerSec > 0 ) + { + IHTicks = (unsigned int)GetTickCount(); + IHByte1 = (IHTicks - OHForm->IFTicks) * IHBytesPerSec; + IHByte2 = (OHForm->IFBytes + iBuflen) * 1000; + + if( IHByte2 > IHByte1 ) /* Added - condition */ + if( ((IHByte2 - IHByte1) _DIV_ IHBytesPerSec) > 10 ) + { + Sleep( (DWORD)((IHByte2 - IHByte1) _DIV_ IHBytesPerSec) ); + } + }; + + /* read the requested data */ + *pRead = OHForm->OFFile->Read( pBuf, iBuflen); + OHForm->IFBytes = OHForm->IFBytes + *pRead; + + OHForm->ProgressBar1->Position = (int)OHForm->IFBytes; /* Added */ + } // end else; + + return MNG_TRUE; +} +//--------------------------------------------------------------------------- +static mng_bool __stdcall ProcessHeader ( mng_handle hHandle, + mng_uint32 iWidth, mng_uint32 iHeight ) +{ +TMainForm *OHForm; + + /* get a fix on our form */ + OHForm = (TMainForm *)mng_get_userdata( hHandle ); + + /* Added */ + OHForm->Caption = ExtractFileName( OHForm->SFFileName ) + + " [" + + String( iWidth ) + + "x" + + String( iHeight ) + + "]"; + + OHForm->OFBitmap->Width = iWidth; /* store the new dimensions */ + OHForm->OFBitmap->Height = iHeight; + OHForm->OFImage->Left = 0; /* adjust the visible component */ + OHForm->OFImage->Top = 0; + OHForm->OFImage->Width = iWidth; + OHForm->OFImage->Height = iHeight; + + OHForm->FormResize (OHForm); /* force re-centering the image*/ + /* clear the canvas & draw an outline */ + OHForm->OFBitmap->Canvas->Brush->Color = clGray; + OHForm->OFBitmap->Canvas->Brush->Style = bsSolid; + OHForm->OFBitmap->Canvas->FillRect( OHForm->OFBitmap->Canvas->ClipRect ); + OHForm->OFBitmap->Canvas->Brush->Color = clRed; + OHForm->OFBitmap->Canvas->Brush->Style = bsSolid; + OHForm->OFBitmap->Canvas->Pen->Color = clRed; + OHForm->OFBitmap->Canvas->Pen->Style = psSolid; + OHForm->OFBitmap->Canvas->FrameRect( OHForm->OFBitmap->Canvas->ClipRect); + + /* make sure it gets out there */ + OHForm->OFImage->Picture->Assign( OHForm->OFBitmap ); + + /* tell the library we want funny windows-bgr*/ + if( mng_set_canvasstyle( hHandle, MNG_CANVAS_BGR8 ) ) + OHForm->MNGerror( "libmng reported an error setting the canvas style" ); + + return MNG_TRUE; +} +//--------------------------------------------------------------------------- +static mng_ptr __stdcall GetCanvasLine ( mng_handle hHandle, + mng_uint32 iLinenr ) +{ +TMainForm *OHForm; + + /* get a fix on our form */ + OHForm = (TMainForm *)mng_get_userdata( hHandle ); + + /* easy with these bitmap objects ! */ + return OHForm->OFBitmap->ScanLine[ iLinenr ]; +} +//--------------------------------------------------------------------------- +static mng_bool __stdcall ImageRefresh ( mng_handle hHandle, + mng_uint32 iX, mng_uint32 iY, mng_uint32 iWidth, mng_uint32 iHeight ) +{ +TMainForm *OHForm; + + /* get a fix on our form */ + OHForm = (TMainForm *)mng_get_userdata( hHandle ); + + /* force redraw */ + OHForm->OFImage->Picture->Assign( OHForm->OFBitmap ); + + return MNG_TRUE; +} +//--------------------------------------------------------------------------- +static mng_uint32 __stdcall GetTickCount( mng_handle hHandle ) +{ + return GetTickCount(); /* windows knows that */ +} +//--------------------------------------------------------------------------- +static mng_bool __stdcall SetTimer( mng_handle hHandle, mng_uint32 iMsecs ) +{ +TMainForm *OHForm; + + /* get a fix on our form */ + OHForm = (TMainForm *)mng_get_userdata( hHandle ); + OHForm->OFTimer->Interval = iMsecs; /* and set the timer */ + OHForm->OFTimer->Enabled = true; + + return MNG_TRUE; +} +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +void __fastcall TMainForm::FormCreate(TObject *Sender) +{ +String SHProfileName; +mng_uint16 IHRed, IHGreen, IHBlue; /* word */ + + OFBitmap = new Graphics::TBitmap(); /* initialize */ + IFBytesPerSec = 10000000; + OFFile = 0; + + OFOpenDialog->InitialDir = ""; + OFBitmap->HandleType = bmDIB; /* make it a 24-bit DIB */ + OFBitmap->PixelFormat = pf24bit; + + /* now initialize the library */ + IFHandle = mng_initialize( mng_ptr(this), Memalloc, Memfree, _NIL_ ); + + if( IFHandle == _NIL_ ) + { + MNGerror ("libmng initializiation error"\ + "\n"\ + "Program aborted" + ); + PostMessage( Handle, WM_CLOSE, 0, 0); + return; // was Exit + }; + + /* no need to store chunk-info ! */ + mng_set_storechunks( IFHandle, MNG_FALSE ); + + /* use suspension-buffer */ + mng_set_suspensionmode( IFHandle, MNG_TRUE ); + + /* set all the callbacks */ + if( + (mng_setcb_openstream (IFHandle, Openstream ) != MNG_NOERROR) _OR_ + (mng_setcb_closestream (IFHandle, Closestream ) != MNG_NOERROR) _OR_ + (mng_setcb_readdata (IFHandle, Readdata ) != MNG_NOERROR) _OR_ + (mng_setcb_processheader(IFHandle, ProcessHeader) != MNG_NOERROR) _OR_ + (mng_setcb_getcanvasline(IFHandle, GetCanvasLine) != MNG_NOERROR) _OR_ + (mng_setcb_refresh (IFHandle, ImageRefresh ) != MNG_NOERROR) _OR_ + (mng_setcb_gettickcount (IFHandle, GetTickCount ) != MNG_NOERROR) _OR_ + (mng_setcb_settimer (IFHandle, SetTimer ) != MNG_NOERROR) + ) + { + MNGerror ("libmng reported an error setting a callback function!"\ + "\n"\ + "Program aborted" + ); + PostMessage( Handle, WM_CLOSE, 0, 0 ); + return; // was Exit + }; + + /* supply our own bg-color */ + IHRed = (mng_uint16)((Color ) _AND_ 0xFF); + IHGreen = (mng_uint16)((Color _SHR_ 8) _AND_ 0xFF); + IHBlue = (mng_uint16)((Color _SHR_ 16) _AND_ 0xFF); + + IHRed = (mng_uint16)((IHRed _SHL_ 8) + IHRed); + IHGreen = (mng_uint16)((IHGreen _SHL_ 8) + IHGreen); + IHBlue = (mng_uint16)((IHBlue _SHL_ 8) + IHBlue); + + if( mng_set_bgcolor (IFHandle, IHRed, IHGreen, IHBlue) != MNG_NOERROR ) + MNGerror( "libmng reported an error setting the background color!"); +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::FormCloseQuery(TObject *Sender, + bool &CanClose) +{ + BFCancelled = true; + + /* if we're still animating then stop it */ + if( OFTimer->Enabled ) + { + if( mng_display_freeze (IFHandle) != MNG_NOERROR ) + MNGerror ("libmng reported an error during display_freeze!" ); + } + + OFTimer->Enabled = false; + + mng_cleanup( &IFHandle ); + +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::FormShow(TObject *Sender) +{ + FormResize( this ); +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::FormResize(TObject *Sender) +{ + /* center the image in the window */ + + if( ClientWidth < OFImage->Width ) + OFImage->Left = 0; + else + OFImage->Left = (ClientWidth - OFImage->Width ) _DIV_ 2; + + if( ClientHeight < OFImage->Height ) + OFImage->Top = 0; + else + OFImage->Top = (ClientHeight - OFImage->Height) _DIV_ 2; + + ProgressBar1->Width = Panel1->Width - 8; /* Added */ + +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::FormKeyDown(TObject *Sender, WORD &Key, + TShiftState Shift) +{ + /* pressing will freeze an animation */ + if( Key == VK_ESCAPE ) + { + if( OFTimer->Enabled ) + { + if( mng_display_freeze( IFHandle) != MNG_NOERROR ) + MNGerror( "libmng reported an error during display_freeze!" ); + } + + OFTimer->Enabled = false; /* don't let that timer go off then ! */ + BFCancelled = true; + } + +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::OFTimerTimer(TObject *Sender) +{ +mng_retcode IHRslt; + + OFTimer->Enabled = false; /* only once ! */ + + if( _NOT_ BFCancelled ) + { + /* and inform the library */ + IHRslt = mng_display_resume( IFHandle ); + + if( (IHRslt != MNG_NOERROR) _AND_ (IHRslt != MNG_NEEDTIMERWAIT) ) + MNGerror( "libmng reported an error during display_resume!" ); + + }; + +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::OFMenuFileOpenClick(TObject *Sender) +{ +mng_retcode IHRslt; + + OFOpenDialog->InitialDir = ""; +OFOpenDialog->InitialDir = GetCurrentDir(); //@@ + OFOpenDialog->FileName = SFFileName; + + if( OFOpenDialog->Execute() ) /* get the filename */ + { + if( OFTimer->Enabled ) /* if the lib was active; stop it */ + { + OFTimer->Enabled = false; + + Application->ProcessMessages(); /* process any timer requests (for safety) */ + /* now freeze the animation */ + if( mng_display_freeze( IFHandle ) != MNG_NOERROR ) + MNGerror( "libmng reported an error during display_freeze!" ); + }; + + /* save interesting fields */ + SFFileName = OFOpenDialog->FileName; + IFTicks = GetTickCount(); + IFBytes = 0; + BFCancelled = false; + + /* always reset (just in case) */ + if( mng_reset( IFHandle ) != MNG_NOERROR ) + { + MNGerror( "libmng reported an error during reset!" ); + } + else + { + /* and let the lib do it's job ! */ + IHRslt = mng_readdisplay (IFHandle); + + if( (IHRslt != MNG_NOERROR) _AND_ (IHRslt != MNG_NEEDTIMERWAIT) ) + MNGerror( "libmng reported an error reading the input file!" ); + + }; + }; +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::OFMenuFileProfileClick(TObject *Sender) +{ +char SHProfileDir[ MAX_PATH ]; + + GetSystemDirectory( SHProfileDir, MAX_PATH ); + strcat( SHProfileDir, "\\Color" ); + + OFOpenDialogProfile->InitialDir = String( SHProfileDir ); + + if( OFOpenDialogProfile->Execute() ) + { + if( mng_set_outputprofile( IFHandle, OFOpenDialogProfile->FileName.c_str()) != 0 ) + MNGerror( "libmng reported an error setting the output-profile!" ); + } +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::OFMenuFileExitClick(TObject *Sender) +{ + if( mng_cleanup( &IFHandle ) != MNG_NOERROR ) + MNGerror( "libmng cleanup error" ); + + Close(); + +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::OFMenuOptionsModemSpeedClick(TObject *Sender) +{ + + OFMenuOptionsModem28k8->Checked = false; + OFMenuOptionsModem33k6->Checked = false; + OFMenuOptionsModem56k->Checked = false; + OFMenuOptionsModemISDN64->Checked = false; + OFMenuOptionsModemISDN128->Checked = false; + OFMenuOptionsModemCable512->Checked = false; + OFMenuOptionsModemUnlimited->Checked = false; + + if( IFBytesPerSec == (unsigned int)OFMenuOptionsModem28k8->Tag _DIV_ 10 ) + OFMenuOptionsModem28k8->Checked = true; + else + if( IFBytesPerSec == (unsigned int)OFMenuOptionsModem33k6->Tag _DIV_ 10 ) + OFMenuOptionsModem33k6->Checked = true; + else + if( IFBytesPerSec == (unsigned int)OFMenuOptionsModem56k->Tag _DIV_ 10 ) + OFMenuOptionsModem56k->Checked = true; + else + if( IFBytesPerSec == (unsigned int)OFMenuOptionsModemISDN64->Tag _DIV_ 10 ) + OFMenuOptionsModemISDN64->Checked = true; + else + if( IFBytesPerSec == (unsigned int)OFMenuOptionsModemISDN128->Tag _DIV_ 10 ) + OFMenuOptionsModemISDN128->Checked = true; + else + /* Added - changedit was the line below ! */ +// if( IFBytesPerSec == (unsigned int)OFMenuOptionsModemUnlimited->Tag _DIV_ 10 ) + if( IFBytesPerSec == (unsigned int)OFMenuOptionsModemCable512->Tag _DIV_ 10 ) + OFMenuOptionsModemCable512->Checked = true; + else + OFMenuOptionsModemUnlimited->Checked = true; + +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::OFMenuOptionsModemXClick(TObject *Sender) +{ + IFBytesPerSec = ((TMenuItem*)Sender)->Tag _DIV_ 10; +} +//--------------------------------------------------------------------------- +void TMainForm::MNGerror( String SHMsg ) +{ +/* get extended info */ +mng_uint32 iErrorcode; +mng_uint8 iSeverity; +mng_chunkid iChunkname; +mng_uint32 iChunkseq; +mng_int32 iExtra1; +mng_int32 iExtra2; +mng_pchar zErrortext; +char szFormatStr[ 256 ]; + + iErrorcode = mng_getlasterror (IFHandle, &iSeverity, + &iChunkname, &iChunkseq, &iExtra1, &iExtra2, + (mng_pchar*)&zErrortext); + + wsprintf( szFormatStr, + "Error = %d; Severity = %d; Chunknr = %d; Extra1 = %d", + (int)iErrorcode, (int)iSeverity, (int)iChunkseq, (int)iExtra1 + ); + + MessageDlg( SHMsg + + "\n\n" + + String(zErrortext) + + "\n\n" + + szFormatStr, /* see wsprintf above */ + mtError, + TMsgDlgButtons() << mbOK, + 0 + ); + +} +//--------------------------------------------------------------------------- + diff --git a/Engine/lib/lmng/bcb/mngview/Main.dfm b/Engine/lib/lmng/bcb/mngview/Main.dfm new file mode 100644 index 000000000..ae72e95c1 Binary files /dev/null and b/Engine/lib/lmng/bcb/mngview/Main.dfm differ diff --git a/Engine/lib/lmng/bcb/mngview/Main.h b/Engine/lib/lmng/bcb/mngview/Main.h new file mode 100644 index 000000000..94dac0191 --- /dev/null +++ b/Engine/lib/lmng/bcb/mngview/Main.h @@ -0,0 +1,84 @@ +//--------------------------------------------------------------------------- +#ifndef MainH +#define MainH +//--------------------------------------------------------------------------- +#include +#include +#include +#include +#include +#include +#include +#include +//--------------------------------------------------------------------------- +// These MUST be defined before we include "Libmng.h +# define MNG_SUPPORT_READ +# define MNG_ACCESS_CHUNKS +# define MNG_STORE_CHUNKS +# define MNG_NO_CMS +# define MNG_USE_DLL +# define MNG_SUPPORT_DISPLAY +# define MNG_SKIP_ZLIB // we don't need the zlib definitions here +# define MNG_SKIP_IJG6B // we don't need the IJG definitions here +#include "libmng.h" +//--------------------------------------------------------------------------- +class TMainForm : public TForm +{ +__published: // IDE-managed Components + TMainMenu *OFMainMenu; + TMenuItem *OFMenuFile; + TMenuItem *OFMenuFileOpen; + TMenuItem *OFMenuFileProfile; + TMenuItem *OFMenuFileN1; + TMenuItem *OFMenuFileExit; + TMenuItem *OFMenuOptions; + TMenuItem *OFMenuOptionsModemSpeed; + TMenuItem *OFMenuOptionsModem28k8; + TMenuItem *OFMenuOptionsModem33k6; + TMenuItem *OFMenuOptionsModem56k; + TMenuItem *OFMenuOptionsModemISDN64; + TMenuItem *OFMenuOptionsModemISDN128; + TMenuItem *OFMenuOptionsModemCable512; + TMenuItem *OFMenuOptionsModemUnlimited; + TOpenDialog *OFOpenDialog; + TTimer *OFTimer; + TOpenDialog *OFOpenDialogProfile; + TImage *OFImage; + TPanel *Panel1; + TProgressBar *ProgressBar1; + void __fastcall FormCreate(TObject *Sender); + void __fastcall FormCloseQuery(TObject *Sender, bool &CanClose); + void __fastcall FormShow(TObject *Sender); + void __fastcall FormResize(TObject *Sender); + void __fastcall FormKeyDown(TObject *Sender, WORD &Key, + TShiftState Shift); + void __fastcall OFTimerTimer(TObject *Sender); + void __fastcall OFMenuFileOpenClick(TObject *Sender); + void __fastcall OFMenuFileProfileClick(TObject *Sender); + void __fastcall OFMenuFileExitClick(TObject *Sender); + void __fastcall OFMenuOptionsModemSpeedClick(TObject *Sender); + void __fastcall OFMenuOptionsModemXClick(TObject *Sender); +private: // User declarations +public : + // Data - was private in the pascal version + String SFFileName; /* filename of the input stream */ + TFileStream *OFFile; /* input stream */ + mng_handle IFHandle; /* the libray handle */ + Graphics::TBitmap *OFBitmap; /* drawing canvas */ +# ifdef TEST_RGB8_A8 + void *OFAlpha; +# endif + bool BFCancelled; /* or app-exit */ + unsigned int IFTicks; /* used to fake slow connections */ + unsigned int IFBytes; + unsigned int IFBytesPerSec; + // Methods + void MNGerror( String SHMsg ); +public: // User declarations + __fastcall TMainForm(TComponent* Owner); +}; +//--------------------------------------------------------------------------- +extern PACKAGE TMainForm *MainForm; +//--------------------------------------------------------------------------- +#endif + diff --git a/Engine/lib/lmng/bcb/mngview/MngView.bpr b/Engine/lib/lmng/bcb/mngview/MngView.bpr new file mode 100644 index 000000000..f80283bb1 --- /dev/null +++ b/Engine/lib/lmng/bcb/mngview/MngView.bpr @@ -0,0 +1,187 @@ +# --------------------------------------------------------------------------- +!if !$d(BCB) +BCB = $(MAKEDIR)\.. +!endif + +# --------------------------------------------------------------------------- +# IDE SECTION +# --------------------------------------------------------------------------- +# The following section of the project makefile is managed by the BCB IDE. +# It is recommended to use the IDE to change any of the values in this +# section. +# --------------------------------------------------------------------------- + +VERSION = BCB.03 +# --------------------------------------------------------------------------- +PROJECT = MngView.exe +OBJFILES = MngView.obj Main.obj +RESFILES = MngView.res +DEFFILE = +RESDEPEN = $(RESFILES) Main.dfm +LIBFILES = ..\win32dll\libmng.lib +LIBRARIES = VCL35.lib +SPARELIBS = VCL35.lib +PACKAGES = VCLX35.bpi VCL35.bpi VCLDB35.bpi VCLDBX35.bpi bcbsmp35.bpi dclocx35.bpi \ + QRPT35.bpi +# --------------------------------------------------------------------------- +PATHCPP = .; +PATHASM = .; +PATHPAS = .; +PATHRC = .; +DEBUGLIBPATH = $(BCB)\lib\debug +RELEASELIBPATH = $(BCB)\lib\release +# --------------------------------------------------------------------------- +CFLAG1 = -Od -w -Ve -r- -k -y -v -vi- -c -b- -w-par -w-inl -Vx -tW +CFLAG2 = -I$(BCB)\include;$(BCB)\include\vcl;..\..;..\..\..\zlib;..\..\..\jpgsrc6b +CFLAG3 = -Tkh30000 -6 +PFLAGS = -U$(BCB)\lib\obj;$(BCB)\lib;$(RELEASELIBPATH) \ + -I$(BCB)\include;$(BCB)\include\vcl;..\..;..\..\..\zlib;..\..\..\jpgsrc6b -$Y \ + -$W -$O- -v -JPHN -M +RFLAGS = -i$(BCB)\include;$(BCB)\include\vcl;..\..;..\..\..\zlib;..\..\..\jpgsrc6b +AFLAGS = /i$(BCB)\include /i$(BCB)\include\vcl /i..\.. /i..\..\..\zlib \ + /i..\..\..\jpgsrc6b /mx /w2 /zd /dMNG_USE_DLL +LFLAGS = -L$(BCB)\lib\obj;$(BCB)\lib;$(RELEASELIBPATH) -aa -Tpe -x -Gn +IFLAGS = +# --------------------------------------------------------------------------- +ALLOBJ = c0w32.obj sysinit.obj $(OBJFILES) +ALLRES = $(RESFILES) +ALLLIB = $(LIBFILES) $(LIBRARIES) import32.lib cp32mt.lib +# --------------------------------------------------------------------------- +!ifdef IDEOPTIONS + +[Version Info] +IncludeVerInfo=0 +AutoIncBuild=0 +MajorVer=1 +MinorVer=0 +Release=0 +Build=0 +Debug=0 +PreRelease=0 +Special=0 +Private=0 +DLL=0 +Locale=2057 +CodePage=1252 + +[Version Info Keys] +CompanyName= +FileDescription= +FileVersion=1.0.0.0 +InternalName= +LegalCopyright= +LegalTrademarks= +OriginalFilename= +ProductName= +ProductVersion=1.0.0.0 +Comments= + +[HistoryLists\hlIncludePath] +Count=8 +Item0=$(BCB)\include;$(BCB)\include\vcl;..\..;..\..\..\zlib;..\..\..\jpgsrc6b +Item1=$(BCB)\include;$(BCB)\include\vcl;..\..;..\..\..\zlib;..\..\..\jpgsrc6b;..\win32dll +Item2=$(BCB)\include;$(BCB)\include\vcl;..\..;..\..\..\zlib;..\..\..\ijgsrc6b +Item3=$(BCB)\include;$(BCB)\include\vcl;..\..;..\..\..\zlib;..\..\..\libjpeg +Item4=$(BCB)\include;$(BCB)\include\vcl;..\..;..\..\zlib +Item5=$(BCB)\include;$(BCB)\include\vcl;..\.. +Item6=$(BCB)\include;$(BCB)\include\vcl +Item7=..\..\delphi;$(BCB)\include;$(BCB)\include\vcl + +[HistoryLists\hlLibraryPath] +Count=2 +Item0=$(BCB)\lib\obj;$(BCB)\lib +Item1=..\..\delphi;$(BCB)\lib\obj;$(BCB)\lib + +[HistoryLists\hlDebugSourcePath] +Count=1 +Item0=$(BCB)\source\vcl + +[HistoryLists\hlConditionals] +Count=3 +Item0=MNG_USE_DLL +Item1=_RTLDLL +Item2=_RTLDLL;USEPACKAGES + +[Debugging] +DebugSourceDirs=$(BCB)\source\vcl + +[Parameters] +RunParams= +HostApplication= + +!endif + +# --------------------------------------------------------------------------- +# MAKE SECTION +# --------------------------------------------------------------------------- +# This section of the project file is not used by the BCB IDE. It is for +# the benefit of building from the command-line using the MAKE utility. +# --------------------------------------------------------------------------- + +.autodepend +# --------------------------------------------------------------------------- +!if !$d(BCC32) +BCC32 = bcc32 +!endif + +!if !$d(DCC32) +DCC32 = dcc32 +!endif + +!if !$d(TASM32) +TASM32 = tasm32 +!endif + +!if !$d(LINKER) +LINKER = ilink32 +!endif + +!if !$d(BRCC32) +BRCC32 = brcc32 +!endif +# --------------------------------------------------------------------------- +!if $d(PATHCPP) +.PATH.CPP = $(PATHCPP) +.PATH.C = $(PATHCPP) +!endif + +!if $d(PATHPAS) +.PATH.PAS = $(PATHPAS) +!endif + +!if $d(PATHASM) +.PATH.ASM = $(PATHASM) +!endif + +!if $d(PATHRC) +.PATH.RC = $(PATHRC) +!endif +# --------------------------------------------------------------------------- +$(PROJECT): $(OBJFILES) $(RESDEPEN) $(DEFFILE) + $(BCB)\BIN\$(LINKER) @&&! + $(LFLAGS) + + $(ALLOBJ), + + $(PROJECT),, + + $(ALLLIB), + + $(DEFFILE), + + $(ALLRES) +! +# --------------------------------------------------------------------------- +.pas.hpp: + $(BCB)\BIN\$(DCC32) $(PFLAGS) {$< } + +.pas.obj: + $(BCB)\BIN\$(DCC32) $(PFLAGS) {$< } + +.cpp.obj: + $(BCB)\BIN\$(BCC32) $(CFLAG1) $(CFLAG2) $(CFLAG3) -n$(@D) {$< } + +.c.obj: + $(BCB)\BIN\$(BCC32) $(CFLAG1) $(CFLAG2) $(CFLAG3) -n$(@D) {$< } + +.asm.obj: + $(BCB)\BIN\$(TASM32) $(AFLAGS) $<, $@ + +.rc.res: + $(BCB)\BIN\$(BRCC32) $(RFLAGS) -fo$@ $< +# --------------------------------------------------------------------------- diff --git a/Engine/lib/lmng/bcb/mngview/MngView.cpp b/Engine/lib/lmng/bcb/mngview/MngView.cpp new file mode 100644 index 000000000..bc4624bc0 --- /dev/null +++ b/Engine/lib/lmng/bcb/mngview/MngView.cpp @@ -0,0 +1,23 @@ +//--------------------------------------------------------------------------- +#include +#pragma hdrstop +USERES("MngView.res"); +USEFORM("Main.cpp", MainForm); +USEFILE("README.txt"); +USELIB("..\win32dll\libmng.lib"); +//--------------------------------------------------------------------------- +WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) +{ + try + { + Application->Initialize(); + Application->CreateForm(__classid(TMainForm), &MainForm); + Application->Run(); + } + catch (Exception &exception) + { + Application->ShowException(&exception); + } + return 0; +} +//--------------------------------------------------------------------------- diff --git a/Engine/lib/lmng/bcb/mngview/MngView.res b/Engine/lib/lmng/bcb/mngview/MngView.res new file mode 100644 index 000000000..a596e0985 Binary files /dev/null and b/Engine/lib/lmng/bcb/mngview/MngView.res differ diff --git a/Engine/lib/lmng/bcb/mngview/README.TXT b/Engine/lib/lmng/bcb/mngview/README.TXT new file mode 100644 index 000000000..28a54321a --- /dev/null +++ b/Engine/lib/lmng/bcb/mngview/README.TXT @@ -0,0 +1,22 @@ + +Please note : + + If your project includes the header file "Rpcndr.h", then +you will then have to define "HAVE_BOOLEAN" before "libmng.h" +is included. Why ? ... + +"jmorecfg.h" has +#ifndef HAVE_BOOLEAN +typedef int boolean; +#endif + + and "Rpcndr.h" has +typedef unsigned char boolean + + This >>MAY<< affect other libraries used in the same project +that depend on the jpeg source - especially as "boolean" is +used in structures (possible alignment problems). For example +"Sam leffler's" LibTiff can be built with the Jpeg codec. + + Just a little something to be aware of !. AP - 15/9/2000. + diff --git a/Engine/lib/lmng/bcb/win32dll/libmng.bpr b/Engine/lib/lmng/bcb/win32dll/libmng.bpr new file mode 100644 index 000000000..c02c4a645 --- /dev/null +++ b/Engine/lib/lmng/bcb/win32dll/libmng.bpr @@ -0,0 +1,315 @@ +# --------------------------------------------------------------------------- +!if !$d(BCB) +BCB = $(MAKEDIR)\.. +!endif + +# --------------------------------------------------------------------------- +# IDE SECTION +# --------------------------------------------------------------------------- +# The following section of the project makefile is managed by the BCB IDE. +# It is recommended to use the IDE to change any of the values in this +# section. +# --------------------------------------------------------------------------- + +VERSION = BCB.03 +# --------------------------------------------------------------------------- +PROJECT = libmng.dll +OBJFILES = ..\..\..\obj\libmng.obj ..\..\..\obj\libmng_hlapi.obj \ + ..\..\..\obj\libmng_callback_xs.obj ..\..\..\obj\libmng_prop_xs.obj \ + ..\..\..\obj\libmng_chunk_xs.obj ..\..\..\obj\libmng_object_prc.obj \ + ..\..\..\obj\libmng_chunk_descr.obj ..\..\..\obj\libmng_chunk_prc.obj \ + ..\..\..\obj\libmng_chunk_io.obj ..\..\..\obj\libmng_read.obj \ + ..\..\..\obj\libmng_write.obj ..\..\..\obj\libmng_display.obj \ + ..\..\..\obj\libmng_dither.obj ..\..\..\obj\libmng_pixels.obj \ + ..\..\..\obj\libmng_filter.obj ..\..\..\obj\libmng_error.obj \ + ..\..\..\obj\libmng_trace.obj ..\..\..\obj\libmng_cms.obj \ + ..\..\..\obj\libmng_zlib.obj ..\..\..\obj\libmng_jpeg.obj \ + ..\..\..\obj\adler32.obj ..\..\..\obj\compress.obj ..\..\..\obj\crc32.obj \ + ..\..\..\obj\deflate.obj ..\..\..\obj\inffast.obj ..\..\..\obj\inflate.obj \ + ..\..\..\obj\inftrees.obj ..\..\..\obj\trees.obj ..\..\..\obj\uncompr.obj \ + ..\..\..\obj\zutil.obj ..\..\..\obj\jquant2.obj ..\..\..\obj\jcapistd.obj \ + ..\..\..\obj\jccoefct.obj ..\..\..\obj\jccolor.obj ..\..\..\obj\jcdctmgr.obj \ + ..\..\..\obj\jchuff.obj ..\..\..\obj\jcinit.obj ..\..\..\obj\jcmainct.obj \ + ..\..\..\obj\jcmarker.obj ..\..\..\obj\jcmaster.obj ..\..\..\obj\jcomapi.obj \ + ..\..\..\obj\jcparam.obj ..\..\..\obj\jcphuff.obj ..\..\..\obj\jcprepct.obj \ + ..\..\..\obj\jcsample.obj ..\..\..\obj\jctrans.obj ..\..\..\obj\jdapistd.obj \ + ..\..\..\obj\jdatadst.obj ..\..\..\obj\jdatasrc.obj ..\..\..\obj\jdcoefct.obj \ + ..\..\..\obj\jdcolor.obj ..\..\..\obj\jddctmgr.obj ..\..\..\obj\jdhuff.obj \ + ..\..\..\obj\jdinput.obj ..\..\..\obj\jdmainct.obj ..\..\..\obj\jdmarker.obj \ + ..\..\..\obj\jdmaster.obj ..\..\..\obj\jdmerge.obj ..\..\..\obj\jdphuff.obj \ + ..\..\..\obj\jdpostct.obj ..\..\..\obj\jdsample.obj ..\..\..\obj\jdtrans.obj \ + ..\..\..\obj\jerror.obj ..\..\..\obj\jfdctflt.obj ..\..\..\obj\jfdctfst.obj \ + ..\..\..\obj\jfdctint.obj ..\..\..\obj\jidctflt.obj ..\..\..\obj\jidctfst.obj \ + ..\..\..\obj\jidctint.obj ..\..\..\obj\jidctred.obj ..\..\..\obj\jmemmgr.obj \ + ..\..\..\obj\jmemnobs.obj ..\..\..\obj\jquant1.obj ..\..\..\obj\jcapimin.obj \ + ..\..\..\obj\jutils.obj ..\..\..\obj\jdapimin.obj +RESFILES = libmng.res +RESDEPEN = $(RESFILES) +LIBFILES = ..\..\..\lcms\Projects\Bcc-5.5-static\lcmsstat.lib +LIBRARIES = VCL35.lib +SPARELIBS = VCL35.lib +PACKAGES = vclx35.bpi VCL35.bpi vcldb35.bpi vcldbx35.bpi bcbsmp35.bpi dclocx35.bpi \ + Qrpt35.bpi +DEFFILE = +# --------------------------------------------------------------------------- +PATHCPP = .;..\..;..\..\..\zlib;..\..\..\jpgsrc6b +PATHASM = .; +PATHPAS = .; +PATHRC = .; +DEBUGLIBPATH = $(BCB)\lib\debug +RELEASELIBPATH = $(BCB)\lib\release +# --------------------------------------------------------------------------- +CFLAG1 = -WD -O2 -Hc -w- -d -k- -vi -w-par -c -tWD +CFLAG2 = -D_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;XMNG_DECREMENT_LOOPS;MNG_OPTIMIZE_FOOTPRINT_INIT;XMNG_OPTIMIZE_FOOTPRINT_MAGN;MNG_OPTIMIZE_OBJCLEANUP;MNG_OPTIMIZE_CHUNKINITFREE;MNG_OPTIMIZE_CHUNKASSIGN;MNG_OPTIMIZE_CHUNKREADER;XMNG_OPTIMIZE_DISPLAYCALLS;XMNG_INCLUDE_MPNG_PROPOSAL;XMNG_INCLUDE_ANG_PROPOSAL \ + -I"c:\program files\borland\cbuilder3\projects";..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms\include;..\..\..\lcms\src;$(BCB)\include;$(BCB)\bin \ + -H=$(BCB)\lib\vcl35.csm +CFLAG3 = -Tkh30000 -ff -pr -wuse -wucp -wstv -wstu -wsig -wpin -wnod -wnak -wdef -wcln \ + -wbbf -wasm -wamp -wamb +PFLAGS = -D_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;XMNG_DECREMENT_LOOPS;MNG_OPTIMIZE_FOOTPRINT_INIT;XMNG_OPTIMIZE_FOOTPRINT_MAGN;MNG_OPTIMIZE_OBJCLEANUP;MNG_OPTIMIZE_CHUNKINITFREE;MNG_OPTIMIZE_CHUNKASSIGN;MNG_OPTIMIZE_CHUNKREADER;XMNG_OPTIMIZE_DISPLAYCALLS;XMNG_INCLUDE_MPNG_PROPOSAL;XMNG_INCLUDE_ANG_PROPOSAL \ + -N2..\..\..\obj -N0..\..\..\obj \ + -U"c:\program files\borland\cbuilder3\projects";..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms\include;..\..\..\lcms\src;$(BCB)\lib;$(BCB)\bin;$(RELEASELIBPATH) \ + -I"c:\program files\borland\cbuilder3\projects";..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms\include;..\..\..\lcms\src;$(BCB)\include;$(BCB)\bin \ + -H -W -$L- -$D- -v -JPHN -M +RFLAGS = -D_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;XMNG_DECREMENT_LOOPS;MNG_OPTIMIZE_FOOTPRINT_INIT;XMNG_OPTIMIZE_FOOTPRINT_MAGN;MNG_OPTIMIZE_OBJCLEANUP;MNG_OPTIMIZE_CHUNKINITFREE;MNG_OPTIMIZE_CHUNKASSIGN;MNG_OPTIMIZE_CHUNKREADER;XMNG_OPTIMIZE_DISPLAYCALLS;XMNG_INCLUDE_MPNG_PROPOSAL;XMNG_INCLUDE_ANG_PROPOSAL \ + -i"c:\program files\borland\cbuilder3\projects";..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms\include;..\..\..\lcms\src;$(BCB)\include;$(BCB)\bin +AFLAGS = /i"c:\program files\borland\cbuilder3\projects" /i..\..\..\libmng-devel \ + /i..\..\..\zlib /i..\..\..\jpgsrc6b /i..\..\..\lcms\include /i..\..\..\lcms\src \ + /i$(BCB)\include /i$(BCB)\bin /d_NO_VCL /dMNG_BUILD_DLL /dMNG_FULL_CMS \ + /dMNG_STRICT_ANSI /dMNG_CHECK_BAD_ICCP /dZLIB_DLL /dZLIB_WINAPI \ + /dMNG_OPTIMIZE_FOOTPRINT_COMPOSE /dMNG_OPTIMIZE_FOOTPRINT_DIV \ + /dMNG_OPTIMIZE_FOOTPRINT_SWITCH /dXMNG_DECREMENT_LOOPS \ + /dMNG_OPTIMIZE_FOOTPRINT_INIT /dXMNG_OPTIMIZE_FOOTPRINT_MAGN \ + /dMNG_OPTIMIZE_OBJCLEANUP /dMNG_OPTIMIZE_CHUNKINITFREE \ + /dMNG_OPTIMIZE_CHUNKASSIGN /dMNG_OPTIMIZE_CHUNKREADER \ + /dXMNG_OPTIMIZE_DISPLAYCALLS /dXMNG_INCLUDE_MPNG_PROPOSAL \ + /dXMNG_INCLUDE_ANG_PROPOSAL /mx /w2 /zd +LFLAGS = -L"c:\program files\borland\cbuilder3\projects";..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms\include;..\..\..\lcms\src;$(BCB)\lib;$(BCB)\bin;$(RELEASELIBPATH) \ + -H:0x1000000 -Hc:0x10000 -B:0x60000000 -aa -Tpd -s -Gn -Gi -M -wdpl -d +IFLAGS = +# --------------------------------------------------------------------------- +ALLOBJ = c0d32.obj $(OBJFILES) +ALLRES = $(RESFILES) +ALLLIB = $(LIBFILES) import32.lib cw32mt.lib +# --------------------------------------------------------------------------- +!ifdef IDEOPTIONS + +[Version Info] +IncludeVerInfo=1 +AutoIncBuild=1 +MajorVer=1 +MinorVer=0 +Release=10 +Build=1440 +Debug=0 +PreRelease=0 +Special=0 +Private=0 +DLL=1 +Locale=1033 +CodePage=1252 + +[Version Info Keys] +CompanyName=PNG/MNG Group +FileDescription=libmng - THE MNG library +FileVersion=1.0.10.1440 +InternalName=libmng +LegalCopyright=Copyright © 2000-2007 G. Juyn, 2007 G.Randers-Pherson +LegalTrademarks= +OriginalFilename=libmng.dll +ProductName=libmng +ProductVersion=1.0.10 +Comments= + +[HistoryLists\hlIncludePath] +Count=12 +Item0=c:\program files\borland\cbuilder3\projects;..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms\include;..\..\..\lcms\src;$(BCB)\include;$(BCB)\bin +Item1=..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms\include;..\..\..\lcms\src;$(BCB)\include;$(BCB)\bin +Item2=c:\program files\borland\cbuilder3\projects;..\..\..\libmng;..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms\include;..\..\..\lcms\src;$(BCB)\include;$(BCB)\bin +Item3=..\..\..\libmng;..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms\include;..\..\..\lcms\src;$(BCB)\include;$(BCB)\bin +Item4=..\..\..\libmng;..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms\include;$(BCB)\include;$(BCB)\bin +Item5=..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms\include;$(BCB)\include;$(BCB)\bin +Item6=..\..\..\libmng;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms\include;$(BCB)\include;$(BCB)\bin +Item7=..\..\..\..\jpgsrc6b;..\..\..\..\lcms\include;..\..\..\..\lcms\source;..\..\..\..\zlib;..\..\..\..\libmng;$(BCB)\include;$(BCB)\bin +Item8=..\..\..\..\lcms\include;..\..\..\..\lcms\source;..\..\..\..\zlib;..\..\..\..\libmng;$(BCB)\include;$(BCB)\bin +Item9=..\..\..\..\zlib;..\..\..\..\libmng;$(BCB)\include;$(BCB)\bin +Item10=..\..\libmng;$(BCB)\include;$(BCB)\bin +Item11=..\..\libmng;$(BCB)\include + +[HistoryLists\hlLibraryPath] +Count=12 +Item0=c:\program files\borland\cbuilder3\projects;..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms\include;..\..\..\lcms\src;$(BCB)\lib;$(BCB)\bin +Item1=..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms\include;..\..\..\lcms\src;$(BCB)\lib;$(BCB)\bin +Item2=c:\program files\borland\cbuilder3\projects;..\..\..\lcms\src;..\..\..\libmng;..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;$(BCB)\lib;$(BCB)\bin +Item3=..\..\..\lcms\src;..\..\..\libmng;..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;$(BCB)\lib;$(BCB)\bin +Item4=..\..\..\libmng;..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;$(BCB)\lib;$(BCB)\bin +Item5=..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;$(BCB)\lib;$(BCB)\bin +Item6=..\..\..\libmng;..\..\..\jpgsrc6b;..\..\..\zlib;$(BCB)\lib;$(BCB)\bin +Item7=..\..\..\..\jpgsrc6b;..\..\..\..\lcms\source;..\..\..\..\zlib;..\..\..\..\libmng;$(BCB)\lib;$(BCB)\bin +Item8=..\..\..\..\lcms\source;..\..\..\..\zlib;..\..\..\..\libmng;$(BCB)\lib;$(BCB)\bin +Item9=..\..\..\..\zlib;..\..\..\..\libmng;$(BCB)\lib;$(BCB)\bin +Item10=..\..\libmng;$(BCB)\lib;$(BCB)\bin +Item11=..\..\libmng;$(BCB)\lib + +[HistoryLists\hlDebugSourcePath] +Count=7 +Item0=..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms\src +Item1=..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms\source;..\..\..\lcms\src +Item2=..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms\source +Item3=..\..\..\jpgsrc6b;..\..\..\lcms\source;..\..\..\zlib;..\..\..\libmng +Item4=..\..\..\..\jpgsrc6b;..\..\..\..\lcms\source;..\..\..\..\zlib;..\..\..\..\libmng +Item5=..\..\..\..\libmng +Item6=..\..\libmng + +[HistoryLists\hlConditionals] +Count=30 +Item0=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;XMNG_DECREMENT_LOOPS;MNG_OPTIMIZE_FOOTPRINT_INIT;XMNG_OPTIMIZE_FOOTPRINT_MAGN;MNG_OPTIMIZE_OBJCLEANUP;MNG_OPTIMIZE_CHUNKINITFREE;MNG_OPTIMIZE_CHUNKASSIGN;MNG_OPTIMIZE_CHUNKREADER;XMNG_OPTIMIZE_DISPLAYCALLS;XMNG_INCLUDE_MPNG_PROPOSAL;XMNG_INCLUDE_ANG_PROPOSAL +Item1=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;XMNG_DECREMENT_LOOPS;MNG_OPTIMIZE_FOOTPRINT_INIT;XMNG_OPTIMIZE_FOOTPRINT_MAGN;MNG_OPTIMIZE_OBJCLEANUP;MNG_OPTIMIZE_CHUNKINITFREE;MNG_OPTIMIZE_CHUNKASSIGN;MNG_OPTIMIZE_CHUNKREADER;XMNG_OPTIMIZE_DISPLAYCALLS;MNG_INCLUDE_MPNG_PROPOSAL;MNG_INCLUDE_ANG_PROPOSAL +Item2=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;XMNG_DECREMENT_LOOPS;MNG_OPTIMIZE_FOOTPRINT_INIT;XMNG_OPTIMIZE_FOOTPRINT_MAGN;MNG_OPTIMIZE_OBJCLEANUP;MNG_OPTIMIZE_CHUNKINITFREE;MNG_OPTIMIZE_CHUNKASSIGN;MNG_OPTIMIZE_CHUNKREADER;XMNG_OPTIMIZE_DISPLAYCALLS;XMNG_INCLUDE_MPNG_PROPOSAL;MNG_INCLUDE_ANG_PROPOSAL +Item3=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;XMNG_DECREMENT_LOOPS;MNG_OPTIMIZE_FOOTPRINT_INIT;XMNG_OPTIMIZE_FOOTPRINT_MAGN;MNG_OPTIMIZE_OBJCLEANUP;MNG_OPTIMIZE_CHUNKINITFREE;MNG_OPTIMIZE_CHUNKASSIGN;MNG_OPTIMIZE_CHUNKREADER;XMNG_OPTIMIZE_DISPLAYCALLS;MNG_INCLUDE_MPNG_PROPOSAL;XMNG_INCLUDE_ANG_PROPOSAL +Item4=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;XMNG_DECREMENT_LOOPS;MNG_OPTIMIZE_FOOTPRINT_INIT;XMNG_OPTIMIZE_FOOTPRINT_MAGN;MNG_OPTIMIZE_OBJCLEANUP;MNG_OPTIMIZE_CHUNKINITFREE;MNG_OPTIMIZE_CHUNKASSIGN;MNG_OPTIMIZE_CHUNKREADER;XMNG_OPTIMIZE_DISPLAYCALLS;MNG_INCLUDE_MPNG_PROPOSAL;MNG_INCLUDE_ANG6_PROPOSAL +Item5=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;XMNG_DECREMENT_LOOPS;MNG_OPTIMIZE_FOOTPRINT_INIT;XMNG_OPTIMIZE_FOOTPRINT_MAGN;MNG_OPTIMIZE_OBJCLEANUP;MNG_OPTIMIZE_CHUNKINITFREE;MNG_OPTIMIZE_CHUNKASSIGN;MNG_OPTIMIZE_CHUNKREADER;XMNG_OPTIMIZE_DISPLAYCALLS;MNG_INCLUDE_MPNG_PROPOSAL +Item6=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;XMNG_DECREMENT_LOOPS;MNG_OPTIMIZE_FOOTPRINT_INIT;XMNG_OPTIMIZE_FOOTPRINT_MAGN;MNG_OPTIMIZE_OBJCLEANUP;MNG_OPTIMIZE_CHUNKINITFREE;MNG_OPTIMIZE_CHUNKASSIGN;MNG_OPTIMIZE_CHUNKREADER;XMNG_OPTIMIZE_DISPLAYCALLS;XMNG_INCLUDE_MPNG_PROPOSAL +Item7=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;XMNG_DECREMENT_LOOPS;MNG_OPTIMIZE_FOOTPRINT_INIT;XMNG_OPTIMIZE_FOOTPRINT_MAGN;MNG_OPTIMIZE_OBJCLEANUP;MNG_OPTIMIZE_CHUNKINITFREE;MNG_OPTIMIZE_CHUNKASSIGN;XMNG_OPTIMIZE_CHUNKREADER;XMNG_OPTIMIZE_DISPLAYCALLS;MNG_INCLUDE_MPNG_PROPOSAL +Item8=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;XMNG_DECREMENT_LOOPS;MNG_OPTIMIZE_FOOTPRINT_INIT;XMNG_OPTIMIZE_FOOTPRINT_MAGN;MNG_OPTIMIZE_OBJCLEANUP;MNG_OPTIMIZE_CHUNKINITFREE;MNG_OPTIMIZE_CHUNKASSIGN;MNG_OPTIMIZE_CHUNKREADER;XMNG_OPTIMIZE_DISPLAYCALLS;MNG_SUPPORT_MPNG_PROPOSAL +Item9=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;XMNG_DECREMENT_LOOPS;MNG_OPTIMIZE_FOOTPRINT_INIT;XMNG_OPTIMIZE_FOOTPRINT_MAGN;MNG_OPTIMIZE_OBJCLEANUP;MNG_OPTIMIZE_CHUNKINITFREE;MNG_OPTIMIZE_CHUNKASSIGN;MNG_OPTIMIZE_CHUNKREADER;XMNG_OPTIMIZE_DISPLAYCALLS +Item10=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;XMNG_DECREMENT_LOOPS;MNG_OPTIMIZE_FOOTPRINT_INIT;XMNG_OPTIMIZE_FOOTPRINT_MAGN;MNG_OPTIMIZE_OBJCLEANUP;MNG_OPTIMIZE_CHUNKINITFREE;MNG_OPTIMIZE_CHUNKASSIGN;MNG_OPTIMIZE_CHUNKREADER;MNG_OPTIMIZE_DISPLAYCALLS +Item11=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;MNG_DECREMENT_LOOPS;MNG_OPTIMIZE_FOOTPRINT_INIT;XMNG_OPTIMIZE_FOOTPRINT_MAGN;MNG_OPTIMIZE_OBJCLEANUP;MNG_OPTIMIZE_CHUNKINITFREE;MNG_OPTIMIZE_CHUNKASSIGN;MNG_OPTIMIZE_CHUNKREADER;XMNG_OPTIMIZE_DISPLAYCALLS +Item12=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;MNG_DECREMENT_LOOPS;MNG_OPTIMIZE_FOOTPRINT_INIT;MNG_OPTIMIZE_FOOTPRINT_MAGN;MNG_OPTIMIZE_OBJCLEANUP;MNG_OPTIMIZE_CHUNKINITFREE;MNG_OPTIMIZE_CHUNKASSIGN;MNG_OPTIMIZE_CHUNKREADER;XMNG_OPTIMIZE_DISPLAYCALLS +Item13=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;MNG_OPTIMIZE_FOOTPRINT_INIT;MNG_OPTIMIZE_OBJCLEANUP;MNG_OPTIMIZE_CHUNKINITFREE;MNG_OPTIMIZE_CHUNKASSIGN;MNG_OPTIMIZE_CHUNKREADER;XMNG_OPTIMIZE_DISPLAYCALLS +Item14=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;MNG_OPTIMIZE_FOOTPRINT_INIT;MNG_OPTIMIZE_OBJCLEANUP;MNG_OPTIMIZE_CHUNKINITFREE;MNG_OPTIMIZE_CHUNKASSIGN;MNG_OPTIMIZE_CHUNKREADER;XMNG_OPTIMIZE_DISPLAYCALLS;MNG_NO_OLD_VERSIONS +Item15=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;MNG_OPTIMIZE_FOOTPRINT_INIT;MNG_OPTIMIZE_OBJCLEANUP;MNG_OPTIMIZE_CHUNKINITFREE;MNG_OPTIMIZE_CHUNKASSIGN;MNG_OPTIMIZE_CHUNKREADER;MNG_OPTIMIZE_DISPLAYCALLS +Item16=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;MNG_OPTIMIZE_FOOTPRINT_INIT;MNG_OPTIMIZE_OBJCLEANUP;MNG_OPTIMIZE_CHUNKINITFREE;MNG_OPTIMIZE_CHUNKASSIGN;MNG_OPTIMIZE_CHUNKREADER +Item17=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;MNG_OPTIMIZE_FOOTPRINT_INIT;XMNG_OPTIMIZE_OBJCLEANUP;XMNG_OPTIMIZE_CHUNKINITFREE;XMNG_OPTIMIZE_CHUNKASSIGN;XMNG_OPTIMIZE_CHUNKREADER +Item18=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;MNG_OPTIMIZE_FOOTPRINT_INIT;MNG_OPTIMIZE_OBJCLEANUP;MNG_OPTIMIZE_CHUNKINITFREE;MNG_OPTIMIZE_CHUNKASSIGN;XMNG_OPTIMIZE_CHUNKREADER +Item19=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;MNG_OPTIMIZE_FOOTPRINT_INIT;MNG_OPTIMIZE_CHUNKINITFREE;MNG_OPTIMIZE_OBJCLEANUP;MNG_OPTIMIZE_CHUNKASSIGN;MNG_OPTIMIZE_CHUNKREADER +Item20=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;MNG_OPTIMIZE_FOOTPRINT_INIT;MNG_OPTIMIZE_CHUNKINITFREE;MNG_OPTIMIZE_OBJCLEANUP;MNG_OPTIMIZE_CHUNKASSIGN;XMNG_OPTIMIZE_CHUNKREADER +Item21=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;MNG_OPTIMIZE_FOOTPRINT_INIT;MNG_OPTIMIZE_CHUNKINITFREE;MNG_OPTIMIZE_OBJCLEANUP;MNG_OPTIMIZE_CHUNKASSIGN +Item22=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;MNG_OPTIMIZE_FOOTPRINT_INIT;MNG_OPTIMIZE_CHUNKINITFREE;MNG_OPTIMIZE_OBJCLEANUP +Item23=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;MNG_OPTIMIZE_FOOTPRINT_INIT;MNG_OPTIMIZE_CHUNKINITFREE +Item24=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;MNG_OPTIMIZE_FOOTPRINT_INIT;XMNG_OPTIMIZE_CHUNKINITFREE +Item25=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;MNG_OPTIMIZE_FOOTPRINT_INIT +Item26=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;XMNG_OPTIMIZE_FOOTPRINT_COMPOSE;XMNG_OPTIMIZE_FOOTPRINT_DIV;XMNG_OPTIMIZE_FOOTPRINT_SWITCH;XMNG_OPTIMIZE_FOOTPRINT_INIT +Item27=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;MNG_OPTIMIZE_FOOTPRINT_INIT;MNG_SUPPORT_TRACE +Item28=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;MNG_OPTIMIZE_FOOTPRINT_INIT;x_MNG_NO_16BIT_SUPPORT +Item29=_NO_VCL;MNG_BUILD_DLL;MNG_FULL_CMS;MNG_STRICT_ANSI;MNG_CHECK_BAD_ICCP;ZLIB_DLL;ZLIB_WINAPI;MNG_OPTIMIZE_FOOTPRINT_COMPOSE;MNG_OPTIMIZE_FOOTPRINT_DIV;MNG_OPTIMIZE_FOOTPRINT_SWITCH;MNG_OPTIMIZE_FOOTPRINT_INIT;MNG_NO_16BIT_SUPPORT + +[HistoryLists\hlIntOutputDir] +Count=2 +Item0=..\..\..\obj +Item1=..\..\..\..\obj + +[HistoryLists\hlHostApplication] +Count=23 +Item0=D:\Triple-T\Software\mnglib3t\libmng-devel\bcb\win32dll\mngview.exe +Item1=D:\Triple-T\Software\mnglib3t\libmng-devel\bcb\win32dll\MNGJNGportal.exe +Item2=D:\Triple-T\Software\mnglib3t\libmng-devel\bcb\win32dll\gif2mng.exe +Item3=D:\Triple-T\Software\mnglib3t\libmng-devel\bcb\win32dll\mngview_push.exe +Item4=D:\Triple-T\Software\mnglib3t\libmng-devel\bcb\win32dll\mngtree.exe +Item5=D:\Triple-T\Software\mnglib3t\libmng-devel\bcb\win32dll\mngdump.exe +Item6=D:\Triple-T\Software\mnglib3t\libmng-devel\bcb\win32dll\lm_diag.exe +Item7=D:\Triple-T\Software\mnglib3t\libmng-devel\bcb\win32dll\eMNGma.exe +Item8=D:\Triple-T\Software\mnglib3t\libmng-devel\bcb\win32dll\bogus.exe +Item9=D:\Triple-T\Software\mnglib3t\libmng-devel\bcb\win32dll\TestNGImage.exe +Item10=D:\Triple-T\Software\mnglib3t\libmng-devel\bcb\win32dll\mngrepair.exe +Item11=D:\Triple-T\Software\mnglib3t\libmng-devel\bcb\win32dll\Test_lz.exe +Item12=D:\Triple-T\Software\LossyPNG\Bin\Test_lz.exe +Item13=D:\Triple-T\Software\mnglib3t\libmng-devel\bcb\win32dll\SlowView.exe +Item14=D:\Triple-T\Software\mnglib3t\libmng-devel\bcb\win32dll\Bin\eMNGma.exe +Item15=D:\Triple-T\Software\mnglib3t\libmng-devel\bcb\win32dll\mngpromo.exe +Item16=D:\Triple-T\Software\mnglib3t\libmng\bcb\win32dll\mngdump.exe +Item17=D:\Triple-T\Software\mnglib3t\libmng\bcb\win32dll\mngview.exe +Item18=D:\Triple-T\Software\mnglib3t\libmng\bcb\win32dll\bogus.exe +Item19=D:\Triple-T\Software\mnglib3t\libmng\bcb\win32dll\mngtree.exe +Item20=D:\Triple-T\Software\mnglib3t\libmng\samples\bcb\win32dll\mngview.exe +Item21=D:\Triple-T\Software\mnglib3t\libmng\samples\bcb\win32dll\mngdump.exe +Item22=D:\Triple-T\Software\mnglib3t\libmng\samples\Delphi3\mngdump\mngdump.exe + +[HistoryLists\hlRunParameters] +Count=3 +Item0=sample.mng +Item1=roilion02.mng roilion02-fixed.mng +Item2=usflag-lc-d63.mng + +[Debugging] +DebugSourceDirs=..\..\..\libmng-devel;..\..\..\zlib;..\..\..\jpgsrc6b;..\..\..\lcms\src + +[Parameters] +RunParams= +HostApplication=D:\Triple-T\Software\mnglib3t\libmng-devel\bcb\win32dll\mngview.exe + +!endif + +# --------------------------------------------------------------------------- +# MAKE SECTION +# --------------------------------------------------------------------------- +# This section of the project file is not used by the BCB IDE. It is for +# the benefit of building from the command-line using the MAKE utility. +# --------------------------------------------------------------------------- + +.autodepend +# --------------------------------------------------------------------------- +!if !$d(BCC32) +BCC32 = bcc32 +!endif + +!if !$d(DCC32) +DCC32 = dcc32 +!endif + +!if !$d(TASM32) +TASM32 = tasm32 +!endif + +!if !$d(LINKER) +LINKER = ilink32 +!endif + +!if !$d(BRCC32) +BRCC32 = brcc32 +!endif +# --------------------------------------------------------------------------- +!if $d(PATHCPP) +.PATH.CPP = $(PATHCPP) +.PATH.C = $(PATHCPP) +!endif + +!if $d(PATHPAS) +.PATH.PAS = $(PATHPAS) +!endif + +!if $d(PATHASM) +.PATH.ASM = $(PATHASM) +!endif + +!if $d(PATHRC) +.PATH.RC = $(PATHRC) +!endif +# --------------------------------------------------------------------------- +$(PROJECT): $(OBJFILES) $(RESDEPEN) $(DEFFILE) + $(BCB)\BIN\$(LINKER) @&&! + $(LFLAGS) + + $(ALLOBJ), + + $(PROJECT),, + + $(ALLLIB), + + $(DEFFILE), + + $(ALLRES) +! +# --------------------------------------------------------------------------- +.pas.hpp: + $(BCB)\BIN\$(DCC32) $(PFLAGS) {$< } + +.pas.obj: + $(BCB)\BIN\$(DCC32) $(PFLAGS) {$< } + +.cpp.obj: + $(BCB)\BIN\$(BCC32) $(CFLAG1) $(CFLAG2) $(CFLAG3) -n$(@D) {$< } + +.c.obj: + $(BCB)\BIN\$(BCC32) $(CFLAG1) $(CFLAG2) $(CFLAG3) -n$(@D) {$< } + +.asm.obj: + $(BCB)\BIN\$(TASM32) $(AFLAGS) $<, $@ + +.rc.res: + $(BCB)\BIN\$(BRCC32) $(RFLAGS) -fo$@ $< +# --------------------------------------------------------------------------- diff --git a/Engine/lib/lmng/bcb/win32dll/libmng.cpp b/Engine/lib/lmng/bcb/win32dll/libmng.cpp new file mode 100644 index 000000000..5ba4a8a89 --- /dev/null +++ b/Engine/lib/lmng/bcb/win32dll/libmng.cpp @@ -0,0 +1,153 @@ +/* ************************************************************************** */ +/* * * */ +/* * COPYRIGHT NOTICE: * */ +/* * * */ +/* * Copyright (c) 2000-2007 Gerard Juyn (gerard@libmng.com) * */ +/* * [You may insert additional notices after this sentence if you modify * */ +/* * this source] * */ +/* * * */ +/* * For the purposes of this copyright and license, "Contributing Authors" * */ +/* * is defined as the following set of individuals: * */ +/* * * */ +/* * Gerard Juyn * */ +/* * (hopefully some more to come...) * */ +/* * * */ +/* * The MNG Library is supplied "AS IS". The Contributing Authors * */ +/* * disclaim all warranties, expressed or implied, including, without * */ +/* * limitation, the warranties of merchantability and of fitness for any * */ +/* * purpose. The Contributing Authors assume no liability for direct, * */ +/* * indirect, incidental, special, exemplary, or consequential damages, * */ +/* * which may result from the use of the MNG Library, even if advised of * */ +/* * the possibility of such damage. * */ +/* * * */ +/* * Permission is hereby granted to use, copy, modify, and distribute this * */ +/* * source code, or portions hereof, for any purpose, without fee, subject * */ +/* * to the following restrictions: * */ +/* * * */ +/* * 1. The origin of this source code must not be misrepresented; * */ +/* * you must not claim that you wrote the original software. * */ +/* * * */ +/* * 2. Altered versions must be plainly marked as such and must not be * */ +/* * misrepresented as being the original source. * */ +/* * * */ +/* * 3. This Copyright notice may not be removed or altered from any source * */ +/* * or altered source distribution. * */ +/* * * */ +/* * The Contributing Authors specifically permit, without fee, and * */ +/* * encourage the use of this source code as a component to supporting * */ +/* * the MNG and JNG file format in commercial products. If you use this * */ +/* * source code in a product, acknowledgment would be highly appreciated. * */ +/* * * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng.cpp copyright (c) 2000-2002 G.Juyn * */ +/* * version : 1.0.5 * */ +/* * * */ +/* * purpose : generic dll project assembly file * */ +/* * * */ +/* * author : G.Juyn * */ +/* * web : http://www.3-t.com * */ +/* * email : mailto:info@3-t.com * */ +/* * * */ +/* * comment : Autogenerated file with the libmng.dll BCB project * */ +/* * * */ +/* * changes : 0.5.1 - 05/14/2000 - G.Juyn * */ +/* * - added this block * */ +/* * * */ +/* * 1.0.5 - 08/20/2000 - G.Juyn * */ +/* * - version-number & copyright * */ +/* * * */ +/* ************************************************************************** */ + +#include +#pragma hdrstop +#include + +/* ************************************************************************** */ + +USERES("libmng.res"); +USEUNIT("..\..\libmng_hlapi.c"); +USEUNIT("..\..\libmng_callback_xs.c"); +USEUNIT("..\..\libmng_prop_xs.c"); +USEUNIT("..\..\libmng_chunk_xs.c"); +USEUNIT("..\..\libmng_object_prc.c"); +USEUNIT("..\..\libmng_chunk_descr.c"); +USEUNIT("..\..\libmng_chunk_prc.c"); +USEUNIT("..\..\libmng_chunk_io.c"); +USEUNIT("..\..\libmng_read.c"); +USEUNIT("..\..\libmng_write.c"); +USEUNIT("..\..\libmng_display.c"); +USEUNIT("..\..\libmng_dither.c"); +USEUNIT("..\..\libmng_pixels.c"); +USEUNIT("..\..\libmng_filter.c"); +USEUNIT("..\..\libmng_error.c"); +USEUNIT("..\..\libmng_trace.c"); +USEUNIT("..\..\libmng_cms.c"); +USEUNIT("..\..\libmng_zlib.c"); +USEUNIT("..\..\libmng_jpeg.c"); +USEUNIT("..\..\..\zlib\adler32.c"); +USEUNIT("..\..\..\zlib\compress.c"); +USEUNIT("..\..\..\zlib\crc32.c"); +USEUNIT("..\..\..\zlib\deflate.c"); +USEUNIT("..\..\..\zlib\inffast.c"); +USEUNIT("..\..\..\zlib\inflate.c"); +USEUNIT("..\..\..\zlib\inftrees.c"); +USEUNIT("..\..\..\zlib\trees.c"); +USEUNIT("..\..\..\zlib\uncompr.c"); +USEUNIT("..\..\..\zlib\zutil.c"); +USEUNIT("..\..\..\jpgsrc6b\jquant2.c"); +USEUNIT("..\..\..\jpgsrc6b\jcapistd.c"); +USEUNIT("..\..\..\jpgsrc6b\jccoefct.c"); +USEUNIT("..\..\..\jpgsrc6b\jccolor.c"); +USEUNIT("..\..\..\jpgsrc6b\jcdctmgr.c"); +USEUNIT("..\..\..\jpgsrc6b\jchuff.c"); +USEUNIT("..\..\..\jpgsrc6b\jcinit.c"); +USEUNIT("..\..\..\jpgsrc6b\jcmainct.c"); +USEUNIT("..\..\..\jpgsrc6b\jcmarker.c"); +USEUNIT("..\..\..\jpgsrc6b\jcmaster.c"); +USEUNIT("..\..\..\jpgsrc6b\jcomapi.c"); +USEUNIT("..\..\..\jpgsrc6b\jcparam.c"); +USEUNIT("..\..\..\jpgsrc6b\jcphuff.c"); +USEUNIT("..\..\..\jpgsrc6b\jcprepct.c"); +USEUNIT("..\..\..\jpgsrc6b\jcsample.c"); +USEUNIT("..\..\..\jpgsrc6b\jctrans.c"); +USEUNIT("..\..\..\jpgsrc6b\jdapistd.c"); +USEUNIT("..\..\..\jpgsrc6b\jdatadst.c"); +USEUNIT("..\..\..\jpgsrc6b\jdatasrc.c"); +USEUNIT("..\..\..\jpgsrc6b\jdcoefct.c"); +USEUNIT("..\..\..\jpgsrc6b\jdcolor.c"); +USEUNIT("..\..\..\jpgsrc6b\jddctmgr.c"); +USEUNIT("..\..\..\jpgsrc6b\jdhuff.c"); +USEUNIT("..\..\..\jpgsrc6b\jdinput.c"); +USEUNIT("..\..\..\jpgsrc6b\jdmainct.c"); +USEUNIT("..\..\..\jpgsrc6b\jdmarker.c"); +USEUNIT("..\..\..\jpgsrc6b\jdmaster.c"); +USEUNIT("..\..\..\jpgsrc6b\jdmerge.c"); +USEUNIT("..\..\..\jpgsrc6b\jdphuff.c"); +USEUNIT("..\..\..\jpgsrc6b\jdpostct.c"); +USEUNIT("..\..\..\jpgsrc6b\jdsample.c"); +USEUNIT("..\..\..\jpgsrc6b\jdtrans.c"); +USEUNIT("..\..\..\jpgsrc6b\jerror.c"); +USEUNIT("..\..\..\jpgsrc6b\jfdctflt.c"); +USEUNIT("..\..\..\jpgsrc6b\jfdctfst.c"); +USEUNIT("..\..\..\jpgsrc6b\jfdctint.c"); +USEUNIT("..\..\..\jpgsrc6b\jidctflt.c"); +USEUNIT("..\..\..\jpgsrc6b\jidctfst.c"); +USEUNIT("..\..\..\jpgsrc6b\jidctint.c"); +USEUNIT("..\..\..\jpgsrc6b\jidctred.c"); +USEUNIT("..\..\..\jpgsrc6b\jmemmgr.c"); +USEUNIT("..\..\..\jpgsrc6b\jmemnobs.c"); +USEUNIT("..\..\..\jpgsrc6b\jquant1.c"); +USEUNIT("..\..\..\jpgsrc6b\jcapimin.c"); +USEUNIT("..\..\..\jpgsrc6b\jutils.c"); +USEUNIT("..\..\..\jpgsrc6b\jdapimin.c"); +USELIB("..\..\..\lcms\Projects\Bcc-5.5-static\lcmsstat.lib"); +//--------------------------------------------------------------------------- +#pragma argsused +int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*) +{ + return 1; +} +/* ************************************************************************** */ + diff --git a/Engine/lib/lmng/bcb/win32dll/libmng.dll b/Engine/lib/lmng/bcb/win32dll/libmng.dll new file mode 100644 index 000000000..50c4e8250 Binary files /dev/null and b/Engine/lib/lmng/bcb/win32dll/libmng.dll differ diff --git a/Engine/lib/lmng/contrib/README b/Engine/lib/lmng/contrib/README new file mode 100644 index 000000000..08d01e9a0 --- /dev/null +++ b/Engine/lib/lmng/contrib/README @@ -0,0 +1 @@ +For details check the README.contrib file in the parent directory! diff --git a/Engine/lib/lmng/contrib/bcb/mngdump/About.cpp b/Engine/lib/lmng/contrib/bcb/mngdump/About.cpp new file mode 100644 index 000000000..dce6ea8eb --- /dev/null +++ b/Engine/lib/lmng/contrib/bcb/mngdump/About.cpp @@ -0,0 +1,25 @@ +//--------------------------------------------------------------------------- +// About.cpp : About pane text +//--------------------------------------------------------------------------- +#include "About.h" + +// NB : If more text is to go in here don't forget to enlarge min/max +extern char *_szAbout = { +"\nCopyright (c) 2000 Andy Protano (a.a.protano@care4free.net)"\ +"\n Gerard Juyn (gerard@libmng.com)"\ +"\n"\ +"\nLibmng website - www.libmng.com" +"\nMNG Homepage - www.libpng.org/pub/mng" +"\nPNG Homepage - www.libpng.org/pub/png" +"\n" +"\nThis program is based upon the orignal \'MngTree\' by Gerard Juyn"\ +"\nAuthor & current maintainer : Andy Protano (a.a.protano@care4free.net)"\ +"\n"\ +"\nThis software is based on libmng which in its turn is based on software by :"\ +"\n"\ +"\nIndependant JPEG Group - http://www.ijg.org"\ +"\nLcms (little CMS) library by Marti Maria Saguar - http://www.lcms.colorid.de"\ +"\nZlib - http://www.info-zip.org/pub/infozip/zlib"\ +}; +//--------------------------------------------------------------------------- + diff --git a/Engine/lib/lmng/contrib/bcb/mngdump/About.h b/Engine/lib/lmng/contrib/bcb/mngdump/About.h new file mode 100644 index 000000000..fd36c637c --- /dev/null +++ b/Engine/lib/lmng/contrib/bcb/mngdump/About.h @@ -0,0 +1,7 @@ +//--------------------------------------------------------------------------- +#ifndef AboutH +#define AboutH +//--------------------------------------------------------------------------- +extern char *_szAbout; +#endif + diff --git a/Engine/lib/lmng/contrib/bcb/mngdump/CALLBACK.CPP b/Engine/lib/lmng/contrib/bcb/mngdump/CALLBACK.CPP new file mode 100644 index 000000000..c2378fed4 --- /dev/null +++ b/Engine/lib/lmng/contrib/bcb/mngdump/CALLBACK.CPP @@ -0,0 +1,96 @@ +//--------------------------------------------------------------------------- +#include +#pragma hdrstop +# include + +#include "Main.h" + +//--------------------------------------------------------------------------- +mng_ptr __stdcall TMainForm::Alloc( mng_size_t iSize ) +{ + return (mng_ptr)calloc( 1, (size_t)iSize ); +} +//--------------------------------------------------------------------------- +void __stdcall TMainForm::Free( mng_ptr pPtr, mng_size_t iSize ) +{ + free( pPtr ); + return; +} +//--------------------------------------------------------------------------- +mng_bool __stdcall TMainForm::FileReadData( mng_handle hMNG, + mng_ptr pBuf, + mng_uint32 iSize, + mng_uint32 *iRead ) +{ +TMainForm *MainForm = (TMainForm *)mng_get_userdata( hMNG ); + + *iRead = fread( pBuf, 1, iSize, MainForm->GetFd() ); + + // iRead will indicate EOF + + MainForm->ProgressBar1->Position += (int)iRead; + + return MNG_TRUE; +} +//--------------------------------------------------------------------------- +mng_bool __stdcall TMainForm::ProcessHeader( mng_handle hHandle, + mng_uint32 iWidth, + mng_uint32 iHeight ) +{ +TMainForm *MainForm = (TMainForm *)mng_get_userdata( hHandle ); + + MainForm->Caption = ExtractFileName( MainForm->OpenDialog1->FileName ) + + " [" + + String( iWidth ) + + "x" + + String( iHeight ) + + "]"; + + Application->Title = MainForm->asAppName + " " + MainForm->Caption; + + MainForm->ProgressBar1->Max = iWidth * iHeight; + + return MNG_TRUE; +} +//--------------------------------------------------------------------------- +mng_bool __stdcall TMainForm::OpenStream( mng_handle hMNG ) +{ + // nothing to do ! + return MNG_TRUE; +} +//--------------------------------------------------------------------------- +mng_bool __stdcall TMainForm::CloseStream( mng_handle hMNG ) +{ + MainForm->ProgressBar1->Position = 0; + + return MNG_TRUE; +} +//--------------------------------------------------------------------------- +mng_bool __stdcall TMainForm::IterateChunks( mng_handle hMNG, + mng_handle hChunk, + mng_chunkid iChunktype, + mng_uint32 iChunkseq ) +{ +TMainForm *MainForm = (TMainForm *)mng_get_userdata( hMNG ); +char aCh[5]; + + // decode the chunkname + aCh[0] = (char)((iChunktype >> 24) & 0xFF); + aCh[1] = (char)((iChunktype >> 16) & 0xFF); + aCh[2] = (char)((iChunktype >> 8) & 0xFF); + aCh[3] = (char)((iChunktype ) & 0xFF); + aCh[4] = (char)0; // zero terminate - used as a "C" string below + + MainForm->RichEditReport->Lines->Add( "" ); + + MainForm->RichEditReport->Lines->Add( + "Chunk " + String( iChunkseq + 1 ) + " : " + String( aCh ) ); + + // Add Chunk text to listbox + MainForm->ListBoxChunks->Items->Add( aCh ); + + // keep'm coming ... unless we encounter an error + return MainForm->ShowChunk( hMNG, hChunk, iChunktype ); +} +//--------------------------------------------------------------------------- + diff --git a/Engine/lib/lmng/contrib/bcb/mngdump/CALLBACK.H b/Engine/lib/lmng/contrib/bcb/mngdump/CALLBACK.H new file mode 100644 index 000000000..224a3130c --- /dev/null +++ b/Engine/lib/lmng/contrib/bcb/mngdump/CALLBACK.H @@ -0,0 +1,39 @@ +//--------------------------------------------------------------------------- +#include +#include "Main.h" +#include "libmng.h" +#pragma hdrstop + +#ifndef CallbackH +#define CallbackH + +//--------------------------------------------------------------------------- +extern mng_ptr __stdcall myalloc( mng_size_t iSize ); +//--------------------------------------------------------------------------- +extern void __stdcall myfree( mng_ptr pPtr, mng_size_t iSize ); +//--------------------------------------------------------------------------- +extern mng_bool __stdcall myreaddata( mng_handle hMNG, + mng_ptr pBuf, + mng_uint32 iSize, + mng_uint32 *iRead ); +//--------------------------------------------------------------------------- +extern mng_bool __stdcall ProcessHeader( mng_handle hHandle, + mng_uint32 iWidth, + mng_uint32 iHeight ); +//--------------------------------------------------------------------------- +extern mng_bool __stdcall myopenstream( mng_handle hMNG ); +//--------------------------------------------------------------------------- +extern mng_bool __stdcall myclosestream( mng_handle hMNG ); +//--------------------------------------------------------------------------- +extern mng_bool __stdcall myiterchunk ( mng_handle hMNG, + mng_handle hChunk, + mng_chunkid iChunktype, + mng_uint32 iChunkseq ); +//--------------------------------------------------------------------------- +extern mng_bool __stdcall mytraceproc( mng_handle hHandle, + mng_int32 iFuncnr, + mng_int32 iFuncseq, + mng_pchar zFuncname ); +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +#endif diff --git a/Engine/lib/lmng/contrib/bcb/mngdump/CHUNKS.CPP b/Engine/lib/lmng/contrib/bcb/mngdump/CHUNKS.CPP new file mode 100644 index 000000000..16a7f1a07 --- /dev/null +++ b/Engine/lib/lmng/contrib/bcb/mngdump/CHUNKS.CPP @@ -0,0 +1,1689 @@ +// +// Chunks.cpp +// +// The following functions are to assemble a string for each chunk +// The function name "Info_????" denote which chunk it handles +// +// Comments marked @todo@ may, or maynot, be filled in at a later date ! +// +//--------------------------------------------------------------------------- +#include +#pragma hdrstop +#include "Main.h" + +# define xUSE_UNKNOWN // remove "x" to report of "UNKNOWN" - else "" + +# define FFACTOR ((float)100000.0) + +// NB We must use Octal for RichEdit's newlines - MicroSoft love Octal ! +# define nl "\015\012" +# define TAB asTab + +// Ps : If anyone reading this knows how to do this using token +// pasting please let me know ! (AP) + +// ConCat newline to string +# define NL as = as + nl + +// Integer ARGuement +# define IARG( _str_, _int_ )\ + as = as + TAB + (_str_) + " " + String( _int_ ) + +// Bool ARGuement +# define BARG( _str_, _bool_ )\ + if( WantsBool() )\ + as = as + TAB + (_str_) + " " + ((_bool_) ? "true" : "false");\ + else\ + as = as + TAB + (_str_) + " " + IntToStr( _bool_ ); + +// Float ARGuement +# define FARG( _str_, _float_ )\ + as = as + TAB + (_str_) + " " + String( _float_ ) + +// szText ARGuement +# define ZARG( _str_, _sz_ )\ + as = as + TAB + (_str_) + " \"" + String( _sz_ ) + "\"" + +// Concat general strings +# define STR( _str_ ) as = as + _str_ + +// Concat Hex string +# define HEXSTR( _str_ ) as = as + IntToHex(_str_,2) + +// Concat Macro Identifier to ARG +#define MI( _str_ ) as = as + TAB + _str_; + +// Concat "(" Comment ")" to ARG +#define PCOM( _str_ )\ + if( WantsComments() ) as = as + TAB + "(" + _str_ + ")"; + +// Concat "float(" Comment ")" to ARG +#define FPCOM( _str_ )\ + if( WantsComments() ) as = as + TAB + "float(" + _str_ + ")"; + +//--------------------------------------------------------------------------- +// Macro Identifiers - these, below, are used several times +// NB those used once have local #define's and #undef's +//--------------------------------------------------------------------------- + +# ifdef USE_UNKNOWN +# define UNKNOWN "UNKNOWN" +# else +# define UNKNOWN "" +# endif +// Does this need two to be more visible ? +# define ILLEGAL_VALUE ">> ILLEGAL VALUE << " + +// IHDR, BASI, JHDR, PROM, sPLT +# define MI_BITDEPTH( _i_ )\ + if( WantsMacroIds() )\ + switch( (_i_) ) {\ + case MNG_BITDEPTH_1 : MI( "MNG_BITDEPTH_1" ); break;\ + case MNG_BITDEPTH_2 : MI( "MNG_BITDEPTH_2" ); break;\ + case MNG_BITDEPTH_4 : MI( "MNG_BITDEPTH_4" ); break;\ + case MNG_BITDEPTH_8 : MI( "MNG_BITDEPTH_8" ); break;\ + case MNG_BITDEPTH_16 : MI( "MNG_BITDEPTH_16" ); break;\ + default : MI(ILLEGAL_VALUE);\ + } + +// IHDR, BASI, PROM +# define MI_COLORTYPE( _i_ )\ + if( WantsMacroIds() )\ + switch( (_i_) ) {\ + case MNG_COLORTYPE_GRAY : MI( "MNG_COLORTYPE_GRAY" ); break;\ + case MNG_COLORTYPE_RGB : MI( "MNG_COLORTYPE_RGB" ); break;\ + case MNG_COLORTYPE_INDEXED : MI( "MNG_COLORTYPE_INDEXED" ); break;\ + case MNG_COLORTYPE_GRAYA : MI( "MNG_COLORTYPE_GRAYA" ); break;\ + case MNG_COLORTYPE_RGBA : MI( "MNG_COLORTYPE_RGBA" ); break;\ + default : MI(ILLEGAL_VALUE);\ + } + +// IHDR, zTXt, iTXt, iCCP, BASI, JHDR +# define MI_COMPRESSION_DEFLATE( _i_ )\ + if( WantsMacroIds() ) {\ + if( (_i_) == MNG_COMPRESSION_DEFLATE ) {\ + MI( "MNG_COMPRESSION_DEFLATE" );\ + } else {\ + MI( ILLEGAL_VALUE );\ + }\ + } + +// IHDR, BASI, JHDR +# define MI_FILTER( _i_ )\ + if( WantsMacroIds() )\ + switch( _i_ ) {\ + case MNG_FILTER_ADAPTIVE : MI( "MNG_FILTER_ADAPTIVE" ); break;\ + case MNG_FILTER_NO_ADAPTIVE : MI( "MNG_FILTER_NO_ADAPTIVE" ); break;\ + case MNG_FILTER_DIFFERING : MI( "MNG_FILTER_DIFFERING" ); break;\ + case MNG_FILTER_MASK : MI( "MNG_FILTER_MASK" ); break;\ + default : MI(UNKNOWN);\ + } + //NB MNG_FILTER_NO_DIFFERING == MNG_FILTER_ADAPTIVE + +// IHDR, BASI, JHDR +#define MI_INTERLACE( _i_ )\ + if( WantsMacroIds() ) {\ + if( (_i_) == MNG_INTERLACE_NONE ) {\ + MI( "MNG_INTERLACE_NONE" )\ + } else if( (_i_) == MNG_INTERLACE_ADAM7 ) {\ + MI( "MNG_INTERLACE_ADAM7" );\ + } else { MI(ILLEGAL_VALUE);} \ + } + +// pHYs, pHYg +#define MI_UNITS( _i_ )\ + if( WantsMacroIds() ) {\ + if( (_i_) == MNG_UNIT_UNKNOWN ) {\ + MI( "MNG_UNIT_UNKNOWN" )\ + } else if( (_i_) == MNG_UNIT_METER ) {\ + MI( "MNG_UNIT_METER" );\ + } else { MI(UNKNOWN);} \ + } + +// CLON & MOVE +#define MI_LOCATION( _i_ )\ + if( WantsMacroIds() ) {\ + if( (_i_) == MNG_LOCATION_ABSOLUTE ) {\ + MI( "MNG_LOCATION_ABSOLUTE" )\ + } else if( (_i_) == MNG_LOCATION_RELATIVE ) {\ + MI( "MNG_LOCATION_RELATIVE" );\ + } else { MI(ILLEGAL_VALUE);} \ + } + +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_BACK( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +// NB for BACKGROUND other values are only advisory +#define MI_BACKGROUND( _i_ )\ + if( WantsMacroIds() ) {\ + if( (_i_) == MNG_BACKGROUNDCOLOR_MANDATORY ) {\ + MI( "MNG_BACKGROUNDCOLOR_MANDATORY" )\ + } else if( (_i_) == MNG_BACKGROUNDIMAGE_MANDATORY ) {\ + MI( "MNG_BACKGROUNDIMAGE_MANDATORY" );\ + } \ + } + +# define MI_BACKGROUND_TILE( _i_ )\ + if( WantsMacroIds() )\ + switch( _i_ ) {\ + case MNG_BACKGROUNDIMAGE_NOTILE : MI( "MNG_BACKGROUNDIMAGE_NOTILE" ); break;\ + case MNG_BACKGROUNDIMAGE_TILE : MI( "MNG_BACKGROUNDIMAGE_TILE" ); break;\ + default : MI(UNKNOWN);\ + } +mng_uint16 iRed; +mng_uint16 iGreen; +mng_uint16 iBlue; +mng_uint8 iMandatory; +mng_uint16 iImageid; +mng_uint8 iTile; + + if( mng_getchunk_back( hMNG, hChunk, + &iRed, &iGreen, &iBlue, + &iMandatory, &iImageid, &iTile ) != 0 ) + return false; + + IARG( "iRed", iRed ); + NL; IARG( "iGreen",iGreen ); + NL; IARG( "iBlue", iBlue ); + NL; IARG( "iMandatory", iMandatory ); MI_BACKGROUND( iMandatory ); + NL; IARG( "iImageid", iImageid ); + NL; IARG( "iTile", iTile ); MI_BACKGROUND_TILE( iTile ); + + return true; +# undef MI_BACKGROUND +# undef MI_BACKGROUND_TILE +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_BASI( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +#define MI_VIEWABLE( _i_ )\ + if( WantsMacroIds() ) {\ + if( (_i_) == MNG_NOTVIEWABLE ) {\ + MI( "MNG_NOTVIEWABLE" )\ + } else if( (_i_) == MNG_VIEWABLE ) {\ + MI( "MNG_VIEWABLE" );\ + } else { MI(UNKNOWN);} \ + } +mng_uint32 iWidth; +mng_uint32 iHeight; +mng_uint8 iBitdepth; +mng_uint8 iColortype; +mng_uint8 iCompression; +mng_uint8 iFilter; +mng_uint8 iInterlace; +mng_uint16 iRed; +mng_uint16 iGreen; +mng_uint16 iBlue; +mng_uint16 iAlpha; +mng_uint8 iViewable; + + if( mng_getchunk_basi( hMNG, hChunk, + &iWidth, &iHeight, &iBitdepth, &iColortype, + &iCompression, &iFilter, &iInterlace, + &iRed, &iGreen, &iBlue, &iAlpha, + &iViewable ) != 0 ) + return false; + + IARG( "iWidth", iWidth ); + NL; IARG( "iHeight", iHeight ); + NL; IARG( "iBitdepth", iBitdepth ); MI_BITDEPTH( iBitdepth ); + NL; IARG( "iColortype", iColortype ); MI_COLORTYPE( iColortype ); + NL; IARG( "iCompression", iCompression ); MI_COMPRESSION_DEFLATE( iCompression ); + NL; IARG( "iFilter", iFilter ); MI_FILTER( iFilter ); + NL; IARG( "iInterlace", iInterlace ); MI_INTERLACE( iInterlace ); + NL; IARG( "iRed", iRed ); + NL; IARG( "iGreen", iGreen ); + NL; IARG( "iBlue", iBlue ); + NL; IARG( "iAlpha", iAlpha ); + NL; IARG( "iViewable", iViewable ); MI_VIEWABLE( iViewable ); + + return true; +# undef MI_VIEWABLE +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_CLIP( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +# define MI_CLIPPING( _i_ )\ + if( WantsMacroIds() )\ + switch( _i_ ) {\ + case MNG_CLIPPING_ABSOLUTE : MI( "MNG_CLIPPING_ABSOLUTE" ); break;\ + case MNG_CLIPPING_RELATIVE : MI( "MNG_CLIPPING_RELATIVE" ); break;\ + default : MI(UNKNOWN);\ + } + +mng_uint16 iFirstid; +mng_uint16 iLastid; +mng_uint8 iCliptype; +mng_int32 iClipl; +mng_int32 iClipr; +mng_int32 iClipt; +mng_int32 iClipb; + + if( mng_getchunk_clip( hMNG, hChunk, + &iFirstid, &iLastid, + &iCliptype, &iClipl, &iClipr, &iClipt, &iClipb ) != 0 ) + return false; + + IARG( "iFirstid", iFirstid ); + NL; IARG( "iLastid", iLastid ); + NL; IARG( "iCliptype", iCliptype ); MI_CLIPPING( iCliptype ); + NL; IARG( "iClipl", iClipl ); + NL; IARG( "iClipr", iClipr ); + NL; IARG( "iClipt", iClipt ); + NL; IARG( "iClipb", iClipb ); + + return true; +# undef MI_CLIPPING +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_CLON( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +# define MI_CLONTYPE( _i_ )\ + if( WantsMacroIds() )\ + switch( (_i_) ) {\ + case MNG_FULL_CLONE : MI( "MNG_FULL_CLONE" ); break;\ + case MNG_PARTIAL_CLONE : MI( "MNG_PARTIAL_CLONE" ); break;\ + case MNG_RENUMBER : MI( "MNG_RENUMBER" ); break;\ + default : MI(UNKNOWN);\ + } + +#define MI_CLON_CONCRETE( _i_ )\ + if( WantsMacroIds() ) {\ + if( (_i_) == MNG_CONCRETE_ASPARENT ) {\ + MI( "MNG_CONCRETE_ASPARENT" )\ + } else if( (_i_) == MNG_CONCRETE_MAKEABSTRACT ) {\ + MI( "MNG_CONCRETE_MAKEABSTRACT" );\ + } else { MI(UNKNOWN);} \ + } + +mng_uint16 iSourceid; +mng_uint16 iCloneid; +mng_uint8 iClonetype; +mng_uint8 iDonotshow; +mng_uint8 iConcrete; +mng_bool bHasloca; +mng_uint8 iLocationtype; +mng_int32 iLocationx; +mng_int32 iLocationy; + + if( mng_getchunk_clon( hMNG, hChunk, + &iSourceid, &iCloneid, &iClonetype, &iDonotshow, + &iConcrete, &bHasloca, + &iLocationtype, &iLocationx, &iLocationy ) != 0 ) + return false; + + IARG( "iSourceid", iSourceid ); + NL; IARG( "iCloneid", iCloneid ); + NL; IARG( "iClonetype", iClonetype ); MI_CLONTYPE( iClonetype ); + NL; IARG( "iDonotshow", iDonotshow ); + NL; IARG( "iConcrete", iConcrete ); MI_CLON_CONCRETE( iConcrete ); + NL; BARG( "bHasloca", bHasloca ); + NL; IARG( "iLocationtype", iLocationtype ); MI_LOCATION( iLocationtype ); + NL; IARG( "iLocationx", iLocationx ); + NL; IARG( "iLocationy", iLocationy ); + + return true; +# undef MI_CLONTYPE +# undef MI_CLON_CONCRETE +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_DBYK( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +# define MI_POLARITY( _i_ )\ + if( WantsMacroIds() )\ + switch( _i_ ) {\ + case MNG_POLARITY_ONLY : MI( "MNG_POLARITY_ONLY" ); break;\ + case MNG_POLARITY_ALLBUT : MI( "MNG_POLARITY_ALLBUT" ); break;\ + default : MI(UNKNOWN);\ + } +mng_chunkid iChunkname; +mng_uint8 iPolarity; +mng_uint32 iKeywordssize; +mng_pchar zKeywords; + + if( mng_getchunk_dbyk( hMNG, hChunk, + &iChunkname, &iPolarity, &iKeywordssize, &zKeywords ) != 0 ) + return false; + + IARG( "iChunkname", iChunkname ); // show chunk name ? @todo@ + NL; IARG( "iPolarity", iPolarity ); MI_POLARITY( iPolarity ); + NL; IARG( "iKeywordssize", iKeywordssize ); + NL; ZARG( "zKeywords", zKeywords ); + + return true; +# undef MI_POLARITY +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_DEFI( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +#define MI_DONOTSHOW( _i_ )\ + if( WantsMacroIds() ) {\ + if( (_i_) == MNG_DONOTSHOW_VISIBLE ) {\ + MI( "MNG_DONOTSHOW_VISIBLE" )\ + } else if( (_i_) == MNG_DONOTSHOW_NOTVISIBLE ) {\ + MI( "MNG_DONOTSHOW_NOTVISIBLE" );\ + }\ + } + +#define MI_DEFI_CONCRETE( _i_ )\ + if( WantsMacroIds() ) {\ + if( (_i_) == MNG_ABSTRACT ) {\ + MI( "MNG_ABSTRACT" )\ + } else if( (_i_) == MNG_CONCRETE ) {\ + MI( "MNG_CONCRETE" );\ + }\ + } +mng_uint16 iObjectid; +mng_uint8 iDonotshow; +mng_uint8 iConcrete; +mng_bool bHasloca; +mng_int32 iXlocation; +mng_int32 iYlocation; +mng_bool bHasclip; +mng_int32 iLeftcb; +mng_int32 iRightcb; +mng_int32 iTopcb; +mng_int32 iBottomcb; + + if( mng_getchunk_defi( hMNG, hChunk, + &iObjectid, &iDonotshow, &iConcrete, + &bHasloca, &iXlocation, &iYlocation, + &bHasclip, &iLeftcb, &iRightcb, &iTopcb, &iBottomcb ) != 0 ) + return false; + + IARG( "iObjectid", iObjectid ); + NL; IARG( "iDonotshow", iDonotshow ); MI_DONOTSHOW( iDonotshow ); + NL; IARG( "iConcrete", iConcrete ); MI_DEFI_CONCRETE( iConcrete ); + NL; BARG( "bHasloca", bHasloca ); + NL; IARG( "iXlocation", iXlocation ); + NL; IARG( "iYlocation", iYlocation ); + NL; BARG( "bHasclip", bHasclip ); + NL; IARG( "iLeftcb", iLeftcb ); + NL; IARG( "iRightcb", iRightcb ); + NL; IARG( "iTopcb", iTopcb ); + NL; IARG( "iBottomcb", iBottomcb ); + + return true; +# undef MI_DONOTSHOW +# undef MI_DEFI_CONCRETE +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_DHDR( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +# define MI_IMAGETYPE( _i_ )\ + if( WantsMacroIds() )\ + switch( _i_ ) {\ + case MNG_IMAGETYPE_UNKNOWN : MI( "MNG_IMAGETYPE_UNKNOWN" ); break;\ + case MNG_IMAGETYPE_PNG : MI( "MNG_IMAGETYPE_PNG" ); break;\ + case MNG_IMAGETYPE_JNG : MI( "MNG_IMAGETYPE_JNG" ); break;\ + default : MI(UNKNOWN);\ + } + +# define MI_DELTATYPE( _i_ )\ + if( WantsMacroIds() )\ + switch( _i_ ) {\ + case MNG_DELTATYPE_REPLACE : MI( "MNG_DELTATYPE_REPLACE" ); break;\ + case MNG_DELTATYPE_BLOCKPIXELADD : MI( "MNG_DELTATYPE_BLOCKPIXELADD" ); break;\ + case MNG_DELTATYPE_BLOCKALPHAADD : MI( "MNG_DELTATYPE_BLOCKALPHAADD" ); break;\ + case MNG_DELTATYPE_BLOCKCOLORADD : MI( "MNG_DELTATYPE_BLOCKCOLORADD" ); break;\ + case MNG_DELTATYPE_BLOCKPIXELREPLACE : MI( "MNG_DELTATYPE_BLOCKPIXELREPLACE" ); break;\ + case MNG_DELTATYPE_BLOCKALPHAREPLACE : MI( "MNG_DELTATYPE_BLOCKALPHAREPLACE" ); break;\ + case MNG_DELTATYPE_BLOCKCOLORREPLACE : MI( "MNG_DELTATYPE_BLOCKCOLORREPLACE" ); break;\ + case MNG_DELTATYPE_NOCHANGE : MI( "MNG_DELTATYPE_NOCHANGE" ); break;\ + default : MI(UNKNOWN);\ + } + +mng_uint16 iObjectid; +mng_uint8 iImagetype; +mng_uint8 iDeltatype; +mng_uint32 iBlockwidth; +mng_uint32 iBlockheight; +mng_uint32 iBlockx; +mng_uint32 iBlocky; + + if( mng_getchunk_dhdr( hMNG, hChunk, + &iObjectid, &iImagetype, &iDeltatype, + &iBlockwidth, &iBlockheight, &iBlockx, &iBlocky ) != 0 ) + return false; + + IARG( "iObjectid", iObjectid ); + NL; IARG( "iImagetype", iImagetype ); MI_IMAGETYPE( iImagetype ); + NL; IARG( "iDeltatype", iDeltatype ); MI_DELTATYPE( iDeltatype ); + NL; IARG( "iBlockwidth", iBlockwidth ); + NL; IARG( "iBlockheight", iBlockheight ); + NL; IARG( "iBlockx", iBlockx ); + NL; IARG( "iBlocky", iBlocky ); + + return true; +# undef MI_IMAGETYPE +# undef MI_DELTATYPE +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_DISC( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +mng_uint32 iCount; +mng_uint16p pObjectids; + + if( mng_getchunk_disc( hMNG, hChunk, &iCount, &pObjectids ) != 0 ) + return false; + + IARG( "iCount", iCount ); + //pObjectids pObjectids + + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_DROP( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +mng_uint32 iCount; +mng_chunkidp pChunknames; + + if( mng_getchunk_drop( hMNG, hChunk, &iCount, &pChunknames ) != 0 ) + return false; + + IARG( "iCount", iCount ); + // pChunknamesp Chunknames // Iterate chunk names ? @todo@ + + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_ENDL( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +mng_uint8 iLevel; + + if( mng_getchunk_endl( hMNG, hChunk, &iLevel ) != 0 ) + return false; + + IARG( "iLevel", iLevel ); + + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_FRAM( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +# define MI_BOUNDARY( _i_ )\ + if( WantsMacroIds() )\ + switch( _i_ ) {\ + case MNG_BOUNDARY_ABSOLUTE : MI( "MNG_BOUNDARY_ABSOLUTE" ); break;\ + case MNG_BOUNDARY_RELATIVE : MI( "MNG_BOUNDARY_RELATIVE" ); break;\ + default : MI(UNKNOWN);\ + } + +# define MI_FRAMINGMODE( _i_ )\ + if( WantsMacroIds() )\ + switch( _i_ ) {\ + case MNG_FRAMINGMODE_NOCHANGE : MI( "MNG_FRAMINGMODE_NOCHANGE" ); break;\ + case MNG_FRAMINGMODE_1 : MI( "MNG_FRAMINGMODE_1" ); break;\ + case MNG_FRAMINGMODE_2 : MI( "MNG_FRAMINGMODE_2" ); break;\ + case MNG_FRAMINGMODE_3 : MI( "MNG_FRAMINGMODE_3" ); break;\ + case MNG_FRAMINGMODE_4 : MI( "MNG_FRAMINGMODE_4" ); break;\ + default : MI(UNKNOWN);\ + } + +# define MI_CHANGEDELAY( _i_ )\ + if( WantsMacroIds() )\ + switch( _i_ ) {\ + case MNG_CHANGEDELAY_NO : MI( "MNG_CHANGEDELAY_NO" ); break;\ + case MNG_CHANGEDELAY_NEXTSUBFRAME : MI( "MNG_CHANGEDELAY_NEXTSUBFRAME" ); break;\ + case MNG_CHANGEDELAY_DEFAULT : MI( "MNG_CHANGEDELAY_DEFAULT" ); break;\ + default : MI(UNKNOWN);\ + } + +# define MI_CHANGETIMOUT( _i_ )\ + if( WantsMacroIds() )\ + switch( _i_ ) {\ + case MNG_CHANGETIMOUT_NO : MI( "MNG_CHANGETIMOUT_NO" ); break;\ + case MNG_CHANGETIMOUT_DETERMINISTIC_1 : MI( "MNG_CHANGETIMOUT_DETERMINISTIC_1" ); break;\ + case MNG_CHANGETIMOUT_DETERMINISTIC_2 : MI( "MNG_CHANGETIMOUT_DETERMINISTIC_2" ); break;\ + case MNG_CHANGETIMOUT_DECODER_1 : MI( "MNG_CHANGETIMOUT_DECODER_1" ); break;\ + case MNG_CHANGETIMOUT_DECODER_2 : MI( "MNG_CHANGETIMOUT_DECODER_2" ); break;\ + case MNG_CHANGETIMOUT_USER_1 : MI( "MNG_CHANGETIMOUT_USER_1" ); break;\ + case MNG_CHANGETIMOUT_USER_2 : MI( "MNG_CHANGETIMOUT_USER_2" ); break;\ + case MNG_CHANGETIMOUT_EXTERNAL_1 : MI( "MNG_CHANGETIMOUT_EXTERNAL_1" ); break;\ + case MNG_CHANGETIMOUT_EXTERNAL_2 : MI( " MNG_CHANGETIMOUT_EXTERNAL_2" ); break;\ + default : MI(UNKNOWN);\ + } + +# define MI_CHANGECLIPPING( _i_ )\ + if( WantsMacroIds() )\ + switch( _i_ ) {\ + case MNG_CHANGECLIPPING_NO : MI( "MNG_CHANGECLIPPING_NO" ); break;\ + case MNG_CHANGECLIPPING_NEXTSUBFRAME : MI( "MNG_CHANGECLIPPING_NEXTSUBFRAME" ); break;\ + case MNG_CHANGECLIPPING_DEFAULT : MI( "MNG_CHANGECLIPPING_DEFAULT" ); break;\ + default : MI(UNKNOWN);\ + } + +# define MI_CHANGESYNCID( _i_ )\ + if( WantsMacroIds() )\ + switch( _i_ ) {\ + case MNG_CHANGESYNCID_NO : MI( "MNG_CHANGESYNCID_NO" ); break;\ + case MNG_CHANGESYNCID_NEXTSUBFRAME : MI( "MNG_CHANGESYNCID_NEXTSUBFRAME" ); break;\ + case MNG_CHANGESYNCID_DEFAULT : MI( "MNG_CHANGESYNCID_DEFAULT" ); break;\ + default : MI(UNKNOWN);\ + } + +mng_bool bEmpty; +mng_uint8 iMode; +mng_uint32 iNamesize; +mng_pchar zName; +mng_uint8 iChangedelay; +mng_uint8 iChangetimeout; +mng_uint8 iChangeclipping; +mng_uint8 iChangesyncid; +mng_uint32 iDelay; +mng_uint32 iTimeout; +mng_uint8 iBoundarytype; +mng_int32 iBoundaryl; +mng_int32 iBoundaryr; +mng_int32 iBoundaryt; +mng_int32 iBoundaryb; +mng_uint32 iCount; +mng_uint32p pSyncids; + + if( mng_getchunk_fram( hMNG, hChunk, + &bEmpty, &iMode, &iNamesize, &zName, + &iChangedelay, &iChangetimeout, &iChangeclipping, &iChangesyncid, + &iDelay, &iTimeout, + &iBoundarytype, &iBoundaryl, &iBoundaryr,&iBoundaryt, &iBoundaryb, + &iCount, &pSyncids ) != 0 ) + return false; + + BARG( "bEmpty", bEmpty ); + NL; IARG( "iMode", iMode ); MI_FRAMINGMODE( iMode ); + NL; IARG( "iNamesize", iNamesize ); + NL; ZARG( "zName", zName ); + NL; IARG( "iChangedelay", iChangedelay ); MI_CHANGEDELAY( iChangedelay ); + NL; IARG( "iChangetimeout", iChangetimeout ); MI_CHANGETIMOUT( iChangetimeout ); + NL; IARG( "iChangeclipping", iChangeclipping ); MI_CHANGECLIPPING( iChangeclipping ); + NL; IARG( "iChangesyncid", iChangesyncid ); MI_CHANGESYNCID( iChangesyncid ); + NL; IARG( "iDelay", iDelay ); + NL; IARG( "iTimeout", iTimeout ); + NL; IARG( "iBoundarytype", iBoundarytype ); MI_BOUNDARY( iBoundarytype ); + NL; IARG( "iBoundaryl", iBoundaryl ); + NL; IARG( "iBoundaryr", iBoundaryr ); + NL; IARG( "iBoundaryt", iBoundaryt ); + NL; IARG( "iBoundaryb", iBoundaryb ); + NL; IARG( "iCount", iCount ); + //pSyncids pSyncids @todo@ + + return true; +# undef MI_BOUNDARY +# undef MI_FRAMINGMODE +# undef MI_CHANGEDELAY +# undef MI_CHANGETIMOUT +# undef MI_CHANGECLIPPING +# undef MI_CHANGESYNCID +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_IDAT( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +mng_uint32 iRawlen; +mng_ptr pRawdata; + + if( mng_getchunk_idat( hMNG, hChunk, &iRawlen, &pRawdata ) != 0 ) + return false; + + IARG( "iRawlen", iRawlen ); + + if( WantsRawData() ) { + Byte *bp = (Byte*)pRawdata; + + NL; STR( TAB + "Rawdata : " ); + // show the first 16 bytes, as hex + for( int n = 0; n < 16; n +=1 ) + HEXSTR( (int)bp[n] ) + " "; + } + + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_IEND( mng_handle hMNG, mng_handle hChunk, String &as ) +{ + NL + TAB; STR( "End of Image." ); + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_IHDR( + mng_handle hMNG, mng_handle hChunk, String &as ) +{ +mng_uint32 iWidth; +mng_uint32 iHeight; +mng_uint8 iBitdepth; +mng_uint8 iColortype; +mng_uint8 iCompression; +mng_uint8 iFilter; +mng_uint8 iInterlace; + + if( mng_getchunk_ihdr( hMNG, hChunk, &iWidth, &iHeight, &iBitdepth, + &iColortype, &iCompression, &iFilter, &iInterlace ) != 0 ) + return false; + + IARG( "iWidth", iWidth ); + NL; IARG( "iHeight", iHeight ); + NL; IARG( "iBitdepth", iBitdepth ); MI_BITDEPTH( iBitdepth ); + NL; IARG( "iColortype", iColortype ); MI_COLORTYPE( iColortype ); + NL; IARG( "iCompression", iCompression ); MI_COMPRESSION_DEFLATE( iCompression ); + NL; IARG( "iFilter", iFilter ); MI_FILTER( iFilter ); + NL; IARG( "iInterlace", iInterlace ); MI_INTERLACE( iInterlace ); + + if( WantsComments() ) + { + NL; NL + TAB; + switch( iColortype ) { + case MNG_COLORTYPE_GRAY : + switch( iBitdepth ) { + case 1 : case 2 : case 4 : case 8 : case 16 : + STR( "Each pixel value is a greyscale level -" ); + } // inner switch + break; + case MNG_COLORTYPE_RGB : + switch( iBitdepth ) { + case 8 : case 16 : + STR( "Each pixel value is an R,G,B series -" ); + } // inner switch + break; + case MNG_COLORTYPE_INDEXED : + switch( iBitdepth ) { + case 1 : case 2 : case 4 : case 8 : + STR( "Each pixel value is a palette index -" ); + } // inner switch + break; + case MNG_COLORTYPE_GRAYA : + switch( iBitdepth ) { + case 8 : case 16 : + STR( "Each pixel value is a greyscale level, "\ + "followed by an Alpha channel level -" ); + } // inner switch + break; + case MNG_COLORTYPE_RGBA : + switch( iBitdepth ){ + case 8 : case 16: + STR( "Each pixel value is an R,G,B "\ + "series, followed by an Alpha channel level -" ); + } // inner switch + break; + } + STR( " " + String(iBitdepth) + " bits per pixel." ); + } + + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_JDAT( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +mng_uint32 iRawlen; +mng_ptr pRawdata = NULL; + + if( mng_getchunk_jdat( hMNG, hChunk, &iRawlen, &pRawdata ) != 0 ) + return false; + + IARG( "iRawlen", iRawlen ); + + if( WantsRawData() ) { + Byte *bp = (Byte*)pRawdata; + + NL; STR( TAB + "Rawdata : " ); + // show the first 16 bytes, as hex + for( int n = 0; n < 16; n +=1 ) + HEXSTR( (int)bp[n] ) + " "; + } + + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_JHDR( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +# define MI_JPEG_COLORTYPE( _i_ )\ + if( WantsMacroIds() ) {\ + switch( _i_ ){\ + case MNG_COLORTYPE_JPEGGRAY : MI( "MNG_COLORTYPE_JPEGGRAY");break;\ + case MNG_COLORTYPE_JPEGCOLOR : MI( "MNG_COLORTYPE_JPEGCOLOR");break;\ + case MNG_COLORTYPE_JPEGGRAYA : MI( "MNG_COLORTYPE_JPEGGRAYA");break;\ + case MNG_COLORTYPE_JPEGCOLORA : MI( "MNG_COLORTYPE_JPEGCOLORA");break;\ + default : MI(UNKNOWN);\ + }\ +} + +# define MI_JPEG_BITDEPTH( _i_ )\ + if( WantsMacroIds() ) {\ + switch( _i_ ){\ + case MNG_BITDEPTH_JPEG8 : MI("MNG_BITDEPTH_JPEG8");break;\ + case MNG_BITDEPTH_JPEG12 : MI("MNG_BITDEPTH_JPEG12");break;\ + case MNG_BITDEPTH_JPEG8AND12 : MI("MNG_BITDEPTH_JPEG8AND12");break;\ + default : MI(UNKNOWN);\ + }\ + } + +# define MI_JPEGCOMPRESSION( _i_ )\ + if( WantsMacroIds() ) {\ + if( iImagecompression == MNG_COMPRESSION_BASELINEJPEG ) {\ + MI( "MNG_COMPRESSION_BASELINEJPEG" );\ + } else { MI(ILLEGAL_VALUE); };\ + } + +# define MI_JPEGINTERLACE( _i_ )\ + if( WantsMacroIds() ) {\ + if( iImageinterlace == MNG_INTERLACE_SEQUENTIAL ) {\ + MI( "MNG_INTERLACE_SEQUENTIAL");\ + } else {\ + MI( "MNG_INTERLACE_PROGRESSIVE" );\ + }\ + } + +// NB alpha bitdepth is not png bitdepth because it can be 0 (zero) +# define MI_ALPHABITDEPTH( _i_ )\ + if( WantsMacroIds() )\ + switch( (_i_) ) {\ + case MNG_BITDEPTH_1 : MI( "MNG_BITDEPTH_1" ); break;\ + case MNG_BITDEPTH_2 : MI( "MNG_BITDEPTH_2" ); break;\ + case MNG_BITDEPTH_4 : MI( "MNG_BITDEPTH_4" ); break;\ + case MNG_BITDEPTH_8 : MI( "MNG_BITDEPTH_8" ); break;\ + case MNG_BITDEPTH_16 : MI( "MNG_BITDEPTH_16" ); break;\ + case 0 : break;\ + default : MI(ILLEGAL_VALUE);\ + } + +mng_uint32 iWidth; +mng_uint32 iHeight; +mng_uint8 iColortype; +mng_uint8 iImagesampledepth; +mng_uint8 iImagecompression; +mng_uint8 iImageinterlace; +mng_uint8 iAlphasampledepth; +mng_uint8 iAlphacompression; +mng_uint8 iAlphafilter; +mng_uint8 iAlphainterlace; + + if( mng_getchunk_jhdr( hMNG, hChunk, + &iWidth, &iHeight, &iColortype, &iImagesampledepth, + &iImagecompression, &iImageinterlace, + &iAlphasampledepth, &iAlphacompression, + &iAlphafilter, &iAlphainterlace ) != 0 ) + return false; + + IARG( "iWidth", iWidth ); + NL; IARG( "iHeight", iHeight ); + NL; IARG( "iColortype", iColortype ); MI_JPEG_COLORTYPE( iColortype ); + NL; IARG( "iImagesampledepth", iImagesampledepth ); MI_JPEG_BITDEPTH( iImagesampledepth ); + NL; IARG( "iImagecompression", iImagecompression ); MI_JPEGCOMPRESSION( iImagecompression ); + NL; IARG( "iImageinterlace", iImageinterlace ); MI_JPEGINTERLACE( iImageinterlace ); + NL; IARG( "iAlphasampledepth", iAlphasampledepth ); MI_ALPHABITDEPTH( iAlphasampledepth ); + NL; IARG( "iAlphacompression", iAlphacompression ); MI_COMPRESSION_DEFLATE( iAlphacompression ); + NL; IARG( "iAlphafilter", iAlphafilter ); MI_FILTER( iAlphafilter ); + NL; IARG( "iAlphainterlace", iAlphainterlace ); MI_INTERLACE( iAlphainterlace ); + + return true; +# undef MI_JPEG_COLORTYPE +# undef MI_JPEG_BITDEPTH +# undef MI_JPEGCOMPRESSION +# undef MI_JPEGINTERLACE +# undef MI_ALPHABITDEPTH +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_LOOP( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +# define MI_TERMINATION( _i_ )\ + if( WantsMacroIds() )\ + switch( (_i_) ) {\ + case MNG_TERMINATION_DECODER_NC :\ + MI( "MNG_TERMINATION_DECODER_NC" ); break;\ + case MNG_TERMINATION_USER_NC :\ + MI( "MNG_TERMINATION_USER_NC" ); break;\ + case MNG_TERMINATION_EXTERNAL_NC :\ + MI( "MNG_TERMINATION_EXTERNAL_NC" ); break;\ + case MNG_TERMINATION_DETERMINISTIC_NC :\ + MI( "MNG_TERMINATION_DETERMINISTIC_NC" ); break;\ + case MNG_TERMINATION_DECODER_C :\ + MI( "MNG_TERMINATION_DECODER_C" ); break;\ + case MNG_TERMINATION_USER_C :\ + MI( "MNG_TERMINATION_USER_C" ); break;\ + case MNG_TERMINATION_EXTERNAL_C :\ + MI( "MNG_TERMINATION_EXTERNAL_C" ); break;\ + case MNG_TERMINATION_DETERMINISTIC_C :\ + MI( "MNG_TERMINATION_DETERMINISTIC_C" ); break;\ + default : MI(UNKNOWN);\ + } +mng_uint8 iLevel; +mng_uint32 iRepeat; +mng_uint8 iTermination; +mng_uint32 iItermin; +mng_uint32 iItermax; +mng_uint32 iCount; +mng_uint32p pSignals; + + if( mng_getchunk_loop( hMNG, hChunk, + &iLevel, &iRepeat, &iTermination, + &iItermin, &iItermax, &iCount, &pSignals ) != 0 ) + return false; + + IARG( "iLevel", iLevel ); + NL; IARG( "iRepeat", iRepeat ); + NL; IARG( "iTermination", iTermination ); MI_TERMINATION( iTermination ); + NL; IARG( "iItermin", iItermin ); + NL; IARG( "iItermax", iItermax ); + NL; IARG( "iCount", iCount ); + //pSignals pSignals //@todo@ + + return true; +# undef MI_TERMINATION +} +//--------------------------------------------------------------------------- +/* +bool __fastcall TMainForm::Info_M?GN( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +mng_uint16 iFirstid; +mng_uint16 iLastid; +mng_uint16 iMethodX; +mng_uint16 iMX; +mng_uint16 iMY; +mng_uint16 iML; +mng_uint16 iMR; +mng_uint16 iMT; +mng_uint16 iMB; +mng_uint16 iMethodY; + + if( mng_getchunk_magn( hMNG, hChunk, + &iFirstid, &iLastid, + &iMethodX, &iMX, &iMY, &iML, &iMR, &iMT, &iMB, + &iMethodY ) != 0 ) + return false; + + IARG( "iFirstid", iFirstid ); + NL; IARG( "iLastid", iLastid ); + NL; IARG( "iMethodX", iMethodX ); + NL; IARG( "iMX", iMX ); + NL; IARG( "iMY", iMY ); + NL; IARG( "iML", iML ); + NL; IARG( "iMR", iMR ); + NL; IARG( "iMT", iMT ); + NL; IARG( "iMB", iMB ); + NL; IARG( "iMethodY", iMethodY ); + + return true; +} +*/ +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_MEND( mng_handle hMNG, mng_handle hChunk, String &as ) +{ + NL + TAB; STR( "End of Multiply Network Graphic." ); + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_MHDR( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +// NB "iSimplicity" is a bit field +# define MI_SIMPLICITY( _i_ )\ + if( WantsMacroIds() ) {\ + if( (_i_) & MNG_SIMPLICITY_VALID )\ + MI( "MNG_SIMPLICITY_VALID" );\ + if( (_i_) & MNG_SIMPLICITY_SIMPLEFEATURES )\ + MI( "MNG_SIMPLICITY_SIMPLEFEATURES" );\ + if( (_i_) & MNG_SIMPLICITY_COMPLEXFEATURES )\ + MI( "MNG_SIMPLICITY_COMPLEXFEATURES" );\ + if( (_i_) & MNG_SIMPLICITY_TRANSPARENCY )\ + MI( "MNG_SIMPLICITY_TRANSPARENCY" );\ + if( (_i_) & MNG_SIMPLICITY_JNG )\ + MI( "MNG_SIMPLICITY_JNG" );\ + if( (_i_) & MNG_SIMPLICITY_DELTAPNG )\ + MI( "MNG_SIMPLICITY_DELTAPNG" );\ + } + +mng_uint32 iWidth; +mng_uint32 iHeight; +mng_uint32 iTicks; +mng_uint32 iLayercount; +mng_uint32 iFramecount; +mng_uint32 iPlaytime; +mng_uint32 iSimplicity; + + if( mng_getchunk_mhdr( hMNG, hChunk, + &iWidth, &iHeight, &iTicks, + &iLayercount, &iFramecount, &iPlaytime, + &iSimplicity ) != 0 ) + return false; + + IARG( "iWidth", iWidth ); + NL; IARG( "iHeight", iHeight ); + NL; IARG( "iTicks", iTicks ); + NL; IARG( "iLayercount", iLayercount ); + NL; IARG( "iFramecount", iFramecount ); + NL; IARG( "iPlaytime", iPlaytime ); + NL; IARG( "iSimplicity", iSimplicity ); MI_SIMPLICITY( iSimplicity ); + + return true; +# undef MI_SIMPLICITY +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_MOVE( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +mng_uint16 iFirstid; +mng_uint16 iLastid; +mng_uint8 iMovetype; +mng_int32 iMovex; +mng_int32 iMovey; + + if( mng_getchunk_move( hMNG, hChunk, + &iFirstid, &iLastid, &iMovetype, &iMovex, &iMovey ) != 0 ) + return false; + + IARG( "iFirstid", iFirstid ); + NL; IARG( "iLastid", iLastid ); + NL; IARG( "iMovetype", iMovetype ); MI_LOCATION( iMovetype ); + NL; IARG( "iMovex", iMovex ); + NL; IARG( "iMovey", iMovey ); + + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_ORDR( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +mng_uint32 iCount; + + if( mng_getchunk_ordr( hMNG, hChunk, &iCount ) != 0 ) + return false; + + IARG( "iCount", iCount ); + + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_PAST( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +// PAST +# define MI_TARGET( _i_ )\ + if( WantsMacroIds() )\ + switch( _i_ ) {\ + case MNG_TARGET_ABSOLUTE : MI( "MNG_TARGET_ABSOLUTE" ); break;\ + case MNG_TARGET_RELATIVE_SAMEPAST : MI( "MNG_TARGET_RELATIVE_SAMEPAST" ); break;\ + case MNG_TARGET_RELATIVE_PREVPAST : MI( "MNG_TARGET_RELATIVE_PREVPAST" ); break;\ + default : MI(UNKNOWN);\ + } + +// COMPOSITE, ORIENTATION, OFFSET & BOUNDARY depend upon "iCount" +mng_uint16 iDestid; +mng_uint8 iTargettype; +mng_int32 iTargetx; +mng_int32 iTargety; +mng_uint32 iCount; + + if( mng_getchunk_past( hMNG, hChunk, + &iDestid, &iTargettype, &iTargetx, &iTargety, &iCount ) != 0 ) + return false; + + IARG( "iDestid", iDestid ); + NL; IARG( "iTargettype", iTargettype ); MI_TARGET( iTargettype ); + NL; IARG( "iTargetx", iTargetx ); + NL; IARG( "iTargety", iTargety ); + NL; IARG( "iCount", iCount ); + + return true; +# undef MI_TARGET +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_PLTE( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +mng_uint32 iCount; +mng_palette8 aPalette; +mng_uint32 iPalEntry; + + if( mng_getchunk_plte( hMNG, hChunk, &iCount, &aPalette ) != 0 ) + return false; + + IARG( "iCount", iCount ); + + if( WantsPaletteEntries() ) + { + iPalEntry = 0; + do{ + if( WantsRgbOrder() ) + { + as = as + nl + + TAB + "Palette entry [" + PadInt( iPalEntry ) + "]" + + TAB + "R(" + PadInt( aPalette[ iPalEntry ].iRed ) + ") " + + TAB + "G(" + PadInt( aPalette[ iPalEntry ].iGreen ) + ") " + + TAB + "B(" + PadInt( aPalette[ iPalEntry ].iBlue ) + ")"; + } + else + { + as = as + nl + + TAB + "Palette entry [" + PadInt( iPalEntry ) + "]" + + TAB + "B(" + PadInt( aPalette[ iPalEntry ].iBlue ) + ") " + + TAB + "G(" + PadInt( aPalette[ iPalEntry ].iGreen ) + ") " + + TAB + "R(" + PadInt( aPalette[ iPalEntry ].iRed ) + ")"; + + }; + iPalEntry += 1; + } while( iPalEntry < iCount ); + } + + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_PPLT( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +/* +# define MI_DELTATYPE( _i_ )\ + if( WantsMacroIds() )\ + switch( _i_ ) {\ + case MNG_DELTATYPE_REPLACERGB : MI( "MNG_DELTATYPE_REPLACERGB" ); break;\ + case MNG_DELTATYPE_DELTARGB : MI( "MNG_DELTATYPE_DELTARGB" ); break;\ + case MNG_DELTATYPE_REPLACEALPHA : MI( "MNG_DELTATYPE_REPLACEALPHA" ); break;\ + case MNG_DELTATYPE_DELTAALPHA : MI( "MNG_DELTATYPE_DELTAALPHA" ); break;\ + case MNG_DELTATYPE_REPLACERGBA : MI( "MNG_DELTATYPE_REPLACERGBA" ); break;\ + case MNG_DELTATYPE_DELTARGBA : MI( "MNG_DELTATYPE_DELTARGBA" ); break;\ + default : MI(UNKNOWN);\ + } +*/ +mng_uint32 iCount; + + if( mng_getchunk_pplt( hMNG, hChunk, &iCount ) != 0 ) + return false; + + IARG( "iCount", iCount ); + + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_PROM( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +# define MI_FILLMETHOD( _i_ )\ + if( WantsMacroIds() )\ + switch( _i_ ) {\ + case MNG_FILLMETHOD_LEFTBITREPLICATE : MI( "MNG_FILLMETHOD_LEFTBITREPLICATE" ); break;\ + case MNG_FILLMETHOD_ZEROFILL : MI( "MNG_FILLMETHOD_ZEROFILL" ); break;\ + default : MI(UNKNOWN);\ + } +mng_uint8 iColortype; +mng_uint8 iSampledepth; +mng_uint8 iFilltype; + + if( mng_getchunk_prom( hMNG, hChunk, + &iColortype, &iSampledepth, &iFilltype ) != 0 ) + return false; + + IARG( "iColortype", iColortype ); MI_COLORTYPE( iColortype ); + NL; IARG( "iSampledepth", iSampledepth ); MI_BITDEPTH( iSampledepth ); + NL; IARG( "iFilltype", iFilltype ); MI_FILLMETHOD( iFilltype ); + + return true; +# undef MI_FILLMETHOD +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_SAVE( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +# define MI_SAVEOFFSET( _i_ )\ + if( WantsMacroIds() )\ + switch( _i_ ) {\ + case MNG_SAVEOFFSET_4BYTE : MI( "MNG_SAVEOFFSET_4BYTE" ); break;\ + case MNG_SAVEOFFSET_8BYTE : MI( "MNG_SAVEOFFSET_8BYTE" ); break;\ + default : MI(UNKNOWN);\ + } + +/* +# define MI_SAVEENTRY( _i_ )\ + if( WantsMacroIds() )\ + switch( _i_ ) {\ + case MNG_SAVEENTRY_SEGMENTFULL : MI( "MNG_SAVEENTRY_SEGMENTFULL" ); break;\ + case MNG_SAVEENTRY_SEGMENT : MI( "MNG_SAVEENTRY_SEGMENT" ); break;\ + case MNG_SAVEENTRY_SUBFRAME : MI( "MNG_SAVEENTRY_SUBFRAME" ); break;\ + case MNG_SAVEENTRY_EXPORTEDIMAGE : MI( "MNG_SAVEENTRY_EXPORTEDIMAGE" ); break;\ + default : MI(UNKNOWN);\ + } +*/ +mng_bool bEmpty; +mng_uint8 iOffsettype; +mng_uint32 iCount; + + if( mng_getchunk_save( hMNG, hChunk, + &bEmpty, &iOffsettype, &iCount ) != 0 ) + return false; + + BARG( "bEmpty", bEmpty ); + NL; IARG( "iOffsettype", iOffsettype ); MI_SAVEOFFSET( iOffsettype ); + NL; IARG( "iCount", iCount ); + + return true; +# undef MI_SAVEOFFSET +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_SEEK( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +mng_uint32 iNamesize; +mng_pchar zName; + + if( mng_getchunk_seek( hMNG, hChunk, &iNamesize, &zName ) != 0 ) + return false; + + IARG( "iNamesize", iNamesize ); + NL; ZARG( "zName", zName ); + + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_SHOW( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +# define MI_SHOWMODE( _i_ )\ + if( WantsMacroIds() )\ + switch( _i_ ) {\ + case MNG_SHOWMODE_0 : MI( "MNG_SHOWMODE_0" ); break;\ + case MNG_SHOWMODE_1 : MI( "MNG_SHOWMODE_1" ); break;\ + case MNG_SHOWMODE_2 : MI( "MNG_SHOWMODE_2" ); break;\ + case MNG_SHOWMODE_3 : MI( "MNG_SHOWMODE_3" ); break;\ + case MNG_SHOWMODE_4 : MI( "MNG_SHOWMODE_4" ); break;\ + case MNG_SHOWMODE_5 : MI( "MNG_SHOWMODE_5" ); break;\ + case MNG_SHOWMODE_6 : MI( "MNG_SHOWMODE_6" ); break;\ + case MNG_SHOWMODE_7 : MI( "MNG_SHOWMODE_7" ); break;\ + default : MI(UNKNOWN);\ + } +mng_bool bEmpty; +mng_uint16 iFirstid; +mng_uint16 iLastid; +mng_uint8 iMode; + + if( mng_getchunk_show( hMNG, hChunk, + &bEmpty, &iFirstid, &iLastid, &iMode ) != 0 ) + return false; + + BARG( "bEmpty", bEmpty ); + NL; IARG( "iFirstid", iFirstid ); + NL; IARG( "iLastid", iLastid ); + NL; IARG( "iMode", iMode ); MI_SHOWMODE( iMode ); + + return true; +# undef MI_SHOWMODE +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_TERM( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +# define MI_TERMACTION( _i_ )\ + if( WantsMacroIds() )\ + switch( _i_ ) {\ + case MNG_TERMACTION_LASTFRAME : MI( "MNG_TERMACTION_LASTFRAME" ); break;\ + case MNG_TERMACTION_CLEAR : MI( "MNG_TERMACTION_CLEAR" ); break;\ + case MNG_TERMACTION_FIRSTFRAME : MI( "MNG_TERMACTION_FIRSTFRAME" ); break;\ + case MNG_TERMACTION_REPEAT : MI( "MNG_TERMACTION_REPEAT" ); break;\ + default : MI(UNKNOWN);\ + } + +# define MI_ITERACTION( _i_ )\ + if( WantsMacroIds() )\ + switch( _i_ ) {\ + case MNG_ITERACTION_LASTFRAME : MI( "MNG_ITERACTION_LASTFRAME" ); break;\ + case MNG_ITERACTION_CLEAR : MI( "MNG_ITERACTION_CLEAR" ); break;\ + case MNG_ITERACTION_FIRSTFRAME : MI( "MNG_ITERACTION_FIRSTFRAME" ); break;\ + default : MI(UNKNOWN);\ + } + +mng_uint8 iTermaction; +mng_uint8 iIteraction; +mng_uint32 iDelay; +mng_uint32 iItermax; + + if( mng_getchunk_term( hMNG, hChunk, + &iTermaction, &iIteraction, &iDelay, &iItermax ) != 0 ) + return false; + + IARG( "iTermaction", iTermaction ); MI_TERMACTION( iTermaction ); + NL; IARG( "iIteraction", iIteraction ); MI_ITERACTION( iIteraction ); + NL; IARG( "iDelay", iDelay ); + NL; IARG( "iItermax", iItermax ); + + return true; +# undef MI_TERMACTION +# undef MI_ITERACTION +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_bKGD( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +mng_bool bEmpty; +mng_uint8 iType; +mng_uint8 iIndex; +mng_uint16 iGray; +mng_uint16 iRed; +mng_uint16 iGreen; +mng_uint16 iBlue; + + if( mng_getchunk_bkgd( hMNG, hChunk, + &bEmpty, &iType, &iIndex, &iGray, + &iRed, &iGreen, &iBlue ) != 0 ) + return false; + + BARG( "bEmpty", bEmpty ); + NL; IARG( "iType", iType ); + NL; IARG( "iIndex", iIndex ); + NL; IARG( "iGray", iGray ); + NL; IARG( "iRed", iRed ); + NL; IARG( "iGreen", iGreen ); + NL; IARG( "iBlue", iBlue ); + + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_cHRM( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +mng_bool bEmpty; +mng_uint32 iWhitepointx; +mng_uint32 iWhitepointy; +mng_uint32 iRedx; +mng_uint32 iRedy; +mng_uint32 iGreenx; +mng_uint32 iGreeny; +mng_uint32 iBluex; +mng_uint32 iBluey; + + if( mng_getchunk_chrm( hMNG, hChunk, + &bEmpty, &iWhitepointx, &iWhitepointy, + &iRedx, &iRedy, + &iGreenx,&iGreeny, + &iBluex, &iBluey ) != 0 ) + return false; + + BARG( "bEmpty", bEmpty ); + NL; FARG( "iWhitepointx", iWhitepointx ); + FPCOM( String( (float)(iWhitepointx /FFACTOR) ) ); + NL; FARG( "iWhitepointy", iWhitepointy ); + FPCOM( String( (float)(iWhitepointy /FFACTOR) ) ); + + NL; FARG( "iRedx", iRedx ); + FPCOM( String( (float)(iRedx /FFACTOR) ) ); + NL; FARG( "iRedy", iRedy ); + FPCOM( String( (float)(iRedy /FFACTOR) ) ); + + NL; FARG( "iGreenx", iGreenx ); + FPCOM( String( (float)(iGreenx /FFACTOR) ) ); + NL; FARG( "iGreeny", iGreeny ); + FPCOM( String( (float)(iGreeny /FFACTOR) ) ); + + NL; FARG( "iBluex", iBluex ); + FPCOM( String( (float)(iBluex /FFACTOR) ) ); + NL; FARG( "iBluey", iBluey ); + FPCOM( String( (float)(iBluey /FFACTOR) ) ); + + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_eXPI( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +mng_uint16 iSnapshotid; +mng_uint32 iNamesize; +mng_pchar zName; + + if( mng_getchunk_expi( hMNG, hChunk, + &iSnapshotid, &iNamesize, &zName ) != 0 ) + return false; + + IARG( "iSnapshotid", iSnapshotid ); + NL; IARG( "iNamesize", iNamesize ); + NL; ZARG( "zName", zName ); + + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_fPRI( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +# define MI_PRIORITY( _i_ )\ + if( WantsMacroIds() )\ + switch( _i_ ) {\ + case MNG_PRIORITY_ABSOLUTE : MI( "MNG_PRIORITY_ABSOLUTE" ); break;\ + case MNG_PRIORITY_RELATIVE : MI( "MNG_PRIORITY_RELATIVE" ); break;\ + default : MI(UNKNOWN);\ + } +mng_uint8 iDeltatype; +mng_uint8 iPriority; + + if( mng_getchunk_fpri( hMNG, hChunk, &iDeltatype, &iPriority ) != 0 ) + return false; + + IARG( "iDeltatype", iDeltatype ); MI_PRIORITY( iDeltatype ); + NL; IARG( "iPriority", iPriority ); + + return true; +# undef MI_PRIORITY +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_gAMA( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +mng_bool bEmpty; +mng_uint32 iGamma; + + if( mng_getchunk_gama( hMNG, hChunk, &bEmpty, &iGamma ) != 0 ) + return false; + + BARG( "bEmpty", bEmpty ); + NL; FARG( "iGamma", iGamma ); + FPCOM( String( (float)(iGamma /FFACTOR) ) ); + + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_hIST( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +mng_uint32 iEntrycount; +mng_uint16arr aEntries; + + if( mng_getchunk_hist( hMNG, hChunk, &iEntrycount, &aEntries ) != 0 ) + return false; + + + IARG( "iEntrycount", iEntrycount ); + //aEntries aEntries @todo@ + + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_iCCP( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +mng_bool bEmpty; +mng_uint32 iNamesize; +mng_pchar zName; +mng_uint8 iCompression; +mng_uint32 iProfilesize; +mng_ptr pProfile; + + if( mng_getchunk_iccp( hMNG, hChunk, + &bEmpty, &iNamesize, &zName, &iCompression, + &iProfilesize,&pProfile ) != 0 ) + return false; + + BARG( "bEmpty", bEmpty ); + NL; IARG( "iNamesize", iNamesize ); + NL; ZARG( "Name", zName ); + NL; IARG( "iCompression", iCompression ); MI_COMPRESSION_DEFLATE( iCompression ); + NL; IARG( "iProfilesize", iProfilesize ); +// "pProfile " + String( pProfile ); + + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_iTXt( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +#define MI_ITXT_FLAG( _i_ )\ + if( WantsMacroIds() ) {\ + if( (_i_) == MNG_FLAG_UNCOMPRESSED ) {\ + MI( "MNG_FLAG_UNCOMPRESSED" )\ + } else if( (_i_) == MNG_FLAG_COMPRESSED ) {\ + MI( "MNG_FLAG_COMPRESSED" );\ + }\ + } +mng_uint32 iKeywordsize; +mng_pchar zKeyword; +mng_uint8 iCompressionflag; +mng_uint8 iCompressionmethod; +mng_uint32 iLanguagesize; +mng_pchar zLanguage; +mng_uint32 iTranslationsize; +mng_pchar zTranslation; +mng_uint32 iTextsize; +mng_pchar zText; + + if( mng_getchunk_itxt( hMNG, hChunk, + &iKeywordsize, &zKeyword, + &iCompressionflag, &iCompressionmethod, + &iLanguagesize, &zLanguage, + &iTranslationsize, &zTranslation, + &iTextsize, &zText ) != 0 ) + return false; + + IARG( "iKeywordsize", iKeywordsize ); + NL; ZARG( "zKeyword", zKeyword ); + NL; IARG( "iCompressionflag", iCompressionflag ); MI_ITXT_FLAG( iCompressionflag ); + NL; IARG( "iCompressionmethod", iCompressionmethod ); MI_COMPRESSION_DEFLATE( iCompressionflag ); + NL; IARG( "iLanguagesize", iLanguagesize ); + NL; ZARG( "zLanguage", zLanguage ); + NL; IARG( "iTranslationsize", iTranslationsize ); + NL; ZARG( "zTranslation", zTranslation ); + NL; IARG( "iTextsize", iTextsize ); + NL; ZARG( "zText", zText ); + + return true; +# undef MI_ITXT_FLAG +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_nEED( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +mng_uint32 iKeywordssize; +mng_pchar zKeywords; + + if( mng_getchunk_need( hMNG, hChunk, &iKeywordssize, &zKeywords ) != 0) + return false; + + IARG( "iKeywordssize", iKeywordssize ); + NL; IARG( "zKeywords", zKeywords ); + + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_pHYg( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +mng_bool bEmpty; +mng_uint32 iSizex; +mng_uint32 iSizey; +mng_uint8 iUnit; + + if( mng_getchunk_phyg( hMNG, hChunk, + &bEmpty, &iSizex, &iSizey, &iUnit ) != 0 ) + return false; + + BARG( "bEmpty", bEmpty ); + NL; IARG( "iSizex", iSizex ); + NL; IARG( "iSizey", iSizey ); + NL; IARG( "iUnit", iUnit ); MI_UNITS( iUnit ); + + if( iUnit ) { + PCOM("X/Y pixels per unit" ); + } else { + if( iSizex == iSizey ) { + PCOM( "Square pixels" ); + } + } + + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_pHYs( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +mng_bool bEmpty; +mng_uint32 iSizex; +mng_uint32 iSizey; +mng_uint8 iUnit; + + if( mng_getchunk_phys( hMNG, hChunk, + &bEmpty, &iSizex, &iSizey, &iUnit ) != 0 ) + return false; + + BARG( "bEmpty", bEmpty ); + NL; IARG( "iSizex", iSizex ); + NL; IARG( "iSizey", iSizey ); + NL; IARG( "iUnit", iUnit ); MI_UNITS( iUnit ); + + if( iUnit ) { + PCOM("X/Y pixels per unit" ); + } else { + if( iSizex == iSizey ) { + PCOM( "Square pixels" ); + } + } + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_sBIT( mng_handle hMNG, mng_handle hChunk, String &as ) +{ // tested with cs3* cs5* cs8* +mng_bool bEmpty; +mng_uint8 iType; +mng_uint8arr4 aBits; + + if( mng_getchunk_sbit( hMNG, hChunk, &bEmpty, &iType, &aBits ) != 0 ) + return false; + + BARG( "bEmpty", bEmpty ); + NL; IARG( "iType", iType ); + //aBits aBits @todo@ + + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_sPLT( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +mng_bool bEmpty; +mng_uint32 iNamesize; +mng_pchar zName; +mng_uint8 iSampledepth; +mng_uint32 iEntrycount; +mng_ptr pEntries; + + if( mng_getchunk_splt( hMNG, hChunk, + &bEmpty, &iNamesize, &zName, + &iSampledepth, &iEntrycount, &pEntries ) != 0 ) + return false; + + BARG( "bEmpty", bEmpty ); + NL; IARG( "iNamesize", iNamesize ); + NL; ZARG( "zName", zName ); + NL; IARG( "iSampledepth", iSampledepth ); MI_BITDEPTH( iSampledepth ); + NL; IARG( "iEntrycount", iEntrycount ); + //pEntries pEntries @todo@ + + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_sRGB( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +# define MI_RENDERINGINTENT( _i_ )\ + if( WantsMacroIds() ) {\ + switch( (_i_) ) {\ + case MNG_INTENT_PERCEPTUAL :\ + MI( "MNG_INTENT_PERCEPTUAL" ); break;\ + case MNG_INTENT_RELATIVECOLORIMETRIC :\ + MI( "MNG_INTENT_RELATIVECOLORIMETRIC" );break;\ + case MNG_INTENT_SATURATION :\ + MI( "MNG_INTENT_SATURATION" ); break;\ + case MNG_INTENT_ABSOLUTECOLORIMETRIC :\ + MI( "MNG_INTENT_ABSOLUTECOLORIMETRIC" );break;\ + }\ + } +mng_bool bEmpty; +mng_uint8 iRenderingintent; + + if( mng_getchunk_srgb( hMNG, hChunk,&bEmpty, &iRenderingintent ) != 0) + return false; + + BARG( "bEmpty", bEmpty ); + NL; IARG( "iRenderingintent", iRenderingintent ); + MI_RENDERINGINTENT( iRenderingintent ); + + return true; +# undef MI_RENDERINGINTENT +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_tEXt( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +mng_uint32 iKeywordsize; +mng_pchar zKeyword; +mng_uint32 iTextsize; +mng_pchar zText; + + if( mng_getchunk_text( hMNG, hChunk, + &iKeywordsize, &zKeyword, &iTextsize, &zText ) != 0 ) + return false; + + IARG( "iKeywordsize", iKeywordsize ); + NL; ZARG( "zKeyword", zKeyword ); + NL; IARG( "iTextsize", iTextsize ); + NL; ZARG( "zText", zText ); + + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_tIME( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +mng_uint16 iYear; +mng_uint8 iMonth; +mng_uint8 iDay; +mng_uint8 iHour; +mng_uint8 iMinute; +mng_uint8 iSecond; + + if( mng_getchunk_time( hMNG, hChunk, + &iYear, &iMonth, &iDay, &iHour, &iMinute, &iSecond ) != 0 ) + return false; + + IARG( "iYear", iYear ); + NL; IARG( "iMonth", iMonth ); + NL; IARG( "iDay", iDay ); + NL; IARG( "iHour", iHour ); + NL; IARG( "iMinute", iMinute ); + NL; IARG( "iSecond", iSecond ); + // Do not do help line here - may confuse international readers ! + + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_tRNS( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +mng_bool bEmpty; +mng_bool bGlobal; +mng_uint8 iType; +mng_uint32 iCount; +mng_uint8arr aAlphas; +mng_uint16 iGray; +mng_uint16 iRed; +mng_uint16 iGreen; +mng_uint16 iBlue; +mng_uint32 iRawlen; +mng_uint8arr aRawdata; + + if( mng_getchunk_trns( hMNG, hChunk, + &bEmpty, &bGlobal, &iType, &iCount, + &aAlphas, + &iGray, &iRed, &iGreen, &iBlue, + &iRawlen, + &aRawdata ) != 0 ) + return false; + + BARG( "bEmpty", bEmpty ); + NL; BARG( "bGlobal", bGlobal ); + NL; IARG( "iType", iType ); + NL; IARG( "iCount", iCount ); +//aAlphas aAlphas @todo@ + NL; IARG( "iGray", iGray ); + NL; IARG( "iRed", iRed ); + NL; IARG( "iGreen", iGreen ); + NL; IARG( "iBlue", iBlue ); + NL; IARG( "iRawlen", iRawlen ); +//aRawdata aRawdata @todo@ + + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_zTXt( mng_handle hMNG, mng_handle hChunk, String &as ) +{ +mng_uint32 iKeywordsize; +mng_pchar zKeyword; +mng_uint8 iCompression; +mng_uint32 iTextsize; +mng_pchar zText; + + if( mng_getchunk_ztxt( hMNG, hChunk, + &iKeywordsize, &zKeyword, &iCompression, &iTextsize, &zText ) != 0 ) + return false; + + IARG( "iKeywordsize", iKeywordsize ); + NL; ZARG( "zKeyword", zKeyword ); + NL; IARG( "iCompression", iCompression ); MI_COMPRESSION_DEFLATE( iCompression ); + NL; IARG( "iTextsize", iTextsize ); + NL; ZARG( "zText", zText ); + + return true; +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::Info_Unknown( mng_handle hMNG, mng_handle hChunk, String &as ) +{ + NL + TAB; STR( "Unknown Chunk" ); + NL + TAB; STR( nl + TAB + "(See help tab for a list of unsupported chunks)!" ); + + return true; +} +//--------------------------------------------------------------------------- + + + diff --git a/Engine/lib/lmng/contrib/bcb/mngdump/Help.cpp b/Engine/lib/lmng/contrib/bcb/mngdump/Help.cpp new file mode 100644 index 000000000..c9e1ab509 --- /dev/null +++ b/Engine/lib/lmng/contrib/bcb/mngdump/Help.cpp @@ -0,0 +1,23 @@ +//--------------------------------------------------------------------------- +// Help.cpp : Help pane text +//--------------------------------------------------------------------------- +#include "Help.h" + +// NB : If more text is to go in here don't forget to enlarge min/max +extern char *_szHelp = { +"\nA MNG developers tool to display the chunk data of MNG/JNG/PNG files."\ +"\n" +"\nMngDump - Version 1.0 - September 2000"\ +"\n"\ +"\n NOTE - This program comes with NO warranties whatsoever."\ +"\n"\ +"\nInstruction for use ... press the folder button and load a file !"\ +"\n"\ +"\nThe following chunks are (currently) unsupported :"\ +"\nIJNG, IPNG, JSEP, MaGN, MAGN, oFFs, pCAL, sCAL"\ +"\n"\ +"\nThe program will be periodically updated, inline with libmng's growth."\ +"\nPlease report any bugs, suggestions, etc to the maintainer." +}; +//--------------------------------------------------------------------------- + diff --git a/Engine/lib/lmng/contrib/bcb/mngdump/Help.h b/Engine/lib/lmng/contrib/bcb/mngdump/Help.h new file mode 100644 index 000000000..736d562d2 --- /dev/null +++ b/Engine/lib/lmng/contrib/bcb/mngdump/Help.h @@ -0,0 +1,6 @@ +//--------------------------------------------------------------------------- +#ifndef HelpH +#define HelpH +//--------------------------------------------------------------------------- +extern char *_szHelp; +#endif diff --git a/Engine/lib/lmng/contrib/bcb/mngdump/MNGDUMP.BPR b/Engine/lib/lmng/contrib/bcb/mngdump/MNGDUMP.BPR new file mode 100644 index 000000000..67845d0b0 --- /dev/null +++ b/Engine/lib/lmng/contrib/bcb/mngdump/MNGDUMP.BPR @@ -0,0 +1,195 @@ +# --------------------------------------------------------------------------- +!if !$d(BCB) +BCB = $(MAKEDIR)\.. +!endif + +# --------------------------------------------------------------------------- +# IDE SECTION +# --------------------------------------------------------------------------- +# The following section of the project makefile is managed by the BCB IDE. +# It is recommended to use the IDE to change any of the values in this +# section. +# --------------------------------------------------------------------------- + +VERSION = BCB.03 +# --------------------------------------------------------------------------- +PROJECT = MNGDUMP.exe +OBJFILES = MNGDUMP.obj Main.obj Chunks.obj About.obj Callback.obj Help.obj +RESFILES = MngDump.res +DEFFILE = +RESDEPEN = $(RESFILES) Main.dfm +LIBFILES = ..\..\..\bcb\win32dll\libmng.lib +LIBRARIES = VCL35.lib +SPARELIBS = VCL35.lib +PACKAGES = VCLX35.bpi VCL35.bpi VCLDB35.bpi VCLDBX35.bpi bcbsmp35.bpi dclocx35.bpi \ + QRPT35.bpi +# --------------------------------------------------------------------------- +PATHCPP = .; +PATHASM = .; +PATHPAS = .; +PATHRC = .; +DEBUGLIBPATH = $(BCB)\lib\debug +RELEASELIBPATH = $(BCB)\lib\release +# --------------------------------------------------------------------------- +CFLAG1 = -O2 -w -Ve -k- -vi -c -b- -w-par -w-inl -Vx -tW +CFLAG2 = -I..\win32dll;$(BCB)\include;$(BCB)\include\vcl;..\..\..\libmng;..\..\..\zlib;..\..\..\jpgsrc6b +CFLAG3 = -Tkh30000 -6 +PFLAGS = -U..\win32dll;$(BCB)\lib\obj;$(BCB)\lib;$(RELEASELIBPATH) \ + -I..\win32dll;$(BCB)\include;$(BCB)\include\vcl;..\..\..\libmng;..\..\..\zlib;..\..\..\jpgsrc6b \ + -$L- -$D- -v -JPHN -M +RFLAGS = -i..\win32dll;$(BCB)\include;$(BCB)\include\vcl;..\..\..\libmng;..\..\..\zlib;..\..\..\jpgsrc6b +AFLAGS = /i$(BCB)\include /i$(BCB)\include\vcl /i..\..\..\libmng /i..\..\..\zlib \ + /i..\..\..\jpgsrc6b /mx /w2 /zd /dMNG_SUPPORT_READ /dMNG_ACCESS_CHUNKS \ + /dMNG_STORE_CHUNKS /dMNG_NO_CMS /dMNG_USE_DLL /dHAVE_BOOLEAN +LFLAGS = -L..\win32dll;$(BCB)\lib\obj;$(BCB)\lib;$(RELEASELIBPATH) -aa -Tpe -x -Gn +IFLAGS = +# --------------------------------------------------------------------------- +ALLOBJ = c0w32.obj sysinit.obj $(OBJFILES) +ALLRES = $(RESFILES) +ALLLIB = $(LIBFILES) $(LIBRARIES) import32.lib cp32mt.lib +# --------------------------------------------------------------------------- +!ifdef IDEOPTIONS + +[Version Info] +IncludeVerInfo=1 +AutoIncBuild=0 +MajorVer=1 +MinorVer=0 +Release=0 +Build=0 +Debug=0 +PreRelease=1 +Special=0 +Private=0 +DLL=0 +Locale=2057 +CodePage=1252 + +[Version Info Keys] +CompanyName=RDS +FileDescription=MngDump +FileVersion=1.0.0.0 +InternalName= +LegalCopyright=Copyright (c) 2000 G.Juyn +LegalTrademarks= +OriginalFilename=MngTree +ProductName= +ProductVersion=1.0.0.0 +Comments=Author Andy Protano - September 2000 + +[HistoryLists\hlIncludePath] +Count=10 +Item0=$(BCB)\include;$(BCB)\include\vcl;..\..\..\libmng;..\..\..\zlib;..\..\..\jpgsrc6b +Item1=$(BCB)\include;$(BCB)\include\vcl;..\..;..\..\..\zlib;..\..\..\jpgsrc6b +Item2=..\mngtree;..\..\delphi\mngview;$(BCB)\include;$(BCB)\include\vcl;..\..\..\libmng;..\..\..\zlib;..\..\..\jpgsrc6b +Item3=..\mngtree;..\..\delphi\mngview;$(BCB)\include;$(BCB)\include\vcl;..\..\..\libmng;..\..\..\zlib;..\..\..\libjpeg +Item4=..\..\delphi\mngview;$(BCB)\include;$(BCB)\include\vcl;..\..;..\..\..\zlib;..\..\..\libjpeg +Item5=$(BCB)\include;$(BCB)\include\vcl;..\..;..\..\..\zlib;..\..\..\libjpeg +Item6=$(BCB)\include;$(BCB)\include\vcl;..\..;..\..\zlib;..\..\libjpeg +Item7=$(BCB)\include;$(BCB)\include\vcl;..\.. +Item8=$(BCB)\include;$(BCB)\include\vcl;.. +Item9=$(BCB)\include;$(BCB)\include\vcl + +[HistoryLists\hlLibraryPath] +Count=4 +Item0=$(BCB)\lib\obj;$(BCB)\lib +Item1=..\mngtree;$(BCB)\lib\obj;$(BCB)\lib +Item2=..\mngtree;..\..\delphi\mngview;$(BCB)\lib\obj;$(BCB)\lib +Item3=..\..\delphi\mngview;$(BCB)\lib\obj;$(BCB)\lib + +[HistoryLists\hlDebugSourcePath] +Count=1 +Item0=$(BCB)\source\vcl + +[HistoryLists\hlConditionals] +Count=6 +Item0=MNG_SUPPORT_READ;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_NO_CMS;MNG_USE_DLL;HAVE_BOOLEAN +Item1=_RTLDLL;MNG_SUPPORT_READ;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_NO_CMS;MNG_USE_DLL;HAVE_BOOLEAN +Item2=_RTLDLL;MNG_SUPPORT_READ;MNG_ACCESS_CHUNKS;MNG_STORE_CHUNKS;MNG_NO_CMS;MNG_USE_DLL +Item3=_RTLDLL;HAVE_BOOLEAN +Item4=_RTLDLL +Item5=_RTLDLL;USEPACKAGES + +[Debugging] +DebugSourceDirs=$(BCB)\source\vcl + +[Parameters] +RunParams= +HostApplication= + +!endif + +# --------------------------------------------------------------------------- +# MAKE SECTION +# --------------------------------------------------------------------------- +# This section of the project file is not used by the BCB IDE. It is for +# the benefit of building from the command-line using the MAKE utility. +# --------------------------------------------------------------------------- + +.autodepend +# --------------------------------------------------------------------------- +!if !$d(BCC32) +BCC32 = bcc32 +!endif + +!if !$d(DCC32) +DCC32 = dcc32 +!endif + +!if !$d(TASM32) +TASM32 = tasm32 +!endif + +!if !$d(LINKER) +LINKER = ilink32 +!endif + +!if !$d(BRCC32) +BRCC32 = brcc32 +!endif +# --------------------------------------------------------------------------- +!if $d(PATHCPP) +.PATH.CPP = $(PATHCPP) +.PATH.C = $(PATHCPP) +!endif + +!if $d(PATHPAS) +.PATH.PAS = $(PATHPAS) +!endif + +!if $d(PATHASM) +.PATH.ASM = $(PATHASM) +!endif + +!if $d(PATHRC) +.PATH.RC = $(PATHRC) +!endif +# --------------------------------------------------------------------------- +$(PROJECT): $(OBJFILES) $(RESDEPEN) $(DEFFILE) + $(BCB)\BIN\$(LINKER) @&&! + $(LFLAGS) + + $(ALLOBJ), + + $(PROJECT),, + + $(ALLLIB), + + $(DEFFILE), + + $(ALLRES) +! +# --------------------------------------------------------------------------- +.pas.hpp: + $(BCB)\BIN\$(DCC32) $(PFLAGS) {$< } + +.pas.obj: + $(BCB)\BIN\$(DCC32) $(PFLAGS) {$< } + +.cpp.obj: + $(BCB)\BIN\$(BCC32) $(CFLAG1) $(CFLAG2) $(CFLAG3) -n$(@D) {$< } + +.c.obj: + $(BCB)\BIN\$(BCC32) $(CFLAG1) $(CFLAG2) $(CFLAG3) -n$(@D) {$< } + +.asm.obj: + $(BCB)\BIN\$(TASM32) $(AFLAGS) $<, $@ + +.rc.res: + $(BCB)\BIN\$(BRCC32) $(RFLAGS) -fo$@ $< +# --------------------------------------------------------------------------- diff --git a/Engine/lib/lmng/contrib/bcb/mngdump/MNGDUMP.RES b/Engine/lib/lmng/contrib/bcb/mngdump/MNGDUMP.RES new file mode 100644 index 000000000..f912f29b3 Binary files /dev/null and b/Engine/lib/lmng/contrib/bcb/mngdump/MNGDUMP.RES differ diff --git a/Engine/lib/lmng/contrib/bcb/mngdump/MNGDUMP.cpp b/Engine/lib/lmng/contrib/bcb/mngdump/MNGDUMP.cpp new file mode 100644 index 000000000..883f6d4b2 --- /dev/null +++ b/Engine/lib/lmng/contrib/bcb/mngdump/MNGDUMP.cpp @@ -0,0 +1,27 @@ +//--------------------------------------------------------------------------- +#include +#pragma hdrstop +USERES("MngDump.res"); +USEFORM("Main.cpp", MainForm); +USEUNIT("Chunks.cpp"); +USEUNIT("About.cpp"); +USEUNIT("Callback.cpp"); +USEUNIT("Help.cpp"); +USELIB("..\..\..\bcb\win32dll\libmng.lib"); +//--------------------------------------------------------------------------- +WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) +{ + try + { + Application->Initialize(); + Application->Title = "MngDump"; + Application->CreateForm(__classid(TMainForm), &MainForm); + Application->Run(); + } + catch (Exception &exception) + { + Application->ShowException(&exception); + } + return 0; +} +//--------------------------------------------------------------------------- diff --git a/Engine/lib/lmng/contrib/bcb/mngdump/Main.cpp b/Engine/lib/lmng/contrib/bcb/mngdump/Main.cpp new file mode 100644 index 000000000..af7a916d2 --- /dev/null +++ b/Engine/lib/lmng/contrib/bcb/mngdump/Main.cpp @@ -0,0 +1,793 @@ +//--------------------------------------------------------------------------- +#include +#include +#include +#pragma hdrstop +//--------------------------------------------------------------------------- +#pragma package(smart_init) +#pragma resource "*.dfm" +#include "Main.h" +TMainForm *MainForm; +//--------------------------------------------------------------------------- +#include "About.h" +#include "Help.h" + +# define PAGE_CHUNKS 1 +# define PAGE_REPORT 2 +# define PAGE_OPTIONS 3 +# define PAGE_ABOUT 4 +# define PAGE_HELP 5 + +/* ************************************************************************** */ +/* * * */ +/* * MngDump is based on Gerard Juyn's MngTree * */ +/* * * */ +/* * COPYRIGHT NOTICE: * */ +/* * * */ +/* * Copyright (c) 2000 Andy Protano * */ +/* * Gerard Juyn (gerard@libmng.com) * */ +/* * [You may insert additional notices after this sentence if you modify * */ +/* * this source] * */ +/* * * */ +/* * For the purposes of this copyright and license, "Contributing Authors" * */ +/* * is defined as the following set of individuals: * */ +/* * * */ +/* * Gerard Juyn (gerard@libmng.com) * */ +/* * Andy Protano (a.a.protano@care4free.net) * */ +/* * * */ +/* * This program is supplied "AS IS". The Contributing Authors * */ +/* * disclaim all warranties, expressed or implied, including, without * */ +/* * limitation, the warranties of merchantability and of fitness for any * */ +/* * purpose. The Contributing Authors assume no liability for direct, * */ +/* * indirect, incidental, special, exemplary, or consequential damages, * */ +/* * which may result from the use of the MNG Library, even if advised of * */ +/* * the possibility of such damage. * */ +/* * * */ +/* * Permission is hereby granted to use, copy, modify, and distribute this * */ +/* * source code, or portions hereof, for any purpose, without fee, subject * */ +/* * to the following restrictions: * */ +/* * * */ +/* * 1. The origin of this source code must not be misrepresented; * */ +/* * you must not claim that you wrote the original software. * */ +/* * * */ +/* * 2. Altered versions must be plainly marked as such and must not be * */ +/* * misrepresented as being the original source. * */ +/* * * */ +/* * 3. This Copyright notice may not be removed or altered from any source * */ +/* * or altered source distribution. * */ +/* * * */ +/* * The Contributing Authors specifically permit, without fee, and * */ +/* * encourage the use of this source code as a component to supporting * */ +/* * the MNG and JNG file format in commercial products. If you use this * */ +/* * source code in a product, acknowledgment would be highly appreciated. * */ +/* * * */ +/* ************************************************************************** */ + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +__fastcall TMainForm::TMainForm(TComponent* Owner) + : TForm(Owner) +{ +} +//--------------------------------------------------------------------------- +__fastcall TMainForm::~TMainForm( void ) +{ +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::OnMinMax(TMessage& Msg) +{ + ((POINT far *)Msg.LParam)[3].x = 500; + ((POINT far *)Msg.LParam)[3].y = 450; + + TForm::Dispatch(&Msg); +} +//--------------------------------------------------------------------------- +// MessageBox Methods +// ---------------------------------------------------------------------- +int __fastcall TMainForm::MessageBox( String &as, UINT flags ) +{ + return ::MessageBox( Handle, as.c_str(), Application->Title.c_str(), flags ); +} +// ---------------------------------------------------------------------- +void __fastcall TMainForm::MsgBoxOk( String as ) +{ + (void)MessageBox( as, MB_ICONINFORMATION ); +} +//------------------------------------------------------------------------- +int __fastcall TMainForm::MsgBoxYN( String as ) +{ + return MessageBox( as, MB_YESNO + MB_ICONQUESTION + MB_DEFBUTTON1 ); +} +//------------------------------------------------------------------------- +int __fastcall TMainForm::MsgBoxYNC( String as ) +{ + return MessageBox( as, MB_YESNOCANCEL | MB_ICONQUESTION ); +} +//------------------------------------------------------------------------- +void __fastcall TMainForm::MsgBoxStop( String as ) +{ + (void)MessageBox( as, MB_ICONSTOP ); +} +//------------------------------------------------------------------------- +void __fastcall TMainForm::MsgBoxInfo( String as ) +{ + (void)MessageBox( as, MB_OK | MB_ICONINFORMATION ); +} +//------------------------------------------------------------------------- +void __fastcall TMainForm::MsgBoxWarn( String as ) +{ + (void)MessageBox( as, MB_OK | MB_ICONWARNING ); +} +//------------------------------------------------------------------------- +void __fastcall TMainForm::FormCreate(TObject *Sender) +{ + // For when the application name changes - coz i'll forget ! + asAppName = Application->Title; + // And while we've got it set the caption + Caption = Application->Title; + + // Load About Screen + LabelAbout->Caption = _szAbout; + // Load Help Screen + LabelHelp->Caption = _szHelp; + + fd = NULL; + strList = new TStringList(); + + asTab = " ";// default tab size 2 + RadioGroupTabSize->ItemIndex = 1; // range is zero based + + // Start with the help pane + PageControl1->ActivePage = tsHelp; + + mnuWordWrap->Checked = false; + // This call will TOGGLE and set word wrap for all the applicables + // editors and enable/disable the Horz scroll bar. + mnuWordWrapClick( this ); // So start with false to get to true; + + // Clear things from design time settings + StaticTextStatus->Caption = ""; + //strList->Clear(); constructor dodid that + pnlChunks->Caption = ""; + ListBoxChunks->Clear(); + RichEditChunks->Clear(); + RichEditReport->Clear(); + ProgressBar1->Min = 0; + ProgressBar1->Position = 0; + SetChunkCount( 0 ); + + // Set default states for switches + cbPaletteEntries->Checked = false; + cbRGBOrder->Checked = true; + cbRawData->Checked = true; + cbComments->Checked = true; + cbMacroIdentifier->Checked = true; + cbBool->Checked = true; +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::FormDestroy(TObject *Sender) +{ + delete strList; +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::mnuExitClick(TObject *Sender) +{ + Close(); +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::mnuFileOpenClick(TObject *Sender) +{ + if( !OpenDialog1->Execute() ) + return; + + Application->ProcessMessages(); // Refresh the screen + LoadFile(); + +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::mnuReloadClick(TObject *Sender) +{ + // Only reload - if we have something to reload + if( OpenDialog1->FileName.Length() ) + LoadFile(); + else + MsgBoxStop( "Nothing to reload yet !" ); +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::mnuUndoClick(TObject *Sender) +{ + if( RichEditReport->Lines->Count ) // Do we have something in there ? + RichEditReport->Undo(); +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::mnuCutClick(TObject *Sender) +{ + if( RichEditReport->Lines->Count ) // Do we have something in there ? + RichEditReport->CutToClipboard(); +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::mnuCopyClick(TObject *Sender) +{ + if( RichEditReport->Lines->Count ) // Do we have something in there ? + RichEditReport->CopyToClipboard(); +} +//------------------------------------------------------------------------- +void __fastcall TMainForm::sbtnCopyChunkClick(TObject *Sender) +{ + if( RichEditChunks->Lines->Count ) // Do we have something in there ? + { + // Copy chunk data from Chunks pane to clipboard + RichEditChunks->SelectAll(); + RichEditChunks->CopyToClipboard(); + RichEditChunks->SelLength = 0; + } +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::mnuPasteClick(TObject *Sender) +{ + if( RichEditReport->Lines->Count ) // Do we have something in there ? + RichEditReport->PasteFromClipboard(); +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::mnuSelectAllClick(TObject *Sender) +{ + if( RichEditReport->Lines->Count ) // Do we have something in there ? + RichEditReport->SelectAll(); +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::mnuWordWrapClick(TObject *Sender) +{ +bool bWrap; + + mnuWordWrap->Checked = !mnuWordWrap->Checked; + bWrap = mnuWordWrap->Checked; + + RichEditChunks->WordWrap = bWrap; + RichEditReport->WordWrap = bWrap; + + if( bWrap ) { // if Word wrap on then no need for horizontal scrollbar + RichEditChunks->ScrollBars = ssVertical; + RichEditReport->ScrollBars = ssVertical; + } else { // Horizontal scrollbar required (auto) + RichEditChunks->ScrollBars = ssBoth; + RichEditReport->ScrollBars = ssBoth; + } +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::mnuSetFontClick(TObject *Sender) +{ + if( FontDialog1->Execute() ) + { + RichEditChunks->Font = FontDialog1->Font; + RichEditReport->Font = FontDialog1->Font; + } +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::FontDialog1Apply(TObject *Sender, HWND Wnd) +{ + RichEditChunks->Font = FontDialog1->Font; + RichEditReport->Font = FontDialog1->Font; +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::mnuFindClick(TObject *Sender) +{ + if( RichEditReport->Lines->Count == 0 ) + { + MsgBoxStop( "Find what ?."); + return; + } + + FindDialog->Execute(); +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::FindDialogFind(TObject *Sender) +{ +TSearchTypes st; +TFindDialog *fd; +int newpos; + + if( RichEditReport->Lines->Count == 0 ) + { + MsgBoxStop( "Find what ?."); + return; + } + + fd = dynamic_cast( Sender ); + + if( fd->Options.Contains( frMatchCase ) ) + st << stMatchCase; + if( fd->Options.Contains( frWholeWord ) ) + st << stWholeWord; + + if( RichEditReport->SelLength ) + RichEditReport->SelStart += 1; + + newpos = RichEditReport->FindText( fd->FindText, + RichEditReport->SelStart, RichEditReport->Text.Length(), st ); + + if( newpos != -1 ) { + RichEditReport->SelStart = newpos; + RichEditReport->SelLength = fd->FindText.Length(); + } else { + MsgBoxInfo( "End of document reached." ); + RichEditReport->SelStart = 0; + } +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::mnuFindNextClick(TObject *Sender) +{ + if( RichEditReport->Lines->Count == 0 ) + { + MsgBoxStop( "Find what ?."); + return; + } + + FindDialogFind( FindDialog ); +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::mnuPrintClick(TObject *Sender) +{ + if( RichEditReport->Lines->Count == 0 ) + { + MsgBoxStop( "I can't find anything to print !."); + return; + } + + RichEditReport->Print( ExtractFileName( OpenDialog1->FileName ) ); + +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::mnuPrintSetupClick(TObject *Sender) +{ + PrinterSetupDialog->Execute(); +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::mnuSaveClick(TObject *Sender) +{ + if( RichEditReport->Lines->Count == 0 ) + { + MsgBoxStop( "I can't find anything to save !."); + return; + } + + // Grab the graphic's filename and change to "txt" extension + { + char szFile[ MAXFILE ]; + char szExt[ MAXEXT ]; + char szDest[ MAXPATH ]; + String as; + + fnsplit( OpenDialog1->FileName.c_str(), NULL, NULL, szFile, szExt ); + + memset( szDest, 0, MAXPATH ); + strcat( szDest, szFile ); + strcat( szDest, "\." ); + strcat( szDest, "txt" ); + as = szDest; + + // Initially create a text file of the same name as the graphic + // but with "txt" extension + SaveDialog1->FileName = as; + } + + if( !SaveDialog1->Execute() ) + return; // Given up hey ? + + RichEditReport->Lines->SaveToFile( SaveDialog1->FileName ); +} +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +void __fastcall TMainForm::LoadFile( void ) +{ +int bStatus; + + // Can we open the file ? + if( (fd = fopen( OpenDialog1->FileName.c_str(), "rb") ) == NULL ) + { + MsgBoxStop( "Cannot open input file\n\n" + OpenDialog1->FileName ); + return; + } + + pnlChunks->Caption = ""; + strList->Clear(); + ListBoxChunks->Clear(); + RichEditChunks->Clear(); + RichEditReport->Clear(); + + ProgressBar1->Min = 0; + ProgressBar1->Position = 0; + SetChunkCount( 0 ); + + RichEditReport->Lines->Add( "" ); + RichEditReport->Lines->Add( "Chunk contents of file :" ); + RichEditReport->Lines->Add( OpenDialog1->FileName ); + RichEditReport->Lines->Add( "" ); + + // Make report pane visible - prove we are working and not locked up + PageControl1->ActivePage = tsReport; + +try { + StaticTextStatus->Caption = "Working ... please wait"; + StaticTextStatus->Update(); + bStatus = DumpTree(); // true indicates success +} +__finally { + ProgressBar1->Position = 0; + StaticTextStatus->Caption = "Ok"; + fclose( fd ); + fd = NULL; +} + + if( !bStatus ) // We have an error + { + pnlChunks->Caption = ""; + strList->Clear(); + ListBoxChunks->Clear(); + RichEditChunks->Clear(); + + RichEditReport->Lines->Add(""); + RichEditReport->Lines->Add("An error occurred while reading the file."); + + } else { // Read file and chunks without any problems + + // Set Report Panels caption + pnlChunks->Caption = "Chunk 1 of " + String( ListBoxChunks->Items->Count ); + + // Set Listbox to first item + ListBoxChunks->ItemIndex = 0; + + // Put gleeful message in report pane + RichEditChunks->Lines->Add( "" ); + RichEditChunks->Lines->Add( + "No error's found when reading file ... " ); + RichEditChunks->Lines->Add( + "Click the list box to display the data for each chunk."); + + // Place cursor at top of editor + RichEditChunks->SelStart = 0; + RichEditChunks->SelLength = 0; + + // Place cursor at top of editor + RichEditReport->SelStart = 0; + RichEditReport->SelLength = 0; + + // Lock richedits - so an immediate undo does not make data vanish ! + RichEditReport->Modified = false; + // @ap@ Borland should fix this ! (it don't work) + } + +} +//--------------------------------------------------------------------------- +bool __fastcall TMainForm::DumpTree( void ) +{ +mng_handle hMNG; + + // let's initialize the library + hMNG = mng_initialize( (mng_ptr)this, Alloc, Free, NULL ); + + if( !hMNG ) // did that work out ? + { + MNGError( hMNG, "Cannot initialize libmng." ); + mng_cleanup( &hMNG ); // Always cleanup the library + MsgBoxStop( "Bye!" ); + Application->Terminate(); // Exit now + } + + // setup callbacks + if( (mng_setcb_openstream ( hMNG, OpenStream ) != 0) || + (mng_setcb_closestream ( hMNG, CloseStream ) != 0) || + (mng_setcb_processheader( hMNG,ProcessHeader ) != 0) || + (mng_setcb_readdata ( hMNG, FileReadData ) != 0) + ) + { + MNGError( hMNG, "Cannot set callbacks for libmng."); + mng_cleanup( &hMNG ); // Always cleanup the library + MsgBoxStop( "Bye!" ); + Application->Terminate(); // Exit now + } + else + { + // read the file into memory + if( mng_read( hMNG ) != 0 ) + { + // Because we read the whole file in first, + // here is where bad input files first choke ! + MNGError( hMNG, "Cannot read the file." ); + mng_cleanup( &hMNG ); // Always cleanup the library + return false; + } + else + { + // run through the chunk list + if( mng_iterate_chunks( hMNG, 0, IterateChunks ) != 0 ) + { + MNGError( hMNG, "Error Getting Chunk info!" ); + mng_cleanup( &hMNG ); // Always cleanup the library + return false; // Errors may occur with bad chunk data + } + } + } + + mng_cleanup( &hMNG ); // Always cleanup the library + + return true; +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::MNGError( mng_handle hMNG, String SHMsg ) +{ +// get extended info +mng_uint32 iErrorcode; +mng_uint8 iSeverity; +mng_chunkid iChunkname; +mng_uint32 iChunkseq; +mng_int32 iExtra1; +mng_int32 iExtra2; +mng_pchar zErrortext; +char szFormatStr[ 256 ]; + + iErrorcode = mng_getlasterror( hMNG, &iSeverity, &iChunkname, + &iChunkseq, &iExtra1, &iExtra2, + &zErrortext); + + wsprintf( szFormatStr, + "Error = %d; Severity = %d; Chunknr = %d; Extra1 = %d", + (int)iErrorcode, (int)iSeverity, (int)iChunkseq, (int)iExtra1 + ); + + MsgBoxStop( SHMsg + "\n\n" + String( zErrortext ) + "\n\n" + String( szFormatStr ) ); +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::ListBoxChunksClick(TObject *Sender) +{ +// When the Chunks listbox is clicked ... +// display strings from strlist in the RichEditChunks window. +int iIndex = ListBoxChunks->ItemIndex; + + if( !ListBoxChunks->Items->Count ) + return; + + // Update panel to reflect the selection : "Chunk N of NChunks" + pnlChunks->Caption = "Chunk " + String( iIndex + 1) + + " of " + String( ListBoxChunks->Items->Count ); + + // Reset Chunks windows ... + RichEditChunks->Clear(); + RichEditChunks->Lines->Add( "" ); + // ... Chunks header string : "Chunk N : XXXX" (XXXX == Chunk name) + RichEditChunks->Lines->Add( "Chunk " + String( iIndex + 1 ) + " : " + + ListBoxChunks->Items->Strings[ iIndex ] ); + // ... Seperator line + //RichEditChunks->Lines->Add(""); + + // ... add the data from the stringlist + { + String as = strList->Strings[ iIndex ] + 1; + String z; + int n =1; + do{ + if( as[n] == '\n' ) + { + RichEditChunks->Lines->Add( z ); + z = ""; + } + else + z = z + as[n]; + n+=1; + } while( n < as.Length() ); + RichEditChunks->Lines->Add( z ); + } + + // ... Seperator line + RichEditChunks->Lines->Add(""); + + // Place cursor at top of editor + RichEditChunks->SelStart = 0; + RichEditChunks->SelLength = 0; + +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::EventShowPage(TObject *Sender) +{ + + switch( ((TTabSheet *)Sender)->Tag ) { + case PAGE_REPORT : + mnuPrint->Enabled = true; + mnuEdit->Enabled = true; + mnuUndo->Enabled = true; + mnuSave->Enabled = true; + sbtnSave->Enabled = true; + sbtnUndo->Enabled = true; + mnuCut->Enabled = true; + sbtnCut->Enabled = true; + mnuCopy->Enabled = true; + sbtnCopy->Enabled = true; + mnuPaste->Enabled = true; + sbtnPaste->Enabled = true; + mnuSearch->Enabled = true; + sbtnPrint->Enabled = true; + break; + //case PAGE_CHUNKS : + //case PAGE_OPTIONS : + ///case PAGE_ABOUT : + //case PAGE_HELP : + default : + mnuPrint->Enabled = false; + mnuEdit->Enabled = false; + // no need to enable/disable menu commands as none will be available + mnuSave->Enabled = false; + sbtnSave->Enabled = false; + sbtnUndo->Enabled = false; + sbtnCut->Enabled = false; + sbtnCopy->Enabled = false; + sbtnPaste->Enabled = false; + mnuSearch->Enabled = false; + sbtnPrint->Enabled = false; + } + +} +//--------------------------------------------------------------------------- +void __fastcall TMainForm::RadioGroupTabSizeClick(TObject *Sender) +{ + switch( RadioGroupTabSize->ItemIndex ) { + case 0 : asTab = " "; break; + case 1 : asTab = " "; break; + case 2 : asTab = " "; break; + case 3 : asTab = " "; break; + case 4 : asTab = " "; break; + case 5 : asTab = " "; break; + } +} +//--------------------------------------------------------------------------- +String __fastcall TMainForm::PadInt( int iInt, int iWidth ) +{ + +char szFmtStr[ 24 ]; +char szInt[ 24 ]; // Should be wide enough ! +String as; + wsprintf( szFmtStr,"%%%d\d\0",iWidth ); //"%[iWidth]d" + as = szFmtStr; + wsprintf( szInt, szFmtStr, iInt ); + + return String( szInt ); +} +//--------------------------------------------------------------------------- +mng_bool __fastcall TMainForm::ShowChunk( + mng_handle hMNG, mng_handle hChunk, mng_chunkid iChunktype ) +{ +String asDataText; + + // Fill asDataText with string data including newline's ... + // this is added to string list for each chunk + // and also added to the tail end of "Report" + + // NOTE : + // Return True to continue processing. + // If mng_getchunk_xxxx fails just return false to the + // caller "(bool) myiterchunk" which inturn will then return false + // to "(mng_retcode) mng_iterate_chunks(...)" which will then + // give us the correct error code. + // In other words DON'T check for errors in "(bool) myiterchunk" ! + + switch( iChunktype ) { + case MNG_UINT_BACK : + if( !Info_BACK( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_BASI : + if( !Info_BASI( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_CLIP : + if( !Info_CLIP( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_CLON : + if( !Info_CLON( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_DBYK : // untested @ap@ + if( !Info_DBYK( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_DEFI : + if( !Info_DEFI( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_DHDR : + if( !Info_DHDR( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_DISC : // untested @ap@ + if( !Info_DISC( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_DROP : // untested @ap@ + if( !Info_DROP( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_ENDL : + if( !Info_ENDL( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_FRAM : + if( !Info_FRAM( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_IDAT : + if( !Info_IDAT( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_IEND : + if( !Info_IEND( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_IHDR : + if( !Info_IHDR( hMNG, hChunk, asDataText ) ) return false; break; +#define MNG_UINT_IJNG 0x494a4e47L // Function AWOL @ap@ +#define MNG_UINT_IPNG 0x49504e47L // Function AWOL @ap@ + case MNG_UINT_JDAT : + if( !Info_JDAT( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_JHDR : + if( !Info_JHDR( hMNG, hChunk, asDataText ) ) return false; break; +#define MNG_UINT_JSEP 0x4a534550L // Function AWOL @ap@ + case MNG_UINT_LOOP : + if( !Info_LOOP( hMNG, hChunk, asDataText ) ) return false; break; +#define MNG_UINT_MaGN 0x4d61474eL // which one "mng_getchunk_magn" ? + case MNG_UINT_MEND : + if( !Info_MEND( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_MHDR : + if( !Info_MHDR( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_MOVE : + if( !Info_MOVE( hMNG, hChunk, asDataText ) ) return false; break; +#define MNG_UINT_MaGN 0x4d61474eL // which one "mng_getchunk_magn" ? + case MNG_UINT_ORDR : + if( !Info_ORDR( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_PAST : + if( !Info_PAST( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_PLTE : + if( !Info_PLTE( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_PPLT : + if( !Info_PPLT( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_PROM : + if( !Info_PROM( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_SAVE : + if( !Info_SAVE( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_SEEK : + if( !Info_SEEK( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_SHOW : + if( !Info_SHOW( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_TERM : + if( !Info_TERM( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_bKGD : + if( !Info_bKGD( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_cHRM : + if( !Info_cHRM( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_eXPI : + if( !Info_eXPI( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_fPRI : + if( !Info_fPRI( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_gAMA : + if( !Info_gAMA( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_hIST : + if( !Info_hIST( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_iCCP : + if( !Info_iCCP( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_iTXt : // untested @ap@ + if( !Info_iTXt( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_nEED : + if( !Info_nEED( hMNG, hChunk, asDataText ) ) return false; break; +#define MNG_UINT_oFFs 0x6f464673L // Function AWOL @ap@ +#define MNG_UINT_pCAL 0x7043414cL // Function AWOL @ap@ + case MNG_UINT_pHYg : // untested @ap@ + if( !Info_pHYg( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_pHYs : + if( !Info_pHYs( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_sBIT : + if( !Info_sBIT( hMNG, hChunk, asDataText ) ) return false; break; +#define MNG_UINT_sCAL 0x7343414cL // Function AWOL @ap@ + case MNG_UINT_sPLT : // untested @ap@ + if( !Info_sPLT( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_sRGB : + if( !Info_sRGB( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_tEXt : + if( !Info_tEXt( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_tIME : + if( !Info_tIME( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_tRNS : + if( !Info_tRNS( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_zTXt : + if( !Info_zTXt( hMNG, hChunk, asDataText ) ) return false; break; + case MNG_UINT_HUH : + default : // this will catch unknown chunks - Huh ! + if( !Info_Unknown( hMNG, hChunk, asDataText ) ) return false; break; + } // end of switch + //------------------------------------------------- + + // Add this chunk's string to our string list + strList->Insert( GetChunkCount(), asDataText ); + + // Now's the time to bump chunk count + IncChunkCount(); + + // Add this chunk's string to the Report + MainForm->RichEditReport->Lines->Add( asDataText ); + + return true; +} +//--------------------------------------------------------------------------- + diff --git a/Engine/lib/lmng/contrib/bcb/mngdump/Main.dfm b/Engine/lib/lmng/contrib/bcb/mngdump/Main.dfm new file mode 100644 index 000000000..3187217d0 Binary files /dev/null and b/Engine/lib/lmng/contrib/bcb/mngdump/Main.dfm differ diff --git a/Engine/lib/lmng/contrib/bcb/mngdump/Main.h b/Engine/lib/lmng/contrib/bcb/mngdump/Main.h new file mode 100644 index 000000000..09df9b688 --- /dev/null +++ b/Engine/lib/lmng/contrib/bcb/mngdump/Main.h @@ -0,0 +1,296 @@ +//--------------------------------------------------------------------------- +#ifndef MainH +#define MainH +//--------------------------------------------------------------------------- +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//--------------------------------------------------------------------------- +// These MUST be defined before we include "Libmng.h +//# define MNG_SUPPORT_READ +//# define MNG_ACCESS_CHUNKS +//# define MNG_STORE_CHUNKS +//# define MNG_NO_CMS +# define MNG_USE_DLL +# define MNG_SKIP_ZLIB +# define MNG_SKIP_LCMS +# define MNG_SKIP_IJG6B + +#include "../../../libmng.h" +//--------------------------------------------------------------------------- +class TMainForm : public TForm +{ +__published: // IDE-managed Components + TMainMenu *MainMenu1; + TMenuItem *mnuFile; + TMenuItem *mnuOpen; + TMenuItem *mnuReload; + TMenuItem *mnuSave; + TMenuItem *N1; + TMenuItem *mnuPrint; + TMenuItem *mnuPrintSetup; + TMenuItem *N2; + TMenuItem *mnuExit; + TMenuItem *mnuEdit; + TMenuItem *mnuUndo; + TMenuItem *N3; + TMenuItem *mnuCut; + TMenuItem *mnuCopy; + TMenuItem *mnuPaste; + TMenuItem *N4; + TMenuItem *mnuSelectAll; + TMenuItem *N5; + TMenuItem *mnuSetFont; + TMenuItem *mnuWordWrap; + TMenuItem *mnuSearch; + TMenuItem *mnuFind; + TMenuItem *mnuFindNext; + TPanel *Panel1; + TSpeedButton *sbtnOpen; + TSpeedButton *sbtnReload; + TSpeedButton *sbtnSave; + TSpeedButton *sbtnPrint; + TSpeedButton *sbtnPrintSetup; + TSpeedButton *sbtnUndo; + TSpeedButton *sbtnCut; + TSpeedButton *sbtnCopy; + TSpeedButton *sbtnPaste; + TOpenDialog *OpenDialog1; + TSaveDialog *SaveDialog1; + TPrintDialog *PrintDialog; + TPrinterSetupDialog *PrinterSetupDialog; + TFontDialog *FontDialog1; + TFindDialog *FindDialog; + TPageControl *PageControl1; + TTabSheet *tsChunks; + TTabSheet *tsReport; + TPanel *pnlChunks; + TListBox *ListBoxChunks; + TSpeedButton *sbtnCopyChunk; + TRichEdit *RichEditChunks; + TRichEdit *RichEditReport; + TPanel *PanelStatusBar; + TProgressBar *ProgressBar1; + TTabSheet *tsOptions; + TTabSheet *tsAbout; + TLabel *LabelAbout; + TTabSheet *tsHelp; + TLabel *LabelHelp; + TCheckBox *cbBool; + TCheckBox *cbMacroIdentifier; + TCheckBox *cbRawData; + TCheckBox *cbRGBOrder; + TCheckBox *cbPaletteEntries; + TCheckBox *cbComments; + TRadioGroup *RadioGroupTabSize; + TStaticText *StaticTextStatus; + TStaticText *StaticText1; + void __fastcall mnuFileOpenClick(TObject *Sender); + void __fastcall FormCreate(TObject *Sender); + void __fastcall ListBoxChunksClick(TObject *Sender); + void __fastcall mnuReloadClick(TObject *Sender); + void __fastcall mnuExitClick(TObject *Sender); + void __fastcall FormDestroy(TObject *Sender); + void __fastcall mnuUndoClick(TObject *Sender); + void __fastcall mnuCutClick(TObject *Sender); + void __fastcall mnuCopyClick(TObject *Sender); + void __fastcall mnuPasteClick(TObject *Sender); + void __fastcall mnuSelectAllClick(TObject *Sender); + void __fastcall mnuWordWrapClick(TObject *Sender); + void __fastcall mnuSetFontClick(TObject *Sender); + void __fastcall FontDialog1Apply(TObject *Sender, HWND Wnd); + void __fastcall mnuFindClick(TObject *Sender); + void __fastcall FindDialogFind(TObject *Sender); + void __fastcall mnuFindNextClick(TObject *Sender); + void __fastcall sbtnCopyChunkClick(TObject *Sender); + void __fastcall mnuPrintClick(TObject *Sender); + void __fastcall mnuPrintSetupClick(TObject *Sender); + void __fastcall mnuSaveClick(TObject *Sender); + void __fastcall EventShowPage(TObject *Sender); + void __fastcall RadioGroupTabSizeClick(TObject *Sender); +protected : + void virtual __fastcall OnMinMax(TMessage& Msg); + BEGIN_MESSAGE_MAP // to limit minimum form size for about/help panes + MESSAGE_HANDLER( WM_GETMINMAXINFO, TMessage, OnMinMax ) + END_MESSAGE_MAP(TForm) +private: // User declarations + FILE *fd; + int iChunkCount; // To link stringList to listbox + String asTab; // Number of spaces to use as tab stop +public: // User declarations + // Constructor + __fastcall TMainForm(TComponent* Owner); + // Destructor + __fastcall ~TMainForm( void ); + + // ------------------------------------------------------------------ + // Callbacks ... as static member functions + // ------------------------------------------------------------------ + static mng_ptr __stdcall Alloc( mng_size_t iSize ); + static void __stdcall Free( mng_ptr pPtr, mng_size_t iSize ); + static mng_bool __stdcall FileReadData( mng_handle hMNG, + mng_ptr pBuf, + mng_uint32 iSize, + mng_uint32 *iRead ); + static mng_bool __stdcall ProcessHeader( mng_handle hHandle, + mng_uint32 iWidth, + mng_uint32 iHeight ); + static mng_bool __stdcall OpenStream( mng_handle hMNG ); + static mng_bool __stdcall CloseStream( mng_handle hMNG ); + static mng_bool __stdcall IterateChunks( mng_handle hMNG, + mng_handle hChunk, + mng_chunkid iChunktype, + mng_uint32 iChunkseq ); + + // ------------------------------------------------------------------ + //public data members + // ------------------------------------------------------------------ + // Associates a string, strList[n], with a chunk in the listbox[N] + TStringList *strList; + String asAppName; // pinch Application->Title at startup + + // ------------------------------------------------------------------ + // MessageBox functions + // ------------------------------------------------------------------ + int __fastcall MessageBox( String &as, UINT flags ); + void __fastcall MsgBoxOk( String as ); + int __fastcall MsgBoxYN( String as ); + int __fastcall MsgBoxYNC( String as ); + void __fastcall MsgBoxStop( String as ); + void __fastcall MsgBoxInfo( String as ); + void __fastcall MsgBoxWarn( String as ); + + // ------------------------------------------------------------------ + // Just to isolate teh "FILE *fd" variable from possible change. + inline FILE* __fastcall GetFd( void ) + { return fd; }; + + // ------------------------------------------------------------------ + // Options + // ------------------------------------------------------------------ + inline bool _fastcall WantsComments( void ) + { return cbComments->Checked; }; + + inline bool _fastcall WantsPaletteEntries( void ) + { return cbPaletteEntries->Checked; }; + + inline bool _fastcall WantsRgbOrder( void ) + { return cbRGBOrder->Checked; }; + + inline bool _fastcall WantsRawData( void ) + { return cbRawData->Checked; }; + + inline bool _fastcall WantsMacroIds( void ) + { return cbMacroIdentifier->Checked; }; + + inline bool _fastcall WantsBool( void ) + { return cbBool->Checked; }; + + // ------------------------------------------------------------------ + // Chunk count stuff + // ------------------------------------------------------------------ + inline int __fastcall IncChunkCount( void ) + { return iChunkCount += 1; }; + inline int __fastcall SetChunkCount( int anInt ) + { iChunkCount = anInt; }; + inline int __fastcall GetChunkCount( void ) + { return iChunkCount; }; + + // ------------------------------------------------------------------ + // Open a file, initialise things, and then calls "Dumptree(...)" + void __fastcall LoadFile( void ); + + // All LibMng calls involving the library handle are made from here : + // Initialize the library + // Setup callbacks + // Read the file into memory + // Run through the chunk list (mng_iterate_chunks) + // Cleanup the library + // Show Report page, all done. (then explode into the starry heavens!) + bool __fastcall DumpTree( void ); + + // Handle library errors gracefully. + void __fastcall MNGError( mng_handle hMNG, String SHMsg ); + + // Uesd with Palette entries - to right align int's to N chars width + String __fastcall PadInt( int iInt, int iWidth = 3 ); // default = 3 + + // A long switch that calls Info_XXXX's to assemble the chunks string + mng_bool __fastcall ShowChunk( mng_handle hMNG, + mng_handle hChunk, + mng_chunkid iChunktype ); + + // ------------------------------------------------------------------ + // The following functions are to assemble a string for each chunk. + // The function name "Info_????" denotes which chunk it handles + // Definitions can be found in "Chunks.cpp" + // ------------------------------------------------------------------ + bool __fastcall Info_BACK( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_BASI( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_CLIP( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_CLON( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_DBYK( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_DEFI( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_DHDR( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_DISC( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_DROP( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_ENDL( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_FRAM( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_IDAT( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_IEND( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_IHDR( mng_handle hMNG, mng_handle hChunk, String &as ); + // MNG_UINT_IJNG - Function missing @ap@ + // MNG_UINT_IPNG - Function missing @ap@ + bool __fastcall Info_JDAT( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_JHDR( mng_handle hMNG, mng_handle hChunk, String &as ); + // MNG_UINT_JSEP - Function missing @ap@ + bool __fastcall Info_LOOP( mng_handle hMNG, mng_handle hChunk, String &as ); + // MAGN ? MNG_UINT_MAGN @ap@ + bool __fastcall Info_MEND( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_MHDR( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_MOVE( mng_handle hMNG, mng_handle hChunk, String &as ); + // MaGN ? MNG_UINT_MaGN @ap@ + bool __fastcall Info_ORDR( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_PAST( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_PLTE( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_PPLT( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_PROM( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_SAVE( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_SEEK( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_SHOW( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_TERM( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_bKGD( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_cHRM( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_eXPI( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_fPRI( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_gAMA( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_hIST( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_iCCP( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_iTXt( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_nEED( mng_handle hMNG, mng_handle hChunk, String &as ); + // MNG_UINT_oFFs - Function missing @ap@ + // MNG_UINT_pCAL - Function missing @ap@ + bool __fastcall Info_pHYg( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_pHYs( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_sBIT( mng_handle hMNG, mng_handle hChunk, String &as ); + // MNG_UINT_sCAL - Function missing @ap@ + bool __fastcall Info_sPLT( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_sRGB( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_tEXt( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_tIME( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_tRNS( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_zTXt( mng_handle hMNG, mng_handle hChunk, String &as ); + bool __fastcall Info_Unknown( mng_handle hMNG, mng_handle hChunk, String &as ); + +}; +//--------------------------------------------------------------------------- +extern PACKAGE TMainForm *MainForm; +//--------------------------------------------------------------------------- +#endif diff --git a/Engine/lib/lmng/contrib/delphi/libmng.pas b/Engine/lib/lmng/contrib/delphi/libmng.pas new file mode 100644 index 000000000..1fabb2331 --- /dev/null +++ b/Engine/lib/lmng/contrib/delphi/libmng.pas @@ -0,0 +1,1811 @@ +unit libmng; + +{****************************************************************************} +{* *} +{* COPYRIGHT NOTICE: *} +{* *} +{* Copyright (c) 2000-2004 Gerard Juyn (gerard@libmng.com) *} +{* [You may insert additional notices after this sentence if you modify *} +{* this source] *} +{* *} +{* For the purposes of this copyright and license, "Contributing Authors" *} +{* is defined as the following set of individuals: *} +{* *} +{* Gerard Juyn *} +{* (hopefully some more to come...) *} +{* *} +{* The MNG Library is supplied "AS IS". The Contributing Authors *} +{* disclaim all warranties, expressed or implied, including, without *} +{* limitation, the warranties of merchantability and of fitness for any *} +{* purpose. The Contributing Authors assume no liability for direct, *} +{* indirect, incidental, special, exemplary, or consequential damages, *} +{* which may result from the use of the MNG Library, even if advised of *} +{* the possibility of such damage. *} +{* *} +{* Permission is hereby granted to use, copy, modify, and distribute this *} +{* source code, or portions hereof, for any purpose, without fee, subject *} +{* to the following restrictions: *} +{* *} +{* 1. The origin of this source code must not be misrepresented; *} +{* you must not claim that you wrote the original software. *} +{* *} +{* 2. Altered versions must be plainly marked as such and must not be *} +{* misrepresented as being the original source. *} +{* *} +{* 3. This Copyright notice may not be removed or altered from any source *} +{* or altered source distribution. *} +{* *} +{* The Contributing Authors specifically permit, without fee, and *} +{* encourage the use of this source code as a component to supporting *} +{* the MNG and JNG file format in commercial products. If you use this *} +{* source code in a product, acknowledgment would be highly appreciated. *} +{* *} +{****************************************************************************} +{* *} +{* project : libmng *} +{* file : libmng.pas copyright (c) 2000-2004 G.Juyn *} +{* version : 1.0.8 *} +{* *} +{* purpose : libmng.dll wrapper unit *} +{* *} +{* author : G.Juyn *} +{* web : http://www.3-t.com *} +{* email : mailto:info (at) 3-t (dot) com *} +{* *} +{* comment : contains the pascal-translation of libmng.h *} +{* can be used by Delphi programs to access the libmng.dll *} +{* *} +{* changes : 0.5.1 - 05/02/2000 - G.Juyn *} +{* - added this version block *} +{* 0.5.1 - 05/08/2000 - G.Juyn *} +{* - changed to stdcall convention *} +{* 0.5.1 - 05/11/2000 - G.Juyn *} +{* - changed callback prototypes *} +{* - added TRUE/FALSE/NULL constants *} +{* - added setoutputprofile2 & setsrgbprofile2 *} +{* - added several new types *} +{* - added chunk-access functions *} +{* - added new error- & tracecodes *} +{* *} +{* 0.5.2 - 05/24/2000 - G.Juyn *} +{* - removed error- & trace-strings since they are now *} +{* provided by the library *} +{* *} +{* 0.5.3 - 06/21/2000 - G.Juyn *} +{* - fixed definition of imagetype *} +{* - added definition of speedtype *} +{* - added get/set speed parameter *} +{* - added get imagelevel parameter *} +{* 0.5.3 - 06/26/2000 - G.Juyn *} +{* - changed definition of userdata to mng_ptr *} +{* 0.5.3 - 06/28/2000 - G.Juyn *} +{* - added mng_size_t definition *} +{* - changed definition of memory alloc size to mng_size_t *} +{* 0.5.3 - 06/29/2000 - G.Juyn *} +{* - changed order of refresh parameters *} +{* - changed definition of mng_handle *} +{* *} +{* 0.9.0 - 06/30/2000 - G.Juyn *} +{* - changed refresh parameters to 'x,y,width,height' *} +{* *} +{* 0.9.1 - 07/08/2000 - G.Juyn *} +{* - added libmng errorcode constants *} +{* 0.9.1 - 07/10/2000 - G.Juyn *} +{* - added new libmng functions *} +{* 0.9.1 - 07/19/2000 - G.Juyn *} +{* - fixed several type definitions *} +{* 0.9.1 - 07/25/2000 - G.Juyn *} +{* - fixed definition of mng_imgtype *} +{* *} +{* 0.9.2 - 08/04/2000 - G.Juyn *} +{* - fixed in line with libmng.h *} +{* 0.9.2 - 08/05/2000 - G.Juyn *} +{* - added function to set simplicity field *} +{* *} +{* 0.9.3 - 10/21/2000 - G.Juyn *} +{* - added several new HLAPI entry points *} +{* *} +{* 1.0.5 - 09/16/2002 - G.Juyn *} +{* - added dynamic MNG features *} +{* *} +{* 1.0.8 - 04/12/2004 - G.Juyn *} +{* - added CRC existence & checking flags *} +{* - added push mechanisms *} +{* *} +{****************************************************************************} + +interface + +{****************************************************************************} + +const MNG_TRUE = TRUE; + MNG_FALSE = FALSE; + MNG_NULL = nil; + +type mng_uint32 = cardinal; + mng_int32 = integer; + mng_uint16 = word; + mng_int16 = smallint; + mng_uint8 = byte; + mng_int8 = shortint; + mng_bool = boolean; + mng_ptr = pointer; + mng_pchar = pchar; + + mng_handle = pointer; + mng_retcode = mng_int32; + mng_chunkid = mng_uint32; + + mng_size_t = cardinal; + + mng_imgtype = (mng_it_unknown, mng_it_png, mng_it_mng, mng_it_jng); + mng_speedtype = (mng_st_normal, mng_st_fast, mng_st_slow, mng_st_slowest); + + mng_uint32p = ^mng_uint32; + mng_uint16p = ^mng_uint16; + mng_uint8p = ^mng_uint8; + mng_chunkidp = ^mng_chunkid; + + mng_palette8e = packed record { 8-bit palette element } + iRed : mng_uint8; + iGreen : mng_uint8; + iBlue : mng_uint8; + end; + + mng_palette8 = packed array [0 .. 255] of mng_palette8e; + + mng_uint8arr = packed array [0 .. 255] of mng_uint8; + mng_uint8arr4 = packed array [0 .. 3] of mng_uint8; + mng_uint16arr = packed array [0 .. 255] of mng_uint16; + mng_uint32arr2 = packed array [0 .. 1] of mng_uint32; + +{****************************************************************************} + +type mng_memalloc = function ( iLen : mng_size_t) : mng_ptr; stdcall; +type mng_memfree = procedure ( pPtr : mng_ptr; + iLen : mng_size_t); stdcall; + +type mng_releasedata = procedure ( pUserData : mng_ptr; + pData : mng_ptr; + iLength : mng_size_t); stdcall; + +type mng_openstream = function ( hHandle : mng_handle) : mng_bool; stdcall; +type mng_closestream = function ( hHandle : mng_handle) : mng_bool; stdcall; + +type mng_readdata = function ( hHandle : mng_handle; + pBuf : mng_ptr; + iBuflen : mng_uint32; + var pRead : mng_uint32) : mng_bool; stdcall; + +type mng_writedata = function ( hHandle : mng_handle; + pBuf : mng_ptr; + iBuflen : mng_uint32; + var pWritten : mng_uint32) : mng_bool; stdcall; + +type mng_errorproc = function ( hHandle : mng_handle; + iErrorcode : mng_retcode; + iSeverity : mng_uint8; + iChunkname : mng_chunkid; + iChunkseq : mng_uint32; + iExtra1 : mng_int32; + iExtra2 : mng_int32; + zErrortext : mng_pchar ) : mng_bool; stdcall; +type mng_traceproc = function ( hHandle : mng_handle; + iFuncnr : mng_int32; + iFuncseq : mng_uint32; + zFuncname : mng_pchar ) : mng_bool; stdcall; + +type mng_processheader = function ( hHandle : mng_handle; + iWidth : mng_uint32; + iHeight : mng_uint32) : mng_bool; stdcall; +type mng_processtext = function ( hHandle : mng_handle; + iType : mng_uint8; + zKeyword : mng_pchar; + zText : mng_pchar; + zLanguage : mng_pchar; + zTranslation : mng_pchar ) : mng_bool; stdcall; + +type mng_processsave = function ( hHandle : mng_handle) : mng_bool; stdcall; +type mng_processseek = function ( hHandle : mng_handle; + zName : mng_pchar ) : mng_bool; stdcall; + +type mng_processneed = function ( hHandle : mng_handle; + zKeyword : mng_pchar ) : mng_bool; stdcall; + +type mng_processunknown = function ( hHandle : mng_handle; + iChunkid : mng_chunkid; + iRawlen : mng_uint32; + pRawdata : mng_ptr ) : mng_bool; stdcall; + +type mng_getcanvasline = function ( hHandle : mng_handle; + iLinenr : mng_uint32) : mng_ptr; stdcall; +type mng_getalphaline = function ( hHandle : mng_handle; + iLinenr : mng_uint32) : mng_ptr; stdcall; +type mng_getbkgdline = function ( hHandle : mng_handle; + iLinenr : mng_uint32) : mng_ptr; stdcall; +type mng_refresh = function ( hHandle : mng_handle; + iX : mng_uint32; + iY : mng_uint32; + iWidth : mng_uint32; + iHeight : mng_uint32) : mng_bool; stdcall; + +type mng_gettickcount = function ( hHandle : mng_handle) : mng_uint32; stdcall; +type mng_settimer = function ( hHandle : mng_handle; + iMsecs : mng_uint32) : mng_bool; stdcall; + +type mng_processgamma = function ( hHandle : mng_handle; + iGamma : mng_uint32) : mng_bool; stdcall; +type mng_processchroma = function ( hHandle : mng_handle; + iWhitex : mng_uint32; + iWhitey : mng_uint32; + iRedx : mng_uint32; + iRedy : mng_uint32; + iGreenx : mng_uint32; + iGreeny : mng_uint32; + iBluex : mng_uint32; + iBluey : mng_uint32) : mng_bool; stdcall; +type mng_processsrgb = function ( hHandle : mng_handle; + iIntent : mng_uint8 ) : mng_bool; stdcall; +type mng_processiccp = function ( hHandle : mng_handle; + iProfilesize : mng_uint32; + pProfile : mng_ptr ) : mng_bool; stdcall; +type mng_processarow = function ( hHandle : mng_handle; + iRowsamples : mng_uint32; + bIsRGBA16 : mng_bool; + pRow : mng_ptr ) : mng_bool; stdcall; + +type mng_iteratechunk = function ( hHandle : mng_handle; + hChunk : mng_handle; + iChunkid : mng_chunkid; + iChunkseq : mng_uint32) : mng_bool; stdcall; + +{****************************************************************************} + +function mng_initialize ( pUserdata : mng_ptr; + fMemalloc : mng_memalloc; + fMemfree : mng_memfree; + fTraceproc : mng_traceproc ) : mng_handle; stdcall; + +function mng_reset ( hHandle : mng_handle ) : mng_retcode; stdcall; + +function mng_cleanup (var hHandle : mng_handle ) : mng_retcode; stdcall; + +function mng_read ( hHandle : mng_handle ) : mng_retcode; stdcall; +function mng_read_pushdata ( hHandle : mng_handle; + pData : mng_ptr; + iLength : mng_uint32; + bTakeownership : mng_bool ) : mng_retcode; stdcall; +function mng_read_pushsig ( hHandle : mng_handle; + eSigtype : mng_imgtype ) : mng_retcode; stdcall; +function mng_read_pushchunk ( hHandle : mng_handle; + pData : mng_ptr; + iLength : mng_uint32; + bTakeownership : mng_bool ) : mng_retcode; stdcall; +function mng_read_resume ( hHandle : mng_handle ) : mng_retcode; stdcall; +function mng_write ( hHandle : mng_handle ) : mng_retcode; stdcall; +function mng_create ( hHandle : mng_handle ) : mng_retcode; stdcall; + +function mng_readdisplay ( hHandle : mng_handle ) : mng_retcode; stdcall; +function mng_display ( hHandle : mng_handle ) : mng_retcode; stdcall; +function mng_display_resume ( hHandle : mng_handle ) : mng_retcode; stdcall; +function mng_display_freeze ( hHandle : mng_handle ) : mng_retcode; stdcall; +function mng_display_reset ( hHandle : mng_handle ) : mng_retcode; stdcall; +function mng_display_goframe ( hHandle : mng_handle; + iFramenr : mng_uint32 ) : mng_retcode; stdcall; +function mng_display_golayer ( hHandle : mng_handle; + iLayernr : mng_uint32 ) : mng_retcode; stdcall; +function mng_display_gotime ( hHandle : mng_handle; + iPlaytime : mng_uint32 ) : mng_retcode; stdcall; + +function mng_trapevent ( hHandle : mng_handle; + iEventtype : mng_uint8; + iX : mng_int32; + iY : mng_int32 ) : mng_retcode; stdcall; + +function mng_getlasterror ( hHandle : mng_handle; + var iSeverity : mng_uint8; + var iChunkname : mng_chunkid; + var iChunkseq : mng_uint32; + var iExtra1 : mng_int32; + var iExtra2 : mng_int32; + var zErrortext : mng_pchar ) : mng_retcode; stdcall; + +{****************************************************************************} + +function mng_setcb_memalloc ( hHandle : mng_handle; + fProc : mng_memalloc ) : mng_retcode; stdcall; +function mng_setcb_memfree ( hHandle : mng_handle; + fProc : mng_memfree ) : mng_retcode; stdcall; +function mng_setcb_releasedata ( hHandle : mng_handle; + fProc : mng_releasedata ) : mng_retcode; stdcall; + +function mng_setcb_openstream ( hHandle : mng_handle; + fProc : mng_openstream ) : mng_retcode; stdcall; +function mng_setcb_closestream ( hHandle : mng_handle; + fProc : mng_closestream ) : mng_retcode; stdcall; + +function mng_setcb_readdata ( hHandle : mng_handle; + fProc : mng_readdata ) : mng_retcode; stdcall; + +function mng_setcb_writedata ( hHandle : mng_handle; + fProc : mng_writedata ) : mng_retcode; stdcall; + +function mng_setcb_errorproc ( hHandle : mng_handle; + fProc : mng_errorproc ) : mng_retcode; stdcall; +function mng_setcb_traceproc ( hHandle : mng_handle; + fProc : mng_traceproc ) : mng_retcode; stdcall; + +function mng_setcb_processheader ( hHandle : mng_handle; + fProc : mng_processheader) : mng_retcode; stdcall; +function mng_setcb_processtext ( hHandle : mng_handle; + fProc : mng_processtext ) : mng_retcode; stdcall; + +function mng_setcb_getcanvasline ( hHandle : mng_handle; + fProc : mng_getcanvasline) : mng_retcode; stdcall; +function mng_setcb_getalphaline ( hHandle : mng_handle; + fProc : mng_getalphaline ) : mng_retcode; stdcall; +function mng_setcb_getbkgdline ( hHandle : mng_handle; + fProc : mng_getbkgdline ) : mng_retcode; stdcall; +function mng_setcb_refresh ( hHandle : mng_handle; + fProc : mng_refresh ) : mng_retcode; stdcall; + +function mng_setcb_gettickcount ( hHandle : mng_handle; + fProc : mng_gettickcount ) : mng_retcode; stdcall; +function mng_setcb_settimer ( hHandle : mng_handle; + fProc : mng_settimer ) : mng_retcode; stdcall; + +function mng_setcb_processgamma ( hHandle : mng_handle; + fProc : mng_processgamma ) : mng_retcode; stdcall; +function mng_setcb_processchroma ( hHandle : mng_handle; + fProc : mng_processchroma) : mng_retcode; stdcall; +function mng_setcb_processsrgb ( hHandle : mng_handle; + fProc : mng_processsrgb ) : mng_retcode; stdcall; +function mng_setcb_processiccp ( hHandle : mng_handle; + fProc : mng_processiccp ) : mng_retcode; stdcall; +function mng_setcb_processarow ( hHandle : mng_handle; + fProc : mng_processarow ) : mng_retcode; stdcall; + +{****************************************************************************} + +function mng_getcb_memalloc ( hHandle : mng_handle ) : mng_memalloc; stdcall; +function mng_getcb_memfree ( hHandle : mng_handle ) : mng_memfree; stdcall; +function mng_getcb_releasedata ( hHandle : mng_handle ) : mng_releasedata; stdcall; + +function mng_getcb_openstream ( hHandle : mng_handle ) : mng_openstream; stdcall; +function mng_getcb_closestream ( hHandle : mng_handle ) : mng_closestream; stdcall; + +function mng_getcb_readdata ( hHandle : mng_handle ) : mng_readdata; stdcall; + +function mng_getcb_writedata ( hHandle : mng_handle ) : mng_writedata; stdcall; + +function mng_getcb_errorproc ( hHandle : mng_handle ) : mng_errorproc; stdcall; +function mng_getcb_traceproc ( hHandle : mng_handle ) : mng_traceproc; stdcall; + +function mng_getcb_processheader ( hHandle : mng_handle ) : mng_processheader; stdcall; +function mng_getcb_processtext ( hHandle : mng_handle ) : mng_processtext; stdcall; + +function mng_getcb_getcanvasline ( hHandle : mng_handle ) : mng_getcanvasline; stdcall; +function mng_getcb_getalphaline ( hHandle : mng_handle ) : mng_getalphaline; stdcall; +function mng_getcb_getbkgdline ( hHandle : mng_handle ) : mng_getbkgdline; stdcall; +function mng_getcb_refresh ( hHandle : mng_handle ) : mng_refresh; stdcall; + +function mng_getcb_gettickcount ( hHandle : mng_handle ) : mng_gettickcount; stdcall; +function mng_getcb_settimer ( hHandle : mng_handle ) : mng_settimer; stdcall; + +function mng_getcb_processgamma ( hHandle : mng_handle ) : mng_processgamma; stdcall; +function mng_getcb_processchroma ( hHandle : mng_handle ) : mng_processchroma; stdcall; +function mng_getcb_processsrgb ( hHandle : mng_handle ) : mng_processsrgb; stdcall; +function mng_getcb_processiccp ( hHandle : mng_handle ) : mng_processiccp; stdcall; +function mng_getcb_processarow ( hHandle : mng_handle ) : mng_processarow; stdcall; + +{****************************************************************************} + +function mng_set_userdata ( hHandle : mng_handle; + pUserdata : mng_ptr ) : mng_retcode; stdcall; + +function mng_set_canvasstyle ( hHandle : mng_handle; + iStyle : mng_uint32 ) : mng_retcode; stdcall; +function mng_set_bkgdstyle ( hHandle : mng_handle; + iStyle : mng_uint32 ) : mng_retcode; stdcall; + +function mng_set_bgcolor ( hHandle : mng_handle; + iRed : mng_uint16; + iGreen : mng_uint16; + iBlue : mng_uint16 ) : mng_retcode; stdcall; + +function mng_set_usebkgd ( hHandle : mng_handle; + bUseBKGD : mng_bool ) : mng_retcode; stdcall; + +function mng_set_storechunks ( hHandle : mng_handle; + bStorechunks : mng_bool ) : mng_retcode; stdcall; + +function mng_set_cacheplayback ( hHandle : mng_handle; + bCacheplayback : mng_bool ) : mng_retcode; stdcall; + +function mng_set_viewgammaint ( hHandle : mng_handle; + iGamma : mng_uint32 ) : mng_retcode; stdcall; +function mng_set_displaygammaint ( hHandle : mng_handle; + iGamma : mng_uint32 ) : mng_retcode; stdcall; +function mng_set_dfltimggammaint ( hHandle : mng_handle; + iGamma : mng_uint32 ) : mng_retcode; stdcall; + +function mng_set_srgb ( hHandle : mng_handle; + bIssRGB : mng_bool ) : mng_retcode; stdcall; +function mng_set_outputprofile ( hHandle : mng_handle; + zFilename : mng_pchar ) : mng_retcode; stdcall; +function mng_set_outputprofile2 ( hHandle : mng_handle; + iProfilesize : mng_uint32; + pProfile : mng_ptr ) : mng_retcode; stdcall; +function mng_set_srgbprofile ( hHandle : mng_handle; + zFilename : mng_pchar ) : mng_retcode; stdcall; +function mng_set_srgbprofile2 ( hHandle : mng_handle; + iProfilesize : mng_uint32; + pProfile : mng_ptr ) : mng_retcode; stdcall; + +function mng_set_maxcanvaswidth ( hHandle : mng_handle; + iMaxwidth : mng_uint32 ) : mng_retcode; stdcall; +function mng_set_maxcanvasheight ( hHandle : mng_handle; + iMaxheight : mng_uint32 ) : mng_retcode; stdcall; +function mng_set_maxcanvassize ( hHandle : mng_handle; + iMaxwidth : mng_uint32; + iMaxheight : mng_uint32 ) : mng_retcode; stdcall; + +function mng_set_suspensionmode ( hHandle : mng_handle; + bSuspensionmode : mng_bool ) : mng_retcode; stdcall; + +function mng_set_speed ( hHandle : mng_handle; + iSpeed : mng_speedtype ) : mng_retcode; stdcall; + +function mng_set_crcmode ( hHandle : mng_handle; + iCrcmode : mng_uint32 ) : mng_retcode; stdcall; + +{****************************************************************************} + +function mng_get_userdata ( hHandle : mng_handle ) : mng_ptr; stdcall; + +function mng_get_sigtype ( hHandle : mng_handle ) : mng_imgtype; stdcall; +function mng_get_imagetype ( hHandle : mng_handle ) : mng_imgtype; stdcall; +function mng_get_imagewidth ( hHandle : mng_handle ) : mng_uint32; stdcall; +function mng_get_imageheight ( hHandle : mng_handle ) : mng_uint32; stdcall; +function mng_get_ticks ( hHandle : mng_handle ) : mng_uint32; stdcall; +function mng_get_framecount ( hHandle : mng_handle ) : mng_uint32; stdcall; +function mng_get_layercount ( hHandle : mng_handle ) : mng_uint32; stdcall; +function mng_get_playtime ( hHandle : mng_handle ) : mng_uint32; stdcall; +function mng_get_simplicity ( hHandle : mng_handle ) : mng_uint32; stdcall; + +function mng_get_canvasstyle ( hHandle : mng_handle ) : mng_uint32; stdcall; +function mng_get_bkgdstyle ( hHandle : mng_handle ) : mng_uint32; stdcall; + +procedure mng_get_bgcolor ( hHandle : mng_handle; + var iRed : mng_uint16; + var iGreen : mng_uint16; + var iBlue : mng_uint16 ); stdcall; + +function mng_get_usebkgd ( hHandle : mng_handle ) : mng_bool; stdcall; + +function mng_get_storechunks ( hHandle : mng_handle ) : mng_bool; stdcall; +function mng_get_cacheplayback ( hHandle : mng_handle ) : mng_bool; stdcall; + +function mng_get_viewgammaint ( hHandle : mng_handle ) : mng_uint32; stdcall; +function mng_get_displaygammaint ( hHandle : mng_handle ) : mng_uint32; stdcall; +function mng_get_dfltimggammaint ( hHandle : mng_handle ) : mng_uint32; stdcall; + +function mng_get_srgb ( hHandle : mng_handle ) : mng_bool; stdcall; + +function mng_get_maxcanvaswidth ( hHandle : mng_handle ) : mng_uint32; stdcall; +function mng_get_maxcanvasheight ( hHandle : mng_handle ) : mng_uint32; stdcall; + +function mng_get_suspensionmode ( hHandle : mng_handle ) : mng_bool; stdcall; + +function mng_get_speed ( hHandle : mng_handle ) : mng_speedtype; stdcall; +function mng_get_imagelevel ( hHandle : mng_handle ) : mng_uint32; stdcall; + +function mng_get_starttime ( hHandle : mng_handle ) : mng_uint32; stdcall; +function mng_get_runtime ( hHandle : mng_handle ) : mng_uint32; stdcall; +function mng_get_currentframe ( hHandle : mng_handle ) : mng_uint32; stdcall; +function mng_get_currentlayer ( hHandle : mng_handle ) : mng_uint32; stdcall; +function mng_get_currentplaytime ( hHandle : mng_handle ) : mng_uint32; stdcall; +function mng_get_totalframes ( hHandle : mng_handle ) : mng_uint32; stdcall; +function mng_get_totallayers ( hHandle : mng_handle ) : mng_uint32; stdcall; +function mng_get_totalplaytime ( hHandle : mng_handle ) : mng_uint32; stdcall; + +function mng_status_error ( hHandle : mng_handle ) : mng_bool; stdcall; +function mng_status_reading ( hHandle : mng_handle ) : mng_bool; stdcall; +function mng_status_suspendbreak ( hHandle : mng_handle ) : mng_bool; stdcall; +function mng_status_creating ( hHandle : mng_handle ) : mng_bool; stdcall; +function mng_status_writing ( hHandle : mng_handle ) : mng_bool; stdcall; +function mng_status_displaying ( hHandle : mng_handle ) : mng_bool; stdcall; +function mng_status_running ( hHandle : mng_handle ) : mng_bool; stdcall; +function mng_status_timerbreak ( hHandle : mng_handle ) : mng_bool; stdcall; +function mng_status_dynamic ( hHandle : mng_handle ) : mng_bool; stdcall; + +{****************************************************************************} + +function mng_iterate_chunks ( hHandle : mng_handle; + iChunkseq : mng_uint32; + fProc : mng_iteratechunk ) : mng_retcode; stdcall; + +{****************************************************************************} + +function mng_getchunk_ihdr ( hHandle : mng_handle; + hChunk : mng_handle; + var iWidth : mng_uint32; + var iHeight : mng_uint32; + var iBitdepth : mng_uint8; + var iColortype : mng_uint8; + var iCompression : mng_uint8; + var iFilter : mng_uint8; + var iInterlace : mng_uint8 ) : mng_retcode; stdcall; + +function mng_getchunk_plte ( hHandle : mng_handle; + hChunk : mng_handle; + var iCount : mng_uint32; + var aPalette : mng_palette8 ) : mng_retcode; stdcall; + +function mng_getchunk_idat ( hHandle : mng_handle; + hChunk : mng_handle; + var iRawlen : mng_uint32; + var pRawdata : mng_ptr ) : mng_retcode; stdcall; + +function mng_getchunk_trns ( hHandle : mng_handle; + hChunk : mng_handle; + var bEmpty : mng_bool; + var bGlobal : mng_bool; + var iType : mng_uint8; + var iCount : mng_uint32; + var aAlphas : mng_uint8arr; + var iGray : mng_uint16; + var iRed : mng_uint16; + var iGreen : mng_uint16; + var iBlue : mng_uint16; + var iRawlen : mng_uint32; + var aRawdata : mng_uint8arr ) : mng_retcode; stdcall; + +function mng_getchunk_gama ( hHandle : mng_handle; + hChunk : mng_handle; + var bEmpty : mng_bool; + var iGamma : mng_uint32 ) : mng_retcode; stdcall; + +function mng_getchunk_chrm ( hHandle : mng_handle; + hChunk : mng_handle; + var bEmpty : mng_bool; + var iWhitepointx : mng_uint32; + var iWhitepointy : mng_uint32; + var iRedx : mng_uint32; + var iRedy : mng_uint32; + var iGreenx : mng_uint32; + var iGreeny : mng_uint32; + var iBluex : mng_uint32; + var iBluey : mng_uint32 ) : mng_retcode; stdcall; + +function mng_getchunk_srgb ( hHandle : mng_handle; + hChunk : mng_handle; + var bEmpty : mng_bool; + var iRenderingintent : mng_uint8 ) : mng_retcode; stdcall; + +function mng_getchunk_iccp ( hHandle : mng_handle; + hChunk : mng_handle; + var bEmpty : mng_bool; + var iNamesize : mng_uint32; + var zName : mng_pchar; + var iCompression : mng_uint8; + var iProfilesize : mng_uint32; + var pProfile : mng_ptr ) : mng_retcode; stdcall; + +function mng_getchunk_text ( hHandle : mng_handle; + hChunk : mng_handle; + var iKeywordsize : mng_uint32; + var zKeyword : mng_pchar; + var iTextsize : mng_uint32; + var zText : mng_pchar ) : mng_retcode; stdcall; + +function mng_getchunk_ztxt ( hHandle : mng_handle; + hChunk : mng_handle; + var iKeywordsize : mng_uint32; + var zKeyword : mng_pchar; + var iCompression : mng_uint8; + var iTextsize : mng_uint32; + var zText : mng_pchar ) : mng_retcode; stdcall; + +function mng_getchunk_itxt ( hHandle : mng_handle; + hChunk : mng_handle; + var iKeywordsize : mng_uint32; + var zKeyword : mng_pchar; + var iCompressionflag : mng_uint8; + var iCompressionmethod : mng_uint8; + var iLanguagesize : mng_uint32; + var zLanguage : mng_pchar; + var iTranslationsize : mng_uint32; + var zTranslation : mng_pchar; + var iTextsize : mng_uint32; + var zText : mng_pchar ) : mng_retcode; stdcall; + +function mng_getchunk_bkgd ( hHandle : mng_handle; + hChunk : mng_handle; + var bEmpty : mng_bool; + var iType : mng_uint8; + var iIndex : mng_uint8; + var iGray : mng_uint16; + var iRed : mng_uint16; + var iGreen : mng_uint16; + var iBlue : mng_uint16 ) : mng_retcode; stdcall; + +function mng_getchunk_phys ( hHandle : mng_handle; + hChunk : mng_handle; + var bEmpty : mng_bool; + var iSizex : mng_uint32; + var iSizey : mng_uint32; + var iUnit : mng_uint8 ) : mng_retcode; stdcall; + +function mng_getchunk_sbit ( hHandle : mng_handle; + hChunk : mng_handle; + var bEmpty : mng_bool; + var iType : mng_uint8; + var aBits : mng_uint8arr4) : mng_retcode; stdcall; + +function mng_getchunk_splt ( hHandle : mng_handle; + hChunk : mng_handle; + var bEmpty : mng_bool; + var iNamesize : mng_uint32; + var zName : mng_pchar; + var iSampledepth : mng_uint8; + var iEntrycount : mng_uint32; + var pEntries : mng_ptr ) : mng_retcode; stdcall; + +function mng_getchunk_hist ( hHandle : mng_handle; + hChunk : mng_handle; + var iEntrycount : mng_uint32; + var aEntries : mng_uint16arr) : mng_retcode; stdcall; + +function mng_getchunk_time ( hHandle : mng_handle; + hChunk : mng_handle; + var iYear : mng_uint16; + var iMonth : mng_uint8; + var iDay : mng_uint8; + var iHour : mng_uint8; + var iMinute : mng_uint8; + var iSecond : mng_uint8 ) : mng_retcode; stdcall; + +function mng_getchunk_mhdr ( hHandle : mng_handle; + hChunk : mng_handle; + var iWidth : mng_uint32; + var iHeight : mng_uint32; + var iTicks : mng_uint32; + var iLayercount : mng_uint32; + var iFramecount : mng_uint32; + var iPlaytime : mng_uint32; + var iSimplicity : mng_uint32 ) : mng_retcode; stdcall; + +function mng_getchunk_loop ( hHandle : mng_handle; + hChunk : mng_handle; + var iLevel : mng_uint8; + var iRepeat : mng_uint32; + var iTermination : mng_uint8; + var iItermin : mng_uint32; + var iItermax : mng_uint32; + var iCount : mng_uint32; + var pSignals : mng_uint32p ) : mng_retcode; stdcall; + +function mng_getchunk_endl ( hHandle : mng_handle; + hChunk : mng_handle; + var iLevel : mng_uint8 ) : mng_retcode; stdcall; + +function mng_getchunk_defi ( hHandle : mng_handle; + hChunk : mng_handle; + var iObjectid : mng_uint16; + var iDonotshow : mng_uint8; + var iConcrete : mng_uint8; + var bHasloca : mng_bool; + var iXlocation : mng_int32; + var iYlocation : mng_int32; + var bHasclip : mng_bool; + var iLeftcb : mng_int32; + var iRightcb : mng_int32; + var iTopcb : mng_int32; + var iBottomcb : mng_int32 ) : mng_retcode; stdcall; + +function mng_getchunk_basi ( hHandle : mng_handle; + hChunk : mng_handle; + var iWidth : mng_uint32; + var iHeight : mng_uint32; + var iBitdepth : mng_uint8; + var iColortype : mng_uint8; + var iCompression : mng_uint8; + var iFilter : mng_uint8; + var iInterlace : mng_uint8; + var iRed : mng_uint16; + var iGreen : mng_uint16; + var iBlue : mng_uint16; + var iAlpha : mng_uint16; + var iViewable : mng_uint8 ) : mng_retcode; stdcall; + +function mng_getchunk_clon ( hHandle : mng_handle; + hChunk : mng_handle; + var iSourceid : mng_uint16; + var iCloneid : mng_uint16; + var iClonetype : mng_uint8; + var iDonotshow : mng_uint8; + var iConcrete : mng_uint8; + var bHasloca : mng_bool; + var iLocationtype : mng_uint8; + var iLocationx : mng_int32; + var iLocationy : mng_int32 ) : mng_retcode; stdcall; + +function mng_getchunk_past ( hHandle : mng_handle; + hChunk : mng_handle; + var iDestid : mng_uint16; + var iTargettype : mng_uint8; + var iTargetx : mng_int32; + var iTargety : mng_int32; + var iCount : mng_uint32 ) : mng_retcode; stdcall; + +function mng_getchunk_past_src ( hHandle : mng_handle; + hChunk : mng_handle; + iEntry : mng_uint32; + var iSourceid : mng_uint16; + var iComposition : mng_uint8; + var iOrientation : mng_uint8; + var iOffsettype : mng_uint8; + var iOffsetx : mng_int32; + var iOffsety : mng_int32; + var iBoundarytype : mng_uint8; + var iBoundaryl : mng_int32; + var iBoundaryr : mng_int32; + var iBoundaryt : mng_int32; + var iBoundaryb : mng_int32 ) : mng_retcode; stdcall; + +function mng_getchunk_disc ( hHandle : mng_handle; + hChunk : mng_handle; + var iCount : mng_uint32; + var pObjectids : mng_uint16p ) : mng_retcode; stdcall; + +function mng_getchunk_back ( hHandle : mng_handle; + hChunk : mng_handle; + var iRed : mng_uint16; + var iGreen : mng_uint16; + var iBlue : mng_uint16; + var iMandatory : mng_uint8; + var iImageid : mng_uint16; + var iTile : mng_uint8 ) : mng_retcode; stdcall; + +function mng_getchunk_fram ( hHandle : mng_handle; + hChunk : mng_handle; + var bEmpty : mng_bool; + var iMode : mng_uint8; + var iNamesize : mng_uint32; + var zName : mng_pchar; + var iChangedelay : mng_uint8; + var iChangetimeout : mng_uint8; + var iChangeclipping : mng_uint8; + var iChangesyncid : mng_uint8; + var iDelay : mng_uint32; + var iTimeout : mng_uint32; + var iBoundarytype : mng_uint8; + var iBoundaryl : mng_int32; + var iBoundaryr : mng_int32; + var iBoundaryt : mng_int32; + var iBoundaryb : mng_int32; + var iCount : mng_uint32; + var pSyncids : mng_uint32p ) : mng_retcode; stdcall; + +function mng_getchunk_move ( hHandle : mng_handle; + hChunk : mng_handle; + var iFirstid : mng_uint16; + var iLastid : mng_uint16; + var iMovetype : mng_uint8; + var iMovex : mng_int32; + var iMovey : mng_int32 ) : mng_retcode; stdcall; + +function mng_getchunk_clip ( hHandle : mng_handle; + hChunk : mng_handle; + var iFirstid : mng_uint16; + var iLastid : mng_uint16; + var iCliptype : mng_uint8; + var iClipl : mng_int32; + var iClipr : mng_int32; + var iClipt : mng_int32; + var iClipb : mng_int32 ) : mng_retcode; stdcall; + +function mng_getchunk_show ( hHandle : mng_handle; + hChunk : mng_handle; + var bEmpty : mng_bool; + var iFirstid : mng_uint16; + var iLastid : mng_uint16; + var iMode : mng_uint8 ) : mng_retcode; stdcall; + +function mng_getchunk_term ( hHandle : mng_handle; + hChunk : mng_handle; + var iTermaction : mng_uint8; + var iIteraction : mng_uint8; + var iDelay : mng_uint32; + var iItermax : mng_uint32 ) : mng_retcode; stdcall; + +function mng_getchunk_save ( hHandle : mng_handle; + hChunk : mng_handle; + var bEmpty : mng_bool; + var iOffsettype : mng_uint8; + var iCount : mng_uint32 ) : mng_retcode; stdcall; + +function mng_getchunk_save_entry ( hHandle : mng_handle; + hChunk : mng_handle; + iEntry : mng_uint32; + var iEntrytype : mng_uint8; + var iOffset : mng_uint32arr2; + var iStarttime : mng_uint32arr2; + var iLayernr : mng_uint32; + var iFramenr : mng_uint32; + var iNamesize : mng_uint32; + var zName : mng_pchar ) : mng_retcode; stdcall; + +function mng_getchunk_seek ( hHandle : mng_handle; + hChunk : mng_handle; + var iNamesize : mng_uint32; + var zName : mng_pchar ) : mng_retcode; stdcall; + +function mng_getchunk_expi ( hHandle : mng_handle; + hChunk : mng_handle; + var iSnapshotid : mng_uint16; + var iNamesize : mng_uint32; + var zName : mng_pchar ) : mng_retcode; stdcall; + +function mng_getchunk_fpri ( hHandle : mng_handle; + hChunk : mng_handle; + var iDeltatype : mng_uint8; + var iPriority : mng_uint8 ) : mng_retcode; stdcall; + +function mng_getchunk_need ( hHandle : mng_handle; + hChunk : mng_handle; + var iKeywordssize : mng_uint32; + var zKeywords : mng_pchar ) : mng_retcode; stdcall; + +function mng_getchunk_phyg ( hHandle : mng_handle; + hChunk : mng_handle; + var bEmpty : mng_bool; + var iSizex : mng_uint32; + var iSizey : mng_uint32; + var iUnit : mng_uint8 ) : mng_retcode; stdcall; + +function mng_getchunk_jhdr ( hHandle : mng_handle; + hChunk : mng_handle; + var iWidth : mng_uint32; + var iHeight : mng_uint32; + var iColortype : mng_uint8; + var iImagesampledepth : mng_uint8; + var iImagecompression : mng_uint8; + var iImageinterlace : mng_uint8; + var iAlphasampledepth : mng_uint8; + var iAlphacompression : mng_uint8; + var iAlphafilter : mng_uint8; + var iAlphainterlace : mng_uint8 ) : mng_retcode; stdcall; + +function mng_getchunk_jdat ( hHandle : mng_handle; + hChunk : mng_handle; + var iRawlen : mng_uint32; + var pRawdata : mng_ptr ) : mng_retcode; stdcall; + +function mng_getchunk_dhdr ( hHandle : mng_handle; + hChunk : mng_handle; + var iObjectid : mng_uint16; + var iImagetype : mng_uint8; + var iDeltatype : mng_uint8; + var iBlockwidth : mng_uint32; + var iBlockheight : mng_uint32; + var iBlockx : mng_uint32; + var iBlocky : mng_uint32 ) : mng_retcode; stdcall; + +function mng_getchunk_prom ( hHandle : mng_handle; + hChunk : mng_handle; + var iColortype : mng_uint8; + var iSampledepth : mng_uint8; + var iFilltype : mng_uint8 ) : mng_retcode; stdcall; + +function mng_getchunk_pplt ( hHandle : mng_handle; + hChunk : mng_handle; + var iCount : mng_uint32 ) : mng_retcode; stdcall; + +function mng_getchunk_pplt_entry ( hHandle : mng_handle; + hChunk : mng_handle; + iEntry : mng_uint32; + var iRed : mng_uint16; + var iGreen : mng_uint16; + var iBlue : mng_uint16; + var iAlpha : mng_uint16; + var bUsed : mng_bool ) : mng_retcode; stdcall; + +function mng_getchunk_drop ( hHandle : mng_handle; + hChunk : mng_handle; + var iCount : mng_uint32; + var pChunknames : mng_chunkidp ) : mng_retcode; stdcall; + +function mng_getchunk_dbyk ( hHandle : mng_handle; + hChunk : mng_handle; + var iChunkname : mng_chunkid; + var iPolarity : mng_uint8; + var iKeywordssize : mng_uint32; + var zKeywords : mng_pchar ) : mng_retcode; stdcall; + +function mng_getchunk_ordr ( hHandle : mng_handle; + hChunk : mng_handle; + var iCount : mng_uint32 ) : mng_retcode; stdcall; + +function mng_getchunk_ordr_entry ( hHandle : mng_handle; + hChunk : mng_handle; + iEntry : mng_uint32; + var iChunkname : mng_chunkid; + var iOrdertype : mng_uint8 ) : mng_retcode; stdcall; + +function mng_getchunk_unknown ( hHandle : mng_handle; + hChunk : mng_handle; + var iChunkname : mng_chunkid; + var iRawlen : mng_uint32; + var pRawdata : mng_ptr ) : mng_retcode; stdcall; + +{****************************************************************************} + +function mng_putchunk_ihdr ( hHandle : mng_handle; + iWidth : mng_uint32; + iHeight : mng_uint32; + iBitdepth : mng_uint8; + iColortype : mng_uint8; + iCompression : mng_uint8; + iFilter : mng_uint8; + iInterlace : mng_uint8 ) : mng_retcode; stdcall; + +function mng_putchunk_plte ( hHandle : mng_handle; + iCount : mng_uint32; + aPalette : mng_palette8 ) : mng_retcode; stdcall; + +function mng_putchunk_idat ( hHandle : mng_handle; + iRawlen : mng_uint32; + pRawdata : mng_ptr ) : mng_retcode; stdcall; + +function mng_putchunk_iend ( hHandle : mng_handle ) : mng_retcode; stdcall; + +function mng_putchunk_trns ( hHandle : mng_handle; + bEmpty : mng_bool; + bGlobal : mng_bool; + iType : mng_uint8; + iCount : mng_uint32; + aAlphas : mng_uint8arr; + iGray : mng_uint16; + iRed : mng_uint16; + iGreen : mng_uint16; + iBlue : mng_uint16; + iRawlen : mng_uint32; + aRawdata : mng_uint8arr ) : mng_retcode; stdcall; + +function mng_putchunk_gama ( hHandle : mng_handle; + bEmpty : mng_bool; + iGamma : mng_uint32 ) : mng_retcode; stdcall; + +function mng_putchunk_chrm ( hHandle : mng_handle; + bEmpty : mng_bool; + iWhitepointx : mng_uint32; + iWhitepointy : mng_uint32; + iRedx : mng_uint32; + iRedy : mng_uint32; + iGreenx : mng_uint32; + iGreeny : mng_uint32; + iBluex : mng_uint32; + iBluey : mng_uint32 ) : mng_retcode; stdcall; + +function mng_putchunk_srgb ( hHandle : mng_handle; + bEmpty : mng_bool; + iRenderingintent : mng_uint8 ) : mng_retcode; stdcall; + +function mng_putchunk_iccp ( hHandle : mng_handle; + bEmpty : mng_bool; + iNamesize : mng_uint32; + zName : mng_pchar; + iCompression : mng_uint8; + iProfilesize : mng_uint32; + pProfile : mng_ptr ) : mng_retcode; stdcall; + +function mng_putchunk_text ( hHandle : mng_handle; + iKeywordsize : mng_uint32; + zKeyword : mng_pchar; + iTextsize : mng_uint32; + zText : mng_pchar ) : mng_retcode; stdcall; + +function mng_putchunk_ztxt ( hHandle : mng_handle; + iKeywordsize : mng_uint32; + zKeyword : mng_pchar; + iCompression : mng_uint8; + iTextsize : mng_uint32; + zText : mng_pchar ) : mng_retcode; stdcall; + +function mng_putchunk_itxt ( hHandle : mng_handle; + iKeywordsize : mng_uint32; + zKeyword : mng_pchar; + iCompressionflag : mng_uint8; + iCompressionmethod : mng_uint8; + iLanguagesize : mng_uint32; + zLanguage : mng_pchar; + iTranslationsize : mng_uint32; + zTranslation : mng_pchar; + iTextsize : mng_uint32; + zText : mng_pchar ) : mng_retcode; stdcall; + +function mng_putchunk_bkgd ( hHandle : mng_handle; + bEmpty : mng_bool; + iType : mng_uint8; + iIndex : mng_uint8; + iGray : mng_uint16; + iRed : mng_uint16; + iGreen : mng_uint16; + iBlue : mng_uint16 ) : mng_retcode; stdcall; + +function mng_putchunk_phys ( hHandle : mng_handle; + bEmpty : mng_bool; + iSizex : mng_uint32; + iSizey : mng_uint32; + iUnit : mng_uint8 ) : mng_retcode; stdcall; + +function mng_putchunk_sbit ( hHandle : mng_handle; + bEmpty : mng_bool; + iType : mng_uint8; + aBits : mng_uint8arr4) : mng_retcode; stdcall; + +function mng_putchunk_splt ( hHandle : mng_handle; + bEmpty : mng_bool; + iNamesize : mng_uint32; + zName : mng_pchar; + iSampledepth : mng_uint8; + iEntrycount : mng_uint32; + pEntries : mng_ptr ) : mng_retcode; stdcall; + +function mng_putchunk_hist ( hHandle : mng_handle; + iEntrycount : mng_uint32; + aEntries : mng_uint16arr) : mng_retcode; stdcall; + +function mng_putchunk_time ( hHandle : mng_handle; + iYear : mng_uint16; + iMonth : mng_uint8; + iDay : mng_uint8; + iHour : mng_uint8; + iMinute : mng_uint8; + iSecond : mng_uint8 ) : mng_retcode; stdcall; + +function mng_putchunk_mhdr ( hHandle : mng_handle; + iWidth : mng_uint32; + iHeight : mng_uint32; + iTicks : mng_uint32; + iLayercount : mng_uint32; + iFramecount : mng_uint32; + iPlaytime : mng_uint32; + iSimplicity : mng_uint32 ) : mng_retcode; stdcall; + +function mng_putchunk_mend ( hHandle : mng_handle ) : mng_retcode; stdcall; + +function mng_putchunk_loop ( hHandle : mng_handle; + iLevel : mng_uint8; + iRepeat : mng_uint32; + iTermination : mng_uint8; + iItermin : mng_uint32; + iItermax : mng_uint32; + iCount : mng_uint32; + pSignals : mng_uint32p ) : mng_retcode; stdcall; + +function mng_putchunk_endl ( hHandle : mng_handle; + iLevel : mng_uint8 ) : mng_retcode; stdcall; + +function mng_putchunk_defi ( hHandle : mng_handle; + iObjectid : mng_uint16; + iDonotshow : mng_uint8; + iConcrete : mng_uint8; + bHasloca : mng_bool; + iXlocation : mng_int32; + iYlocation : mng_int32; + bHasclip : mng_bool; + iLeftcb : mng_int32; + iRightcb : mng_int32; + iTopcb : mng_int32; + iBottomcb : mng_int32 ) : mng_retcode; stdcall; + +function mng_putchunk_basi ( hHandle : mng_handle; + iWidth : mng_uint32; + iHeight : mng_uint32; + iBitdepth : mng_uint8; + iColortype : mng_uint8; + iCompression : mng_uint8; + iFilter : mng_uint8; + iInterlace : mng_uint8; + iRed : mng_uint16; + iGreen : mng_uint16; + iBlue : mng_uint16; + iAlpha : mng_uint16; + iViewable : mng_uint8 ) : mng_retcode; stdcall; + +function mng_putchunk_clon ( hHandle : mng_handle; + iSourceid : mng_uint16; + iCloneid : mng_uint16; + iClonetype : mng_uint8; + iDonotshow : mng_uint8; + iConcrete : mng_uint8; + bHasloca : mng_bool; + iLocationtype : mng_uint8; + iLocationx : mng_int32; + iLocationy : mng_int32 ) : mng_retcode; stdcall; + +function mng_putchunk_past ( hHandle : mng_handle; + iDestid : mng_uint16; + iTargettype : mng_uint8; + iTargetx : mng_int32; + iTargety : mng_int32; + iCount : mng_uint32 ) : mng_retcode; stdcall; + +function mng_putchunk_past_src ( hHandle : mng_handle; + iEntry : mng_uint32; + iSourceid : mng_uint16; + iComposition : mng_uint8; + iOrientation : mng_uint8; + iOffsettype : mng_uint8; + iOffsetx : mng_int32; + iOffsety : mng_int32; + iBoundarytype : mng_uint8; + iBoundaryl : mng_int32; + iBoundaryr : mng_int32; + iBoundaryt : mng_int32; + iBoundaryb : mng_int32 ) : mng_retcode; stdcall; + +function mng_putchunk_disc ( hHandle : mng_handle; + iCount : mng_uint32; + pObjectids : mng_uint16p ) : mng_retcode; stdcall; + +function mng_putchunk_back ( hHandle : mng_handle; + iRed : mng_uint16; + iGreen : mng_uint16; + iBlue : mng_uint16; + iMandatory : mng_uint8; + iImageid : mng_uint16; + iTile : mng_uint8 ) : mng_retcode; stdcall; + +function mng_putchunk_fram ( hHandle : mng_handle; + bEmpty : mng_bool; + iMode : mng_uint8; + iNamesize : mng_uint32; + zName : mng_pchar; + iChangedelay : mng_uint8; + iChangetimeout : mng_uint8; + iChangeclipping : mng_uint8; + iChangesyncid : mng_uint8; + iDelay : mng_uint32; + iTimeout : mng_uint32; + iBoundarytype : mng_uint8; + iBoundaryl : mng_int32; + iBoundaryr : mng_int32; + iBoundaryt : mng_int32; + iBoundaryb : mng_int32; + iCount : mng_uint32; + pSyncids : mng_uint32p ) : mng_retcode; stdcall; + +function mng_putchunk_move ( hHandle : mng_handle; + iFirstid : mng_uint16; + iLastid : mng_uint16; + iMovetype : mng_uint8; + iMovex : mng_int32; + iMovey : mng_int32 ) : mng_retcode; stdcall; + +function mng_putchunk_clip ( hHandle : mng_handle; + iFirstid : mng_uint16; + iLastid : mng_uint16; + iCliptype : mng_uint8; + iClipl : mng_int32; + iClipr : mng_int32; + iClipt : mng_int32; + iClipb : mng_int32 ) : mng_retcode; stdcall; + +function mng_putchunk_show ( hHandle : mng_handle; + bEmpty : mng_bool; + iFirstid : mng_uint16; + iLastid : mng_uint16; + iMode : mng_uint8 ) : mng_retcode; stdcall; + +function mng_putchunk_term ( hHandle : mng_handle; + iTermaction : mng_uint8; + iIteraction : mng_uint8; + iDelay : mng_uint32; + iItermax : mng_uint32 ) : mng_retcode; stdcall; + +function mng_putchunk_save ( hHandle : mng_handle; + bEmpty : mng_bool; + iOffsettype : mng_uint8; + iCount : mng_uint32 ) : mng_retcode; stdcall; + +function mng_putchunk_save_entry ( hHandle : mng_handle; + iEntry : mng_uint32; + iEntrytype : mng_uint8; + iOffset : mng_uint32arr2; + iStarttime : mng_uint32arr2; + iLayernr : mng_uint32; + iFramenr : mng_uint32; + iNamesize : mng_uint32; + zName : mng_pchar ) : mng_retcode; stdcall; + +function mng_putchunk_seek ( hHandle : mng_handle; + iNamesize : mng_uint32; + zName : mng_pchar ) : mng_retcode; stdcall; + +function mng_putchunk_expi ( hHandle : mng_handle; + iSnapshotid : mng_uint16; + iNamesize : mng_uint32; + zName : mng_pchar ) : mng_retcode; stdcall; + +function mng_putchunk_fpri ( hHandle : mng_handle; + iDeltatype : mng_uint8; + iPriority : mng_uint8 ) : mng_retcode; stdcall; + +function mng_putchunk_need ( hHandle : mng_handle; + iKeywordssize : mng_uint32; + zKeywords : mng_pchar ) : mng_retcode; stdcall; + +function mng_putchunk_phyg ( hHandle : mng_handle; + bEmpty : mng_bool; + iSizex : mng_uint32; + iSizey : mng_uint32; + iUnit : mng_uint8 ) : mng_retcode; stdcall; + +function mng_putchunk_jhdr ( hHandle : mng_handle; + iWidth : mng_uint32; + iHeight : mng_uint32; + iColortype : mng_uint8; + iImagesampledepth : mng_uint8; + iImagecompression : mng_uint8; + iImageinterlace : mng_uint8; + iAlphasampledepth : mng_uint8; + iAlphacompression : mng_uint8; + iAlphafilter : mng_uint8; + iAlphainterlace : mng_uint8 ) : mng_retcode; stdcall; + +function mng_putchunk_jdat ( hHandle : mng_handle; + iRawlen : mng_uint32; + pRawdata : mng_ptr ) : mng_retcode; stdcall; + +function mng_putchunk_dhdr ( hHandle : mng_handle; + iObjectid : mng_uint16; + iImagetype : mng_uint8; + iDeltatype : mng_uint8; + iBlockwidth : mng_uint32; + iBlockheight : mng_uint32; + iBlockx : mng_uint32; + iBlocky : mng_uint32 ) : mng_retcode; stdcall; + +function mng_putchunk_prom ( hHandle : mng_handle; + iColortype : mng_uint8; + iSampledepth : mng_uint8; + iFilltype : mng_uint8 ) : mng_retcode; stdcall; + +function mng_putchunk_pplt ( hHandle : mng_handle; + iCount : mng_uint32 ) : mng_retcode; stdcall; + +function mng_putchunk_pplt_entry ( hHandle : mng_handle; + iEntry : mng_uint32; + iRed : mng_uint16; + iGreen : mng_uint16; + iBlue : mng_uint16; + iAlpha : mng_uint16; + bUsed : mng_bool ) : mng_retcode; stdcall; + +function mng_putchunk_drop ( hHandle : mng_handle; + iCount : mng_uint32; + pChunknames : mng_chunkidp ) : mng_retcode; stdcall; + +function mng_putchunk_dbyk ( hHandle : mng_handle; + iChunkname : mng_chunkid; + iPolarity : mng_uint8; + iKeywordssize : mng_uint32; + zKeywords : mng_pchar ) : mng_retcode; stdcall; + +function mng_putchunk_ordr ( hHandle : mng_handle; + iCount : mng_uint32 ) : mng_retcode; stdcall; + +function mng_putchunk_ordr_entry ( hHandle : mng_handle; + iEntry : mng_uint32; + iChunkname : mng_chunkid; + iOrdertype : mng_uint8 ) : mng_retcode; stdcall; + +function mng_putchunk_unknown ( hHandle : mng_handle; + iChunkname : mng_chunkid; + iRawlen : mng_uint32; + pRawdata : mng_ptr ) : mng_retcode; stdcall; + +{****************************************************************************} + +function mng_updatemngheader ( hHandle : mng_handle; + iFramecount : mng_uint32; + iLayercount : mng_uint32; + iPlaytime : mng_uint32 ) : mng_retcode; stdcall; + +function mng_updatemngsimplicity ( hHandle : mng_handle; + iSimplicity : mng_uint32 ) : mng_retcode; stdcall; + +{****************************************************************************} + +const MNG_NOERROR = 0; + + MNG_OUTOFMEMORY = 1; + MNG_INVALIDHANDLE = 2; + MNG_NOCALLBACK = 3; + MNG_UNEXPECTEDEOF = 4; + MNG_ZLIBERROR = 5; + MNG_JPEGERROR = 6; + MNG_LCMSERROR = 7; + MNG_NOOUTPUTPROFILE = 8; + MNG_NOSRGBPROFILE = 9; + MNG_BUFOVERFLOW = 10; + MNG_FUNCTIONINVALID = 11; + MNG_OUTPUTERROR = 12; + MNG_JPEGBUFTOOSMALL = 13; + MNG_NEEDMOREDATA = 14; + MNG_NEEDTIMERWAIT = 15; + MNG_NEEDSECTIONWAIT = 16; + + MNG_APPIOERROR = 901; + MNG_APPTIMERERROR = 902; + MNG_APPCMSERROR = 903; + MNG_APPMISCERROR = 904; + MNG_APPTRACEABORT = 905; + + MNG_INTERNALERROR = 999; + + MNG_INVALIDSIG = 1025; + MNG_INVALIDCRC = 1027; + MNG_INVALIDLENGTH = 1028; + MNG_SEQUENCEERROR = 1029; + MNG_CHUNKNOTALLOWED = 1030; + MNG_MULTIPLEERROR = 1031; + MNG_PLTEMISSING = 1032; + MNG_IDATMISSING = 1033; + MNG_CANNOTBEEMPTY = 1034; + MNG_GLOBALLENGTHERR = 1035; + MNG_INVALIDBITDEPTH = 1036; + MNG_INVALIDCOLORTYPE = 1037; + MNG_INVALIDCOMPRESS = 1038; + MNG_INVALIDFILTER = 1039; + MNG_INVALIDINTERLACE = 1040; + MNG_NOTENOUGHIDAT = 1041; + MNG_PLTEINDEXERROR = 1042; + MNG_NULLNOTFOUND = 1043; + MNG_KEYWORDNULL = 1044; + MNG_OBJECTUNKNOWN = 1045; + MNG_OBJECTEXISTS = 1046; + MNG_TOOMUCHIDAT = 1047; + MNG_INVSAMPLEDEPTH = 1048; + MNG_INVOFFSETSIZE = 1049; + MNG_INVENTRYTYPE = 1050; + MNG_ENDWITHNULL = 1051; + MNG_INVIMAGETYPE = 1052; + MNG_INVDELTATYPE = 1053; + MNG_INVALIDINDEX = 1054; + MNG_TOOMUCHJDAT = 1055; + MNG_JPEGPARMSERR = 1056; + MNG_INVFILLMETHOD = 1057; + MNG_OBJNOTCONCRETE = 1058; + MNG_TARGETNOALPHA = 1059; + MNG_MNGTOOCOMPLEX = 1060; + MNG_UNKNOWNCRITICAL = 1061; + MNG_UNSUPPORTEDNEED = 1062; + MNG_INVALIDDELTA = 1063; + MNG_INVALIDMETHOD = 1064; + MNG_IMPROBABLELENGTH = 1065; + MNG_INVALIDBLOCK = 1066; + MNG_INVALIDEVENT = 1067; + MNG_INVALIDMASK = 1068; + MNG_NOMATCHINGLOOP = 1069; + MNG_SEEKNOTFOUND = 1070; + + MNG_INVALIDCNVSTYLE = 2049; + MNG_WRONGCHUNK = 2050; + MNG_INVALIDENTRYIX = 2051; + MNG_NOHEADER = 2052; + MNG_NOCORRCHUNK = 2053; + MNG_NOMHDR = 2054; + + MNG_IMAGETOOLARGE = 4097; + MNG_NOTANANIMATION = 4098; + MNG_FRAMENRTOOHIGH = 4099; + MNG_LAYERNRTOOHIGH = 4100; + MNG_PLAYTIMETOOHIGH = 4101; + MNG_FNNOTIMPLEMENTED = 4102; + + MNG_IMAGEFROZEN = 8193; + +{****************************************************************************} + +const MNG_CANVAS_RGB8 = $00000000; + MNG_CANVAS_RGBA8 = $00001000; + MNG_CANVAS_ARGB8 = $00003000; + MNG_CANVAS_RGB8_A8 = $00005000; + MNG_CANVAS_BGR8 = $00000001; + MNG_CANVAS_BGRX8 = $00010001; + MNG_CANVAS_BGRA8 = $00001001; + MNG_CANVAS_ABGR8 = $00003001; + MNG_CANVAS_RGB16 = $00000100; { not supported yet } + MNG_CANVAS_RGBA16 = $00001100; { not supported yet } + MNG_CANVAS_ARGB16 = $00003100; { not supported yet } + MNG_CANVAS_BGR16 = $00000101; { not supported yet } + MNG_CANVAS_BGRA16 = $00001101; { not supported yet } + MNG_CANVAS_ABGR16 = $00003101; { not supported yet } + MNG_CANVAS_GRAY8 = $00000002; { not supported yet } + MNG_CANVAS_GRAY16 = $00000102; { not supported yet } + MNG_CANVAS_GRAYA8 = $00001002; { not supported yet } + MNG_CANVAS_GRAYA16 = $00001102; { not supported yet } + MNG_CANVAS_AGRAY8 = $00003002; { not supported yet } + MNG_CANVAS_AGRAY16 = $00003102; { not supported yet } + MNG_CANVAS_DX15 = $00000003; { not supported yet } + MNG_CANVAS_DX16 = $00000004; { not supported yet } + +{****************************************************************************} + +const MNG_UINT_HUH = $40404040; + + MNG_UINT_BACK = $4241434b; + MNG_UINT_BASI = $42415349; + MNG_UINT_CLIP = $434c4950; + MNG_UINT_CLON = $434c4f4e; + MNG_UINT_DBYK = $4442594b; + MNG_UINT_DEFI = $44454649; + MNG_UINT_DHDR = $44484452; + MNG_UINT_DISC = $44495343; + MNG_UINT_DROP = $44524f50; + MNG_UINT_ENDL = $454e444c; + MNG_UINT_FRAM = $4652414d; + MNG_UINT_IDAT = $49444154; + MNG_UINT_IEND = $49454e44; + MNG_UINT_IHDR = $49484452; + MNG_UINT_IJNG = $494a4e47; + MNG_UINT_IPNG = $49504e47; + MNG_UINT_JDAT = $4a444154; + MNG_UINT_JHDR = $4a484452; + MNG_UINT_JSEP = $4a534550; + MNG_UINT_LOOP = $4c4f4f50; + MNG_UINT_MEND = $4d454e44; + MNG_UINT_MHDR = $4d484452; + MNG_UINT_MOVE = $4d4f5645; + MNG_UINT_ORDR = $4f524452; + MNG_UINT_PAST = $50415354; + MNG_UINT_PLTE = $504c5445; + MNG_UINT_PPLT = $50504c54; + MNG_UINT_PROM = $50524f4d; + MNG_UINT_SAVE = $53415645; + MNG_UINT_SEEK = $5345454b; + MNG_UINT_SHOW = $53484f57; + MNG_UINT_TERM = $5445524d; + MNG_UINT_bKGD = $624b4744; + MNG_UINT_cHRM = $6348524d; + MNG_UINT_eXPI = $65585049; + MNG_UINT_fPRI = $66505249; + MNG_UINT_gAMA = $67414d41; + MNG_UINT_hIST = $68495354; + MNG_UINT_iCCP = $69434350; + MNG_UINT_iTXt = $69545874; + MNG_UINT_nEED = $6e454544; + MNG_UINT_oFFs = $6f464673; + MNG_UINT_pCAL = $7043414c; + MNG_UINT_pHYg = $70444167; + MNG_UINT_pHYs = $70485973; + MNG_UINT_sBIT = $73424954; + MNG_UINT_sCAL = $7343414c; + MNG_UINT_sPLT = $73504c54; + MNG_UINT_sRGB = $73524742; + MNG_UINT_tEXt = $74455874; + MNG_UINT_tIME = $74494d45; + MNG_UINT_tRNS = $74524e53; + MNG_UINT_zTXt = $7a545874; + + MNG_UINT_evNT = $65764e54; + +{****************************************************************************} + +implementation + +{****************************************************************************} + +const mngdll = 'libmng.dll'; + +{****************************************************************************} + +function mng_initialize; external mngdll; +function mng_reset; external mngdll; +function mng_cleanup; external mngdll; + +function mng_read; external mngdll; +function mng_read_pushdata; external mngdll; +function mng_read_pushsig; external mngdll; +function mng_read_pushchunk; external mngdll; +function mng_read_resume; external mngdll; +function mng_write; external mngdll; +function mng_create; external mngdll; + +function mng_readdisplay; external mngdll; +function mng_display; external mngdll; +function mng_display_resume; external mngdll; +function mng_display_freeze; external mngdll; +function mng_display_reset; external mngdll; +function mng_display_goframe; external mngdll; +function mng_display_golayer; external mngdll; +function mng_display_gotime; external mngdll; + +function mng_trapevent; external mngdll; + +function mng_getlasterror; external mngdll; + +{****************************************************************************} + +function mng_setcb_memalloc; external mngdll; +function mng_setcb_memfree; external mngdll; +function mng_setcb_releasedata; external mngdll; + +function mng_setcb_openstream; external mngdll; +function mng_setcb_closestream; external mngdll; + +function mng_setcb_readdata; external mngdll; + +function mng_setcb_writedata; external mngdll; + +function mng_setcb_errorproc; external mngdll; +function mng_setcb_traceproc; external mngdll; + +function mng_setcb_processheader; external mngdll; +function mng_setcb_processtext; external mngdll; + +function mng_setcb_getcanvasline; external mngdll; +function mng_setcb_getalphaline; external mngdll; +function mng_setcb_getbkgdline; external mngdll; +function mng_setcb_refresh; external mngdll; + +function mng_setcb_gettickcount; external mngdll; +function mng_setcb_settimer; external mngdll; + +function mng_setcb_processgamma; external mngdll; +function mng_setcb_processchroma; external mngdll; +function mng_setcb_processsrgb; external mngdll; +function mng_setcb_processiccp; external mngdll; +function mng_setcb_processarow; external mngdll; + +{****************************************************************************} + +function mng_getcb_memalloc; external mngdll; +function mng_getcb_memfree; external mngdll; +function mng_getcb_releasedata; external mngdll; + +function mng_getcb_openstream; external mngdll; +function mng_getcb_closestream; external mngdll; + +function mng_getcb_readdata; external mngdll; + +function mng_getcb_writedata; external mngdll; + +function mng_getcb_errorproc; external mngdll; +function mng_getcb_traceproc; external mngdll; + +function mng_getcb_processheader; external mngdll; +function mng_getcb_processtext; external mngdll; + +function mng_getcb_getcanvasline; external mngdll; +function mng_getcb_getalphaline; external mngdll; +function mng_getcb_getbkgdline; external mngdll; +function mng_getcb_refresh; external mngdll; + +function mng_getcb_gettickcount; external mngdll; +function mng_getcb_settimer; external mngdll; + +function mng_getcb_processgamma; external mngdll; +function mng_getcb_processchroma; external mngdll; +function mng_getcb_processsrgb; external mngdll; +function mng_getcb_processiccp; external mngdll; +function mng_getcb_processarow; external mngdll; + +{****************************************************************************} + +function mng_set_userdata; external mngdll; + +function mng_set_canvasstyle; external mngdll; +function mng_set_bkgdstyle; external mngdll; + +function mng_set_bgcolor; external mngdll; +function mng_set_usebkgd; external mngdll; + +function mng_set_storechunks; external mngdll; +function mng_set_cacheplayback; external mngdll; + +// function mng_set_viewgamma; external mngdll; +// function mng_set_displaygamma; external mngdll; +// function mng_set_dfltimggamma; external mngdll; +function mng_set_viewgammaint; external mngdll; +function mng_set_displaygammaint; external mngdll; +function mng_set_dfltimggammaint; external mngdll; + +function mng_set_srgb; external mngdll; +function mng_set_outputprofile; external mngdll; +function mng_set_outputprofile2; external mngdll; +function mng_set_srgbprofile; external mngdll; +function mng_set_srgbprofile2; external mngdll; + +function mng_set_maxcanvaswidth; external mngdll; +function mng_set_maxcanvasheight; external mngdll; +function mng_set_maxcanvassize; external mngdll; + +function mng_set_suspensionmode; external mngdll; +function mng_set_speed; external mngdll; +function mng_set_crcmode; external mngdll; + +{****************************************************************************} + +function mng_get_userdata; external mngdll; + +function mng_get_sigtype; external mngdll; +function mng_get_imagetype; external mngdll; +function mng_get_imagewidth; external mngdll; +function mng_get_imageheight; external mngdll; +function mng_get_ticks; external mngdll; +function mng_get_framecount; external mngdll; +function mng_get_layercount; external mngdll; +function mng_get_playtime; external mngdll; +function mng_get_simplicity; external mngdll; + +function mng_get_canvasstyle; external mngdll; +function mng_get_bkgdstyle; external mngdll; + +procedure mng_get_bgcolor; external mngdll; +function mng_get_usebkgd; external mngdll; + +function mng_get_storechunks; external mngdll; +function mng_get_cacheplayback; external mngdll; + +// function mng_get_viewgamma; external mngdll; +// function mng_get_displaygamma; external mngdll; +// function mng_get_dfltimggamma; external mngdll; +function mng_get_viewgammaint; external mngdll; +function mng_get_displaygammaint; external mngdll; +function mng_get_dfltimggammaint; external mngdll; + +function mng_get_srgb; external mngdll; + +function mng_get_maxcanvaswidth; external mngdll; +function mng_get_maxcanvasheight; external mngdll; + +function mng_get_suspensionmode; external mngdll; + +function mng_get_speed; external mngdll; +function mng_get_imagelevel; external mngdll; + +function mng_get_starttime; external mngdll; +function mng_get_runtime; external mngdll; +function mng_get_currentframe; external mngdll; +function mng_get_currentlayer; external mngdll; +function mng_get_currentplaytime; external mngdll; +function mng_get_totalframes; external mngdll; +function mng_get_totallayers; external mngdll; +function mng_get_totalplaytime; external mngdll; + +function mng_status_error; external mngdll; +function mng_status_reading; external mngdll; +function mng_status_suspendbreak; external mngdll; +function mng_status_creating; external mngdll; +function mng_status_writing; external mngdll; +function mng_status_displaying; external mngdll; +function mng_status_running; external mngdll; +function mng_status_timerbreak; external mngdll; +function mng_status_dynamic; external mngdll; + +{****************************************************************************} + +function mng_iterate_chunks; external mngdll; + +{****************************************************************************} + +function mng_getchunk_ihdr; external mngdll; +function mng_getchunk_plte; external mngdll; +function mng_getchunk_idat; external mngdll; +function mng_getchunk_trns; external mngdll; +function mng_getchunk_gama; external mngdll; +function mng_getchunk_chrm; external mngdll; +function mng_getchunk_srgb; external mngdll; +function mng_getchunk_iccp; external mngdll; +function mng_getchunk_text; external mngdll; +function mng_getchunk_ztxt; external mngdll; +function mng_getchunk_itxt; external mngdll; +function mng_getchunk_bkgd; external mngdll; +function mng_getchunk_phys; external mngdll; +function mng_getchunk_sbit; external mngdll; +function mng_getchunk_splt; external mngdll; +function mng_getchunk_hist; external mngdll; +function mng_getchunk_time; external mngdll; +function mng_getchunk_mhdr; external mngdll; +function mng_getchunk_loop; external mngdll; +function mng_getchunk_endl; external mngdll; +function mng_getchunk_defi; external mngdll; +function mng_getchunk_basi; external mngdll; +function mng_getchunk_clon; external mngdll; +function mng_getchunk_past; external mngdll; +function mng_getchunk_past_src; external mngdll; +function mng_getchunk_disc; external mngdll; +function mng_getchunk_back; external mngdll; +function mng_getchunk_fram; external mngdll; +function mng_getchunk_move; external mngdll; +function mng_getchunk_clip; external mngdll; +function mng_getchunk_show; external mngdll; +function mng_getchunk_term; external mngdll; +function mng_getchunk_save; external mngdll; +function mng_getchunk_save_entry; external mngdll; +function mng_getchunk_seek; external mngdll; +function mng_getchunk_expi; external mngdll; +function mng_getchunk_fpri; external mngdll; +function mng_getchunk_need; external mngdll; +function mng_getchunk_phyg; external mngdll; +function mng_getchunk_jhdr; external mngdll; +function mng_getchunk_jdat; external mngdll; +function mng_getchunk_dhdr; external mngdll; +function mng_getchunk_prom; external mngdll; +function mng_getchunk_pplt; external mngdll; +function mng_getchunk_pplt_entry; external mngdll; +function mng_getchunk_drop; external mngdll; +function mng_getchunk_dbyk; external mngdll; +function mng_getchunk_ordr; external mngdll; +function mng_getchunk_ordr_entry; external mngdll; +function mng_getchunk_unknown; external mngdll; + +{****************************************************************************} + +function mng_putchunk_ihdr; external mngdll; +function mng_putchunk_plte; external mngdll; +function mng_putchunk_idat; external mngdll; +function mng_putchunk_iend; external mngdll; +function mng_putchunk_trns; external mngdll; +function mng_putchunk_gama; external mngdll; +function mng_putchunk_chrm; external mngdll; +function mng_putchunk_srgb; external mngdll; +function mng_putchunk_iccp; external mngdll; +function mng_putchunk_text; external mngdll; +function mng_putchunk_ztxt; external mngdll; +function mng_putchunk_itxt; external mngdll; +function mng_putchunk_bkgd; external mngdll; +function mng_putchunk_phys; external mngdll; +function mng_putchunk_sbit; external mngdll; +function mng_putchunk_splt; external mngdll; +function mng_putchunk_hist; external mngdll; +function mng_putchunk_time; external mngdll; +function mng_putchunk_mhdr; external mngdll; +function mng_putchunk_mend; external mngdll; +function mng_putchunk_loop; external mngdll; +function mng_putchunk_endl; external mngdll; +function mng_putchunk_defi; external mngdll; +function mng_putchunk_basi; external mngdll; +function mng_putchunk_clon; external mngdll; +function mng_putchunk_past; external mngdll; +function mng_putchunk_past_src; external mngdll; +function mng_putchunk_disc; external mngdll; +function mng_putchunk_back; external mngdll; +function mng_putchunk_fram; external mngdll; +function mng_putchunk_move; external mngdll; +function mng_putchunk_clip; external mngdll; +function mng_putchunk_show; external mngdll; +function mng_putchunk_term; external mngdll; +function mng_putchunk_save; external mngdll; +function mng_putchunk_save_entry; external mngdll; +function mng_putchunk_seek; external mngdll; +function mng_putchunk_expi; external mngdll; +function mng_putchunk_fpri; external mngdll; +function mng_putchunk_need; external mngdll; +function mng_putchunk_phyg; external mngdll; +function mng_putchunk_jhdr; external mngdll; +function mng_putchunk_jdat; external mngdll; +function mng_putchunk_dhdr; external mngdll; +function mng_putchunk_prom; external mngdll; +function mng_putchunk_pplt; external mngdll; +function mng_putchunk_pplt_entry; external mngdll; +function mng_putchunk_drop; external mngdll; +function mng_putchunk_dbyk; external mngdll; +function mng_putchunk_ordr; external mngdll; +function mng_putchunk_ordr_entry; external mngdll; +function mng_putchunk_unknown; external mngdll; + +{****************************************************************************} + +function mng_updatemngheader; external mngdll; +function mng_updatemngsimplicity; external mngdll; + +{****************************************************************************} + +end. diff --git a/Engine/lib/lmng/contrib/delphi/mngview/Main.dfm b/Engine/lib/lmng/contrib/delphi/mngview/Main.dfm new file mode 100644 index 000000000..cb86b81c6 Binary files /dev/null and b/Engine/lib/lmng/contrib/delphi/mngview/Main.dfm differ diff --git a/Engine/lib/lmng/contrib/delphi/mngview/Main.pas b/Engine/lib/lmng/contrib/delphi/mngview/Main.pas new file mode 100644 index 000000000..daf04cee5 --- /dev/null +++ b/Engine/lib/lmng/contrib/delphi/mngview/Main.pas @@ -0,0 +1,692 @@ +unit Main; + +interface + +uses + Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, + Menus, StdCtrls, ExtCtrls, Buttons, + libmng; + +{****************************************************************************} +{* For conditions of distribution and use, *} +{* see copyright notice in libmng.pas *} +{****************************************************************************} +{* *} +{* project : libmng *} +{* file : main.pas copyright (c) 2000-2002 G.Juyn *} +{* version : 1.0.5 *} +{* *} +{* purpose : Main form for mngview application *} +{* *} +{* author : G.Juyn *} +{* web : http://www.3-t.com *} +{* email : mailto:info@3-t.com *} +{* *} +{* comment : this is the heart of the mngview applciation *} +{* *} +{* changes : 0.5.1 - 05/02/2000 - G.Juyn *} +{* - added this version block *} +{* - made the initialization part more robust *} +{* eg. program aborts on initialization errors *} +{* - B002(105797) - added check for existence of default sRGB *} +{* profile (now included in distribution) *} +{* - added mng_cleanup to program exit *} +{* 0.5.1 - 05/08/2000 - G.Juyn *} +{* - changed to stdcall convention *} +{* 0.5.1 - 05/11/2000 - G.Juyn *} +{* - changed callback function declarations *} +{* *} +{* 0.5.3 - 06/16/2000 - G.Juyn *} +{* - removed processmessages call from refresh callback *} +{* 0.5.3 - 06/17/2000 - G.Juyn *} +{* - switched "storechunks" off *} +{* 0.5.3 - 06/26/2000 - G.Juyn *} +{* - changed definition of userdata to mng_ptr *} +{* 0.5.3 - 06/28/2000 - G.Juyn *} +{* - changed the default icon to something more appropriate *} +{* - changed definition of memory alloc size to mng_size_t *} +{* 0.5.3 - 06/29/2000 - G.Juyn *} +{* - changed order of refresh parameters *} +{* *} +{* 0.9.0 - 06/30/2000 - G.Juyn *} +{* - changed refresh parameters to 'x,y,width,height' *} +{* *} +{* 0.9.1 - 07/08/2000 - G.Juyn *} +{* - fixed to use returncode constants *} +{* - changed to accomodate MNG_NEEDTIMERWAIT returncode *} +{* 0.9.1 - 07/10/2000 - G.Juyn *} +{* - changed to use suspension-mode *} +{* *} +{* 0.9.3 - 09/11/2000 - G.Juyn *} +{* - removed some tesst-stuff *} +{* *} +{* 1.0.1 - 05/02/2000 - G.Juyn *} +{* - removed loading default sRGB profile (auto in libmng) *} +{* *} +{* 1.0.5 - 09/16/2002 - G.Juyn *} +{* - added dynamic MNG features *} +{* 1.0.5 - 11/27/2002 - G.Juyn *} +{* - fixed freeze during read-cycle *} +{* *} +{****************************************************************************} + +type + TMainForm = class(TForm) + + OFMainMenu: TMainMenu; + OFMenuFile: TMenuItem; + OFMenuFileOpen: TMenuItem; + OFMenuFileProfile: TMenuItem; + OFMenuFileN1: TMenuItem; + OFMenuFileExit: TMenuItem; + + OFMenuOptions: TMenuItem; + OFMenuOptionsModemSpeed: TMenuItem; + OFMenuOptionsModem28k8: TMenuItem; + OFMenuOptionsModem33k6: TMenuItem; + OFMenuOptionsModem56k: TMenuItem; + OFMenuOptionsModemISDN64: TMenuItem; + OFMenuOptionsModemISDN128: TMenuItem; + OFMenuOptionsModemCable512: TMenuItem; + OFMenuOptionsModemUnlimited: TMenuItem; + + OFTimer: TTimer; + + OFOpenDialog: TOpenDialog; + OFOpenDialogProfile: TOpenDialog; + + OFImage: TImage; + + procedure FormCreate(Sender: TObject); + procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); + procedure FormShow(Sender: TObject); + procedure FormResize(Sender: TObject); + procedure FormKeyDown(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, + Y: Integer); + + procedure OFImageMouseMove(Sender: TObject; Shift: TShiftState; X, + Y: Integer); + procedure OFImageMouseDown(Sender: TObject; Button: TMouseButton; + Shift: TShiftState; X, Y: Integer); + procedure OFImageMouseUp(Sender: TObject; Button: TMouseButton; + Shift: TShiftState; X, Y: Integer); + + procedure OFTimerTimer(Sender: TObject); + + procedure OFMenuFileOpenClick(Sender: TObject); + procedure OFMenuFileProfileClick(Sender: TObject); + procedure OFMenuFileExitClick(Sender: TObject); + + procedure OFMenuOptionsModemSpeedClick(Sender: TObject); + procedure OFMenuOptionsModemXClick(Sender: TObject); + + private + { Private declarations } + + SFFileName : string; { filename of the input stream } + OFFile : TFileStream; { input stream } + IFHandle : mng_handle; { the libray handle } + OFBitmap : TBitmap; { drawing canvas } + BFCancelled : boolean; { or app-exit } + BFHasMouse : boolean; { mouse is/was over image } + + IFTicks : cardinal; { used to fake slow connections } + IFBytes : cardinal; + IFBytesPerSec : integer; + + procedure MNGerror (SHMsg : string); + + public + { Public declarations } + + end; + +var + MainForm: TMainForm; + +{****************************************************************************} + +implementation + +{$R *.DFM} + +{****************************************************************************} + +{$F+} +function Memalloc (iLen : mng_uint32) : mng_ptr; stdcall; +{$F-} +begin + getmem (Result, iLen); { get memory from the heap } + fillchar (Result^, iLen, 0); { and initialize it } +end; + +{****************************************************************************} + +{$F+} +procedure Memfree (iPtr : mng_ptr; + iLen : mng_size_t); stdcall; +{$F-} +begin + freemem (iPtr, iLen); { free the memory } +end; + +{****************************************************************************} + +{$F+} +function Openstream (hHandle : mng_handle) : mng_bool; stdcall; +{$F-} + +var OHForm : TMainForm; + +begin { get a fix on our form } + OHForm := TMainForm (mng_get_userdata (hHandle)); + + with OHFORM do + begin + if OFFile <> nil then { free previous stream (if any) } + OFFile.Free; + { open a new stream } + OFFile := TFileStream.Create (SFFileName, fmOpenRead or fmShareDenyWrite); + end; + + Result := MNG_TRUE; +end; + +{****************************************************************************} + +{$F+} +function Closestream (hHandle : mng_handle) : mng_bool; stdcall; +{$F-} + +var OHForm : TMainForm; + +begin { get a fix on our form } + OHForm := TMainForm (mng_get_userdata (hHandle)); + + with OHFORM do + begin + OFFile.Free; { cleanup the stream } + OFFile := nil; { don't use it again ! } + end; + + Result := MNG_TRUE; +end; + +{****************************************************************************} + +{$F+} +function Readdata ( hHandle : mng_handle; + pBuf : mng_ptr; + iBuflen : mng_uint32; + var pRead : mng_uint32) : mng_bool; stdcall; +{$F-} + +var OHForm : TMainForm; + IHTicks : cardinal; + IHByte1 : cardinal; + IHByte2 : cardinal; + IHBytesPerSec : cardinal; + +begin + { get a fix on our form } + OHForm := TMainForm (mng_get_userdata (hHandle)); + + with OHForm do + begin { are we at EOF ? } + if OFFile.Position >= OFFile.Size then + begin + pRead := 0; { indicate so } + end + else + begin + IHBytesPerSec := IFBytesPerSec; { fake a slow connection } + + if IHBytesPerSec > 0 then + begin + IHTicks := Windows.GetTickCount; + IHByte1 := round (((IHTicks - IFTicks) / 1000) * IHBytesPerSec); + IHByte2 := (IFBytes + iBuflen); + + if ((IHByte2 - IHByte1) div IHBytesPerSec) > 10 then + Windows.Sleep ((IHByte2 - IHByte1) div IHBytesPerSec); + + end; + { read the requested data } + pRead := OFFile.Read (pBuf^, iBuflen); + IFBytes := IFBytes + pRead; + end; + end; + + Result := MNG_TRUE; +end; + +{****************************************************************************} + +{$F+} +function ProcessHeader (hHandle : mng_handle; + iWidth : mng_uint32; + iHeight : mng_uint32) : mng_bool; stdcall; +{$F-} + +var OHForm : TMainForm; + +begin { get a fix on our form } + OHForm := TMainForm (mng_get_userdata (hHandle)); + + with OHForm do + begin + OFBitmap.Width := iWidth; { store the new dimensions } + OFBitmap.Height := iHeight; + OFImage.Left := 0; { adjust the visible component } + OFImage.Top := 0; + OFImage.Width := iWidth; + OFImage.Height := iHeight; + + FormResize (OHForm); { force re-centering the image} + { clear the canvas & draw an outline } + OFBitmap.Canvas.Brush.Color := clGray; + OFBitmap.Canvas.Brush.Style := bsSolid; + OFBitmap.Canvas.FillRect (OFBitmap.Canvas.ClipRect); + OFBitmap.Canvas.Brush.Color := clRed; + OFBitmap.Canvas.Brush.Style := bsSolid; + OFBitmap.Canvas.Pen.Color := clRed; + OFBitmap.Canvas.Pen.Style := psSolid; + OFBitmap.Canvas.FrameRect (OFBitmap.Canvas.ClipRect); + + OFImage.Picture.Assign (OFBitmap); { make sure it gets out there } + { tell the library we want funny windows-bgr} + if mng_set_canvasstyle (hHandle, MNG_CANVAS_BGRX8) <> 0 then + MNGerror ('libmng reported an error setting the canvas style'); + + end; + + Result := MNG_TRUE; +end; + +{****************************************************************************} + +{$F+} +function GetCanvasLine (hHandle : mng_handle; + iLinenr : mng_uint32) : mng_ptr; stdcall; +{$F-} + +var OHForm : TMainForm; + +begin { get a fix on our form } + OHForm := TMainForm (mng_get_userdata (hHandle)); + { easy with these bitmap objects ! } + Result := OHForm.OFBitmap.ScanLine [iLinenr]; +end; + +{****************************************************************************} + +{$F+} +function ImageRefresh (hHandle : mng_handle; + iX : mng_uint32; + iY : mng_uint32; + iWidth : mng_uint32; + iHeight : mng_uint32) : mng_bool; stdcall; +{$F-} + +var OHForm : TMainForm; + +begin { get a fix on our form } + OHForm := TMainForm (mng_get_userdata (hHandle)); + { force redraw } + OHForm.OFImage.Picture.Assign (OHForm.OFBitmap); + + Result := MNG_TRUE; +end; + + +{****************************************************************************} + +{$F+} +function GetTickCount (hHandle : mng_handle) : mng_uint32; stdcall; +{$F-} +begin + Result := Windows.GetTickCount; { windows knows that } +end; + +{****************************************************************************} + +{$F+} +function SetTimer (hHandle : mng_handle; + iMsecs : mng_uint32) : mng_bool; stdcall; +{$F-} + +var OHForm : TMainForm; + +begin { get a fix on our form } + OHForm := TMainForm (mng_get_userdata (hHandle)); + OHForm.OFTimer.Interval := iMsecs; { and set the timer } + OHForm.OFTimer.Enabled := true; + + Result := MNG_TRUE; +end; + +{****************************************************************************} + +procedure TMainForm.FormCreate(Sender: TObject); + +var IHRed, IHGreen, IHBlue : word; + +begin { initialize } + OFBitmap := TBitmap.Create; + IFBytesPerSec := 10000000; + BFHasMouse := false; + OFFile := nil; + + OFOpenDialog.Initialdir := ''; + OFBitmap.HandleType := bmDIB; { make it a 24-bit DIB } + OFBitmap.PixelFormat := pf32bit; + { now initialize the library } + IFHandle := mng_initialize (mng_ptr(self), Memalloc, Memfree, nil); + + if IFHandle = NIL then + begin + MNGerror ('libmng initialization error' + #13#10 + + 'Program aborted'); + Windows.Postmessage (handle, WM_Close, 0, 0); + Exit; + end; + { no need to store chunk-info ! } + mng_set_storechunks (IFHandle, MNG_FALSE); + { do not use suspension-buffer } + mng_set_suspensionmode (IFHandle, MNG_FALSE); + { set all the callbacks } + if (mng_setcb_openstream (IFHandle, Openstream ) <> MNG_NOERROR) or + (mng_setcb_closestream (IFHandle, Closestream ) <> MNG_NOERROR) or + (mng_setcb_readdata (IFHandle, Readdata ) <> MNG_NOERROR) or + (mng_setcb_processheader (IFHandle, ProcessHeader) <> MNG_NOERROR) or + (mng_setcb_getcanvasline (IFHandle, GetCanvasLine) <> MNG_NOERROR) or + (mng_setcb_refresh (IFHandle, ImageRefresh ) <> MNG_NOERROR) or + (mng_setcb_gettickcount (IFHandle, GetTickCount ) <> MNG_NOERROR) or + (mng_setcb_settimer (IFHandle, SetTimer ) <> MNG_NOERROR) then + begin + MNGerror ('libmng reported an error setting a callback function!' + #13#10 + + 'Program aborted'); + Windows.Postmessage (handle, WM_Close, 0, 0); + Exit; + end; + + IHRed := (Color ) and $FF; { supply our own bg-color } + IHGreen := (Color shr 8) and $FF; + IHBlue := (Color shr 16) and $FF; + + IHRed := (IHRed shl 8) + IHRed; + IHGreen := (IHGreen shl 8) + IHGreen; + IHBlue := (IHBlue shl 8) + IHBlue; + + if mng_set_bgcolor (IFHandle, IHRed, IHGreen, IHBlue) <> MNG_NOERROR then + MNGerror ('libmng reported an error setting the background color!'); + +end; + +{****************************************************************************} + +procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean); +begin + OFTimer.Enabled := false; + BFCancelled := true; + { if we're still animating then stop it } + if mng_status_running (IFHandle) and not mng_status_reading (IFHandle) then + if mng_display_freeze (IFHandle) <> MNG_NOERROR then + MNGerror ('libmng reported an error during display_freeze!'); + + mng_cleanup (IFHandle); +end; + +{****************************************************************************} + +procedure TMainForm.FormShow(Sender: TObject); +begin + FormResize (self); +end; + +{****************************************************************************} + +procedure TMainForm.FormResize(Sender: TObject); +begin { center the image in the window } + if ClientWidth < OFImage.Width then + OFImage.Left := 0 + else + OFImage.Left := (ClientWidth - OFImage.Width ) div 2; + + if ClientHeight < OFImage.Height then + OFImage.Top := 0 + else + OFImage.Top := (ClientHeight - OFImage.Height) div 2; + +end; + +{****************************************************************************} + +procedure TMainForm.FormKeyDown(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + if Key = vk_Escape then { pressing will freeze an animation } + begin + OFTimer.Enabled := false; { don't let that timer go off then ! } + BFCancelled := true; + + if mng_status_running (IFHandle) and not mng_status_reading (IFHandle) then + if mng_display_freeze (IFHandle) <> MNG_NOERROR then + MNGerror ('libmng reported an error during display_freeze!'); + end; +end; + +{****************************************************************************} + +procedure TMainForm.FormMouseMove(Sender: TObject; Shift: TShiftState; X, + Y: Integer); +begin + if mng_status_dynamic (IFHandle) then + begin + if BFHasMouse then { if we had the mouse, it's left ! } + begin + if mng_trapevent (IFHandle, 3, 0, 0) <> MNG_NOERROR then + MNGerror ('libmng reported an error during trapevent!'); + + BFHasMouse := false; + end; + end; +end; + +{****************************************************************************} + +procedure TMainForm.OFImageMouseMove(Sender: TObject; Shift: TShiftState; + X, Y: Integer); +begin + if mng_status_dynamic (IFHandle) then + begin + if BFHasMouse then { did we have the mouse already ? } + begin + if mng_trapevent (IFHandle, 2, X, Y) <> MNG_NOERROR then + MNGerror ('libmng reported an error during trapevent!'); + end + else + begin { if not, it has entered ! } + if mng_trapevent (IFHandle, 1, X, Y) <> MNG_NOERROR then + MNGerror ('libmng reported an error during trapevent!'); + + BFHasMouse := true; + end; + end; +end; + +{****************************************************************************} + +procedure TMainForm.OFImageMouseDown(Sender: TObject; Button: TMouseButton; + Shift: TShiftState; X, Y: Integer); +begin + if mng_status_dynamic (IFHandle) then + if mng_trapevent (IFHandle, 4, X, Y) <> MNG_NOERROR then + MNGerror ('libmng reported an error during trapevent!'); +end; + +{****************************************************************************} + +procedure TMainForm.OFImageMouseUp(Sender: TObject; Button: TMouseButton; + Shift: TShiftState; X, Y: Integer); +begin + if mng_status_dynamic (IFHandle) then + if mng_trapevent (IFHandle, 5, X, Y) <> MNG_NOERROR then + MNGerror ('libmng reported an error during trapevent!'); +end; + +{****************************************************************************} + +procedure TMainForm.OFTimerTimer(Sender: TObject); + +var IHRslt : mng_retcode; + +begin + OFTimer.Enabled := false; { only once ! } + + if not BFCancelled then + begin { and inform the library } + IHRslt := mng_display_resume (IFHandle); + + if (IHRslt <> MNG_NOERROR) and (IHRslt <> MNG_NEEDTIMERWAIT) then + MNGerror ('libmng reported an error during display_resume!'); + + end; +end; + +{****************************************************************************} + +procedure TMainForm.OFMenuFileOpenClick(Sender: TObject); + +var IHRslt : mng_retcode; + +begin + OFOpenDialog.InitialDir := ''; + OFOpenDialog.FileName := SFFileName; + + if OFOpenDialog.Execute then { get the filename } + begin + if OFTimer.Enabled then { if the lib was active; stop it } + begin + OFTimer.Enabled := false; + + Application.ProcessMessages; { process any timer requests (for safety) } + { now freeze the animation } + if mng_display_freeze (IFHandle) <> MNG_NOERROR then + MNGerror ('libmng reported an error during display_freeze!'); + end; + { save interesting fields } + SFFileName := OFOpenDialog.FileName; + IFTicks := Windows.GetTickCount; + IFBytes := 0; + BFCancelled := false; + { always reset (just in case) } + if mng_reset (IFHandle) <> MNG_NOERROR then + MNGerror ('libmng reported an error during reset!') + else + begin { and let the lib do it's job ! } + IHRslt := mng_readdisplay (IFHandle); + + if (IHRslt <> MNG_NOERROR) and (IHRSLT <> MNG_NEEDTIMERWAIT) then + MNGerror ('libmng reported an error reading the input file!'); + + end; + end; +end; + +{****************************************************************************} + +procedure TMainForm.OFMenuFileProfileClick(Sender: TObject); + +var SHProfileDir : array [0 .. MAX_PATH + 20] of char; + +begin + GetSystemDirectory (@SHProfileDir, MAX_PATH); + strcat (@SHProfileDir, '\Color'); + + OFOpenDialogProfile.InitialDir := strpas (@SHProfileDir); + + if OFOpenDialogProfile.Execute then + if mng_set_outputprofile (IFHandle, pchar (OFOpenDialogProfile.FileName)) <> 0 then + MNGerror ('libmng reported an error setting the output-profile!'); + +end; + +{****************************************************************************} + +procedure TMainForm.OFMenuFileExitClick(Sender: TObject); +begin + if mng_cleanup (IFHandle) <> MNG_NOERROR then + MNGerror ('libmng cleanup error'); + + Close; +end; + +{****************************************************************************} + +procedure TMainForm.OFMenuOptionsModemSpeedClick(Sender: TObject); +begin + OFMenuOptionsModem28k8.Checked := false; + OFMenuOptionsModem33k6.Checked := false; + OFMenuOptionsModem56k.Checked := false; + OFMenuOptionsModemISDN64.Checked := false; + OFMenuOptionsModemISDN128.Checked := false; + OFMenuOptionsModemCable512.Checked := false; + OFMenuOptionsModemUnlimited.Checked := false; + + if IFBytesPerSec = OFMenuOptionsModem28k8.Tag div 10 then + OFMenuOptionsModem28k8.Checked := true + else + if IFBytesPerSec = OFMenuOptionsModem33k6.Tag div 10 then + OFMenuOptionsModem33k6.Checked := true + else + if IFBytesPerSec = OFMenuOptionsModem56k.Tag div 10 then + OFMenuOptionsModem56k.Checked := true + else + if IFBytesPerSec = OFMenuOptionsModemISDN64.Tag div 10 then + OFMenuOptionsModemISDN64.Checked := true + else + if IFBytesPerSec = OFMenuOptionsModemISDN128.Tag div 10 then + OFMenuOptionsModemISDN128.Checked := true + else + if IFBytesPerSec = OFMenuOptionsModemUnlimited.Tag div 10 then + OFMenuOptionsModemCable512.Checked := true + else + OFMenuOptionsModemUnlimited.Checked := true; + +end; + +{****************************************************************************} + +procedure TMainForm.OFMenuOptionsModemXClick(Sender: TObject); +begin + IFBytesPerSec := TMenuItem (Sender).Tag div 10; +end; + +{****************************************************************************} + +procedure TMainForm.MNGerror; + +var iErrorcode : mng_uint32; + iSeverity : mng_uint8; + iChunkname : mng_chunkid; + iChunkseq : mng_uint32; + iExtra1 : mng_int32; + iExtra2 : mng_int32; + zErrortext : mng_pchar; + +begin { get extended info } + iErrorcode := mng_getlasterror (IFHandle, iSeverity, iChunkname, iChunkseq, + iExtra1, iExtra2, zErrortext); + + MessageDlg (SHMsg + #13#10#13#10 + strpas (zErrortext) + #13#10#13#10 + + Format ('Error = %d; Severity = %d; Chunknr = %d; Extra1 = %d', + [iErrorcode, iSeverity, iChunkseq, iExtra1]), + mtError, [mbOK], 0); +end; + +{****************************************************************************} + +end. diff --git a/Engine/lib/lmng/contrib/delphi/mngview/mngview.dpr b/Engine/lib/lmng/contrib/delphi/mngview/mngview.dpr new file mode 100644 index 000000000..6408c75de --- /dev/null +++ b/Engine/lib/lmng/contrib/delphi/mngview/mngview.dpr @@ -0,0 +1,14 @@ +program mngview; + +uses + Forms, + Main in 'Main.pas' {MainForm}, + libmng in '..\libmng.pas'; + +{$R *.RES} + +begin + Application.Initialize; + Application.CreateForm(TMainForm, MainForm); + Application.Run; +end. diff --git a/Engine/lib/lmng/contrib/gcc/fbmngplay/COPYING b/Engine/lib/lmng/contrib/gcc/fbmngplay/COPYING new file mode 100644 index 000000000..d60c31a97 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/fbmngplay/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Engine/lib/lmng/contrib/gcc/fbmngplay/ChangeLog b/Engine/lib/lmng/contrib/gcc/fbmngplay/ChangeLog new file mode 100644 index 000000000..fd0859233 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/fbmngplay/ChangeLog @@ -0,0 +1,10 @@ +******************* Thu Sep 13 15:28:01 CEST 2001 ******************** + +* new option -S to play on start console only. +* new option -c to play animationon any fb console +* split fbmngplay to several files. + +******************* Wed Sep 12 14:51:44 CEST 2001 ******************** + +* new option -p for dynamic positioning of the mng animation in the + upper right corner. diff --git a/Engine/lib/lmng/contrib/gcc/fbmngplay/Makefile b/Engine/lib/lmng/contrib/gcc/fbmngplay/Makefile new file mode 100644 index 000000000..27ad8db33 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/fbmngplay/Makefile @@ -0,0 +1,50 @@ +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +SHELL = /bin/sh +CC = gcc + +SOURCES = fbmngplay.c +PROGRAMS = fbmngplay fbmngplay.static +OBJECTS = fbmngplay.o messages.o mng.o console.o + +LDFLAGS = -L/usr/lib +LIBSS = /usr/lib/libmng-mini.a /usr/lib/libz.a -lm +LIBSD = -lmng -lz -lm + +CFLAGS = -Os -Wall -Wmissing-prototypes -Wstrict-prototypes -D_REENTRANT +COMPILE = $(CC) $(CFLAGS) +LINKS = $(CC) $(CFLAGS) $(LDFLAGS) -static -o $@ +LINKD = $(CC) $(CFLAGS) $(LDFLAGS) -o $@ + +all: $(PROGRAMS) + strip -s $(PROGRAMS) +.SUFFIXES: +.SUFFIXES: .S .c .o .s + +.s.o: + $(COMPILE) -c $< + +.S.o: + $(COMPILE) -c $< + +mostlyclean: + -rm -f *.o core +clean: mostlyclean + -rm -f fbmngplay fbmngplay.static + +fbmngplay: $(OBJECTS) + @rm -f fbmngplay + $(LINKD) $(LDFLAGS) $(OBJECTS) $(LIBSD) +fbmngplay.static: $(OBJECTS) + @rm -f fbmngplay.static + $(LINKS) $(LDFLAGS) $(OBJECTS) $(LIBSS) + +.PHONY: mostlyclean clean fbmngplay fbmngplay.static all diff --git a/Engine/lib/lmng/contrib/gcc/fbmngplay/README b/Engine/lib/lmng/contrib/gcc/fbmngplay/README new file mode 100644 index 000000000..4e839370a --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/fbmngplay/README @@ -0,0 +1,30 @@ +* fbmngplay * + +a simple fbcon based mng player + +This is a simple example program, using the kernel framebuffer device +to display mng animation decoded by the new libmng implementation. + +This player is based on the SDL version included in the libmng package +from Ralph Giles . The code's fairly rough at this +point, but there was no example player for *nix in the distribution. +Patches welcome, of course. + +On a unix-like system, the build instructions are simple: + +(install and/or build the mng library from libmng.com) +make +make install + +To use the player: + +fbmngplay .mng .mng ... + +To stop looping animations, press CTRL C or send a TERM or INT signal. + +See the options with +fbmngplay -h + +--- +Stefan Reinauer, +$Date: 2002/09/26 18:09:36 $ diff --git a/Engine/lib/lmng/contrib/gcc/fbmngplay/console.c b/Engine/lib/lmng/contrib/gcc/fbmngplay/console.c new file mode 100644 index 000000000..4b5d5fb43 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/fbmngplay/console.c @@ -0,0 +1,107 @@ +/* + fbmngplay - fb console MNG player. + (c) 2001 by Stefan Reinauer, + + This program is based on mngplay, part of libmng, written and (C) by + Ralph Giles + + This program my be redistributed under the terms of the + GNU General Public Licence, version 2, or at your preference, + any later version. + + This file is based on getfd.c from the kbd package. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "console.h" + +int start_console = 0; + +/* + * getfd.c + * + * Get an fd for use with kbd/console ioctls. + * We try several things because opening /dev/console will fail + * if someone else used X (which does a chown on /dev/console). + */ + +static int +is_a_console(const int fd) { + char arg; + + arg = 0; + return (ioctl(fd, KDGKBTYPE, &arg) == 0 + && ((arg == KB_101) || (arg == KB_84))); +} + +static int +open_a_console(const char * const fnam) { + int fd; + + fd = open(fnam, O_RDONLY); + if (fd < 0 && errno == EACCES) + fd = open(fnam, O_WRONLY); + if (fd < 0) + return -1; + if (!is_a_console(fd)) { + close(fd); + return -1; + } + return fd; +} + +int getfd (const char * const nm) { + int fd; + + if (nm) { + fd = open_a_console(nm); + if (fd >= 0) + return fd; + } else { + fd = open_a_console("/dev/tty"); + if (fd >= 0) + return fd; + + fd = open_a_console("/dev/tty0"); + if (fd >= 0) + return fd; + + fd = open_a_console("/dev/console"); + if (fd >= 0) + return fd; + + for (fd = 0; fd < 3; fd++) + if (is_a_console(fd)) + return fd; + } + fprintf(stderr, + "Couldnt get a file descriptor referring to the console\n"); + exit(1); /* total failure */ +} + +int fd; +int current_console(void) +{ + struct vt_stat vtstat; + if (ioctl(fd, VT_GETSTATE, &vtstat)) { + fprintf(stderr,"fbmngplay: VT_GETSTATE\n"); + exit(1); + } + return vtstat.v_active; + +} + +void init_consoles(void) +{ + // get current tty + fd = getfd(0); + start_console=current_console(); +} + diff --git a/Engine/lib/lmng/contrib/gcc/fbmngplay/console.h b/Engine/lib/lmng/contrib/gcc/fbmngplay/console.h new file mode 100644 index 000000000..465efb484 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/fbmngplay/console.h @@ -0,0 +1,21 @@ +/* + fbmngplay - fb console MNG player. + (c) 2001 by Stefan Reinauer, + + This program is based on mngplay, part of libmng, written and (C) by + Ralph Giles + + This program my be redistributed under the terms of the + GNU General Public Licence, version 2, or at your preference, + any later version. +*/ + +#ifndef __CONSOLES_H +#define __CONSOLES_H + +extern int getfd(const char* const); +extern void init_consoles(void); +extern int current_console(void); +extern int start_console; + +#endif diff --git a/Engine/lib/lmng/contrib/gcc/fbmngplay/fbmngplay.c b/Engine/lib/lmng/contrib/gcc/fbmngplay/fbmngplay.c new file mode 100644 index 000000000..8f26a2c5a --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/fbmngplay/fbmngplay.c @@ -0,0 +1,228 @@ +/* + fbmngplay - fb console MNG player. + (c) 2001 by Stefan Reinauer, + + This program is based on mngplay, part of libmng, written and (C) by + Ralph Giles + + This program my be redistributed under the terms of the + GNU General Public Licence, version 2, or at your preference, + any later version. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include "fbmngplay.h" +#include "messages.h" +#include "console.h" +#include "mng.h" + +volatile int run = 1; +int verbose = 0; +int buffered = 0; +int dynpos = 0; +int waitsignal = 0; +int delta = 16; +int sconly=0; + +void sigint_handler(int signal); +void sigterm_handler(int signal); +void sigusr1_handler(int signal); + +void sigint_handler(int signal) +{ + run = 2; +} + +void sigterm_handler(int signal) +{ + restore_area(); + run = 0; +} + +void sigusr1_handler(int signal) +{ + run = 0; +} + +int main(int argc, char *argv[]) +{ + int fbdev,c,option_index; + unsigned int alpha; + struct fb_var_screeninfo var; + + /* Check which console we're running on */ + init_consoles(); + + /* allocate our stream data structure */ + mng = (mngstuff *) calloc(1, sizeof(*mng)); + if (mng == NULL) { + fprintf(stderr, "could not allocate stream structure.\n"); + exit(0); + } + alpha = 100; + mng->alpha = 100; + mng->fbx = 15; + mng->fby = 15; + mng->background = NULL; + + while (1) { + static struct option long_options[] = { + {"help", 0, 0, 'h'}, + {"verbose", 0, 0, 'v'}, + {"alpha", 1, 0, 'a'}, + {"buffered", 0, 0, 'b'}, + {"signal", 0, 0, 's'}, + {"delta", 0, 0, 'd'}, + {"position", 0, 0, 'p'}, + {"version", 0, 0, 'V'}, + {"start-console",0,0,'S'}, + {"console",1,0,'c'}, + {0, 0, 0, 0} + }; + + c = getopt_long(argc, argv, "a:x:y:bh?vsd:pVSc:", + long_options, &option_index); + + if (c == -1) + break; + + switch (c) { + case 'a': + alpha = atoi(optarg); + if (alpha > 100) + alpha = 100; + mng->alpha = alpha; + break; + case 'x': + mng->fbx = atoi(optarg); + break; + case 'y': + mng->fby = atoi(optarg); + break; + case 'd': + delta = atoi(optarg); + break; + case '?': + case 'h': + usage(argv[0]); + exit(0); + case 'v': + verbose = 1; + break; + case 's': + waitsignal = 1; + break; + case 'b': + buffered = 1; + break; + case 'p': + dynpos = 1; + break; + case 'V': + version(); + exit(0); + case 'c': + start_console=atoi(optarg); + case 'S': + sconly=1; + break; + default: + break; + } + } + + if (optind >= argc) { + printf("Which files do you want to play?\n"); + exit(0); + } + + //init_consoles(); + + /* Initialize framebuffer */ + fbdev = open("/dev/fb0", O_RDWR); + if (fbdev < 0) { + fprintf(stderr, "error while opening framebuffer.\n"); + exit(fbdev); + } + + ioctl(fbdev, FBIOGET_VSCREENINFO, &var); + mng->fbwidth = var.xres; + mng->fbheight = var.yres; + mng->fbbpp = var.bits_per_pixel; + + mng->display = + mmap(NULL, var.xres * var.yres * (var.bits_per_pixel >> 3), + PROT_WRITE | PROT_READ, MAP_SHARED, fbdev, 0); + + /* arrange to call the shutdown routine before we exit */ + atexit(&cleanup); + + while (optind < argc) { + // leftover arguements are filenames. + mng->filename = argv[optind++]; + + /* set up the mng decoder for our stream */ + mng->mng = mng_initialize(mng, mngalloc, mngfree, MNG_NULL); + if (mng->mng == MNG_NULL) { + fprintf(stderr, "could not initialize libmng.\n"); + exit(1); + } + + /* set the callbacks */ + mng_setcb_errorproc(mng->mng, mngerror); + mng_setcb_openstream(mng->mng, mngopenstream); + mng_setcb_closestream(mng->mng, mngclosestream); + mng_setcb_readdata(mng->mng, mngreadstream); + mng_setcb_gettickcount(mng->mng, mnggetticks); + mng_setcb_settimer(mng->mng, mngsettimer); + mng_setcb_processheader(mng->mng, mngprocessheader); + mng_setcb_getcanvasline(mng->mng, mnggetcanvasline); + mng_setcb_refresh(mng->mng, mngrefresh); + /* FIXME: should check for errors here */ + + signal(SIGINT, sigint_handler); + signal(SIGTERM, sigterm_handler); + + mng_readdisplay(mng->mng); + + /* loop though the frames */ + while (mng->delay && run) { + mdelay(mng->delay); + mng->delay = 0; + mng_display_resume(mng->mng); + if (run == 2) { + if (mng->alpha == 0) + run = 0; + mng->alpha -= delta; + if (mng->alpha < 0) + mng->alpha = 0; + } + } + + if (waitsignal && optind < argc) { + signal(SIGUSR1, sigusr1_handler); + run = 1; + while (run) { + sleep(2); + } + } + + memset(mng->copybuffer, 0, + 4 * mng->width * mng->height); + run = 1; + mng->alpha = alpha; + if (optind == argc) { /* last file */ + restore_area(); + } + } + + /* cleanup and quit */ + return mngquit(mng->mng); +} diff --git a/Engine/lib/lmng/contrib/gcc/fbmngplay/fbmngplay.h b/Engine/lib/lmng/contrib/gcc/fbmngplay/fbmngplay.h new file mode 100644 index 000000000..9af1c2764 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/fbmngplay/fbmngplay.h @@ -0,0 +1,43 @@ +/* + fbmngplay - fb console MNG player. + (c) 2001 by Stefan Reinauer, + + This program is based on mngplay, part of libmng, written and (C) by + Ralph Giles + + This program my be redistributed under the terms of the + GNU General Public Licence, version 2, or at your preference, + any later version. +*/ + +#ifndef __FBMNGPLAY_H +#define __FBMNGPLAY_H + +#include + +#define FBMNGPLAY_VERSION "0.3" + +/* structure for keeping track of our mng stream inside the callbacks */ +typedef struct { + FILE *file; /* pointer to the file we're decoding */ + char *filename; /* pointer to the file's path/name */ + mng_uint32 delay; /* ticks to wait before resuming decode */ + unsigned char *display; /* pointer to display */ + unsigned char *copybuffer; + unsigned char *background; + mng_handle mng; /* mng handle */ + int width, height; + int fbwidth, fbheight, fbbpp; + int fbx, fby; + int alpha; +} mngstuff; + +extern volatile int run; +extern int verbose; +extern int buffered; +extern int dynpos; +extern int waitsignal; +extern int delta; +extern int sconly; + +#endif diff --git a/Engine/lib/lmng/contrib/gcc/fbmngplay/messages.c b/Engine/lib/lmng/contrib/gcc/fbmngplay/messages.c new file mode 100644 index 000000000..2356ddf66 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/fbmngplay/messages.c @@ -0,0 +1,40 @@ +/* + fbmngplay - fb console MNG player. + (c) 2001 by Stefan Reinauer, + + This program is based on mngplay, part of libmng, written and (C) by + Ralph Giles + + This program my be redistributed under the terms of the + GNU General Public Licence, version 2, or at your preference, + any later version. +*/ + +#include + +#include "fbmngplay.h" +#include "messages.h" + +void usage(char *name) +{ + fprintf(stderr, "\nusage: %s [ -x ] [ -y ] [ -a ] [-b] [-v]" + " [-s] [file.mng [file.mng [...]]]\n", name); + fprintf(stderr, "\n -x: x coordinate\n"); + fprintf(stderr, " -y: y coordinate\n"); + fprintf(stderr, " -a, --alpha: default alpha channel 1..100\n"); + fprintf(stderr, " -v, --verbose: verbose mode\n"); + fprintf(stderr, " -b, --buffered: buffered mode\n"); + fprintf(stderr, " -s, --signal: wait for SIGUSR1 between animations\n"); + fprintf(stderr, " -p, --position: dynamically select position\n"); + fprintf(stderr, " -V, --version: show version and exit\n"); + fprintf(stderr, " -?, -h, --help: print this help.\n\n"); + fprintf(stderr, " -S --start-console: only output animation on console it was started on.\n"); +} + +void version(void) +{ + fprintf(stderr, "fbmngplay v%s, Copyright (C) 2001 Stefan Reinauer\n",FBMNGPLAY_VERSION); + fprintf(stderr, "fbmngplay comes with ABSOLUTELY NO WARRANTY;\n"); + fprintf(stderr,"This is free software, and you are welcome to redistribute it\n"); + fprintf(stderr,"under certain conditions; Check the GPL for details.\n"); +} diff --git a/Engine/lib/lmng/contrib/gcc/fbmngplay/messages.h b/Engine/lib/lmng/contrib/gcc/fbmngplay/messages.h new file mode 100644 index 000000000..fe20eea5a --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/fbmngplay/messages.h @@ -0,0 +1,19 @@ +/* + fbmngplay - fb console MNG player. + (c) 2001 by Stefan Reinauer, + + This program is based on mngplay, part of libmng, written and (C) by + Ralph Giles + + This program my be redistributed under the terms of the + GNU General Public Licence, version 2, or at your preference, + any later version. +*/ + +#ifndef __MESSAGES_H +#define __MESSAGES_H + +extern void usage(char *name); +extern void version(void); + +#endif diff --git a/Engine/lib/lmng/contrib/gcc/fbmngplay/mng.c b/Engine/lib/lmng/contrib/gcc/fbmngplay/mng.c new file mode 100644 index 000000000..b6564cce4 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/fbmngplay/mng.c @@ -0,0 +1,398 @@ +/* + fbmngplay - fb console MNG player. + (c) 2001 by Stefan Reinauer, + + This program is based on mngplay, part of libmng, written and (C) by + Ralph Giles + + This program my be redistributed under the terms of the + GNU General Public Licence, version 2, or at your preference, + any later version. +*/ + +#include +#include +#include + +#include "fbmngplay.h" +#include "console.h" +#include "mng.h" + +mngstuff *mng; +unsigned char *bufferstream; +unsigned long bufferpos = 0, buffersize = 0; + +inline void mdelay(unsigned long msec) +{ + usleep(msec * 1000); +} + +/* callbacks for the mng decoder */ + +/* memory allocation; data must be zeroed */ +mng_ptr mngalloc(mng_uint32 size) +{ + return (mng_ptr) calloc(1, size); +} + +/* memory deallocation */ +void mngfree(mng_ptr p, mng_uint32 size) +{ + free(p); + return; +} + +mng_bool mngopenstream(mng_handle mng) +{ + mngstuff *mymng; + + /* look up our stream struct */ + mymng = (mngstuff *) mng_get_userdata(mng); + + /* open the file */ + mymng->file = fopen(mymng->filename, "rb"); + if (mymng->file == NULL) { + fprintf(stderr, "unable to open '%s'\n", mymng->filename); + run = 0; + return MNG_FALSE; + } + + if (buffered) { + unsigned long len; + fseek(mymng->file, 0, SEEK_END); + len = ftell(mymng->file); + rewind(mymng->file); + bufferstream = malloc(len); + if (!bufferstream) { + /* Not enough memory for buffers + * -> we go back to unbuffered mode + */ + printf("Reverted to non buffered mode.\n"); + buffered = 0; + return MNG_TRUE; + } + buffersize = len; + fread(bufferstream, 1, len, mymng->file); + bufferpos = 0; + fclose(mymng->file); + mymng->file = NULL; + } + + return MNG_TRUE; +} + +mng_bool mngclosestream(mng_handle mng) +{ + mngstuff *mymng; + + /* look up our stream struct */ + mymng = (mngstuff *) mng_get_userdata(mng); + + /* close the file */ + if (mymng->file) + fclose(mymng->file); + mymng->file = NULL; /* for safety */ + + if (bufferstream) { + free(bufferstream); + bufferstream = 0; + buffersize = 0; + bufferpos = 0; + } + return MNG_TRUE; +} + +/* feed data to the decoder */ +mng_bool mngreadstream(mng_handle mng, mng_ptr buffer, + mng_uint32 size, mng_uint32 * bytesread) +{ + mngstuff *mymng; + + /* look up our stream struct */ + mymng = (mngstuff *) mng_get_userdata(mng); + if (!buffered) { + /* read the requested amount of data from the file */ + *bytesread = fread(buffer, 1, size, mymng->file); + } else { + *bytesread = (buffersize - bufferpos) < + size ? (buffersize - bufferpos) : size; + memcpy(buffer, bufferstream + bufferpos, *bytesread); + bufferpos += (*bytesread); + } + return MNG_TRUE; +} + +/* the header's been read. set up the display stuff */ +mng_bool mngprocessheader(mng_handle mng, + mng_uint32 width, mng_uint32 height) +{ + mngstuff *mymng; + unsigned char *copybuffer, *background; + unsigned char *src; + + mymng = (mngstuff *) mng_get_userdata(mng); + mymng->width = width; + mymng->height = height; + + if (dynpos) { + mymng->fbx = (mymng->fbwidth)-width-15; + switch (mymng->fbheight) { + case 768: + mymng->fby = 15; + break; + case 1024: + mymng->fby = 30; + break; + default: + mymng->fby = 0; + break; + } + } + + copybuffer = (unsigned char *) malloc(width * height * 4); + if (copybuffer == NULL) { + fprintf(stderr, "could not allocate copy buffer.\n"); + exit(0); + } + mymng->copybuffer = copybuffer; + + if (!mymng->background) { + background = + (unsigned char *) malloc(width * height * + (mymng->fbbpp >> 3)); + if (background == NULL) { + fprintf(stderr, + "could not allocate background buffer.\n"); + exit(0); + } + mymng->background = background; + + src = mymng->display + (mymng->fbwidth * mymng->fby + + mymng->fbx) * (mymng->fbbpp >> 3); + + while (height--) { + memcpy(background, src, + width * (mymng->fbbpp >> 3)); + background += width * (mymng->fbbpp >> 3); + src += mymng->fbwidth * (mymng->fbbpp >> 3); + } + } + /* tell the mng decoder about our bit-depth choice */ + /* FIXME: this works on intel. is it correct in general? */ + mng_set_canvasstyle(mng, MNG_CANVAS_BGRA8); + + return MNG_TRUE; +} + +/* return a row pointer for the decoder to fill */ +mng_ptr mnggetcanvasline(mng_handle mng, mng_uint32 line) +{ + mngstuff *mymng; + mng_ptr row; + + /* dereference our structure */ + mymng = (mngstuff *) mng_get_userdata(mng); + + /* we assume any necessary locking has happened + outside, in the frame level code */ + row = mymng->copybuffer + mymng->width * 4 * line; + + return (row); +} + +/* timer */ +mng_uint32 mnggetticks(mng_handle mng) +{ + mng_uint32 ticks; + struct timeval tv; + struct timezone tz; + + gettimeofday(&tv, &tz); + ticks = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); + + return (ticks); +} + +static inline void copyline(unsigned char *dest, unsigned char *src, + unsigned char *background, mngstuff * mymng) +{ + // BGRA8 + unsigned int i = mymng->width; + unsigned int fr, fg, fb, br, bg, bb, r, g, b, a; + unsigned short output, input; + + while (i--) { + fb = *src++; + fg = *src++; + fr = *src++; + + a = *src++; + a = a * mymng->alpha / 100; + switch (mymng->fbbpp) { + case 16: + input = *((unsigned short *) background)++; + br = (input >> 8) & 0xf8; + bg = (input >> 3) & 0xfc; + bb = input << 3 & 0xff; + break; + case 24: + bb = *background++; + bg = *background++; + br = *background++; + break; + case 32: + bb = *background++; + bg = *background++; + br = *background++; + background++; + break; + default: + br = 0; + bg = 0; + bb = 0; + printf("depth not supported.\n"); + run = 0; + break; + } + + r = ((fr * a) + (br * (0x100 - a))) >> 8; + g = ((fg * a) + (bg * (0x100 - a))) >> 8; + b = ((fb * a) + (bb * (0x100 - a))) >> 8; + + switch (mymng->fbbpp) { + case 16: + // dumb 24->16 bit conversion. + r >>= 3; + g >>= 2; + b >>= 3; + + output = (r << 11) | (g << 5) | b; + *((unsigned short *) dest)++ = output; + break; + case 24: + *dest++ = b; + *dest++ = g; + *dest++ = r; + break; + case 32: + *dest++ = b; + *dest++ = g; + *dest++ = r; + dest++; + break; + default: + break; + } + } +} + +mng_bool mngrefresh(mng_handle mng, mng_uint32 x, mng_uint32 y, + mng_uint32 w, mng_uint32 h) +{ + mngstuff *mymng; + unsigned char *dest, *src, *background; + + if (sconly && current_console()!=start_console) + return MNG_TRUE; + + /* dereference our structure */ + mymng = (mngstuff *) mng_get_userdata(mng); + + dest = mymng->display + ((mymng->fby * mymng->fbwidth + mymng->fbx) + * (mymng->fbbpp >> 3)); + src = mymng->copybuffer; + background = mymng->background; + /* refresh the screen with the new frame */ + while (h-- > 0) { + copyline(dest, src, background, mymng); + dest += mymng->fbwidth * (mymng->fbbpp >> 3); + background += mymng->width * (mymng->fbbpp >> 3); + src += 4 * mymng->width; // 4 bytes per pixel due to RGBA + } + + // remove traces + memset(mymng->copybuffer, 0, 4 * mymng->width * mymng->height); + + return MNG_TRUE; +} + +/* interframe delay callback */ +mng_bool mngsettimer(mng_handle mng, mng_uint32 msecs) +{ + mngstuff *mymng; + + /* look up our stream struct */ + mymng = (mngstuff *) mng_get_userdata(mng); + + /* set the timer for when the decoder wants to be woken */ + mymng->delay = msecs; + + return MNG_TRUE; +} + +mng_bool mngerror(mng_handle mng, mng_int32 code, mng_int8 severity, + mng_chunkid chunktype, mng_uint32 chunkseq, + mng_int32 extra1, mng_int32 extra2, mng_pchar text) +{ + mngstuff *mymng; + char chunk[5]; + + /* dereference our data so we can get the filename */ + mymng = (mngstuff *) mng_get_userdata(mng); + /* pull out the chuck type as a string */ + // FIXME: does this assume unsigned char? + chunk[0] = (char) ((chunktype >> 24) & 0xFF); + chunk[1] = (char) ((chunktype >> 16) & 0xFF); + chunk[2] = (char) ((chunktype >> 8) & 0xFF); + chunk[3] = (char) ((chunktype) & 0xFF); + chunk[4] = '\0'; + + /* output the error */ + fprintf(stderr, "error playing '%s' chunk %s (%d):\n", + mymng->filename, chunk, chunkseq); + fprintf(stderr, "%s\n", text); + + return 0; +} + +int mngquit(mng_handle mng) +{ + mngstuff *mymng; + + /* dereference our data so we can free it */ + mymng = (mngstuff *) mng_get_userdata(mng); + + /* cleanup. this will call mymngclosestream */ + mng_cleanup(&mng); + + /* free our data */ + free(mymng); + /* quit */ + exit(0); +} + +void cleanup(void) +{ + mngquit(mng->mng); + exit(0); +} + +void restore_area(void) +{ + int height, width; + unsigned char *dest, *background; + + if (sconly && current_console()!=start_console) + return; + + background = mng->background; + height = mng->height; + width = mng->width; + dest = mng->display + (mng->fbwidth * mng->fby + + mng->fbx) * (mng->fbbpp >> 3); + while (height--) { + memcpy(dest, background, width * (mng->fbbpp >> 3)); + background += width * (mng->fbbpp >> 3); + dest += mng->fbwidth * (mng->fbbpp >> 3); + } +} diff --git a/Engine/lib/lmng/contrib/gcc/fbmngplay/mng.h b/Engine/lib/lmng/contrib/gcc/fbmngplay/mng.h new file mode 100644 index 000000000..11dfb94b2 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/fbmngplay/mng.h @@ -0,0 +1,38 @@ +/* + fbmngplay - fb console MNG player. + (c) 2001 by Stefan Reinauer, + + This program is based on mngplay, part of libmng, written and (C) by + Ralph Giles + + This program my be redistributed under the terms of the + GNU General Public Licence, version 2, or at your preference, + any later version. +*/ + +#ifndef __HOOKS_H +#define __HOOKS_H + +#include "fbmngplay.h" + +extern mngstuff *mng; + +extern inline void mdelay(unsigned long msec); +extern mng_ptr mngalloc(mng_uint32 size); +extern void mngfree(mng_ptr p, mng_uint32 size); +extern mng_bool mngopenstream(mng_handle mng); +extern mng_bool mngclosestream(mng_handle mng); +extern mng_bool mngreadstream( mng_handle mng, mng_ptr buffer, mng_uint32 size, mng_uint32 * bytesread); +extern mng_bool mngprocessheader( mng_handle mng, mng_uint32 width, mng_uint32 height); +extern mng_ptr mnggetcanvasline(mng_handle mng, mng_uint32 line); +extern mng_uint32 mnggetticks(mng_handle mng); +extern mng_bool mngrefresh(mng_handle mng, mng_uint32 x, mng_uint32 y, mng_uint32 w, mng_uint32 h); +extern mng_bool mngsettimer(mng_handle mng, mng_uint32 msecs); +extern mng_bool mngerror(mng_handle mng, mng_int32 code, mng_int8 severity, + mng_chunkid chunktype, mng_uint32 chunkseq, + mng_int32 extra1, mng_int32 extra2, mng_pchar text); +extern int mngquit(mng_handle mng); +extern void cleanup(void); +extern void restore_area(void); +#endif + diff --git a/Engine/lib/lmng/contrib/gcc/gtk-mng-view/COPYING b/Engine/lib/lmng/contrib/gcc/gtk-mng-view/COPYING new file mode 100644 index 000000000..eb685a5ec --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/gtk-mng-view/COPYING @@ -0,0 +1,481 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/Engine/lib/lmng/contrib/gcc/gtk-mng-view/Makefile b/Engine/lib/lmng/contrib/gcc/gtk-mng-view/Makefile new file mode 100644 index 000000000..6e0783e40 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/gtk-mng-view/Makefile @@ -0,0 +1,29 @@ +LIBMNG_PREFIX = /usr/local + +CC = gcc + +CFLAGS = -g -O2 -Wall \ + `pkg-config --cflags gtk+-2.0` \ + -I$(LIBMNG_PREFIX)/include + +LIBS = `pkg-config --libs gtk+-2.0` \ + -L$(LIBMNG_PREFIX)/lib -lmng -ljpeg -llcms -lz + +OBJ = dummy.o \ + gtk-mng-view.o + +EXE_BASENAME=gmngview + +all: $(EXE_BASENAME) + +$(EXE_BASENAME): $(OBJ) + $(CC) -o $(EXE_BASENAME) $(OBJ) $(LIBS) + +#$(EXE_BASENAME)-static: $(OBJ) +# $(CC) -static -o $(EXE_BASENAME)-static $(OBJ) $(LIBS) + +.c.o: gtk-mng-view.h + $(CC) -c $(CFLAGS) $< + +clean: + rm -f core $(EXE_BASENAME) $(EXE_BASENAME)-static $(OBJ) diff --git a/Engine/lib/lmng/contrib/gcc/gtk-mng-view/README b/Engine/lib/lmng/contrib/gcc/gtk-mng-view/README new file mode 100644 index 000000000..af7a21ff0 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/gtk-mng-view/README @@ -0,0 +1,38 @@ +From dummy.c: + +/* + * Very simple program, that has been used by us + * (V. Babin and + * S. Kondrat ) + * during toying with libmng (http://www.limng.com) + * + * License: GPL :-)) + * + * 7 July 2001: added key-press/button-press handling to exit viewer without + * window-manager help; added libmng version info [Greg Roelofs] + */ + +From gtk-mng-view.c: + +/* Toy widget for GTK+ for displaying MNG animations. + * + * Copyright (C) 2000 The Free Software Foundation + * + * Author(s): Volodymyr Babin + * + * This code is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + diff --git a/Engine/lib/lmng/contrib/gcc/gtk-mng-view/README.compile b/Engine/lib/lmng/contrib/gcc/gtk-mng-view/README.compile new file mode 100644 index 000000000..fb97b563b --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/gtk-mng-view/README.compile @@ -0,0 +1,8 @@ +Require: + + * gtk+ >= 1.2.0 (tested on 1.2.8) + * libmng (tested on 0.9.2) + +You have specify Your libmng instalation prefix +before typing `make' in the first line of a Makefile +in this directory. Later type `make'. diff --git a/Engine/lib/lmng/contrib/gcc/gtk-mng-view/dummy.c b/Engine/lib/lmng/contrib/gcc/gtk-mng-view/dummy.c new file mode 100644 index 000000000..c0aa16ec2 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/gtk-mng-view/dummy.c @@ -0,0 +1,139 @@ +/* + * Very simple program, that has been used by us + * (V. Babin and + * S. Kondrat ) + * during toying with libmng (http://www.libmng.com) + * + * License: GPL :-)) + * + * 7 July 2001: added key-press/button-press handling to exit viewer without + * window-manager help; added libmng version info [Greg Roelofs] + */ + +#include +#include +#include +#include +#include +#include +#include "gtk-mng-view.h" + +#define BLOCK_SIZE 4096 + +static guint +read_file (const gchar* file_name, guchar ** ptr) +{ + gint fd; + guint size = 0; + guint bytes_read = 0; + + if ((fd = open (file_name, O_RDONLY)) == -1) + { + perror (file_name); + * ptr = NULL; + return 0; + } + + * ptr = g_new (guchar, BLOCK_SIZE); + while ((bytes_read = read (fd, * ptr + size, BLOCK_SIZE))) + { + size += bytes_read; + * ptr = (guchar *) g_realloc (* ptr, size + BLOCK_SIZE); + } + close (fd); + + * ptr = (guchar *) g_realloc (* ptr, size); + return size; +} + +int +main (int argc, char ** argv) +{ + GtkMngView * mng_view; + GtkWidget * window; + GtkWidget * align; + GtkWidget * frame; + guchar * mng_data = NULL; + guint mng_data_size; + + if (argc < 2) + { + g_print ("Usage: %s \n\n", argv[0]); + + g_print (" Compiled with GTK+ %d.%d.%d; using GTK+ %d.%d.%d.\n", + GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION, + gtk_major_version, gtk_minor_version, gtk_micro_version); +#ifdef GDK_PIXBUF_VERSION + g_print (" Compiled with gdk-pixbuf %s; using gdk-pixbuf %s.\n", + GDK_PIXBUF_VERSION, gdk_pixbuf_version); +#endif + g_print (" Compiled with libmng %s; using libmng %s.\n", + MNG_VERSION_TEXT, mng_version_text()); + g_print (" Compiled with zlib %s; using zlib %s.\n", + ZLIB_VERSION, zlib_version); +#ifdef JPEG_LIB_VERSION + { + int major = JPEG_LIB_VERSION / 10; + int minor = JPEG_LIB_VERSION % 10; + char minoralpha[2]; + + if (minor) + { + minoralpha[0] = (char)(minor - 1 + 'a'); + minoralpha[1] = '\0'; + } + else + minoralpha[0] = '\0'; + g_print (" Compiled with libjpeg %d%s.\n", major, minoralpha); + } +#endif + g_print ("\nPress Esc or Q, or click mouse button, to quit.\n"); + return 1; + } + + mng_data_size = read_file (* (argv + 1), &mng_data); + + if (mng_data == NULL) + return 1; + + gtk_init (&argc, &argv); + gdk_rgb_init (); + gdk_rgb_set_verbose (TRUE); + gtk_widget_set_default_visual (gdk_rgb_get_visual ()); + gtk_widget_set_default_colormap (gdk_rgb_get_cmap ()); + + window = gtk_widget_new (GTK_TYPE_WINDOW, + "GtkWindow::type", GTK_WINDOW_TOPLEVEL, + "GtkWindow::title", "MNG animation", + "GtkContainer::border_width", 5, + NULL); + gtk_signal_connect (GTK_OBJECT (window), "delete_event", + GTK_SIGNAL_FUNC (gtk_main_quit), NULL); + gtk_signal_connect (GTK_OBJECT (window), "destroy", + GTK_SIGNAL_FUNC (gtk_main_quit), NULL); + + /* any keypress (e.g., Esc or Q) or mouse-button click will quit viewer */ + gtk_signal_connect (GTK_OBJECT (window), "key_press_event", + GTK_SIGNAL_FUNC (gtk_main_quit), NULL); + gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK); + gtk_signal_connect (GTK_OBJECT (window), "button_press_event", + GTK_SIGNAL_FUNC (gtk_main_quit), NULL); + + align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0); + gtk_container_add (GTK_CONTAINER (window), align); + frame = gtk_frame_new (NULL); + gtk_container_add (GTK_CONTAINER (align), frame); + + /* actually it */ + mng_view = GTK_MNG_VIEW (gtk_mng_view_new ()); + gtk_container_add (GTK_CONTAINER (frame), GTK_WIDGET (mng_view)); + + gtk_mng_view_load_mng_from_memory (mng_view, mng_data, mng_data_size); + g_free (mng_data); + + /* rest in piece */ + gtk_widget_show_all (window); + gtk_main (); + + return 0; +} diff --git a/Engine/lib/lmng/contrib/gcc/gtk-mng-view/gtk-mng-view.c b/Engine/lib/lmng/contrib/gcc/gtk-mng-view/gtk-mng-view.c new file mode 100644 index 000000000..2a10d7c33 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/gtk-mng-view/gtk-mng-view.c @@ -0,0 +1,471 @@ +/* Toy widget for GTK+ for displaying MNG animations. + * + * Copyright (C) 2000 The Free Software Foundation + * + * Author(s): Volodymyr Babin + * + * This code is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include "gtk-mng-view.h" +#include + +/* MNG callbacks */ + +static mng_ptr +mng_malloc_callback (mng_size_t how_many) +{ + return (mng_ptr) g_new0 (gchar, how_many); +} + +static void +mng_free_callback (mng_ptr pointer, mng_size_t number) +{ + g_free (pointer); +} + +static mng_bool +mng_open_stream_callback (mng_handle mng_h) +{ + return MNG_TRUE; +} + +static mng_bool +mng_close_stream_callback (mng_handle mng_h) +{ + return MNG_TRUE; +} + +static mng_bool +mng_read_data_callback (mng_handle mng_h, + mng_ptr buffer, + mng_uint32 bytes_requested, + mng_uint32 * bytes_read) +{ + guint available_mng_food; + + GtkMngView * mng_view = GTK_MNG_VIEW (mng_get_userdata (mng_h)); + + available_mng_food = mng_view->bytes_to_eat - mng_view->bytes_eaten; + if (available_mng_food > 0 && mng_view->mng_food != NULL) + { + * bytes_read = (mng_uint32) MIN ((mng_uint32) available_mng_food, bytes_requested); + memcpy (buffer, mng_view->mng_food + mng_view->bytes_eaten, * bytes_read); + mng_view->bytes_eaten += * bytes_read; + return MNG_TRUE; + } + else + return MNG_FALSE; +} + +static mng_bool +mng_process_header_callback (mng_handle mng_h, + mng_uint32 width, + mng_uint32 height) +{ + GtkMngView * mng_view; + + mng_view = GTK_MNG_VIEW (mng_get_userdata (mng_h)); + + mng_view->width = width; + mng_view->height = height; + + g_free (mng_view->MNG_drawing_buffer); + mng_view->MNG_drawing_buffer = g_new0 (guchar, 3 * width * height); + + gtk_widget_queue_resize (GTK_WIDGET (mng_view)); + return MNG_TRUE; +} + +static gboolean +gtk_mng_view_animator (GtkMngView * mng_view) +{ + mng_retcode retcode; + + retcode = mng_display_resume (mng_view->MNG_handle); + + if (retcode == MNG_NOERROR) + { + mng_view->timeout_ID = 0; + return FALSE; + } + else if (retcode == MNG_NEEDTIMERWAIT) + return FALSE; + else + g_warning ("mng_display_resume() return not good value"); + + return FALSE; +} + +static mng_bool +mng_set_timer_callback (mng_handle mng_h, + mng_uint32 delay) +{ + GtkMngView * mng_view; + + mng_view = GTK_MNG_VIEW (mng_get_userdata (mng_h)); + mng_view->timeout_ID = gtk_timeout_add (delay, + (GtkFunction) gtk_mng_view_animator, + mng_view); + return MNG_TRUE; +} + +static mng_uint32 +mng_get_tickcount_callback (mng_handle mng_h) +{ + gdouble seconds; + gulong microseconds; + + GtkMngView * mng_view; + + mng_view = GTK_MNG_VIEW (mng_get_userdata (mng_h)); + seconds = g_timer_elapsed (mng_view->timer, + µseconds); + + return ((mng_uint32) (seconds*1000.0 + ((gdouble) microseconds)/1000.0)); +} + +static mng_ptr +mng_get_canvas_line_callback (mng_handle mng_h, + mng_uint32 line) +{ + GtkMngView * mng_view; + + mng_view = GTK_MNG_VIEW (mng_get_userdata (mng_h)); + return mng_view->MNG_drawing_buffer + 3 * line * mng_view->width; +} + +static void gtk_mng_view_paint (GtkMngView *, GdkRectangle *); + +static mng_bool +mng_refresh_callback (mng_handle mng_h, + mng_uint32 x, + mng_uint32 y, + mng_uint32 width, + mng_uint32 height) +{ + GtkMngView * mng_view; + + mng_view = GTK_MNG_VIEW (mng_get_userdata (mng_h)); + + if (GTK_WIDGET_REALIZED (mng_view)) + { + GdkRectangle rectangle; + + rectangle.x = x; + rectangle.y = y; + rectangle.width = width; + rectangle.height = height; + gtk_mng_view_paint (mng_view, &rectangle); + } + return MNG_TRUE; +} + +static gboolean +gtk_mng_view_init_libmng (GtkMngView * mng_view) +{ + GtkWidget * widget; + + g_return_val_if_fail (IS_GTK_MNG_VIEW (mng_view), FALSE); + + if (mng_view->MNG_handle) + mng_cleanup (&mng_view->MNG_handle); + + mng_view->MNG_handle = mng_initialize (mng_view, + mng_malloc_callback, + mng_free_callback, + MNG_NULL); + + if (mng_view->MNG_handle == MNG_NULL) + return FALSE; + + if (mng_setcb_openstream (mng_view->MNG_handle, mng_open_stream_callback) != MNG_NOERROR || + mng_setcb_closestream (mng_view->MNG_handle, mng_close_stream_callback) != MNG_NOERROR || + mng_setcb_readdata (mng_view->MNG_handle, mng_read_data_callback) != MNG_NOERROR || + mng_setcb_processheader (mng_view->MNG_handle, mng_process_header_callback) != MNG_NOERROR || + mng_setcb_settimer (mng_view->MNG_handle, mng_set_timer_callback) != MNG_NOERROR || + mng_setcb_gettickcount (mng_view->MNG_handle, mng_get_tickcount_callback) != MNG_NOERROR || + mng_setcb_getcanvasline (mng_view->MNG_handle, mng_get_canvas_line_callback) != MNG_NOERROR || + mng_setcb_refresh (mng_view->MNG_handle, mng_refresh_callback) != MNG_NOERROR) + { + mng_cleanup (&mng_view->MNG_handle); + return FALSE; + } + + mng_set_canvasstyle (mng_view->MNG_handle, MNG_CANVAS_RGB8); + + widget = GTK_WIDGET (mng_view); + + if (!GTK_WIDGET_REALIZED (widget)) + gtk_widget_realize (widget); + + mng_set_bgcolor (mng_view->MNG_handle, + widget->style->bg[GTK_STATE_NORMAL].red, + widget->style->bg[GTK_STATE_NORMAL].green, + widget->style->bg[GTK_STATE_NORMAL].blue); + return TRUE; +} + +/* GTK+ widget methods */ + +static GtkWidgetClass * parent_class = NULL; + +static void +gtk_mng_view_finalize (GObject * obj) +{ + GtkMngView * mng_view = GTK_MNG_VIEW (obj); + + g_timer_destroy (mng_view->timer); + + if (mng_view->timeout_ID) + gtk_timeout_remove (mng_view->timeout_ID); + + g_free (mng_view->MNG_drawing_buffer); + + if (mng_view->MNG_handle) + mng_cleanup (&mng_view->MNG_handle); + + G_OBJECT_CLASS (parent_class)->finalize (obj); +} + +static void +gtk_mng_view_size_request (GtkWidget * widget, GtkRequisition * requisition) +{ + GtkMngView * mng_view; + + g_return_if_fail (IS_GTK_MNG_VIEW (widget)); + g_return_if_fail (requisition != NULL); + + mng_view = (GtkMngView *) widget; + + requisition->width = mng_view->width; + requisition->height = mng_view->height; +} + +static void +gtk_mng_view_size_allocate (GtkWidget * widget, GtkAllocation * allocation) +{ + g_return_if_fail (IS_GTK_MNG_VIEW (widget)); + g_return_if_fail (allocation != NULL); + + if (GTK_WIDGET_REALIZED (widget)) + gdk_window_move_resize (widget->window, + allocation->x, + allocation->y, + allocation->width, + allocation->height); +} + +static void +gtk_mng_view_paint (GtkMngView * mng_view, + GdkRectangle * area) +{ + GtkWidget * widget; + guint rowstride; + guchar * buffer; + register guchar * ptr; + register guchar * bptr; + + widget = GTK_WIDGET (mng_view); + + g_assert (GTK_WIDGET_REALIZED (widget)); + + rowstride = 3 * area->width; + buffer = g_new (guchar, rowstride * area->height); + + + bptr = buffer; + ptr = mng_view->MNG_drawing_buffer + + 3 * (area->y * mng_view->width + area->x); + + while (bptr < buffer + rowstride * area->height) + { + memcpy (bptr, ptr, rowstride); + bptr += rowstride; + ptr += 3 * mng_view->width; + } + + gdk_draw_rgb_image (widget->window, + widget->style->white_gc, + area->x, + area->y, + area->width, + area->height, + GDK_RGB_DITHER_NORMAL, + buffer, + rowstride); + + g_free (buffer); + gdk_flush (); +} + +static gboolean +gtk_mng_view_expose (GtkWidget * widget, GdkEventExpose * event) +{ + g_return_val_if_fail (IS_GTK_MNG_VIEW (widget), FALSE); + g_return_val_if_fail (event != NULL, FALSE); + + if (GTK_WIDGET_REALIZED (widget)) + { + GdkRectangle dummy; + GdkRectangle rectangle; + GtkMngView * mng_view; + + mng_view = GTK_MNG_VIEW (widget); + dummy.x = dummy.y = 0; + dummy.width = mng_view->width; + dummy.height = mng_view->height; + + if (gdk_rectangle_intersect (&dummy, &event->area, &rectangle)) + gtk_mng_view_paint (mng_view, &rectangle); + } + return FALSE; +} + +static void +gtk_mng_view_realize (GtkWidget * widget) +{ + GdkWindowAttr attributes; + gint attributes_mask; + + g_return_if_fail (IS_GTK_MNG_VIEW (widget)); + + GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED); + + attributes.window_type = GDK_WINDOW_CHILD; + attributes.x = widget->allocation.x; + attributes.y = widget->allocation.y; + attributes.width = widget->allocation.width; + attributes.height = widget->allocation.height; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.visual = gtk_widget_get_visual (widget); + attributes.colormap = gtk_widget_get_colormap (widget); + attributes.event_mask = gtk_widget_get_events (widget); + attributes.event_mask |= GDK_EXPOSURE_MASK; + attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; + widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), + &attributes, attributes_mask); + gdk_window_set_user_data (widget->window, widget); + widget->style = gtk_style_attach (widget->style, widget->window); + gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); +} + +static void +gtk_mng_view_init (GtkMngView * mng_view) +{ + g_return_if_fail (IS_GTK_MNG_VIEW (mng_view)); + + GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (mng_view), GTK_NO_WINDOW); + + mng_view->MNG_handle = NULL; + mng_view->MNG_drawing_buffer = NULL; + mng_view->timeout_ID = 0; + mng_view->timer = g_timer_new (); + g_timer_start (mng_view->timer); + mng_view->mng_food = NULL; +} + +static void +gtk_mng_view_class_init (GtkMngViewClass * klass) +{ + GObjectClass * object_class; + GtkWidgetClass * widget_class; + + parent_class = g_type_class_peek_parent (klass); + + object_class = G_OBJECT_CLASS (klass); + object_class->finalize = gtk_mng_view_finalize; + + widget_class = GTK_WIDGET_CLASS (klass); + widget_class->size_request = gtk_mng_view_size_request; + widget_class->size_allocate = gtk_mng_view_size_allocate; + widget_class->expose_event = gtk_mng_view_expose; + widget_class->realize = gtk_mng_view_realize; +} + +GtkType +gtk_mng_view_get_type (void) +{ + static GtkType type = 0; + + if (!type) + { + static const GtkTypeInfo type_info = + { + "GtkMngView", + sizeof (GtkMngView), + sizeof (GtkMngViewClass), + (GtkClassInitFunc) gtk_mng_view_class_init, + (GtkObjectInitFunc) gtk_mng_view_init, + NULL, NULL, + (GtkClassInitFunc) NULL + }; + type = gtk_type_unique (GTK_TYPE_WIDGET, &type_info); + } + return type; +} + +GtkWidget * +gtk_mng_view_new (void) +{ + return GTK_WIDGET (gtk_type_new (GTK_MNG_VIEW_TYPE)); +} + +gboolean +gtk_mng_view_load_mng_from_memory (GtkMngView * mng_view, + guchar * data_to_eat, + guint data_size) +{ + g_return_val_if_fail (IS_GTK_MNG_VIEW (mng_view), FALSE); + g_return_val_if_fail (data_size > 27, FALSE); + g_return_val_if_fail (data_to_eat != NULL, FALSE); + + if (data_to_eat[0] != 0x8a || + data_to_eat[1] != 'M' || + data_to_eat[2] != 'N' || + data_to_eat[3] != 'G' || + data_to_eat[4] != 0x0d || + data_to_eat[5] != 0x0a || + data_to_eat[6] != 0x1a || + data_to_eat[7] != 0x0a) + { + g_warning ("not mng format"); + return FALSE; + } + + if (gtk_mng_view_init_libmng (mng_view)) + { + mng_view->bytes_to_eat = data_size; + mng_view->bytes_eaten = 0; + mng_view->mng_food = data_to_eat; + + if (mng_read (mng_view->MNG_handle) != MNG_NOERROR) + { + g_warning ("libmng read error"); + mng_cleanup (&mng_view->MNG_handle); + return FALSE; + } + else + return mng_display (mng_view->MNG_handle); + } + else + { + g_warning ("error initializing libmng"); + return FALSE; + } + return TRUE; +} diff --git a/Engine/lib/lmng/contrib/gcc/gtk-mng-view/gtk-mng-view.h b/Engine/lib/lmng/contrib/gcc/gtk-mng-view/gtk-mng-view.h new file mode 100644 index 000000000..1cbb33524 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/gtk-mng-view/gtk-mng-view.h @@ -0,0 +1,64 @@ +/* Toy widget for GTK+ for displaying MNG animations. + * + * Copyright (C) 2000 The Free Software Foundation + * + * Author(s): Volodymyr Babin + * + * This code is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GTK_MNG_VIEW_H__ +#define __GTK_MNG_VIEW_H__ + +#include +#include + +#define GTK_MNG_VIEW_TYPE (gtk_mng_view_get_type ()) +#define GTK_MNG_VIEW(o) (GTK_CHECK_CAST ((o), GTK_MNG_VIEW_TYPE, GtkMngView)) +#define GTK_MNG_VIEW_CLASS(k) (GTK_CHECK_CLASS_CAST ((k), GTK_MNG_VIEW_TYPE, GtkMngViewClass)) +#define IS_GTK_MNG_VIEW(o) (GTK_CHECK_TYPE ((o), GTK_MNG_VIEW_TYPE)) +#define IS_GTK_MNG_VIEW_CLASS(k) (GTK_CHECK_CLASS_TYPE ((k), GTK_MNG_VIEW_TYPE)) + +typedef struct _GtkMngView GtkMngView; +typedef struct _GtkMngViewClass GtkMngViewClass; + +struct _GtkMngView +{ + GtkWidget widget; + /* private */ + GTimer * timer; + guint timeout_ID; + guint width; + guint height; + mng_handle MNG_handle; + guchar * MNG_drawing_buffer; + guchar * mng_food; + guint bytes_to_eat; + guint bytes_eaten; +}; + +struct _GtkMngViewClass +{ + GtkWidgetClass klass; +}; + +GtkType gtk_mng_view_get_type (void); +GtkWidget * gtk_mng_view_new (void); + +/* returns !FALSE on success */ +gboolean gtk_mng_view_load_mng_from_memory (GtkMngView *, guchar *, guint); + +#endif /* __GTK_MNG_VIEW_H__ */ diff --git a/Engine/lib/lmng/contrib/gcc/gtk-mng-view/linux.mng b/Engine/lib/lmng/contrib/gcc/gtk-mng-view/linux.mng new file mode 100644 index 000000000..16ffa0436 Binary files /dev/null and b/Engine/lib/lmng/contrib/gcc/gtk-mng-view/linux.mng differ diff --git a/Engine/lib/lmng/contrib/gcc/mngtree/makefile.linux b/Engine/lib/lmng/contrib/gcc/mngtree/makefile.linux new file mode 100644 index 000000000..14b985eb4 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/mngtree/makefile.linux @@ -0,0 +1,43 @@ +# makefile for mngtree test-program on Linux ELF with gcc + +prefix=/usr/local + +CC=gcc + +INCPATH=$(prefix)/include +LIBPATH=$(prefix)/lib + +JPEGLIB=/usr/lib + +ALIGN= +# for i386: +#ALIGN=-malign-loops=2 -malign-functions=2 + +WARNMORE=-Wwrite-strings -Wpointer-arith -Wshadow \ + -Wmissing-declarations -Wtraditional -Wcast-align \ + -Wstrict-prototypes -Wmissing-prototypes #-Wconversion + +# for pgcc version 2.95.1, -O3 is buggy; don't use it. + +CFLAGS=-I$(INCPATH) -Wall -O3 -funroll-loops -DMNG_USE_SO $(ALIGN) # $(WARNMORE) -g +LDFLAGS=-L. -Wl,-rpath,. -L$(LIBPATH) -Wl,-rpath,$(LIBPATH) \ + -L$(JPEGLIB) -Wl,-rpath,$(JPEGLIB) -lmng -lz -ljpeg -lm + +OBJS = mngtree.o + +.SUFFIXES: .c .o + +.c.o: + $(CC) -c $(CFLAGS) -o $@ $*.c + +all: mngtree + +mngtree: mngtree.o + $(CC) -o mngtree $(CFLAGS) mngtree.o $(LDFLAGS) + +clean: + /bin/rm -f *.o mngtree + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +mngtree.o: mngtree.c diff --git a/Engine/lib/lmng/contrib/gcc/mngtree/mngtree.c b/Engine/lib/lmng/contrib/gcc/mngtree/mngtree.c new file mode 100644 index 000000000..daa613c4a --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/mngtree/mngtree.c @@ -0,0 +1,237 @@ +/* ************************************************************************** */ +/* * * */ +/* * COPYRIGHT NOTICE: * */ +/* * * */ +/* * Copyright (c) 2000 Gerard Juyn (gerard :at: libmng.com) * */ +/* * [You may insert additional notices after this sentence if you modify * */ +/* * this source] * */ +/* * * */ +/* * For the purposes of this copyright and license, "Contributing Authors" * */ +/* * is defined as the following set of individuals: * */ +/* * * */ +/* * Gerard Juyn * */ +/* * (hopefully some more to come...) * */ +/* * * */ +/* * The MNG Library is supplied "AS IS". The Contributing Authors * */ +/* * disclaim all warranties, expressed or implied, including, without * */ +/* * limitation, the warranties of merchantability and of fitness for any * */ +/* * purpose. The Contributing Authors assume no liability for direct, * */ +/* * indirect, incidental, special, exemplary, or consequential damages, * */ +/* * which may result from the use of the MNG Library, even if advised of * */ +/* * the possibility of such damage. * */ +/* * * */ +/* * Permission is hereby granted to use, copy, modify, and distribute this * */ +/* * source code, or portions hereof, for any purpose, without fee, subject * */ +/* * to the following restrictions: * */ +/* * * */ +/* * 1. The origin of this source code must not be misrepresented; * */ +/* * you must not claim that you wrote the original software. * */ +/* * * */ +/* * 2. Altered versions must be plainly marked as such and must not be * */ +/* * misrepresented as being the original source. * */ +/* * * */ +/* * 3. This Copyright notice may not be removed or altered from any source * */ +/* * or altered source distribution. * */ +/* * * */ +/* * The Contributing Authors specifically permit, without fee, and * */ +/* * encourage the use of this source code as a component to supporting * */ +/* * the MNG and JNG file format in commercial products. If you use this * */ +/* * source code in a product, acknowledgment would be highly appreciated. * */ +/* * * */ +/* ************************************************************************** */ +/* * * */ +/* * project : mngtree * */ +/* * file : mngtree.c copyright (c) 2000 G.Juyn * */ +/* * version : 1.0.1 * */ +/* * * */ +/* * purpose : main project file * */ +/* * * */ +/* * author : G.Juyn * */ +/* * web : http://www.3-t.com * */ +/* * email : mailto:info@3-t.com * */ +/* * * */ +/* * comment : mngtree simply dumps the chunk-structure of the supplied * */ +/* * first parameter to stdout (should be a xNG-file) * */ +/* * * */ +/* * changes : 0.5.2 - 06/03/2000 - G.Juyn * */ +/* * - fixed for compilation under Linux * */ +/* * 0.5.3 - 06/26/2000 - G.Juyn * */ +/* * - changed definition of userdata to mng_ptr * */ +/* * 0.5.3 - 06/28/2000 - G.Juyn * */ +/* * - changed memory allocation size parameters to mng_size_t * */ +/* * * */ +/* * 1.0.1 - 12/07/2003 - G.Juyn * */ +/* * - fixed inclusion of libmng.h (Thanks Raphael) * */ +/* * * */ +/* ************************************************************************** */ + +#include +#include +#include + +#include "../../../libmng.h" + +/* ************************************************************************** */ + +typedef struct user_struct { + + FILE *hFile; /* file handle */ + int iIndent; /* for nice indented formatting */ + + } userdata; + +typedef userdata * userdatap; + +/* ************************************************************************** */ + +mng_ptr myalloc (mng_size_t iSize) +{ /* duh! */ + return (mng_ptr)calloc (1, (size_t)iSize); +} + +/* ************************************************************************** */ + +void myfree (mng_ptr pPtr, mng_size_t iSize) +{ + free (pPtr); /* duh! */ + return; +} + +/* ************************************************************************** */ + +mng_bool myopenstream (mng_handle hMNG) +{ + return MNG_TRUE; /* already opened in main function */ +} + +/* ************************************************************************** */ + +mng_bool myclosestream (mng_handle hMNG) +{ + return MNG_TRUE; /* gets closed in main function */ +} + +/* ************************************************************************** */ + +mng_bool myreaddata (mng_handle hMNG, + mng_ptr pBuf, + mng_uint32 iSize, + mng_uint32 *iRead) +{ /* get to my file handle */ + userdatap pMydata = (userdatap)mng_get_userdata (hMNG); + /* read it */ + *iRead = fread (pBuf, 1, iSize, pMydata->hFile); + /* iRead will indicate EOF */ + return MNG_TRUE; +} + +/* ************************************************************************** */ + +mng_bool myiterchunk (mng_handle hMNG, + mng_handle hChunk, + mng_chunkid iChunktype, + mng_uint32 iChunkseq) +{ /* get to my file handle */ + userdatap pMydata = (userdatap)mng_get_userdata (hMNG); + char aCh[4]; + char zIndent[80]; + int iX; + /* decode the chunkname */ + aCh[0] = (char)((iChunktype >> 24) & 0xFF); + aCh[1] = (char)((iChunktype >> 16) & 0xFF); + aCh[2] = (char)((iChunktype >> 8) & 0xFF); + aCh[3] = (char)((iChunktype ) & 0xFF); + /* indent less ? */ + if ( (iChunktype == MNG_UINT_MEND) || (iChunktype == MNG_UINT_IEND) || + (iChunktype == MNG_UINT_ENDL) ) + pMydata->iIndent -= 2; + /* this looks ugly; but I haven't + figured out how to do it prettier */ + for (iX = 0; iX < pMydata->iIndent; iX++) + zIndent[iX] = ' '; + zIndent[pMydata->iIndent] = '\0'; + /* print a nicely indented line */ + printf ("%s%c%c%c%c\n", zIndent, aCh[0], aCh[1], aCh[2], aCh[3]); + /* indent more ? */ + if ( (iChunktype == MNG_UINT_MHDR) || (iChunktype == MNG_UINT_IHDR) || + (iChunktype == MNG_UINT_JHDR) || (iChunktype == MNG_UINT_DHDR) || + (iChunktype == MNG_UINT_BASI) || (iChunktype == MNG_UINT_LOOP) ) + pMydata->iIndent += 2; + + return MNG_TRUE; /* keep'm coming... */ +} + +/* ************************************************************************** */ + +int dumptree (char * zFilename) +{ + userdatap pMydata; + mng_handle hMNG; + mng_retcode iRC; + /* get a data buffer */ + pMydata = (userdatap)calloc (1, sizeof (userdata)); + + if (pMydata == NULL) /* oke ? */ + { + fprintf (stderr, "Cannot allocate a data buffer.\n"); + return 1; + } + /* can we open the file ? */ + if ((pMydata->hFile = fopen (zFilename, "rb")) == NULL) + { /* error out if we can't */ + fprintf (stderr, "Cannot open input file %s.\n", zFilename); + return 1; + } + /* let's initialize the library */ + hMNG = mng_initialize ((mng_ptr)pMydata, myalloc, myfree, MNG_NULL); + + if (!hMNG) /* did that work out ? */ + { + fprintf (stderr, "Cannot initialize libmng.\n"); + iRC = 1; + } + else + { /* setup callbacks */ + if ( ((iRC = mng_setcb_openstream (hMNG, myopenstream )) != 0) || + ((iRC = mng_setcb_closestream (hMNG, myclosestream)) != 0) || + ((iRC = mng_setcb_readdata (hMNG, myreaddata )) != 0) ) + fprintf (stderr, "Cannot set callbacks for libmng.\n"); + else + { /* read the file into memory */ + if ((iRC = mng_read (hMNG)) != 0) + fprintf (stderr, "Cannot read the file.\n"); + else + { + pMydata->iIndent = 2; /* start of the indenting at a nice level */ + + printf ("Starting dump of %s.\n\n", zFilename); + /* run through the chunk list */ + if ((iRC = mng_iterate_chunks (hMNG, 0, myiterchunk)) != 0) + fprintf (stderr, "Cannot iterate the chunks.\n"); + + printf ("\nDone.\n"); + } + } + + mng_cleanup (&hMNG); /* cleanup the library */ + } + + fclose (pMydata->hFile); /* cleanup */ + free (pMydata); + + return iRC; +} + +/* ************************************************************************** */ + +int main(int argc, char *argv[]) +{ + if (argc > 1) /* need that first parameter ! */ + return dumptree (argv[1]); + else + printf ("\nUsage: mngtree \n\n"); + + return 0; +} + +/* ************************************************************************** */ diff --git a/Engine/lib/lmng/contrib/gcc/sdl-mngplay/.deps/mngplay.P b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/.deps/mngplay.P new file mode 100644 index 000000000..6d68a8a48 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/.deps/mngplay.P @@ -0,0 +1,114 @@ +mngplay.o: mngplay.c /usr/include/stdio.h /usr/include/features.h \ + /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h \ + /usr/lib/gcc-lib/i386-slackware-linux/egcs-2.91.66/include/stddef.h \ + /usr/lib/gcc-lib/i386-slackware-linux/egcs-2.91.66/include/stdarg.h \ + /usr/include/bits/types.h /usr/include/libio.h \ + /usr/include/_G_config.h /usr/include/bits/stdio_lim.h \ + /usr/include/bits/stdio.h /usr/include/stdlib.h \ + /usr/include/sys/types.h /usr/include/time.h /usr/include/endian.h \ + /usr/include/bits/endian.h /usr/include/sys/select.h \ + /usr/include/bits/select.h /usr/include/bits/sigset.h \ + /usr/include/sys/sysmacros.h /usr/include/alloca.h \ + /usr/local/include/SDL/SDL.h /usr/local/include/SDL/SDL_main.h \ + /usr/local/include/SDL/SDL_types.h \ + /usr/local/include/SDL/SDL_getenv.h \ + /usr/local/include/SDL/SDL_error.h \ + /usr/local/include/SDL/begin_code.h \ + /usr/local/include/SDL/close_code.h \ + /usr/local/include/SDL/SDL_rwops.h /usr/local/include/SDL/SDL_timer.h \ + /usr/local/include/SDL/SDL_audio.h \ + /usr/local/include/SDL/SDL_byteorder.h \ + /usr/local/include/SDL/SDL_cdrom.h \ + /usr/local/include/SDL/SDL_joystick.h \ + /usr/local/include/SDL/SDL_events.h \ + /usr/local/include/SDL/SDL_active.h \ + /usr/local/include/SDL/SDL_keyboard.h \ + /usr/local/include/SDL/SDL_keysym.h \ + /usr/local/include/SDL/SDL_mouse.h /usr/local/include/SDL/SDL_video.h \ + /usr/local/include/SDL/SDL_mutex.h /usr/local/include/SDL/SDL_quit.h \ + /usr/local/include/SDL/SDL_version.h /usr/local/include/libmng.h \ + /usr/local/include/libmng_conf.h /usr/local/include/libmng_types.h \ + /usr/local/include/zlib.h /usr/local/include/zconf.h \ + /usr/include/setjmp.h /usr/include/bits/setjmp.h \ + /usr/local/include/jpeglib.h /usr/local/include/jconfig.h \ + /usr/local/include/jmorecfg.h \ + /usr/lib/gcc-lib/i386-slackware-linux/egcs-2.91.66/include/limits.h \ + /usr/lib/gcc-lib/i386-slackware-linux/egcs-2.91.66/include/syslimits.h \ + /usr/include/limits.h /usr/include/bits/posix1_lim.h \ + /usr/include/bits/local_lim.h /usr/include/linux/limits.h \ + /usr/include/bits/posix2_lim.h /usr/include/string.h \ + /usr/include/bits/string.h /usr/include/bits/string2.h \ + /usr/include/math.h /usr/include/bits/huge_val.h \ + /usr/include/bits/mathdef.h /usr/include/bits/mathcalls.h \ + /usr/lib/gcc-lib/i386-slackware-linux/egcs-2.91.66/include/float.h \ + /usr/include/bits/mathinline.h +mngplay.c : +/usr/include/stdio.h : +/usr/include/features.h : +/usr/include/sys/cdefs.h : +/usr/include/gnu/stubs.h : +/usr/lib/gcc-lib/i386-slackware-linux/egcs-2.91.66/include/stddef.h : +/usr/lib/gcc-lib/i386-slackware-linux/egcs-2.91.66/include/stdarg.h : +/usr/include/bits/types.h : +/usr/include/libio.h : +/usr/include/_G_config.h : +/usr/include/bits/stdio_lim.h : +/usr/include/bits/stdio.h : +/usr/include/stdlib.h : +/usr/include/sys/types.h : +/usr/include/time.h : +/usr/include/endian.h : +/usr/include/bits/endian.h : +/usr/include/sys/select.h : +/usr/include/bits/select.h : +/usr/include/bits/sigset.h : +/usr/include/sys/sysmacros.h : +/usr/include/alloca.h : +/usr/local/include/SDL/SDL.h : +/usr/local/include/SDL/SDL_main.h : +/usr/local/include/SDL/SDL_types.h : +/usr/local/include/SDL/SDL_getenv.h : +/usr/local/include/SDL/SDL_error.h : +/usr/local/include/SDL/begin_code.h : +/usr/local/include/SDL/close_code.h : +/usr/local/include/SDL/SDL_rwops.h : +/usr/local/include/SDL/SDL_timer.h : +/usr/local/include/SDL/SDL_audio.h : +/usr/local/include/SDL/SDL_byteorder.h : +/usr/local/include/SDL/SDL_cdrom.h : +/usr/local/include/SDL/SDL_joystick.h : +/usr/local/include/SDL/SDL_events.h : +/usr/local/include/SDL/SDL_active.h : +/usr/local/include/SDL/SDL_keyboard.h : +/usr/local/include/SDL/SDL_keysym.h : +/usr/local/include/SDL/SDL_mouse.h : +/usr/local/include/SDL/SDL_video.h : +/usr/local/include/SDL/SDL_mutex.h : +/usr/local/include/SDL/SDL_quit.h : +/usr/local/include/SDL/SDL_version.h : +/usr/local/include/libmng.h : +/usr/local/include/libmng_conf.h : +/usr/local/include/libmng_types.h : +/usr/local/include/zlib.h : +/usr/local/include/zconf.h : +/usr/include/setjmp.h : +/usr/include/bits/setjmp.h : +/usr/local/include/jpeglib.h : +/usr/local/include/jconfig.h : +/usr/local/include/jmorecfg.h : +/usr/lib/gcc-lib/i386-slackware-linux/egcs-2.91.66/include/limits.h : +/usr/lib/gcc-lib/i386-slackware-linux/egcs-2.91.66/include/syslimits.h : +/usr/include/limits.h : +/usr/include/bits/posix1_lim.h : +/usr/include/bits/local_lim.h : +/usr/include/linux/limits.h : +/usr/include/bits/posix2_lim.h : +/usr/include/string.h : +/usr/include/bits/string.h : +/usr/include/bits/string2.h : +/usr/include/math.h : +/usr/include/bits/huge_val.h : +/usr/include/bits/mathdef.h : +/usr/include/bits/mathcalls.h : +/usr/lib/gcc-lib/i386-slackware-linux/egcs-2.91.66/include/float.h : +/usr/include/bits/mathinline.h : diff --git a/Engine/lib/lmng/contrib/gcc/sdl-mngplay/AUTHORS b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/AUTHORS new file mode 100644 index 000000000..4ff88f20e --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/AUTHORS @@ -0,0 +1,7 @@ +Contributors to mnyplay + +Ralph Giles +Greg Roelofs (minor contribs!) + +-- +$Date: 2002/09/26 18:09:37 $ diff --git a/Engine/lib/lmng/contrib/gcc/sdl-mngplay/COPYING b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/COPYING new file mode 100644 index 000000000..d60c31a97 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Engine/lib/lmng/contrib/gcc/sdl-mngplay/ChangeLog b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/ChangeLog new file mode 100644 index 000000000..c7605a8b1 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/ChangeLog @@ -0,0 +1,29 @@ +2001-07-08 Greg Roelofs + - added SDL/libmng/zlib/libjpeg version info to usage screen + - added mouse-click handling (as an alternate quit mode) + - added completely static build + - fixed automake setup to order options and libraries correctly on + link line, and included resulting files (configure, Makefile.in, + etc.) in order to match instructions in INSTALL file + +2000-07-06 Ralph Giles + * Release 0.1 + - added error handling callback + - added event system so you can actually quit + (click the window closebox or type 'escape' or 'q') + - window titles from the filename + - added basic auto* + +2000-07-05 Ralph Giles + * snapshot; + - changed refresh parameters to patch libmng 0.9 + +2000-06-06 Ralph Giles + * snapshot; + - basic playing works now with the MNGsuite tests + +2000-06-06 Ralph Giles + * snapshot; + - doesn't really work yet + +# beginning =) diff --git a/Engine/lib/lmng/contrib/gcc/sdl-mngplay/INSTALL b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/INSTALL new file mode 100644 index 000000000..b42a17ac4 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/INSTALL @@ -0,0 +1,182 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. diff --git a/Engine/lib/lmng/contrib/gcc/sdl-mngplay/Makefile.am b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/Makefile.am new file mode 100644 index 000000000..059b1d3e7 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/Makefile.am @@ -0,0 +1,10 @@ +# process this with automake to create Makefile.in + +bin_PROGRAMS = mngplay mngplay-static + +mngplay_SOURCES = mngplay.c +mngplay_static_SOURCES = mngplay.c + +mngplay_static_LDFLAGS = -static + +# end diff --git a/Engine/lib/lmng/contrib/gcc/sdl-mngplay/Makefile.in b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/Makefile.in new file mode 100644 index 000000000..2881fb680 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/Makefile.in @@ -0,0 +1,534 @@ +# Makefile.in generated by automake 1.9.6 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# process this with automake to create Makefile.in + +SOURCES = $(mngplay_SOURCES) $(mngplay_static_SOURCES) + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = . +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +bin_PROGRAMS = mngplay$(EXEEXT) mngplay-static$(EXEEXT) +subdir = . +DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in $(top_srcdir)/configure AUTHORS COPYING \ + ChangeLog INSTALL depcomp install-sh missing +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \ + $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ + configure.lineno configure.status.lineno +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +PROGRAMS = $(bin_PROGRAMS) +am_mngplay_OBJECTS = mngplay.$(OBJEXT) +mngplay_OBJECTS = $(am_mngplay_OBJECTS) +mngplay_DEPENDENCIES = +am_mngplay_static_OBJECTS = mngplay.$(OBJEXT) +mngplay_static_OBJECTS = $(am_mngplay_static_OBJECTS) +mngplay_static_DEPENDENCIES = +DEFAULT_INCLUDES = -I. -I$(srcdir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(mngplay_SOURCES) $(mngplay_static_SOURCES) +DIST_SOURCES = $(mngplay_SOURCES) $(mngplay_static_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) +am__remove_distdir = \ + { test ! -d $(distdir) \ + || { find $(distdir) -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -fr $(distdir); }; } +DIST_ARCHIVES = $(distdir).tar.gz +GZIP_ENV = --best +distuninstallcheck_listfiles = find . -type f -print +distcleancheck_listfiles = find . -type f -print +ACLOCAL = @ACLOCAL@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GREP = @GREP@ +HAS_SDL = @HAS_SDL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +ac_ct_CC = @ac_ct_CC@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build_alias = @build_alias@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host_alias = @host_alias@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +mngplay_LDADD = @mngplay_LDADD@ +mngplay_static_LDADD = @mngplay_static_LDADD@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +mngplay_SOURCES = mngplay.c +mngplay_static_SOURCES = mngplay.c +mngplay_static_LDFLAGS = -static +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj +am--refresh: + @: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + echo ' cd $(srcdir) && $(AUTOMAKE) --foreign '; \ + cd $(srcdir) && $(AUTOMAKE) --foreign \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --foreign Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + echo ' $(SHELL) ./config.status'; \ + $(SHELL) ./config.status;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(srcdir) && $(AUTOCONF) +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + ; then \ + f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \ + $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \ + else :; fi; \ + done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \ + rm -f "$(DESTDIR)$(bindir)/$$f"; \ + done + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) +mngplay$(EXEEXT): $(mngplay_OBJECTS) $(mngplay_DEPENDENCIES) + @rm -f mngplay$(EXEEXT) + $(LINK) $(mngplay_LDFLAGS) $(mngplay_OBJECTS) $(mngplay_LDADD) $(LIBS) +mngplay-static$(EXEEXT): $(mngplay_static_OBJECTS) $(mngplay_static_DEPENDENCIES) + @rm -f mngplay-static$(EXEEXT) + $(LINK) $(mngplay_static_LDFLAGS) $(mngplay_static_OBJECTS) $(mngplay_static_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mngplay.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` +uninstall-info-am: + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + $(am__remove_distdir) + mkdir $(distdir) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + -find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(SHELL) $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r $(distdir) +dist-gzip: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__remove_distdir) + +dist-bzip2: distdir + tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2 + $(am__remove_distdir) + +dist-tarZ: distdir + tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z + $(am__remove_distdir) + +dist-shar: distdir + shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz + $(am__remove_distdir) + +dist-zip: distdir + -rm -f $(distdir).zip + zip -rq $(distdir).zip $(distdir) + $(am__remove_distdir) + +dist dist-all: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__remove_distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + case '$(DIST_ARCHIVES)' in \ + *.tar.gz*) \ + GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(am__untar) ;;\ + *.tar.bz2*) \ + bunzip2 -c $(distdir).tar.bz2 | $(am__untar) ;;\ + *.tar.Z*) \ + uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ + *.shar.gz*) \ + GZIP=$(GZIP_ENV) gunzip -c $(distdir).shar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ + esac + chmod -R a-w $(distdir); chmod a+w $(distdir) + mkdir $(distdir)/_build + mkdir $(distdir)/_inst + chmod a-w $(distdir) + dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ + && cd $(distdir)/_build \ + && ../configure --srcdir=.. --prefix="$$dc_install_base" \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ + distuninstallcheck \ + && chmod -R a-w "$$dc_install_base" \ + && ({ \ + (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ + distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ + } || { rm -rf "$$dc_destdir"; exit 1; }) \ + && rm -rf "$$dc_destdir" \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && rm -rf $(DIST_ARCHIVES) \ + && $(MAKE) $(AM_MAKEFLAGS) distcleancheck + $(am__remove_distdir) + @(echo "$(distdir) archives ready for distribution: "; \ + list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ + sed -e '1{h;s/./=/g;p;x;}' -e '$${p;x;}' +distuninstallcheck: + @cd $(distuninstallcheck_dir) \ + && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \ + || { echo "ERROR: files left after uninstall:" ; \ + if test -n "$(DESTDIR)"; then \ + echo " (check DESTDIR support)"; \ + fi ; \ + $(distuninstallcheck_listfiles) ; \ + exit 1; } >&2 +distcleancheck: distclean + @if test '$(srcdir)' = . ; then \ + echo "ERROR: distcleancheck can only run from a VPATH build" ; \ + exit 1 ; \ + fi + @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left in build directory after distclean:" ; \ + $(distcleancheck_listfiles) ; \ + exit 1; } >&2 +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic mostlyclean-am + +distclean: distclean-am + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: install-binPROGRAMS + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf $(top_srcdir)/autom4te.cache + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-info-am + +.PHONY: CTAGS GTAGS all all-am am--refresh check check-am clean \ + clean-binPROGRAMS clean-generic ctags dist dist-all dist-bzip2 \ + dist-gzip dist-shar dist-tarZ dist-zip distcheck distclean \ + distclean-compile distclean-generic distclean-tags \ + distcleancheck distdir distuninstallcheck dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-data install-data-am install-exec install-exec-am \ + install-info install-info-am install-man install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \ + uninstall-am uninstall-binPROGRAMS uninstall-info-am + + +# end +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/Engine/lib/lmng/contrib/gcc/sdl-mngplay/README b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/README new file mode 100644 index 000000000..0a99d0a62 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/README @@ -0,0 +1,27 @@ +* mngplay * + +a simple SDL-based mng player + +This is a simple example program, using the Simple Direct media Layer +to display mng animation decoded by the new libmng implementation. + +SDL and libmng are quite portable, but I've only tried it on x86 Linux. +Project files for MacOS, BeOS and Win32 are welcome. The code's fairly +rough at this point, but there was no example player for *nix in the +distribution. Patches welcome, of course. + +On a unix-like system, the build instructions are simple: + +(install and/or build the SDL libraries from libsdl.org) +(install and/or build the mng library from libmng.com) +./configure (or ./autogen.sh if you're using the cvs source) +make +make install + +To use the player: + +mngplay .mng + +--- +Ralph Giles +$Date: 2002/09/26 18:09:37 $ diff --git a/Engine/lib/lmng/contrib/gcc/sdl-mngplay/acinclude.m4 b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/acinclude.m4 new file mode 100644 index 000000000..9505b56ba --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/acinclude.m4 @@ -0,0 +1,34 @@ +dnl autoconf macros for detecting libmng +dnl add this to your aclocal or acinclude to make use of it +dnl +dnl (c) 2000 Ralph Giles +dnl + +dnl A basic check: looks for libmng and its dependencies +dnl and adds the required bits to CFLAGS and LIBS + +# check for libmng +AC_DEFUN(LIBMNG_CHECK, [ + dnl prerequisites first + AC_CHECK_LIB(jpeg, jpeg_set_defaults) + AC_CHECK_LIB(z, inflate) + dnl now the library + AC_CHECK_LIB(mng, mng_readdisplay, [_libmng_present=1]) + AC_CHECK_HEADER(libmng.h) + dnl see if we need the optional link dependency + AC_CHECK_LIB(lcms, cmsCreateRGBProfile, [ + AC_CHECK_HEADER(lcms.h) + AC_CHECK_LIB(mng, mnglcms_initlibrary, [ + LIBS="$LIBS -llcms" + AC_DEFINE(HAVE_LIBLCMS) + _libmng_present=1 + ]) + ]) + if test $_libmng_present -eq 1; then + LIBS="-lmng $LIBS" + AC_DEFINE(HAVE_LIBMNG) + fi + _libmng_present= +]) + +dnl end LIBMNG macros diff --git a/Engine/lib/lmng/contrib/gcc/sdl-mngplay/aclocal.m4 b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/aclocal.m4 new file mode 100644 index 000000000..6b72bbb1e --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/aclocal.m4 @@ -0,0 +1,851 @@ +# generated automatically by aclocal 1.9.6 -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004 +# Free Software Foundation, Inc. +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +# Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_AUTOMAKE_VERSION(VERSION) +# ---------------------------- +# Automake X.Y traces this macro to ensure aclocal.m4 has been +# generated from the m4 files accompanying Automake X.Y. +AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version="1.9"]) + +# AM_SET_CURRENT_AUTOMAKE_VERSION +# ------------------------------- +# Call AM_AUTOMAKE_VERSION so it can be traced. +# This function is AC_REQUIREd by AC_INIT_AUTOMAKE. +AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], + [AM_AUTOMAKE_VERSION([1.9.6])]) + +# AM_AUX_DIR_EXPAND -*- Autoconf -*- + +# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to +# `$srcdir', `$srcdir/..', or `$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is `.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +AC_DEFUN([AM_AUX_DIR_EXPAND], +[dnl Rely on autoconf to set up CDPATH properly. +AC_PREREQ([2.50])dnl +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` +]) + +# AM_CONDITIONAL -*- Autoconf -*- + +# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 7 + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +AC_DEFUN([AM_CONDITIONAL], +[AC_PREREQ(2.52)dnl + ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_SUBST([$1_TRUE]) +AC_SUBST([$1_FALSE]) +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi +AC_CONFIG_COMMANDS_PRE( +[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then + AC_MSG_ERROR([[conditional "$1" was never defined. +Usually this means the macro was only invoked conditionally.]]) +fi])]) + + +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 8 + +# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + +# _AM_DEPENDENCIES(NAME) +# ---------------------- +# See how the compiler implements dependency checking. +# NAME is "CC", "CXX", "GCJ", or "OBJC". +# We try a few techniques and use that to set a single cache variable. +# +# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was +# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular +# dependency, and given that the user is not expected to run this macro, +# just rely on AC_PROG_CC. +AC_DEFUN([_AM_DEPENDENCIES], +[AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl +AC_REQUIRE([AM_MAKE_INCLUDE])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl + +ifelse([$1], CC, [depcc="$CC" am_compiler_list=], + [$1], CXX, [depcc="$CXX" am_compiler_list=], + [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'], + [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +AC_CACHE_CHECK([dependency style of $depcc], + [am_cv_$1_dependencies_compiler_type], +[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_$1_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + case $depmode in + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + none) break ;; + esac + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. + if depmode=$depmode \ + source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) +AM_CONDITIONAL([am__fastdep$1], [ + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES +AC_DEFUN([AM_SET_DEPDIR], +[AC_REQUIRE([AM_SET_LEADING_DOT])dnl +AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE(dependency-tracking, +[ --disable-dependency-tracking speeds up one-time build + --enable-dependency-tracking do not reject slow dependency extractors]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +AC_SUBST([AMDEPBACKSLASH]) +]) + +# Generate code to set up dependency tracking. -*- Autoconf -*- + +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +#serial 3 + +# _AM_OUTPUT_DEPENDENCY_COMMANDS +# ------------------------------ +AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +[for mf in $CONFIG_FILES; do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named `Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # So let's grep whole file. + if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then + dirpart=`AS_DIRNAME("$mf")` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running `make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n 's/^U = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`AS_DIRNAME(["$file"])` + AS_MKDIR_P([$dirpart/$fdir]) + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done +done +])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +# AM_OUTPUT_DEPENDENCY_COMMANDS +# ----------------------------- +# This macro should only be invoked once -- use via AC_REQUIRE. +# +# This code is only required when automatic dependency tracking +# is enabled. FIXME. This creates each `.P' file that we will +# need in order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], +[AC_CONFIG_COMMANDS([depfiles], + [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], + [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) +]) + +# Do all the work for Automake. -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 12 + +# This macro actually does too much. Some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_PREREQ([2.58])dnl +dnl Autoconf wants to disallow AM_ names. We explicitly allow +dnl the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl +AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl +AC_REQUIRE([AC_PROG_INSTALL])dnl +# test to see if srcdir already configured +if test "`cd $srcdir && pwd`" != "`pwd`" && + test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi +AC_SUBST([CYGPATH_W]) + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl + AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl + AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package]) + AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version}) +AM_MISSING_PROG(AUTOCONF, autoconf) +AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version}) +AM_MISSING_PROG(AUTOHEADER, autoheader) +AM_MISSING_PROG(MAKEINFO, makeinfo) +AM_PROG_INSTALL_SH +AM_PROG_INSTALL_STRIP +AC_REQUIRE([AM_PROG_MKDIR_P])dnl +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_SET_LEADING_DOT])dnl +_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], + [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], + [_AM_PROG_TAR([v7])])]) +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_CC], + [_AM_DEPENDENCIES(CC)], + [define([AC_PROG_CC], + defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl +AC_PROVIDE_IFELSE([AC_PROG_CXX], + [_AM_DEPENDENCIES(CXX)], + [define([AC_PROG_CXX], + defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl +]) +]) + + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. The stamp files are numbered to have different names. + +# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the +# loop where config.status creates the headers, so we can generate +# our stamp files there. +AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], +[# Compute $1's index in $config_headers. +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $1 | $1:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $1" >`AS_DIRNAME([$1])`/stamp-h[]$_am_stamp_count]) + +# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +install_sh=${install_sh-"$am_aux_dir/install-sh"} +AC_SUBST(install_sh)]) + +# Copyright (C) 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 2 + +# Check whether the underlying file-system supports filenames +# with a leading dot. For instance MS-DOS doesn't. +AC_DEFUN([AM_SET_LEADING_DOT], +[rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null +AC_SUBST([am__leading_dot])]) + +# Check to see how 'make' treats includes. -*- Autoconf -*- + +# Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 3 + +# AM_MAKE_INCLUDE() +# ----------------- +# Check to see how make treats includes. +AC_DEFUN([AM_MAKE_INCLUDE], +[am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo done +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +AC_MSG_CHECKING([for style of include used by $am_make]) +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# We grep out `Entering directory' and `Leaving directory' +# messages which can occur if `w' ends up in MAKEFLAGS. +# In particular we don't look at `^make:' because GNU make might +# be invoked under some other name (usually "gmake"), in which +# case it prints its new name instead of `make'. +if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then + am__include=include + am__quote= + _am_result=GNU +fi +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then + am__include=.include + am__quote="\"" + _am_result=BSD + fi +fi +AC_SUBST([am__include]) +AC_SUBST([am__quote]) +AC_MSG_RESULT([$_am_result]) +rm -f confinc confmf +]) + +# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- + +# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 4 + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it supports --run. +# If it does, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + AC_MSG_WARN([`missing' script is too old or missing]) +fi +]) + +# Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_MKDIR_P +# --------------- +# Check whether `mkdir -p' is supported, fallback to mkinstalldirs otherwise. +# +# Automake 1.8 used `mkdir -m 0755 -p --' to ensure that directories +# created by `make install' are always world readable, even if the +# installer happens to have an overly restrictive umask (e.g. 077). +# This was a mistake. There are at least two reasons why we must not +# use `-m 0755': +# - it causes special bits like SGID to be ignored, +# - it may be too restrictive (some setups expect 775 directories). +# +# Do not use -m 0755 and let people choose whatever they expect by +# setting umask. +# +# We cannot accept any implementation of `mkdir' that recognizes `-p'. +# Some implementations (such as Solaris 8's) are not thread-safe: if a +# parallel make tries to run `mkdir -p a/b' and `mkdir -p a/c' +# concurrently, both version can detect that a/ is missing, but only +# one can create it and the other will error out. Consequently we +# restrict ourselves to GNU make (using the --version option ensures +# this.) +AC_DEFUN([AM_PROG_MKDIR_P], +[if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then + # We used to keeping the `.' as first argument, in order to + # allow $(mkdir_p) to be used without argument. As in + # $(mkdir_p) $(somedir) + # where $(somedir) is conditionally defined. However this is wrong + # for two reasons: + # 1. if the package is installed by a user who cannot write `.' + # make install will fail, + # 2. the above comment should most certainly read + # $(mkdir_p) $(DESTDIR)$(somedir) + # so it does not work when $(somedir) is undefined and + # $(DESTDIR) is not. + # To support the latter case, we have to write + # test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir), + # so the `.' trick is pointless. + mkdir_p='mkdir -p --' +else + # On NextStep and OpenStep, the `mkdir' command does not + # recognize any option. It will interpret all options as + # directories to create, and then abort because `.' already + # exists. + for d in ./-p ./--version; + do + test -d $d && rmdir $d + done + # $(mkinstalldirs) is defined by Automake if mkinstalldirs exists. + if test -f "$ac_aux_dir/mkinstalldirs"; then + mkdir_p='$(mkinstalldirs)' + else + mkdir_p='$(install_sh) -d' + fi +fi +AC_SUBST([mkdir_p])]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 3 + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# ------------------------------ +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), 1)]) + +# _AM_SET_OPTIONS(OPTIONS) +# ---------------------------------- +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[AC_FOREACH([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# Check to make sure that the build environment is sane. -*- Autoconf -*- + +# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005 +# Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 4 + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Just in case +sleep 1 +echo timestamp > conftest.file +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftest.file` + fi + rm -f conftest.file + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken +alias in your environment]) + fi + + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT(yes)]) + +# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_STRIP +# --------------------- +# One issue with vendor `install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in `make install-strip', and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# Installed binaries are usually stripped using `strip' when the user +# run `make install-strip'. However `strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the `STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be `maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# Check how to create a tarball. -*- Autoconf -*- + +# Copyright (C) 2004, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# serial 2 + +# _AM_PROG_TAR(FORMAT) +# -------------------- +# Check how to create a tarball in format FORMAT. +# FORMAT should be one of `v7', `ustar', or `pax'. +# +# Substitute a variable $(am__tar) that is a command +# writing to stdout a FORMAT-tarball containing the directory +# $tardir. +# tardir=directory && $(am__tar) > result.tar +# +# Substitute a variable $(am__untar) that extract such +# a tarball read from stdin. +# $(am__untar) < result.tar +AC_DEFUN([_AM_PROG_TAR], +[# Always define AMTAR for backward compatibility. +AM_MISSING_PROG([AMTAR], [tar]) +m4_if([$1], [v7], + [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'], + [m4_case([$1], [ustar],, [pax],, + [m4_fatal([Unknown tar format])]) +AC_MSG_CHECKING([how to create a $1 tar archive]) +# Loop over all known methods to create a tar archive until one works. +_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' +_am_tools=${am_cv_prog_tar_$1-$_am_tools} +# Do not fold the above two line into one, because Tru64 sh and +# Solaris sh will not grok spaces in the rhs of `-'. +for _am_tool in $_am_tools +do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; + do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break + + # tar/untar a dummy directory, and stop if the command works + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar /dev/null 2>&1 && break + fi +done +rm -rf conftest.dir + +AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) +AC_MSG_RESULT([$am_cv_prog_tar_$1])]) +AC_SUBST([am__tar]) +AC_SUBST([am__untar]) +]) # _AM_PROG_TAR + +m4_include([acinclude.m4]) diff --git a/Engine/lib/lmng/contrib/gcc/sdl-mngplay/autogen.notes b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/autogen.notes new file mode 100644 index 000000000..8f217578d --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/autogen.notes @@ -0,0 +1,54 @@ +[The end of this file is the result of the following command, executed + under tcsh: + + (time /bin/sh autogen.sh) >>&! autogen.log & + + If you have automake and autoconf, the following minimal set of files + is sufficient to rerun the autogen.sh script: + + Makefile.am + acinclude.m4 + autogen.sh + configure.in + mngplay.c + + It will then create the following files: + + aclocal.m4 + mkinstalldirs [symbolic link to utility supplied with automake] + missing [symbolic link to utility supplied with automake] + install-sh [symbolic link to utility supplied with automake] + Makefile.in + configure + + The following documentation files are also part of mngplay: + + AUTHORS + COPYING + ChangeLog + INSTALL + README + + I have replaced the symbolic links with copies of the actual utilities and + included all files in order to match the instructions in the INSTALL file. + Either run configure and make in the usual manner, or, if you prefer, rerun + autogen.sh from scratch and then run configure and make. + + Greg Roelofs, 20010708 +] + +------------------------------------------------------------------------------ + +If you wish to pass any options to configure, please specify them on the +`autogen.sh' command line. +For example use --prefix= to specify the install directory. + +processing . +Running aclocal ... +Running automake --foreign ... +automake: configure.in: installing `./install-sh' +automake: configure.in: installing `./mkinstalldirs' +automake: configure.in: installing `./missing' +Running autoconf ... +Skipping configure process. +1.370u 0.220s 0:03.32 47.8% 0+0k 0+0io 3201pf+0w diff --git a/Engine/lib/lmng/contrib/gcc/sdl-mngplay/autogen.sh b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/autogen.sh new file mode 100644 index 000000000..73864c449 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/autogen.sh @@ -0,0 +1,135 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. + +PKGNAME="mngplay" + +# GRR 20010708: added this; just want to create configure script, not run it: +NOCONFIGURE="true" + +am_opt="--foreign" + +DIE=0 + +# try to guess the proper treetop +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. +(test -f $srcdir/configure.in \ + && test -f $srcdir/mngplay.c) || { + echo -n "**Error**: Directory "\`$srcdir\'" does not look like the" + echo " top-level $PKGNAME directory" + exit 1 +} + + +# check for autoconf +(autoconf --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`autoconf' installed to compile $PKGNAME." + echo "Download the appropriate package for your distribution," + echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" + DIE=1 +} + +# check for automake +(automake --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`automake' installed to compile $PKGNAME." + echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz" + echo "(or a newer version if it is available)." + DIE=1 + NO_AUTOMAKE=yes +} + + +# if no automake, don't bother testing for aclocal +test -n "$NO_AUTOMAKE" || (aclocal --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: Missing \`aclocal'. The version of \`automake'" + echo "installed doesn't appear recent enough." + echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz" + echo "(or a newer version if it is available)." + DIE=1 +} + +if test "$DIE" -eq 1; then + exit 1 +fi + +if test -z "$*"; then + echo + echo "If you wish to pass any options to configure, please specify them on the" + echo \`$0\'" command line." + echo "For example use --prefix= to specify the install directory." + echo +fi + +case $CC in +xlc ) + am_opt="$(am_opt) --include-deps";; +esac + +for coin in `find $srcdir -name configure.in -print` +do + dr=`dirname $coin` + if test -f $dr/NO-AUTO-GEN; then + echo skipping $dr -- flagged as no auto-gen + else + echo processing $dr + macrodirs=`sed -n -e 's,AM_ACLOCAL_INCLUDE(\(.*\)),\1,gp' < $coin` + ( cd $dr + aclocalinclude="$ACLOCAL_FLAGS" + for k in $macrodirs; do + if test -d $k; then + aclocalinclude="$aclocalinclude -I $k" + ##else + ## echo "**Warning**: No such directory \`$k'. Ignored." + fi + done + if grep "^AM_GNU_GETTEXT" configure.in >/dev/null; then + if grep "sed.*POTFILES" configure.in >/dev/null; then + : do nothing -- we still have an old unmodified configure.in + else + echo "Creating $dr/aclocal.m4 ..." + test -r $dr/aclocal.m4 || touch $dr/aclocal.m4 + echo "Running gettextize... Ignore non-fatal messages." + echo "no" | gettextize --force --copy + echo "Making $dr/aclocal.m4 writable ..." + test -r $dr/aclocal.m4 && chmod u+w $dr/aclocal.m4 + fi + fi + if grep "^AM_GNOME_GETTEXT" configure.in >/dev/null; then + echo "Creating $dr/aclocal.m4 ..." + test -r $dr/aclocal.m4 || touch $dr/aclocal.m4 + echo "Running gettextize... Ignore non-fatal messages." + echo "no" | gettextize --force --copy + echo "Making $dr/aclocal.m4 writable ..." + test -r $dr/aclocal.m4 && chmod u+w $dr/aclocal.m4 + fi + if grep "^AC_PROG_LIBTOOL" configure.in >/dev/null; then + echo "Running libtoolize..." + libtoolize --force --copy + fi + echo "Running aclocal $aclocalinclude ..." + aclocal $aclocalinclude + if grep "^AM_CONFIG_HEADER" configure.in >/dev/null; then + echo "Running autoheader..." + autoheader + fi + echo "Running automake $am_opt ..." + automake --add-missing $am_opt + echo "Running autoconf ..." + autoconf + ) + fi +done + +#conf_flags="--enable-maintainer-mode --enable-debug " + +if test x$NOCONFIGURE = x; then + echo Running $srcdir/configure $conf_flags "$@" ... + $srcdir/configure $conf_flags "$@" +else + echo Skipping configure process. +fi + +# end diff --git a/Engine/lib/lmng/contrib/gcc/sdl-mngplay/configure b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/configure new file mode 100644 index 000000000..857b82741 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/configure @@ -0,0 +1,5782 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.61. +# +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +# 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + + + +# PATH needs CR +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +as_nl=' +' +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + { (exit 1); exit 1; } +fi + +# Work around bugs in pre-3.0 UWIN ksh. +for as_var in ENV MAIL MAILPATH +do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# CDPATH. +$as_unset CDPATH + + +if test "x$CONFIG_SHELL" = x; then + if (eval ":") 2>/dev/null; then + as_have_required=yes +else + as_have_required=no +fi + + if test $as_have_required = yes && (eval ": +(as_func_return () { + (exit \$1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = \"\$1\" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test \$exitcode = 0) || { (exit 1); exit 1; } + +( + as_lineno_1=\$LINENO + as_lineno_2=\$LINENO + test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" && + test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; } +") 2> /dev/null; then + : +else + as_candidate_shells= + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + case $as_dir in + /*) + for as_base in sh bash ksh sh5; do + as_candidate_shells="$as_candidate_shells $as_dir/$as_base" + done;; + esac +done +IFS=$as_save_IFS + + + for as_shell in $as_candidate_shells $SHELL; do + # Try only shells that exist, to save several forks. + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { ("$as_shell") 2> /dev/null <<\_ASEOF +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + +: +_ASEOF +}; then + CONFIG_SHELL=$as_shell + as_have_required=yes + if { "$as_shell" 2> /dev/null <<\_ASEOF +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + +: +(as_func_return () { + (exit $1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = "$1" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test $exitcode = 0) || { (exit 1); exit 1; } + +( + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; } + +_ASEOF +}; then + break +fi + +fi + + done + + if test "x$CONFIG_SHELL" != x; then + for as_var in BASH_ENV ENV + do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + done + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} +fi + + + if test $as_have_required = no; then + echo This script requires a shell more modern than all the + echo shells that I found on your system. Please install a + echo modern shell, or manually run the script under such a + echo shell if you do have one. + { (exit 1); exit 1; } +fi + + +fi + +fi + + + +(eval "as_func_return () { + (exit \$1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = \"\$1\" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test \$exitcode = 0") || { + echo No shell found that supports shell functions. + echo Please tell autoconf@gnu.org about your system, + echo including any error possibly output before this + echo message +} + + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line after each line using $LINENO; the second 'sed' + # does the real work. The second script uses 'N' to pair each + # line-number line with the line containing $LINENO, and appends + # trailing '-' during substitution so that $LINENO is not a special + # case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # scripts with optimization help from Paolo Bonzini. Blame Lee + # E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in +-n*) + case `echo 'x\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + *) ECHO_C='\c';; + esac;; +*) + ECHO_N='-n';; +esac + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir +fi +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + + +exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= + +ac_unique_file="mngplay.c" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='SHELL +PATH_SEPARATOR +PACKAGE_NAME +PACKAGE_TARNAME +PACKAGE_VERSION +PACKAGE_STRING +PACKAGE_BUGREPORT +exec_prefix +prefix +program_transform_name +bindir +sbindir +libexecdir +datarootdir +datadir +sysconfdir +sharedstatedir +localstatedir +includedir +oldincludedir +docdir +infodir +htmldir +dvidir +pdfdir +psdir +libdir +localedir +mandir +DEFS +ECHO_C +ECHO_N +ECHO_T +LIBS +build_alias +host_alias +target_alias +INSTALL_PROGRAM +INSTALL_SCRIPT +INSTALL_DATA +CYGPATH_W +PACKAGE +VERSION +ACLOCAL +AUTOCONF +AUTOMAKE +AUTOHEADER +MAKEINFO +install_sh +STRIP +INSTALL_STRIP_PROGRAM +mkdir_p +AWK +SET_MAKE +am__leading_dot +AMTAR +am__tar +am__untar +CC +CFLAGS +LDFLAGS +CPPFLAGS +ac_ct_CC +EXEEXT +OBJEXT +DEPDIR +am__include +am__quote +AMDEP_TRUE +AMDEP_FALSE +AMDEPBACKSLASH +CCDEPMODE +am__fastdepCC_TRUE +am__fastdepCC_FALSE +CPP +GREP +EGREP +HAS_SDL +mngplay_LDADD +mngplay_static_LDADD +LIBOBJS +LTLIBOBJS' +ac_subst_files='' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'` + eval enable_$ac_feature=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'` + eval enable_$ac_feature=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/[-.]/_/g'` + eval with_$ac_package=\$ac_optarg ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/[-.]/_/g'` + eval with_$ac_package=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute directory names. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; } +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + { echo "$as_me: error: Working directory cannot be determined" >&2 + { (exit 1); exit 1; }; } +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + { echo "$as_me: error: pwd does not report name of working directory" >&2 + { (exit 1); exit 1; }; } + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$0" || +$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$0" : 'X\(//\)[^/]' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +echo X"$0" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || { echo "$as_me: error: $ac_msg" >&2 + { (exit 1); exit 1; }; } + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures this package to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +Program names: + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names +_ACEOF +fi + +if test -n "$ac_init_help"; then + + cat <<\_ACEOF + +Optional Features: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --disable-dependency-tracking speeds up one-time build + --enable-dependency-tracking do not reject slow dependency extractors + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +configure +generated by GNU Autoconf 2.61 + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by $as_me, which was +generated by GNU Autoconf 2.61. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + echo "PATH: $as_dir" +done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args '$ac_arg'" + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5 +echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + *) $as_unset $ac_var ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------------- ## +## File substitutions. ## +## ------------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -n "$CONFIG_SITE"; then + set x "$CONFIG_SITE" +elif test "x$prefix" != xNONE; then + set x "$prefix/share/config.site" "$prefix/etc/config.site" +else + set x "$ac_default_prefix/share/config.site" \ + "$ac_default_prefix/etc/config.site" +fi +shift +for ac_site_file +do + if test -r "$ac_site_file"; then + { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + + + + + + + + + + + + + + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +am__api_version="1.9" +ac_aux_dir= +for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&5 +echo "$as_me: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&2;} + { (exit 1); exit 1; }; } +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +{ echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6; } +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in + ./ | .// | /cC/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + done + done + ;; +esac +done +IFS=$as_save_IFS + + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ echo "$as_me:$LINENO: result: $INSTALL" >&5 +echo "${ECHO_T}$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +{ echo "$as_me:$LINENO: checking whether build environment is sane" >&5 +echo $ECHO_N "checking whether build environment is sane... $ECHO_C" >&6; } +# Just in case +sleep 1 +echo timestamp > conftest.file +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftest.file` + fi + rm -f conftest.file + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + { { echo "$as_me:$LINENO: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" >&5 +echo "$as_me: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" >&2;} + { (exit 1); exit 1; }; } + fi + + test "$2" = conftest.file + ) +then + # Ok. + : +else + { { echo "$as_me:$LINENO: error: newly created file is older than distributed files! +Check your system clock" >&5 +echo "$as_me: error: newly created file is older than distributed files! +Check your system clock" >&2;} + { (exit 1); exit 1; }; } +fi +{ echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } +test "$program_prefix" != NONE && + program_transform_name="s&^&$program_prefix&;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s&\$&$program_suffix&;$program_transform_name" +# Double any \ or $. echo might interpret backslashes. +# By default was `s,x,x', remove it if useless. +cat <<\_ACEOF >conftest.sed +s/[\\$]/&&/g;s/;s,x,x,$// +_ACEOF +program_transform_name=`echo $program_transform_name | sed -f conftest.sed` +rm -f conftest.sed + +# expand $ac_aux_dir to an absolute path +am_aux_dir=`cd $ac_aux_dir && pwd` + +test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" +# Use eval to expand $SHELL +if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +else + am_missing_run= + { echo "$as_me:$LINENO: WARNING: \`missing' script is too old or missing" >&5 +echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;} +fi + +if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then + # We used to keeping the `.' as first argument, in order to + # allow $(mkdir_p) to be used without argument. As in + # $(mkdir_p) $(somedir) + # where $(somedir) is conditionally defined. However this is wrong + # for two reasons: + # 1. if the package is installed by a user who cannot write `.' + # make install will fail, + # 2. the above comment should most certainly read + # $(mkdir_p) $(DESTDIR)$(somedir) + # so it does not work when $(somedir) is undefined and + # $(DESTDIR) is not. + # To support the latter case, we have to write + # test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir), + # so the `.' trick is pointless. + mkdir_p='mkdir -p --' +else + # On NextStep and OpenStep, the `mkdir' command does not + # recognize any option. It will interpret all options as + # directories to create, and then abort because `.' already + # exists. + for d in ./-p ./--version; + do + test -d $d && rmdir $d + done + # $(mkinstalldirs) is defined by Automake if mkinstalldirs exists. + if test -f "$ac_aux_dir/mkinstalldirs"; then + mkdir_p='$(mkinstalldirs)' + else + mkdir_p='$(install_sh) -d' + fi +fi + +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_AWK+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_AWK="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + { echo "$as_me:$LINENO: result: $AWK" >&5 +echo "${ECHO_T}$AWK" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + test -n "$AWK" && break +done + +{ echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6; } +set x ${MAKE-make}; ac_make=`echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if { as_var=ac_cv_prog_make_${ac_make}_set; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } + SET_MAKE= +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + +rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null + +# test to see if srcdir already configured +if test "`cd $srcdir && pwd`" != "`pwd`" && + test -f $srcdir/config.status; then + { { echo "$as_me:$LINENO: error: source directory already configured; run \"make distclean\" there first" >&5 +echo "$as_me: error: source directory already configured; run \"make distclean\" there first" >&2;} + { (exit 1); exit 1; }; } +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi + + +# Define the identity of the package. + PACKAGE=mngplay + VERSION=$VERSION + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE "$PACKAGE" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define VERSION "$VERSION" +_ACEOF + +# Some tools Automake needs. + +ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} + + +AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} + + +AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} + + +AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} + + +MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} + +install_sh=${install_sh-"$am_aux_dir/install-sh"} + +# Installed binaries are usually stripped using `strip' when the user +# run `make install-strip'. However `strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the `STRIP' environment variable to overrule this program. +if test "$cross_compiling" != no; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_STRIP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { echo "$as_me:$LINENO: result: $STRIP" >&5 +echo "${ECHO_T}$STRIP" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_STRIP="strip" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5 +echo "${ECHO_T}$ac_ct_STRIP" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +fi +INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s" + +# We need awk for the "check" target. The system "awk" is bad on +# some platforms. +# Always define AMTAR for backward compatibility. + +AMTAR=${AMTAR-"${am_missing_run}tar"} + +am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -' + + + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO: checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (ac_try="$ac_compiler --version >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compiler --version >&5") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -v >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compiler -v >&5") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -V >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compiler -V >&5") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 +echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6; } +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +# +# List of possible output files, starting from the most likely. +# The algorithm is not robust to junk in `.', hence go to wildcards (a.*) +# only as a last resort. b.out is created by i960 compilers. +ac_files='a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out' +# +# The IRIX 6 linker writes into existing files which may not be +# executable, retaining their permissions. Remove them first so a +# subsequent execution test works. +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { (ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi + +{ echo "$as_me:$LINENO: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6; } +if test -z "$ac_file"; then + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext + +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6; } +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +{ echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } + +rm -f a.out a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6; } +{ echo "$as_me:$LINENO: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6; } + +{ echo "$as_me:$LINENO: checking for suffix of executables" >&5 +echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6; } +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +{ echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +{ echo "$as_me:$LINENO: checking for suffix of object files" >&5 +echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6; } +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6; } +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_compiler_gnu=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6; } +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6; } +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + CFLAGS="" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5 +echo $ECHO_N "checking for $CC option to accept ISO C89... $ECHO_C" >&6; } +if test "${ac_cv_prog_cc_c89+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_c89=$ac_arg +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6; } ;; + xno) + { echo "$as_me:$LINENO: result: unsupported" >&5 +echo "${ECHO_T}unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_c89" >&6; } ;; +esac + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +DEPDIR="${am__leading_dot}deps" + +ac_config_commands="$ac_config_commands depfiles" + + +am_make=${MAKE-make} +cat > confinc << 'END' +am__doit: + @echo done +.PHONY: am__doit +END +# If we don't find an include directive, just comment out the code. +{ echo "$as_me:$LINENO: checking for style of include used by $am_make" >&5 +echo $ECHO_N "checking for style of include used by $am_make... $ECHO_C" >&6; } +am__include="#" +am__quote= +_am_result=none +# First try GNU make style include. +echo "include confinc" > confmf +# We grep out `Entering directory' and `Leaving directory' +# messages which can occur if `w' ends up in MAKEFLAGS. +# In particular we don't look at `^make:' because GNU make might +# be invoked under some other name (usually "gmake"), in which +# case it prints its new name instead of `make'. +if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then + am__include=include + am__quote= + _am_result=GNU +fi +# Now try BSD make style include. +if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf + if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then + am__include=.include + am__quote="\"" + _am_result=BSD + fi +fi + + +{ echo "$as_me:$LINENO: result: $_am_result" >&5 +echo "${ECHO_T}$_am_result" >&6; } +rm -f confinc confmf + +# Check whether --enable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then + enableval=$enable_dependency_tracking; +fi + +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' +fi + + +if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi + + + + +depcc="$CC" am_compiler_list= + +{ echo "$as_me:$LINENO: checking dependency style of $depcc" >&5 +echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6; } +if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + case $depmode in + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + none) break ;; + esac + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. + if depmode=$depmode \ + source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5 +echo "${ECHO_T}$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + + +if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 +echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi + +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi + +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ echo "$as_me:$LINENO: result: $CPP" >&5 +echo "${ECHO_T}$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi + +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi + +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&5 +echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5 +echo $ECHO_N "checking for grep that handles long lines and -e... $ECHO_C" >&6; } +if test "${ac_cv_path_GREP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Extract the first word of "grep ggrep" to use in msg output +if test -z "$GREP"; then +set dummy grep ggrep; ac_prog_name=$2 +if test "${ac_cv_path_GREP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_path_GREP_found=false +# Loop through the user's path and test for each of PROGNAME-LIST +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue + # Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + echo $ECHO_N "0123456789$ECHO_C" >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + ac_count=`expr $ac_count + 1` + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + + $ac_path_GREP_found && break 3 + done +done + +done +IFS=$as_save_IFS + + +fi + +GREP="$ac_cv_path_GREP" +if test -z "$GREP"; then + { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 +echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} + { (exit 1); exit 1; }; } +fi + +else + ac_cv_path_GREP=$GREP +fi + + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5 +echo "${ECHO_T}$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ echo "$as_me:$LINENO: checking for egrep" >&5 +echo $ECHO_N "checking for egrep... $ECHO_C" >&6; } +if test "${ac_cv_path_EGREP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + # Extract the first word of "egrep" to use in msg output +if test -z "$EGREP"; then +set dummy egrep; ac_prog_name=$2 +if test "${ac_cv_path_EGREP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_path_EGREP_found=false +# Loop through the user's path and test for each of PROGNAME-LIST +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue + # Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + echo $ECHO_N "0123456789$ECHO_C" >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + ac_count=`expr $ac_count + 1` + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + + $ac_path_EGREP_found && break 3 + done +done + +done +IFS=$as_save_IFS + + +fi + +EGREP="$ac_cv_path_EGREP" +if test -z "$EGREP"; then + { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 +echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} + { (exit 1); exit 1; }; } +fi + +else + ac_cv_path_EGREP=$EGREP +fi + + + fi +fi +{ echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5 +echo "${ECHO_T}$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6; } +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_header_stdc=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +fi +{ echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + + +# Extract the first word of "sdl-config", so it can be a program name with args. +set dummy sdl-config; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_HAS_SDL+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$HAS_SDL"; then + ac_cv_prog_HAS_SDL="$HAS_SDL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_HAS_SDL="yes" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +HAS_SDL=$ac_cv_prog_HAS_SDL +if test -n "$HAS_SDL"; then + { echo "$as_me:$LINENO: result: $HAS_SDL" >&5 +echo "${ECHO_T}$HAS_SDL" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + +if test "x$HAS_SDL" != "xyes"; then + { { echo "$as_me:$LINENO: error: SDL library not found! + You need this for any display to happen. (rather the point) + You can get a copy at " >&5 +echo "$as_me: error: SDL library not found! + You need this for any display to happen. (rather the point) + You can get a copy at " >&2;} + { (exit 1); exit 1; }; } +fi +CFLAGS="$CFLAGS `sdl-config --cflags`" + +mngplay_LDADD="`sdl-config --libs`" +mngplay_static_LDADD="`sdl-config --static-libs`" + + + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. + + + + + + + + + +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +{ echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_Header=no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + +{ echo "$as_me:$LINENO: checking for jpeg_set_defaults in -ljpeg" >&5 +echo $ECHO_N "checking for jpeg_set_defaults in -ljpeg... $ECHO_C" >&6; } +if test "${ac_cv_lib_jpeg_jpeg_set_defaults+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ljpeg $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char jpeg_set_defaults (); +int +main () +{ +return jpeg_set_defaults (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + ac_cv_lib_jpeg_jpeg_set_defaults=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_jpeg_jpeg_set_defaults=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ echo "$as_me:$LINENO: result: $ac_cv_lib_jpeg_jpeg_set_defaults" >&5 +echo "${ECHO_T}$ac_cv_lib_jpeg_jpeg_set_defaults" >&6; } +if test $ac_cv_lib_jpeg_jpeg_set_defaults = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBJPEG 1 +_ACEOF + + LIBS="-ljpeg $LIBS" + +fi + + +{ echo "$as_me:$LINENO: checking for inflate in -lz" >&5 +echo $ECHO_N "checking for inflate in -lz... $ECHO_C" >&6; } +if test "${ac_cv_lib_z_inflate+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lz $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char inflate (); +int +main () +{ +return inflate (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + ac_cv_lib_z_inflate=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_z_inflate=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ echo "$as_me:$LINENO: result: $ac_cv_lib_z_inflate" >&5 +echo "${ECHO_T}$ac_cv_lib_z_inflate" >&6; } +if test $ac_cv_lib_z_inflate = yes; then + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBZ 1 +_ACEOF + + LIBS="-lz $LIBS" + +fi + + { echo "$as_me:$LINENO: checking for mng_readdisplay in -lmng" >&5 +echo $ECHO_N "checking for mng_readdisplay in -lmng... $ECHO_C" >&6; } +if test "${ac_cv_lib_mng_mng_readdisplay+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lmng $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char mng_readdisplay (); +int +main () +{ +return mng_readdisplay (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + ac_cv_lib_mng_mng_readdisplay=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_mng_mng_readdisplay=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ echo "$as_me:$LINENO: result: $ac_cv_lib_mng_mng_readdisplay" >&5 +echo "${ECHO_T}$ac_cv_lib_mng_mng_readdisplay" >&6; } +if test $ac_cv_lib_mng_mng_readdisplay = yes; then + _libmng_present=1 +fi + + if test "${ac_cv_header_libmng_h+set}" = set; then + { echo "$as_me:$LINENO: checking for libmng.h" >&5 +echo $ECHO_N "checking for libmng.h... $ECHO_C" >&6; } +if test "${ac_cv_header_libmng_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +{ echo "$as_me:$LINENO: result: $ac_cv_header_libmng_h" >&5 +echo "${ECHO_T}$ac_cv_header_libmng_h" >&6; } +else + # Is the header compilable? +{ echo "$as_me:$LINENO: checking libmng.h usability" >&5 +echo $ECHO_N "checking libmng.h usability... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6; } + +# Is the header present? +{ echo "$as_me:$LINENO: checking libmng.h presence" >&5 +echo $ECHO_N "checking libmng.h presence... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: libmng.h: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: libmng.h: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: libmng.h: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: libmng.h: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: libmng.h: present but cannot be compiled" >&5 +echo "$as_me: WARNING: libmng.h: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: libmng.h: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: libmng.h: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: libmng.h: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: libmng.h: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: libmng.h: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: libmng.h: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: libmng.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: libmng.h: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: libmng.h: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: libmng.h: in the future, the compiler will take precedence" >&2;} + + ;; +esac +{ echo "$as_me:$LINENO: checking for libmng.h" >&5 +echo $ECHO_N "checking for libmng.h... $ECHO_C" >&6; } +if test "${ac_cv_header_libmng_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_header_libmng_h=$ac_header_preproc +fi +{ echo "$as_me:$LINENO: result: $ac_cv_header_libmng_h" >&5 +echo "${ECHO_T}$ac_cv_header_libmng_h" >&6; } + +fi + + + { echo "$as_me:$LINENO: checking for cmsCreateRGBProfile in -llcms" >&5 +echo $ECHO_N "checking for cmsCreateRGBProfile in -llcms... $ECHO_C" >&6; } +if test "${ac_cv_lib_lcms_cmsCreateRGBProfile+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-llcms $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char cmsCreateRGBProfile (); +int +main () +{ +return cmsCreateRGBProfile (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + ac_cv_lib_lcms_cmsCreateRGBProfile=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_lcms_cmsCreateRGBProfile=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ echo "$as_me:$LINENO: result: $ac_cv_lib_lcms_cmsCreateRGBProfile" >&5 +echo "${ECHO_T}$ac_cv_lib_lcms_cmsCreateRGBProfile" >&6; } +if test $ac_cv_lib_lcms_cmsCreateRGBProfile = yes; then + + if test "${ac_cv_header_lcms_h+set}" = set; then + { echo "$as_me:$LINENO: checking for lcms.h" >&5 +echo $ECHO_N "checking for lcms.h... $ECHO_C" >&6; } +if test "${ac_cv_header_lcms_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +{ echo "$as_me:$LINENO: result: $ac_cv_header_lcms_h" >&5 +echo "${ECHO_T}$ac_cv_header_lcms_h" >&6; } +else + # Is the header compilable? +{ echo "$as_me:$LINENO: checking lcms.h usability" >&5 +echo $ECHO_N "checking lcms.h usability... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6; } + +# Is the header present? +{ echo "$as_me:$LINENO: checking lcms.h presence" >&5 +echo $ECHO_N "checking lcms.h presence... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: lcms.h: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: lcms.h: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: lcms.h: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: lcms.h: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: lcms.h: present but cannot be compiled" >&5 +echo "$as_me: WARNING: lcms.h: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: lcms.h: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: lcms.h: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: lcms.h: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: lcms.h: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: lcms.h: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: lcms.h: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: lcms.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: lcms.h: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: lcms.h: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: lcms.h: in the future, the compiler will take precedence" >&2;} + + ;; +esac +{ echo "$as_me:$LINENO: checking for lcms.h" >&5 +echo $ECHO_N "checking for lcms.h... $ECHO_C" >&6; } +if test "${ac_cv_header_lcms_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_header_lcms_h=$ac_header_preproc +fi +{ echo "$as_me:$LINENO: result: $ac_cv_header_lcms_h" >&5 +echo "${ECHO_T}$ac_cv_header_lcms_h" >&6; } + +fi + + + { echo "$as_me:$LINENO: checking for mnglcms_initlibrary in -lmng" >&5 +echo $ECHO_N "checking for mnglcms_initlibrary in -lmng... $ECHO_C" >&6; } +if test "${ac_cv_lib_mng_mnglcms_initlibrary+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lmng $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char mnglcms_initlibrary (); +int +main () +{ +return mnglcms_initlibrary (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + ac_cv_lib_mng_mnglcms_initlibrary=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_lib_mng_mnglcms_initlibrary=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ echo "$as_me:$LINENO: result: $ac_cv_lib_mng_mnglcms_initlibrary" >&5 +echo "${ECHO_T}$ac_cv_lib_mng_mnglcms_initlibrary" >&6; } +if test $ac_cv_lib_mng_mnglcms_initlibrary = yes; then + + LIBS="$LIBS -llcms" + cat >>confdefs.h <<\_ACEOF +#define HAVE_LIBLCMS 1 +_ACEOF + + _libmng_present=1 + +fi + + +fi + + if test $_libmng_present -eq 1; then + LIBS="-lmng $LIBS" + cat >>confdefs.h <<\_ACEOF +#define HAVE_LIBMNG 1 +_ACEOF + + fi + _libmng_present= + + +ac_config_files="$ac_config_files Makefile" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5 +echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + *) $as_unset $ac_var ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + test "x$cache_file" != "x/dev/null" && + { echo "$as_me:$LINENO: updating cache $cache_file" >&5 +echo "$as_me: updating cache $cache_file" >&6;} + cat confcache >$cache_file + else + { echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5 +echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Transform confdefs.h into DEFS. +# Protect against shell expansion while executing Makefile rules. +# Protect against Makefile macro expansion. +# +# If the first sed substitution is executed (which looks for macros that +# take arguments), then branch to the quote section. Otherwise, +# look for a macro that doesn't take arguments. +ac_script=' +t clear +:clear +s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g +t quote +s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g +t quote +b any +:quote +s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g +s/\[/\\&/g +s/\]/\\&/g +s/\$/$$/g +H +:any +${ + g + s/^\n// + s/\n/ /g + p +} +' +DEFS=`sed -n "$ac_script" confdefs.h` + + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + +if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&5 +echo "$as_me: error: conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." >&2;} + { (exit 1); exit 1; }; } +fi + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + + + +# PATH needs CR +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +as_nl=' +' +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + { (exit 1); exit 1; } +fi + +# Work around bugs in pre-3.0 UWIN ksh. +for as_var in ENV MAIL MAILPATH +do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# CDPATH. +$as_unset CDPATH + + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line after each line using $LINENO; the second 'sed' + # does the real work. The second script uses 'N' to pair each + # line-number line with the line containing $LINENO, and appends + # trailing '-' during substitution so that $LINENO is not a special + # case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # scripts with optimization help from Paolo Bonzini. Blame Lee + # E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in +-n*) + case `echo 'x\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + *) ECHO_C='\c';; + esac;; +*) + ECHO_N='-n';; +esac + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir +fi +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 + +# Save the log message, to keep $[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by $as_me, which was +generated by GNU Autoconf 2.61. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +# Files that config.status was made for. +config_files="$ac_config_files" +config_commands="$ac_config_commands" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + +Configuration files: +$config_files + +Configuration commands: +$config_commands + +Report bugs to ." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.61, + with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2006 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + echo "$ac_cs_version"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + CONFIG_FILES="$CONFIG_FILES $ac_optarg" + ac_need_defaults=false;; + --he | --h | --help | --hel | -h ) + echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +if \$ac_cs_recheck; then + echo "running CONFIG_SHELL=$SHELL $SHELL $0 "$ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 + CONFIG_SHELL=$SHELL + export CONFIG_SHELL + exec $SHELL "$0"$ac_configure_args \$ac_configure_extra_args --no-create --no-recursion +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +# +# INIT-COMMANDS +# +AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + + *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= + trap 'exit_status=$? + { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status +' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || +{ + echo "$me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +# +# Set up the sed scripts for CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "$CONFIG_FILES"; then + +_ACEOF + + + +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + cat >conf$$subs.sed <<_ACEOF +SHELL!$SHELL$ac_delim +PATH_SEPARATOR!$PATH_SEPARATOR$ac_delim +PACKAGE_NAME!$PACKAGE_NAME$ac_delim +PACKAGE_TARNAME!$PACKAGE_TARNAME$ac_delim +PACKAGE_VERSION!$PACKAGE_VERSION$ac_delim +PACKAGE_STRING!$PACKAGE_STRING$ac_delim +PACKAGE_BUGREPORT!$PACKAGE_BUGREPORT$ac_delim +exec_prefix!$exec_prefix$ac_delim +prefix!$prefix$ac_delim +program_transform_name!$program_transform_name$ac_delim +bindir!$bindir$ac_delim +sbindir!$sbindir$ac_delim +libexecdir!$libexecdir$ac_delim +datarootdir!$datarootdir$ac_delim +datadir!$datadir$ac_delim +sysconfdir!$sysconfdir$ac_delim +sharedstatedir!$sharedstatedir$ac_delim +localstatedir!$localstatedir$ac_delim +includedir!$includedir$ac_delim +oldincludedir!$oldincludedir$ac_delim +docdir!$docdir$ac_delim +infodir!$infodir$ac_delim +htmldir!$htmldir$ac_delim +dvidir!$dvidir$ac_delim +pdfdir!$pdfdir$ac_delim +psdir!$psdir$ac_delim +libdir!$libdir$ac_delim +localedir!$localedir$ac_delim +mandir!$mandir$ac_delim +DEFS!$DEFS$ac_delim +ECHO_C!$ECHO_C$ac_delim +ECHO_N!$ECHO_N$ac_delim +ECHO_T!$ECHO_T$ac_delim +LIBS!$LIBS$ac_delim +build_alias!$build_alias$ac_delim +host_alias!$host_alias$ac_delim +target_alias!$target_alias$ac_delim +INSTALL_PROGRAM!$INSTALL_PROGRAM$ac_delim +INSTALL_SCRIPT!$INSTALL_SCRIPT$ac_delim +INSTALL_DATA!$INSTALL_DATA$ac_delim +CYGPATH_W!$CYGPATH_W$ac_delim +PACKAGE!$PACKAGE$ac_delim +VERSION!$VERSION$ac_delim +ACLOCAL!$ACLOCAL$ac_delim +AUTOCONF!$AUTOCONF$ac_delim +AUTOMAKE!$AUTOMAKE$ac_delim +AUTOHEADER!$AUTOHEADER$ac_delim +MAKEINFO!$MAKEINFO$ac_delim +install_sh!$install_sh$ac_delim +STRIP!$STRIP$ac_delim +INSTALL_STRIP_PROGRAM!$INSTALL_STRIP_PROGRAM$ac_delim +mkdir_p!$mkdir_p$ac_delim +AWK!$AWK$ac_delim +SET_MAKE!$SET_MAKE$ac_delim +am__leading_dot!$am__leading_dot$ac_delim +AMTAR!$AMTAR$ac_delim +am__tar!$am__tar$ac_delim +am__untar!$am__untar$ac_delim +CC!$CC$ac_delim +CFLAGS!$CFLAGS$ac_delim +LDFLAGS!$LDFLAGS$ac_delim +CPPFLAGS!$CPPFLAGS$ac_delim +ac_ct_CC!$ac_ct_CC$ac_delim +EXEEXT!$EXEEXT$ac_delim +OBJEXT!$OBJEXT$ac_delim +DEPDIR!$DEPDIR$ac_delim +am__include!$am__include$ac_delim +am__quote!$am__quote$ac_delim +AMDEP_TRUE!$AMDEP_TRUE$ac_delim +AMDEP_FALSE!$AMDEP_FALSE$ac_delim +AMDEPBACKSLASH!$AMDEPBACKSLASH$ac_delim +CCDEPMODE!$CCDEPMODE$ac_delim +am__fastdepCC_TRUE!$am__fastdepCC_TRUE$ac_delim +am__fastdepCC_FALSE!$am__fastdepCC_FALSE$ac_delim +CPP!$CPP$ac_delim +GREP!$GREP$ac_delim +EGREP!$EGREP$ac_delim +HAS_SDL!$HAS_SDL$ac_delim +mngplay_LDADD!$mngplay_LDADD$ac_delim +mngplay_static_LDADD!$mngplay_static_LDADD$ac_delim +LIBOBJS!$LIBOBJS$ac_delim +LTLIBOBJS!$LTLIBOBJS$ac_delim +_ACEOF + + if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 82; then + break + elif $ac_last_try; then + { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed` +if test -n "$ac_eof"; then + ac_eof=`echo "$ac_eof" | sort -nru | sed 1q` + ac_eof=`expr $ac_eof + 1` +fi + +cat >>$CONFIG_STATUS <<_ACEOF +cat >"\$tmp/subs-1.sed" <<\CEOF$ac_eof +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b end +_ACEOF +sed ' +s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g +s/^/s,@/; s/!/@,|#_!!_#|/ +:n +t n +s/'"$ac_delim"'$/,g/; t +s/$/\\/; p +N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n +' >>$CONFIG_STATUS >$CONFIG_STATUS <<_ACEOF +:end +s/|#_!!_#|//g +CEOF$ac_eof +_ACEOF + + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/ +s/:*\${srcdir}:*/:/ +s/:*@srcdir@:*/:/ +s/^\([^=]*=[ ]*\):*/\1/ +s/:*$// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF +fi # test -n "$CONFIG_FILES" + + +for ac_tag in :F $CONFIG_FILES :C $CONFIG_COMMANDS +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) { { echo "$as_me:$LINENO: error: Invalid tag $ac_tag." >&5 +echo "$as_me: error: Invalid tag $ac_tag." >&2;} + { (exit 1); exit 1; }; };; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + { { echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5 +echo "$as_me: error: cannot find input file: $ac_f" >&2;} + { (exit 1); exit 1; }; };; + esac + ac_file_inputs="$ac_file_inputs $ac_f" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input="Generated from "`IFS=: + echo $* | sed 's|^[^:]*/||;s|:[^:]*/|, |g'`" by configure." + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + fi + + case $ac_tag in + *:-:* | *:-) cat >"$tmp/stdin";; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + { as_dir="$ac_dir" + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 +echo "$as_me: error: cannot create directory $as_dir" >&2;} + { (exit 1); exit 1; }; }; } + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= + +case `sed -n '/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p +' $ac_file_inputs` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s&@configure_input@&$configure_input&;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +$ac_datarootdir_hack +" $ac_file_inputs | sed -f "$tmp/subs-1.sed" >$tmp/out + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && + { echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&5 +echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&2;} + + rm -f "$tmp/stdin" + case $ac_file in + -) cat "$tmp/out"; rm -f "$tmp/out";; + *) rm -f "$ac_file"; mv "$tmp/out" $ac_file;; + esac + ;; + + + :C) { echo "$as_me:$LINENO: executing $ac_file commands" >&5 +echo "$as_me: executing $ac_file commands" >&6;} + ;; + esac + + + case $ac_file$ac_mode in + "depfiles":C) test x"$AMDEP_TRUE" != x"" || for mf in $CONFIG_FILES; do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named `Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # So let's grep whole file. + if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then + dirpart=`$as_dirname -- "$mf" || +$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$mf" : 'X\(//\)[^/]' \| \ + X"$mf" : 'X\(//\)$' \| \ + X"$mf" : 'X\(/\)' \| . 2>/dev/null || +echo X"$mf" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running `make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n 's/^U = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`$as_dirname -- "$file" || +$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$file" : 'X\(//\)[^/]' \| \ + X"$file" : 'X\(//\)$' \| \ + X"$file" : 'X\(/\)' \| . 2>/dev/null || +echo X"$file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + { as_dir=$dirpart/$fdir + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 +echo "$as_me: error: cannot create directory $as_dir" >&2;} + { (exit 1); exit 1; }; }; } + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done +done + ;; + + esac +done # for ac_tag + + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + diff --git a/Engine/lib/lmng/contrib/gcc/sdl-mngplay/configure.in b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/configure.in new file mode 100644 index 000000000..758926411 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/configure.in @@ -0,0 +1,35 @@ +dnl Process this file with autoconf to produce a configure script. + +dnl Version of this release +VERSION=0.1 + +AC_INIT(mngplay.c) +AM_INIT_AUTOMAKE(mngplay, $VERSION) + +dnl AC_CANONICAL_SYSTEM +AC_LANG_C + +dnl Checks for programs. +AC_PROG_CC + +dnl Checks for header files. +AC_HEADER_STDC + +dnl check for Simple Direct Media Layer +AC_CHECK_PROG(HAS_SDL, sdl-config, yes) +if test "x$HAS_SDL" != "xyes"; then + AC_MSG_ERROR([ SDL library not found! + You need this for any display to happen. (rather the point) + You can get a copy at ]) +fi +CFLAGS="$CFLAGS `sdl-config --cflags`" + +mngplay_LDADD="`sdl-config --libs`" +mngplay_static_LDADD="`sdl-config --static-libs`" +AC_SUBST(mngplay_LDADD) +AC_SUBST(mngplay_static_LDADD) + +dnl check for libmng - macro in acinclude.m4 +LIBMNG_CHECK() + +AC_OUTPUT(Makefile) diff --git a/Engine/lib/lmng/contrib/gcc/sdl-mngplay/depcomp b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/depcomp new file mode 100644 index 000000000..04701da53 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/depcomp @@ -0,0 +1,530 @@ +#! /bin/sh +# depcomp - compile a program generating dependencies as side-effects + +scriptversion=2005-07-09.11 + +# Copyright (C) 1999, 2000, 2003, 2004, 2005 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Alexandre Oliva . + +case $1 in + '') + echo "$0: No command. Try \`$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: depcomp [--help] [--version] PROGRAM [ARGS] + +Run PROGRAMS ARGS to compile a file, generating dependencies +as side-effects. + +Environment variables: + depmode Dependency tracking mode. + source Source file read by `PROGRAMS ARGS'. + object Object file output by `PROGRAMS ARGS'. + DEPDIR directory where to store dependencies. + depfile Dependency file to output. + tmpdepfile Temporary file to use when outputing dependencies. + libtool Whether libtool is used (yes/no). + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "depcomp $scriptversion" + exit $? + ;; +esac + +if test -z "$depmode" || test -z "$source" || test -z "$object"; then + echo "depcomp: Variables source, object and depmode must be set" 1>&2 + exit 1 +fi + +# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. +depfile=${depfile-`echo "$object" | + sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# Some modes work just like other modes, but use different flags. We +# parameterize here, but still list the modes in the big case below, +# to make depend.m4 easier to write. Note that we *cannot* use a case +# here, because this file can only contain one case statement. +if test "$depmode" = hp; then + # HP compiler uses -M and no extra arg. + gccflag=-M + depmode=gcc +fi + +if test "$depmode" = dashXmstdout; then + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout +fi + +case "$depmode" in +gcc3) +## gcc 3 implements dependency tracking that does exactly what +## we want. Yay! Note: for some reason libtool 1.4 doesn't like +## it if -MD -MP comes after the -MF stuff. Hmm. + "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## There are various ways to get dependency output from gcc. Here's +## why we pick this rather obscure method: +## - Don't want to use -MD because we'd like the dependencies to end +## up in a subdir. Having to rename by hand is ugly. +## (We might end up doing this anyway to support other compilers.) +## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like +## -MM, not -M (despite what the docs say). +## - Using -M directly means running the compiler twice (even worse +## than renaming). + if test -z "$gccflag"; then + gccflag=-MD, + fi + "$@" -Wp,"$gccflag$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz +## The second -e expression handles DOS-style file names with drive letters. + sed -e 's/^[^:]*: / /' \ + -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" +## This next piece of magic avoids the `deleted header file' problem. +## The problem is that when a header file which appears in a .P file +## is deleted, the dependency causes make to die (because there is +## typically no way to rebuild the header). We avoid this by adding +## dummy dependencies for each header file. Too bad gcc doesn't do +## this for us directly. + tr ' ' ' +' < "$tmpdepfile" | +## Some versions of gcc put a space before the `:'. On the theory +## that the space means something, we add a space to the output as +## well. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +sgi) + if test "$libtool" = yes; then + "$@" "-Wp,-MDupdate,$tmpdepfile" + else + "$@" -MDupdate "$tmpdepfile" + fi + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + + if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files + echo "$object : \\" > "$depfile" + + # Clip off the initial element (the dependent). Don't try to be + # clever and replace this with sed code, as IRIX sed won't handle + # lines with more than a fixed number of characters (4096 in + # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; + # the IRIX cc adds comments like `#:fec' to the end of the + # dependency line. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \ + tr ' +' ' ' >> $depfile + echo >> $depfile + + # The second pass generates a dummy entry for each header file. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> $depfile + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. In older versions, this file always lives in the + # current directory. Also, the AIX compiler puts `$object:' at the + # start of each line; $object doesn't have directory information. + # Version 6 uses the directory in both cases. + stripped=`echo "$object" | sed 's/\(.*\)\..*$/\1/'` + tmpdepfile="$stripped.u" + if test "$libtool" = yes; then + "$@" -Wc,-M + else + "$@" -M + fi + stat=$? + + if test -f "$tmpdepfile"; then : + else + stripped=`echo "$stripped" | sed 's,^.*/,,'` + tmpdepfile="$stripped.u" + fi + + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + + if test -f "$tmpdepfile"; then + outname="$stripped.o" + # Each line is of the form `foo.o: dependent.h'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile" + sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile" + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +icc) + # Intel's C compiler understands `-MD -MF file'. However on + # icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c + # ICC 7.0 will fill foo.d with something like + # foo.o: sub/foo.c + # foo.o: sub/foo.h + # which is wrong. We want: + # sub/foo.o: sub/foo.c + # sub/foo.o: sub/foo.h + # sub/foo.c: + # sub/foo.h: + # ICC 7.1 will output + # foo.o: sub/foo.c sub/foo.h + # and will wrap long lines using \ : + # foo.o: sub/foo.c ... \ + # sub/foo.h ... \ + # ... + + "$@" -MD -MF "$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each line is of the form `foo.o: dependent.h', + # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" | + sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +tru64) + # The Tru64 compiler uses -MD to generate dependencies as a side + # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in `foo.d' instead, so we check for that too. + # Subdirectories are respected. + dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` + test "x$dir" = "x$object" && dir= + base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` + + if test "$libtool" = yes; then + # With Tru64 cc, shared objects can also be used to make a + # static library. This mecanism is used in libtool 1.4 series to + # handle both shared and static libraries in a single compilation. + # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d. + # + # With libtool 1.5 this exception was removed, and libtool now + # generates 2 separate objects for the 2 libraries. These two + # compilations output dependencies in in $dir.libs/$base.o.d and + # in $dir$base.o.d. We have to check for both files, because + # one of the two compilations can be disabled. We should prefer + # $dir$base.o.d over $dir.libs/$base.o.d because the latter is + # automatically cleaned when .libs/ is deleted, while ignoring + # the former would cause a distcleancheck panic. + tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4 + tmpdepfile2=$dir$base.o.d # libtool 1.5 + tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5 + tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504 + "$@" -Wc,-MD + else + tmpdepfile1=$dir$base.o.d + tmpdepfile2=$dir$base.d + tmpdepfile3=$dir$base.d + tmpdepfile4=$dir$base.d + "$@" -MD + fi + + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" + do + test -f "$tmpdepfile" && break + done + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" + # That's a tab and a space in the []. + sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" + else + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +#nosideeffect) + # This comment above is used by automake to tell side-effect + # dependency tracking mechanisms from slower ones. + +dashmstdout) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test $1 != '--mode=compile'; do + shift + done + shift + fi + + # Remove `-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + test -z "$dashmflag" && dashmflag=-M + # Require at least two characters before searching for `:' + # in the target name. This is to cope with DOS-style filenames: + # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise. + "$@" $dashmflag | + sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + tr ' ' ' +' < "$tmpdepfile" | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +dashXmstdout) + # This case only exists to satisfy depend.m4. It is never actually + # run, as this mode is specially recognized in the preamble. + exit 1 + ;; + +makedepend) + "$@" || exit $? + # Remove any Libtool call + if test "$libtool" = yes; then + while test $1 != '--mode=compile'; do + shift + done + shift + fi + # X makedepend + shift + cleared=no + for arg in "$@"; do + case $cleared in + no) + set ""; shift + cleared=yes ;; + esac + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift ;; + # Strip any option that makedepend may not understand. Remove + # the object too, otherwise makedepend will parse it as a source file. + -*|$object) + ;; + *) + set fnord "$@" "$arg"; shift ;; + esac + done + obj_suffix="`echo $object | sed 's/^.*\././'`" + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + sed '1,2d' "$tmpdepfile" | tr ' ' ' +' | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" "$tmpdepfile".bak + ;; + +cpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test $1 != '--mode=compile'; do + shift + done + shift + fi + + # Remove `-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + "$@" -E | + sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' | + sed '$ s: \\$::' > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + cat < "$tmpdepfile" >> "$depfile" + sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvisualcpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o, + # because we must use -o when running libtool. + "$@" || exit $? + IFS=" " + for arg + do + case "$arg" in + "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") + set fnord "$@" + shift + shift + ;; + *) + set fnord "$@" "$arg" + shift + shift + ;; + esac + done + "$@" -E | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile" + echo " " >> "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/Engine/lib/lmng/contrib/gcc/sdl-mngplay/install-sh b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/install-sh new file mode 100644 index 000000000..4d4a9519e --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/install-sh @@ -0,0 +1,323 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2005-05-14.22 + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +chmodcmd="$chmodprog 0755" +chowncmd= +chgrpcmd= +stripcmd= +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src= +dst= +dir_arg= +dstarg= +no_target_directory= + +usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: +-c (ignored) +-d create directories instead of installing files. +-g GROUP $chgrpprog installed files to GROUP. +-m MODE $chmodprog installed files to MODE. +-o USER $chownprog installed files to USER. +-s $stripprog installed files. +-t DIRECTORY install into DIRECTORY. +-T report an error if DSTFILE is a directory. +--help display this help and exit. +--version display version info and exit. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG +" + +while test -n "$1"; do + case $1 in + -c) shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + --help) echo "$usage"; exit $?;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -s) stripcmd=$stripprog + shift + continue;; + + -t) dstarg=$2 + shift + shift + continue;; + + -T) no_target_directory=true + shift + continue;; + + --version) echo "$0 $scriptversion"; exit $?;; + + *) # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + test -n "$dir_arg$dstarg" && break + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dstarg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dstarg" + shift # fnord + fi + shift # arg + dstarg=$arg + done + break;; + esac +done + +if test -z "$1"; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call `install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +for src +do + # Protect names starting with `-'. + case $src in + -*) src=./$src ;; + esac + + if test -n "$dir_arg"; then + dst=$src + src= + + if test -d "$dst"; then + mkdircmd=: + chmodcmd= + else + mkdircmd=$mkdirprog + fi + else + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dstarg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + + dst=$dstarg + # Protect names starting with `-'. + case $dst in + -*) dst=./$dst ;; + esac + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dstarg: Is a directory" >&2 + exit 1 + fi + dst=$dst/`basename "$src"` + fi + fi + + # This sed command emulates the dirname command. + dstdir=`echo "$dst" | sed -e 's,/*$,,;s,[^/]*$,,;s,/*$,,;s,^$,.,'` + + # Make sure that the destination directory exists. + + # Skip lots of stat calls in the usual case. + if test ! -d "$dstdir"; then + defaultIFS=' + ' + IFS="${IFS-$defaultIFS}" + + oIFS=$IFS + # Some sh's can't handle IFS=/ for some reason. + IFS='%' + set x `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'` + shift + IFS=$oIFS + + pathcomp= + + while test $# -ne 0 ; do + pathcomp=$pathcomp$1 + shift + if test ! -d "$pathcomp"; then + $mkdirprog "$pathcomp" + # mkdir can fail with a `File exist' error in case several + # install-sh are creating the directory concurrently. This + # is OK. + test -d "$pathcomp" || exit + fi + pathcomp=$pathcomp/ + done + fi + + if test -n "$dir_arg"; then + $doit $mkdircmd "$dst" \ + && { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \ + && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \ + && { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \ + && { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; } + + else + dstfile=`basename "$dst"` + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + trap '(exit $?); exit' 1 2 13 15 + + # Copy the file name to the temp name. + $doit $cpprog "$src" "$dsttmp" && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \ + && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \ + && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \ + && { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } && + + # Now rename the file to the real destination. + { $doit $mvcmd -f "$dsttmp" "$dstdir/$dstfile" 2>/dev/null \ + || { + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + if test -f "$dstdir/$dstfile"; then + $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \ + || $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \ + || { + echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2 + (exit 1); exit 1 + } + else + : + fi + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dstdir/$dstfile" + } + } + fi || { (exit 1); exit 1; } +done + +# The final little trick to "correctly" pass the exit status to the exit trap. +{ + (exit 0); exit 0 +} + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/Engine/lib/lmng/contrib/gcc/sdl-mngplay/missing b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/missing new file mode 100644 index 000000000..894e786e1 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/missing @@ -0,0 +1,360 @@ +#! /bin/sh +# Common stub for a few missing GNU programs while installing. + +scriptversion=2005-06-08.21 + +# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# Originally by Fran,cois Pinard , 1996. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 +fi + +run=: + +# In the cases where this matters, `missing' is being run in the +# srcdir already. +if test -f configure.ac; then + configure_ac=configure.ac +else + configure_ac=configure.in +fi + +msg="missing on your system" + +case "$1" in +--run) + # Try to run requested program, and just exit if it succeeds. + run= + shift + "$@" && exit 0 + # Exit code 63 means version mismatch. This often happens + # when the user try to use an ancient version of a tool on + # a file that requires a minimum version. In this case we + # we should proceed has if the program had been absent, or + # if --run hadn't been passed. + if test $? = 63; then + run=: + msg="probably too old" + fi + ;; + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an +error status if there is no known handling for PROGRAM. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + --run try to run the given command, and emulate it if it fails + +Supported PROGRAM values: + aclocal touch file \`aclocal.m4' + autoconf touch file \`configure' + autoheader touch file \`config.h.in' + automake touch all \`Makefile.in' files + bison create \`y.tab.[ch]', if possible, from existing .[ch] + flex create \`lex.yy.c', if possible, from existing .c + help2man touch the output file + lex create \`lex.yy.c', if possible, from existing .c + makeinfo touch the output file + tar try tar, gnutar, gtar, then tar without non-portable flags + yacc create \`y.tab.[ch]', if possible, from existing .[ch] + +Send bug reports to ." + exit $? + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing $scriptversion (GNU Automake)" + exit $? + ;; + + -*) + echo 1>&2 "$0: Unknown \`$1' option" + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 + ;; + +esac + +# Now exit if we have it, but it failed. Also exit now if we +# don't have it and --version was passed (most likely to detect +# the program). +case "$1" in + lex|yacc) + # Not GNU programs, they don't have --version. + ;; + + tar) + if test -n "$run"; then + echo 1>&2 "ERROR: \`tar' requires --run" + exit 1 + elif test "x$2" = "x--version" || test "x$2" = "x--help"; then + exit 1 + fi + ;; + + *) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + elif test "x$2" = "x--version" || test "x$2" = "x--help"; then + # Could not run --version or --help. This is probably someone + # running `$TOOL --version' or `$TOOL --help' to check whether + # $TOOL exists and not knowing $TOOL uses missing. + exit 1 + fi + ;; +esac + +# If it does not exist, or fails to run (possibly an outdated version), +# try to emulate it. +case "$1" in + aclocal*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`acinclude.m4' or \`${configure_ac}'. You might want + to install the \`Automake' and \`Perl' packages. Grab them from + any GNU archive site." + touch aclocal.m4 + ;; + + autoconf) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`${configure_ac}'. You might want to install the + \`Autoconf' and \`GNU m4' packages. Grab them from any GNU + archive site." + touch configure + ;; + + autoheader) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`acconfig.h' or \`${configure_ac}'. You might want + to install the \`Autoconf' and \`GNU m4' packages. Grab them + from any GNU archive site." + files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` + test -z "$files" && files="config.h" + touch_files= + for f in $files; do + case "$f" in + *:*) touch_files="$touch_files "`echo "$f" | + sed -e 's/^[^:]*://' -e 's/:.*//'`;; + *) touch_files="$touch_files $f.in";; + esac + done + touch $touch_files + ;; + + automake*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. + You might want to install the \`Automake' and \`Perl' packages. + Grab them from any GNU archive site." + find . -type f -name Makefile.am -print | + sed 's/\.am$/.in/' | + while read f; do touch "$f"; done + ;; + + autom4te) + echo 1>&2 "\ +WARNING: \`$1' is needed, but is $msg. + You might have modified some files without having the + proper tools for further handling them. + You can get \`$1' as part of \`Autoconf' from any GNU + archive site." + + file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'` + test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'` + if test -f "$file"; then + touch $file + else + test -z "$file" || exec >$file + echo "#! /bin/sh" + echo "# Created by GNU Automake missing as a replacement of" + echo "# $ $@" + echo "exit 0" + chmod +x $file + exit 1 + fi + ;; + + bison|yacc) + echo 1>&2 "\ +WARNING: \`$1' $msg. You should only need it if + you modified a \`.y' file. You may need the \`Bison' package + in order for those modifications to take effect. You can get + \`Bison' from any GNU archive site." + rm -f y.tab.c y.tab.h + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.y) + SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.c + fi + SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.h + fi + ;; + esac + fi + if [ ! -f y.tab.h ]; then + echo >y.tab.h + fi + if [ ! -f y.tab.c ]; then + echo 'main() { return 0; }' >y.tab.c + fi + ;; + + lex|flex) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a \`.l' file. You may need the \`Flex' package + in order for those modifications to take effect. You can get + \`Flex' from any GNU archive site." + rm -f lex.yy.c + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.l) + SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" lex.yy.c + fi + ;; + esac + fi + if [ ! -f lex.yy.c ]; then + echo 'main() { return 0; }' >lex.yy.c + fi + ;; + + help2man) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a dependency of a manual page. You may need the + \`Help2man' package in order for those modifications to take + effect. You can get \`Help2man' from any GNU archive site." + + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'` + fi + if [ -f "$file" ]; then + touch $file + else + test -z "$file" || exec >$file + echo ".ab help2man is required to generate this page" + exit 1 + fi + ;; + + makeinfo) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a \`.texi' or \`.texinfo' file, or any other file + indirectly affecting the aspect of the manual. The spurious + call might also be the consequence of using a buggy \`make' (AIX, + DU, IRIX). You might want to install the \`Texinfo' package or + the \`GNU make' package. Grab either from any GNU archive site." + # The file to touch is that specified with -o ... + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + # ... or it is the one specified with @setfilename ... + infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` + file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $infile` + # ... or it is derived from the source name (dir/f.texi becomes f.info) + test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info + fi + # If the file does not exist, the user really needs makeinfo; + # let's fail without touching anything. + test -f $file || exit 1 + touch $file + ;; + + tar) + shift + + # We have already tried tar in the generic part. + # Look for gnutar/gtar before invocation to avoid ugly error + # messages. + if (gnutar --version > /dev/null 2>&1); then + gnutar "$@" && exit 0 + fi + if (gtar --version > /dev/null 2>&1); then + gtar "$@" && exit 0 + fi + firstarg="$1" + if shift; then + case "$firstarg" in + *o*) + firstarg=`echo "$firstarg" | sed s/o//` + tar "$firstarg" "$@" && exit 0 + ;; + esac + case "$firstarg" in + *h*) + firstarg=`echo "$firstarg" | sed s/h//` + tar "$firstarg" "$@" && exit 0 + ;; + esac + fi + + echo 1>&2 "\ +WARNING: I can't seem to be able to run \`tar' with the given arguments. + You may want to install GNU tar or Free paxutils, or check the + command line arguments." + exit 1 + ;; + + *) + echo 1>&2 "\ +WARNING: \`$1' is needed, and is $msg. + You might have modified some files without having the + proper tools for further handling them. Check the \`README' file, + it often tells you about the needed prerequisites for installing + this package. You may also peek at any GNU archive site, in case + some other package would contain this missing \`$1' program." + exit 1 + ;; +esac + +exit 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/Engine/lib/lmng/contrib/gcc/sdl-mngplay/mngplay.c b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/mngplay.c new file mode 100644 index 000000000..b6bc0fa82 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/sdl-mngplay/mngplay.c @@ -0,0 +1,507 @@ +/* + mngplay + + $Date: 2003/12/07 09:45:16 $ + + Ralph Giles + + This program my be redistributed under the terms of the + GNU General Public Licence, version 2, or at your preference, + any later version. + + (this assuming there's no problem with libmng not being GPL...) + + + this is an SDL based mng player. the code is very rough; + patches welcome. + + + GRR 20010708: added SDL/libmng/zlib/libjpeg version info, mouse-click + handling (alternate quit mode); improved automake setup + + Raphael Assenat + 2003/11/26: added command line options to run in alternate color depths. + +*/ + +#include +#include + +#include +#include + +#include // basename + + +#define DEFAULT_SDL_VIDEO_DEPTH 32 + +/* structure for keeping track of our mng stream inside the callbacks */ +typedef struct { + FILE *file; /* pointer to the file we're decoding */ + char *filename; /* pointer to the file's path/name */ + SDL_Surface *surface; /* SDL display */ + mng_uint32 delay; /* ticks to wait before resuming decode */ + int sdl_video_depth; /* The depth for SDL_SetVideoMode */ +} mngstuff; + +/* callbacks for the mng decoder */ + +/* memory allocation; data must be zeroed */ +mng_ptr mymngalloc(mng_uint32 size) +{ + return (mng_ptr)calloc(1, size); +} + +/* memory deallocation */ +void mymngfree(mng_ptr p, mng_uint32 size) +{ + free(p); + return; +} + +mng_bool mymngopenstream(mng_handle mng) +{ + mngstuff *mymng; + + /* look up our stream struct */ + mymng = (mngstuff*)mng_get_userdata(mng); + + /* open the file */ + mymng->file = fopen(mymng->filename, "rb"); + if (mymng->file == NULL) { + fprintf(stderr, "unable to open '%s'\n", mymng->filename); + return MNG_FALSE; + } + + return MNG_TRUE; +} + +mng_bool mymngclosestream(mng_handle mng) +{ + mngstuff *mymng; + + /* look up our stream struct */ + mymng = (mngstuff*)mng_get_userdata(mng); + + /* close the file */ + fclose(mymng->file); + mymng->file = NULL; /* for safety */ + + return MNG_TRUE; +} + +/* feed data to the decoder */ +mng_bool mymngreadstream(mng_handle mng, mng_ptr buffer, + mng_uint32 size, mng_uint32 *bytesread) +{ + mngstuff *mymng; + + /* look up our stream struct */ + mymng = (mngstuff*)mng_get_userdata(mng); + + /* read the requested amount of data from the file */ + *bytesread = fread(buffer, 1, size, mymng->file); + + return MNG_TRUE; +} + +/* the header's been read. set up the display stuff */ +mng_bool mymngprocessheader(mng_handle mng, + mng_uint32 width, mng_uint32 height) +{ + mngstuff *mymng; + SDL_Surface *screen; + char title[256]; + +// fprintf(stderr, "our mng is %dx%d\n", width,height); + + /* retreive our user data */ + mymng = (mngstuff*)mng_get_userdata(mng); + + screen = SDL_SetVideoMode(width,height, mymng->sdl_video_depth, SDL_SWSURFACE); + if (screen == NULL) { + fprintf(stderr, "unable to allocate %dx%d video memory: %s\n", + width, height, SDL_GetError()); + return MNG_FALSE; + } + + printf("SDL Video Mode: %dx%d bpp=%d\n", width, height, mymng->sdl_video_depth); + + /* save the surface pointer */ + mymng->surface = screen; + + /* set a descriptive window title */ + snprintf(title, 256, "mngplay: %s", mymng->filename); + SDL_WM_SetCaption(title, "mngplay"); + + /* in necessary, lock the drawing surface to the decoder + can safely fill it. We'll unlock elsewhere before display */ + if (SDL_MUSTLOCK(mymng->surface)) { + if ( SDL_LockSurface(mymng->surface) < 0 ) { + fprintf(stderr, "could not lock display surface\n"); + exit(1); + } + } + +/* + printf("RGBA Masks: %08X %08X %08X %08X\n", + mymng->surface->format->Rmask, + mymng->surface->format->Gmask, + mymng->surface->format->Bmask, + mymng->surface->format->Amask); + printf("RGBA Shifts: %08X %08X %08X %08X\n", + mymng->surface->format->Rshift, + mymng->surface->format->Gshift, + mymng->surface->format->Bshift, + mymng->surface->format->Ashift); +*/ + /* Choose a canvas style which matches the SDL_Surface pixel format */ + switch(mymng->surface->format->BitsPerPixel) + { + case 32: + if (mymng->surface->format->Amask==0) { + /* No alpha (padding byte) */ + if (mymng->surface->format->Bshift==0) { + /* Blue first */ + mng_set_canvasstyle(mng, MNG_CANVAS_BGRX8); + } else { + /* Red first */ + fprintf(stderr, "No matching mng canvas for sdl pixel format. Colors may be wrong.\n"); + mng_set_canvasstyle(mng, MNG_CANVAS_BGRX8); + } + } + else { + /* Real alpha */ + if (mymng->surface->format->Bshift==0) { + /* Blue first */ + mng_set_canvasstyle(mng, MNG_CANVAS_BGRA8); + } else { + /* Red first */ + mng_set_canvasstyle(mng, MNG_CANVAS_RGBA8); + } + } + break; + case 24: + if (mymng->surface->format->Amask==0) { + /* No alpha here should mean true rgb24bit */ + if (mymng->surface->format->Bshift==0) { + /* Blue first */ + mng_set_canvasstyle(mng, MNG_CANVAS_BGR8); + } else { + /* Red first */ + mng_set_canvasstyle(mng, MNG_CANVAS_RGB8); + } + } + else { + /* If there is an alpha and we are in 24 bpp, this must + * mean rgb5658 */ + if (mymng->surface->format->Bshift==0) { + /* Blue first */ + mng_set_canvasstyle(mng, MNG_CANVAS_BGRA565); + } else { + /* Red first */ + mng_set_canvasstyle(mng, MNG_CANVAS_RGBA565); + } + } + break; + case 16: + if (mymng->surface->format->Bshift==0) { + /* Blue first */ + mng_set_canvasstyle(mng, MNG_CANVAS_BGR565); + } else { + /* Red first */ + mng_set_canvasstyle(mng, MNG_CANVAS_RGB565); + } + break; + default: + return MNG_FALSE; + } + + return MNG_TRUE; +} + +/* return a row pointer for the decoder to fill */ +mng_ptr mymnggetcanvasline(mng_handle mng, mng_uint32 line) +{ + mngstuff *mymng; + SDL_Surface *surface; + mng_ptr row; + + /* dereference our structure */ + mymng = (mngstuff*)mng_get_userdata(mng); + + /* we assume any necessary locking has happened + outside, in the frame level code */ + row = mymng->surface->pixels + mymng->surface->pitch*line; + +// fprintf(stderr, " returning pointer to line %d (%p)\n", line, row); + + return (row); +} + +/* timer */ +mng_uint32 mymnggetticks(mng_handle mng) +{ + mng_uint32 ticks; + + ticks = (mng_uint32)SDL_GetTicks(); +// fprintf(stderr, " %d\t(returning tick count)\n",ticks); + + return(ticks); +} + +mng_bool mymngrefresh(mng_handle mng, mng_uint32 x, mng_uint32 y, + mng_uint32 w, mng_uint32 h) +{ + mngstuff *mymng; + SDL_Rect frame; + + frame.x = x; + frame.y = y; + frame.w = w; + frame.h = h; + + /* dereference our structure */ + mymng = (mngstuff*)mng_get_userdata(mng); + + /* if necessary, unlock the display */ + if (SDL_MUSTLOCK(mymng->surface)) { + SDL_UnlockSurface(mymng->surface); + } + + /* refresh the screen with the new frame */ + SDL_UpdateRects(mymng->surface, 1, &frame); + + /* in necessary, relock the drawing surface */ + if (SDL_MUSTLOCK(mymng->surface)) { + if ( SDL_LockSurface(mymng->surface) < 0 ) { + fprintf(stderr, "could not lock display surface\n"); + return MNG_FALSE; + } + } + + + return MNG_TRUE; +} + +/* interframe delay callback */ +mng_bool mymngsettimer(mng_handle mng, mng_uint32 msecs) +{ + mngstuff *mymng; + +// fprintf(stderr," pausing for %d ms\n", msecs); + + /* look up our stream struct */ + mymng = (mngstuff*)mng_get_userdata(mng); + + /* set the timer for when the decoder wants to be woken */ + mymng->delay = msecs; + + return MNG_TRUE; + +} + +mng_bool mymngerror(mng_handle mng, mng_int32 code, mng_int8 severity, + mng_chunkid chunktype, mng_uint32 chunkseq, + mng_int32 extra1, mng_int32 extra2, mng_pchar text) +{ + mngstuff *mymng; + char chunk[5]; + + /* dereference our data so we can get the filename */ + mymng = (mngstuff*)mng_get_userdata(mng); + + /* pull out the chuck type as a string */ + // FIXME: does this assume unsigned char? + chunk[0] = (char)((chunktype >> 24) & 0xFF); + chunk[1] = (char)((chunktype >> 16) & 0xFF); + chunk[2] = (char)((chunktype >> 8) & 0xFF); + chunk[3] = (char)((chunktype ) & 0xFF); + chunk[4] = '\0'; + + /* output the error */ + fprintf(stderr, "error playing '%s' chunk %s (%d):\n", + mymng->filename, chunk, chunkseq); + fprintf(stderr, "%s\n", text); + + return (0); +} + +int mymngquit(mng_handle mng) +{ + mngstuff *mymng; + + /* dereference our data so we can free it */ + mymng = (mngstuff*)mng_get_userdata(mng); + + /* cleanup. this will call mymngclosestream */ + mng_cleanup(&mng); + + /* free our data */ + free(mymng); + + /* quit */ + exit(0); +} + +int checkevents(mng_handle mng) +{ + SDL_Event event; + + /* check if there's an event pending */ + if (!SDL_PollEvent(&event)) { + return 0; /* no events pending */ + } + + /* we have an event; process it */ + switch (event.type) { + case SDL_QUIT: + mymngquit(mng); /* quit */ + break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + mymngquit(mng); + break; + case SDL_KEYUP: + switch (event.key.keysym.sym) { + case SDLK_ESCAPE: + case SDLK_q: + mymngquit(mng); + break; + } + /* FALL THROUGH */ + default: + return 1; + } + + return 0; /* GRR ADDED: non-void function */ +} + +int main(int argc, char *argv[]) +{ + mngstuff *mymng; + mng_handle mng; + SDL_Rect updaterect; + + if (argc < 2) { + const SDL_version *pSDLver = SDL_Linked_Version(); + + fprintf(stderr, "Usage: %s mngfile [depth]\n\n", basename(argv[0])); + fprintf(stderr, " where 'depth' is 15,16,24 or 32\n"); + fprintf(stderr, + " Compiled with SDL %d.%d.%d; using SDL %d.%d.%d.\n", + SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL, + pSDLver->major, pSDLver->minor, pSDLver->patch); + fprintf(stderr, " Compiled with libmng %s; using libmng %s.\n", + MNG_VERSION_TEXT, mng_version_text()); + fprintf(stderr, " Compiled with zlib %s; using zlib %s.\n", + ZLIB_VERSION, zlib_version); +#ifdef JPEG_LIB_VERSION + { + int major = JPEG_LIB_VERSION / 10; + int minor = JPEG_LIB_VERSION % 10; + char minoralpha[2]; + + if (minor) { + minoralpha[0] = (char)(minor - 1 + 'a'); + minoralpha[1] = '\0'; + } else + minoralpha[0] = '\0'; + fprintf(stderr, " Compiled with libjpeg %d%s.\n", + major, minoralpha); + } +#endif + fprintf(stderr, + "\nPress Esc or Q, or click mouse button, to quit.\n"); + exit(1); + } + + /* allocate our stream data structure */ + mymng = (mngstuff*)calloc(1, sizeof(*mymng)); + if (mymng == NULL) { + fprintf(stderr, "could not allocate stream structure.\n"); + exit(0); + } + + /* pass the name of the file we want to play */ + mymng->filename = argv[1]; + + /* pass the color depth we wish to use */ + if (argc>=3) { + mymng->sdl_video_depth = atoi(argv[2]); + switch(mymng->sdl_video_depth) { + case 15: + case 16: + case 24: + case 32: + break; + default: + fprintf(stderr, "Unsupported color depth. Choices are: 15, 16, 24 and 32\n"); + exit(1); + } + } + else { + mymng->sdl_video_depth = DEFAULT_SDL_VIDEO_DEPTH; + } + + /* set up the mng decoder for our stream */ + mng = mng_initialize(mymng, mymngalloc, mymngfree, MNG_NULL); + if (mng == MNG_NULL) { + fprintf(stderr, "could not initialize libmng.\n"); + exit(1); + } + + /* set the callbacks */ + mng_setcb_errorproc(mng, mymngerror); + mng_setcb_openstream(mng, mymngopenstream); + mng_setcb_closestream(mng, mymngclosestream); + mng_setcb_readdata(mng, mymngreadstream); + mng_setcb_gettickcount(mng, mymnggetticks); + mng_setcb_settimer(mng, mymngsettimer); + mng_setcb_processheader(mng, mymngprocessheader); + mng_setcb_getcanvasline(mng, mymnggetcanvasline); + mng_setcb_refresh(mng, mymngrefresh); + /* FIXME: should check for errors here */ + + /* initialize SDL */ + if (SDL_Init(SDL_INIT_VIDEO) < 0) { + fprintf(stderr, "%s: Unable to initialize SDL (%s)\n", + argv[0], SDL_GetError()); + exit(1); + } + /* arrange to call the shutdown routine before we exit */ + atexit(SDL_Quit); + + /* restrict event handling to the relevant bits */ + SDL_EventState(SDL_KEYDOWN, SDL_IGNORE); /* keyup only */ + SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE); +// SDL_EventState(SDL_MOUSEBUTTONDOWN, SDL_IGNORE); +// SDL_EventState(SDL_MOUSEBUTTONUP, SDL_IGNORE); + +// fprintf(stderr, "playing mng...maybe.\n"); + + mng_readdisplay(mng); + + /* loop though the frames */ + while (mymng->delay) { +// fprintf(stderr, " waiting for %d ms\n", mymng->delay); + SDL_Delay(mymng->delay); + + /* reset the delay in case the decoder + doesn't update it again */ + mymng->delay = 0; + + mng_display_resume(mng); + + /* check for user input (just quit at this point) */ + checkevents(mng); + } + + /* żhay alguno? pause before quitting */ + fprintf(stderr, "pausing before shutdown...\n"); + SDL_Delay(1000); + + /* cleanup and quit */ + mymngquit(mng); +} + diff --git a/Engine/lib/lmng/contrib/gcc/xmngview/Makefile b/Engine/lib/lmng/contrib/gcc/xmngview/Makefile new file mode 100644 index 000000000..4b38662f7 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/xmngview/Makefile @@ -0,0 +1,20 @@ +PROC=athlon +CFLAGS=-g -Wall -O2 -march=$(PROC) -mcpu=$(PROC) +MOTIFLIB=/usr/local/lesstif/lib +MOTIFINC=/usr/local/lesstif/include +MNGLIB=-lmng +LIBS=-L/usr/X11R6/lib -L$(MOTIFLIB) -lXm -lXt -lX11 $(MNGLIB) -lm +INC=-I/usr/X11R6/include -I$(MOTIFINC) +CC=gcc +LDFLAGS= + +all: clean compile + +compile: + $(CC) $(CFLAGS) $(INC) color.c xmngview.c -o xmngview $(LIBS) + +clean: + rm -f xmngview core + +install: + cp -a xmngview /usr/local/bin/xmngview diff --git a/Engine/lib/lmng/contrib/gcc/xmngview/README b/Engine/lib/lmng/contrib/gcc/xmngview/README new file mode 100644 index 000000000..576228efa --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/xmngview/README @@ -0,0 +1,23 @@ +Program: +======== + +'xmngview' displays MNG/JNG/PNG files. The program can be called + + xmngview + xmngview filename + +This program is used within a plugin library for a browser: + xmngview -wWINDOWID -bgBGPIXEL filename + +Limitations: +============ + needs Lesstif/Motif, i.e. '-lXm -lXt -lX11'. + depth >= 8 (tested with 8bpp, 16bpp, 24bpp) + perhaps INTEL only + perhaps LINUX only + +libmng: +======= + read the first lines of xmngview.c + +szukw000, March 2003 diff --git a/Engine/lib/lmng/contrib/gcc/xmngview/color.c b/Engine/lib/lmng/contrib/gcc/xmngview/color.c new file mode 100644 index 000000000..2e75a1c92 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/xmngview/color.c @@ -0,0 +1,645 @@ +/* + * This code is mainly code I have found in + * ida-0.14 Gerd Knorr + * http://bytesex.org/ida + * Ida is a small and fast image viewer, motif-based. + * + * Copyright (C) 2002 Gerd Knorr + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "xmng.h" + +static void (*dither_line)(unsigned char *src, unsigned char *dest, + unsigned int y, unsigned int width); +static void dither_line_gray(unsigned char *src, unsigned char *dest, + unsigned int y, unsigned int width); +static void dither_line_color(unsigned char *src, unsigned char *dest, + unsigned int y, unsigned int width); +static void init_dither(int shades_r, int shades_g, int shades_b, + int shades_gray); + +static XVisualInfo *vis_info; + +/* PseudoColor: ditherresult => colormap-entry +*/ +static int x11_colors; +static int x11_grays; +static unsigned long *x11_map; +static unsigned long x11_map_color[256]; +static unsigned long x11_map_gray[64]; + +static unsigned long x11_red; +static unsigned long x11_green; +static unsigned long x11_blue; + +static int try_red[] = {4, 6, 6, 5, 4}; +static int try_green[] = {8, 6, 6, 5, 4}; +static int try_blue[] = {4, 6, 4, 5, 4}; + +/* TrueColor: r,g,b => X11-color +*/ +static unsigned long x11_lut_red[256]; +static unsigned long x11_lut_green[256]; +static unsigned long x11_lut_blue[256]; +static unsigned long x11_lut_gray[256]; + +#define x11_black x11_map_gray[0] +#define x11_gray x11_map_gray[47*x11_grays/64] +#define x11_lightgray x11_map_gray[55*x11_grays/64] +#define x11_white x11_map_gray[63*x11_grays/64] + +static int x11_alloc_grays(Display * dpy, Colormap cmap, unsigned long *colors, + int gray) +{ + XColor akt_color; + int i, upb; + upb = gray - 1; + + for (i = 0; i < gray; i++) + { +/* FIXME: original code + akt_color.red = i * 65535 / upb; + akt_color.green = i * 65535 / upb; + akt_color.blue = i * 65535 / upb; +*/ + akt_color.red = i * 255 / upb; + akt_color.green = i * 255 / upb; + akt_color.blue = i * 255 / upb; + + if (!XAllocColor(dpy, cmap, &akt_color)) + { +/* no free color cell available +*/ + XFreeColors(dpy, cmap, colors, i, 0); + return 1; + } + colors[i] = akt_color.pixel; + } + return 0; +} + +static int x11_alloc_colorcube(Display * dpy, Colormap cmap, + unsigned long *colors, int red, int green, int blue) +{ + XColor akt_color; + int i, upb_r, upb_g, upb_b; + + upb_r = red - 1; upb_g = green - 1; upb_b = blue - 1; + + for (i = 0; i < red * green * blue; i++) + { + akt_color.red = ((i / (green * blue)) % red) * 65535 / upb_r; + akt_color.green = ((i / blue) % green) * 65535 / upb_g; + akt_color.blue = (i % blue) * 65535 / upb_b; + + if (!XAllocColor(dpy, cmap, &akt_color)) + { +/* no free color cell available +*/ + XFreeColors(dpy, cmap, colors, i, 0); + return 1; + } + colors[i] = akt_color.pixel; + } + return 0; +} + +static unsigned long x11_alloc_color(Display * dpy, Colormap cmap, int red, + int green, int blue) +{ + XColor akt_color; + + akt_color.red = red; + akt_color.green = green; + akt_color.blue = blue; + + XAllocColor(dpy, cmap, &akt_color); + return akt_color.pixel; +} + +static void x11_create_lut(unsigned long red_mask, + unsigned long green_mask, + unsigned long blue_mask) +{ + int rgb_red_bits = 0; + int rgb_red_shift = 0; + int rgb_green_bits = 0; + int rgb_green_shift = 0; + int rgb_blue_bits = 0; + int rgb_blue_shift = 0; + int i; + unsigned long mask; + + for (i = 0; i < 24; i++) + { + mask = (1 << i); + if (red_mask & mask) + rgb_red_bits++; + else + if (!rgb_red_bits) + rgb_red_shift++; + if (green_mask & mask) + rgb_green_bits++; + else + if (!rgb_green_bits) + rgb_green_shift++; + + if (blue_mask & mask) + rgb_blue_bits++; + else + if (!rgb_blue_bits) + rgb_blue_shift++; + } + + for (i = 0; i < 256; i++) + { + x11_lut_red[i] = (i >> (8 - rgb_red_bits)) << rgb_red_shift; + x11_lut_green[i] = (i >> (8 - rgb_green_bits)) << rgb_green_shift; + x11_lut_blue[i] = (i >> (8 - rgb_blue_bits)) << rgb_blue_shift; + x11_lut_gray[i] = + x11_lut_red[i] | x11_lut_green[i] | x11_lut_blue[i]; + } +} + +void x11_init_color(ImageInfo *img) +{ + Colormap cmap; + XVisualInfo vis_template; + int found, vis_class; + unsigned int i; + Display *dpy; + + dpy = img->dpy; + cmap = DefaultColormap(dpy, DefaultScreen(dpy)); + if (0 == x11_grays) + x11_grays = 8; + +/* Ask for visual type +*/ + vis_template.screen = XDefaultScreen(dpy); + vis_template.visualid = + XVisualIDFromVisual(img->visual); + vis_info = + XGetVisualInfo(dpy, VisualIDMask | VisualScreenMask, &vis_template, + &found); + if (XShmQueryExtension(dpy)) + img->have_shmem = 1; + +#if defined(__cplusplus) || defined(c_plusplus) + vis_class = vis_info->c_class; +#else + vis_class = vis_info->class; +#endif + if(vis_class == TrueColor) + { + img->gray = 0; /* XXX testing... */ + img->display_depth = 4; + img->display_type = TRUECOLOR; + + x11_create_lut(vis_info->red_mask, vis_info->green_mask, + vis_info->blue_mask); + x11_black = x11_alloc_color(dpy, cmap, 0, 0, 0); + x11_gray = x11_alloc_color(dpy, cmap, 0xc000, 0xc000, 0xc000); + x11_lightgray = x11_alloc_color(dpy, cmap, 0xe000, 0xe000, 0xe000); + x11_white = x11_alloc_color(dpy, cmap, 0xffff, 0xffff, 0xffff); + } + else + if(vis_class == PseudoColor && vis_info->depth == 8) + { + img->display_depth = 1; + img->display_type = PSEUDOCOLOR; + if (0 != x11_alloc_grays(dpy, cmap, x11_map_gray, x11_grays)) + { + fprintf(stderr, "%s:%d:Sorry, can't allocate %d grays\n", + __FILE__,__LINE__,x11_grays); + Viewer_postlude(); + exit(1); + } + if (!img->gray) + { + for (i = 0; i < sizeof(try_red) / sizeof(int); i++) + { + if (0 == x11_alloc_colorcube + (dpy, cmap, x11_map_color, + try_red[i], try_green[i], try_blue[i])) + { + x11_colors = try_red[i] * try_green[i] * try_blue[i]; + + init_dither(try_red[i], try_green[i], try_blue[i], x11_grays); + + break; + } + } + if (i == sizeof(try_red) / sizeof(int)) + { + img->gray = 1; + fputs("failed to allocate colors, using grayscaled\n", stderr); + } + } + if (img->gray) + init_dither(2, 2, 2, x11_grays); + } + else + if(vis_class == StaticGray || vis_class == GrayScale) + { + img->display_depth = 1; + img->display_type = PSEUDOCOLOR; + x11_grays = 64; + img->gray = 1; + + init_dither(2, 2, 2, x11_grays); + + if (0 != x11_alloc_grays(dpy, cmap, x11_map_gray, x11_grays)) + { + fprintf(stderr, "%s:%d: Sorry, can't allocate %d grays\n", + __FILE__,__LINE__, x11_grays); + Viewer_postlude(); + exit(1); + } + } + else + { + fprintf(stderr, "%s:%d: Sorry, can't handle visual\n", __FILE__,__LINE__); + Viewer_postlude(); + exit(1); + } +/* some common colors +*/ + x11_red = x11_alloc_color(dpy, cmap, 65535, 0, 0); + x11_green = x11_alloc_color(dpy, cmap, 0, 65535, 0); + x11_blue = x11_alloc_color(dpy, cmap, 0, 0, 65535); + + if (img->gray) + { + x11_map = x11_map_gray; + + dither_line = dither_line_gray; + } + else + { + x11_map = x11_map_color; + + dither_line = dither_line_color; + } +} + +static int mitshm_bang = 0; + +static int x11_error_dev_null(Display * dpy, XErrorEvent * event) +{ + mitshm_bang = 1; + return 0; +} + +XImage *x11_create_ximage(ImageInfo *img) +{ + XImage *ximage = NULL; + unsigned char *data; + XShmSegmentInfo *shminfo = NULL; + int (*old_handler)(Display * dpy, XErrorEvent * event); + + if (img->have_shmem) + { + old_handler = XSetErrorHandler(x11_error_dev_null); + img->shm = shminfo = (XShmSegmentInfo*)malloc(sizeof(XShmSegmentInfo)); + memset(shminfo, 0, sizeof(XShmSegmentInfo)); + ximage = XShmCreateImage(img->dpy, + img->visual, + img->depth, + ZPixmap, NULL, + shminfo, img->img_width, img->img_height); + if (ximage) + { + shminfo->shmid = shmget(IPC_PRIVATE, + ximage->bytes_per_line * ximage->height, + IPC_CREAT | 0777); + + if (-1 == shminfo->shmid) + { + fprintf(stderr,"shmget(%dMB): %s\n", + ximage->bytes_per_line * ximage->height / 1024 / 1024, + strerror(errno)); + goto oom; + } + shminfo->shmaddr = (char *) shmat(shminfo->shmid, 0, 0); + + if ((void *) -1 == shminfo->shmaddr) + { + perror("shmat"); + goto oom; + } + ximage->data = shminfo->shmaddr; + shminfo->readOnly = False; + + XShmAttach(img->dpy, shminfo); + XSync(img->dpy, False); + shmctl(shminfo->shmid, IPC_RMID, 0); + + if (mitshm_bang) + { + img->have_shmem = 0; + shmdt(shminfo->shmaddr); + free(shminfo); + img->shm = shminfo = NULL; + XDestroyImage(ximage); + ximage = NULL; + } + } + else + { + img->have_shmem = 0; + free(shminfo); + img->shm = shminfo = NULL; + } + XSetErrorHandler(old_handler); + } + + if (ximage == NULL) + { + img->shm = NULL; + if (NULL == (data = (unsigned char*) + malloc(img->img_width * img->img_height * img->display_depth))) + { + fprintf(stderr,"Oops: out of memory\n"); + goto oom; + } + ximage = XCreateImage(img->dpy, + img->visual, + img->depth, + ZPixmap, 0, (char*)data, + img->img_width, img->img_height, + 8, 0); + } + memset(ximage->data, 0, ximage->bytes_per_line * ximage->height); + + return ximage; + + oom: + if (shminfo) + { + if (shminfo->shmid && shminfo->shmid != -1) + shmctl(shminfo->shmid, IPC_RMID, 0); + free(shminfo); + } + if (ximage) + XDestroyImage(ximage); + img->shm = 0; + return NULL; +} + +void x11_destroy_ximage(ImageInfo *img) +{ + XShmSegmentInfo *shminfo = (XShmSegmentInfo*)img->shm; + + if (shminfo) + { + XShmDetach(img->dpy, shminfo); + XDestroyImage(img->ximage); + shmdt(shminfo->shmaddr); + free(shminfo); + } + else + XDestroyImage(img->ximage); +} +/* + * ordered dither routines + * + * stolen from The GIMP and trimmed for speed + */ +#define DITHER_LEVEL 8 + +static long red_mult, green_mult; +static long red_dither[256]; +static long green_dither[256]; +static long blue_dither[256]; +static long gray_dither[256]; + +typedef unsigned long vector[DITHER_LEVEL]; +typedef vector matrix[DITHER_LEVEL]; + +#if DITHER_LEVEL == 8 +#define DITHER_MASK 7 +static matrix origDM = +{ + {0, 32, 8, 40, 2, 34, 10, 42}, + {48, 16, 56, 24, 50, 18, 58, 26}, + {12, 44, 4, 36, 14, 46, 6, 38}, + {60, 28, 52, 20, 62, 30, 54, 22}, + {3, 35, 11, 43, 1, 33, 9, 41}, + {51, 19, 59, 27, 49, 17, 57, 25}, + {15, 47, 7, 39, 13, 45, 5, 37}, + {63, 31, 55, 23, 61, 29, 53, 21} +}; +static matrix DM; + +#endif /* DITHER_LEVEL == 8 */ + +#if DITHER_LEVEL == 4 +#define DITHER_MASK 3 +static matrix origDM = +{ + {0, 8, 2, 10}, + {12, 4, 14, 6}, + {3, 11, 1, 9}, + {15, 7, 13, 5} +}; +static matrix DM; + +#endif + +static void init_dither(int shades_r, int shades_g, int shades_b, + int shades_gray) +{ + int i, j; + unsigned char low_shade, high_shade; + unsigned short index; + float red_colors_per_shade; + float green_colors_per_shade; + float blue_colors_per_shade; + float gray_colors_per_shade; + + red_mult = shades_g * shades_b; + green_mult = shades_b; + + red_colors_per_shade = 256.0 / (shades_r - 1); + green_colors_per_shade = 256.0 / (shades_g - 1); + blue_colors_per_shade = 256.0 / (shades_b - 1); + gray_colors_per_shade = 256.0 / (shades_gray - 1); + +/* this avoids a shift when checking these values +*/ + memcpy(DM, origDM, sizeof(unsigned long)*DITHER_LEVEL*DITHER_LEVEL); + for (i = 0; i < DITHER_LEVEL; i++) + for (j = 0; j < DITHER_LEVEL; j++) + DM[i][j] *= 0x10000; + +/* setup arrays containing three bytes of information for red, green, & blue + * the arrays contain : + * 1st byte: low end shade value + * 2nd byte: high end shade value + * 3rd & 4th bytes: ordered dither matrix index +*/ + + for (i = 0; i < 256; i++) + { +/* setup the red information +*/ + low_shade = (unsigned char) (i / red_colors_per_shade); + high_shade = low_shade + 1; + + index = (unsigned short) + (((i - low_shade * red_colors_per_shade) / red_colors_per_shade) * + (DITHER_LEVEL * DITHER_LEVEL + 1)); + + low_shade *= red_mult; + high_shade *= red_mult; + + red_dither[i] = (index << 16) + (high_shade << 8) + (low_shade); + +/* setup the green information +*/ + low_shade = (unsigned char) (i / green_colors_per_shade); + high_shade = low_shade + 1; + + index = (unsigned short) + (((i - low_shade * green_colors_per_shade) / green_colors_per_shade) * + (DITHER_LEVEL * DITHER_LEVEL + 1)); + + low_shade *= green_mult; + high_shade *= green_mult; + + green_dither[i] = (index << 16) + (high_shade << 8) + (low_shade); + +/* setup the blue information +*/ + low_shade = (unsigned char) (i / blue_colors_per_shade); + high_shade = low_shade + 1; + + index = (unsigned short) + (((i - low_shade * blue_colors_per_shade) / blue_colors_per_shade) * + (DITHER_LEVEL * DITHER_LEVEL + 1)); + + blue_dither[i] = (index << 16) + (high_shade << 8) + (low_shade); + +/* setup the gray information +*/ + low_shade = (unsigned char) (i / gray_colors_per_shade); + high_shade = low_shade + 1; + + index = (unsigned short) + (((i - low_shade * gray_colors_per_shade) / gray_colors_per_shade) * + (DITHER_LEVEL * DITHER_LEVEL + 1)); + + gray_dither[i] = (index << 16) + (high_shade << 8) + (low_shade); + } +} + +static void dither_line_color(unsigned char *src, unsigned char *dest, + unsigned int y, unsigned int width) +{ + unsigned long a, b, dval, *ymod; + unsigned int x; + ymod = DM[y & DITHER_MASK]; + + for(x = 0; x < width; x++) + { + dval = ymod[x & DITHER_MASK]; + + b = red_dither[src[0]]; + if (dval < b) + b >>= 8; + + a = green_dither[src[1]]; + if (dval < a) + a >>= 8; + b += a; + + a = blue_dither[src[2]]; + if (dval < a) + a >>= 8; + b += a; + src += RGB_SIZE; + *dest++ = (unsigned char)(b & 0xff); + } +} + +static void dither_line_gray(unsigned char *src, unsigned char *dest, + unsigned int y, unsigned int width) +{ + unsigned long a, g, *ymod; + unsigned int x; + + ymod = DM[y & DITHER_MASK]; + + for(x = 0; x < width; x++) + { + g = (src[0]*3 + src[1]*6 + src[2]) / 10; + a = gray_dither[g]; + src += RGB_SIZE; + + if (ymod[x & DITHER_MASK] < a) + a >>= 8; + + *dest++ = a & 0xff; + } +} + +void viewer_renderline(ImageInfo *img, unsigned char *scanline, + unsigned int row, unsigned int x, unsigned int width) +{ + unsigned char *src, *dst; + unsigned long pix; + XImage *ximage; + unsigned int col, max_col; + unsigned short mng_rgb_size; + + mng_rgb_size = img->mng_rgb_size; + ximage = img->ximage; + src = scanline; + col = x; + max_col = x + width; + + if (img->display_type == PSEUDOCOLOR) + { + dst = img->dither_line; + dither_line(src, dst, row, width); + + while(col < max_col) + { + XPutPixel(ximage, col, row, x11_map[dst[0]]); + ++col; + ++dst; + } + return; + } +/* display_type == TRUECOLOR +*/ + while(col < max_col) + { + pix = x11_lut_red[src[0]] | x11_lut_green[src[1]] | x11_lut_blue[src[2]]; + + XPutPixel(ximage, col, row, pix); + + ++col; + src += RGB_SIZE; + } +} diff --git a/Engine/lib/lmng/contrib/gcc/xmngview/xmng.h b/Engine/lib/lmng/contrib/gcc/xmngview/xmng.h new file mode 100644 index 000000000..3012b4e9f --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/xmngview/xmng.h @@ -0,0 +1,103 @@ +#ifndef _XMNG_H_ +#define _XMNG_H +#define RGB_SIZE 3 +#define CANVAS_RGB8_SIZE 3 +#define CANVAS_RGBA8_SIZE 4 +#define CANVAS_ARGB8_SIZE 4 +#define CANVAS_RGB8_A8_SIZE 4 +#define CANVAS_BGR8_SIZE 3 +#define CANVAS_BGRA8_SIZE 4 +#define CANVAS_BGRA8PM_SIZE 4 +#define CANVAS_ABGR8_SIZE 4 + +#define MNG_MAGIC "\x8aMNG\x0d\x0a\x1a\x0a" +#define JNG_MAGIC "\x8bJNG\x0d\x0a\x1a\x0a" +#define PNG_MAGIC "\x89PNG\x0d\x0a\x1a\x0a" +#define PSEUDOCOLOR 1 +#define TRUECOLOR 2 + +#define MNG_TYPE 1 +#define JNG_TYPE 2 +#define PNG_TYPE 3 + +#define SPACE_X 10 +#define SPACE_Y 10 +#define BUT_ENTRY_BORDER 0 +#define FRAME_SHADOW_WIDTH 2 +#define ANY_WIDTH 4 + +#define OK MNG_NOERROR +#define MAX_COLORBUF 64 + +typedef struct +{ + unsigned int frozen:1; + unsigned int restarted:1; + unsigned int stopped:1; + unsigned int single_step_wanted:1; + unsigned int single_step_served:1; + unsigned int has_bg_color:1; + unsigned int has_bg_pixel:1; + unsigned int x11_init:1; + unsigned int timer_active:1; + + mng_handle user_handle; + Widget canvas; + int type; + XtIntervalId timeout_ID; + mng_uint32 counter; + mng_uint32 delay; + mng_uint32 img_width, img_height; + mng_uint32 read_len; + mng_uint32 read_pos; + unsigned char *read_buf; + unsigned char *mng_buf; + unsigned char *dither_line; + + Window external_win; + Window frame_win; + Window control_win; + GC gc; + Display *dpy; + Window win; + unsigned short mng_rgb_size; + unsigned short mng_bytes_per_line; + XImage *ximage; + int src_x, src_y; + int dst_x, dst_y; + unsigned int frame_w, frame_h; + + void *shm; + int gray; + int display_depth, display_type; + int have_shmem; + Pixel bg_pixel; + unsigned short xbg_red, xbg_green, xbg_blue; + unsigned char bg_red, bg_green, bg_blue; + Visual *visual; + unsigned int depth; +/* do not free */ + struct timeval timer_start; + struct timeval timer_end; + + char *read_idf; + FILE *reader; + int *argc_ptr; + char **argv; + char bg_color[MAX_COLORBUF]; +} ImageInfo; + +#define XPUTIMAGE(dpy,dr,gc,xi,a,b,c,d,w,h) \ + if (have_shmem) \ + XShmPutImage(dpy,dr,gc,xi,a,b,c,d,w,h,True); \ + else \ + XPutImage(dpy,dr,gc,xi,a,b,c,d,w,h) + +extern void Viewer_postlude(void); +extern XImage *x11_create_ximage(ImageInfo *data); +extern void x11_destroy_ximage(ImageInfo *data); +extern void x11_init_color(ImageInfo *data); +extern void viewer_renderline(ImageInfo *data, unsigned char *scanline, + unsigned int row, unsigned int x, unsigned int width); + +#endif diff --git a/Engine/lib/lmng/contrib/gcc/xmngview/xmngview.c b/Engine/lib/lmng/contrib/gcc/xmngview/xmngview.c new file mode 100644 index 000000000..3198c7b95 --- /dev/null +++ b/Engine/lib/lmng/contrib/gcc/xmngview/xmngview.c @@ -0,0 +1,1185 @@ +/* Built with libmng-1.0.9 + * Compiled on linux with gcc-3.3.4 + * james@blastwave.org suggested the single step mode and wrote: + * "xmngview works on Solaris both Sparc and Intel and compiles with Sun's cc" + * + * + * This program my be redistributed under the terms of the + * GNU General Public Licence, version 2, or at your preference, + * any later version. + * + * For more information about libmng please visit: + * + * The official libmng web-site: + * http://www.libmng.com + * + * Libmng on SourceForge: + * http://libmng.sourceforge.net + * + * The official MNG homepage: + * http://www.libpng.org/pub/mng + * + * The official PNG homepage: + * http://www.libpng.org/pub/png +*/ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xmng.h" + +#define DEFAULT_BACKGROUND "grey77" +static char version[]={"0.6"}; + +static void run_viewer(FILE *reader, char *read_idf); + +static mng_handle user_handle; +static ImageInfo img; +static struct timeval start_tv, now_tv; +static XtIntervalId timeout_ID; +static char *prg_idf; + +static XtAppContext app_context; +static Widget toplevel, main_form, canvas, file_label; +static XmFontList file_font; +static Dimension start_width; + +#define SLASH '/' +/* + * Cnf: XQueryColor(3X11) +*/ +static char *parse_rgb_color(char *val) +{ + char *s, *d; + int ch; + char status, rgb_type; + char r[6], g[6], b[6], rgb[24]; + + rgb_type = 0; + status = 1; + s = val; + memset(r, 0, 6); + memset(g, 0, 6); + memset(b, 0, 6); + + if(strncasecmp(s, "rgb:", 4) == 0) + { + rgb_type = 1; + s += 4; + if((d = strchr(s, SLASH))) + { + *d = 0; + + if(d - s > 4) + s[4] = 0; + strcpy(r, s); + + s = ++d; + + if((d = strchr(s, SLASH))) + { + *d = 0; + if(d - s > 4) + s[4] = 0; + strcpy(g, s); + + s = d + 1; + while((ch = *++d) && isxdigit(ch)); + *d = 0; + if(d - s > 4) + s[4] = 0; + strcpy(b, s); + + } + if(*r == 0 || *g == 0 || *b == 0) + return NULL; + + s = r - 1; + while((ch = *++s)) + { + if(isxdigit(ch)) continue; + status = rgb_type = 0; + break; + } + s = g - 1; + while((ch = *++s)) + { + if(isxdigit(ch)) continue; + status = rgb_type = 0; + break; + } + s = b - 1; + while((ch = *++s)) + { + if(isxdigit(ch)) continue; + status = rgb_type = 0; + break; + } + if(status) + { + strcpy(rgb, "rgb:"); + d = rgb + 4; + s = r; + while(*s) *d++ = *s++; + *d++ = SLASH; + s = g; + while(*s) *d++ = *s++; + *d++ = SLASH; + s = b; + while(*s) *d++ = *s++; + *d = 0; + + return strdup(rgb); + } + } /* if((slash = strchr(s, SLASH))) */ + return NULL; + } + + s = val; + if(*s == '#' || isdigit(*s)) + { + if(*s != '#') + --s; + while((ch = *++s)) + { + if(isxdigit(ch)) continue; + status = 0; + break; + } + if(status) + { + d = rgb; + s = val; + if(*s == '#') + ++s; +/* + * #RGB (4 bits each) + * #RRGGBB (8 bits each) + * #RRRGGGBBB (12 bits each) + * #RRRRGGGGBBBB (16 bits each) +*/ + if(strlen(s) > 12) + s[12] = 0; + *d++ = '#'; + strcpy(d, s); + return strdup(rgb); + } + return NULL; + } + +/* + * 'white', 'LavenderBlush', 'dark slate gray', 'grey12' +*/ + s = val - 1; + while((ch = *++s)) + { + if(isalnum(ch) || isspace(ch)) continue; + status = 0; + break; + } + if(!status) + return NULL; + return strdup(val); + +}/* parse_rgb_color() */ + +static void set_bg_pixel(ImageInfo *img) +{ + XColor xcolor; + Widget w; + char *s, *d; + int found; + + w = img->canvas; + + if(!img->has_bg_pixel) + { + if(img->has_bg_color) + { + s = strdup(img->bg_color); + + d = parse_rgb_color(s); + + free(s); + + if(d) + { + strcpy(img->bg_color, d); + free(d); + } + else + img->has_bg_color = 0; + } + if(!img->has_bg_color) + { + strcpy(img->bg_color, DEFAULT_BACKGROUND); + img->has_bg_color = 1; + } + + found = XParseColor(img->dpy, + DefaultColormap(img->dpy, DefaultScreen(img->dpy)), + img->bg_color, &xcolor); + + if(!found) + { + strcpy(img->bg_color, DEFAULT_BACKGROUND); + + found = XParseColor(img->dpy, + DefaultColormap(img->dpy, DefaultScreen(img->dpy)), + img->bg_color, &xcolor); + + } + xcolor.flags = DoRed | DoGreen | DoBlue; + + XAllocColor(img->dpy, + DefaultColormap(img->dpy, DefaultScreen(img->dpy)), + &xcolor); + } + else + { + xcolor.pixel = img->bg_pixel; + xcolor.flags = DoRed|DoGreen|DoBlue; + + found = XQueryColor(img->dpy, + DefaultColormap(img->dpy, DefaultScreen(img->dpy)), + &xcolor); + } + img->bg_pixel = xcolor.pixel; + img->xbg_red = xcolor.red; + img->xbg_green = xcolor.green; + img->xbg_blue = xcolor.blue; + img->bg_red = (unsigned char)xcolor.red&0xff; + img->bg_green = (unsigned char)xcolor.green&0xff; + img->bg_blue = (unsigned char)xcolor.blue&0xff; + img->has_bg_pixel = 1; + +}/* set_bg_pixel() */ + +static void fsb_cancel_cb(Widget w, XtPointer client, XtPointer call) +{ + XtUnmanageChild(w); +} + +void create_file_dialog(Widget w, char *button_text, char *title_text, + void(*fsb_select_cb)(Widget,XtPointer,XtPointer)) +{ + Arg args[4]; + int cnt; + Widget dialog; + XmString button_str, title_str, filter; + Widget child; + + cnt = 0; + dialog = XmCreateFileSelectionDialog(w, "Files", args, cnt); + + XtUnmanageChild(XmFileSelectionBoxGetChild(dialog,XmDIALOG_HELP_BUTTON)); + XtAddCallback(dialog, XmNcancelCallback, fsb_cancel_cb, NULL); + XtAddCallback(dialog, XmNokCallback, fsb_select_cb, NULL); + button_str = XmStringCreateLocalized(button_text); + title_str = XmStringCreateLocalized(title_text); + filter = XmStringCreateLocalized("*.[jmp]ng"); + XtVaSetValues(dialog, + XmNokLabelString, button_str, + XmNdialogTitle, title_str, + XmNpattern, filter, + XmNfileFilterStyle, XmFILTER_NONE, + NULL); + XmStringFree(button_str); + XmStringFree(title_str); + XmStringFree(filter); + child = XmFileSelectionBoxGetChild(dialog, XmDIALOG_FILTER_TEXT); + XtVaSetValues(child, XmNfontList, file_font, NULL); + child = XmFileSelectionBoxGetChild(dialog, XmDIALOG_DIR_LIST); + XtVaSetValues(child, XmNfontList, file_font, NULL); + child = XmFileSelectionBoxGetChild(dialog, XmDIALOG_LIST); + XtVaSetValues(child, XmNfontList, file_font, NULL); + child = XmFileSelectionBoxGetChild(dialog, XmDIALOG_TEXT); + XtVaSetValues(child, XmNfontList, file_font, NULL); + + XtManageChild(dialog); + XMapRaised(XtDisplay (dialog), XtWindow (XtParent (dialog))); +} + +void run_mng_file_cb(Widget w, XtPointer client, XtPointer call) +{ + XmFileSelectionBoxCallbackStruct *fsb; + char *read_idf; + FILE *reader; + + XtUnmanageChild(w); + fsb = (XmFileSelectionBoxCallbackStruct *)call; + XmStringGetLtoR(fsb->value, XmSTRING_DEFAULT_CHARSET, &read_idf); + + if(read_idf == NULL || *read_idf == 0) return; + + reader = fopen(read_idf, "r"); + if(reader == NULL) + { + perror(read_idf); + fprintf(stderr, "\n\n%s: cannot open file '%s'\n\n", prg_idf, read_idf); + return; + } + run_viewer(reader, read_idf); + + free(read_idf); +} + +static void user_reset_data(void) +{ + if(timeout_ID) XtRemoveTimeOut(timeout_ID); + timeout_ID = 0; + mng_cleanup(&img.user_handle); + + img.read_pos = 0; + free(img.read_buf); + img.read_buf = NULL; + img.read_len = 0; + img.img_width = 0; + img.img_height = 0; + img.mng_bytes_per_line = 0; + img.read_idf = NULL; + img.frozen = 0; + img.restarted = 0; + img.single_step_wanted = 0; + img.single_step_served = 0; + + XClearWindow(img.dpy, img.win); +} + +void browse_file_cb(Widget w, XtPointer client, XtPointer call) +{ + if(img.user_handle) + user_reset_data(); + + img.stopped = 0; + img.frozen = 0; + img.restarted = 0; + create_file_dialog(w, "Select", "Select MNG file", run_mng_file_cb); +} + +void Viewer_postlude(void) +{ + if(timeout_ID) XtRemoveTimeOut(timeout_ID); + mng_cleanup(&img.user_handle); + if(img.reader) fclose(img.reader); + if(img.ximage) XDestroyImage(img.ximage); + if(img.read_buf) free(img.read_buf); + if(img.mng_buf) free(img.mng_buf); + if(img.dither_line) free(img.dither_line); + if(!img.external_win && img.dpy) XtCloseDisplay(img.dpy); + fputc('\n', stderr); +} + +static void user_init_data(ImageInfo *img) +{ + unsigned int depth; + int screen; + Display *dpy; + + dpy = img->dpy; + screen = DefaultScreen(dpy); + depth = DefaultDepth(dpy, screen); + img->depth = depth; + + if(!img->visual) + { + img->visual = DefaultVisual(dpy, screen); + img->gc = DefaultGC(dpy, DefaultScreen(dpy)); + } + else + { + if(img->mng_buf) free(img->mng_buf); + if(img->dither_line) free(img->dither_line); + + x11_destroy_ximage(img); + } + + set_bg_pixel(img); + + mng_set_bgcolor(img->user_handle, + img->xbg_red, img->xbg_green, img->xbg_blue); + + img->mng_bytes_per_line = img->img_width * img->mng_rgb_size; + img->mng_buf = (unsigned char*) + calloc(1, img->mng_bytes_per_line * img->img_height); + img->dither_line = (unsigned char*) + calloc(1, img->mng_bytes_per_line); + + if(!img->x11_init) + { + x11_init_color(img); + + img->x11_init = 1; + } + img->ximage = x11_create_ximage(img); + + if(img->ximage == NULL) + { + Viewer_postlude(); + exit(0); + } +} + +static void player_exit_cb(Widget w, XtPointer client, XtPointer call) +{ + Viewer_postlude(); + exit(0); +} + +static void player_stop_cb(Widget w, XtPointer client, XtPointer call) +{ + if(img.type != MNG_TYPE) return; + if(!img.user_handle) return; + if(img.stopped) return; + + user_reset_data(); + img.stopped = 1; +} + +static void player_single_step_cb(Widget w, XtPointer client, XtPointer call) +{ + if(img.type != MNG_TYPE) return; + if(!img.user_handle) return; + if(img.stopped) return; + + if(img.single_step_served) + { + img.single_step_served = 0; + img.frozen = 0; + + img.single_step_wanted = 1; + return; + } + if(timeout_ID) XtRemoveTimeOut(timeout_ID); + timeout_ID = 0; + img.single_step_wanted = 1; + mng_display_resume(img.user_handle); +} + +static void player_pause_cb(Widget w, XtPointer client, XtPointer call) +{ + if(img.type != MNG_TYPE) return; + if(!img.user_handle) return; + if(img.stopped) return; + if(img.frozen) return; + + if(timeout_ID) XtRemoveTimeOut(timeout_ID); + timeout_ID = 0; + img.frozen = 1; + img.single_step_served = 0; + img.single_step_wanted = 0; +} + +static void player_resume_cb(Widget w, XtPointer client, XtPointer call) +{ + if(img.type != MNG_TYPE) return; + if(!img.user_handle) return; + if(img.stopped) return; + + if(!img.frozen + && !img.single_step_served) + return; + img.frozen = 0; + + if(img.single_step_served + || img.single_step_wanted) + { + img.single_step_served = 0; + img.single_step_wanted = 0; + + if(timeout_ID) XtRemoveTimeOut(timeout_ID); + timeout_ID = 0; + } + mng_display_resume(img.user_handle); +} + +static void player_restart_cb(Widget w, XtPointer client, XtPointer call) +{ + if(img.type != MNG_TYPE) return; + if(!img.user_handle) return; + if(img.stopped) return; + + img.frozen = 1; + if(timeout_ID) XtRemoveTimeOut(timeout_ID); + timeout_ID = 0; + + img.frozen = 0; + img.single_step_served = 0; + img.single_step_wanted = 0; + + img.read_pos = 0; + mng_reset(img.user_handle); + img.restarted = 1; + gettimeofday(&start_tv, NULL); + mng_read(img.user_handle); + mng_display(img.user_handle); +} + +static void release_event_cb(Widget w, XtPointer client, XEvent *event, + Boolean *cont) +{ + Viewer_postlude(); + exit(0); +} + +static void redraw(int type) +{ + if((type == Expose || type == GraphicsExpose) + && img.ximage) + { + XPutImage(img.dpy, img.win, img.gc, img.ximage, + 0, 0, 0, 0, img.img_width, img.img_height); + } +} + +static void exposures_cb(Widget w, XtPointer client, + XmDrawingAreaCallbackStruct *cbs) +{ + + redraw(cbs->event->xany.type); +} + +static mng_ptr user_alloc(mng_size_t len) +{ + return calloc(1, len + 2); +} + +static void user_free(mng_ptr buf, mng_size_t len) +{ + free(buf); +} + +static mng_bool user_read(mng_handle user_handle, mng_ptr out_buf, + mng_uint32 req_len, mng_uint32 *out_len) +{ + mng_uint32 more; + ImageInfo *img; + + img = (ImageInfo *)mng_get_userdata(user_handle); + + more = img->read_len - img->read_pos; + + if(more > 0 + && img->read_buf != NULL) + { + if(req_len < more) + more = req_len; + memcpy(out_buf, img->read_buf + img->read_pos, more); + img->read_pos += more; + *out_len = more; + + return MNG_TRUE; + } + return MNG_FALSE; +} + +static mng_bool user_open_stream(mng_handle user_handle) +{ + return MNG_TRUE; +} + +static mng_bool user_close_stream(mng_handle user_handle) +{ + return MNG_TRUE; +} + +static void create_widgets(mng_uint32 width, mng_uint32 height) +{ + Widget but_rc, but_frame, canvas_frame; + Widget but1, but2, but3, but4, but5, but6, but7; + + toplevel = XtAppInitialize(&app_context, "xmngview", NULL, 0, + img.argc_ptr, img.argv, + 0, 0, 0); + + main_form = XtVaCreateManagedWidget("main_form", + xmFormWidgetClass, toplevel, + XmNhorizontalSpacing, SPACE_X, + XmNverticalSpacing, SPACE_Y, + XmNresizable, True, + NULL); + but_frame = XtVaCreateManagedWidget("but_frame", + xmFrameWidgetClass, main_form, + XmNshadowType, XmSHADOW_ETCHED_OUT, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XmNshadowThickness, FRAME_SHADOW_WIDTH, + NULL); + but_rc = XtVaCreateManagedWidget("but_rc", + xmRowColumnWidgetClass, but_frame, + XmNentryAlignment, XmALIGNMENT_CENTER, + XmNorientation, XmHORIZONTAL, + XmNpacking, XmPACK_COLUMN, + XmNnumColumns, 1, + XmNresizeWidth, True, + XmNentryBorder, BUT_ENTRY_BORDER, + NULL); + but1 = XtVaCreateManagedWidget("Exit", + xmPushButtonWidgetClass, but_rc, + NULL); + XtAddCallback(but1, XmNactivateCallback, + player_exit_cb, (XtPointer)toplevel); + + but2 = XtVaCreateManagedWidget("Pause", + xmPushButtonWidgetClass, but_rc, + NULL); + XtAddCallback(but2, XmNactivateCallback, + player_pause_cb, (XtPointer)toplevel); + + but3 = XtVaCreateManagedWidget("GoOn", + xmPushButtonWidgetClass, but_rc, + NULL); + XtAddCallback(but3, XmNactivateCallback, + player_resume_cb, NULL); + + but4 = XtVaCreateManagedWidget("Restart", + xmPushButtonWidgetClass, but_rc, + NULL); + XtAddCallback(but4, XmNactivateCallback, + player_restart_cb, NULL); + + but5 = XtVaCreateManagedWidget("Step", + xmPushButtonWidgetClass, but_rc, + NULL); + XtAddCallback(but5, XmNactivateCallback, + player_single_step_cb, NULL); + + but6 = XtVaCreateManagedWidget("Finish", + xmPushButtonWidgetClass, but_rc, + NULL); + XtAddCallback(but6, XmNactivateCallback, + player_stop_cb, NULL); + + but7 = XtVaCreateManagedWidget("Browse", + xmPushButtonWidgetClass, but_rc, + NULL); + XtAddCallback(but7, XmNactivateCallback, + browse_file_cb, NULL); + + file_label = XtVaCreateManagedWidget("FILE: ", + xmLabelWidgetClass, main_form, + XmNalignment, XmALIGNMENT_BEGINNING, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, but_frame, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + NULL); + + canvas_frame = XtVaCreateManagedWidget("canvas_frame", + xmFrameWidgetClass, main_form, + XmNshadowType, XmSHADOW_ETCHED_OUT, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, file_label, + XmNbottomAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + NULL); + + canvas = XtVaCreateManagedWidget("canvas", + xmDrawingAreaWidgetClass, canvas_frame, + XmNheight, height, + XmNwidth, width, + NULL); + + XtAddEventHandler(canvas, + ButtonReleaseMask|ButtonPressMask, + False, release_event_cb, (XtPointer)toplevel); + + XtAddCallback(canvas, + XmNexposeCallback, (XtCallbackProc)exposures_cb, (XtPointer)&img); + + XtRealizeWidget(toplevel); + + if(start_width == 0) + { + width = height = 0; + + start_width = (FRAME_SHADOW_WIDTH<<1); + XtVaGetValues(but1, XmNwidth, &width, NULL); + start_width += width + (BUT_ENTRY_BORDER<<1) + ANY_WIDTH; + XtVaGetValues(but2, XmNwidth, &width, NULL); + start_width += width + (BUT_ENTRY_BORDER<<1) + ANY_WIDTH; + XtVaGetValues(but3, XmNwidth, &width, NULL); + start_width += width + (BUT_ENTRY_BORDER<<1) + ANY_WIDTH; + XtVaGetValues(but4, XmNwidth, &width, NULL); + start_width += width + (BUT_ENTRY_BORDER<<1) + ANY_WIDTH; + XtVaGetValues(but5, XmNwidth, &width, NULL); + start_width += width + (BUT_ENTRY_BORDER<<1) + ANY_WIDTH; + XtVaGetValues(but6, XmNwidth, &width, NULL); + start_width += width + (BUT_ENTRY_BORDER<<1); + XtVaGetValues(but7, XmNwidth, &width, NULL); + start_width += width + (BUT_ENTRY_BORDER<<1); + } + img.canvas = canvas; + img.dpy = XtDisplay(img.canvas); + img.win = XtWindow(img.canvas); + file_font = XmFontListAppendEntry(NULL, + XmFontListEntryCreate(XmFONTLIST_DEFAULT_TAG, + XmFONT_IS_FONT, + XLoadQueryFont(img.dpy, + "-*-helvetica-medium-r-*-*-12-*-*-*-*-*-iso8859-1"))); +} + +static mng_bool user_process_header(mng_handle user_handle, + mng_uint32 width, mng_uint32 height) +{ + ImageInfo *img; + Dimension cw, ch, tw, th, dh, dw, fw, fh; + XmString xmstr; + char *s, buf[128]; + + img = (ImageInfo*)mng_get_userdata(user_handle); + + if(img->restarted) + { + img->restarted = 0; + return MNG_TRUE; + } + img->img_width = width; + img->img_height = height; + + if(!img->external_win) + { + if(!img->canvas) + create_widgets(width, height); + else + { + tw = th = fw = fh = cw = ch = 0; + + XtVaGetValues(toplevel, XmNwidth, &tw, XmNheight, &th, NULL); + XtVaGetValues(main_form, XmNwidth, &fw, XmNheight, &fh, NULL); + XtVaGetValues(img->canvas, XmNwidth, &cw, XmNheight, &ch, NULL); + + if(height > ch) + { + dh = height - ch; + th += dh; + fh += dh; + } else + if(ch > height) + { + dh = ch - height; + th -= dh; + fh -= dh; + } + if(width > cw) + { + dw = width - cw; + tw += dw; + fw += dw; + } else + if(cw > width) + { + if(width > start_width) + dw = cw - width; + else + dw = cw - start_width; + tw -= dw; + fw -= dw; + } + if(fw < start_width) + { + tw = start_width + (SPACE_X<<1); + fw = start_width; + } + XtVaSetValues(toplevel, XmNwidth,tw , XmNheight,th , NULL); + XtVaSetValues(main_form, XmNwidth,fw , XmNheight,fh , NULL); + XtVaSetValues(img->canvas, XmNwidth,width , XmNheight,height , NULL); + } + } + else + if(img->external_win) + { + Display *dpy; + + XtToolkitInitialize(); + app_context = XtCreateApplicationContext(); + dpy = XtOpenDisplay(app_context, NULL,NULL,"xmngview", + NULL, 0, img->argc_ptr, img->argv); + img->dpy = dpy; + img->win = img->external_win; + + XSelectInput(dpy, img->win, ExposureMask); + } + user_init_data(img); + + if(img->canvas) + { + s = strrchr(img->read_idf, '/'); + if(s == NULL) s = img->read_idf; else ++s; + s = strdup(s); + if(strlen(s) > 64) s[64] = 0; + sprintf(buf, "%s (%d x %d)", s, img->img_width, img->img_height); + xmstr = XmStringCreateLtoR((char*)buf, XmSTRING_DEFAULT_CHARSET); + XtVaSetValues(file_label, XmNlabelString, xmstr, NULL); + XmStringFree(xmstr); + free(s); + } + gettimeofday(&start_tv, NULL); + return MNG_TRUE; +} + +static void wait_cb(XtPointer client, XtIntervalId * id) +{ + timeout_ID = 0; + + if(img.frozen + || img.single_step_served) + { +// gettimeofday(&start_tv, NULL); + + timeout_ID = XtAppAddTimeOut(app_context, + img.delay, wait_cb, NULL); + } + else + { + mng_display_resume(img.user_handle); + } +} + +static mng_bool user_set_timer(mng_handle user_handle, mng_uint32 delay) +{ + ImageInfo *img; + + img = (ImageInfo*)mng_get_userdata(user_handle); + img->delay = delay; + + timeout_ID = XtAppAddTimeOut(app_context, + delay, wait_cb, NULL); + + return MNG_TRUE; +} + +static mng_uint32 user_get_tick_count(mng_handle user_handle) +{ + double sec, usec; + mng_uint32 ticks; + + gettimeofday(&now_tv, NULL); + + sec = (double)(now_tv.tv_sec - start_tv.tv_sec); + usec = (double)now_tv.tv_usec - (double)start_tv.tv_usec; + ticks = (mng_uint32)(sec * 1000.0 + usec/1000.0); +//fprintf(stderr,"TICKS %u (%f:%f)\n", ticks, sec, usec); + return ticks; +} + +static mng_ptr user_get_canvas_line(mng_handle user_handle, mng_uint32 line) +{ + ImageInfo *img; + + img = (ImageInfo*)mng_get_userdata(user_handle); + + return img->mng_buf + img->mng_bytes_per_line * line; +} + +static mng_bool user_refresh(mng_handle user_handle, mng_uint32 x, + mng_uint32 y, mng_uint32 width, mng_uint32 height) +{ + ImageInfo *img; + mng_uint32 src_len; + unsigned char *src_start, *src_buf; + int row, max_row; + Display *dpy; + GC gc; + Window win; + XImage *ximage; + Visual *visual; + int have_shmem; + + img = (ImageInfo*)mng_get_userdata(user_handle); + + if(img->single_step_wanted) + img->single_step_served = 1; + + win = img->win; + gc = img->gc; + dpy = img->dpy; + ximage = img->ximage; + visual = img->visual; + have_shmem = img->have_shmem; + + max_row = y + height; + row = y; + src_len = img->mng_bytes_per_line; + src_buf = src_start = img->mng_buf + img->mng_rgb_size * x + y * src_len; + + while(row < max_row) + { + viewer_renderline(img, src_start, row, x, width); + + ++row; + src_start += src_len; + } + XPUTIMAGE(dpy, win, gc, ximage, x, y, x, y, width, height); + XSync(dpy, False); + return MNG_TRUE; +} + +static mng_bool user_error(mng_handle user_handle, mng_int32 code, + mng_int8 severity, + mng_chunkid chunktype, mng_uint32 chunkseq, + mng_int32 extra1, mng_int32 extra2, mng_pchar text) +{ + ImageInfo *img; + unsigned char chunk[5]; + + img = (ImageInfo*)mng_get_userdata(user_handle); + + chunk[0] = (char)((chunktype >> 24) & 0xFF); + chunk[1] = (char)((chunktype >> 16) & 0xFF); + chunk[2] = (char)((chunktype >> 8) & 0xFF); + chunk[3] = (char)((chunktype ) & 0xFF); + chunk[4] = '\0'; + + fprintf(stderr, "\n\n%s: error playing(%s) chunk[%d]'%s':\n", + prg_idf, img->read_idf, chunkseq, chunk); + fprintf(stderr, "code(%d) severity(%d) extra1(%d) extra2(%d)" + "\ntext:'%s'\n\n", code, severity, extra1, extra2, text); + return 0; +} + +static mng_bool prelude(void) +{ +#define MAXBUF 8 + unsigned char buf[MAXBUF]; + + if(fread(buf, 1, MAXBUF, img.reader) != MAXBUF) + { + fprintf(stderr,"\n%s:prelude\n\tcannot read signature \n", + prg_idf); + return MNG_FALSE; + } + + if(memcmp(buf, MNG_MAGIC, 8) == 0) + img.type = MNG_TYPE; + else + if(memcmp(buf, JNG_MAGIC, 8) == 0) + img.type = JNG_TYPE; + else + if(memcmp(buf, PNG_MAGIC, 8) == 0) + img.type = PNG_TYPE; + if(!img.type) + { + fprintf(stderr,"\n%s:'%s' is no MNG / JNG / PNG file\n", + prg_idf, img.read_idf); + return MNG_FALSE; + } + fseek(img.reader, 0, SEEK_SET); + fseek(img.reader, 0, SEEK_END); + img.read_len = ftell(img.reader); + fseek(img.reader, 0, SEEK_SET); + + if(!img.user_handle) + { + user_handle = mng_initialize(&img, user_alloc, user_free, MNG_NULL); + + if(user_handle == MNG_NULL) + { + fprintf(stderr, "\n%s: cannot initialize libmng.\n", prg_idf); + return MNG_FALSE; + } + img.user_handle = user_handle; + + mng_set_canvasstyle(user_handle, MNG_CANVAS_RGB8); + img.mng_rgb_size = CANVAS_RGB8_SIZE; + + if(mng_setcb_openstream(user_handle, user_open_stream) != OK + || mng_setcb_closestream(user_handle, user_close_stream) != OK + || mng_setcb_readdata(user_handle, user_read) != OK + || mng_setcb_settimer(user_handle, user_set_timer) != OK + || mng_setcb_gettickcount(user_handle, user_get_tick_count) != OK + || mng_setcb_processheader(user_handle, user_process_header) != OK + || mng_setcb_getcanvasline(user_handle, user_get_canvas_line) != OK + || mng_setcb_refresh(user_handle, user_refresh) != OK + || mng_setcb_errorproc(user_handle, user_error) != OK + ) + { + fprintf(stderr,"\n%s: cannot set callbacks for libmng.\n", + prg_idf); + return MNG_FALSE; + } + } + img.read_buf = (unsigned char*)calloc(1, img.read_len + 2); + fread(img.read_buf, 1, img.read_len, img.reader); + fclose(img.reader); + img.reader = NULL; + + return MNG_TRUE; +} + +static void run_viewer(FILE *reader, char *read_idf) +{ + XEvent event; + + img.read_idf = read_idf; + img.reader = reader; + + if(read_idf != NULL) + { + if(prelude() == MNG_FALSE) + return ; + + gettimeofday(&start_tv, NULL); + + mng_read(img.user_handle); + mng_display(img.user_handle); + } + + if(!img.external_win) + { + XtAppMainLoop(app_context); + } + else + while(1) + { + XtAppNextEvent(app_context, &event); + + redraw(event.type); + } +} + +static void usage(const char *prg) +{ + const char *bar= +"\n------------------------------------------------------------------------\n"; + + fputs(bar, stderr); + fprintf(stderr,"%s version %s\n" + "USAGE: %s [--w WINDOW] [--bg BACKGROUND_COLOR] [FILE]\n", + prg, version, prg); + fputs("\twith BACKGROUND_COLOR = " + "(\"TEXT\" | \"#RGB\" | \"rgb:R/G/B\" | \"PIXEL\")\n" + "\te.g.\n\t(--bg \"red\" | --bg \"#ff0000\" " + "| --bg \"rgb:ff/00/00\" | --bg \"0xf800\")\n" + "\twith FILE=(idf.mng | idf.jng | idf.png)",stderr); + fputs(bar, stderr); +} + +static void shrink_name(char *buf) +{ + char *s, *d; + int ch; + + s = d = buf; + while((ch = *s++)) + { + if(isspace(ch)) continue; + *d++ = tolower(ch); + } + *d = 0; +} + +int main(int argc, char **argv) +{ + FILE *reader; + char *read_idf, *s; + char *ok; + int i; + unsigned char has_bg_color, has_bg_pixel; + Window external_win; + Pixel bg_pixel; + + if((prg_idf = strrchr(argv[0], '/')) == NULL) + prg_idf = argv[0]; + else + ++prg_idf; + + memset(&img, 0, sizeof(ImageInfo)); + external_win = 0; read_idf = NULL; reader = NULL; + has_bg_color = has_bg_pixel = 0; + bg_pixel = 0; + i = 0; + + while(++i < argc) + { + s = argv[i]; + + if(strcmp(s, "--help") == 0 + || strcmp(s, "-help") == 0 + || *s == '?') + { + usage(prg_idf); + return 0; + } + if(strcasecmp(s, "--w") == 0) + { + ++i; + s = argv[i]; + external_win = strtoul(s, &ok, 10); + if(*ok) + return 0; + continue; + } + if(strcasecmp(s, "--bg") == 0) + { + ++i; + s = argv[i]; + if(*s == '#' || strncasecmp(s, "rgb:", 4) == 0 || isalpha(*s)) + { + strncpy(img.bg_color, s, MAX_COLORBUF); + img.bg_color[MAX_COLORBUF] = 0; + has_bg_color = 1; + + if(*s != '#') + shrink_name(img.bg_color); + continue; + } + bg_pixel = strtoul(s, &ok, 16); + + if(*ok == 0) + has_bg_pixel = 1; + continue; + } + if(*s != '-') + { + read_idf = s; continue; + } + } + if(read_idf != NULL) + { + reader = fopen(read_idf, "rb"); + if(reader == NULL) + { + perror(read_idf); + fprintf(stderr, "\n\n%s: cannot open file '%s'\n\n", prg_idf, read_idf); + return 0; + } + } + img.argv = argv; + img.argc_ptr = &argc; + img.external_win = external_win; + img.has_bg_pixel = has_bg_pixel; + img.bg_pixel = bg_pixel; + img.has_bg_color = has_bg_color; + + if(!has_bg_pixel && !has_bg_color) + { + strcpy(img.bg_color, DEFAULT_BACKGROUND); + img.has_bg_color = 1; + } + + if(read_idf == NULL && external_win == 0) + create_widgets(5,5); + + run_viewer(reader, read_idf); + + Viewer_postlude(); + return 0; +} diff --git a/Engine/lib/lmng/contrib/kylix/libmng.pas b/Engine/lib/lmng/contrib/kylix/libmng.pas new file mode 100644 index 000000000..f81c17645 --- /dev/null +++ b/Engine/lib/lmng/contrib/kylix/libmng.pas @@ -0,0 +1,1726 @@ +unit libmng; + +{****************************************************************************} +{* *} +{* COPYRIGHT NOTICE: *} +{* *} +{* Copyright (c) 2000-2002 Gerard Juyn (gerard@libmng.com) *} +{* [You may insert additional notices after this sentence if you modify *} +{* this source] *} +{* *} +{* For the purposes of this copyright and license, "Contributing Authors" *} +{* is defined as the following set of individuals: *} +{* *} +{* Gerard Juyn *} +{* (hopefully some more to come...) *} +{* *} +{* The MNG Library is supplied "AS IS". The Contributing Authors *} +{* disclaim all warranties, expressed or implied, including, without *} +{* limitation, the warranties of merchantability and of fitness for any *} +{* purpose. The Contributing Authors assume no liability for direct, *} +{* indirect, incidental, special, exemplary, or consequential damages, *} +{* which may result from the use of the MNG Library, even if advised of *} +{* the possibility of such damage. *} +{* *} +{* Permission is hereby granted to use, copy, modify, and distribute this *} +{* source code, or portions hereof, for any purpose, without fee, subject *} +{* to the following restrictions: *} +{* *} +{* 1. The origin of this source code must not be misrepresented; *} +{* you must not claim that you wrote the original software. *} +{* *} +{* 2. Altered versions must be plainly marked as such and must not be *} +{* misrepresented as being the original source. *} +{* *} +{* 3. This Copyright notice may not be removed or altered from any source *} +{* or altered source distribution. *} +{* *} +{* The Contributing Authors specifically permit, without fee, and *} +{* encourage the use of this source code as a component to supporting *} +{* the MNG and JNG file format in commercial products. If you use this *} +{* source code in a product, acknowledgment would be highly appreciated. *} +{* *} +{****************************************************************************} +{* *} +{* project : libmng *} +{* file : libmng.pas copyright (c) 2000-2002 G.Juyn *} +{* version : 1.0.5 *} +{* *} +{* purpose : libmng.so wrapper unit *} +{* *} +{* author : G.Juyn *} +{* web : http://www.3-t.com *} +{* email : mailto:info@3-t.com *} +{* *} +{* comment : contains the pascal-translation of libmng.h *} +{* can be used by Kylix programs to access the libmng.so *} +{* *} +{* changes : 1.0.5 - 09/21/2002 - G.Juyn *} +{* - modified for Kylix use *} +{* *} +{****************************************************************************} + +interface + +{****************************************************************************} + +const MNG_TRUE = TRUE; + MNG_FALSE = FALSE; + MNG_NULL = nil; + +type mng_uint32 = cardinal; + mng_int32 = integer; + mng_uint16 = word; + mng_int16 = smallint; + mng_uint8 = byte; + mng_int8 = shortint; + mng_bool = boolean; + mng_ptr = pointer; + mng_pchar = pchar; + + mng_handle = pointer; + mng_retcode = mng_int32; + mng_chunkid = mng_uint32; + + mng_size_t = cardinal; + + mng_imgtype = (mng_it_unknown, mng_it_png, mng_it_mng, mng_it_jng); + mng_speedtype = (mng_st_normal, mng_st_fast, mng_st_slow, mng_st_slowest); + + mng_uint32p = ^mng_uint32; + mng_uint16p = ^mng_uint16; + mng_uint8p = ^mng_uint8; + mng_chunkidp = ^mng_chunkid; + + mng_palette8e = packed record { 8-bit palette element } + iRed : mng_uint8; + iGreen : mng_uint8; + iBlue : mng_uint8; + end; + + mng_palette8 = packed array [0 .. 255] of mng_palette8e; + + mng_uint8arr = packed array [0 .. 255] of mng_uint8; + mng_uint8arr4 = packed array [0 .. 3] of mng_uint8; + mng_uint16arr = packed array [0 .. 255] of mng_uint16; + mng_uint32arr2 = packed array [0 .. 1] of mng_uint32; + +{****************************************************************************} + +type mng_memalloc = function ( iLen : mng_size_t) : mng_ptr; cdecl; + mng_memfree = procedure ( iPtr : mng_ptr; + iLen : mng_size_t); cdecl; + +type mng_openstream = function ( hHandle : mng_handle) : mng_bool; cdecl; +type mng_closestream = function ( hHandle : mng_handle) : mng_bool; cdecl; + +type mng_readdata = function ( hHandle : mng_handle; + pBuf : mng_ptr; + iBuflen : mng_uint32; + var pRead : mng_uint32) : mng_bool; cdecl; + +type mng_writedata = function ( hHandle : mng_handle; + pBuf : mng_ptr; + iBuflen : mng_uint32; + var pWritten : mng_uint32) : mng_bool; cdecl; + +type mng_errorproc = function ( hHandle : mng_handle; + iErrorcode : mng_retcode; + iSeverity : mng_uint8; + iChunkname : mng_chunkid; + iChunkseq : mng_uint32; + iExtra1 : mng_int32; + iExtra2 : mng_int32; + zErrortext : mng_pchar ) : mng_bool; cdecl; +type mng_traceproc = function ( hHandle : mng_handle; + iFuncnr : mng_int32; + iFuncseq : mng_uint32; + zFuncname : mng_pchar ) : mng_bool; cdecl; + +type mng_processheader = function ( hHandle : mng_handle; + iWidth : mng_uint32; + iHeight : mng_uint32) : mng_bool; cdecl; +type mng_processtext = function ( hHandle : mng_handle; + iType : mng_uint8; + zKeyword : mng_pchar; + zText : mng_pchar; + zLanguage : mng_pchar; + zTranslation : mng_pchar ) : mng_bool; cdecl; + +type mng_processsave = function ( hHandle : mng_handle) : mng_bool; cdecl; +type mng_processseek = function ( hHandle : mng_handle; + zName : mng_pchar ) : mng_bool; cdecl; + +type mng_processneed = function ( hHandle : mng_handle; + zKeyword : mng_pchar ) : mng_bool; cdecl; + +type mng_processunknown = function ( hHandle : mng_handle; + iChunkid : mng_chunkid; + iRawlen : mng_uint32; + pRawdata : mng_ptr ) : mng_bool; cdecl; + +type mng_getcanvasline = function ( hHandle : mng_handle; + iLinenr : mng_uint32) : mng_ptr; cdecl; +type mng_getalphaline = function ( hHandle : mng_handle; + iLinenr : mng_uint32) : mng_ptr; cdecl; +type mng_getbkgdline = function ( hHandle : mng_handle; + iLinenr : mng_uint32) : mng_ptr; cdecl; +type mng_refresh = function ( hHandle : mng_handle; + iX : mng_uint32; + iY : mng_uint32; + iWidth : mng_uint32; + iHeight : mng_uint32) : mng_bool; cdecl; + +type mng_gettickcount = function ( hHandle : mng_handle) : mng_uint32; cdecl; +type mng_settimer = function ( hHandle : mng_handle; + iMsecs : mng_uint32) : mng_bool; cdecl; + +type mng_processgamma = function ( hHandle : mng_handle; + iGamma : mng_uint32) : mng_bool; cdecl; +type mng_processchroma = function ( hHandle : mng_handle; + iWhitex : mng_uint32; + iWhitey : mng_uint32; + iRedx : mng_uint32; + iRedy : mng_uint32; + iGreenx : mng_uint32; + iGreeny : mng_uint32; + iBluex : mng_uint32; + iBluey : mng_uint32) : mng_bool; cdecl; +type mng_processsrgb = function ( hHandle : mng_handle; + iIntent : mng_uint8 ) : mng_bool; cdecl; +type mng_processiccp = function ( hHandle : mng_handle; + iProfilesize : mng_uint32; + pProfile : mng_ptr ) : mng_bool; cdecl; +type mng_processarow = function ( hHandle : mng_handle; + iRowsamples : mng_uint32; + bIsRGBA16 : mng_bool; + pRow : mng_ptr ) : mng_bool; cdecl; + +type mng_iteratechunk = function ( hHandle : mng_handle; + hChunk : mng_handle; + iChunkid : mng_chunkid; + iChunkseq : mng_uint32) : mng_bool; cdecl; + +{****************************************************************************} + +function mng_initialize ( pUserdata : mng_ptr; + fMemalloc : mng_memalloc; + fMemfree : mng_memfree; + fTraceproc : mng_traceproc ) : mng_handle; cdecl; + +function mng_reset ( hHandle : mng_handle ) : mng_retcode; cdecl; + +function mng_cleanup (var hHandle : mng_handle ) : mng_retcode; cdecl; + +function mng_read ( hHandle : mng_handle ) : mng_retcode; cdecl; +function mng_read_resume ( hHandle : mng_handle ) : mng_retcode; cdecl; +function mng_write ( hHandle : mng_handle ) : mng_retcode; cdecl; +function mng_create ( hHandle : mng_handle ) : mng_retcode; cdecl; + +function mng_readdisplay ( hHandle : mng_handle ) : mng_retcode; cdecl; +function mng_display ( hHandle : mng_handle ) : mng_retcode; cdecl; +function mng_display_resume ( hHandle : mng_handle ) : mng_retcode; cdecl; +function mng_display_freeze ( hHandle : mng_handle ) : mng_retcode; cdecl; +function mng_display_reset ( hHandle : mng_handle ) : mng_retcode; cdecl; +function mng_display_goframe ( hHandle : mng_handle; + iFramenr : mng_uint32 ) : mng_retcode; cdecl; +function mng_display_golayer ( hHandle : mng_handle; + iLayernr : mng_uint32 ) : mng_retcode; cdecl; +function mng_display_gotime ( hHandle : mng_handle; + iPlaytime : mng_uint32 ) : mng_retcode; cdecl; + +function mng_trapevent ( hHandle : mng_handle; + iEventtype : mng_uint8; + iX : mng_int32; + iY : mng_int32 ) : mng_retcode; cdecl; + +function mng_getlasterror ( hHandle : mng_handle; + var iSeverity : mng_uint8; + var iChunkname : mng_chunkid; + var iChunkseq : mng_uint32; + var iExtra1 : mng_int32; + var iExtra2 : mng_int32; + var zErrortext : mng_pchar ) : mng_retcode; cdecl; + +{****************************************************************************} + +function mng_setcb_memalloc ( hHandle : mng_handle; + fProc : mng_memalloc ) : mng_retcode; cdecl; +function mng_setcb_memfree ( hHandle : mng_handle; + fProc : mng_memfree ) : mng_retcode; cdecl; + +function mng_setcb_openstream ( hHandle : mng_handle; + fProc : mng_openstream ) : mng_retcode; cdecl; +function mng_setcb_closestream ( hHandle : mng_handle; + fProc : mng_closestream ) : mng_retcode; cdecl; + +function mng_setcb_readdata ( hHandle : mng_handle; + fProc : mng_readdata ) : mng_retcode; cdecl; + +function mng_setcb_writedata ( hHandle : mng_handle; + fProc : mng_writedata ) : mng_retcode; cdecl; + +function mng_setcb_errorproc ( hHandle : mng_handle; + fProc : mng_errorproc ) : mng_retcode; cdecl; +function mng_setcb_traceproc ( hHandle : mng_handle; + fProc : mng_traceproc ) : mng_retcode; cdecl; + +function mng_setcb_processheader ( hHandle : mng_handle; + fProc : mng_processheader) : mng_retcode; cdecl; +function mng_setcb_processtext ( hHandle : mng_handle; + fProc : mng_processtext ) : mng_retcode; cdecl; + +function mng_setcb_getcanvasline ( hHandle : mng_handle; + fProc : mng_getcanvasline) : mng_retcode; cdecl; +function mng_setcb_getalphaline ( hHandle : mng_handle; + fProc : mng_getalphaline ) : mng_retcode; cdecl; +function mng_setcb_getbkgdline ( hHandle : mng_handle; + fProc : mng_getbkgdline ) : mng_retcode; cdecl; +function mng_setcb_refresh ( hHandle : mng_handle; + fProc : mng_refresh ) : mng_retcode; cdecl; + +function mng_setcb_gettickcount ( hHandle : mng_handle; + fProc : mng_gettickcount ) : mng_retcode; cdecl; +function mng_setcb_settimer ( hHandle : mng_handle; + fProc : mng_settimer ) : mng_retcode; cdecl; + +function mng_setcb_processgamma ( hHandle : mng_handle; + fProc : mng_processgamma ) : mng_retcode; cdecl; +function mng_setcb_processchroma ( hHandle : mng_handle; + fProc : mng_processchroma) : mng_retcode; cdecl; +function mng_setcb_processsrgb ( hHandle : mng_handle; + fProc : mng_processsrgb ) : mng_retcode; cdecl; +function mng_setcb_processiccp ( hHandle : mng_handle; + fProc : mng_processiccp ) : mng_retcode; cdecl; +function mng_setcb_processarow ( hHandle : mng_handle; + fProc : mng_processarow ) : mng_retcode; cdecl; + +{****************************************************************************} + +function mng_getcb_memalloc ( hHandle : mng_handle ) : mng_memalloc; cdecl; +function mng_getcb_memfree ( hHandle : mng_handle ) : mng_memfree; cdecl; + +function mng_getcb_openstream ( hHandle : mng_handle ) : mng_openstream; cdecl; +function mng_getcb_closestream ( hHandle : mng_handle ) : mng_closestream; cdecl; + +function mng_getcb_readdata ( hHandle : mng_handle ) : mng_readdata; cdecl; + +function mng_getcb_writedata ( hHandle : mng_handle ) : mng_writedata; cdecl; + +function mng_getcb_errorproc ( hHandle : mng_handle ) : mng_errorproc; cdecl; +function mng_getcb_traceproc ( hHandle : mng_handle ) : mng_traceproc; cdecl; + +function mng_getcb_processheader ( hHandle : mng_handle ) : mng_processheader; cdecl; +function mng_getcb_processtext ( hHandle : mng_handle ) : mng_processtext; cdecl; + +function mng_getcb_getcanvasline ( hHandle : mng_handle ) : mng_getcanvasline; cdecl; +function mng_getcb_getalphaline ( hHandle : mng_handle ) : mng_getalphaline; cdecl; +function mng_getcb_getbkgdline ( hHandle : mng_handle ) : mng_getbkgdline; cdecl; +function mng_getcb_refresh ( hHandle : mng_handle ) : mng_refresh; cdecl; + +function mng_getcb_gettickcount ( hHandle : mng_handle ) : mng_gettickcount; cdecl; +function mng_getcb_settimer ( hHandle : mng_handle ) : mng_settimer; cdecl; + +function mng_getcb_processgamma ( hHandle : mng_handle ) : mng_processgamma; cdecl; +function mng_getcb_processchroma ( hHandle : mng_handle ) : mng_processchroma; cdecl; +function mng_getcb_processsrgb ( hHandle : mng_handle ) : mng_processsrgb; cdecl; +function mng_getcb_processiccp ( hHandle : mng_handle ) : mng_processiccp; cdecl; +function mng_getcb_processarow ( hHandle : mng_handle ) : mng_processarow; cdecl; + +{****************************************************************************} + +function mng_set_userdata ( hHandle : mng_handle; + pUserdata : mng_ptr ) : mng_retcode; cdecl; + +function mng_set_canvasstyle ( hHandle : mng_handle; + iStyle : mng_uint32 ) : mng_retcode; cdecl; +function mng_set_bkgdstyle ( hHandle : mng_handle; + iStyle : mng_uint32 ) : mng_retcode; cdecl; + +function mng_set_bgcolor ( hHandle : mng_handle; + iRed : mng_uint16; + iGreen : mng_uint16; + iBlue : mng_uint16 ) : mng_retcode; cdecl; + +function mng_set_usebkgd ( hHandle : mng_handle; + bUseBKGD : mng_bool ) : mng_retcode; cdecl; + +function mng_set_storechunks ( hHandle : mng_handle; + bStorechunks : mng_bool ) : mng_retcode; cdecl; + +function mng_set_cacheplayback ( hHandle : mng_handle; + bCacheplayback : mng_bool ) : mng_retcode; cdecl; + +function mng_set_viewgammaint ( hHandle : mng_handle; + iGamma : mng_uint32 ) : mng_retcode; cdecl; +function mng_set_displaygammaint ( hHandle : mng_handle; + iGamma : mng_uint32 ) : mng_retcode; cdecl; +function mng_set_dfltimggammaint ( hHandle : mng_handle; + iGamma : mng_uint32 ) : mng_retcode; cdecl; + +function mng_set_srgb ( hHandle : mng_handle; + bIssRGB : mng_bool ) : mng_retcode; cdecl; +function mng_set_outputprofile ( hHandle : mng_handle; + zFilename : mng_pchar ) : mng_retcode; cdecl; +function mng_set_outputprofile2 ( hHandle : mng_handle; + iProfilesize : mng_uint32; + pProfile : mng_ptr ) : mng_retcode; cdecl; +function mng_set_srgbprofile ( hHandle : mng_handle; + zFilename : mng_pchar ) : mng_retcode; cdecl; +function mng_set_srgbprofile2 ( hHandle : mng_handle; + iProfilesize : mng_uint32; + pProfile : mng_ptr ) : mng_retcode; cdecl; + +function mng_set_maxcanvaswidth ( hHandle : mng_handle; + iMaxwidth : mng_uint32 ) : mng_retcode; cdecl; +function mng_set_maxcanvasheight ( hHandle : mng_handle; + iMaxheight : mng_uint32 ) : mng_retcode; cdecl; +function mng_set_maxcanvassize ( hHandle : mng_handle; + iMaxwidth : mng_uint32; + iMaxheight : mng_uint32 ) : mng_retcode; cdecl; + +function mng_set_suspensionmode ( hHandle : mng_handle; + bSuspensionmode : mng_bool ) : mng_retcode; cdecl; + +function mng_set_speed ( hHandle : mng_handle; + iSpeed : mng_speedtype ) : mng_retcode; cdecl; + +{****************************************************************************} + +function mng_get_userdata ( hHandle : mng_handle ) : mng_ptr; cdecl; + +function mng_get_sigtype ( hHandle : mng_handle ) : mng_imgtype; cdecl; +function mng_get_imagetype ( hHandle : mng_handle ) : mng_imgtype; cdecl; +function mng_get_imagewidth ( hHandle : mng_handle ) : mng_uint32; cdecl; +function mng_get_imageheight ( hHandle : mng_handle ) : mng_uint32; cdecl; +function mng_get_ticks ( hHandle : mng_handle ) : mng_uint32; cdecl; +function mng_get_framecount ( hHandle : mng_handle ) : mng_uint32; cdecl; +function mng_get_layercount ( hHandle : mng_handle ) : mng_uint32; cdecl; +function mng_get_playtime ( hHandle : mng_handle ) : mng_uint32; cdecl; +function mng_get_simplicity ( hHandle : mng_handle ) : mng_uint32; cdecl; + +function mng_get_canvasstyle ( hHandle : mng_handle ) : mng_uint32; cdecl; +function mng_get_bkgdstyle ( hHandle : mng_handle ) : mng_uint32; cdecl; + +procedure mng_get_bgcolor ( hHandle : mng_handle; + var iRed : mng_uint16; + var iGreen : mng_uint16; + var iBlue : mng_uint16 ); cdecl; + +function mng_get_usebkgd ( hHandle : mng_handle ) : mng_bool; cdecl; + +function mng_get_storechunks ( hHandle : mng_handle ) : mng_bool; cdecl; +function mng_get_cacheplayback ( hHandle : mng_handle ) : mng_bool; cdecl; + +function mng_get_viewgammaint ( hHandle : mng_handle ) : mng_uint32; cdecl; +function mng_get_displaygammaint ( hHandle : mng_handle ) : mng_uint32; cdecl; +function mng_get_dfltimggammaint ( hHandle : mng_handle ) : mng_uint32; cdecl; + +function mng_get_srgb ( hHandle : mng_handle ) : mng_bool; cdecl; + +function mng_get_maxcanvaswidth ( hHandle : mng_handle ) : mng_uint32; cdecl; +function mng_get_maxcanvasheight ( hHandle : mng_handle ) : mng_uint32; cdecl; + +function mng_get_suspensionmode ( hHandle : mng_handle ) : mng_bool; cdecl; + +function mng_get_speed ( hHandle : mng_handle ) : mng_speedtype; cdecl; +function mng_get_imagelevel ( hHandle : mng_handle ) : mng_uint32; cdecl; + +function mng_get_starttime ( hHandle : mng_handle ) : mng_uint32; cdecl; +function mng_get_runtime ( hHandle : mng_handle ) : mng_uint32; cdecl; +function mng_get_currentframe ( hHandle : mng_handle ) : mng_uint32; cdecl; +function mng_get_currentlayer ( hHandle : mng_handle ) : mng_uint32; cdecl; +function mng_get_currentplaytime ( hHandle : mng_handle ) : mng_uint32; cdecl; + +function mng_status_error ( hHandle : mng_handle ) : mng_bool; cdecl; +function mng_status_reading ( hHandle : mng_handle ) : mng_bool; cdecl; +function mng_status_suspendbreak ( hHandle : mng_handle ) : mng_bool; cdecl; +function mng_status_creating ( hHandle : mng_handle ) : mng_bool; cdecl; +function mng_status_writing ( hHandle : mng_handle ) : mng_bool; cdecl; +function mng_status_displaying ( hHandle : mng_handle ) : mng_bool; cdecl; +function mng_status_running ( hHandle : mng_handle ) : mng_bool; cdecl; +function mng_status_timerbreak ( hHandle : mng_handle ) : mng_bool; cdecl; +function mng_status_dynamic ( hHandle : mng_handle ) : mng_bool; cdecl; + +{****************************************************************************} + +function mng_iterate_chunks ( hHandle : mng_handle; + iChunkseq : mng_uint32; + fProc : mng_iteratechunk ) : mng_retcode; cdecl; + +{****************************************************************************} + +function mng_getchunk_ihdr ( hHandle : mng_handle; + hChunk : mng_handle; + var iWidth : mng_uint32; + var iHeight : mng_uint32; + var iBitdepth : mng_uint8; + var iColortype : mng_uint8; + var iCompression : mng_uint8; + var iFilter : mng_uint8; + var iInterlace : mng_uint8 ) : mng_retcode; cdecl; + +function mng_getchunk_plte ( hHandle : mng_handle; + hChunk : mng_handle; + var iCount : mng_uint32; + var aPalette : mng_palette8 ) : mng_retcode; cdecl; + +function mng_getchunk_idat ( hHandle : mng_handle; + hChunk : mng_handle; + var iRawlen : mng_uint32; + var pRawdata : mng_ptr ) : mng_retcode; cdecl; + +function mng_getchunk_trns ( hHandle : mng_handle; + hChunk : mng_handle; + var bEmpty : mng_bool; + var bGlobal : mng_bool; + var iType : mng_uint8; + var iCount : mng_uint32; + var aAlphas : mng_uint8arr; + var iGray : mng_uint16; + var iRed : mng_uint16; + var iGreen : mng_uint16; + var iBlue : mng_uint16; + var iRawlen : mng_uint32; + var aRawdata : mng_uint8arr ) : mng_retcode; cdecl; + +function mng_getchunk_gama ( hHandle : mng_handle; + hChunk : mng_handle; + var bEmpty : mng_bool; + var iGamma : mng_uint32 ) : mng_retcode; cdecl; + +function mng_getchunk_chrm ( hHandle : mng_handle; + hChunk : mng_handle; + var bEmpty : mng_bool; + var iWhitepointx : mng_uint32; + var iWhitepointy : mng_uint32; + var iRedx : mng_uint32; + var iRedy : mng_uint32; + var iGreenx : mng_uint32; + var iGreeny : mng_uint32; + var iBluex : mng_uint32; + var iBluey : mng_uint32 ) : mng_retcode; cdecl; + +function mng_getchunk_srgb ( hHandle : mng_handle; + hChunk : mng_handle; + var bEmpty : mng_bool; + var iRenderingintent : mng_uint8 ) : mng_retcode; cdecl; + +function mng_getchunk_iccp ( hHandle : mng_handle; + hChunk : mng_handle; + var bEmpty : mng_bool; + var iNamesize : mng_uint32; + var zName : mng_pchar; + var iCompression : mng_uint8; + var iProfilesize : mng_uint32; + var pProfile : mng_ptr ) : mng_retcode; cdecl; + +function mng_getchunk_text ( hHandle : mng_handle; + hChunk : mng_handle; + var iKeywordsize : mng_uint32; + var zKeyword : mng_pchar; + var iTextsize : mng_uint32; + var zText : mng_pchar ) : mng_retcode; cdecl; + +function mng_getchunk_ztxt ( hHandle : mng_handle; + hChunk : mng_handle; + var iKeywordsize : mng_uint32; + var zKeyword : mng_pchar; + var iCompression : mng_uint8; + var iTextsize : mng_uint32; + var zText : mng_pchar ) : mng_retcode; cdecl; + +function mng_getchunk_itxt ( hHandle : mng_handle; + hChunk : mng_handle; + var iKeywordsize : mng_uint32; + var zKeyword : mng_pchar; + var iCompressionflag : mng_uint8; + var iCompressionmethod : mng_uint8; + var iLanguagesize : mng_uint32; + var zLanguage : mng_pchar; + var iTranslationsize : mng_uint32; + var zTranslation : mng_pchar; + var iTextsize : mng_uint32; + var zText : mng_pchar ) : mng_retcode; cdecl; + +function mng_getchunk_bkgd ( hHandle : mng_handle; + hChunk : mng_handle; + var bEmpty : mng_bool; + var iType : mng_uint8; + var iIndex : mng_uint8; + var iGray : mng_uint16; + var iRed : mng_uint16; + var iGreen : mng_uint16; + var iBlue : mng_uint16 ) : mng_retcode; cdecl; + +function mng_getchunk_phys ( hHandle : mng_handle; + hChunk : mng_handle; + var bEmpty : mng_bool; + var iSizex : mng_uint32; + var iSizey : mng_uint32; + var iUnit : mng_uint8 ) : mng_retcode; cdecl; + +function mng_getchunk_sbit ( hHandle : mng_handle; + hChunk : mng_handle; + var bEmpty : mng_bool; + var iType : mng_uint8; + var aBits : mng_uint8arr4) : mng_retcode; cdecl; + +function mng_getchunk_splt ( hHandle : mng_handle; + hChunk : mng_handle; + var bEmpty : mng_bool; + var iNamesize : mng_uint32; + var zName : mng_pchar; + var iSampledepth : mng_uint8; + var iEntrycount : mng_uint32; + var pEntries : mng_ptr ) : mng_retcode; cdecl; + +function mng_getchunk_hist ( hHandle : mng_handle; + hChunk : mng_handle; + var iEntrycount : mng_uint32; + var aEntries : mng_uint16arr) : mng_retcode; cdecl; + +function mng_getchunk_time ( hHandle : mng_handle; + hChunk : mng_handle; + var iYear : mng_uint16; + var iMonth : mng_uint8; + var iDay : mng_uint8; + var iHour : mng_uint8; + var iMinute : mng_uint8; + var iSecond : mng_uint8 ) : mng_retcode; cdecl; + +function mng_getchunk_mhdr ( hHandle : mng_handle; + hChunk : mng_handle; + var iWidth : mng_uint32; + var iHeight : mng_uint32; + var iTicks : mng_uint32; + var iLayercount : mng_uint32; + var iFramecount : mng_uint32; + var iPlaytime : mng_uint32; + var iSimplicity : mng_uint32 ) : mng_retcode; cdecl; + +function mng_getchunk_loop ( hHandle : mng_handle; + hChunk : mng_handle; + var iLevel : mng_uint8; + var iRepeat : mng_uint32; + var iTermination : mng_uint8; + var iItermin : mng_uint32; + var iItermax : mng_uint32; + var iCount : mng_uint32; + var pSignals : mng_uint32p ) : mng_retcode; cdecl; + +function mng_getchunk_endl ( hHandle : mng_handle; + hChunk : mng_handle; + var iLevel : mng_uint8 ) : mng_retcode; cdecl; + +function mng_getchunk_defi ( hHandle : mng_handle; + hChunk : mng_handle; + var iObjectid : mng_uint16; + var iDonotshow : mng_uint8; + var iConcrete : mng_uint8; + var bHasloca : mng_bool; + var iXlocation : mng_int32; + var iYlocation : mng_int32; + var bHasclip : mng_bool; + var iLeftcb : mng_int32; + var iRightcb : mng_int32; + var iTopcb : mng_int32; + var iBottomcb : mng_int32 ) : mng_retcode; cdecl; + +function mng_getchunk_basi ( hHandle : mng_handle; + hChunk : mng_handle; + var iWidth : mng_uint32; + var iHeight : mng_uint32; + var iBitdepth : mng_uint8; + var iColortype : mng_uint8; + var iCompression : mng_uint8; + var iFilter : mng_uint8; + var iInterlace : mng_uint8; + var iRed : mng_uint16; + var iGreen : mng_uint16; + var iBlue : mng_uint16; + var iAlpha : mng_uint16; + var iViewable : mng_uint8 ) : mng_retcode; cdecl; + +function mng_getchunk_clon ( hHandle : mng_handle; + hChunk : mng_handle; + var iSourceid : mng_uint16; + var iCloneid : mng_uint16; + var iClonetype : mng_uint8; + var iDonotshow : mng_uint8; + var iConcrete : mng_uint8; + var bHasloca : mng_bool; + var iLocationtype : mng_uint8; + var iLocationx : mng_int32; + var iLocationy : mng_int32 ) : mng_retcode; cdecl; + +function mng_getchunk_past ( hHandle : mng_handle; + hChunk : mng_handle; + var iDestid : mng_uint16; + var iTargettype : mng_uint8; + var iTargetx : mng_int32; + var iTargety : mng_int32; + var iCount : mng_uint32 ) : mng_retcode; cdecl; + +function mng_getchunk_past_src ( hHandle : mng_handle; + hChunk : mng_handle; + iEntry : mng_uint32; + var iSourceid : mng_uint16; + var iComposition : mng_uint8; + var iOrientation : mng_uint8; + var iOffsettype : mng_uint8; + var iOffsetx : mng_int32; + var iOffsety : mng_int32; + var iBoundarytype : mng_uint8; + var iBoundaryl : mng_int32; + var iBoundaryr : mng_int32; + var iBoundaryt : mng_int32; + var iBoundaryb : mng_int32 ) : mng_retcode; cdecl; + +function mng_getchunk_disc ( hHandle : mng_handle; + hChunk : mng_handle; + var iCount : mng_uint32; + var pObjectids : mng_uint16p ) : mng_retcode; cdecl; + +function mng_getchunk_back ( hHandle : mng_handle; + hChunk : mng_handle; + var iRed : mng_uint16; + var iGreen : mng_uint16; + var iBlue : mng_uint16; + var iMandatory : mng_uint8; + var iImageid : mng_uint16; + var iTile : mng_uint8 ) : mng_retcode; cdecl; + +function mng_getchunk_fram ( hHandle : mng_handle; + hChunk : mng_handle; + var bEmpty : mng_bool; + var iMode : mng_uint8; + var iNamesize : mng_uint32; + var zName : mng_pchar; + var iChangedelay : mng_uint8; + var iChangetimeout : mng_uint8; + var iChangeclipping : mng_uint8; + var iChangesyncid : mng_uint8; + var iDelay : mng_uint32; + var iTimeout : mng_uint32; + var iBoundarytype : mng_uint8; + var iBoundaryl : mng_int32; + var iBoundaryr : mng_int32; + var iBoundaryt : mng_int32; + var iBoundaryb : mng_int32; + var iCount : mng_uint32; + var pSyncids : mng_uint32p ) : mng_retcode; cdecl; + +function mng_getchunk_move ( hHandle : mng_handle; + hChunk : mng_handle; + var iFirstid : mng_uint16; + var iLastid : mng_uint16; + var iMovetype : mng_uint8; + var iMovex : mng_int32; + var iMovey : mng_int32 ) : mng_retcode; cdecl; + +function mng_getchunk_clip ( hHandle : mng_handle; + hChunk : mng_handle; + var iFirstid : mng_uint16; + var iLastid : mng_uint16; + var iCliptype : mng_uint8; + var iClipl : mng_int32; + var iClipr : mng_int32; + var iClipt : mng_int32; + var iClipb : mng_int32 ) : mng_retcode; cdecl; + +function mng_getchunk_show ( hHandle : mng_handle; + hChunk : mng_handle; + var bEmpty : mng_bool; + var iFirstid : mng_uint16; + var iLastid : mng_uint16; + var iMode : mng_uint8 ) : mng_retcode; cdecl; + +function mng_getchunk_term ( hHandle : mng_handle; + hChunk : mng_handle; + var iTermaction : mng_uint8; + var iIteraction : mng_uint8; + var iDelay : mng_uint32; + var iItermax : mng_uint32 ) : mng_retcode; cdecl; + +function mng_getchunk_save ( hHandle : mng_handle; + hChunk : mng_handle; + var bEmpty : mng_bool; + var iOffsettype : mng_uint8; + var iCount : mng_uint32 ) : mng_retcode; cdecl; + +function mng_getchunk_save_entry ( hHandle : mng_handle; + hChunk : mng_handle; + iEntry : mng_uint32; + var iEntrytype : mng_uint8; + var iOffset : mng_uint32arr2; + var iStarttime : mng_uint32arr2; + var iLayernr : mng_uint32; + var iFramenr : mng_uint32; + var iNamesize : mng_uint32; + var zName : mng_pchar ) : mng_retcode; cdecl; + +function mng_getchunk_seek ( hHandle : mng_handle; + hChunk : mng_handle; + var iNamesize : mng_uint32; + var zName : mng_pchar ) : mng_retcode; cdecl; + +function mng_getchunk_expi ( hHandle : mng_handle; + hChunk : mng_handle; + var iSnapshotid : mng_uint16; + var iNamesize : mng_uint32; + var zName : mng_pchar ) : mng_retcode; cdecl; + +function mng_getchunk_fpri ( hHandle : mng_handle; + hChunk : mng_handle; + var iDeltatype : mng_uint8; + var iPriority : mng_uint8 ) : mng_retcode; cdecl; + +function mng_getchunk_need ( hHandle : mng_handle; + hChunk : mng_handle; + var iKeywordssize : mng_uint32; + var zKeywords : mng_pchar ) : mng_retcode; cdecl; + +function mng_getchunk_phyg ( hHandle : mng_handle; + hChunk : mng_handle; + var bEmpty : mng_bool; + var iSizex : mng_uint32; + var iSizey : mng_uint32; + var iUnit : mng_uint8 ) : mng_retcode; cdecl; + +function mng_getchunk_jhdr ( hHandle : mng_handle; + hChunk : mng_handle; + var iWidth : mng_uint32; + var iHeight : mng_uint32; + var iColortype : mng_uint8; + var iImagesampledepth : mng_uint8; + var iImagecompression : mng_uint8; + var iImageinterlace : mng_uint8; + var iAlphasampledepth : mng_uint8; + var iAlphacompression : mng_uint8; + var iAlphafilter : mng_uint8; + var iAlphainterlace : mng_uint8 ) : mng_retcode; cdecl; + +function mng_getchunk_jdat ( hHandle : mng_handle; + hChunk : mng_handle; + var iRawlen : mng_uint32; + var pRawdata : mng_ptr ) : mng_retcode; cdecl; + +function mng_getchunk_dhdr ( hHandle : mng_handle; + hChunk : mng_handle; + var iObjectid : mng_uint16; + var iImagetype : mng_uint8; + var iDeltatype : mng_uint8; + var iBlockwidth : mng_uint32; + var iBlockheight : mng_uint32; + var iBlockx : mng_uint32; + var iBlocky : mng_uint32 ) : mng_retcode; cdecl; + +function mng_getchunk_prom ( hHandle : mng_handle; + hChunk : mng_handle; + var iColortype : mng_uint8; + var iSampledepth : mng_uint8; + var iFilltype : mng_uint8 ) : mng_retcode; cdecl; + +function mng_getchunk_pplt ( hHandle : mng_handle; + hChunk : mng_handle; + var iCount : mng_uint32 ) : mng_retcode; cdecl; + +function mng_getchunk_pplt_entry ( hHandle : mng_handle; + hChunk : mng_handle; + iEntry : mng_uint32; + var iRed : mng_uint16; + var iGreen : mng_uint16; + var iBlue : mng_uint16; + var iAlpha : mng_uint16; + var bUsed : mng_bool ) : mng_retcode; cdecl; + +function mng_getchunk_drop ( hHandle : mng_handle; + hChunk : mng_handle; + var iCount : mng_uint32; + var pChunknames : mng_chunkidp ) : mng_retcode; cdecl; + +function mng_getchunk_dbyk ( hHandle : mng_handle; + hChunk : mng_handle; + var iChunkname : mng_chunkid; + var iPolarity : mng_uint8; + var iKeywordssize : mng_uint32; + var zKeywords : mng_pchar ) : mng_retcode; cdecl; + +function mng_getchunk_ordr ( hHandle : mng_handle; + hChunk : mng_handle; + var iCount : mng_uint32 ) : mng_retcode; cdecl; + +function mng_getchunk_ordr_entry ( hHandle : mng_handle; + hChunk : mng_handle; + iEntry : mng_uint32; + var iChunkname : mng_chunkid; + var iOrdertype : mng_uint8 ) : mng_retcode; cdecl; + +function mng_getchunk_unknown ( hHandle : mng_handle; + hChunk : mng_handle; + var iChunkname : mng_chunkid; + var iRawlen : mng_uint32; + var pRawdata : mng_ptr ) : mng_retcode; cdecl; + +{****************************************************************************} + +function mng_putchunk_ihdr ( hHandle : mng_handle; + iWidth : mng_uint32; + iHeight : mng_uint32; + iBitdepth : mng_uint8; + iColortype : mng_uint8; + iCompression : mng_uint8; + iFilter : mng_uint8; + iInterlace : mng_uint8 ) : mng_retcode; cdecl; + +function mng_putchunk_plte ( hHandle : mng_handle; + iCount : mng_uint32; + aPalette : mng_palette8 ) : mng_retcode; cdecl; + +function mng_putchunk_idat ( hHandle : mng_handle; + iRawlen : mng_uint32; + pRawdata : mng_ptr ) : mng_retcode; cdecl; + +function mng_putchunk_iend ( hHandle : mng_handle ) : mng_retcode; cdecl; + +function mng_putchunk_trns ( hHandle : mng_handle; + bEmpty : mng_bool; + bGlobal : mng_bool; + iType : mng_uint8; + iCount : mng_uint32; + aAlphas : mng_uint8arr; + iGray : mng_uint16; + iRed : mng_uint16; + iGreen : mng_uint16; + iBlue : mng_uint16; + iRawlen : mng_uint32; + aRawdata : mng_uint8arr ) : mng_retcode; cdecl; + +function mng_putchunk_gama ( hHandle : mng_handle; + bEmpty : mng_bool; + iGamma : mng_uint32 ) : mng_retcode; cdecl; + +function mng_putchunk_chrm ( hHandle : mng_handle; + bEmpty : mng_bool; + iWhitepointx : mng_uint32; + iWhitepointy : mng_uint32; + iRedx : mng_uint32; + iRedy : mng_uint32; + iGreenx : mng_uint32; + iGreeny : mng_uint32; + iBluex : mng_uint32; + iBluey : mng_uint32 ) : mng_retcode; cdecl; + +function mng_putchunk_srgb ( hHandle : mng_handle; + bEmpty : mng_bool; + iRenderingintent : mng_uint8 ) : mng_retcode; cdecl; + +function mng_putchunk_iccp ( hHandle : mng_handle; + bEmpty : mng_bool; + iNamesize : mng_uint32; + zName : mng_pchar; + iCompression : mng_uint8; + iProfilesize : mng_uint32; + pProfile : mng_ptr ) : mng_retcode; cdecl; + +function mng_putchunk_text ( hHandle : mng_handle; + iKeywordsize : mng_uint32; + zKeyword : mng_pchar; + iTextsize : mng_uint32; + zText : mng_pchar ) : mng_retcode; cdecl; + +function mng_putchunk_ztxt ( hHandle : mng_handle; + iKeywordsize : mng_uint32; + zKeyword : mng_pchar; + iCompression : mng_uint8; + iTextsize : mng_uint32; + zText : mng_pchar ) : mng_retcode; cdecl; + +function mng_putchunk_itxt ( hHandle : mng_handle; + iKeywordsize : mng_uint32; + zKeyword : mng_pchar; + iCompressionflag : mng_uint8; + iCompressionmethod : mng_uint8; + iLanguagesize : mng_uint32; + zLanguage : mng_pchar; + iTranslationsize : mng_uint32; + zTranslation : mng_pchar; + iTextsize : mng_uint32; + zText : mng_pchar ) : mng_retcode; cdecl; + +function mng_putchunk_bkgd ( hHandle : mng_handle; + bEmpty : mng_bool; + iType : mng_uint8; + iIndex : mng_uint8; + iGray : mng_uint16; + iRed : mng_uint16; + iGreen : mng_uint16; + iBlue : mng_uint16 ) : mng_retcode; cdecl; + +function mng_putchunk_phys ( hHandle : mng_handle; + bEmpty : mng_bool; + iSizex : mng_uint32; + iSizey : mng_uint32; + iUnit : mng_uint8 ) : mng_retcode; cdecl; + +function mng_putchunk_sbit ( hHandle : mng_handle; + bEmpty : mng_bool; + iType : mng_uint8; + aBits : mng_uint8arr4) : mng_retcode; cdecl; + +function mng_putchunk_splt ( hHandle : mng_handle; + bEmpty : mng_bool; + iNamesize : mng_uint32; + zName : mng_pchar; + iSampledepth : mng_uint8; + iEntrycount : mng_uint32; + pEntries : mng_ptr ) : mng_retcode; cdecl; + +function mng_putchunk_hist ( hHandle : mng_handle; + iEntrycount : mng_uint32; + aEntries : mng_uint16arr) : mng_retcode; cdecl; + +function mng_putchunk_time ( hHandle : mng_handle; + iYear : mng_uint16; + iMonth : mng_uint8; + iDay : mng_uint8; + iHour : mng_uint8; + iMinute : mng_uint8; + iSecond : mng_uint8 ) : mng_retcode; cdecl; + +function mng_putchunk_mhdr ( hHandle : mng_handle; + iWidth : mng_uint32; + iHeight : mng_uint32; + iTicks : mng_uint32; + iLayercount : mng_uint32; + iFramecount : mng_uint32; + iPlaytime : mng_uint32; + iSimplicity : mng_uint32 ) : mng_retcode; cdecl; + +function mng_putchunk_mend ( hHandle : mng_handle ) : mng_retcode; cdecl; + +function mng_putchunk_loop ( hHandle : mng_handle; + iLevel : mng_uint8; + iRepeat : mng_uint32; + iTermination : mng_uint8; + iItermin : mng_uint32; + iItermax : mng_uint32; + iCount : mng_uint32; + pSignals : mng_uint32p ) : mng_retcode; cdecl; + +function mng_putchunk_endl ( hHandle : mng_handle; + iLevel : mng_uint8 ) : mng_retcode; cdecl; + +function mng_putchunk_defi ( hHandle : mng_handle; + iObjectid : mng_uint16; + iDonotshow : mng_uint8; + iConcrete : mng_uint8; + bHasloca : mng_bool; + iXlocation : mng_int32; + iYlocation : mng_int32; + bHasclip : mng_bool; + iLeftcb : mng_int32; + iRightcb : mng_int32; + iTopcb : mng_int32; + iBottomcb : mng_int32 ) : mng_retcode; cdecl; + +function mng_putchunk_basi ( hHandle : mng_handle; + iWidth : mng_uint32; + iHeight : mng_uint32; + iBitdepth : mng_uint8; + iColortype : mng_uint8; + iCompression : mng_uint8; + iFilter : mng_uint8; + iInterlace : mng_uint8; + iRed : mng_uint16; + iGreen : mng_uint16; + iBlue : mng_uint16; + iAlpha : mng_uint16; + iViewable : mng_uint8 ) : mng_retcode; cdecl; + +function mng_putchunk_clon ( hHandle : mng_handle; + iSourceid : mng_uint16; + iCloneid : mng_uint16; + iClonetype : mng_uint8; + iDonotshow : mng_uint8; + iConcrete : mng_uint8; + bHasloca : mng_bool; + iLocationtype : mng_uint8; + iLocationx : mng_int32; + iLocationy : mng_int32 ) : mng_retcode; cdecl; + +function mng_putchunk_past ( hHandle : mng_handle; + iDestid : mng_uint16; + iTargettype : mng_uint8; + iTargetx : mng_int32; + iTargety : mng_int32; + iCount : mng_uint32 ) : mng_retcode; cdecl; + +function mng_putchunk_past_src ( hHandle : mng_handle; + iEntry : mng_uint32; + iSourceid : mng_uint16; + iComposition : mng_uint8; + iOrientation : mng_uint8; + iOffsettype : mng_uint8; + iOffsetx : mng_int32; + iOffsety : mng_int32; + iBoundarytype : mng_uint8; + iBoundaryl : mng_int32; + iBoundaryr : mng_int32; + iBoundaryt : mng_int32; + iBoundaryb : mng_int32 ) : mng_retcode; cdecl; + +function mng_putchunk_disc ( hHandle : mng_handle; + iCount : mng_uint32; + pObjectids : mng_uint16p ) : mng_retcode; cdecl; + +function mng_putchunk_back ( hHandle : mng_handle; + iRed : mng_uint16; + iGreen : mng_uint16; + iBlue : mng_uint16; + iMandatory : mng_uint8; + iImageid : mng_uint16; + iTile : mng_uint8 ) : mng_retcode; cdecl; + +function mng_putchunk_fram ( hHandle : mng_handle; + bEmpty : mng_bool; + iMode : mng_uint8; + iNamesize : mng_uint32; + zName : mng_pchar; + iChangedelay : mng_uint8; + iChangetimeout : mng_uint8; + iChangeclipping : mng_uint8; + iChangesyncid : mng_uint8; + iDelay : mng_uint32; + iTimeout : mng_uint32; + iBoundarytype : mng_uint8; + iBoundaryl : mng_int32; + iBoundaryr : mng_int32; + iBoundaryt : mng_int32; + iBoundaryb : mng_int32; + iCount : mng_uint32; + pSyncids : mng_uint32p ) : mng_retcode; cdecl; + +function mng_putchunk_move ( hHandle : mng_handle; + iFirstid : mng_uint16; + iLastid : mng_uint16; + iMovetype : mng_uint8; + iMovex : mng_int32; + iMovey : mng_int32 ) : mng_retcode; cdecl; + +function mng_putchunk_clip ( hHandle : mng_handle; + iFirstid : mng_uint16; + iLastid : mng_uint16; + iCliptype : mng_uint8; + iClipl : mng_int32; + iClipr : mng_int32; + iClipt : mng_int32; + iClipb : mng_int32 ) : mng_retcode; cdecl; + +function mng_putchunk_show ( hHandle : mng_handle; + bEmpty : mng_bool; + iFirstid : mng_uint16; + iLastid : mng_uint16; + iMode : mng_uint8 ) : mng_retcode; cdecl; + +function mng_putchunk_term ( hHandle : mng_handle; + iTermaction : mng_uint8; + iIteraction : mng_uint8; + iDelay : mng_uint32; + iItermax : mng_uint32 ) : mng_retcode; cdecl; + +function mng_putchunk_save ( hHandle : mng_handle; + bEmpty : mng_bool; + iOffsettype : mng_uint8; + iCount : mng_uint32 ) : mng_retcode; cdecl; + +function mng_putchunk_save_entry ( hHandle : mng_handle; + iEntry : mng_uint32; + iEntrytype : mng_uint8; + iOffset : mng_uint32arr2; + iStarttime : mng_uint32arr2; + iLayernr : mng_uint32; + iFramenr : mng_uint32; + iNamesize : mng_uint32; + zName : mng_pchar ) : mng_retcode; cdecl; + +function mng_putchunk_seek ( hHandle : mng_handle; + iNamesize : mng_uint32; + zName : mng_pchar ) : mng_retcode; cdecl; + +function mng_putchunk_expi ( hHandle : mng_handle; + iSnapshotid : mng_uint16; + iNamesize : mng_uint32; + zName : mng_pchar ) : mng_retcode; cdecl; + +function mng_putchunk_fpri ( hHandle : mng_handle; + iDeltatype : mng_uint8; + iPriority : mng_uint8 ) : mng_retcode; cdecl; + +function mng_putchunk_need ( hHandle : mng_handle; + iKeywordssize : mng_uint32; + zKeywords : mng_pchar ) : mng_retcode; cdecl; + +function mng_putchunk_phyg ( hHandle : mng_handle; + bEmpty : mng_bool; + iSizex : mng_uint32; + iSizey : mng_uint32; + iUnit : mng_uint8 ) : mng_retcode; cdecl; + +function mng_putchunk_jhdr ( hHandle : mng_handle; + iWidth : mng_uint32; + iHeight : mng_uint32; + iColortype : mng_uint8; + iImagesampledepth : mng_uint8; + iImagecompression : mng_uint8; + iImageinterlace : mng_uint8; + iAlphasampledepth : mng_uint8; + iAlphacompression : mng_uint8; + iAlphafilter : mng_uint8; + iAlphainterlace : mng_uint8 ) : mng_retcode; cdecl; + +function mng_putchunk_jdat ( hHandle : mng_handle; + iRawlen : mng_uint32; + pRawdata : mng_ptr ) : mng_retcode; cdecl; + +function mng_putchunk_dhdr ( hHandle : mng_handle; + iObjectid : mng_uint16; + iImagetype : mng_uint8; + iDeltatype : mng_uint8; + iBlockwidth : mng_uint32; + iBlockheight : mng_uint32; + iBlockx : mng_uint32; + iBlocky : mng_uint32 ) : mng_retcode; cdecl; + +function mng_putchunk_prom ( hHandle : mng_handle; + iColortype : mng_uint8; + iSampledepth : mng_uint8; + iFilltype : mng_uint8 ) : mng_retcode; cdecl; + +function mng_putchunk_pplt ( hHandle : mng_handle; + iCount : mng_uint32 ) : mng_retcode; cdecl; + +function mng_putchunk_pplt_entry ( hHandle : mng_handle; + iEntry : mng_uint32; + iRed : mng_uint16; + iGreen : mng_uint16; + iBlue : mng_uint16; + iAlpha : mng_uint16; + bUsed : mng_bool ) : mng_retcode; cdecl; + +function mng_putchunk_drop ( hHandle : mng_handle; + iCount : mng_uint32; + pChunknames : mng_chunkidp ) : mng_retcode; cdecl; + +function mng_putchunk_dbyk ( hHandle : mng_handle; + iChunkname : mng_chunkid; + iPolarity : mng_uint8; + iKeywordssize : mng_uint32; + zKeywords : mng_pchar ) : mng_retcode; cdecl; + +function mng_putchunk_ordr ( hHandle : mng_handle; + iCount : mng_uint32 ) : mng_retcode; cdecl; + +function mng_putchunk_ordr_entry ( hHandle : mng_handle; + iEntry : mng_uint32; + iChunkname : mng_chunkid; + iOrdertype : mng_uint8 ) : mng_retcode; cdecl; + +function mng_putchunk_unknown ( hHandle : mng_handle; + iChunkname : mng_chunkid; + iRawlen : mng_uint32; + pRawdata : mng_ptr ) : mng_retcode; cdecl; + +{****************************************************************************} + +function mng_updatemngheader ( hHandle : mng_handle; + iFramecount : mng_uint32; + iLayercount : mng_uint32; + iPlaytime : mng_uint32 ) : mng_retcode; cdecl; + +function mng_updatemngsimplicity ( hHandle : mng_handle; + iSimplicity : mng_uint32 ) : mng_retcode; cdecl; + +{****************************************************************************} + +const MNG_NOERROR = 0; + + MNG_OUTOFMEMORY = 1; + MNG_INVALIDHANDLE = 2; + MNG_NOCALLBACK = 3; + MNG_UNEXPECTEDEOF = 4; + MNG_ZLIBERROR = 5; + MNG_JPEGERROR = 6; + MNG_LCMSERROR = 7; + MNG_NOOUTPUTPROFILE = 8; + MNG_NOSRGBPROFILE = 9; + MNG_BUFOVERFLOW = 10; + MNG_FUNCTIONINVALID = 11; + MNG_OUTPUTERROR = 12; + MNG_JPEGBUFTOOSMALL = 13; + MNG_NEEDMOREDATA = 14; + MNG_NEEDTIMERWAIT = 15; + MNG_NEEDSECTIONWAIT = 16; + + MNG_APPIOERROR = 901; + MNG_APPTIMERERROR = 902; + MNG_APPCMSERROR = 903; + MNG_APPMISCERROR = 904; + MNG_APPTRACEABORT = 905; + + MNG_INTERNALERROR = 999; + + MNG_INVALIDSIG = 1025; + MNG_INVALIDCRC = 1027; + MNG_INVALIDLENGTH = 1028; + MNG_SEQUENCEERROR = 1029; + MNG_CHUNKNOTALLOWED = 1030; + MNG_MULTIPLEERROR = 1031; + MNG_PLTEMISSING = 1032; + MNG_IDATMISSING = 1033; + MNG_CANNOTBEEMPTY = 1034; + MNG_GLOBALLENGTHERR = 1035; + MNG_INVALIDBITDEPTH = 1036; + MNG_INVALIDCOLORTYPE = 1037; + MNG_INVALIDCOMPRESS = 1038; + MNG_INVALIDFILTER = 1039; + MNG_INVALIDINTERLACE = 1040; + MNG_NOTENOUGHIDAT = 1041; + MNG_PLTEINDEXERROR = 1042; + MNG_NULLNOTFOUND = 1043; + MNG_KEYWORDNULL = 1044; + MNG_OBJECTUNKNOWN = 1045; + MNG_OBJECTEXISTS = 1046; + MNG_TOOMUCHIDAT = 1047; + MNG_INVSAMPLEDEPTH = 1048; + MNG_INVOFFSETSIZE = 1049; + MNG_INVENTRYTYPE = 1050; + MNG_ENDWITHNULL = 1051; + MNG_INVIMAGETYPE = 1052; + MNG_INVDELTATYPE = 1053; + MNG_INVALIDINDEX = 1054; + MNG_TOOMUCHJDAT = 1055; + MNG_JPEGPARMSERR = 1056; + MNG_INVFILLMETHOD = 1057; + MNG_OBJNOTCONCRETE = 1058; + MNG_TARGETNOALPHA = 1059; + MNG_MNGTOOCOMPLEX = 1060; + MNG_UNKNOWNCRITICAL = 1061; + MNG_UNSUPPORTEDNEED = 1062; + MNG_INVALIDDELTA = 1063; + MNG_INVALIDMETHOD = 1064; + MNG_IMPROBABLELENGTH = 1065; + MNG_INVALIDBLOCK = 1066; + MNG_INVALIDEVENT = 1067; + MNG_INVALIDMASK = 1068; + MNG_NOMATCHINGLOOP = 1069; + MNG_SEEKNOTFOUND = 1070; + + MNG_INVALIDCNVSTYLE = 2049; + MNG_WRONGCHUNK = 2050; + MNG_INVALIDENTRYIX = 2051; + MNG_NOHEADER = 2052; + MNG_NOCORRCHUNK = 2053; + MNG_NOMHDR = 2054; + + MNG_IMAGETOOLARGE = 4097; + MNG_NOTANANIMATION = 4098; + MNG_FRAMENRTOOHIGH = 4099; + MNG_LAYERNRTOOHIGH = 4100; + MNG_PLAYTIMETOOHIGH = 4101; + MNG_FNNOTIMPLEMENTED = 4102; + + MNG_IMAGEFROZEN = 8193; + +{****************************************************************************} + +const MNG_CANVAS_RGB8 = $00000000; + MNG_CANVAS_RGBA8 = $00001000; + MNG_CANVAS_ARGB8 = $00003000; + MNG_CANVAS_RGB8_A8 = $00005000; + MNG_CANVAS_BGR8 = $00000001; + MNG_CANVAS_BGRX8 = $00010001; + MNG_CANVAS_BGRA8 = $00001001; + MNG_CANVAS_ABGR8 = $00003001; + MNG_CANVAS_RGB16 = $00000100; { not supported yet } + MNG_CANVAS_RGBA16 = $00001100; { not supported yet } + MNG_CANVAS_ARGB16 = $00003100; { not supported yet } + MNG_CANVAS_BGR16 = $00000101; { not supported yet } + MNG_CANVAS_BGRA16 = $00001101; { not supported yet } + MNG_CANVAS_ABGR16 = $00003101; { not supported yet } + MNG_CANVAS_GRAY8 = $00000002; { not supported yet } + MNG_CANVAS_GRAY16 = $00000102; { not supported yet } + MNG_CANVAS_GRAYA8 = $00001002; { not supported yet } + MNG_CANVAS_GRAYA16 = $00001102; { not supported yet } + MNG_CANVAS_AGRAY8 = $00003002; { not supported yet } + MNG_CANVAS_AGRAY16 = $00003102; { not supported yet } + MNG_CANVAS_DX15 = $00000003; { not supported yet } + MNG_CANVAS_DX16 = $00000004; { not supported yet } + +{****************************************************************************} + +const MNG_UINT_HUH = $40404040; + + MNG_UINT_BACK = $4241434b; + MNG_UINT_BASI = $42415349; + MNG_UINT_CLIP = $434c4950; + MNG_UINT_CLON = $434c4f4e; + MNG_UINT_DBYK = $4442594b; + MNG_UINT_DEFI = $44454649; + MNG_UINT_DHDR = $44484452; + MNG_UINT_DISC = $44495343; + MNG_UINT_DROP = $44524f50; + MNG_UINT_ENDL = $454e444c; + MNG_UINT_FRAM = $4652414d; + MNG_UINT_IDAT = $49444154; + MNG_UINT_IEND = $49454e44; + MNG_UINT_IHDR = $49484452; + MNG_UINT_IJNG = $494a4e47; + MNG_UINT_IPNG = $49504e47; + MNG_UINT_JDAT = $4a444154; + MNG_UINT_JHDR = $4a484452; + MNG_UINT_JSEP = $4a534550; + MNG_UINT_LOOP = $4c4f4f50; + MNG_UINT_MEND = $4d454e44; + MNG_UINT_MHDR = $4d484452; + MNG_UINT_MOVE = $4d4f5645; + MNG_UINT_ORDR = $4f524452; + MNG_UINT_PAST = $50415354; + MNG_UINT_PLTE = $504c5445; + MNG_UINT_PPLT = $50504c54; + MNG_UINT_PROM = $50524f4d; + MNG_UINT_SAVE = $53415645; + MNG_UINT_SEEK = $5345454b; + MNG_UINT_SHOW = $53484f57; + MNG_UINT_TERM = $5445524d; + MNG_UINT_bKGD = $624b4744; + MNG_UINT_cHRM = $6348524d; + MNG_UINT_eXPI = $65585049; + MNG_UINT_fPRI = $66505249; + MNG_UINT_gAMA = $67414d41; + MNG_UINT_hIST = $68495354; + MNG_UINT_iCCP = $69434350; + MNG_UINT_iTXt = $69545874; + MNG_UINT_nEED = $6e454544; + MNG_UINT_oFFs = $6f464673; + MNG_UINT_pCAL = $7043414c; + MNG_UINT_pHYg = $70444167; + MNG_UINT_pHYs = $70485973; + MNG_UINT_sBIT = $73424954; + MNG_UINT_sCAL = $7343414c; + MNG_UINT_sPLT = $73504c54; + MNG_UINT_sRGB = $73524742; + MNG_UINT_tEXt = $74455874; + MNG_UINT_tIME = $74494d45; + MNG_UINT_tRNS = $74524e53; + MNG_UINT_zTXt = $7a545874; + + MNG_UINT_evNT = $65764e54; + +{****************************************************************************} + +implementation + +{****************************************************************************} + +const mngdll = 'libmng.so'; + +{****************************************************************************} + +function mng_initialize; external mngdll; +function mng_reset; external mngdll; +function mng_cleanup; external mngdll; + +function mng_read; external mngdll; +function mng_read_resume; external mngdll; +function mng_write; external mngdll; +function mng_create; external mngdll; + +function mng_readdisplay; external mngdll; +function mng_display; external mngdll; +function mng_display_resume; external mngdll; +function mng_display_freeze; external mngdll; +function mng_display_reset; external mngdll; +function mng_display_goframe; external mngdll; +function mng_display_golayer; external mngdll; +function mng_display_gotime; external mngdll; + +function mng_trapevent; external mngdll; + +function mng_getlasterror; external mngdll; + +{****************************************************************************} + +function mng_setcb_memalloc; external mngdll; +function mng_setcb_memfree; external mngdll; + +function mng_setcb_openstream; external mngdll; +function mng_setcb_closestream; external mngdll; + +function mng_setcb_readdata; external mngdll; + +function mng_setcb_writedata; external mngdll; + +function mng_setcb_errorproc; external mngdll; +function mng_setcb_traceproc; external mngdll; + +function mng_setcb_processheader; external mngdll; +function mng_setcb_processtext; external mngdll; + +function mng_setcb_getcanvasline; external mngdll; +function mng_setcb_getalphaline; external mngdll; +function mng_setcb_getbkgdline; external mngdll; +function mng_setcb_refresh; external mngdll; + +function mng_setcb_gettickcount; external mngdll; +function mng_setcb_settimer; external mngdll; + +function mng_setcb_processgamma; external mngdll; +function mng_setcb_processchroma; external mngdll; +function mng_setcb_processsrgb; external mngdll; +function mng_setcb_processiccp; external mngdll; +function mng_setcb_processarow; external mngdll; + +{****************************************************************************} + +function mng_getcb_memalloc; external mngdll; +function mng_getcb_memfree; external mngdll; + +function mng_getcb_openstream; external mngdll; +function mng_getcb_closestream; external mngdll; + +function mng_getcb_readdata; external mngdll; + +function mng_getcb_writedata; external mngdll; + +function mng_getcb_errorproc; external mngdll; +function mng_getcb_traceproc; external mngdll; + +function mng_getcb_processheader; external mngdll; +function mng_getcb_processtext; external mngdll; + +function mng_getcb_getcanvasline; external mngdll; +function mng_getcb_getalphaline; external mngdll; +function mng_getcb_getbkgdline; external mngdll; +function mng_getcb_refresh; external mngdll; + +function mng_getcb_gettickcount; external mngdll; +function mng_getcb_settimer; external mngdll; + +function mng_getcb_processgamma; external mngdll; +function mng_getcb_processchroma; external mngdll; +function mng_getcb_processsrgb; external mngdll; +function mng_getcb_processiccp; external mngdll; +function mng_getcb_processarow; external mngdll; + +{****************************************************************************} + +function mng_set_userdata; external mngdll; + +function mng_set_canvasstyle; external mngdll; +function mng_set_bkgdstyle; external mngdll; + +function mng_set_bgcolor; external mngdll; +function mng_set_usebkgd; external mngdll; + +function mng_set_storechunks; external mngdll; +function mng_set_cacheplayback; external mngdll; + +// function mng_set_viewgamma; external mngdll; +// function mng_set_displaygamma; external mngdll; +// function mng_set_dfltimggamma; external mngdll; +function mng_set_viewgammaint; external mngdll; +function mng_set_displaygammaint; external mngdll; +function mng_set_dfltimggammaint; external mngdll; + +function mng_set_srgb; external mngdll; +function mng_set_outputprofile; external mngdll; +function mng_set_outputprofile2; external mngdll; +function mng_set_srgbprofile; external mngdll; +function mng_set_srgbprofile2; external mngdll; + +function mng_set_maxcanvaswidth; external mngdll; +function mng_set_maxcanvasheight; external mngdll; +function mng_set_maxcanvassize; external mngdll; + +function mng_set_suspensionmode; external mngdll; + +function mng_set_speed; external mngdll; + +{****************************************************************************} + +function mng_get_userdata; external mngdll; + +function mng_get_sigtype; external mngdll; +function mng_get_imagetype; external mngdll; +function mng_get_imagewidth; external mngdll; +function mng_get_imageheight; external mngdll; +function mng_get_ticks; external mngdll; +function mng_get_framecount; external mngdll; +function mng_get_layercount; external mngdll; +function mng_get_playtime; external mngdll; +function mng_get_simplicity; external mngdll; + +function mng_get_canvasstyle; external mngdll; +function mng_get_bkgdstyle; external mngdll; + +procedure mng_get_bgcolor; external mngdll; +function mng_get_usebkgd; external mngdll; + +function mng_get_storechunks; external mngdll; +function mng_get_cacheplayback; external mngdll; + +// function mng_get_viewgamma; external mngdll; +// function mng_get_displaygamma; external mngdll; +// function mng_get_dfltimggamma; external mngdll; +function mng_get_viewgammaint; external mngdll; +function mng_get_displaygammaint; external mngdll; +function mng_get_dfltimggammaint; external mngdll; + +function mng_get_srgb; external mngdll; + +function mng_get_maxcanvaswidth; external mngdll; +function mng_get_maxcanvasheight; external mngdll; + +function mng_get_suspensionmode; external mngdll; + +function mng_get_speed; external mngdll; +function mng_get_imagelevel; external mngdll; + +function mng_get_starttime; external mngdll; +function mng_get_runtime; external mngdll; +function mng_get_currentframe; external mngdll; +function mng_get_currentlayer; external mngdll; +function mng_get_currentplaytime; external mngdll; + +function mng_status_error; external mngdll; +function mng_status_reading; external mngdll; +function mng_status_suspendbreak; external mngdll; +function mng_status_creating; external mngdll; +function mng_status_writing; external mngdll; +function mng_status_displaying; external mngdll; +function mng_status_running; external mngdll; +function mng_status_timerbreak; external mngdll; +function mng_status_dynamic; external mngdll; + +{****************************************************************************} + +function mng_iterate_chunks; external mngdll; + +{****************************************************************************} + +function mng_getchunk_ihdr; external mngdll; +function mng_getchunk_plte; external mngdll; +function mng_getchunk_idat; external mngdll; +function mng_getchunk_trns; external mngdll; +function mng_getchunk_gama; external mngdll; +function mng_getchunk_chrm; external mngdll; +function mng_getchunk_srgb; external mngdll; +function mng_getchunk_iccp; external mngdll; +function mng_getchunk_text; external mngdll; +function mng_getchunk_ztxt; external mngdll; +function mng_getchunk_itxt; external mngdll; +function mng_getchunk_bkgd; external mngdll; +function mng_getchunk_phys; external mngdll; +function mng_getchunk_sbit; external mngdll; +function mng_getchunk_splt; external mngdll; +function mng_getchunk_hist; external mngdll; +function mng_getchunk_time; external mngdll; +function mng_getchunk_mhdr; external mngdll; +function mng_getchunk_loop; external mngdll; +function mng_getchunk_endl; external mngdll; +function mng_getchunk_defi; external mngdll; +function mng_getchunk_basi; external mngdll; +function mng_getchunk_clon; external mngdll; +function mng_getchunk_past; external mngdll; +function mng_getchunk_past_src; external mngdll; +function mng_getchunk_disc; external mngdll; +function mng_getchunk_back; external mngdll; +function mng_getchunk_fram; external mngdll; +function mng_getchunk_move; external mngdll; +function mng_getchunk_clip; external mngdll; +function mng_getchunk_show; external mngdll; +function mng_getchunk_term; external mngdll; +function mng_getchunk_save; external mngdll; +function mng_getchunk_save_entry; external mngdll; +function mng_getchunk_seek; external mngdll; +function mng_getchunk_expi; external mngdll; +function mng_getchunk_fpri; external mngdll; +function mng_getchunk_need; external mngdll; +function mng_getchunk_phyg; external mngdll; +function mng_getchunk_jhdr; external mngdll; +function mng_getchunk_jdat; external mngdll; +function mng_getchunk_dhdr; external mngdll; +function mng_getchunk_prom; external mngdll; +function mng_getchunk_pplt; external mngdll; +function mng_getchunk_pplt_entry; external mngdll; +function mng_getchunk_drop; external mngdll; +function mng_getchunk_dbyk; external mngdll; +function mng_getchunk_ordr; external mngdll; +function mng_getchunk_ordr_entry; external mngdll; +function mng_getchunk_unknown; external mngdll; + +{****************************************************************************} + +function mng_putchunk_ihdr; external mngdll; +function mng_putchunk_plte; external mngdll; +function mng_putchunk_idat; external mngdll; +function mng_putchunk_iend; external mngdll; +function mng_putchunk_trns; external mngdll; +function mng_putchunk_gama; external mngdll; +function mng_putchunk_chrm; external mngdll; +function mng_putchunk_srgb; external mngdll; +function mng_putchunk_iccp; external mngdll; +function mng_putchunk_text; external mngdll; +function mng_putchunk_ztxt; external mngdll; +function mng_putchunk_itxt; external mngdll; +function mng_putchunk_bkgd; external mngdll; +function mng_putchunk_phys; external mngdll; +function mng_putchunk_sbit; external mngdll; +function mng_putchunk_splt; external mngdll; +function mng_putchunk_hist; external mngdll; +function mng_putchunk_time; external mngdll; +function mng_putchunk_mhdr; external mngdll; +function mng_putchunk_mend; external mngdll; +function mng_putchunk_loop; external mngdll; +function mng_putchunk_endl; external mngdll; +function mng_putchunk_defi; external mngdll; +function mng_putchunk_basi; external mngdll; +function mng_putchunk_clon; external mngdll; +function mng_putchunk_past; external mngdll; +function mng_putchunk_past_src; external mngdll; +function mng_putchunk_disc; external mngdll; +function mng_putchunk_back; external mngdll; +function mng_putchunk_fram; external mngdll; +function mng_putchunk_move; external mngdll; +function mng_putchunk_clip; external mngdll; +function mng_putchunk_show; external mngdll; +function mng_putchunk_term; external mngdll; +function mng_putchunk_save; external mngdll; +function mng_putchunk_save_entry; external mngdll; +function mng_putchunk_seek; external mngdll; +function mng_putchunk_expi; external mngdll; +function mng_putchunk_fpri; external mngdll; +function mng_putchunk_need; external mngdll; +function mng_putchunk_phyg; external mngdll; +function mng_putchunk_jhdr; external mngdll; +function mng_putchunk_jdat; external mngdll; +function mng_putchunk_dhdr; external mngdll; +function mng_putchunk_prom; external mngdll; +function mng_putchunk_pplt; external mngdll; +function mng_putchunk_pplt_entry; external mngdll; +function mng_putchunk_drop; external mngdll; +function mng_putchunk_dbyk; external mngdll; +function mng_putchunk_ordr; external mngdll; +function mng_putchunk_ordr_entry; external mngdll; +function mng_putchunk_unknown; external mngdll; + +{****************************************************************************} + +function mng_updatemngheader; external mngdll; +function mng_updatemngsimplicity; external mngdll; + +{****************************************************************************} + +end. diff --git a/Engine/lib/lmng/contrib/kylix/mngview/Main.dfm b/Engine/lib/lmng/contrib/kylix/mngview/Main.dfm new file mode 100644 index 000000000..5d231e759 Binary files /dev/null and b/Engine/lib/lmng/contrib/kylix/mngview/Main.dfm differ diff --git a/Engine/lib/lmng/contrib/kylix/mngview/Main.pas b/Engine/lib/lmng/contrib/kylix/mngview/Main.pas new file mode 100644 index 000000000..0407b2408 --- /dev/null +++ b/Engine/lib/lmng/contrib/kylix/mngview/Main.pas @@ -0,0 +1,555 @@ +unit Main; + +interface + +uses + Qt, QExtCtrls, QDialogs, QMenus, QTypes, QGraphics, QControls, QForms, + SysUtils, Classes, QStdCtrls, IdGlobal, + libmng; + +{****************************************************************************} +{* For conditions of distribution and use, *} +{* see copyright notice in libmng.pas *} +{****************************************************************************} +{* *} +{* project : libmng *} +{* file : main.pas copyright (c) 2000-2002 G.Juyn *} +{* version : 1.0.5 *} +{* *} +{* purpose : Main form for mngview application *} +{* *} +{* author : G.Juyn *} +{* web : http://www.3-t.com *} +{* email : mailto:info@3-t.com *} +{* *} +{* comment : this is the heart of the mngview applciation *} +{* *} +{* changes : 1.0.5 - 09/21/2002 - G.Juyn *} +{* - modified for Kylix use *} +{* *} +{****************************************************************************} + +type + TMainForm = class(TForm) + + OFMainMenu: TMainMenu; + OFMenuFile: TMenuItem; + OFMenuFileOpen: TMenuItem; + OFMenuFileN1: TMenuItem; + OFMenuFileExit: TMenuItem; + + OFTimer: TTimer; + + OFOpenDialog: TOpenDialog; + OFPanel: TPanel; + OFImage: TImage; + + procedure FormCreate(Sender: TObject); + procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean); + procedure FormShow(Sender: TObject); + procedure FormResize(Sender: TObject); + procedure FormKeyDown(Sender: TObject; var Key: Word; + Shift: TShiftState); + procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, + Y: Integer); + + procedure OFImageMouseMove(Sender: TObject; Shift: TShiftState; X, + Y: Integer); + procedure OFImageMouseDown(Sender: TObject; Button: TMouseButton; + Shift: TShiftState; X, Y: Integer); + procedure OFImageMouseUp(Sender: TObject; Button: TMouseButton; + Shift: TShiftState; X, Y: Integer); + + procedure OFTimerTimer(Sender: TObject); + + procedure OFMenuFileOpenClick(Sender: TObject); + procedure OFMenuFileExitClick(Sender: TObject); + + private + { Private declarations } + + SFFileName : string; { filename of the input stream } + OFFile : TFileStream; { input stream } + IFHandle : mng_handle; { the libray handle } + OFBitmap : TBitmap; { drawing canvas } + BFCancelled : boolean; { or app-exit } + BFHasMouse : boolean; { mouse is/was over image } + + procedure MNGerror (SHMsg : string); + + public + { Public declarations } + + end; + +var + MainForm: TMainForm; + +{****************************************************************************} + +implementation + +{$R *.dfm} + +{****************************************************************************} + +{$F+} +function Memalloc (iLen : mng_uint32) : mng_ptr; cdecl; +{$F-} +begin + getmem (Result, iLen); { get memory from the heap } + fillchar (Result^, iLen, 0); { and initialize it } +end; + +{****************************************************************************} + +{$F+} +procedure Memfree (iPtr : mng_ptr; + iLen : mng_size_t); cdecl; +{$F-} +begin + freemem (iPtr, iLen); { free the memory } +end; + +{****************************************************************************} + +{$F+} +function Openstream (hHandle : mng_handle) : mng_bool; cdecl; +{$F-} + +var OHForm : TMainForm; + +begin { get a fix on our form } + OHForm := TMainForm (mng_get_userdata (hHandle)); + + with OHFORM do + begin + if OFFile <> nil then { free previous stream (if any) } + OFFile.Free; + { open a new stream } + OFFile := TFileStream.Create (SFFileName, fmOpenRead or fmShareDenyWrite); + end; + + Result := MNG_TRUE; +end; + +{****************************************************************************} + +{$F+} +function Closestream (hHandle : mng_handle) : mng_bool; cdecl; +{$F-} + +var OHForm : TMainForm; + +begin { get a fix on our form } + OHForm := TMainForm (mng_get_userdata (hHandle)); + + with OHFORM do + begin + OFFile.Free; { cleanup the stream } + OFFile := nil; { don't use it again ! } + end; + + Result := MNG_TRUE; +end; + +{****************************************************************************} + +{$F+} +function Readdata ( hHandle : mng_handle; + pBuf : mng_ptr; + iBuflen : mng_uint32; + var pRead : mng_uint32) : mng_bool; cdecl; +{$F-} + +var OHForm : TMainForm; + +begin + { get a fix on our form } + OHForm := TMainForm (mng_get_userdata (hHandle)); + + with OHForm do + begin { are we at EOF ? } + if OFFile.Position >= OFFile.Size then + begin + pRead := 0; { indicate so } + end + else + begin + { read the requested data } + pRead := OFFile.Read (pBuf^, iBuflen); + end; + end; + + Result := MNG_TRUE; +end; + +{****************************************************************************} + +{$F+} +function ProcessHeader (hHandle : mng_handle; + iWidth : mng_uint32; + iHeight : mng_uint32) : mng_bool; cdecl; +{$F-} + +var OHForm : TMainForm; + +begin { get a fix on our form } + OHForm := TMainForm (mng_get_userdata (hHandle)); + + with OHForm do + begin + OFBitmap.Width := iWidth; { store the new dimensions } + OFBitmap.Height := iHeight; + OFBitmap.PixelFormat := pf32bit; + + OFImage.Left := 0; { adjust the visible component } + OFImage.Top := 0; + OFImage.Width := iWidth; + OFImage.Height := iHeight; + + FormResize (OHForm); { force re-centering the image} + { clear the canvas & draw an outline } + OFBitmap.Canvas.Brush.Color := clGray; + OFBitmap.Canvas.Brush.Style := bsSolid; + OFBitmap.Canvas.FillRect (OFBitmap.Canvas.ClipRect); + + OFImage.Picture.Assign (OFBitmap); { make sure it gets out there } + { tell the library we want funny windows-bgr} + if mng_set_canvasstyle (hHandle, MNG_CANVAS_BGRX8) <> 0 then + MNGerror ('libmng reported an error setting the canvas style'); + + end; + + Result := MNG_TRUE; +end; + +{****************************************************************************} + +{$F+} +function GetCanvasLine (hHandle : mng_handle; + iLinenr : mng_uint32) : mng_ptr; cdecl; +{$F-} + +var OHForm : TMainForm; + +begin { get a fix on our form } + OHForm := TMainForm (mng_get_userdata (hHandle)); + { easy with these bitmap objects ! } + Result := TBitmap(OHForm.OFImage.Picture.Graphic).ScanLine [iLinenr]; +end; + +{****************************************************************************} + +{$F+} +function ImageRefresh (hHandle : mng_handle; + iX : mng_uint32; + iY : mng_uint32; + iWidth : mng_uint32; + iHeight : mng_uint32) : mng_bool; cdecl; +{$F-} + +var OHForm : TMainForm; + +begin { get a fix on our form } + OHForm := TMainForm (mng_get_userdata (hHandle)); + { force redraw } + OHForm.OFImage.Refresh; + + Result := MNG_TRUE; +end; + + +{****************************************************************************} + +{$F+} +function MyGetTickCount (hHandle : mng_handle) : mng_uint32; cdecl; +{$F-} +begin + Result := GetTickCount; { the system knows that } +end; + +{****************************************************************************} + +{$F+} +function SetTimer (hHandle : mng_handle; + iMsecs : mng_uint32) : mng_bool; cdecl; +{$F-} + +var OHForm : TMainForm; + +begin { get a fix on our form } + OHForm := TMainForm (mng_get_userdata (hHandle)); + OHForm.OFTimer.Interval := iMsecs; { and set the timer } + OHForm.OFTimer.Enabled := true; + + Result := MNG_TRUE; +end; + +{****************************************************************************} + +procedure TMainForm.FormCreate(Sender: TObject); + +var IHRed, IHGreen, IHBlue : word; + +begin { initialize } + OFBitmap := TBitmap.Create; + BFHasMouse := false; + OFFile := nil; + + OFOpenDialog.Initialdir := ''; + { now initialize the library } + IFHandle := mng_initialize (mng_ptr(self), Memalloc, Memfree, nil); + + if IFHandle = NIL then + begin + MNGerror ('libmng initialization error' + #13#10 + + 'Program aborted'); + Application.Terminate; + end; + { no need to store chunk-info ! } + mng_set_storechunks (IFHandle, MNG_FALSE); + { do not use suspension-buffer } + mng_set_suspensionmode (IFHandle, MNG_FALSE); + { set all the callbacks } + if (mng_setcb_openstream (IFHandle, Openstream ) <> MNG_NOERROR) or + (mng_setcb_closestream (IFHandle, Closestream ) <> MNG_NOERROR) or + (mng_setcb_readdata (IFHandle, Readdata ) <> MNG_NOERROR) or + (mng_setcb_processheader (IFHandle, ProcessHeader ) <> MNG_NOERROR) or + (mng_setcb_getcanvasline (IFHandle, GetCanvasLine ) <> MNG_NOERROR) or + (mng_setcb_refresh (IFHandle, ImageRefresh ) <> MNG_NOERROR) or + (mng_setcb_gettickcount (IFHandle, MyGetTickCount ) <> MNG_NOERROR) or + (mng_setcb_settimer (IFHandle, SetTimer ) <> MNG_NOERROR) then + begin + MNGerror ('libmng reported an error setting a callback function!' + #13#10 + + 'Program aborted'); + Application.Terminate; + end; + + IHRed := (Color ) and $FF; { supply our own bg-color } + IHGreen := (Color shr 8) and $FF; + IHBlue := (Color shr 16) and $FF; + + IHRed := (IHRed shl 8) + IHRed; + IHGreen := (IHGreen shl 8) + IHGreen; + IHBlue := (IHBlue shl 8) + IHBlue; + + if mng_set_bgcolor (IFHandle, IHRed, IHGreen, IHBlue) <> MNG_NOERROR then + MNGerror ('libmng reported an error setting the background color!'); + +end; + +{****************************************************************************} + +procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean); +begin + BFCancelled := true; + + if OFTimer.Enabled then { if we're still animating then stop it } + begin + OFTimer.Enabled := false; + + Application.ProcessMessages; + + if mng_reset (IFHandle) <> MNG_NOERROR then + MNGerror ('libmng reported an error during reset!'); + + end; + + mng_cleanup (IFHandle); +end; + +{****************************************************************************} + +procedure TMainForm.FormShow(Sender: TObject); +begin + FormResize (self); +end; + +{****************************************************************************} + +procedure TMainForm.FormResize(Sender: TObject); +begin { center the image in the window } + if ClientWidth < OFImage.Width then + OFImage.Left := 0 + else + OFImage.Left := (ClientWidth - OFImage.Width ) div 2; + + if ClientHeight < OFImage.Height then + OFImage.Top := 0 + else + OFImage.Top := (ClientHeight - OFImage.Height) div 2; + +end; + +{****************************************************************************} + +procedure TMainForm.FormKeyDown(Sender: TObject; var Key: Word; + Shift: TShiftState); +begin + if Key = Key_Escape then { pressing will freeze an animation } + begin + if OFTimer.Enabled then + if mng_display_freeze (IFHandle) <> MNG_NOERROR then + MNGerror ('libmng reported an error during display_freeze!'); + + OFTimer.Enabled := false; { don't let that timer go off then ! } + BFCancelled := true; + end; +end; + +{****************************************************************************} + +procedure TMainForm.FormMouseMove(Sender: TObject; Shift: TShiftState; X, + Y: Integer); +begin + if mng_status_dynamic (IFHandle) then + begin + if BFHasMouse then { if we had the mouse, it's left ! } + begin + if mng_trapevent (IFHandle, 3, 0, 0) <> MNG_NOERROR then + MNGerror ('libmng reported an error during trapevent!'); + + BFHasMouse := false; + end; + end; +end; + +{****************************************************************************} + +procedure TMainForm.OFImageMouseMove(Sender: TObject; Shift: TShiftState; + X, Y: Integer); +begin + if mng_status_dynamic (IFHandle) then + begin + if BFHasMouse then { did we have the mouse already ? } + begin + if mng_trapevent (IFHandle, 2, X, Y) <> MNG_NOERROR then + MNGerror ('libmng reported an error during trapevent!'); + end + else + begin { if not, it has entered ! } + if mng_trapevent (IFHandle, 1, X, Y) <> MNG_NOERROR then + MNGerror ('libmng reported an error during trapevent!'); + + BFHasMouse := true; + end; + end; +end; + +{****************************************************************************} + +procedure TMainForm.OFImageMouseDown(Sender: TObject; Button: TMouseButton; + Shift: TShiftState; X, Y: Integer); +begin + if mng_status_dynamic (IFHandle) then + if mng_trapevent (IFHandle, 4, X, Y) <> MNG_NOERROR then + MNGerror ('libmng reported an error during trapevent!'); +end; + +{****************************************************************************} + +procedure TMainForm.OFImageMouseUp(Sender: TObject; Button: TMouseButton; + Shift: TShiftState; X, Y: Integer); +begin + if mng_status_dynamic (IFHandle) then + if mng_trapevent (IFHandle, 5, X, Y) <> MNG_NOERROR then + MNGerror ('libmng reported an error during trapevent!'); +end; + +{****************************************************************************} + +procedure TMainForm.OFTimerTimer(Sender: TObject); + +var IHRslt : mng_retcode; + +begin + OFTimer.Enabled := false; { only once ! } + + if not BFCancelled then + begin { and inform the library } + IHRslt := mng_display_resume (IFHandle); + + if (IHRslt <> MNG_NOERROR) and (IHRslt <> MNG_NEEDTIMERWAIT) then + MNGerror ('libmng reported an error during display_resume!'); + + end; +end; + +{****************************************************************************} + +procedure TMainForm.OFMenuFileOpenClick(Sender: TObject); + +var IHRslt : mng_retcode; + +begin + OFOpenDialog.InitialDir := ''; + OFOpenDialog.FileName := SFFileName; + + if OFOpenDialog.Execute then { get the filename } + begin + if OFTimer.Enabled then { if the lib was active; stop it } + begin + OFTimer.Enabled := false; + + Application.ProcessMessages; { process any timer requests (for safety) } + { now freeze the animation } + if mng_reset (IFHandle) <> MNG_NOERROR then + MNGerror ('libmng reported an error during reset!'); + end; + { save interesting fields } + SFFileName := OFOpenDialog.FileName; + BFCancelled := false; + + OFImage.Picture.Graphic := nil; { clear the output-canvas } + OFImage.Refresh; + { always reset (just in case) } + if mng_reset (IFHandle) <> MNG_NOERROR then + MNGerror ('libmng reported an error during reset!') + else + begin { and let the lib do it's job ! } + IHRslt := mng_readdisplay (IFHandle); + + if (IHRslt <> MNG_NOERROR) and (IHRSLT <> MNG_NEEDTIMERWAIT) then + MNGerror ('libmng reported an error reading the input file!'); + + end; + end; +end; + +{****************************************************************************} + +procedure TMainForm.OFMenuFileExitClick(Sender: TObject); +begin + if mng_cleanup (IFHandle) <> MNG_NOERROR then + MNGerror ('libmng cleanup error'); + + Close; +end; + +{****************************************************************************} + +procedure TMainForm.MNGerror; + +var iErrorcode : mng_uint32; + iSeverity : mng_uint8; + iChunkname : mng_chunkid; + iChunkseq : mng_uint32; + iExtra1 : mng_int32; + iExtra2 : mng_int32; + zErrortext : mng_pchar; + +begin { get extended info } + iErrorcode := mng_getlasterror (IFHandle, iSeverity, iChunkname, iChunkseq, + iExtra1, iExtra2, zErrortext); + + MessageDlg (SHMsg + #13#10#13#10 + strpas (zErrortext) + #13#10#13#10 + + Format ('Error = %d; Severity = %d; Chunknr = %d; Extra1 = %d', + [iErrorcode, iSeverity, iChunkseq, iExtra1]), + mtError, [mbOK], 0); + Application.Terminate; +end; + +{****************************************************************************} + +end. diff --git a/Engine/lib/lmng/contrib/kylix/mngview/mngview b/Engine/lib/lmng/contrib/kylix/mngview/mngview new file mode 100644 index 000000000..bd29748bc --- /dev/null +++ b/Engine/lib/lmng/contrib/kylix/mngview/mngview @@ -0,0 +1,17 @@ +#!/bin/bash +# +app_install_dir=/data/Triple-T/Software/mnglib3t/libmng-devel/contrib/kylix/mngview +app_path=$app_install_dir/mngview.bin +app_ld_path=$app_install_dir/lib +# +if [ -n "$LD_LIBRARY_PATH" ]; then + export LD_LIBRARY_PATH="$app_ld_path:$LD_LIBRARY_PATH" +else + export LD_LIBRARY_PATH="$app_ld_path" +fi +# +if [ -z "$LANG" ]; then + export LANG="en_US" +fi +# +exec $app_path $* diff --git a/Engine/lib/lmng/contrib/kylix/mngview/mngview.conf b/Engine/lib/lmng/contrib/kylix/mngview/mngview.conf new file mode 100644 index 000000000..f2aed0130 --- /dev/null +++ b/Engine/lib/lmng/contrib/kylix/mngview/mngview.conf @@ -0,0 +1,33 @@ +-$A8 +-$B- +-$C+ +-$D+ +-$E- +-$F- +-$G+ +-$H+ +-$I+ +-$J- +-$K- +-$L+ +-$M- +-$N+ +-$O+ +-$P+ +-$Q- +-$R- +-$S- +-$T- +-$U- +-$V+ +-$W- +-$X+ +-$YD +-$Z1 +-cg +-H+ +-W+ +-M +-$M16384,1048576 +-K$00400000 +-DCLX_USE_LIBQT diff --git a/Engine/lib/lmng/contrib/kylix/mngview/mngview.dpr b/Engine/lib/lmng/contrib/kylix/mngview/mngview.dpr new file mode 100644 index 000000000..d16a2ce38 --- /dev/null +++ b/Engine/lib/lmng/contrib/kylix/mngview/mngview.dpr @@ -0,0 +1,17 @@ +program mngview; + +uses + QForms, + Main in 'Main.pas' {MainForm}, + libmng in '../libmng.pas'; + +{$E .bin} + +{$R *.res} + +begin + Application.Initialize; + Application.Title := 'mngview - libmng test-viewer in Kylix'; + Application.CreateForm(TMainForm, MainForm); + Application.Run; +end. diff --git a/Engine/lib/lmng/contrib/kylix/mngview/mngview.kof b/Engine/lib/lmng/contrib/kylix/mngview/mngview.kof new file mode 100644 index 000000000..70dc3abfc --- /dev/null +++ b/Engine/lib/lmng/contrib/kylix/mngview/mngview.kof @@ -0,0 +1,61 @@ +[Compiler] +A=8 +B=0 +C=1 +D=1 +E=0 +F=0 +G=1 +H=1 +I=1 +J=0 +K=0 +L=1 +M=0 +N=1 +O=1 +P=1 +Q=0 +R=0 +S=0 +T=0 +U=0 +V=1 +W=0 +X=1 +Y=1 +Z=1 +ShowHints=1 +ShowWarnings=1 +UnitAliases= + +[Linker] +MapFile=0 +OutputObjs=0 +ConsoleApp=1 +DebugInfo=0 +RemoteSymbols=0 +MinStackSize=16384 +MaxStackSize=1048576 +ImageBase=4194304 +ExeDescription= +DynamicLoader=/lib/ld-linux.so.2 + +[Directories] +OutputDir= +UnitOutputDir= +PackageDLLOutputDir= +PackageDCPOutputDir= +SearchPath= +Packages=baseclx:visualclx:dataclx:visualdbclx:netclx:netdataclx:xmlrtl:indy:webdsnapclx:websnapclx +Conditionals=CLX_USE_LIBQT +DebugSourceDirs= +UsePackages=0 + +[Parameters] +RunParams= +HostApplication= +Launcher=/usr/X11R6/bin/xterm -T KylixDebuggerOutput -e bash -i -c %debuggee% +UseLauncher=0 +DebugCWD= + diff --git a/Engine/lib/lmng/contrib/kylix/mngview/mngview.res b/Engine/lib/lmng/contrib/kylix/mngview/mngview.res new file mode 100644 index 000000000..fec7f73ca Binary files /dev/null and b/Engine/lib/lmng/contrib/kylix/mngview/mngview.res differ diff --git a/Engine/lib/lmng/contrib/msvc/libmng-msvc.lib/README.txt b/Engine/lib/lmng/contrib/msvc/libmng-msvc.lib/README.txt new file mode 100644 index 000000000..23b9f1d5a --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/libmng-msvc.lib/README.txt @@ -0,0 +1,26 @@ +.lib & .def files to link an MSVC project against the standard DLL build with BCB. + +Please note that this version is created for libmng 1.0.5, but should work with +1.0.6 as there were no API changes between the two versions. + +These files are courtesy of Alex Volkov. Here's what he had to say about it: + +------------------------------------------------------------------------------- + +That reminds me, I remade the MSVC libmng.lib for linking to the +bcb-generated dll. The .lib and its corresponding .def are attached. The +.def I generated by simply dumping all the exports from the bcb .dll and +later added the .def to the contrib/msvc/win32dll project. The .def is +necessary for MSVC to name the imports correctly, otherwise it produces +mangled symbol names. What is ironic is that it is not necessary to +completely build the dll with msvc -- you need just the resulting .lib -- +but you still have to setup the entire project for building the dll because of +the stdcall calling convention. The resulting .lib is not perfect -- the hints +are all off, of course -- but it does the job. In any case, the win32 exes +linked with the attached .lib in fact work with bcb-generated dll just fine. I +know the building process can be somehow automated, but I have not had the time +to work on that, and for me it was a one-shot deal. + +Alex. + +------------------------------------------------------------------------------- diff --git a/Engine/lib/lmng/contrib/msvc/libmng-msvc.lib/libmng.def b/Engine/lib/lmng/contrib/msvc/libmng-msvc.lib/libmng.def new file mode 100644 index 000000000..5f4f18758 --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/libmng-msvc.lib/libmng.def @@ -0,0 +1,380 @@ +LIBRARY libmng +EXPORTS + jcopy_block_row + jcopy_sample_rows + jdiv_round_up + jinit_1pass_quantizer + jinit_2pass_quantizer + jinit_c_coef_controller + jinit_c_main_controller + jinit_c_master_control + jinit_c_prep_controller + jinit_color_converter + jinit_color_deconverter + jinit_compress_master + jinit_d_coef_controller + jinit_d_main_controller + jinit_d_post_controller + jinit_downsampler + jinit_forward_dct + jinit_huff_decoder + jinit_huff_encoder + jinit_input_controller + jinit_inverse_dct + jinit_marker_reader + jinit_marker_writer + jinit_master_decompress + jinit_memory_mgr + jinit_merged_upsampler + jinit_phuff_decoder + jinit_phuff_encoder + jinit_upsampler + jpeg_CreateCompress + jpeg_CreateDecompress + jpeg_abort + jpeg_abort_compress + jpeg_abort_decompress + jpeg_add_quant_table + jpeg_alloc_huff_table + jpeg_alloc_quant_table + jpeg_calc_output_dimensions + jpeg_consume_input + jpeg_copy_critical_parameters + jpeg_default_colorspace + jpeg_destroy + jpeg_destroy_compress + jpeg_destroy_decompress + jpeg_fdct_float + jpeg_fdct_ifast + jpeg_fdct_islow + jpeg_fill_bit_buffer + jpeg_finish_compress + jpeg_finish_decompress + jpeg_finish_output + jpeg_free_large + jpeg_free_small + jpeg_gen_optimal_table + jpeg_get_large + jpeg_get_small + jpeg_has_multiple_scans + jpeg_huff_decode + jpeg_save_markers + jpeg_set_colorspace + jpeg_set_defaults + jpeg_set_linear_quality + jpeg_set_marker_processor + jpeg_set_quality + jpeg_simple_progression + jpeg_start_compress + jpeg_start_decompress + jpeg_start_output + jpeg_std_error + jpeg_stdio_dest + jpeg_stdio_src + jpeg_suppress_tables + jpeg_write_coefficients + jpeg_write_m_byte + jpeg_write_m_header + jpeg_write_marker + jpeg_write_raw_data + jpeg_write_scanlines + jpeg_write_tables + jround_up + jzero_far + mng_cleanup + mng_copy_chunk + mng_create + mng_display + mng_display_freeze + mng_display_goframe + mng_display_golayer + mng_display_gotime + mng_display_reset + mng_display_resume + mng_get_alphabitdepth + mng_get_alphacompression + mng_get_alphadepth + mng_get_alphafilter + mng_get_alphainterlace + mng_get_bgcolor + mng_get_bitdepth + mng_get_bkgdstyle + mng_get_cacheplayback + mng_get_canvasstyle + mng_get_colortype + mng_get_compression + mng_get_currentframe + mng_get_currentlayer + mng_get_currentplaytime + mng_get_dfltimggamma + mng_get_dfltimggammaint + mng_get_displaygamma + mng_get_displaygammaint + mng_get_doprogressive + mng_get_filter + mng_get_framecount + mng_get_imageheight + mng_get_imagelevel + mng_get_imagetype + mng_get_imagewidth + mng_get_interlace + mng_get_jpeg_dctmethod + mng_get_jpeg_maxjdat + mng_get_jpeg_optimized + mng_get_jpeg_progressive + mng_get_jpeg_quality + mng_get_jpeg_smoothing + mng_get_lastbackchunk + mng_get_lastseekname + mng_get_layercount + mng_get_maxcanvasheight + mng_get_maxcanvaswidth + mng_get_playtime + mng_get_refreshpass + mng_get_runtime + mng_get_sectionbreaks + mng_get_sigtype + mng_get_simplicity + mng_get_speed + mng_get_srgb + mng_get_starttime + mng_get_storechunks + mng_get_suspensionmode + mng_get_ticks + mng_get_totalframes + mng_get_totallayers + mng_get_totalplaytime + mng_get_usebkgd + mng_get_userdata + mng_get_viewgamma + mng_get_viewgammaint + mng_get_zlib_level + mng_get_zlib_maxidat + mng_get_zlib_memlevel + mng_get_zlib_method + mng_get_zlib_strategy + mng_get_zlib_windowbits + mng_getcb_closestream + mng_getcb_errorproc + mng_getcb_getalphaline + mng_getcb_getbkgdline + mng_getcb_getcanvasline + mng_getcb_gettickcount + mng_getcb_memalloc + mng_getcb_memfree + mng_getcb_openstream + mng_getcb_processheader + mng_getcb_processneed + mng_getcb_processsave + mng_getcb_processseek + mng_getcb_processterm + mng_getcb_processtext + mng_getcb_processunknown + mng_getcb_readdata + mng_getcb_refresh + mng_getcb_settimer + mng_getcb_writedata + mng_getchunk_back + mng_getchunk_basi + mng_getchunk_bkgd + mng_getchunk_chrm + mng_getchunk_clip + mng_getchunk_clon + mng_getchunk_dbyk + mng_getchunk_defi + mng_getchunk_dhdr + mng_getchunk_disc + mng_getchunk_drop + mng_getchunk_endl + mng_getchunk_evnt + mng_getchunk_evnt_entry + mng_getchunk_expi + mng_getchunk_fpri + mng_getchunk_fram + mng_getchunk_gama + mng_getchunk_hist + mng_getchunk_iccp + mng_getchunk_idat + mng_getchunk_ihdr + mng_getchunk_itxt + mng_getchunk_jdat + mng_getchunk_jhdr + mng_getchunk_loop + mng_getchunk_magn + mng_getchunk_mhdr + mng_getchunk_move + mng_getchunk_need + mng_getchunk_ordr + mng_getchunk_ordr_entry + mng_getchunk_past + mng_getchunk_past_src + mng_getchunk_phyg + mng_getchunk_phys + mng_getchunk_plte + mng_getchunk_pplt + mng_getchunk_pplt_entry + mng_getchunk_prom + mng_getchunk_save + mng_getchunk_save_entry + mng_getchunk_sbit + mng_getchunk_seek + mng_getchunk_show + mng_getchunk_splt + mng_getchunk_srgb + mng_getchunk_term + mng_getchunk_text + mng_getchunk_time + mng_getchunk_trns + mng_getchunk_unknown + mng_getchunk_ztxt + mng_getimgdata_chunk + mng_getimgdata_chunkseq + mng_getimgdata_seq + mng_getlasterror + mng_initialize + mng_iterate_chunks + mng_putchunk_back + mng_putchunk_basi + mng_putchunk_bkgd + mng_putchunk_chrm + mng_putchunk_clip + mng_putchunk_clon + mng_putchunk_dbyk + mng_putchunk_defi + mng_putchunk_dhdr + mng_putchunk_disc + mng_putchunk_drop + mng_putchunk_endl + mng_putchunk_evnt + mng_putchunk_evnt_entry + mng_putchunk_expi + mng_putchunk_fpri + mng_putchunk_fram + mng_putchunk_gama + mng_putchunk_hist + mng_putchunk_iccp + mng_putchunk_idat + mng_putchunk_iend + mng_putchunk_ihdr + mng_putchunk_ipng + mng_putchunk_itxt + mng_putchunk_jdat + mng_putchunk_jhdr + mng_putchunk_jsep + mng_putchunk_loop + mng_putchunk_magn + mng_putchunk_mend + mng_putchunk_mhdr + mng_putchunk_move + mng_putchunk_need + mng_putchunk_ordr + mng_putchunk_ordr_entry + mng_putchunk_past + mng_putchunk_past_src + mng_putchunk_phyg + mng_putchunk_phys + mng_putchunk_plte + mng_putchunk_pplt + mng_putchunk_pplt_entry + mng_putchunk_prom + mng_putchunk_save + mng_putchunk_save_entry + mng_putchunk_sbit + mng_putchunk_seek + mng_putchunk_show + mng_putchunk_splt + mng_putchunk_srgb + mng_putchunk_term + mng_putchunk_text + mng_putchunk_time + mng_putchunk_trns + mng_putchunk_unknown + mng_putchunk_ztxt + mng_putimgdata_ihdr + mng_putimgdata_jhdr + mng_read + mng_read_resume + mng_readdisplay + mng_reset + mng_set_bgcolor + mng_set_bkgdstyle + mng_set_cacheplayback + mng_set_canvasstyle + mng_set_dfltimggamma + mng_set_dfltimggammaint + mng_set_displaygamma + mng_set_displaygammaint + mng_set_doprogressive + mng_set_jpeg_dctmethod + mng_set_jpeg_maxjdat + mng_set_jpeg_optimized + mng_set_jpeg_progressive + mng_set_jpeg_quality + mng_set_jpeg_smoothing + mng_set_maxcanvasheight + mng_set_maxcanvassize + mng_set_maxcanvaswidth + mng_set_outputprofile + mng_set_outputprofile2 + mng_set_outputsrgb + mng_set_sectionbreaks + mng_set_speed + mng_set_srgb + mng_set_srgbimplicit + mng_set_srgbprofile + mng_set_srgbprofile2 + mng_set_storechunks + mng_set_suspensionmode + mng_set_usebkgd + mng_set_userdata + mng_set_viewgamma + mng_set_viewgammaint + mng_set_zlib_level + mng_set_zlib_maxidat + mng_set_zlib_memlevel + mng_set_zlib_method + mng_set_zlib_strategy + mng_set_zlib_windowbits + mng_setcb_closestream + mng_setcb_errorproc + mng_setcb_getalphaline + mng_setcb_getbkgdline + mng_setcb_getcanvasline + mng_setcb_gettickcount + mng_setcb_memalloc + mng_setcb_memfree + mng_setcb_openstream + mng_setcb_processheader + mng_setcb_processmend + mng_setcb_processneed + mng_setcb_processsave + mng_setcb_processseek + mng_setcb_processterm + mng_setcb_processtext + mng_setcb_processunknown + mng_setcb_readdata + mng_setcb_refresh + mng_setcb_settimer + mng_setcb_writedata + mng_status_creating + mng_status_displaying + mng_status_dynamic + mng_status_error + mng_status_reading + mng_status_running + mng_status_runningevent + mng_status_suspendbreak + mng_status_timerbreak + mng_status_writing + mng_supports_func + mng_trapevent + mng_updatemngheader + mng_updatemngsimplicity + mng_version_beta + mng_version_dll + mng_version_major + mng_version_minor + mng_version_release + mng_version_so + mng_version_text + mng_write diff --git a/Engine/lib/lmng/contrib/msvc/libmng-msvc.lib/libmng.lib b/Engine/lib/lmng/contrib/msvc/libmng-msvc.lib/libmng.lib new file mode 100644 index 000000000..375db10d5 Binary files /dev/null and b/Engine/lib/lmng/contrib/msvc/libmng-msvc.lib/libmng.lib differ diff --git a/Engine/lib/lmng/contrib/msvc/makemng/Makefile b/Engine/lib/lmng/contrib/msvc/makemng/Makefile new file mode 100644 index 000000000..b5c31b5f5 --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/makemng/Makefile @@ -0,0 +1,37 @@ +######################################################################## +# This is a GNU makefile - tested on CYGWIN and Linux +# +# You may need to compile or install libmng before compiling this. +# Libmng also requires zlib, jpeg and lcms +# + +TARGET = makemng +# These flags are needed to get it to compile with libmng.dll on CYGWIN +# CYGWINFLAGS = -DMNG_USE_DLL +CYGWINFLAGS = +MNGFLAGS = -I. -DMNG_SKIP_LCMS -DMNG_SKIP_IJG6B \ + -DMNG_SUPPORT_READ -DMNG_SUPPORT_WRITE -DMNG_SUPPORT_DISPLAY \ + -DMNG_ACCESS_CHUNKS + +CC = gcc +CFLAGS = -W -Wall -O0 # -g +LIBS = -L/usr/local/lib -lmng -lm -lz -ljpeg -llcms +LDFLAGS= + +BINS = $(TARGET) $(TARGET).exe +SRCS = makemng.c filelist.c +OBJS = makemng.o filelist.o + +.SUFFIXES: .c .o + +.c.o: + $(CC) -c $(CFLAGS) -o $@ $*.c $(MNGFLAGS) $(CYGWINFLAGS) + +all: $(TARGET) + +$(TARGET): $(OBJS) + $(CC) -o $(TARGET) $(OBJS) $(LIBS) $(LDFLAGS) + +clean: + rm -f $(BINS) *.o + diff --git a/Engine/lib/lmng/contrib/msvc/makemng/README b/Engine/lib/lmng/contrib/msvc/makemng/README new file mode 100644 index 000000000..dbd26ea89 --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/makemng/README @@ -0,0 +1,24 @@ +MAKEMNG - Jan 10, 2003 + +A simple MNG encoder. MAKEMNG takes a bunch of still frames (PNGs) +as input, computes delta images and stuffs it all into an MNG, thus +creating an animation. + +You will need zlib to compile and run this app. +The included Makefile should work on Linux and CYGWIN. +The MSVC project will most likely need modifications for your +environment. + +usage: makemng [-v] [-f rate] [-r] [-s size] [-o outputfile] +produces an MNG animation from a bunch of frame images +options: + -v : be verbose, explains things no human should know + -f rate : sets the framerate; rate is 1..100 per second (default 5) + -b : auto-select background frame (instead of frame0) + -r : split delta frames into full rectangles only + -s size : enable sector cleanup and set sector size (8..64) +diagnostical options: + -d : generate delta-mask PNGs (form: mask_FRM1_FRM2.png) + +------------- +Alex Volkov diff --git a/Engine/lib/lmng/contrib/msvc/makemng/filelist.c b/Engine/lib/lmng/contrib/msvc/makemng/filelist.c new file mode 100644 index 000000000..852d018f6 --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/makemng/filelist.c @@ -0,0 +1,276 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* This file contains two versions of make_file_list(); one for Windows, and + * one for other systems (POSIX). + */ +#ifdef WIN32 +#include +#include +#include +#include + +char** +make_file_list (const char* pattern, int* pnum_entries) +{ + int num_entries, length; + char** StringTable; + char** lpLastOffs; + + num_entries = 0; + StringTable = 0; + do + { + int slen; + long handle; + struct _finddata_t f; + + if (num_entries == 0) + length = 0; + else + { + slen = (num_entries + 1) * sizeof (char*); + StringTable = (char**) malloc (slen + length); + if (StringTable == 0) + break; + + lpLastOffs = StringTable; + *lpLastOffs = (char*)StringTable + slen; + + num_entries = 0; + length = slen; + } + + handle = _findfirst (pattern, &f); + if (handle != -1) + { + do + { + if (f.attrib & (_A_HIDDEN | _A_SUBDIR | _A_SYSTEM)) + continue; + + slen = strlen (f.name) + 1; + length += slen; + + if (StringTable) + { + char *lpStr; + char **lpLo, **lpHi; + + lpLo = StringTable; + lpHi = lpLastOffs - 1; + while (lpLo <= lpHi) + { + char c1, c2; + char *pStr; + int LocLen; + char **lpMid; + + lpMid = lpLo + ((lpHi - lpLo) >> 1); + + LocLen = lpMid[1] - lpMid[0]; + if (LocLen > slen) + LocLen = slen; + + lpStr = lpMid[0]; + pStr = f.name; + while (LocLen-- + && (c1 = toupper (*lpStr++)) + == (c2 = toupper (*pStr++))) + ; + + if (c1 <= c2) + lpLo = lpMid + 1; + else + lpHi = lpMid - 1; + } + + lpStr = lpLo[0]; + memmove (lpStr + slen, lpStr, lpLastOffs[0] - lpLo[0]); + strcpy (lpStr, f.name); + + for (lpHi = lpLastOffs++; lpHi >= lpLo; --lpHi) + lpHi[1] = lpHi[0] + slen; + } + + ++num_entries; + + } while (_findnext (handle, &f) == 0); + + _findclose (handle); + } + } while (num_entries && StringTable == 0); + + if (StringTable == 0) + *pnum_entries = 0; + else + *pnum_entries = num_entries; + + return StringTable; +} + +#else /* ! defined(WIN32) */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char** +make_file_list (const char* pattern, int* pnum_entries) +{ + size_t num_entries, length; + char** StringTable; + char** lpLastOffs; + char* slash; // Pointer inside pattern to the last / + char path[PATH_MAX]; // buffer for a filename with path + char* file; // Pointer inside path to the filename + size_t pathlen; // length of path, excluding last / and filename + + slash = (char*) strrchr ((const char *) pattern, '/'); + if (slash == NULL) + { + pathlen = 1; + path[0] = '.'; + } + else + { + pathlen = slash - pattern; + memcpy (path, pattern, pathlen); + pattern = slash + 1; + } + file = path + pathlen + 1; + + num_entries = 0; + StringTable = 0; + do + { + int slen; + DIR *handle; + + if (num_entries == 0) + length = 0; + else + { + slen = (num_entries + 1) * sizeof (char*); + StringTable = (char**) malloc (slen + length); + if (StringTable == 0) + break; + + lpLastOffs = StringTable; + *lpLastOffs = (char*)StringTable + slen; + + num_entries = 0; + length = slen; + } + + path[pathlen] = '\0'; // strip any file part + handle = opendir((const char *) path); + if (handle != NULL) + { + path[pathlen] = '/'; + // change the 0 char to a slash; a filename will be + // attached here. + while (1) + { + struct dirent *de; + struct stat sb; + + de = readdir(handle); + if (de == NULL) + break; + if (de->d_name[0] == '.') + continue; + strcpy (file, de->d_name); + // attach the filename to path + if (stat(path, &sb) == -1) + continue; + if (!S_ISREG(sb.st_mode)) + continue; + if (fnmatch(pattern, de->d_name, 0) != 0) + continue; + + slen = strlen (de->d_name) + 1; + length += slen; + + if (StringTable) + { + char *lpStr; + char **lpLo, **lpHi; + + lpLo = StringTable; + lpHi = lpLastOffs - 1; + while (lpLo <= lpHi) + { + char c1, c2; + char *pStr; + int LocLen; + char **lpMid; + + lpMid = lpLo + ((lpHi - lpLo) >> 1); + + LocLen = lpMid[1] - lpMid[0]; + if (LocLen > slen) + LocLen = slen; + + lpStr = lpMid[0]; + pStr = de->d_name; + while (LocLen-- + && (c1 = toupper (*lpStr++)) + == (c2 = toupper (*pStr++))) + ; + + if (c1 <= c2) + lpLo = lpMid + 1; + else + lpHi = lpMid - 1; + } + + lpStr = lpLo[0]; + memmove (lpStr + slen, lpStr, lpLastOffs[0] - lpLo[0]); + strcpy (lpStr, de->d_name); + + for (lpHi = lpLastOffs++; lpHi >= lpLo; --lpHi) + lpHi[1] = lpHi[0] + slen; + } + + ++num_entries; + } + closedir(handle); + } + } while (num_entries && StringTable == 0); + + if (StringTable == 0) + *pnum_entries = 0; + else + *pnum_entries = num_entries; + + return StringTable; +} + +#endif + +void +free_file_list (char** list) +{ + if (list) + free(list); +} diff --git a/Engine/lib/lmng/contrib/msvc/makemng/getopt/getopt.c b/Engine/lib/lmng/contrib/msvc/makemng/getopt/getopt.c new file mode 100644 index 000000000..08d13d4aa --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/makemng/getopt/getopt.c @@ -0,0 +1,1060 @@ +/* Getopt for GNU. + NOTE: getopt is now part of the C library, so if you don't know what + "Keep this file name-space clean" means, talk to drepper@gnu.org + before changing it! + Copyright (C) 1987,88,89,90,91,92,93,94,95,96,98,99,2000,2001 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* This tells Alpha OSF/1 not to define a getopt prototype in . + Ditto for AIX 3.2 and . */ +#ifndef _NO_PROTO +# define _NO_PROTO +#endif + +#include + +#ifndef HAVE_GETOPT_H +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +# ifndef const +# define const +# endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +# include +# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +# define ELIDE_CODE +# endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +/* Don't include stdlib.h for non-GNU C libraries because some of them + contain conflicting prototypes for getopt. */ +# include +# include +#endif /* GNU C library. */ + +#ifdef VMS +# include +# if HAVE_STRING_H - 0 +# include +# endif +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. */ +# if defined HAVE_LIBINTL_H || defined _LIBC +# include +# ifndef _ +# define _(msgid) gettext (msgid) +# endif +# else +# define _(msgid) (msgid) +# endif +#endif + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. + + Setting the environment variable POSIXLY_CORRECT disables permutation. + Then the behavior is completely standard. + + GNU application programs can use a third alternative mode in which + they can distinguish the relative order of options and other arguments. */ + +#include "getopt.h" + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +/* 1003.2 says this must be 1 before any call. */ +int optind = 1; + +/* Formerly, initialization of getopt depended on optind==0, which + causes problems with re-calling getopt as programs generally don't + know that. */ + +int __getopt_initialized; + +/* The next char to be scanned in the option-element + in which the last option character we returned was found. + This allows us to pick up the scan where we left off. + + If this is zero, or a null string, it means resume the scan + by advancing to the next ARGV-element. */ + +static char *nextchar; + +/* Callers store zero here to inhibit the error message + for unrecognized options. */ + +int opterr = 1; + +/* Set to an option character which was unrecognized. + This must be initialized on some systems to avoid linking in the + system's own getopt implementation. */ + +int optopt = '?'; + +/* Describe how to deal with options that follow non-option ARGV-elements. + + If the caller did not specify anything, + the default is REQUIRE_ORDER if the environment variable + POSIXLY_CORRECT is defined, PERMUTE otherwise. + + REQUIRE_ORDER means don't recognize them as options; + stop option processing when the first non-option is seen. + This is what Unix does. + This mode of operation is selected by either setting the environment + variable POSIXLY_CORRECT, or using `+' as the first character + of the list of option characters. + + PERMUTE is the default. We permute the contents of ARGV as we scan, + so that eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written to + expect this. + + RETURN_IN_ORDER is an option available to programs that were written + to expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. + Using `-' as the first character of the list of option characters + selects this mode of operation. + + The special argument `--' forces an end of option-scanning regardless + of the value of `ordering'. In the case of RETURN_IN_ORDER, only + `--' can cause `getopt' to return -1 with `optind' != ARGC. */ + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +/* Value of POSIXLY_CORRECT environment variable. */ +static char *posixly_correct; + +#ifdef __GNU_LIBRARY__ +/* We want to avoid inclusion of string.h with non-GNU libraries + because there are many ways it can cause trouble. + On some systems, it contains special magic macros that don't work + in GCC. */ +# include +# define my_index strchr +#else + +#ifndef WIN32 +# if HAVE_STRING_H +# include +# else +# include +# endif +#else +# include +#endif + +/* Avoid depending on library functions or files + whose names are inconsistent. */ + +#ifndef getenv +extern char *getenv (); +#endif + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +/* If using GCC, we can safely declare strlen this way. + If not using GCC, it is ok not to declare it. */ +#ifdef __GNUC__ +/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. + That was relevant to code that was here before. */ +# if (!defined __STDC__ || !__STDC__) && !defined strlen +/* gcc with -traditional declares the built-in strlen to return int, + and has done so at least since version 2.4.5. -- rms. */ +extern int strlen (const char *); +# endif /* not __STDC__ */ +#endif /* __GNUC__ */ + +#endif /* not __GNU_LIBRARY__ */ + +/* Handle permutation of arguments. */ + +/* Describe the part of ARGV that contains non-options that have + been skipped. `first_nonopt' is the index in ARGV of the first of them; + `last_nonopt' is the index after the last of them. */ + +static int first_nonopt; +static int last_nonopt; + +#ifdef _LIBC +/* Stored original parameters. + XXX This is no good solution. We should rather copy the args so + that we can compare them later. But we must not use malloc(3). */ +extern int __libc_argc; +extern char **__libc_argv; + +/* Bash 2.0 gives us an environment variable containing flags + indicating ARGV elements that should not be considered arguments. */ + +# ifdef USE_NONOPTION_FLAGS +/* Defined in getopt_init.c */ +extern char *__getopt_nonoption_flags; + +static int nonoption_flags_max_len; +static int nonoption_flags_len; +# endif + +# ifdef USE_NONOPTION_FLAGS +# define SWAP_FLAGS(ch1, ch2) \ + if (nonoption_flags_len > 0) \ + { \ + char __tmp = __getopt_nonoption_flags[ch1]; \ + __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ + __getopt_nonoption_flags[ch2] = __tmp; \ + } +# else +# define SWAP_FLAGS(ch1, ch2) +# endif +#else /* !_LIBC */ +# define SWAP_FLAGS(ch1, ch2) +#endif /* _LIBC */ + +/* Exchange two adjacent subsequences of ARGV. + One subsequence is elements [first_nonopt,last_nonopt) + which contains all the non-options that have been skipped so far. + The other is elements [last_nonopt,optind), which contains all + the options processed since those non-options were skipped. + + `first_nonopt' and `last_nonopt' are relocated so that they describe + the new indices of the non-options in ARGV after they are moved. */ + +#if defined __STDC__ && __STDC__ +static void exchange (char **); +#endif + +static void +exchange (argv) + char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + /* Exchange the shorter segment with the far end of the longer segment. + That puts the shorter segment into the right place. + It leaves the longer segment in the right place overall, + but it consists of two parts that need to be swapped next. */ + +#if defined _LIBC && defined USE_NONOPTION_FLAGS + /* First make sure the handling of the `__getopt_nonoption_flags' + string can work normally. Our top argument must be in the range + of the string. */ + if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) + { + /* We must extend the array. The user plays games with us and + presents new arguments. */ + char *new_str = HMalloc (top + 1); + if (new_str == NULL) + nonoption_flags_len = nonoption_flags_max_len = 0; + else + { + memset (__mempcpy (new_str, __getopt_nonoption_flags, + nonoption_flags_max_len), + '\0', top + 1 - nonoption_flags_max_len); + nonoption_flags_max_len = top + 1; + __getopt_nonoption_flags = new_str; + } + } +#endif + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + /* Bottom segment is the short one. */ + int len = middle - bottom; + register int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } + else + { + /* Top segment is the short one. */ + int len = top - middle; + register int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS (bottom + i, middle + i); + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + + /* Update records for the slots the non-options now occupy. */ + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +/* Initialize the internal data when the first call is made. */ + +#if defined __STDC__ && __STDC__ +static const char *_getopt_initialize (int, char *const *, const char *); +#endif +static const char * +_getopt_initialize (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + /* Start processing options with ARGV-element 1 (since ARGV-element 0 + is the program name); the sequence of previously skipped + non-option ARGV-elements is empty. */ + + first_nonopt = last_nonopt = optind; + + nextchar = NULL; + + posixly_correct = getenv ("POSIXLY_CORRECT"); + + /* Determine how to handle the ordering of options and nonoptions. */ + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + +#if defined _LIBC && defined USE_NONOPTION_FLAGS + if (posixly_correct == NULL + && argc == __libc_argc && argv == __libc_argv) + { + if (nonoption_flags_max_len == 0) + { + if (__getopt_nonoption_flags == NULL + || __getopt_nonoption_flags[0] == '\0') + nonoption_flags_max_len = -1; + else + { + const char *orig_str = __getopt_nonoption_flags; + int len = nonoption_flags_max_len = strlen (orig_str); + if (nonoption_flags_max_len < argc) + nonoption_flags_max_len = argc; + __getopt_nonoption_flags = + (char *) HMalloc (nonoption_flags_max_len); + if (__getopt_nonoption_flags == NULL) + nonoption_flags_max_len = -1; + else + memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), + '\0', nonoption_flags_max_len - len); + } + } + nonoption_flags_len = nonoption_flags_max_len; + } + else + nonoption_flags_len = 0; +#endif + + return optstring; +} + +/* Scan elements of ARGV (whose length is ARGC) for option characters + given in OPTSTRING. + + If an element of ARGV starts with '-', and is not exactly "-" or "--", + then it is an option element. The characters of this element + (aside from the initial '-') are option characters. If `getopt' + is called repeatedly, it returns successively each of the option characters + from each of the option elements. + + If `getopt' finds another option character, it returns that character, + updating `optind' and `nextchar' so that the next call to `getopt' can + resume the scan with the following option character or ARGV-element. + + If there are no more option characters, `getopt' returns -1. + Then `optind' is the index in ARGV of the first ARGV-element + that is not an option. (The ARGV-elements have been permuted + so that those that are not options now come last.) + + OPTSTRING is a string containing the legitimate option characters. + If an option character is seen that is not listed in OPTSTRING, + return '?' after printing an error message. If you set `opterr' to + zero, the error message is suppressed but we still return '?'. + + If a char in OPTSTRING is followed by a colon, that means it wants an arg, + so the following text in the same ARGV-element, or the text of the following + ARGV-element, is returned in `optarg'. Two colons mean an option that + wants an optional arg; if there is text in the current ARGV-element, + it is returned in `optarg', otherwise `optarg' is set to zero. + + If OPTSTRING starts with `-' or `+', it requests different methods of + handling the non-option ARGV-elements. + See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + + Long-named options begin with `--' instead of `-'. + Their names may be abbreviated as long as the abbreviation is unique + or is an exact match for some defined option. If they have an + argument, it follows the option name in the same ARGV-element, separated + from the option name by a `=', or else the in next ARGV-element. + When `getopt' finds a long-named option, it returns 0 if that option's + `flag' field is nonzero, the value of the option's `val' field + if the `flag' field is zero. + + The elements of ARGV aren't really const, because we permute them. + But we pretend they're const in the prototype to be compatible + with other systems. + + LONGOPTS is a vector of `struct option' terminated by an + element containing a name which is zero. + + LONGIND returns the index in LONGOPT of the long-named option found. + It is only valid when a long-named option has been found by the most + recent call. + + If LONG_ONLY is nonzero, '-' as well as '--' can introduce + long-named options. */ + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + int print_errors = opterr; + if (optstring[0] == ':') + print_errors = 0; + + if (argc < 1) + return -1; + + optarg = NULL; + + if (optind == 0 || !__getopt_initialized) + { + if (optind == 0) + optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring); + __getopt_initialized = 1; + } + + /* Test whether ARGV[optind] points to a non-option argument. + Either it does not have option syntax, or there is an environment flag + from the shell indicating it is not an option. The later information + is only used when the used in the GNU libc. */ +#if defined _LIBC && defined USE_NONOPTION_FLAGS +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ + || (optind < nonoption_flags_len \ + && __getopt_nonoption_flags[optind] == '1')) +#else +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') +#endif + + if (nextchar == NULL || *nextchar == '\0') + { + /* Advance to the next ARGV-element. */ + + /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been + moved back by the user (who may also have changed the arguments). */ + if (last_nonopt > optind) + last_nonopt = optind; + if (first_nonopt > optind) + first_nonopt = optind; + + if (ordering == PERMUTE) + { + /* If we have just processed some options following some non-options, + exchange them so that the options come first. */ + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + /* Skip any additional non-options + and extend the range of non-options previously skipped. */ + + while (optind < argc && NONOPTION_P) + optind++; + last_nonopt = optind; + } + + /* The special ARGV-element `--' means premature end of options. + Skip it like a null option, + then exchange with previous non-options as if it were an option, + then skip everything else like a non-option. */ + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + /* If we have done all the ARGV-elements, stop the scan + and back over any non-options that we skipped and permuted. */ + + if (optind == argc) + { + /* Set the next-arg-index to point at the non-options + that we previously skipped, so the caller will digest them. */ + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return -1; + } + + /* If we have come to a non-option and did not permute it, + either stop the scan or describe it to the caller and pass it by. */ + + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + optarg = argv[optind++]; + return 1; + } + + /* We have found another option-ARGV-element. + Skip the initial punctuation. */ + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + /* Decode the current option-ARGV-element. */ + + /* Check whether the ARGV-element is a long option. + + If long_only and the ARGV-element has the form "-f", where f is + a valid short option, don't consider it an abbreviated form of + a long option that starts with f. Otherwise there would be no + way to give the -f short option. + + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an abbreviation of + the long option, just like "--fu", and not "-f" with arg "u". + + This distinction seems to be the most useful approach. */ + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) + == (unsigned int) strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else if (long_only + || pfound->has_arg != p->has_arg + || pfound->flag != p->flag + || pfound->val != p->val) + /* Second or later nonexact match found. */ + ambig = 1; + } + + if (ambig && !exact) + { + if (print_errors) + fprintf (stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (print_errors) + { + if (argv[optind - 1][1] == '-') + /* --option */ + fprintf (stderr, + _("%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); + else + /* +option or -option */ + fprintf (stderr, + _("%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], pfound->name); + } + + nextchar += strlen (nextchar); + + optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (print_errors) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short + option, then it's an error. + Otherwise interpret it as a short option. */ + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (print_errors) + { + if (argv[optind][1] == '-') + /* --option */ + fprintf (stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + optopt = 0; + return '?'; + } + } + + /* Look at and handle the next short option-character. */ + + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + /* Increment `optind' when we start to process its last character. */ + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (print_errors) + { + if (posixly_correct) + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: illegal option -- %c\n"), + argv[0], c); + else + fprintf (stderr, _("%s: invalid option -- %c\n"), + argv[0], c); + } + optopt = c; + return '?'; + } + /* Convenience. Treat POSIX -W foo same as long option --foo */ + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (print_errors) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + + /* optarg is now the argument, see if it's in the + table of longopts. */ + + for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match + or abbreviated matches. */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) + { + if (print_errors) + fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (print_errors) + fprintf (stderr, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); + + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (print_errors) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + /* This is an option that accepts an argument optionally. */ + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + /* This is an option that requires an argument. */ + if (*nextchar != '\0') + { + optarg = nextchar; + /* If we end this ARGV-element by taking the rest as an arg, + we must advance to the next element now. */ + optind++; + } + else if (optind == argc) + { + if (print_errors) + { + /* 1003.2 specifies the format of this message. */ + fprintf (stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + /* We already incremented `optind' once; + increment it again when taking next ARGV-elt as argument. */ + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +/* Compile with -DTEST to make an executable for use in testing + the above definition of `getopt'. */ + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + + c = getopt (argc, argv, "abc:d:0123456789"); + if (c == -1) + break; + + switch (c) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ + +#endif /* HAVE_GETOPT_H */ diff --git a/Engine/lib/lmng/contrib/msvc/makemng/getopt/getopt.h b/Engine/lib/lmng/contrib/msvc/makemng/getopt/getopt.h new file mode 100644 index 000000000..a1b8dd665 --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/makemng/getopt/getopt.h @@ -0,0 +1,180 @@ +/* Declarations for getopt. + Copyright (C) 1989-1994, 1996-1999, 2001 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifndef _GETOPT_H + +#ifndef __need_getopt +# define _GETOPT_H 1 +#endif + +/* If __GNU_LIBRARY__ is not already defined, either we are being used + standalone, or this is the first header included in the source file. + If we are being used with glibc, we need to include , but + that does not exist if we are standalone. So: if __GNU_LIBRARY__ is + not defined, include , which will pull in for us + if it's from glibc. (Why ctype.h? It's guaranteed to exist and it + doesn't flood the namespace with stuff the way some other headers do.) */ +#if !defined __GNU_LIBRARY__ +# include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* For communication from `getopt' to the caller. + When `getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when `ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to `getopt'. + + On entry to `getopt', zero means this is the first call; initialize. + + When `getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, `optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message `getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; + +#ifndef __need_getopt +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of `struct option' terminated by an element containing a name which is + zero. + + The field `has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +struct option +{ +# if (defined __STDC__ && __STDC__) || defined __cplusplus + const char *name; +# else + char *name; +# endif + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the `has_arg' field of `struct option'. */ + +# define no_argument 0 +# define required_argument 1 +# define optional_argument 2 +#endif /* need getopt */ + + +/* Get definitions and prototypes for functions to process the + arguments in ARGV (ARGC of them, minus the program name) for + options given in OPTS. + + Return the option character from OPTS just read. Return -1 when + there are no more options. For unrecognized options, or options + missing arguments, `optopt' is set to the option letter, and '?' is + returned. + + The OPTS string is a list of characters which are recognized option + letters, optionally followed by colons, specifying that that letter + takes an argument, to be placed in `optarg'. + + If a letter in OPTS is followed by two colons, its argument is + optional. This behavior is specific to the GNU `getopt'. + + The argument `--' causes premature termination of argument + scanning, explicitly telling `getopt' that there are no more + options. + + If OPTS begins with `--', then non-option arguments are treated as + arguments to the option '\0'. This behavior is specific to the GNU + `getopt'. */ + +#if (defined __STDC__ && __STDC__) || defined __cplusplus +# ifdef __GNU_LIBRARY__ +/* Many other libraries have conflicting prototypes for getopt, with + differences in the consts, in stdlib.h. To avoid compilation + errors, only prototype getopt for the GNU C library. */ +extern int getopt (int __argc, char *const *__argv, const char *__shortopts); +# else /* not __GNU_LIBRARY__ */ +extern int getopt (); +# endif /* __GNU_LIBRARY__ */ + +# ifndef __need_getopt +extern int getopt_long (int __argc, char *const *__argv, const char *__shortopts, + const struct option *__longopts, int *__longind); +extern int getopt_long_only (int __argc, char *const *__argv, + const char *__shortopts, + const struct option *__longopts, int *__longind); + +/* Internal only. Users should not call this directly. */ +extern int _getopt_internal (int __argc, char *const *__argv, + const char *__shortopts, + const struct option *__longopts, int *__longind, + int __long_only); +# endif +#else /* not __STDC__ */ +extern int getopt (); +# ifndef __need_getopt +extern int getopt_long (); +extern int getopt_long_only (); + +extern int _getopt_internal (); +# endif +#endif /* __STDC__ */ + +#ifdef __cplusplus +} +#endif + +/* Make sure we later can get all the definitions and declarations. */ +#undef __need_getopt + +#endif /* getopt.h */ diff --git a/Engine/lib/lmng/contrib/msvc/makemng/getopt/getopt1.c b/Engine/lib/lmng/contrib/msvc/makemng/getopt/getopt1.c new file mode 100644 index 000000000..57eee0821 --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/makemng/getopt/getopt1.c @@ -0,0 +1,189 @@ +/* getopt_long and getopt_long_only entry points for GNU getopt. + Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include + +#ifndef HAVE_GETOPT_H +#include "getopt.h" + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +#ifndef const +#define const +#endif +#endif + +#include + +/* Comment out all this code if we are using the GNU C Library, and are not + actually compiling the library itself. This code is part of the GNU C + Library, but also included in many other GNU distributions. Compiling + and linking in this code is a waste when using the GNU C library + (especially if it is a shared library). Rather than having every GNU + program understand `configure --with-gnu-libc' and omit the object files, + it is simpler to just do this in the source for each such file. */ + +#define GETOPT_INTERFACE_VERSION 2 +#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 +#include +#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION +#define ELIDE_CODE +#endif +#endif + +#ifndef ELIDE_CODE + + +/* This needs to come after some library #include + to get __GNU_LIBRARY__ defined. */ +#ifdef __GNU_LIBRARY__ +#include +#endif + +#ifndef NULL +#define NULL 0 +#endif + +int +getopt_long (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 0); +} + +/* Like getopt_long, but '-' as well as '--' can indicate a long option. + If an option that starts with '-' (not '--') doesn't match a long option, + but does match a short option, it is parsed as a short option + instead. */ + +int +getopt_long_only (argc, argv, options, long_options, opt_index) + int argc; + char *const *argv; + const char *options; + const struct option *long_options; + int *opt_index; +{ + return _getopt_internal (argc, argv, options, long_options, opt_index, 1); +} + + +#endif /* Not ELIDE_CODE. */ + +#ifdef TEST + +#include + +int +main (argc, argv) + int argc; + char **argv; +{ + int c; + int digit_optind = 0; + + while (1) + { + int this_option_optind = optind ? optind : 1; + int option_index = 0; + static struct option long_options[] = + { + {"add", 1, 0, 0}, + {"append", 0, 0, 0}, + {"delete", 1, 0, 0}, + {"verbose", 0, 0, 0}, + {"create", 0, 0, 0}, + {"file", 1, 0, 0}, + {0, 0, 0, 0} + }; + + c = getopt_long (argc, argv, "abc:d:0123456789", + long_options, &option_index); + if (c == -1) + break; + + switch (c) + { + case 0: + printf ("option %s", long_options[option_index].name); + if (optarg) + printf (" with arg %s", optarg); + printf ("\n"); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (digit_optind != 0 && digit_optind != this_option_optind) + printf ("digits occur in two different argv-elements.\n"); + digit_optind = this_option_optind; + printf ("option %c\n", c); + break; + + case 'a': + printf ("option a\n"); + break; + + case 'b': + printf ("option b\n"); + break; + + case 'c': + printf ("option c with value `%s'\n", optarg); + break; + + case 'd': + printf ("option d with value `%s'\n", optarg); + break; + + case '?': + break; + + default: + printf ("?? getopt returned character code 0%o ??\n", c); + } + } + + if (optind < argc) + { + printf ("non-option ARGV-elements: "); + while (optind < argc) + printf ("%s ", argv[optind++]); + printf ("\n"); + } + + exit (0); +} + +#endif /* TEST */ + +#endif /* HAVE_GETOPT_H */ diff --git a/Engine/lib/lmng/contrib/msvc/makemng/makemng.c b/Engine/lib/lmng/contrib/msvc/makemng/makemng.c new file mode 100644 index 000000000..878b07134 --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/makemng/makemng.c @@ -0,0 +1,1765 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef WIN32 +# include +#else +# include +#endif + + +struct options +{ + char *inmask; + char *inpath; + char *outfile; + int framerate; + char verbose; + char backimage; + char deltamask; + int sectorsize; + int fullrects; +} opts; + +// externals +char** make_file_list (const char* pattern, int* pnum_entries); +void free_file_list (char** list); + +// internals +int error (int errn, const char* fmt, const char* s); +void verbose (const char* fmt, const char* s); +void verbose_d (const char* fmt, int val); +void parse_arguments (int argc, char *argv[], struct options *opts); +int read_file_list (void); +void calc_mng_dims (void); +void select_back_image (void); +void delta_images (void); +int write_mng_file (void); + +typedef union +{ + struct + { + unsigned char r, g, b, a; + } bchan; + unsigned char channels[4]; + unsigned int value; +} RGBA; + +typedef struct _file_info +{ + FILE* f; + char* fname; + char* fmode; + int w, h; + int x, y; + RGBA* image; + unsigned char* indimg; + int delay; + int identical; + unsigned short objid; + unsigned short cloneid; + int clone; + unsigned short precloneid; + int preclone; + struct _file_info* next; +} file_info; + +void file_info_free (); + +// MNG callbacks +mng_ptr MNG_DECL mng_alloc (mng_size_t iLen); +void MNG_DECL mng_free (mng_ptr pPtr, mng_size_t iLen); +mng_bool MNG_DECL mng_open_stream(mng_handle mng); +mng_bool MNG_DECL mng_close_stream(mng_handle mng); +mng_bool MNG_DECL mng_read_stream(mng_handle mng, mng_ptr buffer, mng_uint32 size, mng_uint32p bytes); +mng_bool MNG_DECL mng_write_stream (mng_handle mng, mng_ptr buffer, mng_uint32 size, mng_uint32p bytes); +mng_bool MNG_DECL mng_process_header(mng_handle mng, mng_uint32 width, mng_uint32 height); +mng_ptr MNG_DECL mng_get_canvasline_read(mng_handle mng, mng_uint32 line); +mng_ptr MNG_DECL mng_get_canvasline_write(mng_handle mng, mng_uint32 line); +mng_bool MNG_DECL mng_refresh_display(mng_handle mng, mng_uint32 x, mng_uint32 y, mng_uint32 w, mng_uint32 h); +mng_uint32 MNG_DECL mng_get_tickcount(mng_handle mng); +mng_bool MNG_DECL mng_set_timer(mng_handle mng, mng_uint32 msecs); + +#define MAX_COLORS 0x100 + +// global png/mng data +char** Files = 0; +int cFiles = 0; +file_info* Infos = 0; +mng_palette8 Pal; +int cPalClr = 0; +int mngw = 0, mngh = 0; +int iback = 0; +int timerate = 100; // default 100 ticks per second +int framedelay = 20; // default 5 fps +int _curframe = -1; +int _curdeltaframe = -1; + +int +main(int argc, char* argv[]) +{ + int ret = 0; + + parse_arguments (argc, argv, &opts); + + if (opts.framerate) // update delay + framedelay = timerate / opts.framerate; + + if (!opts.inpath && (strchr (opts.inmask, '/') || strchr (opts.inmask, '\\'))) + { + char *pch1, *pch2; + + opts.inpath = (char*) calloc (strlen (opts.inmask), 1); + if (!opts.inpath) + return error (EXIT_FAILURE, "No memory", 0); + + strcpy (opts.inpath, opts.inmask); + pch1 = strrchr (opts.inpath, '/'); + pch2 = strrchr (opts.inpath, '\\'); + if (pch2 > pch1) + pch1 = pch2; + + pch1[1] = 0; // term the path + + verbose ("Frame files in dir: %s\n", opts.inpath); + } + + if (!opts.outfile) + { + char* pch; + + opts.outfile = (char*) calloc (strlen(opts.inmask) + 6, 1); + if (!opts.outfile) + return error (EXIT_FAILURE, "No memory", 0); + + strcpy (opts.outfile, opts.inmask); + while ((pch = strchr (opts.outfile, '*')) != 0) + strcpy (pch, pch + 1); + + pch = strstr (opts.outfile, ".png"); + if (!pch) + pch = opts.outfile + strlen (opts.outfile); + strcpy (pch, ".mng"); + + if (pch == opts.outfile || pch[-1] == '/' || pch[-1] == '\\') + { // have to fix blank name + memmove (pch + 1, pch, strlen(pch) + 1); + *pch = '1'; + } + + verbose ("Output file: %s\n", opts.outfile); + } + + fprintf (stderr, "using timerate of %d and framedelay of %d\n", timerate, framedelay); + + ret = read_file_list (); + if (!ret) + { + calc_mng_dims (); + if (opts.backimage) + select_back_image (); + delta_images (); + + ret = write_mng_file (); + } + + file_info_free (); + free_file_list (Files); + + return ret; +} + +int +error(int errn, const char* fmt, const char* s) +{ + fprintf(stderr, fmt, s); + return errn; +} + +void +verbose(const char* fmt, const char* s) +{ + if (!opts.verbose) + return; + + fprintf(stderr, fmt, s); +} + +void verbose_d (const char* fmt, int val) +{ + if (!opts.verbose) + return; + + fprintf(stderr, fmt, val); +} + +void +usage() +{ + fprintf(stderr, "usage: makemng [-v] [-f rate] [-r] [-s size] [-o outputfile] \n"); + fprintf(stderr, "produces an MNG animation from a bunch of frame images\n"); + fprintf(stderr, "options:\n"); + fprintf(stderr, " -v\t\t : be verbose, explains things no human should know\n"); + fprintf(stderr, " -f rate\t : sets the framerate; rate is 1..100 per second (default 5)\n"); + fprintf(stderr, " -b\t\t : auto-select background frame (instead of frame0)\n"); + fprintf(stderr, " -r\t\t : split delta frames into full rectangles only\n"); + fprintf(stderr, " -s size\t : enable sector cleanup and set sector size (8..64)\n"); + fprintf(stderr, "diagnostical options:\n"); + fprintf(stderr, " -d\t\t : generate delta-mask PNGs (form: mask_FRM1_FRM2.png)\n"); +} + +void +parse_arguments(int argc, char *argv[], struct options *opts) +{ + char ch; + + memset(opts, '\0', sizeof (struct options)); + while ((ch = getopt(argc, argv, "?hvbdrf:s:o:")) != -1) + { + switch(ch) + { + case 'o': + opts->outfile = optarg; + break; + case 'f': + opts->framerate = atoi(optarg); + if (opts->framerate < 1 || opts->framerate > 100) + { + fprintf(stderr, "invalid -f option value\n"); + usage(); + exit(EXIT_FAILURE); + } + break; + case 'd': + opts->deltamask = 1; + break; + case 'b': + opts->backimage = 1; + break; + case 'r': + opts->fullrects = 1; + break; + case 's': + opts->sectorsize = atoi(optarg); + if (opts->sectorsize < 8 || opts->sectorsize > 64) + { + fprintf(stderr, "invalid -r option value\n"); + usage(); + exit(EXIT_FAILURE); + } + break; + case 'v': + opts->verbose = 1; + break; + case '?': + case 'h': + default: + usage(); + exit(EXIT_FAILURE); + } + } + + argc -= optind; + argv += optind; + if (argc != 1) + { + usage(); + exit(EXIT_FAILURE); + } + + opts->inmask = argv[0]; +} + +void +make_file_name(int index, char* buf) +{ + if (opts.inpath) + strcpy(buf, opts.inpath); + else + *buf = 0; + + strcat(buf, Files[index]); +} + +void +file_info_init (file_info* ms) +{ + memset(ms, 0, sizeof(*ms)); + ms->identical = -1; + ms->clone = -1; + ms->preclone = -1; +} + +void +file_info_cleanup (file_info* ms) +{ + file_info* fi = ms; + + while (fi) + { + file_info* tempi = fi; + + if (fi->image) + { + free(fi->image); + fi->image = 0; + } + + if (fi->indimg) + { + free(fi->indimg); + fi->indimg = 0; + } + + if (fi->f) + { + fclose(fi->f); + fi->f = 0; + } + + + fi = fi->next; + if (tempi != ms) + free (tempi); + } +} + +void +file_info_free () +{ + int i; + + if (Infos) + { + for (i = 0; i < cFiles; i++) + file_info_cleanup (Infos + i); + + free (Infos); + Infos = 0; + } +} + +int +equal_colors (RGBA rgba, mng_palette8e mng_clr) +{ + return rgba.bchan.r == mng_clr.iRed && + rgba.bchan.g == mng_clr.iGreen && + rgba.bchan.b == mng_clr.iBlue; +} + +int +lookup_palette (RGBA rgba) +{ + int i; + + for (i = 0; i < cPalClr && !equal_colors(rgba, Pal[i]); i++) + ; + + return i < cPalClr ? i : -1; +} + +int +update_palette (file_info* ms) +{ + int i; + + for (i = 0; i < ms->w * ms->h; i++) + { + RGBA rgba = ms->image[i]; + int ipal = lookup_palette (rgba); + if (ipal == -1) + { // add color + if (cPalClr >= MAX_COLORS) + return 1; + + Pal[cPalClr].iRed = rgba.bchan.r; + Pal[cPalClr].iGreen = rgba.bchan.g; + Pal[cPalClr].iBlue = rgba.bchan.b; + cPalClr++; + } + } + return 0; +} + +int +convert_image_indexed (file_info* ms) +{ + int i; + + ms->indimg = (unsigned char*) malloc (ms->w * ms->h); + if (!ms->indimg) + return 230; + + for (i = 0; i < ms->w * ms->h; i++) + { + int ipal = lookup_palette (ms->image[i]); + + if (ipal == -1) + return 1; // something is screwed + + ms->indimg[i] = ipal; + } + + free (ms->image); + ms->image = 0; + + return 0; +} + +int +read_file_list (void) +{ + int ret = 0; + mng_handle mng; + char namebuf[260]; + int i; + + cFiles = 0; + Files = make_file_list(opts.inmask, &cFiles); + + if (!Files || cFiles == 0) + { + fprintf (stderr, "No frame files found\n"); + return 1; + } + + Infos = (file_info*) malloc (sizeof(file_info) * cFiles); + if (!Infos) + return 251; + + memset(Infos, 0, sizeof(file_info) * cFiles); + + mng = mng_initialize (MNG_NULL, mng_alloc, mng_free, MNG_NULL); + if (mng == MNG_NULL) + return 250; + + // set the callbacks + mng_setcb_openstream(mng, mng_open_stream); + mng_setcb_closestream(mng, mng_close_stream); + mng_setcb_readdata(mng, mng_read_stream); + mng_setcb_processheader(mng, mng_process_header); + mng_setcb_getcanvasline(mng, mng_get_canvasline_read); + mng_setcb_gettickcount(mng, mng_get_tickcount); + mng_setcb_settimer(mng, mng_set_timer); + mng_setcb_refresh(mng, mng_refresh_display); + + for (i = 0; i < cFiles && !ret; i++) + { + file_info* rf = Infos + i; + + file_info_init (rf); + make_file_name (i, namebuf); + rf->fname = namebuf; + rf->fmode = "rb"; + + verbose_d ("%03d ", i); verbose ("reading '%s'...", rf->fname); + + mng_reset (mng); + mng_set_userdata (mng, rf); + + for (ret = mng_readdisplay (mng); + ret == MNG_NEEDMOREDATA || ret == MNG_NEEDTIMERWAIT; + ret = mng_display_resume (mng)) + { + if (ret == MNG_NEEDTIMERWAIT) + rf->delay = 0; + } + + if (ret) + { + fprintf (stderr, "Could not read '%s'\n", rf->fname); + ret = 2; + } + + ret = update_palette (rf); + if (ret) + { + fprintf (stderr, "Too many unique colors (%d processed), giving up\n", i); + ret = 3; + } + + ret = convert_image_indexed (rf); + if (ret) + { + fprintf (stderr, "Image conversion failed on '%s'\n", rf->fname); + ret = 4; + } + + verbose (" done\n", 0); + } + + mng_cleanup (&mng); + + if (ret == MNG_NOERROR) + fprintf (stderr, "%d animation frames\n", cFiles); + + return ret; +} + +void +calc_mng_dims (void) +{ + int i; + + mngw = mngh = -1; + + // get max dims + for (i = 0; i < cFiles; i++) + { + if (Infos[i].w > mngw) + mngw = Infos[i].w; + + if (Infos[i].h > mngh) + mngh = Infos[i].h; + } + + // adjust images - center + for (i = 0; i < cFiles; i++) + { + if (Infos[i].w < mngw) + Infos[i].x = (mngw - Infos[i].w) >> 1; + + if (Infos[i].h < mngh) + Infos[i].y = (mngh - Infos[i].h) >> 1; + } +} + +int +compare_images (file_info* i1, file_info* i2) +{ + int cnt = 0; + int w, h, x, y; + int dx1, dx2, dy1, dy2; + + if (i1->w > i2->w) + { + w = i2->w; + dx1 = i2->x; + dx2 = 0; + } + else if (i1->w < i2->w) + { + w = i1->w; + dx1 = 0; + dx2 = i1->x; + } + else + { + w = i1->w; + dx1 = dx2 = 0; + } + + if (i1->h > i2->h) + { + h = i2->h; + dy1 = i2->y; + dy2 = 0; + } + else if (i1->h < i2->h) + { + h = i1->h; + dy1 = 0; + dy2 = i1->y; + } + else + { + h = i1->h; + dy1 = dy2 = 0; + } + + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + { + if (i1->indimg[(y + dy1) * i1->w + x + dx1] != i2->indimg[(y + dy2) * i2->w + x + dx2]) + cnt++; + } + } + return cnt; +} + +void +select_back_image (void) +{ + int i; + int* cdiff; + int max; + + cdiff = (int*) calloc (cFiles, sizeof(int)); + if (!cdiff) + return; + + verbose ("selecting optimal background image...", 0); + + for (i = 2; i < cFiles; i++) + { + if (Infos[i].w == mngw && Infos[i].h == mngh) + { + cdiff[i] = compare_images (Infos + i, Infos + i - 1) - + compare_images (Infos + i, Infos + 0); + } + else + { + // image is smaller than animation and cannot be background + cdiff[i] = 0x7fffffff; + } + } + + // the difference has to be big enough + // or it will be useless + iback = 0; + max = mngw * mngh / 32; + + for (i = 2; i < cFiles; i++) + { + if (cdiff[i] > max) + { + iback = i; + max = cdiff[i]; + } + } + + verbose (" done\n", 0); + fprintf(stderr, "frame %03d selected as background\n", iback); +} + +int +equal_images (int i1, int i2) +{ + // deference identical chain + while (Infos[i1].identical != -1) + i1 = Infos[i1].identical; + while (Infos[i2].identical != -1) + i2 = Infos[i2].identical; + + if (i1 == i2) + return 1; + + if (Infos[i1].x != Infos[i2].x || Infos[i1].y != Infos[i2].y + || Infos[i1].w != Infos[i2].w || Infos[i1].h != Infos[i2].h) + return 0; + + return compare_images (Infos + i1, Infos + i2) == 0; +} + +int +delta_adjust_positions (int* pos1, int* pos2) +{ + if (*pos1 <= *pos2) + return 1; + else + { + (*pos1)--; + (*pos2)--; + return -1; + } +} + +void +clean_expansion_horz (unsigned char* mask, int w, int x1, int y1, int x2, int y2, int threshold) +{ + int x, y, dx, dy; + // assume anything out of bounds is cleared + int prevclear = threshold + 1; + + dy = delta_adjust_positions (&y1, &y2); + dx = delta_adjust_positions (&x1, &x2); + + for (y = y1; y != y2; y += dy) + { + int dcnt, ecnt; + + dcnt = ecnt = 0; + + for (x = x1; x != x2; x += dx) + { + if (mask[y * w + x] == 1) + dcnt++; + else if (mask[y * w + x] == 2 || mask[y * w + x] == 3) + ecnt++; + } + + if (dcnt == 0 && ecnt == 0) + { // line is clear + prevclear++; + } + else if (dcnt == 0) + { + if (prevclear >= threshold) + { // it's not clear yet, but it will be in a moment ;) + int lx, ly = y; + + if (prevclear == threshold) + { // need to clean everything we just checked + ly = y - prevclear * dy; + } + + for (ly = ly; ly != y + dy; ly += dy) + for (lx = x1; lx != x2; lx += dx) + mask[ly * w + lx] = 0; + } + prevclear++; + } + else + { // line is dirty + prevclear = 0; + } + } +} + +void +clean_expansion_vert (unsigned char* mask, int w, int x1, int y1, int x2, int y2, int threshold) +{ + int x, y, dx, dy; + // assume anything out of bounds is cleared + int prevclear = threshold + 1; + + dy = delta_adjust_positions (&y1, &y2); + dx = delta_adjust_positions (&x1, &x2); + + for (x = x1; x != x2; x += dx) + { + int dcnt, ecnt; + + dcnt = ecnt = 0; + + for (y = y1; y != y2; y += dy) + { + if (mask[y * w + x] == 1) + dcnt++; + else if (mask[y * w + x] == 2 || mask[y * w + x] == 3) + ecnt++; + } + + if (dcnt == 0 && ecnt == 0) + { // line is clear + prevclear++; + } + else if (dcnt == 0) + { + if (prevclear >= threshold) + { // it's not clear yet, but it will be in a moment ;) + int ly, lx = x; + + if (prevclear == threshold) + { // need to clean everything we just checked + lx = x - prevclear * dx; + } + + for (lx = lx; lx != x + dx; lx += dx) + for (ly = y1; ly != y2; ly += dy) + mask[ly * w + lx] = 0; + } + prevclear++; + } + else + { // line is dirty + prevclear = 0; + } + } +} + +struct _expand_corner +{ + int x, y; + int tx1, ty1; + int tx2, ty2; +} +const expand_corner [] = +{ + {0, 0, 1, 0, 0, 1}, // top-left + {1, 0, 0, 0, 0, 1}, // top-mid, from left + {1, 0, 2, 0, 2, 1}, // top-mid, from right + {2, 0, 1, 0, 2, 1}, // top-right + {0, 1, 0, 0, 1, 0}, // mid-left, from top + {0, 1, 0, 2, 1, 2}, // mid-left, from bottom + {2, 1, 1, 0, 2, 0}, // mid-right, from top + {2, 1, 1, 2, 2, 2}, // mid-right, from bottom + {0, 2, 1, 2, 0, 1}, // bot-left + {1, 2, 0, 1, 0, 2}, // bot-mid, from left + {1, 2, 2, 1, 2, 2}, // bot-mid, from right + {2, 2, 1, 2, 2, 1}, // bot-right + + {-1,-1, -1,-1, -1,-1} // term +}; + +// this will recursively expand the missing corner pixels +// recursion is limited so we dont overflow the stack +int +expand_rect (char* mask, int x, int y, int w, int h) +{ + static int level = 0; + int x1, y1, x2, y2, i, lx, ly; + const struct _expand_corner* pc; + signed char matrix[3][3]; + int cnt = 0; + + if (level > 99) + return 1; // make sure parent knows it failed + + level++; + + if (x > 0) + x1 = x - 1; + else + { + for (i = 0; i < 3; i++) + matrix[0][i] = -1; + + x1 = x; + } + + if (y > 0) + y1 = y - 1; + else + { + for (i = 0; i < 3; i++) + matrix[i][0] = -1; + + y1 = y; + } + + if (x + 1 < w) + x2 = x + 2; + else + { + for (i = 0; i < 3; i++) + matrix[2][i] = -1; + + x2 = x + 1; + } + + if (y + 1 < h) + y2 = y + 2; + else + { + for (i = 0; i < 3; i++) + matrix[i][2] = -1; + + y2 = y + 1; + } + + for (ly = y1; ly < y2; ly++) + for (lx = x1; lx < x2; lx++) + matrix[lx - x + 1][ly - y + 1] = mask[ly * w + lx]; + + // check corner pixels + for (pc = expand_corner; pc->x != -1; pc++) + { + if (matrix[pc->x][pc->y] == 0 && matrix[pc->tx1][pc->ty1] > 0 && matrix[pc->tx2][pc->ty2] > 0) + { // corner pixel missing + int ofs = (y - 1 + pc->y) * w + (x - 1 + pc->x); + + matrix[pc->x][pc->y] = 3; + + // but it may already be present in the mask (recursive) + if (mask[ofs] == 0) + { + mask[ofs] = 3; + cnt += 1 + expand_rect (mask, x - 1 + pc->x, y - 1 + pc->y, w, h); + } + } + } + + level--; + + return cnt; +} + +file_info* +file_info_add_image (file_info* fi) +{ + file_info* ni; + + ni = (file_info*) malloc (sizeof(file_info)); + if (!ni) + return 0; + + file_info_init (ni); + + while (fi->next) + fi = fi->next; + + return fi->next = ni; +} + +int +is_multi_delta_image (file_info* fi) +{ + return fi && fi->next; +} + +#define MASK_COLORS 4 +mng_palette8e mask_pal[MASK_COLORS] = +{ + {0x00, 0x00, 0x00}, + {0xff, 0xff, 0xff}, + {0x00, 0xff, 0x00}, + {0x00, 0x00, 0xff} +}; + +void +create_mask_png (char* mask, int w, int h) +{ + int ret = 0; + mng_handle mng; + file_info wf; + char fname[260]; + mng_ptr imgdata; + unsigned char* tempdata; + unsigned char* p; + uLong srcLen; + uLong dstLen; + int i; + + file_info_init (&wf); + sprintf(fname, "mask_%03d_%03d.png", _curframe, _curdeltaframe); + + wf.fname = fname; + wf.fmode = "wb"; + + // extra byte in front of each line for filter type + srcLen = w * h + h; + tempdata = (mng_ptr) malloc(srcLen); + if (!tempdata) + return; + + // maximum necessary space + // deflated data can be 100.1% + 12 bytes in worst case + dstLen = srcLen + srcLen / 100 + 20; // extra 8 for safety + imgdata = (mng_ptr) malloc(dstLen); + if (!imgdata) + return; + + for (i = 0, p = tempdata; i < w * h; i++, p++) + { + if (i % w == 0) + { // write filter byte + *p++ = 0; + } + + *p = mask[i]; + } + + if (Z_OK != compress2(imgdata, &dstLen, tempdata, srcLen, 9)) + return; + + free(tempdata); + + mng = mng_initialize (&wf, mng_alloc, mng_free, MNG_NULL); + if (mng == MNG_NULL) + return; + + // set the callbacks + mng_setcb_openstream(mng, mng_open_stream); + mng_setcb_closestream(mng, mng_close_stream); + mng_setcb_writedata(mng, mng_write_stream); + + ret = mng_create (mng); + + ret = mng_putchunk_ihdr (mng, w, h, + MNG_BITDEPTH_8, MNG_COLORTYPE_INDEXED, MNG_COMPRESSION_DEFLATE, + MNG_FILTER_ADAPTIVE, MNG_INTERLACE_NONE); + + if (ret == MNG_NOERROR) + ret = mng_putchunk_plte (mng, 4, mask_pal); + if (ret == MNG_NOERROR) + ret = mng_putchunk_idat (mng, dstLen, imgdata); + if (ret == MNG_NOERROR) + ret = mng_putchunk_iend (mng); + + free (imgdata); + + if (ret == MNG_NOERROR) + ret = mng_write (mng); + + mng_cleanup (&mng); + + file_info_cleanup (&wf); +} + +int +build_delta (file_info* i1, file_info* i2) +{ + int w, h, x, y; + int dx1, dx2, dy1, dy2; + int cnt; + char* mask = 0; + + if (i1->w > i2->w) + { + w = i2->w; + dx1 = i2->x; + dx2 = 0; + } + else if (i1->w < i2->w) + { + w = i1->w; + dx1 = 0; + dx2 = i1->x; + } + else + { + w = i1->w; + dx1 = dx2 = 0; + } + + if (i1->h > i2->h) + { + h = i2->h; + dy1 = i2->y; + dy2 = 0; + } + else if (i1->h < i2->h) + { + h = i1->h; + dy1 = 0; + dy2 = i1->y; + } + else + { + h = i1->h; + dy1 = dy2 = 0; + } + + mask = (char*) malloc (w * h); + if (!mask) + return 220; + memset(mask, 0, w * h); + + // build diff mask first + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + { + if (i1->indimg[(y + dy1) * i1->w + x + dx1] != i2->indimg[(y + dy2) * i2->w + x + dx2]) + // diff pixel + mask[y * w + x] = 1; + } + } + + // coarse expand the diff pixels + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + { + if (mask[y * w + x] == 1) + { + int x1 = x - 2; + int x2 = x + 3; + int y1 = y - 2; + int y2 = y + 3; + int lx; + if (x1 < 0) + x1 = 0; + if (x2 > w) + x2 = w; + if (y1 < 0) + y1 = 0; + if (y2 > h) + y2 = h; + + for (y1 = y1; y1 < y2; y1++) + for (lx = x1; lx < x2; lx++) + if (mask[y1 * w + lx] == 0) + mask[y1 * w + lx] = 2; + } + } + } + + // scan and remove extra expansion horizontally and vertically + clean_expansion_vert (mask, w, 0, 0, w, h, 1); + clean_expansion_vert (mask, w, w, 0, 0, h, 1); + clean_expansion_horz (mask, w, 0, 0, w, h, 1); + clean_expansion_horz (mask, w, 0, h, w, 0, 1); + + + do // coarse expand the diff pixels + { // merge would-be diff rectangles in the process + cnt = 0; + + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + { + if (mask[y * w + x] != 0) + cnt += expand_rect (mask, x, y, w, h); + } + } + // repeat is something was expanded + } while (cnt != 0); + + // at this point we should have guaranteed non-overlapping + // rectangles that cover all of the delta areas + + if (opts.sectorsize) + { // final expansion cleanup + for (y = 0; y < h; y += opts.sectorsize) + { + for (x = 0; x < w; x += opts.sectorsize) + { + int x2, y2; + + cnt = 0; + for (y2 = y; y2 < y + opts.sectorsize && y2 < h; y2++) + for (x2 = x; x2 < x + opts.sectorsize && x2 < w; x2++) + if (mask[y2 * w + x2] == 1) + cnt++; + + if (cnt > 0) + continue; // dirty sector + + // clean up sector + for (y2 = y; y2 < y + opts.sectorsize && y2 < h; y2++) + for (x2 = x; x2 < x + opts.sectorsize && x2 < w; x2++) + mask[y2 * w + x2] = 0; + + } + } + } + + // check how muany pixels have to be replaced + for (x = 0, cnt = 0; x < w * h; x++) + if (mask[x]) + cnt++; + + if (opts.deltamask) + create_mask_png (mask, w, h); + + // generate delta images + if (cnt != w * h) + { + int ofs; + + for (y = 0, ofs = 0; y < h; y++) + { + for (x = 0; x < w; x++, ofs++) + { + if (mask[ofs] != 0) + { // copy masked rectangle into a new image + // and clear the mask + int i; + int rw, rh; + int x2, y2; + unsigned char* src; + unsigned char* dst; + file_info* ni; + + ni = file_info_add_image (i1); + if (!ni) + { + x = w; + y = h; + break; + } + + + // lookup delta rectangle + for (i = x, src = mask + ofs; i < w && *src != 0; i++, src++) + ; + ni->w = rw = i - x; + + if (opts.fullrects) + { // locate only complete rectangles + y2 = y + 1; + for (i = y + 1, src = mask + ofs; i < h && *src != 0; i++, src += w) + { + unsigned char* src2; + + y2 = i; + for (x2 = x, src2 = src; x2 < x + rw && *src2 != 0; x2++, src2++) + ; + + if (x2 < x + rw) + break; + } + } + else + { // any rectangles + for (y2 = y + 1, src = mask + ofs; y2 < h && *src != 0; y2++, src += w) + ; + } + + ni->h = rh = y2 - y; + + ni->indimg = (unsigned char*) malloc (rw * rh); + if (!ni->indimg) + { + x = w; + y = h; + break; + } + + // copy the pixels + for (i = 0, src = i1->indimg + (dy1 + y) * i1->w + dx1 + x, dst = ni->indimg; + i < rh; + i++, src += i1->w, dst += rw + ) + { + memcpy (dst, src, rw); + memset (mask + ofs + i * w, 0, rw); + } + + ni->x = i1->x + dx1 + x; + ni->y = i1->y + dy1 + y; + } + } + } + + if (i1->next) + { // dispose of the original + file_info* ni = i1->next; + free (i1->indimg); + i1->indimg = ni->indimg; + i1->x = ni->x; + i1->y = ni->y; + i1->w = ni->w; + i1->h = ni->h; + i1->next = ni->next; + + free (ni); + } + } + else + { // break here + cnt = 1; + } + + if (mask) + free (mask); + + return 0; +} + +void +delta_images (void) +{ + int i; + unsigned short nextid = 0x101; + + verbose ("calculating frame image deltas", 0); + + Infos[iback].objid = nextid++; + if (iback != 0) + { // set the first frame objid different + // from back id + Infos[0].objid = nextid++; + } + + // remove dupes + for (i = 1; i < cFiles; i++) + { + int i2; + + if (i == iback) + continue; + + Infos[i].objid = Infos[i - 1].objid; + + for (i2 = i - 1; i2 >= 0 && Infos[i].identical == -1; i2--) + { + int orgi2 = i2; + + // deference identical chain + while (Infos[i2].identical != -1) + i2 = Infos[i2].identical; + + if (equal_images (i, i2)) + { + Infos[i].identical = i2; + // dont need image data anymore + if (Infos[i].indimg) + { + free (Infos[i].indimg); + Infos[i].indimg = 0; + } + + if (orgi2 != i - 1) + { // detached descendant + // clone the object for it + if (Infos[i2].clone == -1) + { // no clones yet + Infos[i2].cloneid = nextid++; + Infos[i2].clone = i; + Infos[i].objid = Infos[i2].cloneid; + } + else + { // already cloned for another frame + // tell the frame to preclone it for + // this frame too + // dereference preclone chain first + for (i2 = Infos[i2].clone; Infos[i2].preclone != -1; i2 = Infos[i2].preclone) + ; + Infos[i2].preclone = i; + Infos[i2].precloneid = nextid++; + Infos[i].objid = Infos[i2].precloneid; + } + } + } + } + verbose (".", 0); + } + verbose ("|", 0); + + // compute deltas + for (i = cFiles - 1; i >= 0; i--) + { + int i2; + + if (i == iback || Infos[i].identical != -1) + // no delta needed + continue; + else + { + if (i == 0 && i != iback) + { // delta against original background + i2 = iback; + } + else + { // deref indentical chain + for (i2 = i - 1; i2 >= 0 && Infos[i2].identical != -1; i2 = Infos[i2].identical) + ; + + // sanity check + if (Infos[i2].objid != Infos[i].objid) + { + fprintf (stderr, "delta_images: logical error 1\n"); + exit(EXIT_FAILURE); + } + } + + // debug info + _curframe = i; + _curdeltaframe = i2; + + build_delta (Infos + i, Infos + i2); + } + verbose (".", 0); + } + + verbose ("\n", 0); +} + +int +get_png_image_data (file_info* ms, unsigned char* imgdata) +{ + int i; + + for (i = 0; i < ms->w * ms->h; i++, imgdata++) + { + if (i % ms->w == 0) + { // write filter byte + *imgdata++ = 0; + } + + *imgdata = ms->indimg[i]; + } + return 0; +} + +int +compress_png (file_info* ms, mng_ptr imgdata, mng_uint32 imglen, mng_uint32p bytes) +{ + int ret = 0; + unsigned char* tempdata; + uLong srcLen; + + *bytes = 0; + + // extra byte in front of each line for filter type + srcLen = ms->w * ms->h + ms->h; + tempdata = (mng_ptr) malloc(srcLen); + if (!tempdata) + return 241; + + ret = get_png_image_data (ms, tempdata); + if (!ret) + { + uLong dstLen = imglen; + + if (Z_OK == compress2(imgdata, &dstLen, tempdata, srcLen, 9)) + *bytes = dstLen; + else + ret = 253; + } + + free(tempdata); + + return ret; +} + +int +output_png (mng_handle mng, file_info* rf, int delta) +{ + int ret = 0; + mng_ptr imgdata; + mng_uint32 imglen; + mng_uint32 cbcomp; + unsigned short objid = rf->objid; + + // maximum necessary space + // deflated data can be 100.1% + 12 bytes in worst case + imglen = mngw * mngh + mngh; + imglen += imglen / 100 + 20; // extra 8 for safety + imgdata = (mng_ptr) malloc(imglen); + if (!imgdata) + return 252; + + do + { + if (delta) + { // output delta + ret = mng_putchunk_dhdr (mng, objid, + MNG_IMAGETYPE_PNG, MNG_DELTATYPE_BLOCKPIXELREPLACE, + rf->w, rf->h, rf->x, rf->y); + } + else + { // output image verbatim + ret = mng_putchunk_ihdr (mng, rf->w, rf->h, + MNG_BITDEPTH_8, MNG_COLORTYPE_INDEXED, MNG_COMPRESSION_DEFLATE, + MNG_FILTER_ADAPTIVE, MNG_INTERLACE_NONE); + + if (ret == MNG_NOERROR) + { // write empty PLTE to use the global PLTE + ret = mng_putchunk_plte (mng, 0, Pal); + //ret = mng_putchunk_plte (mng, cPalClr, Pal); // enable to write plain PNG + } + } + + if (ret == MNG_NOERROR) + ret = compress_png (rf, imgdata, imglen, &cbcomp); + + if (ret == MNG_NOERROR) + ret = mng_putchunk_idat (mng, cbcomp, imgdata); + + if (ret == MNG_NOERROR) + ret = mng_putchunk_iend (mng); + + } while ((rf = rf->next) != 0 && ret == MNG_NOERROR); + + free (imgdata); + + return ret; +} + +int +write_mng_file (void) +{ + int ret = 0; + mng_handle mng; + file_info wf; + file_info rf; + file_info backf; + int i; + unsigned short lastobjid; + char curframemode, newframemode; + + mng = mng_initialize (MNG_NULL, mng_alloc, mng_free, MNG_NULL); + if (mng == MNG_NULL) + { + fprintf (stderr, "libmng did not init properly\n"); + return 250; + } + + // set the callbacks + mng_setcb_openstream(mng, mng_open_stream); + mng_setcb_closestream(mng, mng_close_stream); + mng_setcb_writedata(mng, mng_write_stream); + + file_info_init (&wf); + wf.fname = opts.outfile; + wf.fmode = "wb"; + mng_set_userdata (mng, &wf); + + ret = mng_create (mng); + if (ret != MNG_NOERROR) + fprintf (stderr, "Could not create '%s'\n", wf.fname); + else + verbose ("writing MNG file '%s'", wf.fname); + + ret = mng_putchunk_mhdr (mng, mngw, mngh, timerate, 0, 0, 0, + MNG_SIMPLICITY_VALID | MNG_SIMPLICITY_SIMPLEFEATURES | + MNG_SIMPLICITY_COMPLEXFEATURES | MNG_SIMPLICITY_DELTAPNG | 0x240); + + //ret = mng_putchunk_term (mng, MNG_TERMACTION_LASTFRAME, MNG_ITERACTION_LASTFRAME, 0, 0); + + ret = mng_putchunk_plte (mng, cPalClr, Pal); + + ret = mng_putchunk_back (mng, 0,0,0, 0, 0, MNG_BACKGROUNDIMAGE_NOTILE); + + curframemode = MNG_FRAMINGMODE_1; + ret = mng_putchunk_fram (mng, MNG_FALSE, curframemode, 0,MNG_NULL, + MNG_CHANGEDELAY_DEFAULT, MNG_CHANGETIMOUT_NO, MNG_CHANGECLIPPING_NO, MNG_CHANGESYNCID_NO, + framedelay, 0,0,0,0,0,0, MNG_NULL,0); + + // define the staring image/object + backf = Infos[iback]; + ret = mng_putchunk_defi (mng, backf.objid, MNG_DONOTSHOW_NOTVISIBLE, MNG_CONCRETE, MNG_FALSE, 0,0, MNG_FALSE, 0,0,0,0); + ret = output_png (mng, &backf, 0); + + //ret = mng_putchunk_save (mng, MNG_TRUE, 0,0); + //ret = mng_putchunk_seek (mng, 5, "start"); + + if (iback != 0) + { // clone the starting object for the first frame + ret = mng_putchunk_clon (mng, backf.objid, Infos[0].objid, + MNG_FULL_CLONE, MNG_DONOTSHOW_NOTVISIBLE, MNG_CONCRETE_ASPARENT, + MNG_FALSE, 0,0,0); + } + + lastobjid = 0; + + for (i = 0; i < cFiles && ret == MNG_NOERROR; i++) + { + rf = Infos[i]; + + if (rf.precloneid != 0) + { // pre-clone the object for another frame + ret = mng_putchunk_clon (mng, rf.objid, rf.precloneid, + MNG_FULL_CLONE, MNG_DONOTSHOW_NOTVISIBLE, MNG_CONCRETE_ASPARENT, + MNG_FALSE, 0,0,0); + } + + if (is_multi_delta_image (&rf)) + // multi-delta png; frame mode: 0-delay for subframe + newframemode = MNG_FRAMINGMODE_2; + else + // frame mode: 1 image per frame + newframemode = MNG_FRAMINGMODE_1; + + + if (newframemode != curframemode) + { // change framing mode only + ret = mng_putchunk_fram (mng, MNG_FALSE, newframemode, 0,MNG_NULL, + MNG_CHANGEDELAY_NO, MNG_CHANGETIMOUT_NO, MNG_CHANGECLIPPING_NO, MNG_CHANGESYNCID_NO, + 0,0,0,0,0,0,0, MNG_NULL,0); + curframemode = newframemode; + } + else if (curframemode == MNG_FRAMINGMODE_2) + { // start new subframe + ret = mng_putchunk_fram (mng, MNG_TRUE, 0,0,MNG_NULL,0,0,0,0,0,0,0,0,0,0,0,0,0); + } + + if (rf.indimg != 0 && i != iback) + { // display a delta png + ret = output_png (mng, &rf, 1); + } + + if (rf.cloneid != 0) + { // post-clone the object for another frame + ret = mng_putchunk_clon (mng, rf.objid, rf.cloneid, + MNG_FULL_CLONE, MNG_DONOTSHOW_NOTVISIBLE, MNG_CONCRETE_ASPARENT, + MNG_FALSE, 0,0,0); + } + + if (rf.objid != lastobjid || rf.identical != -1) + { // show the object for this frame + ret = mng_putchunk_show (mng, MNG_FALSE, rf.objid, rf.objid, MNG_SHOWMODE_0); + lastobjid = rf.objid; + } + + verbose (".", 0); + } + + //ret = mng_putchunk_seek (mng, 3, "end"); + ret = mng_putchunk_mend (mng); + + ret = mng_write (mng); + + file_info_cleanup (&wf); + + mng_cleanup (&mng); + + if (ret == MNG_NOERROR) + verbose ("finished.\n", 0); + else + fprintf (stderr, "Could not create MNG file\n"); + + return ret; +} + +mng_ptr MNG_DECL +mng_alloc (mng_size_t iLen) +{ + mng_ptr ptr; + + if (iLen & 0x80000000) + return 0; // MNG error! + + ptr = malloc (iLen); + if (ptr) + memset(ptr, 0, iLen); + + return ptr; +} + +void MNG_DECL +mng_free (mng_ptr pPtr, mng_size_t iLen) +{ + if (iLen) + free (pPtr); +} + +mng_bool MNG_DECL +mng_open_stream (mng_handle mng) +{ + file_info* ms; + + ms = (file_info*) mng_get_userdata (mng); + + ms->f = fopen (ms->fname, ms->fmode); + if (!ms->f) + { + fprintf(stderr, "unable to open '%s'\n", ms->fname); + return MNG_FALSE; + } + + return MNG_TRUE; +} + +mng_bool MNG_DECL +mng_close_stream (mng_handle mng) +{ + file_info* ms; + + ms = (file_info*) mng_get_userdata (mng); + + fclose(ms->f); + ms->f = NULL; + + return MNG_TRUE; +} + +mng_bool MNG_DECL +mng_read_stream (mng_handle mng, mng_ptr buffer, mng_uint32 size, mng_uint32p bytes) +{ + file_info* ms; + + ms = (file_info*) mng_get_userdata (mng); + + *bytes = fread(buffer, 1, size, ms->f); + + return MNG_TRUE; +} + +mng_bool MNG_DECL +mng_write_stream (mng_handle mng, mng_ptr buffer, mng_uint32 size, mng_uint32p bytes) +{ + file_info* ms; + + ms = (file_info*) mng_get_userdata (mng); + + *bytes = fwrite(buffer, 1, size, ms->f); + + return MNG_TRUE; +} + +mng_bool MNG_DECL +mng_process_header (mng_handle mng, mng_uint32 width, mng_uint32 height) +{ + file_info* ms; + + ms = (file_info*) mng_get_userdata (mng); + ms->w = width; + ms->h = height; + ms->image = (RGBA*) malloc(sizeof(RGBA) * width * height); + if (!ms->image) + return MNG_FALSE; + + mng_set_canvasstyle(mng, MNG_CANVAS_RGBA8); + + return MNG_TRUE; +} + +mng_ptr MNG_DECL +mng_get_canvasline_read (mng_handle mng, mng_uint32 line) +{ + file_info* ms; + mng_ptr row; + + ms = (file_info*) mng_get_userdata (mng); + + row = ms->image + ms->w * line; + + return row; +} + +mng_ptr MNG_DECL +mng_get_canvasline_write (mng_handle mng, mng_uint32 line) +{ + file_info* ms; + + ms = (file_info*) mng_get_userdata (mng); + + //if (!ms->rowdata) + // ms->rowdata = (unsigned char*) malloc (ms->w); + //if (!ms->rowdata) + // return MNG_NULL; + + //make_pal_row (ms, line, ms->rowdata); + + // satisfying compiler + line = 0; + + return MNG_NULL; +} + +mng_bool MNG_DECL +mng_refresh_display (mng_handle mng, mng_uint32 x, mng_uint32 y, mng_uint32 w, mng_uint32 h) +{ + // not implemented + file_info* ms; + + ms = (file_info*) mng_get_userdata (mng); + + // satisfying compiler + x = y = w = h = 0; + + return MNG_TRUE; +} + +mng_uint32 MNG_DECL +mng_get_tickcount (mng_handle mng) +{ + // not implemented + file_info* ms; + static int tick = 0; + + ms = (file_info*) mng_get_userdata (mng); + + return tick += 50; +} + +mng_bool MNG_DECL +mng_set_timer (mng_handle mng, mng_uint32 msecs) +{ + // not implemented + file_info* ms; + + ms = (file_info*) mng_get_userdata (mng); + ms->delay = msecs; + + return MNG_TRUE; +} diff --git a/Engine/lib/lmng/contrib/msvc/makemng/makemng.dsp b/Engine/lib/lmng/contrib/msvc/makemng/makemng.dsp new file mode 100644 index 000000000..c5b5aca22 --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/makemng/makemng.dsp @@ -0,0 +1,114 @@ +# Microsoft Developer Studio Project File - Name="makemng" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=makemng - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "makemng.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "makemng.mak" CFG="makemng - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "makemng - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "makemng - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=xicl6.exe +RSC=rc.exe + +!IF "$(CFG)" == "makemng - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "." /I "..\..\src" /I "..\..\src\msvc++" /I "..\..\src\getopt" /D "NDEBUG" /D "WINDOWS" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "MNG_USE_DLL" /D "MNG_SKIP_LCMS" /D "MNG_SKIP_IJG6B" /D "ZLIB_DLL" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=xilink6.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib comdlg32.lib advapi32.lib shell32.lib uuid.lib zlib.lib libmng.lib /nologo /subsystem:console /machine:I386 /libpath:"." + +!ELSEIF "$(CFG)" == "makemng - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "." /I "..\..\src" /I "..\..\src\msvc++" /I "..\..\src\getopt" /D "_DEBUG" /D "ZLIB_DLL" /D "WINDOWS" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "MNG_USE_DLL" /D "MNG_SKIP_LCMS" /D "MNG_SKIP_IJG6B" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=xilink6.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib comdlg32.lib advapi32.lib shell32.lib uuid.lib zlib.lib libmng.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /libpath:"." + +!ENDIF + +# Begin Target + +# Name "makemng - Win32 Release" +# Name "makemng - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\filelist.c +# End Source File +# Begin Source File + +SOURCE=..\..\src\getopt\getopt.c +# End Source File +# Begin Source File + +SOURCE=.\makemng.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\getopt.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/Engine/lib/lmng/contrib/msvc/makemng/makemng.dsw b/Engine/lib/lmng/contrib/msvc/makemng/makemng.dsw new file mode 100644 index 000000000..ab12fac01 --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/makemng/makemng.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "makemng"=.\makemng.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/Engine/lib/lmng/contrib/msvc/mngplg/COPYING-LCMS b/Engine/lib/lmng/contrib/msvc/mngplg/COPYING-LCMS new file mode 100644 index 000000000..e98f0e17b --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/mngplg/COPYING-LCMS @@ -0,0 +1,515 @@ + + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/cur_ie.cur b/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/cur_ie.cur new file mode 100644 index 000000000..159ad4e86 Binary files /dev/null and b/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/cur_ie.cur differ diff --git a/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/cur_ns.cur b/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/cur_ns.cur new file mode 100644 index 000000000..196bb25c1 Binary files /dev/null and b/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/cur_ns.cur differ diff --git a/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/license.txt b/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/license.txt new file mode 100644 index 000000000..6756e5e7e --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/license.txt @@ -0,0 +1,40 @@ +MNGPLG +A simple browser plug-in for the MNG image/animation file format. + +By Jason Summers +Web site: + + +COPYRIGHT NOTICE + +Copyright (c) 2000-2002 by Jason Summers + +THIS SOFTWARE IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT WITHOUT +ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR +FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, +YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL +ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE +THIS SOFTWARE AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING +ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THIS SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THIS SOFTWARE TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + +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 source code must not be misrepresented; + you must not claim that you wrote the original software. + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original source. + 3. Altered binary versions must not be misrepresented as being the + original. + 4. This Copyright notice may not be removed or altered from any source + or altered source distribution, although you may add a Copyright + notice for yourself for any code that you have written. diff --git a/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/npapidefs.h b/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/npapidefs.h new file mode 100644 index 000000000..3d6f82132 --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/npapidefs.h @@ -0,0 +1,245 @@ +// npapidefs.h +// minimal version of the defs from the NS plugin SDK + +#ifndef NPAPIDEFS_H +#define NPAPIDEFS_H + +#define NP_VERSION_MAJOR 0 +#define NP_VERSION_MINOR 11 + +#define NPVERS_HAS_STREAMOUTPUT 8 +#define NPVERS_HAS_NOTIFICATION 9 +#define NPVERS_HAS_LIVECONNECT 9 +#define NPVERS_HAS_WINDOWLESS 11 + +#define NPERR_NO_ERROR 0 +#define NPERR_GENERIC_ERROR 1 +#define NPERR_INVALID_INSTANCE_ERROR 2 +#define NPERR_INVALID_FUNCTABLE_ERROR 3 +#define NPERR_MODULE_LOAD_FAILED_ERROR 4 +#define NPERR_OUT_OF_MEMORY_ERROR 5 +#define NPERR_INVALID_PLUGIN_ERROR 6 +#define NPERR_INVALID_PLUGIN_DIR_ERROR 7 +#define NPERR_INCOMPATIBLE_VERSION_ERROR 8 +#define NPERR_INVALID_PARAM 9 +#define NPERR_INVALID_URL 10 +#define NPERR_FILE_NOT_FOUND 11 +#define NPERR_NO_DATA 12 +#define NPERR_STREAM_NOT_SEEKABLE 13 + +#define NP_EMBED 1 +#define NP_FULL 2 + +#define NP_NORMAL 1 +#define NP_SEEK 2 +#define NP_ASFILE 3 +#define NP_ASFILEONLY 4 + +#define NPRES_DONE 0 +#define NPRES_NETWORK_ERR 1 +#define NPRES_USER_BREAK 2 + +typedef unsigned short uint16; +typedef unsigned long uint32; +typedef short int16; +typedef long int32; + +typedef unsigned char NPBool; +typedef int16 NPError; +typedef int16 NPReason; +typedef char* NPMIMEType; +typedef HRGN NPRegion; + +typedef void* JRIGlobalRef; +struct JRIEnvInterface; +typedef struct JRIEnvInterface JRIEnvInterface; +typedef const JRIEnvInterface* JRIEnv; +struct _jobject; +typedef struct _jobject *jobject; +typedef jobject jref; + +typedef struct _NPRect +{ + uint16 top; + uint16 left; + uint16 bottom; + uint16 right; +} NPRect; + +typedef struct _NPP +{ + void* pdata; + void* ndata; +} NPP_t; + +typedef NPP_t* NPP; + +typedef struct _NPStream +{ + void* pdata; + void* ndata; + const char* url; + uint32 end; + uint32 lastmodified; + void* notifyData; +} NPStream; + +typedef enum { + NPPVpluginNameString = 1, + NPPVpluginDescriptionString, + NPPVpluginWindowBool, + NPPVpluginTransparentBool +} NPPVariable; + +typedef enum { + NPNVxDisplay = 1, + NPNVxtAppContext, + NPNVnetscapeWindow, + NPNVjavascriptEnabledBool, + NPNVasdEnabledBool, + NPNVisOfflineBool +} NPNVariable; + +typedef enum { + NPWindowTypeWindow = 1, + NPWindowTypeDrawable +} NPWindowType; + +typedef struct _NPSavedData +{ + int32 len; + void* buf; +} NPSavedData; + +typedef struct _NPByteRange +{ + int32 offset; + uint32 length; + struct _NPByteRange* next; +} NPByteRange; + +typedef struct _NPFullPrint +{ + NPBool pluginPrinted; + NPBool printOne; + void* platformPrint; +} NPFullPrint; + +typedef struct _NPWindow +{ + void* window; + int32 x; + int32 y; + uint32 width; + uint32 height; + NPRect clipRect; + NPWindowType type; +} NPWindow; + +typedef struct _NPEmbedPrint +{ + NPWindow window; + void* platformPrint; +} NPEmbedPrint; + +typedef struct _NPPrint +{ + uint16 mode; + union + { + NPFullPrint fullPrint; + NPEmbedPrint embedPrint; + } print; +} NPPrint; + +typedef struct _NPEvent +{ + uint16 event; + uint32 wParam; + uint32 lParam; +} NPEvent; + + +typedef NPError (*NPP_NewUPP)(NPMIMEType,NPP,uint16,int16,char* argn[],char* argv[],NPSavedData*); +typedef NPError (*NPP_DestroyUPP)(NPP instance, NPSavedData** save); +typedef NPError (*NPP_SetWindowUPP)(NPP,NPWindow*); +typedef NPError (*NPP_NewStreamUPP)(NPP,NPMIMEType,NPStream*,NPBool,uint16*); +typedef NPError (*NPP_DestroyStreamUPP)(NPP,NPStream*,NPReason); +typedef int32 (*NPP_WriteReadyUPP)(NPP instance,NPStream*); +typedef int32 (*NPP_WriteUPP)(NPP,NPStream*,int32,int32,void*); +typedef void (*NPP_StreamAsFileUPP)(NPP,NPStream*,const char*); +typedef void (*NPP_PrintUPP)(NPP,NPPrint*); +typedef int16 (*NPP_HandleEventUPP)(NPP,void*); +typedef void (*NPP_URLNotifyUPP)(NPP,const char*,NPReason,void*); +typedef NPError (*NPP_GetValueUPP)(NPP,NPPVariable,void*); +typedef NPError (*NPP_SetValueUPP)(NPP,NPNVariable,void*); +typedef NPError (*NPN_GetValueUPP)(NPP,NPNVariable,void*); +typedef NPError (*NPN_SetValueUPP)(NPP,NPPVariable,void*); +typedef NPError (*NPN_GetURLNotifyUPP)(NPP,const char*,const char*,void*); +typedef NPError (*NPN_PostURLNotifyUPP)(NPP,const char*,const char*,uint32,const char*,NPBool,void*); +typedef NPError (*NPN_GetURLUPP)(NPP,const char*,const char*); +typedef NPError (*NPN_PostURLUPP)(NPP,const char*,const char*,uint32,const char*,NPBool); +typedef NPError (*NPN_RequestReadUPP)(NPStream*,NPByteRange*); +typedef NPError (*NPN_NewStreamUPP)(NPP,NPMIMEType,const char*,NPStream**); +typedef int32 (*NPN_WriteUPP)(NPP,NPStream*,int32,void*); +typedef NPError (*NPN_DestroyStreamUPP)(NPP,NPStream*,NPReason); +typedef void (*NPN_StatusUPP)(NPP instance, const char*); +typedef const char* (*NPN_UserAgentUPP)(NPP); +typedef void* (*NPN_MemAllocUPP)(uint32); +typedef void (*NPN_MemFreeUPP)(void*); +typedef uint32 (*NPN_MemFlushUPP)(uint32); +typedef void (*NPN_ReloadPluginsUPP)(NPBool); +typedef JRIEnv* (*NPN_GetJavaEnvUPP)(void); +typedef jref (*NPN_GetJavaPeerUPP)(NPP); +typedef void (*NPN_InvalidateRectUPP)(NPP,NPRect*); +typedef void (*NPN_InvalidateRegionUPP)(NPP,NPRegion); +typedef void (*NPN_ForceRedrawUPP)(NPP); + + +typedef struct _NPPluginFuncs { + uint16 size; + uint16 version; + NPP_NewUPP newp; + NPP_DestroyUPP destroy; + NPP_SetWindowUPP setwindow; + NPP_NewStreamUPP newstream; + NPP_DestroyStreamUPP destroystream; + NPP_StreamAsFileUPP asfile; + NPP_WriteReadyUPP writeready; + NPP_WriteUPP write; + NPP_PrintUPP print; + NPP_HandleEventUPP event; + NPP_URLNotifyUPP urlnotify; + JRIGlobalRef javaClass; + NPP_GetValueUPP getvalue; + NPP_SetValueUPP setvalue; +} NPPluginFuncs; + +typedef struct _NPNetscapeFuncs { + uint16 size; + uint16 version; + NPN_GetURLUPP geturl; + NPN_PostURLUPP posturl; + NPN_RequestReadUPP requestread; + NPN_NewStreamUPP newstream; + NPN_WriteUPP write; + NPN_DestroyStreamUPP destroystream; + NPN_StatusUPP status; + NPN_UserAgentUPP uagent; + NPN_MemAllocUPP memalloc; + NPN_MemFreeUPP memfree; + NPN_MemFlushUPP memflush; + NPN_ReloadPluginsUPP reloadplugins; + NPN_GetJavaEnvUPP getJavaEnv; + NPN_GetJavaPeerUPP getJavaPeer; + NPN_GetURLNotifyUPP geturlnotify; + NPN_PostURLNotifyUPP posturlnotify; + NPN_GetValueUPP getvalue; + NPN_SetValueUPP setvalue; + NPN_InvalidateRectUPP invalidaterect; + NPN_InvalidateRegionUPP invalidateregion; + NPN_ForceRedrawUPP forceredraw; +} NPNetscapeFuncs; + + +#endif // NPAPIDEFS_H diff --git a/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/npmngplg.c b/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/npmngplg.c new file mode 100644 index 000000000..91719bdf3 --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/npmngplg.c @@ -0,0 +1,1936 @@ +/* -*- Mode: C; tab-width: 4; -*- */ +/* npmngplg.c + * MNG browser plugin + * By Jason Summers + * Based on libmng by Gerard Juyn + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "resource.h" +#include "libmng.h" +#include "jversion.h" // part of libjpeg +#include "zlib.h" // for zlibVersion +#include "npapidefs.h" + +#define MNGPLG_CMS +//#define MNGPLG_TRACE + +#define IDBASE 47000 +#define ID_SAVEAS (IDBASE+0) +#define ID_COPYIMAGE (IDBASE+1) +#define ID_COPYURL (IDBASE+2) +#define ID_VIEWIMAGE (IDBASE+3) +#define ID_ABOUT (IDBASE+4) +#define ID_FREEZE (IDBASE+5) +#define ID_RESTARTANIM (IDBASE+6) +#define ID_COPYLINKLOC (IDBASE+7) +#define ID_STOPANIM (IDBASE+8) +#define ID_SHOWERROR (IDBASE+9) +#define ID_PROPERTIES (IDBASE+10) + +#define MNGPLGVERS "1.0.1" + +/* instance-specific data */ +typedef struct pluginstruct_ +{ + NPWindow* fWindow; + uint16 fMode; + HWND fhWnd; + WNDPROC fDefaultWindowProc; + NPP instance; + + mng_handle mng; + +#define STATE_INIT 0 +#define STATE_LOADING 1 // stream opened +#define STATE_VALIDFRAME 2 // at least one frame has been displayed +#define STATE_LOADED 3 // image loaded; stream closed + + +#define MAXLEN_TEXT 5000 + +#define MAXLEN_URL 300 +#define MAXLEN_TARGET 100 + + // I think I'm not doing this very well. Probably there really needs to be + // two state variables, one for loading from the network, and one for + // the libmng processing. (or use libmng's new getstate API?) + int loadstate; + + int paintedyet; + + int scrolling; // allow scrolling of the image? + int xscrollpos, yscrollpos; + int windowwidth, windowheight; // client size of current window + + int diblinesize; + DWORD dibsize; + DWORD filesize; + DWORD libmngpos; // count of bytes that have been sent to libmng + DWORD byteswanted; // libmng asked for this many more bytes (add to libmngpos) + + unsigned char *mngdata; // stores the MNG file in memory + DWORD bytesloaded; // + DWORD bytesalloc; // size of mngdata + int needresume; // if previous mng_readdisplay call returned NEEDMOREDATA + + char *textdata; + + int errorflag; // set if an error occurs that prevents displaying the image + char errormsg[256]; + + unsigned char *lpdib; // pointer to header section of dib + unsigned char *lpdibbits; // pointer to "bits" section of dib (follows the header) + LPBITMAPINFOHEADER lpdibinfo; // alias for lpdib + int frozen; + int timer_set; + int timer2_set; + int dynamicmng; + int mouse_over_mng; + int mouse_captured; + + int force_bgcolor; + mng_uint16 bg_r,bg_g,bg_b; // background color + + unsigned char url[MAX_PATH]; // the url of the stream + + int islink; + HCURSOR linkcursor; + HBRUSH bkgdbrush; + unsigned char linkurl[MAXLEN_URL]; + unsigned char linktarget[MAXLEN_TARGET]; + +} PluginInstance; + + +/* global variables */ + +#ifdef MNGPLG_TRACE +static FILE *tracefile; +#endif + +static const char* gInstanceLookupString = "pdata"; +static HMODULE g_hInst = NULL; +static HCURSOR hcurHandNS; +static HFONT hfontMsg; + +/* function prototypes */ +LRESULT CALLBACK DlgProcAbout(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK DlgProcProp(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); +LRESULT CALLBACK PluginWindowProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); + +void set_scrollbars(PluginInstance *This); + + +//////////////////////////// NPN_functions + +static NPNetscapeFuncs* g_pNavigatorFuncs; +static NPPluginFuncs* g_pluginFuncs; + +static const char* NPN_UserAgent(NPP instance) +{ + return g_pNavigatorFuncs->uagent(instance); +} + +static NPError NPN_GetURL(NPP instance, const char *url, const char *target) +{ + return g_pNavigatorFuncs->geturl(instance, url, target); +} + + + +/* ----------------------------------------------------------------------- */ + +BOOL APIENTRY DllMain(HANDLE hModule, DWORD reason, LPVOID lpReserved) +{ + switch(reason) { + case DLL_PROCESS_ATTACH: + g_hInst=hModule; + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + break; + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + + +/* ----------------------------------------------------------------------- */ + +static void warn(PluginInstance *This, char *fmt, ...) +{ + va_list ap; + char buf[2048]; + HWND hwnd; + + va_start(ap, fmt); + wvsprintf(buf,fmt, ap); + va_end(ap); + + if(This) hwnd= This->fhWnd; + else hwnd=NULL; + + MessageBox(hwnd,buf,"MNG Plug-in",MB_OK|MB_ICONWARNING); +} + + +static void set_error(PluginInstance *This, char *fmt, ...) +{ + va_list ap; + char buf[2048]; + HWND hwnd; + + va_start(ap, fmt); + wvsprintf(buf,fmt, ap); + va_end(ap); + + if(This) hwnd= This->fhWnd; + else hwnd=NULL; + + This->errorflag=1; + lstrcpyn(This->errormsg,buf,256); + + if(This->lpdib) { + free(This->lpdib); + This->lpdib=NULL; + } + This->xscrollpos = This->yscrollpos = 0; + + if(This->fhWnd) + InvalidateRect(This->fhWnd,NULL,TRUE); +} + +/* ----------------------------------------------------------------------- */ +// MNG callbacks +#define MNGPLG_CALLBACK MNG_DECL + +static mng_ptr MNGPLG_CALLBACK memallocfunc(mng_size_t n) +{ + return (mng_ptr) calloc(n,1); +} + + +static void MNGPLG_CALLBACK memfreefunc(mng_ptr p, mng_size_t n) +{ + free((void*)p); +} + + +static mng_bool MNGPLG_CALLBACK callback_openstream (mng_handle mng) +{ +// PluginInstance *This; +// This = (PluginInstance*) mng_get_userdata(mng); + return MNG_TRUE; +} + +static mng_bool MNGPLG_CALLBACK callback_closestream (mng_handle mng) +{ + PluginInstance *This; + This = (PluginInstance*) mng_get_userdata(mng); + This->loadstate = STATE_LOADED; // this is probably redundant + + return MNG_TRUE; +} + + +static mng_bool MNGPLG_CALLBACK callback_readdata (mng_handle mng,mng_ptr pBuf, + mng_uint32 Buflen,mng_uint32 *pRead) +{ + int n; + PluginInstance *This; + This = (PluginInstance*) mng_get_userdata(mng); + +#ifdef MNGPLG_TRACE + fprintf(tracefile,"readdata callback buflen=%d loadstate=%d bytesloaded=%d libmngpos=%d\n", + Buflen,This->loadstate,This->bytesloaded, This->libmngpos); +#endif + + + // do we have enough data available? + if(This->bytesloaded - This->libmngpos >= Buflen) { + CopyMemory(pBuf,&This->mngdata[This->libmngpos],Buflen); + (*pRead)= Buflen; + This->libmngpos += Buflen; + +#ifdef MNGPLG_TRACE + fprintf(tracefile,"returning full: %d\n",Buflen); +#endif + This->byteswanted=0; + return MNG_TRUE; + } + else if(This->loadstate>=STATE_LOADED) { + // We don't have the data it asked for, but we're at the end + // of file, so send it anyway...? + + n=This->bytesloaded-This->libmngpos; + + if(n>0) { + CopyMemory(pBuf,&This->mngdata[This->libmngpos],n); + This->libmngpos+=n; + } + (*pRead)=n; + // so what do we return? +#ifdef MNGPLG_TRACE + fprintf(tracefile,"returning partial: %d\n",n); +#endif + This->byteswanted=0; + return MNG_TRUE; + } + + // else we don't yet have the data it's requesting +#ifdef MNGPLG_TRACE + fprintf(tracefile,"returning 0\n"); +#endif + (*pRead)=0; + This->byteswanted=Buflen; + return MNG_TRUE; +} + + +static mng_bool MNGPLG_CALLBACK callback_processheader(mng_handle mng,mng_uint32 iWidth,mng_uint32 iHeight) +{ + PluginInstance *This; + This = (PluginInstance*) mng_get_userdata(mng); + + This->diblinesize = (((iWidth * 24)+31)/32)*4; + This->dibsize = sizeof(BITMAPINFOHEADER) + This->diblinesize*iHeight; + This->lpdib = calloc(This->dibsize,1); + This->lpdibinfo = (LPBITMAPINFOHEADER)This->lpdib; + This->lpdibbits = &This->lpdib[sizeof(BITMAPINFOHEADER)]; + ZeroMemory((void*)This->lpdib,sizeof(BITMAPINFOHEADER)); + This->lpdibinfo->biSize = sizeof(BITMAPINFOHEADER); + This->lpdibinfo->biWidth = iWidth; + This->lpdibinfo->biHeight = iHeight; + This->lpdibinfo->biPlanes = 1; + This->lpdibinfo->biBitCount = 24; + + mng_set_canvasstyle (mng, MNG_CANVAS_BGR8); + +/* if(This->fhWnd) { + if((int)iWidth > This->windowwidth || (int)iHeight > This->windowheight) { + This->scrolling=1; + } + } */ + + set_scrollbars(This); + return MNG_TRUE; +} + + +static mng_ptr MNGPLG_CALLBACK callback_getcanvasline (mng_handle mng, mng_uint32 iLinenr) +{ + unsigned char *pp; + + PluginInstance *This; + This = (PluginInstance*) mng_get_userdata(mng); + pp = (&This->lpdibbits[(This->lpdibinfo->biHeight-1-iLinenr)*This->diblinesize]); + return (mng_ptr) pp; +} + +static mng_bool MNGPLG_CALLBACK callback_refresh (mng_handle mng, mng_uint32 iLeft, mng_uint32 iTop, + mng_uint32 iRight, mng_uint32 iBottom) +{ + PluginInstance *This; + RECT rect; + + This = (PluginInstance*) mng_get_userdata(mng); + + if(This->loadstateloadstate=STATE_VALIDFRAME; + } + + if(This->fhWnd) { + if(This->paintedyet) { + rect.left= iLeft - This->xscrollpos; + rect.top= iTop - This->yscrollpos; + rect.right= iLeft+iRight; + rect.bottom= iTop+iBottom; + + InvalidateRect(This->fhWnd,&rect,FALSE); + } + else { + // Make sure the first paint clears the whole plugin window + InvalidateRect(This->fhWnd,NULL,TRUE); + This->paintedyet=1; + } + UpdateWindow(This->fhWnd); + } + return MNG_TRUE; +} + +static mng_uint32 MNGPLG_CALLBACK callback_gettickcount (mng_handle mng) +{ + return GetTickCount(); +} + + +static mng_bool MNGPLG_CALLBACK callback_settimer (mng_handle mng,mng_uint32 iMsecs) +{ + PluginInstance *This; + This = (PluginInstance*) mng_get_userdata(mng); + + if(This->fhWnd) { + if(!SetTimer(This->fhWnd,1,(UINT)iMsecs,NULL)) { + warn(This,"Unable to create a timer for animation"); + This->frozen=1; + //return MNG_FALSE; + return MNG_TRUE; + } + This->timer_set=1; + } + return MNG_TRUE; +} + +static mng_bool MNGPLG_CALLBACK callback_processtext(mng_handle mng, + mng_uint8 iType, mng_pchar zKeyword, mng_pchar zText, + mng_pchar zLanguage, mng_pchar zTranslation) +{ + PluginInstance *This; + int pos,i; + + This = (PluginInstance*) mng_get_userdata(mng); + + if(!This->textdata) { + This->textdata=(char*)malloc(MAXLEN_TEXT+10); + if(!This->textdata) return MNG_TRUE; + lstrcpy(This->textdata,""); + } + + pos=lstrlen(This->textdata); + if(pos>=(MAXLEN_TEXT-10)) return MNG_TRUE; + + if(pos>0) { /* separate items with a blank line */ + This->textdata[pos++]='\r'; + This->textdata[pos++]='\n'; + This->textdata[pos++]='\r'; + This->textdata[pos++]='\n'; + } + + for(i=0;zKeyword[i];i++) { + if(postextdata[pos++]=zKeyword[i]; + } + This->textdata[pos++]=':'; + This->textdata[pos++]=' '; + + for(i=0;zText[i];i++) { + if(postextdata[pos++]='\r'; + } + This->textdata[pos++]=zText[i]; + } + } + This->textdata[pos++]='\0'; + + return MNG_TRUE; +} + +#ifdef MNGPLG_TRACE +static mng_bool MNGPLG_CALLBACK callback_traceproc (mng_handle mng, + mng_int32 iFuncnr, + mng_int32 iFuncseq, + mng_pchar zFuncname) +{ + if(tracefile) { + fprintf(tracefile,"%d\t%d\t%d\t%s\n",(int)mng,iFuncnr,iFuncseq,zFuncname); + } + return MNG_TRUE; +} +#endif + +/* ----------------------------------------------------------------------- */ +static int file_exists(const char *fn) +{ + HANDLE h; + + // try to open with no access + h=CreateFile(fn,0,FILE_SHARE_READ,NULL,OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL,NULL); + if(h == INVALID_HANDLE_VALUE) { return 0; } + CloseHandle(h); + return 1; +} + +static void handle_read_error(PluginInstance *This, mng_retcode rv) +{ + mng_int8 iSeverity; + mng_chunkid iChunkname; + mng_uint32 iChunkseq; + mng_int32 iExtra1; + mng_int32 iExtra2; + mng_pchar zErrortext; + +#ifdef MNGPLG_TRACE + fprintf(tracefile,"returned: %d\n",rv); +#endif + + switch(rv) { + case MNG_NOERROR: case MNG_NEEDTIMERWAIT: + break; + + case MNG_NEEDMOREDATA: + if(This->loadstate>=STATE_LOADED) { + set_error(This,"Unexpected end of file"); + } + else { + This->needresume=1; + } + break; + + case MNG_INVALIDSIG: + set_error(This,"Invalid or missing MNG file (maybe a 404 Not Found error)"); + break; + + default: + + mng_getlasterror(This->mng, &iSeverity,&iChunkname,&iChunkseq,&iExtra1, + &iExtra2,&zErrortext); + + if(zErrortext) { + set_error(This,"Error reported by libmng (%d)\r\n\r\n%s",(int)rv,zErrortext); + } + else { + set_error(This,"Error %d reported by libmng",(int)rv); + } + } +} + +#ifdef MNGPLG_CMS + +static int init_color_management(PluginInstance *This) +{ + mng_set_outputsrgb(This->mng); + return 1; +} + +#endif + +// return 1 if okay +static int my_init_mng(PluginInstance *This) +{ + mng_retcode rv; + int err; + + This->mng = mng_initialize((mng_ptr)This,memallocfunc,memfreefunc,NULL); + //(mng_memalloc) (mng_memfree) + +#ifdef MNGPLG_CMS + init_color_management(This); +#endif + + err=0; + rv=mng_setcb_openstream (This->mng, callback_openstream ); if(rv) err++; + rv=mng_setcb_closestream (This->mng, callback_closestream ); if(rv) err++; + rv=mng_setcb_readdata (This->mng, callback_readdata ); if(rv) err++; + rv=mng_setcb_processheader (This->mng, callback_processheader); if(rv) err++; + rv=mng_setcb_getcanvasline (This->mng, callback_getcanvasline); if(rv) err++; + rv=mng_setcb_refresh (This->mng, callback_refresh ); if(rv) err++; + rv=mng_setcb_gettickcount (This->mng, callback_gettickcount ); if(rv) err++; + rv=mng_setcb_settimer (This->mng, callback_settimer ); if(rv) err++; + rv=mng_setcb_processtext (This->mng, callback_processtext ); if(rv) err++; + +#ifdef MNGPLG_TRACE + rv=mng_setcb_traceproc (This->mng, callback_traceproc ); if(rv) err++; +#endif + if(err) { + warn(This,"Error setting libmng callback functions"); + return 0; + } + + rv= mng_set_suspensionmode (This->mng,MNG_TRUE); + if(rv) { + warn(This,"Error setting suspension mode"); + return 0; + } + + // if the web page author provided a bgcolor, use it + if(This->force_bgcolor) { + rv=mng_set_bgcolor (This->mng, This->bg_r, This->bg_g, This->bg_b); + } + +#ifdef MNGPLG_TRACE + fprintf(tracefile,"initial readdisplay\n"); +#endif + + handle_read_error(This, mng_readdisplay(This->mng) ); + return 1; +} + +/* Global initialization */ +static NPError NPP_Initialize(void) +{ + if(!g_hInst) { + warn(NULL,"MNG plugin error: Cannot load resources"); + } + +#ifdef MNGPLG_TRACE + tracefile=fopen("c:\\temp\\mngtrace.txt","w"); +#endif + +#ifndef IDC_HAND +#define IDC_HAND MAKEINTRESOURCE(32649) +#endif + hcurHandNS = LoadCursor(NULL,IDC_HAND); + if(!hcurHandNS) { + hcurHandNS=LoadCursor(g_hInst,"CURHAND_NS"); + } + + hfontMsg=CreateFont(-12,0,0,0,FW_DONTCARE,TRUE,0,0,ANSI_CHARSET, + OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DRAFT_QUALITY, + VARIABLE_PITCH|FF_SWISS,"Arial"); + return NPERR_NO_ERROR; +} + +/* Global shutdown */ +static void NPP_Shutdown(void) +{ +#ifdef MNGPLG_TRACE + if(tracefile) { + fclose(tracefile); + tracefile=NULL; + } +#endif + if(hfontMsg) DeleteObject((HGDIOBJ)hfontMsg); + return; +} + + +static unsigned char gethex(const char *s) +{ + int v[2]; + int i; + + v[0]=v[1]=0; + for(i=0;i<2;i++) { + if(s[i]>='a' && s[i]<='f') v[i]=s[i]-87; + if(s[i]>='A' && s[i]<='F') v[i]=s[i]-55; + if(s[i]>='0' && s[i]<='9') v[i]=s[i]-48; + } + return (unsigned char)(v[0]*16+v[1]); +} + +static void hexcolor2rgb(const char *s, mng_uint16 *r, mng_uint16 *g, mng_uint16 *b) +{ + if(lstrlen(s)!=7) return; + if(s[0]!='#') return; + (*r)= gethex(&s[1]); (*r)= ((*r)<<8)|(*r); + (*g)= gethex(&s[3]); (*g)= ((*g)<<8)|(*g); + (*b)= gethex(&s[5]); (*b)= ((*b)<<8)|(*b); +} + +static void find_window_size(PluginInstance *This) +{ + RECT r; + if(This->scrolling) { // make sure scrollbars exist if needed + ShowScrollBar(This->fhWnd,SB_BOTH,TRUE); + } + GetClientRect(This->fhWnd, &r); + This->windowwidth=r.right; + This->windowheight=r.bottom; +} + +static void set_scrollbars(PluginInstance *This) +{ + SCROLLINFO si; + int maxpos; + + if(!This->scrolling) return; + if(!This->fhWnd) return; + + ZeroMemory(&si,sizeof(SCROLLINFO)); + si.cbSize = sizeof(SCROLLINFO); + + // horizontal + if(This->lpdib) { + maxpos=This->lpdibinfo->biWidth-This->windowwidth; + if(maxpos<0) maxpos=0; + if(This->xscrollpos>maxpos) This->xscrollpos=maxpos; + if(This->xscrollpos<0) This->xscrollpos=0; + + si.fMask = SIF_ALL|SIF_DISABLENOSCROLL; + si.nMin = 0; + si.nMax = This->lpdibinfo->biWidth -1; + si.nPage = This->windowwidth; + si.nPos = This->xscrollpos; + } + else { // no image to display + si.fMask = SIF_ALL|SIF_DISABLENOSCROLL; + si.nMin = 0; + si.nMax = 0; + si.nPage = 1; + si.nPos = 0; + } + SetScrollInfo(This->fhWnd,SB_HORZ,&si,TRUE); + + // vertical + if(This->lpdib) { + maxpos=This->lpdibinfo->biHeight-This->windowheight; + if(maxpos<0) maxpos=0; + if(This->yscrollpos>maxpos) This->yscrollpos=maxpos; + if(This->yscrollpos<0) This->yscrollpos=0; + + si.fMask = SIF_ALL|SIF_DISABLENOSCROLL; + si.nMin = 0; + si.nMax = This->lpdibinfo->biHeight -1; + si.nPage = This->windowheight; + si.nPos = This->yscrollpos; + } + SetScrollInfo(This->fhWnd,SB_VERT,&si,TRUE); +} + + +#define SCROLLLINE 40 + +static void scrollmsg(PluginInstance *This, UINT msg,int code, short int pos) +{ + int page; + int dx, dy; // amount of scrolling + int x_orig, y_orig; + + if(!This->scrolling) return; + if(!This->lpdib) return; + + x_orig=This->xscrollpos; + y_orig=This->yscrollpos; + + if(msg==WM_HSCROLL) { + page=This->windowwidth-15; + if(pagexscrollpos-=SCROLLLINE; break; + case SB_LINERIGHT: This->xscrollpos+=SCROLLLINE; break; + case SB_PAGELEFT: This->xscrollpos-=page; break; + case SB_PAGERIGHT: This->xscrollpos+=page; break; + case SB_LEFT: This->xscrollpos=0; break; + case SB_RIGHT: This->xscrollpos=This->lpdibinfo->biWidth; break; + case SB_THUMBTRACK: This->xscrollpos=pos; break; + default: return; + } + set_scrollbars(This); + } + else if(msg==WM_VSCROLL) { + page=This->windowheight-15; + if(pageyscrollpos-=SCROLLLINE; break; + case SB_LINEDOWN: This->yscrollpos+=SCROLLLINE; break; + case SB_PAGEUP: This->yscrollpos-=page; break; + case SB_PAGEDOWN: This->yscrollpos+=page; break; + case SB_TOP: This->yscrollpos=0; break; + case SB_BOTTOM: This->yscrollpos=This->lpdibinfo->biHeight; break; + case SB_THUMBTRACK: This->yscrollpos=pos; break; + default: return; + } + set_scrollbars(This); + } + + dx= x_orig - This->xscrollpos; + dy= y_orig - This->yscrollpos; + + if(dx || dy) { // if any change + // GetClientRect(This->fhWnd,&cliprect); + ScrollWindowEx(This->fhWnd,dx,dy,NULL,NULL /*&cliprect*/,NULL,NULL,SW_INVALIDATE); + } +} + +/* Once-per-instance initialization */ +static NPError NPP_New(NPMIMEType pluginType,NPP instance,uint16 mode, + int16 argc,char* argn[],char* argv[],NPSavedData* saved) +{ + PluginInstance* This; + int i; + + if (instance == NULL) { + return NPERR_INVALID_INSTANCE_ERROR; + } + instance->pdata = calloc(sizeof(PluginInstance),1); + + + This = (PluginInstance*) instance->pdata; + if (This == NULL) { + return NPERR_OUT_OF_MEMORY_ERROR; + } + + This->force_bgcolor=1; + This->bg_r = This->bg_g = This->bg_b = 0xffff; + + /* record some info for later lookup */ + This->fWindow = NULL; + This->fMode = mode; + + This->fhWnd = NULL; + This->fDefaultWindowProc = NULL; + This->instance = instance; /* save the instance id for reverse lookups */ + This->scrolling = (mode==NP_FULL); + This->xscrollpos = This->yscrollpos = 0; + This->windowwidth = This->windowheight = 0; + + This->loadstate = STATE_INIT; + This->paintedyet = 0; + This->mng=0; + This->lpdib=NULL; + lstrcpy(This->url,""); + This->frozen=0; + This->needresume=0; + This->errorflag=0; + lstrcpy(This->errormsg,""); + + This->dibsize = This->filesize = 0; + + lstrcpy(This->linkurl,""); + lstrcpy(This->linktarget,"_self"); + This->islink=0; + + This->timer_set=0; + This->timer2_set=0; + This->dynamicmng= -1; + This->mouse_over_mng=0; + This->mouse_captured=0; + + This->linkcursor = hcurHandNS; + + // examine the tag arguments + for(i=0;iforce_bgcolor=1; + hexcolor2rgb(argv[i],&This->bg_r,&This->bg_g,&This->bg_b); + } + else if(!_stricmp(argn[i],"href")) { + lstrcpyn(This->linkurl,argv[i],MAXLEN_URL); + This->islink=1; + } + else if(!_stricmp(argn[i],"target")) { + lstrcpyn(This->linktarget,argv[i],MAXLEN_TARGET); + } + } + + This->bkgdbrush=NULL; + if(This->force_bgcolor) + This->bkgdbrush=CreateSolidBrush(RGB(This->bg_r,This->bg_g,This->bg_b)); + + return NPERR_NO_ERROR; +} + +static void BeforeDestroyWindow(PluginInstance *This) +{ + if(This->timer_set) { + KillTimer(This->fhWnd,1); + This->timer_set=0; + } + if(This->timer2_set) { + KillTimer(This->fhWnd,2); + This->timer2_set=0; + } + if(This->mouse_captured) { + ReleaseCapture(); + This->mouse_captured=0; + } + + SetWindowLong( This->fhWnd, GWL_WNDPROC, (LONG)This->fDefaultWindowProc); // unsubclass + This->fDefaultWindowProc = NULL; + This->fhWnd = NULL; +} + +static NPError NPP_Destroy(NPP instance, NPSavedData** save) +{ + PluginInstance* This; + + if (instance == NULL) return NPERR_INVALID_INSTANCE_ERROR; + This = (PluginInstance*) instance->pdata; + + if(!This) return NPERR_INVALID_INSTANCE_ERROR; + + if(This->mng) { + This->dynamicmng=0; + mng_cleanup(&This->mng); + This->mng=0; + } + if(This->lpdib) { + free(This->lpdib); + This->lpdib=NULL; + } + if(This->mngdata) { + free(This->mngdata); + This->mngdata=NULL; + This->bytesalloc=0; + } + if(This->textdata) { + free(This->textdata); + This->textdata=NULL; + } + + if( This->fhWnd ) { // un-subclass the plugin window + BeforeDestroyWindow(This); + } + + if(This->bkgdbrush) DeleteObject((HGDIOBJ)This->bkgdbrush); + + if(This) { + if(instance->pdata) free(instance->pdata); + instance->pdata = NULL; + } + + return NPERR_NO_ERROR; +} + +/* Browser is providing us with a window */ +static NPError NPP_SetWindow(NPP instance, NPWindow* window) +{ + NPError result = NPERR_NO_ERROR; + PluginInstance* This; + + if (instance == NULL) return NPERR_INVALID_INSTANCE_ERROR; + + This = (PluginInstance*) instance->pdata; + + if( This->fhWnd != NULL ) { /* If we already have a window... */ + + if( (window == NULL) || ( window->window == NULL ) ) { + /* There is now no window to use. get rid of the old + * one and exit. */ + BeforeDestroyWindow(This); + This->fWindow=window; + return NPERR_NO_ERROR; + } + + else if ( This->fhWnd == (HWND) window->window ) { + /* The new window is the same as the old one. Redraw and get out. */ + This->fWindow=window; + + InvalidateRect( This->fhWnd, NULL, FALSE ); + /* UpdateWindow( This->fhWnd ); */ + return NPERR_NO_ERROR; + } + else { + /* Unsubclass the old window, so that we can subclass the new + * one later. */ + BeforeDestroyWindow(This); + } + } + else if( (window == NULL) || ( window->window == NULL ) ) { + /* We can just get out of here if there is no current + * window and there is no new window to use. */ + This->fWindow=window; + + return NPERR_NO_ERROR; + } + + /* Subclass the new window so that we can begin drawing and + * receiving window messages. */ + This->fDefaultWindowProc = (WNDPROC)SetWindowLong( (HWND)window->window, GWL_WNDPROC, (LONG)PluginWindowProc); + This->fhWnd = (HWND) window->window; + SetProp( This->fhWnd, gInstanceLookupString, (HANDLE)This); + + This->fWindow = window; + find_window_size(This); + + set_scrollbars(This); + + InvalidateRect( This->fhWnd, NULL, TRUE ); + UpdateWindow( This->fhWnd ); + + return result; +} + +// browser is announcing its intent to send data to us +static NPError NPP_NewStream(NPP instance,NPMIMEType type,NPStream *stream, + NPBool seekable,uint16 *stype) { + PluginInstance* This; + + if(instance==NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + This = (PluginInstance*) instance->pdata; + if(!This) + return NPERR_GENERIC_ERROR; + + /* save the URL for later */ + lstrcpyn(This->url,stream->url,MAX_PATH); + + + This->libmngpos=0; + This->bytesloaded=0; + This->bytesalloc=0; + This->byteswanted=0; + This->mngdata=NULL; + This->textdata=NULL; + + // if we know the total length of the stream in advance + // (most of the time we will, hopefully), allocate that amount. + if(stream->end > 0) { + This->mngdata = malloc(stream->end); + This->bytesalloc= stream->end; + } + + my_init_mng(This); + + This->loadstate=STATE_LOADING; + + (*stype)=NP_NORMAL; + + return NPERR_NO_ERROR; +} + +static int32 NPP_WriteReady(NPP instance, NPStream *stream) +{ + /* Number of bytes ready to accept in NPP_Write() */ + /* We can handle any amount, so just return some really big number. */ + return (int32)0X0FFFFFFF; +} + +#define ALLOC_CHUNK_SIZE 131072 + +static int32 NPP_Write(NPP instance, NPStream *stream, int32 offset, int32 len, void *buffer) +{ + PluginInstance* This; + +#ifdef MNGPLG_TRACE + fprintf(tracefile,"NPP_Write offs=%d len=%d\n",offset,len); +#endif + + + if(!instance) return -1; + This = (PluginInstance*) instance->pdata; + if(!This) return -1; + if(len<1) return len; + + if(offset+len > (int)This->bytesalloc) { // oops, overflowed our memory buffer + This->bytesalloc += ALLOC_CHUNK_SIZE; + if(This->mngdata) { + This->mngdata=realloc(This->mngdata, This->bytesalloc); + } + else { // first time + This->mngdata=malloc(This->bytesalloc); + } + if(!This->mngdata) { + warn(This,"Cannot allocate memory for image (%d,%d,%p",offset,len,buffer); + return -1; + } + } + + // now we should have enough room to copy the data to memory + + CopyMemory(&This->mngdata[offset],buffer,len); + + This->bytesloaded = offset+len; + + // now, check if it's time to call mng_read_resume + if(This->needresume && + (This->bytesloaded >= (This->libmngpos + This->byteswanted)) ) + { + This->needresume=0; +// handle_read_error(This, mng_read_resume(This->mng) ); + +#ifdef MNGPLG_TRACE + fprintf(tracefile,"NPP_Write display_resume bytesloaded=%d libmngpos=%d byteswanted=%d\n", + This->bytesloaded,This->libmngpos,This->byteswanted); +#endif + + handle_read_error(This, mng_display_resume(This->mng) ); + } + + + return len; // The number of bytes accepted -- we always accept them all. +} + +/* DestroyStream gets called after the file has finished loading, + */ +static NPError NPP_DestroyStream(NPP instance, NPStream *stream, NPError reason) +{ + PluginInstance* This; + if(!instance) return NPERR_INVALID_INSTANCE_ERROR; + + This = (PluginInstance*) instance->pdata; +// if(reason==NPRES_DONE) { + This->filesize = This->bytesloaded; + This->loadstate = STATE_LOADED; +// } + + if(reason!=NPRES_DONE) { + set_error(This,"Image load failed or was canceled (%d)",(int)reason); + This->needresume=0; + if(This->timer_set) { KillTimer(This->fhWnd,1); This->timer_set=0; } + return NPERR_NO_ERROR; + } + +#ifdef MNGPLG_TRACE + fprintf(tracefile,"NPP_DestroyStream reason=%d needresume=%d\n",reason,This->needresume); +#endif + + if(This->needresume) { + This->needresume=0; +// handle_read_error(This, mng_read_resume(This->mng) ); + handle_read_error(This, mng_display_resume(This->mng) ); +// This->needresume=0; + } + + return NPERR_NO_ERROR; +} + + +static void NPP_StreamAsFile(NPP instance, NPStream *stream, const char* fname) +{ + return; +} + +// Print embedded plug-in (via the browser's Print command) +static void NPP_Print(NPP instance, NPPrint* printInfo) +{ + PluginInstance* This; + if (instance == NULL) return; + This = (PluginInstance*) instance->pdata; + + if(printInfo == NULL) { + // Some browsers (Netscape) set printInfo to NULL to tell the plugin + // to print in full page mode (this may be a bug). + // PrintFullPage(); -- full page printing not implemented + return; + } + + if (printInfo->mode == NP_FULL) { + /* the plugin is full-page, and the browser is giving it a chance + * to print in the manner of its choosing */ + void* platformPrint = printInfo->print.fullPrint.platformPrint; + NPBool printOne = printInfo->print.fullPrint.printOne; + + /* Setting this to FALSE and returning *should* cause the browser to + * call NPP_Print again, this time with mode=NP_EMBED. + * However, that doesn't happen with any browser I've ever seen :-(. + * Instead of the following line, you will probably need to implement + * printing yourself. You also might as well set pluginPrinted to TRUE, + * though every browser I've tested ignores it. */ + printInfo->print.fullPrint.pluginPrinted = FALSE; + /* or */ + /* PrintFullPage(); + * printInfo->print.fullPrint.pluginPrinted = TRUE; */ + } + else { // we are embedded, and the browser had provided a printer context + HDC pdc; + int prevstretchmode; + NPWindow* printWindow; + + if(This->loadstate < STATE_VALIDFRAME) return; + + printWindow= &(printInfo->print.embedPrint.window); + + /* embedPrint.platformPrint is a Windows device context in disguise */ + + /* The definition of NPWindow changed between API verion 0.9 and 0.11, + * increasing in size from 28 to 32 bytes. This normally makes it + * impossible for version 0.9 browsers to print version 0.11 plugins + * (because the platformPrint field ends up at the wrong offset) -- + * unless the plugin takes special care to detect this situation. + * To work around it, if we are compiled with API 0.11 or higher, + * and the browser is version 0.9 or earlier, we look for the HDC + * 4 bytes earlier, at offset 28 instead of 32 (of the embedPrint + * sub-structure). + */ + + if(sizeof(NPWindow)>28 && /* i.e. is plugin API >= 0.11? */ + HIBYTE(g_pNavigatorFuncs->version)==0 && + LOBYTE(g_pNavigatorFuncs->version)<=9) { + char *tmpc; + HDC *tmph; + + tmpc= (char*)&(printInfo->print.embedPrint); + tmph= (HDC*)&tmpc[28]; + pdc= *tmph; + } + else { + pdc= (HDC) (printInfo->print.embedPrint.platformPrint); + } + + if(!This->lpdib) return; + + prevstretchmode=SetStretchBltMode(pdc,COLORONCOLOR); + StretchDIBits(pdc, + printWindow->x,printWindow->y, + printWindow->width,printWindow->height, /* dest coords */ + 0,0,This->lpdibinfo->biWidth, This->lpdibinfo->biHeight, /* source coords */ + This->lpdibbits, (LPBITMAPINFO)This->lpdib, + DIB_RGB_COLORS,SRCCOPY); + if(prevstretchmode) SetStretchBltMode(pdc,prevstretchmode); + } + return; +} + + +/*+++++++++++++++++++++++++++++++++++++++++++++++++ + * NPP_URLNotify: + * Notifies the instance of the completion of a URL request. + +++++++++++++++++++++++++++++++++++++++++++++++++*/ +static void NPP_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData) +{ + return; +} + + +/**********************************************************************/ + +/* Try to make a filename from the url. Caller must provide fn[MAX_PATH] buffer. + * This function attempts to extract a bitmap filename from a URL, + * but if it doesn't look like it contains an appropriate name, + * it leaves it blank. */ +static void url2filename(char *fn, char *url) +{ + int title,ext,i; + + lstrcpy(fn,""); + ext=0; /* position of the file extention */ + title=0; /* position of the base filename */ + for(i=0;url[i];i++) { + if(url[i]=='.') ext=i+1; + if(url[i]=='/') title=i+1; + if(url[i]=='\\') title=i+1; // handle Microsoft's bogus file: "URLs" + if(url[i]==':') title=i+1; + if(url[i]=='=') title=i+1; + } + + if (!_stricmp(&url[ext],"mng") || + !_stricmp(&url[ext],"jng") || + !_stricmp(&url[ext],"png") ) + { + lstrcpyn(fn,&url[title],MAX_PATH); + } +} + +// sanitize string and escape '&'s for use in a menu +static void escapeformenu(unsigned char *s1) +{ + int f, t, len; + unsigned char s2[200]; + + t=0; + len=lstrlen(s1); if(len>50) len=50; + for(f=0;fmng || This->loadstatebytesloaded != This->filesize) + { + warn(This,"Image not loaded -- can't save"); + return; + } + + if(lstrlen(This->url)) { + url2filename(fn,This->url); + } + else { + lstrcpy(fn,""); + } + + ZeroMemory(&ofn,sizeof(OPENFILENAME)); + ofn.lStructSize=sizeof(OPENFILENAME); + ofn.hwndOwner=This->fhWnd; + ofn.nFilterIndex=1; + ofn.lpstrTitle="Save Image As..."; + ofn.lpstrFile=fn; + ofn.nMaxFile=MAX_PATH; + ofn.Flags=OFN_PATHMUSTEXIST|OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT; + + t=mng_get_sigtype(This->mng); + if(t==mng_it_png) { + ofn.lpstrDefExt="png"; // FIXME also give an option of MNG + ofn.lpstrFilter="PNG (*.png)\0*.png\0\0"; + } + else if(t==mng_it_jng) { + ofn.lpstrDefExt="jng"; // FIXME also give an option of MNG + ofn.lpstrFilter="JNG (*.jng)\0*.jng\0\0"; + } + else { + ofn.lpstrFilter="MNG (*.mng)\0*.mng\0\0"; + ofn.lpstrDefExt="mng"; + } + + if(GetSaveFileName(&ofn)) { + // save to filename: ofn.lpstrFile + hfile=CreateFile(ofn.lpstrFile,GENERIC_WRITE,FILE_SHARE_READ, + NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); + if(hfile==INVALID_HANDLE_VALUE) { + warn(This,"Unable to write file"); + } + else { + b=WriteFile(hfile, This->mngdata, This->filesize, + &byteswritten,NULL); + if(!b || byteswritten != This->filesize) { + warn(This,"Error writing file"); + } + CloseHandle(hfile); + } + } +} + + +static void CopyToClipboard(PluginInstance *This,unsigned char *mem,int size,UINT format) +{ + HGLOBAL hClip; + LPVOID lpClip; + + if(!mem) return; + + if(!OpenClipboard(NULL)) { + warn(This,"Can't open the clipboard"); + return; + } + + if(EmptyClipboard()) { + hClip=GlobalAlloc(GMEM_ZEROINIT|GMEM_MOVEABLE|GMEM_DDESHARE,size); + lpClip=GlobalLock(hClip); + if(lpClip) { + CopyMemory(lpClip,mem,size); + GlobalUnlock(hClip); + if(!SetClipboardData(format,hClip)) { + warn(This,"Can't set clipboard data"); + } + } + else { + warn(This,"Can't allocate memory for clipboard"); + } + } + else { + warn(This,"Can't clear the clipboard"); + } + CloseClipboard(); +} + +static void AboutDialog(PluginInstance *This) +{ + DialogBoxParam(g_hInst,"ABOUTDLG",This->fhWnd,(DLGPROC)DlgProcAbout,(LPARAM)This); +} + +static void PropDialog(PluginInstance *This) +{ + //if(This->textdata) + DialogBoxParam(g_hInst,"PROPDLG",This->fhWnd,(DLGPROC)DlgProcProp,(LPARAM)This); +} + +static void display_last_error(PluginInstance *This) +{ + if(This->errorflag) { + warn(This,"%s",This->errormsg); + } +} + +static void DynamicMNG_FireEvent(PluginInstance *This, mng_uint8 eventtype, POINTS pos) +{ + mng_retcode r; + if(!This->mng) return; + if(This->dynamicmng == 0) return; + if(This->dynamicmng == -1) { + r=mng_status_dynamic(This->mng); + if(r==MNG_FALSE) { + return; + } + else { + This->dynamicmng=1; + } + } + mng_trapevent(This->mng, eventtype, pos.x+This->xscrollpos, pos.y+This->yscrollpos); +} + + +static void ContextMenu(PluginInstance *This, HWND hwnd) +{ + int cmd; + HMENU menu; + POINT pt; + unsigned char buf[MAX_PATH], buf2[200]; + + pt.x=0; pt.y=0; + GetCursorPos(&pt); + + // create context menu dynamically + menu=CreatePopupMenu(); + if(This->errorflag) { + AppendMenu(menu,MF_ENABLED,ID_SHOWERROR,"SHOW ERROR MESSAGE"); + AppendMenu(menu,MF_SEPARATOR,0,NULL); + } + + AppendMenu(menu,(This->loadstate>=STATE_LOADED?MF_ENABLED:MF_GRAYED),ID_SAVEAS,"Save Image &As..."); + AppendMenu(menu,(This->lpdib?MF_ENABLED:MF_GRAYED),ID_COPYIMAGE,"&Copy Image"); + AppendMenu(menu,MF_ENABLED,ID_COPYURL,"Cop&y Image Location"); + if(This->islink) { + AppendMenu(menu,MF_ENABLED,ID_COPYLINKLOC,"Copy Link Location"); + } + + url2filename(buf,This->url); + escapeformenu(buf); + if(lstrlen(buf)) { + wsprintf(buf2,"View Image (%s)",buf); + } + else { + wsprintf(buf2,"View Image"); + } + AppendMenu(menu,MF_ENABLED,ID_VIEWIMAGE,buf2); + + + AppendMenu(menu,MF_SEPARATOR,0,NULL); + // AppendMenu(menu,(This->mng?MF_ENABLED:MF_GRAYED),ID_STOPANIM,"Stop Animation"); + + + AppendMenu(menu,(This->mng?MF_ENABLED:MF_GRAYED)| + (This->frozen?MF_CHECKED:MF_UNCHECKED),ID_FREEZE,"&Freeze Animation"); + + // AppendMenu(menu,(This->mng?MF_ENABLED:MF_GRAYED),ID_RESTARTANIM,"Restart Animation"); + + AppendMenu(menu,MF_SEPARATOR,0,NULL); + + AppendMenu(menu,MF_ENABLED,ID_PROPERTIES,"Properties..."); + + AppendMenu(menu,MF_ENABLED,ID_ABOUT,"About MNG Plug-in..."); + + cmd=TrackPopupMenuEx(menu, TPM_LEFTALIGN|TPM_TOPALIGN|TPM_NONOTIFY|TPM_RETURNCMD| + TPM_RIGHTBUTTON,pt.x,pt.y,hwnd,NULL); + + DestroyMenu(menu); + + switch(cmd) { + + case ID_STOPANIM: + if(This->mng) { + KillTimer(This->fhWnd,1); + This->timer_set=0; + mng_display_freeze(This->mng); + } + break; + + case ID_FREEZE: + This->frozen = !This->frozen; + if(This->frozen) { + KillTimer(This->fhWnd,1); + This->timer_set=0; + mng_display_freeze(This->mng); + } + else { + handle_read_error(This, mng_display_resume(This->mng) ); + + } + break; + + case ID_RESTARTANIM: + if(!This->frozen) { + KillTimer(This->fhWnd,1); + This->timer_set=0; + mng_display_freeze(This->mng); + } + This->frozen=1; + mng_display_reset(This->mng); + This->frozen=0; + handle_read_error(This, mng_display_resume(This->mng) ); + break; + + case ID_SAVEAS: + SaveImage(This); + break; + case ID_COPYIMAGE: + if(This->lpdib) { + CopyToClipboard(This,(unsigned char*)This->lpdib,This->dibsize,CF_DIB); + } + else { + warn(This,"No image to copy"); + } + break; + case ID_COPYURL: + CopyToClipboard(This,This->url,lstrlen(This->url)+1,CF_TEXT); + break; + case ID_COPYLINKLOC: + if(This->islink) { + CopyToClipboard(This,This->linkurl,lstrlen(This->linkurl)+1,CF_TEXT); + } + break; + case ID_VIEWIMAGE: + if(lstrlen(This->url)) + NPN_GetURL(This->instance,This->url,"_self"); + break; + case ID_PROPERTIES: + PropDialog(This); + break; + case ID_ABOUT: + AboutDialog(This); + break; + case ID_SHOWERROR: + display_last_error(This); + break; + } +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++ + * PluginWindowProc + * Handle the Windows window-event loop. + +++++++++++++++++++++++++++++++++++++++++++++++++*/ +static LRESULT CALLBACK PluginWindowProc( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) +{ + PluginInstance* This; + HDC hdc; + RECT rect; + + This = (PluginInstance*) GetProp(hWnd, gInstanceLookupString); + + if(!This) return DefWindowProc( hWnd, Msg, wParam, lParam); + + switch(Msg) { + + case WM_ERASEBKGND: + { + HBRUSH br; + hdc= (HDC)wParam; + + if(This->bkgdbrush) + br=This->bkgdbrush; + else + br=GetStockObject(GRAY_BRUSH); + + GetClientRect(hWnd,&rect); + FillRect(hdc,&rect,br); + return 1; + } + + case WM_HSCROLL: + case WM_VSCROLL: + scrollmsg(This,Msg,(int)(LOWORD(wParam)),(short int)(HIWORD(wParam))); + return 0; + + case WM_SIZE: + find_window_size(This); + set_scrollbars(This); + return 0; + + + case WM_CONTEXTMENU: case WM_RBUTTONUP: + ContextMenu(This, hWnd); + return 0; + + + case WM_SETCURSOR: + if(LOWORD(lParam)==HTCLIENT) { + if(This->islink) { + SetCursor(This->linkcursor); + return 1; + } + } + break; + + case WM_LBUTTONDOWN: + SetCapture(This->fhWnd); + This->mouse_captured=1; + + if(This->dynamicmng && This->mng && !This->errorflag) { + DynamicMNG_FireEvent(This,4,MAKEPOINTS(lParam)); + } + return 0; + + case WM_LBUTTONUP: + { + RECT rc; + POINT pt; + + if(This->mouse_captured) { + ReleaseCapture(); + This->mouse_captured=0; + } + if(This->dynamicmng && This->mng && !This->errorflag) { + DynamicMNG_FireEvent(This,5,MAKEPOINTS(lParam)); + } + + // if mouse is not over image, don't follow links, etc. + GetWindowRect(This->fhWnd,&rc); + GetCursorPos(&pt); + if(!PtInRect(&rc,pt)) return 0; + + if(This->islink) { + NPN_GetURL(This->instance,This->linkurl,This->linktarget); + return 0; + } + else if(This->errorflag) { + display_last_error(This); + } + } + return 0; + + case WM_MOUSEMOVE: + + if(This->dynamicmng && This->mng && This->lpdib && !This->errorflag) { + POINTS pos; + int overimage; + + pos=MAKEPOINTS(lParam); + overimage=0; + if(pos.x>=0 && pos.xlpdibinfo->biWidth && pos.y>=0 && pos.ylpdibinfo->biHeight) { + overimage=1; + } + + if(overimage) { + if(This->mouse_over_mng) { + // mouse is still over image: mouse move event + DynamicMNG_FireEvent(This,2,pos); // 2=mouse move + } + else { + // mouse wasn't over the image but now it is: mouse-enter event + DynamicMNG_FireEvent(This,1,pos); // mouse enter + } + } + else { // mouse not now over image + if(This->mouse_over_mng) { // ... but it used to be + pos.x=0; pos.y=0; + DynamicMNG_FireEvent(This,3,pos); // 3=mouse leave + } + } + + This->mouse_over_mng=overimage; // remember for next time + + if(This->mouse_over_mng && (This->dynamicmng==1) ) { +#define MOUSE_POLL_INTERVAL 100 // milliseconds + SetTimer(This->fhWnd,2,MOUSE_POLL_INTERVAL,NULL); + This->timer2_set=0; + } + } + return 0; + + case WM_PAINT: + { + PAINTSTRUCT paintStruct; + HDC hdc; + RECT rect2; + + hdc = BeginPaint( hWnd, &paintStruct ); + SetWindowOrgEx(hdc,This->xscrollpos,This->yscrollpos,NULL); + + GetClientRect(hWnd,&rect); + if(This) { + if(This->errorflag || !This->lpdib) { + SelectObject(hdc,hfontMsg); + Rectangle(hdc,rect.left,rect.top,rect.right,rect.bottom); + rect2.left=rect.left+2; + rect2.top=rect.top+2; + rect2.right=rect.right-2; + rect2.bottom=rect.bottom-2; + if(This->errorflag) { + DrawText(hdc,"MNG PLUG-IN ERROR!",-1,&rect2,DT_LEFT|DT_WORDBREAK); + } + else { + if(This->loadstate>=STATE_LOADING) { + DrawText(hdc,"MNG image loading...",-1,&rect2,DT_LEFT|DT_WORDBREAK); + } + else { + DrawText(hdc,"MNG plug-in",-1,&rect2,DT_LEFT|DT_WORDBREAK); + } + } + } + else if(This->lpdib) { + StretchDIBits(hdc, + 0,0,This->lpdibinfo->biWidth,This->lpdibinfo->biHeight, + 0,0,This->lpdibinfo->biWidth,This->lpdibinfo->biHeight, + &((BYTE*)(This->lpdib))[sizeof(BITMAPINFOHEADER)], + (LPBITMAPINFO)This->lpdib,DIB_RGB_COLORS,SRCCOPY); + } + } + + + EndPaint( hWnd, &paintStruct ); + } + return 0; + + case WM_TIMER: + switch(wParam) { + case 1: // the main animation timer + KillTimer(hWnd,1); + This->timer_set=0; + +#ifdef MNGPLG_TRACE + fprintf(tracefile,"WM_TIMER display_resume bytesloaded=%d\n",This->bytesloaded); +#endif + + if(This->mng) { + if(!This->needresume) { + handle_read_error(This, mng_display_resume(This->mng) ); + } + } + return 0; + + case 2: // timer for polling mouse position + { + RECT rc; + POINT pt; + POINTS pos; + + GetWindowRect(hWnd,&rc); + GetCursorPos(&pt); + if(!PtInRect(&rc,pt)) { + KillTimer(hWnd,2); + pos.x=0; pos.y=0; + DynamicMNG_FireEvent(This,3,pos); // 3=mouse leave + This->mouse_over_mng=0; + } + } + return 0; + } + break; + + } + + /* Forward unprocessed messages on to their original destination + * (the window proc we replaced) */ + return This->fDefaultWindowProc(hWnd, Msg, wParam, lParam); +} + +static LRESULT CALLBACK DlgProcProp(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) +{ + char buf[4096],buf2[1024]; + + switch(Msg) { + case WM_INITDIALOG: + { + DWORD tabs[1]; + + PluginInstance *This=(PluginInstance*)lParam; + + tabs[0]= 60; + SendDlgItemMessage(hWnd,IDC_IMGINFO,EM_SETTABSTOPS,(WPARAM)1,(LPARAM)tabs); + + wsprintf(buf,"URL:\t%s\r\n",This->url); + + if(This->lpdib) { + wsprintf(buf2,"Dimensions:\t%d x %d\r\n",This->lpdibinfo->biWidth, + This->lpdibinfo->biHeight); + lstrcat(buf,buf2); + } + + if(This->lpdib && This->fMode==NP_EMBED) { + wsprintf(buf2,"Window:\t%d x %d\r\n",This->windowwidth, + This->windowheight); + lstrcat(buf,buf2); + } + + if(This->filesize) { + wsprintf(buf2,"File size:\t%u bytes\r\n",This->filesize); + lstrcat(buf,buf2); + } + +#ifdef _DEBUG + if(This->mngdata && This->lpdib && This->bytesalloc && This->dibsize) { + // note this doesn't include memory used by libmng + wsprintf(buf2,"Memory used:\t%u bytes\r\n", + This->bytesalloc + This->dibsize); + lstrcat(buf,buf2); + } +#endif + + if(This->islink) { + wsprintf(buf2,"Link to:\t%s\r\n",This->linkurl); + lstrcat(buf,buf2); + if(strcmp(This->linktarget,"_self")) { + wsprintf(buf2,"Link target:\t%s\r\n",This->linktarget); + lstrcat(buf,buf2); + } + } + + if(This->loadstate >= STATE_VALIDFRAME) { + wsprintf(buf2,"Signature:\t%s\r\n",get_imagetype_name(mng_get_sigtype(This->mng))); + lstrcat(buf,buf2); + wsprintf(buf2,"Image type:\t%s\r\n",get_imagetype_name(mng_get_imagetype(This->mng))); + lstrcat(buf,buf2); + wsprintf(buf2,"Simplicity:\t0x%08x\r\n",mng_get_simplicity(This->mng)); + lstrcat(buf,buf2); + wsprintf(buf2,"Frame count:\t%u\r\n",mng_get_framecount(This->mng)); + lstrcat(buf,buf2); + wsprintf(buf2,"Layer count:\t%u\r\n",mng_get_layercount(This->mng)); + lstrcat(buf,buf2); + wsprintf(buf2,"Play time:\t%u\r\n",mng_get_playtime(This->mng)); + lstrcat(buf,buf2); + } + + SetDlgItemText(hWnd,IDC_IMGINFO,buf); + + if(This->textdata) + SetDlgItemText(hWnd,IDC_MNGTEXT,This->textdata); + } + return(TRUE); + case WM_CLOSE: + EndDialog(hWnd,0); + return(TRUE); + case WM_COMMAND: + switch(wParam) { + case IDOK: + case IDCANCEL: + EndDialog(hWnd,0); + return(TRUE); + } + } + return(FALSE); +} + +static LRESULT CALLBACK DlgProcAbout(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) +{ + char buf[4096],buf2[1024],buf3[300]; + + switch(Msg) { + case WM_INITDIALOG: + { + //DWORD tabs[1]; + + PluginInstance *This=(PluginInstance*)lParam; + + //tabs[0]= 60; + //SendDlgItemMessage(hWnd,IDC_IMGINFO,EM_SETTABSTOPS,(WPARAM)1,(LPARAM)tabs); + + wsprintf(buf,"MNGPLG Plug-in, Version %s\r\n%s" +#ifdef _DEBUG + " DEBUG BUILD" +#endif + "\r\nCopyright (C) 2000-2002 by Jason Summers\r\n\r\n",MNGPLGVERS,__DATE__); + + wsprintf(buf2,"Based on libmng by Gerard Juyn.\r\n"); + lstrcat(buf,buf2); + + wsprintf(buf2,"libmng version: %s\r\n\r\n",mng_version_text()); + lstrcat(buf,buf2); + + wsprintf(buf2,"Uses the zlib compression library.\r\n"); + lstrcat(buf,buf2); + wsprintf(buf2,"zlib version: %s\r\n\r\n",zlibVersion()); + lstrcat(buf,buf2); + + wsprintf(buf2,"This software is based in part on the work of the " + "Independent JPEG Group.\r\n"); + lstrcat(buf,buf2); + // This really only gives the version of the libjpeg header used when + // compiling this plugin, but I don't know how to query libjpeg for its + // version. + wsprintf(buf2,"IJG JPEG library version: %s\r\n%s\r\n\r\n",JVERSION,JCOPYRIGHT); + lstrcat(buf,buf2); + +#ifdef MNGPLG_CMS + wsprintf(buf2,"Uses the lcms color management library by Martí Maria. " + "lcms is distributed under the terms of the GNU LESSER GENERAL PUBLIC LICENSE. " + "See the file COPYING-LCMS.\r\n\r\n"); + lstrcat(buf,buf2); +#endif + + if(GetModuleFileName(g_hInst,buf3,260)) { + wsprintf(buf2,"MNGPLG location: %s\r\n",buf3); + lstrcat(buf,buf2); + } + + SetDlgItemText(hWnd,IDC_PRGINFO,buf); + + } + return(TRUE); + case WM_CLOSE: + EndDialog(hWnd,0); + return(TRUE); + case WM_COMMAND: + switch(wParam) { + case IDOK: + case IDCANCEL: + EndDialog(hWnd,0); + return(TRUE); + } + } + return(FALSE); +} + + +///////////////////// +///////////////////// low-level plug-in NPAPI functions + +static JRIGlobalRef Private_GetJavaClass(void) +{ + return NULL; +} + +NPError WINAPI NP_GetEntryPoints(NPPluginFuncs* pFuncs) +{ + if(!pFuncs) return NPERR_INVALID_FUNCTABLE_ERROR; + + pFuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR; + pFuncs->newp = NPP_New; + pFuncs->destroy = NPP_Destroy; + pFuncs->setwindow = NPP_SetWindow; + pFuncs->newstream = NPP_NewStream; + pFuncs->destroystream = NPP_DestroyStream; + pFuncs->asfile = NPP_StreamAsFile; + pFuncs->writeready = NPP_WriteReady; + pFuncs->write = NPP_Write; + pFuncs->print = NPP_Print; + pFuncs->event = NULL; + + g_pluginFuncs = pFuncs; + + return NPERR_NO_ERROR; +} + +NPError WINAPI NP_Initialize(NPNetscapeFuncs* pFuncs) +{ + int navMinorVers; + + if(!pFuncs) return NPERR_INVALID_FUNCTABLE_ERROR; + + g_pNavigatorFuncs = pFuncs; // save it for future reference + + if(HIBYTE(pFuncs->version) > NP_VERSION_MAJOR) + return NPERR_INCOMPATIBLE_VERSION_ERROR; + + navMinorVers = g_pNavigatorFuncs->version & 0xFF; + if(navMinorVers>=NPVERS_HAS_NOTIFICATION) + g_pluginFuncs->urlnotify = NPP_URLNotify; + if( navMinorVers>=NPVERS_HAS_LIVECONNECT) + g_pluginFuncs->javaClass = Private_GetJavaClass(); + + return NPP_Initialize(); +} + +NPError WINAPI NP_Shutdown() +{ + NPP_Shutdown(); + + g_pNavigatorFuncs = NULL; + return NPERR_NO_ERROR; +} diff --git a/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/npmngplg.def b/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/npmngplg.def new file mode 100644 index 000000000..9dcd7dc5f --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/npmngplg.def @@ -0,0 +1,6 @@ +LIBRARY npmngplg + +EXPORTS + NP_GetEntryPoints @1 + NP_Initialize @2 + NP_Shutdown @3 diff --git a/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/npmngplg.dep b/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/npmngplg.dep new file mode 100644 index 000000000..c7a306e5c --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/npmngplg.dep @@ -0,0 +1,22 @@ +# Microsoft Developer Studio Generated Dependency File, included by npmngplg.mak + +.\npmngplg.c : \ + "..\..\program files\microsoft visual studio\vc98\include\basetsd.h"\ + "..\jpgsrc6b\jconfig.h"\ + "..\jpgsrc6b\jmorecfg.h"\ + "..\jpgsrc6b\jpeglib.h"\ + "..\jpgsrc6b\jversion.h"\ + "..\lcms-1.08a\include\icc34.h"\ + "..\lcms-1.08a\include\lcms.h"\ + "..\libmng-1.0.4\libmng.h"\ + "..\libmng-1.0.4\libmng_conf.h"\ + "..\libmng-1.0.4\libmng_types.h"\ + "..\zlib-1.1.4\zconf.h"\ + "..\zlib-1.1.4\zlib.h"\ + ".\npapidefs.h"\ + + +.\npmngplg.rc : \ + ".\cur_ie.cur"\ + ".\cur_ns.cur"\ + diff --git a/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/npmngplg.dsp b/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/npmngplg.dsp new file mode 100644 index 000000000..5bd4698ff --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/npmngplg.dsp @@ -0,0 +1,123 @@ +# Microsoft Developer Studio Project File - Name="npmngplg" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=npmngplg - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "npmngplg.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "npmngplg.mak" CFG="npmngplg - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "npmngplg - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "npmngplg - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "npmngplg - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "..\libmng-1.0.5" /I "..\zlib-1.1.4" /I "..\jpgsrc6b" /I "..\lcms-1.09b\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 ..\libmng-1.0.5\Release\libmng.lib ..\zlib-1.1.4\Release\zlib.lib ..\jpgsrc6b\Release\libjpeg.lib ..\lcms-1.09b\src\Release\lcms.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib /nologo /subsystem:windows /dll /machine:I386 +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "npmngplg - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\libmng-1.0.5" /I "..\zlib-1.1.4" /I "..\jpgsrc6b" /I "..\lcms-1.09b\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 ..\libmng-1.0.5\Debug\libmng.lib ..\zlib-1.1.4\Debug\zlib.lib ..\jpgsrc6b\Debug\libjpeg.lib ..\lcms-1.09b\src\Debug\lcms.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /out:"c:\program files\opera\program\plugins\npmngplg.dll" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "npmngplg - Win32 Release" +# Name "npmngplg - Win32 Debug" +# Begin Source File + +SOURCE=.\cur_ns.cur +# End Source File +# Begin Source File + +SOURCE=.\npapidefs.h +# End Source File +# Begin Source File + +SOURCE=.\npmngplg.c + +!IF "$(CFG)" == "npmngplg - Win32 Release" + +# SUBTRACT CPP /YX + +!ELSEIF "$(CFG)" == "npmngplg - Win32 Debug" + +# SUBTRACT CPP /Fr /YX /Yc /Yu + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\npmngplg.def +# End Source File +# Begin Source File + +SOURCE=.\npmngplg.rc +# End Source File +# End Target +# End Project diff --git a/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/npmngplg.dsw b/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/npmngplg.dsw new file mode 100644 index 000000000..a52c8b0c2 --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/npmngplg.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "npmngplg"=.\npmngplg.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/npmngplg.mak b/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/npmngplg.mak new file mode 100644 index 000000000..6ed10b619 --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/npmngplg.mak @@ -0,0 +1,225 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on npmngplg.dsp +!IF "$(CFG)" == "" +CFG=npmngplg - Win32 Debug +!MESSAGE No configuration specified. Defaulting to npmngplg - Win32 Debug. +!ENDIF + +!IF "$(CFG)" != "npmngplg - Win32 Release" && "$(CFG)" != "npmngplg - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "npmngplg.mak" CFG="npmngplg - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "npmngplg - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "npmngplg - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "npmngplg - Win32 Release" + +OUTDIR=.\Release +INTDIR=.\Release +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +ALL : "$(OUTDIR)\npmngplg.dll" + + +CLEAN : + -@erase "$(INTDIR)\npmngplg.obj" + -@erase "$(INTDIR)\npmngplg.res" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(OUTDIR)\npmngplg.dll" + -@erase "$(OUTDIR)\npmngplg.exp" + -@erase "$(OUTDIR)\npmngplg.lib" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MT /W3 /GX /O2 /I "..\libmng-1.0.5" /I "..\zlib-1.1.4" /I "..\jpgsrc6b" /I "..\lcms-1.09b\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /Fp"$(INTDIR)\npmngplg.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\npmngplg.res" /d "NDEBUG" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\npmngplg.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=..\libmng-1.0.5\Release\libmng.lib ..\zlib-1.1.4\Release\zlib.lib ..\jpgsrc6b\Release\libjpeg.lib ..\lcms-1.09b\src\Release\lcms.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib /nologo /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\npmngplg.pdb" /machine:I386 /def:".\npmngplg.def" /out:"$(OUTDIR)\npmngplg.dll" /implib:"$(OUTDIR)\npmngplg.lib" +DEF_FILE= \ + ".\npmngplg.def" +LINK32_OBJS= \ + "$(INTDIR)\npmngplg.obj" \ + "$(INTDIR)\npmngplg.res" + +"$(OUTDIR)\npmngplg.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "npmngplg - Win32 Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug + +ALL : "..\..\program files\opera\program\plugins\npmngplg.dll" + + +CLEAN : + -@erase "$(INTDIR)\npmngplg.obj" + -@erase "$(INTDIR)\npmngplg.res" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(INTDIR)\vc60.pdb" + -@erase "$(OUTDIR)\npmngplg.exp" + -@erase "$(OUTDIR)\npmngplg.lib" + -@erase "$(OUTDIR)\npmngplg.pdb" + -@erase "..\..\program files\opera\program\plugins\npmngplg.dll" + -@erase "..\..\program files\opera\program\plugins\npmngplg.ilk" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\libmng-1.0.5" /I "..\zlib-1.1.4" /I "..\jpgsrc6b" /I "..\lcms-1.09b\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR"$(INTDIR)\\" /Fp"$(INTDIR)\npmngplg.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\npmngplg.res" /d "_DEBUG" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\npmngplg.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=..\libmng-1.0.5\Debug\libmng.lib ..\zlib-1.1.4\Debug\zlib.lib ..\jpgsrc6b\Debug\libjpeg.lib ..\lcms-1.09b\src\Debug\lcms.lib kernel32.lib user32.lib gdi32.lib comdlg32.lib /nologo /subsystem:windows /dll /incremental:yes /pdb:"$(OUTDIR)\npmngplg.pdb" /debug /machine:I386 /def:".\npmngplg.def" /out:"c:\program files\opera\program\plugins\npmngplg.dll" /implib:"$(OUTDIR)\npmngplg.lib" /pdbtype:sept +DEF_FILE= \ + ".\npmngplg.def" +LINK32_OBJS= \ + "$(INTDIR)\npmngplg.obj" \ + "$(INTDIR)\npmngplg.res" + +"..\..\program files\opera\program\plugins\npmngplg.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("npmngplg.dep") +!INCLUDE "npmngplg.dep" +!ELSE +!MESSAGE Warning: cannot find "npmngplg.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "npmngplg - Win32 Release" || "$(CFG)" == "npmngplg - Win32 Debug" +SOURCE=.\npmngplg.c + +!IF "$(CFG)" == "npmngplg - Win32 Release" + +CPP_SWITCHES=/nologo /MT /W3 /GX /O2 /I "..\libmng-1.0.5" /I "..\zlib-1.1.4" /I "..\jpgsrc6b" /I "..\lcms-1.09b\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c + +"$(INTDIR)\npmngplg.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) @<< + $(CPP_SWITCHES) $(SOURCE) +<< + + +!ELSEIF "$(CFG)" == "npmngplg - Win32 Debug" + +CPP_SWITCHES=/nologo /MTd /W3 /Gm /GX /ZI /Od /I "..\libmng-1.0.5" /I "..\zlib-1.1.4" /I "..\jpgsrc6b" /I "..\lcms-1.09b\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c + +"$(INTDIR)\npmngplg.obj" : $(SOURCE) "$(INTDIR)" + $(CPP) @<< + $(CPP_SWITCHES) $(SOURCE) +<< + + +!ENDIF + +SOURCE=.\npmngplg.rc + +"$(INTDIR)\npmngplg.res" : $(SOURCE) "$(INTDIR)" + $(RSC) $(RSC_PROJ) $(SOURCE) + + + +!ENDIF + diff --git a/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/npmngplg.rc b/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/npmngplg.rc new file mode 100644 index 000000000..c0dee930d --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/npmngplg.rc @@ -0,0 +1,175 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,1,1 + PRODUCTVERSION 1,0,1,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "Comments", "\0" + VALUE "CompanyName", "Jason Summers\0" + VALUE "FileDescription", "MNG Plug-in 1.0.1\0" + VALUE "FileExtents", "mng|mng|jng|jng\0" + VALUE "FileOpenName", "MNG Animation (*.mng)|MNG Animation (*.mng)|JNG Image (*.jng)|JNG Image (*.jng)\0" + VALUE "FileVersion", "1.0.1\0" + VALUE "InternalName", "npmngplg\0" + VALUE "LegalCopyright", "Copyright © 2000-2002\0" + VALUE "LegalTrademarks", "\0" + VALUE "MIMEType", "video/mng|video/x-mng|image/jng|image/x-jng\0" + VALUE "OriginalFilename", "npmngplg.dll\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "MNG Plug-in 1.0.1\0" + VALUE "ProductVersion", "1.0.1\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // !_MAC + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +ABOUTDLG DIALOG DISCARDABLE 0, 0, 282, 137 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "MNGPLG" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,225,7,50,14,WS_GROUP + EDITTEXT IDC_PRGINFO,7,7,213,123,ES_MULTILINE | ES_AUTOVSCROLL | + ES_READONLY | NOT WS_BORDER | WS_VSCROLL | WS_GROUP +END + +PROPDLG DIALOG DISCARDABLE 0, 0, 344, 186 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Image properties" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,287,7,50,14,WS_GROUP + EDITTEXT IDC_IMGINFO,7,7,275,97,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_GROUP + LTEXT "Image comments:",IDC_STATIC,7,108,56,8 + EDITTEXT IDC_MNGTEXT,7,119,275,60,ES_MULTILINE | ES_READONLY | + WS_VSCROLL | WS_GROUP +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + "ABOUTDLG", DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 275 + TOPMARGIN, 7 + BOTTOMMARGIN, 130 + END + + "PROPDLG", DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 337 + TOPMARGIN, 7 + BOTTOMMARGIN, 179 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Cursor +// + +CURHAND_NS CURSOR DISCARDABLE "cur_ns.cur" +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/resource.h b/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/resource.h new file mode 100644 index 000000000..878c604fa --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/mngplg/mngplg-src-1.0.1/resource.h @@ -0,0 +1,19 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by npmngplg.rc +// +#define IDR_DATA1 106 +#define IDC_PRGINFO 1000 +#define IDC_IMGINFO 1001 +#define IDC_MNGTEXT 1002 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 110 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1004 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Engine/lib/lmng/contrib/msvc/mngplg/readme.txt b/Engine/lib/lmng/contrib/msvc/mngplg/readme.txt new file mode 100644 index 000000000..002c4f13a --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/mngplg/readme.txt @@ -0,0 +1,211 @@ +MNGPLG +A simple browser plug-in for the MNG image/animation file format. + +By Jason Summers +Version 1.0.1 2 Oct 2002 +Web site: + + +COPYRIGHT NOTICE + +Copyright (c) 2000-2002 by Jason Summers + +THIS SOFTWARE IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT WITHOUT +ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR +FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, +YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL +ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE +THIS SOFTWARE AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING +ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THIS SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THIS SOFTWARE TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + +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 source code must not be misrepresented; + you must not claim that you wrote the original software. + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original source. + 3. Altered binary versions must not be misrepresented as being the + original. + 4. This Copyright notice may not be removed or altered from any source + or altered source distribution, although you may add a Copyright + notice for yourself for any code that you have written. + + +This software uses several third-party libraries (listed below), some of +which are optional. If you redistribute MNGPLG, it is your responsibility to +comply with the licenses of any libraries used. + +This package includes a compiled executable plug-in file, npmngplg.dll. This +file includes code from lcms, which is distributed under the LGPL. To the +best of my understanding, that basically means that anyone distributing that +file must (1) make it possible for the recipient to modify the plug-in to +use a new or modified version of lcms, and (2) make available the lcms +source code. Requirement (1) is satisfied by the inclusion of the source +code. For requirement (2), you can find out how to get the lcms source code +at the web site listed at the beginning of this document, if necessary. + + +--------- + +Based on libmng. + Copyright (c) 2000,2002 Gerard Juyn (gerard@libmng.com) + + +Uses the zlib compression library. + (C) 1995-2002 Jean-loup Gailly and Mark Adler + +This software is based in part on the work of the Independent JPEG Group. + Copyright (C) 1991-1998, Thomas G. Lane + +Uses the lcms color management library by Martí Maria Saguer. + (distributed under the GNU LESSER GENERAL PUBLIC LICENSE) + +--------- + +SECURITY NOTICE + +Although I've tried to write it carefully, MNGPLG has not had any sort of +security audit. Due to the nature of plug-ins, it is possible for certain +types of bugs to exist which may allow remote web sites to take control of +your computer or do harm to it by sending a carefully constructed data file +to the plug-in. If you are paranoid about security, you may not wish to +leave MNGPLG enabled in your browser for an extended period of time. + +--------- + +INTRODUCTION + +MNGPLG is a Netscape-style browser plug-in which displays the MNG +image/animation format. It is configured to claim the following MIME types: + + video/x-mng + video/mng + image/x-jng + image/jng + +It claims the file extensions ".mng" and ".jng", but file extensions should +only apply when no MIME type is available (e.g. on an FTP site, or on your +local hard disk). + +It can also display PNG image files, but it would cause too many problems +for it to try to claim the PNG data type. + +If you are configuring a web server to support MNG and JNG, the correct +MIME types to use are "video/x-mng" and "image/x-jng", since the MIME types +have not, as of this writing, been officially registered. + + +REQUIREMENTS + +MNG requires a 32-bit Windows operating system, and a 32-bit web browser +that supports Netscape-style plug-ins. For example, it works in Netscape 3 +and higher, Opera 3.51 and higher, and Microsoft Internet Explorer from +about version 3 to 5.0. (It does not readily work in IE 5.5sp2 and +higher.) Netscape 6 and higher (and related browsers) include native +support for MNG, so it should not be necessary to use this plug-in. + + +INSTALLATION + +There's no install program. To install it, copy the included "npmngplg.dll" +file to your browser's "Plugins" folder, then restart your browser. + +For Netscape 4.x, the Plugins folder is typically located somewhere like: +C:\Program Files\Netscape\Communicator\Program\Plugins + +Note: Windows Explorer, by default, is configured to hide files that end in +".dll". You should probably change that setting. I'd tell you how, but it's +different in almost every version of Windows. + +In Netscape 4.x, you can verify that the plug-in is installed by choosing +Help|About Plug-ins from the main menu (with JavaScript enabled). + +To uninstall, delete the npmngplg.dll file. It does not create any other +files. It currently does not write anything to the Windows registry. + + +HOW TO USE (FOR END USERS) + +Right-click on an MNG image as it is being displayed to get a menu with some +of the usual features. + +Right-click and choose "Properties" to display some internal information +about the image. Some images have embedded text information that will be +shown in the "Image comments" area. For technical reasons, some or all of +the comments may not be available until the animation completes a full loop. + + +HOW TO USE (FOR WEB DEVELOPERS) + +First, if at all possible, configure your web server (not browser) to +assign the MIME type "video/x-mng" to files that end in ".mng", and +assign type "image/x-jng" to files that end in ".jng". + +The most reliable way to embed MNG files in a web page is (unfortunately) +to use the nonstandard tag. For example: + + + +The src, width, and height attributes are required. Width and height should +match the actual width and height of the image. + +Transparency is not supported, and probably never will be. However, you can +supply a background color to use in transparent areas by using the BGCOLOR +attribute in the EMBED tag, i.e.: + + + +You cannot use color names like "red"; you must use the hexadecimal format +as in the example. + +An image can be made into a "hotlink" by including an HREF and optionally a +TARGET attribute in the EMBED tag. For example: + + + + +SOURCE CODE + +The C source code is included. I've only tested it with libmng 1.0.5, +but it's probably also compatible with other versions, maybe with +minor changes. + +To compile it, you'll need: + +- libmng MNG library . + +libmng in turn uses some other libraries: + + - zlib compression library + + - IJG JPEG library + + - [optional] lcms "Little Color Management System" library. + +If you include lcms, turn on the MNG_FULL_CMS option in libmng_conf.h +before compiling. Note that lcms is distributed under the LGPL -- be sure +you understand the implications of that before distributing any resulting +executable files. + +If you don't include lcms, comment out the "#define MNGPLG_CMS" line in +npmngplg.c. + +I also recommend turning on the MNG_ERROR_TELLTALE and +MNG_SUPPORT_DYNAMICMNG options in libmng_conf.h. + +The files from the Netscape plug-in SDK are no longer needed as of MNGPNG +0.9.4. + +Make sure to include the npmngplg.def file in your project, or declare the +necessary DLL entry points in some other way. diff --git a/Engine/lib/lmng/contrib/msvc/mngview/MNGView.dsp b/Engine/lib/lmng/contrib/msvc/mngview/MNGView.dsp new file mode 100644 index 000000000..b2c114e8c --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/mngview/MNGView.dsp @@ -0,0 +1,126 @@ +# Microsoft Developer Studio Project File - Name="MNGView" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** NICHT BEARBEITEN ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=MNGView - Win32 Debug +!MESSAGE Dies ist kein gültiges Makefile. Zum Erstellen dieses Projekts mit NMAKE +!MESSAGE verwenden Sie den Befehl "Makefile exportieren" und führen Sie den Befehl +!MESSAGE +!MESSAGE NMAKE /f "MNGView.mak". +!MESSAGE +!MESSAGE Sie können beim Ausführen von NMAKE eine Konfiguration angeben +!MESSAGE durch Definieren des Makros CFG in der Befehlszeile. Zum Beispiel: +!MESSAGE +!MESSAGE NMAKE /f "MNGView.mak" CFG="MNGView - Win32 Debug" +!MESSAGE +!MESSAGE Für die Konfiguration stehen zur Auswahl: +!MESSAGE +!MESSAGE "MNGView - Win32 Release" (basierend auf "Win32 (x86) Application") +!MESSAGE "MNGView - Win32 Debug" (basierend auf "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "MNGView - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0xc07 /d "NDEBUG" +# ADD RSC /l 0xc07 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 /out:"MNGView.exe" + +!ELSEIF "$(CFG)" == "MNGView - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0xc07 /d "_DEBUG" +# ADD RSC /l 0xc07 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "MNGView - Win32 Release" +# Name "MNGView - Win32 Debug" +# Begin Group "Quellcodedateien" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\Main.cpp +# End Source File +# Begin Source File + +SOURCE=.\Main.rc +# End Source File +# Begin Source File + +SOURCE=.\mng.cpp +# End Source File +# End Group +# Begin Group "Header-Dateien" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\Main.h +# End Source File +# Begin Source File + +SOURCE=.\resource.h +# End Source File +# End Group +# Begin Group "Ressourcendateien" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\Mng.ico +# End Source File +# End Group +# End Target +# End Project diff --git a/Engine/lib/lmng/contrib/msvc/mngview/MNGView.dsw b/Engine/lib/lmng/contrib/msvc/mngview/MNGView.dsw new file mode 100644 index 000000000..54bc89302 --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/mngview/MNGView.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNUNG: DIESE ARBEITSBEREICHSDATEI DARF NICHT BEARBEITET ODER GELÖSCHT WERDEN! + +############################################################################### + +Project: "MNGView"=.\MNGView.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/Engine/lib/lmng/contrib/msvc/mngview/MNGView.opt b/Engine/lib/lmng/contrib/msvc/mngview/MNGView.opt new file mode 100644 index 000000000..1106e85eb Binary files /dev/null and b/Engine/lib/lmng/contrib/msvc/mngview/MNGView.opt differ diff --git a/Engine/lib/lmng/contrib/msvc/mngview/MNGView.plg b/Engine/lib/lmng/contrib/msvc/mngview/MNGView.plg new file mode 100644 index 000000000..c950d9e5d --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/mngview/MNGView.plg @@ -0,0 +1,34 @@ + + +
      +

      Erstellungsprotokoll

      +

      +--------------------Konfiguration: MNGView - Win32 Release-------------------- +

      +

      Befehlszeilen

      +Erstellen der temporären Datei "C:\WINDOWS\TEMP\RSPF386.TMP" mit Inhalten +[ +/nologo /ML /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /Fp"Release/MNGView.pch" /YX /Fo"Release/" /Fd"Release/" /FD /c +"F:\Nikgames\Libmng\contrib\MNGView\Main.cpp" +] +Creating command line "cl.exe @C:\WINDOWS\TEMP\RSPF386.TMP" +Erstellen der temporären Datei "C:\WINDOWS\TEMP\RSPF387.TMP" mit Inhalten +[ +kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /incremental:no /pdb:"Release/MNGView.pdb" /machine:I386 /out:"MNGView.exe" +.\Release\Main.obj +.\Release\mng.obj +.\Release\Main.res +] +Erstellen der Befehlzeile "link.exe @C:\WINDOWS\TEMP\RSPF387.TMP" +

      Ausgabefenster

      +Kompilierung läuft... +Main.cpp +Linker-Vorgang läuft... + + + +

      Ergebnisse

      +MNGView.exe - 0 Fehler, 0 Warnung(en) +
      + + diff --git a/Engine/lib/lmng/contrib/msvc/mngview/Main.cpp b/Engine/lib/lmng/contrib/msvc/mngview/Main.cpp new file mode 100644 index 000000000..bf1f98d6b --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/mngview/Main.cpp @@ -0,0 +1,334 @@ +//--------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------- +// +// MNGView Sample Application for VC6: +// Loads all MNG/JNG/PNG Files LibMNG can do +// Can save a single Frame to PNG Format. +// +// This code is public domain. +// Created by Nikolaus Brennig, November 14th, 2000. +// virtualnik@nol.at +// http://cust.nol.at/ppee +// +// Tab: 4 +// +//--------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------- + +#define WIN32_LEAN_AND_MEAN +#include "Main.h" + + +//--------------------------------------------------------------------------------------------- +// Libs (its up to you to make VC find the libs; set the paths in the options): +//--------------------------------------------------------------------------------------------- +#pragma comment(lib,"comctl32.lib") +#pragma comment(lib,"libmng.lib") +#pragma comment(lib,"libjpeg.lib") +#pragma comment(lib,"libz.lib") +#pragma comment(lib,"lcmsstat.lib") + + +//--------------------------------------------------------------------------------------------- +// Vars: +//--------------------------------------------------------------------------------------------- +HWND hPicWin; +HINSTANCE hinst; +HMENU hMenu; +HDC MemDC, hdc; +HBITMAP MemImage, DefaultMemImage; +ANIMFILE AnimFile; +RECT rcRect; +OPENFILENAME ofn, sfn; +int W, H, Bits; +int dx, dy; +char OFNFile[1024]; +char CurDir[1024]; + + +//--------------------------------------------------------------------------------------------- +// Loads a file: +//--------------------------------------------------------------------------------------------- +VOID LoadOFN( HWND hwnd ) +{ + GetCurrentDirectory( sizeof(CurDir), CurDir ); + + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = hwnd; + ofn.lpstrFile = OFNFile; + ofn.nMaxFile = sizeof(OFNFile); + ofn.lpstrFilter = "Network Graphics (*.mng;*.jng:*.png)\0*.mng;*.jng;*.png\0"; + ofn.nFilterIndex = 1; + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = NULL; + ofn.lpstrInitialDir = CurDir; + ofn.lpstrTitle = "Load:"; + ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | + OFN_HIDEREADONLY | OFN_NONETWORKBUTTON; + + // Display the Open dialog box. + if( GetOpenFileName(&ofn) ) + { + dx = dy = W = H = 0; + if( AnimFile.isAnimation == 1 ) CleanUpMNG(); + LoadMNG( OFNFile, hwnd, hdc ); + } +} + + +//--------------------------------------------------------------------------------------------- +// Saves a file: +//--------------------------------------------------------------------------------------------- +VOID SaveSFN( HWND hwnd ) +{ + GetCurrentDirectory( sizeof(CurDir), CurDir ); + + // Initialize OPENFILENAME + sfn.lStructSize = sizeof(OPENFILENAME); + sfn.hwndOwner = hwnd; + sfn.lpstrFile = OFNFile; + sfn.nMaxFile = sizeof(OFNFile); + sfn.lpstrFilter = "PNG\0*.png\0"; + sfn.nFilterIndex = 1; + sfn.lpstrFileTitle = 0; + sfn.nMaxFileTitle = 0; + sfn.lpstrInitialDir = CurDir; + sfn.lpstrTitle = "Save an Image"; + sfn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_NONETWORKBUTTON; + + // Display the Open dialog box. + if( GetSaveFileName(&sfn) ) + { + SaveMNG( OFNFile, MemDC, MemImage ); + } +} + + +//--------------------------------------------------------------------------------------------- +// For stringhandling... +//--------------------------------------------------------------------------------------------- +VOID catpath( char *dst, const char *src ) +{ + int len = lstrlen(dst); + if( len > 0 && (dst[len-1] != '\\' && dst[len-1] != '/') ) lstrcat( dst, "\\" ); + lstrcat( dst, src ); +} + + +//--------------------------------------------------------------------------------------------- +// MainWindow WindowProc +//--------------------------------------------------------------------------------------------- +long WINAPI WindowProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) +{ + switch( message ) + { + case WM_TIMER: + { + int wTimerID = wParam; + if( AnimFile.isAnimation == 1 && wTimerID == 2 ) + { + UpdateMNG(); + SendMessage( hPicWin, WM_PAINT, 0, 0 ); + } + } + break; + + case WM_COMMAND: + switch( LOWORD(wParam) ) + { + case FILE_OPEN: + LoadOFN(hwnd); + break; + + case FILE_SAVE: + SaveSFN(hwnd); + break; + + case FILE_EXIT: + DestroyWindow( hPicWin ); + break; + + case HELP_ABOUT: + Warning( + "MNGView Sample Application for VC6.\n" \ + "This Code is Public Domain.\n" \ + "Created by Nikolaus Brennig." + ); + break; + } + break; + + case WM_ERASEBKGND: + return 0L; + + case WM_PAINT: + // GetDC: + GetClientRect( hPicWin, &rcRect ); + hdc = GetDC( hPicWin ); + + if( MemDC == 0 ) + { + BitBlt( hdc, 0, 0, rcRect.right, rcRect.bottom, MemDC, 0, 0, BLACKNESS ); + ReleaseDC( hPicWin, hdc ); + break; + } + + // Erase: + // Upper area... + BitBlt( hdc, 0, 0, rcRect.right, (0+dy), MemDC, 0, 0, BLACKNESS ); + // Lower area... + BitBlt( hdc, 0, (0+dy)+H, rcRect.right, rcRect.bottom - ((0+dy)+H), MemDC, 0, 0, BLACKNESS ); + // Left area... + BitBlt( hdc, 0, 0, (0+dx), rcRect.bottom, MemDC, 0, 0, BLACKNESS ); + // Right area... + BitBlt( hdc, (0+dx)+W, 0, rcRect.right, rcRect.bottom, MemDC, 0, 0, BLACKNESS ); + + // Show Imageframe: + BitBlt( hdc, dx, dy, W, H, MemDC, 0, 0, SRCCOPY ); + + // Release DC... + ReleaseDC( hPicWin, hdc ); + break; + + case WM_HSCROLL: + { + int nScrollCode = (int) LOWORD(wParam); // scroll bar value + if( nScrollCode == SB_LINELEFT ) dx += 10; + if( nScrollCode == SB_LINERIGHT ) dx -= 10; + SendMessage( hwnd, WM_PAINT, 0, 0 ); + } + break; + + case WM_VSCROLL: + { + int nScrollCode = (int) LOWORD(wParam); // scroll bar value + if( nScrollCode == SB_LINEUP ) dy += 10; + if( nScrollCode == SB_LINEDOWN ) dy -= 10; + SendMessage( hwnd, WM_PAINT, 0, 0 ); + } + break; + + case WM_GETMINMAXINFO: + ((MINMAXINFO*)lParam)->ptMinTrackSize.x = 550; + ((MINMAXINFO*)lParam)->ptMinTrackSize.y = 450; + return 0L; + + case WM_DESTROY: + if( AnimFile.isAnimation == 1 ) + CleanUpMNG(); + if( hMenu ) DestroyMenu( hMenu ); + PostQuitMessage(0); + return 0L; + } + + return DefWindowProc( hwnd, message, wParam, lParam ); +} + + +//--------------------------------------------------------------------------------------------- +// ok, initen wir mal das Window mit den Styleparametern... +//--------------------------------------------------------------------------------------------- +BOOL InitApplication( HINSTANCE hInstance, int nCmdShow, LPSTR lpCommandLine ) +{ + WNDCLASSEX wcex; + + ZeroMemory( &wcex, sizeof(wcex) ); + wcex.cbSize = sizeof(wcex); + wcex.style = 0; + wcex.lpfnWndProc = WindowProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = hInstance; + wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(APPICON)); + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = HBRUSH(GetStockObject(BLACK_BRUSH)); + wcex.lpszMenuName = 0; + wcex.lpszClassName = "MNGViewClass"; + wcex.hIconSm = 0; + + if( !RegisterClassEx(&wcex) ) + return Error( "RegisterClass failed!" ); + + // Init: + W = 0; + H = 0; + AnimFile.isAnimation = 0; + MemDC = 0; + MemImage = 0; + DefaultMemImage = 0; + + // Create the Window: + hPicWin = CreateWindowEx( + 0, + "MNGViewClass", + TITLE, + WS_OVERLAPPEDWINDOW|WS_SYSMENU|WS_CLIPSIBLINGS|WS_CLIPCHILDREN|WS_HSCROLL|WS_VSCROLL, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + NULL, NULL, + hInstance, + NULL + ); + if( !hPicWin ) + return Error( "Couldn't create Window!" ); + + // Load our menu... + hMenu = LoadMenu( GetModuleHandle(NULL), MAKEINTRESOURCE(THEMENU) ); + if( hMenu == 0 ) Warning( "Unable to load Menu!" ); + SetMenu( hPicWin, hMenu ); + + InitCommonControls(); + + UpdateWindow(hPicWin); + ShowWindow(hPicWin, SW_NORMAL); + + return true; +} + + +//--------------------------------------------------------------------------------------------- +// Ok. Das ist die Startfunktion, die wird als erstes von Windows augerufen... +//--------------------------------------------------------------------------------------------- +int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) +{ + MSG msg; + + hinst = hInstance; + + if( !InitApplication(hInstance, nCmdShow, lpCmdLine) ) + return -1; + + while( GetMessage(&msg, NULL, 0, 0) ) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return msg.wParam; +} + + +//--------------------------------------------------------------------------------------------- +// Error +//--------------------------------------------------------------------------------------------- +BOOL Error( const char *err ) +{ + MessageBox( hPicWin, err, TITLE, MB_ICONHAND+MB_OK ); + DestroyWindow( hPicWin ); + + return FALSE; +} + + +//--------------------------------------------------------------------------------------------- +// Warning +//--------------------------------------------------------------------------------------------- +BOOL Warning( const char *err ) +{ + MessageBox( hPicWin, err, TITLE, MB_ICONHAND+MB_OK ); + + return FALSE; +} + diff --git a/Engine/lib/lmng/contrib/msvc/mngview/Main.h b/Engine/lib/lmng/contrib/msvc/mngview/Main.h new file mode 100644 index 000000000..9d9743f4d --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/mngview/Main.h @@ -0,0 +1,75 @@ +//--------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------- +// +// MNGView Sample Application for VC6: +// Loads all MNG/JNG/PNG Files LibMNG can do +// Can save a single Frame to PNG Format. +// +// This code is public domain. +// Created by Nikolaus Brennig, November 14th, 2000. +// virtualnik@nol.at +// http://cust.nol.at/ppee +// +// Tab: 4 +// +//--------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------- + +#ifndef _MAIN_H +#define _MAIN_H + + +//--------------------------------------------------------------------------------------------- +// Includes: +//--------------------------------------------------------------------------------------------- +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include "Resource.h" + + +//--------------------------------------------------------------------------------------------- +// Defines: +//--------------------------------------------------------------------------------------------- +#define TITLE "MNGView Sample Application 1.0" + + +//--------------------------------------------------------------------------------------------- +// global Vars: +//--------------------------------------------------------------------------------------------- +extern HWND hPicWin; +extern HINSTANCE hinst; +extern HDC MemDC, hdc; +extern HBITMAP MemImage, DefaultMemImage; +extern int W, H, Bits; + + +typedef struct +{ + int MaxFrame; + int CurFrame; + int Delay; + int isAnimation; +} ANIMFILE; +extern ANIMFILE AnimFile; + + +//--------------------------------------------------------------------------------------------- +// Function prototypes: +//--------------------------------------------------------------------------------------------- +BOOL Error( const char *err ); +BOOL Warning( const char *err ); +VOID catpath( char *dst, const char *src ); + + +// MNG.cpp specific: +VOID LoadMNG( LPSTR Filename, HWND hwnd, HDC hdc ); +VOID SaveMNG( LPSTR Filename, HDC hdc, HBITMAP hBmp ); + +VOID UpdateMNG(); +VOID CleanUpMNG(); + + +#endif \ No newline at end of file diff --git a/Engine/lib/lmng/contrib/msvc/mngview/Main.rc b/Engine/lib/lmng/contrib/msvc/mngview/Main.rc new file mode 100644 index 000000000..dc4df8de1 --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/mngview/Main.rc @@ -0,0 +1,105 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "Windows.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// Englisch (USA) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +THEMENU MENU DISCARDABLE +BEGIN + POPUP "File" + BEGIN + MENUITEM "Open", FILE_OPEN + MENUITEM "Save", FILE_SAVE + MENUITEM SEPARATOR + MENUITEM "Exit", FILE_EXIT + END + POPUP "Help" + BEGIN + MENUITEM "About", HELP_ABOUT + END +END + +#endif // Englisch (USA) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// Deutsch (Österreich) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEA) +#ifdef _WIN32 +LANGUAGE LANG_GERMAN, SUBLANG_GERMAN_AUSTRIAN +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +APPICON ICON DISCARDABLE "MNG.ico" + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""Windows.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // Deutsch (Österreich) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Engine/lib/lmng/contrib/msvc/mngview/Mng.ico b/Engine/lib/lmng/contrib/msvc/mngview/Mng.ico new file mode 100644 index 000000000..9016eafbb Binary files /dev/null and b/Engine/lib/lmng/contrib/msvc/mngview/Mng.ico differ diff --git a/Engine/lib/lmng/contrib/msvc/mngview/mng.cpp b/Engine/lib/lmng/contrib/msvc/mngview/mng.cpp new file mode 100644 index 000000000..dbfc56a01 --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/mngview/mng.cpp @@ -0,0 +1,615 @@ +//--------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------- +// +// MNGView Sample Application for VC6: +// Loads all MNG/JNG/PNG Files LibMNG can do +// Can save a single Frame to PNG Format. +// +// This code is public domain. +// Created by Nikolaus Brennig, November 14th, 2000. +// virtualnik@nol.at +// http://cust.nol.at/ppee +// +// Tab: 4 +// +//--------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------- +#define WIN32_LEAN_AND_MEAN +#include "Main.h" +#include + + +//--------------------------------------------------------------------------------------------- +// VARS: +//--------------------------------------------------------------------------------------------- +typedef struct +{ + FILE *file; + LPSTR filename; + mng_uint32 delay; +} mngstuff; + +int lineWidth; +BYTE *mngdestbuffer; +mngstuff *mymngstuff; +mng_handle Curmng; + + + +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// +//--------------------------------------------------------------------------------------------- +// callbacks for the mng decoder: +//--------------------------------------------------------------------------------------------- +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// + +//--------------------------------------------------------------------------------------------- +// memory allocation; data must be zeroed +//--------------------------------------------------------------------------------------------- +mng_ptr mymngalloc( mng_uint32 size ) +{ + return (mng_ptr)calloc(1, size); +} + + +//--------------------------------------------------------------------------------------------- +// memory deallocation +//--------------------------------------------------------------------------------------------- +void mymngfree(mng_ptr p, mng_uint32 size) +{ + free(p); +} + + +//--------------------------------------------------------------------------------------------- +// Stream open: +//--------------------------------------------------------------------------------------------- +mng_bool mymngopenstream(mng_handle mng) +{ + mngstuff *mymng; + + // look up our stream struct + mymng = (mngstuff*)mng_get_userdata(mng); + + // open the file + mymng->file = fopen( mymng->filename, "rb" ); + if( mymng->file == NULL ) + { + char temp[100]; + sprintf( temp, "Unable to open File: %s", mymng->filename ); + Warning( temp ); + return MNG_FALSE; + } + + return MNG_TRUE; +} + + +//--------------------------------------------------------------------------------------------- +// Stream open for Writing: +//--------------------------------------------------------------------------------------------- +mng_bool mymngopenstreamwrite(mng_handle mng) +{ + mngstuff *mymng; + + // look up our stream struct + mymng = (mngstuff*)mng_get_userdata(mng); + + // open the file + mymng->file = fopen( mymng->filename, "wb" ); + if( mymng->file == NULL ) + { + Warning( "unable to open file!" ); + return MNG_FALSE; + } + + return MNG_TRUE; +} + + +//--------------------------------------------------------------------------------------------- +// Stream close: +//--------------------------------------------------------------------------------------------- +mng_bool mymngclosestream(mng_handle mng) +{ + return MNG_TRUE; // We close the file ourself, mng_cleanup doesnt seem to do it... +} + + +//--------------------------------------------------------------------------------------------- +// feed data to the decoder +//--------------------------------------------------------------------------------------------- +mng_bool mymngreadstream( mng_handle mng, mng_ptr buffer, mng_uint32 size, mng_uint32 *bytesread ) +{ + mngstuff *mymng; + + // look up our stream struct + mymng = (mngstuff*)mng_get_userdata(mng); + + // read the requested amount of data from the file + *bytesread = fread( buffer, sizeof(BYTE), size, mymng->file ); + + return MNG_TRUE; +} + + +//--------------------------------------------------------------------------------------------- +// the header's been read. set up the display stuff +//--------------------------------------------------------------------------------------------- +mng_bool mymngprocessheader( mng_handle mng, mng_uint32 width, mng_uint32 height ) +{ + // Store values: + W = width; H = height; + Bits = 24; + lineWidth = ((((W * Bits) + 31) >> 5) << 2); + + // Create decoderbuffer: + mngdestbuffer = new BYTE[lineWidth*H]; + if( mngdestbuffer == 0 ) Warning( "Out of Memory!" ); + + // Create the MemoryBitmap now, we store there the image... + if( DefaultMemImage ) SelectObject( MemDC, DefaultMemImage ); + if( MemDC ) DeleteDC( MemDC ); + if( MemImage ) DeleteObject( MemImage ); + hdc = GetDC( hPicWin ); + MemDC = CreateCompatibleDC( 0 ); + MemImage = CreateCompatibleBitmap( hdc, W, H ); + DefaultMemImage = (HBITMAP)SelectObject( MemDC, MemImage ); + ReleaseDC( hPicWin, hdc ); + + // Set output style: + mng_set_canvasstyle( mng, MNG_CANVAS_BGR8 ); + + return MNG_TRUE; +} + + +//--------------------------------------------------------------------------------------------- +// return a row pointer for the decoder to fill +//--------------------------------------------------------------------------------------------- +mng_ptr mymnggetcanvasline( mng_handle mng, mng_uint32 line ) +{ + return (mng_ptr)(mngdestbuffer + (lineWidth*(H-1-line))); +} + + +//--------------------------------------------------------------------------------------------- +// timer +//--------------------------------------------------------------------------------------------- +mng_uint32 mymnggetticks(mng_handle mng) +{ + return (mng_uint32)GetTickCount(); +} + + +//--------------------------------------------------------------------------------------------- +// Refresh: +//--------------------------------------------------------------------------------------------- +mng_bool mymngrefresh( mng_handle mng, mng_uint32 x, mng_uint32 y, mng_uint32 w, mng_uint32 h ) +{ + PBITMAPINFO bmpi = (PBITMAPINFO)LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER)); + bmpi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmpi->bmiHeader.biWidth = W; + bmpi->bmiHeader.biHeight = H; + bmpi->bmiHeader.biPlanes = 1; + bmpi->bmiHeader.biCompression = BI_RGB; + bmpi->bmiHeader.biBitCount = Bits; + bmpi->bmiHeader.biSizeImage = 0; + bmpi->bmiHeader.biClrUsed = 0; + bmpi->bmiHeader.biClrImportant = 0; + + // Now blt the Image onto our MemDC... + StretchDIBits( MemDC, 0, 0, W, H, 0, 0, W, H, mngdestbuffer, bmpi, 0, SRCCOPY ); + LocalFree((PBITMAPINFO)bmpi); + + return MNG_TRUE; +} + + +//--------------------------------------------------------------------------------------------- +// interframe delay callback +//--------------------------------------------------------------------------------------------- +mng_bool mymngsettimer(mng_handle mng, mng_uint32 msecs) +{ + mngstuff *mymng; + + // look up our stream struct + mymng = (mngstuff*)mng_get_userdata(mng); + + // set the timer for when the decoder wants to be woken + mymng->delay = msecs; + + return MNG_TRUE; +} + + +//--------------------------------------------------------------------------------------------- +// Error Callback; +//--------------------------------------------------------------------------------------------- +mng_bool mymngerror( + mng_handle mng, mng_int32 code, mng_int8 severity, + mng_chunkid chunktype, mng_uint32 chunkseq, + mng_int32 extra1, mng_int32 extra2, mng_pchar text + ) +{ + char chunk[5]; + + // pull out the chuck type as a string + // FIXME: does this assume unsigned char? + chunk[0] = (char)((chunktype >> 24) & 0xFF); + chunk[1] = (char)((chunktype >> 16) & 0xFF); + chunk[2] = (char)((chunktype >> 8) & 0xFF); + chunk[3] = (char)((chunktype ) & 0xFF); + chunk[4] = '\0'; + + // output the error: + char temp[1000]; + sprintf( temp, "error playing chunk %s (%d)", chunk, chunkseq ); + Warning( temp ); + + // No need for anymore decoding: + KillTimer( hPicWin, 2 ); + + // error occured; + return MNG_FALSE; +} + + +//--------------------------------------------------------------------------------------------- +// Load a MNG/JNG/PNG file: +//--------------------------------------------------------------------------------------------- +VOID LoadMNG( LPSTR Filename, HWND hwnd, HDC hdc ) +{ + // allocate our stream data structure + mymngstuff = (mngstuff*)calloc(1, sizeof(*mymngstuff)); + if( mymngstuff == NULL ) + { + Warning( "Unable to allocate MNG struct!" ); + return; + } + + // pass the name of the file we want to play + mymngstuff->filename = Filename; + + // set up the mng decoder for our stream + Curmng = mng_initialize(mymngstuff, mymngalloc, mymngfree, MNG_NULL); + if(Curmng == MNG_NULL) + { + free(mymngstuff); + Warning( "MNG Init Error!" ); + return; + } + + // No need to store chunks: + mng_set_storechunks(Curmng, MNG_FALSE); + + // Set the colorprofile, lcms uses this: + mng_set_srgb( Curmng, MNG_TRUE ); + char DestDir[2048]; + SearchPath( NULL, "MNGVIEW.EXE", NULL, sizeof(DestDir), DestDir, NULL ); + lstrcpyn( DestDir, DestDir, lstrlen(DestDir)-lstrlen("MNGVIEW.EXE") ); + catpath( DestDir, "sRGB.icm" ); + FILE *RGBfile = fopen( DestDir, "rb" ); + if( RGBfile == 0 ) + { + mng_cleanup(&Curmng); + free(mymngstuff); + Warning( "Need file \"sRGB.icm\" !" ); + return; + } + fclose(RGBfile); + mng_set_outputprofile(Curmng, DestDir); + + // Set white as background color: + WORD Red = (255 << 8) + 255; + WORD Green = (255 << 8) + 255; + WORD Blue = (255 << 8) + 255; + mng_set_bgcolor( Curmng, Red, Green, Blue ); + + // If PNG Background is available, use it: + mng_set_usebkgd( Curmng, MNG_TRUE ); + + // set the callbacks + mng_setcb_errorproc(Curmng, mymngerror); + mng_setcb_openstream(Curmng, mymngopenstream); + mng_setcb_closestream(Curmng, mymngclosestream); + mng_setcb_readdata(Curmng, mymngreadstream); + mng_setcb_gettickcount(Curmng, mymnggetticks); + mng_setcb_settimer(Curmng, mymngsettimer); + mng_setcb_processheader(Curmng, mymngprocessheader); + mng_setcb_getcanvasline(Curmng, mymnggetcanvasline); + mng_setcb_refresh(Curmng, mymngrefresh); + + // Read the stuff: + mng_readdisplay(Curmng); + + AnimFile.CurFrame = mng_get_layercount( Curmng ); + AnimFile.MaxFrame = mng_get_framecount( Curmng ); + AnimFile.isAnimation = 1; + AnimFile.Delay = mymngstuff->delay; + + // Start the whole thing: + SetTimer( hPicWin, 2, mymngstuff->delay, 0 ); +} + + +//--------------------------------------------------------------------------------------------- +// Called when loading a new file or Appquit: +//--------------------------------------------------------------------------------------------- +void CleanUpMNG() +{ + KillTimer( hPicWin, 2 ); + mng_cleanup(&Curmng); + fclose( mymngstuff->file ); + free(mymngstuff); + delete [] mngdestbuffer; +} + + +//--------------------------------------------------------------------------------------------- +// Called when timer says next frame/layer/update is needed: +//--------------------------------------------------------------------------------------------- +void UpdateMNG() +{ + mymngstuff->delay = 0; + if( MNG_NEEDTIMERWAIT == mng_display_resume(Curmng) ) + { + KillTimer( hPicWin, 2 ); + SetTimer( hPicWin, 2, mymngstuff->delay, 0 ); + } + else + { + CleanUpMNG(); + AnimFile.CurFrame = -1; + AnimFile.MaxFrame = -1; + AnimFile.isAnimation = -1; + AnimFile.Delay = -1; + } +} + + + +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// +//--------------------------------------------------------------------------------------------- +// MNG WRITING STUFF: +//--------------------------------------------------------------------------------------------- +/////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////// +int OffsetX=0,OffsetY=0,OffsetW=0,OffsetH=0; +BYTE *srcbuffer=0, *tmpbuffer; + + +//--------------------------------------------------------------------------------------------- +// Callback for writing data: +//--------------------------------------------------------------------------------------------- +mng_bool mymngwritedata( mng_handle hMNG, mng_ptr pBuf, mng_uint32 iSize, mng_uint32 *iWritten ) +{ + mngstuff *pMydata = (mngstuff*)mng_get_userdata(hMNG); + + *iWritten = fwrite( pBuf, sizeof(BYTE), iSize, pMydata->file ); + + if( *iWritten < iSize ) + { + Warning( "write error" ); + return MNG_FALSE; + } + + return MNG_TRUE; +} + + +//--------------------------------------------------------------------------------------------- +// swap Rs and Bs... +//--------------------------------------------------------------------------------------------- +BOOL RGBFromBGR( BYTE *buf, UINT widthPix, UINT height ) +{ + UINT col, row; + LPBYTE pRed, pBlu; + BYTE tmp; + + if (buf==NULL)return FALSE; + + INT TmpRow = 0; + INT WidthBytes = widthPix*3; + if(WidthBytes & 0x003) WidthBytes = (WidthBytes | 3) + 1; + INT OurCol = 0; + for( row=0; rowbmiHeader.biSize = sizeof(BITMAPINFOHEADER); + pbmi->bmiHeader.biWidth = bmp.bmWidth; + pbmi->bmiHeader.biHeight = -bmp.bmHeight; + pbmi->bmiHeader.biPlanes = bmp.bmPlanes; + pbmi->bmiHeader.biBitCount = 24; + pbmi->bmiHeader.biCompression = BI_RGB; + pbmi->bmiHeader.biSizeImage = LineWidth * H * FrameCount; + pbmi->bmiHeader.biClrImportant = 0; + + // Alloc Memory... + srcbuffer = 0; + srcbuffer = new BYTE[LineWidth*H*FrameCount]; + if( srcbuffer == 0 ) + Warning( "srcbuffer == 0!" ); + + // get the buffer and modify the format: + if( 0 == GetDIBits( MemDC, hBmp2, 0, (WORD) (H*FrameCount), srcbuffer, pbmi, 0 ) ) + Warning( "no GetDIBits!!!" ); + RGBFromBGR( srcbuffer, W, H*FrameCount ); + if( srcbuffer == 0 ) + Warning( "srcbuffer == 0!" ); + + // Freee. + LocalFree((PBITMAPINFO)pbmi); +} + + +//--------------------------------------------------------------------------------------------- +// Writes a single PNG datastream +//--------------------------------------------------------------------------------------------- +VOID WritePNG( mng_handle hMNG, int Frame, int FrameCount ) +{ + BYTE *dstbuffer; + INT LineWidth; + INT WidthBytes; + + OffsetX=0; OffsetY=0; OffsetW=W; OffsetH=H; + + // Get WidthBytes... + WidthBytes = W*3; + LineWidth = W*3; + if(LineWidth & 0x003) LineWidth = (LineWidth | 3) + 1; + + tmpbuffer = new BYTE[(WidthBytes+1)*OffsetH]; + if( tmpbuffer == 0 ) Warning( "Out of Memory!" ); + + // Write DEFI chunk. + mng_putchunk_defi( hMNG, 0, 0, 0, MNG_TRUE, OffsetX, OffsetY, MNG_FALSE, 0, 0, 0, 0 ); + + // Write Header: + mng_putchunk_ihdr( + hMNG, + OffsetW, OffsetH, + MNG_BITDEPTH_8/*iBitdepth*/, + MNG_COLORTYPE_RGB/*iColortype*/, + MNG_COMPRESSION_DEFLATE/*iCompression*/, + MNG_FILTER_ADAPTIVE/*iFilter*/, + MNG_INTERLACE_NONE /*iInterlace*/ + ); + + // transfer data, add Filterbyte: + for( int Row=0; Row No Filter. + tmpbuffer[Row*(WidthBytes+1)]=0; + + // Copy the scanline: + memcpy( + tmpbuffer+Row*(WidthBytes+1)+1, + srcbuffer+((OffsetY+Row)*(LineWidth))+OffsetX, + WidthBytes + ); + } + + // Free srcbuffer if not animated GIF: + delete [] srcbuffer; + + // Compress data with ZLib (Deflate): + dstbuffer = new BYTE[(WidthBytes+1)*OffsetH]; + if( dstbuffer == 0 ) Warning( "Out of Memory!" ); + DWORD dstbufferSize=(WidthBytes+1)*OffsetH; + + // Compress data: + if( Z_OK != compress2( + (Bytef *)dstbuffer, (ULONG *)&dstbufferSize, + (const Bytef*)tmpbuffer, (ULONG) (WidthBytes+1)*OffsetH, + 9 + )) Warning( "Unable to compress imagedata!" ); + + // Write Data into MNG File: + mng_putchunk_idat( hMNG, dstbufferSize, (mng_ptr*)dstbuffer); + mng_putchunk_iend(hMNG); + + // Free the stuff: + delete [] tmpbuffer; + delete [] dstbuffer; +} + + +//--------------------------------------------------------------------------------------------- +// Writes a MNG (24bit) +//--------------------------------------------------------------------------------------------- +VOID SaveMNG( LPSTR Filename, HDC hdc, HBITMAP hBmp ) +{ + mng_handle hMNG; + + // check if currently a MNG file is loaded: + if( AnimFile.isAnimation == 1 ) + CleanUpMNG(); + + // Creates the srcbuffer for imagedata: + CreateSrcBuffer( 0, 1, hBmp ); + + // allocate our stream data structure + mymngstuff = (mngstuff*)calloc(1, sizeof(*mymngstuff)); + if( mymngstuff == NULL ) + { + Warning( "Cannot allocate data buffer." ); + return; + } + + // pass the name of the file we want to play + mymngstuff->filename = Filename; + + // init the lib: + hMNG = mng_initialize((mng_ptr)mymngstuff, mymngalloc, mymngfree, MNG_NULL); + if( !hMNG ) + { + Warning( "Cannot initialize libmng." ); + return; + } + else + { + mng_setcb_openstream(hMNG, mymngopenstreamwrite ); + mng_setcb_closestream(hMNG, mymngclosestream); + mng_setcb_writedata(hMNG, mymngwritedata); + + // Write File: + mng_create(hMNG); + + // Just a single Frame (save a normal PNG): + WritePNG( hMNG, 0, 1 ); + + // Now write file: + mng_write(hMNG); + + // Free the stuff: + fclose( mymngstuff->file ); + mng_cleanup(&hMNG); + } + + free( mymngstuff ); +} + + diff --git a/Engine/lib/lmng/contrib/msvc/mngview/resource.h b/Engine/lib/lmng/contrib/msvc/mngview/resource.h new file mode 100644 index 000000000..281352369 --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/mngview/resource.h @@ -0,0 +1,22 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by Main.rc +// +#define THEMENU 102 +#define APPICON 200 +#define FILE_OPEN 40002 +#define FILE_EXIT 40003 +#define HELP_ABOUT 40004 +#define FILE_SAVE 40005 +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 104 +#define _APS_NEXT_COMMAND_VALUE 40006 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Engine/lib/lmng/contrib/msvc/mngview/sRGB.icm b/Engine/lib/lmng/contrib/msvc/mngview/sRGB.icm new file mode 100644 index 000000000..7f9d18d09 Binary files /dev/null and b/Engine/lib/lmng/contrib/msvc/mngview/sRGB.icm differ diff --git a/Engine/lib/lmng/contrib/msvc/win32dll/README.txt b/Engine/lib/lmng/contrib/msvc/win32dll/README.txt new file mode 100644 index 000000000..d0c079a64 --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/win32dll/README.txt @@ -0,0 +1,23 @@ +MSVC project files for libmng.dll +--------------------------------- + +Contribution by Chad Austin +(This README by Gerard Juyn) + +These project files were kindly donated by Chad. Please note that it +requires jpeglib, zlib and lcms to be in a directory which is at the +same level as the libmng directory. + +I'm not sure how things work, since I don't have access to MSVC, so +it could be this needs a little tweaking, considering the location +where I put contributions. + +As to the DLL itself. If you want to distribute libmng.dll with your +Application and store it in the standard Windows system-directory, you +*must* use the libmng.dll provided in this distribution. *Not* a dll +you have compiled yourself, unless you place this dll in the same +directory as your own Application!!! + +Thanks, + +Gerard diff --git a/Engine/lib/lmng/contrib/msvc/win32dll/libmng.dsp b/Engine/lib/lmng/contrib/msvc/win32dll/libmng.dsp new file mode 100644 index 000000000..70fb028d8 --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/win32dll/libmng.dsp @@ -0,0 +1,465 @@ +# Microsoft Developer Studio Project File - Name="libmng" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=libmng - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "libmng.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "libmng.mak" CFG="libmng - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "libmng - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "libmng - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "libmng - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIBMNG_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W2 /GX /O2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIBMNG_EXPORTS" /D "MNG_BUILD_DLL" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 + +!ELSEIF "$(CFG)" == "libmng - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIBMNG_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W2 /Gm /GX /ZI /Od /I "zlib" /I "jpeg" /I "lcms/include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "LIBMNG_EXPORTS" /D "MNG_BUILD_DLL" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "libmng - Win32 Release" +# Name "libmng - Win32 Debug" +# Begin Group "mng" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\..\libmng_callback_xs.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\libmng_chunk_io.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\libmng_chunk_prc.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\libmng_chunk_xs.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\libmng_cms.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\libmng_display.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\libmng_dither.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\libmng_error.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\libmng_filter.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\libmng_hlapi.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\libmng_jpeg.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\libmng_object_prc.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\libmng_pixels.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\libmng_prop_xs.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\libmng_read.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\libmng_trace.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\libmng_write.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\libmng_zlib.c +# End Source File +# End Group +# Begin Group "jpeg" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jcapimin.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jcapistd.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jccoefct.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jccolor.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jcdctmgr.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jchuff.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jcinit.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jcmainct.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jcmarker.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jcmaster.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jcomapi.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jcparam.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jcphuff.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jcprepct.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jcsample.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jctrans.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jdapimin.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jdapistd.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jdatadst.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jdatasrc.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jdcoefct.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jdcolor.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jddctmgr.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jdhuff.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jdinput.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jdmainct.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jdmarker.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jdmaster.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jdmerge.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jdphuff.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jdpostct.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jdsample.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jdtrans.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jerror.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jfdctflt.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jfdctfst.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jfdctint.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jidctflt.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jidctfst.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jidctint.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jidctred.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jmemmgr.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jmemnobs.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jquant1.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jquant2.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\jpgsrc6b\jutils.c +# End Source File +# End Group +# Begin Group "lcms" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\..\..\lcms\source\CMSCNVRT.C +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\lcms\source\CMSERR.C +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\lcms\source\CMSGAMMA.C +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\lcms\source\CMSGMT.C +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\lcms\source\cmsintrp.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\lcms\source\cmsio1.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\lcms\source\CMSLUT.C +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\lcms\source\CMSMATSH.C +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\lcms\source\cmsmtrx.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\lcms\source\CMSPACK.C +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\lcms\source\cmspcs.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\lcms\source\CMSWTPNT.C +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\lcms\source\cmsxform.c +# End Source File +# End Group +# Begin Group "zlib" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\..\..\..\zlib\adler32.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\zlib\compress.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\zlib\crc32.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\zlib\deflate.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\zlib\infblock.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\zlib\infcodes.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\zlib\inffast.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\zlib\inflate.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\zlib\inftrees.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\zlib\infutil.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\zlib\trees.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\zlib\uncompr.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\..\zlib\zutil.c +# End Source File +# End Group +# End Target +# End Project diff --git a/Engine/lib/lmng/contrib/msvc/win32dll/libmng.dsw b/Engine/lib/lmng/contrib/msvc/win32dll/libmng.dsw new file mode 100644 index 000000000..40148e03b --- /dev/null +++ b/Engine/lib/lmng/contrib/msvc/win32dll/libmng.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "libmng"=".\libmng.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/Engine/lib/lmng/doc/Plan1.png b/Engine/lib/lmng/doc/Plan1.png new file mode 100644 index 000000000..ced551727 Binary files /dev/null and b/Engine/lib/lmng/doc/Plan1.png differ diff --git a/Engine/lib/lmng/doc/Plan2.png b/Engine/lib/lmng/doc/Plan2.png new file mode 100644 index 000000000..3619ea663 Binary files /dev/null and b/Engine/lib/lmng/doc/Plan2.png differ diff --git a/Engine/lib/lmng/doc/doc.readme b/Engine/lib/lmng/doc/doc.readme new file mode 100644 index 000000000..e92e93cb5 --- /dev/null +++ b/Engine/lib/lmng/doc/doc.readme @@ -0,0 +1,19 @@ +This directory hosts the documentation for libmng. + +You will find a lot of useful info on the web-site: +http://www.libmng.com + +Man-pages are in the man sub-directory + +RPM specification files are in the RPM sub-directory + +Files in this directory: + +- libmng.txt + +Description of the library proper and its usage + +- Plan1.png & Plan2.png + +Visual representation of the functional and technical +design of the library (in PNG format of course!) diff --git a/Engine/lib/lmng/doc/libmng.txt b/Engine/lib/lmng/doc/libmng.txt new file mode 100644 index 000000000..b9e9bc4b8 --- /dev/null +++ b/Engine/lib/lmng/doc/libmng.txt @@ -0,0 +1,1107 @@ +libmng - Multiple-image Network Graphics (MNG) Reference Library 1.0.9 + +DESCRIPTION +The libmng library supports decoding, displaying, encoding, and various +other manipulations of the Multiple-image Network Graphics (MNG) format +image files. It uses the zlib compression library, and optionally the +JPEG library by the Independant JPEG Group (IJG) and/or +lcms (little cms), a color-management library by Marti Maria Saguer. + + +I. Introduction + +This file describes how to use and modify the MNG reference library +(known as libmng) for your own use. There are seven sections to this +file: introduction, callbacks, housekeeping, reading, displaying, +writing, and modification and configuration notes for various special +platforms. We assume that libmng is already installed; see the +INSTALL.README file for instructions on how to install libmng. + +Libmng was written to support and promote the MNG specification. + +The latest MNG specification (currently 1.0) is available at + http://www.libpng.org/pub/mng/ + +Other information about MNG can be found at the MNG home page at + http://www.libpng.org/pub/mng/ + +The latest version of libmng can be found at its own homepage at + http://www.libmng.com/ + +In most cases the library will not need to be changed. +For standardization purposes the library contains both a Windows DLL +and a makefile for building a shared library (SO). The library is +written in C, but an interface for Borland Delphi is also available. + +Libmng has been designed to handle multiple sessions at one time, +to be easily modifiable, to be portable to the vast majority of +machines (ANSI, K&R, 32-, and 64-bit) available, and to be easy +to use. + +Libmng uses zlib for its compression and decompression of MNG files. +Further information about zlib, and the latest version of zlib, can be +found at the zlib home page, . +The zlib compression utility is a general purpose utility that is +useful for more than MNG/PNG files, and can be used without libmng. +See the documentation delivered with zlib for more details. + +Libmng optionally uses the JPEG library by the Independant JPEG Group +(IJG). This library is used for the JNG sub-format, which is part of +the MNG specification, and allows for inclusion of JPEG decoded and +thus highly compressed (photographic) images. +Further information about the IJG JPEG library and the latest sources +can be found at . + +Libmng can also optionally use the lcms (little CMS) library by +Marti Maria Saguer. This library provides an excellent color-management +system (CMS), which gives libmng the ability to provide full +color-correction for images with the proper color-information encoded. +Further information and the latest sources can be found at +. + +Libmng is thread safe, provided the threads are using different +handles as returned by the initialization call. +Each thread should have its own handle and thus its own image. +Libmng does not protect itself against two threads using the +same instance of a handle. + +The libmng.h header file is the single reference needed for programming +with libmng: + +#include + + +II. Callbacks + +Libmng makes extensive use of callback functions. This is meant to +keep the library as platform-independant and flexible as possible. +Actually, the first call you will make to the library, already contains +three parameters you can use to provide callback entry-points. + +Most functions must return a mng_bool (boolean). Returning MNG_FALSE +indicates the library the callback failed in some way and the library +will immediately return from whatever it was doing back to the +application. Returning MNG_TRUE indicates there were no problems and +processing can continue. + +Let's step through each of the possible callbacks. The sections on +reading, displaying and writing will also explain which callbacks are +needed when and where. + +- mng_ptr mng_memalloc (mng_size_t iLen) + +A very basic function which the library uses to allocate a memory-block +with the given size. A typical implementation would be: + + mng_ptr my_alloc (mng_size_t iLen) { + return calloc (1, iSize); + } + +Note that the library requires you to zero-out the memory-block!!! + +- void mng_memfree (mng_ptr pPtr, + mng_size_t iLen) + +Counterpart of the previous function. Typically: + + void my_free (mng_ptr pPtr, mng_size_t iLen) { + free (pPtr); + } + +- mng_bool mng_openstream (mng_handle hHandle) +- mng_bool mng_closestream (mng_handle hHandle) + +These are called by the library just before it starts to process +(either read or write) a file and just after the processing stops. +This is the recommended place to do I/O initialization & finalization. +Whether you do or not, is up to you. The library does not put any +meaning into the calls. They are simply provided for your convenience. + +- mng_bool mng_readdata (mng_handle hHandle, + mng_ptr pBuf, + mng_uint32 iBuflen, + mng_uint32p pRead) + +This function is called when the library needs some more input while +reading an image. The reading process supports two modes: +Suspension-mode (SMOD) and non-suspension-mode (NSMOD). +See mng_set_suspensionmode() for a more detailed description. + +In NSMOD, the library requires you to return exactly the amount of bytes +requested (= iBuflen). Any lesser amount indicates the input file +is exhausted and the library will return a MNG_UNEXPECTEDEOF errorcode. + +In SMOD, you may return a smaller amount of bytes than requested. +This tells the library it should temporarily wait for more input to +arrive. The lib will return with MNG_NEEDMOREDATA, and will expect a +call to mng_read_resume() or mng_display_resume() next, as soon as +more input-data has arrived. + +For NSMOD this function could be as simple as: + + mng_bool my_read (mng_handle hHandle, + mng_ptr pBuf, + mng_uint32 iBuflen, + mng_uint32p pRead) { + *pRead = fread (pBuf, 1, iBuflen, myfile); + return MNG_TRUE; + } + +- mng_bool mng_writedata (mng_handle hHandle, + mng_ptr pBuf, + mng_uint32 iBuflen, + mng_uint32p pWritten) + +This function is called during the mng_write() function to actually +output data to the file. There is no suspension-mode during write, +so the application must return the exact number of bytes the library +requests to be written. + +A typical implementation could be: + + mng_bool my_write (mng_handle hHandle, + mng_ptr pBuf, + mng_uint32 iBuflen, + mng_uint32p pWritten) { + *pWritten = fwrite (pBuf, 1, iBuflen, myfile); + return MNG_TRUE; + } + +- mng_bool mng_errorproc (mng_handle hHandle, + mng_int32 iErrorcode, + mng_int8 iSeverity, + mng_chunkid iChunkname, + mng_uint32 iChunkseq, + mng_int32 iExtra1, + mng_int32 iExtra2, + mng_pchar zErrortext) + +This function is called whenever an error is detected inside the +library. This may be caused by invalid input, callbacks indicating +failure, or wrongfully calling functions out of place. + +If you do not provide this callback the library will still return +an errorcode from the called function, and the mng_getlasterror() +function can be used to retrieve the other parameters. + +This function is currently only provided for convenience, but may +at some point be used to indicate certain errors may be acceptable, +and processing should continue. + +- mng_bool mng_traceproc (mng_handle hHandle, + mng_int32 iFuncnr, + mng_int32 iFuncseq, + mng_pchar zFuncname) + +This function is provided to allow a functional analysis of the +library. This may be useful if you encounter certain errors and +cannot determine what the problem is. + +Almost all functions inside the library will activate this +callback with an appropriate function-name at the start and end +of the function. Please note that large images may generate an +enormous amount of calls. + +- mng_bool mng_processheader (mng_handle hHandle, + mng_uint32 iWidth, + mng_uint32 iHeight) + +This function is called once the header information of an input- +image has been processed. At this point the image dimensions are +available and also some other properties depending on the type +of the image. Eg. for a MNG the frame-/layercount, playtime & +simplicity fields are known. + +The primary purpose of this callback is to inform the application +of the size of the image, and for the application to initialize +the drawing canvas to be used by the library. This is also a good +point to set the canvas-style. Eg. mng_set_canvasstyle(). + +- mng_bool mng_processtext (mng_handle hHandle, + mng_uint8 iType, + mng_pchar zKeyword, + mng_pchar zText, + mng_pchar zLanguage, + mng_pchar zTranslation) + +This callback is activated for each textual chunk in the input- +image. These are tEXt, zTXt & iTXt. It may be used to retain +specific comments for presentation to the user. + +- mng_bool mng_processsave (mng_handle hHandle) +- mng_bool mng_processseek (mng_handle hHandle, + mng_pchar zName) + +The purpose of these callbacks is to signal the processing of the +SAVE & SEEK chunks in a MNG input-file. This may be used in the +future to specify some special processing. At the moment these +functions are only provided as a signal. + +- mng_ptr mng_getcanvasline (mng_handle hHandle, + mng_uint32 iLinenr) +- mng_ptr mng_getbkgdline (mng_handle hHandle, + mng_uint32 iLinenr) +- mng_ptr mng_getalphaline (mng_handle hHandle, + mng_uint32 iLinenr) + +These callbacks are used to access the drawing canvas, background +canvas and an optional separate alpha-channel canvas. The latter is +used only with the MNG_CANVAS_RGB8_A8 canvas-style. + +If the getbkgdline() callback is not supplied the library will +composite full or partially transparent pixels in the image against +a specified background color. See mng_set_bgcolor() for more details. +If a chosen canvas-style includes an alpha-channel, this callback +is very likely not needed. + +The application is responsible for returning a pointer to a line of +pixels, which should be in the exact format as defined by the call +to mng_set_canvasstyle() and mng_set_bkgdstyle(), without gaps between +the representation of each pixel. + +- mng_bool mng_refresh (mng_handle hHandle, + mng_uint32 iX, + mng_uint32 iY, + mng_uint32 iWidth, + mng_uint32 iHeight) + +This callback is called when the library has drawn a complete frame +onto the drawing canvas, and it is ready to be displayed. +The application is responsible for transferring the drawing canvas +from memory onto the actual output device. + +- mng_uint32 mng_gettickcount (mng_handle hHandle) + +This function should return the number of milliseconds on some internal +clock. The entire animation timing depends heavily on this function, +1and the number returned should be as accurate as possible. + +- mng_bool mng_settimer (mng_handle hHandle, + mng_uint32 iMsecs) + +This callback is activated every time the library requires a "pause". +Note that the function itself should NOT execute the wait. It should +simply store the time-field and allow the library to return. Libmng +will return with the MNG_NEEDTIMERWAIT code, indicating the callback +was called and it is now time to execute the pause. + +After the indicated number of milliseconds have elapsed, the application +should call mng_display_resume(), to resume the animation as planned. + +This method allows for both a real timer or a simple wait command in the +application. Whichever method you select, both the gettickcount() and +settimer() callbacks are crucial for proper animation timing. + +- mng_bool mng_processgamma (mng_handle hHandle, + mng_uint32 iGamma) +- mng_bool mng_processchroma (mng_handle hHandle, + mng_uint32 iWhitepointx, + mng_uint32 iWhitepointy, + mng_uint32 iRedx, + mng_uint32 iRedy, + mng_uint32 iGreenx, + mng_uint32 iGreeny, + mng_uint32 iBluex, + mng_uint32 iBluey) +- mng_bool mng_processsrgb (mng_handle hHandle, + mng_uint8 iRenderingintent) +- mng_bool mng_processiccp (mng_handle hHandle, + mng_uint32 iProfilesize, + mng_ptr pProfile) +- mng_bool mng_processarow (mng_handle hHandle, + mng_uint32 iRowsamples, + mng_bool bIsRGBA16, + mng_ptr pRow) + +These callbacks are only required when you selected the MNG_APP_CMS +directive during compilation of the library. See the configuration +section for more details. + +- mng_bool mng_iteratechunk (mng_handle hHandle, + mng_handle hChunk, + mng_chunkid iChunkid, + mng_uint32 iChunkseq) + +This callback is only used for the mng_iterate_chunks() function. +It is called exactly once for each chunk stored. + + +III. Housekeeping + + +> Memory management + +The library can use internal memory allocation/deallocation or use +provided callbacks for its memory management. The choice is made at +compilation time. See the section on customization for details. + +If internal management has been selected, the memory callback functions +need not be supplied. Even if you do supply them they will not be used. +The actual code used is similar to the code discussed in the callback +section: + + pPtr = calloc (1, iSize); + + free (pPtr); + +If your compiler does not support these functions, or you wish to monitor +the library's use of memory for certain reasons, you can choose to +compile the library with external memory management. In this case the +memory callback functions MUST be supplied, and should function as if the +above code was used. + + +> Initialization + +The basic initialization of the library is short and swift: + + myhandle = mng_initialize (myuserdata, my_alloc, + my_free, MNG_NULL); + if (myhandle == MNG_NULL) + /* process error */; + +The first field is an application-only parameter. It is saved in +libmng's internal structures and available at all times through the +mng_get_userdata() function. This is especially handy in callback functions +if your program may be handling multiple files at the same time. + +The second and third field supply the library with the memory callback +1function entry-points. These are described in more detail in the callback +section and the previous paragraph. + +The fourth and last field may be used to supply the library with the +entry-point of a trace callback function. For regular use you will not +need this! + +The function returns a handle which will be your ticket to MNG-heaven. +All other functions rely on this handle. It is the single fixed unique +reference-point between your application and the library. + +You should call the initialization function for each image you wish to +process simultaneously. If you are processing images consecutively, you can +reset the internal status of the library with the mng_reset() function. +This function will clear all internal state variables, free any stored +chunks and/or objects, etc, etc. Your callbacks and other external parameters +will be retained. + +After you successfully received the handle it is time to set the required +callbacks. The sections on reading, displaying & writing indicate which +callbacks are required and which are optional. +To set the callbacks simply do: + + myretcode = mng_setcb_xxxxxx (myhandle, my_xxxxxx); + if (myretcode != MNG_NOERROR) + /* process error */; + +Naturally you'd replace the x's with the name of the callback. + + +> Cleanup + +Once you've gotten hold of that precious mng_handle, you should always, +and I mean always, call the cleanup function when you're done. +Just do: + + mng_cleanup (myhandle); + +And you're done. There shouldn't be an ounce of memory spilled after +that call. + +Note that if you would like to process multiple files consecutively +you do not need to do mng_cleanup() / mng_initialize() between each file +but simply + + myretcode = mng_reset (myhandle); + if (myretcode != MNG_NOERROR) + /* process error */; + +will suffice. Saves some time and effort, that. + + +> Error handling + +From the examples in the previous paragraphs you may have noticed a +meticulous scheme for error handling. And yes, that's exactly what it is. +Practically each call simply returns an errorcode, indicating success, +eg. MNG_NOERROR or failure, anything else but MNG_NEEDMOREDATA and +MNG_NEEDTIMERWAIT. These latter two will be discussed in more detail in +their respective fields of interest: the reading section and displaying +section respectively. + +It is the application's responsibility to check the returncode after +each call. You can call mng_getlasterror() to receive the details of +the last detected error. This even includes a discriptive error-message +if you enabled that option during compilation of the library. + +Note that after receiving an error it is still possible to call the +library, but it's also very likely that any following call will fail. +The only functions deemed to work will be mng_reset() and mng_cleanup(). +Yes, if you abort your program after an error, you should still call +mng_cleanup(). + + +IV. Reading + +Reading a MNG, JNG or PNG is fairly easy. It depends slightly on your +ultimate goal how certain specifics are to be handled, but the basics +are similar in all cases. + +For the read functioins to work you must have compiled the library with +the MNG_READ_SUPPRT directive. The standard DLL and Shared Library +have this on by default! + + +> Setup + +Naturally you must have initialized the library and be the owner of +a mng_handle. The following callbacks are essential: + + mng_openstream, mng_readdata, mng_closestream + +You may optionally define: + + mng_errorproc, mng_traceproc + mng_processheader, mng_processtext + mng_processsave, mng_processseek + +The reading bit will also fail if you are already creating or +displaying a file. Seems a bit obvious, but I thought I'd mention it, +just in case. + + +> To suspend or not to suspend + +There is one choice you need to make before calling the read function. +Are you in need of suspension-mode or not? + +If you're reading from a disk you most certainly do not need +suspension-mode. Even the oldest and slowest of disks will be fast +enough for straight reading. + +However, if your input comes from a really slow device, such as a +dialup-line or the likes, you may opt for suspension-mode. This is done +by calling + + myretcode = mng_set_suspensionmode (myhandle, + MNG_TRUE); + if (myretcode != MNG_NOERROR) + /* process error */; + +Suspension-mode will force the library to use special buffering on the +input. This allows your application to receive data of arbitrarily length +and return this in the mng_readdata() callback, without disturbing the +chunk processing routines of the library. + +Suspension-mode does require a little extra care in the main logic of the +1application. The read function may return with MNG_NEEDMOREDATA when the +mng_readdata() callback returns less data then it needs to process the +next chunk. This indicates the application to wait for more data to arrive +and then resume processing by calling mng_read_resume(). + + +> The read HLAPI + +The actual reading is just plain simple. Since all I/O is done +1outside the library through the callbacks, the library can focus on +its real task. Understanding, checking and labelling the input data! + +All you really need to do is this: + + myretcode = mng_read (myhandle); + if (myretcode != MNG_NOERROR) + /* process error */; + +Of course, if you're on suspension-mode the code is a little more +complicated: + + myretcode = mng_read (myhandle); + + while (myretcode == MNG_NEEDMOREDATA) { + /* wait for input-data to arrive */ + myretcode = mng_read_resume (myhandle); + } + + if (myretcode != MNG_NOERROR) + /* process error */; + +This is rather crude and more sophisticated programming methods may +dictate another approach. Whatever method you decide on, it should +act as if the above code was in its place. + +There is also the mng_readdisplay() function, but this is discussed +in the displaying section. It functions pretty much as the mng_read() +function, but also immediately starts displaying the image. +mng_read_resume() should be replaced by mng_display_resume() in that +case! + + +> What happens inside + +What actually happens inside the library depends on the configuration +options set during the compilation of the library. + +Basically the library will first read the 8-byte file header, to determine +its validity and the type of image it is about to process. Then it will +repeatedly read a 4-byte chunk-length and then the remainder of the chunk +until it either reaches EOF (indicated by the mng_readdata() callback) or +implicitly decides EOF as it processed the logically last chunk of the +image. + +Applications that require strict conformity and do not allow superfluous +data after the ending chunk, will need to perform this check in their +mng_closestream() callback. + +Each chunk is then checked on CRC, after which it is handed over to the +appropriate chunk processing routine. These routines will disect the +chunk, check the validity of its contents, check its position with respect +to other chunks, etc, etc. + +If everything checks out, the chunk is further processed as follows: + +If display support has been selected during compilation, certain pre-display +initialization will take place. + +If chunk-storage support has been selected during compilation, the chunks +data may be stored in a special internal structure and held for future +reference. + + +> Storing and accessing chunks + +One of the compilation options activates support for chunk storage. +This option may be useful if you want to examine an image. The directive +is MNG_STORE_CHUNKS. You must also turn on the MNG_ACCESS_CHUNKS +directive. + +The actual storage facility can be turned on or off with the +mng_set_storechunks() function. If set to MNG_TRUE, chunks will be +stored as they are read. + +At any point you can then call the mng_iterate_chunks() function +to iterate through the current list of chunks. This function requires +a callback which is called for each chunk and receives a specific +chunk-handle. This chunk-handle can be used to call the appropriate +mng_getchunk_xxxx() function, to access the chunks properties. + +A typical implementation may look like this: + + mng_bool my_iteratechunk (mng_handle hHandle, + mng_handle hChunk, + mng_chunkid iChunkid, + mng_uint32 iChunkseq) { + switch (iChunkid) { + case MNG_UINT_MHDR : { /* process MHDR */; + break; } + case MNG_UINT_FRAM : { /* process FRAM */; + break; } + + ...etc... + + case MNG_UINT_HUH : { /* unknown chunk */; + break; } + default : { /* duh; forgot one */; } + } + + return MNG_TRUE; /* keep'm coming */ + } + +To get to the actual chunk fields of lets say a SHOW chunk you would do: + + mng_bool isempty; + mng_uint16 firstid, lastid; + mng_uint8 showmode; + + myretcode mng_getchunk_show (hHandle, hChunk, + isempty, firstid, + lastid, showmode); + if (myretcode != MNG_NOERROR) + /* process error */; + + +V. Displaying + + +> Setup + +Assuming you have initialized the library and are the owner of +a mng_handle. The following callbacks are essential: + + mng_getcanvasline, mng_refresh + mng_gettickcount, mng_settimer + +If you wish to use an application supplied background you must supply: + + mng_getbkgdline + +If you wish to use the MNG_CANVAS_RGB8_A8 canvas style you must supply: + + mng_getalphaline + +You may optionally define: + + mng_errorproc, mng_traceproc + mng_processheader, mng_processtext + mng_processsave, mng_processseek + +Note that the mng_processheader() callback is optional but will +be quite significant for proper operation! + +Displaying an image will fail if you are creating a file or already +displaying one. Yes, you can't display it twice! + + +> A word on canvas styles + +The canvas style describes how your drawing canvas is made up. +You must set this before the library actually starts drawing, so +the mng_processheader() callback is a pretty good place for it. + +Currently only 8-bit RGB canvas styles are supported, either with +or without an alpha channel. + +If you like to do alpha composition yourself you can select one of +the canvas styles that include an alpha channel. You can even have +a separate alpha canvas by selecting the MNG_CANVAS_RGB8_A8 style. + +All styles require a compact model. Eg. MNG_CANVAS_BGR8 requires +your canvas lines in bgrbgrbgr... storage, where each letter +represents an 8-bit value of the corresponding color, and each +threesome makes up the values of one(1) pixel. + +The library processes a line at a time, so the canvas lines do not +actually need to be consecutive in memory. + + +> Alpha composition and application backgrounds + +All Network Graphics can be partially transparent. This requires +special processing if you need to display an image against some +background. Note that the MNG header (MHDR chunk) contains a +simplicity field indicating whether transparency information in +the file is critical or not. This only applies to embedded images, +which means the full image-frame of the MNG may still contain fully +transparent pixels! + +Depending on your needs you can supply a single background color, +a background canvas or tell the library to return the alpha-channel +and do alpha composition yourself. + +This is different from the BACK chunk in a MNG, or the bKGD chunk +in an (embedded) PNG or JNG. The BACK chunk indicates an optional or +mandatory background color and/or image. The bKGD chunk only indicates +an optional background color. These chunks indicate the Authors +preferences. They may be absent in which case you need to supply +some sort of background yourself. + +> Composing against a background color + +This is the easiest method. Call the mng_set_bgcolor() function to +set the values of the red, green and blue component of your preferred +background color. + +Use one of the canvas styles that do not have an alpha-channel, and +which matches your output requirements. + +> Composing against a background canvas + +This is somewhat more complicated. You will need to set the +mng_getbkgdline() callback. This will be called whenever the library +needs to compose a partially transparent line. + +This canvas must hold the background against which the image should +be composed. Its size must match exactly with the image dimensions +and thus the drawing canvas! + +Use one of the canvas styles that do not have an alpha-channel, and +which matches your output requirements. The canvas style of the +background canvas may even differ from the drawing canvas. The library's +composing will still function properly. + +> Composing within the application + +If you have the option in your application to draw a (partially) +transparent canvas to the output device, this option is preferred. + +Select one of the canvas styles that do have an alpha-channel. +The library will now supply the appropriate alpha information, +allowing the application to compose the image as it sees fit. + + +> Color information and CMS + +Network Graphics may, and usually will, contain color-correction +information. This information is intended to compensate for the +difference in recording and display devices used. + +This document does not address the specifics of color-management. +See the PNG specification for a more detailed description. + +> Using little cms by Marti Maria Saguer + +This is the easiest method, providing you can compile the lcms package. +Select the MNG_FULL_CMS directive during compilation, and sit back and +relax. The library will take care of all color-correction for you. + +> Using an OS- or application-supplied CMS + +If you are so lucky to have access to CMS functionality from within +your application, you may instruct the library to leave color-correction +to you. + +Select the MNG_APP_CMS directive during compilation of the library. +You MUST also set the following callbacks: + + mng_processgamma, mng_processchroma, + mng_processsrgb, mng_processiccp and + mng_processarow + +The last callback is called when the library needs you to correct +an arbitrary line of pixels. The other callbacks are called when +the corresponding color-information is encountered in the file. +You must store this information somewhere for use in the +mng_processarow() callback. + +> Using gamma-only correction + +This isn't a preferred method, but it's better than no correction +at all. Gamma-only correction will at least compensate for +gamma-differences between the original recorder and your output device. + +Select the MNG_GAMMA_ONLY directive during compilation +of the library. Your compiler MUST support fp operations. + +> No color correction + +Ouch. This is really bad. This is the least preferred method, +but may be necessary if your system cannot use lcms, doesn't +have its own CMS, and does not allow fp operations, ruling out +the gamma-only option. + +Select the MNG_NO_CMS directive during compilation. +Images will definitely not be displayed as seen by the Author!!! + + +> Animations and timing + +Animations require some form of timing support. The library relies +on two callbacks for this purpose. The mng_gettickcount() and +mng_settimer() callbacks. mng_gettickcount() is used to determine +the passing of time in milliseconds since the beginning of the +animation. This is also used to compensate during suspension-mode +if you are using the mng_readdisplay() function to read & display +the file simultaneously. + +The callback may return an arbitrary number of milliseconds, but +this number must increase proportionaly between calls. Most modern +systems will have some tickcount() function which derives its +input from an internal clock. The value returned from this function +is more than adequate for libmng. + +The mng_settimer() callback is called when the library determines +a little "pause" is required before rendering another frame of the +animation. The pause interval is also expressed in milliseconds. +Your application should store this value and return immediately. +The library will then make appropriate arrangements to store its +internal state and returns to your application with the +MNG_NEEDTIMERWAIT code. + +At that point you should suspend processing and wait the given +interval. Please use your OS features for this. Do not engage some +sort of loop. That is real bad programming practice. Most modern +systems will have some timing functions. A simple wait() function +may suffice, but this may prevent your applications main-task from +running, and possibly prevent the actual update of your output device. + + +> The mng_refresh() callback + +The mng_refresh() callback is called whenever the library has +"finished" drawing a new frame onto your canvas, and just before it +will call the mng_settimer() callback. + +This allows you to perform some actions necessary to "refresh" the +canvas onto your output device. Please do NOT suspend processing +inside this callback. This must be handled after the mng_settimer() +callback! + + +> Displaying while reading + +This method is preferred if you are reading from a slow input device +(such as a dialup-line) and you wish to start displaying something +as quickly as possible. This functionality is provided mainly for +browser-type applications but may be appropriate for other +applications as well. + +The method is usually used in unison with the suspension-mode of +the read module. A typical implementation would look like this: + + /* initiale library and set required callbacks */ + + /* activate suspension-mode */ + myretcode = mng_set_suspensionmode (myhandle, + MNG_TRUE); + if (myretcode != MNG_NOERROR) + /* process error */; + + myretcode = mng_readdisplay (myhandle); + + while ((myretcode == MNG_NEEDMOREDATA) || + (myretcode == MNG_NEEDTIMERWAIT)) { + if (myretcode == MNG_NEEDMOREDATA) + /* wait for more input-data */; + else + /* wait for timer interval */; + + myretcode = mng_display_resume (myhandle); + } + + if (myretcode != MNG_NOERROR) + /* process error */; + +More advanced programming methods may require a different approach, +but the final result should function as in the code above. + + +> Displaying after reading + +This method is used to display a file that was previously read. +It is primarily meant for viewers with direct file access, such as +1a local harddisk. + +Once you have successfully read the file, all you need to do is: + + myretcode = mng_display (myhandle); + + while (myretcode == MNG_NEEDTIMERWAIT) { + /* wait for timer interval */; + myretcode = mng_display_resume (myhandle); + } + + if (myretcode != MNG_NOERROR) + /* process error */; + +Again, more advanced programming methods may require a different +approach, but the final result should function as in the code above. + + +> Display manipulation + +Several HLAPI functions are provided to allow a user to manipulate +the normal flow of an animation. + +- mng_display_freeze (mng_handle hHandle) + +This will "freeze" the animation in place. + +- mng_display_resume (mng_handle hHandle) + +This function can be used to resume a frozen animation, or to force +the library to advance the animation to the next frame. + +- mng_display_reset (mng_handle hHandle) + +This function will "reset" the animation into its pristine state. +Calling mng_display() afterwards will re-display the animation +from the first frame. + +- mng_display_golayer (mng_handle hHandle, + mng_uint32 iLayer) +- mng_display_goframe (mng_handle hHandle, + mng_uint32 iFrame) +- mng_display_goplaytime (mng_handle hHandle, + mng_uint32 iPlaytime) + +These three functions can be used to "jump" to a specific layer, frame +or timeslot in the animation. You must "freeze" the animation before +using any of these functions. + +All above functions may only be called during a timer interval! +It is the applications responsibility to cleanup any resources with +respect to the timer wait. + + +VI. Writing + +The main focus of the library lies in its displaying capabilites. +But it does offer writing support as well. +You can create and write a file, or you can write a file you +have previously read, providing the storage of chunks was enabled +and active. + +For this to work you must have compiled the library with the +MNG_WRITE_SUPPO1RT and MNG_ACCESS_CHUNKS directives. The standard DLL and +Shared Library have this on by default! + + +> Setup + +As always you must have initialized the library and be the owner of +a mng_handle. The following callbacks are essential: + + mng_openstream, mng_writedata, mng_closestream + +You can optionally define: + + mng_errorproc, mng_traceproc + +The creation and writing functions will fail if you are in the middle +of reading, creating or writing a file. + + +> Creating a new file + +To start a new file the library must be in its initial state. +First you need to tell the library your intentions: + + myretcode = mng_create (myhandle); + if (myretcode != MNG_NOERROR) + /* process error */; + +After that you start adding the appropriate chunks: + + myretcode = mng_putchunk_mhdr (myhandle, ...); + if (myretcode != MNG_NOERROR) + /* process error */; + +And so on, and so forth. Note that the library will automatically signal +the logical end of the file by the ending chunk. Also the first chunk +will indicate the library the filetype (eg. PNG, JNG or MNG) and force +the proper signature when writing the file. + +The code above can be simplified, as you can always get the last errorcode +by using the mng_getlasterror() function: + + if ( (mng_putchunk_xxxx (myhandle, ...)) or + (mng_putchunk_xxxx (myhandle, ...)) or + ...etc... ) + /* process error */; + +Please note that you must have a pretty good understanding of the chunk +specification. Unlike the read functions, there are virtually no checks, +so it is quite possible to write completely wrong files. +It is a good practice to read back your file into the library to verify +its integrity. + +Once you've got all the chunks added, all you do is: + + myretcode mng_write (myhandle); + if (myretcode != MNG_NOERROR) + /* process error */; + +And presto. You're done. The real work is of course carried out in +your callbacks. Note that this is a single operation as opposed to +the read & display functions that may return with MNG_NEEDMOREDATA +and/or MNG_NEEDTIMERWAIT. The write function just does the job, and +only returns after it's finished or if it encounters some +unrecoverable error. + + +> Writing a previously read file + +If you have already successfully read a file, you can use the library to +write it out as a copy or something. You MUST have compiled the library +with the MNG_STORE_CHUNKS directive, and you must have done +mng_set_storechunks (myhandle, MNG_TRUE). + +This doesn't require the MNG_ACCESS_CHUNKS directive, unless you want +to fiddle with the chunks as well. + +Again all you need to do is: + + myretcode mng_write (myhandle); + if (myretcode != MNG_NOERROR) + /* process error */; + + +VII. Modifying/Customizing libmng: + +to do + +> Compilation directives + +to do + +> Platform dependant modification + +to do + + +References : + +libmng : + http://www.libmng.com/ + +zlib : + http://www.info-zip.org/pub/infozip/zlib/ + +IJG JPEG library : + http://www.ijg.org/ + +lcms (little CMS) by Marti Maria Saguer : + http://www.littlecms.com/ + +MNG specification: + http://www.libpng.org/pub/mng + + +In the case of any inconsistency between the MNG specification +and this library, the specification takes precedence. + + +The contributing authors would like to thank all those who helped +with testing, bug fixes, and patience. This wouldn't have been +possible without all of you!!! + + +COPYRIGHT NOTICE: + +Copyright (c) 2000,2001 Gerard Juyn + +For the purposes of this copyright and license, "Contributing Authors" +is defined as the following set of individuals: + + Gerard Juyn + +The MNG Library is supplied "AS IS". The Contributing Authors +disclaim all warranties, expressed or implied, including, without +limitation, the warranties of merchantability and of fitness for any +purpose. The Contributing Authors assume no liability for direct, +indirect, incidental, special, exemplary, or consequential damages, +which may result from the use of the MNG Library, even if advised of +the possibility of such damage. + +Permission is hereby granted to use, copy, modify, and distribute this +source code, or portions hereof, for any purpose, without fee, subject +to the following restrictions: + +1. The origin of this source code must not be misrepresented; +you must not claim that you wrote the original software. + +2. Altered versions must be plainly marked as such and must not be +misrepresented as being the original source. + +3. This Copyright notice may not be removed or altered from any source +or altered source distribution. + +The Contributing Authors specifically permit, without fee, and +encourage the use of this source code as a component to supporting +the MNG and JNG file format in commercial products. If you use this +source code in a product, acknowledgment would be highly appreciated. + + +Remarks : + +Parts of this software have been adapted from the libpng library. +Although this library supports all features from the PNG specification +(as MNG descends from it) it does not require the libpng library. +It does require the zlib library and optionally the IJG JPEG library, +and/or the "little-cms" library by Marti Maria Saguer (depending on the +inclusion of support for JNG and Full-Color-Management respectively. + +This library's function is primarily to read and display MNG +animations. It is not meant as a full-featured image-editing +component! It does however offer creation and editing functionality +at the chunk level. (future modifications may include some more +support for creation and or editing) + diff --git a/Engine/lib/lmng/doc/man/jng.5 b/Engine/lib/lmng/doc/man/jng.5 new file mode 100644 index 000000000..0e371ad0e --- /dev/null +++ b/Engine/lib/lmng/doc/man/jng.5 @@ -0,0 +1,37 @@ +.TH JNG 5 "July 26, 2000" +.SH NAME +jng \- JPEG Network Graphics (JNG) sub-format +.SH DESCRIPTION +JNG (JPEG Network Graphics) is a sub-format of the MNG (Multiple-image +Network Graphics) format. As with MNG it extends on the features of the +popular PNG (Portable Network Graphics) image-format. +.br + +This sub-format was designed to support a lossy compression-method. +It is based completely on the JPEG specification. It adds the high-compression +ratios of JPEG for photographic images. + +As a member of the Network Graphics family, JNG was deemed adequate as a +stand-alone format as it extends the JPEG format with color-correction and +transparency features. + +.SH "SEE ALSO" +.IR png(5) ", " mng(5) ", " libmng(3) +.LP +MNG 1.00, February 9, 2001: +.IP +http://www.libpng.org/pub/mng + +.SH AUTHORS +This man page: Gerard Juyn +.LP +Multiple-image Network Graphics (MNG) Specification Version 1.00 (Februari 9, 2001): +Glenn Randers-Pehrson and others (png-list@ccrc.wustl.edu). +.LP + +.SH COPYRIGHT NOTICE +The MNG-1.00 specification is copyright (c) 1998-2001 Glenn Randers-Pehrson. +See the specification for conditions of use and distribution. +.LP +.\" end of man page + diff --git a/Engine/lib/lmng/doc/man/libmng.3 b/Engine/lib/lmng/doc/man/libmng.3 new file mode 100644 index 000000000..5321faaa1 --- /dev/null +++ b/Engine/lib/lmng/doc/man/libmng.3 @@ -0,0 +1,1146 @@ +.TH LIBMNG 3 "January 30th, 2005" +.SH NAME +libmng \- Multiple-image Network Graphics (MNG) Reference Library 1.0.9 +.SH SYNOPSIS +\fI\fB + +\fB#include \fP + + +.SH DESCRIPTION +The +.I libmng +library supports decoding, displaying, encoding, and various other +manipulations of the Multiple-image Network Graphics (MNG) format +image files. It uses the +.IR zlib(3) +compression library, and optionally the JPEG library by the Independant +JPEG Group (IJG) and/or lcms (little cms), a color-management library +by Marti Maria Saguer. + + +.SH I. Introduction + +This file describes how to use and modify the MNG reference library +(known as libmng) for your own use. There are seven sections to this +file: introduction, callbacks, housekeeping, reading, displaying, +writing, and modification and configuration notes for various special +platforms. We assume that libmng is already installed; see the +INSTALL.README file for instructions on how to install libmng. + +Libmng was written to support and promote the MNG specification. + +The MNG-1.0 specification is available at +. + +Other information about MNG can be found at the MNG home page, +. +The latest version of libmng can be found at its own homepage at +. + +In most cases the library will not need to be changed. +For standardization purposes the library contains both a Windows DLL +and a makefile for building a shared library (SO). The library is +written in C, but an interface for Borland Delphi is also available. + +Libmng has been designed to handle multiple sessions at one time, +to be easily modifiable, to be portable to the vast majority of +machines (ANSI, K&R, 32-, and 64-bit) available, and to be easy +to use. + +Libmng uses zlib for its compression and decompression of MNG files. +Further information about zlib, and the latest version of zlib, can be +found at the zlib home page, . +The zlib compression utility is a general purpose utility that is +useful for more than MNG/PNG files, and can be used without libmng. +See the documentation delivered with zlib for more details. + +Libmng optionally uses the JPEG library by the Independant JPEG Group +(IJG). This library is used for the JNG sub-format, which is part of +the MNG specification, and allows for inclusion of JPEG decoded and +thus highly compressed (photographic) images. +Further information about the IJG JPEG library and the latest sources +can be found at . + +Libmng can also optionally use the lcms (little CMS) library by +Marti Maria Saguer. This library provides an excellent color-management +system (CMS), which gives libmng the ability to provide full +color-correction for images with the proper color-information encoded. +Further information and the latest sources can be found at +. + +Libmng is thread safe, provided the threads are using different +handles as returned by the initialization call. +Each thread should have its own handle and thus its own image. +Libmng does not protect itself against two threads using the +same instance of a handle. + +The libmng.h header file is the single reference needed for programming +with libmng: + +#include + + +.SH II. Callbacks + +Libmng makes extensive use of callback functions. This is meant to +keep the library as platform-independant and flexible as possible. +Actually, the first call you will make to the library, already contains +three parameters you can use to provide callback entry-points. + +Most functions must return a mng_bool (boolean). Returning MNG_FALSE +indicates the library the callback failed in some way and the library +will immediately return from whatever it was doing back to the +application. Returning MNG_TRUE indicates there were no problems and +processing can continue. + +Let's step through each of the possible callbacks. The sections on +reading, displaying and writing will also explain which callbacks are +needed when and where. + +\- mng_ptr mng_memalloc (mng_size_t iLen) + +A very basic function which the library uses to allocate a memory-block +with the given size. A typical implementation would be: + + mng_ptr my_alloc (mng_size_t iLen) { + return calloc (1, iLen); + } + +Note that the library requires you to zero-out the memory-block!!! + +\- void mng_memfree (mng_ptr pPtr, + mng_size_t iLen) + +Counterpart of the previous function. Typically: + + void my_free (mng_ptr pPtr, mng_size_t iLen) { + free (pPtr); + } + +\- mng_bool mng_openstream (mng_handle hHandle) + +\- mng_bool mng_closestream (mng_handle hHandle) + +These are called by the library just before it starts to process +(either read or write) a file and just after the processing stops. +This is the recommended place to do I/O initialization & finalization. +Whether you do or not, is up to you. The library does not put any +meaning into the calls. They are simply provided for your convenience. + +\- mng_bool mng_readdata (mng_handle hHandle, + mng_ptr pBuf, + mng_uint32 iBuflen, + mng_uint32p pRead) + +This function is called when the library needs some more input while +reading an image. The reading process supports two modes: +Suspension-mode (SMOD) and non-suspension-mode (NSMOD). +See mng_set_suspensionmode() for a more detailed description. + +In NSMOD, the library requires you to return exactly the amount of bytes +requested (= iBuflen). Any lesser amount indicates the input file +is exhausted and the library will return a MNG_UNEXPECTEDEOF errorcode. + +In SMOD, you may return a smaller amount of bytes than requested. +This tells the library it should temporarily wait for more input to +arrive. The lib will return with MNG_NEEDMOREDATA, and will expect a +call to mng_read_resume() or mng_display_resume() next, as soon as +more input-data has arrived. + +For NSMOD this function could be as simple as: + + mng_bool my_read (mng_handle hHandle, + mng_ptr pBuf, + mng_uint32 iBuflen, + mng_uint32p pRead) { + *pRead = fread (pBuf, 1, iBuflen, myfile); + return MNG_TRUE; + } + +\- mng_bool mng_writedata (mng_handle hHandle, + mng_ptr pBuf, + mng_uint32 iBuflen, + mng_uint32p pWritten) + +This function is called during the mng_write() function to actually +output data to the file. There is no suspension-mode during write, +so the application must return the exact number of bytes the library +requests to be written. + +A typical implementation could be: + + mng_bool my_write (mng_handle hHandle, + mng_ptr pBuf, + mng_uint32 iBuflen, + mng_uint32p pWritten) { + *pWritten = fwrite (pBuf, 1, iBuflen, myfile); + return MNG_TRUE; + } + +\- mng_bool mng_errorproc (mng_handle hHandle, + mng_int32 iErrorcode, + mng_int8 iSeverity, + mng_chunkid iChunkname, + mng_uint32 iChunkseq, + mng_int32 iExtra1, + mng_int32 iExtra2, + mng_pchar zErrortext) + +This function is called whenever an error is detected inside the +library. This may be caused by invalid input, callbacks indicating +failure, or wrongfully calling functions out of place. + +If you do not provide this callback the library will still return +an errorcode from the called function, and the mng_getlasterror() +function can be used to retrieve the other parameters. + +This function is currently only provided for convenience, but may +at some point be used to indicate certain errors may be acceptable, +and processing should continue. + +\- mng_bool mng_traceproc (mng_handle hHandle, + mng_int32 iFuncnr, + mng_int32 iFuncseq, + mng_pchar zFuncname) + +This function is provided to allow a functional analysis of the +library. This may be useful if you encounter certain errors and +cannot determine what the problem is. + +Almost all functions inside the library will activate this +callback with an appropriate function-name at the start and end +of the function. Please note that large images may generate an +enormous amount of calls. + +\- mng_bool mng_processheader (mng_handle hHandle, + mng_uint32 iWidth, + mng_uint32 iHeight) + +This function is called once the header information of an input- +image has been processed. At this point the image dimensions are +available and also some other properties depending on the type +of the image. Eg. for a MNG the frame-/layercount, playtime & +simplicity fields are known. + +The primary purpose of this callback is to inform the application +of the size of the image, and for the application to initialize +the drawing canvas to be used by the library. This is also a good +point to set the canvas-style. Eg. mng_set_canvasstyle(). + +\- mng_bool mng_processtext (mng_handle hHandle, + mng_uint8 iType, + mng_pchar zKeyword, + mng_pchar zText, + mng_pchar zLanguage, + mng_pchar zTranslation) + +This callback is activated for each textual chunk in the input- +image. These are tEXt, zTXt & iTXt. It may be used to retain +specific comments for presentation to the user. + +\- mng_bool mng_processsave (mng_handle hHandle) + +\- mng_bool mng_processseek (mng_handle hHandle, + mng_pchar zName) + +The purpose of these callbacks is to signal the processing of the +SAVE & SEEK chunks in a MNG input-file. This may be used in the +future to specify some special processing. At the moment these +functions are only provided as a signal. + +\- mng_ptr mng_getcanvasline (mng_handle hHandle, + mng_uint32 iLinenr) + +\- mng_ptr mng_getbkgdline (mng_handle hHandle, + mng_uint32 iLinenr) + +\- mng_ptr mng_getalphaline (mng_handle hHandle, + mng_uint32 iLinenr) + +These callbacks are used to access the drawing canvas, background +canvas and an optional separate alpha-channel canvas. The latter is +used only with the MNG_CANVAS_RGB8_A8 canvas-style. + +If the getbkgdline() callback is not supplied the library will +composite fully or partially transparent pixels in the image against +a specified background color. See mng_set_bgcolor() for more details. +If a chosen canvas-style includes an alpha-channel, this callback +is very likely not needed. + +The application is responsible for returning a pointer to a line of +pixels, which should be in the exact format as defined by the call +to mng_set_canvasstyle() and mng_set_bkgdstyle(), without gaps between +the representation of each pixel, unless specified by the canvas-style. + +\- mng_bool mng_refresh (mng_handle hHandle, + mng_uint32 iX, + mng_uint32 iY, + mng_uint32 iWidth, + mng_uint32 iHeight) + +This callback is called when the library has drawn a complete frame +onto the drawing canvas, and it is ready to be displayed. +The application is responsible for transferring the drawing canvas +from memory onto the actual output device. + +\- mng_uint32 mng_gettickcount (mng_handle hHandle) + +This function should return the number of milliseconds on some internal +clock. The entire animation timing depends heavily on this function, +and the number returned should be as accurate as possible. + +\- mng_bool mng_settimer (mng_handle hHandle, + mng_uint32 iMsecs) + +This callback is activated every time the library requires a "pause". +Note that the function itself should NOT execute the wait. It should +simply store the time-field and allow the library to return. Libmng +will return with the MNG_NEEDTIMERWAIT code, indicating the callback +was called and it is now time to execute the pause. + +After the indicated number of milliseconds have elapsed, the application +should call mng_display_resume(), to resume the animation as planned. + +This method allows for both a real timer or a simple wait command in the +application. Whichever method you select, both the gettickcount() and +settimer() callbacks are crucial for proper animation timing. + +\- mng_bool mng_processgamma (mng_handle hHandle, + mng_uint32 iGamma) + +\- mng_bool mng_processchroma (mng_handle hHandle, + mng_uint32 iWhitepointx, + mng_uint32 iWhitepointy, + mng_uint32 iRedx, + mng_uint32 iRedy, + mng_uint32 iGreenx, + mng_uint32 iGreeny, + mng_uint32 iBluex, + mng_uint32 iBluey) + +\- mng_bool mng_processsrgb (mng_handle hHandle, + mng_uint8 iRenderingintent) + +\- mng_bool mng_processiccp (mng_handle hHandle, + mng_uint32 iProfilesize, + mng_ptr pProfile) + +\- mng_bool mng_processarow (mng_handle hHandle, + mng_uint32 iRowsamples, + mng_bool bIsRGBA16, + mng_ptr pRow) + +These callbacks are only required when you selected the MNG_APP_CMS +directive during compilation of the library. See the configuration +section for more details. + +\- mng_bool mng_iteratechunk (mng_handle hHandle, + mng_handle hChunk, + mng_chunkid iChunkid, + mng_uint32 iChunkseq) + +This callback is only used for the mng_iterate_chunks() function. +It is called exactly once for each chunk stored. + + +.SH III. Housekeeping + + +.SS Memory management + +The library can use internal memory allocation/deallocation or use +provided callbacks for its memory management. The choice is made at +compilation time. See the section on customization for details. + +If internal management has been selected, the memory callback functions +need not be supplied. Even if you do supply them they will not be used. +The actual code used is similar to the code discussed in the callback +section: + + pPtr = calloc (1, iLen); + + free (pPtr); + +If your compiler does not support these functions, or you wish to monitor +the library's use of memory for certain reasons, you can choose to +compile the library with external memory management. In this case the +memory callback functions MUST be supplied, and should function as if the +above code was used. + + +.SS Initialization + +The basic initialization of the library is short and swift: + + myhandle = mng_initialize (myuserdata, my_alloc, + my_free, MNG_NULL); + if (myhandle == MNG_NULL) + /* process error */; + +The first field is an application-only parameter. It is saved in +libmng's internal structures and available at all times through the +mng_get_userdata() function. This is especially handy in callback functions +if your program may be handling multiple files at the same time. + +The second and third field supply the library with the memory callback +function entry-points. These are described in more detail in the callback +section and the previous paragraph. + +The fourth and last field may be used to supply the library with the +entry-point of a trace callback function. For regular use you will not +need this! + +The function returns a handle which will be your ticket to MNG-heaven. +All other functions rely on this handle. It is the single fixed unique +reference-point between your application and the library. + +You should call the initialization function for each image you wish to +process simultaneously. If you are processing images consecutively, you can +reset the internal status of the library with the mng_reset() function. +This function will clear all internal state variables, free any stored +chunks and/or objects, etc, etc. Your callbacks and other external parameters +will be retained. + +After you successfully received the handle it is time to set the required +callbacks. The sections on reading, displaying & writing indicate which +callbacks are required and which are optional. +To set the callbacks simply do: + + myretcode = mng_setcb_xxxxxx (myhandle, my_xxxxxx); + if (myretcode != MNG_NOERROR) + /* process error */; + +Naturally you'd replace the x's with the name of the callback. + + +.SS Cleanup + +Once you've gotten hold of that precious mng_handle, you should always, +and I mean always, call the cleanup function when you're done. +Just do: + + mng_cleanup (myhandle); + +And you're done. There shouldn't be an ounce of memory spilled after +that call. + +Note that if you would like to process multiple files consecutively +you do not need to do mng_cleanup() / mng_initialize() between each file +but simply + + myretcode = mng_reset (myhandle); + if (myretcode != MNG_NOERROR) + /* process error */; + +will suffice. Saves some time and effort, that. + + +.SS Error handling + +From the examples in the previous paragraphs you may have noticed a +meticulous scheme for error handling. And yes, that's exactly what it is. +Practically each call simply returns an errorcode, indicating success, +eg. MNG_NOERROR or failure, anything else but MNG_NEEDMOREDATA and +MNG_NEEDTIMERWAIT. These latter two will be discussed in more detail in +their respective fields of interest: the reading section and displaying +section respectively. + +It is the application's responsibility to check the returncode after +each call. You can call mng_getlasterror() to receive the details of +the last detected error. This even includes a discriptive error-message +if you enabled that option during compilation of the library. + +Note that after receiving an error it is still possible to call the +library, but it's also very likely that any following call will fail. +The only functions deemed to work will be mng_reset() and mng_cleanup(). +Yes, if you abort your program after an error, you should still call +mng_cleanup(). + + +.SH IV. Reading + +Reading a MNG, JNG or PNG is fairly easy. It depends slightly on your +ultimate goal how certain specifics are to be handled, but the basics +are similar in all cases. + +For the read functioins to work you must have compiled the library with +the MNG_READ_SUPPRT directive. The standard DLL and Shared Library +have this on by default! + + +.SS Setup + +Naturally you must have initialized the library and be the owner of +a mng_handle. The following callbacks are essential: + + mng_openstream, mng_readdata, mng_closestream + +You may optionally define: + + mng_errorproc, mng_traceproc + mng_processheader, mng_processtext + mng_processsave, mng_processseek + +The reading bit will also fail if you are already creating or +displaying a file. Seems a bit obvious, but I thought I'd mention it, +just in case. + + +.SS To suspend or not to suspend + +There is one choice you need to make before calling the read function. +Are you in need of suspension-mode or not? + +If you're reading from a disk you most certainly do not need +suspension-mode. Even the oldest and slowest of disks will be fast +enough for straight reading. + +However, if your input comes from a really slow device, such as a +dialup-line or the likes, you may opt for suspension-mode. This is done +by calling + + myretcode = mng_set_suspensionmode (myhandle, + MNG_TRUE); + if (myretcode != MNG_NOERROR) + /* process error */; + +Suspension-mode will force the library to use special buffering on the +input. This allows your application to receive data of arbitrarily length +and return this in the mng_readdata() callback, without disturbing the +chunk processing routines of the library. + +Suspension-mode does require a little extra care in the main logic of the +application. The read function may return with MNG_NEEDMOREDATA when the +mng_readdata() callback returns less data then it needs to process the +next chunk. This indicates the application to wait for more data to arrive +and then resume processing by calling mng_read_resume(). + + +.SS The read HLAPI + +The actual reading is just plain simple. Since all I/O is done +outside the library through the callbacks, the library can focus on +its real task. Understanding, checking and labelling the input data! + +All you really need to do is this: + + myretcode = mng_read (myhandle); + if (myretcode != MNG_NOERROR) + /* process error */; + +Of course, if you're on suspension-mode the code is a little more +complicated: + + myretcode = mng_read (myhandle); + + while (myretcode == MNG_NEEDMOREDATA) { + /* wait for input-data to arrive */ + myretcode = mng_read_resume (myhandle); + } + + if (myretcode != MNG_NOERROR) + /* process error */; + +This is rather crude and more sophisticated programming methods may +dictate another approach. Whatever method you decide on, it should +act as if the above code was in its place. + +There is also the mng_readdisplay() function, but this is discussed +in the displaying section. It functions pretty much as the mng_read() +function, but also immediately starts displaying the image. +mng_read_resume() should be replaced by mng_display_resume() in that +case! + + +.SS What happens inside + +What actually happens inside the library depends on the configuration +options set during the compilation of the library. + +Basically the library will first read the 8-byte file header, to determine +its validity and the type of image it is about to process. Then it will +repeatedly read a 4-byte chunk-length and then the remainder of the chunk +until it either reaches EOF (indicated by the mng_readdata() callback) or +implicitly decides EOF as it processed the logically last chunk of the +image. + +Applications that require strict conformity and do not allow superfluous +data after the ending chunk, will need to perform this check in their +mng_closestream() callback. + +Each chunk is then checked on CRC, after which it is handed over to the +appropriate chunk processing routine. These routines will disect the +chunk, check the validity of its contents, check its position with respect +to other chunks, etc, etc. + +If everything checks out, the chunk is further processed as follows: + +If display support has been selected during compilation, certain pre-display +initialization will take place. + +If chunk-storage support has been selected during compilation, the chunks +data may be stored in a special internal structure and held for future +reference. + + +.SS Storing and accessing chunks + +One of the compilation options activates support for chunk storage. +This option may be useful if you want to examine an image. The directive +is MNG_STORE_CHUNKS. You must also turn on the MNG_ACCESS_CHUNKS +directive. + +The actual storage facility can be turned on or off with the +mng_set_storechunks() function. If set to MNG_TRUE, chunks will be +stored as they are read. + +At any point you can then call the mng_iterate_chunks() function +to iterate through the current list of chunks. This function requires +a callback which is called for each chunk and receives a specific +chunk-handle. This chunk-handle can be used to call the appropriate +mng_getchunk_xxxx() function, to access the chunks properties. + +A typical implementation may look like this: + + mng_bool my_iteratechunk (mng_handle hHandle, + mng_handle hChunk, + mng_chunkid iChunkid, + mng_uint32 iChunkseq) { + switch (iChunkid) { + case MNG_UINT_MHDR : { /* process MHDR */; + break; } + case MNG_UINT_FRAM : { /* process FRAM */; + break; } + + ...etc... + + case MNG_UINT_HUH : { /* unknown chunk */; + break; } + default : { /* duh; forgot one */; } + } + + return MNG_TRUE; /* keep'm coming */ + } + +To get to the actual chunk fields of lets say a SHOW chunk you would do: + + mng_bool isempty; + mng_uint16 firstid, lastid; + mng_uint8 showmode; + + myretcode mng_getchunk_show (hHandle, hChunk, + isempty, firstid, + lastid, showmode); + if (myretcode != MNG_NOERROR) + /* process error */; + + +.SH V. Displaying + + +.SS Setup + +Assuming you have initialized the library and are the owner of +a mng_handle. The following callbacks are essential: + + mng_getcanvasline, mng_refresh + mng_gettickcount, mng_settimer + +If you wish to use an application supplied background you must supply: + + mng_getbkgdline + +If you wish to use the MNG_CANVAS_RGB8_A8 canvas style you must supply: + + mng_getalphaline + +You may optionally define: + + mng_errorproc, mng_traceproc + mng_processheader, mng_processtext + mng_processsave, mng_processseek + +Note that the mng_processheader() callback is optional but will +be quite significant for proper operation! + +Displaying an image will fail if you are creating a file or already +displaying one. Yes, you can't display it twice! + + +.SS A word on canvas styles + +The canvas style describes how your drawing canvas is made up. +You must set this before the library actually starts drawing, so +the mng_processheader() callback is a pretty good place for it. + +Currently only 8-bit RGB canvas styles are supported, either with +or without an alpha channel. + +If you like to do alpha composition yourself you can select one of +the canvas styles that include an alpha channel. You can even have +a separate alpha canvas by selecting the MNG_CANVAS_RGB8_A8 style. + +All styles require a compact model. Eg. MNG_CANVAS_BGR8 requires +your canvas lines in bgrbgrbgr... storage, where each letter +represents an 8-bit value of the corresponding color, and each +threesome makes up the values of one(1) pixel. + +The library processes a line at a time, so the canvas lines do not +actually need to be consecutive in memory. + + +.SS Alpha composition and application backgrounds + +All Network Graphics can be partially transparent. This requires +special processing if you need to display an image against some +background. Note that the MNG header (MHDR chunk) contains a +simplicity field indicating whether transparency information in +the file is critical or not. This only applies to embedded images, +which means the full image-frame of the MNG may still contain fully +transparent pixels! + +Depending on your needs you can supply a single background color, +a background canvas or tell the library to return the alpha-channel +and do alpha composition yourself. + +This is different from the BACK chunk in a MNG, or the bKGD chunk +in an (embedded) PNG or JNG. The BACK chunk indicates an optional or +mandatory background color and/or image. The bKGD chunk only indicates +an optional background color. These chunks indicate the Authors +preferences. They may be absent in which case you need to supply +some sort of background yourself. + +.SS Composing against a background color + +This is the easiest method. Call the mng_set_bgcolor() function to +set the values of the red, green and blue component of your preferred +background color. + +Use one of the canvas styles that do not have an alpha-channel, and +which matches your output requirements. + +.SS Composing against a background canvas + +This is somewhat more complicated. You will need to set the +mng_getbkgdline() callback. This will be called whenever the library +needs to compose a partially transparent line. + +This canvas must hold the background against which the image should +be composed. Its size must match exactly with the image dimensions +and thus the drawing canvas! + +Use one of the canvas styles that do not have an alpha-channel, and +which matches your output requirements. The canvas style of the +background canvas may even differ from the drawing canvas. The library's +composing will still function properly. + +.SS Composing within the application + +If you have the option in your application to draw a (partially) +transparent canvas to the output device, this option is preferred. + +Select one of the canvas styles that do have an alpha-channel. +The library will now supply the appropriate alpha information, +allowing the application to compose the image as it sees fit. + + +.SS Color information and CMS + +Network Graphics may, and usually will, contain color-correction +information. This information is intended to compensate for the +difference in recording and display devices used. + +This document does not address the specifics of color-management. +See the PNG specification for a more detailed description. + +.SS Using little cms by Marti Maria Saguer + +This is the easiest method, providing you can compile the lcms package. +Select the MNG_FULL_CMS directive during compilation, and sit back and +relax. The library will take care of all color-correction for you. + +.SS Using an OS- or application-supplied CMS + +If you are so lucky to have access to CMS functionality from within +your application, you may instruct the library to leave color-correction +to you. + +Select the MNG_APP_CMS directive during compilation of the library. +You MUST also set the following callbacks: + + mng_processgamma, mng_processchroma, + mng_processsrgb, mng_processiccp and + mng_processarow + +The last callback is called when the library needs you to correct +an arbitrary line of pixels. The other callbacks are called when +the corresponding color-information is encountered in the file. +You must store this information somewhere for use in the +mng_processarow() callback. + +.SS Using gamma-only correction + +This isn't a preferred method, but it's better than no correction +at all. Gamma-only correction will at least compensate for +gamma-differences between the original recorder and your output device. + +Select the MNG_GAMMA_ONLY directive during compilation +of the library. Your compiler MUST support fp operations. + +.SS No color correction + +Ouch. This is really bad. This is the least preferred method, +but may be necessary if your system cannot use lcms, doesn't +have its own CMS, and does not allow fp operations, ruling out +the gamma-only option. + +Select the MNG_NO_CMS directive during compilation. +Images will definitely not be displayed as seen by the Author!!! + + +.SS Animations and timing + +Animations require some form of timing support. The library relies +on two callbacks for this purpose. The mng_gettickcount() and +mng_settimer() callbacks. mng_gettickcount() is used to determine +the passing of time in milliseconds since the beginning of the +animation. This is also used to compensate during suspension-mode +if you are using the mng_readdisplay() function to read & display +the file simultaneously. + +The callback may return an arbitrary number of milliseconds, but +this number must increase proportionaly between calls. Most modern +systems will have some tickcount() function which derives its +input from an internal clock. The value returned from this function +is more than adequate for libmng. + +The mng_settimer() callback is called when the library determines +a little "pause" is required before rendering another frame of the +animation. The pause interval is also expressed in milliseconds. +Your application should store this value and return immediately. +The library will then make appropriate arrangements to store its +internal state and returns to your application with the +MNG_NEEDTIMERWAIT code. + +At that point you should suspend processing and wait the given +interval. Please use your OS features for this. Do not engage some +sort of loop. That is real bad programming practice. Most modern +systems will have some timing functions. A simple wait() function +may suffice, but this may prevent your applications main-task from +running, and possibly prevent the actual update of your output device. + + +.SS The mng_refresh() callback + +The mng_refresh() callback is called whenever the library has +"finished" drawing a new frame onto your canvas, and just before it +will call the mng_settimer() callback. + +This allows you to perform some actions necessary to "refresh" the +canvas onto your output device. Please do NOT suspend processing +inside this callback. This must be handled after the mng_settimer() +callback! + + +.SS Displaying while reading + +This method is preferred if you are reading from a slow input device +(such as a dialup-line) and you wish to start displaying something +as quickly as possible. This functionality is provided mainly for +browser-type applications but may be appropriate for other +applications as well. + +The method is usually used in unison with the suspension-mode of +the read module. A typical implementation would look like this: + + /* initiale library and set required callbacks */ + + /* activate suspension-mode */ + myretcode = mng_set_suspensionmode (myhandle, + MNG_TRUE); + if (myretcode != MNG_NOERROR) + /* process error */; + + myretcode = mng_readdisplay (myhandle); + + while ((myretcode == MNG_NEEDMOREDATA) || + (myretcode == MNG_NEEDTIMERWAIT)) { + if (myretcode == MNG_NEEDMOREDATA) + /* wait for more input-data */; + else + /* wait for timer interval */; + + myretcode = mng_display_resume (myhandle); + } + + if (myretcode != MNG_NOERROR) + /* process error */; + +More advanced programming methods may require a different approach, +but the final result should function as in the code above. + + +.SS Displaying after reading + +This method is used to display a file that was previously read. +It is primarily meant for viewers with direct file access, such as +1a local harddisk. + +Once you have successfully read the file, all you need to do is: + + myretcode = mng_display (myhandle); + + while (myretcode == MNG_NEEDTIMERWAIT) { + /* wait for timer interval */; + myretcode = mng_display_resume (myhandle); + } + + if (myretcode != MNG_NOERROR) + /* process error */; + +Again, more advanced programming methods may require a different +approach, but the final result should function as in the code above. + + +.SS Display manipulation + +Several HLAPI functions are provided to allow a user to manipulate +the normal flow of an animation. + +\- mng_display_freeze (mng_handle hHandle) + +This will "freeze" the animation in place. + +\- mng_display_resume (mng_handle hHandle) + +This function can be used to resume a frozen animation, or to force +the library to advance the animation to the next frame. + +\- mng_display_reset (mng_handle hHandle) + +This function will "reset" the animation into its pristine state. +Calling mng_display() afterwards will re-display the animation +from the first frame. + +\- mng_display_golayer (mng_handle hHandle, + mng_uint32 iLayer) + +\- mng_display_goframe (mng_handle hHandle, + mng_uint32 iFrame) + +\- mng_display_gotime (mng_handle hHandle, + mng_uint32 iPlaytime) + +These three functions can be used to "jump" to a specific layer, frame +or timeslot in the animation. You must "freeze" the animation before +using any of these functions. + +All above functions may only be called during a timer interval! +It is the applications responsibility to cleanup any resources with +respect to the timer wait. + + +.SH VI. Writing + +The main focus of the library lies in its displaying capabilites. +But it does offer writing support as well. +You can create and write a file, or you can write a file you +have previously read, providing the storage of chunks was enabled +and active. + +For this to work you must have compiled the library with the +MNG_WRITE_SUPPO1RT and MNG_ACCESS_CHUNKS directives. The standard DLL and +Shared Library have this on by default! + + +.SS Setup + +As always you must have initialized the library and be the owner of +a mng_handle. The following callbacks are essential: + + mng_openstream, mng_writedata, mng_closestream + +You can optionally define: + + mng_errorproc, mng_traceproc + +The creation and writing functions will fail if you are in the middle +of reading, creating or writing a file. + + +.SS Creating a new file + +To start a new file the library must be in its initial state. +First you need to tell the library your intentions: + + myretcode = mng_create (myhandle); + if (myretcode != MNG_NOERROR) + /* process error */; + +After that you start adding the appropriate chunks: + + myretcode = mng_put1chunk_mhdr (myhandle, ...); + if (myretcode != MNG_NOERROR) + /* process error */; + +And so on, and so forth. Note that the library will automatically signal +the logical end of the file by the ending chunk. Also the first chunk +will indicate the library the filetype (eg. PNG, JNG or MNG) and force +the proper signature when writing the file. + +The code above can be simplified, as you can always get the last errorcode +by using the mng_getlasterror() function: + + if ( (mng_putchunk_xxxx (myhandle, ...)) or + (mng_putchunk_xxxx (myhandle, ...)) or + ...etc... ) + /* process error */; + +Please note that you must have a pretty good understanding of the chunk +specification. Unlike the read functions, there are virtually no checks, +so it is quite possible to write completely wrong files. +It is a good practice to read back your file into the library to verify +its integrity. + +Once you've got all the chunks added, all you do is: + + myretcode mng_write (myhandle); + if (myretcode != MNG_NOERROR) + /* process error */; + +And presto. You're done. The real work is of course carried out in +your callbacks. Note that this is a single operation as opposed to +the read & display functions that may return with MNG_NEEDMOREDATA +and/or MNG_NEEDTIMERWAIT. The write function just does the job, and +only returns after it's finished or if it encounters some +unrecoverable error. + + +.SS Writing a previously read file + +If you have already successfully read a file, you can use the library to +write it out as a copy or something. You MUST have compiled the library +with the MNG_STORE_CHUNKS directive, and you must have done +mng_set_storechunks (myhandle, MNG_TRUE). + +This doesn't require the MNG_ACCESS_CHUNKS directive, unless you want +to fiddle with the chunks as well. + +Again all you need to do is: + + myretcode mng_write (myhandle); + if (myretcode != MNG_NOERROR) + /* process error */; + + +.SH VII. Modifying/Customizing libmng: + +not finished yet + +.SS Compilation directives + +not finished yet + +.SS Platform dependant modification + +not finished yet + +.SH "SEE ALSO" +.IR mng(5), jng(5), png(5), libpng(3) + +.LP +libmng : +.IP +.br +http://www.libmng.com + +.LP +zlib : +.IP +.br +http://www.info-zip.org/pub/infozip/zlib/ + +.LP +IJG JPEG library : +.IP +.br +http://www.ijg.org + +.LP +lcms (little CMS) by Marti Maria Saguer : +.IP +.br +http://www.littlecms.com/ + +.LP +MNG specification: +.IP +.br +http://www.libpng.org/pub/mng + +.LP +In the case of any inconsistency between the MNG specification +and this library, the specification takes precedence. + + +.SH AUTHORS +This man page: Gerard Juyn + + +The contributing authors would like to thank all those who helped +with testing, bug fixes, and patience. This wouldn't have been +possible without all of you!!! + + +.SH COPYRIGHT NOTICE: + +Copyright (c) 2000-2002 Gerard Juyn + +For the purposes of this copyright and license, "Contributing Authors" +is defined as the following set of individuals: + + Gerard Juyn + +The MNG Library is supplied "AS IS". The Contributing Authors +disclaim all warranties, expressed or implied, including, without +limitation, the warranties of merchantability and of fitness for any +purpose. The Contributing Authors assume no liability for direct, +indirect, incidental, special, exemplary, or consequential damages, +which may result from the use of the MNG Library, even if advised of +the possibility of such damage. + +Permission is hereby granted to use, copy, modify, and distribute this +source code, or portions hereof, for any purpose, without fee, subject +to the following restrictions: + +1. The origin of this source code must not be misrepresented; +you must not claim that you wrote the original software. + +2. Altered versions must be plainly marked as such and must not be +misrepresented as being the original source. + +3. This Copyright notice may not be removed or altered from any source +or altered source distribution. + +The Contributing Authors specifically permit, without fee, and +encourage the use of this source code as a component to supporting +the MNG and JNG file format in commercial products. If you use this +source code in a product, acknowledgment would be highly appreciated. + +.SH Remarks + +Parts of this software have been adapted from the libpng library. +Although this library supports all features from the PNG specification +(as MNG descends from it) it does not require the libpng library. +It does require the zlib library and optionally the IJG JPEG library, +and/or the "little-cms" library by Marti Maria Saguer (depending on the +inclusion of support for JNG and Full-Color-Management respectively. + +This library's function is primarily to read and display MNG +animations. It is not meant as a full-featured image-editing +component! It does however offer creation and editing functionality +at the chunk level. (future modifications may include some more +support for creation and or editing) + +.\" end of man page diff --git a/Engine/lib/lmng/doc/man/mng.5 b/Engine/lib/lmng/doc/man/mng.5 new file mode 100644 index 000000000..e40c24910 --- /dev/null +++ b/Engine/lib/lmng/doc/man/mng.5 @@ -0,0 +1,42 @@ +.TH MNG 5 "July 25, 2000" +.SH NAME +mng \- Multiple-image Network Graphics (MNG) format +.SH DESCRIPTION +MNG (Multiple-image Network Graphics) is the animation extension of the +popular PNG image-format. PNG (Portable Network Graphics) is an +extensible file format for the lossless, portable, well-compressed +storage of raster images. +.br + +MNG has advanced animation features which make it very useful as a full +replacement for GIF animations. These features allow animations that +are impossible with GIF or result in much smaller files as GIF. + +As MNG builds on the same structure as PNG, it is robust, extensible and +free of patents. It retains the same clever file integrity checks as in PNG. + +MNG also embraces the lossy JPEG image-format in a sub-format named JNG, +which allows for alpha-transparency and color-correction on highly +compressed (photographic) images. + +.SH "SEE ALSO" +.IR png(5) ", " jng(5) ", " libmng(3) ", " libpng(3) ", " zlib(3) ", " +deflate(5) ", " zlib(5) ", " jpeg(5) +.LP +MNG 1.00, Februari 9, 2001: +.IP +.br +http://www.libpng.org/pub/mng +.SH AUTHORS +This man page: Gerard Juyn +.LP +Multiple-image Network Graphics (MNG) Specification Version 1.00 (Februari 9, 2001): +Glenn Randers-Pehrson and others (png-list@ccrc.wustl.edu). +.LP + +.SH COPYRIGHT NOTICE +The MNG-1.00 specification is copyright (c) 1998-2001 Glenn Randers-Pehrson. +See the specification for conditions of use and distribution. +.LP +.\" end of man page + diff --git a/Engine/lib/lmng/doc/misc/magic.dif b/Engine/lib/lmng/doc/misc/magic.dif new file mode 100644 index 000000000..005ad5a74 --- /dev/null +++ b/Engine/lib/lmng/doc/misc/magic.dif @@ -0,0 +1,30 @@ +--- magic.orig Wed Aug 14 16:48:56 2002 ++++ magic Wed Aug 14 16:50:09 2002 +@@ -2544,6 +2544,27 @@ + >>28 byte 1 interlaced + 1 string PNG PNG image data, CORRUPTED + ++#MNG ++# 0x8a M N G 0x0d 0x0a 0x1a 0x0a [4-byte pad] ++# M H D R [4-byte width][4-byte height][4-byte ticks][4-byte layers] ++# [4-byte frame][4-byte time] ++# ++0 string \x8aMNG MNG image data, ++>4 belong !0x0d0a1a0a CORRUPTED ++>4 belong 0x0d0a1a0a ++>>16 belong x %ld x ++>>20 belong x %ld ++ ++#JNG ++# 0x8b J N G 0x0d 0x0a 0x1a 0x0a [4-byte pad] ++# J H D R [4-byte width][4-byte height] ++# ++0 string \x8bJNG JNG image data, ++>4 belong !0x0d0a1a0a CORRUPTED ++>4 belong 0x0d0a1a0a ++>>16 belong x %ld x ++>>20 belong x %ld ++ + # GIF + 0 string GIF8 GIF image data + >4 string 7a \b, version 8%s, diff --git a/Engine/lib/lmng/doc/rpm/libmng-1.0.10-rhconf.patch b/Engine/lib/lmng/doc/rpm/libmng-1.0.10-rhconf.patch new file mode 100644 index 000000000..a73b79dbe --- /dev/null +++ b/Engine/lib/lmng/doc/rpm/libmng-1.0.10-rhconf.patch @@ -0,0 +1,38 @@ +--- libmng/makefiles/makefile.linux.orig Sat Jul 1 15:10:35 2000 ++++ libmng/makefiles/makefile.linux Sat Jul 1 15:14:52 2000 +@@ -13,19 +13,19 @@ + OPTIONS = -DMNG_BUILD_SO + + # where "make install" puts libmng.a,libmng.so*,libmng.h,libmng_conf.h,libmng_types.h +-prefix=/usr/local ++prefix=/usr + + # Where the zlib library and include files are located +-ZLIBLIB=../zlib +-ZLIBINC=../zlib ++ZLIBLIB=/usr/lib ++ZLIBINC=/usr/include + + # Where the jpeg library and include files are located +-JPEGLIB=../jpgsrc +-JPEGINC=../jpgsrc ++JPEGLIB=/usr/lib ++JPEGINC=/usr/include + + # Where the lcms library and include files are located +-LCMSLIB=../lcms/lib +-LCMSINC=../lcms/source ++LCMSLIB=/usr/lib ++LCMSINC=/usr/include + + ALIGN= + # for i386: +@@ -37,7 +37,7 @@ + + # for pgcc version 2.95.1, -O3 is buggy; don't use it. + +-CFLAGS=-I$(ZLIBINC) -I$(JPEGINC) -I$(LCMSINC) -Wall -O3 -funroll-loops \ ++CFLAGS=-I$(ZLIBINC) -I$(JPEGINC) -I$(LCMSINC) -Wall $(RPM_OPT_FLAGS) \ + $(OPTIONS) $(ALIGN) # $(WARNMORE) -g + LDFLAGS=-L. -Wl,-rpath,. \ + -L$(ZLIBLIB) -Wl,-rpath,$(ZLIBLIB) \ diff --git a/Engine/lib/lmng/doc/rpm/libmng.spec b/Engine/lib/lmng/doc/rpm/libmng.spec new file mode 100644 index 000000000..89908d7a9 --- /dev/null +++ b/Engine/lib/lmng/doc/rpm/libmng.spec @@ -0,0 +1,116 @@ +Summary: A library of functions for manipulating MNG format files. +Name: libmng +Version: 1.0.10 +Release: 2.1 +Copyright: AS IS +Group: System Environment/Libraries +Source0: libmng-%{PACKAGE_VERSION}.tar.gz +Patch: libmng-%{PACKAGE_VERSION}-rhconf.patch +URL: http://www.libmng.com/ +BuildRoot: /var/tmp/libmng-root +BuildPrereq: libjpeg-devel, zlib-devel, lcms-devel + +%description +libmng - library for reading, writing, displaying and examing +Multiple-Image Network Graphics. MNG is the animation extension to the +popular PNG image-format. + +%package devel +Summary: Development tools for programs to manipulate MNG format files. +Group: Development/Libraries +Requires: libmng = %{PACKAGE_VERSION} +%description devel +The libmng-devel package contains the header files and static +libraries necessary for developing programs using the MNG +(Multiple-Image Network Graphics) library. + +If you want to develop programs which will manipulate MNG image format +files, you should install libmng-devel. You'll also need to install +the libmng package. + +%changelog +* Fri Jul 13 2007 Glenn Randers-Pehrson +- updated to 1.0.10 + +* Thu Aug 5 2004 Gerard Juyn +* Sun Jan 30 2005 Gerard Juyn +- updated to 1.0.9 + +* Thu Aug 5 2004 Gerard Juyn +- updated to 1.0.8 + +* Sun Mar 21 2004 Gerard Juyn +- updated to 1.0.7 + +* Sun Oct 19 2003 Gerard Juyn +- updated to 1.0.6 + +* Tue Sep 24 2002 Gerard Juyn +- updated to 1.0.5 + +* Sun Jun 23 2002 Gerard Juyn +- updated to 1.0.4 + +* Mon Sep 18 2001 Gerard Juyn +- updated to 1.0.3 + +* Sat Jul 7 2001 Gerard Juyn +- updated to 1.0.2 + +* Wed May 2 2001 Gerard Juyn +- updated to 1.0.1 + +* Mon Feb 5 2001 Gerard Juyn +- updated to 1.0.0 + +* Fri Jan 19 2001 Gerard Juyn +- updated to 0.9.4 + +* Sat Oct 28 2000 Gerard Juyn +- updated to 0.9.3 + +* Tue Aug 15 2000 MATSUURA Takanori +- based on libmng-0.9.2/doc/rpm/libmng.spec +- use %%configure and %%makeinstall + +* Sat Aug 5 2000 Gerard Juyn +- updated to 0.9.2 + +* Wed Jul 26 2000 Gerard Juyn +- updated to 0.9.1 + +* Sat Jul 1 2000 MATSUURA Takanori +- updated to 0.9.0 + +* Sat Jun 24 2000 MATSUURA Takanori +- 1st release for RPM + +%prep +%setup +%configure + +%build +make + +%install +rm -rf $RPM_BUILD_ROOT +%makeinstall + +%clean +rm -rf $RPM_BUILD_ROOT + +%post -p /sbin/ldconfig + +%postun -p /sbin/ldconfig + +%files +%defattr(-,root,root) +%doc CHANGES LICENSE README doc +/usr/lib/libmng.so.* + +%files devel +%defattr(-,root,root) +/usr/include/* +/usr/lib/libmng.a +/usr/lib/libmng.so + diff --git a/Engine/lib/lmng/libmng.h b/Engine/lib/lmng/libmng.h new file mode 100644 index 000000000..b3b1ab141 --- /dev/null +++ b/Engine/lib/lmng/libmng.h @@ -0,0 +1,2932 @@ +/* ************************************************************************** */ +/* * * */ +/* * COPYRIGHT NOTICE: * */ +/* * * */ +/* * Copyright (c) 2000-2007 Gerard Juyn * */ +/* * [You may insert additional notices after this sentence if you modify * */ +/* * this source] * */ +/* * * */ +/* * For the purposes of this copyright and license, "Contributing Authors" * */ +/* * is defined as the following set of individuals: * */ +/* * * */ +/* * Gerard Juyn - gjuyn :at: users.sourceforge.net * */ +/* * Glenn Randers-Pehrson - glennrp :at: users.sourceforge.net * */ +/* * Raphael Assenat - raph :at: raphnet.net * */ +/* * John Stiles - * */ +/* * * */ +/* * The MNG Library is supplied "AS IS". The Contributing Authors * */ +/* * disclaim all warranties, expressed or implied, including, without * */ +/* * limitation, the warranties of merchantability and of fitness for any * */ +/* * purpose. The Contributing Authors assume no liability for direct, * */ +/* * indirect, incidental, special, exemplary, or consequential damages, * */ +/* * which may result from the use of the MNG Library, even if advised of * */ +/* * the possibility of such damage. * */ +/* * * */ +/* * Permission is hereby granted to use, copy, modify, and distribute this * */ +/* * source code, or portions hereof, for any purpose, without fee, subject * */ +/* * to the following restrictions: * */ +/* * * */ +/* * 1. The origin of this source code must not be misrepresented; * */ +/* * you must not claim that you wrote the original software. * */ +/* * * */ +/* * 2. Altered versions must be plainly marked as such and must not be * */ +/* * misrepresented as being the original source. * */ +/* * * */ +/* * 3. This Copyright notice may not be removed or altered from any source * */ +/* * or altered source distribution. * */ +/* * * */ +/* * The Contributing Authors specifically permit, without fee, and * */ +/* * encourage the use of this source code as a component to supporting * */ +/* * the MNG and JNG file format in commercial products. If you use this * */ +/* * source code in a product, acknowledgment would be highly appreciated. * */ +/* * * */ +/* ************************************************************************** */ +/* * * */ +/* * Parts of this software have been adapted from the libpng package. * */ +/* * Although this library supports all features from the PNG specification * */ +/* * (as MNG descends from it) it does not require the libpng package. * */ +/* * It does require the zlib library and optionally the IJG jpeg library, * */ +/* * and/or the "little-cms" library by Marti Maria (depending on the * */ +/* * inclusion of support for JNG and Full-Color-Management respectively. * */ +/* * * */ +/* * This library's function is primarily to read and display MNG * */ +/* * animations. It is not meant as a full-featured image-editing * */ +/* * component! It does however offer creation and editing functionality * */ +/* * at the chunk level. * */ +/* * (future modifications may include some more support for creation * */ +/* * and or editing) * */ +/* * * */ +/* ************************************************************************** */ + +/* ************************************************************************** */ +/* * * */ +/* * Version numbering * */ +/* * * */ +/* * X.Y.Z : X = release (0 = initial build) * */ +/* * Y = major version (uneven = test; even = production) * */ +/* * Z = minor version (bugfixes; 2 is older than 10) * */ +/* * * */ +/* * production versions only appear when a test-version is extensively * */ +/* * tested and found stable or for intermediate bug-fixes (recognized by * */ +/* * a change in the Z number) * */ +/* * * */ +/* * x.1.x = test version * */ +/* * x.2.x = production version * */ +/* * x.3.x = test version * */ +/* * x.4.x = production version * */ +/* * etc. * */ +/* * * */ +/* ************************************************************************** */ +/* * * */ +/* * Identifier naming conventions throughout this library * */ +/* * * */ +/* * iXxxx = an integer * */ +/* * dXxxx = a float * */ +/* * pXxxx = a pointer * */ +/* * bXxxx = a boolean * */ +/* * eXxxx = an enumeration * */ +/* * hXxxx = a handle * */ +/* * zXxxx = a zero-terminated string (pchar) * */ +/* * fXxxx = a pointer to a function (callback) * */ +/* * aXxxx = an array * */ +/* * sXxxx = a structure * */ +/* * * */ +/* * Macros & defines are in all uppercase. * */ +/* * Functions & typedefs in all lowercase. * */ +/* * Exported stuff is prefixed with MNG_ or mng_ respectively. * */ +/* * * */ +/* * (I may have missed a couple; don't hesitate to let me know!) * */ +/* * * */ +/* ************************************************************************** */ + +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng.h copyright (c) 2000-2007 G.Juyn * */ +/* * version : 1.0.10 * */ +/* * * */ +/* * purpose : main application interface * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : The main application interface. An application should not * */ +/* * need access to any of the other modules! * */ +/* * * */ +/* * changes : 0.5.1 - 05/06/2000 - G.Juyn * */ +/* * - changed chunk iteration function * */ +/* * 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - added chunk access functions * */ +/* * - added version control constants & functions * */ +/* * - changed strict-ANSI stuff * */ +/* * 0.5.1 - 05/11/2000 - G.Juyn * */ +/* * - added set_outputprofile2 & set_srgbprofile2 * */ +/* * - added empty-chunk put-routines * */ +/* * 0.5.1 - 05/12/2000 - G.Juyn * */ +/* * - added version_dll & VERSION_DLL (for consistency) * */ +/* * - added version control explanatory text & samples * */ +/* * 0.5.1 - 05/15/2000 - G.Juyn * */ +/* * - added getimgdata & putimgdata functions * */ +/* * * */ +/* * 0.5.2 - 05/16/2000 - G.Juyn * */ +/* * - changed the version parameters (obviously) * */ +/* * 0.5.2 - 05/18/2000 - G.Juyn * */ +/* * - complimented constants for chunk-property values * */ +/* * 0.5.2 - 05/23/2000 - G.Juyn * */ +/* * - fixed MNG_UINT_pHYg value * */ +/* * 0.5.2 - 05/24/2000 - G.Juyn * */ +/* * - added support for get/set default zlib/IJG parms * */ +/* * 0.5.2 - 06/02/2000 - G.Juyn * */ +/* * - added MNG_BIGENDIAN_SUPPORT (contributed by Tim Rowley) * */ +/* * - separated configuration-options into "mng_conf.h" * */ +/* * - added RGB8_A8 canvasstyle * */ +/* * - added getalphaline callback for RGB8_A8 canvasstyle * */ +/* * 0.5.2 - 06/06/2000 - G.Juyn * */ +/* * - moved errorcodes from "mng_error.h" * */ +/* * - added mng_read_resume function to support * */ +/* * read-suspension * */ +/* * * */ +/* * 0.5.3 - 06/16/2000 - G.Juyn * */ +/* * - changed the version parameters (obviously) * */ +/* * 0.5.3 - 06/21/2000 - G.Juyn * */ +/* * - added get/set for speedtype to facilitate testing * */ +/* * - added get for imagelevel during processtext callback * */ +/* * 0.5.3 - 06/24/2000 - G.Juyn * */ +/* * - fixed inclusion of IJG read/write code * */ +/* * 0.5.3 - 06/26/2000 - G.Juyn * */ +/* * - changed userdata variable to mng_ptr * */ +/* * * */ +/* * 0.9.0 - 06/30/2000 - G.Juyn * */ +/* * - changed refresh parameters to 'x,y,width,height' * */ +/* * * */ +/* * 0.9.1 - 07/06/2000 - G.Juyn * */ +/* * - added MNG_NEEDTIMERWAIT errorcode * */ +/* * - changed comments to indicate modified behavior for * */ +/* * timer & suspension breaks * */ +/* * 0.9.1 - 07/08/2000 - G.Juyn * */ +/* * - added get routines for internal display variables * */ +/* * - added get/set routines for suspensionmode variable * */ +/* * 0.9.1 - 07/15/2000 - G.Juyn * */ +/* * - added callbacks for SAVE/SEEK processing * */ +/* * - added get/set routines for sectionbreak variable * */ +/* * - added NEEDSECTIONWAIT errorcode * */ +/* * 0.9.1 - 07/19/2000 - G.Juyn * */ +/* * - added function to set frame-/layer-count & playtime * */ +/* * - added errorcode for updatemngheader if not a MNG * */ +/* * * */ +/* * 0.9.2 - 07/31/2000 - G.Juyn * */ +/* * - fixed problem with trace-functions improperly wrapped * */ +/* * - added status_xxxx functions * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * - added function to set simplicity field * */ +/* * * */ +/* * 0.9.3 - 08/09/2000 - G.Juyn * */ +/* * - added check for simplicity-bits in MHDR * */ +/* * 0.9.3 - 08/12/2000 - G.Juyn * */ +/* * - added workaround for faulty PhotoShop iCCP chunk * */ +/* * 0.9.3 - 08/26/2000 - G.Juyn * */ +/* * - added MAGN chunk * */ +/* * 0.9.3 - 09/07/2000 - G.Juyn * */ +/* * - added support for new filter_types * */ +/* * 0.9.3 - 10/10/2000 - G.Juyn * */ +/* * - added support for alpha-depth prediction * */ +/* * 0.9.3 - 10/11/2000 - G.Juyn * */ +/* * - fixed processing of unknown critical chunks * */ +/* * - removed test-MaGN * */ +/* * - added PNG/MNG spec version indicators * */ +/* * - added support for nEED * */ +/* * 0.9.3 - 10/16/2000 - G.Juyn * */ +/* * - added functions to retrieve PNG/JNG specific header-info * */ +/* * - added JDAA chunk * */ +/* * 0.9.3 - 10/17/2000 - G.Juyn * */ +/* * - added callback to process non-critical unknown chunks * */ +/* * 0.9.3 - 10/20/2000 - G.Juyn * */ +/* * - added errocode for delayed delta-processing * */ +/* * - added get/set for bKGD preference setting * */ +/* * 0.9.3 - 10/21/2000 - G.Juyn * */ +/* * - added get function for interlace/progressive display * */ +/* * * */ +/* * 0.9.4 - 01/18/2001 - G.Juyn * */ +/* * - added errorcode for MAGN methods * */ +/* * - removed test filter-methods 1 & 65 * */ +/* * * */ +/* * 1.0.0 - 02/05/2001 - G.Juyn * */ +/* * - version numbers (obviously) * */ +/* * * */ +/* * 1.0.1 - 02/08/2001 - G.Juyn * */ +/* * - added MEND processing callback * */ +/* * 1.0.1 - 04/21/2001 - G.Juyn (code by G.Kelly) * */ +/* * - added BGRA8 canvas with premultiplied alpha * */ +/* * 1.0.1 - 05/02/2001 - G.Juyn * */ +/* * - added "default" sRGB generation (Thanks Marti!) * */ +/* * * */ +/* * 1.0.2 - 06/23/2001 - G.Juyn * */ +/* * - added optimization option for MNG-video playback * */ +/* * - added processterm callback * */ +/* * 1.0.2 - 06/25/2001 - G.Juyn * */ +/* * - added late binding errorcode (not used internally) * */ +/* * - added option to turn off progressive refresh * */ +/* * * */ +/* * 1.0.3 - 08/06/2001 - G.Juyn * */ +/* * - added get function for last processed BACK chunk * */ +/* * * */ +/* * 1.0.5 - 07/04/2002 - G.Juyn * */ +/* * - added errorcode for extreme chunk-sizes * */ +/* * 1.0.5 - 08/07/2002 - G.Juyn * */ +/* * - added test-option for PNG filter method 193 (=no filter) * */ +/* * 1.0.5 - 08/15/2002 - G.Juyn * */ +/* * - completed PROM support * */ +/* * - completed delta-image support * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - added HLAPI function to copy chunks * */ +/* * 1.0.5 - 09/14/2002 - G.Juyn * */ +/* * - added event handling for dynamic MNG * */ +/* * - added 'supports' call to check function availability * */ +/* * 1.0.5 - 09/15/2002 - G.Juyn * */ +/* * - fixed LOOP iteration=0 special case * */ +/* * 1.0.5 - 09/20/2002 - G.Juyn * */ +/* * - added support for PAST * */ +/* * 1.0.5 - 09/22/2002 - G.Juyn * */ +/* * - added bgrx8 canvas (filler byte) * */ +/* * 1.0.5 - 10/07/2002 - G.Juyn * */ +/* * - added check for TERM placement during create/write * */ +/* * - added beta version function & constant * */ +/* * 1.0.5 - 11/07/2002 - G.Juyn * */ +/* * - added support to get totals after mng_read() * */ +/* * * */ +/* * 1.0.6 - 07/07/2003 - G. Randers-Pehrson * */ +/* * - added support for reducing the footprint of libmng * */ +/* * by macros that optionally skip unused chunks, remove * */ +/* * 16-bit sample support, remove Delta support, and * */ +/* * remove JNG support, to accomodate Mozilla/Firebird. * */ +/* * 1.0.6 - 07/14/2003 - G. Randers-Pehrson * */ +/* * - further optional removal of unused functions * */ +/* * * */ +/* * 1.0.7 - 11/27/2003 - R.A * */ +/* * - added CANVAS_RGB565 and CANVAS_BGR565 * */ +/* * 1.0.7 - 12/06/2003 - R.A * */ +/* * - added CANVAS_RGBA565 and CANVAS_BGRA565 * */ +/* * 1.0.7 - 01/25/2004 - J.S * */ +/* * - added premultiplied alpha canvas' for RGBA, ARGB, ABGR * */ +/* * 1.0.7 - 03/07/2004 - G. Randers-Pehrson * */ +/* * - put gamma, cms-related declarations inside #ifdef * */ +/* * 1.0.7 - 03/10/2004 - G.R-P * */ +/* * - added conditionals around openstream/closestream * */ +/* * * */ +/* * 1.0.8 - 04/02/2004 - G.Juyn * */ +/* * - added CRC existence & checking flags * */ +/* * 1.0.8 - 04/12/2004 - G.Juyn * */ +/* * - added data-push mechanisms for specialized decoders * */ +/* * 1.0.8 - 06/05/2004 - G.R-P * */ +/* * - define MNG_INCLUDE_ZLIB when MNG_USE_ZLIB_CRC is defined * */ +/* * * */ +/* * 1.0.9 - 10/03/2004 - G.Juyn * */ +/* * - added function to retrieve current FRAM delay * */ +/* * 1.0.9 - 10/14/2004 - G.Juyn * */ +/* * - added bgr565_a8 canvas-style (thanks to J. Elvander) * */ +/* * 1.0.9 - 10/17/2004 - G.Juyn * */ +/* * - fixed PPLT getchunk/putchunk routines * */ +/* * * */ +/* * 1.0.10 - 03/07/2006 - (thanks to W. Manthey) * */ +/* * - added CANVAS_RGB555 and CANVAS_BGR555 * */ +/* * 1.0.10 - 04/08/2007 - G.Juyn * */ +/* * - added support for mPNG proposal * */ +/* * 1.0.10 - 04/12/2007 - G.Juyn * */ +/* * - added support for ANG proposal * */ +/* * * */ +/* ************************************************************************** */ + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +#ifndef _libmng_h_ +#define _libmng_h_ + +/* ************************************************************************** */ + +#include "libmng_conf.h" /* user-specific configuration options */ + +/* ************************************************************************** */ + +#define MNG_CHECK_BAD_ICCP /* let's catch that sucker !!! */ + +#ifdef MNG_SUPPORT_READ /* dependencies based on user-configuration */ +#define MNG_INCLUDE_READ_PROCS +#endif + +#ifdef MNG_SUPPORT_WRITE +#define MNG_INCLUDE_WRITE_PROCS +#endif + +#ifdef MNG_USE_ZLIB_CRC +#define MNG_INCLUDE_ZLIB +#endif + +#ifdef MNG_SUPPORT_DISPLAY +#define MNG_INCLUDE_FILTERS +#define MNG_INCLUDE_INTERLACE +#define MNG_INCLUDE_OBJECTS +#define MNG_INCLUDE_DISPLAY_PROCS +#define MNG_INCLUDE_TIMING_PROCS +#define MNG_INCLUDE_ZLIB +#endif + +#ifdef MNG_STORE_CHUNKS +#define MNG_INCLUDE_ZLIB +#endif + +#ifdef MNG_SUPPORT_IJG6B +#define MNG_INCLUDE_JNG +#define MNG_INCLUDE_IJG6B +#define MNG_USE_SETJMP +#endif + +#ifdef MNG_INCLUDE_JNG +#if defined(MNG_SUPPORT_DISPLAY) || defined(MNG_ACCESS_CHUNKS) +#define MNG_INCLUDE_JNG_READ +#endif +#if defined(MNG_SUPPORT_WRITE) || defined(MNG_ACCESS_CHUNKS) +#define MNG_INCLUDE_JNG_WRITE +#endif +#endif + +#ifdef MNG_FULL_CMS +#define MNG_INCLUDE_LCMS +#endif + +#ifdef MNG_AUTO_DITHER +#define MNG_INCLUDE_DITHERING +#endif + +#ifdef MNG_SUPPORT_TRACE +#define MNG_INCLUDE_TRACE_PROCS +#ifdef MNG_TRACE_TELLTALE +#define MNG_INCLUDE_TRACE_STRINGS +#endif +#endif + +#ifdef MNG_ERROR_TELLTALE +#define MNG_INCLUDE_ERROR_STRINGS +#endif + +#ifdef MNG_OPTIMIZE_CHUNKINITFREE +#ifndef MNG_OPTIMIZE_CHUNKACCESS +#define MNG_OPTIMIZE_CHUNKACCESS +#endif +#else +#ifdef MNG_OPTIMIZE_CHUNKACCESS +#undef MNG_OPTIMIZE_CHUNKACCESS +#endif +#endif + +/* ************************************************************************** */ + +#include "libmng_types.h" /* platform-specific definitions + and other assorted stuff */ + +/* ************************************************************************** */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* ************************************************************************** */ +/* * * */ +/* * Versioning control * */ +/* * * */ +/* * version_so and version_dll will NOT reflect version_major; * */ +/* * these will only change for binary incompatible changes (which will * */ +/* * hopefully never occur) * */ +/* * note: they will be set to 1 on the first public release !!! * */ +/* * * */ +/* * first public release: * */ +/* * #define MNG_VERSION_TEXT "1.0.0" * */ +/* * #define MNG_VERSION_SO 1 eg. libmng.so.1 * */ +/* * #define MNG_VERSION_DLL 1 eg. libmng.dll * */ +/* * #define MNG_VERSION_MAJOR 1 * */ +/* * #define MNG_VERSION_MINOR 0 * */ +/* * #define MNG_VERSION_RELEASE 0 * */ +/* * * */ +/* * bug fix & cosmetics : * */ +/* * #define MNG_VERSION_TEXT "1.0.1" * */ +/* * #define MNG_VERSION_SO 1 eg. libmng.so.1 * */ +/* * #define MNG_VERSION_DLL 1 eg. libmng.dll * */ +/* * #define MNG_VERSION_MAJOR 1 * */ +/* * #define MNG_VERSION_MINOR 0 * */ +/* * #define MNG_VERSION_RELEASE 1 * */ +/* * * */ +/* * feature change : * */ +/* * #define MNG_VERSION_TEXT "1.2.0" * */ +/* * #define MNG_VERSION_SO 1 eg. libmng.so.1 * */ +/* * #define MNG_VERSION_DLL 1 eg. libmng.dll * */ +/* * #define MNG_VERSION_MAJOR 1 * */ +/* * #define MNG_VERSION_MINOR 2 * */ +/* * #define MNG_VERSION_RELEASE 0 * */ +/* * * */ +/* * major rewrite (still binary compatible) : * */ +/* * #define MNG_VERSION_TEXT "2.0.0" * */ +/* * #define MNG_VERSION_SO 1 eg. libmng.so.1 * */ +/* * #define MNG_VERSION_DLL 1 eg. libmng.dll * */ +/* * #define MNG_VERSION_MAJOR 2 * */ +/* * #define MNG_VERSION_MINOR 0 * */ +/* * #define MNG_VERSION_RELEASE 0 * */ +/* * * */ +/* * binary incompatible change: * */ +/* * #define MNG_VERSION_TEXT "13.0.0" * */ +/* * #define MNG_VERSION_SO 2 eg. libmng.so.2 * */ +/* * #define MNG_VERSION_DLL 2 eg. libmng2.dll * */ +/* * #define MNG_VERSION_MAJOR 13 * */ +/* * #define MNG_VERSION_MINOR 0 * */ +/* * #define MNG_VERSION_RELEASE 0 * */ +/* * * */ +/* * note that version_so & version_dll will always remain equal so it * */ +/* * doesn't matter which one is called to do version-checking; they are * */ +/* * just provided for their target platform * */ +/* * * */ +/* ************************************************************************** */ + +#define MNG_VERSION_TEXT "1.0.10" +#define MNG_VERSION_SO 1 /* eg. libmng.so.1 */ +#define MNG_VERSION_DLL 1 /* but: libmng.dll (!) */ +#define MNG_VERSION_MAJOR 1 +#define MNG_VERSION_MINOR 0 +#define MNG_VERSION_RELEASE 10 +#define MNG_VERSION_BETA MNG_FALSE + +MNG_EXT mng_pchar MNG_DECL mng_version_text (void); +MNG_EXT mng_uint8 MNG_DECL mng_version_so (void); +MNG_EXT mng_uint8 MNG_DECL mng_version_dll (void); +MNG_EXT mng_uint8 MNG_DECL mng_version_major (void); +MNG_EXT mng_uint8 MNG_DECL mng_version_minor (void); +MNG_EXT mng_uint8 MNG_DECL mng_version_release (void); +MNG_EXT mng_bool MNG_DECL mng_version_beta (void); + +/* use the following call to check wether the version of libmng your app + is using supports the given function; this is useful in apps that dynamically + load the library to make sure a certain function will work; the result will + be MNG_TRUE if the given function is implemented in this version of the library; + Major/Minor/Version indicate the version the function became available; + (if these fields are zero the function is not yet implemented!) */ +#ifdef MNG_SUPPORT_FUNCQUERY +MNG_EXT mng_bool MNG_DECL mng_supports_func (mng_pchar zFunction, + mng_uint8* iMajor, + mng_uint8* iMinor, + mng_uint8* iRelease); +#endif + +/* ************************************************************************** */ +/* * * */ +/* * MNG/PNG specification level conformance * */ +/* * * */ +/* ************************************************************************** */ + +#define MNG_PNG_VERSION "1.2" +#define MNG_PNG_VERSION_MAJ 1 +#define MNG_PNG_VERSION_MIN 2 + +#define MNG_MNG_VERSION "1.1" +#define MNG_MNG_VERSION_MAJ 1 +#define MNG_MNG_VERSION_MIN 1 +#define MNG_MNG_DRAFT 99 /* deprecated; + only used for nEED "MNG DRAFT nn" */ + +/* ************************************************************************** */ +/* * * */ +/* * High-level application functions * */ +/* * * */ +/* ************************************************************************** */ + +/* library initialization function */ +/* must be the first called before anything can be done at all */ +/* initializes internal datastructure(s) */ +MNG_EXT mng_handle MNG_DECL mng_initialize (mng_ptr pUserdata, + mng_memalloc fMemalloc, + mng_memfree fMemfree, + mng_traceproc fTraceproc); + +/* library reset function */ +/* can be used to re-initialize the library, so another image can be + processed. there's absolutely no harm in calling it, even when it's not + really necessary */ +MNG_EXT mng_retcode MNG_DECL mng_reset (mng_handle hHandle); + +/* library cleanup function */ +/* must be the last called to clean up internal datastructure(s) */ +MNG_EXT mng_retcode MNG_DECL mng_cleanup (mng_handle* hHandle); + +/* high-level read functions */ +/* use mng_read if you simply want to read a Network Graphic */ +/* mng_read_resume is used in I/O-read-suspension scenarios, where the + "readdata" callback may return FALSE & length=0 indicating its buffer is + depleted or too short to supply the required bytes, and the buffer needs + to be refilled; libmng will return the errorcode MNG_NEEDMOREDATA telling + the app to refill its read-buffer after which it must call mng_read_resume + (or mng_display_resume if it also displaying the image simultaneously) */ +#ifdef MNG_SUPPORT_READ +MNG_EXT mng_retcode MNG_DECL mng_read (mng_handle hHandle); +MNG_EXT mng_retcode MNG_DECL mng_read_resume (mng_handle hHandle); +#endif + +/* high-level "data push" functions */ +/* these functions can be used in situations where data is streaming into the + application and needs to be buffered by libmng before it is actually + requested by libmng itself. the pushing complements the normal reading + mechanism, but applications can decide to always return "0 bytes read" to + make libmng go into suspension mode with the returncode MNG_NEEDMOREDATA */ +/* mng_read_pushdata can be used to push blobs of data of arbitrary size; + mng_read_pushsig and mng_read_pushchunk can be used if the application + has already done some low-level decoding (eg. at the chunk level) */ +/* the data being pushed into libmng with mng_read_pushdata *must* contain + the regular 4-byte chunklength, but *must not* contain it with + mng_read_pushchunk!!! */ +/* mng_read_pushsig is used to prevent libmng from trying to parse the regular + PNG/JNG/MNG signature bytes; the application must have done this itself + and *must* indicate the proper type in the function call or things will + go amiss!! + also you *must* call this first, so pretty much right after mng_initialize + and certainly before any call to mng_read or mng_readdisplay !!!! */ +/* IMPORTANT!!! data can only be safely pushed when libmng is in a + "wait" state; eg. during MNG_NEEDTIMERWAIT, MNG_NEEDSECTIONWAIT or + MNG_NEEDMOREDATA !!! this just means you can't have one thread displaying + and another thread pushing data !!! */ +/* if bOwnership = MNG_TRUE, libmng will retain the supplied pointer and + *will* expect the buffer to remain available until libmng is finished + with it; what happens then depends on whether or not you have set the + releasedata() callback; if this is set than the supplied buffer will + be returned through this callback and your application can take care of + cleaning it up, otherwise libmng will use its internal freeing mechanism + (which, depending on compile-options, will be the standard C free() call, + or the memfree() callback */ +/* if bOwnership = MNG_FALSE, libmng will just copy the data into its own + buffers and dispose of it in the normal way */ +#ifdef MNG_SUPPORT_READ +MNG_EXT mng_retcode MNG_DECL mng_read_pushdata (mng_handle hHandle, + mng_ptr pData, + mng_size_t iLength, + mng_bool bTakeownership); +MNG_EXT mng_retcode MNG_DECL mng_read_pushsig (mng_handle hHandle, + mng_imgtype eSigtype); +MNG_EXT mng_retcode MNG_DECL mng_read_pushchunk (mng_handle hHandle, + mng_ptr pChunk, + mng_size_t iLength, + mng_bool bTakeownership); +#endif + +/* high-level write & create functions */ +/* use this if you want to write a previously read Network Graphic or + if you want to create a new graphic and write it */ +/* to write a previously read graphic you must have defined MNG_STORE_CHUNKS */ +/* to create a new graphic you'll also need access to the chunks + (eg. #define MNG_ACCESS_CHUNKS !) */ +#ifdef MNG_SUPPORT_WRITE +MNG_EXT mng_retcode MNG_DECL mng_write (mng_handle hHandle); +MNG_EXT mng_retcode MNG_DECL mng_create (mng_handle hHandle); +#endif + +/* high-level display functions */ +/* use these to display a previously read or created graphic or + to read & display a graphic simultaneously */ +/* mng_display_resume should be called after a timer-interval + expires that was set through the settimer-callback, after a + read suspension-break, or, to resume an animation after a call + to mng_display_freeze/mng_display_reset */ +/* mng_display_freeze thru mng_display_gotime can be used to influence + the display of an image, BUT ONLY if it has been completely read! */ +#ifdef MNG_SUPPORT_DISPLAY +#ifdef MNG_SUPPORT_READ +MNG_EXT mng_retcode MNG_DECL mng_readdisplay (mng_handle hHandle); +#endif +MNG_EXT mng_retcode MNG_DECL mng_display (mng_handle hHandle); +MNG_EXT mng_retcode MNG_DECL mng_display_resume (mng_handle hHandle); +MNG_EXT mng_retcode MNG_DECL mng_display_freeze (mng_handle hHandle); +MNG_EXT mng_retcode MNG_DECL mng_display_reset (mng_handle hHandle); +#ifndef MNG_NO_DISPLAY_GO_SUPPORTED +MNG_EXT mng_retcode MNG_DECL mng_display_goframe (mng_handle hHandle, + mng_uint32 iFramenr); +MNG_EXT mng_retcode MNG_DECL mng_display_golayer (mng_handle hHandle, + mng_uint32 iLayernr); +MNG_EXT mng_retcode MNG_DECL mng_display_gotime (mng_handle hHandle, + mng_uint32 iPlaytime); +#endif +#endif /* MNG_SUPPORT_DISPLAY */ + +/* event processing function */ +/* this needs to be called by the app when dynamic MNG is enabled and + a specific event occurs in the user-interface */ +#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_SUPPORT_DYNAMICMNG) +MNG_EXT mng_retcode MNG_DECL mng_trapevent (mng_handle hHandle, + mng_uint8 iEventtype, + mng_int32 iX, + mng_int32 iY); +#endif + +/* error reporting function */ +/* use this if you need more detailed info on the last error */ +/* iExtra1 & iExtra2 may contain errorcodes from zlib, jpeg, etc... */ +/* zErrortext will only be filled if you #define MNG_ERROR_TELLTALE */ +MNG_EXT mng_retcode MNG_DECL mng_getlasterror (mng_handle hHandle, + mng_int8* iSeverity, + mng_chunkid* iChunkname, + mng_uint32* iChunkseq, + mng_int32* iExtra1, + mng_int32* iExtra2, + mng_pchar* zErrortext); + +/* ************************************************************************** */ +/* * * */ +/* * Callback set functions * */ +/* * * */ +/* ************************************************************************** */ + +/* memory callbacks */ +/* called to allocate and release internal datastructures */ +#ifndef MNG_INTERNAL_MEMMNGMT +MNG_EXT mng_retcode MNG_DECL mng_setcb_memalloc (mng_handle hHandle, + mng_memalloc fProc); +MNG_EXT mng_retcode MNG_DECL mng_setcb_memfree (mng_handle hHandle, + mng_memfree fProc); +#endif /* MNG_INTERNAL_MEMMNGMT */ + +/* open- & close-stream callbacks */ +/* called to open & close streams for input or output */ +#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE) +#ifndef MNG_NO_OPEN_CLOSE_STREAM +MNG_EXT mng_retcode MNG_DECL mng_setcb_openstream (mng_handle hHandle, + mng_openstream fProc); +MNG_EXT mng_retcode MNG_DECL mng_setcb_closestream (mng_handle hHandle, + mng_closestream fProc); +#endif +#endif + +/* read callback */ +/* called to get data from the inputstream */ +#ifdef MNG_SUPPORT_READ +MNG_EXT mng_retcode MNG_DECL mng_setcb_readdata (mng_handle hHandle, + mng_readdata fProc); +#endif + +/* write callback */ +/* called to put data into the outputstream */ +#ifdef MNG_SUPPORT_WRITE +MNG_EXT mng_retcode MNG_DECL mng_setcb_writedata (mng_handle hHandle, + mng_writedata fProc); +#endif + +/* error callback */ +/* called when an error occurs */ +/* the application can determine if the error is recoverable, + and may inform the library by setting specific returncodes */ +MNG_EXT mng_retcode MNG_DECL mng_setcb_errorproc (mng_handle hHandle, + mng_errorproc fProc); + +/* trace callback */ +/* called to show the currently executing function */ +#ifdef MNG_SUPPORT_TRACE +MNG_EXT mng_retcode MNG_DECL mng_setcb_traceproc (mng_handle hHandle, + mng_traceproc fProc); +#endif + +/* callbacks for read processing */ +/* processheader is called when all header information has been gathered + from the inputstream */ +/* processtext is called for every tEXt, zTXt and iTXt chunk in the + inputstream (iType=0 for tEXt, 1 for zTXt and 2 for iTXt); + you can call get_imagelevel to check at what nesting-level the chunk is + encountered (eg. tEXt inside an embedded image inside a MNG -> level == 2; + in most other case -> level == 1) */ +/* processsave & processseek are called for SAVE/SEEK chunks */ +/* processneed is called for the nEED chunk; you should specify a callback + for this as the default behavior will be to abort processing, unless + the requirement is one of: + - a supported chunk + - the text "draft nn" where nn is a numeric value + - the text "MNG-1.0" or "MNG-1.1" + - the text "CACHEOFF" */ +/* processmend is called at the very end of the animation-stream; + note that this may not be the end of the animation though! */ +/* processterm is called when a TERM chunk is encountered; there can be only + 1 in the stream (or none) */ +/* processunknown is called after reading each non-critical unknown chunk */ +#ifdef MNG_SUPPORT_READ +MNG_EXT mng_retcode MNG_DECL mng_setcb_processheader (mng_handle hHandle, + mng_processheader fProc); +MNG_EXT mng_retcode MNG_DECL mng_setcb_processtext (mng_handle hHandle, + mng_processtext fProc); +MNG_EXT mng_retcode MNG_DECL mng_setcb_processsave (mng_handle hHandle, + mng_processsave fProc); +MNG_EXT mng_retcode MNG_DECL mng_setcb_processseek (mng_handle hHandle, + mng_processseek fProc); +MNG_EXT mng_retcode MNG_DECL mng_setcb_processneed (mng_handle hHandle, + mng_processneed fProc); +MNG_EXT mng_retcode MNG_DECL mng_setcb_processmend (mng_handle hHandle, + mng_processmend fProc); +MNG_EXT mng_retcode MNG_DECL mng_setcb_processterm (mng_handle hHandle, + mng_processterm fProc); +MNG_EXT mng_retcode MNG_DECL mng_setcb_processunknown(mng_handle hHandle, + mng_processunknown fProc); +#endif + +/* callbacks for display processing */ +/* getcanvasline is called to get an access-pointer to a line on the + drawing-canvas */ +/* getbkgdline is called to get an access-pointer to a line from the + background-canvas */ +/* refresh is called to inform the GUI to redraw the current canvas onto + its output device (eg. in Win32 this would mean sending an + invalidate message for the specified region */ +/* NOTE that the update-region is specified as x,y,width,height; eg. the + invalidate message for Windows requires left,top,right,bottom parameters + where the bottom-right is exclusive of the region!! + to get these correctly is as simple as: + left = x; + top = y; + right = x + width; + bottom = y + height; + if your implementation requires inclusive points, simply subtract 1 from + both the right & bottom values calculated above. + */ +#ifdef MNG_SUPPORT_DISPLAY +MNG_EXT mng_retcode MNG_DECL mng_setcb_getcanvasline (mng_handle hHandle, + mng_getcanvasline fProc); +MNG_EXT mng_retcode MNG_DECL mng_setcb_getbkgdline (mng_handle hHandle, + mng_getbkgdline fProc); +MNG_EXT mng_retcode MNG_DECL mng_setcb_getalphaline (mng_handle hHandle, + mng_getalphaline fProc); +MNG_EXT mng_retcode MNG_DECL mng_setcb_refresh (mng_handle hHandle, + mng_refresh fProc); + +/* timing callbacks */ +/* gettickcount is called to get the system tickcount (milliseconds); + this is used to determine the remaining interval between frames */ +/* settimer is called to inform the application that it should set a timer; + when the timer is triggered the app must call mng_display_resume */ +MNG_EXT mng_retcode MNG_DECL mng_setcb_gettickcount (mng_handle hHandle, + mng_gettickcount fProc); +MNG_EXT mng_retcode MNG_DECL mng_setcb_settimer (mng_handle hHandle, + mng_settimer fProc); + +/* color management callbacks */ +/* called to transmit color management information to the application */ +/* these are only used when you #define MNG_APP_CMS */ +#ifdef MNG_APP_CMS +MNG_EXT mng_retcode MNG_DECL mng_setcb_processgamma (mng_handle hHandle, + mng_processgamma fProc); +MNG_EXT mng_retcode MNG_DECL mng_setcb_processchroma (mng_handle hHandle, + mng_processchroma fProc); +MNG_EXT mng_retcode MNG_DECL mng_setcb_processsrgb (mng_handle hHandle, + mng_processsrgb fProc); +MNG_EXT mng_retcode MNG_DECL mng_setcb_processiccp (mng_handle hHandle, + mng_processiccp fProc); +MNG_EXT mng_retcode MNG_DECL mng_setcb_processarow (mng_handle hHandle, + mng_processarow fProc); +#endif /* MNG_APP_CMS */ +#endif /* MNG_SUPPORT_DISPLAY */ + +/* release push data callback */ +/* used when the app pushes data into libmng (as opposed to libmng pulling it) + and relinquishes ownership of the pushed data-buffer, but *does* want to + release (free) the buffer itself once libmng has finished processing it */ +#ifdef MNG_SUPPORT_READ +MNG_EXT mng_retcode MNG_DECL mng_setcb_releasedata (mng_handle hHandle, + mng_releasedata fProc); +#endif + +/* ************************************************************************** */ +/* * * */ +/* * Callback get functions * */ +/* * * */ +/* ************************************************************************** */ + +/* see _setcb_ */ +#ifndef MNG_INTERNAL_MEMMNGMT +MNG_EXT mng_memalloc MNG_DECL mng_getcb_memalloc (mng_handle hHandle); +MNG_EXT mng_memfree MNG_DECL mng_getcb_memfree (mng_handle hHandle); +#endif + +/* see _setcb_ */ +#ifdef MNG_SUPPORT_READ +MNG_EXT mng_releasedata MNG_DECL mng_getcb_releasedata (mng_handle hHandle); +#endif + +/* see _setcb_ */ +#if defined(MNG_SUPPORT_READ) || defined(MNG_WRITE_SUPPORT) +#ifndef MNG_NO_OPEN_CLOSE_STREAM +MNG_EXT mng_openstream MNG_DECL mng_getcb_openstream (mng_handle hHandle); +MNG_EXT mng_closestream MNG_DECL mng_getcb_closestream (mng_handle hHandle); +#endif +#endif + +/* see _setcb_ */ +#ifdef MNG_SUPPORT_READ +MNG_EXT mng_readdata MNG_DECL mng_getcb_readdata (mng_handle hHandle); +#endif + +/* see _setcb_ */ +#ifdef MNG_SUPPORT_WRITE +MNG_EXT mng_writedata MNG_DECL mng_getcb_writedata (mng_handle hHandle); +#endif + +/* see _setcb_ */ +MNG_EXT mng_errorproc MNG_DECL mng_getcb_errorproc (mng_handle hHandle); + +/* see _setcb_ */ +#ifdef MNG_SUPPORT_TRACE +MNG_EXT mng_traceproc MNG_DECL mng_getcb_traceproc (mng_handle hHandle); +#endif + +/* see _setcb_ */ +#ifdef MNG_SUPPORT_READ +MNG_EXT mng_processheader MNG_DECL mng_getcb_processheader (mng_handle hHandle); +MNG_EXT mng_processtext MNG_DECL mng_getcb_processtext (mng_handle hHandle); +MNG_EXT mng_processsave MNG_DECL mng_getcb_processsave (mng_handle hHandle); +MNG_EXT mng_processseek MNG_DECL mng_getcb_processseek (mng_handle hHandle); +MNG_EXT mng_processneed MNG_DECL mng_getcb_processneed (mng_handle hHandle); +MNG_EXT mng_processunknown MNG_DECL mng_getcb_processunknown (mng_handle hHandle); +MNG_EXT mng_processterm MNG_DECL mng_getcb_processterm (mng_handle hHandle); +#endif + +/* see _setcb_ */ +#ifdef MNG_SUPPORT_DISPLAY +MNG_EXT mng_getcanvasline MNG_DECL mng_getcb_getcanvasline (mng_handle hHandle); +MNG_EXT mng_getbkgdline MNG_DECL mng_getcb_getbkgdline (mng_handle hHandle); +MNG_EXT mng_getalphaline MNG_DECL mng_getcb_getalphaline (mng_handle hHandle); +MNG_EXT mng_refresh MNG_DECL mng_getcb_refresh (mng_handle hHandle); + +/* see _setcb_ */ +MNG_EXT mng_gettickcount MNG_DECL mng_getcb_gettickcount (mng_handle hHandle); +MNG_EXT mng_settimer MNG_DECL mng_getcb_settimer (mng_handle hHandle); + +/* see _setcb_ */ +#ifdef MNG_APP_CMS +MNG_EXT mng_processgamma MNG_DECL mng_getcb_processgamma (mng_handle hHandle); +MNG_EXT mng_processchroma MNG_DECL mng_getcb_processchroma (mng_handle hHandle); +MNG_EXT mng_processsrgb MNG_DECL mng_getcb_processsrgb (mng_handle hHandle); +MNG_EXT mng_processiccp MNG_DECL mng_getcb_processiccp (mng_handle hHandle); +MNG_EXT mng_processarow MNG_DECL mng_getcb_processarow (mng_handle hHandle); +#endif /* MNG_APP_CMS */ +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ +/* * * */ +/* * Property set functions * */ +/* * * */ +/* ************************************************************************** */ + +/* Application data pointer */ +/* provided for application use; not used by the library */ +MNG_EXT mng_retcode MNG_DECL mng_set_userdata (mng_handle hHandle, + mng_ptr pUserdata); + +/* The style of the drawing- & background-canvas */ +/* only used for displaying images */ +/* both are initially set to 24-bit RGB (eg. 8-bit per channel) */ +MNG_EXT mng_retcode MNG_DECL mng_set_canvasstyle (mng_handle hHandle, + mng_uint32 iStyle); +MNG_EXT mng_retcode MNG_DECL mng_set_bkgdstyle (mng_handle hHandle, + mng_uint32 iStyle); + +/* The default background color */ +/* only used if the getbkgdline callback is not defined */ +/* for initially painting the canvas and restoring (part of) the background */ +MNG_EXT mng_retcode MNG_DECL mng_set_bgcolor (mng_handle hHandle, + mng_uint16 iRed, + mng_uint16 iGreen, + mng_uint16 iBlue); + +/* Indicates preferred use of the bKGD chunk for PNG images */ +MNG_EXT mng_retcode MNG_DECL mng_set_usebkgd (mng_handle hHandle, + mng_bool bUseBKGD); + +/* Indicates storage of read chunks */ +/* only useful if you #define mng_store_chunks */ +/* can be used to dynamically change storage management */ +MNG_EXT mng_retcode MNG_DECL mng_set_storechunks (mng_handle hHandle, + mng_bool bStorechunks); + +/* Indicates breaks requested when processing SAVE/SEEK */ +/* set this to let the app handle section breaks; the library will return + MNG_NEEDSECTIONWAIT return-codes for each SEEK chunk */ +MNG_EXT mng_retcode MNG_DECL mng_set_sectionbreaks (mng_handle hHandle, + mng_bool bSectionbreaks); + +/* Indicates storage of playback info (ON by default!) */ +/* can be used to turn off caching of playback info; this is useful to + specifically optimize MNG-video playback; note that if caching is turned off + LOOP chunks will be flagged as errors! TERM chunks will be ignored and only + passed to the processterm() callback if it is defined by the app; also, this + feature can only be used with mng_readdisplay(); mng_read(), + mng_display_reset() and mng_display_goxxxx() will return an error; + once this option is turned off it can't be turned on for the same stream!!! */ +MNG_EXT mng_retcode MNG_DECL mng_set_cacheplayback (mng_handle hHandle, + mng_bool bCacheplayback); + +/* Indicates automatic progressive refreshes for large images (ON by default!) */ +/* turn this off if you do not want intermittent painting while a large image + is being read. useful if the input-stream comes from a fast medium, such + as a local harddisk */ +MNG_EXT mng_retcode MNG_DECL mng_set_doprogressive (mng_handle hHandle, + mng_bool bDoProgressive); + +/* Indicates existence and required checking of the CRC in input streams, + and generation in output streams */ +/* !!!! Use this ONLY if you know what you are doing !!!! */ +/* The value is a combination of the following flags: + 0x0000001 = CRC is present in the input stream + 0x0000002 = CRC must be generated in the output stream + 0x0000010 = CRC should be checked for ancillary chunks + 0x0000020 = a faulty CRC for ancillary chunks generates a warning only + 0x0000040 = a faulty CRC for ancillary chunks generates an error + 0x0000100 = CRC should be checked for critical chunks + 0x0000200 = a faulty CRC for critical chunks generates a warning only + 0x0000400 = a faulty CRC for critical chunks generates an error + + The default is 0x00000533 = CRC present in input streams; should be checked; + warning for ancillary chunks; error for critical + chunks; generate CRC for output streams + + Note that some combinations are meaningless; eg. if the CRC is not present + it won't do any good to turn the checking flags on; if a checking flag + is off, it doesn't do any good to ask for generation of warnings or errors. + Also libmng will generate either an error or a warning, not both, + so if you specify both the default will be to generate an error! + The only useful combinations for input are 331, 551, 351, 531, 0, 301, 501 + and optionally 031 and 051, but only checking ancillary chunks and not + critical chunks is generally not a very good idea!!! + If you've also writing these values should be combined with 0x02 if + CRC's are required in the output stream + */ +MNG_EXT mng_retcode MNG_DECL mng_set_crcmode (mng_handle hHandle, + mng_uint32 iCrcmode); + +/* Color-management necessaries */ +/* + ************************************************************************* + !!!!!!!! THIS NEXT BIT IS IMPORTANT !!!!!!!!! + ************************************************************************* + + If you have defined MNG_FULL_CMS (and are using lcms), you will have to + think hard about the following routines. + + lcms requires 2 profiles to work off the differences in the input-image + and the output-device. The ICC profile for the input-image will be + embedded within it to reflect its color-characteristics, but the output + profile depends on the output-device, which is something only *YOU* know + about. sRGB (standard RGB) is common for x86 compatible environments + (eg. Windows, Linux and some others) + + If you are compiling for a sRGB compliant system you probably won't have + to do anything special. (unless you want to of course) + + If you are compiling for a non-sRGB compliant system + (eg. SGI, Mac, Next, others...) + you *MUST* define a proper ICC profile for the generic output-device + associated with that platform. + + In either event, you may also want to offer an option to your users to + set the profile manually, or, if you know how, set it from a + system-defined default. + + TO RECAP: for sRGB systems (Windows, Linux) no action required! + for non-sRGB systems (SGI, Mac, Next) ACTION REQUIRED! + + Please visit http://www.srgb.com, http://www.color.org and + http://www.littlecms.com for more info. + + ************************************************************************* + !!!!!!!! THE BIT ABOVE IS IMPORTANT !!!!!!!!! + ************************************************************************* +*/ +/* mng_set_srgb tells libmng if it's running on a sRGB compliant system or not + the default is already set to MNG_TRUE */ +/* mng_set_outputprofile, mng_set_outputprofile2, mng_set_outputsrgb + are used to set the default profile describing the output-device + by default it is already initialized with an sRGB profile */ +/* mng_set_srgbprofile, mng_set_srgbprofile2, mng_set_srgbimplicit + are used to set the default profile describing a standard sRGB device + this is used when the input-image is tagged only as being sRGB, but the + output-device is defined as not being sRGB compliant + by default it is already initialized with a standard sRGB profile */ +#if defined(MNG_SUPPORT_DISPLAY) +MNG_EXT mng_retcode MNG_DECL mng_set_srgb (mng_handle hHandle, + mng_bool bIssRGB); +MNG_EXT mng_retcode MNG_DECL mng_set_outputprofile (mng_handle hHandle, + mng_pchar zFilename); +MNG_EXT mng_retcode MNG_DECL mng_set_outputprofile2 (mng_handle hHandle, + mng_uint32 iProfilesize, + mng_ptr pProfile); +MNG_EXT mng_retcode MNG_DECL mng_set_outputsrgb (mng_handle hHandle); +MNG_EXT mng_retcode MNG_DECL mng_set_srgbprofile (mng_handle hHandle, + mng_pchar zFilename); +MNG_EXT mng_retcode MNG_DECL mng_set_srgbprofile2 (mng_handle hHandle, + mng_uint32 iProfilesize, + mng_ptr pProfile); +MNG_EXT mng_retcode MNG_DECL mng_set_srgbimplicit (mng_handle hHandle); +#endif + +#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS) +/* Gamma settings */ +/* ... blabla (explain gamma processing a little; eg. formula & stuff) ... */ +MNG_EXT mng_retcode MNG_DECL mng_set_viewgamma (mng_handle hHandle, + mng_float dGamma); +MNG_EXT mng_retcode MNG_DECL mng_set_displaygamma (mng_handle hHandle, + mng_float dGamma); +MNG_EXT mng_retcode MNG_DECL mng_set_dfltimggamma (mng_handle hHandle, + mng_float dGamma); +MNG_EXT mng_retcode MNG_DECL mng_set_viewgammaint (mng_handle hHandle, + mng_uint32 iGamma); +MNG_EXT mng_retcode MNG_DECL mng_set_displaygammaint (mng_handle hHandle, + mng_uint32 iGamma); +MNG_EXT mng_retcode MNG_DECL mng_set_dfltimggammaint (mng_handle hHandle, + mng_uint32 iGamma); +#endif + +#ifndef MNG_SKIP_MAXCANVAS +/* Ultimate clipping size */ +/* used to limit extreme graphics from overloading the system */ +/* if a graphic exceeds these limits a warning is issued, which can + be ignored by the app (using the errorproc callback). in that case + the library will use these settings to clip the input graphic, and + the app's canvas must account for this */ +MNG_EXT mng_retcode MNG_DECL mng_set_maxcanvaswidth (mng_handle hHandle, + mng_uint32 iMaxwidth); +MNG_EXT mng_retcode MNG_DECL mng_set_maxcanvasheight (mng_handle hHandle, + mng_uint32 iMaxheight); +MNG_EXT mng_retcode MNG_DECL mng_set_maxcanvassize (mng_handle hHandle, + mng_uint32 iMaxwidth, + mng_uint32 iMaxheight); +#endif + +/* ZLIB default compression parameters */ +/* these are used when writing out chunks */ +/* they are also used when compressing PNG image-data or JNG alpha-data; + in this case you can set them just before calling mng_putimgdata_ihdr */ +/* set to your liking; usually the defaults will suffice though! */ +/* check the documentation for ZLIB for details on these parameters */ +#ifdef MNG_INCLUDE_ZLIB +MNG_EXT mng_retcode MNG_DECL mng_set_zlib_level (mng_handle hHandle, + mng_int32 iZlevel); +MNG_EXT mng_retcode MNG_DECL mng_set_zlib_method (mng_handle hHandle, + mng_int32 iZmethod); +MNG_EXT mng_retcode MNG_DECL mng_set_zlib_windowbits (mng_handle hHandle, + mng_int32 iZwindowbits); +MNG_EXT mng_retcode MNG_DECL mng_set_zlib_memlevel (mng_handle hHandle, + mng_int32 iZmemlevel); +MNG_EXT mng_retcode MNG_DECL mng_set_zlib_strategy (mng_handle hHandle, + mng_int32 iZstrategy); + +MNG_EXT mng_retcode MNG_DECL mng_set_zlib_maxidat (mng_handle hHandle, + mng_uint32 iMaxIDAT); +#endif /* MNG_INCLUDE_ZLIB */ + +/* JNG default compression parameters (based on IJG code) */ +/* these are used when compressing JNG image-data; so you can set them + just before calling mng_putimgdata_jhdr */ +/* set to your liking; usually the defaults will suffice though! */ +/* check the documentation for IJGSRC6B for details on these parameters */ +#ifdef MNG_INCLUDE_JNG +#ifdef MNG_INCLUDE_IJG6B +MNG_EXT mng_retcode MNG_DECL mng_set_jpeg_dctmethod (mng_handle hHandle, + mngjpeg_dctmethod eJPEGdctmethod); +#endif +MNG_EXT mng_retcode MNG_DECL mng_set_jpeg_quality (mng_handle hHandle, + mng_int32 iJPEGquality); +MNG_EXT mng_retcode MNG_DECL mng_set_jpeg_smoothing (mng_handle hHandle, + mng_int32 iJPEGsmoothing); +MNG_EXT mng_retcode MNG_DECL mng_set_jpeg_progressive(mng_handle hHandle, + mng_bool bJPEGprogressive); +MNG_EXT mng_retcode MNG_DECL mng_set_jpeg_optimized (mng_handle hHandle, + mng_bool bJPEGoptimized); + +MNG_EXT mng_retcode MNG_DECL mng_set_jpeg_maxjdat (mng_handle hHandle, + mng_uint32 iMaxJDAT); +#endif /* MNG_INCLUDE_JNG */ + +/* Suspension-mode setting */ +/* use this to activate the internal suspension-buffer to improve + read-suspension processing */ +/* TODO: write-suspension ??? */ +#if defined(MNG_SUPPORT_READ) +MNG_EXT mng_retcode MNG_DECL mng_set_suspensionmode (mng_handle hHandle, + mng_bool bSuspensionmode); +#endif + +/* Speed setting */ +/* use this to influence the display-speed of animations */ +#if defined(MNG_SUPPORT_DISPLAY) +MNG_EXT mng_retcode MNG_DECL mng_set_speed (mng_handle hHandle, + mng_speedtype iSpeed); +#endif + +/* ************************************************************************** */ +/* * * */ +/* * Property get functions * */ +/* * * */ +/* ************************************************************************** */ + +/* see _set_ */ +MNG_EXT mng_ptr MNG_DECL mng_get_userdata (mng_handle hHandle); + +/* Network Graphic header details */ +/* these get filled once the graphics header is processed, + so they are available in the processheader callback; before that + they are zeroed out and imagetype is set to it_unknown */ +/* this might be a good point for the app to initialize the drawing-canvas! */ +/* note that some fields are only set for the first(!) header-chunk: + MNG/MHDR (imagetype = mng_it_mng) - ticks thru simplicity + PNG/IHDR (imagetype = mng_it_png) - bitdepth thru interlace + JNG/JHDR (imagetype = mng_it_jng) - bitdepth thru compression & + interlace thru alphainterlace */ +MNG_EXT mng_imgtype MNG_DECL mng_get_sigtype (mng_handle hHandle); +MNG_EXT mng_imgtype MNG_DECL mng_get_imagetype (mng_handle hHandle); +MNG_EXT mng_uint32 MNG_DECL mng_get_imagewidth (mng_handle hHandle); +MNG_EXT mng_uint32 MNG_DECL mng_get_imageheight (mng_handle hHandle); + +MNG_EXT mng_uint32 MNG_DECL mng_get_ticks (mng_handle hHandle); +MNG_EXT mng_uint32 MNG_DECL mng_get_framecount (mng_handle hHandle); +MNG_EXT mng_uint32 MNG_DECL mng_get_layercount (mng_handle hHandle); +MNG_EXT mng_uint32 MNG_DECL mng_get_playtime (mng_handle hHandle); +MNG_EXT mng_uint32 MNG_DECL mng_get_simplicity (mng_handle hHandle); + +MNG_EXT mng_uint8 MNG_DECL mng_get_bitdepth (mng_handle hHandle); +MNG_EXT mng_uint8 MNG_DECL mng_get_colortype (mng_handle hHandle); +MNG_EXT mng_uint8 MNG_DECL mng_get_compression (mng_handle hHandle); +MNG_EXT mng_uint8 MNG_DECL mng_get_filter (mng_handle hHandle); +MNG_EXT mng_uint8 MNG_DECL mng_get_interlace (mng_handle hHandle); +MNG_EXT mng_uint8 MNG_DECL mng_get_alphabitdepth (mng_handle hHandle); +MNG_EXT mng_uint8 MNG_DECL mng_get_alphacompression(mng_handle hHandle); +MNG_EXT mng_uint8 MNG_DECL mng_get_alphafilter (mng_handle hHandle); +MNG_EXT mng_uint8 MNG_DECL mng_get_alphainterlace (mng_handle hHandle); + +/* indicates the predicted alpha-depth required to properly display the image */ +/* gets set once the graphics header is processed and is available in the + processheader callback for any type of input-image (PNG, JNG or MNG) */ +/* possible values are 0,1,2,4,8,16 + 0 = no transparency required + 1 = on/off transparency required (alpha-values are 0 or 2^bit_depth-1) + 2+ = semi-transparency required (values will be scaled to the bitdepth of the + canvasstyle supplied by the application) */ +MNG_EXT mng_uint8 MNG_DECL mng_get_alphadepth (mng_handle hHandle); + +/* defines whether a refresh() callback is called for an interlace pass (PNG) + or progressive scan (JNG) */ +/* returns the interlace pass number for PNG or a fabricated pass number for JNG; + returns 0 in all other cases */ +/* only useful if the image_type = mng_it_png or mng_it_jng and if the image + is actually interlaced (PNG) or progressive (JNG) */ +#ifdef MNG_SUPPORT_DISPLAY +MNG_EXT mng_uint8 MNG_DECL mng_get_refreshpass (mng_handle hHandle); +#endif + +/* see _set_ */ +MNG_EXT mng_uint32 MNG_DECL mng_get_canvasstyle (mng_handle hHandle); +MNG_EXT mng_uint32 MNG_DECL mng_get_bkgdstyle (mng_handle hHandle); + +/* see _set_ */ +MNG_EXT mng_retcode MNG_DECL mng_get_bgcolor (mng_handle hHandle, + mng_uint16* iRed, + mng_uint16* iGreen, + mng_uint16* iBlue); + +/* see _set_ */ +MNG_EXT mng_bool MNG_DECL mng_get_usebkgd (mng_handle hHandle); + +/* see _set_ */ +MNG_EXT mng_bool MNG_DECL mng_get_storechunks (mng_handle hHandle); + +/* see _set_ */ +MNG_EXT mng_bool MNG_DECL mng_get_sectionbreaks (mng_handle hHandle); + +/* see _set_ */ +MNG_EXT mng_bool MNG_DECL mng_get_cacheplayback (mng_handle hHandle); + +/* see _set_ */ +MNG_EXT mng_bool MNG_DECL mng_get_doprogressive (mng_handle hHandle); + +/* see _set_ */ +MNG_EXT mng_uint32 MNG_DECL mng_get_crcmode (mng_handle hHandle); + +/* see _set_ */ +#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_FULL_CMS) +MNG_EXT mng_bool MNG_DECL mng_get_srgb (mng_handle hHandle); +#endif + +/* see _set_ */ +MNG_EXT mng_float MNG_DECL mng_get_viewgamma (mng_handle hHandle); +MNG_EXT mng_float MNG_DECL mng_get_displaygamma (mng_handle hHandle); +MNG_EXT mng_float MNG_DECL mng_get_dfltimggamma (mng_handle hHandle); +MNG_EXT mng_uint32 MNG_DECL mng_get_viewgammaint (mng_handle hHandle); +MNG_EXT mng_uint32 MNG_DECL mng_get_displaygammaint (mng_handle hHandle); +MNG_EXT mng_uint32 MNG_DECL mng_get_dfltimggammaint (mng_handle hHandle); + +#ifndef MNG_SKIP_MAXCANVAS +/* see _set_ */ +MNG_EXT mng_uint32 MNG_DECL mng_get_maxcanvaswidth (mng_handle hHandle); +MNG_EXT mng_uint32 MNG_DECL mng_get_maxcanvasheight (mng_handle hHandle); +#endif + +/* see _set_ */ +#ifdef MNG_INCLUDE_ZLIB +MNG_EXT mng_int32 MNG_DECL mng_get_zlib_level (mng_handle hHandle); +MNG_EXT mng_int32 MNG_DECL mng_get_zlib_method (mng_handle hHandle); +MNG_EXT mng_int32 MNG_DECL mng_get_zlib_windowbits (mng_handle hHandle); +MNG_EXT mng_int32 MNG_DECL mng_get_zlib_memlevel (mng_handle hHandle); +MNG_EXT mng_int32 MNG_DECL mng_get_zlib_strategy (mng_handle hHandle); + +MNG_EXT mng_uint32 MNG_DECL mng_get_zlib_maxidat (mng_handle hHandle); +#endif /* MNG_INCLUDE_ZLIB */ + +/* see _set_ */ +#ifdef MNG_INCLUDE_JNG +#ifdef MNG_INCLUDE_IJG6B +MNG_EXT mngjpeg_dctmethod + MNG_DECL mng_get_jpeg_dctmethod (mng_handle hHandle); +#endif +MNG_EXT mng_int32 MNG_DECL mng_get_jpeg_quality (mng_handle hHandle); +MNG_EXT mng_int32 MNG_DECL mng_get_jpeg_smoothing (mng_handle hHandle); +MNG_EXT mng_bool MNG_DECL mng_get_jpeg_progressive(mng_handle hHandle); +MNG_EXT mng_bool MNG_DECL mng_get_jpeg_optimized (mng_handle hHandle); + +MNG_EXT mng_uint32 MNG_DECL mng_get_jpeg_maxjdat (mng_handle hHandle); +#endif /* MNG_INCLUDE_JNG */ + +/* see _set_ */ +#if defined(MNG_SUPPORT_READ) +MNG_EXT mng_bool MNG_DECL mng_get_suspensionmode (mng_handle hHandle); +#endif + +/* see _set_ */ +#if defined(MNG_SUPPORT_DISPLAY) +MNG_EXT mng_speedtype + MNG_DECL mng_get_speed (mng_handle hHandle); +#endif + +/* Image-level */ +/* this can be used inside the processtext callback to determine the level of + text of the image being processed; the value 1 is returned for top-level + texts, and the value 2 for a text inside an embedded image inside a MNG */ +MNG_EXT mng_uint32 MNG_DECL mng_get_imagelevel (mng_handle hHandle); + +/* BACK info */ +/* can be used to retrieve the color & mandatory values for the last processed + BACK chunk of a MNG (will fail for other image-types); + if no BACK chunk was processed yet, it will return all zeroes */ +#ifdef MNG_SUPPORT_DISPLAY +MNG_EXT mng_retcode MNG_DECL mng_get_lastbackchunk (mng_handle hHandle, + mng_uint16* iRed, + mng_uint16* iGreen, + mng_uint16* iBlue, + mng_uint8* iMandatory); +#endif + +/* SEEK info */ +/* can be used to retrieve the segmentname of the last processed SEEK chunk; + if no SEEK chunk was processed or its segmentname was empty, the function + will return an empty string; the provided buffer must be at least 80 bytes!! */ +#ifdef MNG_SUPPORT_DISPLAY +MNG_EXT mng_retcode MNG_DECL mng_get_lastseekname (mng_handle hHandle, + mng_pchar zSegmentname); +#endif + +/* FRAM info */ +/* can be used to retrieve the current FRAM delay; this may be useful when + retrieving a stream of frames with their corresponding delays by "fake" + reading and displaying the file */ +#ifdef MNG_SUPPORT_DISPLAY +MNG_EXT mng_uint32 MNG_DECL mng_get_currframdelay (mng_handle hHandle); +#endif + +/* Display status variables */ +/* these get filled & updated during display processing */ +/* starttime is the tickcount at the start of displaying the animation */ +/* runtime is the actual number of millisecs since the start of the animation */ +/* currentframe, currentlayer & currentplaytime indicate the current + frame/layer/playtime(msecs) in the animation (these keep increasing; + even after the animation loops back to the TERM chunk) */ +/* totalframes, totallayers & totalplaytime are filled after a complete run + of an animation (eg. at MEND); they are also valid after just reading the MNG */ +#ifdef MNG_SUPPORT_DISPLAY +MNG_EXT mng_uint32 MNG_DECL mng_get_starttime (mng_handle hHandle); +MNG_EXT mng_uint32 MNG_DECL mng_get_runtime (mng_handle hHandle); +#ifndef MNG_NO_CURRENT_INFO +MNG_EXT mng_uint32 MNG_DECL mng_get_currentframe (mng_handle hHandle); +MNG_EXT mng_uint32 MNG_DECL mng_get_currentlayer (mng_handle hHandle); +MNG_EXT mng_uint32 MNG_DECL mng_get_currentplaytime (mng_handle hHandle); +MNG_EXT mng_uint32 MNG_DECL mng_get_totalframes (mng_handle hHandle); +MNG_EXT mng_uint32 MNG_DECL mng_get_totallayers (mng_handle hHandle); +MNG_EXT mng_uint32 MNG_DECL mng_get_totalplaytime (mng_handle hHandle); +#endif +#endif + +/* Status variables */ +/* these indicate the internal state of the library */ +/* most indicate exactly what you would expect - + status_error: true if the last function call returned an errorcode + status_reading: true if the library is (still) reading an image + status_suspendbreak: true if the library has suspended for "I/O" + status_creating: true if the library is in the middle of creating an image + status_writing: true if the library is in the middle of writing an image + status_displaying: true if the library is displaying an image + status_running: true if display processing is active (eg. not frozen or reset) + status_timerbreak: true if the library has suspended for a "timer-break" + status_dynamic: true if the library encountered an evNT chunk in the MNG + status_runningevent: true if the library is processing an external event */ +/* eg. mng_readdisplay() will turn the reading, displaying and running status on; + when EOF is reached the reading status will be turned off */ +MNG_EXT mng_bool MNG_DECL mng_status_error (mng_handle hHandle); +#ifdef MNG_SUPPORT_READ +MNG_EXT mng_bool MNG_DECL mng_status_reading (mng_handle hHandle); +MNG_EXT mng_bool MNG_DECL mng_status_suspendbreak (mng_handle hHandle); +#endif +#ifdef MNG_SUPPORT_WRITE +MNG_EXT mng_bool MNG_DECL mng_status_creating (mng_handle hHandle); +MNG_EXT mng_bool MNG_DECL mng_status_writing (mng_handle hHandle); +#endif +#ifdef MNG_SUPPORT_DISPLAY +MNG_EXT mng_bool MNG_DECL mng_status_displaying (mng_handle hHandle); +MNG_EXT mng_bool MNG_DECL mng_status_running (mng_handle hHandle); +MNG_EXT mng_bool MNG_DECL mng_status_timerbreak (mng_handle hHandle); +#endif +#ifdef MNG_SUPPORT_DYNAMICMNG +MNG_EXT mng_bool MNG_DECL mng_status_dynamic (mng_handle hHandle); +MNG_EXT mng_bool MNG_DECL mng_status_runningevent (mng_handle hHandle); +#endif + +/* ************************************************************************** */ +/* * * */ +/* * Chunk access functions * */ +/* * * */ +/* ************************************************************************** */ + +#ifdef MNG_ACCESS_CHUNKS + +/* ************************************************************************** */ + +/* use this to iterate the stored chunks */ +/* requires MNG_ACCESS_CHUNKS & MNG_STORE_CHUNKS */ +/* starts from the supplied chunk-index-nr; the first chunk has index 0!! */ +MNG_EXT mng_retcode MNG_DECL mng_iterate_chunks (mng_handle hHandle, + mng_uint32 iChunkseq, + mng_iteratechunk fProc); + +/* use the next function inside your 'iteratechunk' callback to copy + the given chunk to a new mng you are creating */ +/* the 'out' handle should be in 'create' status! */ +#ifdef MNG_SUPPORT_WRITE +MNG_EXT mng_retcode MNG_DECL mng_copy_chunk (mng_handle hHandle, + mng_handle hChunk, + mng_handle hHandleOut); +#endif + +/* ************************************************************************** */ + +/* use these to get chunk data from within the callback in iterate_chunks */ +MNG_EXT mng_retcode MNG_DECL mng_getchunk_ihdr (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iWidth, + mng_uint32 *iHeight, + mng_uint8 *iBitdepth, + mng_uint8 *iColortype, + mng_uint8 *iCompression, + mng_uint8 *iFilter, + mng_uint8 *iInterlace); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_plte (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iCount, + mng_palette8 *aPalette); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_idat (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iRawlen, + mng_ptr *pRawdata); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_trns (mng_handle hHandle, + mng_handle hChunk, + mng_bool *bEmpty, + mng_bool *bGlobal, + mng_uint8 *iType, + mng_uint32 *iCount, + mng_uint8arr *aAlphas, + mng_uint16 *iGray, + mng_uint16 *iRed, + mng_uint16 *iGreen, + mng_uint16 *iBlue, + mng_uint32 *iRawlen, + mng_uint8arr *aRawdata); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_gama (mng_handle hHandle, + mng_handle hChunk, + mng_bool *bEmpty, + mng_uint32 *iGamma); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_chrm (mng_handle hHandle, + mng_handle hChunk, + mng_bool *bEmpty, + mng_uint32 *iWhitepointx, + mng_uint32 *iWhitepointy, + mng_uint32 *iRedx, + mng_uint32 *iRedy, + mng_uint32 *iGreenx, + mng_uint32 *iGreeny, + mng_uint32 *iBluex, + mng_uint32 *iBluey); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_srgb (mng_handle hHandle, + mng_handle hChunk, + mng_bool *bEmpty, + mng_uint8 *iRenderingintent); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_iccp (mng_handle hHandle, + mng_handle hChunk, + mng_bool *bEmpty, + mng_uint32 *iNamesize, + mng_pchar *zName, + mng_uint8 *iCompression, + mng_uint32 *iProfilesize, + mng_ptr *pProfile); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_text (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iKeywordsize, + mng_pchar *zKeyword, + mng_uint32 *iTextsize, + mng_pchar *zText); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_ztxt (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iKeywordsize, + mng_pchar *zKeyword, + mng_uint8 *iCompression, + mng_uint32 *iTextsize, + mng_pchar *zText); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_itxt (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iKeywordsize, + mng_pchar *zKeyword, + mng_uint8 *iCompressionflag, + mng_uint8 *iCompressionmethod, + mng_uint32 *iLanguagesize, + mng_pchar *zLanguage, + mng_uint32 *iTranslationsize, + mng_pchar *zTranslation, + mng_uint32 *iTextsize, + mng_pchar *zText); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_bkgd (mng_handle hHandle, + mng_handle hChunk, + mng_bool *bEmpty, + mng_uint8 *iType, + mng_uint8 *iIndex, + mng_uint16 *iGray, + mng_uint16 *iRed, + mng_uint16 *iGreen, + mng_uint16 *iBlue); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_phys (mng_handle hHandle, + mng_handle hChunk, + mng_bool *bEmpty, + mng_uint32 *iSizex, + mng_uint32 *iSizey, + mng_uint8 *iUnit); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_sbit (mng_handle hHandle, + mng_handle hChunk, + mng_bool *bEmpty, + mng_uint8 *iType, + mng_uint8arr4 *aBits); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_splt (mng_handle hHandle, + mng_handle hChunk, + mng_bool *bEmpty, + mng_uint32 *iNamesize, + mng_pchar *zName, + mng_uint8 *iSampledepth, + mng_uint32 *iEntrycount, + mng_ptr *pEntries); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_hist (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iEntrycount, + mng_uint16arr *aEntries); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_time (mng_handle hHandle, + mng_handle hChunk, + mng_uint16 *iYear, + mng_uint8 *iMonth, + mng_uint8 *iDay, + mng_uint8 *iHour, + mng_uint8 *iMinute, + mng_uint8 *iSecond); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_mhdr (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iWidth, + mng_uint32 *iHeight, + mng_uint32 *iTicks, + mng_uint32 *iLayercount, + mng_uint32 *iFramecount, + mng_uint32 *iPlaytime, + mng_uint32 *iSimplicity); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_loop (mng_handle hHandle, + mng_handle hChunk, + mng_uint8 *iLevel, + mng_uint32 *iRepeat, + mng_uint8 *iTermination, + mng_uint32 *iItermin, + mng_uint32 *iItermax, + mng_uint32 *iCount, + mng_uint32p *pSignals); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_endl (mng_handle hHandle, + mng_handle hChunk, + mng_uint8 *iLevel); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_defi (mng_handle hHandle, + mng_handle hChunk, + mng_uint16 *iObjectid, + mng_uint8 *iDonotshow, + mng_uint8 *iConcrete, + mng_bool *bHasloca, + mng_int32 *iXlocation, + mng_int32 *iYlocation, + mng_bool *bHasclip, + mng_int32 *iLeftcb, + mng_int32 *iRightcb, + mng_int32 *iTopcb, + mng_int32 *iBottomcb); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_basi (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iWidth, + mng_uint32 *iHeight, + mng_uint8 *iBitdepth, + mng_uint8 *iColortype, + mng_uint8 *iCompression, + mng_uint8 *iFilter, + mng_uint8 *iInterlace, + mng_uint16 *iRed, + mng_uint16 *iGreen, + mng_uint16 *iBlue, + mng_uint16 *iAlpha, + mng_uint8 *iViewable); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_clon (mng_handle hHandle, + mng_handle hChunk, + mng_uint16 *iSourceid, + mng_uint16 *iCloneid, + mng_uint8 *iClonetype, + mng_uint8 *iDonotshow, + mng_uint8 *iConcrete, + mng_bool *bHasloca, + mng_uint8 *iLocationtype, + mng_int32 *iLocationx, + mng_int32 *iLocationy); + +#ifndef MNG_SKIPCHUNK_PAST +MNG_EXT mng_retcode MNG_DECL mng_getchunk_past (mng_handle hHandle, + mng_handle hChunk, + mng_uint16 *iDestid, + mng_uint8 *iTargettype, + mng_int32 *iTargetx, + mng_int32 *iTargety, + mng_uint32 *iCount); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_past_src (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 iEntry, + mng_uint16 *iSourceid, + mng_uint8 *iComposition, + mng_uint8 *iOrientation, + mng_uint8 *iOffsettype, + mng_int32 *iOffsetx, + mng_int32 *iOffsety, + mng_uint8 *iBoundarytype, + mng_int32 *iBoundaryl, + mng_int32 *iBoundaryr, + mng_int32 *iBoundaryt, + mng_int32 *iBoundaryb); +#endif + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_disc (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iCount, + mng_uint16p *pObjectids); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_back (mng_handle hHandle, + mng_handle hChunk, + mng_uint16 *iRed, + mng_uint16 *iGreen, + mng_uint16 *iBlue, + mng_uint8 *iMandatory, + mng_uint16 *iImageid, + mng_uint8 *iTile); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_fram (mng_handle hHandle, + mng_handle hChunk, + mng_bool *bEmpty, + mng_uint8 *iMode, + mng_uint32 *iNamesize, + mng_pchar *zName, + mng_uint8 *iChangedelay, + mng_uint8 *iChangetimeout, + mng_uint8 *iChangeclipping, + mng_uint8 *iChangesyncid, + mng_uint32 *iDelay, + mng_uint32 *iTimeout, + mng_uint8 *iBoundarytype, + mng_int32 *iBoundaryl, + mng_int32 *iBoundaryr, + mng_int32 *iBoundaryt, + mng_int32 *iBoundaryb, + mng_uint32 *iCount, + mng_uint32p *pSyncids); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_move (mng_handle hHandle, + mng_handle hChunk, + mng_uint16 *iFirstid, + mng_uint16 *iLastid, + mng_uint8 *iMovetype, + mng_int32 *iMovex, + mng_int32 *iMovey); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_clip (mng_handle hHandle, + mng_handle hChunk, + mng_uint16 *iFirstid, + mng_uint16 *iLastid, + mng_uint8 *iCliptype, + mng_int32 *iClipl, + mng_int32 *iClipr, + mng_int32 *iClipt, + mng_int32 *iClipb); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_show (mng_handle hHandle, + mng_handle hChunk, + mng_bool *bEmpty, + mng_uint16 *iFirstid, + mng_uint16 *iLastid, + mng_uint8 *iMode); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_term (mng_handle hHandle, + mng_handle hChunk, + mng_uint8 *iTermaction, + mng_uint8 *iIteraction, + mng_uint32 *iDelay, + mng_uint32 *iItermax); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_save (mng_handle hHandle, + mng_handle hChunk, + mng_bool *bEmpty, + mng_uint8 *iOffsettype, + mng_uint32 *iCount); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_save_entry (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 iEntry, + mng_uint8 *iEntrytype, + mng_uint32arr2 *iOffset, + mng_uint32arr2 *iStarttime, + mng_uint32 *iLayernr, + mng_uint32 *iFramenr, + mng_uint32 *iNamesize, + mng_pchar *zName); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_seek (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iNamesize, + mng_pchar *zName); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_expi (mng_handle hHandle, + mng_handle hChunk, + mng_uint16 *iSnapshotid, + mng_uint32 *iNamesize, + mng_pchar *zName); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_fpri (mng_handle hHandle, + mng_handle hChunk, + mng_uint8 *iDeltatype, + mng_uint8 *iPriority); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_need (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iKeywordssize, + mng_pchar *zKeywords); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_phyg (mng_handle hHandle, + mng_handle hChunk, + mng_bool *bEmpty, + mng_uint32 *iSizex, + mng_uint32 *iSizey, + mng_uint8 *iUnit); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_jhdr (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iWidth, + mng_uint32 *iHeight, + mng_uint8 *iColortype, + mng_uint8 *iImagesampledepth, + mng_uint8 *iImagecompression, + mng_uint8 *iImageinterlace, + mng_uint8 *iAlphasampledepth, + mng_uint8 *iAlphacompression, + mng_uint8 *iAlphafilter, + mng_uint8 *iAlphainterlace); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_jdat (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iRawlen, + mng_ptr *pRawdata); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_jdaa (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iRawlen, + mng_ptr *pRawdata); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_dhdr (mng_handle hHandle, + mng_handle hChunk, + mng_uint16 *iObjectid, + mng_uint8 *iImagetype, + mng_uint8 *iDeltatype, + mng_uint32 *iBlockwidth, + mng_uint32 *iBlockheight, + mng_uint32 *iBlockx, + mng_uint32 *iBlocky); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_prom (mng_handle hHandle, + mng_handle hChunk, + mng_uint8 *iColortype, + mng_uint8 *iSampledepth, + mng_uint8 *iFilltype); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_pplt (mng_handle hHandle, + mng_handle hChunk, + mng_uint8 *iDeltatype, + mng_uint32 *iCount); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_pplt_entry (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 iEntry, + mng_uint16 *iRed, + mng_uint16 *iGreen, + mng_uint16 *iBlue, + mng_uint16 *iAlpha, + mng_bool *bUsed); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_drop (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iCount, + mng_chunkidp *pChunknames); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_dbyk (mng_handle hHandle, + mng_handle hChunk, + mng_chunkid *iChunkname, + mng_uint8 *iPolarity, + mng_uint32 *iKeywordssize, + mng_pchar *zKeywords); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_ordr (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iCount); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_ordr_entry (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 iEntry, + mng_chunkid *iChunkname, + mng_uint8 *iOrdertype); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_magn (mng_handle hHandle, + mng_handle hChunk, + mng_uint16 *iFirstid, + mng_uint16 *iLastid, + mng_uint16 *iMethodX, + mng_uint16 *iMX, + mng_uint16 *iMY, + mng_uint16 *iML, + mng_uint16 *iMR, + mng_uint16 *iMT, + mng_uint16 *iMB, + mng_uint16 *iMethodY); + +#ifdef MNG_INCLUDE_MPNG_PROPOSAL +MNG_EXT mng_retcode MNG_DECL mng_getchunk_mpng (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iFramewidth, + mng_uint32 *iFrameheight, + mng_uint16 *iNumplays, + mng_uint16 *iTickspersec, + mng_uint8 *iCompressionmethod, + mng_uint32 *iCount); +MNG_EXT mng_retcode MNG_DECL mng_getchunk_mpng_frame (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 iEntry, + mng_uint32 *iX, + mng_uint32 *iY, + mng_uint32 *iWidth, + mng_uint32 *iHeight, + mng_int32 *iXoffset, + mng_int32 *iYoffset, + mng_uint16 *iTicks); +#endif + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_evnt (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iCount); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_evnt_entry (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 iEntry, + mng_uint8 *iEventtype, + mng_uint8 *iMasktype, + mng_int32 *iLeft, + mng_int32 *iRight, + mng_int32 *iTop, + mng_int32 *iBottom, + mng_uint16 *iObjectid, + mng_uint8 *iIndex, + mng_uint32 *iSegmentnamesize, + mng_pchar *zSegmentname); + +MNG_EXT mng_retcode MNG_DECL mng_getchunk_unknown (mng_handle hHandle, + mng_handle hChunk, + mng_chunkid *iChunkname, + mng_uint32 *iRawlen, + mng_ptr *pRawdata); + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_WRITE_PROCS + +/* use these to create new chunks at the end of the chunk-list */ +/* requires at least MNG_ACCESS_CHUNKS (MNG_SUPPORT_WRITE may be nice too) */ +MNG_EXT mng_retcode MNG_DECL mng_putchunk_ihdr (mng_handle hHandle, + mng_uint32 iWidth, + mng_uint32 iHeight, + mng_uint8 iBitdepth, + mng_uint8 iColortype, + mng_uint8 iCompression, + mng_uint8 iFilter, + mng_uint8 iInterlace); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_plte (mng_handle hHandle, + mng_uint32 iCount, + mng_palette8 aPalette); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_idat (mng_handle hHandle, + mng_uint32 iRawlen, + mng_ptr pRawdata); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_iend (mng_handle hHandle); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_trns (mng_handle hHandle, + mng_bool bEmpty, + mng_bool bGlobal, + mng_uint8 iType, + mng_uint32 iCount, + mng_uint8arr aAlphas, + mng_uint16 iGray, + mng_uint16 iRed, + mng_uint16 iGreen, + mng_uint16 iBlue, + mng_uint32 iRawlen, + mng_uint8arr aRawdata); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_gama (mng_handle hHandle, + mng_bool bEmpty, + mng_uint32 iGamma); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_chrm (mng_handle hHandle, + mng_bool bEmpty, + mng_uint32 iWhitepointx, + mng_uint32 iWhitepointy, + mng_uint32 iRedx, + mng_uint32 iRedy, + mng_uint32 iGreenx, + mng_uint32 iGreeny, + mng_uint32 iBluex, + mng_uint32 iBluey); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_srgb (mng_handle hHandle, + mng_bool bEmpty, + mng_uint8 iRenderingintent); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_iccp (mng_handle hHandle, + mng_bool bEmpty, + mng_uint32 iNamesize, + mng_pchar zName, + mng_uint8 iCompression, + mng_uint32 iProfilesize, + mng_ptr pProfile); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_text (mng_handle hHandle, + mng_uint32 iKeywordsize, + mng_pchar zKeyword, + mng_uint32 iTextsize, + mng_pchar zText); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_ztxt (mng_handle hHandle, + mng_uint32 iKeywordsize, + mng_pchar zKeyword, + mng_uint8 iCompression, + mng_uint32 iTextsize, + mng_pchar zText); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_itxt (mng_handle hHandle, + mng_uint32 iKeywordsize, + mng_pchar zKeyword, + mng_uint8 iCompressionflag, + mng_uint8 iCompressionmethod, + mng_uint32 iLanguagesize, + mng_pchar zLanguage, + mng_uint32 iTranslationsize, + mng_pchar zTranslation, + mng_uint32 iTextsize, + mng_pchar zText); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_bkgd (mng_handle hHandle, + mng_bool bEmpty, + mng_uint8 iType, + mng_uint8 iIndex, + mng_uint16 iGray, + mng_uint16 iRed, + mng_uint16 iGreen, + mng_uint16 iBlue); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_phys (mng_handle hHandle, + mng_bool bEmpty, + mng_uint32 iSizex, + mng_uint32 iSizey, + mng_uint8 iUnit); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_sbit (mng_handle hHandle, + mng_bool bEmpty, + mng_uint8 iType, + mng_uint8arr4 aBits); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_splt (mng_handle hHandle, + mng_bool bEmpty, + mng_uint32 iNamesize, + mng_pchar zName, + mng_uint8 iSampledepth, + mng_uint32 iEntrycount, + mng_ptr pEntries); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_hist (mng_handle hHandle, + mng_uint32 iEntrycount, + mng_uint16arr aEntries); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_time (mng_handle hHandle, + mng_uint16 iYear, + mng_uint8 iMonth, + mng_uint8 iDay, + mng_uint8 iHour, + mng_uint8 iMinute, + mng_uint8 iSecond); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_mhdr (mng_handle hHandle, + mng_uint32 iWidth, + mng_uint32 iHeight, + mng_uint32 iTicks, + mng_uint32 iLayercount, + mng_uint32 iFramecount, + mng_uint32 iPlaytime, + mng_uint32 iSimplicity); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_mend (mng_handle hHandle); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_loop (mng_handle hHandle, + mng_uint8 iLevel, + mng_uint32 iRepeat, + mng_uint8 iTermination, + mng_uint32 iItermin, + mng_uint32 iItermax, + mng_uint32 iCount, + mng_uint32p pSignals); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_endl (mng_handle hHandle, + mng_uint8 iLevel); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_defi (mng_handle hHandle, + mng_uint16 iObjectid, + mng_uint8 iDonotshow, + mng_uint8 iConcrete, + mng_bool bHasloca, + mng_int32 iXlocation, + mng_int32 iYlocation, + mng_bool bHasclip, + mng_int32 iLeftcb, + mng_int32 iRightcb, + mng_int32 iTopcb, + mng_int32 iBottomcb); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_basi (mng_handle hHandle, + mng_uint32 iWidth, + mng_uint32 iHeight, + mng_uint8 iBitdepth, + mng_uint8 iColortype, + mng_uint8 iCompression, + mng_uint8 iFilter, + mng_uint8 iInterlace, + mng_uint16 iRed, + mng_uint16 iGreen, + mng_uint16 iBlue, + mng_uint16 iAlpha, + mng_uint8 iViewable); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_clon (mng_handle hHandle, + mng_uint16 iSourceid, + mng_uint16 iCloneid, + mng_uint8 iClonetype, + mng_uint8 iDonotshow, + mng_uint8 iConcrete, + mng_bool bHasloca, + mng_uint8 iLocationtype, + mng_int32 iLocationx, + mng_int32 iLocationy); + +#ifndef MNG_SKIPCHUNK_PAST +MNG_EXT mng_retcode MNG_DECL mng_putchunk_past (mng_handle hHandle, + mng_uint16 iDestid, + mng_uint8 iTargettype, + mng_int32 iTargetx, + mng_int32 iTargety, + mng_uint32 iCount); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_past_src (mng_handle hHandle, + mng_uint32 iEntry, + mng_uint16 iSourceid, + mng_uint8 iComposition, + mng_uint8 iOrientation, + mng_uint8 iOffsettype, + mng_int32 iOffsetx, + mng_int32 iOffsety, + mng_uint8 iBoundarytype, + mng_int32 iBoundaryl, + mng_int32 iBoundaryr, + mng_int32 iBoundaryt, + mng_int32 iBoundaryb); +#endif + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_disc (mng_handle hHandle, + mng_uint32 iCount, + mng_uint16p pObjectids); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_back (mng_handle hHandle, + mng_uint16 iRed, + mng_uint16 iGreen, + mng_uint16 iBlue, + mng_uint8 iMandatory, + mng_uint16 iImageid, + mng_uint8 iTile); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_fram (mng_handle hHandle, + mng_bool bEmpty, + mng_uint8 iMode, + mng_uint32 iNamesize, + mng_pchar zName, + mng_uint8 iChangedelay, + mng_uint8 iChangetimeout, + mng_uint8 iChangeclipping, + mng_uint8 iChangesyncid, + mng_uint32 iDelay, + mng_uint32 iTimeout, + mng_uint8 iBoundarytype, + mng_int32 iBoundaryl, + mng_int32 iBoundaryr, + mng_int32 iBoundaryt, + mng_int32 iBoundaryb, + mng_uint32 iCount, + mng_uint32p pSyncids); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_move (mng_handle hHandle, + mng_uint16 iFirstid, + mng_uint16 iLastid, + mng_uint8 iMovetype, + mng_int32 iMovex, + mng_int32 iMovey); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_clip (mng_handle hHandle, + mng_uint16 iFirstid, + mng_uint16 iLastid, + mng_uint8 iCliptype, + mng_int32 iClipl, + mng_int32 iClipr, + mng_int32 iClipt, + mng_int32 iClipb); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_show (mng_handle hHandle, + mng_bool bEmpty, + mng_uint16 iFirstid, + mng_uint16 iLastid, + mng_uint8 iMode); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_term (mng_handle hHandle, + mng_uint8 iTermaction, + mng_uint8 iIteraction, + mng_uint32 iDelay, + mng_uint32 iItermax); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_save (mng_handle hHandle, + mng_bool bEmpty, + mng_uint8 iOffsettype, + mng_uint32 iCount); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_save_entry (mng_handle hHandle, + mng_uint32 iEntry, + mng_uint8 iEntrytype, + mng_uint32arr2 iOffset, + mng_uint32arr2 iStarttime, + mng_uint32 iLayernr, + mng_uint32 iFramenr, + mng_uint32 iNamesize, + mng_pchar zName); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_seek (mng_handle hHandle, + mng_uint32 iNamesize, + mng_pchar zName); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_expi (mng_handle hHandle, + mng_uint16 iSnapshotid, + mng_uint32 iNamesize, + mng_pchar zName); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_fpri (mng_handle hHandle, + mng_uint8 iDeltatype, + mng_uint8 iPriority); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_need (mng_handle hHandle, + mng_uint32 iKeywordssize, + mng_pchar zKeywords); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_phyg (mng_handle hHandle, + mng_bool bEmpty, + mng_uint32 iSizex, + mng_uint32 iSizey, + mng_uint8 iUnit); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_jhdr (mng_handle hHandle, + mng_uint32 iWidth, + mng_uint32 iHeight, + mng_uint8 iColortype, + mng_uint8 iImagesampledepth, + mng_uint8 iImagecompression, + mng_uint8 iImageinterlace, + mng_uint8 iAlphasampledepth, + mng_uint8 iAlphacompression, + mng_uint8 iAlphafilter, + mng_uint8 iAlphainterlace); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_jdat (mng_handle hHandle, + mng_uint32 iRawlen, + mng_ptr pRawdata); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_jdaa (mng_handle hHandle, + mng_uint32 iRawlen, + mng_ptr pRawdata); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_jsep (mng_handle hHandle); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_dhdr (mng_handle hHandle, + mng_uint16 iObjectid, + mng_uint8 iImagetype, + mng_uint8 iDeltatype, + mng_uint32 iBlockwidth, + mng_uint32 iBlockheight, + mng_uint32 iBlockx, + mng_uint32 iBlocky); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_prom (mng_handle hHandle, + mng_uint8 iColortype, + mng_uint8 iSampledepth, + mng_uint8 iFilltype); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_ipng (mng_handle hHandle); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_pplt (mng_handle hHandle, + mng_uint8 iDeltatype, + mng_uint32 iCount); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_pplt_entry (mng_handle hHandle, + mng_uint32 iEntry, + mng_uint16 iRed, + mng_uint16 iGreen, + mng_uint16 iBlue, + mng_uint16 iAlpha); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_jpng (mng_handle hHandle); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_drop (mng_handle hHandle, + mng_uint32 iCount, + mng_chunkidp pChunknames); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_dbyk (mng_handle hHandle, + mng_chunkid iChunkname, + mng_uint8 iPolarity, + mng_uint32 iKeywordssize, + mng_pchar zKeywords); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_ordr (mng_handle hHandle, + mng_uint32 iCount); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_ordr_entry (mng_handle hHandle, + mng_uint32 iEntry, + mng_chunkid iChunkname, + mng_uint8 iOrdertype); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_magn (mng_handle hHandle, + mng_uint16 iFirstid, + mng_uint16 iLastid, + mng_uint16 iMethodX, + mng_uint16 iMX, + mng_uint16 iMY, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint16 iMT, + mng_uint16 iMB, + mng_uint16 iMethodY); + +#ifdef MNG_INCLUDE_MPNG_PROPOSAL +MNG_EXT mng_retcode MNG_DECL mng_putchunk_mpng (mng_handle hHandle, + mng_uint32 iFramewidth, + mng_uint32 iFrameheight, + mng_uint16 iNumplays, + mng_uint16 iTickspersec, + mng_uint8 iCompressionmethod, + mng_uint32 iCount); +MNG_EXT mng_retcode MNG_DECL mng_putchunk_mpng_frame (mng_handle hHandle, + mng_uint32 iEntry, + mng_uint32 iX, + mng_uint32 iY, + mng_uint32 iWidth, + mng_uint32 iHeight, + mng_int32 iXoffset, + mng_int32 iYoffset, + mng_uint16 iTicks); +#endif + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_evnt (mng_handle hHandle, + mng_uint32 iCount); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_evnt_entry (mng_handle hHandle, + mng_uint32 iEntry, + mng_uint8 iEventtype, + mng_uint8 iMasktype, + mng_int32 iLeft, + mng_int32 iRight, + mng_int32 iTop, + mng_int32 iBottom, + mng_uint16 iObjectid, + mng_uint8 iIndex, + mng_uint32 iSegmentnamesize, + mng_pchar zSegmentname); + +MNG_EXT mng_retcode MNG_DECL mng_putchunk_unknown (mng_handle hHandle, + mng_chunkid iChunkname, + mng_uint32 iRawlen, + mng_ptr pRawdata); + +#endif /* MNG_INCLUDE_WRITE_PROCS */ + +/* ************************************************************************** */ + +/* use these functions to access the actual image-data in stored chunks, + as opposed to the IDAT/JDAT data */ +/* to get accurate pixel-data the canvasstyle should seriously reflect the + bitdepth/colortype combination of the preceding IHDR/JHDR/BASI/DHDR; + all input can be converted to rgb(a)8 (rgb(a)16 for 16-bit images), but + there are only limited conversions back (see below for putimgdata) */ + +/* call this function if you want to extract the nth image from the list; + the first image is designated seqnr 0! */ +/* this function finds the IHDR/JHDR/BASI/DHDR with the appropriate seqnr, + starting from the beginning of the chunk-list; this may tend to get a little + slow for animations with a large number of chunks for images near the end */ +/* supplying a seqnr past the last image in the animation will return with + an errorcode */ +MNG_EXT mng_retcode MNG_DECL mng_getimgdata_seq (mng_handle hHandle, + mng_uint32 iSeqnr, + mng_uint32 iCanvasstyle, + mng_getcanvasline fGetcanvasline); + +/* both the following functions will search forward to find the first IDAT/JDAT, + and then traverse back to find the start of the image (IHDR,JHDR,DHDR,BASI); + note that this is very fast compared to decoding the IDAT/JDAT, so there's + not really a need for optimization; either can be called from the + iterate_chunks callback when a IHDR/JHDR is encountered; for BASI/DHDR there + may not be real image-data so it's wisest to keep iterating till the IEND, + and then call either of these functions if necessary (remember the correct seqnr!) */ + +/* call this function if you want to extract the image starting at or after the nth + position in the chunk-list; this number is returned in the iterate_chunks callback */ +MNG_EXT mng_retcode MNG_DECL mng_getimgdata_chunkseq (mng_handle hHandle, + mng_uint32 iSeqnr, + mng_uint32 iCanvasstyle, + mng_getcanvasline fGetcanvasline); + +/* call this function if you want to extract the image starting at or after the + indicated chunk; the handle of a chunk is returned in the iterate_chunks callback */ +MNG_EXT mng_retcode MNG_DECL mng_getimgdata_chunk (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 iCanvasstyle, + mng_getcanvasline fGetcanvasline); + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_WRITE_PROCS + +/* use the following functions to add image-data to the list of stored chunks */ +/* note that this only adds the IDAT or JDAT chunks and no others; you must call + one of these functions after you 'put' the initial chunks of the image and + before you 'put' the closing chunks */ +/* the canvasstyle should seriously reflect the bitdepth/colortype combination; + eg. bitdepth=16 would expect a 16-bit canvasstyle, + colortype=g or ga would expect a gray or gray+alpha style respectively + and so on, and so forth ... + (nb. the number of conversions will be extremely limited for the moment!) */ + +MNG_EXT mng_retcode MNG_DECL mng_putimgdata_ihdr (mng_handle hHandle, + mng_uint32 iWidth, + mng_uint32 iHeight, + mng_uint8 iColortype, + mng_uint8 iBitdepth, + mng_uint8 iCompression, + mng_uint8 iFilter, + mng_uint8 iInterlace, + mng_uint32 iCanvasstyle, + mng_getcanvasline fGetcanvasline); + +MNG_EXT mng_retcode MNG_DECL mng_putimgdata_jhdr (mng_handle hHandle, + mng_uint32 iWidth, + mng_uint32 iHeight, + mng_uint8 iColortype, + mng_uint8 iBitdepth, + mng_uint8 iCompression, + mng_uint8 iInterlace, + mng_uint8 iAlphaBitdepth, + mng_uint8 iAlphaCompression, + mng_uint8 iAlphaFilter, + mng_uint8 iAlphaInterlace, + mng_uint32 iCanvasstyle, + mng_getcanvasline fGetcanvasline); + +/* ************************************************************************** */ + +/* use the following functions to set the framecount/layercount/playtime or + simplicity of an animation you are creating; this may be useful if these + variables are calculated during the creation-process */ + +MNG_EXT mng_retcode MNG_DECL mng_updatemngheader (mng_handle hHandle, + mng_uint32 iFramecount, + mng_uint32 iLayercount, + mng_uint32 iPlaytime); + +MNG_EXT mng_retcode MNG_DECL mng_updatemngsimplicity (mng_handle hHandle, + mng_uint32 iSimplicity); + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_WRITE_PROCS */ + +#endif /* MNG_ACCESS_CHUNKS */ + +/* ************************************************************************** */ +/* * * */ +/* * Error-code structure * */ +/* * * */ +/* * 0b0000 00xx xxxx xxxx - basic errors; severity 9 (environment) * */ +/* * 0b0000 01xx xxxx xxxx - chunk errors; severity 9 (image induced) * */ +/* * 0b0000 10xx xxxx xxxx - severity 5 errors (application induced) * */ +/* * 0b0001 00xx xxxx xxxx - severity 2 warnings (recoverable) * */ +/* * 0b0010 00xx xxxx xxxx - severity 1 warnings (recoverable) * */ +/* * * */ +/* ************************************************************************** */ + +#define MNG_NOERROR (mng_retcode)0 /* er.. indicates all's well */ + +#define MNG_OUTOFMEMORY (mng_retcode)1 /* oops, buy some megabytes! */ +#define MNG_INVALIDHANDLE (mng_retcode)2 /* call mng_initialize first */ +#define MNG_NOCALLBACK (mng_retcode)3 /* set the callbacks please */ +#define MNG_UNEXPECTEDEOF (mng_retcode)4 /* what'd ya do with the data? */ +#define MNG_ZLIBERROR (mng_retcode)5 /* zlib burped */ +#define MNG_JPEGERROR (mng_retcode)6 /* jpglib complained */ +#define MNG_LCMSERROR (mng_retcode)7 /* little cms stressed out */ +#define MNG_NOOUTPUTPROFILE (mng_retcode)8 /* no output-profile defined */ +#define MNG_NOSRGBPROFILE (mng_retcode)9 /* no sRGB-profile defined */ +#define MNG_BUFOVERFLOW (mng_retcode)10 /* zlib output-buffer overflow */ +#define MNG_FUNCTIONINVALID (mng_retcode)11 /* ay, totally inappropriate */ +#define MNG_OUTPUTERROR (mng_retcode)12 /* disk full ? */ +#define MNG_JPEGBUFTOOSMALL (mng_retcode)13 /* can't handle buffer overflow*/ +#define MNG_NEEDMOREDATA (mng_retcode)14 /* I'm hungry, give me more */ +#define MNG_NEEDTIMERWAIT (mng_retcode)15 /* Sleep a while then wake me */ +#define MNG_NEEDSECTIONWAIT (mng_retcode)16 /* just processed a SEEK */ +#define MNG_LOOPWITHCACHEOFF (mng_retcode)17 /* LOOP when playback info off */ + +#define MNG_DLLNOTLOADED (mng_retcode)99 /* late binding failed */ + +#define MNG_APPIOERROR (mng_retcode)901 /* application I/O error */ +#define MNG_APPTIMERERROR (mng_retcode)902 /* application timing error */ +#define MNG_APPCMSERROR (mng_retcode)903 /* application CMS error */ +#define MNG_APPMISCERROR (mng_retcode)904 /* application other error */ +#define MNG_APPTRACEABORT (mng_retcode)905 /* application aborts on trace */ + +#define MNG_INTERNALERROR (mng_retcode)999 /* internal inconsistancy */ + +#define MNG_INVALIDSIG (mng_retcode)1025 /* invalid graphics file */ +#define MNG_INVALIDCRC (mng_retcode)1027 /* crc check failed */ +#define MNG_INVALIDLENGTH (mng_retcode)1028 /* chunklength mystifies me */ +#define MNG_SEQUENCEERROR (mng_retcode)1029 /* invalid chunk sequence */ +#define MNG_CHUNKNOTALLOWED (mng_retcode)1030 /* completely out-of-place */ +#define MNG_MULTIPLEERROR (mng_retcode)1031 /* only one occurence allowed */ +#define MNG_PLTEMISSING (mng_retcode)1032 /* indexed-color requires PLTE */ +#define MNG_IDATMISSING (mng_retcode)1033 /* IHDR-block requires IDAT */ +#define MNG_CANNOTBEEMPTY (mng_retcode)1034 /* must contain some data */ +#define MNG_GLOBALLENGTHERR (mng_retcode)1035 /* global data incorrect */ +#define MNG_INVALIDBITDEPTH (mng_retcode)1036 /* bitdepth out-of-range */ +#define MNG_INVALIDCOLORTYPE (mng_retcode)1037 /* colortype out-of-range */ +#define MNG_INVALIDCOMPRESS (mng_retcode)1038 /* compression method invalid */ +#define MNG_INVALIDFILTER (mng_retcode)1039 /* filter method invalid */ +#define MNG_INVALIDINTERLACE (mng_retcode)1040 /* interlace method invalid */ +#define MNG_NOTENOUGHIDAT (mng_retcode)1041 /* ran out of compressed data */ +#define MNG_PLTEINDEXERROR (mng_retcode)1042 /* palette-index out-of-range */ +#define MNG_NULLNOTFOUND (mng_retcode)1043 /* couldn't find null-separator*/ +#define MNG_KEYWORDNULL (mng_retcode)1044 /* keyword cannot be empty */ +#define MNG_OBJECTUNKNOWN (mng_retcode)1045 /* the object can't be found */ +#define MNG_OBJECTEXISTS (mng_retcode)1046 /* the object already exists */ +#define MNG_TOOMUCHIDAT (mng_retcode)1047 /* got too much compressed data*/ +#define MNG_INVSAMPLEDEPTH (mng_retcode)1048 /* sampledepth out-of-range */ +#define MNG_INVOFFSETSIZE (mng_retcode)1049 /* invalid offset-size */ +#define MNG_INVENTRYTYPE (mng_retcode)1050 /* invalid entry-type */ +#define MNG_ENDWITHNULL (mng_retcode)1051 /* may not end with NULL */ +#define MNG_INVIMAGETYPE (mng_retcode)1052 /* invalid image_type */ +#define MNG_INVDELTATYPE (mng_retcode)1053 /* invalid delta_type */ +#define MNG_INVALIDINDEX (mng_retcode)1054 /* index-value invalid */ +#define MNG_TOOMUCHJDAT (mng_retcode)1055 /* got too much compressed data*/ +#define MNG_JPEGPARMSERR (mng_retcode)1056 /* JHDR/JPEG parms do not match*/ +#define MNG_INVFILLMETHOD (mng_retcode)1057 /* invalid fill_method */ +#define MNG_OBJNOTCONCRETE (mng_retcode)1058 /* object must be concrete */ +#define MNG_TARGETNOALPHA (mng_retcode)1059 /* object has no alpha-channel */ +#define MNG_MNGTOOCOMPLEX (mng_retcode)1060 /* can't handle complexity */ +#define MNG_UNKNOWNCRITICAL (mng_retcode)1061 /* unknown critical chunk found*/ +#define MNG_UNSUPPORTEDNEED (mng_retcode)1062 /* nEED requirement unsupported*/ +#define MNG_INVALIDDELTA (mng_retcode)1063 /* Delta operation illegal */ +#define MNG_INVALIDMETHOD (mng_retcode)1064 /* invalid MAGN method */ +#define MNG_IMPROBABLELENGTH (mng_retcode)1065 /* impropable chunk length */ +#define MNG_INVALIDBLOCK (mng_retcode)1066 /* invalid delta block */ +#define MNG_INVALIDEVENT (mng_retcode)1067 /* invalid event_type */ +#define MNG_INVALIDMASK (mng_retcode)1068 /* invalid mask_type */ +#define MNG_NOMATCHINGLOOP (mng_retcode)1069 /* ENDL without matching LOOP */ +#define MNG_SEEKNOTFOUND (mng_retcode)1070 /* EvNT points to unknown SEEK */ +#define MNG_OBJNOTABSTRACT (mng_retcode)1071 /* object must be abstract */ +#define MNG_TERMSEQERROR (mng_retcode)1072 /* TERM in wrong place */ +#define MNG_INVALIDFIELDVAL (mng_retcode)1073 /* invalid fieldvalue (generic)*/ +#define MNG_INVALIDWIDTH (mng_retcode)1074 /* invalid frame/image width */ +#define MNG_INVALIDHEIGHT (mng_retcode)1075 /* invalid frame/image height */ + +#define MNG_INVALIDCNVSTYLE (mng_retcode)2049 /* can't make anything of this */ +#define MNG_WRONGCHUNK (mng_retcode)2050 /* accessing the wrong chunk */ +#define MNG_INVALIDENTRYIX (mng_retcode)2051 /* accessing the wrong entry */ +#define MNG_NOHEADER (mng_retcode)2052 /* must have had header first */ +#define MNG_NOCORRCHUNK (mng_retcode)2053 /* can't find parent chunk */ +#define MNG_NOMHDR (mng_retcode)2054 /* no MNG header available */ + +#define MNG_IMAGETOOLARGE (mng_retcode)4097 /* input-image way too big */ +#define MNG_NOTANANIMATION (mng_retcode)4098 /* file not a MNG */ +#define MNG_FRAMENRTOOHIGH (mng_retcode)4099 /* frame-nr out-of-range */ +#define MNG_LAYERNRTOOHIGH (mng_retcode)4100 /* layer-nr out-of-range */ +#define MNG_PLAYTIMETOOHIGH (mng_retcode)4101 /* playtime out-of-range */ +#define MNG_FNNOTIMPLEMENTED (mng_retcode)4102 /* function not yet available */ + +#define MNG_IMAGEFROZEN (mng_retcode)8193 /* stopped displaying */ + +#define MNG_LCMS_NOHANDLE 1 /* LCMS returned NULL handle */ +#define MNG_LCMS_NOMEM 2 /* LCMS returned NULL gammatab */ +#define MNG_LCMS_NOTRANS 3 /* LCMS returned NULL transform*/ + +/* ************************************************************************** */ +/* * * */ +/* * Canvas styles * */ +/* * * */ +/* * Note that the intentions are pretty darn good, but that the focus * */ +/* * is currently on 8-bit color support * */ +/* * * */ +/* * The RGB8_A8 style is defined for apps that require a separate * */ +/* * canvas for the color-planes and the alpha-plane (eg. mozilla) * */ +/* * This requires for the app to supply the "getalphaline" callback!!! * */ +/* * * */ +/* ************************************************************************** */ + +#define MNG_CANVAS_RGB8 0x00000000L +#define MNG_CANVAS_RGBA8 0x00001000L +#define MNG_CANVAS_RGBA8_PM 0x00009000L +#define MNG_CANVAS_ARGB8 0x00003000L +#define MNG_CANVAS_ARGB8_PM 0x0000B000L +#define MNG_CANVAS_RGB8_A8 0x00005000L +#define MNG_CANVAS_BGR8 0x00000001L +#define MNG_CANVAS_BGRX8 0x00010001L +#define MNG_CANVAS_BGRA8 0x00001001L +#define MNG_CANVAS_BGRA8PM 0x00009001L /* backward compatibility */ +#define MNG_CANVAS_BGRA8_PM 0x00009001L +#define MNG_CANVAS_ABGR8 0x00003001L +#define MNG_CANVAS_ABGR8_PM 0x0000B001L +#define MNG_CANVAS_RGB16 0x00000100L /* not supported yet */ +#define MNG_CANVAS_RGBA16 0x00001100L /* not supported yet */ +#define MNG_CANVAS_ARGB16 0x00003100L /* not supported yet */ +#define MNG_CANVAS_BGR16 0x00000101L /* not supported yet */ +#define MNG_CANVAS_BGRA16 0x00001101L /* not supported yet */ +#define MNG_CANVAS_ABGR16 0x00003101L /* not supported yet */ +#define MNG_CANVAS_GRAY8 0x00000002L /* not supported yet */ +#define MNG_CANVAS_GRAY16 0x00000102L /* not supported yet */ +#define MNG_CANVAS_GRAYA8 0x00001002L /* not supported yet */ +#define MNG_CANVAS_GRAYA16 0x00001102L /* not supported yet */ +#define MNG_CANVAS_AGRAY8 0x00003002L /* not supported yet */ +#define MNG_CANVAS_AGRAY16 0x00003102L /* not supported yet */ +#define MNG_CANVAS_DX15 0x00000003L /* not supported yet */ +#define MNG_CANVAS_DX16 0x00000004L /* not supported yet */ + +#define MNG_CANVAS_RGB565 0x00000005L +#define MNG_CANVAS_RGBA565 0x00001005L +#define MNG_CANVAS_BGR565 0x00000006L +#define MNG_CANVAS_BGRA565 0x00001006L +#define MNG_CANVAS_BGR565_A8 0x00004006L + +#define MNG_CANVAS_RGB555 0x00000007L +#define MNG_CANVAS_BGR555 0x00000008L + +#define MNG_CANVAS_PIXELTYPE(C) (C & 0x000000FFL) +#define MNG_CANVAS_BITDEPTH(C) (C & 0x00000100L) +#define MNG_CANVAS_HASALPHA(C) (C & 0x00001000L) +#define MNG_CANVAS_ALPHAFIRST(C) (C & 0x00002000L) +#define MNG_CANVAS_ALPHASEPD(C) (C & 0x00004000L) +#define MNG_CANVAS_ALPHAPM(C) (C & 0x00008000L) +#define MNG_CANVAS_HASFILLER(C) (C & 0x00010000L) + +#define MNG_CANVAS_RGB(C) (MNG_CANVAS_PIXELTYPE (C) == 0) +#define MNG_CANVAS_BGR(C) (MNG_CANVAS_PIXELTYPE (C) == 1) +#define MNG_CANVAS_GRAY(C) (MNG_CANVAS_PIXELTYPE (C) == 2) +#define MNG_CANVAS_DIRECTX15(C) (MNG_CANVAS_PIXELTYPE (C) == 3) +#define MNG_CANVAS_DIRECTX16(C) (MNG_CANVAS_PIXELTYPE (C) == 4) +#define MNG_CANVAS_RGB_565(C) (MNG_CANVAS_PIXELTYPE (C) == 5) +#define MNG_CANVAS_BGR_565(C) (MNG_CANVAS_PIXELTYPE (C) == 6) +#define MNG_CANVAS_8BIT(C) (!MNG_CANVAS_BITDEPTH (C)) +#define MNG_CANVAS_16BIT(C) (MNG_CANVAS_BITDEPTH (C)) +#define MNG_CANVAS_PIXELFIRST(C) (!MNG_CANVAS_ALPHAFIRST (C)) + +/* ************************************************************************** */ +/* * * */ +/* * Chunk names (idea adapted from libpng 1.1.0 - png.h) * */ +/* * * */ +/* ************************************************************************** */ + +#define MNG_UINT_HUH 0x40404040L + +#define MNG_UINT_BACK 0x4241434bL +#define MNG_UINT_BASI 0x42415349L +#define MNG_UINT_CLIP 0x434c4950L +#define MNG_UINT_CLON 0x434c4f4eL +#define MNG_UINT_DBYK 0x4442594bL +#define MNG_UINT_DEFI 0x44454649L +#define MNG_UINT_DHDR 0x44484452L +#define MNG_UINT_DISC 0x44495343L +#define MNG_UINT_DROP 0x44524f50L +#define MNG_UINT_ENDL 0x454e444cL +#define MNG_UINT_FRAM 0x4652414dL +#define MNG_UINT_IDAT 0x49444154L +#define MNG_UINT_IEND 0x49454e44L +#define MNG_UINT_IHDR 0x49484452L +#define MNG_UINT_IJNG 0x494a4e47L +#define MNG_UINT_IPNG 0x49504e47L +#define MNG_UINT_JDAA 0x4a444141L +#define MNG_UINT_JDAT 0x4a444154L +#define MNG_UINT_JHDR 0x4a484452L +#define MNG_UINT_JSEP 0x4a534550L +#define MNG_UINT_JdAA 0x4a644141L +#define MNG_UINT_LOOP 0x4c4f4f50L +#define MNG_UINT_MAGN 0x4d41474eL +#define MNG_UINT_MEND 0x4d454e44L +#define MNG_UINT_MHDR 0x4d484452L +#define MNG_UINT_MOVE 0x4d4f5645L +#define MNG_UINT_ORDR 0x4f524452L +#define MNG_UINT_PAST 0x50415354L +#define MNG_UINT_PLTE 0x504c5445L +#define MNG_UINT_PPLT 0x50504c54L +#define MNG_UINT_PROM 0x50524f4dL +#define MNG_UINT_SAVE 0x53415645L +#define MNG_UINT_SEEK 0x5345454bL +#define MNG_UINT_SHOW 0x53484f57L +#define MNG_UINT_TERM 0x5445524dL +#define MNG_UINT_adAT 0x61644154L +#define MNG_UINT_ahDR 0x61684452L +#define MNG_UINT_bKGD 0x624b4744L +#define MNG_UINT_cHRM 0x6348524dL +#define MNG_UINT_eXPI 0x65585049L +#define MNG_UINT_evNT 0x65764e54L +#define MNG_UINT_fPRI 0x66505249L +#define MNG_UINT_gAMA 0x67414d41L +#define MNG_UINT_hIST 0x68495354L +#define MNG_UINT_iCCP 0x69434350L +#define MNG_UINT_iTXt 0x69545874L +#define MNG_UINT_mpNG 0x6d704e47L +#define MNG_UINT_nEED 0x6e454544L +#define MNG_UINT_oFFs 0x6f464673L +#define MNG_UINT_pCAL 0x7043414cL +#define MNG_UINT_pHYg 0x70444167L +#define MNG_UINT_pHYs 0x70485973L +#define MNG_UINT_sBIT 0x73424954L +#define MNG_UINT_sCAL 0x7343414cL +#define MNG_UINT_sPLT 0x73504c54L +#define MNG_UINT_sRGB 0x73524742L +#define MNG_UINT_tEXt 0x74455874L +#define MNG_UINT_tIME 0x74494d45L +#define MNG_UINT_tRNS 0x74524e53L +#define MNG_UINT_zTXt 0x7a545874L + +/* ************************************************************************** */ +/* * * */ +/* * Chunk property values * */ +/* * * */ +/* ************************************************************************** */ + +#define MNG_BITDEPTH_1 1 /* IHDR, BASI, JHDR, PROM */ +#define MNG_BITDEPTH_2 2 +#define MNG_BITDEPTH_4 4 +#define MNG_BITDEPTH_8 8 /* sPLT */ +#define MNG_BITDEPTH_16 16 + +#define MNG_COLORTYPE_GRAY 0 /* IHDR, BASI, PROM */ +#define MNG_COLORTYPE_RGB 2 +#define MNG_COLORTYPE_INDEXED 3 +#define MNG_COLORTYPE_GRAYA 4 +#define MNG_COLORTYPE_RGBA 6 + +#define MNG_COMPRESSION_DEFLATE 0 /* IHDR, zTXt, iTXt, iCCP, + BASI, JHDR */ + +#define MNG_FILTER_ADAPTIVE 0 /* IHDR, BASI, JHDR */ +/* #define MNG_FILTER_NO_ADAPTIVE 1 +#define MNG_FILTER_NO_DIFFERING 0 +#define MNG_FILTER_DIFFERING 0x40 +#define MNG_FILTER_MASK (MNG_FILTER_NO_ADAPTIVE | MNG_FILTER_DIFFERING) */ +#ifdef FILTER192 +#define MNG_FILTER_DIFFERING 0xC0 +#endif +#ifdef FILTER193 +#define MNG_FILTER_NOFILTER 0xC1 +#endif + +#define MNG_INTERLACE_NONE 0 /* IHDR, BASI, JHDR */ +#define MNG_INTERLACE_ADAM7 1 + +#define MNG_FILTER_NONE 0 /* IDAT */ +#define MNG_FILTER_SUB 1 +#define MNG_FILTER_UP 2 +#define MNG_FILTER_AVERAGE 3 +#define MNG_FILTER_PAETH 4 + +#define MNG_INTENT_PERCEPTUAL 0 /* sRGB */ +#define MNG_INTENT_RELATIVECOLORIMETRIC 1 +#define MNG_INTENT_SATURATION 2 +#define MNG_INTENT_ABSOLUTECOLORIMETRIC 3 + /* tEXt, zTXt, iTXt */ +#define MNG_TEXT_TITLE "Title" +#define MNG_TEXT_AUTHOR "Author" +#define MNG_TEXT_DESCRIPTION "Description" +#define MNG_TEXT_COPYRIGHT "Copyright" +#define MNG_TEXT_CREATIONTIME "Creation Time" +#define MNG_TEXT_SOFTWARE "Software" +#define MNG_TEXT_DISCLAIMER "Disclaimer" +#define MNG_TEXT_WARNING "Warning" +#define MNG_TEXT_SOURCE "Source" +#define MNG_TEXT_COMMENT "Comment" + +#define MNG_FLAG_UNCOMPRESSED 0 /* iTXt */ +#define MNG_FLAG_COMPRESSED 1 + +#define MNG_UNIT_UNKNOWN 0 /* pHYs, pHYg */ +#define MNG_UNIT_METER 1 + /* MHDR */ +#define MNG_SIMPLICITY_VALID 0x00000001 +#define MNG_SIMPLICITY_SIMPLEFEATURES 0x00000002 +#define MNG_SIMPLICITY_COMPLEXFEATURES 0x00000004 +#define MNG_SIMPLICITY_TRANSPARENCY 0x00000008 +#define MNG_SIMPLICITY_JNG 0x00000010 +#define MNG_SIMPLICITY_DELTAPNG 0x00000020 + +#define MNG_TERMINATION_DECODER_NC 0 /* LOOP */ +#define MNG_TERMINATION_USER_NC 1 +#define MNG_TERMINATION_EXTERNAL_NC 2 +#define MNG_TERMINATION_DETERMINISTIC_NC 3 +#define MNG_TERMINATION_DECODER_C 4 +#define MNG_TERMINATION_USER_C 5 +#define MNG_TERMINATION_EXTERNAL_C 6 +#define MNG_TERMINATION_DETERMINISTIC_C 7 + +#define MNG_DONOTSHOW_VISIBLE 0 /* DEFI */ +#define MNG_DONOTSHOW_NOTVISIBLE 1 + +#define MNG_ABSTRACT 0 /* DEFI */ +#define MNG_CONCRETE 1 + +#define MNG_NOTVIEWABLE 0 /* BASI */ +#define MNG_VIEWABLE 1 + +#define MNG_FULL_CLONE 0 /* CLON */ +#define MNG_PARTIAL_CLONE 1 +#define MNG_RENUMBER 2 + +#define MNG_CONCRETE_ASPARENT 0 /* CLON */ +#define MNG_CONCRETE_MAKEABSTRACT 1 + +#define MNG_LOCATION_ABSOLUTE 0 /* CLON, MOVE */ +#define MNG_LOCATION_RELATIVE 1 + +#ifndef MNG_SKIPCHUNK_PAST +#define MNG_TARGET_ABSOLUTE 0 /* PAST */ +#define MNG_TARGET_RELATIVE_SAMEPAST 1 +#define MNG_TARGET_RELATIVE_PREVPAST 2 + +#define MNG_COMPOSITE_OVER 0 /* PAST */ +#define MNG_COMPOSITE_REPLACE 1 +#define MNG_COMPOSITE_UNDER 2 + +#define MNG_ORIENTATION_SAME 0 /* PAST */ +#define MNG_ORIENTATION_180DEG 2 +#define MNG_ORIENTATION_FLIPHORZ 4 +#define MNG_ORIENTATION_FLIPVERT 6 +#define MNG_ORIENTATION_TILED 8 + +#define MNG_OFFSET_ABSOLUTE 0 /* PAST */ +#define MNG_OFFSET_RELATIVE 1 +#endif + +#define MNG_BOUNDARY_ABSOLUTE 0 /* PAST, FRAM */ +#define MNG_BOUNDARY_RELATIVE 1 + +#define MNG_BACKGROUNDCOLOR_MANDATORY 0x01 /* BACK */ +#define MNG_BACKGROUNDIMAGE_MANDATORY 0x02 /* BACK */ + +#define MNG_BACKGROUNDIMAGE_NOTILE 0 /* BACK */ +#define MNG_BACKGROUNDIMAGE_TILE 1 + +#define MNG_FRAMINGMODE_NOCHANGE 0 /* FRAM */ +#define MNG_FRAMINGMODE_1 1 +#define MNG_FRAMINGMODE_2 2 +#define MNG_FRAMINGMODE_3 3 +#define MNG_FRAMINGMODE_4 4 + +#define MNG_CHANGEDELAY_NO 0 /* FRAM */ +#define MNG_CHANGEDELAY_NEXTSUBFRAME 1 +#define MNG_CHANGEDELAY_DEFAULT 2 + +#define MNG_CHANGETIMOUT_NO 0 /* FRAM */ +#define MNG_CHANGETIMOUT_DETERMINISTIC_1 1 +#define MNG_CHANGETIMOUT_DETERMINISTIC_2 2 +#define MNG_CHANGETIMOUT_DECODER_1 3 +#define MNG_CHANGETIMOUT_DECODER_2 4 +#define MNG_CHANGETIMOUT_USER_1 5 +#define MNG_CHANGETIMOUT_USER_2 6 +#define MNG_CHANGETIMOUT_EXTERNAL_1 7 +#define MNG_CHANGETIMOUT_EXTERNAL_2 8 + +#define MNG_CHANGECLIPPING_NO 0 /* FRAM */ +#define MNG_CHANGECLIPPING_NEXTSUBFRAME 1 +#define MNG_CHANGECLIPPING_DEFAULT 2 + +#define MNG_CHANGESYNCID_NO 0 /* FRAM */ +#define MNG_CHANGESYNCID_NEXTSUBFRAME 1 +#define MNG_CHANGESYNCID_DEFAULT 2 + +#define MNG_CLIPPING_ABSOLUTE 0 /* CLIP */ +#define MNG_CLIPPING_RELATIVE 1 + +#define MNG_SHOWMODE_0 0 /* SHOW */ +#define MNG_SHOWMODE_1 1 +#define MNG_SHOWMODE_2 2 +#define MNG_SHOWMODE_3 3 +#define MNG_SHOWMODE_4 4 +#define MNG_SHOWMODE_5 5 +#define MNG_SHOWMODE_6 6 +#define MNG_SHOWMODE_7 7 + +#define MNG_TERMACTION_LASTFRAME 0 /* TERM */ +#define MNG_TERMACTION_CLEAR 1 +#define MNG_TERMACTION_FIRSTFRAME 2 +#define MNG_TERMACTION_REPEAT 3 + +#define MNG_ITERACTION_LASTFRAME 0 /* TERM */ +#define MNG_ITERACTION_CLEAR 1 +#define MNG_ITERACTION_FIRSTFRAME 2 + +#define MNG_SAVEOFFSET_4BYTE 4 /* SAVE */ +#define MNG_SAVEOFFSET_8BYTE 8 + +#define MNG_SAVEENTRY_SEGMENTFULL 0 /* SAVE */ +#define MNG_SAVEENTRY_SEGMENT 1 +#define MNG_SAVEENTRY_SUBFRAME 2 +#define MNG_SAVEENTRY_EXPORTEDIMAGE 3 + +#define MNG_PRIORITY_ABSOLUTE 0 /* fPRI */ +#define MNG_PRIORITY_RELATIVE 1 + +#ifdef MNG_INCLUDE_JNG +#define MNG_COLORTYPE_JPEGGRAY 8 /* JHDR */ +#define MNG_COLORTYPE_JPEGCOLOR 10 +#define MNG_COLORTYPE_JPEGGRAYA 12 +#define MNG_COLORTYPE_JPEGCOLORA 14 + +#define MNG_BITDEPTH_JPEG8 8 /* JHDR */ +#define MNG_BITDEPTH_JPEG12 12 +#define MNG_BITDEPTH_JPEG8AND12 20 + +#define MNG_COMPRESSION_BASELINEJPEG 8 /* JHDR */ + +#define MNG_INTERLACE_SEQUENTIAL 0 /* JHDR */ +#define MNG_INTERLACE_PROGRESSIVE 8 +#endif /* MNG_INCLUDE_JNG */ + +#define MNG_IMAGETYPE_UNKNOWN 0 /* DHDR */ +#define MNG_IMAGETYPE_PNG 1 +#define MNG_IMAGETYPE_JNG 2 + +#define MNG_DELTATYPE_REPLACE 0 /* DHDR */ +#define MNG_DELTATYPE_BLOCKPIXELADD 1 +#define MNG_DELTATYPE_BLOCKALPHAADD 2 +#define MNG_DELTATYPE_BLOCKCOLORADD 3 +#define MNG_DELTATYPE_BLOCKPIXELREPLACE 4 +#define MNG_DELTATYPE_BLOCKALPHAREPLACE 5 +#define MNG_DELTATYPE_BLOCKCOLORREPLACE 6 +#define MNG_DELTATYPE_NOCHANGE 7 + +#define MNG_FILLMETHOD_LEFTBITREPLICATE 0 /* PROM */ +#define MNG_FILLMETHOD_ZEROFILL 1 + +#define MNG_DELTATYPE_REPLACERGB 0 /* PPLT */ +#define MNG_DELTATYPE_DELTARGB 1 +#define MNG_DELTATYPE_REPLACEALPHA 2 +#define MNG_DELTATYPE_DELTAALPHA 3 +#define MNG_DELTATYPE_REPLACERGBA 4 +#define MNG_DELTATYPE_DELTARGBA 5 + +#define MNG_POLARITY_ONLY 0 /* DBYK */ +#define MNG_POLARITY_ALLBUT 1 + +#define MNG_EVENT_NONE 0 /* evNT */ +#define MNG_EVENT_MOUSEENTER 1 +#define MNG_EVENT_MOUSEMOVE 2 +#define MNG_EVENT_MOUSEEXIT 3 +#define MNG_EVENT_MOUSEDOWN 4 +#define MNG_EVENT_MOUSEUP 5 + +#define MNG_MASK_NONE 0 /* evNT */ +#define MNG_MASK_BOX 1 +#define MNG_MASK_OBJECT 2 +#define MNG_MASK_OBJECTIX 3 +#define MNG_MASK_BOXOBJECT 4 +#define MNG_MASK_BOXOBJECTIX 5 + +/* ************************************************************************** */ +/* * * */ +/* * Processtext callback types * */ +/* * * */ +/* ************************************************************************** */ + +#define MNG_TYPE_TEXT 0 +#define MNG_TYPE_ZTXT 1 +#define MNG_TYPE_ITXT 2 + +/* ************************************************************************** */ +/* * * */ +/* * CRC processing masks * */ +/* * * */ +/* ************************************************************************** */ + +#define MNG_CRC_INPUT 0x0000000f +#define MNG_CRC_INPUT_NONE 0x00000000 +#define MNG_CRC_INPUT_PRESENT 0x00000001 +#define MNG_CRC_OUTPUT 0x000000f0 +#define MNG_CRC_OUTPUT_NONE 0x00000000 +#define MNG_CRC_OUTPUT_GENERATE 0x00000020 +#define MNG_CRC_OUTPUT_DUMMY 0x00000040 +#define MNG_CRC_ANCILLARY 0x00000f00 +#define MNG_CRC_ANCILLARY_IGNORE 0x00000000 +#define MNG_CRC_ANCILLARY_DISCARD 0x00000100 +#define MNG_CRC_ANCILLARY_WARNING 0x00000200 +#define MNG_CRC_ANCILLARY_ERROR 0x00000300 +#define MNG_CRC_CRITICAL 0x0000f000 +#define MNG_CRC_CRITICAL_IGNORE 0x00000000 +#define MNG_CRC_CRITICAL_WARNING 0x00002000 +#define MNG_CRC_CRITICAL_ERROR 0x00003000 +#define MNG_CRC_DEFAULT 0x00002121 + +/* ************************************************************************** */ + +#ifdef __cplusplus +} +#endif + +#endif /* _libmng_h_ */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ + diff --git a/Engine/lib/lmng/libmng_callback_xs.c b/Engine/lib/lmng/libmng_callback_xs.c new file mode 100644 index 000000000..ff1a22a70 --- /dev/null +++ b/Engine/lib/lmng/libmng_callback_xs.c @@ -0,0 +1,1239 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_callback_xs.c copyright (c) 2000-2004 G.Juyn * */ +/* * version : 1.0.9 * */ +/* * * */ +/* * purpose : callback get/set interface (implementation) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : implementation of the callback get/set functions * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - fixed calling convention * */ +/* * - changed strict-ANSI stuff * */ +/* * 0.5.1 - 05/12/2000 - G.Juyn * */ +/* * - changed trace to macro for callback error-reporting * */ +/* * * */ +/* * 0.5.2 - 05/31/2000 - G.Juyn * */ +/* * - fixed up punctuation (contribution by Tim Rowley) * */ +/* * 0.5.2 - 06/02/2000 - G.Juyn * */ +/* * - added getalphaline callback for RGB8_A8 canvasstyle * */ +/* * * */ +/* * 0.9.1 - 07/15/2000 - G.Juyn * */ +/* * - added callbacks for SAVE/SEEK processing * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 0.9.3 - 10/11/2000 - G.Juyn * */ +/* * - added support for nEED * */ +/* * 0.9.3 - 10/17/2000 - G.Juyn * */ +/* * - added callback to process non-critical unknown chunks * */ +/* * * */ +/* * 1.0.1 - 02/08/2001 - G.Juyn * */ +/* * - added MEND processing callback * */ +/* * * */ +/* * 1.0.2 - 06/23/2001 - G.Juyn * */ +/* * - added processterm callback * */ +/* * * */ +/* * 1.0.6 - 07/07/2003 - G. R-P * */ +/* * - added SKIPCHUNK feature * */ +/* * * */ +/* * 1.0.7 - 03/10/2004 - G.R-P * */ +/* * - added conditionals around openstream/closestream * */ +/* * 1.0.7 - 03/19/2004 - G.R-P * */ +/* * - fixed typo (MNG_SKIPCHUNK_SAVE -> MNG_SKIPCHUNK_nEED * */ +/* * * */ +/* * 1.0.8 - 04/10/2004 - G.Juyn * */ +/* * - added data-push mechanisms for specialized decoders * */ +/* * * */ +/* * 1.0.9 - 09/18/2004 - G.R-P. * */ +/* * - added two SKIPCHUNK_TERM conditionals * */ +/* * 1.0.9 - 12/20/2004 - G.Juyn * */ +/* * - cleaned up macro-invocations (thanks to D. Airlie) * */ +/* * * */ +/* ************************************************************************** */ + +#include "libmng.h" +#include "libmng_data.h" +#include "libmng_error.h" +#include "libmng_trace.h" +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +/* ************************************************************************** */ +/* * * */ +/* * Callback set functions * */ +/* * * */ +/* ************************************************************************** */ + +#ifndef MNG_INTERNAL_MEMMNGMT +mng_retcode MNG_DECL mng_setcb_memalloc (mng_handle hHandle, + mng_memalloc fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_MEMALLOC, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fMemalloc = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_MEMALLOC, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_INTERNAL_MEMMNGMT */ + +/* ************************************************************************** */ + +#ifndef MNG_INTERNAL_MEMMNGMT +mng_retcode MNG_DECL mng_setcb_memfree (mng_handle hHandle, + mng_memfree fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_MEMFREE, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fMemfree = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_MEMFREE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_INTERNAL_MEMMNGMT */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +mng_retcode MNG_DECL mng_setcb_releasedata (mng_handle hHandle, + mng_releasedata fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_RELEASEDATA, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fReleasedata = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_RELEASEDATA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE) +#ifndef MNG_NO_OPEN_CLOSE_STREAM +mng_retcode MNG_DECL mng_setcb_openstream (mng_handle hHandle, + mng_openstream fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_OPENSTREAM, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fOpenstream = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_OPENSTREAM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif /* MNG_SUPPORT_READ || MNG_SUPPORT_WRITE */ + +/* ************************************************************************** */ + +#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE) +#ifndef MNG_NO_OPEN_CLOSE_STREAM +mng_retcode MNG_DECL mng_setcb_closestream (mng_handle hHandle, + mng_closestream fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_CLOSESTREAM, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fClosestream = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_CLOSESTREAM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif /* MNG_SUPPORT_READ || MNG_SUPPORT_WRITE */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +mng_retcode MNG_DECL mng_setcb_readdata (mng_handle hHandle, + mng_readdata fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_READDATA, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fReaddata = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_READDATA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_WRITE +mng_retcode MNG_DECL mng_setcb_writedata (mng_handle hHandle, + mng_writedata fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_WRITEDATA, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fWritedata = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_WRITEDATA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_WRITE */ + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_setcb_errorproc (mng_handle hHandle, + mng_errorproc fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_ERRORPROC, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fErrorproc = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_ERRORPROC, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_TRACE +mng_retcode MNG_DECL mng_setcb_traceproc (mng_handle hHandle, + mng_traceproc fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_TRACEPROC, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fTraceproc = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_TRACEPROC, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_TRACE */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +mng_retcode MNG_DECL mng_setcb_processheader (mng_handle hHandle, + mng_processheader fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSHEADER, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fProcessheader = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSHEADER, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +#ifndef MNG_SKIPCHUNK_tEXt +mng_retcode MNG_DECL mng_setcb_processtext (mng_handle hHandle, + mng_processtext fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSTEXT, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fProcesstext = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSTEXT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif /* MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +#ifndef MNG_SKIPCHUNK_SAVE +mng_retcode MNG_DECL mng_setcb_processsave (mng_handle hHandle, + mng_processsave fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSSAVE, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fProcesssave = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSSAVE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif /* MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +#ifndef MNG_SKIPCHUNK_SEEK +mng_retcode MNG_DECL mng_setcb_processseek (mng_handle hHandle, + mng_processseek fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSSEEK, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fProcessseek = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSSEEK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif /* MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +#ifndef MNG_SKIPCHUNK_nEED +mng_retcode MNG_DECL mng_setcb_processneed (mng_handle hHandle, + mng_processneed fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSNEED, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fProcessneed = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSNEED, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif /* MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +mng_retcode MNG_DECL mng_setcb_processmend (mng_handle hHandle, + mng_processmend fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSMEND, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fProcessmend = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSMEND, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +mng_retcode MNG_DECL mng_setcb_processunknown (mng_handle hHandle, + mng_processunknown fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSUNKNOWN, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fProcessunknown = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSUNKNOWN, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +#ifndef MNG_SKIPCHUNK_TERM +mng_retcode MNG_DECL mng_setcb_processterm (mng_handle hHandle, + mng_processterm fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSTERM, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fProcessterm = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSTERM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif /* MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_retcode MNG_DECL mng_setcb_getcanvasline (mng_handle hHandle, + mng_getcanvasline fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_GETCANVASLINE, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fGetcanvasline = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_GETCANVASLINE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_retcode MNG_DECL mng_setcb_getbkgdline (mng_handle hHandle, + mng_getbkgdline fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_GETBKGDLINE, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fGetbkgdline = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_GETBKGDLINE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_retcode MNG_DECL mng_setcb_getalphaline (mng_handle hHandle, + mng_getalphaline fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_GETALPHALINE, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fGetalphaline = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_GETALPHALINE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_retcode MNG_DECL mng_setcb_refresh (mng_handle hHandle, + mng_refresh fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_REFRESH, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fRefresh = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_REFRESH, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_retcode MNG_DECL mng_setcb_gettickcount (mng_handle hHandle, + mng_gettickcount fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_GETTICKCOUNT, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fGettickcount = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_GETTICKCOUNT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_retcode MNG_DECL mng_setcb_settimer (mng_handle hHandle, + mng_settimer fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_SETTIMER, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fSettimer = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_SETTIMER, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_APP_CMS) +mng_retcode MNG_DECL mng_setcb_processgamma (mng_handle hHandle, + mng_processgamma fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSGAMA, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fProcessgamma = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSGAMA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_DISPLAY && MNG_APP_CMS */ + +/* ************************************************************************** */ + +#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_APP_CMS) +#ifndef MNG_SKIPCHUNK_cHRM +mng_retcode MNG_DECL mng_setcb_processchroma (mng_handle hHandle, + mng_processchroma fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSCHROMA, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fProcesschroma = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSCHROMA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif /* MNG_SUPPORT_DISPLAY && MNG_APP_CMS */ + +/* ************************************************************************** */ + +#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_APP_CMS) +mng_retcode MNG_DECL mng_setcb_processsrgb (mng_handle hHandle, + mng_processsrgb fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSSRGB, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fProcesssrgb = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSSRGB, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_DISPLAY && MNG_APP_CMS */ + +/* ************************************************************************** */ + +#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_APP_CMS) +#ifndef MNG_SKIPCHUNK_iCCP +mng_retcode MNG_DECL mng_setcb_processiccp (mng_handle hHandle, + mng_processiccp fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSICCP, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fProcessiccp = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSICCP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif /* MNG_SUPPORT_DISPLAY && MNG_APP_CMS */ + +/* ************************************************************************** */ + +#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_APP_CMS) +mng_retcode MNG_DECL mng_setcb_processarow (mng_handle hHandle, + mng_processarow fProc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSAROW, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->fProcessarow = fProc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SETCB_PROCESSAROW, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_DISPLAY && MNG_APP_CMS */ + +/* ************************************************************************** */ +/* * * */ +/* * Callback get functions * */ +/* * * */ +/* ************************************************************************** */ + +#ifndef MNG_INTERNAL_MEMMNGMT +mng_memalloc MNG_DECL mng_getcb_memalloc (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_MEMALLOC, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_MEMALLOC, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fMemalloc; +} +#endif /* MNG_INTERNAL_MEMMNGMT */ + +/* ************************************************************************** */ + +#ifndef MNG_INTERNAL_MEMMNGMT +mng_memfree MNG_DECL mng_getcb_memfree (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_MEMFREE, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_MEMFREE, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fMemfree; +} +#endif /* MNG_INTERNAL_MEMMNGMT */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +mng_releasedata MNG_DECL mng_getcb_releasedata (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_RELEASEDATA, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_RELEASEDATA, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fReleasedata; +} +#endif /* MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +mng_readdata MNG_DECL mng_getcb_readdata (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_READDATA, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_READDATA, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fReaddata; +} +#endif /* MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE) +#ifndef MNG_NO_OPEN_CLOSE_STREAM +mng_openstream MNG_DECL mng_getcb_openstream (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_OPENSTREAM, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_OPENSTREAM, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fOpenstream; +} +#endif +#endif /* MNG_SUPPORT_READ || MNG_SUPPORT_WRITE */ + +/* ************************************************************************** */ + +#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE) +#ifndef MNG_NO_OPEN_CLOSE_STREAM +mng_closestream MNG_DECL mng_getcb_closestream (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_CLOSESTREAM, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_CLOSESTREAM, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fClosestream; +} +#endif +#endif /* MNG_SUPPORT_READ || MNG_SUPPORT_WRITE */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_WRITE +mng_writedata MNG_DECL mng_getcb_writedata (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_WRITEDATA, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_WRITEDATA, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fWritedata; +} +#endif /* MNG_SUPPORT_WRITE */ + +/* ************************************************************************** */ + +mng_errorproc MNG_DECL mng_getcb_errorproc (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_ERRORPROC, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_ERRORPROC, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fErrorproc; +} + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_TRACE +mng_traceproc MNG_DECL mng_getcb_traceproc (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_TRACEPROC, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_TRACEPROC, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fTraceproc; +} +#endif /* MNG_SUPPORT_TRACE */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +mng_processheader MNG_DECL mng_getcb_processheader (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSHEADER, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSHEADER, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fProcessheader; +} +#endif /* MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +#ifndef MNG_SKIPCHUNK_tEXt +mng_processtext MNG_DECL mng_getcb_processtext (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSTEXT, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSTEXT, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fProcesstext; +} +#endif +#endif /* MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +#ifndef MNG_SKIPCHUNK_SAVE +mng_processsave MNG_DECL mng_getcb_processsave (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSSAVE, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSSAVE, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fProcesssave; +} +#endif +#endif /* MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +#ifndef MNG_SKIPCHUNK_SEEK +mng_processseek MNG_DECL mng_getcb_processseek (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSSEEK, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSSEEK, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fProcessseek; +} +#endif +#endif /* MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +#ifndef MNG_SKIPCHUNK_nEED +mng_processneed MNG_DECL mng_getcb_processneed (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSNEED, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSNEED, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fProcessneed; +} +#endif +#endif /* MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +mng_processmend MNG_DECL mng_getcb_processmend (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSMEND, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSMEND, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fProcessmend; +} +#endif /* MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +mng_processunknown MNG_DECL mng_getcb_processunknown (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSUNKNOWN, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSUNKNOWN, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fProcessunknown; +} +#endif /* MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +#ifndef MNG_SKIPCHUNK_TERM +mng_processterm MNG_DECL mng_getcb_processterm (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSTERM, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSTERM, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fProcessterm; +} +#endif +#endif /* MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_getcanvasline MNG_DECL mng_getcb_getcanvasline (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_GETCANVASLINE, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_GETCANVASLINE, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fGetcanvasline; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_getbkgdline MNG_DECL mng_getcb_getbkgdline (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_GETBKGDLINE, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_GETBKGDLINE, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fGetbkgdline; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_getalphaline MNG_DECL mng_getcb_getalphaline (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_GETALPHALINE, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_GETALPHALINE, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fGetalphaline; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_refresh MNG_DECL mng_getcb_refresh (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_REFRESH, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_REFRESH, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fRefresh; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_gettickcount MNG_DECL mng_getcb_gettickcount (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_GETTICKCOUNT, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_GETTICKCOUNT, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fGettickcount; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_settimer MNG_DECL mng_getcb_settimer (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_SETTIMER, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_SETTIMER, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fSettimer; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_APP_CMS) +mng_processgamma MNG_DECL mng_getcb_processgamma (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSGAMMA, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSGAMMA, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fProcessgamma; +} +#endif /* MNG_SUPPORT_DISPLAY && MNG_APP_CMS */ + +/* ************************************************************************** */ + +#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_APP_CMS) +#ifndef MNG_SKIPCHUNK_cHRM +mng_processchroma MNG_DECL mng_getcb_processchroma (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSCHROMA, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSCHROMA, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fProcesschroma; +} +#endif +#endif /* MNG_SUPPORT_DISPLAY && MNG_APP_CMS */ + +/* ************************************************************************** */ + +#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_APP_CMS) +mng_processsrgb MNG_DECL mng_getcb_processsrgb (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSSRGB, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSSRGB, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fProcesssrgb; +} +#endif /* MNG_SUPPORT_DISPLAY && MNG_APP_CMS */ + +/* ************************************************************************** */ + +#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_APP_CMS) +#ifndef MNG_SKIPCHUNK_iCCP +mng_processiccp MNG_DECL mng_getcb_processiccp (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSICCP, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSICCP, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fProcessiccp; +} +#endif +#endif /* MNG_SUPPORT_DISPLAY && MNG_APP_CMS */ + +/* ************************************************************************** */ + +#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_APP_CMS) +mng_processarow MNG_DECL mng_getcb_processarow (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSAROW, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GETCB_PROCESSAROW, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->fProcessarow; +} +#endif /* MNG_SUPPORT_DISPLAY && MNG_APP_CMS */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ + + + diff --git a/Engine/lib/lmng/libmng_chunk_descr.c b/Engine/lib/lmng/libmng_chunk_descr.c new file mode 100644 index 000000000..e1004a540 --- /dev/null +++ b/Engine/lib/lmng/libmng_chunk_descr.c @@ -0,0 +1,6090 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_chunk_descr.c copyright (c) 2005-2007 G.Juyn * */ +/* * version : 1.0.10 * */ +/* * * */ +/* * purpose : Chunk descriptor functions (implementation) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : implementation of the chunk- anf field-descriptor * */ +/* * routines * */ +/* * * */ +/* * changes : 1.0.9 - 12/06/2004 - G.Juyn * */ +/* * - added conditional MNG_OPTIMIZE_CHUNKREADER * */ +/* * 1.0.9 - 12/11/2004 - G.Juyn * */ +/* * - made all constants 'static' * */ +/* * 1.0.9 - 12/20/2004 - G.Juyn * */ +/* * - cleaned up macro-invocations (thanks to D. Airlie) * */ +/* * 1.0.9 - 01/17/2005 - G.Juyn * */ +/* * - fixed problem with global PLTE/tRNS * */ +/* * * */ +/* * 1.0.10 - 01/17/2005 - G.R-P. * */ +/* * - added typecast to appease the compiler * */ +/* * 1.0.10 - 04/08/2007 - G.Juyn * */ +/* * - added support for mPNG proposal * */ +/* * 1.0.10 - 04/12/2007 - G.Juyn * */ +/* * - added support for ANG proposal * */ +/* * * */ +/* ************************************************************************** */ + +#include /* needed for offsetof() */ + +#include "libmng.h" +#include "libmng_data.h" +#include "libmng_error.h" +#include "libmng_trace.h" +#ifdef __BORLANDC__ +#pragma hdrstop +#endif +#include "libmng_memory.h" +#include "libmng_objects.h" +#include "libmng_chunks.h" +#include "libmng_chunk_descr.h" +#include "libmng_object_prc.h" +#include "libmng_chunk_prc.h" +#include "libmng_chunk_io.h" +#include "libmng_display.h" + +#ifdef MNG_INCLUDE_ANG_PROPOSAL +#include "libmng_pixels.h" +#include "libmng_filter.h" +#endif + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +/* ************************************************************************** */ + +#ifdef MNG_OPTIMIZE_CHUNKREADER +#if defined(MNG_INCLUDE_READ_PROCS) || defined(MNG_INCLUDE_WRITE_PROCS) + +/* ************************************************************************** */ +/* ************************************************************************** */ +/* PNG chunks */ + +MNG_LOCAL mng_field_descriptor mng_fields_ihdr [] = + { + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_NOHIGHBIT, + 1, 0, 4, 4, + offsetof(mng_ihdr, iWidth), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_NOHIGHBIT, + 1, 0, 4, 4, + offsetof(mng_ihdr, iHeight), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 1, 16, 1, 1, + offsetof(mng_ihdr, iBitdepth), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 6, 1, 1, + offsetof(mng_ihdr, iColortype), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 1, 1, + offsetof(mng_ihdr, iCompression), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 1, 1, + offsetof(mng_ihdr, iFilter), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 1, 1, 1, + offsetof(mng_ihdr, iInterlace), MNG_NULL, MNG_NULL} + }; + +/* ************************************************************************** */ + +MNG_LOCAL mng_field_descriptor mng_fields_plte [] = + { + {mng_debunk_plte, + MNG_NULL, + 0, 0, 0, 0, + MNG_NULL, MNG_NULL, MNG_NULL} + }; + +/* ************************************************************************** */ + +MNG_LOCAL mng_field_descriptor mng_fields_idat [] = + { + {MNG_NULL, + MNG_NULL, + 0, 0, 0, 0, + offsetof(mng_idat, pData), MNG_NULL, offsetof(mng_idat, iDatasize)} + }; + +/* ************************************************************************** */ + +MNG_LOCAL mng_field_descriptor mng_fields_trns [] = + { + {mng_debunk_trns, + MNG_NULL, + 0, 0, 0, 0, + MNG_NULL, MNG_NULL, MNG_NULL} + }; + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_gAMA +MNG_LOCAL mng_field_descriptor mng_fields_gama [] = + { + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_gama, iGamma), MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_cHRM +MNG_LOCAL mng_field_descriptor mng_fields_chrm [] = + { + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_chrm, iWhitepointx), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_chrm, iWhitepointy), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_chrm, iRedx), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_chrm, iRedy), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_chrm, iGreeny), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_chrm, iGreeny), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_chrm, iBluex), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_chrm, iBluey), MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_sRGB +MNG_LOCAL mng_field_descriptor mng_fields_srgb [] = + { + {MNG_NULL, + MNG_FIELD_INT, + 0, 4, 1, 1, + offsetof(mng_srgb, iRenderingintent), MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_iCCP +MNG_LOCAL mng_field_descriptor mng_fields_iccp [] = + { + {MNG_NULL, + MNG_FIELD_TERMINATOR, + 0, 0, 1, 79, + offsetof(mng_iccp, zName), MNG_NULL, offsetof(mng_iccp, iNamesize)}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 1, 1, + offsetof(mng_iccp, iCompression), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_DEFLATED, + 0, 0, 0, 0, + offsetof(mng_iccp, pProfile), MNG_NULL, offsetof(mng_iccp, iProfilesize)} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_tEXt +MNG_LOCAL mng_field_descriptor mng_fields_text [] = + { + {MNG_NULL, + MNG_FIELD_TERMINATOR, + 0, 0, 1, 79, + offsetof(mng_text, zKeyword), MNG_NULL, offsetof(mng_text, iKeywordsize)}, + {MNG_NULL, + MNG_NULL, + 0, 0, 0, 0, + offsetof(mng_text, zText), MNG_NULL, offsetof(mng_text, iTextsize)} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_zTXt +MNG_LOCAL mng_field_descriptor mng_fields_ztxt [] = + { + {MNG_NULL, + MNG_FIELD_TERMINATOR, + 0, 0, 1, 79, + offsetof(mng_ztxt, zKeyword), MNG_NULL, offsetof(mng_ztxt, iKeywordsize)}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 1, 1, + offsetof(mng_ztxt, iCompression), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_DEFLATED, + 0, 0, 0, 0, + offsetof(mng_ztxt, zText), MNG_NULL, offsetof(mng_ztxt, iTextsize)} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_iTXt +MNG_LOCAL mng_field_descriptor mng_fields_itxt [] = + { + {MNG_NULL, + MNG_FIELD_TERMINATOR, + 0, 0, 1, 79, + offsetof(mng_itxt, zKeyword), MNG_NULL, offsetof(mng_itxt, iKeywordsize)}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 1, 1, 1, + offsetof(mng_itxt, iCompressionflag), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 1, 1, + offsetof(mng_itxt, iCompressionmethod), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_TERMINATOR, + 0, 0, 0, 0, + offsetof(mng_itxt, zLanguage), MNG_NULL, offsetof(mng_itxt, iLanguagesize)}, + {MNG_NULL, + MNG_FIELD_TERMINATOR, + 0, 0, 0, 0, + offsetof(mng_itxt, zTranslation), MNG_NULL, offsetof(mng_itxt, iTranslationsize)}, + {mng_deflate_itxt, + MNG_NULL, + 0, 0, 0, 0, + MNG_NULL, MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_bKGD +MNG_LOCAL mng_field_descriptor mng_fields_bkgd [] = + { + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_PUTIMGTYPE, + 0, 0, 0, 0, + offsetof(mng_bkgd, iType), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_IFIMGTYPE3, + 0, 0xFF, 1, 1, + offsetof(mng_bkgd, iIndex), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_IFIMGTYPE0 | MNG_FIELD_IFIMGTYPE4, + 0, 0xFFFF, 2, 2, + offsetof(mng_bkgd, iGray), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_IFIMGTYPE2 | MNG_FIELD_IFIMGTYPE6, + 0, 0xFFFF, 2, 2, + offsetof(mng_bkgd, iRed), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_IFIMGTYPE2 | MNG_FIELD_IFIMGTYPE6, + 0, 0xFFFF, 2, 2, + offsetof(mng_bkgd, iGreen), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_IFIMGTYPE2 | MNG_FIELD_IFIMGTYPE6, + 0, 0xFFFF, 2, 2, + offsetof(mng_bkgd, iBlue), MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_pHYs +MNG_LOCAL mng_field_descriptor mng_fields_phys [] = + { + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_phys, iSizex), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_phys, iSizey), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 1, 1, 1, + offsetof(mng_phys, iUnit), MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_sBIT +MNG_LOCAL mng_field_descriptor mng_fields_sbit [] = + { + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_PUTIMGTYPE, + 0, 0, 0, 0, + offsetof(mng_sbit, iType), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_IFIMGTYPES, + 0, 0xFF, 1, 1, + offsetof(mng_sbit, aBits[0]), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_IFIMGTYPE2 | MNG_FIELD_IFIMGTYPE3 | MNG_FIELD_IFIMGTYPE4 | MNG_FIELD_IFIMGTYPE6, + 0, 0xFF, 1, 1, + offsetof(mng_sbit, aBits[1]), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_IFIMGTYPE2 | MNG_FIELD_IFIMGTYPE3 | MNG_FIELD_IFIMGTYPE6, + 0, 0xFF, 1, 1, + offsetof(mng_sbit, aBits[2]), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_IFIMGTYPE6, + 0, 0xFF, 1, 1, + offsetof(mng_sbit, aBits[3]), MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_sPLT +MNG_LOCAL mng_field_descriptor mng_fields_splt [] = + { + {MNG_NULL, + MNG_NULL, + 0, 0, 1, 79, + offsetof(mng_splt, zName), MNG_NULL, offsetof(mng_splt, iNamesize)}, + {MNG_NULL, + MNG_FIELD_INT, + 8, 16, 1, 1, + offsetof(mng_splt, iSampledepth), MNG_NULL, MNG_NULL}, + {mng_splt_entries, + MNG_NULL, + 0, 0, 0, 0, + MNG_NULL, MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_hIST +MNG_LOCAL mng_field_descriptor mng_fields_hist [] = + { + {mng_hist_entries, + MNG_NULL, + 0, 0, 0, 0, + MNG_NULL, MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_tIME +MNG_LOCAL mng_field_descriptor mng_fields_time [] = + { + {MNG_NULL, + MNG_FIELD_INT, + 0, 0xFFFF, 2, 2, + offsetof(mng_time, iYear), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 1, 12, 1, 1, + offsetof(mng_time, iMonth), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 1, 31, 1, 1, + offsetof(mng_time, iDay), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 24, 1, 1, + offsetof(mng_time, iHour), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 60, 1, 1, + offsetof(mng_time, iMinute), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 60, 1, 1, + offsetof(mng_time, iSecond), MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ +/* ************************************************************************** */ +/* JNG chunks */ + +#ifdef MNG_INCLUDE_JNG +MNG_LOCAL mng_field_descriptor mng_fields_jhdr [] = + { + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_NOHIGHBIT, + 1, 0, 4, 4, + offsetof(mng_jhdr, iWidth), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_NOHIGHBIT, + 1, 0, 4, 4, + offsetof(mng_jhdr, iHeight), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 8, 16, 1, 1, + offsetof(mng_jhdr, iColortype), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 8, 20, 1, 1, + offsetof(mng_jhdr, iImagesampledepth), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 8, 8, 1, 1, + offsetof(mng_jhdr, iImagecompression), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 8, 1, 1, + offsetof(mng_jhdr, iImageinterlace), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 16, 1, 1, + offsetof(mng_jhdr, iAlphasampledepth), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 8, 1, 1, + offsetof(mng_jhdr, iAlphacompression), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 1, 1, + offsetof(mng_jhdr, iAlphafilter), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 1, 1, 1, + offsetof(mng_jhdr, iAlphainterlace), MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +#define mng_fields_jdaa mng_fields_idat +#define mng_fields_jdat mng_fields_idat +#endif + +/* ************************************************************************** */ +/* ************************************************************************** */ +/* MNG chunks */ + +MNG_LOCAL mng_field_descriptor mng_fields_mhdr [] = + { + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_mhdr, iWidth), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_mhdr, iHeight), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_mhdr, iTicks), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_mhdr, iLayercount), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_mhdr, iFramecount), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_mhdr, iPlaytime), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_mhdr, iSimplicity), MNG_NULL, MNG_NULL} + }; + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_LOOP +MNG_LOCAL mng_field_descriptor mng_fields_loop [] = + { + {mng_debunk_loop, + MNG_NULL, + 0, 0, 0, 0, + MNG_NULL, MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_LOOP +MNG_LOCAL mng_field_descriptor mng_fields_endl [] = + { + {MNG_NULL, + MNG_FIELD_INT, + 0, 0xFF, 1, 1, + offsetof(mng_endl, iLevel), MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_DEFI +MNG_LOCAL mng_field_descriptor mng_fields_defi [] = + { + {MNG_NULL, + MNG_FIELD_INT, + 0, 0xFFFF, 2, 2, + offsetof(mng_defi, iObjectid), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL, + 0, 0xFF, 1, 1, + offsetof(mng_defi, iDonotshow), offsetof(mng_defi, bHasdonotshow), MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL, + 0, 0xFF, 1, 1, + offsetof(mng_defi, iConcrete), offsetof(mng_defi, bHasconcrete), MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP1, + 0, 0, 4, 4, + offsetof(mng_defi, iXlocation), offsetof(mng_defi, bHasloca), MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP1, + 0, 0, 4, 4, + offsetof(mng_defi, iYlocation), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP2, + 0, 0, 4, 4, + offsetof(mng_defi, iLeftcb), offsetof(mng_defi, bHasclip), MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP2, + 0, 0, 4, 4, + offsetof(mng_defi, iRightcb), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP2, + 0, 0, 4, 4, + offsetof(mng_defi, iTopcb), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP2, + 0, 0, 4, 4, + offsetof(mng_defi, iBottomcb), MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_BASI +MNG_LOCAL mng_field_descriptor mng_fields_basi [] = + { + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_basi, iWidth), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_basi, iHeight), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 1, 16, 1, 1, + offsetof(mng_basi, iBitdepth), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 6, 1, 1, + offsetof(mng_basi, iColortype), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 1, 1, + offsetof(mng_basi, iCompression), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 1, 1, + offsetof(mng_basi, iFilter), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 1, 1, 1, + offsetof(mng_basi, iInterlace), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP1, + 0, 0xFFFF, 2, 2, + offsetof(mng_basi, iRed), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP1, + 0, 0xFFFF, 2, 2, + offsetof(mng_basi, iGreen), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP1, + 0, 0xFFFF, 2, 2, + offsetof(mng_basi, iBlue), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL, + 0, 0xFFFF, 2, 2, + offsetof(mng_basi, iAlpha), offsetof(mng_basi, bHasalpha), MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL, + 0, 1, 1, 1, + offsetof(mng_basi, iViewable), MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_CLON +MNG_LOCAL mng_field_descriptor mng_fields_clon [] = + { + {MNG_NULL, + MNG_FIELD_INT, + 0, 0xFFFF, 2, 2, + offsetof(mng_clon, iSourceid), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0xFFFF, 2, 2, + offsetof(mng_clon, iCloneid), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL, + 0, 2, 1, 1, + offsetof(mng_clon, iClonetype), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL, + 0, 1, 1, 1, + offsetof(mng_clon, iDonotshow), offsetof(mng_clon, bHasdonotshow), MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL, + 0, 1, 1, 1, + offsetof(mng_clon, iConcrete), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP1, + 0, 2, 1, 1, + offsetof(mng_clon, iLocationtype), offsetof(mng_clon, bHasloca), MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP1, + 0, 0, 4, 4, + offsetof(mng_clon, iLocationx), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP1, + 0, 0, 4, 4, + offsetof(mng_clon, iLocationy), MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_PAST +MNG_LOCAL mng_field_descriptor mng_fields_past [] = + { + {mng_debunk_past, + MNG_NULL, + 0, 0, 0, 0, + MNG_NULL, MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_DISC +MNG_LOCAL mng_field_descriptor mng_fields_disc [] = + { + {mng_disc_entries, + MNG_NULL, + 0, 0, 0, 0, + MNG_NULL, MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_BACK +MNG_LOCAL mng_field_descriptor mng_fields_back [] = + { + {MNG_NULL, + MNG_FIELD_INT, + 0, 0xFFFF, 2, 2, + offsetof(mng_back, iRed), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0xFFFF, 2, 2, + offsetof(mng_back, iGreen), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0xFFFF, 2, 2, + offsetof(mng_back, iBlue), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL, + 0, 3, 1, 1, + offsetof(mng_back, iMandatory), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL, + 0, 0xFFFF, 2, 2, + offsetof(mng_back, iImageid), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL, + 0, 1, 1, 1, + offsetof(mng_back, iTile), MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_FRAM +MNG_LOCAL mng_field_descriptor mng_fields_fram [] = + { + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL, + 0, 4, 1, 1, + offsetof(mng_fram, iMode), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_TERMINATOR | MNG_FIELD_OPTIONAL, + 0, 0, 1, 79, + offsetof(mng_fram, zName), MNG_NULL, offsetof(mng_fram, iNamesize)}, + {mng_fram_remainder, + MNG_FIELD_OPTIONAL, + 0, 0, 0, 0, + MNG_NULL, MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_MOVE +MNG_LOCAL mng_field_descriptor mng_fields_move [] = + { + {MNG_NULL, + MNG_FIELD_INT, + 0, 0xFFFF, 2, 2, + offsetof(mng_move, iFirstid), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0xFFFF, 2, 2, + offsetof(mng_move, iLastid), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 1, 1, 1, + offsetof(mng_move, iMovetype), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_move, iMovex), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_move, iMovey), MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_CLIP +MNG_LOCAL mng_field_descriptor mng_fields_clip [] = + { + {MNG_NULL, + MNG_FIELD_INT, + 0, 0xFFFF, 2, 2, + offsetof(mng_clip, iFirstid), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0xFFFF, 2, 2, + offsetof(mng_clip, iLastid), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 1, 1, 1, + offsetof(mng_clip, iCliptype), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_clip, iClipl), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_clip, iClipr), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_clip, iClipt), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_clip, iClipb), MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SHOW +MNG_LOCAL mng_field_descriptor mng_fields_show [] = + { + {MNG_NULL, + MNG_FIELD_INT, + 1, 0xFFFF, 2, 2, + offsetof(mng_show, iFirstid), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL, + 1, 0xFFFF, 2, 2, + offsetof(mng_show, iLastid), offsetof(mng_show, bHaslastid), MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL, + 0, 7, 1, 1, + offsetof(mng_show, iMode), MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_TERM +MNG_LOCAL mng_field_descriptor mng_fields_term [] = + { + {MNG_NULL, + MNG_FIELD_INT, + 0, 3, 1, 1, + offsetof(mng_term, iTermaction), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP1, + 0, 2, 1, 1, + offsetof(mng_term, iIteraction), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP1, + 0, 0, 4, 4, + offsetof(mng_term, iDelay), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP1, + 0, 0, 4, 4, + offsetof(mng_term, iItermax), MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SAVE +MNG_LOCAL mng_field_descriptor mng_fields_save [] = + { + {mng_save_entries, + MNG_NULL, + 0, 0, 0, 0, + MNG_NULL, MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SEEK +MNG_LOCAL mng_field_descriptor mng_fields_seek [] = + { + {MNG_NULL, + MNG_NULL, + 0, 0, 1, 79, + offsetof(mng_seek, zName), MNG_NULL, offsetof(mng_seek, iNamesize)} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_eXPI +MNG_LOCAL mng_field_descriptor mng_fields_expi [] = + { + {MNG_NULL, + MNG_FIELD_INT, + 0, 0xFFFF, 2, 2, + offsetof(mng_expi, iSnapshotid), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_NULL, + 0, 0, 1, 79, + offsetof(mng_expi, zName), MNG_NULL, offsetof(mng_expi, iNamesize)} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_fPRI +MNG_LOCAL mng_field_descriptor mng_fields_fpri [] = + { + {MNG_NULL, + MNG_FIELD_INT, + 0, 1, 1, 1, + offsetof(mng_fpri, iDeltatype), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0xFF, 1, 1, + offsetof(mng_fpri, iPriority), MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_nEED +MNG_LOCAL mng_field_descriptor mng_fields_need [] = + { + {MNG_NULL, + MNG_NULL, + 0, 0, 1, 0, + offsetof(mng_need, zKeywords), MNG_NULL, offsetof(mng_need, iKeywordssize)} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_pHYg +#define mng_fields_phyg mng_fields_phys +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +MNG_LOCAL mng_field_descriptor mng_fields_dhdr [] = + { + {MNG_NULL, + MNG_FIELD_INT, + 0, 0xFFFF, 2, 2, + offsetof(mng_dhdr, iObjectid), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 2, 1, 1, + offsetof(mng_dhdr, iImagetype), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 7, 1, 1, + offsetof(mng_dhdr, iDeltatype), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP1, + 0, 0, 4, 4, + offsetof(mng_dhdr, iBlockwidth), offsetof(mng_dhdr, bHasblocksize), MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP1, + 0, 0, 4, 4, + offsetof(mng_dhdr, iBlockheight), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP2, + 0, 0, 4, 4, + offsetof(mng_dhdr, iBlockx), offsetof(mng_dhdr, bHasblockloc), MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT | MNG_FIELD_OPTIONAL | MNG_FIELD_GROUP2, + 0, 0, 4, 4, + offsetof(mng_dhdr, iBlocky), MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +MNG_LOCAL mng_field_descriptor mng_fields_prom [] = + { + {MNG_NULL, + MNG_FIELD_INT, + 0, 14, 1, 1, + offsetof(mng_prom, iColortype), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 16, 1, 1, + offsetof(mng_prom, iSampledepth), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 1, 1, 1, + offsetof(mng_prom, iFilltype), MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +MNG_LOCAL mng_field_descriptor mng_fields_pplt [] = + { + {MNG_NULL, + MNG_FIELD_INT, + 0, 5, 1, 1, + offsetof(mng_pplt, iDeltatype), MNG_NULL, MNG_NULL}, + {mng_pplt_entries, + MNG_NULL, + 0, 0, 0, 0, + MNG_NULL, MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +MNG_LOCAL mng_field_descriptor mng_fields_drop [] = + { + {mng_drop_entries, + MNG_NULL, + 0, 0, 0, 0, + MNG_NULL, MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_DBYK +MNG_LOCAL mng_field_descriptor mng_fields_dbyk [] = + { + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_dbyk, iChunkname), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 1, 1, 1, + offsetof(mng_dbyk, iPolarity), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_NULL, + 0, 0, 1, 0, + offsetof(mng_dbyk, zKeywords), MNG_NULL, offsetof(mng_dbyk, iKeywordssize)} + }; +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_ORDR +MNG_LOCAL mng_field_descriptor mng_fields_ordr [] = + { + {mng_drop_entries, + MNG_NULL, + 0, 0, 0, 0, + MNG_NULL, MNG_NULL, MNG_NULL} + }; +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_MAGN +MNG_LOCAL mng_field_descriptor mng_fields_magn [] = + { + {mng_debunk_magn, + MNG_NULL, + 0, 0, 0, 0, + MNG_NULL, MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_MPNG_PROPOSAL +MNG_LOCAL mng_field_descriptor mng_fields_mpng [] = + { + {MNG_NULL, + MNG_FIELD_INT, + 1, 0, 4, 4, + offsetof(mng_mpng, iFramewidth), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 1, 0, 4, 4, + offsetof(mng_mpng, iFrameheight), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0xFFFF, 2, 2, + offsetof(mng_mpng, iNumplays), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 1, 0xFFFF, 2, 2, + offsetof(mng_mpng, iTickspersec), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 1, 1, + offsetof(mng_mpng, iCompressionmethod), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_DEFLATED, + 0, 0, 1, 0, + offsetof(mng_mpng, pFrames), MNG_NULL, offsetof(mng_mpng, iFramessize)} + }; +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_ANG_PROPOSAL +MNG_LOCAL mng_field_descriptor mng_fields_ahdr [] = + { + {MNG_NULL, + MNG_FIELD_INT, + 1, 0, 4, 4, + offsetof(mng_ahdr, iNumframes), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_ahdr, iTickspersec), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 0, 4, 4, + offsetof(mng_ahdr, iNumplays), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 1, 0, 4, 4, + offsetof(mng_ahdr, iTilewidth), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 1, 0, 4, 4, + offsetof(mng_ahdr, iTileheight), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 1, 1, 1, + offsetof(mng_ahdr, iInterlace), MNG_NULL, MNG_NULL}, + {MNG_NULL, + MNG_FIELD_INT, + 0, 1, 1, 1, + offsetof(mng_ahdr, iStillused), MNG_NULL, MNG_NULL} + }; + +MNG_LOCAL mng_field_descriptor mng_fields_adat [] = + { + {mng_adat_tiles, + MNG_NULL, + 0, 0, 0, 0, + MNG_NULL, MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_evNT +MNG_LOCAL mng_field_descriptor mng_fields_evnt [] = + { + {mng_evnt_entries, + MNG_NULL, + 0, 0, 0, 0, + MNG_NULL, MNG_NULL, MNG_NULL} + }; +#endif + +/* ************************************************************************** */ + +MNG_LOCAL mng_field_descriptor mng_fields_unknown [] = + { + {MNG_NULL, + MNG_NULL, + 0, 0, 1, 0, + offsetof(mng_unknown_chunk, pData), MNG_NULL, offsetof(mng_unknown_chunk, iDatasize)} + }; + +/* ************************************************************************** */ +/* ************************************************************************** */ +/* PNG chunks */ + +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_ihdr = + {mng_it_png, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_ihdr, + mng_fields_ihdr, (sizeof(mng_fields_ihdr) / sizeof(mng_field_descriptor)), + MNG_DESCR_GLOBAL, + MNG_NULL, + MNG_DESCR_NOIHDR | MNG_DESCR_NOJHDR | MNG_DESCR_NOBASI | MNG_DESCR_NOIDAT | MNG_DESCR_NOPLTE}; + +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_plte = + {mng_it_png, mng_create_none, 0, offsetof(mng_plte, bEmpty), + MNG_NULL, MNG_NULL, mng_special_plte, + mng_fields_plte, (sizeof(mng_fields_plte) / sizeof(mng_field_descriptor)), + MNG_DESCR_GLOBAL | MNG_DESCR_EMPTYEMBED, + MNG_DESCR_GenHDR, + MNG_DESCR_NOIDAT | MNG_DESCR_NOJDAT | MNG_DESCR_NOJDAA}; + +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_idat = + {mng_it_png, mng_create_none, 0, offsetof(mng_idat, bEmpty), + MNG_NULL, MNG_NULL, mng_special_idat, + mng_fields_idat, (sizeof(mng_fields_idat) / sizeof(mng_field_descriptor)), + MNG_DESCR_EMPTYEMBED, + MNG_DESCR_GenHDR, + MNG_DESCR_NOJSEP}; + +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_iend = + {mng_it_png, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_iend, + MNG_NULL, 0, + MNG_DESCR_EMPTY | MNG_DESCR_EMPTYEMBED, + MNG_DESCR_GenHDR, + MNG_NULL}; + +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_trns = + {mng_it_png, mng_create_none, 0, offsetof(mng_trns, bEmpty), + MNG_NULL, MNG_NULL, mng_special_trns, + mng_fields_trns, (sizeof(mng_fields_trns) / sizeof(mng_field_descriptor)), + MNG_DESCR_GLOBAL | MNG_DESCR_EMPTYEMBED, + MNG_DESCR_GenHDR, + MNG_DESCR_NOIDAT | MNG_DESCR_NOJDAT | MNG_DESCR_NOJDAA}; + +#ifndef MNG_SKIPCHUNK_gAMA +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_gama = + {mng_it_png, mng_create_none, 0, offsetof(mng_gama, bEmpty), + MNG_NULL, MNG_NULL, mng_special_gama, + mng_fields_gama, (sizeof(mng_fields_gama) / sizeof(mng_field_descriptor)), + MNG_DESCR_GLOBAL | MNG_DESCR_EMPTYEMBED | MNG_DESCR_EMPTYGLOBAL, + MNG_DESCR_GenHDR, + MNG_DESCR_NOPLTE | MNG_DESCR_NOIDAT | MNG_DESCR_NOJDAT | MNG_DESCR_NOJDAA}; +#endif + +#ifndef MNG_SKIPCHUNK_cHRM +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_chrm = + {mng_it_png, mng_create_none, 0, offsetof(mng_chrm, bEmpty), + MNG_NULL, MNG_NULL, mng_special_chrm, + mng_fields_chrm, (sizeof(mng_fields_chrm) / sizeof(mng_field_descriptor)), + MNG_DESCR_GLOBAL | MNG_DESCR_EMPTYEMBED | MNG_DESCR_EMPTYGLOBAL, + MNG_DESCR_GenHDR, + MNG_DESCR_NOPLTE | MNG_DESCR_NOIDAT | MNG_DESCR_NOJDAT | MNG_DESCR_NOJDAA}; +#endif + +#ifndef MNG_SKIPCHUNK_sRGB +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_srgb = + {mng_it_png, mng_create_none, 0, offsetof(mng_srgb, bEmpty), + MNG_NULL, MNG_NULL, mng_special_srgb, + mng_fields_srgb, (sizeof(mng_fields_srgb) / sizeof(mng_field_descriptor)), + MNG_DESCR_GLOBAL | MNG_DESCR_EMPTYEMBED | MNG_DESCR_EMPTYGLOBAL, + MNG_DESCR_GenHDR, + MNG_DESCR_NOPLTE | MNG_DESCR_NOIDAT | MNG_DESCR_NOJDAT | MNG_DESCR_NOJDAA}; +#endif + +#ifndef MNG_SKIPCHUNK_iCCP +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_iccp = + {mng_it_png, mng_create_none, 0, offsetof(mng_iccp, bEmpty), + MNG_NULL, MNG_NULL, mng_special_iccp, + mng_fields_iccp, (sizeof(mng_fields_iccp) / sizeof(mng_field_descriptor)), + MNG_DESCR_GLOBAL | MNG_DESCR_EMPTYEMBED | MNG_DESCR_EMPTYGLOBAL, + MNG_DESCR_GenHDR, + MNG_DESCR_NOPLTE | MNG_DESCR_NOIDAT | MNG_DESCR_NOJDAT | MNG_DESCR_NOJDAA}; +#endif + +#ifndef MNG_SKIPCHUNK_tEXt +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_text = + {mng_it_png, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_text, + mng_fields_text, (sizeof(mng_fields_text) / sizeof(mng_field_descriptor)), + MNG_DESCR_GLOBAL, + MNG_DESCR_GenHDR, + MNG_NULL}; +#endif + +#ifndef MNG_SKIPCHUNK_zTXt +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_ztxt = + {mng_it_png, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_ztxt, + mng_fields_ztxt, (sizeof(mng_fields_ztxt) / sizeof(mng_field_descriptor)), + MNG_DESCR_GLOBAL, + MNG_DESCR_GenHDR, + MNG_NULL}; +#endif + +#ifndef MNG_SKIPCHUNK_iTXt +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_itxt = + {mng_it_png, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_itxt, + mng_fields_itxt, (sizeof(mng_fields_itxt) / sizeof(mng_field_descriptor)), + MNG_DESCR_GLOBAL, + MNG_DESCR_GenHDR, + MNG_NULL}; +#endif + +#ifndef MNG_SKIPCHUNK_bKGD +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_bkgd = + {mng_it_png, mng_create_none, 0, offsetof(mng_bkgd, bEmpty), + MNG_NULL, MNG_NULL, mng_special_bkgd, + mng_fields_bkgd, (sizeof(mng_fields_bkgd) / sizeof(mng_field_descriptor)), + MNG_DESCR_GLOBAL | MNG_DESCR_EMPTYEMBED | MNG_DESCR_EMPTYGLOBAL, + MNG_DESCR_GenHDR, + MNG_DESCR_NOIDAT | MNG_DESCR_NOJDAT | MNG_DESCR_NOJDAA}; +#endif + +#ifndef MNG_SKIPCHUNK_pHYs +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_phys = + {mng_it_png, mng_create_none, 0, offsetof(mng_phys, bEmpty), + MNG_NULL, MNG_NULL, mng_special_phys, + mng_fields_phys, (sizeof(mng_fields_phys) / sizeof(mng_field_descriptor)), + MNG_DESCR_GLOBAL | MNG_DESCR_EMPTYEMBED | MNG_DESCR_EMPTYGLOBAL, + MNG_DESCR_GenHDR, + MNG_DESCR_NOIDAT | MNG_DESCR_NOJDAT | MNG_DESCR_NOJDAA}; +#endif + +#ifndef MNG_SKIPCHUNK_sBIT +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_sbit = + {mng_it_png, mng_create_none, 0, offsetof(mng_sbit, bEmpty), + MNG_NULL, MNG_NULL, mng_special_sbit, + mng_fields_sbit, (sizeof(mng_fields_sbit) / sizeof(mng_field_descriptor)), + MNG_DESCR_GLOBAL | MNG_DESCR_EMPTYEMBED | MNG_DESCR_EMPTYGLOBAL, + MNG_DESCR_GenHDR, + MNG_DESCR_NOIDAT | MNG_DESCR_NOJDAT | MNG_DESCR_NOJDAA}; +#endif + +#ifndef MNG_SKIPCHUNK_sPLT +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_splt = + {mng_it_png, mng_create_none, 0, offsetof(mng_splt, bEmpty), + MNG_NULL, MNG_NULL, mng_special_splt, + mng_fields_splt, (sizeof(mng_fields_splt) / sizeof(mng_field_descriptor)), + MNG_DESCR_GLOBAL | MNG_DESCR_EMPTYEMBED | MNG_DESCR_EMPTYGLOBAL, + MNG_DESCR_GenHDR, + MNG_DESCR_NOIDAT | MNG_DESCR_NOJDAT | MNG_DESCR_NOJDAA}; +#endif + +#ifndef MNG_SKIPCHUNK_hIST +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_hist = + {mng_it_png, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_hist, + mng_fields_hist, (sizeof(mng_fields_hist) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_DESCR_GenHDR | MNG_DESCR_PLTE, + MNG_DESCR_NOIDAT | MNG_DESCR_NOJDAT | MNG_DESCR_NOJDAA}; +#endif + +#ifndef MNG_SKIPCHUNK_tIME +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_time = + {mng_it_png, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_time, + mng_fields_time, (sizeof(mng_fields_time) / sizeof(mng_field_descriptor)), + MNG_DESCR_GLOBAL, + MNG_DESCR_GenHDR, + MNG_NULL}; +#endif + +/* ************************************************************************** */ +/* JNG chunks */ + +#ifdef MNG_INCLUDE_JNG +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_jhdr = + {mng_it_jng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_jhdr, + mng_fields_jhdr, (sizeof(mng_fields_jhdr) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_NULL, + MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR}; +#endif + +#ifdef MNG_INCLUDE_JNG +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_jdaa = + {mng_it_jng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_jdaa, + mng_fields_jdaa, (sizeof(mng_fields_jdaa) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_DESCR_JngHDR, + MNG_DESCR_NOJSEP}; +#endif + +#ifdef MNG_INCLUDE_JNG +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_jdat = + {mng_it_jng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_jdat, + mng_fields_jdat, (sizeof(mng_fields_jdat) / sizeof(mng_field_descriptor)), + MNG_DESCR_EMPTYEMBED, + MNG_DESCR_JngHDR, + MNG_NULL}; +#endif + +#ifdef MNG_INCLUDE_JNG +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_jsep = + {mng_it_jng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_jsep, + MNG_NULL, 0, + MNG_DESCR_EMPTY | MNG_DESCR_EMPTYEMBED, + MNG_DESCR_JngHDR, + MNG_DESCR_NOJSEP}; +#endif + +/* ************************************************************************** */ +/* MNG chunks */ + +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_mhdr = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_mhdr, + mng_fields_mhdr, (sizeof(mng_fields_mhdr) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_NULL, + MNG_DESCR_NOMHDR | MNG_DESCR_NOIHDR | MNG_DESCR_NOJHDR}; + +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_mend = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_mend, + MNG_NULL, 0, + MNG_DESCR_EMPTY | MNG_DESCR_EMPTYGLOBAL, + MNG_DESCR_MHDR, + MNG_NULL}; + +#ifndef MNG_SKIPCHUNK_LOOP +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_loop = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_loop, + mng_fields_loop, (sizeof(mng_fields_loop) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_DESCR_MHDR, + MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR}; + +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_endl = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_endl, + mng_fields_endl, (sizeof(mng_fields_endl) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_DESCR_MHDR, + MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR}; +#endif + +#ifndef MNG_SKIPCHUNK_DEFI +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_defi = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_defi, + mng_fields_defi, (sizeof(mng_fields_defi) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_DESCR_MHDR, + MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR}; +#endif + +#ifndef MNG_SKIPCHUNK_BASI +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_basi = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_basi, + mng_fields_basi, (sizeof(mng_fields_basi) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_DESCR_MHDR, + MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR}; +#endif + +#ifndef MNG_SKIPCHUNK_CLON +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_clon = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_clon, + mng_fields_clon, (sizeof(mng_fields_clon) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_DESCR_MHDR, + MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR}; +#endif + +#ifndef MNG_SKIPCHUNK_PAST +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_past = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_past, + mng_fields_past, (sizeof(mng_fields_past) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_DESCR_MHDR, + MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR}; +#endif + +#ifndef MNG_SKIPCHUNK_DISC +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_disc = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_disc, + mng_fields_disc, (sizeof(mng_fields_disc) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_DESCR_MHDR, + MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR}; +#endif + +#ifndef MNG_SKIPCHUNK_BACK +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_back = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_back, + mng_fields_back, (sizeof(mng_fields_back) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_DESCR_MHDR, + MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR}; +#endif + +#ifndef MNG_SKIPCHUNK_FRAM +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_fram = + {mng_it_mng, mng_create_none, 0, offsetof(mng_fram, bEmpty), + MNG_NULL, MNG_NULL, mng_special_fram, + mng_fields_fram, (sizeof(mng_fields_fram) / sizeof(mng_field_descriptor)), + MNG_DESCR_EMPTY | MNG_DESCR_EMPTYGLOBAL, + MNG_DESCR_MHDR, + MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR}; +#endif + +#ifndef MNG_SKIPCHUNK_MOVE +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_move = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_move, + mng_fields_move, (sizeof(mng_fields_move) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_DESCR_MHDR, + MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR}; +#endif + +#ifndef MNG_SKIPCHUNK_CLIP +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_clip = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_clip, + mng_fields_clip, (sizeof(mng_fields_clip) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_DESCR_MHDR, + MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR}; +#endif + +#ifndef MNG_SKIPCHUNK_SHOW +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_show = + {mng_it_mng, mng_create_none, 0, offsetof(mng_show, bEmpty), + MNG_NULL, MNG_NULL, mng_special_show, + mng_fields_show, (sizeof(mng_fields_show) / sizeof(mng_field_descriptor)), + MNG_DESCR_EMPTY | MNG_DESCR_EMPTYGLOBAL, + MNG_DESCR_MHDR, + MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR}; +#endif + +#ifndef MNG_SKIPCHUNK_TERM +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_term = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_term, + mng_fields_term, (sizeof(mng_fields_term) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_DESCR_MHDR, + MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR | MNG_DESCR_NOTERM | MNG_DESCR_NOLOOP}; +#endif + +#ifndef MNG_SKIPCHUNK_SAVE +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_save = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_save, + mng_fields_save, (sizeof(mng_fields_save) / sizeof(mng_field_descriptor)), + MNG_DESCR_EMPTY | MNG_DESCR_EMPTYGLOBAL, + MNG_DESCR_MHDR, + MNG_DESCR_NOSAVE | MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR}; +#endif + +#ifndef MNG_SKIPCHUNK_SEEK +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_seek = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_seek, + mng_fields_seek, (sizeof(mng_fields_seek) / sizeof(mng_field_descriptor)), + MNG_DESCR_EMPTY | MNG_DESCR_EMPTYGLOBAL, + MNG_DESCR_MHDR | MNG_DESCR_SAVE, + MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR}; +#endif + +#ifndef MNG_SKIPCHUNK_eXPI +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_expi = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_expi, + mng_fields_expi, (sizeof(mng_fields_expi) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_DESCR_MHDR, + MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR}; +#endif + +#ifndef MNG_SKIPCHUNK_fPRI +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_fpri = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_fpri, + mng_fields_fpri, (sizeof(mng_fields_fpri) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_DESCR_MHDR, + MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR}; +#endif + +#ifndef MNG_SKIPCHUNK_nEED +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_need = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_need, + mng_fields_need, (sizeof(mng_fields_need) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_DESCR_MHDR, + MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR}; +#endif + +#ifndef MNG_SKIPCHUNK_pHYg +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_phyg = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_phyg, + mng_fields_phyg, (sizeof(mng_fields_phyg) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_DESCR_MHDR, + MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR}; +#endif + +#ifndef MNG_NO_DELTA_PNG +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_dhdr = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_dhdr, + mng_fields_dhdr, (sizeof(mng_fields_dhdr) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_DESCR_MHDR, + MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR}; +#endif + +#ifndef MNG_NO_DELTA_PNG +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_prom = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_prom, + mng_fields_prom, (sizeof(mng_fields_prom) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_DESCR_MHDR | MNG_DESCR_DHDR, + MNG_NULL}; +#endif + +#ifndef MNG_NO_DELTA_PNG +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_ipng = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_ipng, + MNG_NULL, 0, + MNG_DESCR_EMPTY | MNG_DESCR_EMPTYEMBED, + MNG_DESCR_MHDR | MNG_DESCR_DHDR, + MNG_NULL}; +#endif + +#ifndef MNG_NO_DELTA_PNG +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_pplt = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_pplt, + mng_fields_pplt, (sizeof(mng_fields_pplt) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_DESCR_MHDR | MNG_DESCR_DHDR, + MNG_NULL}; +#endif + +#ifndef MNG_NO_DELTA_PNG +#ifdef MNG_INCLUDE_JNG +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_ijng = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_ijng, + MNG_NULL, 0, + MNG_DESCR_EMPTY | MNG_DESCR_EMPTYEMBED, + MNG_DESCR_MHDR | MNG_DESCR_DHDR, + MNG_NULL}; +#endif +#endif + +#ifndef MNG_NO_DELTA_PNG +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_drop = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_drop, + mng_fields_drop, (sizeof(mng_fields_drop) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_DESCR_MHDR | MNG_DESCR_DHDR, + MNG_NULL}; +#endif + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_DBYK +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_dbyk = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_dbyk, + mng_fields_dbyk, (sizeof(mng_fields_dbyk) / sizeof(mng_field_descriptor)), + MNG_DESCR_EMPTY | MNG_DESCR_EMPTYEMBED, + MNG_DESCR_MHDR | MNG_DESCR_DHDR, + MNG_NULL}; +#endif +#endif + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_ORDR +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_ordr = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_ordr, + mng_fields_ordr, (sizeof(mng_fields_ordr) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_DESCR_MHDR | MNG_DESCR_DHDR, + MNG_NULL}; +#endif +#endif + +#ifndef MNG_SKIPCHUNK_MAGN +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_magn = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_magn, + mng_fields_magn, (sizeof(mng_fields_magn) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_DESCR_MHDR, + MNG_DESCR_NOIHDR | MNG_DESCR_NOBASI | MNG_DESCR_NODHDR | MNG_DESCR_NOJHDR}; +#endif + +#ifndef MNG_SKIPCHUNK_evNT +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_evnt = + {mng_it_mng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_evnt, + mng_fields_evnt, (sizeof(mng_fields_evnt) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_DESCR_MHDR, + MNG_DESCR_NOSAVE}; +#endif + +#ifdef MNG_INCLUDE_MPNG_PROPOSAL +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_mpng = + {mng_it_mpng, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_mpng, + mng_fields_mpng, (sizeof(mng_fields_mpng) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_NULL, + MNG_DESCR_NOMHDR | MNG_DESCR_NOIDAT | MNG_DESCR_NOJDAT}; +#endif + +#ifdef MNG_INCLUDE_ANG_PROPOSAL +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_ahdr = + {mng_it_ang, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_ahdr, + mng_fields_ahdr, (sizeof(mng_fields_ahdr) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_DESCR_IHDR, + MNG_DESCR_NOMHDR | MNG_DESCR_NOJHDR | MNG_DESCR_NOIDAT}; + +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_adat = + {mng_it_ang, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_adat, + mng_fields_adat, (sizeof(mng_fields_adat) / sizeof(mng_field_descriptor)), + MNG_NULL, + MNG_DESCR_IHDR, + MNG_DESCR_NOMHDR | MNG_DESCR_NOJHDR}; +#endif + +/* ************************************************************************** */ +/* ************************************************************************** */ +/* the good ol' unknown babe */ + +MNG_LOCAL mng_chunk_descriptor mng_chunk_descr_unknown = + {mng_it_png, mng_create_none, 0, 0, + MNG_NULL, MNG_NULL, mng_special_unknown, + mng_fields_unknown, (sizeof(mng_fields_unknown) / sizeof(mng_field_descriptor)), + MNG_DESCR_EMPTY | MNG_DESCR_EMPTYEMBED, + MNG_NULL, + MNG_NULL}; + +/* ************************************************************************** */ +/* ************************************************************************** */ + +MNG_LOCAL mng_chunk_header mng_chunk_unknown = + {MNG_UINT_HUH, mng_init_general, mng_free_unknown, + mng_read_general, mng_write_unknown, mng_assign_unknown, + 0, 0, sizeof(mng_unknown_chunk), &mng_chunk_descr_unknown}; + +/* ************************************************************************** */ + + /* the table-idea & binary search code was adapted from + libpng 1.1.0 (pngread.c) */ + /* NOTE1: the table must remain sorted by chunkname, otherwise the binary + search will break !!! (ps. watch upper-/lower-case chunknames !!) */ + /* NOTE2: the layout must remain equal to the header part of all the + chunk-structures (yes, that means even the pNext and pPrev fields; + it's wasting a bit of space, but hey, the code is a lot easier) */ + +MNG_LOCAL mng_chunk_header mng_chunk_table [] = + { +#ifndef MNG_SKIPCHUNK_BACK + {MNG_UINT_BACK, mng_init_general, mng_free_general, mng_read_general, mng_write_back, mng_assign_general, 0, 0, sizeof(mng_back), &mng_chunk_descr_back}, +#endif +#ifndef MNG_SKIPCHUNK_BASI + {MNG_UINT_BASI, mng_init_general, mng_free_general, mng_read_general, mng_write_basi, mng_assign_general, 0, 0, sizeof(mng_basi), &mng_chunk_descr_basi}, +#endif +#ifndef MNG_SKIPCHUNK_CLIP + {MNG_UINT_CLIP, mng_init_general, mng_free_general, mng_read_general, mng_write_clip, mng_assign_general, 0, 0, sizeof(mng_clip), &mng_chunk_descr_clip}, +#endif +#ifndef MNG_SKIPCHUNK_CLON + {MNG_UINT_CLON, mng_init_general, mng_free_general, mng_read_general, mng_write_clon, mng_assign_general, 0, 0, sizeof(mng_clon), &mng_chunk_descr_clon}, +#endif +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_DBYK + {MNG_UINT_DBYK, mng_init_general, mng_free_dbyk, mng_read_general, mng_write_dbyk, mng_assign_dbyk, 0, 0, sizeof(mng_dbyk), &mng_chunk_descr_dbyk}, +#endif +#endif +#ifndef MNG_SKIPCHUNK_DEFI + {MNG_UINT_DEFI, mng_init_general, mng_free_general, mng_read_general, mng_write_defi, mng_assign_general, 0, 0, sizeof(mng_defi), &mng_chunk_descr_defi}, +#endif +#ifndef MNG_NO_DELTA_PNG + {MNG_UINT_DHDR, mng_init_general, mng_free_general, mng_read_general, mng_write_dhdr, mng_assign_general, 0, 0, sizeof(mng_dhdr), &mng_chunk_descr_dhdr}, +#endif +#ifndef MNG_SKIPCHUNK_DISC + {MNG_UINT_DISC, mng_init_general, mng_free_disc, mng_read_general, mng_write_disc, mng_assign_disc, 0, 0, sizeof(mng_disc), &mng_chunk_descr_disc}, +#endif +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_DROP + {MNG_UINT_DROP, mng_init_general, mng_free_drop, mng_read_general, mng_write_drop, mng_assign_drop, 0, 0, sizeof(mng_drop), &mng_chunk_descr_drop}, +#endif +#endif +#ifndef MNG_SKIPCHUNK_LOOP + {MNG_UINT_ENDL, mng_init_general, mng_free_general, mng_read_general, mng_write_endl, mng_assign_general, 0, 0, sizeof(mng_endl), &mng_chunk_descr_endl}, +#endif +#ifndef MNG_SKIPCHUNK_FRAM + {MNG_UINT_FRAM, mng_init_general, mng_free_fram, mng_read_general, mng_write_fram, mng_assign_fram, 0, 0, sizeof(mng_fram), &mng_chunk_descr_fram}, +#endif + {MNG_UINT_IDAT, mng_init_general, mng_free_idat, mng_read_general, mng_write_idat, mng_assign_idat, 0, 0, sizeof(mng_idat), &mng_chunk_descr_idat}, /* 12-th element! */ + {MNG_UINT_IEND, mng_init_general, mng_free_general, mng_read_general, mng_write_iend, mng_assign_general, 0, 0, sizeof(mng_iend), &mng_chunk_descr_iend}, + {MNG_UINT_IHDR, mng_init_general, mng_free_general, mng_read_general, mng_write_ihdr, mng_assign_general, 0, 0, sizeof(mng_ihdr), &mng_chunk_descr_ihdr}, +#ifndef MNG_NO_DELTA_PNG +#ifdef MNG_INCLUDE_JNG + {MNG_UINT_IJNG, mng_init_general, mng_free_general, mng_read_general, mng_write_ijng, mng_assign_general, 0, 0, sizeof(mng_ijng), &mng_chunk_descr_ijng}, +#endif + {MNG_UINT_IPNG, mng_init_general, mng_free_general, mng_read_general, mng_write_ipng, mng_assign_general, 0, 0, sizeof(mng_ipng), &mng_chunk_descr_ipng}, +#endif +#ifdef MNG_INCLUDE_JNG + {MNG_UINT_JDAA, mng_init_general, mng_free_jdaa, mng_read_general, mng_write_jdaa, mng_assign_jdaa, 0, 0, sizeof(mng_jdaa), &mng_chunk_descr_jdaa}, + {MNG_UINT_JDAT, mng_init_general, mng_free_jdat, mng_read_general, mng_write_jdat, mng_assign_jdat, 0, 0, sizeof(mng_jdat), &mng_chunk_descr_jdat}, + {MNG_UINT_JHDR, mng_init_general, mng_free_general, mng_read_general, mng_write_jhdr, mng_assign_general, 0, 0, sizeof(mng_jhdr), &mng_chunk_descr_jhdr}, + {MNG_UINT_JSEP, mng_init_general, mng_free_general, mng_read_general, mng_write_jsep, mng_assign_general, 0, 0, sizeof(mng_jsep), &mng_chunk_descr_jsep}, + {MNG_UINT_JdAA, mng_init_general, mng_free_jdaa, mng_read_general, mng_write_jdaa, mng_assign_jdaa, 0, 0, sizeof(mng_jdaa), &mng_chunk_descr_jdaa}, +#endif +#ifndef MNG_SKIPCHUNK_LOOP + {MNG_UINT_LOOP, mng_init_general, mng_free_loop, mng_read_general, mng_write_loop, mng_assign_loop, 0, 0, sizeof(mng_loop), &mng_chunk_descr_loop}, +#endif +#ifndef MNG_SKIPCHUNK_MAGN + {MNG_UINT_MAGN, mng_init_general, mng_free_general, mng_read_general, mng_write_magn, mng_assign_general, 0, 0, sizeof(mng_magn), &mng_chunk_descr_magn}, +#endif + {MNG_UINT_MEND, mng_init_general, mng_free_general, mng_read_general, mng_write_mend, mng_assign_general, 0, 0, sizeof(mng_mend), &mng_chunk_descr_mend}, + {MNG_UINT_MHDR, mng_init_general, mng_free_general, mng_read_general, mng_write_mhdr, mng_assign_general, 0, 0, sizeof(mng_mhdr), &mng_chunk_descr_mhdr}, +#ifndef MNG_SKIPCHUNK_MOVE + {MNG_UINT_MOVE, mng_init_general, mng_free_general, mng_read_general, mng_write_move, mng_assign_general, 0, 0, sizeof(mng_move), &mng_chunk_descr_move}, +#endif +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_ORDR + {MNG_UINT_ORDR, mng_init_general, mng_free_ordr, mng_read_general, mng_write_ordr, mng_assign_ordr, 0, 0, sizeof(mng_ordr), &mng_chunk_descr_ordr}, +#endif +#endif +#ifndef MNG_SKIPCHUNK_PAST + {MNG_UINT_PAST, mng_init_general, mng_free_past, mng_read_general, mng_write_past, mng_assign_past, 0, 0, sizeof(mng_past), &mng_chunk_descr_past}, +#endif + {MNG_UINT_PLTE, mng_init_general, mng_free_general, mng_read_general, mng_write_plte, mng_assign_general, 0, 0, sizeof(mng_plte), &mng_chunk_descr_plte}, +#ifndef MNG_NO_DELTA_PNG + {MNG_UINT_PPLT, mng_init_general, mng_free_general, mng_read_general, mng_write_pplt, mng_assign_general, 0, 0, sizeof(mng_pplt), &mng_chunk_descr_pplt}, + {MNG_UINT_PROM, mng_init_general, mng_free_general, mng_read_general, mng_write_prom, mng_assign_general, 0, 0, sizeof(mng_prom), &mng_chunk_descr_prom}, +#endif +#ifndef MNG_SKIPCHUNK_SAVE + {MNG_UINT_SAVE, mng_init_general, mng_free_save, mng_read_general, mng_write_save, mng_assign_save, 0, 0, sizeof(mng_save), &mng_chunk_descr_save}, +#endif +#ifndef MNG_SKIPCHUNK_SEEK + {MNG_UINT_SEEK, mng_init_general, mng_free_seek, mng_read_general, mng_write_seek, mng_assign_seek, 0, 0, sizeof(mng_seek), &mng_chunk_descr_seek}, +#endif +#ifndef MNG_SKIPCHUNK_SHOW + {MNG_UINT_SHOW, mng_init_general, mng_free_general, mng_read_general, mng_write_show, mng_assign_general, 0, 0, sizeof(mng_show), &mng_chunk_descr_show}, +#endif +#ifndef MNG_SKIPCHUNK_TERM + {MNG_UINT_TERM, mng_init_general, mng_free_general, mng_read_general, mng_write_term, mng_assign_general, 0, 0, sizeof(mng_term), &mng_chunk_descr_term}, +#endif +#ifdef MNG_INCLUDE_ANG_PROPOSAL + {MNG_UINT_adAT, mng_init_general, mng_free_adat, mng_read_general, mng_write_adat, mng_assign_adat, 0, 0, sizeof(mng_adat), &mng_chunk_descr_adat}, + {MNG_UINT_ahDR, mng_init_general, mng_free_general, mng_read_general, mng_write_ahdr, mng_assign_ahdr, 0, 0, sizeof(mng_ahdr), &mng_chunk_descr_ahdr}, +#endif +#ifndef MNG_SKIPCHUNK_bKGD + {MNG_UINT_bKGD, mng_init_general, mng_free_general, mng_read_general, mng_write_bkgd, mng_assign_general, 0, 0, sizeof(mng_bkgd), &mng_chunk_descr_bkgd}, +#endif +#ifndef MNG_SKIPCHUNK_cHRM + {MNG_UINT_cHRM, mng_init_general, mng_free_general, mng_read_general, mng_write_chrm, mng_assign_general, 0, 0, sizeof(mng_chrm), &mng_chunk_descr_chrm}, +#endif +#ifndef MNG_SKIPCHUNK_eXPI + {MNG_UINT_eXPI, mng_init_general, mng_free_expi, mng_read_general, mng_write_expi, mng_assign_expi, 0, 0, sizeof(mng_expi), &mng_chunk_descr_expi}, +#endif +#ifndef MNG_SKIPCHUNK_evNT + {MNG_UINT_evNT, mng_init_general, mng_free_evnt, mng_read_general, mng_write_evnt, mng_assign_evnt, 0, 0, sizeof(mng_evnt), &mng_chunk_descr_evnt}, +#endif +#ifndef MNG_SKIPCHUNK_fPRI + {MNG_UINT_fPRI, mng_init_general, mng_free_general, mng_read_general, mng_write_fpri, mng_assign_general, 0, 0, sizeof(mng_fpri), &mng_chunk_descr_fpri}, +#endif +#ifndef MNG_SKIPCHUNK_gAMA + {MNG_UINT_gAMA, mng_init_general, mng_free_general, mng_read_general, mng_write_gama, mng_assign_general, 0, 0, sizeof(mng_gama), &mng_chunk_descr_gama}, +#endif +#ifndef MNG_SKIPCHUNK_hIST + {MNG_UINT_hIST, mng_init_general, mng_free_general, mng_read_general, mng_write_hist, mng_assign_general, 0, 0, sizeof(mng_hist), &mng_chunk_descr_hist}, +#endif +#ifndef MNG_SKIPCHUNK_iCCP + {MNG_UINT_iCCP, mng_init_general, mng_free_iccp, mng_read_general, mng_write_iccp, mng_assign_iccp, 0, 0, sizeof(mng_iccp), &mng_chunk_descr_iccp}, +#endif +#ifndef MNG_SKIPCHUNK_iTXt + {MNG_UINT_iTXt, mng_init_general, mng_free_itxt, mng_read_general, mng_write_itxt, mng_assign_itxt, 0, 0, sizeof(mng_itxt), &mng_chunk_descr_itxt}, +#endif +#ifdef MNG_INCLUDE_MPNG_PROPOSAL + {MNG_UINT_mpNG, mng_init_general, mng_free_mpng, mng_read_general, mng_write_mpng, mng_assign_mpng, 0, 0, sizeof(mng_mpng), &mng_chunk_descr_mpng}, +#endif +#ifndef MNG_SKIPCHUNK_nEED + {MNG_UINT_nEED, mng_init_general, mng_free_need, mng_read_general, mng_write_need, mng_assign_need, 0, 0, sizeof(mng_need), &mng_chunk_descr_need}, +#endif +/* TODO: {MNG_UINT_oFFs, 0, 0, 0, 0, 0, 0}, */ +/* TODO: {MNG_UINT_pCAL, 0, 0, 0, 0, 0, 0}, */ +#ifndef MNG_SKIPCHUNK_pHYg + {MNG_UINT_pHYg, mng_init_general, mng_free_general, mng_read_general, mng_write_phyg, mng_assign_general, 0, 0, sizeof(mng_phyg), &mng_chunk_descr_phyg}, +#endif +#ifndef MNG_SKIPCHUNK_pHYs + {MNG_UINT_pHYs, mng_init_general, mng_free_general, mng_read_general, mng_write_phys, mng_assign_general, 0, 0, sizeof(mng_phys), &mng_chunk_descr_phys}, +#endif +#ifndef MNG_SKIPCHUNK_sBIT + {MNG_UINT_sBIT, mng_init_general, mng_free_general, mng_read_general, mng_write_sbit, mng_assign_general, 0, 0, sizeof(mng_sbit), &mng_chunk_descr_sbit}, +#endif +/* TODO: {MNG_UINT_sCAL, 0, 0, 0, 0, 0, 0}, */ +#ifndef MNG_SKIPCHUNK_sPLT + {MNG_UINT_sPLT, mng_init_general, mng_free_splt, mng_read_general, mng_write_splt, mng_assign_splt, 0, 0, sizeof(mng_splt), &mng_chunk_descr_splt}, +#endif + {MNG_UINT_sRGB, mng_init_general, mng_free_general, mng_read_general, mng_write_srgb, mng_assign_general, 0, 0, sizeof(mng_srgb), &mng_chunk_descr_srgb}, +#ifndef MNG_SKIPCHUNK_tEXt + {MNG_UINT_tEXt, mng_init_general, mng_free_text, mng_read_general, mng_write_text, mng_assign_text, 0, 0, sizeof(mng_text), &mng_chunk_descr_text}, +#endif +#ifndef MNG_SKIPCHUNK_tIME + {MNG_UINT_tIME, mng_init_general, mng_free_general, mng_read_general, mng_write_time, mng_assign_general, 0, 0, sizeof(mng_time), &mng_chunk_descr_time}, +#endif + {MNG_UINT_tRNS, mng_init_general, mng_free_general, mng_read_general, mng_write_trns, mng_assign_general, 0, 0, sizeof(mng_trns), &mng_chunk_descr_trns}, +#ifndef MNG_SKIPCHUNK_zTXt + {MNG_UINT_zTXt, mng_init_general, mng_free_ztxt, mng_read_general, mng_write_ztxt, mng_assign_ztxt, 0, 0, sizeof(mng_ztxt), &mng_chunk_descr_ztxt}, +#endif + }; + +/* ************************************************************************** */ +/* ************************************************************************** */ + +void mng_get_chunkheader (mng_chunkid iChunkname, + mng_chunk_headerp pResult) +{ + /* binary search variables */ + mng_int32 iTop, iLower, iUpper, iMiddle; + mng_chunk_headerp pEntry; /* pointer to found entry */ + /* determine max index of table */ + iTop = (sizeof (mng_chunk_table) / sizeof (mng_chunk_table [0])) - 1; + + /* binary search; with 54 chunks, worst-case is 7 comparisons */ + iLower = 0; +#ifndef MNG_NO_DELTA_PNG + iMiddle = 11; /* start with the IDAT entry */ +#else + iMiddle = 8; +#endif + iUpper = iTop; + pEntry = 0; /* no goods yet! */ + + do /* the binary search itself */ + { + if (mng_chunk_table [iMiddle].iChunkname < iChunkname) + iLower = iMiddle + 1; + else if (mng_chunk_table [iMiddle].iChunkname > iChunkname) + iUpper = iMiddle - 1; + else + { + pEntry = &mng_chunk_table [iMiddle]; + break; + } + iMiddle = (iLower + iUpper) >> 1; + } + while (iLower <= iUpper); + + if (!pEntry) /* unknown chunk ? */ + pEntry = &mng_chunk_unknown; /* make it so! */ + + MNG_COPY (pResult, pEntry, sizeof(mng_chunk_header)); + + return; +} + +/* ************************************************************************** */ +/* ************************************************************************** */ +/* PNG chunks */ + +MNG_C_SPECIALFUNC (mng_special_ihdr) +{ + pData->bHasIHDR = MNG_TRUE; /* indicate IHDR is present */ + /* and store interesting fields */ + if ((!pData->bHasDHDR) || (pData->iDeltatype == MNG_DELTATYPE_NOCHANGE)) + { + pData->iDatawidth = ((mng_ihdrp)pChunk)->iWidth; + pData->iDataheight = ((mng_ihdrp)pChunk)->iHeight; + } + + pData->iBitdepth = ((mng_ihdrp)pChunk)->iBitdepth; + pData->iColortype = ((mng_ihdrp)pChunk)->iColortype; + pData->iCompression = ((mng_ihdrp)pChunk)->iCompression; + pData->iFilter = ((mng_ihdrp)pChunk)->iFilter; + pData->iInterlace = ((mng_ihdrp)pChunk)->iInterlace; + +#if defined(MNG_NO_1_2_4BIT_SUPPORT) || defined(MNG_NO_16BIT_SUPPORT) + pData->iPNGmult = 1; + pData->iPNGdepth = pData->iBitdepth; +#endif + +#ifdef MNG_NO_1_2_4BIT_SUPPORT + if (pData->iBitdepth < 8) + pData->iBitdepth = 8; +#endif + +#ifdef MNG_NO_16BIT_SUPPORT + if (pData->iBitdepth > 8) + { + pData->iBitdepth = 8; + pData->iPNGmult = 2; + } +#endif + + if ((pData->iBitdepth != 8) /* parameter validity checks */ +#ifndef MNG_NO_1_2_4BIT_SUPPORT + && (pData->iBitdepth != 1) && + (pData->iBitdepth != 2) && + (pData->iBitdepth != 4) +#endif +#ifndef MNG_NO_16BIT_SUPPORT + && (pData->iBitdepth != 16) +#endif + ) + MNG_ERROR (pData, MNG_INVALIDBITDEPTH); + + if ((pData->iColortype != MNG_COLORTYPE_GRAY ) && + (pData->iColortype != MNG_COLORTYPE_RGB ) && + (pData->iColortype != MNG_COLORTYPE_INDEXED) && + (pData->iColortype != MNG_COLORTYPE_GRAYA ) && + (pData->iColortype != MNG_COLORTYPE_RGBA ) ) + MNG_ERROR (pData, MNG_INVALIDCOLORTYPE); + + if ((pData->iColortype == MNG_COLORTYPE_INDEXED) && (pData->iBitdepth > 8)) + MNG_ERROR (pData, MNG_INVALIDBITDEPTH); + + if (((pData->iColortype == MNG_COLORTYPE_RGB ) || + (pData->iColortype == MNG_COLORTYPE_GRAYA ) || + (pData->iColortype == MNG_COLORTYPE_RGBA ) ) && + (pData->iBitdepth < 8 ) ) + MNG_ERROR (pData, MNG_INVALIDBITDEPTH); + + if (pData->iCompression != MNG_COMPRESSION_DEFLATE) + MNG_ERROR (pData, MNG_INVALIDCOMPRESS); + +#if defined(FILTER192) || defined(FILTER193) + if ((pData->iFilter != MNG_FILTER_ADAPTIVE ) && +#if defined(FILTER192) && defined(FILTER193) + (pData->iFilter != MNG_FILTER_DIFFERING) && + (pData->iFilter != MNG_FILTER_NOFILTER ) ) +#else +#ifdef FILTER192 + (pData->iFilter != MNG_FILTER_DIFFERING) ) +#else + (pData->iFilter != MNG_FILTER_NOFILTER ) ) +#endif +#endif + MNG_ERROR (pData, MNG_INVALIDFILTER); +#else + if (pData->iFilter) + MNG_ERROR (pData, MNG_INVALIDFILTER); +#endif + + if ((pData->iInterlace != MNG_INTERLACE_NONE ) && + (pData->iInterlace != MNG_INTERLACE_ADAM7) ) + MNG_ERROR (pData, MNG_INVALIDINTERLACE); + +#ifdef MNG_SUPPORT_DISPLAY +#ifndef MNG_NO_DELTA_PNG + if (pData->bHasDHDR) /* check the colortype for delta-images ! */ + { + mng_imagedatap pBuf = ((mng_imagep)pData->pObjzero)->pImgbuf; + + if (pData->iColortype != pBuf->iColortype) + { + if ( ( (pData->iColortype != MNG_COLORTYPE_INDEXED) || + (pBuf->iColortype == MNG_COLORTYPE_GRAY ) ) && + ( (pData->iColortype != MNG_COLORTYPE_GRAY ) || + (pBuf->iColortype == MNG_COLORTYPE_INDEXED) ) ) + MNG_ERROR (pData, MNG_INVALIDCOLORTYPE); + } + } +#endif +#endif + + if (!pData->bHasheader) /* first chunk ? */ + { + pData->bHasheader = MNG_TRUE; /* we've got a header */ + pData->eImagetype = mng_it_png; /* then this must be a PNG */ + pData->iWidth = pData->iDatawidth; + pData->iHeight = pData->iDataheight; + /* predict alpha-depth ! */ + if ((pData->iColortype == MNG_COLORTYPE_GRAYA ) || + (pData->iColortype == MNG_COLORTYPE_RGBA ) ) + pData->iAlphadepth = pData->iBitdepth; + else + if (pData->iColortype == MNG_COLORTYPE_INDEXED) + pData->iAlphadepth = 8; /* worst case scenario */ + else + pData->iAlphadepth = 1; /* Possible tRNS cheap binary transparency */ + /* fits on maximum canvas ? */ + if ((pData->iWidth > pData->iMaxwidth) || (pData->iHeight > pData->iMaxheight)) + MNG_WARNING (pData, MNG_IMAGETOOLARGE); + +#if !defined(MNG_INCLUDE_MPNG_PROPOSAL) || !defined(MNG_SUPPORT_DISPLAY) + if (pData->fProcessheader) /* inform the app ? */ + if (!pData->fProcessheader (((mng_handle)pData), pData->iWidth, pData->iHeight)) + MNG_ERROR (pData, MNG_APPMISCERROR); +#endif + } + + if (!pData->bHasDHDR) + pData->iImagelevel++; /* one level deeper */ + +#ifdef MNG_SUPPORT_DISPLAY + return mng_process_display_ihdr (pData); +#else + return MNG_NOERROR; +#endif /* MNG_SUPPORT_DISPLAY */ +} + +/* ************************************************************************** */ + +MNG_F_SPECIALFUNC (mng_debunk_plte) +{ + mng_pltep pPLTE = (mng_pltep)pChunk; + mng_uint32 iRawlen = *piRawlen; + mng_uint8p pRawdata = *ppRawdata; + /* length must be multiple of 3 */ + if (((iRawlen % 3) != 0) || (iRawlen > 768)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + /* this is the exact length */ + pPLTE->iEntrycount = iRawlen / 3; + + MNG_COPY (pPLTE->aEntries, pRawdata, iRawlen); + + *piRawlen = 0; + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +MNG_C_SPECIALFUNC (mng_special_plte) +{ /* multiple PLTE only inside BASI */ + if ((pData->bHasPLTE) && (!pData->bHasBASI)) + MNG_ERROR (pData, MNG_MULTIPLEERROR); + + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) + { /* only allowed for indexed-color or + rgb(a)-color! */ + if ((pData->iColortype != MNG_COLORTYPE_RGB ) && + (pData->iColortype != MNG_COLORTYPE_INDEXED) && + (pData->iColortype != MNG_COLORTYPE_RGBA ) ) + MNG_ERROR (pData, MNG_CHUNKNOTALLOWED); + /* empty only allowed if global present */ + if ((((mng_pltep)pChunk)->bEmpty) && (!pData->bHasglobalPLTE)) + MNG_ERROR (pData, MNG_CANNOTBEEMPTY); + } + else + { + if (((mng_pltep)pChunk)->bEmpty) /* cannot be empty as global! */ + MNG_ERROR (pData, MNG_CANNOTBEEMPTY); + } + + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) + pData->bHasPLTE = MNG_TRUE; /* got it! */ + else + pData->bHasglobalPLTE = MNG_TRUE; + + pData->iPLTEcount = ((mng_pltep)pChunk)->iEntrycount; + +#ifdef MNG_SUPPORT_DISPLAY + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) + { + mng_imagep pImage; + mng_imagedatap pBuf; + +#ifndef MNG_NO_DELTA_PNG + if (pData->bHasDHDR) /* processing delta-image ? */ + { /* store in object 0 !!! */ + pImage = (mng_imagep)pData->pObjzero; + pBuf = pImage->pImgbuf; + pBuf->bHasPLTE = MNG_TRUE; /* it's definitely got a PLTE now */ + pBuf->iPLTEcount = ((mng_pltep)pChunk)->iEntrycount; + MNG_COPY (pBuf->aPLTEentries, ((mng_pltep)pChunk)->aEntries, + sizeof (pBuf->aPLTEentries)); + } + else +#endif + { /* get the current object */ + pImage = (mng_imagep)pData->pCurrentobj; + if (!pImage) /* no object then dump it in obj 0 */ + pImage = (mng_imagep)pData->pObjzero; + + pBuf = pImage->pImgbuf; /* address the object buffer */ + pBuf->bHasPLTE = MNG_TRUE; /* and tell it it's got a PLTE now */ + + if (((mng_pltep)pChunk)->bEmpty) /* if empty, inherit from global */ + { + pBuf->iPLTEcount = pData->iGlobalPLTEcount; + MNG_COPY (pBuf->aPLTEentries, pData->aGlobalPLTEentries, + sizeof (pBuf->aPLTEentries)); + + if (pData->bHasglobalTRNS) /* also copy global tRNS ? */ + { + mng_uint32 iRawlen2 = pData->iGlobalTRNSrawlen; + mng_uint8p pRawdata2 = (mng_uint8p)(pData->aGlobalTRNSrawdata); + /* indicate tRNS available */ + pBuf->bHasTRNS = MNG_TRUE; + /* global length oke ? */ + if ((iRawlen2 == 0) || (iRawlen2 > pBuf->iPLTEcount)) + MNG_ERROR (pData, MNG_GLOBALLENGTHERR); + /* copy it */ + pBuf->iTRNScount = iRawlen2; + MNG_COPY (pBuf->aTRNSentries, pRawdata2, iRawlen2); + } + } + else + { /* store fields for future reference */ + pBuf->iPLTEcount = ((mng_pltep)pChunk)->iEntrycount; + MNG_COPY (pBuf->aPLTEentries, ((mng_pltep)pChunk)->aEntries, + sizeof (pBuf->aPLTEentries)); + } + } + } + else /* store as global */ + { + pData->iGlobalPLTEcount = ((mng_pltep)pChunk)->iEntrycount; + MNG_COPY (pData->aGlobalPLTEentries, ((mng_pltep)pChunk)->aEntries, + sizeof (pData->aGlobalPLTEentries)); + /* create an animation object */ + return mng_create_ani_plte (pData); + } +#endif /* MNG_SUPPORT_DISPLAY */ + + return MNG_NOERROR; /* done */ +} + +/* ************************************************************************** */ + +MNG_C_SPECIALFUNC (mng_special_idat) +{ +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasJHDR) && + (pData->iJHDRalphacompression != MNG_COMPRESSION_DEFLATE)) + MNG_ERROR (pData, MNG_SEQUENCEERROR); +#endif + /* not allowed for deltatype NO_CHANGE */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && ((pData->iDeltatype == MNG_DELTATYPE_NOCHANGE))) + MNG_ERROR (pData, MNG_CHUNKNOTALLOWED); +#endif + /* can only be empty in BASI-block! */ + if ((((mng_idatp)pChunk)->bEmpty) && (!pData->bHasBASI)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + /* indexed-color requires PLTE */ + if ((pData->bHasIHDR) && (pData->iColortype == 3) && (!pData->bHasPLTE)) + MNG_ERROR (pData, MNG_PLTEMISSING); + + pData->bHasIDAT = MNG_TRUE; /* got some IDAT now, don't we */ + + return MNG_NOERROR; /* done */ +} + +/* ************************************************************************** */ + +MNG_C_SPECIALFUNC (mng_special_iend) +{ /* IHDR-block requires IDAT */ + if ((pData->bHasIHDR) && (!pData->bHasIDAT)) + MNG_ERROR (pData, MNG_IDATMISSING); + + pData->iImagelevel--; /* one level up */ + +#ifdef MNG_SUPPORT_DISPLAY + { /* create an animation object */ + mng_retcode iRetcode = mng_create_ani_image (pData); + if (iRetcode) /* on error bail out */ + return iRetcode; + /* display processing */ + iRetcode = mng_process_display_iend (pData); + if (iRetcode) /* on error bail out */ + return iRetcode; + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_SUPPORT_DISPLAY + if (!pData->bTimerset) /* reset only if not broken !!! */ + { +#endif + /* IEND signals the end for most ... */ + pData->bHasIHDR = MNG_FALSE; + pData->bHasBASI = MNG_FALSE; + pData->bHasDHDR = MNG_FALSE; +#ifdef MNG_INCLUDE_JNG + pData->bHasJHDR = MNG_FALSE; + pData->bHasJSEP = MNG_FALSE; + pData->bHasJDAA = MNG_FALSE; + pData->bHasJDAT = MNG_FALSE; +#endif + pData->bHasPLTE = MNG_FALSE; + pData->bHasTRNS = MNG_FALSE; + pData->bHasGAMA = MNG_FALSE; + pData->bHasCHRM = MNG_FALSE; + pData->bHasSRGB = MNG_FALSE; + pData->bHasICCP = MNG_FALSE; + pData->bHasBKGD = MNG_FALSE; + pData->bHasIDAT = MNG_FALSE; +#ifdef MNG_SUPPORT_DISPLAY + } +#endif + + return MNG_NOERROR; /* done */ +} + +/* ************************************************************************** */ + +MNG_F_SPECIALFUNC (mng_debunk_trns) +{ + mng_trnsp pTRNS = (mng_trnsp)pChunk; + mng_uint32 iRawlen = *piRawlen; + mng_uint8p pRawdata = *ppRawdata; + + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) + { /* not global! */ + pTRNS->bGlobal = MNG_FALSE; + pTRNS->iType = pData->iColortype; + + if (iRawlen != 0) + { + switch (pData->iColortype) /* store fields */ + { + case 0: { /* gray */ + if (iRawlen != 2) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + pTRNS->iGray = mng_get_uint16 (pRawdata); + break; + } + case 2: { /* rgb */ + if (iRawlen != 6) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + pTRNS->iRed = mng_get_uint16 (pRawdata); + pTRNS->iGreen = mng_get_uint16 (pRawdata+2); + pTRNS->iBlue = mng_get_uint16 (pRawdata+4); + break; + } + case 3: { /* indexed */ + if (iRawlen > 256) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + pTRNS->iCount = iRawlen; + MNG_COPY (pTRNS->aEntries, pRawdata, iRawlen); + break; + } + } + } + } + else /* it's global! */ + { + if (iRawlen == 0) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + pTRNS->bGlobal = MNG_TRUE; + pTRNS->iType = 0; + pTRNS->iRawlen = iRawlen; + MNG_COPY (pTRNS->aRawdata, pRawdata, iRawlen); + + pData->iGlobalTRNSrawlen = iRawlen; + MNG_COPY (pData->aGlobalTRNSrawdata, pRawdata, iRawlen); + } + + *piRawlen = 0; + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +MNG_C_SPECIALFUNC (mng_special_trns) +{ /* multiple tRNS only inside BASI */ + if ((pData->bHasTRNS) && (!pData->bHasBASI)) + MNG_ERROR (pData, MNG_MULTIPLEERROR); + + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) + { /* not allowed with full alpha-channel */ + if ((pData->iColortype == 4) || (pData->iColortype == 6)) + MNG_ERROR (pData, MNG_CHUNKNOTALLOWED); + + if (!((mng_trnsp)pChunk)->bEmpty) /* filled ? */ + { +#ifdef MNG_SUPPORT_DISPLAY + if (pData->iColortype == 3) + { + mng_imagep pImage = (mng_imagep)pData->pCurrentobj; + mng_imagedatap pBuf; + + if (!pImage) /* no object then check obj 0 */ + pImage = (mng_imagep)pData->pObjzero; + + pBuf = pImage->pImgbuf; /* address object buffer */ + + if (((mng_trnsp)pChunk)->iCount > pBuf->iPLTEcount) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + } +#endif + } + else /* if empty there must be global stuff! */ + { + if (!pData->bHasglobalTRNS) + MNG_ERROR (pData, MNG_CANNOTBEEMPTY); + } + } + + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) + pData->bHasTRNS = MNG_TRUE; /* indicate tRNS available */ + else + pData->bHasglobalTRNS = MNG_TRUE; + +#ifdef MNG_SUPPORT_DISPLAY + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) + { + mng_imagep pImage; + mng_imagedatap pBuf; + mng_uint8p pRawdata2; + mng_uint32 iRawlen2; + +#ifndef MNG_NO_DELTA_PNG + if (pData->bHasDHDR) /* processing delta-image ? */ + { /* store in object 0 !!! */ +#if defined(MNG_NO_1_2_4BIT_SUPPORT) + mng_uint8 multiplier[]={0,255,85,0,17,0,0,0,1,0,0,0,0,0,0,0,1}; +#endif + pImage = (mng_imagep)pData->pObjzero; + pBuf = pImage->pImgbuf; /* address object buffer */ + pBuf->bHasTRNS = MNG_TRUE; /* tell it it's got a tRNS now */ + pBuf->iTRNSgray = 0; + pBuf->iTRNSred = 0; + pBuf->iTRNSgreen = 0; + pBuf->iTRNSblue = 0; + pBuf->iTRNScount = 0; + + switch (pData->iColortype) /* store fields for future reference */ + { + case 0: { /* gray */ + pBuf->iTRNSgray = ((mng_trnsp)pChunk)->iGray; +#if defined(MNG_NO_1_2_4BIT_SUPPORT) + pBuf->iTRNSgray *= multiplier[pData->iPNGdepth]; +#endif +#if defined(MNG_NO_16BIT_SUPPORT) + if (pData->iPNGmult == 2) + pBuf->iTRNSgray >>= 8; +#endif + break; + } + case 2: { /* rgb */ + pBuf->iTRNSred = ((mng_trnsp)pChunk)->iRed; + pBuf->iTRNSgreen = ((mng_trnsp)pChunk)->iGreen; + pBuf->iTRNSblue = ((mng_trnsp)pChunk)->iBlue; +#if defined(MNG_NO_16BIT_SUPPORT) + if (pData->iPNGmult == 2) + { + pBuf->iTRNSred >>= 8; + pBuf->iTRNSgreen >>= 8; + pBuf->iTRNSblue >>= 8; + } +#endif + break; + } + case 3: { /* indexed */ + pBuf->iTRNScount = ((mng_trnsp)pChunk)->iCount; + MNG_COPY (pBuf->aTRNSentries, + ((mng_trnsp)pChunk)->aEntries, + ((mng_trnsp)pChunk)->iCount); + break; + } + } + } + else +#endif + { /* address current object */ + pImage = (mng_imagep)pData->pCurrentobj; + + if (!pImage) /* no object then dump it in obj 0 */ + pImage = (mng_imagep)pData->pObjzero; + + pBuf = pImage->pImgbuf; /* address object buffer */ + pBuf->bHasTRNS = MNG_TRUE; /* and tell it it's got a tRNS now */ + pBuf->iTRNSgray = 0; + pBuf->iTRNSred = 0; + pBuf->iTRNSgreen = 0; + pBuf->iTRNSblue = 0; + pBuf->iTRNScount = 0; + + if (((mng_trnsp)pChunk)->bEmpty) /* if empty, inherit from global */ + { + iRawlen2 = pData->iGlobalTRNSrawlen; + pRawdata2 = (mng_ptr)(pData->aGlobalTRNSrawdata); + /* global length oke ? */ + if ((pData->iColortype == 0) && (iRawlen2 != 2)) + MNG_ERROR (pData, MNG_GLOBALLENGTHERR); + + if ((pData->iColortype == 2) && (iRawlen2 != 6)) + MNG_ERROR (pData, MNG_GLOBALLENGTHERR); + + if ((pData->iColortype == 3) && ((iRawlen2 == 0) || (iRawlen2 > pBuf->iPLTEcount))) + MNG_ERROR (pData, MNG_GLOBALLENGTHERR); + + switch (pData->iColortype) /* store fields for future reference */ + { + case 0: { /* gray */ + pBuf->iTRNSgray = mng_get_uint16 (pRawdata2); +#if defined(MNG_NO_16BIT_SUPPORT) + if (pData->iPNGmult == 2) + pBuf->iTRNSgray >>= 8; +#endif + break; + } + case 2: { /* rgb */ + pBuf->iTRNSred = mng_get_uint16 (pRawdata2); + pBuf->iTRNSgreen = mng_get_uint16 (pRawdata2+2); + pBuf->iTRNSblue = mng_get_uint16 (pRawdata2+4); +#if defined(MNG_NO_16BIT_SUPPORT) + if (pData->iPNGmult == 2) + { + pBuf->iTRNSred >>= 8; + pBuf->iTRNSgreen >>= 8; + pBuf->iTRNSblue >>= 8; + } +#endif + break; + } + case 3: { /* indexed */ + pBuf->iTRNScount = iRawlen2; + MNG_COPY (pBuf->aTRNSentries, pRawdata2, iRawlen2); + break; + } + } + } + else + { + switch (pData->iColortype) /* store fields for future reference */ + { + case 0: { /* gray */ + pBuf->iTRNSgray = ((mng_trnsp)pChunk)->iGray; +#if defined(MNG_NO_16BIT_SUPPORT) + if (pData->iPNGmult == 2) + pBuf->iTRNSgray >>= 8; +#endif + break; + } + case 2: { /* rgb */ + pBuf->iTRNSred = ((mng_trnsp)pChunk)->iRed; + pBuf->iTRNSgreen = ((mng_trnsp)pChunk)->iGreen; + pBuf->iTRNSblue = ((mng_trnsp)pChunk)->iBlue; +#if defined(MNG_NO_16BIT_SUPPORT) + if (pData->iPNGmult == 2) + { + pBuf->iTRNSred >>= 8; + pBuf->iTRNSgreen >>= 8; + pBuf->iTRNSblue >>= 8; + } +#endif + break; + } + case 3: { /* indexed */ + pBuf->iTRNScount = ((mng_trnsp)pChunk)->iCount; + MNG_COPY (pBuf->aTRNSentries, + ((mng_trnsp)pChunk)->aEntries, + ((mng_trnsp)pChunk)->iCount); + break; + } + } + } + } + } + else + { /* create an animation object */ + return mng_create_ani_trns (pData); + } +#endif /* MNG_SUPPORT_DISPLAY */ + + return MNG_NOERROR; /* done */ +} + +/* ************************************************************************** */ + +MNG_C_SPECIALFUNC (mng_special_gama) +{ +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + pData->bHasGAMA = MNG_TRUE; /* indicate we've got it */ + else + pData->bHasglobalGAMA = (mng_bool)!((mng_gamap)pChunk)->bEmpty; + +#ifdef MNG_SUPPORT_DISPLAY +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + { + mng_imagep pImage; + +#ifndef MNG_NO_DELTA_PNG + if (pData->bHasDHDR) /* update delta image ? */ + pImage = (mng_imagep)pData->pObjzero; + else +#endif + { + pImage = (mng_imagep)pData->pCurrentobj; + if (!pImage) /* no object then dump it in obj 0 */ + pImage = (mng_imagep)pData->pObjzero; + } + /* store for color-processing routines */ + pImage->pImgbuf->iGamma = ((mng_gamap)pChunk)->iGamma; + pImage->pImgbuf->bHasGAMA = MNG_TRUE; + } + else + { /* store as global */ + if (!((mng_gamap)pChunk)->bEmpty) + pData->iGlobalGamma = ((mng_gamap)pChunk)->iGamma; + /* create an animation object */ + return mng_create_ani_gama (pData, pChunk); + } +#endif /* MNG_SUPPORT_DISPLAY */ + + return MNG_NOERROR; /* done */ +} + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_cHRM +MNG_C_SPECIALFUNC (mng_special_chrm) +{ +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + pData->bHasCHRM = MNG_TRUE; /* indicate we've got it */ + else + pData->bHasglobalCHRM = (mng_bool)!((mng_chrmp)pChunk)->bEmpty; + +#ifdef MNG_SUPPORT_DISPLAY + { +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + { + mng_imagep pImage; + mng_imagedatap pBuf; + +#ifndef MNG_NO_DELTA_PNG + if (pData->bHasDHDR) /* update delta image ? */ + pImage = (mng_imagep)pData->pObjzero; + else +#endif + { + pImage = (mng_imagep)pData->pCurrentobj; + if (!pImage) /* no object then dump it in obj 0 */ + pImage = (mng_imagep)pData->pObjzero; + } + + pBuf = pImage->pImgbuf; /* address object buffer */ + pBuf->bHasCHRM = MNG_TRUE; /* and tell it it's got a CHRM now */ + /* store for color-processing routines */ + pBuf->iWhitepointx = ((mng_chrmp)pChunk)->iWhitepointx; + pBuf->iWhitepointy = ((mng_chrmp)pChunk)->iWhitepointy; + pBuf->iPrimaryredx = ((mng_chrmp)pChunk)->iRedx; + pBuf->iPrimaryredy = ((mng_chrmp)pChunk)->iRedy; + pBuf->iPrimarygreenx = ((mng_chrmp)pChunk)->iGreenx; + pBuf->iPrimarygreeny = ((mng_chrmp)pChunk)->iGreeny; + pBuf->iPrimarybluex = ((mng_chrmp)pChunk)->iBluex; + pBuf->iPrimarybluey = ((mng_chrmp)pChunk)->iBluey; + } + else + { /* store as global */ + if (!((mng_chrmp)pChunk)->bEmpty) + { + pData->iGlobalWhitepointx = ((mng_chrmp)pChunk)->iWhitepointx; + pData->iGlobalWhitepointy = ((mng_chrmp)pChunk)->iWhitepointy; + pData->iGlobalPrimaryredx = ((mng_chrmp)pChunk)->iRedx; + pData->iGlobalPrimaryredy = ((mng_chrmp)pChunk)->iRedy; + pData->iGlobalPrimarygreenx = ((mng_chrmp)pChunk)->iGreenx; + pData->iGlobalPrimarygreeny = ((mng_chrmp)pChunk)->iGreeny; + pData->iGlobalPrimarybluex = ((mng_chrmp)pChunk)->iBluex; + pData->iGlobalPrimarybluey = ((mng_chrmp)pChunk)->iBluey; + } + /* create an animation object */ + return mng_create_ani_chrm (pData, pChunk); + } + } +#endif /* MNG_SUPPORT_DISPLAY */ + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +MNG_C_SPECIALFUNC (mng_special_srgb) +{ +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + pData->bHasSRGB = MNG_TRUE; /* indicate we've got it */ + else + pData->bHasglobalSRGB = (mng_bool)!((mng_srgbp)pChunk)->bEmpty; + +#ifdef MNG_SUPPORT_DISPLAY +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + { + mng_imagep pImage; + +#ifndef MNG_NO_DELTA_PNG + if (pData->bHasDHDR) /* update delta image ? */ + pImage = (mng_imagep)pData->pObjzero; + else +#endif + { + pImage = (mng_imagep)pData->pCurrentobj; + if (!pImage) /* no object then dump it in obj 0 */ + pImage = (mng_imagep)pData->pObjzero; + } + /* store for color-processing routines */ + pImage->pImgbuf->iRenderingintent = ((mng_srgbp)pChunk)->iRenderingintent; + pImage->pImgbuf->bHasSRGB = MNG_TRUE; + } + else + { /* store as global */ + if (!((mng_srgbp)pChunk)->bEmpty) + pData->iGlobalRendintent = ((mng_srgbp)pChunk)->iRenderingintent; + /* create an animation object */ + return mng_create_ani_srgb (pData, pChunk); + } +#endif /* MNG_SUPPORT_DISPLAY */ + + return MNG_NOERROR; /* done */ +} + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_iCCP +MNG_C_SPECIALFUNC (mng_special_iccp) +{ + mng_retcode iRetcode; + mng_chunk_headerp pDummy; + +#ifdef MNG_CHECK_BAD_ICCP /* Check for bad iCCP chunk */ + if (!strncmp (((mng_iccpp)pChunk)->zName, "Photoshop ICC profile", 21)) + { + if (((mng_iccpp)pChunk)->iProfilesize == 2615) /* is it the sRGB profile ? */ + { + mng_chunk_header chunk_srgb; + mng_get_chunkheader (MNG_UINT_sRGB, &chunk_srgb); + /* pretend it's an sRGB chunk then ! */ + iRetcode = mng_read_general (pData, &chunk_srgb, 1, (mng_ptr)"0", &pDummy); + if (iRetcode) /* on error bail out */ + return iRetcode; + + pDummy->fCleanup (pData, pDummy); + } + } + else + { +#endif /* MNG_CHECK_BAD_ICCP */ + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + pData->bHasICCP = MNG_TRUE; /* indicate we've got it */ + else + pData->bHasglobalICCP = (mng_bool)!((mng_iccpp)pChunk)->bEmpty; + +#ifdef MNG_SUPPORT_DISPLAY +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + { + mng_imagep pImage; + +#ifndef MNG_NO_DELTA_PNG + if (pData->bHasDHDR) /* update delta image ? */ + { /* store in object 0 ! */ + pImage = (mng_imagep)pData->pObjzero; + + if (pImage->pImgbuf->pProfile) /* profile existed ? */ + MNG_FREEX (pData, pImage->pImgbuf->pProfile, pImage->pImgbuf->iProfilesize); + /* allocate a buffer & copy it */ + MNG_ALLOC (pData, pImage->pImgbuf->pProfile, ((mng_iccpp)pChunk)->iProfilesize); + MNG_COPY (pImage->pImgbuf->pProfile, ((mng_iccpp)pChunk)->pProfile, ((mng_iccpp)pChunk)->iProfilesize); + /* store its length as well */ + pImage->pImgbuf->iProfilesize = ((mng_iccpp)pChunk)->iProfilesize; + pImage->pImgbuf->bHasICCP = MNG_TRUE; + } + else +#endif + { + pImage = (mng_imagep)pData->pCurrentobj; + + if (!pImage) /* no object then dump it in obj 0 */ + pImage = (mng_imagep)pData->pObjzero; + + if (pImage->pImgbuf->pProfile) /* profile existed ? */ + MNG_FREEX (pData, pImage->pImgbuf->pProfile, pImage->pImgbuf->iProfilesize); + /* allocate a buffer & copy it */ + MNG_ALLOC (pData, pImage->pImgbuf->pProfile, ((mng_iccpp)pChunk)->iProfilesize); + MNG_COPY (pImage->pImgbuf->pProfile, ((mng_iccpp)pChunk)->pProfile, ((mng_iccpp)pChunk)->iProfilesize); + /* store its length as well */ + pImage->pImgbuf->iProfilesize = ((mng_iccpp)pChunk)->iProfilesize; + pImage->pImgbuf->bHasICCP = MNG_TRUE; + } + } + else + { /* store as global */ + if (pData->pGlobalProfile) /* did we have a global profile ? */ + MNG_FREEX (pData, pData->pGlobalProfile, pData->iGlobalProfilesize); + + if (((mng_iccpp)pChunk)->bEmpty) /* empty chunk ? */ + { + pData->iGlobalProfilesize = 0; /* reset to null */ + pData->pGlobalProfile = MNG_NULL; + } + else + { /* allocate a global buffer & copy it */ + MNG_ALLOC (pData, pData->pGlobalProfile, ((mng_iccpp)pChunk)->iProfilesize); + MNG_COPY (pData->pGlobalProfile, ((mng_iccpp)pChunk)->pProfile, ((mng_iccpp)pChunk)->iProfilesize); + /* store its length as well */ + pData->iGlobalProfilesize = ((mng_iccpp)pChunk)->iProfilesize; + } + /* create an animation object */ + return mng_create_ani_iccp (pData, pChunk); + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_CHECK_BAD_ICCP + } +#endif + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_tEXt +MNG_C_SPECIALFUNC (mng_special_text) +{ + if (pData->fProcesstext) /* inform the application ? */ + { + mng_bool bOke = pData->fProcesstext ((mng_handle)pData, MNG_TYPE_TEXT, + ((mng_textp)pChunk)->zKeyword, + ((mng_textp)pChunk)->zText, 0, 0); + if (!bOke) + MNG_ERROR (pData, MNG_APPMISCERROR); + } + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_zTXt +MNG_C_SPECIALFUNC (mng_special_ztxt) +{ + if (pData->fProcesstext) /* inform the application ? */ + { + mng_bool bOke = pData->fProcesstext ((mng_handle)pData, MNG_TYPE_ZTXT, + ((mng_ztxtp)pChunk)->zKeyword, + ((mng_ztxtp)pChunk)->zText, 0, 0); + if (!bOke) + MNG_ERROR (pData, MNG_APPMISCERROR); + } + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_iTXt +MNG_F_SPECIALFUNC (mng_deflate_itxt) +{ + mng_itxtp pITXT = (mng_itxtp)pChunk; + mng_uint32 iBufsize = 0; + mng_uint8p pBuf = 0; + mng_uint32 iTextlen = 0; + + if (pITXT->iCompressionflag) /* decompress the text ? */ + { + mng_retcode iRetcode = mng_inflate_buffer (pData, *ppRawdata, *piRawlen, + &pBuf, &iBufsize, &iTextlen); + + if (iRetcode) /* on error bail out */ + { /* don't forget to drop the temp buffer */ + MNG_FREEX (pData, pBuf, iBufsize); + return iRetcode; + } + + MNG_ALLOC (pData, pITXT->zText, iTextlen+1); + MNG_COPY (pITXT->zText, pBuf, iTextlen); + + pITXT->iTextsize = iTextlen; + + MNG_FREEX (pData, pBuf, iBufsize); + + } else { + + MNG_ALLOC (pData, pITXT->zText, (*piRawlen)+1); + MNG_COPY (pITXT->zText, *ppRawdata, *piRawlen); + + pITXT->iTextsize = *piRawlen; + } + + *piRawlen = 0; + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_iTXt +MNG_C_SPECIALFUNC (mng_special_itxt) +{ + if (pData->fProcesstext) /* inform the application ? */ + { + mng_bool bOke = pData->fProcesstext ((mng_handle)pData, MNG_TYPE_ITXT, + ((mng_itxtp)pChunk)->zKeyword, + ((mng_itxtp)pChunk)->zText, + ((mng_itxtp)pChunk)->zLanguage, + ((mng_itxtp)pChunk)->zTranslation); + if (!bOke) + MNG_ERROR (pData, MNG_APPMISCERROR); + } + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_bKGD +MNG_C_SPECIALFUNC (mng_special_bkgd) +{ +#ifdef MNG_SUPPORT_DISPLAY + mng_imagep pImage = (mng_imagep)pData->pCurrentobj; + mng_imagedatap pBuf; +#endif + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + pData->bHasBKGD = MNG_TRUE; /* indicate bKGD available */ + else + pData->bHasglobalBKGD = (mng_bool)!(((mng_bkgdp)pChunk)->bEmpty); + +#ifdef MNG_SUPPORT_DISPLAY + if (!pImage) /* if no object dump it in obj 0 */ + pImage = (mng_imagep)pData->pObjzero; + pBuf = pImage->pImgbuf; /* address object buffer */ + +#ifdef MNG_INCLUDE_JNG + if (pData->bHasJHDR) + { + pBuf->bHasBKGD = MNG_TRUE; /* tell the object it's got bKGD now */ + + switch (pData->iJHDRcolortype) /* store fields for future reference */ + { + case 8 : ; /* gray */ + case 12 : { /* graya */ + pBuf->iBKGDgray = ((mng_bkgdp)pChunk)->iGray; + break; + } + case 10 : ; /* rgb */ + case 14 : { /* rgba */ + pBuf->iBKGDred = ((mng_bkgdp)pChunk)->iRed; + pBuf->iBKGDgreen = ((mng_bkgdp)pChunk)->iGreen; + pBuf->iBKGDblue = ((mng_bkgdp)pChunk)->iBlue; + break; + } + } + } + else +#endif /* MNG_INCLUDE_JNG */ + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) + { + pBuf->bHasBKGD = MNG_TRUE; /* tell the object it's got bKGD now */ + + switch (pData->iColortype) /* store fields for future reference */ + { + case 0 : ; /* gray */ + case 4 : { /* graya */ + pBuf->iBKGDgray = ((mng_bkgdp)pChunk)->iGray; + break; + } + case 2 : ; /* rgb */ + case 6 : { /* rgba */ + pBuf->iBKGDred = ((mng_bkgdp)pChunk)->iRed; + pBuf->iBKGDgreen = ((mng_bkgdp)pChunk)->iGreen; + pBuf->iBKGDblue = ((mng_bkgdp)pChunk)->iBlue; + break; + } + case 3 : { /* indexed */ + pBuf->iBKGDindex = ((mng_bkgdp)pChunk)->iIndex; + break; + } + } + } + else /* store as global */ + { + if (!(((mng_bkgdp)pChunk)->bEmpty)) + { + pData->iGlobalBKGDred = ((mng_bkgdp)pChunk)->iRed; + pData->iGlobalBKGDgreen = ((mng_bkgdp)pChunk)->iGreen; + pData->iGlobalBKGDblue = ((mng_bkgdp)pChunk)->iBlue; + } + /* create an animation object */ + return mng_create_ani_bkgd (pData); + } +#endif /* MNG_SUPPORT_DISPLAY */ + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_pHYs +MNG_C_SPECIALFUNC (mng_special_phys) +{ +#ifdef MNG_SUPPORT_DISPLAY + { + + + /* TODO: something !!! */ + + + } +#endif /* MNG_SUPPORT_DISPLAY */ + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_sBIT +MNG_C_SPECIALFUNC (mng_special_sbit) +{ +#ifdef MNG_SUPPORT_DISPLAY + { + + + /* TODO: something !!! */ + + + } +#endif /* MNG_SUPPORT_DISPLAY */ + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_sPLT +MNG_F_SPECIALFUNC (mng_splt_entries) +{ + mng_spltp pSPLT = (mng_spltp)pChunk; + mng_uint32 iRawlen = *piRawlen; + mng_uint8p pRawdata = *ppRawdata; + + if ((pSPLT->iSampledepth != MNG_BITDEPTH_8 ) && + (pSPLT->iSampledepth != MNG_BITDEPTH_16) ) + MNG_ERROR (pData, MNG_INVSAMPLEDEPTH); + /* check remaining length */ + if ( ((pSPLT->iSampledepth == MNG_BITDEPTH_8 ) && (iRawlen % 6 != 0)) || + ((pSPLT->iSampledepth == MNG_BITDEPTH_16) && (iRawlen % 10 != 0)) ) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + if (pSPLT->iSampledepth == MNG_BITDEPTH_8) + pSPLT->iEntrycount = iRawlen / 6; + else + pSPLT->iEntrycount = iRawlen / 10; + + if (iRawlen) + { + MNG_ALLOC (pData, pSPLT->pEntries, iRawlen); + MNG_COPY (pSPLT->pEntries, pRawdata, iRawlen); + } + + *piRawlen = 0; + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_sPLT +MNG_C_SPECIALFUNC (mng_special_splt) +{ +#ifdef MNG_SUPPORT_DISPLAY + { + + + /* TODO: something !!! */ + + + } +#endif /* MNG_SUPPORT_DISPLAY */ + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_hIST +MNG_F_SPECIALFUNC (mng_hist_entries) +{ + mng_histp pHIST = (mng_histp)pChunk; + mng_uint32 iRawlen = *piRawlen; + mng_uint8p pRawdata = *ppRawdata; + mng_uint32 iX; + + if ( ((iRawlen & 0x01) != 0) || ((iRawlen >> 1) != pData->iPLTEcount) ) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + pHIST->iEntrycount = iRawlen >> 1; + + for (iX = 0; iX < pHIST->iEntrycount; iX++) + { + pHIST->aEntries[iX] = mng_get_uint16 (pRawdata); + pRawdata += 2; + } + + *piRawlen = 0; + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_hIST +MNG_C_SPECIALFUNC (mng_special_hist) +{ +#ifdef MNG_SUPPORT_DISPLAY + { + + + /* TODO: something !!! */ + + + } +#endif /* MNG_SUPPORT_DISPLAY */ + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_tIME +MNG_C_SPECIALFUNC (mng_special_time) +{ +/* if (pData->fProcesstime) */ /* inform the application ? */ +/* { + + pData->fProcesstime ((mng_handle)pData, ); + } */ + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ +/* ************************************************************************** */ +/* JNG chunks */ + +#ifdef MNG_INCLUDE_JNG +MNG_C_SPECIALFUNC (mng_special_jhdr) +{ + if ((pData->eSigtype == mng_it_jng) && (pData->iChunkseq > 1)) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + /* inside a JHDR-IEND block now */ + pData->bHasJHDR = MNG_TRUE; + /* and store interesting fields */ + pData->iDatawidth = ((mng_jhdrp)pChunk)->iWidth; + pData->iDataheight = ((mng_jhdrp)pChunk)->iHeight; + pData->iJHDRcolortype = ((mng_jhdrp)pChunk)->iColortype; + pData->iJHDRimgbitdepth = ((mng_jhdrp)pChunk)->iImagesampledepth; + pData->iJHDRimgcompression = ((mng_jhdrp)pChunk)->iImagecompression; + pData->iJHDRimginterlace = ((mng_jhdrp)pChunk)->iImageinterlace; + pData->iJHDRalphabitdepth = ((mng_jhdrp)pChunk)->iAlphasampledepth; + pData->iJHDRalphacompression = ((mng_jhdrp)pChunk)->iAlphacompression; + pData->iJHDRalphafilter = ((mng_jhdrp)pChunk)->iAlphafilter; + pData->iJHDRalphainterlace = ((mng_jhdrp)pChunk)->iAlphainterlace; + +#if defined(MNG_NO_1_2_4BIT_SUPPORT) || defined(MNG_NO_16BIT_SUPPORT) + pData->iPNGmult = 1; + pData->iPNGdepth = pData->iJHDRalphabitdepth; +#endif + +#ifdef MNG_NO_1_2_4BIT_SUPPORT + if (pData->iJHDRalphabitdepth < 8) + pData->iJHDRalphabitdepth = 8; +#endif + +#ifdef MNG_NO_16BIT_SUPPORT + if (pData->iJHDRalphabitdepth > 8) + { + pData->iPNGmult = 2; + pData->iJHDRalphabitdepth = 8; + } +#endif + /* parameter validity checks */ + if ((pData->iJHDRcolortype != MNG_COLORTYPE_JPEGGRAY ) && + (pData->iJHDRcolortype != MNG_COLORTYPE_JPEGCOLOR ) && + (pData->iJHDRcolortype != MNG_COLORTYPE_JPEGGRAYA ) && + (pData->iJHDRcolortype != MNG_COLORTYPE_JPEGCOLORA) ) + MNG_ERROR (pData, MNG_INVALIDCOLORTYPE); + + if ((pData->iJHDRimgbitdepth != MNG_BITDEPTH_JPEG8 ) && + (pData->iJHDRimgbitdepth != MNG_BITDEPTH_JPEG12 ) && + (pData->iJHDRimgbitdepth != MNG_BITDEPTH_JPEG8AND12) ) + MNG_ERROR (pData, MNG_INVALIDBITDEPTH); + + if ((pData->iJHDRcolortype == MNG_COLORTYPE_JPEGGRAYA ) || + (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGCOLORA) ) + { + if ((pData->iJHDRalphabitdepth != MNG_BITDEPTH_8 ) +#ifndef MNG_NO_1_2_4BIT_SUPPORT + && (pData->iJHDRalphabitdepth != MNG_BITDEPTH_1 ) && + (pData->iJHDRalphabitdepth != MNG_BITDEPTH_2 ) && + (pData->iJHDRalphabitdepth != MNG_BITDEPTH_4 ) +#endif +#ifndef MNG_NO_16BIT_SUPPORT + && (pData->iJHDRalphabitdepth != MNG_BITDEPTH_16) +#endif + ) + MNG_ERROR (pData, MNG_INVALIDBITDEPTH); + + if ((pData->iJHDRalphacompression != MNG_COMPRESSION_DEFLATE ) && + (pData->iJHDRalphacompression != MNG_COMPRESSION_BASELINEJPEG) ) + MNG_ERROR (pData, MNG_INVALIDCOMPRESS); + + if ((pData->iJHDRalphacompression == MNG_COMPRESSION_BASELINEJPEG) && + (pData->iJHDRalphabitdepth != MNG_BITDEPTH_8 ) ) + MNG_ERROR (pData, MNG_INVALIDBITDEPTH); + +#if defined(FILTER192) || defined(FILTER193) + if ((pData->iJHDRalphafilter != MNG_FILTER_ADAPTIVE ) && +#if defined(FILTER192) && defined(FILTER193) + (pData->iJHDRalphafilter != MNG_FILTER_DIFFERING) && + (pData->iJHDRalphafilter != MNG_FILTER_NOFILTER ) ) +#else +#ifdef FILTER192 + (pData->iJHDRalphafilter != MNG_FILTER_DIFFERING) ) +#else + (pData->iJHDRalphafilter != MNG_FILTER_NOFILTER ) ) +#endif +#endif + MNG_ERROR (pData, MNG_INVALIDFILTER); +#else + if (pData->iJHDRalphafilter) + MNG_ERROR (pData, MNG_INVALIDFILTER); +#endif + + } + else + { + if (pData->iJHDRalphabitdepth) + MNG_ERROR (pData, MNG_INVALIDBITDEPTH); + if (pData->iJHDRalphacompression) + MNG_ERROR (pData, MNG_INVALIDCOMPRESS); + if (pData->iJHDRalphafilter) + MNG_ERROR (pData, MNG_INVALIDFILTER); + if (pData->iJHDRalphainterlace) + MNG_ERROR (pData, MNG_INVALIDINTERLACE); + } + + if (!pData->bHasheader) /* first chunk ? */ + { + pData->bHasheader = MNG_TRUE; /* we've got a header */ + pData->eImagetype = mng_it_jng; /* then this must be a JNG */ + pData->iWidth = ((mng_jhdrp)pChunk)->iWidth; + pData->iHeight = ((mng_jhdrp)pChunk)->iHeight; + /* predict alpha-depth ! */ + if ((pData->iJHDRcolortype == MNG_COLORTYPE_JPEGGRAYA ) || + (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGCOLORA) ) + pData->iAlphadepth = pData->iJHDRalphabitdepth; + else + pData->iAlphadepth = 0; + /* fits on maximum canvas ? */ + if ((pData->iWidth > pData->iMaxwidth) || (pData->iHeight > pData->iMaxheight)) + MNG_WARNING (pData, MNG_IMAGETOOLARGE); + + if (pData->fProcessheader) /* inform the app ? */ + if (!pData->fProcessheader (((mng_handle)pData), pData->iWidth, pData->iHeight)) + MNG_ERROR (pData, MNG_APPMISCERROR); + + } + + pData->iColortype = 0; /* fake grayscale for other routines */ + pData->iImagelevel++; /* one level deeper */ + +#ifdef MNG_SUPPORT_DISPLAY + { + mng_retcode iRetcode = mng_process_display_jhdr (pData); + if (iRetcode) /* on error bail out */ + return iRetcode; + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_NO_16BIT_SUPPORT + if (((mng_jhdrp)pChunk)->iAlphasampledepth > 8) + ((mng_jhdrp)pChunk)->iAlphasampledepth = 8; +#endif + + return MNG_NOERROR; /* done */ +} +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +MNG_C_SPECIALFUNC (mng_special_jdaa) +{ + if (pData->iJHDRalphacompression != MNG_COMPRESSION_BASELINEJPEG) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + pData->bHasJDAA = MNG_TRUE; /* got some JDAA now, don't we */ + return MNG_NOERROR; +} +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +MNG_C_SPECIALFUNC (mng_special_jdat) +{ + pData->bHasJDAT = MNG_TRUE; /* got some JDAT now, don't we */ + return MNG_NOERROR; +} +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +MNG_C_SPECIALFUNC (mng_special_jsep) +{ + pData->bHasJSEP = MNG_TRUE; /* indicate we've had the 8-/12-bit separator */ + return MNG_NOERROR; +} +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ +/* ************************************************************************** */ +/* MNG chunks */ + +MNG_C_SPECIALFUNC (mng_special_mhdr) +{ + if (pData->bHasheader) /* can only be the first chunk! */ + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + pData->bHasMHDR = MNG_TRUE; /* oh boy, a real MNG */ + pData->bHasheader = MNG_TRUE; /* we've got a header */ + pData->eImagetype = mng_it_mng; /* fill header fields */ + pData->iWidth = ((mng_mhdrp)pChunk)->iWidth; + pData->iHeight = ((mng_mhdrp)pChunk)->iHeight; + pData->iTicks = ((mng_mhdrp)pChunk)->iTicks; + pData->iLayercount = ((mng_mhdrp)pChunk)->iLayercount; + pData->iFramecount = ((mng_mhdrp)pChunk)->iFramecount; + pData->iPlaytime = ((mng_mhdrp)pChunk)->iPlaytime; + pData->iSimplicity = ((mng_mhdrp)pChunk)->iSimplicity; +#ifndef MNG_NO_OLD_VERSIONS + pData->bPreDraft48 = MNG_FALSE; +#endif + /* predict alpha-depth */ + if ((pData->iSimplicity & 0x00000001) == 0) +#ifndef MNG_NO_16BIT_SUPPORT + pData->iAlphadepth = 16; /* no indicators = assume the worst */ +#else + pData->iAlphadepth = 8; /* anything else = assume the worst */ +#endif + else + if ((pData->iSimplicity & 0x00000008) == 0) + pData->iAlphadepth = 0; /* no transparency at all */ + else + if ((pData->iSimplicity & 0x00000140) == 0x00000040) + pData->iAlphadepth = 1; /* no semi-transparency guaranteed */ + else +#ifndef MNG_NO_16BIT_SUPPORT + pData->iAlphadepth = 16; /* anything else = assume the worst */ +#else + pData->iAlphadepth = 8; /* anything else = assume the worst */ +#endif + +#ifdef MNG_INCLUDE_JNG /* can we handle the complexity ? */ + if (pData->iSimplicity & 0x0000FC00) +#else + if (pData->iSimplicity & 0x0000FC10) +#endif + MNG_ERROR (pData, MNG_MNGTOOCOMPLEX); + /* fits on maximum canvas ? */ + if ((pData->iWidth > pData->iMaxwidth) || (pData->iHeight > pData->iMaxheight)) + MNG_WARNING (pData, MNG_IMAGETOOLARGE); + + if (pData->fProcessheader) /* inform the app ? */ + if (!pData->fProcessheader (((mng_handle)pData), pData->iWidth, pData->iHeight)) + MNG_ERROR (pData, MNG_APPMISCERROR); + + pData->iImagelevel++; /* one level deeper */ + + return MNG_NOERROR; /* done */ +} + +/* ************************************************************************** */ + +MNG_C_SPECIALFUNC (mng_special_mend) +{ +#ifdef MNG_SUPPORT_DISPLAY + { /* do something */ + mng_retcode iRetcode = mng_process_display_mend (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + if (!pData->iTotalframes) /* save totals */ + pData->iTotalframes = pData->iFrameseq; + if (!pData->iTotallayers) + pData->iTotallayers = pData->iLayerseq; + if (!pData->iTotalplaytime) + pData->iTotalplaytime = pData->iFrametime; + } +#endif /* MNG_SUPPORT_DISPLAY */ + + pData->bHasMHDR = MNG_FALSE; /* end of the line, bro! */ + + return MNG_NOERROR; /* done */ +} + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_LOOP +MNG_F_SPECIALFUNC (mng_debunk_loop) +{ + mng_loopp pLOOP = (mng_loopp)pChunk; + mng_uint32 iRawlen = *piRawlen; + mng_uint8p pRawdata = *ppRawdata; + + if (iRawlen >= 5) /* length checks */ + { + if (iRawlen >= 6) + { + if ((iRawlen - 6) % 4 != 0) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + } + } + else + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + if (iRawlen >= 5) /* store the fields */ + { + pLOOP->iLevel = *pRawdata; + +#ifndef MNG_NO_OLD_VERSIONS + if (pData->bPreDraft48) + { + pLOOP->iTermination = *(pRawdata+1); + pLOOP->iRepeat = mng_get_uint32 (pRawdata+2); + } + else +#endif + { + pLOOP->iRepeat = mng_get_uint32 (pRawdata+1); + } + + if (iRawlen >= 6) + { +#ifndef MNG_NO_OLD_VERSIONS + if (!pData->bPreDraft48) +#endif + pLOOP->iTermination = *(pRawdata+5); + + if (iRawlen >= 10) + { + pLOOP->iItermin = mng_get_uint32 (pRawdata+6); + +#ifndef MNG_NO_LOOP_SIGNALS_SUPPORTED + if (iRawlen >= 14) + { + pLOOP->iItermax = mng_get_uint32 (pRawdata+10); + pLOOP->iCount = (iRawlen - 14) / 4; + + if (pLOOP->iCount) + { + MNG_ALLOC (pData, pLOOP->pSignals, pLOOP->iCount << 2); + +#ifndef MNG_BIGENDIAN_SUPPORTED + { + mng_uint32 iX; + mng_uint8p pIn = pRawdata + 14; + mng_uint32p pOut = (mng_uint32p)pLOOP->pSignals; + + for (iX = 0; iX < pLOOP->iCount; iX++) + { + *pOut++ = mng_get_uint32 (pIn); + pIn += 4; + } + } +#else + MNG_COPY (pLOOP->pSignals, pRawdata + 14, pLOOP->iCount << 2); +#endif /* !MNG_BIGENDIAN_SUPPORTED */ + } + } +#endif + } + } + } + + *piRawlen = 0; + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_LOOP +MNG_C_SPECIALFUNC (mng_special_loop) +{ + if (!pData->bCacheplayback) /* must store playback info to work!! */ + MNG_ERROR (pData, MNG_LOOPWITHCACHEOFF); + +#ifdef MNG_SUPPORT_DISPLAY + { + mng_retcode iRetcode; + + pData->bHasLOOP = MNG_TRUE; /* indicate we're inside a loop */ + /* create the LOOP ani-object */ + iRetcode = mng_create_ani_loop (pData, pChunk); + if (iRetcode) /* on error bail out */ + return iRetcode; + /* skip till matching ENDL if iteration=0 */ + if ((!pData->bSkipping) && (((mng_loopp)pChunk)->iRepeat == 0)) + pData->bSkipping = MNG_TRUE; + } +#endif /* MNG_SUPPORT_DISPLAY */ + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_LOOP +MNG_C_SPECIALFUNC (mng_special_endl) +{ +#ifdef MNG_SUPPORT_DISPLAY + if (pData->bHasLOOP) /* are we really processing a loop ? */ + { + mng_uint8 iLevel = ((mng_endlp)pChunk)->iLevel; + /* create an ENDL animation object */ + return mng_create_ani_endl (pData, iLevel); + } + else + MNG_ERROR (pData, MNG_NOMATCHINGLOOP); +#endif /* MNG_SUPPORT_DISPLAY */ + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_DEFI +MNG_C_SPECIALFUNC (mng_special_defi) +{ +#ifdef MNG_SUPPORT_DISPLAY + mng_retcode iRetcode; + + pData->iDEFIobjectid = ((mng_defip)pChunk)->iObjectid; + pData->bDEFIhasdonotshow = ((mng_defip)pChunk)->bHasdonotshow; + pData->iDEFIdonotshow = ((mng_defip)pChunk)->iDonotshow; + pData->bDEFIhasconcrete = ((mng_defip)pChunk)->bHasconcrete; + pData->iDEFIconcrete = ((mng_defip)pChunk)->iConcrete; + pData->bDEFIhasloca = ((mng_defip)pChunk)->bHasloca; + pData->iDEFIlocax = ((mng_defip)pChunk)->iXlocation; + pData->iDEFIlocay = ((mng_defip)pChunk)->iYlocation; + pData->bDEFIhasclip = ((mng_defip)pChunk)->bHasclip; + pData->iDEFIclipl = ((mng_defip)pChunk)->iLeftcb; + pData->iDEFIclipr = ((mng_defip)pChunk)->iRightcb; + pData->iDEFIclipt = ((mng_defip)pChunk)->iTopcb; + pData->iDEFIclipb = ((mng_defip)pChunk)->iBottomcb; + /* create an animation object */ + iRetcode = mng_create_ani_defi (pData); + if (!iRetcode) /* do display processing */ + iRetcode = mng_process_display_defi (pData); + return iRetcode; +#else + return MNG_NOERROR; /* done */ +#endif /* MNG_SUPPORT_DISPLAY */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_BASI +MNG_C_SPECIALFUNC (mng_special_basi) +{ + pData->bHasBASI = MNG_TRUE; /* inside a BASI-IEND block now */ + /* store interesting fields */ + pData->iDatawidth = ((mng_basip)pChunk)->iWidth; + pData->iDataheight = ((mng_basip)pChunk)->iHeight; + pData->iBitdepth = ((mng_basip)pChunk)->iBitdepth; + pData->iColortype = ((mng_basip)pChunk)->iColortype; + pData->iCompression = ((mng_basip)pChunk)->iCompression; + pData->iFilter = ((mng_basip)pChunk)->iFilter; + pData->iInterlace = ((mng_basip)pChunk)->iInterlace; + +#if defined(MNG_NO_1_2_4BIT_SUPPORT) || defined(MNG_NO_16BIT_SUPPORT) + pData->iPNGmult = 1; + pData->iPNGdepth = pData->iBitdepth; +#endif + +#ifdef MNG_NO_1_2_4BIT_SUPPORT + if (pData->iBitdepth < 8) + pData->iBitdepth = 8; +#endif +#ifdef MNG_NO_16BIT_SUPPORT + if (pData->iBitdepth > 8) + { + pData->iBitdepth = 8; + pData->iPNGmult = 2; + } +#endif + + if ((pData->iBitdepth != 8) /* parameter validity checks */ +#ifndef MNG_NO_1_2_4BIT_SUPPORT + && (pData->iBitdepth != 1) && + (pData->iBitdepth != 2) && + (pData->iBitdepth != 4) +#endif +#ifndef MNG_NO_16BIT_SUPPORT + && (pData->iBitdepth != 16) +#endif + ) + MNG_ERROR (pData, MNG_INVALIDBITDEPTH); + + if ((pData->iColortype != MNG_COLORTYPE_GRAY ) && + (pData->iColortype != MNG_COLORTYPE_RGB ) && + (pData->iColortype != MNG_COLORTYPE_INDEXED) && + (pData->iColortype != MNG_COLORTYPE_GRAYA ) && + (pData->iColortype != MNG_COLORTYPE_RGBA ) ) + MNG_ERROR (pData, MNG_INVALIDCOLORTYPE); + + if ((pData->iColortype == MNG_COLORTYPE_INDEXED) && (pData->iBitdepth > 8)) + MNG_ERROR (pData, MNG_INVALIDBITDEPTH); + + if (((pData->iColortype == MNG_COLORTYPE_RGB ) || + (pData->iColortype == MNG_COLORTYPE_GRAYA ) || + (pData->iColortype == MNG_COLORTYPE_RGBA ) ) && + (pData->iBitdepth < 8 ) ) + MNG_ERROR (pData, MNG_INVALIDBITDEPTH); + +#if defined(FILTER192) || defined(FILTER193) + if ((pData->iFilter != MNG_FILTER_ADAPTIVE ) && +#if defined(FILTER192) && defined(FILTER193) + (pData->iFilter != MNG_FILTER_DIFFERING) && + (pData->iFilter != MNG_FILTER_NOFILTER ) ) +#else +#ifdef FILTER192 + (pData->iFilter != MNG_FILTER_DIFFERING) ) +#else + (pData->iFilter != MNG_FILTER_NOFILTER ) ) +#endif +#endif + MNG_ERROR (pData, MNG_INVALIDFILTER); +#else + if (pData->iFilter) + MNG_ERROR (pData, MNG_INVALIDFILTER); +#endif + + pData->iImagelevel++; /* one level deeper */ + +#ifdef MNG_SUPPORT_DISPLAY + { /* create an animation object */ + mng_retcode iRetcode = mng_create_ani_basi (pData, pChunk); + if (iRetcode) /* on error bail out */ + return iRetcode; + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_NO_16BIT_SUPPORT + if (((mng_basip)pChunk)->iBitdepth > 8) + ((mng_basip)pChunk)->iBitdepth = 8; +#endif + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_CLON +MNG_C_SPECIALFUNC (mng_special_clon) +{ +#ifdef MNG_SUPPORT_DISPLAY + return mng_create_ani_clon (pData, pChunk); +#else + return MNG_NOERROR; /* done */ +#endif /* MNG_SUPPORT_DISPLAY */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_PAST +MNG_F_SPECIALFUNC (mng_debunk_past) +{ + mng_pastp pPAST = (mng_pastp)pChunk; + mng_uint32 iRawlen = *piRawlen; + mng_uint8p pRawdata = *ppRawdata; + mng_uint32 iSize; + mng_uint32 iX; + mng_past_sourcep pSource; + /* check the length */ + if ((iRawlen < 41) || (((iRawlen - 11) % 30) != 0)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + pPAST->iDestid = mng_get_uint16 (pRawdata); + pPAST->iTargettype = *(pRawdata+2); + pPAST->iTargetx = mng_get_int32 (pRawdata+3); + pPAST->iTargety = mng_get_int32 (pRawdata+7); + pPAST->iCount = ((iRawlen - 11) / 30); /* how many entries again? */ + iSize = pPAST->iCount * sizeof (mng_past_source); + + pRawdata += 11; + /* get a buffer for all the source blocks */ + MNG_ALLOC (pData, pPAST->pSources, iSize); + + pSource = (mng_past_sourcep)(pPAST->pSources); + + for (iX = pPAST->iCount; iX > 0; iX--) + { /* now copy the source blocks */ + pSource->iSourceid = mng_get_uint16 (pRawdata); + pSource->iComposition = *(pRawdata+2); + pSource->iOrientation = *(pRawdata+3); + pSource->iOffsettype = *(pRawdata+4); + pSource->iOffsetx = mng_get_int32 (pRawdata+5); + pSource->iOffsety = mng_get_int32 (pRawdata+9); + pSource->iBoundarytype = *(pRawdata+13); + pSource->iBoundaryl = mng_get_int32 (pRawdata+14); + pSource->iBoundaryr = mng_get_int32 (pRawdata+18); + pSource->iBoundaryt = mng_get_int32 (pRawdata+22); + pSource->iBoundaryb = mng_get_int32 (pRawdata+26); + + pSource++; + pRawdata += 30; + } + + *piRawlen = 0; + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_PAST +MNG_C_SPECIALFUNC (mng_special_past) +{ +#ifdef MNG_SUPPORT_DISPLAY + return mng_create_ani_past (pData, pChunk); +#else + return MNG_NOERROR; +#endif /* MNG_SUPPORT_DISPLAY */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_DISC +MNG_F_SPECIALFUNC (mng_disc_entries) +{ + mng_discp pDISC = (mng_discp)pChunk; + mng_uint32 iRawlen = *piRawlen; + mng_uint8p pRawdata = *ppRawdata; + + if ((iRawlen % 2) != 0) /* check the length */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + pDISC->iCount = (iRawlen / sizeof (mng_uint16)); + + if (pDISC->iCount) + { + MNG_ALLOC (pData, pDISC->pObjectids, iRawlen); + +#ifndef MNG_BIGENDIAN_SUPPORTED + { + mng_uint32 iX; + mng_uint8p pIn = pRawdata; + mng_uint16p pOut = pDISC->pObjectids; + + for (iX = pDISC->iCount; iX > 0; iX--) + { + *pOut++ = mng_get_uint16 (pIn); + pIn += 2; + } + } +#else + MNG_COPY (pDISC->pObjectids, pRawdata, iRawlen); +#endif /* !MNG_BIGENDIAN_SUPPORTED */ + } + + *piRawlen = 0; + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_DISC +MNG_C_SPECIALFUNC (mng_special_disc) +{ +#ifdef MNG_SUPPORT_DISPLAY + return mng_create_ani_disc (pData, pChunk); +#else + return MNG_NOERROR; +#endif /* MNG_SUPPORT_DISPLAY */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_BACK +MNG_C_SPECIALFUNC (mng_special_back) +{ +#ifdef MNG_SUPPORT_DISPLAY + /* retrieve the fields */ + pData->bHasBACK = MNG_TRUE; + pData->iBACKred = ((mng_backp)pChunk)->iRed; + pData->iBACKgreen = ((mng_backp)pChunk)->iGreen; + pData->iBACKblue = ((mng_backp)pChunk)->iBlue; + pData->iBACKmandatory = ((mng_backp)pChunk)->iMandatory; + pData->iBACKimageid = ((mng_backp)pChunk)->iImageid; + pData->iBACKtile = ((mng_backp)pChunk)->iTile; + + return mng_create_ani_back (pData); +#else + return MNG_NOERROR; +#endif /* MNG_SUPPORT_DISPLAY */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_FRAM +MNG_F_SPECIALFUNC (mng_fram_remainder) +{ + mng_framp pFRAM = (mng_framp)pChunk; + mng_uint32 iRawlen = *piRawlen; + mng_uint8p pRawdata = *ppRawdata; + mng_uint32 iRequired = 0; + + if (iRawlen < 4) /* must have at least 4 bytes */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + iRequired = 4; /* calculate and check required remaining length */ + + pFRAM->iChangedelay = *pRawdata; + pFRAM->iChangetimeout = *(pRawdata+1); + pFRAM->iChangeclipping = *(pRawdata+2); + pFRAM->iChangesyncid = *(pRawdata+3); + + if (pFRAM->iChangedelay ) { iRequired += 4; } + if (pFRAM->iChangetimeout ) { iRequired += 4; } + if (pFRAM->iChangeclipping) { iRequired += 17; } + + if (pFRAM->iChangesyncid) + { + if ((iRawlen - iRequired) % 4 != 0) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + } + else + { + if (iRawlen != iRequired) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + } + + pRawdata += 4; + + if (pFRAM->iChangedelay) /* delay changed ? */ + { + pFRAM->iDelay = mng_get_uint32 (pRawdata); + pRawdata += 4; + } + + if (pFRAM->iChangetimeout) /* timeout changed ? */ + { + pFRAM->iTimeout = mng_get_uint32 (pRawdata); + pRawdata += 4; + } + + if (pFRAM->iChangeclipping) /* clipping changed ? */ + { + pFRAM->iBoundarytype = *pRawdata; + pFRAM->iBoundaryl = mng_get_int32 (pRawdata+1); + pFRAM->iBoundaryr = mng_get_int32 (pRawdata+5); + pFRAM->iBoundaryt = mng_get_int32 (pRawdata+9); + pFRAM->iBoundaryb = mng_get_int32 (pRawdata+13); + pRawdata += 17; + } + + if (pFRAM->iChangesyncid) + { + pFRAM->iCount = (iRawlen - iRequired) / 4; + + if (pFRAM->iCount) + { + MNG_ALLOC (pData, pFRAM->pSyncids, pFRAM->iCount * 4); + +#ifndef MNG_BIGENDIAN_SUPPORTED + { + mng_uint32 iX; + mng_uint32p pOut = pFRAM->pSyncids; + + for (iX = pFRAM->iCount; iX > 0; iX--) + { + *pOut++ = mng_get_uint32 (pRawdata); + pRawdata += 4; + } + } +#else + MNG_COPY (pFRAM->pSyncids, pRawdata, pFRAM->iCount * 4); +#endif /* !MNG_BIGENDIAN_SUPPORTED */ + } + } + +#ifndef MNG_NO_OLD_VERSIONS + if (pData->bPreDraft48) /* old style input-stream ? */ + { + switch (pFRAM->iMode) /* fix the framing mode then */ + { + case 0: { break; } + case 1: { pFRAM->iMode = 3; break; } + case 2: { pFRAM->iMode = 4; break; } + case 3: { pFRAM->iMode = 1; break; } + case 4: { pFRAM->iMode = 1; break; } + case 5: { pFRAM->iMode = 2; break; } + default: { pFRAM->iMode = 1; break; } + } + } +#endif + + *piRawlen = 0; + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_FRAM +MNG_C_SPECIALFUNC (mng_special_fram) +{ +#ifdef MNG_SUPPORT_DISPLAY + return mng_create_ani_fram (pData, pChunk); +#else + return MNG_NOERROR; +#endif /* MNG_SUPPORT_DISPLAY */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_MOVE +MNG_C_SPECIALFUNC (mng_special_move) +{ +#ifdef MNG_SUPPORT_DISPLAY + return mng_create_ani_move (pData, pChunk); +#else + return MNG_NOERROR; +#endif /* MNG_SUPPORT_DISPLAY */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_CLIP +MNG_C_SPECIALFUNC (mng_special_clip) +{ +#ifdef MNG_SUPPORT_DISPLAY + return mng_create_ani_clip (pData, pChunk); +#else + return MNG_NOERROR; +#endif /* MNG_SUPPORT_DISPLAY */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SHOW +MNG_C_SPECIALFUNC (mng_special_show) +{ +#ifdef MNG_SUPPORT_DISPLAY + mng_retcode iRetcode; + + if (!((mng_showp)pChunk)->bEmpty) /* any data ? */ + { + if (!((mng_showp)pChunk)->bHaslastid) + ((mng_showp)pChunk)->iLastid = ((mng_showp)pChunk)->iFirstid; + + pData->iSHOWfromid = ((mng_showp)pChunk)->iFirstid; + pData->iSHOWtoid = ((mng_showp)pChunk)->iLastid; + pData->iSHOWmode = ((mng_showp)pChunk)->iMode; + } + else /* use defaults then */ + { + pData->iSHOWfromid = 1; + pData->iSHOWtoid = 65535; + pData->iSHOWmode = 2; + } + /* create a SHOW animation object */ + iRetcode = mng_create_ani_show (pData); + if (!iRetcode) /* go and do it! */ + iRetcode = mng_process_display_show (pData); + +#endif /* MNG_SUPPORT_DISPLAY */ + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_TERM +MNG_C_SPECIALFUNC (mng_special_term) +{ + /* should be behind MHDR or SAVE !! */ + if ((!pData->bHasSAVE) && (pData->iChunkseq > 2)) + { + pData->bMisplacedTERM = MNG_TRUE; /* indicate we found a misplaced TERM */ + /* and send a warning signal!!! */ + MNG_WARNING (pData, MNG_SEQUENCEERROR); + } + + pData->bHasTERM = MNG_TRUE; + + if (pData->fProcessterm) /* inform the app ? */ + if (!pData->fProcessterm (((mng_handle)pData), + ((mng_termp)pChunk)->iTermaction, + ((mng_termp)pChunk)->iIteraction, + ((mng_termp)pChunk)->iDelay, + ((mng_termp)pChunk)->iItermax)) + MNG_ERROR (pData, MNG_APPMISCERROR); + +#ifdef MNG_SUPPORT_DISPLAY + { /* create the TERM ani-object */ + mng_retcode iRetcode = mng_create_ani_term (pData, pChunk); + if (iRetcode) /* on error bail out */ + return iRetcode; + /* save for future reference */ + pData->pTermaniobj = pData->pLastaniobj; + } +#endif /* MNG_SUPPORT_DISPLAY */ + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SAVE +MNG_F_SPECIALFUNC (mng_save_entries) +{ + mng_savep pSAVE = (mng_savep)pChunk; + mng_uint32 iRawlen = *piRawlen; + mng_uint8p pRawdata = *ppRawdata; + mng_save_entryp pEntry = MNG_NULL; + mng_uint32 iCount = 0; + mng_uint8 iOtype = *pRawdata; + mng_uint8 iEtype; + mng_uint8p pTemp; + mng_uint8p pNull; + mng_uint32 iLen; + mng_uint32 iOffset[2]; + mng_uint32 iStarttime[2]; + mng_uint32 iFramenr; + mng_uint32 iLayernr; + mng_uint32 iX; + mng_uint32 iNamesize; + + if ((iOtype != 4) && (iOtype != 8)) + MNG_ERROR (pData, MNG_INVOFFSETSIZE); + + pSAVE->iOffsettype = iOtype; + + for (iX = 0; iX < 2; iX++) /* do this twice to get the count first ! */ + { + pTemp = pRawdata + 1; + iLen = iRawlen - 1; + + if (iX) /* second run ? */ + { + MNG_ALLOC (pData, pEntry, (iCount * sizeof (mng_save_entry))); + + pSAVE->iCount = iCount; + pSAVE->pEntries = pEntry; + } + + while (iLen) /* anything left ? */ + { + iEtype = *pTemp; /* entrytype */ + + if ((iEtype != 0) && (iEtype != 1) && (iEtype != 2) && (iEtype != 3)) + MNG_ERROR (pData, MNG_INVENTRYTYPE); + + pTemp++; + + if (iEtype > 1) + { + iOffset [0] = 0; + iOffset [1] = 0; + iStarttime [0] = 0; + iStarttime [1] = 0; + iLayernr = 0; + iFramenr = 0; + } + else + { + if (iOtype == 4) + { + iOffset [0] = 0; + iOffset [1] = mng_get_uint32 (pTemp); + + pTemp += 4; + } + else + { + iOffset [0] = mng_get_uint32 (pTemp); + iOffset [1] = mng_get_uint32 (pTemp+4); + + pTemp += 8; + } + + if (iEtype > 0) + { + iStarttime [0] = 0; + iStarttime [1] = 0; + iLayernr = 0; + iFramenr = 0; + } + else + { + if (iOtype == 4) + { + iStarttime [0] = 0; + iStarttime [1] = mng_get_uint32 (pTemp+0); + iLayernr = mng_get_uint32 (pTemp+4); + iFramenr = mng_get_uint32 (pTemp+8); + + pTemp += 12; + } + else + { + iStarttime [0] = mng_get_uint32 (pTemp+0); + iStarttime [1] = mng_get_uint32 (pTemp+4); + iLayernr = mng_get_uint32 (pTemp+8); + iFramenr = mng_get_uint32 (pTemp+12); + + pTemp += 16; + } + } + } + + pNull = pTemp; /* get the name length */ + while (*pNull) + pNull++; + + if ((pNull - pRawdata) > (mng_int32)iRawlen) + { + iNamesize = iLen; /* no null found; so end of SAVE */ + iLen = 0; + } + else + { + iNamesize = pNull - pTemp; /* should be another entry */ + iLen -= iNamesize; + + if (!iLen) /* must not end with a null ! */ + MNG_ERROR (pData, MNG_ENDWITHNULL); + } + + if (!pEntry) + { + iCount++; + } + else + { + pEntry->iEntrytype = iEtype; + pEntry->iOffset [0] = iOffset [0]; + pEntry->iOffset [1] = iOffset [1]; + pEntry->iStarttime [0] = iStarttime [0]; + pEntry->iStarttime [1] = iStarttime [1]; + pEntry->iLayernr = iLayernr; + pEntry->iFramenr = iFramenr; + pEntry->iNamesize = iNamesize; + + if (iNamesize) + { + MNG_ALLOC (pData, pEntry->zName, iNamesize+1); + MNG_COPY (pEntry->zName, pTemp, iNamesize); + } + + pEntry++; + } + + pTemp += iNamesize; + } + } + + *piRawlen = 0; + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SAVE +MNG_C_SPECIALFUNC (mng_special_save) +{ + pData->bHasSAVE = MNG_TRUE; + + if (pData->fProcesssave) /* inform the application ? */ + { + mng_bool bOke = pData->fProcesssave ((mng_handle)pData); + if (!bOke) + MNG_ERROR (pData, MNG_APPMISCERROR); + } + +#ifdef MNG_SUPPORT_DISPLAY + { + mng_retcode iRetcode; + + /* TODO: something with the parameters */ + + /* create a SAVE animation object */ + iRetcode = mng_create_ani_save (pData); + if (!iRetcode) /* process it */ + iRetcode = mng_process_display_save (pData); + if (iRetcode) /* on error bail out */ + return iRetcode; + } +#endif /* MNG_SUPPORT_DISPLAY */ + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SEEK +MNG_C_SPECIALFUNC (mng_special_seek) +{ + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_DISPLAY + /* create a SEEK animation object */ + iRetcode = mng_create_ani_seek (pData, pChunk); + if (iRetcode) /* on error bail out */ + return iRetcode; + +#endif /* MNG_SUPPORT_DISPLAY */ + + if (pData->fProcessseek) /* inform the app ? */ + if (!pData->fProcessseek ((mng_handle)pData, ((mng_seekp)pChunk)->zName)) + MNG_ERROR (pData, MNG_APPMISCERROR); + +#ifdef MNG_SUPPORT_DISPLAY + return mng_process_display_seek (pData); +#else + return MNG_NOERROR; +#endif /* MNG_SUPPORT_DISPLAY */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_eXPI +MNG_C_SPECIALFUNC (mng_special_expi) +{ +#ifdef MNG_SUPPORT_DISPLAY + { + + + /* TODO: something !!! */ + + + } +#endif /* MNG_SUPPORT_DISPLAY */ + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_fPRI +MNG_C_SPECIALFUNC (mng_special_fpri) +{ +#ifdef MNG_SUPPORT_DISPLAY + { + + + /* TODO: something !!! */ + + + } +#endif /* MNG_SUPPORT_DISPLAY */ + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_nEED +MNG_LOCAL mng_bool CheckKeyword (mng_datap pData, + mng_uint8p pKeyword) +{ + mng_chunkid handled_chunks [] = + { + MNG_UINT_BACK, /* keep it sorted !!!! */ + MNG_UINT_BASI, + MNG_UINT_CLIP, + MNG_UINT_CLON, +#ifndef MNG_NO_DELTA_PNG +/* TODO: MNG_UINT_DBYK, */ +#endif + MNG_UINT_DEFI, +#ifndef MNG_NO_DELTA_PNG + MNG_UINT_DHDR, +#endif + MNG_UINT_DISC, +#ifndef MNG_NO_DELTA_PNG +/* TODO: MNG_UINT_DROP, */ +#endif + MNG_UINT_ENDL, + MNG_UINT_FRAM, + MNG_UINT_IDAT, + MNG_UINT_IEND, + MNG_UINT_IHDR, +#ifndef MNG_NO_DELTA_PNG +#ifdef MNG_INCLUDE_JNG + MNG_UINT_IJNG, +#endif + MNG_UINT_IPNG, +#endif +#ifdef MNG_INCLUDE_JNG + MNG_UINT_JDAA, + MNG_UINT_JDAT, + MNG_UINT_JHDR, +/* TODO: MNG_UINT_JSEP, */ + MNG_UINT_JdAA, +#endif + MNG_UINT_LOOP, + MNG_UINT_MAGN, + MNG_UINT_MEND, + MNG_UINT_MHDR, + MNG_UINT_MOVE, +/* TODO: MNG_UINT_ORDR, */ + MNG_UINT_PAST, + MNG_UINT_PLTE, +#ifndef MNG_NO_DELTA_PNG + MNG_UINT_PPLT, + MNG_UINT_PROM, +#endif + MNG_UINT_SAVE, + MNG_UINT_SEEK, + MNG_UINT_SHOW, + MNG_UINT_TERM, + MNG_UINT_bKGD, + MNG_UINT_cHRM, +/* TODO: MNG_UINT_eXPI, */ + MNG_UINT_evNT, +/* TODO: MNG_UINT_fPRI, */ + MNG_UINT_gAMA, +/* TODO: MNG_UINT_hIST, */ + MNG_UINT_iCCP, + MNG_UINT_iTXt, + MNG_UINT_nEED, +/* TODO: MNG_UINT_oFFs, */ +/* TODO: MNG_UINT_pCAL, */ +/* TODO: MNG_UINT_pHYg, */ +/* TODO: MNG_UINT_pHYs, */ +/* TODO: MNG_UINT_sBIT, */ +/* TODO: MNG_UINT_sCAL, */ +/* TODO: MNG_UINT_sPLT, */ + MNG_UINT_sRGB, + MNG_UINT_tEXt, + MNG_UINT_tIME, + MNG_UINT_tRNS, + MNG_UINT_zTXt, + }; + + mng_bool bOke = MNG_FALSE; + + if (pData->fProcessneed) /* does the app handle it ? */ + bOke = pData->fProcessneed ((mng_handle)pData, (mng_pchar)pKeyword); + + if (!bOke) + { /* find the keyword length */ + mng_uint8p pNull = pKeyword; + while (*pNull) + pNull++; + + if ((pNull - pKeyword) == 4) /* test a chunk ? */ + { /* get the chunk-id */ + mng_chunkid iChunkid = (*pKeyword << 24) + (*(pKeyword+1) << 16) + + (*(pKeyword+2) << 8) + (*(pKeyword+3) ); + /* binary search variables */ + mng_int32 iTop, iLower, iUpper, iMiddle; + /* determine max index of table */ + iTop = (sizeof (handled_chunks) / sizeof (handled_chunks [0])) - 1; + + /* binary search; with 52 chunks, worst-case is 7 comparisons */ + iLower = 0; + iMiddle = iTop >> 1; + iUpper = iTop; + + do /* the binary search itself */ + { + if (handled_chunks [iMiddle] < iChunkid) + iLower = iMiddle + 1; + else if (handled_chunks [iMiddle] > iChunkid) + iUpper = iMiddle - 1; + else + { + bOke = MNG_TRUE; + break; + } + + iMiddle = (iLower + iUpper) >> 1; + } + while (iLower <= iUpper); + } + /* test draft ? */ + if ((!bOke) && ((pNull - pKeyword) == 8) && + (*pKeyword == 'd') && (*(pKeyword+1) == 'r') && + (*(pKeyword+2) == 'a') && (*(pKeyword+3) == 'f') && + (*(pKeyword+4) == 't') && (*(pKeyword+5) == ' ')) + { + mng_uint32 iDraft; + + iDraft = (*(pKeyword+6) - '0') * 10 + (*(pKeyword+7) - '0'); + bOke = (mng_bool)(iDraft <= MNG_MNG_DRAFT); + } + /* test MNG 1.0/1.1 ? */ + if ((!bOke) && ((pNull - pKeyword) == 7) && + (*pKeyword == 'M') && (*(pKeyword+1) == 'N') && + (*(pKeyword+2) == 'G') && (*(pKeyword+3) == '-') && + (*(pKeyword+4) == '1') && (*(pKeyword+5) == '.') && + ((*(pKeyword+6) == '0') || (*(pKeyword+6) == '1'))) + bOke = MNG_TRUE; + /* test CACHEOFF ? */ + if ((!bOke) && ((pNull - pKeyword) == 8) && + (*pKeyword == 'C') && (*(pKeyword+1) == 'A') && + (*(pKeyword+2) == 'C') && (*(pKeyword+3) == 'H') && + (*(pKeyword+4) == 'E') && (*(pKeyword+5) == 'O') && + (*(pKeyword+6) == 'F') && (*(pKeyword+7) == 'F')) + { + if (!pData->pFirstaniobj) /* only if caching hasn't started yet ! */ + { + bOke = MNG_TRUE; + pData->bCacheplayback = MNG_FALSE; + pData->bStorechunks = MNG_FALSE; + } + } + } + + return bOke; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_nEED +MNG_C_SPECIALFUNC (mng_special_need) +{ + /* let's check it */ + mng_bool bOke = MNG_TRUE; + mng_uint8p pNull, pTemp, pMax; + + pTemp = (mng_uint8p)((mng_needp)pChunk)->zKeywords; + pMax = (mng_uint8p)(pTemp + ((mng_needp)pChunk)->iKeywordssize); + pNull = pTemp; + while (*pNull) + pNull++; + + while ((bOke) && (pNull < pMax)) + { + bOke = CheckKeyword (pData, pTemp); + pTemp = pNull + 1; + pNull = pTemp; + while (*pNull) + pNull++; + } + + if (bOke) + bOke = CheckKeyword (pData, pTemp); + + if (!bOke) + MNG_ERROR (pData, MNG_UNSUPPORTEDNEED); + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_pHYg +MNG_C_SPECIALFUNC (mng_special_phyg) +{ +#ifdef MNG_SUPPORT_DISPLAY + { + + + /* TODO: something !!! */ + + + } +#endif /* MNG_SUPPORT_DISPLAY */ + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +MNG_C_SPECIALFUNC (mng_special_dhdr) +{ + if ((((mng_dhdrp)pChunk)->iDeltatype == MNG_DELTATYPE_REPLACE) && (((mng_dhdrp)pChunk)->bHasblockloc)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + if ((((mng_dhdrp)pChunk)->iDeltatype == MNG_DELTATYPE_NOCHANGE) && (((mng_dhdrp)pChunk)->bHasblocksize)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + pData->bHasDHDR = MNG_TRUE; /* inside a DHDR-IEND block now */ + pData->iDeltatype = ((mng_dhdrp)pChunk)->iDeltatype; + + pData->iImagelevel++; /* one level deeper */ + +#ifdef MNG_SUPPORT_DISPLAY + return mng_create_ani_dhdr (pData, pChunk); +#else + return MNG_NOERROR; +#endif /* MNG_SUPPORT_DISPLAY */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +MNG_C_SPECIALFUNC (mng_special_prom) +{ + if ((((mng_promp)pChunk)->iColortype != MNG_COLORTYPE_GRAY ) && + (((mng_promp)pChunk)->iColortype != MNG_COLORTYPE_RGB ) && + (((mng_promp)pChunk)->iColortype != MNG_COLORTYPE_INDEXED) && + (((mng_promp)pChunk)->iColortype != MNG_COLORTYPE_GRAYA ) && + (((mng_promp)pChunk)->iColortype != MNG_COLORTYPE_RGBA ) ) + MNG_ERROR (pData, MNG_INVALIDCOLORTYPE); + +#ifdef MNG_NO_16BIT_SUPPORT + if (((mng_promp)pChunk)->iSampledepth == MNG_BITDEPTH_16 ) + ((mng_promp)pChunk)->iSampledepth = MNG_BITDEPTH_8; +#endif + + if ((((mng_promp)pChunk)->iSampledepth != MNG_BITDEPTH_1 ) && + (((mng_promp)pChunk)->iSampledepth != MNG_BITDEPTH_2 ) && + (((mng_promp)pChunk)->iSampledepth != MNG_BITDEPTH_4 ) && + (((mng_promp)pChunk)->iSampledepth != MNG_BITDEPTH_8 ) +#ifndef MNG_NO_16BIT_SUPPORT + && (((mng_promp)pChunk)->iSampledepth != MNG_BITDEPTH_16) +#endif + ) + MNG_ERROR (pData, MNG_INVSAMPLEDEPTH); + +#ifdef MNG_SUPPORT_DISPLAY + { + mng_retcode iRetcode = mng_create_ani_prom (pData, pChunk); + if (iRetcode) /* on error bail out */ + return iRetcode; + } +#endif /* MNG_SUPPORT_DISPLAY */ + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +MNG_C_SPECIALFUNC (mng_special_ipng) +{ +#ifdef MNG_SUPPORT_DISPLAY + mng_retcode iRetcode = mng_create_ani_ipng (pData); + if (!iRetcode) /* process it */ + iRetcode = mng_process_display_ipng (pData); + if (iRetcode) /* on error bail out */ + return iRetcode; +#endif /* MNG_SUPPORT_DISPLAY */ + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +MNG_F_SPECIALFUNC (mng_pplt_entries) +{ + mng_ppltp pPPLT = (mng_ppltp)pChunk; + mng_uint32 iRawlen = *piRawlen; + mng_uint8p pRawdata = *ppRawdata; + mng_uint8 iDeltatype = pPPLT->iDeltatype; + mng_uint32 iMax = 0; + mng_int32 iX, iY, iM; + mng_rgbpaltab aIndexentries; + mng_uint8arr aAlphaentries; + mng_uint8arr aUsedentries; + /* must be indexed color ! */ + if (pData->iColortype != MNG_COLORTYPE_INDEXED) + MNG_ERROR (pData, MNG_INVALIDCOLORTYPE); + + for (iY = 255; iY >= 0; iY--) /* reset arrays */ + { + aIndexentries [iY].iRed = 0; + aIndexentries [iY].iGreen = 0; + aIndexentries [iY].iBlue = 0; + aAlphaentries [iY] = 255; + aUsedentries [iY] = 0; + } + + while (iRawlen) /* as long as there are entries left ... */ + { + mng_uint32 iDiff; + + if (iRawlen < 2) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + iX = (mng_int32)(*pRawdata); /* get start and end index */ + iM = (mng_int32)(*(pRawdata+1)); + + if (iM < iX) + MNG_ERROR (pData, MNG_INVALIDINDEX); + + if (iM >= (mng_int32) iMax) /* determine highest used index */ + iMax = iM + 1; + + pRawdata += 2; + iRawlen -= 2; + iDiff = (iM - iX + 1); + if ((iDeltatype == MNG_DELTATYPE_REPLACERGB ) || + (iDeltatype == MNG_DELTATYPE_DELTARGB ) ) + iDiff = iDiff * 3; + else + if ((iDeltatype == MNG_DELTATYPE_REPLACERGBA) || + (iDeltatype == MNG_DELTATYPE_DELTARGBA ) ) + iDiff = iDiff * 4; + + if (iRawlen < iDiff) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + if ((iDeltatype == MNG_DELTATYPE_REPLACERGB ) || + (iDeltatype == MNG_DELTATYPE_DELTARGB ) ) + { + for (iY = iX; iY <= iM; iY++) + { + aIndexentries [iY].iRed = *pRawdata; + aIndexentries [iY].iGreen = *(pRawdata+1); + aIndexentries [iY].iBlue = *(pRawdata+2); + aUsedentries [iY] = 1; + + pRawdata += 3; + iRawlen -= 3; + } + } + else + if ((iDeltatype == MNG_DELTATYPE_REPLACEALPHA) || + (iDeltatype == MNG_DELTATYPE_DELTAALPHA ) ) + { + for (iY = iX; iY <= iM; iY++) + { + aAlphaentries [iY] = *pRawdata; + aUsedentries [iY] = 1; + + pRawdata++; + iRawlen--; + } + } + else + { + for (iY = iX; iY <= iM; iY++) + { + aIndexentries [iY].iRed = *pRawdata; + aIndexentries [iY].iGreen = *(pRawdata+1); + aIndexentries [iY].iBlue = *(pRawdata+2); + aAlphaentries [iY] = *(pRawdata+3); + aUsedentries [iY] = 1; + + pRawdata += 4; + iRawlen -= 4; + } + } + } + + switch (pData->iBitdepth) /* check maximum allowed entries for bitdepth */ + { + case MNG_BITDEPTH_1 : { + if (iMax > 2) + MNG_ERROR (pData, MNG_INVALIDINDEX); + break; + } + case MNG_BITDEPTH_2 : { + if (iMax > 4) + MNG_ERROR (pData, MNG_INVALIDINDEX); + break; + } + case MNG_BITDEPTH_4 : { + if (iMax > 16) + MNG_ERROR (pData, MNG_INVALIDINDEX); + break; + } + } + + pPPLT->iCount = iMax; + + for (iY = 255; iY >= 0; iY--) + { + pPPLT->aEntries [iY].iRed = aIndexentries [iY].iRed; + pPPLT->aEntries [iY].iGreen = aIndexentries [iY].iGreen; + pPPLT->aEntries [iY].iBlue = aIndexentries [iY].iBlue; + pPPLT->aEntries [iY].iAlpha = aAlphaentries [iY]; + pPPLT->aEntries [iY].bUsed = (mng_bool)(aUsedentries [iY]); + } + + { /* create animation object */ + mng_retcode iRetcode = mng_create_ani_pplt (pData, iDeltatype, iMax, + aIndexentries, aAlphaentries, + aUsedentries); + if (iRetcode) + return iRetcode; + } + + *piRawlen = 0; + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +MNG_C_SPECIALFUNC (mng_special_pplt) +{ + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifdef MNG_INCLUDE_JNG +MNG_C_SPECIALFUNC (mng_special_ijng) +{ +#ifdef MNG_SUPPORT_DISPLAY + mng_retcode iRetcode = mng_create_ani_ijng (pData); + if (!iRetcode) /* process it */ + iRetcode = mng_process_display_ijng (pData); + return iRetcode; +#else + return MNG_NOERROR; /* done */ +#endif /* MNG_SUPPORT_DISPLAY */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +MNG_F_SPECIALFUNC (mng_drop_entries) +{ + mng_dropp pDROP = (mng_dropp)pChunk; + mng_uint32 iRawlen = *piRawlen; + mng_uint8p pRawdata = *ppRawdata; + mng_uint32 iX; + mng_uint32p pEntry; + /* check length */ + if ((iRawlen < 4) || ((iRawlen % 4) != 0)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + MNG_ALLOC (pData, pEntry, iRawlen); + pDROP->iCount = iRawlen / 4; + pDROP->pChunknames = (mng_ptr)pEntry; + + for (iX = pDROP->iCount; iX > 0; iX--) + { + *pEntry++ = mng_get_uint32 (pRawdata); + pRawdata += 4; + } + + *piRawlen = 0; + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +MNG_C_SPECIALFUNC (mng_special_drop) +{ +#ifdef MNG_SUPPORT_DISPLAY + { + + + /* TODO: something !!! */ + + + } +#endif /* MNG_SUPPORT_DISPLAY */ + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_DBYK +MNG_C_SPECIALFUNC (mng_special_dbyk) +{ +#ifdef MNG_SUPPORT_DISPLAY + { + + + /* TODO: something !!! */ + + + } +#endif /* MNG_SUPPORT_DISPLAY */ + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_ORDR +MNG_F_SPECIALFUNC (mng_ordr_entries) +{ + mng_ordrp pORDR = (mng_ordrp)pChunk; + mng_uint32 iRawlen = *piRawlen; + mng_uint8p pRawdata = *ppRawdata; + mng_uint32 iX; + mng_ordr_entryp pEntry; + /* check length */ + if ((iRawlen < 5) || ((iRawlen % 5) != 0)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + MNG_ALLOC (pData, pEntry, iRawlen); + pORDR->iCount = iRawlen / 5; + pORDR->pEntries = (mng_ptr)pEntry; + + for (iX = pORDR->iCount; iX > 0; iX--) + { + pEntry->iChunkname = mng_get_uint32 (pRawdata); + pEntry->iOrdertype = *(pRawdata+4); + pEntry++; + pRawdata += 5; + } + + *piRawlen = 0; + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_ORDR +MNG_C_SPECIALFUNC (mng_special_ordr) +{ +#ifdef MNG_SUPPORT_DISPLAY + { + + + /* TODO: something !!! */ + + + } +#endif /* MNG_SUPPORT_DISPLAY */ + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_MAGN +MNG_F_SPECIALFUNC (mng_debunk_magn) +{ + mng_magnp pMAGN = (mng_magnp)pChunk; + mng_uint32 iRawlen = *piRawlen; + mng_uint8p pRawdata = *ppRawdata; + mng_bool bFaulty; + /* check length */ + if (iRawlen > 20) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + /* following is an ugly hack to allow faulty layout caused by previous + versions of libmng and MNGeye, which wrote MAGN with a 16-bit + MethodX/MethodY (as opposed to the proper 8-bit as defined in the spec!) */ + + if ((iRawlen == 6) || (iRawlen == 8) || (iRawlen == 10) || (iRawlen == 12) || + (iRawlen == 14) || (iRawlen == 16) || (iRawlen == 20)) + bFaulty = MNG_TRUE; /* these lengths are all wrong */ + else /* length 18 can be right or wrong !!! */ + if ((iRawlen == 18) && (mng_get_uint16 (pRawdata+4) <= 5) && + (mng_get_uint16 (pRawdata+6) < 256) && + (mng_get_uint16 (pRawdata+8) < 256) && + (mng_get_uint16 (pRawdata+10) < 256) && + (mng_get_uint16 (pRawdata+12) < 256) && + (mng_get_uint16 (pRawdata+14) < 256) && + (mng_get_uint16 (pRawdata+16) < 256)) + bFaulty = MNG_TRUE; /* this is very likely the wrong layout */ + else + bFaulty = MNG_FALSE; /* all other cases are handled as right */ + + if (bFaulty) /* wrong layout ? */ + { + if (iRawlen > 0) /* get the fields */ + pMAGN->iFirstid = mng_get_uint16 (pRawdata); + else + pMAGN->iFirstid = 0; + + if (iRawlen > 2) + pMAGN->iLastid = mng_get_uint16 (pRawdata+2); + else + pMAGN->iLastid = pMAGN->iFirstid; + + if (iRawlen > 4) + pMAGN->iMethodX = (mng_uint8)(mng_get_uint16 (pRawdata+4)); + else + pMAGN->iMethodX = 0; + + if (iRawlen > 6) + pMAGN->iMX = mng_get_uint16 (pRawdata+6); + else + pMAGN->iMX = 1; + + if (iRawlen > 8) + pMAGN->iMY = mng_get_uint16 (pRawdata+8); + else + pMAGN->iMY = pMAGN->iMX; + + if (iRawlen > 10) + pMAGN->iML = mng_get_uint16 (pRawdata+10); + else + pMAGN->iML = pMAGN->iMX; + + if (iRawlen > 12) + pMAGN->iMR = mng_get_uint16 (pRawdata+12); + else + pMAGN->iMR = pMAGN->iMX; + + if (iRawlen > 14) + pMAGN->iMT = mng_get_uint16 (pRawdata+14); + else + pMAGN->iMT = pMAGN->iMY; + + if (iRawlen > 16) + pMAGN->iMB = mng_get_uint16 (pRawdata+16); + else + pMAGN->iMB = pMAGN->iMY; + + if (iRawlen > 18) + pMAGN->iMethodY = (mng_uint8)(mng_get_uint16 (pRawdata+18)); + else + pMAGN->iMethodY = pMAGN->iMethodX; + } + else /* proper layout !!!! */ + { + if (iRawlen > 0) /* get the fields */ + pMAGN->iFirstid = mng_get_uint16 (pRawdata); + else + pMAGN->iFirstid = 0; + + if (iRawlen > 2) + pMAGN->iLastid = mng_get_uint16 (pRawdata+2); + else + pMAGN->iLastid = pMAGN->iFirstid; + + if (iRawlen > 4) + pMAGN->iMethodX = *(pRawdata+4); + else + pMAGN->iMethodX = 0; + + if (iRawlen > 5) + pMAGN->iMX = mng_get_uint16 (pRawdata+5); + else + pMAGN->iMX = 1; + + if (iRawlen > 7) + pMAGN->iMY = mng_get_uint16 (pRawdata+7); + else + pMAGN->iMY = pMAGN->iMX; + + if (iRawlen > 9) + pMAGN->iML = mng_get_uint16 (pRawdata+9); + else + pMAGN->iML = pMAGN->iMX; + + if (iRawlen > 11) + pMAGN->iMR = mng_get_uint16 (pRawdata+11); + else + pMAGN->iMR = pMAGN->iMX; + + if (iRawlen > 13) + pMAGN->iMT = mng_get_uint16 (pRawdata+13); + else + pMAGN->iMT = pMAGN->iMY; + + if (iRawlen > 15) + pMAGN->iMB = mng_get_uint16 (pRawdata+15); + else + pMAGN->iMB = pMAGN->iMY; + + if (iRawlen > 17) + pMAGN->iMethodY = *(pRawdata+17); + else + pMAGN->iMethodY = pMAGN->iMethodX; + } + /* check field validity */ + if ((pMAGN->iMethodX > 5) || (pMAGN->iMethodY > 5)) + MNG_ERROR (pData, MNG_INVALIDMETHOD); + + *piRawlen = 0; + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_MAGN +MNG_C_SPECIALFUNC (mng_special_magn) +{ +#ifdef MNG_SUPPORT_DISPLAY + return mng_create_ani_magn (pData, pChunk); +#else + return MNG_NOERROR; +#endif /* MNG_SUPPORT_DISPLAY */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_evNT +MNG_F_SPECIALFUNC (mng_evnt_entries) +{ + mng_evntp pEVNT = (mng_evntp)pChunk; + mng_uint32 iRawlen; + mng_uint8p pRawdata; +#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_SUPPORT_DYNAMICMNG) + mng_retcode iRetcode; +#endif + mng_uint8p pNull; + mng_uint8 iEventtype; + mng_uint8 iMasktype; + mng_int32 iLeft; + mng_int32 iRight; + mng_int32 iTop; + mng_int32 iBottom; + mng_uint16 iObjectid; + mng_uint8 iIndex; + mng_uint32 iNamesize; + mng_uint32 iCount = 0; + mng_evnt_entryp pEntry = MNG_NULL; + mng_uint32 iX; + + for (iX = 0; iX < 2; iX++) + { + iRawlen = *piRawlen; + pRawdata = *ppRawdata; + + if (iX) /* second run ? */ + { + MNG_ALLOC (pData, pEntry, (iCount * sizeof (mng_evnt_entry))); + pEVNT->iCount = iCount; + pEVNT->pEntries = pEntry; + } + + while (iRawlen) /* anything left ? */ + { + if (iRawlen < 2) /* must have at least 2 bytes ! */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + iEventtype = *pRawdata; /* eventtype */ + if (iEventtype > 5) + MNG_ERROR (pData, MNG_INVALIDEVENT); + + pRawdata++; + + iMasktype = *pRawdata; /* masktype */ + if (iMasktype > 5) + MNG_ERROR (pData, MNG_INVALIDMASK); + + pRawdata++; + iRawlen -= 2; + + iLeft = 0; + iRight = 0; + iTop = 0; + iBottom = 0; + iObjectid = 0; + iIndex = 0; + + switch (iMasktype) + { + case 1 : + { + if (iRawlen > 16) + { + iLeft = mng_get_int32 (pRawdata); + iRight = mng_get_int32 (pRawdata+4); + iTop = mng_get_int32 (pRawdata+8); + iBottom = mng_get_int32 (pRawdata+12); + pRawdata += 16; + iRawlen -= 16; + } + else + MNG_ERROR (pData, MNG_INVALIDLENGTH); + break; + } + case 2 : + { + if (iRawlen > 2) + { + iObjectid = mng_get_uint16 (pRawdata); + pRawdata += 2; + iRawlen -= 2; + } + else + MNG_ERROR (pData, MNG_INVALIDLENGTH); + break; + } + case 3 : + { + if (iRawlen > 3) + { + iObjectid = mng_get_uint16 (pRawdata); + iIndex = *(pRawdata+2); + pRawdata += 3; + iRawlen -= 3; + } + else + MNG_ERROR (pData, MNG_INVALIDLENGTH); + break; + } + case 4 : + { + if (iRawlen > 18) + { + iLeft = mng_get_int32 (pRawdata); + iRight = mng_get_int32 (pRawdata+4); + iTop = mng_get_int32 (pRawdata+8); + iBottom = mng_get_int32 (pRawdata+12); + iObjectid = mng_get_uint16 (pRawdata+16); + pRawdata += 18; + iRawlen -= 18; + } + else + MNG_ERROR (pData, MNG_INVALIDLENGTH); + break; + } + case 5 : + { + if (iRawlen > 19) + { + iLeft = mng_get_int32 (pRawdata); + iRight = mng_get_int32 (pRawdata+4); + iTop = mng_get_int32 (pRawdata+8); + iBottom = mng_get_int32 (pRawdata+12); + iObjectid = mng_get_uint16 (pRawdata+16); + iIndex = *(pRawdata+18); + pRawdata += 19; + iRawlen -= 19; + } + else + MNG_ERROR (pData, MNG_INVALIDLENGTH); + break; + } + } + + pNull = pRawdata; /* get the name length */ + while (*pNull) + pNull++; + + if ((pNull - pRawdata) > (mng_int32)iRawlen) + { + iNamesize = iRawlen; /* no null found; so end of evNT */ + iRawlen = 0; + } + else + { + iNamesize = pNull - pRawdata; /* should be another entry */ + iRawlen = iRawlen - iNamesize - 1; + + if (!iRawlen) /* must not end with a null ! */ + MNG_ERROR (pData, MNG_ENDWITHNULL); + } + + if (!iX) + { + iCount++; + } + else + { + pEntry->iEventtype = iEventtype; + pEntry->iMasktype = iMasktype; + pEntry->iLeft = iLeft; + pEntry->iRight = iRight; + pEntry->iTop = iTop; + pEntry->iBottom = iBottom; + pEntry->iObjectid = iObjectid; + pEntry->iIndex = iIndex; + pEntry->iSegmentnamesize = iNamesize; + + if (iNamesize) + { + MNG_ALLOC (pData, pEntry->zSegmentname, iNamesize+1); + MNG_COPY (pEntry->zSegmentname, pRawdata, iNamesize); + } + +#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_SUPPORT_DYNAMICMNG) + iRetcode = mng_create_event (pData, (mng_ptr)pEntry); + if (iRetcode) /* on error bail out */ + return iRetcode; +#endif + + pEntry++; + } + + pRawdata = pRawdata + iNamesize + 1; + } + } + + *piRawlen = 0; + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_evNT +MNG_C_SPECIALFUNC (mng_special_evnt) +{ + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_MPNG_PROPOSAL +MNG_C_SPECIALFUNC (mng_special_mpng) +{ + if ((pData->eImagetype != mng_it_png) && (pData->eImagetype != mng_it_jng)) + MNG_ERROR (pData, MNG_CHUNKNOTALLOWED); + +#ifdef MNG_SUPPORT_DISPLAY + return mng_create_mpng_obj (pData, pChunk); +#else + return MNG_NOERROR; +#endif +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_ANG_PROPOSAL +MNG_C_SPECIALFUNC (mng_special_ahdr) +{ +#ifdef MNG_SUPPORT_DISPLAY + return mng_create_ang_obj (pData, pChunk); +#else + return MNG_NOERROR; +#endif +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_ANG_PROPOSAL +MNG_F_SPECIALFUNC (mng_adat_tiles) +{ + if ((pData->eImagetype != mng_it_ang) || (!pData->pANG)) + MNG_ERROR (pData, MNG_CHUNKNOTALLOWED); + + { + mng_adatp pADAT = (mng_adatp)pChunk; + mng_ang_objp pANG = (mng_ang_objp)pData->pANG; + mng_uint32 iRawlen = *piRawlen; + mng_uint8p pRawdata = *ppRawdata; + mng_retcode iRetcode; + mng_uint8p pBuf; + mng_uint32 iBufsize; + mng_uint32 iRealsize; + mng_uint8p pTemp; + mng_uint8p pTemp2; + mng_int32 iX; + mng_int32 iSize; + +#ifdef MNG_SUPPORT_DISPLAY + mng_imagep pImage; + mng_int32 iTemplen; + mng_uint8p pSwap; + + mng_processobject pProcess; + + mng_uint32 iSavedatawidth; + mng_uint32 iSavedataheight; + + mng_fptr fSaveinitrowproc; + mng_fptr fSavestorerow; + mng_fptr fSaveprocessrow; + mng_fptr fSavedifferrow; + mng_imagep fSavestoreobj; + mng_imagedatap fSavestorebuf; + +#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT + png_imgtype eSavepngimgtype; +#endif + + mng_uint8 iSaveinterlace; + mng_int8 iSavepass; + mng_int32 iSaverow; + mng_int32 iSaverowinc; + mng_int32 iSavecol; + mng_int32 iSavecolinc; + mng_int32 iSaverowsamples; + mng_int32 iSavesamplemul; + mng_int32 iSavesampleofs; + mng_int32 iSavesamplediv; + mng_int32 iSaverowsize; + mng_int32 iSaverowmax; + mng_int32 iSavefilterofs; + mng_int32 iSavepixelofs; + mng_uint32 iSavelevel0; + mng_uint32 iSavelevel1; + mng_uint32 iSavelevel2; + mng_uint32 iSavelevel3; + mng_uint8p pSaveworkrow; + mng_uint8p pSaveprevrow; + mng_uint8p pSaverGBArow; + mng_bool bSaveisRGBA16; + mng_bool bSaveisOpaque; + mng_int32 iSavefilterbpp; + + mng_int32 iSavedestl; + mng_int32 iSavedestt; + mng_int32 iSavedestr; + mng_int32 iSavedestb; + mng_int32 iSavesourcel; + mng_int32 iSavesourcet; + mng_int32 iSavesourcer; + mng_int32 iSavesourceb; +#endif /* MNG_SUPPORT_DISPLAY */ + + iRetcode = mng_inflate_buffer (pData, pRawdata, iRawlen, + &pBuf, &iBufsize, &iRealsize); + if (iRetcode) /* on error bail out */ + { /* don't forget to drop the temp buffer */ + MNG_FREEX (pData, pBuf, iBufsize); + return iRetcode; + } + /* get buffer for tile info in ADAT chunk */ + pADAT->iTilessize = pANG->iNumframes * sizeof(mng_adat_tile); + MNG_ALLOCX (pData, pADAT->pTiles, pADAT->iTilessize); + if (!pADAT->pTiles) + { + pADAT->iTilessize = 0; + MNG_FREEX (pData, pBuf, iBufsize); + MNG_ERROR (pData, MNG_OUTOFMEMORY); + } + + pTemp = pBuf; + pTemp2 = (mng_uint8p)pADAT->pTiles; + + if (!pANG->iStillused) + iSize = 12; + else + iSize = 13; + + for (iX = 0; iX < pANG->iNumframes; iX++) + { + MNG_COPY (pTemp2, pTemp, iSize); + pTemp += iSize; + pTemp2 += sizeof(mng_adat_tile); + } + +#ifdef MNG_SUPPORT_DISPLAY + /* get buffer for tile info in ANG object */ + pANG->iTilessize = pADAT->iTilessize; + MNG_ALLOCX (pData, pANG->pTiles, pANG->iTilessize); + if (!pANG->pTiles) + { + pANG->iTilessize = 0; + MNG_FREEX (pData, pBuf, iBufsize); + MNG_ERROR (pData, MNG_OUTOFMEMORY); + } + /* copy it from the ADAT object */ + MNG_COPY (pANG->pTiles, pADAT->pTiles, pANG->iTilessize); + + /* save IDAT work-parms */ + fSaveinitrowproc = pData->fInitrowproc; + fSavestorerow = pData->fDisplayrow; + fSaveprocessrow = pData->fProcessrow; + fSavedifferrow = pData->fDifferrow; + fSavestoreobj = pData->pStoreobj; + fSavestorebuf = pData->pStorebuf; + +#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT + eSavepngimgtype = pData->ePng_imgtype; +#endif + + iSavedatawidth = pData->iDatawidth; + iSavedataheight = pData->iDataheight; + iSaveinterlace = pData->iInterlace; + iSavepass = pData->iPass; + iSaverow = pData->iRow; + iSaverowinc = pData->iRowinc; + iSavecol = pData->iCol; + iSavecolinc = pData->iColinc; + iSaverowsamples = pData->iRowsamples; + iSavesamplemul = pData->iSamplemul; + iSavesampleofs = pData->iSampleofs; + iSavesamplediv = pData->iSamplediv; + iSaverowsize = pData->iRowsize; + iSaverowmax = pData->iRowmax; + iSavefilterofs = pData->iFilterofs; + iSavepixelofs = pData->iPixelofs; + iSavelevel0 = pData->iLevel0; + iSavelevel1 = pData->iLevel1; + iSavelevel2 = pData->iLevel2; + iSavelevel3 = pData->iLevel3; + pSaveworkrow = pData->pWorkrow; + pSaveprevrow = pData->pPrevrow; + pSaverGBArow = pData->pRGBArow; + bSaveisRGBA16 = pData->bIsRGBA16; + bSaveisOpaque = pData->bIsOpaque; + iSavefilterbpp = pData->iFilterbpp; + iSavedestl = pData->iDestl; + iSavedestt = pData->iDestt; + iSavedestr = pData->iDestr; + iSavedestb = pData->iDestb; + iSavesourcel = pData->iSourcel; + iSavesourcet = pData->iSourcet; + iSavesourcer = pData->iSourcer; + iSavesourceb = pData->iSourceb; + + pData->iDatawidth = pANG->iTilewidth; + pData->iDataheight = pANG->iTileheight; + + pData->iDestl = 0; + pData->iDestt = 0; + pData->iDestr = pANG->iTilewidth; + pData->iDestb = pANG->iTileheight; + pData->iSourcel = 0; + pData->iSourcet = 0; + pData->iSourcer = pANG->iTilewidth; + pData->iSourceb = pANG->iTileheight; + + pData->fInitrowproc = MNG_NULL; + pData->fStorerow = MNG_NULL; + pData->fProcessrow = MNG_NULL; + pData->fDifferrow = MNG_NULL; + + /* clone image object to store the pixel-data from object 0 */ + iRetcode = mng_clone_imageobject (pData, 1, MNG_FALSE, MNG_FALSE, MNG_FALSE, + MNG_FALSE, 0, 0, 0, pData->pObjzero, &pImage); + if (iRetcode) /* on error, drop temp buffer and bail */ + { + MNG_FREEX (pData, pBuf, iBufsize); + return iRetcode; + } + + /* make sure we got the right dimensions and interlacing */ + iRetcode = mng_reset_object_details (pData, pImage, pANG->iTilewidth, pANG->iTileheight, + pImage->pImgbuf->iBitdepth, pImage->pImgbuf->iColortype, + pImage->pImgbuf->iCompression, pImage->pImgbuf->iFilter, + pANG->iInterlace, MNG_FALSE); + if (iRetcode) /* on error, drop temp buffer and bail */ + { + MNG_FREEX (pData, pBuf, iBufsize); + return iRetcode; + } + + pData->pStoreobj = pImage; + +#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT + pData->fInitrowproc = (mng_fptr)mng_init_rowproc; + pData->ePng_imgtype = mng_png_imgtype(pData->iColortype,pData->iBitdepth); +#else + switch (pData->iColortype) /* determine row initialization routine */ + { + case 0 : { /* gray */ + switch (pData->iBitdepth) + { +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case 1 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_g1_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_g1_i; + + break; + } + case 2 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_g2_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_g2_i; + + break; + } + case 4 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_g4_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_g4_i; + break; + } +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + case 8 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_g8_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_g8_i; + + break; + } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_g16_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_g16_i; + + break; + } +#endif + } + + break; + } + case 2 : { /* rgb */ + switch (pData->iBitdepth) + { + case 8 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_rgb8_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_rgb8_i; + break; + } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_rgb16_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_rgb16_i; + + break; + } +#endif + } + + break; + } + case 3 : { /* indexed */ + switch (pData->iBitdepth) + { +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case 1 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_idx1_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_idx1_i; + + break; + } + case 2 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_idx2_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_idx2_i; + + break; + } + case 4 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_idx4_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_idx4_i; + + break; + } +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + case 8 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_idx8_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_idx8_i; + + break; + } + } + + break; + } + case 4 : { /* gray+alpha */ + switch (pData->iBitdepth) + { + case 8 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_ga8_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_ga8_i; + + break; + } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_ga16_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_ga16_i; + break; + } +#endif + } + + break; + } + case 6 : { /* rgb+alpha */ + switch (pData->iBitdepth) + { + case 8 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_rgba8_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_rgba8_i; + + break; + } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_rgba16_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_rgba16_i; + + break; + } +#endif + } + + break; + } + } +#endif /* MNG_OPTIMIZE_FOOTPRINT_INIT */ + + pData->iFilterofs = 0; /* determine filter characteristics */ + pData->iLevel0 = 0; /* default levels */ + pData->iLevel1 = 0; + pData->iLevel2 = 0; + pData->iLevel3 = 0; + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + { + switch (pData->iColortype) + { + case 0 : { + if (pData->iBitdepth <= 8) + pData->iFilterofs = 1; + else + pData->iFilterofs = 2; + + break; + } + case 2 : { + if (pData->iBitdepth <= 8) + pData->iFilterofs = 3; + else + pData->iFilterofs = 6; + + break; + } + case 3 : { + pData->iFilterofs = 1; + break; + } + case 4 : { + if (pData->iBitdepth <= 8) + pData->iFilterofs = 2; + else + pData->iFilterofs = 4; + + break; + } + case 6 : { + if (pData->iBitdepth <= 8) + pData->iFilterofs = 4; + else + pData->iFilterofs = 8; + + break; + } + } + } +#endif + +#ifdef FILTER193 /* no adaptive filtering ? */ + if (pData->iFilter == MNG_FILTER_NOFILTER) + pData->iPixelofs = pData->iFilterofs; + else +#endif + pData->iPixelofs = pData->iFilterofs + 1; + + if (pData->fInitrowproc) /* need to initialize row processing? */ + { + iRetcode = ((mng_initrowproc)pData->fInitrowproc) (pData); + if (iRetcode) + { + MNG_FREEX (pData, pBuf, iBufsize); + return iRetcode; + } + } + /* calculate remainder of buffer */ + pTemp = pBuf + (mng_int32)(pANG->iNumframes * iSize); + iTemplen = iRealsize - (mng_int32)(pANG->iNumframes * iSize); + + do + { + if (iTemplen > pData->iRowmax) /* get a pixel-row from the temp buffer */ + { + MNG_COPY (pData->pWorkrow, pTemp, pData->iRowmax); + } + else + { + MNG_COPY (pData->pWorkrow, pTemp, iTemplen); + } + + { /* image not completed yet ? */ + if (pData->iRow < (mng_int32)pData->iDataheight) + { +#ifdef MNG_NO_1_2_4BIT_SUPPORT + if (pData->iPNGdepth == 1) + { + /* Inflate Workrow to 8-bit */ + mng_int32 iX; + mng_uint8p pSrc = pData->pWorkrow+1; + mng_uint8p pDest = pSrc + pData->iRowsize - (pData->iRowsize+7)/8; + + for (iX = ((pData->iRowsize+7)/8) ; iX > 0 ; iX--) + *pDest++ = *pSrc++; + + pDest = pData->pWorkrow+1; + pSrc = pDest + pData->iRowsize - (pData->iRowsize+7)/8; + for (iX = pData->iRowsize; ;) + { + *pDest++ = (((*pSrc)>>7)&1); + if (iX-- <= 0) + break; + *pDest++ = (((*pSrc)>>6)&1); + if (iX-- <= 0) + break; + *pDest++ = (((*pSrc)>>5)&1); + if (iX-- <= 0) + break; + *pDest++ = (((*pSrc)>>4)&1); + if (iX-- <= 0) + break; + *pDest++ = (((*pSrc)>>3)&1); + if (iX-- <= 0) + break; + *pDest++ = (((*pSrc)>>2)&1); + if (iX-- <= 0) + break; + *pDest++ = (((*pSrc)>>1)&1); + if (iX-- <= 0) + break; + *pDest++ = (((*pSrc) )&1); + if (iX-- <= 0) + break; + pSrc++; + } + } + else if (pData->iPNGdepth == 2) + { + /* Inflate Workrow to 8-bit */ + mng_int32 iX; + mng_uint8p pSrc = pData->pWorkrow+1; + mng_uint8p pDest = pSrc + pData->iRowsize - (2*pData->iRowsize+7)/8; + + for (iX = ((2*pData->iRowsize+7)/8) ; iX > 0 ; iX--) + *pDest++ = *pSrc++; + + pDest = pData->pWorkrow+1; + pSrc = pDest + pData->iRowsize - (2*pData->iRowsize+7)/8; + for (iX = pData->iRowsize; ;) + { + *pDest++ = (((*pSrc)>>6)&3); + if (iX-- <= 0) + break; + *pDest++ = (((*pSrc)>>4)&3); + if (iX-- <= 0) + break; + *pDest++ = (((*pSrc)>>2)&3); + if (iX-- <= 0) + break; + *pDest++ = (((*pSrc) )&3); + if (iX-- <= 0) + break; + pSrc++; + } + } + else if (pData->iPNGdepth == 4) + { + /* Inflate Workrow to 8-bit */ + mng_int32 iX; + mng_uint8p pSrc = pData->pWorkrow+1; + mng_uint8p pDest = pSrc + pData->iRowsize - (4*pData->iRowsize+7)/8; + + for (iX = ((4*pData->iRowsize+7)/8) ; iX > 0 ; iX--) + *pDest++ = *pSrc++; + + pDest = pData->pWorkrow+1; + pSrc = pDest + pData->iRowsize - (4*pData->iRowsize+7)/8; + for (iX = pData->iRowsize; ;) + { + *pDest++ = (((*pSrc)>>4)&0x0f); + if (iX-- <= 0) + break; + *pDest++ = (((*pSrc) )&0x0f); + if (iX-- <= 0) + break; + pSrc++; + } + } + if (pData->iPNGdepth < 8 && pData->iColortype == 0) + { + /* Expand samples to 8-bit by LBR */ + mng_int32 iX; + mng_uint8p pSrc = pData->pWorkrow+1; + mng_uint8 multiplier[]={0,255,85,0,17,0,0,0,1}; + + for (iX = pData->iRowsize; iX > 0; iX--) + *pSrc++ *= multiplier[pData->iPNGdepth]; + } +#endif +#ifdef MNG_NO_16BIT_SUPPORT + if (pData->iPNGdepth > 8) + { + /* Reduce Workrow to 8-bit */ + mng_int32 iX; + mng_uint8p pSrc = pData->pWorkrow+1; + mng_uint8p pDest = pSrc; + + for (iX = pData->iRowsize; iX > 0; iX--) + { + *pDest = *pSrc; + pDest++; + pSrc+=2; + } + } +#endif + +#ifdef FILTER192 /* has leveling info ? */ + if (pData->iFilterofs == MNG_FILTER_DIFFERING) + iRetcode = init_rowdiffering (pData); + else +#endif + iRetcode = MNG_NOERROR; + /* filter the row if necessary */ + if ((!iRetcode) && (pData->iFilterofs < pData->iPixelofs ) && + (*(pData->pWorkrow + pData->iFilterofs)) ) + iRetcode = mng_filter_a_row (pData); + + /* additional leveling/differing ? */ + if ((!iRetcode) && (pData->fDifferrow)) + { + iRetcode = ((mng_differrow)pData->fDifferrow) (pData); + + pSwap = pData->pWorkrow; + pData->pWorkrow = pData->pPrevrow; + pData->pPrevrow = pSwap; /* make sure we're processing the right data */ + } + + if (!iRetcode) + { + { /* process this row */ + if ((!iRetcode) && (pData->fProcessrow)) + iRetcode = ((mng_processrow)pData->fProcessrow) (pData); + /* store in object ? */ + if ((!iRetcode) && (pData->fStorerow)) + iRetcode = ((mng_storerow)pData->fStorerow) (pData); + } + } + + if (iRetcode) /* on error bail out */ + { + MNG_FREEX (pData, pBuf, iBufsize); + MNG_ERROR (pData, iRetcode); + } + + if (!pData->fDifferrow) /* swap row-pointers */ + { + pSwap = pData->pWorkrow; + pData->pWorkrow = pData->pPrevrow; + pData->pPrevrow = pSwap; /* so prev points to the processed row! */ + } + /* adjust variables for next row */ + iRetcode = mng_next_row (pData); + + if (iRetcode) /* on error bail out */ + { + MNG_FREEX (pData, pBuf, iBufsize); + MNG_ERROR (pData, iRetcode); + } + } + } + + pTemp += pData->iRowmax; + iTemplen -= pData->iRowmax; + } /* until some error or EOI + or all pixels received */ + while ( (iTemplen > 0) && + ( (pData->iRow < (mng_int32)pData->iDataheight) || + ( (pData->iPass >= 0) && (pData->iPass < 7) ) ) ); + + mng_cleanup_rowproc (pData); /* cleanup row processing buffers !! */ + + /* restore saved work-parms */ + pData->iDatawidth = iSavedatawidth; + pData->iDataheight = iSavedataheight; + + pData->fInitrowproc = fSaveinitrowproc; + pData->fDisplayrow = fSavestorerow; + pData->fProcessrow = fSaveprocessrow; + pData->fDifferrow = fSavedifferrow; + pData->pStoreobj = fSavestoreobj; + pData->pStorebuf = fSavestorebuf; + +#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT + pData->ePng_imgtype = eSavepngimgtype; +#endif + + pData->iInterlace = iSaveinterlace; + pData->iPass = iSavepass; + pData->iRow = iSaverow; + pData->iRowinc = iSaverowinc; + pData->iCol = iSavecol; + pData->iColinc = iSavecolinc; + pData->iRowsamples = iSaverowsamples; + pData->iSamplemul = iSavesamplemul; + pData->iSampleofs = iSavesampleofs; + pData->iSamplediv = iSavesamplediv; + pData->iRowsize = iSaverowsize; + pData->iRowmax = iSaverowmax; + pData->iFilterofs = iSavefilterofs; + pData->iPixelofs = iSavepixelofs; + pData->iLevel0 = iSavelevel0; + pData->iLevel1 = iSavelevel1; + pData->iLevel2 = iSavelevel2; + pData->iLevel3 = iSavelevel3; + pData->pWorkrow = pSaveworkrow; + pData->pPrevrow = pSaveprevrow; + pData->pRGBArow = pSaverGBArow; + pData->bIsRGBA16 = bSaveisRGBA16; + pData->bIsOpaque = bSaveisOpaque; + pData->iFilterbpp = iSavefilterbpp; + pData->iDestl = iSavedestl; + pData->iDestt = iSavedestt; + pData->iDestr = iSavedestr; + pData->iDestb = iSavedestb; + pData->iSourcel = iSavesourcel; + pData->iSourcet = iSavesourcet; + pData->iSourcer = iSavesourcer; + pData->iSourceb = iSavesourceb; + + /* create the animation directives ! */ + pProcess = (mng_processobject)pANG->sHeader.fProcess; + iRetcode = pProcess (pData, (mng_objectp)pData->pANG); + if (iRetcode) + return iRetcode; + +#endif /* MNG_SUPPORT_DISPLAY */ + + MNG_FREE (pData, pBuf, iBufsize); /* always free the temp buffer ! */ + } + + *piRawlen = 0; + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_ANG_PROPOSAL +MNG_C_SPECIALFUNC (mng_special_adat) +{ + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +MNG_C_SPECIALFUNC (mng_special_unknown) +{ + /* critical chunk ? */ + if ((((mng_uint32)pData->iChunkname & 0x20000000) == 0) +#ifdef MNG_SKIPCHUNK_SAVE + && (pData->iChunkname != MNG_UINT_SAVE) +#endif +#ifdef MNG_SKIPCHUNK_SEEK + && (pData->iChunkname != MNG_UINT_SEEK) +#endif +#ifdef MNG_SKIPCHUNK_DBYK + && (pData->iChunkname != MNG_UINT_DBYK) +#endif +#ifdef MNG_SKIPCHUNK_ORDR + && (pData->iChunkname != MNG_UINT_ORDR) +#endif + ) + MNG_ERROR (pData, MNG_UNKNOWNCRITICAL); + + if (pData->fProcessunknown) /* let the app handle it ? */ + { + mng_bool bOke = pData->fProcessunknown ((mng_handle)pData, pData->iChunkname, + ((mng_unknown_chunkp)pChunk)->iDatasize, + ((mng_unknown_chunkp)pChunk)->pData); + if (!bOke) + MNG_ERROR (pData, MNG_APPMISCERROR); + } + + return MNG_NOERROR; /* done */ +} + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_READ_PROCS || MNG_INCLUDE_WRITE_PROCS */ +#endif /* MNG_OPTIMIZE_CHUNKREADER */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ + + + + + diff --git a/Engine/lib/lmng/libmng_chunk_descr.h b/Engine/lib/lmng/libmng_chunk_descr.h new file mode 100644 index 000000000..3781ab052 --- /dev/null +++ b/Engine/lib/lmng/libmng_chunk_descr.h @@ -0,0 +1,146 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_chunk_descr.h copyright (c) 2007 G.Juyn * */ +/* * version : 1.0.10 * */ +/* * * */ +/* * purpose : Chunk descriptor functions (implementation) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : definition of the chunk- anf field-descriptor routines * */ +/* * * */ +/* * changes : 1.0.9 - 12/06/2004 - G.Juyn * */ +/* * - added conditional MNG_OPTIMIZE_CHUNKREADER * */ +/* * * */ +/* * 1.0.10 - 04/08/2007 - G.Juyn * */ +/* * - added support for mPNG proposal * */ +/* * 1.0.10 - 04/12/2007 - G.Juyn * */ +/* * - added support for ANG proposal * */ +/* * * */ +/* ************************************************************************** */ + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +#ifndef _libmng_chunk_descr_h_ +#define _libmng_chunk_descr_h_ + +/* ************************************************************************** */ + +#ifdef MNG_OPTIMIZE_CHUNKREADER +#if defined(MNG_INCLUDE_READ_PROCS) || defined(MNG_INCLUDE_WRITE_PROCS) + +/* ************************************************************************** */ + +void mng_get_chunkheader (mng_chunkid iChunkname, + mng_chunk_headerp pResult); + +/* ************************************************************************** */ + +#define MNG_F_SPECIALFUNC(n) mng_retcode n (mng_datap pData, \ + mng_chunkp pChunk, \ + mng_uint32* piRawlen, \ + mng_uint8p* ppRawdata) + +MNG_F_SPECIALFUNC (mng_debunk_plte) ; +MNG_F_SPECIALFUNC (mng_debunk_trns) ; +MNG_F_SPECIALFUNC (mng_deflate_itxt) ; +MNG_F_SPECIALFUNC (mng_splt_entries) ; +MNG_F_SPECIALFUNC (mng_hist_entries) ; + +MNG_F_SPECIALFUNC (mng_debunk_loop) ; +MNG_F_SPECIALFUNC (mng_debunk_past) ; +MNG_F_SPECIALFUNC (mng_disc_entries) ; +MNG_F_SPECIALFUNC (mng_fram_remainder) ; +MNG_F_SPECIALFUNC (mng_save_entries) ; +MNG_F_SPECIALFUNC (mng_pplt_entries) ; +MNG_F_SPECIALFUNC (mng_drop_entries) ; +MNG_F_SPECIALFUNC (mng_ordr_entries) ; +MNG_F_SPECIALFUNC (mng_debunk_magn) ; +MNG_F_SPECIALFUNC (mng_evnt_entries) ; +MNG_F_SPECIALFUNC (mng_adat_tiles) ; + +/* ************************************************************************** */ + +#define MNG_C_SPECIALFUNC(n) mng_retcode n (mng_datap pData, \ + mng_chunkp pChunk) + +MNG_C_SPECIALFUNC (mng_special_ihdr) ; +MNG_C_SPECIALFUNC (mng_special_plte) ; +MNG_C_SPECIALFUNC (mng_special_idat) ; +MNG_C_SPECIALFUNC (mng_special_iend) ; +MNG_C_SPECIALFUNC (mng_special_trns) ; +MNG_C_SPECIALFUNC (mng_special_gama) ; +MNG_C_SPECIALFUNC (mng_special_chrm) ; +MNG_C_SPECIALFUNC (mng_special_srgb) ; +MNG_C_SPECIALFUNC (mng_special_iccp) ; +MNG_C_SPECIALFUNC (mng_special_text) ; +MNG_C_SPECIALFUNC (mng_special_ztxt) ; +MNG_C_SPECIALFUNC (mng_special_itxt) ; +MNG_C_SPECIALFUNC (mng_special_bkgd) ; +MNG_C_SPECIALFUNC (mng_special_phys) ; +MNG_C_SPECIALFUNC (mng_special_sbit) ; +MNG_C_SPECIALFUNC (mng_special_splt) ; +MNG_C_SPECIALFUNC (mng_special_hist) ; +MNG_C_SPECIALFUNC (mng_special_time) ; + +MNG_C_SPECIALFUNC (mng_special_jhdr) ; +MNG_C_SPECIALFUNC (mng_special_jdaa) ; +MNG_C_SPECIALFUNC (mng_special_jdat) ; +MNG_C_SPECIALFUNC (mng_special_jsep) ; + +MNG_C_SPECIALFUNC (mng_special_mhdr) ; +MNG_C_SPECIALFUNC (mng_special_mend) ; +MNG_C_SPECIALFUNC (mng_special_loop) ; +MNG_C_SPECIALFUNC (mng_special_endl) ; +MNG_C_SPECIALFUNC (mng_special_defi) ; +MNG_C_SPECIALFUNC (mng_special_basi) ; +MNG_C_SPECIALFUNC (mng_special_clon) ; +MNG_C_SPECIALFUNC (mng_special_past) ; +MNG_C_SPECIALFUNC (mng_special_disc) ; +MNG_C_SPECIALFUNC (mng_special_back) ; +MNG_C_SPECIALFUNC (mng_special_fram) ; +MNG_C_SPECIALFUNC (mng_special_move) ; +MNG_C_SPECIALFUNC (mng_special_clip) ; +MNG_C_SPECIALFUNC (mng_special_show) ; +MNG_C_SPECIALFUNC (mng_special_term) ; +MNG_C_SPECIALFUNC (mng_special_save) ; +MNG_C_SPECIALFUNC (mng_special_seek) ; +MNG_C_SPECIALFUNC (mng_special_expi) ; +MNG_C_SPECIALFUNC (mng_special_fpri) ; +MNG_C_SPECIALFUNC (mng_special_need) ; +MNG_C_SPECIALFUNC (mng_special_phyg) ; + +MNG_C_SPECIALFUNC (mng_special_dhdr) ; +MNG_C_SPECIALFUNC (mng_special_prom) ; +MNG_C_SPECIALFUNC (mng_special_ipng) ; +MNG_C_SPECIALFUNC (mng_special_pplt) ; +MNG_C_SPECIALFUNC (mng_special_ijng) ; +MNG_C_SPECIALFUNC (mng_special_drop) ; +MNG_C_SPECIALFUNC (mng_special_dbyk) ; +MNG_C_SPECIALFUNC (mng_special_ordr) ; + +MNG_C_SPECIALFUNC (mng_special_magn) ; +MNG_C_SPECIALFUNC (mng_special_evnt) ; +MNG_C_SPECIALFUNC (mng_special_mpng) ; +MNG_C_SPECIALFUNC (mng_special_ahdr) ; +MNG_C_SPECIALFUNC (mng_special_adat) ; +MNG_C_SPECIALFUNC (mng_special_unknown) ; + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_READ_PROCS) || MNG_INCLUDE_WRITE_PROCS */ +#endif /* MNG_OPTIMIZE_CHUNKREADER */ + +/* ************************************************************************** */ + +#endif /* _libmng_chunk_descr_h_ */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ diff --git a/Engine/lib/lmng/libmng_chunk_io.c b/Engine/lib/lmng/libmng_chunk_io.c new file mode 100644 index 000000000..eb18099fd --- /dev/null +++ b/Engine/lib/lmng/libmng_chunk_io.c @@ -0,0 +1,10740 @@ +/** ************************************************************************* */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_chunk_io.c copyright (c) 2000-2007 G.Juyn * */ +/* * version : 1.0.10 * */ +/* * * */ +/* * purpose : Chunk I/O routines (implementation) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : implementation of chunk input/output routines * */ +/* * * */ +/* * changes : 0.5.1 - 05/01/2000 - G.Juyn * */ +/* * - cleaned up left-over teststuff in the BACK chunk routine * */ +/* * 0.5.1 - 05/04/2000 - G.Juyn * */ +/* * - changed CRC initialization to use dynamic structure * */ +/* * (wasn't thread-safe the old way !) * */ +/* * 0.5.1 - 05/06/2000 - G.Juyn * */ +/* * - filled in many missing sequence&length checks * */ +/* * - filled in many missing chunk-store snippets * */ +/* * 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - added checks for running animations * */ +/* * - filled some write routines * */ +/* * - changed strict-ANSI stuff * */ +/* * 0.5.1 - 05/10/2000 - G.Juyn * */ +/* * - filled some more write routines * */ +/* * 0.5.1 - 05/11/2000 - G.Juyn * */ +/* * - filled remaining write routines * */ +/* * - fixed read_pplt with regard to deltatype * */ +/* * - added callback error-reporting support * */ +/* * - added pre-draft48 support (short MHDR, frame_mode, LOOP) * */ +/* * 0.5.1 - 05/12/2000 - G.Juyn * */ +/* * - changed trace to macro for callback error-reporting * */ +/* * - fixed chunk-storage bit in several routines * */ +/* * 0.5.1 - 05/13/2000 - G.Juyn * */ +/* * - added eMNGma hack (will be removed in 1.0.0 !!!) * */ +/* * - added TERM animation object pointer (easier reference) * */ +/* * - supplemented the SAVE & SEEK display processing * */ +/* * * */ +/* * 0.5.2 - 05/18/2000 - G.Juyn * */ +/* * - B004 - fixed problem with MNG_SUPPORT_WRITE not defined * */ +/* * also for MNG_SUPPORT_WRITE without MNG_INCLUDE_JNG * */ +/* * 0.5.2 - 05/19/2000 - G.Juyn * */ +/* * - cleaned up some code regarding mixed support * */ +/* * 0.5.2 - 05/20/2000 - G.Juyn * */ +/* * - implemented JNG support * */ +/* * 0.5.2 - 05/24/2000 - G.Juyn * */ +/* * - added support for global color-chunks in animation * */ +/* * - added support for global PLTE,tRNS,bKGD in animation * */ +/* * - added support for SAVE & SEEK in animation * */ +/* * 0.5.2 - 05/29/2000 - G.Juyn * */ +/* * - changed ani_create calls not returning object pointer * */ +/* * - create ani objects always (not just inside TERM/LOOP) * */ +/* * 0.5.2 - 05/30/2000 - G.Juyn * */ +/* * - added support for delta-image processing * */ +/* * 0.5.2 - 05/31/2000 - G.Juyn * */ +/* * - fixed up punctuation (contributed by Tim Rowley) * */ +/* * 0.5.2 - 06/02/2000 - G.Juyn * */ +/* * - changed SWAP_ENDIAN to BIGENDIAN_SUPPORTED * */ +/* * 0.5.2 - 06/03/2000 - G.Juyn * */ +/* * - fixed makeup for Linux gcc compile * */ +/* * * */ +/* * 0.5.3 - 06/12/2000 - G.Juyn * */ +/* * - added processing of color-info on delta-image * */ +/* * 0.5.3 - 06/13/2000 - G.Juyn * */ +/* * - fixed handling of empty SAVE chunk * */ +/* * 0.5.3 - 06/17/2000 - G.Juyn * */ +/* * - changed to support delta-images * */ +/* * - added extra checks for delta-images * */ +/* * 0.5.3 - 06/20/2000 - G.Juyn * */ +/* * - fixed possible trouble if IEND display-process got * */ +/* * broken up * */ +/* * 0.5.3 - 06/21/2000 - G.Juyn * */ +/* * - added processing of PLTE & tRNS for delta-images * */ +/* * - added administration of imagelevel parameter * */ +/* * 0.5.3 - 06/22/2000 - G.Juyn * */ +/* * - implemented support for PPLT chunk * */ +/* * 0.5.3 - 06/26/2000 - G.Juyn * */ +/* * - added precaution against faulty iCCP chunks from PS * */ +/* * 0.5.3 - 06/29/2000 - G.Juyn * */ +/* * - fixed some 64-bit warnings * */ +/* * * */ +/* * 0.9.1 - 07/14/2000 - G.Juyn * */ +/* * - changed pre-draft48 frame_mode=3 to frame_mode=1 * */ +/* * 0.9.1 - 07/16/2000 - G.Juyn * */ +/* * - fixed storage of images during mng_read() * */ +/* * - fixed support for mng_display() after mng_read() * */ +/* * 0.9.1 - 07/19/2000 - G.Juyn * */ +/* * - fixed several chunk-writing routines * */ +/* * 0.9.1 - 07/24/2000 - G.Juyn * */ +/* * - fixed reading of still-images * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 0.9.3 - 08/07/2000 - G.Juyn * */ +/* * - B111300 - fixup for improved portability * */ +/* * 0.9.3 - 08/08/2000 - G.Juyn * */ +/* * - fixed compiler-warnings from Mozilla * */ +/* * 0.9.3 - 08/09/2000 - G.Juyn * */ +/* * - added check for simplicity-bits in MHDR * */ +/* * 0.9.3 - 08/12/2000 - G.Juyn * */ +/* * - fixed check for simplicity-bits in MHDR (JNG) * */ +/* * 0.9.3 - 08/12/2000 - G.Juyn * */ +/* * - added workaround for faulty PhotoShop iCCP chunk * */ +/* * 0.9.3 - 08/22/2000 - G.Juyn * */ +/* * - fixed write-code for zTXt & iTXt * */ +/* * - fixed read-code for iTXt * */ +/* * 0.9.3 - 08/26/2000 - G.Juyn * */ +/* * - added MAGN chunk * */ +/* * 0.9.3 - 09/07/2000 - G.Juyn * */ +/* * - added support for new filter_types * */ +/* * 0.9.3 - 09/10/2000 - G.Juyn * */ +/* * - fixed DEFI behavior * */ +/* * 0.9.3 - 10/02/2000 - G.Juyn * */ +/* * - fixed simplicity-check in compliance with draft 81/0.98a * */ +/* * 0.9.3 - 10/10/2000 - G.Juyn * */ +/* * - added support for alpha-depth prediction * */ +/* * 0.9.3 - 10/11/2000 - G.Juyn * */ +/* * - added support for nEED * */ +/* * 0.9.3 - 10/16/2000 - G.Juyn * */ +/* * - added support for JDAA * */ +/* * 0.9.3 - 10/17/2000 - G.Juyn * */ +/* * - fixed support for MAGN * */ +/* * - implemented nEED "xxxx" (where "xxxx" is a chunkid) * */ +/* * - added callback to process non-critical unknown chunks * */ +/* * - fixed support for bKGD * */ +/* * 0.9.3 - 10/23/2000 - G.Juyn * */ +/* * - fixed bug in empty PLTE handling * */ +/* * * */ +/* * 0.9.4 - 11/20/2000 - G.Juyn * */ +/* * - changed IHDR filter_method check for PNGs * */ +/* * 0.9.4 - 1/18/2001 - G.Juyn * */ +/* * - added errorchecking for MAGN methods * */ +/* * - removed test filter-methods 1 & 65 * */ +/* * * */ +/* * 0.9.5 - 1/25/2001 - G.Juyn * */ +/* * - fixed some small compiler warnings (thanks Nikki) * */ +/* * * */ +/* * 1.0.2 - 05/05/2000 - G.Juyn * */ +/* * - B421427 - writes wrong format in bKGD and tRNS * */ +/* * 1.0.2 - 06/20/2000 - G.Juyn * */ +/* * - B434583 - compiler-warning if MNG_STORE_CHUNKS undefined * */ +/* * * */ +/* * 1.0.5 - 07/08/2002 - G.Juyn * */ +/* * - B578572 - removed eMNGma hack (thanks Dimitri!) * */ +/* * 1.0.5 - 08/07/2002 - G.Juyn * */ +/* * - added test-option for PNG filter method 193 (=no filter) * */ +/* * 1.0.5 - 08/15/2002 - G.Juyn * */ +/* * - completed PROM support * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * 1.0.5 - 09/07/2002 - G.Juyn * */ +/* * - fixed reading of FRAM with just frame_mode and name * */ +/* * 1.0.5 - 09/13/2002 - G.Juyn * */ +/* * - fixed read/write of MAGN chunk * */ +/* * 1.0.5 - 09/14/2002 - G.Juyn * */ +/* * - added event handling for dynamic MNG * */ +/* * 1.0.5 - 09/15/2002 - G.Juyn * */ +/* * - fixed LOOP iteration=0 special case * */ +/* * 1.0.5 - 09/19/2002 - G.Juyn * */ +/* * - misplaced TERM is now treated as warning * */ +/* * 1.0.5 - 09/20/2002 - G.Juyn * */ +/* * - added support for PAST * */ +/* * 1.0.5 - 10/03/2002 - G.Juyn * */ +/* * - fixed chunk-storage for evNT chunk * */ +/* * 1.0.5 - 10/07/2002 - G.Juyn * */ +/* * - fixed DISC support * */ +/* * - added another fix for misplaced TERM chunk * */ +/* * 1.0.5 - 10/17/2002 - G.Juyn * */ +/* * - fixed initializtion of pIds in dISC read routine * */ +/* * 1.0.5 - 11/06/2002 - G.Juyn * */ +/* * - added support for nEED "MNG 1.1" * */ +/* * - added support for nEED "CACHEOFF" * */ +/* * * */ +/* * 1.0.6 - 05/25/2003 - G.R-P * */ +/* * - added MNG_SKIPCHUNK_cHNK footprint optimizations * */ +/* * 1.0.6 - 06/02/2003 - G.R-P * */ +/* * - removed some redundant checks for iRawlen==0 * */ +/* * 1.0.6 - 06/22/2003 - G.R-P * */ +/* * - added MNG_NO_16BIT_SUPPORT, MNG_NO_DELTA_PNG reductions * */ +/* * - optionally use zlib's crc32 function instead of * */ +/* * local mng_update_crc * */ +/* * 1.0.6 - 07/14/2003 - G.R-P * */ +/* * - added MNG_NO_LOOP_SIGNALS_SUPPORTED conditional * */ +/* * 1.0.6 - 07/29/2003 - G.R-P * */ +/* * - added conditionals around PAST chunk support * */ +/* * 1.0.6 - 08/17/2003 - G.R-P * */ +/* * - added conditionals around non-VLC chunk support * */ +/* * * */ +/* * 1.0.7 - 10/29/2003 - G.R-P * */ +/* * - revised JDAA and JDAT readers to avoid compiler bug * */ +/* * 1.0.7 - 01/25/2004 - J.S * */ +/* * - added premultiplied alpha canvas' for RGBA, ARGB, ABGR * */ +/* * 1.0.7 - 01/27/2004 - J.S * */ +/* * - fixed inclusion of IJNG chunk for non-JNG use * */ +/* * 1.0.7 - 02/26/2004 - G.Juyn * */ +/* * - fixed bug in chunk-storage of SHOW chunk (from == to) * */ +/* * * */ +/* * 1.0.8 - 04/02/2004 - G.Juyn * */ +/* * - added CRC existence & checking flags * */ +/* * 1.0.8 - 07/07/2004 - G.R-P * */ +/* * - change worst-case iAlphadepth to 1 for standalone PNGs * */ +/* * * */ +/* * 1.0.9 - 09/28/2004 - G.R-P * */ +/* * - improved handling of cheap transparency when 16-bit * */ +/* * support is disabled * */ +/* * 1.0.9 - 10/04/2004 - G.Juyn * */ +/* * - fixed bug in writing sBIT for indexed color * */ +/* * 1.0.9 - 10/10/2004 - G.R-P. * */ +/* * - added MNG_NO_1_2_4BIT_SUPPORT * */ +/* * 1.0.9 - 12/05/2004 - G.Juyn * */ +/* * - added conditional MNG_OPTIMIZE_CHUNKINITFREE * */ +/* * 1.0.9 - 12/06/2004 - G.Juyn * */ +/* * - added conditional MNG_OPTIMIZE_CHUNKASSIGN * */ +/* * 1.0.9 - 12/07/2004 - G.Juyn * */ +/* * - added conditional MNG_OPTIMIZE_CHUNKREADER * */ +/* * 1.0.9 - 12/11/2004 - G.Juyn * */ +/* * - added conditional MNG_OPTIMIZE_DISPLAYCALLS * */ +/* * 1.0.9 - 12/20/2004 - G.Juyn * */ +/* * - cleaned up macro-invocations (thanks to D. Airlie) * */ +/* * 1.0.9 - 01/17/2005 - G.Juyn * */ +/* * - fixed problem with global PLTE/tRNS * */ +/* * * */ +/* * 1.0.10 - 02/07/2005 - G.Juyn * */ +/* * - fixed display routines called twice for FULL_MNG * */ +/* * support in mozlibmngconf.h * */ +/* * 1.0.10 - 12/04/2005 - G.R-P. * */ +/* * - #ifdef out use of mng_inflate_buffer when it is not * */ +/* * available. * */ +/* * 1.0.10 - 04/08/2007 - G.Juyn * */ +/* * - added support for mPNG proposal * */ +/* * 1.0.10 - 04/12/2007 - G.Juyn * */ +/* * - added support for ANG proposal * */ +/* * 1.0.10 - 05/02/2007 - G.Juyn * */ +/* * - fixed inflate_buffer for extreme compression ratios * */ +/* * * */ +/* ************************************************************************** */ + +#include "libmng.h" +#include "libmng_data.h" +#include "libmng_error.h" +#include "libmng_trace.h" +#ifdef __BORLANDC__ +#pragma hdrstop +#endif +#include "libmng_objects.h" +#include "libmng_object_prc.h" +#include "libmng_chunks.h" +#ifdef MNG_CHECK_BAD_ICCP +#include "libmng_chunk_prc.h" +#endif +#include "libmng_memory.h" +#include "libmng_display.h" +#include "libmng_zlib.h" +#include "libmng_pixels.h" +#include "libmng_chunk_io.h" + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +/* ************************************************************************** */ +/* * * */ +/* * CRC - Cyclic Redundancy Check * */ +/* * * */ +/* * The code below is taken directly from the sample provided with the * */ +/* * PNG specification. * */ +/* * (it is only adapted to the library's internal data-definitions) * */ +/* * * */ +/* ************************************************************************** */ +/* Make the table for a fast CRC. */ +#ifndef MNG_USE_ZLIB_CRC +MNG_LOCAL void make_crc_table (mng_datap pData) +{ + mng_uint32 iC; + mng_int32 iN, iK; + + for (iN = 0; iN < 256; iN++) + { + iC = (mng_uint32) iN; + + for (iK = 0; iK < 8; iK++) + { + if (iC & 1) + iC = 0xedb88320U ^ (iC >> 1); + else + iC = iC >> 1; + } + + pData->aCRCtable [iN] = iC; + } + + pData->bCRCcomputed = MNG_TRUE; +} +#endif + +/* Update a running CRC with the bytes buf[0..len-1]--the CRC + should be initialized to all 1's, and the transmitted value + is the 1's complement of the final running CRC (see the + crc() routine below). */ + +MNG_LOCAL mng_uint32 update_crc (mng_datap pData, + mng_uint32 iCrc, + mng_uint8p pBuf, + mng_int32 iLen) +{ +#ifdef MNG_USE_ZLIB_CRC + return crc32 (iCrc, pBuf, iLen); +#else + mng_uint32 iC = iCrc; + mng_int32 iN; + + if (!pData->bCRCcomputed) + make_crc_table (pData); + + for (iN = 0; iN < iLen; iN++) + iC = pData->aCRCtable [(iC ^ pBuf [iN]) & 0xff] ^ (iC >> 8); + + return iC; +#endif +} + +/* Return the CRC of the bytes buf[0..len-1]. */ +mng_uint32 mng_crc (mng_datap pData, + mng_uint8p pBuf, + mng_int32 iLen) +{ +#ifdef MNG_USE_ZLIB_CRC + return update_crc (pData, 0, pBuf, iLen); +#else + return update_crc (pData, 0xffffffffU, pBuf, iLen) ^ 0xffffffffU; +#endif +} + +/* ************************************************************************** */ +/* * * */ +/* * Routines for swapping byte-order from and to graphic files * */ +/* * (This code is adapted from the libpng package) * */ +/* * * */ +/* ************************************************************************** */ + +#ifndef MNG_BIGENDIAN_SUPPORTED + +/* ************************************************************************** */ + +mng_uint32 mng_get_uint32 (mng_uint8p pBuf) +{ + mng_uint32 i = ((mng_uint32)(*pBuf) << 24) + + ((mng_uint32)(*(pBuf + 1)) << 16) + + ((mng_uint32)(*(pBuf + 2)) << 8) + + (mng_uint32)(*(pBuf + 3)); + return (i); +} + +/* ************************************************************************** */ + +mng_int32 mng_get_int32 (mng_uint8p pBuf) +{ + mng_int32 i = ((mng_int32)(*pBuf) << 24) + + ((mng_int32)(*(pBuf + 1)) << 16) + + ((mng_int32)(*(pBuf + 2)) << 8) + + (mng_int32)(*(pBuf + 3)); + return (i); +} + +/* ************************************************************************** */ + +mng_uint16 mng_get_uint16 (mng_uint8p pBuf) +{ + mng_uint16 i = (mng_uint16)(((mng_uint16)(*pBuf) << 8) + + (mng_uint16)(*(pBuf + 1))); + return (i); +} + +/* ************************************************************************** */ + +void mng_put_uint32 (mng_uint8p pBuf, + mng_uint32 i) +{ + *pBuf = (mng_uint8)((i >> 24) & 0xff); + *(pBuf+1) = (mng_uint8)((i >> 16) & 0xff); + *(pBuf+2) = (mng_uint8)((i >> 8) & 0xff); + *(pBuf+3) = (mng_uint8)(i & 0xff); +} + +/* ************************************************************************** */ + +void mng_put_int32 (mng_uint8p pBuf, + mng_int32 i) +{ + *pBuf = (mng_uint8)((i >> 24) & 0xff); + *(pBuf+1) = (mng_uint8)((i >> 16) & 0xff); + *(pBuf+2) = (mng_uint8)((i >> 8) & 0xff); + *(pBuf+3) = (mng_uint8)(i & 0xff); +} + +/* ************************************************************************** */ + +void mng_put_uint16 (mng_uint8p pBuf, + mng_uint16 i) +{ + *pBuf = (mng_uint8)((i >> 8) & 0xff); + *(pBuf+1) = (mng_uint8)(i & 0xff); +} + +/* ************************************************************************** */ + +#endif /* !MNG_BIGENDIAN_SUPPORTED */ + +/* ************************************************************************** */ +/* * * */ +/* * Helper routines to simplify chunk-data extraction * */ +/* * * */ +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_READ_PROCS + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +MNG_LOCAL mng_uint8p find_null (mng_uint8p pIn) +{ + mng_uint8p pOut = pIn; + while (*pOut) /* the read_graphic routine has made sure there's */ + pOut++; /* always at least 1 zero-byte in the buffer */ + return pOut; +} +#endif + +/* ************************************************************************** */ + +#if !defined(MNG_SKIPCHUNK_iCCP) || !defined(MNG_SKIPCHUNK_zTXt) || \ + !defined(MNG_SKIPCHUNK_iTXt) || defined(MNG_INCLUDE_MPNG_PROPOSAL) || \ + defined(MNG_INCLUDE_ANG_PROPOSAL) +mng_retcode mng_inflate_buffer (mng_datap pData, + mng_uint8p pInbuf, + mng_uint32 iInsize, + mng_uint8p *pOutbuf, + mng_uint32 *iOutsize, + mng_uint32 *iRealsize) +{ + mng_retcode iRetcode = MNG_NOERROR; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INFLATE_BUFFER, MNG_LC_START); +#endif + + if (iInsize) /* anything to do ? */ + { + *iOutsize = iInsize * 3; /* estimate uncompressed size */ + /* and allocate a temporary buffer */ + MNG_ALLOC (pData, *pOutbuf, *iOutsize); + + do + { + mngzlib_inflateinit (pData); /* initialize zlib */ + /* let zlib know where to store the output */ + pData->sZlib.next_out = *pOutbuf; + /* "size - 1" so we've got space for the + zero-termination of a possible string */ + pData->sZlib.avail_out = *iOutsize - 1; + /* ok; let's inflate... */ + iRetcode = mngzlib_inflatedata (pData, iInsize, pInbuf); + /* determine actual output size */ + *iRealsize = (mng_uint32)pData->sZlib.total_out; + + mngzlib_inflatefree (pData); /* zlib's done */ + + if (iRetcode == MNG_BUFOVERFLOW) /* not enough space ? */ + { /* then get some more */ + MNG_FREEX (pData, *pOutbuf, *iOutsize); + *iOutsize = *iOutsize + *iOutsize; + MNG_ALLOC (pData, *pOutbuf, *iOutsize); + } + } /* repeat if we didn't have enough space */ + while ((iRetcode == MNG_BUFOVERFLOW) && + (*iOutsize < 200 * iInsize)); + + if (!iRetcode) /* if oke ? */ + *((*pOutbuf) + *iRealsize) = 0; /* then put terminator zero */ + + } + else + { + *pOutbuf = 0; /* nothing to do; then there's no output */ + *iOutsize = 0; + *iRealsize = 0; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INFLATE_BUFFER, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_READ_PROCS */ + +/* ************************************************************************** */ +/* * * */ +/* * Helper routines to simplify chunk writing * */ +/* * * */ +/* ************************************************************************** */ +#ifdef MNG_INCLUDE_WRITE_PROCS +/* ************************************************************************** */ + +#if !defined(MNG_SKIPCHUNK_iCCP) || !defined(MNG_SKIPCHUNK_zTXt) || !defined(MNG_SKIPCHUNK_iTXt) +MNG_LOCAL mng_retcode deflate_buffer (mng_datap pData, + mng_uint8p pInbuf, + mng_uint32 iInsize, + mng_uint8p *pOutbuf, + mng_uint32 *iOutsize, + mng_uint32 *iRealsize) +{ + mng_retcode iRetcode = MNG_NOERROR; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DEFLATE_BUFFER, MNG_LC_START); +#endif + + if (iInsize) /* anything to do ? */ + { + *iOutsize = (iInsize * 5) >> 2; /* estimate compressed size */ + /* and allocate a temporary buffer */ + MNG_ALLOC (pData, *pOutbuf, *iOutsize); + + do + { + mngzlib_deflateinit (pData); /* initialize zlib */ + /* let zlib know where to store the output */ + pData->sZlib.next_out = *pOutbuf; + pData->sZlib.avail_out = *iOutsize; + /* ok; let's deflate... */ + iRetcode = mngzlib_deflatedata (pData, iInsize, pInbuf); + /* determine actual output size */ + *iRealsize = pData->sZlib.total_out; + + mngzlib_deflatefree (pData); /* zlib's done */ + + if (iRetcode == MNG_BUFOVERFLOW) /* not enough space ? */ + { /* then get some more */ + MNG_FREEX (pData, *pOutbuf, *iOutsize); + *iOutsize = *iOutsize + (iInsize >> 1); + MNG_ALLOC (pData, *pOutbuf, *iOutsize); + } + } /* repeat if we didn't have enough space */ + while (iRetcode == MNG_BUFOVERFLOW); + } + else + { + *pOutbuf = 0; /* nothing to do; then there's no output */ + *iOutsize = 0; + *iRealsize = 0; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DEFLATE_BUFFER, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +MNG_LOCAL mng_retcode write_raw_chunk (mng_datap pData, + mng_chunkid iChunkname, + mng_uint32 iRawlen, + mng_uint8p pRawdata) +{ + mng_uint32 iCrc; + mng_uint32 iWritten; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_RAW_CHUNK, MNG_LC_START); +#endif + /* temporary buffer ? */ + if ((pRawdata != 0) && (pRawdata != pData->pWritebuf+8)) + { /* store length & chunktype in default buffer */ + mng_put_uint32 (pData->pWritebuf, iRawlen); + mng_put_uint32 (pData->pWritebuf+4, (mng_uint32)iChunkname); + + if (pData->iCrcmode & MNG_CRC_OUTPUT) + { + if ((pData->iCrcmode & MNG_CRC_OUTPUT) == MNG_CRC_OUTPUT_GENERATE) + { /* calculate the crc */ + iCrc = update_crc (pData, 0xffffffffL, pData->pWritebuf+4, 4); + iCrc = update_crc (pData, iCrc, pRawdata, iRawlen) ^ 0xffffffffL; + } else { + iCrc = 0; /* dummy crc */ + } /* store in default buffer */ + mng_put_uint32 (pData->pWritebuf+8, iCrc); + } + /* write the length & chunktype */ + if (!pData->fWritedata ((mng_handle)pData, pData->pWritebuf, 8, &iWritten)) + MNG_ERROR (pData, MNG_APPIOERROR); + + if (iWritten != 8) /* disk full ? */ + MNG_ERROR (pData, MNG_OUTPUTERROR); + /* write the temporary buffer */ + if (!pData->fWritedata ((mng_handle)pData, pRawdata, iRawlen, &iWritten)) + MNG_ERROR (pData, MNG_APPIOERROR); + + if (iWritten != iRawlen) /* disk full ? */ + MNG_ERROR (pData, MNG_OUTPUTERROR); + + if (pData->iCrcmode & MNG_CRC_OUTPUT) + { /* write the crc */ + if (!pData->fWritedata ((mng_handle)pData, pData->pWritebuf+8, 4, &iWritten)) + MNG_ERROR (pData, MNG_APPIOERROR); + + if (iWritten != 4) /* disk full ? */ + MNG_ERROR (pData, MNG_OUTPUTERROR); + } + } + else + { /* prefix with length & chunktype */ + mng_put_uint32 (pData->pWritebuf, iRawlen); + mng_put_uint32 (pData->pWritebuf+4, (mng_uint32)iChunkname); + + if (pData->iCrcmode & MNG_CRC_OUTPUT) + { + if ((pData->iCrcmode & MNG_CRC_OUTPUT) == MNG_CRC_OUTPUT_GENERATE) + /* calculate the crc */ + iCrc = mng_crc (pData, pData->pWritebuf+4, iRawlen + 4); + else + iCrc = 0; /* dummy crc */ + /* add it to the buffer */ + mng_put_uint32 (pData->pWritebuf + iRawlen + 8, iCrc); + /* write it in a single pass */ + if (!pData->fWritedata ((mng_handle)pData, pData->pWritebuf, iRawlen + 12, &iWritten)) + MNG_ERROR (pData, MNG_APPIOERROR); + + if (iWritten != iRawlen + 12) /* disk full ? */ + MNG_ERROR (pData, MNG_OUTPUTERROR); + } else { + if (!pData->fWritedata ((mng_handle)pData, pData->pWritebuf, iRawlen + 8, &iWritten)) + MNG_ERROR (pData, MNG_APPIOERROR); + + if (iWritten != iRawlen + 8) /* disk full ? */ + MNG_ERROR (pData, MNG_OUTPUTERROR); + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_RAW_CHUNK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ +/* B004 */ +#endif /* MNG_INCLUDE_WRITE_PROCS */ +/* B004 */ +/* ************************************************************************** */ +/* * * */ +/* * chunk read functions * */ +/* * * */ +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_READ_PROCS + +/* ************************************************************************** */ + +#ifdef MNG_OPTIMIZE_CHUNKREADER + +/* ************************************************************************** */ + +MNG_LOCAL mng_retcode create_chunk_storage (mng_datap pData, + mng_chunkp pHeader, + mng_uint32 iRawlen, + mng_uint8p pRawdata, + mng_field_descp pField, + mng_uint16 iFields, + mng_chunkp* ppChunk, + mng_bool bWorkcopy) +{ + mng_field_descp pTempfield = pField; + mng_uint16 iFieldcount = iFields; + mng_uint8p pTempdata = pRawdata; + mng_uint32 iTemplen = iRawlen; + mng_uint16 iLastgroup = 0; + mng_uint8p pChunkdata; + mng_uint32 iDatalen; + mng_uint8 iColortype; + mng_bool bProcess; + /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + if (iRetcode) /* on error bail out */ + return iRetcode; + + if (((mng_chunk_headerp)(*ppChunk))->iChunkname == MNG_UINT_HUH) + ((mng_chunk_headerp)(*ppChunk))->iChunkname = pData->iChunkname; + + if ((!bWorkcopy) || + ((((mng_chunk_headerp)pHeader)->iChunkname != MNG_UINT_IDAT) && + (((mng_chunk_headerp)pHeader)->iChunkname != MNG_UINT_JDAT) && + (((mng_chunk_headerp)pHeader)->iChunkname != MNG_UINT_JDAA) )) + { + pChunkdata = (mng_uint8p)(*ppChunk); + +#ifdef MNG_INCLUDE_JNG /* determine current colortype */ + if (pData->bHasJHDR) + iColortype = (mng_uint8)(pData->iJHDRcolortype - 8); + else +#endif /* MNG_INCLUDE_JNG */ + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) + iColortype = pData->iColortype; + else + iColortype = 6; + + if (iTemplen) /* not empty ? */ + { /* then go fill the fields */ + while ((iFieldcount) && (iTemplen)) + { + if (pTempfield->iOffsetchunk) + { + if (pTempfield->iFlags & MNG_FIELD_PUTIMGTYPE) + { + *(pChunkdata+pTempfield->iOffsetchunk) = iColortype; + bProcess = MNG_FALSE; + } + else + if (pTempfield->iFlags & MNG_FIELD_IFIMGTYPES) + bProcess = (mng_bool)(((pTempfield->iFlags & MNG_FIELD_IFIMGTYPE0) && (iColortype == 0)) || + ((pTempfield->iFlags & MNG_FIELD_IFIMGTYPE2) && (iColortype == 2)) || + ((pTempfield->iFlags & MNG_FIELD_IFIMGTYPE3) && (iColortype == 3)) || + ((pTempfield->iFlags & MNG_FIELD_IFIMGTYPE4) && (iColortype == 4)) || + ((pTempfield->iFlags & MNG_FIELD_IFIMGTYPE6) && (iColortype == 6)) ); + else + bProcess = MNG_TRUE; + + if (bProcess) + { + iLastgroup = (mng_uint16)(pTempfield->iFlags & MNG_FIELD_GROUPMASK); + /* numeric field ? */ + if (pTempfield->iFlags & MNG_FIELD_INT) + { + if (iTemplen < pTempfield->iLengthmax) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + switch (pTempfield->iLengthmax) + { + case 1 : { mng_uint8 iNum = *pTempdata; + if (((mng_uint16)iNum < pTempfield->iMinvalue) || + ((mng_uint16)iNum > pTempfield->iMaxvalue) ) + MNG_ERROR (pData, MNG_INVALIDFIELDVAL); + *(pChunkdata+pTempfield->iOffsetchunk) = iNum; + break; } + case 2 : { mng_uint16 iNum = mng_get_uint16 (pTempdata); + if ((iNum < pTempfield->iMinvalue) || (iNum > pTempfield->iMaxvalue)) + MNG_ERROR (pData, MNG_INVALIDFIELDVAL); + *((mng_uint16p)(pChunkdata+pTempfield->iOffsetchunk)) = iNum; + break; } + case 4 : { mng_uint32 iNum = mng_get_uint32 (pTempdata); + if ((iNum < pTempfield->iMinvalue) || + ((pTempfield->iFlags & MNG_FIELD_NOHIGHBIT) && (iNum & 0x80000000)) ) + MNG_ERROR (pData, MNG_INVALIDFIELDVAL); + *((mng_uint32p)(pChunkdata+pTempfield->iOffsetchunk)) = iNum; + break; } + } + + pTempdata += pTempfield->iLengthmax; + iTemplen -= pTempfield->iLengthmax; + + } else { /* not numeric so it's a bunch of bytes */ + + if (!pTempfield->iOffsetchunklen) /* big fat NONO */ + MNG_ERROR (pData, MNG_INTERNALERROR); + /* with terminating 0 ? */ + if (pTempfield->iFlags & MNG_FIELD_TERMINATOR) + { + mng_uint8p pWork = pTempdata; + while (*pWork) /* find the zero */ + pWork++; + iDatalen = (mng_uint32)(pWork - pTempdata); + } else { /* no terminator, so everything that's left ! */ + iDatalen = iTemplen; + } + + if ((pTempfield->iLengthmax) && (iDatalen > pTempfield->iLengthmax)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); +#if !defined(MNG_SKIPCHUNK_iCCP) || !defined(MNG_SKIPCHUNK_zTXt) || \ + !defined(MNG_SKIPCHUNK_iTXt) || defined(MNG_INCLUDE_MPNG_PROPOSAL) || \ + defined(MNG_INCLUDE_ANG_PROPOSAL) + /* needs decompression ? */ + if (pTempfield->iFlags & MNG_FIELD_DEFLATED) + { + mng_uint8p pBuf = 0; + mng_uint32 iBufsize = 0; + mng_uint32 iRealsize; + mng_ptr pWork; + + iRetcode = mng_inflate_buffer (pData, pTempdata, iDatalen, + &pBuf, &iBufsize, &iRealsize); + +#ifdef MNG_CHECK_BAD_ICCP /* Check for bad iCCP chunk */ + if ((iRetcode) && (((mng_chunk_headerp)pHeader)->iChunkname == MNG_UINT_iCCP)) + { + *((mng_ptr *)(pChunkdata+pTempfield->iOffsetchunk)) = MNG_NULL; + *((mng_uint32p)(pChunkdata+pTempfield->iOffsetchunklen)) = iDatalen; + } + else +#endif + { + if (iRetcode) + return iRetcode; + +#if defined(MNG_INCLUDE_MPNG_PROPOSAL) || defined(MNG_INCLUDE_ANG_PROPOSAL) + if ( (((mng_chunk_headerp)pHeader)->iChunkname == MNG_UINT_mpNG) || + (((mng_chunk_headerp)pHeader)->iChunkname == MNG_UINT_adAT) ) + { + MNG_ALLOC (pData, pWork, iRealsize); + } + else + { +#endif + /* don't forget to generate null terminator */ + MNG_ALLOC (pData, pWork, iRealsize+1); +#if defined(MNG_INCLUDE_MPNG_PROPOSAL) || defined(MNG_INCLUDE_ANG_PROPOSAL) + } +#endif + MNG_COPY (pWork, pBuf, iRealsize); + + *((mng_ptr *)(pChunkdata+pTempfield->iOffsetchunk)) = pWork; + *((mng_uint32p)(pChunkdata+pTempfield->iOffsetchunklen)) = iRealsize; + } + + if (pBuf) /* free the temporary buffer */ + MNG_FREEX (pData, pBuf, iBufsize); + + } else +#endif + { /* no decompression, so just copy */ + + mng_ptr pWork; + /* don't forget to generate null terminator */ + MNG_ALLOC (pData, pWork, iDatalen+1); + MNG_COPY (pWork, pTempdata, iDatalen); + + *((mng_ptr *)(pChunkdata+pTempfield->iOffsetchunk)) = pWork; + *((mng_uint32p)(pChunkdata+pTempfield->iOffsetchunklen)) = iDatalen; + } + + if (pTempfield->iFlags & MNG_FIELD_TERMINATOR) + iDatalen++; /* skip the terminating zero as well !!! */ + + iTemplen -= iDatalen; + pTempdata += iDatalen; + } + /* need to set an indicator ? */ + if (pTempfield->iOffsetchunkind) + *((mng_uint8p)(pChunkdata+pTempfield->iOffsetchunkind)) = MNG_TRUE; + } + } + + if (pTempfield->pSpecialfunc) /* special function required ? */ + { + iRetcode = pTempfield->pSpecialfunc(pData, *ppChunk, &iTemplen, &pTempdata); + if (iRetcode) /* on error bail out */ + return iRetcode; + } + + pTempfield++; /* Neeeeeeexxxtt */ + iFieldcount--; + } + + if (iTemplen) /* extra data ??? */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + while (iFieldcount) /* not enough data ??? */ + { + if (pTempfield->iFlags & MNG_FIELD_IFIMGTYPES) + bProcess = (mng_bool)(((pTempfield->iFlags & MNG_FIELD_IFIMGTYPE0) && (iColortype == 0)) || + ((pTempfield->iFlags & MNG_FIELD_IFIMGTYPE2) && (iColortype == 2)) || + ((pTempfield->iFlags & MNG_FIELD_IFIMGTYPE3) && (iColortype == 3)) || + ((pTempfield->iFlags & MNG_FIELD_IFIMGTYPE4) && (iColortype == 4)) || + ((pTempfield->iFlags & MNG_FIELD_IFIMGTYPE6) && (iColortype == 6)) ); + else + bProcess = MNG_TRUE; + + if (bProcess) + { + if (!(pTempfield->iFlags & MNG_FIELD_OPTIONAL)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + if ((pTempfield->iFlags & MNG_FIELD_GROUPMASK) && + ((mng_uint16)(pTempfield->iFlags & MNG_FIELD_GROUPMASK) == iLastgroup)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + } + + pTempfield++; + iFieldcount--; + } + } + } + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +READ_CHUNK (mng_read_general) +{ + mng_retcode iRetcode = MNG_NOERROR; + mng_chunk_descp pDescr = ((mng_chunk_headerp)pHeader)->pChunkdescr; + mng_field_descp pField; + mng_uint16 iFields; + + if (!pDescr) /* this is a bad booboo !!! */ + MNG_ERROR (pData, MNG_INTERNALERROR); + + pField = pDescr->pFielddesc; + iFields = pDescr->iFielddesc; + /* check chunk against signature */ + if ((pDescr->eImgtype == mng_it_mng) && (pData->eSigtype != mng_it_mng)) + MNG_ERROR (pData, MNG_CHUNKNOTALLOWED); + + if ((pDescr->eImgtype == mng_it_jng) && (pData->eSigtype == mng_it_png)) + MNG_ERROR (pData, MNG_CHUNKNOTALLOWED); + /* empties allowed ? */ + if ((iRawlen == 0) && (!(pDescr->iAllowed & MNG_DESCR_EMPTY))) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + if ((pData->eImagetype != mng_it_mng) || (!(pDescr->iAllowed & MNG_DESCR_GLOBAL))) + { /* *a* header required ? */ + if ((pDescr->iMusthaves & MNG_DESCR_GenHDR) && +#ifdef MNG_INCLUDE_JNG + (!pData->bHasIHDR) && (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR)) +#else + (!pData->bHasIHDR) && (!pData->bHasBASI) && (!pData->bHasDHDR)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pDescr->iMusthaves & MNG_DESCR_JngHDR) && + (!pData->bHasDHDR) && (!pData->bHasJHDR)) + MNG_ERROR (pData, MNG_SEQUENCEERROR); +#endif + } + /* specific chunk pre-requisite ? */ + if (((pDescr->iMusthaves & MNG_DESCR_IHDR) && (!pData->bHasIHDR)) || +#ifdef MNG_INCLUDE_JNG + ((pDescr->iMusthaves & MNG_DESCR_JHDR) && (!pData->bHasJHDR)) || +#endif + ((pDescr->iMusthaves & MNG_DESCR_DHDR) && (!pData->bHasDHDR)) || + ((pDescr->iMusthaves & MNG_DESCR_LOOP) && (!pData->bHasLOOP)) || + ((pDescr->iMusthaves & MNG_DESCR_PLTE) && (!pData->bHasPLTE)) || + ((pDescr->iMusthaves & MNG_DESCR_MHDR) && (!pData->bHasMHDR)) || + ((pDescr->iMusthaves & MNG_DESCR_SAVE) && (!pData->bHasSAVE)) ) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + /* specific chunk undesired ? */ + if (((pDescr->iMustNOThaves & MNG_DESCR_NOIHDR) && (pData->bHasIHDR)) || + ((pDescr->iMustNOThaves & MNG_DESCR_NOBASI) && (pData->bHasBASI)) || + ((pDescr->iMustNOThaves & MNG_DESCR_NODHDR) && (pData->bHasDHDR)) || + ((pDescr->iMustNOThaves & MNG_DESCR_NOIDAT) && (pData->bHasIDAT)) || + ((pDescr->iMustNOThaves & MNG_DESCR_NOPLTE) && (pData->bHasPLTE)) || +#ifdef MNG_INCLUDE_JNG + ((pDescr->iMustNOThaves & MNG_DESCR_NOJHDR) && (pData->bHasJHDR)) || + ((pDescr->iMustNOThaves & MNG_DESCR_NOJDAT) && (pData->bHasJDAT)) || + ((pDescr->iMustNOThaves & MNG_DESCR_NOJDAA) && (pData->bHasJDAA)) || + ((pDescr->iMustNOThaves & MNG_DESCR_NOJSEP) && (pData->bHasJSEP)) || +#endif + ((pDescr->iMustNOThaves & MNG_DESCR_NOMHDR) && (pData->bHasMHDR)) || + ((pDescr->iMustNOThaves & MNG_DESCR_NOLOOP) && (pData->bHasLOOP)) || + ((pDescr->iMustNOThaves & MNG_DESCR_NOTERM) && (pData->bHasTERM)) || + ((pDescr->iMustNOThaves & MNG_DESCR_NOSAVE) && (pData->bHasSAVE)) ) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (pData->eSigtype == mng_it_mng) /* check global and embedded empty chunks */ + { +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + { + if ((iRawlen == 0) && (!(pDescr->iAllowed & MNG_DESCR_EMPTYEMBED))) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + } else { + if ((iRawlen == 0) && (!(pDescr->iAllowed & MNG_DESCR_EMPTYGLOBAL))) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + } + } + + if (pDescr->pSpecialfunc) /* need special processing ? */ + { + iRetcode = create_chunk_storage (pData, pHeader, iRawlen, pRawdata, + pField, iFields, ppChunk, MNG_TRUE); + if (iRetcode) /* on error bail out */ + return iRetcode; + /* empty indicator ? */ + if ((!iRawlen) && (pDescr->iOffsetempty)) + *(((mng_uint8p)*ppChunk)+pDescr->iOffsetempty) = MNG_TRUE; + + iRetcode = pDescr->pSpecialfunc(pData, *ppChunk); + if (iRetcode) /* on error bail out */ + return iRetcode; + + if ((((mng_chunk_headerp)pHeader)->iChunkname == MNG_UINT_IDAT) || + (((mng_chunk_headerp)pHeader)->iChunkname == MNG_UINT_JDAT) || + (((mng_chunk_headerp)pHeader)->iChunkname == MNG_UINT_JDAA) ) + { + iRetcode = ((mng_chunk_headerp)*ppChunk)->fCleanup (pData, *ppChunk); + if (iRetcode) /* on error bail out */ + return iRetcode; + *ppChunk = MNG_NULL; + } else { +#ifdef MNG_STORE_CHUNKS + if (!pData->bStorechunks) +#endif + { + iRetcode = ((mng_chunk_headerp)*ppChunk)->fCleanup (pData, *ppChunk); + if (iRetcode) /* on error bail out */ + return iRetcode; + *ppChunk = MNG_NULL; + } + } + } + +#ifdef MNG_SUPPORT_DISPLAY + if (iRawlen) + { +#ifdef MNG_OPTIMIZE_DISPLAYCALLS + pData->iRawlen = iRawlen; + pData->pRawdata = pRawdata; +#endif + + /* display processing */ +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + if (((mng_chunk_headerp)pHeader)->iChunkname == MNG_UINT_IDAT) + iRetcode = mng_process_display_idat (pData, iRawlen, pRawdata); +#ifdef MNG_INCLUDE_JNG + else + if (((mng_chunk_headerp)pHeader)->iChunkname == MNG_UINT_JDAT) + iRetcode = mng_process_display_jdat (pData, iRawlen, pRawdata); + else + if (((mng_chunk_headerp)pHeader)->iChunkname == MNG_UINT_JDAA) + iRetcode = mng_process_display_jdaa (pData, iRawlen, pRawdata); +#endif +#else + if (((mng_chunk_headerp)pHeader)->iChunkname == MNG_UINT_IDAT) + iRetcode = mng_process_display_idat (pData); +#ifdef MNG_INCLUDE_JNG + else + if (((mng_chunk_headerp)pHeader)->iChunkname == MNG_UINT_JDAT) + iRetcode = mng_process_display_jdat (pData); + else + if (((mng_chunk_headerp)pHeader)->iChunkname == MNG_UINT_JDAA) + iRetcode = mng_process_display_jdaa (pData); +#endif +#endif + + if (iRetcode) + return iRetcode; + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if ((pData->bStorechunks) && (!(*ppChunk))) + { + iRetcode = create_chunk_storage (pData, pHeader, iRawlen, pRawdata, + pField, iFields, ppChunk, MNG_FALSE); + if (iRetcode) /* on error bail out */ + return iRetcode; + /* empty indicator ? */ + if ((!iRawlen) && (pDescr->iOffsetempty)) + *(((mng_uint8p)*ppChunk)+pDescr->iOffsetempty) = MNG_TRUE; + } +#endif /* MNG_STORE_CHUNKS */ + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#endif /* MNG_OPTIMIZE_CHUNKREADER */ + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +READ_CHUNK (mng_read_ihdr) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_IHDR, MNG_LC_START); +#endif + + if (iRawlen != 13) /* length oke ? */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + /* only allowed inside PNG or MNG */ + if ((pData->eSigtype != mng_it_png) && (pData->eSigtype != mng_it_mng)) + MNG_ERROR (pData, MNG_CHUNKNOTALLOWED); + /* sequence checks */ + if ((pData->eSigtype == mng_it_png) && (pData->iChunkseq > 1)) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasIDAT) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasIDAT)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + pData->bHasIHDR = MNG_TRUE; /* indicate IHDR is present */ + /* and store interesting fields */ + if ((!pData->bHasDHDR) || (pData->iDeltatype == MNG_DELTATYPE_NOCHANGE)) + { + pData->iDatawidth = mng_get_uint32 (pRawdata); + pData->iDataheight = mng_get_uint32 (pRawdata+4); + } + + pData->iBitdepth = *(pRawdata+8); + pData->iColortype = *(pRawdata+9); + pData->iCompression = *(pRawdata+10); + pData->iFilter = *(pRawdata+11); + pData->iInterlace = *(pRawdata+12); + +#if defined(MNG_NO_1_2_4BIT_SUPPORT) || defined(MNG_NO_16BIT_SUPPORT) + pData->iPNGmult = 1; + pData->iPNGdepth = pData->iBitdepth; +#endif + +#ifdef MNG_NO_1_2_4BIT_SUPPORT + if (pData->iBitdepth < 8) + pData->iBitdepth = 8; +#endif + +#ifdef MNG_NO_16BIT_SUPPORT + if (pData->iBitdepth > 8) + { + pData->iBitdepth = 8; + pData->iPNGmult = 2; + } +#endif + + if ((pData->iBitdepth != 8) /* parameter validity checks */ +#ifndef MNG_NO_1_2_4BIT_SUPPORT + && (pData->iBitdepth != 1) && + (pData->iBitdepth != 2) && + (pData->iBitdepth != 4) +#endif +#ifndef MNG_NO_16BIT_SUPPORT + && (pData->iBitdepth != 16) +#endif + ) + MNG_ERROR (pData, MNG_INVALIDBITDEPTH); + + if ((pData->iColortype != MNG_COLORTYPE_GRAY ) && + (pData->iColortype != MNG_COLORTYPE_RGB ) && + (pData->iColortype != MNG_COLORTYPE_INDEXED) && + (pData->iColortype != MNG_COLORTYPE_GRAYA ) && + (pData->iColortype != MNG_COLORTYPE_RGBA ) ) + MNG_ERROR (pData, MNG_INVALIDCOLORTYPE); + + if ((pData->iColortype == MNG_COLORTYPE_INDEXED) && (pData->iBitdepth > 8)) + MNG_ERROR (pData, MNG_INVALIDBITDEPTH); + + if (((pData->iColortype == MNG_COLORTYPE_RGB ) || + (pData->iColortype == MNG_COLORTYPE_GRAYA ) || + (pData->iColortype == MNG_COLORTYPE_RGBA ) ) && + (pData->iBitdepth < 8 ) ) + MNG_ERROR (pData, MNG_INVALIDBITDEPTH); + + if (pData->iCompression != MNG_COMPRESSION_DEFLATE) + MNG_ERROR (pData, MNG_INVALIDCOMPRESS); + +#if defined(FILTER192) || defined(FILTER193) + if ((pData->iFilter != MNG_FILTER_ADAPTIVE ) && +#if defined(FILTER192) && defined(FILTER193) + (pData->iFilter != MNG_FILTER_DIFFERING) && + (pData->iFilter != MNG_FILTER_NOFILTER ) ) +#else +#ifdef FILTER192 + (pData->iFilter != MNG_FILTER_DIFFERING) ) +#else + (pData->iFilter != MNG_FILTER_NOFILTER ) ) +#endif +#endif + MNG_ERROR (pData, MNG_INVALIDFILTER); +#else + if (pData->iFilter) + MNG_ERROR (pData, MNG_INVALIDFILTER); +#endif + + if ((pData->iInterlace != MNG_INTERLACE_NONE ) && + (pData->iInterlace != MNG_INTERLACE_ADAM7) ) + MNG_ERROR (pData, MNG_INVALIDINTERLACE); + +#ifdef MNG_SUPPORT_DISPLAY +#ifndef MNG_NO_DELTA_PNG + if (pData->bHasDHDR) /* check the colortype for delta-images ! */ + { + mng_imagedatap pBuf = ((mng_imagep)pData->pObjzero)->pImgbuf; + + if (pData->iColortype != pBuf->iColortype) + { + if ( ( (pData->iColortype != MNG_COLORTYPE_INDEXED) || + (pBuf->iColortype == MNG_COLORTYPE_GRAY ) ) && + ( (pData->iColortype != MNG_COLORTYPE_GRAY ) || + (pBuf->iColortype == MNG_COLORTYPE_INDEXED) ) ) + MNG_ERROR (pData, MNG_INVALIDCOLORTYPE); + } + } +#endif +#endif + + if (!pData->bHasheader) /* first chunk ? */ + { + pData->bHasheader = MNG_TRUE; /* we've got a header */ + pData->eImagetype = mng_it_png; /* then this must be a PNG */ + pData->iWidth = pData->iDatawidth; + pData->iHeight = pData->iDataheight; + /* predict alpha-depth ! */ + if ((pData->iColortype == MNG_COLORTYPE_GRAYA ) || + (pData->iColortype == MNG_COLORTYPE_RGBA ) ) + pData->iAlphadepth = pData->iBitdepth; + else + if (pData->iColortype == MNG_COLORTYPE_INDEXED) + pData->iAlphadepth = 8; /* worst case scenario */ + else + pData->iAlphadepth = 1; /* Possible tRNS cheap binary transparency */ + /* fits on maximum canvas ? */ + if ((pData->iWidth > pData->iMaxwidth) || (pData->iHeight > pData->iMaxheight)) + MNG_WARNING (pData, MNG_IMAGETOOLARGE); + +#if !defined(MNG_INCLUDE_MPNG_PROPOSAL) || !defined(MNG_SUPPORT_DISPLAY) + if (pData->fProcessheader) /* inform the app ? */ + if (!pData->fProcessheader (((mng_handle)pData), pData->iWidth, pData->iHeight)) + MNG_ERROR (pData, MNG_APPMISCERROR); +#endif + } + + if (!pData->bHasDHDR) + pData->iImagelevel++; /* one level deeper */ + +#ifdef MNG_SUPPORT_DISPLAY + { + mng_retcode iRetcode = mng_process_display_ihdr (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the fields */ + ((mng_ihdrp)*ppChunk)->iWidth = mng_get_uint32 (pRawdata); + ((mng_ihdrp)*ppChunk)->iHeight = mng_get_uint32 (pRawdata+4); + ((mng_ihdrp)*ppChunk)->iBitdepth = pData->iBitdepth; + ((mng_ihdrp)*ppChunk)->iColortype = pData->iColortype; + ((mng_ihdrp)*ppChunk)->iCompression = pData->iCompression; + ((mng_ihdrp)*ppChunk)->iFilter = pData->iFilter; + ((mng_ihdrp)*ppChunk)->iInterlace = pData->iInterlace; + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_IHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif /* MNG_OPTIMIZE_CHUNKREADER */ + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +READ_CHUNK (mng_read_plte) +{ +#if defined(MNG_SUPPORT_DISPLAY) || defined(MNG_STORE_CHUNKS) + mng_uint32 iX; + mng_uint8p pRawdata2; +#endif +#ifdef MNG_SUPPORT_DISPLAY + mng_uint32 iRawlen2; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_PLTE, MNG_LC_START); +#endif + /* sequence checks */ + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && + (!pData->bHasBASI) && (!pData->bHasDHDR) ) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIDAT) || (pData->bHasJHDR)) +#else + if (pData->bHasIDAT) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + /* multiple PLTE only inside BASI */ + if ((pData->bHasPLTE) && (!pData->bHasBASI)) + MNG_ERROR (pData, MNG_MULTIPLEERROR); + /* length must be multiple of 3 */ + if (((iRawlen % 3) != 0) || (iRawlen > 768)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) + { /* only allowed for indexed-color or + rgb(a)-color! */ + if ((pData->iColortype != 2) && (pData->iColortype != 3) && (pData->iColortype != 6)) + MNG_ERROR (pData, MNG_CHUNKNOTALLOWED); + /* empty only allowed if global present */ + if ((iRawlen == 0) && (!pData->bHasglobalPLTE)) + MNG_ERROR (pData, MNG_CANNOTBEEMPTY); + } + else + { + if (iRawlen == 0) /* cannot be empty as global! */ + MNG_ERROR (pData, MNG_CANNOTBEEMPTY); + } + + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) + pData->bHasPLTE = MNG_TRUE; /* got it! */ + else + pData->bHasglobalPLTE = MNG_TRUE; + + pData->iPLTEcount = iRawlen / 3; + +#ifdef MNG_SUPPORT_DISPLAY + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) + { + mng_imagep pImage; + mng_imagedatap pBuf; + +#ifndef MNG_NO_DELTA_PNG + if (pData->bHasDHDR) /* processing delta-image ? */ + { /* store in object 0 !!! */ + pImage = (mng_imagep)pData->pObjzero; + pBuf = pImage->pImgbuf; + pBuf->bHasPLTE = MNG_TRUE; /* it's definitely got a PLTE now */ + pBuf->iPLTEcount = iRawlen / 3; /* this is the exact length */ + pRawdata2 = pRawdata; /* copy the entries */ + + for (iX = 0; iX < iRawlen / 3; iX++) + { + pBuf->aPLTEentries[iX].iRed = *pRawdata2; + pBuf->aPLTEentries[iX].iGreen = *(pRawdata2+1); + pBuf->aPLTEentries[iX].iBlue = *(pRawdata2+2); + + pRawdata2 += 3; + } + } + else +#endif + { /* get the current object */ + pImage = (mng_imagep)pData->pCurrentobj; + + if (!pImage) /* no object then dump it in obj 0 */ + pImage = (mng_imagep)pData->pObjzero; + + pBuf = pImage->pImgbuf; /* address the object buffer */ + pBuf->bHasPLTE = MNG_TRUE; /* and tell it it's got a PLTE now */ + + if (!iRawlen) /* if empty, inherit from global */ + { + pBuf->iPLTEcount = pData->iGlobalPLTEcount; + MNG_COPY (pBuf->aPLTEentries, pData->aGlobalPLTEentries, + sizeof (pBuf->aPLTEentries)); + + if (pData->bHasglobalTRNS) /* also copy global tRNS ? */ + { /* indicate tRNS available */ + pBuf->bHasTRNS = MNG_TRUE; + + iRawlen2 = pData->iGlobalTRNSrawlen; + pRawdata2 = (mng_uint8p)(pData->aGlobalTRNSrawdata); + /* global length oke ? */ + if ((iRawlen2 == 0) || (iRawlen2 > pBuf->iPLTEcount)) + MNG_ERROR (pData, MNG_GLOBALLENGTHERR); + /* copy it */ + pBuf->iTRNScount = iRawlen2; + MNG_COPY (pBuf->aTRNSentries, pRawdata2, iRawlen2); + } + } + else + { /* store fields for future reference */ + pBuf->iPLTEcount = iRawlen / 3; + pRawdata2 = pRawdata; + + for (iX = 0; iX < pBuf->iPLTEcount; iX++) + { + pBuf->aPLTEentries[iX].iRed = *pRawdata2; + pBuf->aPLTEentries[iX].iGreen = *(pRawdata2+1); + pBuf->aPLTEentries[iX].iBlue = *(pRawdata2+2); + + pRawdata2 += 3; + } + } + } + } + else /* store as global */ + { + pData->iGlobalPLTEcount = iRawlen / 3; + pRawdata2 = pRawdata; + + for (iX = 0; iX < pData->iGlobalPLTEcount; iX++) + { + pData->aGlobalPLTEentries[iX].iRed = *pRawdata2; + pData->aGlobalPLTEentries[iX].iGreen = *(pRawdata2+1); + pData->aGlobalPLTEentries[iX].iBlue = *(pRawdata2+2); + + pRawdata2 += 3; + } + + { /* create an animation object */ + mng_retcode iRetcode = mng_create_ani_plte (pData, pData->iGlobalPLTEcount, + pData->aGlobalPLTEentries); + if (iRetcode) /* on error bail out */ + return iRetcode; + } + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_pltep)*ppChunk)->bEmpty = (mng_bool)(iRawlen == 0); + ((mng_pltep)*ppChunk)->iEntrycount = iRawlen / 3; + pRawdata2 = pRawdata; + + for (iX = 0; iX < ((mng_pltep)*ppChunk)->iEntrycount; iX++) + { + ((mng_pltep)*ppChunk)->aEntries[iX].iRed = *pRawdata2; + ((mng_pltep)*ppChunk)->aEntries[iX].iGreen = *(pRawdata2+1); + ((mng_pltep)*ppChunk)->aEntries[iX].iBlue = *(pRawdata2+2); + + pRawdata2 += 3; + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_PLTE, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif /* MNG_OPTIMIZE_CHUNKREADER */ + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +READ_CHUNK (mng_read_idat) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_IDAT, MNG_LC_START); +#endif + +#ifdef MNG_INCLUDE_JNG /* sequence checks */ + if ((!pData->bHasIHDR) && (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR)) +#else + if ((!pData->bHasIHDR) && (!pData->bHasBASI) && (!pData->bHasDHDR)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasJHDR) && + (pData->iJHDRalphacompression != MNG_COMPRESSION_DEFLATE)) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (pData->bHasJSEP) + MNG_ERROR (pData, MNG_SEQUENCEERROR); +#endif + /* not allowed for deltatype NO_CHANGE */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && ((pData->iDeltatype == MNG_DELTATYPE_NOCHANGE))) + MNG_ERROR (pData, MNG_CHUNKNOTALLOWED); +#endif + /* can only be empty in BASI-block! */ + if ((iRawlen == 0) && (!pData->bHasBASI)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + /* indexed-color requires PLTE */ + if ((pData->bHasIHDR) && (pData->iColortype == 3) && (!pData->bHasPLTE)) + MNG_ERROR (pData, MNG_PLTEMISSING); + + pData->bHasIDAT = MNG_TRUE; /* got some IDAT now, don't we */ + +#ifdef MNG_SUPPORT_DISPLAY + if (iRawlen) + { /* display processing */ + mng_retcode iRetcode = mng_process_display_idat (pData, iRawlen, pRawdata); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_idatp)*ppChunk)->bEmpty = (mng_bool)(iRawlen == 0); + ((mng_idatp)*ppChunk)->iDatasize = iRawlen; + + if (iRawlen != 0) /* is there any data ? */ + { + MNG_ALLOC (pData, ((mng_idatp)*ppChunk)->pData, iRawlen); + MNG_COPY (((mng_idatp)*ppChunk)->pData, pRawdata, iRawlen); + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_IDAT, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +READ_CHUNK (mng_read_iend) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_IEND, MNG_LC_START); +#endif + + if (iRawlen > 0) /* must not contain data! */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + +#ifdef MNG_INCLUDE_JNG /* sequence checks */ + if ((!pData->bHasIHDR) && (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR)) +#else + if ((!pData->bHasIHDR) && (!pData->bHasBASI) && (!pData->bHasDHDR)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + /* IHDR-block requires IDAT */ + if ((pData->bHasIHDR) && (!pData->bHasIDAT)) + MNG_ERROR (pData, MNG_IDATMISSING); + + pData->iImagelevel--; /* one level up */ + +#ifdef MNG_SUPPORT_DISPLAY + { /* create an animation object */ + mng_retcode iRetcode = mng_create_ani_image (pData); + if (iRetcode) /* on error bail out */ + return iRetcode; + /* display processing */ + iRetcode = mng_process_display_iend (pData); + if (iRetcode) /* on error bail out */ + return iRetcode; + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_SUPPORT_DISPLAY + if (!pData->bTimerset) /* reset only if not broken !!! */ + { +#endif + /* IEND signals the end for most ... */ + pData->bHasIHDR = MNG_FALSE; + pData->bHasBASI = MNG_FALSE; + pData->bHasDHDR = MNG_FALSE; +#ifdef MNG_INCLUDE_JNG + pData->bHasJHDR = MNG_FALSE; + pData->bHasJSEP = MNG_FALSE; + pData->bHasJDAA = MNG_FALSE; + pData->bHasJDAT = MNG_FALSE; +#endif + pData->bHasPLTE = MNG_FALSE; + pData->bHasTRNS = MNG_FALSE; + pData->bHasGAMA = MNG_FALSE; + pData->bHasCHRM = MNG_FALSE; + pData->bHasSRGB = MNG_FALSE; + pData->bHasICCP = MNG_FALSE; + pData->bHasBKGD = MNG_FALSE; + pData->bHasIDAT = MNG_FALSE; +#ifdef MNG_SUPPORT_DISPLAY + } +#endif + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_IEND, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +READ_CHUNK (mng_read_trns) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_TRNS, MNG_LC_START); +#endif + /* sequence checks */ + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && + (!pData->bHasBASI) && (!pData->bHasDHDR) ) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIDAT) || (pData->bHasJHDR)) +#else + if (pData->bHasIDAT) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + /* multiple tRNS only inside BASI */ + if ((pData->bHasTRNS) && (!pData->bHasBASI)) + MNG_ERROR (pData, MNG_MULTIPLEERROR); + + if (iRawlen > 256) /* it just can't be bigger than that! */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) + { /* not allowed with full alpha-channel */ + if ((pData->iColortype == 4) || (pData->iColortype == 6)) + MNG_ERROR (pData, MNG_CHUNKNOTALLOWED); + + if (iRawlen != 0) /* filled ? */ + { /* length checks */ + if ((pData->iColortype == 0) && (iRawlen != 2)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + if ((pData->iColortype == 2) && (iRawlen != 6)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + +#ifdef MNG_SUPPORT_DISPLAY + if (pData->iColortype == 3) + { + mng_imagep pImage = (mng_imagep)pData->pCurrentobj; + mng_imagedatap pBuf; + + if (!pImage) /* no object then check obj 0 */ + pImage = (mng_imagep)pData->pObjzero; + + pBuf = pImage->pImgbuf; /* address object buffer */ + + if (iRawlen > pBuf->iPLTEcount) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + } +#endif + } + else /* if empty there must be global stuff! */ + { + if (!pData->bHasglobalTRNS) + MNG_ERROR (pData, MNG_CANNOTBEEMPTY); + } + } + + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) + pData->bHasTRNS = MNG_TRUE; /* indicate tRNS available */ + else + pData->bHasglobalTRNS = MNG_TRUE; + +#ifdef MNG_SUPPORT_DISPLAY + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) + { + mng_imagep pImage; + mng_imagedatap pBuf; + mng_uint8p pRawdata2; + mng_uint32 iRawlen2; + +#ifndef MNG_NO_DELTA_PNG + if (pData->bHasDHDR) /* processing delta-image ? */ + { /* store in object 0 !!! */ + pImage = (mng_imagep)pData->pObjzero; + pBuf = pImage->pImgbuf; /* address object buffer */ + + switch (pData->iColortype) /* store fields for future reference */ + { + case 0: { /* gray */ +#if defined(MNG_NO_1_2_4BIT_SUPPORT) + mng_uint8 multiplier[]={0,255,85,0,17,0,0,0,1, + 0,0,0,0,0,0,0,1}; +#endif + pBuf->iTRNSgray = mng_get_uint16 (pRawdata); + pBuf->iTRNSred = 0; + pBuf->iTRNSgreen = 0; + pBuf->iTRNSblue = 0; + pBuf->iTRNScount = 0; +#if defined(MNG_NO_1_2_4BIT_SUPPORT) + pBuf->iTRNSgray *= multiplier[pData->iPNGdepth]; +#endif +#if defined(MNG_NO_16BIT_SUPPORT) + if (pData->iPNGmult == 2) + pBuf->iTRNSgray >>= 8; +#endif + break; + } + case 2: { /* rgb */ + pBuf->iTRNSgray = 0; + pBuf->iTRNSred = mng_get_uint16 (pRawdata); + pBuf->iTRNSgreen = mng_get_uint16 (pRawdata+2); + pBuf->iTRNSblue = mng_get_uint16 (pRawdata+4); + pBuf->iTRNScount = 0; +#if defined(MNG_NO_16BIT_SUPPORT) + if (pData->iPNGmult == 2) + { + pBuf->iTRNSred >>= 8; + pBuf->iTRNSgreen >>= 8; + pBuf->iTRNSblue >>= 8; + } +#endif + break; + } + case 3: { /* indexed */ + pBuf->iTRNSgray = 0; + pBuf->iTRNSred = 0; + pBuf->iTRNSgreen = 0; + pBuf->iTRNSblue = 0; + pBuf->iTRNScount = iRawlen; + MNG_COPY (pBuf->aTRNSentries, pRawdata, iRawlen); + break; + } + } + + pBuf->bHasTRNS = MNG_TRUE; /* tell it it's got a tRNS now */ + } + else +#endif + { /* address current object */ + pImage = (mng_imagep)pData->pCurrentobj; + + if (!pImage) /* no object then dump it in obj 0 */ + pImage = (mng_imagep)pData->pObjzero; + + pBuf = pImage->pImgbuf; /* address object buffer */ + pBuf->bHasTRNS = MNG_TRUE; /* and tell it it's got a tRNS now */ + + if (iRawlen == 0) /* if empty, inherit from global */ + { + iRawlen2 = pData->iGlobalTRNSrawlen; + pRawdata2 = (mng_ptr)(pData->aGlobalTRNSrawdata); + /* global length oke ? */ + if ((pData->iColortype == 0) && (iRawlen2 != 2)) + MNG_ERROR (pData, MNG_GLOBALLENGTHERR); + + if ((pData->iColortype == 2) && (iRawlen2 != 6)) + MNG_ERROR (pData, MNG_GLOBALLENGTHERR); + + if ((pData->iColortype == 3) && ((iRawlen2 == 0) || (iRawlen2 > pBuf->iPLTEcount))) + MNG_ERROR (pData, MNG_GLOBALLENGTHERR); + } + else + { + iRawlen2 = iRawlen; + pRawdata2 = pRawdata; + } + + switch (pData->iColortype) /* store fields for future reference */ + { + case 0: { /* gray */ + pBuf->iTRNSgray = mng_get_uint16 (pRawdata2); + pBuf->iTRNSred = 0; + pBuf->iTRNSgreen = 0; + pBuf->iTRNSblue = 0; + pBuf->iTRNScount = 0; +#if defined(MNG_NO_16BIT_SUPPORT) + if (pData->iPNGmult == 2) + pBuf->iTRNSgray >>= 8; +#endif + break; + } + case 2: { /* rgb */ + pBuf->iTRNSgray = 0; + pBuf->iTRNSred = mng_get_uint16 (pRawdata2); + pBuf->iTRNSgreen = mng_get_uint16 (pRawdata2+2); + pBuf->iTRNSblue = mng_get_uint16 (pRawdata2+4); + pBuf->iTRNScount = 0; +#if defined(MNG_NO_16BIT_SUPPORT) + if (pData->iPNGmult == 2) + { + pBuf->iTRNSred >>= 8; + pBuf->iTRNSgreen >>= 8; + pBuf->iTRNSblue >>= 8; + } +#endif + break; + } + case 3: { /* indexed */ + pBuf->iTRNSgray = 0; + pBuf->iTRNSred = 0; + pBuf->iTRNSgreen = 0; + pBuf->iTRNSblue = 0; + pBuf->iTRNScount = iRawlen2; + MNG_COPY (pBuf->aTRNSentries, pRawdata2, iRawlen2); + break; + } + } + } + } + else /* store as global */ + { + pData->iGlobalTRNSrawlen = iRawlen; + MNG_COPY (pData->aGlobalTRNSrawdata, pRawdata, iRawlen); + + { /* create an animation object */ + mng_retcode iRetcode = mng_create_ani_trns (pData, pData->iGlobalTRNSrawlen, + pData->aGlobalTRNSrawdata); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) + { /* not global! */ + ((mng_trnsp)*ppChunk)->bGlobal = MNG_FALSE; + ((mng_trnsp)*ppChunk)->iType = pData->iColortype; + + if (iRawlen == 0) /* if empty, indicate so */ + ((mng_trnsp)*ppChunk)->bEmpty = MNG_TRUE; + else + { + ((mng_trnsp)*ppChunk)->bEmpty = MNG_FALSE; + + switch (pData->iColortype) /* store fields */ + { + case 0: { /* gray */ + ((mng_trnsp)*ppChunk)->iGray = mng_get_uint16 (pRawdata); + break; + } + case 2: { /* rgb */ + ((mng_trnsp)*ppChunk)->iRed = mng_get_uint16 (pRawdata); + ((mng_trnsp)*ppChunk)->iGreen = mng_get_uint16 (pRawdata+2); + ((mng_trnsp)*ppChunk)->iBlue = mng_get_uint16 (pRawdata+4); + break; + } + case 3: { /* indexed */ + ((mng_trnsp)*ppChunk)->iCount = iRawlen; + MNG_COPY (((mng_trnsp)*ppChunk)->aEntries, pRawdata, iRawlen); + break; + } + } + } + } + else /* it's global! */ + { + ((mng_trnsp)*ppChunk)->bEmpty = (mng_bool)(iRawlen == 0); + ((mng_trnsp)*ppChunk)->bGlobal = MNG_TRUE; + ((mng_trnsp)*ppChunk)->iType = 0; + ((mng_trnsp)*ppChunk)->iRawlen = iRawlen; + + MNG_COPY (((mng_trnsp)*ppChunk)->aRawdata, pRawdata, iRawlen); + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_TRNS, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +READ_CHUNK (mng_read_gama) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_GAMA, MNG_LC_START); +#endif + /* sequence checks */ +#ifdef MNG_INCLUDE_JNG + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && + (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR)) +#else + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && + (!pData->bHasBASI) && (!pData->bHasDHDR) ) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIDAT) || (pData->bHasPLTE) || (pData->bHasJDAT) || (pData->bHasJDAA)) +#else + if ((pData->bHasIDAT) || (pData->bHasPLTE)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + { /* length must be exactly 4 */ + if (iRawlen != 4) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + } + else + { /* length must be empty or exactly 4 */ + if ((iRawlen != 0) && (iRawlen != 4)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + } + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + pData->bHasGAMA = MNG_TRUE; /* indicate we've got it */ + else + pData->bHasglobalGAMA = (mng_bool)(iRawlen != 0); + +#ifdef MNG_SUPPORT_DISPLAY +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + { + mng_imagep pImage; + +#ifndef MNG_NO_DELTA_PNG + if (pData->bHasDHDR) /* update delta image ? */ + { /* store in object 0 ! */ + pImage = (mng_imagep)pData->pObjzero; + /* store for color-processing routines */ + pImage->pImgbuf->iGamma = mng_get_uint32 (pRawdata); + pImage->pImgbuf->bHasGAMA = MNG_TRUE; + } + else +#endif + { + pImage = (mng_imagep)pData->pCurrentobj; + + if (!pImage) /* no object then dump it in obj 0 */ + pImage = (mng_imagep)pData->pObjzero; + /* store for color-processing routines */ + pImage->pImgbuf->iGamma = mng_get_uint32 (pRawdata); + pImage->pImgbuf->bHasGAMA = MNG_TRUE; + } + } + else + { /* store as global */ + if (iRawlen != 0) + pData->iGlobalGamma = mng_get_uint32 (pRawdata); + + { /* create an animation object */ + mng_retcode iRetcode = mng_create_ani_gama (pData, (mng_bool)(iRawlen == 0), + pData->iGlobalGamma); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_gamap)*ppChunk)->bEmpty = (mng_bool)(iRawlen == 0); + + if (iRawlen) + ((mng_gamap)*ppChunk)->iGamma = mng_get_uint32 (pRawdata); + + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_GAMA, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_cHRM +READ_CHUNK (mng_read_chrm) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_CHRM, MNG_LC_START); +#endif + /* sequence checks */ +#ifdef MNG_INCLUDE_JNG + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && + (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR)) +#else + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && + (!pData->bHasBASI) && (!pData->bHasDHDR) ) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIDAT) || (pData->bHasPLTE) || (pData->bHasJDAT) || (pData->bHasJDAA)) +#else + if ((pData->bHasIDAT) || (pData->bHasPLTE)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + { /* length must be exactly 32 */ + if (iRawlen != 32) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + } + else + { /* length must be empty or exactly 32 */ + if ((iRawlen != 0) && (iRawlen != 32)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + } + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + pData->bHasCHRM = MNG_TRUE; /* indicate we've got it */ + else + pData->bHasglobalCHRM = (mng_bool)(iRawlen != 0); + +#ifdef MNG_SUPPORT_DISPLAY + { + mng_uint32 iWhitepointx, iWhitepointy; + mng_uint32 iPrimaryredx, iPrimaryredy; + mng_uint32 iPrimarygreenx, iPrimarygreeny; + mng_uint32 iPrimarybluex, iPrimarybluey; + + iWhitepointx = mng_get_uint32 (pRawdata); + iWhitepointy = mng_get_uint32 (pRawdata+4); + iPrimaryredx = mng_get_uint32 (pRawdata+8); + iPrimaryredy = mng_get_uint32 (pRawdata+12); + iPrimarygreenx = mng_get_uint32 (pRawdata+16); + iPrimarygreeny = mng_get_uint32 (pRawdata+20); + iPrimarybluex = mng_get_uint32 (pRawdata+24); + iPrimarybluey = mng_get_uint32 (pRawdata+28); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + { + mng_imagep pImage; + mng_imagedatap pBuf; + +#ifndef MNG_NO_DELTA_PNG + if (pData->bHasDHDR) /* update delta image ? */ + { /* store it in object 0 ! */ + pImage = (mng_imagep)pData->pObjzero; + + pBuf = pImage->pImgbuf; /* address object buffer */ + pBuf->bHasCHRM = MNG_TRUE; /* and tell it it's got a CHRM now */ + /* store for color-processing routines */ + pBuf->iWhitepointx = iWhitepointx; + pBuf->iWhitepointy = iWhitepointy; + pBuf->iPrimaryredx = iPrimaryredx; + pBuf->iPrimaryredy = iPrimaryredy; + pBuf->iPrimarygreenx = iPrimarygreenx; + pBuf->iPrimarygreeny = iPrimarygreeny; + pBuf->iPrimarybluex = iPrimarybluex; + pBuf->iPrimarybluey = iPrimarybluey; + } + else +#endif + { + pImage = (mng_imagep)pData->pCurrentobj; + + if (!pImage) /* no object then dump it in obj 0 */ + pImage = (mng_imagep)pData->pObjzero; + + pBuf = pImage->pImgbuf; /* address object buffer */ + pBuf->bHasCHRM = MNG_TRUE; /* and tell it it's got a CHRM now */ + /* store for color-processing routines */ + pBuf->iWhitepointx = iWhitepointx; + pBuf->iWhitepointy = iWhitepointy; + pBuf->iPrimaryredx = iPrimaryredx; + pBuf->iPrimaryredy = iPrimaryredy; + pBuf->iPrimarygreenx = iPrimarygreenx; + pBuf->iPrimarygreeny = iPrimarygreeny; + pBuf->iPrimarybluex = iPrimarybluex; + pBuf->iPrimarybluey = iPrimarybluey; + } + } + else + { /* store as global */ + if (iRawlen != 0) + { + pData->iGlobalWhitepointx = iWhitepointx; + pData->iGlobalWhitepointy = iWhitepointy; + pData->iGlobalPrimaryredx = iPrimaryredx; + pData->iGlobalPrimaryredy = iPrimaryredy; + pData->iGlobalPrimarygreenx = iPrimarygreenx; + pData->iGlobalPrimarygreeny = iPrimarygreeny; + pData->iGlobalPrimarybluex = iPrimarybluex; + pData->iGlobalPrimarybluey = iPrimarybluey; + } + + { /* create an animation object */ + mng_retcode iRetcode = mng_create_ani_chrm (pData, (mng_bool)(iRawlen == 0), + iWhitepointx, iWhitepointy, + iPrimaryredx, iPrimaryredy, + iPrimarygreenx, iPrimarygreeny, + iPrimarybluex, iPrimarybluey); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + } + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_chrmp)*ppChunk)->bEmpty = (mng_bool)(iRawlen == 0); + + if (iRawlen) + { + ((mng_chrmp)*ppChunk)->iWhitepointx = mng_get_uint32 (pRawdata); + ((mng_chrmp)*ppChunk)->iWhitepointy = mng_get_uint32 (pRawdata+4); + ((mng_chrmp)*ppChunk)->iRedx = mng_get_uint32 (pRawdata+8); + ((mng_chrmp)*ppChunk)->iRedy = mng_get_uint32 (pRawdata+12); + ((mng_chrmp)*ppChunk)->iGreenx = mng_get_uint32 (pRawdata+16); + ((mng_chrmp)*ppChunk)->iGreeny = mng_get_uint32 (pRawdata+20); + ((mng_chrmp)*ppChunk)->iBluex = mng_get_uint32 (pRawdata+24); + ((mng_chrmp)*ppChunk)->iBluey = mng_get_uint32 (pRawdata+28); + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_CHRM, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +READ_CHUNK (mng_read_srgb) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_SRGB, MNG_LC_START); +#endif + /* sequence checks */ +#ifdef MNG_INCLUDE_JNG + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && + (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR)) +#else + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && + (!pData->bHasBASI) && (!pData->bHasDHDR) ) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIDAT) || (pData->bHasPLTE) || (pData->bHasJDAT) || (pData->bHasJDAA)) +#else + if ((pData->bHasIDAT) || (pData->bHasPLTE)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + { /* length must be exactly 1 */ + if (iRawlen != 1) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + } + else + { /* length must be empty or exactly 1 */ + if ((iRawlen != 0) && (iRawlen != 1)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + } + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + pData->bHasSRGB = MNG_TRUE; /* indicate we've got it */ + else + pData->bHasglobalSRGB = (mng_bool)(iRawlen != 0); + +#ifdef MNG_SUPPORT_DISPLAY +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + { + mng_imagep pImage; + +#ifndef MNG_NO_DELTA_PNG + if (pData->bHasDHDR) /* update delta image ? */ + { /* store in object 0 ! */ + pImage = (mng_imagep)pData->pObjzero; + /* store for color-processing routines */ + pImage->pImgbuf->iRenderingintent = *pRawdata; + pImage->pImgbuf->bHasSRGB = MNG_TRUE; + } + else +#endif + { + pImage = (mng_imagep)pData->pCurrentobj; + + if (!pImage) /* no object then dump it in obj 0 */ + pImage = (mng_imagep)pData->pObjzero; + /* store for color-processing routines */ + pImage->pImgbuf->iRenderingintent = *pRawdata; + pImage->pImgbuf->bHasSRGB = MNG_TRUE; + } + } + else + { /* store as global */ + if (iRawlen != 0) + pData->iGlobalRendintent = *pRawdata; + + { /* create an animation object */ + mng_retcode iRetcode = mng_create_ani_srgb (pData, (mng_bool)(iRawlen == 0), + pData->iGlobalRendintent); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_srgbp)*ppChunk)->bEmpty = (mng_bool)(iRawlen == 0); + + if (iRawlen) + ((mng_srgbp)*ppChunk)->iRenderingintent = *pRawdata; + + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_SRGB, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_iCCP +READ_CHUNK (mng_read_iccp) +{ + mng_retcode iRetcode; + mng_uint8p pTemp; + mng_uint32 iCompressedsize; + mng_uint32 iProfilesize; + mng_uint32 iBufsize = 0; + mng_uint8p pBuf = 0; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_ICCP, MNG_LC_START); +#endif + /* sequence checks */ +#ifdef MNG_INCLUDE_JNG + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && + (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR)) +#else + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && + (!pData->bHasBASI) && (!pData->bHasDHDR) ) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIDAT) || (pData->bHasPLTE) || (pData->bHasJDAT) || (pData->bHasJDAA)) +#else + if ((pData->bHasIDAT) || (pData->bHasPLTE)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + { /* length must be at least 2 */ + if (iRawlen < 2) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + } + else + { /* length must be empty or at least 2 */ + if ((iRawlen != 0) && (iRawlen < 2)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + } + + pTemp = find_null (pRawdata); /* find null-separator */ + /* not found inside input-data ? */ + if ((pTemp - pRawdata) > (mng_int32)iRawlen) + MNG_ERROR (pData, MNG_NULLNOTFOUND); + /* determine size of compressed profile */ + iCompressedsize = (mng_uint32)(iRawlen - (pTemp - pRawdata) - 2); + /* decompress the profile */ + iRetcode = mng_inflate_buffer (pData, pTemp+2, iCompressedsize, + &pBuf, &iBufsize, &iProfilesize); + +#ifdef MNG_CHECK_BAD_ICCP /* Check for bad iCCP chunk */ + if ((iRetcode) && (!strncmp ((char *)pRawdata, "Photoshop ICC profile", 21))) + { + if (iRawlen == 2615) /* is it the sRGB profile ? */ + { + mng_chunk_header chunk_srgb = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_sRGB, mng_init_general, mng_free_general, mng_read_srgb, mng_write_srgb, mng_assign_general, 0, 0, sizeof(mng_srgb)}; +#else + {MNG_UINT_sRGB, mng_init_srgb, mng_free_srgb, mng_read_srgb, mng_write_srgb, mng_assign_srgb, 0, 0}; +#endif + /* pretend it's an sRGB chunk then ! */ + iRetcode = mng_read_srgb (pData, &chunk_srgb, 1, (mng_ptr)"0", ppChunk); + + if (iRetcode) /* on error bail out */ + { /* don't forget to drop the temp buffer */ + MNG_FREEX (pData, pBuf, iBufsize); + return iRetcode; + } + } + } + else + { +#endif /* MNG_CHECK_BAD_ICCP */ + + if (iRetcode) /* on error bail out */ + { /* don't forget to drop the temp buffer */ + MNG_FREEX (pData, pBuf, iBufsize); + return iRetcode; + } + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + pData->bHasICCP = MNG_TRUE; /* indicate we've got it */ + else + pData->bHasglobalICCP = (mng_bool)(iRawlen != 0); + +#ifdef MNG_SUPPORT_DISPLAY +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + { + mng_imagep pImage; + +#ifndef MNG_NO_DELTA_PNG + if (pData->bHasDHDR) /* update delta image ? */ + { /* store in object 0 ! */ + pImage = (mng_imagep)pData->pObjzero; + + if (pImage->pImgbuf->pProfile) /* profile existed ? */ + MNG_FREEX (pData, pImage->pImgbuf->pProfile, pImage->pImgbuf->iProfilesize); + /* allocate a buffer & copy it */ + MNG_ALLOC (pData, pImage->pImgbuf->pProfile, iProfilesize); + MNG_COPY (pImage->pImgbuf->pProfile, pBuf, iProfilesize); + /* store its length as well */ + pImage->pImgbuf->iProfilesize = iProfilesize; + pImage->pImgbuf->bHasICCP = MNG_TRUE; + } + else +#endif + { + pImage = (mng_imagep)pData->pCurrentobj; + + if (!pImage) /* no object then dump it in obj 0 */ + pImage = (mng_imagep)pData->pObjzero; + + if (pImage->pImgbuf->pProfile) /* profile existed ? */ + MNG_FREEX (pData, pImage->pImgbuf->pProfile, pImage->pImgbuf->iProfilesize); + /* allocate a buffer & copy it */ + MNG_ALLOC (pData, pImage->pImgbuf->pProfile, iProfilesize); + MNG_COPY (pImage->pImgbuf->pProfile, pBuf, iProfilesize); + /* store its length as well */ + pImage->pImgbuf->iProfilesize = iProfilesize; + pImage->pImgbuf->bHasICCP = MNG_TRUE; + } + } + else + { /* store as global */ + if (iRawlen == 0) /* empty chunk ? */ + { + if (pData->pGlobalProfile) /* did we have a global profile ? */ + MNG_FREEX (pData, pData->pGlobalProfile, pData->iGlobalProfilesize); + + pData->iGlobalProfilesize = 0; /* reset to null */ + pData->pGlobalProfile = MNG_NULL; + } + else + { /* allocate a global buffer & copy it */ + MNG_ALLOC (pData, pData->pGlobalProfile, iProfilesize); + MNG_COPY (pData->pGlobalProfile, pBuf, iProfilesize); + /* store its length as well */ + pData->iGlobalProfilesize = iProfilesize; + } + + /* create an animation object */ + iRetcode = mng_create_ani_iccp (pData, (mng_bool)(iRawlen == 0), + pData->iGlobalProfilesize, + pData->pGlobalProfile); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + { /* don't forget to drop the temp buffer */ + MNG_FREEX (pData, pBuf, iBufsize); + return iRetcode; + } + /* store the fields */ + ((mng_iccpp)*ppChunk)->bEmpty = (mng_bool)(iRawlen == 0); + + if (iRawlen) /* not empty ? */ + { + if (!pBuf) /* hasn't been unpuzzled it yet ? */ + { /* find null-separator */ + pTemp = find_null (pRawdata); + /* not found inside input-data ? */ + if ((pTemp - pRawdata) > (mng_int32)iRawlen) + MNG_ERROR (pData, MNG_NULLNOTFOUND); + /* determine size of compressed profile */ + iCompressedsize = iRawlen - (pTemp - pRawdata) - 2; + /* decompress the profile */ + iRetcode = mng_inflate_buffer (pData, pTemp+2, iCompressedsize, + &pBuf, &iBufsize, &iProfilesize); + + if (iRetcode) /* on error bail out */ + { /* don't forget to drop the temp buffer */ + MNG_FREEX (pData, pBuf, iBufsize); + return iRetcode; + } + } + + ((mng_iccpp)*ppChunk)->iNamesize = (mng_uint32)(pTemp - pRawdata); + + if (((mng_iccpp)*ppChunk)->iNamesize) + { + MNG_ALLOC (pData, ((mng_iccpp)*ppChunk)->zName, + ((mng_iccpp)*ppChunk)->iNamesize + 1); + MNG_COPY (((mng_iccpp)*ppChunk)->zName, pRawdata, + ((mng_iccpp)*ppChunk)->iNamesize); + } + + ((mng_iccpp)*ppChunk)->iCompression = *(pTemp+1); + ((mng_iccpp)*ppChunk)->iProfilesize = iProfilesize; + + MNG_ALLOC (pData, ((mng_iccpp)*ppChunk)->pProfile, iProfilesize); + MNG_COPY (((mng_iccpp)*ppChunk)->pProfile, pBuf, iProfilesize); + } + } +#endif /* MNG_STORE_CHUNKS */ + + if (pBuf) /* free the temporary buffer */ + MNG_FREEX (pData, pBuf, iBufsize); + +#ifdef MNG_CHECK_BAD_ICCP + } +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_ICCP, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_tEXt +READ_CHUNK (mng_read_text) +{ + mng_uint32 iKeywordlen, iTextlen; + mng_pchar zKeyword, zText; + mng_uint8p pTemp; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_TEXT, MNG_LC_START); +#endif + /* sequence checks */ +#ifdef MNG_INCLUDE_JNG + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && + (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR)) +#else + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && + (!pData->bHasBASI) && (!pData->bHasDHDR) ) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (iRawlen < 2) /* length must be at least 2 */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + pTemp = find_null (pRawdata); /* find the null separator */ + /* not found inside input-data ? */ + if ((pTemp - pRawdata) > (mng_int32)iRawlen) + MNG_ERROR (pData, MNG_NULLNOTFOUND); + + if (pTemp == pRawdata) /* there must be at least 1 char for keyword */ + MNG_ERROR (pData, MNG_KEYWORDNULL); + + iKeywordlen = (mng_uint32)(pTemp - pRawdata); + iTextlen = iRawlen - iKeywordlen - 1; + + if (pData->fProcesstext) /* inform the application ? */ + { + mng_bool bOke; + + MNG_ALLOC (pData, zKeyword, iKeywordlen + 1); + MNG_COPY (zKeyword, pRawdata, iKeywordlen); + + MNG_ALLOCX (pData, zText, iTextlen + 1); + + if (!zText) /* on error bail out */ + { + MNG_FREEX (pData, zKeyword, iKeywordlen + 1); + MNG_ERROR (pData, MNG_OUTOFMEMORY); + } + + if (iTextlen) + MNG_COPY (zText, pTemp+1, iTextlen); + + bOke = pData->fProcesstext ((mng_handle)pData, MNG_TYPE_TEXT, zKeyword, zText, 0, 0); + + MNG_FREEX (pData, zText, iTextlen + 1); + MNG_FREEX (pData, zKeyword, iKeywordlen + 1); + + if (!bOke) + MNG_ERROR (pData, MNG_APPMISCERROR); + + } + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_textp)*ppChunk)->iKeywordsize = iKeywordlen; + ((mng_textp)*ppChunk)->iTextsize = iTextlen; + + if (iKeywordlen) + { + MNG_ALLOC (pData, ((mng_textp)*ppChunk)->zKeyword, iKeywordlen+1); + MNG_COPY (((mng_textp)*ppChunk)->zKeyword, pRawdata, iKeywordlen); + } + + if (iTextlen) + { + MNG_ALLOC (pData, ((mng_textp)*ppChunk)->zText, iTextlen+1); + MNG_COPY (((mng_textp)*ppChunk)->zText, pTemp+1, iTextlen); + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_TEXT, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_zTXt +READ_CHUNK (mng_read_ztxt) +{ + mng_retcode iRetcode; + mng_uint32 iKeywordlen, iTextlen; + mng_pchar zKeyword; + mng_uint8p pTemp; + mng_uint32 iCompressedsize; + mng_uint32 iBufsize; + mng_uint8p pBuf; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_ZTXT, MNG_LC_START); +#endif + /* sequence checks */ +#ifdef MNG_INCLUDE_JNG + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && + (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR)) +#else + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && + (!pData->bHasBASI) && (!pData->bHasDHDR) ) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (iRawlen < 3) /* length must be at least 3 */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + pTemp = find_null (pRawdata); /* find the null separator */ + /* not found inside input-data ? */ + if ((pTemp - pRawdata) > (mng_int32)iRawlen) + MNG_ERROR (pData, MNG_NULLNOTFOUND); + + if (pTemp == pRawdata) /* there must be at least 1 char for keyword */ + MNG_ERROR (pData, MNG_KEYWORDNULL); + + if (*(pTemp+1) != 0) /* only deflate compression-method allowed */ + MNG_ERROR (pData, MNG_INVALIDCOMPRESS); + + iKeywordlen = (mng_uint32)(pTemp - pRawdata); + iCompressedsize = (mng_uint32)(iRawlen - iKeywordlen - 2); + + zKeyword = 0; /* there's no keyword buffer yet */ + pBuf = 0; /* or a temporary buffer ! */ + + if (pData->fProcesstext) /* inform the application ? */ + { /* decompress the text */ + iRetcode = mng_inflate_buffer (pData, pTemp+2, iCompressedsize, + &pBuf, &iBufsize, &iTextlen); + + if (iRetcode) /* on error bail out */ + { /* don't forget to drop the temp buffers */ + MNG_FREEX (pData, pBuf, iBufsize); + return iRetcode; + } + + MNG_ALLOCX (pData, zKeyword, iKeywordlen+1); + + if (!zKeyword) /* on error bail out */ + { /* don't forget to drop the temp buffers */ + MNG_FREEX (pData, pBuf, iBufsize); + MNG_ERROR (pData, MNG_OUTOFMEMORY); + } + + MNG_COPY (zKeyword, pRawdata, iKeywordlen); + + if (!pData->fProcesstext ((mng_handle)pData, MNG_TYPE_ZTXT, zKeyword, (mng_pchar)pBuf, 0, 0)) + { /* don't forget to drop the temp buffers */ + MNG_FREEX (pData, pBuf, iBufsize); + MNG_FREEX (pData, zKeyword, iKeywordlen+1); + MNG_ERROR (pData, MNG_APPMISCERROR); + } + } + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + { /* don't forget to drop the temp buffers */ + MNG_FREEX (pData, pBuf, iBufsize); + MNG_FREEX (pData, zKeyword, iKeywordlen+1); + return iRetcode; + } + /* store the fields */ + ((mng_ztxtp)*ppChunk)->iKeywordsize = iKeywordlen; + ((mng_ztxtp)*ppChunk)->iCompression = *(pTemp+1); + + if ((!pBuf) && (iCompressedsize)) /* did we not get a text-buffer yet ? */ + { /* decompress the text */ + iRetcode = mng_inflate_buffer (pData, pTemp+2, iCompressedsize, + &pBuf, &iBufsize, &iTextlen); + + if (iRetcode) /* on error bail out */ + { /* don't forget to drop the temp buffers */ + MNG_FREEX (pData, pBuf, iBufsize); + MNG_FREEX (pData, zKeyword, iKeywordlen+1); + return iRetcode; + } + } + + MNG_ALLOCX (pData, ((mng_ztxtp)*ppChunk)->zKeyword, iKeywordlen + 1); + /* on error bail out */ + if (!((mng_ztxtp)*ppChunk)->zKeyword) + { /* don't forget to drop the temp buffers */ + MNG_FREEX (pData, pBuf, iBufsize); + MNG_FREEX (pData, zKeyword, iKeywordlen+1); + MNG_ERROR (pData, MNG_OUTOFMEMORY); + } + + MNG_COPY (((mng_ztxtp)*ppChunk)->zKeyword, pRawdata, iKeywordlen); + + ((mng_ztxtp)*ppChunk)->iTextsize = iTextlen; + + if (iCompressedsize) + { + MNG_ALLOCX (pData, ((mng_ztxtp)*ppChunk)->zText, iTextlen + 1); + /* on error bail out */ + if (!((mng_ztxtp)*ppChunk)->zText) + { /* don't forget to drop the temp buffers */ + MNG_FREEX (pData, pBuf, iBufsize); + MNG_FREEX (pData, zKeyword, iKeywordlen+1); + MNG_ERROR (pData, MNG_OUTOFMEMORY); + } + + MNG_COPY (((mng_ztxtp)*ppChunk)->zText, pBuf, iTextlen); + } + } +#endif /* MNG_STORE_CHUNKS */ + + MNG_FREEX (pData, pBuf, iBufsize); /* free the temporary buffers */ + MNG_FREEX (pData, zKeyword, iKeywordlen+1); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_ZTXT, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_iTXt +READ_CHUNK (mng_read_itxt) +{ + mng_retcode iRetcode; + mng_uint32 iKeywordlen, iTextlen, iLanguagelen, iTranslationlen; + mng_pchar zKeyword, zLanguage, zTranslation; + mng_uint8p pNull1, pNull2, pNull3; + mng_uint32 iCompressedsize; + mng_uint8 iCompressionflag; + mng_uint32 iBufsize; + mng_uint8p pBuf; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_ITXT, MNG_LC_START); +#endif + /* sequence checks */ +#ifdef MNG_INCLUDE_JNG + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && + (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR)) +#else + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && + (!pData->bHasBASI) && (!pData->bHasDHDR) ) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (iRawlen < 6) /* length must be at least 6 */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + pNull1 = find_null (pRawdata); /* find the null separators */ + pNull2 = find_null (pNull1+3); + pNull3 = find_null (pNull2+1); + /* not found inside input-data ? */ + if (((pNull1 - pRawdata) > (mng_int32)iRawlen) || + ((pNull2 - pRawdata) > (mng_int32)iRawlen) || + ((pNull3 - pRawdata) > (mng_int32)iRawlen) ) + MNG_ERROR (pData, MNG_NULLNOTFOUND); + + if (pNull1 == pRawdata) /* there must be at least 1 char for keyword */ + MNG_ERROR (pData, MNG_KEYWORDNULL); + /* compression or not ? */ + if ((*(pNull1+1) != 0) && (*(pNull1+1) != 1)) + MNG_ERROR (pData, MNG_INVALIDCOMPRESS); + + if (*(pNull1+2) != 0) /* only deflate compression-method allowed */ + MNG_ERROR (pData, MNG_INVALIDCOMPRESS); + + iKeywordlen = (mng_uint32)(pNull1 - pRawdata); + iLanguagelen = (mng_uint32)(pNull2 - pNull1 - 3); + iTranslationlen = (mng_uint32)(pNull3 - pNull2 - 1); + iCompressedsize = (mng_uint32)(iRawlen - iKeywordlen - iLanguagelen - iTranslationlen - 5); + iCompressionflag = *(pNull1+1); + + zKeyword = 0; /* no buffers acquired yet */ + zLanguage = 0; + zTranslation = 0; + pBuf = 0; + iTextlen = 0; + + if (pData->fProcesstext) /* inform the application ? */ + { + if (iCompressionflag) /* decompress the text ? */ + { + iRetcode = mng_inflate_buffer (pData, pNull3+1, iCompressedsize, + &pBuf, &iBufsize, &iTextlen); + + if (iRetcode) /* on error bail out */ + { /* don't forget to drop the temp buffer */ + MNG_FREEX (pData, pBuf, iBufsize); + return iRetcode; + } + } + else + { + iTextlen = iCompressedsize; + iBufsize = iTextlen+1; /* plus 1 for terminator byte!!! */ + + MNG_ALLOC (pData, pBuf, iBufsize); + MNG_COPY (pBuf, pNull3+1, iTextlen); + } + + MNG_ALLOCX (pData, zKeyword, iKeywordlen + 1); + MNG_ALLOCX (pData, zLanguage, iLanguagelen + 1); + MNG_ALLOCX (pData, zTranslation, iTranslationlen + 1); + /* on error bail out */ + if ((!zKeyword) || (!zLanguage) || (!zTranslation)) + { /* don't forget to drop the temp buffers */ + MNG_FREEX (pData, zTranslation, iTranslationlen + 1); + MNG_FREEX (pData, zLanguage, iLanguagelen + 1); + MNG_FREEX (pData, zKeyword, iKeywordlen + 1); + MNG_FREEX (pData, pBuf, iBufsize); + MNG_ERROR (pData, MNG_OUTOFMEMORY); + } + + MNG_COPY (zKeyword, pRawdata, iKeywordlen); + MNG_COPY (zLanguage, pNull1+3, iLanguagelen); + MNG_COPY (zTranslation, pNull2+1, iTranslationlen); + + if (!pData->fProcesstext ((mng_handle)pData, MNG_TYPE_ITXT, zKeyword, (mng_pchar)pBuf, + zLanguage, zTranslation)) + { /* don't forget to drop the temp buffers */ + MNG_FREEX (pData, zTranslation, iTranslationlen + 1); + MNG_FREEX (pData, zLanguage, iLanguagelen + 1); + MNG_FREEX (pData, zKeyword, iKeywordlen + 1); + MNG_FREEX (pData, pBuf, iBufsize); + + MNG_ERROR (pData, MNG_APPMISCERROR); + } + } + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + { /* don't forget to drop the temp buffers */ + MNG_FREEX (pData, zTranslation, iTranslationlen + 1); + MNG_FREEX (pData, zLanguage, iLanguagelen + 1); + MNG_FREEX (pData, zKeyword, iKeywordlen + 1); + MNG_FREEX (pData, pBuf, iBufsize); + return iRetcode; + } + /* store the fields */ + ((mng_itxtp)*ppChunk)->iKeywordsize = iKeywordlen; + ((mng_itxtp)*ppChunk)->iLanguagesize = iLanguagelen; + ((mng_itxtp)*ppChunk)->iTranslationsize = iTranslationlen; + ((mng_itxtp)*ppChunk)->iCompressionflag = *(pNull1+1); + ((mng_itxtp)*ppChunk)->iCompressionmethod = *(pNull1+2); + + if ((!pBuf) && (iCompressedsize)) /* did we not get a text-buffer yet ? */ + { + if (iCompressionflag) /* decompress the text ? */ + { + iRetcode = mng_inflate_buffer (pData, pNull3+1, iCompressedsize, + &pBuf, &iBufsize, &iTextlen); + + if (iRetcode) /* on error bail out */ + { /* don't forget to drop the temp buffers */ + MNG_FREEX (pData, zTranslation, iTranslationlen + 1); + MNG_FREEX (pData, zLanguage, iLanguagelen + 1); + MNG_FREEX (pData, zKeyword, iKeywordlen + 1); + MNG_FREEX (pData, pBuf, iBufsize); + return iRetcode; + } + } + else + { + iTextlen = iCompressedsize; + iBufsize = iTextlen+1; /* plus 1 for terminator byte!!! */ + + MNG_ALLOC (pData, pBuf, iBufsize); + MNG_COPY (pBuf, pNull3+1, iTextlen); + } + } + + MNG_ALLOCX (pData, ((mng_itxtp)*ppChunk)->zKeyword, iKeywordlen + 1); + MNG_ALLOCX (pData, ((mng_itxtp)*ppChunk)->zLanguage, iLanguagelen + 1); + MNG_ALLOCX (pData, ((mng_itxtp)*ppChunk)->zTranslation, iTranslationlen + 1); + /* on error bail out */ + if ((!((mng_itxtp)*ppChunk)->zKeyword ) || + (!((mng_itxtp)*ppChunk)->zLanguage ) || + (!((mng_itxtp)*ppChunk)->zTranslation) ) + { /* don't forget to drop the temp buffers */ + MNG_FREEX (pData, zTranslation, iTranslationlen + 1); + MNG_FREEX (pData, zLanguage, iLanguagelen + 1); + MNG_FREEX (pData, zKeyword, iKeywordlen + 1); + MNG_FREEX (pData, pBuf, iBufsize); + MNG_ERROR (pData, MNG_OUTOFMEMORY); + } + + MNG_COPY (((mng_itxtp)*ppChunk)->zKeyword, pRawdata, iKeywordlen); + MNG_COPY (((mng_itxtp)*ppChunk)->zLanguage, pNull1+3, iLanguagelen); + MNG_COPY (((mng_itxtp)*ppChunk)->zTranslation, pNull2+1, iTranslationlen); + + ((mng_itxtp)*ppChunk)->iTextsize = iTextlen; + + if (iTextlen) + { + MNG_ALLOCX (pData, ((mng_itxtp)*ppChunk)->zText, iTextlen + 1); + + if (!((mng_itxtp)*ppChunk)->zText) + { /* don't forget to drop the temp buffers */ + MNG_FREEX (pData, zTranslation, iTranslationlen + 1); + MNG_FREEX (pData, zLanguage, iLanguagelen + 1); + MNG_FREEX (pData, zKeyword, iKeywordlen + 1); + MNG_FREEX (pData, pBuf, iBufsize); + MNG_ERROR (pData, MNG_OUTOFMEMORY); + } + + MNG_COPY (((mng_itxtp)*ppChunk)->zText, pBuf, iTextlen); + } + } +#endif /* MNG_STORE_CHUNKS */ + /* free the temporary buffers */ + MNG_FREEX (pData, zTranslation, iTranslationlen + 1); + MNG_FREEX (pData, zLanguage, iLanguagelen + 1); + MNG_FREEX (pData, zKeyword, iKeywordlen + 1); + MNG_FREEX (pData, pBuf, iBufsize); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_ITXT, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_bKGD +READ_CHUNK (mng_read_bkgd) +{ +#ifdef MNG_SUPPORT_DISPLAY + mng_imagep pImage = (mng_imagep)pData->pCurrentobj; + mng_imagedatap pBuf; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_BKGD, MNG_LC_START); +#endif + /* sequence checks */ +#ifdef MNG_INCLUDE_JNG + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && + (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR)) +#else + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && + (!pData->bHasBASI) && (!pData->bHasDHDR) ) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIDAT) || (pData->bHasJDAT) || (pData->bHasJDAA)) +#else + if (pData->bHasIDAT) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (iRawlen > 6) /* it just can't be bigger than that! */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + +#ifdef MNG_INCLUDE_JNG /* length checks */ + if (pData->bHasJHDR) + { + if (((pData->iJHDRcolortype == 8) || (pData->iJHDRcolortype == 12)) && (iRawlen != 2)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + if (((pData->iJHDRcolortype == 10) || (pData->iJHDRcolortype == 14)) && (iRawlen != 6)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + } + else +#endif /* MNG_INCLUDE_JNG */ + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) + { + if (((pData->iColortype == 0) || (pData->iColortype == 4)) && (iRawlen != 2)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + if (((pData->iColortype == 2) || (pData->iColortype == 6)) && (iRawlen != 6)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + if ((pData->iColortype == 3) && (iRawlen != 1)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + } + else + { + if (iRawlen != 6) /* global is always 16-bit RGB ! */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + } + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + pData->bHasBKGD = MNG_TRUE; /* indicate bKGD available */ + else + pData->bHasglobalBKGD = (mng_bool)(iRawlen != 0); + +#ifdef MNG_SUPPORT_DISPLAY + if (!pImage) /* if no object dump it in obj 0 */ + pImage = (mng_imagep)pData->pObjzero; + + pBuf = pImage->pImgbuf; /* address object buffer */ + +#ifdef MNG_INCLUDE_JNG + if (pData->bHasJHDR) + { + pBuf->bHasBKGD = MNG_TRUE; /* tell the object it's got bKGD now */ + + switch (pData->iJHDRcolortype) /* store fields for future reference */ + { + case 8 : ; /* gray */ + case 12 : { /* graya */ + pBuf->iBKGDgray = mng_get_uint16 (pRawdata); + break; + } + case 10 : ; /* rgb */ + case 14 : { /* rgba */ + pBuf->iBKGDred = mng_get_uint16 (pRawdata); + pBuf->iBKGDgreen = mng_get_uint16 (pRawdata+2); + pBuf->iBKGDblue = mng_get_uint16 (pRawdata+4); + break; + } + } + } + else +#endif /* MNG_INCLUDE_JNG */ + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) + { + pBuf->bHasBKGD = MNG_TRUE; /* tell the object it's got bKGD now */ + + switch (pData->iColortype) /* store fields for future reference */ + { + case 0 : ; /* gray */ + case 4 : { /* graya */ + pBuf->iBKGDgray = mng_get_uint16 (pRawdata); + break; + } + case 2 : ; /* rgb */ + case 6 : { /* rgba */ + pBuf->iBKGDred = mng_get_uint16 (pRawdata); + pBuf->iBKGDgreen = mng_get_uint16 (pRawdata+2); + pBuf->iBKGDblue = mng_get_uint16 (pRawdata+4); + break; + } + case 3 : { /* indexed */ + pBuf->iBKGDindex = *pRawdata; + break; + } + } + } + else /* store as global */ + { + if (iRawlen) + { + pData->iGlobalBKGDred = mng_get_uint16 (pRawdata); + pData->iGlobalBKGDgreen = mng_get_uint16 (pRawdata+2); + pData->iGlobalBKGDblue = mng_get_uint16 (pRawdata+4); + } + + { /* create an animation object */ + mng_retcode iRetcode = mng_create_ani_bkgd (pData, pData->iGlobalBKGDred, + pData->iGlobalBKGDgreen, + pData->iGlobalBKGDblue); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_bkgdp)*ppChunk)->bEmpty = (mng_bool)(iRawlen == 0); + ((mng_bkgdp)*ppChunk)->iType = pData->iColortype; + + if (iRawlen) + { + switch (iRawlen) /* guess from length */ + { + case 1 : { /* indexed */ + ((mng_bkgdp)*ppChunk)->iType = 3; + ((mng_bkgdp)*ppChunk)->iIndex = *pRawdata; + break; + } + case 2 : { /* gray */ + ((mng_bkgdp)*ppChunk)->iType = 0; + ((mng_bkgdp)*ppChunk)->iGray = mng_get_uint16 (pRawdata); + break; + } + case 6 : { /* rgb */ + ((mng_bkgdp)*ppChunk)->iType = 2; + ((mng_bkgdp)*ppChunk)->iRed = mng_get_uint16 (pRawdata); + ((mng_bkgdp)*ppChunk)->iGreen = mng_get_uint16 (pRawdata+2); + ((mng_bkgdp)*ppChunk)->iBlue = mng_get_uint16 (pRawdata+4); + break; + } + } + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_BKGD, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_pHYs +READ_CHUNK (mng_read_phys) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_PHYS, MNG_LC_START); +#endif + /* sequence checks */ +#ifdef MNG_INCLUDE_JNG + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && + (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR)) +#else + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && + (!pData->bHasBASI) && (!pData->bHasDHDR) ) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIDAT) || (pData->bHasJDAT) || (pData->bHasJDAA)) +#else + if (pData->bHasIDAT) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + /* it's 9 bytes or empty; no more, no less! */ + if ((iRawlen != 9) && (iRawlen != 0)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + +#ifdef MNG_SUPPORT_DISPLAY + { + + + /* TODO: something !!! */ + + + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_physp)*ppChunk)->bEmpty = (mng_bool)(iRawlen == 0); + + if (iRawlen) + { + ((mng_physp)*ppChunk)->iSizex = mng_get_uint32 (pRawdata); + ((mng_physp)*ppChunk)->iSizey = mng_get_uint32 (pRawdata+4); + ((mng_physp)*ppChunk)->iUnit = *(pRawdata+8); + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_PHYS, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_sBIT +READ_CHUNK (mng_read_sbit) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_SBIT, MNG_LC_START); +#endif + /* sequence checks */ +#ifdef MNG_INCLUDE_JNG + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && + (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR)) +#else + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && + (!pData->bHasBASI) && (!pData->bHasDHDR) ) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasPLTE) || (pData->bHasIDAT) || (pData->bHasJDAT) || (pData->bHasJDAA)) +#else + if ((pData->bHasPLTE) || (pData->bHasIDAT)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (iRawlen > 4) /* it just can't be bigger than that! */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + +#ifdef MNG_INCLUDE_JNG /* length checks */ + if (pData->bHasJHDR) + { + if ((pData->iJHDRcolortype == 8) && (iRawlen != 1)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + if ((pData->iJHDRcolortype == 10) && (iRawlen != 3)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + if ((pData->iJHDRcolortype == 12) && (iRawlen != 2)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + if ((pData->iJHDRcolortype == 14) && (iRawlen != 4)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + } + else +#endif /* MNG_INCLUDE_JNG */ + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) + { + if ((pData->iColortype == 0) && (iRawlen != 1)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + if ((pData->iColortype == 2) && (iRawlen != 3)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + if ((pData->iColortype == 3) && (iRawlen != 3)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + if ((pData->iColortype == 4) && (iRawlen != 2)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + if ((pData->iColortype == 6) && (iRawlen != 4)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + } + else + { /* global = empty or RGBA */ + if ((iRawlen != 0) && (iRawlen != 4)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + } + +#ifdef MNG_SUPPORT_DISPLAY + { + + + /* TODO: something !!! */ + + + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_sbitp)*ppChunk)->bEmpty = (mng_bool)(iRawlen == 0); + + if (iRawlen) + { +#ifdef MNG_INCLUDE_JNG + if (pData->bHasJHDR) + ((mng_sbitp)*ppChunk)->iType = pData->iJHDRcolortype; + else +#endif + if (pData->bHasIHDR) + ((mng_sbitp)*ppChunk)->iType = pData->iColortype; + else /* global ! */ + ((mng_sbitp)*ppChunk)->iType = 6; + + if (iRawlen > 0) + ((mng_sbitp)*ppChunk)->aBits [0] = *pRawdata; + if (iRawlen > 1) + ((mng_sbitp)*ppChunk)->aBits [1] = *(pRawdata+1); + if (iRawlen > 2) + ((mng_sbitp)*ppChunk)->aBits [2] = *(pRawdata+2); + if (iRawlen > 3) + ((mng_sbitp)*ppChunk)->aBits [3] = *(pRawdata+3); + + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_SBIT, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_sPLT +READ_CHUNK (mng_read_splt) +{ + mng_uint8p pTemp; + mng_uint32 iNamelen; + mng_uint8 iSampledepth; + mng_uint32 iRemain; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_SPLT, MNG_LC_START); +#endif + /* sequence checks */ + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && + (!pData->bHasBASI) && (!pData->bHasDHDR) ) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (pData->bHasIDAT) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (iRawlen) + { + pTemp = find_null (pRawdata); /* find null-separator */ + /* not found inside input-data ? */ + if ((pTemp - pRawdata) > (mng_int32)iRawlen) + MNG_ERROR (pData, MNG_NULLNOTFOUND); + + iNamelen = (mng_uint32)(pTemp - pRawdata); + iSampledepth = *(pTemp+1); + iRemain = (iRawlen - 2 - iNamelen); + + if ((iSampledepth != 1) && (iSampledepth != 2)) + MNG_ERROR (pData, MNG_INVSAMPLEDEPTH); + /* check remaining length */ + if ( ((iSampledepth == 1) && (iRemain % 6 != 0)) || + ((iSampledepth == 2) && (iRemain % 10 != 0)) ) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + } + else + { + pTemp = MNG_NULL; + iNamelen = 0; + iSampledepth = 0; + iRemain = 0; + } + +#ifdef MNG_SUPPORT_DISPLAY + { + + + /* TODO: something !!! */ + + + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_spltp)*ppChunk)->bEmpty = (mng_bool)(iRawlen == 0); + + if (iRawlen) + { + ((mng_spltp)*ppChunk)->iNamesize = iNamelen; + ((mng_spltp)*ppChunk)->iSampledepth = iSampledepth; + + if (iSampledepth == 1) + ((mng_spltp)*ppChunk)->iEntrycount = iRemain / 6; + else + ((mng_spltp)*ppChunk)->iEntrycount = iRemain / 10; + + if (iNamelen) + { + MNG_ALLOC (pData, ((mng_spltp)*ppChunk)->zName, iNamelen+1); + MNG_COPY (((mng_spltp)*ppChunk)->zName, pRawdata, iNamelen); + } + + if (iRemain) + { + MNG_ALLOC (pData, ((mng_spltp)*ppChunk)->pEntries, iRemain); + MNG_COPY (((mng_spltp)*ppChunk)->pEntries, pTemp+2, iRemain); + } + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_SPLT, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_hIST +READ_CHUNK (mng_read_hist) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_HIST, MNG_LC_START); +#endif + /* sequence checks */ + if ((!pData->bHasIHDR) && (!pData->bHasBASI) && (!pData->bHasDHDR) ) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if ((!pData->bHasPLTE) || (pData->bHasIDAT)) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + /* length oke ? */ + if ( ((iRawlen & 0x01) != 0) || ((iRawlen >> 1) != pData->iPLTEcount) ) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + +#ifdef MNG_SUPPORT_DISPLAY + { + + + /* TODO: something !!! */ + + + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { + mng_uint32 iX; + /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_histp)*ppChunk)->iEntrycount = iRawlen >> 1; + + for (iX = 0; iX < (iRawlen >> 1); iX++) + { + ((mng_histp)*ppChunk)->aEntries [iX] = mng_get_uint16 (pRawdata); + pRawdata += 2; + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_HIST, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_tIME +READ_CHUNK (mng_read_time) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_TIME, MNG_LC_START); +#endif + /* sequence checks */ +#ifdef MNG_INCLUDE_JNG + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && + (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR)) +#else + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && + (!pData->bHasBASI) && (!pData->bHasDHDR) ) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (iRawlen != 7) /* length must be exactly 7 */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + +/* if (pData->fProcesstime) */ /* inform the application ? */ +/* { + + pData->fProcesstime ((mng_handle)pData, ); + } */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_timep)*ppChunk)->iYear = mng_get_uint16 (pRawdata); + ((mng_timep)*ppChunk)->iMonth = *(pRawdata+2); + ((mng_timep)*ppChunk)->iDay = *(pRawdata+3); + ((mng_timep)*ppChunk)->iHour = *(pRawdata+4); + ((mng_timep)*ppChunk)->iMinute = *(pRawdata+5); + ((mng_timep)*ppChunk)->iSecond = *(pRawdata+6); + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_TIME, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +READ_CHUNK (mng_read_mhdr) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_MHDR, MNG_LC_START); +#endif + + if (pData->eSigtype != mng_it_mng) /* sequence checks */ + MNG_ERROR (pData, MNG_CHUNKNOTALLOWED); + + if (pData->bHasheader) /* can only be the first chunk! */ + MNG_ERROR (pData, MNG_SEQUENCEERROR); + /* correct length ? */ +#ifndef MNG_NO_OLD_VERSIONS + if ((iRawlen != 28) && (iRawlen != 12)) +#else + if ((iRawlen != 28)) +#endif + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + pData->bHasMHDR = MNG_TRUE; /* oh boy, a real MNG */ + pData->bHasheader = MNG_TRUE; /* we've got a header */ + pData->eImagetype = mng_it_mng; /* fill header fields */ + pData->iWidth = mng_get_uint32 (pRawdata); + pData->iHeight = mng_get_uint32 (pRawdata+4); + pData->iTicks = mng_get_uint32 (pRawdata+8); + +#ifndef MNG_NO_OLD_VERSIONS + if (iRawlen == 28) /* proper MHDR ? */ + { +#endif + pData->iLayercount = mng_get_uint32 (pRawdata+12); + pData->iFramecount = mng_get_uint32 (pRawdata+16); + pData->iPlaytime = mng_get_uint32 (pRawdata+20); + pData->iSimplicity = mng_get_uint32 (pRawdata+24); + +#ifndef MNG_NO_OLD_VERSIONS + pData->bPreDraft48 = MNG_FALSE; + } + else /* probably pre-draft48 then */ + { + pData->iLayercount = 0; + pData->iFramecount = 0; + pData->iPlaytime = 0; + pData->iSimplicity = 0; + + pData->bPreDraft48 = MNG_TRUE; + } +#endif + /* predict alpha-depth */ + if ((pData->iSimplicity & 0x00000001) == 0) +#ifndef MNG_NO_16BIT_SUPPORT + pData->iAlphadepth = 16; /* no indicators = assume the worst */ +#else + pData->iAlphadepth = 8; /* anything else = assume the worst */ +#endif + else + if ((pData->iSimplicity & 0x00000008) == 0) + pData->iAlphadepth = 0; /* no transparency at all */ + else + if ((pData->iSimplicity & 0x00000140) == 0x00000040) + pData->iAlphadepth = 1; /* no semi-transparency guaranteed */ + else +#ifndef MNG_NO_16BIT_SUPPORT + pData->iAlphadepth = 16; /* anything else = assume the worst */ +#else + pData->iAlphadepth = 8; /* anything else = assume the worst */ +#endif + +#ifdef MNG_INCLUDE_JNG /* can we handle the complexity ? */ + if (pData->iSimplicity & 0x0000FC00) +#else + if (pData->iSimplicity & 0x0000FC10) +#endif + MNG_ERROR (pData, MNG_MNGTOOCOMPLEX); + /* fits on maximum canvas ? */ + if ((pData->iWidth > pData->iMaxwidth) || (pData->iHeight > pData->iMaxheight)) + MNG_WARNING (pData, MNG_IMAGETOOLARGE); + + if (pData->fProcessheader) /* inform the app ? */ + if (!pData->fProcessheader (((mng_handle)pData), pData->iWidth, pData->iHeight)) + MNG_ERROR (pData, MNG_APPMISCERROR); + + pData->iImagelevel++; /* one level deeper */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_mhdrp)*ppChunk)->iWidth = pData->iWidth; + ((mng_mhdrp)*ppChunk)->iHeight = pData->iHeight; + ((mng_mhdrp)*ppChunk)->iTicks = pData->iTicks; + ((mng_mhdrp)*ppChunk)->iLayercount = pData->iLayercount; + ((mng_mhdrp)*ppChunk)->iFramecount = pData->iFramecount; + ((mng_mhdrp)*ppChunk)->iPlaytime = pData->iPlaytime; + ((mng_mhdrp)*ppChunk)->iSimplicity = pData->iSimplicity; + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_MHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +READ_CHUNK (mng_read_mend) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_MEND, MNG_LC_START); +#endif + + if (!pData->bHasMHDR) /* sequence checks */ + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (iRawlen > 0) /* must not contain data! */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + +#ifdef MNG_SUPPORT_DISPLAY + { /* do something */ + mng_retcode iRetcode = mng_process_display_mend (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + if (!pData->iTotalframes) /* save totals */ + pData->iTotalframes = pData->iFrameseq; + if (!pData->iTotallayers) + pData->iTotallayers = pData->iLayerseq; + if (!pData->iTotalplaytime) + pData->iTotalplaytime = pData->iFrametime; + } +#endif /* MNG_SUPPORT_DISPLAY */ + + pData->bHasMHDR = MNG_FALSE; /* end of the line, bro! */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_MEND, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_LOOP +READ_CHUNK (mng_read_loop) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_LOOP, MNG_LC_START); +#endif + + if (!pData->bHasMHDR) /* sequence checks */ + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (!pData->bCacheplayback) /* must store playback info to work!! */ + MNG_ERROR (pData, MNG_LOOPWITHCACHEOFF); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (iRawlen >= 5) /* length checks */ + { + if (iRawlen >= 6) + { + if ((iRawlen - 6) % 4 != 0) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + } + } + else + MNG_ERROR (pData, MNG_INVALIDLENGTH); + +#ifdef MNG_SUPPORT_DISPLAY + { + mng_uint8 iLevel; + mng_uint32 iRepeat; + mng_uint8 iTermination = 0; + mng_uint32 iItermin = 1; + mng_uint32 iItermax = 0x7fffffffL; + mng_retcode iRetcode; + + pData->bHasLOOP = MNG_TRUE; /* indicate we're inside a loop */ + + iLevel = *pRawdata; /* determine the fields for processing */ + +#ifndef MNG_NO_OLD_VERSIONS + if (pData->bPreDraft48) + { + iTermination = *(pRawdata+1); + + iRepeat = mng_get_uint32 (pRawdata+2); + } + else +#endif + iRepeat = mng_get_uint32 (pRawdata+1); + + if (iRawlen >= 6) + { +#ifndef MNG_NO_OLD_VERSIONS + if (!pData->bPreDraft48) +#endif + iTermination = *(pRawdata+5); + + if (iRawlen >= 10) + { + iItermin = mng_get_uint32 (pRawdata+6); + + if (iRawlen >= 14) + { + iItermax = mng_get_uint32 (pRawdata+10); + + /* TODO: process signals */ + + } + } + } + /* create the LOOP ani-object */ + iRetcode = mng_create_ani_loop (pData, iLevel, iRepeat, iTermination, + iItermin, iItermax, 0, 0); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* skip till matching ENDL if iteration=0 */ + if ((!pData->bSkipping) && (iRepeat == 0)) + pData->bSkipping = MNG_TRUE; + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + if (iRawlen >= 5) /* store the fields */ + { + ((mng_loopp)*ppChunk)->iLevel = *pRawdata; + +#ifndef MNG_NO_OLD_VERSIONS + if (pData->bPreDraft48) + { + ((mng_loopp)*ppChunk)->iTermination = *(pRawdata+1); + ((mng_loopp)*ppChunk)->iRepeat = mng_get_uint32 (pRawdata+2); + } + else +#endif + { + ((mng_loopp)*ppChunk)->iRepeat = mng_get_uint32 (pRawdata+1); + } + + if (iRawlen >= 6) + { +#ifndef MNG_NO_OLD_VERSIONS + if (!pData->bPreDraft48) +#endif + ((mng_loopp)*ppChunk)->iTermination = *(pRawdata+5); + + if (iRawlen >= 10) + { + ((mng_loopp)*ppChunk)->iItermin = mng_get_uint32 (pRawdata+6); + +#ifndef MNG_NO_LOOP_SIGNALS_SUPPORTED + if (iRawlen >= 14) + { + ((mng_loopp)*ppChunk)->iItermax = mng_get_uint32 (pRawdata+10); + ((mng_loopp)*ppChunk)->iCount = (iRawlen - 14) / 4; + + if (((mng_loopp)*ppChunk)->iCount) + { + MNG_ALLOC (pData, ((mng_loopp)*ppChunk)->pSignals, + ((mng_loopp)*ppChunk)->iCount << 2); + +#ifndef MNG_BIGENDIAN_SUPPORTED + { + mng_uint32 iX; + mng_uint8p pIn = pRawdata + 14; + mng_uint32p pOut = (mng_uint32p)((mng_loopp)*ppChunk)->pSignals; + + for (iX = 0; iX < ((mng_loopp)*ppChunk)->iCount; iX++) + { + *pOut++ = mng_get_uint32 (pIn); + pIn += 4; + } + } +#else + MNG_COPY (((mng_loopp)*ppChunk)->pSignals, pRawdata + 14, + ((mng_loopp)*ppChunk)->iCount << 2); +#endif /* !MNG_BIGENDIAN_SUPPORTED */ + } + } +#endif + } + } + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_LOOP, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_LOOP +READ_CHUNK (mng_read_endl) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_ENDL, MNG_LC_START); +#endif + + if (!pData->bHasMHDR) /* sequence checks */ + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (iRawlen != 1) /* length must be exactly 1 */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + +#ifdef MNG_SUPPORT_DISPLAY + { + if (pData->bHasLOOP) /* are we really processing a loop ? */ + { + mng_uint8 iLevel = *pRawdata; /* get the nest level */ + /* create an ENDL animation object */ + mng_retcode iRetcode = mng_create_ani_endl (pData, iLevel); + + if (iRetcode) /* on error bail out */ + return iRetcode; + +/* { + mng_ani_endlp pENDL = (mng_ani_endlp)pData->pLastaniobj; + + iRetcode = pENDL->sHeader.fProcess (pData, pENDL); + + if (iRetcode) + return iRetcode; + } */ + } + else + MNG_ERROR (pData, MNG_NOMATCHINGLOOP); + + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_endlp)*ppChunk)->iLevel = *pRawdata; + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_ENDL, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_DEFI +READ_CHUNK (mng_read_defi) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_DEFI, MNG_LC_START); +#endif + + if (!pData->bHasMHDR) /* sequence checks */ + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + /* check the length */ + if ((iRawlen != 2) && (iRawlen != 3) && (iRawlen != 4) && + (iRawlen != 12) && (iRawlen != 28)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + +#ifdef MNG_SUPPORT_DISPLAY + { + mng_retcode iRetcode; + + pData->iDEFIobjectid = mng_get_uint16 (pRawdata); + + if (iRawlen > 2) + { + pData->bDEFIhasdonotshow = MNG_TRUE; + pData->iDEFIdonotshow = *(pRawdata+2); + } + else + { + pData->bDEFIhasdonotshow = MNG_FALSE; + pData->iDEFIdonotshow = 0; + } + + if (iRawlen > 3) + { + pData->bDEFIhasconcrete = MNG_TRUE; + pData->iDEFIconcrete = *(pRawdata+3); + } + else + { + pData->bDEFIhasconcrete = MNG_FALSE; + pData->iDEFIconcrete = 0; + } + + if (iRawlen > 4) + { + pData->bDEFIhasloca = MNG_TRUE; + pData->iDEFIlocax = mng_get_int32 (pRawdata+4); + pData->iDEFIlocay = mng_get_int32 (pRawdata+8); + } + else + { + pData->bDEFIhasloca = MNG_FALSE; + pData->iDEFIlocax = 0; + pData->iDEFIlocay = 0; + } + + if (iRawlen > 12) + { + pData->bDEFIhasclip = MNG_TRUE; + pData->iDEFIclipl = mng_get_int32 (pRawdata+12); + pData->iDEFIclipr = mng_get_int32 (pRawdata+16); + pData->iDEFIclipt = mng_get_int32 (pRawdata+20); + pData->iDEFIclipb = mng_get_int32 (pRawdata+24); + } + else + { + pData->bDEFIhasclip = MNG_FALSE; + pData->iDEFIclipl = 0; + pData->iDEFIclipr = 0; + pData->iDEFIclipt = 0; + pData->iDEFIclipb = 0; + } + /* create an animation object */ + iRetcode = mng_create_ani_defi (pData); + + if (!iRetcode) /* do display processing */ + iRetcode = mng_process_display_defi (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_defip)*ppChunk)->iObjectid = mng_get_uint16 (pRawdata); + + if (iRawlen > 2) + { + ((mng_defip)*ppChunk)->bHasdonotshow = MNG_TRUE; + ((mng_defip)*ppChunk)->iDonotshow = *(pRawdata+2); + } + else + ((mng_defip)*ppChunk)->bHasdonotshow = MNG_FALSE; + + if (iRawlen > 3) + { + ((mng_defip)*ppChunk)->bHasconcrete = MNG_TRUE; + ((mng_defip)*ppChunk)->iConcrete = *(pRawdata+3); + } + else + ((mng_defip)*ppChunk)->bHasconcrete = MNG_FALSE; + + if (iRawlen > 4) + { + ((mng_defip)*ppChunk)->bHasloca = MNG_TRUE; + ((mng_defip)*ppChunk)->iXlocation = mng_get_int32 (pRawdata+4); + ((mng_defip)*ppChunk)->iYlocation = mng_get_int32 (pRawdata+8); + } + else + ((mng_defip)*ppChunk)->bHasloca = MNG_FALSE; + + if (iRawlen > 12) + { + ((mng_defip)*ppChunk)->bHasclip = MNG_TRUE; + ((mng_defip)*ppChunk)->iLeftcb = mng_get_int32 (pRawdata+12); + ((mng_defip)*ppChunk)->iRightcb = mng_get_int32 (pRawdata+16); + ((mng_defip)*ppChunk)->iTopcb = mng_get_int32 (pRawdata+20); + ((mng_defip)*ppChunk)->iBottomcb = mng_get_int32 (pRawdata+24); + } + else + ((mng_defip)*ppChunk)->bHasclip = MNG_FALSE; + + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_DEFI, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_BASI +READ_CHUNK (mng_read_basi) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_BASI, MNG_LC_START); +#endif + + if (!pData->bHasMHDR) /* sequence checks */ + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + /* check the length */ + if ((iRawlen != 13) && (iRawlen != 19) && (iRawlen != 21) && (iRawlen != 22)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + pData->bHasBASI = MNG_TRUE; /* inside a BASI-IEND block now */ + /* store interesting fields */ + pData->iDatawidth = mng_get_uint32 (pRawdata); + pData->iDataheight = mng_get_uint32 (pRawdata+4); + pData->iBitdepth = *(pRawdata+8); + pData->iColortype = *(pRawdata+9); + pData->iCompression = *(pRawdata+10); + pData->iFilter = *(pRawdata+11); + pData->iInterlace = *(pRawdata+12); + + +#if defined(MNG_NO_1_2_4BIT_SUPPORT) || defined(MNG_NO_16BIT_SUPPORT) + pData->iPNGmult = 1; + pData->iPNGdepth = pData->iBitdepth; +#endif + +#ifdef MNG_NO_1_2_4BIT_SUPPORT + if (pData->iBitdepth < 8) + pData->iBitdepth = 8; +#endif +#ifdef MNG_NO_16BIT_SUPPORT + if (pData->iBitdepth > 8) + { + pData->iBitdepth = 8; + pData->iPNGmult = 2; + } +#endif + + if ((pData->iBitdepth != 8) /* parameter validity checks */ +#ifndef MNG_NO_1_2_4BIT_SUPPORT + && (pData->iBitdepth != 1) && + (pData->iBitdepth != 2) && + (pData->iBitdepth != 4) +#endif +#ifndef MNG_NO_16BIT_SUPPORT + && (pData->iBitdepth != 16) +#endif + ) + MNG_ERROR (pData, MNG_INVALIDBITDEPTH); + + if ((pData->iColortype != MNG_COLORTYPE_GRAY ) && + (pData->iColortype != MNG_COLORTYPE_RGB ) && + (pData->iColortype != MNG_COLORTYPE_INDEXED) && + (pData->iColortype != MNG_COLORTYPE_GRAYA ) && + (pData->iColortype != MNG_COLORTYPE_RGBA ) ) + MNG_ERROR (pData, MNG_INVALIDCOLORTYPE); + + if ((pData->iColortype == MNG_COLORTYPE_INDEXED) && (pData->iBitdepth > 8)) + MNG_ERROR (pData, MNG_INVALIDBITDEPTH); + + if (((pData->iColortype == MNG_COLORTYPE_RGB ) || + (pData->iColortype == MNG_COLORTYPE_GRAYA ) || + (pData->iColortype == MNG_COLORTYPE_RGBA ) ) && + (pData->iBitdepth < 8 ) ) + MNG_ERROR (pData, MNG_INVALIDBITDEPTH); + + if (pData->iCompression != MNG_COMPRESSION_DEFLATE) + MNG_ERROR (pData, MNG_INVALIDCOMPRESS); + +#if defined(FILTER192) || defined(FILTER193) + if ((pData->iFilter != MNG_FILTER_ADAPTIVE ) && +#if defined(FILTER192) && defined(FILTER193) + (pData->iFilter != MNG_FILTER_DIFFERING) && + (pData->iFilter != MNG_FILTER_NOFILTER ) ) +#else +#ifdef FILTER192 + (pData->iFilter != MNG_FILTER_DIFFERING) ) +#else + (pData->iFilter != MNG_FILTER_NOFILTER ) ) +#endif +#endif + MNG_ERROR (pData, MNG_INVALIDFILTER); +#else + if (pData->iFilter) + MNG_ERROR (pData, MNG_INVALIDFILTER); +#endif + + if ((pData->iInterlace != MNG_INTERLACE_NONE ) && + (pData->iInterlace != MNG_INTERLACE_ADAM7) ) + MNG_ERROR (pData, MNG_INVALIDINTERLACE); + + pData->iImagelevel++; /* one level deeper */ + +#ifdef MNG_SUPPORT_DISPLAY + { + mng_uint16 iRed = 0; + mng_uint16 iGreen = 0; + mng_uint16 iBlue = 0; + mng_bool bHasalpha = MNG_FALSE; + mng_uint16 iAlpha = 0xFFFF; + mng_uint8 iViewable = 0; + mng_retcode iRetcode; + + if (iRawlen > 13) /* get remaining fields, if any */ + { + iRed = mng_get_uint16 (pRawdata+13); + iGreen = mng_get_uint16 (pRawdata+15); + iBlue = mng_get_uint16 (pRawdata+17); + } + + if (iRawlen > 19) + { + bHasalpha = MNG_TRUE; + iAlpha = mng_get_uint16 (pRawdata+19); + } + + if (iRawlen > 21) + iViewable = *(pRawdata+21); + /* create an animation object */ + iRetcode = mng_create_ani_basi (pData, iRed, iGreen, iBlue, + bHasalpha, iAlpha, iViewable); + +/* if (!iRetcode) + iRetcode = mng_process_display_basi (pData, iRed, iGreen, iBlue, + bHasalpha, iAlpha, iViewable); */ + + if (iRetcode) /* on error bail out */ + return iRetcode; + + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_basip)*ppChunk)->iWidth = mng_get_uint32 (pRawdata); + ((mng_basip)*ppChunk)->iHeight = mng_get_uint32 (pRawdata+4); +#ifdef MNG_NO_16BIT_SUPPORT + if (*(pRawdata+8) > 8) + ((mng_basip)*ppChunk)->iBitdepth = 8; + else +#endif + ((mng_basip)*ppChunk)->iBitdepth = *(pRawdata+8); + ((mng_basip)*ppChunk)->iColortype = *(pRawdata+9); + ((mng_basip)*ppChunk)->iCompression = *(pRawdata+10); + ((mng_basip)*ppChunk)->iFilter = *(pRawdata+11); + ((mng_basip)*ppChunk)->iInterlace = *(pRawdata+12); + + if (iRawlen > 13) + { + ((mng_basip)*ppChunk)->iRed = mng_get_uint16 (pRawdata+13); + ((mng_basip)*ppChunk)->iGreen = mng_get_uint16 (pRawdata+15); + ((mng_basip)*ppChunk)->iBlue = mng_get_uint16 (pRawdata+17); + } + + if (iRawlen > 19) + ((mng_basip)*ppChunk)->iAlpha = mng_get_uint16 (pRawdata+19); + + if (iRawlen > 21) + ((mng_basip)*ppChunk)->iViewable = *(pRawdata+21); + + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_BASI, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_CLON +READ_CHUNK (mng_read_clon) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_CLON, MNG_LC_START); +#endif + + if (!pData->bHasMHDR) /* sequence checks */ + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + /* check the length */ + if ((iRawlen != 4) && (iRawlen != 5) && (iRawlen != 6) && + (iRawlen != 7) && (iRawlen != 16)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + +#ifdef MNG_SUPPORT_DISPLAY + { + mng_uint16 iSourceid, iCloneid; + mng_uint8 iClonetype = 0; + mng_bool bHasdonotshow = MNG_FALSE; + mng_uint8 iDonotshow = 0; + mng_uint8 iConcrete = 0; + mng_bool bHasloca = MNG_FALSE; + mng_uint8 iLocationtype = 0; + mng_int32 iLocationx = 0; + mng_int32 iLocationy = 0; + mng_retcode iRetcode; + + iSourceid = mng_get_uint16 (pRawdata); + iCloneid = mng_get_uint16 (pRawdata+2); + + if (iRawlen > 4) + iClonetype = *(pRawdata+4); + + if (iRawlen > 5) + { + bHasdonotshow = MNG_TRUE; + iDonotshow = *(pRawdata+5); + } + + if (iRawlen > 6) + iConcrete = *(pRawdata+6); + + if (iRawlen > 7) + { + bHasloca = MNG_TRUE; + iLocationtype = *(pRawdata+7); + iLocationx = mng_get_int32 (pRawdata+8); + iLocationy = mng_get_int32 (pRawdata+12); + } + + iRetcode = mng_create_ani_clon (pData, iSourceid, iCloneid, iClonetype, + bHasdonotshow, iDonotshow, iConcrete, + bHasloca, iLocationtype, iLocationx, iLocationy); + +/* if (!iRetcode) + iRetcode = mng_process_display_clon (pData, iSourceid, iCloneid, iClonetype, + bHasdonotshow, iDonotshow, iConcrete, + bHasloca, iLocationtype, iLocationx, + iLocationy); */ + + if (iRetcode) /* on error bail out */ + return iRetcode; + + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_clonp)*ppChunk)->iSourceid = mng_get_uint16 (pRawdata); + ((mng_clonp)*ppChunk)->iCloneid = mng_get_uint16 (pRawdata+2); + + if (iRawlen > 4) + ((mng_clonp)*ppChunk)->iClonetype = *(pRawdata+4); + + if (iRawlen > 5) + ((mng_clonp)*ppChunk)->iDonotshow = *(pRawdata+5); + + if (iRawlen > 6) + ((mng_clonp)*ppChunk)->iConcrete = *(pRawdata+6); + + if (iRawlen > 7) + { + ((mng_clonp)*ppChunk)->bHasloca = MNG_TRUE; + ((mng_clonp)*ppChunk)->iLocationtype = *(pRawdata+7); + ((mng_clonp)*ppChunk)->iLocationx = mng_get_int32 (pRawdata+8); + ((mng_clonp)*ppChunk)->iLocationy = mng_get_int32 (pRawdata+12); + } + else + { + ((mng_clonp)*ppChunk)->bHasloca = MNG_FALSE; + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_CLON, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_PAST +READ_CHUNK (mng_read_past) +{ +#if defined(MNG_STORE_CHUNKS) || defined(MNG_SUPPORT_DISPLAY) + mng_retcode iRetcode; + mng_uint16 iTargetid; + mng_uint8 iTargettype; + mng_int32 iTargetx; + mng_int32 iTargety; + mng_uint32 iCount; + mng_uint32 iSize; + mng_ptr pSources; + mng_uint32 iX; + mng_past_sourcep pSource; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_PAST, MNG_LC_START); +#endif + + if (!pData->bHasMHDR) /* sequence checks */ + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + /* check the length */ + if ((iRawlen < 41) || (((iRawlen - 11) % 30) != 0)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + +#if defined(MNG_STORE_CHUNKS) || defined(MNG_SUPPORT_DISPLAY) + iTargetid = mng_get_uint16 (pRawdata); + iTargettype = *(pRawdata+2); + iTargetx = mng_get_int32 (pRawdata+3); + iTargety = mng_get_int32 (pRawdata+7); + iCount = ((iRawlen - 11) / 30); /* how many entries again? */ + iSize = iCount * sizeof (mng_past_source); + + pRawdata += 11; + /* get a buffer for all the source blocks */ + MNG_ALLOC (pData, pSources, iSize); + + pSource = (mng_past_sourcep)pSources; + + for (iX = 0; iX < iCount; iX++) /* now copy the source blocks */ + { + pSource->iSourceid = mng_get_uint16 (pRawdata); + pSource->iComposition = *(pRawdata+2); + pSource->iOrientation = *(pRawdata+3); + pSource->iOffsettype = *(pRawdata+4); + pSource->iOffsetx = mng_get_int32 (pRawdata+5); + pSource->iOffsety = mng_get_int32 (pRawdata+9); + pSource->iBoundarytype = *(pRawdata+13); + pSource->iBoundaryl = mng_get_int32 (pRawdata+14); + pSource->iBoundaryr = mng_get_int32 (pRawdata+18); + pSource->iBoundaryt = mng_get_int32 (pRawdata+22); + pSource->iBoundaryb = mng_get_int32 (pRawdata+26); + + pSource++; + pRawdata += 30; + } +#endif + +#ifdef MNG_SUPPORT_DISPLAY + { /* create playback object */ + iRetcode = mng_create_ani_past (pData, iTargetid, iTargettype, iTargetx, + iTargety, iCount, pSources); + +/* if (!iRetcode) + iRetcode = mng_process_display_past (pData, iTargetid, iTargettype, iTargetx, + iTargety, iCount, pSources); */ + + if (iRetcode) /* on error bail out */ + { + MNG_FREEX (pData, pSources, iSize); + return iRetcode; + } + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + { + MNG_FREEX (pData, pSources, iSize); + return iRetcode; + } + /* store the fields */ + ((mng_pastp)*ppChunk)->iDestid = iTargetid; + ((mng_pastp)*ppChunk)->iTargettype = iTargettype; + ((mng_pastp)*ppChunk)->iTargetx = iTargetx; + ((mng_pastp)*ppChunk)->iTargety = iTargety; + ((mng_pastp)*ppChunk)->iCount = iCount; + /* get a buffer & copy the source blocks */ + MNG_ALLOC (pData, ((mng_pastp)*ppChunk)->pSources, iSize); + MNG_COPY (((mng_pastp)*ppChunk)->pSources, pSources, iSize); + } +#endif /* MNG_STORE_CHUNKS */ + +#if defined(MNG_STORE_CHUNKS) || defined(MNG_SUPPORT_DISPLAY) + /* free the source block buffer */ + MNG_FREEX (pData, pSources, iSize); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_PAST, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_DISC +READ_CHUNK (mng_read_disc) +{ +#if defined(MNG_SUPPORT_DISPLAY) || defined(MNG_STORE_CHUNKS) + mng_uint32 iCount; + mng_uint16p pIds = MNG_NULL; + mng_retcode iRetcode; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_DISC, MNG_LC_START); +#endif + + if (!pData->bHasMHDR) /* sequence checks */ + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if ((iRawlen % 2) != 0) /* check the length */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + +#if defined(MNG_SUPPORT_DISPLAY) || defined(MNG_STORE_CHUNKS) + iCount = (iRawlen / sizeof (mng_uint16)); + + if (iCount) + { + MNG_ALLOC (pData, pIds, iRawlen); + +#ifndef MNG_BIGENDIAN_SUPPORTED + { + mng_uint32 iX; + mng_uint8p pIn = pRawdata; + mng_uint16p pOut = pIds; + + for (iX = 0; iX < iCount; iX++) + { + *pOut++ = mng_get_uint16 (pIn); + pIn += 2; + } + } +#else + MNG_COPY (pIds, pRawdata, iRawlen); +#endif /* !MNG_BIGENDIAN_SUPPORTED */ + } +#endif + +#ifdef MNG_SUPPORT_DISPLAY + { /* create playback object */ + iRetcode = mng_create_ani_disc (pData, iCount, pIds); + +/* if (!iRetcode) + iRetcode = mng_process_display_disc (pData, iCount, pIds); */ + + if (iRetcode) /* on error bail out */ + return iRetcode; + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_discp)*ppChunk)->iCount = iCount; + + if (iRawlen) + { + MNG_ALLOC (pData, ((mng_discp)*ppChunk)->pObjectids, iRawlen); + MNG_COPY (((mng_discp)*ppChunk)->pObjectids, pIds, iRawlen); + } + } +#endif /* MNG_STORE_CHUNKS */ + +#if defined(MNG_SUPPORT_DISPLAY) || defined(MNG_STORE_CHUNKS) + if (iRawlen) + MNG_FREEX (pData, pIds, iRawlen); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_DISC, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_BACK +READ_CHUNK (mng_read_back) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_BACK, MNG_LC_START); +#endif + + if (!pData->bHasMHDR) /* sequence checks */ + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + /* check the length */ + if ((iRawlen != 6) && (iRawlen != 7) && (iRawlen != 9) && (iRawlen != 10)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + +#ifdef MNG_SUPPORT_DISPLAY + { + mng_retcode iRetcode; + /* retrieve the fields */ + pData->bHasBACK = MNG_TRUE; + pData->iBACKred = mng_get_uint16 (pRawdata); + pData->iBACKgreen = mng_get_uint16 (pRawdata+2); + pData->iBACKblue = mng_get_uint16 (pRawdata+4); + + if (iRawlen > 6) + pData->iBACKmandatory = *(pRawdata+6); + else + pData->iBACKmandatory = 0; + + if (iRawlen > 7) + pData->iBACKimageid = mng_get_uint16 (pRawdata+7); + else + pData->iBACKimageid = 0; + + if (iRawlen > 9) + pData->iBACKtile = *(pRawdata+9); + else + pData->iBACKtile = 0; + + iRetcode = mng_create_ani_back (pData, pData->iBACKred, pData->iBACKgreen, + pData->iBACKblue, pData->iBACKmandatory, + pData->iBACKimageid, pData->iBACKtile); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_backp)*ppChunk)->iRed = mng_get_uint16 (pRawdata); + ((mng_backp)*ppChunk)->iGreen = mng_get_uint16 (pRawdata+2); + ((mng_backp)*ppChunk)->iBlue = mng_get_uint16 (pRawdata+4); + + if (iRawlen > 6) + ((mng_backp)*ppChunk)->iMandatory = *(pRawdata+6); + + if (iRawlen > 7) + ((mng_backp)*ppChunk)->iImageid = mng_get_uint16 (pRawdata+7); + + if (iRawlen > 9) + ((mng_backp)*ppChunk)->iTile = *(pRawdata+9); + + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_BACK, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_FRAM +READ_CHUNK (mng_read_fram) +{ + mng_uint8p pTemp; +#ifdef MNG_STORE_CHUNKS + mng_uint32 iNamelen; +#endif + mng_uint32 iRemain; + mng_uint32 iRequired = 0; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_FRAM, MNG_LC_START); +#endif + + if (!pData->bHasMHDR) /* sequence checks */ + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (iRawlen <= 1) /* only framing-mode ? */ + { +#ifdef MNG_STORE_CHUNKS + iNamelen = 0; /* indicate so */ +#endif + iRemain = 0; + pTemp = MNG_NULL; + } + else + { + pTemp = find_null (pRawdata+1); /* find null-separator */ + /* not found inside input-data ? */ + if ((pTemp - pRawdata) > (mng_int32)iRawlen) + pTemp = pRawdata + iRawlen; /* than remainder is name */ + +#ifdef MNG_STORE_CHUNKS + iNamelen = (mng_uint32)((pTemp - pRawdata) - 1); +#endif + iRemain = (mng_uint32)(iRawlen - (pTemp - pRawdata)); + + if (iRemain) /* if there is remaining data it's less 1 byte */ + iRemain--; + + if ((iRemain) && (iRemain < 4)) /* remains must be empty or at least 4 bytes */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + if (iRemain) + { + iRequired = 4; /* calculate and check required remaining length */ + + if (*(pTemp+1)) { iRequired += 4; } + if (*(pTemp+2)) { iRequired += 4; } + if (*(pTemp+3)) { iRequired += 17; } + + if (*(pTemp+4)) + { + if ((iRemain - iRequired) % 4 != 0) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + } + else + { + if (iRemain != iRequired) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + } + } + } + +#ifdef MNG_SUPPORT_DISPLAY + { + mng_uint8p pWork = pTemp; + mng_uint8 iFramemode = 0; + mng_uint8 iChangedelay = 0; + mng_uint32 iDelay = 0; + mng_uint8 iChangetimeout = 0; + mng_uint32 iTimeout = 0; + mng_uint8 iChangeclipping = 0; + mng_uint8 iCliptype = 0; + mng_int32 iClipl = 0; + mng_int32 iClipr = 0; + mng_int32 iClipt = 0; + mng_int32 iClipb = 0; + mng_retcode iRetcode; + + if (iRawlen) /* any data specified ? */ + { + if (*(pRawdata)) /* save the new framing mode ? */ + { + iFramemode = *(pRawdata); + +#ifndef MNG_NO_OLD_VERSIONS + if (pData->bPreDraft48) /* old style input-stream ? */ + { + switch (iFramemode) + { + case 0: { break; } + case 1: { iFramemode = 3; break; } + case 2: { iFramemode = 4; break; } + case 3: { iFramemode = 1; break; } + case 4: { iFramemode = 1; break; } + case 5: { iFramemode = 2; break; } + default: { iFramemode = 1; break; } + } + } +#endif + } + + if (iRemain) + { + iChangedelay = *(pWork+1); + iChangetimeout = *(pWork+2); + iChangeclipping = *(pWork+3); + pWork += 5; + + if (iChangedelay) /* delay changed ? */ + { + iDelay = mng_get_uint32 (pWork); + pWork += 4; + } + + if (iChangetimeout) /* timeout changed ? */ + { + iTimeout = mng_get_uint32 (pWork); + pWork += 4; + } + + if (iChangeclipping) /* clipping changed ? */ + { + iCliptype = *pWork; + iClipl = mng_get_int32 (pWork+1); + iClipr = mng_get_int32 (pWork+5); + iClipt = mng_get_int32 (pWork+9); + iClipb = mng_get_int32 (pWork+13); + } + } + } + + iRetcode = mng_create_ani_fram (pData, iFramemode, iChangedelay, iDelay, + iChangetimeout, iTimeout, + iChangeclipping, iCliptype, + iClipl, iClipr, iClipt, iClipb); + +/* if (!iRetcode) + iRetcode = mng_process_display_fram (pData, iFramemode, iChangedelay, iDelay, + iChangetimeout, iTimeout, + iChangeclipping, iCliptype, + iClipl, iClipr, iClipt, iClipb); */ + + if (iRetcode) /* on error bail out */ + return iRetcode; + + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_framp)*ppChunk)->bEmpty = (mng_bool)(iRawlen == 0); + + if (iRawlen) + { + mng_uint8 iFramemode = *(pRawdata); + +#ifndef MNG_NO_OLD_VERSIONS + if (pData->bPreDraft48) /* old style input-stream ? */ + { + switch (iFramemode) + { + case 1: { iFramemode = 3; break; } + case 2: { iFramemode = 4; break; } + case 3: { iFramemode = 5; break; } /* TODO: provision for mode=5 ??? */ + case 4: { iFramemode = 1; break; } + case 5: { iFramemode = 2; break; } + default: { iFramemode = 1; break; } + } + } +#endif + + ((mng_framp)*ppChunk)->iMode = iFramemode; + ((mng_framp)*ppChunk)->iNamesize = iNamelen; + + if (iNamelen) + { + MNG_ALLOC (pData, ((mng_framp)*ppChunk)->zName, iNamelen+1); + MNG_COPY (((mng_framp)*ppChunk)->zName, pRawdata+1, iNamelen); + } + + if (iRemain) + { + ((mng_framp)*ppChunk)->iChangedelay = *(pTemp+1); + ((mng_framp)*ppChunk)->iChangetimeout = *(pTemp+2); + ((mng_framp)*ppChunk)->iChangeclipping = *(pTemp+3); + ((mng_framp)*ppChunk)->iChangesyncid = *(pTemp+4); + + pTemp += 5; + + if (((mng_framp)*ppChunk)->iChangedelay) + { + ((mng_framp)*ppChunk)->iDelay = mng_get_uint32 (pTemp); + pTemp += 4; + } + + if (((mng_framp)*ppChunk)->iChangetimeout) + { + ((mng_framp)*ppChunk)->iTimeout = mng_get_uint32 (pTemp); + pTemp += 4; + } + + if (((mng_framp)*ppChunk)->iChangeclipping) + { + ((mng_framp)*ppChunk)->iBoundarytype = *pTemp; + ((mng_framp)*ppChunk)->iBoundaryl = mng_get_int32 (pTemp+1); + ((mng_framp)*ppChunk)->iBoundaryr = mng_get_int32 (pTemp+5); + ((mng_framp)*ppChunk)->iBoundaryt = mng_get_int32 (pTemp+9); + ((mng_framp)*ppChunk)->iBoundaryb = mng_get_int32 (pTemp+13); + pTemp += 17; + } + + if (((mng_framp)*ppChunk)->iChangesyncid) + { + ((mng_framp)*ppChunk)->iCount = (iRemain - iRequired) / 4; + + if (((mng_framp)*ppChunk)->iCount) + { + MNG_ALLOC (pData, ((mng_framp)*ppChunk)->pSyncids, + ((mng_framp)*ppChunk)->iCount * 4); + +#ifndef MNG_BIGENDIAN_SUPPORTED + { + mng_uint32 iX; + mng_uint32p pOut = ((mng_framp)*ppChunk)->pSyncids; + + for (iX = 0; iX < ((mng_framp)*ppChunk)->iCount; iX++) + { + *pOut++ = mng_get_uint32 (pTemp); + pTemp += 4; + } + } +#else + MNG_COPY (((mng_framp)*ppChunk)->pSyncids, pTemp, + ((mng_framp)*ppChunk)->iCount * 4); +#endif /* !MNG_BIGENDIAN_SUPPORTED */ + } + } + } + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_FRAM, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_MOVE +READ_CHUNK (mng_read_move) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_MOVE, MNG_LC_START); +#endif + + if (!pData->bHasMHDR) /* sequence checks */ + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (iRawlen != 13) /* check the length */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + +#ifdef MNG_SUPPORT_DISPLAY + { + mng_retcode iRetcode; + /* create a MOVE animation object */ + iRetcode = mng_create_ani_move (pData, mng_get_uint16 (pRawdata), + mng_get_uint16 (pRawdata+2), + *(pRawdata+4), + mng_get_int32 (pRawdata+5), + mng_get_int32 (pRawdata+9)); + +/* if (!iRetcode) + iRetcode = mng_process_display_move (pData, + mng_get_uint16 (pRawdata), + mng_get_uint16 (pRawdata+2), + *(pRawdata+4), + mng_get_int32 (pRawdata+5), + mng_get_int32 (pRawdata+9)); */ + + if (iRetcode) /* on error bail out */ + return iRetcode; + + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_movep)*ppChunk)->iFirstid = mng_get_uint16 (pRawdata); + ((mng_movep)*ppChunk)->iLastid = mng_get_uint16 (pRawdata+2); + ((mng_movep)*ppChunk)->iMovetype = *(pRawdata+4); + ((mng_movep)*ppChunk)->iMovex = mng_get_int32 (pRawdata+5); + ((mng_movep)*ppChunk)->iMovey = mng_get_int32 (pRawdata+9); + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_MOVE, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_CLIP +READ_CHUNK (mng_read_clip) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_CLIP, MNG_LC_START); +#endif + + if (!pData->bHasMHDR) /* sequence checks */ + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (iRawlen != 21) /* check the length */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + +#ifdef MNG_SUPPORT_DISPLAY + { + mng_retcode iRetcode; + /* create a CLIP animation object */ + iRetcode = mng_create_ani_clip (pData, mng_get_uint16 (pRawdata), + mng_get_uint16 (pRawdata+2), + *(pRawdata+4), + mng_get_int32 (pRawdata+5), + mng_get_int32 (pRawdata+9), + mng_get_int32 (pRawdata+13), + mng_get_int32 (pRawdata+17)); + +/* if (!iRetcode) + iRetcode = mng_process_display_clip (pData, + mng_get_uint16 (pRawdata), + mng_get_uint16 (pRawdata+2), + *(pRawdata+4), + mng_get_int32 (pRawdata+5), + mng_get_int32 (pRawdata+9), + mng_get_int32 (pRawdata+13), + mng_get_int32 (pRawdata+17)); */ + + if (iRetcode) /* on error bail out */ + return iRetcode; + + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_clipp)*ppChunk)->iFirstid = mng_get_uint16 (pRawdata); + ((mng_clipp)*ppChunk)->iLastid = mng_get_uint16 (pRawdata+2); + ((mng_clipp)*ppChunk)->iCliptype = *(pRawdata+4); + ((mng_clipp)*ppChunk)->iClipl = mng_get_int32 (pRawdata+5); + ((mng_clipp)*ppChunk)->iClipr = mng_get_int32 (pRawdata+9); + ((mng_clipp)*ppChunk)->iClipt = mng_get_int32 (pRawdata+13); + ((mng_clipp)*ppChunk)->iClipb = mng_get_int32 (pRawdata+17); + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_CLIP, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_SHOW +READ_CHUNK (mng_read_show) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_SHOW, MNG_LC_START); +#endif + + if (!pData->bHasMHDR) /* sequence checks */ + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + /* check the length */ + if ((iRawlen != 0) && (iRawlen != 2) && (iRawlen != 4) && (iRawlen != 5)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + +#ifdef MNG_SUPPORT_DISPLAY + { + mng_retcode iRetcode; + + if (iRawlen) /* determine parameters if any */ + { + pData->iSHOWfromid = mng_get_uint16 (pRawdata); + + if (iRawlen > 2) + pData->iSHOWtoid = mng_get_uint16 (pRawdata+2); + else + pData->iSHOWtoid = pData->iSHOWfromid; + + if (iRawlen > 4) + pData->iSHOWmode = *(pRawdata+4); + else + pData->iSHOWmode = 0; + } + else /* use defaults then */ + { + pData->iSHOWmode = 2; + pData->iSHOWfromid = 1; + pData->iSHOWtoid = 65535; + } + /* create a SHOW animation object */ + iRetcode = mng_create_ani_show (pData, pData->iSHOWfromid, + pData->iSHOWtoid, pData->iSHOWmode); + + if (!iRetcode) + iRetcode = mng_process_display_show (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_showp)*ppChunk)->bEmpty = (mng_bool)(iRawlen == 0); + + if (iRawlen) + { + ((mng_showp)*ppChunk)->iFirstid = mng_get_uint16 (pRawdata); + + if (iRawlen > 2) + ((mng_showp)*ppChunk)->iLastid = mng_get_uint16 (pRawdata+2); + else + ((mng_showp)*ppChunk)->iLastid = ((mng_showp)*ppChunk)->iFirstid; + + if (iRawlen > 4) + ((mng_showp)*ppChunk)->iMode = *(pRawdata+4); + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_SHOW, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_TERM +READ_CHUNK (mng_read_term) +{ + mng_uint8 iTermaction; + mng_uint8 iIteraction = 0; + mng_uint32 iDelay = 0; + mng_uint32 iItermax = 0; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_TERM, MNG_LC_START); +#endif + + if (!pData->bHasMHDR) /* sequence checks */ + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + /* should be behind MHDR or SAVE !! */ + if ((!pData->bHasSAVE) && (pData->iChunkseq > 2)) + { + pData->bMisplacedTERM = MNG_TRUE; /* indicate we found a misplaced TERM */ + /* and send a warning signal!!! */ + MNG_WARNING (pData, MNG_SEQUENCEERROR); + } + + if (pData->bHasLOOP) /* no way, jose! */ + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (pData->bHasTERM) /* only 1 allowed! */ + MNG_ERROR (pData, MNG_MULTIPLEERROR); + /* check the length */ + if ((iRawlen != 1) && (iRawlen != 10)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + pData->bHasTERM = MNG_TRUE; + + iTermaction = *pRawdata; /* get the fields */ + + if (iRawlen > 1) + { + iIteraction = *(pRawdata+1); + iDelay = mng_get_uint32 (pRawdata+2); + iItermax = mng_get_uint32 (pRawdata+6); + } + + if (pData->fProcessterm) /* inform the app ? */ + if (!pData->fProcessterm (((mng_handle)pData), iTermaction, iIteraction, + iDelay, iItermax)) + MNG_ERROR (pData, MNG_APPMISCERROR); + +#ifdef MNG_SUPPORT_DISPLAY + { /* create the TERM ani-object */ + mng_retcode iRetcode = mng_create_ani_term (pData, iTermaction, iIteraction, + iDelay, iItermax); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* save for future reference */ + pData->pTermaniobj = pData->pLastaniobj; + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_termp)*ppChunk)->iTermaction = iTermaction; + ((mng_termp)*ppChunk)->iIteraction = iIteraction; + ((mng_termp)*ppChunk)->iDelay = iDelay; + ((mng_termp)*ppChunk)->iItermax = iItermax; + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_TERM, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_SAVE +READ_CHUNK (mng_read_save) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_SAVE, MNG_LC_START); +#endif + /* sequence checks */ + if ((!pData->bHasMHDR) || (pData->bHasSAVE)) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + pData->bHasSAVE = MNG_TRUE; + + if (pData->fProcesssave) /* inform the application ? */ + { + mng_bool bOke = pData->fProcesssave ((mng_handle)pData); + + if (!bOke) + MNG_ERROR (pData, MNG_APPMISCERROR); + } + +#ifdef MNG_SUPPORT_DISPLAY + { + mng_retcode iRetcode; + + + /* TODO: something with the parameters */ + + + /* create a SAVE animation object */ + iRetcode = mng_create_ani_save (pData); + + if (!iRetcode) + iRetcode = mng_process_display_save (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_savep)*ppChunk)->bEmpty = (mng_bool)(iRawlen == 0); + + if (iRawlen) /* not empty ? */ + { + mng_uint8 iOtype = *pRawdata; + mng_uint8 iEtype; + mng_uint32 iCount = 0; + mng_uint8p pTemp; + mng_uint8p pNull; + mng_uint32 iLen; + mng_uint32 iOffset[2]; + mng_uint32 iStarttime[2]; + mng_uint32 iFramenr; + mng_uint32 iLayernr; + mng_uint32 iX; + mng_save_entryp pEntry = MNG_NULL; + mng_uint32 iNamesize; + + if ((iOtype != 4) && (iOtype != 8)) + MNG_ERROR (pData, MNG_INVOFFSETSIZE); + + ((mng_savep)*ppChunk)->iOffsettype = iOtype; + + for (iX = 0; iX < 2; iX++) /* do this twice to get the count first ! */ + { + pTemp = pRawdata + 1; + iLen = iRawlen - 1; + + if (iX) /* second run ? */ + { + MNG_ALLOC (pData, pEntry, (iCount * sizeof (mng_save_entry))); + + ((mng_savep)*ppChunk)->iCount = iCount; + ((mng_savep)*ppChunk)->pEntries = pEntry; + } + + while (iLen) /* anything left ? */ + { + iEtype = *pTemp; /* entrytype */ + + if ((iEtype != 0) && (iEtype != 1) && (iEtype != 2) && (iEtype != 3)) + MNG_ERROR (pData, MNG_INVENTRYTYPE); + + pTemp++; + + if (iEtype > 1) + { + iOffset [0] = 0; + iOffset [1] = 0; + iStarttime [0] = 0; + iStarttime [1] = 0; + iLayernr = 0; + iFramenr = 0; + } + else + { + if (iOtype == 4) + { + iOffset [0] = 0; + iOffset [1] = mng_get_uint32 (pTemp); + + pTemp += 4; + } + else + { + iOffset [0] = mng_get_uint32 (pTemp); + iOffset [1] = mng_get_uint32 (pTemp+4); + + pTemp += 8; + } + + if (iEtype > 0) + { + iStarttime [0] = 0; + iStarttime [1] = 0; + iLayernr = 0; + iFramenr = 0; + } + else + { + if (iOtype == 4) + { + iStarttime [0] = 0; + iStarttime [1] = mng_get_uint32 (pTemp+0); + iLayernr = mng_get_uint32 (pTemp+4); + iFramenr = mng_get_uint32 (pTemp+8); + + pTemp += 12; + } + else + { + iStarttime [0] = mng_get_uint32 (pTemp+0); + iStarttime [1] = mng_get_uint32 (pTemp+4); + iLayernr = mng_get_uint32 (pTemp+8); + iFramenr = mng_get_uint32 (pTemp+12); + + pTemp += 16; + } + } + } + + pNull = find_null (pTemp); /* get the name length */ + + if ((pNull - pRawdata) > (mng_int32)iRawlen) + { + iNamesize = iLen; /* no null found; so end of SAVE */ + iLen = 0; + } + else + { + iNamesize = pNull - pTemp; /* should be another entry */ + iLen -= iNamesize; + + if (!iLen) /* must not end with a null ! */ + MNG_ERROR (pData, MNG_ENDWITHNULL); + } + + if (!pEntry) + { + iCount++; + } + else + { + pEntry->iEntrytype = iEtype; + pEntry->iOffset [0] = iOffset [0]; + pEntry->iOffset [1] = iOffset [1]; + pEntry->iStarttime [0] = iStarttime [0]; + pEntry->iStarttime [1] = iStarttime [1]; + pEntry->iLayernr = iLayernr; + pEntry->iFramenr = iFramenr; + pEntry->iNamesize = iNamesize; + + if (iNamesize) + { + MNG_ALLOC (pData, pEntry->zName, iNamesize+1); + MNG_COPY (pEntry->zName, pTemp, iNamesize); + } + + pEntry++; + } + + pTemp += iNamesize; + } + } + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_SAVE, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_SEEK +READ_CHUNK (mng_read_seek) +{ + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_SEEK, MNG_LC_START); +#endif + /* sequence checks */ + if ((!pData->bHasMHDR) || (!pData->bHasSAVE)) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_SUPPORT_DISPLAY + /* create a SEEK animation object */ + iRetcode = mng_create_ani_seek (pData, iRawlen, (mng_pchar)pRawdata); + + if (iRetcode) /* on error bail out */ + return iRetcode; + +#endif /* MNG_SUPPORT_DISPLAY */ + + if (pData->fProcessseek) /* inform the app ? */ + { + mng_bool bOke; + mng_pchar zName; + + MNG_ALLOC (pData, zName, iRawlen + 1); + + if (iRawlen) + MNG_COPY (zName, pRawdata, iRawlen); + + bOke = pData->fProcessseek ((mng_handle)pData, zName); + + MNG_FREEX (pData, zName, iRawlen + 1); + + if (!bOke) + MNG_ERROR (pData, MNG_APPMISCERROR); + } + +#ifdef MNG_SUPPORT_DISPLAY + /* do display processing of the SEEK */ + iRetcode = mng_process_display_seek (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_seekp)*ppChunk)->iNamesize = iRawlen; + + if (iRawlen) + { + MNG_ALLOC (pData, ((mng_seekp)*ppChunk)->zName, iRawlen+1); + MNG_COPY (((mng_seekp)*ppChunk)->zName, pRawdata, iRawlen); + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_SEEK, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_eXPI +READ_CHUNK (mng_read_expi) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_EXPI, MNG_LC_START); +#endif + + if (!pData->bHasMHDR) /* sequence checks */ + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (iRawlen < 3) /* check the length */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + +#ifdef MNG_SUPPORT_DISPLAY + { + + + /* TODO: something !!! */ + + + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_expip)*ppChunk)->iSnapshotid = mng_get_uint16 (pRawdata); + ((mng_expip)*ppChunk)->iNamesize = iRawlen - 2; + + if (((mng_expip)*ppChunk)->iNamesize) + { + MNG_ALLOC (pData, ((mng_expip)*ppChunk)->zName, + ((mng_expip)*ppChunk)->iNamesize + 1); + MNG_COPY (((mng_expip)*ppChunk)->zName, pRawdata+2, + ((mng_expip)*ppChunk)->iNamesize); + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_EXPI, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_fPRI +READ_CHUNK (mng_read_fpri) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_FPRI, MNG_LC_START); +#endif + + if (!pData->bHasMHDR) /* sequence checks */ + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (iRawlen != 2) /* must be two bytes long */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + +#ifdef MNG_SUPPORT_DISPLAY + { + + + /* TODO: something !!! */ + + + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_fprip)*ppChunk)->iDeltatype = *pRawdata; + ((mng_fprip)*ppChunk)->iPriority = *(pRawdata+1); + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_FPRI, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_nEED +MNG_LOCAL mng_bool CheckKeyword (mng_datap pData, + mng_uint8p pKeyword) +{ + mng_chunkid handled_chunks [] = + { + MNG_UINT_BACK, /* keep it sorted !!!! */ + MNG_UINT_BASI, + MNG_UINT_CLIP, + MNG_UINT_CLON, +#ifndef MNG_NO_DELTA_PNG +/* TODO: MNG_UINT_DBYK, */ +#endif + MNG_UINT_DEFI, +#ifndef MNG_NO_DELTA_PNG + MNG_UINT_DHDR, +#endif + MNG_UINT_DISC, +#ifndef MNG_NO_DELTA_PNG +/* TODO: MNG_UINT_DROP, */ +#endif + MNG_UINT_ENDL, + MNG_UINT_FRAM, + MNG_UINT_IDAT, + MNG_UINT_IEND, + MNG_UINT_IHDR, +#ifndef MNG_NO_DELTA_PNG +#ifdef MNG_INCLUDE_JNG + MNG_UINT_IJNG, +#endif + MNG_UINT_IPNG, +#endif +#ifdef MNG_INCLUDE_JNG + MNG_UINT_JDAA, + MNG_UINT_JDAT, + MNG_UINT_JHDR, +/* TODO: MNG_UINT_JSEP, */ + MNG_UINT_JdAA, +#endif + MNG_UINT_LOOP, + MNG_UINT_MAGN, + MNG_UINT_MEND, + MNG_UINT_MHDR, + MNG_UINT_MOVE, +/* TODO: MNG_UINT_ORDR, */ + MNG_UINT_PAST, + MNG_UINT_PLTE, +#ifndef MNG_NO_DELTA_PNG + MNG_UINT_PPLT, + MNG_UINT_PROM, +#endif + MNG_UINT_SAVE, + MNG_UINT_SEEK, + MNG_UINT_SHOW, + MNG_UINT_TERM, +#ifdef MNG_INCLUDE_ANG_PROPOSAL + MNG_UINT_adAT, + MNG_UINT_ahDR, +#endif + MNG_UINT_bKGD, + MNG_UINT_cHRM, +/* TODO: MNG_UINT_eXPI, */ + MNG_UINT_evNT, +/* TODO: MNG_UINT_fPRI, */ + MNG_UINT_gAMA, +/* TODO: MNG_UINT_hIST, */ + MNG_UINT_iCCP, + MNG_UINT_iTXt, +#ifdef MNG_INCLUDE_MPNG_PROPOSAL + MNG_UINT_mpNG, +#endif + MNG_UINT_nEED, +/* TODO: MNG_UINT_oFFs, */ +/* TODO: MNG_UINT_pCAL, */ +/* TODO: MNG_UINT_pHYg, */ +/* TODO: MNG_UINT_pHYs, */ +/* TODO: MNG_UINT_sBIT, */ +/* TODO: MNG_UINT_sCAL, */ +/* TODO: MNG_UINT_sPLT, */ + MNG_UINT_sRGB, + MNG_UINT_tEXt, + MNG_UINT_tIME, + MNG_UINT_tRNS, + MNG_UINT_zTXt, + }; + + mng_bool bOke = MNG_FALSE; + + if (pData->fProcessneed) /* does the app handle it ? */ + bOke = pData->fProcessneed ((mng_handle)pData, (mng_pchar)pKeyword); + + if (!bOke) + { /* find the keyword length */ + mng_uint8p pNull = find_null (pKeyword); + + if (pNull - pKeyword == 4) /* test a chunk ? */ + { /* get the chunk-id */ + mng_chunkid iChunkid = (*pKeyword << 24) + (*(pKeyword+1) << 16) + + (*(pKeyword+2) << 8) + (*(pKeyword+3) ); + /* binary search variables */ + mng_int32 iTop, iLower, iUpper, iMiddle; + /* determine max index of table */ + iTop = (sizeof (handled_chunks) / sizeof (handled_chunks [0])) - 1; + + /* binary search; with 52 chunks, worst-case is 7 comparisons */ + iLower = 0; + iMiddle = iTop >> 1; + iUpper = iTop; + + do /* the binary search itself */ + { + if (handled_chunks [iMiddle] < iChunkid) + iLower = iMiddle + 1; + else if (handled_chunks [iMiddle] > iChunkid) + iUpper = iMiddle - 1; + else + { + bOke = MNG_TRUE; + break; + } + + iMiddle = (iLower + iUpper) >> 1; + } + while (iLower <= iUpper); + } + /* test draft ? */ + if ((!bOke) && (pNull - pKeyword == 8) && + (*pKeyword == 'd') && (*(pKeyword+1) == 'r') && + (*(pKeyword+2) == 'a') && (*(pKeyword+3) == 'f') && + (*(pKeyword+4) == 't') && (*(pKeyword+5) == ' ')) + { + mng_uint32 iDraft; + + iDraft = (*(pKeyword+6) - '0') * 10 + (*(pKeyword+7) - '0'); + bOke = (mng_bool)(iDraft <= MNG_MNG_DRAFT); + } + /* test MNG 1.0/1.1 ? */ + if ((!bOke) && (pNull - pKeyword == 7) && + (*pKeyword == 'M') && (*(pKeyword+1) == 'N') && + (*(pKeyword+2) == 'G') && (*(pKeyword+3) == '-') && + (*(pKeyword+4) == '1') && (*(pKeyword+5) == '.') && + ((*(pKeyword+6) == '0') || (*(pKeyword+6) == '1'))) + bOke = MNG_TRUE; + /* test CACHEOFF ? */ + if ((!bOke) && (pNull - pKeyword == 8) && + (*pKeyword == 'C') && (*(pKeyword+1) == 'A') && + (*(pKeyword+2) == 'C') && (*(pKeyword+3) == 'H') && + (*(pKeyword+4) == 'E') && (*(pKeyword+5) == 'O') && + (*(pKeyword+6) == 'F') && (*(pKeyword+7) == 'F')) + { + if (!pData->pFirstaniobj) /* only if caching hasn't started yet ! */ + { + bOke = MNG_TRUE; + pData->bCacheplayback = MNG_FALSE; + pData->bStorechunks = MNG_FALSE; + } + } + } + + return bOke; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_nEED +READ_CHUNK (mng_read_need) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_NEED, MNG_LC_START); +#endif + + if (!pData->bHasMHDR) /* sequence checks */ + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (iRawlen < 1) /* check the length */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + { /* let's check it */ + mng_bool bOke = MNG_TRUE; + mng_pchar zKeywords; + mng_uint8p pNull, pTemp; + + MNG_ALLOC (pData, zKeywords, iRawlen + 1); + + if (iRawlen) + MNG_COPY (zKeywords, pRawdata, iRawlen); + + pTemp = (mng_uint8p)zKeywords; + pNull = find_null (pTemp); + + while ((bOke) && (pNull < (mng_uint8p)zKeywords + iRawlen)) + { + bOke = CheckKeyword (pData, pTemp); + pTemp = pNull + 1; + pNull = find_null (pTemp); + } + + if (bOke) + bOke = CheckKeyword (pData, pTemp); + + MNG_FREEX (pData, zKeywords, iRawlen + 1); + + if (!bOke) + MNG_ERROR (pData, MNG_UNSUPPORTEDNEED); + } + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_needp)*ppChunk)->iKeywordssize = iRawlen; + + if (iRawlen) + { + MNG_ALLOC (pData, ((mng_needp)*ppChunk)->zKeywords, iRawlen+1); + MNG_COPY (((mng_needp)*ppChunk)->zKeywords, pRawdata, iRawlen); + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_NEED, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_pHYg +READ_CHUNK (mng_read_phyg) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_PHYG, MNG_LC_START); +#endif + + if (!pData->bHasMHDR) /* sequence checks */ + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + /* it's 9 bytes or empty; no more, no less! */ + if ((iRawlen != 9) && (iRawlen != 0)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + +#ifdef MNG_SUPPORT_DISPLAY + { + + + /* TODO: something !!! */ + + + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_phygp)*ppChunk)->bEmpty = (mng_bool)(iRawlen == 0); + + if (iRawlen) + { + ((mng_phygp)*ppChunk)->iSizex = mng_get_uint32 (pRawdata); + ((mng_phygp)*ppChunk)->iSizey = mng_get_uint32 (pRawdata+4); + ((mng_phygp)*ppChunk)->iUnit = *(pRawdata+8); + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_PHYG, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_INCLUDE_JNG +READ_CHUNK (mng_read_jhdr) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_JHDR, MNG_LC_START); +#endif + /* sequence checks */ + if ((pData->eSigtype != mng_it_jng) && (pData->eSigtype != mng_it_mng)) + MNG_ERROR (pData, MNG_CHUNKNOTALLOWED); + + if ((pData->eSigtype == mng_it_jng) && (pData->iChunkseq > 1)) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (iRawlen != 16) /* length oke ? */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + /* inside a JHDR-IEND block now */ + pData->bHasJHDR = MNG_TRUE; + /* and store interesting fields */ + pData->iDatawidth = mng_get_uint32 (pRawdata); + pData->iDataheight = mng_get_uint32 (pRawdata+4); + pData->iJHDRcolortype = *(pRawdata+8); + pData->iJHDRimgbitdepth = *(pRawdata+9); + pData->iJHDRimgcompression = *(pRawdata+10); + pData->iJHDRimginterlace = *(pRawdata+11); + pData->iJHDRalphabitdepth = *(pRawdata+12); + pData->iJHDRalphacompression = *(pRawdata+13); + pData->iJHDRalphafilter = *(pRawdata+14); + pData->iJHDRalphainterlace = *(pRawdata+15); + + +#if defined(MNG_NO_1_2_4BIT_SUPPORT) || defined(MNG_NO_16BIT_SUPPORT) + pData->iPNGmult = 1; + pData->iPNGdepth = pData->iJHDRalphabitdepth; +#endif + +#ifdef MNG_NO_1_2_4BIT_SUPPORT + if (pData->iJHDRalphabitdepth < 8) + pData->iJHDRalphabitdepth = 8; +#endif + +#ifdef MNG_NO_16BIT_SUPPORT + if (pData->iJHDRalphabitdepth > 8) + { + pData->iPNGmult = 2; + pData->iJHDRalphabitdepth = 8; + } +#endif + /* parameter validity checks */ + if ((pData->iJHDRcolortype != MNG_COLORTYPE_JPEGGRAY ) && + (pData->iJHDRcolortype != MNG_COLORTYPE_JPEGCOLOR ) && + (pData->iJHDRcolortype != MNG_COLORTYPE_JPEGGRAYA ) && + (pData->iJHDRcolortype != MNG_COLORTYPE_JPEGCOLORA) ) + MNG_ERROR (pData, MNG_INVALIDCOLORTYPE); + + if ((pData->iJHDRimgbitdepth != MNG_BITDEPTH_JPEG8 ) && + (pData->iJHDRimgbitdepth != MNG_BITDEPTH_JPEG12 ) && + (pData->iJHDRimgbitdepth != MNG_BITDEPTH_JPEG8AND12) ) + MNG_ERROR (pData, MNG_INVALIDBITDEPTH); + + if (pData->iJHDRimgcompression != MNG_COMPRESSION_BASELINEJPEG) + MNG_ERROR (pData, MNG_INVALIDCOMPRESS); + + if ((pData->iJHDRimginterlace != MNG_INTERLACE_SEQUENTIAL ) && + (pData->iJHDRimginterlace != MNG_INTERLACE_PROGRESSIVE) ) + MNG_ERROR (pData, MNG_INVALIDINTERLACE); + + if ((pData->iJHDRcolortype == MNG_COLORTYPE_JPEGGRAYA ) || + (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGCOLORA) ) + { + if ((pData->iJHDRalphabitdepth != MNG_BITDEPTH_8 ) +#ifndef MNG_NO_1_2_4BIT_SUPPORT + && (pData->iJHDRalphabitdepth != MNG_BITDEPTH_1 ) && + (pData->iJHDRalphabitdepth != MNG_BITDEPTH_2 ) && + (pData->iJHDRalphabitdepth != MNG_BITDEPTH_4 ) +#endif +#ifndef MNG_NO_16BIT_SUPPORT + && (pData->iJHDRalphabitdepth != MNG_BITDEPTH_16) +#endif + ) + MNG_ERROR (pData, MNG_INVALIDBITDEPTH); + + if ((pData->iJHDRalphacompression != MNG_COMPRESSION_DEFLATE ) && + (pData->iJHDRalphacompression != MNG_COMPRESSION_BASELINEJPEG) ) + MNG_ERROR (pData, MNG_INVALIDCOMPRESS); + + if ((pData->iJHDRalphacompression == MNG_COMPRESSION_BASELINEJPEG) && + (pData->iJHDRalphabitdepth != MNG_BITDEPTH_8 ) ) + MNG_ERROR (pData, MNG_INVALIDBITDEPTH); + +#if defined(FILTER192) || defined(FILTER193) + if ((pData->iJHDRalphafilter != MNG_FILTER_ADAPTIVE ) && +#if defined(FILTER192) && defined(FILTER193) + (pData->iJHDRalphafilter != MNG_FILTER_DIFFERING) && + (pData->iJHDRalphafilter != MNG_FILTER_NOFILTER ) ) +#else +#ifdef FILTER192 + (pData->iJHDRalphafilter != MNG_FILTER_DIFFERING) ) +#else + (pData->iJHDRalphafilter != MNG_FILTER_NOFILTER ) ) +#endif +#endif + MNG_ERROR (pData, MNG_INVALIDFILTER); +#else + if (pData->iJHDRalphafilter) + MNG_ERROR (pData, MNG_INVALIDFILTER); +#endif + + if ((pData->iJHDRalphainterlace != MNG_INTERLACE_NONE ) && + (pData->iJHDRalphainterlace != MNG_INTERLACE_ADAM7) ) + MNG_ERROR (pData, MNG_INVALIDINTERLACE); + + } + else + { + if (pData->iJHDRalphabitdepth) + MNG_ERROR (pData, MNG_INVALIDBITDEPTH); + + if (pData->iJHDRalphacompression) + MNG_ERROR (pData, MNG_INVALIDCOMPRESS); + + if (pData->iJHDRalphafilter) + MNG_ERROR (pData, MNG_INVALIDFILTER); + + if (pData->iJHDRalphainterlace) + MNG_ERROR (pData, MNG_INVALIDINTERLACE); + + } + + if (!pData->bHasheader) /* first chunk ? */ + { + pData->bHasheader = MNG_TRUE; /* we've got a header */ + pData->eImagetype = mng_it_jng; /* then this must be a JNG */ + pData->iWidth = mng_get_uint32 (pRawdata); + pData->iHeight = mng_get_uint32 (pRawdata+4); + /* predict alpha-depth ! */ + if ((pData->iJHDRcolortype == MNG_COLORTYPE_JPEGGRAYA ) || + (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGCOLORA) ) + pData->iAlphadepth = pData->iJHDRalphabitdepth; + else + pData->iAlphadepth = 0; + /* fits on maximum canvas ? */ + if ((pData->iWidth > pData->iMaxwidth) || (pData->iHeight > pData->iMaxheight)) + MNG_WARNING (pData, MNG_IMAGETOOLARGE); + + if (pData->fProcessheader) /* inform the app ? */ + if (!pData->fProcessheader (((mng_handle)pData), pData->iWidth, pData->iHeight)) + MNG_ERROR (pData, MNG_APPMISCERROR); + + } + + pData->iColortype = 0; /* fake grayscale for other routines */ + pData->iImagelevel++; /* one level deeper */ + +#ifdef MNG_SUPPORT_DISPLAY + { + mng_retcode iRetcode = mng_process_display_jhdr (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_jhdrp)*ppChunk)->iWidth = mng_get_uint32 (pRawdata); + ((mng_jhdrp)*ppChunk)->iHeight = mng_get_uint32 (pRawdata+4); + ((mng_jhdrp)*ppChunk)->iColortype = *(pRawdata+8); + ((mng_jhdrp)*ppChunk)->iImagesampledepth = *(pRawdata+9); + ((mng_jhdrp)*ppChunk)->iImagecompression = *(pRawdata+10); + ((mng_jhdrp)*ppChunk)->iImageinterlace = *(pRawdata+11); + ((mng_jhdrp)*ppChunk)->iAlphasampledepth = *(pRawdata+12); +#ifdef MNG_NO_16BIT_SUPPORT + if (*(pRawdata+12) > 8) + ((mng_jhdrp)*ppChunk)->iAlphasampledepth = 8; +#endif + ((mng_jhdrp)*ppChunk)->iAlphacompression = *(pRawdata+13); + ((mng_jhdrp)*ppChunk)->iAlphafilter = *(pRawdata+14); + ((mng_jhdrp)*ppChunk)->iAlphainterlace = *(pRawdata+15); + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_JHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#else +#define read_jhdr 0 +#endif /* MNG_INCLUDE_JNG */ +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_INCLUDE_JNG +READ_CHUNK (mng_read_jdaa) +{ +#if defined(MNG_SUPPORT_DISPLAY) || defined(MNG_STORE_CHUNKS) + volatile mng_retcode iRetcode; + + iRetcode=MNG_NOERROR; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_JDAA, MNG_LC_START); +#endif + /* sequence checks */ + if ((!pData->bHasJHDR) && (!pData->bHasDHDR)) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (pData->bHasJSEP) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (pData->iJHDRalphacompression != MNG_COMPRESSION_BASELINEJPEG) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (iRawlen == 0) /* can never be empty */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + pData->bHasJDAA = MNG_TRUE; /* got some JDAA now, don't we */ + +#ifdef MNG_SUPPORT_DISPLAY + iRetcode = mng_process_display_jdaa (pData, iRawlen, pRawdata); + + if (iRetcode) /* on error bail out */ + return iRetcode; +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_jdaap)*ppChunk)->bEmpty = (mng_bool)(iRawlen == 0); + ((mng_jdaap)*ppChunk)->iDatasize = iRawlen; + + if (iRawlen != 0) /* is there any data ? */ + { + MNG_ALLOC (pData, ((mng_jdaap)*ppChunk)->pData, iRawlen); + MNG_COPY (((mng_jdaap)*ppChunk)->pData, pRawdata, iRawlen); + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_JDAA, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#else +#define read_jdaa 0 +#endif /* MNG_INCLUDE_JNG */ +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_INCLUDE_JNG +READ_CHUNK (mng_read_jdat) +{ +#if defined(MNG_SUPPORT_DISPLAY) || defined(MNG_STORE_CHUNKS) + volatile mng_retcode iRetcode; + + iRetcode=MNG_NOERROR; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_JDAT, MNG_LC_START); +#endif + /* sequence checks */ + if ((!pData->bHasJHDR) && (!pData->bHasDHDR)) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (iRawlen == 0) /* can never be empty */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + pData->bHasJDAT = MNG_TRUE; /* got some JDAT now, don't we */ + +#ifdef MNG_SUPPORT_DISPLAY + iRetcode = mng_process_display_jdat (pData, iRawlen, pRawdata); + + if (iRetcode) /* on error bail out */ + return iRetcode; +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_jdatp)*ppChunk)->bEmpty = (mng_bool)(iRawlen == 0); + ((mng_jdatp)*ppChunk)->iDatasize = iRawlen; + + if (iRawlen != 0) /* is there any data ? */ + { + MNG_ALLOC (pData, ((mng_jdatp)*ppChunk)->pData, iRawlen); + MNG_COPY (((mng_jdatp)*ppChunk)->pData, pRawdata, iRawlen); + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_JDAT, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#else +#define read_jdat 0 +#endif /* MNG_INCLUDE_JNG */ +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_INCLUDE_JNG +READ_CHUNK (mng_read_jsep) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_JSEP, MNG_LC_START); +#endif + + if (!pData->bHasJHDR) /* sequence checks */ + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (iRawlen != 0) /* must be empty ! */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + pData->bHasJSEP = MNG_TRUE; /* indicate we've had the 8-/12-bit separator */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_JSEP, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#else +#define read_jsep 0 +#endif /* MNG_INCLUDE_JNG */ +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_NO_DELTA_PNG +READ_CHUNK (mng_read_dhdr) +{ + mng_uint8 iImagetype, iDeltatype; +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_DHDR, MNG_LC_START); +#endif + + if (!pData->bHasMHDR) /* sequence checks */ + MNG_ERROR (pData, MNG_SEQUENCEERROR); + +#ifdef MNG_INCLUDE_JNG + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((pData->bHasIHDR) || (pData->bHasBASI) || (pData->bHasDHDR)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + /* check for valid length */ + if ((iRawlen != 4) && (iRawlen != 12) && (iRawlen != 20)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + iImagetype = *(pRawdata+2); /* check fields for validity */ + iDeltatype = *(pRawdata+3); + + if (iImagetype > MNG_IMAGETYPE_JNG) + MNG_ERROR (pData, MNG_INVIMAGETYPE); + + if (iDeltatype > MNG_DELTATYPE_NOCHANGE) + MNG_ERROR (pData, MNG_INVDELTATYPE); + + if ((iDeltatype == MNG_DELTATYPE_REPLACE) && (iRawlen > 12)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + if ((iDeltatype == MNG_DELTATYPE_NOCHANGE) && (iRawlen > 4)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + pData->bHasDHDR = MNG_TRUE; /* inside a DHDR-IEND block now */ + pData->iDeltatype = iDeltatype; + + pData->iImagelevel++; /* one level deeper */ + +#ifdef MNG_SUPPORT_DISPLAY + { + mng_uint16 iObjectid = mng_get_uint16 (pRawdata); + mng_uint32 iBlockwidth = 0; + mng_uint32 iBlockheight = 0; + mng_uint32 iBlockx = 0; + mng_uint32 iBlocky = 0; + mng_retcode iRetcode; + + if (iRawlen > 4) + { + iBlockwidth = mng_get_uint32 (pRawdata+4); + iBlockheight = mng_get_uint32 (pRawdata+8); + } + + if (iRawlen > 12) + { + iBlockx = mng_get_uint32 (pRawdata+12); + iBlocky = mng_get_uint32 (pRawdata+16); + } + + iRetcode = mng_create_ani_dhdr (pData, iObjectid, iImagetype, iDeltatype, + iBlockwidth, iBlockheight, iBlockx, iBlocky); + +/* if (!iRetcode) + iRetcode = mng_process_display_dhdr (pData, iObjectid, iImagetype, iDeltatype, + iBlockwidth, iBlockheight, iBlockx, iBlocky); */ + + if (iRetcode) /* on error bail out */ + return iRetcode; + + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_dhdrp)*ppChunk)->iObjectid = mng_get_uint16 (pRawdata); + ((mng_dhdrp)*ppChunk)->iImagetype = iImagetype; + ((mng_dhdrp)*ppChunk)->iDeltatype = iDeltatype; + + if (iRawlen > 4) + { + ((mng_dhdrp)*ppChunk)->iBlockwidth = mng_get_uint32 (pRawdata+4); + ((mng_dhdrp)*ppChunk)->iBlockheight = mng_get_uint32 (pRawdata+8); + } + + if (iRawlen > 12) + { + ((mng_dhdrp)*ppChunk)->iBlockx = mng_get_uint32 (pRawdata+12); + ((mng_dhdrp)*ppChunk)->iBlocky = mng_get_uint32 (pRawdata+16); + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_DHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_NO_DELTA_PNG +READ_CHUNK (mng_read_prom) +{ + mng_uint8 iColortype; + mng_uint8 iSampledepth; + mng_uint8 iFilltype; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_PROM, MNG_LC_START); +#endif + /* sequence checks */ + if ((!pData->bHasMHDR) || (!pData->bHasDHDR)) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (iRawlen != 3) /* gotta be exactly 3 bytes */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + iColortype = *pRawdata; /* check fields for validity */ + iSampledepth = *(pRawdata+1); + iFilltype = *(pRawdata+2); + + if ((iColortype != MNG_COLORTYPE_GRAY ) && + (iColortype != MNG_COLORTYPE_RGB ) && + (iColortype != MNG_COLORTYPE_INDEXED) && + (iColortype != MNG_COLORTYPE_GRAYA ) && + (iColortype != MNG_COLORTYPE_RGBA ) ) + MNG_ERROR (pData, MNG_INVALIDCOLORTYPE); + +#ifdef MNG_NO_16BIT_SUPPORT + if (iSampledepth == MNG_BITDEPTH_16 ) + iSampledepth = MNG_BITDEPTH_8; +#endif + + if ((iSampledepth != MNG_BITDEPTH_1 ) && + (iSampledepth != MNG_BITDEPTH_2 ) && + (iSampledepth != MNG_BITDEPTH_4 ) && + (iSampledepth != MNG_BITDEPTH_8 ) +#ifndef MNG_NO_16BIT_SUPPORT + && (iSampledepth != MNG_BITDEPTH_16) +#endif + ) + MNG_ERROR (pData, MNG_INVSAMPLEDEPTH); + + if ((iFilltype != MNG_FILLMETHOD_LEFTBITREPLICATE) && + (iFilltype != MNG_FILLMETHOD_ZEROFILL ) ) + MNG_ERROR (pData, MNG_INVFILLMETHOD); + +#ifdef MNG_SUPPORT_DISPLAY + { + mng_retcode iRetcode = mng_create_ani_prom (pData, iSampledepth, + iColortype, iFilltype); + +/* if (!iRetcode) + iRetcode = mng_process_display_prom (pData, iSampledepth, + iColortype, iFilltype); */ + + if (iRetcode) /* on error bail out */ + return iRetcode; + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_promp)*ppChunk)->iColortype = iColortype; + ((mng_promp)*ppChunk)->iSampledepth = iSampledepth; + ((mng_promp)*ppChunk)->iFilltype = iFilltype; + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_PROM, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_NO_DELTA_PNG +READ_CHUNK (mng_read_ipng) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_IPNG, MNG_LC_START); +#endif + /* sequence checks */ + if ((!pData->bHasMHDR) || (!pData->bHasDHDR)) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (iRawlen != 0) /* gotta be empty */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + +#ifdef MNG_SUPPORT_DISPLAY + { + mng_retcode iRetcode = mng_create_ani_ipng (pData); + + if (!iRetcode) + iRetcode = mng_process_display_ipng (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_IPNG, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_NO_DELTA_PNG +READ_CHUNK (mng_read_pplt) +{ + mng_uint8 iDeltatype; + mng_uint8p pTemp; + mng_uint32 iLen; + mng_uint8 iX, iM; + mng_uint32 iY; + mng_uint32 iMax; + mng_rgbpaltab aIndexentries; + mng_uint8arr aAlphaentries; + mng_uint8arr aUsedentries; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_PPLT, MNG_LC_START); +#endif + /* sequence checks */ + if ((!pData->bHasMHDR) && (!pData->bHasDHDR)) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (iRawlen < 1) /* must have at least 1 byte */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + iDeltatype = *pRawdata; + /* valid ? */ + if (iDeltatype > MNG_DELTATYPE_DELTARGBA) + MNG_ERROR (pData, MNG_INVDELTATYPE); + /* must be indexed color ! */ + if (pData->iColortype != MNG_COLORTYPE_INDEXED) + MNG_ERROR (pData, MNG_INVALIDCOLORTYPE); + + pTemp = pRawdata + 1; + iLen = iRawlen - 1; + iMax = 0; + + for (iY = 0; iY < 256; iY++) /* reset arrays */ + { + aIndexentries [iY].iRed = 0; + aIndexentries [iY].iGreen = 0; + aIndexentries [iY].iBlue = 0; + aAlphaentries [iY] = 255; + aUsedentries [iY] = 0; + } + + while (iLen) /* as long as there are entries left ... */ + { + mng_uint32 iDiff; + + if (iLen < 2) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + iX = *pTemp; /* get start and end index */ + iM = *(pTemp+1); + + if (iM < iX) + MNG_ERROR (pData, MNG_INVALIDINDEX); + + if ((mng_uint32)iM >= iMax) /* determine highest used index */ + iMax = (mng_uint32)iM + 1; + + pTemp += 2; + iLen -= 2; + iDiff = (iM - iX + 1); + if ((iDeltatype == MNG_DELTATYPE_REPLACERGB ) || + (iDeltatype == MNG_DELTATYPE_DELTARGB ) ) + iDiff = iDiff * 3; + else + if ((iDeltatype == MNG_DELTATYPE_REPLACERGBA) || + (iDeltatype == MNG_DELTATYPE_DELTARGBA ) ) + iDiff = iDiff * 4; + + if (iLen < iDiff) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + if ((iDeltatype == MNG_DELTATYPE_REPLACERGB ) || + (iDeltatype == MNG_DELTATYPE_DELTARGB ) ) + { + for (iY = (mng_uint32)iX; iY <= (mng_uint32)iM; iY++) + { + aIndexentries [iY].iRed = *pTemp; + aIndexentries [iY].iGreen = *(pTemp+1); + aIndexentries [iY].iBlue = *(pTemp+2); + aUsedentries [iY] = 1; + + pTemp += 3; + iLen -= 3; + } + } + else + if ((iDeltatype == MNG_DELTATYPE_REPLACEALPHA) || + (iDeltatype == MNG_DELTATYPE_DELTAALPHA ) ) + { + for (iY = (mng_uint32)iX; iY <= (mng_uint32)iM; iY++) + { + aAlphaentries [iY] = *pTemp; + aUsedentries [iY] = 1; + + pTemp++; + iLen--; + } + } + else + { + for (iY = (mng_uint32)iX; iY <= (mng_uint32)iM; iY++) + { + aIndexentries [iY].iRed = *pTemp; + aIndexentries [iY].iGreen = *(pTemp+1); + aIndexentries [iY].iBlue = *(pTemp+2); + aAlphaentries [iY] = *(pTemp+3); + aUsedentries [iY] = 1; + + pTemp += 4; + iLen -= 4; + } + } + } + + switch (pData->iBitdepth) /* check maximum allowed entries for bitdepth */ + { + case MNG_BITDEPTH_1 : { + if (iMax > 2) + MNG_ERROR (pData, MNG_INVALIDINDEX); + break; + } + case MNG_BITDEPTH_2 : { + if (iMax > 4) + MNG_ERROR (pData, MNG_INVALIDINDEX); + break; + } + case MNG_BITDEPTH_4 : { + if (iMax > 16) + MNG_ERROR (pData, MNG_INVALIDINDEX); + break; + } + } + +#ifdef MNG_SUPPORT_DISPLAY + { /* create animation object */ + mng_retcode iRetcode = mng_create_ani_pplt (pData, iDeltatype, iMax, + aIndexentries, aAlphaentries, + aUsedentries); + +/* if (!iRetcode) + iRetcode = mng_process_display_pplt (pData, iDeltatype, iMax, aIndexentries, + aAlphaentries, aUsedentries); */ + + if (iRetcode) /* on error bail out */ + return iRetcode; + + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_ppltp)*ppChunk)->iDeltatype = iDeltatype; + ((mng_ppltp)*ppChunk)->iCount = iMax; + + for (iY = 0; iY < 256; iY++) + { + ((mng_ppltp)*ppChunk)->aEntries [iY].iRed = aIndexentries [iY].iRed; + ((mng_ppltp)*ppChunk)->aEntries [iY].iGreen = aIndexentries [iY].iGreen; + ((mng_ppltp)*ppChunk)->aEntries [iY].iBlue = aIndexentries [iY].iBlue; + ((mng_ppltp)*ppChunk)->aEntries [iY].iAlpha = aAlphaentries [iY]; + ((mng_ppltp)*ppChunk)->aEntries [iY].bUsed = (mng_bool)(aUsedentries [iY]); + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_PPLT, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_NO_DELTA_PNG +#ifdef MNG_INCLUDE_JNG +READ_CHUNK (mng_read_ijng) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_IJNG, MNG_LC_START); +#endif + /* sequence checks */ + if ((!pData->bHasMHDR) || (!pData->bHasDHDR)) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (iRawlen != 0) /* gotta be empty */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + +#ifdef MNG_SUPPORT_DISPLAY + { + mng_retcode iRetcode = mng_create_ani_ijng (pData); + + if (!iRetcode) + iRetcode = mng_process_display_ijng (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_IJNG, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_NO_DELTA_PNG +READ_CHUNK (mng_read_drop) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_DROP, MNG_LC_START); +#endif + /* sequence checks */ + if ((!pData->bHasMHDR) || (!pData->bHasDHDR)) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + /* check length */ + if ((iRawlen < 4) || ((iRawlen % 4) != 0)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + +#ifdef MNG_SUPPORT_DISPLAY + { + + + /* TODO: something !!! */ + + + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_dropp)*ppChunk)->iCount = iRawlen / 4; + + if (iRawlen) + { + mng_uint32 iX; + mng_uint8p pTemp = pRawdata; + mng_uint32p pEntry; + + MNG_ALLOC (pData, pEntry, iRawlen); + + ((mng_dropp)*ppChunk)->pChunknames = (mng_ptr)pEntry; + + for (iX = 0; iX < iRawlen / 4; iX++) + { + *pEntry = mng_get_uint32 (pTemp); + + pTemp += 4; + pEntry++; + } + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_DROP, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_DBYK +READ_CHUNK (mng_read_dbyk) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_DBYK, MNG_LC_START); +#endif + /* sequence checks */ + if ((!pData->bHasMHDR) || (!pData->bHasDHDR)) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (iRawlen < 6) /* must be at least 6 long */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + +#ifdef MNG_SUPPORT_DISPLAY + { + + + /* TODO: something !!! */ + + + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_dbykp)*ppChunk)->iChunkname = mng_get_uint32 (pRawdata); + ((mng_dbykp)*ppChunk)->iPolarity = *(pRawdata+4); + ((mng_dbykp)*ppChunk)->iKeywordssize = iRawlen - 5; + + if (iRawlen > 5) + { + MNG_ALLOC (pData, ((mng_dbykp)*ppChunk)->zKeywords, iRawlen-4); + MNG_COPY (((mng_dbykp)*ppChunk)->zKeywords, pRawdata+5, iRawlen-5); + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_DBYK, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_ORDR +READ_CHUNK (mng_read_ordr) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_ORDR, MNG_LC_START); +#endif + /* sequence checks */ + if ((!pData->bHasMHDR) || (!pData->bHasDHDR)) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + /* check length */ + if ((iRawlen < 5) || ((iRawlen % 5) != 0)) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + +#ifdef MNG_SUPPORT_DISPLAY + { + + + /* TODO: something !!! */ + + + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_ordrp)*ppChunk)->iCount = iRawlen / 5; + + if (iRawlen) + { + mng_uint32 iX; + mng_ordr_entryp pEntry; + mng_uint8p pTemp = pRawdata; + + MNG_ALLOC (pData, pEntry, iRawlen); + + ((mng_ordrp)*ppChunk)->pEntries = pEntry; + + for (iX = 0; iX < iRawlen / 5; iX++) + { + pEntry->iChunkname = mng_get_uint32 (pTemp); + pEntry->iOrdertype = *(pTemp+4); + + pTemp += 5; + pEntry++; + } + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_ORDR, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_MAGN +READ_CHUNK (mng_read_magn) +{ + mng_uint16 iFirstid, iLastid; + mng_uint8 iMethodX, iMethodY; + mng_uint16 iMX, iMY, iML, iMR, iMT, iMB; + mng_bool bFaulty; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_MAGN, MNG_LC_START); +#endif + /* sequence checks */ +#ifdef MNG_SUPPORT_JNG + if ((!pData->bHasMHDR) || (pData->bHasIHDR) || (pData->bHasDHDR) || (pData->bHasJHDR)) +#else + if ((!pData->bHasMHDR) || (pData->bHasIHDR) || (pData->bHasDHDR)) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + /* check length */ + if (iRawlen > 20) + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + /* following is an ugly hack to allow faulty layout caused by previous + versions of libmng and MNGeye, which wrote MAGN with a 16-bit + MethodX/MethodY (as opposed to the proper 8-bit as defined in the spec!) */ + + if ((iRawlen == 6) || (iRawlen == 8) || (iRawlen == 10) || (iRawlen == 12) || + (iRawlen == 14) || (iRawlen == 16) || (iRawlen == 20)) + bFaulty = MNG_TRUE; /* these lengths are all wrong */ + else /* length 18 can be right or wrong !!! */ + if ((iRawlen == 18) && (mng_get_uint16 (pRawdata+4) <= 5) && + (mng_get_uint16 (pRawdata+6) < 256) && + (mng_get_uint16 (pRawdata+8) < 256) && + (mng_get_uint16 (pRawdata+10) < 256) && + (mng_get_uint16 (pRawdata+12) < 256) && + (mng_get_uint16 (pRawdata+14) < 256) && + (mng_get_uint16 (pRawdata+16) < 256)) + bFaulty = MNG_TRUE; /* this is very likely the wrong layout */ + else + bFaulty = MNG_FALSE; /* all other cases are handled as right */ + + if (bFaulty) /* wrong layout ? */ + { + if (iRawlen > 0) /* get the fields */ + iFirstid = mng_get_uint16 (pRawdata); + else + iFirstid = 0; + + if (iRawlen > 2) + iLastid = mng_get_uint16 (pRawdata+2); + else + iLastid = iFirstid; + + if (iRawlen > 4) + iMethodX = (mng_uint8)(mng_get_uint16 (pRawdata+4)); + else + iMethodX = 0; + + if (iRawlen > 6) + iMX = mng_get_uint16 (pRawdata+6); + else + iMX = 1; + + if (iRawlen > 8) + iMY = mng_get_uint16 (pRawdata+8); + else + iMY = iMX; + + if (iRawlen > 10) + iML = mng_get_uint16 (pRawdata+10); + else + iML = iMX; + + if (iRawlen > 12) + iMR = mng_get_uint16 (pRawdata+12); + else + iMR = iMX; + + if (iRawlen > 14) + iMT = mng_get_uint16 (pRawdata+14); + else + iMT = iMY; + + if (iRawlen > 16) + iMB = mng_get_uint16 (pRawdata+16); + else + iMB = iMY; + + if (iRawlen > 18) + iMethodY = (mng_uint8)(mng_get_uint16 (pRawdata+18)); + else + iMethodY = iMethodX; + } + else /* proper layout !!!! */ + { + if (iRawlen > 0) /* get the fields */ + iFirstid = mng_get_uint16 (pRawdata); + else + iFirstid = 0; + + if (iRawlen > 2) + iLastid = mng_get_uint16 (pRawdata+2); + else + iLastid = iFirstid; + + if (iRawlen > 4) + iMethodX = *(pRawdata+4); + else + iMethodX = 0; + + if (iRawlen > 5) + iMX = mng_get_uint16 (pRawdata+5); + else + iMX = 1; + + if (iRawlen > 7) + iMY = mng_get_uint16 (pRawdata+7); + else + iMY = iMX; + + if (iRawlen > 9) + iML = mng_get_uint16 (pRawdata+9); + else + iML = iMX; + + if (iRawlen > 11) + iMR = mng_get_uint16 (pRawdata+11); + else + iMR = iMX; + + if (iRawlen > 13) + iMT = mng_get_uint16 (pRawdata+13); + else + iMT = iMY; + + if (iRawlen > 15) + iMB = mng_get_uint16 (pRawdata+15); + else + iMB = iMY; + + if (iRawlen > 17) + iMethodY = *(pRawdata+17); + else + iMethodY = iMethodX; + } + /* check field validity */ + if ((iMethodX > 5) || (iMethodY > 5)) + MNG_ERROR (pData, MNG_INVALIDMETHOD); + +#ifdef MNG_SUPPORT_DISPLAY + { + mng_retcode iRetcode; + + iRetcode = mng_create_ani_magn (pData, iFirstid, iLastid, iMethodX, + iMX, iMY, iML, iMR, iMT, iMB, iMethodY); + +/* if (!iRetcode) + iRetcode = mng_process_display_magn (pData, iFirstid, iLastid, iMethodX, + iMX, iMY, iML, iMR, iMT, iMB, iMethodY); */ + + if (iRetcode) /* on error bail out */ + return iRetcode; + + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_magnp)*ppChunk)->iFirstid = iFirstid; + ((mng_magnp)*ppChunk)->iLastid = iLastid; + ((mng_magnp)*ppChunk)->iMethodX = iMethodX; + ((mng_magnp)*ppChunk)->iMX = iMX; + ((mng_magnp)*ppChunk)->iMY = iMY; + ((mng_magnp)*ppChunk)->iML = iML; + ((mng_magnp)*ppChunk)->iMR = iMR; + ((mng_magnp)*ppChunk)->iMT = iMT; + ((mng_magnp)*ppChunk)->iMB = iMB; + ((mng_magnp)*ppChunk)->iMethodY = iMethodY; + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_MAGN, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_INCLUDE_MPNG_PROPOSAL +READ_CHUNK (mng_read_mpng) +{ + mng_uint32 iFramewidth; + mng_uint32 iFrameheight; + mng_uint16 iTickspersec; + mng_uint32 iFramessize; + mng_uint32 iCompressedsize; +#if defined(MNG_SUPPORT_DISPLAY) || defined(MNG_STORE_CHUNKS) + mng_retcode iRetcode; + mng_uint16 iNumplays; + mng_uint32 iBufsize; + mng_uint8p pBuf = 0; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_MPNG, MNG_LC_START); +#endif + /* sequence checks */ + if (!pData->bHasIHDR) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (iRawlen < 41) /* length must be at least 41 */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + + iFramewidth = mng_get_int32 (pRawdata); + if (iFramewidth == 0) /* frame_width must not be zero */ + MNG_ERROR (pData, MNG_INVALIDWIDTH); + + iFrameheight = mng_get_int32 (pRawdata+4); + if (iFrameheight == 0) /* frame_height must not be zero */ + MNG_ERROR (pData, MNG_INVALIDHEIGHT); + + iTickspersec = mng_get_uint16 (pRawdata+10); + if (iTickspersec == 0) /* delay_den must not be zero */ + MNG_ERROR (pData, MNG_INVALIDFIELDVAL); + + if (*(pRawdata+12) != 0) /* only deflate compression-method allowed */ + MNG_ERROR (pData, MNG_INVALIDCOMPRESS); + +#if defined(MNG_SUPPORT_DISPLAY) || defined(MNG_STORE_CHUNKS) + iNumplays = mng_get_uint16 (pRawdata+8); + iCompressedsize = (mng_uint32)(iRawlen - 13); +#endif + +#ifdef MNG_SUPPORT_DISPLAY + { + iRetcode = mng_inflate_buffer (pData, pRawdata+13, iCompressedsize, + &pBuf, &iBufsize, &iFramessize); + if (iRetcode) /* on error bail out */ + { + MNG_FREEX (pData, pBuf, iBufsize); + return iRetcode; + } + + if (iFramessize % 26) + { + MNG_FREEX (pData, pBuf, iBufsize); + MNG_ERROR (pData, MNG_INVALIDLENGTH); + } + + iRetcode = mng_create_mpng_obj (pData, iFramewidth, iFrameheight, iNumplays, + iTickspersec, iFramessize, pBuf); + if (iRetcode) /* on error bail out */ + { + MNG_FREEX (pData, pBuf, iBufsize); + return iRetcode; + } + } +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the fields */ + ((mng_mpngp)*ppChunk)->iFramewidth = iFramewidth; + ((mng_mpngp)*ppChunk)->iFrameheight = iFrameheight; + ((mng_mpngp)*ppChunk)->iNumplays = iNumplays; + ((mng_mpngp)*ppChunk)->iTickspersec = iTickspersec; + ((mng_mpngp)*ppChunk)->iCompressionmethod = *(pRawdata+14); + +#ifndef MNG_SUPPORT_DISPLAY + iRetcode = mng_inflate_buffer (pData, pRawdata+13, iCompressedsize, + &pBuf, &iBufsize, &iFramessize); + if (iRetcode) /* on error bail out */ + { + MNG_FREEX (pData, pBuf, iBufsize); + return iRetcode; + } + + if (iFramessize % 26) + { + MNG_FREEX (pData, pBuf, iBufsize); + MNG_ERROR (pData, MNG_INVALIDLENGTH); + } +#endif + + if (iFramessize) + { + MNG_ALLOCX (pData, ((mng_mpngp)*ppChunk)->pFrames, iFramessize); + if (((mng_mpngp)*ppChunk)->pFrames == 0) + { + MNG_FREEX (pData, pBuf, iBufsize); + MNG_ERROR (pData, MNG_OUTOFMEMORY); + } + + ((mng_mpngp)*ppChunk)->iFramessize = iFramessize; + MNG_COPY (((mng_mpngp)*ppChunk)->pFrames, pBuf, iFramessize); + } + } +#endif /* MNG_STORE_CHUNKS */ + +#if defined(MNG_SUPPORT_DISPLAY) || defined(MNG_STORE_CHUNKS) + MNG_FREEX (pData, pBuf, iBufsize); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_MPNG, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifndef MNG_SKIPCHUNK_evNT +READ_CHUNK (mng_read_evnt) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_EVNT, MNG_LC_START); +#endif + /* sequence checks */ + if ((!pData->bHasMHDR) || (pData->bHasSAVE)) + MNG_ERROR (pData, MNG_SEQUENCEERROR); + + if (iRawlen < 2) /* must have at least 1 entry ! */ + MNG_ERROR (pData, MNG_INVALIDLENGTH); + +#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_SUPPORT_DYNAMICMNG) + { + if (iRawlen) /* not empty ? */ + { + mng_retcode iRetcode; + mng_uint8p pTemp; + mng_uint8p pNull; + mng_uint32 iLen; + mng_uint8 iEventtype; + mng_uint8 iMasktype; + mng_int32 iLeft; + mng_int32 iRight; + mng_int32 iTop; + mng_int32 iBottom; + mng_uint16 iObjectid; + mng_uint8 iIndex; + mng_uint32 iNamesize; + + pTemp = pRawdata; + iLen = iRawlen; + + while (iLen) /* anything left ? */ + { + iEventtype = *pTemp; /* eventtype */ + if (iEventtype > 5) + MNG_ERROR (pData, MNG_INVALIDEVENT); + + pTemp++; + + iMasktype = *pTemp; /* masktype */ + if (iMasktype > 5) + MNG_ERROR (pData, MNG_INVALIDMASK); + + pTemp++; + iLen -= 2; + + iLeft = 0; + iRight = 0; + iTop = 0; + iBottom = 0; + iObjectid = 0; + iIndex = 0; + + switch (iMasktype) + { + case 1 : + { + if (iLen > 16) + { + iLeft = mng_get_int32 (pTemp); + iRight = mng_get_int32 (pTemp+4); + iTop = mng_get_int32 (pTemp+8); + iBottom = mng_get_int32 (pTemp+12); + pTemp += 16; + iLen -= 16; + } + else + MNG_ERROR (pData, MNG_INVALIDLENGTH); + break; + } + case 2 : + { + if (iLen > 2) + { + iObjectid = mng_get_uint16 (pTemp); + pTemp += 2; + iLen -= 2; + } + else + MNG_ERROR (pData, MNG_INVALIDLENGTH); + break; + } + case 3 : + { + if (iLen > 3) + { + iObjectid = mng_get_uint16 (pTemp); + iIndex = *(pTemp+2); + pTemp += 3; + iLen -= 3; + } + else + MNG_ERROR (pData, MNG_INVALIDLENGTH); + break; + } + case 4 : + { + if (iLen > 18) + { + iLeft = mng_get_int32 (pTemp); + iRight = mng_get_int32 (pTemp+4); + iTop = mng_get_int32 (pTemp+8); + iBottom = mng_get_int32 (pTemp+12); + iObjectid = mng_get_uint16 (pTemp+16); + pTemp += 18; + iLen -= 18; + } + else + MNG_ERROR (pData, MNG_INVALIDLENGTH); + break; + } + case 5 : + { + if (iLen > 19) + { + iLeft = mng_get_int32 (pTemp); + iRight = mng_get_int32 (pTemp+4); + iTop = mng_get_int32 (pTemp+8); + iBottom = mng_get_int32 (pTemp+12); + iObjectid = mng_get_uint16 (pTemp+16); + iIndex = *(pTemp+18); + pTemp += 19; + iLen -= 19; + } + else + MNG_ERROR (pData, MNG_INVALIDLENGTH); + break; + } + } + + pNull = find_null (pTemp); /* get the name length */ + + if ((pNull - pTemp) > (mng_int32)iLen) + { + iNamesize = iLen; /* no null found; so end of evNT */ + iLen = 0; + } + else + { + iNamesize = pNull - pTemp; /* should be another entry */ + iLen = iLen - iNamesize - 1; + + if (!iLen) /* must not end with a null ! */ + MNG_ERROR (pData, MNG_ENDWITHNULL); + } + + iRetcode = mng_create_event (pData, iEventtype, iMasktype, iLeft, iRight, + iTop, iBottom, iObjectid, iIndex, + iNamesize, (mng_pchar)pTemp); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + pTemp = pTemp + iNamesize + 1; + } + } + } +#endif /* MNG_SUPPORT_DISPLAY && MNG_SUPPORT_DYNAMICMNG */ + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + if (iRawlen) /* not empty ? */ + { + mng_uint32 iX; + mng_uint32 iCount = 0; + mng_uint8p pTemp; + mng_uint8p pNull; + mng_uint32 iLen; + mng_uint8 iEventtype; + mng_uint8 iMasktype; + mng_int32 iLeft; + mng_int32 iRight; + mng_int32 iTop; + mng_int32 iBottom; + mng_uint16 iObjectid; + mng_uint8 iIndex; + mng_uint32 iNamesize; + mng_evnt_entryp pEntry = MNG_NULL; + + for (iX = 0; iX < 2; iX++) /* do this twice to get the count first ! */ + { + pTemp = pRawdata; + iLen = iRawlen; + + if (iX) /* second run ? */ + { + MNG_ALLOC (pData, pEntry, (iCount * sizeof (mng_evnt_entry))); + + ((mng_evntp)*ppChunk)->iCount = iCount; + ((mng_evntp)*ppChunk)->pEntries = pEntry; + } + + while (iLen) /* anything left ? */ + { + iEventtype = *pTemp; /* eventtype */ + if (iEventtype > 5) + MNG_ERROR (pData, MNG_INVALIDEVENT); + + pTemp++; + + iMasktype = *pTemp; /* masktype */ + if (iMasktype > 5) + MNG_ERROR (pData, MNG_INVALIDMASK); + + pTemp++; + iLen -= 2; + + iLeft = 0; + iRight = 0; + iTop = 0; + iBottom = 0; + iObjectid = 0; + iIndex = 0; + + switch (iMasktype) + { + case 1 : + { + if (iLen > 16) + { + iLeft = mng_get_int32 (pTemp); + iRight = mng_get_int32 (pTemp+4); + iTop = mng_get_int32 (pTemp+8); + iBottom = mng_get_int32 (pTemp+12); + pTemp += 16; + iLen -= 16; + } + else + MNG_ERROR (pData, MNG_INVALIDLENGTH); + break; + } + case 2 : + { + if (iLen > 2) + { + iObjectid = mng_get_uint16 (pTemp); + pTemp += 2; + iLen -= 2; + } + else + MNG_ERROR (pData, MNG_INVALIDLENGTH); + break; + } + case 3 : + { + if (iLen > 3) + { + iObjectid = mng_get_uint16 (pTemp); + iIndex = *(pTemp+2); + pTemp += 3; + iLen -= 3; + } + else + MNG_ERROR (pData, MNG_INVALIDLENGTH); + break; + } + case 4 : + { + if (iLen > 18) + { + iLeft = mng_get_int32 (pTemp); + iRight = mng_get_int32 (pTemp+4); + iTop = mng_get_int32 (pTemp+8); + iBottom = mng_get_int32 (pTemp+12); + iObjectid = mng_get_uint16 (pTemp+16); + pTemp += 18; + iLen -= 18; + } + else + MNG_ERROR (pData, MNG_INVALIDLENGTH); + break; + } + case 5 : + { + if (iLen > 19) + { + iLeft = mng_get_int32 (pTemp); + iRight = mng_get_int32 (pTemp+4); + iTop = mng_get_int32 (pTemp+8); + iBottom = mng_get_int32 (pTemp+12); + iObjectid = mng_get_uint16 (pTemp+16); + iIndex = *(pTemp+18); + pTemp += 19; + iLen -= 19; + } + else + MNG_ERROR (pData, MNG_INVALIDLENGTH); + break; + } + } + + pNull = find_null (pTemp); /* get the name length */ + + if ((pNull - pTemp) > (mng_int32)iLen) + { + iNamesize = iLen; /* no null found; so end of evNT */ + iLen = 0; + } + else + { + iNamesize = pNull - pTemp; /* should be another entry */ + iLen = iLen - iNamesize - 1; + + if (!iLen) /* must not end with a null ! */ + MNG_ERROR (pData, MNG_ENDWITHNULL); + } + + if (!iX) + { + iCount++; + } + else + { + pEntry->iEventtype = iEventtype; + pEntry->iMasktype = iMasktype; + pEntry->iLeft = iLeft; + pEntry->iRight = iRight; + pEntry->iTop = iTop; + pEntry->iBottom = iBottom; + pEntry->iObjectid = iObjectid; + pEntry->iIndex = iIndex; + pEntry->iSegmentnamesize = iNamesize; + + if (iNamesize) + { + MNG_ALLOC (pData, pEntry->zSegmentname, iNamesize+1); + MNG_COPY (pEntry->zSegmentname, pTemp, iNamesize); + } + + pEntry++; + } + + pTemp = pTemp + iNamesize + 1; + } + } + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_EVNT, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +READ_CHUNK (mng_read_unknown) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_UNKNOWN, MNG_LC_START); +#endif + /* sequence checks */ +#ifdef MNG_INCLUDE_JNG + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && + (!pData->bHasBASI) && (!pData->bHasDHDR) && (!pData->bHasJHDR)) +#else + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && + (!pData->bHasBASI) && (!pData->bHasDHDR) ) +#endif + MNG_ERROR (pData, MNG_SEQUENCEERROR); + /* critical chunk ? */ + if ((((mng_uint32)pData->iChunkname & 0x20000000) == 0) +#ifdef MNG_SKIPCHUNK_SAVE + && (pData->iChunkname != MNG_UINT_SAVE) +#endif +#ifdef MNG_SKIPCHUNK_SEEK + && (pData->iChunkname != MNG_UINT_SEEK) +#endif +#ifdef MNG_SKIPCHUNK_DBYK + && (pData->iChunkname != MNG_UINT_DBYK) +#endif +#ifdef MNG_SKIPCHUNK_ORDR + && (pData->iChunkname != MNG_UINT_ORDR) +#endif + ) + MNG_ERROR (pData, MNG_UNKNOWNCRITICAL); + + if (pData->fProcessunknown) /* let the app handle it ? */ + { + mng_bool bOke = pData->fProcessunknown ((mng_handle)pData, pData->iChunkname, + iRawlen, (mng_ptr)pRawdata); + + if (!bOke) + MNG_ERROR (pData, MNG_APPMISCERROR); + } + +#ifdef MNG_STORE_CHUNKS + if (pData->bStorechunks) + { /* initialize storage */ + mng_retcode iRetcode = ((mng_chunk_headerp)pHeader)->fCreate (pData, pHeader, ppChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* store the length */ + ((mng_chunk_headerp)*ppChunk)->iChunkname = pData->iChunkname; + ((mng_unknown_chunkp)*ppChunk)->iDatasize = iRawlen; + + if (iRawlen == 0) /* any data at all ? */ + ((mng_unknown_chunkp)*ppChunk)->pData = 0; + else + { /* then store it */ + MNG_ALLOC (pData, ((mng_unknown_chunkp)*ppChunk)->pData, iRawlen); + MNG_COPY (((mng_unknown_chunkp)*ppChunk)->pData, pRawdata, iRawlen); + } + } +#endif /* MNG_STORE_CHUNKS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_UNKNOWN, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} +#endif + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_READ_PROCS */ + +/* ************************************************************************** */ +/* * * */ +/* * chunk write functions * */ +/* * * */ +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_WRITE_PROCS + +/* ************************************************************************** */ + +WRITE_CHUNK (mng_write_ihdr) +{ + mng_ihdrp pIHDR; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_IHDR, MNG_LC_START); +#endif + + pIHDR = (mng_ihdrp)pChunk; /* address the proper chunk */ + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 13; + /* fill the output buffer */ + mng_put_uint32 (pRawdata, pIHDR->iWidth); + mng_put_uint32 (pRawdata+4, pIHDR->iHeight); + + *(pRawdata+8) = pIHDR->iBitdepth; + *(pRawdata+9) = pIHDR->iColortype; + *(pRawdata+10) = pIHDR->iCompression; + *(pRawdata+11) = pIHDR->iFilter; + *(pRawdata+12) = pIHDR->iInterlace; + /* and write it */ + iRetcode = write_raw_chunk (pData, pIHDR->sHeader.iChunkname, iRawlen, pRawdata); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_IHDR, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +WRITE_CHUNK (mng_write_plte) +{ + mng_pltep pPLTE; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + mng_uint8p pTemp; + mng_uint32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_PLTE, MNG_LC_START); +#endif + + pPLTE = (mng_pltep)pChunk; /* address the proper chunk */ + + if (pPLTE->bEmpty) /* write empty chunk ? */ + iRetcode = write_raw_chunk (pData, pPLTE->sHeader.iChunkname, 0, 0); + else + { + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = pPLTE->iEntrycount * 3; + /* fill the output buffer */ + pTemp = pRawdata; + + for (iX = 0; iX < pPLTE->iEntrycount; iX++) + { + *pTemp = pPLTE->aEntries [iX].iRed; + *(pTemp+1) = pPLTE->aEntries [iX].iGreen; + *(pTemp+2) = pPLTE->aEntries [iX].iBlue; + + pTemp += 3; + } + /* and write it */ + iRetcode = write_raw_chunk (pData, pPLTE->sHeader.iChunkname, iRawlen, pRawdata); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_PLTE, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +WRITE_CHUNK (mng_write_idat) +{ + mng_idatp pIDAT; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_IDAT, MNG_LC_START); +#endif + + pIDAT = (mng_idatp)pChunk; /* address the proper chunk */ + + if (pIDAT->bEmpty) /* and write it */ + iRetcode = write_raw_chunk (pData, pIDAT->sHeader.iChunkname, 0, 0); + else + iRetcode = write_raw_chunk (pData, pIDAT->sHeader.iChunkname, + pIDAT->iDatasize, pIDAT->pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_IDAT, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +WRITE_CHUNK (mng_write_iend) +{ + mng_iendp pIEND; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_IEND, MNG_LC_START); +#endif + + pIEND = (mng_iendp)pChunk; /* address the proper chunk */ + /* and write it */ + iRetcode = write_raw_chunk (pData, pIEND->sHeader.iChunkname, 0, 0); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_IEND, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +WRITE_CHUNK (mng_write_trns) +{ + mng_trnsp pTRNS; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + mng_uint8p pTemp; + mng_uint32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_TRNS, MNG_LC_START); +#endif + + pTRNS = (mng_trnsp)pChunk; /* address the proper chunk */ + + if (pTRNS->bEmpty) /* write empty chunk ? */ + iRetcode = write_raw_chunk (pData, pTRNS->sHeader.iChunkname, 0, 0); + else + if (pTRNS->bGlobal) /* write global chunk ? */ + iRetcode = write_raw_chunk (pData, pTRNS->sHeader.iChunkname, + pTRNS->iRawlen, (mng_uint8p)pTRNS->aRawdata); + else + { + pRawdata = pData->pWritebuf+8; /* init output buffer */ + iRawlen = 0; /* and default size */ + + switch (pTRNS->iType) + { + case 0: { + iRawlen = 2; /* fill the size & output buffer */ + mng_put_uint16 (pRawdata, pTRNS->iGray); + + break; + } + case 2: { + iRawlen = 6; /* fill the size & output buffer */ + mng_put_uint16 (pRawdata, pTRNS->iRed); + mng_put_uint16 (pRawdata+2, pTRNS->iGreen); + mng_put_uint16 (pRawdata+4, pTRNS->iBlue); + + break; + } + case 3: { /* init output buffer size */ + iRawlen = pTRNS->iCount; + + pTemp = pRawdata; /* fill the output buffer */ + + for (iX = 0; iX < pTRNS->iCount; iX++) + { + *pTemp = pTRNS->aEntries[iX]; + pTemp++; + } + + break; + } + } + /* write the chunk */ + iRetcode = write_raw_chunk (pData, pTRNS->sHeader.iChunkname, + iRawlen, pRawdata); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_TRNS, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +WRITE_CHUNK (mng_write_gama) +{ + mng_gamap pGAMA; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_GAMA, MNG_LC_START); +#endif + + pGAMA = (mng_gamap)pChunk; /* address the proper chunk */ + + if (pGAMA->bEmpty) /* write empty ? */ + iRetcode = write_raw_chunk (pData, pGAMA->sHeader.iChunkname, 0, 0); + else + { + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 4; + /* fill the buffer */ + mng_put_uint32 (pRawdata, pGAMA->iGamma); + /* and write it */ + iRetcode = write_raw_chunk (pData, pGAMA->sHeader.iChunkname, + iRawlen, pRawdata); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_GAMA, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_cHRM +WRITE_CHUNK (mng_write_chrm) +{ + mng_chrmp pCHRM; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_CHRM, MNG_LC_START); +#endif + + pCHRM = (mng_chrmp)pChunk; /* address the proper chunk */ + + if (pCHRM->bEmpty) /* write empty ? */ + iRetcode = write_raw_chunk (pData, pCHRM->sHeader.iChunkname, 0, 0); + else + { + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 32; + /* fill the buffer */ + mng_put_uint32 (pRawdata, pCHRM->iWhitepointx); + mng_put_uint32 (pRawdata+4, pCHRM->iWhitepointy); + mng_put_uint32 (pRawdata+8, pCHRM->iRedx); + mng_put_uint32 (pRawdata+12, pCHRM->iRedy); + mng_put_uint32 (pRawdata+16, pCHRM->iGreenx); + mng_put_uint32 (pRawdata+20, pCHRM->iGreeny); + mng_put_uint32 (pRawdata+24, pCHRM->iBluex); + mng_put_uint32 (pRawdata+28, pCHRM->iBluey); + /* and write it */ + iRetcode = write_raw_chunk (pData, pCHRM->sHeader.iChunkname, + iRawlen, pRawdata); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_CHRM, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +WRITE_CHUNK (mng_write_srgb) +{ + mng_srgbp pSRGB; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_SRGB, MNG_LC_START); +#endif + + pSRGB = (mng_srgbp)pChunk; /* address the proper chunk */ + + if (pSRGB->bEmpty) /* write empty ? */ + iRetcode = write_raw_chunk (pData, pSRGB->sHeader.iChunkname, 0, 0); + else + { + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 1; + /* fill the buffer */ + *pRawdata = pSRGB->iRenderingintent; + /* and write it */ + iRetcode = write_raw_chunk (pData, pSRGB->sHeader.iChunkname, + iRawlen, pRawdata); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_SRGB, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_iCCP +WRITE_CHUNK (mng_write_iccp) +{ + mng_iccpp pICCP; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + mng_uint8p pTemp; + mng_uint8p pBuf = 0; + mng_uint32 iBuflen; + mng_uint32 iReallen; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_ICCP, MNG_LC_START); +#endif + + pICCP = (mng_iccpp)pChunk; /* address the proper chunk */ + + if (pICCP->bEmpty) /* write empty ? */ + iRetcode = write_raw_chunk (pData, pICCP->sHeader.iChunkname, 0, 0); + else + { /* compress the profile */ + iRetcode = deflate_buffer (pData, pICCP->pProfile, pICCP->iProfilesize, + &pBuf, &iBuflen, &iReallen); + + if (!iRetcode) /* still oke ? */ + { + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = pICCP->iNamesize + 2 + iReallen; + /* requires large buffer ? */ + if (iRawlen > pData->iWritebufsize) + MNG_ALLOC (pData, pRawdata, iRawlen); + + pTemp = pRawdata; /* fill the buffer */ + + if (pICCP->iNamesize) + { + MNG_COPY (pTemp, pICCP->zName, pICCP->iNamesize); + pTemp += pICCP->iNamesize; + } + + *pTemp = 0; + *(pTemp+1) = pICCP->iCompression; + pTemp += 2; + + if (iReallen) + MNG_COPY (pTemp, pBuf, iReallen); + /* and write it */ + iRetcode = write_raw_chunk (pData, pICCP->sHeader.iChunkname, + iRawlen, pRawdata); + /* drop the temp buffer ? */ + if (iRawlen > pData->iWritebufsize) + MNG_FREEX (pData, pRawdata, iRawlen); + + } + + MNG_FREEX (pData, pBuf, iBuflen); /* always drop the extra buffer */ + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_ICCP, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_tEXt +WRITE_CHUNK (mng_write_text) +{ + mng_textp pTEXT; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + mng_uint8p pTemp; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_TEXT, MNG_LC_START); +#endif + + pTEXT = (mng_textp)pChunk; /* address the proper chunk */ + + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = pTEXT->iKeywordsize + 1 + pTEXT->iTextsize; + /* requires large buffer ? */ + if (iRawlen > pData->iWritebufsize) + MNG_ALLOC (pData, pRawdata, iRawlen); + + pTemp = pRawdata; /* fill the buffer */ + + if (pTEXT->iKeywordsize) + { + MNG_COPY (pTemp, pTEXT->zKeyword, pTEXT->iKeywordsize); + pTemp += pTEXT->iKeywordsize; + } + + *pTemp = 0; + pTemp += 1; + + if (pTEXT->iTextsize) + MNG_COPY (pTemp, pTEXT->zText, pTEXT->iTextsize); + /* and write it */ + iRetcode = write_raw_chunk (pData, pTEXT->sHeader.iChunkname, + iRawlen, pRawdata); + + if (iRawlen > pData->iWritebufsize) /* drop the temp buffer ? */ + MNG_FREEX (pData, pRawdata, iRawlen); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_TEXT, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_zTXt +WRITE_CHUNK (mng_write_ztxt) +{ + mng_ztxtp pZTXT; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + mng_uint8p pTemp; + mng_uint8p pBuf = 0; + mng_uint32 iBuflen; + mng_uint32 iReallen; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_ZTXT, MNG_LC_START); +#endif + + pZTXT = (mng_ztxtp)pChunk; /* address the proper chunk */ + /* compress the text */ + iRetcode = deflate_buffer (pData, (mng_uint8p)pZTXT->zText, pZTXT->iTextsize, + &pBuf, &iBuflen, &iReallen); + + if (!iRetcode) /* all ok ? */ + { + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = pZTXT->iKeywordsize + 2 + iReallen; + /* requires large buffer ? */ + if (iRawlen > pData->iWritebufsize) + MNG_ALLOC (pData, pRawdata, iRawlen); + + pTemp = pRawdata; /* fill the buffer */ + + if (pZTXT->iKeywordsize) + { + MNG_COPY (pTemp, pZTXT->zKeyword, pZTXT->iKeywordsize); + pTemp += pZTXT->iKeywordsize; + } + + *pTemp = 0; /* terminator zero */ + pTemp++; + *pTemp = 0; /* compression type */ + pTemp++; + + if (iReallen) + MNG_COPY (pTemp, pBuf, iReallen); + /* and write it */ + iRetcode = write_raw_chunk (pData, pZTXT->sHeader.iChunkname, + iRawlen, pRawdata); + /* drop the temp buffer ? */ + if (iRawlen > pData->iWritebufsize) + MNG_FREEX (pData, pRawdata, iRawlen); + + } + + MNG_FREEX (pData, pBuf, iBuflen); /* always drop the compression buffer */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_ZTXT, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_iTXt +WRITE_CHUNK (mng_write_itxt) +{ + mng_itxtp pITXT; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + mng_uint8p pTemp; + mng_uint8p pBuf = 0; + mng_uint32 iBuflen; + mng_uint32 iReallen; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_ITXT, MNG_LC_START); +#endif + + pITXT = (mng_itxtp)pChunk; /* address the proper chunk */ + + if (pITXT->iCompressionflag) /* compress the text */ + iRetcode = deflate_buffer (pData, (mng_uint8p)pITXT->zText, pITXT->iTextsize, + &pBuf, &iBuflen, &iReallen); + else + iRetcode = MNG_NOERROR; + + if (!iRetcode) /* all ok ? */ + { + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = pITXT->iKeywordsize + pITXT->iLanguagesize + + pITXT->iTranslationsize + 5; + + if (pITXT->iCompressionflag) + iRawlen = iRawlen + iReallen; + else + iRawlen = iRawlen + pITXT->iTextsize; + /* requires large buffer ? */ + if (iRawlen > pData->iWritebufsize) + MNG_ALLOC (pData, pRawdata, iRawlen); + + pTemp = pRawdata; /* fill the buffer */ + + if (pITXT->iKeywordsize) + { + MNG_COPY (pTemp, pITXT->zKeyword, pITXT->iKeywordsize); + pTemp += pITXT->iKeywordsize; + } + + *pTemp = 0; + pTemp++; + *pTemp = pITXT->iCompressionflag; + pTemp++; + *pTemp = pITXT->iCompressionmethod; + pTemp++; + + if (pITXT->iLanguagesize) + { + MNG_COPY (pTemp, pITXT->zLanguage, pITXT->iLanguagesize); + pTemp += pITXT->iLanguagesize; + } + + *pTemp = 0; + pTemp++; + + if (pITXT->iTranslationsize) + { + MNG_COPY (pTemp, pITXT->zTranslation, pITXT->iTranslationsize); + pTemp += pITXT->iTranslationsize; + } + + *pTemp = 0; + pTemp++; + + if (pITXT->iCompressionflag) + { + if (iReallen) + MNG_COPY (pTemp, pBuf, iReallen); + } + else + { + if (pITXT->iTextsize) + MNG_COPY (pTemp, pITXT->zText, pITXT->iTextsize); + } + /* and write it */ + iRetcode = write_raw_chunk (pData, pITXT->sHeader.iChunkname, + iRawlen, pRawdata); + /* drop the temp buffer ? */ + if (iRawlen > pData->iWritebufsize) + MNG_FREEX (pData, pRawdata, iRawlen); + + } + + MNG_FREEX (pData, pBuf, iBuflen); /* always drop the compression buffer */ + + if (iRetcode) /* on error bail out */ + return iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_ITXT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_bKGD +WRITE_CHUNK (mng_write_bkgd) +{ + mng_bkgdp pBKGD; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_BKGD, MNG_LC_START); +#endif + + pBKGD = (mng_bkgdp)pChunk; /* address the proper chunk */ + + if (pBKGD->bEmpty) /* write empty ? */ + iRetcode = write_raw_chunk (pData, pBKGD->sHeader.iChunkname, 0, 0); + else + { + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 0; /* and default size */ + + switch (pBKGD->iType) + { + case 0: { /* gray */ + iRawlen = 2; /* fill the size & output buffer */ + mng_put_uint16 (pRawdata, pBKGD->iGray); + + break; + } + case 2: { /* rgb */ + iRawlen = 6; /* fill the size & output buffer */ + mng_put_uint16 (pRawdata, pBKGD->iRed); + mng_put_uint16 (pRawdata+2, pBKGD->iGreen); + mng_put_uint16 (pRawdata+4, pBKGD->iBlue); + + break; + } + case 3: { /* indexed */ + iRawlen = 1; /* fill the size & output buffer */ + *pRawdata = pBKGD->iIndex; + + break; + } + } + /* and write it */ + iRetcode = write_raw_chunk (pData, pBKGD->sHeader.iChunkname, + iRawlen, pRawdata); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_BKGD, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_pHYs +WRITE_CHUNK (mng_write_phys) +{ + mng_physp pPHYS; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_PHYS, MNG_LC_START); +#endif + + pPHYS = (mng_physp)pChunk; /* address the proper chunk */ + + if (pPHYS->bEmpty) /* write empty ? */ + iRetcode = write_raw_chunk (pData, pPHYS->sHeader.iChunkname, 0, 0); + else + { + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 9; + /* fill the output buffer */ + mng_put_uint32 (pRawdata, pPHYS->iSizex); + mng_put_uint32 (pRawdata+4, pPHYS->iSizey); + + *(pRawdata+8) = pPHYS->iUnit; + /* and write it */ + iRetcode = write_raw_chunk (pData, pPHYS->sHeader.iChunkname, + iRawlen, pRawdata); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_PHYS, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_sBIT +WRITE_CHUNK (mng_write_sbit) +{ + mng_sbitp pSBIT; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_SBIT, MNG_LC_START); +#endif + + pSBIT = (mng_sbitp)pChunk; /* address the proper chunk */ + + if (pSBIT->bEmpty) /* write empty ? */ + iRetcode = write_raw_chunk (pData, pSBIT->sHeader.iChunkname, 0, 0); + else + { + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 0; /* and default size */ + + switch (pSBIT->iType) + { + case 0: { /* gray */ + iRawlen = 1; /* fill the size & output buffer */ + *pRawdata = pSBIT->aBits[0]; + + break; + } + case 2: { /* rgb */ + iRawlen = 3; /* fill the size & output buffer */ + *pRawdata = pSBIT->aBits[0]; + *(pRawdata+1) = pSBIT->aBits[1]; + *(pRawdata+2) = pSBIT->aBits[2]; + + break; + } + case 3: { /* indexed */ + iRawlen = 3; /* fill the size & output buffer */ + *pRawdata = pSBIT->aBits[0]; + *pRawdata = pSBIT->aBits[1]; + *pRawdata = pSBIT->aBits[2]; + + break; + } + case 4: { /* gray + alpha */ + iRawlen = 2; /* fill the size & output buffer */ + *pRawdata = pSBIT->aBits[0]; + *(pRawdata+1) = pSBIT->aBits[1]; + + break; + } + case 6: { /* rgb + alpha */ + iRawlen = 4; /* fill the size & output buffer */ + *pRawdata = pSBIT->aBits[0]; + *(pRawdata+1) = pSBIT->aBits[1]; + *(pRawdata+2) = pSBIT->aBits[2]; + *(pRawdata+3) = pSBIT->aBits[3]; + + break; + } + case 10: { /* jpeg gray */ + iRawlen = 1; /* fill the size & output buffer */ + *pRawdata = pSBIT->aBits[0]; + + break; + } + case 12: { /* jpeg rgb */ + iRawlen = 3; /* fill the size & output buffer */ + *pRawdata = pSBIT->aBits[0]; + *(pRawdata+1) = pSBIT->aBits[1]; + *(pRawdata+2) = pSBIT->aBits[2]; + + break; + } + case 14: { /* jpeg gray + alpha */ + iRawlen = 2; /* fill the size & output buffer */ + *pRawdata = pSBIT->aBits[0]; + *(pRawdata+1) = pSBIT->aBits[1]; + + break; + } + case 16: { /* jpeg rgb + alpha */ + iRawlen = 4; /* fill the size & output buffer */ + *pRawdata = pSBIT->aBits[0]; + *(pRawdata+1) = pSBIT->aBits[1]; + *(pRawdata+2) = pSBIT->aBits[2]; + *(pRawdata+3) = pSBIT->aBits[3]; + + break; + } + } + /* and write it */ + iRetcode = write_raw_chunk (pData, pSBIT->sHeader.iChunkname, + iRawlen, pRawdata); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_SBIT, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_sPLT +WRITE_CHUNK (mng_write_splt) +{ + mng_spltp pSPLT; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + mng_uint32 iEntrieslen; + mng_uint8p pTemp; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_SPLT, MNG_LC_START); +#endif + + pSPLT = (mng_spltp)pChunk; /* address the proper chunk */ + + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iEntrieslen = ((pSPLT->iSampledepth >> 3) * 4 + 2) * pSPLT->iEntrycount; + iRawlen = pSPLT->iNamesize + 2 + iEntrieslen; + /* requires large buffer ? */ + if (iRawlen > pData->iWritebufsize) + MNG_ALLOC (pData, pRawdata, iRawlen); + + pTemp = pRawdata; /* fill the buffer */ + + if (pSPLT->iNamesize) + { + MNG_COPY (pTemp, pSPLT->zName, pSPLT->iNamesize); + pTemp += pSPLT->iNamesize; + } + + *pTemp = 0; + *(pTemp+1) = pSPLT->iSampledepth; + pTemp += 2; + + if (pSPLT->iEntrycount) + MNG_COPY (pTemp, pSPLT->pEntries, iEntrieslen); + /* and write it */ + iRetcode = write_raw_chunk (pData, pSPLT->sHeader.iChunkname, + iRawlen, pRawdata); + + if (iRawlen > pData->iWritebufsize) /* drop the temp buffer ? */ + MNG_FREEX (pData, pRawdata, iRawlen); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_SPLT, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_hIST +WRITE_CHUNK (mng_write_hist) +{ + mng_histp pHIST; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + mng_uint8p pTemp; + mng_uint32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_HIST, MNG_LC_START); +#endif + + pHIST = (mng_histp)pChunk; /* address the proper chunk */ + + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = pHIST->iEntrycount << 1; + + pTemp = pRawdata; /* fill the output buffer */ + + for (iX = 0; iX < pHIST->iEntrycount; iX++) + { + mng_put_uint16 (pTemp, pHIST->aEntries [iX]); + pTemp += 2; + } + /* and write it */ + iRetcode = write_raw_chunk (pData, pHIST->sHeader.iChunkname, + iRawlen, pRawdata); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_HIST, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_tIME +WRITE_CHUNK (mng_write_time) +{ + mng_timep pTIME; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_TIME, MNG_LC_START); +#endif + + pTIME = (mng_timep)pChunk; /* address the proper chunk */ + + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 7; + /* fill the output buffer */ + mng_put_uint16 (pRawdata, pTIME->iYear); + + *(pRawdata+2) = pTIME->iMonth; + *(pRawdata+3) = pTIME->iDay; + *(pRawdata+4) = pTIME->iHour; + *(pRawdata+5) = pTIME->iMinute; + *(pRawdata+6) = pTIME->iSecond; + /* and write it */ + iRetcode = write_raw_chunk (pData, pTIME->sHeader.iChunkname, + iRawlen, pRawdata); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_TIME, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +WRITE_CHUNK (mng_write_mhdr) +{ + mng_mhdrp pMHDR; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_MHDR, MNG_LC_START); +#endif + + pMHDR = (mng_mhdrp)pChunk; /* address the proper chunk */ + + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 28; + /* fill the output buffer */ + mng_put_uint32 (pRawdata, pMHDR->iWidth); + mng_put_uint32 (pRawdata+4, pMHDR->iHeight); + mng_put_uint32 (pRawdata+8, pMHDR->iTicks); + mng_put_uint32 (pRawdata+12, pMHDR->iLayercount); + mng_put_uint32 (pRawdata+16, pMHDR->iFramecount); + mng_put_uint32 (pRawdata+20, pMHDR->iPlaytime); + mng_put_uint32 (pRawdata+24, pMHDR->iSimplicity); + + /* and write it */ + iRetcode = write_raw_chunk (pData, pMHDR->sHeader.iChunkname, + iRawlen, pRawdata); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_MHDR, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +WRITE_CHUNK (mng_write_mend) +{ + mng_mendp pMEND; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_MEND, MNG_LC_START); +#endif + + pMEND = (mng_mendp)pChunk; /* address the proper chunk */ + /* and write it */ + iRetcode = write_raw_chunk (pData, pMEND->sHeader.iChunkname, 0, 0); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_MEND, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +WRITE_CHUNK (mng_write_loop) +{ + mng_loopp pLOOP; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; +#ifndef MNG_NO_LOOP_SIGNALS_SUPPORTED + mng_uint8p pTemp1; + mng_uint32p pTemp2; + mng_uint32 iX; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_LOOP, MNG_LC_START); +#endif + + pLOOP = (mng_loopp)pChunk; /* address the proper chunk */ + + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 5; + /* fill the output buffer */ + *pRawdata = pLOOP->iLevel; + mng_put_uint32 (pRawdata+1, pLOOP->iRepeat); + + if (pLOOP->iTermination) + { + iRawlen++; + *(pRawdata+5) = pLOOP->iTermination; + + if ((pLOOP->iCount) || + (pLOOP->iItermin != 1) || (pLOOP->iItermax != 0x7FFFFFFFL)) + { + iRawlen += 8; + + mng_put_uint32 (pRawdata+6, pLOOP->iItermin); + mng_put_uint32 (pRawdata+10, pLOOP->iItermax); + +#ifndef MNG_NO_LOOP_SIGNALS_SUPPORTED + if (pLOOP->iCount) + { + iRawlen += pLOOP->iCount * 4; + + pTemp1 = pRawdata+14; + pTemp2 = pLOOP->pSignals; + + for (iX = 0; iX < pLOOP->iCount; iX++) + { + mng_put_uint32 (pTemp1, *pTemp2); + + pTemp1 += 4; + pTemp2++; + } + } +#endif + } + } + /* and write it */ + iRetcode = write_raw_chunk (pData, pLOOP->sHeader.iChunkname, + iRawlen, pRawdata); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_LOOP, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +WRITE_CHUNK (mng_write_endl) +{ + mng_endlp pENDL; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_ENDL, MNG_LC_START); +#endif + + pENDL = (mng_endlp)pChunk; /* address the proper chunk */ + + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 1; + + *pRawdata = pENDL->iLevel; /* fill the output buffer */ + /* and write it */ + iRetcode = write_raw_chunk (pData, pENDL->sHeader.iChunkname, + iRawlen, pRawdata); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_ENDL, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +WRITE_CHUNK (mng_write_defi) +{ + mng_defip pDEFI; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_DEFI, MNG_LC_START); +#endif + + pDEFI = (mng_defip)pChunk; /* address the proper chunk */ + + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 2; + /* fill the output buffer */ + mng_put_uint16 (pRawdata, pDEFI->iObjectid); + + if ((pDEFI->iDonotshow) || (pDEFI->iConcrete) || (pDEFI->bHasloca) || (pDEFI->bHasclip)) + { + iRawlen++; + *(pRawdata+2) = pDEFI->iDonotshow; + + if ((pDEFI->iConcrete) || (pDEFI->bHasloca) || (pDEFI->bHasclip)) + { + iRawlen++; + *(pRawdata+3) = pDEFI->iConcrete; + + if ((pDEFI->bHasloca) || (pDEFI->bHasclip)) + { + iRawlen += 8; + + mng_put_uint32 (pRawdata+4, pDEFI->iXlocation); + mng_put_uint32 (pRawdata+8, pDEFI->iYlocation); + + if (pDEFI->bHasclip) + { + iRawlen += 16; + + mng_put_uint32 (pRawdata+12, pDEFI->iLeftcb); + mng_put_uint32 (pRawdata+16, pDEFI->iRightcb); + mng_put_uint32 (pRawdata+20, pDEFI->iTopcb); + mng_put_uint32 (pRawdata+24, pDEFI->iBottomcb); + } + } + } + } + /* and write it */ + iRetcode = write_raw_chunk (pData, pDEFI->sHeader.iChunkname, + iRawlen, pRawdata); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_DEFI, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +WRITE_CHUNK (mng_write_basi) +{ + mng_basip pBASI; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + mng_bool bOpaque; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_BASI, MNG_LC_START); +#endif + + pBASI = (mng_basip)pChunk; /* address the proper chunk */ + +#ifndef MNG_NO_16BIT_SUPPORT + if (pBASI->iBitdepth <= 8) /* determine opacity alpha-field */ +#endif + bOpaque = (mng_bool)(pBASI->iAlpha == 0xFF); +#ifndef MNG_NO_16BIT_SUPPORT + else + bOpaque = (mng_bool)(pBASI->iAlpha == 0xFFFF); +#endif + + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 13; + /* fill the output buffer */ + mng_put_uint32 (pRawdata, pBASI->iWidth); + mng_put_uint32 (pRawdata+4, pBASI->iHeight); + + *(pRawdata+8) = pBASI->iBitdepth; + *(pRawdata+9) = pBASI->iColortype; + *(pRawdata+10) = pBASI->iCompression; + *(pRawdata+11) = pBASI->iFilter; + *(pRawdata+12) = pBASI->iInterlace; + + if ((pBASI->iRed) || (pBASI->iGreen) || (pBASI->iBlue) || + (!bOpaque) || (pBASI->iViewable)) + { + iRawlen += 6; + mng_put_uint16 (pRawdata+13, pBASI->iRed); + mng_put_uint16 (pRawdata+15, pBASI->iGreen); + mng_put_uint16 (pRawdata+17, pBASI->iBlue); + + if ((!bOpaque) || (pBASI->iViewable)) + { + iRawlen += 2; + mng_put_uint16 (pRawdata+19, pBASI->iAlpha); + + if (pBASI->iViewable) + { + iRawlen++; + *(pRawdata+21) = pBASI->iViewable; + } + } + } + /* and write it */ + iRetcode = write_raw_chunk (pData, pBASI->sHeader.iChunkname, + iRawlen, pRawdata); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_BASI, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +WRITE_CHUNK (mng_write_clon) +{ + mng_clonp pCLON; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_CLON, MNG_LC_START); +#endif + + pCLON = (mng_clonp)pChunk; /* address the proper chunk */ + + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 4; + /* fill the output buffer */ + mng_put_uint16 (pRawdata, pCLON->iSourceid); + mng_put_uint16 (pRawdata+2, pCLON->iCloneid); + + if ((pCLON->iClonetype) || (pCLON->iDonotshow) || (pCLON->iConcrete) || (pCLON->bHasloca)) + { + iRawlen++; + *(pRawdata+4) = pCLON->iClonetype; + + if ((pCLON->iDonotshow) || (pCLON->iConcrete) || (pCLON->bHasloca)) + { + iRawlen++; + *(pRawdata+5) = pCLON->iDonotshow; + + if ((pCLON->iConcrete) || (pCLON->bHasloca)) + { + iRawlen++; + *(pRawdata+6) = pCLON->iConcrete; + + if (pCLON->bHasloca) + { + iRawlen += 9; + *(pRawdata+7) = pCLON->iLocationtype; + mng_put_int32 (pRawdata+8, pCLON->iLocationx); + mng_put_int32 (pRawdata+12, pCLON->iLocationy); + } + } + } + } + /* and write it */ + iRetcode = write_raw_chunk (pData, pCLON->sHeader.iChunkname, + iRawlen, pRawdata); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_CLON, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_PAST +WRITE_CHUNK (mng_write_past) +{ + mng_pastp pPAST; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + mng_past_sourcep pSource; + mng_uint32 iX; + mng_uint8p pTemp; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_PAST, MNG_LC_START); +#endif + + pPAST = (mng_pastp)pChunk; /* address the proper chunk */ + + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 11 + (30 * pPAST->iCount); + /* requires large buffer ? */ + if (iRawlen > pData->iWritebufsize) + MNG_ALLOC (pData, pRawdata, iRawlen); + /* fill the output buffer */ + mng_put_uint16 (pRawdata, pPAST->iDestid); + + *(pRawdata+2) = pPAST->iTargettype; + + mng_put_int32 (pRawdata+3, pPAST->iTargetx); + mng_put_int32 (pRawdata+7, pPAST->iTargety); + + pTemp = pRawdata+11; + pSource = pPAST->pSources; + + for (iX = 0; iX < pPAST->iCount; iX++) + { + mng_put_uint16 (pTemp, pSource->iSourceid); + + *(pTemp+2) = pSource->iComposition; + *(pTemp+3) = pSource->iOrientation; + *(pTemp+4) = pSource->iOffsettype; + + mng_put_int32 (pTemp+5, pSource->iOffsetx); + mng_put_int32 (pTemp+9, pSource->iOffsety); + + *(pTemp+13) = pSource->iBoundarytype; + + mng_put_int32 (pTemp+14, pSource->iBoundaryl); + mng_put_int32 (pTemp+18, pSource->iBoundaryr); + mng_put_int32 (pTemp+22, pSource->iBoundaryt); + mng_put_int32 (pTemp+26, pSource->iBoundaryb); + + pSource++; + pTemp += 30; + } + /* and write it */ + iRetcode = write_raw_chunk (pData, pPAST->sHeader.iChunkname, + iRawlen, pRawdata); + /* free temporary buffer ? */ + if (iRawlen > pData->iWritebufsize) + MNG_FREEX (pData, pRawdata, iRawlen); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_PAST, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +WRITE_CHUNK (mng_write_disc) +{ + mng_discp pDISC; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + mng_uint32 iX; + mng_uint8p pTemp1; + mng_uint16p pTemp2; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_DISC, MNG_LC_START); +#endif + + pDISC = (mng_discp)pChunk; /* address the proper chunk */ + + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = pDISC->iCount << 1; + + pTemp1 = pRawdata; /* fill the output buffer */ + pTemp2 = pDISC->pObjectids; + + for (iX = 0; iX < pDISC->iCount; iX++) + { + mng_put_uint16 (pTemp1, *pTemp2); + + pTemp2++; + pTemp1 += 2; + } + /* and write it */ + iRetcode = write_raw_chunk (pData, pDISC->sHeader.iChunkname, + iRawlen, pRawdata); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_DISC, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +WRITE_CHUNK (mng_write_back) +{ + mng_backp pBACK; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_BACK, MNG_LC_START); +#endif + + pBACK = (mng_backp)pChunk; /* address the proper chunk */ + + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 6; + /* fill the output buffer */ + mng_put_uint16 (pRawdata, pBACK->iRed); + mng_put_uint16 (pRawdata+2, pBACK->iGreen); + mng_put_uint16 (pRawdata+4, pBACK->iBlue); + + if ((pBACK->iMandatory) || (pBACK->iImageid) || (pBACK->iTile)) + { + iRawlen++; + *(pRawdata+6) = pBACK->iMandatory; + + if ((pBACK->iImageid) || (pBACK->iTile)) + { + iRawlen += 2; + mng_put_uint16 (pRawdata+7, pBACK->iImageid); + + if (pBACK->iTile) + { + iRawlen++; + *(pRawdata+9) = pBACK->iTile; + } + } + } + /* and write it */ + iRetcode = write_raw_chunk (pData, pBACK->sHeader.iChunkname, + iRawlen, pRawdata); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_BACK, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +WRITE_CHUNK (mng_write_fram) +{ + mng_framp pFRAM; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + mng_uint8p pTemp; + mng_uint32p pTemp2; + mng_uint32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_FRAM, MNG_LC_START); +#endif + + pFRAM = (mng_framp)pChunk; /* address the proper chunk */ + + if (pFRAM->bEmpty) /* empty ? */ + iRetcode = write_raw_chunk (pData, pFRAM->sHeader.iChunkname, 0, 0); + else + { + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 1; + /* fill the output buffer */ + *pRawdata = pFRAM->iMode; + + if ((pFRAM->iNamesize ) || + (pFRAM->iChangedelay ) || (pFRAM->iChangetimeout) || + (pFRAM->iChangeclipping) || (pFRAM->iChangesyncid ) ) + { + if (pFRAM->iNamesize) + MNG_COPY (pRawdata+1, pFRAM->zName, pFRAM->iNamesize); + + iRawlen += pFRAM->iNamesize; + pTemp = pRawdata + pFRAM->iNamesize + 1; + + if ((pFRAM->iChangedelay ) || (pFRAM->iChangetimeout) || + (pFRAM->iChangeclipping) || (pFRAM->iChangesyncid ) ) + { + *pTemp = 0; + *(pTemp+1) = pFRAM->iChangedelay; + *(pTemp+2) = pFRAM->iChangetimeout; + *(pTemp+3) = pFRAM->iChangeclipping; + *(pTemp+4) = pFRAM->iChangesyncid; + + iRawlen += 5; + pTemp += 5; + + if (pFRAM->iChangedelay) + { + mng_put_uint32 (pTemp, pFRAM->iDelay); + iRawlen += 4; + pTemp += 4; + } + + if (pFRAM->iChangetimeout) + { + mng_put_uint32 (pTemp, pFRAM->iTimeout); + iRawlen += 4; + pTemp += 4; + } + + if (pFRAM->iChangeclipping) + { + *pTemp = pFRAM->iBoundarytype; + + mng_put_uint32 (pTemp+1, pFRAM->iBoundaryl); + mng_put_uint32 (pTemp+5, pFRAM->iBoundaryr); + mng_put_uint32 (pTemp+9, pFRAM->iBoundaryt); + mng_put_uint32 (pTemp+13, pFRAM->iBoundaryb); + + iRawlen += 17; + pTemp += 17; + } + + if (pFRAM->iChangesyncid) + { + iRawlen += pFRAM->iCount * 4; + pTemp2 = pFRAM->pSyncids; + + for (iX = 0; iX < pFRAM->iCount; iX++) + { + mng_put_uint32 (pTemp, *pTemp2); + + pTemp2++; + pTemp += 4; + } + } + } + } + /* and write it */ + iRetcode = write_raw_chunk (pData, pFRAM->sHeader.iChunkname, + iRawlen, pRawdata); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_FRAM, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +WRITE_CHUNK (mng_write_move) +{ + mng_movep pMOVE; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_MOVE, MNG_LC_START); +#endif + + pMOVE = (mng_movep)pChunk; /* address the proper chunk */ + + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 13; + /* fill the output buffer */ + mng_put_uint16 (pRawdata, pMOVE->iFirstid); + mng_put_uint16 (pRawdata+2, pMOVE->iLastid); + + *(pRawdata+4) = pMOVE->iMovetype; + + mng_put_int32 (pRawdata+5, pMOVE->iMovex); + mng_put_int32 (pRawdata+9, pMOVE->iMovey); + /* and write it */ + iRetcode = write_raw_chunk (pData, pMOVE->sHeader.iChunkname, + iRawlen, pRawdata); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_MOVE, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +WRITE_CHUNK (mng_write_clip) +{ + mng_clipp pCLIP; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_CLIP, MNG_LC_START); +#endif + + pCLIP = (mng_clipp)pChunk; /* address the proper chunk */ + + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 21; + /* fill the output buffer */ + mng_put_uint16 (pRawdata, pCLIP->iFirstid); + mng_put_uint16 (pRawdata+2, pCLIP->iLastid); + + *(pRawdata+4) = pCLIP->iCliptype; + + mng_put_int32 (pRawdata+5, pCLIP->iClipl); + mng_put_int32 (pRawdata+9, pCLIP->iClipr); + mng_put_int32 (pRawdata+13, pCLIP->iClipt); + mng_put_int32 (pRawdata+17, pCLIP->iClipb); + /* and write it */ + iRetcode = write_raw_chunk (pData, pCLIP->sHeader.iChunkname, + iRawlen, pRawdata); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_CLIP, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +WRITE_CHUNK (mng_write_show) +{ + mng_showp pSHOW; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_SHOW, MNG_LC_START); +#endif + + pSHOW = (mng_showp)pChunk; /* address the proper chunk */ + + if (pSHOW->bEmpty) /* empty ? */ + iRetcode = write_raw_chunk (pData, pSHOW->sHeader.iChunkname, 0, 0); + else + { + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 2; + /* fill the output buffer */ + mng_put_uint16 (pRawdata, pSHOW->iFirstid); + + if ((pSHOW->iLastid != pSHOW->iFirstid) || (pSHOW->iMode)) + { + iRawlen += 2; + mng_put_uint16 (pRawdata+2, pSHOW->iLastid); + + if (pSHOW->iMode) + { + iRawlen++; + *(pRawdata+4) = pSHOW->iMode; + } + } + /* and write it */ + iRetcode = write_raw_chunk (pData, pSHOW->sHeader.iChunkname, + iRawlen, pRawdata); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_SHOW, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +WRITE_CHUNK (mng_write_term) +{ + mng_termp pTERM; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_TERM, MNG_LC_START); +#endif + + pTERM = (mng_termp)pChunk; /* address the proper chunk */ + + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 1; + + *pRawdata = pTERM->iTermaction; /* fill the output buffer */ + + if (pTERM->iTermaction == 3) + { + iRawlen = 10; + *(pRawdata+1) = pTERM->iIteraction; + + mng_put_uint32 (pRawdata+2, pTERM->iDelay); + mng_put_uint32 (pRawdata+6, pTERM->iItermax); + } + /* and write it */ + iRetcode = write_raw_chunk (pData, pTERM->sHeader.iChunkname, + iRawlen, pRawdata); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_TERM, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SAVE +WRITE_CHUNK (mng_write_save) +{ + mng_savep pSAVE; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + mng_save_entryp pEntry; + mng_uint32 iEntrysize; + mng_uint8p pTemp; + mng_uint32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_SAVE, MNG_LC_START); +#endif + + pSAVE = (mng_savep)pChunk; /* address the proper chunk */ + + if (pSAVE->bEmpty) /* empty ? */ + iRetcode = write_raw_chunk (pData, pSAVE->sHeader.iChunkname, 0, 0); + else + { + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 1; + + *pRawdata = pSAVE->iOffsettype; /* fill the output buffer */ + + if (pSAVE->iOffsettype == 16) + iEntrysize = 25; + else + iEntrysize = 17; + + pTemp = pRawdata+1; + pEntry = pSAVE->pEntries; + + for (iX = 0; iX < pSAVE->iCount; iX++) + { + if (iX) /* put separator null-byte, except the first */ + { + *pTemp = 0; + pTemp++; + iRawlen++; + } + + iRawlen += iEntrysize + pEntry->iNamesize; + *pTemp = pEntry->iEntrytype; + + if (pSAVE->iOffsettype == 16) + { + mng_put_uint32 (pTemp+1, pEntry->iOffset[0]); + mng_put_uint32 (pTemp+5, pEntry->iOffset[1]); + mng_put_uint32 (pTemp+9, pEntry->iStarttime[0]); + mng_put_uint32 (pTemp+13, pEntry->iStarttime[1]); + mng_put_uint32 (pTemp+17, pEntry->iLayernr); + mng_put_uint32 (pTemp+21, pEntry->iFramenr); + + pTemp += 25; + } + else + { + mng_put_uint32 (pTemp+1, pEntry->iOffset[1]); + mng_put_uint32 (pTemp+5, pEntry->iStarttime[1]); + mng_put_uint32 (pTemp+9, pEntry->iLayernr); + mng_put_uint32 (pTemp+13, pEntry->iFramenr); + + pTemp += 17; + } + + if (pEntry->iNamesize) + { + MNG_COPY (pTemp, pEntry->zName, pEntry->iNamesize); + pTemp += pEntry->iNamesize; + } + + pEntry++; + } + /* and write it */ + iRetcode = write_raw_chunk (pData, pSAVE->sHeader.iChunkname, + iRawlen, pRawdata); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_SAVE, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SEEK +WRITE_CHUNK (mng_write_seek) +{ + mng_seekp pSEEK; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_SEEK, MNG_LC_START); +#endif + + pSEEK = (mng_seekp)pChunk; /* address the proper chunk */ + + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = pSEEK->iNamesize; + + if (iRawlen) /* fill the output buffer */ + MNG_COPY (pRawdata, pSEEK->zName, iRawlen); + /* and write it */ + iRetcode = write_raw_chunk (pData, pSEEK->sHeader.iChunkname, + iRawlen, pRawdata); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_SEEK, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_eXPI +WRITE_CHUNK (mng_write_expi) +{ + mng_expip pEXPI; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_EXPI, MNG_LC_START); +#endif + + pEXPI = (mng_expip)pChunk; /* address the proper chunk */ + + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 2 + pEXPI->iNamesize; + /* fill the output buffer */ + mng_put_uint16 (pRawdata, pEXPI->iSnapshotid); + + if (pEXPI->iNamesize) + MNG_COPY (pRawdata+2, pEXPI->zName, pEXPI->iNamesize); + /* and write it */ + iRetcode = write_raw_chunk (pData, pEXPI->sHeader.iChunkname, + iRawlen, pRawdata); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_EXPI, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_fPRI +WRITE_CHUNK (mng_write_fpri) +{ + mng_fprip pFPRI; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_FPRI, MNG_LC_START); +#endif + + pFPRI = (mng_fprip)pChunk; /* address the proper chunk */ + + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 2; + + *pRawdata = pFPRI->iDeltatype; /* fill the output buffer */ + *(pRawdata+1) = pFPRI->iPriority; + /* and write it */ + iRetcode = write_raw_chunk (pData, pFPRI->sHeader.iChunkname, + iRawlen, pRawdata); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_FPRI, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_nEED +WRITE_CHUNK (mng_write_need) +{ + mng_needp pNEED; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_NEED, MNG_LC_START); +#endif + + pNEED = (mng_needp)pChunk; /* address the proper chunk */ + + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = pNEED->iKeywordssize; + /* fill the output buffer */ + if (pNEED->iKeywordssize) + MNG_COPY (pRawdata, pNEED->zKeywords, pNEED->iKeywordssize); + /* and write it */ + iRetcode = write_raw_chunk (pData, pNEED->sHeader.iChunkname, + iRawlen, pRawdata); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_NEED, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_pHYg +WRITE_CHUNK (mng_write_phyg) +{ + mng_phygp pPHYG; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_PHYG, MNG_LC_START); +#endif + + pPHYG = (mng_phygp)pChunk; /* address the proper chunk */ + + if (pPHYG->bEmpty) /* write empty ? */ + iRetcode = write_raw_chunk (pData, pPHYG->sHeader.iChunkname, 0, 0); + else + { + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 9; + /* fill the output buffer */ + mng_put_uint32 (pRawdata, pPHYG->iSizex); + mng_put_uint32 (pRawdata+4, pPHYG->iSizey); + + *(pRawdata+8) = pPHYG->iUnit; + /* and write it */ + iRetcode = write_raw_chunk (pData, pPHYG->sHeader.iChunkname, + iRawlen, pRawdata); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_PHYG, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +/* B004 */ +#ifdef MNG_INCLUDE_JNG +/* B004 */ +WRITE_CHUNK (mng_write_jhdr) +{ + mng_jhdrp pJHDR; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_JHDR, MNG_LC_START); +#endif + + pJHDR = (mng_jhdrp)pChunk; /* address the proper chunk */ + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 16; + /* fill the output buffer */ + mng_put_uint32 (pRawdata, pJHDR->iWidth); + mng_put_uint32 (pRawdata+4, pJHDR->iHeight); + + *(pRawdata+8) = pJHDR->iColortype; + *(pRawdata+9) = pJHDR->iImagesampledepth; + *(pRawdata+10) = pJHDR->iImagecompression; + *(pRawdata+11) = pJHDR->iImageinterlace; + *(pRawdata+12) = pJHDR->iAlphasampledepth; + *(pRawdata+13) = pJHDR->iAlphacompression; + *(pRawdata+14) = pJHDR->iAlphafilter; + *(pRawdata+15) = pJHDR->iAlphainterlace; + /* and write it */ + iRetcode = write_raw_chunk (pData, pJHDR->sHeader.iChunkname, iRawlen, pRawdata); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_JHDR, MNG_LC_END); +#endif + + return iRetcode; +} +#else +#define write_jhdr 0 +/* B004 */ +#endif /* MNG_INCLUDE_JNG */ +/* B004 */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +WRITE_CHUNK (mng_write_jdaa) +{ + mng_jdatp pJDAA; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_JDAA, MNG_LC_START); +#endif + + pJDAA = (mng_jdaap)pChunk; /* address the proper chunk */ + + if (pJDAA->bEmpty) /* and write it */ + iRetcode = write_raw_chunk (pData, pJDAA->sHeader.iChunkname, 0, 0); + else + iRetcode = write_raw_chunk (pData, pJDAA->sHeader.iChunkname, + pJDAA->iDatasize, pJDAA->pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_JDAA, MNG_LC_END); +#endif + + return iRetcode; +} +#else +#define write_jdaa 0 +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +/* B004 */ +#ifdef MNG_INCLUDE_JNG +/* B004 */ +WRITE_CHUNK (mng_write_jdat) +{ + mng_jdatp pJDAT; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_JDAT, MNG_LC_START); +#endif + + pJDAT = (mng_jdatp)pChunk; /* address the proper chunk */ + + if (pJDAT->bEmpty) /* and write it */ + iRetcode = write_raw_chunk (pData, pJDAT->sHeader.iChunkname, 0, 0); + else + iRetcode = write_raw_chunk (pData, pJDAT->sHeader.iChunkname, + pJDAT->iDatasize, pJDAT->pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_JDAT, MNG_LC_END); +#endif + + return iRetcode; +} +#else +#define write_jdat 0 +/* B004 */ +#endif /* MNG_INCLUDE_JNG */ +/* B004 */ + +/* ************************************************************************** */ + +/* B004 */ +#ifdef MNG_INCLUDE_JNG +/* B004 */ +WRITE_CHUNK (mng_write_jsep) +{ + mng_jsepp pJSEP; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_JSEP, MNG_LC_START); +#endif + + pJSEP = (mng_jsepp)pChunk; /* address the proper chunk */ + /* and write it */ + iRetcode = write_raw_chunk (pData, pJSEP->sHeader.iChunkname, 0, 0); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_JSEP, MNG_LC_END); +#endif + + return iRetcode; +} +#else +#define write_jsep 0 +/* B004 */ +#endif /* MNG_INCLUDE_JNG */ +/* B004 */ + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +WRITE_CHUNK (mng_write_dhdr) +{ + mng_dhdrp pDHDR; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_DHDR, MNG_LC_START); +#endif + + pDHDR = (mng_dhdrp)pChunk; /* address the proper chunk */ + + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 4; + /* fill the output buffer */ + mng_put_uint16 (pRawdata, pDHDR->iObjectid); + + *(pRawdata+2) = pDHDR->iImagetype; + *(pRawdata+3) = pDHDR->iDeltatype; + + if (pDHDR->iDeltatype != 7) + { + iRawlen += 8; + mng_put_uint32 (pRawdata+4, pDHDR->iBlockwidth); + mng_put_uint32 (pRawdata+8, pDHDR->iBlockheight); + + if (pDHDR->iDeltatype != 0) + { + iRawlen += 8; + mng_put_uint32 (pRawdata+12, pDHDR->iBlockx); + mng_put_uint32 (pRawdata+16, pDHDR->iBlocky); + } + } + /* and write it */ + iRetcode = write_raw_chunk (pData, pDHDR->sHeader.iChunkname, + iRawlen, pRawdata); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_DHDR, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +WRITE_CHUNK (mng_write_prom) +{ + mng_promp pPROM; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_PROM, MNG_LC_START); +#endif + + pPROM = (mng_promp)pChunk; /* address the proper chunk */ + + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 3; + + *pRawdata = pPROM->iColortype; /* fill the output buffer */ + *(pRawdata+1) = pPROM->iSampledepth; + *(pRawdata+2) = pPROM->iFilltype; + /* and write it */ + iRetcode = write_raw_chunk (pData, pPROM->sHeader.iChunkname, + iRawlen, pRawdata); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_PROM, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +WRITE_CHUNK (mng_write_ipng) +{ + mng_ipngp pIPNG; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_IPNG, MNG_LC_START); +#endif + + pIPNG = (mng_ipngp)pChunk; /* address the proper chunk */ + /* and write it */ + iRetcode = write_raw_chunk (pData, pIPNG->sHeader.iChunkname, 0, 0); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_IPNG, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +WRITE_CHUNK (mng_write_pplt) +{ + mng_ppltp pPPLT; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + mng_pplt_entryp pEntry; + mng_uint8p pTemp; + mng_uint32 iX; + mng_bool bHasgroup; + mng_uint8p pLastid = 0; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_PPLT, MNG_LC_START); +#endif + + pPPLT = (mng_ppltp)pChunk; /* address the proper chunk */ + + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 1; + + *pRawdata = pPPLT->iDeltatype; /* fill the output buffer */ + + pTemp = pRawdata+1; + bHasgroup = MNG_FALSE; + + for (iX = 0; iX < pPPLT->iCount; iX++) + { + pEntry = &pPPLT->aEntries[iX]; + + if (pEntry->bUsed) /* valid entry ? */ + { + if (!bHasgroup) /* start a new group ? */ + { + bHasgroup = MNG_TRUE; + pLastid = pTemp+1; + + *pTemp = (mng_uint8)iX; + *(pTemp+1) = 0; + + pTemp += 2; + iRawlen += 2; + } + + switch (pPPLT->iDeltatype) /* add group-entry depending on type */ + { + case 0: ; + case 1: { + *pTemp = pEntry->iRed; + *(pTemp+1) = pEntry->iGreen; + *(pTemp+2) = pEntry->iBlue; + + pTemp += 3; + iRawlen += 3; + + break; + } + + case 2: ; + case 3: { + *pTemp = pEntry->iAlpha; + + pTemp++; + iRawlen++; + + break; + } + + case 4: ; + case 5: { + *pTemp = pEntry->iRed; + *(pTemp+1) = pEntry->iGreen; + *(pTemp+2) = pEntry->iBlue; + *(pTemp+3) = pEntry->iAlpha; + + pTemp += 4; + iRawlen += 4; + + break; + } + + } + } + else + { + if (bHasgroup) /* finish off a group ? */ + *pLastid = (mng_uint8)(iX-1); + + bHasgroup = MNG_FALSE; + } + } + + if (bHasgroup) /* last group unfinished ? */ + *pLastid = (mng_uint8)(pPPLT->iCount-1); + /* write the output buffer */ + iRetcode = write_raw_chunk (pData, pPPLT->sHeader.iChunkname, + iRawlen, pRawdata); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_PPLT, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifdef MNG_INCLUDE_JNG +WRITE_CHUNK (mng_write_ijng) +{ + mng_ijngp pIJNG; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_IJNG, MNG_LC_START); +#endif + + pIJNG = (mng_ijngp)pChunk; /* address the proper chunk */ + /* and write it */ + iRetcode = write_raw_chunk (pData, pIJNG->sHeader.iChunkname, 0, 0); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_IJNG, MNG_LC_END); +#endif + + return iRetcode; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +WRITE_CHUNK (mng_write_drop) +{ + mng_dropp pDROP; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + mng_uint32 iX; + mng_uint8p pTemp1; + mng_chunkidp pTemp2; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_DROP, MNG_LC_START); +#endif + + pDROP = (mng_dropp)pChunk; /* address the proper chunk */ + + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = pDROP->iCount << 2; + + pTemp1 = pRawdata; /* fill the output buffer */ + pTemp2 = pDROP->pChunknames; + + for (iX = 0; iX < pDROP->iCount; iX++) + { + mng_put_uint32 (pTemp1, (mng_uint32)*pTemp2); + + pTemp2++; + pTemp1 += 4; + } + /* and write it */ + iRetcode = write_raw_chunk (pData, pDROP->sHeader.iChunkname, + iRawlen, pRawdata); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_DROP, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_DBYK +WRITE_CHUNK (mng_write_dbyk) +{ + mng_dbykp pDBYK; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_DBYK, MNG_LC_START); +#endif + + pDBYK = (mng_dbykp)pChunk; /* address the proper chunk */ + + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 5 + pDBYK->iKeywordssize; + /* fill the output buffer */ + mng_put_uint32 (pRawdata, pDBYK->iChunkname); + *(pRawdata+4) = pDBYK->iPolarity; + + if (pDBYK->iKeywordssize) + MNG_COPY (pRawdata+5, pDBYK->zKeywords, pDBYK->iKeywordssize); + /* and write it */ + iRetcode = write_raw_chunk (pData, pDBYK->sHeader.iChunkname, + iRawlen, pRawdata); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_DBYK, MNG_LC_END); +#endif + + return iRetcode; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_ORDR +WRITE_CHUNK (mng_write_ordr) +{ + mng_ordrp pORDR; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + mng_uint8p pTemp; + mng_ordr_entryp pEntry; + mng_uint32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_ORDR, MNG_LC_START); +#endif + + pORDR = (mng_ordrp)pChunk; /* address the proper chunk */ + + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = pORDR->iCount * 5; + + pTemp = pRawdata; /* fill the output buffer */ + pEntry = pORDR->pEntries; + + for (iX = 0; iX < pORDR->iCount; iX++) + { + mng_put_uint32 (pTemp, pEntry->iChunkname); + *(pTemp+4) = pEntry->iOrdertype; + pTemp += 5; + pEntry++; + } + /* and write it */ + iRetcode = write_raw_chunk (pData, pORDR->sHeader.iChunkname, + iRawlen, pRawdata); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_ORDR, MNG_LC_END); +#endif + + return iRetcode; +} +#endif +#endif + +/* ************************************************************************** */ + +WRITE_CHUNK (mng_write_magn) +{ + mng_magnp pMAGN; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_MAGN, MNG_LC_START); +#endif + + pMAGN = (mng_magnp)pChunk; /* address the proper chunk */ + + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 18; + /* fill the output buffer */ + mng_put_uint16 (pRawdata, pMAGN->iFirstid); + mng_put_uint16 (pRawdata+2, pMAGN->iLastid); + *(pRawdata+4) = pMAGN->iMethodX; + mng_put_uint16 (pRawdata+5, pMAGN->iMX); + mng_put_uint16 (pRawdata+7, pMAGN->iMY); + mng_put_uint16 (pRawdata+9, pMAGN->iML); + mng_put_uint16 (pRawdata+11, pMAGN->iMR); + mng_put_uint16 (pRawdata+13, pMAGN->iMT); + mng_put_uint16 (pRawdata+15, pMAGN->iMB); + *(pRawdata+17) = pMAGN->iMethodY; + /* optimize length */ + if (pMAGN->iMethodY == pMAGN->iMethodX) + { + iRawlen--; + + if (pMAGN->iMB == pMAGN->iMY) + { + iRawlen -= 2; + + if (pMAGN->iMT == pMAGN->iMY) + { + iRawlen -= 2; + + if (pMAGN->iMR == pMAGN->iMX) + { + iRawlen -= 2; + + if (pMAGN->iML == pMAGN->iMX) + { + iRawlen -= 2; + + if (pMAGN->iMY == pMAGN->iMX) + { + iRawlen -= 2; + + if (pMAGN->iMX == 1) + { + iRawlen -= 2; + + if (pMAGN->iMethodX == 0) + { + iRawlen--; + + if (pMAGN->iLastid == pMAGN->iFirstid) + { + iRawlen -= 2; + + if (pMAGN->iFirstid == 0) + iRawlen = 0; + + } + } + } + } + } + } + } + } + } + /* and write it */ + iRetcode = write_raw_chunk (pData, pMAGN->sHeader.iChunkname, + iRawlen, pRawdata); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_MAGN, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_MPNG_PROPOSAL +WRITE_CHUNK (mng_write_mpng) +{ + mng_mpngp pMPNG; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + mng_uint8p pBuf = 0; + mng_uint32 iBuflen; + mng_uint32 iReallen; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_MPNG, MNG_LC_START); +#endif + + pMPNG = (mng_mpngp)pChunk; /* address the proper chunk */ + /* compress the frame structures */ + iRetcode = deflate_buffer (pData, (mng_uint8p)pMPNG->pFrames, pMPNG->iFramessize, + &pBuf, &iBuflen, &iReallen); + + if (!iRetcode) /* all ok ? */ + { + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 15 + iReallen; + /* requires large buffer ? */ + if (iRawlen > pData->iWritebufsize) + MNG_ALLOC (pData, pRawdata, iRawlen); + /* fill the buffer */ + mng_put_uint32 (pRawdata, pMPNG->iFramewidth); + mng_put_uint32 (pRawdata+4, pMPNG->iFrameheight); + mng_put_uint16 (pRawdata+8, pMPNG->iNumplays); + mng_put_uint16 (pRawdata+10, pMPNG->iTickspersec); + *(pRawdata+12) = pMPNG->iCompressionmethod; + + if (iReallen) + MNG_COPY (pRawdata+13, pBuf, iReallen); + /* and write it */ + iRetcode = write_raw_chunk (pData, pMPNG->sHeader.iChunkname, + iRawlen, pRawdata); + /* drop the temp buffer ? */ + if (iRawlen > pData->iWritebufsize) + MNG_FREEX (pData, pRawdata, iRawlen); + } + + MNG_FREEX (pData, pBuf, iBuflen); /* always drop the compression buffer */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_MPNG, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_ANG_PROPOSAL +WRITE_CHUNK (mng_write_ahdr) +{ + mng_ahdrp pAHDR; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_AHDR, MNG_LC_START); +#endif + + pAHDR = (mng_ahdrp)pChunk; /* address the proper chunk */ + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 22; + /* fill the buffer */ + mng_put_uint32 (pRawdata, pAHDR->iNumframes); + mng_put_uint32 (pRawdata+4, pAHDR->iTickspersec); + mng_put_uint32 (pRawdata+8, pAHDR->iNumplays); + mng_put_uint32 (pRawdata+12, pAHDR->iTilewidth); + mng_put_uint32 (pRawdata+16, pAHDR->iTileheight); + *(pRawdata+20) = pAHDR->iInterlace; + *(pRawdata+21) = pAHDR->iStillused; + /* and write it */ + iRetcode = write_raw_chunk (pData, pAHDR->sHeader.iChunkname, + iRawlen, pRawdata); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_AHDR, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_ANG_PROPOSAL +WRITE_CHUNK (mng_write_adat) +{ + + /* TODO: something */ + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_evNT +WRITE_CHUNK (mng_write_evnt) +{ + mng_evntp pEVNT; + mng_uint8p pRawdata; + mng_uint32 iRawlen; + mng_retcode iRetcode; + mng_evnt_entryp pEntry; + mng_uint8p pTemp; + mng_uint32 iX; + mng_uint32 iNamesize; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_EVNT, MNG_LC_START); +#endif + + pEVNT = (mng_evntp)pChunk; /* address the proper chunk */ + + if (!pEVNT->iCount) /* empty ? */ + iRetcode = write_raw_chunk (pData, pEVNT->sHeader.iChunkname, 0, 0); + else + { + pRawdata = pData->pWritebuf+8; /* init output buffer & size */ + iRawlen = 0; + pTemp = pRawdata; + pEntry = pEVNT->pEntries; + + for (iX = 0; iX < pEVNT->iCount; iX++) + { + if (iX) /* put separator null-byte, except the first */ + { + *pTemp = 0; + pTemp++; + iRawlen++; + } + + *pTemp = pEntry->iEventtype; + *(pTemp+1) = pEntry->iMasktype; + pTemp += 2; + iRawlen += 2; + + switch (pEntry->iMasktype) + { + case 1 : + { + mng_put_int32 (pTemp, pEntry->iLeft); + mng_put_int32 (pTemp+4, pEntry->iRight); + mng_put_int32 (pTemp+8, pEntry->iTop); + mng_put_int32 (pTemp+12, pEntry->iBottom); + pTemp += 16; + iRawlen += 16; + break; + } + case 2 : + { + mng_put_uint16 (pTemp, pEntry->iObjectid); + pTemp += 2; + iRawlen += 2; + break; + } + case 3 : + { + mng_put_uint16 (pTemp, pEntry->iObjectid); + *(pTemp+2) = pEntry->iIndex; + pTemp += 3; + iRawlen += 3; + break; + } + case 4 : + { + mng_put_int32 (pTemp, pEntry->iLeft); + mng_put_int32 (pTemp+4, pEntry->iRight); + mng_put_int32 (pTemp+8, pEntry->iTop); + mng_put_int32 (pTemp+12, pEntry->iBottom); + mng_put_uint16 (pTemp+16, pEntry->iObjectid); + pTemp += 18; + iRawlen += 18; + break; + } + case 5 : + { + mng_put_int32 (pTemp, pEntry->iLeft); + mng_put_int32 (pTemp+4, pEntry->iRight); + mng_put_int32 (pTemp+8, pEntry->iTop); + mng_put_int32 (pTemp+12, pEntry->iBottom); + mng_put_uint16 (pTemp+16, pEntry->iObjectid); + *(pTemp+18) = pEntry->iIndex; + pTemp += 19; + iRawlen += 19; + break; + } + } + + iNamesize = pEntry->iSegmentnamesize; + + if (iNamesize) + { + MNG_COPY (pTemp, pEntry->zSegmentname, iNamesize); + pTemp += iNamesize; + iRawlen += iNamesize; + } + + pEntry++; + } + /* and write it */ + iRetcode = write_raw_chunk (pData, pEVNT->sHeader.iChunkname, + iRawlen, pRawdata); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_EVNT, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +WRITE_CHUNK (mng_write_unknown) +{ + mng_unknown_chunkp pUnknown; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_UNKNOWN, MNG_LC_START); +#endif + /* address the proper chunk */ + pUnknown = (mng_unknown_chunkp)pChunk; + /* and write it */ + iRetcode = write_raw_chunk (pData, pUnknown->sHeader.iChunkname, + pUnknown->iDatasize, pUnknown->pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_UNKNOWN, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_WRITE_PROCS */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ + diff --git a/Engine/lib/lmng/libmng_chunk_io.h b/Engine/lib/lmng/libmng_chunk_io.h new file mode 100644 index 000000000..f8505baec --- /dev/null +++ b/Engine/lib/lmng/libmng_chunk_io.h @@ -0,0 +1,415 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_chunk_io.h copyright (c) 2000-2007 G.Juyn * */ +/* * version : 1.0.109 * */ +/* * * */ +/* * purpose : Chunk I/O routines (definition) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : Definition of the chunk input/output routines * */ +/* * * */ +/* * changes : 0.5.1 - 05/04/2000 - G.Juyn * */ +/* * - changed CRC initialization to use dynamic structure * */ +/* * (wasn't thread-safe the old way !) * */ +/* * 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed write routines definition * */ +/* * - changed strict-ANSI stuff * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 0.9.3 - 08/26/2000 - G.Juyn * */ +/* * - added MAGN chunk * */ +/* * 0.9.3 - 10/16/2000 - G.Juyn * */ +/* * - added support for JDAA * */ +/* * * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * 1.0.5 - 09/14/2002 - G.Juyn * */ +/* * - added event handling for dynamic MNG * */ +/* * * */ +/* * 1.0.6 - 07/07/2003 - G.R-P * */ +/* * - added SKIP_CHUNK and NO_DELTA_PNG support * */ +/* * 1.0.6 - 07/29/2003 - G.R-P * */ +/* * - added conditionals around PAST chunk support * */ +/* * * */ +/* * 1.0.7 - 03/24/2004 - G.R-P * */ +/* * - fixed SKIPCHUNK_itXT and SKIPCHUNK_ztXT typos * */ +/* * * */ +/* * 1.0.9 - 12/07/2004 - G.Juyn * */ +/* * - added conditional MNG_OPTIMIZE_CHUNKREADER * */ +/* * * */ +/* * 1.0.10 - 04/08/2007 - G.Juyn * */ +/* * - added support for mPNG proposal * */ +/* * 1.0.10 - 04/12/2007 - G.Juyn * */ +/* * - added support for ANG proposal * */ +/* * * */ +/* ************************************************************************** */ + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +#ifndef _libmng_chunk_io_h_ +#define _libmng_chunk_io_h_ + +/* ************************************************************************** */ + +mng_uint32 mng_crc (mng_datap pData, + mng_uint8p buf, + mng_int32 len); + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_READ_PROCS + +/* ************************************************************************** */ + +mng_retcode mng_inflate_buffer (mng_datap pData, + mng_uint8p pInbuf, + mng_uint32 iInsize, + mng_uint8p *pOutbuf, + mng_uint32 *iOutsize, + mng_uint32 *iRealsize); + +/* ************************************************************************** */ + +#define READ_CHUNK(n) mng_retcode n (mng_datap pData, \ + mng_chunkp pHeader, \ + mng_uint32 iRawlen, \ + mng_uint8p pRawdata, \ + mng_chunkp* ppChunk) + +#ifdef MNG_OPTIMIZE_CHUNKREADER +READ_CHUNK (mng_read_general) ; +#endif + +READ_CHUNK (mng_read_ihdr) ; +READ_CHUNK (mng_read_plte) ; +READ_CHUNK (mng_read_idat) ; +READ_CHUNK (mng_read_iend) ; +READ_CHUNK (mng_read_trns) ; +READ_CHUNK (mng_read_gama) ; +READ_CHUNK (mng_read_chrm) ; +READ_CHUNK (mng_read_srgb) ; +#ifndef MNG_SKIPCHUNK_iCCP +READ_CHUNK (mng_read_iccp) ; +#endif +#ifndef MNG_SKIPCHUNK_tEXt +READ_CHUNK (mng_read_text) ; +#endif +#ifndef MNG_SKIPCHUNK_zTXt +READ_CHUNK (mng_read_ztxt) ; +#endif +#ifndef MNG_SKIPCHUNK_iTXt +READ_CHUNK (mng_read_itxt) ; +#endif +#ifndef MNG_SKIPCHUNK_bKGD +READ_CHUNK (mng_read_bkgd) ; +#endif +#ifndef MNG_SKIPCHUNK_pHYs +READ_CHUNK (mng_read_phys) ; +#endif +#ifndef MNG_SKIPCHUNK_sBIT +READ_CHUNK (mng_read_sbit) ; +#endif +#ifndef MNG_SKIPCHUNK_sPLT +READ_CHUNK (mng_read_splt) ; +#endif +#ifndef MNG_SKIPCHUNK_hIST +READ_CHUNK (mng_read_hist) ; +#endif +#ifndef MNG_SKIPCHUNK_tIME +READ_CHUNK (mng_read_time) ; +#endif +READ_CHUNK (mng_read_mhdr) ; +READ_CHUNK (mng_read_mend) ; +READ_CHUNK (mng_read_loop) ; +READ_CHUNK (mng_read_endl) ; +READ_CHUNK (mng_read_defi) ; +READ_CHUNK (mng_read_basi) ; +READ_CHUNK (mng_read_clon) ; +#ifndef MNG_SKIPCHUNK_PAST +READ_CHUNK (mng_read_past) ; +#endif +READ_CHUNK (mng_read_disc) ; +READ_CHUNK (mng_read_back) ; +READ_CHUNK (mng_read_fram) ; +READ_CHUNK (mng_read_move) ; +READ_CHUNK (mng_read_clip) ; +READ_CHUNK (mng_read_show) ; +READ_CHUNK (mng_read_term) ; +READ_CHUNK (mng_read_save) ; +READ_CHUNK (mng_read_seek) ; +#ifndef MNG_SKIPCHUNK_eXPI +READ_CHUNK (mng_read_expi) ; +#endif +#ifndef MNG_SKIPCHUNK_fPRI +READ_CHUNK (mng_read_fpri) ; +#endif +#ifndef MNG_SKIPCHUNK_pHYg +READ_CHUNK (mng_read_phyg) ; +#endif +#ifdef MNG_INCLUDE_JNG +READ_CHUNK (mng_read_jhdr) ; +READ_CHUNK (mng_read_jdaa) ; +READ_CHUNK (mng_read_jdat) ; +READ_CHUNK (mng_read_jsep) ; +#endif +#ifndef MNG_NO_DELTA_PNG +READ_CHUNK (mng_read_dhdr) ; +READ_CHUNK (mng_read_prom) ; +READ_CHUNK (mng_read_ipng) ; +READ_CHUNK (mng_read_pplt) ; +#ifdef MNG_INCLUDE_JNG +READ_CHUNK (mng_read_ijng) ; +#endif +READ_CHUNK (mng_read_drop) ; +READ_CHUNK (mng_read_dbyk) ; +READ_CHUNK (mng_read_ordr) ; +#endif +READ_CHUNK (mng_read_magn) ; +#ifndef MNG_SKIPCHUNK_nEED +READ_CHUNK (mng_read_need) ; +#endif +#ifdef MNG_INCLUDE_MPNG_PROPOSAL +READ_CHUNK (mng_read_mpng) ; +#endif +#ifndef MNG_SKIPCHUNK_evNT +READ_CHUNK (mng_read_evnt) ; +#endif +READ_CHUNK (mng_read_unknown) ; + +/* ************************************************************************** */ + +#else /* MNG_INCLUDE_READ_PROCS */ +#define mng_read_ihdr 0 +#define mng_read_plte 0 +#define mng_read_idat 0 +#define mng_read_iend 0 +#define mng_read_trns 0 +#define mng_read_gama 0 +#define mng_read_chrm 0 +#define mng_read_srgb 0 +#define mng_read_iccp 0 +#define mng_read_text 0 +#define mng_read_ztxt 0 +#define mng_read_itxt 0 +#define mng_read_bkgd 0 +#define mng_read_phys 0 +#define mng_read_sbit 0 +#define mng_read_splt 0 +#define mng_read_hist 0 +#define mng_read_time 0 +#define mng_read_mhdr 0 +#define mng_read_mend 0 +#define mng_read_loop 0 +#define mng_read_endl 0 +#define mng_read_defi 0 +#define mng_read_basi 0 +#define mng_read_clon 0 +#ifndef MNG_SKIPCHUNK_PAST +#define mng_read_past 0 +#endif +#define mng_read_disc 0 +#define mng_read_back 0 +#define mng_read_fram 0 +#define mng_read_move 0 +#define mng_read_clip 0 +#define mng_read_show 0 +#define mng_read_term 0 +#define mng_read_save 0 +#define mng_read_seek 0 +#define mng_read_expi 0 +#define mng_read_fpri 0 +#define mng_read_phyg 0 +#ifdef MNG_INCLUDE_JNG +#define mng_read_jhdr 0 +#define mng_read_jdaa 0 +#define mng_read_jdat 0 +#define mng_read_jsep 0 +#endif +#ifndef MNG_NO_DELTA_PNG +#define mng_read_dhdr 0 +#define mng_read_prom 0 +#define mng_read_ipng 0 +#define mng_read_pplt 0 +#ifdef MNG_INCLUDE_JNG +#define mng_read_ijng 0 +#endif +#define mng_read_drop 0 +#define mng_read_dbyk 0 +#define mng_read_ordr 0 +#endif +#define mng_read_magn 0 +#define mng_read_need 0 +#ifdef MNG_INCLUDE_MPNG_PROPOSAL +#define mng_read_mpng 0 +#endif +#define mng_read_evnt 0 +#define mng_read_unknown 0 +#endif /* MNG_INCLUDE_READ_PROCS */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_WRITE_PROCS + +#define WRITE_CHUNK(n) mng_retcode n (mng_datap pData, \ + mng_chunkp pChunk) + +WRITE_CHUNK (mng_write_ihdr) ; +WRITE_CHUNK (mng_write_plte) ; +WRITE_CHUNK (mng_write_idat) ; +WRITE_CHUNK (mng_write_iend) ; +WRITE_CHUNK (mng_write_trns) ; +WRITE_CHUNK (mng_write_gama) ; +WRITE_CHUNK (mng_write_chrm) ; +WRITE_CHUNK (mng_write_srgb) ; +WRITE_CHUNK (mng_write_iccp) ; +WRITE_CHUNK (mng_write_text) ; +WRITE_CHUNK (mng_write_ztxt) ; +WRITE_CHUNK (mng_write_itxt) ; +WRITE_CHUNK (mng_write_bkgd) ; +WRITE_CHUNK (mng_write_phys) ; +WRITE_CHUNK (mng_write_sbit) ; +WRITE_CHUNK (mng_write_splt) ; +WRITE_CHUNK (mng_write_hist) ; +WRITE_CHUNK (mng_write_time) ; +WRITE_CHUNK (mng_write_mhdr) ; +WRITE_CHUNK (mng_write_mend) ; +WRITE_CHUNK (mng_write_loop) ; +WRITE_CHUNK (mng_write_endl) ; +WRITE_CHUNK (mng_write_defi) ; +WRITE_CHUNK (mng_write_basi) ; +WRITE_CHUNK (mng_write_clon) ; +#ifndef MNG_SKIPCHUNK_PAST +WRITE_CHUNK (mng_write_past) ; +#endif +WRITE_CHUNK (mng_write_disc) ; +WRITE_CHUNK (mng_write_back) ; +WRITE_CHUNK (mng_write_fram) ; +WRITE_CHUNK (mng_write_move) ; +WRITE_CHUNK (mng_write_clip) ; +WRITE_CHUNK (mng_write_show) ; +WRITE_CHUNK (mng_write_term) ; +WRITE_CHUNK (mng_write_save) ; +WRITE_CHUNK (mng_write_seek) ; +WRITE_CHUNK (mng_write_expi) ; +WRITE_CHUNK (mng_write_fpri) ; +WRITE_CHUNK (mng_write_phyg) ; +#ifdef MNG_INCLUDE_JNG +WRITE_CHUNK (mng_write_jhdr) ; +WRITE_CHUNK (mng_write_jdaa) ; +WRITE_CHUNK (mng_write_jdat) ; +WRITE_CHUNK (mng_write_jsep) ; +#endif +#ifndef MNG_NO_DELTA_PNG +WRITE_CHUNK (mng_write_dhdr) ; +WRITE_CHUNK (mng_write_prom) ; +WRITE_CHUNK (mng_write_ipng) ; +WRITE_CHUNK (mng_write_pplt) ; +#ifdef MNG_INCLUDE_JNG +WRITE_CHUNK (mng_write_ijng) ; +#endif +WRITE_CHUNK (mng_write_drop) ; +WRITE_CHUNK (mng_write_dbyk) ; +WRITE_CHUNK (mng_write_ordr) ; +#endif +WRITE_CHUNK (mng_write_magn) ; +WRITE_CHUNK (mng_write_need) ; +#ifdef MNG_INCLUDE_MPNG_PROPOSAL +WRITE_CHUNK (mng_write_mpng) ; +#endif +#ifdef MNG_INCLUDE_ANG_PROPOSAL +WRITE_CHUNK (mng_write_ahdr) ; +WRITE_CHUNK (mng_write_adat) ; +#endif +WRITE_CHUNK (mng_write_evnt) ; +WRITE_CHUNK (mng_write_unknown) ; + +/* ************************************************************************** */ + +#else /* MNG_INCLUDE_WRITE_PROCS */ +#define mng_write_ihdr 0 +#define mng_write_plte 0 +#define mng_write_idat 0 +#define mng_write_iend 0 +#define mng_write_trns 0 +#define mng_write_gama 0 +#define mng_write_chrm 0 +#define mng_write_srgb 0 +#define mng_write_iccp 0 +#define mng_write_text 0 +#define mng_write_ztxt 0 +#define mng_write_itxt 0 +#define mng_write_bkgd 0 +#define mng_write_phys 0 +#define mng_write_sbit 0 +#define mng_write_splt 0 +#define mng_write_hist 0 +#define mng_write_time 0 +#define mng_write_mhdr 0 +#define mng_write_mend 0 +#define mng_write_loop 0 +#define mng_write_endl 0 +#define mng_write_defi 0 +#define mng_write_basi 0 +#define mng_write_clon 0 +#ifndef MNG_SKIPCHUNK_PAST +#define mng_write_past 0 +#endif +#define mng_write_disc 0 +#define mng_write_back 0 +#define mng_write_fram 0 +#define mng_write_move 0 +#define mng_write_clip 0 +#define mng_write_show 0 +#define mng_write_term 0 +#define mng_write_save 0 +#define mng_write_seek 0 +#define mng_write_expi 0 +#define mng_write_fpri 0 +#define mng_write_phyg 0 +#ifdef MNG_INCLUDE_JNG +#define mng_write_jhdr 0 +#define mng_write_jdaa 0 +#define mng_write_jdat 0 +#define mng_write_jsep 0 +#endif +#ifndef MNG_NO_DELTA_PNG +#define mng_write_dhdr 0 +#define mng_write_prom 0 +#define mng_write_ipng 0 +#define mng_write_pplt 0 +#ifdef MNG_INCLUDE_JNG +#define mng_write_ijng 0 +#endif +#define mng_write_drop 0 +#define mng_write_dbyk 0 +#define mng_write_ordr 0 +#endif +#define mng_write_magn 0 +#define mng_write_need 0 +#ifdef MNG_INCLUDE_MPNG_PROPOSAL +#define mng_write_mpng 0 +#endif +#ifdef MNG_INCLUDE_ANG_PROPOSAL +#define mng_write_adat 0 +#define mng_write_ahdr 0 +#endif +#define mng_write_evnt 0 +#define mng_write_unknown 0 +#endif /* MNG_INCLUDE_WRITE_PROCS */ + +/* ************************************************************************** */ + +#endif /* _libmng_chunk_io_h_ */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ diff --git a/Engine/lib/lmng/libmng_chunk_prc.c b/Engine/lib/lmng/libmng_chunk_prc.c new file mode 100644 index 000000000..e633e7e26 --- /dev/null +++ b/Engine/lib/lmng/libmng_chunk_prc.c @@ -0,0 +1,4452 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_chunk_prc.c copyright (c) 2000-2005 G.Juyn * */ +/* * version : 1.0.10 * */ +/* * * */ +/* * purpose : Chunk initialization & cleanup (implementation) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : implementation of the chunk initialization & cleanup * */ +/* * routines * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed strict-ANSI stuff * */ +/* * 0.5.1 - 05/12/2000 - G.Juyn * */ +/* * - changed trace to macro for callback error-reporting * */ +/* * * */ +/* * 0.9.1 - 07/19/2000 - G.Juyn * */ +/* * - fixed creation-code * */ +/* * * */ +/* * 0.9.2 - 07/31/2000 - G.Juyn * */ +/* * - put add_chunk() inside MNG_INCLUDE_WRITE_PROCS wrapper * */ +/* * 0.9.2 - 08/01/2000 - G.Juyn * */ +/* * - wrapper for add_chunk() changed * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 0.9.3 - 08/26/2000 - G.Juyn * */ +/* * - added MAGN chunk * */ +/* * 0.9.3 - 10/16/2000 - G.Juyn * */ +/* * - added support for JDAA * */ +/* * * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * - added HLAPI function to copy chunks * */ +/* * 1.0.5 - 09/14/2002 - G.Juyn * */ +/* * - added event handling for dynamic MNG * */ +/* * 1.0.5 - 10/04/2002 - G.Juyn * */ +/* * - fixed chunk-storage for evNT chunk * */ +/* * 1.0.5 - 10/17/2002 - G.Juyn * */ +/* * - fixed issue in freeing evNT chunk * */ +/* * * */ +/* * 1.0.6 - 07/07/2003 - G.R-P * */ +/* * - added MNG_SKIPCHUNK_cHNK footprint optimizations * */ +/* * - added MNG_NO_DELTA_PNG reduction feature * */ +/* * 1.0.6 - 07/14/2003 - G.R-P * */ +/* * - added MNG_NO_LOOP_SIGNALS_SUPPORTED conditional * */ +/* * 1.0.6 - 07/29/2003 - G.R-P * */ +/* * - added conditionals around PAST chunk support * */ +/* * 1.0.6 - 08/17/2003 - G.R-P * */ +/* * - added conditionals around non-VLC chunk support * */ +/* * * */ +/* * 1.0.7 - 03/24/2004 - G.R-P * */ +/* * - fixed SKIPCHUNK_eXPI -> fPRI typo * */ +/* * * */ +/* * 1.0.9 - 09/25/2004 - G.Juyn * */ +/* * - replaced MNG_TWEAK_LARGE_FILES with permanent solution * */ +/* * 1.0.9 - 12/05/2004 - G.Juyn * */ +/* * - added conditional MNG_OPTIMIZE_CHUNKINITFREE * */ +/* * 1.0.9 - 12/06/2004 - G.Juyn * */ +/* * - added conditional MNG_OPTIMIZE_CHUNKASSIGN * */ +/* * 1.0.9 - 12/20/2004 - G.Juyn * */ +/* * - cleaned up macro-invocations (thanks to D. Airlie) * */ +/* * * */ +/* * 1.0.10 - 07/30/2005 - G.Juyn * */ +/* * - fixed problem with CLON object during readdisplay() * */ +/* * 1.0.10 - 04/08/2007 - G.Juyn * */ +/* * - added support for mPNG proposal * */ +/* * 1.0.10 - 04/12/2007 - G.Juyn * */ +/* * - added support for ANG proposal * */ +/* * * */ +/* ************************************************************************** */ + +#include "libmng.h" +#include "libmng_data.h" +#include "libmng_error.h" +#include "libmng_trace.h" +#ifdef __BORLANDC__ +#pragma hdrstop +#endif +#include "libmng_memory.h" +#include "libmng_chunks.h" +#include "libmng_chunk_prc.h" + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +/* ************************************************************************** */ +/* * * */ +/* * General chunk routines * */ +/* * * */ +/* ************************************************************************** */ + +void mng_add_chunk (mng_datap pData, + mng_chunkp pChunk) +{ + if (!pData->pFirstchunk) /* list is still empty ? */ + { + pData->pFirstchunk = pChunk; /* then this becomes the first */ + +#ifdef MNG_SUPPORT_WRITE + if (!pData->iFirstchunkadded) + { + pData->iFirstchunkadded = ((mng_chunk_headerp)pChunk)->iChunkname; +#endif + + if (((mng_chunk_headerp)pChunk)->iChunkname == MNG_UINT_IHDR) + pData->eImagetype = mng_it_png; + else +#ifdef MNG_INCLUDE_JNG + if (((mng_chunk_headerp)pChunk)->iChunkname == MNG_UINT_JHDR) + pData->eImagetype = mng_it_jng; + else +#endif + pData->eImagetype = mng_it_mng; + + pData->eSigtype = pData->eImagetype; +#ifdef MNG_SUPPORT_WRITE + } +#endif + } + else + { /* else we make appropriate links */ + ((mng_chunk_headerp)pChunk)->pPrev = pData->pLastchunk; + ((mng_chunk_headerp)pData->pLastchunk)->pNext = pChunk; + } + + pData->pLastchunk = pChunk; /* and it's always the last */ + + return; +} + +/* ************************************************************************** */ +/* * * */ +/* * Chunk specific initialization routines * */ +/* * * */ +/* ************************************************************************** */ + +#ifdef MNG_OPTIMIZE_CHUNKINITFREE +INIT_CHUNK_HDR (mng_init_general) +{ + MNG_ALLOC (pData, *ppChunk, ((mng_chunk_headerp)pHeader)->iChunksize); + MNG_COPY (*ppChunk, pHeader, sizeof (mng_chunk_header)); + return MNG_NOERROR; +} + +#else /* MNG_OPTIMIZE_CHUNKINITFREE */ + +/* ************************************************************************** */ + +INIT_CHUNK_HDR (mng_init_ihdr) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_IHDR, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_ihdr)); + ((mng_ihdrp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_IHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +INIT_CHUNK_HDR (mng_init_plte) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_PLTE, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_plte)); + ((mng_pltep)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_PLTE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +INIT_CHUNK_HDR (mng_init_idat) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_IDAT, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_idat)); + ((mng_idatp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_IDAT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +INIT_CHUNK_HDR (mng_init_iend) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_IEND, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_iend)); + ((mng_iendp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_IEND, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +INIT_CHUNK_HDR (mng_init_trns) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_TRNS, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_trns)); + ((mng_trnsp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_TRNS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_gAMA +INIT_CHUNK_HDR (mng_init_gama) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_GAMA, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_gama)); + ((mng_gamap)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_GAMA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_cHRM +INIT_CHUNK_HDR (mng_init_chrm) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_CHRM, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_chrm)); + ((mng_chrmp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_CHRM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_sRGB +INIT_CHUNK_HDR (mng_init_srgb) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_SRGB, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_srgb)); + ((mng_srgbp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_SRGB, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_iCCP +INIT_CHUNK_HDR (mng_init_iccp) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_ICCP, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_iccp)); + ((mng_iccpp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_ICCP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_tEXt +INIT_CHUNK_HDR (mng_init_text) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_TEXT, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_text)); + ((mng_textp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_TEXT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_zTXt +INIT_CHUNK_HDR (mng_init_ztxt) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_ZTXT, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_ztxt)); + ((mng_ztxtp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_ZTXT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_iTXt +INIT_CHUNK_HDR (mng_init_itxt) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_ITXT, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_itxt)); + ((mng_itxtp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_ITXT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_bKGD +INIT_CHUNK_HDR (mng_init_bkgd) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_BKGD, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_bkgd)); + ((mng_bkgdp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_BKGD, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_pHYs +INIT_CHUNK_HDR (mng_init_phys) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_PHYS, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_phys)); + ((mng_physp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_PHYS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_sBIT +INIT_CHUNK_HDR (mng_init_sbit) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_SBIT, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_sbit)); + ((mng_sbitp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_SBIT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_sPLT +INIT_CHUNK_HDR (mng_init_splt) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_SPLT, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_splt)); + ((mng_spltp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_SPLT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_hIST +INIT_CHUNK_HDR (mng_init_hist) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_HIST, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_hist)); + ((mng_histp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_HIST, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_tIME +INIT_CHUNK_HDR (mng_init_time) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_TIME, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_time)); + ((mng_timep)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_TIME, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +INIT_CHUNK_HDR (mng_init_mhdr) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_MHDR, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_mhdr)); + ((mng_mhdrp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_MHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +INIT_CHUNK_HDR (mng_init_mend) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_MEND, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_mend)); + ((mng_mendp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_MEND, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_LOOP +INIT_CHUNK_HDR (mng_init_loop) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_LOOP, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_loop)); + ((mng_loopp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_LOOP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +INIT_CHUNK_HDR (mng_init_endl) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_ENDL, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_endl)); + ((mng_endlp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_ENDL, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_DEFI +INIT_CHUNK_HDR (mng_init_defi) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_DEFI, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_defi)); + ((mng_defip)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_DEFI, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_BASI +INIT_CHUNK_HDR (mng_init_basi) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_BASI, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_basi)); + ((mng_basip)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_BASI, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_CLON +INIT_CHUNK_HDR (mng_init_clon) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_CLON, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_clon)); + ((mng_clonp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_CLON, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_PAST +INIT_CHUNK_HDR (mng_init_past) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_PAST, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_past)); + ((mng_pastp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_PAST, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_DISC +INIT_CHUNK_HDR (mng_init_disc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_DISC, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_disc)); + ((mng_discp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_DISC, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_BACK +INIT_CHUNK_HDR (mng_init_back) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_BACK, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_back)); + ((mng_backp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_BACK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_FRAM +INIT_CHUNK_HDR (mng_init_fram) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_FRAM, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_fram)); + ((mng_framp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_FRAM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_MOVE +INIT_CHUNK_HDR (mng_init_move) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_MOVE, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_move)); + ((mng_movep)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_MOVE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_CLIP +INIT_CHUNK_HDR (mng_init_clip) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_CLIP, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_clip)); + ((mng_clipp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_CLIP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SHOW +INIT_CHUNK_HDR (mng_init_show) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_SHOW, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_show)); + ((mng_showp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_SHOW, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_TERM +INIT_CHUNK_HDR (mng_init_term) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_TERM, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_term)); + ((mng_termp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_TERM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SAVE +INIT_CHUNK_HDR (mng_init_save) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_SAVE, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_save)); + ((mng_savep)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_SAVE, MNG_LC_END); +#endif + + return MNG_NOERROR; + +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SEEK +INIT_CHUNK_HDR (mng_init_seek) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_SEEK, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_seek)); + ((mng_seekp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_SEEK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_eXPI +INIT_CHUNK_HDR (mng_init_expi) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_EXPI, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_expi)); + ((mng_expip)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_EXPI, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_fPRI +INIT_CHUNK_HDR (mng_init_fpri) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_FPRI, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_fpri)); + ((mng_fprip)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_FPRI, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_nEED +INIT_CHUNK_HDR (mng_init_need) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_NEED, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_need)); + ((mng_needp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_NEED, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_pHYg +INIT_CHUNK_HDR (mng_init_phyg) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_PHYG, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_phyg)); + ((mng_phygp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_PHYG, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +INIT_CHUNK_HDR (mng_init_jhdr) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_JHDR, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_jhdr)); + ((mng_jhdrp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_JHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +INIT_CHUNK_HDR (mng_init_jdaa) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_JDAA, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_jdaa)); + ((mng_jdaap)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_JDAA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +INIT_CHUNK_HDR (mng_init_jdat) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_JDAT, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_jdat)); + ((mng_jdatp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_JDAT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +INIT_CHUNK_HDR (mng_init_jsep) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_JSEP, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_jsep)); + ((mng_jsepp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_JSEP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +INIT_CHUNK_HDR (mng_init_dhdr) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_DHDR, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_dhdr)); + ((mng_dhdrp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_DHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +INIT_CHUNK_HDR (mng_init_prom) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_PROM, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_prom)); + ((mng_promp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_PROM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +INIT_CHUNK_HDR (mng_init_ipng) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_IPNG, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_ipng)); + ((mng_ipngp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_IPNG, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +INIT_CHUNK_HDR (mng_init_pplt) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_PPLT, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_pplt)); + ((mng_ppltp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_PPLT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifdef MNG_INCLUDE_JNG +INIT_CHUNK_HDR (mng_init_ijng) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_IJNG, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_ijng)); + ((mng_ijngp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_IJNG, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +INIT_CHUNK_HDR (mng_init_drop) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_DROP, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_drop)); + ((mng_dropp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_DROP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_DBYK +INIT_CHUNK_HDR (mng_init_dbyk) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_DBYK, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_dbyk)); + ((mng_dbykp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_DBYK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_ORDR +INIT_CHUNK_HDR (mng_init_ordr) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_ORDR, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_ordr)); + ((mng_ordrp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_ORDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_MAGN +INIT_CHUNK_HDR (mng_init_magn) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_MAGN, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_magn)); + ((mng_magnp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_MAGN, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_evNT +INIT_CHUNK_HDR (mng_init_evnt) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_EVNT, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_evnt)); + ((mng_evntp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_EVNT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +INIT_CHUNK_HDR (mng_init_unknown) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_UNKNOWN, MNG_LC_START); +#endif + + MNG_ALLOC (pData, *ppChunk, sizeof (mng_unknown_chunk)); + ((mng_unknown_chunkp)*ppChunk)->sHeader = *((mng_chunk_headerp)pHeader); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_UNKNOWN, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#endif /* MNG_OPTIMIZE_CHUNKINITFREE */ + +/* ************************************************************************** */ +/* * * */ +/* * Chunk specific cleanup routines * */ +/* * * */ +/* ************************************************************************** */ + +#ifdef MNG_OPTIMIZE_CHUNKINITFREE +FREE_CHUNK_HDR (mng_free_general) +{ + MNG_FREEX (pData, pHeader, ((mng_chunk_headerp)pHeader)->iChunksize); + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +FREE_CHUNK_HDR (mng_free_ihdr) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_IHDR, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_ihdr)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_IHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +FREE_CHUNK_HDR (mng_free_plte) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_PLTE, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_plte)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_PLTE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +FREE_CHUNK_HDR (mng_free_idat) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_IDAT, MNG_LC_START); +#endif + + if (((mng_idatp)pHeader)->iDatasize) + MNG_FREEX (pData, ((mng_idatp)pHeader)->pData, + ((mng_idatp)pHeader)->iDatasize); + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + MNG_FREEX (pData, pHeader, sizeof (mng_idat)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_IDAT, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + return MNG_NOERROR; +#else + return mng_free_general(pData, pHeader); +#endif +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +FREE_CHUNK_HDR (mng_free_iend) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_IEND, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_iend)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_IEND, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +FREE_CHUNK_HDR (mng_free_trns) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_TRNS, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_trns)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_TRNS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +#ifndef MNG_SKIPCHUNK_gAMA +FREE_CHUNK_HDR (mng_free_gama) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_GAMA, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_gama)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_GAMA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +#ifndef MNG_SKIPCHUNK_cHRM +FREE_CHUNK_HDR (mng_free_chrm) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_CHRM, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_chrm)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_CHRM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +#ifndef MNG_SKIPCHUNK_sRGB +FREE_CHUNK_HDR (mng_free_srgb) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_SRGB, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_srgb)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_SRGB, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_iCCP +FREE_CHUNK_HDR (mng_free_iccp) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ICCP, MNG_LC_START); +#endif + + if (((mng_iccpp)pHeader)->iNamesize) + MNG_FREEX (pData, ((mng_iccpp)pHeader)->zName, + ((mng_iccpp)pHeader)->iNamesize + 1); + + if (((mng_iccpp)pHeader)->iProfilesize) + MNG_FREEX (pData, ((mng_iccpp)pHeader)->pProfile, + ((mng_iccpp)pHeader)->iProfilesize); + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + MNG_FREEX (pData, pHeader, sizeof (mng_iccp)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ICCP, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + return MNG_NOERROR; +#else + return mng_free_general(pData, pHeader); +#endif +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_tEXt +FREE_CHUNK_HDR (mng_free_text) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_TEXT, MNG_LC_START); +#endif + + if (((mng_textp)pHeader)->iKeywordsize) + MNG_FREEX (pData, ((mng_textp)pHeader)->zKeyword, + ((mng_textp)pHeader)->iKeywordsize + 1); + + if (((mng_textp)pHeader)->iTextsize) + MNG_FREEX (pData, ((mng_textp)pHeader)->zText, + ((mng_textp)pHeader)->iTextsize + 1); + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + MNG_FREEX (pData, pHeader, sizeof (mng_text)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_TEXT, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + return MNG_NOERROR; +#else + return mng_free_general(pData, pHeader); +#endif +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_zTXt +FREE_CHUNK_HDR (mng_free_ztxt) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ZTXT, MNG_LC_START); +#endif + + if (((mng_ztxtp)pHeader)->iKeywordsize) + MNG_FREEX (pData, ((mng_ztxtp)pHeader)->zKeyword, + ((mng_ztxtp)pHeader)->iKeywordsize + 1); + + if (((mng_ztxtp)pHeader)->iTextsize) + MNG_FREEX (pData, ((mng_ztxtp)pHeader)->zText, + ((mng_ztxtp)pHeader)->iTextsize); + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + MNG_FREEX (pData, pHeader, sizeof (mng_ztxt)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ZTXT, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + return MNG_NOERROR; +#else + return mng_free_general(pData, pHeader); +#endif +} +#endif + +/* ************************************************************************** */ +#ifndef MNG_SKIPCHUNK_iTXt +FREE_CHUNK_HDR (mng_free_itxt) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ITXT, MNG_LC_START); +#endif + + if (((mng_itxtp)pHeader)->iKeywordsize) + MNG_FREEX (pData, ((mng_itxtp)pHeader)->zKeyword, + ((mng_itxtp)pHeader)->iKeywordsize + 1); + + if (((mng_itxtp)pHeader)->iLanguagesize) + MNG_FREEX (pData, ((mng_itxtp)pHeader)->zLanguage, + ((mng_itxtp)pHeader)->iLanguagesize + 1); + + if (((mng_itxtp)pHeader)->iTranslationsize) + MNG_FREEX (pData, ((mng_itxtp)pHeader)->zTranslation, + ((mng_itxtp)pHeader)->iTranslationsize + 1); + + if (((mng_itxtp)pHeader)->iTextsize) + MNG_FREEX (pData, ((mng_itxtp)pHeader)->zText, + ((mng_itxtp)pHeader)->iTextsize); + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + MNG_FREEX (pData, pHeader, sizeof (mng_itxt)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ITXT, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + return MNG_NOERROR; +#else + return mng_free_general(pData, pHeader); +#endif +} +#endif + +/* ************************************************************************** */ +#ifdef MNG_INCLUDE_MPNG_PROPOSAL +FREE_CHUNK_HDR (mng_free_mpng) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_MPNG, MNG_LC_START); +#endif + + if (((mng_mpngp)pHeader)->iFramessize) + MNG_FREEX (pData, ((mng_mpngp)pHeader)->pFrames, + ((mng_mpngp)pHeader)->iFramessize); + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + MNG_FREEX (pData, pHeader, sizeof (mng_mpng)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_MPNG, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + return MNG_NOERROR; +#else + return mng_free_general(pData, pHeader); +#endif +} +#endif + +/* ************************************************************************** */ +#ifdef MNG_INCLUDE_ANG_PROPOSAL +FREE_CHUNK_HDR (mng_free_adat) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ADAT, MNG_LC_START); +#endif + + if (((mng_adatp)pHeader)->iTilessize) + MNG_FREEX (pData, ((mng_adatp)pHeader)->pTiles, ((mng_adatp)pHeader)->iTilessize); + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + MNG_FREEX (pData, pHeader, sizeof (mng_adat)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ADAT, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + return MNG_NOERROR; +#else + return mng_free_general(pData, pHeader); +#endif +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +#ifndef MNG_SKIPCHUNK_bKGD +FREE_CHUNK_HDR (mng_free_bkgd) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_BKGD, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_bkgd)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_BKGD, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +#ifndef MNG_SKIPCHUNK_pHYs +FREE_CHUNK_HDR (mng_free_phys) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_PHYS, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_phys)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_PHYS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +#ifndef MNG_SKIPCHUNK_sBIT +FREE_CHUNK_HDR (mng_free_sbit) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_SBIT, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_sbit)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_SBIT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_sPLT +FREE_CHUNK_HDR (mng_free_splt) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_SPLT, MNG_LC_START); +#endif + + if (((mng_spltp)pHeader)->iNamesize) + MNG_FREEX (pData, ((mng_spltp)pHeader)->zName, + ((mng_spltp)pHeader)->iNamesize + 1); + + if (((mng_spltp)pHeader)->iEntrycount) + MNG_FREEX (pData, ((mng_spltp)pHeader)->pEntries, + ((mng_spltp)pHeader)->iEntrycount * + (((mng_spltp)pHeader)->iSampledepth * 3 + sizeof (mng_uint16)) ); + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + MNG_FREEX (pData, pHeader, sizeof (mng_splt)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_SPLT, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + return MNG_NOERROR; +#else + return mng_free_general(pData, pHeader); +#endif +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +#ifndef MNG_SKIPCHUNK_hIST +FREE_CHUNK_HDR (mng_free_hist) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_HIST, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_hist)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_HIST, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +#ifndef MNG_SKIPCHUNK_tIME +FREE_CHUNK_HDR (mng_free_time) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_TIME, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_time)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_TIME, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +FREE_CHUNK_HDR (mng_free_mhdr) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_MHDR, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_mhdr)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_MHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +FREE_CHUNK_HDR (mng_free_mend) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_MEND, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_mend)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_MEND, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_LOOP +FREE_CHUNK_HDR (mng_free_loop) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_LOOP, MNG_LC_START); +#endif + +#ifndef MNG_NO_LOOP_SIGNALS_SUPPORTED + if (((mng_loopp)pHeader)->iCount) + MNG_FREEX (pData, ((mng_loopp)pHeader)->pSignals, + ((mng_loopp)pHeader)->iCount * sizeof (mng_uint32) ); +#endif + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + MNG_FREEX (pData, pHeader, sizeof (mng_loop)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_LOOP, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + return MNG_NOERROR; +#else + return mng_free_general(pData, pHeader); +#endif +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +FREE_CHUNK_HDR (mng_free_endl) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ENDL, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_endl)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ENDL, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +#ifndef MNG_SKIPCHUNK_DEFI +FREE_CHUNK_HDR (mng_free_defi) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_DEFI, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_defi)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_DEFI, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +#ifndef MNG_SKIPCHUNK_BASI +FREE_CHUNK_HDR (mng_free_basi) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_BASI, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_basi)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_BASI, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +#ifndef MNG_SKIPCHUNK_CLON +FREE_CHUNK_HDR (mng_free_clon) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_CLON, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_clon)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_CLON, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_PAST +FREE_CHUNK_HDR (mng_free_past) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_PAST, MNG_LC_START); +#endif + + if (((mng_pastp)pHeader)->iCount) + MNG_FREEX (pData, ((mng_pastp)pHeader)->pSources, + ((mng_pastp)pHeader)->iCount * sizeof (mng_past_source) ); + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + MNG_FREEX (pData, pHeader, sizeof (mng_past)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_PAST, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + return MNG_NOERROR; +#else + return mng_free_general(pData, pHeader); +#endif +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_DISC +FREE_CHUNK_HDR (mng_free_disc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_DISC, MNG_LC_START); +#endif + + if (((mng_discp)pHeader)->iCount) + MNG_FREEX (pData, ((mng_discp)pHeader)->pObjectids, + ((mng_discp)pHeader)->iCount * sizeof (mng_uint16) ); + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + MNG_FREEX (pData, pHeader, sizeof (mng_disc)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_DISC, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + return MNG_NOERROR; +#else + return mng_free_general(pData, pHeader); +#endif +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +#ifndef MNG_SKIPCHUNK_BACK +FREE_CHUNK_HDR (mng_free_back) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_BACK, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_back)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_BACK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_FRAM +FREE_CHUNK_HDR (mng_free_fram) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_FRAM, MNG_LC_START); +#endif + + if (((mng_framp)pHeader)->iNamesize) + MNG_FREEX (pData, ((mng_framp)pHeader)->zName, + ((mng_framp)pHeader)->iNamesize + 1); + + if (((mng_framp)pHeader)->iCount) + MNG_FREEX (pData, ((mng_framp)pHeader)->pSyncids, + ((mng_framp)pHeader)->iCount * sizeof (mng_uint32) ); + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + MNG_FREEX (pData, pHeader, sizeof (mng_fram)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_FRAM, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + return MNG_NOERROR; +#else + return mng_free_general(pData, pHeader); +#endif +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +#ifndef MNG_SKIPCHUNK_MOVE +FREE_CHUNK_HDR (mng_free_move) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_MOVE, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_move)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_MOVE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +#ifndef MNG_SKIPCHUNK_CLIP +FREE_CHUNK_HDR (mng_free_clip) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_CLIP, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_clip)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_CLIP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +#ifndef MNG_SKIPCHUNK_SHOW +FREE_CHUNK_HDR (mng_free_show) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_SHOW, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_show)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_SHOW, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +#ifndef MNG_SKIPCHUNK_TERM +FREE_CHUNK_HDR (mng_free_term) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_TERM, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_term)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_TERM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SAVE +FREE_CHUNK_HDR (mng_free_save) +{ + mng_save_entryp pEntry = ((mng_savep)pHeader)->pEntries; + mng_uint32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_SAVE, MNG_LC_START); +#endif + + for (iX = 0; iX < ((mng_savep)pHeader)->iCount; iX++) + { + if (pEntry->iNamesize) + MNG_FREEX (pData, pEntry->zName, pEntry->iNamesize); + + pEntry = pEntry + sizeof (mng_save_entry); + } + + if (((mng_savep)pHeader)->iCount) + MNG_FREEX (pData, ((mng_savep)pHeader)->pEntries, + ((mng_savep)pHeader)->iCount * sizeof (mng_save_entry) ); + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + MNG_FREEX (pData, pHeader, sizeof (mng_save)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_SAVE, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + return MNG_NOERROR; +#else + return mng_free_general(pData, pHeader); +#endif +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SEEK +FREE_CHUNK_HDR (mng_free_seek) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_SEEK, MNG_LC_START); +#endif + + if (((mng_seekp)pHeader)->iNamesize) + MNG_FREEX (pData, ((mng_seekp)pHeader)->zName, + ((mng_seekp)pHeader)->iNamesize + 1); + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + MNG_FREEX (pData, pHeader, sizeof (mng_seek)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_SEEK, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + return MNG_NOERROR; +#else + return mng_free_general(pData, pHeader); +#endif +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_eXPI +FREE_CHUNK_HDR (mng_free_expi) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_EXPI, MNG_LC_START); +#endif + + if (((mng_expip)pHeader)->iNamesize) + MNG_FREEX (pData, ((mng_expip)pHeader)->zName, + ((mng_expip)pHeader)->iNamesize + 1); + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + MNG_FREEX (pData, pHeader, sizeof (mng_expi)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_EXPI, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + return MNG_NOERROR; +#else + return mng_free_general(pData, pHeader); +#endif +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +#ifndef MNG_SKIPCHUNK_fPRI +FREE_CHUNK_HDR (mng_free_fpri) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_FPRI, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_fpri)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_FPRI, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_nEED +FREE_CHUNK_HDR (mng_free_need) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_NEED, MNG_LC_START); +#endif + + if (((mng_needp)pHeader)->iKeywordssize) + MNG_FREEX (pData, ((mng_needp)pHeader)->zKeywords, + ((mng_needp)pHeader)->iKeywordssize + 1); + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + MNG_FREEX (pData, pHeader, sizeof (mng_need)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_NEED, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + return MNG_NOERROR; +#else + return mng_free_general(pData, pHeader); +#endif +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +#ifndef MNG_SKIPCHUNK_pHYg +FREE_CHUNK_HDR (mng_free_phyg) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_PHYG, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_phyg)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_PHYG, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +#ifdef MNG_INCLUDE_JNG +FREE_CHUNK_HDR (mng_free_jhdr) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_JHDR, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_jhdr)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_JHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_INCLUDE_JNG */ +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +FREE_CHUNK_HDR (mng_free_jdaa) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_JDAA, MNG_LC_START); +#endif + + if (((mng_jdaap)pHeader)->iDatasize) + MNG_FREEX (pData, ((mng_jdaap)pHeader)->pData, + ((mng_jdaap)pHeader)->iDatasize); + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + MNG_FREEX (pData, pHeader, sizeof (mng_jdaa)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_JDAA, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + return MNG_NOERROR; +#else + return mng_free_general(pData, pHeader); +#endif +} +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +FREE_CHUNK_HDR (mng_free_jdat) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_JDAT, MNG_LC_START); +#endif + + if (((mng_jdatp)pHeader)->iDatasize) + MNG_FREEX (pData, ((mng_jdatp)pHeader)->pData, + ((mng_jdatp)pHeader)->iDatasize); + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + MNG_FREEX (pData, pHeader, sizeof (mng_jdat)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_JDAT, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + return MNG_NOERROR; +#else + return mng_free_general(pData, pHeader); +#endif +} +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +#ifdef MNG_INCLUDE_JNG +FREE_CHUNK_HDR (mng_free_jsep) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_JSEP, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_jsep)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_JSEP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_INCLUDE_JNG */ +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +#ifndef MNG_NO_DELTA_PNG +FREE_CHUNK_HDR (mng_free_dhdr) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_DHDR, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_dhdr)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_DHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +#ifndef MNG_NO_DELTA_PNG +FREE_CHUNK_HDR (mng_free_prom) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_PROM, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_prom)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_PROM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +#ifndef MNG_NO_DELTA_PNG +FREE_CHUNK_HDR (mng_free_ipng) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_IPNG, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_ipng)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_IPNG, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +#ifndef MNG_NO_DELTA_PNG +FREE_CHUNK_HDR (mng_free_pplt) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_PPLT, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_pplt)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_PPLT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +#ifndef MNG_NO_DELTA_PNG +#ifdef MNG_INCLUDE_JNG +FREE_CHUNK_HDR (mng_free_ijng) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_IJNG, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_ijng)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_IJNG, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +FREE_CHUNK_HDR (mng_free_drop) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_DROP, MNG_LC_START); +#endif + + if (((mng_dropp)pHeader)->iCount) + MNG_FREEX (pData, ((mng_dropp)pHeader)->pChunknames, + ((mng_dropp)pHeader)->iCount * sizeof (mng_chunkid) ); + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + MNG_FREEX (pData, pHeader, sizeof (mng_drop)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_DROP, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + return MNG_NOERROR; +#else + return mng_free_general(pData, pHeader); +#endif +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_DBYK +FREE_CHUNK_HDR (mng_free_dbyk) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_DBYK, MNG_LC_START); +#endif + + if (((mng_dbykp)pHeader)->iKeywordssize) + MNG_FREEX (pData, ((mng_dbykp)pHeader)->zKeywords, + ((mng_dbykp)pHeader)->iKeywordssize); + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + MNG_FREEX (pData, pHeader, sizeof (mng_dbyk)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_DBYK, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + return MNG_NOERROR; +#else + return mng_free_general(pData, pHeader); +#endif +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_ORDR +FREE_CHUNK_HDR (mng_free_ordr) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ORDR, MNG_LC_START); +#endif + + if (((mng_ordrp)pHeader)->iCount) + MNG_FREEX (pData, ((mng_ordrp)pHeader)->pEntries, + ((mng_ordrp)pHeader)->iCount * sizeof (mng_ordr_entry) ); + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + MNG_FREEX (pData, pHeader, sizeof (mng_ordr)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ORDR, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + return MNG_NOERROR; +#else + return mng_free_general(pData, pHeader); +#endif +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE +#ifndef MNG_SKIPCHUNK_MAGN +FREE_CHUNK_HDR (mng_free_magn) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_MAGN, MNG_LC_START); +#endif + + MNG_FREEX (pData, pHeader, sizeof (mng_magn)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_MAGN, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_evNT +FREE_CHUNK_HDR (mng_free_evnt) +{ + mng_evnt_entryp pEntry = ((mng_evntp)pHeader)->pEntries; + mng_uint32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_EVNT, MNG_LC_START); +#endif + + for (iX = 0; iX < ((mng_evntp)pHeader)->iCount; iX++) + { + if (pEntry->iSegmentnamesize) + MNG_FREEX (pData, pEntry->zSegmentname, pEntry->iSegmentnamesize+1); + + pEntry++; + } + + if (((mng_evntp)pHeader)->iCount) + MNG_FREEX (pData, ((mng_evntp)pHeader)->pEntries, + ((mng_evntp)pHeader)->iCount * sizeof (mng_evnt_entry) ); + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + MNG_FREEX (pData, pHeader, sizeof (mng_evnt)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_EVNT, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + return MNG_NOERROR; +#else + return mng_free_general(pData, pHeader); +#endif +} +#endif + +/* ************************************************************************** */ + +FREE_CHUNK_HDR (mng_free_unknown) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_UNKNOWN, MNG_LC_START); +#endif + + if (((mng_unknown_chunkp)pHeader)->iDatasize) + MNG_FREEX (pData, ((mng_unknown_chunkp)pHeader)->pData, + ((mng_unknown_chunkp)pHeader)->iDatasize); + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + MNG_FREEX (pData, pHeader, sizeof (mng_unknown_chunk)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_UNKNOWN, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_CHUNKINITFREE + return MNG_NOERROR; +#else + return mng_free_general(pData, pHeader); +#endif +} + +/* ************************************************************************** */ +/* * * */ +/* * Chunk specific copy routines * */ +/* * * */ +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_WRITE_PROCS + +/* ************************************************************************** */ + +#ifdef MNG_OPTIMIZE_CHUNKASSIGN +ASSIGN_CHUNK_HDR (mng_assign_general) +{ + mng_ptr pSrc = (mng_uint8p)pChunkfrom + sizeof (mng_chunk_header); + mng_ptr pDst = (mng_uint8p)pChunkto + sizeof (mng_chunk_header); + mng_size_t iLen = ((mng_chunk_headerp)pChunkfrom)->iChunksize - sizeof (mng_chunk_header); + + MNG_COPY (pDst, pSrc, iLen); + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +ASSIGN_CHUNK_HDR (mng_assign_ihdr) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_IHDR, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_IHDR) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_ihdrp)pChunkto)->iWidth = ((mng_ihdrp)pChunkfrom)->iWidth; + ((mng_ihdrp)pChunkto)->iHeight = ((mng_ihdrp)pChunkfrom)->iHeight; + ((mng_ihdrp)pChunkto)->iBitdepth = ((mng_ihdrp)pChunkfrom)->iBitdepth; + ((mng_ihdrp)pChunkto)->iColortype = ((mng_ihdrp)pChunkfrom)->iColortype; + ((mng_ihdrp)pChunkto)->iCompression = ((mng_ihdrp)pChunkfrom)->iCompression; + ((mng_ihdrp)pChunkto)->iFilter = ((mng_ihdrp)pChunkfrom)->iFilter; + ((mng_ihdrp)pChunkto)->iInterlace = ((mng_ihdrp)pChunkfrom)->iInterlace; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_IHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +ASSIGN_CHUNK_HDR (mng_assign_plte) +{ + mng_uint32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_PLTE, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_PLTE) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_pltep)pChunkto)->bEmpty = ((mng_pltep)pChunkfrom)->bEmpty; + ((mng_pltep)pChunkto)->iEntrycount = ((mng_pltep)pChunkfrom)->iEntrycount; + + for (iX = 0; iX < ((mng_pltep)pChunkto)->iEntrycount; iX++) + ((mng_pltep)pChunkto)->aEntries [iX] = ((mng_pltep)pChunkfrom)->aEntries [iX]; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_PLTE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +ASSIGN_CHUNK_HDR (mng_assign_idat) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_IDAT, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_IDAT) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_idatp)pChunkto)->bEmpty = ((mng_idatp)pChunkfrom)->bEmpty; + ((mng_idatp)pChunkto)->iDatasize = ((mng_idatp)pChunkfrom)->iDatasize; + + if (((mng_idatp)pChunkto)->iDatasize) + { + MNG_ALLOC (pData, ((mng_idatp)pChunkto)->pData, ((mng_idatp)pChunkto)->iDatasize); + MNG_COPY (((mng_idatp)pChunkto)->pData, ((mng_idatp)pChunkfrom)->pData, + ((mng_idatp)pChunkto)->iDatasize); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_IDAT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +ASSIGN_CHUNK_HDR (mng_assign_iend) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_IEND, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_IEND) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_IEND, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +ASSIGN_CHUNK_HDR (mng_assign_trns) +{ + mng_uint32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_TRNS, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_tRNS) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_trnsp)pChunkto)->bEmpty = ((mng_trnsp)pChunkfrom)->bEmpty; + ((mng_trnsp)pChunkto)->bGlobal = ((mng_trnsp)pChunkfrom)->bGlobal; + ((mng_trnsp)pChunkto)->iType = ((mng_trnsp)pChunkfrom)->iType; + ((mng_trnsp)pChunkto)->iCount = ((mng_trnsp)pChunkfrom)->iCount; + ((mng_trnsp)pChunkto)->iGray = ((mng_trnsp)pChunkfrom)->iGray; + ((mng_trnsp)pChunkto)->iRed = ((mng_trnsp)pChunkfrom)->iRed; + ((mng_trnsp)pChunkto)->iGreen = ((mng_trnsp)pChunkfrom)->iGreen; + ((mng_trnsp)pChunkto)->iBlue = ((mng_trnsp)pChunkfrom)->iBlue; + ((mng_trnsp)pChunkto)->iRawlen = ((mng_trnsp)pChunkfrom)->iRawlen; + + for (iX = 0; iX < ((mng_trnsp)pChunkto)->iCount; iX++) + ((mng_trnsp)pChunkto)->aEntries [iX] = ((mng_trnsp)pChunkfrom)->aEntries [iX]; + + for (iX = 0; iX < ((mng_trnsp)pChunkto)->iRawlen; iX++) + ((mng_trnsp)pChunkto)->aRawdata [iX] = ((mng_trnsp)pChunkfrom)->aRawdata [iX]; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_TRNS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +#ifndef MNG_SKIPCHUNK_gAMA +ASSIGN_CHUNK_HDR (mng_assign_gama) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_GAMA, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_gAMA) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_gamap)pChunkto)->bEmpty = ((mng_gamap)pChunkfrom)->bEmpty; + ((mng_gamap)pChunkto)->iGamma = ((mng_gamap)pChunkfrom)->iGamma; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_GAMA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +#ifndef MNG_SKIPCHUNK_cHRM +ASSIGN_CHUNK_HDR (mng_assign_chrm) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_CHRM, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_cHRM) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_chrmp)pChunkto)->bEmpty = ((mng_chrmp)pChunkfrom)->bEmpty; + ((mng_chrmp)pChunkto)->iWhitepointx = ((mng_chrmp)pChunkfrom)->iWhitepointx; + ((mng_chrmp)pChunkto)->iWhitepointy = ((mng_chrmp)pChunkfrom)->iWhitepointy; + ((mng_chrmp)pChunkto)->iRedx = ((mng_chrmp)pChunkfrom)->iRedx; + ((mng_chrmp)pChunkto)->iRedy = ((mng_chrmp)pChunkfrom)->iRedy; + ((mng_chrmp)pChunkto)->iGreenx = ((mng_chrmp)pChunkfrom)->iGreenx; + ((mng_chrmp)pChunkto)->iGreeny = ((mng_chrmp)pChunkfrom)->iGreeny; + ((mng_chrmp)pChunkto)->iBluex = ((mng_chrmp)pChunkfrom)->iBluex; + ((mng_chrmp)pChunkto)->iBluey = ((mng_chrmp)pChunkfrom)->iBluey; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_CHRM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +#ifndef MNG_SKIPCHUNK_sRGB +ASSIGN_CHUNK_HDR (mng_assign_srgb) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_SRGB, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_sRGB) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_srgbp)pChunkto)->iRenderingintent = ((mng_srgbp)pChunkfrom)->iRenderingintent; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_SRGB, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_iCCP +ASSIGN_CHUNK_HDR (mng_assign_iccp) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_ICCP, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_iCCP) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_iccpp)pChunkto)->bEmpty = ((mng_iccpp)pChunkfrom)->bEmpty; + ((mng_iccpp)pChunkto)->iNamesize = ((mng_iccpp)pChunkfrom)->iNamesize; + ((mng_iccpp)pChunkto)->iCompression = ((mng_iccpp)pChunkfrom)->iCompression; + ((mng_iccpp)pChunkto)->iProfilesize = ((mng_iccpp)pChunkfrom)->iProfilesize; + + if (((mng_iccpp)pChunkto)->iNamesize) + { + MNG_ALLOC (pData, ((mng_iccpp)pChunkto)->zName, ((mng_iccpp)pChunkto)->iNamesize); + MNG_COPY (((mng_iccpp)pChunkto)->zName, ((mng_iccpp)pChunkfrom)->zName, + ((mng_iccpp)pChunkto)->iNamesize); + } + + if (((mng_iccpp)pChunkto)->iProfilesize) + { + MNG_ALLOC (pData, ((mng_iccpp)pChunkto)->pProfile, ((mng_iccpp)pChunkto)->iProfilesize); + MNG_COPY (((mng_iccpp)pChunkto)->pProfile, ((mng_iccpp)pChunkfrom)->pProfile, + ((mng_iccpp)pChunkto)->iProfilesize); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_ICCP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_tEXt +ASSIGN_CHUNK_HDR (mng_assign_text) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_TEXT, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_tEXt) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_textp)pChunkto)->iKeywordsize = ((mng_textp)pChunkfrom)->iKeywordsize; + ((mng_textp)pChunkto)->iTextsize = ((mng_textp)pChunkfrom)->iTextsize; + + if (((mng_textp)pChunkto)->iKeywordsize) + { + MNG_ALLOC (pData, ((mng_itxtp)pChunkto)->zKeyword, ((mng_textp)pChunkto)->iKeywordsize); + MNG_COPY (((mng_itxtp)pChunkto)->zKeyword, ((mng_textp)pChunkfrom)->zKeyword, + ((mng_itxtp)pChunkto)->iKeywordsize); + } + + if (((mng_textp)pChunkto)->iTextsize) + { + MNG_ALLOC (pData, ((mng_textp)pChunkto)->zText, ((mng_textp)pChunkto)->iTextsize); + MNG_COPY (((mng_textp)pChunkto)->zText, ((mng_textp)pChunkfrom)->zText, + ((mng_textp)pChunkto)->iTextsize); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_TEXT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_zTXt +ASSIGN_CHUNK_HDR (mng_assign_ztxt) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_ZTXT, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_zTXt) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_ztxtp)pChunkto)->iKeywordsize = ((mng_ztxtp)pChunkfrom)->iKeywordsize; + ((mng_ztxtp)pChunkto)->iCompression = ((mng_ztxtp)pChunkfrom)->iCompression; + ((mng_ztxtp)pChunkto)->iTextsize = ((mng_ztxtp)pChunkfrom)->iTextsize; + + if (((mng_ztxtp)pChunkto)->iKeywordsize) + { + MNG_ALLOC (pData, ((mng_ztxtp)pChunkto)->zKeyword, ((mng_ztxtp)pChunkto)->iKeywordsize); + MNG_COPY (((mng_ztxtp)pChunkto)->zKeyword, ((mng_ztxtp)pChunkfrom)->zKeyword, + ((mng_ztxtp)pChunkto)->iKeywordsize); + } + + if (((mng_ztxtp)pChunkto)->iTextsize) + { + MNG_ALLOC (pData, ((mng_ztxtp)pChunkto)->zText, ((mng_ztxtp)pChunkto)->iTextsize); + MNG_COPY (((mng_ztxtp)pChunkto)->zText, ((mng_ztxtp)pChunkfrom)->zText, + ((mng_ztxtp)pChunkto)->iTextsize); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_ZTXT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_iTXt +ASSIGN_CHUNK_HDR (mng_assign_itxt) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_ITXT, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_iTXt) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_itxtp)pChunkto)->iKeywordsize = ((mng_itxtp)pChunkfrom)->iKeywordsize; + ((mng_itxtp)pChunkto)->iCompressionflag = ((mng_itxtp)pChunkfrom)->iCompressionflag; + ((mng_itxtp)pChunkto)->iCompressionmethod = ((mng_itxtp)pChunkfrom)->iCompressionmethod; + ((mng_itxtp)pChunkto)->iLanguagesize = ((mng_itxtp)pChunkfrom)->iLanguagesize; + ((mng_itxtp)pChunkto)->iTranslationsize = ((mng_itxtp)pChunkfrom)->iTranslationsize; + ((mng_itxtp)pChunkto)->iTextsize = ((mng_itxtp)pChunkfrom)->iTextsize; + + if (((mng_itxtp)pChunkto)->iKeywordsize) + { + MNG_ALLOC (pData, ((mng_itxtp)pChunkto)->zKeyword, ((mng_itxtp)pChunkto)->iKeywordsize); + MNG_COPY (((mng_itxtp)pChunkto)->zKeyword, ((mng_itxtp)pChunkfrom)->zKeyword, + ((mng_itxtp)pChunkto)->iKeywordsize); + } + + if (((mng_itxtp)pChunkto)->iTextsize) + { + MNG_ALLOC (pData, ((mng_itxtp)pChunkto)->zLanguage, ((mng_itxtp)pChunkto)->iLanguagesize); + MNG_COPY (((mng_itxtp)pChunkto)->zLanguage, ((mng_itxtp)pChunkfrom)->zLanguage, + ((mng_itxtp)pChunkto)->iLanguagesize); + } + + if (((mng_itxtp)pChunkto)->iTextsize) + { + MNG_ALLOC (pData, ((mng_itxtp)pChunkto)->zTranslation, ((mng_itxtp)pChunkto)->iTranslationsize); + MNG_COPY (((mng_itxtp)pChunkto)->zTranslation, ((mng_itxtp)pChunkfrom)->zTranslation, + ((mng_itxtp)pChunkto)->iTranslationsize); + } + + if (((mng_itxtp)pChunkto)->iTextsize) + { + MNG_ALLOC (pData, ((mng_itxtp)pChunkto)->zText, ((mng_itxtp)pChunkto)->iTextsize); + MNG_COPY (((mng_itxtp)pChunkto)->zText, ((mng_itxtp)pChunkfrom)->zText, + ((mng_itxtp)pChunkto)->iTextsize); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_ITXT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +#ifndef MNG_SKIPCHUNK_bKGD +ASSIGN_CHUNK_HDR (mng_assign_bkgd) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_BKGD, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_bKGD) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_bkgdp)pChunkto)->bEmpty = ((mng_bkgdp)pChunkfrom)->bEmpty; + ((mng_bkgdp)pChunkto)->iType = ((mng_bkgdp)pChunkfrom)->iType; + ((mng_bkgdp)pChunkto)->iIndex = ((mng_bkgdp)pChunkfrom)->iIndex; + ((mng_bkgdp)pChunkto)->iGray = ((mng_bkgdp)pChunkfrom)->iGray; + ((mng_bkgdp)pChunkto)->iRed = ((mng_bkgdp)pChunkfrom)->iRed; + ((mng_bkgdp)pChunkto)->iGreen = ((mng_bkgdp)pChunkfrom)->iGreen; + ((mng_bkgdp)pChunkto)->iBlue = ((mng_bkgdp)pChunkfrom)->iBlue; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_BKGD, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +#ifndef MNG_SKIPCHUNK_pHYs +ASSIGN_CHUNK_HDR (mng_assign_phys) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_PHYS, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_pHYs) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_physp)pChunkto)->bEmpty = ((mng_physp)pChunkfrom)->bEmpty; + ((mng_physp)pChunkto)->iSizex = ((mng_physp)pChunkfrom)->iSizex; + ((mng_physp)pChunkto)->iSizey = ((mng_physp)pChunkfrom)->iSizey; + ((mng_physp)pChunkto)->iUnit = ((mng_physp)pChunkfrom)->iUnit; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_PHYS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +#ifndef MNG_SKIPCHUNK_sBIT +ASSIGN_CHUNK_HDR (mng_assign_sbit) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_SBIT, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_sBIT) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_sbitp)pChunkto)->bEmpty = ((mng_sbitp)pChunkfrom)->bEmpty; + ((mng_sbitp)pChunkto)->iType = ((mng_sbitp)pChunkfrom)->iType; + ((mng_sbitp)pChunkto)->aBits [0] = ((mng_sbitp)pChunkfrom)->aBits [0]; + ((mng_sbitp)pChunkto)->aBits [1] = ((mng_sbitp)pChunkfrom)->aBits [1]; + ((mng_sbitp)pChunkto)->aBits [2] = ((mng_sbitp)pChunkfrom)->aBits [2]; + ((mng_sbitp)pChunkto)->aBits [3] = ((mng_sbitp)pChunkfrom)->aBits [3]; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_SBIT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_sPLT +ASSIGN_CHUNK_HDR (mng_assign_splt) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_SPLT, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_sPLT) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_spltp)pChunkto)->bEmpty = ((mng_spltp)pChunkfrom)->bEmpty; + ((mng_spltp)pChunkto)->iNamesize = ((mng_spltp)pChunkfrom)->iNamesize; + ((mng_spltp)pChunkto)->iSampledepth = ((mng_spltp)pChunkfrom)->iSampledepth; + ((mng_spltp)pChunkto)->iEntrycount = ((mng_spltp)pChunkfrom)->iEntrycount; + ((mng_spltp)pChunkto)->pEntries = ((mng_spltp)pChunkfrom)->pEntries; + + if (((mng_spltp)pChunkto)->iNamesize) + { + MNG_ALLOC (pData, ((mng_spltp)pChunkto)->zName, ((mng_spltp)pChunkto)->iNamesize); + MNG_COPY (((mng_spltp)pChunkto)->zName, ((mng_spltp)pChunkfrom)->zName, + ((mng_spltp)pChunkto)->iNamesize); + } + + if (((mng_spltp)pChunkto)->iEntrycount) + { + mng_uint32 iLen = ((mng_spltp)pChunkto)->iEntrycount * + (((mng_spltp)pChunkto)->iSampledepth * 3 + sizeof (mng_uint16)); + + MNG_ALLOC (pData, ((mng_spltp)pChunkto)->pEntries, iLen); + MNG_COPY (((mng_spltp)pChunkto)->pEntries, ((mng_spltp)pChunkfrom)->pEntries, iLen); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_SPLT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +#ifndef MNG_SKIPCHUNK_hIST +ASSIGN_CHUNK_HDR (mng_assign_hist) +{ + mng_uint32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_HIST, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_hIST) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_histp)pChunkto)->iEntrycount = ((mng_histp)pChunkfrom)->iEntrycount; + + for (iX = 0; iX < ((mng_histp)pChunkto)->iEntrycount; iX++) + ((mng_histp)pChunkto)->aEntries [iX] = ((mng_histp)pChunkfrom)->aEntries [iX]; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_HIST, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +#ifndef MNG_SKIPCHUNK_tIME +ASSIGN_CHUNK_HDR (mng_assign_time) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_TIME, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_tIME) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_timep)pChunkto)->iYear = ((mng_timep)pChunkfrom)->iYear; + ((mng_timep)pChunkto)->iMonth = ((mng_timep)pChunkfrom)->iMonth; + ((mng_timep)pChunkto)->iDay = ((mng_timep)pChunkfrom)->iDay; + ((mng_timep)pChunkto)->iHour = ((mng_timep)pChunkfrom)->iHour; + ((mng_timep)pChunkto)->iMinute = ((mng_timep)pChunkfrom)->iMinute; + ((mng_timep)pChunkto)->iSecond = ((mng_timep)pChunkfrom)->iSecond; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_TIME, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +ASSIGN_CHUNK_HDR (mng_assign_mhdr) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_MHDR, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_mhdrp)pChunkto)->iWidth = ((mng_mhdrp)pChunkfrom)->iWidth; + ((mng_mhdrp)pChunkto)->iHeight = ((mng_mhdrp)pChunkfrom)->iHeight; + ((mng_mhdrp)pChunkto)->iTicks = ((mng_mhdrp)pChunkfrom)->iTicks; + ((mng_mhdrp)pChunkto)->iLayercount = ((mng_mhdrp)pChunkfrom)->iLayercount; + ((mng_mhdrp)pChunkto)->iFramecount = ((mng_mhdrp)pChunkfrom)->iFramecount; + ((mng_mhdrp)pChunkto)->iPlaytime = ((mng_mhdrp)pChunkfrom)->iPlaytime; + ((mng_mhdrp)pChunkto)->iSimplicity = ((mng_mhdrp)pChunkfrom)->iSimplicity; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_MHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +ASSIGN_CHUNK_HDR (mng_assign_mend) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_MEND, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_MEND) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_MEND, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_LOOP +ASSIGN_CHUNK_HDR (mng_assign_loop) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_LOOP, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_LOOP) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_loopp)pChunkto)->iLevel = ((mng_loopp)pChunkfrom)->iLevel; + ((mng_loopp)pChunkto)->iRepeat = ((mng_loopp)pChunkfrom)->iRepeat; + ((mng_loopp)pChunkto)->iTermination = ((mng_loopp)pChunkfrom)->iTermination; + ((mng_loopp)pChunkto)->iItermin = ((mng_loopp)pChunkfrom)->iItermin; + ((mng_loopp)pChunkto)->iItermax = ((mng_loopp)pChunkfrom)->iItermax; + ((mng_loopp)pChunkto)->iCount = ((mng_loopp)pChunkfrom)->iCount; + +#ifndef MNG_NO_LOOP_SIGNALS_SUPPORTED + if (((mng_loopp)pChunkto)->iCount) + { + mng_uint32 iLen = ((mng_loopp)pChunkto)->iCount * sizeof (mng_uint32); + MNG_ALLOC (pData, ((mng_loopp)pChunkto)->pSignals, iLen); + MNG_COPY (((mng_loopp)pChunkto)->pSignals, ((mng_loopp)pChunkfrom)->pSignals, iLen); + } +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_LOOP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +ASSIGN_CHUNK_HDR (mng_assign_endl) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_ENDL, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_ENDL) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_endlp)pChunkto)->iLevel = ((mng_endlp)pChunkfrom)->iLevel; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_ENDL, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +#ifndef MNG_SKIPCHUNK_DEFI +ASSIGN_CHUNK_HDR (mng_assign_defi) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_DEFI, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_DEFI) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_defip)pChunkto)->iObjectid = ((mng_defip)pChunkfrom)->iObjectid; + ((mng_defip)pChunkto)->bHasdonotshow = ((mng_defip)pChunkfrom)->bHasdonotshow; + ((mng_defip)pChunkto)->iDonotshow = ((mng_defip)pChunkfrom)->iDonotshow; + ((mng_defip)pChunkto)->bHasconcrete = ((mng_defip)pChunkfrom)->bHasconcrete; + ((mng_defip)pChunkto)->iConcrete = ((mng_defip)pChunkfrom)->iConcrete; + ((mng_defip)pChunkto)->bHasloca = ((mng_defip)pChunkfrom)->bHasloca; + ((mng_defip)pChunkto)->iXlocation = ((mng_defip)pChunkfrom)->iXlocation; + ((mng_defip)pChunkto)->iYlocation = ((mng_defip)pChunkfrom)->iYlocation; + ((mng_defip)pChunkto)->bHasclip = ((mng_defip)pChunkfrom)->bHasclip; + ((mng_defip)pChunkto)->iLeftcb = ((mng_defip)pChunkfrom)->iLeftcb; + ((mng_defip)pChunkto)->iRightcb = ((mng_defip)pChunkfrom)->iRightcb; + ((mng_defip)pChunkto)->iTopcb = ((mng_defip)pChunkfrom)->iTopcb; + ((mng_defip)pChunkto)->iBottomcb = ((mng_defip)pChunkfrom)->iBottomcb; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_DEFI, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +#ifndef MNG_SKIPCHUNK_BASI +ASSIGN_CHUNK_HDR (mng_assign_basi) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_BASI, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_BASI) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_basip)pChunkto)->iWidth = ((mng_basip)pChunkfrom)->iWidth; + ((mng_basip)pChunkto)->iHeight = ((mng_basip)pChunkfrom)->iHeight; + ((mng_basip)pChunkto)->iBitdepth = ((mng_basip)pChunkfrom)->iBitdepth; + ((mng_basip)pChunkto)->iColortype = ((mng_basip)pChunkfrom)->iColortype; + ((mng_basip)pChunkto)->iCompression = ((mng_basip)pChunkfrom)->iCompression; + ((mng_basip)pChunkto)->iFilter = ((mng_basip)pChunkfrom)->iFilter; + ((mng_basip)pChunkto)->iInterlace = ((mng_basip)pChunkfrom)->iInterlace; + ((mng_basip)pChunkto)->iRed = ((mng_basip)pChunkfrom)->iRed; + ((mng_basip)pChunkto)->iGreen = ((mng_basip)pChunkfrom)->iGreen; + ((mng_basip)pChunkto)->iBlue = ((mng_basip)pChunkfrom)->iBlue; + ((mng_basip)pChunkto)->iAlpha = ((mng_basip)pChunkfrom)->iAlpha; + ((mng_basip)pChunkto)->iViewable = ((mng_basip)pChunkfrom)->iViewable; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_BASI, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +#ifndef MNG_SKIPCHUNK_CLON +ASSIGN_CHUNK_HDR (mng_assign_clon) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_CLON, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_CLON) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_clonp)pChunkto)->iSourceid = ((mng_clonp)pChunkfrom)->iSourceid; + ((mng_clonp)pChunkto)->iCloneid = ((mng_clonp)pChunkfrom)->iCloneid; + ((mng_clonp)pChunkto)->iClonetype = ((mng_clonp)pChunkfrom)->iClonetype; +#ifdef MNG_OPTIMIZE_CHUNKREADER + ((mng_clonp)pChunkto)->bHasdonotshow = ((mng_clonp)pChunkfrom)->bHasdonotshow; +#endif + ((mng_clonp)pChunkto)->iDonotshow = ((mng_clonp)pChunkfrom)->iDonotshow; + ((mng_clonp)pChunkto)->iConcrete = ((mng_clonp)pChunkfrom)->iConcrete; + ((mng_clonp)pChunkto)->bHasloca = ((mng_clonp)pChunkfrom)->bHasloca; + ((mng_clonp)pChunkto)->iLocationtype = ((mng_clonp)pChunkfrom)->iLocationtype; + ((mng_clonp)pChunkto)->iLocationx = ((mng_clonp)pChunkfrom)->iLocationx; + ((mng_clonp)pChunkto)->iLocationy = ((mng_clonp)pChunkfrom)->iLocationy; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_CLON, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_PAST +ASSIGN_CHUNK_HDR (mng_assign_past) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_PAST, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_PAST) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_pastp)pChunkto)->iDestid = ((mng_pastp)pChunkfrom)->iDestid; + ((mng_pastp)pChunkto)->iTargettype = ((mng_pastp)pChunkfrom)->iTargettype; + ((mng_pastp)pChunkto)->iTargetx = ((mng_pastp)pChunkfrom)->iTargetx; + ((mng_pastp)pChunkto)->iTargety = ((mng_pastp)pChunkfrom)->iTargety; + ((mng_pastp)pChunkto)->iCount = ((mng_pastp)pChunkfrom)->iCount; + + if (((mng_pastp)pChunkto)->iCount) + { + mng_uint32 iLen = ((mng_pastp)pChunkto)->iCount * sizeof (mng_past_source); + + MNG_ALLOC (pData, ((mng_pastp)pChunkto)->pSources, iLen); + MNG_COPY (((mng_pastp)pChunkto)->pSources, ((mng_pastp)pChunkfrom)->pSources, iLen); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_PAST, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_DISC +ASSIGN_CHUNK_HDR (mng_assign_disc) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_DISC, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_DISC) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_discp)pChunkto)->iCount = ((mng_discp)pChunkfrom)->iCount; + + if (((mng_discp)pChunkto)->iCount) + { + mng_uint32 iLen = ((mng_discp)pChunkto)->iCount * sizeof (mng_uint16); + + MNG_ALLOC (pData, ((mng_discp)pChunkto)->pObjectids, iLen); + MNG_COPY (((mng_discp)pChunkto)->pObjectids, ((mng_discp)pChunkfrom)->pObjectids, iLen); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_DISC, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +#ifndef MNG_SKIPCHUNK_BACK +ASSIGN_CHUNK_HDR (mng_assign_back) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_BACK, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_BACK) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_backp)pChunkto)->iRed = ((mng_backp)pChunkfrom)->iRed; + ((mng_backp)pChunkto)->iGreen = ((mng_backp)pChunkfrom)->iGreen; + ((mng_backp)pChunkto)->iBlue = ((mng_backp)pChunkfrom)->iBlue; + ((mng_backp)pChunkto)->iMandatory = ((mng_backp)pChunkfrom)->iMandatory; + ((mng_backp)pChunkto)->iImageid = ((mng_backp)pChunkfrom)->iImageid; + ((mng_backp)pChunkto)->iTile = ((mng_backp)pChunkfrom)->iTile; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_BACK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_FRAM +ASSIGN_CHUNK_HDR (mng_assign_fram) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_FRAM, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_FRAM) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_framp)pChunkto)->bEmpty = ((mng_framp)pChunkfrom)->bEmpty; + ((mng_framp)pChunkto)->iMode = ((mng_framp)pChunkfrom)->iMode; + ((mng_framp)pChunkto)->iNamesize = ((mng_framp)pChunkfrom)->iNamesize; + ((mng_framp)pChunkto)->iChangedelay = ((mng_framp)pChunkfrom)->iChangedelay; + ((mng_framp)pChunkto)->iChangetimeout = ((mng_framp)pChunkfrom)->iChangetimeout; + ((mng_framp)pChunkto)->iChangeclipping = ((mng_framp)pChunkfrom)->iChangeclipping; + ((mng_framp)pChunkto)->iChangesyncid = ((mng_framp)pChunkfrom)->iChangesyncid; + ((mng_framp)pChunkto)->iDelay = ((mng_framp)pChunkfrom)->iDelay; + ((mng_framp)pChunkto)->iTimeout = ((mng_framp)pChunkfrom)->iTimeout; + ((mng_framp)pChunkto)->iBoundarytype = ((mng_framp)pChunkfrom)->iBoundarytype; + ((mng_framp)pChunkto)->iBoundaryl = ((mng_framp)pChunkfrom)->iBoundaryl; + ((mng_framp)pChunkto)->iBoundaryr = ((mng_framp)pChunkfrom)->iBoundaryr; + ((mng_framp)pChunkto)->iBoundaryt = ((mng_framp)pChunkfrom)->iBoundaryt; + ((mng_framp)pChunkto)->iBoundaryb = ((mng_framp)pChunkfrom)->iBoundaryb; + ((mng_framp)pChunkto)->iCount = ((mng_framp)pChunkfrom)->iCount; + + if (((mng_framp)pChunkto)->iNamesize) + { + MNG_ALLOC (pData, ((mng_framp)pChunkto)->zName, ((mng_framp)pChunkto)->iNamesize); + MNG_COPY (((mng_framp)pChunkto)->zName, ((mng_framp)pChunkfrom)->zName, + ((mng_framp)pChunkto)->iNamesize); + } + + if (((mng_framp)pChunkto)->iCount) + { + mng_uint32 iLen = ((mng_framp)pChunkto)->iCount * sizeof (mng_uint32); + + MNG_ALLOC (pData, ((mng_framp)pChunkto)->pSyncids, iLen); + MNG_COPY (((mng_framp)pChunkto)->pSyncids, ((mng_framp)pChunkfrom)->pSyncids, iLen); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_FRAM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +#ifndef MNG_SKIPCHUNK_MOVE +ASSIGN_CHUNK_HDR (mng_assign_move) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_MOVE, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_MOVE) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_movep)pChunkto)->iFirstid = ((mng_movep)pChunkfrom)->iFirstid; + ((mng_movep)pChunkto)->iLastid = ((mng_movep)pChunkfrom)->iLastid; + ((mng_movep)pChunkto)->iMovetype = ((mng_movep)pChunkfrom)->iMovetype; + ((mng_movep)pChunkto)->iMovex = ((mng_movep)pChunkfrom)->iMovex; + ((mng_movep)pChunkto)->iMovey = ((mng_movep)pChunkfrom)->iMovey; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_MOVE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +#ifndef MNG_SKIPCHUNK_CLIP +ASSIGN_CHUNK_HDR (mng_assign_clip) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_CLIP, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_CLIP) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_clipp)pChunkto)->iFirstid = ((mng_clipp)pChunkfrom)->iFirstid; + ((mng_clipp)pChunkto)->iLastid = ((mng_clipp)pChunkfrom)->iLastid; + ((mng_clipp)pChunkto)->iCliptype = ((mng_clipp)pChunkfrom)->iCliptype; + ((mng_clipp)pChunkto)->iClipl = ((mng_clipp)pChunkfrom)->iClipl; + ((mng_clipp)pChunkto)->iClipr = ((mng_clipp)pChunkfrom)->iClipr; + ((mng_clipp)pChunkto)->iClipt = ((mng_clipp)pChunkfrom)->iClipt; + ((mng_clipp)pChunkto)->iClipb = ((mng_clipp)pChunkfrom)->iClipb; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_CLIP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +#ifndef MNG_SKIPCHUNK_SHOW +ASSIGN_CHUNK_HDR (mng_assign_show) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_SHOW, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_SHOW) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_showp)pChunkto)->bEmpty = ((mng_showp)pChunkfrom)->bEmpty; + ((mng_showp)pChunkto)->iFirstid = ((mng_showp)pChunkfrom)->iFirstid; + ((mng_showp)pChunkto)->iLastid = ((mng_showp)pChunkfrom)->iLastid; + ((mng_showp)pChunkto)->iMode = ((mng_showp)pChunkfrom)->iMode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_SHOW, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +#ifndef MNG_SKIPCHUNK_TERM +ASSIGN_CHUNK_HDR (mng_assign_term) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_TERM, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_TERM) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_termp)pChunkto)->iTermaction = ((mng_termp)pChunkfrom)->iTermaction; + ((mng_termp)pChunkto)->iIteraction = ((mng_termp)pChunkfrom)->iIteraction; + ((mng_termp)pChunkto)->iDelay = ((mng_termp)pChunkfrom)->iDelay; + ((mng_termp)pChunkto)->iItermax = ((mng_termp)pChunkfrom)->iItermax; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_TERM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SAVE +ASSIGN_CHUNK_HDR (mng_assign_save) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_SAVE, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_SAVE) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_savep)pChunkto)->bEmpty = ((mng_savep)pChunkfrom)->bEmpty; + ((mng_savep)pChunkto)->iOffsettype = ((mng_savep)pChunkfrom)->iOffsettype; + ((mng_savep)pChunkto)->iCount = ((mng_savep)pChunkfrom)->iCount; + + if (((mng_savep)pChunkto)->iCount) + { + mng_uint32 iX; + mng_save_entryp pEntry; + mng_uint32 iLen = ((mng_savep)pChunkto)->iCount * sizeof (mng_save_entry); + + MNG_ALLOC (pData, ((mng_savep)pChunkto)->pEntries, iLen); + MNG_COPY (((mng_savep)pChunkto)->pEntries, ((mng_savep)pChunkfrom)->pEntries, iLen); + + pEntry = ((mng_savep)pChunkto)->pEntries; + + for (iX = 0; iX < ((mng_savep)pChunkto)->iCount; iX++) + { + if (pEntry->iNamesize) + { + mng_pchar pTemp = pEntry->zName; + + MNG_ALLOC (pData, pEntry->zName, pEntry->iNamesize); + MNG_COPY (pEntry->zName, pTemp, pEntry->iNamesize); + } + else + { + pEntry->zName = MNG_NULL; + } + + pEntry++; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_SAVE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SEEK +ASSIGN_CHUNK_HDR (mng_assign_seek) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_SEEK, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_SEEK) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_seekp)pChunkto)->iNamesize = ((mng_seekp)pChunkfrom)->iNamesize; + + if (((mng_seekp)pChunkto)->iNamesize) + { + MNG_ALLOC (pData, ((mng_seekp)pChunkto)->zName, ((mng_seekp)pChunkto)->iNamesize); + MNG_COPY (((mng_seekp)pChunkto)->zName, ((mng_seekp)pChunkfrom)->zName, + ((mng_seekp)pChunkto)->iNamesize); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_SEEK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_eXPI +ASSIGN_CHUNK_HDR (mng_assign_expi) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_EXPI, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_eXPI) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_expip)pChunkto)->iSnapshotid = ((mng_expip)pChunkfrom)->iSnapshotid; + ((mng_expip)pChunkto)->iNamesize = ((mng_expip)pChunkfrom)->iNamesize; + + if (((mng_expip)pChunkto)->iNamesize) + { + MNG_ALLOC (pData, ((mng_expip)pChunkto)->zName, ((mng_expip)pChunkto)->iNamesize); + MNG_COPY (((mng_expip)pChunkto)->zName, ((mng_expip)pChunkfrom)->zName, + ((mng_expip)pChunkto)->iNamesize); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_EXPI, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +#ifndef MNG_SKIPCHUNK_fPRI +ASSIGN_CHUNK_HDR (mng_assign_fpri) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_FPRI, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_fPRI) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_fprip)pChunkto)->iDeltatype = ((mng_fprip)pChunkfrom)->iDeltatype; + ((mng_fprip)pChunkto)->iPriority = ((mng_fprip)pChunkfrom)->iPriority; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_FPRI, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_nEED +ASSIGN_CHUNK_HDR (mng_assign_need) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_NEED, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_nEED) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_needp)pChunkto)->iKeywordssize = ((mng_needp)pChunkfrom)->iKeywordssize; + + if (((mng_needp)pChunkto)->iKeywordssize) + { + MNG_ALLOC (pData, ((mng_needp)pChunkto)->zKeywords, ((mng_needp)pChunkto)->iKeywordssize); + MNG_COPY (((mng_needp)pChunkto)->zKeywords, ((mng_needp)pChunkfrom)->zKeywords, + ((mng_needp)pChunkto)->iKeywordssize); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_NEED, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +#ifndef MNG_SKIPCHUNK_pHYg +ASSIGN_CHUNK_HDR (mng_assign_phyg) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_PHYG, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_pHYg) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_phygp)pChunkto)->bEmpty = ((mng_phygp)pChunkfrom)->bEmpty; + ((mng_phygp)pChunkto)->iSizex = ((mng_phygp)pChunkfrom)->iSizex; + ((mng_phygp)pChunkto)->iSizey = ((mng_phygp)pChunkfrom)->iSizey; + ((mng_phygp)pChunkto)->iUnit = ((mng_phygp)pChunkfrom)->iUnit; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_PHYG, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +#ifdef MNG_INCLUDE_JNG +ASSIGN_CHUNK_HDR (mng_assign_jhdr) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_JHDR, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_JHDR) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_jhdrp)pChunkto)->iWidth = ((mng_jhdrp)pChunkfrom)->iWidth; + ((mng_jhdrp)pChunkto)->iHeight = ((mng_jhdrp)pChunkfrom)->iHeight; + ((mng_jhdrp)pChunkto)->iColortype = ((mng_jhdrp)pChunkfrom)->iColortype; + ((mng_jhdrp)pChunkto)->iImagesampledepth = ((mng_jhdrp)pChunkfrom)->iImagesampledepth; + ((mng_jhdrp)pChunkto)->iImagecompression = ((mng_jhdrp)pChunkfrom)->iImagecompression; + ((mng_jhdrp)pChunkto)->iImageinterlace = ((mng_jhdrp)pChunkfrom)->iImageinterlace; + ((mng_jhdrp)pChunkto)->iAlphasampledepth = ((mng_jhdrp)pChunkfrom)->iAlphasampledepth; + ((mng_jhdrp)pChunkto)->iAlphacompression = ((mng_jhdrp)pChunkfrom)->iAlphacompression; + ((mng_jhdrp)pChunkto)->iAlphafilter = ((mng_jhdrp)pChunkfrom)->iAlphafilter; + ((mng_jhdrp)pChunkto)->iAlphainterlace = ((mng_jhdrp)pChunkfrom)->iAlphainterlace; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_JHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_INCLUDE_JNG */ +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +ASSIGN_CHUNK_HDR (mng_assign_jdaa) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_JDAA, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_JDAA) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_jdaap)pChunkto)->bEmpty = ((mng_jdaap)pChunkfrom)->bEmpty; + ((mng_jdaap)pChunkto)->iDatasize = ((mng_jdaap)pChunkfrom)->iDatasize; + + if (((mng_jdaap)pChunkto)->iDatasize) + { + MNG_ALLOC (pData, ((mng_jdaap)pChunkto)->pData, ((mng_jdaap)pChunkto)->iDatasize); + MNG_COPY (((mng_jdaap)pChunkto)->pData, ((mng_jdaap)pChunkfrom)->pData, + ((mng_jdaap)pChunkto)->iDatasize); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_JDAA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +ASSIGN_CHUNK_HDR (mng_assign_jdat) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_JDAT, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_JDAT) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_jdatp)pChunkto)->bEmpty = ((mng_jdatp)pChunkfrom)->bEmpty; + ((mng_jdatp)pChunkto)->iDatasize = ((mng_jdatp)pChunkfrom)->iDatasize; + + if (((mng_jdatp)pChunkto)->iDatasize) + { + MNG_ALLOC (pData, ((mng_jdatp)pChunkto)->pData, ((mng_jdatp)pChunkto)->iDatasize); + MNG_COPY (((mng_jdatp)pChunkto)->pData, ((mng_jdatp)pChunkfrom)->pData, + ((mng_jdatp)pChunkto)->iDatasize); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_JDAT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +#ifdef MNG_INCLUDE_JNG +ASSIGN_CHUNK_HDR (mng_assign_jsep) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_JSEP, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_JSEP) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_JSEP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_INCLUDE_JNG */ +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +#ifndef MNG_NO_DELTA_PNG +ASSIGN_CHUNK_HDR (mng_assign_dhdr) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_DHDR, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_DHDR) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_dhdrp)pChunkto)->iObjectid = ((mng_dhdrp)pChunkfrom)->iObjectid; + ((mng_dhdrp)pChunkto)->iImagetype = ((mng_dhdrp)pChunkfrom)->iImagetype; + ((mng_dhdrp)pChunkto)->iDeltatype = ((mng_dhdrp)pChunkfrom)->iDeltatype; + ((mng_dhdrp)pChunkto)->iBlockwidth = ((mng_dhdrp)pChunkfrom)->iBlockwidth; + ((mng_dhdrp)pChunkto)->iBlockheight = ((mng_dhdrp)pChunkfrom)->iBlockheight; + ((mng_dhdrp)pChunkto)->iBlockx = ((mng_dhdrp)pChunkfrom)->iBlockx; + ((mng_dhdrp)pChunkto)->iBlocky = ((mng_dhdrp)pChunkfrom)->iBlocky; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_DHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +#ifndef MNG_NO_DELTA_PNG +ASSIGN_CHUNK_HDR (mng_assign_prom) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_PROM, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_PROM) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_promp)pChunkto)->iColortype = ((mng_promp)pChunkfrom)->iColortype; + ((mng_promp)pChunkto)->iSampledepth = ((mng_promp)pChunkfrom)->iSampledepth; + ((mng_promp)pChunkto)->iFilltype = ((mng_promp)pChunkfrom)->iFilltype; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_PROM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +#ifndef MNG_NO_DELTA_PNG +ASSIGN_CHUNK_HDR (mng_assign_ipng) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_IPNG, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_IPNG) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_IPNG, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +#ifndef MNG_NO_DELTA_PNG +ASSIGN_CHUNK_HDR (mng_assign_pplt) +{ + mng_uint32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_PPLT, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_PPLT) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_ppltp)pChunkto)->iDeltatype = ((mng_ppltp)pChunkfrom)->iDeltatype; + ((mng_ppltp)pChunkto)->iCount = ((mng_ppltp)pChunkfrom)->iCount; + + for (iX = 0; iX < ((mng_ppltp)pChunkto)->iCount; iX++) + ((mng_ppltp)pChunkto)->aEntries [iX] = ((mng_ppltp)pChunkfrom)->aEntries [iX]; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_PPLT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +#ifndef MNG_NO_DELTA_PNG +#ifdef MNG_INCLUDE_JNG +ASSIGN_CHUNK_HDR (mng_assign_ijng) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_IJNG, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_IJNG) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_IJNG, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +ASSIGN_CHUNK_HDR (mng_assign_drop) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_DROP, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_DROP) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_dropp)pChunkto)->iCount = ((mng_dropp)pChunkfrom)->iCount; + + if (((mng_dropp)pChunkto)->iCount) + { + mng_uint32 iLen = ((mng_dropp)pChunkto)->iCount * sizeof (mng_uint32); + + MNG_ALLOC (pData, ((mng_dropp)pChunkto)->pChunknames, iLen); + MNG_COPY (((mng_dropp)pChunkto)->pChunknames, ((mng_dropp)pChunkfrom)->pChunknames, iLen); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_DROP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_DBYK +ASSIGN_CHUNK_HDR (mng_assign_dbyk) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_DBYK, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_DBYK) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_dbykp)pChunkto)->iChunkname = ((mng_dbykp)pChunkfrom)->iChunkname; + ((mng_dbykp)pChunkto)->iPolarity = ((mng_dbykp)pChunkfrom)->iPolarity; + ((mng_dbykp)pChunkto)->iKeywordssize = ((mng_dbykp)pChunkfrom)->iKeywordssize; + + if (((mng_dbykp)pChunkto)->iKeywordssize) + { + MNG_ALLOC (pData, ((mng_dbykp)pChunkto)->zKeywords, ((mng_dbykp)pChunkto)->iKeywordssize); + MNG_COPY (((mng_dbykp)pChunkto)->zKeywords, ((mng_dbykp)pChunkfrom)->zKeywords, + ((mng_dbykp)pChunkto)->iKeywordssize); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_DBYK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_ORDR +ASSIGN_CHUNK_HDR (mng_assign_ordr) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_ORDR, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_ORDR) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_ordrp)pChunkto)->iCount = ((mng_ordrp)pChunkfrom)->iCount; + + if (((mng_ordrp)pChunkto)->iCount) + { + mng_uint32 iLen = ((mng_ordrp)pChunkto)->iCount * sizeof (mng_ordr_entry); + + MNG_ALLOC (pData, ((mng_ordrp)pChunkto)->pEntries, iLen); + MNG_COPY (((mng_ordrp)pChunkto)->pEntries, ((mng_ordrp)pChunkfrom)->pEntries, iLen); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_ORDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKASSIGN +#ifndef MNG_SKIPCHUNK_MAGN +ASSIGN_CHUNK_HDR (mng_assign_magn) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_MAGN, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_MAGN) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_magnp)pChunkto)->iFirstid = ((mng_magnp)pChunkfrom)->iFirstid; + ((mng_magnp)pChunkto)->iLastid = ((mng_magnp)pChunkfrom)->iLastid; + ((mng_magnp)pChunkto)->iMethodX = ((mng_magnp)pChunkfrom)->iMethodX; + ((mng_magnp)pChunkto)->iMX = ((mng_magnp)pChunkfrom)->iMX; + ((mng_magnp)pChunkto)->iMY = ((mng_magnp)pChunkfrom)->iMY; + ((mng_magnp)pChunkto)->iML = ((mng_magnp)pChunkfrom)->iML; + ((mng_magnp)pChunkto)->iMR = ((mng_magnp)pChunkfrom)->iMR; + ((mng_magnp)pChunkto)->iMT = ((mng_magnp)pChunkfrom)->iMT; + ((mng_magnp)pChunkto)->iMB = ((mng_magnp)pChunkfrom)->iMB; + ((mng_magnp)pChunkto)->iMethodY = ((mng_magnp)pChunkfrom)->iMethodY; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_MAGN, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_MPNG_PROPOSAL +ASSIGN_CHUNK_HDR (mng_assign_mpng) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_MPNG, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_mpNG) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_mpngp)pChunkto)->iFramewidth = ((mng_mpngp)pChunkfrom)->iFramewidth; + ((mng_mpngp)pChunkto)->iFrameheight = ((mng_mpngp)pChunkfrom)->iFrameheight; + ((mng_mpngp)pChunkto)->iNumplays = ((mng_mpngp)pChunkfrom)->iNumplays; + ((mng_mpngp)pChunkto)->iTickspersec = ((mng_mpngp)pChunkfrom)->iTickspersec; + ((mng_mpngp)pChunkto)->iCompressionmethod = ((mng_mpngp)pChunkfrom)->iCompressionmethod; + ((mng_mpngp)pChunkto)->iFramessize = ((mng_mpngp)pChunkfrom)->iFramessize; + + if (((mng_mpngp)pChunkto)->iFramessize) + { + MNG_ALLOC (pData, ((mng_mpngp)pChunkto)->pFrames, ((mng_mpngp)pChunkto)->iFramessize); + MNG_COPY (((mng_mpngp)pChunkto)->pFrames, ((mng_mpngp)pChunkfrom)->pFrames, + ((mng_mpngp)pChunkto)->iFramessize); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_MPNG, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_ANG_PROPOSAL +ASSIGN_CHUNK_HDR (mng_assign_ahdr) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_AHDR, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_ahDR) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_ahdrp)pChunkto)->iNumframes = ((mng_ahdrp)pChunkfrom)->iNumframes; + ((mng_ahdrp)pChunkto)->iTickspersec = ((mng_ahdrp)pChunkfrom)->iTickspersec; + ((mng_ahdrp)pChunkto)->iNumplays = ((mng_ahdrp)pChunkfrom)->iNumplays; + ((mng_ahdrp)pChunkto)->iTilewidth = ((mng_ahdrp)pChunkfrom)->iTilewidth; + ((mng_ahdrp)pChunkto)->iTileheight = ((mng_ahdrp)pChunkfrom)->iTileheight; + ((mng_ahdrp)pChunkto)->iInterlace = ((mng_ahdrp)pChunkfrom)->iInterlace; + ((mng_ahdrp)pChunkto)->iStillused = ((mng_ahdrp)pChunkfrom)->iStillused; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_AHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_ANG_PROPOSAL +ASSIGN_CHUNK_HDR (mng_assign_adat) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_ADAT, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_adAT) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_adatp)pChunkto)->iTilessize = ((mng_adatp)pChunkfrom)->iTilessize; + + if (((mng_adatp)pChunkto)->iTilessize) + { + MNG_ALLOC (pData, ((mng_adatp)pChunkto)->pTiles, ((mng_adatp)pChunkto)->iTilessize); + MNG_COPY (((mng_adatp)pChunkto)->pTiles, ((mng_adatp)pChunkfrom)->pTiles, + ((mng_adatp)pChunkto)->iTilessize); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_ADAT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_evNT +ASSIGN_CHUNK_HDR (mng_assign_evnt) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_EVNT, MNG_LC_START); +#endif + + if (((mng_chunk_headerp)pChunkfrom)->iChunkname != MNG_UINT_evNT) + MNG_ERROR (pData, MNG_WRONGCHUNK); /* ouch */ + + ((mng_evntp)pChunkto)->iCount = ((mng_evntp)pChunkfrom)->iCount; + + if (((mng_evntp)pChunkto)->iCount) + { + mng_uint32 iX; + mng_evnt_entryp pEntry; + mng_uint32 iLen = ((mng_evntp)pChunkto)->iCount * sizeof (mng_evnt_entry); + + MNG_ALLOC (pData, ((mng_evntp)pChunkto)->pEntries, iLen); + MNG_COPY (((mng_evntp)pChunkto)->pEntries, ((mng_evntp)pChunkfrom)->pEntries, iLen); + + pEntry = ((mng_evntp)pChunkto)->pEntries; + + for (iX = 0; iX < ((mng_evntp)pChunkto)->iCount; iX++) + { + if (pEntry->iSegmentnamesize) + { + mng_pchar pTemp = pEntry->zSegmentname; + + MNG_ALLOC (pData, pEntry->zSegmentname, pEntry->iSegmentnamesize+1); + MNG_COPY (pEntry->zSegmentname, pTemp, pEntry->iSegmentnamesize); + } + else + { + pEntry->zSegmentname = MNG_NULL; + } + + pEntry++; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_EVNT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +ASSIGN_CHUNK_HDR (mng_assign_unknown) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_UNKNOWN, MNG_LC_START); +#endif + + ((mng_unknown_chunkp)pChunkto)->iDatasize = ((mng_unknown_chunkp)pChunkfrom)->iDatasize; + + if (((mng_unknown_chunkp)pChunkto)->iDatasize) + { + MNG_ALLOC (pData, ((mng_unknown_chunkp)pChunkto)->pData, ((mng_unknown_chunkp)pChunkto)->iDatasize); + MNG_COPY (((mng_unknown_chunkp)pChunkto)->pData, ((mng_unknown_chunkp)pChunkfrom)->pData, + ((mng_unknown_chunkp)pChunkto)->iDatasize); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ASSIGN_UNKNOWN, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_WRITE_PROCS */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ + diff --git a/Engine/lib/lmng/libmng_chunk_prc.h b/Engine/lib/lmng/libmng_chunk_prc.h new file mode 100644 index 000000000..0cf0f3c1d --- /dev/null +++ b/Engine/lib/lmng/libmng_chunk_prc.h @@ -0,0 +1,381 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_chunk_prc.h copyright (c) 2000-2007 G.Juyn * */ +/* * version : 1.0.10 * */ +/* * * */ +/* * purpose : Chunk initialization & cleanup (definition) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : definition of the chunk initialization & cleanup routines * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed strict-ANSI stuff * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 0.9.3 - 08/26/2000 - G.Juyn * */ +/* * - added MAGN chunk * */ +/* * 0.9.3 - 10/16/2000 - G.Juyn * */ +/* * - added support for JDAA * */ +/* * * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * 1.0.5 - 09/14/2002 - G.Juyn * */ +/* * - added event handling for dynamic MNG * */ +/* * * */ +/* * 1.0.6 - 07/07/2003 - G.R-P * */ +/* * - added NO_DELTA_PNG support * */ +/* * 1.0.6 - 07/29/2003 - G.R-P * */ +/* * - added conditionals around PAST chunk support * */ +/* * * */ +/* * 1.0.9 - 12/05/2004 - G.Juyn * */ +/* * - added conditional MNG_OPTIMIZE_CHUNKINITFREE * */ +/* * 1.0.9 - 12/06/2004 - G.Juyn * */ +/* * - added conditional MNG_OPTIMIZE_CHUNKASSIGN * */ +/* * * */ +/* * 1.0.10 - 04/08/2007 - G.Juyn * */ +/* * - added support for mPNG proposal * */ +/* * 1.0.10 - 04/12/2007 - G.Juyn * */ +/* * - added support for ANG proposal * */ +/* * * */ +/* ************************************************************************** */ + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +#ifndef _libmng_chunk_prc_h_ +#define _libmng_chunk_prc_h_ + +/* ************************************************************************** */ + +void mng_add_chunk (mng_datap pData, + mng_chunkp pChunk); + +/* ************************************************************************** */ + +#define INIT_CHUNK_HDR(n) mng_retcode n (mng_datap pData, \ + mng_chunkp pHeader, \ + mng_chunkp* ppChunk) + +#ifdef MNG_OPTIMIZE_CHUNKINITFREE +INIT_CHUNK_HDR (mng_init_general) ; +#else +INIT_CHUNK_HDR (mng_init_ihdr) ; +INIT_CHUNK_HDR (mng_init_plte) ; +INIT_CHUNK_HDR (mng_init_idat) ; +INIT_CHUNK_HDR (mng_init_iend) ; +INIT_CHUNK_HDR (mng_init_trns) ; +INIT_CHUNK_HDR (mng_init_gama) ; +INIT_CHUNK_HDR (mng_init_chrm) ; +INIT_CHUNK_HDR (mng_init_srgb) ; +INIT_CHUNK_HDR (mng_init_iccp) ; +INIT_CHUNK_HDR (mng_init_text) ; +INIT_CHUNK_HDR (mng_init_ztxt) ; +INIT_CHUNK_HDR (mng_init_itxt) ; +INIT_CHUNK_HDR (mng_init_bkgd) ; +INIT_CHUNK_HDR (mng_init_phys) ; +INIT_CHUNK_HDR (mng_init_sbit) ; +INIT_CHUNK_HDR (mng_init_splt) ; +INIT_CHUNK_HDR (mng_init_hist) ; +INIT_CHUNK_HDR (mng_init_time) ; +INIT_CHUNK_HDR (mng_init_mhdr) ; +INIT_CHUNK_HDR (mng_init_mend) ; +INIT_CHUNK_HDR (mng_init_loop) ; +INIT_CHUNK_HDR (mng_init_endl) ; +INIT_CHUNK_HDR (mng_init_defi) ; +INIT_CHUNK_HDR (mng_init_basi) ; +INIT_CHUNK_HDR (mng_init_clon) ; +#ifndef MNG_SKIPCHUNK_PAST +INIT_CHUNK_HDR (mng_init_past) ; +#endif +INIT_CHUNK_HDR (mng_init_disc) ; +INIT_CHUNK_HDR (mng_init_back) ; +INIT_CHUNK_HDR (mng_init_fram) ; +INIT_CHUNK_HDR (mng_init_move) ; +INIT_CHUNK_HDR (mng_init_clip) ; +INIT_CHUNK_HDR (mng_init_show) ; +INIT_CHUNK_HDR (mng_init_term) ; +INIT_CHUNK_HDR (mng_init_save) ; +INIT_CHUNK_HDR (mng_init_seek) ; +INIT_CHUNK_HDR (mng_init_expi) ; +INIT_CHUNK_HDR (mng_init_fpri) ; +INIT_CHUNK_HDR (mng_init_need) ; +INIT_CHUNK_HDR (mng_init_phyg) ; +#ifdef MNG_INCLUDE_JNG +INIT_CHUNK_HDR (mng_init_jhdr) ; +INIT_CHUNK_HDR (mng_init_jdaa) ; +INIT_CHUNK_HDR (mng_init_jdat) ; +INIT_CHUNK_HDR (mng_init_jsep) ; +#endif +#ifndef MNG_NO_DELTA_PNG +INIT_CHUNK_HDR (mng_init_dhdr) ; +INIT_CHUNK_HDR (mng_init_prom) ; +INIT_CHUNK_HDR (mng_init_ipng) ; +INIT_CHUNK_HDR (mng_init_pplt) ; +#ifdef MNG_INCLUDE_JNG +INIT_CHUNK_HDR (mng_init_ijng) ; +#endif +INIT_CHUNK_HDR (mng_init_drop) ; +INIT_CHUNK_HDR (mng_init_dbyk) ; +INIT_CHUNK_HDR (mng_init_ordr) ; +#endif +INIT_CHUNK_HDR (mng_init_magn) ; +INIT_CHUNK_HDR (mng_init_evnt) ; +INIT_CHUNK_HDR (mng_init_unknown) ; +#endif /* MNG_OPTIMIZE_CHUNKINITFREE */ + +/* ************************************************************************** */ + +#define FREE_CHUNK_HDR(n) mng_retcode n (mng_datap pData, \ + mng_chunkp pHeader) + +#ifdef MNG_OPTIMIZE_CHUNKINITFREE +FREE_CHUNK_HDR (mng_free_general) ; +#else /* MNG_OPTIMIZE_CHUNKINITFREE */ +FREE_CHUNK_HDR (mng_free_ihdr) ; +FREE_CHUNK_HDR (mng_free_plte) ; +FREE_CHUNK_HDR (mng_free_iend) ; +FREE_CHUNK_HDR (mng_free_trns) ; +FREE_CHUNK_HDR (mng_free_gama) ; +FREE_CHUNK_HDR (mng_free_chrm) ; +FREE_CHUNK_HDR (mng_free_srgb) ; +FREE_CHUNK_HDR (mng_free_bkgd) ; +FREE_CHUNK_HDR (mng_free_phys) ; +FREE_CHUNK_HDR (mng_free_sbit) ; +FREE_CHUNK_HDR (mng_free_hist) ; +FREE_CHUNK_HDR (mng_free_time) ; +FREE_CHUNK_HDR (mng_free_mhdr) ; +FREE_CHUNK_HDR (mng_free_mend) ; +FREE_CHUNK_HDR (mng_free_endl) ; +FREE_CHUNK_HDR (mng_free_defi) ; +FREE_CHUNK_HDR (mng_free_basi) ; +FREE_CHUNK_HDR (mng_free_clon) ; +FREE_CHUNK_HDR (mng_free_back) ; +FREE_CHUNK_HDR (mng_free_move) ; +FREE_CHUNK_HDR (mng_free_clip) ; +FREE_CHUNK_HDR (mng_free_show) ; +FREE_CHUNK_HDR (mng_free_term) ; +FREE_CHUNK_HDR (mng_free_fpri) ; +FREE_CHUNK_HDR (mng_free_phyg) ; +#ifdef MNG_INCLUDE_JNG +FREE_CHUNK_HDR (mng_free_jhdr) ; +FREE_CHUNK_HDR (mng_free_jsep) ; +#endif +#ifndef MNG_NO_DELTA_PNG +FREE_CHUNK_HDR (mng_free_dhdr) ; +FREE_CHUNK_HDR (mng_free_prom) ; +FREE_CHUNK_HDR (mng_free_ipng) ; +FREE_CHUNK_HDR (mng_free_pplt) ; +#ifdef MNG_INCLUDE_JNG +FREE_CHUNK_HDR (mng_free_ijng) ; +#endif +#endif +FREE_CHUNK_HDR (mng_free_magn) ; +#endif /* MNG_OPTIMIZE_CHUNKINITFREE */ + +FREE_CHUNK_HDR (mng_free_idat) ; +FREE_CHUNK_HDR (mng_free_iccp) ; +FREE_CHUNK_HDR (mng_free_text) ; +FREE_CHUNK_HDR (mng_free_ztxt) ; +FREE_CHUNK_HDR (mng_free_itxt) ; +FREE_CHUNK_HDR (mng_free_splt) ; +FREE_CHUNK_HDR (mng_free_loop) ; +#ifndef MNG_SKIPCHUNK_PAST +FREE_CHUNK_HDR (mng_free_past) ; +#endif +FREE_CHUNK_HDR (mng_free_disc) ; +FREE_CHUNK_HDR (mng_free_fram) ; +FREE_CHUNK_HDR (mng_free_save) ; +FREE_CHUNK_HDR (mng_free_seek) ; +FREE_CHUNK_HDR (mng_free_expi) ; +FREE_CHUNK_HDR (mng_free_need) ; +#ifdef MNG_INCLUDE_JNG +FREE_CHUNK_HDR (mng_free_jdaa) ; +FREE_CHUNK_HDR (mng_free_jdat) ; +#endif +#ifndef MNG_NO_DELTA_PNG +FREE_CHUNK_HDR (mng_free_drop) ; +FREE_CHUNK_HDR (mng_free_dbyk) ; +FREE_CHUNK_HDR (mng_free_ordr) ; +#endif +#ifdef MNG_INCLUDE_MPNG_PROPOSAL +FREE_CHUNK_HDR (mng_free_mpng) ; +#endif +#ifdef MNG_INCLUDE_ANG_PROPOSAL +FREE_CHUNK_HDR (mng_free_adat) ; +#endif +FREE_CHUNK_HDR (mng_free_evnt) ; +FREE_CHUNK_HDR (mng_free_unknown) ; + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_WRITE_PROCS + +#define ASSIGN_CHUNK_HDR(n) mng_retcode n (mng_datap pData, \ + mng_chunkp pChunkto, \ + mng_chunkp pChunkfrom) + +#ifdef MNG_OPTIMIZE_CHUNKASSIGN +ASSIGN_CHUNK_HDR (mng_assign_general) ; +#else /* MNG_OPTIMIZE_CHUNKASSIGN */ +ASSIGN_CHUNK_HDR (mng_assign_ihdr) ; +ASSIGN_CHUNK_HDR (mng_assign_plte) ; +ASSIGN_CHUNK_HDR (mng_assign_iend) ; +ASSIGN_CHUNK_HDR (mng_assign_trns) ; +ASSIGN_CHUNK_HDR (mng_assign_gama) ; +ASSIGN_CHUNK_HDR (mng_assign_chrm) ; +ASSIGN_CHUNK_HDR (mng_assign_srgb) ; +ASSIGN_CHUNK_HDR (mng_assign_bkgd) ; +ASSIGN_CHUNK_HDR (mng_assign_phys) ; +ASSIGN_CHUNK_HDR (mng_assign_sbit) ; +ASSIGN_CHUNK_HDR (mng_assign_hist) ; +ASSIGN_CHUNK_HDR (mng_assign_time) ; +ASSIGN_CHUNK_HDR (mng_assign_mhdr) ; +ASSIGN_CHUNK_HDR (mng_assign_mend) ; +ASSIGN_CHUNK_HDR (mng_assign_endl) ; +ASSIGN_CHUNK_HDR (mng_assign_defi) ; +ASSIGN_CHUNK_HDR (mng_assign_basi) ; +ASSIGN_CHUNK_HDR (mng_assign_clon) ; +ASSIGN_CHUNK_HDR (mng_assign_back) ; +ASSIGN_CHUNK_HDR (mng_assign_move) ; +ASSIGN_CHUNK_HDR (mng_assign_clip) ; +ASSIGN_CHUNK_HDR (mng_assign_show) ; +ASSIGN_CHUNK_HDR (mng_assign_term) ; +ASSIGN_CHUNK_HDR (mng_assign_fpri) ; +ASSIGN_CHUNK_HDR (mng_assign_phyg) ; +#ifdef MNG_INCLUDE_JNG +ASSIGN_CHUNK_HDR (mng_assign_jhdr) ; +ASSIGN_CHUNK_HDR (mng_assign_jsep) ; +#endif +#ifndef MNG_NO_DELTA_PNG +ASSIGN_CHUNK_HDR (mng_assign_dhdr) ; +ASSIGN_CHUNK_HDR (mng_assign_prom) ; +ASSIGN_CHUNK_HDR (mng_assign_ipng) ; +ASSIGN_CHUNK_HDR (mng_assign_pplt) ; +#ifdef MNG_INCLUDE_JNG +ASSIGN_CHUNK_HDR (mng_assign_ijng) ; +#endif +#endif +ASSIGN_CHUNK_HDR (mng_assign_magn) ; +#endif /* MNG_OPTIMIZE_CHUNKASSIGN */ + +ASSIGN_CHUNK_HDR (mng_assign_idat) ; +ASSIGN_CHUNK_HDR (mng_assign_iccp) ; +ASSIGN_CHUNK_HDR (mng_assign_text) ; +ASSIGN_CHUNK_HDR (mng_assign_ztxt) ; +ASSIGN_CHUNK_HDR (mng_assign_itxt) ; +ASSIGN_CHUNK_HDR (mng_assign_splt) ; +ASSIGN_CHUNK_HDR (mng_assign_loop) ; +#ifndef MNG_SKIPCHUNK_PAST +ASSIGN_CHUNK_HDR (mng_assign_past) ; +#endif +ASSIGN_CHUNK_HDR (mng_assign_disc) ; +ASSIGN_CHUNK_HDR (mng_assign_fram) ; +ASSIGN_CHUNK_HDR (mng_assign_save) ; +ASSIGN_CHUNK_HDR (mng_assign_seek) ; +ASSIGN_CHUNK_HDR (mng_assign_need) ; +ASSIGN_CHUNK_HDR (mng_assign_expi) ; +#ifdef MNG_INCLUDE_JNG +ASSIGN_CHUNK_HDR (mng_assign_jdaa) ; +ASSIGN_CHUNK_HDR (mng_assign_jdat) ; +#endif +#ifndef MNG_NO_DELTA_PNG +ASSIGN_CHUNK_HDR (mng_assign_drop) ; +ASSIGN_CHUNK_HDR (mng_assign_dbyk) ; +ASSIGN_CHUNK_HDR (mng_assign_ordr) ; +#endif +#ifdef MNG_INCLUDE_MPNG_PROPOSAL +ASSIGN_CHUNK_HDR (mng_assign_mpng) ; +#endif +#ifdef MNG_INCLUDE_ANG_PROPOSAL +ASSIGN_CHUNK_HDR (mng_assign_ahdr) ; +ASSIGN_CHUNK_HDR (mng_assign_adat) ; +#endif +ASSIGN_CHUNK_HDR (mng_assign_evnt) ; +ASSIGN_CHUNK_HDR (mng_assign_unknown) ; + +/* ************************************************************************** */ + +#else /* MNG_INCLUDE_WRITE_PROCS */ +#define mng_assign_general 0 +#define mng_assign_ihdr 0 +#define mng_assign_plte 0 +#define mng_assign_idat 0 +#define mng_assign_iend 0 +#define mng_assign_trns 0 +#define mng_assign_gama 0 +#define mng_assign_chrm 0 +#define mng_assign_srgb 0 +#define mng_assign_iccp 0 +#define mng_assign_text 0 +#define mng_assign_ztxt 0 +#define mng_assign_itxt 0 +#define mng_assign_bkgd 0 +#define mng_assign_phys 0 +#define mng_assign_sbit 0 +#define mng_assign_splt 0 +#define mng_assign_hist 0 +#define mng_assign_time 0 +#define mng_assign_mhdr 0 +#define mng_assign_mend 0 +#define mng_assign_loop 0 +#define mng_assign_endl 0 +#define mng_assign_defi 0 +#define mng_assign_basi 0 +#define mng_assign_clon 0 +#ifndef MNG_SKIPCHUNK_PAST +#define mng_assign_past 0 +#endif +#define mng_assign_disc 0 +#define mng_assign_back 0 +#define mng_assign_fram 0 +#define mng_assign_move 0 +#define mng_assign_clip 0 +#define mng_assign_show 0 +#define mng_assign_term 0 +#define mng_assign_save 0 +#define mng_assign_seek 0 +#define mng_assign_expi 0 +#define mng_assign_fpri 0 +#define mng_assign_phyg 0 +#ifdef MNG_INCLUDE_JNG +#define mng_assign_jhdr 0 +#define mng_assign_jdaa 0 +#define mng_assign_jdat 0 +#define mng_assign_jsep 0 +#endif +#ifndef MNG_NO_DELTA_PNG +#define mng_assign_dhdr 0 +#define mng_assign_prom 0 +#define mng_assign_ipng 0 +#define mng_assign_pplt 0 +#ifdef MNG_INCLUDE_JNG +#define mng_assign_ijng 0 +#endif +#define mng_assign_drop 0 +#define mng_assign_dbyk 0 +#define mng_assign_ordr 0 +#endif +#define mng_assign_magn 0 +#define mng_assign_need 0 +#define mng_assign_mpng 0 +#define mng_assign_ahdr 0 +#define mng_assign_adat 0 +#define mng_assign_evnt 0 +#define mng_assign_unknown 0 +#endif /* MNG_INCLUDE_WRITE_PROCS */ + +/* ************************************************************************** */ + +#endif /* _libmng_chunk_prc_h_ */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ diff --git a/Engine/lib/lmng/libmng_chunk_xs.c b/Engine/lib/lmng/libmng_chunk_xs.c new file mode 100644 index 000000000..13114090d --- /dev/null +++ b/Engine/lib/lmng/libmng_chunk_xs.c @@ -0,0 +1,7016 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_chunk_xs.c copyright (c) 2000-2007 G.Juyn * */ +/* * version : 1.0.10 * */ +/* * * */ +/* * purpose : chunk access functions (implementation) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : implementation of the chunk access functions * */ +/* * * */ +/* * changes : 0.5.1 - 05/06/2000 - G.Juyn * */ +/* * - changed and filled iterate-chunk function * */ +/* * 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - fixed calling convention * */ +/* * - added getchunk functions * */ +/* * - added putchunk functions * */ +/* * - changed strict-ANSI stuff * */ +/* * 0.5.1 - 05/11/2000 - G.Juyn * */ +/* * - added empty-chunk put-routines * */ +/* * 0.5.1 - 05/12/2000 - G.Juyn * */ +/* * - changed trace to macro for callback error-reporting * */ +/* * 0.5.1 - 05/15/2000 - G.Juyn * */ +/* * - added getimgdata & putimgdata functions * */ +/* * * */ +/* * 0.5.2 - 05/19/2000 - G.Juyn * */ +/* * - B004 - fixed problem with MNG_SUPPORT_WRITE not defined * */ +/* * also for MNG_SUPPORT_WRITE without MNG_INCLUDE_JNG * */ +/* * - Cleaned up some code regarding mixed support * */ +/* * * */ +/* * 0.9.1 - 07/19/2000 - G.Juyn * */ +/* * - fixed creation-code * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * - added function to set simplicity field * */ +/* * - fixed putchunk_unknown() function * */ +/* * * */ +/* * 0.9.3 - 08/07/2000 - G.Juyn * */ +/* * - B111300 - fixup for improved portability * */ +/* * 0.9.3 - 08/26/2000 - G.Juyn * */ +/* * - added MAGN chunk * */ +/* * 0.9.3 - 10/20/2000 - G.Juyn * */ +/* * - fixed putchunk_plte() to set bEmpty parameter * */ +/* * * */ +/* * 0.9.5 - 01/25/2001 - G.Juyn * */ +/* * - fixed some small compiler warnings (thanks Nikki) * */ +/* * * */ +/* * 1.0.5 - 09/07/2002 - G.Juyn * */ +/* * - B578940 - unimplemented functions return errorcode * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * - added HLAPI function to copy chunks * */ +/* * 1.0.5 - 09/14/2002 - G.Juyn * */ +/* * - added event handling for dynamic MNG * */ +/* * 1.0.5 - 10/07/2002 - G.Juyn * */ +/* * - added check for TERM placement during create/write * */ +/* * 1.0.5 - 11/28/2002 - G.Juyn * */ +/* * - fixed definition of iMethodX/Y for MAGN chunk * */ +/* * * */ +/* * 1.0.6 - 05/25/2003 - G.R-P * */ +/* * - added MNG_SKIPCHUNK_cHNK footprint optimizations * */ +/* * 1.0.6 - 07/07/2003 - G.R-P * */ +/* * - added MNG_NO_DELTA_PNG reduction and more SKIPCHUNK * */ +/* * optimizations * */ +/* * 1.0.6 - 07/29/2003 - G.R-P * */ +/* * - added conditionals around PAST chunk support * */ +/* * 1.0.6 - 08/17/2003 - G.R-P * */ +/* * - added conditionals around non-VLC chunk support * */ +/* * * */ +/* * 1.0.8 - 04/01/2004 - G.Juyn * */ +/* * - added missing get-/put-chunk-jdaa * */ +/* * 1.0.8 - 08/02/2004 - G.Juyn * */ +/* * - added conditional to allow easier writing of large MNG's * */ +/* * * */ +/* * 1.0.9 - 09/17/2004 - G.R-P * */ +/* * - added two more conditionals * */ +/* * 1.0.9 - 09/25/2004 - G.Juyn * */ +/* * - replaced MNG_TWEAK_LARGE_FILES with permanent solution * */ +/* * 1.0.9 - 17/14/2004 - G.Juyn * */ +/* * - fixed PPLT getchunk/putchunk routines * */ +/* * 1.0.9 - 12/05/2004 - G.Juyn * */ +/* * - added conditional MNG_OPTIMIZE_CHUNKINITFREE * */ +/* * 1.0.9 - 12/20/2004 - G.Juyn * */ +/* * - cleaned up macro-invocations (thanks to D. Airlie) * */ +/* * * */ +/* * 1.0.10 - 04/08/2007 - G.Juyn * */ +/* * - added support for mPNG proposal * */ +/* * * */ +/* ************************************************************************** */ + +#include "libmng.h" +#include "libmng_data.h" +#include "libmng_error.h" +#include "libmng_trace.h" +#ifdef __BORLANDC__ +#pragma hdrstop +#endif +#include "libmng_memory.h" +#include "libmng_chunks.h" +#ifdef MNG_OPTIMIZE_CHUNKREADER +#include "libmng_chunk_descr.h" +#endif +#include "libmng_chunk_prc.h" +#include "libmng_chunk_io.h" + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +/* ************************************************************************** */ + +#ifdef MNG_ACCESS_CHUNKS + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_iterate_chunks (mng_handle hHandle, + mng_uint32 iChunkseq, + mng_iteratechunk fProc) +{ + mng_uint32 iSeq; + mng_chunkid iChunkname; + mng_datap pData; + mng_chunkp pChunk; + mng_bool bCont; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_ITERATE_CHUNKS, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = ((mng_datap)hHandle); /* and make it addressable */ + + iSeq = 0; + bCont = MNG_TRUE; + pChunk = pData->pFirstchunk; /* get the first chunk */ + /* as long as there are some more */ + while ((pChunk) && (bCont)) /* and the app didn't signal a stop */ + { + if (iSeq >= iChunkseq) /* reached the first target ? */ + { /* then call this and next ones back in... */ + iChunkname = ((mng_chunk_headerp)pChunk)->iChunkname; + bCont = fProc (hHandle, (mng_handle)pChunk, iChunkname, iSeq); + } + + iSeq++; /* next one */ + pChunk = ((mng_chunk_headerp)pChunk)->pNext; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_ITERATE_CHUNKS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_WRITE +mng_retcode MNG_DECL mng_copy_chunk (mng_handle hHandle, + mng_handle hChunk, + mng_handle hHandleOut) +{ + mng_datap pDataOut; + mng_chunkp pChunk; + mng_chunkp pChunkOut; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_COPY_CHUNK, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handles */ + MNG_VALIDHANDLE (hHandleOut) + + pDataOut = (mng_datap)hHandleOut; /* make outhandle addressable */ + pChunk = (mng_chunkp)hChunk; /* address the chunk */ + + if (!pDataOut->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pDataOut, MNG_FUNCTIONINVALID) + /* create a new chunk */ + iRetcode = ((mng_createchunk)((mng_chunk_headerp)pChunk)->fCreate) + (pDataOut, ((mng_chunk_headerp)pChunk), &pChunkOut); + if (!iRetcode) /* assign the chunk-specific data */ + iRetcode = ((mng_assignchunk)((mng_chunk_headerp)pChunk)->fAssign) + (pDataOut, pChunkOut, pChunk); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + mng_add_chunk (pDataOut, pChunkOut); /* and put it in the output-stream */ + + /* could it be the end of the chain ? */ + if (((mng_chunk_headerp)pChunkOut)->iChunkname == MNG_UINT_IEND) + { +#ifdef MNG_INCLUDE_JNG + if ((pDataOut->iFirstchunkadded == MNG_UINT_IHDR) || + (pDataOut->iFirstchunkadded == MNG_UINT_JHDR) ) +#else + if (pDataOut->iFirstchunkadded == MNG_UINT_IHDR) +#endif + pDataOut->bCreating = MNG_FALSE; /* right; this should be the last chunk !!! */ + } + + if (((mng_chunk_headerp)pChunkOut)->iChunkname == MNG_UINT_MEND) + pDataOut->bCreating = MNG_FALSE; /* definitely this should be the last !!! */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_COPY_CHUNK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_WRITE */ + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_getchunk_ihdr (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iWidth, + mng_uint32 *iHeight, + mng_uint8 *iBitdepth, + mng_uint8 *iColortype, + mng_uint8 *iCompression, + mng_uint8 *iFilter, + mng_uint8 *iInterlace) +{ + mng_datap pData; + mng_ihdrp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_IHDR, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_ihdrp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_IHDR) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iWidth = pChunk->iWidth; /* fill the fields */ + *iHeight = pChunk->iHeight; + *iBitdepth = pChunk->iBitdepth; + *iColortype = pChunk->iColortype; + *iCompression = pChunk->iCompression; + *iFilter = pChunk->iFilter; + *iInterlace = pChunk->iInterlace; + + /* fill the chunk */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_IHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_getchunk_plte (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iCount, + mng_palette8 *aPalette) +{ + mng_datap pData; + mng_pltep pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PLTE, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_pltep)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_PLTE) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iCount = pChunk->iEntrycount; /* fill the fields */ + + MNG_COPY (*aPalette, pChunk->aEntries, sizeof (mng_palette8)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PLTE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_getchunk_idat (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iRawlen, + mng_ptr *pRawdata) +{ + mng_datap pData; + mng_idatp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_IDAT, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_idatp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_IDAT) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iRawlen = pChunk->iDatasize; /* fill the fields */ + *pRawdata = pChunk->pData; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_IDAT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_getchunk_trns (mng_handle hHandle, + mng_handle hChunk, + mng_bool *bEmpty, + mng_bool *bGlobal, + mng_uint8 *iType, + mng_uint32 *iCount, + mng_uint8arr *aAlphas, + mng_uint16 *iGray, + mng_uint16 *iRed, + mng_uint16 *iGreen, + mng_uint16 *iBlue, + mng_uint32 *iRawlen, + mng_uint8arr *aRawdata) +{ + mng_datap pData; + mng_trnsp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_TRNS, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_trnsp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_tRNS) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *bEmpty = pChunk->bEmpty; /* fill the fields */ + *bGlobal = pChunk->bGlobal; + *iType = pChunk->iType; + *iCount = pChunk->iCount; + *iGray = pChunk->iGray; + *iRed = pChunk->iRed; + *iGreen = pChunk->iGreen; + *iBlue = pChunk->iBlue; + *iRawlen = pChunk->iRawlen; + + MNG_COPY (*aAlphas, pChunk->aEntries, sizeof (mng_uint8arr)); + MNG_COPY (*aRawdata, pChunk->aRawdata, sizeof (mng_uint8arr)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_TRNS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_gAMA +mng_retcode MNG_DECL mng_getchunk_gama (mng_handle hHandle, + mng_handle hChunk, + mng_bool *bEmpty, + mng_uint32 *iGamma) +{ + mng_datap pData; + mng_gamap pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_GAMA, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_gamap)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_gAMA) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *bEmpty = pChunk->bEmpty; /* fill the fields */ + *iGamma = pChunk->iGamma; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_GAMA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_cHRM +mng_retcode MNG_DECL mng_getchunk_chrm (mng_handle hHandle, + mng_handle hChunk, + mng_bool *bEmpty, + mng_uint32 *iWhitepointx, + mng_uint32 *iWhitepointy, + mng_uint32 *iRedx, + mng_uint32 *iRedy, + mng_uint32 *iGreenx, + mng_uint32 *iGreeny, + mng_uint32 *iBluex, + mng_uint32 *iBluey) +{ + mng_datap pData; + mng_chrmp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_CHRM, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_chrmp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_cHRM) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *bEmpty = pChunk->bEmpty; /* fill the fields */ + *iWhitepointx = pChunk->iWhitepointx; + *iWhitepointy = pChunk->iWhitepointy; + *iRedx = pChunk->iRedx; + *iRedy = pChunk->iRedy; + *iGreenx = pChunk->iGreenx; + *iGreeny = pChunk->iGreeny; + *iBluex = pChunk->iBluex; + *iBluey = pChunk->iBluey; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_CHRM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_sRGB +mng_retcode MNG_DECL mng_getchunk_srgb (mng_handle hHandle, + mng_handle hChunk, + mng_bool *bEmpty, + mng_uint8 *iRenderingintent) +{ + mng_datap pData; + mng_srgbp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SRGB, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_srgbp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_sRGB) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *bEmpty = pChunk->bEmpty; /* fill the fields */ + *iRenderingintent = pChunk->iRenderingintent; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SRGB, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_iCCP +mng_retcode MNG_DECL mng_getchunk_iccp (mng_handle hHandle, + mng_handle hChunk, + mng_bool *bEmpty, + mng_uint32 *iNamesize, + mng_pchar *zName, + mng_uint8 *iCompression, + mng_uint32 *iProfilesize, + mng_ptr *pProfile) +{ + mng_datap pData; + mng_iccpp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_ICCP, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_iccpp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_iCCP) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *bEmpty = pChunk->bEmpty; /* fill the fields */ + *iNamesize = pChunk->iNamesize; + *zName = pChunk->zName; + *iCompression = pChunk->iCompression; + *iProfilesize = pChunk->iProfilesize; + *pProfile = pChunk->pProfile; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_ICCP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_tEXt +mng_retcode MNG_DECL mng_getchunk_text (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iKeywordsize, + mng_pchar *zKeyword, + mng_uint32 *iTextsize, + mng_pchar *zText) +{ + mng_datap pData; + mng_textp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_TEXT, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_textp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_tEXt) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + /* fill the fields */ + *iKeywordsize = pChunk->iKeywordsize; + *zKeyword = pChunk->zKeyword; + *iTextsize = pChunk->iTextsize; + *zText = pChunk->zText; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_TEXT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_zTXt +mng_retcode MNG_DECL mng_getchunk_ztxt (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iKeywordsize, + mng_pchar *zKeyword, + mng_uint8 *iCompression, + mng_uint32 *iTextsize, + mng_pchar *zText) +{ + mng_datap pData; + mng_ztxtp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_ZTXT, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_ztxtp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_zTXt) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + /* fill the fields */ + *iKeywordsize = pChunk->iKeywordsize; + *zKeyword = pChunk->zKeyword; + *iCompression = pChunk->iCompression; + *iTextsize = pChunk->iTextsize; + *zText = pChunk->zText; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_ZTXT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_iTXt +mng_retcode MNG_DECL mng_getchunk_itxt (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iKeywordsize, + mng_pchar *zKeyword, + mng_uint8 *iCompressionflag, + mng_uint8 *iCompressionmethod, + mng_uint32 *iLanguagesize, + mng_pchar *zLanguage, + mng_uint32 *iTranslationsize, + mng_pchar *zTranslation, + mng_uint32 *iTextsize, + mng_pchar *zText) +{ + mng_datap pData; + mng_itxtp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_ITXT, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_itxtp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_iTXt) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + /* fill the fields */ + *iKeywordsize = pChunk->iKeywordsize; + *zKeyword = pChunk->zKeyword; + *iCompressionflag = pChunk->iCompressionflag; + *iCompressionmethod = pChunk->iCompressionmethod; + *iLanguagesize = pChunk->iLanguagesize; + *zLanguage = pChunk->zLanguage; + *iTranslationsize = pChunk->iTranslationsize; + *zTranslation = pChunk->zTranslation; + *iTextsize = pChunk->iTextsize; + *zText = pChunk->zText; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_ITXT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_bKGD +mng_retcode MNG_DECL mng_getchunk_bkgd (mng_handle hHandle, + mng_handle hChunk, + mng_bool *bEmpty, + mng_uint8 *iType, + mng_uint8 *iIndex, + mng_uint16 *iGray, + mng_uint16 *iRed, + mng_uint16 *iGreen, + mng_uint16 *iBlue) +{ + mng_datap pData; + mng_bkgdp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_BKGD, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_bkgdp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_bKGD) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *bEmpty = pChunk->bEmpty; /* fill the fields */ + *iType = pChunk->iType; + *iIndex = pChunk->iIndex; + *iGray = pChunk->iGray; + *iRed = pChunk->iRed; + *iGreen = pChunk->iGreen; + *iBlue = pChunk->iBlue; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_BKGD, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_pHYs +mng_retcode MNG_DECL mng_getchunk_phys (mng_handle hHandle, + mng_handle hChunk, + mng_bool *bEmpty, + mng_uint32 *iSizex, + mng_uint32 *iSizey, + mng_uint8 *iUnit) +{ + mng_datap pData; + mng_physp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PHYS, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_physp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_pHYs) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *bEmpty = pChunk->bEmpty; /* fill the fields */ + *iSizex = pChunk->iSizex; + *iSizey = pChunk->iSizey; + *iUnit = pChunk->iUnit; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PHYS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_sBIT +mng_retcode MNG_DECL mng_getchunk_sbit (mng_handle hHandle, + mng_handle hChunk, + mng_bool *bEmpty, + mng_uint8 *iType, + mng_uint8arr4 *aBits) +{ + mng_datap pData; + mng_sbitp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SBIT, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_sbitp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_sBIT) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *bEmpty = pChunk->bEmpty; + *iType = pChunk->iType; + (*aBits)[0] = pChunk->aBits[0]; + (*aBits)[1] = pChunk->aBits[1]; + (*aBits)[2] = pChunk->aBits[2]; + (*aBits)[3] = pChunk->aBits[3]; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SBIT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_sPLT +mng_retcode MNG_DECL mng_getchunk_splt (mng_handle hHandle, + mng_handle hChunk, + mng_bool *bEmpty, + mng_uint32 *iNamesize, + mng_pchar *zName, + mng_uint8 *iSampledepth, + mng_uint32 *iEntrycount, + mng_ptr *pEntries) +{ + mng_datap pData; + mng_spltp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SPLT, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_spltp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_sPLT) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *bEmpty = pChunk->bEmpty; /* fill the fields */ + *iNamesize = pChunk->iNamesize; + *zName = pChunk->zName; + *iSampledepth = pChunk->iSampledepth; + *iEntrycount = pChunk->iEntrycount; + *pEntries = pChunk->pEntries; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SPLT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_hIST +mng_retcode MNG_DECL mng_getchunk_hist (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iEntrycount, + mng_uint16arr *aEntries) +{ + mng_datap pData; + mng_histp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_HIST, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_histp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_hIST) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iEntrycount = pChunk->iEntrycount; /* fill the fields */ + + MNG_COPY (*aEntries, pChunk->aEntries, sizeof (mng_uint16arr)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_HIST, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_tIME +mng_retcode MNG_DECL mng_getchunk_time (mng_handle hHandle, + mng_handle hChunk, + mng_uint16 *iYear, + mng_uint8 *iMonth, + mng_uint8 *iDay, + mng_uint8 *iHour, + mng_uint8 *iMinute, + mng_uint8 *iSecond) +{ + mng_datap pData; + mng_timep pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_TIME, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_timep)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_tIME) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iYear = pChunk->iYear; /* fill the fields */ + *iMonth = pChunk->iMonth; + *iDay = pChunk->iDay; + *iHour = pChunk->iHour; + *iMinute = pChunk->iMinute; + *iSecond = pChunk->iSecond; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_TIME, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_getchunk_mhdr (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iWidth, + mng_uint32 *iHeight, + mng_uint32 *iTicks, + mng_uint32 *iLayercount, + mng_uint32 *iFramecount, + mng_uint32 *iPlaytime, + mng_uint32 *iSimplicity) +{ + mng_datap pData; + mng_mhdrp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_MHDR, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_mhdrp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iWidth = pChunk->iWidth; /* fill the fields */ + *iHeight = pChunk->iHeight; + *iTicks = pChunk->iTicks; + *iLayercount = pChunk->iLayercount; + *iFramecount = pChunk->iFramecount; + *iPlaytime = pChunk->iPlaytime; + *iSimplicity = pChunk->iSimplicity; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_MHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_LOOP +mng_retcode MNG_DECL mng_getchunk_loop (mng_handle hHandle, + mng_handle hChunk, + mng_uint8 *iLevel, + mng_uint32 *iRepeat, + mng_uint8 *iTermination, + mng_uint32 *iItermin, + mng_uint32 *iItermax, + mng_uint32 *iCount, + mng_uint32p *pSignals) +{ + mng_datap pData; + mng_loopp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_LOOP, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_loopp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_LOOP) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iLevel = pChunk->iLevel; /* fill teh fields */ + *iRepeat = pChunk->iRepeat; + *iTermination = pChunk->iTermination; + *iItermin = pChunk->iItermin; + *iItermax = pChunk->iItermax; + *iCount = pChunk->iCount; + *pSignals = pChunk->pSignals; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_LOOP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_getchunk_endl (mng_handle hHandle, + mng_handle hChunk, + mng_uint8 *iLevel) +{ + mng_datap pData; + mng_endlp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_ENDL, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_endlp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_ENDL) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iLevel = pChunk->iLevel; /* fill the field */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_ENDL, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_DEFI +mng_retcode MNG_DECL mng_getchunk_defi (mng_handle hHandle, + mng_handle hChunk, + mng_uint16 *iObjectid, + mng_uint8 *iDonotshow, + mng_uint8 *iConcrete, + mng_bool *bHasloca, + mng_int32 *iXlocation, + mng_int32 *iYlocation, + mng_bool *bHasclip, + mng_int32 *iLeftcb, + mng_int32 *iRightcb, + mng_int32 *iTopcb, + mng_int32 *iBottomcb) +{ + mng_datap pData; + mng_defip pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_DEFI, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_defip)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_DEFI) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iObjectid = pChunk->iObjectid; /* fill the fields */ + *iDonotshow = pChunk->iDonotshow; + *iConcrete = pChunk->iConcrete; + *bHasloca = pChunk->bHasloca; + *iXlocation = pChunk->iXlocation; + *iYlocation = pChunk->iYlocation; + *bHasclip = pChunk->bHasclip; + *iLeftcb = pChunk->iLeftcb; + *iRightcb = pChunk->iRightcb; + *iTopcb = pChunk->iTopcb; + *iBottomcb = pChunk->iBottomcb; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_DEFI, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_BASI +mng_retcode MNG_DECL mng_getchunk_basi (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iWidth, + mng_uint32 *iHeight, + mng_uint8 *iBitdepth, + mng_uint8 *iColortype, + mng_uint8 *iCompression, + mng_uint8 *iFilter, + mng_uint8 *iInterlace, + mng_uint16 *iRed, + mng_uint16 *iGreen, + mng_uint16 *iBlue, + mng_uint16 *iAlpha, + mng_uint8 *iViewable) +{ + mng_datap pData; + mng_basip pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_BASI, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_basip)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_BASI) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iWidth = pChunk->iWidth; /* fill the fields */ + *iHeight = pChunk->iHeight; + *iBitdepth = pChunk->iBitdepth; + *iColortype = pChunk->iColortype; + *iCompression = pChunk->iCompression; + *iFilter = pChunk->iFilter; + *iInterlace = pChunk->iInterlace; + *iRed = pChunk->iRed; + *iGreen = pChunk->iGreen; + *iBlue = pChunk->iBlue; + *iAlpha = pChunk->iAlpha; + *iViewable = pChunk->iViewable; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_BASI, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_CLON +mng_retcode MNG_DECL mng_getchunk_clon (mng_handle hHandle, + mng_handle hChunk, + mng_uint16 *iSourceid, + mng_uint16 *iCloneid, + mng_uint8 *iClonetype, + mng_uint8 *iDonotshow, + mng_uint8 *iConcrete, + mng_bool *bHasloca, + mng_uint8 *iLocationtype, + mng_int32 *iLocationx, + mng_int32 *iLocationy) +{ + mng_datap pData; + mng_clonp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_CLON, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_clonp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_CLON) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iSourceid = pChunk->iSourceid; /* fill the fields */ + *iCloneid = pChunk->iCloneid; + *iClonetype = pChunk->iClonetype; + *iDonotshow = pChunk->iDonotshow; + *iConcrete = pChunk->iConcrete; + *bHasloca = pChunk->bHasloca; + *iLocationtype = pChunk->iLocationtype; + *iLocationx = pChunk->iLocationx; + *iLocationy = pChunk->iLocationy; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_CLON, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_PAST +mng_retcode MNG_DECL mng_getchunk_past (mng_handle hHandle, + mng_handle hChunk, + mng_uint16 *iDestid, + mng_uint8 *iTargettype, + mng_int32 *iTargetx, + mng_int32 *iTargety, + mng_uint32 *iCount) +{ + mng_datap pData; + mng_pastp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PAST, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_pastp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_PAST) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iDestid = pChunk->iDestid; /* fill the fields */ + *iTargettype = pChunk->iTargettype; + *iTargetx = pChunk->iTargetx; + *iTargety = pChunk->iTargety; + *iCount = pChunk->iCount; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PAST, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_PAST +mng_retcode MNG_DECL mng_getchunk_past_src (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 iEntry, + mng_uint16 *iSourceid, + mng_uint8 *iComposition, + mng_uint8 *iOrientation, + mng_uint8 *iOffsettype, + mng_int32 *iOffsetx, + mng_int32 *iOffsety, + mng_uint8 *iBoundarytype, + mng_int32 *iBoundaryl, + mng_int32 *iBoundaryr, + mng_int32 *iBoundaryt, + mng_int32 *iBoundaryb) +{ + mng_datap pData; + mng_pastp pChunk; + mng_past_sourcep pEntry; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PAST_SRC, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_pastp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_PAST) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + if (iEntry >= pChunk->iCount) /* valid index ? */ + MNG_ERROR (pData, MNG_INVALIDENTRYIX) + /* address the entry */ + pEntry = pChunk->pSources + iEntry; + + *iSourceid = pEntry->iSourceid; /* fill the fields */ + *iComposition = pEntry->iComposition; + *iOrientation = pEntry->iOrientation; + *iOffsettype = pEntry->iOffsettype; + *iOffsetx = pEntry->iOffsetx; + *iOffsety = pEntry->iOffsety; + *iBoundarytype = pEntry->iBoundarytype; + *iBoundaryl = pEntry->iBoundaryl; + *iBoundaryr = pEntry->iBoundaryr; + *iBoundaryt = pEntry->iBoundaryt; + *iBoundaryb = pEntry->iBoundaryb; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PAST_SRC, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_DISC +mng_retcode MNG_DECL mng_getchunk_disc (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iCount, + mng_uint16p *pObjectids) +{ + mng_datap pData; + mng_discp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_DISC, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_discp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_DISC) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iCount = pChunk->iCount; /* fill the fields */ + *pObjectids = pChunk->pObjectids; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_DISC, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_BACK +mng_retcode MNG_DECL mng_getchunk_back (mng_handle hHandle, + mng_handle hChunk, + mng_uint16 *iRed, + mng_uint16 *iGreen, + mng_uint16 *iBlue, + mng_uint8 *iMandatory, + mng_uint16 *iImageid, + mng_uint8 *iTile) +{ + mng_datap pData; + mng_backp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_BACK, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_backp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_BACK) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iRed = pChunk->iRed; /* fill the fields */ + *iGreen = pChunk->iGreen; + *iBlue = pChunk->iBlue; + *iMandatory = pChunk->iMandatory; + *iImageid = pChunk->iImageid; + *iTile = pChunk->iTile; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_BACK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_FRAM +mng_retcode MNG_DECL mng_getchunk_fram (mng_handle hHandle, + mng_handle hChunk, + mng_bool *bEmpty, + mng_uint8 *iMode, + mng_uint32 *iNamesize, + mng_pchar *zName, + mng_uint8 *iChangedelay, + mng_uint8 *iChangetimeout, + mng_uint8 *iChangeclipping, + mng_uint8 *iChangesyncid, + mng_uint32 *iDelay, + mng_uint32 *iTimeout, + mng_uint8 *iBoundarytype, + mng_int32 *iBoundaryl, + mng_int32 *iBoundaryr, + mng_int32 *iBoundaryt, + mng_int32 *iBoundaryb, + mng_uint32 *iCount, + mng_uint32p *pSyncids) +{ + mng_datap pData; + mng_framp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_FRAM, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_framp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_FRAM) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *bEmpty = pChunk->bEmpty; /* fill the fields */ + *iMode = pChunk->iMode; + *iNamesize = pChunk->iNamesize; + *zName = pChunk->zName; + *iChangedelay = pChunk->iChangedelay; + *iChangetimeout = pChunk->iChangetimeout; + *iChangeclipping = pChunk->iChangeclipping; + *iChangesyncid = pChunk->iChangesyncid; + *iDelay = pChunk->iDelay; + *iTimeout = pChunk->iTimeout; + *iBoundarytype = pChunk->iBoundarytype; + *iBoundaryl = pChunk->iBoundaryl; + *iBoundaryr = pChunk->iBoundaryr; + *iBoundaryt = pChunk->iBoundaryt; + *iBoundaryb = pChunk->iBoundaryb; + *iCount = pChunk->iCount; + *pSyncids = pChunk->pSyncids; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_FRAM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_MOVE +mng_retcode MNG_DECL mng_getchunk_move (mng_handle hHandle, + mng_handle hChunk, + mng_uint16 *iFirstid, + mng_uint16 *iLastid, + mng_uint8 *iMovetype, + mng_int32 *iMovex, + mng_int32 *iMovey) +{ + mng_datap pData; + mng_movep pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_MOVE, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_movep)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_MOVE) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iFirstid = pChunk->iFirstid; /* fill the fields */ + *iLastid = pChunk->iLastid; + *iMovetype = pChunk->iMovetype; + *iMovex = pChunk->iMovex; + *iMovey = pChunk->iMovey; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_MOVE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_CLIP +mng_retcode MNG_DECL mng_getchunk_clip (mng_handle hHandle, + mng_handle hChunk, + mng_uint16 *iFirstid, + mng_uint16 *iLastid, + mng_uint8 *iCliptype, + mng_int32 *iClipl, + mng_int32 *iClipr, + mng_int32 *iClipt, + mng_int32 *iClipb) +{ + mng_datap pData; + mng_clipp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_CLIP, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_clipp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_CLIP) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iFirstid = pChunk->iFirstid; /* fill the fields */ + *iLastid = pChunk->iLastid; + *iCliptype = pChunk->iCliptype; + *iClipl = pChunk->iClipl; + *iClipr = pChunk->iClipr; + *iClipt = pChunk->iClipt; + *iClipb = pChunk->iClipb; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_CLIP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SHOW +mng_retcode MNG_DECL mng_getchunk_show (mng_handle hHandle, + mng_handle hChunk, + mng_bool *bEmpty, + mng_uint16 *iFirstid, + mng_uint16 *iLastid, + mng_uint8 *iMode) +{ + mng_datap pData; + mng_showp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SHOW, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_showp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_SHOW) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *bEmpty = pChunk->bEmpty; /* fill the fields */ + *iFirstid = pChunk->iFirstid; + *iLastid = pChunk->iLastid; + *iMode = pChunk->iMode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SHOW, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_TERM +mng_retcode MNG_DECL mng_getchunk_term (mng_handle hHandle, + mng_handle hChunk, + mng_uint8 *iTermaction, + mng_uint8 *iIteraction, + mng_uint32 *iDelay, + mng_uint32 *iItermax) +{ + mng_datap pData; + mng_termp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_TERM, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_termp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_TERM) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iTermaction = pChunk->iTermaction; /* fill the fields */ + *iIteraction = pChunk->iIteraction; + *iDelay = pChunk->iDelay; + *iItermax = pChunk->iItermax; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_TERM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SAVE +mng_retcode MNG_DECL mng_getchunk_save (mng_handle hHandle, + mng_handle hChunk, + mng_bool *bEmpty, + mng_uint8 *iOffsettype, + mng_uint32 *iCount) +{ + mng_datap pData; + mng_savep pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SAVE, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_savep)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_SAVE) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *bEmpty = pChunk->bEmpty; /* fill the fields */ + *iOffsettype = pChunk->iOffsettype; + *iCount = pChunk->iCount; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SAVE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_getchunk_save_entry (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 iEntry, + mng_uint8 *iEntrytype, + mng_uint32arr2 *iOffset, + mng_uint32arr2 *iStarttime, + mng_uint32 *iLayernr, + mng_uint32 *iFramenr, + mng_uint32 *iNamesize, + mng_pchar *zName) +{ + mng_datap pData; + mng_savep pChunk; + mng_save_entryp pEntry; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SAVE_ENTRY, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_savep)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_SAVE) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + if (iEntry >= pChunk->iCount) /* valid index ? */ + MNG_ERROR (pData, MNG_INVALIDENTRYIX) + + pEntry = pChunk->pEntries + iEntry; /* address the entry */ + /* fill the fields */ + *iEntrytype = pEntry->iEntrytype; + (*iOffset)[0] = pEntry->iOffset[0]; + (*iOffset)[1] = pEntry->iOffset[1]; + (*iStarttime)[0] = pEntry->iStarttime[0]; + (*iStarttime)[1] = pEntry->iStarttime[1]; + *iLayernr = pEntry->iLayernr; + *iFramenr = pEntry->iFramenr; + *iNamesize = pEntry->iNamesize; + *zName = pEntry->zName; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SAVE_ENTRY, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SEEK +mng_retcode MNG_DECL mng_getchunk_seek (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iNamesize, + mng_pchar *zName) +{ + mng_datap pData; + mng_seekp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SEEK, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_seekp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_SEEK) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iNamesize = pChunk->iNamesize; /* fill the fields */ + *zName = pChunk->zName; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_SEEK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_eXPI +mng_retcode MNG_DECL mng_getchunk_expi (mng_handle hHandle, + mng_handle hChunk, + mng_uint16 *iSnapshotid, + mng_uint32 *iNamesize, + mng_pchar *zName) +{ + mng_datap pData; + mng_expip pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_EXPI, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_expip)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_eXPI) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iSnapshotid = pChunk->iSnapshotid; /* fill the fields */ + *iNamesize = pChunk->iNamesize; + *zName = pChunk->zName; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_EXPI, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_fPRI +mng_retcode MNG_DECL mng_getchunk_fpri (mng_handle hHandle, + mng_handle hChunk, + mng_uint8 *iDeltatype, + mng_uint8 *iPriority) +{ + mng_datap pData; + mng_fprip pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_FPRI, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_fprip)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_fPRI) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iDeltatype = pChunk->iDeltatype; /* fill the fields */ + *iPriority = pChunk->iPriority; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_FPRI, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_nEED +mng_retcode MNG_DECL mng_getchunk_need (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iKeywordssize, + mng_pchar *zKeywords) +{ + mng_datap pData; + mng_needp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_NEED, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_needp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_nEED) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + /* fill the fields */ + *iKeywordssize = pChunk->iKeywordssize; + *zKeywords = pChunk->zKeywords; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_NEED, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_pHYg +mng_retcode MNG_DECL mng_getchunk_phyg (mng_handle hHandle, + mng_handle hChunk, + mng_bool *bEmpty, + mng_uint32 *iSizex, + mng_uint32 *iSizey, + mng_uint8 *iUnit) +{ + mng_datap pData; + mng_phygp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PHYG, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_phygp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_pHYg) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *bEmpty = pChunk->bEmpty; /* fill the fields */ + *iSizex = pChunk->iSizex; + *iSizey = pChunk->iSizey; + *iUnit = pChunk->iUnit; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PHYG, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG + +mng_retcode MNG_DECL mng_getchunk_jhdr (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iWidth, + mng_uint32 *iHeight, + mng_uint8 *iColortype, + mng_uint8 *iImagesampledepth, + mng_uint8 *iImagecompression, + mng_uint8 *iImageinterlace, + mng_uint8 *iAlphasampledepth, + mng_uint8 *iAlphacompression, + mng_uint8 *iAlphafilter, + mng_uint8 *iAlphainterlace) +{ + mng_datap pData; + mng_jhdrp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_JHDR, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_jhdrp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_JHDR) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iWidth = pChunk->iWidth; /* fill the fields */ + *iHeight = pChunk->iHeight; + *iColortype = pChunk->iColortype; + *iImagesampledepth = pChunk->iImagesampledepth; + *iImagecompression = pChunk->iImagecompression; + *iImageinterlace = pChunk->iImageinterlace; + *iAlphasampledepth = pChunk->iAlphasampledepth; + *iAlphacompression = pChunk->iAlphacompression; + *iAlphafilter = pChunk->iAlphafilter; + *iAlphainterlace = pChunk->iAlphainterlace; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_JHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG + +mng_retcode MNG_DECL mng_getchunk_jdat (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iRawlen, + mng_ptr *pRawdata) +{ + mng_datap pData; + mng_jdatp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_JDAT, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_jdatp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_JDAT) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iRawlen = pChunk->iDatasize; /* fill the fields */ + *pRawdata = pChunk->pData; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_JDAT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG + +mng_retcode MNG_DECL mng_getchunk_jdaa (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iRawlen, + mng_ptr *pRawdata) +{ + mng_datap pData; + mng_jdaap pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_JDAA, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_jdaap)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_JDAA) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iRawlen = pChunk->iDatasize; /* fill the fields */ + *pRawdata = pChunk->pData; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_JDAA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +mng_retcode MNG_DECL mng_getchunk_dhdr (mng_handle hHandle, + mng_handle hChunk, + mng_uint16 *iObjectid, + mng_uint8 *iImagetype, + mng_uint8 *iDeltatype, + mng_uint32 *iBlockwidth, + mng_uint32 *iBlockheight, + mng_uint32 *iBlockx, + mng_uint32 *iBlocky) +{ + mng_datap pData; + mng_dhdrp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_DHDR, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_dhdrp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_DHDR) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iObjectid = pChunk->iObjectid; /* fill the fields */ + *iImagetype = pChunk->iImagetype; + *iDeltatype = pChunk->iDeltatype; + *iBlockwidth = pChunk->iBlockwidth; + *iBlockheight = pChunk->iBlockheight; + *iBlockx = pChunk->iBlockx; + *iBlocky = pChunk->iBlocky; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_DHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +mng_retcode MNG_DECL mng_getchunk_prom (mng_handle hHandle, + mng_handle hChunk, + mng_uint8 *iColortype, + mng_uint8 *iSampledepth, + mng_uint8 *iFilltype) +{ + mng_datap pData; + mng_promp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PROM, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_promp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_PROM) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iColortype = pChunk->iColortype; /* fill the fields */ + *iSampledepth = pChunk->iSampledepth; + *iFilltype = pChunk->iFilltype; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PROM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +mng_retcode MNG_DECL mng_getchunk_pplt (mng_handle hHandle, + mng_handle hChunk, + mng_uint8 *iDeltatype, + mng_uint32 *iCount) +{ + mng_datap pData; + mng_ppltp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PPLT, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_ppltp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_PPLT) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iDeltatype = pChunk->iDeltatype; /* fill the fields */ + *iCount = pChunk->iCount; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PPLT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +mng_retcode MNG_DECL mng_getchunk_pplt_entry (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 iEntry, + mng_uint16 *iRed, + mng_uint16 *iGreen, + mng_uint16 *iBlue, + mng_uint16 *iAlpha, + mng_bool *bUsed) +{ + mng_datap pData; + mng_ppltp pChunk; + mng_pplt_entryp pEntry; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PPLT_ENTRY, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_ppltp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_PPLT) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + if (iEntry >= pChunk->iCount) /* valid index ? */ + MNG_ERROR (pData, MNG_INVALIDENTRYIX) + + pEntry = &pChunk->aEntries[iEntry]; /* address the entry */ + + *iRed = pEntry->iRed; /* fill the fields */ + *iGreen = pEntry->iGreen; + *iBlue = pEntry->iBlue; + *iAlpha = pEntry->iAlpha; + *bUsed = pEntry->bUsed; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_PPLT_ENTRY, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +mng_retcode MNG_DECL mng_getchunk_drop (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iCount, + mng_chunkidp *pChunknames) +{ + mng_datap pData; + mng_dropp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_DROP, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_dropp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_DROP) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iCount = pChunk->iCount; /* fill the fields */ + *pChunknames = pChunk->pChunknames; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_DROP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_DBYK +mng_retcode MNG_DECL mng_getchunk_dbyk (mng_handle hHandle, + mng_handle hChunk, + mng_chunkid *iChunkname, + mng_uint8 *iPolarity, + mng_uint32 *iKeywordssize, + mng_pchar *zKeywords) +{ + mng_datap pData; + mng_dbykp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_DBYK, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_dbykp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_DBYK) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iChunkname = pChunk->iChunkname; /* fill the fields */ + *iPolarity = pChunk->iPolarity; + *iKeywordssize = pChunk->iKeywordssize; + *zKeywords = pChunk->zKeywords; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_DBYK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_ORDR +mng_retcode MNG_DECL mng_getchunk_ordr (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iCount) +{ + mng_datap pData; + mng_ordrp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_ORDR, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_ordrp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_ORDR) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iCount = pChunk->iCount; /* fill the field */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_ORDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_ORDR +mng_retcode MNG_DECL mng_getchunk_ordr_entry (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 iEntry, + mng_chunkid *iChunkname, + mng_uint8 *iOrdertype) +{ + mng_datap pData; + mng_ordrp pChunk; + mng_ordr_entryp pEntry; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_ORDR_ENTRY, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_ordrp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_ORDR) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + if (iEntry >= pChunk->iCount) /* valid index ? */ + MNG_ERROR (pData, MNG_INVALIDENTRYIX) + + pEntry = pChunk->pEntries + iEntry; /* address the proper entry */ + + *iChunkname = pEntry->iChunkname; /* fill the fields */ + *iOrdertype = pEntry->iOrdertype; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_ORDR_ENTRY, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_MAGN +mng_retcode MNG_DECL mng_getchunk_magn (mng_handle hHandle, + mng_handle hChunk, + mng_uint16 *iFirstid, + mng_uint16 *iLastid, + mng_uint16 *iMethodX, + mng_uint16 *iMX, + mng_uint16 *iMY, + mng_uint16 *iML, + mng_uint16 *iMR, + mng_uint16 *iMT, + mng_uint16 *iMB, + mng_uint16 *iMethodY) +{ + mng_datap pData; + mng_magnp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_MAGN, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_magnp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_MAGN) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iFirstid = pChunk->iFirstid; /* fill the fields */ + *iLastid = pChunk->iLastid; + *iMethodX = (mng_uint16)pChunk->iMethodX; + *iMX = pChunk->iMX; + *iMY = pChunk->iMY; + *iML = pChunk->iML; + *iMR = pChunk->iMR; + *iMT = pChunk->iMT; + *iMB = pChunk->iMB; + *iMethodY = (mng_uint16)pChunk->iMethodY; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_MAGN, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_MPNG_PROPOSAL +MNG_EXT mng_retcode MNG_DECL mng_getchunk_mpng (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iFramewidth, + mng_uint32 *iFrameheight, + mng_uint16 *iNumplays, + mng_uint16 *iTickspersec, + mng_uint8 *iCompressionmethod, + mng_uint32 *iCount) +{ + mng_datap pData; + mng_mpngp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_MPNG, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_mpngp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_mpNG) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + /* fill the fields */ + *iFramewidth = pChunk->iFramewidth; + *iFrameheight = pChunk->iFrameheight; + *iNumplays = pChunk->iNumplays; + *iTickspersec = pChunk->iTickspersec; + *iCompressionmethod = pChunk->iCompressionmethod; + *iCount = pChunk->iFramessize / sizeof (mng_mpng_frame); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_MPNG, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_MPNG_PROPOSAL +MNG_EXT mng_retcode MNG_DECL mng_getchunk_mpng_frame (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 iEntry, + mng_uint32 *iX, + mng_uint32 *iY, + mng_uint32 *iWidth, + mng_uint32 *iHeight, + mng_int32 *iXoffset, + mng_int32 *iYoffset, + mng_uint16 *iTicks) +{ + mng_datap pData; + mng_mpngp pChunk; + mng_mpng_framep pFrame; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_MPNG_FRAME, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_mpngp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_mpNG) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + /* valid index ? */ + if (iEntry >= (pChunk->iFramessize / sizeof (mng_mpng_frame))) + MNG_ERROR (pData, MNG_INVALIDENTRYIX) + + pFrame = pChunk->pFrames + iEntry; /* address the entry */ + /* fill the fields */ + *iX = pFrame->iX; + *iY = pFrame->iY; + *iWidth = pFrame->iWidth; + *iHeight = pFrame->iHeight; + *iXoffset = pFrame->iXoffset; + *iYoffset = pFrame->iYoffset; + *iTicks = pFrame->iTicks; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_MPNG_FRAME, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_evNT +mng_retcode MNG_DECL mng_getchunk_evnt (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 *iCount) +{ + mng_datap pData; + mng_evntp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_EVNT, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_evntp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_evNT) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + *iCount = pChunk->iCount; /* fill the fields */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_EVNT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_getchunk_evnt_entry (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 iEntry, + mng_uint8 *iEventtype, + mng_uint8 *iMasktype, + mng_int32 *iLeft, + mng_int32 *iRight, + mng_int32 *iTop, + mng_int32 *iBottom, + mng_uint16 *iObjectid, + mng_uint8 *iIndex, + mng_uint32 *iSegmentnamesize, + mng_pchar *zSegmentname) +{ + mng_datap pData; + mng_evntp pChunk; + mng_evnt_entryp pEntry; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_EVNT_ENTRY, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_evntp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.iChunkname != MNG_UINT_evNT) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + + if (iEntry >= pChunk->iCount) /* valid index ? */ + MNG_ERROR (pData, MNG_INVALIDENTRYIX) + + pEntry = pChunk->pEntries + iEntry; /* address the entry */ + /* fill the fields */ + *iEventtype = pEntry->iEventtype; + *iMasktype = pEntry->iMasktype; + *iLeft = pEntry->iLeft; + *iRight = pEntry->iRight; + *iTop = pEntry->iTop; + *iBottom = pEntry->iBottom; + *iObjectid = pEntry->iObjectid; + *iIndex = pEntry->iIndex; + *iSegmentnamesize = pEntry->iSegmentnamesize; + *zSegmentname = pEntry->zSegmentname; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_EVNT_ENTRY, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_getchunk_unknown (mng_handle hHandle, + mng_handle hChunk, + mng_chunkid *iChunkname, + mng_uint32 *iRawlen, + mng_ptr *pRawdata) +{ + mng_datap pData; + mng_unknown_chunkp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_UNKNOWN, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + pChunk = (mng_unknown_chunkp)hChunk; /* address the chunk */ + + if (pChunk->sHeader.fCleanup != mng_free_unknown) + MNG_ERROR (pData, MNG_WRONGCHUNK) /* ouch */ + /* fill the fields */ + *iChunkname = pChunk->sHeader.iChunkname; + *iRawlen = pChunk->iDatasize; + *pRawdata = pChunk->pData; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETCHUNK_UNKNOWN, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_WRITE_PROCS + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_TERM +MNG_LOCAL mng_bool check_term (mng_datap pData, + mng_chunkid iChunkname) +{ + mng_chunk_headerp pChunk = (mng_chunk_headerp)pData->pLastchunk; + + if (!pChunk) /* nothing added yet ? */ + return MNG_TRUE; + /* last added chunk is TERM ? */ + if (pChunk->iChunkname != MNG_UINT_TERM) + return MNG_TRUE; + /* previous to last is MHDR ? */ + if ((pChunk->pPrev) && (((mng_chunk_headerp)pChunk->pPrev)->iChunkname == MNG_UINT_MHDR)) + return MNG_TRUE; + + if (iChunkname == MNG_UINT_SEEK) /* new chunk to be added is SEEK ? */ + return MNG_TRUE; + + return MNG_FALSE; +} +#endif + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_putchunk_ihdr (mng_handle hHandle, + mng_uint32 iWidth, + mng_uint32 iHeight, + mng_uint8 iBitdepth, + mng_uint8 iColortype, + mng_uint8 iCompression, + mng_uint8 iFilter, + mng_uint8 iInterlace) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_IHDR, mng_init_general, mng_free_general, mng_read_ihdr, mng_write_ihdr, mng_assign_general, 0, 0, sizeof(mng_ihdr)}; +#else + {MNG_UINT_IHDR, mng_init_ihdr, mng_free_ihdr, mng_read_ihdr, mng_write_ihdr, mng_assign_ihdr, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_IHDR, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_IHDR)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_ihdr (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_IHDR, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + ((mng_ihdrp)pChunk)->iWidth = iWidth; + ((mng_ihdrp)pChunk)->iHeight = iHeight; + ((mng_ihdrp)pChunk)->iBitdepth = iBitdepth; + ((mng_ihdrp)pChunk)->iColortype = iColortype; + ((mng_ihdrp)pChunk)->iCompression = iCompression; + ((mng_ihdrp)pChunk)->iFilter = iFilter; + ((mng_ihdrp)pChunk)->iInterlace = iInterlace; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_IHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_putchunk_plte (mng_handle hHandle, + mng_uint32 iCount, + mng_palette8 aPalette) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_PLTE, mng_init_general, mng_free_general, mng_read_plte, mng_write_plte, mng_assign_general, 0, 0, sizeof(mng_plte)}; +#else + {MNG_UINT_PLTE, mng_init_plte, mng_free_plte, mng_read_plte, mng_write_plte, mng_assign_plte, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PLTE, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a header first! */ + if (pData->iFirstchunkadded == 0) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_PLTE)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_plte (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_PLTE, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_pltep)pChunk)->iEntrycount = iCount; + ((mng_pltep)pChunk)->bEmpty = (mng_bool)(iCount == 0); + + MNG_COPY (((mng_pltep)pChunk)->aEntries, aPalette, sizeof (mng_palette8)); + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PLTE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_putchunk_idat (mng_handle hHandle, + mng_uint32 iRawlen, + mng_ptr pRawdata) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_IDAT, mng_init_general, mng_free_idat, mng_read_idat, mng_write_idat, mng_assign_idat, 0, 0, sizeof(mng_idat)}; +#else + {MNG_UINT_IDAT, mng_init_idat, mng_free_idat, mng_read_idat, mng_write_idat, mng_assign_idat, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_IDAT, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a header first! */ + if (pData->iFirstchunkadded == 0) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_IDAT)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_idat (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_IDAT, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_idatp)pChunk)->bEmpty = (mng_bool)(iRawlen == 0); + ((mng_idatp)pChunk)->iDatasize = iRawlen; + + if (iRawlen) + { + MNG_ALLOC (pData, ((mng_idatp)pChunk)->pData, iRawlen); + MNG_COPY (((mng_idatp)pChunk)->pData, pRawdata, iRawlen); + } + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_IDAT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_putchunk_iend (mng_handle hHandle) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_IEND, mng_init_general, mng_free_general, mng_read_iend, mng_write_iend, mng_assign_general, 0, 0, sizeof(mng_iend)}; +#else + {MNG_UINT_IEND, mng_init_iend, mng_free_iend, mng_read_iend, mng_write_iend, mng_assign_iend, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_IEND, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a header first! */ + if (pData->iFirstchunkadded == 0) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_IEND)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_iend (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_IEND, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_INCLUDE_JNG + if ((pData->iFirstchunkadded == MNG_UINT_IHDR) || + (pData->iFirstchunkadded == MNG_UINT_JHDR) ) +#else + if (pData->iFirstchunkadded == MNG_UINT_IHDR) +#endif + pData->bCreating = MNG_FALSE; /* should be last chunk !!! */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_IEND, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_putchunk_trns (mng_handle hHandle, + mng_bool bEmpty, + mng_bool bGlobal, + mng_uint8 iType, + mng_uint32 iCount, + mng_uint8arr aAlphas, + mng_uint16 iGray, + mng_uint16 iRed, + mng_uint16 iGreen, + mng_uint16 iBlue, + mng_uint32 iRawlen, + mng_uint8arr aRawdata) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_tRNS, mng_init_general, mng_free_general, mng_read_trns, mng_write_trns, mng_assign_general, 0, 0, sizeof(mng_trns)}; +#else + {MNG_UINT_tRNS, mng_init_trns, mng_free_trns, mng_read_trns, mng_write_trns, mng_assign_trns, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_TRNS, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a header first! */ + if (pData->iFirstchunkadded == 0) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_tRNS)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_trns (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_tRNS, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_trnsp)pChunk)->bEmpty = bEmpty; + ((mng_trnsp)pChunk)->bGlobal = bGlobal; + ((mng_trnsp)pChunk)->iType = iType; + ((mng_trnsp)pChunk)->iCount = iCount; + ((mng_trnsp)pChunk)->iGray = iGray; + ((mng_trnsp)pChunk)->iRed = iRed; + ((mng_trnsp)pChunk)->iGreen = iGreen; + ((mng_trnsp)pChunk)->iBlue = iBlue; + ((mng_trnsp)pChunk)->iRawlen = iRawlen; + + MNG_COPY (((mng_trnsp)pChunk)->aEntries, aAlphas, sizeof (mng_uint8arr)); + MNG_COPY (((mng_trnsp)pChunk)->aRawdata, aRawdata, sizeof (mng_uint8arr)); + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_TRNS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_gAMA +mng_retcode MNG_DECL mng_putchunk_gama (mng_handle hHandle, + mng_bool bEmpty, + mng_uint32 iGamma) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_gAMA, mng_init_general, mng_free_general, mng_read_gama, mng_write_gama, mng_assign_general, 0, 0, sizeof(mng_gama)}; +#else + {MNG_UINT_gAMA, mng_init_gama, mng_free_gama, mng_read_gama, mng_write_gama, mng_assign_gama, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_GAMA, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a header first! */ + if (pData->iFirstchunkadded == 0) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_gAMA)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_gama (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_gAMA, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_gamap)pChunk)->bEmpty = bEmpty; + ((mng_gamap)pChunk)->iGamma = iGamma; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_GAMA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_cHRM +mng_retcode MNG_DECL mng_putchunk_chrm (mng_handle hHandle, + mng_bool bEmpty, + mng_uint32 iWhitepointx, + mng_uint32 iWhitepointy, + mng_uint32 iRedx, + mng_uint32 iRedy, + mng_uint32 iGreenx, + mng_uint32 iGreeny, + mng_uint32 iBluex, + mng_uint32 iBluey) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_cHRM, mng_init_general, mng_free_general, mng_read_chrm, mng_write_chrm, mng_assign_general, 0, 0, sizeof(mng_chrm)}; +#else + {MNG_UINT_cHRM, mng_init_chrm, mng_free_chrm, mng_read_chrm, mng_write_chrm, mng_assign_chrm, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_CHRM, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a header first! */ + if (pData->iFirstchunkadded == 0) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_cHRM)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_chrm (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_cHRM, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_chrmp)pChunk)->bEmpty = bEmpty; + ((mng_chrmp)pChunk)->iWhitepointx = iWhitepointx; + ((mng_chrmp)pChunk)->iWhitepointy = iWhitepointy; + ((mng_chrmp)pChunk)->iRedx = iRedx; + ((mng_chrmp)pChunk)->iRedy = iRedy; + ((mng_chrmp)pChunk)->iGreenx = iGreenx; + ((mng_chrmp)pChunk)->iGreeny = iGreeny; + ((mng_chrmp)pChunk)->iBluex = iBluex; + ((mng_chrmp)pChunk)->iBluey = iBluey; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_CHRM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_sRGB +mng_retcode MNG_DECL mng_putchunk_srgb (mng_handle hHandle, + mng_bool bEmpty, + mng_uint8 iRenderingintent) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_sRGB, mng_init_general, mng_free_general, mng_read_srgb, mng_write_srgb, mng_assign_general, 0, 0, sizeof(mng_srgb)}; +#else + {MNG_UINT_sRGB, mng_init_srgb, mng_free_srgb, mng_read_srgb, mng_write_srgb, mng_assign_srgb, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SRGB, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a header first! */ + if (pData->iFirstchunkadded == 0) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_sRGB)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_srgb (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_sRGB, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_srgbp)pChunk)->bEmpty = bEmpty; + ((mng_srgbp)pChunk)->iRenderingintent = iRenderingintent; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SRGB, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_iCCP +mng_retcode MNG_DECL mng_putchunk_iccp (mng_handle hHandle, + mng_bool bEmpty, + mng_uint32 iNamesize, + mng_pchar zName, + mng_uint8 iCompression, + mng_uint32 iProfilesize, + mng_ptr pProfile) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_iCCP, mng_init_general, mng_free_iccp, mng_read_iccp, mng_write_iccp, mng_assign_iccp, 0, 0, sizeof(mng_iccp)}; +#else + {MNG_UINT_iCCP, mng_init_iccp, mng_free_iccp, mng_read_iccp, mng_write_iccp, mng_assign_iccp, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_ICCP, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a header first! */ + if (pData->iFirstchunkadded == 0) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_iCCP)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_iccp (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_iCCP, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_iccpp)pChunk)->bEmpty = bEmpty; + ((mng_iccpp)pChunk)->iNamesize = iNamesize; + ((mng_iccpp)pChunk)->iCompression = iCompression; + ((mng_iccpp)pChunk)->iProfilesize = iProfilesize; + + if (iNamesize) + { + MNG_ALLOC (pData, ((mng_iccpp)pChunk)->zName, iNamesize + 1); + MNG_COPY (((mng_iccpp)pChunk)->zName, zName, iNamesize); + } + + if (iProfilesize) + { + MNG_ALLOC (pData, ((mng_iccpp)pChunk)->pProfile, iProfilesize); + MNG_COPY (((mng_iccpp)pChunk)->pProfile, pProfile, iProfilesize); + } + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_ICCP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_tEXt +mng_retcode MNG_DECL mng_putchunk_text (mng_handle hHandle, + mng_uint32 iKeywordsize, + mng_pchar zKeyword, + mng_uint32 iTextsize, + mng_pchar zText) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_tEXt, mng_init_general, mng_free_text, mng_read_text, mng_write_text, mng_assign_text, 0, 0, sizeof(mng_text)}; +#else + {MNG_UINT_tEXt, mng_init_text, mng_free_text, mng_read_text, mng_write_text, mng_assign_text, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_TEXT, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a header first! */ + if (pData->iFirstchunkadded == 0) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_tEXt)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_text (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_tEXt, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_textp)pChunk)->iKeywordsize = iKeywordsize; + ((mng_textp)pChunk)->iTextsize = iTextsize; + + if (iKeywordsize) + { + MNG_ALLOC (pData, ((mng_textp)pChunk)->zKeyword, iKeywordsize + 1); + MNG_COPY (((mng_textp)pChunk)->zKeyword, zKeyword, iKeywordsize); + } + + if (iTextsize) + { + MNG_ALLOC (pData, ((mng_textp)pChunk)->zText, iTextsize + 1); + MNG_COPY (((mng_textp)pChunk)->zText, zText, iTextsize); + } + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_TEXT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_zTXt +mng_retcode MNG_DECL mng_putchunk_ztxt (mng_handle hHandle, + mng_uint32 iKeywordsize, + mng_pchar zKeyword, + mng_uint8 iCompression, + mng_uint32 iTextsize, + mng_pchar zText) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_zTXt, mng_init_general, mng_free_ztxt, mng_read_ztxt, mng_write_ztxt, mng_assign_ztxt, 0, 0, sizeof(mng_ztxt)}; +#else + {MNG_UINT_zTXt, mng_init_ztxt, mng_free_ztxt, mng_read_ztxt, mng_write_ztxt, mng_assign_ztxt, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_ZTXT, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a header first! */ + if (pData->iFirstchunkadded == 0) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_zTXt)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_ztxt (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_zTXt, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_ztxtp)pChunk)->iKeywordsize = iKeywordsize; + ((mng_ztxtp)pChunk)->iCompression = iCompression; + ((mng_ztxtp)pChunk)->iTextsize = iTextsize; + + if (iKeywordsize) + { + MNG_ALLOC (pData, ((mng_ztxtp)pChunk)->zKeyword, iKeywordsize + 1); + MNG_COPY (((mng_ztxtp)pChunk)->zKeyword, zKeyword, iKeywordsize); + } + + if (iTextsize) + { + MNG_ALLOC (pData, ((mng_ztxtp)pChunk)->zText, iTextsize + 1); + MNG_COPY (((mng_ztxtp)pChunk)->zText, zText, iTextsize); + } + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_ZTXT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_iTXt +mng_retcode MNG_DECL mng_putchunk_itxt (mng_handle hHandle, + mng_uint32 iKeywordsize, + mng_pchar zKeyword, + mng_uint8 iCompressionflag, + mng_uint8 iCompressionmethod, + mng_uint32 iLanguagesize, + mng_pchar zLanguage, + mng_uint32 iTranslationsize, + mng_pchar zTranslation, + mng_uint32 iTextsize, + mng_pchar zText) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_iTXt, mng_init_general, mng_free_itxt, mng_read_itxt, mng_write_itxt, mng_assign_itxt, 0, 0, sizeof(mng_itxt)}; +#else + {MNG_UINT_iTXt, mng_init_itxt, mng_free_itxt, mng_read_itxt, mng_write_itxt, mng_assign_itxt, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_ITXT, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a header first! */ + if (pData->iFirstchunkadded == 0) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_iTXt)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_itxt (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_iTXt, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_itxtp)pChunk)->iKeywordsize = iKeywordsize; + ((mng_itxtp)pChunk)->iCompressionflag = iCompressionflag; + ((mng_itxtp)pChunk)->iCompressionmethod = iCompressionmethod; + ((mng_itxtp)pChunk)->iLanguagesize = iLanguagesize; + ((mng_itxtp)pChunk)->iTranslationsize = iTranslationsize; + ((mng_itxtp)pChunk)->iTextsize = iTextsize; + + if (iKeywordsize) + { + MNG_ALLOC (pData, ((mng_itxtp)pChunk)->zKeyword, iKeywordsize + 1); + MNG_COPY (((mng_itxtp)pChunk)->zKeyword, zKeyword, iKeywordsize); + } + + if (iLanguagesize) + { + MNG_ALLOC (pData, ((mng_itxtp)pChunk)->zLanguage, iLanguagesize + 1); + MNG_COPY (((mng_itxtp)pChunk)->zLanguage, zLanguage, iLanguagesize); + } + + if (iTranslationsize) + { + MNG_ALLOC (pData, ((mng_itxtp)pChunk)->zTranslation, iTranslationsize + 1); + MNG_COPY (((mng_itxtp)pChunk)->zTranslation, zTranslation, iTranslationsize); + } + + if (iTextsize) + { + MNG_ALLOC (pData, ((mng_itxtp)pChunk)->zText, iTextsize + 1); + MNG_COPY (((mng_itxtp)pChunk)->zText, zText, iTextsize); + } + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_ITXT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_bKGD +mng_retcode MNG_DECL mng_putchunk_bkgd (mng_handle hHandle, + mng_bool bEmpty, + mng_uint8 iType, + mng_uint8 iIndex, + mng_uint16 iGray, + mng_uint16 iRed, + mng_uint16 iGreen, + mng_uint16 iBlue) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_bKGD, mng_init_general, mng_free_general, mng_read_bkgd, mng_write_bkgd, mng_assign_general, 0, 0, sizeof(mng_bkgd)}; +#else + {MNG_UINT_bKGD, mng_init_bkgd, mng_free_bkgd, mng_read_bkgd, mng_write_bkgd, mng_assign_bkgd, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_BKGD, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a header first! */ + if (pData->iFirstchunkadded == 0) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_bKGD)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_bkgd (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_bKGD, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_bkgdp)pChunk)->bEmpty = bEmpty; + ((mng_bkgdp)pChunk)->iType = iType; + ((mng_bkgdp)pChunk)->iIndex = iIndex; + ((mng_bkgdp)pChunk)->iGray = iGray; + ((mng_bkgdp)pChunk)->iRed = iRed; + ((mng_bkgdp)pChunk)->iGreen = iGreen; + ((mng_bkgdp)pChunk)->iBlue = iBlue; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_BKGD, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_pHYs +mng_retcode MNG_DECL mng_putchunk_phys (mng_handle hHandle, + mng_bool bEmpty, + mng_uint32 iSizex, + mng_uint32 iSizey, + mng_uint8 iUnit) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_pHYs, mng_init_general, mng_free_general, mng_read_phys, mng_write_phys, mng_assign_general, 0, 0, sizeof(mng_phys)}; +#else + {MNG_UINT_pHYs, mng_init_phys, mng_free_phys, mng_read_phys, mng_write_phys, mng_assign_phys, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PHYS, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a header first! */ + if (pData->iFirstchunkadded == 0) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_pHYs)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_phys (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_pHYs, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_physp)pChunk)->bEmpty = bEmpty; + ((mng_physp)pChunk)->iSizex = iSizex; + ((mng_physp)pChunk)->iSizey = iSizey; + ((mng_physp)pChunk)->iUnit = iUnit; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PHYS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_sBIT +mng_retcode MNG_DECL mng_putchunk_sbit (mng_handle hHandle, + mng_bool bEmpty, + mng_uint8 iType, + mng_uint8arr4 aBits) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_sBIT, mng_init_general, mng_free_general, mng_read_sbit, mng_write_sbit, mng_assign_general, 0, 0, sizeof(mng_sbit)}; +#else + {MNG_UINT_sBIT, mng_init_sbit, mng_free_sbit, mng_read_sbit, mng_write_sbit, mng_assign_sbit, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SBIT, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a header first! */ + if (pData->iFirstchunkadded == 0) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_sBIT)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_sbit (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_sBIT, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_sbitp)pChunk)->bEmpty = bEmpty; + ((mng_sbitp)pChunk)->iType = iType; + ((mng_sbitp)pChunk)->aBits[0] = aBits[0]; + ((mng_sbitp)pChunk)->aBits[1] = aBits[1]; + ((mng_sbitp)pChunk)->aBits[2] = aBits[2]; + ((mng_sbitp)pChunk)->aBits[3] = aBits[3]; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SBIT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_sPLT +mng_retcode MNG_DECL mng_putchunk_splt (mng_handle hHandle, + mng_bool bEmpty, + mng_uint32 iNamesize, + mng_pchar zName, + mng_uint8 iSampledepth, + mng_uint32 iEntrycount, + mng_ptr pEntries) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_sPLT, mng_init_general, mng_free_splt, mng_read_splt, mng_write_splt, mng_assign_splt, 0, 0, sizeof(mng_splt)}; +#else + {MNG_UINT_sPLT, mng_init_splt, mng_free_splt, mng_read_splt, mng_write_splt, mng_assign_splt, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SPLT, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a header first! */ + if (pData->iFirstchunkadded == 0) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_sPLT)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_splt (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_sPLT, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_spltp)pChunk)->bEmpty = bEmpty; + ((mng_spltp)pChunk)->iNamesize = iNamesize; + ((mng_spltp)pChunk)->iSampledepth = iSampledepth; + ((mng_spltp)pChunk)->iEntrycount = iEntrycount; + + if (iNamesize) + { + MNG_ALLOC (pData, ((mng_spltp)pChunk)->zName, iNamesize + 1); + MNG_COPY (((mng_spltp)pChunk)->zName, zName, iNamesize); + } + + if (iEntrycount) + { + mng_uint32 iSize = iEntrycount * ((iSampledepth >> 1) + 2); + + MNG_ALLOC (pData, ((mng_spltp)pChunk)->pEntries, iSize); + MNG_COPY (((mng_spltp)pChunk)->pEntries, pEntries, iSize); + } + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SPLT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_hIST +mng_retcode MNG_DECL mng_putchunk_hist (mng_handle hHandle, + mng_uint32 iEntrycount, + mng_uint16arr aEntries) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_hIST, mng_init_general, mng_free_general, mng_read_hist, mng_write_hist, mng_assign_general, 0, 0, sizeof(mng_hist)}; +#else + {MNG_UINT_hIST, mng_init_hist, mng_free_hist, mng_read_hist, mng_write_hist, mng_assign_hist, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_HIST, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a header first! */ + if (pData->iFirstchunkadded == 0) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_hIST)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_hist (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_hIST, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_histp)pChunk)->iEntrycount = iEntrycount; + + MNG_COPY (((mng_histp)pChunk)->aEntries, aEntries, sizeof (mng_uint16arr)); + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_HIST, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_tIME +mng_retcode MNG_DECL mng_putchunk_time (mng_handle hHandle, + mng_uint16 iYear, + mng_uint8 iMonth, + mng_uint8 iDay, + mng_uint8 iHour, + mng_uint8 iMinute, + mng_uint8 iSecond) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_tIME, mng_init_general, mng_free_general, mng_read_time, mng_write_time, mng_assign_general, 0, 0, sizeof(mng_time)}; +#else + {MNG_UINT_tIME, mng_init_time, mng_free_time, mng_read_time, mng_write_time, mng_assign_time, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_TIME, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_tIME)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* must have had a header first! */ + if (pData->iFirstchunkadded == 0) + MNG_ERROR (pData, MNG_NOHEADER) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_time (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_tIME, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_timep)pChunk)->iYear = iYear; + ((mng_timep)pChunk)->iMonth = iMonth; + ((mng_timep)pChunk)->iDay = iDay; + ((mng_timep)pChunk)->iHour = iHour; + ((mng_timep)pChunk)->iMinute = iMinute; + ((mng_timep)pChunk)->iSecond = iSecond; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_TIME, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_putchunk_mhdr (mng_handle hHandle, + mng_uint32 iWidth, + mng_uint32 iHeight, + mng_uint32 iTicks, + mng_uint32 iLayercount, + mng_uint32 iFramecount, + mng_uint32 iPlaytime, + mng_uint32 iSimplicity) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_MHDR, mng_init_general, mng_free_general, mng_read_mhdr, mng_write_mhdr, mng_assign_general, 0, 0, sizeof(mng_mhdr)}; +#else + {MNG_UINT_MHDR, mng_init_mhdr, mng_free_mhdr, mng_read_mhdr, mng_write_mhdr, mng_assign_mhdr, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_MHDR, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must be very first! */ + if (pData->iFirstchunkadded != 0) + MNG_ERROR (pData, MNG_SEQUENCEERROR) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_MHDR)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_mhdr (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_MHDR, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_mhdrp)pChunk)->iWidth = iWidth; + ((mng_mhdrp)pChunk)->iHeight = iHeight; + ((mng_mhdrp)pChunk)->iTicks = iTicks; + ((mng_mhdrp)pChunk)->iLayercount = iLayercount; + ((mng_mhdrp)pChunk)->iFramecount = iFramecount; + ((mng_mhdrp)pChunk)->iPlaytime = iPlaytime; + ((mng_mhdrp)pChunk)->iSimplicity = iSimplicity; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_MHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_putchunk_mend (mng_handle hHandle) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_MEND, mng_init_general, mng_free_general, mng_read_mend, mng_write_mend, mng_assign_general, 0, 0, sizeof(mng_mend)}; +#else + {MNG_UINT_MEND, mng_init_mend, mng_free_mend, mng_read_mend, mng_write_mend, mng_assign_mend, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_MEND, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a header first! */ + if (pData->iFirstchunkadded == 0) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_MEND)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_mend (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_MEND, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + + pData->bCreating = MNG_FALSE; /* should be last chunk !!! */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_MEND, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_LOOP +mng_retcode MNG_DECL mng_putchunk_loop (mng_handle hHandle, + mng_uint8 iLevel, + mng_uint32 iRepeat, + mng_uint8 iTermination, + mng_uint32 iItermin, + mng_uint32 iItermax, + mng_uint32 iCount, + mng_uint32p pSignals) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_LOOP, mng_init_general, mng_free_loop, mng_read_loop, mng_write_loop, mng_assign_loop, 0, 0, sizeof(mng_loop)}; +#else + {MNG_UINT_LOOP, mng_init_loop, mng_free_loop, mng_read_loop, mng_write_loop, mng_assign_loop, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_LOOP, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_LOOP)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_loop (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_LOOP, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_loopp)pChunk)->iLevel = iLevel; + ((mng_loopp)pChunk)->iRepeat = iRepeat; + ((mng_loopp)pChunk)->iTermination = iTermination; + ((mng_loopp)pChunk)->iItermin = iItermin; + ((mng_loopp)pChunk)->iItermax = iItermax; + ((mng_loopp)pChunk)->iCount = iCount; + ((mng_loopp)pChunk)->pSignals = pSignals; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_LOOP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_putchunk_endl (mng_handle hHandle, + mng_uint8 iLevel) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_ENDL, mng_init_general, mng_free_general, mng_read_endl, mng_write_endl, mng_assign_general, 0, 0, sizeof(mng_endl)}; +#else + {MNG_UINT_ENDL, mng_init_endl, mng_free_endl, mng_read_endl, mng_write_endl, mng_assign_endl, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_ENDL, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_ENDL)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_endl (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_ENDL, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_endlp)pChunk)->iLevel = iLevel; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_ENDL, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_DEFI +mng_retcode MNG_DECL mng_putchunk_defi (mng_handle hHandle, + mng_uint16 iObjectid, + mng_uint8 iDonotshow, + mng_uint8 iConcrete, + mng_bool bHasloca, + mng_int32 iXlocation, + mng_int32 iYlocation, + mng_bool bHasclip, + mng_int32 iLeftcb, + mng_int32 iRightcb, + mng_int32 iTopcb, + mng_int32 iBottomcb) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_DEFI, mng_init_general, mng_free_general, mng_read_defi, mng_write_defi, mng_assign_general, 0, 0, sizeof(mng_defi)}; +#else + {MNG_UINT_DEFI, mng_init_defi, mng_free_defi, mng_read_defi, mng_write_defi, mng_assign_defi, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_DEFI, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_DEFI)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_defi (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_DEFI, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_defip)pChunk)->iObjectid = iObjectid; + ((mng_defip)pChunk)->iDonotshow = iDonotshow; + ((mng_defip)pChunk)->iConcrete = iConcrete; + ((mng_defip)pChunk)->bHasloca = bHasloca; + ((mng_defip)pChunk)->iXlocation = iXlocation; + ((mng_defip)pChunk)->iYlocation = iYlocation; + ((mng_defip)pChunk)->bHasclip = bHasclip; + ((mng_defip)pChunk)->iLeftcb = iLeftcb; + ((mng_defip)pChunk)->iRightcb = iRightcb; + ((mng_defip)pChunk)->iTopcb = iTopcb; + ((mng_defip)pChunk)->iBottomcb = iBottomcb; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_DEFI, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_BASI +mng_retcode MNG_DECL mng_putchunk_basi (mng_handle hHandle, + mng_uint32 iWidth, + mng_uint32 iHeight, + mng_uint8 iBitdepth, + mng_uint8 iColortype, + mng_uint8 iCompression, + mng_uint8 iFilter, + mng_uint8 iInterlace, + mng_uint16 iRed, + mng_uint16 iGreen, + mng_uint16 iBlue, + mng_uint16 iAlpha, + mng_uint8 iViewable) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_BASI, mng_init_general, mng_free_general, mng_read_basi, mng_write_basi, mng_assign_general, 0, 0, sizeof(mng_basi)}; +#else + {MNG_UINT_BASI, mng_init_basi, mng_free_basi, mng_read_basi, mng_write_basi, mng_assign_basi, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_BASI, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_BASI)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_basi (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_BASI, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_basip)pChunk)->iWidth = iWidth; + ((mng_basip)pChunk)->iHeight = iHeight; + ((mng_basip)pChunk)->iBitdepth = iBitdepth; + ((mng_basip)pChunk)->iColortype = iColortype; + ((mng_basip)pChunk)->iCompression = iCompression; + ((mng_basip)pChunk)->iFilter = iFilter; + ((mng_basip)pChunk)->iInterlace = iInterlace; + ((mng_basip)pChunk)->iRed = iRed; + ((mng_basip)pChunk)->iGreen = iGreen; + ((mng_basip)pChunk)->iBlue = iBlue; + ((mng_basip)pChunk)->iAlpha = iAlpha; + ((mng_basip)pChunk)->iViewable = iViewable; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_BASI, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_CLON +mng_retcode MNG_DECL mng_putchunk_clon (mng_handle hHandle, + mng_uint16 iSourceid, + mng_uint16 iCloneid, + mng_uint8 iClonetype, + mng_uint8 iDonotshow, + mng_uint8 iConcrete, + mng_bool bHasloca, + mng_uint8 iLocationtype, + mng_int32 iLocationx, + mng_int32 iLocationy) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_CLON, mng_init_general, mng_free_general, mng_read_clon, mng_write_clon, mng_assign_general, 0, 0, sizeof(mng_clon)}; +#else + {MNG_UINT_CLON, mng_init_clon, mng_free_clon, mng_read_clon, mng_write_clon, mng_assign_clon, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_CLON, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_CLON)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_clon (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_CLON, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_clonp)pChunk)->iSourceid = iSourceid; + ((mng_clonp)pChunk)->iCloneid = iCloneid; + ((mng_clonp)pChunk)->iClonetype = iClonetype; + ((mng_clonp)pChunk)->iDonotshow = iDonotshow; + ((mng_clonp)pChunk)->iConcrete = iConcrete; + ((mng_clonp)pChunk)->bHasloca = bHasloca; + ((mng_clonp)pChunk)->iLocationtype = iLocationtype; + ((mng_clonp)pChunk)->iLocationx = iLocationx; + ((mng_clonp)pChunk)->iLocationy = iLocationy; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_CLON, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_PAST +mng_retcode MNG_DECL mng_putchunk_past (mng_handle hHandle, + mng_uint16 iDestid, + mng_uint8 iTargettype, + mng_int32 iTargetx, + mng_int32 iTargety, + mng_uint32 iCount) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_PAST, mng_init_general, mng_free_past, mng_read_past, mng_write_past, mng_assign_past, 0, 0, sizeof(mng_past)}; +#else + {MNG_UINT_PAST, mng_init_past, mng_free_past, mng_read_past, mng_write_past, mng_assign_past, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PAST, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_PAST)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_past (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_PAST, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_pastp)pChunk)->iDestid = iDestid; + ((mng_pastp)pChunk)->iTargettype = iTargettype; + ((mng_pastp)pChunk)->iTargetx = iTargetx; + ((mng_pastp)pChunk)->iTargety = iTargety; + ((mng_pastp)pChunk)->iCount = iCount; + + if (iCount) + MNG_ALLOC (pData, ((mng_pastp)pChunk)->pSources, iCount * sizeof (mng_past_source)); + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PAST, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_PAST +mng_retcode MNG_DECL mng_putchunk_past_src (mng_handle hHandle, + mng_uint32 iEntry, + mng_uint16 iSourceid, + mng_uint8 iComposition, + mng_uint8 iOrientation, + mng_uint8 iOffsettype, + mng_int32 iOffsetx, + mng_int32 iOffsety, + mng_uint8 iBoundarytype, + mng_int32 iBoundaryl, + mng_int32 iBoundaryr, + mng_int32 iBoundaryt, + mng_int32 iBoundaryb) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_past_sourcep pEntry; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PAST_SRC, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + + pChunk = pData->pLastchunk; /* last one must have been PAST ! */ + + if (((mng_chunk_headerp)pChunk)->iChunkname != MNG_UINT_PAST) + MNG_ERROR (pData, MNG_NOCORRCHUNK) + /* index out of bounds ? */ + if (iEntry >= ((mng_pastp)pChunk)->iCount) + MNG_ERROR (pData, MNG_INVALIDENTRYIX) + /* address proper entry */ + pEntry = ((mng_pastp)pChunk)->pSources + iEntry; + + pEntry->iSourceid = iSourceid; /* fill entry */ + pEntry->iComposition = iComposition; + pEntry->iOrientation = iOrientation; + pEntry->iOffsettype = iOffsettype; + pEntry->iOffsetx = iOffsetx; + pEntry->iOffsety = iOffsety; + pEntry->iBoundarytype = iBoundarytype; + pEntry->iBoundaryl = iBoundaryl; + pEntry->iBoundaryr = iBoundaryr; + pEntry->iBoundaryt = iBoundaryt; + pEntry->iBoundaryb = iBoundaryb; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PAST_SRC, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_DISC +mng_retcode MNG_DECL mng_putchunk_disc (mng_handle hHandle, + mng_uint32 iCount, + mng_uint16p pObjectids) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_DISC, mng_init_general, mng_free_disc, mng_read_disc, mng_write_disc, mng_assign_disc, 0, 0, sizeof(mng_disc)}; +#else + {MNG_UINT_DISC, mng_init_disc, mng_free_disc, mng_read_disc, mng_write_disc, mng_assign_disc, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_DISC, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_DISC)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_disc (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_DISC, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_discp)pChunk)->iCount = iCount; + + if (iCount) + { + mng_uint32 iSize = iCount * sizeof (mng_uint32); + + MNG_ALLOC (pData, ((mng_discp)pChunk)->pObjectids, iSize); + MNG_COPY (((mng_discp)pChunk)->pObjectids, pObjectids, iSize); + } + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_DISC, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_BACK +mng_retcode MNG_DECL mng_putchunk_back (mng_handle hHandle, + mng_uint16 iRed, + mng_uint16 iGreen, + mng_uint16 iBlue, + mng_uint8 iMandatory, + mng_uint16 iImageid, + mng_uint8 iTile) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_BACK, mng_init_general, mng_free_general, mng_read_back, mng_write_back, mng_assign_general, 0, 0, sizeof(mng_back)}; +#else + {MNG_UINT_BACK, mng_init_back, mng_free_back, mng_read_back, mng_write_back, mng_assign_back, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_BACK, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_BACK)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_back (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_BACK, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_backp)pChunk)->iRed = iRed; + ((mng_backp)pChunk)->iGreen = iGreen; + ((mng_backp)pChunk)->iBlue = iBlue; + ((mng_backp)pChunk)->iMandatory = iMandatory; + ((mng_backp)pChunk)->iImageid = iImageid; + ((mng_backp)pChunk)->iTile = iTile; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_BACK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_FRAM +mng_retcode MNG_DECL mng_putchunk_fram (mng_handle hHandle, + mng_bool bEmpty, + mng_uint8 iMode, + mng_uint32 iNamesize, + mng_pchar zName, + mng_uint8 iChangedelay, + mng_uint8 iChangetimeout, + mng_uint8 iChangeclipping, + mng_uint8 iChangesyncid, + mng_uint32 iDelay, + mng_uint32 iTimeout, + mng_uint8 iBoundarytype, + mng_int32 iBoundaryl, + mng_int32 iBoundaryr, + mng_int32 iBoundaryt, + mng_int32 iBoundaryb, + mng_uint32 iCount, + mng_uint32p pSyncids) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_FRAM, mng_init_general, mng_free_fram, mng_read_fram, mng_write_fram, mng_assign_fram, 0, 0, sizeof(mng_fram)}; +#else + {MNG_UINT_FRAM, mng_init_fram, mng_free_fram, mng_read_fram, mng_write_fram, mng_assign_fram, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_FRAM, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_FRAM)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_fram (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_FRAM, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_framp)pChunk)->bEmpty = bEmpty; + ((mng_framp)pChunk)->iMode = iMode; + ((mng_framp)pChunk)->iNamesize = iNamesize; + ((mng_framp)pChunk)->iChangedelay = iChangedelay; + ((mng_framp)pChunk)->iChangetimeout = iChangetimeout; + ((mng_framp)pChunk)->iChangeclipping = iChangeclipping; + ((mng_framp)pChunk)->iChangesyncid = iChangesyncid; + ((mng_framp)pChunk)->iDelay = iDelay; + ((mng_framp)pChunk)->iTimeout = iTimeout; + ((mng_framp)pChunk)->iBoundarytype = iBoundarytype; + ((mng_framp)pChunk)->iBoundaryl = iBoundaryl; + ((mng_framp)pChunk)->iBoundaryr = iBoundaryr; + ((mng_framp)pChunk)->iBoundaryt = iBoundaryt; + ((mng_framp)pChunk)->iBoundaryb = iBoundaryb; + ((mng_framp)pChunk)->iCount = iCount; + + if (iNamesize) + { + MNG_ALLOC (pData, ((mng_framp)pChunk)->zName, iNamesize + 1); + MNG_COPY (((mng_framp)pChunk)->zName, zName, iNamesize); + } + + if (iCount) + { + mng_uint32 iSize = iCount * sizeof (mng_uint32); + + MNG_ALLOC (pData, ((mng_framp)pChunk)->pSyncids, iSize); + MNG_COPY (((mng_framp)pChunk)->pSyncids, pSyncids, iSize); + } + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_FRAM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_MOVE +mng_retcode MNG_DECL mng_putchunk_move (mng_handle hHandle, + mng_uint16 iFirstid, + mng_uint16 iLastid, + mng_uint8 iMovetype, + mng_int32 iMovex, + mng_int32 iMovey) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_MOVE, mng_init_general, mng_free_general, mng_read_move, mng_write_move, mng_assign_general, 0, 0, sizeof(mng_move)}; +#else + {MNG_UINT_MOVE, mng_init_move, mng_free_move, mng_read_move, mng_write_move, mng_assign_move, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_MOVE, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_MOVE)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_move (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_MOVE, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_movep)pChunk)->iFirstid = iFirstid; + ((mng_movep)pChunk)->iLastid = iLastid; + ((mng_movep)pChunk)->iMovetype = iMovetype; + ((mng_movep)pChunk)->iMovex = iMovex; + ((mng_movep)pChunk)->iMovey = iMovey; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_MOVE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_CLIP +mng_retcode MNG_DECL mng_putchunk_clip (mng_handle hHandle, + mng_uint16 iFirstid, + mng_uint16 iLastid, + mng_uint8 iCliptype, + mng_int32 iClipl, + mng_int32 iClipr, + mng_int32 iClipt, + mng_int32 iClipb) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_CLIP, mng_init_general, mng_free_general, mng_read_clip, mng_write_clip, mng_assign_general, 0, 0, sizeof(mng_clip)}; +#else + {MNG_UINT_CLIP, mng_init_clip, mng_free_clip, mng_read_clip, mng_write_clip, mng_assign_clip, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_CLIP, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_CLIP)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_clip (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_CLIP, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_clipp)pChunk)->iFirstid = iFirstid; + ((mng_clipp)pChunk)->iLastid = iLastid; + ((mng_clipp)pChunk)->iCliptype = iCliptype; + ((mng_clipp)pChunk)->iClipl = iClipl; + ((mng_clipp)pChunk)->iClipr = iClipr; + ((mng_clipp)pChunk)->iClipt = iClipt; + ((mng_clipp)pChunk)->iClipb = iClipb; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_CLIP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SHOW +mng_retcode MNG_DECL mng_putchunk_show (mng_handle hHandle, + mng_bool bEmpty, + mng_uint16 iFirstid, + mng_uint16 iLastid, + mng_uint8 iMode) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_SHOW, mng_init_general, mng_free_general, mng_read_show, mng_write_show, mng_assign_general, 0, 0, sizeof(mng_show)}; +#else + {MNG_UINT_SHOW, mng_init_show, mng_free_show, mng_read_show, mng_write_show, mng_assign_show, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SHOW, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_SHOW)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_show (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_SHOW, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_showp)pChunk)->bEmpty = bEmpty; + ((mng_showp)pChunk)->iFirstid = iFirstid; + ((mng_showp)pChunk)->iLastid = iLastid; + ((mng_showp)pChunk)->iMode = iMode; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SHOW, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_TERM +mng_retcode MNG_DECL mng_putchunk_term (mng_handle hHandle, + mng_uint8 iTermaction, + mng_uint8 iIteraction, + mng_uint32 iDelay, + mng_uint32 iItermax) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_TERM, mng_init_general, mng_free_general, mng_read_term, mng_write_term, mng_assign_general, 0, 0, sizeof(mng_term)}; +#else + {MNG_UINT_TERM, mng_init_term, mng_free_term, mng_read_term, mng_write_term, mng_assign_term, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_TERM, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_term (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_TERM, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_termp)pChunk)->iTermaction = iTermaction; + ((mng_termp)pChunk)->iIteraction = iIteraction; + ((mng_termp)pChunk)->iDelay = iDelay; + ((mng_termp)pChunk)->iItermax = iItermax; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_TERM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SAVE +mng_retcode MNG_DECL mng_putchunk_save (mng_handle hHandle, + mng_bool bEmpty, + mng_uint8 iOffsettype, + mng_uint32 iCount) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_SAVE, mng_init_general, mng_free_save, mng_read_save, mng_write_save, mng_assign_save, 0, 0, sizeof(mng_save)}; +#else + {MNG_UINT_SAVE, mng_init_save, mng_free_save, mng_read_save, mng_write_save, mng_assign_save, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SAVE, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_SAVE)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_save (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_SAVE, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_savep)pChunk)->bEmpty = bEmpty; + ((mng_savep)pChunk)->iOffsettype = iOffsettype; + ((mng_savep)pChunk)->iCount = iCount; + + if (iCount) + MNG_ALLOC (pData, ((mng_savep)pChunk)->pEntries, iCount * sizeof (mng_save_entry)); + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SAVE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_putchunk_save_entry (mng_handle hHandle, + mng_uint32 iEntry, + mng_uint8 iEntrytype, + mng_uint32arr2 iOffset, + mng_uint32arr2 iStarttime, + mng_uint32 iLayernr, + mng_uint32 iFramenr, + mng_uint32 iNamesize, + mng_pchar zName) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_save_entryp pEntry; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SAVE_ENTRY, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + + pChunk = pData->pLastchunk; /* last one must have been SAVE ! */ + + if (((mng_chunk_headerp)pChunk)->iChunkname != MNG_UINT_SAVE) + MNG_ERROR (pData, MNG_NOCORRCHUNK) + /* index out of bounds ? */ + if (iEntry >= ((mng_savep)pChunk)->iCount) + MNG_ERROR (pData, MNG_INVALIDENTRYIX) + /* address proper entry */ + pEntry = ((mng_savep)pChunk)->pEntries + iEntry; + + pEntry->iEntrytype = iEntrytype; /* fill entry */ + pEntry->iOffset[0] = iOffset[0]; + pEntry->iOffset[1] = iOffset[1]; + pEntry->iStarttime[0] = iStarttime[0]; + pEntry->iStarttime[1] = iStarttime[1]; + pEntry->iLayernr = iLayernr; + pEntry->iFramenr = iFramenr; + pEntry->iNamesize = iNamesize; + + if (iNamesize) + { + MNG_ALLOC (pData, pEntry->zName, iNamesize + 1); + MNG_COPY (pEntry->zName, zName, iNamesize); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SAVE_ENTRY, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SEEK +mng_retcode MNG_DECL mng_putchunk_seek (mng_handle hHandle, + mng_uint32 iNamesize, + mng_pchar zName) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_SEEK, mng_init_general, mng_free_seek, mng_read_seek, mng_write_seek, mng_assign_seek, 0, 0, sizeof(mng_seek)}; +#else + {MNG_UINT_SEEK, mng_init_seek, mng_free_seek, mng_read_seek, mng_write_seek, mng_assign_seek, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SEEK, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_SEEK)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_seek (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_SEEK, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_seekp)pChunk)->iNamesize = iNamesize; + + if (iNamesize) + { + MNG_ALLOC (pData, ((mng_seekp)pChunk)->zName, iNamesize + 1); + MNG_COPY (((mng_seekp)pChunk)->zName, zName, iNamesize); + } + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_SEEK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_eXPI +mng_retcode MNG_DECL mng_putchunk_expi (mng_handle hHandle, + mng_uint16 iSnapshotid, + mng_uint32 iNamesize, + mng_pchar zName) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_eXPI, mng_init_general, mng_free_expi, mng_read_expi, mng_write_expi, mng_assign_general, 0, 0, sizeof(mng_expi)}; +#else + {MNG_UINT_eXPI, mng_init_expi, mng_free_expi, mng_read_expi, mng_write_expi, mng_assign_expi, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_EXPI, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_eXPI)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_expi (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_eXPI, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_expip)pChunk)->iSnapshotid = iSnapshotid; + ((mng_expip)pChunk)->iNamesize = iNamesize; + + if (iNamesize) + { + MNG_ALLOC (pData, ((mng_expip)pChunk)->zName, iNamesize + 1); + MNG_COPY (((mng_expip)pChunk)->zName, zName, iNamesize); + } + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_EXPI, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_fPRI +mng_retcode MNG_DECL mng_putchunk_fpri (mng_handle hHandle, + mng_uint8 iDeltatype, + mng_uint8 iPriority) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_fPRI, mng_init_general, mng_free_general, mng_read_fpri, mng_write_fpri, mng_assign_general, 0, 0, sizeof(mng_fpri)}; +#else + {MNG_UINT_fPRI, mng_init_fpri, mng_free_fpri, mng_read_fpri, mng_write_fpri, mng_assign_fpri, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_FPRI, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_fPRI)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_fpri (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_fPRI, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_fprip)pChunk)->iDeltatype = iDeltatype; + ((mng_fprip)pChunk)->iPriority = iPriority; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_FPRI, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_nEED +mng_retcode MNG_DECL mng_putchunk_need (mng_handle hHandle, + mng_uint32 iKeywordssize, + mng_pchar zKeywords) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_nEED, mng_init_general, mng_free_need, mng_read_need, mng_write_need, mng_assign_need, 0, 0, sizeof(mng_need)}; +#else + {MNG_UINT_nEED, mng_init_need, mng_free_need, mng_read_need, mng_write_need, mng_assign_need, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_NEED, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_nEED)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_need (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_nEED, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_needp)pChunk)->iKeywordssize = iKeywordssize; + + if (iKeywordssize) + { + MNG_ALLOC (pData, ((mng_needp)pChunk)->zKeywords, iKeywordssize + 1); + MNG_COPY (((mng_needp)pChunk)->zKeywords, zKeywords, iKeywordssize); + } + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_NEED, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_pHYg +mng_retcode MNG_DECL mng_putchunk_phyg (mng_handle hHandle, + mng_bool bEmpty, + mng_uint32 iSizex, + mng_uint32 iSizey, + mng_uint8 iUnit) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_pHYg, mng_init_general, mng_free_general, mng_read_phyg, mng_write_phyg, mng_assign_general, 0, 0, sizeof(mng_phyg)}; +#else + {MNG_UINT_pHYg, mng_init_phyg, mng_free_phyg, mng_read_phyg, mng_write_phyg, mng_assign_phyg, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PHYG, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_pHYg)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_phyg (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_pHYg, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_phygp)pChunk)->bEmpty = bEmpty; + ((mng_phygp)pChunk)->iSizex = iSizex; + ((mng_phygp)pChunk)->iSizey = iSizey; + ((mng_phygp)pChunk)->iUnit = iUnit; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PHYG, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG + +mng_retcode MNG_DECL mng_putchunk_jhdr (mng_handle hHandle, + mng_uint32 iWidth, + mng_uint32 iHeight, + mng_uint8 iColortype, + mng_uint8 iImagesampledepth, + mng_uint8 iImagecompression, + mng_uint8 iImageinterlace, + mng_uint8 iAlphasampledepth, + mng_uint8 iAlphacompression, + mng_uint8 iAlphafilter, + mng_uint8 iAlphainterlace) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_JHDR, mng_init_general, mng_free_general, mng_read_jhdr, mng_write_jhdr, mng_assign_general, 0, 0, sizeof(mng_jhdr)}; +#else + {MNG_UINT_JHDR, mng_init_jhdr, mng_free_jhdr, mng_read_jhdr, mng_write_jhdr, mng_assign_jhdr, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_JHDR, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_JHDR)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_jhdr (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_JHDR, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_jhdrp)pChunk)->iWidth = iWidth; + ((mng_jhdrp)pChunk)->iHeight = iHeight; + ((mng_jhdrp)pChunk)->iColortype = iColortype; + ((mng_jhdrp)pChunk)->iImagesampledepth = iImagesampledepth; + ((mng_jhdrp)pChunk)->iImagecompression = iImagecompression; + ((mng_jhdrp)pChunk)->iImageinterlace = iImageinterlace; + ((mng_jhdrp)pChunk)->iAlphasampledepth = iAlphasampledepth; + ((mng_jhdrp)pChunk)->iAlphacompression = iAlphacompression; + ((mng_jhdrp)pChunk)->iAlphafilter = iAlphafilter; + ((mng_jhdrp)pChunk)->iAlphainterlace = iAlphainterlace; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_JHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG + +mng_retcode MNG_DECL mng_putchunk_jdat (mng_handle hHandle, + mng_uint32 iRawlen, + mng_ptr pRawdata) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_JDAT, mng_init_general, mng_free_jdat, mng_read_jdat, mng_write_jdat, mng_assign_jdat, 0, 0, sizeof(mng_jdat)}; +#else + {MNG_UINT_JDAT, mng_init_jdat, mng_free_jdat, mng_read_jdat, mng_write_jdat, mng_assign_jdat, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_JDAT, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR or JHDR first! */ + if ((pData->iFirstchunkadded != MNG_UINT_MHDR) && + (pData->iFirstchunkadded != MNG_UINT_JHDR) ) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_JDAT)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_jdat (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_JDAT, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_jdatp)pChunk)->iDatasize = iRawlen; + + if (iRawlen) + { + MNG_ALLOC (pData, ((mng_jdatp)pChunk)->pData, iRawlen); + MNG_COPY (((mng_jdatp)pChunk)->pData, pRawdata, iRawlen); + } + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_JDAT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG + +mng_retcode MNG_DECL mng_putchunk_jdaa (mng_handle hHandle, + mng_uint32 iRawlen, + mng_ptr pRawdata) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_JDAA, mng_init_general, mng_free_jdaa, mng_read_jdaa, mng_write_jdaa, mng_assign_jdaa, 0, 0, sizeof(mng_jdaa)}; +#else + {MNG_UINT_JDAA, mng_init_jdaa, mng_free_jdaa, mng_read_jdaa, mng_write_jdaa, mng_assign_jdaa, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_JDAA, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR or JHDR first! */ + if ((pData->iFirstchunkadded != MNG_UINT_MHDR) && + (pData->iFirstchunkadded != MNG_UINT_JHDR) ) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_JDAA)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_jdaa (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_JDAA, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_jdaap)pChunk)->iDatasize = iRawlen; + + if (iRawlen) + { + MNG_ALLOC (pData, ((mng_jdaap)pChunk)->pData, iRawlen); + MNG_COPY (((mng_jdaap)pChunk)->pData, pRawdata, iRawlen); + } + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_JDAA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG + +mng_retcode MNG_DECL mng_putchunk_jsep (mng_handle hHandle) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_JSEP, mng_init_general, mng_free_general, mng_read_jsep, mng_write_jsep, mng_assign_general, 0, 0, sizeof(mng_jsep)}; +#else + {MNG_UINT_JSEP, mng_init_jsep, mng_free_jsep, mng_read_jsep, mng_write_jsep, mng_assign_jsep, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_JSEP, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR or JHDR first! */ + if ((pData->iFirstchunkadded != MNG_UINT_MHDR) && + (pData->iFirstchunkadded != MNG_UINT_JHDR) ) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_JSEP)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_jsep (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_JSEP, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_JSEP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +mng_retcode MNG_DECL mng_putchunk_dhdr (mng_handle hHandle, + mng_uint16 iObjectid, + mng_uint8 iImagetype, + mng_uint8 iDeltatype, + mng_uint32 iBlockwidth, + mng_uint32 iBlockheight, + mng_uint32 iBlockx, + mng_uint32 iBlocky) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_DHDR, mng_init_general, mng_free_general, mng_read_dhdr, mng_write_dhdr, mng_assign_general, 0, 0, sizeof(mng_dhdr)}; +#else + {MNG_UINT_DHDR, mng_init_dhdr, mng_free_dhdr, mng_read_dhdr, mng_write_dhdr, mng_assign_dhdr, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_DHDR, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_DHDR)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_dhdr (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_DHDR, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_dhdrp)pChunk)->iObjectid = iObjectid; + ((mng_dhdrp)pChunk)->iImagetype = iImagetype; + ((mng_dhdrp)pChunk)->iDeltatype = iDeltatype; + ((mng_dhdrp)pChunk)->iBlockwidth = iBlockwidth; + ((mng_dhdrp)pChunk)->iBlockheight = iBlockheight; + ((mng_dhdrp)pChunk)->iBlockx = iBlockx; + ((mng_dhdrp)pChunk)->iBlocky = iBlocky; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_DHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +mng_retcode MNG_DECL mng_putchunk_prom (mng_handle hHandle, + mng_uint8 iColortype, + mng_uint8 iSampledepth, + mng_uint8 iFilltype) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_PROM, mng_init_general, mng_free_general, mng_read_prom, mng_write_prom, mng_assign_general, 0, 0, sizeof(mng_prom)}; +#else + {MNG_UINT_PROM, mng_init_prom, mng_free_prom, mng_read_prom, mng_write_prom, mng_assign_prom, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PROM, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_PROM)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_prom (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_PROM, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_promp)pChunk)->iColortype = iColortype; + ((mng_promp)pChunk)->iSampledepth = iSampledepth; + ((mng_promp)pChunk)->iFilltype = iFilltype; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PROM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +mng_retcode MNG_DECL mng_putchunk_ipng (mng_handle hHandle) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_IPNG, mng_init_general, mng_free_general, mng_read_ipng, mng_write_ipng, mng_assign_general, 0, 0, sizeof(mng_ipng)}; +#else + {MNG_UINT_IPNG, mng_init_ipng, mng_free_ipng, mng_read_ipng, mng_write_ipng, mng_assign_ipng, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_IPNG, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_IPNG)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_ipng (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_IPNG, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_IPNG, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +mng_retcode MNG_DECL mng_putchunk_pplt (mng_handle hHandle, + mng_uint8 iDeltatype, + mng_uint32 iCount) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_PPLT, mng_init_general, mng_free_general, mng_read_pplt, mng_write_pplt, mng_assign_general, 0, 0, sizeof(mng_pplt)}; +#else + {MNG_UINT_PPLT, mng_init_pplt, mng_free_pplt, mng_read_pplt, mng_write_pplt, mng_assign_pplt, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PPLT, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_PPLT)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_pplt (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_PPLT, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_ppltp)pChunk)->iDeltatype = iDeltatype; + ((mng_ppltp)pChunk)->iCount = iCount; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PPLT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +mng_retcode MNG_DECL mng_putchunk_pplt_entry (mng_handle hHandle, + mng_uint32 iEntry, + mng_uint16 iRed, + mng_uint16 iGreen, + mng_uint16 iBlue, + mng_uint16 iAlpha) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_pplt_entryp pEntry; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PPLT_ENTRY, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + + pChunk = pData->pLastchunk; /* last one must have been PPLT ! */ + + if (((mng_chunk_headerp)pChunk)->iChunkname != MNG_UINT_PPLT) + MNG_ERROR (pData, MNG_NOCORRCHUNK) + + /* index out of bounds ? */ + if (iEntry >= ((mng_ppltp)pChunk)->iCount) + MNG_ERROR (pData, MNG_INVALIDENTRYIX) + /* address proper entry */ + pEntry = (mng_pplt_entryp)(((mng_ppltp)pChunk)->aEntries) + iEntry; + + pEntry->iRed = (mng_uint8)iRed; /* fill the entry */ + pEntry->iGreen = (mng_uint8)iGreen; + pEntry->iBlue = (mng_uint8)iBlue; + pEntry->iAlpha = (mng_uint8)iAlpha; + pEntry->bUsed = MNG_TRUE; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_PPLT_ENTRY, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifdef MNG_INCLUDE_JNG +mng_retcode MNG_DECL mng_putchunk_ijng (mng_handle hHandle) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_IJNG, mng_init_general, mng_free_general, mng_read_ijng, mng_write_ijng, mng_assign_general, 0, 0, sizeof(mng_ijng)}; +#else + {MNG_UINT_IJNG, mng_init_ijng, mng_free_ijng, mng_read_ijng, mng_write_ijng, mng_assign_ijng, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_IJNG, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_IJNG)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_ijng (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_IJNG, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_IJNG, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +mng_retcode MNG_DECL mng_putchunk_drop (mng_handle hHandle, + mng_uint32 iCount, + mng_chunkidp pChunknames) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_DROP, mng_init_general, mng_free_drop, mng_read_drop, mng_write_drop, mng_assign_drop, 0, 0, sizeof(mng_drop)}; +#else + {MNG_UINT_DROP, mng_init_drop, mng_free_drop, mng_read_drop, mng_write_drop, mng_assign_drop, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_DROP, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_DROP)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_drop (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_DROP, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_dropp)pChunk)->iCount = iCount; + + if (iCount) + { + mng_uint32 iSize = iCount * sizeof (mng_chunkid); + + MNG_ALLOC (pData, ((mng_dropp)pChunk)->pChunknames, iSize); + MNG_COPY (((mng_dropp)pChunk)->pChunknames, pChunknames, iSize); + } + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_DROP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_DBYK +mng_retcode MNG_DECL mng_putchunk_dbyk (mng_handle hHandle, + mng_chunkid iChunkname, + mng_uint8 iPolarity, + mng_uint32 iKeywordssize, + mng_pchar zKeywords) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_DBYK, mng_init_general, mng_free_dbyk, mng_read_dbyk, mng_write_dbyk, mng_assign_dbyk, 0, 0, sizeof(mng_dbyk)}; +#else + {MNG_UINT_DBYK, mng_init_dbyk, mng_free_dbyk, mng_read_dbyk, mng_write_dbyk, mng_assign_dbyk, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_DBYK, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_DBYK)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_dbyk (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_DBYK, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_dbykp)pChunk)->iChunkname = iChunkname; + ((mng_dbykp)pChunk)->iPolarity = iPolarity; + ((mng_dbykp)pChunk)->iKeywordssize = iKeywordssize; + + if (iKeywordssize) + { + MNG_ALLOC (pData, ((mng_dbykp)pChunk)->zKeywords, iKeywordssize + 1); + MNG_COPY (((mng_dbykp)pChunk)->zKeywords, zKeywords, iKeywordssize); + } + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_DBYK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_ORDR +mng_retcode MNG_DECL mng_putchunk_ordr (mng_handle hHandle, + mng_uint32 iCount) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_ORDR, mng_init_general, mng_free_ordr, mng_read_ordr, mng_write_ordr, mng_assign_ordr, 0, 0, sizeof(mng_ordr)}; +#else + {MNG_UINT_ORDR, mng_init_ordr, mng_free_ordr, mng_read_ordr, mng_write_ordr, mng_assign_ordr, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_ORDR, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_ORDR)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_ordr (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_ORDR, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_ordrp)pChunk)->iCount = iCount; + + if (iCount) + MNG_ALLOC (pData, ((mng_ordrp)pChunk)->pEntries, iCount * sizeof (mng_ordr_entry)); + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_ORDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_ORDR +mng_retcode MNG_DECL mng_putchunk_ordr_entry (mng_handle hHandle, + mng_uint32 iEntry, + mng_chunkid iChunkname, + mng_uint8 iOrdertype) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_ordr_entryp pEntry; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_ORDR_ENTRY, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + + pChunk = pData->pLastchunk; /* last one must have been ORDR ! */ + + if (((mng_chunk_headerp)pChunk)->iChunkname != MNG_UINT_ORDR) + MNG_ERROR (pData, MNG_NOCORRCHUNK) + /* index out of bounds ? */ + if (iEntry >= ((mng_ordrp)pChunk)->iCount) + MNG_ERROR (pData, MNG_INVALIDENTRYIX) + /* address proper entry */ + pEntry = ((mng_ordrp)pChunk)->pEntries + iEntry; + + pEntry->iChunkname = iChunkname; /* fill the entry */ + pEntry->iOrdertype = iOrdertype; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_ORDR_ENTRY, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_MAGN +mng_retcode MNG_DECL mng_putchunk_magn (mng_handle hHandle, + mng_uint16 iFirstid, + mng_uint16 iLastid, + mng_uint16 iMethodX, + mng_uint16 iMX, + mng_uint16 iMY, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint16 iMT, + mng_uint16 iMB, + mng_uint16 iMethodY) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_MAGN, mng_init_general, mng_free_general, mng_read_magn, mng_write_magn, mng_assign_general, 0, 0, sizeof(mng_magn)}; +#else + {MNG_UINT_MAGN, mng_init_magn, mng_free_magn, mng_read_magn, mng_write_magn, mng_assign_magn, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_MAGN, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_MAGN)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_magn (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_MAGN, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_magnp)pChunk)->iFirstid = iFirstid; + ((mng_magnp)pChunk)->iLastid = iLastid; + ((mng_magnp)pChunk)->iMethodX = (mng_uint8)iMethodX; + ((mng_magnp)pChunk)->iMX = iMX; + ((mng_magnp)pChunk)->iMY = iMY; + ((mng_magnp)pChunk)->iML = iML; + ((mng_magnp)pChunk)->iMR = iMR; + ((mng_magnp)pChunk)->iMT = iMT; + ((mng_magnp)pChunk)->iMB = iMB; + ((mng_magnp)pChunk)->iMethodY = (mng_uint8)iMethodY; + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_MAGN, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_MPNG_PROPOSAL +MNG_EXT mng_retcode MNG_DECL mng_putchunk_mpng (mng_handle hHandle, + mng_uint32 iFramewidth, + mng_uint32 iFrameheight, + mng_uint16 iNumplays, + mng_uint16 iTickspersec, + mng_uint8 iCompressionmethod, + mng_uint32 iCount) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_mpNG, mng_init_general, mng_free_mpng, mng_read_mpng, mng_write_mpng, mng_assign_mpng, 0, 0, sizeof(mng_mpng)}; +#else + {MNG_UINT_mpNG, mng_init_mpng, mng_free_mpng, mng_read_mpng, mng_write_mpng, mng_assign_mpng, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_MPNG, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a IHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_IHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_mpng (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_mpNG, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_mpngp)pChunk)->iFramewidth = iFramewidth; + ((mng_mpngp)pChunk)->iFrameheight = iFrameheight; + ((mng_mpngp)pChunk)->iNumplays = iNumplays; + ((mng_mpngp)pChunk)->iTickspersec = iTickspersec; + ((mng_mpngp)pChunk)->iCompressionmethod = iCompressionmethod; + ((mng_mpngp)pChunk)->iFramessize = iCount * sizeof (mng_mpng_frame); + + if (iCount) + MNG_ALLOC (pData, ((mng_mpngp)pChunk)->pFrames, ((mng_mpngp)pChunk)->iFramessize); + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_MPNG, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_MPNG_PROPOSAL +MNG_EXT mng_retcode MNG_DECL mng_putchunk_mpng_frame (mng_handle hHandle, + mng_uint32 iEntry, + mng_uint32 iX, + mng_uint32 iY, + mng_uint32 iWidth, + mng_uint32 iHeight, + mng_int32 iXoffset, + mng_int32 iYoffset, + mng_uint16 iTicks) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_mpng_framep pFrame; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_MPNG_FRAME, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a IHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_IHDR) + MNG_ERROR (pData, MNG_NOHEADER) + + pChunk = pData->pLastchunk; /* last one must have been mpNG ! */ + + if (((mng_chunk_headerp)pChunk)->iChunkname != MNG_UINT_mpNG) + MNG_ERROR (pData, MNG_NOCORRCHUNK) + /* index out of bounds ? */ + if (iEntry >= (((mng_mpngp)pChunk)->iFramessize / sizeof (mng_mpng_frame))) + MNG_ERROR (pData, MNG_INVALIDENTRYIX) + /* address proper entry */ + pFrame = ((mng_mpngp)pChunk)->pFrames + iEntry; + /* fill entry */ + pFrame->iX = iX; + pFrame->iY = iY; + pFrame->iWidth = iWidth; + pFrame->iHeight = iHeight; + pFrame->iXoffset = iXoffset; + pFrame->iYoffset = iYoffset; + pFrame->iTicks = iTicks; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_MPNG_FRAME, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_evNT +mng_retcode MNG_DECL mng_putchunk_evnt (mng_handle hHandle, + mng_uint32 iCount) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_evNT, mng_init_general, mng_free_evnt, mng_read_evnt, mng_write_evnt, mng_assign_evnt, 0, 0, sizeof(mng_evnt)}; +#else + {MNG_UINT_evNT, mng_init_evnt, mng_free_evnt, mng_read_evnt, mng_write_evnt, mng_assign_evnt, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_EVNT, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, MNG_UINT_evNT)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_evnt (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_evNT, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_evntp)pChunk)->iCount = iCount; + + if (iCount) + MNG_ALLOC (pData, ((mng_evntp)pChunk)->pEntries, iCount * sizeof (mng_evnt_entry)); + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_EVNT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_putchunk_evnt_entry (mng_handle hHandle, + mng_uint32 iEntry, + mng_uint8 iEventtype, + mng_uint8 iMasktype, + mng_int32 iLeft, + mng_int32 iRight, + mng_int32 iTop, + mng_int32 iBottom, + mng_uint16 iObjectid, + mng_uint8 iIndex, + mng_uint32 iSegmentnamesize, + mng_pchar zSegmentname) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_evnt_entryp pEntry; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_EVNT_ENTRY, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a MHDR first! */ + if (pData->iFirstchunkadded != MNG_UINT_MHDR) + MNG_ERROR (pData, MNG_NOHEADER) + + pChunk = pData->pLastchunk; /* last one must have been evNT ! */ + + if (((mng_chunk_headerp)pChunk)->iChunkname != MNG_UINT_evNT) + MNG_ERROR (pData, MNG_NOCORRCHUNK) + /* index out of bounds ? */ + if (iEntry >= ((mng_evntp)pChunk)->iCount) + MNG_ERROR (pData, MNG_INVALIDENTRYIX) + /* address proper entry */ + pEntry = ((mng_evntp)pChunk)->pEntries + iEntry; + /* fill entry */ + pEntry->iEventtype = iEventtype; + pEntry->iMasktype = iMasktype; + pEntry->iLeft = iLeft; + pEntry->iRight = iRight; + pEntry->iTop = iTop; + pEntry->iBottom = iBottom; + pEntry->iObjectid = iObjectid; + pEntry->iIndex = iIndex; + pEntry->iSegmentnamesize = iSegmentnamesize; + + if (iSegmentnamesize) + { + MNG_ALLOC (pData, pEntry->zSegmentname, iSegmentnamesize + 1); + MNG_COPY (pEntry->zSegmentname, zSegmentname, iSegmentnamesize); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_EVNT_ENTRY, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_putchunk_unknown (mng_handle hHandle, + mng_chunkid iChunkname, + mng_uint32 iRawlen, + mng_ptr pRawdata) +{ + mng_datap pData; + mng_chunkp pChunk; + mng_retcode iRetcode; +#ifndef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_header sChunkheader = +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + {MNG_UINT_HUH, mng_init_general, mng_free_unknown, mng_read_unknown, mng_write_unknown, mng_assign_unknown, 0, 0, sizeof(mng_unknown_chunk)}; +#else + {MNG_UINT_HUH, mng_init_unknown, mng_free_unknown, mng_read_unknown, mng_write_unknown, mng_assign_unknown, 0, 0}; +#endif +#else + mng_chunk_header sChunkheader; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_UNKNOWN, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must have had a header first! */ + if (pData->iFirstchunkadded == 0) + MNG_ERROR (pData, MNG_NOHEADER) + /* prevent misplaced TERM ! */ + if (!check_term (pData, iChunkname)) + MNG_ERROR (pData, MNG_TERMSEQERROR) + /* create the chunk */ +#ifndef MNG_OPTIMIZE_CHUNKREADER +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#else + iRetcode = mng_init_unknown (pData, &sChunkheader, &pChunk); +#endif +#else + mng_get_chunkheader(MNG_UINT_HUH, &sChunkheader); + iRetcode = mng_init_general (pData, &sChunkheader, &pChunk); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fill the chunk */ + ((mng_unknown_chunkp)pChunk)->sHeader.iChunkname = iChunkname; + ((mng_unknown_chunkp)pChunk)->iDatasize = iRawlen; + + if (iRawlen) + { + MNG_ALLOC (pData, ((mng_unknown_chunkp)pChunk)->pData, iRawlen); + MNG_COPY (((mng_unknown_chunkp)pChunk)->pData, pRawdata, iRawlen); + } + + mng_add_chunk (pData, pChunk); /* add it to the list */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTCHUNK_UNKNOWN, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_WRITE_PROCS */ + +/* ************************************************************************** */ +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_getimgdata_seq (mng_handle hHandle, + mng_uint32 iSeqnr, + mng_uint32 iCanvasstyle, + mng_getcanvasline fGetcanvasline) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETIMGDATA_SEQ, MNG_LC_START); +#endif + + + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETIMGDATA_SEQ, MNG_LC_END); +#endif + + return MNG_FNNOTIMPLEMENTED; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_getimgdata_chunkseq (mng_handle hHandle, + mng_uint32 iSeqnr, + mng_uint32 iCanvasstyle, + mng_getcanvasline fGetcanvasline) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETIMGDATA_CHUNKSEQ, MNG_LC_START); +#endif + + + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETIMGDATA_CHUNKSEQ, MNG_LC_END); +#endif + + return MNG_FNNOTIMPLEMENTED; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_getimgdata_chunk (mng_handle hHandle, + mng_handle hChunk, + mng_uint32 iCanvasstyle, + mng_getcanvasline fGetcanvasline) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETIMGDATA_CHUNK, MNG_LC_START); +#endif + + + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETIMGDATA_CHUNK, MNG_LC_END); +#endif + + return MNG_FNNOTIMPLEMENTED; +} + +/* ************************************************************************** */ +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_WRITE_PROCS + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_putimgdata_ihdr (mng_handle hHandle, + mng_uint32 iWidth, + mng_uint32 iHeight, + mng_uint8 iColortype, + mng_uint8 iBitdepth, + mng_uint8 iCompression, + mng_uint8 iFilter, + mng_uint8 iInterlace, + mng_uint32 iCanvasstyle, + mng_getcanvasline fGetcanvasline) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTIMGDATA_IHDR, MNG_LC_START); +#endif + + + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTIMGDATA_IHDR, MNG_LC_END); +#endif + + return MNG_FNNOTIMPLEMENTED; +} + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +mng_retcode MNG_DECL mng_putimgdata_jhdr (mng_handle hHandle, + mng_uint32 iWidth, + mng_uint32 iHeight, + mng_uint8 iColortype, + mng_uint8 iBitdepth, + mng_uint8 iCompression, + mng_uint8 iInterlace, + mng_uint8 iAlphaBitdepth, + mng_uint8 iAlphaCompression, + mng_uint8 iAlphaFilter, + mng_uint8 iAlphaInterlace, + mng_uint32 iCanvasstyle, + mng_getcanvasline fGetcanvasline) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTIMGDATA_JHDR, MNG_LC_START); +#endif + + + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_PUTIMGDATA_JHDR, MNG_LC_END); +#endif + + return MNG_FNNOTIMPLEMENTED; +} +#endif + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_updatemngheader (mng_handle hHandle, + mng_uint32 iFramecount, + mng_uint32 iLayercount, + mng_uint32 iPlaytime) +{ + mng_datap pData; + mng_chunkp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_UPDATEMNGHEADER, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must be a MNG animation! */ + if ((pData->eImagetype != mng_it_mng) || (pData->iFirstchunkadded != MNG_UINT_MHDR)) + MNG_ERROR (pData, MNG_NOMHDR) + + pChunk = pData->pFirstchunk; /* get the first chunk */ + /* and update the variables */ + ((mng_mhdrp)pChunk)->iFramecount = iFramecount; + ((mng_mhdrp)pChunk)->iLayercount = iLayercount; + ((mng_mhdrp)pChunk)->iPlaytime = iPlaytime; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_UPDATEMNGHEADER, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_updatemngsimplicity (mng_handle hHandle, + mng_uint32 iSimplicity) +{ + mng_datap pData; + mng_chunkp pChunk; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_UPDATEMNGSIMPLICITY, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = (mng_datap)hHandle; /* and make it addressable */ + + if (!pData->bCreating) /* aren't we creating a new file ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID) + /* must be a MNG animation! */ + if ((pData->eImagetype != mng_it_mng) || (pData->iFirstchunkadded != MNG_UINT_MHDR)) + MNG_ERROR (pData, MNG_NOMHDR) + + pChunk = pData->pFirstchunk; /* get the first chunk */ + /* and update the variable */ + ((mng_mhdrp)pChunk)->iSimplicity = iSimplicity; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_UPDATEMNGSIMPLICITY, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_WRITE_PROCS */ + +/* ************************************************************************** */ + +#endif /* MNG_ACCESS_CHUNKS */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ + + + diff --git a/Engine/lib/lmng/libmng_chunks.h b/Engine/lib/lmng/libmng_chunks.h new file mode 100644 index 000000000..d10bf2dc7 --- /dev/null +++ b/Engine/lib/lmng/libmng_chunks.h @@ -0,0 +1,1026 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_chunks.h copyright (c) 2000-2007 G.Juyn * */ +/* * version : 1.0.10 * */ +/* * * */ +/* * purpose : Chunk structures (definition) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : Definition of known chunk structures * */ +/* * * */ +/* * changes : 0.5.1 - 05/04/2000 - G.Juyn * */ +/* * - put in some extra comments * */ +/* * 0.5.1 - 05/06/2000 - G.Juyn * */ +/* * - fixed layout for sBIT, PPLT * */ +/* * 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed write callback definition * */ +/* * - changed strict-ANSI stuff * */ +/* * 0.5.1 - 05/11/2000 - G.Juyn * */ +/* * - fixed layout for PPLT again (missed deltatype ?!?) * */ +/* * * */ +/* * 0.5.2 - 05/31/2000 - G.Juyn * */ +/* * - removed useless definition (contributed by Tim Rowley) * */ +/* * 0.5.2 - 06/03/2000 - G.Juyn * */ +/* * - fixed makeup for Linux gcc compile * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 0.9.3 - 08/26/2000 - G.Juyn * */ +/* * - added MAGN chunk * */ +/* * 0.9.3 - 09/10/2000 - G.Juyn * */ +/* * - fixed DEFI behavior * */ +/* * 0.9.3 - 10/16/2000 - G.Juyn * */ +/* * - added JDAA chunk * */ +/* * * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - added HLAPI function to copy chunks * */ +/* * 1.0.5 - 09/14/2002 - G.Juyn * */ +/* * - added event handling for dynamic MNG * */ +/* * 1.0.5 - 11/28/2002 - G.Juyn * */ +/* * - fixed definition of iMethodX/Y for MAGN chunk * */ +/* * * */ +/* * 1.0.6 - 05/25/2003 - G.R-P * */ +/* * added MNG_SKIPCHUNK_cHNK footprint optimizations * */ +/* * 1.0.6 - 07/29/2003 - G.R-P * */ +/* * - added conditionals around PAST chunk support * */ +/* * * */ +/* * 1.0.7 - 03/24/2004 - G.R-P * */ +/* * - added conditional around MNG_NO_DELTA_PNG support * */ +/* * * */ +/* * 1.0.9 - 12/05/2004 - G.Juyn * */ +/* * - added conditional MNG_OPTIMIZE_CHUNKINITFREE * */ +/* * 1.0.9 - 12/06/2004 - G.Juyn * */ +/* * - added conditional MNG_OPTIMIZE_CHUNKREADER * */ +/* * * */ +/* * 1.0.10 - 04/08/2007 - G.Juyn * */ +/* * - added support for mPNG proposal * */ +/* * 1.0.10 - 04/12/2007 - G.Juyn * */ +/* * - added support for ANG proposal * */ +/* * * */ +/* ************************************************************************** */ + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +#ifndef _libmng_chunks_h_ +#define _libmng_chunks_h_ + +/* ************************************************************************** */ + +#ifdef MNG_SWAP_ENDIAN +#define PNG_SIG 0x474e5089L +#define JNG_SIG 0x474e4a8bL +#define MNG_SIG 0x474e4d8aL +#define POST_SIG 0x0a1a0a0dL +#else +#define PNG_SIG 0x89504e47L +#define JNG_SIG 0x8b4a4e47L +#define MNG_SIG 0x8a4d4e47L +#define POST_SIG 0x0d0a1a0aL +#endif + +/* ************************************************************************** */ + +#ifdef MNG_OPTIMIZE_CHUNKREADER + +typedef mng_retcode (*mng_f_specialfunc) (mng_datap pData, + mng_chunkp pChunk, + mng_uint32* piRawlen, + mng_uint8p* ppRawdata); + +typedef mng_retcode (*mng_c_specialfunc) (mng_datap pData, + mng_chunkp pChunk); + +#define MNG_FIELD_OPTIONAL 0x0001 +#define MNG_FIELD_TERMINATOR 0x0002 +#define MNG_FIELD_REPETITIVE 0x0004 +#define MNG_FIELD_DEFLATED 0x0008 +#define MNG_FIELD_IFIMGTYPES 0x01F0 /* image-type mask */ +#define MNG_FIELD_IFIMGTYPE0 0x0010 +#define MNG_FIELD_IFIMGTYPE2 0x0020 +#define MNG_FIELD_IFIMGTYPE3 0x0040 +#define MNG_FIELD_IFIMGTYPE4 0x0080 +#define MNG_FIELD_IFIMGTYPE6 0x0100 +#define MNG_FIELD_PUTIMGTYPE 0x0200 +#define MNG_FIELD_NOHIGHBIT 0x0400 +#define MNG_FIELD_GROUPMASK 0x7000 +#define MNG_FIELD_GROUP1 0x1000 +#define MNG_FIELD_GROUP2 0x2000 +#define MNG_FIELD_GROUP3 0x3000 +#define MNG_FIELD_GROUP4 0x4000 +#define MNG_FIELD_GROUP5 0x5000 +#define MNG_FIELD_GROUP6 0x6000 +#define MNG_FIELD_GROUP7 0x7000 +#define MNG_FIELD_INT 0x8000 + +typedef struct { /* chunk-field descriptor */ + mng_f_specialfunc pSpecialfunc; + mng_uint16 iFlags; + mng_uint16 iMinvalue; + mng_uint16 iMaxvalue; + mng_uint16 iLengthmin; + mng_uint16 iLengthmax; + mng_uint16 iOffsetchunk; + mng_uint16 iOffsetchunkind; + mng_uint16 iOffsetchunklen; + } mng_field_descriptor; +typedef mng_field_descriptor * mng_field_descp; + +#define MNG_DESCR_GLOBAL 0x0001 +#define MNG_DESCR_EMPTY 0x0002 +#define MNG_DESCR_EMPTYEMBED 0x0006 +#define MNG_DESCR_EMPTYGLOBAL 0x000A + +#define MNG_DESCR_GenHDR 0x0001 /* IHDR/JHDR/BASI/DHDR */ +#define MNG_DESCR_JngHDR 0x0002 /* JHDR/DHDR */ +#define MNG_DESCR_MHDR 0x0004 +#define MNG_DESCR_IHDR 0x0008 +#define MNG_DESCR_JHDR 0x0010 +#define MNG_DESCR_DHDR 0x0020 +#define MNG_DESCR_LOOP 0x0040 +#define MNG_DESCR_PLTE 0x0080 +#define MNG_DESCR_SAVE 0x0100 + +#define MNG_DESCR_NOIHDR 0x0001 +#define MNG_DESCR_NOJHDR 0x0002 +#define MNG_DESCR_NOBASI 0x0004 +#define MNG_DESCR_NODHDR 0x0008 +#define MNG_DESCR_NOIDAT 0x0010 +#define MNG_DESCR_NOJDAT 0x0020 +#define MNG_DESCR_NOJDAA 0x0040 +#define MNG_DESCR_NOPLTE 0x0080 +#define MNG_DESCR_NOJSEP 0x0100 +#define MNG_DESCR_NOMHDR 0x0200 +#define MNG_DESCR_NOTERM 0x0400 +#define MNG_DESCR_NOLOOP 0x0800 +#define MNG_DESCR_NOSAVE 0x1000 + +typedef struct { /* chunk descriptor */ + mng_imgtype eImgtype; + mng_createobjtype eCreateobject; + mng_uint16 iObjsize; + mng_uint16 iOffsetempty; + mng_ptr pObjcleanup; + mng_ptr pObjprocess; + mng_c_specialfunc pSpecialfunc; + mng_field_descp pFielddesc; + mng_uint16 iFielddesc; + mng_uint16 iAllowed; + mng_uint16 iMusthaves; + mng_uint16 iMustNOThaves; + } mng_chunk_descriptor; +typedef mng_chunk_descriptor * mng_chunk_descp; + +#endif /* MNG_OPTIMIZE_CHUNKREADER */ + +/* ************************************************************************** */ + +typedef mng_retcode (*mng_createchunk) (mng_datap pData, + mng_chunkp pHeader, + mng_chunkp* ppChunk); + +typedef mng_retcode (*mng_cleanupchunk) (mng_datap pData, + mng_chunkp pHeader); + +typedef mng_retcode (*mng_readchunk) (mng_datap pData, + mng_chunkp pHeader, + mng_uint32 iRawlen, + mng_uint8p pRawdata, + mng_chunkp* pChunk); + +typedef mng_retcode (*mng_writechunk) (mng_datap pData, + mng_chunkp pChunk); + +typedef mng_retcode (*mng_assignchunk) (mng_datap pData, + mng_chunkp pChunkto, + mng_chunkp pChunkfrom); + +/* ************************************************************************** */ + +typedef struct { /* generic header */ + mng_chunkid iChunkname; + mng_createchunk fCreate; + mng_cleanupchunk fCleanup; + mng_readchunk fRead; + mng_writechunk fWrite; + mng_assignchunk fAssign; + mng_chunkp pNext; /* for double-linked list */ + mng_chunkp pPrev; +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + mng_size_t iChunksize; +#endif +#ifdef MNG_OPTIMIZE_CHUNKREADER + mng_chunk_descp pChunkdescr; +#endif + } mng_chunk_header; +typedef mng_chunk_header * mng_chunk_headerp; + +/* ************************************************************************** */ + +typedef struct { /* IHDR */ + mng_chunk_header sHeader; + mng_uint32 iWidth; + mng_uint32 iHeight; + mng_uint8 iBitdepth; + mng_uint8 iColortype; + mng_uint8 iCompression; + mng_uint8 iFilter; + mng_uint8 iInterlace; + } mng_ihdr; +typedef mng_ihdr * mng_ihdrp; + +/* ************************************************************************** */ + +typedef struct { /* PLTE */ + mng_chunk_header sHeader; + mng_bool bEmpty; + mng_uint32 iEntrycount; + mng_rgbpaltab aEntries; + } mng_plte; +typedef mng_plte * mng_pltep; + +/* ************************************************************************** */ + +typedef struct { /* IDAT */ + mng_chunk_header sHeader; + mng_bool bEmpty; + mng_uint32 iDatasize; + mng_ptr pData; + } mng_idat; +typedef mng_idat * mng_idatp; + +/* ************************************************************************** */ + +typedef struct { /* IEND */ + mng_chunk_header sHeader; + } mng_iend; +typedef mng_iend * mng_iendp; + +/* ************************************************************************** */ + +typedef struct { /* tRNS */ + mng_chunk_header sHeader; + mng_bool bEmpty; + mng_bool bGlobal; + mng_uint8 iType; /* colortype (0,2,3) */ + mng_uint32 iCount; + mng_uint8arr aEntries; + mng_uint16 iGray; + mng_uint16 iRed; + mng_uint16 iGreen; + mng_uint16 iBlue; + mng_uint32 iRawlen; + mng_uint8arr aRawdata; + } mng_trns; +typedef mng_trns * mng_trnsp; + +/* ************************************************************************** */ + +typedef struct { /* gAMA */ + mng_chunk_header sHeader; + mng_bool bEmpty; + mng_uint32 iGamma; + } mng_gama; +typedef mng_gama * mng_gamap; + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_cHRM +typedef struct { /* cHRM */ + mng_chunk_header sHeader; + mng_bool bEmpty; + mng_uint32 iWhitepointx; + mng_uint32 iWhitepointy; + mng_uint32 iRedx; + mng_uint32 iRedy; + mng_uint32 iGreenx; + mng_uint32 iGreeny; + mng_uint32 iBluex; + mng_uint32 iBluey; + } mng_chrm; +typedef mng_chrm * mng_chrmp; +#endif + +/* ************************************************************************** */ + +typedef struct { /* sRGB */ + mng_chunk_header sHeader; + mng_bool bEmpty; + mng_uint8 iRenderingintent; + } mng_srgb; +typedef mng_srgb * mng_srgbp; + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_iCCP +typedef struct { /* iCCP */ + mng_chunk_header sHeader; + mng_bool bEmpty; + mng_uint32 iNamesize; + mng_pchar zName; + mng_uint8 iCompression; + mng_uint32 iProfilesize; + mng_ptr pProfile; + } mng_iccp; +typedef mng_iccp * mng_iccpp; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_tEXt +typedef struct { /* tEXt */ + mng_chunk_header sHeader; + mng_uint32 iKeywordsize; + mng_pchar zKeyword; + mng_uint32 iTextsize; + mng_pchar zText; + } mng_text; +typedef mng_text * mng_textp; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_zTXt +typedef struct { /* zTXt */ + mng_chunk_header sHeader; + mng_uint32 iKeywordsize; + mng_pchar zKeyword; + mng_uint8 iCompression; + mng_uint32 iTextsize; + mng_pchar zText; + } mng_ztxt; +typedef mng_ztxt * mng_ztxtp; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_iTXt +typedef struct { /* iTXt */ + mng_chunk_header sHeader; + mng_uint32 iKeywordsize; + mng_pchar zKeyword; + mng_uint8 iCompressionflag; + mng_uint8 iCompressionmethod; + mng_uint32 iLanguagesize; + mng_pchar zLanguage; + mng_uint32 iTranslationsize; + mng_pchar zTranslation; + mng_uint32 iTextsize; + mng_pchar zText; + } mng_itxt; +typedef mng_itxt * mng_itxtp; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_bKGD +typedef struct { /* bKGD */ + mng_chunk_header sHeader; + mng_bool bEmpty; + mng_uint8 iType; /* 3=indexed, 0=gray, 2=rgb */ + mng_uint8 iIndex; + mng_uint16 iGray; + mng_uint16 iRed; + mng_uint16 iGreen; + mng_uint16 iBlue; + } mng_bkgd; +typedef mng_bkgd * mng_bkgdp; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_pHYs +typedef struct { /* pHYs */ + mng_chunk_header sHeader; + mng_bool bEmpty; + mng_uint32 iSizex; + mng_uint32 iSizey; + mng_uint8 iUnit; + } mng_phys; +typedef mng_phys * mng_physp; +#endif + +/* ************************************************************************** */ +#ifndef MNG_SKIPCHUNK_sBIT + +typedef struct { /* sBIT */ + mng_chunk_header sHeader; + mng_bool bEmpty; + mng_uint8 iType; /* colortype (0,2,3,4,6,10,12,14,16) */ + mng_uint8arr4 aBits; + } mng_sbit; +typedef mng_sbit * mng_sbitp; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_sPLT +typedef struct { /* sPLT */ + mng_chunk_header sHeader; + mng_bool bEmpty; + mng_uint32 iNamesize; + mng_pchar zName; + mng_uint8 iSampledepth; + mng_uint32 iEntrycount; + mng_ptr pEntries; + } mng_splt; +typedef mng_splt * mng_spltp; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_hIST +typedef struct { /* hIST */ + mng_chunk_header sHeader; + mng_uint32 iEntrycount; + mng_uint16arr aEntries; + } mng_hist; +typedef mng_hist * mng_histp; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_tIME +typedef struct { /* tIME */ + mng_chunk_header sHeader; + mng_uint16 iYear; + mng_uint8 iMonth; + mng_uint8 iDay; + mng_uint8 iHour; + mng_uint8 iMinute; + mng_uint8 iSecond; + } mng_time; +typedef mng_time * mng_timep; +#endif + +/* ************************************************************************** */ + +typedef struct { /* MHDR */ + mng_chunk_header sHeader; + mng_uint32 iWidth; + mng_uint32 iHeight; + mng_uint32 iTicks; + mng_uint32 iLayercount; + mng_uint32 iFramecount; + mng_uint32 iPlaytime; + mng_uint32 iSimplicity; + } mng_mhdr; +typedef mng_mhdr * mng_mhdrp; + +/* ************************************************************************** */ + +typedef struct { /* MEND */ + mng_chunk_header sHeader; + } mng_mend; +typedef mng_mend * mng_mendp; + +/* ************************************************************************** */ + +typedef struct { /* LOOP */ + mng_chunk_header sHeader; + mng_uint8 iLevel; + mng_uint32 iRepeat; + mng_uint8 iTermination; + mng_uint32 iItermin; + mng_uint32 iItermax; + mng_uint32 iCount; + mng_uint32p pSignals; + } mng_loop; +typedef mng_loop * mng_loopp; + +/* ************************************************************************** */ + +typedef struct { /* ENDL */ + mng_chunk_header sHeader; + mng_uint8 iLevel; + } mng_endl; +typedef mng_endl * mng_endlp; + +/* ************************************************************************** */ + +typedef struct { /* DEFI */ + mng_chunk_header sHeader; + mng_uint16 iObjectid; + mng_bool bHasdonotshow; + mng_uint8 iDonotshow; + mng_bool bHasconcrete; + mng_uint8 iConcrete; + mng_bool bHasloca; + mng_int32 iXlocation; + mng_int32 iYlocation; + mng_bool bHasclip; + mng_int32 iLeftcb; + mng_int32 iRightcb; + mng_int32 iTopcb; + mng_int32 iBottomcb; + } mng_defi; +typedef mng_defi * mng_defip; + +/* ************************************************************************** */ + +typedef struct { /* BASI */ + mng_chunk_header sHeader; + mng_uint32 iWidth; + mng_uint32 iHeight; + mng_uint8 iBitdepth; + mng_uint8 iColortype; + mng_uint8 iCompression; + mng_uint8 iFilter; + mng_uint8 iInterlace; + mng_uint16 iRed; + mng_uint16 iGreen; + mng_uint16 iBlue; +#ifdef MNG_OPTIMIZE_CHUNKREADER + mng_bool bHasalpha; +#endif + mng_uint16 iAlpha; + mng_uint8 iViewable; + } mng_basi; +typedef mng_basi * mng_basip; + +/* ************************************************************************** */ + +typedef struct { /* CLON */ + mng_chunk_header sHeader; + mng_uint16 iSourceid; + mng_uint16 iCloneid; + mng_uint8 iClonetype; +#ifdef MNG_OPTIMIZE_CHUNKREADER + mng_bool bHasdonotshow; +#endif + mng_uint8 iDonotshow; + mng_uint8 iConcrete; + mng_bool bHasloca; + mng_uint8 iLocationtype; + mng_int32 iLocationx; + mng_int32 iLocationy; + } mng_clon; +typedef mng_clon * mng_clonp; + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_PAST +typedef struct { /* PAST source */ + mng_uint16 iSourceid; + mng_uint8 iComposition; + mng_uint8 iOrientation; + mng_uint8 iOffsettype; + mng_int32 iOffsetx; + mng_int32 iOffsety; + mng_uint8 iBoundarytype; + mng_int32 iBoundaryl; + mng_int32 iBoundaryr; + mng_int32 iBoundaryt; + mng_int32 iBoundaryb; + } mng_past_source; +typedef mng_past_source * mng_past_sourcep; + +typedef struct { /* PAST */ + mng_chunk_header sHeader; + mng_uint16 iDestid; + mng_uint8 iTargettype; + mng_int32 iTargetx; + mng_int32 iTargety; + mng_uint32 iCount; + mng_past_sourcep pSources; + } mng_past; +typedef mng_past * mng_pastp; +#endif + +/* ************************************************************************** */ + +typedef struct { /* DISC */ + mng_chunk_header sHeader; + mng_uint32 iCount; + mng_uint16p pObjectids; + } mng_disc; +typedef mng_disc * mng_discp; + +/* ************************************************************************** */ + +typedef struct { /* BACK */ + mng_chunk_header sHeader; + mng_uint16 iRed; + mng_uint16 iGreen; + mng_uint16 iBlue; + mng_uint8 iMandatory; + mng_uint16 iImageid; + mng_uint8 iTile; + } mng_back; +typedef mng_back * mng_backp; + +/* ************************************************************************** */ + +typedef struct { /* FRAM */ + mng_chunk_header sHeader; + mng_bool bEmpty; + mng_uint8 iMode; + mng_uint32 iNamesize; + mng_pchar zName; + mng_uint8 iChangedelay; + mng_uint8 iChangetimeout; + mng_uint8 iChangeclipping; + mng_uint8 iChangesyncid; + mng_uint32 iDelay; + mng_uint32 iTimeout; + mng_uint8 iBoundarytype; + mng_int32 iBoundaryl; + mng_int32 iBoundaryr; + mng_int32 iBoundaryt; + mng_int32 iBoundaryb; + mng_uint32 iCount; + mng_uint32p pSyncids; + } mng_fram; +typedef mng_fram * mng_framp; + +/* ************************************************************************** */ + +typedef struct { /* MOVE */ + mng_chunk_header sHeader; + mng_uint16 iFirstid; + mng_uint16 iLastid; + mng_uint8 iMovetype; + mng_int32 iMovex; + mng_int32 iMovey; + } mng_move; +typedef mng_move * mng_movep; + +/* ************************************************************************** */ + +typedef struct { /* CLIP */ + mng_chunk_header sHeader; + mng_uint16 iFirstid; + mng_uint16 iLastid; + mng_uint8 iCliptype; + mng_int32 iClipl; + mng_int32 iClipr; + mng_int32 iClipt; + mng_int32 iClipb; + } mng_clip; +typedef mng_clip * mng_clipp; + +/* ************************************************************************** */ + +typedef struct { /* SHOW */ + mng_chunk_header sHeader; + mng_bool bEmpty; + mng_uint16 iFirstid; +#ifdef MNG_OPTIMIZE_CHUNKREADER + mng_bool bHaslastid; +#endif + mng_uint16 iLastid; + mng_uint8 iMode; + } mng_show; +typedef mng_show * mng_showp; + +/* ************************************************************************** */ + +typedef struct { /* TERM */ + mng_chunk_header sHeader; + mng_uint8 iTermaction; + mng_uint8 iIteraction; + mng_uint32 iDelay; + mng_uint32 iItermax; + } mng_term; +typedef mng_term * mng_termp; + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SAVE +typedef struct { /* SAVE entry */ + mng_uint8 iEntrytype; + mng_uint32arr2 iOffset; /* 0=MSI, 1=LSI */ + mng_uint32arr2 iStarttime; /* 0=MSI, 1=LSI */ + mng_uint32 iLayernr; + mng_uint32 iFramenr; + mng_uint32 iNamesize; + mng_pchar zName; + } mng_save_entry; +typedef mng_save_entry * mng_save_entryp; + +typedef struct { /* SAVE */ + mng_chunk_header sHeader; + mng_bool bEmpty; + mng_uint8 iOffsettype; + mng_uint32 iCount; + mng_save_entryp pEntries; + } mng_save; +typedef mng_save * mng_savep; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SEEK +typedef struct { /* SEEK */ + mng_chunk_header sHeader; + mng_uint32 iNamesize; + mng_pchar zName; + } mng_seek; +typedef mng_seek * mng_seekp; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_eXPI +typedef struct { /* eXPI */ + mng_chunk_header sHeader; + mng_uint16 iSnapshotid; + mng_uint32 iNamesize; + mng_pchar zName; + } mng_expi; +typedef mng_expi * mng_expip; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_fPRI +typedef struct { /* fPRI */ + mng_chunk_header sHeader; + mng_uint8 iDeltatype; + mng_uint8 iPriority; + } mng_fpri; +typedef mng_fpri * mng_fprip; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_nEED +typedef struct { /* nEED */ + mng_chunk_header sHeader; + mng_uint32 iKeywordssize; + mng_pchar zKeywords; + } mng_need; +typedef mng_need * mng_needp; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_pHYg +typedef mng_phys mng_phyg; /* pHYg */ +typedef mng_phyg * mng_phygp; +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG + +typedef struct { /* JHDR */ + mng_chunk_header sHeader; + mng_uint32 iWidth; + mng_uint32 iHeight; + mng_uint8 iColortype; + mng_uint8 iImagesampledepth; + mng_uint8 iImagecompression; + mng_uint8 iImageinterlace; + mng_uint8 iAlphasampledepth; + mng_uint8 iAlphacompression; + mng_uint8 iAlphafilter; + mng_uint8 iAlphainterlace; + } mng_jhdr; +typedef mng_jhdr * mng_jhdrp; + +/* ************************************************************************** */ + +typedef mng_idat mng_jdaa; /* JDAA */ +typedef mng_jdaa * mng_jdaap; + +/* ************************************************************************** */ + +typedef mng_idat mng_jdat; /* JDAT */ +typedef mng_jdat * mng_jdatp; + +/* ************************************************************************** */ + +typedef struct { /* JSEP */ + mng_chunk_header sHeader; + } mng_jsep; +typedef mng_jsep * mng_jsepp; + +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG + +typedef struct { /* DHDR */ + mng_chunk_header sHeader; + mng_uint16 iObjectid; + mng_uint8 iImagetype; + mng_uint8 iDeltatype; +#ifdef MNG_OPTIMIZE_CHUNKREADER + mng_bool bHasblocksize; +#endif + mng_uint32 iBlockwidth; + mng_uint32 iBlockheight; +#ifdef MNG_OPTIMIZE_CHUNKREADER + mng_bool bHasblockloc; +#endif + mng_uint32 iBlockx; + mng_uint32 iBlocky; + } mng_dhdr; +typedef mng_dhdr * mng_dhdrp; + +/* ************************************************************************** */ + +typedef struct { /* PROM */ + mng_chunk_header sHeader; + mng_uint8 iColortype; + mng_uint8 iSampledepth; + mng_uint8 iFilltype; + } mng_prom; +typedef mng_prom * mng_promp; + +/* ************************************************************************** */ + +typedef struct { /* IPNG */ + mng_chunk_header sHeader; + } mng_ipng; +typedef mng_ipng *mng_ipngp; + +/* ************************************************************************** */ + +typedef struct { /* PPLT entry */ + mng_uint8 iRed; + mng_uint8 iGreen; + mng_uint8 iBlue; + mng_uint8 iAlpha; + mng_bool bUsed; + } mng_pplt_entry; +typedef mng_pplt_entry * mng_pplt_entryp; + +typedef struct { /* PPLT */ + mng_chunk_header sHeader; + mng_uint8 iDeltatype; + mng_uint32 iCount; + mng_pplt_entry aEntries [256]; + } mng_pplt; +typedef mng_pplt * mng_ppltp; + +/* ************************************************************************** */ + +typedef struct { /* IJNG */ + mng_chunk_header sHeader; + } mng_ijng; +typedef mng_ijng *mng_ijngp; + +/* ************************************************************************** */ + +typedef struct { /* DROP */ + mng_chunk_header sHeader; + mng_uint32 iCount; + mng_chunkidp pChunknames; + } mng_drop; +typedef mng_drop * mng_dropp; + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_DBYK +typedef struct { /* DBYK */ + mng_chunk_header sHeader; + mng_chunkid iChunkname; + mng_uint8 iPolarity; + mng_uint32 iKeywordssize; + mng_pchar zKeywords; + } mng_dbyk; +typedef mng_dbyk * mng_dbykp; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_ORDR +typedef struct { /* ORDR entry */ + mng_chunkid iChunkname; + mng_uint8 iOrdertype; + } mng_ordr_entry; +typedef mng_ordr_entry * mng_ordr_entryp; + +typedef struct mng_ordr_struct { /* ORDR */ + mng_chunk_header sHeader; + mng_uint32 iCount; + mng_ordr_entryp pEntries; + } mng_ordr; +typedef mng_ordr * mng_ordrp; +#endif +#endif /* MNG_NO_DELTA_PNG */ + +/* ************************************************************************** */ + +typedef struct { /* MAGN */ + mng_chunk_header sHeader; + mng_uint16 iFirstid; + mng_uint16 iLastid; + mng_uint8 iMethodX; + mng_uint16 iMX; + mng_uint16 iMY; + mng_uint16 iML; + mng_uint16 iMR; + mng_uint16 iMT; + mng_uint16 iMB; + mng_uint8 iMethodY; + } mng_magn; +typedef mng_magn * mng_magnp; + +/* ************************************************************************** */ + +typedef struct { /* evNT entry */ + mng_uint8 iEventtype; + mng_uint8 iMasktype; + mng_int32 iLeft; + mng_int32 iRight; + mng_int32 iTop; + mng_int32 iBottom; + mng_uint16 iObjectid; + mng_uint8 iIndex; + mng_uint32 iSegmentnamesize; + mng_pchar zSegmentname; + } mng_evnt_entry; +typedef mng_evnt_entry * mng_evnt_entryp; + +typedef struct { /* evNT */ + mng_chunk_header sHeader; + mng_uint32 iCount; + mng_evnt_entryp pEntries; + } mng_evnt; +typedef mng_evnt * mng_evntp; + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_MPNG_PROPOSAL +typedef struct { /* mpNG frame */ + mng_uint32 iX; + mng_uint32 iY; + mng_uint32 iWidth; + mng_uint32 iHeight; + mng_int32 iXoffset; + mng_int32 iYoffset; + mng_uint16 iTicks; + } mng_mpng_frame; +typedef mng_mpng_frame * mng_mpng_framep; + +typedef struct { /* mpNG */ + mng_chunk_header sHeader; + mng_uint32 iFramewidth; + mng_uint32 iFrameheight; + mng_uint16 iNumplays; + mng_uint16 iTickspersec; + mng_uint8 iCompressionmethod; + mng_uint32 iFramessize; + mng_mpng_framep pFrames; + } mng_mpng; +typedef mng_mpng * mng_mpngp; +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_ANG_PROPOSAL +typedef struct { /* ahDR */ + mng_chunk_header sHeader; + mng_uint32 iNumframes; + mng_uint32 iTickspersec; + mng_uint32 iNumplays; + mng_uint32 iTilewidth; + mng_uint32 iTileheight; + mng_uint8 iInterlace; + mng_uint8 iStillused; + } mng_ahdr; +typedef mng_ahdr * mng_ahdrp; + +typedef struct { /* adAT tile */ + mng_uint32 iTicks; + mng_int32 iXoffset; + mng_int32 iYoffset; + mng_uint8 iTilesource; + } mng_adat_tile; +typedef mng_adat_tile * mng_adat_tilep; + +typedef struct { /* adAT */ + mng_chunk_header sHeader; + mng_uint32 iTilessize; + mng_adat_tilep pTiles; + } mng_adat; +typedef mng_adat * mng_adatp; +#endif + +/* ************************************************************************** */ + +typedef struct { /* unknown chunk */ + mng_chunk_header sHeader; + mng_uint32 iDatasize; + mng_ptr pData; + } mng_unknown_chunk; +typedef mng_unknown_chunk * mng_unknown_chunkp; + +/* ************************************************************************** */ + +#endif /* _libmng_chunks_h_ */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ diff --git a/Engine/lib/lmng/libmng_cms.c b/Engine/lib/lmng/libmng_cms.c new file mode 100644 index 000000000..999575f66 --- /dev/null +++ b/Engine/lib/lmng/libmng_cms.c @@ -0,0 +1,758 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_cms.c copyright (c) 2000-2004 G.Juyn * */ +/* * version : 1.0.9 * */ +/* * * */ +/* * purpose : color management routines (implementation) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : implementation of the color management routines * */ +/* * * */ +/* * changes : 0.5.1 - 05/01/2000 - G.Juyn * */ +/* * - B001(105795) - fixed a typo and misconception about * */ +/* * freeing allocated gamma-table. (reported by Marti Maria) * */ +/* * 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed strict-ANSI stuff * */ +/* * 0.5.1 - 05/09/2000 - G.Juyn * */ +/* * - filled application-based color-management routines * */ +/* * 0.5.1 - 05/11/2000 - G.Juyn * */ +/* * - added creatememprofile * */ +/* * - added callback error-reporting support * */ +/* * 0.5.1 - 05/12/2000 - G.Juyn * */ +/* * - changed trace to macro for callback error-reporting * */ +/* * * */ +/* * 0.5.2 - 06/10/2000 - G.Juyn * */ +/* * - fixed some compilation-warnings (contrib Jason Morris) * */ +/* * * */ +/* * 0.5.3 - 06/21/2000 - G.Juyn * */ +/* * - fixed problem with color-correction for stored images * */ +/* * 0.5.3 - 06/23/2000 - G.Juyn * */ +/* * - fixed problem with incorrect gamma-correction * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 0.9.3 - 08/31/2000 - G.Juyn * */ +/* * - fixed sRGB precedence for gamma_only corection * */ +/* * * */ +/* * 0.9.4 - 12/16/2000 - G.Juyn * */ +/* * - fixed mixup of data- & function-pointers (thanks Dimitri)* */ +/* * * */ +/* * 1.0.1 - 03/31/2001 - G.Juyn * */ +/* * - ignore gamma=0 (see png-list for more info) * */ +/* * 1.0.1 - 04/25/2001 - G.Juyn (reported by Gregg Kelly) * */ +/* * - fixed problem with cms profile being created multiple * */ +/* * times when both iCCP & cHRM/gAMA are present * */ +/* * 1.0.1 - 04/25/2001 - G.Juyn * */ +/* * - moved mng_clear_cms to libmng_cms * */ +/* * 1.0.1 - 05/02/2001 - G.Juyn * */ +/* * - added "default" sRGB generation (Thanks Marti!) * */ +/* * * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * 1.0.5 - 09/19/2002 - G.Juyn * */ +/* * - optimized color-correction routines * */ +/* * 1.0.5 - 09/23/2002 - G.Juyn * */ +/* * - added in-memory color-correction of abstract images * */ +/* * 1.0.5 - 11/08/2002 - G.Juyn * */ +/* * - fixed issues in init_app_cms() * */ +/* * * */ +/* * 1.0.6 - 04/11/2003 - G.Juyn * */ +/* * - B719420 - fixed several MNG_APP_CMS problems * */ +/* * 1.0.6 - 07/11/2003 - G. R-P * */ +/* * - added conditional MNG_SKIPCHUNK_cHRM/iCCP * */ +/* * * */ +/* * 1.0.9 - 12/20/2004 - G.Juyn * */ +/* * - cleaned up macro-invocations (thanks to D. Airlie) * */ +/* * * */ +/* ************************************************************************** */ + +#include "libmng.h" +#include "libmng_data.h" +#include "libmng_error.h" +#include "libmng_trace.h" +#ifdef __BORLANDC__ +#pragma hdrstop +#endif +#include "libmng_objects.h" +#include "libmng_cms.h" + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_DISPLAY_PROCS + +/* ************************************************************************** */ +/* * * */ +/* * Little CMS helper routines * */ +/* * * */ +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_LCMS + +#define MNG_CMS_FLAGS 0 + +/* ************************************************************************** */ + +void mnglcms_initlibrary () +{ + cmsErrorAction (LCMS_ERROR_IGNORE); /* LCMS should ignore errors! */ +} + +/* ************************************************************************** */ + +mng_cmsprof mnglcms_createfileprofile (mng_pchar zFilename) +{ + return cmsOpenProfileFromFile (zFilename, "r"); +} + +/* ************************************************************************** */ + +mng_cmsprof mnglcms_creatememprofile (mng_uint32 iProfilesize, + mng_ptr pProfile) +{ + return cmsOpenProfileFromMem (pProfile, iProfilesize); +} + +/* ************************************************************************** */ + +mng_cmsprof mnglcms_createsrgbprofile (void) +{ + cmsCIExyY D65; + cmsCIExyYTRIPLE Rec709Primaries = { + {0.6400, 0.3300, 1.0}, + {0.3000, 0.6000, 1.0}, + {0.1500, 0.0600, 1.0} + }; + LPGAMMATABLE Gamma24[3]; + mng_cmsprof hsRGB; + + cmsWhitePointFromTemp(6504, &D65); + Gamma24[0] = Gamma24[1] = Gamma24[2] = cmsBuildGamma(256, 2.4); + hsRGB = cmsCreateRGBProfile(&D65, &Rec709Primaries, Gamma24); + cmsFreeGamma(Gamma24[0]); + + return hsRGB; +} + +/* ************************************************************************** */ + +void mnglcms_freeprofile (mng_cmsprof hProf) +{ + cmsCloseProfile (hProf); + return; +} + +/* ************************************************************************** */ + +void mnglcms_freetransform (mng_cmstrans hTrans) +{ +/* B001 start */ + cmsDeleteTransform (hTrans); +/* B001 end */ + return; +} + +/* ************************************************************************** */ + +mng_retcode mng_clear_cms (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CLEAR_CMS, MNG_LC_START); +#endif + + if (pData->hTrans) /* transformation still active ? */ + mnglcms_freetransform (pData->hTrans); + + pData->hTrans = 0; + + if (pData->hProf1) /* file profile still active ? */ + mnglcms_freeprofile (pData->hProf1); + + pData->hProf1 = 0; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CLEAR_CMS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_LCMS */ + +/* ************************************************************************** */ +/* * * */ +/* * Color-management initialization & correction routines * */ +/* * * */ +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_LCMS + +mng_retcode mng_init_full_cms (mng_datap pData, + mng_bool bGlobal, + mng_bool bObject, + mng_bool bRetrobj) +{ + mng_cmsprof hProf; + mng_cmstrans hTrans; + mng_imagep pImage = MNG_NULL; + mng_imagedatap pBuf = MNG_NULL; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_FULL_CMS, MNG_LC_START); +#endif + + if (bObject) /* use object if present ? */ + { /* current object ? */ + if ((mng_imagep)pData->pCurrentobj) + pImage = (mng_imagep)pData->pCurrentobj; + else /* if not; use object 0 */ + pImage = (mng_imagep)pData->pObjzero; + } + + if (bRetrobj) /* retrieving from an object ? */ + pImage = (mng_imagep)pData->pRetrieveobj; + + if (pImage) /* are we using an object ? */ + pBuf = pImage->pImgbuf; /* then address the buffer */ + + if ((!pBuf) || (!pBuf->bCorrected)) /* is the buffer already corrected ? */ + { +#ifndef MNG_SKIPCHUNK_iCCP + if (((pBuf) && (pBuf->bHasICCP)) || ((bGlobal) && (pData->bHasglobalICCP))) + { + if (!pData->hProf2) /* output profile not defined ? */ + { /* then assume sRGB !! */ + pData->hProf2 = mnglcms_createsrgbprofile (); + + if (!pData->hProf2) /* handle error ? */ + MNG_ERRORL (pData, MNG_LCMS_NOHANDLE); + } + + if ((pBuf) && (pBuf->bHasICCP)) /* generate a profile handle */ + hProf = cmsOpenProfileFromMem (pBuf->pProfile, pBuf->iProfilesize); + else + hProf = cmsOpenProfileFromMem (pData->pGlobalProfile, pData->iGlobalProfilesize); + + pData->hProf1 = hProf; /* save for future use */ + + if (!hProf) /* handle error ? */ + MNG_ERRORL (pData, MNG_LCMS_NOHANDLE); + +#ifndef MNG_NO_16BIT_SUPPORT + if (pData->bIsRGBA16) /* 16-bit intermediates ? */ + hTrans = cmsCreateTransform (hProf, TYPE_RGBA_16_SE, + pData->hProf2, TYPE_RGBA_16_SE, + INTENT_PERCEPTUAL, MNG_CMS_FLAGS); + else +#endif + hTrans = cmsCreateTransform (hProf, TYPE_RGBA_8, + pData->hProf2, TYPE_RGBA_8, + INTENT_PERCEPTUAL, MNG_CMS_FLAGS); + + pData->hTrans = hTrans; /* save for future use */ + + if (!hTrans) /* handle error ? */ + MNG_ERRORL (pData, MNG_LCMS_NOTRANS); + /* load color-correction routine */ + pData->fCorrectrow = (mng_fptr)mng_correct_full_cms; + + return MNG_NOERROR; /* and done */ + } + else +#endif + if (((pBuf) && (pBuf->bHasSRGB)) || ((bGlobal) && (pData->bHasglobalSRGB))) + { + mng_uint8 iIntent; + + if (pData->bIssRGB) /* sRGB system ? */ + return MNG_NOERROR; /* no conversion required */ + + if (!pData->hProf3) /* sRGB profile not defined ? */ + { /* then create it implicitly !! */ + pData->hProf3 = mnglcms_createsrgbprofile (); + + if (!pData->hProf3) /* handle error ? */ + MNG_ERRORL (pData, MNG_LCMS_NOHANDLE); + } + + hProf = pData->hProf3; /* convert from sRGB profile */ + + if ((pBuf) && (pBuf->bHasSRGB)) /* determine rendering intent */ + iIntent = pBuf->iRenderingintent; + else + iIntent = pData->iGlobalRendintent; + + if (pData->bIsRGBA16) /* 16-bit intermediates ? */ + hTrans = cmsCreateTransform (hProf, TYPE_RGBA_16_SE, + pData->hProf2, TYPE_RGBA_16_SE, + iIntent, MNG_CMS_FLAGS); + else + hTrans = cmsCreateTransform (hProf, TYPE_RGBA_8, + pData->hProf2, TYPE_RGBA_8, + iIntent, MNG_CMS_FLAGS); + + pData->hTrans = hTrans; /* save for future use */ + + if (!hTrans) /* handle error ? */ + MNG_ERRORL (pData, MNG_LCMS_NOTRANS); + /* load color-correction routine */ + pData->fCorrectrow = (mng_fptr)mng_correct_full_cms; + + return MNG_NOERROR; /* and done */ + } + else + if ( (((pBuf) && (pBuf->bHasCHRM)) || ((bGlobal) && (pData->bHasglobalCHRM))) && + ( ((pBuf) && (pBuf->bHasGAMA) && (pBuf->iGamma > 0)) || + ((bGlobal) && (pData->bHasglobalGAMA) && (pData->iGlobalGamma > 0)) ) ) + { + mng_CIExyY sWhitepoint; + mng_CIExyYTRIPLE sPrimaries; + mng_gammatabp pGammatable[3]; + mng_float dGamma; + + if (!pData->hProf2) /* output profile not defined ? */ + { /* then assume sRGB !! */ + pData->hProf2 = mnglcms_createsrgbprofile (); + + if (!pData->hProf2) /* handle error ? */ + MNG_ERRORL (pData, MNG_LCMS_NOHANDLE); + } + +#ifndef MNG_SKIPCHUNK_cHRM + if ((pBuf) && (pBuf->bHasCHRM)) /* local cHRM ? */ + { + sWhitepoint.x = (mng_float)pBuf->iWhitepointx / 100000; + sWhitepoint.y = (mng_float)pBuf->iWhitepointy / 100000; + sPrimaries.Red.x = (mng_float)pBuf->iPrimaryredx / 100000; + sPrimaries.Red.y = (mng_float)pBuf->iPrimaryredy / 100000; + sPrimaries.Green.x = (mng_float)pBuf->iPrimarygreenx / 100000; + sPrimaries.Green.y = (mng_float)pBuf->iPrimarygreeny / 100000; + sPrimaries.Blue.x = (mng_float)pBuf->iPrimarybluex / 100000; + sPrimaries.Blue.y = (mng_float)pBuf->iPrimarybluey / 100000; + } + else + { + sWhitepoint.x = (mng_float)pData->iGlobalWhitepointx / 100000; + sWhitepoint.y = (mng_float)pData->iGlobalWhitepointy / 100000; + sPrimaries.Red.x = (mng_float)pData->iGlobalPrimaryredx / 100000; + sPrimaries.Red.y = (mng_float)pData->iGlobalPrimaryredy / 100000; + sPrimaries.Green.x = (mng_float)pData->iGlobalPrimarygreenx / 100000; + sPrimaries.Green.y = (mng_float)pData->iGlobalPrimarygreeny / 100000; + sPrimaries.Blue.x = (mng_float)pData->iGlobalPrimarybluex / 100000; + sPrimaries.Blue.y = (mng_float)pData->iGlobalPrimarybluey / 100000; + } +#endif + + sWhitepoint.Y = /* Y component is always 1.0 */ + sPrimaries.Red.Y = + sPrimaries.Green.Y = + sPrimaries.Blue.Y = 1.0; + + if ((pBuf) && (pBuf->bHasGAMA)) /* get the gamma value */ + dGamma = (mng_float)pBuf->iGamma / 100000; + else + dGamma = (mng_float)pData->iGlobalGamma / 100000; + + dGamma = pData->dViewgamma / dGamma; + + pGammatable [0] = /* and build the lookup tables */ + pGammatable [1] = + pGammatable [2] = cmsBuildGamma (256, dGamma); + + if (!pGammatable [0]) /* enough memory ? */ + MNG_ERRORL (pData, MNG_LCMS_NOMEM); + /* create the profile */ + hProf = cmsCreateRGBProfile (&sWhitepoint, &sPrimaries, pGammatable); + + cmsFreeGamma (pGammatable [0]); /* free the temporary gamma tables ? */ + /* yes! but just the one! */ + + pData->hProf1 = hProf; /* save for future use */ + + if (!hProf) /* handle error ? */ + MNG_ERRORL (pData, MNG_LCMS_NOHANDLE); + + if (pData->bIsRGBA16) /* 16-bit intermediates ? */ + hTrans = cmsCreateTransform (hProf, TYPE_RGBA_16_SE, + pData->hProf2, TYPE_RGBA_16_SE, + INTENT_PERCEPTUAL, MNG_CMS_FLAGS); + else + hTrans = cmsCreateTransform (hProf, TYPE_RGBA_8, + pData->hProf2, TYPE_RGBA_8, + INTENT_PERCEPTUAL, MNG_CMS_FLAGS); + + pData->hTrans = hTrans; /* save for future use */ + + if (!hTrans) /* handle error ? */ + MNG_ERRORL (pData, MNG_LCMS_NOTRANS); + /* load color-correction routine */ + pData->fCorrectrow = (mng_fptr)mng_correct_full_cms; + + return MNG_NOERROR; /* and done */ + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_FULL_CMS, MNG_LC_END); +#endif + /* if we get here, we'll only do gamma */ + return mng_init_gamma_only (pData, bGlobal, bObject, bRetrobj); +} +#endif /* MNG_INCLUDE_LCMS */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_LCMS +mng_retcode mng_correct_full_cms (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CORRECT_FULL_CMS, MNG_LC_START); +#endif + + cmsDoTransform (pData->hTrans, pData->pRGBArow, pData->pRGBArow, pData->iRowsamples); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CORRECT_FULL_CMS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_INCLUDE_LCMS */ + +/* ************************************************************************** */ + +#if defined(MNG_GAMMA_ONLY) || defined(MNG_FULL_CMS) || defined(MNG_APP_CMS) +mng_retcode mng_init_gamma_only (mng_datap pData, + mng_bool bGlobal, + mng_bool bObject, + mng_bool bRetrobj) +{ + mng_float dGamma; + mng_imagep pImage = MNG_NULL; + mng_imagedatap pBuf = MNG_NULL; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_GAMMA_ONLY, MNG_LC_START); +#endif + + if (bObject) /* use object if present ? */ + { /* current object ? */ + if ((mng_imagep)pData->pCurrentobj) + pImage = (mng_imagep)pData->pCurrentobj; + else /* if not; use object 0 */ + pImage = (mng_imagep)pData->pObjzero; + } + + if (bRetrobj) /* retrieving from an object ? */ + pImage = (mng_imagep)pData->pRetrieveobj; + + if (pImage) /* are we using an object ? */ + pBuf = pImage->pImgbuf; /* then address the buffer */ + + if ((!pBuf) || (!pBuf->bCorrected)) /* is the buffer already corrected ? */ + { + if ((pBuf) && (pBuf->bHasSRGB)) /* get the gamma value */ + dGamma = 0.45455; + else + if ((pBuf) && (pBuf->bHasGAMA)) + dGamma = (mng_float)pBuf->iGamma / 100000; + else + if ((bGlobal) && (pData->bHasglobalSRGB)) + dGamma = 0.45455; + else + if ((bGlobal) && (pData->bHasglobalGAMA)) + dGamma = (mng_float)pData->iGlobalGamma / 100000; + else + dGamma = pData->dDfltimggamma; + + if (dGamma > 0) /* ignore gamma=0 */ + { + dGamma = pData->dViewgamma / (dGamma * pData->dDisplaygamma); + + if (dGamma != pData->dLastgamma) /* lookup table needs to be computed ? */ + { + mng_int32 iX; + + pData->aGammatab [0] = 0; + + for (iX = 1; iX <= 255; iX++) + pData->aGammatab [iX] = (mng_uint8)(pow (iX / 255.0, dGamma) * 255 + 0.5); + + pData->dLastgamma = dGamma; /* keep for next time */ + } + /* load color-correction routine */ + pData->fCorrectrow = (mng_fptr)mng_correct_gamma_only; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_GAMMA_ONLY, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_GAMMA_ONLY || MNG_FULL_CMS || MNG_APP_CMS */ + +/* ************************************************************************** */ + +#if defined(MNG_GAMMA_ONLY) || defined(MNG_FULL_CMS) || defined(MNG_APP_CMS) +mng_retcode mng_correct_gamma_only (mng_datap pData) +{ + mng_uint8p pWork; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CORRECT_GAMMA_ONLY, MNG_LC_START); +#endif + + pWork = pData->pRGBArow; /* address intermediate row */ + + if (pData->bIsRGBA16) /* 16-bit intermediate row ? */ + { + + + /* TODO: 16-bit precision gamma processing */ + /* we'll just do the high-order byte for now */ + + + /* convert all samples in the row */ + for (iX = 0; iX < pData->iRowsamples; iX++) + { /* using the precalculated gamma lookup table */ + *pWork = pData->aGammatab [*pWork]; + *(pWork+2) = pData->aGammatab [*(pWork+2)]; + *(pWork+4) = pData->aGammatab [*(pWork+4)]; + + pWork += 8; + } + } + else + { /* convert all samples in the row */ + for (iX = 0; iX < pData->iRowsamples; iX++) + { /* using the precalculated gamma lookup table */ + *pWork = pData->aGammatab [*pWork]; + *(pWork+1) = pData->aGammatab [*(pWork+1)]; + *(pWork+2) = pData->aGammatab [*(pWork+2)]; + + pWork += 4; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CORRECT_GAMMA_ONLY, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_GAMMA_ONLY || MNG_FULL_CMS || MNG_APP_CMS */ + +/* ************************************************************************** */ + +#ifdef MNG_APP_CMS +mng_retcode mng_init_app_cms (mng_datap pData, + mng_bool bGlobal, + mng_bool bObject, + mng_bool bRetrobj) +{ + mng_imagep pImage = MNG_NULL; + mng_imagedatap pBuf = MNG_NULL; + mng_bool bDone = MNG_FALSE; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_APP_CMS, MNG_LC_START); +#endif + + if (bObject) /* use object if present ? */ + { /* current object ? */ + if ((mng_imagep)pData->pCurrentobj) + pImage = (mng_imagep)pData->pCurrentobj; + else /* if not; use object 0 */ + pImage = (mng_imagep)pData->pObjzero; + } + + if (bRetrobj) /* retrieving from an object ? */ + pImage = (mng_imagep)pData->pRetrieveobj; + + if (pImage) /* are we using an object ? */ + pBuf = pImage->pImgbuf; /* then address the buffer */ + + if ((!pBuf) || (!pBuf->bCorrected)) /* is the buffer already corrected ? */ + { +#ifndef MNG_SKIPCHUNK_iCCP + if ( (pData->fProcessiccp) && + (((pBuf) && (pBuf->bHasICCP)) || ((bGlobal) && (pData->bHasglobalICCP))) ) + { + mng_uint32 iProfilesize; + mng_ptr pProfile; + + if ((pBuf) && (pBuf->bHasICCP)) /* get the right profile */ + { + iProfilesize = pBuf->iProfilesize; + pProfile = pBuf->pProfile; + } + else + { + iProfilesize = pData->iGlobalProfilesize; + pProfile = pData->pGlobalProfile; + } + /* inform the app */ + if (!pData->fProcessiccp ((mng_handle)pData, iProfilesize, pProfile)) + MNG_ERROR (pData, MNG_APPCMSERROR); + /* load color-correction routine */ + pData->fCorrectrow = (mng_fptr)mng_correct_app_cms; + bDone = MNG_TRUE; + } +#endif + + if ( (pData->fProcesssrgb) && + (((pBuf) && (pBuf->bHasSRGB)) || ((bGlobal) && (pData->bHasglobalSRGB))) ) + { + mng_uint8 iIntent; + + if ((pBuf) && (pBuf->bHasSRGB)) /* determine rendering intent */ + iIntent = pBuf->iRenderingintent; + else + iIntent = pData->iGlobalRendintent; + /* inform the app */ + if (!pData->fProcesssrgb ((mng_handle)pData, iIntent)) + MNG_ERROR (pData, MNG_APPCMSERROR); + /* load color-correction routine */ + pData->fCorrectrow = (mng_fptr)mng_correct_app_cms; + bDone = MNG_TRUE; + } + +#ifndef MNG_SKIPCHUNK_cHRM + if ( (pData->fProcesschroma) && + (((pBuf) && (pBuf->bHasCHRM)) || ((bGlobal) && (pData->bHasglobalCHRM))) ) + { + mng_uint32 iWhitepointx, iWhitepointy; + mng_uint32 iPrimaryredx, iPrimaryredy; + mng_uint32 iPrimarygreenx, iPrimarygreeny; + mng_uint32 iPrimarybluex, iPrimarybluey; + + if ((pBuf) && (pBuf->bHasCHRM)) /* local cHRM ? */ + { + iWhitepointx = pBuf->iWhitepointx; + iWhitepointy = pBuf->iWhitepointy; + iPrimaryredx = pBuf->iPrimaryredx; + iPrimaryredy = pBuf->iPrimaryredy; + iPrimarygreenx = pBuf->iPrimarygreenx; + iPrimarygreeny = pBuf->iPrimarygreeny; + iPrimarybluex = pBuf->iPrimarybluex; + iPrimarybluey = pBuf->iPrimarybluey; + } + else + { + iWhitepointx = pData->iGlobalWhitepointx; + iWhitepointy = pData->iGlobalWhitepointy; + iPrimaryredx = pData->iGlobalPrimaryredx; + iPrimaryredy = pData->iGlobalPrimaryredy; + iPrimarygreenx = pData->iGlobalPrimarygreenx; + iPrimarygreeny = pData->iGlobalPrimarygreeny; + iPrimarybluex = pData->iGlobalPrimarybluex; + iPrimarybluey = pData->iGlobalPrimarybluey; + } + /* inform the app */ + if (!pData->fProcesschroma ((mng_handle)pData, iWhitepointx, iWhitepointy, + iPrimaryredx, iPrimaryredy, + iPrimarygreenx, iPrimarygreeny, + iPrimarybluex, iPrimarybluey)) + MNG_ERROR (pData, MNG_APPCMSERROR); + /* load color-correction routine */ + pData->fCorrectrow = (mng_fptr)mng_correct_app_cms; + bDone = MNG_TRUE; + } +#endif + + if ( (pData->fProcessgamma) && + (((pBuf) && (pBuf->bHasGAMA)) || ((bGlobal) && (pData->bHasglobalGAMA))) ) + { + mng_uint32 iGamma; + + if ((pBuf) && (pBuf->bHasGAMA)) /* get the gamma value */ + iGamma = pBuf->iGamma; + else + iGamma = pData->iGlobalGamma; + /* inform the app */ + if (!pData->fProcessgamma ((mng_handle)pData, iGamma)) + { /* app wants us to use internal routines ! */ + iRetcode = mng_init_gamma_only (pData, bGlobal, bObject, bRetrobj); + if (iRetcode) /* on error bail out */ + return iRetcode; + } + else + { /* load color-correction routine */ + pData->fCorrectrow = (mng_fptr)mng_correct_app_cms; + } + + bDone = MNG_TRUE; + } + + if (!bDone) /* no color-info at all ? */ + { + /* then use default image gamma ! */ + if (!pData->fProcessgamma ((mng_handle)pData, + (mng_uint32)((pData->dDfltimggamma * 100000) + 0.5))) + { /* app wants us to use internal routines ! */ + iRetcode = mng_init_gamma_only (pData, bGlobal, bObject, bRetrobj); + if (iRetcode) /* on error bail out */ + return iRetcode; + } + else + { /* load color-correction routine */ + pData->fCorrectrow = (mng_fptr)mng_correct_app_cms; + } + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_APP_CMS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_APP_CMS */ + +/* ************************************************************************** */ + +#ifdef MNG_APP_CMS +mng_retcode mng_correct_app_cms (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CORRECT_APP_CMS, MNG_LC_START); +#endif + + if (pData->fProcessarow) /* let the app do something with our row */ + if (!pData->fProcessarow ((mng_handle)pData, pData->iRowsamples, + pData->bIsRGBA16, pData->pRGBArow)) + MNG_ERROR (pData, MNG_APPCMSERROR); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CORRECT_APP_CMS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_APP_CMS */ + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_DISPLAY_PROCS */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ + + + diff --git a/Engine/lib/lmng/libmng_cms.h b/Engine/lib/lmng/libmng_cms.h new file mode 100644 index 000000000..4459f805f --- /dev/null +++ b/Engine/lib/lmng/libmng_cms.h @@ -0,0 +1,92 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_cms.h copyright (c) 2000-2003 G.Juyn * */ +/* * version : 1.0.6 * */ +/* * * */ +/* * purpose : color management routines (definition) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : Definition of color management routines * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed strict-ANSI stuff * */ +/* * 0.5.1 - 05/11/2000 - G.Juyn * */ +/* * - added creatememprofile * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 1.0.1 - 04/25/2001 - G.Juyn * */ +/* * - moved mng_clear_cms to libmng_cms * */ +/* * 1.0.1 - 05/02/2001 - G.Juyn * */ +/* * - added "default" sRGB generation (Thanks Marti!) * */ +/* * * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * 1.0.5 - 09/19/2002 - G.Juyn * */ +/* * - optimized color-correction routines * */ +/* * * */ +/* * 1.0.6 - 04/11/2003 - G.Juyn * */ +/* * - B719420 - fixed several MNG_APP_CMS problems * */ +/* * * */ +/* ************************************************************************** */ + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +#ifndef _libmng_cms_h_ +#define _libmng_cms_h_ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_LCMS +void mnglcms_initlibrary (void); +mng_cmsprof mnglcms_createfileprofile (mng_pchar zFilename); +mng_cmsprof mnglcms_creatememprofile (mng_uint32 iProfilesize, + mng_ptr pProfile ); +mng_cmsprof mnglcms_createsrgbprofile (void); +void mnglcms_freeprofile (mng_cmsprof hProf ); +void mnglcms_freetransform (mng_cmstrans hTrans ); + +mng_retcode mng_clear_cms (mng_datap pData ); +#endif + +/* ************************************************************************** */ + +#ifdef MNG_FULL_CMS +mng_retcode mng_init_full_cms (mng_datap pData, + mng_bool bGlobal, + mng_bool bObject, + mng_bool bRetrobj); +mng_retcode mng_correct_full_cms (mng_datap pData); +#endif + +#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS) +mng_retcode mng_init_gamma_only (mng_datap pData, + mng_bool bGlobal, + mng_bool bObject, + mng_bool bRetrobj); +mng_retcode mng_correct_gamma_only (mng_datap pData); +#endif + +#ifdef MNG_APP_CMS +mng_retcode mng_init_app_cms (mng_datap pData, + mng_bool bGlobal, + mng_bool bObject, + mng_bool bRetrobj); +mng_retcode mng_correct_app_cms (mng_datap pData); +#endif + +/* ************************************************************************** */ + +#endif /* _libmng_cms_h_ */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ diff --git a/Engine/lib/lmng/libmng_conf.h b/Engine/lib/lmng/libmng_conf.h new file mode 100644 index 000000000..8441ee087 --- /dev/null +++ b/Engine/lib/lmng/libmng_conf.h @@ -0,0 +1,295 @@ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_conf.h copyright (c) G.Juyn 2000-2004 * */ +/* * version : 1.0.9 * */ +/* * * */ +/* * purpose : main configuration file * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : The configuration file. Change this to include/exclude * */ +/* * the options you want or do not want in libmng. * */ +/* * * */ +/* * changes : 0.5.2 - 06/02/2000 - G.Juyn * */ +/* * - separated configuration-options into this file * */ +/* * - changed to most likely configuration (?) * */ +/* * 0.5.2 - 06/03/2000 - G.Juyn * */ +/* * - changed options to create a standard so-library * */ +/* * with everything enabled * */ +/* * 0.5.2 - 06/04/2000 - G.Juyn * */ +/* * - changed options to create a standard win32-dll * */ +/* * with everything enabled * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 0.9.3 - 08/12/2000 - G.Juyn * */ +/* * - added workaround for faulty PhotoShop iCCP chunk * */ +/* * 0.9.3 - 09/16/2000 - G.Juyn * */ +/* * - removed trace-options from default SO/DLL builds * */ +/* * * */ +/* * 1.0.4 - 06/22/2002 - G.Juyn * */ +/* * - B526138 - returned IJGSRC6B calling convention to * */ +/* * default for MSVC * */ +/* * * */ +/* * 1.0.5 - 09/14/2002 - G.Juyn * */ +/* * - added event handling for dynamic MNG * */ +/* * - added 'supports' call to check function availability * */ +/* * * */ +/* * 1.0.6 - 06/22/2002 - G.R-P * */ +/* * - added MNG_NO_INCLUDE_JNG conditional * */ +/* * - added MNG_SKIPCHUNK_evNT conditional * */ +/* * 1.0.6 - 07/14/2002 - G.R-P * */ +/* * - added MNG_NO_SUPPORT_FUNCQUERY conditional * */ +/* * * */ +/* * 1.0.7 - 03/07/2004 - G.R-P * */ +/* * - added MNG_VERSION_QUERY_SUPPORT_ conditional * */ +/* * * */ +/* * 1.0.9 - 05/12/2004 - G.Juyn * */ +/* * - clearified MNG_BIGENDIAN_SUPPORTED conditional * */ +/* * - added MNG_LITTLEENDIAN_SUPPORTED conditional * */ +/* * * */ +/* ************************************************************************** */ + + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +#ifndef _libmng_conf_h_ +#define _libmng_conf_h_ + +#ifdef MNG_MOZILLA_CFG +#include "special\mozcfg\mozlibmngconf.h" +#endif + +/* ************************************************************************** */ +/* * * */ +/* * User-selectable compile-time options * */ +/* * * */ +/* ************************************************************************** */ + +/* enable exactly one(1) of the MNG-(sub)set selectors */ +/* use this to select which (sub)set of the MNG specification you wish + to support */ +/* generally you'll want full support as the library provides it automatically + for you! if you're really strung on memory-requirements you can opt + to enable less support (but it's just NOT a good idea!) */ +/* NOTE that this isn't actually implemented yet */ + +#if !defined(MNG_SUPPORT_FULL) && !defined(MNG_SUPPORT_LC) && !defined(MNG_SUPPORT_VLC) +#define MNG_SUPPORT_FULL +/* #define MNG_SUPPORT_LC */ +/* #define MNG_SUPPORT_VLC */ +#endif + +/* ************************************************************************** */ + +/* enable JPEG support if required */ +/* use this to enable the JNG support routines */ +/* this requires an external jpeg package; + currently only IJG's jpgsrc6b is supported! */ +/* NOTE that the IJG code can be either 8- or 12-bit (eg. not both); + so choose the one you've defined in jconfig.h; if you don't know what + the heck I'm talking about, just leave it at 8-bit support (thank you!) */ + +#ifndef MNG_NO_INCLUDE_JNG +#ifdef MNG_SUPPORT_FULL /* full support includes JNG */ +#define MNG_SUPPORT_IJG6B +#endif + +#ifndef MNG_SUPPORT_IJG6B +#if defined(MNG_BUILD_SO) || defined(MNG_USE_SO) || defined(MNG_BUILD_DLL) || defined(MNG_USE_DLL) +#define MNG_SUPPORT_IJG6B +#endif +#endif + +#if defined(MNG_SUPPORT_IJG6B) && !defined(MNG_SUPPORT_JPEG8) && !defined(MNG_SUPPORT_JPEG12) +#define MNG_SUPPORT_JPEG8 +/* #define MNG_SUPPORT_JPEG12 */ +#endif + +/* The following is required to export the IJG routines from the DLL in + the Windows-standard calling convention; + currently this only works for Borland C++ !!! */ + +#if defined(MNG_BUILD_DLL) || defined(MNG_USE_DLL) +#if defined(MNG_SUPPORT_IJG6B) && defined(__BORLANDC__) +#define MNG_DEFINE_JPEG_STDCALL +#endif +#endif +#endif + +/* ************************************************************************** */ + +/* enable required high-level functions */ +/* use this to select the high-level functions you require */ +/* if you only need to display a MNG, disable write support! */ +/* if you only need to examine a MNG, disable write & display support! */ +/* if you only need to copy a MNG, disable display support! */ +/* if you only need to create a MNG, disable read & display support! */ +/* NOTE that turning all options off will be very unuseful! */ + +#if !defined(MNG_SUPPORT_READ) && !defined(MNG_SUPPORT_WRITE) && !defined(MNG_SUPPORT_DISPLAY) +#define MNG_SUPPORT_READ +#if defined(MNG_BUILD_SO) || defined(MNG_USE_SO) || defined(MNG_BUILD_DLL) || defined(MNG_USE_DLL) +#define MNG_SUPPORT_WRITE +#endif +#define MNG_SUPPORT_DISPLAY +#endif + +/* ************************************************************************** */ + +/* enable chunk access functions */ +/* use this to select whether you need access to the individual chunks */ +/* useful if you want to examine a read MNG (you'll also need MNG_STORE_CHUNKS !)*/ +/* required if you need to create & write a new MNG! */ + +#ifndef MNG_ACCESS_CHUNKS +#if defined(MNG_BUILD_SO) || defined(MNG_USE_SO) || defined(MNG_BUILD_DLL) || defined(MNG_USE_DLL) +#define MNG_ACCESS_CHUNKS +#endif +#endif + +/* ************************************************************************** */ + +/* enable exactly one(1) of the color-management functionality selectors */ +/* use this to select the level of automatic color support */ +/* MNG_FULL_CMS requires the lcms (little cms) external package ! */ +/* if you want your own app (or the OS) to handle color-management + select MNG_APP_CMS */ + +#define MNG_GAMMA_ONLY +/* #define MNG_FULL_CMS */ +/* #define MNG_APP_CMS */ + +/* ************************************************************************** */ + +/* enable automatic dithering */ +/* use this if you need dithering support to convert high-resolution + images to a low-resolution output-device */ +/* NOTE that this is not supported yet */ + +/* #define MNG_AUTO_DITHER */ + +/* ************************************************************************** */ + +/* enable whether chunks should be stored for reference later */ +/* use this if you need to examine the chunks of a MNG you have read, + or (re-)write a MNG you have read */ +/* turn this off if you want to reduce memory-consumption */ + +#ifndef MNG_STORE_CHUNKS +#if defined(MNG_BUILD_SO) || defined(MNG_USE_SO) || defined(MNG_BUILD_DLL) || defined(MNG_USE_DLL) +#define MNG_STORE_CHUNKS +#endif +#endif + +/* ************************************************************************** */ + +/* enable internal memory management (if your compiler supports it) */ +/* use this if your compiler supports the 'standard' memory functions + (calloc & free), and you want the library to use these functions and not + bother your app with memory-callbacks */ + +/* #define MNG_INTERNAL_MEMMNGMT */ + +/* ************************************************************************** */ + +/* enable internal tracing-functionality (manual debugging purposes) */ +/* use this if you have trouble location bugs or problems */ +/* NOTE that you'll need to specify the trace callback function! */ + +/* #define MNG_SUPPORT_TRACE */ + +/* ************************************************************************** */ + +/* enable extended error- and trace-telltaling */ +/* use this if you need explanatory messages with errors and/or tracing */ + +#if !defined(MNG_ERROR_TELLTALE) && !defined(MNG_TRACE_TELLTALE) +#if defined(MNG_BUILD_SO) || defined(MNG_USE_SO) || defined(MNG_BUILD_DLL) || defined(MNG_USE_DLL) +#define MNG_ERROR_TELLTALE +#define MNG_TRACE_TELLTALE +#endif +#endif + +/* ************************************************************************** */ + +/* enable BIG/LITTLE endian optimizations */ +/* enable BIG if you're on an architecture that supports big-endian reads + and writes that aren't word-aligned */ +/* according to reliable sources this only works for PowerPC (bigendian mode) + and 680x0 */ +/* enable LITTLE if you're on an architecture that supports little-endian */ +/* when in doubt leave both off !!! */ + +/* #define MNG_BIGENDIAN_SUPPORTED */ +/* #define MNG_LITTLEENDIAN_SUPPORTED */ + +/* ************************************************************************** */ +/* enable 'version' functions */ +#if !defined(MNG_VERSION_QUERY_SUPPORT) && \ + !defined(MNG_NO_VERSION_QUERY_SUPPORT) +#define MNG_VERSION_QUERY_SUPPORT +#endif + +/* enable 'supports' function */ +/* use this if you need to query the availability of functions at runtime; + useful for apps that dynamically load the library and that need specific + functions */ + +#if !defined(MNG_NO_SUPPORT_FUNCQUERY) && !defined(MNG_SUPPORT_FUNCQUERY) +#if defined(MNG_BUILD_SO) || defined(MNG_USE_SO) || \ + defined(MNG_BUILD_DLL) || defined(MNG_USE_DLL) +#define MNG_SUPPORT_FUNCQUERY +#endif +#endif + +/* ************************************************************************** */ + +/* enable dynamic MNG features */ +/* use this if you would like to have dynamic support for specifically + designed MNGs; eg. this is useful for 'rollover' effects such as common + on the world wide web */ + +#ifndef MNG_SUPPORT_DYNAMICMNG +#if defined(MNG_BUILD_SO) || defined(MNG_USE_SO) || defined(MNG_BUILD_DLL) || defined(MNG_USE_DLL) +#define MNG_SUPPORT_DYNAMICMNG +#endif +#endif +#ifndef MNG_SUPPORT_DYNAMICMNG +#ifndef MNG_SKIPCHUNK_evNT +#define MNG_SKIPCHUNK_evNT +#endif +#endif + +#ifdef MNG_INCLUDE_JNG +#ifndef MNG_NO_ACCESS_JPEG +#ifndef MNG_ACCESS_JPEG +#define MNG_ACCESS_JPEG +#endif +#endif +#endif + +#ifdef MNG_INCLUDE_ZLIB +#ifndef MNG_NO_ACCESS_ZLIB +#ifndef MNG_ACCESS_ZLIB +#define MNG_ACCESS_ZLIB +#endif +#endif +#endif + +/* ************************************************************************** */ +/* * * */ +/* * End of user-selectable compile-time options * */ +/* * * */ +/* ************************************************************************** */ + +#endif /* _libmng_conf_h_ */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ + diff --git a/Engine/lib/lmng/libmng_data.h b/Engine/lib/lmng/libmng_data.h new file mode 100644 index 000000000..430dca96b --- /dev/null +++ b/Engine/lib/lmng/libmng_data.h @@ -0,0 +1,1029 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_data.h copyright (c) 2000-2007 G.Juyn * */ +/* * version : 1.0.10 * */ +/* * * */ +/* * purpose : main data structure definition * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : Definition of the library main data structure * */ +/* * * */ +/* * changes : 0.5.1 - 05/04/2000 - G.Juyn * */ +/* * - added CRC table to main structure (for thread-safety) * */ +/* * 0.5.1 - 05/06/2000 - G.Juyn * */ +/* * - added iPLTEentries for checking hIST-length * */ +/* * 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed palette definition to exported palette-type * */ +/* * - removed frozen indicator * */ +/* * - added create/write indicators * */ +/* * - changed strict-ANSI stuff * */ +/* * 0.5.1 - 05/13/2000 - G.Juyn * */ +/* * - added eMNGma hack (will be removed in 1.0.0 !!!) * */ +/* * - added TERM animation object pointer (easier reference) * */ +/* * - added saved-data structure for SAVE/SEEK processing * */ +/* * * */ +/* * 0.5.2 - 05/18/2000 - G.Juyn * */ +/* * - added fields for JNG support (IJG-based) * */ +/* * 0.5.2 - 05/24/2000 - G.Juyn * */ +/* * - changed global tRNS definition * */ +/* * 0.5.2 - 05/30/2000 - G.Juyn * */ +/* * - added delta-image fields * */ +/* * 0.5.2 - 06/01/2000 - G.Juyn * */ +/* * - added internal delta-image processing callbacks * */ +/* * 0.5.2 - 06/02/2000 - G.Juyn * */ +/* * - changed SWAP_ENDIAN to BIGENDIAN_SUPPORTED * */ +/* * (contributed by Tim Rowley) * */ +/* * - added getalphaline callback for RGB8_A8 canvasstyle * */ +/* * 0.5.2 - 06/06/2000 - G.Juyn * */ +/* * - added parameter for delayed buffer-processing * */ +/* * * */ +/* * 0.5.3 - 06/16/2000 - G.Juyn * */ +/* * - added update-region parms for refresh calback * */ +/* * - added Needrefresh parameter * */ +/* * 0.5.3 - 06/17/2000 - G.Juyn * */ +/* * - added Deltaimmediate parm for faster delta-processing * */ +/* * 0.5.3 - 06/21/2000 - G.Juyn * */ +/* * - added Speed parameter to facilitate testing * */ +/* * - added Imagelevel parameter for processtext callback * */ +/* * 0.5.3 - 06/26/2000 - G.Juyn * */ +/* * - changed userdata variable to mng_ptr * */ +/* * * */ +/* * 0.9.1 - 07/07/2000 - G.Juyn * */ +/* * - added variables for go_xxxx processing * */ +/* * 0.9.1 - 07/08/2000 - G.Juyn * */ +/* * - added variables for improved timing support * */ +/* * 0.9.1 - 07/15/2000 - G.Juyn * */ +/* * - added callbacks for SAVE/SEEK processing * */ +/* * - added variable for NEEDSECTIONWAIT breaks * */ +/* * - added variable for freeze & reset processing * */ +/* * 0.9.1 - 07/17/2000 - G.Juyn * */ +/* * - fixed suspension-buffering for 32K+ chunks * */ +/* * * */ +/* * 0.9.2 - 07/29/2000 - G.Juyn * */ +/* * - removed Nextbackxxx fields (no longer used) * */ +/* * 0.9.2 - 07/31/2000 - G.Juyn * */ +/* * - fixed wrapping of suspension parameters * */ +/* * 0.9.2 - 08/04/2000 - G.Juyn * */ +/* * - B111096 - fixed large-buffer read-suspension * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 0.9.3 - 08/26/2000 - G.Juyn * */ +/* * - added MAGN chunk * */ +/* * 0.9.3 - 09/07/2000 - G.Juyn * */ +/* * - added support for new filter_types * */ +/* * 0.9.3 - 09/10/2000 - G.Juyn * */ +/* * - fixed DEFI behavior * */ +/* * 0.9.3 - 10/10/2000 - G.Juyn * */ +/* * - added support for alpha-depth prediction * */ +/* * 0.9.3 - 10/11/2000 - G.Juyn * */ +/* * - added support for nEED * */ +/* * 0.9.3 - 10/16/2000 - G.Juyn * */ +/* * - added optional support for bKGD for PNG images * */ +/* * - added support for JDAA * */ +/* * 0.9.3 - 10/17/2000 - G.Juyn * */ +/* * - added callback to process non-critical unknown chunks * */ +/* * - fixed support for bKGD * */ +/* * 0.9.3 - 10/19/2000 - G.Juyn * */ +/* * - implemented delayed delta-processing * */ +/* * 0.9.4 - 12/16/2000 - G.Juyn * */ +/* * - fixed mixup of data- & function-pointers (thanks Dimitri)* */ +/* * * */ +/* * 1.0.1 - 02/08/2001 - G.Juyn * */ +/* * - added MEND processing callback * */ +/* * 1.0.1 - 02/13/2001 - G.Juyn * */ +/* * - fixed first FRAM_MODE=4 timing problem * */ +/* * * */ +/* * 1.0.2 - 06/23/2001 - G.Juyn * */ +/* * - added optimization option for MNG-video playback * */ +/* * - added processterm callback * */ +/* * 1.0.2 - 06/25/2001 - G.Juyn * */ +/* * - added option to turn off progressive refresh * */ +/* * * */ +/* * 1.0.5 - 07/08/2002 - G.Juyn * */ +/* * - B578572 - removed eMNGma hack (thanks Dimitri!) * */ +/* * 1.0.5 - 07/16/2002 - G.Juyn * */ +/* * - B581625 - large chunks fail with suspension reads * */ +/* * 1.0.5 - 08/15/2002 - G.Juyn * */ +/* * - completed PROM support * */ +/* * 1.0.5 - 09/15/2002 - G.Juyn * */ +/* * - fixed LOOP iteration=0 special case * */ +/* * 1.0.5 - 09/20/2002 - G.Juyn * */ +/* * - finished support for BACK image & tiling * */ +/* * 1.0.5 - 10/07/2002 - G.Juyn * */ +/* * - added another fix for misplaced TERM chunk * */ +/* * - completed support for condition=2 in TERM chunk * */ +/* * 1.0.5 - 10/20/2002 - G.Juyn * */ +/* * - fixed processing for multiple objects in MAGN * */ +/* * - fixed display of visible target of PAST operation * */ +/* * 1.0.5 - 11/07/2002 - G.Juyn * */ +/* * - added support to get totals after mng_read() * */ +/* * 1.0.5 - 24/02/2003 - G.Juyn * */ +/* * - B683152 - libjpeg suspension not always honored correctly* */ +/* * * */ +/* * 1.0.6 - 04/11/2003 - G.Juyn * */ +/* * - B719420 - fixed several MNG_APP_CMS problems * */ +/* * 1.0.6 - 07/05/2003 - G. R-P * */ +/* * - optionally use zlib's crc32() function * */ +/* * 1.0.6 - 07/29/2003 - G.R-P * */ +/* * - added SKIPCHUNK conditionals around PAST chunk support * */ +/* * 1.0.6 - 08/17/2003 - G.R-P * */ +/* * - added iPNGdepth member to pData structure * */ +/* * * */ +/* * 1.0.7 - 03/10/2004 - G.R-P * */ +/* * - added conditionals around openstream/closestream * */ +/* * 1.0.7 - 03/24/2004 - G.R-P * */ +/* * - added more SKIPCHUNK conditionals * */ +/* * * */ +/* * 1.0.8 - 04/02/2004 - G.Juyn * */ +/* * - added CRC existence & checking flags * */ +/* * 1.0.8 - 04/10/2004 - G.Juyn * */ +/* * - added data-push mechanisms for specialized decoders * */ +/* * * */ +/* * 1.0.9 - 12/11/2004 - G.Juyn * */ +/* * - added conditional MNG_OPTIMIZE_DISPLAYCALLS * */ +/* * * */ +/* * 1.0.10 - 04/08/2007 - G.Juyn * */ +/* * - added support for mPNG proposal * */ +/* * 1.0.10 - 04/12/2007 - G.Juyn * */ +/* * - added support for ANG proposal * */ +/* * * */ +/* ************************************************************************** */ + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +#ifndef _libmng_data_h_ +#define _libmng_data_h_ + +/* ************************************************************************** */ + +#define MNG_MAGIC 0x52530a0aL + +/* ************************************************************************** */ +/* * * */ +/* * Internal structures * */ +/* * * */ +/* ************************************************************************** */ + +typedef mng_palette8 mng_rgbpaltab; + +/* ************************************************************************** */ +/* * * */ +/* * The saved_data structure * */ +/* * * */ +/* * This contains the saved data after a SAVE chunk has been processed. * */ +/* * The data is saved from the main data structure during SAVE processing, * */ +/* * and restored to the main data structure during SEEK processing. * */ +/* * * */ +/* ************************************************************************** */ + +typedef struct mng_savedata_struct { + +#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE) + mng_bool bHasglobalPLTE; /* global PLTE chunk processed */ + mng_bool bHasglobalTRNS; /* global tRNS chunk processed */ + mng_bool bHasglobalGAMA; /* global gAMA chunk processed */ + mng_bool bHasglobalCHRM; /* global cHRM chunk processed */ + mng_bool bHasglobalSRGB; /* global sRGB chunk processed */ + mng_bool bHasglobalICCP; /* global iCCP chunk processed */ + mng_bool bHasglobalBKGD; /* global bKGD chunk processed */ +#endif /* MNG_SUPPORT_READ || MNG_SUPPORT_WRITE */ + +#ifdef MNG_SUPPORT_DISPLAY + mng_uint16 iBACKred; /* BACK fields */ + mng_uint16 iBACKgreen; + mng_uint16 iBACKblue; + mng_uint8 iBACKmandatory; + mng_uint16 iBACKimageid; + mng_uint8 iBACKtile; + + mng_uint8 iFRAMmode; /* FRAM fields (global) */ + mng_uint32 iFRAMdelay; + mng_uint32 iFRAMtimeout; + mng_bool bFRAMclipping; + mng_int32 iFRAMclipl; + mng_int32 iFRAMclipr; + mng_int32 iFRAMclipt; + mng_int32 iFRAMclipb; + + mng_uint32 iGlobalPLTEcount; /* global PLTE fields */ + mng_rgbpaltab aGlobalPLTEentries; + + mng_uint32 iGlobalTRNSrawlen; /* global tRNS fields */ + mng_uint8arr aGlobalTRNSrawdata; + + mng_uint32 iGlobalGamma; /* global gAMA fields */ + +#ifndef MNG_SKIPCHUNK_cHRM + mng_uint32 iGlobalWhitepointx; /* global cHRM fields */ + mng_uint32 iGlobalWhitepointy; + mng_uint32 iGlobalPrimaryredx; + mng_uint32 iGlobalPrimaryredy; + mng_uint32 iGlobalPrimarygreenx; + mng_uint32 iGlobalPrimarygreeny; + mng_uint32 iGlobalPrimarybluex; + mng_uint32 iGlobalPrimarybluey; +#endif + + mng_uint8 iGlobalRendintent; /* global sRGB fields */ + + mng_uint32 iGlobalProfilesize; /* global iCCP fields */ + mng_ptr pGlobalProfile; + + mng_uint16 iGlobalBKGDred; /* global bKGD fields */ + mng_uint16 iGlobalBKGDgreen; + mng_uint16 iGlobalBKGDblue; +#endif /* MNG_SUPPORT_DISPLAY */ + + } mng_savedata; + +typedef mng_savedata * mng_savedatap; + +/* ************************************************************************** */ +/* * * */ +/* * Internal buffer structure for data push mechanisms * */ +/* * * */ +/* ************************************************************************** */ + +typedef struct { + mng_ptr pNext; /* for linked list */ + mng_ptr pData; /* used for chunks & data */ + mng_uint32 iLength; + mng_bool bOwned; + mng_uint8p pDatanext; /* only used for data */ + mng_uint32 iRemaining; + } mng_pushdata; +typedef mng_pushdata * mng_pushdatap; + +/* ************************************************************************** */ +/* * * */ +/* * The main libmng data structure * */ +/* * * */ +/* * The handle used in all functions points to this structure which * */ +/* * contains all volatile data necessary to process the network graphic. * */ +/* * * */ +/* ************************************************************************** */ + +typedef struct mng_data_struct { + + mng_uint32 iMagic; /* magic number to validate + a given handle */ + mng_ptr pUserdata; /* application workdata */ + + mng_imgtype eSigtype; /* image information */ + mng_imgtype eImagetype; /* initially zeroed */ + mng_uint32 iWidth; /* filled after header is processed */ + mng_uint32 iHeight; + mng_uint32 iTicks; /* these only after MHDR */ + mng_uint32 iLayercount; + mng_uint32 iFramecount; + mng_uint32 iPlaytime; + mng_uint32 iSimplicity; + mng_uint8 iAlphadepth; /* indicates expected alpha-depth */ + + mng_uint32 iImagelevel; /* level of image inside a stream */ + + mng_uint32 iCanvasstyle; /* layout of the drawing-canvas */ + mng_uint32 iBkgdstyle; /* layout of the background-canvas */ + + mng_int8 iMagnify; /* magnification factor (not used yet) */ + mng_uint32 iOffsetx; /* x-offset for extremely large image */ + mng_uint32 iOffsety; /* y-offset for extremely large image */ + mng_uint32 iCanvaswidth; /* real canvas size */ + mng_uint32 iCanvasheight; /* must be set by processheader callback */ + + mng_uint16 iBGred; /* default background color */ + mng_uint16 iBGgreen; /* initially "black" */ + mng_uint16 iBGblue; + mng_bool bUseBKGD; /* preferred use of bKGD for PNG */ + + mng_bool bIssRGB; /* indicates sRGB system */ + +#ifdef MNG_FULL_CMS /* little CMS variables */ + mng_cmsprof hProf1; /* image input profile */ + mng_cmsprof hProf2; /* default output profile */ + mng_cmsprof hProf3; /* default sRGB profile */ + mng_cmstrans hTrans; /* current transformation handle */ +#endif + + mng_float dViewgamma; /* gamma calculation variables */ + mng_float dDisplaygamma; /* initially set for sRGB conditions */ + mng_float dDfltimggamma; + + mng_bool bStorechunks; /* switch for storing chunkdata */ + mng_bool bSectionbreaks; /* indicate NEEDSECTIONWAIT breaks */ + mng_bool bCacheplayback; /* switch to cache playback info */ + mng_bool bDoProgressive; /* progressive refresh for large images */ + mng_uint32 iCrcmode; /* CRC existence & checking flags */ + + mng_speedtype iSpeed; /* speed-modifier for animations */ + + mng_uint32 iMaxwidth; /* maximum canvas size */ + mng_uint32 iMaxheight; /* initially set to 1024 x 1024 */ + + mng_int32 iErrorcode; /* error reporting fields */ + mng_int8 iSeverity; + mng_int32 iErrorx1; + mng_int32 iErrorx2; + mng_pchar zErrortext; + + mng_memalloc fMemalloc; /* callback pointers */ + mng_memfree fMemfree; /* initially nulled */ + mng_releasedata fReleasedata; +#ifndef MNG_NO_OPEN_CLOSE_STREAM + mng_openstream fOpenstream; + mng_closestream fClosestream; +#endif + mng_readdata fReaddata; + mng_writedata fWritedata; + mng_errorproc fErrorproc; + mng_traceproc fTraceproc; + mng_processheader fProcessheader; + mng_processtext fProcesstext; + mng_processsave fProcesssave; + mng_processseek fProcessseek; + mng_processneed fProcessneed; + mng_processmend fProcessmend; + mng_processunknown fProcessunknown; + mng_processterm fProcessterm; + mng_getcanvasline fGetcanvasline; + mng_getbkgdline fGetbkgdline; + mng_getalphaline fGetalphaline; + mng_refresh fRefresh; + mng_gettickcount fGettickcount; + mng_settimer fSettimer; + mng_processgamma fProcessgamma; + mng_processchroma fProcesschroma; + mng_processsrgb fProcesssrgb; + mng_processiccp fProcessiccp; + mng_processarow fProcessarow; + +#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE) +#ifndef MNG_NO_OLD_VERSIONS + mng_bool bPreDraft48; /* flags ancient style draft */ +#endif + + mng_chunkid iChunkname; /* read/write-state variables */ + mng_uint32 iChunkseq; + mng_chunkp pFirstchunk; /* double-linked list of */ + mng_chunkp pLastchunk; /* stored chunk-structures */ + + mng_bool bHasheader; /* first header chunk processed */ + mng_bool bHasMHDR; /* inside a MHDR-MEND sequence */ + mng_bool bHasIHDR; /* inside a IHDR-IEND sequence */ + mng_bool bHasBASI; /* inside a BASI-IEND sequence */ + mng_bool bHasDHDR; /* inside a DHDR-IEND sequence */ +#ifdef MNG_INCLUDE_JNG + mng_bool bHasJHDR; /* inside a JHDR-IEND sequence */ + mng_bool bHasJSEP; /* passed the JSEP separator */ + mng_bool bHasJDAA; /* at least 1 JDAA processed */ + mng_bool bHasJDAT; /* at least 1 JDAT processed */ +#endif + mng_bool bHasPLTE; /* PLTE chunk processed */ + mng_bool bHasTRNS; /* tRNS chunk processed */ + mng_bool bHasGAMA; /* gAMA chunk processed */ + mng_bool bHasCHRM; /* cHRM chunk processed */ + mng_bool bHasSRGB; /* sRGB chunk processed */ + mng_bool bHasICCP; /* iCCP chunk processed */ + mng_bool bHasBKGD; /* bKGD chunk processed */ + mng_bool bHasIDAT; /* at least 1 IDAT processed */ + + mng_bool bHasSAVE; /* SAVE chunk processed */ + mng_bool bHasBACK; /* BACK chunk processed */ + mng_bool bHasFRAM; /* FRAM chunk processed */ + mng_bool bHasTERM; /* TERM chunk processed */ + mng_bool bHasLOOP; /* at least 1 LOOP open */ + + mng_bool bHasglobalPLTE; /* global PLTE chunk processed */ + mng_bool bHasglobalTRNS; /* global tRNS chunk processed */ + mng_bool bHasglobalGAMA; /* global gAMA chunk processed */ + mng_bool bHasglobalCHRM; /* global cHRM chunk processed */ + mng_bool bHasglobalSRGB; /* global sRGB chunk processed */ + mng_bool bHasglobalICCP; /* global iCCP chunk processed */ + mng_bool bHasglobalBKGD; /* global bKGD chunk processed */ + + mng_uint32 iDatawidth; /* IHDR/BASI/DHDR fields */ + mng_uint32 iDataheight; /* valid if inside IHDR-IEND, */ + mng_uint8 iBitdepth; /* BASI-IEND or DHDR-IEND */ + mng_uint8 iColortype; + mng_uint8 iCompression; + mng_uint8 iFilter; + mng_uint8 iInterlace; + + mng_uint32 iPLTEcount; /* PLTE fields */ + +#ifdef MNG_INCLUDE_JNG + mng_uint8 iJHDRcolortype; /* JHDR fields */ + mng_uint8 iJHDRimgbitdepth; /* valid if inside JHDR-IEND */ + mng_uint8 iJHDRimgcompression; + mng_uint8 iJHDRimginterlace; + mng_uint8 iJHDRalphabitdepth; + mng_uint8 iJHDRalphacompression; + mng_uint8 iJHDRalphafilter; + mng_uint8 iJHDRalphainterlace; +#endif + +#endif /* MNG_SUPPORT_READ || MNG_SUPPORT_WRITE */ + +#ifdef MNG_SUPPORT_READ + mng_bool bReading; /* read processing variables */ + mng_bool bHavesig; + mng_bool bEOF; + mng_uint32 iReadbufsize; + mng_uint8p pReadbuf; + + mng_uint32 iLargebufsize; /* temp for very large chunks */ + mng_uint8p pLargebuf; + + mng_uint32 iSuspendtime; /* tickcount at last suspension */ + mng_bool bSuspended; /* input-reading has been suspended; + we're expecting a call to + mng_read_resume! */ + mng_uint8 iSuspendpoint; /* indicates at which point the flow + was broken to suspend input-reading */ + + mng_bool bSuspensionmode; /* I/O-suspension variables */ + mng_uint32 iSuspendbufsize; + mng_uint8p pSuspendbuf; + mng_uint8p pSuspendbufnext; + mng_uint32 iSuspendbufleft; + mng_uint32 iChunklen; /* chunk length */ + mng_uint8p pReadbufnext; /* 32K+ suspension-processing */ + mng_uint8p pLargebufnext; + + mng_pushdatap pFirstpushchunk; /* variables for push mechanisms */ + mng_pushdatap pLastpushchunk; + mng_pushdatap pFirstpushdata; + mng_pushdatap pLastpushdata; +#endif /* MNG_SUPPORT_READ */ + +#ifdef MNG_SUPPORT_WRITE + mng_bool bCreating; /* create/write processing variables */ + mng_bool bWriting; + mng_chunkid iFirstchunkadded; + mng_uint32 iWritebufsize; + mng_uint8p pWritebuf; +#endif + +#ifdef MNG_SUPPORT_DISPLAY + mng_bool bDisplaying; /* display-state variables */ + mng_bool bFramedone; + mng_uint32 iFrameseq; + mng_uint32 iLayerseq; + mng_uint32 iFrametime; /* millisecs */ + + mng_uint32 iTotalframes; /* end-totals after mng_read() */ + mng_uint32 iTotallayers; + mng_uint32 iTotalplaytime; /* millisecs */ + + mng_bool bSkipping; /* LOOP iteration=0 */ + +#ifdef MNG_SUPPORT_DYNAMICMNG + mng_bool bDynamic; /* MNG is dynamic (eg. has events) */ + mng_bool bRunningevent; /* currently processing an event */ + mng_bool bStopafterseek; /* stop after next SEEK */ + mng_int32 iEventx; /* X/Y of current event */ + mng_int32 iEventy; + mng_objectp pLastmousemove; /* last event triggered */ +#endif + + mng_uint32 iRequestframe; /* go_xxxx variables */ + mng_uint32 iRequestlayer; + mng_uint32 iRequesttime; + mng_bool bSearching; + + mng_bool bRestorebkgd; /* flags restore required before IDAT/JDAT */ + + mng_uint32 iRuntime; /* millisecs since start */ + mng_uint32 iSynctime; /* tickcount at last framesync */ + mng_uint32 iStarttime; /* tickcount at start */ + mng_uint32 iEndtime; /* tickcount at end */ + mng_bool bRunning; /* animation is active */ + mng_bool bTimerset; /* the timer has been set; + we're expecting a call to + mng_display_resume! */ + mng_uint8 iBreakpoint; /* indicates at which point the + flow was broken to run the timer */ + mng_bool bSectionwait; /* indicates a section break */ + mng_bool bFreezing; /* indicates app requested a freeze */ + mng_bool bResetting; /* indicates app requested a reset */ + mng_bool bNeedrefresh; /* indicates screen-refresh is needed */ + mng_bool bMisplacedTERM; /* indicates TERM is out of place */ + mng_bool bOnlyfirstframe; /* show first frame after TERM and stop */ + mng_uint32 iFramesafterTERM; /* determines frame-count after TERM */ + mng_objectp pCurrentobj; /* current "object" */ + mng_objectp pCurraniobj; /* current animation object + "to be"/"being" processed */ + mng_objectp pTermaniobj; /* TERM animation object */ + mng_uint32 iIterations; /* TERM/MEND iteration count */ + mng_objectp pObjzero; /* "on-the-fly" image (object = 0) */ + mng_objectp pLastclone; /* last clone */ + mng_objectp pStoreobj; /* current store object for row routines */ + mng_objectp pStorebuf; /* current store object-buffer for row routines */ + mng_objectp pRetrieveobj; /* current retrieve object for row routines */ + mng_savedatap pSavedata; /* pointer to saved data (after SAVE) */ + + mng_uint32 iUpdateleft; /* update region for refresh */ + mng_uint32 iUpdateright; + mng_uint32 iUpdatetop; + mng_uint32 iUpdatebottom; + + mng_int8 iPass; /* current interlacing pass; + negative value means no interlace */ + mng_int32 iRow; /* current row counter */ + mng_int32 iRowinc; /* row increment for this pass */ + mng_int32 iCol; /* current starting column */ + mng_int32 iColinc; /* column increment for this pass */ + mng_int32 iRowsamples; /* nr. of samples in current workrow */ + mng_int32 iSamplemul; /* needed to calculate rowsize */ + mng_int32 iSampleofs; /* from rowsamples */ + mng_int32 iSamplediv; + mng_int32 iRowsize; /* size of actual data in work row */ + mng_int32 iRowmax; /* maximum size of data in work row */ + mng_int32 iFilterofs; /* offset to filter-byte in work row */ + mng_int32 iPixelofs; /* offset to pixel-bytes in work row */ + mng_uint32 iLevel0; /* leveling variables */ + mng_uint32 iLevel1; + mng_uint32 iLevel2; + mng_uint32 iLevel3; + mng_uint8p pWorkrow; /* working row of pixel-data */ + mng_uint8p pPrevrow; /* previous row of pixel-data */ + mng_uint8p pRGBArow; /* intermediate row of RGBA8 or RGBA16 data */ + mng_bool bIsRGBA16; /* indicates intermediate row is RGBA16 */ + mng_bool bIsOpaque; /* indicates intermediate row is fully opaque */ + mng_int32 iFilterbpp; /* bpp index for filtering routines */ + + mng_int32 iSourcel; /* variables for showing objects */ + mng_int32 iSourcer; + mng_int32 iSourcet; + mng_int32 iSourceb; + mng_int32 iDestl; + mng_int32 iDestr; + mng_int32 iDestt; + mng_int32 iDestb; + + mng_objectp pFirstimgobj; /* double-linked list of */ + mng_objectp pLastimgobj; /* image-object structures */ + mng_objectp pFirstaniobj; /* double-linked list of */ + mng_objectp pLastaniobj; /* animation-object structures */ +#ifdef MNG_SUPPORT_DYNAMICMNG + mng_objectp pFirstevent; /* double-linked list of */ + mng_objectp pLastevent; /* event-object structures */ +#endif + +#if defined(MNG_GAMMA_ONLY) || defined(MNG_FULL_CMS) || defined(MNG_APP_CMS) + mng_uint8 aGammatab[256]; /* precomputed gamma lookup table */ + mng_float dLastgamma; /* last gamma used to compute table */ +#endif + + mng_fptr fDisplayrow; /* internal callback to display an + uncompressed/unfiltered/ + color-corrected row */ + mng_fptr fRestbkgdrow; /* internal callback for restore- + background processing of a row */ + mng_fptr fCorrectrow; /* internal callback to color-correct an + uncompressed/unfiltered row */ + mng_fptr fRetrieverow; /* internal callback to retrieve an + uncompressed/unfiltered row of data */ + mng_fptr fStorerow; /* internal callback to store an + uncompressed/unfiltered row of data */ + mng_fptr fProcessrow; /* internal callback to process an + uncompressed row of data */ + mng_fptr fDifferrow; /* internal callback to perform + added filter leveling and + differing on an unfiltered row */ + mng_fptr fScalerow; /* internal callback to scale a + delta-row to the bitdepth of its target */ + mng_fptr fDeltarow; /* internal callback to execute a + delta-row onto a target */ +#ifndef MNG_SKIPCHUNK_PAST + mng_fptr fFliprow; /* internal callback to flip a row of pixels + left<->right for a PAST operation */ + mng_fptr fTilerow; /* internal callback to tile a row of pixels + during a PAST operation */ +#endif + mng_fptr fInitrowproc; /* internal callback to initialize + the row processing */ + + mng_uint16 iDEFIobjectid; /* DEFI fields */ + mng_bool bDEFIhasdonotshow; + mng_uint8 iDEFIdonotshow; + mng_bool bDEFIhasconcrete; + mng_uint8 iDEFIconcrete; + mng_bool bDEFIhasloca; + mng_int32 iDEFIlocax; + mng_int32 iDEFIlocay; + mng_bool bDEFIhasclip; + mng_int32 iDEFIclipl; + mng_int32 iDEFIclipr; + mng_int32 iDEFIclipt; + mng_int32 iDEFIclipb; + + mng_uint16 iBACKred; /* BACK fields */ + mng_uint16 iBACKgreen; + mng_uint16 iBACKblue; + mng_uint8 iBACKmandatory; + mng_uint16 iBACKimageid; + mng_uint8 iBACKtile; + + mng_int32 iBackimgoffsx; /* temp variables for restore_bkgd */ + mng_int32 iBackimgoffsy; + mng_uint32 iBackimgwidth; + mng_uint32 iBackimgheight; + +#ifndef MNG_SKIPCHUNK_FRAM + mng_uint8 iFRAMmode; /* FRAM fields (global) */ + mng_uint32 iFRAMdelay; + mng_uint32 iFRAMtimeout; + mng_bool bFRAMclipping; + mng_int32 iFRAMclipl; + mng_int32 iFRAMclipr; + mng_int32 iFRAMclipt; + mng_int32 iFRAMclipb; + + mng_uint8 iFramemode; /* current subframe variables */ + mng_uint32 iFramedelay; + mng_uint32 iFrametimeout; + mng_bool bFrameclipping; + mng_int32 iFrameclipl; + mng_int32 iFrameclipr; + mng_int32 iFrameclipt; + mng_int32 iFrameclipb; + + mng_uint32 iNextdelay; /* delay *after* next image */ +#endif + +#ifndef MNG_SKIPCHUNK_SHOW + mng_uint8 iSHOWmode; /* SHOW fields */ + mng_uint16 iSHOWfromid; + mng_uint16 iSHOWtoid; + mng_uint16 iSHOWnextid; + mng_int16 iSHOWskip; +#endif + + mng_uint32 iGlobalPLTEcount; /* global PLTE fields */ + mng_rgbpaltab aGlobalPLTEentries; + + mng_uint32 iGlobalTRNSrawlen; /* global tRNS fields */ + mng_uint8arr aGlobalTRNSrawdata; + + mng_uint32 iGlobalGamma; /* global gAMA fields */ + +#ifndef MNG_SKIPCHUNK_cHRM + mng_uint32 iGlobalWhitepointx; /* global cHRM fields */ + mng_uint32 iGlobalWhitepointy; + mng_uint32 iGlobalPrimaryredx; + mng_uint32 iGlobalPrimaryredy; + mng_uint32 iGlobalPrimarygreenx; + mng_uint32 iGlobalPrimarygreeny; + mng_uint32 iGlobalPrimarybluex; + mng_uint32 iGlobalPrimarybluey; +#endif + + mng_uint8 iGlobalRendintent; /* global sRGB fields */ + +#ifndef MNG_SKIPCHUNK_iCCP + mng_uint32 iGlobalProfilesize; /* global iCCP fields */ + mng_ptr pGlobalProfile; +#endif + + mng_uint16 iGlobalBKGDred; /* global bKGD fields */ + mng_uint16 iGlobalBKGDgreen; + mng_uint16 iGlobalBKGDblue; + + mng_ptr pDeltaImage; /* delta-image fields */ + mng_uint8 iDeltaImagetype; +#endif /* MNG_SUPPORT_DISPLAY */ + mng_uint8 iDeltatype; /* need this one in read processing !! */ +#ifdef MNG_SUPPORT_DISPLAY + mng_uint32 iDeltaBlockwidth; + mng_uint32 iDeltaBlockheight; + mng_uint32 iDeltaBlockx; + mng_uint32 iDeltaBlocky; + mng_bool bDeltaimmediate; + + mng_fptr fDeltagetrow; /* internal delta-proc callbacks */ + mng_fptr fDeltaaddrow; + mng_fptr fDeltareplacerow; + mng_fptr fDeltaputrow; + +#ifndef MNG_SKIPCHUNK_PROM + mng_fptr fPromoterow; /* internal PROM fields */ + mng_fptr fPromBitdepth; + mng_ptr pPromBuf; + mng_uint8 iPromColortype; + mng_uint8 iPromBitdepth; + mng_uint8 iPromFilltype; + mng_uint32 iPromWidth; + mng_ptr pPromSrc; + mng_ptr pPromDst; +#endif + +#ifndef MNG_SKIPCHUNK_MAGN + mng_uint16 iMAGNfromid; + mng_uint16 iMAGNcurrentid; + mng_uint16 iMAGNtoid; +#endif + +#ifndef MNG_SKIPCHUNK_PAST + mng_uint16 iPASTid; + mng_int32 iPastx; /* target x/y of last PAST */ + mng_int32 iPasty; +#endif + + mng_objectp pLastseek; /* last processed ani_seek object */ + +#ifdef MNG_INCLUDE_MPNG_PROPOSAL + mng_objectp pMPNG; /* mpNG object if available */ +#endif +#ifdef MNG_INCLUDE_ANG_PROPOSAL + mng_objectp pANG; /* ANG object if available */ +#endif + +#endif /* MNG_SUPPORT_DISPLAY */ + +#ifdef MNG_INCLUDE_ZLIB + z_stream sZlib; /* zlib (de)compression variables */ + + mng_int32 iZlevel; /* zlib compression parameters */ + mng_int32 iZmethod; + mng_int32 iZwindowbits; + mng_int32 iZmemlevel; + mng_int32 iZstrategy; + + mng_uint32 iMaxIDAT; /* maximum size of IDAT data */ + + mng_bool bInflating; /* indicates "inflate" in progress */ + mng_bool bDeflating; /* indicates "deflate" in progress */ +#endif /* MNG_INCLUDE_ZLIB */ + +#ifdef MNG_INCLUDE_JNG + mngjpeg_dctmethod eJPEGdctmethod; /* IJG compression variables */ + mng_int32 iJPEGquality; + mng_int32 iJPEGsmoothing; + mng_bool bJPEGcompressprogr; + mng_bool bJPEGcompressopt; + + mng_uint32 iMaxJDAT; /* maximum size of JDAT/JDAA data */ + + mngjpeg_compp pJPEGcinfo; /* compression structure */ + mngjpeg_errorp pJPEGcerr; /* error-manager compress */ + + mngjpeg_decompp pJPEGdinfo; /* decompression structure (JDAT) */ + mngjpeg_errorp pJPEGderr; /* error-manager decompress (JDAT) */ + mngjpeg_sourcep pJPEGdsrc; /* source-manager decompress (JDAT) */ + + mngjpeg_decompp pJPEGdinfo2; /* decompression structure (JDAA) */ + mngjpeg_errorp pJPEGderr2; /* error-manager decompress (JDAA) */ + mngjpeg_sourcep pJPEGdsrc2; /* source-manager decompress (JDAA) */ + + mng_uint8p pJPEGbuf; /* buffer for JPEG (de)compression (JDAT) */ + mng_uint32 iJPEGbufmax; /* allocated space for buffer (JDAT) */ + mng_uint8p pJPEGcurrent; /* current pointer into buffer (JDAT) */ + mng_uint32 iJPEGbufremain; /* remaining bytes in buffer (JDAT) */ + mng_uint32 iJPEGtoskip; /* bytes to skip on next input-block (JDAT) */ + + mng_uint8p pJPEGbuf2; /* buffer for JPEG (de)compression (JDAA) */ + mng_uint32 iJPEGbufmax2; /* allocated space for buffer (JDAA) */ + mng_uint8p pJPEGcurrent2; /* current pointer into buffer (JDAA) */ + mng_uint32 iJPEGbufremain2; /* remaining bytes in buffer (JDAA) */ + mng_uint32 iJPEGtoskip2; /* bytes to skip on next input-block (JDAA) */ + + mng_uint8p pJPEGrow; /* buffer for a JPEG row of samples (JDAT) */ + mng_uint32 iJPEGrowlen; + + mng_uint8p pJPEGrow2; /* buffer for a JPEG row of samples (JDAA) */ + mng_uint32 iJPEGrowlen2; + + mng_bool bJPEGcompress; /* indicates "compress" initialized */ + + mng_bool bJPEGdecompress; /* indicates "decompress" initialized (JDAT) */ + mng_bool bJPEGhasheader; /* indicates "readheader" succeeded (JDAT) */ + mng_bool bJPEGdecostarted; /* indicates "decompress" started (JDAT) */ + mng_bool bJPEGscanstarted; /* indicates "first scan" started (JDAT) */ + mng_bool bJPEGscanending; /* indicates "finish_output" suspended (JDAT) */ + mng_bool bJPEGprogressive; /* indicates a progressive image (JDAT) */ + + mng_bool bJPEGdecompress2; /* indicates "decompress" initialized (JDAA) */ + mng_bool bJPEGhasheader2; /* indicates "readheader" succeeded (JDAA) */ + mng_bool bJPEGdecostarted2; /* indicates "decompress" started (JDAA) */ + mng_bool bJPEGscanstarted2; /* indicates "first scan" started (JDAA) */ + mng_bool bJPEGprogressive2; /* indicates a progressive image (JDAA) */ + + mng_fptr fStorerow2; /* internal callback to store an + uncompressed/unfiltered row of JPEG-data (JDAT) */ + + mng_fptr fStorerow3; /* internal callback to store an + uncompressed/unfiltered row of JPEG-data (JDAA) */ + + mng_uint32 iJPEGrow; /* row-number for current JPEG row */ + mng_uint32 iJPEGalpharow; /* nr. of rows filled with alpha */ + mng_uint32 iJPEGrgbrow; /* nr. of rows filled with 'color'-info */ + mng_uint32 iJPEGdisprow; /* nr. of rows already displayed "on-the-fly" */ + +#if defined(MNG_USE_SETJMP) && defined (MNG_INCLUDE_IJG6B) + jmp_buf sErrorbuf; /* setjmp/longjmp buffer (error-recovery) */ +#endif + +#endif /* MNG_INCLUDE_JNG */ + +#ifndef MNG_USE_ZLIB_CRC + mng_uint32 aCRCtable [256]; /* CRC prefab table */ + mng_bool bCRCcomputed; /* "has been built" indicator */ +#endif + +#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT + png_imgtype ePng_imgtype; +#endif + +#if defined(MNG_NO_1_2_4BIT_SUPPORT) || defined(MNG_NO_16BIT_SUPPORT) + mng_uint8 iPNGdepth; /* Real input depth */ + mng_uint8 iPNGmult; +#endif + +#ifdef MNG_OPTIMIZE_DISPLAYCALLS + mng_uint32 iRawlen; /* temp vars for display processing */ + mng_uint8p pRawdata; +#ifndef MNG_SKIPCHUNK_BASI + mng_uint16 iBASIred; + mng_uint16 iBASIgreen; + mng_uint16 iBASIblue; + mng_bool bBASIhasalpha; + mng_uint16 iBASIalpha; + mng_uint8 iBASIviewable; +#endif +#ifndef MNG_SKIPCHUNK_CLON + mng_uint16 iCLONsourceid; + mng_uint16 iCLONcloneid; + mng_uint8 iCLONclonetype; + mng_bool bCLONhasdonotshow; + mng_uint8 iCLONdonotshow; + mng_uint8 iCLONconcrete; + mng_bool bCLONhasloca; + mng_uint8 iCLONlocationtype; + mng_int32 iCLONlocationx; + mng_int32 iCLONlocationy; +#endif +#ifndef MNG_SKIPCHUNK_DISC + mng_uint32 iDISCcount; + mng_uint16p pDISCids; +#endif +#ifndef MNG_SKIPCHUNK_FRAM + mng_uint8 iTempFramemode; + mng_uint8 iTempChangedelay; + mng_uint32 iTempDelay; + mng_uint8 iTempChangetimeout; + mng_uint32 iTempTimeout; + mng_uint8 iTempChangeclipping; + mng_uint8 iTempCliptype; + mng_int32 iTempClipl; + mng_int32 iTempClipr; + mng_int32 iTempClipt; + mng_int32 iTempClipb; +#endif +#ifndef MNG_SKIPCHUNK_MOVE + mng_uint16 iMOVEfromid; + mng_uint16 iMOVEtoid; + mng_uint8 iMOVEmovetype; + mng_int32 iMOVEmovex; + mng_int32 iMOVEmovey; +#endif +#ifndef MNG_SKIPCHUNK_CLIP + mng_uint16 iCLIPfromid; + mng_uint16 iCLIPtoid; + mng_uint8 iCLIPcliptype; + mng_int32 iCLIPclipl; + mng_int32 iCLIPclipr; + mng_int32 iCLIPclipt; + mng_int32 iCLIPclipb; +#endif +#ifndef MNG_NO_DELTA_PNG + mng_uint16 iDHDRobjectid; + mng_uint8 iDHDRimagetype; + mng_uint8 iDHDRdeltatype; + mng_uint32 iDHDRblockwidth; + mng_uint32 iDHDRblockheight; + mng_uint32 iDHDRblockx; + mng_uint32 iDHDRblocky; + mng_uint8 iPROMbitdepth; + mng_uint8 iPROMcolortype; + mng_uint8 iPROMfilltype; + mng_uint8 iPPLTtype; + mng_uint32 iPPLTcount; + mng_palette8ep paPPLTindexentries; + mng_uint8p paPPLTalphaentries; + mng_uint8p paPPLTusedentries; +#endif +#ifndef MNG_SKIPCHUNK_MAGN + mng_uint16 iMAGNfirstid; + mng_uint16 iMAGNlastid; + mng_uint8 iMAGNmethodX; + mng_uint16 iMAGNmX; + mng_uint16 iMAGNmY; + mng_uint16 iMAGNmL; + mng_uint16 iMAGNmR; + mng_uint16 iMAGNmT; + mng_uint16 iMAGNmB; + mng_uint8 iMAGNmethodY; +#endif +#ifndef MNG_SKIPCHUNK_PAST + mng_uint16 iPASTtargetid; + mng_uint8 iPASTtargettype; + mng_int32 iPASTtargetx; + mng_int32 iPASTtargety; + mng_uint32 iPASTcount; + mng_ptr pPASTsources; +#endif +#endif /* MNG_OPTIMIZE_DISPLAYCALLS */ + + } mng_data; + +typedef mng_data * mng_datap; + +/* ************************************************************************** */ +/* * * */ +/* * Internal Callback-Function prototypes * */ +/* * * */ +/* ************************************************************************** */ + +typedef mng_retcode(*mng_displayrow) (mng_datap pData); +typedef mng_retcode(*mng_restbkgdrow) (mng_datap pData); +typedef mng_retcode(*mng_correctrow) (mng_datap pData); +typedef mng_retcode(*mng_retrieverow) (mng_datap pData); +typedef mng_retcode(*mng_storerow) (mng_datap pData); +typedef mng_retcode(*mng_processrow) (mng_datap pData); +typedef mng_retcode(*mng_initrowproc) (mng_datap pData); +typedef mng_retcode(*mng_differrow) (mng_datap pData); +typedef mng_retcode(*mng_scalerow) (mng_datap pData); +typedef mng_retcode(*mng_deltarow) (mng_datap pData); +typedef mng_retcode(*mng_promoterow) (mng_datap pData); +typedef mng_retcode(*mng_fliprow) (mng_datap pData); +typedef mng_retcode(*mng_tilerow) (mng_datap pData); + +typedef mng_uint8 (*mng_bitdepth_8) (mng_uint8 iB); +typedef mng_uint16 (*mng_bitdepth_16) (mng_uint8 iB); + +typedef mng_retcode(*mng_magnify_x) (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p iSrcline, + mng_uint8p iDstline); +typedef mng_retcode(*mng_magnify_y) (mng_datap pData, + mng_int32 iM, + mng_int32 iS, + mng_uint32 iWidth, + mng_uint8p iSrcline1, + mng_uint8p iSrcline2, + mng_uint8p iDstline); + +/* ************************************************************************** */ +/* * * */ +/* * Routines for swapping byte-order from and to graphic files * */ +/* * (This code is adapted from the libpng package) * */ +/* * * */ +/* ************************************************************************** */ + +#ifndef MNG_BIGENDIAN_SUPPORTED +mng_uint32 mng_get_uint32 (mng_uint8p pBuf); +mng_int32 mng_get_int32 (mng_uint8p pBuf); +mng_uint16 mng_get_uint16 (mng_uint8p pBuf); +void mng_put_uint32 (mng_uint8p pBuf, + mng_uint32 i); +void mng_put_int32 (mng_uint8p pBuf, + mng_int32 i); +void mng_put_uint16 (mng_uint8p pBuf, + mng_uint16 i); +#else /* MNG_BIGENDIAN_SUPPORTED */ +#define mng_get_uint32(P) *(mng_uint32p)(P) +#define mng_get_int32(P) *(mng_int32p)(P) +#define mng_get_uint16(P) *(mng_uint16p)(P) +#define mng_put_uint32(P,I) *(mng_uint32p)(P) = (I) +#define mng_put_int32(P,I) *(mng_int32p)(P) = (I) +#define mng_put_uint16(P,I) *(mng_uint16p)(P) = (I) +#endif /* MNG_BIGENDIAN_SUPPORTED */ + +/* ************************************************************************** */ +/* * * */ +/* * Some handy(?) macro definitions * */ +/* * * */ +/* ************************************************************************** */ + +#define MAX_COORD(a, b) (((a) > (b)) ? (a) : (b)) +#define MIN_COORD(a, b) (((a) < (b)) ? (a) : (b)) + +/* ************************************************************************** */ + +#endif /* _libmng_data_h_ */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ diff --git a/Engine/lib/lmng/libmng_display.c b/Engine/lib/lmng/libmng_display.c new file mode 100644 index 000000000..adfb91d0e --- /dev/null +++ b/Engine/lib/lmng/libmng_display.c @@ -0,0 +1,7135 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_display.c copyright (c) 2000-2007 G.Juyn * */ +/* * version : 1.0.10 * */ +/* * * */ +/* * purpose : Display management (implementation) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : implementation of the display management routines * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed strict-ANSI stuff * */ +/* * 0.5.1 - 05/11/2000 - G.Juyn * */ +/* * - added callback error-reporting support * */ +/* * - fixed frame_delay misalignment * */ +/* * 0.5.1 - 05/12/2000 - G.Juyn * */ +/* * - added sanity check for frozen status * */ +/* * - changed trace to macro for callback error-reporting * */ +/* * 0.5.1 - 05/13/2000 - G.Juyn * */ +/* * - changed display_mend to reset state to initial or SAVE * */ +/* * - added eMNGma hack (will be removed in 1.0.0 !!!) * */ +/* * - added TERM animation object pointer (easier reference) * */ +/* * - added process_save & process_seek routines * */ +/* * 0.5.1 - 05/14/2000 - G.Juyn * */ +/* * - added save_state and restore_state for SAVE/SEEK/TERM * */ +/* * processing * */ +/* * * */ +/* * 0.5.2 - 05/20/2000 - G.Juyn * */ +/* * - added JNG support (JHDR/JDAT) * */ +/* * 0.5.2 - 05/23/2000 - G.Juyn * */ +/* * - fixed problem with DEFI clipping * */ +/* * 0.5.2 - 05/30/2000 - G.Juyn * */ +/* * - added delta-image support (DHDR,PROM,IPNG,IJNG) * */ +/* * 0.5.2 - 05/31/2000 - G.Juyn * */ +/* * - fixed pointer confusion (contributed by Tim Rowley) * */ +/* * 0.5.2 - 06/03/2000 - G.Juyn * */ +/* * - fixed makeup for Linux gcc compile * */ +/* * 0.5.2 - 06/05/2000 - G.Juyn * */ +/* * - added support for RGB8_A8 canvasstyle * */ +/* * 0.5.2 - 06/09/2000 - G.Juyn * */ +/* * - fixed timer-handling to run with Mozilla (Tim Rowley) * */ +/* * 0.5.2 - 06/10/2000 - G.Juyn * */ +/* * - fixed some compilation-warnings (contrib Jason Morris) * */ +/* * * */ +/* * 0.5.3 - 06/12/2000 - G.Juyn * */ +/* * - fixed display of stored JNG images * */ +/* * 0.5.3 - 06/13/2000 - G.Juyn * */ +/* * - fixed problem with BASI-IEND as object 0 * */ +/* * 0.5.3 - 06/16/2000 - G.Juyn * */ +/* * - changed progressive-display processing * */ +/* * 0.5.3 - 06/17/2000 - G.Juyn * */ +/* * - changed delta-image processing * */ +/* * 0.5.3 - 06/20/2000 - G.Juyn * */ +/* * - fixed some minor stuff * */ +/* * 0.5.3 - 06/21/2000 - G.Juyn * */ +/* * - added speed-modifier to timing routine * */ +/* * 0.5.3 - 06/22/2000 - G.Juyn * */ +/* * - added support for PPLT chunk processing * */ +/* * 0.5.3 - 06/29/2000 - G.Juyn * */ +/* * - swapped refresh parameters * */ +/* * * */ +/* * 0.9.0 - 06/30/2000 - G.Juyn * */ +/* * - changed refresh parameters to 'x,y,width,height' * */ +/* * * */ +/* * 0.9.1 - 07/07/2000 - G.Juyn * */ +/* * - implemented support for freeze/reset/resume & go_xxxx * */ +/* * 0.9.1 - 07/08/2000 - G.Juyn * */ +/* * - added support for improved timing * */ +/* * 0.9.1 - 07/14/2000 - G.Juyn * */ +/* * - changed EOF processing behavior * */ +/* * - fixed TERM delay processing * */ +/* * 0.9.1 - 07/15/2000 - G.Juyn * */ +/* * - fixed freeze & reset processing * */ +/* * 0.9.1 - 07/16/2000 - G.Juyn * */ +/* * - fixed storage of images during mng_read() * */ +/* * - fixed support for mng_display() after mng_read() * */ +/* * 0.9.1 - 07/24/2000 - G.Juyn * */ +/* * - fixed reading of still-images * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 0.9.3 - 08/07/2000 - G.Juyn * */ +/* * - B111300 - fixup for improved portability * */ +/* * 0.9.3 - 08/21/2000 - G.Juyn * */ +/* * - fixed TERM processing delay of 0 msecs * */ +/* * 0.9.3 - 08/26/2000 - G.Juyn * */ +/* * - added MAGN chunk * */ +/* * 0.9.3 - 09/10/2000 - G.Juyn * */ +/* * - fixed problem with no refresh after TERM * */ +/* * - fixed DEFI behavior * */ +/* * 0.9.3 - 09/16/2000 - G.Juyn * */ +/* * - fixed timing & refresh behavior for single PNG/JNG * */ +/* * 0.9.3 - 09/19/2000 - G.Juyn * */ +/* * - refixed timing & refresh behavior for single PNG/JNG * */ +/* * 0.9.3 - 10/02/2000 - G.Juyn * */ +/* * - fixed timing again (this is getting boring...) * */ +/* * - refixed problem with no refresh after TERM * */ +/* * 0.9.3 - 10/16/2000 - G.Juyn * */ +/* * - added JDAA chunk * */ +/* * 0.9.3 - 10/17/2000 - G.Juyn * */ +/* * - fixed support for bKGD * */ +/* * 0.9.3 - 10/18/2000 - G.Juyn * */ +/* * - fixed delta-processing behavior * */ +/* * 0.9.3 - 10/19/2000 - G.Juyn * */ +/* * - added storage for pixel-/alpha-sampledepth for delta's * */ +/* * 0.9.3 - 10/27/2000 - G.Juyn * */ +/* * - fixed separate read() & display() processing * */ +/* * * */ +/* * 0.9.4 - 10/31/2000 - G.Juyn * */ +/* * - fixed possible loop in display_resume() (Thanks Vova!) * */ +/* * 0.9.4 - 11/20/2000 - G.Juyn * */ +/* * - fixed unwanted repetition in mng_readdisplay() * */ +/* * 0.9.4 - 11/24/2000 - G.Juyn * */ +/* * - moved restore of object 0 to libmng_display * */ +/* * - added restore of object 0 to TERM processing !!! * */ +/* * - fixed TERM delay processing * */ +/* * - fixed TERM end processing (count = 0) * */ +/* * 0.9.4 - 12/16/2000 - G.Juyn * */ +/* * - fixed mixup of data- & function-pointers (thanks Dimitri)* */ +/* * 0.9.4 - 1/18/2001 - G.Juyn * */ +/* * - removed test filter-methods 1 & 65 * */ +/* * - set default level-set for filtertype=64 to all zeroes * */ +/* * * */ +/* * 0.9.5 - 1/20/2001 - G.Juyn * */ +/* * - fixed compiler-warnings Mozilla (thanks Tim) * */ +/* * 0.9.5 - 1/23/2001 - G.Juyn * */ +/* * - fixed timing-problem with switching framing_modes * */ +/* * * */ +/* * 1.0.1 - 02/08/2001 - G.Juyn * */ +/* * - added MEND processing callback * */ +/* * 1.0.1 - 02/13/2001 - G.Juyn * */ +/* * - fixed first FRAM_MODE=4 timing problem * */ +/* * 1.0.1 - 04/21/2001 - G.Juyn * */ +/* * - fixed memory-leak for JNGs with alpha (Thanks Gregg!) * */ +/* * - added BGRA8 canvas with premultiplied alpha * */ +/* * * */ +/* * 1.0.2 - 06/25/2001 - G.Juyn * */ +/* * - fixed memory-leak with delta-images (Thanks Michael!) * */ +/* * * */ +/* * 1.0.5 - 08/15/2002 - G.Juyn * */ +/* * - completed PROM support * */ +/* * - completed delta-image support * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * 1.0.5 - 09/13/2002 - G.Juyn * */ +/* * - fixed read/write of MAGN chunk * */ +/* * 1.0.5 - 09/15/2002 - G.Juyn * */ +/* * - fixed LOOP iteration=0 special case * */ +/* * 1.0.5 - 09/19/2002 - G.Juyn * */ +/* * - fixed color-correction for restore-background handling * */ +/* * - optimized restore-background for bKGD cases * */ +/* * - cleaned up some old stuff * */ +/* * 1.0.5 - 09/20/2002 - G.Juyn * */ +/* * - finished support for BACK image & tiling * */ +/* * - added support for PAST * */ +/* * 1.0.5 - 09/22/2002 - G.Juyn * */ +/* * - added bgrx8 canvas (filler byte) * */ +/* * 1.0.5 - 10/05/2002 - G.Juyn * */ +/* * - fixed dropping mix of frozen/unfrozen objects * */ +/* * 1.0.5 - 10/07/2002 - G.Juyn * */ +/* * - added proposed change in handling of TERM- & if-delay * */ +/* * - added another fix for misplaced TERM chunk * */ +/* * - completed support for condition=2 in TERM chunk * */ +/* * 1.0.5 - 10/18/2002 - G.Juyn * */ +/* * - fixed clipping-problem with BACK tiling (Thanks Sakura!) * */ +/* * 1.0.5 - 10/20/2002 - G.Juyn * */ +/* * - fixed processing for multiple objects in MAGN * */ +/* * - fixed display of visible target of PAST operation * */ +/* * 1.0.5 - 10/30/2002 - G.Juyn * */ +/* * - modified TERM/MEND processing for max(1, TERM_delay, * */ +/* * interframe_delay) * */ +/* * 1.0.5 - 11/04/2002 - G.Juyn * */ +/* * - fixed layer- & frame-counting during read() * */ +/* * - fixed goframe/golayer/gotime processing * */ +/* * 1.0.5 - 01/19/2003 - G.Juyn * */ +/* * - B654627 - fixed SEGV when no gettickcount callback * */ +/* * - B664383 - fixed typo * */ +/* * - finalized changes in TERM/final_delay to elected proposal* */ +/* * * */ +/* * 1.0.6 - 05/11/2003 - G. Juyn * */ +/* * - added conditionals around canvas update routines * */ +/* * 1.0.6 - 05/25/2003 - G.R-P * */ +/* * - added MNG_SKIPCHUNK_cHNK footprint optimizations * */ +/* * 1.0.6 - 07/07/2003 - G.R-P * */ +/* * - added conditionals around some JNG-supporting code * */ +/* * - added conditionals around 16-bit supporting code * */ +/* * - reversed some loops to use decrementing counter * */ +/* * - combined init functions into one function * */ +/* * 1.0.6 - 07/10/2003 - G.R-P * */ +/* * - replaced nested switches with simple init setup function * */ +/* * 1.0.6 - 07/29/2003 - G.R-P * */ +/* * - added conditionals around PAST chunk support * */ +/* * 1.0.6 - 08/17/2003 - G.R-P * */ +/* * - added conditionals around non-VLC chunk support * */ +/* * * */ +/* * 1.0.7 - 11/27/2003 - R.A * */ +/* * - added CANVAS_RGB565 and CANVAS_BGR565 * */ +/* * 1.0.7 - 12/06/2003 - R.A * */ +/* * - added CANVAS_RGBA565 and CANVAS_BGRA565 * */ +/* * 1.0.7 - 01/25/2004 - J.S * */ +/* * - added premultiplied alpha canvas' for RGBA, ARGB, ABGR * */ +/* * * */ +/* * 1.0.8 - 03/31/2004 - G.Juyn * */ +/* * - fixed problem with PAST usage where source > dest * */ +/* * 1.0.8 - 05/04/2004 - G.R-P. * */ +/* * - fixed misplaced 16-bit conditionals * */ +/* * * */ +/* * 1.0.9 - 09/18/2004 - G.R-P. * */ +/* * - revised some SKIPCHUNK conditionals * */ +/* * 1.0.9 - 10/10/2004 - G.R-P. * */ +/* * - added MNG_NO_1_2_4BIT_SUPPORT * */ +/* * 1.0.9 - 10/14/2004 - G.Juyn * */ +/* * - added bgr565_a8 canvas-style (thanks to J. Elvander) * */ +/* * 1.0.9 - 12/11/2004 - G.Juyn * */ +/* * - added conditional MNG_OPTIMIZE_DISPLAYCALLS * */ +/* * 1.0.9 - 12/20/2004 - G.Juyn * */ +/* * - cleaned up macro-invocations (thanks to D. Airlie) * */ +/* * * */ +/* * 1.0.10 - 07/06/2005 - G.R-P. * */ +/* * - added more SKIPCHUNK conditionals * */ +/* * 1.0.10 - 12/28/2005 - G.R-P. * */ +/* * - added missing SKIPCHUNK_MAGN conditional * */ +/* * 1.0.10 - 03/07/2006 - (thanks to W. Manthey) * */ +/* * - added CANVAS_RGB555 and CANVAS_BGR555 * */ +/* * 1.0.10 - 04/08/2007 - G.Juyn * */ +/* * - fixed several compiler warnings * */ +/* * 1.0.10 - 04/08/2007 - G.Juyn * */ +/* * - added support for mPNG proposal * */ +/* * 1.0.10 - 04/12/2007 - G.Juyn * */ +/* * - added support for ANG proposal * */ +/* * * */ +/* ************************************************************************** */ + +#include "libmng.h" +#include "libmng_data.h" +#include "libmng_error.h" +#include "libmng_trace.h" +#ifdef __BORLANDC__ +#pragma hdrstop +#endif +#include "libmng_chunks.h" +#include "libmng_objects.h" +#include "libmng_object_prc.h" +#include "libmng_memory.h" +#include "libmng_zlib.h" +#include "libmng_jpeg.h" +#include "libmng_cms.h" +#include "libmng_pixels.h" +#include "libmng_display.h" + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_DISPLAY_PROCS + +/* ************************************************************************** */ + +MNG_LOCAL mng_retcode set_delay (mng_datap pData, + mng_uint32 iInterval) +{ + if (!iInterval) /* at least 1 msec please! */ + iInterval = 1; + + if (pData->bRunning) /* only when really displaying */ + if (!pData->fSettimer ((mng_handle)pData, iInterval)) + MNG_ERROR (pData, MNG_APPTIMERERROR); + +#ifdef MNG_SUPPORT_DYNAMICMNG + if ((!pData->bDynamic) || (pData->bRunning)) +#else + if (pData->bRunning) +#endif + pData->bTimerset = MNG_TRUE; /* and indicate so */ + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +MNG_LOCAL mng_uint32 calculate_delay (mng_datap pData, + mng_uint32 iDelay) +{ + mng_uint32 iTicks = pData->iTicks; + mng_uint32 iWaitfor = 1; /* default non-MNG delay */ + + if (!iTicks) /* tick_count not specified ? */ + if (pData->eImagetype == mng_it_mng) + iTicks = 1000; + + if (iTicks) + { + switch (pData->iSpeed) /* honor speed modifier */ + { + case mng_st_fast : + { + iWaitfor = (mng_uint32)(( 500 * iDelay) / iTicks); + break; + } + case mng_st_slow : + { + iWaitfor = (mng_uint32)((3000 * iDelay) / iTicks); + break; + } + case mng_st_slowest : + { + iWaitfor = (mng_uint32)((8000 * iDelay) / iTicks); + break; + } + default : + { + iWaitfor = (mng_uint32)((1000 * iDelay) / iTicks); + } + } + } + + return iWaitfor; +} + +/* ************************************************************************** */ +/* * * */ +/* * Progressive display refresh - does the call to the refresh callback * */ +/* * and sets the timer to allow the app to perform the actual refresh to * */ +/* * the screen (eg. process its main message-loop) * */ +/* * * */ +/* ************************************************************************** */ + +mng_retcode mng_display_progressive_refresh (mng_datap pData, + mng_uint32 iInterval) +{ + { /* let the app refresh first ? */ + if ((pData->bRunning) && (!pData->bSkipping) && + (pData->iUpdatetop < pData->iUpdatebottom) && (pData->iUpdateleft < pData->iUpdateright)) + { + if (!pData->fRefresh (((mng_handle)pData), + pData->iUpdateleft, pData->iUpdatetop, + pData->iUpdateright - pData->iUpdateleft, + pData->iUpdatebottom - pData->iUpdatetop)) + MNG_ERROR (pData, MNG_APPMISCERROR); + + pData->iUpdateleft = 0; /* reset update-region */ + pData->iUpdateright = 0; + pData->iUpdatetop = 0; + pData->iUpdatebottom = 0; /* reset refreshneeded indicator */ + pData->bNeedrefresh = MNG_FALSE; + /* interval requested ? */ + if ((!pData->bFreezing) && (iInterval)) + { /* setup the timer */ + mng_retcode iRetcode = set_delay (pData, iInterval); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + } + } + + return MNG_NOERROR; +} + +/* ************************************************************************** */ +/* * * */ +/* * Generic display routines * */ +/* * * */ +/* ************************************************************************** */ + +MNG_LOCAL mng_retcode interframe_delay (mng_datap pData) +{ + mng_uint32 iWaitfor = 0; + mng_uint32 iInterval; + mng_uint32 iRuninterval; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INTERFRAME_DELAY, MNG_LC_START); +#endif + + { +#ifndef MNG_SKIPCHUNK_FRAM + if (pData->iFramedelay > 0) /* real delay ? */ + { /* let the app refresh first ? */ + if ((pData->bRunning) && (!pData->bSkipping) && + (pData->iUpdatetop < pData->iUpdatebottom) && (pData->iUpdateleft < pData->iUpdateright)) + if (!pData->fRefresh (((mng_handle)pData), + pData->iUpdateleft, pData->iUpdatetop, + pData->iUpdateright - pData->iUpdateleft, + pData->iUpdatebottom - pData->iUpdatetop)) + MNG_ERROR (pData, MNG_APPMISCERROR); + + pData->iUpdateleft = 0; /* reset update-region */ + pData->iUpdateright = 0; + pData->iUpdatetop = 0; + pData->iUpdatebottom = 0; /* reset refreshneeded indicator */ + pData->bNeedrefresh = MNG_FALSE; + +#ifndef MNG_SKIPCHUNK_TERM + if (pData->bOnlyfirstframe) /* only processing first frame after TERM ? */ + { + pData->iFramesafterTERM++; + /* did we do a frame yet ? */ + if (pData->iFramesafterTERM > 1) + { /* then that's it; just stop right here ! */ + pData->pCurraniobj = MNG_NULL; + pData->bRunning = MNG_FALSE; + + return MNG_NOERROR; + } + } +#endif + + if (pData->fGettickcount) + { /* get current tickcount */ + pData->iRuntime = pData->fGettickcount ((mng_handle)pData); + /* calculate interval since last sync-point */ + if (pData->iRuntime < pData->iSynctime) + iRuninterval = pData->iRuntime + ~pData->iSynctime + 1; + else + iRuninterval = pData->iRuntime - pData->iSynctime; + /* calculate actual run-time */ + if (pData->iRuntime < pData->iStarttime) + pData->iRuntime = pData->iRuntime + ~pData->iStarttime + 1; + else + pData->iRuntime = pData->iRuntime - pData->iStarttime; + } + else + { + iRuninterval = 0; + } + + iWaitfor = calculate_delay (pData, pData->iFramedelay); + + if (iWaitfor > iRuninterval) /* delay necessary ? */ + iInterval = iWaitfor - iRuninterval; + else + iInterval = 1; /* force app to process messageloop */ + /* set the timer ? */ + if (((pData->bRunning) || (pData->bSearching) || (pData->bReading)) && + (!pData->bSkipping)) + { + iRetcode = set_delay (pData, iInterval); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + } + + if (!pData->bSkipping) /* increase frametime in advance */ + pData->iFrametime = pData->iFrametime + iWaitfor; + /* setup for next delay */ + pData->iFramedelay = pData->iNextdelay; +#endif + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INTERFRAME_DELAY, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +MNG_LOCAL void set_display_routine (mng_datap pData) +{ /* actively running ? */ + if (((pData->bRunning) || (pData->bSearching)) && (!pData->bSkipping)) + { + switch (pData->iCanvasstyle) /* determine display routine */ + { +#ifndef MNG_SKIPCANVAS_RGB8 + case MNG_CANVAS_RGB8 : { pData->fDisplayrow = (mng_fptr)mng_display_rgb8; break; } +#endif +#ifndef MNG_SKIPCANVAS_RGBA8 + case MNG_CANVAS_RGBA8 : { pData->fDisplayrow = (mng_fptr)mng_display_rgba8; break; } +#endif +#ifndef MNG_SKIPCANVAS_RGBA8_PM + case MNG_CANVAS_RGBA8_PM: { pData->fDisplayrow = (mng_fptr)mng_display_rgba8_pm; break; } +#endif +#ifndef MNG_SKIPCANVAS_ARGB8 + case MNG_CANVAS_ARGB8 : { pData->fDisplayrow = (mng_fptr)mng_display_argb8; break; } +#endif +#ifndef MNG_SKIPCANVAS_ARGB8_PM + case MNG_CANVAS_ARGB8_PM: { pData->fDisplayrow = (mng_fptr)mng_display_argb8_pm; break; } +#endif +#ifndef MNG_SKIPCANVAS_RGB8_A8 + case MNG_CANVAS_RGB8_A8 : { pData->fDisplayrow = (mng_fptr)mng_display_rgb8_a8; break; } +#endif +#ifndef MNG_SKIPCANVAS_BGR8 + case MNG_CANVAS_BGR8 : { pData->fDisplayrow = (mng_fptr)mng_display_bgr8; break; } +#endif +#ifndef MNG_SKIPCANVAS_BGRX8 + case MNG_CANVAS_BGRX8 : { pData->fDisplayrow = (mng_fptr)mng_display_bgrx8; break; } +#endif +#ifndef MNG_SKIPCANVAS_BGRA8 + case MNG_CANVAS_BGRA8 : { pData->fDisplayrow = (mng_fptr)mng_display_bgra8; break; } +#endif +#ifndef MNG_SKIPCANVAS_BGRA8_PM + case MNG_CANVAS_BGRA8_PM: { pData->fDisplayrow = (mng_fptr)mng_display_bgra8_pm; break; } +#endif +#ifndef MNG_SKIPCANVAS_ABGR8 + case MNG_CANVAS_ABGR8 : { pData->fDisplayrow = (mng_fptr)mng_display_abgr8; break; } +#endif +#ifndef MNG_SKIPCANVAS_ABGR8_PM + case MNG_CANVAS_ABGR8_PM: { pData->fDisplayrow = (mng_fptr)mng_display_abgr8_pm; break; } +#endif +#ifndef MNG_SKIPCANVAS_RGB565 + case MNG_CANVAS_RGB565 : { pData->fDisplayrow = (mng_fptr)mng_display_rgb565; break; } +#endif +#ifndef MNG_SKIPCANVAS_RGBA565 + case MNG_CANVAS_RGBA565 : { pData->fDisplayrow = (mng_fptr)mng_display_rgba565; break; } +#endif +#ifndef MNG_SKIPCANVAS_BGR565 + case MNG_CANVAS_BGR565 : { pData->fDisplayrow = (mng_fptr)mng_display_bgr565; break; } +#endif +#ifndef MNG_SKIPCANVAS_BGRA565 + case MNG_CANVAS_BGRA565 : { pData->fDisplayrow = (mng_fptr)mng_display_bgra565; break; } +#endif +#ifndef MNG_SKIPCANVAS_BGR565_A8 + case MNG_CANVAS_BGR565_A8 : { pData->fDisplayrow = (mng_fptr)mng_display_bgr565_a8; break; } +#endif +#ifndef MNG_SKIPCANVAS_RGB555 + case MNG_CANVAS_RGB555 : { pData->fDisplayrow = (mng_fptr)mng_display_rgb555; break; } +#endif +#ifndef MNG_SKIPCANVAS_BGR555 + case MNG_CANVAS_BGR555 : { pData->fDisplayrow = (mng_fptr)mng_display_bgr555; break; } +#endif + +#ifndef MNG_NO_16BIT_SUPPORT +/* case MNG_CANVAS_RGB16 : { pData->fDisplayrow = (mng_fptr)mng_display_rgb16; break; } */ +/* case MNG_CANVAS_RGBA16 : { pData->fDisplayrow = (mng_fptr)mng_display_rgba16; break; } */ +/* case MNG_CANVAS_ARGB16 : { pData->fDisplayrow = (mng_fptr)mng_display_argb16; break; } */ +/* case MNG_CANVAS_BGR16 : { pData->fDisplayrow = (mng_fptr)mng_display_bgr16; break; } */ +/* case MNG_CANVAS_BGRA16 : { pData->fDisplayrow = (mng_fptr)mng_display_bgra16; break; } */ +/* case MNG_CANVAS_ABGR16 : { pData->fDisplayrow = (mng_fptr)mng_display_abgr16; break; } */ +#endif +/* case MNG_CANVAS_INDEX8 : { pData->fDisplayrow = (mng_fptr)mng_display_index8; break; } */ +/* case MNG_CANVAS_INDEXA8 : { pData->fDisplayrow = (mng_fptr)mng_display_indexa8; break; } */ +/* case MNG_CANVAS_AINDEX8 : { pData->fDisplayrow = (mng_fptr)mng_display_aindex8; break; } */ +/* case MNG_CANVAS_GRAY8 : { pData->fDisplayrow = (mng_fptr)mng_display_gray8; break; } */ +/* case MNG_CANVAS_AGRAY8 : { pData->fDisplayrow = (mng_fptr)mng_display_agray8; break; } */ +/* case MNG_CANVAS_GRAYA8 : { pData->fDisplayrow = (mng_fptr)mng_display_graya8; break; } */ +#ifndef MNG_NO_16BIT_SUPPORT +/* case MNG_CANVAS_GRAY16 : { pData->fDisplayrow = (mng_fptr)mng_display_gray16; break; } */ +/* case MNG_CANVAS_GRAYA16 : { pData->fDisplayrow = (mng_fptr)mng_display_graya16; break; } */ +/* case MNG_CANVAS_AGRAY16 : { pData->fDisplayrow = (mng_fptr)mng_display_agray16; break; } */ +#endif +/* case MNG_CANVAS_DX15 : { pData->fDisplayrow = (mng_fptr)mng_display_dx15; break; } */ +/* case MNG_CANVAS_DX16 : { pData->fDisplayrow = (mng_fptr)mng_display_dx16; break; } */ + } + } + + return; +} + +/* ************************************************************************** */ + +MNG_LOCAL mng_retcode load_bkgdlayer (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_LOAD_BKGDLAYER, MNG_LC_START); +#endif + /* actively running ? */ + if (((pData->bRunning) || (pData->bSearching)) && (!pData->bSkipping)) + { + mng_int32 iY; + mng_retcode iRetcode; + mng_bool bColorcorr = MNG_FALSE; + /* save values */ + mng_int32 iDestl = pData->iDestl; + mng_int32 iDestr = pData->iDestr; + mng_int32 iDestt = pData->iDestt; + mng_int32 iDestb = pData->iDestb; + mng_int32 iSourcel = pData->iSourcel; + mng_int32 iSourcer = pData->iSourcer; + mng_int32 iSourcet = pData->iSourcet; + mng_int32 iSourceb = pData->iSourceb; + mng_int8 iPass = pData->iPass; + mng_int32 iRow = pData->iRow; + mng_int32 iRowinc = pData->iRowinc; + mng_int32 iCol = pData->iCol; + mng_int32 iColinc = pData->iColinc; + mng_int32 iRowsamples = pData->iRowsamples; + mng_int32 iRowsize = pData->iRowsize; + mng_uint8p pPrevrow = pData->pPrevrow; + mng_uint8p pRGBArow = pData->pRGBArow; + mng_bool bIsRGBA16 = pData->bIsRGBA16; + mng_bool bIsOpaque = pData->bIsOpaque; + mng_fptr fCorrectrow = pData->fCorrectrow; + mng_fptr fDisplayrow = pData->fDisplayrow; + mng_fptr fRetrieverow = pData->fRetrieverow; + mng_objectp pCurrentobj = pData->pCurrentobj; + mng_objectp pRetrieveobj = pData->pRetrieveobj; + + pData->iDestl = 0; /* determine clipping region */ + pData->iDestt = 0; + pData->iDestr = pData->iWidth; + pData->iDestb = pData->iHeight; + +#ifndef MNG_SKIPCHUNK_FRAM + if (pData->bFrameclipping) /* frame clipping specified ? */ + { + pData->iDestl = MAX_COORD (pData->iDestl, pData->iFrameclipl); + pData->iDestt = MAX_COORD (pData->iDestt, pData->iFrameclipt); + pData->iDestr = MIN_COORD (pData->iDestr, pData->iFrameclipr); + pData->iDestb = MIN_COORD (pData->iDestb, pData->iFrameclipb); + } +#endif + /* anything to clear ? */ + if ((pData->iDestr >= pData->iDestl) && (pData->iDestb >= pData->iDestt)) + { + pData->iPass = -1; /* these are the object's dimensions now */ + pData->iRow = 0; + pData->iRowinc = 1; + pData->iCol = 0; + pData->iColinc = 1; + pData->iRowsamples = pData->iWidth; + pData->iRowsize = pData->iRowsamples << 2; + pData->bIsRGBA16 = MNG_FALSE; /* let's keep it simple ! */ + pData->bIsOpaque = MNG_TRUE; + + pData->iSourcel = 0; /* source relative to destination */ + pData->iSourcer = pData->iDestr - pData->iDestl; + pData->iSourcet = 0; + pData->iSourceb = pData->iDestb - pData->iDestt; + + set_display_routine (pData); /* determine display routine */ + /* default restore using preset BG color */ + pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_bgcolor; + +#ifndef MNG_SKIPCHUNK_bKGD + if (((pData->eImagetype == mng_it_png) || (pData->eImagetype == mng_it_jng)) && + (pData->bUseBKGD)) + { /* prefer bKGD in PNG/JNG */ + if (!pData->pCurrentobj) + pData->pCurrentobj = pData->pObjzero; + + if (((mng_imagep)pData->pCurrentobj)->pImgbuf->bHasBKGD) + { + pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_bkgd; + bColorcorr = MNG_TRUE; + } + } +#endif + + if (pData->fGetbkgdline) /* background-canvas-access callback set ? */ + { + switch (pData->iBkgdstyle) + { +#ifndef MNG_SKIPCANVAS_RGB8 + case MNG_CANVAS_RGB8 : { pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_rgb8; break; } +#endif +#ifndef MNG_SKIPCANVAS_BGR8 + case MNG_CANVAS_BGR8 : { pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_bgr8; break; } +#endif +#ifndef MNG_SKIPCANVAS_BGRX8 + case MNG_CANVAS_BGRX8 : { pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_bgrx8; break; } +#endif +#ifndef MNG_SKIPCANVAS_BGR565 + case MNG_CANVAS_BGR565 : { pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_bgr565; break; } +#endif +#ifndef MNG_SKIPCANVAS_RGB565 + case MNG_CANVAS_RGB565 : { pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_rgb565; break; } +#endif +#ifndef MNG_NO_16BIT_SUPPORT + /* case MNG_CANVAS_RGB16 : { pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_rgb16; break; } */ + /* case MNG_CANVAS_BGR16 : { pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_bgr16; break; } */ +#endif + /* case MNG_CANVAS_INDEX8 : { pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_index8; break; } */ + /* case MNG_CANVAS_GRAY8 : { pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_gray8; break; } */ +#ifndef MNG_NO_16BIT_SUPPORT + /* case MNG_CANVAS_GRAY16 : { pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_gray16; break; } */ +#endif + /* case MNG_CANVAS_DX15 : { pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_dx15; break; } */ + /* case MNG_CANVAS_DX16 : { pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_dx16; break; } */ + } + } + +#ifndef MNG_SKIPCHUNK_BACK + if (pData->bHasBACK) + { /* background image ? */ + if ((pData->iBACKmandatory & 0x02) && (pData->iBACKimageid)) + { + pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_backcolor; + bColorcorr = MNG_TRUE; + } + else /* background color ? */ + if (pData->iBACKmandatory & 0x01) + { + pData->fRestbkgdrow = (mng_fptr)mng_restore_bkgd_backcolor; + bColorcorr = MNG_TRUE; + } + } +#endif + + pData->fCorrectrow = MNG_NULL; /* default no color-correction */ + + if (bColorcorr) /* do we have to do color-correction ? */ + { +#ifdef MNG_NO_CMS + iRetcode = MNG_NOERROR; +#else +#if defined(MNG_FULL_CMS) /* determine color-management routine */ + iRetcode = mng_init_full_cms (pData, MNG_TRUE, MNG_FALSE, MNG_FALSE); +#elif defined(MNG_GAMMA_ONLY) + iRetcode = mng_init_gamma_only (pData, MNG_TRUE, MNG_FALSE, MNG_FALSE); +#elif defined(MNG_APP_CMS) + iRetcode = mng_init_app_cms (pData, MNG_TRUE, MNG_FALSE, MNG_FALSE); +#endif + if (iRetcode) /* on error bail out */ + return iRetcode; +#endif /* MNG_NO_CMS */ + } + /* get a temporary row-buffer */ + MNG_ALLOC (pData, pData->pRGBArow, pData->iRowsize); + + iY = pData->iDestt; /* this is where we start */ + iRetcode = MNG_NOERROR; /* so far, so good */ + + while ((!iRetcode) && (iY < pData->iDestb)) + { /* restore a background row */ + iRetcode = ((mng_restbkgdrow)pData->fRestbkgdrow) (pData); + /* color correction ? */ + if ((!iRetcode) && (pData->fCorrectrow)) + iRetcode = ((mng_correctrow)pData->fCorrectrow) (pData); + + if (!iRetcode) /* so... display it */ + iRetcode = ((mng_displayrow)pData->fDisplayrow) (pData); + + if (!iRetcode) + iRetcode = mng_next_row (pData); + + iY++; /* and next line */ + } + /* drop the temporary row-buffer */ + MNG_FREE (pData, pData->pRGBArow, pData->iRowsize); + + if (iRetcode) /* on error bail out */ + return iRetcode; + +#if defined(MNG_FULL_CMS) /* cleanup cms stuff */ + if (bColorcorr) /* did we do color-correction ? */ + { + iRetcode = mng_clear_cms (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } +#endif +#ifndef MNG_SKIPCHUNK_BACK + /* background image ? */ + if ((pData->bHasBACK) && (pData->iBACKmandatory & 0x02) && (pData->iBACKimageid)) + { + mng_imagep pImage; + /* let's find that object then */ + pData->pRetrieveobj = mng_find_imageobject (pData, pData->iBACKimageid); + pImage = (mng_imagep)pData->pRetrieveobj; + /* exists, viewable and visible ? */ + if ((pImage) && (pImage->bViewable) && (pImage->bVisible)) + { /* will it fall within the target region ? */ + if ((pImage->iPosx < pData->iDestr) && (pImage->iPosy < pData->iDestb) && + ((pData->iBACKtile) || + ((pImage->iPosx + (mng_int32)pImage->pImgbuf->iWidth >= pData->iDestl) && + (pImage->iPosy + (mng_int32)pImage->pImgbuf->iHeight >= pData->iDestt) )) && + ((!pImage->bClipped) || + ((pImage->iClipl <= pImage->iClipr) && (pImage->iClipt <= pImage->iClipb) && + (pImage->iClipl < pData->iDestr) && (pImage->iClipr >= pData->iDestl) && + (pImage->iClipt < pData->iDestb) && (pImage->iClipb >= pData->iDestt) ))) + { /* right; we've got ourselves something to do */ + if (pImage->bClipped) /* clip output region with image's clipping region ? */ + { + if (pImage->iClipl > pData->iDestl) + pData->iDestl = pImage->iClipl; + if (pImage->iClipr < pData->iDestr) + pData->iDestr = pImage->iClipr; + if (pImage->iClipt > pData->iDestt) + pData->iDestt = pImage->iClipt; + if (pImage->iClipb < pData->iDestb) + pData->iDestb = pImage->iClipb; + } + /* image offset does some extra clipping too ! */ + if (pImage->iPosx > pData->iDestl) + pData->iDestl = pImage->iPosx; + if (pImage->iPosy > pData->iDestt) + pData->iDestt = pImage->iPosy; + + if (!pData->iBACKtile) /* without tiling further clipping is needed */ + { + if (pImage->iPosx + (mng_int32)pImage->pImgbuf->iWidth < pData->iDestr) + pData->iDestr = pImage->iPosx + (mng_int32)pImage->pImgbuf->iWidth; + if (pImage->iPosy + (mng_int32)pImage->pImgbuf->iHeight < pData->iDestb) + pData->iDestb = pImage->iPosy + (mng_int32)pImage->pImgbuf->iHeight; + } + + pData->iSourcel = 0; /* source relative to destination */ + pData->iSourcer = pData->iDestr - pData->iDestl; + pData->iSourcet = 0; + pData->iSourceb = pData->iDestb - pData->iDestt; + /* 16-bit background ? */ + +#ifdef MNG_NO_16BIT_SUPPORT + pData->bIsRGBA16 = MNG_FALSE; +#else + pData->bIsRGBA16 = (mng_bool)(pImage->pImgbuf->iBitdepth > 8); +#endif + /* let restore routine know the offsets !!! */ + pData->iBackimgoffsx = pImage->iPosx; + pData->iBackimgoffsy = pImage->iPosy; + pData->iBackimgwidth = pImage->pImgbuf->iWidth; + pData->iBackimgheight = pImage->pImgbuf->iHeight; + pData->iRow = 0; /* start at the top again !! */ + /* determine background object retrieval routine */ + switch (pImage->pImgbuf->iColortype) + { + case 0 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (pImage->pImgbuf->iBitdepth > 8) + pData->fRetrieverow = (mng_fptr)mng_retrieve_g16; + else +#endif + pData->fRetrieverow = (mng_fptr)mng_retrieve_g8; + + pData->bIsOpaque = (mng_bool)(!pImage->pImgbuf->bHasTRNS); + break; + } + + case 2 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (pImage->pImgbuf->iBitdepth > 8) + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgb16; + else +#endif + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgb8; + + pData->bIsOpaque = (mng_bool)(!pImage->pImgbuf->bHasTRNS); + break; + } + + case 3 : { pData->fRetrieverow = (mng_fptr)mng_retrieve_idx8; + pData->bIsOpaque = (mng_bool)(!pImage->pImgbuf->bHasTRNS); + break; + } + + case 4 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (pImage->pImgbuf->iBitdepth > 8) + pData->fRetrieverow = (mng_fptr)mng_retrieve_ga16; + else +#endif + pData->fRetrieverow = (mng_fptr)mng_retrieve_ga8; + + pData->bIsOpaque = MNG_FALSE; + break; + } + + case 6 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (pImage->pImgbuf->iBitdepth > 8) + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba16; + else +#endif + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba8; + + pData->bIsOpaque = MNG_FALSE; + break; + } + + case 8 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (pImage->pImgbuf->iBitdepth > 8) + pData->fRetrieverow = (mng_fptr)mng_retrieve_g16; + else +#endif + pData->fRetrieverow = (mng_fptr)mng_retrieve_g8; + + pData->bIsOpaque = MNG_TRUE; + break; + } + + case 10 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (pImage->pImgbuf->iBitdepth > 8) + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgb16; + else +#endif + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgb8; + + pData->bIsOpaque = MNG_TRUE; + break; + } + + case 12 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (pImage->pImgbuf->iBitdepth > 8) + pData->fRetrieverow = (mng_fptr)mng_retrieve_ga16; + else +#endif + pData->fRetrieverow = (mng_fptr)mng_retrieve_ga8; + + pData->bIsOpaque = MNG_FALSE; + break; + } + + case 14 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (pImage->pImgbuf->iBitdepth > 8) + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba16; + else +#endif + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba8; + + pData->bIsOpaque = MNG_FALSE; + break; + } + } + +#ifdef MNG_NO_CMS + iRetcode = MNG_NOERROR; +#else +#if defined(MNG_FULL_CMS) /* determine color-management routine */ + iRetcode = mng_init_full_cms (pData, MNG_FALSE, MNG_FALSE, MNG_TRUE); +#elif defined(MNG_GAMMA_ONLY) + iRetcode = mng_init_gamma_only (pData, MNG_FALSE, MNG_FALSE, MNG_TRUE); +#elif defined(MNG_APP_CMS) + iRetcode = mng_init_app_cms (pData, MNG_FALSE, MNG_FALSE, MNG_TRUE); +#endif + if (iRetcode) /* on error bail out */ + return iRetcode; +#endif /* MNG_NO_CMS */ + /* get temporary row-buffers */ + MNG_ALLOC (pData, pData->pPrevrow, pData->iRowsize); + MNG_ALLOC (pData, pData->pRGBArow, pData->iRowsize); + + iY = pData->iDestt; /* this is where we start */ + iRetcode = MNG_NOERROR; /* so far, so good */ + + while ((!iRetcode) && (iY < pData->iDestb)) + { /* restore a background row */ + iRetcode = mng_restore_bkgd_backimage (pData); + /* color correction ? */ + if ((!iRetcode) && (pData->fCorrectrow)) + iRetcode = ((mng_correctrow)pData->fCorrectrow) (pData); + + if (!iRetcode) /* so... display it */ + iRetcode = ((mng_displayrow)pData->fDisplayrow) (pData); + + if (!iRetcode) + iRetcode = mng_next_row (pData); + + iY++; /* and next line */ + } + /* drop temporary row-buffers */ + MNG_FREE (pData, pData->pRGBArow, pData->iRowsize); + MNG_FREE (pData, pData->pPrevrow, pData->iRowsize); + + if (iRetcode) /* on error bail out */ + return iRetcode; + +#if defined(MNG_FULL_CMS) /* cleanup cms stuff */ + iRetcode = mng_clear_cms (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; +#endif + } + } + } +#endif + } + + pData->iDestl = iDestl; /* restore values */ + pData->iDestr = iDestr; + pData->iDestt = iDestt; + pData->iDestb = iDestb; + pData->iSourcel = iSourcel; + pData->iSourcer = iSourcer; + pData->iSourcet = iSourcet; + pData->iSourceb = iSourceb; + pData->iPass = iPass; + pData->iRow = iRow; + pData->iRowinc = iRowinc; + pData->iCol = iCol; + pData->iColinc = iColinc; + pData->iRowsamples = iRowsamples; + pData->iRowsize = iRowsize; + pData->pPrevrow = pPrevrow; + pData->pRGBArow = pRGBArow; + pData->bIsRGBA16 = bIsRGBA16; + pData->bIsOpaque = bIsOpaque; + pData->fCorrectrow = fCorrectrow; + pData->fDisplayrow = fDisplayrow; + pData->fRetrieverow = fRetrieverow; + pData->pCurrentobj = pCurrentobj; + pData->pRetrieveobj = pRetrieveobj; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_LOAD_BKGDLAYER, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +MNG_LOCAL mng_retcode clear_canvas (mng_datap pData) +{ + mng_int32 iY; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CLEAR_CANVAS, MNG_LC_START); +#endif + + pData->iDestl = 0; /* clipping region is full canvas! */ + pData->iDestt = 0; + pData->iDestr = pData->iWidth; + pData->iDestb = pData->iHeight; + + pData->iSourcel = 0; /* source is same as destination */ + pData->iSourcer = pData->iWidth; + pData->iSourcet = 0; + pData->iSourceb = pData->iHeight; + + pData->iPass = -1; /* these are the object's dimensions now */ + pData->iRow = 0; + pData->iRowinc = 1; + pData->iCol = 0; + pData->iColinc = 1; + pData->iRowsamples = pData->iWidth; + pData->iRowsize = pData->iRowsamples << 2; + pData->bIsRGBA16 = MNG_FALSE; /* let's keep it simple ! */ + pData->bIsOpaque = MNG_TRUE; + + set_display_routine (pData); /* determine display routine */ + /* get a temporary row-buffer */ + /* it's transparent black by default!! */ + MNG_ALLOC (pData, pData->pRGBArow, pData->iRowsize); + + iY = pData->iDestt; /* this is where we start */ + iRetcode = MNG_NOERROR; /* so far, so good */ + + while ((!iRetcode) && (iY < pData->iDestb)) + { /* clear a row then */ + iRetcode = ((mng_displayrow)pData->fDisplayrow) (pData); + + if (!iRetcode) + iRetcode = mng_next_row (pData); /* adjust variables for next row */ + + iY++; /* and next line */ + } + /* drop the temporary row-buffer */ + MNG_FREE (pData, pData->pRGBArow, pData->iRowsize); + + if (iRetcode) /* on error bail out */ + return iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CLEAR_CANVAS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +MNG_LOCAL mng_retcode next_frame (mng_datap pData, + mng_uint8 iFramemode, + mng_uint8 iChangedelay, + mng_uint32 iDelay, + mng_uint8 iChangetimeout, + mng_uint32 iTimeout, + mng_uint8 iChangeclipping, + mng_uint8 iCliptype, + mng_int32 iClipl, + mng_int32 iClipr, + mng_int32 iClipt, + mng_int32 iClipb) +{ + mng_retcode iRetcode = MNG_NOERROR; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_NEXT_FRAME, MNG_LC_START); +#endif + + if (!pData->iBreakpoint) /* no previous break here ? */ + { +#ifndef MNG_SKIPCHUNK_FRAM + mng_uint8 iOldmode = pData->iFramemode; + /* interframe delay required ? */ + if ((iOldmode == 2) || (iOldmode == 4)) + { + if ((pData->iFrameseq) && (iFramemode != 1) && (iFramemode != 3)) + iRetcode = interframe_delay (pData); + else + pData->iFramedelay = pData->iNextdelay; + } + else + { /* delay before inserting background layer? */ + if ((pData->bFramedone) && (iFramemode == 4)) + iRetcode = interframe_delay (pData); + } + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* now we'll assume we're in the next frame! */ + if (iFramemode) /* save the new framing mode ? */ + { + pData->iFRAMmode = iFramemode; + pData->iFramemode = iFramemode; + } + else /* reload default */ + pData->iFramemode = pData->iFRAMmode; + + if (iChangedelay) /* delay changed ? */ + { + pData->iNextdelay = iDelay; /* for *after* next subframe */ + + if ((iOldmode == 2) || (iOldmode == 4)) + pData->iFramedelay = pData->iFRAMdelay; + + if (iChangedelay == 2) /* also overall ? */ + pData->iFRAMdelay = iDelay; + } + else + { /* reload default */ + pData->iNextdelay = pData->iFRAMdelay; + } + + if (iChangetimeout) /* timeout changed ? */ + { /* for next subframe */ + pData->iFrametimeout = iTimeout; + + if ((iChangetimeout == 2) || /* also overall ? */ + (iChangetimeout == 4) || + (iChangetimeout == 6) || + (iChangetimeout == 8)) + pData->iFRAMtimeout = iTimeout; + } + else /* reload default */ + pData->iFrametimeout = pData->iFRAMtimeout; + + if (iChangeclipping) /* clipping changed ? */ + { + pData->bFrameclipping = MNG_TRUE; + + if (!iCliptype) /* absolute ? */ + { + pData->iFrameclipl = iClipl; + pData->iFrameclipr = iClipr; + pData->iFrameclipt = iClipt; + pData->iFrameclipb = iClipb; + } + else /* relative */ + { + pData->iFrameclipl = pData->iFrameclipl + iClipl; + pData->iFrameclipr = pData->iFrameclipr + iClipr; + pData->iFrameclipt = pData->iFrameclipt + iClipt; + pData->iFrameclipb = pData->iFrameclipb + iClipb; + } + + if (iChangeclipping == 2) /* also overall ? */ + { + pData->bFRAMclipping = MNG_TRUE; + + if (!iCliptype) /* absolute ? */ + { + pData->iFRAMclipl = iClipl; + pData->iFRAMclipr = iClipr; + pData->iFRAMclipt = iClipt; + pData->iFRAMclipb = iClipb; + } + else /* relative */ + { + pData->iFRAMclipl = pData->iFRAMclipl + iClipl; + pData->iFRAMclipr = pData->iFRAMclipr + iClipr; + pData->iFRAMclipt = pData->iFRAMclipt + iClipt; + pData->iFRAMclipb = pData->iFRAMclipb + iClipb; + } + } + } + else + { /* reload defaults */ + pData->bFrameclipping = pData->bFRAMclipping; + pData->iFrameclipl = pData->iFRAMclipl; + pData->iFrameclipr = pData->iFRAMclipr; + pData->iFrameclipt = pData->iFRAMclipt; + pData->iFrameclipb = pData->iFRAMclipb; + } +#endif + } + + if (!pData->bTimerset) /* timer still off ? */ + { + if ( +#ifndef MNG_SKIPCHUNK_FRAM + (pData->iFramemode == 4) || /* insert background layer after a new frame */ +#endif + (!pData->iLayerseq)) /* and certainly before the very first layer */ + iRetcode = load_bkgdlayer (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + pData->iFrameseq++; /* count the frame ! */ + pData->bFramedone = MNG_TRUE; /* and indicate we've done one */ + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_NEXT_FRAME, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +MNG_LOCAL mng_retcode next_layer (mng_datap pData) +{ + mng_imagep pImage; + mng_retcode iRetcode = MNG_NOERROR; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_NEXT_LAYER, MNG_LC_START); +#endif + +#ifndef MNG_SKIPCHUNK_FRAM + if (!pData->iBreakpoint) /* no previous break here ? */ + { /* interframe delay required ? */ + if ((pData->eImagetype == mng_it_mng) && (pData->iLayerseq) && + ((pData->iFramemode == 1) || (pData->iFramemode == 3))) + iRetcode = interframe_delay (pData); + else + pData->iFramedelay = pData->iNextdelay; + + if (iRetcode) /* on error bail out */ + return iRetcode; + } +#endif + + if (!pData->bTimerset) /* timer still off ? */ + { + if (!pData->iLayerseq) /* restore background for the very first layer ? */ + { /* wait till IDAT/JDAT for PNGs & JNGs !!! */ + if ((pData->eImagetype == mng_it_png) || (pData->eImagetype == mng_it_jng)) + pData->bRestorebkgd = MNG_TRUE; + else + { /* for MNG we do it right away */ + iRetcode = load_bkgdlayer (pData); + pData->iLayerseq++; /* and it counts as a layer then ! */ + } + } +#ifndef MNG_SKIPCHUNK_FRAM + else + if (pData->iFramemode == 3) /* restore background for each layer ? */ + iRetcode = load_bkgdlayer (pData); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + +#ifndef MNG_NO_DELTA_PNG + if (pData->bHasDHDR) /* processing a delta-image ? */ + pImage = (mng_imagep)pData->pDeltaImage; + else +#endif + pImage = (mng_imagep)pData->pCurrentobj; + + if (!pImage) /* not an active object ? */ + pImage = (mng_imagep)pData->pObjzero; + /* determine display rectangle */ + pData->iDestl = MAX_COORD ((mng_int32)0, pImage->iPosx); + pData->iDestt = MAX_COORD ((mng_int32)0, pImage->iPosy); + /* is it a valid buffer ? */ + if ((pImage->pImgbuf->iWidth) && (pImage->pImgbuf->iHeight)) + { + pData->iDestr = MIN_COORD ((mng_int32)pData->iWidth, + pImage->iPosx + (mng_int32)pImage->pImgbuf->iWidth ); + pData->iDestb = MIN_COORD ((mng_int32)pData->iHeight, + pImage->iPosy + (mng_int32)pImage->pImgbuf->iHeight); + } + else /* it's a single image ! */ + { + pData->iDestr = MIN_COORD ((mng_int32)pData->iWidth, + (mng_int32)pData->iDatawidth ); + pData->iDestb = MIN_COORD ((mng_int32)pData->iHeight, + (mng_int32)pData->iDataheight); + } + +#ifndef MNG_SKIPCHUNK_FRAM + if (pData->bFrameclipping) /* frame clipping specified ? */ + { + pData->iDestl = MAX_COORD (pData->iDestl, pData->iFrameclipl); + pData->iDestt = MAX_COORD (pData->iDestt, pData->iFrameclipt); + pData->iDestr = MIN_COORD (pData->iDestr, pData->iFrameclipr); + pData->iDestb = MIN_COORD (pData->iDestb, pData->iFrameclipb); + } +#endif + + if (pImage->bClipped) /* is the image clipped itself ? */ + { + pData->iDestl = MAX_COORD (pData->iDestl, pImage->iClipl); + pData->iDestt = MAX_COORD (pData->iDestt, pImage->iClipt); + pData->iDestr = MIN_COORD (pData->iDestr, pImage->iClipr); + pData->iDestb = MIN_COORD (pData->iDestb, pImage->iClipb); + } + /* determine source starting point */ + pData->iSourcel = MAX_COORD ((mng_int32)0, pData->iDestl - pImage->iPosx); + pData->iSourcet = MAX_COORD ((mng_int32)0, pData->iDestt - pImage->iPosy); + + if ((pImage->pImgbuf->iWidth) && (pImage->pImgbuf->iHeight)) + { /* and maximum size */ + pData->iSourcer = MIN_COORD ((mng_int32)pImage->pImgbuf->iWidth, + pData->iSourcel + pData->iDestr - pData->iDestl); + pData->iSourceb = MIN_COORD ((mng_int32)pImage->pImgbuf->iHeight, + pData->iSourcet + pData->iDestb - pData->iDestt); + } + else /* it's a single image ! */ + { + pData->iSourcer = pData->iSourcel + pData->iDestr - pData->iDestl; + pData->iSourceb = pData->iSourcet + pData->iDestb - pData->iDestt; + } + + pData->iLayerseq++; /* count the layer ! */ + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_NEXT_LAYER, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_display_image (mng_datap pData, + mng_imagep pImage, + mng_bool bLayeradvanced) +{ + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_IMAGE, MNG_LC_START); +#endif + /* actively running ? */ +#ifndef MNG_SKIPCHUNK_MAGN + if (((pData->bRunning) || (pData->bSearching)) && (!pData->bSkipping)) + { + if ( (!pData->iBreakpoint) && /* needs magnification ? */ + ( (pImage->iMAGN_MethodX) || (pImage->iMAGN_MethodY) ) ) + { + iRetcode = mng_magnify_imageobject (pData, pImage); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + } +#endif + + pData->pRetrieveobj = pImage; /* so retrieve-row and color-correction can find it */ + + if (!bLayeradvanced) /* need to advance the layer ? */ + { + mng_imagep pSave = pData->pCurrentobj; + pData->pCurrentobj = pImage; + next_layer (pData); /* advance to next layer */ + pData->pCurrentobj = pSave; + } + /* need to restore the background ? */ + if ((!pData->bTimerset) && (pData->bRestorebkgd)) + { + mng_imagep pSave = pData->pCurrentobj; + pData->pCurrentobj = pImage; + pData->bRestorebkgd = MNG_FALSE; + iRetcode = load_bkgdlayer (pData); + pData->pCurrentobj = pSave; + + if (iRetcode) /* on error bail out */ + return iRetcode; + + pData->iLayerseq++; /* and it counts as a layer then ! */ + } + /* actively running ? */ + if (((pData->bRunning) || (pData->bSearching)) && (!pData->bSkipping)) + { + if (!pData->bTimerset) /* all systems still go ? */ + { + pData->iBreakpoint = 0; /* let's make absolutely sure... */ + /* anything to display ? */ + if ((pData->iDestr >= pData->iDestl) && (pData->iDestb >= pData->iDestt)) + { + mng_int32 iY; + + set_display_routine (pData); /* determine display routine */ + /* and image-buffer retrieval routine */ + switch (pImage->pImgbuf->iColortype) + { + case 0 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (pImage->pImgbuf->iBitdepth > 8) + pData->fRetrieverow = (mng_fptr)mng_retrieve_g16; + else +#endif + pData->fRetrieverow = (mng_fptr)mng_retrieve_g8; + + pData->bIsOpaque = (mng_bool)(!pImage->pImgbuf->bHasTRNS); + break; + } + + case 2 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (pImage->pImgbuf->iBitdepth > 8) + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgb16; + else +#endif + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgb8; + + pData->bIsOpaque = (mng_bool)(!pImage->pImgbuf->bHasTRNS); + break; + } + + + case 3 : { pData->fRetrieverow = (mng_fptr)mng_retrieve_idx8; + pData->bIsOpaque = (mng_bool)(!pImage->pImgbuf->bHasTRNS); + break; + } + + + case 4 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (pImage->pImgbuf->iBitdepth > 8) + pData->fRetrieverow = (mng_fptr)mng_retrieve_ga16; + else +#endif + pData->fRetrieverow = (mng_fptr)mng_retrieve_ga8; + + pData->bIsOpaque = MNG_FALSE; + break; + } + + + case 6 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (pImage->pImgbuf->iBitdepth > 8) + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba16; + else +#endif + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba8; + + pData->bIsOpaque = MNG_FALSE; + break; + } + + case 8 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (pImage->pImgbuf->iBitdepth > 8) + pData->fRetrieverow = (mng_fptr)mng_retrieve_g16; + else +#endif + pData->fRetrieverow = (mng_fptr)mng_retrieve_g8; + + pData->bIsOpaque = MNG_TRUE; + break; + } + + case 10 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (pImage->pImgbuf->iBitdepth > 8) + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgb16; + else +#endif + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgb8; + + pData->bIsOpaque = MNG_TRUE; + break; + } + + + case 12 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (pImage->pImgbuf->iBitdepth > 8) + pData->fRetrieverow = (mng_fptr)mng_retrieve_ga16; + else +#endif + pData->fRetrieverow = (mng_fptr)mng_retrieve_ga8; + + pData->bIsOpaque = MNG_FALSE; + break; + } + + + case 14 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (pImage->pImgbuf->iBitdepth > 8) + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba16; + else +#endif + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba8; + + pData->bIsOpaque = MNG_FALSE; + break; + } + + } + + pData->iPass = -1; /* these are the object's dimensions now */ + pData->iRow = pData->iSourcet; + pData->iRowinc = 1; + pData->iCol = 0; + pData->iColinc = 1; + pData->iRowsamples = pImage->pImgbuf->iWidth; + pData->iRowsize = pData->iRowsamples << 2; + pData->bIsRGBA16 = MNG_FALSE; + /* adjust for 16-bit object ? */ +#ifndef MNG_NO_16BIT_SUPPORT + if (pImage->pImgbuf->iBitdepth > 8) + { + pData->bIsRGBA16 = MNG_TRUE; + pData->iRowsize = pData->iRowsamples << 3; + } +#endif + + pData->fCorrectrow = MNG_NULL; /* default no color-correction */ + +#ifdef MNG_NO_CMS + iRetcode = MNG_NOERROR; +#else +#if defined(MNG_FULL_CMS) /* determine color-management routine */ + iRetcode = mng_init_full_cms (pData, MNG_FALSE, MNG_FALSE, MNG_TRUE); +#elif defined(MNG_GAMMA_ONLY) + iRetcode = mng_init_gamma_only (pData, MNG_FALSE, MNG_FALSE, MNG_TRUE); +#elif defined(MNG_APP_CMS) + iRetcode = mng_init_app_cms (pData, MNG_FALSE, MNG_FALSE, MNG_TRUE); +#endif + if (iRetcode) /* on error bail out */ + return iRetcode; +#endif /* MNG_NO_CMS */ + /* get a temporary row-buffer */ + MNG_ALLOC (pData, pData->pRGBArow, pData->iRowsize); + + iY = pData->iSourcet; /* this is where we start */ + + while ((!iRetcode) && (iY < pData->iSourceb)) + { /* get a row */ + iRetcode = ((mng_retrieverow)pData->fRetrieverow) (pData); + /* color correction ? */ + if ((!iRetcode) && (pData->fCorrectrow)) + iRetcode = ((mng_correctrow)pData->fCorrectrow) (pData); + + if (!iRetcode) /* so... display it */ + iRetcode = ((mng_displayrow)pData->fDisplayrow) (pData); + + if (!iRetcode) /* adjust variables for next row */ + iRetcode = mng_next_row (pData); + + iY++; /* and next line */ + } + /* drop the temporary row-buffer */ + MNG_FREE (pData, pData->pRGBArow, pData->iRowsize); + + if (iRetcode) /* on error bail out */ + return iRetcode; + +#if defined(MNG_FULL_CMS) /* cleanup cms stuff */ + iRetcode = mng_clear_cms (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; +#endif + } + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_IMAGE, MNG_LC_END); +#endif + + return MNG_NOERROR; /* whehehe, this is good ! */ +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +mng_retcode mng_execute_delta_image (mng_datap pData, + mng_imagep pTarget, + mng_imagep pDelta) +{ + mng_imagedatap pBuftarget = pTarget->pImgbuf; + mng_imagedatap pBufdelta = pDelta->pImgbuf; + mng_uint32 iY; + mng_retcode iRetcode; + mng_ptr pSaveRGBA; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_EXECUTE_DELTA_IMAGE, MNG_LC_START); +#endif + /* actively running ? */ + if (((pData->bRunning) || (pData->bSearching)) && (!pData->bSkipping)) + { + if (pBufdelta->bHasPLTE) /* palette in delta ? */ + { + mng_uint32 iX; + /* new palette larger than old one ? */ + if ((!pBuftarget->bHasPLTE) || (pBuftarget->iPLTEcount < pBufdelta->iPLTEcount)) + pBuftarget->iPLTEcount = pBufdelta->iPLTEcount; + /* it's definitely got a PLTE now */ + pBuftarget->bHasPLTE = MNG_TRUE; + + for (iX = 0; iX < pBufdelta->iPLTEcount; iX++) + { + pBuftarget->aPLTEentries[iX].iRed = pBufdelta->aPLTEentries[iX].iRed; + pBuftarget->aPLTEentries[iX].iGreen = pBufdelta->aPLTEentries[iX].iGreen; + pBuftarget->aPLTEentries[iX].iBlue = pBufdelta->aPLTEentries[iX].iBlue; + } + } + + if (pBufdelta->bHasTRNS) /* cheap transparency in delta ? */ + { + switch (pData->iColortype) /* drop it into the target */ + { + case 0: { /* gray */ + pBuftarget->iTRNSgray = pBufdelta->iTRNSgray; + pBuftarget->iTRNSred = 0; + pBuftarget->iTRNSgreen = 0; + pBuftarget->iTRNSblue = 0; + pBuftarget->iTRNScount = 0; + break; + } + case 2: { /* rgb */ + pBuftarget->iTRNSgray = 0; + pBuftarget->iTRNSred = pBufdelta->iTRNSred; + pBuftarget->iTRNSgreen = pBufdelta->iTRNSgreen; + pBuftarget->iTRNSblue = pBufdelta->iTRNSblue; + pBuftarget->iTRNScount = 0; + break; + } + case 3: { /* indexed */ + pBuftarget->iTRNSgray = 0; + pBuftarget->iTRNSred = 0; + pBuftarget->iTRNSgreen = 0; + pBuftarget->iTRNSblue = 0; + /* existing range smaller than new one ? */ + if ((!pBuftarget->bHasTRNS) || (pBuftarget->iTRNScount < pBufdelta->iTRNScount)) + pBuftarget->iTRNScount = pBufdelta->iTRNScount; + + MNG_COPY (pBuftarget->aTRNSentries, pBufdelta->aTRNSentries, pBufdelta->iTRNScount); + break; + } + } + + pBuftarget->bHasTRNS = MNG_TRUE; /* tell it it's got a tRNS now */ + } + +#ifndef MNG_SKIPCHUNK_bKGD + if (pBufdelta->bHasBKGD) /* bkgd in source ? */ + { /* drop it onto the target */ + pBuftarget->bHasBKGD = MNG_TRUE; + pBuftarget->iBKGDindex = pBufdelta->iBKGDindex; + pBuftarget->iBKGDgray = pBufdelta->iBKGDgray; + pBuftarget->iBKGDred = pBufdelta->iBKGDred; + pBuftarget->iBKGDgreen = pBufdelta->iBKGDgreen; + pBuftarget->iBKGDblue = pBufdelta->iBKGDblue; + } +#endif + + if (pBufdelta->bHasGAMA) /* gamma in source ? */ + { + pBuftarget->bHasGAMA = MNG_TRUE; /* drop it onto the target */ + pBuftarget->iGamma = pBufdelta->iGamma; + } + +#ifndef MNG_SKIPCHUNK_cHRM + if (pBufdelta->bHasCHRM) /* chroma in delta ? */ + { /* drop it onto the target */ + pBuftarget->bHasCHRM = MNG_TRUE; + pBuftarget->iWhitepointx = pBufdelta->iWhitepointx; + pBuftarget->iWhitepointy = pBufdelta->iWhitepointy; + pBuftarget->iPrimaryredx = pBufdelta->iPrimaryredx; + pBuftarget->iPrimaryredy = pBufdelta->iPrimaryredy; + pBuftarget->iPrimarygreenx = pBufdelta->iPrimarygreenx; + pBuftarget->iPrimarygreeny = pBufdelta->iPrimarygreeny; + pBuftarget->iPrimarybluex = pBufdelta->iPrimarybluex; + pBuftarget->iPrimarybluey = pBufdelta->iPrimarybluey; + } +#endif + +#ifndef MNG_SKIPCHUNK_sRGB + if (pBufdelta->bHasSRGB) /* sRGB in delta ? */ + { /* drop it onto the target */ + pBuftarget->bHasSRGB = MNG_TRUE; + pBuftarget->iRenderingintent = pBufdelta->iRenderingintent; + } +#endif + +#ifndef MNG_SKIPCHUNK_iCCP + if (pBufdelta->bHasICCP) /* ICC profile in delta ? */ + { + pBuftarget->bHasICCP = MNG_TRUE; /* drop it onto the target */ + + if (pBuftarget->pProfile) /* profile existed ? */ + MNG_FREEX (pData, pBuftarget->pProfile, pBuftarget->iProfilesize); + /* allocate a buffer & copy it */ + MNG_ALLOC (pData, pBuftarget->pProfile, pBufdelta->iProfilesize); + MNG_COPY (pBuftarget->pProfile, pBufdelta->pProfile, pBufdelta->iProfilesize); + /* store its length as well */ + pBuftarget->iProfilesize = pBufdelta->iProfilesize; + } +#endif + /* need to execute delta pixels ? */ + if ((!pData->bDeltaimmediate) && (pData->iDeltatype != MNG_DELTATYPE_NOCHANGE)) + { + pData->fScalerow = MNG_NULL; /* not needed by default */ + + switch (pBufdelta->iBitdepth) /* determine scaling routine */ + { +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case 1 : { + switch (pBuftarget->iBitdepth) + { + case 2 : { pData->fScalerow = (mng_fptr)mng_scale_g1_g2; break; } + case 4 : { pData->fScalerow = (mng_fptr)mng_scale_g1_g4; break; } + + case 8 : { pData->fScalerow = (mng_fptr)mng_scale_g1_g8; break; } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { pData->fScalerow = (mng_fptr)mng_scale_g1_g16; break; } +#endif + } + break; + } + + case 2 : { + switch (pBuftarget->iBitdepth) + { + case 1 : { pData->fScalerow = (mng_fptr)mng_scale_g2_g1; break; } + case 4 : { pData->fScalerow = (mng_fptr)mng_scale_g2_g4; break; } + case 8 : { pData->fScalerow = (mng_fptr)mng_scale_g2_g8; break; } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { pData->fScalerow = (mng_fptr)mng_scale_g2_g16; break; } +#endif + } + break; + } + + case 4 : { + switch (pBuftarget->iBitdepth) + { + case 1 : { pData->fScalerow = (mng_fptr)mng_scale_g4_g1; break; } + case 2 : { pData->fScalerow = (mng_fptr)mng_scale_g4_g2; break; } + case 8 : { pData->fScalerow = (mng_fptr)mng_scale_g4_g8; break; } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { pData->fScalerow = (mng_fptr)mng_scale_g4_g16; break; } +#endif + } + break; + } +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + + case 8 : { + switch (pBufdelta->iColortype) + { + case 0 : ; + case 3 : ; + case 8 : { + switch (pBuftarget->iBitdepth) + { +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case 1 : { pData->fScalerow = (mng_fptr)mng_scale_g8_g1; break; } + case 2 : { pData->fScalerow = (mng_fptr)mng_scale_g8_g2; break; } + case 4 : { pData->fScalerow = (mng_fptr)mng_scale_g8_g4; break; } +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { pData->fScalerow = (mng_fptr)mng_scale_g8_g16; break; } +#endif + } + break; + } + case 2 : ; + case 10 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (pBuftarget->iBitdepth == 16) + pData->fScalerow = (mng_fptr)mng_scale_rgb8_rgb16; +#endif + break; + } + case 4 : ; + case 12 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (pBuftarget->iBitdepth == 16) + pData->fScalerow = (mng_fptr)mng_scale_ga8_ga16; +#endif + break; + } + case 6 : ; + case 14 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (pBuftarget->iBitdepth == 16) + pData->fScalerow = (mng_fptr)mng_scale_rgba8_rgba16; +#endif + break; + } + } + break; + } + +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { + switch (pBufdelta->iColortype) + { + case 0 : ; + case 3 : ; + case 8 : { + switch (pBuftarget->iBitdepth) + { +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case 1 : { pData->fScalerow = (mng_fptr)mng_scale_g16_g1; break; } + case 2 : { pData->fScalerow = (mng_fptr)mng_scale_g16_g2; break; } + case 4 : { pData->fScalerow = (mng_fptr)mng_scale_g16_g4; break; } +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + case 8 : { pData->fScalerow = (mng_fptr)mng_scale_g16_g8; break; } + } + break; + } + case 2 : ; + case 10 : { + if (pBuftarget->iBitdepth == 8) + pData->fScalerow = (mng_fptr)mng_scale_rgb16_rgb8; + break; + } + case 4 : ; + case 12 : { + if (pBuftarget->iBitdepth == 8) + pData->fScalerow = (mng_fptr)mng_scale_ga16_ga8; + break; + } + case 6 : ; + case 14 : { + if (pBuftarget->iBitdepth == 8) + pData->fScalerow = (mng_fptr)mng_scale_rgba16_rgba8; + break; + } + } + break; + } +#endif + + } + + pData->fDeltarow = MNG_NULL; /* let's assume there's nothing to do */ + + switch (pBuftarget->iColortype) /* determine delta processing routine */ + { + case 0 : ; + case 8 : { /* gray */ + if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) ) + { + if ((pBufdelta->iColortype == 0) || (pBufdelta->iColortype == 3) || + (pBufdelta->iColortype == 8)) + { + switch (pBuftarget->iBitdepth) + { +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case 1 : { pData->fDeltarow = (mng_fptr)mng_delta_g1_g1; break; } + case 2 : { pData->fDeltarow = (mng_fptr)mng_delta_g2_g2; break; } + case 4 : { pData->fDeltarow = (mng_fptr)mng_delta_g4_g4; break; } +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + case 8 : { pData->fDeltarow = (mng_fptr)mng_delta_g8_g8; break; } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { pData->fDeltarow = (mng_fptr)mng_delta_g16_g16; break; } +#endif + } + } + } + + break; + } + + case 2 : ; + case 10 : { /* rgb */ + if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) ) + { + if ((pBufdelta->iColortype == 2) || (pBufdelta->iColortype == 10)) + { + switch (pBuftarget->iBitdepth) + { + case 8 : { pData->fDeltarow = (mng_fptr)mng_delta_rgb8_rgb8; break; } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { pData->fDeltarow = (mng_fptr)mng_delta_rgb16_rgb16; break; } +#endif + } + } + } + + break; + } + + case 3 : { /* indexed; abuse gray routines */ + if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) ) + { + if ((pBufdelta->iColortype == 0) || (pBufdelta->iColortype == 3)) + { + switch (pBuftarget->iBitdepth) + { +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case 1 : { pData->fDeltarow = (mng_fptr)mng_delta_g1_g1; break; } + case 2 : { pData->fDeltarow = (mng_fptr)mng_delta_g2_g2; break; } + case 4 : { pData->fDeltarow = (mng_fptr)mng_delta_g4_g4; break; } +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + case 8 : { pData->fDeltarow = (mng_fptr)mng_delta_g8_g8; break; } + } + } + } + + break; + } + + case 4 : ; + case 12 : { /* gray + alpha */ + if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) ) + { + if ((pBufdelta->iColortype == 4) || (pBufdelta->iColortype == 12)) + { + switch (pBuftarget->iBitdepth) + { + case 8 : { pData->fDeltarow = (mng_fptr)mng_delta_ga8_ga8; break; } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { pData->fDeltarow = (mng_fptr)mng_delta_ga16_ga16; break; } +#endif + } + } + } + else + if ((pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORADD ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORREPLACE) ) + { + if ((pBufdelta->iColortype == 0) || (pBufdelta->iColortype == 3) || + (pBufdelta->iColortype == 8)) + { + switch (pBuftarget->iBitdepth) + { + case 8 : { pData->fDeltarow = (mng_fptr)mng_delta_ga8_g8; break; } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { pData->fDeltarow = (mng_fptr)mng_delta_ga16_g16; break; } +#endif + } + } + } + else + if ((pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAADD ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAREPLACE) ) + { + if ((pBufdelta->iColortype == 0) || (pBufdelta->iColortype == 3)) + { + switch (pBuftarget->iBitdepth) + { + case 8 : { pData->fDeltarow = (mng_fptr)mng_delta_ga8_a8; break; } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { pData->fDeltarow = (mng_fptr)mng_delta_ga16_a16; break; } +#endif + } + } + } + + break; + } + + case 6 : ; + case 14 : { /* rgb + alpha */ + if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) ) + { + if ((pBufdelta->iColortype == 6) || (pBufdelta->iColortype == 14)) + { + switch (pBuftarget->iBitdepth) + { + case 8 : { pData->fDeltarow = (mng_fptr)mng_delta_rgba8_rgba8; break; } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { pData->fDeltarow = (mng_fptr)mng_delta_rgba16_rgba16; break; } +#endif + } + } + } + else + if ((pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORADD ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORREPLACE) ) + { + if ((pBufdelta->iColortype == 2) || (pBufdelta->iColortype == 10)) + { + switch (pBuftarget->iBitdepth) + { + case 8 : { pData->fDeltarow = (mng_fptr)mng_delta_rgba8_rgb8; break; } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { pData->fDeltarow = (mng_fptr)mng_delta_rgba16_rgb16; break; } +#endif + } + } + } + else + if ((pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAADD ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAREPLACE) ) + { + if ((pBufdelta->iColortype == 0) || (pBufdelta->iColortype == 3)) + { + switch (pBuftarget->iBitdepth) + { + case 8 : { pData->fDeltarow = (mng_fptr)mng_delta_rgba8_a8; break; } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { pData->fDeltarow = (mng_fptr)mng_delta_rgba16_a16; break; } +#endif + } + } + } + + break; + } + + } + + if (pData->fDeltarow) /* do we need to take action ? */ + { + pData->iPass = -1; /* setup row dimensions and stuff */ + pData->iRow = pData->iDeltaBlocky; + pData->iRowinc = 1; + pData->iCol = pData->iDeltaBlockx; + pData->iColinc = 1; + pData->iRowsamples = pBufdelta->iWidth; + pData->iRowsize = pBuftarget->iRowsize; + /* indicate where to retrieve & where to store */ + pData->pRetrieveobj = (mng_objectp)pDelta; + pData->pStoreobj = (mng_objectp)pTarget; + + pSaveRGBA = pData->pRGBArow; /* save current temp-buffer! */ + /* get a temporary row-buffer */ + MNG_ALLOC (pData, pData->pRGBArow, (pBufdelta->iRowsize << 1)); + + iY = 0; /* this is where we start */ + iRetcode = MNG_NOERROR; /* still oke for now */ + + while ((!iRetcode) && (iY < pBufdelta->iHeight)) + { /* get a row */ + mng_uint8p pWork = pBufdelta->pImgdata + (iY * pBufdelta->iRowsize); + + MNG_COPY (pData->pRGBArow, pWork, pBufdelta->iRowsize); + + if (pData->fScalerow) /* scale it (if necessary) */ + iRetcode = ((mng_scalerow)pData->fScalerow) (pData); + + if (!iRetcode) /* and... execute it */ + iRetcode = ((mng_deltarow)pData->fDeltarow) (pData); + + if (!iRetcode) /* adjust variables for next row */ + iRetcode = mng_next_row (pData); + + iY++; /* and next line */ + } + /* drop the temporary row-buffer */ + MNG_FREE (pData, pData->pRGBArow, (pBufdelta->iRowsize << 1)); + pData->pRGBArow = pSaveRGBA; /* restore saved temp-buffer! */ + + if (iRetcode) /* on error bail out */ + return iRetcode; + + } + else + MNG_ERROR (pData, MNG_INVALIDDELTA); + + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_EXECUTE_DELTA_IMAGE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_DELTA_PNG */ + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SAVE +MNG_LOCAL mng_retcode save_state (mng_datap pData) +{ + mng_savedatap pSave; + mng_imagep pImage; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SAVE_STATE, MNG_LC_START); +#endif + + if (pData->pSavedata) /* sanity check */ + MNG_ERROR (pData, MNG_INTERNALERROR); + /* get a buffer for saving */ + MNG_ALLOC (pData, pData->pSavedata, sizeof (mng_savedata)); + + pSave = pData->pSavedata; /* address it more directly */ + /* and copy global data from the main struct */ +#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE) + pSave->bHasglobalPLTE = pData->bHasglobalPLTE; + pSave->bHasglobalTRNS = pData->bHasglobalTRNS; + pSave->bHasglobalGAMA = pData->bHasglobalGAMA; + pSave->bHasglobalCHRM = pData->bHasglobalCHRM; + pSave->bHasglobalSRGB = pData->bHasglobalSRGB; + pSave->bHasglobalICCP = pData->bHasglobalICCP; + pSave->bHasglobalBKGD = pData->bHasglobalBKGD; +#endif /* MNG_SUPPORT_READ || MNG_SUPPORT_WRITE */ + +#ifndef MNG_SKIPCHUNK_BACK + pSave->iBACKred = pData->iBACKred; + pSave->iBACKgreen = pData->iBACKgreen; + pSave->iBACKblue = pData->iBACKblue; + pSave->iBACKmandatory = pData->iBACKmandatory; + pSave->iBACKimageid = pData->iBACKimageid; + pSave->iBACKtile = pData->iBACKtile; +#endif + +#ifndef MNG_SKIPCHUNK_FRAM + pSave->iFRAMmode = pData->iFRAMmode; + pSave->iFRAMdelay = pData->iFRAMdelay; + pSave->iFRAMtimeout = pData->iFRAMtimeout; + pSave->bFRAMclipping = pData->bFRAMclipping; + pSave->iFRAMclipl = pData->iFRAMclipl; + pSave->iFRAMclipr = pData->iFRAMclipr; + pSave->iFRAMclipt = pData->iFRAMclipt; + pSave->iFRAMclipb = pData->iFRAMclipb; +#endif + + pSave->iGlobalPLTEcount = pData->iGlobalPLTEcount; + + MNG_COPY (pSave->aGlobalPLTEentries, pData->aGlobalPLTEentries, sizeof (mng_rgbpaltab)); + + pSave->iGlobalTRNSrawlen = pData->iGlobalTRNSrawlen; + MNG_COPY (pSave->aGlobalTRNSrawdata, pData->aGlobalTRNSrawdata, 256); + + pSave->iGlobalGamma = pData->iGlobalGamma; + +#ifndef MNG_SKIPCHUNK_cHRM + pSave->iGlobalWhitepointx = pData->iGlobalWhitepointx; + pSave->iGlobalWhitepointy = pData->iGlobalWhitepointy; + pSave->iGlobalPrimaryredx = pData->iGlobalPrimaryredx; + pSave->iGlobalPrimaryredy = pData->iGlobalPrimaryredy; + pSave->iGlobalPrimarygreenx = pData->iGlobalPrimarygreenx; + pSave->iGlobalPrimarygreeny = pData->iGlobalPrimarygreeny; + pSave->iGlobalPrimarybluex = pData->iGlobalPrimarybluex; + pSave->iGlobalPrimarybluey = pData->iGlobalPrimarybluey; +#endif + +#ifndef MNG_SKIPCHUNK_sRGB + pSave->iGlobalRendintent = pData->iGlobalRendintent; +#endif + +#ifndef MNG_SKIPCHUNK_iCCP + pSave->iGlobalProfilesize = pData->iGlobalProfilesize; + + if (pSave->iGlobalProfilesize) /* has a profile ? */ + { /* then copy that ! */ + MNG_ALLOC (pData, pSave->pGlobalProfile, pSave->iGlobalProfilesize); + MNG_COPY (pSave->pGlobalProfile, pData->pGlobalProfile, pSave->iGlobalProfilesize); + } +#endif + +#ifndef MNG_SKIPCHUNK_bKGD + pSave->iGlobalBKGDred = pData->iGlobalBKGDred; + pSave->iGlobalBKGDgreen = pData->iGlobalBKGDgreen; + pSave->iGlobalBKGDblue = pData->iGlobalBKGDblue; +#endif + + /* freeze current image objects */ + pImage = (mng_imagep)pData->pFirstimgobj; + + while (pImage) + { /* freeze the object AND its buffer */ + pImage->bFrozen = MNG_TRUE; + pImage->pImgbuf->bFrozen = MNG_TRUE; + /* neeeext */ + pImage = (mng_imagep)pImage->sHeader.pNext; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SAVE_STATE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_reset_objzero (mng_datap pData) +{ + mng_imagep pImage = (mng_imagep)pData->pObjzero; + mng_retcode iRetcode = mng_reset_object_details (pData, pImage, 0, 0, 0, + 0, 0, 0, 0, MNG_TRUE); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + pImage->bVisible = MNG_TRUE; + pImage->bViewable = MNG_TRUE; + pImage->iPosx = 0; + pImage->iPosy = 0; + pImage->bClipped = MNG_FALSE; + pImage->iClipl = 0; + pImage->iClipr = 0; + pImage->iClipt = 0; + pImage->iClipb = 0; +#ifndef MNG_SKIPCHUNK_MAGN + pImage->iMAGN_MethodX = 0; + pImage->iMAGN_MethodY = 0; + pImage->iMAGN_MX = 0; + pImage->iMAGN_MY = 0; + pImage->iMAGN_ML = 0; + pImage->iMAGN_MR = 0; + pImage->iMAGN_MT = 0; + pImage->iMAGN_MB = 0; +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +MNG_LOCAL mng_retcode restore_state (mng_datap pData) +{ +#ifndef MNG_SKIPCHUNK_SAVE + mng_savedatap pSave; +#endif + mng_imagep pImage; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RESTORE_STATE, MNG_LC_START); +#endif + /* restore object 0 status !!! */ + iRetcode = mng_reset_objzero (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* fresh cycle; fake no frames done yet */ + pData->bFramedone = MNG_FALSE; + +#ifndef MNG_SKIPCHUNK_SAVE + if (pData->pSavedata) /* do we have a saved state ? */ + { + pSave = pData->pSavedata; /* address it more directly */ + /* and copy it back to the main struct */ +#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE) + pData->bHasglobalPLTE = pSave->bHasglobalPLTE; + pData->bHasglobalTRNS = pSave->bHasglobalTRNS; + pData->bHasglobalGAMA = pSave->bHasglobalGAMA; + pData->bHasglobalCHRM = pSave->bHasglobalCHRM; + pData->bHasglobalSRGB = pSave->bHasglobalSRGB; + pData->bHasglobalICCP = pSave->bHasglobalICCP; + pData->bHasglobalBKGD = pSave->bHasglobalBKGD; +#endif /* MNG_SUPPORT_READ || MNG_SUPPORT_WRITE */ + +#ifndef MNG_SKIPCHUNK_BACK + pData->iBACKred = pSave->iBACKred; + pData->iBACKgreen = pSave->iBACKgreen; + pData->iBACKblue = pSave->iBACKblue; + pData->iBACKmandatory = pSave->iBACKmandatory; + pData->iBACKimageid = pSave->iBACKimageid; + pData->iBACKtile = pSave->iBACKtile; +#endif + +#ifndef MNG_SKIPCHUNK_FRAM + pData->iFRAMmode = pSave->iFRAMmode; +/* pData->iFRAMdelay = pSave->iFRAMdelay; */ + pData->iFRAMtimeout = pSave->iFRAMtimeout; + pData->bFRAMclipping = pSave->bFRAMclipping; + pData->iFRAMclipl = pSave->iFRAMclipl; + pData->iFRAMclipr = pSave->iFRAMclipr; + pData->iFRAMclipt = pSave->iFRAMclipt; + pData->iFRAMclipb = pSave->iFRAMclipb; + /* NOOOOOOOOOOOO */ +/* pData->iFramemode = pSave->iFRAMmode; + pData->iFramedelay = pSave->iFRAMdelay; + pData->iFrametimeout = pSave->iFRAMtimeout; + pData->bFrameclipping = pSave->bFRAMclipping; + pData->iFrameclipl = pSave->iFRAMclipl; + pData->iFrameclipr = pSave->iFRAMclipr; + pData->iFrameclipt = pSave->iFRAMclipt; + pData->iFrameclipb = pSave->iFRAMclipb; */ + +/* pData->iNextdelay = pSave->iFRAMdelay; */ + pData->iNextdelay = pData->iFramedelay; +#endif + + pData->iGlobalPLTEcount = pSave->iGlobalPLTEcount; + MNG_COPY (pData->aGlobalPLTEentries, pSave->aGlobalPLTEentries, sizeof (mng_rgbpaltab)); + + pData->iGlobalTRNSrawlen = pSave->iGlobalTRNSrawlen; + MNG_COPY (pData->aGlobalTRNSrawdata, pSave->aGlobalTRNSrawdata, 256); + + pData->iGlobalGamma = pSave->iGlobalGamma; + +#ifndef MNG_SKIPCHUNK_cHRM + pData->iGlobalWhitepointx = pSave->iGlobalWhitepointx; + pData->iGlobalWhitepointy = pSave->iGlobalWhitepointy; + pData->iGlobalPrimaryredx = pSave->iGlobalPrimaryredx; + pData->iGlobalPrimaryredy = pSave->iGlobalPrimaryredy; + pData->iGlobalPrimarygreenx = pSave->iGlobalPrimarygreenx; + pData->iGlobalPrimarygreeny = pSave->iGlobalPrimarygreeny; + pData->iGlobalPrimarybluex = pSave->iGlobalPrimarybluex; + pData->iGlobalPrimarybluey = pSave->iGlobalPrimarybluey; +#endif + + pData->iGlobalRendintent = pSave->iGlobalRendintent; + +#ifndef MNG_SKIPCHUNK_iCCP + pData->iGlobalProfilesize = pSave->iGlobalProfilesize; + + if (pData->iGlobalProfilesize) /* has a profile ? */ + { /* then copy that ! */ + MNG_ALLOC (pData, pData->pGlobalProfile, pData->iGlobalProfilesize); + MNG_COPY (pData->pGlobalProfile, pSave->pGlobalProfile, pData->iGlobalProfilesize); + } +#endif + +#ifndef MNG_SKIPCHUNK_bKGD + pData->iGlobalBKGDred = pSave->iGlobalBKGDred; + pData->iGlobalBKGDgreen = pSave->iGlobalBKGDgreen; + pData->iGlobalBKGDblue = pSave->iGlobalBKGDblue; +#endif + } + else /* no saved-data; so reset the lot */ +#endif /* SKIPCHUNK_SAVE */ + { +#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE) + pData->bHasglobalPLTE = MNG_FALSE; + pData->bHasglobalTRNS = MNG_FALSE; + pData->bHasglobalGAMA = MNG_FALSE; + pData->bHasglobalCHRM = MNG_FALSE; + pData->bHasglobalSRGB = MNG_FALSE; + pData->bHasglobalICCP = MNG_FALSE; + pData->bHasglobalBKGD = MNG_FALSE; +#endif /* MNG_SUPPORT_READ || MNG_SUPPORT_WRITE */ + +#ifndef MNG_SKIPCHUNK_TERM + if (!pData->bMisplacedTERM) /* backward compatible ugliness !!! */ + { + pData->iBACKred = 0; + pData->iBACKgreen = 0; + pData->iBACKblue = 0; + pData->iBACKmandatory = 0; + pData->iBACKimageid = 0; + pData->iBACKtile = 0; + } +#endif + +#ifndef MNG_SKIPCHUNK_FRAM + pData->iFRAMmode = 1; +/* pData->iFRAMdelay = 1; */ + pData->iFRAMtimeout = 0x7fffffffl; + pData->bFRAMclipping = MNG_FALSE; + pData->iFRAMclipl = 0; + pData->iFRAMclipr = 0; + pData->iFRAMclipt = 0; + pData->iFRAMclipb = 0; + /* NOOOOOOOOOOOO */ +/* pData->iFramemode = 1; + pData->iFramedelay = 1; + pData->iFrametimeout = 0x7fffffffl; + pData->bFrameclipping = MNG_FALSE; + pData->iFrameclipl = 0; + pData->iFrameclipr = 0; + pData->iFrameclipt = 0; + pData->iFrameclipb = 0; */ + +/* pData->iNextdelay = 1; */ + pData->iNextdelay = pData->iFramedelay; +#endif + + pData->iGlobalPLTEcount = 0; + + pData->iGlobalTRNSrawlen = 0; + + pData->iGlobalGamma = 0; + +#ifndef MNG_SKIPCHUNK_cHRM + pData->iGlobalWhitepointx = 0; + pData->iGlobalWhitepointy = 0; + pData->iGlobalPrimaryredx = 0; + pData->iGlobalPrimaryredy = 0; + pData->iGlobalPrimarygreenx = 0; + pData->iGlobalPrimarygreeny = 0; + pData->iGlobalPrimarybluex = 0; + pData->iGlobalPrimarybluey = 0; +#endif + + pData->iGlobalRendintent = 0; + +#ifndef MNG_SKIPCHUNK_iCCP + if (pData->iGlobalProfilesize) /* free a previous profile ? */ + MNG_FREE (pData, pData->pGlobalProfile, pData->iGlobalProfilesize); + + pData->iGlobalProfilesize = 0; +#endif + +#ifndef MNG_SKIPCHUNK_bKGD + pData->iGlobalBKGDred = 0; + pData->iGlobalBKGDgreen = 0; + pData->iGlobalBKGDblue = 0; +#endif + } + +#ifndef MNG_SKIPCHUNK_TERM + if (!pData->bMisplacedTERM) /* backward compatible ugliness !!! */ + { + pImage = (mng_imagep)pData->pFirstimgobj; + /* drop un-frozen image objects */ + while (pImage) + { + mng_imagep pNext = (mng_imagep)pImage->sHeader.pNext; + + if (!pImage->bFrozen) /* is it un-frozen ? */ + { + mng_imagep pPrev = (mng_imagep)pImage->sHeader.pPrev; + + if (pPrev) /* unlink it */ + pPrev->sHeader.pNext = pNext; + else + pData->pFirstimgobj = pNext; + + if (pNext) + pNext->sHeader.pPrev = pPrev; + else + pData->pLastimgobj = pPrev; + + if (pImage->pImgbuf->bFrozen) /* buffer frozen ? */ + { + if (pImage->pImgbuf->iRefcount < 2) + MNG_ERROR (pData, MNG_INTERNALERROR); + /* decrease ref counter */ + pImage->pImgbuf->iRefcount--; + /* just cleanup the object then */ + MNG_FREEX (pData, pImage, sizeof (mng_image)); + } + else + { /* free the image buffer */ + iRetcode = mng_free_imagedataobject (pData, pImage->pImgbuf); + /* and cleanup the object */ + MNG_FREEX (pData, pImage, sizeof (mng_image)); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + } + + pImage = pNext; /* neeeext */ + } + } +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RESTORE_STATE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ +/* * * */ +/* * General display processing routine * */ +/* * * */ +/* ************************************************************************** */ + +mng_retcode mng_process_display (mng_datap pData) +{ + mng_retcode iRetcode = MNG_NOERROR; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY, MNG_LC_START); +#endif + + if (!pData->iBreakpoint) /* not broken previously ? */ + { + if ((pData->iRequestframe) || (pData->iRequestlayer) || (pData->iRequesttime)) + { + pData->bSearching = MNG_TRUE; /* indicate we're searching */ + + iRetcode = clear_canvas (pData); /* make the canvas virgin black ?!? */ + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* let's start from the top, shall we */ + pData->pCurraniobj = pData->pFirstaniobj; + } + } + + do /* process the objects */ + { + if (pData->bSearching) /* clear timer-flag when searching !!! */ + pData->bTimerset = MNG_FALSE; + /* do we need to finish something first ? */ + if ((pData->iBreakpoint) && (pData->iBreakpoint < 99)) + { + switch (pData->iBreakpoint) /* return to broken display routine */ + { +#ifndef MNG_SKIPCHUNK_FRAM + case 1 : { iRetcode = mng_process_display_fram2 (pData); break; } +#endif +#ifndef MNG_SKIPCHUNK_SHOW + case 3 : ; /* same as 4 !!! */ + case 4 : { iRetcode = mng_process_display_show (pData); break; } +#endif +#ifndef MNG_SKIPCHUNK_CLON + case 5 : { iRetcode = mng_process_display_clon2 (pData); break; } +#endif +#ifndef MNG_SKIPCHUNK_MAGN + case 9 : { iRetcode = mng_process_display_magn2 (pData); break; } + case 10 : { iRetcode = mng_process_display_mend2 (pData); break; } +#endif +#ifndef MNG_SKIPCHUNK_PAST + case 11 : { iRetcode = mng_process_display_past2 (pData); break; } +#endif + default : MNG_ERROR (pData, MNG_INTERNALERROR); + } + } + else + { + if (pData->pCurraniobj) + iRetcode = ((mng_object_headerp)pData->pCurraniobj)->fProcess (pData, pData->pCurraniobj); + } + + if (!pData->bTimerset) /* reset breakpoint flag ? */ + pData->iBreakpoint = 0; + /* can we advance to next object ? */ + if ((!iRetcode) && (pData->pCurraniobj) && + (!pData->bTimerset) && (!pData->bSectionwait)) + { + pData->pCurraniobj = ((mng_object_headerp)pData->pCurraniobj)->pNext; + /* MEND processing to be done ? */ + if ((pData->eImagetype == mng_it_mng) && (!pData->pCurraniobj)) + iRetcode = mng_process_display_mend (pData); + + if (!pData->pCurraniobj) /* refresh after last image ? */ + pData->bNeedrefresh = MNG_TRUE; + } + + if (pData->bSearching) /* are we looking for something ? */ + { + if ((pData->iRequestframe) && (pData->iRequestframe <= pData->iFrameseq)) + { + pData->iRequestframe = 0; /* found the frame ! */ + pData->bSearching = MNG_FALSE; + } + else + if ((pData->iRequestlayer) && (pData->iRequestlayer <= pData->iLayerseq)) + { + pData->iRequestlayer = 0; /* found the layer ! */ + pData->bSearching = MNG_FALSE; + } + else + if ((pData->iRequesttime) && (pData->iRequesttime <= pData->iFrametime)) + { + pData->iRequesttime = 0; /* found the playtime ! */ + pData->bSearching = MNG_FALSE; + } + } + } /* until error or a break or no more objects */ + while ((!iRetcode) && (pData->pCurraniobj) && + (((pData->bRunning) && (!pData->bTimerset)) || (pData->bSearching)) && + (!pData->bSectionwait) && (!pData->bFreezing)); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* refresh needed ? */ + if ((!pData->bTimerset) && (pData->bNeedrefresh)) + { + iRetcode = mng_display_progressive_refresh (pData, 1); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + /* timer break ? */ + if ((pData->bTimerset) && (!pData->iBreakpoint)) + pData->iBreakpoint = 99; + else + if (!pData->bTimerset) + pData->iBreakpoint = 0; /* reset if no timer break */ + + if ((!pData->bTimerset) && (!pData->pCurraniobj)) + pData->bRunning = MNG_FALSE; /* all done now ! */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ +/* * * */ +/* * Chunk display processing routines * */ +/* * * */ +/* ************************************************************************** */ + +#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT +png_imgtype mng_png_imgtype(mng_uint8 colortype, mng_uint8 bitdepth) +{ + png_imgtype ret; + switch (bitdepth) + { +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case 1: + { + png_imgtype imgtype[]={png_g1,png_none,png_none,png_idx1}; + ret=imgtype[colortype]; + break; + } + case 2: + { + png_imgtype imgtype[]={png_g2,png_none,png_none,png_idx2}; + ret=imgtype[colortype]; + break; + } + case 4: + { + png_imgtype imgtype[]={png_g4,png_none,png_none,png_idx4}; + ret=imgtype[colortype]; + break; + } +#endif + case 8: + { + png_imgtype imgtype[]={png_g8,png_none,png_rgb8,png_idx8,png_ga8, + png_none,png_rgba8}; + ret=imgtype[colortype]; + break; + } +#ifndef MNG_NO_16BIT_SUPPORT + case 16: + { + png_imgtype imgtype[]={png_g16,png_none,png_rgb16,png_none,png_ga16, + png_none,png_rgba16}; + ret=imgtype[colortype]; + break; + } +#endif + default: + ret=png_none; + break; + } + return (ret); +} +#endif /* MNG_OPTIMIZE_FOOTPRINT_INIT */ + +/* ************************************************************************** */ + +mng_retcode mng_process_display_ihdr (mng_datap pData) +{ /* address the current "object" if any */ + mng_imagep pImage = (mng_imagep)pData->pCurrentobj; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_IHDR, MNG_LC_START); +#endif + + if (!pData->bHasDHDR) + { + pData->fInitrowproc = MNG_NULL; /* do nothing by default */ + pData->fDisplayrow = MNG_NULL; + pData->fCorrectrow = MNG_NULL; + pData->fStorerow = MNG_NULL; + pData->fProcessrow = MNG_NULL; + pData->fDifferrow = MNG_NULL; + pData->pStoreobj = MNG_NULL; + } + + if (!pData->iBreakpoint) /* not previously broken ? */ + { + mng_retcode iRetcode = MNG_NOERROR; + +#ifndef MNG_NO_DELTA_PNG + if (pData->bHasDHDR) /* is a delta-image ? */ + { + if (pData->iDeltatype == MNG_DELTATYPE_REPLACE) + iRetcode = mng_reset_object_details (pData, (mng_imagep)pData->pDeltaImage, + pData->iDatawidth, pData->iDataheight, + pData->iBitdepth, pData->iColortype, + pData->iCompression, pData->iFilter, + pData->iInterlace, MNG_TRUE); + else + if ((pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) ) + { + ((mng_imagep)pData->pDeltaImage)->pImgbuf->iPixelsampledepth = pData->iBitdepth; + ((mng_imagep)pData->pDeltaImage)->pImgbuf->iAlphasampledepth = pData->iBitdepth; + } + else + if ((pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAADD ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAREPLACE) ) + ((mng_imagep)pData->pDeltaImage)->pImgbuf->iAlphasampledepth = pData->iBitdepth; + else + if ((pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORADD ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORREPLACE) ) + ((mng_imagep)pData->pDeltaImage)->pImgbuf->iPixelsampledepth = pData->iBitdepth; + + if (!iRetcode) + { /* process immediately if bitdepth & colortype are equal */ + pData->bDeltaimmediate = + (mng_bool)((pData->iBitdepth == ((mng_imagep)pData->pDeltaImage)->pImgbuf->iBitdepth ) && + (pData->iColortype == ((mng_imagep)pData->pDeltaImage)->pImgbuf->iColortype) ); + /* be sure to reset object 0 */ + iRetcode = mng_reset_object_details (pData, (mng_imagep)pData->pObjzero, + pData->iDatawidth, pData->iDataheight, + pData->iBitdepth, pData->iColortype, + pData->iCompression, pData->iFilter, + pData->iInterlace, MNG_TRUE); + } + } + else +#endif + { + if (pImage) /* update object buffer ? */ + iRetcode = mng_reset_object_details (pData, pImage, + pData->iDatawidth, pData->iDataheight, + pData->iBitdepth, pData->iColortype, + pData->iCompression, pData->iFilter, + pData->iInterlace, MNG_TRUE); + else + iRetcode = mng_reset_object_details (pData, (mng_imagep)pData->pObjzero, + pData->iDatawidth, pData->iDataheight, + pData->iBitdepth, pData->iColortype, + pData->iCompression, pData->iFilter, + pData->iInterlace, MNG_TRUE); + } + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + +#ifndef MNG_NO_DELTA_PNG + if (!pData->bHasDHDR) +#endif + { + if (pImage) /* real object ? */ + pData->pStoreobj = pImage; /* tell the row routines */ + else /* otherwise use object 0 */ + pData->pStoreobj = pData->pObjzero; + +#if !defined(MNG_INCLUDE_MPNG_PROPOSAL) && !defined(MNG_INCLUDE_ANG_PROPOSAL) + if ( /* display "on-the-fly" ? */ +#ifndef MNG_SKIPCHUNK_MAGN + (((mng_imagep)pData->pStoreobj)->iMAGN_MethodX == 0) && + (((mng_imagep)pData->pStoreobj)->iMAGN_MethodY == 0) && +#endif + ( (pData->eImagetype == mng_it_png ) || + (((mng_imagep)pData->pStoreobj)->bVisible) ) ) + { + next_layer (pData); /* that's a new layer then ! */ + + if (pData->bTimerset) /* timer break ? */ + pData->iBreakpoint = 2; + else + { + pData->iBreakpoint = 0; + /* anything to display ? */ + if ((pData->iDestr > pData->iDestl) && (pData->iDestb > pData->iDestt)) + set_display_routine (pData); /* then determine display routine */ + } + } +#endif + } + + if (!pData->bTimerset) /* no timer break ? */ + { +#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT + pData->fInitrowproc = (mng_fptr)mng_init_rowproc; + pData->ePng_imgtype=mng_png_imgtype(pData->iColortype,pData->iBitdepth); +#else + switch (pData->iColortype) /* determine row initialization routine */ + { + case 0 : { /* gray */ + switch (pData->iBitdepth) + { +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case 1 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_g1_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_g1_i; + + break; + } + case 2 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_g2_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_g2_i; + + break; + } + case 4 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_g4_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_g4_i; + break; + } +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + case 8 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_g8_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_g8_i; + + break; + } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_g16_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_g16_i; + + break; + } +#endif + } + + break; + } + case 2 : { /* rgb */ + switch (pData->iBitdepth) + { + case 8 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_rgb8_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_rgb8_i; + break; + } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_rgb16_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_rgb16_i; + + break; + } +#endif + } + + break; + } + case 3 : { /* indexed */ + switch (pData->iBitdepth) + { +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case 1 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_idx1_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_idx1_i; + + break; + } + case 2 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_idx2_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_idx2_i; + + break; + } + case 4 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_idx4_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_idx4_i; + + break; + } +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + case 8 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_idx8_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_idx8_i; + + break; + } + } + + break; + } + case 4 : { /* gray+alpha */ + switch (pData->iBitdepth) + { + case 8 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_ga8_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_ga8_i; + + break; + } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_ga16_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_ga16_i; + break; + } +#endif + } + + break; + } + case 6 : { /* rgb+alpha */ + switch (pData->iBitdepth) + { + case 8 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_rgba8_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_rgba8_i; + + break; + } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_rgba16_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_rgba16_i; + + break; + } +#endif + } + + break; + } + } +#endif /* MNG_OPTIMIZE_FOOTPRINT_INIT */ + + pData->iFilterofs = 0; /* determine filter characteristics */ + pData->iLevel0 = 0; /* default levels */ + pData->iLevel1 = 0; + pData->iLevel2 = 0; + pData->iLevel3 = 0; + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + { + switch (pData->iColortype) + { + case 0 : { + if (pData->iBitdepth <= 8) + pData->iFilterofs = 1; + else + pData->iFilterofs = 2; + + break; + } + case 2 : { + if (pData->iBitdepth <= 8) + pData->iFilterofs = 3; + else + pData->iFilterofs = 6; + + break; + } + case 3 : { + pData->iFilterofs = 1; + break; + } + case 4 : { + if (pData->iBitdepth <= 8) + pData->iFilterofs = 2; + else + pData->iFilterofs = 4; + + break; + } + case 6 : { + if (pData->iBitdepth <= 8) + pData->iFilterofs = 4; + else + pData->iFilterofs = 8; + + break; + } + } + } +#endif + +#ifdef FILTER193 /* no adaptive filtering ? */ + if (pData->iFilter == MNG_FILTER_NOFILTER) + pData->iPixelofs = pData->iFilterofs; + else +#endif + pData->iPixelofs = pData->iFilterofs + 1; + + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_IHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_MPNG_PROPOSAL +mng_retcode mng_process_display_mpng (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_MPNG, MNG_LC_START); +#endif + + pData->iAlphadepth = 8; /* assume transparency !! */ + + if (pData->fProcessheader) /* inform the app (creating the output canvas) ? */ + { + pData->iWidth = ((mng_mpng_objp)pData->pMPNG)->iFramewidth; + pData->iHeight = ((mng_mpng_objp)pData->pMPNG)->iFrameheight; + + if (!pData->fProcessheader (((mng_handle)pData), pData->iWidth, pData->iHeight)) + MNG_ERROR (pData, MNG_APPMISCERROR); + } + + next_layer (pData); /* first mPNG layer then ! */ + pData->bTimerset = MNG_FALSE; + pData->iBreakpoint = 0; + + if ((pData->iDestr > pData->iDestl) && (pData->iDestb > pData->iDestt)) + set_display_routine (pData); /* then determine display routine */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_MPNG, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_ANG_PROPOSAL +mng_retcode mng_process_display_ang (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_ANG, MNG_LC_START); +#endif + + if (pData->fProcessheader) /* inform the app (creating the output canvas) ? */ + { + if (!pData->fProcessheader (((mng_handle)pData), pData->iWidth, pData->iHeight)) + MNG_ERROR (pData, MNG_APPMISCERROR); + } + + next_layer (pData); /* first mPNG layer then ! */ + pData->bTimerset = MNG_FALSE; + pData->iBreakpoint = 0; + + if ((pData->iDestr > pData->iDestl) && (pData->iDestb > pData->iDestt)) + set_display_routine (pData); /* then determine display routine */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_ANG, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS +mng_retcode mng_process_display_idat (mng_datap pData, + mng_uint32 iRawlen, + mng_uint8p pRawdata) +#else +mng_retcode mng_process_display_idat (mng_datap pData) +#endif +{ + mng_retcode iRetcode = MNG_NOERROR; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_IDAT, MNG_LC_START); +#endif + +#if defined(MNG_INCLUDE_MPNG_PROPOSAL) || defined(MNG_INCLUDE_ANG_PROPOSAL) + if ((pData->eImagetype == mng_it_png) && (pData->iLayerseq <= 0)) + { + if (pData->fProcessheader) /* inform the app (creating the output canvas) ? */ + if (!pData->fProcessheader (((mng_handle)pData), pData->iWidth, pData->iHeight)) + MNG_ERROR (pData, MNG_APPMISCERROR); + + next_layer (pData); /* first regular PNG layer then ! */ + pData->bTimerset = MNG_FALSE; + pData->iBreakpoint = 0; + + if ((pData->iDestr > pData->iDestl) && (pData->iDestb > pData->iDestt)) + set_display_routine (pData); /* then determine display routine */ + } +#endif + + if (pData->bRestorebkgd) /* need to restore the background ? */ + { + pData->bRestorebkgd = MNG_FALSE; + iRetcode = load_bkgdlayer (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + pData->iLayerseq++; /* and it counts as a layer then ! */ + } + + if (pData->fInitrowproc) /* need to initialize row processing? */ + { + iRetcode = ((mng_initrowproc)pData->fInitrowproc) (pData); + pData->fInitrowproc = MNG_NULL; /* only call this once !!! */ + } + + if ((!iRetcode) && (!pData->bInflating)) + /* initialize inflate */ + iRetcode = mngzlib_inflateinit (pData); + + if (!iRetcode) /* all ok? then inflate, my man */ +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + iRetcode = mngzlib_inflaterows (pData, iRawlen, pRawdata); +#else + iRetcode = mngzlib_inflaterows (pData, pData->iRawlen, pData->pRawdata); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_IDAT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_process_display_iend (mng_datap pData) +{ + mng_retcode iRetcode, iRetcode2; + mng_bool bDodisplay = MNG_FALSE; + mng_bool bMagnify = MNG_FALSE; + mng_bool bCleanup = (mng_bool)(pData->iBreakpoint != 0); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_IEND, MNG_LC_START); +#endif + +#ifdef MNG_INCLUDE_JNG /* progressive+alpha JNG can be displayed now */ + if ( (pData->bHasJHDR ) && + ( (pData->bJPEGprogressive) || (pData->bJPEGprogressive2)) && + ( (pData->eImagetype == mng_it_jng ) || + (((mng_imagep)pData->pStoreobj)->bVisible) ) && + ( (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGGRAYA ) || + (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGCOLORA) ) ) + bDodisplay = MNG_TRUE; +#endif + +#ifndef MNG_SKIPCHUNK_MAGN + if ( (pData->pStoreobj) && /* on-the-fly magnification ? */ + ( (((mng_imagep)pData->pStoreobj)->iMAGN_MethodX) || + (((mng_imagep)pData->pStoreobj)->iMAGN_MethodY) ) ) + bMagnify = MNG_TRUE; +#endif + + if ((pData->bHasBASI) || /* was it a BASI stream */ + (bDodisplay) || /* or should we display the JNG */ +#ifndef MNG_SKIPCHUNK_MAGN + (bMagnify) || /* or should we magnify it */ +#endif + /* or did we get broken here last time ? */ + ((pData->iBreakpoint) && (pData->iBreakpoint != 8))) + { + mng_imagep pImage = (mng_imagep)pData->pCurrentobj; + + if (!pImage) /* or was it object 0 ? */ + pImage = (mng_imagep)pData->pObjzero; + /* display it now then ? */ + if ((pImage->bVisible) && (pImage->bViewable)) + { /* ok, so do it */ + iRetcode = mng_display_image (pData, pImage, bDodisplay); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + if (pData->bTimerset) /* timer break ? */ + pData->iBreakpoint = 6; + } + } +#ifndef MNG_NO_DELTA_PNG + else + if ((pData->bHasDHDR) || /* was it a DHDR stream */ + (pData->iBreakpoint == 8)) /* or did we get broken here last time ? */ + { + mng_imagep pImage = (mng_imagep)pData->pDeltaImage; + + if (!pData->iBreakpoint) + { /* perform the delta operations needed */ + iRetcode = mng_execute_delta_image (pData, pImage, (mng_imagep)pData->pObjzero); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + /* display it now then ? */ + if ((pImage->bVisible) && (pImage->bViewable)) + { /* ok, so do it */ + iRetcode = mng_display_image (pData, pImage, MNG_FALSE); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + if (pData->bTimerset) /* timer break ? */ + pData->iBreakpoint = 8; + } + } +#endif + + if (!pData->bTimerset) /* can we continue ? */ + { + pData->iBreakpoint = 0; /* clear this flag now ! */ + + +#ifdef MNG_INCLUDE_MPNG_PROPOSAL + if (pData->eImagetype == mng_it_mpng) + { + pData->pCurraniobj = pData->pFirstaniobj; + } else +#endif +#ifdef MNG_INCLUDE_ANG_PROPOSAL + if (pData->eImagetype == mng_it_ang) + { + pData->pCurraniobj = pData->pFirstaniobj; + } else +#endif + { /* cleanup object 0 */ + mng_reset_object_details (pData, (mng_imagep)pData->pObjzero, + 0, 0, 0, 0, 0, 0, 0, MNG_TRUE); + } + + if (pData->bInflating) /* if we've been inflating */ + { /* cleanup row-processing, */ + iRetcode = mng_cleanup_rowproc (pData); + /* also cleanup inflate! */ + iRetcode2 = mngzlib_inflatefree (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + if (iRetcode2) + return iRetcode2; + } + +#ifdef MNG_INCLUDE_JNG + if (pData->bJPEGdecompress) /* if we've been decompressing JDAT */ + { /* cleanup row-processing, */ + iRetcode = mng_cleanup_rowproc (pData); + /* also cleanup decompress! */ + iRetcode2 = mngjpeg_decompressfree (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + if (iRetcode2) + return iRetcode2; + } + + if (pData->bJPEGdecompress2) /* if we've been decompressing JDAA */ + { /* cleanup row-processing, */ + iRetcode = mng_cleanup_rowproc (pData); + /* also cleanup decompress! */ + iRetcode2 = mngjpeg_decompressfree2 (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + if (iRetcode2) + return iRetcode2; + } +#endif + + if (bCleanup) /* if we got broken last time we need to cleanup */ + { + pData->bHasIHDR = MNG_FALSE; /* IEND signals the end for most ... */ + pData->bHasBASI = MNG_FALSE; + pData->bHasDHDR = MNG_FALSE; +#ifdef MNG_INCLUDE_JNG + pData->bHasJHDR = MNG_FALSE; + pData->bHasJSEP = MNG_FALSE; + pData->bHasJDAA = MNG_FALSE; + pData->bHasJDAT = MNG_FALSE; +#endif + pData->bHasPLTE = MNG_FALSE; + pData->bHasTRNS = MNG_FALSE; + pData->bHasGAMA = MNG_FALSE; + pData->bHasCHRM = MNG_FALSE; + pData->bHasSRGB = MNG_FALSE; + pData->bHasICCP = MNG_FALSE; + pData->bHasBKGD = MNG_FALSE; + pData->bHasIDAT = MNG_FALSE; + } + /* if the image was displayed on the fly, */ + /* we'll have to make the app refresh */ + if ((pData->eImagetype != mng_it_mng) && (pData->fDisplayrow)) + pData->bNeedrefresh = MNG_TRUE; + + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_IEND, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +/* change in the MNG spec with regards to TERM delay & interframe_delay + as proposed by Adam M. Costello (option 4) and finalized by official vote + during december 2002 / check the 'mng-list' archives for more details */ + +mng_retcode mng_process_display_mend (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_MEND, MNG_LC_START); +#endif + +#ifdef MNG_SUPPORT_DYNAMICMNG + if (pData->bStopafterseek) /* need to stop after this ? */ + { + pData->bFreezing = MNG_TRUE; /* stop processing on this one */ + pData->bRunningevent = MNG_FALSE; + pData->bStopafterseek = MNG_FALSE; + pData->bNeedrefresh = MNG_TRUE; /* make sure the last bit is displayed ! */ + } +#endif + +#ifndef MNG_SKIPCHUNK_TERM + /* TERM processed ? */ + if ((pData->bDisplaying) && (pData->bRunning) && + (pData->bHasTERM) && (pData->pTermaniobj)) + { + mng_retcode iRetcode; + mng_ani_termp pTERM; + /* get the right animation object ! */ + pTERM = (mng_ani_termp)pData->pTermaniobj; + + pData->iIterations++; /* increase iteration count */ + + switch (pTERM->iTermaction) /* determine what to do! */ + { + case 0 : { /* show last frame indefinitly */ + break; /* piece of cake, that is... */ + } + + case 1 : { /* cease displaying anything */ + /* max(1, TERM delay, interframe_delay) */ +#ifndef MNG_SKIPCHUNK_FRAM + if (pTERM->iDelay > pData->iFramedelay) + pData->iFramedelay = pTERM->iDelay; + if (!pData->iFramedelay) + pData->iFramedelay = 1; +#endif + + iRetcode = interframe_delay (pData); + /* no interframe_delay? then fake it */ + if ((!iRetcode) && (!pData->bTimerset)) + iRetcode = set_delay (pData, 1); + + if (iRetcode) + return iRetcode; + + pData->iBreakpoint = 10; + break; + } + + case 2 : { /* show first image after TERM */ + iRetcode = restore_state (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* notify the app ? */ + if (pData->fProcessmend) + if (!pData->fProcessmend ((mng_handle)pData, pData->iIterations, 0)) + MNG_ERROR (pData, MNG_APPMISCERROR); + + /* show first frame after TERM chunk */ + pData->pCurraniobj = pTERM; + pData->bOnlyfirstframe = MNG_TRUE; + pData->iFramesafterTERM = 0; + + /* max(1, TERM delay, interframe_delay) */ +#ifndef MNG_SKIPCHUNK_FRAM + if (pTERM->iDelay > pData->iFramedelay) + pData->iFramedelay = pTERM->iDelay; + if (!pData->iFramedelay) + pData->iFramedelay = 1; +#endif + + break; + } + + case 3 : { /* repeat */ + if ((pTERM->iItermax) && (pTERM->iItermax < 0x7FFFFFFF)) + pTERM->iItermax--; + + if (pTERM->iItermax) /* go back to TERM ? */ + { /* restore to initial or SAVE state */ + iRetcode = restore_state (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* notify the app ? */ + if (pData->fProcessmend) + if (!pData->fProcessmend ((mng_handle)pData, + pData->iIterations, pTERM->iItermax)) + MNG_ERROR (pData, MNG_APPMISCERROR); + + /* restart from TERM chunk */ + pData->pCurraniobj = pTERM; + + if (pTERM->iDelay) /* set the delay (?) */ + { + /* max(1, TERM delay, interframe_delay) */ +#ifndef MNG_SKIPCHUNK_FRAM + if (pTERM->iDelay > pData->iFramedelay) + pData->iFramedelay = pTERM->iDelay; + if (!pData->iFramedelay) + pData->iFramedelay = 1; +#endif + + pData->bNeedrefresh = MNG_TRUE; + } + } + else + { + switch (pTERM->iIteraction) + { + case 0 : { /* show last frame indefinitly */ + break; /* piece of cake, that is... */ + } + + case 1 : { /* cease displaying anything */ + /* max(1, TERM delay, interframe_delay) */ +#ifndef MNG_SKIPCHUNK_FRAM + if (pTERM->iDelay > pData->iFramedelay) + pData->iFramedelay = pTERM->iDelay; + if (!pData->iFramedelay) + pData->iFramedelay = 1; +#endif + + iRetcode = interframe_delay (pData); + /* no interframe_delay? then fake it */ + if ((!iRetcode) && (!pData->bTimerset)) + iRetcode = set_delay (pData, 1); + + if (iRetcode) + return iRetcode; + + pData->iBreakpoint = 10; + break; + } + + case 2 : { /* show first image after TERM */ + iRetcode = restore_state (pData); + /* on error bail out */ + if (iRetcode) + return iRetcode; + /* notify the app ? */ + if (pData->fProcessmend) + if (!pData->fProcessmend ((mng_handle)pData, + pData->iIterations, 0)) + MNG_ERROR (pData, MNG_APPMISCERROR); + + /* show first frame after TERM chunk */ + pData->pCurraniobj = pTERM; + pData->bOnlyfirstframe = MNG_TRUE; + pData->iFramesafterTERM = 0; + /* max(1, TERM delay, interframe_delay) */ +#ifndef MNG_SKIPCHUNK_FRAM + if (pTERM->iDelay > pData->iFramedelay) + pData->iFramedelay = pTERM->iDelay; + if (!pData->iFramedelay) + pData->iFramedelay = 1; +#endif + + break; + } + } + } + + break; + } + } + } +#endif /* MNG_SKIPCHUNK_TERM */ + /* just reading ? */ + if ((!pData->bDisplaying) && (pData->bReading)) + if (pData->fProcessmend) /* inform the app ? */ + if (!pData->fProcessmend ((mng_handle)pData, 0, 0)) + MNG_ERROR (pData, MNG_APPMISCERROR); + + if (!pData->pCurraniobj) /* always let the app refresh at the end ! */ + pData->bNeedrefresh = MNG_TRUE; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_MEND, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_process_display_mend2 (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_MEND, MNG_LC_START); +#endif + +#ifndef MNG_SKIPCHUNK_FRAM + pData->bFrameclipping = MNG_FALSE; /* nothing to do but restore the app background */ +#endif + load_bkgdlayer (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_MEND, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_DEFI +mng_retcode mng_process_display_defi (mng_datap pData) +{ + mng_imagep pImage; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_DEFI, MNG_LC_START); +#endif + + if (!pData->iDEFIobjectid) /* object id=0 ? */ + { + pImage = (mng_imagep)pData->pObjzero; + + if (pData->bDEFIhasdonotshow) + pImage->bVisible = (mng_bool)(pData->iDEFIdonotshow == 0); + + if (pData->bDEFIhasloca) + { + pImage->iPosx = pData->iDEFIlocax; + pImage->iPosy = pData->iDEFIlocay; + } + + if (pData->bDEFIhasclip) + { + pImage->bClipped = pData->bDEFIhasclip; + pImage->iClipl = pData->iDEFIclipl; + pImage->iClipr = pData->iDEFIclipr; + pImage->iClipt = pData->iDEFIclipt; + pImage->iClipb = pData->iDEFIclipb; + } + + pData->pCurrentobj = 0; /* not a real object ! */ + } + else + { /* already exists ? */ + pImage = (mng_imagep)mng_find_imageobject (pData, pData->iDEFIobjectid); + + if (!pImage) /* if not; create new */ + { + mng_retcode iRetcode = mng_create_imageobject (pData, pData->iDEFIobjectid, + (mng_bool)(pData->iDEFIconcrete == 1), + (mng_bool)(pData->iDEFIdonotshow == 0), + MNG_FALSE, 0, 0, 0, 0, 0, 0, 0, + pData->iDEFIlocax, pData->iDEFIlocay, + pData->bDEFIhasclip, + pData->iDEFIclipl, pData->iDEFIclipr, + pData->iDEFIclipt, pData->iDEFIclipb, + &pImage); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + else + { /* exists; then set new info */ + if (pData->bDEFIhasdonotshow) + pImage->bVisible = (mng_bool)(pData->iDEFIdonotshow == 0); + + pImage->bViewable = MNG_FALSE; + + if (pData->bDEFIhasloca) + { + pImage->iPosx = pData->iDEFIlocax; + pImage->iPosy = pData->iDEFIlocay; + } + + if (pData->bDEFIhasclip) + { + pImage->bClipped = pData->bDEFIhasclip; + pImage->iClipl = pData->iDEFIclipl; + pImage->iClipr = pData->iDEFIclipr; + pImage->iClipt = pData->iDEFIclipt; + pImage->iClipb = pData->iDEFIclipb; + } + + if (pData->bDEFIhasconcrete) + pImage->pImgbuf->bConcrete = (mng_bool)(pData->iDEFIconcrete == 1); + } + + pData->pCurrentobj = pImage; /* others may want to know this */ + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_DEFI, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_BASI +#ifndef MNG_OPTIMIZE_DISPLAYCALLS +mng_retcode mng_process_display_basi (mng_datap pData, + mng_uint16 iRed, + mng_uint16 iGreen, + mng_uint16 iBlue, + mng_bool bHasalpha, + mng_uint16 iAlpha, + mng_uint8 iViewable) +#else +mng_retcode mng_process_display_basi (mng_datap pData) +#endif +{ /* address the current "object" if any */ + mng_imagep pImage = (mng_imagep)pData->pCurrentobj; + mng_uint8p pWork; + mng_uint32 iX; + mng_imagedatap pBuf; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_BASI, MNG_LC_START); +#endif + + if (!pImage) /* or is it an "on-the-fly" image ? */ + pImage = (mng_imagep)pData->pObjzero; + /* address the object-buffer */ + pBuf = pImage->pImgbuf; + + pData->fDisplayrow = MNG_NULL; /* do nothing by default */ + pData->fCorrectrow = MNG_NULL; + pData->fStorerow = MNG_NULL; + pData->fProcessrow = MNG_NULL; + /* set parms now that they're known */ + iRetcode = mng_reset_object_details (pData, pImage, pData->iDatawidth, + pData->iDataheight, pData->iBitdepth, + pData->iColortype, pData->iCompression, + pData->iFilter, pData->iInterlace, MNG_FALSE); + if (iRetcode) /* on error bail out */ + return iRetcode; + /* save the viewable flag */ +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + pImage->bViewable = (mng_bool)(iViewable == 1); +#else + pImage->bViewable = (mng_bool)(pData->iBASIviewable == 1); +#endif + pBuf->bViewable = pImage->bViewable; + pData->pStoreobj = pImage; /* let row-routines know which object */ + + pWork = pBuf->pImgdata; /* fill the object-buffer with the specified + "color" sample */ + switch (pData->iColortype) /* depending on color_type & bit_depth */ + { + case 0 : { /* gray */ +#ifndef MNG_NO_16BIT_SUPPORT + if (pData->iBitdepth == 16) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iDatawidth * pData->iDataheight; + iX > 0;iX--) +#else + for (iX = 0; iX < pData->iDatawidth * pData->iDataheight; iX++) +#endif + { +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + mng_put_uint16 (pWork, iRed); +#else + mng_put_uint16 (pWork, pData->iBASIred); +#endif + pWork += 2; + } + } + else +#endif + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iDatawidth * pData->iDataheight; + iX > 0;iX--) +#else + for (iX = 0; iX < pData->iDatawidth * pData->iDataheight; iX++) +#endif + { +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + *pWork = (mng_uint8)iRed; +#else + *pWork = (mng_uint8)pData->iBASIred; +#endif + pWork++; + } + } + /* force tRNS ? */ +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + if ((bHasalpha) && (!iAlpha)) +#else + if ((pData->bBASIhasalpha) && (!pData->iBASIalpha)) +#endif + { + pBuf->bHasTRNS = MNG_TRUE; +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + pBuf->iTRNSgray = iRed; +#else + pBuf->iTRNSgray = pData->iBASIred; +#endif + } + + break; + } + + case 2 : { /* rgb */ +#ifndef MNG_NO_16BIT_SUPPORT + if (pData->iBitdepth == 16) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iDatawidth * pData->iDataheight; + iX > 0;iX--) +#else + for (iX = 0; iX < pData->iDatawidth * pData->iDataheight; iX++) +#endif + { +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + mng_put_uint16 (pWork, iRed ); + mng_put_uint16 (pWork+2, iGreen); + mng_put_uint16 (pWork+4, iBlue ); +#else + mng_put_uint16 (pWork, pData->iBASIred ); + mng_put_uint16 (pWork+2, pData->iBASIgreen); + mng_put_uint16 (pWork+4, pData->iBASIblue ); +#endif + pWork += 6; + } + } + else +#endif + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iDatawidth * pData->iDataheight; + iX > 0;iX--) +#else + for (iX = 0; iX < pData->iDatawidth * pData->iDataheight; iX++) +#endif + { +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + *pWork = (mng_uint8)iRed; + *(pWork+1) = (mng_uint8)iGreen; + *(pWork+2) = (mng_uint8)iBlue; +#else + *pWork = (mng_uint8)pData->iBASIred; + *(pWork+1) = (mng_uint8)pData->iBASIgreen; + *(pWork+2) = (mng_uint8)pData->iBASIblue; +#endif + pWork += 3; + } + } + /* force tRNS ? */ +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + if ((bHasalpha) && (!iAlpha)) +#else + if ((pData->bBASIhasalpha) && (!pData->iBASIalpha)) +#endif + { + pBuf->bHasTRNS = MNG_TRUE; +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + pBuf->iTRNSred = iRed; + pBuf->iTRNSgreen = iGreen; + pBuf->iTRNSblue = iBlue; +#else + pBuf->iTRNSred = pData->iBASIred; + pBuf->iTRNSgreen = pData->iBASIgreen; + pBuf->iTRNSblue = pData->iBASIblue; +#endif + } + + break; + } + + case 3 : { /* indexed */ + pBuf->bHasPLTE = MNG_TRUE; + + switch (pData->iBitdepth) + { + case 1 : { pBuf->iPLTEcount = 2; break; } + case 2 : { pBuf->iPLTEcount = 4; break; } + case 4 : { pBuf->iPLTEcount = 16; break; } + case 8 : { pBuf->iPLTEcount = 256; break; } + default : { pBuf->iPLTEcount = 1; break; } + } + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + pBuf->aPLTEentries [0].iRed = (mng_uint8)iRed; + pBuf->aPLTEentries [0].iGreen = (mng_uint8)iGreen; + pBuf->aPLTEentries [0].iBlue = (mng_uint8)iBlue; +#else + pBuf->aPLTEentries [0].iRed = (mng_uint8)pData->iBASIred; + pBuf->aPLTEentries [0].iGreen = (mng_uint8)pData->iBASIgreen; + pBuf->aPLTEentries [0].iBlue = (mng_uint8)pData->iBASIblue; +#endif + + for (iX = 1; iX < pBuf->iPLTEcount; iX++) + { + pBuf->aPLTEentries [iX].iRed = 0; + pBuf->aPLTEentries [iX].iGreen = 0; + pBuf->aPLTEentries [iX].iBlue = 0; + } + /* force tRNS ? */ +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + if ((bHasalpha) && (iAlpha < 255)) +#else + if ((pData->bBASIhasalpha) && (pData->iBASIalpha < 255)) +#endif + { + pBuf->bHasTRNS = MNG_TRUE; + pBuf->iTRNScount = 1; +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + pBuf->aTRNSentries [0] = (mng_uint8)iAlpha; +#else + pBuf->aTRNSentries [0] = (mng_uint8)pData->iBASIalpha; +#endif + } + + break; + } + + case 4 : { /* gray+alpha */ +#ifndef MNG_NO_16BIT_SUPPORT + if (pData->iBitdepth == 16) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iDatawidth * pData->iDataheight; + iX > 0;iX--) +#else + for (iX = 0; iX < pData->iDatawidth * pData->iDataheight; iX++) +#endif + { +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + mng_put_uint16 (pWork, iRed); + mng_put_uint16 (pWork+2, iAlpha); +#else + mng_put_uint16 (pWork, pData->iBASIred); + mng_put_uint16 (pWork+2, pData->iBASIalpha); +#endif + pWork += 4; + } + } + else +#endif + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iDatawidth * pData->iDataheight; + iX > 0;iX--) +#else + for (iX = 0; iX < pData->iDatawidth * pData->iDataheight; iX++) +#endif + { +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + *pWork = (mng_uint8)iRed; + *(pWork+1) = (mng_uint8)iAlpha; +#else + *pWork = (mng_uint8)pData->iBASIred; + *(pWork+1) = (mng_uint8)pData->iBASIalpha; +#endif + pWork += 2; + } + } + + break; + } + + case 6 : { /* rgb+alpha */ +#ifndef MNG_NO_16BIT_SUPPORT + if (pData->iBitdepth == 16) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iDatawidth * pData->iDataheight; + iX > 0;iX--) +#else + for (iX = 0; iX < pData->iDatawidth * pData->iDataheight; iX++) +#endif + { +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + mng_put_uint16 (pWork, iRed); + mng_put_uint16 (pWork+2, iGreen); + mng_put_uint16 (pWork+4, iBlue); + mng_put_uint16 (pWork+6, iAlpha); +#else + mng_put_uint16 (pWork, pData->iBASIred); + mng_put_uint16 (pWork+2, pData->iBASIgreen); + mng_put_uint16 (pWork+4, pData->iBASIblue); + mng_put_uint16 (pWork+6, pData->iBASIalpha); +#endif + pWork += 8; + } + } + else +#endif + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iDatawidth * pData->iDataheight; + iX > 0;iX--) +#else + for (iX = 0; iX < pData->iDatawidth * pData->iDataheight; iX++) +#endif + { +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + *pWork = (mng_uint8)iRed; + *(pWork+1) = (mng_uint8)iGreen; + *(pWork+2) = (mng_uint8)iBlue; + *(pWork+3) = (mng_uint8)iAlpha; +#else + *pWork = (mng_uint8)pData->iBASIred; + *(pWork+1) = (mng_uint8)pData->iBASIgreen; + *(pWork+2) = (mng_uint8)pData->iBASIblue; + *(pWork+3) = (mng_uint8)pData->iBASIalpha; +#endif + pWork += 4; + } + } + + break; + } + + } + +#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT + pData->fInitrowproc = (mng_fptr)mng_init_rowproc; + pData->ePng_imgtype=mng_png_imgtype(pData->iColortype,pData->iBitdepth); +#else + switch (pData->iColortype) /* determine row initialization routine */ + { /* just to accomodate IDAT if it arrives */ + case 0 : { /* gray */ + switch (pData->iBitdepth) + { +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case 1 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_g1_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_g1_i; + + break; + } + case 2 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_g2_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_g2_i; + + break; + } + case 4 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_g4_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_g4_i; + + break; + } +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + case 8 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_g8_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_g8_i; + + break; + } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_g16_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_g16_i; + + break; + } +#endif + } + + break; + } + case 2 : { /* rgb */ + switch (pData->iBitdepth) + { + case 8 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_rgb8_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_rgb8_i; + + break; + } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_rgb16_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_rgb16_i; + + break; + } +#endif + } + + break; + } + case 3 : { /* indexed */ + switch (pData->iBitdepth) + { +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case 1 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_idx1_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_idx1_i; + + break; + } + case 2 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_idx2_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_idx2_i; + + break; + } + case 4 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_idx4_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_idx4_i; + + break; + } +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + case 8 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_idx8_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_idx8_i; + + break; + } + } + + break; + } + case 4 : { /* gray+alpha */ + switch (pData->iBitdepth) + { + case 8 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_ga8_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_ga8_i; + + break; + } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_ga16_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_ga16_i; + + break; + } +#endif + } + + break; + } + case 6 : { /* rgb+alpha */ + switch (pData->iBitdepth) + { + case 8 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_rgba8_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_rgba8_i; + + break; + } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_rgba16_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_rgba16_i; + + break; + } +#endif + } + + break; + } + } +#endif /* MNG_OPTIMIZE_FOOTPRINT_INIT */ + + pData->iFilterofs = 0; /* determine filter characteristics */ + pData->iLevel0 = 0; /* default levels */ + pData->iLevel1 = 0; + pData->iLevel2 = 0; + pData->iLevel3 = 0; + +#ifdef FILTER192 + if (pData->iFilter == 0xC0) /* leveling & differing ? */ + { + switch (pData->iColortype) + { + case 0 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (pData->iBitdepth <= 8) +#endif + pData->iFilterofs = 1; +#ifndef MNG_NO_16BIT_SUPPORT + else + pData->iFilterofs = 2; +#endif + + break; + } + case 2 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (pData->iBitdepth <= 8) +#endif + pData->iFilterofs = 3; +#ifndef MNG_NO_16BIT_SUPPORT + else + pData->iFilterofs = 6; +#endif + + break; + } + case 3 : { + pData->iFilterofs = 1; + break; + } + case 4 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (pData->iBitdepth <= 8) +#endif + pData->iFilterofs = 2; +#ifndef MNG_NO_16BIT_SUPPORT + else + pData->iFilterofs = 4; +#endif + + break; + } + case 6 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (pData->iBitdepth <= 8) +#endif + pData->iFilterofs = 4; +#ifndef MNG_NO_16BIT_SUPPORT + else + pData->iFilterofs = 8; +#endif + + break; + } + } + } +#endif + +#ifdef FILTER193 + if (pData->iFilter == 0xC1) /* no adaptive filtering ? */ + pData->iPixelofs = pData->iFilterofs; + else +#endif + pData->iPixelofs = pData->iFilterofs + 1; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_BASI, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_CLON +#ifndef MNG_OPTIMIZE_DISPLAYCALLS +mng_retcode mng_process_display_clon (mng_datap pData, + mng_uint16 iSourceid, + mng_uint16 iCloneid, + mng_uint8 iClonetype, + mng_bool bHasdonotshow, + mng_uint8 iDonotshow, + mng_uint8 iConcrete, + mng_bool bHasloca, + mng_uint8 iLocationtype, + mng_int32 iLocationx, + mng_int32 iLocationy) +#else +mng_retcode mng_process_display_clon (mng_datap pData) +#endif +{ + mng_imagep pSource, pClone; + mng_bool bVisible, bAbstract; + mng_retcode iRetcode = MNG_NOERROR; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_CLON, MNG_LC_START); +#endif +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + /* locate the source object first */ + pSource = mng_find_imageobject (pData, iSourceid); + /* check if the clone exists */ + pClone = mng_find_imageobject (pData, iCloneid); +#else + /* locate the source object first */ + pSource = mng_find_imageobject (pData, pData->iCLONsourceid); + /* check if the clone exists */ + pClone = mng_find_imageobject (pData, pData->iCLONcloneid); +#endif + + if (!pSource) /* source must exist ! */ + MNG_ERROR (pData, MNG_OBJECTUNKNOWN); + + if (pClone) /* clone must not exist ! */ + MNG_ERROR (pData, MNG_OBJECTEXISTS); + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + if (bHasdonotshow) /* DoNotShow flag filled ? */ + bVisible = (mng_bool)(iDonotshow == 0); + else + bVisible = pSource->bVisible; +#else + if (pData->bCLONhasdonotshow) /* DoNotShow flag filled ? */ + bVisible = (mng_bool)(pData->iCLONdonotshow == 0); + else + bVisible = pSource->bVisible; +#endif + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + bAbstract = (mng_bool)(iConcrete == 1); +#else + bAbstract = (mng_bool)(pData->iCLONconcrete == 1); +#endif + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + switch (iClonetype) /* determine action to take */ + { + case 0 : { /* full clone */ + iRetcode = mng_clone_imageobject (pData, iCloneid, MNG_FALSE, + bVisible, bAbstract, bHasloca, + iLocationtype, iLocationx, iLocationy, + pSource, &pClone); + break; + } + + case 1 : { /* partial clone */ + iRetcode = mng_clone_imageobject (pData, iCloneid, MNG_TRUE, + bVisible, bAbstract, bHasloca, + iLocationtype, iLocationx, iLocationy, + pSource, &pClone); + break; + } + + case 2 : { /* renumber object */ + iRetcode = mng_renum_imageobject (pData, pSource, iCloneid, + bVisible, bAbstract, bHasloca, + iLocationtype, iLocationx, iLocationy); + pClone = pSource; + break; + } + + } +#else + switch (pData->iCLONclonetype) /* determine action to take */ + { + case 0 : { /* full clone */ + iRetcode = mng_clone_imageobject (pData, pData->iCLONcloneid, MNG_FALSE, + bVisible, bAbstract, + pData->bCLONhasloca, pData->iCLONlocationtype, + pData->iCLONlocationx, pData->iCLONlocationy, + pSource, &pClone); + break; + } + + case 1 : { /* partial clone */ + iRetcode = mng_clone_imageobject (pData, pData->iCLONcloneid, MNG_TRUE, + bVisible, bAbstract, + pData->bCLONhasloca, pData->iCLONlocationtype, + pData->iCLONlocationx, pData->iCLONlocationy, + pSource, &pClone); + break; + } + + case 2 : { /* renumber object */ + iRetcode = mng_renum_imageobject (pData, pSource, pData->iCLONcloneid, + bVisible, bAbstract, + pData->bCLONhasloca, pData->iCLONlocationtype, + pData->iCLONlocationx, pData->iCLONlocationy); + pClone = pSource; + break; + } + + } +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + + /* display on the fly ? */ + if ((pClone->bViewable) && (pClone->bVisible)) + { + pData->pLastclone = pClone; /* remember in case of timer break ! */ + /* display it */ + mng_display_image (pData, pClone, MNG_FALSE); + + if (pData->bTimerset) /* timer break ? */ + pData->iBreakpoint = 5; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_CLON, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_process_display_clon2 (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_CLON, MNG_LC_START); +#endif + /* only called after timer break ! */ + mng_display_image (pData, (mng_imagep)pData->pLastclone, MNG_FALSE); + pData->iBreakpoint = 0; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_CLON, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_DISC +#ifndef MNG_OPTIMIZE_DISPLAYCALLS +mng_retcode mng_process_display_disc (mng_datap pData, + mng_uint32 iCount, + mng_uint16p pIds) +#else +mng_retcode mng_process_display_disc (mng_datap pData) +#endif +{ + mng_uint32 iX; + mng_imagep pImage; + mng_uint32 iRetcode; +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_DISC, MNG_LC_START); +#endif + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + if (iCount) /* specific list ? */ +#else + if (pData->iDISCcount) /* specific list ? */ +#endif + { +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + mng_uint16p pWork = pIds; +#else + mng_uint16p pWork = pData->pDISCids; +#endif + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS +#ifdef MNG_DECREMENT_LOOPS /* iterate the list */ + for (iX = iCount; iX > 0; iX--) +#else + for (iX = 0; iX < iCount; iX++) +#endif +#else +#ifdef MNG_DECREMENT_LOOPS /* iterate the list */ + for (iX = pData->iDISCcount; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iDISCcount; iX++) +#endif +#endif + { + pImage = mng_find_imageobject (pData, *pWork++); + + if (pImage) /* found the object ? */ + { /* then drop it */ + iRetcode = mng_free_imageobject (pData, pImage); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + } + } + else /* empty: drop all un-frozen objects */ + { + mng_imagep pNext = (mng_imagep)pData->pFirstimgobj; + + while (pNext) /* any left ? */ + { + pImage = pNext; + pNext = pImage->sHeader.pNext; + + if (!pImage->bFrozen) /* not frozen ? */ + { /* then drop it */ + iRetcode = mng_free_imageobject (pData, pImage); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_DISC, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_FRAM +#ifndef MNG_OPTIMIZE_DISPLAYCALLS +mng_retcode mng_process_display_fram (mng_datap pData, + mng_uint8 iFramemode, + mng_uint8 iChangedelay, + mng_uint32 iDelay, + mng_uint8 iChangetimeout, + mng_uint32 iTimeout, + mng_uint8 iChangeclipping, + mng_uint8 iCliptype, + mng_int32 iClipl, + mng_int32 iClipr, + mng_int32 iClipt, + mng_int32 iClipb) +#else +mng_retcode mng_process_display_fram (mng_datap pData) +#endif +{ + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_FRAM, MNG_LC_START); +#endif + /* advance a frame then */ +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + iRetcode = next_frame (pData, iFramemode, iChangedelay, iDelay, + iChangetimeout, iTimeout, iChangeclipping, + iCliptype, iClipl, iClipr, iClipt, iClipb); +#else + iRetcode = next_frame (pData, pData->iTempFramemode, pData->iTempChangedelay, + pData->iTempDelay, pData->iTempChangetimeout, + pData->iTempTimeout, pData->iTempChangeclipping, + pData->iTempCliptype, pData->iTempClipl, pData->iTempClipr, + pData->iTempClipt, pData->iTempClipb); +#endif + + if (pData->bTimerset) /* timer break ? */ + pData->iBreakpoint = 1; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_FRAM, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +mng_retcode mng_process_display_fram2 (mng_datap pData) +{ + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_FRAM, MNG_LC_START); +#endif + /* again; after the break */ + iRetcode = next_frame (pData, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + pData->iBreakpoint = 0; /* not again! */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_FRAM, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_MOVE +#ifndef MNG_OPTIMIZE_DISPLAYCALLS +mng_retcode mng_process_display_move (mng_datap pData, + mng_uint16 iFromid, + mng_uint16 iToid, + mng_uint8 iMovetype, + mng_int32 iMovex, + mng_int32 iMovey) +#else +mng_retcode mng_process_display_move (mng_datap pData) +#endif +{ + mng_uint16 iX; + mng_imagep pImage; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_MOVE, MNG_LC_START); +#endif + /* iterate the list */ +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + for (iX = iFromid; iX <= iToid; iX++) +#else + for (iX = pData->iMOVEfromid; iX <= pData->iMOVEtoid; iX++) +#endif + { + if (!iX) /* object id=0 ? */ + pImage = (mng_imagep)pData->pObjzero; + else + pImage = mng_find_imageobject (pData, iX); + + if (pImage) /* object exists ? */ + { +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + switch (iMovetype) +#else + switch (pData->iMOVEmovetype) +#endif + { + case 0 : { /* absolute */ +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + pImage->iPosx = iMovex; + pImage->iPosy = iMovey; +#else + pImage->iPosx = pData->iMOVEmovex; + pImage->iPosy = pData->iMOVEmovey; +#endif + break; + } + case 1 : { /* relative */ +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + pImage->iPosx = pImage->iPosx + iMovex; + pImage->iPosy = pImage->iPosy + iMovey; +#else + pImage->iPosx = pImage->iPosx + pData->iMOVEmovex; + pImage->iPosy = pImage->iPosy + pData->iMOVEmovey; +#endif + break; + } + } + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_MOVE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_CLIP +#ifndef MNG_OPTIMIZE_DISPLAYCALLS +mng_retcode mng_process_display_clip (mng_datap pData, + mng_uint16 iFromid, + mng_uint16 iToid, + mng_uint8 iCliptype, + mng_int32 iClipl, + mng_int32 iClipr, + mng_int32 iClipt, + mng_int32 iClipb) +#else +mng_retcode mng_process_display_clip (mng_datap pData) +#endif +{ + mng_uint16 iX; + mng_imagep pImage; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_CLIP, MNG_LC_START); +#endif + /* iterate the list */ +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + for (iX = iFromid; iX <= iToid; iX++) +#else + for (iX = pData->iCLIPfromid; iX <= pData->iCLIPtoid; iX++) +#endif + { + if (!iX) /* object id=0 ? */ + pImage = (mng_imagep)pData->pObjzero; + else + pImage = mng_find_imageobject (pData, iX); + + if (pImage) /* object exists ? */ + { +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + switch (iCliptype) +#else + switch (pData->iCLIPcliptype) +#endif + { + case 0 : { /* absolute */ + pImage->bClipped = MNG_TRUE; +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + pImage->iClipl = iClipl; + pImage->iClipr = iClipr; + pImage->iClipt = iClipt; + pImage->iClipb = iClipb; +#else + pImage->iClipl = pData->iCLIPclipl; + pImage->iClipr = pData->iCLIPclipr; + pImage->iClipt = pData->iCLIPclipt; + pImage->iClipb = pData->iCLIPclipb; +#endif + break; + } + case 1 : { /* relative */ + pImage->bClipped = MNG_TRUE; +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + pImage->iClipl = pImage->iClipl + iClipl; + pImage->iClipr = pImage->iClipr + iClipr; + pImage->iClipt = pImage->iClipt + iClipt; + pImage->iClipb = pImage->iClipb + iClipb; +#else + pImage->iClipl = pImage->iClipl + pData->iCLIPclipl; + pImage->iClipr = pImage->iClipr + pData->iCLIPclipr; + pImage->iClipt = pImage->iClipt + pData->iCLIPclipt; + pImage->iClipb = pImage->iClipb + pData->iCLIPclipb; +#endif + break; + } + } + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_CLIP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SHOW +mng_retcode mng_process_display_show (mng_datap pData) +{ + mng_int16 iX, iS, iFrom, iTo; + mng_imagep pImage; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_SHOW, MNG_LC_START); +#endif + + /* TODO: optimization for the cases where "abs (iTo - iFrom)" is rather high; + especially where ((iFrom==1) && (iTo==65535)); eg. an empty SHOW !!! */ + + if (pData->iBreakpoint == 3) /* previously broken during cycle-mode ? */ + { + pImage = mng_find_imageobject (pData, pData->iSHOWnextid); + + if (pImage) /* still there ? */ + mng_display_image (pData, pImage, MNG_FALSE); + + pData->iBreakpoint = 0; /* let's not go through this again! */ + } + else + { + if (pData->iBreakpoint) /* previously broken at other point ? */ + { /* restore last parms */ + iFrom = (mng_int16)pData->iSHOWfromid; + iTo = (mng_int16)pData->iSHOWtoid; + iX = (mng_int16)pData->iSHOWnextid; + iS = (mng_int16)pData->iSHOWskip; + } + else + { /* regular sequence ? */ + if (pData->iSHOWtoid >= pData->iSHOWfromid) + iS = 1; + else /* reverse sequence ! */ + iS = -1; + + iFrom = (mng_int16)pData->iSHOWfromid; + iTo = (mng_int16)pData->iSHOWtoid; + iX = iFrom; + + pData->iSHOWfromid = (mng_uint16)iFrom; + pData->iSHOWtoid = (mng_uint16)iTo; + pData->iSHOWskip = iS; + } + /* cycle mode ? */ + if ((pData->iSHOWmode == 6) || (pData->iSHOWmode == 7)) + { + mng_uint16 iTrigger = 0; + mng_uint16 iFound = 0; + mng_uint16 iPass = 0; + mng_imagep pFound = 0; + + do + { + iPass++; /* lets prevent endless loops when there + are no potential candidates in the list! */ + + if (iS > 0) /* forward ? */ + { + for (iX = iFrom; iX <= iTo; iX += iS) + { + pImage = mng_find_imageobject (pData, (mng_uint16)iX); + + if (pImage) /* object exists ? */ + { + if (iFound) /* already found a candidate ? */ + pImage->bVisible = MNG_FALSE; + else + if (iTrigger) /* found the trigger ? */ + { + pImage->bVisible = MNG_TRUE; + iFound = iX; + pFound = pImage; + } + else + if (pImage->bVisible) /* ok, this is the trigger */ + { + pImage->bVisible = MNG_FALSE; + iTrigger = iX; + } + } + } + } + else + { + for (iX = iFrom; iX >= iTo; iX += iS) + { + pImage = mng_find_imageobject (pData, (mng_uint16)iX); + + if (pImage) /* object exists ? */ + { + if (iFound) /* already found a candidate ? */ + pImage->bVisible = MNG_FALSE; + else + if (iTrigger) /* found the trigger ? */ + { + pImage->bVisible = MNG_TRUE; + iFound = iX; + pFound = pImage; + } + else + if (pImage->bVisible) /* ok, this is the trigger */ + { + pImage->bVisible = MNG_FALSE; + iTrigger = iX; + } + } + } + } + + if (!iTrigger) /* did not find a trigger ? */ + iTrigger = 1; /* then fake it so the first image + gets nominated */ + } /* cycle back to beginning ? */ + while ((iPass < 2) && (iTrigger) && (!iFound)); + + pData->iBreakpoint = 0; /* just a sanity precaution */ + /* display it ? */ + if ((pData->iSHOWmode == 6) && (pFound)) + { + mng_display_image (pData, pFound, MNG_FALSE); + + if (pData->bTimerset) /* timer set ? */ + { + pData->iBreakpoint = 3; + pData->iSHOWnextid = iFound; /* save it for after the break */ + } + } + } + else + { + do + { + pImage = mng_find_imageobject (pData, iX); + + if (pImage) /* object exists ? */ + { + if (pData->iBreakpoint) /* did we get broken last time ? */ + { /* could only happen in the display routine */ + mng_display_image (pData, pImage, MNG_FALSE); + pData->iBreakpoint = 0; /* only once inside this loop please ! */ + } + else + { + switch (pData->iSHOWmode) /* do what ? */ + { + case 0 : { + pImage->bVisible = MNG_TRUE; + mng_display_image (pData, pImage, MNG_FALSE); + break; + } + case 1 : { + pImage->bVisible = MNG_FALSE; + break; + } + case 2 : { + if (pImage->bVisible) + mng_display_image (pData, pImage, MNG_FALSE); + break; + } + case 3 : { + pImage->bVisible = MNG_TRUE; + break; + } + case 4 : { + pImage->bVisible = (mng_bool)(!pImage->bVisible); + if (pImage->bVisible) + mng_display_image (pData, pImage, MNG_FALSE); + break; + } + case 5 : { + pImage->bVisible = (mng_bool)(!pImage->bVisible); + } + } + } + } + + if (!pData->bTimerset) /* next ? */ + iX += iS; + + } /* continue ? */ + while ((!pData->bTimerset) && (((iS > 0) && (iX <= iTo)) || + ((iS < 0) && (iX >= iTo)) )); + + if (pData->bTimerset) /* timer set ? */ + { + pData->iBreakpoint = 4; + pData->iSHOWnextid = iX; /* save for next time */ + } + else + pData->iBreakpoint = 0; + + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_SHOW, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SAVE +mng_retcode mng_process_display_save (mng_datap pData) +{ + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_SAVE, MNG_LC_START); +#endif + + iRetcode = save_state (pData); /* save the current state */ + + if (iRetcode) /* on error bail out */ + return iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_SAVE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SEEK +mng_retcode mng_process_display_seek (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_SEEK, MNG_LC_START); +#endif + +#ifdef MNG_SUPPORT_DYNAMICMNG + if (pData->bStopafterseek) /* need to stop after this SEEK ? */ + { + pData->bFreezing = MNG_TRUE; /* stop processing on this one */ + pData->bRunningevent = MNG_FALSE; + pData->bStopafterseek = MNG_FALSE; + pData->bNeedrefresh = MNG_TRUE; /* make sure the last bit is displayed ! */ + } + else +#endif + { /* restore the initial or SAVE state */ + mng_retcode iRetcode = restore_state (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + +#ifdef MNG_SUPPORT_DYNAMICMNG + /* stop after next SEEK ? */ + if ((pData->bDynamic) || (pData->bRunningevent)) + pData->bStopafterseek = MNG_TRUE; +#endif + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_SEEK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +mng_retcode mng_process_display_jhdr (mng_datap pData) +{ /* address the current "object" if any */ + mng_imagep pImage = (mng_imagep)pData->pCurrentobj; + mng_retcode iRetcode = MNG_NOERROR; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_JHDR, MNG_LC_START); +#endif + + if (!pData->bHasDHDR) + { + pData->fInitrowproc = MNG_NULL; /* do nothing by default */ + pData->fDisplayrow = MNG_NULL; + pData->fCorrectrow = MNG_NULL; + pData->fStorerow = MNG_NULL; + pData->fProcessrow = MNG_NULL; + pData->fDifferrow = MNG_NULL; + pData->fStorerow2 = MNG_NULL; + pData->fStorerow3 = MNG_NULL; + + pData->pStoreobj = MNG_NULL; /* initialize important work-parms */ + + pData->iJPEGrow = 0; + pData->iJPEGalpharow = 0; + pData->iJPEGrgbrow = 0; + pData->iRowmax = 0; /* so init_rowproc does the right thing ! */ + } + + if (!pData->iBreakpoint) /* not previously broken ? */ + { +#ifndef MNG_NO_DELTA_PNG + if (pData->bHasDHDR) /* delta-image ? */ + { + if (pData->iDeltatype == MNG_DELTATYPE_REPLACE) + { + iRetcode = mng_reset_object_details (pData, (mng_imagep)pData->pDeltaImage, + pData->iDatawidth, pData->iDataheight, + pData->iJHDRimgbitdepth, pData->iJHDRcolortype, + pData->iJHDRalphacompression, pData->iJHDRalphafilter, + pData->iJHDRalphainterlace, MNG_TRUE); + + ((mng_imagep)pData->pDeltaImage)->pImgbuf->iAlphabitdepth = pData->iJHDRalphabitdepth; + ((mng_imagep)pData->pDeltaImage)->pImgbuf->iJHDRcompression = pData->iJHDRimgcompression; + ((mng_imagep)pData->pDeltaImage)->pImgbuf->iJHDRinterlace = pData->iJHDRimginterlace; + ((mng_imagep)pData->pDeltaImage)->pImgbuf->iAlphasampledepth = pData->iJHDRalphabitdepth; + } + else + if ((pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) ) + { + ((mng_imagep)pData->pDeltaImage)->pImgbuf->iPixelsampledepth = pData->iJHDRimgbitdepth; + ((mng_imagep)pData->pDeltaImage)->pImgbuf->iAlphasampledepth = pData->iJHDRalphabitdepth; + } + else + if ((pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAADD ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAREPLACE) ) + ((mng_imagep)pData->pDeltaImage)->pImgbuf->iAlphasampledepth = pData->iJHDRalphabitdepth; + else + if ((pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORADD ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORREPLACE) ) + ((mng_imagep)pData->pDeltaImage)->pImgbuf->iPixelsampledepth = pData->iJHDRimgbitdepth; + + } + else +#endif /* MNG_NO_DELTA_PNG */ + { + if (pImage) /* update object buffer ? */ + { + iRetcode = mng_reset_object_details (pData, pImage, + pData->iDatawidth, pData->iDataheight, + pData->iJHDRimgbitdepth, pData->iJHDRcolortype, + pData->iJHDRalphacompression, pData->iJHDRalphafilter, + pData->iJHDRalphainterlace, MNG_TRUE); + + pImage->pImgbuf->iAlphabitdepth = pData->iJHDRalphabitdepth; + pImage->pImgbuf->iJHDRcompression = pData->iJHDRimgcompression; + pImage->pImgbuf->iJHDRinterlace = pData->iJHDRimginterlace; + pImage->pImgbuf->iAlphasampledepth = pData->iJHDRalphabitdepth; + } + else /* update object 0 */ + { + iRetcode = mng_reset_object_details (pData, (mng_imagep)pData->pObjzero, + pData->iDatawidth, pData->iDataheight, + pData->iJHDRimgbitdepth, pData->iJHDRcolortype, + pData->iJHDRalphacompression, pData->iJHDRalphafilter, + pData->iJHDRalphainterlace, MNG_TRUE); + + ((mng_imagep)pData->pObjzero)->pImgbuf->iAlphabitdepth = pData->iJHDRalphabitdepth; + ((mng_imagep)pData->pObjzero)->pImgbuf->iJHDRcompression = pData->iJHDRimgcompression; + ((mng_imagep)pData->pObjzero)->pImgbuf->iJHDRinterlace = pData->iJHDRimginterlace; + ((mng_imagep)pData->pObjzero)->pImgbuf->iAlphasampledepth = pData->iJHDRalphabitdepth; + } + } + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + + if (!pData->bHasDHDR) + { /* we're always storing a JPEG */ + if (pImage) /* real object ? */ + pData->pStoreobj = pImage; /* tell the row routines */ + else /* otherwise use object 0 */ + pData->pStoreobj = pData->pObjzero; + /* display "on-the-fly" ? */ + if ( +#ifndef MNG_SKIPCHUNK_MAGN + ( ((mng_imagep)pData->pStoreobj)->iMAGN_MethodX == 0) && + ( ((mng_imagep)pData->pStoreobj)->iMAGN_MethodY == 0) && +#endif + ( (pData->eImagetype == mng_it_jng ) || + (((mng_imagep)pData->pStoreobj)->bVisible) ) ) + { + next_layer (pData); /* that's a new layer then ! */ + + pData->iBreakpoint = 0; + + if (pData->bTimerset) /* timer break ? */ + pData->iBreakpoint = 7; + else + if (pData->bRunning) /* still running ? */ + { /* anything to display ? */ + if ((pData->iDestr > pData->iDestl) && (pData->iDestb > pData->iDestt)) + { + set_display_routine (pData); /* then determine display routine */ + /* display from the object we store in */ + pData->pRetrieveobj = pData->pStoreobj; + } + } + } + } + + if (!pData->bTimerset) /* no timer break ? */ + { /* default row initialization ! */ +#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT + pData->ePng_imgtype=png_none; +#endif + pData->fInitrowproc = (mng_fptr)mng_init_rowproc; + + if ((!pData->bHasDHDR) || (pData->iDeltatype == MNG_DELTATYPE_REPLACE)) + { /* 8-bit JPEG ? */ + if (pData->iJHDRimgbitdepth == 8) + { /* intermediate row is 8-bit deep */ + pData->bIsRGBA16 = MNG_FALSE; + pData->iRowsamples = pData->iDatawidth; + + switch (pData->iJHDRcolortype) /* determine pixel processing routines */ + { + case MNG_COLORTYPE_JPEGGRAY : + { + pData->fStorerow2 = (mng_fptr)mng_store_jpeg_g8; + pData->fRetrieverow = (mng_fptr)mng_retrieve_g8; + pData->bIsOpaque = MNG_TRUE; + break; + } + case MNG_COLORTYPE_JPEGCOLOR : + { + pData->fStorerow2 = (mng_fptr)mng_store_jpeg_rgb8; + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgb8; + pData->bIsOpaque = MNG_TRUE; + break; + } + case MNG_COLORTYPE_JPEGGRAYA : + { + pData->fStorerow2 = (mng_fptr)mng_store_jpeg_ga8; + pData->fRetrieverow = (mng_fptr)mng_retrieve_ga8; + pData->bIsOpaque = MNG_FALSE; + break; + } + case MNG_COLORTYPE_JPEGCOLORA : + { + pData->fStorerow2 = (mng_fptr)mng_store_jpeg_rgba8; + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba8; + pData->bIsOpaque = MNG_FALSE; + break; + } + } + } +#ifndef MNG_NO_16BIT_SUPPORT + else + { + pData->bIsRGBA16 = MNG_TRUE; /* intermediate row is 16-bit deep */ + + /* TODO: 12-bit JPEG */ + /* TODO: 8- + 12-bit JPEG (eg. type=20) */ + + } +#endif + /* possible IDAT alpha-channel ? */ + if (pData->iJHDRalphacompression == MNG_COMPRESSION_DEFLATE) + { + /* determine alpha processing routine */ +#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT + pData->fInitrowproc = (mng_fptr)mng_init_rowproc; +#endif + switch (pData->iJHDRalphabitdepth) + { +#ifndef MNG_OPTIMIZE_FOOTPRINT_INIT +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case 1 : { pData->fInitrowproc = (mng_fptr)mng_init_jpeg_a1_ni; break; } + case 2 : { pData->fInitrowproc = (mng_fptr)mng_init_jpeg_a2_ni; break; } + case 4 : { pData->fInitrowproc = (mng_fptr)mng_init_jpeg_a4_ni; break; } +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + case 8 : { pData->fInitrowproc = (mng_fptr)mng_init_jpeg_a8_ni; break; } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { pData->fInitrowproc = (mng_fptr)mng_init_jpeg_a16_ni; break; } +#endif +#else +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case 1 : { pData->ePng_imgtype = png_jpeg_a1; break; } + case 2 : { pData->ePng_imgtype = png_jpeg_a2; break; } + case 4 : { pData->ePng_imgtype = png_jpeg_a4; break; } +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + case 8 : { pData->ePng_imgtype = png_jpeg_a8; break; } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { pData->ePng_imgtype = png_jpeg_a16; break; } +#endif +#endif + } + } + else /* possible JDAA alpha-channel ? */ + if (pData->iJHDRalphacompression == MNG_COMPRESSION_BASELINEJPEG) + { /* 8-bit JPEG ? */ + if (pData->iJHDRimgbitdepth == 8) + { + if (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGGRAYA) + pData->fStorerow3 = (mng_fptr)mng_store_jpeg_g8_alpha; + else + if (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGCOLORA) + pData->fStorerow3 = (mng_fptr)mng_store_jpeg_rgb8_alpha; + } + else + { + /* TODO: 12-bit JPEG with 8-bit JDAA */ + } + } + /* initialize JPEG library */ + iRetcode = mngjpeg_initialize (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + else + { /* must be alpha add/replace !! */ + if ((pData->iDeltatype != MNG_DELTATYPE_BLOCKALPHAADD ) && + (pData->iDeltatype != MNG_DELTATYPE_BLOCKALPHAREPLACE) ) + MNG_ERROR (pData, MNG_INVDELTATYPE); + /* determine alpha processing routine */ +#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT + pData->fInitrowproc = (mng_fptr)mng_init_rowproc; +#endif + switch (pData->iJHDRalphabitdepth) + { +#ifndef MNG_OPTIMIZE_FOOTPRINT_INIT +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case 1 : { pData->fInitrowproc = (mng_fptr)mng_init_g1_ni; break; } + case 2 : { pData->fInitrowproc = (mng_fptr)mng_init_g2_ni; break; } + case 4 : { pData->fInitrowproc = (mng_fptr)mng_init_g4_ni; break; } +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + case 8 : { pData->fInitrowproc = (mng_fptr)mng_init_g8_ni; break; } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { pData->fInitrowproc = (mng_fptr)mng_init_g16_ni; break; } +#endif +#else +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case 1 : { pData->ePng_imgtype = png_jpeg_a1; break; } + case 2 : { pData->ePng_imgtype = png_jpeg_a2; break; } + case 4 : { pData->ePng_imgtype = png_jpeg_a4; break; } +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + case 8 : { pData->ePng_imgtype = png_jpeg_a8; break; } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { pData->ePng_imgtype = png_jpeg_a16; break; } +#endif +#endif /* MNG_OPTIMIZE_FOOTPRINT_INIT */ + } + } + + pData->iFilterofs = 0; /* determine filter characteristics */ + pData->iLevel0 = 0; /* default levels */ + pData->iLevel1 = 0; + pData->iLevel2 = 0; + pData->iLevel3 = 0; + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iJHDRalphafilter == 0xC0) + { + if (pData->iJHDRalphabitdepth <= 8) + pData->iFilterofs = 1; + else + pData->iFilterofs = 2; + + } +#endif +#ifdef FILTER193 /* no adaptive filtering ? */ + if (pData->iJHDRalphafilter == 0xC1) + pData->iPixelofs = pData->iFilterofs; + else +#endif + pData->iPixelofs = pData->iFilterofs + 1; + + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_JHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +#ifndef MNG_OPTIMIZE_DISPLAYCALLS +mng_retcode mng_process_display_jdaa (mng_datap pData, + mng_uint32 iRawlen, + mng_uint8p pRawdata) +#else +mng_retcode mng_process_display_jdaa (mng_datap pData) +#endif +{ + mng_retcode iRetcode = MNG_NOERROR; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_JDAA, MNG_LC_START); +#endif + + if (!pData->bJPEGdecompress2) /* if we're not decompressing already */ + { + if (pData->fInitrowproc) /* initialize row-processing? */ + { + iRetcode = ((mng_initrowproc)pData->fInitrowproc) (pData); + pData->fInitrowproc = MNG_NULL; /* only call this once !!! */ + } + + if (!iRetcode) /* initialize decompress */ + iRetcode = mngjpeg_decompressinit2 (pData); + } + + if (!iRetcode) /* all ok? then decompress, my man */ +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + iRetcode = mngjpeg_decompressdata2 (pData, iRawlen, pRawdata); +#else + iRetcode = mngjpeg_decompressdata2 (pData, pData->iRawlen, pData->pRawdata); +#endif + + if (iRetcode) + return iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_JDAA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +#ifndef MNG_OPTIMIZE_DISPLAYCALLS +mng_retcode mng_process_display_jdat (mng_datap pData, + mng_uint32 iRawlen, + mng_uint8p pRawdata) +#else +mng_retcode mng_process_display_jdat (mng_datap pData) +#endif +{ + mng_retcode iRetcode = MNG_NOERROR; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_JDAT, MNG_LC_START); +#endif + + if (pData->bRestorebkgd) /* need to restore the background ? */ + { + pData->bRestorebkgd = MNG_FALSE; + iRetcode = load_bkgdlayer (pData); + + pData->iLayerseq++; /* and it counts as a layer then ! */ + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + + if (!pData->bJPEGdecompress) /* if we're not decompressing already */ + { + if (pData->fInitrowproc) /* initialize row-processing? */ + { + iRetcode = ((mng_initrowproc)pData->fInitrowproc) (pData); + pData->fInitrowproc = MNG_NULL; /* only call this once !!! */ + } + + if (!iRetcode) /* initialize decompress */ + iRetcode = mngjpeg_decompressinit (pData); + } + + if (!iRetcode) /* all ok? then decompress, my man */ +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + iRetcode = mngjpeg_decompressdata (pData, iRawlen, pRawdata); +#else + iRetcode = mngjpeg_decompressdata (pData, pData->iRawlen, pData->pRawdata); +#endif + + if (iRetcode) + return iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_JDAT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_OPTIMIZE_DISPLAYCALLS +mng_retcode mng_process_display_dhdr (mng_datap pData, + mng_uint16 iObjectid, + mng_uint8 iImagetype, + mng_uint8 iDeltatype, + mng_uint32 iBlockwidth, + mng_uint32 iBlockheight, + mng_uint32 iBlockx, + mng_uint32 iBlocky) +#else +mng_retcode mng_process_display_dhdr (mng_datap pData) +#endif +{ + mng_imagep pImage; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_DHDR, MNG_LC_START); +#endif + + pData->fInitrowproc = MNG_NULL; /* do nothing by default */ + pData->fDisplayrow = MNG_NULL; + pData->fCorrectrow = MNG_NULL; + pData->fStorerow = MNG_NULL; + pData->fProcessrow = MNG_NULL; + pData->pStoreobj = MNG_NULL; + + pData->fDeltagetrow = MNG_NULL; + pData->fDeltaaddrow = MNG_NULL; + pData->fDeltareplacerow = MNG_NULL; + pData->fDeltaputrow = MNG_NULL; + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + pImage = mng_find_imageobject (pData, iObjectid); +#else + pImage = mng_find_imageobject (pData, pData->iDHDRobjectid); +#endif + + if (pImage) /* object exists ? */ + { + if (pImage->pImgbuf->bConcrete) /* is it concrete ? */ + { /* previous magnification to be done ? */ +#ifndef MNG_SKIPCHUNK_MAGN + if ((pImage->iMAGN_MethodX) || (pImage->iMAGN_MethodY)) + { + iRetcode = mng_magnify_imageobject (pData, pImage); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } +#endif + /* save delta fields */ + pData->pDeltaImage = (mng_ptr)pImage; +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + pData->iDeltaImagetype = iImagetype; + pData->iDeltatype = iDeltatype; + pData->iDeltaBlockwidth = iBlockwidth; + pData->iDeltaBlockheight = iBlockheight; + pData->iDeltaBlockx = iBlockx; + pData->iDeltaBlocky = iBlocky; +#else + pData->iDeltaImagetype = pData->iDHDRimagetype; + pData->iDeltatype = pData->iDHDRdeltatype; + pData->iDeltaBlockwidth = pData->iDHDRblockwidth; + pData->iDeltaBlockheight = pData->iDHDRblockheight; + pData->iDeltaBlockx = pData->iDHDRblockx; + pData->iDeltaBlocky = pData->iDHDRblocky; +#endif + /* restore target-object fields */ + pData->iDatawidth = pImage->pImgbuf->iWidth; + pData->iDataheight = pImage->pImgbuf->iHeight; + pData->iBitdepth = pImage->pImgbuf->iBitdepth; + pData->iColortype = pImage->pImgbuf->iColortype; + pData->iCompression = pImage->pImgbuf->iCompression; + pData->iFilter = pImage->pImgbuf->iFilter; + pData->iInterlace = pImage->pImgbuf->iInterlace; + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + if ((iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD ) || + (iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) ) + pData->iBitdepth = pImage->pImgbuf->iPixelsampledepth; + else + if ((iDeltatype == MNG_DELTATYPE_BLOCKALPHAADD ) || + (iDeltatype == MNG_DELTATYPE_BLOCKALPHAREPLACE) ) + pData->iBitdepth = pImage->pImgbuf->iAlphasampledepth; + else + if ((iDeltatype == MNG_DELTATYPE_BLOCKCOLORADD ) || + (iDeltatype == MNG_DELTATYPE_BLOCKCOLORREPLACE) ) + pData->iBitdepth = pImage->pImgbuf->iPixelsampledepth; +#else + if ((pData->iDHDRdeltatype == MNG_DELTATYPE_BLOCKPIXELADD ) || + (pData->iDHDRdeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) ) + pData->iBitdepth = pImage->pImgbuf->iPixelsampledepth; + else + if ((pData->iDHDRdeltatype == MNG_DELTATYPE_BLOCKALPHAADD ) || + (pData->iDHDRdeltatype == MNG_DELTATYPE_BLOCKALPHAREPLACE) ) + pData->iBitdepth = pImage->pImgbuf->iAlphasampledepth; + else + if ((pData->iDHDRdeltatype == MNG_DELTATYPE_BLOCKCOLORADD ) || + (pData->iDHDRdeltatype == MNG_DELTATYPE_BLOCKCOLORREPLACE) ) + pData->iBitdepth = pImage->pImgbuf->iPixelsampledepth; +#endif + +#ifdef MNG_INCLUDE_JNG + pData->iJHDRimgbitdepth = pImage->pImgbuf->iBitdepth; + pData->iJHDRcolortype = pImage->pImgbuf->iColortype; + pData->iJHDRimgcompression = pImage->pImgbuf->iJHDRcompression; + pData->iJHDRimginterlace = pImage->pImgbuf->iJHDRinterlace; + pData->iJHDRalphacompression = pImage->pImgbuf->iCompression; + pData->iJHDRalphafilter = pImage->pImgbuf->iFilter; + pData->iJHDRalphainterlace = pImage->pImgbuf->iInterlace; + pData->iJHDRalphabitdepth = pImage->pImgbuf->iAlphabitdepth; +#endif + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + /* block size specified ? */ + if (iDeltatype != MNG_DELTATYPE_NOCHANGE) + { /* block entirely within target ? */ + if (iDeltatype != MNG_DELTATYPE_REPLACE) + { + if (((iBlockx + iBlockwidth ) > pData->iDatawidth ) || + ((iBlocky + iBlockheight) > pData->iDataheight) ) + MNG_ERROR (pData, MNG_INVALIDBLOCK); + } + + pData->iDatawidth = iBlockwidth; + pData->iDataheight = iBlockheight; + } +#else + /* block size specified ? */ + if (pData->iDHDRdeltatype != MNG_DELTATYPE_NOCHANGE) + { /* block entirely within target ? */ + if (pData->iDHDRdeltatype != MNG_DELTATYPE_REPLACE) + { + if (((pData->iDHDRblockx + pData->iDHDRblockwidth ) > pData->iDatawidth ) || + ((pData->iDHDRblocky + pData->iDHDRblockheight) > pData->iDataheight) ) + MNG_ERROR (pData, MNG_INVALIDBLOCK); + } + + pData->iDatawidth = pData->iDHDRblockwidth; + pData->iDataheight = pData->iDHDRblockheight; + } +#endif + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + switch (iDeltatype) /* determine nr of delta-channels */ +#else + switch (pData->iDHDRdeltatype) /* determine nr of delta-channels */ +#endif + { + case MNG_DELTATYPE_BLOCKALPHAADD : ; + case MNG_DELTATYPE_BLOCKALPHAREPLACE : + { +#ifdef MNG_INCLUDE_JNG + if ((pData->iColortype == MNG_COLORTYPE_GRAYA ) || + (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGGRAYA) ) + { + pData->iColortype = MNG_COLORTYPE_GRAY; + pData->iJHDRcolortype = MNG_COLORTYPE_JPEGGRAY; + } + else + if ((pData->iColortype == MNG_COLORTYPE_RGBA ) || + (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGCOLORA) ) + { + pData->iColortype = MNG_COLORTYPE_GRAY; + pData->iJHDRcolortype = MNG_COLORTYPE_JPEGGRAY; + } +#else + if (pData->iColortype == MNG_COLORTYPE_GRAYA) + pData->iColortype = MNG_COLORTYPE_GRAY; + else + if (pData->iColortype == MNG_COLORTYPE_RGBA) + pData->iColortype = MNG_COLORTYPE_GRAY; +#endif + else /* target has no alpha; that sucks! */ + MNG_ERROR (pData, MNG_TARGETNOALPHA); + + break; + } + + case MNG_DELTATYPE_BLOCKCOLORADD : ; + case MNG_DELTATYPE_BLOCKCOLORREPLACE : + { +#ifdef MNG_INCLUDE_JNG + if ((pData->iColortype == MNG_COLORTYPE_GRAYA ) || + (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGGRAYA) ) + { + pData->iColortype = MNG_COLORTYPE_GRAY; + pData->iJHDRcolortype = MNG_COLORTYPE_JPEGGRAY; + } + else + if ((pData->iColortype == MNG_COLORTYPE_RGBA ) || + (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGCOLORA) ) + { + pData->iColortype = MNG_COLORTYPE_RGB; + pData->iJHDRcolortype = MNG_COLORTYPE_JPEGCOLOR; + } +#else + if (pData->iColortype == MNG_COLORTYPE_GRAYA) + pData->iColortype = MNG_COLORTYPE_GRAY; + else + if (pData->iColortype == MNG_COLORTYPE_RGBA) + pData->iColortype = MNG_COLORTYPE_RGB; +#endif + else /* target has no alpha; that sucks! */ + MNG_ERROR (pData, MNG_TARGETNOALPHA); + + break; + } + + } + /* full image replace ? */ +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + if (iDeltatype == MNG_DELTATYPE_REPLACE) +#else + if (pData->iDHDRdeltatype == MNG_DELTATYPE_REPLACE) +#endif + { + iRetcode = mng_reset_object_details (pData, pImage, + pData->iDatawidth, pData->iDataheight, + pData->iBitdepth, pData->iColortype, + pData->iCompression, pData->iFilter, + pData->iInterlace, MNG_FALSE); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + pData->pStoreobj = pImage; /* and store straight into this object */ + } + else + { + mng_imagedatap pBufzero, pBuf; + /* we store in object 0 and process it later */ + pData->pStoreobj = pData->pObjzero; + /* make sure to initialize object 0 then */ + iRetcode = mng_reset_object_details (pData, (mng_imagep)pData->pObjzero, + pData->iDatawidth, pData->iDataheight, + pData->iBitdepth, pData->iColortype, + pData->iCompression, pData->iFilter, + pData->iInterlace, MNG_TRUE); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + pBuf = pImage->pImgbuf; /* copy possible palette & cheap transparency */ + pBufzero = ((mng_imagep)pData->pObjzero)->pImgbuf; + + pBufzero->bHasPLTE = pBuf->bHasPLTE; + pBufzero->bHasTRNS = pBuf->bHasTRNS; + + if (pBufzero->bHasPLTE) /* copy palette ? */ + { + mng_uint32 iX; + + pBufzero->iPLTEcount = pBuf->iPLTEcount; + + for (iX = 0; iX < pBuf->iPLTEcount; iX++) + { + pBufzero->aPLTEentries [iX].iRed = pBuf->aPLTEentries [iX].iRed; + pBufzero->aPLTEentries [iX].iGreen = pBuf->aPLTEentries [iX].iGreen; + pBufzero->aPLTEentries [iX].iBlue = pBuf->aPLTEentries [iX].iBlue; + } + } + + if (pBufzero->bHasTRNS) /* copy cheap transparency ? */ + { + pBufzero->iTRNSgray = pBuf->iTRNSgray; + pBufzero->iTRNSred = pBuf->iTRNSred; + pBufzero->iTRNSgreen = pBuf->iTRNSgreen; + pBufzero->iTRNSblue = pBuf->iTRNSblue; + pBufzero->iTRNScount = pBuf->iTRNScount; + + MNG_COPY (pBufzero->aTRNSentries, pBuf->aTRNSentries, + sizeof (pBufzero->aTRNSentries)); + } + /* process immediately if bitdepth & colortype are equal */ + pData->bDeltaimmediate = + (mng_bool)((pData->bDisplaying) && (!pData->bSkipping) && + ((pData->bRunning) || (pData->bSearching)) && + (pData->iBitdepth == ((mng_imagep)pData->pDeltaImage)->pImgbuf->iBitdepth ) && + (pData->iColortype == ((mng_imagep)pData->pDeltaImage)->pImgbuf->iColortype) ); + } + +#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT + pData->fInitrowproc = (mng_fptr)mng_init_rowproc; + pData->ePng_imgtype = mng_png_imgtype (pData->iColortype, pData->iBitdepth); +#else + switch (pData->iColortype) /* determine row initialization routine */ + { + case 0 : { /* gray */ + switch (pData->iBitdepth) + { +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case 1 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_g1_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_g1_i; + + break; + } + case 2 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_g2_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_g2_i; + + break; + } + case 4 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_g4_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_g4_i; + + break; + } +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + case 8 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_g8_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_g8_i; + + break; + } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_g16_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_g16_i; + + break; + } +#endif + } + + break; + } + case 2 : { /* rgb */ + switch (pData->iBitdepth) + { + case 8 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_rgb8_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_rgb8_i; + + break; + } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_rgb16_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_rgb16_i; + + break; + } +#endif + } + + break; + } + case 3 : { /* indexed */ + switch (pData->iBitdepth) + { +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case 1 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_idx1_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_idx1_i; + + break; + } + case 2 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_idx2_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_idx2_i; + + break; + } + case 4 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_idx4_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_idx4_i; + + break; + } +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + case 8 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_idx8_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_idx8_i; + + break; + } + } + + break; + } + case 4 : { /* gray+alpha */ + switch (pData->iBitdepth) + { + case 8 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_ga8_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_ga8_i; + + break; + } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_ga16_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_ga16_i; + + break; + } +#endif + } + + break; + } + case 6 : { /* rgb+alpha */ + switch (pData->iBitdepth) + { + case 8 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_rgba8_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_rgba8_i; + + break; + } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { + if (!pData->iInterlace) + pData->fInitrowproc = (mng_fptr)mng_init_rgba16_ni; + else + pData->fInitrowproc = (mng_fptr)mng_init_rgba16_i; + + break; + } +#endif + } + + break; + } + } +#endif /* MNG_OPTIMIZE_FOOTPRINT_INIT */ + } + else + MNG_ERROR (pData, MNG_OBJNOTCONCRETE); + + } + else + MNG_ERROR (pData, MNG_OBJECTUNKNOWN); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_DHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_OPTIMIZE_DISPLAYCALLS +mng_retcode mng_process_display_prom (mng_datap pData, + mng_uint8 iBitdepth, + mng_uint8 iColortype, + mng_uint8 iFilltype) +#else +mng_retcode mng_process_display_prom (mng_datap pData) +#endif +{ + mng_imagep pImage; + mng_imagedatap pBuf; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_PROM, MNG_LC_START); +#endif + + if (!pData->pDeltaImage) /* gotta have this now! */ + MNG_ERROR (pData, MNG_INVALIDDELTA); + + pImage = (mng_imagep)pData->pDeltaImage; + pBuf = pImage->pImgbuf; + /* can't demote bitdepth! */ +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + if (iBitdepth < pBuf->iBitdepth) + MNG_ERROR (pData, MNG_INVALIDBITDEPTH); + + if ( ((pBuf->iColortype == MNG_COLORTYPE_GRAY ) && + (iColortype != MNG_COLORTYPE_GRAY ) && + (iColortype != MNG_COLORTYPE_GRAYA ) && + (iColortype != MNG_COLORTYPE_RGB ) && + (iColortype != MNG_COLORTYPE_RGBA ) ) || + ((pBuf->iColortype == MNG_COLORTYPE_GRAYA ) && + (iColortype != MNG_COLORTYPE_GRAYA ) && + (iColortype != MNG_COLORTYPE_RGBA ) ) || + ((pBuf->iColortype == MNG_COLORTYPE_RGB ) && + (iColortype != MNG_COLORTYPE_RGB ) && + (iColortype != MNG_COLORTYPE_RGBA ) ) || + ((pBuf->iColortype == MNG_COLORTYPE_RGBA ) && + (iColortype != MNG_COLORTYPE_RGBA ) ) || +#ifdef MNG_INCLUDE_JNG + ((pBuf->iColortype == MNG_COLORTYPE_JPEGGRAY ) && + (iColortype != MNG_COLORTYPE_JPEGGRAY ) && + (iColortype != MNG_COLORTYPE_JPEGCOLOR ) && + (iColortype != MNG_COLORTYPE_JPEGGRAYA ) && + (iColortype != MNG_COLORTYPE_JPEGCOLORA) ) || + ((pBuf->iColortype == MNG_COLORTYPE_JPEGCOLOR ) && + (iColortype != MNG_COLORTYPE_JPEGCOLOR ) && + (iColortype != MNG_COLORTYPE_JPEGCOLORA) ) || + ((pBuf->iColortype == MNG_COLORTYPE_JPEGGRAYA ) && + (iColortype != MNG_COLORTYPE_JPEGGRAYA ) && + (iColortype != MNG_COLORTYPE_JPEGCOLORA) ) || + ((pBuf->iColortype == MNG_COLORTYPE_JPEGCOLORA) && + (iColortype != MNG_COLORTYPE_JPEGCOLORA) ) || +#endif + ((pBuf->iColortype == MNG_COLORTYPE_INDEXED ) && + (iColortype != MNG_COLORTYPE_INDEXED ) && + (iColortype != MNG_COLORTYPE_RGB ) && + (iColortype != MNG_COLORTYPE_RGBA ) ) ) + MNG_ERROR (pData, MNG_INVALIDCOLORTYPE); + + iRetcode = mng_promote_imageobject (pData, pImage, iBitdepth, iColortype, iFilltype); +#else + if (pData->iPROMbitdepth < pBuf->iBitdepth) + MNG_ERROR (pData, MNG_INVALIDBITDEPTH); + + if ( ((pBuf->iColortype == MNG_COLORTYPE_GRAY ) && + (pData->iPROMcolortype != MNG_COLORTYPE_GRAY ) && + (pData->iPROMcolortype != MNG_COLORTYPE_GRAYA ) && + (pData->iPROMcolortype != MNG_COLORTYPE_RGB ) && + (pData->iPROMcolortype != MNG_COLORTYPE_RGBA ) ) || + ((pBuf->iColortype == MNG_COLORTYPE_GRAYA ) && + (pData->iPROMcolortype != MNG_COLORTYPE_GRAYA ) && + (pData->iPROMcolortype != MNG_COLORTYPE_RGBA ) ) || + ((pBuf->iColortype == MNG_COLORTYPE_RGB ) && + (pData->iPROMcolortype != MNG_COLORTYPE_RGB ) && + (pData->iPROMcolortype != MNG_COLORTYPE_RGBA ) ) || + ((pBuf->iColortype == MNG_COLORTYPE_RGBA ) && + (pData->iPROMcolortype != MNG_COLORTYPE_RGBA ) ) || +#ifdef MNG_INCLUDE_JNG + ((pBuf->iColortype == MNG_COLORTYPE_JPEGGRAY ) && + (pData->iPROMcolortype != MNG_COLORTYPE_JPEGGRAY ) && + (pData->iPROMcolortype != MNG_COLORTYPE_JPEGCOLOR ) && + (pData->iPROMcolortype != MNG_COLORTYPE_JPEGGRAYA ) && + (pData->iPROMcolortype != MNG_COLORTYPE_JPEGCOLORA) ) || + ((pBuf->iColortype == MNG_COLORTYPE_JPEGCOLOR ) && + (pData->iPROMcolortype != MNG_COLORTYPE_JPEGCOLOR ) && + (pData->iPROMcolortype != MNG_COLORTYPE_JPEGCOLORA) ) || + ((pBuf->iColortype == MNG_COLORTYPE_JPEGGRAYA ) && + (pData->iPROMcolortype != MNG_COLORTYPE_JPEGGRAYA ) && + (pData->iPROMcolortype != MNG_COLORTYPE_JPEGCOLORA) ) || + ((pBuf->iColortype == MNG_COLORTYPE_JPEGCOLORA) && + (pData->iPROMcolortype != MNG_COLORTYPE_JPEGCOLORA) ) || +#endif + ((pBuf->iColortype == MNG_COLORTYPE_INDEXED ) && + (pData->iPROMcolortype != MNG_COLORTYPE_INDEXED ) && + (pData->iPROMcolortype != MNG_COLORTYPE_RGB ) && + (pData->iPROMcolortype != MNG_COLORTYPE_RGBA ) ) ) + MNG_ERROR (pData, MNG_INVALIDCOLORTYPE); + + iRetcode = mng_promote_imageobject (pData, pImage, pData->iPROMbitdepth, + pData->iPROMcolortype, pData->iPROMfilltype); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_PROM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +mng_retcode mng_process_display_ipng (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_IPNG, MNG_LC_START); +#endif + /* indicate it for what it is now */ + pData->iDeltaImagetype = MNG_IMAGETYPE_PNG; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_IPNG, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifdef MNG_INCLUDE_JNG +mng_retcode mng_process_display_ijng (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_IJNG, MNG_LC_START); +#endif + /* indicate it for what it is now */ + pData->iDeltaImagetype = MNG_IMAGETYPE_JNG; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_IJNG, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_OPTIMIZE_DISPLAYCALLS +mng_retcode mng_process_display_pplt (mng_datap pData, + mng_uint8 iType, + mng_uint32 iCount, + mng_palette8ep paIndexentries, + mng_uint8p paAlphaentries, + mng_uint8p paUsedentries) +#else +mng_retcode mng_process_display_pplt (mng_datap pData) +#endif +{ + mng_uint32 iX; + mng_imagep pImage = (mng_imagep)pData->pObjzero; + mng_imagedatap pBuf = pImage->pImgbuf; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_PPLT, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + iX = iCount; +#else + iX = pData->iPPLTcount; +#endif +#endif + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + switch (iType) +#else + switch (pData->iPPLTtype) +#endif + { + case MNG_DELTATYPE_REPLACERGB : + { +#ifdef MNG_DECREMENT_LOOPS + for (; iX > 0;iX--) +#else +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + for (iX = 0; iX < iCount; iX++) +#else + for (iX = 0; iX < pData->iPPLTcount; iX++) +#endif +#endif + { +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + if (paUsedentries [iX]) + { + pBuf->aPLTEentries [iX].iRed = paIndexentries [iX].iRed; + pBuf->aPLTEentries [iX].iGreen = paIndexentries [iX].iGreen; + pBuf->aPLTEentries [iX].iBlue = paIndexentries [iX].iBlue; + } +#else + if (pData->paPPLTusedentries [iX]) + { + pBuf->aPLTEentries [iX].iRed = pData->paPPLTindexentries [iX].iRed; + pBuf->aPLTEentries [iX].iGreen = pData->paPPLTindexentries [iX].iGreen; + pBuf->aPLTEentries [iX].iBlue = pData->paPPLTindexentries [iX].iBlue; + } +#endif + } + + break; + } + case MNG_DELTATYPE_DELTARGB : + { +#ifdef MNG_DECREMENT_LOOPS + for (; iX > 0;iX--) +#else +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + for (iX = 0; iX < iCount; iX++) +#else + for (iX = 0; iX < pData->iPPLTcount; iX++) +#endif +#endif + { +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + if (paUsedentries [iX]) + { + pBuf->aPLTEentries [iX].iRed = + (mng_uint8)(pBuf->aPLTEentries [iX].iRed + + paIndexentries [iX].iRed ); + pBuf->aPLTEentries [iX].iGreen = + (mng_uint8)(pBuf->aPLTEentries [iX].iGreen + + paIndexentries [iX].iGreen); + pBuf->aPLTEentries [iX].iBlue = + (mng_uint8)(pBuf->aPLTEentries [iX].iBlue + + paIndexentries [iX].iBlue ); + } +#else + if (pData->paPPLTusedentries [iX]) + { + pBuf->aPLTEentries [iX].iRed = + (mng_uint8)(pBuf->aPLTEentries [iX].iRed + + pData->paPPLTindexentries [iX].iRed ); + pBuf->aPLTEentries [iX].iGreen = + (mng_uint8)(pBuf->aPLTEentries [iX].iGreen + + pData->paPPLTindexentries [iX].iGreen); + pBuf->aPLTEentries [iX].iBlue = + (mng_uint8)(pBuf->aPLTEentries [iX].iBlue + + pData->paPPLTindexentries [iX].iBlue ); + } +#endif + } + + break; + } + case MNG_DELTATYPE_REPLACEALPHA : + { +#ifdef MNG_DECREMENT_LOOPS + for (; iX > 0;iX--) +#else +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + for (iX = 0; iX < iCount; iX++) +#else + for (iX = 0; iX < pData->iPPLTcount; iX++) +#endif +#endif + { +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + if (paUsedentries [iX]) + pBuf->aTRNSentries [iX] = paAlphaentries [iX]; + } +#else + if (pData->paPPLTusedentries [iX]) + pBuf->aTRNSentries [iX] = pData->paPPLTalphaentries [iX]; + } +#endif + + break; + } + case MNG_DELTATYPE_DELTAALPHA : + { +#ifdef MNG_DECREMENT_LOOPS + for (; iX > 0;iX--) +#else +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + for (iX = 0; iX < iCount; iX++) +#else + for (iX = 0; iX < pData->iPPLTcount; iX++) +#endif +#endif + { +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + if (paUsedentries [iX]) + pBuf->aTRNSentries [iX] = + (mng_uint8)(pBuf->aTRNSentries [iX] + + paAlphaentries [iX]); +#else + if (pData->paPPLTusedentries [iX]) + pBuf->aTRNSentries [iX] = + (mng_uint8)(pBuf->aTRNSentries [iX] + + pData->paPPLTalphaentries [iX]); +#endif + } + + break; + } + case MNG_DELTATYPE_REPLACERGBA : + { +#ifdef MNG_DECREMENT_LOOPS + for (; iX > 0;iX--) +#else +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + for (iX = 0; iX < iCount; iX++) +#else + for (iX = 0; iX < pData->iPPLTcount; iX++) +#endif +#endif + { +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + if (paUsedentries [iX]) + { + pBuf->aPLTEentries [iX].iRed = paIndexentries [iX].iRed; + pBuf->aPLTEentries [iX].iGreen = paIndexentries [iX].iGreen; + pBuf->aPLTEentries [iX].iBlue = paIndexentries [iX].iBlue; + pBuf->aTRNSentries [iX] = paAlphaentries [iX]; + } +#else + if (pData->paPPLTusedentries [iX]) + { + pBuf->aPLTEentries [iX].iRed = pData->paPPLTindexentries [iX].iRed; + pBuf->aPLTEentries [iX].iGreen = pData->paPPLTindexentries [iX].iGreen; + pBuf->aPLTEentries [iX].iBlue = pData->paPPLTindexentries [iX].iBlue; + pBuf->aTRNSentries [iX] = pData->paPPLTalphaentries [iX]; + } +#endif + } + + break; + } + case MNG_DELTATYPE_DELTARGBA : + { +#ifdef MNG_DECREMENT_LOOPS + for (; iX > 0;iX--) +#else +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + for (iX = 0; iX < iCount; iX++) +#else + for (iX = 0; iX < pData->iPPLTcount; iX++) +#endif +#endif + { +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + if (paUsedentries [iX]) + { + pBuf->aPLTEentries [iX].iRed = + (mng_uint8)(pBuf->aPLTEentries [iX].iRed + + paIndexentries [iX].iRed ); + pBuf->aPLTEentries [iX].iGreen = + (mng_uint8)(pBuf->aPLTEentries [iX].iGreen + + paIndexentries [iX].iGreen); + pBuf->aPLTEentries [iX].iBlue = + (mng_uint8)(pBuf->aPLTEentries [iX].iBlue + + paIndexentries [iX].iBlue ); + pBuf->aTRNSentries [iX] = + (mng_uint8)(pBuf->aTRNSentries [iX] + + paAlphaentries [iX]); + } +#else + if (pData->paPPLTusedentries [iX]) + { + pBuf->aPLTEentries [iX].iRed = + (mng_uint8)(pBuf->aPLTEentries [iX].iRed + + pData->paPPLTindexentries [iX].iRed ); + pBuf->aPLTEentries [iX].iGreen = + (mng_uint8)(pBuf->aPLTEentries [iX].iGreen + + pData->paPPLTindexentries [iX].iGreen); + pBuf->aPLTEentries [iX].iBlue = + (mng_uint8)(pBuf->aPLTEentries [iX].iBlue + + pData->paPPLTindexentries [iX].iBlue ); + pBuf->aTRNSentries [iX] = + (mng_uint8)(pBuf->aTRNSentries [iX] + + pData->paPPLTalphaentries [iX]); + } +#endif + } + + break; + } + } + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + if ((iType != MNG_DELTATYPE_REPLACERGB) && (iType != MNG_DELTATYPE_DELTARGB)) +#else + if ((pData->iPPLTtype != MNG_DELTATYPE_REPLACERGB) && + (pData->iPPLTtype != MNG_DELTATYPE_DELTARGB ) ) +#endif + { + if (pBuf->bHasTRNS) + { +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + if (iCount > pBuf->iTRNScount) + pBuf->iTRNScount = iCount; +#else + if (pData->iPPLTcount > pBuf->iTRNScount) + pBuf->iTRNScount = pData->iPPLTcount; +#endif + } + else + { +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + pBuf->iTRNScount = iCount; + pBuf->bHasTRNS = MNG_TRUE; +#else + pBuf->iTRNScount = pData->iPPLTcount; + pBuf->bHasTRNS = MNG_TRUE; +#endif + } + } + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + if ((iType != MNG_DELTATYPE_REPLACEALPHA) && (iType != MNG_DELTATYPE_DELTAALPHA)) +#else + if ((pData->iPPLTtype != MNG_DELTATYPE_REPLACEALPHA) && + (pData->iPPLTtype != MNG_DELTATYPE_DELTAALPHA ) ) +#endif + { +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + if (iCount > pBuf->iPLTEcount) + pBuf->iPLTEcount = iCount; +#else + if (pData->iPPLTcount > pBuf->iPLTEcount) + pBuf->iPLTEcount = pData->iPPLTcount; +#endif + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_PPLT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_MAGN +#ifndef MNG_OPTIMIZE_DISPLAYCALLS +mng_retcode mng_process_display_magn (mng_datap pData, + mng_uint16 iFirstid, + mng_uint16 iLastid, + mng_uint8 iMethodX, + mng_uint16 iMX, + mng_uint16 iMY, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint16 iMT, + mng_uint16 iMB, + mng_uint8 iMethodY) +#else +mng_retcode mng_process_display_magn (mng_datap pData) +#endif +{ + mng_uint16 iX; + mng_imagep pImage; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_MAGN, MNG_LC_START); +#endif + /* iterate the object-ids */ +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + for (iX = iFirstid; iX <= iLastid; iX++) +#else + for (iX = pData->iMAGNfirstid; iX <= pData->iMAGNlastid; iX++) +#endif + { + if (iX == 0) /* process object 0 ? */ + { + pImage = (mng_imagep)pData->pObjzero; + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + pImage->iMAGN_MethodX = iMethodX; + pImage->iMAGN_MethodY = iMethodY; + pImage->iMAGN_MX = iMX; + pImage->iMAGN_MY = iMY; + pImage->iMAGN_ML = iML; + pImage->iMAGN_MR = iMR; + pImage->iMAGN_MT = iMT; + pImage->iMAGN_MB = iMB; +#else + pImage->iMAGN_MethodX = pData->iMAGNmethodX; + pImage->iMAGN_MethodY = pData->iMAGNmethodY; + pImage->iMAGN_MX = pData->iMAGNmX; + pImage->iMAGN_MY = pData->iMAGNmY; + pImage->iMAGN_ML = pData->iMAGNmL; + pImage->iMAGN_MR = pData->iMAGNmR; + pImage->iMAGN_MT = pData->iMAGNmT; + pImage->iMAGN_MB = pData->iMAGNmB; +#endif + } + else + { + pImage = mng_find_imageobject (pData, iX); + /* object exists & is not frozen ? */ + if ((pImage) && (!pImage->bFrozen)) + { /* previous magnification to be done ? */ + if ((pImage->iMAGN_MethodX) || (pImage->iMAGN_MethodY)) + { + mng_retcode iRetcode = mng_magnify_imageobject (pData, pImage); + if (iRetcode) /* on error bail out */ + return iRetcode; + } + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + pImage->iMAGN_MethodX = iMethodX; + pImage->iMAGN_MethodY = iMethodY; + pImage->iMAGN_MX = iMX; + pImage->iMAGN_MY = iMY; + pImage->iMAGN_ML = iML; + pImage->iMAGN_MR = iMR; + pImage->iMAGN_MT = iMT; + pImage->iMAGN_MB = iMB; +#else + pImage->iMAGN_MethodX = pData->iMAGNmethodX; + pImage->iMAGN_MethodY = pData->iMAGNmethodY; + pImage->iMAGN_MX = pData->iMAGNmX; + pImage->iMAGN_MY = pData->iMAGNmY; + pImage->iMAGN_ML = pData->iMAGNmL; + pImage->iMAGN_MR = pData->iMAGNmR; + pImage->iMAGN_MT = pData->iMAGNmT; + pImage->iMAGN_MB = pData->iMAGNmB; +#endif + } + } + } + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + pData->iMAGNfromid = iFirstid; + pData->iMAGNtoid = iLastid; + iX = iFirstid; +#else + pData->iMAGNfromid = pData->iMAGNfirstid; + pData->iMAGNtoid = pData->iMAGNlastid; + iX = pData->iMAGNfirstid; +#endif + /* iterate again for showing */ +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + while ((iX <= iLastid) && (!pData->bTimerset)) +#else + while ((iX <= pData->iMAGNlastid) && (!pData->bTimerset)) +#endif + { + pData->iMAGNcurrentid = iX; + + if (iX) /* only real objects ! */ + { + pImage = mng_find_imageobject (pData, iX); + /* object exists & is not frozen & + is visible & is viewable ? */ + if ((pImage) && (!pImage->bFrozen) && + (pImage->bVisible) && (pImage->bViewable)) + { + mng_retcode iRetcode = mng_display_image (pData, pImage, MNG_FALSE); + if (iRetcode) + return iRetcode; + } + } + + iX++; + } + + if (pData->bTimerset) /* broken ? */ + pData->iBreakpoint = 9; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_MAGN, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_process_display_magn2 (mng_datap pData) +{ + mng_uint16 iX; + mng_imagep pImage; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_MAGN, MNG_LC_START); +#endif + + iX = pData->iMAGNcurrentid; + /* iterate again for showing */ + while ((iX <= pData->iMAGNtoid) && (!pData->bTimerset)) + { + pData->iMAGNcurrentid = iX; + + if (iX) /* only real objects ! */ + { + pImage = mng_find_imageobject (pData, iX); + /* object exists & is not frozen & + is visible & is viewable ? */ + if ((pImage) && (!pImage->bFrozen) && + (pImage->bVisible) && (pImage->bViewable)) + { + mng_retcode iRetcode = mng_display_image (pData, pImage, MNG_FALSE); + if (iRetcode) + return iRetcode; + } + } + + iX++; + } + + if (pData->bTimerset) /* broken ? */ + pData->iBreakpoint = 9; + else + pData->iBreakpoint = 0; /* not again ! */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_MAGN, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_PAST +#ifndef MNG_OPTIMIZE_DISPLAYCALLS +mng_retcode mng_process_display_past (mng_datap pData, + mng_uint16 iTargetid, + mng_uint8 iTargettype, + mng_int32 iTargetx, + mng_int32 iTargety, + mng_uint32 iCount, + mng_ptr pSources) +#else +mng_retcode mng_process_display_past (mng_datap pData) +#endif +{ + mng_retcode iRetcode = MNG_NOERROR; + mng_imagep pTargetimg; + mng_imagep pSourceimg; +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + mng_past_sourcep pSource = (mng_past_sourcep)pSources; +#else + mng_past_sourcep pSource = (mng_past_sourcep)pData->pPASTsources; +#endif + mng_uint32 iX = 0; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_PAST, MNG_LC_START); +#endif + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + if (iTargetid) /* a real destination object ? */ +#else + if (pData->iPASTtargetid) /* a real destination object ? */ +#endif + { /* let's find it then */ +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + pTargetimg = (mng_imagep)mng_find_imageobject (pData, iTargetid); +#else + pTargetimg = (mng_imagep)mng_find_imageobject (pData, pData->iPASTtargetid); +#endif + + if (!pTargetimg) /* if it doesn't exists; do a barf */ + MNG_ERROR (pData, MNG_OBJECTUNKNOWN); + /* it's gotta be abstract !!! */ + if (pTargetimg->pImgbuf->bConcrete) + MNG_ERROR (pData, MNG_OBJNOTABSTRACT); + /* we want 32-/64-bit RGBA to play with ! */ + if ((pTargetimg->pImgbuf->iBitdepth <= MNG_BITDEPTH_8) || + (pTargetimg->pImgbuf->iColortype == MNG_COLORTYPE_GRAY) || + (pTargetimg->pImgbuf->iColortype == MNG_COLORTYPE_RGB) || + (pTargetimg->pImgbuf->iColortype == MNG_COLORTYPE_INDEXED) || + (pTargetimg->pImgbuf->iColortype == MNG_COLORTYPE_GRAYA) ) + iRetcode = mng_promote_imageobject (pData, pTargetimg, MNG_BITDEPTH_8, + MNG_COLORTYPE_RGBA, + MNG_FILLMETHOD_LEFTBITREPLICATE); + else + if ((pTargetimg->pImgbuf->iBitdepth > MNG_BITDEPTH_8) && + ((pTargetimg->pImgbuf->iColortype == MNG_COLORTYPE_GRAY) || + (pTargetimg->pImgbuf->iColortype == MNG_COLORTYPE_RGB) || + (pTargetimg->pImgbuf->iColortype == MNG_COLORTYPE_GRAYA) ) ) + iRetcode = mng_promote_imageobject (pData, pTargetimg, MNG_BITDEPTH_16, + MNG_COLORTYPE_RGBA, + MNG_FILLMETHOD_LEFTBITREPLICATE); +#ifdef MNG_INCLUDE_JNG + else + if ((pTargetimg->pImgbuf->iColortype == MNG_COLORTYPE_JPEGGRAY) || + (pTargetimg->pImgbuf->iColortype == MNG_COLORTYPE_JPEGCOLOR) || + (pTargetimg->pImgbuf->iColortype == MNG_COLORTYPE_JPEGGRAYA) ) + iRetcode = mng_promote_imageobject (pData, pTargetimg, + pTargetimg->pImgbuf->iBitdepth, + MNG_COLORTYPE_JPEGCOLORA, + MNG_FILLMETHOD_LEFTBITREPLICATE); +#endif + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* make it really abstract ? */ + if (!pTargetimg->pImgbuf->bCorrected) + { + iRetcode = mng_colorcorrect_object (pData, pTargetimg); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + } + else + { /* pasting into object 0 !!! */ + pTargetimg = (mng_imagep)pData->pObjzero; + /* is it usable ??? */ + if ((pTargetimg->bClipped) && + (pTargetimg->iClipr > pTargetimg->iPosx) && + (pTargetimg->iClipb > pTargetimg->iPosy)) + { + /* make it 32-bit RGBA please !!! */ + iRetcode = mng_reset_object_details (pData, pTargetimg, + pTargetimg->iClipr - pTargetimg->iPosx, + pTargetimg->iClipb - pTargetimg->iPosy, + MNG_BITDEPTH_8, MNG_COLORTYPE_RGBA, + 0, 0, 0, MNG_FALSE); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + else + pTargetimg = MNG_NULL; /* clipped beyond visibility ! */ + } + + if (pTargetimg) /* usable destination ? */ + { + mng_int32 iSourceY; + mng_int32 iSourceYinc; + mng_int32 iSourcerowsize; + mng_int32 iSourcesamples; + mng_bool bSourceRGBA16; + mng_int32 iTargetY; + mng_int32 iTargetrowsize; + mng_int32 iTargetsamples; + mng_bool bTargetRGBA16 = MNG_FALSE; + mng_int32 iTemprowsize; + mng_imagedatap pBuf; +#ifndef MNG_SKIPCHUNK_MAGN + /* needs magnification ? */ + if ((pTargetimg->iMAGN_MethodX) || (pTargetimg->iMAGN_MethodY)) + iRetcode = mng_magnify_imageobject (pData, pTargetimg); +#endif + + if (!iRetcode) /* still ok ? */ + { + bTargetRGBA16 = (mng_bool)(pTargetimg->pImgbuf->iBitdepth > 8); + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + switch (iTargettype) /* determine target x/y */ +#else + switch (pData->iPASTtargettype) /* determine target x/y */ +#endif + { + case 0 : { +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + pData->iPastx = iTargetx; + pData->iPasty = iTargety; +#else + pData->iPastx = pData->iPASTtargetx; + pData->iPasty = pData->iPASTtargety; +#endif + break; + } + + case 1 : { +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + pData->iPastx = pTargetimg->iPastx + iTargetx; + pData->iPasty = pTargetimg->iPasty + iTargety; +#else + pData->iPastx = pTargetimg->iPastx + pData->iPASTtargetx; + pData->iPasty = pTargetimg->iPasty + pData->iPASTtargety; +#endif + break; + } + + case 2 : { +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + pData->iPastx += iTargetx; + pData->iPasty += iTargety; +#else + pData->iPastx += pData->iPASTtargetx; + pData->iPasty += pData->iPASTtargety; +#endif + break; + } + } + /* save for next time ... */ + pTargetimg->iPastx = pData->iPastx; + pTargetimg->iPasty = pData->iPasty; + /* address destination for row-routines */ + pData->pStoreobj = (mng_objectp)pTargetimg; + pData->pStorebuf = (mng_objectp)pTargetimg->pImgbuf; + } + /* process the sources one by one */ +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + while ((!iRetcode) && (iX < iCount)) +#else + while ((!iRetcode) && (iX < pData->iPASTcount)) +#endif + { /* find the little bastards first */ + pSourceimg = (mng_imagep)mng_find_imageobject (pData, pSource->iSourceid); + /* exists and viewable? */ + if ((pSourceimg) && (pSourceimg->bViewable)) + { /* needs magnification ? */ +#ifndef MNG_SKIPCHUNK_MAGN + if ((pSourceimg->iMAGN_MethodX) || (pSourceimg->iMAGN_MethodY)) + iRetcode = mng_magnify_imageobject (pData, pSourceimg); +#endif + + if (!iRetcode) /* still ok ? */ + { + pBuf = (mng_imagedatap)pSourceimg->pImgbuf; + /* address source for row-routines */ + pData->pRetrieveobj = (mng_objectp)pSourceimg; + + pData->iPass = -1; /* init row-processing variables */ + pData->iRowinc = 1; + pData->iColinc = 1; + pData->iPixelofs = 0; + iSourcesamples = (mng_int32)pBuf->iWidth; + iSourcerowsize = pBuf->iRowsize; + bSourceRGBA16 = (mng_bool)(pBuf->iBitdepth > 8); + /* make sure the delta-routines do the right thing */ + pData->iDeltatype = MNG_DELTATYPE_BLOCKPIXELREPLACE; + + switch (pBuf->iColortype) + { + case 0 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (bSourceRGBA16) + pData->fRetrieverow = (mng_fptr)mng_retrieve_g16; + else +#endif + pData->fRetrieverow = (mng_fptr)mng_retrieve_g8; + + pData->bIsOpaque = (mng_bool)(!pBuf->bHasTRNS); + break; + } + + case 2 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (bSourceRGBA16) + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgb16; + else +#endif + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgb8; + + pData->bIsOpaque = (mng_bool)(!pBuf->bHasTRNS); + break; + } + + + case 3 : { pData->fRetrieverow = (mng_fptr)mng_retrieve_idx8; + pData->bIsOpaque = (mng_bool)(!pBuf->bHasTRNS); + break; + } + + + case 4 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (bSourceRGBA16) + pData->fRetrieverow = (mng_fptr)mng_retrieve_ga16; + else +#endif + pData->fRetrieverow = (mng_fptr)mng_retrieve_ga8; + + pData->bIsOpaque = MNG_FALSE; + break; + } + + + case 6 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (bSourceRGBA16) + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba16; + else +#endif + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba8; + + pData->bIsOpaque = MNG_FALSE; + break; + } + + case 8 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (bSourceRGBA16) + pData->fRetrieverow = (mng_fptr)mng_retrieve_g16; + else +#endif + pData->fRetrieverow = (mng_fptr)mng_retrieve_g8; + + pData->bIsOpaque = MNG_TRUE; + break; + } + + case 10 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (bSourceRGBA16) + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgb16; + else +#endif + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgb8; + + pData->bIsOpaque = MNG_TRUE; + break; + } + + + case 12 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (bSourceRGBA16) + pData->fRetrieverow = (mng_fptr)mng_retrieve_ga16; + else +#endif + pData->fRetrieverow = (mng_fptr)mng_retrieve_ga8; + + pData->bIsOpaque = MNG_FALSE; + break; + } + + + case 14 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (bSourceRGBA16) + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba16; + else +#endif + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba8; + + pData->bIsOpaque = MNG_FALSE; + break; + } + } + /* determine scaling */ +#ifndef MNG_NO_16BIT_SUPPORT +#ifndef MNG_NO_DELTA_PNG + if ((!bSourceRGBA16) && (bTargetRGBA16)) + pData->fScalerow = (mng_fptr)mng_scale_rgba8_rgba16; + else + if ((bSourceRGBA16) && (!bTargetRGBA16)) + pData->fScalerow = (mng_fptr)mng_scale_rgba16_rgba8; + else +#endif +#endif + pData->fScalerow = MNG_NULL; + + /* default no color-correction */ + pData->fCorrectrow = MNG_NULL; + +#if defined(MNG_FULL_CMS) /* determine color-management routine */ + iRetcode = mng_init_full_cms (pData, MNG_FALSE, MNG_FALSE, MNG_TRUE); +#elif defined(MNG_GAMMA_ONLY) + iRetcode = mng_init_gamma_only (pData, MNG_FALSE, MNG_FALSE, MNG_TRUE); +#elif defined(MNG_APP_CMS) + iRetcode = mng_init_app_cms (pData, MNG_FALSE, MNG_FALSE, MNG_TRUE); +#endif + } + + if (!iRetcode) /* still ok ? */ + { + pData->fFliprow = MNG_NULL; /* no flipping or tiling by default */ + pData->fTilerow = MNG_NULL; + /* but perhaps we do have to ... */ + switch (pSource->iOrientation) + { + case 2 : ; + case 4 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (bTargetRGBA16) + pData->fFliprow = (mng_fptr)mng_flip_rgba16; + else +#endif + pData->fFliprow = (mng_fptr)mng_flip_rgba8; + break; + } + + case 8 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (bTargetRGBA16) + pData->fTilerow = (mng_fptr)mng_tile_rgba16; + else +#endif + pData->fTilerow = (mng_fptr)mng_tile_rgba8; + break; + } + } + /* determine composition routine */ + /* note that we're abusing the delta-routine setup !!! */ + switch (pSource->iComposition) + { + case 0 : { /* composite over */ +#ifndef MNG_NO_16BIT_SUPPORT + if (bTargetRGBA16) + pData->fDeltarow = (mng_fptr)mng_composeover_rgba16; + else +#endif + pData->fDeltarow = (mng_fptr)mng_composeover_rgba8; + break; + } + + case 1 : { /* replace */ +#ifndef MNG_NO_16BIT_SUPPORT + if (bTargetRGBA16) + pData->fDeltarow = (mng_fptr)mng_delta_rgba16_rgba16; + else +#endif + pData->fDeltarow = (mng_fptr)mng_delta_rgba8_rgba8; + break; + } + + case 2 : { /* composite under */ +#ifndef MNG_NO_16BIT_SUPPORT + if (bTargetRGBA16) + pData->fDeltarow = (mng_fptr)mng_composeunder_rgba16; + else +#endif + pData->fDeltarow = (mng_fptr)mng_composeunder_rgba8; + break; + } + } + /* determine offsets & clipping */ + if (pSource->iOffsettype == 1) + { + pData->iDestl = pData->iPastx + pSource->iOffsetx; + pData->iDestt = pData->iPasty + pSource->iOffsety; + } + else + { + pData->iDestl = pSource->iOffsetx; + pData->iDestt = pSource->iOffsety; + } + + pData->iDestr = (mng_int32)pTargetimg->pImgbuf->iWidth; + pData->iDestb = (mng_int32)pTargetimg->pImgbuf->iHeight; + /* take the source dimension into account ? */ + if (pSource->iOrientation != 8) + { + pData->iDestr = MIN_COORD (pData->iDestr, pData->iDestl + (mng_int32)pBuf->iWidth); + pData->iDestb = MIN_COORD (pData->iDestb, pData->iDestt + (mng_int32)pBuf->iHeight); + } + /* source clipping */ + if (pSource->iBoundarytype == 1) + { + if (pData->iDestl < pData->iPastx + pSource->iBoundaryl) + pData->iSourcel = pData->iPastx + pSource->iBoundaryl - pData->iDestl; + else + pData->iSourcel = 0; + + if (pData->iDestt < pData->iPasty + pSource->iBoundaryt) + pData->iSourcet = pData->iPasty + pSource->iBoundaryt - pData->iDestt; + else + pData->iSourcet = 0; + + pData->iDestl = MAX_COORD (pData->iDestl, pData->iPastx + pSource->iBoundaryl); + pData->iDestt = MAX_COORD (pData->iDestt, pData->iPasty + pSource->iBoundaryt); + pData->iDestr = MIN_COORD (pData->iDestr, pData->iPastx + pSource->iBoundaryr); + pData->iDestb = MIN_COORD (pData->iDestb, pData->iPasty + pSource->iBoundaryb); + } + else + { + if (pData->iDestl < pSource->iBoundaryl) + pData->iSourcel = pSource->iBoundaryl - pData->iDestl; + else + pData->iSourcel = 0; + + if (pData->iDestt < pSource->iBoundaryt) + pData->iSourcet = pSource->iBoundaryt - pData->iDestt; + else + pData->iSourcet = 0; + + pData->iDestl = MAX_COORD (pData->iDestl, pSource->iBoundaryl); + pData->iDestt = MAX_COORD (pData->iDestt, pSource->iBoundaryt); + pData->iDestr = MIN_COORD (pData->iDestr, pSource->iBoundaryr); + pData->iDestb = MIN_COORD (pData->iDestb, pSource->iBoundaryb); + } + + if (pData->iSourcel) /* indent source ? */ + { +#ifndef MNG_NO_16BIT_SUPPORT + if (bTargetRGBA16) /* abuse tiling routine to shift source-pixels */ + pData->fTilerow = (mng_fptr)mng_tile_rgba16; + else +#endif + pData->fTilerow = (mng_fptr)mng_tile_rgba8; + } + /* anything to display ? */ + if ((pData->iDestl <= pData->iDestr) && (pData->iDestt <= pData->iDestb)) + { /* init variables for the loop */ + if ((pSource->iOrientation == 2) || (pSource->iOrientation == 6)) + { + iSourceY = (mng_int32)pBuf->iHeight - 1 - pData->iSourcet; + iSourceYinc = -1; + } + else + { + iSourceY = pData->iSourcet; + iSourceYinc = 1; + } + + iTargetY = pData->iDestt; + pData->iCol = pData->iDestl; + + iTargetsamples = pData->iDestr - pData->iDestl; + +#ifndef MNG_NO_16BIT_SUPPORT + if (bTargetRGBA16) + iTargetrowsize = (iTargetsamples << 3); + else +#endif + iTargetrowsize = (iTargetsamples << 2); + + /* get temporary work-buffers */ + if (iSourcerowsize > iTargetrowsize) + iTemprowsize = iSourcerowsize << 1; + else + iTemprowsize = iTargetrowsize << 1; + MNG_ALLOC (pData, pData->pRGBArow, iTemprowsize); + MNG_ALLOC (pData, pData->pWorkrow, iTemprowsize); + + while ((!iRetcode) && (iTargetY < pData->iDestb)) + { /* get a row */ + pData->iRow = iSourceY; + pData->iRowsamples = iSourcesamples; + pData->iRowsize = iSourcerowsize; + pData->bIsRGBA16 = bSourceRGBA16; + iRetcode = ((mng_retrieverow)pData->fRetrieverow) (pData); + /* scale it (if necessary) */ + if ((!iRetcode) && (pData->fScalerow)) + iRetcode = ((mng_scalerow)pData->fScalerow) (pData); + + pData->bIsRGBA16 = bTargetRGBA16; + /* color correction (if necessary) */ + if ((!iRetcode) && (pData->fCorrectrow)) + iRetcode = ((mng_correctrow)pData->fCorrectrow) (pData); + /* flipping (if necessary) */ + if ((!iRetcode) && (pData->fFliprow)) + iRetcode = ((mng_fliprow)pData->fFliprow) (pData); + /* tiling (if necessary) */ + if ((!iRetcode) && (pData->fTilerow)) + iRetcode = ((mng_tilerow)pData->fTilerow) (pData); + + if (!iRetcode) /* and paste..... */ + { + pData->iRow = iTargetY; + pData->iRowsamples = iTargetsamples; + pData->iRowsize = iTargetrowsize; + iRetcode = ((mng_deltarow)pData->fDeltarow) (pData); + } + + iSourceY += iSourceYinc; /* and next line */ + + if (iSourceY < 0) + iSourceY = (mng_int32)pBuf->iHeight - 1; + else + if (iSourceY >= (mng_int32)pBuf->iHeight) + iSourceY = 0; + + iTargetY++; + } + /* drop the temporary row-buffer */ + MNG_FREEX (pData, pData->pWorkrow, iTemprowsize); + MNG_FREEX (pData, pData->pRGBArow, iTemprowsize); + } + +#if defined(MNG_FULL_CMS) /* cleanup cms stuff */ + if (!iRetcode) + iRetcode = mng_clear_cms (pData); +#endif + } + + pSource++; /* neeeeext */ + iX++; + } + } + + if (iRetcode) /* on error bail out */ + return iRetcode; + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + if (!iTargetid) /* did we paste into object 0 ? */ +#else + if (!pData->iPASTtargetid) /* did we paste into object 0 ? */ +#endif + { /* display it then ! */ + iRetcode = mng_display_image (pData, pTargetimg, MNG_FALSE); + if (iRetcode) /* on error bail out */ + return iRetcode; + } + else + { /* target is visible & viewable ? */ + if ((pTargetimg->bVisible) && (pTargetimg->bViewable)) + { + iRetcode = mng_display_image (pData, pTargetimg, MNG_FALSE); + if (iRetcode) + return iRetcode; + } + } + } + + if (pData->bTimerset) /* broken ? */ + { +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + pData->iPASTid = iTargetid; +#else + pData->iPASTid = pData->iPASTtargetid; +#endif + pData->iBreakpoint = 11; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_PAST, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SKIPCHUNK_PAST */ + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_PAST +mng_retcode mng_process_display_past2 (mng_datap pData) +{ + mng_retcode iRetcode; + mng_imagep pTargetimg; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_PAST, MNG_LC_START); +#endif + + if (pData->iPASTid) /* a real destination object ? */ + pTargetimg = (mng_imagep)mng_find_imageobject (pData, pData->iPASTid); + else /* otherwise object 0 */ + pTargetimg = (mng_imagep)pData->pObjzero; + + iRetcode = mng_display_image (pData, pTargetimg, MNG_FALSE); + if (iRetcode) + return iRetcode; + + pData->iBreakpoint = 0; /* only once */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_DISPLAY_PAST, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SKIPCHUNK_PAST */ + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_DISPLAY_PROCS */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ + + diff --git a/Engine/lib/lmng/libmng_display.h b/Engine/lib/lmng/libmng_display.h new file mode 100644 index 000000000..f394dd2f6 --- /dev/null +++ b/Engine/lib/lmng/libmng_display.h @@ -0,0 +1,343 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_display.h copyright (c) 2000-2007 G.Juyn * */ +/* * version : 1.0.10 * */ +/* * * */ +/* * purpose : Display management (definition) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : Definition of the display managament routines * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed strict-ANSI stuff * */ +/* * * */ +/* * 0.5.2 - 05/20/2000 - G.Juyn * */ +/* * - added JNG support stuff * */ +/* * * */ +/* * 0.5.3 - 06/16/2000 - G.Juyn * */ +/* * - changed progressive-display processing * */ +/* * 0.5.3 - 06/22/2000 - G.Juyn * */ +/* * - added support for delta-image processing * */ +/* * - added support for PPLT chunk processing * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * 0.9.3 - 08/07/2000 - G.Juyn * */ +/* * - B111300 - fixup for improved portability * */ +/* * 0.9.3 - 08/26/2000 - G.Juyn * */ +/* * - added MAGN chunk * */ +/* * 0.9.3 - 10/16/2000 - G.Juyn * */ +/* * - added JDAA chunk * */ +/* * * */ +/* * 0.9.4 - 11/24/2000 - G.Juyn * */ +/* * - moved restore of object 0 to libmng_display * */ +/* * * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * 1.0.5 - 09/13/2002 - G.Juyn * */ +/* * - fixed read/write of MAGN chunk * */ +/* * 1.0.5 - 09/20/2002 - G.Juyn * */ +/* * - added support for PAST * */ +/* * 1.0.5 - 10/07/2002 - G.Juyn * */ +/* * - added proposed change in handling of TERM- & if-delay * */ +/* * 1.0.5 - 10/20/2002 - G.Juyn * */ +/* * - fixed display of visible target of PAST operation * */ +/* * * */ +/* * 1.0.7 - 03/24/2004 - G.R-P. * */ +/* * - added some SKIPCHUNK conditionals * */ +/* * * */ +/* * 1.0.9 - 12/11/2004 - G.Juyn * */ +/* * - added conditional MNG_OPTIMIZE_DISPLAYCALLS * */ +/* * * */ +/* * 1.0.10 - 04/08/2007 - G.Juyn * */ +/* * - added support for mPNG proposal * */ +/* * 1.0.10 - 04/12/2007 - G.Juyn * */ +/* * - added support for ANG proposal * */ +/* * * */ +/* ************************************************************************** */ + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +#ifndef _libmng_display_h_ +#define _libmng_display_h_ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_DISPLAY_PROCS + +/* ************************************************************************** */ + +mng_retcode mng_display_progressive_refresh (mng_datap pData, + mng_uint32 iInterval); + +/* ************************************************************************** */ + +mng_retcode mng_reset_objzero (mng_datap pData); + +mng_retcode mng_display_image (mng_datap pData, + mng_imagep pImage, + mng_bool bLayeradvanced); + +mng_retcode mng_execute_delta_image (mng_datap pData, + mng_imagep pTarget, + mng_imagep pDelta); + +/* ************************************************************************** */ + +mng_retcode mng_process_display (mng_datap pData); + +/* ************************************************************************** */ + +#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT +png_imgtype mng_png_imgtype (mng_uint8 colortype, + mng_uint8 bitdepth); +#endif + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + +mng_retcode mng_process_display_ihdr (mng_datap pData); + +#ifdef MNG_INCLUDE_MPNG_PROPOSAL +mng_retcode mng_process_display_mpng (mng_datap pData); +#endif + +#ifdef MNG_INCLUDE_ANG_PROPOSAL +mng_retcode mng_process_display_ang (mng_datap pData); +#endif + +mng_retcode mng_process_display_idat (mng_datap pData, + mng_uint32 iRawlen, + mng_uint8p pRawdata); + +mng_retcode mng_process_display_iend (mng_datap pData); +mng_retcode mng_process_display_mend (mng_datap pData); +mng_retcode mng_process_display_mend2 (mng_datap pData); +mng_retcode mng_process_display_defi (mng_datap pData); + +#ifndef MNG_SKIPCHUNK_BASI +mng_retcode mng_process_display_basi (mng_datap pData, + mng_uint16 iRed, + mng_uint16 iGreen, + mng_uint16 iBlue, + mng_bool bHasalpha, + mng_uint16 iAlpha, + mng_uint8 iViewable); +#endif + +#ifndef MNG_SKIPCHUNK_CLON +mng_retcode mng_process_display_clon (mng_datap pData, + mng_uint16 iSourceid, + mng_uint16 iCloneid, + mng_uint8 iClonetype, + mng_bool bHasdonotshow, + mng_uint8 iDonotshow, + mng_uint8 iConcrete, + mng_bool bHasloca, + mng_uint8 iLocationtype, + mng_int32 iLocationx, + mng_int32 iLocationy); +mng_retcode mng_process_display_clon2 (mng_datap pData); +#endif + +#ifndef MNG_SKIPCHUNK_DISC +mng_retcode mng_process_display_disc (mng_datap pData, + mng_uint32 iCount, + mng_uint16p pIds); +#endif + +#ifndef MNG_SKIPCHUNK_FRAM +mng_retcode mng_process_display_fram (mng_datap pData, + mng_uint8 iFramemode, + mng_uint8 iChangedelay, + mng_uint32 iDelay, + mng_uint8 iChangetimeout, + mng_uint32 iTimeout, + mng_uint8 iChangeclipping, + mng_uint8 iCliptype, + mng_int32 iClipl, + mng_int32 iClipr, + mng_int32 iClipt, + mng_int32 iClipb); +mng_retcode mng_process_display_fram2 (mng_datap pData); +#endif + +#ifndef MNG_SKIPCHUNK_MOVE +mng_retcode mng_process_display_move (mng_datap pData, + mng_uint16 iFromid, + mng_uint16 iToid, + mng_uint8 iMovetype, + mng_int32 iMovex, + mng_int32 iMovey); +#endif + +#ifndef MNG_SKIPCHUNK_CLIP +mng_retcode mng_process_display_clip (mng_datap pData, + mng_uint16 iFromid, + mng_uint16 iToid, + mng_uint8 iCliptype, + mng_int32 iClipl, + mng_int32 iClipr, + mng_int32 iClipt, + mng_int32 iClipb); +#endif + +#ifndef MNG_SKIPCHUNK_SHOW +mng_retcode mng_process_display_show (mng_datap pData); +#endif +#ifndef MNG_SKIPCHUNK_SAVE +mng_retcode mng_process_display_save (mng_datap pData); +#endif +#ifndef MNG_SKIPCHUNK_SEEK +mng_retcode mng_process_display_seek (mng_datap pData); +#endif +#ifdef MNG_INCLUDE_JNG +mng_retcode mng_process_display_jhdr (mng_datap pData); + +mng_retcode mng_process_display_jdaa (mng_datap pData, + mng_uint32 iRawlen, + mng_uint8p pRawdata); + +mng_retcode mng_process_display_jdat (mng_datap pData, + mng_uint32 iRawlen, + mng_uint8p pRawdata); + +#endif +#ifndef MNG_NO_DELTA_PNG +mng_retcode mng_process_display_dhdr (mng_datap pData, + mng_uint16 iObjectid, + mng_uint8 iImagetype, + mng_uint8 iDeltatype, + mng_uint32 iBlockwidth, + mng_uint32 iBlockheight, + mng_uint32 iBlockx, + mng_uint32 iBlocky); + +mng_retcode mng_process_display_prom (mng_datap pData, + mng_uint8 iBitdepth, + mng_uint8 iColortype, + mng_uint8 iFilltype); + +mng_retcode mng_process_display_ipng (mng_datap pData); +#ifdef MNG_INCLUDE_JNG +mng_retcode mng_process_display_ijng (mng_datap pData); +#endif + +mng_retcode mng_process_display_pplt (mng_datap pData, + mng_uint8 iType, + mng_uint32 iCount, + mng_palette8ep paIndexentries, + mng_uint8p paAlphaentries, + mng_uint8p paUsedentries); +#endif + +#ifndef MNG_SKIPCHUNK_MAGN +mng_retcode mng_process_display_magn (mng_datap pData, + mng_uint16 iFirstid, + mng_uint16 iLastid, + mng_uint8 iMethodX, + mng_uint16 iMX, + mng_uint16 iMY, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint16 iMT, + mng_uint16 iMB, + mng_uint8 iMethodY); +mng_retcode mng_process_display_magn2 (mng_datap pData); +#endif + +#ifndef MNG_SKIPCHUNK_PAST +mng_retcode mng_process_display_past (mng_datap pData, + mng_uint16 iTargetid, + mng_uint8 iTargettype, + mng_int32 iTargetx, + mng_int32 iTargety, + mng_uint32 iCount, + mng_ptr pSources); +mng_retcode mng_process_display_past2 (mng_datap pData); +#endif + +#else /* MNG_OPTIMIZE_DISPLAYCALLS */ + +mng_retcode mng_process_display_ihdr (mng_datap pData); +#ifdef MNG_INCLUDE_MPNG_PROPOSAL +mng_retcode mng_process_display_mpng (mng_datap pData); +#endif +mng_retcode mng_process_display_idat (mng_datap pData); +mng_retcode mng_process_display_iend (mng_datap pData); +mng_retcode mng_process_display_mend (mng_datap pData); +mng_retcode mng_process_display_mend2 (mng_datap pData); +mng_retcode mng_process_display_defi (mng_datap pData); +#ifndef MNG_SKIPCHUNK_BASI +mng_retcode mng_process_display_basi (mng_datap pData); +#endif +#ifndef MNG_SKIPCHUNK_CLON +mng_retcode mng_process_display_clon (mng_datap pData); +mng_retcode mng_process_display_clon2 (mng_datap pData); +#endif +#ifndef MNG_SKIPCHUNK_DISC +mng_retcode mng_process_display_disc (mng_datap pData); +#endif +#ifndef MNG_SKIPCHUNK_FRAM +mng_retcode mng_process_display_fram (mng_datap pData); +mng_retcode mng_process_display_fram2 (mng_datap pData); +#endif +#ifndef MNG_SKIPCHUNK_MOVE +mng_retcode mng_process_display_move (mng_datap pData); +#endif +#ifndef MNG_SKIPCHUNK_CLIP +mng_retcode mng_process_display_clip (mng_datap pData); +#endif +#ifndef MNG_SKIPCHUNK_SHOW +mng_retcode mng_process_display_show (mng_datap pData); +#endif +#ifndef MNG_SKIPCHUNK_SAVE +mng_retcode mng_process_display_save (mng_datap pData); +#endif +#ifndef MNG_SKIPCHUNK_SEEK +mng_retcode mng_process_display_seek (mng_datap pData); +#endif +#ifdef MNG_INCLUDE_JNG +mng_retcode mng_process_display_jhdr (mng_datap pData); +mng_retcode mng_process_display_jdaa (mng_datap pData); +mng_retcode mng_process_display_jdat (mng_datap pData); +#endif +#ifndef MNG_NO_DELTA_PNG +mng_retcode mng_process_display_dhdr (mng_datap pData); +mng_retcode mng_process_display_prom (mng_datap pData); +mng_retcode mng_process_display_ipng (mng_datap pData); +#ifdef MNG_INCLUDE_JNG +mng_retcode mng_process_display_ijng (mng_datap pData); +#endif +mng_retcode mng_process_display_pplt (mng_datap pData); +#endif +#ifndef MNG_SKIPCHUNK_MAGN +mng_retcode mng_process_display_magn (mng_datap pData); +mng_retcode mng_process_display_magn2 (mng_datap pData); +#endif +#ifndef MNG_SKIPCHUNK_PAST +mng_retcode mng_process_display_past (mng_datap pData); +mng_retcode mng_process_display_past2 (mng_datap pData); +#endif + +#endif /* MNG_OPTIMIZE_DISPLAYCALLS */ + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_DISPLAY_PROCS */ + +/* ************************************************************************** */ + +#endif /* _libmng_display_h_ */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ diff --git a/Engine/lib/lmng/libmng_dither.c b/Engine/lib/lmng/libmng_dither.c new file mode 100644 index 000000000..e23850cef --- /dev/null +++ b/Engine/lib/lmng/libmng_dither.c @@ -0,0 +1,58 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_dither.c copyright (c) 2000-2004 G.Juyn * */ +/* * version : 1.0.9 * */ +/* * * */ +/* * purpose : Dithering routines (implementation) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : implementation of the dithering routines * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed strict-ANSI stuff * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * * */ +/* * 1.0.9 - 12/20/2004 - G.Juyn * */ +/* * - cleaned up macro-invocations (thanks to D. Airlie) * */ +/* * * */ +/* ************************************************************************** */ + +#include "libmng.h" +#include "libmng_data.h" +#include "libmng_error.h" +#include "libmng_trace.h" +#ifdef __BORLANDC__ +#pragma hdrstop +#endif +#include "libmng_dither.h" + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +/* ************************************************************************** */ + +mng_retcode mng_dither_a_row (mng_datap pData, + mng_uint8p pRow) +{ + + + return MNG_NOERROR; +} + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ + + + diff --git a/Engine/lib/lmng/libmng_dither.h b/Engine/lib/lmng/libmng_dither.h new file mode 100644 index 000000000..d9217c0ca --- /dev/null +++ b/Engine/lib/lmng/libmng_dither.h @@ -0,0 +1,45 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_dither.h copyright (c) 2000-2002 G.Juyn * */ +/* * version : 1.0.5 * */ +/* * * */ +/* * purpose : Dithering routines (definition) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : Definition of the dithering routines * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed strict-ANSI stuff * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * * */ +/* ************************************************************************** */ + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +#ifndef _libmng_dither_h_ +#define _libmng_dither_h_ + +/* ************************************************************************** */ + +mng_retcode mng_dither_a_row (mng_datap pData, + mng_uint8p pRow); + +/* ************************************************************************** */ + +#endif /* _libmng_dither_h_ */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ diff --git a/Engine/lib/lmng/libmng_error.c b/Engine/lib/lmng/libmng_error.c new file mode 100644 index 000000000..3a4da2063 --- /dev/null +++ b/Engine/lib/lmng/libmng_error.c @@ -0,0 +1,326 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_error.c copyright (c) 2000-2007 G.Juyn * */ +/* * version : 1.0.10 * */ +/* * * */ +/* * purpose : Error routines (implementation) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : implementation of the general error handling routines * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed strict-ANSI stuff * */ +/* * * */ +/* * 0.5.2 - 05/23/2000 - G.Juyn * */ +/* * - added error telltaling * */ +/* * 0.5.2 - 05/30/2000 - G.Juyn * */ +/* * - added errorstrings for delta-image processing * */ +/* * 0.5.2 - 05/31/2000 - G.Juyn * */ +/* * - fixed up punctuation (contributed by Tim Rowley) * */ +/* * 0.5.2 - 06/06/2000 - G.Juyn * */ +/* * - added errorstring for delayed buffer-processing * */ +/* * * */ +/* * 0.9.1 - 07/06/2000 - G.Juyn * */ +/* * - added MNG_NEEDTIMERWAIT errorstring * */ +/* * 0.9.1 - 07/15/2000 - G.Juyn * */ +/* * - added NEEDSECTIONWAIT errorstring * */ +/* * - added macro + routine to set returncode without * */ +/* * calling error callback * */ +/* * 0.9.1 - 07/19/2000 - G.Juyn * */ +/* * - added errorstring for updatemngheader if not a MNG * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 0.9.3 - 08/09/2000 - G.Juyn * */ +/* * - added check for simplicity-bits in MHDR * */ +/* * 0.9.3 - 10/11/2000 - G.Juyn * */ +/* * - fixed processing of unknown critical chunks * */ +/* * - added support for nEED * */ +/* * 0.9.3 - 10/20/2000 - G.Juyn * */ +/* * - added errorcode for delayed delta-processing * */ +/* * * */ +/* * 0.9.4 - 01/18/2001 - G.Juyn * */ +/* * - added errorcode for MAGN methods * */ +/* * * */ +/* * 1.0.2 - 06/23/2001 - G.Juyn * */ +/* * - added optimization option for MNG-video playback * */ +/* * * */ +/* * 1.0.5 - 07/04/2002 - G.Juyn * */ +/* * - added errorcode for extreme chunk-sizes * */ +/* * 1.0.5 - 08/15/2002 - G.Juyn * */ +/* * - completed delta-image support * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * 1.0.5 - 09/14/2002 - G.Juyn * */ +/* * - added event handling for dynamic MNG * */ +/* * 1.0.5 - 09/15/2002 - G.Juyn * */ +/* * - fixed LOOP iteration=0 special case * */ +/* * 1.0.5 - 09/19/2002 - G.Juyn * */ +/* * - warnings are ignored by default now! * */ +/* * 1.0.5 - 09/20/2002 - G.Juyn * */ +/* * - added support for PAST * */ +/* * 1.0.5 - 10/07/2002 - G.Juyn * */ +/* * - added check for TERM placement during create/write * */ +/* * * */ +/* * 1.0.6 - 07/07/2003 - G. R-P * */ +/* * - added MNG_SKIPCHUNK_CHNK, MNG_NO_DELTA_PNG reductions. * */ +/* * - skipped more code when MNG_INCLUDE_JNG is not enabled. * */ +/* * 1.0.6 - 07/29/2003 - G.R-P * */ +/* * - added conditional around evNT chunk support * */ +/* * * */ +/* * 1.0.7 - 03/24/2004 - G.R-P * */ +/* * - fixed typo on SKIPCHUNK_evNT (->PAST) * */ +/* * * */ +/* * 1.0.9 - 12/20/2004 - G.Juyn * */ +/* * - cleaned up macro-invocations (thanks to D. Airlie) * */ +/* * * */ +/* * 1.0.10 - 04/08/2007 - G.Juyn * */ +/* * - added support for mPNG proposal * */ +/* * * */ +/* ************************************************************************** */ + +#include "libmng.h" +#include "libmng_data.h" +#include "libmng_error.h" +#include "libmng_trace.h" +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_ERROR_STRINGS +MNG_LOCAL mng_error_entry const error_table [] = + { + {MNG_NOERROR, "No error"}, + {MNG_OUTOFMEMORY, "Out of memory"}, + {MNG_INVALIDHANDLE, "The handle is invalid"}, + {MNG_NOCALLBACK, "A required callback is not defined"}, + {MNG_UNEXPECTEDEOF, "Encountered unexpected end-of-file"}, + {MNG_ZLIBERROR, "zlib encountered an error"}, +#ifdef MNG_INCLUDE_JNG + {MNG_JPEGERROR, "ijgsrc6b encountered an error"}, +#endif + {MNG_LCMSERROR, "lcms encountered an error"}, + {MNG_NOOUTPUTPROFILE, "No output-profile defined for CMS"}, + {MNG_NOSRGBPROFILE, "No sRGB-profile defined for CMS"}, + {MNG_BUFOVERFLOW, "Internal buffer-overflow"}, + {MNG_FUNCTIONINVALID, "Function is invalid at this point"}, + {MNG_OUTPUTERROR, "Writing was unsuccessful; disk full?"}, + {MNG_JPEGBUFTOOSMALL, "Internal buffer for JPEG processing too small"}, + {MNG_NEEDMOREDATA, "Reading suspended; waiting for I/O to catch up"}, + {MNG_NEEDTIMERWAIT, "Timer suspension; normal animation delay"}, + {MNG_NEEDSECTIONWAIT, "SEEK suspension; application decides"}, + {MNG_LOOPWITHCACHEOFF, "LOOP encountered when playback cache is turned off"}, + + {MNG_APPIOERROR, "Application signalled I/O error"}, + {MNG_APPTIMERERROR, "Application signalled timing error"}, + {MNG_APPCMSERROR, "Application signalled CMS error"}, + {MNG_APPMISCERROR, "Application signalled an error"}, + {MNG_APPTRACEABORT, "Application signalled error during trace-callback"}, + + {MNG_INTERNALERROR, "Internal error in libmng"}, + + {MNG_INVALIDSIG, "The signature is invalid"}, + {MNG_INVALIDCRC, "The CRC for this chunk is invalid"}, + {MNG_INVALIDLENGTH, "Chunk-length is invalid"}, + {MNG_SEQUENCEERROR, "Chunk out of sequence"}, + {MNG_CHUNKNOTALLOWED, "Chunk not allowed at this point"}, + {MNG_MULTIPLEERROR, "Chunk cannot occur multiple times"}, + {MNG_PLTEMISSING, "Missing PLTE chunk"}, + {MNG_IDATMISSING, "Missing IDAT chunk(s)"}, + {MNG_CANNOTBEEMPTY, "Chunk cannot be empty"}, + {MNG_GLOBALLENGTHERR, "Global data length invalid"}, + {MNG_INVALIDBITDEPTH, "The bit_depth is invalid"}, + {MNG_INVALIDCOLORTYPE, "The color_type is invalid"}, + {MNG_INVALIDCOMPRESS, "The compression_method is invalid"}, + {MNG_INVALIDFILTER, "The filter_method or filter_type is invalid"}, + {MNG_INVALIDINTERLACE, "The interlace_method is invalid"}, + {MNG_NOTENOUGHIDAT, "There is not enough data in the IDAT chunk(s)"}, + {MNG_PLTEINDEXERROR, "Palette-index out of bounds"}, + {MNG_NULLNOTFOUND, "NULL separator not found"}, + {MNG_KEYWORDNULL, "Keyword cannot be zero-length"}, + {MNG_OBJECTUNKNOWN, "Object does not exist"}, + {MNG_OBJECTEXISTS, "Object already exists"}, + {MNG_TOOMUCHIDAT, "Too much data in IDAT chunk(s)"}, + {MNG_INVSAMPLEDEPTH, "The sample_depth is invalid"}, + {MNG_INVOFFSETSIZE, "The offset_type is invalid"}, + {MNG_INVENTRYTYPE, "The entry_type is invalid"}, + {MNG_ENDWITHNULL, "Chunk must not end with NULL byte"}, + {MNG_INVIMAGETYPE, "The image_type is invalid"}, +#ifndef MNG_NO_DELTA_PNG + {MNG_INVDELTATYPE, "The delta_type is invalid"}, +#endif + {MNG_INVALIDINDEX, "Index-value out of bounds"}, +#ifdef MNG_INCLUDE_JNG + {MNG_TOOMUCHJDAT, "Too much data in JDAT chunk(s)"}, + {MNG_JPEGPARMSERR, "JHDR parameters & JFIF-data do not match"}, +#endif + {MNG_INVFILLMETHOD, "The fill_method is invalid"}, +#ifndef MNG_NO_DELTA_PNG + {MNG_OBJNOTCONCRETE, "Target object for DHDR must be concrete"}, +#endif + {MNG_TARGETNOALPHA, "Target object must have alpha-channel"}, + {MNG_MNGTOOCOMPLEX, "MHDR simplicity indicates unsupported feature(s)"}, + {MNG_UNKNOWNCRITICAL, "Unknown critical chunk encountered"}, +#ifndef MNG_SKIPCHUNK_nEED + {MNG_UNSUPPORTEDNEED, "Requested nEED resources are not supported"}, +#endif + {MNG_INVALIDDELTA, "The delta operation is invalid (mismatched color_types?)"}, + {MNG_INVALIDMETHOD, "Method is invalid"}, + {MNG_IMPROBABLELENGTH, "Chunklength is incredibly large"}, + {MNG_INVALIDBLOCK, "Delta block width and or height invalid"}, + {MNG_INVALIDEVENT, "Event type is invalid"}, + {MNG_INVALIDMASK, "Mask type is invalid"}, + {MNG_NOMATCHINGLOOP, "ENDL without matching LOOP"}, +#ifndef MNG_SKIPCHUNK_evNT + {MNG_SEEKNOTFOUND, "evNT points to unknown SEEK"}, +#endif +#ifndef MNG_SKIPCHUNK_PAST + {MNG_OBJNOTABSTRACT, "Destination object for PAST must be abstract"}, +#endif + {MNG_TERMSEQERROR, "TERM misplaced during creation of MNG stream"}, + {MNG_INVALIDFIELDVAL, "invalid fieldvalue (generic)"}, + {MNG_INVALIDWIDTH, "invalid frame/image width"}, + {MNG_INVALIDHEIGHT, "invalid frame/image height"}, + + {MNG_INVALIDCNVSTYLE, "Canvas_style is invalid"}, + {MNG_WRONGCHUNK, "Attempt to access the wrong chunk"}, + {MNG_INVALIDENTRYIX, "Attempt to access an non-existing entry"}, + {MNG_NOHEADER, "No valid header-chunk"}, + {MNG_NOCORRCHUNK, "Parent chunk not found"}, + {MNG_NOMHDR, "No MNG header (MHDR) found"}, + + {MNG_IMAGETOOLARGE, "Image is larger than defined maximum"}, + {MNG_NOTANANIMATION, "Image is not an animation"}, + {MNG_FRAMENRTOOHIGH, "Framenr out of bounds"}, + {MNG_LAYERNRTOOHIGH, "Layernr out of bounds"}, + {MNG_PLAYTIMETOOHIGH, "Playtime out of bounds"}, + {MNG_FNNOTIMPLEMENTED, "Function not yet implemented"}, + {MNG_IMAGEFROZEN, "Image is frozen"}, + + {MNG_LCMS_NOHANDLE, "Handle could not be initialized"}, + {MNG_LCMS_NOMEM, "No memory for gamma-table(s)"}, + {MNG_LCMS_NOTRANS, "Transformation could not be initialized"} + }; +#endif /* MNG_INCLUDE_ERROR_STRINGS */ + +/* ************************************************************************** */ + +mng_bool mng_store_error (mng_datap pData, + mng_retcode iError, + mng_retcode iExtra1, + mng_retcode iExtra2) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEB (pData, MNG_FN_STORE_ERROR, MNG_LC_START); +#endif + + if (pData != 0) + { + pData->iErrorcode = iError; /* save also for getlasterror */ + pData->iErrorx1 = iExtra1; + pData->iErrorx2 = iExtra2; + +#ifdef MNG_INCLUDE_ERROR_STRINGS + { /* binary search variables */ + mng_int32 iTop, iLower, iUpper, iMiddle; + mng_error_entryp pEntry; /* pointer to found entry */ + /* determine max index of table */ + iTop = (sizeof (error_table) / sizeof (error_table [0])) - 1; + + iLower = 0; /* initialize binary search */ + iMiddle = iTop >> 1; /* start in the middle */ + iUpper = iTop; + pEntry = 0; /* no goods yet! */ + + do /* the binary search itself */ + { + if (error_table [iMiddle].iError < iError) + iLower = iMiddle + 1; + else if (error_table [iMiddle].iError > iError) + iUpper = iMiddle - 1; + else + { + pEntry = &error_table [iMiddle]; + break; + } + + iMiddle = (iLower + iUpper) >> 1; + } + while (iLower <= iUpper); + + if (pEntry) /* found it ? */ + pData->zErrortext = pEntry->zErrortext; + else + pData->zErrortext = "Unknown error"; + } +#else /* MNG_INCLUDE_ERROR_STRINGS */ + pData->zErrortext = 0; +#endif /* MNG_INCLUDE_ERROR_STRINGS */ + + if (iError == 0) /* no error is not severe ! */ + { + pData->iSeverity = 0; + } + else + { + switch (iError&0x3C00) /* determine the severity */ + { + case 0x0800 : { pData->iSeverity = 5; break; } + case 0x1000 : { pData->iSeverity = 2; break; } + case 0x2000 : { pData->iSeverity = 1; break; } + default : { pData->iSeverity = 9; } + } + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEB (pData, MNG_FN_STORE_ERROR, MNG_LC_END); +#endif + + return MNG_TRUE; +} + +/* ************************************************************************** */ + +mng_bool mng_process_error (mng_datap pData, + mng_retcode iError, + mng_retcode iExtra1, + mng_retcode iExtra2) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEB (pData, MNG_FN_PROCESS_ERROR, MNG_LC_START); +#endif + + mng_store_error (pData, iError, iExtra1, iExtra2); + + if ((pData != MNG_NULL) && (pData->iMagic == MNG_MAGIC)) + { + if (pData->fErrorproc) /* callback defined ? */ + return pData->fErrorproc (((mng_handle)pData), iError, pData->iSeverity, + pData->iChunkname, pData->iChunkseq, + pData->iErrorx1, pData->iErrorx2, pData->zErrortext); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEB (pData, MNG_FN_PROCESS_ERROR, MNG_LC_END); +#endif + + return MNG_TRUE; /* warnings are ignored by default ! */ +} + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ + diff --git a/Engine/lib/lmng/libmng_error.h b/Engine/lib/lmng/libmng_error.h new file mode 100644 index 000000000..b49ff7330 --- /dev/null +++ b/Engine/lib/lmng/libmng_error.h @@ -0,0 +1,119 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_error.h copyright (c) 2000-2002 G.Juyn * */ +/* * version : 1.0.5 * */ +/* * * */ +/* * purpose : Error functions (definition) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : Definition of the generic error-codes and functions * */ +/* * * */ +/* * changes : 0.5.1 - 05/06/2000 - G.Juyn * */ +/* * - added some errorcodes * */ +/* * 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - added some errorcodes * */ +/* * - changed strict-ANSI stuff * */ +/* * 0.5.1 - 05/11/2000 - G.Juyn * */ +/* * - added application errorcodes (used with callbacks) * */ +/* * - moved chunk-access errorcodes to severity 5 * */ +/* * * */ +/* * 0.5.2 - 05/20/2000 - G.Juyn * */ +/* * - added JNG errorcodes * */ +/* * 0.5.2 - 05/23/2000 - G.Juyn * */ +/* * - added error tell-tale definition * */ +/* * 0.5.2 - 05/30/2000 - G.Juyn * */ +/* * - added errorcodes for delta-image processing * */ +/* * 0.5.2 - 06/06/2000 - G.Juyn * */ +/* * - added errorcode for delayed buffer-processing * */ +/* * - moved errorcodes to "libmng.h" * */ +/* * * */ +/* * 0.9.1 - 07/15/2000 - G.Juyn * */ +/* * - added macro + routine to set returncode without * */ +/* * calling error callback * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * 1.0.5 - 08/20/2002 - G.Juyn * */ +/* * - added option for soft-handling of errors * */ +/* * * */ +/* ************************************************************************** */ + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +#ifndef _libmng_error_h_ +#define _libmng_error_h_ + +/* ************************************************************************** */ +/* * * */ +/* * Default error routines * */ +/* * * */ +/* ************************************************************************** */ + +mng_bool mng_store_error (mng_datap pData, + mng_retcode iError, + mng_retcode iExtra1, + mng_retcode iExtra2); + +mng_bool mng_process_error (mng_datap pData, + mng_retcode iError, + mng_retcode iExtra1, + mng_retcode iExtra2); + +/* ************************************************************************** */ +/* * * */ +/* * Error handling macros * */ +/* * * */ +/* ************************************************************************** */ + +#ifdef MNG_SOFTERRORS +#define MNG_ERROR(D,C) { if (!mng_process_error (D, C, 0, 0)) return C; } +#define MNG_ERRORZ(D,Z) { if (!mng_process_error (D, MNG_ZLIBERROR, Z, 0)) return MNG_ZLIBERROR; } +#define MNG_ERRORJ(D,J) { if (!mng_process_error (D, MNG_JPEGERROR, J, 0)) return MNG_JPEGERROR; } +#define MNG_ERRORL(D,L) { if (!mng_process_error (D, MNG_LCMSERROR, L, 0)) return MNG_LCMSERROR; } +#else +#define MNG_ERROR(D,C) { mng_process_error (D, C, 0, 0); return C; } +#define MNG_ERRORZ(D,Z) { mng_process_error (D, MNG_ZLIBERROR, Z, 0); return MNG_ZLIBERROR; } +#define MNG_ERRORJ(D,J) { mng_process_error (D, MNG_JPEGERROR, J, 0); return MNG_JPEGERROR; } +#define MNG_ERRORL(D,L) { mng_process_error (D, MNG_LCMSERROR, L, 0); return MNG_LCMSERROR; } +#endif + +#define MNG_RETURN(D,C) { mng_store_error (D, C, 0, 0); return C; } + +#define MNG_WARNING(D,C) { if (!mng_process_error (D, C, 0, 0)) return C; } + +#define MNG_VALIDHANDLE(H) { if ((H == 0) || (((mng_datap)H)->iMagic != MNG_MAGIC)) \ + return MNG_INVALIDHANDLE; } +#define MNG_VALIDHANDLEX(H) { if ((H == 0) || (((mng_datap)H)->iMagic != MNG_MAGIC)) \ + return 0; } +#define MNG_VALIDCB(D,C) { if (!((mng_datap)D)->C) \ + MNG_ERROR (((mng_datap)D), MNG_NOCALLBACK) } + +/* ************************************************************************** */ +/* * * */ +/* * Error string-table entry * */ +/* * * */ +/* ************************************************************************** */ + +typedef struct { + mng_retcode iError; + mng_pchar zErrortext; + } mng_error_entry; +typedef mng_error_entry const * mng_error_entryp; + +/* ************************************************************************** */ + +#endif /* _libmng_error_h_ */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ diff --git a/Engine/lib/lmng/libmng_filter.c b/Engine/lib/lmng/libmng_filter.c new file mode 100644 index 000000000..ed69a7534 --- /dev/null +++ b/Engine/lib/lmng/libmng_filter.c @@ -0,0 +1,978 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_filter.c copyright (c) 2000-2004 G.Juyn * */ +/* * version : 1.0.9 * */ +/* * * */ +/* * purpose : Filtering routines (implementation) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : implementation of the filtering routines * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed strict-ANSI stuff * */ +/* * 0.5.1 - 05/12/2000 - G.Juyn * */ +/* * - changed trace to macro for callback error-reporting * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 0.9.3 - 09/07/2000 - G.Juyn * */ +/* * - added support for new filter_types * */ +/* * * */ +/* * 1.0.5 - 08/07/2002 - G.Juyn * */ +/* * - added test-option for PNG filter method 193 (=no filter) * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * * */ +/* * 1.0.6 - 07/07/2003 - G.R-P * */ +/* * - reversed some loops to use decrementing counter * */ +/* * * */ +/* * 1.0.9 - 12/20/2004 - G.Juyn * */ +/* * - cleaned up macro-invocations (thanks to D. Airlie) * */ +/* * * */ +/* ************************************************************************** */ + +#include "libmng.h" +#include "libmng_data.h" +#include "libmng_error.h" +#include "libmng_trace.h" +#ifdef __BORLANDC__ +#pragma hdrstop +#endif +#include "libmng_filter.h" + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_FILTERS + +/* ************************************************************************** */ + +MNG_LOCAL mng_retcode filter_sub (mng_datap pData) +{ + mng_uint32 iBpp; + mng_uint8p pRawx; + mng_uint8p pRawx_prev; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FILTER_SUB, MNG_LC_START); +#endif + + iBpp = pData->iFilterbpp; + pRawx = pData->pWorkrow + pData->iPixelofs + iBpp; + pRawx_prev = pData->pWorkrow + pData->iPixelofs; + + for (iX = iBpp; iX < pData->iRowsize; iX++) + { + *pRawx = (mng_uint8)(*pRawx + *pRawx_prev); + pRawx++; + pRawx_prev++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FILTER_SUB, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +MNG_LOCAL mng_retcode filter_up (mng_datap pData) +{ + mng_uint8p pRawx; + mng_uint8p pPriorx; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FILTER_UP, MNG_LC_START); +#endif + + pRawx = pData->pWorkrow + pData->iPixelofs; + pPriorx = pData->pPrevrow + pData->iPixelofs; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsize - 1; iX >= 0; iX--) +#else + for (iX = 0; iX < pData->iRowsize; iX++) +#endif + { + *pRawx = (mng_uint8)(*pRawx + *pPriorx); + pRawx++; + pPriorx++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FILTER_UP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +MNG_LOCAL mng_retcode filter_average (mng_datap pData) +{ + mng_int32 iBpp; + mng_uint8p pRawx; + mng_uint8p pRawx_prev; + mng_uint8p pPriorx; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FILTER_AVERAGE, MNG_LC_START); +#endif + + iBpp = pData->iFilterbpp; + pRawx = pData->pWorkrow + pData->iPixelofs; + pPriorx = pData->pPrevrow + pData->iPixelofs; + pRawx_prev = pData->pWorkrow + pData->iPixelofs; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = iBpp - 1; iX >= 0; iX--) +#else + for (iX = 0; iX < iBpp; iX++) +#endif + { + *pRawx = (mng_uint8)(*pRawx + ((*pPriorx) >> 1)); + pRawx++; + pPriorx++; + } + + for (iX = iBpp; iX < pData->iRowsize; iX++) + { + *pRawx = (mng_uint8)(*pRawx + ((*pRawx_prev + *pPriorx) >> 1)); + pRawx++; + pPriorx++; + pRawx_prev++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FILTER_AVERAGE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +MNG_LOCAL mng_retcode filter_paeth (mng_datap pData) +{ + mng_int32 iBpp; + mng_uint8p pRawx; + mng_uint8p pRawx_prev; + mng_uint8p pPriorx; + mng_uint8p pPriorx_prev; + mng_int32 iX; + mng_uint32 iA, iB, iC; + mng_uint32 iP; + mng_uint32 iPa, iPb, iPc; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FILTER_PAETH, MNG_LC_START); +#endif + + iBpp = pData->iFilterbpp; + pRawx = pData->pWorkrow + pData->iPixelofs; + pPriorx = pData->pPrevrow + pData->iPixelofs; + pRawx_prev = pData->pWorkrow + pData->iPixelofs; + pPriorx_prev = pData->pPrevrow + pData->iPixelofs; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = iBpp - 1; iX >= 0; iX--) +#else + for (iX = 0; iX < iBpp; iX++) +#endif + { + *pRawx = (mng_uint8)(*pRawx + *pPriorx); + + pRawx++; + pPriorx++; + } + + for (iX = iBpp; iX < pData->iRowsize; iX++) + { + iA = (mng_uint32)*pRawx_prev; + iB = (mng_uint32)*pPriorx; + iC = (mng_uint32)*pPriorx_prev; + iP = iA + iB - iC; + iPa = abs (iP - iA); + iPb = abs (iP - iB); + iPc = abs (iP - iC); + + if ((iPa <= iPb) && (iPa <= iPc)) + *pRawx = (mng_uint8)(*pRawx + iA); + else + if (iPb <= iPc) + *pRawx = (mng_uint8)(*pRawx + iB); + else + *pRawx = (mng_uint8)(*pRawx + iC); + + pRawx++; + pPriorx++; + pRawx_prev++; + pPriorx_prev++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FILTER_PAETH, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_filter_a_row (mng_datap pData) +{ + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FILTER_A_ROW, MNG_LC_START); +#endif + + switch (*(pData->pWorkrow + pData->iFilterofs)) + { + case 1 : { + iRetcode = filter_sub (pData); + break; + } + case 2 : { + iRetcode = filter_up (pData); + break; + } + case 3 : { + iRetcode = filter_average (pData); + break; + } + case 4 : { + iRetcode = filter_paeth (pData); + break; + } + + default : iRetcode = MNG_INVALIDFILTER; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FILTER_A_ROW, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ +/* ************************************************************************** */ + +#ifdef FILTER192 +mng_retcode mng_init_rowdiffering (mng_datap pData) +{ + mng_uint8p pRawi, pRawo; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_ROWDIFFERING, MNG_LC_START); +#endif + + if (pData->iFilter == 0xC0) /* has leveling parameters ? */ + { + switch (pData->iColortype) /* salvage leveling parameters */ + { + case 0 : { /* gray */ + if (pData->iBitdepth <= 8) + pData->iLevel0 = (mng_uint16)*pData->pWorkrow; + else + pData->iLevel0 = mng_get_uint16 (pData->pWorkrow); + + break; + } + case 2 : { /* rgb */ + if (pData->iBitdepth <= 8) + { + pData->iLevel0 = (mng_uint16)*pData->pWorkrow; + pData->iLevel1 = (mng_uint16)*(pData->pWorkrow+1); + pData->iLevel2 = (mng_uint16)*(pData->pWorkrow+2); + } + else + { + pData->iLevel0 = mng_get_uint16 (pData->pWorkrow); + pData->iLevel1 = mng_get_uint16 (pData->pWorkrow+2); + pData->iLevel2 = mng_get_uint16 (pData->pWorkrow+4); + } + + break; + } + case 3 : { /* indexed */ + pData->iLevel0 = (mng_uint16)*pData->pWorkrow; + break; + } + case 4 : { /* gray+alpha */ + if (pData->iBitdepth <= 8) + { + pData->iLevel0 = (mng_uint16)*pData->pWorkrow; + pData->iLevel1 = (mng_uint16)*(pData->pWorkrow+1); + } + else + { + pData->iLevel0 = mng_get_uint16 (pData->pWorkrow); + pData->iLevel1 = mng_get_uint16 (pData->pWorkrow+2); + } + + break; + } + case 6 : { /* rgb+alpha */ + if (pData->iBitdepth <= 8) + { + pData->iLevel0 = (mng_uint16)*pData->pWorkrow; + pData->iLevel1 = (mng_uint16)*(pData->pWorkrow+1); + pData->iLevel2 = (mng_uint16)*(pData->pWorkrow+2); + pData->iLevel3 = (mng_uint16)*(pData->pWorkrow+3); + } + else + { + pData->iLevel0 = mng_get_uint16 (pData->pWorkrow); + pData->iLevel1 = mng_get_uint16 (pData->pWorkrow+2); + pData->iLevel2 = mng_get_uint16 (pData->pWorkrow+4); + pData->iLevel3 = mng_get_uint16 (pData->pWorkrow+6); + } + + break; + } + } + } + /* shift the entire row back in place */ + pRawi = pData->pWorkrow + pData->iFilterofs; + pRawo = pData->pWorkrow; + + for (iX = 0; iX < pData->iRowsize + pData->iPixelofs - pData->iFilterofs; iX++) + *pRawo++ = *pRawi++; + + pData->iFilterofs = 0; /* indicate so ! */ + +#ifdef FILTER193 + if (pData->iFilter == 0xC1) /* no adaptive filtering ? */ + pData->iPixelofs = pData->iFilterofs; + else +#endif + pData->iPixelofs = pData->iFilterofs + 1; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_ROWDIFFERING, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_differ_g1 (mng_datap pData) +{ + mng_uint8p pRawi, pRawo; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_G1, MNG_LC_START); +#endif + + if (pData->iLevel0 & 0x01) /* is it uneven level ? */ + { + pRawi = pData->pWorkrow + pData->iPixelofs; + pRawo = pData->pPrevrow + pData->iPixelofs; + /* just invert every bit */ +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsize - 1; iX >= 0; iX--) +#else + for (iX = 0; iX < pData->iRowsize; iX++) +#endif + *pRawo++ = (mng_uint8)(~(*pRawi++)); + + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_G1, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_differ_g2 (mng_datap pData) +{ + mng_uint8p pRawi, pRawo; + mng_int32 iX; + mng_int32 iC, iS; + mng_uint8 iB, iN, iQ; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_G2, MNG_LC_START); +#endif + + pRawi = pData->pWorkrow + pData->iPixelofs; + pRawo = pData->pPrevrow + pData->iPixelofs; + iC = 0; + iB = 0; + iN = 0; + iS = 0; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples - 1; iX >= 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iC) + { + iC = 4; + iB = *pRawi++; + iN = 0; + iS = 8; + } + + iS -= 2; + iQ = (mng_uint8)(((iB >> iS) + pData->iLevel0) & 0x03); + iN = (mng_uint8)((iN << 2) + iQ); + iC--; + + if (!iC) + *pRawo++ = iN; + + } + + if (iC) + *pRawo = (mng_uint8)(iN << iS); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_G2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_differ_g4 (mng_datap pData) +{ + mng_uint8p pRawi, pRawo; + mng_int32 iX; + mng_int32 iC, iS; + mng_uint8 iB, iN, iQ; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_G4, MNG_LC_START); +#endif + + pRawi = pData->pWorkrow + pData->iPixelofs; + pRawo = pData->pPrevrow + pData->iPixelofs; + iC = 0; + iB = 0; + iN = 0; + iS = 0; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples - 1; iX >= 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iC) + { + iC = 2; + iB = *pRawi++; + iN = 0; + iS = 8; + } + + iS -= 4; + iQ = (mng_uint8)(((iB >> iS) + pData->iLevel0) & 0x0F); + iN = (mng_uint8)((iN << 4) + iQ); + iC--; + + if (!iC) + *pRawo++ = iN; + + } + + if (iC) + *pRawo = (mng_uint8)(iN << iS); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_G4, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_differ_g8 (mng_datap pData) +{ + mng_uint8p pRawi, pRawo; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_G8, MNG_LC_START); +#endif + + pRawi = pData->pWorkrow + pData->iPixelofs; + pRawo = pData->pPrevrow + pData->iPixelofs; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples - 1; iX >= 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pRawo++ = (mng_uint8)(((mng_uint16)*pRawi + pData->iLevel0) & 0xFF); + + pRawi++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_G8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_differ_g16 (mng_datap pData) +{ + mng_uint16p pRawi, pRawo; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_G16, MNG_LC_START); +#endif + + pRawi = (mng_uint16p)(pData->pWorkrow + pData->iPixelofs); + pRawo = (mng_uint16p)(pData->pPrevrow + pData->iPixelofs); + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples - 1; iX >= 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pRawo++ = (mng_uint16)(((mng_uint32)*pRawi + (mng_uint32)pData->iLevel0) & 0xFFFF); + + pRawi++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_G16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_differ_rgb8 (mng_datap pData) +{ + mng_uint8p pRawi, pRawo; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_RGB8, MNG_LC_START); +#endif + + pRawi = pData->pWorkrow + pData->iPixelofs; + pRawo = pData->pPrevrow + pData->iPixelofs; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples - 1; iX >= 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *(pRawo+1) = (mng_uint8)(((mng_uint16)*(pRawi+1) + pData->iLevel1) & 0xFF); + *pRawo = (mng_uint8)(((mng_uint16)*pRawi + pData->iLevel0 + + (mng_uint16)*(pRawo+1)) & 0xFF); + *(pRawo+2) = (mng_uint8)(((mng_uint16)*(pRawi+2) + pData->iLevel2 + + (mng_uint16)*(pRawo+1)) & 0xFF); + + pRawi += 3; + pRawo += 3; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_RGB8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_differ_rgb16 (mng_datap pData) +{ + mng_uint16p pRawi, pRawo; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_RGB16, MNG_LC_START); +#endif + + pRawi = (mng_uint16p)(pData->pWorkrow + pData->iPixelofs); + pRawo = (mng_uint16p)(pData->pPrevrow + pData->iPixelofs); + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples - 1; iX >= 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *(pRawo+1) = (mng_uint16)(((mng_uint32)*(pRawi+1) + (mng_uint32)pData->iLevel1) & 0xFFFF); + *pRawo = (mng_uint16)(((mng_uint32)*pRawi + (mng_uint32)pData->iLevel0 + + (mng_uint32)*(pRawo+1)) & 0xFFFF); + *(pRawo+2) = (mng_uint16)(((mng_uint32)*(pRawi+2) + (mng_uint32)pData->iLevel2 + + (mng_uint32)*(pRawo+1)) & 0xFFFF); + + pRawi += 3; + pRawo += 3; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_RGB16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_differ_idx1 (mng_datap pData) +{ + mng_uint8p pRawi, pRawo; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_IDX1, MNG_LC_START); +#endif + + if (pData->iLevel0 & 0x01) /* is it uneven level ? */ + { + pRawi = pData->pWorkrow + pData->iPixelofs; + pRawo = pData->pPrevrow + pData->iPixelofs; + /* just invert every bit */ +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsize - 1; iX >= 0; iX--) +#else + for (iX = 0; iX < pData->iRowsize; iX++) +#endif + *pRawo++ = (mng_uint8)(~(*pRawi++)); + + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_IDX1, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_differ_idx2 (mng_datap pData) +{ + mng_uint8p pRawi, pRawo; + mng_int32 iX; + mng_int32 iC, iS; + mng_uint8 iB, iN, iQ; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_IDX2, MNG_LC_START); +#endif + + pRawi = pData->pWorkrow + pData->iPixelofs; + pRawo = pData->pPrevrow + pData->iPixelofs; + iC = 0; + iB = 0; + iN = 0; + iS = 0; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples - 1; iX >= 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iC) + { + iC = 4; + iB = *pRawi++; + iN = 0; + iS = 8; + } + + iS -= 2; + iQ = (mng_uint8)(((iB >> iS) + pData->iLevel0) & 0x03); + iN = (mng_uint8)((iN << 2) + iQ); + iC--; + + if (!iC) + *pRawo++ = iN; + + } + + if (iC) + *pRawo = (mng_uint8)(iN << iS); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_IDX2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_differ_idx4 (mng_datap pData) +{ + mng_uint8p pRawi, pRawo; + mng_int32 iX; + mng_int32 iC, iS; + mng_uint8 iB, iN, iQ; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_IDX4, MNG_LC_START); +#endif + + pRawi = pData->pWorkrow + pData->iPixelofs; + pRawo = pData->pPrevrow + pData->iPixelofs; + iC = 0; + iB = 0; + iN = 0; + iS = 0; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples - 1; iX >= 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iC) + { + iC = 2; + iB = *pRawi++; + iN = 0; + iS = 8; + } + + iS -= 4; + iQ = (mng_uint8)(((iB >> iS) + pData->iLevel0) & 0x0F); + iN = (mng_uint8)((iN << 4) + iQ); + iC--; + + if (!iC) + *pRawo++ = iN; + + } + + if (iC) + *pRawo = (mng_uint8)(iN << iS); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_IDX4, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_differ_idx8 (mng_datap pData) +{ + mng_uint8p pRawi, pRawo; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_IDX8, MNG_LC_START); +#endif + + pRawi = pData->pWorkrow + pData->iPixelofs; + pRawo = pData->pPrevrow + pData->iPixelofs; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples - 1; iX >= 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pRawo++ = (mng_uint8)(((mng_uint16)*pRawi + pData->iLevel0) & 0xFF); + + pRawi++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_IDX8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_differ_ga8 (mng_datap pData) +{ + mng_uint8p pRawi, pRawo; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_GA8, MNG_LC_START); +#endif + + pRawi = pData->pWorkrow + pData->iPixelofs; + pRawo = pData->pPrevrow + pData->iPixelofs; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples - 1; iX >= 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pRawo = (mng_uint8)(((mng_uint16)*pRawi + pData->iLevel0) & 0xFF); + *(pRawo+1) = (mng_uint8)(((mng_uint16)*(pRawi+1) + pData->iLevel1) & 0xFF); + + pRawi += 2; + pRawo += 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_GA8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_differ_ga16 (mng_datap pData) +{ + mng_uint16p pRawi, pRawo; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_GA16, MNG_LC_START); +#endif + + pRawi = (mng_uint16p)(pData->pWorkrow + pData->iPixelofs); + pRawo = (mng_uint16p)(pData->pPrevrow + pData->iPixelofs); + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples - 1; iX >= 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pRawo = (mng_uint16)(((mng_uint32)*pRawi + (mng_uint32)pData->iLevel0) & 0xFFFF); + *(pRawo+1) = (mng_uint16)(((mng_uint32)*(pRawi+1) + (mng_uint32)pData->iLevel1) & 0xFFFF); + + pRawi += 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_GA16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_differ_rgba8 (mng_datap pData) +{ + mng_uint8p pRawi, pRawo; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_RGBA8, MNG_LC_START); +#endif + + pRawi = pData->pWorkrow + pData->iPixelofs; + pRawo = pData->pPrevrow + pData->iPixelofs; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples - 1; iX >= 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *(pRawo+1) = (mng_uint8)(((mng_uint16)*(pRawi+1) + pData->iLevel1) & 0xFF); + *pRawo = (mng_uint8)(((mng_uint16)*pRawi + pData->iLevel0 + + (mng_uint16)*(pRawo+1)) & 0xFF); + *(pRawo+2) = (mng_uint8)(((mng_uint16)*(pRawi+2) + pData->iLevel2 + + (mng_uint16)*(pRawo+1)) & 0xFF); + *(pRawo+3) = (mng_uint8)(((mng_uint16)*(pRawi+3) + pData->iLevel3) & 0xFF); + + pRawi += 4; + pRawo += 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_RGBA8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_differ_rgba16 (mng_datap pData) +{ + mng_uint16p pRawi, pRawo; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_RGBA16, MNG_LC_START); +#endif + + pRawi = (mng_uint16p)(pData->pWorkrow + pData->iPixelofs); + pRawo = (mng_uint16p)(pData->pPrevrow + pData->iPixelofs); + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples - 1; iX >= 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *(pRawo+1) = (mng_uint16)(((mng_uint32)*(pRawi+1) + (mng_uint32)pData->iLevel1) & 0xFFFF); + *pRawo = (mng_uint16)(((mng_uint32)*pRawi + (mng_uint32)pData->iLevel0 + + (mng_uint32)*(pRawo+1)) & 0xFFFF); + *(pRawo+2) = (mng_uint16)(((mng_uint32)*(pRawi+2) + (mng_uint32)pData->iLevel2 + + (mng_uint32)*(pRawo+1)) & 0xFFFF); + *(pRawo+3) = (mng_uint16)(((mng_uint32)*(pRawi+3) + (mng_uint32)pData->iLevel3) & 0xFFFF); + + pRawi += 4; + pRawo += 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DIFFER_RGBA16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#endif /* FILTER192 */ + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_FILTERS */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ + diff --git a/Engine/lib/lmng/libmng_filter.h b/Engine/lib/lmng/libmng_filter.h new file mode 100644 index 000000000..9ac9c7f99 --- /dev/null +++ b/Engine/lib/lmng/libmng_filter.h @@ -0,0 +1,69 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_filter.h copyright (c) 2000-2002 G.Juyn * */ +/* * version : 1.0.5 * */ +/* * * */ +/* * purpose : Filtering routines (definition) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : Definition of the filtering routines * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed strict-ANSI stuff * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 0.9.3 - 09/07/2000 - G.Juyn * */ +/* * - added support for new filter_types * */ +/* * * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * * */ +/* ************************************************************************** */ + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +#ifndef _libmng_filter_h_ +#define _libmng_filter_h_ + +/* ************************************************************************** */ + +mng_retcode mng_filter_a_row (mng_datap pData); + +/* ************************************************************************** */ + +#ifdef FILTER192 +mng_retcode mng_init_rowdiffering (mng_datap pData); + +mng_retcode mng_differ_g1 (mng_datap pData); +mng_retcode mng_differ_g2 (mng_datap pData); +mng_retcode mng_differ_g4 (mng_datap pData); +mng_retcode mng_differ_g8 (mng_datap pData); +mng_retcode mng_differ_g16 (mng_datap pData); +mng_retcode mng_differ_rgb8 (mng_datap pData); +mng_retcode mng_differ_rgb16 (mng_datap pData); +mng_retcode mng_differ_idx1 (mng_datap pData); +mng_retcode mng_differ_idx2 (mng_datap pData); +mng_retcode mng_differ_idx4 (mng_datap pData); +mng_retcode mng_differ_idx8 (mng_datap pData); +mng_retcode mng_differ_ga8 (mng_datap pData); +mng_retcode mng_differ_ga16 (mng_datap pData); +mng_retcode mng_differ_rgba8 (mng_datap pData); +mng_retcode mng_differ_rgba16 (mng_datap pData); +#endif + +/* ************************************************************************** */ + +#endif /* _libmng_filter_h_ */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ diff --git a/Engine/lib/lmng/libmng_hlapi.c b/Engine/lib/lmng/libmng_hlapi.c new file mode 100644 index 000000000..6199d593c --- /dev/null +++ b/Engine/lib/lmng/libmng_hlapi.c @@ -0,0 +1,2995 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_hlapi.c copyright (c) 2000-2007 G.Juyn * */ +/* * version : 1.0.10 * */ +/* * * */ +/* * purpose : high-level application API (implementation) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : implementation of the high-level function interface * */ +/* * for applications. * */ +/* * * */ +/* * changes : 0.5.1 - 05/06/2000 - G.Juyn * */ +/* * - added init of iPLTEcount * */ +/* * 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed calling-convention definition * */ +/* * - changed status-handling of display-routines * */ +/* * - added versioning-control routines * */ +/* * - filled the write routine * */ +/* * - changed strict-ANSI stuff * */ +/* * 0.5.1 - 05/11/2000 - G.Juyn * */ +/* * - added callback error-reporting support * */ +/* * 0.5.1 - 05/12/2000 - G.Juyn * */ +/* * - changed trace to macro for callback error-reporting * */ +/* * 0.5.1 - 05/13/2000 - G.Juyn * */ +/* * - added eMNGma hack (will be removed in 1.0.0 !!!) * */ +/* * - added TERM animation object pointer (easier reference) * */ +/* * 0.5.1 - 05/14/2000 - G.Juyn * */ +/* * - added cleanup of saved-data (SAVE/SEEK processing) * */ +/* * 0.5.1 - 05/16/2000 - G.Juyn * */ +/* * - moved the actual write_graphic functionality from here * */ +/* * to its appropriate function in the mng_write module * */ +/* * * */ +/* * 0.5.2 - 05/19/2000 - G.Juyn * */ +/* * - cleaned up some code regarding mixed support * */ +/* * - added JNG support * */ +/* * 0.5.2 - 05/24/2000 - G.Juyn * */ +/* * - moved init of default zlib parms here from "mng_zlib.c" * */ +/* * - added init of default IJG parms * */ +/* * 0.5.2 - 05/29/2000 - G.Juyn * */ +/* * - fixed inconsistancy with freeing global iCCP profile * */ +/* * 0.5.2 - 05/30/2000 - G.Juyn * */ +/* * - added delta-image field initialization * */ +/* * 0.5.2 - 06/06/2000 - G.Juyn * */ +/* * - added initialization of the buffer-suspend parameter * */ +/* * * */ +/* * 0.5.3 - 06/16/2000 - G.Juyn * */ +/* * - added initialization of update-region for refresh * */ +/* * - added initialization of Needrefresh parameter * */ +/* * 0.5.3 - 06/17/2000 - G.Juyn * */ +/* * - added initialization of Deltaimmediate * */ +/* * 0.5.3 - 06/21/2000 - G.Juyn * */ +/* * - added initialization of Speed * */ +/* * - added initialization of Imagelevel * */ +/* * 0.5.3 - 06/26/2000 - G.Juyn * */ +/* * - changed userdata variable to mng_ptr * */ +/* * 0.5.3 - 06/29/2000 - G.Juyn * */ +/* * - fixed initialization routine for new mng_handle type * */ +/* * * */ +/* * 0.9.1 - 07/06/2000 - G.Juyn * */ +/* * - changed mng_display_resume to allow to be called after * */ +/* * a suspension return with MNG_NEEDMOREDATA * */ +/* * - added returncode MNG_NEEDTIMERWAIT for timer breaks * */ +/* * 0.9.1 - 07/07/2000 - G.Juyn * */ +/* * - implemented support for freeze/reset/resume & go_xxxx * */ +/* * 0.9.1 - 07/08/2000 - G.Juyn * */ +/* * - added support for improved timing * */ +/* * - added support for improved I/O-suspension * */ +/* * 0.9.1 - 07/14/2000 - G.Juyn * */ +/* * - changed EOF processing behavior * */ +/* * 0.9.1 - 07/15/2000 - G.Juyn * */ +/* * - added callbacks for SAVE/SEEK processing * */ +/* * - added variable for NEEDSECTIONWAIT breaks * */ +/* * - added variable for freeze & reset processing * */ +/* * 0.9.1 - 07/17/2000 - G.Juyn * */ +/* * - added error cleanup processing * */ +/* * - fixed support for mng_display_reset() * */ +/* * - fixed suspension-buffering for 32K+ chunks * */ +/* * * */ +/* * 0.9.2 - 07/29/2000 - G.Juyn * */ +/* * - fixed small bugs in display processing * */ +/* * 0.9.2 - 07/31/2000 - G.Juyn * */ +/* * - fixed wrapping of suspension parameters * */ +/* * 0.9.2 - 08/04/2000 - G.Juyn * */ +/* * - B111096 - fixed large-buffer read-suspension * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 0.9.3 - 09/07/2000 - G.Juyn * */ +/* * - added support for new filter_types * */ +/* * 0.9.3 - 09/10/2000 - G.Juyn * */ +/* * - fixed DEFI behavior * */ +/* * 0.9.3 - 10/11/2000 - G.Juyn * */ +/* * - added support for nEED * */ +/* * 0.9.3 - 10/16/2000 - G.Juyn * */ +/* * - added optional support for bKGD for PNG images * */ +/* * - raised initial maximum canvas size * */ +/* * - added support for JDAA * */ +/* * 0.9.3 - 10/17/2000 - G.Juyn * */ +/* * - added callback to process non-critical unknown chunks * */ +/* * - fixed support for delta-images during read() / display() * */ +/* * 0.9.3 - 10/18/2000 - G.Juyn * */ +/* * - added closestream() processing for mng_cleanup() * */ +/* * 0.9.3 - 10/27/2000 - G.Juyn * */ +/* * - fixed separate read() & display() processing * */ +/* * * */ +/* * 0.9.4 - 11/20/2000 - G.Juyn * */ +/* * - fixed unwanted repetition in mng_readdisplay() * */ +/* * 0.9.4 - 11/24/2000 - G.Juyn * */ +/* * - moved restore of object 0 to libmng_display * */ +/* * * */ +/* * 1.0.1 - 02/08/2001 - G.Juyn * */ +/* * - added MEND processing callback * */ +/* * 1.0.1 - 02/13/2001 - G.Juyn * */ +/* * - fixed first FRAM_MODE=4 timing problem * */ +/* * 1.0.1 - 04/21/2001 - G.Juyn * */ +/* * - fixed bug with display_reset/display_resume (Thanks G!) * */ +/* * 1.0.1 - 04/22/2001 - G.Juyn * */ +/* * - fixed memory-leak (Thanks Gregg!) * */ +/* * 1.0.1 - 04/23/2001 - G.Juyn * */ +/* * - fixed reset_rundata to drop all objects * */ +/* * 1.0.1 - 04/25/2001 - G.Juyn * */ +/* * - moved mng_clear_cms to libmng_cms * */ +/* * * */ +/* * 1.0.2 - 06/23/2001 - G.Juyn * */ +/* * - added optimization option for MNG-video playback * */ +/* * - added processterm callback * */ +/* * 1.0.2 - 06/25/2001 - G.Juyn * */ +/* * - added option to turn off progressive refresh * */ +/* * * */ +/* * 1.0.5 - 07/08/2002 - G.Juyn * */ +/* * - B578572 - removed eMNGma hack (thanks Dimitri!) * */ +/* * 1.0.5 - 07/16/2002 - G.Juyn * */ +/* * - B581625 - large chunks fail with suspension reads * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * 1.0.5 - 09/15/2002 - G.Juyn * */ +/* * - fixed LOOP iteration=0 special case * */ +/* * 1.0.5 - 10/07/2002 - G.Juyn * */ +/* * - added another fix for misplaced TERM chunk * */ +/* * - completed support for condition=2 in TERM chunk * */ +/* * - added beta version function & constant * */ +/* * 1.0.5 - 10/11/2002 - G.Juyn * */ +/* * - added mng_status_dynamic to supports function * */ +/* * 1.0.5 - 11/04/2002 - G.Juyn * */ +/* * - changed FRAMECOUNT/LAYERCOUNT/PLAYTIME error to warning * */ +/* * 1.0.5 - 11/07/2002 - G.Juyn * */ +/* * - added support to get totals after mng_read() * */ +/* * 1.0.5 - 11/29/2002 - G.Juyn * */ +/* * - fixed goxxxxx() support for zero values * */ +/* * * */ +/* * 1.0.6 - 05/25/2003 - G.R-P * */ +/* * - added MNG_SKIPCHUNK_cHNK footprint optimizations * */ +/* * 1.0.6 - 07/11/2003 - G.R-P * */ +/* * - added conditionals zlib and jpeg property accessors * */ +/* * 1.0.6 - 07/14/2003 - G.R-P * */ +/* * - added conditionals around "mng_display_go*" and other * */ +/* * unused functions * */ +/* * 1.0.6 - 07/29/2003 - G.R-P * */ +/* * - added conditionals around PAST chunk support * */ +/* * * */ +/* * 1.0.7 - 03/07/2004 - G. Randers-Pehrson * */ +/* * - put gamma, cms-related declarations inside #ifdef * */ +/* * 1.0.7 - 03/10/2004 - G.R-P * */ +/* * - added conditionals around openstream/closestream * */ +/* * 1.0.7 - 03/24/2004 - G.R-P * */ +/* * - fixed zTXT -> zTXt typo * */ +/* * * */ +/* * 1.0.8 - 04/02/2004 - G.Juyn * */ +/* * - added CRC existence & checking flags * */ +/* * 1.0.8 - 04/10/2004 - G.Juyn * */ +/* * - added data-push mechanisms for specialized decoders * */ +/* * 1.0.8 - 07/06/2004 - G.R-P * */ +/* * - defend against using undefined openstream function * */ +/* * 1.0.8 - 08/02/2004 - G.Juyn * */ +/* * - added conditional to allow easier writing of large MNG's * */ +/* * * */ +/* * 1.0.9 - 08/17/2004 - G.R-P * */ +/* * - added more SKIPCHUNK conditionals * */ +/* * 1.0.9 - 09/25/2004 - G.Juyn * */ +/* * - replaced MNG_TWEAK_LARGE_FILES with permanent solution * */ +/* * 1.0.9 - 10/03/2004 - G.Juyn * */ +/* * - added function to retrieve current FRAM delay * */ +/* * 1.0.9 - 12/20/2004 - G.Juyn * */ +/* * - cleaned up macro-invocations (thanks to D. Airlie) * */ +/* * * */ +/* * 1.0.10 - 07/06/2005 - G.R-P * */ +/* * - added more SKIPCHUNK conditionals * */ +/* * 1.0.10 - 04/08/2007 - G.Juyn * */ +/* * - added support for mPNG proposal * */ +/* * 1.0.10 - 04/12/2007 - G.Juyn * */ +/* * - added support for ANG proposal * */ +/* * 1.0.10 - 07/06/2007 - G.R-P bugfix by Lucas Quintana * */ +/* * * */ +/* ************************************************************************** */ + +#include "libmng.h" +#include "libmng_data.h" +#include "libmng_error.h" +#include "libmng_trace.h" +#ifdef __BORLANDC__ +#pragma hdrstop +#endif +#include "libmng_objects.h" +#include "libmng_object_prc.h" +#include "libmng_chunks.h" +#include "libmng_memory.h" +#include "libmng_read.h" +#include "libmng_write.h" +#include "libmng_display.h" +#include "libmng_zlib.h" +#include "libmng_jpeg.h" +#include "libmng_cms.h" +#include "libmng_pixels.h" + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +/* ************************************************************************** */ +/* * * */ +/* * local routines * */ +/* * * */ +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +MNG_LOCAL mng_retcode mng_drop_objects (mng_datap pData, + mng_bool bDropaniobj) +{ + mng_objectp pObject; + mng_objectp pNext; + mng_cleanupobject fCleanup; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DROP_OBJECTS, MNG_LC_START); +#endif + + pObject = pData->pFirstimgobj; /* get first stored image-object (if any) */ + + while (pObject) /* more objects to discard ? */ + { + pNext = ((mng_object_headerp)pObject)->pNext; + /* call appropriate cleanup */ + fCleanup = ((mng_object_headerp)pObject)->fCleanup; + fCleanup (pData, pObject); + + pObject = pNext; /* neeeext */ + } + + pData->pFirstimgobj = MNG_NULL; /* clean this up!!! */ + pData->pLastimgobj = MNG_NULL; + + if (bDropaniobj) /* drop animation objects ? */ + { + pObject = pData->pFirstaniobj; /* get first stored animation-object (if any) */ + + while (pObject) /* more objects to discard ? */ + { + pNext = ((mng_object_headerp)pObject)->pNext; + /* call appropriate cleanup */ + fCleanup = ((mng_object_headerp)pObject)->fCleanup; + fCleanup (pData, pObject); + + pObject = pNext; /* neeeext */ + } + + pData->pFirstaniobj = MNG_NULL; /* clean this up!!! */ + pData->pLastaniobj = MNG_NULL; + +#ifdef MNG_SUPPORT_DYNAMICMNG + pObject = pData->pFirstevent; /* get first event-object (if any) */ + + while (pObject) /* more objects to discard ? */ + { + pNext = ((mng_object_headerp)pObject)->pNext; + /* call appropriate cleanup */ + fCleanup = ((mng_object_headerp)pObject)->fCleanup; + fCleanup (pData, pObject); + + pObject = pNext; /* neeeext */ + } + + pData->pFirstevent = MNG_NULL; /* clean this up!!! */ + pData->pLastevent = MNG_NULL; +#endif + } + +#ifdef MNG_INCLUDE_MPNG_PROPOSAL + if (pData->pMPNG) /* drop MPNG data (if any) */ + { + fCleanup = ((mng_object_headerp)pData->pMPNG)->fCleanup; + fCleanup (pData, pData->pMPNG); + pData->pMPNG = MNG_NULL; + } +#endif + +#ifdef MNG_INCLUDE_ANG_PROPOSAL + if (pData->pANG) /* drop ANG data (if any) */ + { + fCleanup = ((mng_object_headerp)pData->pANG)->fCleanup; + fCleanup (pData, pData->pANG); + pData->pANG = MNG_NULL; + } +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DROP_OBJECTS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +#ifndef MNG_SKIPCHUNK_SAVE +MNG_LOCAL mng_retcode mng_drop_savedata (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DROP_SAVEDATA, MNG_LC_START); +#endif + + if (pData->pSavedata) /* sanity check */ + { /* address it more directly */ + mng_savedatap pSave = pData->pSavedata; + + if (pSave->iGlobalProfilesize) /* cleanup the profile ? */ + MNG_FREEX (pData, pSave->pGlobalProfile, pSave->iGlobalProfilesize); + /* cleanup the save structure */ + MNG_FREE (pData, pData->pSavedata, sizeof (mng_savedata)); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DROP_SAVEDATA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +MNG_LOCAL mng_retcode mng_reset_rundata (mng_datap pData) +{ + mng_drop_invalid_objects (pData); /* drop invalidly stored objects */ +#ifndef MNG_SKIPCHUNK_SAVE + mng_drop_savedata (pData); /* drop stored savedata */ +#endif + mng_reset_objzero (pData); /* reset object 0 */ + /* drop stored objects (if any) */ + mng_drop_objects (pData, MNG_FALSE); + + pData->bFramedone = MNG_FALSE; + pData->iFrameseq = 0; /* reset counters & stuff */ + pData->iLayerseq = 0; + pData->iFrametime = 0; + + pData->bSkipping = MNG_FALSE; + +#ifdef MNG_SUPPORT_DYNAMICMNG + pData->bRunningevent = MNG_FALSE; + pData->bStopafterseek = MNG_FALSE; + pData->iEventx = 0; + pData->iEventy = 0; + pData->pLastmousemove = MNG_NULL; +#endif + + pData->iRequestframe = 0; + pData->iRequestlayer = 0; + pData->iRequesttime = 0; + pData->bSearching = MNG_FALSE; + + pData->iRuntime = 0; + pData->iSynctime = 0; + pData->iStarttime = 0; + pData->iEndtime = 0; + pData->bRunning = MNG_FALSE; + pData->bTimerset = MNG_FALSE; + pData->iBreakpoint = 0; + pData->bSectionwait = MNG_FALSE; + pData->bFreezing = MNG_FALSE; + pData->bResetting = MNG_FALSE; + pData->bNeedrefresh = MNG_FALSE; + pData->bOnlyfirstframe = MNG_FALSE; + pData->iFramesafterTERM = 0; + + pData->iIterations = 0; + /* start of animation objects! */ + pData->pCurraniobj = MNG_NULL; + + pData->iUpdateleft = 0; /* reset region */ + pData->iUpdateright = 0; + pData->iUpdatetop = 0; + pData->iUpdatebottom = 0; + pData->iPLTEcount = 0; /* reset PLTE data */ + +#ifndef MNG_SKIPCHUNK_DEFI + pData->iDEFIobjectid = 0; /* reset DEFI data */ + pData->bDEFIhasdonotshow = MNG_FALSE; + pData->iDEFIdonotshow = 0; + pData->bDEFIhasconcrete = MNG_FALSE; + pData->iDEFIconcrete = 0; + pData->bDEFIhasloca = MNG_FALSE; + pData->iDEFIlocax = 0; + pData->iDEFIlocay = 0; + pData->bDEFIhasclip = MNG_FALSE; + pData->iDEFIclipl = 0; + pData->iDEFIclipr = 0; + pData->iDEFIclipt = 0; + pData->iDEFIclipb = 0; +#endif + +#ifndef MNG_SKIPCHUNK_BACK + pData->iBACKred = 0; /* reset BACK data */ + pData->iBACKgreen = 0; + pData->iBACKblue = 0; + pData->iBACKmandatory = 0; + pData->iBACKimageid = 0; + pData->iBACKtile = 0; +#endif + +#ifndef MNG_SKIPCHUNK_FRAM + pData->iFRAMmode = 1; /* default global FRAM variables */ + pData->iFRAMdelay = 1; + pData->iFRAMtimeout = 0x7fffffffl; + pData->bFRAMclipping = MNG_FALSE; + pData->iFRAMclipl = 0; + pData->iFRAMclipr = 0; + pData->iFRAMclipt = 0; + pData->iFRAMclipb = 0; + + pData->iFramemode = 1; /* again for the current frame */ + pData->iFramedelay = 1; + pData->iFrametimeout = 0x7fffffffl; + pData->bFrameclipping = MNG_FALSE; + pData->iFrameclipl = 0; + pData->iFrameclipr = 0; + pData->iFrameclipt = 0; + pData->iFrameclipb = 0; + + pData->iNextdelay = 1; +#endif + +#ifndef MNG_SKIPCHUNK_SHOW + pData->iSHOWmode = 0; /* reset SHOW data */ + pData->iSHOWfromid = 0; + pData->iSHOWtoid = 0; + pData->iSHOWnextid = 0; + pData->iSHOWskip = 0; +#endif + + pData->iGlobalPLTEcount = 0; /* reset global PLTE data */ + + pData->iGlobalTRNSrawlen = 0; /* reset global tRNS data */ + + pData->iGlobalGamma = 0; /* reset global gAMA data */ + +#ifndef MNG_SKIPCHUNK_cHRM + pData->iGlobalWhitepointx = 0; /* reset global cHRM data */ + pData->iGlobalWhitepointy = 0; + pData->iGlobalPrimaryredx = 0; + pData->iGlobalPrimaryredy = 0; + pData->iGlobalPrimarygreenx = 0; + pData->iGlobalPrimarygreeny = 0; + pData->iGlobalPrimarybluex = 0; + pData->iGlobalPrimarybluey = 0; +#endif + +#ifndef MNG_SKIPCHUNK_sRGB + pData->iGlobalRendintent = 0; /* reset global sRGB data */ +#endif + +#ifndef MNG_SKIPCHUNK_iCCP + if (pData->iGlobalProfilesize) /* drop global profile (if any) */ + MNG_FREE (pData, pData->pGlobalProfile, pData->iGlobalProfilesize); + + pData->iGlobalProfilesize = 0; +#endif + +#ifndef MNG_SKIPCHUNK_bKGD + pData->iGlobalBKGDred = 0; /* reset global bKGD data */ + pData->iGlobalBKGDgreen = 0; + pData->iGlobalBKGDblue = 0; +#endif +#ifndef MNG_NO_DELTA_PNG + /* reset delta-image */ + pData->pDeltaImage = MNG_NULL; + pData->iDeltaImagetype = 0; + pData->iDeltatype = 0; + pData->iDeltaBlockwidth = 0; + pData->iDeltaBlockheight = 0; + pData->iDeltaBlockx = 0; + pData->iDeltaBlocky = 0; + pData->bDeltaimmediate = MNG_FALSE; + + pData->fDeltagetrow = MNG_NULL; + pData->fDeltaaddrow = MNG_NULL; + pData->fDeltareplacerow = MNG_NULL; + pData->fDeltaputrow = MNG_NULL; + + pData->fPromoterow = MNG_NULL; + pData->fPromBitdepth = MNG_NULL; + pData->pPromBuf = MNG_NULL; + pData->iPromColortype = 0; + pData->iPromBitdepth = 0; + pData->iPromFilltype = 0; + pData->iPromWidth = 0; + pData->pPromSrc = MNG_NULL; + pData->pPromDst = MNG_NULL; +#endif + +#ifndef MNG_SKIPCHUNK_MAGN + pData->iMAGNfromid = 0; + pData->iMAGNtoid = 0; +#endif + +#ifndef MNG_SKIPCHUNK_PAST + pData->iPastx = 0; + pData->iPasty = 0; +#endif + + pData->pLastseek = MNG_NULL; + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +MNG_LOCAL void cleanup_errors (mng_datap pData) +{ + pData->iErrorcode = MNG_NOERROR; + pData->iSeverity = 0; + pData->iErrorx1 = 0; + pData->iErrorx2 = 0; + pData->zErrortext = MNG_NULL; + + return; +} + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +MNG_LOCAL mng_retcode make_pushbuffer (mng_datap pData, + mng_ptr pPushdata, + mng_size_t iLength, + mng_bool bTakeownership, + mng_pushdatap * pPush) +{ + mng_pushdatap pTemp; + + MNG_ALLOC (pData, pTemp, sizeof(mng_pushdata)); + + pTemp->pNext = MNG_NULL; + + if (bTakeownership) /* are we going to own the buffer? */ + { /* then just copy the pointer */ + pTemp->pData = (mng_uint8p)pPushdata; + } + else + { /* otherwise create new buffer */ + MNG_ALLOCX (pData, pTemp->pData, iLength); + if (!pTemp->pData) /* succeeded? */ + { + MNG_FREEX (pData, pTemp, sizeof(mng_pushdata)); + MNG_ERROR (pData, MNG_OUTOFMEMORY); + } + /* and copy the bytes across */ + MNG_COPY (pTemp->pData, pPushdata, iLength); + } + + pTemp->iLength = iLength; + pTemp->bOwned = bTakeownership; + pTemp->pDatanext = pTemp->pData; + pTemp->iRemaining = iLength; + + *pPush = pTemp; /* return it */ + + return MNG_NOERROR; /* and all's well */ +} +#endif + +#ifdef MNG_VERSION_QUERY_SUPPORT +/* ************************************************************************** */ +/* * * */ +/* * Versioning control * */ +/* * * */ +/* ************************************************************************** */ + +mng_pchar MNG_DECL mng_version_text (void) +{ + return MNG_VERSION_TEXT; +} + +/* ************************************************************************** */ + +mng_uint8 MNG_DECL mng_version_so (void) +{ + return MNG_VERSION_SO; +} + +/* ************************************************************************** */ + +mng_uint8 MNG_DECL mng_version_dll (void) +{ + return MNG_VERSION_DLL; +} + +/* ************************************************************************** */ + +mng_uint8 MNG_DECL mng_version_major (void) +{ + return MNG_VERSION_MAJOR; +} + +/* ************************************************************************** */ + +mng_uint8 MNG_DECL mng_version_minor (void) +{ + return MNG_VERSION_MINOR; +} + +/* ************************************************************************** */ + +mng_uint8 MNG_DECL mng_version_release (void) +{ + return MNG_VERSION_RELEASE; +} + +/* ************************************************************************** */ + +mng_bool MNG_DECL mng_version_beta (void) +{ + return MNG_VERSION_BETA; +} +#endif + +/* ************************************************************************** */ +/* * * */ +/* * 'supports' function * */ +/* * * */ +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_FUNCQUERY +typedef struct { + mng_pchar zFunction; + mng_uint8 iMajor; /* Major == 0 means not implemented ! */ + mng_uint8 iMinor; + mng_uint8 iRelease; + } mng_func_entry; +typedef mng_func_entry const * mng_func_entryp; + +MNG_LOCAL mng_func_entry const func_table [] = + { /* keep it alphabetically sorted !!!!! */ + {"mng_cleanup", 1, 0, 0}, + {"mng_copy_chunk", 1, 0, 5}, + {"mng_create", 1, 0, 0}, + {"mng_display", 1, 0, 0}, + {"mng_display_freeze", 1, 0, 0}, +#ifndef MNG_NO_DISPLAY_GO_SUPPORTED + {"mng_display_goframe", 1, 0, 0}, + {"mng_display_golayer", 1, 0, 0}, + {"mng_display_gotime", 1, 0, 0}, +#endif + {"mng_display_reset", 1, 0, 0}, + {"mng_display_resume", 1, 0, 0}, + {"mng_get_alphabitdepth", 1, 0, 0}, + {"mng_get_alphacompression", 1, 0, 0}, + {"mng_get_alphadepth", 1, 0, 0}, + {"mng_get_alphafilter", 1, 0, 0}, + {"mng_get_alphainterlace", 1, 0, 0}, + {"mng_get_bgcolor", 1, 0, 0}, + {"mng_get_bitdepth", 1, 0, 0}, + {"mng_get_bkgdstyle", 1, 0, 0}, + {"mng_get_cacheplayback", 1, 0, 2}, + {"mng_get_canvasstyle", 1, 0, 0}, + {"mng_get_colortype", 1, 0, 0}, + {"mng_get_compression", 1, 0, 0}, +#ifndef MNG_NO_CURRENT_INFO + {"mng_get_currentframe", 1, 0, 0}, + {"mng_get_currentlayer", 1, 0, 0}, + {"mng_get_currentplaytime", 1, 0, 0}, +#endif + {"mng_get_currframdelay", 1, 0, 9}, +#ifndef MNG_NO_DFLT_INFO + {"mng_get_dfltimggamma", 1, 0, 0}, + {"mng_get_dfltimggammaint", 1, 0, 0}, +#endif + {"mng_get_displaygamma", 1, 0, 0}, + {"mng_get_displaygammaint", 1, 0, 0}, + {"mng_get_doprogressive", 1, 0, 2}, + {"mng_get_filter", 1, 0, 0}, + {"mng_get_framecount", 1, 0, 0}, + {"mng_get_imageheight", 1, 0, 0}, + {"mng_get_imagelevel", 1, 0, 0}, + {"mng_get_imagetype", 1, 0, 0}, + {"mng_get_imagewidth", 1, 0, 0}, + {"mng_get_interlace", 1, 0, 0}, +#ifdef MNG_ACCESS_JPEG + {"mng_get_jpeg_dctmethod", 1, 0, 0}, + {"mng_get_jpeg_maxjdat", 1, 0, 0}, + {"mng_get_jpeg_optimized", 1, 0, 0}, + {"mng_get_jpeg_progressive", 1, 0, 0}, + {"mng_get_jpeg_quality", 1, 0, 0}, + {"mng_get_jpeg_smoothing", 1, 0, 0}, +#endif + {"mng_get_lastbackchunk", 1, 0, 3}, + {"mng_get_lastseekname", 1, 0, 5}, + {"mng_get_layercount", 1, 0, 0}, +#ifndef MNG_SKIP_MAXCANVAS + {"mng_get_maxcanvasheight", 1, 0, 0}, + {"mng_get_maxcanvaswidth", 1, 0, 0}, +#endif + {"mng_get_playtime", 1, 0, 0}, + {"mng_get_refreshpass", 1, 0, 0}, + {"mng_get_runtime", 1, 0, 0}, + {"mng_get_sectionbreaks", 1, 0, 0}, + {"mng_get_sigtype", 1, 0, 0}, + {"mng_get_simplicity", 1, 0, 0}, + {"mng_get_speed", 1, 0, 0}, + {"mng_get_srgb", 1, 0, 0}, + {"mng_get_starttime", 1, 0, 0}, + {"mng_get_storechunks", 1, 0, 0}, + {"mng_get_suspensionmode", 1, 0, 0}, + {"mng_get_ticks", 1, 0, 0}, +#ifndef MNG_NO_CURRENT_INFO + {"mng_get_totalframes", 1, 0, 5}, + {"mng_get_totallayers", 1, 0, 5}, + {"mng_get_totalplaytime", 1, 0, 5}, +#endif + {"mng_get_usebkgd", 1, 0, 0}, + {"mng_get_userdata", 1, 0, 0}, +#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS) + {"mng_get_viewgamma", 1, 0, 0}, + {"mng_get_viewgammaint", 1, 0, 0}, +#endif +#ifdef MNG_ACCESS_ZLIB + {"mng_get_zlib_level", 1, 0, 0}, + {"mng_get_zlib_maxidat", 1, 0, 0}, + {"mng_get_zlib_memlevel", 1, 0, 0}, + {"mng_get_zlib_method", 1, 0, 0}, + {"mng_get_zlib_strategy", 1, 0, 0}, + {"mng_get_zlib_windowbits", 1, 0, 0}, +#endif +#ifndef MNG_NO_OPEN_CLOSE_STREAM + {"mng_getcb_closestream", 1, 0, 0}, +#endif + {"mng_getcb_errorproc", 1, 0, 0}, + {"mng_getcb_getalphaline", 1, 0, 0}, + {"mng_getcb_getbkgdline", 1, 0, 0}, + {"mng_getcb_getcanvasline", 1, 0, 0}, + {"mng_getcb_gettickcount", 1, 0, 0}, + {"mng_getcb_memalloc", 1, 0, 0}, + {"mng_getcb_memfree", 1, 0, 0}, +#ifndef MNG_NO_OPEN_CLOSE_STREAM + {"mng_getcb_openstream", 1, 0, 0}, +#endif + {"mng_getcb_processarow", 1, 0, 0}, + {"mng_getcb_processchroma", 1, 0, 0}, + {"mng_getcb_processgamma", 1, 0, 0}, + {"mng_getcb_processheader", 1, 0, 0}, + {"mng_getcb_processiccp", 1, 0, 0}, + {"mng_getcb_processmend", 1, 0, 1}, + {"mng_getcb_processneed", 1, 0, 0}, + {"mng_getcb_processsave", 1, 0, 0}, + {"mng_getcb_processseek", 1, 0, 0}, + {"mng_getcb_processsrgb", 1, 0, 0}, + {"mng_getcb_processterm", 1, 0, 2}, + {"mng_getcb_processtext", 1, 0, 0}, + {"mng_getcb_processunknown", 1, 0, 0}, + {"mng_getcb_readdata", 1, 0, 0}, + {"mng_getcb_refresh", 1, 0, 0}, + {"mng_getcb_releasedata", 1, 0, 8}, + {"mng_getcb_settimer", 1, 0, 0}, + {"mng_getcb_traceproc", 1, 0, 0}, + {"mng_getcb_writedata", 1, 0, 0}, + {"mng_getchunk_back", 1, 0, 0}, + {"mng_getchunk_basi", 1, 0, 0}, +#ifndef MNG_SKIPCHUNK_bKGD + {"mng_getchunk_bkgd", 1, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_cHRM + {"mng_getchunk_chrm", 1, 0, 0}, +#endif + {"mng_getchunk_clip", 1, 0, 0}, + {"mng_getchunk_clon", 1, 0, 0}, +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_dBYK + {"mng_getchunk_dbyk", 1, 0, 0}, +#endif +#endif + {"mng_getchunk_defi", 1, 0, 0}, +#ifndef MNG_NO_DELTA_PNG + {"mng_getchunk_dhdr", 1, 0, 0}, +#endif + {"mng_getchunk_disc", 1, 0, 0}, +#ifndef MNG_NO_DELTA_PNG + {"mng_getchunk_drop", 1, 0, 0}, +#endif + {"mng_getchunk_endl", 1, 0, 0}, +#ifdef MNG_INCLUDE_MPNG_PROPOSAL + {"mng_getchunk_mpng", 1, 0, 10}, + {"mng_getchunk_mpng_frame", 1, 0, 10}, +#endif +#ifndef MNG_SKIPCHUNK_evNT + {"mng_getchunk_evnt", 1, 0, 5}, + {"mng_getchunk_evnt_entry", 1, 0, 5}, +#endif +#ifndef MNG_SKIPCHUNK_eXPI + {"mng_getchunk_expi", 1, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_fPRI + {"mng_getchunk_fpri", 1, 0, 0}, +#endif + {"mng_getchunk_fram", 1, 0, 0}, + {"mng_getchunk_gama", 1, 0, 0}, +#ifndef MNG_SKIPCHUNK_hIST + {"mng_getchunk_hist", 1, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_iCCP + {"mng_getchunk_iccp", 1, 0, 0}, +#endif + {"mng_getchunk_idat", 1, 0, 0}, + {"mng_getchunk_iend", 1, 0, 0}, + {"mng_getchunk_ihdr", 1, 0, 0}, +#ifndef MNG_NO_DELTA_PNG +#ifdef MNG_INCLUDE_JNG + {"mng_getchunk_ijng", 1, 0, 0}, +#endif + {"mng_getchunk_ipng", 1, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_iTXt + {"mng_getchunk_itxt", 1, 0, 0}, +#endif +#ifdef MNG_INCLUDE_JNG + {"mng_getchunk_jdaa", 1, 0, 0}, + {"mng_getchunk_jdat", 1, 0, 0}, + {"mng_getchunk_jhdr", 1, 0, 0}, + {"mng_getchunk_jsep", 1, 0, 0}, +#endif + {"mng_getchunk_loop", 1, 0, 0}, +#ifndef MNG_SKIPCHUNK_MAGN + {"mng_getchunk_magn", 1, 0, 0}, +#endif + {"mng_getchunk_mend", 1, 0, 0}, + {"mng_getchunk_mhdr", 1, 0, 0}, + {"mng_getchunk_move", 1, 0, 0}, +#ifndef MNG_SKIPCHUNK_nEED + {"mng_getchunk_need", 1, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_ORDR +#ifndef MNG_NO_DELTA_PNG + {"mng_getchunk_ordr", 1, 0, 0}, + {"mng_getchunk_ordr_entry", 1, 0, 0}, +#endif +#endif +#ifndef MNG_SKIPCHUNK_PAST + {"mng_getchunk_past", 1, 0, 0}, + {"mng_getchunk_past_src", 1, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_pHYg + {"mng_getchunk_phyg", 1, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_pHYs + {"mng_getchunk_phys", 1, 0, 0}, +#endif +#ifndef MNG_NO_DELTA_PNG + {"mng_getchunk_plte", 1, 0, 0}, + {"mng_getchunk_pplt", 1, 0, 0}, + {"mng_getchunk_pplt_entry", 1, 0, 0}, + {"mng_getchunk_prom", 1, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_SAVE + {"mng_getchunk_save", 1, 0, 0}, + {"mng_getchunk_save_entry", 1, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_sBIT + {"mng_getchunk_sbit", 1, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_SEEK + {"mng_getchunk_seek", 1, 0, 0}, +#endif + {"mng_getchunk_show", 1, 0, 0}, +#ifndef MNG_SKIPCHUNK_sPLT + {"mng_getchunk_splt", 1, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_sRGB + {"mng_getchunk_srgb", 1, 0, 0}, +#endif + {"mng_getchunk_term", 1, 0, 0}, +#ifndef MNG_SKIPCHUNK_tEXt + {"mng_getchunk_text", 1, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_tIME + {"mng_getchunk_time", 1, 0, 0}, +#endif + {"mng_getchunk_trns", 1, 0, 0}, + {"mng_getchunk_unkown", 1, 0, 0}, +#ifndef MNG_SKIPCHUNK_zTXt + {"mng_getchunk_ztxt", 1, 0, 0}, +#endif + {"mng_getimgdata_chunk", 0, 0, 0}, + {"mng_getimgdata_chunkseq", 0, 0, 0}, + {"mng_getimgdata_seq", 0, 0, 0}, + {"mng_getlasterror", 1, 0, 0}, + {"mng_initialize", 1, 0, 0}, + {"mng_iterate_chunks", 1, 0, 0}, + {"mng_putchunk_back", 1, 0, 0}, +#ifndef MNG_SKIPCHUNK_BASI + {"mng_putchunk_basi", 1, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_bKGD + {"mng_putchunk_bkgd", 1, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_cHRM + {"mng_putchunk_chrm", 1, 0, 0}, +#endif + {"mng_putchunk_clip", 1, 0, 0}, + {"mng_putchunk_clon", 1, 0, 0}, +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_DBYK + {"mng_putchunk_dbyk", 1, 0, 0}, +#endif +#endif + {"mng_putchunk_defi", 1, 0, 0}, +#ifndef MNG_NO_DELTA_PNG + {"mng_putchunk_dhdr", 1, 0, 0}, +#endif + {"mng_putchunk_disc", 1, 0, 0}, +#ifndef MNG_NO_DELTA_PNG + {"mng_putchunk_drop", 1, 0, 0}, +#endif + {"mng_putchunk_endl", 1, 0, 0}, +#ifdef MNG_INCLUDE_MPNG_PROPOSAL + {"mng_putchunk_mpng", 1, 0, 10}, + {"mng_putchunk_mpng_frame", 1, 0, 10}, +#endif +#ifndef MNG_SKIPCHUNK_evNT + {"mng_putchunk_evnt", 1, 0, 5}, + {"mng_putchunk_evnt_entry", 1, 0, 5}, +#endif +#ifndef MNG_SKIPCHUNK_eXPI + {"mng_putchunk_expi", 1, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_fPRI + {"mng_putchunk_fpri", 1, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_FRAM + {"mng_putchunk_fram", 1, 0, 0}, +#endif + {"mng_putchunk_gama", 1, 0, 0}, +#ifndef MNG_SKIPCHUNK_hIST + {"mng_putchunk_hist", 1, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_iCCP + {"mng_putchunk_iccp", 1, 0, 0}, +#endif + {"mng_putchunk_idat", 1, 0, 0}, + {"mng_putchunk_iend", 1, 0, 0}, + {"mng_putchunk_ihdr", 1, 0, 0}, +#ifndef MNG_NO_DELTA_PNG +#ifdef MNG_INCLUDE_JNG + {"mng_putchunk_ijng", 1, 0, 0}, +#endif + {"mng_putchunk_ipng", 1, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_iTXt + {"mng_putchunk_itxt", 1, 0, 0}, +#endif +#ifdef MNG_INCLUDE_JNG + {"mng_putchunk_jdaa", 1, 0, 0}, + {"mng_putchunk_jdat", 1, 0, 0}, + {"mng_putchunk_jhdr", 1, 0, 0}, + {"mng_putchunk_jsep", 1, 0, 0}, +#endif + {"mng_putchunk_loop", 1, 0, 0}, +#ifndef MNG_SKIPCHUNK_MAGN + {"mng_putchunk_magn", 1, 0, 0}, +#endif + {"mng_putchunk_mend", 1, 0, 0}, + {"mng_putchunk_mhdr", 1, 0, 0}, + {"mng_putchunk_move", 1, 0, 0}, +#ifndef MNG_SKIPCHUNK_nEED + {"mng_putchunk_need", 1, 0, 0}, +#endif +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_ORDR + {"mng_putchunk_ordr", 1, 0, 0}, + {"mng_putchunk_ordr_entry", 1, 0, 0}, +#endif +#endif +#ifndef MNG_SKIPCHUNK_PAST + {"mng_putchunk_past", 1, 0, 0}, + {"mng_putchunk_past_src", 1, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_pHYg + {"mng_putchunk_phyg", 1, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_pHYs + {"mng_putchunk_phys", 1, 0, 0}, +#endif +#ifndef MNG_NO_DELTA_PNG + {"mng_putchunk_plte", 1, 0, 0}, + {"mng_putchunk_pplt", 1, 0, 0}, + {"mng_putchunk_pplt_entry", 1, 0, 0}, + {"mng_putchunk_prom", 1, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_SAVE + {"mng_putchunk_save", 1, 0, 0}, + {"mng_putchunk_save_entry", 1, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_sBIT + {"mng_putchunk_sbit", 1, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_SEEK + {"mng_putchunk_seek", 1, 0, 0}, +#endif + {"mng_putchunk_show", 1, 0, 0}, +#ifndef MNG_SKIPCHUNK_sPLT + {"mng_putchunk_splt", 1, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_sRGB + {"mng_putchunk_srgb", 1, 0, 0}, +#endif + {"mng_putchunk_term", 1, 0, 0}, +#ifndef MNG_SKIPCHUNK_tEXt + {"mng_putchunk_text", 1, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_tIME + {"mng_putchunk_time", 1, 0, 0}, +#endif + {"mng_putchunk_trns", 1, 0, 0}, + {"mng_putchunk_unkown", 1, 0, 0}, +#ifndef MNG_SKIPCHUNK_zTXt + {"mng_putchunk_ztxt", 1, 0, 0}, +#endif + {"mng_putimgdata_ihdr", 0, 0, 0}, + {"mng_putimgdata_jhdr", 0, 0, 0}, + {"mng_reset", 1, 0, 0}, + {"mng_read", 1, 0, 0}, + {"mng_read_pushchunk", 1, 0, 8}, + {"mng_read_pushdata", 1, 0, 8}, + {"mng_read_pushsig", 1, 0, 8}, + {"mng_read_resume", 1, 0, 0}, + {"mng_readdisplay", 1, 0, 0}, + {"mng_set_bgcolor", 1, 0, 0}, + {"mng_set_bkgdstyle", 1, 0, 0}, + {"mng_set_cacheplayback", 1, 0, 2}, + {"mng_set_canvasstyle", 1, 0, 0}, + {"mng_set_dfltimggamma", 1, 0, 0}, +#ifndef MNG_NO_DFLT_INFO + {"mng_set_dfltimggammaint", 1, 0, 0}, +#endif + {"mng_set_displaygamma", 1, 0, 0}, + {"mng_set_displaygammaint", 1, 0, 0}, + {"mng_set_doprogressive", 1, 0, 2}, +#ifdef MNG_ACCESS_JPEG + {"mng_set_jpeg_dctmethod", 1, 0, 0}, + {"mng_set_jpeg_maxjdat", 1, 0, 0}, + {"mng_set_jpeg_optimized", 1, 0, 0}, + {"mng_set_jpeg_progressive", 1, 0, 0}, + {"mng_set_jpeg_quality", 1, 0, 0}, + {"mng_set_jpeg_smoothing", 1, 0, 0}, +#endif +#ifndef MNG_SKIP_MAXCANVAS + {"mng_set_maxcanvasheight", 1, 0, 0}, + {"mng_set_maxcanvassize", 1, 0, 0}, + {"mng_set_maxcanvaswidth", 1, 0, 0}, +#endif + {"mng_set_outputprofile", 1, 0, 0}, + {"mng_set_outputprofile2", 1, 0, 0}, + {"mng_set_outputsrgb", 1, 0, 1}, + {"mng_set_sectionbreaks", 1, 0, 0}, + {"mng_set_speed", 1, 0, 0}, + {"mng_set_srgb", 1, 0, 0}, + {"mng_set_srgbimplicit", 1, 0, 1}, + {"mng_set_srgbprofile", 1, 0, 0}, + {"mng_set_srgbprofile2", 1, 0, 0}, + {"mng_set_storechunks", 1, 0, 0}, + {"mng_set_suspensionmode", 1, 0, 0}, + {"mng_set_usebkgd", 1, 0, 0}, + {"mng_set_userdata", 1, 0, 0}, +#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS) + {"mng_set_viewgamma", 1, 0, 0}, + {"mng_set_viewgammaint", 1, 0, 0}, +#endif +#ifdef MNG_ACCESS_ZLIB + {"mng_set_zlib_level", 1, 0, 0}, + {"mng_set_zlib_maxidat", 1, 0, 0}, + {"mng_set_zlib_memlevel", 1, 0, 0}, + {"mng_set_zlib_method", 1, 0, 0}, + {"mng_set_zlib_strategy", 1, 0, 0}, + {"mng_set_zlib_windowbits", 1, 0, 0}, +#endif +#ifndef MNG_NO_OPEN_CLOSE_STREAM + {"mng_setcb_closestream", 1, 0, 0}, +#endif + {"mng_setcb_errorproc", 1, 0, 0}, + {"mng_setcb_getalphaline", 1, 0, 0}, + {"mng_setcb_getbkgdline", 1, 0, 0}, + {"mng_setcb_getcanvasline", 1, 0, 0}, + {"mng_setcb_gettickcount", 1, 0, 0}, + {"mng_setcb_memalloc", 1, 0, 0}, + {"mng_setcb_memfree", 1, 0, 0}, +#ifndef MNG_NO_OPEN_CLOSE_STREAM + {"mng_setcb_openstream", 1, 0, 0}, +#endif + {"mng_setcb_processarow", 1, 0, 0}, + {"mng_setcb_processchroma", 1, 0, 0}, + {"mng_setcb_processgamma", 1, 0, 0}, + {"mng_setcb_processheader", 1, 0, 0}, + {"mng_setcb_processiccp", 1, 0, 0}, + {"mng_setcb_processmend", 1, 0, 1}, + {"mng_setcb_processneed", 1, 0, 0}, + {"mng_setcb_processsave", 1, 0, 0}, + {"mng_setcb_processseek", 1, 0, 0}, + {"mng_setcb_processsrgb", 1, 0, 0}, + {"mng_setcb_processterm", 1, 0, 2}, + {"mng_setcb_processtext", 1, 0, 0}, + {"mng_setcb_processunknown", 1, 0, 0}, + {"mng_setcb_readdata", 1, 0, 0}, + {"mng_setcb_refresh", 1, 0, 0}, + {"mng_setcb_releasedata", 1, 0, 8}, + {"mng_setcb_settimer", 1, 0, 0}, + {"mng_setcb_traceproc", 1, 0, 0}, + {"mng_setcb_writedata", 1, 0, 0}, + {"mng_status_creating", 1, 0, 0}, + {"mng_status_displaying", 1, 0, 0}, + {"mng_status_dynamic", 1, 0, 5}, + {"mng_status_error", 1, 0, 0}, + {"mng_status_reading", 1, 0, 0}, + {"mng_status_running", 1, 0, 0}, + {"mng_status_runningevent", 1, 0, 5}, + {"mng_status_suspendbreak", 1, 0, 0}, + {"mng_status_timerbreak", 1, 0, 0}, + {"mng_status_writing", 1, 0, 0}, + {"mng_supports_func", 1, 0, 5}, + {"mng_trapevent", 1, 0, 5}, + {"mng_updatemngheader", 1, 0, 0}, + {"mng_updatemngsimplicity", 1, 0, 0}, + {"mng_version_beta", 1, 0, 5}, + {"mng_version_dll", 1, 0, 0}, + {"mng_version_major", 1, 0, 0}, + {"mng_version_minor", 1, 0, 0}, + {"mng_version_release", 1, 0, 0}, + {"mng_version_so", 1, 0, 0}, + {"mng_version_text", 1, 0, 0}, + {"mng_write", 1, 0, 0}, + }; + +mng_bool MNG_DECL mng_supports_func (mng_pchar zFunction, + mng_uint8* iMajor, + mng_uint8* iMinor, + mng_uint8* iRelease) +{ + mng_int32 iTop, iLower, iUpper, iMiddle; + mng_func_entryp pEntry; /* pointer to found entry */ + /* determine max index of table */ + iTop = (sizeof (func_table) / sizeof (func_table [0])) - 1; + + iLower = 0; /* initialize binary search */ + iMiddle = iTop >> 1; /* start in the middle */ + iUpper = iTop; + pEntry = 0; /* no goods yet! */ + + do /* the binary search itself */ + { + mng_int32 iRslt = strcmp(func_table [iMiddle].zFunction, zFunction); + if (iRslt < 0) + iLower = iMiddle + 1; + else if (iRslt > 0) + iUpper = iMiddle - 1; + else + { + pEntry = &func_table [iMiddle]; + break; + }; + + iMiddle = (iLower + iUpper) >> 1; + } + while (iLower <= iUpper); + + if (pEntry) /* found it ? */ + { + *iMajor = pEntry->iMajor; + *iMinor = pEntry->iMinor; + *iRelease = pEntry->iRelease; + return MNG_TRUE; + } + else + { + *iMajor = 0; + *iMinor = 0; + *iRelease = 0; + return MNG_FALSE; + } +} +#endif + +/* ************************************************************************** */ +/* * * */ +/* * HLAPI routines * */ +/* * * */ +/* ************************************************************************** */ + +mng_handle MNG_DECL mng_initialize (mng_ptr pUserdata, + mng_memalloc fMemalloc, + mng_memfree fMemfree, + mng_traceproc fTraceproc) +{ + mng_datap pData; +#ifdef MNG_SUPPORT_DISPLAY + mng_retcode iRetcode; + mng_imagep pImage; +#endif + +#ifdef MNG_INTERNAL_MEMMNGMT /* allocate the main datastruc */ + pData = (mng_datap)calloc (1, sizeof (mng_data)); +#else + pData = (mng_datap)fMemalloc (sizeof (mng_data)); +#endif + + if (!pData) + return MNG_NULL; /* error: out of memory?? */ + /* validate the structure */ + pData->iMagic = MNG_MAGIC; + /* save userdata field */ + pData->pUserdata = pUserdata; + /* remember trace callback */ + pData->fTraceproc = fTraceproc; + +#ifdef MNG_SUPPORT_TRACE + if (mng_trace (pData, MNG_FN_INITIALIZE, MNG_LC_INITIALIZE)) + { + MNG_FREEX (pData, pData, sizeof (mng_data)); + return MNG_NULL; + } +#endif + /* default canvas styles are 8-bit RGB */ + pData->iCanvasstyle = MNG_CANVAS_RGB8; + pData->iBkgdstyle = MNG_CANVAS_RGB8; + + pData->iBGred = 0; /* black */ + pData->iBGgreen = 0; + pData->iBGblue = 0; + + pData->bUseBKGD = MNG_TRUE; + +#ifdef MNG_FULL_CMS + pData->bIssRGB = MNG_TRUE; + pData->hProf1 = 0; /* no profiles yet */ + pData->hProf2 = 0; + pData->hProf3 = 0; + pData->hTrans = 0; +#endif + + pData->dViewgamma = 1.0; + pData->dDisplaygamma = 2.2; + pData->dDfltimggamma = 0.45455; + /* initially remember chunks */ + pData->bStorechunks = MNG_TRUE; + /* no breaks at section-borders */ + pData->bSectionbreaks = MNG_FALSE; + /* initially cache playback info */ + pData->bCacheplayback = MNG_TRUE; + /* progressive refresh for large images */ + pData->bDoProgressive = MNG_TRUE; + /* crc exists; should check; error for + critical chunks; warning for ancillery; + generate crc for output */ + pData->iCrcmode = MNG_CRC_DEFAULT; + /* normal animation-speed ! */ + pData->iSpeed = mng_st_normal; + /* initial image limits */ + pData->iMaxwidth = 10000; + pData->iMaxheight = 10000; + +#ifdef MNG_INTERNAL_MEMMNGMT /* internal management */ + pData->fMemalloc = MNG_NULL; + pData->fMemfree = MNG_NULL; +#else /* keep callbacks */ + pData->fMemalloc = fMemalloc; + pData->fMemfree = fMemfree; +#endif + /* no value (yet) */ + pData->fReleasedata = MNG_NULL; +#ifndef MNG_NO_OPEN_CLOSE_STREAM + pData->fOpenstream = MNG_NULL; + pData->fClosestream = MNG_NULL; +#endif + pData->fReaddata = MNG_NULL; + pData->fWritedata = MNG_NULL; + pData->fErrorproc = MNG_NULL; + pData->fProcessheader = MNG_NULL; + pData->fProcesstext = MNG_NULL; + pData->fProcesssave = MNG_NULL; + pData->fProcessseek = MNG_NULL; + pData->fProcessneed = MNG_NULL; + pData->fProcessmend = MNG_NULL; + pData->fProcessunknown = MNG_NULL; + pData->fProcessterm = MNG_NULL; + pData->fGetcanvasline = MNG_NULL; + pData->fGetbkgdline = MNG_NULL; + pData->fGetalphaline = MNG_NULL; + pData->fRefresh = MNG_NULL; + pData->fGettickcount = MNG_NULL; + pData->fSettimer = MNG_NULL; + pData->fProcessgamma = MNG_NULL; + pData->fProcesschroma = MNG_NULL; + pData->fProcesssrgb = MNG_NULL; + pData->fProcessiccp = MNG_NULL; + pData->fProcessarow = MNG_NULL; + +#if defined(MNG_SUPPORT_DISPLAY) && (defined(MNG_GAMMA_ONLY) || defined(MNG_FULL_CMS)) + pData->dLastgamma = 0; /* lookup table needs first-time calc */ +#endif + +#ifdef MNG_SUPPORT_DISPLAY /* create object 0 */ + iRetcode = mng_create_imageobject (pData, 0, MNG_TRUE, MNG_TRUE, MNG_TRUE, + 0, 0, 0, 0, 0, 0, 0, 0, 0, MNG_FALSE, + 0, 0, 0, 0, &pImage); + + if (iRetcode) /* on error drop out */ + { + MNG_FREEX (pData, pData, sizeof (mng_data)); + return MNG_NULL; + } + + pData->pObjzero = pImage; +#endif + +#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_INCLUDE_LCMS) + mnglcms_initlibrary (); /* init lcms particulars */ +#endif + +#ifdef MNG_SUPPORT_READ + pData->bSuspensionmode = MNG_FALSE; + pData->iSuspendbufsize = 0; + pData->pSuspendbuf = MNG_NULL; + pData->pSuspendbufnext = MNG_NULL; + pData->iSuspendbufleft = 0; + pData->iChunklen = 0; + pData->pReadbufnext = MNG_NULL; + pData->pLargebufnext = MNG_NULL; + + pData->pFirstpushchunk = MNG_NULL; + pData->pLastpushchunk = MNG_NULL; + pData->pFirstpushdata = MNG_NULL; + pData->pLastpushdata = MNG_NULL; +#endif + +#ifdef MNG_INCLUDE_ZLIB + mngzlib_initialize (pData); /* initialize zlib structures and such */ + /* default zlib compression parameters */ + pData->iZlevel = MNG_ZLIB_LEVEL; + pData->iZmethod = MNG_ZLIB_METHOD; + pData->iZwindowbits = MNG_ZLIB_WINDOWBITS; + pData->iZmemlevel = MNG_ZLIB_MEMLEVEL; + pData->iZstrategy = MNG_ZLIB_STRATEGY; + /* default maximum IDAT data size */ + pData->iMaxIDAT = MNG_MAX_IDAT_SIZE; +#endif + +#ifdef MNG_INCLUDE_JNG /* default IJG compression parameters */ + pData->eJPEGdctmethod = MNG_JPEG_DCT; + pData->iJPEGquality = MNG_JPEG_QUALITY; + pData->iJPEGsmoothing = MNG_JPEG_SMOOTHING; + pData->bJPEGcompressprogr = MNG_JPEG_PROGRESSIVE; + pData->bJPEGcompressopt = MNG_JPEG_OPTIMIZED; + /* default maximum JDAT data size */ + pData->iMaxJDAT = MNG_MAX_JDAT_SIZE; +#endif + + mng_reset ((mng_handle)pData); + +#ifdef MNG_SUPPORT_TRACE + if (mng_trace (pData, MNG_FN_INITIALIZE, MNG_LC_END)) + { + MNG_FREEX (pData, pData, sizeof (mng_data)); + return MNG_NULL; + } +#endif + + return (mng_handle)pData; /* if we get here, we're in business */ +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_reset (mng_handle hHandle) +{ + mng_datap pData; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_RESET, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = ((mng_datap)(hHandle)); /* address main structure */ + +#ifdef MNG_SUPPORT_DISPLAY +#ifndef MNG_SKIPCHUNK_SAVE + mng_drop_savedata (pData); /* cleanup saved-data from SAVE/SEEK */ +#endif +#endif + +#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_FULL_CMS) + mng_clear_cms (pData); /* cleanup left-over cms stuff if any */ +#endif + +#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_INCLUDE_JNG) + mngjpeg_cleanup (pData); /* cleanup jpeg stuff */ +#endif + +#ifdef MNG_INCLUDE_ZLIB + if (pData->bInflating) /* if we've been inflating */ + { +#ifdef MNG_INCLUDE_DISPLAY_PROCS + mng_cleanup_rowproc (pData); /* cleanup row-processing, */ +#endif + mngzlib_inflatefree (pData); /* cleanup inflate! */ + } +#endif /* MNG_INCLUDE_ZLIB */ + +#ifdef MNG_SUPPORT_READ + if ((pData->bReading) && (!pData->bEOF)) + mng_process_eof (pData); /* cleanup app streaming */ + /* cleanup default read buffers */ + MNG_FREE (pData, pData->pReadbuf, pData->iReadbufsize); + MNG_FREE (pData, pData->pLargebuf, pData->iLargebufsize); + MNG_FREE (pData, pData->pSuspendbuf, pData->iSuspendbufsize); + + while (pData->pFirstpushdata) /* release any pushed data & chunks */ + mng_release_pushdata (pData); + while (pData->pFirstpushchunk) + mng_release_pushchunk (pData); +#endif + +#ifdef MNG_SUPPORT_WRITE /* cleanup default write buffer */ + MNG_FREE (pData, pData->pWritebuf, pData->iWritebufsize); +#endif + +#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE) + mng_drop_chunks (pData); /* drop stored chunks (if any) */ +#endif + +#ifdef MNG_SUPPORT_DISPLAY + mng_drop_objects (pData, MNG_TRUE); /* drop stored objects (if any) */ + +#ifndef MNG_SKIPCHUNK_iCCP + if (pData->iGlobalProfilesize) /* drop global profile (if any) */ + MNG_FREEX (pData, pData->pGlobalProfile, pData->iGlobalProfilesize); +#endif +#endif + + pData->eSigtype = mng_it_unknown; + pData->eImagetype = mng_it_unknown; + pData->iWidth = 0; /* these are unknown yet */ + pData->iHeight = 0; + pData->iTicks = 0; + pData->iLayercount = 0; + pData->iFramecount = 0; + pData->iPlaytime = 0; + pData->iSimplicity = 0; + pData->iAlphadepth = 16; /* assume the worst! */ + + pData->iImagelevel = 0; /* no image encountered */ + + pData->iMagnify = 0; /* 1-to-1 display */ + pData->iOffsetx = 0; /* no offsets */ + pData->iOffsety = 0; + pData->iCanvaswidth = 0; /* let the app decide during processheader */ + pData->iCanvasheight = 0; + /* so far, so good */ + pData->iErrorcode = MNG_NOERROR; + pData->iSeverity = 0; + pData->iErrorx1 = 0; + pData->iErrorx2 = 0; + pData->zErrortext = MNG_NULL; + +#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE) + /* let's assume the best scenario */ +#ifndef MNG_NO_OLD_VERSIONS + pData->bPreDraft48 = MNG_FALSE; +#endif + /* the unknown chunk */ + pData->iChunkname = MNG_UINT_HUH; + pData->iChunkseq = 0; + pData->pFirstchunk = MNG_NULL; + pData->pLastchunk = MNG_NULL; + /* nothing processed yet */ + pData->bHasheader = MNG_FALSE; + pData->bHasMHDR = MNG_FALSE; + pData->bHasIHDR = MNG_FALSE; + pData->bHasBASI = MNG_FALSE; + pData->bHasDHDR = MNG_FALSE; +#ifdef MNG_INCLUDE_JNG + pData->bHasJHDR = MNG_FALSE; + pData->bHasJSEP = MNG_FALSE; + pData->bHasJDAA = MNG_FALSE; + pData->bHasJDAT = MNG_FALSE; +#endif + pData->bHasPLTE = MNG_FALSE; + pData->bHasTRNS = MNG_FALSE; + pData->bHasGAMA = MNG_FALSE; + pData->bHasCHRM = MNG_FALSE; + pData->bHasSRGB = MNG_FALSE; + pData->bHasICCP = MNG_FALSE; + pData->bHasBKGD = MNG_FALSE; + pData->bHasIDAT = MNG_FALSE; + + pData->bHasSAVE = MNG_FALSE; + pData->bHasBACK = MNG_FALSE; + pData->bHasFRAM = MNG_FALSE; + pData->bHasTERM = MNG_FALSE; + pData->bHasLOOP = MNG_FALSE; + /* there's no global stuff yet either */ + pData->bHasglobalPLTE = MNG_FALSE; + pData->bHasglobalTRNS = MNG_FALSE; + pData->bHasglobalGAMA = MNG_FALSE; + pData->bHasglobalCHRM = MNG_FALSE; + pData->bHasglobalSRGB = MNG_FALSE; + pData->bHasglobalICCP = MNG_FALSE; + + pData->iDatawidth = 0; /* no IHDR/BASI/DHDR done yet */ + pData->iDataheight = 0; + pData->iBitdepth = 0; + pData->iColortype = 0; + pData->iCompression = 0; + pData->iFilter = 0; + pData->iInterlace = 0; + +#ifdef MNG_INCLUDE_JNG + pData->iJHDRcolortype = 0; /* no JHDR data */ + pData->iJHDRimgbitdepth = 0; + pData->iJHDRimgcompression = 0; + pData->iJHDRimginterlace = 0; + pData->iJHDRalphabitdepth = 0; + pData->iJHDRalphacompression = 0; + pData->iJHDRalphafilter = 0; + pData->iJHDRalphainterlace = 0; +#endif + +#endif /* MNG_SUPPORT_READ || MNG_SUPPORT_WRITE */ + +#ifdef MNG_SUPPORT_READ /* no reading done */ + pData->bReading = MNG_FALSE; + pData->bHavesig = MNG_FALSE; + pData->bEOF = MNG_FALSE; + pData->iReadbufsize = 0; + pData->pReadbuf = MNG_NULL; + + pData->iLargebufsize = 0; + pData->pLargebuf = MNG_NULL; + + pData->iSuspendtime = 0; + pData->bSuspended = MNG_FALSE; + pData->iSuspendpoint = 0; + + pData->pSuspendbufnext = pData->pSuspendbuf; + pData->iSuspendbufleft = 0; +#endif /* MNG_SUPPORT_READ */ + +#ifdef MNG_SUPPORT_WRITE /* no creating/writing done */ + pData->bCreating = MNG_FALSE; + pData->bWriting = MNG_FALSE; + pData->iFirstchunkadded = 0; + pData->iWritebufsize = 0; + pData->pWritebuf = MNG_NULL; +#endif /* MNG_SUPPORT_WRITE */ + +#ifdef MNG_SUPPORT_DISPLAY /* done nuttin' yet */ + pData->bDisplaying = MNG_FALSE; + pData->iFrameseq = 0; + pData->iLayerseq = 0; + pData->iFrametime = 0; + + pData->iTotallayers = 0; + pData->iTotalframes = 0; + pData->iTotalplaytime = 0; + + pData->bSkipping = MNG_FALSE; + +#ifdef MNG_SUPPORT_DYNAMICMNG + pData->bDynamic = MNG_FALSE; + pData->bRunningevent = MNG_FALSE; + pData->bStopafterseek = MNG_FALSE; + pData->iEventx = 0; + pData->iEventy = 0; + pData->pLastmousemove = MNG_NULL; +#endif + + pData->iRequestframe = 0; + pData->iRequestlayer = 0; + pData->iRequesttime = 0; + pData->bSearching = MNG_FALSE; + + pData->bRestorebkgd = MNG_FALSE; + + pData->iRuntime = 0; + pData->iSynctime = 0; + pData->iStarttime = 0; + pData->iEndtime = 0; + pData->bRunning = MNG_FALSE; + pData->bTimerset = MNG_FALSE; + pData->iBreakpoint = 0; + pData->bSectionwait = MNG_FALSE; + pData->bFreezing = MNG_FALSE; + pData->bResetting = MNG_FALSE; + pData->bNeedrefresh = MNG_FALSE; + pData->bMisplacedTERM = MNG_FALSE; + pData->bOnlyfirstframe = MNG_FALSE; + pData->iFramesafterTERM = 0; + /* these don't exist yet */ + pData->pCurrentobj = MNG_NULL; + pData->pCurraniobj = MNG_NULL; + pData->pTermaniobj = MNG_NULL; + pData->pLastclone = MNG_NULL; + pData->pStoreobj = MNG_NULL; + pData->pStorebuf = MNG_NULL; + pData->pRetrieveobj = MNG_NULL; + /* no saved data ! */ + pData->pSavedata = MNG_NULL; + + pData->iUpdateleft = 0; /* no region updated yet */ + pData->iUpdateright = 0; + pData->iUpdatetop = 0; + pData->iUpdatebottom = 0; + + pData->iPass = -1; /* interlacing stuff and temp buffers */ + pData->iRow = 0; + pData->iRowinc = 1; + pData->iCol = 0; + pData->iColinc = 1; + pData->iRowsamples = 0; + pData->iSamplemul = 0; + pData->iSampleofs = 0; + pData->iSamplediv = 0; + pData->iRowsize = 0; + pData->iRowmax = 0; + pData->iFilterofs = 0; + pData->iPixelofs = 1; + pData->iLevel0 = 0; + pData->iLevel1 = 0; + pData->iLevel2 = 0; + pData->iLevel3 = 0; + pData->pWorkrow = MNG_NULL; + pData->pPrevrow = MNG_NULL; + pData->pRGBArow = MNG_NULL; + pData->bIsRGBA16 = MNG_TRUE; + pData->bIsOpaque = MNG_TRUE; + pData->iFilterbpp = 1; + + pData->iSourcel = 0; /* always initialized just before */ + pData->iSourcer = 0; /* compositing the next layer */ + pData->iSourcet = 0; + pData->iSourceb = 0; + pData->iDestl = 0; + pData->iDestr = 0; + pData->iDestt = 0; + pData->iDestb = 0; + /* lists are empty */ + pData->pFirstimgobj = MNG_NULL; + pData->pLastimgobj = MNG_NULL; + pData->pFirstaniobj = MNG_NULL; + pData->pLastaniobj = MNG_NULL; +#ifdef MNG_SUPPORT_DYNAMICMNG + pData->pFirstevent = MNG_NULL; + pData->pLastevent = MNG_NULL; +#endif + /* no processing callbacks */ + pData->fDisplayrow = MNG_NULL; + pData->fRestbkgdrow = MNG_NULL; + pData->fCorrectrow = MNG_NULL; + pData->fRetrieverow = MNG_NULL; + pData->fStorerow = MNG_NULL; + pData->fProcessrow = MNG_NULL; + pData->fDifferrow = MNG_NULL; + pData->fScalerow = MNG_NULL; + pData->fDeltarow = MNG_NULL; +#ifndef MNG_SKIPCHUNK_PAST + pData->fFliprow = MNG_NULL; + pData->fTilerow = MNG_NULL; +#endif + pData->fInitrowproc = MNG_NULL; + + pData->iPLTEcount = 0; /* no PLTE data */ + +#ifndef MNG_SKIPCHUNK_DEFI + pData->iDEFIobjectid = 0; /* no DEFI data */ + pData->bDEFIhasdonotshow = MNG_FALSE; + pData->iDEFIdonotshow = 0; + pData->bDEFIhasconcrete = MNG_FALSE; + pData->iDEFIconcrete = 0; + pData->bDEFIhasloca = MNG_FALSE; + pData->iDEFIlocax = 0; + pData->iDEFIlocay = 0; + pData->bDEFIhasclip = MNG_FALSE; + pData->iDEFIclipl = 0; + pData->iDEFIclipr = 0; + pData->iDEFIclipt = 0; + pData->iDEFIclipb = 0; +#endif + +#ifndef MNG_SKIPCHUNK_BACK + pData->iBACKred = 0; /* no BACK data */ + pData->iBACKgreen = 0; + pData->iBACKblue = 0; + pData->iBACKmandatory = 0; + pData->iBACKimageid = 0; + pData->iBACKtile = 0; +#endif + +#ifndef MNG_SKIPCHUNK_FRAM + pData->iFRAMmode = 1; /* default global FRAM variables */ + pData->iFRAMdelay = 1; + pData->iFRAMtimeout = 0x7fffffffl; + pData->bFRAMclipping = MNG_FALSE; + pData->iFRAMclipl = 0; + pData->iFRAMclipr = 0; + pData->iFRAMclipt = 0; + pData->iFRAMclipb = 0; + + pData->iFramemode = 1; /* again for the current frame */ + pData->iFramedelay = 1; + pData->iFrametimeout = 0x7fffffffl; + pData->bFrameclipping = MNG_FALSE; + pData->iFrameclipl = 0; + pData->iFrameclipr = 0; + pData->iFrameclipt = 0; + pData->iFrameclipb = 0; + + pData->iNextdelay = 1; +#endif + +#ifndef MNG_SKIPCHUNK_SHOW + pData->iSHOWmode = 0; /* no SHOW data */ + pData->iSHOWfromid = 0; + pData->iSHOWtoid = 0; + pData->iSHOWnextid = 0; + pData->iSHOWskip = 0; +#endif + + pData->iGlobalPLTEcount = 0; /* no global PLTE data */ + + pData->iGlobalTRNSrawlen = 0; /* no global tRNS data */ + + pData->iGlobalGamma = 0; /* no global gAMA data */ + +#ifndef MNG_SKIPCHUNK_cHRM + pData->iGlobalWhitepointx = 0; /* no global cHRM data */ + pData->iGlobalWhitepointy = 0; + pData->iGlobalPrimaryredx = 0; + pData->iGlobalPrimaryredy = 0; + pData->iGlobalPrimarygreenx = 0; + pData->iGlobalPrimarygreeny = 0; + pData->iGlobalPrimarybluex = 0; + pData->iGlobalPrimarybluey = 0; +#endif + + pData->iGlobalRendintent = 0; /* no global sRGB data */ + +#ifndef MNG_SKIPCHUNK_iCCP + pData->iGlobalProfilesize = 0; /* no global iCCP data */ + pData->pGlobalProfile = MNG_NULL; +#endif + +#ifndef MNG_SKIPCHUNK_bKGD + pData->iGlobalBKGDred = 0; /* no global bKGD data */ + pData->iGlobalBKGDgreen = 0; + pData->iGlobalBKGDblue = 0; +#endif + /* no delta-image */ +#ifndef MNG_NO_DELTA_PNG + pData->pDeltaImage = MNG_NULL; + pData->iDeltaImagetype = 0; + pData->iDeltatype = 0; + pData->iDeltaBlockwidth = 0; + pData->iDeltaBlockheight = 0; + pData->iDeltaBlockx = 0; + pData->iDeltaBlocky = 0; + pData->bDeltaimmediate = MNG_FALSE; + + pData->fDeltagetrow = MNG_NULL; + pData->fDeltaaddrow = MNG_NULL; + pData->fDeltareplacerow = MNG_NULL; + pData->fDeltaputrow = MNG_NULL; + + pData->fPromoterow = MNG_NULL; + pData->fPromBitdepth = MNG_NULL; + pData->pPromBuf = MNG_NULL; + pData->iPromColortype = 0; + pData->iPromBitdepth = 0; + pData->iPromFilltype = 0; + pData->iPromWidth = 0; + pData->pPromSrc = MNG_NULL; + pData->pPromDst = MNG_NULL; +#endif + +#ifndef MNG_SKIPCHUNK_MAGN + pData->iMAGNfromid = 0; + pData->iMAGNtoid = 0; +#endif + +#ifndef MNG_SKIPCHUNK_PAST + pData->iPastx = 0; + pData->iPasty = 0; +#endif + + pData->pLastseek = MNG_NULL; +#endif + +#ifdef MNG_INCLUDE_ZLIB + pData->bInflating = 0; /* no inflating or deflating */ + pData->bDeflating = 0; /* going on at the moment */ +#endif + +#ifdef MNG_SUPPORT_DISPLAY /* reset object 0 */ + mng_reset_objzero (pData); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_RESET, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_cleanup (mng_handle* hHandle) +{ + mng_datap pData; /* local vars */ +#ifndef MNG_INTERNAL_MEMMNGMT + mng_memfree fFree; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)*hHandle), MNG_FN_CLEANUP, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (*hHandle) /* check validity handle */ + pData = ((mng_datap)(*hHandle)); /* and address main structure */ + + mng_reset (*hHandle); /* do an implicit reset to cleanup most stuff */ + +#ifdef MNG_SUPPORT_DISPLAY /* drop object 0 */ + mng_free_imageobject (pData, (mng_imagep)pData->pObjzero); +#endif + +#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_FULL_CMS) + if (pData->hProf2) /* output profile defined ? */ + mnglcms_freeprofile (pData->hProf2); + + if (pData->hProf3) /* sRGB profile defined ? */ + mnglcms_freeprofile (pData->hProf3); +#endif + +#ifdef MNG_INCLUDE_ZLIB + mngzlib_cleanup (pData); /* cleanup zlib stuff */ +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)*hHandle), MNG_FN_CLEANUP, MNG_LC_CLEANUP) +#endif + + pData->iMagic = 0; /* invalidate the actual memory */ + +#ifdef MNG_INTERNAL_MEMMNGMT + free ((void *)*hHandle); /* cleanup the data-structure */ +#else + fFree = ((mng_datap)*hHandle)->fMemfree; + fFree ((mng_ptr)*hHandle, sizeof (mng_data)); +#endif + + *hHandle = 0; /* wipe pointer to inhibit future use */ + + return MNG_NOERROR; /* and we're done */ +} + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +mng_retcode MNG_DECL mng_read (mng_handle hHandle) +{ + mng_datap pData; /* local vars */ + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_READ, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle and callbacks */ + pData = ((mng_datap)hHandle); /* and make it addressable */ + +#ifndef MNG_INTERNAL_MEMMNGMT + MNG_VALIDCB (hHandle, fMemalloc) + MNG_VALIDCB (hHandle, fMemfree) +#endif + +#ifndef MNG_NO_OPEN_CLOSE_STREAM + MNG_VALIDCB (hHandle, fOpenstream) + MNG_VALIDCB (hHandle, fClosestream) +#endif + MNG_VALIDCB (hHandle, fReaddata) + +#ifdef MNG_SUPPORT_DISPLAY /* valid at this point ? */ + if ((pData->bReading) || (pData->bDisplaying)) +#else + if (pData->bReading) +#endif + MNG_ERROR (pData, MNG_FUNCTIONINVALID); + +#ifdef MNG_SUPPORT_WRITE + if ((pData->bWriting) || (pData->bCreating)) + MNG_ERROR (pData, MNG_FUNCTIONINVALID); +#endif + + if (!pData->bCacheplayback) /* must store playback info to work!! */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID); + + cleanup_errors (pData); /* cleanup previous errors */ + + pData->bReading = MNG_TRUE; /* read only! */ + +#ifndef MNG_NO_OPEN_CLOSE_STREAM + if (pData->fOpenstream && !pData->fOpenstream (hHandle)) + /* open it and start reading */ + iRetcode = MNG_APPIOERROR; + else +#endif + iRetcode = mng_read_graphic (pData); + + if (pData->bEOF) /* already at EOF ? */ + { + pData->bReading = MNG_FALSE; /* then we're no longer reading */ + +#ifdef MNG_SUPPORT_DISPLAY + mng_reset_rundata (pData); /* reset rundata */ +#endif + } + + if (iRetcode) /* on error bail out */ + return iRetcode; + + if (pData->bSuspended) /* read suspension ? */ + { + iRetcode = MNG_NEEDMOREDATA; + pData->iSuspendtime = pData->fGettickcount ((mng_handle)pData); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_READ, MNG_LC_END); +#endif + + return iRetcode; +} +#endif /* MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +mng_retcode MNG_DECL mng_read_pushdata (mng_handle hHandle, + mng_ptr pData, + mng_size_t iLength, + mng_bool bTakeownership) +{ + mng_datap pMyData; /* local vars */ + mng_pushdatap pPush; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_READ_PUSHDATA, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pMyData = ((mng_datap)hHandle); /* and make it addressable */ + /* create a containing buffer */ + iRetcode = make_pushbuffer (pMyData, pData, iLength, bTakeownership, &pPush); + if (iRetcode) + return iRetcode; + + if (pMyData->pLastpushdata) /* and update the buffer chain */ + pMyData->pLastpushdata->pNext = pPush; + else + pMyData->pFirstpushdata = pPush; + + pMyData->pLastpushdata = pPush; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_READ_PUSHDATA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +mng_retcode MNG_DECL mng_read_pushsig (mng_handle hHandle, + mng_imgtype eSigtype) +{ + mng_datap pData; /* local vars */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_READ_PUSHSIG, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = ((mng_datap)hHandle); /* and make it addressable */ + + if (pData->bHavesig) /* can we expect this call ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID); + + pData->eSigtype = eSigtype; + pData->bHavesig = MNG_TRUE; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_READ_PUSHSIG, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +mng_retcode MNG_DECL mng_read_pushchunk (mng_handle hHandle, + mng_ptr pChunk, + mng_size_t iLength, + mng_bool bTakeownership) +{ + mng_datap pMyData; /* local vars */ + mng_pushdatap pPush; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_READ_PUSHCHUNK, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pMyData = ((mng_datap)hHandle); /* and make it addressable */ + /* create a containing buffer */ + iRetcode = make_pushbuffer (pMyData, pChunk, iLength, bTakeownership, &pPush); + if (iRetcode) + return iRetcode; + + if (pMyData->pLastpushchunk) /* and update the buffer chain */ + pMyData->pLastpushchunk->pNext = pPush; + else + pMyData->pFirstpushchunk = pPush; + + pMyData->pLastpushchunk = pPush; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_READ_PUSHCHUNK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +mng_retcode MNG_DECL mng_read_resume (mng_handle hHandle) +{ + mng_datap pData; /* local vars */ + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_READ_RESUME, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = ((mng_datap)hHandle); /* and make it addressable */ + /* can we expect this call ? */ + if ((!pData->bReading) || (!pData->bSuspended)) + MNG_ERROR (pData, MNG_FUNCTIONINVALID); + + cleanup_errors (pData); /* cleanup previous errors */ + + pData->bSuspended = MNG_FALSE; /* reset the flag */ + +#ifdef MNG_SUPPORT_DISPLAY /* re-synchronize ? */ + if ((pData->bDisplaying) && (pData->bRunning)) + pData->iSynctime = pData->iSynctime - pData->iSuspendtime + + pData->fGettickcount (hHandle); +#endif + + iRetcode = mng_read_graphic (pData); /* continue reading now */ + + if (pData->bEOF) /* at EOF ? */ + { + pData->bReading = MNG_FALSE; /* then we're no longer reading */ + +#ifdef MNG_SUPPORT_DISPLAY + mng_reset_rundata (pData); /* reset rundata */ +#endif + } + + if (iRetcode) /* on error bail out */ + return iRetcode; + + if (pData->bSuspended) /* read suspension ? */ + { + iRetcode = MNG_NEEDMOREDATA; + pData->iSuspendtime = pData->fGettickcount ((mng_handle)pData); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_READ_RESUME, MNG_LC_END); +#endif + + return iRetcode; +} +#endif /* MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_WRITE +mng_retcode MNG_DECL mng_write (mng_handle hHandle) +{ + mng_datap pData; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_WRITE, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle and callbacks */ + pData = ((mng_datap)hHandle); /* and make it addressable */ + +#ifndef MNG_INTERNAL_MEMMNGMT + MNG_VALIDCB (hHandle, fMemalloc) + MNG_VALIDCB (hHandle, fMemfree) +#endif + +#ifndef MNG_NO_OPEN_CLOSE_STREAM + MNG_VALIDCB (hHandle, fOpenstream) + MNG_VALIDCB (hHandle, fClosestream) +#endif + MNG_VALIDCB (hHandle, fWritedata) + +#ifdef MNG_SUPPORT_READ + if (pData->bReading) /* valid at this point ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID); +#endif + + cleanup_errors (pData); /* cleanup previous errors */ + + iRetcode = mng_write_graphic (pData);/* do the write */ + + if (iRetcode) /* on error bail out */ + return iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_WRITE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_WRITE */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_WRITE +mng_retcode MNG_DECL mng_create (mng_handle hHandle) +{ + mng_datap pData; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_CREATE, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle and callbacks */ + pData = ((mng_datap)hHandle); /* and make it addressable */ + +#ifndef MNG_INTERNAL_MEMMNGMT + MNG_VALIDCB (hHandle, fMemalloc) + MNG_VALIDCB (hHandle, fMemfree) +#endif + +#ifdef MNG_SUPPORT_READ + if (pData->bReading) /* valid at this point ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID); +#endif + + if ((pData->bWriting) || (pData->bCreating)) + MNG_ERROR (pData, MNG_FUNCTIONINVALID); + + cleanup_errors (pData); /* cleanup previous errors */ + + iRetcode = mng_reset (hHandle); /* clear any previous stuff */ + + if (iRetcode) /* on error bail out */ + return iRetcode; + + pData->bCreating = MNG_TRUE; /* indicate we're creating a new file */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_CREATE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_WRITE */ + +/* ************************************************************************** */ + +#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_SUPPORT_READ) +mng_retcode MNG_DECL mng_readdisplay (mng_handle hHandle) +{ + mng_datap pData; /* local vars */ + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_READDISPLAY, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle and callbacks */ + pData = ((mng_datap)hHandle); /* and make it addressable */ + +#ifndef MNG_INTERNAL_MEMMNGMT + MNG_VALIDCB (hHandle, fMemalloc) + MNG_VALIDCB (hHandle, fMemfree) +#endif + + MNG_VALIDCB (hHandle, fReaddata) + MNG_VALIDCB (hHandle, fGetcanvasline) + MNG_VALIDCB (hHandle, fRefresh) + MNG_VALIDCB (hHandle, fGettickcount) + MNG_VALIDCB (hHandle, fSettimer) + /* valid at this point ? */ + if ((pData->bReading) || (pData->bDisplaying)) + MNG_ERROR (pData, MNG_FUNCTIONINVALID); + +#ifdef MNG_SUPPORT_WRITE + if ((pData->bWriting) || (pData->bCreating)) + MNG_ERROR (pData, MNG_FUNCTIONINVALID); +#endif + + cleanup_errors (pData); /* cleanup previous errors */ + + pData->bReading = MNG_TRUE; /* read & display! */ + pData->bDisplaying = MNG_TRUE; + pData->bRunning = MNG_TRUE; + pData->iFrameseq = 0; + pData->iLayerseq = 0; + pData->iFrametime = 0; + pData->iRequestframe = 0; + pData->iRequestlayer = 0; + pData->iRequesttime = 0; + pData->bSearching = MNG_FALSE; + pData->iRuntime = 0; + pData->iSynctime = pData->fGettickcount (hHandle); + pData->iSuspendtime = 0; + pData->iStarttime = pData->iSynctime; + pData->iEndtime = 0; + +#ifndef MNG_NO_OPEN_CLOSE_STREAM + if (pData->fOpenstream && !pData->fOpenstream (hHandle)) + /* open it and start reading */ + iRetcode = MNG_APPIOERROR; + else +#endif + iRetcode = mng_read_graphic (pData); + + if (pData->bEOF) /* already at EOF ? */ + { + pData->bReading = MNG_FALSE; /* then we're no longer reading */ + mng_drop_invalid_objects (pData); /* drop invalidly stored objects */ + } + + if (iRetcode) /* on error bail out */ + return iRetcode; + + if (pData->bSuspended) /* read suspension ? */ + { + iRetcode = MNG_NEEDMOREDATA; + pData->iSuspendtime = pData->fGettickcount ((mng_handle)pData); + } + else + if (pData->bTimerset) /* indicate timer break ? */ + iRetcode = MNG_NEEDTIMERWAIT; + else + if (pData->bSectionwait) /* indicate section break ? */ + iRetcode = MNG_NEEDSECTIONWAIT; + else + { /* no breaks = end of run */ + pData->bRunning = MNG_FALSE; + + if (pData->bFreezing) /* dynamic MNG reached SEEK ? */ + pData->bFreezing = MNG_FALSE; /* reset it ! */ + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_READDISPLAY, MNG_LC_END); +#endif + + return iRetcode; +} +#endif /* MNG_SUPPORT_DISPLAY && MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_retcode MNG_DECL mng_display (mng_handle hHandle) +{ + mng_datap pData; /* local vars */ + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle and callbacks */ + pData = ((mng_datap)hHandle); /* and make it addressable */ + +#ifndef MNG_INTERNAL_MEMMNGMT + MNG_VALIDCB (hHandle, fMemalloc) + MNG_VALIDCB (hHandle, fMemfree) +#endif + + MNG_VALIDCB (hHandle, fGetcanvasline) + MNG_VALIDCB (hHandle, fRefresh) + MNG_VALIDCB (hHandle, fGettickcount) + MNG_VALIDCB (hHandle, fSettimer) + + if (pData->bDisplaying) /* valid at this point ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID); + +#ifdef MNG_SUPPORT_READ + if (pData->bReading) + MNG_ERROR (pData, MNG_FUNCTIONINVALID); +#endif + +#ifdef MNG_SUPPORT_WRITE + if ((pData->bWriting) || (pData->bCreating)) + MNG_ERROR (pData, MNG_FUNCTIONINVALID); +#endif + + cleanup_errors (pData); /* cleanup previous errors */ + + pData->bDisplaying = MNG_TRUE; /* display! */ + pData->bRunning = MNG_TRUE; + pData->iFrameseq = 0; + pData->iLayerseq = 0; + pData->iFrametime = 0; + pData->iRequestframe = 0; + pData->iRequestlayer = 0; + pData->iRequesttime = 0; + pData->bSearching = MNG_FALSE; + pData->iRuntime = 0; + pData->iSynctime = pData->fGettickcount (hHandle); +#ifdef MNG_SUPPORT_READ + pData->iSuspendtime = 0; +#endif + pData->iStarttime = pData->iSynctime; + pData->iEndtime = 0; + pData->pCurraniobj = pData->pFirstaniobj; + /* go do it */ + iRetcode = mng_process_display (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + if (pData->bTimerset) /* indicate timer break ? */ + iRetcode = MNG_NEEDTIMERWAIT; + else + { /* no breaks = end of run */ + pData->bRunning = MNG_FALSE; + + if (pData->bFreezing) /* dynamic MNG reached SEEK ? */ + pData->bFreezing = MNG_FALSE; /* reset it ! */ + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY, MNG_LC_END); +#endif + + return iRetcode; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_retcode MNG_DECL mng_display_resume (mng_handle hHandle) +{ + mng_datap pData; /* local vars */ + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY_RESUME, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = ((mng_datap)hHandle); /* and make it addressable */ + + if (!pData->bDisplaying) /* can we expect this call ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID); + + cleanup_errors (pData); /* cleanup previous errors */ + /* was it running ? */ + if ((pData->bRunning) || (pData->bReading)) + { /* are we expecting this call ? */ + if ((pData->bTimerset) || (pData->bSuspended) || (pData->bSectionwait)) + { + pData->bTimerset = MNG_FALSE; /* reset the flags */ + pData->bSectionwait = MNG_FALSE; + +#ifdef MNG_SUPPORT_READ + if (pData->bReading) /* set during read&display ? */ + { + if (pData->bSuspended) /* calculate proper synchronization */ + pData->iSynctime = pData->iSynctime - pData->iSuspendtime + + pData->fGettickcount (hHandle); + else + pData->iSynctime = pData->fGettickcount (hHandle); + + pData->bSuspended = MNG_FALSE; /* now reset this flag */ + /* and continue reading */ + iRetcode = mng_read_graphic (pData); + + if (pData->bEOF) /* already at EOF ? */ + { + pData->bReading = MNG_FALSE; /* then we're no longer reading */ + /* drop invalidly stored objects */ + mng_drop_invalid_objects (pData); + } + } + else +#endif /* MNG_SUPPORT_READ */ + { /* synchronize timing */ + pData->iSynctime = pData->fGettickcount (hHandle); + /* resume display processing */ + iRetcode = mng_process_display (pData); + } + } + else + { + MNG_ERROR (pData, MNG_FUNCTIONINVALID); + } + } + else + { /* synchronize timing */ + pData->iSynctime = pData->fGettickcount (hHandle); + pData->bRunning = MNG_TRUE; /* it's restarted again ! */ + /* resume display processing */ + iRetcode = mng_process_display (pData); + } + + if (iRetcode) /* on error bail out */ + return iRetcode; + + if (pData->bSuspended) /* read suspension ? */ + { + iRetcode = MNG_NEEDMOREDATA; + pData->iSuspendtime = pData->fGettickcount ((mng_handle)pData); + } + else + if (pData->bTimerset) /* indicate timer break ? */ + iRetcode = MNG_NEEDTIMERWAIT; + else + if (pData->bSectionwait) /* indicate section break ? */ + iRetcode = MNG_NEEDSECTIONWAIT; + else + { /* no breaks = end of run */ + pData->bRunning = MNG_FALSE; + + if (pData->bFreezing) /* trying to freeze ? */ + pData->bFreezing = MNG_FALSE; /* then we're there */ + + if (pData->bResetting) /* trying to reset as well ? */ + { /* full stop!!! */ + pData->bDisplaying = MNG_FALSE; + + iRetcode = mng_reset_rundata (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY_RESUME, MNG_LC_END); +#endif + + return iRetcode; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_retcode MNG_DECL mng_display_freeze (mng_handle hHandle) +{ + mng_datap pData; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY_FREEZE, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = ((mng_datap)hHandle); /* and make it addressable */ + /* can we expect this call ? */ + if ((!pData->bDisplaying) || (pData->bReading)) + MNG_ERROR (pData, MNG_FUNCTIONINVALID); + + cleanup_errors (pData); /* cleanup previous errors */ + + if (pData->bRunning) /* is it running ? */ + { + mng_retcode iRetcode; + + pData->bFreezing = MNG_TRUE; /* indicate we need to freeze */ + /* continue "normal" processing */ + iRetcode = mng_display_resume (hHandle); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY_FREEZE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_retcode MNG_DECL mng_display_reset (mng_handle hHandle) +{ + mng_datap pData; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY_RESET, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = ((mng_datap)hHandle); /* and make it addressable */ + /* can we expect this call ? */ + if ((!pData->bDisplaying) || (pData->bReading)) + MNG_ERROR (pData, MNG_FUNCTIONINVALID); + + if (!pData->bCacheplayback) /* must store playback info to work!! */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID); + + cleanup_errors (pData); /* cleanup previous errors */ + + if (pData->bRunning) /* is it running ? */ + { + pData->bFreezing = MNG_TRUE; /* indicate we need to freeze */ + pData->bResetting = MNG_TRUE; /* indicate we're about to reset too */ + /* continue normal processing ? */ + iRetcode = mng_display_resume (hHandle); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + else + { /* full stop!!! */ + pData->bDisplaying = MNG_FALSE; + + iRetcode = mng_reset_rundata (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY_RESET, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +#ifndef MNG_NO_DISPLAY_GO_SUPPORTED +mng_retcode MNG_DECL mng_display_goframe (mng_handle hHandle, + mng_uint32 iFramenr) +{ + mng_datap pData; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY_GOFRAME, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = ((mng_datap)hHandle); /* and make it addressable */ + + if (pData->eImagetype != mng_it_mng) /* is it an animation ? */ + MNG_ERROR (pData, MNG_NOTANANIMATION); + /* can we expect this call ? */ + if ((!pData->bDisplaying) || (pData->bRunning)) + MNG_ERROR ((mng_datap)hHandle, MNG_FUNCTIONINVALID); + + if (!pData->bCacheplayback) /* must store playback info to work!! */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID); + + if (iFramenr > pData->iTotalframes) /* is the parameter within bounds ? */ + MNG_ERROR (pData, MNG_FRAMENRTOOHIGH); + /* within MHDR bounds ? */ + if ((pData->iFramecount) && (iFramenr > pData->iFramecount)) + MNG_WARNING (pData, MNG_FRAMENRTOOHIGH); + + cleanup_errors (pData); /* cleanup previous errors */ + + if (pData->iFrameseq > iFramenr) /* search from current or go back to start ? */ + { + iRetcode = mng_reset_rundata (pData); + if (iRetcode) /* on error bail out */ + return iRetcode; + } + + if (iFramenr) + { + pData->iRequestframe = iFramenr; /* go find the requested frame then */ + iRetcode = mng_process_display (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + pData->bTimerset = MNG_FALSE; /* reset just to be safe */ + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY_GOFRAME, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +#ifndef MNG_NO_DISPLAY_GO_SUPPORTED +mng_retcode MNG_DECL mng_display_golayer (mng_handle hHandle, + mng_uint32 iLayernr) +{ + mng_datap pData; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY_GOLAYER, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = ((mng_datap)hHandle); /* and make it addressable */ + + if (pData->eImagetype != mng_it_mng) /* is it an animation ? */ + MNG_ERROR (pData, MNG_NOTANANIMATION); + /* can we expect this call ? */ + if ((!pData->bDisplaying) || (pData->bRunning)) + MNG_ERROR (pData, MNG_FUNCTIONINVALID); + + if (!pData->bCacheplayback) /* must store playback info to work!! */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID); + + if (iLayernr > pData->iTotallayers) /* is the parameter within bounds ? */ + MNG_ERROR (pData, MNG_LAYERNRTOOHIGH); + /* within MHDR bounds ? */ + if ((pData->iLayercount) && (iLayernr > pData->iLayercount)) + MNG_WARNING (pData, MNG_LAYERNRTOOHIGH); + + cleanup_errors (pData); /* cleanup previous errors */ + + if (pData->iLayerseq > iLayernr) /* search from current or go back to start ? */ + { + iRetcode = mng_reset_rundata (pData); + if (iRetcode) /* on error bail out */ + return iRetcode; + } + + if (iLayernr) + { + pData->iRequestlayer = iLayernr; /* go find the requested layer then */ + iRetcode = mng_process_display (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + pData->bTimerset = MNG_FALSE; /* reset just to be safe */ + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY_GOLAYER, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +#ifndef MNG_NO_DISPLAY_GO_SUPPORTED +mng_retcode MNG_DECL mng_display_gotime (mng_handle hHandle, + mng_uint32 iPlaytime) +{ + mng_datap pData; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY_GOTIME, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = ((mng_datap)hHandle); /* and make it addressable */ + + if (pData->eImagetype != mng_it_mng) /* is it an animation ? */ + MNG_ERROR (pData, MNG_NOTANANIMATION); + /* can we expect this call ? */ + if ((!pData->bDisplaying) || (pData->bRunning)) + MNG_ERROR (pData, MNG_FUNCTIONINVALID); + + if (!pData->bCacheplayback) /* must store playback info to work!! */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID); + /* is the parameter within bounds ? */ + if (iPlaytime > pData->iTotalplaytime) + MNG_ERROR (pData, MNG_PLAYTIMETOOHIGH); + /* within MHDR bounds ? */ + if ((pData->iPlaytime) && (iPlaytime > pData->iPlaytime)) + MNG_WARNING (pData, MNG_PLAYTIMETOOHIGH); + + cleanup_errors (pData); /* cleanup previous errors */ + + if (pData->iFrametime > iPlaytime) /* search from current or go back to start ? */ + { + iRetcode = mng_reset_rundata (pData); + if (iRetcode) /* on error bail out */ + return iRetcode; + } + + if (iPlaytime) + { + pData->iRequesttime = iPlaytime; /* go find the requested playtime then */ + iRetcode = mng_process_display (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + pData->bTimerset = MNG_FALSE; /* reset just to be safe */ + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_DISPLAY_GOTIME, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#if defined(MNG_SUPPORT_DISPLAY) && defined(MNG_SUPPORT_DYNAMICMNG) +mng_retcode MNG_DECL mng_trapevent (mng_handle hHandle, + mng_uint8 iEventtype, + mng_int32 iX, + mng_int32 iY) +{ + mng_datap pData; + mng_eventp pEvent; + mng_bool bFound = MNG_FALSE; + mng_retcode iRetcode; + mng_imagep pImage; + mng_uint8p pPixel; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_TRAPEVENT, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = ((mng_datap)hHandle); /* and make it addressable */ + + if (pData->eImagetype != mng_it_mng) /* is it an animation ? */ + MNG_ERROR (pData, MNG_NOTANANIMATION); + + if (!pData->bDisplaying) /* can we expect this call ? */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID); + + if (!pData->bCacheplayback) /* must store playback info to work!! */ + MNG_ERROR (pData, MNG_FUNCTIONINVALID); + /* let's find a matching event object */ + pEvent = (mng_eventp)pData->pFirstevent; + + while ((pEvent) && (!bFound)) + { /* matching eventtype ? */ + if (pEvent->iEventtype == iEventtype) + { + switch (pEvent->iMasktype) /* check X/Y on basis of masktype */ + { + case MNG_MASK_NONE : /* no mask is easy */ + { + bFound = MNG_TRUE; + break; + } + + case MNG_MASK_BOX : /* inside the given box ? */ + { /* right- and bottom-border don't count ! */ + if ((iX >= pEvent->iLeft) && (iX < pEvent->iRight) && + (iY >= pEvent->iTop) && (iY < pEvent->iBottom)) + bFound = MNG_TRUE; + break; + } + + case MNG_MASK_OBJECT : /* non-zero pixel in the image object ? */ + { + pImage = mng_find_imageobject (pData, pEvent->iObjectid); + /* valid image ? */ + if ((pImage) && (pImage->pImgbuf->iBitdepth <= 8) && + ((pImage->pImgbuf->iColortype == 0) || (pImage->pImgbuf->iColortype == 3)) && + ((mng_int32)pImage->pImgbuf->iWidth > iX) && + ((mng_int32)pImage->pImgbuf->iHeight > iY)) + { + pPixel = pImage->pImgbuf->pImgdata + ((pImage->pImgbuf->iWidth * iY) + iX); + + if (*pPixel) /* non-zero ? */ + bFound = MNG_TRUE; + } + + break; + } + + case MNG_MASK_OBJECTIX : /* pixel in the image object matches index ? */ + { + pImage = mng_find_imageobject (pData, pEvent->iObjectid); + /* valid image ? */ + if ((pImage) && (pImage->pImgbuf->iBitdepth <= 8) && + ((pImage->pImgbuf->iColortype == 0) || (pImage->pImgbuf->iColortype == 3)) && + ((mng_int32)pImage->pImgbuf->iWidth > iX) && (iX >= 0) && + ((mng_int32)pImage->pImgbuf->iHeight > iY) && (iY >= 0)) + { + pPixel = pImage->pImgbuf->pImgdata + ((pImage->pImgbuf->iWidth * iY) + iX); + /* matching index ? */ + if (*pPixel == pEvent->iIndex) + bFound = MNG_TRUE; + } + + break; + } + + case MNG_MASK_BOXOBJECT : /* non-zero pixel in the image object ? */ + { + mng_int32 iTempx = iX - pEvent->iLeft; + mng_int32 iTempy = iY - pEvent->iTop; + + pImage = mng_find_imageobject (pData, pEvent->iObjectid); + /* valid image ? */ + if ((pImage) && (pImage->pImgbuf->iBitdepth <= 8) && + ((pImage->pImgbuf->iColortype == 0) || (pImage->pImgbuf->iColortype == 3)) && + (iTempx < (mng_int32)pImage->pImgbuf->iWidth) && + (iTempx >= 0) && (iX < pEvent->iRight) && + (iTempy < (mng_int32)pImage->pImgbuf->iHeight) && + (iTempy >= 0) && (iY < pEvent->iBottom)) + { + pPixel = pImage->pImgbuf->pImgdata + ((pImage->pImgbuf->iWidth * iTempy) + iTempx); + + if (*pPixel) /* non-zero ? */ + bFound = MNG_TRUE; + } + + break; + } + + case MNG_MASK_BOXOBJECTIX : /* pixel in the image object matches index ? */ + { + mng_int32 iTempx = iX - pEvent->iLeft; + mng_int32 iTempy = iY - pEvent->iTop; + + pImage = mng_find_imageobject (pData, pEvent->iObjectid); + /* valid image ? */ + if ((pImage) && (pImage->pImgbuf->iBitdepth <= 8) && + ((pImage->pImgbuf->iColortype == 0) || (pImage->pImgbuf->iColortype == 3)) && + (iTempx < (mng_int32)pImage->pImgbuf->iWidth) && + (iTempx >= 0) && (iX < pEvent->iRight) && + (iTempy < (mng_int32)pImage->pImgbuf->iHeight) && + (iTempy >= 0) && (iY < pEvent->iBottom)) + { + pPixel = pImage->pImgbuf->pImgdata + ((pImage->pImgbuf->iWidth * iTempy) + iTempx); + /* matching index ? */ + if (*pPixel == pEvent->iIndex) + bFound = MNG_TRUE; + } + + break; + } + + } + } + + if (!bFound) /* try the next one */ + pEvent = (mng_eventp)pEvent->sHeader.pNext; + } + /* found one that's not the last mousemove ? */ + if ((pEvent) && ((mng_objectp)pEvent != pData->pLastmousemove)) + { /* can we start an event process now ? */ + if ((!pData->bReading) && (!pData->bRunning)) + { + pData->iEventx = iX; /* save coordinates */ + pData->iEventy = iY; + /* do it then ! */ + iRetcode = pEvent->sHeader.fProcess (pData, pEvent); + + if (iRetcode) /* on error bail out */ + return iRetcode; + /* remember last mousemove event */ + if (pEvent->iEventtype == MNG_EVENT_MOUSEMOVE) + pData->pLastmousemove = (mng_objectp)pEvent; + else + pData->pLastmousemove = MNG_NULL; + } + else + { + + /* TODO: store unprocessed events or not ??? */ + + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_TRAPEVENT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_getlasterror (mng_handle hHandle, + mng_int8* iSeverity, + mng_chunkid* iChunkname, + mng_uint32* iChunkseq, + mng_int32* iExtra1, + mng_int32* iExtra2, + mng_pchar* zErrortext) +{ + mng_datap pData; /* local vars */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETLASTERROR, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) /* check validity handle */ + pData = ((mng_datap)hHandle); /* and make it addressable */ + + *iSeverity = pData->iSeverity; /* return the appropriate fields */ + +#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE) + *iChunkname = pData->iChunkname; + *iChunkseq = pData->iChunkseq; +#else + *iChunkname = MNG_UINT_HUH; + *iChunkseq = 0; +#endif + + *iExtra1 = pData->iErrorx1; + *iExtra2 = pData->iErrorx2; + *zErrortext = pData->zErrortext; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GETLASTERROR, MNG_LC_END); +#endif + + return pData->iErrorcode; /* and the errorcode */ +} + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ + + diff --git a/Engine/lib/lmng/libmng_jpeg.c b/Engine/lib/lmng/libmng_jpeg.c new file mode 100644 index 000000000..5042e1d55 --- /dev/null +++ b/Engine/lib/lmng/libmng_jpeg.c @@ -0,0 +1,1088 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_jpeg.c copyright (c) 2000-2004 G.Juyn * */ +/* * version : 1.0.9 * */ +/* * * */ +/* * purpose : JPEG library interface (implementation) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : implementation of the JPEG library interface * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed strict-ANSI stuff * */ +/* * * */ +/* * 0.5.2 - 05/22/2000 - G.Juyn * */ +/* * - implemented all the JNG routines * */ +/* * * */ +/* * 0.5.3 - 06/17/2000 - G.Juyn * */ +/* * - added tracing of JPEG calls * */ +/* * 0.5.3 - 06/24/2000 - G.Juyn * */ +/* * - fixed inclusion of IJG read/write code * */ +/* * 0.5.3 - 06/29/2000 - G.Juyn * */ +/* * - fixed some 64-bit warnings * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 0.9.3 - 10/16/2000 - G.Juyn * */ +/* * - added support for JDAA * */ +/* * * */ +/* * 1.0.1 - 04/19/2001 - G.Juyn * */ +/* * - added export of JPEG functions for DLL * */ +/* * 1.0.1 - 04/22/2001 - G.Juyn * */ +/* * - fixed memory-leaks (Thanks Gregg!) * */ +/* * * */ +/* * 1.0.4 - 06/22/2002 - G.Juyn * */ +/* * - B526138 - returned IJGSRC6B calling convention to * */ +/* * default for MSVC * */ +/* * * */ +/* * 1.0.5 - 24/02/2003 - G.Juyn * */ +/* * - B683152 - libjpeg suspension not always honored correctly* */ +/* * * */ +/* * 1.0.6 - 03/04/2003 - G.Juyn * */ +/* * - fixed some compiler-warnings * */ +/* * * */ +/* * 1.0.8 - 08/01/2004 - G.Juyn * */ +/* * - added support for 3+byte pixelsize for JPEG's * */ +/* * * */ +/* * 1.0.9 - 12/20/2004 - G.Juyn * */ +/* * - cleaned up macro-invocations (thanks to D. Airlie) * */ +/* * * */ +/* ************************************************************************** */ + +#include "libmng.h" +#include "libmng_data.h" +#include "libmng_error.h" +#include "libmng_trace.h" +#ifdef __BORLANDC__ +#pragma hdrstop +#endif +#include "libmng_memory.h" +#include "libmng_pixels.h" +#include "libmng_jpeg.h" + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +/* ************************************************************************** */ + +#if defined(MNG_INCLUDE_JNG) && defined(MNG_INCLUDE_DISPLAY_PROCS) + +/* ************************************************************************** */ +/* * * */ +/* * Local IJG callback routines (source-manager, error-manager and such) * */ +/* * * */ +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_IJG6B + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG_READ +#ifdef MNG_DEFINE_JPEG_STDCALL +void MNG_DECL mng_init_source (j_decompress_ptr cinfo) +#else +void mng_init_source (j_decompress_ptr cinfo) +#endif +{ + return; /* nothing needed */ +} +#endif /* MNG_INCLUDE_JNG_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG_READ +#ifdef MNG_DEFINE_JPEG_STDCALL +boolean MNG_DECL mng_fill_input_buffer (j_decompress_ptr cinfo) +#else +boolean mng_fill_input_buffer (j_decompress_ptr cinfo) +#endif +{ + return FALSE; /* force IJG routine to return to caller */ +} +#endif /* MNG_INCLUDE_JNG_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG_READ +#ifdef MNG_DEFINE_JPEG_STDCALL +void MNG_DECL mng_skip_input_data (j_decompress_ptr cinfo, long num_bytes) +#else +void mng_skip_input_data (j_decompress_ptr cinfo, long num_bytes) +#endif +{ + if (num_bytes > 0) /* ignore fony calls */ + { /* address my generic structure */ + mng_datap pData = (mng_datap)cinfo->client_data; + /* address source manager */ + mngjpeg_sourcep pSrc = pData->pJPEGdinfo->src; + /* problem scenario ? */ + if (pSrc->bytes_in_buffer < (size_t)num_bytes) + { /* tell the boss we need to skip some data! */ + pData->iJPEGtoskip = (mng_uint32)((size_t)num_bytes - pSrc->bytes_in_buffer); + + pSrc->bytes_in_buffer = 0; /* let the JPEG lib suspend */ + pSrc->next_input_byte = MNG_NULL; + } + else + { /* simply advance in the buffer */ + pSrc->bytes_in_buffer -= num_bytes; + pSrc->next_input_byte += num_bytes; + } + } + + return; +} +#endif /* MNG_INCLUDE_JNG_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG_READ +#ifdef MNG_DEFINE_JPEG_STDCALL +void MNG_DECL mng_skip_input_data2 (j_decompress_ptr cinfo, long num_bytes) +#else +void mng_skip_input_data2 (j_decompress_ptr cinfo, long num_bytes) +#endif +{ + if (num_bytes > 0) /* ignore fony calls */ + { /* address my generic structure */ + mng_datap pData = (mng_datap)cinfo->client_data; + /* address source manager */ + mngjpeg_sourcep pSrc = pData->pJPEGdinfo2->src; + /* problem scenario ? */ + if (pSrc->bytes_in_buffer < (size_t)num_bytes) + { /* tell the boss we need to skip some data! */ + pData->iJPEGtoskip2 = (mng_uint32)((size_t)num_bytes - pSrc->bytes_in_buffer); + + pSrc->bytes_in_buffer = 0; /* let the JPEG lib suspend */ + pSrc->next_input_byte = MNG_NULL; + } + else + { /* simply advance in the buffer */ + pSrc->bytes_in_buffer -= num_bytes; + pSrc->next_input_byte += num_bytes; + } + } + + return; +} +#endif /* MNG_INCLUDE_JNG_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG_READ +#ifdef MNG_DEFINE_JPEG_STDCALL +void MNG_DECL mng_term_source (j_decompress_ptr cinfo) +#else +void mng_term_source (j_decompress_ptr cinfo) +#endif +{ + return; /* nothing needed */ +} +#endif /* MNG_INCLUDE_JNG_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_USE_SETJMP +#ifdef MNG_DEFINE_JPEG_STDCALL +void MNG_DECL mng_error_exit (j_common_ptr cinfo) +#else +void mng_error_exit (j_common_ptr cinfo) +#endif +{ /* address my generic structure */ + mng_datap pData = (mng_datap)cinfo->client_data; + +#ifdef MNG_ERROR_TELLTALE /* fill the message text ??? */ + (*cinfo->err->output_message) (cinfo); +#endif + /* return to the point of no return... */ + longjmp (pData->sErrorbuf, cinfo->err->msg_code); +} +#endif /* MNG_USE_SETJMP */ + +/* ************************************************************************** */ + +#ifdef MNG_USE_SETJMP +#ifdef MNG_DEFINE_JPEG_STDCALL +void MNG_DECL mng_output_message (j_common_ptr cinfo) +#else +void mng_output_message (j_common_ptr cinfo) +#endif +{ + return; /* just do nothing ! */ +} +#endif /* MNG_USE_SETJMP */ + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_IJG6B */ + +/* ************************************************************************** */ +/* * * */ +/* * Global JPEG routines * */ +/* * * */ +/* ************************************************************************** */ + +mng_retcode mngjpeg_initialize (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_INITIALIZE, MNG_LC_START); +#endif + /* allocate space for JPEG structures if necessary */ +#ifdef MNG_INCLUDE_JNG_READ + if (pData->pJPEGderr == MNG_NULL) + MNG_ALLOC (pData, pData->pJPEGderr, sizeof (mngjpeg_error )); + if (pData->pJPEGdsrc == MNG_NULL) + MNG_ALLOC (pData, pData->pJPEGdsrc, sizeof (mngjpeg_source)); + if (pData->pJPEGdinfo == MNG_NULL) + MNG_ALLOC (pData, pData->pJPEGdinfo, sizeof (mngjpeg_decomp)); + /* enable reverse addressing */ + pData->pJPEGdinfo->client_data = pData; + + if (pData->pJPEGderr2 == MNG_NULL) + MNG_ALLOC (pData, pData->pJPEGderr2, sizeof (mngjpeg_error )); + if (pData->pJPEGdsrc2 == MNG_NULL) + MNG_ALLOC (pData, pData->pJPEGdsrc2, sizeof (mngjpeg_source)); + if (pData->pJPEGdinfo2 == MNG_NULL) + MNG_ALLOC (pData, pData->pJPEGdinfo2, sizeof (mngjpeg_decomp)); + /* enable reverse addressing */ + pData->pJPEGdinfo2->client_data = pData; +#endif + +#ifdef MNG_INCLUDE_JNG_WRITE + if (pData->pJPEGcerr == MNG_NULL) + MNG_ALLOC (pData, pData->pJPEGcerr, sizeof (mngjpeg_error )); + if (pData->pJPEGcinfo == MNG_NULL) + MNG_ALLOC (pData, pData->pJPEGcinfo, sizeof (mngjpeg_comp )); + /* enable reverse addressing */ + pData->pJPEGcinfo->client_data = pData; +#endif + + if (pData->pJPEGbuf == MNG_NULL) /* initialize temporary buffers */ + { + pData->iJPEGbufmax = MNG_JPEG_MAXBUF; + MNG_ALLOC (pData, pData->pJPEGbuf, pData->iJPEGbufmax); + } + + if (pData->pJPEGbuf2 == MNG_NULL) + { + pData->iJPEGbufmax2 = MNG_JPEG_MAXBUF; + MNG_ALLOC (pData, pData->pJPEGbuf2, pData->iJPEGbufmax2); + } + + pData->pJPEGcurrent = pData->pJPEGbuf; + pData->iJPEGbufremain = 0; + pData->pJPEGrow = MNG_NULL; + pData->iJPEGrowlen = 0; + pData->iJPEGtoskip = 0; + + pData->pJPEGcurrent2 = pData->pJPEGbuf2; + pData->iJPEGbufremain2 = 0; + pData->pJPEGrow2 = MNG_NULL; + pData->iJPEGrowlen2 = 0; + pData->iJPEGtoskip2 = 0; + /* not doing anything yet ! */ + pData->bJPEGcompress = MNG_FALSE; + + pData->bJPEGdecompress = MNG_FALSE; + pData->bJPEGhasheader = MNG_FALSE; + pData->bJPEGdecostarted = MNG_FALSE; + pData->bJPEGscanstarted = MNG_FALSE; + pData->bJPEGscanending = MNG_FALSE; + + pData->bJPEGdecompress2 = MNG_FALSE; + pData->bJPEGhasheader2 = MNG_FALSE; + pData->bJPEGdecostarted2 = MNG_FALSE; + pData->bJPEGscanstarted2 = MNG_FALSE; + + pData->iJPEGrow = 0; /* zero input/output lines */ + pData->iJPEGalpharow = 0; + pData->iJPEGrgbrow = 0; + pData->iJPEGdisprow = 0; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_INITIALIZE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mngjpeg_cleanup (mng_datap pData) +{ +#if defined(MNG_INCLUDE_IJG6B) && defined(MNG_USE_SETJMP) + mng_retcode iRetcode; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_CLEANUP, MNG_LC_START); +#endif + +#ifdef MNG_INCLUDE_IJG6B +#ifdef MNG_USE_SETJMP + iRetcode = setjmp (pData->sErrorbuf);/* setup local JPEG error-recovery */ + if (iRetcode != 0) /* got here from longjmp ? */ + MNG_ERRORJ (pData, iRetcode); /* then IJG-lib issued an error */ +#endif + +#ifdef MNG_INCLUDE_JNG_READ /* still decompressing something ? */ + if (pData->bJPEGdecompress) + jpeg_destroy_decompress (pData->pJPEGdinfo); + if (pData->bJPEGdecompress2) + jpeg_destroy_decompress (pData->pJPEGdinfo2); +#endif + +#ifdef MNG_INCLUDE_JNG_WRITE + if (pData->bJPEGcompress) /* still compressing something ? */ + jpeg_destroy_compress (pData->pJPEGcinfo); +#endif + +#endif /* MNG_INCLUDE_IJG6B */ + /* cleanup temporary buffers */ + MNG_FREE (pData, pData->pJPEGbuf2, pData->iJPEGbufmax2); + MNG_FREE (pData, pData->pJPEGbuf, pData->iJPEGbufmax); + /* cleanup space for JPEG structures */ +#ifdef MNG_INCLUDE_JNG_WRITE + MNG_FREE (pData, pData->pJPEGcinfo, sizeof (mngjpeg_comp )); + MNG_FREE (pData, pData->pJPEGcerr, sizeof (mngjpeg_error )); +#endif + +#ifdef MNG_INCLUDE_JNG_READ + MNG_FREE (pData, pData->pJPEGdinfo, sizeof (mngjpeg_decomp)); + MNG_FREE (pData, pData->pJPEGdsrc, sizeof (mngjpeg_source)); + MNG_FREE (pData, pData->pJPEGderr, sizeof (mngjpeg_error )); + MNG_FREE (pData, pData->pJPEGdinfo2, sizeof (mngjpeg_decomp)); + MNG_FREE (pData, pData->pJPEGdsrc2, sizeof (mngjpeg_source)); + MNG_FREE (pData, pData->pJPEGderr2, sizeof (mngjpeg_error )); +#endif + + MNG_FREE (pData, pData->pJPEGrow2, pData->iJPEGrowlen2); + MNG_FREE (pData, pData->pJPEGrow, pData->iJPEGrowlen); + /* whatever we were doing ... */ + /* we don't anymore ... */ + pData->bJPEGcompress = MNG_FALSE; + + pData->bJPEGdecompress = MNG_FALSE; + pData->bJPEGhasheader = MNG_FALSE; + pData->bJPEGdecostarted = MNG_FALSE; + pData->bJPEGscanstarted = MNG_FALSE; + pData->bJPEGscanending = MNG_FALSE; + + pData->bJPEGdecompress2 = MNG_FALSE; + pData->bJPEGhasheader2 = MNG_FALSE; + pData->bJPEGdecostarted2 = MNG_FALSE; + pData->bJPEGscanstarted2 = MNG_FALSE; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_CLEANUP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ +/* * * */ +/* * JPEG decompression routines (JDAT) * */ +/* * * */ +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG_READ +mng_retcode mngjpeg_decompressinit (mng_datap pData) +{ +#if defined(MNG_INCLUDE_IJG6B) && defined(MNG_USE_SETJMP) + mng_retcode iRetcode; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSINIT, MNG_LC_START); +#endif + +#ifdef MNG_INCLUDE_IJG6B + /* allocate and initialize a JPEG decompression object */ + pData->pJPEGdinfo->err = jpeg_std_error (pData->pJPEGderr); + +#ifdef MNG_USE_SETJMP /* setup local JPEG error-routines */ + pData->pJPEGderr->error_exit = mng_error_exit; + pData->pJPEGderr->output_message = mng_output_message; + + iRetcode = setjmp (pData->sErrorbuf);/* setup local JPEG error-recovery */ + if (iRetcode != 0) /* got here from longjmp ? */ + MNG_ERRORJ (pData, iRetcode); /* then IJG-lib issued an error */ +#endif /* MNG_USE_SETJMP */ + + /* allocate and initialize a JPEG decompression object (continued) */ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSINIT, MNG_LC_JPEG_CREATE_DECOMPRESS) +#endif + jpeg_create_decompress (pData->pJPEGdinfo); + + pData->bJPEGdecompress = MNG_TRUE; /* indicate it's initialized */ + + /* specify the source of the compressed data (eg, a file) */ + /* no, not a file; we have buffered input */ + pData->pJPEGdinfo->src = pData->pJPEGdsrc; + /* use the default handler */ + pData->pJPEGdinfo->src->resync_to_restart = jpeg_resync_to_restart; + /* setup local source routine & parms */ + pData->pJPEGdinfo->src->init_source = mng_init_source; + pData->pJPEGdinfo->src->fill_input_buffer = mng_fill_input_buffer; + pData->pJPEGdinfo->src->skip_input_data = mng_skip_input_data; + pData->pJPEGdinfo->src->term_source = mng_term_source; + pData->pJPEGdinfo->src->next_input_byte = pData->pJPEGcurrent; + pData->pJPEGdinfo->src->bytes_in_buffer = pData->iJPEGbufremain; + +#endif /* MNG_INCLUDE_IJG6B */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSINIT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_INCLUDE_JNG_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG_READ +mng_retcode mngjpeg_decompressdata (mng_datap pData, + mng_uint32 iRawsize, + mng_uint8p pRawdata) +{ + mng_retcode iRetcode; + mng_uint32 iRemain; + mng_uint8p pWork; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_START); +#endif + +#if defined (MNG_INCLUDE_IJG6B) && defined(MNG_USE_SETJMP) + iRetcode = setjmp (pData->sErrorbuf);/* initialize local JPEG error-recovery */ + if (iRetcode != 0) /* got here from longjmp ? */ + MNG_ERRORJ (pData, iRetcode); /* then IJG-lib issued an error */ +#endif + + pWork = pRawdata; + iRemain = iRawsize; + + if (pData->iJPEGtoskip) /* JPEG-lib told us to skip some more data ? */ + { + if (iRemain > pData->iJPEGtoskip) /* enough data in this buffer ? */ + { + iRemain -= pData->iJPEGtoskip; /* skip enough to access the next byte */ + pWork += pData->iJPEGtoskip; + + pData->iJPEGtoskip = 0; /* no more to skip then */ + } + else + { + pData->iJPEGtoskip -= iRemain; /* skip all data in the buffer */ + iRemain = 0; /* and indicate this accordingly */ + } + /* the skip set current-pointer to NULL ! */ + pData->pJPEGcurrent = pData->pJPEGbuf; + } + + while (iRemain) /* repeat until no more input-bytes */ + { /* need to shift anything ? */ + if ((pData->pJPEGcurrent > pData->pJPEGbuf) && + (pData->pJPEGcurrent - pData->pJPEGbuf + pData->iJPEGbufremain + iRemain > pData->iJPEGbufmax)) + { + if (pData->iJPEGbufremain > 0) /* then do so */ + MNG_COPY (pData->pJPEGbuf, pData->pJPEGcurrent, pData->iJPEGbufremain); + + pData->pJPEGcurrent = pData->pJPEGbuf; + } + /* does the remaining input fit into the buffer ? */ + if (pData->iJPEGbufremain + iRemain <= pData->iJPEGbufmax) + { /* move the lot */ + MNG_COPY ((pData->pJPEGcurrent + pData->iJPEGbufremain), pWork, iRemain); + + pData->iJPEGbufremain += iRemain;/* adjust remaining_bytes counter */ + iRemain = 0; /* and indicate there's no input left */ + } + else + { /* calculate what does fit */ + mng_uint32 iFits = pData->iJPEGbufmax - pData->iJPEGbufremain; + + if (iFits <= 0) /* no space is just bugger 'm all */ + MNG_ERROR (pData, MNG_JPEGBUFTOOSMALL); + /* move that */ + MNG_COPY ((pData->pJPEGcurrent + pData->iJPEGbufremain), pWork, iFits); + + pData->iJPEGbufremain += iFits; /* adjust remain_bytes counter */ + iRemain -= iFits; /* and the input-parms */ + pWork += iFits; + } + +#ifdef MNG_INCLUDE_IJG6B + pData->pJPEGdinfo->src->next_input_byte = pData->pJPEGcurrent; + pData->pJPEGdinfo->src->bytes_in_buffer = pData->iJPEGbufremain; + + if (!pData->bJPEGhasheader) /* haven't got the header yet ? */ + { + /* call jpeg_read_header() to obtain image info */ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_JPEG_READ_HEADER) +#endif + if (jpeg_read_header (pData->pJPEGdinfo, TRUE) != JPEG_SUSPENDED) + { /* indicate the header's oke */ + pData->bJPEGhasheader = MNG_TRUE; + /* let's do some sanity checks ! */ + if ((pData->pJPEGdinfo->image_width != pData->iDatawidth ) || + (pData->pJPEGdinfo->image_height != pData->iDataheight) ) + MNG_ERROR (pData, MNG_JPEGPARMSERR); + + if ( ((pData->iJHDRcolortype == MNG_COLORTYPE_JPEGGRAY ) || + (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGGRAYA) ) && + (pData->pJPEGdinfo->jpeg_color_space != JCS_GRAYSCALE ) ) + MNG_ERROR (pData, MNG_JPEGPARMSERR); + + if ( ((pData->iJHDRcolortype == MNG_COLORTYPE_JPEGCOLOR ) || + (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGCOLORA) ) && + (pData->pJPEGdinfo->jpeg_color_space != JCS_YCbCr ) ) + MNG_ERROR (pData, MNG_JPEGPARMSERR); + /* indicate whether or not it's progressive */ + pData->bJPEGprogressive = (mng_bool)jpeg_has_multiple_scans (pData->pJPEGdinfo); + /* progressive+alpha can't display "on-the-fly"!! */ + if ((pData->bJPEGprogressive) && + ((pData->iJHDRcolortype == MNG_COLORTYPE_JPEGGRAYA ) || + (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGCOLORA) )) + pData->fDisplayrow = MNG_NULL; + /* allocate a row of JPEG-samples */ + if (pData->pJPEGdinfo->jpeg_color_space == JCS_YCbCr) + pData->iJPEGrowlen = pData->pJPEGdinfo->image_width * RGB_PIXELSIZE; + else + pData->iJPEGrowlen = pData->pJPEGdinfo->image_width; + + MNG_ALLOC (pData, pData->pJPEGrow, pData->iJPEGrowlen); + + pData->iJPEGrgbrow = 0; /* quite empty up to now */ + } + + pData->pJPEGcurrent = (mng_uint8p)pData->pJPEGdinfo->src->next_input_byte; + pData->iJPEGbufremain = (mng_uint32)pData->pJPEGdinfo->src->bytes_in_buffer; + } + /* decompress not started ? */ + if ((pData->bJPEGhasheader) && (!pData->bJPEGdecostarted)) + { + /* set parameters for decompression */ + + if (pData->bJPEGprogressive) /* progressive display ? */ + pData->pJPEGdinfo->buffered_image = TRUE; + + /* jpeg_start_decompress(...); */ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_JPEG_START_DECOMPRESS) +#endif + if (jpeg_start_decompress (pData->pJPEGdinfo) == TRUE) + /* indicate it started */ + pData->bJPEGdecostarted = MNG_TRUE; + + pData->pJPEGcurrent = (mng_uint8p)pData->pJPEGdinfo->src->next_input_byte; + pData->iJPEGbufremain = (mng_uint32)pData->pJPEGdinfo->src->bytes_in_buffer; + } + /* process some scanlines ? */ + if ((pData->bJPEGhasheader) && (pData->bJPEGdecostarted) && + ((!jpeg_input_complete (pData->pJPEGdinfo)) || + (pData->pJPEGdinfo->output_scanline < pData->pJPEGdinfo->output_height) || + ((pData->bJPEGprogressive) && (pData->bJPEGscanending)))) + { + mng_int32 iLines = 0; + + /* for (each output pass) */ + do + { /* address the row output buffer */ + JSAMPROW pRow = (JSAMPROW)pData->pJPEGrow; + + /* init new pass ? */ + if ((pData->bJPEGprogressive) && (!pData->bJPEGscanstarted)) + { + pData->bJPEGscanstarted = MNG_TRUE; + + /* adjust output decompression parameters if required */ + /* nop */ + + /* start a new output pass */ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_JPEG_START_OUTPUT) +#endif + jpeg_start_output (pData->pJPEGdinfo, pData->pJPEGdinfo->input_scan_number); + + pData->iJPEGrow = 0; /* start at row 0 in the image again */ + } + + /* while (scan lines remain to be read) */ + if ((!pData->bJPEGprogressive) || (!pData->bJPEGscanending)) + { + do + { + /* jpeg_read_scanlines(...); */ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_JPEG_READ_SCANLINES) +#endif + iLines = jpeg_read_scanlines (pData->pJPEGdinfo, (JSAMPARRAY)&pRow, 1); + + pData->pJPEGcurrent = (mng_uint8p)pData->pJPEGdinfo->src->next_input_byte; + pData->iJPEGbufremain = (mng_uint32)pData->pJPEGdinfo->src->bytes_in_buffer; + + if (iLines > 0) /* got something ? */ + { + if (pData->fStorerow2) /* store in object ? */ + { + iRetcode = ((mng_storerow)pData->fStorerow2) (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + } + } + } + while ((pData->pJPEGdinfo->output_scanline < pData->pJPEGdinfo->output_height) && + (iLines > 0)); /* until end-of-image or not enough input-data */ + } + + /* terminate output pass */ + if ((pData->bJPEGprogressive) && + (pData->pJPEGdinfo->output_scanline >= pData->pJPEGdinfo->output_height)) + { +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_JPEG_FINISH_OUTPUT) +#endif + if (jpeg_finish_output (pData->pJPEGdinfo) != JPEG_SUSPENDED) + { /* this scan has ended */ + pData->bJPEGscanstarted = MNG_FALSE; + pData->bJPEGscanending = MNG_FALSE; + } + else + { + pData->bJPEGscanending = MNG_TRUE; + } + } + } + while ((!jpeg_input_complete (pData->pJPEGdinfo)) && + (iLines > 0) && (!pData->bJPEGscanending)); + } + /* end of image ? */ + if ((pData->bJPEGhasheader) && (pData->bJPEGdecostarted) && + (!pData->bJPEGscanending) && (jpeg_input_complete (pData->pJPEGdinfo)) && + (pData->pJPEGdinfo->input_scan_number == pData->pJPEGdinfo->output_scan_number)) + { + /* jpeg_finish_decompress(...); */ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_JPEG_FINISH_DECOMPRESS) +#endif + if (jpeg_finish_decompress (pData->pJPEGdinfo) == TRUE) + { /* indicate it's done */ + pData->bJPEGhasheader = MNG_FALSE; + pData->bJPEGdecostarted = MNG_FALSE; + pData->pJPEGcurrent = (mng_uint8p)pData->pJPEGdinfo->src->next_input_byte; + pData->iJPEGbufremain = (mng_uint32)pData->pJPEGdinfo->src->bytes_in_buffer; + /* remaining fluff is an error ! */ + if ((pData->iJPEGbufremain > 0) || (iRemain > 0)) + MNG_ERROR (pData, MNG_TOOMUCHJDAT); + } + } +#endif /* MNG_INCLUDE_IJG6B */ + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_INCLUDE_JNG_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG_READ +mng_retcode mngjpeg_decompressfree (mng_datap pData) +{ +#if defined(MNG_INCLUDE_IJG6B) && defined(MNG_USE_SETJMP) + mng_retcode iRetcode; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSFREE, MNG_LC_START); +#endif + +#ifdef MNG_INCLUDE_IJG6B +#ifdef MNG_USE_SETJMP + iRetcode = setjmp (pData->sErrorbuf);/* setup local JPEG error-recovery */ + if (iRetcode != 0) /* got here from longjmp ? */ + MNG_ERRORJ (pData, iRetcode); /* then IJG-lib issued an error */ +#endif + /* free the row of JPEG-samples*/ + MNG_FREE (pData, pData->pJPEGrow, pData->iJPEGrowlen); + + /* release the JPEG decompression object */ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSFREE, MNG_LC_JPEG_DESTROY_DECOMPRESS) +#endif + jpeg_destroy_decompress (pData->pJPEGdinfo); + + pData->bJPEGdecompress = MNG_FALSE; /* indicate it's done */ + +#endif /* MNG_INCLUDE_IJG6B */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSFREE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_INCLUDE_JNG_READ */ + +/* ************************************************************************** */ +/* * * */ +/* * JPEG decompression routines (JDAA) * */ +/* * * */ +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG_READ +mng_retcode mngjpeg_decompressinit2 (mng_datap pData) +{ +#if defined(MNG_INCLUDE_IJG6B) && defined(MNG_USE_SETJMP) + mng_retcode iRetcode; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSINIT, MNG_LC_START); +#endif + +#ifdef MNG_INCLUDE_IJG6B + /* allocate and initialize a JPEG decompression object */ + pData->pJPEGdinfo2->err = jpeg_std_error (pData->pJPEGderr2); + +#ifdef MNG_USE_SETJMP /* setup local JPEG error-routines */ + pData->pJPEGderr2->error_exit = mng_error_exit; + pData->pJPEGderr2->output_message = mng_output_message; + + iRetcode = setjmp (pData->sErrorbuf);/* setup local JPEG error-recovery */ + if (iRetcode != 0) /* got here from longjmp ? */ + MNG_ERRORJ (pData, iRetcode); /* then IJG-lib issued an error */ +#endif /* MNG_USE_SETJMP */ + + /* allocate and initialize a JPEG decompression object (continued) */ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSINIT, MNG_LC_JPEG_CREATE_DECOMPRESS) +#endif + jpeg_create_decompress (pData->pJPEGdinfo2); + + pData->bJPEGdecompress2 = MNG_TRUE; /* indicate it's initialized */ + + /* specify the source of the compressed data (eg, a file) */ + /* no, not a file; we have buffered input */ + pData->pJPEGdinfo2->src = pData->pJPEGdsrc2; + /* use the default handler */ + pData->pJPEGdinfo2->src->resync_to_restart = jpeg_resync_to_restart; + /* setup local source routine & parms */ + pData->pJPEGdinfo2->src->init_source = mng_init_source; + pData->pJPEGdinfo2->src->fill_input_buffer = mng_fill_input_buffer; + pData->pJPEGdinfo2->src->skip_input_data = mng_skip_input_data2; + pData->pJPEGdinfo2->src->term_source = mng_term_source; + pData->pJPEGdinfo2->src->next_input_byte = pData->pJPEGcurrent2; + pData->pJPEGdinfo2->src->bytes_in_buffer = pData->iJPEGbufremain2; + +#endif /* MNG_INCLUDE_IJG6B */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSINIT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_INCLUDE_JNG_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG_READ +mng_retcode mngjpeg_decompressdata2 (mng_datap pData, + mng_uint32 iRawsize, + mng_uint8p pRawdata) +{ + mng_retcode iRetcode; + mng_uint32 iRemain; + mng_uint8p pWork; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_START); +#endif + +#if defined (MNG_INCLUDE_IJG6B) && defined(MNG_USE_SETJMP) + iRetcode = setjmp (pData->sErrorbuf);/* initialize local JPEG error-recovery */ + if (iRetcode != 0) /* got here from longjmp ? */ + MNG_ERRORJ (pData, iRetcode); /* then IJG-lib issued an error */ +#endif + + pWork = pRawdata; + iRemain = iRawsize; + + if (pData->iJPEGtoskip2) /* JPEG-lib told us to skip some more data ? */ + { + if (iRemain > pData->iJPEGtoskip2) /* enough data in this buffer ? */ + { + iRemain -= pData->iJPEGtoskip2; /* skip enough to access the next byte */ + pWork += pData->iJPEGtoskip2; + + pData->iJPEGtoskip2 = 0; /* no more to skip then */ + } + else + { + pData->iJPEGtoskip2 -= iRemain; /* skip all data in the buffer */ + iRemain = 0; /* and indicate this accordingly */ + } + /* the skip set current-pointer to NULL ! */ + pData->pJPEGcurrent2 = pData->pJPEGbuf2; + } + + while (iRemain) /* repeat until no more input-bytes */ + { /* need to shift anything ? */ + if ((pData->pJPEGcurrent2 > pData->pJPEGbuf2) && + (pData->pJPEGcurrent2 - pData->pJPEGbuf2 + pData->iJPEGbufremain2 + iRemain > pData->iJPEGbufmax2)) + { + if (pData->iJPEGbufremain2 > 0) /* then do so */ + MNG_COPY (pData->pJPEGbuf2, pData->pJPEGcurrent2, pData->iJPEGbufremain2); + + pData->pJPEGcurrent2 = pData->pJPEGbuf2; + } + /* does the remaining input fit into the buffer ? */ + if (pData->iJPEGbufremain2 + iRemain <= pData->iJPEGbufmax2) + { /* move the lot */ + MNG_COPY ((pData->pJPEGcurrent2 + pData->iJPEGbufremain2), pWork, iRemain); + /* adjust remaining_bytes counter */ + pData->iJPEGbufremain2 += iRemain; + iRemain = 0; /* and indicate there's no input left */ + } + else + { /* calculate what does fit */ + mng_uint32 iFits = pData->iJPEGbufmax2 - pData->iJPEGbufremain2; + + if (iFits <= 0) /* no space is just bugger 'm all */ + MNG_ERROR (pData, MNG_JPEGBUFTOOSMALL); + /* move that */ + MNG_COPY ((pData->pJPEGcurrent2 + pData->iJPEGbufremain2), pWork, iFits); + + pData->iJPEGbufremain2 += iFits; /* adjust remain_bytes counter */ + iRemain -= iFits; /* and the input-parms */ + pWork += iFits; + } + +#ifdef MNG_INCLUDE_IJG6B + pData->pJPEGdinfo2->src->next_input_byte = pData->pJPEGcurrent2; + pData->pJPEGdinfo2->src->bytes_in_buffer = pData->iJPEGbufremain2; + + if (!pData->bJPEGhasheader2) /* haven't got the header yet ? */ + { + /* call jpeg_read_header() to obtain image info */ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_JPEG_READ_HEADER) +#endif + if (jpeg_read_header (pData->pJPEGdinfo2, TRUE) != JPEG_SUSPENDED) + { /* indicate the header's oke */ + pData->bJPEGhasheader2 = MNG_TRUE; + /* let's do some sanity checks ! */ + if ((pData->pJPEGdinfo2->image_width != pData->iDatawidth ) || + (pData->pJPEGdinfo2->image_height != pData->iDataheight) ) + MNG_ERROR (pData, MNG_JPEGPARMSERR); + + if (pData->pJPEGdinfo2->jpeg_color_space != JCS_GRAYSCALE) + MNG_ERROR (pData, MNG_JPEGPARMSERR); + /* indicate whether or not it's progressive */ + pData->bJPEGprogressive2 = (mng_bool)jpeg_has_multiple_scans (pData->pJPEGdinfo2); + + if (pData->bJPEGprogressive2) /* progressive alphachannel not allowed !!! */ + MNG_ERROR (pData, MNG_JPEGPARMSERR); + /* allocate a row of JPEG-samples */ + if (pData->pJPEGdinfo2->jpeg_color_space == JCS_YCbCr) + pData->iJPEGrowlen2 = pData->pJPEGdinfo2->image_width * RGB_PIXELSIZE; + else + pData->iJPEGrowlen2 = pData->pJPEGdinfo2->image_width; + + MNG_ALLOC (pData, pData->pJPEGrow2, pData->iJPEGrowlen2); + + pData->iJPEGalpharow = 0; /* quite empty up to now */ + } + + pData->pJPEGcurrent2 = (mng_uint8p)pData->pJPEGdinfo2->src->next_input_byte; + pData->iJPEGbufremain2 = (mng_uint32)pData->pJPEGdinfo2->src->bytes_in_buffer; + } + /* decompress not started ? */ + if ((pData->bJPEGhasheader2) && (!pData->bJPEGdecostarted2)) + { + /* set parameters for decompression */ + + if (pData->bJPEGprogressive2) /* progressive display ? */ + pData->pJPEGdinfo2->buffered_image = TRUE; + + /* jpeg_start_decompress(...); */ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_JPEG_START_DECOMPRESS) +#endif + if (jpeg_start_decompress (pData->pJPEGdinfo2) == TRUE) + /* indicate it started */ + pData->bJPEGdecostarted2 = MNG_TRUE; + + pData->pJPEGcurrent2 = (mng_uint8p)pData->pJPEGdinfo2->src->next_input_byte; + pData->iJPEGbufremain2 = (mng_uint32)pData->pJPEGdinfo2->src->bytes_in_buffer; + } + /* process some scanlines ? */ + if ((pData->bJPEGhasheader2) && (pData->bJPEGdecostarted2) && + ((!jpeg_input_complete (pData->pJPEGdinfo2)) || + (pData->pJPEGdinfo2->output_scanline < pData->pJPEGdinfo2->output_height))) + { + mng_int32 iLines; + + /* for (each output pass) */ + do + { /* address the row output buffer */ + JSAMPROW pRow = (JSAMPROW)pData->pJPEGrow2; + + /* init new pass ? */ + if ((pData->bJPEGprogressive2) && + ((!pData->bJPEGscanstarted2) || + (pData->pJPEGdinfo2->output_scanline >= pData->pJPEGdinfo2->output_height))) + { + pData->bJPEGscanstarted2 = MNG_TRUE; + + /* adjust output decompression parameters if required */ + /* nop */ + + /* start a new output pass */ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_JPEG_START_OUTPUT) +#endif + jpeg_start_output (pData->pJPEGdinfo2, pData->pJPEGdinfo2->input_scan_number); + + pData->iJPEGrow = 0; /* start at row 0 in the image again */ + } + + /* while (scan lines remain to be read) */ + do + { + /* jpeg_read_scanlines(...); */ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_JPEG_READ_SCANLINES) +#endif + iLines = jpeg_read_scanlines (pData->pJPEGdinfo2, (JSAMPARRAY)&pRow, 1); + + pData->pJPEGcurrent2 = (mng_uint8p)pData->pJPEGdinfo2->src->next_input_byte; + pData->iJPEGbufremain2 = (mng_uint32)pData->pJPEGdinfo2->src->bytes_in_buffer; + + if (iLines > 0) /* got something ? */ + { + if (pData->fStorerow3) /* store in object ? */ + { + iRetcode = ((mng_storerow)pData->fStorerow3) (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + } + } + } + while ((pData->pJPEGdinfo2->output_scanline < pData->pJPEGdinfo2->output_height) && + (iLines > 0)); /* until end-of-image or not enough input-data */ + + /* terminate output pass */ + if ((pData->bJPEGprogressive2) && + (pData->pJPEGdinfo2->output_scanline >= pData->pJPEGdinfo2->output_height)) + { +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_JPEG_FINISH_OUTPUT) +#endif + if (jpeg_finish_output (pData->pJPEGdinfo2) == JPEG_SUSPENDED) + jpeg_finish_output (pData->pJPEGdinfo2); + /* this scan has ended */ + pData->bJPEGscanstarted2 = MNG_FALSE; + } + } + while ((!jpeg_input_complete (pData->pJPEGdinfo2)) && (iLines > 0)); + } + /* end of image ? */ + if ((pData->bJPEGhasheader2) && (pData->bJPEGdecostarted2) && + (jpeg_input_complete (pData->pJPEGdinfo2)) && + (pData->pJPEGdinfo2->input_scan_number == pData->pJPEGdinfo2->output_scan_number)) + { + /* jpeg_finish_decompress(...); */ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_JPEG_FINISH_DECOMPRESS) +#endif + if (jpeg_finish_decompress (pData->pJPEGdinfo2) == TRUE) + { /* indicate it's done */ + pData->bJPEGhasheader2 = MNG_FALSE; + pData->bJPEGdecostarted2 = MNG_FALSE; + pData->pJPEGcurrent2 = (mng_uint8p)pData->pJPEGdinfo2->src->next_input_byte; + pData->iJPEGbufremain2 = (mng_uint32)pData->pJPEGdinfo2->src->bytes_in_buffer; + /* remaining fluff is an error ! */ + if ((pData->iJPEGbufremain2 > 0) || (iRemain > 0)) + MNG_ERROR (pData, MNG_TOOMUCHJDAT); + } + } +#endif /* MNG_INCLUDE_IJG6B */ + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSDATA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_INCLUDE_JNG_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG_READ +mng_retcode mngjpeg_decompressfree2 (mng_datap pData) +{ +#if defined(MNG_INCLUDE_IJG6B) && defined(MNG_USE_SETJMP) + mng_retcode iRetcode; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSFREE, MNG_LC_START); +#endif + +#ifdef MNG_INCLUDE_IJG6B +#ifdef MNG_USE_SETJMP + iRetcode = setjmp (pData->sErrorbuf);/* setup local JPEG error-recovery */ + if (iRetcode != 0) /* got here from longjmp ? */ + MNG_ERRORJ (pData, iRetcode); /* then IJG-lib issued an error */ +#endif + /* free the row of JPEG-samples*/ + MNG_FREE (pData, pData->pJPEGrow2, pData->iJPEGrowlen2); + + /* release the JPEG decompression object */ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSFREE, MNG_LC_JPEG_DESTROY_DECOMPRESS) +#endif + jpeg_destroy_decompress (pData->pJPEGdinfo2); + + pData->bJPEGdecompress2 = MNG_FALSE; /* indicate it's done */ + +#endif /* MNG_INCLUDE_IJG6B */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_JPEG_DECOMPRESSFREE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_INCLUDE_JNG_READ */ + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_JNG && MNG_INCLUDE_DISPLAY_PROCS */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ + diff --git a/Engine/lib/lmng/libmng_jpeg.h b/Engine/lib/lmng/libmng_jpeg.h new file mode 100644 index 000000000..a072af9c9 --- /dev/null +++ b/Engine/lib/lmng/libmng_jpeg.h @@ -0,0 +1,57 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_jpeg.h copyright (c) 2000-2002 G.Juyn * */ +/* * version : 1.0.0 * */ +/* * * */ +/* * purpose : JPEG library interface (definition) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : Definition of the JPEG library interface * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed strict-ANSI stuff * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 0.9.3 - 10/16/2000 - G.Juyn * */ +/* * - added support for JDAA * */ +/* * * */ +/* ************************************************************************** */ + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +#ifndef _libmng_jpeg_h_ +#define _libmng_jpeg_h_ + +/* ************************************************************************** */ + +mng_retcode mngjpeg_initialize (mng_datap pData); +mng_retcode mngjpeg_cleanup (mng_datap pData); + +mng_retcode mngjpeg_decompressinit (mng_datap pData); +mng_retcode mngjpeg_decompressdata (mng_datap pData, + mng_uint32 iRawsize, + mng_uint8p pRawdata); +mng_retcode mngjpeg_decompressfree (mng_datap pData); + +mng_retcode mngjpeg_decompressinit2 (mng_datap pData); +mng_retcode mngjpeg_decompressdata2 (mng_datap pData, + mng_uint32 iRawsize, + mng_uint8p pRawdata); +mng_retcode mngjpeg_decompressfree2 (mng_datap pData); + +/* ************************************************************************** */ + +#endif /* _libmng_jpeg_h_ */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ diff --git a/Engine/lib/lmng/libmng_memory.h b/Engine/lib/lmng/libmng_memory.h new file mode 100644 index 000000000..b92d0c13d --- /dev/null +++ b/Engine/lib/lmng/libmng_memory.h @@ -0,0 +1,64 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_memory.h copyright (c) 2000-2003 G.Juyn * */ +/* * version : 1.0.0 * */ +/* * * */ +/* * purpose : Memory management (definition) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : Definition of memory management functions * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed strict-ANSI stuff * */ +/* * * */ +/* * 0.5.3 - 06/12/2000 - G.Juyn * */ +/* * - swapped MNG_COPY parameter-names * */ +/* * 0.5.3 - 06/27/2000 - G.Juyn * */ +/* * - changed size parameter to mng_size_t * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* ************************************************************************** */ + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +#ifndef _libmng_memory_h_ +#define _libmng_memory_h_ + +/* ************************************************************************** */ +/* * * */ +/* * Generic memory manager macros * */ +/* * * */ +/* ************************************************************************** */ + +#ifdef MNG_INTERNAL_MEMMNGMT +#define MNG_ALLOC(H,P,L) { P = calloc (1, (mng_size_t)(L)); \ + if (P == 0) { MNG_ERROR (H, MNG_OUTOFMEMORY) } } +#define MNG_ALLOCX(H,P,L) { P = calloc (1, (mng_size_t)(L)); } +#define MNG_FREE(H,P,L) { if (P) { free (P); P = 0; } } +#define MNG_FREEX(H,P,L) { if (P) free (P); } +#else +#define MNG_ALLOC(H,P,L) { P = H->fMemalloc ((mng_size_t)(L)); \ + if (P == 0) { MNG_ERROR (H, MNG_OUTOFMEMORY) } } +#define MNG_ALLOCX(H,P,L) { P = H->fMemalloc ((mng_size_t)(L)); } +#define MNG_FREE(H,P,L) { if (P) { H->fMemfree (P, (mng_size_t)(L)); P = 0; } } +#define MNG_FREEX(H,P,L) { if (P) { H->fMemfree (P, (mng_size_t)(L)); } } +#endif /* mng_internal_memmngmt */ + +#define MNG_COPY(D,S,L) { memcpy (D, S, (mng_size_t)(L)); } + +/* ************************************************************************** */ + +#endif /* _libmng_memory_h_ */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ diff --git a/Engine/lib/lmng/libmng_object_prc.c b/Engine/lib/lmng/libmng_object_prc.c new file mode 100644 index 000000000..f6691ff42 --- /dev/null +++ b/Engine/lib/lmng/libmng_object_prc.c @@ -0,0 +1,6998 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_object_prc.c copyright (c) 2000-2007 G.Juyn * */ +/* * version : 1.0.10 * */ +/* * * */ +/* * purpose : Object processing routines (implementation) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : implementation of the internal object processing routines * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed strict-ANSI stuff * */ +/* * 0.5.1 - 05/12/2000 - G.Juyn * */ +/* * - changed trace to macro for callback error-reporting * */ +/* * * */ +/* * 0.5.2 - 05/20/2000 - G.Juyn * */ +/* * - fixed to support JNG objects * */ +/* * 0.5.2 - 05/24/2000 - G.Juyn * */ +/* * - added support for global color-chunks in animation * */ +/* * - added support for global PLTE,tRNS,bKGD in animation * */ +/* * - added SAVE & SEEK animation objects * */ +/* * 0.5.2 - 05/29/2000 - G.Juyn * */ +/* * - added initialization of framenr/layernr/playtime * */ +/* * - changed ani_object create routines not to return the * */ +/* * created object (wasn't necessary) * */ +/* * 0.5.2 - 05/30/2000 - G.Juyn * */ +/* * - added object promotion routine (PROM handling) * */ +/* * - added ani-object routines for delta-image processing * */ +/* * - added compression/filter/interlace fields to * */ +/* * object-buffer for delta-image processing * */ +/* * * */ +/* * 0.5.3 - 06/17/2000 - G.Juyn * */ +/* * - changed support for delta-image processing * */ +/* * 0.5.3 - 06/20/2000 - G.Juyn * */ +/* * - fixed some small things (as precaution) * */ +/* * 0.5.3 - 06/21/2000 - G.Juyn * */ +/* * - added processing of PLTE/tRNS & color-info for * */ +/* * delta-images in the ani_objects chain * */ +/* * 0.5.3 - 06/22/2000 - G.Juyn * */ +/* * - added support for PPLT chunk * */ +/* * * */ +/* * 0.9.1 - 07/07/2000 - G.Juyn * */ +/* * - added support for freeze/restart/resume & go_xxxx * */ +/* * 0.9.1 - 07/16/2000 - G.Juyn * */ +/* * - fixed support for mng_display() after mng_read() * */ +/* * * */ +/* * 0.9.2 - 07/29/2000 - G.Juyn * */ +/* * - fixed small bugs in display processing * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 0.9.3 - 08/07/2000 - G.Juyn * */ +/* * - B111300 - fixup for improved portability * */ +/* * 0.9.3 - 08/26/2000 - G.Juyn * */ +/* * - added MAGN chunk * */ +/* * 0.9.3 - 09/10/2000 - G.Juyn * */ +/* * - fixed DEFI behavior * */ +/* * 0.9.3 - 10/17/2000 - G.Juyn * */ +/* * - added valid-flag to stored objects for read() / display()* */ +/* * - added routine to discard "invalid" objects * */ +/* * 0.9.3 - 10/18/2000 - G.Juyn * */ +/* * - fixed delta-processing behavior * */ +/* * 0.9.3 - 10/19/2000 - G.Juyn * */ +/* * - added storage for pixel-/alpha-sampledepth for delta's * */ +/* * * */ +/* * 0.9.4 - 1/18/2001 - G.Juyn * */ +/* * - removed "old" MAGN methods 3 & 4 * */ +/* * - added "new" MAGN methods 3, 4 & 5 * */ +/* * * */ +/* * 0.9.5 - 1/22/2001 - G.Juyn * */ +/* * - B129681 - fixed compiler warnings SGI/Irix * */ +/* * * */ +/* * 1.0.2 - 06/23/2001 - G.Juyn * */ +/* * - added optimization option for MNG-video playback * */ +/* * * */ +/* * 1.0.5 - 08/15/2002 - G.Juyn * */ +/* * - completed PROM support * */ +/* * 1.0.5 - 08/16/2002 - G.Juyn * */ +/* * - completed MAGN support (16-bit functions) * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * 1.0.5 - 09/13/2002 - G.Juyn * */ +/* * - fixed read/write of MAGN chunk * */ +/* * 1.0.5 - 09/15/2002 - G.Juyn * */ +/* * - added event handling for dynamic MNG * */ +/* * 1.0.5 - 09/20/2002 - G.Juyn * */ +/* * - added support for PAST * */ +/* * 1.0.5 - 09/23/2002 - G.Juyn * */ +/* * - fixed reset_object_detail to clear old buffer * */ +/* * - added in-memory color-correction of abstract images * */ +/* * 1.0.5 - 10/05/2002 - G.Juyn * */ +/* * - fixed problem with cloned objects marked as invalid * */ +/* * - fixed problem cloning frozen object_buffers * */ +/* * 1.0.5 - 10/07/2002 - G.Juyn * */ +/* * - fixed DISC support * */ +/* * 1.0.5 - 11/04/2002 - G.Juyn * */ +/* * - fixed goframe/golayer/gotime processing * */ +/* * 1.0.5 - 11/07/2002 - G.Juyn * */ +/* * - fixed magnification bug with object 0 * */ +/* * 1.0.5 - 01/19/2003 - G.Juyn * */ +/* * - B664911 - fixed buffer overflow during init * */ +/* * * */ +/* * 1.0.6 - 04/19/2003 - G.Juyn * */ +/* * - fixed problem with infinite loops during readdisplay() * */ +/* * 1.0.6 - 05/25/2003 - G.R-P * */ +/* * - added MNG_SKIPCHUNK_cHNK footprint optimizations * */ +/* * 1.0.6 - 06/09/2003 - G. R-P * */ +/* * - added conditionals around 8-bit magn routines * */ +/* * 1.0.6 - 07/07/2003 - G.R-P * */ +/* * - added conditionals around some JNG-supporting code * */ +/* * - removed conditionals around 8-bit magn routines * */ +/* * - added conditionals around delta-png and 16-bit code * */ +/* * 1.0.6 - 07/14/2003 - G.R-P * */ +/* * - added MNG_NO_LOOP_SIGNALS_SUPPORTED conditional * */ +/* * 1.0.6 - 07/29/2003 - G.Juyn * */ +/* * - fixed invalid test in promote_imageobject * */ +/* * 1.0.6 - 07/29/2003 - G.R-P. * */ +/* * - added conditionals around PAST chunk support * */ +/* * 1.0.6 - 08/17/2003 - G.R-P. * */ +/* * - added conditionals around MAGN chunk support * */ +/* * * */ +/* * 1.0.7 - 03/21/2004 - G.Juyn * */ +/* * - fixed some 64-bit platform compiler warnings * */ +/* * * */ +/* * 1.0.9 - 10/10/2004 - G.R-P. * */ +/* * - added MNG_NO_1_2_4BIT_SUPPORT support * */ +/* * 1.0.9 - 12/05/2004 - G.Juyn * */ +/* * - added conditional MNG_OPTIMIZE_OBJCLEANUP * */ +/* * 1.0.9 - 12/11/2004 - G.Juyn * */ +/* * - added conditional MNG_OPTIMIZE_DISPLAYCALLS * */ +/* * 1.0.9 - 12/31/2004 - G.R-P. * */ +/* * - fixed warnings about possible uninitialized pointers * */ +/* * 1.0.9 - 01/02/2005 - G.Juyn * */ +/* * - fixing some compiler-warnings * */ +/* * * */ +/* * 1.0.10 - 02/07/2005 - G.Juyn * */ +/* * - fixed some compiler-warnings * */ +/* * 1.0.10 - 07/30/2005 - G.Juyn * */ +/* * - fixed problem with CLON object during readdisplay() * */ +/* * 1.0.10 - 04/08/2007 - G.Juyn * */ +/* * - added support for mPNG proposal * */ +/* * 1.0.10 - 04/12/2007 - G.Juyn * */ +/* * - added support for ANG proposal * */ +/* * * */ +/* ************************************************************************** */ + +#include "libmng.h" +#include "libmng_data.h" +#include "libmng_error.h" +#include "libmng_trace.h" +#ifdef __BORLANDC__ +#pragma hdrstop +#endif +#include "libmng_memory.h" +#include "libmng_chunks.h" +#include "libmng_objects.h" +#include "libmng_display.h" +#include "libmng_pixels.h" +#include "libmng_object_prc.h" +#include "libmng_cms.h" + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_DISPLAY_PROCS + +/* ************************************************************************** */ +/* * * */ +/* * Generic object routines * */ +/* * * */ +/* ************************************************************************** */ + +mng_retcode mng_drop_invalid_objects (mng_datap pData) +{ + mng_objectp pObject; + mng_objectp pNext; + mng_cleanupobject fCleanup; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DROP_INVALID_OBJECTS, MNG_LC_START); +#endif + + pObject = pData->pFirstimgobj; /* get first stored image-object (if any) */ + + while (pObject) /* more objects to check ? */ + { + pNext = ((mng_object_headerp)pObject)->pNext; + /* invalid ? */ + if (!((mng_imagep)pObject)->bValid) + { /* call appropriate cleanup */ + fCleanup = ((mng_object_headerp)pObject)->fCleanup; + fCleanup (pData, pObject); + } + + pObject = pNext; /* neeeext */ + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DROP_INVALID_OBJECTS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifdef MNG_OPTIMIZE_OBJCLEANUP +MNG_LOCAL mng_retcode create_obj_general (mng_datap pData, + mng_size_t iObjsize, + mng_cleanupobject fCleanup, + mng_processobject fProcess, + mng_ptr *ppObject) +{ + mng_object_headerp pWork; + + MNG_ALLOC (pData, pWork, iObjsize); + + pWork->fCleanup = fCleanup; + pWork->fProcess = fProcess; + pWork->iObjsize = iObjsize; + *ppObject = (mng_ptr)pWork; + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +MNG_LOCAL mng_retcode mng_free_obj_general (mng_datap pData, + mng_objectp pObject) +{ + MNG_FREEX (pData, pObject, ((mng_object_headerp)pObject)->iObjsize); + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ +/* * * */ +/* * Image-data-object routines * */ +/* * * */ +/* * these handle the "object buffer" as defined by the MNG specification * */ +/* * * */ +/* ************************************************************************** */ + +mng_retcode mng_create_imagedataobject (mng_datap pData, + mng_bool bConcrete, + mng_bool bViewable, + mng_uint32 iWidth, + mng_uint32 iHeight, + mng_uint8 iBitdepth, + mng_uint8 iColortype, + mng_uint8 iCompression, + mng_uint8 iFilter, + mng_uint8 iInterlace, + mng_imagedatap *ppObject) +{ + mng_imagedatap pImagedata; + mng_uint32 iSamplesize = 0; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_IMGDATAOBJECT, MNG_LC_START); +#endif + /* get a buffer */ +#ifdef MNG_OPTIMIZE_OBJCLEANUP + { + mng_ptr pTemp; + mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_imagedata), + (mng_cleanupobject)mng_free_imagedataobject, + MNG_NULL, &pTemp); + if (iRetcode) + return iRetcode; + pImagedata = (mng_imagedatap)pTemp; + } +#else + MNG_ALLOC (pData, pImagedata, sizeof (mng_imagedata)); + /* fill the appropriate fields */ + pImagedata->sHeader.fCleanup = (mng_cleanupobject)mng_free_imagedataobject; + pImagedata->sHeader.fProcess = MNG_NULL; +#endif + pImagedata->iRefcount = 1; + pImagedata->bFrozen = MNG_FALSE; + pImagedata->bConcrete = bConcrete; + pImagedata->bViewable = bViewable; + pImagedata->iWidth = iWidth; + pImagedata->iHeight = iHeight; + pImagedata->iBitdepth = iBitdepth; + pImagedata->iColortype = iColortype; + pImagedata->iCompression = iCompression; + pImagedata->iFilter = iFilter; + pImagedata->iInterlace = iInterlace; + pImagedata->bCorrected = MNG_FALSE; + pImagedata->iAlphabitdepth = 0; + pImagedata->iJHDRcompression = 0; + pImagedata->iJHDRinterlace = 0; + pImagedata->iPixelsampledepth = iBitdepth; + pImagedata->iAlphasampledepth = iBitdepth; + /* determine samplesize from color_type/bit_depth */ + switch (iColortype) /* for < 8-bit samples we just reserve 8 bits */ + { + case 0 : ; /* gray */ + case 8 : { /* JPEG gray */ +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth > 8) + iSamplesize = 2; + else +#endif + iSamplesize = 1; + + break; + } + case 2 : ; /* rgb */ + case 10 : { /* JPEG rgb */ +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth > 8) + iSamplesize = 6; + else +#endif + iSamplesize = 3; + + break; + } + case 3 : { /* indexed */ + iSamplesize = 1; + break; + } + case 4 : ; /* gray+alpha */ + case 12 : { /* JPEG gray+alpha */ +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth > 8) + iSamplesize = 4; + else +#endif + iSamplesize = 2; + + break; + } + case 6 : ; /* rgb+alpha */ + case 14 : { /* JPEG rgb+alpha */ +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth > 8) + iSamplesize = 8; + else +#endif + iSamplesize = 4; + + break; + } + } + /* make sure we remember all this */ + pImagedata->iSamplesize = iSamplesize; + pImagedata->iRowsize = iSamplesize * iWidth; + pImagedata->iImgdatasize = pImagedata->iRowsize * iHeight; + + if (pImagedata->iImgdatasize) /* need a buffer ? */ + { /* so allocate it */ + MNG_ALLOCX (pData, pImagedata->pImgdata, pImagedata->iImgdatasize); + + if (!pImagedata->pImgdata) /* enough memory ? */ + { + MNG_FREEX (pData, pImagedata, sizeof (mng_imagedata)); + MNG_ERROR (pData, MNG_OUTOFMEMORY); + } + } + /* check global stuff */ + pImagedata->bHasGAMA = pData->bHasglobalGAMA; +#ifndef MNG_SKIPCHUNK_cHRM + pImagedata->bHasCHRM = pData->bHasglobalCHRM; +#endif + pImagedata->bHasSRGB = pData->bHasglobalSRGB; +#ifndef MNG_SKIPCHUNK_iCCP + pImagedata->bHasICCP = pData->bHasglobalICCP; +#endif +#ifndef MNG_SKIPCHUNK_bKGD + pImagedata->bHasBKGD = pData->bHasglobalBKGD; +#endif + + if (pData->bHasglobalGAMA) /* global gAMA present ? */ + pImagedata->iGamma = pData->iGlobalGamma; + +#ifndef MNG_SKIPCHUNK_cHRM + if (pData->bHasglobalCHRM) /* global cHRM present ? */ + { + pImagedata->iWhitepointx = pData->iGlobalWhitepointx; + pImagedata->iWhitepointy = pData->iGlobalWhitepointy; + pImagedata->iPrimaryredx = pData->iGlobalPrimaryredx; + pImagedata->iPrimaryredy = pData->iGlobalPrimaryredy; + pImagedata->iPrimarygreenx = pData->iGlobalPrimarygreenx; + pImagedata->iPrimarygreeny = pData->iGlobalPrimarygreeny; + pImagedata->iPrimarybluex = pData->iGlobalPrimarybluex; + pImagedata->iPrimarybluey = pData->iGlobalPrimarybluey; + } +#endif + + if (pData->bHasglobalSRGB) /* glbal sRGB present ? */ + pImagedata->iRenderingintent = pData->iGlobalRendintent; + +#ifndef MNG_SKIPCHUNK_iCCP + if (pData->bHasglobalICCP) /* glbal iCCP present ? */ + { + pImagedata->iProfilesize = pData->iGlobalProfilesize; + + if (pImagedata->iProfilesize) + { + MNG_ALLOCX (pData, pImagedata->pProfile, pImagedata->iProfilesize); + + if (!pImagedata->pProfile) /* enough memory ? */ + { + MNG_FREEX (pData, pImagedata->pImgdata, pImagedata->iImgdatasize); + MNG_FREEX (pData, pImagedata, sizeof (mng_imagedata)); + MNG_ERROR (pData, MNG_OUTOFMEMORY); + } + + MNG_COPY (pImagedata->pProfile, pData->pGlobalProfile, pImagedata->iProfilesize); + } + } +#endif + +#ifndef MNG_SKIPCHUNK_bKGD + if (pData->bHasglobalBKGD) /* global bKGD present ? */ + { + pImagedata->iBKGDred = pData->iGlobalBKGDred; + pImagedata->iBKGDgreen = pData->iGlobalBKGDgreen; + pImagedata->iBKGDblue = pData->iGlobalBKGDblue; + } +#endif + + *ppObject = pImagedata; /* return it */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_IMGDATAOBJECT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_free_imagedataobject (mng_datap pData, + mng_imagedatap pImagedata) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_IMGDATAOBJECT, MNG_LC_START); +#endif + + if (pImagedata->iRefcount) /* decrease reference count */ + pImagedata->iRefcount--; + + if (!pImagedata->iRefcount) /* reached zero ? */ + { +#ifndef MNG_SKIPCHUNK_iCCP + if (pImagedata->iProfilesize) /* stored an iCCP profile ? */ + MNG_FREEX (pData, pImagedata->pProfile, pImagedata->iProfilesize); +#endif + if (pImagedata->iImgdatasize) /* sample-buffer present ? */ + MNG_FREEX (pData, pImagedata->pImgdata, pImagedata->iImgdatasize); + /* drop the buffer */ + MNG_FREEX (pData, pImagedata, sizeof (mng_imagedata)); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_IMGDATAOBJECT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_clone_imagedataobject (mng_datap pData, + mng_bool bConcrete, + mng_imagedatap pSource, + mng_imagedatap *ppClone) +{ + mng_imagedatap pNewdata; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CLONE_IMGDATAOBJECT, MNG_LC_START); +#endif + /* get a buffer */ + MNG_ALLOC (pData, pNewdata, sizeof (mng_imagedata)); + /* blatently copy the original buffer */ + MNG_COPY (pNewdata, pSource, sizeof (mng_imagedata)); + + pNewdata->iRefcount = 1; /* only the reference count */ + pNewdata->bConcrete = bConcrete; /* and concrete-flag are different */ + pNewdata->bFrozen = MNG_FALSE; + + if (pNewdata->iImgdatasize) /* sample buffer present ? */ + { + MNG_ALLOCX (pData, pNewdata->pImgdata, pNewdata->iImgdatasize); + + if (!pNewdata->pImgdata) /* not enough memory ? */ + { + MNG_FREEX (pData, pNewdata, sizeof (mng_imagedata)); + MNG_ERROR (pData, MNG_OUTOFMEMORY); + } + /* make a copy */ + MNG_COPY (pNewdata->pImgdata, pSource->pImgdata, pNewdata->iImgdatasize); + } + +#ifndef MNG_SKIPCHUNK_iCCP + if (pNewdata->iProfilesize) /* iCCP profile present ? */ + { + MNG_ALLOCX (pData, pNewdata->pProfile, pNewdata->iProfilesize); + + if (!pNewdata->pProfile) /* enough memory ? */ + { + MNG_FREEX (pData, pNewdata, sizeof (mng_imagedata)); + MNG_ERROR (pData, MNG_OUTOFMEMORY); + } + /* make a copy */ + MNG_COPY (pNewdata->pProfile, pSource->pProfile, pNewdata->iProfilesize); + } +#endif + + *ppClone = pNewdata; /* return the clone */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CLONE_IMGDATAOBJECT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ +/* * * */ +/* * Image-object routines * */ +/* * * */ +/* * these handle the "object" as defined by the MNG specification * */ +/* * * */ +/* ************************************************************************** */ + +mng_retcode mng_create_imageobject (mng_datap pData, + mng_uint16 iId, + mng_bool bConcrete, + mng_bool bVisible, + mng_bool bViewable, + mng_uint32 iWidth, + mng_uint32 iHeight, + mng_uint8 iBitdepth, + mng_uint8 iColortype, + mng_uint8 iCompression, + mng_uint8 iFilter, + mng_uint8 iInterlace, + mng_int32 iPosx, + mng_int32 iPosy, + mng_bool bClipped, + mng_int32 iClipl, + mng_int32 iClipr, + mng_int32 iClipt, + mng_int32 iClipb, + mng_imagep *ppObject) +{ + mng_imagep pImage; + mng_imagep pPrev, pNext; + mng_retcode iRetcode; + mng_imagedatap pImgbuf; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_IMGOBJECT, MNG_LC_START); +#endif + /* get a buffer */ + MNG_ALLOC (pData, pImage, sizeof (mng_image)); + /* now get a new "object buffer" */ + iRetcode = mng_create_imagedataobject (pData, bConcrete, bViewable, + iWidth, iHeight, iBitdepth, iColortype, + iCompression, iFilter, iInterlace, + &pImgbuf); + + if (iRetcode) /* on error bail out */ + { + MNG_FREEX (pData, pImage, sizeof (mng_image)); + return iRetcode; + } + /* fill the appropriate fields */ + pImage->sHeader.fCleanup = (mng_cleanupobject)mng_free_imageobject; + pImage->sHeader.fProcess = MNG_NULL; +#ifdef MNG_OPTIMIZE_OBJCLEANUP + pImage->sHeader.iObjsize = sizeof (mng_image); +#endif + pImage->iId = iId; + pImage->bFrozen = MNG_FALSE; + pImage->bVisible = bVisible; + pImage->bViewable = bViewable; + pImage->bValid = (mng_bool)((pData->bDisplaying) && + ((pData->bRunning) || (pData->bSearching)) && + (!pData->bFreezing)); + pImage->iPosx = iPosx; + pImage->iPosy = iPosy; + pImage->bClipped = bClipped; + pImage->iClipl = iClipl; + pImage->iClipr = iClipr; + pImage->iClipt = iClipt; + pImage->iClipb = iClipb; +#ifndef MNG_SKIPCHUNK_MAGN + pImage->iMAGN_MethodX = 0; + pImage->iMAGN_MethodY = 0; + pImage->iMAGN_MX = 0; + pImage->iMAGN_MY = 0; + pImage->iMAGN_ML = 0; + pImage->iMAGN_MR = 0; + pImage->iMAGN_MT = 0; + pImage->iMAGN_MB = 0; +#endif +#ifndef MNG_SKIPCHUNK_PAST + pImage->iPastx = 0; + pImage->iPasty = 0; +#endif + pImage->pImgbuf = pImgbuf; + + if (iId) /* only if not object 0 ! */ + { /* find previous lower object-id */ + pPrev = (mng_imagep)pData->pLastimgobj; + + while ((pPrev) && (pPrev->iId > iId)) + pPrev = (mng_imagep)pPrev->sHeader.pPrev; + + if (pPrev) /* found it ? */ + { + pImage->sHeader.pPrev = pPrev; /* than link it in place */ + pImage->sHeader.pNext = pPrev->sHeader.pNext; + pPrev->sHeader.pNext = pImage; + } + else /* if not found, it becomes the first ! */ + { + pImage->sHeader.pNext = pData->pFirstimgobj; + pData->pFirstimgobj = pImage; + } + + pNext = (mng_imagep)pImage->sHeader.pNext; + + if (pNext) + pNext->sHeader.pPrev = pImage; + else + pData->pLastimgobj = pImage; + + } + + *ppObject = pImage; /* and return the new buffer */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_IMGOBJECT, MNG_LC_END); +#endif + + return MNG_NOERROR; /* okido */ +} + +/* ************************************************************************** */ + +mng_retcode mng_free_imageobject (mng_datap pData, + mng_imagep pImage) +{ + mng_retcode iRetcode; + mng_imagep pPrev = pImage->sHeader.pPrev; + mng_imagep pNext = pImage->sHeader.pNext; + mng_imagedatap pImgbuf = pImage->pImgbuf; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_IMGOBJECT, MNG_LC_START); +#endif + + if (pImage->iId) /* not for object 0 */ + { + if (pPrev) /* unlink from the list first ! */ + pPrev->sHeader.pNext = pImage->sHeader.pNext; + else + pData->pFirstimgobj = pImage->sHeader.pNext; + + if (pNext) + pNext->sHeader.pPrev = pImage->sHeader.pPrev; + else + pData->pLastimgobj = pImage->sHeader.pPrev; + + } + /* unlink the image-data buffer */ + iRetcode = mng_free_imagedataobject (pData, pImgbuf); + /* drop its own buffer */ + MNG_FREEX (pData, pImage, sizeof (mng_image)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_IMGOBJECT, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +mng_imagep mng_find_imageobject (mng_datap pData, + mng_uint16 iId) +{ + mng_imagep pImage = (mng_imagep)pData->pFirstimgobj; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (pData, MNG_FN_FIND_IMGOBJECT, MNG_LC_START); +#endif + /* look up the right id */ + while ((pImage) && (pImage->iId != iId)) + pImage = (mng_imagep)pImage->sHeader.pNext; + +#ifdef MNG_INCLUDE_MPNG_PROPOSAL + if ((!pImage) && (pData->eImagetype == mng_it_mpng)) + pImage = pData->pObjzero; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (pData, MNG_FN_FIND_IMGOBJECT, MNG_LC_END); +#endif + + return pImage; +} + +/* ************************************************************************** */ + +mng_retcode mng_clone_imageobject (mng_datap pData, + mng_uint16 iId, + mng_bool bPartial, + mng_bool bVisible, + mng_bool bAbstract, + mng_bool bHasloca, + mng_uint8 iLocationtype, + mng_int32 iLocationx, + mng_int32 iLocationy, + mng_imagep pSource, + mng_imagep *ppClone) +{ + mng_imagep pNew; + mng_imagep pPrev, pNext; + mng_retcode iRetcode; + mng_imagedatap pImgbuf; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CLONE_IMGOBJECT, MNG_LC_START); +#endif + +#ifndef MNG_SKIPCHUNK_MAGN + if ((pSource->iId) && /* needs magnification ? */ + ((pSource->iMAGN_MethodX) || (pSource->iMAGN_MethodY))) + { + iRetcode = mng_magnify_imageobject (pData, pSource); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } +#endif + /* get a buffer */ +#ifdef MNG_OPTIMIZE_OBJCLEANUP + { + mng_ptr pTemp; + mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_image), + (mng_cleanupobject)mng_free_imageobject, + MNG_NULL, &pTemp); + if (iRetcode) + return iRetcode; + pNew = (mng_imagep)pTemp; + } +#else + MNG_ALLOC (pData, pNew, sizeof (mng_image)); + /* fill or copy the appropriate fields */ + pNew->sHeader.fCleanup = (mng_cleanupobject)mng_free_imageobject; + pNew->sHeader.fProcess = MNG_NULL; +#endif + pNew->iId = iId; + pNew->bFrozen = MNG_FALSE; + pNew->bVisible = bVisible; + pNew->bViewable = pSource->bViewable; + pNew->bValid = MNG_TRUE; + + if (bHasloca) /* location info available ? */ + { + if (iLocationtype == 0) /* absolute position ? */ + { + pNew->iPosx = iLocationx; + pNew->iPosy = iLocationy; + } + else /* relative */ + { + pNew->iPosx = pSource->iPosx + iLocationx; + pNew->iPosy = pSource->iPosy + iLocationy; + } + } + else /* copy from source */ + { + pNew->iPosx = pSource->iPosx; + pNew->iPosy = pSource->iPosy; + } + /* copy clipping info */ + pNew->bClipped = pSource->bClipped; + pNew->iClipl = pSource->iClipl; + pNew->iClipr = pSource->iClipr; + pNew->iClipt = pSource->iClipt; + pNew->iClipb = pSource->iClipb; +#ifndef MNG_SKIPCHUNK_MAGN + /* copy magnification info */ +/* pNew->iMAGN_MethodX = pSource->iMAGN_MethodX; LET'S NOT !!!!!! + pNew->iMAGN_MethodY = pSource->iMAGN_MethodY; + pNew->iMAGN_MX = pSource->iMAGN_MX; + pNew->iMAGN_MY = pSource->iMAGN_MY; + pNew->iMAGN_ML = pSource->iMAGN_ML; + pNew->iMAGN_MR = pSource->iMAGN_MR; + pNew->iMAGN_MT = pSource->iMAGN_MT; + pNew->iMAGN_MB = pSource->iMAGN_MB; */ +#endif + +#ifndef MNG_SKIPCHUNK_PAST + pNew->iPastx = 0; /* initialize PAST info */ + pNew->iPasty = 0; +#endif + + if (iId) /* not for object 0 */ + { /* find previous lower object-id */ + pPrev = (mng_imagep)pData->pLastimgobj; + while ((pPrev) && (pPrev->iId > iId)) + pPrev = (mng_imagep)pPrev->sHeader.pPrev; + + if (pPrev) /* found it ? */ + { + pNew->sHeader.pPrev = pPrev; /* than link it in place */ + pNew->sHeader.pNext = pPrev->sHeader.pNext; + pPrev->sHeader.pNext = pNew; + } + else /* if not found, it becomes the first ! */ + { + pNew->sHeader.pNext = pData->pFirstimgobj; + pData->pFirstimgobj = pNew; + } + + pNext = (mng_imagep)pNew->sHeader.pNext; + + if (pNext) + pNext->sHeader.pPrev = pNew; + else + pData->pLastimgobj = pNew; + + } + + if (bPartial) /* partial clone ? */ + { + pNew->pImgbuf = pSource->pImgbuf; /* use the same object buffer */ + pNew->pImgbuf->iRefcount++; /* and increase the reference count */ + } + else /* create a full clone ! */ + { + mng_bool bConcrete = MNG_FALSE; /* it's abstract by default (?) */ + + if (!bAbstract) /* determine concreteness from source ? */ + bConcrete = pSource->pImgbuf->bConcrete; + /* create a full clone ! */ + iRetcode = mng_clone_imagedataobject (pData, bConcrete, pSource->pImgbuf, &pImgbuf); + + if (iRetcode) /* on error bail out */ + { + MNG_FREEX (pData, pNew, sizeof (mng_image)); + return iRetcode; + } + + pNew->pImgbuf = pImgbuf; /* and remember it */ + } + + *ppClone = pNew; /* return it */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CLONE_IMGOBJECT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_renum_imageobject (mng_datap pData, + mng_imagep pSource, + mng_uint16 iId, + mng_bool bVisible, + mng_bool bAbstract, + mng_bool bHasloca, + mng_uint8 iLocationtype, + mng_int32 iLocationx, + mng_int32 iLocationy) +{ + mng_imagep pPrev, pNext; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RENUM_IMGOBJECT, MNG_LC_START); +#endif + + pSource->bVisible = bVisible; /* store the new visibility */ + + if (bHasloca) /* location info available ? */ + { + if (iLocationtype == 0) /* absolute position ? */ + { + pSource->iPosx = iLocationx; + pSource->iPosy = iLocationy; + } + else /* relative */ + { + pSource->iPosx = pSource->iPosx + iLocationx; + pSource->iPosy = pSource->iPosy + iLocationy; + } + } + + if (iId) /* not for object 0 */ + { /* find previous lower object-id */ + pPrev = (mng_imagep)pData->pLastimgobj; + while ((pPrev) && (pPrev->iId > iId)) + pPrev = (mng_imagep)pPrev->sHeader.pPrev; + /* different from current ? */ + if (pPrev != (mng_imagep)pSource->sHeader.pPrev) + { + if (pSource->sHeader.pPrev) /* unlink from current position !! */ + ((mng_imagep)pSource->sHeader.pPrev)->sHeader.pNext = pSource->sHeader.pNext; + else + pData->pFirstimgobj = pSource->sHeader.pNext; + + if (pSource->sHeader.pNext) + ((mng_imagep)pSource->sHeader.pNext)->sHeader.pPrev = pSource->sHeader.pPrev; + else + pData->pLastimgobj = pSource->sHeader.pPrev; + + if (pPrev) /* found the previous ? */ + { /* than link it in place */ + pSource->sHeader.pPrev = pPrev; + pSource->sHeader.pNext = pPrev->sHeader.pNext; + pPrev->sHeader.pNext = pSource; + } + else /* if not found, it becomes the first ! */ + { + pSource->sHeader.pNext = pData->pFirstimgobj; + pData->pFirstimgobj = pSource; + } + + pNext = (mng_imagep)pSource->sHeader.pNext; + + if (pNext) + pNext->sHeader.pPrev = pSource; + else + pData->pLastimgobj = pSource; + + } + } + + pSource->iId = iId; /* now set the new id! */ + + if (bAbstract) /* force it to abstract ? */ + pSource->pImgbuf->bConcrete = MNG_FALSE; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RENUM_IMGOBJECT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_reset_object_details (mng_datap pData, + mng_imagep pImage, + mng_uint32 iWidth, + mng_uint32 iHeight, + mng_uint8 iBitdepth, + mng_uint8 iColortype, + mng_uint8 iCompression, + mng_uint8 iFilter, + mng_uint8 iInterlace, + mng_bool bResetall) +{ + mng_imagedatap pBuf = pImage->pImgbuf; + mng_uint32 iSamplesize = 0; + mng_uint32 iRowsize; + mng_uint32 iImgdatasize; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RESET_OBJECTDETAILS, MNG_LC_START); +#endif + + pBuf->iWidth = iWidth; /* set buffer characteristics */ + pBuf->iHeight = iHeight; + pBuf->iBitdepth = iBitdepth; + pBuf->iColortype = iColortype; + pBuf->iCompression = iCompression; + pBuf->iFilter = iFilter; + pBuf->iInterlace = iInterlace; + pBuf->bCorrected = MNG_FALSE; + pBuf->iAlphabitdepth = 0; + /* determine samplesize from color_type/bit_depth */ + switch (iColortype) /* for < 8-bit samples we just reserve 8 bits */ + { + case 0 : ; /* gray */ + case 8 : { /* JPEG gray */ +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth > 8) + iSamplesize = 2; + else +#endif + iSamplesize = 1; + + break; + } + case 2 : ; /* rgb */ + case 10 : { /* JPEG rgb */ +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth > 8) + iSamplesize = 6; + else +#endif + iSamplesize = 3; + + break; + } + case 3 : { /* indexed */ + iSamplesize = 1; + break; + } + case 4 : ; /* gray+alpha */ + case 12 : { /* JPEG gray+alpha */ +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth > 8) + iSamplesize = 4; + else +#endif + iSamplesize = 2; + + break; + } + case 6 : ; /* rgb+alpha */ + case 14 : { /* JPEG rgb+alpha */ +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth > 8) + iSamplesize = 8; + else +#endif + iSamplesize = 4; + + break; + } + } + + iRowsize = iSamplesize * iWidth; + iImgdatasize = iRowsize * iHeight; + /* buffer size changed ? */ + if (iImgdatasize != pBuf->iImgdatasize) + { /* drop the old one */ + MNG_FREE (pData, pBuf->pImgdata, pBuf->iImgdatasize); + + if (iImgdatasize) /* allocate new sample-buffer ? */ + MNG_ALLOC (pData, pBuf->pImgdata, iImgdatasize); + } + else + { + if (iImgdatasize) /* clear old buffer */ + { + mng_uint8p pTemp = pBuf->pImgdata; + mng_uint32 iX; + + for (iX = 0; iX < (iImgdatasize & (mng_uint32)(~3L)); iX += 4) + { + *((mng_uint32p)pTemp) = 0x00000000l; + pTemp += 4; + } + + while (pTemp < (pBuf->pImgdata + iImgdatasize)) + { + *pTemp = 0; + pTemp++; + } + } + } + + pBuf->iSamplesize = iSamplesize; /* remember new sizes */ + pBuf->iRowsize = iRowsize; + pBuf->iImgdatasize = iImgdatasize; + + if (!pBuf->iPixelsampledepth) /* set delta sampledepths if empty */ + pBuf->iPixelsampledepth = iBitdepth; + if (!pBuf->iAlphasampledepth) + pBuf->iAlphasampledepth = iBitdepth; + /* dimension set and clipping not ? */ + if ((iWidth) && (iHeight) && (!pImage->bClipped)) + { + pImage->iClipl = 0; /* set clipping to dimension by default */ + pImage->iClipr = iWidth; + pImage->iClipt = 0; + pImage->iClipb = iHeight; + } + +#ifndef MNG_SKIPCHUNK_MAGN + if (pImage->iId) /* reset magnification info ? */ + { + pImage->iMAGN_MethodX = 0; + pImage->iMAGN_MethodY = 0; + pImage->iMAGN_MX = 0; + pImage->iMAGN_MY = 0; + pImage->iMAGN_ML = 0; + pImage->iMAGN_MR = 0; + pImage->iMAGN_MT = 0; + pImage->iMAGN_MB = 0; + } +#endif + + if (bResetall) /* reset the other characteristics ? */ + { +#ifndef MNG_SKIPCHUNK_PAST + pImage->iPastx = 0; + pImage->iPasty = 0; +#endif + + pBuf->bHasPLTE = MNG_FALSE; + pBuf->bHasTRNS = MNG_FALSE; + pBuf->bHasGAMA = pData->bHasglobalGAMA; +#ifndef MNG_SKIPCHUNK_cHRM + pBuf->bHasCHRM = pData->bHasglobalCHRM; +#endif + pBuf->bHasSRGB = pData->bHasglobalSRGB; +#ifndef MNG_SKIPCHUNK_iCCP + pBuf->bHasICCP = pData->bHasglobalICCP; +#endif +#ifndef MNG_SKIPCHUNK_bKGD + pBuf->bHasBKGD = pData->bHasglobalBKGD; +#endif + +#ifndef MNG_SKIPCHUNK_iCCP + if (pBuf->iProfilesize) /* drop possibly old ICC profile */ + { + MNG_FREE (pData, pBuf->pProfile, pBuf->iProfilesize); + pBuf->iProfilesize = 0; + } +#endif + + if (pData->bHasglobalGAMA) /* global gAMA present ? */ + pBuf->iGamma = pData->iGlobalGamma; + +#ifndef MNG_SKIPCHUNK_cHRM + if (pData->bHasglobalCHRM) /* global cHRM present ? */ + { + pBuf->iWhitepointx = pData->iGlobalWhitepointx; + pBuf->iWhitepointy = pData->iGlobalWhitepointy; + pBuf->iPrimaryredx = pData->iGlobalPrimaryredx; + pBuf->iPrimaryredy = pData->iGlobalPrimaryredy; + pBuf->iPrimarygreenx = pData->iGlobalPrimarygreenx; + pBuf->iPrimarygreeny = pData->iGlobalPrimarygreeny; + pBuf->iPrimarybluex = pData->iGlobalPrimarybluex; + pBuf->iPrimarybluey = pData->iGlobalPrimarybluey; + } +#endif + + if (pData->bHasglobalSRGB) /* global sRGB present ? */ + pBuf->iRenderingintent = pData->iGlobalRendintent; + +#ifndef MNG_SKIPCHUNK_iCCP + if (pData->bHasglobalICCP) /* global iCCP present ? */ + { + if (pData->iGlobalProfilesize) + { + MNG_ALLOC (pData, pBuf->pProfile, pData->iGlobalProfilesize); + MNG_COPY (pBuf->pProfile, pData->pGlobalProfile, pData->iGlobalProfilesize); + } + + pBuf->iProfilesize = pData->iGlobalProfilesize; + } +#endif + +#ifndef MNG_SKIPCHUNK_bKGD + if (pData->bHasglobalBKGD) /* global bKGD present ? */ + { + pBuf->iBKGDred = pData->iGlobalBKGDred; + pBuf->iBKGDgreen = pData->iGlobalBKGDgreen; + pBuf->iBKGDblue = pData->iGlobalBKGDblue; + } +#endif + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RESET_OBJECTDETAILS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#if !defined(MNG_NO_DELTA_PNG) || !defined(MNG_SKIPCHUNK_PAST) || !defined(MNG_SKIPCHUNK_MAGN) +mng_retcode mng_promote_imageobject (mng_datap pData, + mng_imagep pImage, + mng_uint8 iBitdepth, + mng_uint8 iColortype, + mng_uint8 iFilltype) +{ + mng_retcode iRetcode = MNG_NOERROR; + mng_imagedatap pBuf = pImage->pImgbuf; + mng_uint32 iW = pBuf->iWidth; + mng_uint32 iH = pBuf->iHeight; + mng_uint8p pNewbuf; + mng_uint32 iNewbufsize; + mng_uint32 iNewrowsize; + mng_uint32 iNewsamplesize = pBuf->iSamplesize; + mng_uint32 iY; + mng_uint8 iTempdepth; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_IMGOBJECT, MNG_LC_START); +#endif + +#ifdef MNG_NO_1_2_4BIT_SUPPORT + if (iBitdepth < 8) + iBitdepth=8; + if (pBuf->iBitdepth < 8) + pBuf->iBitdepth=8; +#endif +#ifdef MNG_NO_16BIT_SUPPORT + if (iBitdepth > 8) + iBitdepth=8; + if (pBuf->iBitdepth > 8) + pBuf->iBitdepth=8; +#endif + + pData->fPromoterow = MNG_NULL; /* init promotion fields */ + pData->fPromBitdepth = MNG_NULL; + pData->iPromColortype = iColortype; + pData->iPromBitdepth = iBitdepth; + pData->iPromFilltype = iFilltype; + + if (iBitdepth != pBuf->iBitdepth) /* determine bitdepth promotion */ + { + if (pBuf->iColortype == MNG_COLORTYPE_INDEXED) + iTempdepth = 8; + else + iTempdepth = pBuf->iBitdepth; + +#ifndef MNG_NO_DELTA_PNG + if (iFilltype == MNG_FILLMETHOD_ZEROFILL) + { + switch (iTempdepth) + { +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case 1 : { + switch (iBitdepth) + { + case 2 : { pData->fPromBitdepth = (mng_fptr)mng_promote_zerofill_1_2; break; } + case 4 : { pData->fPromBitdepth = (mng_fptr)mng_promote_zerofill_1_4; break; } + case 8 : { pData->fPromBitdepth = (mng_fptr)mng_promote_zerofill_1_8; break; } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { pData->fPromBitdepth = (mng_fptr)mng_promote_zerofill_1_16; break; } +#endif + } + break; + } + case 2 : { + switch (iBitdepth) + { + case 4 : { pData->fPromBitdepth = (mng_fptr)mng_promote_zerofill_2_4; break; } + case 8 : { pData->fPromBitdepth = (mng_fptr)mng_promote_zerofill_2_8; break; } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { pData->fPromBitdepth = (mng_fptr)mng_promote_zerofill_2_16; break; } +#endif + } + break; + } + case 4 : { + switch (iBitdepth) + { + case 8 : { pData->fPromBitdepth = (mng_fptr)mng_promote_zerofill_4_8; break; } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { pData->fPromBitdepth = (mng_fptr)mng_promote_zerofill_4_16; break; } +#endif + } + break; + } +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + case 8 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) + pData->fPromBitdepth = (mng_fptr)mng_promote_zerofill_8_16; +#endif + break; + } + } + } + else +#endif + { + switch (iTempdepth) + { +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case 1 : { + switch (iBitdepth) + { + case 2 : { pData->fPromBitdepth = (mng_fptr)mng_promote_replicate_1_2; break; } + case 4 : { pData->fPromBitdepth = (mng_fptr)mng_promote_replicate_1_4; break; } + case 8 : { pData->fPromBitdepth = (mng_fptr)mng_promote_replicate_1_8; break; } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { pData->fPromBitdepth = (mng_fptr)mng_promote_replicate_1_16; break; } +#endif + } + break; + } + case 2 : { + switch (iBitdepth) + { + case 4 : { pData->fPromBitdepth = (mng_fptr)mng_promote_replicate_2_4; break; } + case 8 : { pData->fPromBitdepth = (mng_fptr)mng_promote_replicate_2_8; break; } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { pData->fPromBitdepth = (mng_fptr)mng_promote_replicate_2_16; break; } +#endif + } + break; + } + case 4 : { + switch (iBitdepth) + { + case 8 : { pData->fPromBitdepth = (mng_fptr)mng_promote_replicate_4_8; break; } +#ifndef MNG_NO_16BIT_SUPPORT + case 16 : { pData->fPromBitdepth = (mng_fptr)mng_promote_replicate_4_16; break; } +#endif + } + break; + } +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + case 8 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) + pData->fPromBitdepth = (mng_fptr)mng_promote_replicate_8_16; +#endif + break; + } + } + } + } + /* g -> g */ + if ((pBuf->iColortype == MNG_COLORTYPE_GRAY) && + (iColortype == MNG_COLORTYPE_GRAY)) + { + if (pBuf->iBitdepth <= 8) /* source <= 8 bits */ + { +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) + pData->fPromoterow = (mng_fptr)mng_promote_g8_g16; + else +#endif + pData->fPromoterow = (mng_fptr)mng_promote_g8_g8; + } + + iNewsamplesize = 1; + +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) /* 16-bit wide ? */ + iNewsamplesize = 2; +#endif + } + else /* g -> ga */ + if ((pBuf->iColortype == MNG_COLORTYPE_GRAY) && + (iColortype == MNG_COLORTYPE_GRAYA)) + { + if (pBuf->iBitdepth <= 8) /* source <= 8 bits */ + { +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) + pData->fPromoterow = (mng_fptr)mng_promote_g8_ga16; + else +#endif + pData->fPromoterow = (mng_fptr)mng_promote_g8_ga8; + } +#ifndef MNG_NO_16BIT_SUPPORT + else /* source = 16 bits */ + pData->fPromoterow = (mng_fptr)mng_promote_g16_ga16; +#endif + + iNewsamplesize = 2; + +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) /* 16-bit wide ? */ + iNewsamplesize = 4; +#endif + } + else /* g -> rgb */ + if ((pBuf->iColortype == MNG_COLORTYPE_GRAY) && + (iColortype == MNG_COLORTYPE_RGB)) + { + if (pBuf->iBitdepth <= 8) /* source <= 8 bits */ + { +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) + pData->fPromoterow = (mng_fptr)mng_promote_g8_rgb16; + else +#endif + pData->fPromoterow = (mng_fptr)mng_promote_g8_rgb8; + } +#ifndef MNG_NO_16BIT_SUPPORT + else /* source = 16 bits */ + pData->fPromoterow = (mng_fptr)mng_promote_g16_rgb16; +#endif + + iNewsamplesize = 3; + +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) /* 16-bit wide ? */ + iNewsamplesize = 6; +#endif + } + else /* g -> rgba */ + if ((pBuf->iColortype == MNG_COLORTYPE_GRAY) && + (iColortype == MNG_COLORTYPE_RGBA)) + { + if (pBuf->iBitdepth <= 8) /* source <= 8 bits */ + { +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) + pData->fPromoterow = (mng_fptr)mng_promote_g8_rgba16; + else +#endif + pData->fPromoterow = (mng_fptr)mng_promote_g8_rgba8; + } +#ifndef MNG_NO_16BIT_SUPPORT + else /* source = 16 bits */ + pData->fPromoterow = (mng_fptr)mng_promote_g16_rgba16; +#endif + + iNewsamplesize = 4; + +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) /* 16-bit wide ? */ + iNewsamplesize = 8; +#endif + } + else /* ga -> ga */ + if ((pBuf->iColortype == MNG_COLORTYPE_GRAYA) && + (iColortype == MNG_COLORTYPE_GRAYA)) + { + iNewsamplesize = 2; +#ifndef MNG_NO_16BIT_SUPPORT + if (pBuf->iBitdepth <= 8) /* source <= 8 bits */ + if (iBitdepth == 16) + pData->fPromoterow = (mng_fptr)mng_promote_ga8_ga16; + if (iBitdepth == 16) + iNewsamplesize = 4; +#endif + } + else /* ga -> rgba */ + if ((pBuf->iColortype == MNG_COLORTYPE_GRAYA) && + (iColortype == MNG_COLORTYPE_RGBA)) + { + if (pBuf->iBitdepth <= 8) /* source <= 8 bits */ + { +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) + pData->fPromoterow = (mng_fptr)mng_promote_ga8_rgba16; + else +#endif + pData->fPromoterow = (mng_fptr)mng_promote_ga8_rgba8; + } +#ifndef MNG_NO_16BIT_SUPPORT + else /* source = 16 bits */ + pData->fPromoterow = (mng_fptr)mng_promote_ga16_rgba16; +#endif + + iNewsamplesize = 4; + +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) /* 16-bit wide ? */ + iNewsamplesize = 8; +#endif + } + else /* rgb -> rgb */ + if ((pBuf->iColortype == MNG_COLORTYPE_RGB) && + (iColortype == MNG_COLORTYPE_RGB)) + { + iNewsamplesize = 3; +#ifndef MNG_NO_16BIT_SUPPORT + if (pBuf->iBitdepth <= 8) /* source <= 8 bits */ + if (iBitdepth == 16) + pData->fPromoterow = (mng_fptr)mng_promote_rgb8_rgb16; + if (iBitdepth == 16) + iNewsamplesize = 6; +#endif + } + else /* rgb -> rgba */ + if ((pBuf->iColortype == MNG_COLORTYPE_RGB) && + (iColortype == MNG_COLORTYPE_RGBA)) + { + if (pBuf->iBitdepth <= 8) /* source <= 8 bits */ + { +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) + pData->fPromoterow = (mng_fptr)mng_promote_rgb8_rgba16; + else +#endif + pData->fPromoterow = (mng_fptr)mng_promote_rgb8_rgba8; + } +#ifndef MNG_NO_16BIT_SUPPORT + else /* source = 16 bits */ + pData->fPromoterow = (mng_fptr)mng_promote_rgb16_rgba16; +#endif + + iNewsamplesize = 4; +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) /* 16-bit wide ? */ + iNewsamplesize = 8; +#endif + } + else /* indexed -> rgb */ + if ((pBuf->iColortype == MNG_COLORTYPE_INDEXED) && + (iColortype == MNG_COLORTYPE_RGB)) + { +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) + pData->fPromoterow = (mng_fptr)mng_promote_idx8_rgb16; + else +#endif + pData->fPromoterow = (mng_fptr)mng_promote_idx8_rgb8; + + iNewsamplesize = 3; + +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) /* 16-bit wide ? */ + iNewsamplesize = 6; +#endif + } + else /* indexed -> rgba */ + if ((pBuf->iColortype == MNG_COLORTYPE_INDEXED) && + (iColortype == MNG_COLORTYPE_RGBA)) + { +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) + pData->fPromoterow = (mng_fptr)mng_promote_idx8_rgba16; + else +#endif + pData->fPromoterow = (mng_fptr)mng_promote_idx8_rgba8; + + iNewsamplesize = 4; + +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) /* 16-bit wide ? */ + iNewsamplesize = 8; +#endif + } + else /* rgba -> rgba */ + if ((pBuf->iColortype == MNG_COLORTYPE_RGBA) && + (iColortype == MNG_COLORTYPE_RGBA)) + { + iNewsamplesize = 4; +#ifndef MNG_NO_16BIT_SUPPORT + if (pBuf->iBitdepth <= 8) /* source <= 8 bits */ + { + if (iBitdepth == 16) + pData->fPromoterow = (mng_fptr)mng_promote_rgba8_rgba16; + } + if (iBitdepth == 16) /* 16-bit wide ? */ + iNewsamplesize = 8; +#endif + } +#ifdef MNG_INCLUDE_JNG + else /* JPEG g -> g */ + if ((pBuf->iColortype == MNG_COLORTYPE_JPEGGRAY) && + (iColortype == MNG_COLORTYPE_JPEGGRAY)) + { + if (pBuf->iBitdepth <= 8) /* source <= 8 bits */ + { +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) + pData->fPromoterow = (mng_fptr)mng_promote_g8_g16; + else +#endif + pData->fPromoterow = (mng_fptr)mng_promote_g8_g8; + } + + iNewsamplesize = 1; + +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) /* 16-bit wide ? */ + iNewsamplesize = 2; +#endif + } + else /* JPEG g -> ga */ + if ((pBuf->iColortype == MNG_COLORTYPE_JPEGGRAY) && + (iColortype == MNG_COLORTYPE_JPEGGRAYA)) + { + if (pBuf->iBitdepth <= 8) /* source <= 8 bits */ + { +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) + pData->fPromoterow = (mng_fptr)mng_promote_g8_ga16; + else +#endif + pData->fPromoterow = (mng_fptr)mng_promote_g8_ga8; + } +#ifndef MNG_NO_16BIT_SUPPORT + else /* source = 16 bits */ + pData->fPromoterow = (mng_fptr)mng_promote_g16_ga16; +#endif + + iNewsamplesize = 2; + +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) /* 16-bit wide ? */ + iNewsamplesize = 4; +#endif + } + else /* JPEG g -> rgb */ + if ((pBuf->iColortype == MNG_COLORTYPE_JPEGGRAY) && + (iColortype == MNG_COLORTYPE_JPEGCOLOR)) + { + if (pBuf->iBitdepth <= 8) /* source <= 8 bits */ + { +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) + pData->fPromoterow = (mng_fptr)mng_promote_g8_rgb16; + else +#endif + pData->fPromoterow = (mng_fptr)mng_promote_g8_rgb8; + } +#ifndef MNG_NO_16BIT_SUPPORT + else /* source = 16 bits */ + pData->fPromoterow = (mng_fptr)mng_promote_g16_rgb16; +#endif + + iNewsamplesize = 3; + +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) /* 16-bit wide ? */ + iNewsamplesize = 6; +#endif + } + else /* JPEG g -> rgba */ + if ((pBuf->iColortype == MNG_COLORTYPE_JPEGGRAY) && + (iColortype == MNG_COLORTYPE_JPEGCOLORA)) + { + if (pBuf->iBitdepth <= 8) /* source <= 8 bits */ + { +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) + pData->fPromoterow = (mng_fptr)mng_promote_g8_rgba16; + else +#endif + pData->fPromoterow = (mng_fptr)mng_promote_g8_rgba8; + } +#ifndef MNG_NO_16BIT_SUPPORT + else /* source = 16 bits */ + pData->fPromoterow = (mng_fptr)mng_promote_g16_rgba16; +#endif + + iNewsamplesize = 4; + +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) /* 16-bit wide ? */ + iNewsamplesize = 8; +#endif + } + else /* JPEG ga -> ga */ + if ((pBuf->iColortype == MNG_COLORTYPE_JPEGGRAYA) && + (iColortype == MNG_COLORTYPE_JPEGGRAYA)) + { + iNewsamplesize = 2; +#ifndef MNG_NO_16BIT_SUPPORT + if (pBuf->iBitdepth <= 8) /* source <= 8 bits */ + if (iBitdepth == 16) + pData->fPromoterow = (mng_fptr)mng_promote_ga8_ga16; + if (iBitdepth == 16) + iNewsamplesize = 4; +#endif + + } + else /* JPEG ga -> rgba */ + if ((pBuf->iColortype == MNG_COLORTYPE_JPEGGRAYA) && + (iColortype == MNG_COLORTYPE_JPEGCOLORA)) + { + if (pBuf->iBitdepth <= 8) /* source <= 8 bits */ + { +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) + pData->fPromoterow = (mng_fptr)mng_promote_ga8_rgba16; + else +#endif + pData->fPromoterow = (mng_fptr)mng_promote_ga8_rgba8; + } +#ifndef MNG_NO_16BIT_SUPPORT + else /* source = 16 bits */ + pData->fPromoterow = (mng_fptr)mng_promote_ga16_rgba16; +#endif + + iNewsamplesize = 4; + +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) /* 16-bit wide ? */ + iNewsamplesize = 8; +#endif + } + else /* JPEG rgb -> rgb */ + if ((pBuf->iColortype == MNG_COLORTYPE_JPEGCOLOR) && + (iColortype == MNG_COLORTYPE_JPEGCOLOR)) + { + iNewsamplesize = 3; +#ifndef MNG_NO_16BIT_SUPPORT + if (pBuf->iBitdepth <= 8) /* source <= 8 bits */ + if (iBitdepth == 16) + pData->fPromoterow = (mng_fptr)mng_promote_rgb8_rgb16; + if (iBitdepth == 16) + iNewsamplesize = 6; +#endif + + } + else /* JPEG rgb -> rgba */ + if ((pBuf->iColortype == MNG_COLORTYPE_JPEGCOLOR) && + (iColortype == MNG_COLORTYPE_JPEGCOLORA)) + { + if (pBuf->iBitdepth <= 8) /* source <= 8 bits */ + { +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) + pData->fPromoterow = (mng_fptr)mng_promote_rgb8_rgba16; + else +#endif + pData->fPromoterow = (mng_fptr)mng_promote_rgb8_rgba8; + } +#ifndef MNG_NO_16BIT_SUPPORT + else /* source = 16 bits */ + pData->fPromoterow = (mng_fptr)mng_promote_rgb16_rgba16; +#endif + + iNewsamplesize = 4; + +#ifndef MNG_NO_16BIT_SUPPORT + if (iBitdepth == 16) /* 16-bit wide ? */ + iNewsamplesize = 8; +#endif + } + else /* JPEG rgba -> rgba */ + if ((pBuf->iColortype == MNG_COLORTYPE_JPEGCOLORA) && + (iColortype == MNG_COLORTYPE_JPEGCOLORA)) + { + iNewsamplesize = 4; +#ifndef MNG_NO_16BIT_SUPPORT + if (pBuf->iBitdepth <= 8) /* source <= 8 bits */ + if (iBitdepth == 16) + pData->fPromoterow = (mng_fptr)mng_promote_rgba8_rgba16; + if (iBitdepth == 16) + iNewsamplesize = 8; +#endif + } +#endif /* JNG */ + + /* found a proper promotion ? */ + if (pData->fPromoterow) + { + pData->pPromBuf = (mng_ptr)pBuf; + pData->iPromWidth = pBuf->iWidth; + iNewrowsize = iW * iNewsamplesize; + iNewbufsize = iH * iNewrowsize; + + MNG_ALLOC (pData, pNewbuf, iNewbufsize); + + pData->pPromSrc = (mng_ptr)pBuf->pImgdata; + pData->pPromDst = (mng_ptr)pNewbuf; + iY = 0; + + while ((!iRetcode) && (iY < iH)) + { + iRetcode = ((mng_promoterow)pData->fPromoterow) (pData); + pData->pPromSrc = (mng_uint8p)pData->pPromSrc + pBuf->iRowsize; + pData->pPromDst = (mng_uint8p)pData->pPromDst + iNewrowsize; +/* pData->pPromSrc = (mng_ptr)((mng_uint32)pData->pPromSrc + pBuf->iRowsize); */ +/* pData->pPromDst = (mng_ptr)((mng_uint32)pData->pPromDst + iNewrowsize); */ + iY++; + } + + MNG_FREEX (pData, pBuf->pImgdata, pBuf->iImgdatasize); + + pBuf->iBitdepth = iBitdepth; + pBuf->iColortype = iColortype; + pBuf->iSamplesize = iNewsamplesize; + pBuf->iRowsize = iNewrowsize; + pBuf->iImgdatasize = iNewbufsize; + pBuf->pImgdata = pNewbuf; + pBuf->bHasPLTE = MNG_FALSE; + pBuf->iPLTEcount = 0; + pBuf->bHasTRNS = MNG_FALSE; + pBuf->iTRNScount = 0; + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_IMGOBJECT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_MAGN +mng_retcode mng_magnify_imageobject (mng_datap pData, + mng_imagep pImage) +{ + mng_uint8p pNewdata; + mng_uint8p pSrcline1; + mng_uint8p pSrcline2; + mng_uint8p pTempline; + mng_uint8p pDstline; + mng_uint32 iNewrowsize; + mng_uint32 iNewsize; + mng_uint32 iY; + mng_int32 iS, iM; + mng_retcode iRetcode; + + mng_imagedatap pBuf = pImage->pImgbuf; + mng_uint32 iNewW = pBuf->iWidth; + mng_uint32 iNewH = pBuf->iHeight; + mng_magnify_x fMagnifyX = MNG_NULL; + mng_magnify_y fMagnifyY = MNG_NULL; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_IMGOBJECT, MNG_LC_START); +#endif + + if (pBuf->iColortype == MNG_COLORTYPE_INDEXED) /* indexed color ? */ + { /* concrete buffer ? */ + if ((pBuf->bConcrete) && (pImage->iId)) + MNG_ERROR (pData, MNG_INVALIDCOLORTYPE); + +#ifndef MNG_OPTIMIZE_FOOTPRINT_MAGN + if (pBuf->iTRNScount) /* with transparency ? */ + iRetcode = mng_promote_imageobject (pData, pImage, 8, 6, 0); + else + iRetcode = mng_promote_imageobject (pData, pImage, 8, 2, 0); + + if (iRetcode) /* on error bail out */ + return iRetcode; +#endif + } + +#ifdef MNG_OPTIMIZE_FOOTPRINT_MAGN + /* Promote everything to RGBA, using fill method 0 (LBR) */ + iRetcode = mng_promote_imageobject (pData, pImage, 8, 6, 0); + if (iRetcode) /* on error bail out */ + return iRetcode; +#endif + + if (pImage->iMAGN_MethodX) /* determine new width */ + { + if (pImage->iMAGN_MethodX == 1) + { + iNewW = pImage->iMAGN_ML; + if (pBuf->iWidth > 1) + iNewW = iNewW + pImage->iMAGN_MR; + if (pBuf->iWidth > 2) + iNewW = iNewW + (pBuf->iWidth - 2) * (pImage->iMAGN_MX); + } + else + { + iNewW = pBuf->iWidth + pImage->iMAGN_ML - 1; + if (pBuf->iWidth > 2) + iNewW = iNewW + pImage->iMAGN_MR - 1; + if (pBuf->iWidth > 3) + iNewW = iNewW + (pBuf->iWidth - 3) * (pImage->iMAGN_MX - 1); + } + } + + if (pImage->iMAGN_MethodY) /* determine new height */ + { + if (pImage->iMAGN_MethodY == 1) + { + iNewH = pImage->iMAGN_MT; + if (pBuf->iHeight > 1) + iNewH = iNewH + pImage->iMAGN_ML; + if (pBuf->iHeight > 2) + iNewH = iNewH + (pBuf->iHeight - 2) * (pImage->iMAGN_MY); + } + else + { + iNewH = pBuf->iHeight + pImage->iMAGN_MT - 1; + if (pBuf->iHeight > 2) + iNewH = iNewH + pImage->iMAGN_MB - 1; + if (pBuf->iHeight > 3) + iNewH = iNewH + (pBuf->iHeight - 3) * (pImage->iMAGN_MY - 1); + } + } + /* get new buffer */ + iNewrowsize = iNewW * pBuf->iSamplesize; + iNewsize = iNewH * iNewrowsize; + + MNG_ALLOC (pData, pNewdata, iNewsize); + + switch (pBuf->iColortype) /* determine magnification routines */ + { +#ifndef MNG_OPTIMIZE_FOOTPRINT_MAGN + case 0 : ; + case 8 : { + if (pBuf->iBitdepth <= 8) + { + switch (pImage->iMAGN_MethodX) + { + case 1 : { fMagnifyX = mng_magnify_g8_x1; break; } + case 2 : { fMagnifyX = mng_magnify_g8_x2; break; } + case 3 : { fMagnifyX = mng_magnify_g8_x3; break; } + case 4 : { fMagnifyX = mng_magnify_g8_x2; break; } + case 5 : { fMagnifyX = mng_magnify_g8_x3; break; } + } + + switch (pImage->iMAGN_MethodY) + { + case 1 : { fMagnifyY = mng_magnify_g8_y1; break; } + case 2 : { fMagnifyY = mng_magnify_g8_y2; break; } + case 3 : { fMagnifyY = mng_magnify_g8_y3; break; } + case 4 : { fMagnifyY = mng_magnify_g8_y2; break; } + case 5 : { fMagnifyY = mng_magnify_g8_y3; break; } + } + } +#ifndef MNG_NO_16BIT_SUPPORT + else + { + switch (pImage->iMAGN_MethodX) + { + case 1 : { fMagnifyX = mng_magnify_g16_x1; break; } + case 2 : { fMagnifyX = mng_magnify_g16_x2; break; } + case 3 : { fMagnifyX = mng_magnify_g16_x3; break; } + case 4 : { fMagnifyX = mng_magnify_g16_x2; break; } + case 5 : { fMagnifyX = mng_magnify_g16_x3; break; } + } + + switch (pImage->iMAGN_MethodY) + { + case 1 : { fMagnifyY = mng_magnify_g16_y1; break; } + case 2 : { fMagnifyY = mng_magnify_g16_y2; break; } + case 3 : { fMagnifyY = mng_magnify_g16_y3; break; } + case 4 : { fMagnifyY = mng_magnify_g16_y2; break; } + case 5 : { fMagnifyY = mng_magnify_g16_y3; break; } + } + } +#endif + + break; + } + + case 2 : ; + case 10 : { + if (pBuf->iBitdepth <= 8) + { + switch (pImage->iMAGN_MethodX) + { + case 1 : { fMagnifyX = mng_magnify_rgb8_x1; break; } + case 2 : { fMagnifyX = mng_magnify_rgb8_x2; break; } + case 3 : { fMagnifyX = mng_magnify_rgb8_x3; break; } + case 4 : { fMagnifyX = mng_magnify_rgb8_x2; break; } + case 5 : { fMagnifyX = mng_magnify_rgb8_x3; break; } + } + + switch (pImage->iMAGN_MethodY) + { + case 1 : { fMagnifyY = mng_magnify_rgb8_y1; break; } + case 2 : { fMagnifyY = mng_magnify_rgb8_y2; break; } + case 3 : { fMagnifyY = mng_magnify_rgb8_y3; break; } + case 4 : { fMagnifyY = mng_magnify_rgb8_y2; break; } + case 5 : { fMagnifyY = mng_magnify_rgb8_y3; break; } + } + } +#ifndef MNG_NO_16BIT_SUPPORT + else + { + switch (pImage->iMAGN_MethodX) + { + case 1 : { fMagnifyX = mng_magnify_rgb16_x1; break; } + case 2 : { fMagnifyX = mng_magnify_rgb16_x2; break; } + case 3 : { fMagnifyX = mng_magnify_rgb16_x3; break; } + case 4 : { fMagnifyX = mng_magnify_rgb16_x2; break; } + case 5 : { fMagnifyX = mng_magnify_rgb16_x3; break; } + } + + switch (pImage->iMAGN_MethodY) + { + case 1 : { fMagnifyY = mng_magnify_rgb16_y1; break; } + case 2 : { fMagnifyY = mng_magnify_rgb16_y2; break; } + case 3 : { fMagnifyY = mng_magnify_rgb16_y3; break; } + case 4 : { fMagnifyY = mng_magnify_rgb16_y2; break; } + case 5 : { fMagnifyY = mng_magnify_rgb16_y3; break; } + } + } +#endif + + break; + } + + case 4 : ; + case 12 : { + if (pBuf->iBitdepth <= 8) + { + switch (pImage->iMAGN_MethodX) + { + case 1 : { fMagnifyX = mng_magnify_ga8_x1; break; } + case 2 : { fMagnifyX = mng_magnify_ga8_x2; break; } + case 3 : { fMagnifyX = mng_magnify_ga8_x3; break; } + case 4 : { fMagnifyX = mng_magnify_ga8_x4; break; } + case 5 : { fMagnifyX = mng_magnify_ga8_x5; break; } + } + + switch (pImage->iMAGN_MethodY) + { + case 1 : { fMagnifyY = mng_magnify_ga8_y1; break; } + case 2 : { fMagnifyY = mng_magnify_ga8_y2; break; } + case 3 : { fMagnifyY = mng_magnify_ga8_y3; break; } + case 4 : { fMagnifyY = mng_magnify_ga8_y4; break; } + case 5 : { fMagnifyY = mng_magnify_ga8_y5; break; } + } + } +#ifndef MNG_NO_16BIT_SUPPORT + else + { + switch (pImage->iMAGN_MethodX) + { + case 1 : { fMagnifyX = mng_magnify_ga16_x1; break; } + case 2 : { fMagnifyX = mng_magnify_ga16_x2; break; } + case 3 : { fMagnifyX = mng_magnify_ga16_x3; break; } + case 4 : { fMagnifyX = mng_magnify_ga16_x4; break; } + case 5 : { fMagnifyX = mng_magnify_ga16_x5; break; } + } + + switch (pImage->iMAGN_MethodY) + { + case 1 : { fMagnifyY = mng_magnify_ga16_y1; break; } + case 2 : { fMagnifyY = mng_magnify_ga16_y2; break; } + case 3 : { fMagnifyY = mng_magnify_ga16_y3; break; } + case 4 : { fMagnifyY = mng_magnify_ga16_y4; break; } + case 5 : { fMagnifyY = mng_magnify_ga16_y5; break; } + } + } +#endif + + break; + } +#endif + + case 6 : ; + case 14 : { + if (pBuf->iBitdepth <= 8) + { + switch (pImage->iMAGN_MethodX) + { + case 1 : { fMagnifyX = mng_magnify_rgba8_x1; break; } + case 2 : { fMagnifyX = mng_magnify_rgba8_x2; break; } + case 3 : { fMagnifyX = mng_magnify_rgba8_x3; break; } + case 4 : { fMagnifyX = mng_magnify_rgba8_x4; break; } + case 5 : { fMagnifyX = mng_magnify_rgba8_x5; break; } + } + + switch (pImage->iMAGN_MethodY) + { + case 1 : { fMagnifyY = mng_magnify_rgba8_y1; break; } + case 2 : { fMagnifyY = mng_magnify_rgba8_y2; break; } + case 3 : { fMagnifyY = mng_magnify_rgba8_y3; break; } + case 4 : { fMagnifyY = mng_magnify_rgba8_y4; break; } + case 5 : { fMagnifyY = mng_magnify_rgba8_y5; break; } + } + } +#ifndef MNG_NO_16BIT_SUPPORT +#ifndef MNG_OPTIMIZE_FOOTPRINT_MAGN + else + { + switch (pImage->iMAGN_MethodX) + { + case 1 : { fMagnifyX = mng_magnify_rgba16_x1; break; } + case 2 : { fMagnifyX = mng_magnify_rgba16_x2; break; } + case 3 : { fMagnifyX = mng_magnify_rgba16_x3; break; } + case 4 : { fMagnifyX = mng_magnify_rgba16_x4; break; } + case 5 : { fMagnifyX = mng_magnify_rgba16_x5; break; } + } + + switch (pImage->iMAGN_MethodY) + { + case 1 : { fMagnifyY = mng_magnify_rgba16_y1; break; } + case 2 : { fMagnifyY = mng_magnify_rgba16_y2; break; } + case 3 : { fMagnifyY = mng_magnify_rgba16_y3; break; } + case 4 : { fMagnifyY = mng_magnify_rgba16_y4; break; } + case 5 : { fMagnifyY = mng_magnify_rgba16_y5; break; } + } + } +#endif +#endif + break; + } + } + + pSrcline1 = pBuf->pImgdata; /* initialize row-loop variables */ + pDstline = pNewdata; + /* allocate temporary row */ + MNG_ALLOC (pData, pTempline, iNewrowsize); + + for (iY = 0; iY < pBuf->iHeight; iY++) + { + pSrcline2 = pSrcline1 + pBuf->iRowsize; + + if (fMagnifyX) /* magnifying in X-direction ? */ + { + iRetcode = fMagnifyX (pData, pImage->iMAGN_MX, + pImage->iMAGN_ML, pImage->iMAGN_MR, + pBuf->iWidth, pSrcline1, pDstline); + + if (iRetcode) /* on error bail out */ + { + MNG_FREEX (pData, pTempline, iNewrowsize); + MNG_FREEX (pData, pNewdata, iNewsize); + return iRetcode; + } + } + else + { + MNG_COPY (pDstline, pSrcline1, iNewrowsize); + } + + pDstline += iNewrowsize; + /* magnifying in Y-direction ? */ + if ((fMagnifyY) && + ((iY < pBuf->iHeight - 1) || (pBuf->iHeight == 1) || (pImage->iMAGN_MethodY == 1))) + { + if (iY == 0) /* first interval ? */ + { + if (pBuf->iHeight == 1) /* single row ? */ + pSrcline2 = MNG_NULL; + + iM = (mng_int32)pImage->iMAGN_MT; + } + else /* last interval ? */ + if (((pImage->iMAGN_MethodY == 1) && (iY == (pBuf->iHeight - 1))) || + ((pImage->iMAGN_MethodY != 1) && (iY == (pBuf->iHeight - 2))) ) + iM = (mng_int32)pImage->iMAGN_MB; + else /* middle interval */ + iM = (mng_int32)pImage->iMAGN_MY; + + for (iS = 1; iS < iM; iS++) + { + iRetcode = fMagnifyY (pData, iS, iM, pBuf->iWidth, + pSrcline1, pSrcline2, pTempline); + + if (iRetcode) /* on error bail out */ + { + MNG_FREEX (pData, pTempline, iNewrowsize); + MNG_FREEX (pData, pNewdata, iNewsize); + return iRetcode; + } + + if (fMagnifyX) /* magnifying in X-direction ? */ + { + iRetcode = fMagnifyX (pData, pImage->iMAGN_MX, + pImage->iMAGN_ML, pImage->iMAGN_MR, + pBuf->iWidth, pTempline, pDstline); + + if (iRetcode) /* on error bail out */ + { + MNG_FREEX (pData, pTempline, iNewrowsize); + MNG_FREEX (pData, pNewdata, iNewsize); + return iRetcode; + } + } + else + { + MNG_COPY (pDstline, pTempline, iNewrowsize); + } + + pDstline += iNewrowsize; + } + } + + pSrcline1 += pBuf->iRowsize; + } + /* drop temporary row */ + MNG_FREEX (pData, pTempline, iNewrowsize); + /* drop old pixel-data */ + MNG_FREEX (pData, pBuf->pImgdata, pBuf->iImgdatasize); + + pBuf->pImgdata = pNewdata; /* save new buffer dimensions */ + pBuf->iRowsize = iNewrowsize; + pBuf->iImgdatasize = iNewsize; + pBuf->iWidth = iNewW; + pBuf->iHeight = iNewH; + + if (pImage->iId) /* real object ? */ + { + pImage->iMAGN_MethodX = 0; /* it's done; don't do it again !!! */ + pImage->iMAGN_MethodY = 0; + pImage->iMAGN_MX = 0; + pImage->iMAGN_MY = 0; + pImage->iMAGN_ML = 0; + pImage->iMAGN_MR = 0; + pImage->iMAGN_MT = 0; + pImage->iMAGN_MB = 0; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_IMGOBJECT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_colorcorrect_object (mng_datap pData, + mng_imagep pImage) +{ + mng_imagedatap pBuf = pImage->pImgbuf; + mng_retcode iRetcode; + mng_uint32 iY; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_COLORCORRECT_OBJECT, MNG_LC_START); +#endif + +#ifdef MNG_INCLUDE_JNG + if ((pBuf->iBitdepth < 8) || /* we need 8- or 16-bit RGBA !!! */ + ((pBuf->iColortype != MNG_COLORTYPE_RGBA ) && + (pBuf->iColortype != MNG_COLORTYPE_JPEGCOLORA) )) +#else + if (pBuf->iBitdepth < 8) /* we need 8- or 16-bit RGBA !!! */ +#endif + MNG_ERROR (pData, MNG_OBJNOTABSTRACT); + + if (!pBuf->bCorrected) /* only if not already done ! */ + { /* so the row routines now to find it */ + pData->pRetrieveobj = (mng_objectp)pImage; + pData->pStoreobj = (mng_objectp)pImage; + pData->pStorebuf = (mng_objectp)pImage->pImgbuf; + +#ifndef MNG_NO_16BIT_SUPPORT + if (pBuf->iBitdepth > 8) + { + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba16; + pData->fStorerow = (mng_fptr)mng_store_rgba16; + } + else +#endif + { + pData->fRetrieverow = (mng_fptr)mng_retrieve_rgba8; + pData->fStorerow = (mng_fptr)mng_store_rgba8; + } + + pData->bIsOpaque = MNG_FALSE; + + pData->iPass = -1; /* these are the object's dimensions now */ + pData->iRow = 0; + pData->iRowinc = 1; + pData->iCol = 0; + pData->iColinc = 1; + pData->iRowsamples = pBuf->iWidth; + pData->iRowsize = pData->iRowsamples << 2; + pData->iPixelofs = 0; + pData->bIsRGBA16 = MNG_FALSE; + /* adjust for 16-bit object ? */ +#ifndef MNG_NO_16BIT_SUPPORT + if (pBuf->iBitdepth > 8) + { + pData->bIsRGBA16 = MNG_TRUE; + pData->iRowsize = pData->iRowsamples << 3; + } +#endif + + pData->fCorrectrow = MNG_NULL; /* default no color-correction */ + +#ifdef MNG_NO_CMS + iRetcode = MNG_NOERROR; +#else +#if defined(MNG_FULL_CMS) /* determine color-management routine */ + iRetcode = mng_init_full_cms (pData, MNG_FALSE, MNG_FALSE, MNG_TRUE); +#elif defined(MNG_GAMMA_ONLY) + iRetcode = mng_init_gamma_only (pData, MNG_FALSE, MNG_FALSE, MNG_TRUE); +#elif defined(MNG_APP_CMS) + iRetcode = mng_init_app_cms (pData, MNG_FALSE, MNG_FALSE, MNG_TRUE); +#endif + if (iRetcode) /* on error bail out */ + return iRetcode; +#endif /* MNG_NO_CMS */ + + if (pData->fCorrectrow) /* really correct something ? */ + { /* get a temporary row-buffer */ + MNG_ALLOC (pData, pData->pRGBArow, pData->iRowsize); + + pData->pWorkrow = pData->pRGBArow; + iY = 0; /* start from the top */ + + while ((!iRetcode) && (iY < pBuf->iHeight)) + { /* get a row */ + iRetcode = ((mng_retrieverow)pData->fRetrieverow) (pData); + + if (!iRetcode) /* color correct it */ + iRetcode = ((mng_correctrow)pData->fCorrectrow) (pData); + + if (!iRetcode) /* store it back ! */ + iRetcode = ((mng_storerow)pData->fStorerow) (pData); + + if (!iRetcode) /* adjust variables for next row */ + iRetcode = mng_next_row (pData); + + iY++; /* and next line */ + } + /* drop the temporary row-buffer */ + MNG_FREEX (pData, pData->pRGBArow, pData->iRowsize); + + if (iRetcode) /* on error bail out */ + return iRetcode; + +#if defined(MNG_FULL_CMS) /* cleanup cms stuff */ + iRetcode = mng_clear_cms (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; +#endif + } + + pBuf->bCorrected = MNG_TRUE; /* let's not go through that again ! */ + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_COLORCORRECT_OBJECT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ +/* * * */ +/* * Animation-object routines * */ +/* * * */ +/* * these handle the animation objects used to re-run parts of a MNG. * */ +/* * eg. during LOOP or TERM processing * */ +/* * * */ +/* ************************************************************************** */ + +void mng_add_ani_object (mng_datap pData, + mng_object_headerp pObject) +{ + mng_object_headerp pLast = (mng_object_headerp)pData->pLastaniobj; + + if (pLast) /* link it as last in the chain */ + { + pObject->pPrev = pLast; + pLast->pNext = pObject; + } + else + { + pObject->pPrev = MNG_NULL; /* be on the safe side */ + pData->pFirstaniobj = pObject; + } + + pObject->pNext = MNG_NULL; /* be on the safe side */ + pData->pLastaniobj = pObject; + /* keep track for jumping */ + pObject->iFramenr = pData->iFrameseq; + pObject->iLayernr = pData->iLayerseq; + pObject->iPlaytime = pData->iFrametime; + /* save restart object ? */ + if ((pData->bDisplaying) && (!pData->bRunning) && (!pData->pCurraniobj)) + pData->pCurraniobj = pObject; + + return; +} + +/* ************************************************************************** */ +/* ************************************************************************** */ + +mng_retcode mng_create_ani_image (mng_datap pData) +{ + mng_ani_imagep pImage; + mng_imagep pCurrent; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_IMAGE, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifndef MNG_NO_DELTA_PNG + if (pData->bHasDHDR) /* processing delta-image ? */ + pCurrent = (mng_imagep)pData->pObjzero; + else /* get the current object */ +#endif + pCurrent = (mng_imagep)pData->pCurrentobj; + + if (!pCurrent) /* otherwise object 0 */ + pCurrent = (mng_imagep)pData->pObjzero; + /* now just clone the object !!! */ + iRetcode = mng_clone_imageobject (pData, 0, MNG_FALSE, pCurrent->bVisible, + MNG_FALSE, MNG_FALSE, 0, 0, 0, pCurrent, + &pImage); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + pImage->sHeader.fCleanup = mng_free_ani_image; + pImage->sHeader.fProcess = mng_process_ani_image; + + mng_add_ani_object (pData, (mng_object_headerp)pImage); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_IMAGE, MNG_LC_END); +#endif + + return MNG_NOERROR; /* okido */ +} + +/* ************************************************************************** */ + +mng_retcode mng_free_ani_image (mng_datap pData, + mng_objectp pObject) +{ + mng_ani_imagep pImage = (mng_ani_imagep)pObject; + mng_imagedatap pImgbuf = pImage->pImgbuf; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_IMAGE, MNG_LC_START); +#endif + /* unlink the image-data buffer */ + iRetcode = mng_free_imagedataobject (pData, pImgbuf); + /* drop its own buffer */ + MNG_FREEX (pData, pImage, sizeof (mng_ani_image)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_IMAGE, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_image (mng_datap pData, + mng_objectp pObject) +{ + mng_retcode iRetcode = MNG_NOERROR; + mng_ani_imagep pImage = (mng_imagep)pObject; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_IMAGE, MNG_LC_START); +#endif + +#ifndef MNG_NO_DELTA_PNG + if (pData->bHasDHDR) /* processing delta-image ? */ + { + mng_imagep pDelta = (mng_imagep)pData->pDeltaImage; + + if (!pData->iBreakpoint) /* only execute if not broken before */ + { /* make sure to process pixels as well */ + pData->bDeltaimmediate = MNG_FALSE; + /* execute the delta process */ + iRetcode = mng_execute_delta_image (pData, pDelta, (mng_imagep)pObject); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + /* now go and shoot it off (if required) */ + if ((pDelta->bVisible) && (pDelta->bViewable)) + iRetcode = mng_display_image (pData, pDelta, MNG_FALSE); + + if (!pData->bTimerset) + pData->bHasDHDR = MNG_FALSE; /* this image signifies IEND !! */ + + } + else +#endif + if (pData->pCurrentobj) /* active object ? */ + { + mng_imagep pCurrent = (mng_imagep)pData->pCurrentobj; + mng_imagedatap pBuf = pCurrent->pImgbuf; + + if (!pData->iBreakpoint) /* don't copy it again ! */ + { + if (pBuf->iImgdatasize) /* buffer present in active object ? */ + /* then drop it */ + MNG_FREE (pData, pBuf->pImgdata, pBuf->iImgdatasize); + +#ifndef MNG_SKIPCHUNK_iCCP + if (pBuf->iProfilesize) /* iCCP profile present ? */ + /* then drop it */ + MNG_FREE (pData, pBuf->pProfile, pBuf->iProfilesize); +#endif + /* now blatently copy the animation buffer */ + MNG_COPY (pBuf, pImage->pImgbuf, sizeof (mng_imagedata)); + /* copy viewability */ + pCurrent->bViewable = pImage->bViewable; + + if (pBuf->iImgdatasize) /* sample buffer present ? */ + { /* then make a copy */ + MNG_ALLOC (pData, pBuf->pImgdata, pBuf->iImgdatasize); + MNG_COPY (pBuf->pImgdata, pImage->pImgbuf->pImgdata, pBuf->iImgdatasize); + } + +#ifndef MNG_SKIPCHUNK_iCCP + if (pBuf->iProfilesize) /* iCCP profile present ? */ + { /* then make a copy */ + MNG_ALLOC (pData, pBuf->pProfile, pBuf->iProfilesize); + MNG_COPY (pBuf->pProfile, pImage->pImgbuf->pProfile, pBuf->iProfilesize); + } +#endif + } + /* now go and shoot it off (if required) */ + if ((pCurrent->bVisible) && (pCurrent->bViewable)) + iRetcode = mng_display_image (pData, pCurrent, MNG_FALSE); + } + else + { + mng_imagep pObjzero = (mng_imagep)pData->pObjzero; + mng_imagedatap pBuf = pObjzero->pImgbuf; + + if (!pData->iBreakpoint) /* don't copy it again ! */ + { + if (pBuf->iImgdatasize) /* buffer present in active object ? */ + /* then drop it */ + MNG_FREE (pData, pBuf->pImgdata, pBuf->iImgdatasize); + +#ifndef MNG_SKIPCHUNK_iCCP + if (pBuf->iProfilesize) /* iCCP profile present ? */ + /* then drop it */ + MNG_FREE (pData, pBuf->pProfile, pBuf->iProfilesize); +#endif + /* now blatently copy the animation buffer */ + MNG_COPY (pBuf, pImage->pImgbuf, sizeof (mng_imagedata)); + /* copy viewability */ + pObjzero->bViewable = pImage->bViewable; + + if (pBuf->iImgdatasize) /* sample buffer present ? */ + { /* then make a copy */ + MNG_ALLOC (pData, pBuf->pImgdata, pBuf->iImgdatasize); + MNG_COPY (pBuf->pImgdata, pImage->pImgbuf->pImgdata, pBuf->iImgdatasize); + } + +#ifndef MNG_SKIPCHUNK_iCCP + if (pBuf->iProfilesize) /* iCCP profile present ? */ + { /* then make a copy */ + MNG_ALLOC (pData, pBuf->pProfile, pBuf->iProfilesize); + MNG_COPY (pBuf->pProfile, pImage->pImgbuf->pProfile, pBuf->iProfilesize); + } +#endif + } + /* now go and show it */ + iRetcode = mng_display_image (pData, pObjzero, MNG_FALSE); + } + + if (!iRetcode) /* all's well ? */ + { + if (pData->bTimerset) /* timer break ? */ + pData->iBreakpoint = 99; /* fictive number; no more processing needed! */ + else + pData->iBreakpoint = 0; /* else clear it */ + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_IMAGE, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_ani_plte (mng_datap pData, + mng_uint32 iEntrycount, + mng_palette8ep paEntries) +#else +mng_retcode mng_create_ani_plte (mng_datap pData) +#endif +{ + mng_ani_pltep pPLTE; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_PLTE, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_ptr pTemp; + mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_plte), + mng_free_obj_general, + mng_process_ani_plte, + &pTemp); + if (iRetcode) + return iRetcode; + pPLTE = (mng_ani_pltep)pTemp; +#else + MNG_ALLOC (pData, pPLTE, sizeof (mng_ani_plte)); + + pPLTE->sHeader.fCleanup = mng_free_ani_plte; + pPLTE->sHeader.fProcess = mng_process_ani_plte; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pPLTE); + +#ifndef MNG_OPTIMIZE_CHUNKREADER + pPLTE->iEntrycount = iEntrycount; + MNG_COPY (pPLTE->aEntries, paEntries, sizeof (pPLTE->aEntries)); +#else + pPLTE->iEntrycount = pData->iGlobalPLTEcount; + MNG_COPY (pPLTE->aEntries, pData->aGlobalPLTEentries, sizeof (pPLTE->aEntries)); +#endif + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_PLTE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_OBJCLEANUP +mng_retcode mng_free_ani_plte (mng_datap pData, + mng_objectp pObject) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_PLTE, MNG_LC_START); +#endif + + MNG_FREEX (pData, pObject, sizeof (mng_ani_plte)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_PLTE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_plte (mng_datap pData, + mng_objectp pObject) +{ + mng_ani_pltep pPLTE = (mng_ani_pltep)pObject; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_PLTE, MNG_LC_START); +#endif + + pData->bHasglobalPLTE = MNG_TRUE; + pData->iGlobalPLTEcount = pPLTE->iEntrycount; + + MNG_COPY (pData->aGlobalPLTEentries, pPLTE->aEntries, sizeof (pPLTE->aEntries)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_PLTE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_ani_trns (mng_datap pData, + mng_uint32 iRawlen, + mng_uint8p pRawdata) +#else +mng_retcode mng_create_ani_trns (mng_datap pData) +#endif +{ + mng_ani_trnsp pTRNS; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_TRNS, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_ptr pTemp; + mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_trns), + mng_free_obj_general, + mng_process_ani_trns, + &pTemp); + if (iRetcode) + return iRetcode; + pTRNS = (mng_ani_trnsp)pTemp; +#else + MNG_ALLOC (pData, pTRNS, sizeof (mng_ani_trns)); + + pTRNS->sHeader.fCleanup = mng_free_ani_trns; + pTRNS->sHeader.fProcess = mng_process_ani_trns; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pTRNS); + +#ifndef MNG_OPTIMIZE_CHUNKREADER + pTRNS->iRawlen = iRawlen; + MNG_COPY (pTRNS->aRawdata, pRawdata, sizeof (pTRNS->aRawdata)); +#else + pTRNS->iRawlen = pData->iGlobalTRNSrawlen; + MNG_COPY (pTRNS->aRawdata, pData->aGlobalTRNSrawdata, sizeof (pTRNS->aRawdata)); +#endif + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_TRNS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_OBJCLEANUP +mng_retcode mng_free_ani_trns (mng_datap pData, + mng_objectp pObject) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_TRNS, MNG_LC_START); +#endif + + MNG_FREEX (pData, pObject, sizeof (mng_ani_trns)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_TRNS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_trns (mng_datap pData, + mng_objectp pObject) +{ + mng_ani_trnsp pTRNS = (mng_ani_trnsp)pObject; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_TRNS, MNG_LC_START); +#endif + + pData->bHasglobalTRNS = MNG_TRUE; + pData->iGlobalTRNSrawlen = pTRNS->iRawlen; + + MNG_COPY (pData->aGlobalTRNSrawdata, pTRNS->aRawdata, sizeof (pTRNS->aRawdata)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_TRNS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_gAMA +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_ani_gama (mng_datap pData, + mng_bool bEmpty, + mng_uint32 iGamma) +#else +mng_retcode mng_create_ani_gama (mng_datap pData, + mng_chunkp pChunk) +#endif +{ + mng_ani_gamap pGAMA; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_GAMA, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_ptr pTemp; + mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_gama), + mng_free_obj_general, + mng_process_ani_gama, + &pTemp); + if (iRetcode) + return iRetcode; + pGAMA = (mng_ani_gamap)pTemp; +#else + MNG_ALLOC (pData, pGAMA, sizeof (mng_ani_gama)); + + pGAMA->sHeader.fCleanup = mng_free_ani_gama; + pGAMA->sHeader.fProcess = mng_process_ani_gama; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pGAMA); + +#ifndef MNG_OPTIMIZE_CHUNKREADER + pGAMA->bEmpty = bEmpty; + pGAMA->iGamma = iGamma; +#else + pGAMA->bEmpty = ((mng_gamap)pChunk)->bEmpty; + pGAMA->iGamma = ((mng_gamap)pChunk)->iGamma; +#endif + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_GAMA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_OBJCLEANUP +mng_retcode mng_free_ani_gama (mng_datap pData, + mng_objectp pObject) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_GAMA, MNG_LC_START); +#endif + + MNG_FREEX (pData, pObject, sizeof (mng_ani_gama)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_GAMA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_gama (mng_datap pData, + mng_objectp pObject) +{ + mng_ani_gamap pGAMA = (mng_ani_gamap)pObject; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_GAMA, MNG_LC_START); +#endif + + if (pGAMA->bEmpty) /* empty chunk ? */ + { /* clear global gAMA */ + pData->bHasglobalGAMA = MNG_FALSE; + pData->iGlobalGamma = 0; + } + else + { /* set global gAMA */ + pData->bHasglobalGAMA = MNG_TRUE; + pData->iGlobalGamma = pGAMA->iGamma; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_GAMA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_cHRM +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_ani_chrm (mng_datap pData, + mng_bool bEmpty, + mng_uint32 iWhitepointx, + mng_uint32 iWhitepointy, + mng_uint32 iRedx, + mng_uint32 iRedy, + mng_uint32 iGreenx, + mng_uint32 iGreeny, + mng_uint32 iBluex, + mng_uint32 iBluey) +#else +mng_retcode mng_create_ani_chrm (mng_datap pData, + mng_chunkp pChunk) +#endif +{ + mng_ptr pTemp; + mng_ani_chrmp pCHRM; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_CHRM, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_chrm), + mng_free_obj_general, + mng_process_ani_chrm, + &pTemp); + if (iRetcode) + return iRetcode; + pCHRM = (mng_ani_chrmp)pTemp; +#else + MNG_ALLOC (pData, pCHRM, sizeof (mng_ani_chrm)); + + pCHRM->sHeader.fCleanup = mng_free_ani_chrm; + pCHRM->sHeader.fProcess = mng_process_ani_chrm; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pCHRM); + +#ifndef MNG_OPTIMIZE_CHUNKREADER + pCHRM->bEmpty = bEmpty; + pCHRM->iWhitepointx = iWhitepointx; + pCHRM->iWhitepointy = iWhitepointy; + pCHRM->iRedx = iRedx; + pCHRM->iRedy = iRedy; + pCHRM->iGreenx = iGreenx; + pCHRM->iGreeny = iGreeny; + pCHRM->iBluex = iBluex; + pCHRM->iBluey = iBluey; +#else + pCHRM->bEmpty = ((mng_chrmp)pChunk)->bEmpty; + pCHRM->iWhitepointx = ((mng_chrmp)pChunk)->iWhitepointx; + pCHRM->iWhitepointy = ((mng_chrmp)pChunk)->iWhitepointy; + pCHRM->iRedx = ((mng_chrmp)pChunk)->iRedx; + pCHRM->iRedy = ((mng_chrmp)pChunk)->iRedy; + pCHRM->iGreenx = ((mng_chrmp)pChunk)->iGreenx; + pCHRM->iGreeny = ((mng_chrmp)pChunk)->iGreeny; + pCHRM->iBluex = ((mng_chrmp)pChunk)->iBluex; + pCHRM->iBluey = ((mng_chrmp)pChunk)->iBluey; +#endif + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_CHRM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_OBJCLEANUP +mng_retcode mng_free_ani_chrm (mng_datap pData, + mng_objectp pObject) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_CHRM, MNG_LC_START); +#endif + + MNG_FREEX (pData, pObject, sizeof (mng_ani_chrm)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_CHRM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_chrm (mng_datap pData, + mng_objectp pObject) +{ + mng_ani_chrmp pCHRM = (mng_ani_chrmp)pObject; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_CHRM, MNG_LC_START); +#endif + + if (pCHRM->bEmpty) /* empty chunk ? */ + { /* clear global cHRM */ + pData->bHasglobalCHRM = MNG_FALSE; + pData->iGlobalWhitepointx = 0; + pData->iGlobalWhitepointy = 0; + pData->iGlobalPrimaryredx = 0; + pData->iGlobalPrimaryredy = 0; + pData->iGlobalPrimarygreenx = 0; + pData->iGlobalPrimarygreeny = 0; + pData->iGlobalPrimarybluex = 0; + pData->iGlobalPrimarybluey = 0; + } + else + { /* set global cHRM */ + pData->bHasglobalCHRM = MNG_TRUE; + pData->iGlobalWhitepointx = pCHRM->iWhitepointx; + pData->iGlobalWhitepointy = pCHRM->iWhitepointy; + pData->iGlobalPrimaryredx = pCHRM->iRedx; + pData->iGlobalPrimaryredy = pCHRM->iRedy; + pData->iGlobalPrimarygreenx = pCHRM->iGreenx; + pData->iGlobalPrimarygreeny = pCHRM->iGreeny; + pData->iGlobalPrimarybluex = pCHRM->iBluex; + pData->iGlobalPrimarybluey = pCHRM->iBluey; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_CHRM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_sRGB +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_ani_srgb (mng_datap pData, + mng_bool bEmpty, + mng_uint8 iRenderingintent) +#else +mng_retcode mng_create_ani_srgb (mng_datap pData, + mng_chunkp pChunk) +#endif +{ + mng_ani_srgbp pSRGB; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_SRGB, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_ptr pTemp; + mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_srgb), + mng_free_obj_general, + mng_process_ani_srgb, + &pTemp); + if (iRetcode) + return iRetcode; + pSRGB = (mng_ani_srgbp)pTemp; +#else + MNG_ALLOC (pData, pSRGB, sizeof (mng_ani_srgb)); + + pSRGB->sHeader.fCleanup = mng_free_ani_srgb; + pSRGB->sHeader.fProcess = mng_process_ani_srgb; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pSRGB); + +#ifndef MNG_OPTIMIZE_CHUNKREADER + pSRGB->bEmpty = bEmpty; + pSRGB->iRenderingintent = iRenderingintent; +#else + pSRGB->bEmpty = ((mng_srgbp)pChunk)->bEmpty; + pSRGB->iRenderingintent = ((mng_srgbp)pChunk)->iRenderingintent; +#endif + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_SRGB, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_OBJCLEANUP +mng_retcode mng_free_ani_srgb (mng_datap pData, + mng_objectp pObject) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_SRGB, MNG_LC_START); +#endif + + MNG_FREEX (pData, pObject, sizeof (mng_ani_srgb)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_SRGB, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_srgb (mng_datap pData, + mng_objectp pObject) +{ + mng_ani_srgbp pSRGB = (mng_ani_srgbp)pObject; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_SRGB, MNG_LC_START); +#endif + + if (pSRGB->bEmpty) /* empty chunk ? */ + { /* clear global sRGB */ + pData->bHasglobalSRGB = MNG_FALSE; + pData->iGlobalRendintent = 0; + } + else + { /* set global sRGB */ + pData->bHasglobalSRGB = MNG_TRUE; + pData->iGlobalRendintent = pSRGB->iRenderingintent; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_SRGB, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_iCCP +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_ani_iccp (mng_datap pData, + mng_bool bEmpty, + mng_uint32 iProfilesize, + mng_ptr pProfile) +#else +mng_retcode mng_create_ani_iccp (mng_datap pData, + mng_chunkp pChunk) +#endif +{ + mng_ptr pTemp; + mng_ani_iccpp pICCP; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_ICCP, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_iccp), + mng_free_ani_iccp, + mng_process_ani_iccp, + &pTemp); + if (iRetcode) + return iRetcode; + pICCP = (mng_ani_iccpp)pTemp; +#else + MNG_ALLOC (pData, pICCP, sizeof (mng_ani_iccp)); + + pICCP->sHeader.fCleanup = mng_free_ani_iccp; + pICCP->sHeader.fProcess = mng_process_ani_iccp; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pICCP); + +#ifndef MNG_OPTIMIZE_CHUNKREADER + pICCP->bEmpty = bEmpty; + pICCP->iProfilesize = iProfilesize; + + if (iProfilesize) + { + MNG_ALLOC (pData, pICCP->pProfile, iProfilesize); + MNG_COPY (pICCP->pProfile, pProfile, iProfilesize); + } +#else + pICCP->bEmpty = ((mng_iccpp)pChunk)->bEmpty; + pICCP->iProfilesize = ((mng_iccpp)pChunk)->iProfilesize; + + if (pICCP->iProfilesize) + { + MNG_ALLOC (pData, pICCP->pProfile, pICCP->iProfilesize); + MNG_COPY (pICCP->pProfile, ((mng_iccpp)pChunk)->pProfile, pICCP->iProfilesize); + } +#endif + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_ICCP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_free_ani_iccp (mng_datap pData, + mng_objectp pObject) +{ + mng_ani_iccpp pICCP = (mng_ani_iccpp)pObject; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_ICCP, MNG_LC_START); +#endif + + if (pICCP->iProfilesize) + MNG_FREEX (pData, pICCP->pProfile, pICCP->iProfilesize); + +#ifndef MNG_OPTIMIZE_OBJCLEANUP + MNG_FREEX (pData, pObject, sizeof (mng_ani_iccp)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_ICCP, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_OBJCLEANUP + return MNG_NOERROR; +#else + return mng_free_obj_general(pData, pObject); +#endif +} + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_iccp (mng_datap pData, + mng_objectp pObject) +{ + mng_ani_iccpp pICCP = (mng_ani_iccpp)pObject; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_ICCP, MNG_LC_START); +#endif + + if (pICCP->bEmpty) /* empty chunk ? */ + { /* clear global iCCP */ + pData->bHasglobalICCP = MNG_FALSE; + + if (pData->iGlobalProfilesize) + MNG_FREEX (pData, pData->pGlobalProfile, pData->iGlobalProfilesize); + + pData->iGlobalProfilesize = 0; + pData->pGlobalProfile = MNG_NULL; + } + else + { /* set global iCCP */ + pData->bHasglobalICCP = MNG_TRUE; + pData->iGlobalProfilesize = pICCP->iProfilesize; + + if (pICCP->iProfilesize) + { + MNG_ALLOC (pData, pData->pGlobalProfile, pICCP->iProfilesize); + MNG_COPY (pData->pGlobalProfile, pICCP->pProfile, pICCP->iProfilesize); + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_ICCP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_bKGD +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_ani_bkgd (mng_datap pData, + mng_uint16 iRed, + mng_uint16 iGreen, + mng_uint16 iBlue) +#else +mng_retcode mng_create_ani_bkgd (mng_datap pData) +#endif +{ + mng_ptr pTemp; + mng_ani_bkgdp pBKGD; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_BKGD, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_bkgd), + mng_free_obj_general, + mng_process_ani_bkgd, + &pTemp); + if (iRetcode) + return iRetcode; + pBKGD = (mng_ani_bkgdp)pTemp; +#else + MNG_ALLOC (pData, pBKGD, sizeof (mng_ani_bkgd)); + + pBKGD->sHeader.fCleanup = mng_free_ani_bkgd; + pBKGD->sHeader.fProcess = mng_process_ani_bkgd; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pBKGD); + +#ifndef MNG_OPTIMIZE_CHUNKREADER + pBKGD->iRed = iRed; + pBKGD->iGreen = iGreen; + pBKGD->iBlue = iBlue; +#else + pBKGD->iRed = pData->iGlobalBKGDred; + pBKGD->iGreen = pData->iGlobalBKGDgreen; + pBKGD->iBlue = pData->iGlobalBKGDblue; +#endif + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_BKGD, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_OBJCLEANUP +mng_retcode mng_free_ani_bkgd (mng_datap pData, + mng_objectp pObject) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_BKGD, MNG_LC_START); +#endif + + MNG_FREEX (pData, pObject, sizeof (mng_ani_bkgd)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_BKGD, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_bkgd (mng_datap pData, + mng_objectp pObject) +{ + mng_ani_bkgdp pBKGD = (mng_ani_bkgdp)pObject; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_BKGD, MNG_LC_START); +#endif + + pData->bHasglobalBKGD = MNG_TRUE; + pData->iGlobalBKGDred = pBKGD->iRed; + pData->iGlobalBKGDgreen = pBKGD->iGreen; + pData->iGlobalBKGDblue = pBKGD->iBlue; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_BKGD, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_LOOP +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_ani_loop (mng_datap pData, + mng_uint8 iLevel, + mng_uint32 iRepeatcount, + mng_uint8 iTermcond, + mng_uint32 iItermin, + mng_uint32 iItermax, + mng_uint32 iCount, + mng_uint32p pSignals) +#else +mng_retcode mng_create_ani_loop (mng_datap pData, + mng_chunkp pChunk) +#endif +{ + mng_ani_loopp pLOOP; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_LOOP, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_ptr pTemp; + mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_loop), + mng_free_ani_loop, + mng_process_ani_loop, + &pTemp); + if (iRetcode) + return iRetcode; + pLOOP = (mng_ani_loopp)pTemp; +#else + MNG_ALLOC (pData, pLOOP, sizeof (mng_ani_loop)); + + pLOOP->sHeader.fCleanup = mng_free_ani_loop; + pLOOP->sHeader.fProcess = mng_process_ani_loop; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pLOOP); + +#ifndef MNG_OPTIMIZE_CHUNKREADER + pLOOP->iLevel = iLevel; + pLOOP->iRepeatcount = iRepeatcount; + pLOOP->iTermcond = iTermcond; + pLOOP->iItermin = iItermin; + pLOOP->iItermax = iItermax; + pLOOP->iCount = iCount; + +#ifndef MNG_NO_LOOP_SIGNALS_SUPPORTED + if (iCount) + { + MNG_ALLOC (pData, pLOOP->pSignals, (iCount << 1)); + MNG_COPY (pLOOP->pSignals, pSignals, (iCount << 1)); + } +#endif +#else /* MNG_OPTIMIZE_CHUNKREADER */ + pLOOP->iLevel = ((mng_loopp)pChunk)->iLevel; + pLOOP->iRepeatcount = ((mng_loopp)pChunk)->iRepeat; + pLOOP->iTermcond = ((mng_loopp)pChunk)->iTermination; + pLOOP->iItermin = ((mng_loopp)pChunk)->iItermin; + pLOOP->iItermax = ((mng_loopp)pChunk)->iItermax; + pLOOP->iCount = ((mng_loopp)pChunk)->iCount; + +#ifndef MNG_NO_LOOP_SIGNALS_SUPPORTED + if (pLOOP->iCount) + { + MNG_ALLOC (pData, pLOOP->pSignals, (pLOOP->iCount << 1)); + MNG_COPY (pLOOP->pSignals, ((mng_loopp)pChunk)->pSignals, (pLOOP->iCount << 1)); + } +#endif +#endif /* MNG_OPTIMIZE_CHUNKREADER */ + /* running counter starts with repeat_count */ + pLOOP->iRunningcount = pLOOP->iRepeatcount; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_LOOP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_free_ani_loop (mng_datap pData, + mng_objectp pObject) +{ +#ifndef MNG_NO_LOOP_SIGNALS_SUPPORTED + mng_ani_loopp pLOOP = (mng_ani_loopp)pObject; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_LOOP, MNG_LC_START); +#endif + +#ifndef MNG_NO_LOOP_SIGNALS_SUPPORTED + if (pLOOP->iCount) /* drop signal buffer ? */ + MNG_FREEX (pData, pLOOP->pSignals, (pLOOP->iCount << 1)); +#endif + +#ifndef MNG_OPTIMIZE_OBJCLEANUP + MNG_FREEX (pData, pObject, sizeof (mng_ani_loop)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_LOOP, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_OBJCLEANUP + return MNG_NOERROR; +#else + return mng_free_obj_general(pData, pObject); +#endif +} + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_loop (mng_datap pData, + mng_objectp pObject) +{ + mng_ani_loopp pLOOP = (mng_ani_loopp)pObject; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_LOOP, MNG_LC_START); +#endif + /* just reset the running counter */ + pLOOP->iRunningcount = pLOOP->iRepeatcount; + /* iteration=0 means we're skipping ! */ + if ((!pData->bSkipping) && (pLOOP->iRepeatcount == 0)) + pData->bSkipping = MNG_TRUE; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_LOOP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ +/* ************************************************************************** */ + +mng_retcode mng_create_ani_endl (mng_datap pData, + mng_uint8 iLevel) +{ + mng_ani_endlp pENDL; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_ENDL, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { + mng_retcode iRetcode; +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_ptr pTemp; + iRetcode = create_obj_general (pData, sizeof (mng_ani_endl), + mng_free_obj_general, + mng_process_ani_endl, + &pTemp); + if (iRetcode) + return iRetcode; + pENDL = (mng_ani_endlp)pTemp; +#else + MNG_ALLOC (pData, pENDL, sizeof (mng_ani_endl)); + + pENDL->sHeader.fCleanup = mng_free_ani_endl; + pENDL->sHeader.fProcess = mng_process_ani_endl; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pENDL); + + pENDL->iLevel = iLevel; + + iRetcode = mng_process_ani_endl (pData, (mng_objectp)pENDL); + if (iRetcode) + return iRetcode; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_ENDL, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_OBJCLEANUP +mng_retcode mng_free_ani_endl (mng_datap pData, + mng_objectp pObject) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_ENDL, MNG_LC_START); +#endif + + MNG_FREEX (pData, pObject, sizeof (mng_ani_endl)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_ENDL, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_endl (mng_datap pData, + mng_objectp pObject) +{ + mng_ani_endlp pENDL = (mng_ani_endlp)pObject; + mng_ani_loopp pLOOP; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_ENDL, MNG_LC_START); +#endif + + if (((pData->bDisplaying) && ((pData->bRunning) || (pData->bSearching))) || + (pData->bReading) ) + { + pLOOP = pENDL->pLOOP; /* determine matching LOOP */ + + if (!pLOOP) /* haven't got it yet ? */ + { /* go and look back in the list */ + pLOOP = (mng_ani_loopp)pENDL->sHeader.pPrev; + + while ((pLOOP) && + ((pLOOP->sHeader.fCleanup != mng_free_ani_loop) || + (pLOOP->iLevel != pENDL->iLevel) )) + pLOOP = pLOOP->sHeader.pPrev; + } + /* got it now ? */ + if ((pLOOP) && (pLOOP->iLevel == pENDL->iLevel)) + { + pENDL->pLOOP = pLOOP; /* save for next time ! */ + /* decrease running counter ? */ + if ((pLOOP->iRunningcount) && (pLOOP->iRunningcount < 0x7fffffffL)) + pLOOP->iRunningcount--; + + if ((!pData->bDisplaying) && (pData->bReading) && + (pLOOP->iRunningcount >= 0x7fffffffL)) + { + pData->iTotalframes = 0x7fffffffL; + pData->iTotallayers = 0x7fffffffL; + pData->iTotalplaytime = 0x7fffffffL; + } + else + { + /* TODO: we're cheating out on the termination_condition, + iteration_min, iteration_max and possible signals; + the code is just not ready for that can of worms.... */ + + if (!pLOOP->iRunningcount) /* reached zero ? */ + { /* was this the outer LOOP ? */ + if (pData->pFirstaniobj == (mng_objectp)pLOOP) /* TODO: THIS IS WRONG!! */ + pData->bHasLOOP = MNG_FALSE; + } + else + { + if (pData->pCurraniobj) /* was we processing objects ? */ + pData->pCurraniobj = pLOOP;/* then restart with LOOP */ + else /* else restart behind LOOP !!! */ + pData->pCurraniobj = pLOOP->sHeader.pNext; + } + } + /* does this match a 'skipping' LOOP? */ + if ((pData->bSkipping) && (pLOOP->iRepeatcount == 0)) + pData->bSkipping = MNG_FALSE; + } + else + MNG_ERROR (pData, MNG_NOMATCHINGLOOP); + + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_ENDL, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_DEFI +mng_retcode mng_create_ani_defi (mng_datap pData) +{ + mng_ani_defip pDEFI; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_DEFI, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_ptr pTemp; + mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_defi), + mng_free_obj_general, + mng_process_ani_defi, + &pTemp); + if (iRetcode) + return iRetcode; + pDEFI = (mng_ani_defip)pTemp; +#else + MNG_ALLOC (pData, pDEFI, sizeof (mng_ani_defi)); + + pDEFI->sHeader.fCleanup = mng_free_ani_defi; + pDEFI->sHeader.fProcess = mng_process_ani_defi; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pDEFI); + + pDEFI->iId = pData->iDEFIobjectid; + pDEFI->bHasdonotshow = pData->bDEFIhasdonotshow; + pDEFI->iDonotshow = pData->iDEFIdonotshow; + pDEFI->bHasconcrete = pData->bDEFIhasconcrete; + pDEFI->iConcrete = pData->iDEFIconcrete; + pDEFI->bHasloca = pData->bDEFIhasloca; + pDEFI->iLocax = pData->iDEFIlocax; + pDEFI->iLocay = pData->iDEFIlocay; + pDEFI->bHasclip = pData->bDEFIhasclip; + pDEFI->iClipl = pData->iDEFIclipl; + pDEFI->iClipr = pData->iDEFIclipr; + pDEFI->iClipt = pData->iDEFIclipt; + pDEFI->iClipb = pData->iDEFIclipb; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_DEFI, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_OBJCLEANUP +mng_retcode mng_free_ani_defi (mng_datap pData, + mng_objectp pObject) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_DEFI, MNG_LC_START); +#endif + + MNG_FREEX (pData, pObject, sizeof (mng_ani_defi)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_DEFI, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_defi (mng_datap pData, + mng_objectp pObject) +{ + mng_ani_defip pDEFI = (mng_ani_defip)pObject; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_DEFI, MNG_LC_START); +#endif + + pData->iDEFIobjectid = pDEFI->iId; + pData->bDEFIhasdonotshow = pDEFI->bHasdonotshow; + pData->iDEFIdonotshow = pDEFI->iDonotshow; + pData->bDEFIhasconcrete = pDEFI->bHasconcrete; + pData->iDEFIconcrete = pDEFI->iConcrete; + pData->bDEFIhasloca = pDEFI->bHasloca; + pData->iDEFIlocax = pDEFI->iLocax; + pData->iDEFIlocay = pDEFI->iLocay; + pData->bDEFIhasclip = pDEFI->bHasclip; + pData->iDEFIclipl = pDEFI->iClipl; + pData->iDEFIclipr = pDEFI->iClipr; + pData->iDEFIclipt = pDEFI->iClipt; + pData->iDEFIclipb = pDEFI->iClipb; + + iRetcode = mng_process_display_defi (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_DEFI, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_BASI +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_ani_basi (mng_datap pData, + mng_uint16 iRed, + mng_uint16 iGreen, + mng_uint16 iBlue, + mng_bool bHasalpha, + mng_uint16 iAlpha, + mng_uint8 iViewable) +#else +mng_retcode mng_create_ani_basi (mng_datap pData, + mng_chunkp pChunk) +#endif +{ + mng_ani_basip pBASI; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_BASI, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_ptr pTemp; + iRetcode = create_obj_general (pData, sizeof (mng_ani_basi), + mng_free_obj_general, + mng_process_ani_basi, + &pTemp); + if (iRetcode) + return iRetcode; + pBASI = (mng_ani_basip)pTemp; +#else + MNG_ALLOC (pData, pBASI, sizeof (mng_ani_basi)); + + pBASI->sHeader.fCleanup = mng_free_ani_basi; + pBASI->sHeader.fProcess = mng_process_ani_basi; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pBASI); + +#ifndef MNG_OPTIMIZE_CHUNKREADER + pBASI->iRed = iRed; + pBASI->iGreen = iGreen; + pBASI->iBlue = iBlue; + pBASI->bHasalpha = bHasalpha; + pBASI->iAlpha = iAlpha; + pBASI->iViewable = iViewable; +#else + pBASI->iRed = ((mng_basip)pChunk)->iRed; + pBASI->iGreen = ((mng_basip)pChunk)->iGreen; + pBASI->iBlue = ((mng_basip)pChunk)->iBlue; + pBASI->bHasalpha = ((mng_basip)pChunk)->bHasalpha; + pBASI->iAlpha = ((mng_basip)pChunk)->iAlpha; + pBASI->iViewable = ((mng_basip)pChunk)->iViewable; +#endif + } + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS +#ifndef MNG_OPTIMIZE_CHUNKREADER + iRetcode = mng_process_display_basi (pData, iRed, iGreen, iBlue, + bHasalpha, iAlpha, iViewable); +#else + iRetcode = mng_process_display_basi (pData, + ((mng_basip)pChunk)->iRed, + ((mng_basip)pChunk)->iGreen, + ((mng_basip)pChunk)->iBlue, + ((mng_basip)pChunk)->bHasalpha, + ((mng_basip)pChunk)->iAlpha, + ((mng_basip)pChunk)->iViewable); +#endif +#else +#ifndef MNG_OPTIMIZE_CHUNKREADER + pData->iBASIred = iRed; + pData->iBASIgreen = iGreen; + pData->iBASIblue = iBlue; + pData->bBASIhasalpha = bHasalpha; + pData->iBASIalpha = iAlpha; + pData->iBASIviewable = iViewable; +#else + pData->iBASIred = ((mng_basip)pChunk)->iRed; + pData->iBASIgreen = ((mng_basip)pChunk)->iGreen; + pData->iBASIblue = ((mng_basip)pChunk)->iBlue; + pData->bBASIhasalpha = ((mng_basip)pChunk)->bHasalpha; + pData->iBASIalpha = ((mng_basip)pChunk)->iAlpha; + pData->iBASIviewable = ((mng_basip)pChunk)->iViewable; +#endif + + iRetcode = mng_process_display_basi (pData); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_BASI, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_OBJCLEANUP +mng_retcode mng_free_ani_basi (mng_datap pData, + mng_objectp pObject) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_BASI, MNG_LC_START); +#endif + + MNG_FREEX (pData, pObject, sizeof (mng_ani_basi)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_BASI, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_basi (mng_datap pData, + mng_objectp pObject) +{ + mng_ani_basip pBASI = (mng_ani_basip)pObject; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_BASI, MNG_LC_START); +#endif + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + iRetcode = mng_process_display_basi (pData, pBASI->iRed, pBASI->iGreen, pBASI->iBlue, + pBASI->bHasalpha, pBASI->iAlpha, pBASI->iViewable); +#else + pData->iBASIred = pBASI->iRed; + pData->iBASIgreen = pBASI->iGreen; + pData->iBASIblue = pBASI->iBlue; + pData->bBASIhasalpha = pBASI->bHasalpha; + pData->iBASIalpha = pBASI->iAlpha; + pData->iBASIviewable = pBASI->iViewable; + + iRetcode = mng_process_display_basi (pData); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_BASI, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_CLON +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_ani_clon (mng_datap pData, + mng_uint16 iSourceid, + mng_uint16 iCloneid, + mng_uint8 iClonetype, + mng_bool bHasdonotshow, + mng_uint8 iDonotshow, + mng_uint8 iConcrete, + mng_bool bHasloca, + mng_uint8 iLocatype, + mng_int32 iLocax, + mng_int32 iLocay) +#else +mng_retcode mng_create_ani_clon (mng_datap pData, + mng_chunkp pChunk) +#endif +{ + mng_ani_clonp pCLON; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_CLON, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_ptr pTemp; + iRetcode = create_obj_general (pData, sizeof (mng_ani_clon), + mng_free_obj_general, + mng_process_ani_clon, + &pTemp); + if (iRetcode) + return iRetcode; + pCLON = (mng_ani_clonp)pTemp; +#else + MNG_ALLOC (pData, pCLON, sizeof (mng_ani_clon)); + + pCLON->sHeader.fCleanup = mng_free_ani_clon; + pCLON->sHeader.fProcess = mng_process_ani_clon; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pCLON); + +#ifndef MNG_OPTIMIZE_CHUNKREADER + pCLON->iSourceid = iSourceid; + pCLON->iCloneid = iCloneid; + pCLON->iClonetype = iClonetype; + pCLON->bHasdonotshow = bHasdonotshow; + pCLON->iDonotshow = iDonotshow; + pCLON->iConcrete = iConcrete; + pCLON->bHasloca = bHasloca; + pCLON->iLocatype = iLocatype; + pCLON->iLocax = iLocax; + pCLON->iLocay = iLocay; +#else + pCLON->iSourceid = ((mng_clonp)pChunk)->iSourceid; + pCLON->iCloneid = ((mng_clonp)pChunk)->iCloneid; + pCLON->iClonetype = ((mng_clonp)pChunk)->iClonetype; + pCLON->bHasdonotshow = ((mng_clonp)pChunk)->bHasdonotshow; + pCLON->iDonotshow = ((mng_clonp)pChunk)->iDonotshow; + pCLON->iConcrete = ((mng_clonp)pChunk)->iConcrete; + pCLON->bHasloca = ((mng_clonp)pChunk)->bHasloca; + pCLON->iLocatype = ((mng_clonp)pChunk)->iLocationtype; + pCLON->iLocax = ((mng_clonp)pChunk)->iLocationx; + pCLON->iLocay = ((mng_clonp)pChunk)->iLocationy; +#endif + } + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS +#ifndef MNG_OPTIMIZE_CHUNKREADER + iRetcode = mng_process_display_clon (pData, iSourceid, iCloneid, iClonetype, + bHasdonotshow, iDonotshow, iConcrete, + bHasloca, iLocatype, iLocax, iLocay); +#else + iRetcode = mng_process_display_clon (pData, + ((mng_clonp)pChunk)->iSourceid, + ((mng_clonp)pChunk)->iCloneid, + ((mng_clonp)pChunk)->iClonetype, + ((mng_clonp)pChunk)->bHasdonotshow, + ((mng_clonp)pChunk)->iDonotshow, + ((mng_clonp)pChunk)->iConcrete, + ((mng_clonp)pChunk)->bHasloca, + ((mng_clonp)pChunk)->iLocationtype, + ((mng_clonp)pChunk)->iLocationx, + ((mng_clonp)pChunk)->iLocationy); +#endif +#else +#ifndef MNG_OPTIMIZE_CHUNKREADER + pData->iCLONsourceid = iSourceid; + pData->iCLONcloneid = iCloneid; + pData->iCLONclonetype = iClonetype; + pData->bCLONhasdonotshow = bHasdonotshow; + pData->iCLONdonotshow = iDonotshow; + pData->iCLONconcrete = iConcrete; + pData->bCLONhasloca = bHasloca; + pData->iCLONlocationtype = iLocatype; + pData->iCLONlocationx = iLocax; + pData->iCLONlocationy = iLocay; +#else + pData->iCLONsourceid = ((mng_clonp)pChunk)->iSourceid; + pData->iCLONcloneid = ((mng_clonp)pChunk)->iCloneid; + pData->iCLONclonetype = ((mng_clonp)pChunk)->iClonetype; + pData->bCLONhasdonotshow = ((mng_clonp)pChunk)->bHasdonotshow; + pData->iCLONdonotshow = ((mng_clonp)pChunk)->iDonotshow; + pData->iCLONconcrete = ((mng_clonp)pChunk)->iConcrete; + pData->bCLONhasloca = ((mng_clonp)pChunk)->bHasloca; + pData->iCLONlocationtype = ((mng_clonp)pChunk)->iLocationtype; + pData->iCLONlocationx = ((mng_clonp)pChunk)->iLocationx; + pData->iCLONlocationy = ((mng_clonp)pChunk)->iLocationy; +#endif + + iRetcode = mng_process_display_clon (pData); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_CLON, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_OBJCLEANUP +mng_retcode mng_free_ani_clon (mng_datap pData, + mng_objectp pObject) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_CLON, MNG_LC_START); +#endif + + MNG_FREEX (pData, pObject, sizeof (mng_ani_clon)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_CLON, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_clon (mng_datap pData, + mng_objectp pObject) +{ + mng_ani_clonp pCLON = (mng_ani_clonp)pObject; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_CLON, MNG_LC_START); +#endif + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + iRetcode = mng_process_display_clon (pData, pCLON->iSourceid, pCLON->iCloneid, + pCLON->iClonetype, pCLON->bHasdonotshow, + pCLON->iDonotshow, pCLON->iConcrete, + pCLON->bHasloca, pCLON->iLocatype, + pCLON->iLocax, pCLON->iLocay); +#else + pData->iCLONcloneid = pCLON->iCloneid; + pData->iCLONsourceid = pCLON->iSourceid; + pData->iCLONclonetype = pCLON->iClonetype; + pData->bCLONhasdonotshow = pCLON->bHasdonotshow; + pData->iCLONdonotshow = pCLON->iDonotshow; + pData->iCLONconcrete = pCLON->iConcrete; + pData->bCLONhasloca = pCLON->bHasloca; + pData->iCLONlocationtype = pCLON->iLocatype; + pData->iCLONlocationx = pCLON->iLocax; + pData->iCLONlocationy = pCLON->iLocay; + + iRetcode = mng_process_display_clon (pData); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_CLON, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_BACK +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_ani_back (mng_datap pData, + mng_uint16 iRed, + mng_uint16 iGreen, + mng_uint16 iBlue, + mng_uint8 iMandatory, + mng_uint16 iImageid, + mng_uint8 iTile) +#else +mng_retcode mng_create_ani_back (mng_datap pData) +#endif +{ + mng_ani_backp pBACK; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_BACK, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_ptr pTemp; + mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_back), + mng_free_obj_general, + mng_process_ani_back, + &pTemp); + if (iRetcode) + return iRetcode; + pBACK = (mng_ani_backp)pTemp; +#else + MNG_ALLOC (pData, pBACK, sizeof (mng_ani_back)); + + pBACK->sHeader.fCleanup = mng_free_ani_back; + pBACK->sHeader.fProcess = mng_process_ani_back; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pBACK); + +#ifndef MNG_OPTIMIZE_CHUNKREADER + pBACK->iRed = iRed; + pBACK->iGreen = iGreen; + pBACK->iBlue = iBlue; + pBACK->iMandatory = iMandatory; + pBACK->iImageid = iImageid; + pBACK->iTile = iTile; +#else + pBACK->iRed = pData->iBACKred; + pBACK->iGreen = pData->iBACKgreen; + pBACK->iBlue = pData->iBACKblue; + pBACK->iMandatory = pData->iBACKmandatory; + pBACK->iImageid = pData->iBACKimageid; + pBACK->iTile = pData->iBACKtile; +#endif + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_BACK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_OBJCLEANUP +mng_retcode mng_free_ani_back (mng_datap pData, + mng_objectp pObject) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_BACK, MNG_LC_START); +#endif + + MNG_FREEX (pData, pObject, sizeof (mng_ani_back)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_BACK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_back (mng_datap pData, + mng_objectp pObject) +{ + mng_ani_backp pBACK = (mng_ani_backp)pObject; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_BACK, MNG_LC_START); +#endif + + pData->iBACKred = pBACK->iRed; + pData->iBACKgreen = pBACK->iGreen; + pData->iBACKblue = pBACK->iBlue; + pData->iBACKmandatory = pBACK->iMandatory; + pData->iBACKimageid = pBACK->iImageid; + pData->iBACKtile = pBACK->iTile; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_BACK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_FRAM +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_ani_fram (mng_datap pData, + mng_uint8 iFramemode, + mng_uint8 iChangedelay, + mng_uint32 iDelay, + mng_uint8 iChangetimeout, + mng_uint32 iTimeout, + mng_uint8 iChangeclipping, + mng_uint8 iCliptype, + mng_int32 iClipl, + mng_int32 iClipr, + mng_int32 iClipt, + mng_int32 iClipb) +#else +mng_retcode mng_create_ani_fram (mng_datap pData, + mng_chunkp pChunk) +#endif +{ + mng_ani_framp pFRAM; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_FRAM, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_ptr pTemp; + iRetcode = create_obj_general (pData, sizeof (mng_ani_fram), + mng_free_obj_general, + mng_process_ani_fram, + &pTemp); + if (iRetcode) + return iRetcode; + pFRAM = (mng_ani_framp)pTemp; +#else + MNG_ALLOC (pData, pFRAM, sizeof (mng_ani_fram)); + + pFRAM->sHeader.fCleanup = mng_free_ani_fram; + pFRAM->sHeader.fProcess = mng_process_ani_fram; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pFRAM); + +#ifndef MNG_OPTIMIZE_CHUNKREADER + pFRAM->iFramemode = iFramemode; + pFRAM->iChangedelay = iChangedelay; + pFRAM->iDelay = iDelay; + pFRAM->iChangetimeout = iChangetimeout; + pFRAM->iTimeout = iTimeout; + pFRAM->iChangeclipping = iChangeclipping; + pFRAM->iCliptype = iCliptype; + pFRAM->iClipl = iClipl; + pFRAM->iClipr = iClipr; + pFRAM->iClipt = iClipt; + pFRAM->iClipb = iClipb; +#else + pFRAM->iFramemode = ((mng_framp)pChunk)->iMode; + pFRAM->iChangedelay = ((mng_framp)pChunk)->iChangedelay; + pFRAM->iDelay = ((mng_framp)pChunk)->iDelay; + pFRAM->iChangetimeout = ((mng_framp)pChunk)->iChangetimeout; + pFRAM->iTimeout = ((mng_framp)pChunk)->iTimeout; + pFRAM->iChangeclipping = ((mng_framp)pChunk)->iChangeclipping; + pFRAM->iCliptype = ((mng_framp)pChunk)->iBoundarytype; + pFRAM->iClipl = ((mng_framp)pChunk)->iBoundaryl; + pFRAM->iClipr = ((mng_framp)pChunk)->iBoundaryr; + pFRAM->iClipt = ((mng_framp)pChunk)->iBoundaryt; + pFRAM->iClipb = ((mng_framp)pChunk)->iBoundaryb; +#endif + } + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS +#ifndef MNG_OPTIMIZE_CHUNKREADER + iRetcode = mng_process_display_fram (pData, iFramemode, + iChangedelay, iDelay, + iChangetimeout, iTimeout, + iChangeclipping, iCliptype, + iClipl, iClipr, + iClipt, iClipb); +#else + iRetcode = mng_process_display_fram (pData, + ((mng_framp)pChunk)->iMode, + ((mng_framp)pChunk)->iChangedelay, + ((mng_framp)pChunk)->iDelay, + ((mng_framp)pChunk)->iChangetimeout, + ((mng_framp)pChunk)->iTimeout, + ((mng_framp)pChunk)->iChangeclipping, + ((mng_framp)pChunk)->iBoundarytype, + ((mng_framp)pChunk)->iBoundaryl, + ((mng_framp)pChunk)->iBoundaryr, + ((mng_framp)pChunk)->iBoundaryt, + ((mng_framp)pChunk)->iBoundaryb); +#endif +#else +#ifndef MNG_OPTIMIZE_CHUNKREADER + pData->iTempFramemode = iFramemode; + pData->iTempChangedelay = iChangedelay; + pData->iTempDelay = iDelay; + pData->iTempChangetimeout = iChangetimeout; + pData->iTempTimeout = iTimeout; + pData->iTempChangeclipping = iChangeclipping; + pData->iTempCliptype = iCliptype; + pData->iTempClipl = iClipl; + pData->iTempClipr = iClipr; + pData->iTempClipt = iClipt; + pData->iTempClipb = iClipb; +#else + pData->iTempFramemode = ((mng_framp)pChunk)->iMode; + pData->iTempChangedelay = ((mng_framp)pChunk)->iChangedelay; + pData->iTempDelay = ((mng_framp)pChunk)->iDelay; + pData->iTempChangetimeout = ((mng_framp)pChunk)->iChangetimeout; + pData->iTempTimeout = ((mng_framp)pChunk)->iTimeout; + pData->iTempChangeclipping = ((mng_framp)pChunk)->iChangeclipping; + pData->iTempCliptype = ((mng_framp)pChunk)->iBoundarytype; + pData->iTempClipl = ((mng_framp)pChunk)->iBoundaryl; + pData->iTempClipr = ((mng_framp)pChunk)->iBoundaryr; + pData->iTempClipt = ((mng_framp)pChunk)->iBoundaryt; + pData->iTempClipb = ((mng_framp)pChunk)->iBoundaryb; +#endif + + iRetcode = mng_process_display_fram (pData); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_FRAM, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_OBJCLEANUP +mng_retcode mng_free_ani_fram (mng_datap pData, + mng_objectp pObject) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_FRAM, MNG_LC_START); +#endif + + MNG_FREEX (pData, pObject, sizeof (mng_ani_fram)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_FRAM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_fram (mng_datap pData, + mng_objectp pObject) +{ + mng_ani_framp pFRAM = (mng_ani_framp)pObject; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_FRAM, MNG_LC_START); +#endif + + if (pData->iBreakpoint) /* previously broken ? */ + { + iRetcode = mng_process_display_fram2 (pData); + pData->iBreakpoint = 0; /* not again */ + } + else + { +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + iRetcode = mng_process_display_fram (pData, pFRAM->iFramemode, + pFRAM->iChangedelay, pFRAM->iDelay, + pFRAM->iChangetimeout, pFRAM->iTimeout, + pFRAM->iChangeclipping, pFRAM->iCliptype, + pFRAM->iClipl, pFRAM->iClipr, + pFRAM->iClipt, pFRAM->iClipb); +#else + pData->iTempFramemode = pFRAM->iFramemode; + pData->iTempChangedelay = pFRAM->iChangedelay; + pData->iTempDelay = pFRAM->iDelay; + pData->iTempChangetimeout = pFRAM->iChangetimeout; + pData->iTempTimeout = pFRAM->iTimeout; + pData->iTempChangeclipping = pFRAM->iChangeclipping; + pData->iTempCliptype = pFRAM->iCliptype; + pData->iTempClipl = pFRAM->iClipl; + pData->iTempClipr = pFRAM->iClipr; + pData->iTempClipt = pFRAM->iClipt; + pData->iTempClipb = pFRAM->iClipb; + + iRetcode = mng_process_display_fram (pData); +#endif + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_FRAM, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_MOVE +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_ani_move (mng_datap pData, + mng_uint16 iFirstid, + mng_uint16 iLastid, + mng_uint8 iType, + mng_int32 iLocax, + mng_int32 iLocay) +#else +mng_retcode mng_create_ani_move (mng_datap pData, + mng_chunkp pChunk) +#endif +{ + mng_ani_movep pMOVE; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_MOVE, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_ptr pTemp; + iRetcode = create_obj_general (pData, sizeof (mng_ani_move), + mng_free_obj_general, + mng_process_ani_move, + &pTemp); + if (iRetcode) + return iRetcode; + pMOVE = (mng_ani_movep)pTemp; +#else + MNG_ALLOC (pData, pMOVE, sizeof (mng_ani_move)); + + pMOVE->sHeader.fCleanup = mng_free_ani_move; + pMOVE->sHeader.fProcess = mng_process_ani_move; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pMOVE); + +#ifndef MNG_OPTIMIZE_CHUNKREADER + pMOVE->iFirstid = iFirstid; + pMOVE->iLastid = iLastid; + pMOVE->iType = iType; + pMOVE->iLocax = iLocax; + pMOVE->iLocay = iLocay; +#else + pMOVE->iFirstid = ((mng_movep)pChunk)->iFirstid; + pMOVE->iLastid = ((mng_movep)pChunk)->iLastid; + pMOVE->iType = ((mng_movep)pChunk)->iMovetype; + pMOVE->iLocax = ((mng_movep)pChunk)->iMovex; + pMOVE->iLocay = ((mng_movep)pChunk)->iMovey; +#endif + } + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS +#ifndef MNG_OPTIMIZE_CHUNKREADER + iRetcode = mng_process_display_move (pData, iFirstid, iLastid, + iType, iLocax, iLocay); +#else + iRetcode = mng_process_display_move (pData, + ((mng_movep)pChunk)->iFirstid, + ((mng_movep)pChunk)->iLastid, + ((mng_movep)pChunk)->iMovetype, + ((mng_movep)pChunk)->iMovex, + ((mng_movep)pChunk)->iMovey); +#endif +#else +#ifndef MNG_OPTIMIZE_CHUNKREADER + pData->iMOVEfromid = iFirstid; + pData->iMOVEtoid = iLastid; + pData->iMOVEmovetype = iType; + pData->iMOVEmovex = iLocax; + pData->iMOVEmovey = iLocay; +#else + pData->iMOVEfromid = ((mng_movep)pChunk)->iFirstid; + pData->iMOVEtoid = ((mng_movep)pChunk)->iLastid; + pData->iMOVEmovetype = ((mng_movep)pChunk)->iMovetype; + pData->iMOVEmovex = ((mng_movep)pChunk)->iMovex; + pData->iMOVEmovey = ((mng_movep)pChunk)->iMovey; +#endif + + iRetcode = mng_process_display_move (pData); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_MOVE, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_OBJCLEANUP +mng_retcode mng_free_ani_move (mng_datap pData, + mng_objectp pObject) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_MOVE, MNG_LC_START); +#endif + + MNG_FREEX (pData, pObject, sizeof (mng_ani_move)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_MOVE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_move (mng_datap pData, + mng_objectp pObject) +{ + mng_retcode iRetcode; + mng_ani_movep pMOVE = (mng_ani_movep)pObject; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_MOVE, MNG_LC_START); +#endif + /* re-process the MOVE chunk */ +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + iRetcode = mng_process_display_move (pData, pMOVE->iFirstid, pMOVE->iLastid, + pMOVE->iType, pMOVE->iLocax, pMOVE->iLocay); +#else + pData->iMOVEfromid = pMOVE->iFirstid; + pData->iMOVEtoid = pMOVE->iLastid; + pData->iMOVEmovetype = pMOVE->iType; + pData->iMOVEmovex = pMOVE->iLocax; + pData->iMOVEmovey = pMOVE->iLocay; + + iRetcode = mng_process_display_move (pData); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_MOVE, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_CLIP +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_ani_clip (mng_datap pData, + mng_uint16 iFirstid, + mng_uint16 iLastid, + mng_uint8 iType, + mng_int32 iClipl, + mng_int32 iClipr, + mng_int32 iClipt, + mng_int32 iClipb) +#else +mng_retcode mng_create_ani_clip (mng_datap pData, + mng_chunkp pChunk) +#endif +{ + mng_ani_clipp pCLIP; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_CLIP, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_ptr pTemp; + iRetcode = create_obj_general (pData, sizeof (mng_ani_clip), + mng_free_obj_general, + mng_process_ani_clip, + &pTemp); + if (iRetcode) + return iRetcode; + pCLIP = (mng_ani_clipp)pTemp; +#else + MNG_ALLOC (pData, pCLIP, sizeof (mng_ani_clip)); + + pCLIP->sHeader.fCleanup = mng_free_ani_clip; + pCLIP->sHeader.fProcess = mng_process_ani_clip; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pCLIP); + +#ifndef MNG_OPTIMIZE_CHUNKREADER + pCLIP->iFirstid = iFirstid; + pCLIP->iLastid = iLastid; + pCLIP->iType = iType; + pCLIP->iClipl = iClipl; + pCLIP->iClipr = iClipr; + pCLIP->iClipt = iClipt; + pCLIP->iClipb = iClipb; +#else + pCLIP->iFirstid = ((mng_clipp)pChunk)->iFirstid; + pCLIP->iLastid = ((mng_clipp)pChunk)->iLastid; + pCLIP->iType = ((mng_clipp)pChunk)->iCliptype; + pCLIP->iClipl = ((mng_clipp)pChunk)->iClipl; + pCLIP->iClipr = ((mng_clipp)pChunk)->iClipr; + pCLIP->iClipt = ((mng_clipp)pChunk)->iClipt; + pCLIP->iClipb = ((mng_clipp)pChunk)->iClipb; +#endif + } + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS +#ifndef MNG_OPTIMIZE_CHUNKREADER + iRetcode = mng_process_display_clip (pData, iFirstid, iLastid, + iType, iClipl, iClipr, + iClipt, iClipb); +#else + iRetcode = mng_process_display_clip (pData, + ((mng_clipp)pChunk)->iFirstid, + ((mng_clipp)pChunk)->iLastid, + ((mng_clipp)pChunk)->iCliptype, + ((mng_clipp)pChunk)->iClipl, + ((mng_clipp)pChunk)->iClipr, + ((mng_clipp)pChunk)->iClipt, + ((mng_clipp)pChunk)->iClipb); +#endif +#else +#ifndef MNG_OPTIMIZE_CHUNKREADER + pData->iCLIPfromid = iFirstid; + pData->iCLIPtoid = iLastid; + pData->iCLIPcliptype = iType; + pData->iCLIPclipl = iClipl; + pData->iCLIPclipr = iClipr; + pData->iCLIPclipt = iClipt; + pData->iCLIPclipb = iClipb; +#else + pData->iCLIPfromid = ((mng_clipp)pChunk)->iFirstid; + pData->iCLIPtoid = ((mng_clipp)pChunk)->iLastid; + pData->iCLIPcliptype = ((mng_clipp)pChunk)->iCliptype; + pData->iCLIPclipl = ((mng_clipp)pChunk)->iClipl; + pData->iCLIPclipr = ((mng_clipp)pChunk)->iClipr; + pData->iCLIPclipt = ((mng_clipp)pChunk)->iClipt; + pData->iCLIPclipb = ((mng_clipp)pChunk)->iClipb; +#endif + + iRetcode = mng_process_display_clip (pData); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_CLIP, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_OBJCLEANUP +mng_retcode mng_free_ani_clip (mng_datap pData, + mng_objectp pObject) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_CLIP, MNG_LC_START); +#endif + + MNG_FREEX (pData, pObject, sizeof (mng_ani_clip)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_CLIP, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_clip (mng_datap pData, + mng_objectp pObject) +{ + mng_retcode iRetcode; + mng_ani_clipp pCLIP = (mng_ani_clipp)pObject; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_CLIP, MNG_LC_START); +#endif + /* re-process the CLIP chunk */ +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + iRetcode = mng_process_display_clip (pData, pCLIP->iFirstid, pCLIP->iLastid, + pCLIP->iType, pCLIP->iClipl, pCLIP->iClipr, + pCLIP->iClipt, pCLIP->iClipb); +#else + pData->iCLIPfromid = pCLIP->iFirstid; + pData->iCLIPtoid = pCLIP->iLastid; + pData->iCLIPcliptype = pCLIP->iType; + pData->iCLIPclipl = pCLIP->iClipl; + pData->iCLIPclipr = pCLIP->iClipr; + pData->iCLIPclipt = pCLIP->iClipt; + pData->iCLIPclipb = pCLIP->iClipb; + + iRetcode = mng_process_display_clip (pData); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_CLIP, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SHOW +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_ani_show (mng_datap pData, + mng_uint16 iFirstid, + mng_uint16 iLastid, + mng_uint8 iMode) +#else +mng_retcode mng_create_ani_show (mng_datap pData) +#endif +{ + mng_ani_showp pSHOW; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_SHOW, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_ptr pTemp; + mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_show), + mng_free_obj_general, + mng_process_ani_show, + &pTemp); + if (iRetcode) + return iRetcode; + pSHOW = (mng_ani_showp)pTemp; +#else + MNG_ALLOC (pData, pSHOW, sizeof (mng_ani_show)); + + pSHOW->sHeader.fCleanup = mng_free_ani_show; + pSHOW->sHeader.fProcess = mng_process_ani_show; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pSHOW); + +#ifndef MNG_OPTIMIZE_CHUNKREADER + pSHOW->iFirstid = iFirstid; + pSHOW->iLastid = iLastid; + pSHOW->iMode = iMode; +#else + pSHOW->iFirstid = pData->iSHOWfromid; + pSHOW->iLastid = pData->iSHOWtoid; + pSHOW->iMode = pData->iSHOWmode; +#endif + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_SHOW, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_OBJCLEANUP +mng_retcode mng_free_ani_show (mng_datap pData, + mng_objectp pObject) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_SHOW, MNG_LC_START); +#endif + + MNG_FREEX (pData, pObject, sizeof (mng_ani_show)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_SHOW, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_show (mng_datap pData, + mng_objectp pObject) +{ + mng_retcode iRetcode; + mng_ani_showp pSHOW = (mng_ani_showp)pObject; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_SHOW, MNG_LC_START); +#endif + + if (pData->iBreakpoint) /* returning from breakpoint ? */ + { + iRetcode = mng_process_display_show (pData); + } + else + { /* "re-run" SHOW chunk */ + pData->iSHOWmode = pSHOW->iMode; + pData->iSHOWfromid = pSHOW->iFirstid; + pData->iSHOWtoid = pSHOW->iLastid; + + iRetcode = mng_process_display_show (pData); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_SHOW, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_TERM +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_ani_term (mng_datap pData, + mng_uint8 iTermaction, + mng_uint8 iIteraction, + mng_uint32 iDelay, + mng_uint32 iItermax) +#else +mng_retcode mng_create_ani_term (mng_datap pData, + mng_chunkp pChunk) +#endif +{ + mng_ani_termp pTERM; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_TERM, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_ptr pTemp; + mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_term), + mng_free_obj_general, + mng_process_ani_term, + &pTemp); + if (iRetcode) + return iRetcode; + pTERM = (mng_ani_termp)pTemp; +#else + MNG_ALLOC (pData, pTERM, sizeof (mng_ani_term)); + + pTERM->sHeader.fCleanup = mng_free_ani_term; + pTERM->sHeader.fProcess = mng_process_ani_term; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pTERM); + +#ifndef MNG_OPTIMIZE_CHUNKREADER + pTERM->iTermaction = iTermaction; + pTERM->iIteraction = iIteraction; + pTERM->iDelay = iDelay; + pTERM->iItermax = iItermax; +#else + pTERM->iTermaction = ((mng_termp)pChunk)->iTermaction; + pTERM->iIteraction = ((mng_termp)pChunk)->iIteraction; + pTERM->iDelay = ((mng_termp)pChunk)->iDelay; + pTERM->iItermax = ((mng_termp)pChunk)->iItermax; +#endif + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_TERM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_OBJCLEANUP +mng_retcode mng_free_ani_term (mng_datap pData, + mng_objectp pObject) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_TERM, MNG_LC_START); +#endif + + MNG_FREEX (pData, pObject, sizeof (mng_ani_term)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_TERM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_term (mng_datap pData, + mng_objectp pObject) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_TERM, MNG_LC_START); +#endif + + /* dummy: no action required! */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_TERM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SAVE +mng_retcode mng_create_ani_save (mng_datap pData) +{ + mng_ptr pTemp; + mng_ani_savep pSAVE; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_SAVE, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_save), + mng_free_obj_general, + mng_process_ani_save, + &pTemp); + if (iRetcode) + return iRetcode; + pSAVE = (mng_ani_savep)pTemp; +#else + MNG_ALLOC (pData, pSAVE, sizeof (mng_ani_save)); + + pSAVE->sHeader.fCleanup = mng_free_ani_save; + pSAVE->sHeader.fProcess = mng_process_ani_save; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pSAVE); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_SAVE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_OBJCLEANUP +mng_retcode mng_free_ani_save (mng_datap pData, + mng_objectp pObject) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_SAVE, MNG_LC_START); +#endif + + MNG_FREEX (pData, pObject, sizeof (mng_ani_save)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_SAVE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_save (mng_datap pData, + mng_objectp pObject) +{ + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_SAVE, MNG_LC_START); +#endif + + iRetcode = mng_process_display_save (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_SAVE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_SEEK +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_ani_seek (mng_datap pData, + mng_uint32 iSegmentnamesize, + mng_pchar zSegmentname) +#else +mng_retcode mng_create_ani_seek (mng_datap pData, + mng_chunkp pChunk) +#endif +{ + mng_ptr pTemp; + mng_ani_seekp pSEEK; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_SEEK, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_seek), + mng_free_ani_seek, + mng_process_ani_seek, + &pTemp); + if (iRetcode) + return iRetcode; + pSEEK = (mng_ani_seekp)pTemp; +#else + MNG_ALLOC (pData, pSEEK, sizeof (mng_ani_seek)); + + pSEEK->sHeader.fCleanup = mng_free_ani_seek; + pSEEK->sHeader.fProcess = mng_process_ani_seek; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pSEEK); + + pData->pLastseek = (mng_objectp)pSEEK; + +#ifndef MNG_OPTIMIZE_CHUNKREADER + pSEEK->iSegmentnamesize = iSegmentnamesize; + if (iSegmentnamesize) + { + MNG_ALLOC (pData, pSEEK->zSegmentname, iSegmentnamesize + 1); + MNG_COPY (pSEEK->zSegmentname, zSegmentname, iSegmentnamesize); + } +#else + pSEEK->iSegmentnamesize = ((mng_seekp)pChunk)->iNamesize; + if (pSEEK->iSegmentnamesize) + { + MNG_ALLOC (pData, pSEEK->zSegmentname, pSEEK->iSegmentnamesize + 1); + MNG_COPY (pSEEK->zSegmentname, ((mng_seekp)pChunk)->zName, pSEEK->iSegmentnamesize); + } +#endif + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_SEEK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_free_ani_seek (mng_datap pData, + mng_objectp pObject) +{ + mng_ani_seekp pSEEK = (mng_ani_seekp)pObject; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_SEEK, MNG_LC_START); +#endif + + if (pSEEK->iSegmentnamesize) + MNG_FREEX (pData, pSEEK->zSegmentname, pSEEK->iSegmentnamesize + 1); + +#ifndef MNG_OPTIMIZE_OBJCLEANUP + MNG_FREEX (pData, pObject, sizeof (mng_ani_seek)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_SEEK, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_OBJCLEANUP + return MNG_NOERROR; +#else + return mng_free_obj_general(pData, pObject); +#endif +} + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_seek (mng_datap pData, + mng_objectp pObject) +{ + mng_ani_seekp pSEEK = (mng_ani_seekp)pObject; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_SEEK, MNG_LC_START); +#endif + +#ifdef MNG_SUPPORT_DYNAMICMNG + if (!pData->bStopafterseek) /* can we really process this one ? */ +#endif + { + pData->pLastseek = pObject; + + if (pData->fProcessseek) /* inform the app ? */ + { + mng_bool bOke; + mng_pchar zName; + + MNG_ALLOC (pData, zName, pSEEK->iSegmentnamesize + 1); + + if (pSEEK->iSegmentnamesize) + MNG_COPY (zName, pSEEK->zSegmentname, pSEEK->iSegmentnamesize); + + bOke = pData->fProcessseek ((mng_handle)pData, zName); + + MNG_FREEX (pData, zName, pSEEK->iSegmentnamesize + 1); + + if (!bOke) + MNG_ERROR (pData, MNG_APPMISCERROR); + } + } + + iRetcode = mng_process_display_seek (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_SEEK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_ani_dhdr (mng_datap pData, + mng_uint16 iObjectid, + mng_uint8 iImagetype, + mng_uint8 iDeltatype, + mng_uint32 iBlockwidth, + mng_uint32 iBlockheight, + mng_uint32 iBlockx, + mng_uint32 iBlocky) +#else +mng_retcode mng_create_ani_dhdr (mng_datap pData, + mng_chunkp pChunk) +#endif +{ + mng_ani_dhdrp pDHDR; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_DHDR, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_ptr pTemp; + iRetcode = create_obj_general (pData, sizeof (mng_ani_dhdr), + mng_free_obj_general, + mng_process_ani_dhdr, + &pTemp); + if (iRetcode) + return iRetcode; + pDHDR = (mng_ani_dhdrp)pTemp; +#else + MNG_ALLOC (pData, pDHDR, sizeof (mng_ani_dhdr)); + + pDHDR->sHeader.fCleanup = mng_free_ani_dhdr; + pDHDR->sHeader.fProcess = mng_process_ani_dhdr; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pDHDR); + +#ifndef MNG_OPTIMIZE_CHUNKREADER + pDHDR->iObjectid = iObjectid; + pDHDR->iImagetype = iImagetype; + pDHDR->iDeltatype = iDeltatype; + pDHDR->iBlockwidth = iBlockwidth; + pDHDR->iBlockheight = iBlockheight; + pDHDR->iBlockx = iBlockx; + pDHDR->iBlocky = iBlocky; +#else + pDHDR->iObjectid = ((mng_dhdrp)pChunk)->iObjectid; + pDHDR->iImagetype = ((mng_dhdrp)pChunk)->iImagetype; + pDHDR->iDeltatype = ((mng_dhdrp)pChunk)->iDeltatype; + pDHDR->iBlockwidth = ((mng_dhdrp)pChunk)->iBlockwidth; + pDHDR->iBlockheight = ((mng_dhdrp)pChunk)->iBlockheight; + pDHDR->iBlockx = ((mng_dhdrp)pChunk)->iBlockx; + pDHDR->iBlocky = ((mng_dhdrp)pChunk)->iBlocky; +#endif + } + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS +#ifndef MNG_OPTIMIZE_CHUNKREADER + iRetcode = mng_process_display_dhdr (pData, iObjectid, + iImagetype, iDeltatype, + iBlockwidth, iBlockheight, + iBlockx, iBlocky); +#else + iRetcode = mng_process_display_dhdr (pData, + ((mng_dhdrp)pChunk)->iObjectid, + ((mng_dhdrp)pChunk)->iImagetype, + ((mng_dhdrp)pChunk)->iDeltatype, + ((mng_dhdrp)pChunk)->iBlockwidth, + ((mng_dhdrp)pChunk)->iBlockheight, + ((mng_dhdrp)pChunk)->iBlockx, + ((mng_dhdrp)pChunk)->iBlocky); +#endif +#else +#ifndef MNG_OPTIMIZE_CHUNKREADER + pData->iDHDRobjectid = iObjectid; + pData->iDHDRimagetype = iImagetype; + pData->iDHDRdeltatype = iDeltatype; + pData->iDHDRblockwidth = iBlockwidth; + pData->iDHDRblockheight = iBlockheight; + pData->iDHDRblockx = iBlockx; + pData->iDHDRblocky = iBlocky; +#else + pData->iDHDRobjectid = ((mng_dhdrp)pChunk)->iObjectid; + pData->iDHDRimagetype = ((mng_dhdrp)pChunk)->iImagetype; + pData->iDHDRdeltatype = ((mng_dhdrp)pChunk)->iDeltatype; + pData->iDHDRblockwidth = ((mng_dhdrp)pChunk)->iBlockwidth; + pData->iDHDRblockheight = ((mng_dhdrp)pChunk)->iBlockheight; + pData->iDHDRblockx = ((mng_dhdrp)pChunk)->iBlockx; + pData->iDHDRblocky = ((mng_dhdrp)pChunk)->iBlocky; +#endif + + iRetcode = mng_process_display_dhdr (pData); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_DHDR, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_OBJCLEANUP +mng_retcode mng_free_ani_dhdr (mng_datap pData, + mng_objectp pObject) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_DHDR, MNG_LC_START); +#endif + + MNG_FREEX (pData, pObject, sizeof (mng_ani_dhdr)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_DHDR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_dhdr (mng_datap pData, + mng_objectp pObject) +{ + mng_ani_dhdrp pDHDR = (mng_ani_dhdrp)pObject; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_DHDR, MNG_LC_START); +#endif + + pData->bHasDHDR = MNG_TRUE; /* let everyone know we're inside a DHDR */ + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + iRetcode = mng_process_display_dhdr (pData, pDHDR->iObjectid, + pDHDR->iImagetype, pDHDR->iDeltatype, + pDHDR->iBlockwidth, pDHDR->iBlockheight, + pDHDR->iBlockx, pDHDR->iBlocky); +#else + pData->iDHDRobjectid = pDHDR->iObjectid; + pData->iDHDRimagetype = pDHDR->iImagetype; + pData->iDHDRdeltatype = pDHDR->iDeltatype; + pData->iDHDRblockwidth = pDHDR->iBlockwidth; + pData->iDHDRblockheight = pDHDR->iBlockheight; + pData->iDHDRblockx = pDHDR->iBlockx; + pData->iDHDRblocky = pDHDR->iBlocky; + + iRetcode = mng_process_display_dhdr (pData); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_DHDR, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_ani_prom (mng_datap pData, + mng_uint8 iBitdepth, + mng_uint8 iColortype, + mng_uint8 iFilltype) +#else +mng_retcode mng_create_ani_prom (mng_datap pData, + mng_chunkp pChunk) +#endif +{ + mng_ani_promp pPROM=NULL; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_PROM, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_ptr pTemp; + iRetcode = create_obj_general (pData, sizeof (mng_ani_prom), + mng_free_obj_general, + mng_process_ani_prom, + &pTemp); + if (iRetcode) + return iRetcode; + pPROM = (mng_ani_promp)pTemp; +#else + MNG_ALLOC (pData, pPROM, sizeof (mng_ani_prom)); + + pPROM->sHeader.fCleanup = mng_free_ani_prom; + pPROM->sHeader.fProcess = mng_process_ani_prom; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pPROM); + +#ifndef MNG_OPTIMIZE_CHUNKREADER + pPROM->iBitdepth = iBitdepth; + pPROM->iColortype = iColortype; + pPROM->iFilltype = iFilltype; +#else + pPROM->iBitdepth = ((mng_promp)pChunk)->iSampledepth; + pPROM->iColortype = ((mng_promp)pChunk)->iColortype; + pPROM->iFilltype = ((mng_promp)pChunk)->iFilltype; +#endif + } + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS +#ifndef MNG_OPTIMIZE_CHUNKREADER + iRetcode = mng_process_display_prom (pData, iBitdepth, + iColortype, iFilltype); +#else + iRetcode = mng_process_display_prom (pData, + ((mng_promp)pChunk)->iSampledepth, + ((mng_promp)pChunk)->iColortype, + ((mng_promp)pChunk)->iFilltype); +#endif +#else +#ifndef MNG_OPTIMIZE_CHUNKREADER + pData->iPROMbitdepth = iBitdepth; + pData->iPROMcolortype = iColortype; + pData->iPROMfilltype = iFilltype; +#else + pData->iPROMbitdepth = ((mng_promp)pChunk)->iSampledepth; + pData->iPROMcolortype = ((mng_promp)pChunk)->iColortype; + pData->iPROMfilltype = ((mng_promp)pChunk)->iFilltype; +#endif + + iRetcode = mng_process_display_prom (pData); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_PROM, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_OBJCLEANUP +mng_retcode mng_free_ani_prom (mng_datap pData, + mng_objectp pObject) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_PROM, MNG_LC_START); +#endif + + MNG_FREEX (pData, pObject, sizeof (mng_ani_prom)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_PROM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_prom (mng_datap pData, + mng_objectp pObject) +{ + mng_ani_promp pPROM = (mng_ani_promp)pObject; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_PROM, MNG_LC_START); +#endif + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + iRetcode = mng_process_display_prom (pData, pPROM->iBitdepth, + pPROM->iColortype, pPROM->iFilltype); +#else + pData->iPROMbitdepth = pPROM->iBitdepth; + pData->iPROMcolortype = pPROM->iColortype; + pData->iPROMfilltype = pPROM->iFilltype; + + iRetcode = mng_process_display_prom (pData); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_PROM, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +mng_retcode mng_create_ani_ipng (mng_datap pData) +{ + mng_ani_ipngp pIPNG; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_IPNG, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_ptr pTemp; + mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_ipng), + mng_free_obj_general, + mng_process_ani_ipng, + &pTemp); + if (iRetcode) + return iRetcode; + pIPNG = (mng_ani_ipngp)pTemp; +#else + MNG_ALLOC (pData, pIPNG, sizeof (mng_ani_ipng)); + + pIPNG->sHeader.fCleanup = mng_free_ani_ipng; + pIPNG->sHeader.fProcess = mng_process_ani_ipng; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pIPNG); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_IPNG, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_OBJCLEANUP +mng_retcode mng_free_ani_ipng (mng_datap pData, + mng_objectp pObject) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_IPNG, MNG_LC_START); +#endif + + MNG_FREEX (pData, pObject, sizeof (mng_ani_ipng)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_IPNG, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_ipng (mng_datap pData, + mng_objectp pObject) +{ + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_IPNG, MNG_LC_START); +#endif + + iRetcode = mng_process_display_ipng (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_IPNG, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifdef MNG_INCLUDE_JNG +mng_retcode mng_create_ani_ijng (mng_datap pData) +{ + mng_ani_ijngp pIJNG; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_IJNG, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_ptr pTemp; + mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_ani_ijng), + mng_free_obj_general, + mng_process_ani_ijng, + &pTemp); + if (iRetcode) + return iRetcode; + pIJNG = (mng_ani_ijngp)pTemp; +#else + MNG_ALLOC (pData, pIJNG, sizeof (mng_ani_ijng)); + + pIJNG->sHeader.fCleanup = mng_free_ani_ijng; + pIJNG->sHeader.fProcess = mng_process_ani_ijng; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pIJNG); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_IJNG, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_OBJCLEANUP +mng_retcode mng_free_ani_ijng (mng_datap pData, + mng_objectp pObject) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_IJNG, MNG_LC_START); +#endif + + MNG_FREEX (pData, pObject, sizeof (mng_ani_ijng)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_IJNG, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_ijng (mng_datap pData, + mng_objectp pObject) +{ + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_IJNG, MNG_LC_START); +#endif + + iRetcode = mng_process_display_ijng (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_IJNG, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +mng_retcode mng_create_ani_pplt (mng_datap pData, + mng_uint8 iType, + mng_uint32 iCount, + mng_palette8ep paIndexentries, + mng_uint8p paAlphaentries, + mng_uint8p paUsedentries) +{ + mng_ani_ppltp pPPLT; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_PPLT, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_ptr pTemp; + iRetcode = create_obj_general (pData, sizeof (mng_ani_pplt), + mng_free_obj_general, + mng_process_ani_pplt, + &pTemp); + if (iRetcode) + return iRetcode; + pPPLT = (mng_ani_ppltp)pTemp; +#else + MNG_ALLOC (pData, pPPLT, sizeof (mng_ani_pplt)); + + pPPLT->sHeader.fCleanup = mng_free_ani_pplt; + pPPLT->sHeader.fProcess = mng_process_ani_pplt; +#endif + + pPPLT->iType = iType; + pPPLT->iCount = iCount; + + MNG_COPY (pPPLT->aIndexentries, paIndexentries, sizeof (pPPLT->aIndexentries)); + MNG_COPY (pPPLT->aAlphaentries, paAlphaentries, sizeof (pPPLT->aAlphaentries)); + MNG_COPY (pPPLT->aUsedentries, paUsedentries, sizeof (pPPLT->aUsedentries )); + + mng_add_ani_object (pData, (mng_object_headerp)pPPLT); + } + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + iRetcode = mng_process_display_pplt (pData, iType, iCount, + paIndexentries, paAlphaentries, paUsedentries); +#else + pData->iPPLTtype = iType; + pData->iPPLTcount = iCount; + pData->paPPLTindexentries = paIndexentries; + pData->paPPLTalphaentries = paAlphaentries; + pData->paPPLTusedentries = paUsedentries; + + iRetcode = mng_process_display_pplt (pData); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_PPLT, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_OBJCLEANUP +mng_retcode mng_free_ani_pplt (mng_datap pData, + mng_objectp pObject) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_PPLT, MNG_LC_START); +#endif + + MNG_FREEX (pData, pObject, sizeof (mng_ani_pplt)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_PPLT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_pplt (mng_datap pData, + mng_objectp pObject) +{ + mng_ani_ppltp pPPLT = (mng_ani_ppltp)pObject; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_PPLT, MNG_LC_START); +#endif + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + iRetcode = mng_process_display_pplt (pData, pPPLT->iType, pPPLT->iCount, + pPPLT->aIndexentries, pPPLT->aAlphaentries, + pPPLT->aUsedentries); +#else + pData->iPPLTtype = pPPLT->iType; + pData->iPPLTcount = pPPLT->iCount; + pData->paPPLTindexentries = &pPPLT->aIndexentries; + pData->paPPLTalphaentries = &pPPLT->aAlphaentries; + pData->paPPLTusedentries = &pPPLT->aUsedentries; + + iRetcode = mng_process_display_pplt (pData); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_PPLT, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_MAGN +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_ani_magn (mng_datap pData, + mng_uint16 iFirstid, + mng_uint16 iLastid, + mng_uint8 iMethodX, + mng_uint16 iMX, + mng_uint16 iMY, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint16 iMT, + mng_uint16 iMB, + mng_uint8 iMethodY) +#else +mng_retcode mng_create_ani_magn (mng_datap pData, + mng_chunkp pChunk) +#endif +{ + mng_ani_magnp pMAGN=NULL; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_MAGN, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_ptr pTemp; + iRetcode = create_obj_general (pData, sizeof (mng_ani_magn), + mng_free_obj_general, + mng_process_ani_magn, + &pTemp); + if (iRetcode) + return iRetcode; + pMAGN = (mng_ani_magnp)pTemp; +#else + MNG_ALLOC (pData, pMAGN, sizeof (mng_ani_magn)); + + pMAGN->sHeader.fCleanup = mng_free_ani_magn; + pMAGN->sHeader.fProcess = mng_process_ani_magn; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pMAGN); + +#ifndef MNG_OPTIMIZE_CHUNKREADER + pMAGN->iFirstid = iFirstid; + pMAGN->iLastid = iLastid; + pMAGN->iMethodX = iMethodX; + pMAGN->iMX = iMX; + pMAGN->iMY = iMY; + pMAGN->iML = iML; + pMAGN->iMR = iMR; + pMAGN->iMT = iMT; + pMAGN->iMB = iMB; + pMAGN->iMethodY = iMethodY; +#else + pMAGN->iFirstid = ((mng_magnp)pChunk)->iFirstid; + pMAGN->iLastid = ((mng_magnp)pChunk)->iLastid; + pMAGN->iMethodX = ((mng_magnp)pChunk)->iMethodX; + pMAGN->iMX = ((mng_magnp)pChunk)->iMX; + pMAGN->iMY = ((mng_magnp)pChunk)->iMY; + pMAGN->iML = ((mng_magnp)pChunk)->iML; + pMAGN->iMR = ((mng_magnp)pChunk)->iMR; + pMAGN->iMT = ((mng_magnp)pChunk)->iMT; + pMAGN->iMB = ((mng_magnp)pChunk)->iMB; + pMAGN->iMethodY = ((mng_magnp)pChunk)->iMethodY; +#endif + } + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS +#ifndef MNG_OPTIMIZE_CHUNKREADER + iRetcode = mng_process_display_magn (pData, pMAGN->iFirstid, pMAGN->iLastid, + pMAGN->iMethodX, pMAGN->iMX, pMAGN->iMY, + pMAGN->iML, pMAGN->iMR, pMAGN->iMT, + pMAGN->iMB, pMAGN->iMethodY); +#else + iRetcode = mng_process_display_magn (pData, + ((mng_magnp)pChunk)->iFirstid, + ((mng_magnp)pChunk)->iLastid, + ((mng_magnp)pChunk)->iMethodX, + ((mng_magnp)pChunk)->iMX, + ((mng_magnp)pChunk)->iMY, + ((mng_magnp)pChunk)->iML, + ((mng_magnp)pChunk)->iMR, + ((mng_magnp)pChunk)->iMT, + ((mng_magnp)pChunk)->iMB, + ((mng_magnp)pChunk)->iMethodY); +#endif +#else +#ifndef MNG_OPTIMIZE_CHUNKREADER + pData->iMAGNfirstid = iFirstid; + pData->iMAGNlastid = iLastid; + pData->iMAGNmethodX = iMethodX; + pData->iMAGNmX = iMX; + pData->iMAGNmY = iMY; + pData->iMAGNmL = iML; + pData->iMAGNmR = iMR; + pData->iMAGNmT = iMT; + pData->iMAGNmB = iMB; + pData->iMAGNmethodY = iMethodY; +#else + pData->iMAGNfirstid = ((mng_magnp)pChunk)->iFirstid; + pData->iMAGNlastid = ((mng_magnp)pChunk)->iLastid; + pData->iMAGNmethodX = ((mng_magnp)pChunk)->iMethodX; + pData->iMAGNmX = ((mng_magnp)pChunk)->iMX; + pData->iMAGNmY = ((mng_magnp)pChunk)->iMY; + pData->iMAGNmL = ((mng_magnp)pChunk)->iML; + pData->iMAGNmR = ((mng_magnp)pChunk)->iMR; + pData->iMAGNmT = ((mng_magnp)pChunk)->iMT; + pData->iMAGNmB = ((mng_magnp)pChunk)->iMB; + pData->iMAGNmethodY = ((mng_magnp)pChunk)->iMethodY; +#endif + + iRetcode = mng_process_display_magn (pData); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_MAGN, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_OBJCLEANUP +mng_retcode mng_free_ani_magn (mng_datap pData, + mng_objectp pObject) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_MAGN, MNG_LC_START); +#endif + + MNG_FREEX (pData, pObject, sizeof (mng_ani_magn)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_MAGN, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_magn (mng_datap pData, + mng_objectp pObject) +{ + mng_ani_magnp pMAGN = (mng_ani_magnp)pObject; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_MAGN, MNG_LC_START); +#endif + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + iRetcode = mng_process_display_magn (pData, pMAGN->iFirstid, pMAGN->iLastid, + pMAGN->iMethodX, pMAGN->iMX, pMAGN->iMY, + pMAGN->iML, pMAGN->iMR, pMAGN->iMT, + pMAGN->iMB, pMAGN->iMethodY); +#else + pData->iMAGNfirstid = pMAGN->iFirstid; + pData->iMAGNlastid = pMAGN->iLastid; + pData->iMAGNmethodX = pMAGN->iMethodX; + pData->iMAGNmX = pMAGN->iMX; + pData->iMAGNmY = pMAGN->iMY; + pData->iMAGNmL = pMAGN->iML; + pData->iMAGNmR = pMAGN->iMR; + pData->iMAGNmT = pMAGN->iMT; + pData->iMAGNmB = pMAGN->iMB; + pData->iMAGNmethodY = pMAGN->iMethodY; + + iRetcode = mng_process_display_magn (pData); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_MAGN, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_PAST +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_ani_past (mng_datap pData, + mng_uint16 iTargetid, + mng_uint8 iTargettype, + mng_int32 iTargetx, + mng_int32 iTargety, + mng_uint32 iCount, + mng_ptr pSources) +#else +mng_retcode mng_create_ani_past (mng_datap pData, + mng_chunkp pChunk) +#endif +{ + mng_ani_pastp pPAST; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_PAST, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_ptr pTemp; + iRetcode = create_obj_general (pData, sizeof (mng_ani_past), + mng_free_ani_past, + mng_process_ani_past, + &pTemp); + if (iRetcode) + return iRetcode; + pPAST = (mng_ani_pastp)pTemp; +#else + MNG_ALLOC (pData, pPAST, sizeof (mng_ani_past)); + + pPAST->sHeader.fCleanup = mng_free_ani_past; + pPAST->sHeader.fProcess = mng_process_ani_past; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pPAST); + +#ifndef MNG_OPTIMIZE_CHUNKREADER + pPAST->iTargetid = iTargetid; + pPAST->iTargettype = iTargettype; + pPAST->iTargetx = iTargetx; + pPAST->iTargety = iTargety; + pPAST->iCount = iCount; + + if (iCount) + { + MNG_ALLOC (pData, pPAST->pSources, (iCount * sizeof (mng_past_source))); + MNG_COPY (pPAST->pSources, pSources, (iCount * sizeof (mng_past_source))); + } +#else + pPAST->iTargetid = ((mng_pastp)pChunk)->iDestid; + pPAST->iTargettype = ((mng_pastp)pChunk)->iTargettype; + pPAST->iTargetx = ((mng_pastp)pChunk)->iTargetx; + pPAST->iTargety = ((mng_pastp)pChunk)->iTargety; + pPAST->iCount = ((mng_pastp)pChunk)->iCount; + + if (pPAST->iCount) + { + mng_size_t iSize = (mng_size_t)(pPAST->iCount * sizeof (mng_past_source)); + MNG_ALLOC (pData, pPAST->pSources, iSize); + MNG_COPY (pPAST->pSources, ((mng_pastp)pChunk)->pSources, iSize); + } +#endif + } + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS +#ifndef MNG_OPTIMIZE_CHUNKREADER + iRetcode = mng_process_display_past (pData, iTargetid, iTargettype, + iTargetx, iTargety, + iCount, pSources); +#else + iRetcode = mng_process_display_past (pData, + ((mng_pastp)pChunk)->iDestid, + ((mng_pastp)pChunk)->iTargettype, + ((mng_pastp)pChunk)->iTargetx, + ((mng_pastp)pChunk)->iTargety, + ((mng_pastp)pChunk)->iCount, + ((mng_pastp)pChunk)->pSources); +#endif +#else +#ifndef MNG_OPTIMIZE_CHUNKREADER + pData->iPASTtargetid = iTargetid; + pData->iPASTtargettype = iTargettype; + pData->iPASTtargetx = iTargetx; + pData->iPASTtargety = iTargety; + pData->iPASTcount = iCount; + pData->pPASTsources = pSources; +#else + pData->iPASTtargetid = ((mng_pastp)pChunk)->iDestid; + pData->iPASTtargettype = ((mng_pastp)pChunk)->iTargettype; + pData->iPASTtargetx = ((mng_pastp)pChunk)->iTargetx; + pData->iPASTtargety = ((mng_pastp)pChunk)->iTargety; + pData->iPASTcount = ((mng_pastp)pChunk)->iCount; + pData->pPASTsources = ((mng_pastp)pChunk)->pSources; +#endif + + iRetcode = mng_process_display_past (pData); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_PAST, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_PAST +mng_retcode mng_free_ani_past (mng_datap pData, + mng_objectp pObject) +{ + mng_ani_pastp pPAST = (mng_ani_pastp)pObject; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_PAST, MNG_LC_START); +#endif + + if (pPAST->iCount) + MNG_FREEX (pData, pPAST->pSources, (pPAST->iCount * sizeof (mng_past_source))); + +#ifndef MNG_OPTIMIZE_OBJCLEANUP + MNG_FREEX (pData, pObject, sizeof (mng_ani_past)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_PAST, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_OBJCLEANUP + return MNG_NOERROR; +#else + return mng_free_obj_general(pData, pObject); +#endif +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_PAST +mng_retcode mng_process_ani_past (mng_datap pData, + mng_objectp pObject) +{ + mng_ani_pastp pPAST = (mng_ani_pastp)pObject; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_PAST, MNG_LC_START); +#endif + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + iRetcode = mng_process_display_past (pData, pPAST->iTargetid, pPAST->iTargettype, + pPAST->iTargetx, pPAST->iTargety, + pPAST->iCount, pPAST->pSources); +#else + pData->iPASTtargetid = pPAST->iTargetid; + pData->iPASTtargettype = pPAST->iTargettype; + pData->iPASTtargetx = pPAST->iTargetx; + pData->iPASTtargety = pPAST->iTargety; + pData->iPASTcount = pPAST->iCount; + pData->pPASTsources = pPAST->pSources; + + iRetcode = mng_process_display_past (pData); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_PAST, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_DISC +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_ani_disc (mng_datap pData, + mng_uint32 iCount, + mng_uint16p pIds) +#else +mng_retcode mng_create_ani_disc (mng_datap pData, + mng_chunkp pChunk) +#endif +{ + mng_ani_discp pDISC; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_DISC, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_ptr pTemp; + iRetcode = create_obj_general (pData, sizeof (mng_ani_disc), + mng_free_ani_disc, + mng_process_ani_disc, + &pTemp); + if (iRetcode) + return iRetcode; + pDISC = (mng_ani_discp)pTemp; +#else + MNG_ALLOC (pData, pDISC, sizeof (mng_ani_disc)); + + pDISC->sHeader.fCleanup = mng_free_ani_disc; + pDISC->sHeader.fProcess = mng_process_ani_disc; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pDISC); + +#ifndef MNG_OPTIMIZE_CHUNKREADER + pDISC->iCount = iCount; + + if (iCount) + { + MNG_ALLOC (pData, pDISC->pIds, (iCount << 1)); + MNG_COPY (pDISC->pIds, pIds, (iCount << 1)); + } +#else + pDISC->iCount = ((mng_discp)pChunk)->iCount; + + if (pDISC->iCount) + { + mng_size_t iSize = (mng_size_t)(pDISC->iCount << 1); + MNG_ALLOC (pData, pDISC->pIds, iSize); + MNG_COPY (pDISC->pIds, ((mng_discp)pChunk)->pObjectids, iSize); + } +#endif + } + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS +#ifndef MNG_OPTIMIZE_CHUNKREADER + iRetcode = mng_process_display_disc (pData, iCount, pIds); +#else + iRetcode = mng_process_display_disc (pData, + ((mng_discp)pChunk)->iCount, + ((mng_discp)pChunk)->pObjectids); +#endif +#else +#ifndef MNG_OPTIMIZE_CHUNKREADER + pData->iDISCcount = iCount; + pData->pDISCids = pIds; +#else + pData->iDISCcount = ((mng_discp)pChunk)->iCount; + pData->pDISCids = ((mng_discp)pChunk)->pObjectids; +#endif + + iRetcode = mng_process_display_disc (pData); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANI_DISC, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_free_ani_disc (mng_datap pData, + mng_objectp pObject) +{ + mng_ani_discp pDISC = (mng_ani_discp)pObject; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_DISC, MNG_LC_START); +#endif + + if (pDISC->iCount) + MNG_FREEX (pData, pDISC->pIds, (pDISC->iCount << 1)); + +#ifndef MNG_OPTIMIZE_OBJCLEANUP + MNG_FREEX (pData, pObject, sizeof (mng_ani_disc)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANI_DISC, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_OBJCLEANUP + return MNG_NOERROR; +#else + return mng_free_obj_general(pData, pObject); +#endif +} + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_disc (mng_datap pData, + mng_objectp pObject) +{ + mng_ani_discp pDISC = (mng_ani_discp)pObject; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_DISC, MNG_LC_START); +#endif + +#ifndef MNG_OPTIMIZE_DISPLAYCALLS + iRetcode = mng_process_display_disc (pData, pDISC->iCount, pDISC->pIds); +#else + pData->iDISCcount = pDISC->iCount; + pData->pDISCids = pDISC->pIds; + + iRetcode = mng_process_display_disc (pData); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANI_DISC, MNG_LC_END); +#endif + + return iRetcode; +} +#endif + +/* ************************************************************************** */ +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DYNAMICMNG + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_event (mng_datap pData, + mng_uint8 iEventtype, + mng_uint8 iMasktype, + mng_int32 iLeft, + mng_int32 iRight, + mng_int32 iTop, + mng_int32 iBottom, + mng_uint16 iObjectid, + mng_uint8 iIndex, + mng_uint32 iSegmentnamesize, + mng_pchar zSegmentname) +#else +mng_retcode mng_create_event (mng_datap pData, + mng_ptr pEntry) +#endif +{ + mng_eventp pEvent; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_EVENT, MNG_LC_START); +#endif + + if (pData->bCacheplayback) /* caching playback info ? */ + { + mng_object_headerp pLast; + +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_ptr pTemp; + mng_retcode iRetcode = create_obj_general (pData, sizeof (mng_event), + mng_free_event, + mng_process_event, + &pTemp); + if (iRetcode) + return iRetcode; + pEvent = (mng_eventp)pTemp; +#else + MNG_ALLOC (pData, pEvent, sizeof (mng_event)); + + pEvent->sHeader.fCleanup = mng_free_event; + pEvent->sHeader.fProcess = mng_process_event; +#endif + +#ifndef MNG_OPTIMIZE_CHUNKREADER + pEvent->iEventtype = iEventtype; + pEvent->iMasktype = iMasktype; + pEvent->iLeft = iLeft; + pEvent->iRight = iRight; + pEvent->iTop = iTop; + pEvent->iBottom = iBottom; + pEvent->iObjectid = iObjectid; + pEvent->iIndex = iIndex; + pEvent->iSegmentnamesize = iSegmentnamesize; + + if (iSegmentnamesize) + { + MNG_ALLOC (pData, pEvent->zSegmentname, iSegmentnamesize+1); + MNG_COPY (pEvent->zSegmentname, zSegmentname, iSegmentnamesize); + } +#else + pEvent->iEventtype = ((mng_evnt_entryp)pEntry)->iEventtype; + pEvent->iMasktype = ((mng_evnt_entryp)pEntry)->iMasktype; + pEvent->iLeft = ((mng_evnt_entryp)pEntry)->iLeft; + pEvent->iRight = ((mng_evnt_entryp)pEntry)->iRight; + pEvent->iTop = ((mng_evnt_entryp)pEntry)->iTop; + pEvent->iBottom = ((mng_evnt_entryp)pEntry)->iBottom; + pEvent->iObjectid = ((mng_evnt_entryp)pEntry)->iObjectid; + pEvent->iIndex = ((mng_evnt_entryp)pEntry)->iIndex; + pEvent->iSegmentnamesize = ((mng_evnt_entryp)pEntry)->iSegmentnamesize; + + if (pEvent->iSegmentnamesize) + { + MNG_ALLOC (pData, pEvent->zSegmentname, pEvent->iSegmentnamesize+1); + MNG_COPY (pEvent->zSegmentname, ((mng_evnt_entryp)pEntry)->zSegmentname, pEvent->iSegmentnamesize); + } +#endif + /* fixup the double-linked list */ + pLast = (mng_object_headerp)pData->pLastevent; + + if (pLast) /* link it as last in the chain */ + { + pEvent->sHeader.pPrev = pLast; + pLast->pNext = pEvent; + } + else + { + pData->pFirstevent = pEvent; + } + + pData->pLastevent = pEvent; + pData->bDynamic = MNG_TRUE; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_EVENT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_free_event (mng_datap pData, + mng_objectp pObject) +{ + mng_eventp pEvent = (mng_eventp)pObject; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_EVENT, MNG_LC_START); +#endif + + if (pEvent->iSegmentnamesize) + MNG_FREEX (pData, pEvent->zSegmentname, pEvent->iSegmentnamesize + 1); + +#ifndef MNG_OPTIMIZE_OBJCLEANUP + MNG_FREEX (pData, pEvent, sizeof (mng_event)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_EVENT, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_OBJCLEANUP + return MNG_NOERROR; +#else + return mng_free_obj_general(pData, pObject); +#endif +} + +/* ************************************************************************** */ + +mng_retcode mng_process_event (mng_datap pData, + mng_objectp pObject) +{ +#ifndef MNG_SKIPCHUNK_SEEK + mng_eventp pEvent = (mng_eventp)pObject; + mng_object_headerp pAni; + mng_bool bFound = MNG_FALSE; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_EVENT, MNG_LC_START); +#endif + +#ifndef MNG_SKIPCHUNK_SEEK + if (!pEvent->pSEEK) /* need to find SEEK first ? */ + { + pAni = (mng_object_headerp)pData->pFirstaniobj; + + while ((pAni) && (!bFound)) + { + if ((pAni->fCleanup == mng_free_ani_seek) && + (strcmp(pEvent->zSegmentname, ((mng_ani_seekp)pAni)->zSegmentname) == 0)) + bFound = MNG_TRUE; + else + pAni = (mng_object_headerp)pAni->pNext; + } + + if (pAni) + pEvent->pSEEK = (mng_ani_seekp)pAni; + } + + if (pEvent->pSEEK) /* anything to do ? */ + { + pEvent->iLastx = pData->iEventx; + pEvent->iLasty = pData->iEventy; + /* let's start from this SEEK then */ + pData->pCurraniobj = (mng_objectp)pEvent->pSEEK; + pData->bRunningevent = MNG_TRUE; + /* wake-up the app ! */ + if (!pData->fSettimer ((mng_handle)pData, 5)) + MNG_ERROR (pData, MNG_APPTIMERERROR); + + } + else + MNG_ERROR (pData, MNG_SEEKNOTFOUND); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_EVENT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#endif /* MNG_SUPPORT_DYNAMICMNG */ + +/* ************************************************************************** */ +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_MPNG_PROPOSAL + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_mpng_obj (mng_datap pData, + mng_uint32 iFramewidth, + mng_uint32 iFrameheight, + mng_uint16 iNumplays, + mng_uint16 iTickspersec, + mng_uint32 iFramessize, + mng_ptr pFrames) +#else +mng_retcode mng_create_mpng_obj (mng_datap pData, + mng_ptr pEntry) +#endif +{ + mng_mpng_objp pMPNG; + mng_ptr pTemp; + mng_retcode iRetcode; + mng_uint8p pFrame; + mng_int32 iCnt, iMax; + mng_uint32 iX, iY, iWidth, iHeight; + mng_int32 iXoffset, iYoffset; + mng_uint16 iTicks; + mng_uint16 iDelay; + mng_bool bNewframe; + mng_ani_loopp pLOOP; + mng_ani_endlp pENDL; + mng_ani_framp pFRAM; + mng_ani_movep pMOVE; + mng_ani_clipp pCLIP; + mng_ani_showp pSHOW; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_MPNG_OBJ, MNG_LC_START); +#endif + +#ifdef MNG_OPTIMIZE_OBJCLEANUP + iRetcode = create_obj_general (pData, sizeof (mng_mpng_obj), mng_free_mpng_obj, + mng_process_mpng_obj, &pTemp); + if (iRetcode) + return iRetcode; + pMPNG = (mng_mpng_objp)pTemp; +#else + MNG_ALLOC (pData, pMPNG, sizeof (mng_mpng_obj)); + + pMPNG->sHeader.fCleanup = mng_free_mpng_obj; + pMPNG->sHeader.fProcess = mng_process_mpng_obj; +#endif + +#ifndef MNG_OPTIMIZE_CHUNKREADER + pMPNG->iFramewidth = iFramewidth; + pMPNG->iFrameheight = iFrameheight; + pMPNG->iNumplays = iNumplays; + pMPNG->iTickspersec = iTickspersec; + pMPNG->iFramessize = iFramessize; + + if (iFramessize) + { + MNG_ALLOC (pData, pMPNG->pFrames, iFramessize); + MNG_COPY (pMPNG->pFrames, pFrames, iFramessize); + } +#else + pMPNG->iFramewidth = ((mng_mpngp)pEntry)->iFramewidth; + pMPNG->iFrameheight = ((mng_mpngp)pEntry)->iFrameheight; + pMPNG->iNumplays = ((mng_mpngp)pEntry)->iNumplays; + pMPNG->iTickspersec = ((mng_mpngp)pEntry)->iTickspersec; + pMPNG->iFramessize = ((mng_mpngp)pEntry)->iFramessize; + + if (pMPNG->iFramessize) + { + MNG_ALLOC (pData, pMPNG->pFrames, pMPNG->iFramessize); + MNG_COPY (pMPNG->pFrames, ((mng_mpngp)pEntry)->pFrames, pMPNG->iFramessize); + } +#endif + + pData->pMPNG = pMPNG; + pData->eImagetype = mng_it_mpng; + + iRetcode = mng_process_display_mpng (pData); + if (iRetcode) + return iRetcode; + + /* now let's create the MNG animation directives from this */ + + pFrame = (mng_uint8p)pMPNG->pFrames; + iMax = pMPNG->iFramessize / 26; + /* set up MNG impersonation */ + pData->iTicks = pMPNG->iTickspersec; + pData->iLayercount = iMax; + + if (pMPNG->iNumplays != 1) /* create a LOOP/ENDL pair ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + iRetcode = create_obj_general (pData, sizeof (mng_ani_loop), + mng_free_ani_loop, mng_process_ani_loop, + &((mng_ptr)pLOOP)); + if (iRetcode) + return iRetcode; +#else + MNG_ALLOC (pData, pLOOP, sizeof (mng_ani_loop)); + + pLOOP->sHeader.fCleanup = mng_free_ani_loop; + pLOOP->sHeader.fProcess = mng_process_ani_loop; +#endif + + pLOOP->iLevel = 1; + if (pMPNG->iNumplays) + pLOOP->iRepeatcount = pMPNG->iNumplays; + else + pLOOP->iRepeatcount = 0xFFFFFFFFl; + + mng_add_ani_object (pData, (mng_object_headerp)pLOOP); + } + + bNewframe = MNG_TRUE; /* create the frame display objects */ + + for (iCnt = 0; iCnt < iMax; iCnt++) + { + iX = mng_get_uint32 (pFrame); + iY = mng_get_uint32 (pFrame+4); + iWidth = mng_get_uint32 (pFrame+8); + iHeight = mng_get_uint32 (pFrame+12); + iXoffset = mng_get_int32 (pFrame+16); + iYoffset = mng_get_int32 (pFrame+20); + iTicks = mng_get_uint16 (pFrame+24); + + iDelay = iTicks; + if (!iDelay) + { + mng_uint8p pTemp = pFrame+26; + mng_int32 iTemp = iCnt+1; + + while ((iTemp < iMax) && (!iDelay)) + { + iDelay = mng_get_uint16 (pTemp+24); + pTemp += 26; + iTemp++; + } + } + + if (bNewframe) + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + iRetcode = create_obj_general (pData, sizeof (mng_ani_fram), + mng_free_obj_general, mng_process_ani_fram, + &((mng_ptr)pFRAM)); + if (iRetcode) + return iRetcode; +#else + MNG_ALLOC (pData, pFRAM, sizeof (mng_ani_fram)); + + pFRAM->sHeader.fCleanup = mng_free_ani_fram; + pFRAM->sHeader.fProcess = mng_process_ani_fram; +#endif + + pFRAM->iFramemode = 4; + pFRAM->iChangedelay = 1; + pFRAM->iDelay = iDelay; + + mng_add_ani_object (pData, (mng_object_headerp)pFRAM); + } + +#ifdef MNG_OPTIMIZE_OBJCLEANUP + iRetcode = create_obj_general (pData, sizeof (mng_ani_move), + mng_free_obj_general, + mng_process_ani_move, + &((mng_ptr)pMOVE)); + if (iRetcode) + return iRetcode; +#else + MNG_ALLOC (pData, pMOVE, sizeof (mng_ani_move)); + + pMOVE->sHeader.fCleanup = mng_free_ani_move; + pMOVE->sHeader.fProcess = mng_process_ani_move; +#endif + + pMOVE->iLocax = iXoffset - (mng_int32)iX; + pMOVE->iLocay = iYoffset - (mng_int32)iY; + + mng_add_ani_object (pData, (mng_object_headerp)pMOVE); + +#ifdef MNG_OPTIMIZE_OBJCLEANUP + iRetcode = create_obj_general (pData, sizeof (mng_ani_clip), + mng_free_obj_general, + mng_process_ani_clip, + &((mng_ptr)pCLIP)); + if (iRetcode) + return iRetcode; +#else + MNG_ALLOC (pData, pCLIP, sizeof (mng_ani_clip)); + + pCLIP->sHeader.fCleanup = mng_free_ani_clip; + pCLIP->sHeader.fProcess = mng_process_ani_clip; +#endif + + pCLIP->iClipl = iXoffset; + pCLIP->iClipr = iXoffset + (mng_int32)iWidth; + pCLIP->iClipt = iYoffset; + pCLIP->iClipb = iYoffset + (mng_int32)iHeight; + + mng_add_ani_object (pData, (mng_object_headerp)pCLIP); + +#ifdef MNG_OPTIMIZE_OBJCLEANUP + iRetcode = create_obj_general (pData, sizeof (mng_ani_show), + mng_free_obj_general, mng_process_ani_show, + &((mng_ptr)pSHOW)); + if (iRetcode) + return iRetcode; +#else + MNG_ALLOC (pData, pSHOW, sizeof (mng_ani_show)); + + pSHOW->sHeader.fCleanup = mng_free_ani_show; + pSHOW->sHeader.fProcess = mng_process_ani_show; +#endif + + mng_add_ani_object (pData, (mng_object_headerp)pSHOW); + + bNewframe = (mng_bool)iTicks; + pFrame += 26; + } + + if (pMPNG->iNumplays != 1) /* create a LOOP/ENDL pair ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + iRetcode = create_obj_general (pData, sizeof (mng_ani_endl), + mng_free_obj_general, mng_process_ani_endl, + &((mng_ptr)pENDL)); + if (iRetcode) + return iRetcode; +#else + MNG_ALLOC (pData, pENDL, sizeof (mng_ani_endl)); + + pENDL->sHeader.fCleanup = mng_free_ani_endl; + pENDL->sHeader.fProcess = mng_process_ani_endl; +#endif + + pENDL->iLevel = 1; + + mng_add_ani_object (pData, (mng_object_headerp)pENDL); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_MPNG_OBJ, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_free_mpng_obj (mng_datap pData, + mng_objectp pObject) +{ + mng_mpng_objp pMPNG = (mng_mpng_objp)pObject; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_MPNG_OBJ, MNG_LC_START); +#endif + + if (pMPNG->iFramessize) + MNG_FREEX (pData, pMPNG->pFrames, pMPNG->iFramessize); + +#ifndef MNG_OPTIMIZE_OBJCLEANUP + MNG_FREEX (pData, pMPNG, sizeof (mng_mpng_obj)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_MPNG_OBJ, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_OBJCLEANUP + return MNG_NOERROR; +#else + return mng_free_obj_general(pData, pObject); +#endif +} + +/* ************************************************************************** */ + +mng_retcode mng_process_mpng_obj (mng_datap pData, + mng_objectp pObject) +{ + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_MPNG_PROPOSAL */ + +/* ************************************************************************** */ +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_ANG_PROPOSAL + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_ang_obj (mng_datap pData, + mng_uint32 iNumframes, + mng_uint32 iTickspersec, + mng_uint32 iNumplays, + mng_uint32 iTilewidth, + mng_uint32 iTileheight, + mng_uint8 iInterlace, + mng_uint8 iStillused) +#else +mng_retcode mng_create_ang_obj (mng_datap pData, + mng_ptr pEntry) +#endif +{ + mng_ang_objp pANG; + mng_ptr pTemp; + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANG_OBJ, MNG_LC_START); +#endif + +#ifdef MNG_OPTIMIZE_OBJCLEANUP + iRetcode = create_obj_general (pData, sizeof (mng_ang_obj), mng_free_ang_obj, + mng_process_ang_obj, &pTemp); + if (iRetcode) + return iRetcode; + pANG = (mng_ang_objp)pTemp; +#else + MNG_ALLOC (pData, pANG, sizeof (mng_ang_obj)); + + pANG->sHeader.fCleanup = mng_free_ang_obj; + pANG->sHeader.fProcess = mng_process_ang_obj; +#endif + +#ifndef MNG_OPTIMIZE_CHUNKREADER + pANG->iNumframes = iNumframes; + pANG->iTickspersec = iTickspersec; + pANG->iNumplays = iNumplays; + pANG->iTilewidth = iTilewidth; + pANG->iTileheight = iTileheight; + pANG->iInterlace = iInterlace; + pANG->iStillused = iStillused; +#else + pANG->iNumframes = ((mng_ahdrp)pEntry)->iNumframes; + pANG->iTickspersec = ((mng_ahdrp)pEntry)->iTickspersec; + pANG->iNumplays = ((mng_ahdrp)pEntry)->iNumplays; + pANG->iTilewidth = ((mng_ahdrp)pEntry)->iTilewidth; + pANG->iTileheight = ((mng_ahdrp)pEntry)->iTileheight; + pANG->iInterlace = ((mng_ahdrp)pEntry)->iInterlace; + pANG->iStillused = ((mng_ahdrp)pEntry)->iStillused; +#endif + + pData->pANG = pANG; + pData->eImagetype = mng_it_ang; + + iRetcode = mng_process_display_ang (pData); + if (iRetcode) + return iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CREATE_ANG_OBJ, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_free_ang_obj (mng_datap pData, + mng_objectp pObject) +{ + mng_ang_objp pANG = (mng_ang_objp)pObject; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANG_OBJ, MNG_LC_START); +#endif + + if (pANG->iTilessize) + MNG_FREEX (pData, pANG->pTiles, pANG->iTilessize); + +#ifndef MNG_OPTIMIZE_OBJCLEANUP + MNG_FREEX (pData, pANG, sizeof (mng_ang_obj)); +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FREE_ANG_OBJ, MNG_LC_END); +#endif + +#ifndef MNG_OPTIMIZE_OBJCLEANUP + return MNG_NOERROR; +#else + return mng_free_obj_general(pData, pObject); +#endif +} + +/* ************************************************************************** */ + +mng_retcode mng_process_ang_obj (mng_datap pData, + mng_objectp pObject) +{ + mng_ang_objp pANG = (mng_ang_objp)pObject; + mng_uint8p pTile = (mng_uint8p)pANG->pTiles; + mng_retcode iRetcode; + mng_int32 iCnt, iMax; + mng_uint32 iTicks; + mng_int32 iXoffset, iYoffset; + mng_uint8 iSource; + mng_ani_loopp pLOOP; + mng_ani_endlp pENDL; + mng_ani_framp pFRAM; + mng_ani_movep pMOVE; + mng_ani_showp pSHOW; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANG_OBJ, MNG_LC_START); +#endif + + /* let's create the MNG animation directives from this */ + + iMax = pANG->iNumframes; + /* set up MNG impersonation */ + pData->iTicks = pANG->iTickspersec; + pData->iLayercount = iMax; + + if (pANG->iNumplays != 1) /* create a LOOP/ENDL pair ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + iRetcode = create_obj_general (pData, sizeof (mng_ani_loop), + mng_free_ani_loop, mng_process_ani_loop, + &((mng_ptr)pLOOP)); + if (iRetcode) + return iRetcode; +#else + MNG_ALLOC (pData, pLOOP, sizeof (mng_ani_loop)); + + pLOOP->sHeader.fCleanup = mng_free_ani_loop; + pLOOP->sHeader.fProcess = mng_process_ani_loop; +#endif + + pLOOP->iLevel = 1; + if (pANG->iNumplays) + pLOOP->iRepeatcount = pANG->iNumplays; + else + pLOOP->iRepeatcount = 0xFFFFFFFFl; + + mng_add_ani_object (pData, (mng_object_headerp)pLOOP); + } + + for (iCnt = 0; iCnt < iMax; iCnt++) + { + iTicks = mng_get_uint32 (pTile); + iXoffset = mng_get_int32 (pTile+4); + iYoffset = mng_get_int32 (pTile+8); + iSource = *(pTile+12); + +#ifdef MNG_OPTIMIZE_OBJCLEANUP + iRetcode = create_obj_general (pData, sizeof (mng_ani_fram), + mng_free_obj_general, mng_process_ani_fram, + &((mng_ptr)pFRAM)); + if (iRetcode) + return iRetcode; +#else + MNG_ALLOC (pData, pFRAM, sizeof (mng_ani_fram)); + + pFRAM->sHeader.fCleanup = mng_free_ani_fram; + pFRAM->sHeader.fProcess = mng_process_ani_fram; +#endif + + pFRAM->iFramemode = 4; + pFRAM->iChangedelay = 1; + pFRAM->iDelay = iTicks; + + mng_add_ani_object (pData, (mng_object_headerp)pFRAM); + + if (!iSource) + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + iRetcode = create_obj_general (pData, sizeof (mng_ani_move), + mng_free_obj_general, + mng_process_ani_move, + &((mng_ptr)pMOVE)); + if (iRetcode) + return iRetcode; +#else + MNG_ALLOC (pData, pMOVE, sizeof (mng_ani_move)); + + pMOVE->sHeader.fCleanup = mng_free_ani_move; + pMOVE->sHeader.fProcess = mng_process_ani_move; +#endif + + pMOVE->iFirstid = 1; + pMOVE->iLastid = 1; + pMOVE->iLocax = -iXoffset; + pMOVE->iLocay = -iYoffset; + + mng_add_ani_object (pData, (mng_object_headerp)pMOVE); + } + +#ifdef MNG_OPTIMIZE_OBJCLEANUP + iRetcode = create_obj_general (pData, sizeof (mng_ani_show), + mng_free_obj_general, mng_process_ani_show, + &((mng_ptr)pSHOW)); + if (iRetcode) + return iRetcode; +#else + MNG_ALLOC (pData, pSHOW, sizeof (mng_ani_show)); + + pSHOW->sHeader.fCleanup = mng_free_ani_show; + pSHOW->sHeader.fProcess = mng_process_ani_show; +#endif + + if (iSource) + pSHOW->iFirstid = 0; + else + pSHOW->iFirstid = 1; + pSHOW->iLastid = pSHOW->iFirstid; + + mng_add_ani_object (pData, (mng_object_headerp)pSHOW); + + pTile += sizeof(mng_adat_tile); + } + + if (pANG->iNumplays != 1) /* create a LOOP/ENDL pair ? */ + { +#ifdef MNG_OPTIMIZE_OBJCLEANUP + iRetcode = create_obj_general (pData, sizeof (mng_ani_endl), + mng_free_obj_general, mng_process_ani_endl, + &((mng_ptr)pENDL)); + if (iRetcode) + return iRetcode; +#else + MNG_ALLOC (pData, pENDL, sizeof (mng_ani_endl)); + + pENDL->sHeader.fCleanup = mng_free_ani_endl; + pENDL->sHeader.fProcess = mng_process_ani_endl; +#endif + + pENDL->iLevel = 1; + + mng_add_ani_object (pData, (mng_object_headerp)pENDL); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_ANG_OBJ, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_ANG_PROPOSAL */ + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_DISPLAY_PROCS */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ + diff --git a/Engine/lib/lmng/libmng_object_prc.h b/Engine/lib/lmng/libmng_object_prc.h new file mode 100644 index 000000000..ffd20c871 --- /dev/null +++ b/Engine/lib/lmng/libmng_object_prc.h @@ -0,0 +1,690 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_object_prc.h copyright (c) 2000-2007 G.Juyn * */ +/* * version : 1.0.10 * */ +/* * * */ +/* * purpose : Object processing routines (definition) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : Definition of the internal object processing routines * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed strict-ANSI stuff * */ +/* * * */ +/* * 0.5.2 - 05/24/2000 - G.Juyn * */ +/* * - added support for global color-chunks in animation * */ +/* * - added support for global PLTE,tRNS,bKGD in animation * */ +/* * - added SAVE & SEEK animation objects * */ +/* * 0.5.2 - 05/29/2000 - G.Juyn * */ +/* * - changed ani_object create routines not to return the * */ +/* * created object (wasn't necessary) * */ +/* * - added compression/filter/interlace fields to * */ +/* * object-buffer for delta-image processing * */ +/* * * */ +/* * 0.5.3 - 06/22/2000 - G.Juyn * */ +/* * - added support for PPLT chunk * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 0.9.3 - 08/26/2000 - G.Juyn * */ +/* * - added MAGN chunk * */ +/* * 0.9.3 - 10/17/2000 - G.Juyn * */ +/* * - added routine to discard "invalid" objects * */ +/* * * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * 1.0.5 - 09/13/2002 - G.Juyn * */ +/* * - fixed read/write of MAGN chunk * */ +/* * 1.0.5 - 09/15/2002 - G.Juyn * */ +/* * - added event handling for dynamic MNG * */ +/* * 1.0.5 - 09/20/2002 - G.Juyn * */ +/* * - added support for PAST * */ +/* * 1.0.5 - 09/23/2002 - G.Juyn * */ +/* * - added in-memory color-correction of abstract images * */ +/* * 1.0.5 - 10/07/2002 - G.Juyn * */ +/* * - fixed DISC support * */ +/* * * */ +/* * 1.0.6 - 07/07/2003 - G.R-P * */ +/* * - added conditionals around Delta-PNG code * */ +/* * - added SKIPCHUNK feature * */ +/* * 1.0.6 - 07/29/2003 - G.R-P * */ +/* * - added conditionals around PAST chunk support * */ +/* * * */ +/* * 1.0.7 - 03/24/2004 - G.R-P * */ +/* * - added more SKIPCHUNK conditionals * */ +/* * * */ +/* * 1.0.9 - 12/05/2004 - G.Juyn * */ +/* * - added conditional MNG_OPTIMIZE_OBJCLEANUP * */ +/* * * */ +/* * 1.0.10 - 04/08/2007 - G.Juyn * */ +/* * - added support for mPNG proposal * */ +/* * 1.0.10 - 04/12/2007 - G.Juyn * */ +/* * - added support for ANG proposal * */ +/* * * */ +/* ************************************************************************** */ + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +#ifndef _libmng_object_prc_h_ +#define _libmng_object_prc_h_ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_DISPLAY_PROCS + +/* ************************************************************************** */ + +mng_retcode mng_drop_invalid_objects (mng_datap pData); + +/* ************************************************************************** */ + +mng_retcode mng_create_imagedataobject (mng_datap pData, + mng_bool bConcrete, + mng_bool bViewable, + mng_uint32 iWidth, + mng_uint32 iHeight, + mng_uint8 iBitdepth, + mng_uint8 iColortype, + mng_uint8 iCompression, + mng_uint8 iFilter, + mng_uint8 iInterlace, + mng_imagedatap *ppObject); + +mng_retcode mng_free_imagedataobject (mng_datap pData, + mng_imagedatap pImagedata); + +mng_retcode mng_clone_imagedataobject (mng_datap pData, + mng_bool bConcrete, + mng_imagedatap pSource, + mng_imagedatap *ppClone); + +/* ************************************************************************** */ + +mng_retcode mng_create_imageobject (mng_datap pData, + mng_uint16 iId, + mng_bool bConcrete, + mng_bool bVisible, + mng_bool bViewable, + mng_uint32 iWidth, + mng_uint32 iHeight, + mng_uint8 iBitdepth, + mng_uint8 iColortype, + mng_uint8 iCompression, + mng_uint8 iFilter, + mng_uint8 iInterlace, + mng_int32 iPosx, + mng_int32 iPosy, + mng_bool bClipped, + mng_int32 iClipl, + mng_int32 iClipr, + mng_int32 iClipt, + mng_int32 iClipb, + mng_imagep *ppObject); + +mng_retcode mng_free_imageobject (mng_datap pData, + mng_imagep pImage); + +mng_imagep mng_find_imageobject (mng_datap pData, + mng_uint16 iId); + +mng_retcode mng_clone_imageobject (mng_datap pData, + mng_uint16 iId, + mng_bool bPartial, + mng_bool bVisible, + mng_bool bAbstract, + mng_bool bHasloca, + mng_uint8 iLocationtype, + mng_int32 iLocationx, + mng_int32 iLocationy, + mng_imagep pSource, + mng_imagep *ppClone); + +mng_retcode mng_renum_imageobject (mng_datap pData, + mng_imagep pSource, + mng_uint16 iId, + mng_bool bVisible, + mng_bool bAbstract, + mng_bool bHasloca, + mng_uint8 iLocationtype, + mng_int32 iLocationx, + mng_int32 iLocationy); + +mng_retcode mng_reset_object_details (mng_datap pData, + mng_imagep pImage, + mng_uint32 iWidth, + mng_uint32 iHeight, + mng_uint8 iBitdepth, + mng_uint8 iColortype, + mng_uint8 iCompression, + mng_uint8 iFilter, + mng_uint8 iInterlace, + mng_bool bResetall); + +mng_retcode mng_promote_imageobject (mng_datap pData, + mng_imagep pImage, + mng_uint8 iBitdepth, + mng_uint8 iColortype, + mng_uint8 iFilltype); + +mng_retcode mng_magnify_imageobject (mng_datap pData, + mng_imagep pImage); + +mng_retcode mng_colorcorrect_object (mng_datap pData, + mng_imagep pImage); + +/* ************************************************************************** */ + +mng_retcode mng_create_ani_image (mng_datap pData); + +#ifndef MNG_OPTIMIZE_CHUNKREADER + +mng_retcode mng_create_ani_plte (mng_datap pData, + mng_uint32 iEntrycount, + mng_palette8ep paEntries); + +mng_retcode mng_create_ani_trns (mng_datap pData, + mng_uint32 iRawlen, + mng_uint8p pRawdata); + +mng_retcode mng_create_ani_gama (mng_datap pData, + mng_bool bEmpty, + mng_uint32 iGamma); + +mng_retcode mng_create_ani_chrm (mng_datap pData, + mng_bool bEmpty, + mng_uint32 iWhitepointx, + mng_uint32 iWhitepointy, + mng_uint32 iRedx, + mng_uint32 iRedy, + mng_uint32 iGreenx, + mng_uint32 iGreeny, + mng_uint32 iBluex, + mng_uint32 iBluey); + +mng_retcode mng_create_ani_srgb (mng_datap pData, + mng_bool bEmpty, + mng_uint8 iRenderinginent); + +mng_retcode mng_create_ani_iccp (mng_datap pData, + mng_bool bEmpty, + mng_uint32 iProfilesize, + mng_ptr pProfile); + +mng_retcode mng_create_ani_bkgd (mng_datap pData, + mng_uint16 iRed, + mng_uint16 iGreen, + mng_uint16 iBlue); + +mng_retcode mng_create_ani_loop (mng_datap pData, + mng_uint8 iLevel, + mng_uint32 iRepeatcount, + mng_uint8 iTermcond, + mng_uint32 iItermin, + mng_uint32 iItermax, + mng_uint32 iCount, + mng_uint32p pSignals); + +mng_retcode mng_create_ani_endl (mng_datap pData, + mng_uint8 iLevel); + +mng_retcode mng_create_ani_defi (mng_datap pData); + +mng_retcode mng_create_ani_basi (mng_datap pData, + mng_uint16 iRed, + mng_uint16 iGreen, + mng_uint16 iBlue, + mng_bool bHasalpha, + mng_uint16 iAlpha, + mng_uint8 iViewable); + +mng_retcode mng_create_ani_clon (mng_datap pData, + mng_uint16 iSourceid, + mng_uint16 iCloneid, + mng_uint8 iClonetype, + mng_bool bHasdonotshow, + mng_uint8 iDonotshow, + mng_uint8 iConcrete, + mng_bool bHasloca, + mng_uint8 iLocatype, + mng_int32 iLocax, + mng_int32 iLocay); + +mng_retcode mng_create_ani_back (mng_datap pData, + mng_uint16 iRed, + mng_uint16 iGreen, + mng_uint16 iBlue, + mng_uint8 iMandatory, + mng_uint16 iImageid, + mng_uint8 iTile); + +mng_retcode mng_create_ani_fram (mng_datap pData, + mng_uint8 iFramemode, + mng_uint8 iChangedelay, + mng_uint32 iDelay, + mng_uint8 iChangetimeout, + mng_uint32 iTimeout, + mng_uint8 iChangeclipping, + mng_uint8 iCliptype, + mng_int32 iClipl, + mng_int32 iClipr, + mng_int32 iClipt, + mng_int32 iClipb); + +mng_retcode mng_create_ani_move (mng_datap pData, + mng_uint16 iFirstid, + mng_uint16 iLastid, + mng_uint8 iType, + mng_int32 iLocax, + mng_int32 iLocay); + +mng_retcode mng_create_ani_clip (mng_datap pData, + mng_uint16 iFirstid, + mng_uint16 iLastid, + mng_uint8 iType, + mng_int32 iClipl, + mng_int32 iClipr, + mng_int32 iClipt, + mng_int32 iClipb); + +mng_retcode mng_create_ani_show (mng_datap pData, + mng_uint16 iFirstid, + mng_uint16 iLastid, + mng_uint8 iMode); + +mng_retcode mng_create_ani_term (mng_datap pData, + mng_uint8 iTermaction, + mng_uint8 iIteraction, + mng_uint32 iDelay, + mng_uint32 iItermax); + +#ifndef MNG_SKIPCHUNK_SAVE +mng_retcode mng_create_ani_save (mng_datap pData); +#endif +#ifndef MNG_SKIPCHUNK_SEEK +mng_retcode mng_create_ani_seek (mng_datap pData, + mng_uint32 iSegmentnamesize, + mng_pchar zSegmentname); +#endif +#ifndef MNG_NO_DELTA_PNG +mng_retcode mng_create_ani_dhdr (mng_datap pData, + mng_uint16 iObjectid, + mng_uint8 iImagetype, + mng_uint8 iDeltatype, + mng_uint32 iBlockwidth, + mng_uint32 iBlockheight, + mng_uint32 iBlockx, + mng_uint32 iBlocky); + +mng_retcode mng_create_ani_prom (mng_datap pData, + mng_uint8 iBitdepth, + mng_uint8 iColortype, + mng_uint8 iFilltype); + +mng_retcode mng_create_ani_ipng (mng_datap pData); +mng_retcode mng_create_ani_ijng (mng_datap pData); + +mng_retcode mng_create_ani_pplt (mng_datap pData, + mng_uint8 iType, + mng_uint32 iCount, + mng_palette8ep paIndexentries, + mng_uint8p paAlphaentries, + mng_uint8p paUsedentries); +#endif + +#ifndef MNG_SKIPCHUNK_MAGN +mng_retcode mng_create_ani_magn (mng_datap pData, + mng_uint16 iFirstid, + mng_uint16 iLastid, + mng_uint8 iMethodX, + mng_uint16 iMX, + mng_uint16 iMY, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint16 iMT, + mng_uint16 iMB, + mng_uint8 iMethodY); +#endif + +#ifndef MNG_SKIPCHUNK_PAST +mng_retcode mng_create_ani_past (mng_datap pData, + mng_uint16 iTargetid, + mng_uint8 iTargettype, + mng_int32 iTargetx, + mng_int32 iTargety, + mng_uint32 iCount, + mng_ptr pSources); +#endif + +#ifndef MNG_SKIPCHUNK_DISC +mng_retcode mng_create_ani_disc (mng_datap pData, + mng_uint32 iCount, + mng_uint16p pIds); +#endif + +#else /* MNG_OPTIMIZE_CHUNKREADER */ + +mng_retcode mng_create_ani_plte (mng_datap pData); +mng_retcode mng_create_ani_trns (mng_datap pData); +mng_retcode mng_create_ani_gama (mng_datap pData, + mng_chunkp pChunk); +mng_retcode mng_create_ani_chrm (mng_datap pData, + mng_chunkp pChunk); +mng_retcode mng_create_ani_srgb (mng_datap pData, + mng_chunkp pChunk); +mng_retcode mng_create_ani_iccp (mng_datap pData, + mng_chunkp pChunk); +mng_retcode mng_create_ani_bkgd (mng_datap pData); +mng_retcode mng_create_ani_loop (mng_datap pData, + mng_chunkp pChunk); +mng_retcode mng_create_ani_endl (mng_datap pData, + mng_uint8 iLevel); +mng_retcode mng_create_ani_defi (mng_datap pData); +mng_retcode mng_create_ani_basi (mng_datap pData, + mng_chunkp pChunk); +mng_retcode mng_create_ani_clon (mng_datap pData, + mng_chunkp pChunk); +mng_retcode mng_create_ani_back (mng_datap pData); +mng_retcode mng_create_ani_fram (mng_datap pData, + mng_chunkp pChunk); +mng_retcode mng_create_ani_move (mng_datap pData, + mng_chunkp pChunk); +mng_retcode mng_create_ani_clip (mng_datap pData, + mng_chunkp pChunk); +mng_retcode mng_create_ani_show (mng_datap pData); +mng_retcode mng_create_ani_term (mng_datap pData, + mng_chunkp pChunk); +#ifndef MNG_SKIPCHUNK_SAVE +mng_retcode mng_create_ani_save (mng_datap pData); +#endif +#ifndef MNG_SKIPCHUNK_SEEK +mng_retcode mng_create_ani_seek (mng_datap pData, + mng_chunkp pChunk); +#endif +#ifndef MNG_NO_DELTA_PNG +mng_retcode mng_create_ani_dhdr (mng_datap pData, + mng_chunkp pChunk); +mng_retcode mng_create_ani_prom (mng_datap pData, + mng_chunkp pChunk); +mng_retcode mng_create_ani_ipng (mng_datap pData); +mng_retcode mng_create_ani_ijng (mng_datap pData); + +mng_retcode mng_create_ani_pplt (mng_datap pData, + mng_uint8 iType, + mng_uint32 iCount, + mng_palette8ep paIndexentries, + mng_uint8p paAlphaentries, + mng_uint8p paUsedentries); +#endif + +#ifndef MNG_SKIPCHUNK_MAGN +mng_retcode mng_create_ani_magn (mng_datap pData, + mng_chunkp pChunk); +#endif +#ifndef MNG_SKIPCHUNK_PAST +mng_retcode mng_create_ani_past (mng_datap pData, + mng_chunkp pChunk); +#endif +#ifndef MNG_SKIPCHUNK_DISC +mng_retcode mng_create_ani_disc (mng_datap pData, + mng_chunkp pChunk); +#endif + +#endif /* MNG_OPTIMIZE_CHUNKREADER */ + +/* ************************************************************************** */ + +mng_retcode mng_free_ani_image (mng_datap pData, + mng_objectp pObject); + +#ifndef MNG_OPTIMIZE_OBJCLEANUP + +mng_retcode mng_free_ani_plte (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_free_ani_trns (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_free_ani_gama (mng_datap pData, + mng_objectp pObject); +#ifndef MNG_SKIPCHUNK_cHRM +mng_retcode mng_free_ani_chrm (mng_datap pData, + mng_objectp pObject); +#endif +#ifndef MNG_SKIPCHUNK_sRGB +mng_retcode mng_free_ani_srgb (mng_datap pData, + mng_objectp pObject); +#endif +#ifndef MNG_SKIPCHUNK_bKGD +mng_retcode mng_free_ani_bkgd (mng_datap pData, + mng_objectp pObject); +#endif +#ifndef MNG_SKIPCHUNK_LOOP +mng_retcode mng_free_ani_endl (mng_datap pData, + mng_objectp pObject); +#endif +mng_retcode mng_free_ani_defi (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_free_ani_basi (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_free_ani_clon (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_free_ani_back (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_free_ani_fram (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_free_ani_move (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_free_ani_clip (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_free_ani_show (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_free_ani_term (mng_datap pData, + mng_objectp pObject); +#ifndef MNG_SKIPCHUNK_SAVE +mng_retcode mng_free_ani_save (mng_datap pData, + mng_objectp pObject); +#endif +#ifndef MNG_NO_DELTA_PNG +mng_retcode mng_free_ani_dhdr (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_free_ani_prom (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_free_ani_ipng (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_free_ani_ijng (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_free_ani_pplt (mng_datap pData, + mng_objectp pObject); +#endif +#ifndef MNG_SKIPCHUNK_MAGN +mng_retcode mng_free_ani_magn (mng_datap pData, + mng_objectp pObject); +#endif + +#endif /* MNG_OPTIMIZE_OBJCLEANUP */ + + +#ifndef MNG_SKIPCHUNK_iCCP +mng_retcode mng_free_ani_iccp (mng_datap pData, + mng_objectp pObject); +#endif +#ifndef MNG_SKIPCHUNK_LOOP +mng_retcode mng_free_ani_loop (mng_datap pData, + mng_objectp pObject); +#endif +#ifndef MNG_SKIPCHUNK_SAVE +mng_retcode mng_free_ani_seek (mng_datap pData, + mng_objectp pObject); +#endif +#ifndef MNG_SKIPCHUNK_PAST +mng_retcode mng_free_ani_past (mng_datap pData, + mng_objectp pObject); +#endif +mng_retcode mng_free_ani_disc (mng_datap pData, + mng_objectp pObject); + +/* ************************************************************************** */ + +mng_retcode mng_process_ani_image (mng_datap pData, + mng_objectp pObject); + +mng_retcode mng_process_ani_plte (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_process_ani_trns (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_process_ani_gama (mng_datap pData, + mng_objectp pObject); +#ifndef MNG_SKIPCHUNK_cHRM +mng_retcode mng_process_ani_chrm (mng_datap pData, + mng_objectp pObject); +#endif +#ifndef MNG_SKIPCHUNK_sRGB +mng_retcode mng_process_ani_srgb (mng_datap pData, + mng_objectp pObject); +#endif +#ifndef MNG_SKIPCHUNK_iCCP +mng_retcode mng_process_ani_iccp (mng_datap pData, + mng_objectp pObject); +#endif +#ifndef MNG_SKIPCHUNK_bKGD +mng_retcode mng_process_ani_bkgd (mng_datap pData, + mng_objectp pObject); +#endif +#ifndef MNG_SKIPCHUNK_LOOP +mng_retcode mng_process_ani_loop (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_process_ani_endl (mng_datap pData, + mng_objectp pObject); +#endif +mng_retcode mng_process_ani_defi (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_process_ani_basi (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_process_ani_clon (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_process_ani_back (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_process_ani_fram (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_process_ani_move (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_process_ani_clip (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_process_ani_show (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_process_ani_term (mng_datap pData, + mng_objectp pObject); +#ifndef MNG_SKIPCHUNK_SAVE +mng_retcode mng_process_ani_save (mng_datap pData, + mng_objectp pObject); +#endif +#ifndef MNG_SKIPCHUNK_SEEK +mng_retcode mng_process_ani_seek (mng_datap pData, + mng_objectp pObject); +#endif +#ifndef MNG_NO_DELTA_PNG +mng_retcode mng_process_ani_dhdr (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_process_ani_prom (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_process_ani_ipng (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_process_ani_ijng (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_process_ani_pplt (mng_datap pData, + mng_objectp pObject); +#endif +mng_retcode mng_process_ani_magn (mng_datap pData, + mng_objectp pObject); +#ifndef MNG_SKIPCHUNK_PAST +mng_retcode mng_process_ani_past (mng_datap pData, + mng_objectp pObject); +#endif +mng_retcode mng_process_ani_disc (mng_datap pData, + mng_objectp pObject); + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DYNAMICMNG +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_event (mng_datap pData, + mng_uint8 iEventtype, + mng_uint8 iMasktype, + mng_int32 iLeft, + mng_int32 iRight, + mng_int32 iTop, + mng_int32 iBottom, + mng_uint16 iObjectid, + mng_uint8 iIndex, + mng_uint32 iSegmentnamesize, + mng_pchar zSegmentname); +#else +mng_retcode mng_create_event (mng_datap pData, + mng_ptr pEntry); +#endif +mng_retcode mng_free_event (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_process_event (mng_datap pData, + mng_objectp pObject); +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_MPNG_PROPOSAL +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_mpng_obj (mng_datap pData, + mng_uint32 iFramewidth, + mng_uint32 iFrameheight, + mng_uint16 iNumplays, + mng_uint16 iTickspersec, + mng_uint32 iFramessize, + mng_ptr pFrames); +#else +mng_retcode mng_create_mpng_obj (mng_datap pData, + mng_ptr pEntry); +#endif +mng_retcode mng_free_mpng_obj (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_process_mpng_obj (mng_datap pData, + mng_objectp pObject); +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_ANG_PROPOSAL +#ifndef MNG_OPTIMIZE_CHUNKREADER +mng_retcode mng_create_ang_obj (mng_datap pData, + mng_uint32 iNumframes, + mng_uint32 iTickspersec, + mng_uint32 iNumplays, + mng_uint32 iTilewidth, + mng_uint32 iTileheight, + mng_uint8 iInterlace, + mng_uint8 iStillused); +#else +mng_retcode mng_create_ang_obj (mng_datap pData, + mng_ptr pEntry); +#endif +mng_retcode mng_free_ang_obj (mng_datap pData, + mng_objectp pObject); +mng_retcode mng_process_ang_obj (mng_datap pData, + mng_objectp pObject); +#endif + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_DISPLAY_PROCS */ + +/* ************************************************************************** */ + +#endif /* _libmng_object_prc_h_ */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ diff --git a/Engine/lib/lmng/libmng_objects.h b/Engine/lib/lmng/libmng_objects.h new file mode 100644 index 000000000..053e6b4b2 --- /dev/null +++ b/Engine/lib/lmng/libmng_objects.h @@ -0,0 +1,635 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_objects.h copyright (c) 2000-2007 G.Juyn * */ +/* * version : 1.0.10 * */ +/* * * */ +/* * purpose : Internal object structures (definition) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : Definition of the internal object structures * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed strict-ANSI stuff * */ +/* * * */ +/* * 0.5.2 - 05/23/2000 - G.Juyn * */ +/* * - changed inclusion to DISPLAY_PROCS * */ +/* * 0.5.2 - 05/24/2000 - G.Juyn * */ +/* * - added global color-chunks for animations * */ +/* * - added global PLTE,tRNS,bKGD chunks for animation * */ +/* * - added SAVE & SEEK animation objects * */ +/* * 0.5.2 - 05/29/2000 - G.Juyn * */ +/* * - added framenr/layernr/playtime to object header * */ +/* * 0.5.2 - 05/30/2000 - G.Juyn * */ +/* * - added ani-objects for delta-image processing * */ +/* * - added compression/filter/interlace fields to * */ +/* * object-buffer for delta-image processing * */ +/* * * */ +/* * 0.5.3 - 06/17/2000 - G.Juyn * */ +/* * - changed definition of aTRNSentries * */ +/* * 0.5.3 - 06/22/2000 - G.Juyn * */ +/* * - added definition for PPLT animation-processing * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 0.9.3 - 08/26/2000 - G.Juyn * */ +/* * - added MAGN chunk * */ +/* * 0.9.3 - 09/10/2000 - G.Juyn * */ +/* * - fixed DEFI behavior * */ +/* * 0.9.3 - 10/16/2000 - G.Juyn * */ +/* * - added support for delta-JNG * */ +/* * 0.9.3 - 10/17/2000 - G.Juyn * */ +/* * - added valid-flag to stored objects for read() / display()* */ +/* * 0.9.3 - 10/19/2000 - G.Juyn * */ +/* * - added storage for pixel-/alpha-sampledepth for delta's * */ +/* * * */ +/* * 1.0.5 - 09/13/2002 - G.Juyn * */ +/* * - fixed read/write of MAGN chunk * */ +/* * 1.0.5 - 09/15/2002 - G.Juyn * */ +/* * - added event handling for dynamic MNG * */ +/* * 1.0.5 - 09/20/2002 - G.Juyn * */ +/* * - added support for PAST * */ +/* * 1.0.5 - 09/23/2002 - G.Juyn * */ +/* * - added in-memory color-correction of abstract images * */ +/* * 1.0.5 - 10/07/2002 - G.Juyn * */ +/* * - fixed DISC support * */ +/* * * */ +/* * 1.0.6 - 10/07/2003 - G.R-P * */ +/* * - added SKIPCHUNK conditionals * */ +/* * * */ +/* * 1.0.7 - 03/24/2004 - G.R-P * */ +/* * - added more SKIPCHUNK conditionals * */ +/* * * */ +/* * 1.0.9 - 12/05/2004 - G.Juyn * */ +/* * - added conditional MNG_OPTIMIZE_OBJCLEANUP * */ +/* * * */ +/* * 1.0.10 - 04/08/2007 - G.Juyn * */ +/* * - added support for mPNG proposal * */ +/* * 1.0.10 - 04/12/2007 - G.Juyn * */ +/* * - added support for ANG proposal * */ +/* * * */ +/* ************************************************************************** */ + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +#ifndef _libmng_objects_h_ +#define _libmng_objects_h_ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_DISPLAY_PROCS + +/* ************************************************************************** */ + +typedef mng_retcode (*mng_cleanupobject) (mng_datap pData, + mng_objectp pHeader); + +typedef mng_retcode (*mng_processobject) (mng_datap pData, + mng_objectp pHeader); + +/* ************************************************************************** */ + +typedef struct { + mng_cleanupobject fCleanup; + mng_processobject fProcess; + mng_objectp pNext; /* for double-linked list */ + mng_objectp pPrev; + mng_uint32 iFramenr; + mng_uint32 iLayernr; + mng_uint32 iPlaytime; +#ifdef MNG_OPTIMIZE_OBJCLEANUP + mng_size_t iObjsize; +#endif + } mng_object_header; +typedef mng_object_header * mng_object_headerp; + +/* ************************************************************************** */ + +typedef struct { /* MNG specification "object-buffer" */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_uint32 iRefcount; /* reference counter */ + mng_bool bFrozen; /* frozen flag */ + mng_bool bConcrete; /* concrete flag */ + mng_bool bViewable; /* viewable flag */ + mng_uint32 iWidth; /* image specifics */ + mng_uint32 iHeight; + mng_uint8 iBitdepth; + mng_uint8 iColortype; + mng_uint8 iCompression; + mng_uint8 iFilter; + mng_uint8 iInterlace; + + mng_bool bCorrected; /* indicates if an abstract image + has already been color-corrected */ + + mng_uint8 iAlphabitdepth; /* used only for JNG images */ + mng_uint8 iJHDRcompression; + mng_uint8 iJHDRinterlace; + + mng_uint8 iPixelsampledepth; /* used with delta-images */ + mng_uint8 iAlphasampledepth; + + mng_bool bHasPLTE; /* PLTE chunk present */ + mng_bool bHasTRNS; /* tRNS chunk present */ + mng_bool bHasGAMA; /* gAMA chunk present */ + mng_bool bHasCHRM; /* cHRM chunk present */ + mng_bool bHasSRGB; /* sRGB chunk present */ + mng_bool bHasICCP; /* iCCP chunk present */ + mng_bool bHasBKGD; /* bKGD chunk present */ + + mng_uint32 iPLTEcount; /* PLTE fields */ + mng_rgbpaltab aPLTEentries; + + mng_uint16 iTRNSgray; /* tRNS fields */ + mng_uint16 iTRNSred; + mng_uint16 iTRNSgreen; + mng_uint16 iTRNSblue; + mng_uint32 iTRNScount; + mng_uint8arr aTRNSentries; + + mng_uint32 iGamma; /* gAMA fields */ + + mng_uint32 iWhitepointx; /* cHRM fields */ + mng_uint32 iWhitepointy; + mng_uint32 iPrimaryredx; + mng_uint32 iPrimaryredy; + mng_uint32 iPrimarygreenx; + mng_uint32 iPrimarygreeny; + mng_uint32 iPrimarybluex; + mng_uint32 iPrimarybluey; + + mng_uint8 iRenderingintent; /* sRGB fields */ + + mng_uint32 iProfilesize; /* iCCP fields */ + mng_ptr pProfile; + + mng_uint8 iBKGDindex; /* bKGD fields */ + mng_uint16 iBKGDgray; + mng_uint16 iBKGDred; + mng_uint16 iBKGDgreen; + mng_uint16 iBKGDblue; + + mng_uint32 iSamplesize; /* size of a sample */ + mng_uint32 iRowsize; /* size of a row of samples */ + mng_uint32 iImgdatasize; /* size of the sample data buffer */ + mng_uint8p pImgdata; /* actual sample data buffer */ + + } mng_imagedata; +typedef mng_imagedata * mng_imagedatap; + +/* ************************************************************************** */ + +typedef struct { /* MNG specification "object" */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_uint16 iId; /* object-id */ + mng_bool bFrozen; /* frozen flag */ + mng_bool bVisible; /* potential visibility flag */ + mng_bool bViewable; /* viewable flag */ + mng_bool bValid; /* marks invalid when only reading */ + mng_int32 iPosx; /* location fields */ + mng_int32 iPosy; + mng_bool bClipped; /* clipping fields */ + mng_int32 iClipl; + mng_int32 iClipr; + mng_int32 iClipt; + mng_int32 iClipb; +#ifndef MNG_SKIPCHUNK_MAGN + mng_uint8 iMAGN_MethodX; /* magnification (MAGN) */ + mng_uint8 iMAGN_MethodY; + mng_uint16 iMAGN_MX; + mng_uint16 iMAGN_MY; + mng_uint16 iMAGN_ML; + mng_uint16 iMAGN_MR; + mng_uint16 iMAGN_MT; + mng_uint16 iMAGN_MB; +#endif +#ifndef MNG_SKIPCHUNK_PAST + mng_int32 iPastx; /* target x/y from previous PAST */ + mng_int32 iPasty; +#endif + mng_imagedatap pImgbuf; /* the image-data buffer */ + } mng_image; +typedef mng_image * mng_imagep; + +/* ************************************************************************** */ + + /* "on-the-fly" image (= object 0) */ +typedef mng_image mng_ani_image; /* let's (ab)use the general "object" */ +typedef mng_ani_image * mng_ani_imagep; /* that's actualy crucial, so don't change it! */ + +/* ************************************************************************** */ + +typedef struct { /* global PLTE object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_uint32 iEntrycount; + mng_rgbpaltab aEntries; + } mng_ani_plte; +typedef mng_ani_plte * mng_ani_pltep; + +/* ************************************************************************** */ + +typedef struct { /* global tRNS object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_uint32 iRawlen; + mng_uint8arr aRawdata; + } mng_ani_trns; +typedef mng_ani_trns * mng_ani_trnsp; + +/* ************************************************************************** */ + +typedef struct { /* global gAMA object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_bool bEmpty; + mng_uint32 iGamma; + } mng_ani_gama; +typedef mng_ani_gama * mng_ani_gamap; + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_cHRM +typedef struct { /* global cHRM object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_bool bEmpty; + mng_uint32 iWhitepointx; + mng_uint32 iWhitepointy; + mng_uint32 iRedx; + mng_uint32 iRedy; + mng_uint32 iGreenx; + mng_uint32 iGreeny; + mng_uint32 iBluex; + mng_uint32 iBluey; + } mng_ani_chrm; +typedef mng_ani_chrm * mng_ani_chrmp; +#endif + +/* ************************************************************************** */ + +typedef struct { /* global sRGB object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_bool bEmpty; + mng_uint8 iRenderingintent; + } mng_ani_srgb; +typedef mng_ani_srgb * mng_ani_srgbp; + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_iCCP +typedef struct { /* global iCCP object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_bool bEmpty; + mng_uint32 iProfilesize; + mng_ptr pProfile; + } mng_ani_iccp; +typedef mng_ani_iccp * mng_ani_iccpp; +#endif + +/* ************************************************************************** */ + +typedef struct { /* global bKGD object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_uint16 iRed; + mng_uint16 iGreen; + mng_uint16 iBlue; + } mng_ani_bkgd; +typedef mng_ani_bkgd * mng_ani_bkgdp; + +/* ************************************************************************** */ + +typedef struct { /* LOOP object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_uint8 iLevel; + mng_uint32 iRepeatcount; + mng_uint8 iTermcond; + mng_uint32 iItermin; + mng_uint32 iItermax; + mng_uint32 iCount; + mng_uint32p pSignals; + + mng_uint32 iRunningcount; /* running counter */ + } mng_ani_loop; +typedef mng_ani_loop * mng_ani_loopp; + +/* ************************************************************************** */ + +typedef struct { /* ENDL object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_uint8 iLevel; + + mng_ani_loopp pLOOP; /* matching LOOP */ + } mng_ani_endl; +typedef mng_ani_endl * mng_ani_endlp; + +/* ************************************************************************** */ + +typedef struct { /* DEFI object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_uint16 iId; + mng_bool bHasdonotshow; + mng_uint8 iDonotshow; + mng_bool bHasconcrete; + mng_uint8 iConcrete; + mng_bool bHasloca; + mng_int32 iLocax; + mng_int32 iLocay; + mng_bool bHasclip; + mng_int32 iClipl; + mng_int32 iClipr; + mng_int32 iClipt; + mng_int32 iClipb; + } mng_ani_defi; +typedef mng_ani_defi * mng_ani_defip; + +/* ************************************************************************** */ + +typedef struct { /* BASI object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_uint16 iRed; + mng_uint16 iGreen; + mng_uint16 iBlue; + mng_bool bHasalpha; + mng_uint16 iAlpha; + mng_uint8 iViewable; + } mng_ani_basi; +typedef mng_ani_basi * mng_ani_basip; + +/* ************************************************************************** */ + +typedef struct { /* CLON object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_uint16 iCloneid; + mng_uint16 iSourceid; + mng_uint8 iClonetype; + mng_bool bHasdonotshow; + mng_uint8 iDonotshow; + mng_uint8 iConcrete; + mng_bool bHasloca; + mng_uint8 iLocatype; + mng_int32 iLocax; + mng_int32 iLocay; + } mng_ani_clon; +typedef mng_ani_clon * mng_ani_clonp; + +/* ************************************************************************** */ + +typedef struct { /* BACK object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_uint16 iRed; + mng_uint16 iGreen; + mng_uint16 iBlue; + mng_uint8 iMandatory; + mng_uint16 iImageid; + mng_uint8 iTile; + } mng_ani_back; +typedef mng_ani_back * mng_ani_backp; + +/* ************************************************************************** */ + +typedef struct { /* FRAM object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_uint8 iFramemode; + mng_uint8 iChangedelay; + mng_uint32 iDelay; + mng_uint8 iChangetimeout; + mng_uint32 iTimeout; + mng_uint8 iChangeclipping; + mng_uint8 iCliptype; + mng_int32 iClipl; + mng_int32 iClipr; + mng_int32 iClipt; + mng_int32 iClipb; + } mng_ani_fram; +typedef mng_ani_fram * mng_ani_framp; + +/* ************************************************************************** */ + +typedef struct { /* MOVE object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_uint16 iFirstid; + mng_uint16 iLastid; + mng_uint8 iType; + mng_int32 iLocax; + mng_int32 iLocay; + } mng_ani_move; +typedef mng_ani_move * mng_ani_movep; + +/* ************************************************************************** */ + +typedef struct { /* CLIP object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_uint16 iFirstid; + mng_uint16 iLastid; + mng_uint8 iType; + mng_int32 iClipl; + mng_int32 iClipr; + mng_int32 iClipt; + mng_int32 iClipb; + } mng_ani_clip; +typedef mng_ani_clip * mng_ani_clipp; + +/* ************************************************************************** */ + +typedef struct { /* SHOW object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_uint16 iFirstid; + mng_uint16 iLastid; + mng_uint8 iMode; + } mng_ani_show; +typedef mng_ani_show * mng_ani_showp; + +/* ************************************************************************** */ + +typedef struct { /* TERM object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_uint8 iTermaction; + mng_uint8 iIteraction; + mng_uint32 iDelay; + mng_uint32 iItermax; + } mng_ani_term; +typedef mng_ani_term * mng_ani_termp; + +/* ************************************************************************** */ + +typedef struct { /* SAVE object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + } mng_ani_save; +typedef mng_ani_save * mng_ani_savep; + +/* ************************************************************************** */ + +typedef struct { /* SEEK object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_uint32 iSegmentnamesize; + mng_pchar zSegmentname; + } mng_ani_seek; +typedef mng_ani_seek * mng_ani_seekp; + +/* ************************************************************************** */ +#ifndef MNG_NO_DELTA_PNG +typedef struct { /* DHDR object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_uint16 iObjectid; + mng_uint8 iImagetype; + mng_uint8 iDeltatype; + mng_uint32 iBlockwidth; + mng_uint32 iBlockheight; + mng_uint32 iBlockx; + mng_uint32 iBlocky; + } mng_ani_dhdr; +typedef mng_ani_dhdr * mng_ani_dhdrp; + +/* ************************************************************************** */ + +typedef struct { /* PROM object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_uint8 iBitdepth; + mng_uint8 iColortype; + mng_uint8 iFilltype; + } mng_ani_prom; +typedef mng_ani_prom * mng_ani_promp; + +/* ************************************************************************** */ + +typedef struct { /* IPNG object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + } mng_ani_ipng; +typedef mng_ani_ipng * mng_ani_ipngp; + +/* ************************************************************************** */ + +typedef struct { /* IJNG object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + } mng_ani_ijng; +typedef mng_ani_ijng * mng_ani_ijngp; + +/* ************************************************************************** */ + +typedef struct { /* PPLT object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_uint8 iType; + mng_uint32 iCount; + mng_rgbpaltab aIndexentries; + mng_uint8arr aAlphaentries; + mng_uint8arr aUsedentries; + } mng_ani_pplt; +typedef mng_ani_pplt * mng_ani_ppltp; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_MAGN +typedef struct { /* MAGN object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_uint16 iFirstid; + mng_uint16 iLastid; + mng_uint8 iMethodX; + mng_uint16 iMX; + mng_uint16 iMY; + mng_uint16 iML; + mng_uint16 iMR; + mng_uint16 iMT; + mng_uint16 iMB; + mng_uint8 iMethodY; + } mng_ani_magn; +typedef mng_ani_magn * mng_ani_magnp; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_PAST +typedef struct { /* PAST object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_uint16 iTargetid; + mng_uint8 iTargettype; + mng_int32 iTargetx; + mng_int32 iTargety; + mng_uint32 iCount; + mng_ptr pSources; + } mng_ani_past; +typedef mng_ani_past * mng_ani_pastp; +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_DISC +typedef struct { /* DISC object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_uint32 iCount; + mng_uint16p pIds; + } mng_ani_disc; +typedef mng_ani_disc * mng_ani_discp; +#endif + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DYNAMICMNG +typedef struct { /* event object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_uint8 iEventtype; + mng_uint8 iMasktype; + mng_int32 iLeft; + mng_int32 iRight; + mng_int32 iTop; + mng_int32 iBottom; + mng_uint16 iObjectid; + mng_uint8 iIndex; + mng_uint32 iSegmentnamesize; + mng_pchar zSegmentname; + + mng_ani_seekp pSEEK; /* SEEK ani object */ + mng_int32 iLastx; /* last X/Y coordinates */ + mng_int32 iLasty; + } mng_event; +typedef mng_event * mng_eventp; +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_MPNG_PROPOSAL +typedef struct { /* mPNG object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_uint32 iFramewidth; + mng_uint32 iFrameheight; + mng_uint32 iNumplays; + mng_uint16 iTickspersec; + mng_uint32 iFramessize; + mng_ptr pFrames; + } mng_mpng_obj; +typedef mng_mpng_obj * mng_mpng_objp; +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_ANG_PROPOSAL +typedef struct { /* ANG object */ + mng_object_header sHeader; /* default header (DO NOT REMOVE) */ + mng_uint32 iNumframes; + mng_uint32 iTickspersec; + mng_uint32 iNumplays; + mng_uint32 iTilewidth; + mng_uint32 iTileheight; + mng_uint8 iInterlace; + mng_uint8 iStillused; + mng_uint32 iTilessize; + mng_ptr pTiles; + } mng_ang_obj; +typedef mng_ang_obj * mng_ang_objp; +#endif + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_DISPLAY_PROCS */ + +/* ************************************************************************** */ + +#endif /* _libmng_objects_h_ */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ diff --git a/Engine/lib/lmng/libmng_pixels.c b/Engine/lib/lmng/libmng_pixels.c new file mode 100644 index 000000000..ce5637be9 --- /dev/null +++ b/Engine/lib/lmng/libmng_pixels.c @@ -0,0 +1,24610 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_pixels.c copyright (c) 2000-2005 G.Juyn * */ +/* * version : 1.0.10 * */ +/* * * */ +/* * purpose : Pixel-row management routines (implementation) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : implementation of the pixel-row management routines * */ +/* * * */ +/* * the dual alpha-composing for RGBA/BGRA/etc output-canvas' * */ +/* * is based on the Note on Compositing chapter of the * */ +/* * DOH-3 draft, noted to me by Adam M. Costello * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed strict-ANSI stuff * */ +/* * 0.5.1 - 05/11/2000 - G.Juyn * */ +/* * - added callback error-reporting support * */ +/* * 0.5.1 - 05/12/2000 - G.Juyn * */ +/* * - changed trace to macro for callback error-reporting * */ +/* * * */ +/* * 0.5.2 - 05/22/2000 - G.Juyn * */ +/* * - added JNG support * */ +/* * 0.5.2 - 05/30/2000 - G.Juyn * */ +/* * - fixed minor bugs 16-bit pixel-handling * */ +/* * - added delta-image row-processing routines * */ +/* * 0.5.2 - 06/02/2000 - G.Juyn * */ +/* * - fixed endian support (hopefully) * */ +/* * 0.5.2 - 06/03/2000 - G.Juyn * */ +/* * - fixed makeup for Linux gcc compile * */ +/* * 0.5.2 - 06/05/2000 - G.Juyn * */ +/* * - implemented app bkgd restore routines * */ +/* * - implemented RGBA8, ARGB8, BGRA8 & ABGR8 display routines * */ +/* * - added support for RGB8_A8 canvasstyle * */ +/* * 0.5.2 - 06/09/2000 - G.Juyn * */ +/* * - fixed alpha-handling for alpha canvasstyles * */ +/* * * */ +/* * 0.5.3 - 06/16/2000 - G.Juyn * */ +/* * - changed progressive-display processing * */ +/* * 0.5.3 - 06/17/2000 - G.Juyn * */ +/* * - changed to support delta-images * */ +/* * - optimized some store_xxx routines * */ +/* * 0.5.3 - 06/20/2000 - G.Juyn * */ +/* * - fixed nasty bug with embedded PNG after delta-image * */ +/* * 0.5.3 - 06/24/2000 - G.Juyn * */ +/* * - fixed problem with 16-bit GA format * */ +/* * 0.5.3 - 06/25/2000 - G.Juyn * */ +/* * - fixed problem with cheap transparency for 4-bit gray * */ +/* * - fixed display_xxxx routines for interlaced images * */ +/* * 0.5.3 - 06/28/2000 - G.Juyn * */ +/* * - fixed compiler-warning for non-initialized iB variable * */ +/* * * */ +/* * 0.9.1 - 07/05/2000 - G.Juyn * */ +/* * - fixed mandatory BACK color to be opaque * */ +/* * * */ +/* * 0.9.2 - 07/31/2000 - G.Juyn * */ +/* * - B110547 - fixed bug in interlace code * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 0.9.3 - 08/20/2000 - G.Juyn * */ +/* * - fixed app-supplied background restore * */ +/* * 0.9.3 - 08/26/2000 - G.Juyn * */ +/* * - added MAGN chunk * */ +/* * 0.9.3 - 09/07/2000 - G.Juyn * */ +/* * - added support for new filter_types * */ +/* * 0.9.3 - 09/30/2000 - G.Juyn * */ +/* * - fixed MAGN rounding errors (thanks Matthias!) * */ +/* * 0.9.3 - 10/10/2000 - G.Juyn * */ +/* * - fixed alpha-blending for RGBA canvasstyle * */ +/* * 0.9.3 - 10/11/2000 - G.Juyn * */ +/* * - fixed alpha-blending for other alpha-canvasstyles * */ +/* * 0.9.3 - 10/16/2000 - G.Juyn * */ +/* * - added optional support for bKGD for PNG images * */ +/* * - added support for JDAA * */ +/* * 0.9.3 - 10/17/2000 - G.Juyn * */ +/* * - fixed support for bKGD * */ +/* * 0.9.3 - 10/19/2000 - G.Juyn * */ +/* * - implemented delayed delta-processing * */ +/* * 0.9.3 - 10/28/2000 - G.Juyn * */ +/* * - fixed tRNS processing for gray-image < 8-bits * */ +/* * * */ +/* * 0.9.4 - 12/16/2000 - G.Juyn * */ +/* * - fixed mixup of data- & function-pointers (thanks Dimitri)* */ +/* * 0.9.4 - 1/18/2001 - G.Juyn * */ +/* * - removed "old" MAGN methods 3 & 4 * */ +/* * - added "new" MAGN methods 3, 4 & 5 * */ +/* * - removed test filter-methods 1 & 65 * */ +/* * * */ +/* * 1.0.1 - 04/21/2001 - G.Juyn (code by G.Kelly) * */ +/* * - added BGRA8 canvas with premultiplied alpha * */ +/* * 1.0.1 - 04/25/2001 - G.Juyn * */ +/* * - moved mng_clear_cms to libmng_cms * */ +/* * * */ +/* * 1.0.2 - 06/25/2001 - G.Juyn * */ +/* * - added option to turn off progressive refresh * */ +/* * * */ +/* * 1.0.4 - 11/04/2001 - G.Juyn * */ +/* * - fixed possible compile-problem in cleanup_rowproc * */ +/* * 1.0.4 - 06/22/2002 - G.Juyn * */ +/* * - B558212 - off by one error * */ +/* * - MNG subimage alpha composite wrong for rgba8 images * */ +/* * * */ +/* * 1.0.5 - 08/07/2002 - G.Juyn * */ +/* * - added test-option for PNG filter method 193 (=no filter) * */ +/* * 1.0.5 - 08/15/2002 - G.Juyn * */ +/* * - completed PROM support * */ +/* * - completed delta-image support * */ +/* * 1.0.5 - 08/16/2002 - G.Juyn * */ +/* * - completed MAGN support (16-bit functions) * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * 1.0.5 - 09/19/2002 - G.Juyn * */ +/* * - optimized restore-background for bKGD cases * */ +/* * 1.0.5 - 09/20/2002 - G.Juyn * */ +/* * - finished support for BACK image & tiling * */ +/* * 1.0.5 - 09/22/2002 - G.Juyn * */ +/* * - added bgrx8 canvas (filler byte) * */ +/* * 1.0.5 - 09/23/2002 - G.Juyn * */ +/* * - added compose over/under routines for PAST processing * */ +/* * - added flip & tile routines for PAST processing * */ +/* * * */ +/* * 1.0.6 - 03/09/2003 - G.Juyn * */ +/* * - hiding 12-bit JPEG stuff * */ +/* * 1.0.6 - 05/11/2003 - Glenn RP * */ +/* * - added size-optimization COMPOSE routine usage * */ +/* * 1.0.6 - 05/11/2003 - G. Juyn * */ +/* * - added conditionals around canvas update routines * */ +/* * 1.0.6 - 05/25/2003 - Glenn RP * */ +/* * - added size-optimization DIV255B8 routine usage * */ +/* * 1.0.6 - 06/09/2003 - G. R-P * */ +/* * - added conditionals around 8-bit magn routines * */ +/* * 1.0.6 - 07/07/2003 - G. R-P * */ +/* * - removed conditionals around 8-bit magn routines * */ +/* * - added MNG_NO_16BIT_SUPPORT and MNG_NO_DELTA_PNG * */ +/* * conditionals * */ +/* * - reversed many loops to use decrementing counter * */ +/* * - combined init functions * */ +/* * - converted some switches to array references * */ +/* * 1.0.6 - 07/29/2003 - G.Juyn * */ +/* * - fixed duplicate for-loop * */ +/* * 1.0.6 - 07/29/2003 - G.R-P * */ +/* * - added SKIPCHUNK conditionals around PAST chunk support * */ +/* * - fixed "FOOTPRINT_COMPOSEIV" typo (now "FOOTPRINT_DIV") * */ +/* * 1.0.6 - 08/17/2003 - G.R-P * */ +/* * - added more conditionals around "promote" functions * */ +/* * * */ +/* * 1.0.7 - 11/27/2003 - R.A * */ +/* * - added CANVAS_RGB565 and CANVAS_BGR565 * */ +/* * 1.0.7 - 12/06/2003 - R.A * */ +/* * - added CANVAS_RGBA565 and CANVAS_BGRA565 * */ +/* * 1.0.7 - 01/25/2004 - J.S * */ +/* * - added premultiplied alpha canvas' for RGBA, ARGB, ABGR * */ +/* * 1.0.7 - 03/08/2004 - G.R-P * */ +/* * - added more conditionals around 16-bit-supporting code * */ +/* * 1.0.7 - 03/09/2004 - G.Juyn * */ +/* * - fixed bug in promote_g8_g8 with 16bit support off * */ +/* * 1.0.7 - 03/09/2004 - G.R-P * */ +/* * - more optimizations with 16bit support off * */ +/* * 1.0.7 - 03/10/2004 - G.Juyn * */ +/* * - fixed some warnings for 16bit optimizations * */ +/* * 1.0.7 - 03/21/2004 - G.Juyn * */ +/* * - fixed some 64-bit platform compiler warnings * */ +/* * * */ +/* * 1.0.8 - 06/20/2004 - G.Juyn * */ +/* * - some speed optimizations (thanks to John Stiles) * */ +/* * 1.0.8 - 08/01/2004 - G.Juyn * */ +/* * - added support for 3+byte pixelsize for JPEG's * */ +/* * * */ +/* * 1.0.9 - 10/10/2004 - G.R-P. * */ +/* * - added MNG_NO_1_2_4BIT_SUPPORT * */ +/* * 1.0.9 - 10/14/2004 - G.Juyn * */ +/* * - added bgr565_a8 canvas-style (thanks to J. Elvander) * */ +/* * 1.0.9 - 12/05/2004 - G.Juyn * */ +/* * - added LITTLEENDIAN/BIGENDIAN fixtures (thanks J.Stiles) * */ +/* * - fixed MNG_NO_1_2_4BIT_SUPPORT for TBBN1G04.PNG * */ +/* * 1.0.9 - 12/31/2004 - G.R-P. * */ +/* * - fixed warnings about C++ style (//) comments * */ +/* * * */ +/* * 1.0.10 - 07/06/2005 - G.R-P. * */ +/* * - added MORE MNG_NO_1_2_4BIT_SUPPORT * */ +/* * 1.0.10 - 10/06/2005 - G.R-P. * */ +/* * - alloc more memory for MNG_NO_1_2_4BIT_SUPPORT * */ +/* * 1.0.10 - 12/07/2005 - G.R-P. * */ +/* * - optimized footprint of 16bit support * */ +/* * 1.0.10 - 03/07/2006 - (thanks to W. Manthey) * */ +/* * - added CANVAS_RGB555 and CANVAS_BGR555 * */ +/* * * */ +/* ************************************************************************** */ + +#include "libmng.h" +#include "libmng_data.h" +#include "libmng_error.h" +#include "libmng_trace.h" +#ifdef __BORLANDC__ +#pragma hdrstop +#endif +#include "libmng_objects.h" +#include "libmng_object_prc.h" +#include "libmng_memory.h" +#include "libmng_cms.h" +#include "libmng_filter.h" +#include "libmng_pixels.h" + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_DISPLAY_PROCS + +/* TODO: magnification & canvas-positioning/-clipping */ + +/* TODO: major optimization of pixel-loops by using assembler (?) */ + +/* ************************************************************************** */ +/* * * */ +/* * Interlace tables * */ +/* * * */ +/* ************************************************************************** */ + +MNG_LOCAL mng_uint32 const interlace_row [7] = { 0, 0, 4, 0, 2, 0, 1 }; +MNG_LOCAL mng_uint32 const interlace_rowskip [7] = { 8, 8, 8, 4, 4, 2, 2 }; +MNG_LOCAL mng_uint32 const interlace_col [7] = { 0, 4, 0, 2, 0, 1, 0 }; +MNG_LOCAL mng_uint32 const interlace_colskip [7] = { 8, 8, 4, 4, 2, 2, 1 }; +MNG_LOCAL mng_uint32 const interlace_roundoff [7] = { 7, 7, 3, 3, 1, 1, 0 }; +MNG_LOCAL mng_uint32 const interlace_divider [7] = { 3, 3, 2, 2, 1, 1, 0 }; + +/* ************************************************************************** */ +/* * * */ +/* * Alpha composing macros * */ +/* * the code below is slightly modified from the libpng package * */ +/* * the original was last optimized by Greg Roelofs & Mark Adler * */ +/* * * */ +/* ************************************************************************** */ + +#define MNG_COMPOSE8(RET,FG,ALPHA,BG) { \ + mng_uint16 iH = (mng_uint16)((mng_uint16)(FG) * (mng_uint16)(ALPHA) \ + + (mng_uint16)(BG)*(mng_uint16)(255 - \ + (mng_uint16)(ALPHA)) + (mng_uint16)128); \ + (RET) = (mng_uint8)((iH + (iH >> 8)) >> 8); } + +#define MNG_COMPOSE16(RET,FG,ALPHA,BG) { \ + mng_uint32 iH = (mng_uint32)((mng_uint32)(FG) * (mng_uint32)(ALPHA) \ + + (mng_uint32)(BG)*(mng_uint32)(65535L - \ + (mng_uint32)(ALPHA)) + (mng_uint32)32768L); \ + (RET) = (mng_uint16)((iH + (iH >> 16)) >> 16); } + +/* ************************************************************************** */ +/* * * */ +/* * Alpha blending macros * */ +/* * this code is based on Adam Costello's "Note on Compositing" from the * */ +/* * mng-list which gives the following formula: * */ +/* * * */ +/* * top pixel = (Rt, Gt, Bt, At) * */ +/* * bottom pixel = (Rb, Gb, Bb, Ab) * */ +/* * composite pixel = (Rc, Gc, Bc, Ac) * */ +/* * * */ +/* * all values in the range 0..1 * */ +/* * * */ +/* * Ac = 1 - (1 - At)(1 - Ab) * */ +/* * s = At / Ac * */ +/* * t = (1 - At) Ab / Ac * */ +/* * Rc = s Rt + t Rb * */ +/* * Gc = s Gt + t Gb * */ +/* * Bc = s Bt + t Bb * */ +/* * * */ +/* * (I just hope I coded it correctly in integer arithmetic...) * */ +/* * * */ +/* ************************************************************************** */ + +#define MNG_BLEND8(RT, GT, BT, AT, RB, GB, BB, AB, RC, GC, BC, AC) { \ + mng_uint32 S, T; \ + (AC) = (mng_uint8)((mng_uint32)255 - \ + ((((mng_uint32)255 - (mng_uint32)(AT)) * \ + ((mng_uint32)255 - (mng_uint32)(AB)) ) >> 8)); \ + S = (mng_uint32)(((mng_uint32)(AT) << 8) / \ + (mng_uint32)(AC)); \ + T = (mng_uint32)(((mng_uint32)255 - (mng_uint32)(AT)) * \ + (mng_uint32)(AB) / (mng_uint32)(AC)); \ + (RC) = (mng_uint8)((S * (mng_uint32)(RT) + \ + T * (mng_uint32)(RB) + (mng_uint32)127) >> 8); \ + (GC) = (mng_uint8)((S * (mng_uint32)(GT) + \ + T * (mng_uint32)(GB) + (mng_uint32)127) >> 8); \ + (BC) = (mng_uint8)((S * (mng_uint32)(BT) + \ + T * (mng_uint32)(BB) + (mng_uint32)127) >> 8); } + +#define MNG_BLEND16(RT, GT, BT, AT, RB, GB, BB, AB, RC, GC, BC, AC) { \ + mng_uint32 S, T; \ + (AC) = (mng_uint16)((mng_uint32)65535 - \ + ((((mng_uint32)65535 - (mng_uint32)(AT)) * \ + ((mng_uint32)65535 - (mng_uint32)(AB)) ) >> 16)); \ + S = (mng_uint32)(((mng_uint32)(AT) << 16) / \ + (mng_uint32)(AC)); \ + T = (mng_uint32)(((mng_uint32)65535 - (mng_uint32)(AT)) * \ + (mng_uint32)(AB) / (mng_uint32)(AC)); \ + (RC) = (mng_uint16)((S * (mng_uint32)(RT) + \ + T * (mng_uint32)(RB) + (mng_uint32)32767) >> 16); \ + (GC) = (mng_uint16)((S * (mng_uint32)(GT) + \ + T * (mng_uint32)(GB) + (mng_uint32)32767) >> 16); \ + (BC) = (mng_uint16)((S * (mng_uint32)(BT) + \ + T * (mng_uint32)(BB) + (mng_uint32)32767) >> 16); } + +/* ************************************************************************** */ + +/* note a good optimizing compiler will optimize this */ +#define DIV255B8(x) (mng_uint8)(((x) + 127) / 255) +#define DIV255B16(x) (mng_uint16)(((x) + 32767) / 65535) + +/* ************************************************************************** */ +/* * * */ +/* * Progressive display check - checks to see if progressive display is * */ +/* * in order & indicates so * */ +/* * * */ +/* * The routine is called after a call to one of the display_xxx routines * */ +/* * if appropriate * */ +/* * * */ +/* * The refresh is warrented in the read_chunk routine (mng_read.c) * */ +/* * and only during read&display processing, since there's not much point * */ +/* * doing it from memory! * */ +/* * * */ +/* ************************************************************************** */ + +mng_retcode mng_display_progressive_check (mng_datap pData) +{ + if ((pData->bDoProgressive) && /* need progressive display? */ + ((pData->eImagetype != mng_it_mng) || (pData->iDataheight > 300)) && + (pData->iDestb - pData->iDestt > 50) && (!pData->pCurraniobj)) + { + mng_int32 iC = pData->iRow + pData->iDestt - pData->iSourcet; + + if (iC % 20 == 0) /* every 20th line */ + pData->bNeedrefresh = MNG_TRUE; + + } + + return MNG_NOERROR; +} + +/* ************************************************************************** */ +/* * * */ +/* * Display routines - convert rowdata (which is already color-corrected) * */ +/* * to the output canvas, respecting the opacity information * */ +/* * * */ +/* ************************************************************************** */ + +MNG_LOCAL void check_update_region (mng_datap pData) +{ /* determine actual canvas row */ + mng_int32 iRow = pData->iRow + pData->iDestt - pData->iSourcet; + /* check for change in update-region */ + if ((pData->iDestl < (mng_int32)pData->iUpdateleft) || (pData->iUpdateright == 0)) + pData->iUpdateleft = pData->iDestl; + + if (pData->iDestr > (mng_int32)pData->iUpdateright) + pData->iUpdateright = pData->iDestr; + + if ((iRow < (mng_int32)pData->iUpdatetop) || (pData->iUpdatebottom == 0)) + pData->iUpdatetop = iRow; + + if (iRow+1 > (mng_int32)pData->iUpdatebottom) + pData->iUpdatebottom = iRow+1; + + return; +} + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCANVAS_RGB8 +#ifndef MNG_NO_16BIT_SUPPORT +#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE +mng_retcode mng_display_rgb8 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint16 iA16; + mng_uint16 iFGr16, iFGg16, iFGb16; + mng_uint16 iBGr16, iBGg16, iBGb16; + mng_uint8 iA8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGB8, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol * 3) + (pData->iDestl * 3); + pDataline = pData->pRGBArow; /* address source row */ + + if (pData->bIsRGBA16) /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3); + else + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *pScanline = *pDataline; + *(pScanline+1) = *(pDataline+2); + *(pScanline+2) = *(pDataline+4); + + pScanline += (pData->iColinc * 3); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *pScanline = *pDataline; + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *(pDataline+2); + + pScanline += (pData->iColinc * 3); + pDataline += 4; + } + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA16 = mng_get_uint16 (pDataline+6); + + if (iA16) /* any opacity at all ? */ + { + if (iA16 == 0xFFFF) /* fully opaque ? */ + { /* scale down by dropping the LSB */ + *pScanline = *pDataline; + *(pScanline+1) = *(pDataline+2); + *(pScanline+2) = *(pDataline+4); + } + else + { /* get the proper values */ + iFGr16 = mng_get_uint16 (pDataline ); + iFGg16 = mng_get_uint16 (pDataline+2); + iFGb16 = mng_get_uint16 (pDataline+4); + /* scale background up */ + iBGr16 = (mng_uint16)(*pScanline ); + iBGg16 = (mng_uint16)(*(pScanline+1)); + iBGb16 = (mng_uint16)(*(pScanline+2)); + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* now compose */ + MNG_COMPOSE16(iFGr16, iFGr16, iA16, iBGr16); + MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16); + MNG_COMPOSE16(iFGb16, iFGb16, iA16, iBGb16); + /* and return the composed values */ + *pScanline = (mng_uint8)(iFGr16 >> 8); + *(pScanline+1) = (mng_uint8)(iFGg16 >> 8); + *(pScanline+2) = (mng_uint8)(iFGb16 >> 8); + } + } + + pScanline += (pData->iColinc * 3); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA8 = *(pDataline+3); /* get alpha value */ + + if (iA8) /* any opacity at all ? */ + { + if (iA8 == 0xFF) /* fully opaque ? */ + { /* then simply copy the values */ + *pScanline = *pDataline; + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *(pDataline+2); + } + else + { /* do alpha composing */ + MNG_COMPOSE8 (*pScanline, *pDataline, iA8, *pScanline ); + MNG_COMPOSE8 (*(pScanline+1), *(pDataline+1), iA8, *(pScanline+1)); + MNG_COMPOSE8 (*(pScanline+2), *(pDataline+2), iA8, *(pScanline+2)); + } + } + + pScanline += (pData->iColinc * 3); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGB8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +mng_retcode mng_display_rgb8 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint16 iA16; + mng_uint16 iFGg16; + mng_uint16 iBGg16; + mng_uint8 iA8; + mng_uint8 iBps; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGB8, MNG_LC_START); +#endif + + iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1); + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol * 3) + (pData->iDestl * 3); + pDataline = pData->pRGBArow; /* address source row */ + + /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1)); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *pScanline = *pDataline; + *(pScanline+1) = *(pDataline+iBps); + *(pScanline+2) = *(pDataline+2*iBps); + + pScanline += (pData->iColinc * 3); + pDataline += 4*iBps; + } + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA16 = mng_get_uint16 (pDataline+6); + + if (iA16) /* any opacity at all ? */ + { + if (iA16 == 0xFFFF) /* fully opaque ? */ + { /* scale down by dropping the LSB */ + *pScanline = *pDataline; + *(pScanline+1) = *(pDataline+2); + *(pScanline+2) = *(pDataline+4); + } + else + { /* get the proper values */ + int i; + for (i=2; i >= 0; i--) + { + iFGg16 = mng_get_uint16 (pDataline+i+i); + /* scale background up */ + iBGg16 = (mng_uint16)(*(pScanline+i)); + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + /* now compose */ + MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16); + /* and return the composed values */ + *(pScanline+i) = (mng_uint8)(iFGg16 >> 8); + } + } + } + + pScanline += (pData->iColinc * 3); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA8 = *(pDataline+3); /* get alpha value */ + + if (iA8) /* any opacity at all ? */ + { + if (iA8 == 0xFF) /* fully opaque ? */ + { /* then simply copy the values */ + *pScanline = *pDataline; + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *(pDataline+2); + } + else + { /* do alpha composing */ + int i; + for (i=2; i >= 0; i--) + { + MNG_COMPOSE8 (*(pScanline+i), *(pDataline+i), iA8, *(pScanline+i)); + } + } + } + + pScanline += (pData->iColinc * 3); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGB8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +#else /* MNG_NO_16BIT_SUPPORT */ +mng_retcode mng_display_rgb8 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iA8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGB8, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol * 3) + (pData->iDestl * 3); + pDataline = pData->pRGBArow; /* address source row */ + + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *pScanline = *pDataline; + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *(pDataline+2); + + pScanline += (pData->iColinc * 3); + pDataline += 4; + } + } + } + else + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA8 = *(pDataline+3); /* get alpha value */ + + if (iA8) /* any opacity at all ? */ + { + if (iA8 == 0xFF) /* fully opaque ? */ + { /* then simply copy the values */ + *pScanline = *pDataline; + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *(pDataline+2); + } + else + { /* do alpha composing */ +#ifdef MNG_OPTIMIZE_FOOTPRINT_COMPOSE + int i; + for (i=2; i >= 0; i--) + { + MNG_COMPOSE8 (*(pScanline+i), *(pDataline+i), iA8, *(pScanline+i)); + } +#else + MNG_COMPOSE8 (*pScanline, *pDataline, iA8, *pScanline ); + MNG_COMPOSE8 (*(pScanline+1), *(pDataline+1), iA8, *(pScanline+1)); + MNG_COMPOSE8 (*(pScanline+2), *(pDataline+2), iA8, *(pScanline+2)); +#endif + } + } + + pScanline += (pData->iColinc * 3); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGB8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_16BIT_SUPPORT */ +#endif /* MNG_SKIPCANVAS_RGB8 */ + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCANVAS_RGBA8 +#ifndef MNG_NO_16BIT_SUPPORT +#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE +mng_retcode mng_display_rgba8 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iFGa8, iBGa8, iCa8; + mng_uint16 iFGa16, iBGa16, iCa16; + mng_uint16 iFGr16, iFGg16, iFGb16; + mng_uint16 iBGr16, iBGg16, iBGb16; + mng_uint16 iCr16, iCg16, iCb16; + mng_uint8 iCr8, iCg8, iCb8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA8, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2); + pDataline = pData->pRGBArow; /* address source row */ + + if (pData->bIsRGBA16) /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3); + else + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *pScanline = *pDataline; + *(pScanline+1) = *(pDataline+2); + *(pScanline+2) = *(pDataline+4); + *(pScanline+3) = *(pDataline+6); + + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *pScanline = *pDataline; + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *(pDataline+2); + *(pScanline+3) = *(pDataline+3); + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* get alpha values */ + iFGa16 = mng_get_uint16 (pDataline+6); + iBGa16 = (mng_uint16)(*(pScanline+3)); + iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16; + + if (iFGa16) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa16 == 0xFFFF) || (iBGa16 == 0)) + { /* plain copy it */ + *pScanline = *pDataline; + *(pScanline+1) = *(pDataline+2); + *(pScanline+2) = *(pDataline+4); + *(pScanline+3) = *(pDataline+6); + } + else + { + if (iBGa16 == 0xFFFF) /* background fully opaque ? */ + { /* get the proper values */ + iFGr16 = mng_get_uint16 (pDataline ); + iFGg16 = mng_get_uint16 (pDataline+2); + iFGb16 = mng_get_uint16 (pDataline+4); + /* scale background up */ + iBGr16 = (mng_uint16)(*pScanline ); + iBGg16 = (mng_uint16)(*(pScanline+1)); + iBGb16 = (mng_uint16)(*(pScanline+2)); + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* now compose */ + MNG_COMPOSE16(iFGr16, iFGr16, iFGa16, iBGr16); + MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16); + MNG_COMPOSE16(iFGb16, iFGb16, iFGa16, iBGb16); + /* and return the composed values */ + *pScanline = (mng_uint8)(iFGr16 >> 8); + *(pScanline+1) = (mng_uint8)(iFGg16 >> 8); + *(pScanline+2) = (mng_uint8)(iFGb16 >> 8); + /* alpha remains fully opaque !!! */ + } + else + { /* scale background up */ + iBGr16 = (mng_uint16)(*pScanline ); + iBGg16 = (mng_uint16)(*(pScanline+1)); + iBGb16 = (mng_uint16)(*(pScanline+2)); + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* let's blend */ + MNG_BLEND16 (mng_get_uint16 (pDataline ), + mng_get_uint16 (pDataline+2), + mng_get_uint16 (pDataline+4), iFGa16, + iBGr16, iBGg16, iBGb16, iBGa16, + iCr16, iCg16, iCb16, iCa16); + /* and return the composed values */ + *pScanline = (mng_uint8)(iCr16 >> 8); + *(pScanline+1) = (mng_uint8)(iCg16 >> 8); + *(pScanline+2) = (mng_uint8)(iCb16 >> 8); + *(pScanline+3) = (mng_uint8)(iCa16 >> 8); + } + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iFGa8 = *(pDataline+3); /* get alpha values */ + iBGa8 = *(pScanline+3); + + if (iFGa8) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa8 == 0xFF) || (iBGa8 == 0)) + { /* then simply copy the values */ + *pScanline = *pDataline; + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *(pDataline+2); + *(pScanline+3) = *(pDataline+3); + } + else + { + if (iBGa8 == 0xFF) /* background fully opaque ? */ + { /* do alpha composing */ + MNG_COMPOSE8 (*pScanline, *pDataline, iFGa8, *pScanline ); + MNG_COMPOSE8 (*(pScanline+1), *(pDataline+1), iFGa8, *(pScanline+1)); + MNG_COMPOSE8 (*(pScanline+2), *(pDataline+2), iFGa8, *(pScanline+2)); + /* alpha remains fully opaque !!! */ + } + else + { /* now blend */ + MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8, + *pScanline, *(pScanline+1), *(pScanline+2), iBGa8, + iCr8, iCg8, iCb8, iCa8); + /* and return the composed values */ + *pScanline = iCr8; + *(pScanline+1) = iCg8; + *(pScanline+2) = iCb8; + *(pScanline+3) = iCa8; + } + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +mng_retcode mng_display_rgba8 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iFGa8, iBGa8, iCa8; + mng_uint16 iFGa16, iBGa16, iCa16; + mng_uint16 iFGg16; + mng_uint16 iBGr16, iBGg16, iBGb16; + mng_uint16 iCr16, iCg16, iCb16; + mng_uint8 iCr8, iCg8, iCb8; + mng_uint8 iBps; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA8, MNG_LC_START); +#endif + + iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1); + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2); + pDataline = pData->pRGBArow; /* address source row */ + + /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1)); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *pScanline = *pDataline; + *(pScanline+1) = *(pDataline+iBps); + *(pScanline+2) = *(pDataline+2*iBps); + *(pScanline+3) = *(pDataline+3*iBps); + + pScanline += (pData->iColinc << 2); + pDataline += 4*iBps; + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* get alpha values */ + iFGa16 = mng_get_uint16 (pDataline+6); + iBGa16 = (mng_uint16)(*(pScanline+3)); + iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16; + + if (iFGa16) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa16 == 0xFFFF) || (iBGa16 == 0)) + { /* plain copy it */ + *pScanline = *pDataline; + *(pScanline+1) = *(pDataline+2); + *(pScanline+2) = *(pDataline+4); + *(pScanline+3) = *(pDataline+6); + } + else + { + if (iBGa16 == 0xFFFF) /* background fully opaque ? */ + { /* get the proper values */ + int i; + for (i=2; i >= 0; i--) + { + iFGg16 = mng_get_uint16 (pDataline+i+i); + /* scale background up */ + iBGg16 = (mng_uint16)(*(pScanline+i)); + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + /* now compose */ + MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16); + /* and return the composed values */ + *(pScanline+i) = (mng_uint8)(iFGg16 >> 8); + /* alpha remains fully opaque !!! */ + } + } + else + { /* scale background up */ + iBGr16 = (mng_uint16)(*pScanline ); + iBGg16 = (mng_uint16)(*(pScanline+1)); + iBGb16 = (mng_uint16)(*(pScanline+2)); + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* let's blend */ + MNG_BLEND16 (mng_get_uint16 (pDataline ), + mng_get_uint16 (pDataline+2), + mng_get_uint16 (pDataline+4), iFGa16, + iBGr16, iBGg16, iBGb16, iBGa16, + iCr16, iCg16, iCb16, iCa16); + /* and return the composed values */ + *pScanline = (mng_uint8)(iCr16 >> 8); + *(pScanline+1) = (mng_uint8)(iCg16 >> 8); + *(pScanline+2) = (mng_uint8)(iCb16 >> 8); + *(pScanline+3) = (mng_uint8)(iCa16 >> 8); + } + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iFGa8 = *(pDataline+3); /* get alpha values */ + iBGa8 = *(pScanline+3); + + if (iFGa8) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa8 == 0xFF) || (iBGa8 == 0)) + { /* then simply copy the values */ + *pScanline = *pDataline; + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *(pDataline+2); + *(pScanline+3) = *(pDataline+3); + } + else + { + if (iBGa8 == 0xFF) /* background fully opaque ? */ + { /* do alpha composing */ + int i; + for (i=2; i >= 0; i--) + { + MNG_COMPOSE8 (*(pScanline+i), *(pDataline+i), iFGa8, *(pScanline+i)); + } + /* alpha remains fully opaque !!! */ + } + else + { /* now blend */ + MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8, + *pScanline, *(pScanline+1), *(pScanline+2), iBGa8, + iCr8, iCg8, iCb8, iCa8); + /* and return the composed values */ + *pScanline = iCr8; + *(pScanline+1) = iCg8; + *(pScanline+2) = iCb8; + *(pScanline+3) = iCa8; + } + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +#else /* MNG_NO_16BIT_SUPPORT */ +mng_retcode mng_display_rgba8 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iFGa8, iBGa8, iCa8; + mng_uint8 iCr8, iCg8, iCb8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA8, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2); + pDataline = pData->pRGBArow; /* address source row */ + + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *pScanline = *pDataline; + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *(pDataline+2); + *(pScanline+3) = *(pDataline+3); + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + else + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iFGa8 = *(pDataline+3); /* get alpha values */ + iBGa8 = *(pScanline+3); + + if (iFGa8) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa8 == 0xFF) || (iBGa8 == 0)) + { /* then simply copy the values */ + *pScanline = *pDataline; + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *(pDataline+2); + *(pScanline+3) = *(pDataline+3); + } + else + { + if (iBGa8 == 0xFF) /* background fully opaque ? */ + { /* do alpha composing */ +#ifdef MNG_OPTIMIZE_FOOTPRINT_COMPOSE + int i; + for (i=2; i >= 0; i--) + { + MNG_COMPOSE8 (*(pScanline+i), *(pDataline+i), iFGa8, *(pScanline+i)); + } +#else + MNG_COMPOSE8 (*pScanline, *pDataline, iFGa8, *pScanline ); + MNG_COMPOSE8 (*(pScanline+1), *(pDataline+1), iFGa8, *(pScanline+1)); + MNG_COMPOSE8 (*(pScanline+2), *(pDataline+2), iFGa8, *(pScanline+2)); +#endif + /* alpha remains fully opaque !!! */ + } + else + { /* now blend */ + MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8, + *pScanline, *(pScanline+1), *(pScanline+2), iBGa8, + iCr8, iCg8, iCb8, iCa8); + /* and return the composed values */ + *pScanline = iCr8; + *(pScanline+1) = iCg8; + *(pScanline+2) = iCb8; + *(pScanline+3) = iCa8; + } + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_16BIT_SUPPORT */ +#endif /* MNG_SKIPCANVAS_RGBA8 */ + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCANVAS_RGBA8_PM +#ifndef MNG_NO_16BIT_SUPPORT +#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE +mng_retcode mng_display_rgba8_pm (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint32 s, t; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA8_PM, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2); + pDataline = pData->pRGBArow; /* address source row */ + + if (pData->bIsRGBA16) /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3); + else + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + if ((s = pDataline[6]) == 0) + *(mng_uint32*) pScanline = 0; /* set all components = 0 */ + else + { + if (s == 255) + { + pScanline[0] = pDataline[0]; + pScanline[1] = pDataline[2]; + pScanline[2] = pDataline[4]; + pScanline[3] = 255; + } + else + { +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + int i; + for (i=2; i >= 0; i--) + { + pScanline[2-i] = DIV255B8(s * pDataline[4-i-i]); + } +#else + pScanline[0] = DIV255B8(s * pDataline[0]); + pScanline[1] = DIV255B8(s * pDataline[2]); + pScanline[2] = DIV255B8(s * pDataline[4]); +#endif + pScanline[3] = (mng_uint8)s; + } + } + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values and premultiply */ + if ((s = pDataline[3]) == 0) + *(mng_uint32*) pScanline = 0; /* set all components = 0 */ + else + { + if (s == 255) + { +#ifdef MNG_BIGENDIAN_SUPPORTED + *(mng_uint32*)pScanline = (*(mng_uint32*)pDataline) | 0x000000FF; +#else + pScanline[0] = pDataline[0]; + pScanline[1] = pDataline[1]; + pScanline[2] = pDataline[2]; + pScanline[3] = 255; +#endif + } + else + { +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + int i; + for (i=2; i >= 0; i--) + { + pScanline[2-i] = DIV255B8(s * pDataline[2-i]); + } +#else + pScanline[0] = DIV255B8(s * pDataline[0]); + pScanline[1] = DIV255B8(s * pDataline[1]); + pScanline[2] = DIV255B8(s * pDataline[2]); +#endif + pScanline[3] = (mng_uint8)s; + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* get alpha values */ + if ((s = pDataline[6]) != 0) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if (s == 255) + { /* plain copy it */ + pScanline[0] = pDataline[0]; + pScanline[1] = pDataline[2]; + pScanline[2] = pDataline[4]; + pScanline[3] = 255; + } + else + { /* now blend (premultiplied) */ + t = 255 - s; +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[2-i] = DIV255B8(s * pDataline[4-i-i] + t * + pScanline[2-i]); + } + } +#else + pScanline[0] = DIV255B8(s * pDataline[0] + t * pScanline[0]); + pScanline[1] = DIV255B8(s * pDataline[2] + t * pScanline[1]); + pScanline[2] = DIV255B8(s * pDataline[4] + t * pScanline[2]); +#endif + pScanline[3] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[3]))); + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + if ((s = pDataline[3]) != 0) /* any opacity at all ? */ + { /* fully opaque ? */ + if (s == 255) + { /* then simply copy the values */ +#ifdef MNG_BIGENDIAN_SUPPORTED + *(mng_uint32*)pScanline = (*(mng_uint32*)pDataline) | 0x000000FF; +#else + pScanline[0] = pDataline[0]; + pScanline[1] = pDataline[1]; + pScanline[2] = pDataline[2]; + pScanline[3] = 255; +#endif + } + else + { /* now blend (premultiplied) */ + t = 255 - s; +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[2-i] = DIV255B8(s * pDataline[2-i] + t * + pScanline[2-i]); + } + } +#else + pScanline[0] = DIV255B8(s * pDataline[0] + t * pScanline[0]); + pScanline[1] = DIV255B8(s * pDataline[1] + t * pScanline[1]); + pScanline[2] = DIV255B8(s * pDataline[2] + t * pScanline[2]); +#endif + pScanline[3] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[3]))); + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA8_PM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +mng_retcode mng_display_rgba8_pm (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint32 s, t; + mng_uint8 iBps; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA8_PM, MNG_LC_START); +#endif + + iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1); + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2); + pDataline = pData->pRGBArow; /* address source row */ + + /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1)); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + if ((s = pDataline[6]) == 0) + *(mng_uint32*) pScanline = 0; /* set all components = 0 */ + else + { + if (s == 255) + { + pScanline[0] = pDataline[0]; + pScanline[1] = pDataline[2]; + pScanline[2] = pDataline[4]; + pScanline[3] = 255; + } + else + { +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + int i; + for (i=2; i >= 0; i--) + { + pScanline[2-i] = DIV255B8(s * pDataline[4-i-i]); + } +#else + pScanline[0] = DIV255B8(s * pDataline[0]); + pScanline[1] = DIV255B8(s * pDataline[2]); + pScanline[2] = DIV255B8(s * pDataline[4]); +#endif + pScanline[3] = (mng_uint8)s; + } + } + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values and premultiply */ + if ((s = pDataline[3]) == 0) + *(mng_uint32*) pScanline = 0; /* set all components = 0 */ + else + { + if (s == 255) + { +#ifdef MNG_BIGENDIAN_SUPPORTED + *(mng_uint32*)pScanline = (*(mng_uint32*)pDataline) | 0x000000FF; +#else + pScanline[0] = pDataline[0]; + pScanline[1] = pDataline[1]; + pScanline[2] = pDataline[2]; + pScanline[3] = 255; +#endif + } + else + { +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + int i; + for (i=2; i >= 0; i--) + { + pScanline[2-i] = DIV255B8(s * pDataline[2-i]); + } +#else + pScanline[0] = DIV255B8(s * pDataline[0]); + pScanline[1] = DIV255B8(s * pDataline[1]); + pScanline[2] = DIV255B8(s * pDataline[2]); +#endif + pScanline[3] = (mng_uint8)s; + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* get alpha values */ + if ((s = pDataline[6]) != 0) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if (s == 255) + { /* plain copy it */ + pScanline[0] = pDataline[0]; + pScanline[1] = pDataline[2]; + pScanline[2] = pDataline[4]; + pScanline[3] = 255; + } + else + { /* now blend (premultiplied) */ + t = 255 - s; +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[2-i] = DIV255B8(s * pDataline[4-i-i] + t * + pScanline[2-i]); + } + } +#else + pScanline[0] = DIV255B8(s * pDataline[0] + t * pScanline[0]); + pScanline[1] = DIV255B8(s * pDataline[2] + t * pScanline[1]); + pScanline[2] = DIV255B8(s * pDataline[4] + t * pScanline[2]); +#endif + pScanline[3] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[3]))); + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + if ((s = pDataline[3]) != 0) /* any opacity at all ? */ + { /* fully opaque ? */ + if (s == 255) + { /* then simply copy the values */ +#ifdef MNG_BIGENDIAN_SUPPORTED + *(mng_uint32*)pScanline = (*(mng_uint32*)pDataline) | 0x000000FF; +#else + pScanline[0] = pDataline[0]; + pScanline[1] = pDataline[1]; + pScanline[2] = pDataline[2]; + pScanline[3] = 255; +#endif + } + else + { /* now blend (premultiplied) */ + t = 255 - s; +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[2-i] = DIV255B8(s * pDataline[2-i] + t * + pScanline[2-i]); + } + } +#else + pScanline[0] = DIV255B8(s * pDataline[0] + t * pScanline[0]); + pScanline[1] = DIV255B8(s * pDataline[1] + t * pScanline[1]); + pScanline[2] = DIV255B8(s * pDataline[2] + t * pScanline[2]); +#endif + pScanline[3] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[3]))); + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA8_PM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +#else /* MNG_NO_16BIT_SUPPORT */ +mng_retcode mng_display_rgba8_pm (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint32 s, t; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA8_PM, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2); + pDataline = pData->pRGBArow; /* address source row */ + + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values and premultiply */ + if ((s = pDataline[3]) == 0) + *(mng_uint32*) pScanline = 0; /* set all components = 0 */ + else + { + if (s == 255) + { +#ifdef MNG_BIGENDIAN_SUPPORTED + *(mng_uint32*)pScanline = (*(mng_uint32*)pDataline) | 0x000000FF; +#else + pScanline[0] = pDataline[0]; + pScanline[1] = pDataline[1]; + pScanline[2] = pDataline[2]; + pScanline[3] = 255; +#endif + } + else + { +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + int i; + for (i=2; i >= 0; i--) + { + pScanline[2-i] = DIV255B8(s * pDataline[2-i]); + } +#else + pScanline[0] = DIV255B8(s * pDataline[0]); + pScanline[1] = DIV255B8(s * pDataline[1]); + pScanline[2] = DIV255B8(s * pDataline[2]); +#endif + pScanline[3] = (mng_uint8)s; + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + else + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + if ((s = pDataline[3]) != 0) /* any opacity at all ? */ + { /* fully opaque ? */ + if (s == 255) + { /* then simply copy the values */ +#ifdef MNG_BIGENDIAN_SUPPORTED + *(mng_uint32*)pScanline = (*(mng_uint32*)pDataline) | 0x000000FF; +#else + pScanline[0] = pDataline[0]; + pScanline[1] = pDataline[1]; + pScanline[2] = pDataline[2]; + pScanline[3] = 255; +#endif + } + else + { /* now blend (premultiplied) */ + t = 255 - s; +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[2-i] = DIV255B8(s * pDataline[2-i] + t * + pScanline[2-i]); + } + } +#else + pScanline[0] = DIV255B8(s * pDataline[0] + t * pScanline[0]); + pScanline[1] = DIV255B8(s * pDataline[1] + t * pScanline[1]); + pScanline[2] = DIV255B8(s * pDataline[2] + t * pScanline[2]); +#endif + pScanline[3] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[3]))); + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA8_PM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_16BIT_SUPPORT */ +#endif /* MNG_SKIPCANVAS_RGBA8_PM */ + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCANVAS_ARGB8 +#ifndef MNG_NO_16BIT_SUPPORT +#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE +mng_retcode mng_display_argb8 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iFGa8, iBGa8, iCa8; + mng_uint16 iFGa16, iBGa16, iCa16; + mng_uint16 iFGr16, iFGg16, iFGb16; + mng_uint16 iBGr16, iBGg16, iBGb16; + mng_uint16 iCr16, iCg16, iCb16; + mng_uint8 iCr8, iCg8, iCb8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_ARGB8, MNG_LC_START); +#endif + + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2); + pDataline = pData->pRGBArow; /* address source row */ + + if (pData->bIsRGBA16) /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3); + else + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *pScanline = *(pDataline+6); + *(pScanline+1) = *pDataline; + *(pScanline+2) = *(pDataline+2); + *(pScanline+3) = *(pDataline+4); + + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *pScanline = *(pDataline+3); + *(pScanline+1) = *pDataline; + *(pScanline+2) = *(pDataline+1); + *(pScanline+3) = *(pDataline+2); + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* get alpha values */ + iFGa16 = mng_get_uint16 (pDataline+6); + iBGa16 = (mng_uint16)(*pScanline); + iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16; + + if (iFGa16) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa16 == 0xFFFF) || (iBGa16 == 0)) + { /* plain copy it */ + *pScanline = *(pDataline+6); + *(pScanline+1) = *pDataline; + *(pScanline+2) = *(pDataline+2); + *(pScanline+3) = *(pDataline+4); + } + else + { + if (iBGa16 == 0xFFFF) /* background fully opaque ? */ + { /* get the proper values */ + iFGr16 = mng_get_uint16 (pDataline ); + iFGg16 = mng_get_uint16 (pDataline+2); + iFGb16 = mng_get_uint16 (pDataline+4); + /* scale background up */ + iBGr16 = (mng_uint16)(*(pScanline+1)); + iBGg16 = (mng_uint16)(*(pScanline+2)); + iBGb16 = (mng_uint16)(*(pScanline+3)); + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* now compose */ + MNG_COMPOSE16(iFGr16, iFGr16, iFGa16, iBGr16); + MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16); + MNG_COMPOSE16(iFGb16, iFGb16, iFGa16, iBGb16); + /* and return the composed values */ + /* alpha remains fully opaque !!! */ + *(pScanline+1) = (mng_uint8)(iFGr16 >> 8); + *(pScanline+2) = (mng_uint8)(iFGg16 >> 8); + *(pScanline+3) = (mng_uint8)(iFGb16 >> 8); + } + else + { /* scale background up */ + iBGr16 = (mng_uint16)(*(pScanline+1)); + iBGg16 = (mng_uint16)(*(pScanline+2)); + iBGb16 = (mng_uint16)(*(pScanline+3)); + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* let's blend */ + MNG_BLEND16 (mng_get_uint16 (pDataline ), + mng_get_uint16 (pDataline+2), + mng_get_uint16 (pDataline+4), iFGa16, + iBGr16, iBGg16, iBGb16, iBGa16, + iCr16, iCg16, iCb16, iCa16); + /* and return the composed values */ + *pScanline = (mng_uint8)(iCa16 >> 8); + *(pScanline+1) = (mng_uint8)(iCr16 >> 8); + *(pScanline+2) = (mng_uint8)(iCg16 >> 8); + *(pScanline+3) = (mng_uint8)(iCb16 >> 8); + } + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iFGa8 = *(pDataline+3); /* get alpha values */ + iBGa8 = *pScanline; + + if (iFGa8) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa8 == 0xFF) || (iBGa8 == 0)) + { /* then simply copy the values */ + *pScanline = *(pDataline+3); + *(pScanline+1) = *pDataline; + *(pScanline+2) = *(pDataline+1); + *(pScanline+3) = *(pDataline+2); + } + else + { + if (iBGa8 == 0xFF) /* background fully opaque ? */ + { /* do simple alpha composing */ + /* alpha itself remains fully opaque !!! */ + } + else + { /* now blend */ + MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8, + *(pScanline+1), *(pScanline+2), *(pScanline+3), iBGa8, + iCr8, iCg8, iCb8, iCa8); + /* and return the composed values */ + *pScanline = iCa8; + *(pScanline+1) = iCr8; + *(pScanline+2) = iCg8; + *(pScanline+3) = iCb8; + } + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_ARGB8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +mng_retcode mng_display_argb8 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iFGa8, iBGa8, iCa8; + mng_uint16 iFGa16, iBGa16, iCa16; + mng_uint16 iFGg16; + mng_uint16 iBGr16, iBGg16, iBGb16; + mng_uint16 iCr16, iCg16, iCb16; + mng_uint8 iCr8, iCg8, iCb8; + mng_uint8 iBps; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_ARGB8, MNG_LC_START); +#endif + + iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1); + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2); + pDataline = pData->pRGBArow; /* address source row */ + + /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1)); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *pScanline = *(pDataline+3*iBps); + *(pScanline+1) = *pDataline; + *(pScanline+2) = *(pDataline+iBps); + *(pScanline+3) = *(pDataline+2*iBps); + + pScanline += (pData->iColinc << 2); + pDataline += 4*iBps; + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* get alpha values */ + iFGa16 = mng_get_uint16 (pDataline+6); + iBGa16 = (mng_uint16)(*pScanline); + iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16; + + if (iFGa16) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa16 == 0xFFFF) || (iBGa16 == 0)) + { /* plain copy it */ + *pScanline = *(pDataline+6); + *(pScanline+1) = *pDataline; + *(pScanline+2) = *(pDataline+2); + *(pScanline+3) = *(pDataline+4); + } + else + { + if (iBGa16 == 0xFFFF) /* background fully opaque ? */ + { /* get the proper values */ + int i; + for (i=2; i >= 0; i--) + { + iFGg16 = mng_get_uint16 (pDataline+i+i); + /* scale background up */ + iBGg16 = (mng_uint16)(*(pScanline+i+1)); + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + /* now compose */ + MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16); + /* and return the composed values */ + /* alpha remains fully opaque !!! */ + *(pScanline+i+1) = (mng_uint8)(iFGg16 >> 8); + } + } + else + { /* scale background up */ + iBGr16 = (mng_uint16)(*(pScanline+1)); + iBGg16 = (mng_uint16)(*(pScanline+2)); + iBGb16 = (mng_uint16)(*(pScanline+3)); + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* let's blend */ + MNG_BLEND16 (mng_get_uint16 (pDataline ), + mng_get_uint16 (pDataline+2), + mng_get_uint16 (pDataline+4), iFGa16, + iBGr16, iBGg16, iBGb16, iBGa16, + iCr16, iCg16, iCb16, iCa16); + /* and return the composed values */ + *pScanline = (mng_uint8)(iCa16 >> 8); + *(pScanline+1) = (mng_uint8)(iCr16 >> 8); + *(pScanline+2) = (mng_uint8)(iCg16 >> 8); + *(pScanline+3) = (mng_uint8)(iCb16 >> 8); + } + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iFGa8 = *(pDataline+3); /* get alpha values */ + iBGa8 = *pScanline; + + if (iFGa8) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa8 == 0xFF) || (iBGa8 == 0)) + { /* then simply copy the values */ + *pScanline = *(pDataline+3); + *(pScanline+1) = *pDataline; + *(pScanline+2) = *(pDataline+1); + *(pScanline+3) = *(pDataline+2); + } + else + { + if (iBGa8 == 0xFF) /* background fully opaque ? */ + { /* do simple alpha composing */ + /* alpha itself remains fully opaque !!! */ + int i; + for (i=2; i >= 0; i--) + { + MNG_COMPOSE8 (*(pScanline+i+1), *(pDataline+i), iFGa8, *(pScanline+i+1)); + } + } + else + { /* now blend */ + MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8, + *(pScanline+1), *(pScanline+2), *(pScanline+3), iBGa8, + iCr8, iCg8, iCb8, iCa8); + /* and return the composed values */ + *pScanline = iCa8; + *(pScanline+1) = iCr8; + *(pScanline+2) = iCg8; + *(pScanline+3) = iCb8; + } + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_ARGB8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +#else /* MNG_NO_16BIT_SUPPORT */ +mng_retcode mng_display_argb8 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iFGa8, iBGa8, iCa8; + mng_uint8 iCr8, iCg8, iCb8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_ARGB8, MNG_LC_START); +#endif + + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2); + pDataline = pData->pRGBArow; /* address source row */ + + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *pScanline = *(pDataline+3); + *(pScanline+1) = *pDataline; + *(pScanline+2) = *(pDataline+1); + *(pScanline+3) = *(pDataline+2); + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + else + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iFGa8 = *(pDataline+3); /* get alpha values */ + iBGa8 = *pScanline; + + if (iFGa8) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa8 == 0xFF) || (iBGa8 == 0)) + { /* then simply copy the values */ + *pScanline = *(pDataline+3); + *(pScanline+1) = *pDataline; + *(pScanline+2) = *(pDataline+1); + *(pScanline+3) = *(pDataline+2); + } + else + { + if (iBGa8 == 0xFF) /* background fully opaque ? */ + { /* do simple alpha composing */ + /* alpha itself remains fully opaque !!! */ +#ifdef MNG_OPTIMIZE_FOOTPRINT_COMPOSE + int i; + for (i=2; i >= 0; i--) + { + MNG_COMPOSE8 (*(pScanline+i+1), *(pDataline+i), iFGa8, *(pScanline+i+1)); + } +#else + MNG_COMPOSE8 (*(pScanline+1), *pDataline, iFGa8, *(pScanline+1)); + MNG_COMPOSE8 (*(pScanline+2), *(pDataline+1), iFGa8, *(pScanline+2)); + MNG_COMPOSE8 (*(pScanline+3), *(pDataline+2), iFGa8, *(pScanline+3)); +#endif + } + else + { /* now blend */ + MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8, + *(pScanline+1), *(pScanline+2), *(pScanline+3), iBGa8, + iCr8, iCg8, iCb8, iCa8); + /* and return the composed values */ + *pScanline = iCa8; + *(pScanline+1) = iCr8; + *(pScanline+2) = iCg8; + *(pScanline+3) = iCb8; + } + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_ARGB8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_16BIT_SUPPORT */ +#endif /* MNG_SKIPCANVAS_ARGB8 */ + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCANVAS_ARGB8_PM +#ifndef MNG_NO_16BIT_SUPPORT +#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE +mng_retcode mng_display_argb8_pm (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint32 s, t; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_ARGB8_PM, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2); + pDataline = pData->pRGBArow; /* address source row */ + + if (pData->bIsRGBA16) /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3); + else + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + if ((s = pDataline[6]) == 0) + *(mng_uint32*) pScanline = 0; /* set all components = 0 */ + else + { + if (s == 255) + { + pScanline[0] = 255; + pScanline[1] = pDataline[0]; + pScanline[2] = pDataline[2]; + pScanline[3] = pDataline[4]; + } + else + { + pScanline[0] = (mng_uint8)s; +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[3-i] = DIV255B8(s * pDataline[4-i-i]); + } + } +#else + pScanline[1] = DIV255B8(s * pDataline[0]); + pScanline[2] = DIV255B8(s * pDataline[2]); + pScanline[3] = DIV255B8(s * pDataline[4]); +#endif + } + } + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values and premultiply */ + if ((s = pDataline[3]) == 0) + *(mng_uint32*) pScanline = 0; /* set all components = 0 */ + else + { + if (s == 255) + { + pScanline[0] = 255; + pScanline[1] = pDataline[0]; + pScanline[2] = pDataline[1]; + pScanline[3] = pDataline[2]; + } + else + { + pScanline[0] = (mng_uint8)s; +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[3-i] = DIV255B8(s * pDataline[2-i]); + } + } +#else + pScanline[1] = DIV255B8(s * pDataline[0]); + pScanline[2] = DIV255B8(s * pDataline[1]); + pScanline[3] = DIV255B8(s * pDataline[2]); +#endif + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* get alpha values */ + if ((s = pDataline[6]) != 0) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if (s == 255) + { /* plain copy it */ + pScanline[0] = 255; + pScanline[1] = pDataline[0]; + pScanline[2] = pDataline[2]; + pScanline[3] = pDataline[4]; + } + else + { /* now blend (premultiplied) */ + t = 255 - s; + pScanline[0] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[0]))); +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[3-i] = DIV255B8(s * pDataline[4-i-i] + t * + pScanline[3-i]); + } + } +#else + pScanline[1] = DIV255B8(s * pDataline[0] + t * pScanline[1]); + pScanline[2] = DIV255B8(s * pDataline[2] + t * pScanline[2]); + pScanline[3] = DIV255B8(s * pDataline[4] + t * pScanline[3]); +#endif + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + if ((s = pDataline[3]) != 0) /* any opacity at all ? */ + { /* fully opaque ? */ + if (s == 255) + { /* then simply copy the values */ + pScanline[0] = 255; + pScanline[1] = pDataline[0]; + pScanline[2] = pDataline[1]; + pScanline[3] = pDataline[2]; + } + else + { /* now blend (premultiplied) */ + t = 255 - s; + pScanline[0] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[0]))); +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[3-i] = DIV255B8(s * pDataline[2-i] + t * + pScanline[3-i]); + } + } +#else + pScanline[1] = DIV255B8(s * pDataline[0] + t * pScanline[1]); + pScanline[2] = DIV255B8(s * pDataline[1] + t * pScanline[2]); + pScanline[3] = DIV255B8(s * pDataline[2] + t * pScanline[3]); +#endif + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_ARGB8_PM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +mng_retcode mng_display_argb8_pm (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint32 s, t; + mng_uint8 iBps; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_ARGB8_PM, MNG_LC_START); +#endif + + iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1); + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2); + pDataline = pData->pRGBArow; /* address source row */ + + /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1)); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + if ((s = pDataline[6]) == 0) + *(mng_uint32*) pScanline = 0; /* set all components = 0 */ + else + { + if (s == 255) + { + pScanline[0] = 255; + pScanline[1] = pDataline[0]; + pScanline[2] = pDataline[2]; + pScanline[3] = pDataline[4]; + } + else + { + pScanline[0] = (mng_uint8)s; +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[3-i] = DIV255B8(s * pDataline[4-i-i]); + } + } +#else + pScanline[1] = DIV255B8(s * pDataline[0]); + pScanline[2] = DIV255B8(s * pDataline[2]); + pScanline[3] = DIV255B8(s * pDataline[4]); +#endif + } + } + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values and premultiply */ + if ((s = pDataline[3]) == 0) + *(mng_uint32*) pScanline = 0; /* set all components = 0 */ + else + { + if (s == 255) + { + pScanline[0] = 255; + pScanline[1] = pDataline[0]; + pScanline[2] = pDataline[1]; + pScanline[3] = pDataline[2]; + } + else + { + pScanline[0] = (mng_uint8)s; +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[3-i] = DIV255B8(s * pDataline[2-i]); + } + } +#else + pScanline[1] = DIV255B8(s * pDataline[0]); + pScanline[2] = DIV255B8(s * pDataline[1]); + pScanline[3] = DIV255B8(s * pDataline[2]); +#endif + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* get alpha values */ + if ((s = pDataline[6]) != 0) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if (s == 255) + { /* plain copy it */ + pScanline[0] = 255; + pScanline[1] = pDataline[0]; + pScanline[2] = pDataline[2]; + pScanline[3] = pDataline[4]; + } + else + { /* now blend (premultiplied) */ + t = 255 - s; + pScanline[0] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[0]))); +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[3-i] = DIV255B8(s * pDataline[4-i-i] + t * + pScanline[3-i]); + } + } +#else + pScanline[1] = DIV255B8(s * pDataline[0] + t * pScanline[1]); + pScanline[2] = DIV255B8(s * pDataline[2] + t * pScanline[2]); + pScanline[3] = DIV255B8(s * pDataline[4] + t * pScanline[3]); +#endif + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + if ((s = pDataline[3]) != 0) /* any opacity at all ? */ + { /* fully opaque ? */ + if (s == 255) + { /* then simply copy the values */ + pScanline[0] = 255; + pScanline[1] = pDataline[0]; + pScanline[2] = pDataline[1]; + pScanline[3] = pDataline[2]; + } + else + { /* now blend (premultiplied) */ + t = 255 - s; + pScanline[0] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[0]))); +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[3-i] = DIV255B8(s * pDataline[2-i] + t * + pScanline[3-i]); + } + } +#else + pScanline[1] = DIV255B8(s * pDataline[0] + t * pScanline[1]); + pScanline[2] = DIV255B8(s * pDataline[1] + t * pScanline[2]); + pScanline[3] = DIV255B8(s * pDataline[2] + t * pScanline[3]); +#endif + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_ARGB8_PM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +#else /* MNG_NO_16BIT_SUPPORT */ +mng_retcode mng_display_argb8_pm (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint32 s, t; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_ARGB8_PM, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2); + pDataline = pData->pRGBArow; /* address source row */ + + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values and premultiply */ + if ((s = pDataline[3]) == 0) + *(mng_uint32*) pScanline = 0; /* set all components = 0 */ + else + { + if (s == 255) + { + pScanline[0] = 255; + pScanline[1] = pDataline[0]; + pScanline[2] = pDataline[1]; + pScanline[3] = pDataline[2]; + } + else + { + pScanline[0] = (mng_uint8)s; +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[3-i] = DIV255B8(s * pDataline[2-i]); + } + } +#else + pScanline[1] = DIV255B8(s * pDataline[0]); + pScanline[2] = DIV255B8(s * pDataline[1]); + pScanline[3] = DIV255B8(s * pDataline[2]); +#endif + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + else + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + if ((s = pDataline[3]) != 0) /* any opacity at all ? */ + { /* fully opaque ? */ + if (s == 255) + { /* then simply copy the values */ + pScanline[0] = 255; + pScanline[1] = pDataline[0]; + pScanline[2] = pDataline[1]; + pScanline[3] = pDataline[2]; + } + else + { /* now blend (premultiplied) */ + t = 255 - s; + pScanline[0] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[0]))); +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[3-i] = DIV255B8(s * pDataline[2-i] + t * + pScanline[3-i]); + } + } +#else + pScanline[1] = DIV255B8(s * pDataline[0] + t * pScanline[1]); + pScanline[2] = DIV255B8(s * pDataline[1] + t * pScanline[2]); + pScanline[3] = DIV255B8(s * pDataline[2] + t * pScanline[3]); +#endif + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_ARGB8_PM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_16BIT_SUPPORT */ +#endif /* MNG_SKIPCANVAS_ARGB8_PM */ + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCANVAS_RGB8_A8 +#ifndef MNG_NO_16BIT_SUPPORT +#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE +mng_retcode mng_display_rgb8_a8 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pAlphaline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iFGa8, iBGa8, iCa8; + mng_uint16 iFGa16, iBGa16, iCa16; + mng_uint16 iFGr16, iFGg16, iFGb16; + mng_uint16 iBGr16, iBGg16, iBGb16; + mng_uint16 iCr16, iCg16, iCb16; + mng_uint8 iCr8, iCg8, iCb8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGB8_A8, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination rows */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + pAlphaline = (mng_uint8p)pData->fGetalphaline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination rows starting-point */ + pScanline = pScanline + (pData->iCol * 3) + (pData->iDestl * 3); + pAlphaline = pAlphaline + pData->iCol + pData->iDestl; + + pDataline = pData->pRGBArow; /* address source row */ + + if (pData->bIsRGBA16) /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3); + else + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *pScanline = *pDataline; + *(pScanline+1) = *(pDataline+2); + *(pScanline+2) = *(pDataline+4); + *pAlphaline = *(pDataline+6); + + pScanline += (pData->iColinc * 3); + pAlphaline += pData->iColinc; + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *pScanline = *pDataline; + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *(pDataline+2); + *pAlphaline = *(pDataline+3); + + pScanline += (pData->iColinc * 3); + pAlphaline += pData->iColinc; + pDataline += 4; + } + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* get alpha values */ + iFGa16 = mng_get_uint16 (pDataline+6); + iBGa16 = (mng_uint16)(*pAlphaline); + iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16; + + if (iFGa16) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa16 == 0xFFFF) || (iBGa16 == 0)) + { /* plain copy it */ + *pScanline = *pDataline; + *(pScanline+1) = *(pDataline+2); + *(pScanline+2) = *(pDataline+4); + *pAlphaline = *(pDataline+6); + } + else + { + if (iBGa16 == 0xFFFF) /* background fully opaque ? */ + { /* get the proper values */ + iFGr16 = mng_get_uint16 (pDataline ); + iFGg16 = mng_get_uint16 (pDataline+2); + iFGb16 = mng_get_uint16 (pDataline+4); + /* scale background up */ + iBGr16 = (mng_uint16)(*pScanline ); + iBGg16 = (mng_uint16)(*(pScanline+1)); + iBGb16 = (mng_uint16)(*(pScanline+2)); + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* now compose */ + MNG_COMPOSE16(iFGr16, iFGr16, iFGa16, iBGr16); + MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16); + MNG_COMPOSE16(iFGb16, iFGb16, iFGa16, iBGb16); + /* and return the composed values */ + *pScanline = (mng_uint8)(iFGr16 >> 8); + *(pScanline+1) = (mng_uint8)(iFGg16 >> 8); + *(pScanline+2) = (mng_uint8)(iFGb16 >> 8); + /* alpha remains fully opaque !!! */ + } + else + { /* scale background up */ + iBGr16 = (mng_uint16)(*pScanline ); + iBGg16 = (mng_uint16)(*(pScanline+1)); + iBGb16 = (mng_uint16)(*(pScanline+2)); + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* let's blend */ + MNG_BLEND16 (mng_get_uint16 (pDataline ), + mng_get_uint16 (pDataline+2), + mng_get_uint16 (pDataline+4), iFGa16, + iBGr16, iBGg16, iBGb16, iBGa16, + iCr16, iCg16, iCb16, iCa16); + /* and return the composed values */ + *pScanline = (mng_uint8)(iCr16 >> 8); + *(pScanline+1) = (mng_uint8)(iCg16 >> 8); + *(pScanline+2) = (mng_uint8)(iCb16 >> 8); + *pAlphaline = (mng_uint8)(iCa16 >> 8); + } + } + } + + pScanline += (pData->iColinc * 3); + pAlphaline += pData->iColinc; + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iFGa8 = *(pDataline+3); /* get alpha values */ + iBGa8 = *pAlphaline; + + if (iFGa8) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa8 == 0xFF) || (iBGa8 == 0)) + { /* then simply copy the values */ + *pScanline = *pDataline; + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *(pDataline+2); + *pAlphaline = *(pDataline+3); + } + else + { + if (iBGa8 == 0xFF) /* background fully opaque ? */ + { /* do alpha composing */ + MNG_COMPOSE8 (*pScanline, *pDataline, iFGa8, *pScanline ); + MNG_COMPOSE8 (*(pScanline+1), *(pDataline+1), iFGa8, *(pScanline+1)); + MNG_COMPOSE8 (*(pScanline+2), *(pDataline+2), iFGa8, *(pScanline+2)); + /* alpha remains fully opaque !!! */ + } + else + { /* now blend */ + MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8, + *pScanline, *(pScanline+1), *(pScanline+2), iBGa8, + iCr8, iCg8, iCb8, iCa8); + /* and return the composed values */ + *pScanline = iCr8; + *(pScanline+1) = iCg8; + *(pScanline+2) = iCb8; + *pAlphaline = iCa8; + } + } + } + + pScanline += (pData->iColinc * 3); + pAlphaline += pData->iColinc; + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGB8_A8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +mng_retcode mng_display_rgb8_a8 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pAlphaline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iFGa8, iBGa8, iCa8; + mng_uint16 iFGa16, iBGa16, iCa16; + mng_uint16 iFGg16; + mng_uint16 iBGr16, iBGg16, iBGb16; + mng_uint16 iCr16, iCg16, iCb16; + mng_uint8 iCr8, iCg8, iCb8; + mng_uint8 iBps; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGB8_A8, MNG_LC_START); +#endif + + iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1); + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination rows */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + pAlphaline = (mng_uint8p)pData->fGetalphaline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination rows starting-point */ + pScanline = pScanline + (pData->iCol * 3) + (pData->iDestl * 3); + pAlphaline = pAlphaline + pData->iCol + pData->iDestl; + + pDataline = pData->pRGBArow; /* address source row */ + + /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1)); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *pScanline = *pDataline; + *(pScanline+1) = *(pDataline+iBps); + *(pScanline+2) = *(pDataline+2*iBps); + *pAlphaline = *(pDataline+3*iBps); + + pScanline += (pData->iColinc * 3); + pAlphaline += pData->iColinc; + pDataline += 4*iBps; + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* get alpha values */ + iFGa16 = mng_get_uint16 (pDataline+6); + iBGa16 = (mng_uint16)(*pAlphaline); + iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16; + + if (iFGa16) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa16 == 0xFFFF) || (iBGa16 == 0)) + { /* plain copy it */ + *pScanline = *pDataline; + *(pScanline+1) = *(pDataline+2); + *(pScanline+2) = *(pDataline+4); + *pAlphaline = *(pDataline+6); + } + else + { + if (iBGa16 == 0xFFFF) /* background fully opaque ? */ + { /* get the proper values */ + int i; + for (i=2; i >= 0; i--) + { + iFGg16 = mng_get_uint16 (pDataline+i+i); + /* scale background up */ + iBGg16 = (mng_uint16)(*(pScanline+i)); + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + /* now compose */ + MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16); + /* and return the composed values */ + *(pScanline+i) = (mng_uint8)(iFGg16 >> 8); + /* alpha remains fully opaque !!! */ + } + } + else + { /* scale background up */ + iBGr16 = (mng_uint16)(*pScanline ); + iBGg16 = (mng_uint16)(*(pScanline+1)); + iBGb16 = (mng_uint16)(*(pScanline+2)); + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* let's blend */ + MNG_BLEND16 (mng_get_uint16 (pDataline ), + mng_get_uint16 (pDataline+2), + mng_get_uint16 (pDataline+4), iFGa16, + iBGr16, iBGg16, iBGb16, iBGa16, + iCr16, iCg16, iCb16, iCa16); + /* and return the composed values */ + *pScanline = (mng_uint8)(iCr16 >> 8); + *(pScanline+1) = (mng_uint8)(iCg16 >> 8); + *(pScanline+2) = (mng_uint8)(iCb16 >> 8); + *pAlphaline = (mng_uint8)(iCa16 >> 8); + } + } + } + + pScanline += (pData->iColinc * 3); + pAlphaline += pData->iColinc; + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iFGa8 = *(pDataline+3); /* get alpha values */ + iBGa8 = *pAlphaline; + + if (iFGa8) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa8 == 0xFF) || (iBGa8 == 0)) + { /* then simply copy the values */ + *pScanline = *pDataline; + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *(pDataline+2); + *pAlphaline = *(pDataline+3); + } + else + { + if (iBGa8 == 0xFF) /* background fully opaque ? */ + { /* do alpha composing */ + int i; + for (i=2; i >= 0; i--) + { + MNG_COMPOSE8 (*(pScanline+i), *(pDataline+i), iFGa8, *(pScanline+i)); + } + /* alpha remains fully opaque !!! */ + } + else + { /* now blend */ + MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8, + *pScanline, *(pScanline+1), *(pScanline+2), iBGa8, + iCr8, iCg8, iCb8, iCa8); + /* and return the composed values */ + *pScanline = iCr8; + *(pScanline+1) = iCg8; + *(pScanline+2) = iCb8; + *pAlphaline = iCa8; + } + } + } + + pScanline += (pData->iColinc * 3); + pAlphaline += pData->iColinc; + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGB8_A8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +#else /* MNG_NO_16BIT_SUPPORT */ +mng_retcode mng_display_rgb8_a8 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pAlphaline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iFGa8, iBGa8, iCa8; + mng_uint8 iCr8, iCg8, iCb8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGB8_A8, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination rows */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + pAlphaline = (mng_uint8p)pData->fGetalphaline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination rows starting-point */ + pScanline = pScanline + (pData->iCol * 3) + (pData->iDestl * 3); + pAlphaline = pAlphaline + pData->iCol + pData->iDestl; + + pDataline = pData->pRGBArow; /* address source row */ + + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *pScanline = *pDataline; + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *(pDataline+2); + *pAlphaline = *(pDataline+3); + + pScanline += (pData->iColinc * 3); + pAlphaline += pData->iColinc; + pDataline += 4; + } + } + } + else + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iFGa8 = *(pDataline+3); /* get alpha values */ + iBGa8 = *pAlphaline; + + if (iFGa8) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa8 == 0xFF) || (iBGa8 == 0)) + { /* then simply copy the values */ + *pScanline = *pDataline; + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *(pDataline+2); + *pAlphaline = *(pDataline+3); + } + else + { + if (iBGa8 == 0xFF) /* background fully opaque ? */ + { /* do alpha composing */ +#ifdef MNG_OPTIMIZE_FOOTPRINT_COMPOSE + int i; + for (i=2; i >= 0; i--) + { + MNG_COMPOSE8 (*(pScanline+i), *(pDataline+i), iFGa8, *(pScanline+i)); + } +#else + MNG_COMPOSE8 (*pScanline, *pDataline, iFGa8, *pScanline ); + MNG_COMPOSE8 (*(pScanline+1), *(pDataline+1), iFGa8, *(pScanline+1)); + MNG_COMPOSE8 (*(pScanline+2), *(pDataline+2), iFGa8, *(pScanline+2)); +#endif + /* alpha remains fully opaque !!! */ + } + else + { /* now blend */ + MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8, + *pScanline, *(pScanline+1), *(pScanline+2), iBGa8, + iCr8, iCg8, iCb8, iCa8); + /* and return the composed values */ + *pScanline = iCr8; + *(pScanline+1) = iCg8; + *(pScanline+2) = iCb8; + *pAlphaline = iCa8; + } + } + } + + pScanline += (pData->iColinc * 3); + pAlphaline += pData->iColinc; + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGB8_A8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_16BIT_SUPPORT */ +#endif /* MNG_SKIPCANVAS_RGB8_A8 */ + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCANVAS_BGR8 +#ifndef MNG_NO_16BIT_SUPPORT +#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE +mng_retcode mng_display_bgr8 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint16 iA16; + mng_uint16 iFGr16, iFGg16, iFGb16; + mng_uint16 iBGr16, iBGg16, iBGb16; + mng_uint8 iA8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGR8, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol * 3) + (pData->iDestl * 3); + pDataline = pData->pRGBArow; /* address source row */ + + if (pData->bIsRGBA16) /* adjust source row starting-point */ + pDataline = pDataline + (pData->iSourcel / pData->iColinc) * 8; + else + pDataline = pDataline + (pData->iSourcel / pData->iColinc) * 4; + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *pScanline = *(pDataline+4); + *(pScanline+1) = *(pDataline+2); + *(pScanline+2) = *pDataline; + + pScanline += (pData->iColinc * 3); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *pScanline = *(pDataline+2); + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *pDataline; + + pScanline += (pData->iColinc * 3); + pDataline += 4; + } + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* get alpha value */ + iA16 = mng_get_uint16 (pDataline+6); + + if (iA16) /* any opacity at all ? */ + { + if (iA16 == 0xFFFF) /* fully opaque ? */ + { /* scale down by dropping the LSB */ + *pScanline = *(pDataline+4); + *(pScanline+1) = *(pDataline+2); + *(pScanline+2) = *pDataline; + } + else + { /* get the proper values */ + iFGr16 = mng_get_uint16 (pDataline ); + iFGg16 = mng_get_uint16 (pDataline+2); + iFGb16 = mng_get_uint16 (pDataline+4); + /* scale background up */ + iBGr16 = (mng_uint16)(*(pScanline+2)); + iBGg16 = (mng_uint16)(*(pScanline+1)); + iBGb16 = (mng_uint16)(*pScanline ); + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* now compose */ + MNG_COMPOSE16(iFGr16, iFGr16, iA16, iBGr16); + MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16); + MNG_COMPOSE16(iFGb16, iFGb16, iA16, iBGb16); + /* and return the composed values */ + *pScanline = (mng_uint8)(iFGb16 >> 8); + *(pScanline+1) = (mng_uint8)(iFGg16 >> 8); + *(pScanline+2) = (mng_uint8)(iFGr16 >> 8); + } + } + + pScanline += (pData->iColinc * 3); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA8 = *(pDataline+3); /* get alpha value */ + + if (iA8) /* any opacity at all ? */ + { + if (iA8 == 0xFF) /* fully opaque ? */ + { /* then simply copy the values */ + *pScanline = *(pDataline+2); + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *pDataline; + } + else + { /* do alpha composing */ + MNG_COMPOSE8 (*pScanline, *(pDataline+2), iA8, *pScanline ); + MNG_COMPOSE8 (*(pScanline+1), *(pDataline+1), iA8, *(pScanline+1)); + MNG_COMPOSE8 (*(pScanline+2), *pDataline, iA8, *(pScanline+2)); + } + } + + pScanline += (pData->iColinc * 3); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGR8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +mng_retcode mng_display_bgr8 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint16 iA16; + mng_uint16 iFGg16; + mng_uint16 iBGg16; + mng_uint8 iA8; + mng_uint8 iBps; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGR8, MNG_LC_START); +#endif + + iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1); + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol * 3) + (pData->iDestl * 3); + pDataline = pData->pRGBArow; /* address source row */ + + /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1)); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *pScanline = *(pDataline+2*iBps); + *(pScanline+1) = *(pDataline+iBps); + *(pScanline+2) = *pDataline; + + pScanline += (pData->iColinc * 3); + pDataline += 4*iBps; + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* get alpha value */ + iA16 = mng_get_uint16 (pDataline+6); + + if (iA16) /* any opacity at all ? */ + { + if (iA16 == 0xFFFF) /* fully opaque ? */ + { /* scale down by dropping the LSB */ + *pScanline = *(pDataline+4); + *(pScanline+1) = *(pDataline+2); + *(pScanline+2) = *pDataline; + } + else + { /* get the proper values */ + int i; + for (i=2; i >= 0; i--) + { + iFGg16 = mng_get_uint16 (pDataline+i+i); + /* scale background up */ + iBGg16 = (mng_uint16)(*(pScanline+2-i)); + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + /* now compose */ + MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16); + /* and return the composed values */ + *(pScanline+2-i) = (mng_uint8)(iFGg16 >> 8); + } + } + } + + pScanline += (pData->iColinc * 3); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA8 = *(pDataline+3); /* get alpha value */ + + if (iA8) /* any opacity at all ? */ + { + if (iA8 == 0xFF) /* fully opaque ? */ + { /* then simply copy the values */ + *pScanline = *(pDataline+2); + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *pDataline; + } + else + { /* do alpha composing */ + int i; + for (i=2; i >= 0; i--) + { + MNG_COMPOSE8 (*(pScanline+i), *(pDataline+2-i), iA8, *(pScanline+i)); + } + } + } + + pScanline += (pData->iColinc * 3); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGR8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +#else /* MNG_NO_16BIT_SUPPORT */ +mng_retcode mng_display_bgr8 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iA8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGR8, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol * 3) + (pData->iDestl * 3); + pDataline = pData->pRGBArow; /* address source row */ + + pDataline = pDataline + (pData->iSourcel / pData->iColinc) * 4; + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *pScanline = *(pDataline+2); + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *pDataline; + + pScanline += (pData->iColinc * 3); + pDataline += 4; + } + } + } + else + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA8 = *(pDataline+3); /* get alpha value */ + + if (iA8) /* any opacity at all ? */ + { + if (iA8 == 0xFF) /* fully opaque ? */ + { /* then simply copy the values */ + *pScanline = *(pDataline+2); + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *pDataline; + } + else + { /* do alpha composing */ +#ifdef MNG_OPTIMIZE_FOOTPRINT_COMPOSE + int i; + for (i=2; i >= 0; i--) + { + MNG_COMPOSE8 (*(pScanline+i), *(pDataline+2-i), iA8, *(pScanline+i)); + } +#else + MNG_COMPOSE8 (*pScanline, *(pDataline+2), iA8, *pScanline ); + MNG_COMPOSE8 (*(pScanline+1), *(pDataline+1), iA8, *(pScanline+1)); + MNG_COMPOSE8 (*(pScanline+2), *pDataline, iA8, *(pScanline+2)); +#endif + } + } + + pScanline += (pData->iColinc * 3); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGR8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_16BIT_SUPPORT */ +#endif /* MNG_SKIPCANVAS_BGR8 */ + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCANVAS_BGRX8 +#ifndef MNG_NO_16BIT_SUPPORT +#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE +mng_retcode mng_display_bgrx8 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint16 iA16; + mng_uint16 iFGr16, iFGg16, iFGb16; + mng_uint16 iBGr16, iBGg16, iBGb16; + mng_uint8 iA8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGRX8, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2); + pDataline = pData->pRGBArow; /* address source row */ + + if (pData->bIsRGBA16) /* adjust source row starting-point */ + pDataline = pDataline + (pData->iSourcel / pData->iColinc) * 8; + else + pDataline = pDataline + (pData->iSourcel / pData->iColinc) * 4; + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *pScanline = *(pDataline+4); + *(pScanline+1) = *(pDataline+2); + *(pScanline+2) = *pDataline; + *(pScanline+3) = 0xFF; /* filler byte */ + + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *pScanline = *(pDataline+2); + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *pDataline; + *(pScanline+3) = 0xFF; /* filler byte */ + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* get alpha value */ + iA16 = mng_get_uint16 (pDataline+6); + + if (iA16) /* any opacity at all ? */ + { + if (iA16 == 0xFFFF) /* fully opaque ? */ + { /* scale down by dropping the LSB */ + *pScanline = *(pDataline+4); + *(pScanline+1) = *(pDataline+2); + *(pScanline+2) = *pDataline; + *(pScanline+3) = 0xFF; /* filler byte */ + } + else + { /* get the proper values */ + iFGr16 = mng_get_uint16 (pDataline ); + iFGg16 = mng_get_uint16 (pDataline+2); + iFGb16 = mng_get_uint16 (pDataline+4); + /* scale background up */ + iBGr16 = (mng_uint16)(*(pScanline+2)); + iBGg16 = (mng_uint16)(*(pScanline+1)); + iBGb16 = (mng_uint16)(*pScanline ); + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* now compose */ + MNG_COMPOSE16(iFGr16, iFGr16, iA16, iBGr16); + MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16); + MNG_COMPOSE16(iFGb16, iFGb16, iA16, iBGb16); + /* and return the composed values */ + *pScanline = (mng_uint8)(iFGb16 >> 8); + *(pScanline+1) = (mng_uint8)(iFGg16 >> 8); + *(pScanline+2) = (mng_uint8)(iFGr16 >> 8); + *(pScanline+3) = 0xFF; /* filler byte */ + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA8 = *(pDataline+3); /* get alpha value */ + + if (iA8) /* any opacity at all ? */ + { + if (iA8 == 0xFF) /* fully opaque ? */ + { /* then simply copy the values */ + *pScanline = *(pDataline+2); + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *pDataline; + *(pScanline+3) = 0xFF; /* filler byte */ + } + else + { /* do alpha composing */ + MNG_COMPOSE8 (*pScanline, *(pDataline+2), iA8, *pScanline ); + MNG_COMPOSE8 (*(pScanline+1), *(pDataline+1), iA8, *(pScanline+1)); + MNG_COMPOSE8 (*(pScanline+2), *pDataline, iA8, *(pScanline+2)); + *(pScanline+3) = 0xFF; /* filler byte */ + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGRX8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +mng_retcode mng_display_bgrx8 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint16 iA16; + mng_uint16 iFGg16; + mng_uint16 iBGg16; + mng_uint8 iA8; + mng_uint8 iBps; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGRX8, MNG_LC_START); +#endif + + iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1); + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2); + pDataline = pData->pRGBArow; /* address source row */ + + /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1)); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *pScanline = *(pDataline+2*iBps); + *(pScanline+1) = *(pDataline+iBps); + *(pScanline+2) = *pDataline; + *(pScanline+3) = 0xFF; /* filler byte */ + + pScanline += (pData->iColinc << 2); + pDataline += 4*iBps; + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* get alpha value */ + iA16 = mng_get_uint16 (pDataline+6); + + if (iA16) /* any opacity at all ? */ + { + if (iA16 == 0xFFFF) /* fully opaque ? */ + { /* scale down by dropping the LSB */ + *pScanline = *(pDataline+4); + *(pScanline+1) = *(pDataline+2); + *(pScanline+2) = *pDataline; + *(pScanline+3) = 0xFF; /* filler byte */ + } + else + { /* get the proper values */ + int i; + for (i=2; i >= 0; i--) + { + iFGg16 = mng_get_uint16 (pDataline+i+i); + /* scale background up */ + iBGg16 = (mng_uint16)(*(pScanline+2-i)); + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + /* now compose */ + MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16); + /* and return the composed values */ + *(pScanline+2-i) = (mng_uint8)(iFGg16 >> 8); + } + *(pScanline+3) = 0xFF; /* filler byte */ + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA8 = *(pDataline+3); /* get alpha value */ + + if (iA8) /* any opacity at all ? */ + { + if (iA8 == 0xFF) /* fully opaque ? */ + { /* then simply copy the values */ + *pScanline = *(pDataline+2); + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *pDataline; + *(pScanline+3) = 0xFF; /* filler byte */ + } + else + { /* do alpha composing */ + int i; + for (i=2; i >= 0; i--) + { + MNG_COMPOSE8 (*(pScanline+i), *(pDataline+2-i), iA8, *(pScanline+i)); + } + *(pScanline+3) = 0xFF; /* filler byte */ + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGRX8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +#else /* MNG_NO_16BIT_SUPPORT */ +mng_retcode mng_display_bgrx8 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iA8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGRX8, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2); + pDataline = pData->pRGBArow; /* address source row */ + + pDataline = pDataline + (pData->iSourcel / pData->iColinc) * 4; + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *pScanline = *(pDataline+2); + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *pDataline; + *(pScanline+3) = 0xFF; /* filler byte */ + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + else + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA8 = *(pDataline+3); /* get alpha value */ + + if (iA8) /* any opacity at all ? */ + { + if (iA8 == 0xFF) /* fully opaque ? */ + { /* then simply copy the values */ + *pScanline = *(pDataline+2); + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *pDataline; + *(pScanline+3) = 0xFF; /* filler byte */ + } + else + { /* do alpha composing */ +#ifdef MNG_OPTIMIZE_FOOTPRINT_COMPOSE + int i; + for (i=2; i >= 0; i--) + { + MNG_COMPOSE8 (*(pScanline+i), *(pDataline+2-i), iA8, *(pScanline+i)); + } +#else + MNG_COMPOSE8 (*pScanline, *(pDataline+2), iA8, *pScanline ); + MNG_COMPOSE8 (*(pScanline+1), *(pDataline+1), iA8, *(pScanline+1)); + MNG_COMPOSE8 (*(pScanline+2), *pDataline, iA8, *(pScanline+2)); +#endif + *(pScanline+3) = 0xFF; /* filler byte */ + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGRX8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_16BIT_SUPPORT */ +#endif /* MNG_SKIPCANVAS_BGRX8 */ + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCANVAS_BGRA8 +#ifndef MNG_NO_16BIT_SUPPORT +#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE +mng_retcode mng_display_bgra8 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iFGa8, iBGa8, iCa8; + mng_uint16 iFGa16, iBGa16, iCa16; + mng_uint16 iFGr16, iFGg16, iFGb16; + mng_uint16 iBGr16, iBGg16, iBGb16; + mng_uint16 iCr16, iCg16, iCb16; + mng_uint8 iCr8, iCg8, iCb8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA8, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2); + pDataline = pData->pRGBArow; /* address source row */ + + if (pData->bIsRGBA16) /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3); + else + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *pScanline = *(pDataline+4); + *(pScanline+1) = *(pDataline+2); + *(pScanline+2) = *pDataline; + *(pScanline+3) = *(pDataline+6); + + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *pScanline = *(pDataline+2); + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *pDataline; + *(pScanline+3) = *(pDataline+3); + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* get alpha values */ + iFGa16 = mng_get_uint16 (pDataline+6); + iBGa16 = (mng_uint16)(*(pScanline+3)); + iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16; + + if (iFGa16) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa16 == 0xFFFF) || (iBGa16 == 0)) + { /* plain copy it */ + *pScanline = *(pDataline+4); + *(pScanline+1) = *(pDataline+2); + *(pScanline+2) = *pDataline; + *(pScanline+3) = *(pDataline+6); + } + else + { + if (iBGa16 == 0xFFFF) /* background fully opaque ? */ + { /* get the proper values */ + iFGr16 = mng_get_uint16 (pDataline ); + iFGg16 = mng_get_uint16 (pDataline+2); + iFGb16 = mng_get_uint16 (pDataline+4); + /* scale background up */ + iBGr16 = (mng_uint16)(*(pScanline+2)); + iBGg16 = (mng_uint16)(*(pScanline+1)); + iBGb16 = (mng_uint16)(*pScanline ); + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* now compose */ + MNG_COMPOSE16(iFGr16, iFGr16, iFGa16, iBGr16); + MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16); + MNG_COMPOSE16(iFGb16, iFGb16, iFGa16, iBGb16); + /* and return the composed values */ + *pScanline = (mng_uint8)(iFGb16 >> 8); + *(pScanline+1) = (mng_uint8)(iFGg16 >> 8); + *(pScanline+2) = (mng_uint8)(iFGr16 >> 8); + /* alpha remains fully opaque !!! */ + } + else + { /* scale background up */ + iBGr16 = (mng_uint16)(*(pScanline+2)); + iBGg16 = (mng_uint16)(*(pScanline+1)); + iBGb16 = (mng_uint16)(*pScanline ); + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* let's blend */ + MNG_BLEND16 (mng_get_uint16 (pDataline ), + mng_get_uint16 (pDataline+2), + mng_get_uint16 (pDataline+4), iFGa16, + iBGr16, iBGg16, iBGb16, iBGa16, + iCr16, iCg16, iCb16, iCa16); + /* and return the composed values */ + *pScanline = (mng_uint8)(iCb16 >> 8); + *(pScanline+1) = (mng_uint8)(iCg16 >> 8); + *(pScanline+2) = (mng_uint8)(iCr16 >> 8); + *(pScanline+3) = (mng_uint8)(iCa16 >> 8); + } + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iFGa8 = *(pDataline+3); /* get alpha values */ + iBGa8 = *(pScanline+3); + + if (iFGa8) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa8 == 0xFF) || (iBGa8 == 0)) + { /* then simply copy the values */ + *pScanline = *(pDataline+2); + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *pDataline; + *(pScanline+3) = *(pDataline+3); + } + else + { + if (iBGa8 == 0xFF) /* background fully opaque ? */ + { /* do alpha composing */ + MNG_COMPOSE8 (*pScanline, *(pDataline+2), iFGa8, *pScanline ); + MNG_COMPOSE8 (*(pScanline+1), *(pDataline+1), iFGa8, *(pScanline+1)); + MNG_COMPOSE8 (*(pScanline+2), *pDataline, iFGa8, *(pScanline+2)); + /* alpha remains fully opaque !!! */ + } + else + { /* now blend */ + MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8, + *(pScanline+2), *(pScanline+1), *pScanline, iBGa8, + iCr8, iCg8, iCb8, iCa8); + /* and return the composed values */ + *pScanline = iCb8; + *(pScanline+1) = iCg8; + *(pScanline+2) = iCr8; + *(pScanline+3) = iCa8; + } + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +mng_retcode mng_display_bgra8 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iFGa8, iBGa8, iCa8; + mng_uint16 iFGa16, iBGa16, iCa16; + mng_uint16 iFGg16; + mng_uint16 iBGr16, iBGg16, iBGb16; + mng_uint16 iCr16, iCg16, iCb16; + mng_uint8 iCr8, iCg8, iCb8; + mng_uint8 iBps; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA8, MNG_LC_START); +#endif + + iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1); + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2); + pDataline = pData->pRGBArow; /* address source row */ + + /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1)); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *pScanline = *(pDataline+2*iBps); + *(pScanline+1) = *(pDataline+iBps); + *(pScanline+2) = *pDataline; + *(pScanline+3) = *(pDataline+3*iBps); + + pScanline += (pData->iColinc << 2); + pDataline += 4*iBps; + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* get alpha values */ + iFGa16 = mng_get_uint16 (pDataline+6); + iBGa16 = (mng_uint16)(*(pScanline+3)); + iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16; + + if (iFGa16) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa16 == 0xFFFF) || (iBGa16 == 0)) + { /* plain copy it */ + *pScanline = *(pDataline+4); + *(pScanline+1) = *(pDataline+2); + *(pScanline+2) = *pDataline; + *(pScanline+3) = *(pDataline+6); + } + else + { + if (iBGa16 == 0xFFFF) /* background fully opaque ? */ + { /* get the proper values */ + int i; + for (i=2; i >= 0; i--) + { + iFGg16 = mng_get_uint16 (pDataline+i+i); + /* scale background up */ + iBGg16 = (mng_uint16)(*(pScanline+2-i)); + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + /* now compose */ + MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16); + /* and return the composed values */ + *(pScanline+2-i) = (mng_uint8)(iFGg16 >> 8); + /* alpha remains fully opaque !!! */ + } + } + else + { /* scale background up */ + iBGr16 = (mng_uint16)(*(pScanline+2)); + iBGg16 = (mng_uint16)(*(pScanline+1)); + iBGb16 = (mng_uint16)(*pScanline ); + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* let's blend */ + MNG_BLEND16 (mng_get_uint16 (pDataline ), + mng_get_uint16 (pDataline+2), + mng_get_uint16 (pDataline+4), iFGa16, + iBGr16, iBGg16, iBGb16, iBGa16, + iCr16, iCg16, iCb16, iCa16); + /* and return the composed values */ + *pScanline = (mng_uint8)(iCb16 >> 8); + *(pScanline+1) = (mng_uint8)(iCg16 >> 8); + *(pScanline+2) = (mng_uint8)(iCr16 >> 8); + *(pScanline+3) = (mng_uint8)(iCa16 >> 8); + } + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iFGa8 = *(pDataline+3); /* get alpha values */ + iBGa8 = *(pScanline+3); + + if (iFGa8) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa8 == 0xFF) || (iBGa8 == 0)) + { /* then simply copy the values */ + *pScanline = *(pDataline+2); + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *pDataline; + *(pScanline+3) = *(pDataline+3); + } + else + { + if (iBGa8 == 0xFF) /* background fully opaque ? */ + { /* do alpha composing */ + int i; + for (i=2; i >= 0; i--) + { + MNG_COMPOSE8 (*(pScanline+i), *(pDataline+2-i), iFGa8, *(pScanline+i)); + } + /* alpha remains fully opaque !!! */ + } + else + { /* now blend */ + MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8, + *(pScanline+2), *(pScanline+1), *pScanline, iBGa8, + iCr8, iCg8, iCb8, iCa8); + /* and return the composed values */ + *pScanline = iCb8; + *(pScanline+1) = iCg8; + *(pScanline+2) = iCr8; + *(pScanline+3) = iCa8; + } + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +#else /* MNG_NO_16BIT_SUPPORT */ +mng_retcode mng_display_bgra8 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iFGa8, iBGa8, iCa8; + mng_uint8 iCr8, iCg8, iCb8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA8, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2); + pDataline = pData->pRGBArow; /* address source row */ + + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *pScanline = *(pDataline+2); + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *pDataline; + *(pScanline+3) = *(pDataline+3); + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + else + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iFGa8 = *(pDataline+3); /* get alpha values */ + iBGa8 = *(pScanline+3); + + if (iFGa8) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa8 == 0xFF) || (iBGa8 == 0)) + { /* then simply copy the values */ + *pScanline = *(pDataline+2); + *(pScanline+1) = *(pDataline+1); + *(pScanline+2) = *pDataline; + *(pScanline+3) = *(pDataline+3); + } + else + { + if (iBGa8 == 0xFF) /* background fully opaque ? */ + { /* do alpha composing */ +#ifdef MNG_OPTIMIZE_FOOTPRINT_COMPOSE + int i; + for (i=2; i >= 0; i--) + { + MNG_COMPOSE8 (*(pScanline+i), *(pDataline+2-i), iFGa8, *(pScanline+i)); + } +#else + MNG_COMPOSE8 (*pScanline, *(pDataline+2), iFGa8, *pScanline ); + MNG_COMPOSE8 (*(pScanline+1), *(pDataline+1), iFGa8, *(pScanline+1)); + MNG_COMPOSE8 (*(pScanline+2), *pDataline, iFGa8, *(pScanline+2)); +#endif + /* alpha remains fully opaque !!! */ + } + else + { /* now blend */ + MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8, + *(pScanline+2), *(pScanline+1), *pScanline, iBGa8, + iCr8, iCg8, iCb8, iCa8); + /* and return the composed values */ + *pScanline = iCb8; + *(pScanline+1) = iCg8; + *(pScanline+2) = iCr8; + *(pScanline+3) = iCa8; + } + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_16BIT_SUPPORT */ +#endif /* MNG_SKIPCANVAS_BGRA8 */ + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCANVAS_BGRA8_PM +#ifndef MNG_NO_16BIT_SUPPORT +#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE +mng_retcode mng_display_bgra8_pm (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint32 s, t; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA8PM, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2); + pDataline = pData->pRGBArow; /* address source row */ + + if (pData->bIsRGBA16) /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3); + else + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + if ((s = pDataline[6]) == 0) + *(mng_uint32*) pScanline = 0; /* set all components = 0 */ + else + { + if (s == 255) + { + pScanline[0] = pDataline[4]; + pScanline[1] = pDataline[2]; + pScanline[2] = pDataline[0]; + pScanline[3] = 255; + } + else + { + pScanline[0] = DIV255B8(s * pDataline[4]); + pScanline[1] = DIV255B8(s * pDataline[2]); + pScanline[2] = DIV255B8(s * pDataline[0]); + pScanline[3] = (mng_uint8)s; + } + } + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values and premultiply */ + if ((s = pDataline[3]) == 0) + *(mng_uint32*) pScanline = 0; /* set all components = 0 */ + else + { + if (s == 255) + { + pScanline[0] = pDataline[2]; + pScanline[1] = pDataline[1]; + pScanline[2] = pDataline[0]; + pScanline[3] = 255; + } + else + { + pScanline[0] = DIV255B8(s * pDataline[2]); + pScanline[1] = DIV255B8(s * pDataline[1]); + pScanline[2] = DIV255B8(s * pDataline[0]); + pScanline[3] = (mng_uint8)s; + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* get alpha values */ + if ((s = pDataline[6]) != 0) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if (s == 255) + { /* plain copy it */ + pScanline[0] = pDataline[4]; + pScanline[1] = pDataline[2]; + pScanline[2] = pDataline[0]; + pScanline[3] = 255; + } + else + { /* now blend (premultiplied) */ + t = 255 - s; +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[i] = DIV255B8(s * pDataline[4-i-i] + t * + pScanline[i]); + } + } +#else + pScanline[0] = DIV255B8(s * pDataline[4] + t * pScanline[0]); + pScanline[1] = DIV255B8(s * pDataline[2] + t * pScanline[1]); + pScanline[2] = DIV255B8(s * pDataline[0] + t * pScanline[2]); +#endif + pScanline[3] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[3]))); + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + if ((s = pDataline[3]) != 0) /* any opacity at all ? */ + { /* fully opaque ? */ + if (s == 255) + { /* then simply copy the values */ + pScanline[0] = pDataline[2]; + pScanline[1] = pDataline[1]; + pScanline[2] = pDataline[0]; + pScanline[3] = 255; + } + else + { /* now blend (premultiplied) */ + t = 255 - s; +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[i] = DIV255B8(s * pDataline[2-i] + t * + pScanline[i]); + } + } +#else + pScanline[0] = DIV255B8(s * pDataline[2] + t * pScanline[0]); + pScanline[1] = DIV255B8(s * pDataline[1] + t * pScanline[1]); + pScanline[2] = DIV255B8(s * pDataline[0] + t * pScanline[2]); +#endif + pScanline[3] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[3]))); + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA8PM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +mng_retcode mng_display_bgra8_pm (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint32 s, t; + mng_uint8 iBps; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA8PM, MNG_LC_START); +#endif + + iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1); + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2); + pDataline = pData->pRGBArow; /* address source row */ + + /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1)); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + if ((s = pDataline[6]) == 0) + *(mng_uint32*) pScanline = 0; /* set all components = 0 */ + else + { + if (s == 255) + { + pScanline[0] = pDataline[4]; + pScanline[1] = pDataline[2]; + pScanline[2] = pDataline[0]; + pScanline[3] = 255; + } + else + { +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + int i; + for (i=2; i >= 0; i--) + { + pScanline[i] = DIV255B8(s * pDataline[4-i-i]); + } +#else + pScanline[0] = DIV255B8(s * pDataline[4]); + pScanline[1] = DIV255B8(s * pDataline[2]); + pScanline[2] = DIV255B8(s * pDataline[0]); +#endif + pScanline[3] = (mng_uint8)s; + } + } + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values and premultiply */ + if ((s = pDataline[3]) == 0) + *(mng_uint32*) pScanline = 0; /* set all components = 0 */ + else + { + if (s == 255) + { + pScanline[0] = pDataline[2]; + pScanline[1] = pDataline[1]; + pScanline[2] = pDataline[0]; + pScanline[3] = 255; + } + else + { +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + int i; + for (i=2; i >= 0; i--) + { + pScanline[i] = DIV255B8(s * pDataline[2-i]); + } +#else + pScanline[0] = DIV255B8(s * pDataline[2]); + pScanline[1] = DIV255B8(s * pDataline[1]); + pScanline[2] = DIV255B8(s * pDataline[0]); +#endif + pScanline[3] = (mng_uint8)s; + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* get alpha values */ + if ((s = pDataline[6]) != 0) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if (s == 255) + { /* plain copy it */ + pScanline[0] = pDataline[4]; + pScanline[1] = pDataline[2]; + pScanline[2] = pDataline[0]; + pScanline[3] = 255; + } + else + { /* now blend (premultiplied) */ + t = 255 - s; +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[i] = DIV255B8(s * pDataline[4-i-i] + t * + pScanline[i]); + } + } +#else + pScanline[0] = DIV255B8(s * pDataline[4] + t * pScanline[0]); + pScanline[1] = DIV255B8(s * pDataline[2] + t * pScanline[1]); + pScanline[2] = DIV255B8(s * pDataline[0] + t * pScanline[2]); +#endif + pScanline[3] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[3]))); + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + if ((s = pDataline[3]) != 0) /* any opacity at all ? */ + { /* fully opaque ? */ + if (s == 255) + { /* then simply copy the values */ + pScanline[0] = pDataline[2]; + pScanline[1] = pDataline[1]; + pScanline[2] = pDataline[0]; + pScanline[3] = 255; + } + else + { /* now blend (premultiplied) */ + t = 255 - s; +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[i] = DIV255B8(s * pDataline[2-i] + t * + pScanline[i]); + } + } +#else + pScanline[0] = DIV255B8(s * pDataline[2] + t * pScanline[0]); + pScanline[1] = DIV255B8(s * pDataline[1] + t * pScanline[1]); + pScanline[2] = DIV255B8(s * pDataline[0] + t * pScanline[2]); +#endif + pScanline[3] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[3]))); + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA8PM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +#else /* MNG_NO_16BIT_SUPPORT */ +mng_retcode mng_display_bgra8_pm (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint32 s, t; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA8PM, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2); + pDataline = pData->pRGBArow; /* address source row */ + + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values and premultiply */ + if ((s = pDataline[3]) == 0) + *(mng_uint32*) pScanline = 0; /* set all components = 0 */ + else + { + if (s == 255) + { + pScanline[0] = pDataline[2]; + pScanline[1] = pDataline[1]; + pScanline[2] = pDataline[0]; + pScanline[3] = 255; + } + else + { +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + int i; + for (i=2; i >= 0; i--) + { + pScanline[i] = DIV255B8(s * pDataline[2-i]); + } +#else + pScanline[0] = DIV255B8(s * pDataline[2]); + pScanline[1] = DIV255B8(s * pDataline[1]); + pScanline[2] = DIV255B8(s * pDataline[0]); +#endif + pScanline[3] = (mng_uint8)s; + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + else + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + if ((s = pDataline[3]) != 0) /* any opacity at all ? */ + { /* fully opaque ? */ + if (s == 255) + { /* then simply copy the values */ + pScanline[0] = pDataline[2]; + pScanline[1] = pDataline[1]; + pScanline[2] = pDataline[0]; + pScanline[3] = 255; + } + else + { /* now blend (premultiplied) */ + t = 255 - s; +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[i] = DIV255B8(s * pDataline[2-i] + t * + pScanline[i]); + } + } +#else + pScanline[0] = DIV255B8(s * pDataline[2] + t * pScanline[0]); + pScanline[1] = DIV255B8(s * pDataline[1] + t * pScanline[1]); + pScanline[2] = DIV255B8(s * pDataline[0] + t * pScanline[2]); +#endif + pScanline[3] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[3]))); + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA8PM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_16BIT_SUPPORT */ +#endif /* MNG_SKIPCANVAS_BGRA8_PM */ + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCANVAS_ABGR8 +#ifndef MNG_NO_16BIT_SUPPORT +#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE +mng_retcode mng_display_abgr8 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iFGa8, iBGa8, iCa8; + mng_uint16 iFGa16, iBGa16, iCa16; + mng_uint16 iFGr16, iFGg16, iFGb16; + mng_uint16 iBGr16, iBGg16, iBGb16; + mng_uint16 iCr16, iCg16, iCb16; + mng_uint8 iCr8, iCg8, iCb8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_ABGR8, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2); + pDataline = pData->pRGBArow; /* address source row */ + + if (pData->bIsRGBA16) /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3); + else + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *pScanline = *(pDataline+6); + *(pScanline+1) = *(pDataline+4); + *(pScanline+2) = *(pDataline+2); + *(pScanline+3) = *pDataline; + + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *pScanline = *(pDataline+3); + *(pScanline+1) = *(pDataline+2); + *(pScanline+2) = *(pDataline+1); + *(pScanline+3) = *pDataline; + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* get alpha values */ + iFGa16 = mng_get_uint16 (pDataline+6); + iBGa16 = (mng_uint16)(*pScanline); + iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16; + + if (iFGa16) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa16 == 0xFFFF) || (iBGa16 == 0)) + { /* plain copy it */ + *pScanline = *(pDataline+6); + *(pScanline+1) = *(pDataline+4); + *(pScanline+2) = *(pDataline+2); + *(pScanline+3) = *pDataline; + } + else + { + if (iBGa16 == 0xFFFF) /* background fully opaque ? */ + { /* get the proper values */ + iFGr16 = mng_get_uint16 (pDataline ); + iFGg16 = mng_get_uint16 (pDataline+2); + iFGb16 = mng_get_uint16 (pDataline+4); + /* scale background up */ + iBGr16 = (mng_uint16)(*(pScanline+3)); + iBGg16 = (mng_uint16)(*(pScanline+2)); + iBGb16 = (mng_uint16)(*(pScanline+1)); + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* now compose */ + MNG_COMPOSE16(iFGr16, iFGr16, iFGa16, iBGr16); + MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16); + MNG_COMPOSE16(iFGb16, iFGb16, iFGa16, iBGb16); + /* and return the composed values */ + /* alpha itself remains fully opaque !!! */ + *(pScanline+1) = (mng_uint8)(iFGb16 >> 8); + *(pScanline+2) = (mng_uint8)(iFGg16 >> 8); + *(pScanline+3) = (mng_uint8)(iFGr16 >> 8); + } + else + { /* scale background up */ + iBGr16 = (mng_uint16)(*(pScanline+3)); + iBGg16 = (mng_uint16)(*(pScanline+2)); + iBGb16 = (mng_uint16)(*(pScanline+1)); + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* let's blend */ + MNG_BLEND16 (mng_get_uint16 (pDataline ), + mng_get_uint16 (pDataline+2), + mng_get_uint16 (pDataline+4), iFGa16, + iBGr16, iBGg16, iBGb16, iBGa16, + iCr16, iCg16, iCb16, iCa16); + /* and return the composed values */ + *pScanline = (mng_uint8)(iCa16 >> 8); + *(pScanline+1) = (mng_uint8)(iCb16 >> 8); + *(pScanline+2) = (mng_uint8)(iCg16 >> 8); + *(pScanline+3) = (mng_uint8)(iCr16 >> 8); + } + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iFGa8 = *(pDataline+3); /* get alpha values */ + iBGa8 = *pScanline; + + if (iFGa8) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa8 == 0xFF) || (iBGa8 == 0)) + { /* then simply copy the values */ + *pScanline = *(pDataline+3); + *(pScanline+1) = *(pDataline+2); + *(pScanline+2) = *(pDataline+1); + *(pScanline+3) = *pDataline; + } + else + { + if (iBGa8 == 0xFF) /* background fully opaque ? */ + { /* do simple alpha composing */ + /* alpha itself remains fully opaque !!! */ + MNG_COMPOSE8 (*(pScanline+1), *(pDataline+2), iFGa8, *(pScanline+1)); + MNG_COMPOSE8 (*(pScanline+2), *(pDataline+1), iFGa8, *(pScanline+2)); + MNG_COMPOSE8 (*(pScanline+3), *pDataline, iFGa8, *(pScanline+3)); + } + else + { /* now blend */ + MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8, + *(pScanline+3), *(pScanline+2), *(pScanline+1), iBGa8, + iCr8, iCg8, iCb8, iCa8); + /* and return the composed values */ + *pScanline = iCa8; + *(pScanline+1) = iCb8; + *(pScanline+2) = iCg8; + *(pScanline+3) = iCr8; + } + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_ABGR8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +mng_retcode mng_display_abgr8 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iFGa8, iBGa8, iCa8; + mng_uint16 iFGa16, iBGa16, iCa16; + mng_uint16 iFGg16; + mng_uint16 iBGr16, iBGg16, iBGb16; + mng_uint16 iCr16, iCg16, iCb16; + mng_uint8 iCr8, iCg8, iCb8; + mng_uint8 iBps; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_ABGR8, MNG_LC_START); +#endif + + iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1); + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2); + pDataline = pData->pRGBArow; /* address source row */ + + /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1)); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *pScanline = *(pDataline+3*iBps); + *(pScanline+1) = *(pDataline+2*iBps); + *(pScanline+2) = *(pDataline+iBps); + *(pScanline+3) = *pDataline; + + pScanline += (pData->iColinc << 2); + pDataline += 4*iBps; + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* get alpha values */ + iFGa16 = mng_get_uint16 (pDataline+6); + iBGa16 = (mng_uint16)(*pScanline); + iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16; + + if (iFGa16) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa16 == 0xFFFF) || (iBGa16 == 0)) + { /* plain copy it */ + *pScanline = *(pDataline+6); + *(pScanline+1) = *(pDataline+4); + *(pScanline+2) = *(pDataline+2); + *(pScanline+3) = *pDataline; + } + else + { + if (iBGa16 == 0xFFFF) /* background fully opaque ? */ + { /* get the proper values */ + int i; + for (i=2; i >= 0; i--) + { + iFGg16 = mng_get_uint16 (pDataline+i+i); + /* scale background up */ + iBGg16 = (mng_uint16)(*(pScanline+3-i)); + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + /* now compose */ + MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16); + /* and return the composed values */ + /* alpha itself remains fully opaque !!! */ + *(pScanline+3-i) = (mng_uint8)(iFGg16 >> 8); + } + } + else + { /* scale background up */ + iBGr16 = (mng_uint16)(*(pScanline+3)); + iBGg16 = (mng_uint16)(*(pScanline+2)); + iBGb16 = (mng_uint16)(*(pScanline+1)); + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* let's blend */ + MNG_BLEND16 (mng_get_uint16 (pDataline ), + mng_get_uint16 (pDataline+2), + mng_get_uint16 (pDataline+4), iFGa16, + iBGr16, iBGg16, iBGb16, iBGa16, + iCr16, iCg16, iCb16, iCa16); + /* and return the composed values */ + *pScanline = (mng_uint8)(iCa16 >> 8); + *(pScanline+1) = (mng_uint8)(iCb16 >> 8); + *(pScanline+2) = (mng_uint8)(iCg16 >> 8); + *(pScanline+3) = (mng_uint8)(iCr16 >> 8); + } + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iFGa8 = *(pDataline+3); /* get alpha values */ + iBGa8 = *pScanline; + + if (iFGa8) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa8 == 0xFF) || (iBGa8 == 0)) + { /* then simply copy the values */ + *pScanline = *(pDataline+3); + *(pScanline+1) = *(pDataline+2); + *(pScanline+2) = *(pDataline+1); + *(pScanline+3) = *pDataline; + } + else + { + if (iBGa8 == 0xFF) /* background fully opaque ? */ + { /* do simple alpha composing */ + /* alpha itself remains fully opaque !!! */ + int i; + for (i=2; i >= 0; i--) + { + MNG_COMPOSE8 (*(pScanline+i+1), *(pDataline+2-i), iFGa8, *(pScanline+i+1)); + } + } + else + { /* now blend */ + MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8, + *(pScanline+3), *(pScanline+2), *(pScanline+1), iBGa8, + iCr8, iCg8, iCb8, iCa8); + /* and return the composed values */ + *pScanline = iCa8; + *(pScanline+1) = iCb8; + *(pScanline+2) = iCg8; + *(pScanline+3) = iCr8; + } + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_ABGR8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +#else /* MNG_NO_16BIT_SUPPORT */ +mng_retcode mng_display_abgr8 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iFGa8, iBGa8, iCa8; + mng_uint8 iCr8, iCg8, iCb8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_ABGR8, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2); + pDataline = pData->pRGBArow; /* address source row */ + + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *pScanline = *(pDataline+3); + *(pScanline+1) = *(pDataline+2); + *(pScanline+2) = *(pDataline+1); + *(pScanline+3) = *pDataline; + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + else + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iFGa8 = *(pDataline+3); /* get alpha values */ + iBGa8 = *pScanline; + + if (iFGa8) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa8 == 0xFF) || (iBGa8 == 0)) + { /* then simply copy the values */ + *pScanline = *(pDataline+3); + *(pScanline+1) = *(pDataline+2); + *(pScanline+2) = *(pDataline+1); + *(pScanline+3) = *pDataline; + } + else + { + if (iBGa8 == 0xFF) /* background fully opaque ? */ + { /* do simple alpha composing */ + /* alpha itself remains fully opaque !!! */ +#ifdef MNG_OPTIMIZE_FOOTPRINT_COMPOSE + int i; + for (i=2; i >= 0; i--) + { + MNG_COMPOSE8 (*(pScanline+i+1), *(pDataline+2-i), iFGa8, *(pScanline+i+1)); + } +#else + MNG_COMPOSE8 (*(pScanline+1), *(pDataline+2), iFGa8, *(pScanline+1)); + MNG_COMPOSE8 (*(pScanline+2), *(pDataline+1), iFGa8, *(pScanline+2)); + MNG_COMPOSE8 (*(pScanline+3), *pDataline, iFGa8, *(pScanline+3)); +#endif + } + else + { /* now blend */ + MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8, + *(pScanline+3), *(pScanline+2), *(pScanline+1), iBGa8, + iCr8, iCg8, iCb8, iCa8); + /* and return the composed values */ + *pScanline = iCa8; + *(pScanline+1) = iCb8; + *(pScanline+2) = iCg8; + *(pScanline+3) = iCr8; + } + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_ABGR8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_16BIT_SUPPORT */ +#endif /* MNG_SKIPCANVAS_ABGR8 */ + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCANVAS_ABGR8_PM +#ifndef MNG_NO_16BIT_SUPPORT +#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE +mng_retcode mng_display_abgr8_pm (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint32 s, t; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_ABGR8_PM, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2); + pDataline = pData->pRGBArow; /* address source row */ + + if (pData->bIsRGBA16) /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3); + else + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + if ((s = pDataline[6]) == 0) + *(mng_uint32*) pScanline = 0; /* set all components = 0 */ + else + { + if (s == 255) + { + pScanline[0] = 255; + pScanline[1] = pDataline[4]; + pScanline[2] = pDataline[2]; + pScanline[3] = pDataline[0]; + } + else + { + pScanline[0] = (mng_uint8)s; +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[i+1] = DIV255B8(s * pDataline[4-i-i]); + } + } +#else + pScanline[1] = DIV255B8(s * pDataline[4]); + pScanline[2] = DIV255B8(s * pDataline[2]); + pScanline[3] = DIV255B8(s * pDataline[0]); +#endif + } + } + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values and premultiply */ + if ((s = pDataline[3]) == 0) + *(mng_uint32*) pScanline = 0; /* set all components = 0 */ + else + { + if (s == 255) + { + pScanline[0] = 255; + pScanline[1] = pDataline[2]; + pScanline[2] = pDataline[1]; + pScanline[3] = pDataline[0]; + } + else + { + pScanline[0] = (mng_uint8)s; +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[i+1] = DIV255B8(s * pDataline[2-i]); + } + } +#else + pScanline[1] = DIV255B8(s * pDataline[2]); + pScanline[2] = DIV255B8(s * pDataline[1]); + pScanline[3] = DIV255B8(s * pDataline[0]); +#endif + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* get alpha values */ + if ((s = pDataline[6]) != 0) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if (s == 255) + { /* plain copy it */ + pScanline[0] = 255; + pScanline[1] = pDataline[4]; + pScanline[2] = pDataline[2]; + pScanline[3] = pDataline[0]; + } + else + { /* now blend (premultiplied) */ + t = 255 - s; + pScanline[0] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[0]))); +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[i+1] = DIV255B8(s * pDataline[4-i-i] + t * + pScanline[i+1]); + } + } +#else + pScanline[1] = DIV255B8(s * pDataline[4] + t * pScanline[1]); + pScanline[2] = DIV255B8(s * pDataline[2] + t * pScanline[2]); + pScanline[3] = DIV255B8(s * pDataline[0] + t * pScanline[3]); +#endif + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + if ((s = pDataline[3]) != 0) /* any opacity at all ? */ + { /* fully opaque ? */ + if (s == 255) + { /* then simply copy the values */ + pScanline[0] = 255; + pScanline[1] = pDataline[2]; + pScanline[2] = pDataline[1]; + pScanline[3] = pDataline[0]; + } + else + { /* now blend (premultiplied) */ + t = 255 - s; + pScanline[0] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[0]))); +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[i+1] = DIV255B8(s * pDataline[2-i] + t * + pScanline[i+1]); + } + } +#else + pScanline[1] = DIV255B8(s * pDataline[2] + t * pScanline[1]); + pScanline[2] = DIV255B8(s * pDataline[1] + t * pScanline[2]); + pScanline[3] = DIV255B8(s * pDataline[0] + t * pScanline[3]); +#endif + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_ABGR8_PM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +mng_retcode mng_display_abgr8_pm (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint32 s, t; + mng_uint8 iBps; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_ABGR8_PM, MNG_LC_START); +#endif + + iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1); + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2); + pDataline = pData->pRGBArow; /* address source row */ + + /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1)); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + if ((s = pDataline[6]) == 0) + *(mng_uint32*) pScanline = 0; /* set all components = 0 */ + else + { + if (s == 255) + { + pScanline[0] = 255; + pScanline[1] = pDataline[4]; + pScanline[2] = pDataline[2]; + pScanline[3] = pDataline[0]; + } + else + { + pScanline[0] = (mng_uint8)s; +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[i+1] = DIV255B8(s * pDataline[4-i-i]); + } + } +#else + pScanline[1] = DIV255B8(s * pDataline[4]); + pScanline[2] = DIV255B8(s * pDataline[2]); + pScanline[3] = DIV255B8(s * pDataline[0]); +#endif + } + } + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values and premultiply */ + if ((s = pDataline[3]) == 0) + *(mng_uint32*) pScanline = 0; /* set all components = 0 */ + else + { + if (s == 255) + { + pScanline[0] = 255; + pScanline[1] = pDataline[2]; + pScanline[2] = pDataline[1]; + pScanline[3] = pDataline[0]; + } + else + { + pScanline[0] = (mng_uint8)s; +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[i+1] = DIV255B8(s * pDataline[2-i]); + } + } +#else + pScanline[1] = DIV255B8(s * pDataline[2]); + pScanline[2] = DIV255B8(s * pDataline[1]); + pScanline[3] = DIV255B8(s * pDataline[0]); +#endif + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* get alpha values */ + if ((s = pDataline[6]) != 0) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if (s == 255) + { /* plain copy it */ + pScanline[0] = 255; + pScanline[1] = pDataline[4]; + pScanline[2] = pDataline[2]; + pScanline[3] = pDataline[0]; + } + else + { /* now blend (premultiplied) */ + t = 255 - s; + pScanline[0] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[0]))); +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[i+1] = DIV255B8(s * pDataline[4-i-i] + t * + pScanline[i+1]); + } + } +#else + pScanline[1] = DIV255B8(s * pDataline[4] + t * pScanline[1]); + pScanline[2] = DIV255B8(s * pDataline[2] + t * pScanline[2]); + pScanline[3] = DIV255B8(s * pDataline[0] + t * pScanline[3]); +#endif + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + if ((s = pDataline[3]) != 0) /* any opacity at all ? */ + { /* fully opaque ? */ + if (s == 255) + { /* then simply copy the values */ + pScanline[0] = 255; + pScanline[1] = pDataline[2]; + pScanline[2] = pDataline[1]; + pScanline[3] = pDataline[0]; + } + else + { /* now blend (premultiplied) */ + t = 255 - s; + pScanline[0] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[0]))); +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[i+1] = DIV255B8(s * pDataline[2-i] + t * + pScanline[i+1]); + } + } +#else + pScanline[1] = DIV255B8(s * pDataline[2] + t * pScanline[1]); + pScanline[2] = DIV255B8(s * pDataline[1] + t * pScanline[2]); + pScanline[3] = DIV255B8(s * pDataline[0] + t * pScanline[3]); +#endif + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_ABGR8_PM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +#else /* MNG_NO_16BIT_SUPPORT */ +mng_retcode mng_display_abgr8_pm (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint32 s, t; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_ABGR8_PM, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl << 2); + pDataline = pData->pRGBArow; /* address source row */ + + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values and premultiply */ + if ((s = pDataline[3]) == 0) + *(mng_uint32*) pScanline = 0; /* set all components = 0 */ + else + { + if (s == 255) + { + pScanline[0] = 255; + pScanline[1] = pDataline[2]; + pScanline[2] = pDataline[1]; + pScanline[3] = pDataline[0]; + } + else + { + pScanline[0] = (mng_uint8)s; +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[i+1] = DIV255B8(s * pDataline[2-i]); + } + } +#else + pScanline[1] = DIV255B8(s * pDataline[2]); + pScanline[2] = DIV255B8(s * pDataline[1]); + pScanline[3] = DIV255B8(s * pDataline[0]); +#endif + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + else + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + if ((s = pDataline[3]) != 0) /* any opacity at all ? */ + { /* fully opaque ? */ + if (s == 255) + { /* then simply copy the values */ + pScanline[0] = 255; + pScanline[1] = pDataline[2]; + pScanline[2] = pDataline[1]; + pScanline[3] = pDataline[0]; + } + else + { /* now blend (premultiplied) */ + t = 255 - s; + pScanline[0] = (mng_uint8)(255 - DIV255B8(t * (255 - pScanline[0]))); +#ifdef MNG_OPTIMIZE_FOOTPRINT_DIV + { + int i; + for (i=2; i >= 0; i--) + { + pScanline[i+1] = DIV255B8(s * pDataline[2-i] + t * + pScanline[i+1]); + } + } +#else + pScanline[1] = DIV255B8(s * pDataline[2] + t * pScanline[1]); + pScanline[2] = DIV255B8(s * pDataline[1] + t * pScanline[2]); + pScanline[3] = DIV255B8(s * pDataline[0] + t * pScanline[3]); +#endif + } + } + + pScanline += (pData->iColinc << 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_ABGR8_PM, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_16BIT_SUPPORT */ +#endif /* MNG_SKIPCANVAS_ABGR8_PM */ + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCANVAS_BGR565 +#ifndef MNG_NO_16BIT_SUPPORT +#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE +mng_retcode mng_display_bgr565 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint16 iA16; + mng_uint16 iFGr16, iFGg16, iFGb16; + mng_uint16 iBGr16, iBGg16, iBGb16; + mng_uint8 iA8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGR565, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2); + pDataline = pData->pRGBArow; /* address source row */ + + if (pData->bIsRGBA16) /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3); + else + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline))&0xF8 ) | ( (*(pDataline+2)>>5) ) ); + *pScanline = (mng_uint8)( ( (*(pDataline+4)) >>3) | ( (*(pDataline+2)&0xFC) << 3) ); + + pScanline += (pData->iColinc * 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline))&0xF8 ) | ( (*(pDataline+1)>>5 ) ) ); + *pScanline = (mng_uint8)( ( *(pDataline+2) >>3 ) | ( (*(pDataline+1)&0xFC ) << 3) ); + + + pScanline += (pData->iColinc * 2); + pDataline += 4; + } + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA16 = mng_get_uint16 (pDataline+6); + + if (iA16) /* any opacity at all ? */ + { + if (iA16 == 0xFFFF) /* fully opaque ? */ + { /* scale down by dropping the LSB */ + *(pScanline+1) = (mng_uint8)( (*(pDataline))&0xF8 ) | (mng_uint8)( (*(pDataline+2)>>5 ) ); + *pScanline = (mng_uint8)( (*(pDataline+4)) >>3) | (mng_uint8)( (*(pDataline+2)&0xFC) << 3); + } + else + { /* get the proper values */ + iFGr16 = mng_get_uint16 (pDataline ); + iFGg16 = mng_get_uint16 (pDataline+2); + iFGb16 = mng_get_uint16 (pDataline+4); + /* scale background up */ + + iBGb16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 ); + iBGg16 = (mng_uint16)( (*(pScanline+1) << 5) | (((*(pScanline )) & 0xE0) >>3 ) ); + iBGr16 = (mng_uint16)( (*(pScanline )) << 3 ); + + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* now compose */ + MNG_COMPOSE16(iFGr16, iFGr16, iA16, iBGr16); + MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16); + MNG_COMPOSE16(iFGb16, iFGb16, iA16, iBGb16); + /* and return the composed values */ + *(pScanline+1) = (mng_uint8) ( ( (iFGr16 >> 8)&0xF8 ) | ( (mng_uint8)(iFGg16>>8) >> 5) ); + *pScanline = (mng_uint8) ( ( (iFGb16>>11) ) | (((mng_uint8)(iFGg16>>8)&0xFC) << 3) ); + } + } + + pScanline += (pData->iColinc * 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA8 = *(pDataline+3); /* get alpha value */ + + if (iA8) /* any opacity at all ? */ + { + if (iA8 == 0xFF) /* fully opaque ? */ + { /* then simply copy the values */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline)) &0xF8 ) | (*(pDataline+1) >>5 ) ); + *pScanline = (mng_uint8)( ( ((*(pDataline+2))>>3) ) | ((*(pDataline+1)&0xFC) << 3) ); + } + else + { /* do alpha composing */ + mng_uint8 iRed, iGreen, iBlue; + + iRed = (mng_uint8) ( *(pScanline+1) & 0xF8 ); + iGreen = (mng_uint8) ( (*(pScanline+1) << 5) | ( ((*pScanline) & 0xE0)>>3 ) ); + iBlue = (mng_uint8) ( (*pScanline << 3) ); + + MNG_COMPOSE8 (iRed, *pDataline, iA8, iRed ); + MNG_COMPOSE8 (iGreen, *(pDataline+1), iA8, iGreen ); + MNG_COMPOSE8 (iBlue, *(pDataline+2), iA8, iBlue ); + + *(pScanline+1) = (mng_uint8) ( ( iRed & 0xF8 ) | (iGreen>>5) ); + *pScanline = (mng_uint8) ( ( iBlue >> 3 ) | ( (iGreen & 0xFC) << 3) ); + } + } + + pScanline += (pData->iColinc * 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGR565, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +mng_retcode mng_display_bgr565 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint16 iA16; + mng_uint16 iFGr16, iFGg16, iFGb16; + mng_uint16 iBGr16, iBGg16, iBGb16; + mng_uint8 iA8; + mng_uint8 iBps; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGR565, MNG_LC_START); +#endif + + iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1); + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2); + pDataline = pData->pRGBArow; /* address source row */ + + /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1)); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline))&0xF8 ) | + ( (*(pDataline+iBps)>>5) ) ); + *pScanline = (mng_uint8)( ( (*(pDataline+2*iBps)) >>3) | + ( (*(pDataline+iBps)&0xFC) << 3) ); + + pScanline += (pData->iColinc * 2); + pDataline += 4*iBps; + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA16 = mng_get_uint16 (pDataline+6); + + if (iA16) /* any opacity at all ? */ + { + if (iA16 == 0xFFFF) /* fully opaque ? */ + { /* scale down by dropping the LSB */ + *(pScanline+1) = (mng_uint8)( (*(pDataline))&0xF8 ) | (mng_uint8)( (*(pDataline+2)>>5 ) ); + *pScanline = (mng_uint8)( (*(pDataline+4)) >>3) | (mng_uint8)( (*(pDataline+2)&0xFC) << 3); + } + else + { /* get the proper values */ + iFGr16 = mng_get_uint16 (pDataline ); + iFGg16 = mng_get_uint16 (pDataline+2); + iFGb16 = mng_get_uint16 (pDataline+4); + /* scale background up */ + + iBGb16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 ); + iBGg16 = (mng_uint16)( (*(pScanline+1) << 5) | (((*(pScanline )) & 0xE0) >>3 ) ); + iBGr16 = (mng_uint16)( (*(pScanline )) << 3 ); + + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* now compose */ + MNG_COMPOSE16(iFGr16, iFGr16, iA16, iBGr16); + MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16); + MNG_COMPOSE16(iFGb16, iFGb16, iA16, iBGb16); + /* and return the composed values */ + *(pScanline+1) = (mng_uint8) ( ( (iFGr16 >> 8)&0xF8 ) | ( (mng_uint8)(iFGg16>>8) >> 5) ); + *pScanline = (mng_uint8) ( ( (iFGb16>>11) ) | (((mng_uint8)(iFGg16>>8)&0xFC) << 3) ); + } + } + + pScanline += (pData->iColinc * 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA8 = *(pDataline+3); /* get alpha value */ + + if (iA8) /* any opacity at all ? */ + { + if (iA8 == 0xFF) /* fully opaque ? */ + { /* then simply copy the values */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline)) &0xF8 ) | (*(pDataline+1) >>5 ) ); + *pScanline = (mng_uint8)( ( ((*(pDataline+2))>>3) ) | ((*(pDataline+1)&0xFC) << 3) ); + } + else + { /* do alpha composing */ + mng_uint8 iRed, iGreen, iBlue; + + iRed = (mng_uint8) ( *(pScanline+1) & 0xF8 ); + iGreen = (mng_uint8) ( (*(pScanline+1) << 5) | ( ((*pScanline) & 0xE0)>>3 ) ); + iBlue = (mng_uint8) ( (*pScanline << 3) ); + + MNG_COMPOSE8 (iRed, *pDataline, iA8, iRed ); + MNG_COMPOSE8 (iGreen, *(pDataline+1), iA8, iGreen ); + MNG_COMPOSE8 (iBlue, *(pDataline+2), iA8, iBlue ); + + *(pScanline+1) = (mng_uint8) ( ( iRed & 0xF8 ) | (iGreen>>5) ); + *pScanline = (mng_uint8) ( ( iBlue >> 3 ) | ( (iGreen & 0xFC) << 3) ); + } + } + + pScanline += (pData->iColinc * 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGR565, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +#else /* MNG_NO_16BIT_SUPPORT */ +mng_retcode mng_display_bgr565 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iA8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGR565, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2); + pDataline = pData->pRGBArow; /* address source row */ + + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline))&0xF8 ) | ( (*(pDataline+1)>>5 ) ) ); + *pScanline = (mng_uint8)( ( *(pDataline+2) >>3 ) | ( (*(pDataline+1)&0xFC ) << 3) ); + + + pScanline += (pData->iColinc * 2); + pDataline += 4; + } + } + } + else + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA8 = *(pDataline+3); /* get alpha value */ + + if (iA8) /* any opacity at all ? */ + { + if (iA8 == 0xFF) /* fully opaque ? */ + { /* then simply copy the values */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline)) &0xF8 ) | (*(pDataline+1) >>5 ) ); + *pScanline = (mng_uint8)( ( ((*(pDataline+2))>>3) ) | ((*(pDataline+1)&0xFC) << 3) ); + } + else + { /* do alpha composing */ + mng_uint8 iRed, iGreen, iBlue; + + iRed = (mng_uint8) ( *(pScanline+1) & 0xF8 ); + iGreen = (mng_uint8) ( (*(pScanline+1) << 5) | ( ((*pScanline) & 0xE0)>>3 ) ); + iBlue = (mng_uint8) ( (*pScanline << 3) ); + + MNG_COMPOSE8 (iRed, *pDataline, iA8, iRed ); + MNG_COMPOSE8 (iGreen, *(pDataline+1), iA8, iGreen ); + MNG_COMPOSE8 (iBlue, *(pDataline+2), iA8, iBlue ); + + *(pScanline+1) = (mng_uint8) ( ( iRed & 0xF8 ) | (iGreen>>5) ); + *pScanline = (mng_uint8) ( ( iBlue >> 3 ) | ( (iGreen & 0xFC) << 3) ); + } + } + + pScanline += (pData->iColinc * 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGR565, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_16BIT_SUPPORT */ +#endif /* MNG_SKIPCANVAS_BGR565 */ + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCANVAS_RGB565 +#ifndef MNG_NO_16BIT_SUPPORT +#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE +mng_retcode mng_display_rgb565 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint16 iA16; + mng_uint16 iFGr16, iFGg16, iFGb16; + mng_uint16 iBGr16, iBGg16, iBGb16; + mng_uint8 iA8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGB565, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2); + pDataline = pData->pRGBArow; /* address source row */ + + if (pData->bIsRGBA16) /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3); + else + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *(pScanline+1) = (mng_uint8)( ( ( *(pDataline+4)) & 0xF8) | (*(pDataline+2) >> 5 ) ); + *pScanline = (mng_uint8)( ( ( *(pDataline )) >> 3 ) | ((*(pDataline+2) & 0xFC) << 3) ); + + pScanline += (pData->iColinc * 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline+2)) & 0xF8) | (*(pDataline+1) >> 5 ) ); + *pScanline = (mng_uint8)( ( *(pDataline ) >> 3 ) | ((*(pDataline+1) & 0xFC) << 3) ); + + pScanline += (pData->iColinc * 2); + pDataline += 4; + } + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA16 = mng_get_uint16 (pDataline+6); + + if (iA16) /* any opacity at all ? */ + { + if (iA16 == 0xFFFF) /* fully opaque ? */ + { /* scale down by dropping the LSB */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline+4)) & 0xF8) | (*(pDataline+2)>>5) ); + *pScanline = (mng_uint8)( ( (*(pDataline )) >> 3 ) | ((*(pDataline+2)&0xFC) << 3) ); + } + else + { /* get the proper values */ + iFGr16 = mng_get_uint16 (pDataline ); + iFGg16 = mng_get_uint16 (pDataline+2); + iFGb16 = mng_get_uint16 (pDataline+4); + + /* scale background up */ + iBGr16 = (mng_uint8)( *(pScanline+1) & 0xF8 ); + iBGg16 = (mng_uint8)( (*(pScanline+1) << 5) | ( ((*pScanline) & 0xE0) >> 3 ) ); + iBGb16 = (mng_uint8)( *(pScanline ) << 3 ); + + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* now compose */ + MNG_COMPOSE16(iFGr16, iFGr16, iA16, iBGr16); + MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16); + MNG_COMPOSE16(iFGb16, iFGb16, iA16, iBGb16); + /* and return the composed values */ + *(pScanline+1) = (mng_uint8)( (mng_uint8)((iFGb16 >> 8) &0xF8) | ( (mng_uint8)(iFGg16 >> 8) >> 5 ) ); + *pScanline = (mng_uint8)( (mng_uint8) (iFGr16 >>11) | ( ( (mng_uint8)(iFGg16 >> 8) & 0xFC) << 3) ); + } + } + + pScanline += (pData->iColinc * 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA8 = *(pDataline+3); /* get alpha value */ + + if (iA8) /* any opacity at all ? */ + { + if (iA8 == 0xFF) /* fully opaque ? */ + { /* then simply copy the values */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline+2)) & 0xF8) | ( *(pDataline+1) >> 5 ) ); + *pScanline = (mng_uint8)( ( (*(pDataline )) >> 3 ) | ( (*(pDataline+1) & 0xFC) << 3 ) ); + } + else + { /* do alpha composing */ + mng_uint8 iRed, iGreen, iBlue; + + iRed = (mng_uint8)( *(pScanline+1) & 0xF8); + iGreen = (mng_uint8)( ( *(pScanline+1) << 5 ) | ( ( (*pScanline)&0xE0)>>3 ) ); + iBlue = (mng_uint8)( *(pScanline ) << 3 ); + + MNG_COMPOSE8 (iRed, *(pDataline+2), iA8, iRed ); + MNG_COMPOSE8 (iGreen, *(pDataline+1), iA8, iGreen ); + MNG_COMPOSE8 (iBlue, *(pDataline+0), iA8, iBlue ); + + *(pScanline+1) = (mng_uint8)( ( iRed & 0xF8) | ( iGreen >> 5 ) ); + *pScanline = (mng_uint8)( (iBlue >> 3 ) | ( (iGreen & 0xFC) << 3) ); + } + } + + pScanline += (pData->iColinc * 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGB565, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +mng_retcode mng_display_rgb565 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint16 iA16; + mng_uint16 iFGr16, iFGg16, iFGb16; + mng_uint16 iBGr16, iBGg16, iBGb16; + mng_uint8 iA8; + mng_uint8 iBps; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGB565, MNG_LC_START); +#endif + + iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1); + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2); + pDataline = pData->pRGBArow; /* address source row */ + + /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1)); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *(pScanline+1) = (mng_uint8)( ( ( *(pDataline+2*iBps)) & 0xF8) | + (*(pDataline+iBps) >> 5 ) ); + *pScanline = (mng_uint8)( ( ( *(pDataline )) >> 3 ) | + ((*(pDataline+iBps) & 0xFC) << 3) ); + + pScanline += (pData->iColinc * 2); + pDataline += 4*iBps; + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA16 = mng_get_uint16 (pDataline+6); + + if (iA16) /* any opacity at all ? */ + { + if (iA16 == 0xFFFF) /* fully opaque ? */ + { /* scale down by dropping the LSB */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline+4)) & 0xF8) | (*(pDataline+2)>>5) ); + *pScanline = (mng_uint8)( ( (*(pDataline )) >> 3 ) | ((*(pDataline+2)&0xFC) << 3) ); + } + else + { /* get the proper values */ + iFGr16 = mng_get_uint16 (pDataline ); + iFGg16 = mng_get_uint16 (pDataline+2); + iFGb16 = mng_get_uint16 (pDataline+4); + + /* scale background up */ + iBGr16 = (mng_uint8)( *(pScanline+1) & 0xF8 ); + iBGg16 = (mng_uint8)( (*(pScanline+1) << 5) | ( ((*pScanline) & 0xE0) >> 3 ) ); + iBGb16 = (mng_uint8)( *(pScanline ) << 3 ); + + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* now compose */ + MNG_COMPOSE16(iFGr16, iFGr16, iA16, iBGr16); + MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16); + MNG_COMPOSE16(iFGb16, iFGb16, iA16, iBGb16); + /* and return the composed values */ + *(pScanline+1) = (mng_uint8)( (mng_uint8)((iFGb16 >> 8) &0xF8) | ( (mng_uint8)(iFGg16 >> 8) >> 5 ) ); + *pScanline = (mng_uint8)( (mng_uint8) (iFGr16 >>11) | ( ( (mng_uint8)(iFGg16 >> 8) & 0xFC) << 3) ); + } + } + + pScanline += (pData->iColinc * 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA8 = *(pDataline+3); /* get alpha value */ + + if (iA8) /* any opacity at all ? */ + { + if (iA8 == 0xFF) /* fully opaque ? */ + { /* then simply copy the values */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline+2)) & 0xF8) | ( *(pDataline+1) >> 5 ) ); + *pScanline = (mng_uint8)( ( (*(pDataline )) >> 3 ) | ( (*(pDataline+1) & 0xFC) << 3 ) ); + } + else + { /* do alpha composing */ + mng_uint8 iRed, iGreen, iBlue; + + iRed = (mng_uint8)( *(pScanline+1) & 0xF8); + iGreen = (mng_uint8)( ( *(pScanline+1) << 5 ) | ( ( (*pScanline)&0xE0)>>3 ) ); + iBlue = (mng_uint8)( *(pScanline ) << 3 ); + + MNG_COMPOSE8 (iRed, *(pDataline+2), iA8, iRed ); + MNG_COMPOSE8 (iGreen, *(pDataline+1), iA8, iGreen ); + MNG_COMPOSE8 (iBlue, *(pDataline+0), iA8, iBlue ); + + *(pScanline+1) = (mng_uint8)( ( iRed & 0xF8) | ( iGreen >> 5 ) ); + *pScanline = (mng_uint8)( (iBlue >> 3 ) | ( (iGreen & 0xFC) << 3) ); + } + } + + pScanline += (pData->iColinc * 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGB565, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +#else /* MNG_NO_16BIT_SUPPORT */ +mng_retcode mng_display_rgb565 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iA8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGB565, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2); + pDataline = pData->pRGBArow; /* address source row */ + + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline+2)) & 0xF8) | (*(pDataline+1) >> 5 ) ); + *pScanline = (mng_uint8)( ( *(pDataline ) >> 3 ) | ((*(pDataline+1) & 0xFC) << 3) ); + + pScanline += (pData->iColinc * 2); + pDataline += 4; + } + } + } + else + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA8 = *(pDataline+3); /* get alpha value */ + + if (iA8) /* any opacity at all ? */ + { + if (iA8 == 0xFF) /* fully opaque ? */ + { /* then simply copy the values */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline+2)) & 0xF8) | ( *(pDataline+1) >> 5 ) ); + *pScanline = (mng_uint8)( ( (*(pDataline )) >> 3 ) | ( (*(pDataline+1) & 0xFC) << 3 ) ); + } + else + { /* do alpha composing */ + mng_uint8 iRed, iGreen, iBlue; + + iRed = (mng_uint8)( *(pScanline+1) & 0xF8); + iGreen = (mng_uint8)( ( *(pScanline+1) << 5 ) | ( ( (*pScanline)&0xE0)>>3 ) ); + iBlue = (mng_uint8)( *(pScanline ) << 3 ); + + MNG_COMPOSE8 (iRed, *(pDataline+2), iA8, iRed ); + MNG_COMPOSE8 (iGreen, *(pDataline+1), iA8, iGreen ); + MNG_COMPOSE8 (iBlue, *(pDataline+0), iA8, iBlue ); + + *(pScanline+1) = (mng_uint8)( ( iRed & 0xF8) | ( iGreen >> 5 ) ); + *pScanline = (mng_uint8)( (iBlue >> 3 ) | ( (iGreen & 0xFC) << 3) ); + } + } + + pScanline += (pData->iColinc * 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGB565, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_16BIT_SUPPORT */ +#endif /* MNG_SKIPCANVAS_RGB565 */ + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCANVAS_BGRA565 +#ifndef MNG_NO_16BIT_SUPPORT +#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE +mng_retcode mng_display_bgra565 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iFGa8, iBGa8, iCa8; + mng_uint16 iFGa16, iBGa16, iCa16; + mng_uint16 iFGr16, iFGg16, iFGb16; + mng_uint16 iBGr16, iBGg16, iBGb16; + mng_uint16 iCr16, iCg16, iCb16; + mng_uint8 iCr8, iCg8, iCb8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA565, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl * 3); + pDataline = pData->pRGBArow; /* address source row */ + + if (pData->bIsRGBA16) /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3); + else + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline))&0xF8 ) | ( (*(pDataline+2)>>5) ) ); + *pScanline = (mng_uint8)( ( (*(pDataline+4)) >>3) | ( (*(pDataline+2)&0xFC) << 3) ); + *(pScanline+2) = *(pDataline+6); + + pScanline += (pData->iColinc * 3); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline))&0xF8 ) | ( (*(pDataline+1)>>5 ) ) ); + *pScanline = (mng_uint8)( ( *(pDataline+2) >>3 ) | ( (*(pDataline+1)&0xFC ) << 3) ); + *(pScanline+2) = *(pDataline+3); + + pScanline += (pData->iColinc * 3); + pDataline += 4; + } + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* get alpha values */ + iFGa16 = mng_get_uint16 (pDataline+6); + iBGa16 = (mng_uint16)(*(pScanline+2)); + iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16; + + if (iFGa16) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa16 == 0xFFFF) || (iBGa16 == 0)) + { /* plain copy it */ + *(pScanline+1) = (mng_uint8)( (*(pDataline))&0xF8 ) | (mng_uint8)( (*(pDataline+2)>>5 ) ); + *pScanline = (mng_uint8)( (*(pDataline+4)) >>3) | (mng_uint8)( (*(pDataline+2)&0xFC) << 3); + *(pScanline+2) = *(pDataline+6); + } + else + { + if (iBGa16 == 0xFFFF) /* background fully opaque ? */ + { /* get the proper values */ + iFGr16 = mng_get_uint16 (pDataline ); + iFGg16 = mng_get_uint16 (pDataline+2); + iFGb16 = mng_get_uint16 (pDataline+4); + /* scale background up */ + iBGb16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 ); + iBGg16 = (mng_uint16)( (*(pScanline+1) << 5) | (((*(pScanline )) & 0xE0) >>3 ) ); + iBGr16 = (mng_uint16)( (*(pScanline )) << 3 ); + + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + + /* now compose */ + MNG_COMPOSE16(iFGr16, iFGr16, iFGa16, iBGr16); + MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16); + MNG_COMPOSE16(iFGb16, iFGb16, iFGa16, iBGb16); + /* and return the composed values */ + *(pScanline+1) = (mng_uint8) ( ( (iFGr16 >> 8)&0xF8 ) | ( (mng_uint8)(iFGg16>>8) >> 5) ); + *pScanline = (mng_uint8) ( ( (iFGb16>>11) ) | (((mng_uint8)(iFGg16>>8)&0xFC) << 3) ); + } + else + { /* scale background up */ + iBGb16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 ); + iBGg16 = (mng_uint16)( (*(pScanline+1) << 5) | (((*(pScanline )) & 0xE0) >>3 ) ); + iBGr16 = (mng_uint16)( (*(pScanline )) << 3 ); + + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* let's blend */ + MNG_BLEND16 (mng_get_uint16 (pDataline ), + mng_get_uint16 (pDataline+2), + mng_get_uint16 (pDataline+4), iFGa16, + iBGr16, iBGg16, iBGb16, iBGa16, + iCr16, iCg16, iCb16, iCa16); + /* and return the composed values */ + *(pScanline+1) = (mng_uint8) ( ( (iCr16 >> 8) & 0xF8 ) | ( (mng_uint8)(iCg16 >> 8) >> 5 ) ); + *pScanline = (mng_uint8) ( ( (iCb16 >> 11) ) | (((mng_uint8)(iCg16 >> 8) & 0xFC) << 3) ); + *(pScanline+2) = (mng_uint8)(iCa16 >> 8); + } + } + } + + pScanline += (pData->iColinc * 3); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iFGa8 = *(pDataline+3); /* get alpha values */ + iBGa8 = *(pScanline+2); + + if (iFGa8) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa8 == 0xFF) || (iBGa8 == 0)) + { /* then simply copy the values */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline)) &0xF8 ) | (*(pDataline+1) >>5 ) ); + *pScanline = (mng_uint8)( ( ((*(pDataline+2))>>3) ) | ((*(pDataline+1)&0xFC) << 3) ); + *(pScanline+2) = *(pDataline+3); + } + else + { + mng_uint8 iRed, iGreen, iBlue; + + iRed = (mng_uint8) ( *(pScanline+1) & 0xF8 ); + iGreen = (mng_uint8) ( (*(pScanline+1) << 5) | ( ((*pScanline) & 0xE0)>>3 ) ); + iBlue = (mng_uint8) ( (*pScanline << 3) ); + + if (iBGa8 == 0xFF) /* background fully opaque ? */ + { /* do alpha composing */ + MNG_COMPOSE8 (iRed, *pDataline, iFGa8, iRed ); + MNG_COMPOSE8 (iGreen, *(pDataline+1), iFGa8, iGreen ); + MNG_COMPOSE8 (iBlue, *(pDataline+2), iFGa8, iBlue ); + /* alpha remains fully opaque !!! */ + *(pScanline+1) = (mng_uint8) ( ( iRed & 0xF8 ) | (iGreen>>5) ); + *pScanline = (mng_uint8) ( ( iBlue >> 3 ) | ( (iGreen & 0xFC) << 3) ); + } + else + { /* now blend */ + MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8, + iRed , iGreen , iBlue , iBGa8, + iCr8, iCg8, iCb8, iCa8); + /* and return the composed values */ + + + *pScanline = (mng_uint8) ( ( iCb8 >> 3 ) | ( (iCg8 & 0xFC) << 3) ); + *(pScanline+1) = (mng_uint8) ( ( iCr8 & 0xF8 ) | (iCg8>>5) ); + *(pScanline+2) = (mng_uint8) iCa8; + } + } + } + + pScanline += (pData->iColinc *3); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA565, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +mng_retcode mng_display_bgra565 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iFGa8, iBGa8, iCa8; + mng_uint16 iFGa16, iBGa16, iCa16; + mng_uint16 iFGr16, iFGg16, iFGb16; + mng_uint16 iBGr16, iBGg16, iBGb16; + mng_uint16 iCr16, iCg16, iCb16; + mng_uint8 iCr8, iCg8, iCb8; + mng_uint8 iBps; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA565, MNG_LC_START); +#endif + + iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1); + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl * 3); + pDataline = pData->pRGBArow; /* address source row */ + + /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1)); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline))&0xF8 ) | + ( (*(pDataline+iBps)>>5) ) ); + *pScanline = (mng_uint8)( ( (*(pDataline+2*iBps)) >>3) | + ( (*(pDataline+iBps)&0xFC) << 3) ); + *(pScanline+2) = *(pDataline+3*iBps); + + pScanline += (pData->iColinc * 3); + pDataline += 4*iBps; + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* get alpha values */ + iFGa16 = mng_get_uint16 (pDataline+6); + iBGa16 = (mng_uint16)(*(pScanline+2)); + iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16; + + if (iFGa16) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa16 == 0xFFFF) || (iBGa16 == 0)) + { /* plain copy it */ + *(pScanline+1) = (mng_uint8)( (*(pDataline))&0xF8 ) | (mng_uint8)( (*(pDataline+2)>>5 ) ); + *pScanline = (mng_uint8)( (*(pDataline+4)) >>3) | (mng_uint8)( (*(pDataline+2)&0xFC) << 3); + *(pScanline+2) = *(pDataline+6); + } + else + { + if (iBGa16 == 0xFFFF) /* background fully opaque ? */ + { /* get the proper values */ + iFGr16 = mng_get_uint16 (pDataline ); + iFGg16 = mng_get_uint16 (pDataline+2); + iFGb16 = mng_get_uint16 (pDataline+4); + /* scale background up */ + iBGb16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 ); + iBGg16 = (mng_uint16)( (*(pScanline+1) << 5) | (((*(pScanline )) & 0xE0) >>3 ) ); + iBGr16 = (mng_uint16)( (*(pScanline )) << 3 ); + + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + + /* now compose */ + MNG_COMPOSE16(iFGr16, iFGr16, iFGa16, iBGr16); + MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16); + MNG_COMPOSE16(iFGb16, iFGb16, iFGa16, iBGb16); + /* and return the composed values */ + *(pScanline+1) = (mng_uint8) ( ( (iFGr16 >> 8)&0xF8 ) | ( (mng_uint8)(iFGg16>>8) >> 5) ); + *pScanline = (mng_uint8) ( ( (iFGb16>>11) ) | (((mng_uint8)(iFGg16>>8)&0xFC) << 3) ); + } + else + { /* scale background up */ + iBGb16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 ); + iBGg16 = (mng_uint16)( (*(pScanline+1) << 5) | (((*(pScanline )) & 0xE0) >>3 ) ); + iBGr16 = (mng_uint16)( (*(pScanline )) << 3 ); + + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* let's blend */ + MNG_BLEND16 (mng_get_uint16 (pDataline ), + mng_get_uint16 (pDataline+2), + mng_get_uint16 (pDataline+4), iFGa16, + iBGr16, iBGg16, iBGb16, iBGa16, + iCr16, iCg16, iCb16, iCa16); + /* and return the composed values */ + *(pScanline+1) = (mng_uint8) ( ( (iCr16 >> 8) & 0xF8 ) | ( (mng_uint8)(iCg16 >> 8) >> 5 ) ); + *pScanline = (mng_uint8) ( ( (iCb16 >> 11) ) | (((mng_uint8)(iCg16 >> 8) & 0xFC) << 3) ); + *(pScanline+2) = (mng_uint8)(iCa16 >> 8); + } + } + } + + pScanline += (pData->iColinc * 3); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iFGa8 = *(pDataline+3); /* get alpha values */ + iBGa8 = *(pScanline+2); + + if (iFGa8) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa8 == 0xFF) || (iBGa8 == 0)) + { /* then simply copy the values */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline)) &0xF8 ) | (*(pDataline+1) >>5 ) ); + *pScanline = (mng_uint8)( ( ((*(pDataline+2))>>3) ) | ((*(pDataline+1)&0xFC) << 3) ); + *(pScanline+2) = *(pDataline+3); + } + else + { + mng_uint8 iRed, iGreen, iBlue; + + iRed = (mng_uint8) ( *(pScanline+1) & 0xF8 ); + iGreen = (mng_uint8) ( (*(pScanline+1) << 5) | ( ((*pScanline) & 0xE0)>>3 ) ); + iBlue = (mng_uint8) ( (*pScanline << 3) ); + + if (iBGa8 == 0xFF) /* background fully opaque ? */ + { /* do alpha composing */ + MNG_COMPOSE8 (iRed, *pDataline, iFGa8, iRed ); + MNG_COMPOSE8 (iGreen, *(pDataline+1), iFGa8, iGreen ); + MNG_COMPOSE8 (iBlue, *(pDataline+2), iFGa8, iBlue ); + /* alpha remains fully opaque !!! */ + *(pScanline+1) = (mng_uint8) ( ( iRed & 0xF8 ) | (iGreen>>5) ); + *pScanline = (mng_uint8) ( ( iBlue >> 3 ) | ( (iGreen & 0xFC) << 3) ); + } + else + { /* now blend */ + MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8, + iRed , iGreen , iBlue , iBGa8, + iCr8, iCg8, iCb8, iCa8); + /* and return the composed values */ + + + *pScanline = (mng_uint8) ( ( iCb8 >> 3 ) | ( (iCg8 & 0xFC) << 3) ); + *(pScanline+1) = (mng_uint8) ( ( iCr8 & 0xF8 ) | (iCg8>>5) ); + *(pScanline+2) = (mng_uint8) iCa8; + } + } + } + + pScanline += (pData->iColinc *3); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA565, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +#else /* MNG_NO_16BIT_SUPPORT */ +mng_retcode mng_display_bgra565 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iFGa8, iBGa8, iCa8; + mng_uint8 iCr8, iCg8, iCb8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA565, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl * 3); + pDataline = pData->pRGBArow; /* address source row */ + + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline))&0xF8 ) | ( (*(pDataline+1)>>5 ) ) ); + *pScanline = (mng_uint8)( ( *(pDataline+2) >>3 ) | ( (*(pDataline+1)&0xFC ) << 3) ); + *(pScanline+2) = *(pDataline+3); + + pScanline += (pData->iColinc * 3); + pDataline += 4; + } + } + } + else + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iFGa8 = *(pDataline+3); /* get alpha values */ + iBGa8 = *(pScanline+2); + + if (iFGa8) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa8 == 0xFF) || (iBGa8 == 0)) + { /* then simply copy the values */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline)) &0xF8 ) | (*(pDataline+1) >>5 ) ); + *pScanline = (mng_uint8)( ( ((*(pDataline+2))>>3) ) | ((*(pDataline+1)&0xFC) << 3) ); + *(pScanline+2) = *(pDataline+3); + } + else + { + mng_uint8 iRed, iGreen, iBlue; + + iRed = (mng_uint8) ( *(pScanline+1) & 0xF8 ); + iGreen = (mng_uint8) ( (*(pScanline+1) << 5) | ( ((*pScanline) & 0xE0)>>3 ) ); + iBlue = (mng_uint8) ( (*pScanline << 3) ); + + if (iBGa8 == 0xFF) /* background fully opaque ? */ + { /* do alpha composing */ + MNG_COMPOSE8 (iRed, *pDataline, iFGa8, iRed ); + MNG_COMPOSE8 (iGreen, *(pDataline+1), iFGa8, iGreen ); + MNG_COMPOSE8 (iBlue, *(pDataline+2), iFGa8, iBlue ); + /* alpha remains fully opaque !!! */ + *(pScanline+1) = (mng_uint8) ( ( iRed & 0xF8 ) | (iGreen>>5) ); + *pScanline = (mng_uint8) ( ( iBlue >> 3 ) | ( (iGreen & 0xFC) << 3) ); + } + else + { /* now blend */ + MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8, + iRed , iGreen , iBlue , iBGa8, + iCr8, iCg8, iCb8, iCa8); + /* and return the composed values */ + + + *pScanline = (mng_uint8) ( ( iCb8 >> 3 ) | ( (iCg8 & 0xFC) << 3) ); + *(pScanline+1) = (mng_uint8) ( ( iCr8 & 0xF8 ) | (iCg8>>5) ); + *(pScanline+2) = (mng_uint8) iCa8; + } + } + } + + pScanline += (pData->iColinc *3); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGRA565, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_16BIT_SUPPORT */ +#endif /* MNG_SKIPCANVAS_BGRA565 */ + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCANVAS_RGBA565 +#ifndef MNG_NO_16BIT_SUPPORT +#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE +mng_retcode mng_display_rgba565 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iFGa8, iBGa8, iCa8; + mng_uint16 iFGa16, iBGa16, iCa16; + mng_uint16 iFGr16, iFGg16, iFGb16; + mng_uint16 iBGr16, iBGg16, iBGb16; + mng_uint16 iCr16, iCg16, iCb16; + mng_uint8 iCr8, iCg8, iCb8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA565, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl * 3); + pDataline = pData->pRGBArow; /* address source row */ + + if (pData->bIsRGBA16) /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3); + else + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline+4))&0xF8 ) | ( (*(pDataline+2)>>5) ) ); + *pScanline = (mng_uint8)( ( (*(pDataline)) >>3) | ( (*(pDataline+2)&0xFC) << 3) ); + *(pScanline+2) = *(pDataline+6); + + pScanline += (pData->iColinc * 3); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline+2))&0xF8 ) | ( (*(pDataline+1)>>5 ) ) ); + *pScanline = (mng_uint8)( ( *(pDataline) >>3 ) | ( (*(pDataline+1)&0xFC ) << 3) ); + *(pScanline+2) = *(pDataline+3); + + pScanline += (pData->iColinc * 3); + pDataline += 4; + } + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* get alpha values */ + iFGa16 = mng_get_uint16 (pDataline+6); + iBGa16 = (mng_uint16)(*(pScanline+2)); + iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16; + + if (iFGa16) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa16 == 0xFFFF) || (iBGa16 == 0)) + { /* plain copy it */ + *(pScanline+1) = (mng_uint8)( (*(pDataline+4))&0xF8 ) | (mng_uint8)( (*(pDataline+2)>>5 ) ); + *pScanline = (mng_uint8)( (*(pDataline)) >>3) | (mng_uint8)( (*(pDataline+2)&0xFC) << 3); + *(pScanline+2) = *(pDataline+6); + } + else + { + if (iBGa16 == 0xFFFF) /* background fully opaque ? */ + { /* get the proper values */ + iFGr16 = mng_get_uint16 (pDataline ); + iFGg16 = mng_get_uint16 (pDataline+2); + iFGb16 = mng_get_uint16 (pDataline+4); + /* scale background up */ + iBGr16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 ); + iBGg16 = (mng_uint16)( (*(pScanline+1) << 5) | (((*(pScanline )) & 0xE0) >>3 ) ); + iBGb16 = (mng_uint16)( (*(pScanline )) << 3 ); + + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + + /* now compose */ + MNG_COMPOSE16(iFGr16, iFGr16, iFGa16, iBGr16); + MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16); + MNG_COMPOSE16(iFGb16, iFGb16, iFGa16, iBGb16); + /* and return the composed values */ + *(pScanline+1) = (mng_uint8) ( ( (iFGb16 >> 8)&0xF8 ) | ( (mng_uint8)(iFGg16>>8) >> 5) ); + *pScanline = (mng_uint8) ( ( (iFGr16>>11) ) | (((mng_uint8)(iFGg16>>8)&0xFC) << 3) ); + } + else + { /* scale background up */ + iBGr16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 ); + iBGg16 = (mng_uint16)( (*(pScanline+1) << 5) | (((*(pScanline )) & 0xE0) >>3 ) ); + iBGb16 = (mng_uint16)( (*(pScanline )) << 3 ); + + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* let's blend */ + MNG_BLEND16 (mng_get_uint16 (pDataline ), + mng_get_uint16 (pDataline+2), + mng_get_uint16 (pDataline+4), iFGa16, + iBGr16, iBGg16, iBGb16, iBGa16, + iCr16, iCg16, iCb16, iCa16); + /* and return the composed values */ + *(pScanline+1) = (mng_uint8) ( ( (iCb16 >> 8) & 0xF8 ) | ( (mng_uint8)(iCg16 >> 8) >> 5 ) ); + *pScanline = (mng_uint8) ( ( (iCr16 >> 11) ) | (((mng_uint8)(iCg16 >> 8) & 0xFC) << 3) ); + *(pScanline+2) = (mng_uint8)(iCa16 >> 8); + } + } + } + + pScanline += (pData->iColinc * 3); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iFGa8 = *(pDataline+3); /* get alpha values */ + iBGa8 = *(pScanline+2); + + if (iFGa8) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa8 == 0xFF) || (iBGa8 == 0)) + { /* then simply copy the values */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline+2)) &0xF8 ) | (*(pDataline+1) >>5 ) ); + *pScanline = (mng_uint8)( ( ((*(pDataline))>>3) ) | ((*(pDataline+1)&0xFC) << 3) ); + *(pScanline+2) = *(pDataline+3); + } + else + { + mng_uint8 iRed, iGreen, iBlue; + + iBlue = (mng_uint8) ( *(pScanline+1) & 0xF8 ); + iGreen = (mng_uint8) ( (*(pScanline+1) << 5) | ( ((*pScanline) & 0xE0)>>3 ) ); + iRed = (mng_uint8) ( (*pScanline << 3) ); + + if (iBGa8 == 0xFF) /* background fully opaque ? */ + { /* do alpha composing */ + MNG_COMPOSE8 (iRed, *pDataline, iFGa8, iRed ); + MNG_COMPOSE8 (iGreen, *(pDataline+1), iFGa8, iGreen ); + MNG_COMPOSE8 (iBlue, *(pDataline+2), iFGa8, iBlue ); + /* alpha remains fully opaque !!! */ + *(pScanline+1) = (mng_uint8) ( ( iBlue & 0xF8 ) | (iGreen>>5) ); + *pScanline = (mng_uint8) ( ( iRed >> 3 ) | ( (iGreen & 0xFC) << 3) ); + } + else + { /* now blend */ + MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8, + iRed , iGreen , iBlue , iBGa8, + iCr8, iCg8, iCb8, iCa8); + /* and return the composed values */ + + + *pScanline = (mng_uint8) ( ( iCr8 >> 3 ) | ( (iCg8 & 0xFC) << 3) ); + *(pScanline+1) = (mng_uint8) ( ( iCb8 & 0xF8 ) | (iCg8>>5) ); + *(pScanline+2) = (mng_uint8) iCa8; + } + } + } + + pScanline += (pData->iColinc *3); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA565, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +mng_retcode mng_display_rgba565 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iFGa8, iBGa8, iCa8; + mng_uint16 iFGa16, iBGa16, iCa16; + mng_uint16 iFGr16, iFGg16, iFGb16; + mng_uint16 iBGr16, iBGg16, iBGb16; + mng_uint16 iCr16, iCg16, iCb16; + mng_uint8 iCr8, iCg8, iCb8; + mng_uint8 iBps; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA565, MNG_LC_START); +#endif + + iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1); + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl * 3); + pDataline = pData->pRGBArow; /* address source row */ + + /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1)); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline+2*iBps))&0xF8 ) | + ( (*(pDataline+iBps)>>5) ) ); + *pScanline = (mng_uint8)( ( (*(pDataline)) >>3) | + ( (*(pDataline+iBps)&0xFC) << 3) ); + *(pScanline+2) = *(pDataline+3*iBps); + + pScanline += (pData->iColinc * 3); + pDataline += 4*iBps; + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* get alpha values */ + iFGa16 = mng_get_uint16 (pDataline+6); + iBGa16 = (mng_uint16)(*(pScanline+2)); + iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16; + + if (iFGa16) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa16 == 0xFFFF) || (iBGa16 == 0)) + { /* plain copy it */ + *(pScanline+1) = (mng_uint8)( (*(pDataline+4))&0xF8 ) | (mng_uint8)( (*(pDataline+2)>>5 ) ); + *pScanline = (mng_uint8)( (*(pDataline)) >>3) | (mng_uint8)( (*(pDataline+2)&0xFC) << 3); + *(pScanline+2) = *(pDataline+6); + } + else + { + if (iBGa16 == 0xFFFF) /* background fully opaque ? */ + { /* get the proper values */ + iFGr16 = mng_get_uint16 (pDataline ); + iFGg16 = mng_get_uint16 (pDataline+2); + iFGb16 = mng_get_uint16 (pDataline+4); + /* scale background up */ + iBGr16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 ); + iBGg16 = (mng_uint16)( (*(pScanline+1) << 5) | (((*(pScanline )) & 0xE0) >>3 ) ); + iBGb16 = (mng_uint16)( (*(pScanline )) << 3 ); + + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + + /* now compose */ + MNG_COMPOSE16(iFGr16, iFGr16, iFGa16, iBGr16); + MNG_COMPOSE16(iFGg16, iFGg16, iFGa16, iBGg16); + MNG_COMPOSE16(iFGb16, iFGb16, iFGa16, iBGb16); + /* and return the composed values */ + *(pScanline+1) = (mng_uint8) ( ( (iFGb16 >> 8)&0xF8 ) | ( (mng_uint8)(iFGg16>>8) >> 5) ); + *pScanline = (mng_uint8) ( ( (iFGr16>>11) ) | (((mng_uint8)(iFGg16>>8)&0xFC) << 3) ); + } + else + { /* scale background up */ + iBGr16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 ); + iBGg16 = (mng_uint16)( (*(pScanline+1) << 5) | (((*(pScanline )) & 0xE0) >>3 ) ); + iBGb16 = (mng_uint16)( (*(pScanline )) << 3 ); + + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* let's blend */ + MNG_BLEND16 (mng_get_uint16 (pDataline ), + mng_get_uint16 (pDataline+2), + mng_get_uint16 (pDataline+4), iFGa16, + iBGr16, iBGg16, iBGb16, iBGa16, + iCr16, iCg16, iCb16, iCa16); + /* and return the composed values */ + *(pScanline+1) = (mng_uint8) ( ( (iCb16 >> 8) & 0xF8 ) | ( (mng_uint8)(iCg16 >> 8) >> 5 ) ); + *pScanline = (mng_uint8) ( ( (iCr16 >> 11) ) | (((mng_uint8)(iCg16 >> 8) & 0xFC) << 3) ); + *(pScanline+2) = (mng_uint8)(iCa16 >> 8); + } + } + } + + pScanline += (pData->iColinc * 3); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iFGa8 = *(pDataline+3); /* get alpha values */ + iBGa8 = *(pScanline+2); + + if (iFGa8) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa8 == 0xFF) || (iBGa8 == 0)) + { /* then simply copy the values */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline+2)) &0xF8 ) | (*(pDataline+1) >>5 ) ); + *pScanline = (mng_uint8)( ( ((*(pDataline))>>3) ) | ((*(pDataline+1)&0xFC) << 3) ); + *(pScanline+2) = *(pDataline+3); + } + else + { + mng_uint8 iRed, iGreen, iBlue; + + iBlue = (mng_uint8) ( *(pScanline+1) & 0xF8 ); + iGreen = (mng_uint8) ( (*(pScanline+1) << 5) | ( ((*pScanline) & 0xE0)>>3 ) ); + iRed = (mng_uint8) ( (*pScanline << 3) ); + + if (iBGa8 == 0xFF) /* background fully opaque ? */ + { /* do alpha composing */ + MNG_COMPOSE8 (iRed, *pDataline, iFGa8, iRed ); + MNG_COMPOSE8 (iGreen, *(pDataline+1), iFGa8, iGreen ); + MNG_COMPOSE8 (iBlue, *(pDataline+2), iFGa8, iBlue ); + /* alpha remains fully opaque !!! */ + *(pScanline+1) = (mng_uint8) ( ( iBlue & 0xF8 ) | (iGreen>>5) ); + *pScanline = (mng_uint8) ( ( iRed >> 3 ) | ( (iGreen & 0xFC) << 3) ); + } + else + { /* now blend */ + MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8, + iRed , iGreen , iBlue , iBGa8, + iCr8, iCg8, iCb8, iCa8); + /* and return the composed values */ + + + *pScanline = (mng_uint8) ( ( iCr8 >> 3 ) | ( (iCg8 & 0xFC) << 3) ); + *(pScanline+1) = (mng_uint8) ( ( iCb8 & 0xF8 ) | (iCg8>>5) ); + *(pScanline+2) = (mng_uint8) iCa8; + } + } + } + + pScanline += (pData->iColinc *3); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA565, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +#else /* MNG_NO_16BIT_SUPPORT */ +mng_retcode mng_display_rgba565 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iFGa8, iBGa8, iCa8; + mng_uint8 iCr8, iCg8, iCb8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA565, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol << 2) + (pData->iDestl * 3); + pDataline = pData->pRGBArow; /* address source row */ + + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline+2))&0xF8 ) | ( (*(pDataline+1)>>5 ) ) ); + *pScanline = (mng_uint8)( ( *(pDataline) >>3 ) | ( (*(pDataline+1)&0xFC ) << 3) ); + *(pScanline+2) = *(pDataline+3); + + pScanline += (pData->iColinc * 3); + pDataline += 4; + } + } + } + else + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iFGa8 = *(pDataline+3); /* get alpha values */ + iBGa8 = *(pScanline+2); + + if (iFGa8) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa8 == 0xFF) || (iBGa8 == 0)) + { /* then simply copy the values */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline+2)) &0xF8 ) | (*(pDataline+1) >>5 ) ); + *pScanline = (mng_uint8)( ( ((*(pDataline))>>3) ) | ((*(pDataline+1)&0xFC) << 3) ); + *(pScanline+2) = *(pDataline+3); + } + else + { + mng_uint8 iRed, iGreen, iBlue; + + iBlue = (mng_uint8) ( *(pScanline+1) & 0xF8 ); + iGreen = (mng_uint8) ( (*(pScanline+1) << 5) | ( ((*pScanline) & 0xE0)>>3 ) ); + iRed = (mng_uint8) ( (*pScanline << 3) ); + + if (iBGa8 == 0xFF) /* background fully opaque ? */ + { /* do alpha composing */ + MNG_COMPOSE8 (iRed, *pDataline, iFGa8, iRed ); + MNG_COMPOSE8 (iGreen, *(pDataline+1), iFGa8, iGreen ); + MNG_COMPOSE8 (iBlue, *(pDataline+2), iFGa8, iBlue ); + /* alpha remains fully opaque !!! */ + *(pScanline+1) = (mng_uint8) ( ( iBlue & 0xF8 ) | (iGreen>>5) ); + *pScanline = (mng_uint8) ( ( iRed >> 3 ) | ( (iGreen & 0xFC) << 3) ); + } + else + { /* now blend */ + MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iFGa8, + iRed , iGreen , iBlue , iBGa8, + iCr8, iCg8, iCb8, iCa8); + /* and return the composed values */ + + + *pScanline = (mng_uint8) ( ( iCr8 >> 3 ) | ( (iCg8 & 0xFC) << 3) ); + *(pScanline+1) = (mng_uint8) ( ( iCb8 & 0xF8 ) | (iCg8>>5) ); + *(pScanline+2) = (mng_uint8) iCa8; + } + } + } + + pScanline += (pData->iColinc *3); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGBA565, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_16BIT_SUPPORT */ +#endif /* MNG_SKIPCANVAS_RGBA565 */ + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCANVAS_BGR565_A8 +#ifndef MNG_NO_16BIT_SUPPORT +#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE +mng_retcode mng_display_bgr565_a8 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pAlphaline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint16 iA16; + mng_uint16 iFGr16, iFGg16, iFGb16; + mng_uint16 iBGr16, iBGg16, iBGb16, iBGa16; + mng_uint16 iCr16, iCg16, iCb16, iCa16; + mng_uint8 iA8, iBGa8, iCa8; + mng_uint8 iCr8, iCg8, iCb8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGR565_A8, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + pAlphaline = (mng_uint8p)pData->fGetalphaline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row +starting-point */ + pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2); + pAlphaline = pAlphaline + pData->iCol + pData->iDestl; + pDataline = pData->pRGBArow; /* address source row */ + + if (pData->bIsRGBA16) /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3); + else + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline))&0xF8 ) | ((*(pDataline+2)>>5) ) ); + *pScanline = (mng_uint8)( ( (*(pDataline+4)) >>3) | ((*(pDataline+2)&0xFC) << 3) ); + *pAlphaline = (mng_uint8)(*(pDataline+6)); + + pScanline += (pData->iColinc * 2); + pAlphaline += pData->iColinc; + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline))&0xF8 ) | ((*(pDataline+1)>>5 ) ) ); + *pScanline = (mng_uint8)( ( *(pDataline+2) >>3 ) | ((*(pDataline+1)&0xFC ) << 3) ); + *pAlphaline = (mng_uint8)(*(pDataline+3)); + + pScanline += (pData->iColinc * 2); + pAlphaline += pData->iColinc; + pDataline += 4; + } + } + } + else /* Not fully opaque */ + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA16 = mng_get_uint16 (pDataline+6); + iBGa16 = (mng_uint16)(*pAlphaline); + iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16; + + if (iA16) /* any opacity at all ? */ + { + if ((iA16 == 0xFFFF) || (iBGa16 == 0)) /* fully opaque or background fully transparent ? */ + { /* scale down by dropping the LSB */ + *(pScanline+1) = (mng_uint8)( (*(pDataline))&0xF8 ) | (mng_uint8)( (*(pDataline+2)>>5 ) ); + *pScanline = (mng_uint8)( (*(pDataline+4)) >>3) | (mng_uint8)( (*(pDataline+2)&0xFC) << 3); + *pAlphaline = *(pDataline+6); + } + else + { + if (iBGa16 == 0xFFFF) /* background fully opaque ? */ + { + /* get the proper values */ + iFGr16 = mng_get_uint16 (pDataline ); + iFGg16 = mng_get_uint16 (pDataline+2); + iFGb16 = mng_get_uint16 (pDataline+4); + /* scale background up */ + + iBGb16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 ); + iBGg16 = (mng_uint16)( (*(pScanline+1) << 5) | (((*(pScanline )) & 0xE0) >>3 ) ); + iBGr16 = (mng_uint16)( (*(pScanline )) << 3 ); + + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* now compose */ + MNG_COMPOSE16(iFGr16, iFGr16, iA16, iBGr16); + MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16); + MNG_COMPOSE16(iFGb16, iFGb16, iA16, iBGb16); + /* and return the composed values */ + *(pScanline+1) = (mng_uint8) ( ( (iFGr16 >> 8)&0xF8 ) | ( (mng_uint8)(iFGg16>>8) >> 5) ); + *pScanline = (mng_uint8) ( ( (iFGb16>>11) ) | (((mng_uint8)(iFGg16>>8)&0xFC) << 3) ); + *pAlphaline = (mng_uint8)(iA16>>8); + } + else /* background is not fully opaque */ + { /* scale background up */ + iBGb16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 ); + iBGg16 = (mng_uint16)( (*(pScanline+1) << 5) | (((*(pScanline )) & 0xE0) >>3 ) ); + iBGr16 = (mng_uint16)( (*(pScanline )) << 3 ); + + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* let's blend */ + MNG_BLEND16 (mng_get_uint16 (pDataline ), + mng_get_uint16 (pDataline+2), + mng_get_uint16 (pDataline+4), iA16, + iBGr16, iBGg16, iBGb16, iBGa16, + iCr16, iCg16, iCb16, iCa16); + /* and return the composed values */ + *(pScanline+1) = (mng_uint8) ( ( (iCr16 >> 8)&0xF8 ) | ( (mng_uint8)(iCg16>>8) >> 5) ); + *pScanline = (mng_uint8) ( ( (iCb16>>11) ) | (((mng_uint8)(iCg16>>8)&0xFC) << 3) ); + *pAlphaline = (mng_uint8)(iCa16 >> 8); + } + } + } + + pScanline += (pData->iColinc * 2); + pAlphaline += pData->iColinc; + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; +iX += pData->iColinc) + { + iA8 = *(pDataline+3); /* get alpha value */ + iBGa8 = *pAlphaline; + + if (iA8) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iA8 == 0xFF) || (iBGa8 == 0)) + { /* then simply copy the values */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline)) &0xF8 ) | (*(pDataline+1) >>5 ) ); + *pScanline = (mng_uint8)( ( ((*(pDataline+2))>>3) ) | ((*(pDataline+1)&0xFC) << 3) ); + *pAlphaline = *(pDataline+3); + } + else + { + if (iBGa8 == 0xFF) /* background fully opaque ? */ + { + /* do alpha composing */ + mng_uint8 iRed, iGreen, iBlue; + + iRed = (mng_uint8) ( *(pScanline+1) & 0xF8 ); + iGreen = (mng_uint8) ( (*(pScanline+1) << 5) | (((*pScanline) & 0xE0)>>3 ) ); + iBlue = (mng_uint8) ( (*pScanline << 3) ); + + MNG_COMPOSE8 (iRed, *pDataline, iA8, iRed ); + MNG_COMPOSE8 (iGreen, *(pDataline+1), iA8, iGreen ); + MNG_COMPOSE8 (iBlue, *(pDataline+2), iA8, iBlue ); + + *(pScanline+1) = (mng_uint8) ( ( iRed & 0xF8 ) | (iGreen>>5) ); + *pScanline = (mng_uint8) ( ( iBlue >> 3 ) | ((iGreen & 0xFC) << 3) ); + *pAlphaline = iA8; + } + else /* background not fully opaque */ + { + MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iA8, + *pScanline, *(pScanline+1), *(pScanline+2), iBGa8, + iCr8, iCg8, iCb8, iCa8); + /* and return the composed values */ + *(pScanline+1) = (mng_uint8) ( ( iCr8 & 0xF8 ) | (iCg8>>5) ); + *pScanline = (mng_uint8) ( ( iCb8 >> 3 ) | ((iCg8 & 0xFC) << 3) ); + *pAlphaline = iCa8; + } + } + } + + pScanline += (pData->iColinc * 2); + pAlphaline += pData->iColinc; + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGR565_A8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +mng_retcode mng_display_bgr565_a8 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pAlphaline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint16 iA16; + mng_uint16 iFGr16, iFGg16, iFGb16; + mng_uint16 iBGr16, iBGg16, iBGb16, iBGa16; + mng_uint16 iCr16, iCg16, iCb16, iCa16; + mng_uint8 iA8, iBGa8, iCa8; + mng_uint8 iCr8, iCg8, iCb8; + mng_uint8 iBps; + + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGR565_A8, MNG_LC_START); +#endif + + iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1); + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + pAlphaline = (mng_uint8p)pData->fGetalphaline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row +starting-point */ + pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2); + pAlphaline = pAlphaline + pData->iCol + pData->iDestl; + pDataline = pData->pRGBArow; /* address source row */ + + /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1)); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline))&0xF8 ) | + ((*(pDataline+iBps)>>5) ) ); + *pScanline = (mng_uint8)( ( (*(pDataline+2*iBps)) >>3) | + ((*(pDataline+iBps)&0xFC) << 3) ); + *pAlphaline = (mng_uint8)(*(pDataline+6)); + + pScanline += (pData->iColinc * 2); + pAlphaline += pData->iColinc; + pDataline += 8; + } + } + else /* Not fully opaque */ + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA16 = mng_get_uint16 (pDataline+6); + iBGa16 = (mng_uint16)(*pAlphaline); + iBGa16 = (mng_uint16)(iBGa16 << 8) | iBGa16; + + if (iA16) /* any opacity at all ? */ + { + if ((iA16 == 0xFFFF) || (iBGa16 == 0)) /* fully opaque or background fully transparent ? */ + { /* scale down by dropping the LSB */ + *(pScanline+1) = (mng_uint8)( (*(pDataline))&0xF8 ) | (mng_uint8)( (*(pDataline+2)>>5 ) ); + *pScanline = (mng_uint8)( (*(pDataline+4)) >>3) | (mng_uint8)( (*(pDataline+2)&0xFC) << 3); + *pAlphaline = *(pDataline+6); + } + else + { + if (iBGa16 == 0xFFFF) /* background fully opaque ? */ + { + /* get the proper values */ + iFGr16 = mng_get_uint16 (pDataline ); + iFGg16 = mng_get_uint16 (pDataline+2); + iFGb16 = mng_get_uint16 (pDataline+4); + /* scale background up */ + + iBGb16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 ); + iBGg16 = (mng_uint16)( (*(pScanline+1) << 5) | (((*(pScanline )) & 0xE0) >>3 ) ); + iBGr16 = (mng_uint16)( (*(pScanline )) << 3 ); + + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* now compose */ + MNG_COMPOSE16(iFGr16, iFGr16, iA16, iBGr16); + MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16); + MNG_COMPOSE16(iFGb16, iFGb16, iA16, iBGb16); + /* and return the composed values */ + *(pScanline+1) = (mng_uint8) ( ( (iFGr16 >> 8)&0xF8 ) | ( (mng_uint8)(iFGg16>>8) >> 5) ); + *pScanline = (mng_uint8) ( ( (iFGb16>>11) ) | (((mng_uint8)(iFGg16>>8)&0xFC) << 3) ); + *pAlphaline = (mng_uint8)(iA16>>8); + } + else /* background is not fully opaque */ + { /* scale background up */ + iBGb16 = (mng_uint16)( (*(pScanline+1)) & 0xF8 ); + iBGg16 = (mng_uint16)( (*(pScanline+1) << 5) | (((*(pScanline )) & 0xE0) >>3 ) ); + iBGr16 = (mng_uint16)( (*(pScanline )) << 3 ); + + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* let's blend */ + MNG_BLEND16 (mng_get_uint16 (pDataline ), + mng_get_uint16 (pDataline+2), + mng_get_uint16 (pDataline+4), iA16, + iBGr16, iBGg16, iBGb16, iBGa16, + iCr16, iCg16, iCb16, iCa16); + /* and return the composed values */ + *(pScanline+1) = (mng_uint8) ( ( (iCr16 >> 8)&0xF8 ) | ( (mng_uint8)(iCg16>>8) >> 5) ); + *pScanline = (mng_uint8) ( ( (iCb16>>11) ) | (((mng_uint8)(iCg16>>8)&0xFC) << 3) ); + *pAlphaline = (mng_uint8)(iCa16 >> 8); + } + } + } + + pScanline += (pData->iColinc * 2); + pAlphaline += pData->iColinc; + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; +iX += pData->iColinc) + { + iA8 = *(pDataline+3); /* get alpha value */ + iBGa8 = *pAlphaline; + + if (iA8) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iA8 == 0xFF) || (iBGa8 == 0)) + { /* then simply copy the values */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline)) &0xF8 ) | (*(pDataline+1) >>5 ) ); + *pScanline = (mng_uint8)( ( ((*(pDataline+2))>>3) ) | ((*(pDataline+1)&0xFC) << 3) ); + *pAlphaline = *(pDataline+3); + } + else + { + if (iBGa8 == 0xFF) /* background fully opaque ? */ + { + /* do alpha composing */ + mng_uint8 iRed, iGreen, iBlue; + + iRed = (mng_uint8) ( *(pScanline+1) & 0xF8 ); + iGreen = (mng_uint8) ( (*(pScanline+1) << 5) | (((*pScanline) & 0xE0)>>3 ) ); + iBlue = (mng_uint8) ( (*pScanline << 3) ); + + MNG_COMPOSE8 (iRed, *pDataline, iA8, iRed ); + MNG_COMPOSE8 (iGreen, *(pDataline+1), iA8, iGreen ); + MNG_COMPOSE8 (iBlue, *(pDataline+2), iA8, iBlue ); + + *(pScanline+1) = (mng_uint8) ( ( iRed & 0xF8 ) | (iGreen>>5) ); + *pScanline = (mng_uint8) ( ( iBlue >> 3 ) | ((iGreen & 0xFC) << 3) ); + *pAlphaline = iA8; + } + else /* background not fully opaque */ + { + MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iA8, + *pScanline, *(pScanline+1), *(pScanline+2), iBGa8, + iCr8, iCg8, iCb8, iCa8); + /* and return the composed values */ + *(pScanline+1) = (mng_uint8) ( ( iCr8 & 0xF8 ) | (iCg8>>5) ); + *pScanline = (mng_uint8) ( ( iCb8 >> 3 ) | ((iCg8 & 0xFC) << 3) ); + *pAlphaline = iCa8; + } + } + } + + pScanline += (pData->iColinc * 2); + pAlphaline += pData->iColinc; + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGR565_A8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +#else /* MNG_NO_16BIT_SUPPORT */ +mng_retcode mng_display_bgr565_a8 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pAlphaline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iA8, iBGa8, iCa8; + mng_uint8 iCr8, iCg8, iCb8; + + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGR565_A8, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + pAlphaline = (mng_uint8p)pData->fGetalphaline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row +starting-point */ + pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2); + pAlphaline = pAlphaline + pData->iCol + pData->iDestl; + pDataline = pData->pRGBArow; /* address source row */ + + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline))&0xF8 ) | ((*(pDataline+1)>>5 ) ) ); + *pScanline = (mng_uint8)( ( *(pDataline+2) >>3 ) | ((*(pDataline+1)&0xFC ) << 3) ); + *pAlphaline = (mng_uint8)(*(pDataline+3)); + + pScanline += (pData->iColinc * 2); + pAlphaline += pData->iColinc; + pDataline += 4; + } + } + } + else /* Not fully opaque */ + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; +iX += pData->iColinc) + { + iA8 = *(pDataline+3); /* get alpha value */ + iBGa8 = *pAlphaline; + + if (iA8) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iA8 == 0xFF) || (iBGa8 == 0)) + { /* then simply copy the values */ + *(pScanline+1) = (mng_uint8)( ( (*(pDataline)) &0xF8 ) | (*(pDataline+1) >>5 ) ); + *pScanline = (mng_uint8)( ( ((*(pDataline+2))>>3) ) | ((*(pDataline+1)&0xFC) << 3) ); + *pAlphaline = *(pDataline+3); + } + else + { + if (iBGa8 == 0xFF) /* background fully opaque ? */ + { + /* do alpha composing */ + mng_uint8 iRed, iGreen, iBlue; + + iRed = (mng_uint8) ( *(pScanline+1) & 0xF8 ); + iGreen = (mng_uint8) ( (*(pScanline+1) << 5) | (((*pScanline) & 0xE0)>>3 ) ); + iBlue = (mng_uint8) ( (*pScanline << 3) ); + + MNG_COMPOSE8 (iRed, *pDataline, iA8, iRed ); + MNG_COMPOSE8 (iGreen, *(pDataline+1), iA8, iGreen ); + MNG_COMPOSE8 (iBlue, *(pDataline+2), iA8, iBlue ); + + *(pScanline+1) = (mng_uint8) ( ( iRed & 0xF8 ) | (iGreen>>5) ); + *pScanline = (mng_uint8) ( ( iBlue >> 3 ) | ((iGreen & 0xFC) << 3) ); + *pAlphaline = iA8; + } + else /* background not fully opaque */ + { + MNG_BLEND8 (*pDataline, *(pDataline+1), *(pDataline+2), iA8, + *pScanline, *(pScanline+1), *(pScanline+2), iBGa8, + iCr8, iCg8, iCb8, iCa8); + /* and return the composed values */ + *(pScanline+1) = (mng_uint8) ( ( iCr8 & 0xF8 ) | (iCg8>>5) ); + *pScanline = (mng_uint8) ( ( iCb8 >> 3 ) | ((iCg8 & 0xFC) << 3) ); + *pAlphaline = iCa8; + } + } + } + + pScanline += (pData->iColinc * 2); + pAlphaline += pData->iColinc; + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGR565_A8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_16BIT_SUPPORT */ +#endif /* MNG_SKIPCANVAS_BGR565_A8 */ + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCANVAS_RGB555 +#ifndef MNG_NO_16BIT_SUPPORT +#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE +mng_retcode mng_display_rgb555 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint16 iA16; + mng_uint16 iFGr16, iFGg16, iFGb16; + mng_uint16 iBGr16, iBGg16, iBGb16; + mng_uint8 iA8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGB555, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2); + pDataline = pData->pRGBArow; /* address source row */ + + if (pData->bIsRGBA16) /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3); + else + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *(pScanline+1) = (mng_uint8)( ((*(pDataline+4) & 0xF8) >> 1 ) | (*(pDataline+2) >> 6 ) ); + *pScanline = (mng_uint8)( ( *(pDataline ) >> 3 ) | ((*(pDataline+2) & 0xF8) << 2 ) ); + + pScanline += (pData->iColinc * 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *(pScanline+1) = (mng_uint8)( ((*(pDataline+2) & 0xF8) >> 1 ) | (*(pDataline+1) >> 6 ) ); + *pScanline = (mng_uint8)( ( *(pDataline ) >> 3 ) | ((*(pDataline+1) & 0xF8) << 2 ) ); + + pScanline += (pData->iColinc * 2); + pDataline += 4; + } + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA16 = mng_get_uint16 (pDataline+6); + + if (iA16) /* any opacity at all ? */ + { + if (iA16 == 0xFFFF) /* fully opaque ? */ + { /* scale down by dropping the LSB */ + *(pScanline+1) = (mng_uint8)( ((*(pDataline+4) & 0xF8) >> 1 ) | (*(pDataline+2) >> 6 ) ); + *pScanline = (mng_uint8)( ( *(pDataline ) >> 3 ) | ((*(pDataline+2) & 0xF8) << 2 ) ); + } + else + { /* get the proper values */ + iFGr16 = mng_get_uint16 (pDataline ); + iFGg16 = mng_get_uint16 (pDataline+2); + iFGb16 = mng_get_uint16 (pDataline+4); + + /* scale background up */ + iBGr16 = (mng_uint8)( (*(pScanline+1) & 0xF8) << 1 ); + iBGg16 = (mng_uint8)( (*(pScanline+1) << 6 ) | ( ((*pScanline) & 0xE0) >> 2 ) ); + iBGb16 = (mng_uint8)( *(pScanline ) << 3 ); + + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* now compose */ + MNG_COMPOSE16(iFGr16, iFGr16, iA16, iBGr16); + MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16); + MNG_COMPOSE16(iFGb16, iFGb16, iA16, iBGb16); + /* and return the composed values */ + *(pScanline+1) = (mng_uint8)( (mng_uint8)(((iFGb16 >> 8) & 0xF8) >> 1 ) | ( (mng_uint8)(iFGg16 >> 8) >> 6 ) ); + *pScanline = (mng_uint8)( (mng_uint8) ((iFGr16 >>11) >> 3 ) | ( ( (mng_uint8)(iFGg16 >> 8) & 0xF8) << 2 ) ); + } + } + + pScanline += (pData->iColinc * 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA8 = *(pDataline+3); /* get alpha value */ + + if (iA8) /* any opacity at all ? */ + { + if (iA8 == 0xFF) /* fully opaque ? */ + { /* then simply copy the values */ + *(pScanline+1) = (mng_uint8)( ((*(pDataline+2) & 0xF8) >> 1 ) | (*(pDataline+1) >> 6 ) ); + *pScanline = (mng_uint8)( ( *(pDataline ) >> 3 ) | ((*(pDataline+1) & 0xF8) << 2 ) ); + } + else + { /* do alpha composing */ + mng_uint8 iRed, iGreen, iBlue; + + iRed = (mng_uint8)( (*(pScanline+1) & 0xF8) << 1 ); + iGreen = (mng_uint8)( (*(pScanline+1) << 6 ) | ( ((*pScanline) & 0xE0) >> 2 ) ); + iBlue = (mng_uint8)( *(pScanline ) << 3 ); + + MNG_COMPOSE8 (iRed, *(pDataline+2), iA8, iRed ); + MNG_COMPOSE8 (iGreen, *(pDataline+1), iA8, iGreen ); + MNG_COMPOSE8 (iBlue, *(pDataline+0), iA8, iBlue ); + + *(pScanline+1) = (mng_uint8)( ( (iRed & 0xF8) >> 1 ) | ( iGreen >> 6 ) ); + *pScanline = (mng_uint8)( (iBlue >> 3 ) | ( (iGreen & 0xF8) << 2 ) ); + } + } + + pScanline += (pData->iColinc * 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGB555, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +mng_retcode mng_display_rgb555 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint16 iA16; + mng_uint16 iFGr16, iFGg16, iFGb16; + mng_uint16 iBGr16, iBGg16, iBGb16; + mng_uint8 iA8; + mng_uint8 iBps; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGB555, MNG_LC_START); +#endif + + iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1); + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2); + pDataline = pData->pRGBArow; /* address source row */ + + /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1)); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *(pScanline+1) = (mng_uint8)( ((*(pDataline+2*iBps) & 0xF8) >> 1 ) | (*(pDataline+iBps) >> 6 ) ); + *pScanline = (mng_uint8)( ( *(pDataline ) >> 3 ) | ((*(pDataline+iBps) & 0xF8) << 2 ) ); + + pScanline += (pData->iColinc * 2); + pDataline += 4*iBps; + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA16 = mng_get_uint16 (pDataline+6); + + if (iA16) /* any opacity at all ? */ + { + if (iA16 == 0xFFFF) /* fully opaque ? */ + { /* scale down by dropping the LSB */ + *(pScanline+1) = (mng_uint8)( ((*(pDataline+4) & 0xF8) >> 1 ) | (*(pDataline+2) >> 6 ) ); + *pScanline = (mng_uint8)( ( *(pDataline ) >> 3 ) | ((*(pDataline+2) & 0xF8) << 2 ) ); + } + else + { /* get the proper values */ + iFGr16 = mng_get_uint16 (pDataline ); + iFGg16 = mng_get_uint16 (pDataline+2); + iFGb16 = mng_get_uint16 (pDataline+4); + + /* scale background up */ + iBGr16 = (mng_uint8)( (*(pScanline+1) & 0xF8) << 1 ); + iBGg16 = (mng_uint8)( (*(pScanline+1) << 6 ) | ( ((*pScanline) & 0xE0) >> 2 ) ); + iBGb16 = (mng_uint8)( *(pScanline ) << 3 ); + + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* now compose */ + MNG_COMPOSE16(iFGr16, iFGr16, iA16, iBGr16); + MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16); + MNG_COMPOSE16(iFGb16, iFGb16, iA16, iBGb16); + /* and return the composed values */ + *(pScanline+1) = (mng_uint8)( (mng_uint8)(((iFGb16 >> 8) & 0xF8) >> 1 ) | ( (mng_uint8)(iFGg16 >> 8) >> 6 ) ); + *pScanline = (mng_uint8)( (mng_uint8) ((iFGr16 >>11) >> 3 ) | ( ( (mng_uint8)(iFGg16 >> 8) & 0xF8) << 2 ) ); + } + } + + pScanline += (pData->iColinc * 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA8 = *(pDataline+3); /* get alpha value */ + + if (iA8) /* any opacity at all ? */ + { + if (iA8 == 0xFF) /* fully opaque ? */ + { /* then simply copy the values */ + *(pScanline+1) = (mng_uint8)( ((*(pDataline+2) & 0xF8) >> 1 ) | (*(pDataline+1) >> 6 ) ); + *pScanline = (mng_uint8)( ( *(pDataline ) >> 3 ) | ((*(pDataline+1) & 0xF8) << 2 ) ); + } + else + { /* do alpha composing */ + mng_uint8 iRed, iGreen, iBlue; + + iRed = (mng_uint8)( (*(pScanline+1) & 0xF8) << 1 ); + iGreen = (mng_uint8)( (*(pScanline+1) << 6 ) | ( ((*pScanline) & 0xE0) >> 2 ) ); + iBlue = (mng_uint8)( *(pScanline ) << 3 ); + + MNG_COMPOSE8 (iRed, *(pDataline+2), iA8, iRed ); + MNG_COMPOSE8 (iGreen, *(pDataline+1), iA8, iGreen ); + MNG_COMPOSE8 (iBlue, *(pDataline+0), iA8, iBlue ); + + *(pScanline+1) = (mng_uint8)( ( (iRed & 0xF8) >> 1 ) | ( iGreen >> 6 ) ); + *pScanline = (mng_uint8)( (iBlue >> 3 ) | ( (iGreen & 0xF8) << 2 ) ); + } + } + + pScanline += (pData->iColinc * 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGB555, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +#else /* MNG_NO_16BIT_SUPPORT */ +mng_retcode mng_display_rgb555 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iA8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGB555, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2); + pDataline = pData->pRGBArow; /* address source row */ + + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *(pScanline+1) = (mng_uint8)( ((*(pDataline+2) & 0xF8) >> 1 ) | (*(pDataline+1) >> 6 ) ); + *pScanline = (mng_uint8)( ( *(pDataline ) >> 3 ) | ((*(pDataline+1) & 0xF8) << 2 ) ); + + pScanline += (pData->iColinc * 2); + pDataline += 4; + } + } + } + else + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA8 = *(pDataline+3); /* get alpha value */ + + if (iA8) /* any opacity at all ? */ + { + if (iA8 == 0xFF) /* fully opaque ? */ + { /* then simply copy the values */ + *(pScanline+1) = (mng_uint8)( ((*(pDataline+2) & 0xF8) >> 1 ) | (*(pDataline+1) >> 6 ) ); + *pScanline = (mng_uint8)( ( *(pDataline ) >> 3 ) | ((*(pDataline+1) & 0xF8) << 2 ) ); + } + else + { /* do alpha composing */ + mng_uint8 iRed, iGreen, iBlue; + + iRed = (mng_uint8)( (*(pScanline+1) & 0xF8) << 1 ); + iGreen = (mng_uint8)( (*(pScanline+1) << 6 ) | ( ((*pScanline) & 0xE0) >> 2 ) ); + iBlue = (mng_uint8)( *(pScanline ) << 3 ); + + MNG_COMPOSE8 (iRed, *(pDataline+2), iA8, iRed ); + MNG_COMPOSE8 (iGreen, *(pDataline+1), iA8, iGreen ); + MNG_COMPOSE8 (iBlue, *(pDataline+0), iA8, iBlue ); + + *(pScanline+1) = (mng_uint8)( ( (iRed & 0xF8) >> 1 ) | ( iGreen >> 6 ) ); + *pScanline = (mng_uint8)( (iBlue >> 3 ) | ( (iGreen & 0xF8) << 2 ) ); + } + } + + pScanline += (pData->iColinc * 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_RGB555, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_16BIT_SUPPORT */ +#endif /* MNG_SKIPCANVAS_RGB555 */ + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCANVAS_BGR555 +#ifndef MNG_NO_16BIT_SUPPORT +#ifndef MNG_OPTIMIZE_FOOTPRINT_COMPOSE +mng_retcode mng_display_bgr555 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint16 iA16; + mng_uint16 iFGr16, iFGg16, iFGb16; + mng_uint16 iBGr16, iBGg16, iBGb16; + mng_uint8 iA8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGR555, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2); + pDataline = pData->pRGBArow; /* address source row */ + + if (pData->bIsRGBA16) /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 3); + else + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *(pScanline+1) = (mng_uint8)( ((*(pDataline ) & 0xF8) >> 1 ) | (*(pDataline+2) >> 6 ) ); + *pScanline = (mng_uint8)( ( *(pDataline+4) >> 3 ) | ((*(pDataline+2) & 0xF8) << 2 ) ); + + pScanline += (pData->iColinc * 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *(pScanline+1) = (mng_uint8)( ((*(pDataline ) & 0xF8) >> 1 ) | (*(pDataline+1) >> 6 ) ); + *pScanline = (mng_uint8)( ( *(pDataline+2) >> 3 ) | ((*(pDataline+1) & 0xF8) << 2 ) ); + + pScanline += (pData->iColinc * 2); + pDataline += 4; + } + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA16 = mng_get_uint16 (pDataline+6); + + if (iA16) /* any opacity at all ? */ + { + if (iA16 == 0xFFFF) /* fully opaque ? */ + { /* scale down by dropping the LSB */ + *(pScanline+1) = (mng_uint8)( ((*(pDataline ) & 0xF8) >> 1 ) | (*(pDataline+2) >> 6 ) ); + *pScanline = (mng_uint8)( ( *(pDataline+4) >> 3 ) | ((*(pDataline+2) & 0xF8) << 2 ) ); + } + else + { /* get the proper values */ + iFGr16 = mng_get_uint16 (pDataline ); + iFGg16 = mng_get_uint16 (pDataline+2); + iFGb16 = mng_get_uint16 (pDataline+4); + + /* scale background up */ + iBGb16 = (mng_uint8)( (*(pScanline+1) & 0xF8) << 1 ); + iBGg16 = (mng_uint8)( (*(pScanline+1) << 6 ) | ( ((*pScanline) & 0xE0) >> 2 ) ); + iBGr16 = (mng_uint8)( *(pScanline ) << 3 ); + + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* now compose */ + MNG_COMPOSE16(iFGr16, iFGr16, iA16, iBGr16); + MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16); + MNG_COMPOSE16(iFGb16, iFGb16, iA16, iBGb16); + /* and return the composed values */ + *(pScanline+1) = (mng_uint8)( (mng_uint8)(((iFGr16 >> 8) & 0xF8) >> 1 ) | ( (mng_uint8)(iFGg16 >> 8) >> 6 ) ); + *pScanline = (mng_uint8)( (mng_uint8) ((iFGb16 >>11) >> 3 ) | ( ( (mng_uint8)(iFGg16 >> 8) & 0xF8) << 2 ) ); + } + } + + pScanline += (pData->iColinc * 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA8 = *(pDataline+3); /* get alpha value */ + + if (iA8) /* any opacity at all ? */ + { + if (iA8 == 0xFF) /* fully opaque ? */ + { /* then simply copy the values */ + *(pScanline+1) = (mng_uint8)( ((*(pDataline ) & 0xF8) >> 1 ) | (*(pDataline+1) >> 6 ) ); + *pScanline = (mng_uint8)( ( *(pDataline+2) >> 3 ) | ((*(pDataline+1) & 0xF8) << 2 ) ); + } + else + { /* do alpha composing */ + mng_uint8 iRed, iGreen, iBlue; + + iRed = (mng_uint8)( (*(pScanline+1) & 0xF8) << 1 ); + iGreen = (mng_uint8)( (*(pScanline+1) << 6 ) | ( ((*pScanline) & 0xE0) >> 2 ) ); + iBlue = (mng_uint8)( *(pScanline ) << 3 ); + + MNG_COMPOSE8 (iRed, *(pDataline+0), iA8, iRed ); + MNG_COMPOSE8 (iGreen, *(pDataline+1), iA8, iGreen ); + MNG_COMPOSE8 (iBlue, *(pDataline+2), iA8, iBlue ); + + *(pScanline+1) = (mng_uint8)( ( (iRed & 0xF8) >> 1 ) | ( iGreen >> 6 ) ); + *pScanline = (mng_uint8)( (iBlue >> 3 ) | ( (iGreen & 0xF8) << 2 ) ); + } + } + + pScanline += (pData->iColinc * 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGR555, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#else /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +mng_retcode mng_display_bgr555 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint16 iA16; + mng_uint16 iFGr16, iFGg16, iFGb16; + mng_uint16 iBGr16, iBGg16, iBGb16; + mng_uint8 iA8; + mng_uint8 iBps; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGR555, MNG_LC_START); +#endif + + iBps=(mng_uint8)(pData->bIsRGBA16 ? 2:1); + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2); + pDataline = pData->pRGBArow; /* address source row */ + + /* adjust source row starting-point */ + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << (iBps+1)); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* scale down by dropping the LSB */ + *(pScanline+1) = (mng_uint8)( ((*(pDataline ) & 0xF8) >> 1 ) | (*(pDataline+iBps) >> 6 ) ); + *pScanline = (mng_uint8)( ( *(pDataline+2*iBps) >> 3 ) | ((*(pDataline+iBps) & 0xF8) << 2 ) ); + + pScanline += (pData->iColinc * 2); + pDataline += 4*iBps; + } + } + else + { + if (pData->bIsRGBA16) /* 16-bit input row ? */ + { + + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA16 = mng_get_uint16 (pDataline+6); + + if (iA16) /* any opacity at all ? */ + { + if (iA16 == 0xFFFF) /* fully opaque ? */ + { /* scale down by dropping the LSB */ + *(pScanline+1) = (mng_uint8)( ((*(pDataline ) & 0xF8) >> 1 ) | (*(pDataline+2) >> 6 ) ); + *pScanline = (mng_uint8)( ( *(pDataline+4) >> 3 ) | ((*(pDataline+2) & 0xF8) << 2 ) ); + } + else + { /* get the proper values */ + iFGr16 = mng_get_uint16 (pDataline ); + iFGg16 = mng_get_uint16 (pDataline+2); + iFGb16 = mng_get_uint16 (pDataline+4); + + /* scale background up */ + iBGb16 = (mng_uint8)( (*(pScanline+1) & 0xF8) << 1 ); + iBGg16 = (mng_uint8)( (*(pScanline+1) << 6 ) | ( ((*pScanline) & 0xE0) >> 2 ) ); + iBGr16 = (mng_uint8)( *(pScanline ) << 3 ); + + iBGr16 = (mng_uint16)((mng_uint32)iBGr16 << 8) | iBGr16; + iBGg16 = (mng_uint16)((mng_uint32)iBGg16 << 8) | iBGg16; + iBGb16 = (mng_uint16)((mng_uint32)iBGb16 << 8) | iBGb16; + /* now compose */ + MNG_COMPOSE16(iFGr16, iFGr16, iA16, iBGr16); + MNG_COMPOSE16(iFGg16, iFGg16, iA16, iBGg16); + MNG_COMPOSE16(iFGb16, iFGb16, iA16, iBGb16); + /* and return the composed values */ + *(pScanline+1) = (mng_uint8)( (mng_uint8)(((iFGr16 >> 8) & 0xF8) >> 1 ) | ( (mng_uint8)(iFGg16 >> 8) >> 6 ) ); + *pScanline = (mng_uint8)( (mng_uint8) ((iFGb16 >>11) >> 3 ) | ( ( (mng_uint8)(iFGg16 >> 8) & 0xF8) << 2 ) ); + } + } + + pScanline += (pData->iColinc * 2); + pDataline += 8; + } + } + else + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA8 = *(pDataline+3); /* get alpha value */ + + if (iA8) /* any opacity at all ? */ + { + if (iA8 == 0xFF) /* fully opaque ? */ + { /* then simply copy the values */ + *(pScanline+1) = (mng_uint8)( ((*(pDataline ) & 0xF8) >> 1 ) | (*(pDataline+1) >> 6 ) ); + *pScanline = (mng_uint8)( ( *(pDataline+2) >> 3 ) | ((*(pDataline+1) & 0xF8) << 2 ) ); + } + else + { /* do alpha composing */ + mng_uint8 iRed, iGreen, iBlue; + + iRed = (mng_uint8)( (*(pScanline+1) & 0xF8) << 1 ); + iGreen = (mng_uint8)( (*(pScanline+1) << 6 ) | ( ((*pScanline) & 0xE0) >> 2 ) ); + iBlue = (mng_uint8)( *(pScanline ) << 3 ); + + MNG_COMPOSE8 (iRed, *(pDataline+0), iA8, iRed ); + MNG_COMPOSE8 (iGreen, *(pDataline+1), iA8, iGreen ); + MNG_COMPOSE8 (iBlue, *(pDataline+2), iA8, iBlue ); + + *(pScanline+1) = (mng_uint8)( ( (iRed & 0xF8) >> 1 ) | ( iGreen >> 6 ) ); + *pScanline = (mng_uint8)( (iBlue >> 3 ) | ( (iGreen & 0xF8) << 2 ) ); + } + } + + pScanline += (pData->iColinc * 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGR555, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_OPTIMIZE_FOOTPRINT_COMPOSE */ +#else /* MNG_NO_16BIT_SUPPORT */ +mng_retcode mng_display_bgr555 (mng_datap pData) +{ + mng_uint8p pScanline; + mng_uint8p pDataline; + mng_int32 iX; + mng_uint8 iA8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGR555, MNG_LC_START); +#endif + /* viewable row ? */ + if ((pData->iRow >= pData->iSourcet) && (pData->iRow < pData->iSourceb)) + { /* address destination row */ + pScanline = (mng_uint8p)pData->fGetcanvasline (((mng_handle)pData), + pData->iRow + pData->iDestt - + pData->iSourcet); + /* adjust destination row starting-point */ + pScanline = pScanline + (pData->iCol * 2) + (pData->iDestl * 2); + pDataline = pData->pRGBArow; /* address source row */ + + pDataline = pDataline + ((pData->iSourcel / pData->iColinc) << 2); + + if (pData->bIsOpaque) /* forget about transparency ? */ + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { /* copy the values */ + *(pScanline+1) = (mng_uint8)( ((*(pDataline ) & 0xF8) >> 1 ) | (*(pDataline+1) >> 6 ) ); + *pScanline = (mng_uint8)( ( *(pDataline+2) >> 3 ) | ((*(pDataline+1) & 0xF8) << 2 ) ); + + pScanline += (pData->iColinc * 2); + pDataline += 4; + } + } + } + else + { + { + for (iX = pData->iSourcel + pData->iCol; iX < pData->iSourcer; + iX += pData->iColinc) + { + iA8 = *(pDataline+3); /* get alpha value */ + + if (iA8) /* any opacity at all ? */ + { + if (iA8 == 0xFF) /* fully opaque ? */ + { /* then simply copy the values */ + *(pScanline+1) = (mng_uint8)( ((*(pDataline ) & 0xF8) >> 1 ) | (*(pDataline+1) >> 6 ) ); + *pScanline = (mng_uint8)( ( *(pDataline+2) >> 3 ) | ((*(pDataline+1) & 0xF8) << 2 ) ); + } + else + { /* do alpha composing */ + mng_uint8 iRed, iGreen, iBlue; + + iRed = (mng_uint8)( (*(pScanline+1) & 0xF8) << 1 ); + iGreen = (mng_uint8)( (*(pScanline+1) << 6 ) | ( ((*pScanline) & 0xE0) >> 2 ) ); + iBlue = (mng_uint8)( *(pScanline ) << 3 ); + + MNG_COMPOSE8 (iRed, *(pDataline+0), iA8, iRed ); + MNG_COMPOSE8 (iGreen, *(pDataline+1), iA8, iGreen ); + MNG_COMPOSE8 (iBlue, *(pDataline+2), iA8, iBlue ); + + *(pScanline+1) = (mng_uint8)( ( (iRed & 0xF8) >> 1 ) | ( iGreen >> 6 ) ); + *pScanline = (mng_uint8)( (iBlue >> 3 ) | ( (iGreen & 0xF8) << 2 ) ); + } + } + + pScanline += (pData->iColinc * 2); + pDataline += 4; + } + } + } + } + + check_update_region (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_BGR555, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_16BIT_SUPPORT */ +#endif /* MNG_SKIPCANVAS_BGR555 */ + + +#ifndef MNG_SKIPCHUNK_BACK +/* ************************************************************************** */ +/* * * */ +/* * Background restore routines - restore the background with info from * */ +/* * the BACK and/or bKGD chunk or the app's background canvas * */ +/* * * */ +/* ************************************************************************** */ + +mng_retcode mng_restore_bkgd_backimage (mng_datap pData) +{ + /* save some stuff */ + mng_uint8p pRGBArow = pData->pRGBArow; + mng_int32 iRow = pData->iRow; + mng_int32 iRowsamples = pData->iRowsamples; + + mng_retcode iRetcode; /* work variables */ + mng_uint8p pTemp; + mng_uint8p pWork = pRGBArow; + mng_uint32 iX; + mng_int32 iZ; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RESTORE_BACKIMAGE, MNG_LC_START); +#endif + /* determine row to retrieve */ + pData->iRow = pData->iDestt + iRow + pData->iBackimgoffsy; + + while (pData->iRow >= (mng_int32)pData->iBackimgheight) + pData->iRow -= (mng_int32)pData->iBackimgheight; + /* set width to that of background image */ + pData->iRowsamples = pData->iBackimgwidth; + /* retrieve into alternate buffer ! */ + pData->pRGBArow = pData->pPrevrow; + /* get it then */ + iRetcode = ((mng_retrieverow)pData->fRetrieverow) (pData); + + if (iRetcode) /* on error; bail out */ + return iRetcode; + /* we got the full row; but now need to + paste it into the proper location */ + iX = pData->iDestl - pData->iBackimgoffsx; + + while (iX >= pData->iBackimgwidth) + iX -= pData->iBackimgwidth; + +#ifndef MNG_NO_16BIT_SUPPORT + if (pData->bIsRGBA16) /* 16-bit buffer ? */ + { + pTemp = pData->pPrevrow + (iX << 3); + + for (iZ = (pData->iDestr - pData->iDestl); iZ > 0; iZ--) + { + MNG_COPY (pWork, pTemp, 8); + + pWork += 8; + pTemp += 8; + iX++; + /* reached end of bkgd-image line ? */ + if (iX >= pData->iBackimgwidth) + { + iX = 0; + pTemp = pData->pPrevrow; + } + } + } + else +#endif + { + pTemp = pData->pPrevrow + (iX << 2); + + for (iZ = (pData->iDestr - pData->iDestl); iZ > 0; iZ--) + { + MNG_COPY (pWork, pTemp, 4); + + pWork += 4; + pTemp += 4; + iX++; + /* reached end of bkgd-image line ? */ + if (iX >= pData->iBackimgwidth) + { + iX = 0; + pTemp = pData->pPrevrow; + } + } + } + + pData->pRGBArow = pRGBArow; /* restore original values */ + pData->iRow = iRow; + pData->iRowsamples = iRowsamples; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RESTORE_BACKIMAGE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_restore_bkgd_backcolor (mng_datap pData) +{ + mng_int32 iX; + mng_uint32p pWork32 = (mng_uint32p)pData->pRGBArow; + mng_uint32 iWrite; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RESTORE_BACKCOLOR, MNG_LC_START); +#endif + +#ifdef MNG_BIGENDIAN_SUPPORTED + /* fast way for big endian */ + iWrite = (((mng_uint8)(pData->iBACKred >> 8)) << 24) | + (((mng_uint8)(pData->iBACKgreen >> 8)) << 16) | + (((mng_uint8)(pData->iBACKblue >> 8)) << 8) | + ( 0xFF ); +#elif defined(MNG_LITTLEENDIAN_SUPPORTED) + /* fast way for little endian */ + iWrite = ( 0xFF << 24) | + (((mng_uint8)(pData->iBACKblue >> 8)) << 16) | + (((mng_uint8)(pData->iBACKgreen >> 8)) << 8) | + (((mng_uint8)(pData->iBACKred >> 8)) ); +#else + /* generic way, works on all platforms */ + /* put the data in memory in the correct order */ + { + mng_uint8 aBytes[4]; + aBytes[0] = (mng_uint8)(pData->iBACKred >> 8); + aBytes[1] = (mng_uint8)(pData->iBACKgreen >> 8); + aBytes[2] = (mng_uint8)(pData->iBACKblue >> 8); + aBytes[3] = 0xFF; + /* load that data into a register */ + iWrite = *(mng_uint32*) aBytes; + } +#endif + /* ok; drop the background-color in there */ + for (iX = (pData->iSourcer - pData->iSourcel); iX > 0; iX--) + *pWork32++ = iWrite; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RESTORE_BACKCOLOR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_bKGD +mng_retcode mng_restore_bkgd_bkgd (mng_datap pData) +{ + mng_int32 iX; + mng_uint8p pWork = pData->pRGBArow; + mng_imagep pImage = (mng_imagep)pData->pCurrentobj; + mng_imagedatap pBuf = pImage->pImgbuf; + mng_uint8 iRed = 0; + mng_uint8 iGreen = 0; + mng_uint8 iBlue = 0; + mng_uint32p pWork32 = (mng_uint32p)pWork; + mng_uint32 iWrite; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RESTORE_BKGD, MNG_LC_START); +#endif + + switch (pBuf->iColortype) + { + case 0 : ; /* gray types */ + case 4 : { + mng_uint8 iGray; + +#ifndef MNG_NO_16BIT_SUPPORT + if (pBuf->iBitdepth > 8) + iGray = (mng_uint8)(pBuf->iBKGDgray >> 8); + else +#endif + { +#ifndef MNG_NO_1_2_4BIT_SUPPORT + /* LBR scaling */ + mng_uint8 multiplier[]={0,255,85,0,17,0,0,0,1}; + iGray = (mng_uint8)(multiplier[pBuf->iBitdepth] * pBuf->iBKGDgray); +#else + iGray = (mng_uint8)pBuf->iBKGDgray; +#endif + } + + iRed = iGray; + iGreen = iGray; + iBlue = iGray; + + break; + } + + case 3 : { /* indexed type */ + iRed = pBuf->aPLTEentries [pBuf->iBKGDindex].iRed; + iGreen = pBuf->aPLTEentries [pBuf->iBKGDindex].iGreen; + iBlue = pBuf->aPLTEentries [pBuf->iBKGDindex].iBlue; + + break; + } + + case 2 : ; /* rgb types */ + case 6 : { +#ifndef MNG_NO_16BIT_SUPPORT + if (pBuf->iBitdepth > 8) + { + iRed = (mng_uint8)(pBuf->iBKGDred >> 8); + iGreen = (mng_uint8)(pBuf->iBKGDgreen >> 8); + iBlue = (mng_uint8)(pBuf->iBKGDblue >> 8); + } + else +#endif + { + iRed = (mng_uint8)(pBuf->iBKGDred ); + iGreen = (mng_uint8)(pBuf->iBKGDgreen); + iBlue = (mng_uint8)(pBuf->iBKGDblue ); + } + + break; + } + } + +#ifdef MNG_BIGENDIAN_SUPPORTED + /* fast way for big endian */ + iWrite = (iRed << 24) | + (iGreen << 16) | + (iBlue << 8); +#elif defined(MNG_LITTLEENDIAN_SUPPORTED) + /* fast way for little endian */ + iWrite = (iBlue << 16) | + (iGreen << 8) | + (iRed ); +#else + /* generic way, works on all platforms */ + /* put the data in memory in the correct order */ + { + mng_uint8 aBytes[4]; + aBytes[0] = (mng_uint8)(iRed); + aBytes[1] = (mng_uint8)(iGreen); + aBytes[2] = (mng_uint8)(iBlue); + aBytes[3] = 0x00; + /* load that data into a register */ + iWrite = *(mng_uint32*) aBytes; + } +#endif + /* ok; drop it in there */ + for (iX = (pData->iSourcer - pData->iSourcel); iX > 0; iX--) + *pWork32++ = iWrite; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RESTORE_BKGD, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_restore_bkgd_bgcolor (mng_datap pData) +{ + mng_int32 iX; + mng_uint32p pWork32 = (mng_uint32p)pData->pRGBArow; + mng_uint32 iWrite; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RESTORE_BGCOLOR, MNG_LC_START); +#endif + +#ifdef MNG_BIGENDIAN_SUPPORTED + /* fast way for big endian */ + iWrite = (((mng_uint8)(pData->iBGred >> 8)) << 24) | + (((mng_uint8)(pData->iBGgreen >> 8)) << 16) | + (((mng_uint8)(pData->iBGblue >> 8)) << 8); +#elif defined(MNG_LITTLEENDIAN_SUPPORTED) + /* fast way for little endian */ + iWrite = (((mng_uint8)(pData->iBGblue >> 8)) << 16) | + (((mng_uint8)(pData->iBGgreen >> 8)) << 8) | + (((mng_uint8)(pData->iBGred >> 8)) ); +#else + /* generic way, works on all platforms */ + /* put the data in memory in the correct order */ + { + mng_uint8 aBytes[4]; + aBytes[0] = (mng_uint8)(pData->iBGred >> 8); + aBytes[1] = (mng_uint8)(pData->iBGgreen >> 8); + aBytes[2] = (mng_uint8)(pData->iBGblue >> 8); + aBytes[3] = 0x00; + /* load that data into a register */ + iWrite = *(mng_uint32*) aBytes; + } +#endif + /* ok; drop the background-color in there */ + for (iX = (pData->iSourcer - pData->iSourcel); iX > 0; iX--) + *pWork32++ = iWrite; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RESTORE_BGCOLOR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCANVAS_RGB8 +mng_retcode mng_restore_bkgd_rgb8 (mng_datap pData) +{ + mng_int32 iX; + mng_uint8p pBkgd; + mng_uint8p pWork = pData->pRGBArow; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RESTORE_RGB8, MNG_LC_START); +#endif + + if (pData->fGetbkgdline) /* can we access the background ? */ + { /* point to the right pixel then */ + pBkgd = (mng_uint8p)pData->fGetbkgdline ((mng_handle)pData, + pData->iRow + pData->iDestt) + + (3 * pData->iDestl); + + for (iX = (pData->iSourcer - pData->iSourcel); iX > 0; iX--) + { + *pWork = *pBkgd; /* ok; copy the pixel */ + *(pWork+1) = *(pBkgd+1); + *(pWork+2) = *(pBkgd+2); + *(pWork+3) = 0x00; /* transparant for alpha-canvasses */ + + pWork += 4; + pBkgd += 3; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RESTORE_RGB8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SKIPCANVAS_RGB8 */ + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCANVAS_BGR8 +mng_retcode mng_restore_bkgd_bgr8 (mng_datap pData) +{ + mng_int32 iX; + mng_uint8p pBkgd; + mng_uint8p pWork = pData->pRGBArow; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RESTORE_BGR8, MNG_LC_START); +#endif + + if (pData->fGetbkgdline) /* can we access the background ? */ + { /* point to the right pixel then */ + pBkgd = (mng_uint8p)pData->fGetbkgdline ((mng_handle)pData, + pData->iRow + pData->iDestt) + + (3 * pData->iDestl); + + for (iX = (pData->iSourcer - pData->iSourcel); iX > 0; iX--) + { + *pWork = *(pBkgd+2); /* ok; copy the pixel */ + *(pWork+1) = *(pBkgd+1); + *(pWork+2) = *pBkgd; + *(pWork+3) = 0x00; /* transparant for alpha-canvasses */ + + pWork += 4; + pBkgd += 3; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RESTORE_BGR8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SKIPCANVAS_BGR8 */ + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCANVAS_BGRX8 +mng_retcode mng_restore_bkgd_bgrx8 (mng_datap pData) +{ + mng_int32 iX; + mng_uint8p pBkgd; + mng_uint8p pWork = pData->pRGBArow; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RESTORE_BGRX8, MNG_LC_START); +#endif + + if (pData->fGetbkgdline) /* can we access the background ? */ + { /* point to the right pixel then */ + pBkgd = (mng_uint8p)pData->fGetbkgdline ((mng_handle)pData, + pData->iRow + pData->iDestt) + + (3 * pData->iDestl); + + for (iX = (pData->iSourcer - pData->iSourcel); iX > 0; iX--) + { + *pWork = *(pBkgd+2); /* ok; copy the pixel */ + *(pWork+1) = *(pBkgd+1); + *(pWork+2) = *pBkgd; + *(pWork+3) = 0x00; /* transparant for alpha-canvasses */ + + pWork += 4; + pBkgd += 4; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RESTORE_BGRX8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SKIPCANVAS_BGRX8 */ + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCANVAS_BGR565 +mng_retcode mng_restore_bkgd_bgr565 (mng_datap pData) +{ + mng_int32 iX; + mng_uint8p pBkgd; + mng_uint8p pWork = pData->pRGBArow; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RESTORE_BGR565, MNG_LC_START); +#endif + + if (pData->fGetbkgdline) /* can we access the background ? */ + { /* point to the right pixel then */ + pBkgd = (mng_uint8p)pData->fGetbkgdline ((mng_handle)pData, + pData->iRow + pData->iDestt) + + (3 * pData->iDestl); + + for (iX = (pData->iSourcer - pData->iSourcel); iX > 0; iX--) + { + *pWork = (mng_uint8)( *(pBkgd+1) & 0xF8); /* ok; copy the pixel */ + *(pWork+1) = (mng_uint8)( (*(pBkgd+1) << 5 ) | ( ((*pBkgd)&0xE0)>>3 ) ); + *(pWork+2) = (mng_uint8)( *(pBkgd) << 3 ); + *(pWork+3) = 0x00; /* transparant for alpha-canvasses */ + + pWork += 4; + pBkgd += 2; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RESTORE_BGR565, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SKIPCANVAS_BGR565 */ + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCANVAS_RGB565 +mng_retcode mng_restore_bkgd_rgb565 (mng_datap pData) +{ + mng_int32 iX; + mng_uint8p pBkgd; + mng_uint8p pWork = pData->pRGBArow; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RESTORE_RGB565, MNG_LC_START); +#endif + + if (pData->fGetbkgdline) /* can we access the background ? */ + { /* point to the right pixel then */ + pBkgd = (mng_uint8p)pData->fGetbkgdline ((mng_handle)pData, + pData->iRow + pData->iDestt) + + (3 * pData->iDestl); + + for (iX = (pData->iSourcer - pData->iSourcel); iX > 0; iX--) + { + *pWork = (mng_uint8)( *(pBkgd)&0xF8); /* ok; copy the pixel */ + *(pWork+1) = (mng_uint8)( (*(pBkgd+1) << 5) | ( ((*pBkgd)&0xE0)>>3 ) ); + *(pWork+2) = (mng_uint8)( *(pBkgd+1) << 3); + *(pWork+3) = 0x00; /* transparant for alpha-canvasses */ + + pWork += 4; + pBkgd += 2; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RESTORE_RGB565, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SKIPCANVAS_RBB565 */ + + +/* ************************************************************************** */ +/* * * */ +/* * Row retrieval routines - retrieve processed & uncompressed row-data * */ +/* * from the current "object" * */ +/* * * */ +/* ************************************************************************** */ + +/* TODO: a serious optimization is to retrieve only those pixels that will + actually be displayed; this would require changes in + the "display_image" routine (in mng_display.c) & + all the "retrieve_xxx" routines below & + the "display_xxx" routines above !!!!! + NOTE that "correct_xxx" routines would not require modification */ + +mng_retcode mng_retrieve_g8 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pRetrieveobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pRGBArow; + mng_int32 iX; + mng_uint8 iG; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RETRIEVE_G8, MNG_LC_START); +#endif + + pRGBArow = pData->pRGBArow; /* temporary work pointers */ + pWorkrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize); + + if (pBuf->bHasTRNS) /* tRNS in buffer ? */ + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + iG = *pWorkrow; /* get the gray-value */ + /* is it transparent ? */ + if ((mng_uint16)iG == pBuf->iTRNSgray) + { + *pRGBArow = 0x00; /* nuttin to display */ + *(pRGBArow+1) = 0x00; + *(pRGBArow+2) = 0x00; + *(pRGBArow+3) = 0x00; + } + else + { +#ifndef MNG_NO_1_2_4BIT_SUPPORT + mng_uint8 multiplier[]={0,255,85,0,17,0,0,0,1}; + iG = (mng_uint8)(iG * multiplier[pBuf->iBitdepth]); +#endif + + *pRGBArow = iG; /* put in intermediate row */ + *(pRGBArow+1) = iG; + *(pRGBArow+2) = iG; + *(pRGBArow+3) = 0xFF; + } + + pWorkrow++; /* next pixel */ + pRGBArow += 4; + } + } + else + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { +#ifndef MNG_NO_1_2_4BIT_SUPPORT + mng_uint8 multiplier[]={0,255,85,0,17,0,0,0,1}; /* LBR scaling */ + iG = (mng_uint8)(multiplier[pBuf->iBitdepth] * *pWorkrow); +#else + iG = *pWorkrow; /* get the gray-value */ +#endif + + *pRGBArow = iG; /* put in intermediate row */ + *(pRGBArow+1) = iG; + *(pRGBArow+2) = iG; + *(pRGBArow+3) = 0xFF; + + pWorkrow++; /* next pixel */ + pRGBArow += 4; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RETRIEVE_G8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_retrieve_g16 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pRetrieveobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pRGBArow; + mng_int32 iX; + mng_uint16 iG; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RETRIEVE_G16, MNG_LC_START); +#endif + /* temporary work pointers */ + pRGBArow = pData->pRGBArow; + pWorkrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize); + + if (pBuf->bHasTRNS) /* tRNS in buffer ? */ + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + iG = mng_get_uint16 (pWorkrow); /* get the gray-value */ + /* is it transparent ? */ + if (iG == pBuf->iTRNSgray) + { /* nuttin to display */ + mng_put_uint16 (pRGBArow, 0x0000); + mng_put_uint16 (pRGBArow+2, 0x0000); + mng_put_uint16 (pRGBArow+4, 0x0000); + mng_put_uint16 (pRGBArow+6, 0x0000); + } + else + { /* put in intermediate row */ + mng_put_uint16 (pRGBArow, iG); + mng_put_uint16 (pRGBArow+2, iG); + mng_put_uint16 (pRGBArow+4, iG); + mng_put_uint16 (pRGBArow+6, 0xFFFF); + } + + pWorkrow += 2; /* next pixel */ + pRGBArow += 8; + } + } + else + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + iG = mng_get_uint16 (pWorkrow); /* get the gray-value */ + + mng_put_uint16 (pRGBArow, iG); /* and put in intermediate row */ + mng_put_uint16 (pRGBArow+2, iG); + mng_put_uint16 (pRGBArow+4, iG); + mng_put_uint16 (pRGBArow+6, 0xFFFF); + + pWorkrow += 2; /* next pixel */ + pRGBArow += 8; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RETRIEVE_G16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_retrieve_rgb8 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pRetrieveobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pRGBArow; + mng_int32 iX; + mng_uint8 iR, iG, iB; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RETRIEVE_RGB8, MNG_LC_START); +#endif + + pRGBArow = pData->pRGBArow; /* temporary work pointers */ + pWorkrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize); + + if (pBuf->bHasTRNS) /* tRNS in buffer ? */ + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + iR = *pWorkrow; /* get the rgb-values */ + iG = *(pWorkrow+1); + iB = *(pWorkrow+2); + /* is it transparent ? */ + if (((mng_uint16)iR == pBuf->iTRNSred ) && + ((mng_uint16)iG == pBuf->iTRNSgreen) && + ((mng_uint16)iB == pBuf->iTRNSblue ) ) + { + *pRGBArow = 0x00; /* nothing to display */ + *(pRGBArow+1) = 0x00; + *(pRGBArow+2) = 0x00; + *(pRGBArow+3) = 0x00; + } + else + { + *pRGBArow = iR; /* put in intermediate row */ + *(pRGBArow+1) = iG; + *(pRGBArow+2) = iB; + *(pRGBArow+3) = 0xFF; + } + + pWorkrow += 3; /* next pixel */ + pRGBArow += 4; + } + } + else + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pRGBArow = *pWorkrow; /* just copy the pixel */ + *(pRGBArow+1) = *(pWorkrow+1); + *(pRGBArow+2) = *(pWorkrow+2); + *(pRGBArow+3) = 0xFF; + + pWorkrow += 3; /* next pixel */ + pRGBArow += 4; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RETRIEVE_RGB8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_retrieve_rgb16 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pRetrieveobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pRGBArow; + mng_int32 iX; + mng_uint16 iR, iG, iB; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RETRIEVE_RGB16, MNG_LC_START); +#endif + /* temporary work pointers */ + pRGBArow = pData->pRGBArow; + pWorkrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize); + + if (pBuf->bHasTRNS) /* tRNS in buffer ? */ + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + iR = mng_get_uint16 (pWorkrow); /* get the rgb-values */ + iG = mng_get_uint16 (pWorkrow+2); + iB = mng_get_uint16 (pWorkrow+4); + /* is it transparent ? */ + if ((iR == pBuf->iTRNSred ) && + (iG == pBuf->iTRNSgreen) && + (iB == pBuf->iTRNSblue ) ) + { /* nothing to display */ + mng_put_uint16 (pRGBArow, 0x0000); + mng_put_uint16 (pRGBArow+2, 0x0000); + mng_put_uint16 (pRGBArow+4, 0x0000); + mng_put_uint16 (pRGBArow+6, 0x0000); + } + else + { /* put in intermediate row */ + mng_put_uint16 (pRGBArow, iR); + mng_put_uint16 (pRGBArow+2, iG); + mng_put_uint16 (pRGBArow+4, iB); + mng_put_uint16 (pRGBArow+6, 0xFFFF); + } + + pWorkrow += 6; /* next pixel */ + pRGBArow += 8; + } + } + else + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { /* just copy the pixel */ + mng_put_uint16 (pRGBArow, mng_get_uint16 (pWorkrow )); + mng_put_uint16 (pRGBArow+2, mng_get_uint16 (pWorkrow+2)); + mng_put_uint16 (pRGBArow+4, mng_get_uint16 (pWorkrow+4)); + mng_put_uint16 (pRGBArow+6, 0xFFFF); + + pWorkrow += 6; /* next pixel */ + pRGBArow += 8; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RETRIEVE_RGB16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_retrieve_idx8 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pRetrieveobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pRGBArow; + mng_int32 iX; + mng_uint8 iQ; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RETRIEVE_IDX8, MNG_LC_START); +#endif + + pRGBArow = pData->pRGBArow; /* temporary work pointers */ + pWorkrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize); + + if (pBuf->bHasTRNS) /* tRNS in buffer ? */ + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + iQ = *pWorkrow; /* get the index */ + /* is it valid ? */ + if ((mng_uint32)iQ < pBuf->iPLTEcount) + { /* put in intermediate row */ + *pRGBArow = pBuf->aPLTEentries [iQ].iRed; + *(pRGBArow+1) = pBuf->aPLTEentries [iQ].iGreen; + *(pRGBArow+2) = pBuf->aPLTEentries [iQ].iBlue; + /* transparency for this index ? */ + if ((mng_uint32)iQ < pBuf->iTRNScount) + *(pRGBArow+3) = pBuf->aTRNSentries [iQ]; + else + *(pRGBArow+3) = 0xFF; + } + else + MNG_ERROR (pData, MNG_PLTEINDEXERROR); + + pWorkrow++; /* next pixel */ + pRGBArow += 4; + } + } + else + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + iQ = *pWorkrow; /* get the index */ + /* is it valid ? */ + if ((mng_uint32)iQ < pBuf->iPLTEcount) + { /* put in intermediate row */ + *pRGBArow = pBuf->aPLTEentries [iQ].iRed; + *(pRGBArow+1) = pBuf->aPLTEentries [iQ].iGreen; + *(pRGBArow+2) = pBuf->aPLTEentries [iQ].iBlue; + *(pRGBArow+3) = 0xFF; + } + else + MNG_ERROR (pData, MNG_PLTEINDEXERROR); + + pWorkrow++; /* next pixel */ + pRGBArow += 4; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RETRIEVE_IDX8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_retrieve_ga8 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pRetrieveobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pRGBArow; + mng_int32 iX; + mng_uint8 iG; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RETRIEVE_GA8, MNG_LC_START); +#endif + + pRGBArow = pData->pRGBArow; /* temporary work pointers */ + pWorkrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize); + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + iG = *pWorkrow; /* get the gray-value */ + *pRGBArow = iG; /* put in intermediate row */ + *(pRGBArow+1) = iG; + *(pRGBArow+2) = iG; + *(pRGBArow+3) = *(pWorkrow+1); + + pWorkrow += 2; /* next pixel */ + pRGBArow += 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RETRIEVE_GA8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_retrieve_ga16 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pRetrieveobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pRGBArow; + mng_int32 iX; + mng_uint16 iG; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RETRIEVE_GA16, MNG_LC_START); +#endif + /* temporary work pointers */ + pRGBArow = pData->pRGBArow; + pWorkrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize); + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + iG = mng_get_uint16 (pWorkrow); /* get the gray-value */ + + mng_put_uint16 (pRGBArow, iG); /* and put in intermediate row */ + mng_put_uint16 (pRGBArow+2, iG); + mng_put_uint16 (pRGBArow+4, iG); + mng_put_uint16 (pRGBArow+6, mng_get_uint16 (pWorkrow+2)); + + pWorkrow += 4; /* next pixel */ + pRGBArow += 8; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RETRIEVE_GA16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_retrieve_rgba8 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pRetrieveobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pRGBArow; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RETRIEVE_RGBA8, MNG_LC_START); +#endif + + pRGBArow = pData->pRGBArow; /* temporary work pointers */ + pWorkrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize); + /* can't be easier than this ! */ + MNG_COPY (pRGBArow, pWorkrow, pBuf->iRowsize); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RETRIEVE_RGBA8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_retrieve_rgba16 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pRetrieveobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pRGBArow; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RETRIEVE_RGBA16, MNG_LC_START); +#endif + /* temporary work pointers */ + pRGBArow = pData->pRGBArow; + pWorkrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize); + /* can't be easier than this ! */ + MNG_COPY (pRGBArow, pWorkrow, pBuf->iRowsize); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RETRIEVE_RGBA16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ +/* * * */ +/* * Row storage routines - store processed & uncompressed row-data * */ +/* * into the current "object" * */ +/* * * */ +/* ************************************************************************** */ + +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_store_g1 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + mng_uint8 iB; + mng_uint8 iM; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_G1, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + iM = 0; /* start at pixel 0 */ + iB = 0; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0x80; + } + + if (iB & iM) /* is it white ? */ + *pOutrow = 0x01; /* white */ + else + *pOutrow = 0x00; /* black */ + + pOutrow += pData->iColinc; /* next pixel */ + iM >>= 1; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_G1, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_store_g2 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + mng_uint8 iB; + mng_uint8 iM; + mng_uint32 iS; + mng_uint8 iQ; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_G2, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + iM = 0; /* start at pixel 0 */ + iB = 0; + iS = 0; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0xC0; + iS = 6; + } + + iQ = (mng_uint8)((iB & iM) >> iS); /* get the gray level */ + *pOutrow = iQ; /* put in object buffer */ + + pOutrow += pData->iColinc; /* next pixel */ + iM >>= 2; + iS -= 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_G2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_store_g4 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + mng_uint8 iB; + mng_uint8 iM; + mng_uint32 iS; + mng_uint8 iQ; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_G4, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + iM = 0; /* start at pixel 0 */ + iB = 0; + iS = 0; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0xF0; + iS = 4; + } + + iQ = (mng_uint8)((iB & iM) >> iS); /* get the gray level */ + *pOutrow = iQ; /* put in object buffer */ + + pOutrow += pData->iColinc; /* next pixel */ + iM >>= 4; + iS -= 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_G4, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_store_g8 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_G8, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = *pWorkrow; /* put in object buffer */ + + pOutrow += pData->iColinc; /* next pixel */ + pWorkrow++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_G8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_store_g16 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_G16, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { /* copy into object buffer */ + mng_put_uint16 (pOutrow, mng_get_uint16 (pWorkrow)); + + pOutrow += (pData->iColinc << 1); /* next pixel */ + pWorkrow += 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_G16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_store_rgb8 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_RGB8, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = *pWorkrow; /* copy the RGB bytes */ + *(pOutrow+1) = *(pWorkrow+1); + *(pOutrow+2) = *(pWorkrow+2); + + pWorkrow += 3; /* next pixel */ + pOutrow += (pData->iColinc * 3); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_RGB8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_store_rgb16 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_RGB16, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + MNG_COPY (pOutrow, pWorkrow, 6); /* copy the RGB bytes */ + + pWorkrow += 6; /* next pixel */ + pOutrow += (pData->iColinc * 6); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_RGB16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_store_idx1 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + mng_uint8 iB; + mng_uint8 iM; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_IDX1, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + iM = 0; /* start at pixel 0 */ + iB = 0; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0x80; + } + + if (iB & iM) /* store the index */ + *pOutrow = 0x01; + else + *pOutrow = 0x00; + + pOutrow += pData->iColinc; /* next pixel */ + iM >>= 1; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_IDX1, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_store_idx2 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + mng_uint8 iB; + mng_uint8 iM; + mng_uint32 iS; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_IDX2, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + iM = 0; /* start at pixel 0 */ + iB = 0; + iS = 0; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0xC0; + iS = 6; + } + /* store the index */ + *pOutrow = (mng_uint8)((iB & iM) >> iS); + + pOutrow += pData->iColinc; /* next pixel */ + iM >>= 2; + iS -= 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_IDX2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_store_idx4 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + mng_uint8 iB; + mng_uint8 iM; + mng_uint32 iS; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_IDX4, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + iM = 0; /* start at pixel 0 */ + iB = 0; + iS = 0; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0xF0; + iS = 4; + } + /* store the index */ + *pOutrow = (mng_uint8)((iB & iM) >> iS); + + pOutrow += pData->iColinc; /* next pixel */ + iM >>= 4; + iS -= 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_IDX4, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_store_idx8 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_IDX8, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = *pWorkrow; /* put in object buffer */ + + pOutrow += pData->iColinc; /* next pixel */ + pWorkrow++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_IDX8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_store_ga8 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_GA8, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = *pWorkrow; /* copy the GA bytes */ + *(pOutrow+1) = *(pWorkrow+1); + + pWorkrow += 2; /* next pixel */ + pOutrow += (pData->iColinc << 1); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_GA8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_store_ga16 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_GA16, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + MNG_COPY (pOutrow, pWorkrow, 4); /* copy the GA bytes */ + + pWorkrow += 4; /* next pixel */ + pOutrow += (pData->iColinc << 2); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_GA16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_store_rgba8 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_RGBA8, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = *pWorkrow; /* copy the RGBA bytes */ + *(pOutrow+1) = *(pWorkrow+1); + *(pOutrow+2) = *(pWorkrow+2); + *(pOutrow+3) = *(pWorkrow+3); + + pWorkrow += 4; /* next pixel */ + pOutrow += (pData->iColinc << 2); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_RGBA8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_store_rgba16 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_RGBA16, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + MNG_COPY (pOutrow, pWorkrow, 8); /* copy the RGBA bytes */ + + pWorkrow += 8; /* next pixel */ + pOutrow += (pData->iColinc << 3); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_RGBA16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ +/* * * */ +/* * Row storage routines (JPEG) - store processed & uncompressed row-data * */ +/* * into the current "object" * */ +/* * * */ +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG + +/* ************************************************************************** */ + +mng_retcode mng_store_jpeg_g8 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8, MNG_LC_START); +#endif + + pWorkrow = pData->pJPEGrow; /* temporary work pointers */ + pOutrow = pBuf->pImgdata + (pData->iJPEGrow * pBuf->iRowsize); + /* easy as pie ... */ + MNG_COPY (pOutrow, pWorkrow, pData->iRowsamples); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8, MNG_LC_END); +#endif + + return mng_next_jpeg_row (pData); /* we've got one more row of gray-samples */ +} + +/* ************************************************************************** */ + +mng_retcode mng_store_jpeg_rgb8 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; +#if RGB_PIXELSIZE != 3 + mng_int32 iX; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8, MNG_LC_START); +#endif + + pWorkrow = pData->pJPEGrow; /* temporary work pointers */ + pOutrow = pBuf->pImgdata + (pData->iJPEGrow * pBuf->iRowsize); + +#if RGB_PIXELSIZE == 3 + /* easy as pie ... */ + MNG_COPY (pOutrow, pWorkrow, pData->iRowsamples * 3); +#else +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = *pWorkrow; /* copy pixel into object buffer */ + *(pOutrow+1) = *(pWorkrow+1); + *(pOutrow+2) = *(pWorkrow+2); + + pOutrow += 3; /* next pixel */ + pWorkrow += RGB_PIXELSIZE; + } +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8, MNG_LC_END); +#endif + + return mng_next_jpeg_row (pData); /* we've got one more row of rgb-samples */ +} + +/* ************************************************************************** */ + +mng_retcode mng_store_jpeg_ga8 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_GA8, MNG_LC_START); +#endif + + pWorkrow = pData->pJPEGrow; /* temporary work pointers */ + pOutrow = pBuf->pImgdata + (pData->iJPEGrow * pBuf->iRowsize); + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = *pWorkrow; /* copy into object buffer */ + + pOutrow += 2; /* next pixel */ + pWorkrow++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_GA8, MNG_LC_END); +#endif + + return mng_next_jpeg_row (pData); /* we've got one more row of gray-samples */ +} + +/* ************************************************************************** */ + +mng_retcode mng_store_jpeg_rgba8 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGBA8, MNG_LC_START); +#endif + + pWorkrow = pData->pJPEGrow; /* temporary work pointers */ + pOutrow = pBuf->pImgdata + (pData->iJPEGrow * pBuf->iRowsize); + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = *pWorkrow; /* copy pixel into object buffer */ + *(pOutrow+1) = *(pWorkrow+1); + *(pOutrow+2) = *(pWorkrow+2); + + pOutrow += 4; /* next pixel */ + pWorkrow += RGB_PIXELSIZE; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGBA8, MNG_LC_END); +#endif + + return mng_next_jpeg_row (pData); /* we've got one more row of rgb-samples */ +} + +/* ************************************************************************** */ + +mng_retcode mng_store_jpeg_g8_alpha (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8_ALPHA, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pJPEGrow2; + pOutrow = pBuf->pImgdata + (pData->iJPEGalpharow * pBuf->iRowsize) + 1; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = *pWorkrow; /* put in object buffer */ + + pOutrow += 2; /* next pixel */ + pWorkrow++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8_ALPHA, MNG_LC_END); +#endif + /* we've got one more row of alpha-samples */ + return mng_next_jpeg_alpharow (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_store_jpeg_rgb8_alpha (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8_ALPHA, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pJPEGrow2; + pOutrow = pBuf->pImgdata + (pData->iJPEGalpharow * pBuf->iRowsize) + 3; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = *pWorkrow; /* put in object buffer */ + + pOutrow += 4; /* next pixel */ + pWorkrow++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8_ALPHA, MNG_LC_END); +#endif + /* we've got one more row of alpha-samples */ + return mng_next_jpeg_alpharow (pData); +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_store_jpeg_g8_a1 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + mng_uint8 iB; + mng_uint8 iM; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8_A1, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + 1; + iM = 0; /* start at pixel 0 */ + iB = 0; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0x80; + } + + if (iB & iM) /* is it opaque ? */ + *pOutrow = 0xFF; /* opaque */ + else + *pOutrow = 0x00; /* transparent */ + + pOutrow += 2; /* next pixel */ + iM >>= 1; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8_A1, MNG_LC_END); +#endif + /* we've got one more row of alpha-samples */ + return mng_next_jpeg_alpharow (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_store_jpeg_g8_a2 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + mng_uint8 iB; + mng_uint8 iM; + mng_uint32 iS; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8_A2, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + 1; + iM = 0; /* start at pixel 0 */ + iB = 0; + iS = 0; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0xC0; + iS = 6; + } + +#ifdef MNG_OPTIMIZE_FOOTPRINT_SWITCH + { + const mng_uint8 alpha_level[4] = { 0x00, 0x55, 0xAA, 0xFF}; + *pOutrow = alpha_level[((iB & iM) >> iS)] ; + } +#else + switch ((iB & iM) >> iS) /* determine the alpha level */ + { + case 0x03 : { *pOutrow = 0xFF; break; } + case 0x02 : { *pOutrow = 0xAA; break; } + case 0x01 : { *pOutrow = 0x55; break; } + default : { *pOutrow = 0x00; } + } +#endif + + pOutrow += 2; /* next pixel */ + iM >>= 2; + iS -= 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8_A2, MNG_LC_END); +#endif + /* we've got one more row of alpha-samples */ + return mng_next_jpeg_alpharow (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_store_jpeg_g8_a4 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + mng_uint8 iB; + mng_uint8 iM; + mng_uint32 iS; + mng_uint8 iQ; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8_A4, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + 1; + iM = 0; /* start at pixel 0 */ + iB = 0; + iS = 0; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0xF0; + iS = 4; + } + /* get the alpha level */ + iQ = (mng_uint8)((iB & iM) >> iS); + iQ = (mng_uint8)(iQ + (iQ << 4)); /* expand to 8-bit by replication */ + + *pOutrow = iQ; /* put in object buffer */ + + pOutrow += 2; /* next pixel */ + iM >>= 4; + iS -= 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8_A4, MNG_LC_END); +#endif + /* we've got one more row of alpha-samples */ + return mng_next_jpeg_alpharow (pData); +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_store_jpeg_g8_a8 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8_A8, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + 1; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = *pWorkrow; /* put in object buffer */ + + pOutrow += 2; /* next pixel */ + pWorkrow++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8_A8, MNG_LC_END); +#endif + /* we've got one more row of alpha-samples */ + return mng_next_jpeg_alpharow (pData); +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_store_jpeg_g8_a16 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8_A16, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + 1; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = *pWorkrow; /* only high-order byte! */ + + pOutrow += 2; /* next pixel */ + pWorkrow += 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_G8_A16, MNG_LC_END); +#endif + /* we've got one more row of alpha-samples */ + return mng_next_jpeg_alpharow (pData); +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_store_jpeg_rgb8_a1 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + mng_uint8 iB; + mng_uint8 iM; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8_A1, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + 3; + iM = 0; /* start at pixel 0 */ + iB = 0; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0x80; + } + + if (iB & iM) /* is it opaque ? */ + *pOutrow = 0xFF; /* opaque */ + else + *pOutrow = 0x00; /* transparent */ + + pOutrow += 4; /* next pixel */ + iM >>= 1; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8_A1, MNG_LC_END); +#endif + /* we've got one more row of alpha-samples */ + return mng_next_jpeg_alpharow (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_store_jpeg_rgb8_a2 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + mng_uint8 iB; + mng_uint8 iM; + mng_uint32 iS; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8_A2, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + 3; + iM = 0; /* start at pixel 0 */ + iB = 0; + iS = 0; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0xC0; + iS = 6; + } + +#ifdef MNG_OPTIMIZE_FOOTPRINT_SWITCH + { + const mng_uint8 alpha_level[4] = { 0x00, 0x55, 0xAA, 0xFF}; + *pOutrow = alpha_level[((iB & iM) >> iS)] ; + } +#else + switch ((iB & iM) >> iS) /* determine the alpha level */ + { + case 0x03 : { *pOutrow = 0xFF; break; } + case 0x02 : { *pOutrow = 0xAA; break; } + case 0x01 : { *pOutrow = 0x55; break; } + default : { *pOutrow = 0x00; } + } +#endif + + pOutrow += 4; /* next pixel */ + iM >>= 2; + iS -= 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8_A2, MNG_LC_END); +#endif + /* we've got one more row of alpha-samples */ + return mng_next_jpeg_alpharow (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_store_jpeg_rgb8_a4 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + mng_uint8 iB; + mng_uint8 iM; + mng_uint32 iS; + mng_uint8 iQ; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8_A4, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + 3; + iM = 0; /* start at pixel 0 */ + iB = 0; + iS = 0; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0xF0; + iS = 4; + } + /* get the alpha level */ + iQ = (mng_uint8)((iB & iM) >> iS); + iQ = (mng_uint8)(iQ + (iQ << 4)); /* expand to 8-bit by replication */ + + *pOutrow = iQ; /* put in object buffer */ + + pOutrow += 4; /* next pixel */ + iM >>= 4; + iS -= 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8_A4, MNG_LC_END); +#endif + /* we've got one more row of alpha-samples */ + return mng_next_jpeg_alpharow (pData); +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_store_jpeg_rgb8_a8 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8_A8, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + 3; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = *pWorkrow; /* put in buffer */ + + pOutrow += 4; /* next pixel */ + pWorkrow++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8_A8, MNG_LC_END); +#endif + /* we've got one more row of alpha-samples */ + return mng_next_jpeg_alpharow (pData); +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_store_jpeg_rgb8_a16 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8_A16, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + 3; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = *pWorkrow; /* only high-order byte */ + + pOutrow += 4; /* next pixel */ + pWorkrow += 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_RGB8_A16, MNG_LC_END); +#endif + /* we've got one more row of alpha-samples */ + return mng_next_jpeg_alpharow (pData); +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_JPEG12 +mng_retcode mng_store_jpeg_g12_a1 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + mng_uint8 iB; + mng_uint8 iM; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_G12_A1, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + 2; + iM = 0; /* start at pixel 0 */ + iB = 0; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0x80; + } + + if (iB & iM) /* opaque ? */ + mng_put_uint16 (pOutrow, 0xFFFF);/* opaque */ + else + mng_put_uint16 (pOutrow, 0x0000);/* transparent */ + + pOutrow += 4; /* next pixel */ + iM >>= 1; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_G12_A1, MNG_LC_END); +#endif + /* we've got one more row of alpha-samples */ + return mng_next_jpeg_alpharow (pData); +} +#endif /* MNG_SUPPORT_JPEG12 */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_JPEG12 +mng_retcode mng_store_jpeg_g12_a2 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + mng_uint8 iB; + mng_uint8 iM; + mng_uint32 iS; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_G12_A2, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + 2; + iM = 0; /* start at pixel 0 */ + iB = 0; + iS = 0; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0xC0; + iS = 6; + } + +#ifdef MNG_OPTIMIZE_FOOTPRINT_SWITCH + { + const mng_uint16 gray_level[4] = { 0x0000, 0x5555, 0xAAAA, 0xFFFF}; + mng_put_uint16 (pOutrow, gray_level[((iB & iM) >> iS)]) ; + } +#else + switch ((iB & iM) >> iS) /* determine the gray level */ + { + case 0x03 : { mng_put_uint16 (pOutrow, 0xFFFF); break; } + case 0x02 : { mng_put_uint16 (pOutrow, 0xAAAA); break; } + case 0x01 : { mng_put_uint16 (pOutrow, 0x5555); break; } + default : { mng_put_uint16 (pOutrow, 0x0000); } + } +#endif + + pOutrow += 4; /* next pixel */ + iM >>= 2; + iS -= 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_G12_A2, MNG_LC_END); +#endif + /* we've got one more row of alpha-samples */ + return mng_next_jpeg_alpharow (pData); +} +#endif /* MNG_SUPPORT_JPEG12 */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_JPEG12 +mng_retcode mng_store_jpeg_g12_a4 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + mng_uint8 iB; + mng_uint8 iM; + mng_uint32 iS; + mng_uint16 iQ; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_G12_A4, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + 2; + iM = 0; /* start at pixel 0 */ + iB = 0; + iS = 0; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0xF0; + iS = 4; + } + /* get the gray level */ + iQ = (mng_uint16)((iB & iM) >> iS); + iQ = (mng_uint16)(iQ + (iQ << 4)); /* expand to 16-bit by replication */ + iQ = (mng_uint16)(iQ + (iQ << 8)); + /* put in object buffer */ + mng_put_uint16 (pOutrow, iQ); + + pOutrow += 4; /* next pixel */ + iM >>= 4; + iS -= 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_G12_A4, MNG_LC_END); +#endif + /* we've got one more row of alpha-samples */ + return mng_next_jpeg_alpharow (pData); +} +#endif /* MNG_SUPPORT_JPEG12 */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_JPEG12 +mng_retcode mng_store_jpeg_g12_a8 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + mng_uint16 iW; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_G12_A8, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + 2; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + iW = (mng_uint16)(*pWorkrow); /* get input byte */ + iW = (mng_uint16)(iW + (iW << 8)); /* expand to 16-bit by replication */ + + mng_put_uint16 (pOutrow, iW); /* put in object buffer */ + + pOutrow += 4; /* next pixel */ + pWorkrow++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_G12_A8, MNG_LC_END); +#endif + /* we've got one more row of alpha-samples */ + return mng_next_jpeg_alpharow (pData); +} +#endif /* MNG_SUPPORT_JPEG12 */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_JPEG12 +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_store_jpeg_g12_a16 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_G12_A16, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + 2; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { /* copy it */ + mng_put_uint16 (pOutrow, mng_get_uint16 (pWorkrow)); + + pOutrow += 4; /* next pixel */ + pWorkrow += 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_STORE_JPEG_G12_A16, MNG_LC_END); +#endif + /* we've got one more row of alpha-samples */ + return mng_next_jpeg_alpharow (pData); +} +#endif +#endif /* MNG_SUPPORT_JPEG12 */ + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_JNG */ + +#ifndef MNG_NO_DELTA_PNG +/* ************************************************************************** */ +/* * * */ +/* * Delta-image row routines - apply the processed & uncompressed row-data * */ +/* * onto the target "object" * */ +/* * * */ +/* ************************************************************************** */ + +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_delta_g1 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + mng_uint8 iB; + mng_uint8 iM; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_G1, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iDeltaBlocky * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + + (pData->iDeltaBlockx * pBuf->iSamplesize); + iM = 0; /* start at pixel 0 */ + iB = 0; + /* pixel replace ? */ + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0x80; + } + + if (iB & iM) /* is it white ? */ + *pOutrow = 0xFF; /* white */ + else + *pOutrow = 0x00; /* black */ + + pOutrow += pData->iColinc; /* next pixel */ + iM >>= 1; + } + } + else + { /* pixel add ! */ +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0x80; + } + + if (iB & iM) /* invert if it is white ? */ + *pOutrow = (mng_uint8)(*pOutrow ^ 0xFF); + + pOutrow += pData->iColinc; /* next pixel */ + iM >>= 1; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_G1, MNG_LC_END); +#endif + + return mng_store_g1 (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_delta_g2 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + mng_uint8 iB; + mng_uint8 iM; + mng_uint32 iS; +#ifdef MNG_OPTIMIZE_FOOTPRINT_SWITCH + const mng_uint8 level[4] = { 0x00, 0x55, 0xAA, 0xFF}; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_G2, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iDeltaBlocky * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + + (pData->iDeltaBlockx * pBuf->iSamplesize); + iM = 0; /* start at pixel 0 */ + iB = 0; + iS = 0; + /* pixel replace ? */ + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0xC0; + iS = 6; + } + +#ifdef MNG_OPTIMIZE_FOOTPRINT_SWITCH + *pOutrow = level[((iB & iM) >> iS)] ; +#else + switch ((iB & iM) >> iS) /* determine the alpha level */ + { + case 0x03 : { *pOutrow = 0xFF; break; } + case 0x02 : { *pOutrow = 0xAA; break; } + case 0x01 : { *pOutrow = 0x55; break; } + default : { *pOutrow = 0x00; } + } +#endif + + pOutrow += pData->iColinc; /* next pixel */ + iM >>= 2; + iS -= 2; + } + } + else + { /* pixel add ! */ +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0xC0; + iS = 6; + } + +#ifdef MNG_OPTIMIZE_FOOTPRINT_SWITCH + *pOutrow = level[((*pOutrow >> 6) + ((iB & iM) >> iS)) & 0x03] ; +#else + switch (((*pOutrow >> 6) + ((iB & iM) >> iS)) & 0x03) + { + case 0x03 : { *pOutrow = 0xFF; break; } + case 0x02 : { *pOutrow = 0xAA; break; } + case 0x01 : { *pOutrow = 0x55; break; } + default : { *pOutrow = 0x00; } + } +#endif + + pOutrow += pData->iColinc; /* next pixel */ + iM >>= 2; + iS -= 2; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_G2, MNG_LC_END); +#endif + + return mng_store_g2 (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_delta_g4 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + mng_uint8 iB; + mng_uint8 iM; + mng_uint32 iS; + mng_uint8 iQ; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_G4, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iDeltaBlocky * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + + (pData->iDeltaBlockx * pBuf->iSamplesize); + iM = 0; /* start at pixel 0 */ + iB = 0; + iS = 0; + /* pixel replace ? */ + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0xF0; + iS = 4; + } + /* get the gray level */ + iQ = (mng_uint8)((iB & iM) >> iS); + /* expand to 8-bit by replication */ + iQ = (mng_uint8)(iQ + (iQ << 4)); + + *pOutrow = iQ; /* put in object buffer */ + + pOutrow += pData->iColinc; /* next pixel */ + iM >>= 4; + iS -= 4; + } + } + else + { /* pixel add ! */ +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0xF0; + iS = 4; + } + /* get the gray level */ + iQ = (mng_uint8)(((*pOutrow >> 4) + ((iB & iM) >> iS)) & 0x0F); + /* expand to 8-bit by replication */ + iQ = (mng_uint8)(iQ + (iQ << 4)); + + *pOutrow = iQ; /* put in object buffer */ + + pOutrow += pData->iColinc; /* next pixel */ + iM >>= 4; + iS -= 4; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_G4, MNG_LC_END); +#endif + + return mng_store_g4 (pData); +} +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + +/* ************************************************************************** */ + +mng_retcode mng_delta_g8 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_G8, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iDeltaBlocky * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + + (pData->iDeltaBlockx * pBuf->iSamplesize); + /* pixel replace ? */ + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = *pWorkrow; /* put in object buffer */ + + pOutrow += pData->iColinc; /* next pixel */ + pWorkrow++; + } + } + else + { /* pixel add ! */ +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { /* add to object buffer */ + *pOutrow = (mng_uint8)(*pOutrow + *pWorkrow); + + pOutrow += pData->iColinc; /* next pixel */ + pWorkrow++; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_G8, MNG_LC_END); +#endif + + return mng_store_g8 (pData); +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_delta_g16 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_G16, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iDeltaBlocky * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + + (pData->iDeltaBlockx * pBuf->iSamplesize); + /* pixel replace ? */ + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = *pWorkrow; /* put in object buffer */ + *(pOutrow+1) = *(pWorkrow+1); + /* next pixel */ + pOutrow += (pData->iColinc << 1); + pWorkrow += 2; + } + } + else + { /* pixel add ! */ +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { /* add to object buffer */ + mng_put_uint16 (pOutrow, (mng_uint16)(mng_get_uint16 (pOutrow ) + + mng_get_uint16 (pWorkrow) )); + /* next pixel */ + pOutrow += (pData->iColinc << 1); + pWorkrow += 2; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_G16, MNG_LC_END); +#endif + + return mng_store_g16 (pData); +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_delta_rgb8 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_RGB8, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iDeltaBlocky * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + + (pData->iDeltaBlockx * pBuf->iSamplesize); + /* pixel replace ? */ + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = *pWorkrow; /* put in object buffer */ + *(pOutrow+1) = *(pWorkrow+1); + *(pOutrow+2) = *(pWorkrow+2); + /* next pixel */ + pOutrow += (pData->iColinc * 3); + pWorkrow += 3; + } + } + else + { /* pixel add ! */ +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { /* add to object buffer */ + *pOutrow = (mng_uint8)(*pOutrow + *pWorkrow ); + *(pOutrow+1) = (mng_uint8)(*(pOutrow+1) + *(pWorkrow+1)); + *(pOutrow+2) = (mng_uint8)(*(pOutrow+2) + *(pWorkrow+2)); + /* next pixel */ + pOutrow += (pData->iColinc * 3); + pWorkrow += 3; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_RGB8, MNG_LC_END); +#endif + + return mng_store_rgb8 (pData); +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_delta_rgb16 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_RGB16, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iDeltaBlocky * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + + (pData->iDeltaBlockx * pBuf->iSamplesize); + /* pixel replace ? */ + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = *pWorkrow; /* put in object buffer */ + *(pOutrow+1) = *(pWorkrow+1); + *(pOutrow+2) = *(pWorkrow+2); + *(pOutrow+3) = *(pWorkrow+3); + *(pOutrow+4) = *(pWorkrow+4); + *(pOutrow+5) = *(pWorkrow+5); + /* next pixel */ + pOutrow += (pData->iColinc * 6); + pWorkrow += 6; + } + } + else + { /* pixel add ! */ +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { /* add to object buffer */ + mng_put_uint16 (pOutrow, (mng_uint16)(mng_get_uint16 (pOutrow ) + + mng_get_uint16 (pWorkrow ) )); + mng_put_uint16 (pOutrow+2, (mng_uint16)(mng_get_uint16 (pOutrow+2 ) + + mng_get_uint16 (pWorkrow+2) )); + mng_put_uint16 (pOutrow+4, (mng_uint16)(mng_get_uint16 (pOutrow+4 ) + + mng_get_uint16 (pWorkrow+4) )); + /* next pixel */ + pOutrow += (pData->iColinc * 6); + pWorkrow += 6; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_RGB16, MNG_LC_END); +#endif + + return mng_store_rgb16 (pData); +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_delta_idx1 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + mng_uint8 iB; + mng_uint8 iM; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_IDX1, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iDeltaBlocky * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + + (pData->iDeltaBlockx * pBuf->iSamplesize); + iM = 0; /* start at pixel 0 */ + iB = 0; + /* pixel replace ? */ + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0x80; + } + + if (iB & iM) /* put the right index value */ + *pOutrow = 1; + else + *pOutrow = 0; + + pOutrow += pData->iColinc; /* next pixel */ + iM >>= 1; + } + } + else + { /* pixel add ! */ +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0x80; + } + + if (iB & iM) /* invert if it is non-zero index */ + *pOutrow = (mng_uint8)(*pOutrow ^ 0x01); + + pOutrow += pData->iColinc; /* next pixel */ + iM >>= 1; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_IDX1, MNG_LC_END); +#endif + + return mng_store_idx1 (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_delta_idx2 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + mng_uint8 iB; + mng_uint8 iM; + mng_uint32 iS; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_IDX2, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iDeltaBlocky * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + + (pData->iDeltaBlockx * pBuf->iSamplesize); + iM = 0; /* start at pixel 0 */ + iB = 0; + iS = 0; + /* pixel replace ? */ + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0xC0; + iS = 6; + } + /* put the index */ + *pOutrow = (mng_uint8)((iB & iM) >> iS); + + pOutrow += pData->iColinc; /* next pixel */ + iM >>= 2; + iS -= 2; + } + } + else + { /* pixel add ! */ +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0xC0; + iS = 6; + } + /* calculate the index */ + *pOutrow = (mng_uint8)((*pOutrow + ((iB & iM) >> iS)) & 0x03); + + pOutrow += pData->iColinc; /* next pixel */ + iM >>= 2; + iS -= 2; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_IDX2, MNG_LC_END); +#endif + + return mng_store_idx2 (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_delta_idx4 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + mng_uint8 iB; + mng_uint8 iM; + mng_uint32 iS; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_IDX4, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iDeltaBlocky * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + + (pData->iDeltaBlockx * pBuf->iSamplesize); + iM = 0; /* start at pixel 0 */ + iB = 0; + iS = 0; + /* pixel replace ? */ + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0xF0; + iS = 4; + } + /* put the index */ + *pOutrow = (mng_uint8)((iB & iM) >> iS); + + pOutrow += pData->iColinc; /* next pixel */ + iM >>= 4; + iS -= 4; + } + } + else + { /* pixel add ! */ +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0xF0; + iS = 4; + } + /* calculate the index */ + *pOutrow = (mng_uint8)((*pOutrow + ((iB & iM) >> iS)) & 0x0F); + + pOutrow += pData->iColinc; /* next pixel */ + iM >>= 4; + iS -= 4; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_IDX4, MNG_LC_END); +#endif + + return mng_store_idx4 (pData); +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_delta_idx8 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_IDX8, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iDeltaBlocky * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + + (pData->iDeltaBlockx * pBuf->iSamplesize); + /* pixel replace ? */ + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = *pWorkrow; /* put in object buffer */ + + pOutrow += pData->iColinc; /* next pixel */ + pWorkrow++; + } + } + else + { /* pixel add ! */ +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { /* add to object buffer */ + *pOutrow = (mng_uint8)(*pOutrow + *pWorkrow); + + pOutrow += pData->iColinc; /* next pixel */ + pWorkrow++; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_IDX8, MNG_LC_END); +#endif + + return mng_store_idx8 (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_delta_ga8 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_GA8, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iDeltaBlocky * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + + (pData->iDeltaBlockx * pBuf->iSamplesize); + /* pixel replace ? */ + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = *pWorkrow; /* put in object buffer */ + *(pOutrow+1) = *(pWorkrow+1); + /* next pixel */ + pOutrow += (pData->iColinc << 1); + pWorkrow += 2; + } + } + else + { /* pixel add ! */ +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { /* add to object buffer */ + *pOutrow = (mng_uint8)(*pOutrow + *pWorkrow ); + *(pOutrow+1) = (mng_uint8)(*(pOutrow+1) + *(pWorkrow+1)); + /* next pixel */ + pOutrow += (pData->iColinc << 1); + pWorkrow += 2; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_GA8, MNG_LC_END); +#endif + + return mng_store_ga8 (pData); +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_delta_ga16 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_GA16, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iDeltaBlocky * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + + (pData->iDeltaBlockx * pBuf->iSamplesize); + /* pixel replace ? */ + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = *pWorkrow; /* put in object buffer */ + *(pOutrow+1) = *(pWorkrow+1); + *(pOutrow+2) = *(pWorkrow+2); + *(pOutrow+3) = *(pWorkrow+3); + /* next pixel */ + pOutrow += (pData->iColinc << 2); + pWorkrow += 4; + } + } + else + { /* pixel add ! */ +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { /* add to object buffer */ + mng_put_uint16 (pOutrow, (mng_uint16)(mng_get_uint16 (pOutrow ) + + mng_get_uint16 (pWorkrow ) )); + mng_put_uint16 (pOutrow+2, (mng_uint16)(mng_get_uint16 (pOutrow+2 ) + + mng_get_uint16 (pWorkrow+2) )); + /* next pixel */ + pOutrow += (pData->iColinc << 2); + pWorkrow += 4; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_GA16, MNG_LC_END); +#endif + + return mng_store_ga16 (pData); +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_delta_rgba8 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_RGBA8, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iDeltaBlocky * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + + (pData->iDeltaBlockx * pBuf->iSamplesize); + /* pixel replace ? */ + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = *pWorkrow; /* put in object buffer */ + *(pOutrow+1) = *(pWorkrow+1); + *(pOutrow+2) = *(pWorkrow+2); + *(pOutrow+3) = *(pWorkrow+3); + /* next pixel */ + pOutrow += (pData->iColinc << 2); + pWorkrow += 4; + } + } + else + { /* pixel add ! */ +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { /* add to object buffer */ + *pOutrow = (mng_uint8)(*pOutrow + *pWorkrow ); + *(pOutrow+1) = (mng_uint8)(*(pOutrow+1) + *(pWorkrow+1)); + *(pOutrow+2) = (mng_uint8)(*(pOutrow+2) + *(pWorkrow+2)); + *(pOutrow+3) = (mng_uint8)(*(pOutrow+3) + *(pWorkrow+3)); + /* next pixel */ + pOutrow += (pData->iColinc << 2); + pWorkrow += 4; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_RGBA8, MNG_LC_END); +#endif + + return mng_store_rgba8 (pData); +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_delta_rgba16 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pDeltaImage)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_RGBA16, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iDeltaBlocky * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + + (pData->iDeltaBlockx * pBuf->iSamplesize); + /* pixel replace ? */ + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + MNG_COPY (pOutrow, pWorkrow, 8); /* put in object buffer */ + /* next pixel */ + pOutrow += (pData->iColinc << 3); + pWorkrow += 8; + } + } + else + { /* pixel add ! */ +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { /* add to object buffer */ + mng_put_uint16 (pOutrow, (mng_uint16)(mng_get_uint16 (pOutrow ) + + mng_get_uint16 (pWorkrow ) )); + mng_put_uint16 (pOutrow+2, (mng_uint16)(mng_get_uint16 (pOutrow+2 ) + + mng_get_uint16 (pWorkrow+2) )); + mng_put_uint16 (pOutrow+4, (mng_uint16)(mng_get_uint16 (pOutrow+4 ) + + mng_get_uint16 (pWorkrow+4) )); + mng_put_uint16 (pOutrow+6, (mng_uint16)(mng_get_uint16 (pOutrow+6 ) + + mng_get_uint16 (pWorkrow+6) )); + /* next pixel */ + pOutrow += (pData->iColinc << 3); + pWorkrow += 8; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_RGBA16, MNG_LC_END); +#endif + + return mng_store_rgba16 (pData); +} +#endif + +/* ************************************************************************** */ +/* * * */ +/* * Delta-image row routines - apply the source row onto the target * */ +/* * * */ +/* ************************************************************************** */ + +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_delta_g1_g1 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_G1_G1, MNG_LC_START); +#endif + + pWorkrow = pData->pRGBArow; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + + if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) ) + { + MNG_COPY (pOutrow, pWorkrow, pData->iRowsamples); + } + else + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = (mng_uint8)(((mng_uint16)*pOutrow + + (mng_uint16)*pWorkrow) & 0x01); + + pOutrow++; + pWorkrow++; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_G1_G1, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_delta_g2_g2 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_G2_G2, MNG_LC_START); +#endif + + pWorkrow = pData->pRGBArow; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + + if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) ) + { + MNG_COPY (pOutrow, pWorkrow, pData->iRowsamples); + } + else + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = (mng_uint8)(((mng_uint16)*pOutrow + + (mng_uint16)*pWorkrow) & 0x03); + + pOutrow++; + pWorkrow++; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_G2_G2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_delta_g4_g4 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_G4_G4, MNG_LC_START); +#endif + + pWorkrow = pData->pRGBArow; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + + if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) ) + { + MNG_COPY (pOutrow, pWorkrow, pData->iRowsamples); + } + else + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = (mng_uint8)(((mng_uint16)*pOutrow + + (mng_uint16)*pWorkrow) & 0x0F); + + pOutrow++; + pWorkrow++; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_G4_G4, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_delta_g8_g8 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_G8_G8, MNG_LC_START); +#endif + + pWorkrow = pData->pRGBArow; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + + if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) ) + { + MNG_COPY (pOutrow, pWorkrow, pData->iRowsamples); + } + else + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = (mng_uint8)(((mng_uint16)*pOutrow + + (mng_uint16)*pWorkrow) & 0xFF); + + pOutrow++; + pWorkrow++; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_G8_G8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_delta_g16_g16 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_G16_G16, MNG_LC_START); +#endif + + pWorkrow = pData->pRGBArow; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + + if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) ) + { + MNG_COPY (pOutrow, pWorkrow, (pData->iRowsamples << 1)); + } + else + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + mng_put_uint16 (pOutrow, (mng_uint16)((mng_get_uint16 (pOutrow) + + mng_get_uint16 (pWorkrow)) & 0xFFFF)); + + pOutrow += 2; + pWorkrow += 2; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_G16_G16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif /* MNG_NO_DELTA_PNG */ + +/* ************************************************************************** */ + +mng_retcode mng_delta_rgb8_rgb8 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_RGB8_RGB8, MNG_LC_START); +#endif + + pWorkrow = pData->pRGBArow; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + + if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) ) + { + MNG_COPY (pOutrow, pWorkrow, pData->iRowsamples * 3); + } + else + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples*3; iX > 0; iX--) +#else + for (iX = 0; iX < (pData->iRowsamples * 3); iX++) +#endif + { + *pOutrow = (mng_uint8)(((mng_uint16)*pOutrow + + (mng_uint16)*pWorkrow) & 0xFF); + + pOutrow++; + pWorkrow++; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_RGB8_RGB8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_delta_rgb16_rgb16 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_RGB16_RGB16, MNG_LC_START); +#endif + + pWorkrow = pData->pRGBArow; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + + if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) ) + { + MNG_COPY (pOutrow, pWorkrow, (pData->iRowsamples * 6)); + } + else + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + mng_put_uint16 (pOutrow, (mng_uint16)((mng_get_uint16 (pOutrow ) + + mng_get_uint16 (pWorkrow )) & 0xFFFF)); + mng_put_uint16 (pOutrow+2, (mng_uint16)((mng_get_uint16 (pOutrow+2) + + mng_get_uint16 (pWorkrow+2)) & 0xFFFF)); + mng_put_uint16 (pOutrow+4, (mng_uint16)((mng_get_uint16 (pOutrow+4) + + mng_get_uint16 (pWorkrow+4)) & 0xFFFF)); + + pOutrow += 6; + pWorkrow += 6; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_RGB16_RGB16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +mng_retcode mng_delta_ga8_ga8 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_GA8_GA8, MNG_LC_START); +#endif + + pWorkrow = pData->pRGBArow; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + + if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) ) + { + MNG_COPY (pOutrow, pWorkrow, pData->iRowsamples << 1); + } + else + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = (pData->iRowsamples<<1); iX > 0; iX--) +#else + for (iX = 0; iX < (pData->iRowsamples << 1); iX++) +#endif + { + *pOutrow = (mng_uint8)(((mng_uint16)*pOutrow + + (mng_uint16)*pWorkrow) & 0xFF); + + pOutrow++; + pWorkrow++; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_GA8_GA8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_delta_ga8_g8 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_GA8_G8, MNG_LC_START); +#endif + + pWorkrow = pData->pRGBArow; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORREPLACE) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = *pWorkrow; + + pOutrow += 2; + pWorkrow++; + } + } + else + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORADD) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = (mng_uint8)(((mng_uint16)*pOutrow + + (mng_uint16)*pWorkrow) & 0xFF); + + pOutrow += 2; + pWorkrow++; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_GA8_G8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_delta_ga8_a8 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_GA8_A8, MNG_LC_START); +#endif + + pWorkrow = pData->pRGBArow; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + 1; + + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAREPLACE) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = *pWorkrow; + + pOutrow += 2; + pWorkrow++; + } + } + else + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAADD) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = (mng_uint8)(((mng_uint16)*pOutrow + + (mng_uint16)*pWorkrow) & 0xFF); + + pOutrow += 2; + pWorkrow++; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_GA8_A8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_delta_ga16_ga16 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_GA16_GA16, MNG_LC_START); +#endif + + pWorkrow = pData->pRGBArow; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + + if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) ) + { + MNG_COPY (pOutrow, pWorkrow, (pData->iRowsamples << 2)); + } + else + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + mng_put_uint16 (pOutrow, (mng_uint16)((mng_get_uint16 (pOutrow ) + + mng_get_uint16 (pWorkrow )) & 0xFFFF)); + mng_put_uint16 (pOutrow+2, (mng_uint16)((mng_get_uint16 (pOutrow+2) + + mng_get_uint16 (pWorkrow+2)) & 0xFFFF)); + + pOutrow += 4; + pWorkrow += 4; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_GA16_GA16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_delta_ga16_g16 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_GA16_G16, MNG_LC_START); +#endif + + pWorkrow = pData->pRGBArow; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORREPLACE) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + mng_put_uint16 (pOutrow, mng_get_uint16 (pWorkrow)); + + pOutrow += 4; + pWorkrow += 2; + } + } + else + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORADD) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + mng_put_uint16 (pOutrow, (mng_uint16)((mng_get_uint16 (pOutrow) + + mng_get_uint16 (pWorkrow)) & 0xFFFF)); + + pOutrow += 4; + pWorkrow += 2; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_GA16_G16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_delta_ga16_a16 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_GA16_A16, MNG_LC_START); +#endif + + pWorkrow = pData->pRGBArow; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAREPLACE) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + mng_put_uint16 (pOutrow+2, mng_get_uint16 (pWorkrow)); + + pOutrow += 4; + pWorkrow += 2; + } + } + else + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAADD) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + mng_put_uint16 (pOutrow+2, (mng_uint16)((mng_get_uint16 (pOutrow+2) + + mng_get_uint16 (pWorkrow)) & 0xFFFF)); + + pOutrow += 4; + pWorkrow += 2; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_GA16_A16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif /* MNG_NO_DELTA_PNG */ + +/* ************************************************************************** */ + +mng_retcode mng_delta_rgba8_rgba8 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_RGBA8_RGBA8, MNG_LC_START); +#endif + + pWorkrow = pData->pRGBArow; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + + if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) ) + { + MNG_COPY (pOutrow, pWorkrow, pData->iRowsamples << 2); + } + else + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = (pData->iRowsamples << 2); iX > 0; iX--) +#else + for (iX = 0; iX < (pData->iRowsamples << 2); iX++) +#endif + { + *pOutrow = (mng_uint8)(((mng_uint16)*pOutrow + + (mng_uint16)*pWorkrow) & 0xFF); + + pOutrow++; + pWorkrow++; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_RGBA8_RGBA8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +mng_retcode mng_delta_rgba8_rgb8 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_RGBA8_RGB8, MNG_LC_START); +#endif + + pWorkrow = pData->pRGBArow; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORREPLACE) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = *pWorkrow; + *(pOutrow+1) = *(pWorkrow+1); + *(pOutrow+2) = *(pWorkrow+2); + + pOutrow += 4; + pWorkrow += 3; + } + } + else + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORADD) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = (mng_uint8)(((mng_uint16)*pOutrow + + (mng_uint16)*pWorkrow ) & 0xFF); + *(pOutrow+1) = (mng_uint8)(((mng_uint16)*(pOutrow+1) + + (mng_uint16)*(pWorkrow+1)) & 0xFF); + *(pOutrow+2) = (mng_uint8)(((mng_uint16)*(pOutrow+2) + + (mng_uint16)*(pWorkrow+2)) & 0xFF); + + pOutrow += 4; + pWorkrow += 3; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_RGBA8_RGB8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_delta_rgba8_a8 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_RGBA8_A8, MNG_LC_START); +#endif + + pWorkrow = pData->pRGBArow; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize) + 3; + + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAREPLACE) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = *pWorkrow; + + pOutrow += 4; + pWorkrow++; + } + } + else + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAADD) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = (mng_uint8)(((mng_uint16)*pOutrow + + (mng_uint16)*pWorkrow) & 0xFF); + + pOutrow += 4; + pWorkrow++; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_RGBA8_A8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_DELTA_PNG */ + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_delta_rgba16_rgba16 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_RGBA16_RGBA16, MNG_LC_START); +#endif + + pWorkrow = pData->pRGBArow; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + + if ((pData->iDeltatype == MNG_DELTATYPE_REPLACE ) || + (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELREPLACE) ) + { + MNG_COPY (pOutrow, pWorkrow, (pData->iRowsamples << 3)); + } + else + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKPIXELADD) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + mng_put_uint16 (pOutrow, (mng_uint16)((mng_get_uint16 (pOutrow ) + + mng_get_uint16 (pWorkrow )) & 0xFFFF)); + mng_put_uint16 (pOutrow+2, (mng_uint16)((mng_get_uint16 (pOutrow+2) + + mng_get_uint16 (pWorkrow+2)) & 0xFFFF)); + mng_put_uint16 (pOutrow+4, (mng_uint16)((mng_get_uint16 (pOutrow+4) + + mng_get_uint16 (pWorkrow+4)) & 0xFFFF)); + mng_put_uint16 (pOutrow+6, (mng_uint16)((mng_get_uint16 (pOutrow+6) + + mng_get_uint16 (pWorkrow+6)) & 0xFFFF)); + + pOutrow += 8; + pWorkrow += 8; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_RGBA16_RGBA16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_delta_rgba16_rgb16 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_RGBA16_RGB16, MNG_LC_START); +#endif + + pWorkrow = pData->pRGBArow; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORREPLACE) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + mng_put_uint16 (pOutrow, mng_get_uint16 (pWorkrow )); + mng_put_uint16 (pOutrow+2, mng_get_uint16 (pWorkrow+2)); + mng_put_uint16 (pOutrow+4, mng_get_uint16 (pWorkrow+4)); + + pOutrow += 8; + pWorkrow += 6; + } + } + else + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKCOLORADD) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + mng_put_uint16 (pOutrow, (mng_uint16)((mng_get_uint16 (pOutrow ) + + mng_get_uint16 (pWorkrow )) & 0xFFFF)); + mng_put_uint16 (pOutrow+2, (mng_uint16)((mng_get_uint16 (pOutrow+2) + + mng_get_uint16 (pWorkrow+2)) & 0xFFFF)); + mng_put_uint16 (pOutrow+4, (mng_uint16)((mng_get_uint16 (pOutrow+4) + + mng_get_uint16 (pWorkrow+4)) & 0xFFFF)); + + pOutrow += 8; + pWorkrow += 6; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_RGBA16_RGB16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_delta_rgba16_a16 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_RGBA16_A16, MNG_LC_START); +#endif + + pWorkrow = pData->pRGBArow; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAREPLACE) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + mng_put_uint16 (pOutrow+6, mng_get_uint16 (pWorkrow)); + + pOutrow += 8; + pWorkrow += 2; + } + } + else + if (pData->iDeltatype == MNG_DELTATYPE_BLOCKALPHAADD) + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + mng_put_uint16 (pOutrow+6, (mng_uint16)((mng_get_uint16 (pOutrow+6) + + mng_get_uint16 (pWorkrow)) & 0xFFFF)); + + pOutrow += 8; + pWorkrow += 2; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DELTA_RGBA16_A16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif /* MNG_NO_DELTA_PNG */ + +/* ************************************************************************** */ +/* * * */ +/* * Delta-image row routines - scale the delta to bitdepth of target * */ +/* * * */ +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_scale_g1_g2 (mng_datap pData) +{ + mng_uint8p pWorkrow = pData->pRGBArow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G1_G2, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pWorkrow = (mng_uint8)(*pWorkrow << 1); + pWorkrow++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G1_G2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_scale_g1_g4 (mng_datap pData) +{ + mng_uint8p pWorkrow = pData->pRGBArow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G1_G4, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pWorkrow = (mng_uint8)(*pWorkrow << 3); + pWorkrow++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G1_G4, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_scale_g1_g8 (mng_datap pData) +{ + mng_uint8p pWorkrow = pData->pRGBArow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G1_G8, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pWorkrow = (mng_uint8)(*pWorkrow << 7); + pWorkrow++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G1_G8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_scale_g1_g16 (mng_datap pData) +{ + mng_uint8p pWorkrow = pData->pRGBArow; + mng_uint8p pOutrow = pData->pRGBArow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G1_G16, MNG_LC_START); +#endif + + pWorkrow = pWorkrow + (pData->iRowsamples - 1); + pOutrow = pOutrow + ((pData->iRowsamples - 1) << 1); +/* pWorkrow = (mng_uint8p)((mng_uint32)pWorkrow + pData->iRowsamples - 1); */ +/* pOutrow = (mng_uint8p)((mng_uint32)pOutrow + ((pData->iRowsamples - 1) << 1)); */ + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *(pOutrow+1) = 0; + *pOutrow = (mng_uint8)(*pWorkrow << 7); + + pWorkrow--; + pOutrow -= 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G1_G16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_scale_g2_g4 (mng_datap pData) +{ + mng_uint8p pWorkrow = pData->pRGBArow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G2_G4, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pWorkrow = (mng_uint8)(*pWorkrow << 2); + pWorkrow++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G2_G4, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_scale_g2_g8 (mng_datap pData) +{ + mng_uint8p pWorkrow = pData->pRGBArow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G2_G8, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pWorkrow = (mng_uint8)(*pWorkrow << 6); + pWorkrow++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G2_G8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_scale_g2_g16 (mng_datap pData) +{ + mng_uint8p pWorkrow = pData->pRGBArow; + mng_uint8p pOutrow = pData->pRGBArow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G2_G16, MNG_LC_START); +#endif + + pWorkrow = pWorkrow + (pData->iRowsamples - 1); + pOutrow = pOutrow + ((pData->iRowsamples - 1) << 1); +/* pWorkrow = (mng_uint8p)((mng_uint32)pWorkrow + pData->iRowsamples - 1); */ +/* pOutrow = (mng_uint8p)((mng_uint32)pOutrow + ((pData->iRowsamples - 1) << 1)); */ + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *(pOutrow+1) = 0; + *pOutrow = (mng_uint8)(*pWorkrow << 6); + + pWorkrow--; + pOutrow -= 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G2_G16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_scale_g4_g8 (mng_datap pData) +{ + mng_uint8p pWorkrow = pData->pRGBArow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G4_G8, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pWorkrow = (mng_uint8)(*pWorkrow << 4); + pWorkrow++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G4_G8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_scale_g4_g16 (mng_datap pData) +{ + mng_uint8p pWorkrow = pData->pRGBArow; + mng_uint8p pOutrow = pData->pRGBArow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G4_G16, MNG_LC_START); +#endif + + pWorkrow = pWorkrow + (pData->iRowsamples - 1); + pOutrow = pOutrow + ((pData->iRowsamples - 1) << 1); +/* pWorkrow = (mng_uint8p)((mng_uint32)pWorkrow + pData->iRowsamples - 1); */ +/* pOutrow = (mng_uint8p)((mng_uint32)pOutrow + ((pData->iRowsamples - 1) << 1)); */ + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *(pOutrow+1) = 0; + *pOutrow = (mng_uint8)(*pWorkrow << 4); + + pWorkrow--; + pOutrow -= 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G4_G16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_scale_g8_g16 (mng_datap pData) +{ + mng_uint8p pWorkrow = pData->pRGBArow; + mng_uint8p pOutrow = pData->pRGBArow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G8_G16, MNG_LC_START); +#endif + + pWorkrow = pWorkrow + (pData->iRowsamples - 1); + pOutrow = pOutrow + ((pData->iRowsamples - 1) << 1); +/* pWorkrow = (mng_uint8p)((mng_uint32)pWorkrow + pData->iRowsamples - 1); */ +/* pOutrow = (mng_uint8p)((mng_uint32)pOutrow + ((pData->iRowsamples - 1) << 1)); */ + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *(pOutrow+1) = 0; + *pOutrow = *pWorkrow; + + pWorkrow--; + pOutrow -= 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G8_G16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_scale_ga8_ga16 (mng_datap pData) +{ + mng_uint8p pWorkrow = pData->pRGBArow; + mng_uint8p pOutrow = pData->pRGBArow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_GA8_GA16, MNG_LC_START); +#endif + + pWorkrow = pWorkrow + ((pData->iRowsamples - 1) << 1); + pOutrow = pOutrow + ((pData->iRowsamples - 1) << 2); +/* pWorkrow = (mng_uint8p)((mng_uint32)pWorkrow + ((pData->iRowsamples - 1) << 1)); */ +/* pOutrow = (mng_uint8p)((mng_uint32)pOutrow + ((pData->iRowsamples - 1) << 2)); */ + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *(pOutrow+3) = 0; + *(pOutrow+2) = *(pWorkrow+1); + *(pOutrow+1) = 0; + *pOutrow = *pWorkrow; + + pWorkrow -= 2; + pOutrow -= 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_GA8_GA16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_scale_rgb8_rgb16 (mng_datap pData) +{ + mng_uint8p pWorkrow = pData->pRGBArow; + mng_uint8p pOutrow = pData->pRGBArow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_RGB8_RGB16, MNG_LC_START); +#endif + + pWorkrow = pWorkrow + (3 * (pData->iRowsamples - 1)); + pOutrow = pOutrow + (6 * (pData->iRowsamples - 1)); +/* pWorkrow = (mng_uint8p)((mng_uint32)pWorkrow + 3 * (pData->iRowsamples - 1)); */ +/* pOutrow = (mng_uint8p)((mng_uint32)pOutrow + 6 * (pData->iRowsamples - 1)); */ + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *(pOutrow+5) = 0; + *(pOutrow+4) = *(pWorkrow+2); + *(pOutrow+3) = 0; + *(pOutrow+2) = *(pWorkrow+1); + *(pOutrow+1) = 0; + *pOutrow = *pWorkrow; + + pWorkrow -= 3; + pOutrow -= 6; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_RGB8_RGB16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_scale_rgba8_rgba16 (mng_datap pData) +{ + mng_uint8p pWorkrow = pData->pRGBArow; + mng_uint8p pOutrow = pData->pRGBArow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_RGBA8_RGBA16, MNG_LC_START); +#endif + + pWorkrow = pWorkrow + ((pData->iRowsamples - 1) << 2); + pOutrow = pOutrow + ((pData->iRowsamples - 1) << 3); +/* pWorkrow = (mng_uint8p)((mng_uint32)pWorkrow + ((pData->iRowsamples - 1) << 2)); */ +/* pOutrow = (mng_uint8p)((mng_uint32)pOutrow + ((pData->iRowsamples - 1) << 3)); */ + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *(pOutrow+7) = 0; + *(pOutrow+6) = *(pWorkrow+3); + *(pOutrow+5) = 0; + *(pOutrow+4) = *(pWorkrow+2); + *(pOutrow+3) = 0; + *(pOutrow+2) = *(pWorkrow+1); + *(pOutrow+1) = 0; + *pOutrow = *pWorkrow; + + pWorkrow -= 4; + pOutrow -= 8; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_RGBA8_RGBA16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_scale_g2_g1 (mng_datap pData) +{ + mng_uint8p pWorkrow = pData->pRGBArow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G2_G1, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pWorkrow = (mng_uint8)(*pWorkrow >> 1); + pWorkrow++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G2_G1, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_scale_g4_g1 (mng_datap pData) +{ + mng_uint8p pWorkrow = pData->pRGBArow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G4_G1, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pWorkrow = (mng_uint8)(*pWorkrow >> 3); + pWorkrow++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G4_G1, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_scale_g8_g1 (mng_datap pData) +{ + mng_uint8p pWorkrow = pData->pRGBArow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G8_G1, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pWorkrow = (mng_uint8)(*pWorkrow >> 7); + pWorkrow++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G8_G1, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_scale_g16_g1 (mng_datap pData) +{ + mng_uint8p pWorkrow = pData->pRGBArow; + mng_uint8p pOutrow = pData->pRGBArow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G16_G1, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = (mng_uint8)(mng_get_uint16 (pWorkrow) >> 15); + pOutrow++; + pWorkrow += 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G16_G1, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_scale_g4_g2 (mng_datap pData) +{ + mng_uint8p pWorkrow = pData->pRGBArow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G4_G2, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pWorkrow = (mng_uint8)(*pWorkrow >> 2); + pWorkrow++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G4_G2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_scale_g8_g2 (mng_datap pData) +{ + mng_uint8p pWorkrow = pData->pRGBArow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G8_G2, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pWorkrow = (mng_uint8)(*pWorkrow >> 6); + pWorkrow++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G8_G2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_scale_g16_g2 (mng_datap pData) +{ + mng_uint8p pWorkrow = pData->pRGBArow; + mng_uint8p pOutrow = pData->pRGBArow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G16_G2, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = (mng_uint8)(mng_get_uint16 (pWorkrow) >> 14); + pOutrow++; + pWorkrow += 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G16_G2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_scale_g8_g4 (mng_datap pData) +{ + mng_uint8p pWorkrow = pData->pRGBArow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G8_G4, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pWorkrow = (mng_uint8)(*pWorkrow >> 4); + pWorkrow++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G8_G4, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_scale_g16_g4 (mng_datap pData) +{ + mng_uint8p pWorkrow = pData->pRGBArow; + mng_uint8p pOutrow = pData->pRGBArow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G16_G4, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = (mng_uint8)(mng_get_uint16 (pWorkrow) >> 12); + pOutrow++; + pWorkrow += 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G16_G4, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif /* NO_1_2_4BIT_SUPPORT */ + +/* ************************************************************************** */ + +mng_retcode mng_scale_g16_g8 (mng_datap pData) +{ + mng_uint8p pWorkrow = pData->pRGBArow; + mng_uint8p pOutrow = pData->pRGBArow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G16_G8, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = (mng_uint8)(mng_get_uint16 (pWorkrow) >> 8); + pOutrow++; + pWorkrow += 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_G16_G8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_scale_ga16_ga8 (mng_datap pData) +{ + mng_uint8p pWorkrow = pData->pRGBArow; + mng_uint8p pOutrow = pData->pRGBArow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_GA16_GA8, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = (mng_uint8)(mng_get_uint16 (pWorkrow) >> 8); + pOutrow++; + pWorkrow += 2; + *pOutrow = (mng_uint8)(mng_get_uint16 (pWorkrow) >> 8); + pOutrow++; + pWorkrow += 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_GA16_GA8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_scale_rgb16_rgb8 (mng_datap pData) +{ + mng_uint8p pWorkrow = pData->pRGBArow; + mng_uint8p pOutrow = pData->pRGBArow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_RGB16_RGB8, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = (mng_uint8)(mng_get_uint16 (pWorkrow) >> 8); + pOutrow++; + pWorkrow += 2; + *pOutrow = (mng_uint8)(mng_get_uint16 (pWorkrow) >> 8); + pOutrow++; + pWorkrow += 2; + *pOutrow = (mng_uint8)(mng_get_uint16 (pWorkrow) >> 8); + pOutrow++; + pWorkrow += 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_RGB16_RGB8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_scale_rgba16_rgba8 (mng_datap pData) +{ + mng_uint8p pWorkrow = pData->pRGBArow; + mng_uint8p pOutrow = pData->pRGBArow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_RGBA16_RGBA8, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pOutrow = (mng_uint8)(mng_get_uint16 (pWorkrow) >> 8); + pOutrow++; + pWorkrow += 2; + *pOutrow = (mng_uint8)(mng_get_uint16 (pWorkrow) >> 8); + pOutrow++; + pWorkrow += 2; + *pOutrow = (mng_uint8)(mng_get_uint16 (pWorkrow) >> 8); + pOutrow++; + pWorkrow += 2; + *pOutrow = (mng_uint8)(mng_get_uint16 (pWorkrow) >> 8); + pOutrow++; + pWorkrow += 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_SCALE_RGBA16_RGBA8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ +/* * * */ +/* * Delta-image bit routines - promote bit_depth * */ +/* * * */ +/* ************************************************************************** */ + +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_uint8 mng_promote_replicate_1_2 (mng_uint8 iB) +{ + return (mng_uint8)((iB << 1) | iB); +} + +/* ************************************************************************** */ + +mng_uint8 mng_promote_replicate_1_4 (mng_uint8 iB) +{ + iB = (mng_uint8)((iB << 1) + iB); + return (mng_uint8)((iB << 2) + iB); +} + +/* ************************************************************************** */ + +mng_uint8 mng_promote_replicate_1_8 (mng_uint8 iB) +{ + iB = (mng_uint8)((iB << 1) + iB); + iB = (mng_uint8)((iB << 2) + iB); + return (mng_uint8)((iB << 4) + iB); +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_uint16 mng_promote_replicate_1_16 (mng_uint8 iB) +{ + iB = (mng_uint8)((iB << 1) + iB); + iB = (mng_uint8)((iB << 2) + iB); + iB = (mng_uint8)((iB << 4) + iB); + return (mng_uint16)(((mng_uint16)iB << 8) + (mng_uint16)iB); +} +#endif + +/* ************************************************************************** */ + +mng_uint8 mng_promote_replicate_2_4 (mng_uint8 iB) +{ + return (mng_uint8)((iB << 2) + iB); +} + +/* ************************************************************************** */ + +mng_uint8 mng_promote_replicate_2_8 (mng_uint8 iB) +{ + iB = (mng_uint8)((iB << 2) + iB); + return (mng_uint8)((iB << 4) + iB); +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_uint16 mng_promote_replicate_2_16 (mng_uint8 iB) +{ + iB = (mng_uint8)((iB << 2) + iB); + iB = (mng_uint8)((iB << 4) + iB); + return (mng_uint16)(((mng_uint16)iB << 8) + (mng_uint16)iB); +} +#endif + +/* ************************************************************************** */ + +mng_uint8 mng_promote_replicate_4_8 (mng_uint8 iB) +{ + return (mng_uint8)((iB << 4) + iB); +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_uint16 mng_promote_replicate_4_16 (mng_uint8 iB) +{ + iB = (mng_uint8)((iB << 4) + iB); + return (mng_uint16)(((mng_uint16)iB << 8) + (mng_uint16)iB); +} +#endif +#endif /* NO_1_2_4BIT_SUPPORT */ + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_uint16 mng_promote_replicate_8_16 (mng_uint8 iB) +{ + return (mng_uint16)(((mng_uint16)iB << 8) + (mng_uint16)iB); +} +#endif + +/* ************************************************************************** */ + +#if !defined(MNG_NO_DELTA_PNG) +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_uint8 mng_promote_zerofill_1_2 (mng_uint8 iB) +{ + return (mng_uint8)(iB << 1); +} + +/* ************************************************************************** */ + +mng_uint8 mng_promote_zerofill_1_4 (mng_uint8 iB) +{ + return (mng_uint8)(iB << 3); +} + +/* ************************************************************************** */ + +mng_uint8 mng_promote_zerofill_1_8 (mng_uint8 iB) +{ + return (mng_uint8)(iB << 7); +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_uint16 mng_promote_zerofill_1_16 (mng_uint8 iB) +{ + return (mng_uint16)((mng_uint16)iB << 15); +} +#endif + +/* ************************************************************************** */ + +mng_uint8 mng_promote_zerofill_2_4 (mng_uint8 iB) +{ + return (mng_uint8)(iB << 2); +} + +/* ************************************************************************** */ + +mng_uint8 mng_promote_zerofill_2_8 (mng_uint8 iB) +{ + return (mng_uint8)(iB << 6); +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_uint16 mng_promote_zerofill_2_16 (mng_uint8 iB) +{ + return (mng_uint16)((mng_uint16)iB << 14); +} +#endif + +/* ************************************************************************** */ + +mng_uint8 mng_promote_zerofill_4_8 (mng_uint8 iB) +{ + return (mng_uint8)(iB << 4); +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_uint16 mng_promote_zerofill_4_16 (mng_uint8 iB) +{ + return (mng_uint16)((mng_uint16)iB << 12); +} +#endif +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_uint16 mng_promote_zerofill_8_16 (mng_uint8 iB) +{ + return (mng_uint16)((mng_uint16)iB << 8); +} +#endif +#endif /* MNG_NO_DELTA_PNG */ + +/* ************************************************************************** */ +/* * * */ +/* * Delta-image row routines - promote color_type * */ +/* * * */ +/* ************************************************************************** */ + +#if !defined(MNG_NO_DELTA_PNG) || !defined(MNG_SKIPCHUNK_PAST) || !defined(MNG_SKIPCHUNK_MAGN) +mng_retcode mng_promote_g8_g8 (mng_datap pData) +{ + mng_uint8p pSrcline = (mng_uint8p)pData->pPromSrc; + mng_uint8p pDstline = (mng_uint8p)pData->pPromDst; + mng_uint32 iX; + mng_uint8 iB; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_G8_G8, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iPromWidth; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iPromWidth; iX++) +#endif + { + iB = *pSrcline; + if (pData->fPromBitdepth) /* bitdepth promoted ? */ + iB = ((mng_bitdepth_8)pData->fPromBitdepth) (iB); + *pDstline = iB; + + pSrcline++; + pDstline++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_G8_G8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_promote_g8_g16 (mng_datap pData) +{ + mng_uint8p pSrcline = (mng_uint8p)pData->pPromSrc; + mng_uint8p pDstline = (mng_uint8p)pData->pPromDst; + mng_uint32 iX; + mng_uint16 iW; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_G8_G16, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iPromWidth; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iPromWidth; iX++) +#endif + { + iW = ((mng_bitdepth_16)pData->fPromBitdepth) (*pSrcline); + + *pDstline = (mng_uint8)(iW >> 8); + *(pDstline+1) = (mng_uint8)(iW && 0xFF); + + pSrcline++; + pDstline += 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_G8_G16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_promote_g16_g16 (mng_datap pData) +{ + mng_uint16p pSrcline = (mng_uint16p)pData->pPromSrc; + mng_uint16p pDstline = (mng_uint16p)pData->pPromDst; + mng_uint32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_G16_G16, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iPromWidth; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iPromWidth; iX++) +#endif + { + *pDstline = *pSrcline; + pSrcline++; + pDstline++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_G16_G16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_promote_g8_ga8 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pPromBuf; + mng_uint8p pSrcline = (mng_uint8p)pData->pPromSrc; + mng_uint8p pDstline = (mng_uint8p)pData->pPromDst; + mng_uint32 iX; + mng_uint8 iB; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_G8_GA8, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iPromWidth; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iPromWidth; iX++) +#endif + { + iB = *pSrcline; + /* no cheap transparency ? */ + if ((!pBuf->bHasTRNS) || ((mng_uint16)iB != pBuf->iTRNSgray)) + *(pDstline+1) = 0xFF; + + if (pData->fPromBitdepth) /* bitdepth promoted ? */ + iB = ((mng_bitdepth_8)pData->fPromBitdepth) (iB); + + *pDstline = iB; + + pSrcline++; + pDstline += 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_G8_GA8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_promote_g8_ga16 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pPromBuf; + mng_uint8p pSrcline = (mng_uint8p)pData->pPromSrc; + mng_uint8p pDstline = (mng_uint8p)pData->pPromDst; + mng_uint32 iX; + mng_uint8 iB; + mng_uint16 iW; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_G8_GA16, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iPromWidth; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iPromWidth; iX++) +#endif + { + iB = *pSrcline; + /* no cheap transparency ? */ + if ((!pBuf->bHasTRNS) || ((mng_uint16)iB != pBuf->iTRNSgray)) + { + *(pDstline+2) = 0xFF; + *(pDstline+3) = 0xFF; + } + + iW = ((mng_bitdepth_16)pData->fPromBitdepth) (iB); + + *pDstline = (mng_uint8)(iW >> 8); + *(pDstline+1) = (mng_uint8)(iW && 0xFF); + + pSrcline++; + pDstline += 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_G8_GA16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_promote_g16_ga16 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pPromBuf; + mng_uint16p pSrcline = (mng_uint16p)pData->pPromSrc; + mng_uint16p pDstline = (mng_uint16p)pData->pPromDst; + mng_uint32 iX; + mng_uint16 iW; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_G16_GA16, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iPromWidth; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iPromWidth; iX++) +#endif + { + iW = *pSrcline; + /* no cheap transparency ? */ + if ((!pBuf->bHasTRNS) || ((mng_uint16)iW != pBuf->iTRNSgray)) + *(pDstline+1) = 0xFFFF; + + *pDstline = iW; + + pSrcline++; + pDstline += 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_G16_GA16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_promote_g8_rgb8 (mng_datap pData) +{ + mng_uint8p pSrcline = (mng_uint8p)pData->pPromSrc; + mng_uint8p pDstline = (mng_uint8p)pData->pPromDst; + mng_uint32 iX; + mng_uint8 iB; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_G8_RGB8, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iPromWidth; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iPromWidth; iX++) +#endif + { + iB = *pSrcline; + + if (pData->fPromBitdepth) /* bitdepth promoted ? */ + iB = ((mng_bitdepth_8)pData->fPromBitdepth) (iB); + + *pDstline = iB; + *(pDstline+1) = iB; + *(pDstline+2) = iB; + + pSrcline++; + pDstline += 3; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_G8_RGB8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_promote_g8_rgb16 (mng_datap pData) +{ + mng_uint8p pSrcline = (mng_uint8p)pData->pPromSrc; + mng_uint8p pDstline = (mng_uint8p)pData->pPromDst; + mng_uint32 iX; + mng_uint8 iB; + mng_uint16 iW; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_G8_RGB16, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iPromWidth; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iPromWidth; iX++) +#endif + { + iB = *pSrcline; + iW = ((mng_bitdepth_16)pData->fPromBitdepth) (iB); + + iB = (mng_uint8)(iW >> 8); + *pDstline = iB; + *(pDstline+2) = iB; + *(pDstline+4) = iB; + iB = (mng_uint8)(iW && 0xFF); + *(pDstline+1) = iB; + *(pDstline+3) = iB; + *(pDstline+5) = iB; + + pSrcline++; + pDstline += 6; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_G8_RGB16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_promote_g16_rgb16 (mng_datap pData) +{ + mng_uint16p pSrcline = (mng_uint16p)pData->pPromSrc; + mng_uint16p pDstline = (mng_uint16p)pData->pPromDst; + mng_uint32 iX; + mng_uint16 iW; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_G16_RGB16, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iPromWidth; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iPromWidth; iX++) +#endif + { + iW = *pSrcline; + + *pDstline = iW; + *(pDstline+1) = iW; + *(pDstline+2) = iW; + + pSrcline++; + pDstline += 3; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_G16_RGB16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_promote_g8_rgba8 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pPromBuf; + mng_uint8p pSrcline = (mng_uint8p)pData->pPromSrc; + mng_uint8p pDstline = (mng_uint8p)pData->pPromDst; + mng_uint32 iX; + mng_uint8 iB; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_G8_RGBA8, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iPromWidth; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iPromWidth; iX++) +#endif + { + iB = *pSrcline; + /* no cheap transparency ? */ + if ((!pBuf->bHasTRNS) || ((mng_uint16)iB != pBuf->iTRNSgray)) + *(pDstline+3) = 0xFF; + + if (pData->fPromBitdepth) /* bitdepth promoted ? */ + iB = ((mng_bitdepth_8)pData->fPromBitdepth) (iB); + + *pDstline = iB; + *(pDstline+1) = iB; + *(pDstline+2) = iB; + + pSrcline++; + pDstline += 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_G8_RGBA8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_promote_g8_rgba16 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pPromBuf; + mng_uint8p pSrcline = (mng_uint8p)pData->pPromSrc; + mng_uint8p pDstline = (mng_uint8p)pData->pPromDst; + mng_uint32 iX; + mng_uint8 iB; + mng_uint16 iW; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_G8_RGBA16, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iPromWidth; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iPromWidth; iX++) +#endif + { + iB = *pSrcline; + /* no cheap transparency ? */ + if ((!pBuf->bHasTRNS) || ((mng_uint16)iB != pBuf->iTRNSgray)) + { + *(pDstline+6) = 0xFF; + *(pDstline+7) = 0xFF; + } + + iW = ((mng_bitdepth_16)pData->fPromBitdepth) (iB); + + iB = (mng_uint8)(iW >> 8); + *pDstline = iB; + *(pDstline+2) = iB; + *(pDstline+4) = iB; + iB = (mng_uint8)(iW && 0xFF); + *(pDstline+1) = iB; + *(pDstline+3) = iB; + *(pDstline+5) = iB;; + + pSrcline++; + pDstline += 8; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_G8_RGBA16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_promote_g16_rgba16 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pPromBuf; + mng_uint16p pSrcline = (mng_uint16p)pData->pPromSrc; + mng_uint16p pDstline = (mng_uint16p)pData->pPromDst; + mng_uint32 iX; + mng_uint16 iW; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_G16_RGBA16, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iPromWidth; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iPromWidth; iX++) +#endif + { + iW = *pSrcline; + /* no cheap transparency ? */ + if ((!pBuf->bHasTRNS) || (iW != pBuf->iTRNSgray)) + *(pDstline+3) = 0xFFFF; + + *pDstline = iW; + *(pDstline+1) = iW; + *(pDstline+2) = iW; + + pSrcline++; + pDstline += 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_G16_RGBA16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_promote_ga8_ga16 (mng_datap pData) +{ + mng_uint8p pSrcline = (mng_uint8p)pData->pPromSrc; + mng_uint8p pDstline = (mng_uint8p)pData->pPromDst; + mng_uint32 iX; + mng_uint16 iW; + mng_uint16 iA; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_GA8_GA16, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iPromWidth; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iPromWidth; iX++) +#endif + { + iW = ((mng_bitdepth_16)pData->fPromBitdepth) (*pSrcline); + iA = ((mng_bitdepth_16)pData->fPromBitdepth) (*(pSrcline+1)); + + *pDstline = (mng_uint8)(iW >> 8); + *(pDstline+1) = (mng_uint8)(iW && 0xFF); + *(pDstline+2) = (mng_uint8)(iA >> 8); + *(pDstline+3) = (mng_uint8)(iA && 0xFF); + + pSrcline += 2; + pDstline += 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_GA8_GA16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_promote_ga8_rgba8 (mng_datap pData) +{ + mng_uint8p pSrcline = (mng_uint8p)pData->pPromSrc; + mng_uint8p pDstline = (mng_uint8p)pData->pPromDst; + mng_uint32 iX; + mng_uint8 iB; + mng_uint8 iA; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_GA8_RGBA8, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iPromWidth; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iPromWidth; iX++) +#endif + { + iB = *pSrcline; + iA = *(pSrcline+1); + + *pDstline = iB; + *(pDstline+1) = iB; + *(pDstline+2) = iB; + *(pDstline+3) = iA; + + pSrcline += 2; + pDstline += 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_GA8_RGBA8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_promote_ga8_rgba16 (mng_datap pData) +{ + mng_uint8p pSrcline = (mng_uint8p)pData->pPromSrc; + mng_uint8p pDstline = (mng_uint8p)pData->pPromDst; + mng_uint32 iX; + mng_uint8 iB; + mng_uint16 iW; + mng_uint16 iA; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_GA8_RGBA16, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iPromWidth; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iPromWidth; iX++) +#endif + { + iW = ((mng_bitdepth_16)pData->fPromBitdepth) (*pSrcline); + iA = ((mng_bitdepth_16)pData->fPromBitdepth) (*(pSrcline+1)); + + iB = (mng_uint8)(iW >> 8); + *pDstline = iB; + *(pDstline+2) = iB; + *(pDstline+4) = iB; + iB = (mng_uint8)(iW && 0xFF); + *(pDstline+1) = iB; + *(pDstline+3) = iB; + *(pDstline+5) = iB; + *(pDstline+6) = (mng_uint8)(iA >> 8); + *(pDstline+7) = (mng_uint8)(iA && 0xFF); + + pSrcline += 2; + pDstline += 8; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_GA8_RGBA16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_promote_ga16_rgba16 (mng_datap pData) +{ + mng_uint16p pSrcline = (mng_uint16p)pData->pPromSrc; + mng_uint16p pDstline = (mng_uint16p)pData->pPromDst; + mng_uint32 iX; + mng_uint16 iW; + mng_uint16 iA; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_GA16_RGBA16, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iPromWidth; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iPromWidth; iX++) +#endif + { + iW = *pSrcline; + iA = *(pSrcline+1); + + *pDstline = iW; + *(pDstline+1) = iW; + *(pDstline+2) = iW; + *(pDstline+3) = iA; + + pSrcline += 2; + pDstline += 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_GA16_RGBA16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_promote_rgb8_rgb16 (mng_datap pData) +{ + mng_uint8p pSrcline = (mng_uint8p)pData->pPromSrc; + mng_uint8p pDstline = (mng_uint8p)pData->pPromDst; + mng_uint32 iX; + mng_uint16 iR; + mng_uint16 iG; + mng_uint16 iB; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_RGB8_RGB16, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iPromWidth; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iPromWidth; iX++) +#endif + { + iR = ((mng_bitdepth_16)pData->fPromBitdepth) (*pSrcline); + iG = ((mng_bitdepth_16)pData->fPromBitdepth) (*(pSrcline+1)); + iB = ((mng_bitdepth_16)pData->fPromBitdepth) (*(pSrcline+2)); + + *pDstline = (mng_uint8)(iR >> 8); + *(pDstline+1) = (mng_uint8)(iR && 0xFF); + *(pDstline+2) = (mng_uint8)(iG >> 8); + *(pDstline+3) = (mng_uint8)(iG && 0xFF); + *(pDstline+4) = (mng_uint8)(iB >> 8); + *(pDstline+5) = (mng_uint8)(iB && 0xFF); + + pSrcline += 3; + pDstline += 6; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_RGB8_RGB16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_promote_rgb8_rgba8 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pPromBuf; + mng_uint8p pSrcline = (mng_uint8p)pData->pPromSrc; + mng_uint8p pDstline = (mng_uint8p)pData->pPromDst; + mng_uint32 iX; + mng_uint8 iR; + mng_uint8 iG; + mng_uint8 iB; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_RGB8_RGBA8, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iPromWidth; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iPromWidth; iX++) +#endif + { + iR = *pSrcline; + iG = *(pSrcline+1); + iB = *(pSrcline+2); + /* no cheap transparency ? */ + if ((!pBuf->bHasTRNS) || ((mng_uint16)iR != pBuf->iTRNSred) || + ((mng_uint16)iG != pBuf->iTRNSgreen) || ((mng_uint16)iB != pBuf->iTRNSblue)) + *(pDstline+3) = 0xFF; + + *pDstline = iR; + *(pDstline+1) = iG; + *(pDstline+2) = iB; + + pSrcline += 3; + pDstline += 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_RGB8_RGBA8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_promote_rgb8_rgba16 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pPromBuf; + mng_uint8p pSrcline = (mng_uint8p)pData->pPromSrc; + mng_uint8p pDstline = (mng_uint8p)pData->pPromDst; + mng_uint32 iX; + mng_uint8 iR; + mng_uint8 iG; + mng_uint8 iB; + mng_uint16 iRw; + mng_uint16 iGw; + mng_uint16 iBw; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_RGB8_RGBA16, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iPromWidth; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iPromWidth; iX++) +#endif + { + iR = *pSrcline; + iG = *(pSrcline+1); + iB = *(pSrcline+2); + /* no cheap transparency ? */ + if ((!pBuf->bHasTRNS) || ((mng_uint16)iR != pBuf->iTRNSred) || + ((mng_uint16)iG != pBuf->iTRNSgreen) || ((mng_uint16)iB != pBuf->iTRNSblue)) + { + *(pDstline+6) = 0xFF; + *(pDstline+7) = 0xFF; + } + + iRw = ((mng_bitdepth_16)pData->fPromBitdepth) (iR); + iGw = ((mng_bitdepth_16)pData->fPromBitdepth) (iG); + iBw = ((mng_bitdepth_16)pData->fPromBitdepth) (iB); + + *pDstline = (mng_uint8)(iRw >> 8); + *(pDstline+1) = (mng_uint8)(iRw && 0xFF); + *(pDstline+2) = (mng_uint8)(iGw >> 8); + *(pDstline+3) = (mng_uint8)(iGw && 0xFF); + *(pDstline+4) = (mng_uint8)(iBw >> 8); + *(pDstline+5) = (mng_uint8)(iBw && 0xFF); + + pSrcline += 3; + pDstline += 8; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_RGB8_RGBA16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_promote_rgb16_rgba16 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pPromBuf; + mng_uint16p pSrcline = (mng_uint16p)pData->pPromSrc; + mng_uint16p pDstline = (mng_uint16p)pData->pPromDst; + mng_uint32 iX; + mng_uint16 iR; + mng_uint16 iG; + mng_uint16 iB; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_RGB16_RGBA16, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iPromWidth; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iPromWidth; iX++) +#endif + { + iR = *pSrcline; + iG = *(pSrcline+1); + iB = *(pSrcline+2); + /* no cheap transparency ? */ + if ((!pBuf->bHasTRNS) || (iR != pBuf->iTRNSred) || + (iG != pBuf->iTRNSgreen) || (iB != pBuf->iTRNSblue)) + *(pDstline+3) = 0xFFFF; + + *pDstline = iR; + *(pDstline+1) = iG; + *(pDstline+2) = iB; + + pSrcline += 3; + pDstline += 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_RGB16_RGBA16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_promote_idx8_rgb8 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pPromBuf; + mng_uint8p pSrcline = (mng_uint8p)pData->pPromSrc; + mng_uint8p pDstline = (mng_uint8p)pData->pPromDst; + mng_uint32 iX; + mng_uint8 iB; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_IDX8_RGB8, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iPromWidth; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iPromWidth; iX++) +#endif + { + iB = *pSrcline; + + if ((mng_uint32)iB < pBuf->iPLTEcount) + { + *pDstline = pBuf->aPLTEentries [iB].iRed; + *(pDstline+1) = pBuf->aPLTEentries [iB].iGreen; + *(pDstline+2) = pBuf->aPLTEentries [iB].iBlue; + } + + pSrcline++; + pDstline += 3; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_IDX8_RGB8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_promote_idx8_rgb16 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pPromBuf; + mng_uint8p pSrcline = (mng_uint8p)pData->pPromSrc; + mng_uint8p pDstline = (mng_uint8p)pData->pPromDst; + mng_uint32 iX; + mng_uint8 iN; + mng_uint16 iR; + mng_uint16 iG; + mng_uint16 iB; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_IDX8_RGB16, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iPromWidth; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iPromWidth; iX++) +#endif + { + iN = *pSrcline; + + if ((mng_uint32)iN < pBuf->iPLTEcount) + { + iR = ((mng_bitdepth_16)pData->fPromBitdepth) (pBuf->aPLTEentries [iN].iRed); + iG = ((mng_bitdepth_16)pData->fPromBitdepth) (pBuf->aPLTEentries [iN].iGreen); + iB = ((mng_bitdepth_16)pData->fPromBitdepth) (pBuf->aPLTEentries [iN].iBlue); + *pDstline = (mng_uint8)(iR >> 8); + *(pDstline+1) = (mng_uint8)(iR && 0xFF); + *(pDstline+2) = (mng_uint8)(iG >> 8); + *(pDstline+3) = (mng_uint8)(iG && 0xFF); + *(pDstline+4) = (mng_uint8)(iB >> 8); + *(pDstline+5) = (mng_uint8)(iB && 0xFF); + } + + pSrcline++; + pDstline += 6; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_IDX8_RGB16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_promote_idx8_rgba8 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pPromBuf; + mng_uint8p pSrcline = (mng_uint8p)pData->pPromSrc; + mng_uint8p pDstline = (mng_uint8p)pData->pPromDst; + mng_uint32 iX; + mng_uint8 iB; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_IDX8_RGBA8, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iPromWidth; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iPromWidth; iX++) +#endif + { + iB = *pSrcline; + + if ((mng_uint32)iB < pBuf->iPLTEcount) + { + *pDstline = pBuf->aPLTEentries [iB].iRed; + *(pDstline+1) = pBuf->aPLTEentries [iB].iGreen; + *(pDstline+2) = pBuf->aPLTEentries [iB].iBlue; + + if ((pBuf->bHasTRNS) && ((mng_uint32)iB < pBuf->iTRNScount)) + *(pDstline+3) = pBuf->aTRNSentries [iB]; + else + *(pDstline+3) = 0xFF; + } + + pSrcline++; + pDstline += 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_IDX8_RGBA8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_promote_idx8_rgba16 (mng_datap pData) +{ + mng_imagedatap pBuf = (mng_imagedatap)pData->pPromBuf; + mng_uint8p pSrcline = (mng_uint8p)pData->pPromSrc; + mng_uint8p pDstline = (mng_uint8p)pData->pPromDst; + mng_uint32 iX; + mng_uint8 iN; + mng_uint16 iR; + mng_uint16 iG; + mng_uint16 iB; + mng_uint16 iA; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_IDX8_RGBA16, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iPromWidth; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iPromWidth; iX++) +#endif + { + iN = *pSrcline; + + if ((mng_uint32)iN < pBuf->iPLTEcount) + { + iR = ((mng_bitdepth_16)pData->fPromBitdepth) (pBuf->aPLTEentries [iN].iRed); + iG = ((mng_bitdepth_16)pData->fPromBitdepth) (pBuf->aPLTEentries [iN].iGreen); + iB = ((mng_bitdepth_16)pData->fPromBitdepth) (pBuf->aPLTEentries [iN].iBlue); + + if ((pBuf->bHasTRNS) && ((mng_uint32)iN < pBuf->iTRNScount)) + iA = ((mng_bitdepth_16)pData->fPromBitdepth) (pBuf->aTRNSentries [iN]); + else + iA = 0xFFFF; + + *pDstline = (mng_uint8)(iR >> 8); + *(pDstline+1) = (mng_uint8)(iR && 0xFF); + *(pDstline+2) = (mng_uint8)(iG >> 8); + *(pDstline+3) = (mng_uint8)(iG && 0xFF); + *(pDstline+4) = (mng_uint8)(iB >> 8); + *(pDstline+5) = (mng_uint8)(iB && 0xFF); + *(pDstline+6) = (mng_uint8)(iA >> 8); + *(pDstline+7) = (mng_uint8)(iA && 0xFF); + } + + pSrcline++; + pDstline += 8; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_IDX8_RGBA16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_promote_rgba8_rgba16 (mng_datap pData) +{ + mng_uint8p pSrcline = (mng_uint8p)pData->pPromSrc; + mng_uint8p pDstline = (mng_uint8p)pData->pPromDst; + mng_uint32 iX; + mng_uint16 iR; + mng_uint16 iG; + mng_uint16 iB; + mng_uint16 iA; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_RGBA8_RGBA16, MNG_LC_START); +#endif + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iPromWidth; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iPromWidth; iX++) +#endif + { + iR = ((mng_bitdepth_16)pData->fPromBitdepth) (*pSrcline); + iG = ((mng_bitdepth_16)pData->fPromBitdepth) (*(pSrcline+1)); + iB = ((mng_bitdepth_16)pData->fPromBitdepth) (*(pSrcline+2)); + iA = ((mng_bitdepth_16)pData->fPromBitdepth) (*(pSrcline+3)); + + *pDstline = (mng_uint8)(iR >> 8); + *(pDstline+1) = (mng_uint8)(iR && 0xFF); + *(pDstline+2) = (mng_uint8)(iG >> 8); + *(pDstline+3) = (mng_uint8)(iG && 0xFF); + *(pDstline+4) = (mng_uint8)(iB >> 8); + *(pDstline+5) = (mng_uint8)(iB && 0xFF); + *(pDstline+6) = (mng_uint8)(iA >> 8); + *(pDstline+7) = (mng_uint8)(iA && 0xFF); + + pSrcline += 4; + pDstline += 8; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROMOTE_RGBA8_RGBA16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif /* !defined(MNG_NO_DELTA_PNG) || !defined(MNG_SKIPCHUNK_PAST) || !defined(MNG_SKIPCHUNK_MAGN) */ + +/* ************************************************************************** */ +/* * * */ +/* * Row processing routines - convert uncompressed data from zlib to * */ +/* * managable row-data which serves as input to the color-management * */ +/* * routines * */ +/* * * */ +/* ************************************************************************** */ + +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_process_g1 (mng_datap pData) +{ + mng_uint8p pWorkrow; + mng_uint8p pRGBArow; + mng_int32 iX; + mng_uint8 iB; + mng_uint8 iM; + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_G1, MNG_LC_START); +#endif + + if (!pBuf) /* no object? then use obj 0 */ + pBuf = ((mng_imagep)pData->pObjzero)->pImgbuf; + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pRGBArow = pData->pRGBArow; + iM = 0; /* start at pixel 0 */ + iB = 0; + + if (pBuf->bHasTRNS) /* tRNS encountered ? */ + { + if (pBuf->iTRNSgray) /* white transparent ? */ + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0x80; + } + + if (iB & iM) /* is it white ? */ + /* transparent ! */ + mng_put_uint32 (pRGBArow, 0x00000000); + else /* opaque black */ + mng_put_uint32 (pRGBArow, 0x000000FF); + + pRGBArow += 4; /* next pixel */ + iM >>= 1; + } + } + else /* black transparent */ + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0x80; + } + + if (iB & iM) /* is it white ? */ + /* opaque white */ + mng_put_uint32 (pRGBArow, 0xFFFFFFFF); + else /* transparent */ + mng_put_uint32 (pRGBArow, 0x00000000); + + pRGBArow += 4; /* next pixel */ + iM >>= 1; + } + } + + pData->bIsOpaque = MNG_FALSE; /* it's not fully opaque */ + } + else /* no transparency */ + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0x80; + } + + if (iB & iM) /* is it white ? */ + /* opaque white */ + mng_put_uint32 (pRGBArow, 0xFFFFFFFF); + else /* opaque black */ + mng_put_uint32 (pRGBArow, 0x000000FF); + + pRGBArow += 4; /* next pixel */ + iM >>= 1; + } + + pData->bIsOpaque = MNG_TRUE; /* it's fully opaque */ + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_G1, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_process_g2 (mng_datap pData) +{ + mng_uint8p pWorkrow; + mng_uint8p pRGBArow; + mng_int32 iX; + mng_uint8 iB; + mng_uint8 iM; + mng_uint32 iS; + mng_uint8 iQ; + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; +#ifdef MNG_OPTIMIZE_FOOTPRINT_SWITCH + const mng_uint32 level[4] = { 0x000000FF, 0x555555FF, + 0xAAAAAAFF, 0xFFFFFFFF}; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_G2, MNG_LC_START); +#endif + + if (!pBuf) /* no object? then use obj 0 */ + pBuf = ((mng_imagep)pData->pObjzero)->pImgbuf; + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pRGBArow = pData->pRGBArow; + iM = 0; /* start at pixel 0 */ + iB = 0; + iS = 0; + + if (pBuf->bHasTRNS) /* tRNS encountered ? */ + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0xC0; + iS = 6; + } + /* determine gray level */ + iQ = (mng_uint8)((iB & iM) >> iS); + + if (iQ == pBuf->iTRNSgray) /* transparent ? */ + mng_put_uint32 (pRGBArow, 0x00000000); + else + { +#ifdef MNG_OPTIMIZE_FOOTPRINT_SWITCH + mng_put_uint32 (pRGBArow, level[iQ]); +#else + switch (iQ) /* determine the gray level */ + { + case 0x03 : { mng_put_uint32 (pRGBArow, 0xFFFFFFFF); break; } + case 0x02 : { mng_put_uint32 (pRGBArow, 0xAAAAAAFF); break; } + case 0x01 : { mng_put_uint32 (pRGBArow, 0x555555FF); break; } + default : { mng_put_uint32 (pRGBArow, 0x000000FF); } + } +#endif + } + + pRGBArow += 4; /* next pixel */ + iM >>= 2; + iS -= 2; + } + + pData->bIsOpaque = MNG_FALSE; /* it's not fully opaque */ + } + else + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0xC0; + iS = 6; + } + +#ifdef MNG_OPTIMIZE_FOOTPRINT_SWITCH + mng_put_uint32 (pRGBArow, level[((iB & iM) >> iS)] ); +#else + switch ((iB & iM) >> iS) /* determine the gray level */ + { + case 0x03 : { mng_put_uint32 (pRGBArow, 0xFFFFFFFF); break; } + case 0x02 : { mng_put_uint32 (pRGBArow, 0xAAAAAAFF); break; } + case 0x01 : { mng_put_uint32 (pRGBArow, 0x555555FF); break; } + default : { mng_put_uint32 (pRGBArow, 0x000000FF); } + } +#endif + + pRGBArow += 4; /* next pixel */ + iM >>= 2; + iS -= 2; + } + + pData->bIsOpaque = MNG_TRUE; /* it's fully opaque */ + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_G2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_process_g4 (mng_datap pData) +{ + mng_uint8p pWorkrow; + mng_uint8p pRGBArow; + mng_int32 iX; + mng_uint8 iB; + mng_uint8 iM; + mng_uint32 iS; + mng_uint8 iQ; + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_G4, MNG_LC_START); +#endif + + if (!pBuf) /* no object? then use obj 0 */ + pBuf = ((mng_imagep)pData->pObjzero)->pImgbuf; + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pRGBArow = pData->pRGBArow; + iM = 0; /* start at pixel 0 */ + iB = 0; + iS = 0; + + if (pBuf->bHasTRNS) /* tRNS encountered ? */ + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0xF0; + iS = 4; + } + /* get the gray level */ + iQ = (mng_uint8)((iB & iM) >> iS); + + if (iQ == pBuf->iTRNSgray) /* transparent ? */ + { + *pRGBArow = 0; /* put in intermediate row */ + *(pRGBArow+1) = 0; + *(pRGBArow+2) = 0; + *(pRGBArow+3) = 0; + } + else + { /* expand to 8-bit by replication */ + iQ = (mng_uint8)(iQ + (iQ << 4)); + + *pRGBArow = iQ; /* put in intermediate row */ + *(pRGBArow+1) = iQ; + *(pRGBArow+2) = iQ; + *(pRGBArow+3) = 0xFF; + } + + pRGBArow += 4; /* next pixel */ + iM >>= 4; + iS -= 4; + } + + pData->bIsOpaque = MNG_FALSE; /* it's not fully opaque */ + } + else + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0xF0; + iS = 4; + } + /* get the gray level */ + iQ = (mng_uint8)((iB & iM) >> iS); + iQ = (mng_uint8)(iQ + (iQ << 4));/* expand to 8-bit by replication */ + + *pRGBArow = iQ; /* put in intermediate row */ + *(pRGBArow+1) = iQ; + *(pRGBArow+2) = iQ; + *(pRGBArow+3) = 0xFF; + + pRGBArow += 4; /* next pixel */ + iM >>= 4; + iS -= 4; + } + + pData->bIsOpaque = MNG_TRUE; /* it's fully opaque */ + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_G4, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + +/* ************************************************************************** */ + +mng_retcode mng_process_g8 (mng_datap pData) +{ + mng_uint8p pWorkrow; + mng_uint8p pRGBArow; + mng_int32 iX; + mng_uint8 iB; + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_G8, MNG_LC_START); +#endif + + if (!pBuf) /* no object? then use obj 0 */ + pBuf = ((mng_imagep)pData->pObjzero)->pImgbuf; + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pRGBArow = pData->pRGBArow; + + if (pBuf->bHasTRNS) /* tRNS encountered ? */ + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + iB = *pWorkrow; /* get next input-byte */ + + if (iB == pBuf->iTRNSgray) /* transparent ? */ + { + *pRGBArow = 0; /* put in intermediate row */ + *(pRGBArow+1) = 0; + *(pRGBArow+2) = 0; + *(pRGBArow+3) = 0; + } + else + { + *pRGBArow = iB; /* put in intermediate row */ + *(pRGBArow+1) = iB; + *(pRGBArow+2) = iB; + *(pRGBArow+3) = 0xFF; + } + + pRGBArow += 4; /* next pixel */ + pWorkrow++; + } + + pData->bIsOpaque = MNG_FALSE; /* it's not fully opaque */ + } + else + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + iB = *pWorkrow; /* get next input-byte */ + + *pRGBArow = iB; /* put in intermediate row */ + *(pRGBArow+1) = iB; + *(pRGBArow+2) = iB; + *(pRGBArow+3) = 0xFF; + + pRGBArow += 4; /* next pixel */ + pWorkrow++; + } + + pData->bIsOpaque = MNG_TRUE; /* it's fully opaque */ + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_G8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_process_g16 (mng_datap pData) +{ + mng_uint8p pWorkrow; + mng_uint8p pRGBArow; + mng_int32 iX; + mng_uint16 iW; + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_G16, MNG_LC_START); +#endif + + if (!pBuf) /* no object? then use obj 0 */ + pBuf = ((mng_imagep)pData->pObjzero)->pImgbuf; + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pRGBArow = pData->pRGBArow; + + if (pBuf->bHasTRNS) /* tRNS encountered ? */ + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + iW = mng_get_uint16 (pWorkrow); /* get input */ + + if (iW == pBuf->iTRNSgray) /* transparent ? */ + { /* put in intermediate row */ + mng_put_uint16 (pRGBArow, 0); + mng_put_uint16 (pRGBArow+2, 0); + mng_put_uint16 (pRGBArow+4, 0); + mng_put_uint16 (pRGBArow+6, 0); + } + else + { /* put in intermediate row */ + mng_put_uint16 (pRGBArow, iW); + mng_put_uint16 (pRGBArow+2, iW); + mng_put_uint16 (pRGBArow+4, iW); + mng_put_uint16 (pRGBArow+6, 0xFFFF); + } + + pRGBArow += 8; /* next pixel */ + pWorkrow += 2; + } + + pData->bIsOpaque = MNG_FALSE; /* it's not fully opaque */ + } + else + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + iW = mng_get_uint16 (pWorkrow); /* get input */ + + mng_put_uint16 (pRGBArow, iW); /* and put in intermediate row */ + mng_put_uint16 (pRGBArow+2, iW); + mng_put_uint16 (pRGBArow+4, iW); + mng_put_uint16 (pRGBArow+6, 0xFFFF); + + pRGBArow += 8; /* next pixel */ + pWorkrow += 2; + } + + pData->bIsOpaque = MNG_TRUE; /* it's fully opaque */ + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_G16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_process_rgb8 (mng_datap pData) +{ + mng_uint8p pWorkrow; + mng_uint8p pRGBArow; + mng_int32 iX; + mng_uint8 iR, iG, iB; + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_RGB8, MNG_LC_START); +#endif + + if (!pBuf) /* no object? then use obj 0 */ + pBuf = ((mng_imagep)pData->pObjzero)->pImgbuf; + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pRGBArow = pData->pRGBArow; + + if (pBuf->bHasTRNS) /* tRNS encountered ? */ + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + iR = *pWorkrow; /* get the RGB values */ + iG = *(pWorkrow+1); + iB = *(pWorkrow+2); + /* transparent ? */ + if ((iR == pBuf->iTRNSred) && (iG == pBuf->iTRNSgreen) && + (iB == pBuf->iTRNSblue)) + { + *pRGBArow = 0; /* this pixel is transparent ! */ + *(pRGBArow+1) = 0; + *(pRGBArow+2) = 0; + *(pRGBArow+3) = 0; + } + else + { + *pRGBArow = iR; /* copy the RGB values */ + *(pRGBArow+1) = iG; + *(pRGBArow+2) = iB; + *(pRGBArow+3) = 0xFF; /* this one isn't transparent */ + } + + pWorkrow += 3; /* next pixel */ + pRGBArow += 4; + } + + pData->bIsOpaque = MNG_FALSE; /* it's not fully opaque */ + } + else + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pRGBArow = *pWorkrow; /* copy the RGB bytes */ + *(pRGBArow+1) = *(pWorkrow+1); + *(pRGBArow+2) = *(pWorkrow+2); + *(pRGBArow+3) = 0xFF; /* no alpha; so always fully opaque */ + + pWorkrow += 3; /* next pixel */ + pRGBArow += 4; + } + + pData->bIsOpaque = MNG_TRUE; /* it's fully opaque */ + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_RGB8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_process_rgb16 (mng_datap pData) +{ + mng_uint8p pWorkrow; + mng_uint8p pRGBArow; + mng_int32 iX; + mng_uint16 iR, iG, iB; + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_RGB16, MNG_LC_START); +#endif + + if (!pBuf) /* no object? then use obj 0 */ + pBuf = ((mng_imagep)pData->pObjzero)->pImgbuf; + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pRGBArow = pData->pRGBArow; + + if (pBuf->bHasTRNS) /* tRNS encountered ? */ + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + iR = mng_get_uint16 (pWorkrow); /* get the RGB values */ + iG = mng_get_uint16 (pWorkrow+2); + iB = mng_get_uint16 (pWorkrow+4); + /* transparent ? */ + if ((iR == pBuf->iTRNSred) && (iG == pBuf->iTRNSgreen) && + (iB == pBuf->iTRNSblue)) + { /* transparent then */ + mng_put_uint16 (pRGBArow, 0); + mng_put_uint16 (pRGBArow+2, 0); + mng_put_uint16 (pRGBArow+4, 0); + mng_put_uint16 (pRGBArow+6, 0); + } + else + { /* put in intermediate row */ + mng_put_uint16 (pRGBArow, iR); + mng_put_uint16 (pRGBArow+2, iG); + mng_put_uint16 (pRGBArow+4, iB); + mng_put_uint16 (pRGBArow+6, 0xFFFF); + } + + pWorkrow += 6; /* next pixel */ + pRGBArow += 8; + } + + pData->bIsOpaque = MNG_FALSE; /* it's not fully opaque */ + } + else + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { /* copy the RGB values */ + mng_put_uint16 (pRGBArow, mng_get_uint16 (pWorkrow )); + mng_put_uint16 (pRGBArow+2, mng_get_uint16 (pWorkrow+2)); + mng_put_uint16 (pRGBArow+4, mng_get_uint16 (pWorkrow+4)); + mng_put_uint16 (pRGBArow+6, 0xFFFF); + + pWorkrow += 6; /* next pixel */ + pRGBArow += 8; + } + + pData->bIsOpaque = MNG_TRUE; /* it's fully opaque */ + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_RGB16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_process_idx1 (mng_datap pData) +{ + mng_uint8p pWorkrow; + mng_uint8p pRGBArow; + mng_int32 iX; + mng_uint8 iB; + mng_uint8 iM; + mng_uint32 iS; + mng_uint8 iQ; + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_IDX1, MNG_LC_START); +#endif + + if (!pBuf) /* no object? then use obj 0 */ + pBuf = ((mng_imagep)pData->pObjzero)->pImgbuf; + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pRGBArow = pData->pRGBArow; + iM = 0; /* start at pixel 0 */ + iB = 0; + iS = 0; + + if (pBuf->bHasTRNS) /* tRNS encountered ? */ + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0x80; + iS = 7; + } + /* get the index */ + iQ = (mng_uint8)((iB & iM) >> iS); + /* index valid ? */ + if ((mng_uint32)iQ < pBuf->iPLTEcount) + { /* put in intermediate row */ + *pRGBArow = pBuf->aPLTEentries [iQ].iRed; + *(pRGBArow+1) = pBuf->aPLTEentries [iQ].iGreen; + *(pRGBArow+2) = pBuf->aPLTEentries [iQ].iBlue; + /* transparency for this index ? */ + if ((mng_uint32)iQ < pBuf->iTRNScount) + *(pRGBArow+3) = pBuf->aTRNSentries [iQ]; + else + *(pRGBArow+3) = 0xFF; + } + else + MNG_ERROR (pData, MNG_PLTEINDEXERROR); + + pRGBArow += 4; /* next pixel */ + iM >>= 1; + iS -= 1; + } + + pData->bIsOpaque = MNG_FALSE; /* it's not fully opaque */ + } + else + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0x80; + iS = 7; + } + /* get the index */ + iQ = (mng_uint8)((iB & iM) >> iS); + /* index valid ? */ + if ((mng_uint32)iQ < pBuf->iPLTEcount) + { /* put in intermediate row */ + *pRGBArow = pBuf->aPLTEentries [iQ].iRed; + *(pRGBArow+1) = pBuf->aPLTEentries [iQ].iGreen; + *(pRGBArow+2) = pBuf->aPLTEentries [iQ].iBlue; + *(pRGBArow+3) = 0xFF; + } + else + MNG_ERROR (pData, MNG_PLTEINDEXERROR); + + pRGBArow += 4; /* next pixel */ + iM >>= 1; + iS -= 1; + } + + pData->bIsOpaque = MNG_TRUE; /* it's fully opaque */ + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_IDX1, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_process_idx2 (mng_datap pData) +{ + mng_uint8p pWorkrow; + mng_uint8p pRGBArow; + mng_int32 iX; + mng_uint8 iB; + mng_uint8 iM; + mng_uint32 iS; + mng_uint8 iQ; + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_IDX2, MNG_LC_START); +#endif + + if (!pBuf) /* no object? then use obj 0 */ + pBuf = ((mng_imagep)pData->pObjzero)->pImgbuf; + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pRGBArow = pData->pRGBArow; + iM = 0; /* start at pixel 0 */ + iB = 0; + iS = 0; + + if (pBuf->bHasTRNS) /* tRNS encountered ? */ + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0xC0; + iS = 6; + } + /* get the index */ + iQ = (mng_uint8)((iB & iM) >> iS); + /* index valid ? */ + if ((mng_uint32)iQ < pBuf->iPLTEcount) + { /* put in intermediate row */ + *pRGBArow = pBuf->aPLTEentries [iQ].iRed; + *(pRGBArow+1) = pBuf->aPLTEentries [iQ].iGreen; + *(pRGBArow+2) = pBuf->aPLTEentries [iQ].iBlue; + /* transparency for this index ? */ + if ((mng_uint32)iQ < pBuf->iTRNScount) + *(pRGBArow+3) = pBuf->aTRNSentries [iQ]; + else + *(pRGBArow+3) = 0xFF; + } + else + MNG_ERROR (pData, MNG_PLTEINDEXERROR); + + pRGBArow += 4; /* next pixel */ + iM >>= 2; + iS -= 2; + } + + pData->bIsOpaque = MNG_FALSE; /* it's not fully opaque */ + } + else + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = *pWorkrow; /* get next input-byte */ + pWorkrow++; + iM = 0xC0; + iS = 6; + } + /* get the index */ + iQ = (mng_uint8)((iB & iM) >> iS); + /* index valid ? */ + if ((mng_uint32)iQ < pBuf->iPLTEcount) + { /* put in intermediate row */ + *pRGBArow = pBuf->aPLTEentries [iQ].iRed; + *(pRGBArow+1) = pBuf->aPLTEentries [iQ].iGreen; + *(pRGBArow+2) = pBuf->aPLTEentries [iQ].iBlue; + *(pRGBArow+3) = 0xFF; + } + else + MNG_ERROR (pData, MNG_PLTEINDEXERROR); + + pRGBArow += 4; /* next pixel */ + iM >>= 2; + iS -= 2; + } + + pData->bIsOpaque = MNG_TRUE; /* it's fully opaque */ + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_IDX2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_process_idx4 (mng_datap pData) +{ + mng_uint8p pWorkrow; + mng_uint8p pRGBArow; + mng_int32 iX; + mng_uint8 iB; + mng_uint8 iM; + mng_uint32 iS; + mng_uint8 iQ; + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_IDX4, MNG_LC_START); +#endif + + if (!pBuf) /* no object? then use obj 0 */ + pBuf = ((mng_imagep)pData->pObjzero)->pImgbuf; + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pRGBArow = pData->pRGBArow; + iM = 0; /* start at pixel 0 */ + iB = 0; + iS = 0; + + if (pBuf->bHasTRNS) /* tRNS encountered ? */ + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = pWorkrow [0]; /* get next input-byte */ + pWorkrow++; + iM = 0xF0; + iS = 4; + } + /* get the index */ + iQ = (mng_uint8)((iB & iM) >> iS); + /* index valid ? */ + if ((mng_uint32)iQ < pBuf->iPLTEcount) + { /* put in intermediate row */ + pRGBArow [0] = pBuf->aPLTEentries [iQ].iRed; + pRGBArow [1] = pBuf->aPLTEentries [iQ].iGreen; + pRGBArow [2] = pBuf->aPLTEentries [iQ].iBlue; + /* transparency for this index ? */ + if ((mng_uint32)iQ < pBuf->iTRNScount) + pRGBArow [3] = pBuf->aTRNSentries [iQ]; + else + pRGBArow [3] = 0xFF; + } + else + MNG_ERROR (pData, MNG_PLTEINDEXERROR); + + pRGBArow += 4; /* next pixel */ + iM >>= 4; + iS -= 4; + } + + pData->bIsOpaque = MNG_FALSE; /* it's not fully opaque */ + } + else + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + if (!iM) /* mask underflow ? */ + { + iB = pWorkrow [0]; /* get next input-byte */ + pWorkrow++; + iM = 0xF0; + iS = 4; + } + /* get the index */ + iQ = (mng_uint8)((iB & iM) >> iS); + /* index valid ? */ + if ((mng_uint32)iQ < pBuf->iPLTEcount) + { /* put in intermediate row */ + pRGBArow [0] = pBuf->aPLTEentries [iQ].iRed; + pRGBArow [1] = pBuf->aPLTEentries [iQ].iGreen; + pRGBArow [2] = pBuf->aPLTEentries [iQ].iBlue; + pRGBArow [3] = 0xFF; + } + else + MNG_ERROR (pData, MNG_PLTEINDEXERROR); + + pRGBArow += 4; /* next pixel */ + iM >>= 4; + iS -= 4; + } + + pData->bIsOpaque = MNG_TRUE; /* it's fully opaque */ + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_IDX4, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + +/* ************************************************************************** */ + +mng_retcode mng_process_idx8 (mng_datap pData) +{ + mng_uint8p pWorkrow; + mng_uint8p pRGBArow; + mng_int32 iX; + mng_uint8 iQ; + mng_imagedatap pBuf = (mng_imagedatap)pData->pStorebuf; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_IDX8, MNG_LC_START); +#endif + + if (!pBuf) /* no object? then use obj 0 */ + pBuf = ((mng_imagep)pData->pObjzero)->pImgbuf; + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pRGBArow = pData->pRGBArow; + + if (pBuf->bHasTRNS) /* tRNS encountered ? */ + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + iQ = *pWorkrow; /* get input byte */ + /* index valid ? */ + if ((mng_uint32)iQ < pBuf->iPLTEcount) + { /* put in intermediate row */ + pRGBArow [0] = pBuf->aPLTEentries [iQ].iRed; + pRGBArow [1] = pBuf->aPLTEentries [iQ].iGreen; + pRGBArow [2] = pBuf->aPLTEentries [iQ].iBlue; + /* transparency for this index ? */ + if ((mng_uint32)iQ < pBuf->iTRNScount) + pRGBArow [3] = pBuf->aTRNSentries [iQ]; + else + pRGBArow [3] = 0xFF; + } + else + MNG_ERROR (pData, MNG_PLTEINDEXERROR); + + pRGBArow += 4; /* next pixel */ + pWorkrow++; + } + + pData->bIsOpaque = MNG_FALSE; /* it's not fully opaque */ + } + else + { +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + iQ = *pWorkrow; /* get input byte */ + /* index valid ? */ + if ((mng_uint32)iQ < pBuf->iPLTEcount) + { /* put in intermediate row */ + pRGBArow [0] = pBuf->aPLTEentries [iQ].iRed; + pRGBArow [1] = pBuf->aPLTEentries [iQ].iGreen; + pRGBArow [2] = pBuf->aPLTEentries [iQ].iBlue; + pRGBArow [3] = 0xFF; + } + else + MNG_ERROR (pData, MNG_PLTEINDEXERROR); + + pRGBArow += 4; /* next pixel */ + pWorkrow++; + } + + pData->bIsOpaque = MNG_TRUE; /* it's fully opaque */ + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_IDX8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_process_ga8 (mng_datap pData) +{ + mng_uint8p pWorkrow; + mng_uint8p pRGBArow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_GA8, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pRGBArow = pData->pRGBArow; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + *pRGBArow = *pWorkrow; /* copy the gray value */ + *(pRGBArow+1) = *pWorkrow; + *(pRGBArow+2) = *pWorkrow; + *(pRGBArow+3) = *(pWorkrow+1); /* copy the alpha value */ + + pWorkrow += 2; /* next pixel */ + pRGBArow += 4; + } + + pData->bIsOpaque = MNG_FALSE; /* it's definitely not fully opaque */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_GA8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_process_ga16 (mng_datap pData) +{ + mng_uint8p pWorkrow; + mng_uint8p pRGBArow; + mng_int32 iX; + mng_uint16 iW; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_GA16, MNG_LC_START); +#endif + /* temporary work pointers */ + pWorkrow = pData->pWorkrow + pData->iPixelofs; + pRGBArow = pData->pRGBArow; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + iW = mng_get_uint16 (pWorkrow); /* copy the gray value */ + mng_put_uint16 (pRGBArow, iW); + mng_put_uint16 (pRGBArow+2, iW); + mng_put_uint16 (pRGBArow+4, iW); + /* copy the alpha value */ + mng_put_uint16 (pRGBArow+6, mng_get_uint16 (pWorkrow+2)); + + pWorkrow += 4; /* next pixel */ + pRGBArow += 8; + } + + pData->bIsOpaque = MNG_FALSE; /* it's definitely not fully opaque */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_GA16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_process_rgba8 (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_RGBA8, MNG_LC_START); +#endif + /* this is the easiest transform */ + MNG_COPY (pData->pRGBArow, pData->pWorkrow + pData->iPixelofs, pData->iRowsize); + + pData->bIsOpaque = MNG_FALSE; /* it's definitely not fully opaque */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_RGBA8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_process_rgba16 (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_RGBA16, MNG_LC_START); +#endif + /* this is the easiest transform */ + MNG_COPY (pData->pRGBArow, pData->pWorkrow + pData->iPixelofs, pData->iRowsize); + + pData->bIsOpaque = MNG_FALSE; /* it's definitely not fully opaque */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_RGBA16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ +/* * * */ +/* * Row processing initialization routines - set up the variables needed * */ +/* * to process uncompressed row-data * */ +/* * * */ +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_FOOTPRINT_INIT +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_init_g1_ni (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_G1_NI, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_g1; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_g1; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_g1; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_g1; +#endif + + pData->iPass = -1; + pData->iRow = 0; + pData->iRowinc = 1; + pData->iCol = 0; + pData->iColinc = 1; + pData->iRowsamples = pData->iDatawidth; + pData->iSamplemul = 1; + pData->iSampleofs = 7; + pData->iSamplediv = 3; + pData->iRowsize = (pData->iRowsamples + 7) >> 3; + pData->iRowmax = pData->iRowsize + pData->iPixelofs; + pData->iFilterbpp = 1; + pData->bIsRGBA16 = MNG_FALSE; /* intermediate row is 8-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_G1_NI, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_init_g1_i (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_G1_I, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_g1; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_g1; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_g1; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_g1; +#endif + + pData->iPass = 0; /* from 0..6; is 1..7 in specifications */ + pData->iRow = interlace_row [0]; + pData->iRowinc = interlace_rowskip [0]; + pData->iCol = interlace_col [0]; + pData->iColinc = interlace_colskip [0]; + pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0]; + pData->iSamplemul = 1; + pData->iSampleofs = 7; + pData->iSamplediv = 3; + pData->iRowsize = ((pData->iRowsamples + 7) >> 3); + pData->iRowmax = ((pData->iDatawidth + 7) >> 3) + pData->iPixelofs; + pData->iFilterbpp = 1; + pData->bIsRGBA16 = MNG_FALSE; /* intermediate row is 8-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_G1_I, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_init_g2_ni (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_G2_NI, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_g2; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_g2; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_g2; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_g2; +#endif + + pData->iPass = -1; + pData->iRow = 0; + pData->iRowinc = 1; + pData->iCol = 0; + pData->iColinc = 1; + pData->iRowsamples = pData->iDatawidth; + pData->iSamplemul = 1; + pData->iSampleofs = 3; + pData->iSamplediv = 2; + pData->iRowsize = (pData->iRowsamples + 3) >> 2; + pData->iRowmax = pData->iRowsize + pData->iPixelofs; + pData->iFilterbpp = 1; + pData->bIsRGBA16 = MNG_FALSE; /* intermediate row is 8-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_G2_NI, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_init_g2_i (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_G2_I, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_g2; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_g2; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_g2; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_g2; +#endif + + pData->iPass = 0; /* from 0..6; is 1..7 in specifications */ + pData->iRow = interlace_row [0]; + pData->iRowinc = interlace_rowskip [0]; + pData->iCol = interlace_col [0]; + pData->iColinc = interlace_colskip [0]; + pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0]; + pData->iSamplemul = 1; + pData->iSampleofs = 3; + pData->iSamplediv = 2; + pData->iRowsize = ((pData->iRowsamples + 3) >> 2); + pData->iRowmax = ((pData->iDatawidth + 3) >> 2) + pData->iPixelofs; + pData->iFilterbpp = 1; + pData->bIsRGBA16 = MNG_FALSE; /* intermediate row is 8-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_G2_I, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_init_g4_ni (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_G4_NI, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_g4; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_g4; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_g4; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_g4; +#endif + + pData->iPass = -1; + pData->iRow = 0; + pData->iRowinc = 1; + pData->iCol = 0; + pData->iColinc = 1; + pData->iRowsamples = pData->iDatawidth; + pData->iSamplemul = 1; + pData->iSampleofs = 1; + pData->iSamplediv = 1; + pData->iRowsize = (pData->iRowsamples + 1) >> 1; + pData->iRowmax = pData->iRowsize + pData->iPixelofs; + pData->iFilterbpp = 1; + pData->bIsRGBA16 = MNG_FALSE; /* intermediate row is 8-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_G4_NI, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_init_g4_i (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_G4_I, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_g4; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_g4; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_g4; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_g4; +#endif + + pData->iPass = 0; /* from 0..6; is 1..7 in specifications */ + pData->iRow = interlace_row [0]; + pData->iRowinc = interlace_rowskip [0]; + pData->iCol = interlace_col [0]; + pData->iColinc = interlace_colskip [0]; + pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0]; + pData->iSamplemul = 1; + pData->iSampleofs = 1; + pData->iSamplediv = 1; + pData->iRowsize = ((pData->iRowsamples + 1) >> 1); + pData->iRowmax = ((pData->iDatawidth + 1) >> 1) + pData->iPixelofs; + pData->iFilterbpp = 1; + pData->bIsRGBA16 = MNG_FALSE; /* intermediate row is 8-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_G4_I, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + +/* ************************************************************************** */ + +mng_retcode mng_init_g8_ni (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_G8_NI, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_g8; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_g8; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_g8; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_g8; +#endif + + pData->iPass = -1; + pData->iRow = 0; + pData->iRowinc = 1; + pData->iCol = 0; + pData->iColinc = 1; + pData->iRowsamples = pData->iDatawidth; + pData->iSamplemul = 1; + pData->iSampleofs = 0; + pData->iSamplediv = 0; + pData->iRowsize = pData->iRowsamples; + pData->iRowmax = pData->iRowsize + pData->iPixelofs; + pData->iFilterbpp = 1; + pData->bIsRGBA16 = MNG_FALSE; /* intermediate row is 8-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_G8_NI, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_init_g8_i (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_G8_I, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_g8; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_g8; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_g8; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_g8; +#endif + + pData->iPass = 0; /* from 0..6; is 1..7 in specifications */ + pData->iRow = interlace_row [0]; + pData->iRowinc = interlace_rowskip [0]; + pData->iCol = interlace_col [0]; + pData->iColinc = interlace_colskip [0]; + pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0]; + pData->iSamplemul = 1; + pData->iSampleofs = 0; + pData->iSamplediv = 0; + pData->iRowsize = pData->iRowsamples; + pData->iRowmax = pData->iDatawidth + pData->iPixelofs; + pData->iFilterbpp = 1; + pData->bIsRGBA16 = MNG_FALSE; /* intermediate row is 8-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_G8_I, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_init_g16_ni (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_G16_NI, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_g16; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_g16; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_g16; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_g16; +#endif + + pData->iPass = -1; + pData->iRow = 0; + pData->iRowinc = 1; + pData->iCol = 0; + pData->iColinc = 1; + pData->iRowsamples = pData->iDatawidth; + pData->iSamplemul = 2; + pData->iSampleofs = 0; + pData->iSamplediv = 0; + pData->iRowsize = pData->iRowsamples << 1; + pData->iRowmax = pData->iRowsize + pData->iPixelofs; + pData->iFilterbpp = 2; + pData->bIsRGBA16 = MNG_TRUE; /* intermediate row is 16-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_G16_NI, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_init_g16_i (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_G16_I, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_g16; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_g16; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_g16; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_g16; +#endif + + pData->iPass = 0; /* from 0..6; is 1..7 in specifications */ + pData->iRow = interlace_row [0]; + pData->iRowinc = interlace_rowskip [0]; + pData->iCol = interlace_col [0]; + pData->iColinc = interlace_colskip [0]; + pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0]; + pData->iSamplemul = 2; + pData->iSampleofs = 0; + pData->iSamplediv = 0; + pData->iRowsize = pData->iRowsamples << 1; + pData->iRowmax = (pData->iDatawidth << 1) + pData->iPixelofs; + pData->iFilterbpp = 2; + pData->bIsRGBA16 = MNG_TRUE; /* intermediate row is 16-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_G16_I, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_init_rgb8_ni (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_RGB8_NI, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_rgb8; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_rgb8; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_rgb8; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_rgb8; +#endif + + pData->iPass = -1; + pData->iRow = 0; + pData->iRowinc = 1; + pData->iCol = 0; + pData->iColinc = 1; + pData->iRowsamples = pData->iDatawidth; + pData->iSamplemul = 3; + pData->iSampleofs = 0; + pData->iSamplediv = 0; + pData->iRowsize = pData->iRowsamples * 3; + pData->iRowmax = pData->iRowsize + pData->iPixelofs; + pData->iFilterbpp = 3; + pData->bIsRGBA16 = MNG_FALSE; /* intermediate row is 8-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_RGB8_NI, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_init_rgb8_i (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_RGB8_I, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_rgb8; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_rgb8; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_rgb8; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_rgb8; +#endif + + pData->iPass = 0; /* from 0..6; is 1..7 in specifications */ + pData->iRow = interlace_row [0]; + pData->iRowinc = interlace_rowskip [0]; + pData->iCol = interlace_col [0]; + pData->iColinc = interlace_colskip [0]; + pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0]; + pData->iSamplemul = 3; + pData->iSampleofs = 0; + pData->iSamplediv = 0; + pData->iRowsize = pData->iRowsamples * 3; + pData->iRowmax = (pData->iDatawidth * 3) + pData->iPixelofs; + pData->iFilterbpp = 3; + pData->bIsRGBA16 = MNG_FALSE; /* intermediate row is 8-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_RGB8_I, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_init_rgb16_ni (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_RGB16_NI, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_rgb16; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_rgb16; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_rgb16; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_rgb16; +#endif + + pData->iPass = -1; + pData->iRow = 0; + pData->iRowinc = 1; + pData->iCol = 0; + pData->iColinc = 1; + pData->iRowsamples = pData->iDatawidth; + pData->iSamplemul = 6; + pData->iSampleofs = 0; + pData->iSamplediv = 0; + pData->iRowsize = pData->iRowsamples * 6; + pData->iRowmax = pData->iRowsize + pData->iPixelofs; + pData->iFilterbpp = 6; + pData->bIsRGBA16 = MNG_TRUE; /* intermediate row is 16-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_RGB16_NI, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_init_rgb16_i (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_RGB16_I, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_rgb16; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_rgb16; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_rgb16; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_rgb16; +#endif + + pData->iPass = 0; /* from 0..6; is 1..7 in specifications */ + pData->iRow = interlace_row [0]; + pData->iRowinc = interlace_rowskip [0]; + pData->iCol = interlace_col [0]; + pData->iColinc = interlace_colskip [0]; + pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0]; + pData->iSamplemul = 6; + pData->iSampleofs = 0; + pData->iSamplediv = 0; + pData->iRowsize = pData->iRowsamples * 6; + pData->iRowmax = (pData->iDatawidth * 6) + pData->iPixelofs; + pData->iFilterbpp = 6; + pData->bIsRGBA16 = MNG_TRUE; /* intermediate row is 16-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_RGB16_I, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_init_idx1_ni (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_IDX1_NI, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_idx1; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_idx1; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_idx1; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_idx1; +#endif + + pData->iPass = -1; + pData->iRow = 0; + pData->iRowinc = 1; + pData->iCol = 0; + pData->iColinc = 1; + pData->iRowsamples = pData->iDatawidth; + pData->iSamplemul = 1; + pData->iSampleofs = 7; + pData->iSamplediv = 3; + pData->iRowsize = (pData->iRowsamples + 7) >> 3; + pData->iRowmax = pData->iRowsize + pData->iPixelofs; + pData->iFilterbpp = 1; + pData->bIsRGBA16 = MNG_FALSE; /* intermediate row is 8-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_IDX1_NI, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_init_idx1_i (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_IDX1_I, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_idx1; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_idx1; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_idx1; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_idx1; +#endif + + pData->iPass = 0; /* from 0..6; is 1..7 in specifications */ + pData->iRow = interlace_row [0]; + pData->iRowinc = interlace_rowskip [0]; + pData->iCol = interlace_col [0]; + pData->iColinc = interlace_colskip [0]; + pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0]; + pData->iSamplemul = 1; + pData->iSampleofs = 7; + pData->iSamplediv = 3; + pData->iRowsize = (pData->iRowsamples + 7) >> 3; + pData->iRowmax = ((pData->iDatawidth + 7) >> 3) + pData->iPixelofs; + pData->iFilterbpp = 1; + pData->bIsRGBA16 = MNG_FALSE; /* intermediate row is 8-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_IDX1_I, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_init_idx2_ni (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_IDX2_NI, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_idx2; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_idx2; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_idx2; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_idx2; +#endif + + pData->iPass = -1; + pData->iRow = 0; + pData->iRowinc = 1; + pData->iCol = 0; + pData->iColinc = 1; + pData->iRowsamples = pData->iDatawidth; + pData->iSamplemul = 1; + pData->iSampleofs = 3; + pData->iSamplediv = 2; + pData->iRowsize = (pData->iRowsamples + 3) >> 2; + pData->iRowmax = pData->iRowsize + pData->iPixelofs; + pData->iFilterbpp = 1; + pData->bIsRGBA16 = MNG_FALSE; /* intermediate row is 8-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_IDX2_NI, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_init_idx2_i (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_IDX2_I, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_idx2; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_idx2; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_idx2; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_idx2; +#endif + + pData->iPass = 0; /* from 0..6; is 1..7 in specifications */ + pData->iRow = interlace_row [0]; + pData->iRowinc = interlace_rowskip [0]; + pData->iCol = interlace_col [0]; + pData->iColinc = interlace_colskip [0]; + pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0]; + pData->iSamplemul = 1; + pData->iSampleofs = 3; + pData->iSamplediv = 2; + pData->iRowsize = (pData->iRowsamples + 3) >> 2; + pData->iRowmax = ((pData->iDatawidth + 3) >> 2) + pData->iPixelofs; + pData->iFilterbpp = 1; + pData->bIsRGBA16 = MNG_FALSE; /* intermediate row is 8-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_IDX2_I, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_init_idx4_ni (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_IDX4_NI, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_idx4; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_idx4; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_idx4; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_idx4; +#endif + + pData->iPass = -1; + pData->iRow = 0; + pData->iRowinc = 1; + pData->iCol = 0; + pData->iColinc = 1; + pData->iRowsamples = pData->iDatawidth; + pData->iSamplemul = 1; + pData->iSampleofs = 1; + pData->iSamplediv = 1; + pData->iRowsize = (pData->iRowsamples + 1) >> 1; + pData->iRowmax = pData->iRowsize + pData->iPixelofs; + pData->iFilterbpp = 1; + pData->bIsRGBA16 = MNG_FALSE; /* intermediate row is 8-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_IDX4_NI, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_init_idx4_i (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_IDX4_I, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_idx4; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_idx4; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_idx4; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_idx4; +#endif + + pData->iPass = 0; /* from 0..6; is 1..7 in specifications */ + pData->iRow = interlace_row [0]; + pData->iRowinc = interlace_rowskip [0]; + pData->iCol = interlace_col [0]; + pData->iColinc = interlace_colskip [0]; + pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0]; + pData->iSamplemul = 1; + pData->iSampleofs = 1; + pData->iSamplediv = 1; + pData->iRowsize = (pData->iRowsamples + 1) >> 1; + pData->iRowmax = ((pData->iDatawidth + 1) >> 1) + pData->iPixelofs; + pData->iFilterbpp = 1; + pData->bIsRGBA16 = MNG_FALSE; /* intermediate row is 8-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_IDX4_I, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + +/* ************************************************************************** */ + +mng_retcode mng_init_idx8_ni (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_IDX8_NI, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_idx8; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_idx8; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_idx8; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_idx8; +#endif + + pData->iPass = -1; + pData->iRow = 0; + pData->iRowinc = 1; + pData->iCol = 0; + pData->iColinc = 1; + pData->iRowsamples = pData->iDatawidth; + pData->iSamplemul = 1; + pData->iSampleofs = 0; + pData->iSamplediv = 0; + pData->iRowsize = pData->iRowsamples; + pData->iRowmax = pData->iRowsize + pData->iPixelofs; + pData->iFilterbpp = 1; + pData->bIsRGBA16 = MNG_FALSE; /* intermediate row is 8-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_IDX8_NI, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_init_idx8_i (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_IDX8_I, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_idx8; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_idx8; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_idx8; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_idx8; +#endif + + pData->iPass = 0; /* from 0..6; is 1..7 in specifications */ + pData->iRow = interlace_row [0]; + pData->iRowinc = interlace_rowskip [0]; + pData->iCol = interlace_col [0]; + pData->iColinc = interlace_colskip [0]; + pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0]; + pData->iSamplemul = 1; + pData->iSampleofs = 0; + pData->iSamplediv = 0; + pData->iRowsize = pData->iRowsamples; + pData->iRowmax = pData->iDatawidth + pData->iPixelofs; + pData->iFilterbpp = 1; + pData->bIsRGBA16 = MNG_FALSE; /* intermediate row is 8-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_IDX8_I, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_init_ga8_ni (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_GA8_NI, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_ga8; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_ga8; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_ga8; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_ga8; +#endif + + pData->iPass = -1; + pData->iRow = 0; + pData->iRowinc = 1; + pData->iCol = 0; + pData->iColinc = 1; + pData->iRowsamples = pData->iDatawidth; + pData->iSamplemul = 2; + pData->iSampleofs = 0; + pData->iSamplediv = 0; + pData->iRowsize = pData->iRowsamples << 1; + pData->iRowmax = pData->iRowsize + pData->iPixelofs; + pData->iFilterbpp = 2; + pData->bIsRGBA16 = MNG_FALSE; /* intermediate row is 8-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_GA8_NI, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_init_ga8_i (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_GA8_I, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_ga8; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_ga8; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_ga8; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_ga8; +#endif + + pData->iPass = 0; /* from 0..6; is 1..7 in specifications */ + pData->iRow = interlace_row [0]; + pData->iRowinc = interlace_rowskip [0]; + pData->iCol = interlace_col [0]; + pData->iColinc = interlace_colskip [0]; + pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0]; + pData->iSamplemul = 2; + pData->iSampleofs = 0; + pData->iSamplediv = 0; + pData->iRowsize = pData->iRowsamples << 1; + pData->iRowmax = (pData->iDatawidth << 1) + pData->iPixelofs; + pData->iFilterbpp = 2; + pData->bIsRGBA16 = MNG_FALSE; /* intermediate row is 8-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_GA8_I, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_init_ga16_ni (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_GA16_NI, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_ga16; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_ga16; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_ga16; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_ga16; +#endif + + pData->iPass = -1; + pData->iRow = 0; + pData->iRowinc = 1; + pData->iCol = 0; + pData->iColinc = 1; + pData->iRowsamples = pData->iDatawidth; + pData->iSamplemul = 4; + pData->iSampleofs = 0; + pData->iSamplediv = 0; + pData->iRowsize = pData->iRowsamples << 2; + pData->iRowmax = pData->iRowsize + pData->iPixelofs; + pData->iFilterbpp = 4; + pData->bIsRGBA16 = MNG_TRUE; /* intermediate row is 16-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_GA16_NI, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_init_ga16_i (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_GA16_I, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_ga16; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_ga16; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_ga16; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_ga16; +#endif + + pData->iPass = 0; /* from 0..6; is 1..7 in specifications */ + pData->iRow = interlace_row [0]; + pData->iRowinc = interlace_rowskip [0]; + pData->iCol = interlace_col [0]; + pData->iColinc = interlace_colskip [0]; + pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0]; + pData->iSamplemul = 4; + pData->iSampleofs = 0; + pData->iSamplediv = 0; + pData->iRowsize = pData->iRowsamples << 2; + pData->iRowmax = (pData->iDatawidth << 2) + pData->iPixelofs; + pData->iFilterbpp = 4; + pData->bIsRGBA16 = MNG_TRUE; /* intermediate row is 16-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_GA16_I, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_init_rgba8_ni (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_RGBA8_NI, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_rgba8; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_rgba8; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_rgba8; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_rgba8; +#endif + + pData->iPass = -1; + pData->iRow = 0; + pData->iRowinc = 1; + pData->iCol = 0; + pData->iColinc = 1; + pData->iRowsamples = pData->iDatawidth; + pData->iSamplemul = 4; + pData->iSampleofs = 0; + pData->iSamplediv = 0; + pData->iRowsize = pData->iRowsamples << 2; + pData->iRowmax = pData->iRowsize + pData->iPixelofs; + pData->iFilterbpp = 4; + pData->bIsRGBA16 = MNG_FALSE; /* intermediate row is 8-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_RGBA8_NI, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} +/* ************************************************************************** */ + +mng_retcode mng_init_rgba8_i (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_RGBA8_I, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_rgba8; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_rgba8; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_rgba8; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_rgba8; +#endif + + pData->iPass = 0; /* from 0..6; is 1..7 in specifications */ + pData->iRow = interlace_row [0]; + pData->iRowinc = interlace_rowskip [0]; + pData->iCol = interlace_col [0]; + pData->iColinc = interlace_colskip [0]; + pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0]; + pData->iSamplemul = 4; + pData->iSampleofs = 0; + pData->iSamplediv = 0; + pData->iRowsize = pData->iRowsamples << 2; + pData->iRowmax = (pData->iDatawidth << 2) + pData->iPixelofs; + pData->iFilterbpp = 4; + pData->bIsRGBA16 = MNG_FALSE; /* intermediate row is 8-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_RGBA8_I, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_init_rgba16_ni (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_RGBA16_NI, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_rgba16; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_rgba16; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_rgba16; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_rgba16; +#endif + + pData->iPass = -1; + pData->iRow = 0; + pData->iRowinc = 1; + pData->iCol = 0; + pData->iColinc = 1; + pData->iRowsamples = pData->iDatawidth; + pData->iSamplemul = 8; + pData->iSampleofs = 0; + pData->iSamplediv = 0; + pData->iRowsize = pData->iRowsamples << 3; + pData->iRowmax = pData->iRowsize + pData->iPixelofs; + pData->iFilterbpp = 8; + pData->bIsRGBA16 = MNG_TRUE; /* intermediate row is 16-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_RGBA16_NI, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_init_rgba16_i (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_RGBA16_I, MNG_LC_START); +#endif + + if (pData->fDisplayrow) + pData->fProcessrow = (mng_fptr)mng_process_rgba16; + + if (pData->pStoreobj) /* store in object too ? */ + { /* immediate delta ? */ +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + pData->fStorerow = (mng_fptr)mng_delta_rgba16; + else +#endif + pData->fStorerow = (mng_fptr)mng_store_rgba16; + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_rgba16; +#endif + + pData->iPass = 0; /* from 0..6; (1..7 in specification) */ + pData->iRow = interlace_row [0]; + pData->iRowinc = interlace_rowskip [0]; + pData->iCol = interlace_col [0]; + pData->iColinc = interlace_colskip [0]; + pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> interlace_divider [0]; + pData->iSamplemul = 8; + pData->iSampleofs = 0; + pData->iSamplediv = 0; + pData->iRowsize = pData->iRowsamples << 3; + pData->iRowmax = (pData->iDatawidth << 3) + pData->iPixelofs; + pData->iFilterbpp = 8; + pData->bIsRGBA16 = MNG_TRUE; /* intermediate row is 16-bit deep */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_RGBA16_I, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} +#endif + +/* ************************************************************************** */ +/* * * */ +/* * Row processing initialization routines (JPEG) - set up the variables * */ +/* * needed to process uncompressed row-data * */ +/* * * */ +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG + +/* ************************************************************************** */ + +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_init_jpeg_a1_ni (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_JPEG_A1_NI, MNG_LC_START); +#endif + + if (pData->pStoreobj) /* store in object too ? */ + { + if (pData->iJHDRimgbitdepth == 8) + { + switch (pData->iJHDRcolortype) + { + case 12 : { pData->fStorerow = (mng_fptr)mng_store_jpeg_g8_a1; break; } + case 14 : { pData->fStorerow = (mng_fptr)mng_store_jpeg_rgb8_a1; break; } + } + } + + /* TODO: bitdepth 12 & 20 */ + + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_g1; +#endif + + pData->iPass = -1; + pData->iRow = 0; + pData->iRowinc = 1; + pData->iCol = 0; + pData->iColinc = 1; + pData->iRowsamples = pData->iDatawidth; + pData->iSamplemul = 1; + pData->iSampleofs = 7; + pData->iSamplediv = 3; + pData->iRowsize = (pData->iRowsamples + 7) >> 3; + pData->iRowmax = pData->iRowsize + pData->iPixelofs; + pData->iFilterbpp = 1; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_JPEG_A1_NI, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_init_jpeg_a2_ni (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_JPEG_A2_NI, MNG_LC_START); +#endif + + if (pData->pStoreobj) /* store in object too ? */ + { + if (pData->iJHDRimgbitdepth == 8) + { + switch (pData->iJHDRcolortype) + { + case 12 : { pData->fStorerow = (mng_fptr)mng_store_jpeg_g8_a2; break; } + case 14 : { pData->fStorerow = (mng_fptr)mng_store_jpeg_rgb8_a2; break; } + } + } + + /* TODO: bitdepth 12 & 20 */ + + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_g2; +#endif + + pData->iPass = -1; + pData->iRow = 0; + pData->iRowinc = 1; + pData->iCol = 0; + pData->iColinc = 1; + pData->iRowsamples = pData->iDatawidth; + pData->iSamplemul = 1; + pData->iSampleofs = 3; + pData->iSamplediv = 2; + pData->iRowsize = (pData->iRowsamples + 3) >> 2; + pData->iRowmax = pData->iRowsize + pData->iPixelofs; + pData->iFilterbpp = 1; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_JPEG_A2_NI, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_init_jpeg_a4_ni (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_JPEG_A4_NI, MNG_LC_START); +#endif + + if (pData->pStoreobj) /* store in object too ? */ + { + if (pData->iJHDRimgbitdepth == 8) + { + switch (pData->iJHDRcolortype) + { + case 12 : { pData->fStorerow = (mng_fptr)mng_store_jpeg_g8_a4; break; } + case 14 : { pData->fStorerow = (mng_fptr)mng_store_jpeg_rgb8_a4; break; } + } + } + + /* TODO: bitdepth 12 & 20 */ + + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_g4; +#endif + + pData->iPass = -1; + pData->iRow = 0; + pData->iRowinc = 1; + pData->iCol = 0; + pData->iColinc = 1; + pData->iRowsamples = pData->iDatawidth; + pData->iSamplemul = 1; + pData->iSampleofs = 1; + pData->iSamplediv = 1; + pData->iRowsize = (pData->iRowsamples + 1) >> 1; + pData->iRowmax = pData->iRowsize + pData->iPixelofs; + pData->iFilterbpp = 1; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_JPEG_A4_NI, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + +/* ************************************************************************** */ + +mng_retcode mng_init_jpeg_a8_ni (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_JPEG_A8_NI, MNG_LC_START); +#endif + + if (pData->pStoreobj) /* store in object too ? */ + { + if (pData->iJHDRimgbitdepth == 8) + { + switch (pData->iJHDRcolortype) + { + case 12 : { pData->fStorerow = (mng_fptr)mng_store_jpeg_g8_a8; break; } + case 14 : { pData->fStorerow = (mng_fptr)mng_store_jpeg_rgb8_a8; break; } + } + } + + /* TODO: bitdepth 12 & 20 */ + + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_g8; +#endif + + pData->iPass = -1; + pData->iRow = 0; + pData->iRowinc = 1; + pData->iCol = 0; + pData->iColinc = 1; + pData->iRowsamples = pData->iDatawidth; + pData->iSamplemul = 1; + pData->iSampleofs = 0; + pData->iSamplediv = 0; + pData->iRowsize = pData->iRowsamples; + pData->iRowmax = pData->iRowsize + pData->iPixelofs; + pData->iFilterbpp = 1; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_JPEG_A8_NI, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_init_jpeg_a16_ni (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_JPEG_A16_NI, MNG_LC_START); +#endif + + if (pData->pStoreobj) /* store in object too ? */ + { + if (pData->iJHDRimgbitdepth == 8) + { + switch (pData->iJHDRcolortype) + { + case 12 : { pData->fStorerow = (mng_fptr)mng_store_jpeg_g8_a16; break; } + case 14 : { pData->fStorerow = (mng_fptr)mng_store_jpeg_rgb8_a16; break; } + } + } + + /* TODO: bitdepth 12 & 20 */ + + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + pData->fDifferrow = (mng_fptr)mng_differ_g16; +#endif + + pData->iPass = -1; + pData->iRow = 0; + pData->iRowinc = 1; + pData->iCol = 0; + pData->iColinc = 1; + pData->iRowsamples = pData->iDatawidth; + pData->iSamplemul = 2; + pData->iSampleofs = 0; + pData->iSamplediv = 0; + pData->iRowsize = pData->iRowsamples << 1; + pData->iRowmax = pData->iRowsize + pData->iPixelofs; + pData->iFilterbpp = 2; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_JPEG_A16_NI, MNG_LC_END); +#endif + + return mng_init_rowproc (pData); +} +#endif + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_JNG */ +#endif /* MNG_OPTIMIZE_FOOTPRINT_INIT */ + + +/* ************************************************************************** */ +/* * * */ +/* * Generic row processing initialization & cleanup routines * */ +/* * - initialize the buffers used by the row processing routines * */ +/* * - cleanup the buffers used by the row processing routines * */ +/* * * */ +/* ************************************************************************** */ + +mng_retcode mng_init_rowproc (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_ROWPROC, MNG_LC_START); +#endif + +#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT + if (pData->ePng_imgtype != png_none) + { + if (pData->fDisplayrow) + switch (pData->ePng_imgtype) + { +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case png_g1: + pData->fProcessrow = (mng_fptr)mng_process_g1; + break; + case png_g2: + pData->fProcessrow = (mng_fptr)mng_process_g2; + break; + case png_g4: + pData->fProcessrow = (mng_fptr)mng_process_g4; + break; +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + case png_g8: + pData->fProcessrow = (mng_fptr)mng_process_g8; + break; +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case png_idx1: + pData->fProcessrow = (mng_fptr)mng_process_idx1; + break; + case png_idx2: + pData->fProcessrow = (mng_fptr)mng_process_idx2; + break; + case png_idx4: + pData->fProcessrow = (mng_fptr)mng_process_idx4; + break; +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + case png_idx8: + pData->fProcessrow = (mng_fptr)mng_process_idx8; + break; + case png_ga8: + pData->fProcessrow = (mng_fptr)mng_process_ga8; + break; + case png_rgb8: + pData->fProcessrow = (mng_fptr)mng_process_rgb8; + break; + case png_rgba8: + pData->fProcessrow = (mng_fptr)mng_process_rgba8; + break; +#ifndef MNG_NO_16BIT_SUPPORT + case png_g16: + pData->fProcessrow = (mng_fptr)mng_process_g16; + break; + case png_ga16: + pData->fProcessrow = (mng_fptr)mng_process_ga16; + break; + case png_rgb16: + pData->fProcessrow = (mng_fptr)mng_process_rgb16; + break; + case png_rgba16: + pData->fProcessrow = (mng_fptr)mng_process_rgba16; + break; +#endif + default: + break; + } + + if (pData->pStoreobj) /* store in object too ? */ + { +#ifndef MNG_NO_DELTA_PNG + if ((pData->bHasDHDR) && (pData->bDeltaimmediate)) + switch (pData->ePng_imgtype) + { +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case png_g1: + pData->fStorerow = (mng_fptr)mng_delta_g1; + break; + case png_g2: + pData->fStorerow = (mng_fptr)mng_delta_g2; + break; + case png_g4: + pData->fStorerow = (mng_fptr)mng_delta_g4; + break; +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + case png_g8: + pData->fStorerow = (mng_fptr)mng_delta_g8; + break; +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case png_idx1: + pData->fStorerow = (mng_fptr)mng_delta_idx1; + break; + case png_idx2: + pData->fStorerow = (mng_fptr)mng_delta_idx2; + break; + case png_idx4: + pData->fStorerow = (mng_fptr)mng_delta_idx4; + break; +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + case png_idx8: + pData->fStorerow = (mng_fptr)mng_delta_idx8; + break; + case png_ga8: + pData->fStorerow = (mng_fptr)mng_delta_ga8; + break; + case png_rgb8: + pData->fStorerow = (mng_fptr)mng_delta_rgb8; + break; + case png_rgba8: + pData->fStorerow = (mng_fptr)mng_delta_rgba8; + break; +#ifndef MNG_NO_16BIT_SUPPORT + case png_g16: + pData->fStorerow = (mng_fptr)mng_delta_g16; + break; + case png_ga16: + pData->fStorerow = (mng_fptr)mng_delta_ga16; + break; + case png_rgb16: + pData->fStorerow = (mng_fptr)mng_delta_rgb16; + break; + case png_rgba16: + pData->fStorerow = (mng_fptr)mng_delta_rgba16; + break; +#endif + default: + break; + } + else +#endif /* MNG_NO_DELTA_PNG */ + switch (pData->ePng_imgtype) + { +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case png_g1: + pData->fStorerow = (mng_fptr)mng_store_g1; + break; + case png_g2: + pData->fStorerow = (mng_fptr)mng_store_g2; + break; + case png_g4: + pData->fStorerow = (mng_fptr)mng_store_g4; + break; +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + case png_g8: + pData->fStorerow = (mng_fptr)mng_store_g8; + break; +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case png_idx1: + pData->fStorerow = (mng_fptr)mng_store_idx1; + break; + case png_idx2: + pData->fStorerow = (mng_fptr)mng_store_idx2; + break; + case png_idx4: + pData->fStorerow = (mng_fptr)mng_store_idx4; + break; +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + case png_idx8: + pData->fStorerow = (mng_fptr)mng_store_idx8; + break; + case png_ga8: + pData->fStorerow = (mng_fptr)mng_store_ga8; + break; + case png_rgb8: + pData->fStorerow = (mng_fptr)mng_store_rgb8; + break; + case png_rgba8: + pData->fStorerow = (mng_fptr)mng_store_rgba8; + break; +#ifndef MNG_NO_16BIT_SUPPORT + case png_g16: + pData->fStorerow = (mng_fptr)mng_store_g16; + break; + case png_ga16: + pData->fStorerow = (mng_fptr)mng_store_ga16; + break; + case png_rgb16: + pData->fStorerow = (mng_fptr)mng_store_rgb16; + break; + case png_rgba16: + pData->fStorerow = (mng_fptr)mng_store_rgba16; + break; +#endif + +#ifdef MNG_INCLUDE_JNG +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case png_jpeg_a1: +/* if (pData->iJHDRimgbitdepth == 8) */ + { + switch (pData->iJHDRcolortype) + { + case 12 : + { pData->fStorerow = (mng_fptr)mng_store_jpeg_g8_a1; break; } + case 14 : + { pData->fStorerow = (mng_fptr)mng_store_jpeg_rgb8_a1; break; } + } + } + /* TODO: bitdepth 12 & 20 */ + break; + case png_jpeg_a2: +/* if (pData->iJHDRimgbitdepth == 8) */ + { + switch (pData->iJHDRcolortype) + { + case 12 : + { pData->fStorerow = (mng_fptr)mng_store_jpeg_g8_a2; break; } + case 14 : + { pData->fStorerow = (mng_fptr)mng_store_jpeg_rgb8_a2; break; } + } + } + break; + /* TODO: bitdepth 12 & 20 */ + case png_jpeg_a4: +/* if (pData->iJHDRimgbitdepth == 8) */ + { + switch (pData->iJHDRcolortype) + { + case 12 : + { pData->fStorerow = (mng_fptr)mng_store_jpeg_g8_a4; break; } + case 14 : + { pData->fStorerow = (mng_fptr)mng_store_jpeg_rgb8_a4; break; } + } + } + break; +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + /* TODO: bitdepth 12 & 20 */ + case png_jpeg_a8: +/* if (pData->iJHDRimgbitdepth == 8) */ + { + switch (pData->iJHDRcolortype) + { + case 12 : + { pData->fStorerow = (mng_fptr)mng_store_jpeg_g8_a8; break; } + case 14 : + { pData->fStorerow = (mng_fptr)mng_store_jpeg_rgb8_a8; break; } + } + } + break; + /* TODO: bitdepth 12 & 20 */ +#ifndef MNG_NO_16BIT_SUPPORT + case png_jpeg_a16: +/* if (pData->iJHDRimgbitdepth == 8) */ + { + switch (pData->iJHDRcolortype) + { + case 12 : + { pData->fStorerow = (mng_fptr)mng_store_jpeg_g8_a16; break; } + case 14 : + { pData->fStorerow = (mng_fptr)mng_store_jpeg_rgb8_a16; break; } + } + } + break; + /* TODO: bitdepth 12 & 20 */ +#endif +#endif /* MNG_INCLUDE_JNG */ + default: + break; + } + } + +#ifdef FILTER192 /* leveling & differing ? */ + if (pData->iFilter == MNG_FILTER_DIFFERING) + switch (pData->ePng_imgtype) + { +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case png_g1: +#ifdef MNG_INCLUDE_JNG + case png_jpeg_a1: +#endif + pData->fDifferrow = (mng_fptr)mng_differ_g1; + break; + case png_g2: +#ifdef MNG_INCLUDE_JNG + case png_jpeg_a2: +#endif + pData->fDifferrow = (mng_fptr)mng_differ_g2; + break; + case png_g4: +#ifdef MNG_INCLUDE_JNG + case png_jpeg_a4: +#endif + pData->fDifferrow = (mng_fptr)mng_differ_g4; + break; +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + case png_g8: +#ifdef MNG_INCLUDE_JNG + case png_jpeg_a8: +#endif + pData->fDifferrow = (mng_fptr)mng_differ_g8; + break; + case png_rgb8: + pData->fDifferrow = (mng_fptr)mng_differ_rgb8; + break; +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case png_idx1: + pData->fDifferrow = (mng_fptr)mng_differ_idx1; + break; + case png_idx2: + pData->fDifferrow = (mng_fptr)mng_differ_idx2; + break; + case png_idx4: + pData->fDifferrow = (mng_fptr)mng_differ_idx4; + break; +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + case png_idx8: + pData->fDifferrow = (mng_fptr)mng_differ_idx8; + break; + case png_ga8: + pData->fDifferrow = (mng_fptr)mng_differ_ga8; + break; + case png_rgb8: + pData->fDifferrow = (mng_fptr)mng_differ_rgb8; + break; + case png_rgba8: + pData->fDifferrow = (mng_fptr)mng_differ_rgba8; + break; +#ifndef MNG_NO_16BIT_SUPPORT + case png_g16: +#ifdef MNG_INCLUDE_JNG + case png_jpeg_a16: +#endif + pData->fDifferrow = (mng_fptr)mng_differ_g16; + break; + case png_ga16: + pData->fDifferrow = (mng_fptr)mng_differ_ga16; + break; + case png_rgb16: + pData->fDifferrow = (mng_fptr)mng_differ_rgb16; + break; + case png_rgba16: + pData->fDifferrow = (mng_fptr)mng_differ_rgba16; + break; +#endif + default: + break; + } +#endif + + switch (pData->ePng_imgtype) + { +#ifndef MNG_NO_1_2_4BIT_SUPPORT + case png_g1: + case png_idx1: +#ifdef MNG_INCLUDE_JNG + case png_jpeg_a1: +#endif + pData->iSamplemul = 1; + pData->iSampleofs = 7; + pData->iSamplediv = 3; + pData->iFilterbpp = 1; + break; + case png_g2: + case png_idx2: +#ifdef MNG_INCLUDE_JNG + case png_jpeg_a2: +#endif + pData->iSamplemul = 1; + pData->iSampleofs = 3; + pData->iSamplediv = 2; + pData->iFilterbpp = 1; + break; + case png_g4: + case png_idx4: +#ifdef MNG_INCLUDE_JNG + case png_jpeg_a4: +#endif + pData->iSamplemul = 1; + pData->iSampleofs = 1; + pData->iSamplediv = 1; + pData->iFilterbpp = 1; + break; +#endif /* MNG_NO_1_2_4BIT_SUPPORT */ + case png_g8: + case png_idx8: +#ifdef MNG_INCLUDE_JNG + case png_jpeg_a8: +#endif + pData->iSamplemul = 1; + pData->iSampleofs = 0; + pData->iSamplediv = 0; + pData->iFilterbpp = 1; + break; + case png_ga8: +#ifndef MNG_NO_16BIT_SUPPORT + case png_g16: +#ifdef MNG_INCLUDE_JNG + case png_jpeg_a16: +#endif +#endif + pData->iSamplemul = 2; + pData->iSampleofs = 0; + pData->iSamplediv = 0; + pData->iFilterbpp = 2; + break; + case png_rgb8: + pData->iSamplemul = 3; + pData->iSampleofs = 0; + pData->iSamplediv = 0; + pData->iFilterbpp = 3; + break; +#ifndef MNG_NO_16BIT_SUPPORT + case png_ga16: +#endif + case png_rgba8: + pData->iSamplemul = 4; + pData->iSampleofs = 0; + pData->iSamplediv = 0; + pData->iFilterbpp = 4; + break; +#ifndef MNG_NO_16BIT_SUPPORT + case png_rgb16: + pData->iSamplemul = 6; + pData->iSampleofs = 0; + pData->iSamplediv = 0; + pData->iFilterbpp = 6; + break; + case png_rgba16: + pData->iSamplemul = 8; + pData->iSampleofs = 0; + pData->iSamplediv = 0; + pData->iFilterbpp = 8; + break; +#endif + default: + break; + } + + if (pData->iInterlace) /* noninterlaced */ + { + pData->iPass = 0; /* from 0..6; (1..7 in specification) */ + pData->iRow = interlace_row [0]; + pData->iRowinc = interlace_rowskip [0]; + pData->iCol = interlace_col [0]; + pData->iColinc = interlace_colskip [0]; + pData->iRowsamples = (pData->iDatawidth + interlace_roundoff [0]) >> + interlace_divider [0]; + pData->iRowmax = ((pData->iDatawidth * pData->iSamplemul + + pData->iSampleofs) >> pData->iSamplediv) + pData->iPixelofs; + } + else /* interlaced */ + { + pData->iPass = -1; + pData->iRow = 0; + pData->iRowinc = 1; + pData->iCol = 0; + pData->iColinc = 1; + pData->iRowsamples = pData->iDatawidth; + } + if (pData->iSamplediv > 0) + pData->iRowsize = (pData->iRowsamples + pData->iSampleofs) >> + pData->iSamplediv; + else + pData->iRowsize = (pData->iRowsamples * pData->iSamplemul); + + if (!pData->iInterlace) /* noninterlaced */ + pData->iRowmax = pData->iRowsize + pData->iPixelofs; + +#ifdef MNG_NO_16BIT_SUPPORT + pData->bIsRGBA16 = MNG_FALSE; +#else + switch (pData->ePng_imgtype) + { + case png_g16: + case png_ga16: + case png_rgb16: + case png_rgba16: + pData->bIsRGBA16 = MNG_TRUE; + break; + default: + pData->bIsRGBA16 = MNG_FALSE; + break; + } +#endif + + } +#endif /* MNG_OPTIMIZE_FOOTPRINT_INIT */ + + if (pData->pStoreobj) /* storage object selected ? */ + { + pData->pStorebuf = ((mng_imagep)pData->pStoreobj)->pImgbuf; + /* and so it becomes viewable ! */ + ((mng_imagep)pData->pStoreobj)->bViewable = MNG_TRUE; + ((mng_imagedatap)pData->pStorebuf)->bViewable = MNG_TRUE; + } + + /* allocate the buffers; the individual init routines have already + calculated the required maximum size; except in the case of a JNG + without alpha!!! */ + if (pData->iRowmax) + { +#if defined(MNG_NO_16BIT_SUPPORT) || defined (MNG_NO_1_2_4BIT_SUPPORT) + mng_uint8 iRowadd = 0; +#ifdef MNG_NO_1_2_4BIT_SUPPORT + if (pData->iPNGdepth < 8) + iRowadd=(pData->iPNGdepth*pData->iRowmax+7)/8; +#endif +#ifdef MNG_NO_16BIT_SUPPORT + if (pData->iPNGdepth > 8) + iRowadd=pData->iRowmax; +#endif + MNG_ALLOC (pData, pData->pWorkrow, pData->iRowmax+iRowadd); + MNG_ALLOC (pData, pData->pPrevrow, pData->iRowmax+iRowadd); +#else + MNG_ALLOC (pData, pData->pWorkrow, pData->iRowmax); + MNG_ALLOC (pData, pData->pPrevrow, pData->iRowmax); +#endif + } + + /* allocate an RGBA16 row for intermediate processing */ + MNG_ALLOC (pData, pData->pRGBArow, (pData->iDatawidth << 3)); + +#ifndef MNG_NO_CMS + if (pData->fDisplayrow) /* display "on-the-fly" ? */ + { +#if defined(MNG_FULL_CMS) /* determine color-management initialization */ + mng_retcode iRetcode = mng_init_full_cms (pData, MNG_TRUE, MNG_TRUE, MNG_FALSE); +#elif defined(MNG_GAMMA_ONLY) + mng_retcode iRetcode = mng_init_gamma_only (pData, MNG_TRUE, MNG_TRUE, MNG_FALSE); +#elif defined(MNG_APP_CMS) + mng_retcode iRetcode = mng_init_app_cms (pData, MNG_TRUE, MNG_TRUE, MNG_FALSE); +#endif + if (iRetcode) /* on error bail out */ + return iRetcode; + } +#endif /* !MNG_NO_CMS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_INIT_ROWPROC, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_next_row (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_NEXT_ROW, MNG_LC_START); +#endif + + pData->iRow += pData->iRowinc; /* increase the row counter */ + + if (pData->iPass >= 0) /* interlaced ? */ + { + while ((pData->iPass < 7) && /* went 'outside' the image ? */ + ((pData->iRow >= (mng_int32)pData->iDataheight) || + (pData->iCol >= (mng_int32)pData->iDatawidth ) )) + { + pData->iPass++; /* next pass ! */ + + if (pData->iPass < 7) /* there's only 7 passes ! */ + { + pData->iRow = interlace_row [pData->iPass]; + pData->iRowinc = interlace_rowskip [pData->iPass]; + pData->iCol = interlace_col [pData->iPass]; + pData->iColinc = interlace_colskip [pData->iPass]; + pData->iRowsamples = (pData->iDatawidth - pData->iCol + interlace_roundoff [pData->iPass]) + >> interlace_divider [pData->iPass]; + + if (pData->iSamplemul > 1) /* recalculate row dimension */ + pData->iRowsize = pData->iRowsamples * pData->iSamplemul; + else + if (pData->iSamplediv > 0) + pData->iRowsize = (pData->iRowsamples + pData->iSampleofs) >> pData->iSamplediv; + else + pData->iRowsize = pData->iRowsamples; + + } + + if ((pData->iPass < 7) && /* reset previous row to zeroes ? */ + (pData->iRow < (mng_int32)pData->iDataheight) && + (pData->iCol < (mng_int32)pData->iDatawidth ) ) + { /* making sure the filters will work properly! */ + mng_int32 iX; + mng_uint8p pTemp = pData->pPrevrow; + +#ifdef MNG_NO_16BIT_SUPPORT +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iPNGmult*pData->iRowsize; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iPNGmult*pData->iRowsize; iX++) +#endif +#else +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsize; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsize; iX++) +#endif +#endif + { + *pTemp = 0; + pTemp++; + } + } + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_NEXT_ROW, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_cleanup_rowproc (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CLEANUP_ROWPROC, MNG_LC_START); +#endif + +#ifdef MNG_INCLUDE_LCMS /* cleanup cms profile/transform */ + { + mng_retcode iRetcode = mng_clear_cms (pData); + if (iRetcode) /* on error bail out */ + return iRetcode; + } +#endif /* MNG_INCLUDE_LCMS */ + + if (pData->pRGBArow) /* cleanup buffer for intermediate row */ + MNG_FREEX (pData, pData->pRGBArow, (pData->iDatawidth << 3)); + if (pData->pPrevrow) /* cleanup buffer for previous row */ + MNG_FREEX (pData, pData->pPrevrow, pData->iRowmax); + if (pData->pWorkrow) /* cleanup buffer for working row */ + MNG_FREEX (pData, pData->pWorkrow, pData->iRowmax); + + pData->pWorkrow = MNG_NULL; /* propogate uninitialized buffers */ + pData->pPrevrow = MNG_NULL; + pData->pRGBArow = MNG_NULL; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_CLEANUP_ROWPROC, MNG_LC_END); +#endif + + return MNG_NOERROR; /* woohiii */ +} + +/* ************************************************************************** */ +/* * * */ +/* * Generic row processing routines for JNG * */ +/* * * */ +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG + +/* ************************************************************************** */ + +mng_retcode mng_display_jpeg_rows (mng_datap pData) +{ + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_JPEG_ROWS, MNG_LC_START); +#endif + /* any completed rows ? */ + if ((pData->iJPEGrow > pData->iJPEGdisprow) && + (pData->iJPEGalpharow > pData->iJPEGdisprow) ) + { + mng_uint32 iX, iMax; + mng_uint32 iSaverow = pData->iRow; /* save alpha decompression row-count */ + /* determine the highest complete(!) row */ + if (pData->iJPEGrow > pData->iJPEGalpharow) + iMax = pData->iJPEGalpharow; + else + iMax = pData->iJPEGrow; + /* display the rows */ + for (iX = pData->iJPEGdisprow; iX < iMax; iX++) + { + pData->iRow = iX; /* make sure we all know which row to handle */ + /* makeup an intermediate row from the buffer */ + iRetcode = ((mng_retrieverow)pData->fRetrieverow) (pData); + /* color-correct it if necessary */ + if ((!iRetcode) && (pData->fCorrectrow)) + iRetcode = ((mng_correctrow)pData->fCorrectrow) (pData); + + if (!iRetcode) /* and display it */ + { + iRetcode = ((mng_displayrow)pData->fDisplayrow) (pData); + + if (!iRetcode) /* check progressive display refresh */ + iRetcode = mng_display_progressive_check (pData); + } + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + + pData->iJPEGdisprow = iMax; /* keep track of the last displayed row */ + pData->iRow = iSaverow; /* restore alpha decompression row-count */ + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DISPLAY_JPEG_ROWS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_next_jpeg_alpharow (mng_datap pData) +{ + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_NEXT_JPEG_ALPHAROW, MNG_LC_START); +#endif + + pData->iJPEGalpharow++; /* count the row */ + + if (pData->fDisplayrow) /* display "on-the-fly" ? */ + { /* try to display what you can */ + iRetcode = mng_display_jpeg_rows (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_NEXT_JPEG_ALPHAROW, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_next_jpeg_row (mng_datap pData) +{ + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_NEXT_JPEG_ROW, MNG_LC_START); +#endif + + pData->iJPEGrow++; /* increase the row-counter */ + + if (pData->fDisplayrow) /* display "on-the-fly" ? */ + { /* has alpha channel ? */ + if ((pData->iJHDRcolortype == MNG_COLORTYPE_JPEGGRAYA ) || + (pData->iJHDRcolortype == MNG_COLORTYPE_JPEGCOLORA) ) + { /* try to display what you can */ + iRetcode = mng_display_jpeg_rows (pData); + } + else + { /* make sure we all know which row to handle */ + pData->iRow = pData->iJPEGrow - 1; + /* makeup an intermediate row from the buffer */ + iRetcode = ((mng_retrieverow)pData->fRetrieverow) (pData); + /* color-correct it if necessary */ + if ((!iRetcode) && (pData->fCorrectrow)) + iRetcode = ((mng_correctrow)pData->fCorrectrow) (pData); + + if (!iRetcode) /* and display it */ + { + iRetcode = ((mng_displayrow)pData->fDisplayrow) (pData); + + if (!iRetcode) /* check progressive display refresh */ + iRetcode = mng_display_progressive_check (pData); + } + } + + if (iRetcode) /* on error bail out */ + return iRetcode; + } + + /* surpassed last filled row ? */ + if (pData->iJPEGrow > pData->iJPEGrgbrow) + pData->iJPEGrgbrow = pData->iJPEGrow; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_NEXT_JPEG_ROW, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_MAGN +#ifndef MNG_OPTIMIZE_FOOTPRINT_MAGN +#ifndef MNG_NO_GRAY_SUPPORT +mng_retcode mng_magnify_g8_x1 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX, iS, iM; + mng_uint8p pTempsrc1; + mng_uint8p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_G8_X1, MNG_LC_START); +#endif + + pTempsrc1 = pSrcline; /* initialize pixel-loop */ + pTempdst = pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + + if (iX == 0) /* first interval ? */ + iM = iML; + else + if (iX == (iWidth - 1)) /* last interval ? */ + iM = iMR; + else + iM = iMX; + + for (iS = 1; iS < iM; iS++) /* fill interval */ + { + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + } + + pTempsrc1++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_G8_X1, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_g8_x2 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_int32 iS, iM; + mng_uint8p pTempsrc1; + mng_uint8p pTempsrc2; + mng_uint8p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_G8_X2, MNG_LC_START); +#endif + + pTempsrc1 = pSrcline; /* initialize pixel-loop */ + pTempdst = pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + pTempsrc2 = pTempsrc1 + 1; + + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + + if (iX == 0) /* first interval ? */ + { + if (iWidth == 1) /* single pixel ? */ + pTempsrc2 = MNG_NULL; + + iM = iML; + } + else + if (iX == (iWidth - 2)) /* last interval ? */ + iM = iMR; + else + iM = iMX; + /* fill interval ? */ + if ((iX < iWidth - 1) || (iWidth == 1)) + { + if (pTempsrc2) /* do we have the second pixel ? */ + { /* is it same as first ? */ + if (*pTempsrc1 == *pTempsrc2) + { + for (iS = 1; iS < iM; iS++) /* then just repeat the first */ + { + *pTempdst = *pTempsrc1; + pTempdst++; + } + } + else + { + for (iS = 1; iS < iM; iS++) /* calculate the distances */ + { + *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2)) + (mng_int32)(*pTempsrc1) ); + pTempdst++; + } + } + } + else + { + for (iS = 1; iS < iM; iS++) + { + *pTempdst = *pTempsrc1; /* repeat first source pixel */ + pTempdst++; + } + } + } + + pTempsrc1++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_G8_X2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_g8_x3 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_int32 iS, iM, iH; + mng_uint8p pTempsrc1; + mng_uint8p pTempsrc2; + mng_uint8p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_G8_X3, MNG_LC_START); +#endif + + pTempsrc1 = pSrcline; /* initialize pixel-loop */ + pTempdst = pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + pTempsrc2 = pTempsrc1 + 1; + + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + + if (iX == 0) /* first interval ? */ + { + if (iWidth == 1) /* single pixel ? */ + pTempsrc2 = MNG_NULL; + + iM = iML; + } + else + if (iX == (iWidth - 2)) /* last interval ? */ + iM = iMR; + else + iM = iMX; + /* fill interval ? */ + if ((iX < iWidth - 1) || (iWidth == 1)) + { + if (pTempsrc2) /* do we have the second pixel ? */ + { /* is it same as first ? */ + if (*pTempsrc1 == *pTempsrc2) + { + for (iS = 1; iS < iM; iS++) /* then just repeat the first */ + { + *pTempdst = *pTempsrc1; + pTempdst++; + } + } + else + { + iH = (iM+1) / 2; /* calculate halfway point */ + + for (iS = 1; iS < iH; iS++) /* replicate first half */ + { + *pTempdst = *pTempsrc1; + pTempdst++; + } + + for (iS = iH; iS < iM; iS++) /* replicate second half */ + { + *pTempdst = *pTempsrc2; + pTempdst++; + } + } + } + else + { + for (iS = 1; iS < iM; iS++) + { + *pTempdst = *pTempsrc1; /* repeat first source pixel */ + pTempdst++; + } + } + } + + pTempsrc1++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_G8_X3, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_GRAY_SUPPORT */ + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgb8_x1 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX, iS, iM; + mng_uint8p pTempsrc1; + mng_uint8p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB8_X1, MNG_LC_START); +#endif + + pTempsrc1 = pSrcline; /* initialize pixel-loop */ + pTempdst = pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + + if (iX == 0) /* first interval ? */ + iM = iML; + else + if (iX == (iWidth - 1)) /* last interval ? */ + iM = iMR; + else + iM = iMX; + + for (iS = 1; iS < iM; iS++) /* fill interval */ + { + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + } + + pTempsrc1 += 3; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB8_X1, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgb8_x2 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_int32 iS, iM; + mng_uint8p pTempsrc1; + mng_uint8p pTempsrc2; + mng_uint8p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB8_X2, MNG_LC_START); +#endif + + pTempsrc1 = pSrcline; /* initialize pixel-loop */ + pTempdst = pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + pTempsrc2 = pTempsrc1 + 3; + + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + + if (iX == 0) /* first interval ? */ + { + if (iWidth == 1) /* single pixel ? */ + pTempsrc2 = MNG_NULL; + + iM = (mng_int32)iML; + } + else + if (iX == (iWidth - 2)) /* last interval ? */ + iM = (mng_int32)iMR; + else + iM = (mng_int32)iMX; + /* fill interval ? */ + if ((iX < iWidth - 1) || (iWidth == 1)) + { + if (pTempsrc2) /* do we have the second pixel ? */ + { + for (iS = 1; iS < iM; iS++) + { + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; /* just repeat the first */ + else /* calculate the distance */ + *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2)) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + + if (*(pTempsrc1+1) == *(pTempsrc2+1)) + *pTempdst = *(pTempsrc1+1); + else + *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+1)) - + (mng_int32)(*(pTempsrc1+1)) ) + iM) / + (iM * 2)) + (mng_int32)(*(pTempsrc1+1)) ); + + pTempdst++; + + if (*(pTempsrc1+2) == *(pTempsrc2+2)) + *pTempdst = *(pTempsrc1+2); + else + *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+2)) - + (mng_int32)(*(pTempsrc1+2)) ) + iM) / + (iM * 2)) + (mng_int32)(*(pTempsrc1+2)) ); + + pTempdst++; + } + } + else + { + for (iS = 1; iS < iM; iS++) + { + *pTempdst = *pTempsrc1; /* repeat first source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + } + } + } + + pTempsrc1 += 3; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB8_X2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgb8_x3 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_int32 iS, iM, iH; + mng_uint8p pTempsrc1; + mng_uint8p pTempsrc2; + mng_uint8p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB8_X3, MNG_LC_START); +#endif + + pTempsrc1 = pSrcline; /* initialize pixel-loop */ + pTempdst = pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + pTempsrc2 = pTempsrc1 + 3; + + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + + if (iX == 0) /* first interval ? */ + { + if (iWidth == 1) /* single pixel ? */ + pTempsrc2 = MNG_NULL; + + iM = (mng_int32)iML; + } + else + if (iX == (iWidth - 2)) /* last interval ? */ + iM = (mng_int32)iMR; + else + iM = (mng_int32)iMX; + /* fill interval ? */ + if ((iX < iWidth - 1) || (iWidth == 1)) + { + if (pTempsrc2) /* do we have the second pixel ? */ + { + iH = (iM+1) / 2; /* calculate halfway point */ + + for (iS = 1; iS < iH; iS++) /* replicate first half */ + { + *pTempdst = *pTempsrc1; + *(pTempdst+1) = *(pTempsrc1+1); + *(pTempdst+2) = *(pTempsrc1+2); + + pTempdst += 3; + } + + for (iS = iH; iS < iM; iS++) /* replicate second half */ + { + *pTempdst = *pTempsrc2; + *(pTempdst+1) = *(pTempsrc2+1); + *(pTempdst+2) = *(pTempsrc2+2); + + pTempdst += 3; + } + } + else + { + for (iS = 1; iS < iM; iS++) + { + *pTempdst = *pTempsrc1; /* repeat first source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + } + } + } + + pTempsrc1 += 3; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB8_X3, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_GRAY_SUPPORT +mng_retcode mng_magnify_ga8_x1 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX, iS, iM; + mng_uint8p pTempsrc1; + mng_uint8p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_X1, MNG_LC_START); +#endif + + pTempsrc1 = pSrcline; /* initialize pixel-loop */ + pTempdst = pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + + if (iX == 0) /* first interval ? */ + iM = iML; + else + if (iX == (iWidth - 1)) /* last interval ? */ + iM = iMR; + else + iM = iMX; + + for (iS = 1; iS < iM; iS++) /* fill interval */ + { + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + } + + pTempsrc1 += 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_X1, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_ga8_x2 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_int32 iS, iM; + mng_uint8p pTempsrc1; + mng_uint8p pTempsrc2; + mng_uint8p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_X2, MNG_LC_START); +#endif + + pTempsrc1 = pSrcline; /* initialize pixel-loop */ + pTempdst = pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + pTempsrc2 = pTempsrc1 + 2; + + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + + if (iX == 0) /* first interval ? */ + { + if (iWidth == 1) /* single pixel ? */ + pTempsrc2 = MNG_NULL; + + iM = iML; + } + else + if (iX == (iWidth - 2)) /* last interval ? */ + iM = iMR; + else + iM = iMX; + /* fill interval ? */ + if ((iX < iWidth - 1) || (iWidth == 1)) + { + if (pTempsrc2) /* do we have the second pixel ? */ + { + for (iS = 1; iS < iM; iS++) + { + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; /* just repeat the first */ + else /* calculate the distance */ + *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2)) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + + if (*(pTempsrc1+1) == *(pTempsrc2+1)) + *pTempdst = *(pTempsrc1+1); + else + *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+1)) - + (mng_int32)(*(pTempsrc1+1)) ) + iM) / + (iM * 2)) + (mng_int32)(*(pTempsrc1+1)) ); + + pTempdst++; + } + } + else + { + for (iS = 1; iS < iM; iS++) + { + *pTempdst = *pTempsrc1; /* repeat first source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + } + } + } + + pTempsrc1 += 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_X2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_ga8_x3 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_int32 iS, iM, iH; + mng_uint8p pTempsrc1; + mng_uint8p pTempsrc2; + mng_uint8p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_X3, MNG_LC_START); +#endif + + pTempsrc1 = pSrcline; /* initialize pixel-loop */ + pTempdst = pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + pTempsrc2 = pTempsrc1 + 2; + + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + + if (iX == 0) /* first interval ? */ + { + if (iWidth == 1) /* single pixel ? */ + pTempsrc2 = MNG_NULL; + + iM = iML; + } + else + if (iX == (iWidth - 2)) /* last interval ? */ + iM = iMR; + else + iM = iMX; + /* fill interval ? */ + if ((iX < iWidth - 1) || (iWidth == 1)) + { + if (pTempsrc2) /* do we have the second pixel ? */ + { + iH = (iM+1) / 2; /* calculate halfway point */ + + for (iS = 1; iS < iH; iS++) /* replicate first half */ + { + *pTempdst = *pTempsrc1; + *(pTempdst+1) = *(pTempsrc1+1); + + pTempdst += 2; + } + + for (iS = iH; iS < iM; iS++) /* replicate second half */ + { + *pTempdst = *pTempsrc2; + *(pTempdst+1) = *(pTempsrc2+1); + + pTempdst += 2; + } + } + else + { + for (iS = 1; iS < iM; iS++) + { + *pTempdst = *pTempsrc1; /* repeat first source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + } + } + } + + pTempsrc1 += 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_X3, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_ga8_x4 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_int32 iS, iM, iH; + mng_uint8p pTempsrc1; + mng_uint8p pTempsrc2; + mng_uint8p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_X4, MNG_LC_START); +#endif + + pTempsrc1 = pSrcline; /* initialize pixel-loop */ + pTempdst = pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + pTempsrc2 = pTempsrc1 + 2; + + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + + if (iX == 0) /* first interval ? */ + { + if (iWidth == 1) /* single pixel ? */ + pTempsrc2 = MNG_NULL; + + iM = iML; + } + else + if (iX == (iWidth - 2)) /* last interval ? */ + iM = iMR; + else + iM = iMX; + /* fill interval ? */ + if ((iX < iWidth - 1) || (iWidth == 1)) + { + if (pTempsrc2) /* do we have the second pixel ? */ + { + iH = (iM+1) / 2; /* calculate halfway point */ + + for (iS = 1; iS < iH; iS++) /* first half */ + { + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; /* just repeat the first */ + else /* calculate the distance */ + *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2)) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + + *pTempdst = *(pTempsrc1+1); /* replicate alpha from left */ + + pTempdst++; + } + + for (iS = iH; iS < iM; iS++) /* second half */ + { + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; /* just repeat the first */ + else /* calculate the distance */ + *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2)) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + + *pTempdst = *(pTempsrc2+1); /* replicate alpha from right */ + + pTempdst++; + } + } + else + { + for (iS = 1; iS < iM; iS++) + { + *pTempdst = *pTempsrc1; /* repeat first source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + } + } + } + + pTempsrc1 += 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_X4, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_ga8_x5 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_int32 iS, iM, iH; + mng_uint8p pTempsrc1; + mng_uint8p pTempsrc2; + mng_uint8p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_X5, MNG_LC_START); +#endif + + pTempsrc1 = pSrcline; /* initialize pixel-loop */ + pTempdst = pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + pTempsrc2 = pTempsrc1 + 2; + + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + + if (iX == 0) /* first interval ? */ + { + if (iWidth == 1) /* single pixel ? */ + pTempsrc2 = MNG_NULL; + + iM = iML; + } + else + if (iX == (iWidth - 2)) /* last interval ? */ + iM = iMR; + else + iM = iMX; + /* fill interval ? */ + if ((iX < iWidth - 1) || (iWidth == 1)) + { + if (pTempsrc2) /* do we have the second pixel ? */ + { + iH = (iM+1) / 2; /* calculate halfway point */ + + for (iS = 1; iS < iH; iS++) /* first half */ + { + *pTempdst = *pTempsrc1; /* replicate gray from left */ + + pTempdst++; + + if (*(pTempsrc1+1) == *(pTempsrc2+1)) + *pTempdst = *(pTempsrc1+1);/* just repeat the first */ + else /* calculate the distance */ + *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+1)) - + (mng_int32)(*(pTempsrc1+1)) ) + iM) / + (iM * 2)) + (mng_int32)(*(pTempsrc1+1)) ); + + pTempdst++; + } + + for (iS = iH; iS < iM; iS++) /* second half */ + { + *pTempdst = *pTempsrc2; /* replicate gray from right */ + + pTempdst++; + + if (*(pTempsrc1+1) == *(pTempsrc2+1)) + *pTempdst = *(pTempsrc1+1);/* just repeat the first */ + else /* calculate the distance */ + *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+1)) - + (mng_int32)(*(pTempsrc1+1)) ) + iM) / + (iM * 2)) + (mng_int32)(*(pTempsrc1+1)) ); + + pTempdst++; + } + } + else + { + for (iS = 1; iS < iM; iS++) + { + *pTempdst = *pTempsrc1; /* repeat first source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + } + } + } + + pTempsrc1 += 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_X5, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_GRAY_SUPPORT */ +#endif /* MNG_OPTIMIZE_FOOTPRINT_MAGN */ + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgba8_x1 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX, iS, iM; + mng_uint8p pTempsrc1; + mng_uint8p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_X1, MNG_LC_START); +#endif + + pTempsrc1 = pSrcline; /* initialize pixel-loop */ + pTempdst = pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + *pTempdst = *(pTempsrc1+3); + pTempdst++; + + if (iX == 0) /* first interval ? */ + iM = iML; + else + if (iX == (iWidth - 1)) /* last interval ? */ + iM = iMR; + else + iM = iMX; + + for (iS = 1; iS < iM; iS++) /* fill interval */ + { + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + *pTempdst = *(pTempsrc1+3); + pTempdst++; + } + + pTempsrc1 += 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_X1, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgba8_x2 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_int32 iS, iM; + mng_uint8p pTempsrc1; + mng_uint8p pTempsrc2; + mng_uint8p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_X2, MNG_LC_START); +#endif + + pTempsrc1 = pSrcline; /* initialize pixel-loop */ + pTempdst = pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + pTempsrc2 = pTempsrc1 + 4; + + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + *pTempdst = *(pTempsrc1+3); + pTempdst++; + + if (iX == 0) /* first interval ? */ + { + if (iWidth == 1) /* single pixel ? */ + pTempsrc2 = MNG_NULL; + + iM = (mng_int32)iML; + } + else + if (iX == (iWidth - 2)) /* last interval ? */ + iM = (mng_int32)iMR; + else + iM = (mng_int32)iMX; + /* fill interval ? */ + if ((iX < iWidth - 1) || (iWidth == 1)) + { + if (pTempsrc2) /* do we have the second pixel ? */ + { + for (iS = 1; iS < iM; iS++) + { + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; /* just repeat the first */ + else /* calculate the distance */ + *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2)) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + + if (*(pTempsrc1+1) == *(pTempsrc2+1)) + *pTempdst = *(pTempsrc1+1); + else + *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+1)) - + (mng_int32)(*(pTempsrc1+1)) ) + iM) / + (iM * 2)) + (mng_int32)(*(pTempsrc1+1)) ); + + pTempdst++; + + if (*(pTempsrc1+2) == *(pTempsrc2+2)) + *pTempdst = *(pTempsrc1+2); + else + *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+2)) - + (mng_int32)(*(pTempsrc1+2)) ) + iM) / + (iM * 2)) + (mng_int32)(*(pTempsrc1+2)) ); + + pTempdst++; + + if (*(pTempsrc1+3) == *(pTempsrc2+3)) + *pTempdst = *(pTempsrc1+3); + else + *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+3)) - + (mng_int32)(*(pTempsrc1+3)) ) + iM) / + (iM * 2)) + (mng_int32)(*(pTempsrc1+3)) ); + + pTempdst++; + } + } + else + { + for (iS = 1; iS < iM; iS++) + { + *pTempdst = *pTempsrc1; /* repeat first source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + *pTempdst = *(pTempsrc1+3); + pTempdst++; + } + } + } + + pTempsrc1 += 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_X2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgba8_x3 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_int32 iS, iM, iH; + mng_uint8p pTempsrc1; + mng_uint8p pTempsrc2; + mng_uint8p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_X3, MNG_LC_START); +#endif + + pTempsrc1 = pSrcline; /* initialize pixel-loop */ + pTempdst = pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + pTempsrc2 = pTempsrc1 + 4; + + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + *pTempdst = *(pTempsrc1+3); + pTempdst++; + + if (iX == 0) /* first interval ? */ + { + if (iWidth == 1) /* single pixel ? */ + pTempsrc2 = MNG_NULL; + + iM = (mng_int32)iML; + } + else + if (iX == (iWidth - 2)) /* last interval ? */ + iM = (mng_int32)iMR; + else + iM = (mng_int32)iMX; + /* fill interval ? */ + if ((iX < iWidth - 1) || (iWidth == 1)) + { + if (pTempsrc2) /* do we have the second pixel ? */ + { + iH = (iM+1) / 2; /* calculate halfway point */ + + for (iS = 1; iS < iH; iS++) /* replicate first half */ + { + *pTempdst = *pTempsrc1; + *(pTempdst+1) = *(pTempsrc1+1); + *(pTempdst+2) = *(pTempsrc1+2); + *(pTempdst+3) = *(pTempsrc1+3); + + pTempdst += 4; + } + + for (iS = iH; iS < iM; iS++) /* replicate second half */ + { + *pTempdst = *pTempsrc2; + *(pTempdst+1) = *(pTempsrc2+1); + *(pTempdst+2) = *(pTempsrc2+2); + *(pTempdst+3) = *(pTempsrc2+3); + + pTempdst += 4; + } + } + else + { + for (iS = 1; iS < iM; iS++) + { + *pTempdst = *pTempsrc1; /* repeat first source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + *pTempdst = *(pTempsrc1+3); + pTempdst++; + } + } + } + + pTempsrc1 += 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_X3, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgba8_x4 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_int32 iS, iM, iH; + mng_uint8p pTempsrc1; + mng_uint8p pTempsrc2; + mng_uint8p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_X4, MNG_LC_START); +#endif + + pTempsrc1 = pSrcline; /* initialize pixel-loop */ + pTempdst = pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + pTempsrc2 = pTempsrc1 + 4; + + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + *pTempdst = *(pTempsrc1+3); + pTempdst++; + + if (iX == 0) /* first interval ? */ + { + if (iWidth == 1) /* single pixel ? */ + pTempsrc2 = MNG_NULL; + + iM = (mng_int32)iML; + } + else + if (iX == (iWidth - 2)) /* last interval ? */ + iM = (mng_int32)iMR; + else + iM = (mng_int32)iMX; + /* fill interval ? */ + if ((iX < iWidth - 1) || (iWidth == 1)) + { + if (pTempsrc2) /* do we have the second pixel ? */ + { + iH = (iM+1) / 2; /* calculate halfway point */ + + for (iS = 1; iS < iH; iS++) /* first half */ + { + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; /* just repeat the first */ + else /* calculate the distance */ + *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2)) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + + if (*(pTempsrc1+1) == *(pTempsrc2+1)) + *pTempdst = *(pTempsrc1+1); + else + *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+1)) - + (mng_int32)(*(pTempsrc1+1)) ) + iM) / + (iM * 2)) + (mng_int32)(*(pTempsrc1+1)) ); + + pTempdst++; + + if (*(pTempsrc1+2) == *(pTempsrc2+2)) + *pTempdst = *(pTempsrc1+2); + else + *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+2)) - + (mng_int32)(*(pTempsrc1+2)) ) + iM) / + (iM * 2)) + (mng_int32)(*(pTempsrc1+2)) ); + + pTempdst++; + /* replicate alpha from left */ + *pTempdst = *(pTempsrc1+3); + + pTempdst++; + } + + for (iS = iH; iS < iM; iS++) /* second half */ + { + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; /* just repeat the first */ + else /* calculate the distance */ + *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2)) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + + if (*(pTempsrc1+1) == *(pTempsrc2+1)) + *pTempdst = *(pTempsrc1+1); + else + *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+1)) - + (mng_int32)(*(pTempsrc1+1)) ) + iM) / + (iM * 2)) + (mng_int32)(*(pTempsrc1+1)) ); + + pTempdst++; + + if (*(pTempsrc1+2) == *(pTempsrc2+2)) + *pTempdst = *(pTempsrc1+2); + else + *pTempdst = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+2)) - + (mng_int32)(*(pTempsrc1+2)) ) + iM) / + (iM * 2)) + (mng_int32)(*(pTempsrc1+2)) ); + + pTempdst++; + /* replicate alpha from right */ + *pTempdst = *(pTempsrc2+3); + + pTempdst++; + } + } + else + { + for (iS = 1; iS < iM; iS++) + { + *pTempdst = *pTempsrc1; /* repeat first source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + *pTempdst = *(pTempsrc1+3); + pTempdst++; + } + } + } + + pTempsrc1 += 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_X4, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgba8_x5 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_int32 iS, iM, iH; + mng_uint8p pTempsrc1; + mng_uint8p pTempsrc2; + mng_uint8p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_X5, MNG_LC_START); +#endif + + pTempsrc1 = pSrcline; /* initialize pixel-loop */ + pTempdst = pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + pTempsrc2 = pTempsrc1 + 4; + + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + *pTempdst = *(pTempsrc1+3); + pTempdst++; + + if (iX == 0) /* first interval ? */ + { + if (iWidth == 1) /* single pixel ? */ + pTempsrc2 = MNG_NULL; + + iM = (mng_int32)iML; + } + else + if (iX == (iWidth - 2)) /* last interval ? */ + iM = (mng_int32)iMR; + else + iM = (mng_int32)iMX; + /* fill interval ? */ + if ((iX < iWidth - 1) || (iWidth == 1)) + { + if (pTempsrc2) /* do we have the second pixel ? */ + { + iH = (iM+1) / 2; /* calculate halfway point */ + + for (iS = 1; iS < iH; iS++) /* first half */ + { + *pTempdst = *pTempsrc1; /* replicate color from left */ + *(pTempdst+1) = *(pTempsrc1+1); + *(pTempdst+2) = *(pTempsrc1+2); + + if (*(pTempsrc1+3) == *(pTempsrc2+3)) + *(pTempdst+3) = *(pTempsrc1+3); + else + *(pTempdst+3) = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+3)) - + (mng_int32)(*(pTempsrc1+3)) ) + iM) / + (iM * 2)) + (mng_int32)(*(pTempsrc1+3)) ); + + pTempdst += 4; + } + + for (iS = iH; iS < iM; iS++) /* second half */ + { + *pTempdst = *pTempsrc2; /* replicate color from right */ + *(pTempdst+1) = *(pTempsrc2+1); + *(pTempdst+2) = *(pTempsrc2+2); + + if (*(pTempsrc1+3) == *(pTempsrc2+3)) + *(pTempdst+3) = *(pTempsrc1+3); + else + *(pTempdst+3) = (mng_uint8)(((2 * iS * ( (mng_int32)(*(pTempsrc2+3)) - + (mng_int32)(*(pTempsrc1+3)) ) + iM) / + (iM * 2)) + (mng_int32)(*(pTempsrc1+3)) ); + + pTempdst += 4; + } + } + else + { + for (iS = 1; iS < iM; iS++) + { + *pTempdst = *pTempsrc1; /* repeat first source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + *pTempdst = *(pTempsrc1+3); + pTempdst++; + } + } + } + + pTempsrc1 += 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_X4, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_FOOTPRINT_MAGN +#ifndef MNG_NO_GRAY_SUPPORT +mng_retcode mng_magnify_g8_y1 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_G8_Y1, MNG_LC_START); +#endif + + MNG_COPY (pDstline, pSrcline1, iWidth); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_G8_Y1, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ +mng_retcode mng_magnify_g8_y2 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_uint8p pTempsrc1; + mng_uint8p pTempsrc2; + mng_uint8p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_G8_Y2, MNG_LC_START); +#endif + + pTempsrc1 = pSrcline1; /* initialize pixel-loop */ + pTempsrc2 = pSrcline2; + pTempdst = pDstline; + + if (pTempsrc2) /* do we have a second line ? */ + { + for (iX = 0; iX < iWidth; iX++) + { /* calculate the distances */ + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2) ) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + } + } + else + { /* just repeat the entire line */ + MNG_COPY (pTempdst, pTempsrc1, iWidth); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_G8_Y2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_g8_y3 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_G8_Y3, MNG_LC_START); +#endif + + if (pSrcline2) /* do we have a second line ? */ + { + if (iS < (iM+1) / 2) /* top half ? */ + MNG_COPY (pDstline, pSrcline1, iWidth) + else + MNG_COPY (pDstline, pSrcline2, iWidth); + } + else + { /* just repeat the entire line */ + MNG_COPY (pDstline, pSrcline1, iWidth); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_G8_Y3, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_GRAY_SUPPORT */ + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgb8_y1 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB8_Y1, MNG_LC_START); +#endif + + MNG_COPY (pDstline, pSrcline1, iWidth * 3); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB8_Y1, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgb8_y2 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_uint8p pTempsrc1; + mng_uint8p pTempsrc2; + mng_uint8p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB8_Y2, MNG_LC_START); +#endif + + pTempsrc1 = pSrcline1; /* initialize pixel-loop */ + pTempsrc2 = pSrcline2; + pTempdst = pDstline; + + if (pTempsrc2) /* do we have a second line ? */ + { + for (iX = 0; iX < iWidth; iX++) + { /* calculate the distances */ + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2) ) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2) ) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2) ) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + } + } + else + { /* just repeat the entire line */ + MNG_COPY (pTempdst, pTempsrc1, iWidth * 3); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB8_Y2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgb8_y3 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB8_Y3, MNG_LC_START); +#endif + + if (pSrcline2) /* do we have a second line ? */ + { + if (iS < (iM+1) / 2) /* top half ? */ + MNG_COPY (pDstline, pSrcline1, iWidth * 3) + else + MNG_COPY (pDstline, pSrcline2, iWidth * 3); + } + else + { /* just repeat the entire line */ + MNG_COPY (pDstline, pSrcline1, iWidth * 3); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB8_Y3, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_GRAY_SUPPORT +mng_retcode mng_magnify_ga8_y1 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_Y1, MNG_LC_START); +#endif + + MNG_COPY (pDstline, pSrcline1, iWidth << 1); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_Y1, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_ga8_y2 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_uint8p pTempsrc1; + mng_uint8p pTempsrc2; + mng_uint8p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_Y2, MNG_LC_START); +#endif + + pTempsrc1 = pSrcline1; /* initialize pixel-loop */ + pTempsrc2 = pSrcline2; + pTempdst = pDstline; + + if (pTempsrc2) /* do we have a second line ? */ + { + for (iX = 0; iX < iWidth; iX++) + { /* calculate the distances */ + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2) ) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2) ) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + } + } + else + { /* just repeat the entire line */ + MNG_COPY (pTempdst, pTempsrc1, iWidth << 1); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_Y2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_ga8_y3 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_Y3, MNG_LC_START); +#endif + + if (pSrcline2) /* do we have a second line ? */ + { + if (iS < (iM+1) / 2) /* top half ? */ + MNG_COPY (pDstline, pSrcline1, iWidth << 1) + else + MNG_COPY (pDstline, pSrcline2, iWidth << 1); + } + else + { /* just repeat the entire line */ + MNG_COPY (pDstline, pSrcline1, iWidth << 1); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_Y2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_ga8_y4 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_uint8p pTempsrc1; + mng_uint8p pTempsrc2; + mng_uint8p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_Y4, MNG_LC_START); +#endif + + pTempsrc1 = pSrcline1; /* initialize pixel-loop */ + pTempsrc2 = pSrcline2; + pTempdst = pDstline; + + if (pTempsrc2) /* do we have a second line ? */ + { + if (iS < (iM+1) / 2) /* top half ? */ + { + for (iX = 0; iX < iWidth; iX++) + { /* calculate the distances */ + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2) ) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2 += 2; + + *pTempdst++ = *pTempsrc1++; /* replicate alpha from top */ + } + } + else + { + for (iX = 0; iX < iWidth; iX++) + { /* calculate the distances */ + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2) ) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + pTempsrc1 += 2; + pTempsrc2++; + + *pTempdst++ = *pTempsrc2++; /* replicate alpha from bottom */ + } + } + } + else + { /* just repeat the entire line */ + MNG_COPY (pTempdst, pTempsrc1, iWidth << 1); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_Y4, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_ga8_y5 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_uint8p pTempsrc1; + mng_uint8p pTempsrc2; + mng_uint8p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_Y5, MNG_LC_START); +#endif + + pTempsrc1 = pSrcline1; /* initialize pixel-loop */ + pTempsrc2 = pSrcline2; + pTempdst = pDstline; + + if (pTempsrc2) /* do we have a second line ? */ + { + if (iS < (iM+1) / 2) /* top half ? */ + { + for (iX = 0; iX < iWidth; iX++) + { + *pTempdst = *pTempsrc1; /* replicate gray from top */ + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + + if (*pTempsrc1 == *pTempsrc2) /* calculate the distances */ + *pTempdst = *pTempsrc1; + else + *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2) ) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + } + } + else + { + for (iX = 0; iX < iWidth; iX++) + { + *pTempdst = *pTempsrc2; /* replicate gray from bottom */ + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + + if (*pTempsrc1 == *pTempsrc2) /* calculate the distances */ + *pTempdst = *pTempsrc1; + else + *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2) ) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + } + } + } + else + { /* just repeat the entire line */ + MNG_COPY (pTempdst, pTempsrc1, iWidth << 1); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA8_Y5, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_GRAY_SUPPORT */ +#endif /* MNG_OPTIMIZE_FOOTPRINT_MAGN */ + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgba8_y1 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_Y1, MNG_LC_START); +#endif + + MNG_COPY (pDstline, pSrcline1, iWidth << 2); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_Y1, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgba8_y2 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_uint8p pTempsrc1; + mng_uint8p pTempsrc2; + mng_uint8p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_Y2, MNG_LC_START); +#endif + + pTempsrc1 = pSrcline1; /* initialize pixel-loop */ + pTempsrc2 = pSrcline2; + pTempdst = pDstline; + + if (pTempsrc2) /* do we have a second line ? */ + { + for (iX = 0; iX < iWidth; iX++) + { /* calculate the distances */ + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2) ) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2) ) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2) ) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2) ) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + } + } + else + { /* just repeat the entire line */ + MNG_COPY (pTempdst, pTempsrc1, iWidth << 2); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_Y2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgba8_y3 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_Y3, MNG_LC_START); +#endif + + if (pSrcline2) /* do we have a second line ? */ + { + if (iS < (iM+1) / 2) /* top half ? */ + MNG_COPY (pDstline, pSrcline1, iWidth << 2) + else + MNG_COPY (pDstline, pSrcline2, iWidth << 2); + } + else + { /* just repeat the entire line */ + MNG_COPY (pDstline, pSrcline1, iWidth << 2); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_Y2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgba8_y4 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_uint8p pTempsrc1; + mng_uint8p pTempsrc2; + mng_uint8p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_Y4, MNG_LC_START); +#endif + + pTempsrc1 = pSrcline1; /* initialize pixel-loop */ + pTempsrc2 = pSrcline2; + pTempdst = pDstline; + + if (pTempsrc2) /* do we have a second line ? */ + { + if (iS < (iM+1) / 2) /* top half ? */ + { + for (iX = 0; iX < iWidth; iX++) + { /* calculate the distances */ + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2) ) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2) ) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2) ) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2 += 2; + + *pTempdst++ = *pTempsrc1++; /* replicate alpha from top */ + } + } + else + { + for (iX = 0; iX < iWidth; iX++) + { /* calculate the distances */ + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2) ) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2) ) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2) ) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + pTempsrc1 += 2; + pTempsrc2++; + + *pTempdst++ = *pTempsrc2++; /* replicate alpha from bottom */ + } + } + } + else + { /* just repeat the entire line */ + MNG_COPY (pTempdst, pTempsrc1, iWidth << 2); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_Y4, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgba8_y5 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_uint8p pTempsrc1; + mng_uint8p pTempsrc2; + mng_uint8p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_Y5, MNG_LC_START); +#endif + + pTempsrc1 = pSrcline1; /* initialize pixel-loop */ + pTempsrc2 = pSrcline2; + pTempdst = pDstline; + + if (pTempsrc2) /* do we have a second line ? */ + { + if (iS < (iM+1) / 2) /* top half ? */ + { + for (iX = 0; iX < iWidth; iX++) + { + *pTempdst++ = *pTempsrc1++; /* replicate color from top */ + *pTempdst++ = *pTempsrc1++; + *pTempdst++ = *pTempsrc1++; + + pTempsrc2 += 3; + + if (*pTempsrc1 == *pTempsrc2) /* calculate the distances */ + *pTempdst = *pTempsrc1; + else + *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2) ) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + } + } + else + { + for (iX = 0; iX < iWidth; iX++) + { + *pTempdst++ = *pTempsrc2++; /* replicate color from bottom */ + *pTempdst++ = *pTempsrc2++; + *pTempdst++ = *pTempsrc2++; + + pTempsrc1 += 3; + + if (*pTempsrc1 == *pTempsrc2) /* calculate the distances */ + *pTempdst = *pTempsrc1; + else + *pTempdst = (mng_uint8)( ( (2 * iS * ( (mng_int32)(*pTempsrc2) - + (mng_int32)(*pTempsrc1) ) + iM) / + (iM * 2) ) + (mng_int32)(*pTempsrc1) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + } + } + } + else + { /* just repeat the entire line */ + MNG_COPY (pTempdst, pTempsrc1, iWidth << 2); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA8_Y5, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_FOOTPRINT_MAGN +#ifndef MNG_NO_16BIT_SUPPORT +#ifndef MNG_NO_GRAY_SUPPORT +mng_retcode mng_magnify_g16_x1 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX, iS, iM; + mng_uint16p pTempsrc1; + mng_uint16p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_G16_X1, MNG_LC_START); +#endif + + pTempsrc1 = (mng_uint16p)pSrcline; /* initialize pixel-loop */ + pTempdst = (mng_uint16p)pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + + if (iX == 0) /* first interval ? */ + iM = iML; + else + if (iX == (iWidth - 1)) /* last interval ? */ + iM = iMR; + else + iM = iMX; + + for (iS = 1; iS < iM; iS++) /* fill interval */ + { + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + } + + pTempsrc1++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_G16_X1, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_g16_x2 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_int32 iS, iM; + mng_uint16p pTempsrc1; + mng_uint16p pTempsrc2; + mng_uint16p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_G16_X2, MNG_LC_START); +#endif + + pTempsrc1 = (mng_uint16p)pSrcline; /* initialize pixel-loop */ + pTempdst = (mng_uint16p)pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + pTempsrc2 = pTempsrc1 + 1; + + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + + if (iX == 0) /* first interval ? */ + { + if (iWidth == 1) /* single pixel ? */ + pTempsrc2 = MNG_NULL; + + iM = iML; + } + else + if (iX == (iWidth - 2)) /* last interval ? */ + iM = iMR; + else + iM = iMX; + /* fill interval ? */ + if ((iX < iWidth - 1) || (iWidth == 1)) + { + if (pTempsrc2) /* do we have the second pixel ? */ + { /* is it same as first ? */ + if (*pTempsrc1 == *pTempsrc2) + { + for (iS = 1; iS < iM; iS++) /* then just repeat the first */ + { + *pTempdst = *pTempsrc1; + pTempdst++; + } + } + else + { + for (iS = 1; iS < iM; iS++) /* calculate the distances */ + { + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) )); + pTempdst++; + } + } + } + else + { + for (iS = 1; iS < iM; iS++) + { + *pTempdst = *pTempsrc1; /* repeat first source pixel */ + pTempdst++; + } + } + } + + pTempsrc1++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_G16_X2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_g16_x3 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_int32 iS, iM, iH; + mng_uint16p pTempsrc1; + mng_uint16p pTempsrc2; + mng_uint16p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_G16_X3, MNG_LC_START); +#endif + + pTempsrc1 = (mng_uint16p)pSrcline; /* initialize pixel-loop */ + pTempdst = (mng_uint16p)pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + pTempsrc2 = pTempsrc1 + 1; + + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + + if (iX == 0) /* first interval ? */ + { + if (iWidth == 1) /* single pixel ? */ + pTempsrc2 = MNG_NULL; + + iM = iML; + } + else + if (iX == (iWidth - 2)) /* last interval ? */ + iM = iMR; + else + iM = iMX; + /* fill interval ? */ + if ((iX < iWidth - 1) || (iWidth == 1)) + { + if (pTempsrc2) /* do we have the second pixel ? */ + { /* is it same as first ? */ + if (*pTempsrc1 == *pTempsrc2) + { + for (iS = 1; iS < iM; iS++) /* then just repeat the first */ + { + *pTempdst = *pTempsrc1; + pTempdst++; + } + } + else + { + iH = (iM+1) / 2; /* calculate halfway point */ + + for (iS = 1; iS < iH; iS++) /* replicate first half */ + { + *pTempdst = *pTempsrc1; + pTempdst++; + } + + for (iS = iH; iS < iM; iS++) /* replicate second half */ + { + *pTempdst = *pTempsrc2; + pTempdst++; + } + } + } + else + { + for (iS = 1; iS < iM; iS++) + { + *pTempdst = *pTempsrc1; /* repeat first source pixel */ + pTempdst++; + } + } + } + + pTempsrc1++; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_G16_X3, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_GRAY_SUPPORT */ + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgb16_x1 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX, iS, iM; + mng_uint16p pTempsrc1; + mng_uint16p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB16_X1, MNG_LC_START); +#endif + + pTempsrc1 = (mng_uint16p)pSrcline; /* initialize pixel-loop */ + pTempdst = (mng_uint16p)pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + + if (iX == 0) /* first interval ? */ + iM = iML; + else + if (iX == (iWidth - 1)) /* last interval ? */ + iM = iMR; + else + iM = iMX; + + for (iS = 1; iS < iM; iS++) /* fill interval */ + { + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + } + + pTempsrc1 += 3; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB16_X1, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgb16_x2 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_int32 iS, iM; + mng_uint16p pTempsrc1; + mng_uint16p pTempsrc2; + mng_uint16p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB16_X2, MNG_LC_START); +#endif + + pTempsrc1 = (mng_uint16p)pSrcline; /* initialize pixel-loop */ + pTempdst = (mng_uint16p)pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + pTempsrc2 = pTempsrc1 + 3; + + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + + if (iX == 0) /* first interval ? */ + { + if (iWidth == 1) /* single pixel ? */ + pTempsrc2 = MNG_NULL; + + iM = (mng_int32)iML; + } + else + if (iX == (iWidth - 2)) /* last interval ? */ + iM = (mng_int32)iMR; + else + iM = (mng_int32)iMX; + /* fill interval ? */ + if ((iX < iWidth - 1) || (iWidth == 1)) + { + if (pTempsrc2) /* do we have the second pixel ? */ + { + for (iS = 1; iS < iM; iS++) + { + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; /* just repeat the first */ + else /* calculate the distance */ + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + + if (*(pTempsrc1+1) == *(pTempsrc2+1)) + *pTempdst = *(pTempsrc1+1); + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+1))) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1))) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1))) ) ); + + pTempdst++; + + if (*(pTempsrc1+2) == *(pTempsrc2+2)) + *pTempdst = *(pTempsrc1+2); + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+2))) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+2))) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+2))) ) ); + + pTempdst++; + } + } + else + { + for (iS = 1; iS < iM; iS++) + { + *pTempdst = *pTempsrc1; /* repeat first source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + } + } + } + + pTempsrc1 += 3; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB16_X2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgb16_x3 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_int32 iS, iM, iH; + mng_uint16p pTempsrc1; + mng_uint16p pTempsrc2; + mng_uint16p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB16_X3, MNG_LC_START); +#endif + + pTempsrc1 = (mng_uint16p)pSrcline; /* initialize pixel-loop */ + pTempdst = (mng_uint16p)pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + pTempsrc2 = pTempsrc1 + 3; + + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + + if (iX == 0) /* first interval ? */ + { + if (iWidth == 1) /* single pixel ? */ + pTempsrc2 = MNG_NULL; + + iM = (mng_int32)iML; + } + else + if (iX == (iWidth - 2)) /* last interval ? */ + iM = (mng_int32)iMR; + else + iM = (mng_int32)iMX; + /* fill interval ? */ + if ((iX < iWidth - 1) || (iWidth == 1)) + { + if (pTempsrc2) /* do we have the second pixel ? */ + { + iH = (iM+1) / 2; /* calculate halfway point */ + + for (iS = 1; iS < iH; iS++) /* replicate first half */ + { + *pTempdst = *pTempsrc1; + *(pTempdst+1) = *(pTempsrc1+1); + *(pTempdst+2) = *(pTempsrc1+2); + + pTempdst += 3; + } + + for (iS = iH; iS < iM; iS++) /* replicate second half */ + { + *pTempdst = *pTempsrc2; + *(pTempdst+1) = *(pTempsrc2+1); + *(pTempdst+2) = *(pTempsrc2+2); + + pTempdst += 3; + } + } + else + { + for (iS = 1; iS < iM; iS++) + { + *pTempdst = *pTempsrc1; /* repeat first source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + } + } + } + + pTempsrc1 += 3; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB16_X3, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_GRAY_SUPPORT +mng_retcode mng_magnify_ga16_x1 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX, iS, iM; + mng_uint16p pTempsrc1; + mng_uint16p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_X1, MNG_LC_START); +#endif + + pTempsrc1 = (mng_uint16p) pSrcline; /* initialize pixel-loop */ + pTempdst = (mng_uint16p)pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + + if (iX == 0) /* first interval ? */ + iM = iML; + else + if (iX == (iWidth - 1)) /* last interval ? */ + iM = iMR; + else + iM = iMX; + + for (iS = 1; iS < iM; iS++) /* fill interval */ + { + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + } + + pTempsrc1 += 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_X1, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_ga16_x2 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_int32 iS, iM; + mng_uint16p pTempsrc1; + mng_uint16p pTempsrc2; + mng_uint16p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_X2, MNG_LC_START); +#endif + + pTempsrc1 = (mng_uint16p)pSrcline; /* initialize pixel-loop */ + pTempdst = (mng_uint16p)pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + pTempsrc2 = pTempsrc1 + 2; + + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + + if (iX == 0) /* first interval ? */ + { + if (iWidth == 1) /* single pixel ? */ + pTempsrc2 = MNG_NULL; + + iM = iML; + } + else + if (iX == (iWidth - 2)) /* last interval ? */ + iM = iMR; + else + iM = iMX; + /* fill interval ? */ + if ((iX < iWidth - 1) || (iWidth == 1)) + { + if (pTempsrc2) /* do we have the second pixel ? */ + { + for (iS = 1; iS < iM; iS++) + { + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; /* just repeat the first */ + else /* calculate the distance */ + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + + if (*(pTempsrc1+1) == *(pTempsrc2+1)) + *pTempdst = *(pTempsrc1+1); + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+1))) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1))) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1))) ) ); + + pTempdst++; + } + } + else + { + for (iS = 1; iS < iM; iS++) + { + *pTempdst = *pTempsrc1; /* repeat first source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + } + } + } + + pTempsrc1 += 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_X2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_ga16_x3 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_int32 iS, iM, iH; + mng_uint16p pTempsrc1; + mng_uint16p pTempsrc2; + mng_uint16p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_X3, MNG_LC_START); +#endif + + pTempsrc1 = (mng_uint16p)pSrcline; /* initialize pixel-loop */ + pTempdst = (mng_uint16p)pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + pTempsrc2 = pTempsrc1 + 2; + + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + + if (iX == 0) /* first interval ? */ + { + if (iWidth == 1) /* single pixel ? */ + pTempsrc2 = MNG_NULL; + + iM = iML; + } + else + if (iX == (iWidth - 2)) /* last interval ? */ + iM = iMR; + else + iM = iMX; + /* fill interval ? */ + if ((iX < iWidth - 1) || (iWidth == 1)) + { + if (pTempsrc2) /* do we have the second pixel ? */ + { + iH = (iM+1) / 2; /* calculate halfway point */ + + for (iS = 1; iS < iH; iS++) /* replicate first half */ + { + *pTempdst = *pTempsrc1; + *(pTempdst+1) = *(pTempsrc1+1); + + pTempdst += 2; + } + + for (iS = iH; iS < iM; iS++) /* replicate second half */ + { + *pTempdst = *pTempsrc2; + *(pTempdst+1) = *(pTempsrc2+1); + + pTempdst += 2; + } + } + else + { + for (iS = 1; iS < iM; iS++) + { + *pTempdst = *pTempsrc1; /* repeat first source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + } + } + } + + pTempsrc1 += 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_X3, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_ga16_x4 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_int32 iS, iM, iH; + mng_uint16p pTempsrc1; + mng_uint16p pTempsrc2; + mng_uint16p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_X4, MNG_LC_START); +#endif + + pTempsrc1 = (mng_uint16p)pSrcline; /* initialize pixel-loop */ + pTempdst = (mng_uint16p)pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + pTempsrc2 = pTempsrc1 + 2; + + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + + if (iX == 0) /* first interval ? */ + { + if (iWidth == 1) /* single pixel ? */ + pTempsrc2 = MNG_NULL; + + iM = iML; + } + else + if (iX == (iWidth - 2)) /* last interval ? */ + iM = iMR; + else + iM = iMX; + /* fill interval ? */ + if ((iX < iWidth - 1) || (iWidth == 1)) + { + if (pTempsrc2) /* do we have the second pixel ? */ + { + iH = (iM+1) / 2; /* calculate halfway point */ + + for (iS = 1; iS < iH; iS++) /* first half */ + { + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; /* just repeat the first */ + else /* calculate the distance */ + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + + *pTempdst = *(pTempsrc1+1); /* replicate alpha from left */ + + pTempdst++; + } + + for (iS = iH; iS < iM; iS++) /* second half */ + { + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; /* just repeat the first */ + else /* calculate the distance */ + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + + *pTempdst = *(pTempsrc2+1); /* replicate alpha from right */ + + pTempdst++; + } + } + else + { + for (iS = 1; iS < iM; iS++) + { + *pTempdst = *pTempsrc1; /* repeat first source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + } + } + } + + pTempsrc1 += 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_X4, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_ga16_x5 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_int32 iS, iM, iH; + mng_uint16p pTempsrc1; + mng_uint16p pTempsrc2; + mng_uint16p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_X5, MNG_LC_START); +#endif + + pTempsrc1 = (mng_uint16p)pSrcline; /* initialize pixel-loop */ + pTempdst = (mng_uint16p)pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + pTempsrc2 = pTempsrc1 + 2; + + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + + if (iX == 0) /* first interval ? */ + { + if (iWidth == 1) /* single pixel ? */ + pTempsrc2 = MNG_NULL; + + iM = iML; + } + else + if (iX == (iWidth - 2)) /* last interval ? */ + iM = iMR; + else + iM = iMX; + /* fill interval ? */ + if ((iX < iWidth - 1) || (iWidth == 1)) + { + if (pTempsrc2) /* do we have the second pixel ? */ + { + iH = (iM+1) / 2; /* calculate halfway point */ + + for (iS = 1; iS < iH; iS++) /* first half */ + { + *pTempdst = *pTempsrc1; /* replicate gray from left */ + + pTempdst++; + + if (*(pTempsrc1+1) == *(pTempsrc2+1)) + *pTempdst = *(pTempsrc1+1);/* just repeat the first */ + else /* calculate the distance */ + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+1))) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1))) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1))) ) ); + + pTempdst++; + } + + for (iS = iH; iS < iM; iS++) /* second half */ + { + *pTempdst = *pTempsrc2; /* replicate gray from right */ + + pTempdst++; + + if (*(pTempsrc1+1) == *(pTempsrc2+1)) + *pTempdst = *(pTempsrc1+1);/* just repeat the first */ + else /* calculate the distance */ + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+1))) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1))) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1))) ) ); + + pTempdst++; + } + } + else + { + for (iS = 1; iS < iM; iS++) + { + *pTempdst = *pTempsrc1; /* repeat first source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + } + } + } + + pTempsrc1 += 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_X5, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_GRAY_SUPPORT */ + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgba16_x1 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX, iS, iM; + mng_uint16p pTempsrc1; + mng_uint16p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_X1, MNG_LC_START); +#endif + + pTempsrc1 = (mng_uint16p)pSrcline; /* initialize pixel-loop */ + pTempdst = (mng_uint16p)pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + *pTempdst = *(pTempsrc1+3); + pTempdst++; + + if (iX == 0) /* first interval ? */ + iM = iML; + else + if (iX == (iWidth - 1)) /* last interval ? */ + iM = iMR; + else + iM = iMX; + + for (iS = 1; iS < iM; iS++) /* fill interval */ + { + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + *pTempdst = *(pTempsrc1+3); + pTempdst++; + } + + pTempsrc1 += 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_X1, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgba16_x2 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_int32 iS, iM; + mng_uint16p pTempsrc1; + mng_uint16p pTempsrc2; + mng_uint16p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_X2, MNG_LC_START); +#endif + + pTempsrc1 = (mng_uint16p)pSrcline; /* initialize pixel-loop */ + pTempdst = (mng_uint16p)pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + pTempsrc2 = pTempsrc1 + 4; + + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + *pTempdst = *(pTempsrc1+3); + pTempdst++; + + if (iX == 0) /* first interval ? */ + { + if (iWidth == 1) /* single pixel ? */ + pTempsrc2 = MNG_NULL; + + iM = (mng_int32)iML; + } + else + if (iX == (iWidth - 2)) /* last interval ? */ + iM = (mng_int32)iMR; + else + iM = (mng_int32)iMX; + /* fill interval ? */ + if ((iX < iWidth - 1) || (iWidth == 1)) + { + if (pTempsrc2) /* do we have the second pixel ? */ + { + for (iS = 1; iS < iM; iS++) + { + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; /* just repeat the first */ + else /* calculate the distance */ + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + + if (*(pTempsrc1+1) == *(pTempsrc2+1)) + *pTempdst = *(pTempsrc1+1); + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+1))) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1))) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1))) ) ); + + pTempdst++; + + if (*(pTempsrc1+2) == *(pTempsrc2+2)) + *pTempdst = *(pTempsrc1+2); + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+2))) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+2))) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+2))) ) ); + + pTempdst++; + + if (*(pTempsrc1+3) == *(pTempsrc2+3)) + *pTempdst = *(pTempsrc1+3); + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+3))) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+3))) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+3))) ) ); + + pTempdst++; + } + } + else + { + for (iS = 1; iS < iM; iS++) + { + *pTempdst = *pTempsrc1; /* repeat first source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + *pTempdst = *(pTempsrc1+3); + pTempdst++; + } + } + } + + pTempsrc1 += 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_X2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgba16_x3 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_int32 iS, iM, iH; + mng_uint16p pTempsrc1; + mng_uint16p pTempsrc2; + mng_uint16p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_X3, MNG_LC_START); +#endif + + pTempsrc1 = (mng_uint16p)pSrcline; /* initialize pixel-loop */ + pTempdst = (mng_uint16p)pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + pTempsrc2 = pTempsrc1 + 4; + + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + *pTempdst = *(pTempsrc1+3); + pTempdst++; + + if (iX == 0) /* first interval ? */ + { + if (iWidth == 1) /* single pixel ? */ + pTempsrc2 = MNG_NULL; + + iM = (mng_int32)iML; + } + else + if (iX == (iWidth - 2)) /* last interval ? */ + iM = (mng_int32)iMR; + else + iM = (mng_int32)iMX; + /* fill interval ? */ + if ((iX < iWidth - 1) || (iWidth == 1)) + { + if (pTempsrc2) /* do we have the second pixel ? */ + { + iH = (iM+1) / 2; /* calculate halfway point */ + + for (iS = 1; iS < iH; iS++) /* replicate first half */ + { + *pTempdst = *pTempsrc1; + *(pTempdst+1) = *(pTempsrc1+1); + *(pTempdst+2) = *(pTempsrc1+2); + *(pTempdst+3) = *(pTempsrc1+3); + + pTempdst += 4; + } + + for (iS = iH; iS < iM; iS++) /* replicate second half */ + { + *pTempdst = *pTempsrc2; + *(pTempdst+1) = *(pTempsrc2+1); + *(pTempdst+2) = *(pTempsrc2+2); + *(pTempdst+3) = *(pTempsrc2+3); + + pTempdst += 4; + } + } + else + { + for (iS = 1; iS < iM; iS++) + { + *pTempdst = *pTempsrc1; /* repeat first source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + *pTempdst = *(pTempsrc1+3); + pTempdst++; + } + } + } + + pTempsrc1 += 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_X3, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgba16_x4 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_int32 iS, iM, iH; + mng_uint16p pTempsrc1; + mng_uint16p pTempsrc2; + mng_uint16p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_X4, MNG_LC_START); +#endif + + pTempsrc1 = (mng_uint16p)pSrcline; /* initialize pixel-loop */ + pTempdst = (mng_uint16p)pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + pTempsrc2 = pTempsrc1 + 4; + + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + *pTempdst = *(pTempsrc1+3); + pTempdst++; + + if (iX == 0) /* first interval ? */ + { + if (iWidth == 1) /* single pixel ? */ + pTempsrc2 = MNG_NULL; + + iM = (mng_int32)iML; + } + else + if (iX == (iWidth - 2)) /* last interval ? */ + iM = (mng_int32)iMR; + else + iM = (mng_int32)iMX; + /* fill interval ? */ + if ((iX < iWidth - 1) || (iWidth == 1)) + { + if (pTempsrc2) /* do we have the second pixel ? */ + { + iH = (iM+1) / 2; /* calculate halfway point */ + + for (iS = 1; iS < iH; iS++) /* first half */ + { + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; /* just repeat the first */ + else /* calculate the distance */ + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + + if (*(pTempsrc1+1) == *(pTempsrc2+1)) + *pTempdst = *(pTempsrc1+1); + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+1))) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1))) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1))) ) ); + + pTempdst++; + + if (*(pTempsrc1+2) == *(pTempsrc2+2)) + *pTempdst = *(pTempsrc1+2); + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+2))) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+2))) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+2))) ) ); + + pTempdst++; + /* replicate alpha from left */ + *pTempdst = *(pTempsrc1+3); + + pTempdst++; + } + + for (iS = iH; iS < iM; iS++) /* second half */ + { + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; /* just repeat the first */ + else /* calculate the distance */ + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + + if (*(pTempsrc1+1) == *(pTempsrc2+1)) + *pTempdst = *(pTempsrc1+1); + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+1))) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1))) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+1))) ) ); + + pTempdst++; + + if (*(pTempsrc1+2) == *(pTempsrc2+2)) + *pTempdst = *(pTempsrc1+2); + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+2))) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+2))) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+2))) ) ); + + pTempdst++; + /* replicate alpha from right */ + *pTempdst = *(pTempsrc2+3); + + pTempdst++; + } + } + else + { + for (iS = 1; iS < iM; iS++) + { + *pTempdst = *pTempsrc1; /* repeat first source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + *pTempdst = *(pTempsrc1+3); + pTempdst++; + } + } + } + + pTempsrc1 += 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_X4, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgba16_x5 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_int32 iS, iM, iH; + mng_uint16p pTempsrc1; + mng_uint16p pTempsrc2; + mng_uint16p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_X5, MNG_LC_START); +#endif + + pTempsrc1 = (mng_uint16p)pSrcline; /* initialize pixel-loop */ + pTempdst = (mng_uint16p)pDstline; + + for (iX = 0; iX < iWidth; iX++) + { + pTempsrc2 = pTempsrc1 + 4; + + *pTempdst = *pTempsrc1; /* copy original source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + *pTempdst = *(pTempsrc1+3); + pTempdst++; + + if (iX == 0) /* first interval ? */ + { + if (iWidth == 1) /* single pixel ? */ + pTempsrc2 = MNG_NULL; + + iM = (mng_int32)iML; + } + else + if (iX == (iWidth - 2)) /* last interval ? */ + iM = (mng_int32)iMR; + else + iM = (mng_int32)iMX; + /* fill interval ? */ + if ((iX < iWidth - 1) || (iWidth == 1)) + { + if (pTempsrc2) /* do we have the second pixel ? */ + { + iH = (iM+1) / 2; /* calculate halfway point */ + + for (iS = 1; iS < iH; iS++) /* first half */ + { + *pTempdst = *pTempsrc1; /* replicate color from left */ + *(pTempdst+1) = *(pTempsrc1+1); + *(pTempdst+2) = *(pTempsrc1+2); + + if (*(pTempsrc1+3) == *(pTempsrc2+3)) + *(pTempdst+3) = *(pTempsrc1+3); + else + mng_put_uint16 ((mng_uint8p)(pTempdst+3), + (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+3))) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+3))) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+3))) ) ); + + pTempdst += 4; + } + + for (iS = iH; iS < iM; iS++) /* second half */ + { + *pTempdst = *pTempsrc2; /* replicate color from right */ + *(pTempdst+1) = *(pTempsrc2+1); + *(pTempdst+2) = *(pTempsrc2+2); + + if (*(pTempsrc1+3) == *(pTempsrc2+3)) + *(pTempdst+3) = *(pTempsrc1+3); + else + mng_put_uint16 ((mng_uint8p)(pTempdst+3), + (mng_uint16)(((2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc2+3))) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+3))) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)(pTempsrc1+3))) ) ); + + pTempdst += 4; + } + } + else + { + for (iS = 1; iS < iM; iS++) + { + *pTempdst = *pTempsrc1; /* repeat first source pixel */ + pTempdst++; + *pTempdst = *(pTempsrc1+1); + pTempdst++; + *pTempdst = *(pTempsrc1+2); + pTempdst++; + *pTempdst = *(pTempsrc1+3); + pTempdst++; + } + } + } + + pTempsrc1 += 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_X4, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_GRAY_SUPPORT +mng_retcode mng_magnify_g16_y1 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_G16_Y1, MNG_LC_START); +#endif + + MNG_COPY (pDstline, pSrcline1, (iWidth << 1)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_G16_Y1, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_g16_y2 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_uint16p pTempsrc1; + mng_uint16p pTempsrc2; + mng_uint16p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_G16_Y2, MNG_LC_START); +#endif + + pTempsrc1 = (mng_uint16p)pSrcline1; /* initialize pixel-loop */ + pTempsrc2 = (mng_uint16p)pSrcline2; + pTempdst = (mng_uint16p)pDstline; + + if (pTempsrc2) /* do we have a second line ? */ + { + for (iX = 0; iX < iWidth; iX++) + { /* calculate the distances */ + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + } + } + else + { /* just repeat the entire line */ + MNG_COPY (pTempdst, pTempsrc1, (iWidth << 1)); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_G16_Y2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_g16_y3 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_G16_Y3, MNG_LC_START); +#endif + + if (pSrcline2) /* do we have a second line ? */ + { + if (iS < (iM+1) / 2) /* top half ? */ + MNG_COPY (pDstline, pSrcline1, (iWidth << 1)) + else + MNG_COPY (pDstline, pSrcline2, (iWidth << 1)); + } + else + { /* just repeat the entire line */ + MNG_COPY (pDstline, pSrcline1, (iWidth << 1)); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_G16_Y3, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_GRAY_SUPPORT */ + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgb16_y1 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB16_Y1, MNG_LC_START); +#endif + + MNG_COPY (pDstline, pSrcline1, iWidth * 6); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB16_Y1, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgb16_y2 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_uint16p pTempsrc1; + mng_uint16p pTempsrc2; + mng_uint16p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB16_Y2, MNG_LC_START); +#endif + + pTempsrc1 = (mng_uint16p)pSrcline1; /* initialize pixel-loop */ + pTempsrc2 = (mng_uint16p)pSrcline2; + pTempdst = (mng_uint16p)pDstline; + + if (pTempsrc2) /* do we have a second line ? */ + { + for (iX = 0; iX < iWidth; iX++) + { /* calculate the distances */ + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + } + } + else + { /* just repeat the entire line */ + MNG_COPY (pTempdst, pTempsrc1, iWidth * 6); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB16_Y2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgb16_y3 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB16_Y3, MNG_LC_START); +#endif + + if (pSrcline2) /* do we have a second line ? */ + { + if (iS < (iM+1) / 2) /* top half ? */ + MNG_COPY (pDstline, pSrcline1, iWidth * 6) + else + MNG_COPY (pDstline, pSrcline2, iWidth * 6); + } + else + { /* just repeat the entire line */ + MNG_COPY (pDstline, pSrcline1, iWidth * 6); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGB16_Y3, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_GRAY_SUPPORT +mng_retcode mng_magnify_ga16_y1 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_Y1, MNG_LC_START); +#endif + + MNG_COPY (pDstline, pSrcline1, (iWidth << 2)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_Y1, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_ga16_y2 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_uint16p pTempsrc1; + mng_uint16p pTempsrc2; + mng_uint16p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_Y2, MNG_LC_START); +#endif + + pTempsrc1 = (mng_uint16p)pSrcline1; /* initialize pixel-loop */ + pTempsrc2 = (mng_uint16p)pSrcline2; + pTempdst = (mng_uint16p)pDstline; + + if (pTempsrc2) /* do we have a second line ? */ + { + for (iX = 0; iX < iWidth; iX++) + { /* calculate the distances */ + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + } + } + else + { /* just repeat the entire line */ + MNG_COPY (pTempdst, pTempsrc1, (iWidth << 2)); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_Y2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_ga16_y3 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_Y3, MNG_LC_START); +#endif + + if (pSrcline2) /* do we have a second line ? */ + { + if (iS < (iM+1) / 2) /* top half ? */ + MNG_COPY (pDstline, pSrcline1, (iWidth << 2)) + else + MNG_COPY (pDstline, pSrcline2, (iWidth << 2)); + } + else + { /* just repeat the entire line */ + MNG_COPY (pDstline, pSrcline1, (iWidth << 2)); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_Y2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_ga16_y4 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_uint16p pTempsrc1; + mng_uint16p pTempsrc2; + mng_uint16p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_Y4, MNG_LC_START); +#endif + + pTempsrc1 = (mng_uint16p)pSrcline1; /* initialize pixel-loop */ + pTempsrc2 = (mng_uint16p)pSrcline2; + pTempdst = (mng_uint16p)pDstline; + + if (pTempsrc2) /* do we have a second line ? */ + { + if (iS < (iM+1) / 2) /* top half ? */ + { + for (iX = 0; iX < iWidth; iX++) + { /* calculate the distances */ + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2 += 2; + + *pTempdst++ = *pTempsrc1++; /* replicate alpha from top */ + } + } + else + { + for (iX = 0; iX < iWidth; iX++) + { /* calculate the distances */ + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + pTempsrc1 += 2; + pTempsrc2++; + + *pTempdst++ = *pTempsrc2++; /* replicate alpha from bottom */ + } + } + } + else + { /* just repeat the entire line */ + MNG_COPY (pTempdst, pTempsrc1, (iWidth << 2)); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_Y4, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_ga16_y5 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_uint16p pTempsrc1; + mng_uint16p pTempsrc2; + mng_uint16p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_Y5, MNG_LC_START); +#endif + + pTempsrc1 = (mng_uint16p)pSrcline1; /* initialize pixel-loop */ + pTempsrc2 = (mng_uint16p)pSrcline2; + pTempdst = (mng_uint16p)pDstline; + + if (pTempsrc2) /* do we have a second line ? */ + { + if (iS < (iM+1) / 2) /* top half ? */ + { + for (iX = 0; iX < iWidth; iX++) + { + *pTempdst = *pTempsrc1; /* replicate gray from top */ + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + + if (*pTempsrc1 == *pTempsrc2) /* calculate the distances */ + *pTempdst = *pTempsrc1; + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + } + } + else + { + for (iX = 0; iX < iWidth; iX++) + { + *pTempdst = *pTempsrc2; /* replicate gray from bottom */ + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + + if (*pTempsrc1 == *pTempsrc2) /* calculate the distances */ + *pTempdst = *pTempsrc1; + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + } + } + } + else + { /* just repeat the entire line */ + MNG_COPY (pTempdst, pTempsrc1, (iWidth << 2)); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_GA16_Y5, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_GRAY_SUPPORT */ + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgba16_y1 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_Y1, MNG_LC_START); +#endif + + MNG_COPY (pDstline, pSrcline1, (iWidth << 3)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_Y1, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgba16_y2 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_uint16p pTempsrc1; + mng_uint16p pTempsrc2; + mng_uint16p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_Y2, MNG_LC_START); +#endif + + pTempsrc1 = (mng_uint16p)pSrcline1; /* initialize pixel-loop */ + pTempsrc2 = (mng_uint16p)pSrcline2; + pTempdst = (mng_uint16p)pDstline; + + if (pTempsrc2) /* do we have a second line ? */ + { + for (iX = 0; iX < iWidth; iX++) + { /* calculate the distances */ + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + } + } + else + { /* just repeat the entire line */ + MNG_COPY (pTempdst, pTempsrc1, (iWidth << 3)); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_Y2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgba16_y3 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_Y3, MNG_LC_START); +#endif + + if (pSrcline2) /* do we have a second line ? */ + { + if (iS < (iM+1) / 2) /* top half ? */ + MNG_COPY (pDstline, pSrcline1, (iWidth << 3)) + else + MNG_COPY (pDstline, pSrcline2, (iWidth << 3)); + } + else + { /* just repeat the entire line */ + MNG_COPY (pDstline, pSrcline1, (iWidth << 3)); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_Y2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgba16_y4 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_uint16p pTempsrc1; + mng_uint16p pTempsrc2; + mng_uint16p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_Y4, MNG_LC_START); +#endif + + pTempsrc1 = (mng_uint16p)pSrcline1; /* initialize pixel-loop */ + pTempsrc2 = (mng_uint16p)pSrcline2; + pTempdst = (mng_uint16p)pDstline; + + if (pTempsrc2) /* do we have a second line ? */ + { + if (iS < (iM+1) / 2) /* top half ? */ + { + for (iX = 0; iX < iWidth; iX++) + { /* calculate the distances */ + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2 += 2; + + *pTempdst++ = *pTempsrc1++; /* replicate alpha from top */ + } + } + else + { + for (iX = 0; iX < iWidth; iX++) + { /* calculate the distances */ + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + + if (*pTempsrc1 == *pTempsrc2) + *pTempdst = *pTempsrc1; + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + pTempsrc1 += 2; + pTempsrc2++; + + *pTempdst++ = *pTempsrc2++; /* replicate alpha from bottom */ + } + } + } + else + { /* just repeat the entire line */ + MNG_COPY (pTempdst, pTempsrc1, (iWidth << 3)); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_Y4, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_magnify_rgba16_y5 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline) +{ + mng_uint32 iX; + mng_uint16p pTempsrc1; + mng_uint16p pTempsrc2; + mng_uint16p pTempdst; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_Y5, MNG_LC_START); +#endif + + pTempsrc1 = (mng_uint16p)pSrcline1; /* initialize pixel-loop */ + pTempsrc2 = (mng_uint16p)pSrcline2; + pTempdst = (mng_uint16p)pDstline; + + if (pTempsrc2) /* do we have a second line ? */ + { + if (iS < (iM+1) / 2) /* top half ? */ + { + for (iX = 0; iX < iWidth; iX++) + { + *pTempdst++ = *pTempsrc1++; /* replicate color from top */ + *pTempdst++ = *pTempsrc1++; + *pTempdst++ = *pTempsrc1++; + + pTempsrc2 += 3; + + if (*pTempsrc1 == *pTempsrc2) /* calculate the distances */ + *pTempdst = *pTempsrc1; + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + } + } + else + { + for (iX = 0; iX < iWidth; iX++) + { + *pTempdst++ = *pTempsrc2++; /* replicate color from bottom */ + *pTempdst++ = *pTempsrc2++; + *pTempdst++ = *pTempsrc2++; + + pTempsrc1 += 3; + + if (*pTempsrc1 == *pTempsrc2) /* calculate the distances */ + *pTempdst = *pTempsrc1; + else + mng_put_uint16 ((mng_uint8p)pTempdst, + (mng_uint16)( ( (2 * iS * ( (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc2)) - + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) + iM) / + (iM * 2)) + (mng_int32)(mng_get_uint16 ((mng_uint8p)pTempsrc1)) ) ); + + pTempdst++; + pTempsrc1++; + pTempsrc2++; + } + } + } + else + { /* just repeat the entire line */ + MNG_COPY (pTempdst, pTempsrc1, (iWidth << 3)); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_MAGNIFY_RGBA16_Y5, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_NO_16BIT_SUPPORT */ +#endif /* MNG_OPTIMIZE_FOOTPRINT_MAGN */ +#endif /* MNG_SKIPCHUNK_MAGN */ + +/* ************************************************************************** */ +/* * * */ +/* * PAST composition routines - compose over/under with a target object * */ +/* * * */ +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_PAST +mng_retcode mng_composeover_rgba8 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + mng_uint8 iFGa8, iBGa8; + mng_uint8 iCr8, iCg8, iCb8, iCa8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_COMPOSEOVER_RGBA8, MNG_LC_START); +#endif + + pWorkrow = pData->pRGBArow; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + iFGa8 = *(pWorkrow+3); /* get alpha values */ + iBGa8 = *(pOutrow+3); + + if (iFGa8) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa8 == 0xFF) || (iBGa8 == 0)) + { /* then simply copy the values */ + *pOutrow = *pWorkrow; + *(pOutrow+1) = *(pWorkrow+1); + *(pOutrow+2) = *(pWorkrow+2); + *(pOutrow+3) = iFGa8; + } + else + { + if (iBGa8 == 0xFF) /* background fully opaque ? */ + { /* do alpha composing */ +#ifdef MNG_OPTIMIZE_FOOTPRINT_COMPOSE + int i; + for (i=2; i >= 0; i--) + { + MNG_COMPOSE8 (*(pOutrow+i), *(pWorkrow+i), iFGa8, *(pOutrow+i)); + } +#else + MNG_COMPOSE8 (*pOutrow, *pWorkrow, iFGa8, *pOutrow ); + MNG_COMPOSE8 (*(pOutrow+1), *(pWorkrow+1), iFGa8, *(pOutrow+1)); + MNG_COMPOSE8 (*(pOutrow+2), *(pWorkrow+2), iFGa8, *(pOutrow+2)); +#endif + /* alpha remains fully opaque !!! */ + } + else + { /* here we'll have to blend */ + MNG_BLEND8 (*pWorkrow, *(pWorkrow+1), *(pWorkrow+2), iFGa8, + *pOutrow, *(pOutrow+1), *(pOutrow+2), iBGa8, + iCr8, iCg8, iCb8, iCa8); + /* and return the composed values */ + *pOutrow = iCr8; + *(pOutrow+1) = iCg8; + *(pOutrow+2) = iCb8; + *(pOutrow+3) = iCa8; + } + } + } + + pOutrow += 4; + pWorkrow += 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_COMPOSEOVER_RGBA8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_composeover_rgba16 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf; + mng_uint16p pWorkrow; + mng_uint16p pOutrow; + mng_int32 iX; + mng_uint16 iFGa16, iFGr16, iFGg16, iFGb16; + mng_uint16 iBGa16, iBGr16, iBGg16, iBGb16; + mng_uint16 iCr16, iCg16, iCb16, iCa16; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_COMPOSEOVER_RGBA16, MNG_LC_START); +#endif + + pWorkrow = (mng_uint16p)pData->pRGBArow; + pOutrow = (mng_uint16p)(pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize)); + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { /* get alpha values */ + iFGa16 = mng_get_uint16 ((mng_uint8p)(pWorkrow+3)); + iBGa16 = mng_get_uint16 ((mng_uint8p)(pOutrow+3)); + + if (iFGa16) /* any opacity at all ? */ + { /* fully opaque or background fully transparent ? */ + if ((iFGa16 == 0xFFFF) || (iBGa16 == 0)) + { /* then simply copy the values */ + *pOutrow = *pWorkrow; + *(pOutrow+1) = *(pWorkrow+1); + *(pOutrow+2) = *(pWorkrow+2); + *(pOutrow+3) = *(pWorkrow+3); + } + else + { /* get color values */ + iFGr16 = mng_get_uint16 ((mng_uint8p)pWorkrow); + iFGg16 = mng_get_uint16 ((mng_uint8p)(pWorkrow+1)); + iFGb16 = mng_get_uint16 ((mng_uint8p)(pWorkrow+2)); + iBGr16 = mng_get_uint16 ((mng_uint8p)pOutrow); + iBGg16 = mng_get_uint16 ((mng_uint8p)(pOutrow+1)); + iBGb16 = mng_get_uint16 ((mng_uint8p)(pOutrow+2)); + + if (iBGa16 == 0xFFFF) /* background fully opaque ? */ + { /* do alpha composing */ + MNG_COMPOSE16 (iFGr16, iFGr16, iFGa16, iBGr16); + MNG_COMPOSE16 (iFGg16, iFGg16, iFGa16, iBGg16); + MNG_COMPOSE16 (iFGb16, iFGb16, iFGa16, iBGb16); + + mng_put_uint16 ((mng_uint8p)pOutrow, iFGr16); + mng_put_uint16 ((mng_uint8p)(pOutrow+1), iFGg16); + mng_put_uint16 ((mng_uint8p)(pOutrow+2), iFGb16); + /* alpha remains fully opaque !!! */ + } + else + { /* here we'll have to blend */ + MNG_BLEND16 (iFGr16, iFGg16, iFGb16, iFGa16, + iBGr16, iBGg16, iBGb16, iBGa16, + iCr16, iCg16, iCb16, iCa16); + /* and return the composed values */ + mng_put_uint16 ((mng_uint8p)pOutrow, iCr16); + mng_put_uint16 ((mng_uint8p)(pOutrow+1), iCg16); + mng_put_uint16 ((mng_uint8p)(pOutrow+2), iCb16); + mng_put_uint16 ((mng_uint8p)(pOutrow+3), iCa16); + } + } + } + + pOutrow += 4; + pWorkrow += 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_COMPOSEOVER_RGBA16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_composeunder_rgba8 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf; + mng_uint8p pWorkrow; + mng_uint8p pOutrow; + mng_int32 iX; + mng_uint8 iFGa8, iBGa8; + mng_uint8 iCr8, iCg8, iCb8, iCa8; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_COMPOSEUNDER_RGBA8, MNG_LC_START); +#endif + + pWorkrow = pData->pRGBArow; + pOutrow = pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize); + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { + iFGa8 = *(pOutrow+3); /* get alpha values */ + iBGa8 = *(pWorkrow+3); + /* anything to do at all ? */ + if ((iBGa8) && (iFGa8 != 0xFF)) + { + if (iBGa8 == 0xFF) /* background fully opaque ? */ + { /* do alpha composing */ +#ifdef MNG_OPTIMIZE_FOOTPRINT_COMPOSE + int i; + for (i=2; i >= 0; i--) + { + MNG_COMPOSE8 (*(pOutrow+i), *(pOutrow+i), iFGa8, *(pWorkrow+i)); + } +#else + MNG_COMPOSE8 (*pOutrow, *pOutrow, iFGa8, *pWorkrow ); + MNG_COMPOSE8 (*(pOutrow+1), *(pOutrow+1), iFGa8, *(pWorkrow+1)); + MNG_COMPOSE8 (*(pOutrow+2), *(pOutrow+2), iFGa8, *(pWorkrow+2)); +#endif + *(pOutrow+3) = 0xFF; /* alpha becomes fully opaque !!! */ + } + else + { /* here we'll have to blend */ + MNG_BLEND8 (*pOutrow, *(pOutrow+1), *(pOutrow+2), iFGa8, + *pWorkrow, *(pWorkrow+1), *(pWorkrow+2), iBGa8, + iCr8, iCg8, iCb8, iCa8); + /* and return the composed values */ + *pOutrow = iCr8; + *(pOutrow+1) = iCg8; + *(pOutrow+2) = iCb8; + *(pOutrow+3) = iCa8; + } + } + + pOutrow += 4; + pWorkrow += 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_COMPOSEUNDER_RGBA8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_composeunder_rgba16 (mng_datap pData) +{ + mng_imagedatap pBuf = ((mng_imagep)pData->pStoreobj)->pImgbuf; + mng_uint16p pWorkrow; + mng_uint16p pOutrow; + mng_int32 iX; + mng_uint16 iFGa16, iFGr16, iFGg16, iFGb16; + mng_uint16 iBGa16, iBGr16, iBGg16, iBGb16; + mng_uint16 iCr16, iCg16, iCb16, iCa16; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_COMPOSEUNDER_RGBA16, MNG_LC_START); +#endif + + pWorkrow = (mng_uint16p)pData->pRGBArow; + pOutrow = (mng_uint16p)(pBuf->pImgdata + (pData->iRow * pBuf->iRowsize ) + + (pData->iCol * pBuf->iSamplesize)); + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { /* get alpha values */ + iFGa16 = mng_get_uint16 ((mng_uint8p)(pOutrow+3)); + iBGa16 = mng_get_uint16 ((mng_uint8p)(pWorkrow+3)); + /* anything to do at all ? */ + if ((iBGa16) && (iFGa16 != 0xFFFF)) + { + iFGr16 = mng_get_uint16 ((mng_uint8p)pOutrow); + iFGg16 = mng_get_uint16 ((mng_uint8p)(pOutrow+1)); + iFGb16 = mng_get_uint16 ((mng_uint8p)(pOutrow+2)); + iBGr16 = mng_get_uint16 ((mng_uint8p)pWorkrow); + iBGg16 = mng_get_uint16 ((mng_uint8p)(pWorkrow+1)); + iBGb16 = mng_get_uint16 ((mng_uint8p)(pWorkrow+2)); + + if (iBGa16 == 0xFFFF) /* background fully opaque ? */ + { /* do alpha composing */ + MNG_COMPOSE16 (iFGr16, iFGr16, iFGa16, iBGr16); + MNG_COMPOSE16 (iFGg16, iFGg16, iFGa16, iBGg16); + MNG_COMPOSE16 (iFGb16, iFGb16, iFGa16, iBGb16); + + mng_put_uint16 ((mng_uint8p)pOutrow, iFGr16); + mng_put_uint16 ((mng_uint8p)(pOutrow+1), iFGg16); + mng_put_uint16 ((mng_uint8p)(pOutrow+2), iFGb16); + *(pOutrow+3) = 0xFFFF; /* alpha becomes fully opaque !!! */ + } + else + { /* here we'll have to blend */ + MNG_BLEND16 (iFGr16, iFGg16, iFGb16, iFGa16, + iBGr16, iBGg16, iBGb16, iBGa16, + iCr16, iCg16, iCb16, iCa16); + /* and return the composed values */ + mng_put_uint16 ((mng_uint8p)pOutrow, iCr16); + mng_put_uint16 ((mng_uint8p)(pOutrow+1), iCg16); + mng_put_uint16 ((mng_uint8p)(pOutrow+2), iCb16); + mng_put_uint16 ((mng_uint8p)(pOutrow+3), iCa16); + } + } + + pOutrow += 4; + pWorkrow += 4; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_COMPOSEUNDER_RGBA16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif + +/* ************************************************************************** */ +/* * * */ +/* * PAST flip & tile routines - flip or tile a row of pixels * */ +/* * * */ +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_PAST +mng_retcode mng_flip_rgba8 (mng_datap pData) +{ + mng_uint32p pWorkrow; + mng_uint32p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FLIP_RGBA8, MNG_LC_START); +#endif + /* setup temp pointers */ + pWorkrow = (mng_uint32p)pData->pRGBArow + pData->iRowsamples - 1; + pOutrow = (mng_uint32p)pData->pWorkrow; + /* swap original buffers */ + pData->pWorkrow = pData->pRGBArow; + pData->pRGBArow = (mng_uint8p)pOutrow; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { /* let's flip */ + *pOutrow = *pWorkrow; + pOutrow++; + pWorkrow--; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FLIP_RGBA8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_flip_rgba16 (mng_datap pData) +{ + mng_uint32p pWorkrow; + mng_uint32p pOutrow; + mng_int32 iX; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FLIP_RGBA16, MNG_LC_START); +#endif + /* setup temp pointers */ + pWorkrow = (mng_uint32p)pData->pRGBArow + ((pData->iRowsamples - 1) << 1); + pOutrow = (mng_uint32p)pData->pWorkrow; + /* swap original buffers */ + pData->pWorkrow = pData->pRGBArow; + pData->pRGBArow = (mng_uint8p)pOutrow; + +#ifdef MNG_DECREMENT_LOOPS + for (iX = pData->iRowsamples; iX > 0; iX--) +#else + for (iX = 0; iX < pData->iRowsamples; iX++) +#endif + { /* let's flip */ + *pOutrow = *pWorkrow; + *(pOutrow + 1) = *(pWorkrow + 1); + + pOutrow += 2; + pWorkrow -= 2; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_FLIP_RGBA16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode mng_tile_rgba8 (mng_datap pData) +{ + mng_uint32p pWorkrow; + mng_uint32p pOutrow; + mng_int32 iX; + mng_uint32 iZ, iMax; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_TILE_RGBA8, MNG_LC_START); +#endif + + iZ = pData->iSourcel; /* indent ? */ + /* what's our source-length */ + iMax = ((mng_imagep)pData->pRetrieveobj)->pImgbuf->iWidth; + /* setup temp pointers */ + pWorkrow = (mng_uint32p)pData->pRGBArow + iZ; + pOutrow = (mng_uint32p)pData->pWorkrow; + /* swap original buffers */ + pData->pWorkrow = pData->pRGBArow; + pData->pRGBArow = (mng_uint8p)pOutrow; + + for (iX = pData->iDestl; iX < pData->iDestr; iX++) + { /* tiiiile */ + *pOutrow = *pWorkrow; + + pWorkrow++; + pOutrow++; + iZ++; + + if (iZ >= iMax) /* end of source ? */ + { + iZ = 0; + pWorkrow = (mng_uint32p)pData->pWorkrow; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_TILE_RGBA8, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_tile_rgba16 (mng_datap pData) +{ + mng_uint32p pWorkrow; + mng_uint32p pOutrow; + mng_int32 iX; + mng_uint32 iZ, iMax; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_TILE_RGBA16, MNG_LC_START); +#endif + + iZ = pData->iSourcel; /* indent ? */ + /* what's our source-length */ + iMax = ((mng_imagep)pData->pRetrieveobj)->pImgbuf->iWidth; + /* setup temp pointers */ + pWorkrow = (mng_uint32p)pData->pRGBArow + (iZ << 1); + pOutrow = (mng_uint32p)pData->pWorkrow; + /* swap original buffers */ + pData->pWorkrow = pData->pRGBArow; + pData->pRGBArow = (mng_uint8p)pOutrow; + + for (iX = pData->iDestl; iX < pData->iDestr; iX++) + { /* tiiiile */ + *pOutrow = *pWorkrow; + *(pOutrow + 1) = *(pWorkrow + 1); + + pWorkrow += 2; + pOutrow += 2; + iZ++; + + if (iZ >= iMax) /* end of source ? */ + { + iZ = 0; + pWorkrow = (mng_uint32p)pData->pWorkrow; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_TILE_RGBA16, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif /* MNG_SKIPCHUNK_PAST */ + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_DISPLAY_PROCS */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ diff --git a/Engine/lib/lmng/libmng_pixels.h b/Engine/lib/lmng/libmng_pixels.h new file mode 100644 index 000000000..5a0281e89 --- /dev/null +++ b/Engine/lib/lmng/libmng_pixels.h @@ -0,0 +1,1147 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_pixels.h copyright (c) 2000-2006 G.Juyn * */ +/* * version : 1.0.10 * */ +/* * * */ +/* * purpose : Pixel-row management routines (definition) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : Definition of the pixel-row management routines * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed strict-ANSI stuff * */ +/* * * */ +/* * 0.5.2 - 05/22/2000 - G.Juyn * */ +/* * - added some JNG definitions * */ +/* * - added delta-image row-processing routines * */ +/* * 0.5.2 - 06/05/2000 - G.Juyn * */ +/* * - added support for RGB8_A8 canvasstyle * */ +/* * * */ +/* * 0.5.3 - 06/16/2000 - G.Juyn * */ +/* * - changed progressive-display processing * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 0.9.3 - 08/26/2000 - G.Juyn * */ +/* * - added MAGN support * */ +/* * 0.9.3 - 09/07/2000 - G.Juyn * */ +/* * - added support for new filter_types * */ +/* * 0.9.3 - 10/16/2000 - G.Juyn * */ +/* * - added optional support for bKGD for PNG images * */ +/* * - added support for JDAA * */ +/* * 0.9.3 - 10/19/2000 - G.Juyn * */ +/* * - implemented delayed delta-processing * */ +/* * * */ +/* * 0.9.4 - 1/18/2001 - G.Juyn * */ +/* * - added "new" MAGN methods 3, 4 & 5 * */ +/* * * */ +/* * 1.0.1 - 04/21/2001 - G.Juyn (code by G.Kelly) * */ +/* * - added BGRA8 canvas with premultiplied alpha * */ +/* * * */ +/* * 1.0.5 - 08/15/2002 - G.Juyn * */ +/* * - completed PROM support * */ +/* * - completed delta-image support * */ +/* * 1.0.5 - 08/16/2002 - G.Juyn * */ +/* * - completed MAGN support (16-bit functions) * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * 1.0.5 - 09/22/2002 - G.Juyn * */ +/* * - added bgrx8 canvas (filler byte) * */ +/* * 1.0.5 - 09/23/2002 - G.Juyn * */ +/* * - added compose over/under routines for PAST processing * */ +/* * - added flip & tile routines for PAST processing * */ +/* * * */ +/* * 1.0.6 - 03/09/2003 - G.Juyn * */ +/* * - hiding 12-bit JPEG stuff * */ +/* * 1.0.6 - 05/11/2003 - G. Juyn * */ +/* * - added conditionals around canvas update routines * */ +/* * 1.0.6 - 06/09/2003 - G. R-P * */ +/* * - added conditionals around 8-bit magn routines * */ +/* * 1.0.6 - 07/07/2003 - G. R-P * */ +/* * - removed conditionals around 8-bit magn routines * */ +/* * - added conditionals around 16-bit and delta-PNG * */ +/* * supporting code * */ +/* * 1.0.6 - 07/29/2003 - G.R-P * */ +/* * - added SKIPCHUNK conditionals around PAST chunk support * */ +/* * 1.0.6 - 08/18/2003 - G.R-P * */ +/* * - added conditionals around 1, 2, and 4-bit prototypes * */ +/* * * */ +/* * 1.0.7 - 11/27/2003 - R.A * */ +/* * - added CANVAS_RGB565 and CANVAS_BGR565 * */ +/* * 1.0.7 - 12/06/2003 - R.A * */ +/* * - added CANVAS_RGBA565 and CANVAS_BGRA565 * */ +/* * 1.0.7 - 01/25/2004 - J.S * */ +/* * - added premultiplied alpha canvas' for RGBA, ARGB, ABGR * */ +/* * * */ +/* * 1.0.9 - 10/10/2004 - G.R-P. * */ +/* * - added MNG_NO_1_2_4BIT_SUPPORT * */ +/* * 1.0.9 - 10/14/2004 - G.Juyn * */ +/* * - added bgr565_a8 canvas-style (thanks to J. Elvander) * */ +/* * * */ +/* * 1.0.10 - 03/07/2006 - (thanks to W. Manthey) * */ +/* * - added CANVAS_RGB555 and CANVAS_BGR555 * */ +/* * * */ +/* ************************************************************************** */ + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +#ifndef _libmng_pixels_h_ +#define _libmng_pixels_h_ + +/* ************************************************************************** */ +/* * * */ +/* * Progressive display check - checks to see if progressive display is * */ +/* * in order & indicates so * */ +/* * * */ +/* * The routine is called after a call to one of the display_xxx routines * */ +/* * if appropriate * */ +/* * * */ +/* * The refresh is warrented in the read_chunk routine (mng_read.c) * */ +/* * and only during read&display processing, since there's not much point * */ +/* * doing it from memory! * */ +/* * * */ +/* ************************************************************************** */ + +mng_retcode mng_display_progressive_check (mng_datap pData); + +/* ************************************************************************** */ +/* * * */ +/* * Display routines - convert rowdata (which is already color-corrected) * */ +/* * to the output canvas, respecting any transparency information * */ +/* * * */ +/* ************************************************************************** */ + +#ifndef MNG_SKIPCANVAS_RGB8 +mng_retcode mng_display_rgb8 (mng_datap pData); +#endif +#ifndef MNG_SKIPCANVAS_RGBA8 +mng_retcode mng_display_rgba8 (mng_datap pData); +#endif +#ifndef MNG_SKIPCANVAS_RGBA8_PM +mng_retcode mng_display_rgba8_pm (mng_datap pData); +#endif +#ifndef MNG_SKIPCANVAS_ARGB8 +mng_retcode mng_display_argb8 (mng_datap pData); +#endif +#ifndef MNG_SKIPCANVAS_ARGB8_PM +mng_retcode mng_display_argb8_pm (mng_datap pData); +#endif +#ifndef MNG_SKIPCANVAS_RGB8_A8 +mng_retcode mng_display_rgb8_a8 (mng_datap pData); +#endif +#ifndef MNG_SKIPCANVAS_BGR8 +mng_retcode mng_display_bgr8 (mng_datap pData); +#endif +#ifndef MNG_SKIPCANVAS_BGRX8 +mng_retcode mng_display_bgrx8 (mng_datap pData); +#endif +#ifndef MNG_SKIPCANVAS_BGRA8 +mng_retcode mng_display_bgra8 (mng_datap pData); +#endif +#ifndef MNG_SKIPCANVAS_BGRA8_PM +mng_retcode mng_display_bgra8_pm (mng_datap pData); +#endif +#ifndef MNG_SKIPCANVAS_ABGR8 +mng_retcode mng_display_abgr8 (mng_datap pData); +#endif +#ifndef MNG_SKIPCANVAS_ABGR8_PM +mng_retcode mng_display_abgr8_pm (mng_datap pData); +#endif +#ifndef MNG_SKIPCANVAS_RGB565 +mng_retcode mng_display_rgb565 (mng_datap pData); +#endif +#ifndef MNG_SKIPCANVAS_RGBA565 +mng_retcode mng_display_rgba565 (mng_datap pData); +#endif +#ifndef MNG_SKIPCANVAS_BGR565 +mng_retcode mng_display_bgr565 (mng_datap pData); +#endif +#ifndef MNG_SKIPCANVAS_BGRA565 +mng_retcode mng_display_bgra565 (mng_datap pData); +#endif +#ifndef MNG_SKIPCANVAS_BGR565_A8 +mng_retcode mng_display_bgr565_a8 (mng_datap pData); +#endif +#ifndef MNG_SKIPCANVAS_RGB555 +mng_retcode mng_display_rgb555 (mng_datap pData); +#endif +#ifndef MNG_SKIPCANVAS_BGR555 +mng_retcode mng_display_bgr555 (mng_datap pData); +#endif + +/* ************************************************************************** */ +/* * * */ +/* * Background restore routines - restore the background with info from * */ +/* * the BACK and/or bKGD chunk and/or the app's background canvas * */ +/* * * */ +/* ************************************************************************** */ + +mng_retcode mng_restore_bkgd_backimage (mng_datap pData); +mng_retcode mng_restore_bkgd_backcolor (mng_datap pData); +mng_retcode mng_restore_bkgd_bkgd (mng_datap pData); +mng_retcode mng_restore_bkgd_bgcolor (mng_datap pData); +#ifndef MNG_SKIPCANVAS_RGB8 +mng_retcode mng_restore_bkgd_rgb8 (mng_datap pData); +#endif +#ifndef MNG_SKIPCANVAS_BGR8 +mng_retcode mng_restore_bkgd_bgr8 (mng_datap pData); +#endif +#ifndef MNG_SKIPCANVAS_BGRX8 +mng_retcode mng_restore_bkgd_bgrx8 (mng_datap pData); +#endif +#ifndef MNG_SKIPCANVAS_RGB565 +mng_retcode mng_restore_bkgd_rgb565 (mng_datap pData); +#endif +#ifndef MNG_SKIPCANVAS_BGR565 +mng_retcode mng_restore_bkgd_bgr565 (mng_datap pData); +#endif + +/* ************************************************************************** */ +/* * * */ +/* * Row retrieval routines - retrieve processed & uncompressed row-data * */ +/* * from the current "object" * */ +/* * * */ +/* ************************************************************************** */ + +mng_retcode mng_retrieve_g8 (mng_datap pData); +mng_retcode mng_retrieve_rgb8 (mng_datap pData); +mng_retcode mng_retrieve_idx8 (mng_datap pData); +mng_retcode mng_retrieve_ga8 (mng_datap pData); +mng_retcode mng_retrieve_rgba8 (mng_datap pData); +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_retrieve_g16 (mng_datap pData); +mng_retcode mng_retrieve_ga16 (mng_datap pData); +mng_retcode mng_retrieve_rgb16 (mng_datap pData); +mng_retcode mng_retrieve_rgba16 (mng_datap pData); +#endif + +/* ************************************************************************** */ +/* * * */ +/* * Row storage routines - store processed & uncompressed row-data * */ +/* * into the current "object" * */ +/* * * */ +/* ************************************************************************** */ + +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_store_g1 (mng_datap pData); +mng_retcode mng_store_g2 (mng_datap pData); +mng_retcode mng_store_g4 (mng_datap pData); +mng_retcode mng_store_idx1 (mng_datap pData); +mng_retcode mng_store_idx2 (mng_datap pData); +mng_retcode mng_store_idx4 (mng_datap pData); +#endif +mng_retcode mng_store_idx8 (mng_datap pData); +mng_retcode mng_store_rgb8 (mng_datap pData); +mng_retcode mng_store_g8 (mng_datap pData); +mng_retcode mng_store_ga8 (mng_datap pData); +mng_retcode mng_store_rgba8 (mng_datap pData); +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_store_g16 (mng_datap pData); +mng_retcode mng_store_ga16 (mng_datap pData); +mng_retcode mng_store_rgb16 (mng_datap pData); +mng_retcode mng_store_rgba16 (mng_datap pData); +#endif + +/* ************************************************************************** */ +/* * * */ +/* * Row storage routines (JPEG) - store processed & uncompressed row-data * */ +/* * into the current "object" * */ +/* * * */ +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +mng_retcode mng_store_jpeg_g8 (mng_datap pData); +mng_retcode mng_store_jpeg_rgb8 (mng_datap pData); +mng_retcode mng_store_jpeg_ga8 (mng_datap pData); +mng_retcode mng_store_jpeg_rgba8 (mng_datap pData); + +#ifdef MNG_SUPPORT_JPEG12 +mng_retcode mng_store_jpeg_g12 (mng_datap pData); +mng_retcode mng_store_jpeg_rgb12 (mng_datap pData); +mng_retcode mng_store_jpeg_ga12 (mng_datap pData); +mng_retcode mng_store_jpeg_rgba12 (mng_datap pData); +#endif + +mng_retcode mng_store_jpeg_g8_alpha (mng_datap pData); +mng_retcode mng_store_jpeg_rgb8_alpha (mng_datap pData); + +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_store_jpeg_g8_a1 (mng_datap pData); +mng_retcode mng_store_jpeg_g8_a2 (mng_datap pData); +mng_retcode mng_store_jpeg_g8_a4 (mng_datap pData); +#endif +mng_retcode mng_store_jpeg_g8_a8 (mng_datap pData); +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_store_jpeg_g8_a16 (mng_datap pData); +#endif + +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_store_jpeg_rgb8_a1 (mng_datap pData); +mng_retcode mng_store_jpeg_rgb8_a2 (mng_datap pData); +mng_retcode mng_store_jpeg_rgb8_a4 (mng_datap pData); +#endif +mng_retcode mng_store_jpeg_rgb8_a8 (mng_datap pData); +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_store_jpeg_rgb8_a16 (mng_datap pData); +#endif + +#ifdef MNG_SUPPORT_JPEG12 +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_store_jpeg_g12_a1 (mng_datap pData); +mng_retcode mng_store_jpeg_g12_a2 (mng_datap pData); +mng_retcode mng_store_jpeg_g12_a4 (mng_datap pData); +#endif +mng_retcode mng_store_jpeg_g12_a8 (mng_datap pData); +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_store_jpeg_g12_a16 (mng_datap pData); +#endif + +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_store_jpeg_rgb12_a1 (mng_datap pData); +mng_retcode mng_store_jpeg_rgb12_a2 (mng_datap pData); +mng_retcode mng_store_jpeg_rgb12_a4 (mng_datap pData); +#endif +mng_retcode mng_store_jpeg_rgb12_a8 (mng_datap pData); +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_store_jpeg_rgb12_a16 (mng_datap pData); +#endif +#endif +#endif + +/* ************************************************************************** */ +/* * * */ +/* * Delta-image row routines - apply the processed & uncompressed row-data * */ +/* * onto the target "object" * */ +/* * * */ +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_delta_g1 (mng_datap pData); +mng_retcode mng_delta_g2 (mng_datap pData); +mng_retcode mng_delta_g4 (mng_datap pData); +#endif +mng_retcode mng_delta_g8 (mng_datap pData); +mng_retcode mng_delta_g16 (mng_datap pData); +mng_retcode mng_delta_rgb8 (mng_datap pData); +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_delta_rgb16 (mng_datap pData); +#endif +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_delta_idx1 (mng_datap pData); +mng_retcode mng_delta_idx2 (mng_datap pData); +mng_retcode mng_delta_idx4 (mng_datap pData); +#endif +mng_retcode mng_delta_idx8 (mng_datap pData); +mng_retcode mng_delta_ga8 (mng_datap pData); +mng_retcode mng_delta_rgba8 (mng_datap pData); +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_delta_ga16 (mng_datap pData); +mng_retcode mng_delta_rgba16 (mng_datap pData); +#endif + +/* ************************************************************************** */ +/* * * */ +/* * Delta-image row routines - apply the source row onto the target * */ +/* * * */ +/* ************************************************************************** */ + +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_delta_g1_g1 (mng_datap pData); +mng_retcode mng_delta_g2_g2 (mng_datap pData); +mng_retcode mng_delta_g4_g4 (mng_datap pData); +#endif +mng_retcode mng_delta_g8_g8 (mng_datap pData); +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_delta_g16_g16 (mng_datap pData); +#endif +mng_retcode mng_delta_ga8_ga8 (mng_datap pData); +mng_retcode mng_delta_ga8_g8 (mng_datap pData); +mng_retcode mng_delta_ga8_a8 (mng_datap pData); +mng_retcode mng_delta_rgba8_rgb8 (mng_datap pData); +mng_retcode mng_delta_rgba8_a8 (mng_datap pData); +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_delta_ga16_ga16 (mng_datap pData); +mng_retcode mng_delta_ga16_g16 (mng_datap pData); +mng_retcode mng_delta_ga16_a16 (mng_datap pData); +mng_retcode mng_delta_rgba16_a16 (mng_datap pData); +mng_retcode mng_delta_rgba16_rgb16 (mng_datap pData); +#endif +#endif /* MNG_NO_DELTA_PNG */ +mng_retcode mng_delta_rgb8_rgb8 (mng_datap pData); /* Used for PAST */ +mng_retcode mng_delta_rgba8_rgba8 (mng_datap pData); +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_delta_rgb16_rgb16 (mng_datap pData); +mng_retcode mng_delta_rgba16_rgba16 (mng_datap pData); +#endif + +#ifndef MNG_NO_DELTA_PNG +/* ************************************************************************** */ +/* * * */ +/* * Delta-image row routines - scale the delta to bitdepth of target * */ +/* * * */ +/* ************************************************************************** */ + +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_scale_g1_g2 (mng_datap pData); +mng_retcode mng_scale_g1_g4 (mng_datap pData); +mng_retcode mng_scale_g1_g8 (mng_datap pData); +mng_retcode mng_scale_g2_g4 (mng_datap pData); +mng_retcode mng_scale_g2_g8 (mng_datap pData); +mng_retcode mng_scale_g4_g8 (mng_datap pData); +#endif +#ifndef MNG_NO_16BIT_SUPPORT +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_scale_g1_g16 (mng_datap pData); +mng_retcode mng_scale_g2_g16 (mng_datap pData); +mng_retcode mng_scale_g4_g16 (mng_datap pData); +#endif +mng_retcode mng_scale_g8_g16 (mng_datap pData); +mng_retcode mng_scale_ga8_ga16 (mng_datap pData); +mng_retcode mng_scale_rgb8_rgb16 (mng_datap pData); +mng_retcode mng_scale_rgba8_rgba16 (mng_datap pData); +#endif + +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_scale_g2_g1 (mng_datap pData); +mng_retcode mng_scale_g4_g1 (mng_datap pData); +mng_retcode mng_scale_g8_g1 (mng_datap pData); +mng_retcode mng_scale_g4_g2 (mng_datap pData); +mng_retcode mng_scale_g8_g2 (mng_datap pData); +mng_retcode mng_scale_g8_g4 (mng_datap pData); +#endif +#ifndef MNG_NO_16BIT_SUPPORT +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_scale_g16_g1 (mng_datap pData); +mng_retcode mng_scale_g16_g2 (mng_datap pData); +mng_retcode mng_scale_g16_g4 (mng_datap pData); +#endif +mng_retcode mng_scale_g16_g8 (mng_datap pData); +mng_retcode mng_scale_ga16_ga8 (mng_datap pData); +mng_retcode mng_scale_rgb16_rgb8 (mng_datap pData); +mng_retcode mng_scale_rgba16_rgba8 (mng_datap pData); +#endif +#endif /* MNG_NO_DELTA_PNG */ + +/* ************************************************************************** */ +/* * * */ +/* * Delta-image bit routines - promote bit_depth * */ +/* * * */ +/* ************************************************************************** */ + +mng_uint8 mng_promote_replicate_1_2 (mng_uint8 iB); +mng_uint8 mng_promote_replicate_1_4 (mng_uint8 iB); +mng_uint8 mng_promote_replicate_1_8 (mng_uint8 iB); +mng_uint8 mng_promote_replicate_2_4 (mng_uint8 iB); +mng_uint8 mng_promote_replicate_2_8 (mng_uint8 iB); +mng_uint8 mng_promote_replicate_4_8 (mng_uint8 iB); +#ifndef MNG_NO_16BIT_SUPPORT +mng_uint16 mng_promote_replicate_1_16 (mng_uint8 iB); +mng_uint16 mng_promote_replicate_2_16 (mng_uint8 iB); +mng_uint16 mng_promote_replicate_4_16 (mng_uint8 iB); +mng_uint16 mng_promote_replicate_8_16 (mng_uint8 iB); +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DELTA_PNG +mng_uint8 mng_promote_zerofill_1_2 (mng_uint8 iB); +mng_uint8 mng_promote_zerofill_1_4 (mng_uint8 iB); +mng_uint8 mng_promote_zerofill_1_8 (mng_uint8 iB); +mng_uint8 mng_promote_zerofill_2_4 (mng_uint8 iB); +mng_uint8 mng_promote_zerofill_2_8 (mng_uint8 iB); +mng_uint8 mng_promote_zerofill_4_8 (mng_uint8 iB); +#ifndef MNG_NO_16BIT_SUPPORT +mng_uint16 mng_promote_zerofill_1_16 (mng_uint8 iB); +mng_uint16 mng_promote_zerofill_2_16 (mng_uint8 iB); +mng_uint16 mng_promote_zerofill_4_16 (mng_uint8 iB); +mng_uint16 mng_promote_zerofill_8_16 (mng_uint8 iB); +#endif +#endif /* MNG_NO_DELTA_PNG */ + +/* ************************************************************************** */ +/* * * */ +/* * Delta-image row routines - promote color_type * */ +/* * * */ +/* ************************************************************************** */ + +mng_retcode mng_promote_g8_g8 (mng_datap pData); +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_promote_g8_g16 (mng_datap pData); +mng_retcode mng_promote_g16_g16 (mng_datap pData); +#endif + +mng_retcode mng_promote_g8_ga8 (mng_datap pData); +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_promote_g8_ga16 (mng_datap pData); +mng_retcode mng_promote_g16_ga16 (mng_datap pData); +#endif + +mng_retcode mng_promote_g8_rgb8 (mng_datap pData); +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_promote_g8_rgb16 (mng_datap pData); +mng_retcode mng_promote_g16_rgb16 (mng_datap pData); +#endif + +mng_retcode mng_promote_g8_rgba8 (mng_datap pData); +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_promote_g8_rgba16 (mng_datap pData); +mng_retcode mng_promote_g16_rgba16 (mng_datap pData); + +mng_retcode mng_promote_ga8_ga16 (mng_datap pData); +#endif + +mng_retcode mng_promote_ga8_rgba8 (mng_datap pData); +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_promote_ga8_rgba16 (mng_datap pData); +mng_retcode mng_promote_ga16_rgba16 (mng_datap pData); +#endif + +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_promote_rgb8_rgb16 (mng_datap pData); +#endif + +mng_retcode mng_promote_rgb8_rgba8 (mng_datap pData); +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_promote_rgb8_rgba16 (mng_datap pData); +mng_retcode mng_promote_rgb16_rgba16 (mng_datap pData); +#endif + +mng_retcode mng_promote_idx8_rgb8 (mng_datap pData); +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_promote_idx8_rgb16 (mng_datap pData); +#endif + +mng_retcode mng_promote_idx8_rgba8 (mng_datap pData); +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_promote_idx8_rgba16 (mng_datap pData); + +mng_retcode mng_promote_rgba8_rgba16 (mng_datap pData); +#endif + +/* ************************************************************************** */ +/* * * */ +/* * Row processing routines - convert uncompressed data from zlib to * */ +/* * managable row-data which serves as input to the color-management * */ +/* * routines * */ +/* * * */ +/* ************************************************************************** */ + +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_process_g1 (mng_datap pData); +mng_retcode mng_process_g2 (mng_datap pData); +mng_retcode mng_process_g4 (mng_datap pData); +#endif +mng_retcode mng_process_g8 (mng_datap pData); +mng_retcode mng_process_rgb8 (mng_datap pData); +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_process_idx1 (mng_datap pData); +mng_retcode mng_process_idx2 (mng_datap pData); +mng_retcode mng_process_idx4 (mng_datap pData); +#endif +mng_retcode mng_process_idx8 (mng_datap pData); +mng_retcode mng_process_ga8 (mng_datap pData); +mng_retcode mng_process_rgba8 (mng_datap pData); +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_process_g16 (mng_datap pData); +mng_retcode mng_process_ga16 (mng_datap pData); +mng_retcode mng_process_rgb16 (mng_datap pData); +mng_retcode mng_process_rgba16 (mng_datap pData); +#endif + +/* ************************************************************************** */ +/* * * */ +/* * Row processing initialization routines - set up the variables needed * */ +/* * to process uncompressed row-data * */ +/* * * */ +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_FOOTPRINT_INIT +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_init_g1_i (mng_datap pData); +mng_retcode mng_init_g2_i (mng_datap pData); +mng_retcode mng_init_g4_i (mng_datap pData); +#endif +mng_retcode mng_init_g8_i (mng_datap pData); +mng_retcode mng_init_rgb8_i (mng_datap pData); +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_init_idx1_i (mng_datap pData); +mng_retcode mng_init_idx2_i (mng_datap pData); +mng_retcode mng_init_idx4_i (mng_datap pData); +#endif +mng_retcode mng_init_idx8_i (mng_datap pData); +mng_retcode mng_init_ga8_i (mng_datap pData); +mng_retcode mng_init_rgba8_i (mng_datap pData); +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_init_g1_ni (mng_datap pData); +mng_retcode mng_init_g2_ni (mng_datap pData); +mng_retcode mng_init_g4_ni (mng_datap pData); +#endif +mng_retcode mng_init_g8_ni (mng_datap pData); +mng_retcode mng_init_rgb8_ni (mng_datap pData); +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_init_idx1_ni (mng_datap pData); +mng_retcode mng_init_idx2_ni (mng_datap pData); +mng_retcode mng_init_idx4_ni (mng_datap pData); +#endif +mng_retcode mng_init_idx8_ni (mng_datap pData); +mng_retcode mng_init_ga8_ni (mng_datap pData); +mng_retcode mng_init_rgba8_ni (mng_datap pData); +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_init_g16_i (mng_datap pData); +mng_retcode mng_init_rgb16_i (mng_datap pData); +mng_retcode mng_init_ga16_i (mng_datap pData); +mng_retcode mng_init_rgba16_i (mng_datap pData); +mng_retcode mng_init_g16_ni (mng_datap pData); +mng_retcode mng_init_rgb16_ni (mng_datap pData); +mng_retcode mng_init_ga16_ni (mng_datap pData); +mng_retcode mng_init_rgba16_ni (mng_datap pData); +#endif +#endif + +/* ************************************************************************** */ +/* * * */ +/* * Row processing initialization routines (JPEG) - set up the variables * */ +/* * needed to process uncompressed row-data * */ +/* * * */ +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_FOOTPRINT_INIT +#ifdef MNG_INCLUDE_JNG +#ifndef MNG_NO_1_2_4BIT_SUPPORT +mng_retcode mng_init_jpeg_a1_ni (mng_datap pData); +mng_retcode mng_init_jpeg_a2_ni (mng_datap pData); +mng_retcode mng_init_jpeg_a4_ni (mng_datap pData); +#endif +mng_retcode mng_init_jpeg_a8_ni (mng_datap pData); +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_init_jpeg_a16_ni (mng_datap pData); +#endif +#endif +#endif + +/* ************************************************************************** */ +/* * * */ +/* * General row processing routines * */ +/* * * */ +/* ************************************************************************** */ + +mng_retcode mng_init_rowproc (mng_datap pData); +mng_retcode mng_next_row (mng_datap pData); +#ifdef MNG_INCLUDE_JNG +mng_retcode mng_next_jpeg_alpharow (mng_datap pData); +mng_retcode mng_next_jpeg_row (mng_datap pData); +#endif +mng_retcode mng_cleanup_rowproc (mng_datap pData); + +/* ************************************************************************** */ +/* * * */ +/* * Magnification row routines - apply magnification transforms * */ +/* * * */ +/* ************************************************************************** */ + +#ifndef MNG_OPTIMIZE_FOOTPRINT_MAGN +mng_retcode mng_magnify_g8_x1 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_g8_x2 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_g8_x3 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgb8_x1 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgb8_x2 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgb8_x3 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_ga8_x1 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_ga8_x2 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_ga8_x3 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_ga8_x4 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_ga8_x5 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +#endif +mng_retcode mng_magnify_rgba8_x1 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgba8_x2 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgba8_x3 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgba8_x4 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgba8_x5 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +#ifndef MNG_OPTIMIZE_FOOTPRINT_MAGN +mng_retcode mng_magnify_g8_y1 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_g8_y2 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_g8_y3 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgb8_y1 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgb8_y2 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgb8_y3 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_ga8_y1 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_ga8_y2 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_ga8_y3 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_ga8_y4 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_ga8_y5 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +#endif +mng_retcode mng_magnify_rgba8_y1 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgba8_y2 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgba8_y3 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgba8_y4 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgba8_y5 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); + +/* ************************************************************************** */ +#ifndef MNG_NO_16BIT_SUPPORT +#ifndef MNG_OPTIMIZE_FOOTPRINT_MAGN +mng_retcode mng_magnify_g16_x1 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_g16_x2 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_g16_x3 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgb16_x1 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgb16_x2 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgb16_x3 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_ga16_x1 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_ga16_x2 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_ga16_x3 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_ga16_x4 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_ga16_x5 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgba16_x1 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgba16_x2 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgba16_x3 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgba16_x4 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgba16_x5 (mng_datap pData, + mng_uint16 iMX, + mng_uint16 iML, + mng_uint16 iMR, + mng_uint32 iWidth, + mng_uint8p pSrcline, + mng_uint8p pDstline); + +mng_retcode mng_magnify_g16_y1 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_g16_y2 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_g16_y3 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgb16_y1 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgb16_y2 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgb16_y3 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_ga16_y1 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_ga16_y2 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_ga16_y3 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_ga16_y4 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_ga16_y5 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgba16_y1 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgba16_y2 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgba16_y3 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgba16_y4 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +mng_retcode mng_magnify_rgba16_y5 (mng_datap pData, + mng_int32 iS, + mng_int32 iM, + mng_uint32 iWidth, + mng_uint8p pSrcline1, + mng_uint8p pSrcline2, + mng_uint8p pDstline); +#endif +#endif + +/* ************************************************************************** */ +/* * * */ +/* * PAST composition routines - compose over/under with a target object * */ +/* * * */ +/* ************************************************************************** */ + +mng_retcode mng_composeover_rgba8 (mng_datap pData); +#ifndef MNG_SKIPCHUNK_PAST +mng_retcode mng_composeunder_rgba8 (mng_datap pData); +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_composeover_rgba16 (mng_datap pData); +mng_retcode mng_composeunder_rgba16 (mng_datap pData); +#endif +#endif + +/* ************************************************************************** */ +/* * * */ +/* * PAST flip & tile routines - flip or tile a row of pixels * */ +/* * * */ +/* ************************************************************************** */ + +#ifndef MNG_SKIPCHUNK_PAST +mng_retcode mng_flip_rgba8 (mng_datap pData); +mng_retcode mng_tile_rgba8 (mng_datap pData); +#ifndef MNG_NO_16BIT_SUPPORT +mng_retcode mng_flip_rgba16 (mng_datap pData); +mng_retcode mng_tile_rgba16 (mng_datap pData); +#endif +#endif + +/* ************************************************************************** */ + +#endif /* _libmng_pixels_h_ */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ diff --git a/Engine/lib/lmng/libmng_prop_xs.c b/Engine/lib/lmng/libmng_prop_xs.c new file mode 100644 index 000000000..88e416d5c --- /dev/null +++ b/Engine/lib/lmng/libmng_prop_xs.c @@ -0,0 +1,2799 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_prop_xs.c copyright (c) 2000-2006 G.Juyn * */ +/* * version : 1.0.10 * */ +/* * * */ +/* * purpose : property get/set interface (implementation) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : implementation of the property get/set functions * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - fixed calling convention * */ +/* * - changed strict-ANSI stuff * */ +/* * 0.5.1 - 05/11/2000 - G.Juyn * */ +/* * - added set_outputprofile2 & set_srgbprofile2 * */ +/* * 0.5.1 - 05/12/2000 - G.Juyn * */ +/* * - changed trace to macro for callback error-reporting * */ +/* * * */ +/* * 0.5.2 - 05/23/2000 - G.Juyn * */ +/* * - changed inclusion of cms-routines * */ +/* * 0.5.2 - 05/24/2000 - G.Juyn * */ +/* * - added support for get/set default zlib/IJG parms * */ +/* * 0.5.2 - 05/31/2000 - G.Juyn * */ +/* * - fixed up punctuation (contribution by Tim Rowley) * */ +/* * 0.5.2 - 06/05/2000 - G.Juyn * */ +/* * - added support for RGB8_A8 canvasstyle * */ +/* * * */ +/* * 0.5.3 - 06/21/2000 - G.Juyn * */ +/* * - added get/set for speedtype to facilitate testing * */ +/* * - added get for imagelevel during processtext callback * */ +/* * 0.5.3 - 06/26/2000 - G.Juyn * */ +/* * - changed userdata variable to mng_ptr * */ +/* * 0.5.3 - 06/29/2000 - G.Juyn * */ +/* * - fixed incompatible return-types * */ +/* * * */ +/* * 0.9.1 - 07/08/2000 - G.Juyn * */ +/* * - added get routines for internal display variables * */ +/* * - added get/set routines for suspensionmode variable * */ +/* * 0.9.1 - 07/15/2000 - G.Juyn * */ +/* * - added get/set routines for sectionbreak variable * */ +/* * * */ +/* * 0.9.2 - 07/31/2000 - G.Juyn * */ +/* * - added status_xxxx functions * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 0.9.3 - 10/10/2000 - G.Juyn * */ +/* * - added support for alpha-depth prediction * */ +/* * 0.9.3 - 10/16/2000 - G.Juyn * */ +/* * - added functions to retrieve PNG/JNG specific header-info * */ +/* * 0.9.3 - 10/20/2000 - G.Juyn * */ +/* * - added get/set for bKGD preference setting * */ +/* * 0.9.3 - 10/21/2000 - G.Juyn * */ +/* * - added get function for interlace/progressive display * */ +/* * * */ +/* * 1.0.1 - 04/21/2001 - G.Juyn (code by G.Kelly) * */ +/* * - added BGRA8 canvas with premultiplied alpha * */ +/* * 1.0.1 - 05/02/2001 - G.Juyn * */ +/* * - added "default" sRGB generation (Thanks Marti!) * */ +/* * * */ +/* * 1.0.2 - 06/23/2001 - G.Juyn * */ +/* * - added optimization option for MNG-video playback * */ +/* * 1.0.2 - 06/25/2001 - G.Juyn * */ +/* * - added option to turn off progressive refresh * */ +/* * * */ +/* * 1.0.3 - 08/06/2001 - G.Juyn * */ +/* * - added get function for last processed BACK chunk * */ +/* * * */ +/* * 1.0.4 - 06/22/2002 - G.Juyn * */ +/* * - B495442 - invalid returnvalue in mng_get_suspensionmode * */ +/* * * */ +/* * 1.0.5 - 09/14/2002 - G.Juyn * */ +/* * - added event handling for dynamic MNG * */ +/* * 1.0.5 - 09/22/2002 - G.Juyn * */ +/* * - added bgrx8 canvas (filler byte) * */ +/* * 1.0.5 - 11/07/2002 - G.Juyn * */ +/* * - added support to get totals after mng_read() * */ +/* * * */ +/* * 1.0.6 - 05/11/2003 - G. Juyn * */ +/* * - added conditionals around canvas update routines * */ +/* * 1.0.6 - 07/07/2003 - G.R-P * */ +/* * - added conditionals around some JNG-supporting code * */ +/* * 1.0.6 - 07/11/2003 - G.R-P * */ +/* * - added conditionals zlib and jpeg property accessors * */ +/* * 1.0.6 - 07/14/2003 - G.R-P * */ +/* * - added conditionals around various unused functions * */ +/* * * */ +/* * 1.0.7 - 11/27/2003 - R.A * */ +/* * - added CANVAS_RGB565 and CANVAS_BGR565 * */ +/* * 1.0.7 - 12/06/2003 - R.A * */ +/* * - added CANVAS_RGBA565 and CANVAS_BGRA565 * */ +/* * 1.0.7 - 01/25/2004 - J.S * */ +/* * - added premultiplied alpha canvas' for RGBA, ARGB, ABGR * */ +/* * 1.0.7 - 03/07/2004 - G.R-P. * */ +/* * - put gamma, cms-related functions inside #ifdef * */ +/* * * */ +/* * 1.0.8 - 04/02/2004 - G.Juyn * */ +/* * - added CRC existence & checking flags * */ +/* * * */ +/* * 1.0.9 - 09/18/2004 - G.R-P. * */ +/* * - added some MNG_SUPPORT_WRITE conditionals * */ +/* * 1.0.9 - 10/03/2004 - G.Juyn * */ +/* * - added function to retrieve current FRAM delay * */ +/* * 1.0.9 - 10/14/2004 - G.Juyn * */ +/* * - added bgr565_a8 canvas-style (thanks to J. Elvander) * */ +/* * 1.0.9 - 12/20/2004 - G.Juyn * */ +/* * - cleaned up macro-invocations (thanks to D. Airlie) * */ +/* * * */ +/* * 1.0.10 - 03/07/2006 - (thanks to W. Manthey) * */ +/* * - added CANVAS_RGB555 and CANVAS_BGR555 * */ +/* * * */ +/* ************************************************************************** */ + +#include "libmng.h" +#include "libmng_data.h" +#include "libmng_error.h" +#include "libmng_trace.h" +#ifdef __BORLANDC__ +#pragma hdrstop +#endif +#include "libmng_objects.h" +#include "libmng_memory.h" +#include "libmng_cms.h" + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +/* ************************************************************************** */ +/* * * */ +/* * Property set functions * */ +/* * * */ +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_set_userdata (mng_handle hHandle, + mng_ptr pUserdata) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_USERDATA, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->pUserdata = pUserdata; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_USERDATA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_set_canvasstyle (mng_handle hHandle, + mng_uint32 iStyle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_CANVASSTYLE, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + + switch (iStyle) + { +#ifndef MNG_SKIPCANVAS_RGB8 + case MNG_CANVAS_RGB8 : break; +#endif +#ifndef MNG_SKIPCANVAS_RGBA8 + case MNG_CANVAS_RGBA8 : break; +#endif +#ifndef MNG_SKIPCANVAS_RGBA8_PM + case MNG_CANVAS_RGBA8_PM: break; +#endif +#ifndef MNG_SKIPCANVAS_ARGB8 + case MNG_CANVAS_ARGB8 : break; +#endif +#ifndef MNG_SKIPCANVAS_ARGB8_PM + case MNG_CANVAS_ARGB8_PM: break; +#endif +#ifndef MNG_SKIPCANVAS_RGB8_A8 + case MNG_CANVAS_RGB8_A8 : break; +#endif +#ifndef MNG_SKIPCANVAS_BGR8 + case MNG_CANVAS_BGR8 : break; +#endif +#ifndef MNG_SKIPCANVAS_BGRX8 + case MNG_CANVAS_BGRX8 : break; +#endif +#ifndef MNG_SKIPCANVAS_BGRA8 + case MNG_CANVAS_BGRA8 : break; +#endif +#ifndef MNG_SKIPCANVAS_BGRA8_PM + case MNG_CANVAS_BGRA8_PM: break; +#endif +#ifndef MNG_SKIPCANVAS_ABGR8 + case MNG_CANVAS_ABGR8 : break; +#endif +#ifndef MNG_SKIPCANVAS_ABGR8_PM + case MNG_CANVAS_ABGR8_PM: break; +#endif +#ifndef MNG_SKIPCANVAS_RGB565 + case MNG_CANVAS_RGB565 : break; +#endif +#ifndef MNG_SKIPCANVAS_RGBA565 + case MNG_CANVAS_RGBA565 : break; +#endif +#ifndef MNG_SKIPCANVAS_BGR565 + case MNG_CANVAS_BGR565 : break; +#endif +#ifndef MNG_SKIPCANVAS_BGRA565 + case MNG_CANVAS_BGRA565 : break; +#endif +#ifndef MNG_SKIPCANVAS_BGR565_A8 + case MNG_CANVAS_BGR565_A8 : break; +#endif +#ifndef MNG_SKIPCANVAS_RGB555 + case MNG_CANVAS_RGB555 : break; +#endif +#ifndef MNG_SKIPCANVAS_BGR555 + case MNG_CANVAS_BGR555 : break; +#endif +/* case MNG_CANVAS_RGB16 : break; */ +/* case MNG_CANVAS_RGBA16 : break; */ +/* case MNG_CANVAS_ARGB16 : break; */ +/* case MNG_CANVAS_BGR16 : break; */ +/* case MNG_CANVAS_BGRA16 : break; */ +/* case MNG_CANVAS_ABGR16 : break; */ +/* case MNG_CANVAS_INDEX8 : break; */ +/* case MNG_CANVAS_INDEXA8 : break; */ +/* case MNG_CANVAS_AINDEX8 : break; */ +/* case MNG_CANVAS_GRAY8 : break; */ +/* case MNG_CANVAS_GRAY16 : break; */ +/* case MNG_CANVAS_GRAYA8 : break; */ +/* case MNG_CANVAS_GRAYA16 : break; */ +/* case MNG_CANVAS_AGRAY8 : break; */ +/* case MNG_CANVAS_AGRAY16 : break; */ +/* case MNG_CANVAS_DX15 : break; */ +/* case MNG_CANVAS_DX16 : break; */ + default : { MNG_ERROR (((mng_datap)hHandle), MNG_INVALIDCNVSTYLE) }; + } + + ((mng_datap)hHandle)->iCanvasstyle = iStyle; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_CANVASSTYLE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_set_bkgdstyle (mng_handle hHandle, + mng_uint32 iStyle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_BKGDSTYLE, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + + switch (iStyle) /* alpha-modes not supported */ + { +#ifndef MNG_SKIPCANVAS_RGB8 + case MNG_CANVAS_RGB8 : break; +#endif +#ifndef MNG_SKIPCANVAS_BGR8 + case MNG_CANVAS_BGR8 : break; +#endif +#ifndef MNG_SKIPCANVAS_BGRX8 + case MNG_CANVAS_BGRX8 : break; +#endif +#ifndef MNG_SKIPCANVAS_RGB565 + case MNG_CANVAS_RGB565 : break; +#endif +#ifndef MNG_SKIPCANVAS_BGR565 + case MNG_CANVAS_BGR565 : break; +#endif +/* case MNG_CANVAS_RGB16 : break; */ +/* case MNG_CANVAS_BGR16 : break; */ +/* case MNG_CANVAS_INDEX8 : break; */ +/* case MNG_CANVAS_GRAY8 : break; */ +/* case MNG_CANVAS_GRAY16 : break; */ +/* case MNG_CANVAS_DX15 : break; */ +/* case MNG_CANVAS_DX16 : break; */ + default : MNG_ERROR (((mng_datap)hHandle), MNG_INVALIDCNVSTYLE); + } + + ((mng_datap)hHandle)->iBkgdstyle = iStyle; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_BKGDSTYLE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_set_bgcolor (mng_handle hHandle, + mng_uint16 iRed, + mng_uint16 iGreen, + mng_uint16 iBlue) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_BGCOLOR, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->iBGred = iRed; + ((mng_datap)hHandle)->iBGgreen = iGreen; + ((mng_datap)hHandle)->iBGblue = iBlue; + ((mng_datap)hHandle)->bUseBKGD = MNG_FALSE; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_BGCOLOR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_set_usebkgd (mng_handle hHandle, + mng_bool bUseBKGD) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_USEBKGD, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->bUseBKGD = bUseBKGD; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_USEBKGD, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_set_storechunks (mng_handle hHandle, + mng_bool bStorechunks) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_STORECHUNKS, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->bStorechunks = bStorechunks; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_STORECHUNKS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_set_sectionbreaks (mng_handle hHandle, + mng_bool bSectionbreaks) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SECTIONBREAKS, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->bSectionbreaks = bSectionbreaks; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SECTIONBREAKS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_set_cacheplayback (mng_handle hHandle, + mng_bool bCacheplayback) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_CACHEPLAYBACK, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + + if (((mng_datap)hHandle)->bHasheader) + MNG_ERROR (((mng_datap)hHandle), MNG_FUNCTIONINVALID); + + ((mng_datap)hHandle)->bCacheplayback = bCacheplayback; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_CACHEPLAYBACK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_set_doprogressive (mng_handle hHandle, + mng_bool bDoProgressive) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_DOPROGRESSIVE, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + + ((mng_datap)hHandle)->bDoProgressive = bDoProgressive; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_DOPROGRESSIVE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_set_crcmode (mng_handle hHandle, + mng_uint32 iCrcmode) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_CRCMODE, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + + ((mng_datap)hHandle)->iCrcmode = iCrcmode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_CRCMODE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_retcode MNG_DECL mng_set_srgb (mng_handle hHandle, + mng_bool bIssRGB) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SRGB, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->bIssRGB = bIssRGB; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SRGB, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +#ifndef MNG_SKIPCHUNK_iCCP +mng_retcode MNG_DECL mng_set_outputprofile (mng_handle hHandle, + mng_pchar zFilename) +{ +#ifdef MNG_INCLUDE_LCMS + mng_datap pData; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_OUTPUTPROFILE, MNG_LC_START); +#endif + +#ifdef MNG_INCLUDE_LCMS + MNG_VALIDHANDLE (hHandle) + + pData = (mng_datap)hHandle; /* address the structure */ + + if (pData->hProf2) /* previously defined ? */ + mnglcms_freeprofile (pData->hProf2); + /* allocate new CMS profile handle */ + pData->hProf2 = mnglcms_createfileprofile (zFilename); + + if (!pData->hProf2) /* handle error ? */ + MNG_ERRORL (pData, MNG_LCMS_NOHANDLE); +#endif /* MNG_INCLUDE_LCMS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_OUTPUTPROFILE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +#ifndef MNG_SKIPCHUNK_iCCP +mng_retcode MNG_DECL mng_set_outputprofile2 (mng_handle hHandle, + mng_uint32 iProfilesize, + mng_ptr pProfile) +{ +#ifdef MNG_INCLUDE_LCMS + mng_datap pData; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_OUTPUTPROFILE2, MNG_LC_START); +#endif + +#ifdef MNG_INCLUDE_LCMS + MNG_VALIDHANDLE (hHandle) + + pData = (mng_datap)hHandle; /* address the structure */ + + if (pData->hProf2) /* previously defined ? */ + mnglcms_freeprofile (pData->hProf2); + /* allocate new CMS profile handle */ + pData->hProf2 = mnglcms_creatememprofile (iProfilesize, pProfile); + + if (!pData->hProf2) /* handle error ? */ + MNG_ERRORL (pData, MNG_LCMS_NOHANDLE); +#endif /* MNG_INCLUDE_LCMS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_OUTPUTPROFILE2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_set_outputsrgb (mng_handle hHandle) +{ +#ifdef MNG_INCLUDE_LCMS + mng_datap pData; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_OUTPUTSRGB, MNG_LC_START); +#endif + +#ifdef MNG_INCLUDE_LCMS + MNG_VALIDHANDLE (hHandle) + + pData = (mng_datap)hHandle; /* address the structure */ + + if (pData->hProf2) /* previously defined ? */ + mnglcms_freeprofile (pData->hProf2); + /* allocate new CMS profile handle */ + pData->hProf2 = mnglcms_createsrgbprofile (); + + if (!pData->hProf2) /* handle error ? */ + MNG_ERRORL (pData, MNG_LCMS_NOHANDLE); +#endif /* MNG_INCLUDE_LCMS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_OUTPUTSRGB, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_retcode MNG_DECL mng_set_srgbprofile (mng_handle hHandle, + mng_pchar zFilename) +{ +#ifdef MNG_INCLUDE_LCMS + mng_datap pData; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SRGBPROFILE2, MNG_LC_START); +#endif + +#ifdef MNG_INCLUDE_LCMS + MNG_VALIDHANDLE (hHandle) + + pData = (mng_datap)hHandle; /* address the structure */ + + if (pData->hProf3) /* previously defined ? */ + mnglcms_freeprofile (pData->hProf3); + /* allocate new CMS profile handle */ + pData->hProf3 = mnglcms_createfileprofile (zFilename); + + if (!pData->hProf3) /* handle error ? */ + MNG_ERRORL (pData, MNG_LCMS_NOHANDLE); +#endif /* MNG_INCLUDE_LCMS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SRGBPROFILE2, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_retcode MNG_DECL mng_set_srgbprofile2 (mng_handle hHandle, + mng_uint32 iProfilesize, + mng_ptr pProfile) +{ +#ifdef MNG_INCLUDE_LCMS + mng_datap pData; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SRGBPROFILE, MNG_LC_START); +#endif + +#ifdef MNG_INCLUDE_LCMS + MNG_VALIDHANDLE (hHandle) + + pData = (mng_datap)hHandle; /* address the structure */ + + if (pData->hProf3) /* previously defined ? */ + mnglcms_freeprofile (pData->hProf3); + /* allocate new CMS profile handle */ + pData->hProf3 = mnglcms_creatememprofile (iProfilesize, pProfile); + + if (!pData->hProf3) /* handle error ? */ + MNG_ERRORL (pData, MNG_LCMS_NOHANDLE); +#endif /* MNG_INCLUDE_LCMS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SRGBPROFILE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_set_srgbimplicit (mng_handle hHandle) +{ +#ifdef MNG_INCLUDE_LCMS + mng_datap pData; +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SRGBIMPLICIT, MNG_LC_START); +#endif + +#ifdef MNG_INCLUDE_LCMS + MNG_VALIDHANDLE (hHandle) + + pData = (mng_datap)hHandle; /* address the structure */ + + if (pData->hProf3) /* previously defined ? */ + mnglcms_freeprofile (pData->hProf3); + /* allocate new CMS profile handle */ + pData->hProf3 = mnglcms_createsrgbprofile (); + + if (!pData->hProf3) /* handle error ? */ + MNG_ERRORL (pData, MNG_LCMS_NOHANDLE); +#endif /* MNG_INCLUDE_LCMS */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SRGBIMPLICIT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS) +mng_retcode MNG_DECL mng_set_viewgamma (mng_handle hHandle, + mng_float dGamma) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_VIEWGAMMA, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->dViewgamma = dGamma; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_VIEWGAMMA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_set_displaygamma (mng_handle hHandle, + mng_float dGamma) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_DISPLAYGAMMA, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->dDisplaygamma = dGamma; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_DISPLAYGAMMA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_set_dfltimggamma (mng_handle hHandle, + mng_float dGamma) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_DFLTIMGGAMMA, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->dDfltimggamma = dGamma; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_DFLTIMGGAMMA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS) +mng_retcode MNG_DECL mng_set_viewgammaint (mng_handle hHandle, + mng_uint32 iGamma) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_VIEWGAMMA, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->dViewgamma = (mng_float)iGamma / 100000; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_VIEWGAMMA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_set_displaygammaint (mng_handle hHandle, + mng_uint32 iGamma) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_DISPLAYGAMMA, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->dDisplaygamma = (mng_float)iGamma / 100000; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_DISPLAYGAMMA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#ifndef MNG_NO_DFLT_INFO +mng_retcode MNG_DECL mng_set_dfltimggammaint (mng_handle hHandle, + mng_uint32 iGamma) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_DFLTIMGGAMMA, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->dDfltimggamma = (mng_float)iGamma / 100000; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_DFLTIMGGAMMA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIP_MAXCANVAS +mng_retcode MNG_DECL mng_set_maxcanvaswidth (mng_handle hHandle, + mng_uint32 iMaxwidth) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_MAXCANVASWIDTH, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->iMaxwidth = iMaxwidth; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_MAXCANVASWIDTH, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_set_maxcanvasheight (mng_handle hHandle, + mng_uint32 iMaxheight) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_MAXCANVASHEIGHT, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->iMaxheight = iMaxheight; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_MAXCANVASHEIGHT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_set_maxcanvassize (mng_handle hHandle, + mng_uint32 iMaxwidth, + mng_uint32 iMaxheight) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_MAXCANVASSIZE, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->iMaxwidth = iMaxwidth; + ((mng_datap)hHandle)->iMaxheight = iMaxheight; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_MAXCANVASSIZE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_ZLIB +#ifdef MNG_ACCESS_ZLIB +#ifdef MNG_SUPPORT_WRITE +mng_retcode MNG_DECL mng_set_zlib_level (mng_handle hHandle, + mng_int32 iZlevel) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_ZLIB_LEVEL, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->iZlevel = iZlevel; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_ZLIB_LEVEL, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_WRITE */ +#endif /* MNG_ACCESS_ZLIB */ +#endif /* MNG_INCLUDE_ZLIB */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_ZLIB +#ifdef MNG_ACCESS_ZLIB +#ifdef MNG_SUPPORT_WRITE +mng_retcode MNG_DECL mng_set_zlib_method (mng_handle hHandle, + mng_int32 iZmethod) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_ZLIB_METHOD, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->iZmethod = iZmethod; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_ZLIB_METHOD, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_WRITE */ +#endif /* MNG_ACCESS_ZLIB */ +#endif /* MNG_INCLUDE_ZLIB */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_ZLIB +#ifdef MNG_ACCESS_ZLIB +#ifdef MNG_SUPPORT_WRITE +mng_retcode MNG_DECL mng_set_zlib_windowbits (mng_handle hHandle, + mng_int32 iZwindowbits) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_ZLIB_WINDOWBITS, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->iZwindowbits = iZwindowbits; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_ZLIB_WINDOWBITS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_WRITE */ +#endif /* MNG_ACCESS_ZLIB */ +#endif /* MNG_INCLUDE_ZLIB */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_ZLIB +#ifdef MNG_ACCESS_ZLIB +#ifdef MNG_SUPPORT_WRITE +mng_retcode MNG_DECL mng_set_zlib_memlevel (mng_handle hHandle, + mng_int32 iZmemlevel) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_ZLIB_MEMLEVEL, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->iZmemlevel = iZmemlevel; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_ZLIB_MEMLEVEL, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_WRITE */ +#endif /* MNG_ACCESS_ZLIB */ +#endif /* MNG_INCLUDE_ZLIB */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_ZLIB +#ifdef MNG_ACCESS_ZLIB +#ifdef MNG_SUPPORT_WRITE +mng_retcode MNG_DECL mng_set_zlib_strategy (mng_handle hHandle, + mng_int32 iZstrategy) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_ZLIB_STRATEGY, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->iZstrategy = iZstrategy; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_ZLIB_STRATEGY, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_WRITE */ +#endif /* MNG_ACCESS_ZLIB */ +#endif /* MNG_INCLUDE_ZLIB */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_ZLIB +#ifdef MNG_ACCESS_ZLIB +#ifdef MNG_SUPPORT_WRITE +mng_retcode MNG_DECL mng_set_zlib_maxidat (mng_handle hHandle, + mng_uint32 iMaxIDAT) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_ZLIB_MAXIDAT, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->iMaxIDAT = iMaxIDAT; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_ZLIB_MAXIDAT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_WRITE */ +#endif /* MNG_ACCESS_ZLIB */ +#endif /* MNG_INCLUDE_ZLIB */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +#ifdef MNG_ACCESS_JPEG +#ifdef MNG_SUPPORT_WRITE +mng_retcode MNG_DECL mng_set_jpeg_dctmethod (mng_handle hHandle, + mngjpeg_dctmethod eJPEGdctmethod) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_JPEG_DCTMETHOD, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->eJPEGdctmethod = eJPEGdctmethod; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_JPEG_DCTMETHOD, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif MNG_SUPPORT_WRITE +#endif /* MNG_ACCESS_JPEG */ +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +#ifdef MNG_ACCESS_JPEG +#ifdef MNG_SUPPORT_WRITE +mng_retcode MNG_DECL mng_set_jpeg_quality (mng_handle hHandle, + mng_int32 iJPEGquality) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_JPEG_QUALITY, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->iJPEGquality = iJPEGquality; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_JPEG_QUALITY, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_WRITE */ +#endif /* MNG_ACCESS_JPEG */ +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +#ifdef MNG_ACCESS_JPEG +#ifdef MNG_SUPPORT_WRITE +mng_retcode MNG_DECL mng_set_jpeg_smoothing (mng_handle hHandle, + mng_int32 iJPEGsmoothing) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_JPEG_SMOOTHING, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->iJPEGsmoothing = iJPEGsmoothing; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_JPEG_SMOOTHING, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_WRITE */ +#endif /* MNG_ACCESS_JPEG */ +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +#ifdef MNG_ACCESS_JPEG +#ifdef MNG_SUPPORT_WRITE +mng_retcode MNG_DECL mng_set_jpeg_progressive (mng_handle hHandle, + mng_bool bJPEGprogressive) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_JPEG_PROGRESSIVE, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->bJPEGcompressprogr = bJPEGprogressive; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_JPEG_PROGRESSIVE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_WRITE */ +#endif /* MNG_ACCESS_JPEG */ +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +#ifdef MNG_ACCESS_JPEG +#ifdef MNG_SUPPORT_WRITE +mng_retcode MNG_DECL mng_set_jpeg_optimized (mng_handle hHandle, + mng_bool bJPEGoptimized) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_JPEG_OPTIMIZED, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->bJPEGcompressopt = bJPEGoptimized; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_JPEG_OPTIMIZED, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_WRITE */ +#endif /* MNG_ACCESS_JPEG */ +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +#ifdef MNG_ACCESS_JPEG +#ifdef MNG_SUPPORT_WRITE +mng_retcode MNG_DECL mng_set_jpeg_maxjdat (mng_handle hHandle, + mng_uint32 iMaxJDAT) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_JPEG_MAXJDAT, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->iMaxJDAT = iMaxJDAT; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_JPEG_MAXJDAT, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_WRITE */ +#endif /* MNG_ACCESS_JPEG */ +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +mng_retcode MNG_DECL mng_set_suspensionmode (mng_handle hHandle, + mng_bool bSuspensionmode) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SUSPENSIONMODE, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + + if (((mng_datap)hHandle)->bReading) /* we must NOT be reading !!! */ + MNG_ERROR ((mng_datap)hHandle, MNG_FUNCTIONINVALID); + + ((mng_datap)hHandle)->bSuspensionmode = bSuspensionmode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SUSPENSIONMODE, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_retcode MNG_DECL mng_set_speed (mng_handle hHandle, + mng_speedtype iSpeed) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SPEED, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + ((mng_datap)hHandle)->iSpeed = iSpeed; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_SET_SPEED, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ +/* * * */ +/* * Property get functions * */ +/* * * */ +/* ************************************************************************** */ + +mng_ptr MNG_DECL mng_get_userdata (mng_handle hHandle) +{ /* no tracing in here to prevent recursive calls */ + MNG_VALIDHANDLEX (hHandle) + return ((mng_datap)hHandle)->pUserdata; +} + +/* ************************************************************************** */ + +mng_imgtype MNG_DECL mng_get_sigtype (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_SIGTYPE, MNG_LC_START); +#endif + + if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC)) + return mng_it_unknown; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_SIGTYPE, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->eSigtype; +} + +/* ************************************************************************** */ + +mng_imgtype MNG_DECL mng_get_imagetype (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_IMAGETYPE, MNG_LC_START); +#endif + + if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC)) + return mng_it_unknown; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_IMAGETYPE, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->eImagetype; +} + +/* ************************************************************************** */ + +mng_uint32 MNG_DECL mng_get_imagewidth (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_IMAGEWIDTH, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_IMAGEWIDTH, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iWidth; +} + +/* ************************************************************************** */ + +mng_uint32 MNG_DECL mng_get_imageheight (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_IMAGEWIDTH, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_IMAGEHEIGHT, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iHeight; +} + +/* ************************************************************************** */ + +mng_uint32 MNG_DECL mng_get_ticks (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_TICKS, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_TICKS, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iTicks; +} + +/* ************************************************************************** */ + +mng_uint32 MNG_DECL mng_get_framecount (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_FRAMECOUNT, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_FRAMECOUNT, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iFramecount; +} + +/* ************************************************************************** */ + +mng_uint32 MNG_DECL mng_get_layercount (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_LAYERCOUNT, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_LAYERCOUNT, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iLayercount; +} + +/* ************************************************************************** */ + +mng_uint32 MNG_DECL mng_get_playtime (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_PLAYTIME, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_PLAYTIME, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iPlaytime; +} + +/* ************************************************************************** */ + +mng_uint32 MNG_DECL mng_get_simplicity (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_SIMPLICITY, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_SIMPLICITY, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iSimplicity; +} + +/* ************************************************************************** */ + +mng_uint8 MNG_DECL mng_get_bitdepth (mng_handle hHandle) +{ + mng_uint8 iRslt; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_BITDEPTH, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + + if (((mng_datap)hHandle)->eImagetype == mng_it_png) + iRslt = ((mng_datap)hHandle)->iBitdepth; + else +#ifdef MNG_INCLUDE_JNG + if (((mng_datap)hHandle)->eImagetype == mng_it_jng) + iRslt = ((mng_datap)hHandle)->iJHDRimgbitdepth; + else +#endif + iRslt = 0; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_BITDEPTH, MNG_LC_END); +#endif + + return iRslt; +} + +/* ************************************************************************** */ + +mng_uint8 MNG_DECL mng_get_colortype (mng_handle hHandle) +{ + mng_uint8 iRslt; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_COLORTYPE, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + + if (((mng_datap)hHandle)->eImagetype == mng_it_png) + iRslt = ((mng_datap)hHandle)->iColortype; + else +#ifdef MNG_INCLUDE_JNG + if (((mng_datap)hHandle)->eImagetype == mng_it_jng) + iRslt = ((mng_datap)hHandle)->iJHDRcolortype; + else +#endif + iRslt = 0; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_COLORTYPE, MNG_LC_END); +#endif + + return iRslt; +} + +/* ************************************************************************** */ + +mng_uint8 MNG_DECL mng_get_compression (mng_handle hHandle) +{ + mng_uint8 iRslt; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_COMPRESSION, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + + if (((mng_datap)hHandle)->eImagetype == mng_it_png) + iRslt = ((mng_datap)hHandle)->iCompression; + else +#ifdef MNG_INCLUDE_JNG + if (((mng_datap)hHandle)->eImagetype == mng_it_jng) + iRslt = ((mng_datap)hHandle)->iJHDRimgcompression; + else +#endif + iRslt = 0; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_COMPRESSION, MNG_LC_END); +#endif + + return iRslt; +} + +/* ************************************************************************** */ + +mng_uint8 MNG_DECL mng_get_filter (mng_handle hHandle) +{ + mng_uint8 iRslt; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_FILTER, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + + if (((mng_datap)hHandle)->eImagetype == mng_it_png) + iRslt = ((mng_datap)hHandle)->iFilter; + else + iRslt = 0; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_FILTER, MNG_LC_END); +#endif + + return iRslt; +} + +/* ************************************************************************** */ + +mng_uint8 MNG_DECL mng_get_interlace (mng_handle hHandle) +{ + mng_uint8 iRslt; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_INTERLACE, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + + if (((mng_datap)hHandle)->eImagetype == mng_it_png) + iRslt = ((mng_datap)hHandle)->iInterlace; + else +#ifdef MNG_INCLUDE_JNG + if (((mng_datap)hHandle)->eImagetype == mng_it_jng) + iRslt = ((mng_datap)hHandle)->iJHDRimginterlace; + else +#endif + iRslt = 0; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_INTERLACE, MNG_LC_END); +#endif + + return iRslt; +} + +/* ************************************************************************** */ + +mng_uint8 MNG_DECL mng_get_alphabitdepth (mng_handle hHandle) +{ + mng_uint8 iRslt; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ALPHABITDEPTH, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_INCLUDE_JNG + if (((mng_datap)hHandle)->eImagetype == mng_it_jng) + iRslt = ((mng_datap)hHandle)->iJHDRalphabitdepth; + else +#endif + iRslt = 0; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ALPHABITDEPTH, MNG_LC_END); +#endif + + return iRslt; +} + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_uint8 MNG_DECL mng_get_refreshpass (mng_handle hHandle) +{ + mng_uint8 iRslt; + mng_datap pData; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_REFRESHPASS, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + + pData = (mng_datap)hHandle; + /* for PNG we know the exact pass */ + if ((pData->eImagetype == mng_it_png) && (pData->iPass >= 0)) + iRslt = pData->iPass; +#ifdef MNG_INCLUDE_JNG + else /* for JNG we'll fake it... */ + if ((pData->eImagetype == mng_it_jng) && + (pData->bJPEGhasheader) && (pData->bJPEGdecostarted) && + (pData->bJPEGprogressive)) + { + if (pData->pJPEGdinfo->input_scan_number <= 1) + iRslt = 0; /* first pass (I think...) */ + else + if (jpeg_input_complete (pData->pJPEGdinfo)) + iRslt = 7; /* input complete; aka final pass */ + else + iRslt = 3; /* anything between 0 and 7 will do */ + + } +#endif + else + iRslt = 0; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_REFRESHPASS, MNG_LC_END); +#endif + + return iRslt; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ +mng_uint8 MNG_DECL mng_get_alphacompression (mng_handle hHandle) +{ + mng_uint8 iRslt; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ALPHACOMPRESSION, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_INCLUDE_JNG + if (((mng_datap)hHandle)->eImagetype == mng_it_jng) + iRslt = ((mng_datap)hHandle)->iJHDRalphacompression; + else +#endif + iRslt = 0; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ALPHACOMPRESSION, MNG_LC_END); +#endif + + return iRslt; +} + +/* ************************************************************************** */ + +mng_uint8 MNG_DECL mng_get_alphafilter (mng_handle hHandle) +{ + mng_uint8 iRslt; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ALPHAFILTER, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_INCLUDE_JNG + if (((mng_datap)hHandle)->eImagetype == mng_it_jng) + iRslt = ((mng_datap)hHandle)->iJHDRalphafilter; + else +#endif + iRslt = 0; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ALPHAFILTER, MNG_LC_END); +#endif + + return iRslt; +} + +/* ************************************************************************** */ + +mng_uint8 MNG_DECL mng_get_alphainterlace (mng_handle hHandle) +{ + mng_uint8 iRslt; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ALPHAINTERLACE, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_INCLUDE_JNG + if (((mng_datap)hHandle)->eImagetype == mng_it_jng) + iRslt = ((mng_datap)hHandle)->iJHDRalphainterlace; + else +#endif + iRslt = 0; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ALPHAINTERLACE, MNG_LC_END); +#endif + + return iRslt; +} + +/* ************************************************************************** */ + +mng_uint8 MNG_DECL mng_get_alphadepth (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ALPHADEPTH, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ALPHADEPTH, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iAlphadepth; +} + +/* ************************************************************************** */ + +mng_uint32 MNG_DECL mng_get_canvasstyle (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_CANVASSTYLE, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_CANVASSTYLE, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iCanvasstyle; +} + +/* ************************************************************************** */ + +mng_uint32 MNG_DECL mng_get_bkgdstyle (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_BKGDSTYLE, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_BKGDSTYLE, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iBkgdstyle; +} + +/* ************************************************************************** */ + +mng_retcode MNG_DECL mng_get_bgcolor (mng_handle hHandle, + mng_uint16* iRed, + mng_uint16* iGreen, + mng_uint16* iBlue) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GET_BGCOLOR, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + *iRed = ((mng_datap)hHandle)->iBGred; + *iGreen = ((mng_datap)hHandle)->iBGgreen; + *iBlue = ((mng_datap)hHandle)->iBGblue; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (((mng_datap)hHandle), MNG_FN_GET_BGCOLOR, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_bool MNG_DECL mng_get_usebkgd (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_USEBKGD, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_USEBKGD, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->bUseBKGD; +} + +/* ************************************************************************** */ + +mng_bool MNG_DECL mng_get_storechunks (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_STORECHUNKS, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_STORECHUNKS, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->bStorechunks; +} + +/* ************************************************************************** */ + +mng_bool MNG_DECL mng_get_sectionbreaks (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_SECTIONBREAKS, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_SECTIONBREAKS, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->bSectionbreaks; +} + +/* ************************************************************************** */ + +mng_bool MNG_DECL mng_get_cacheplayback (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_CACHEPLAYBACK, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_CACHEPLAYBACK, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->bCacheplayback; +} + +/* ************************************************************************** */ + +mng_bool MNG_DECL mng_get_doprogressive (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_DOPROGRESSIVE, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_DOPROGRESSIVE, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->bDoProgressive; +} + +/* ************************************************************************** */ + +mng_uint32 MNG_DECL mng_get_crcmode (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_CRCMODE, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_CRCMODE, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iCrcmode; +} + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_bool MNG_DECL mng_get_srgb (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_SRGB, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEB (((mng_datap)hHandle), MNG_FN_GET_SRGB, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->bIssRGB; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS) +mng_float MNG_DECL mng_get_viewgamma (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_VIEWGAMMA, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_VIEWGAMMA, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->dViewgamma; +} +#endif + +/* ************************************************************************** */ + +#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS) +mng_float MNG_DECL mng_get_displaygamma (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_DISPLAYGAMMA, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_DISPLAYGAMMA, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->dDisplaygamma; +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DFLT_INFO +#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS) +mng_float MNG_DECL mng_get_dfltimggamma (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_DFLTIMGGAMMA, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_DFLTIMGGAMMA, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->dDfltimggamma; +} +#endif +#endif + +/* ************************************************************************** */ + +#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS) +mng_uint32 MNG_DECL mng_get_viewgammaint (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_VIEWGAMMA, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_VIEWGAMMA, MNG_LC_END); +#endif + + return (mng_uint32)(((mng_datap)hHandle)->dViewgamma * 100000); +} +#endif + +/* ************************************************************************** */ + +#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS) +mng_uint32 MNG_DECL mng_get_displaygammaint (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_DISPLAYGAMMA, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_DISPLAYGAMMA, MNG_LC_END); +#endif + + return (mng_uint32)(((mng_datap)hHandle)->dDisplaygamma * 100000); +} +#endif + +/* ************************************************************************** */ + +#ifndef MNG_NO_DFLT_INFO +#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS) +mng_uint32 MNG_DECL mng_get_dfltimggammaint (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_DFLTIMGGAMMA, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_DFLTIMGGAMMA, MNG_LC_END); +#endif + + return (mng_uint32)(((mng_datap)hHandle)->dDfltimggamma * 100000); +} +#endif +#endif + +/* ************************************************************************** */ + +#ifndef MNG_SKIP_MAXCANVAS +mng_uint32 MNG_DECL mng_get_maxcanvaswidth (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_MAXCANVASWIDTH, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_MAXCANVASWIDTH, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iMaxwidth; +} + +/* ************************************************************************** */ + +mng_uint32 MNG_DECL mng_get_maxcanvasheight (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_MAXCANVASHEIGHT, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_MAXCANVASHEIGHT, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iMaxheight; +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_ZLIB +#ifdef MNG_ACCESS_ZLIB +mng_int32 MNG_DECL mng_get_zlib_level (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ZLIB_LEVEL, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ZLIB_LEVEL, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iZlevel; +} +#endif /* MNG_ACCESS_ZLIB */ +#endif /* MNG_INCLUDE_ZLIB */ + +/* ************************************************************************** */ +#ifdef MNG_INCLUDE_ZLIB +#ifdef MNG_ACCESS_ZLIB +mng_int32 MNG_DECL mng_get_zlib_method (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ZLIB_METHOD, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ZLIB_METHOD, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iZmethod; +} + +#endif /* MNG_ACCESS_ZLIB */ +#endif /* MNG_INCLUDE_ZLIB */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_ZLIB +#ifdef MNG_ACCESS_ZLIB +mng_int32 MNG_DECL mng_get_zlib_windowbits (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ZLIB_WINDOWBITS, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ZLIB_WINDOWBITS, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iZwindowbits; +} +#endif /* MNG_ACCESS_ZLIB */ +#endif /* MNG_INCLUDE_ZLIB */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_ZLIB +#ifdef MNG_ACCESS_ZLIB +mng_int32 MNG_DECL mng_get_zlib_memlevel (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ZLIB_MEMLEVEL, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ZLIB_MEMLEVEL, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iZmemlevel; +} +#endif /* MNG_ACCESS_ZLIB */ +#endif /* MNG_INCLUDE_ZLIB */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_ZLIB +#ifdef MNG_ACCESS_ZLIB +mng_int32 MNG_DECL mng_get_zlib_strategy (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ZLIB_STRATEGY, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ZLIB_STRATEGY, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iZstrategy; +} +#endif /* MNG_ACCESS_ZLIB */ +#endif /* MNG_INCLUDE_ZLIB */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_ZLIB +#ifdef MNG_ACCESS_ZLIB +mng_uint32 MNG_DECL mng_get_zlib_maxidat (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ZLIB_MAXIDAT, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_ZLIB_MAXIDAT, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iMaxIDAT; +} +#endif /* MNG_ACCESS_ZLIB */ +#endif /* MNG_INCLUDE_ZLIB */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +#ifdef MNG_ACCESS_JPEG +mngjpeg_dctmethod MNG_DECL mng_get_jpeg_dctmethod (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_JPEG_DCTMETHOD, MNG_LC_START); +#endif + + if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC)) + return JDCT_ISLOW; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_JPEG_DCTMETHOD, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->eJPEGdctmethod; +} +#endif /* MNG_ACCESS_JPEG */ +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +#ifdef MNG_ACCESS_JPEG +mng_int32 MNG_DECL mng_get_jpeg_quality (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_JPEG_QUALITY, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_JPEG_QUALITY, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iJPEGquality; +} +#endif /* MNG_ACCESS_JPEG */ +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +#ifdef MNG_ACCESS_JPEG +mng_int32 MNG_DECL mng_get_jpeg_smoothing (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_JPEG_SMOOTHING, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_JPEG_SMOOTHING, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iJPEGsmoothing; +} +#endif /* MNG_ACCESS_JPEG */ +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +#ifdef MNG_ACCESS_JPEG +mng_bool MNG_DECL mng_get_jpeg_progressive (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_JPEG_PROGRESSIVE, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_JPEG_PROGRESSIVE, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->bJPEGcompressprogr; +} +#endif /* MNG_ACCESS_JPEG */ +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +#ifdef MNG_ACCESS_JPEG +mng_bool MNG_DECL mng_get_jpeg_optimized (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_JPEG_OPTIMIZED, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_JPEG_OPTIMIZED, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->bJPEGcompressopt; +} +#endif /* MNG_ACCESS_JPEG */ +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG +#ifdef MNG_ACCESS_JPEG +mng_uint32 MNG_DECL mng_get_jpeg_maxjdat (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_JPEG_MAXJDAT, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_JPEG_MAXJDAT, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iMaxJDAT; +} +#endif /* MNG_ACCESS_JPEG */ +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +mng_bool MNG_DECL mng_get_suspensionmode (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_SUSPENSIONMODE, MNG_LC_START); +#endif + + if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC)) + return MNG_FALSE; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_SUSPENSIONMODE, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->bSuspensionmode; +} +#endif /* MNG_SUPPORT_READ */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_speedtype MNG_DECL mng_get_speed (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_SPEED, MNG_LC_START); +#endif + + if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC)) + return mng_st_normal; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_SPEED, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iSpeed; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +mng_uint32 MNG_DECL mng_get_imagelevel (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_IMAGELEVEL, MNG_LC_START); +#endif + + MNG_VALIDHANDLEX (hHandle) + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_IMAGELEVEL, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iImagelevel; +} + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_retcode MNG_DECL mng_get_lastbackchunk (mng_handle hHandle, + mng_uint16* iRed, + mng_uint16* iGreen, + mng_uint16* iBlue, + mng_uint8* iMandatory) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_LASTBACKCHUNK, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + + if (((mng_datap)hHandle)->eImagetype != mng_it_mng) + MNG_ERROR (((mng_datap)hHandle), MNG_FUNCTIONINVALID); + + *iRed = ((mng_datap)hHandle)->iBACKred; + *iGreen = ((mng_datap)hHandle)->iBACKgreen; + *iBlue = ((mng_datap)hHandle)->iBACKblue; + *iMandatory = ((mng_datap)hHandle)->iBACKmandatory; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_LASTBACKCHUNK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_retcode MNG_DECL mng_get_lastseekname (mng_handle hHandle, + mng_pchar zSegmentname) +{ + mng_datap pData; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_LASTSEEKNAME, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + + pData = (mng_datap)hHandle; + /* only allowed for MNG ! */ + if (pData->eImagetype != mng_it_mng) + MNG_ERROR (pData, MNG_FUNCTIONINVALID); + + if (pData->pLastseek) /* is there a last SEEK ? */ + { + mng_ani_seekp pSEEK = (mng_ani_seekp)pData->pLastseek; + + if (pSEEK->iSegmentnamesize) /* copy the name if there is one */ + MNG_COPY (zSegmentname, pSEEK->zSegmentname, pSEEK->iSegmentnamesize); + + *(((mng_uint8p)zSegmentname) + pSEEK->iSegmentnamesize) = 0; + } + else + { /* return an empty string */ + *((mng_uint8p)zSegmentname) = 0; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_LASTSEEKNAME, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_uint32 MNG_DECL mng_get_currframdelay (mng_handle hHandle) +{ + mng_datap pData; + mng_uint32 iRslt; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_CURRFRAMDELAY, MNG_LC_START); +#endif + + MNG_VALIDHANDLE (hHandle) + + pData = (mng_datap)hHandle; + /* only allowed for MNG ! */ + if (pData->eImagetype != mng_it_mng) + MNG_ERROR (pData, MNG_FUNCTIONINVALID); + + iRslt = pData->iFramedelay; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_CURRFRAMDELAY, MNG_LC_END); +#endif + + return iRslt; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_uint32 MNG_DECL mng_get_starttime (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_STARTTIME, MNG_LC_START); +#endif + + if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC)) + return mng_st_normal; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_STARTTIME, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iStarttime; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_uint32 MNG_DECL mng_get_runtime (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_RUNTIME, MNG_LC_START); +#endif + + if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC)) + return mng_st_normal; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_RUNTIME, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iRuntime; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +#ifndef MNG_NO_CURRENT_INFO +mng_uint32 MNG_DECL mng_get_currentframe (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_CURRENTFRAME, MNG_LC_START); +#endif + + if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC)) + return mng_st_normal; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_CURRENTFRAME, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iFrameseq; +} +#endif +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +#ifndef MNG_NO_CURRENT_INFO +mng_uint32 MNG_DECL mng_get_currentlayer (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_CURRENTLAYER, MNG_LC_START); +#endif + + if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC)) + return mng_st_normal; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_CURRENTLAYER, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iLayerseq; +} +#endif +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +#ifndef MNG_NO_CURRENT_INFO +mng_uint32 MNG_DECL mng_get_currentplaytime (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_CURRENTPLAYTIME, MNG_LC_START); +#endif + + if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC)) + return mng_st_normal; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_CURRENTPLAYTIME, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iFrametime; +} +#endif +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +#ifndef MNG_NO_CURRENT_INFO +mng_uint32 MNG_DECL mng_get_totalframes (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_TOTALFRAMES, MNG_LC_START); +#endif + + if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC)) + return mng_st_normal; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_TOTALFRAMES, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iTotalframes; +} +#endif +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +#ifndef MNG_NO_CURRENT_INFO +mng_uint32 MNG_DECL mng_get_totallayers (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_TOTALLAYERS, MNG_LC_START); +#endif + + if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC)) + return mng_st_normal; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_TOTALLAYERS, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iTotallayers; +} +#endif +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +#ifndef MNG_NO_CURRENT_INFO +mng_uint32 MNG_DECL mng_get_totalplaytime (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_TOTALPLAYTIME, MNG_LC_START); +#endif + + if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC)) + return mng_st_normal; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_GET_TOTALPLAYTIME, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->iTotalplaytime; +} +#endif +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +mng_bool MNG_DECL mng_status_error (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_ERROR, MNG_LC_START); +#endif + + if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC)) + return MNG_FALSE; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_ERROR, MNG_LC_END); +#endif + + return (mng_bool)((mng_datap)hHandle)->iErrorcode; +} + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +mng_bool MNG_DECL mng_status_reading (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_READING, MNG_LC_START); +#endif + + if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC)) + return MNG_FALSE; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_READING, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->bReading; +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_READ +mng_bool MNG_DECL mng_status_suspendbreak (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_SUSPENDBREAK, MNG_LC_START); +#endif + + if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC)) + return MNG_FALSE; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_SUSPENDBREAK, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->bSuspended; +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_WRITE +mng_bool MNG_DECL mng_status_creating (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_CREATING, MNG_LC_START); +#endif + + if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC)) + return MNG_FALSE; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_CREATING, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->bCreating; +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_WRITE +mng_bool MNG_DECL mng_status_writing (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_WRITING, MNG_LC_START); +#endif + + if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC)) + return MNG_FALSE; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_WRITING, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->bWriting; +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_bool MNG_DECL mng_status_displaying (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_DISPLAYING, MNG_LC_START); +#endif + + if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC)) + return MNG_FALSE; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_DISPLAYING, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->bDisplaying; +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_bool MNG_DECL mng_status_running (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_RUNNING, MNG_LC_START); +#endif + + if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC)) + return MNG_FALSE; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_RUNNING, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->bRunning; +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_bool MNG_DECL mng_status_timerbreak (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_TIMERBREAK, MNG_LC_START); +#endif + + if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC)) + return MNG_FALSE; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_TIMERBREAK, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->bTimerset; +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DYNAMICMNG +mng_bool MNG_DECL mng_status_dynamic (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_DYNAMIC, MNG_LC_START); +#endif + + if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC)) + return MNG_FALSE; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_DYNAMIC, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->bDynamic; +} +#endif + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DYNAMICMNG +mng_bool MNG_DECL mng_status_runningevent (mng_handle hHandle) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_RUNNINGEVENT, MNG_LC_START); +#endif + + if ((hHandle == 0) || (((mng_datap)hHandle)->iMagic != MNG_MAGIC)) + return MNG_FALSE; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACEX (((mng_datap)hHandle), MNG_FN_STATUS_RUNNINGEVENT, MNG_LC_END); +#endif + + return ((mng_datap)hHandle)->bRunningevent; +} +#endif + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ + diff --git a/Engine/lib/lmng/libmng_read.c b/Engine/lib/lmng/libmng_read.c new file mode 100644 index 000000000..c922e19a5 --- /dev/null +++ b/Engine/lib/lmng/libmng_read.c @@ -0,0 +1,1369 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_read.c copyright (c) 2000-2007 G.Juyn * */ +/* * version : 1.0.10 * */ +/* * * */ +/* * purpose : Read logic (implementation) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : implementation of the high-level read logic * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed strict-ANSI stuff * */ +/* * 0.5.1 - 05/11/2000 - G.Juyn * */ +/* * - added callback error-reporting support * */ +/* * 0.5.1 - 05/12/2000 - G.Juyn * */ +/* * - changed trace to macro for callback error-reporting * */ +/* * * */ +/* * 0.5.2 - 05/19/2000 - G.Juyn * */ +/* * - cleaned up some code regarding mixed support * */ +/* * 0.5.2 - 05/20/2000 - G.Juyn * */ +/* * - added support for JNG * */ +/* * 0.5.2 - 05/31/2000 - G.Juyn * */ +/* * - fixed up punctuation (contribution by Tim Rowley) * */ +/* * * */ +/* * 0.5.3 - 06/16/2000 - G.Juyn * */ +/* * - changed progressive-display processing * */ +/* * * */ +/* * 0.9.1 - 07/08/2000 - G.Juyn * */ +/* * - changed read-processing for improved I/O-suspension * */ +/* * 0.9.1 - 07/14/2000 - G.Juyn * */ +/* * - changed EOF processing behavior * */ +/* * 0.9.1 - 07/14/2000 - G.Juyn * */ +/* * - changed default readbuffer size from 1024 to 4200 * */ +/* * * */ +/* * 0.9.2 - 07/27/2000 - G.Juyn * */ +/* * - B110320 - fixed GCC warning about mix-sized pointer math * */ +/* * 0.9.2 - 07/31/2000 - G.Juyn * */ +/* * - B110546 - fixed for improperly returning UNEXPECTEDEOF * */ +/* * 0.9.2 - 08/04/2000 - G.Juyn * */ +/* * - B111096 - fixed large-buffer read-suspension * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 0.9.3 - 08/26/2000 - G.Juyn * */ +/* * - added MAGN chunk * */ +/* * 0.9.3 - 10/11/2000 - G.Juyn * */ +/* * - removed test-MaGN * */ +/* * 0.9.3 - 10/16/2000 - G.Juyn * */ +/* * - added support for JDAA * */ +/* * * */ +/* * 0.9.5 - 01/23/2001 - G.Juyn * */ +/* * - fixed timing-problem with switching framing_modes * */ +/* * * */ +/* * 1.0.4 - 06/22/2002 - G.Juyn * */ +/* * - B495443 - incorrect suspend check in read_databuffer * */ +/* * * */ +/* * 1.0.5 - 07/04/2002 - G.Juyn * */ +/* * - added errorcode for extreme chunk-sizes * */ +/* * 1.0.5 - 07/08/2002 - G.Juyn * */ +/* * - B578572 - removed eMNGma hack (thanks Dimitri!) * */ +/* * 1.0.5 - 07/16/2002 - G.Juyn * */ +/* * - B581625 - large chunks fail with suspension reads * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * - added HLAPI function to copy chunks * */ +/* * 1.0.5 - 09/16/2002 - G.Juyn * */ +/* * - added event handling for dynamic MNG * */ +/* * * */ +/* * 1.0.6 - 05/25/2003 - G.R-P * */ +/* * - added MNG_SKIPCHUNK_cHNK footprint optimizations * */ +/* * 1.0.6 - 07/07/2003 - G.R-P * */ +/* * - added MNG_NO_DELTA_PNG reduction * */ +/* * - skip additional code when MNG_INCLUDE_JNG is not enabled * */ +/* * 1.0.6 - 07/29/2003 - G.R-P * */ +/* * - added conditionals around PAST chunk support * */ +/* * 1.0.6 - 08/17/2003 - G.R-P * */ +/* * - added conditionals around non-VLC chunk support * */ +/* * * */ +/* * 1.0.7 - 03/10/2004 - G.R-P * */ +/* * - added conditionals around openstream/closestream * */ +/* * * */ +/* * 1.0.8 - 04/08/2004 - G.Juyn * */ +/* * - added CRC existence & checking flags * */ +/* * 1.0.8 - 04/11/2004 - G.Juyn * */ +/* * - added data-push mechanisms for specialized decoders * */ +/* * 1.0.8 - 07/06/2004 - G.R-P * */ +/* * - defend against using undefined closestream function * */ +/* * 1.0.8 - 07/28/2004 - G.R-P * */ +/* * - added check for extreme chunk-lengths * */ +/* * * */ +/* * 1.0.9 - 09/16/2004 - G.Juyn * */ +/* * - fixed chunk pushing mechanism * */ +/* * 1.0.9 - 12/05/2004 - G.Juyn * */ +/* * - added conditional MNG_OPTIMIZE_CHUNKINITFREE * */ +/* * 1.0.9 - 12/06/2004 - G.Juyn * */ +/* * - added conditional MNG_OPTIMIZE_CHUNKASSIGN * */ +/* * - added conditional MNG_OPTIMIZE_CHUNKREADER * */ +/* * 1.0.9 - 12/20/2004 - G.Juyn * */ +/* * - cleaned up macro-invocations (thanks to D. Airlie) * */ +/* * 1.0.9 - 12/31/2004 - G.R-P * */ +/* * - removed stray characters from #ifdef directive * */ +/* * * */ +/* * 1.0.10 - 04/08/2007 - G.Juyn * */ +/* * - added support for mPNG proposal * */ +/* * * */ +/* ************************************************************************** */ + +#include "libmng.h" +#include "libmng_data.h" +#include "libmng_error.h" +#include "libmng_trace.h" +#ifdef __BORLANDC__ +#pragma hdrstop +#endif +#include "libmng_memory.h" +#include "libmng_objects.h" +#include "libmng_object_prc.h" +#include "libmng_chunks.h" +#ifdef MNG_OPTIMIZE_CHUNKREADER +#include "libmng_chunk_descr.h" +#endif +#include "libmng_chunk_prc.h" +#include "libmng_chunk_io.h" +#include "libmng_display.h" +#include "libmng_read.h" + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_READ_PROCS + +/* ************************************************************************** */ + +mng_retcode mng_process_eof (mng_datap pData) +{ + if (!pData->bEOF) /* haven't closed the stream yet ? */ + { + pData->bEOF = MNG_TRUE; /* now we do! */ + +#ifndef MNG_NO_OPEN_CLOSE_STREAM + if (pData->fClosestream && !pData->fClosestream ((mng_handle)pData)) + { + MNG_ERROR (pData, MNG_APPIOERROR); + } +#endif + } + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_release_pushdata (mng_datap pData) +{ + mng_pushdatap pFirst = pData->pFirstpushdata; + mng_pushdatap pNext = pFirst->pNext; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RELEASE_PUSHDATA, MNG_LC_START); +#endif + + pData->pFirstpushdata = pNext; /* next becomes the first */ + + if (!pNext) /* no next? => no last! */ + pData->pLastpushdata = MNG_NULL; + /* buffer owned and release callback defined? */ + if ((pFirst->bOwned) && (pData->fReleasedata)) + pData->fReleasedata ((mng_handle)pData, pFirst->pData, pFirst->iLength); + else /* otherwise use internal free mechanism */ + MNG_FREEX (pData, pFirst->pData, pFirst->iLength); + /* and free it */ + MNG_FREEX (pData, pFirst, sizeof(mng_pushdata)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RELEASE_PUSHDATA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mng_release_pushchunk (mng_datap pData) +{ + mng_pushdatap pFirst = pData->pFirstpushchunk; + mng_pushdatap pNext = pFirst->pNext; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RELEASE_PUSHCHUNK, MNG_LC_START); +#endif + + pData->pFirstpushchunk = pNext; /* next becomes the first */ + + if (!pNext) /* no next? => no last! */ + pData->pLastpushchunk = MNG_NULL; + /* buffer owned and release callback defined? */ + if ((pFirst->bOwned) && (pData->fReleasedata)) + pData->fReleasedata ((mng_handle)pData, pFirst->pData, pFirst->iLength); + else /* otherwise use internal free mechanism */ + MNG_FREEX (pData, pFirst->pData, pFirst->iLength); + /* and free it */ + MNG_FREEX (pData, pFirst, sizeof(mng_pushdata)); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_RELEASE_PUSHCHUNK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +MNG_LOCAL mng_retcode read_data (mng_datap pData, + mng_uint8p pBuf, + mng_uint32 iSize, + mng_uint32 * iRead) +{ + mng_retcode iRetcode; + mng_uint32 iTempsize = iSize; + mng_uint8p pTempbuf = pBuf; + mng_pushdatap pPush = pData->pFirstpushdata; + mng_uint32 iPushsize = 0; + *iRead = 0; /* nothing yet */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_DATA, MNG_LC_START); +#endif + + while (pPush) /* calculate size of pushed data */ + { + iPushsize += pPush->iRemaining; + pPush = pPush->pNext; + } + + if (iTempsize <= iPushsize) /* got enough push data? */ + { + while (iTempsize) + { + pPush = pData->pFirstpushdata; + /* enough data remaining in this buffer? */ + if (pPush->iRemaining <= iTempsize) + { /* no: then copy what we've got */ + MNG_COPY (pTempbuf, pPush->pDatanext, pPush->iRemaining); + /* move pointers & lengths */ + pTempbuf += pPush->iRemaining; + *iRead += pPush->iRemaining; + iTempsize -= pPush->iRemaining; + /* release the depleted buffer */ + iRetcode = mng_release_pushdata (pData); + if (iRetcode) + return iRetcode; + } + else + { /* copy the needed bytes */ + MNG_COPY (pTempbuf, pPush->pDatanext, iTempsize); + /* move pointers & lengths */ + pPush->iRemaining -= iTempsize; + pPush->pDatanext += iTempsize; + pTempbuf += iTempsize; + *iRead += iTempsize; + iTempsize = 0; /* all done!!! */ + } + } + } + else + { + mng_uint32 iTempread = 0; + /* get it from the app then */ + if (!pData->fReaddata (((mng_handle)pData), pTempbuf, iTempsize, &iTempread)) + MNG_ERROR (pData, MNG_APPIOERROR); + + *iRead += iTempread; + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_DATA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +MNG_LOCAL mng_retcode read_databuffer (mng_datap pData, + mng_uint8p pBuf, + mng_uint8p * pBufnext, + mng_uint32 iSize, + mng_uint32 * iRead) +{ + mng_retcode iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_DATABUFFER, MNG_LC_START); +#endif + + if (pData->bSuspensionmode) + { + mng_uint8p pTemp; + mng_uint32 iTemp; + + *iRead = 0; /* let's be negative about the outcome */ + + if (!pData->pSuspendbuf) /* need to create a suspension buffer ? */ + { + pData->iSuspendbufsize = MNG_SUSPENDBUFFERSIZE; + /* so, create it */ + MNG_ALLOC (pData, pData->pSuspendbuf, pData->iSuspendbufsize); + + pData->iSuspendbufleft = 0; /* make sure to fill it first time */ + pData->pSuspendbufnext = pData->pSuspendbuf; + } + /* more than our buffer can hold ? */ + if (iSize > pData->iSuspendbufsize) + { + mng_uint32 iRemain; + + if (!*pBufnext) /* first time ? */ + { + if (pData->iSuspendbufleft) /* do we have some data left ? */ + { /* then copy it */ + MNG_COPY (pBuf, pData->pSuspendbufnext, pData->iSuspendbufleft); + /* fixup variables */ + *pBufnext = pBuf + pData->iSuspendbufleft; + pData->pSuspendbufnext = pData->pSuspendbuf; + pData->iSuspendbufleft = 0; + } + else + { + *pBufnext = pBuf; + } + } + /* calculate how much to get */ + iRemain = iSize - (mng_uint32)(*pBufnext - pBuf); + /* let's go get it */ + iRetcode = read_data (pData, *pBufnext, iRemain, &iTemp); + if (iRetcode) + return iRetcode; + /* first read after suspension return 0 means EOF */ + if ((pData->iSuspendpoint) && (iTemp == 0)) + { /* that makes it final */ + mng_retcode iRetcode = mng_process_eof (pData); + if (iRetcode) /* on error bail out */ + return iRetcode; + /* indicate the source is depleted */ + *iRead = iSize - iRemain + iTemp; + } + else + { + if (iTemp < iRemain) /* suspension required ? */ + { + *pBufnext = *pBufnext + iTemp; + pData->bSuspended = MNG_TRUE; + } + else + { + *iRead = iSize; /* got it all now ! */ + } + } + } + else + { /* need to read some more ? */ + while ((!pData->bSuspended) && (!pData->bEOF) && (iSize > pData->iSuspendbufleft)) + { /* not enough space left in buffer ? */ + if (pData->iSuspendbufsize - pData->iSuspendbufleft - + (mng_uint32)(pData->pSuspendbufnext - pData->pSuspendbuf) < + MNG_SUSPENDREQUESTSIZE) + { + if (pData->iSuspendbufleft) /* then lets shift (if there's anything left) */ + MNG_COPY (pData->pSuspendbuf, pData->pSuspendbufnext, pData->iSuspendbufleft); + /* adjust running pointer */ + pData->pSuspendbufnext = pData->pSuspendbuf; + } + /* still not enough room ? */ + if (pData->iSuspendbufsize - pData->iSuspendbufleft < MNG_SUSPENDREQUESTSIZE) + MNG_ERROR (pData, MNG_INTERNALERROR); + /* now read some more data */ + pTemp = pData->pSuspendbufnext + pData->iSuspendbufleft; + + iRetcode = read_data (pData, pTemp, MNG_SUSPENDREQUESTSIZE, &iTemp); + if (iRetcode) + return iRetcode; + /* adjust fill-counter */ + pData->iSuspendbufleft += iTemp; + /* first read after suspension returning 0 means EOF */ + if ((pData->iSuspendpoint) && (iTemp == 0)) + { /* that makes it final */ + mng_retcode iRetcode = mng_process_eof (pData); + if (iRetcode) /* on error bail out */ + return iRetcode; + + if (pData->iSuspendbufleft) /* return the leftover scraps */ + MNG_COPY (pBuf, pData->pSuspendbufnext, pData->iSuspendbufleft); + /* and indicate so */ + *iRead = pData->iSuspendbufleft; + pData->pSuspendbufnext = pData->pSuspendbuf; + pData->iSuspendbufleft = 0; + } + else + { /* suspension required ? */ + if ((iSize > pData->iSuspendbufleft) && (iTemp < MNG_SUSPENDREQUESTSIZE)) + pData->bSuspended = MNG_TRUE; + + } + + pData->iSuspendpoint = 0; /* reset it here in case we loop back */ + } + + if ((!pData->bSuspended) && (!pData->bEOF)) + { /* return the data ! */ + MNG_COPY (pBuf, pData->pSuspendbufnext, iSize); + + *iRead = iSize; /* returned it all */ + /* adjust suspension-buffer variables */ + pData->pSuspendbufnext += iSize; + pData->iSuspendbufleft -= iSize; + } + } + } + else + { + iRetcode = read_data (pData, (mng_ptr)pBuf, iSize, iRead); + if (iRetcode) + return iRetcode; + if (*iRead == 0) /* suspension required ? */ + pData->bSuspended = MNG_TRUE; + } + + pData->iSuspendpoint = 0; /* safely reset it here ! */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_DATABUFFER, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +MNG_LOCAL mng_retcode process_raw_chunk (mng_datap pData, + mng_uint8p pBuf, + mng_uint32 iBuflen) +{ + +#ifndef MNG_OPTIMIZE_CHUNKREADER + /* the table-idea & binary search code was adapted from + libpng 1.1.0 (pngread.c) */ + /* NOTE1: the table must remain sorted by chunkname, otherwise the binary + search will break !!! (ps. watch upper-/lower-case chunknames !!) */ + /* NOTE2: the layout must remain equal to the header part of all the + chunk-structures (yes, that means even the pNext and pPrev fields; + it's wasting a bit of space, but hey, the code is a lot easier) */ + +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + mng_chunk_header mng_chunk_unknown = {MNG_UINT_HUH, mng_init_general, mng_free_unknown, + mng_read_unknown, mng_write_unknown, mng_assign_unknown, 0, 0, sizeof(mng_unknown_chunk)}; +#else + mng_chunk_header mng_chunk_unknown = {MNG_UINT_HUH, mng_init_unknown, mng_free_unknown, + mng_read_unknown, mng_write_unknown, mng_assign_unknown, 0, 0}; +#endif + +#ifdef MNG_OPTIMIZE_CHUNKINITFREE + + mng_chunk_header mng_chunk_table [] = + { +#ifndef MNG_SKIPCHUNK_BACK + {MNG_UINT_BACK, mng_init_general, mng_free_general, mng_read_back, mng_write_back, mng_assign_general, 0, 0, sizeof(mng_back)}, +#endif +#ifndef MNG_SKIPCHUNK_BASI + {MNG_UINT_BASI, mng_init_general, mng_free_general, mng_read_basi, mng_write_basi, mng_assign_general, 0, 0, sizeof(mng_basi)}, +#endif +#ifndef MNG_SKIPCHUNK_CLIP + {MNG_UINT_CLIP, mng_init_general, mng_free_general, mng_read_clip, mng_write_clip, mng_assign_general, 0, 0, sizeof(mng_clip)}, +#endif +#ifndef MNG_SKIPCHUNK_CLON + {MNG_UINT_CLON, mng_init_general, mng_free_general, mng_read_clon, mng_write_clon, mng_assign_general, 0, 0, sizeof(mng_clon)}, +#endif +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_DBYK + {MNG_UINT_DBYK, mng_init_general, mng_free_dbyk, mng_read_dbyk, mng_write_dbyk, mng_assign_dbyk, 0, 0, sizeof(mng_dbyk)}, +#endif +#endif +#ifndef MNG_SKIPCHUNK_DEFI + {MNG_UINT_DEFI, mng_init_general, mng_free_general, mng_read_defi, mng_write_defi, mng_assign_general, 0, 0, sizeof(mng_defi)}, +#endif +#ifndef MNG_NO_DELTA_PNG + {MNG_UINT_DHDR, mng_init_general, mng_free_general, mng_read_dhdr, mng_write_dhdr, mng_assign_general, 0, 0, sizeof(mng_dhdr)}, +#endif +#ifndef MNG_SKIPCHUNK_DISC + {MNG_UINT_DISC, mng_init_general, mng_free_disc, mng_read_disc, mng_write_disc, mng_assign_disc, 0, 0, sizeof(mng_disc)}, +#endif +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_DROP + {MNG_UINT_DROP, mng_init_general, mng_free_drop, mng_read_drop, mng_write_drop, mng_assign_drop, 0, 0, sizeof(mng_drop)}, +#endif +#endif +#ifndef MNG_SKIPCHUNK_LOOP + {MNG_UINT_ENDL, mng_init_general, mng_free_general, mng_read_endl, mng_write_endl, mng_assign_general, 0, 0, sizeof(mng_endl)}, +#endif +#ifndef MNG_SKIPCHUNK_FRAM + {MNG_UINT_FRAM, mng_init_general, mng_free_fram, mng_read_fram, mng_write_fram, mng_assign_fram, 0, 0, sizeof(mng_fram)}, +#endif + {MNG_UINT_IDAT, mng_init_general, mng_free_idat, mng_read_idat, mng_write_idat, mng_assign_idat, 0, 0, sizeof(mng_idat)}, /* 12-th element! */ + {MNG_UINT_IEND, mng_init_general, mng_free_general, mng_read_iend, mng_write_iend, mng_assign_general, 0, 0, sizeof(mng_iend)}, + {MNG_UINT_IHDR, mng_init_general, mng_free_general, mng_read_ihdr, mng_write_ihdr, mng_assign_general, 0, 0, sizeof(mng_ihdr)}, +#ifndef MNG_NO_DELTA_PNG +#ifdef MNG_INCLUDE_JNG + {MNG_UINT_IJNG, mng_init_general, mng_free_general, mng_read_ijng, mng_write_ijng, mng_assign_general, 0, 0, sizeof(mng_ijng)}, +#endif + {MNG_UINT_IPNG, mng_init_general, mng_free_general, mng_read_ipng, mng_write_ipng, mng_assign_general, 0, 0, sizeof(mng_ipng)}, +#endif +#ifdef MNG_INCLUDE_JNG + {MNG_UINT_JDAA, mng_init_general, mng_free_jdaa, mng_read_jdaa, mng_write_jdaa, mng_assign_jdaa, 0, 0, sizeof(mng_jdaa)}, + {MNG_UINT_JDAT, mng_init_general, mng_free_jdat, mng_read_jdat, mng_write_jdat, mng_assign_jdat, 0, 0, sizeof(mng_jdat)}, + {MNG_UINT_JHDR, mng_init_general, mng_free_general, mng_read_jhdr, mng_write_jhdr, mng_assign_general, 0, 0, sizeof(mng_jhdr)}, + {MNG_UINT_JSEP, mng_init_general, mng_free_general, mng_read_jsep, mng_write_jsep, mng_assign_general, 0, 0, sizeof(mng_jsep)}, + {MNG_UINT_JdAA, mng_init_general, mng_free_jdaa, mng_read_jdaa, mng_write_jdaa, mng_assign_jdaa, 0, 0, sizeof(mng_jdaa)}, +#endif +#ifndef MNG_SKIPCHUNK_LOOP + {MNG_UINT_LOOP, mng_init_general, mng_free_loop, mng_read_loop, mng_write_loop, mng_assign_loop, 0, 0, sizeof(mng_loop)}, +#endif +#ifndef MNG_SKIPCHUNK_MAGN + {MNG_UINT_MAGN, mng_init_general, mng_free_general, mng_read_magn, mng_write_magn, mng_assign_general, 0, 0, sizeof(mng_magn)}, +#endif + {MNG_UINT_MEND, mng_init_general, mng_free_general, mng_read_mend, mng_write_mend, mng_assign_general, 0, 0, sizeof(mng_mend)}, + {MNG_UINT_MHDR, mng_init_general, mng_free_general, mng_read_mhdr, mng_write_mhdr, mng_assign_general, 0, 0, sizeof(mng_mhdr)}, +#ifndef MNG_SKIPCHUNK_MOVE + {MNG_UINT_MOVE, mng_init_general, mng_free_general, mng_read_move, mng_write_move, mng_assign_general, 0, 0, sizeof(mng_move)}, +#endif +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_ORDR + {MNG_UINT_ORDR, mng_init_general, mng_free_ordr, mng_read_ordr, mng_write_ordr, mng_assign_ordr, 0, 0, sizeof(mng_ordr)}, +#endif +#endif +#ifndef MNG_SKIPCHUNK_PAST + {MNG_UINT_PAST, mng_init_general, mng_free_past, mng_read_past, mng_write_past, mng_assign_past, 0, 0, sizeof(mng_past)}, +#endif + {MNG_UINT_PLTE, mng_init_general, mng_free_general, mng_read_plte, mng_write_plte, mng_assign_general, 0, 0, sizeof(mng_plte)}, +#ifndef MNG_NO_DELTA_PNG + {MNG_UINT_PPLT, mng_init_general, mng_free_general, mng_read_pplt, mng_write_pplt, mng_assign_general, 0, 0, sizeof(mng_pplt)}, + {MNG_UINT_PROM, mng_init_general, mng_free_general, mng_read_prom, mng_write_prom, mng_assign_general, 0, 0, sizeof(mng_prom)}, +#endif +#ifndef MNG_SKIPCHUNK_SAVE + {MNG_UINT_SAVE, mng_init_general, mng_free_save, mng_read_save, mng_write_save, mng_assign_save, 0, 0, sizeof(mng_save)}, +#endif +#ifndef MNG_SKIPCHUNK_SEEK + {MNG_UINT_SEEK, mng_init_general, mng_free_seek, mng_read_seek, mng_write_seek, mng_assign_seek, 0, 0, sizeof(mng_seek)}, +#endif +#ifndef MNG_SKIPCHUNK_SHOW + {MNG_UINT_SHOW, mng_init_general, mng_free_general, mng_read_show, mng_write_show, mng_assign_general, 0, 0, sizeof(mng_show)}, +#endif +#ifndef MNG_SKIPCHUNK_TERM + {MNG_UINT_TERM, mng_init_general, mng_free_general, mng_read_term, mng_write_term, mng_assign_general, 0, 0, sizeof(mng_term)}, +#endif +#ifndef MNG_SKIPCHUNK_bKGD + {MNG_UINT_bKGD, mng_init_general, mng_free_general, mng_read_bkgd, mng_write_bkgd, mng_assign_general, 0, 0, sizeof(mng_bkgd)}, +#endif +#ifndef MNG_SKIPCHUNK_cHRM + {MNG_UINT_cHRM, mng_init_general, mng_free_general, mng_read_chrm, mng_write_chrm, mng_assign_general, 0, 0, sizeof(mng_chrm)}, +#endif +#ifndef MNG_SKIPCHUNK_eXPI + {MNG_UINT_eXPI, mng_init_general, mng_free_expi, mng_read_expi, mng_write_expi, mng_assign_expi, 0, 0, sizeof(mng_expi)}, +#endif +#ifndef MNG_SKIPCHUNK_evNT + {MNG_UINT_evNT, mng_init_general, mng_free_evnt, mng_read_evnt, mng_write_evnt, mng_assign_evnt, 0, 0, sizeof(mng_evnt)}, +#endif +#ifndef MNG_SKIPCHUNK_fPRI + {MNG_UINT_fPRI, mng_init_general, mng_free_general, mng_read_fpri, mng_write_fpri, mng_assign_general, 0, 0, sizeof(mng_fpri)}, +#endif +#ifndef MNG_SKIPCHUNK_gAMA + {MNG_UINT_gAMA, mng_init_general, mng_free_general, mng_read_gama, mng_write_gama, mng_assign_general, 0, 0, sizeof(mng_gama)}, +#endif +#ifndef MNG_SKIPCHUNK_hIST + {MNG_UINT_hIST, mng_init_general, mng_free_general, mng_read_hist, mng_write_hist, mng_assign_general, 0, 0, sizeof(mng_hist)}, +#endif +#ifndef MNG_SKIPCHUNK_iCCP + {MNG_UINT_iCCP, mng_init_general, mng_free_iccp, mng_read_iccp, mng_write_iccp, mng_assign_iccp, 0, 0, sizeof(mng_iccp)}, +#endif +#ifndef MNG_SKIPCHUNK_iTXt + {MNG_UINT_iTXt, mng_init_general, mng_free_itxt, mng_read_itxt, mng_write_itxt, mng_assign_itxt, 0, 0, sizeof(mng_itxt)}, +#endif +#ifdef MNG_INCLUDE_MPNG_PROPOSAL + {MNG_UINT_mpNG, mng_init_general, mng_free_mpng, mng_read_mpng, mng_write_mpng, mng_assign_mpng, 0, 0, sizeof(mng_mpng)}, +#endif +#ifndef MNG_SKIPCHUNK_nEED + {MNG_UINT_nEED, mng_init_general, mng_free_need, mng_read_need, mng_write_need, mng_assign_need, 0, 0, sizeof(mng_need)}, +#endif +/* TODO: {MNG_UINT_oFFs, 0, 0, 0, 0, 0, 0}, */ +/* TODO: {MNG_UINT_pCAL, 0, 0, 0, 0, 0, 0}, */ +#ifndef MNG_SKIPCHUNK_pHYg + {MNG_UINT_pHYg, mng_init_general, mng_free_general, mng_read_phyg, mng_write_phyg, mng_assign_general, 0, 0, sizeof(mng_phyg)}, +#endif +#ifndef MNG_SKIPCHUNK_pHYs + {MNG_UINT_pHYs, mng_init_general, mng_free_general, mng_read_phys, mng_write_phys, mng_assign_general, 0, 0, sizeof(mng_phys)}, +#endif +#ifndef MNG_SKIPCHUNK_sBIT + {MNG_UINT_sBIT, mng_init_general, mng_free_general, mng_read_sbit, mng_write_sbit, mng_assign_general, 0, 0, sizeof(mng_sbit)}, +#endif +/* TODO: {MNG_UINT_sCAL, 0, 0, 0, 0, 0, 0}, */ +#ifndef MNG_SKIPCHUNK_sPLT + {MNG_UINT_sPLT, mng_init_general, mng_free_splt, mng_read_splt, mng_write_splt, mng_assign_splt, 0, 0, sizeof(mng_splt)}, +#endif + {MNG_UINT_sRGB, mng_init_general, mng_free_general, mng_read_srgb, mng_write_srgb, mng_assign_general, 0, 0, sizeof(mng_srgb)}, +#ifndef MNG_SKIPCHUNK_tEXt + {MNG_UINT_tEXt, mng_init_general, mng_free_text, mng_read_text, mng_write_text, mng_assign_text, 0, 0, sizeof(mng_text)}, +#endif +#ifndef MNG_SKIPCHUNK_tIME + {MNG_UINT_tIME, mng_init_general, mng_free_general, mng_read_time, mng_write_time, mng_assign_general, 0, 0, sizeof(mng_time)}, +#endif + {MNG_UINT_tRNS, mng_init_general, mng_free_general, mng_read_trns, mng_write_trns, mng_assign_general, 0, 0, sizeof(mng_trns)}, +#ifndef MNG_SKIPCHUNK_zTXt + {MNG_UINT_zTXt, mng_init_general, mng_free_ztxt, mng_read_ztxt, mng_write_ztxt, mng_assign_ztxt, 0, 0, sizeof(mng_ztxt)}, +#endif + }; + +#else /* MNG_OPTIMIZE_CHUNKINITFREE */ + + mng_chunk_header mng_chunk_table [] = + { +#ifndef MNG_SKIPCHUNK_BACK + {MNG_UINT_BACK, mng_init_back, mng_free_back, mng_read_back, mng_write_back, mng_assign_back, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_BASI + {MNG_UINT_BASI, mng_init_basi, mng_free_basi, mng_read_basi, mng_write_basi, mng_assign_basi, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_CLIP + {MNG_UINT_CLIP, mng_init_clip, mng_free_clip, mng_read_clip, mng_write_clip, mng_assign_clip, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_CLON + {MNG_UINT_CLON, mng_init_clon, mng_free_clon, mng_read_clon, mng_write_clon, mng_assign_clon, 0, 0}, +#endif +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_DBYK + {MNG_UINT_DBYK, mng_init_dbyk, mng_free_dbyk, mng_read_dbyk, mng_write_dbyk, mng_assign_dbyk, 0, 0}, +#endif +#endif +#ifndef MNG_SKIPCHUNK_DEFI + {MNG_UINT_DEFI, mng_init_defi, mng_free_defi, mng_read_defi, mng_write_defi, mng_assign_defi, 0, 0}, +#endif +#ifndef MNG_NO_DELTA_PNG + {MNG_UINT_DHDR, mng_init_dhdr, mng_free_dhdr, mng_read_dhdr, mng_write_dhdr, mng_assign_dhdr, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_DISC + {MNG_UINT_DISC, mng_init_disc, mng_free_disc, mng_read_disc, mng_write_disc, mng_assign_disc, 0, 0}, +#endif +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_DROP + {MNG_UINT_DROP, mng_init_drop, mng_free_drop, mng_read_drop, mng_write_drop, mng_assign_drop, 0, 0}, +#endif +#endif +#ifndef MNG_SKIPCHUNK_LOOP + {MNG_UINT_ENDL, mng_init_endl, mng_free_endl, mng_read_endl, mng_write_endl, mng_assign_endl, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_FRAM + {MNG_UINT_FRAM, mng_init_fram, mng_free_fram, mng_read_fram, mng_write_fram, mng_assign_fram, 0, 0}, +#endif + {MNG_UINT_IDAT, mng_init_idat, mng_free_idat, mng_read_idat, mng_write_idat, mng_assign_idat, 0, 0}, /* 12-th element! */ + {MNG_UINT_IEND, mng_init_iend, mng_free_iend, mng_read_iend, mng_write_iend, mng_assign_iend, 0, 0}, + {MNG_UINT_IHDR, mng_init_ihdr, mng_free_ihdr, mng_read_ihdr, mng_write_ihdr, mng_assign_ihdr, 0, 0}, +#ifndef MNG_NO_DELTA_PNG +#ifdef MNG_INCLUDE_JNG + {MNG_UINT_IJNG, mng_init_ijng, mng_free_ijng, mng_read_ijng, mng_write_ijng, mng_assign_ijng, 0, 0}, +#endif + {MNG_UINT_IPNG, mng_init_ipng, mng_free_ipng, mng_read_ipng, mng_write_ipng, mng_assign_ipng, 0, 0}, +#endif +#ifdef MNG_INCLUDE_JNG + {MNG_UINT_JDAA, mng_init_jdaa, mng_free_jdaa, mng_read_jdaa, mng_write_jdaa, mng_assign_jdaa, 0, 0}, + {MNG_UINT_JDAT, mng_init_jdat, mng_free_jdat, mng_read_jdat, mng_write_jdat, mng_assign_jdat, 0, 0}, + {MNG_UINT_JHDR, mng_init_jhdr, mng_free_jhdr, mng_read_jhdr, mng_write_jhdr, mng_assign_jhdr, 0, 0}, + {MNG_UINT_JSEP, mng_init_jsep, mng_free_jsep, mng_read_jsep, mng_write_jsep, mng_assign_jsep, 0, 0}, + {MNG_UINT_JdAA, mng_init_jdaa, mng_free_jdaa, mng_read_jdaa, mng_write_jdaa, mng_assign_jdaa, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_LOOP + {MNG_UINT_LOOP, mng_init_loop, mng_free_loop, mng_read_loop, mng_write_loop, mng_assign_loop, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_MAGN + {MNG_UINT_MAGN, mng_init_magn, mng_free_magn, mng_read_magn, mng_write_magn, mng_assign_magn, 0, 0}, +#endif + {MNG_UINT_MEND, mng_init_mend, mng_free_mend, mng_read_mend, mng_write_mend, mng_assign_mend, 0, 0}, + {MNG_UINT_MHDR, mng_init_mhdr, mng_free_mhdr, mng_read_mhdr, mng_write_mhdr, mng_assign_mhdr, 0, 0}, +#ifndef MNG_SKIPCHUNK_MOVE + {MNG_UINT_MOVE, mng_init_move, mng_free_move, mng_read_move, mng_write_move, mng_assign_move, 0, 0}, +#endif +#ifndef MNG_NO_DELTA_PNG +#ifndef MNG_SKIPCHUNK_ORDR + {MNG_UINT_ORDR, mng_init_ordr, mng_free_ordr, mng_read_ordr, mng_write_ordr, mng_assign_ordr, 0, 0}, +#endif +#endif +#ifndef MNG_SKIPCHUNK_PAST + {MNG_UINT_PAST, mng_init_past, mng_free_past, mng_read_past, mng_write_past, mng_assign_past, 0, 0}, +#endif + {MNG_UINT_PLTE, mng_init_plte, mng_free_plte, mng_read_plte, mng_write_plte, mng_assign_plte, 0, 0}, +#ifndef MNG_NO_DELTA_PNG + {MNG_UINT_PPLT, mng_init_pplt, mng_free_pplt, mng_read_pplt, mng_write_pplt, mng_assign_pplt, 0, 0}, + {MNG_UINT_PROM, mng_init_prom, mng_free_prom, mng_read_prom, mng_write_prom, mng_assign_prom, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_SAVE + {MNG_UINT_SAVE, mng_init_save, mng_free_save, mng_read_save, mng_write_save, mng_assign_save, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_SEEK + {MNG_UINT_SEEK, mng_init_seek, mng_free_seek, mng_read_seek, mng_write_seek, mng_assign_seek, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_SHOW + {MNG_UINT_SHOW, mng_init_show, mng_free_show, mng_read_show, mng_write_show, mng_assign_show, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_TERM + {MNG_UINT_TERM, mng_init_term, mng_free_term, mng_read_term, mng_write_term, mng_assign_term, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_bKGD + {MNG_UINT_bKGD, mng_init_bkgd, mng_free_bkgd, mng_read_bkgd, mng_write_bkgd, mng_assign_bkgd, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_cHRM + {MNG_UINT_cHRM, mng_init_chrm, mng_free_chrm, mng_read_chrm, mng_write_chrm, mng_assign_chrm, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_eXPI + {MNG_UINT_eXPI, mng_init_expi, mng_free_expi, mng_read_expi, mng_write_expi, mng_assign_expi, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_evNT + {MNG_UINT_evNT, mng_init_evnt, mng_free_evnt, mng_read_evnt, mng_write_evnt, mng_assign_evnt, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_fPRI + {MNG_UINT_fPRI, mng_init_fpri, mng_free_fpri, mng_read_fpri, mng_write_fpri, mng_assign_fpri, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_gAMA + {MNG_UINT_gAMA, mng_init_gama, mng_free_gama, mng_read_gama, mng_write_gama, mng_assign_gama, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_hIST + {MNG_UINT_hIST, mng_init_hist, mng_free_hist, mng_read_hist, mng_write_hist, mng_assign_hist, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_iCCP + {MNG_UINT_iCCP, mng_init_iccp, mng_free_iccp, mng_read_iccp, mng_write_iccp, mng_assign_iccp, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_iTXt + {MNG_UINT_iTXt, mng_init_itxt, mng_free_itxt, mng_read_itxt, mng_write_itxt, mng_assign_itxt, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_nEED + {MNG_UINT_nEED, mng_init_need, mng_free_need, mng_read_need, mng_write_need, mng_assign_need, 0, 0}, +#endif +/* TODO: {MNG_UINT_oFFs, 0, 0, 0, 0, 0, 0}, */ +/* TODO: {MNG_UINT_pCAL, 0, 0, 0, 0, 0, 0}, */ +#ifndef MNG_SKIPCHUNK_pHYg + {MNG_UINT_pHYg, mng_init_phyg, mng_free_phyg, mng_read_phyg, mng_write_phyg, mng_assign_phyg, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_pHYs + {MNG_UINT_pHYs, mng_init_phys, mng_free_phys, mng_read_phys, mng_write_phys, mng_assign_phys, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_sBIT + {MNG_UINT_sBIT, mng_init_sbit, mng_free_sbit, mng_read_sbit, mng_write_sbit, mng_assign_sbit, 0, 0}, +#endif +/* TODO: {MNG_UINT_sCAL, 0, 0, 0, 0, 0, 0}, */ +#ifndef MNG_SKIPCHUNK_sPLT + {MNG_UINT_sPLT, mng_init_splt, mng_free_splt, mng_read_splt, mng_write_splt, mng_assign_splt, 0, 0}, +#endif + {MNG_UINT_sRGB, mng_init_srgb, mng_free_srgb, mng_read_srgb, mng_write_srgb, mng_assign_srgb, 0, 0}, +#ifndef MNG_SKIPCHUNK_tEXt + {MNG_UINT_tEXt, mng_init_text, mng_free_text, mng_read_text, mng_write_text, mng_assign_text, 0, 0}, +#endif +#ifndef MNG_SKIPCHUNK_tIME + {MNG_UINT_tIME, mng_init_time, mng_free_time, mng_read_time, mng_write_time, mng_assign_time, 0, 0}, +#endif + {MNG_UINT_tRNS, mng_init_trns, mng_free_trns, mng_read_trns, mng_write_trns, mng_assign_trns, 0, 0}, +#ifndef MNG_SKIPCHUNK_zTXt + {MNG_UINT_zTXt, mng_init_ztxt, mng_free_ztxt, mng_read_ztxt, mng_write_ztxt, mng_assign_ztxt, 0, 0}, +#endif + }; + +#endif /* MNG_OPTIMIZE_CHUNKINITFREE */ + + /* binary search variables */ + mng_int32 iTop, iLower, iUpper, iMiddle; + mng_chunk_headerp pEntry; /* pointer to found entry */ +#else + mng_chunk_header sEntry; /* temp chunk-header */ +#endif /* MNG_OPTIMIZE_CHUNKREADER */ + + mng_chunkid iChunkname; /* the chunk's tag */ + mng_chunkp pChunk; /* chunk structure (if #define MNG_STORE_CHUNKS) */ + mng_retcode iRetcode; /* temporary error-code */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_RAW_CHUNK, MNG_LC_START); +#endif + /* reset timer indicator on read-cycle */ + if ((pData->bReading) && (!pData->bDisplaying)) + pData->bTimerset = MNG_FALSE; + /* get the chunkname */ + iChunkname = (mng_chunkid)(mng_get_uint32 (pBuf)); + + pBuf += sizeof (mng_chunkid); /* adjust the buffer */ + iBuflen -= sizeof (mng_chunkid); + pChunk = 0; + +#ifndef MNG_OPTIMIZE_CHUNKREADER + /* determine max index of table */ + iTop = (sizeof (mng_chunk_table) / sizeof (mng_chunk_table [0])) - 1; + + /* binary search; with 54 chunks, worst-case is 7 comparisons */ + iLower = 0; +#ifndef MNG_NO_DELTA_PNG + iMiddle = 11; /* start with the IDAT entry */ +#else + iMiddle = 8; +#endif + iUpper = iTop; + pEntry = 0; /* no goods yet! */ + + do /* the binary search itself */ + { + if (mng_chunk_table [iMiddle].iChunkname < iChunkname) + iLower = iMiddle + 1; + else if (mng_chunk_table [iMiddle].iChunkname > iChunkname) + iUpper = iMiddle - 1; + else + { + pEntry = &mng_chunk_table [iMiddle]; + break; + } + + iMiddle = (iLower + iUpper) >> 1; + } + while (iLower <= iUpper); + + if (!pEntry) /* unknown chunk ? */ + pEntry = &mng_chunk_unknown; /* make it so! */ + +#else /* MNG_OPTIMIZE_CHUNKREADER */ + + mng_get_chunkheader (iChunkname, &sEntry); + +#endif /* MNG_OPTIMIZE_CHUNKREADER */ + + pData->iChunkname = iChunkname; /* keep track of where we are */ + pData->iChunkseq++; + +#ifndef MNG_OPTIMIZE_CHUNKREADER + if (pEntry->fRead) /* read-callback available ? */ + { + iRetcode = pEntry->fRead (pData, pEntry, iBuflen, (mng_ptr)pBuf, &pChunk); + + if (!iRetcode) /* everything oke ? */ + { /* remember unknown chunk's id */ + if ((pChunk) && (pEntry->iChunkname == MNG_UINT_HUH)) + ((mng_chunk_headerp)pChunk)->iChunkname = iChunkname; + } + } +#else /* MNG_OPTIMIZE_CHUNKREADER */ + if (sEntry.fRead) /* read-callback available ? */ + { + iRetcode = sEntry.fRead (pData, &sEntry, iBuflen, (mng_ptr)pBuf, &pChunk); + +#ifndef MNG_OPTIMIZE_CHUNKREADER + if (!iRetcode) /* everything oke ? */ + { /* remember unknown chunk's id */ + if ((pChunk) && (sEntry.iChunkname == MNG_UINT_HUH)) + ((mng_chunk_headerp)pChunk)->iChunkname = iChunkname; + } +#endif + } +#endif /* MNG_OPTIMIZE_CHUNKREADER */ + else + iRetcode = MNG_NOERROR; + + if (pChunk) /* store this chunk ? */ + mng_add_chunk (pData, pChunk); /* do it */ + +#ifdef MNG_INCLUDE_JNG /* implicit EOF ? */ + if ((!pData->bHasMHDR) && (!pData->bHasIHDR) && (!pData->bHasJHDR)) +#else + if ((!pData->bHasMHDR) && (!pData->bHasIHDR)) +#endif + iRetcode = mng_process_eof (pData);/* then do some EOF processing */ + + if (iRetcode) /* on error bail out */ + return iRetcode; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_PROCESS_RAW_CHUNK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +MNG_LOCAL mng_retcode check_chunk_crc (mng_datap pData, + mng_uint8p pBuf, + mng_uint32 iBuflen) +{ + mng_uint32 iCrc; /* calculated CRC */ + mng_bool bDiscard = MNG_FALSE; + mng_retcode iRetcode = MNG_NOERROR; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_CHUNK_CRC, MNG_LC_START); +#endif + + if (pData->iCrcmode & MNG_CRC_INPUT) /* crc included ? */ + { + mng_bool bCritical = (mng_bool)((*pBuf & 0x20) == 0); + mng_uint32 iL = iBuflen - (mng_uint32)(sizeof (iCrc)); + + if (((bCritical ) && (pData->iCrcmode & MNG_CRC_CRITICAL )) || + ((!bCritical) && (pData->iCrcmode & MNG_CRC_ANCILLARY))) + { /* calculate the crc */ + iCrc = mng_crc (pData, pBuf, iL); + /* and check it */ + if (!(iCrc == mng_get_uint32 (pBuf + iL))) + { + mng_bool bWarning = MNG_FALSE; + mng_bool bError = MNG_FALSE; + + if (bCritical) + { + switch (pData->iCrcmode & MNG_CRC_CRITICAL) + { + case MNG_CRC_CRITICAL_WARNING : { bWarning = MNG_TRUE; break; } + case MNG_CRC_CRITICAL_ERROR : { bError = MNG_TRUE; break; } + } + } + else + { + switch (pData->iCrcmode & MNG_CRC_ANCILLARY) + { + case MNG_CRC_ANCILLARY_DISCARD : { bDiscard = MNG_TRUE; break; } + case MNG_CRC_ANCILLARY_WARNING : { bWarning = MNG_TRUE; break; } + case MNG_CRC_ANCILLARY_ERROR : { bError = MNG_TRUE; break; } + } + } + + if (bWarning) + MNG_WARNING (pData, MNG_INVALIDCRC); + if (bError) + MNG_ERROR (pData, MNG_INVALIDCRC); + } + } + + if (!bDiscard) /* still processing ? */ + iRetcode = process_raw_chunk (pData, pBuf, iL); + } + else + { /* no crc => straight onto processing */ + iRetcode = process_raw_chunk (pData, pBuf, iBuflen); + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_CHUNK_CRC, MNG_LC_END); +#endif + + return iRetcode; +} + +/* ************************************************************************** */ + +MNG_LOCAL mng_retcode read_chunk (mng_datap pData) +{ + mng_uint32 iBufmax = pData->iReadbufsize; + mng_uint8p pBuf = pData->pReadbuf; + mng_uint32 iBuflen = 0; /* number of bytes requested */ + mng_uint32 iRead = 0; /* number of bytes read */ + mng_retcode iRetcode = MNG_NOERROR; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_CHUNK, MNG_LC_START); +#endif + +#ifdef MNG_SUPPORT_DISPLAY + if (pData->pCurraniobj) /* processing an animation object ? */ + { + do /* process it then */ + { + iRetcode = ((mng_object_headerp)pData->pCurraniobj)->fProcess (pData, pData->pCurraniobj); + /* refresh needed ? */ +/* if ((!iRetcode) && (!pData->bTimerset) && (pData->bNeedrefresh)) + iRetcode = display_progressive_refresh (pData, 1); */ + /* can we advance to next object ? */ + if ((!iRetcode) && (pData->pCurraniobj) && + (!pData->bTimerset) && (!pData->bSectionwait)) + { /* reset timer indicator on read-cycle */ + if ((pData->bReading) && (!pData->bDisplaying)) + pData->bTimerset = MNG_FALSE; + + pData->pCurraniobj = ((mng_object_headerp)pData->pCurraniobj)->pNext; + /* TERM processing to be done ? */ + if ((!pData->pCurraniobj) && (pData->bHasTERM) && (!pData->bHasMHDR)) + iRetcode = mng_process_display_mend (pData); + } + } /* until error or a break or no more objects */ + while ((!iRetcode) && (pData->pCurraniobj) && + (!pData->bTimerset) && (!pData->bSectionwait) && (!pData->bFreezing)); + } + else + { + if (pData->iBreakpoint) /* do we need to finish something first ? */ + { + switch (pData->iBreakpoint) /* return to broken display routine */ + { +#ifndef MNG_SKIPCHUNK_FRAM + case 1 : { iRetcode = mng_process_display_fram2 (pData); break; } +#endif + case 2 : { iRetcode = mng_process_display_ihdr (pData); break; } +#ifndef MNG_SKIPCHUNK_SHOW + case 3 : ; /* same as 4 !!! */ + case 4 : { iRetcode = mng_process_display_show (pData); break; } +#endif +#ifndef MNG_SKIPCHUNK_CLON + case 5 : { iRetcode = mng_process_display_clon2 (pData); break; } +#endif +#ifdef MNG_INCLUDE_JNG + case 7 : { iRetcode = mng_process_display_jhdr (pData); break; } +#endif + case 6 : ; /* same as 8 !!! */ + case 8 : { iRetcode = mng_process_display_iend (pData); break; } +#ifndef MNG_SKIPCHUNK_MAGN + case 9 : { iRetcode = mng_process_display_magn2 (pData); break; } +#endif + case 10 : { iRetcode = mng_process_display_mend2 (pData); break; } +#ifndef MNG_SKIPCHUNK_PAST + case 11 : { iRetcode = mng_process_display_past2 (pData); break; } +#endif + } + } + } + + if (iRetcode) /* on error bail out */ + return iRetcode; + +#endif /* MNG_SUPPORT_DISPLAY */ + /* can we continue processing now, or do we */ + /* need to wait for the timer to finish (again) ? */ +#ifdef MNG_SUPPORT_DISPLAY + if ((!pData->bTimerset) && (!pData->bSectionwait) && (!pData->bEOF)) +#else + if (!pData->bEOF) +#endif + { +#ifdef MNG_SUPPORT_DISPLAY + /* freezing in progress ? */ + if ((pData->bFreezing) && (pData->iSuspendpoint == 0)) + pData->bRunning = MNG_FALSE; /* then this is the right moment to do it */ +#endif + + if (pData->iSuspendpoint <= 2) + { + iBuflen = sizeof (mng_uint32); /* read length */ + iRetcode = read_databuffer (pData, pBuf, &pData->pReadbufnext, iBuflen, &iRead); + + if (iRetcode) /* bail on errors */ + return iRetcode; + + if (pData->bSuspended) /* suspended ? */ + pData->iSuspendpoint = 2; + else /* save the length */ + { + pData->iChunklen = mng_get_uint32 (pBuf); + if (pData->iChunklen > 0x7ffffff) + return MNG_INVALIDLENGTH; + } + + } + + if (!pData->bSuspended) /* still going ? */ + { /* previously suspended or not eof ? */ + if ((pData->iSuspendpoint > 2) || (iRead == iBuflen)) + { /* determine length chunkname + data (+ crc) */ + if (pData->iCrcmode & MNG_CRC_INPUT) + iBuflen = pData->iChunklen + (mng_uint32)(sizeof (mng_chunkid) + sizeof (mng_uint32)); + else + iBuflen = pData->iChunklen + (mng_uint32)(sizeof (mng_chunkid)); + + /* do we have enough data in the current push buffer ? */ + if ((pData->pFirstpushdata) && (iBuflen <= pData->pFirstpushdata->iRemaining)) + { + mng_pushdatap pPush = pData->pFirstpushdata; + pBuf = pPush->pDatanext; + pPush->pDatanext += iBuflen; + pPush->iRemaining -= iBuflen; + pData->iSuspendpoint = 0; /* safely reset this here ! */ + + iRetcode = check_chunk_crc (pData, pBuf, iBuflen); + if (iRetcode) + return iRetcode; + + if (!pPush->iRemaining) /* buffer depleted? then release it */ + iRetcode = mng_release_pushdata (pData); + } + else + { + if (iBuflen < iBufmax) /* does it fit in default buffer ? */ + { /* note that we don't use the full size + so there's always a zero-byte at the + very end !!! */ + iRetcode = read_databuffer (pData, pBuf, &pData->pReadbufnext, iBuflen, &iRead); + if (iRetcode) /* bail on errors */ + return iRetcode; + + if (pData->bSuspended) /* suspended ? */ + pData->iSuspendpoint = 3; + else + { + if (iRead != iBuflen) /* did we get all the data ? */ + MNG_ERROR (pData, MNG_UNEXPECTEDEOF); + iRetcode = check_chunk_crc (pData, pBuf, iBuflen); + } + } + else + { + if (iBuflen > 16777216) /* is the length incredible? */ + MNG_ERROR (pData, MNG_IMPROBABLELENGTH); + + if (!pData->iSuspendpoint) /* create additional large buffer ? */ + { /* again reserve space for the last zero-byte */ + pData->iLargebufsize = iBuflen + 1; + pData->pLargebufnext = MNG_NULL; + MNG_ALLOC (pData, pData->pLargebuf, pData->iLargebufsize); + } + + iRetcode = read_databuffer (pData, pData->pLargebuf, &pData->pLargebufnext, iBuflen, &iRead); + if (iRetcode) + return iRetcode; + + if (pData->bSuspended) /* suspended ? */ + pData->iSuspendpoint = 4; + else + { + if (iRead != iBuflen) /* did we get all the data ? */ + MNG_ERROR (pData, MNG_UNEXPECTEDEOF); + iRetcode = check_chunk_crc (pData, pData->pLargebuf, iBuflen); + /* cleanup additional large buffer */ + MNG_FREE (pData, pData->pLargebuf, pData->iLargebufsize); + } + } + } + + if (iRetcode) /* on error bail out */ + return iRetcode; + + } + else + { /* that's final */ + iRetcode = mng_process_eof (pData); + + if (iRetcode) /* on error bail out */ + return iRetcode; + + if ((iRead != 0) || /* did we get an unexpected eof ? */ +#ifdef MNG_INCLUDE_JNG + (pData->bHasIHDR || pData->bHasMHDR || pData->bHasJHDR)) +#else + (pData->bHasIHDR || pData->bHasMHDR)) +#endif + MNG_ERROR (pData, MNG_UNEXPECTEDEOF); + } + } + } + +#ifdef MNG_SUPPORT_DISPLAY /* refresh needed ? */ + if ((!pData->bTimerset) && (!pData->bSuspended) && (pData->bNeedrefresh)) + { + iRetcode = mng_display_progressive_refresh (pData, 1); + + if (iRetcode) /* on error bail out */ + return iRetcode; + } +#endif + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_CHUNK, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +MNG_LOCAL mng_retcode process_pushedchunk (mng_datap pData) +{ + mng_pushdatap pPush; + mng_retcode iRetcode = MNG_NOERROR; + +#ifdef MNG_SUPPORT_DISPLAY + if (pData->pCurraniobj) /* processing an animation object ? */ + { + do /* process it then */ + { + iRetcode = ((mng_object_headerp)pData->pCurraniobj)->fProcess (pData, pData->pCurraniobj); + /* refresh needed ? */ +/* if ((!iRetcode) && (!pData->bTimerset) && (pData->bNeedrefresh)) + iRetcode = display_progressive_refresh (pData, 1); */ + /* can we advance to next object ? */ + if ((!iRetcode) && (pData->pCurraniobj) && + (!pData->bTimerset) && (!pData->bSectionwait)) + { /* reset timer indicator on read-cycle */ + if ((pData->bReading) && (!pData->bDisplaying)) + pData->bTimerset = MNG_FALSE; + + pData->pCurraniobj = ((mng_object_headerp)pData->pCurraniobj)->pNext; + /* TERM processing to be done ? */ + if ((!pData->pCurraniobj) && (pData->bHasTERM) && (!pData->bHasMHDR)) + iRetcode = mng_process_display_mend (pData); + } + } /* until error or a break or no more objects */ + while ((!iRetcode) && (pData->pCurraniobj) && + (!pData->bTimerset) && (!pData->bSectionwait) && (!pData->bFreezing)); + } + else + { + if (pData->iBreakpoint) /* do we need to finish something first ? */ + { + switch (pData->iBreakpoint) /* return to broken display routine */ + { +#ifndef MNG_SKIPCHUNK_FRAM + case 1 : { iRetcode = mng_process_display_fram2 (pData); break; } +#endif + case 2 : { iRetcode = mng_process_display_ihdr (pData); break; } +#ifndef MNG_SKIPCHUNK_SHOW + case 3 : ; /* same as 4 !!! */ + case 4 : { iRetcode = mng_process_display_show (pData); break; } +#endif +#ifndef MNG_SKIPCHUNK_CLON + case 5 : { iRetcode = mng_process_display_clon2 (pData); break; } +#endif +#ifdef MNG_INCLUDE_JNG + case 7 : { iRetcode = mng_process_display_jhdr (pData); break; } +#endif + case 6 : ; /* same as 8 !!! */ + case 8 : { iRetcode = mng_process_display_iend (pData); break; } +#ifndef MNG_SKIPCHUNK_MAGN + case 9 : { iRetcode = mng_process_display_magn2 (pData); break; } +#endif + case 10 : { iRetcode = mng_process_display_mend2 (pData); break; } +#ifndef MNG_SKIPCHUNK_PAST + case 11 : { iRetcode = mng_process_display_past2 (pData); break; } +#endif + } + } + } + + if (iRetcode) /* on error bail out */ + return iRetcode; + +#endif /* MNG_SUPPORT_DISPLAY */ + /* can we continue processing now, or do we */ + /* need to wait for the timer to finish (again) ? */ +#ifdef MNG_SUPPORT_DISPLAY + if ((!pData->bTimerset) && (!pData->bSectionwait) && (!pData->bEOF)) +#else + if (!pData->bEOF) +#endif + { + pData->iSuspendpoint = 0; /* safely reset it here ! */ + pPush = pData->pFirstpushchunk; + + iRetcode = process_raw_chunk (pData, pPush->pData, pPush->iLength); + if (iRetcode) + return iRetcode; + +#ifdef MNG_SUPPORT_DISPLAY /* refresh needed ? */ + if ((!pData->bTimerset) && (!pData->bSuspended) && (pData->bNeedrefresh)) + { + iRetcode = mng_display_progressive_refresh (pData, 1); + if (iRetcode) /* on error bail out */ + return iRetcode; + } +#endif + } + + return mng_release_pushchunk (pData); +} + +/* ************************************************************************** */ + +mng_retcode mng_read_graphic (mng_datap pData) +{ + mng_uint32 iBuflen; /* number of bytes requested */ + mng_uint32 iRead; /* number of bytes read */ + mng_retcode iRetcode; /* temporary error-code */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_GRAPHIC, MNG_LC_START); +#endif + + if (!pData->pReadbuf) /* buffer allocated ? */ + { + pData->iReadbufsize = 4200; /* allocate a default read buffer */ + MNG_ALLOC (pData, pData->pReadbuf, pData->iReadbufsize); + } + /* haven't processed the signature ? */ + if ((!pData->bHavesig) || (pData->iSuspendpoint == 1)) + { + iBuflen = 2 * sizeof (mng_uint32); /* read signature */ + + iRetcode = read_databuffer (pData, pData->pReadbuf, &pData->pReadbufnext, iBuflen, &iRead); + + if (iRetcode) + return iRetcode; + + if (pData->bSuspended) /* input suspension ? */ + pData->iSuspendpoint = 1; + else + { + if (iRead != iBuflen) /* full signature received ? */ + MNG_ERROR (pData, MNG_UNEXPECTEDEOF); + /* is it a valid signature ? */ + if (mng_get_uint32 (pData->pReadbuf) == PNG_SIG) + pData->eSigtype = mng_it_png; + else +#ifdef MNG_INCLUDE_JNG + if (mng_get_uint32 (pData->pReadbuf) == JNG_SIG) + pData->eSigtype = mng_it_jng; + else +#endif + if (mng_get_uint32 (pData->pReadbuf) == MNG_SIG) + pData->eSigtype = mng_it_mng; + else + MNG_ERROR (pData, MNG_INVALIDSIG); + /* all of it ? */ + if (mng_get_uint32 (pData->pReadbuf+4) != POST_SIG) + MNG_ERROR (pData, MNG_INVALIDSIG); + + pData->bHavesig = MNG_TRUE; + } + } + + if (!pData->bSuspended) /* still going ? */ + { + do + { /* reset timer during mng_read() ? */ + if ((pData->bReading) && (!pData->bDisplaying)) + pData->bTimerset = MNG_FALSE; + + if (pData->pFirstpushchunk) /* chunks pushed ? */ + iRetcode = process_pushedchunk (pData); /* process the pushed chunk */ + else + iRetcode = read_chunk (pData); /* read & process a chunk */ + + if (iRetcode) /* on error bail out */ + return iRetcode; + } +#ifdef MNG_SUPPORT_DISPLAY /* until EOF or a break-request */ + while (((!pData->bEOF) || (pData->pCurraniobj)) && + (!pData->bSuspended) && (!pData->bSectionwait) && + ((!pData->bTimerset) || ((pData->bReading) && (!pData->bDisplaying)))); +#else + while ((!pData->bEOF) && (!pData->bSuspended)); +#endif + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_READ_GRAPHIC, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_READ_PROCS */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ + diff --git a/Engine/lib/lmng/libmng_read.h b/Engine/lib/lmng/libmng_read.h new file mode 100644 index 000000000..119cc3e60 --- /dev/null +++ b/Engine/lib/lmng/libmng_read.h @@ -0,0 +1,53 @@ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_read.h copyright (c) 2000-2004 G.Juyn * */ +/* * version : 1.0.8 * */ +/* * * */ +/* * purpose : Read management (definition) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : Definition of the read management routines * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed strict-ANSI stuff * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 0.9.3 - 10/18/2000 - G.Juyn * */ +/* * - added closestream() processing for mng_cleanup() * */ +/* * * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * * */ +/* * 1.0.8 - 04/12/2004 - G.Juyn * */ +/* * - added data-push mechanisms for specialized decoders * */ +/* * * */ +/* ************************************************************************** */ + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +#ifndef _libmng_read_h_ +#define _libmng_read_h_ + +/* ************************************************************************** */ + +mng_retcode mng_process_eof (mng_datap pData); + +mng_retcode mng_release_pushdata (mng_datap pData); + +mng_retcode mng_release_pushchunk (mng_datap pData); + +mng_retcode mng_read_graphic (mng_datap pData); + +/* ************************************************************************** */ + +#endif /* _libmng_read_h_ */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ diff --git a/Engine/lib/lmng/libmng_trace.c b/Engine/lib/lmng/libmng_trace.c new file mode 100644 index 000000000..a6a2cab2b --- /dev/null +++ b/Engine/lib/lmng/libmng_trace.c @@ -0,0 +1,1683 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_trace.c copyright (c) 2000-2007 G.Juyn * */ +/* * version : 1.0.10 * */ +/* * * */ +/* * purpose : Trace functions (implementation) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : implementation of the trace functions * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed strict-ANSI stuff * */ +/* * 0.5.1 - 05/12/2000 - G.Juyn * */ +/* * - added callback error-reporting support * */ +/* * * */ +/* * 0.5.2 - 05/23/2000 - G.Juyn * */ +/* * - added trace telltale reporting * */ +/* * 0.5.2 - 05/24/2000 - G.Juyn * */ +/* * - added tracestrings for global animation color-chunks * */ +/* * - added tracestrings for get/set of default ZLIB/IJG parms * */ +/* * - added tracestrings for global PLTE,tRNS,bKGD * */ +/* * 0.5.2 - 05/30/2000 - G.Juyn * */ +/* * - added tracestrings for image-object promotion * */ +/* * - added tracestrings for delta-image processing * */ +/* * 0.5.2 - 06/02/2000 - G.Juyn * */ +/* * - added tracestrings for getalphaline callback * */ +/* * 0.5.2 - 06/05/2000 - G.Juyn * */ +/* * - added tracestring for RGB8_A8 canvasstyle * */ +/* * 0.5.2 - 06/06/2000 - G.Juyn * */ +/* * - added tracestring for mng_read_resume HLAPI function * */ +/* * * */ +/* * 0.5.3 - 06/21/2000 - G.Juyn * */ +/* * - added tracestrings for get/set speedtype * */ +/* * - added tracestring for get imagelevel * */ +/* * 0.5.3 - 06/22/2000 - G.Juyn * */ +/* * - added tracestring for delta-image processing * */ +/* * - added tracestrings for PPLT chunk processing * */ +/* * * */ +/* * 0.9.1 - 07/07/2000 - G.Juyn * */ +/* * - added tracecodes for special display processing * */ +/* * 0.9.1 - 07/08/2000 - G.Juyn * */ +/* * - added tracestring for get/set suspensionmode * */ +/* * - added tracestrings for get/set display variables * */ +/* * - added tracecode for read_databuffer (I/O-suspension) * */ +/* * 0.9.1 - 07/15/2000 - G.Juyn * */ +/* * - added tracestrings for SAVE/SEEK callbacks * */ +/* * - added tracestrings for get/set sectionbreaks * */ +/* * - added tracestring for special error routine * */ +/* * 0.9.1 - 07/19/2000 - G.Juyn * */ +/* * - added tracestring for updatemngheader * */ +/* * * */ +/* * 0.9.2 - 07/31/2000 - G.Juyn * */ +/* * - added tracestrings for status_xxxxx functions * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * - added tracestring for updatemngsimplicity * */ +/* * * */ +/* * 0.9.3 - 08/26/2000 - G.Juyn * */ +/* * - added MAGN chunk * */ +/* * 0.9.3 - 09/07/2000 - G.Juyn * */ +/* * - added support for new filter_types * */ +/* * 0.9.3 - 10/10/2000 - G.Juyn * */ +/* * - added support for alpha-depth prediction * */ +/* * 0.9.3 - 10/11/2000 - G.Juyn * */ +/* * - added JDAA chunk * */ +/* * - added support for nEED * */ +/* * 0.9.3 - 10/16/2000 - G.Juyn * */ +/* * - added functions to retrieve PNG/JNG specific header-info * */ +/* * - added optional support for bKGD for PNG images * */ +/* * 0.9.3 - 10/17/2000 - G.Juyn * */ +/* * - added callback to process non-critical unknown chunks * */ +/* * - added routine to discard "invalid" objects * */ +/* * 0.9.3 - 10/19/2000 - G.Juyn * */ +/* * - implemented delayed delta-processing * */ +/* * 0.9.3 - 10/20/2000 - G.Juyn * */ +/* * - added get/set for bKGD preference setting * */ +/* * 0.9.3 - 10/21/2000 - G.Juyn * */ +/* * - added get function for interlace/progressive display * */ +/* * * */ +/* * 0.9.4 - 1/18/2001 - G.Juyn * */ +/* * - added "new" MAGN methods 3, 4 & 5 * */ +/* * * */ +/* * 1.0.1 - 02/08/2001 - G.Juyn * */ +/* * - added MEND processing callback * */ +/* * 1.0.1 - 04/21/2001 - G.Juyn (code by G.Kelly) * */ +/* * - added BGRA8 canvas with premultiplied alpha * */ +/* * 1.0.1 - 05/02/2001 - G.Juyn * */ +/* * - added "default" sRGB generation (Thanks Marti!) * */ +/* * * */ +/* * 1.0.2 - 06/23/2001 - G.Juyn * */ +/* * - added optimization option for MNG-video playback * */ +/* * - added processterm callback * */ +/* * 1.0.2 - 06/25/2001 - G.Juyn * */ +/* * - added option to turn off progressive refresh * */ +/* * * */ +/* * 1.0.3 - 08/06/2001 - G.Juyn * */ +/* * - added get function for last processed BACK chunk * */ +/* * * */ +/* * 1.0.5 - 08/15/2002 - G.Juyn * */ +/* * - completed PROM support * */ +/* * - completed delta-image support * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * - added HLAPI function to copy chunks * */ +/* * 1.0.5 - 09/14/2002 - G.Juyn * */ +/* * - added event handling for dynamic MNG * */ +/* * 1.0.5 - 09/20/2002 - G.Juyn * */ +/* * - added support for PAST * */ +/* * 1.0.5 - 09/22/2002 - G.Juyn * */ +/* * - added bgrx8 canvas (filler byte) * */ +/* * 1.0.5 - 09/23/2002 - G.Juyn * */ +/* * - added in-memory color-correction of abstract images * */ +/* * - added compose over/under routines for PAST processing * */ +/* * - added flip & tile routines for PAST processing * */ +/* * 1.0.5 - 10/09/2002 - G.Juyn * */ +/* * - fixed trace-constants for PAST chunk * */ +/* * 1.0.5 - 11/07/2002 - G.Juyn * */ +/* * - added support to get totals after mng_read() * */ +/* * * */ +/* * 1.0.6 - 07/07/2003 - G.R-P * */ +/* * - added conditionals around JNG and Delta-PNG code * */ +/* * 1.0.6 - 07/14/2003 - G.R-P * */ +/* * - added conditionals around various unused functions * */ +/* * 1.0.6 - 07/29/2003 - G.R-P * */ +/* * - added conditionals around PAST chunk support * */ +/* * * */ +/* * 1.0.7 - 11/27/2003 - R.A * */ +/* * - added CANVAS_RGB565 and CANVAS_BGR565 * */ +/* * 1.0.7 - 01/25/2004 - J.S * */ +/* * - added premultiplied alpha canvas' for RGBA, ARGB, ABGR * */ +/* * 1.0.7 - 03/07/2004 - G. Randers-Pehrson * */ +/* * - put gamma, cms-related declarations inside #ifdef * */ +/* * 1.0.7 - 03/10/2004 - G.R-P * */ +/* * - added conditionals around openstream/closestream * */ +/* * * */ +/* * 1.0.8 - 04/02/2004 - G.Juyn * */ +/* * - added CRC existence & checking flags * */ +/* * 1.0.8 - 04/11/2004 - G.Juyn * */ +/* * - added data-push mechanisms for specialized decoders * */ +/* * * */ +/* * 1.0.9 - 10/03/2004 - G.Juyn * */ +/* * - added function to retrieve current FRAM delay * */ +/* * 1.0.9 - 10/14/2004 - G.Juyn * */ +/* * - added bgr565_a8 canvas-style (thanks to J. Elvander) * */ +/* * * */ +/* * 1.0.10 - 04/08/2007 - G.Juyn * */ +/* * - added support for mPNG proposal * */ +/* * 1.0.10 - 07/06/2007 - G.R-P bugfix by Lucas Quintana * */ +/* * * */ +/* ************************************************************************** */ + +#include "libmng.h" +#include "libmng_data.h" +#include "libmng_error.h" +#include "libmng_trace.h" +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_TRACE_PROCS + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_TRACE_STRINGS +MNG_LOCAL mng_trace_entry const trace_table [] = + { + {MNG_FN_INITIALIZE, "initialize"}, + {MNG_FN_RESET, "reset"}, + {MNG_FN_CLEANUP, "cleanup"}, + {MNG_FN_READ, "read"}, + {MNG_FN_WRITE, "write"}, + {MNG_FN_CREATE, "create"}, + {MNG_FN_READDISPLAY, "readdisplay"}, + {MNG_FN_DISPLAY, "display"}, + {MNG_FN_DISPLAY_RESUME, "display_resume"}, + {MNG_FN_DISPLAY_FREEZE, "display_freeze"}, + {MNG_FN_DISPLAY_RESET, "display_reset"}, +#ifndef MNG_NO_DISPLAY_GO_SUPPORTED + {MNG_FN_DISPLAY_GOFRAME, "display_goframe"}, + {MNG_FN_DISPLAY_GOLAYER, "display_golayer"}, + {MNG_FN_DISPLAY_GOTIME, "display_gotime"}, +#endif + {MNG_FN_GETLASTERROR, "getlasterror"}, + {MNG_FN_READ_RESUME, "read_resume"}, + {MNG_FN_TRAPEVENT, "trapevent"}, + {MNG_FN_READ_PUSHDATA, "read_pushdata"}, + {MNG_FN_READ_PUSHSIG, "read_pushsig"}, + {MNG_FN_READ_PUSHCHUNK, "read_pushchunk"}, + + {MNG_FN_SETCB_MEMALLOC, "setcb_memalloc"}, + {MNG_FN_SETCB_MEMFREE, "setcb_memfree"}, + {MNG_FN_SETCB_READDATA, "setcb_readdata"}, + {MNG_FN_SETCB_WRITEDATA, "setcb_writedata"}, + {MNG_FN_SETCB_ERRORPROC, "setcb_errorproc"}, + {MNG_FN_SETCB_TRACEPROC, "setcb_traceproc"}, + {MNG_FN_SETCB_PROCESSHEADER, "setcb_processheader"}, + {MNG_FN_SETCB_PROCESSTEXT, "setcb_processtext"}, + {MNG_FN_SETCB_GETCANVASLINE, "setcb_getcanvasline"}, + {MNG_FN_SETCB_GETBKGDLINE, "setcb_getbkgdline"}, + {MNG_FN_SETCB_REFRESH, "setcb_refresh"}, + {MNG_FN_SETCB_GETTICKCOUNT, "setcb_gettickcount"}, + {MNG_FN_SETCB_SETTIMER, "setcb_settimer"}, + {MNG_FN_SETCB_PROCESSGAMMA, "setcb_processgamma"}, + {MNG_FN_SETCB_PROCESSCHROMA, "setcb_processchroma"}, + {MNG_FN_SETCB_PROCESSSRGB, "setcb_processsrgb"}, + {MNG_FN_SETCB_PROCESSICCP, "setcb_processiccp"}, + {MNG_FN_SETCB_PROCESSAROW, "setcb_processarow"}, +#ifndef MNG_NO_OPEN_CLOSE_STREAM + {MNG_FN_SETCB_OPENSTREAM, "setcb_openstream"}, + {MNG_FN_SETCB_CLOSESTREAM, "setcb_closestream"}, +#endif + {MNG_FN_SETCB_GETALPHALINE, "setcb_getalphaline"}, + {MNG_FN_SETCB_PROCESSSAVE, "setcb_processsave"}, + {MNG_FN_SETCB_PROCESSSEEK, "setcb_processseek"}, + {MNG_FN_SETCB_PROCESSNEED, "setcb_processneed"}, + {MNG_FN_SETCB_PROCESSUNKNOWN, "setcb_processunknown"}, + {MNG_FN_SETCB_PROCESSMEND, "setcb_processmend"}, + {MNG_FN_SETCB_PROCESSTERM, "setcb_processterm"}, + {MNG_FN_SETCB_RELEASEDATA, "setcb_releasedata"}, + + {MNG_FN_GETCB_MEMALLOC, "getcb_memalloc"}, + {MNG_FN_GETCB_MEMFREE, "getcb_memfree"}, + {MNG_FN_GETCB_READDATA, "getcb_readdata,"}, + {MNG_FN_GETCB_WRITEDATA, "getcb_writedata"}, + {MNG_FN_GETCB_ERRORPROC, "getcb_errorproc"}, + {MNG_FN_GETCB_TRACEPROC, "getcb_traceproc"}, + {MNG_FN_GETCB_PROCESSHEADER, "getcb_processheader"}, + {MNG_FN_GETCB_PROCESSTEXT, "getcb_processtext"}, + {MNG_FN_GETCB_GETCANVASLINE, "getcb_getcanvasline"}, + {MNG_FN_GETCB_GETBKGDLINE, "getcb_getbkgdline"}, + {MNG_FN_GETCB_REFRESH, "getcb_refresh"}, + {MNG_FN_GETCB_GETTICKCOUNT, "getcb_gettickcount"}, + {MNG_FN_GETCB_SETTIMER, "getcb_settimer"}, + {MNG_FN_GETCB_PROCESSGAMMA, "getcb_processgamma"}, + {MNG_FN_GETCB_PROCESSCHROMA, "getcb_processchroma"}, + {MNG_FN_GETCB_PROCESSSRGB, "getcb_processsrgb"}, + {MNG_FN_GETCB_PROCESSICCP, "getcb_processiccp"}, + {MNG_FN_GETCB_PROCESSAROW, "getcb_processarow"}, +#ifndef MNG_NO_OPEN_CLOSE_STREAM + {MNG_FN_GETCB_OPENSTREAM, "getcb_openstream"}, + {MNG_FN_GETCB_CLOSESTREAM, "getcb_closestream"}, +#endif + {MNG_FN_GETCB_GETALPHALINE, "getcb_getalphaline"}, + {MNG_FN_GETCB_PROCESSSAVE, "getcb_processsave"}, + {MNG_FN_GETCB_PROCESSSEEK, "getcb_processseek"}, + {MNG_FN_GETCB_PROCESSNEED, "getcb_processneed"}, + {MNG_FN_GETCB_PROCESSUNKNOWN, "getcb_processunknown"}, + {MNG_FN_GETCB_PROCESSMEND, "getcb_processmend"}, + {MNG_FN_GETCB_PROCESSTERM, "getcb_processterm"}, + {MNG_FN_GETCB_RELEASEDATA, "getcb_releasedata"}, + + {MNG_FN_SET_USERDATA, "set_userdata"}, + {MNG_FN_SET_CANVASSTYLE, "set_canvasstyle"}, + {MNG_FN_SET_BKGDSTYLE, "set_bkgdstyle"}, + {MNG_FN_SET_BGCOLOR, "set_bgcolor"}, + {MNG_FN_SET_STORECHUNKS, "set_storechunks"}, +#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS) + {MNG_FN_SET_VIEWGAMMA, "set_viewgamma"}, +#ifndef MNG_NO_DFLT_INFO + {MNG_FN_SET_DISPLAYGAMMA, "set_displaygamma"}, +#endif + {MNG_FN_SET_DFLTIMGGAMMA, "set_dfltimggamma"}, +#endif + {MNG_FN_SET_SRGB, "set_srgb"}, + {MNG_FN_SET_OUTPUTPROFILE, "set_outputprofile"}, + {MNG_FN_SET_SRGBPROFILE, "set_srgbprofile"}, +#ifndef MNG_SKIP_MAXCANVAS + {MNG_FN_SET_MAXCANVASWIDTH, "set_maxcanvaswidth"}, + {MNG_FN_SET_MAXCANVASHEIGHT, "set_maxcanvasheight"}, + {MNG_FN_SET_MAXCANVASSIZE, "set_maxcanvassize"}, +#endif +#ifndef MNG_NO_ACCESS_ZLIB + {MNG_FN_SET_ZLIB_LEVEL, "set_zlib_level"}, + {MNG_FN_SET_ZLIB_METHOD, "set_zlib_method"}, + {MNG_FN_SET_ZLIB_WINDOWBITS, "set_zlib_windowbits"}, + {MNG_FN_SET_ZLIB_MEMLEVEL, "set_zlib_memlevel"}, + {MNG_FN_SET_ZLIB_STRATEGY, "set_zlib_strategy"}, + {MNG_FN_SET_ZLIB_MAXIDAT, "set_zlib_maxidat"}, +#endif +#ifndef MNG_NO_ACCESS_JPEG + {MNG_FN_SET_JPEG_DCTMETHOD, "set_jpeg_dctmethod"}, + {MNG_FN_SET_JPEG_QUALITY, "set_jpeg_quality"}, + {MNG_FN_SET_JPEG_SMOOTHING, "set_jpeg_smoothing"}, + {MNG_FN_SET_JPEG_PROGRESSIVE, "set_jpeg_progressive"}, + {MNG_FN_SET_JPEG_OPTIMIZED, "set_jpeg_optimized"}, + {MNG_FN_SET_JPEG_MAXJDAT, "set_jpeg_maxjdat"}, +#endif + {MNG_FN_SET_SPEED, "set_speed"}, + {MNG_FN_SET_SUSPENSIONMODE, "set_suspensionmode"}, + {MNG_FN_SET_SECTIONBREAKS, "set_sectionbreaks"}, + {MNG_FN_SET_USEBKGD, "set_usebkgd"}, + {MNG_FN_SET_OUTPUTPROFILE2, "set_outputprofile2"}, + {MNG_FN_SET_SRGBPROFILE2, "set_srgbprofile2"}, + {MNG_FN_SET_OUTPUTSRGB, "set_outputsrgb"}, + {MNG_FN_SET_SRGBIMPLICIT, "set_srgbimplicit"}, + {MNG_FN_SET_CACHEPLAYBACK, "set_cacheplayback"}, + {MNG_FN_SET_DOPROGRESSIVE, "set_doprogressive"}, + {MNG_FN_SET_CRCMODE, "set_crcmode"}, + + {MNG_FN_GET_USERDATA, "get_userdata"}, + {MNG_FN_GET_SIGTYPE, "get_sigtype"}, + {MNG_FN_GET_IMAGETYPE, "get_imagetype"}, + {MNG_FN_GET_IMAGEWIDTH, "get_imagewidth"}, + {MNG_FN_GET_IMAGEHEIGHT, "get_imageheight"}, + {MNG_FN_GET_TICKS, "get_ticks"}, + {MNG_FN_GET_FRAMECOUNT, "get_framecount"}, + {MNG_FN_GET_LAYERCOUNT, "get_layercount"}, + {MNG_FN_GET_PLAYTIME, "get_playtime"}, + {MNG_FN_GET_SIMPLICITY, "get_simplicity"}, + {MNG_FN_GET_CANVASSTYLE, "get_canvasstyle"}, + {MNG_FN_GET_BKGDSTYLE, "get_bkgdstyle"}, + {MNG_FN_GET_BGCOLOR, "get_bgcolor"}, + {MNG_FN_GET_STORECHUNKS, "get_storechunks"}, +#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS) + {MNG_FN_GET_VIEWGAMMA, "get_viewgamma"}, + {MNG_FN_GET_DISPLAYGAMMA, "get_displaygamma"}, +#ifndef MNG_NO_DFLT_INFO + {MNG_FN_GET_DFLTIMGGAMMA, "get_dfltimggamma"}, +#endif +#endif + {MNG_FN_GET_SRGB, "get_srgb"}, +#ifndef MNG_SKIP_MAXCANVAS + {MNG_FN_GET_MAXCANVASWIDTH, "get_maxcanvaswidth"}, + {MNG_FN_GET_MAXCANVASHEIGHT, "get_maxcanvasheight"}, +#endif +#ifndef MNG_NO_ACCESS_ZLIB + {MNG_FN_GET_ZLIB_LEVEL, "get_zlib_level"}, + {MNG_FN_GET_ZLIB_METHOD, "get_zlib_method"}, + {MNG_FN_GET_ZLIB_WINDOWBITS, "get_zlib_windowbits"}, + {MNG_FN_GET_ZLIB_MEMLEVEL, "get_zlib_memlevel"}, + {MNG_FN_GET_ZLIB_STRATEGY, "get_zlib_strategy"}, + {MNG_FN_GET_ZLIB_MAXIDAT, "get_zlib_maxidat"}, +#endif +#ifndef MNG_NO_ACCESS_JPEG + {MNG_FN_GET_JPEG_DCTMETHOD, "get_jpeg_dctmethod"}, + {MNG_FN_GET_JPEG_QUALITY, "get_jpeg_quality"}, + {MNG_FN_GET_JPEG_SMOOTHING, "get_jpeg_smoothing"}, + {MNG_FN_GET_JPEG_PROGRESSIVE, "get_jpeg_progressive"}, + {MNG_FN_GET_JPEG_OPTIMIZED, "get_jpeg_optimized"}, + {MNG_FN_GET_JPEG_MAXJDAT, "get_jpeg_maxjdat"}, +#endif + {MNG_FN_GET_SPEED, "get_speed"}, + {MNG_FN_GET_IMAGELEVEL, "get_imagelevel"}, + {MNG_FN_GET_SUSPENSIONMODE, "get_speed"}, + {MNG_FN_GET_STARTTIME, "get_starttime"}, + {MNG_FN_GET_RUNTIME, "get_runtime"}, +#ifndef MNG_NO_CURRENT_INFO + {MNG_FN_GET_CURRENTFRAME, "get_currentframe"}, + {MNG_FN_GET_CURRENTLAYER, "get_currentlayer"}, + {MNG_FN_GET_CURRENTPLAYTIME, "get_currentplaytime"}, +#endif + {MNG_FN_GET_SECTIONBREAKS, "get_sectionbreaks"}, + {MNG_FN_GET_ALPHADEPTH, "get_alphadepth"}, + {MNG_FN_GET_BITDEPTH, "get_bitdepth"}, + {MNG_FN_GET_COLORTYPE, "get_colortype"}, + {MNG_FN_GET_COMPRESSION, "get_compression"}, + {MNG_FN_GET_FILTER, "get_filter"}, + {MNG_FN_GET_INTERLACE, "get_interlace"}, + {MNG_FN_GET_ALPHABITDEPTH, "get_alphabitdepth"}, + {MNG_FN_GET_ALPHACOMPRESSION, "get_alphacompression"}, + {MNG_FN_GET_ALPHAFILTER, "get_alphafilter"}, + {MNG_FN_GET_ALPHAINTERLACE, "get_alphainterlace"}, + {MNG_FN_GET_USEBKGD, "get_usebkgd"}, + {MNG_FN_GET_REFRESHPASS, "get_refreshpass"}, + {MNG_FN_GET_CACHEPLAYBACK, "get_cacheplayback"}, + {MNG_FN_GET_DOPROGRESSIVE, "get_doprogressive"}, + {MNG_FN_GET_LASTBACKCHUNK, "get_lastbackchunk"}, + {MNG_FN_GET_LASTSEEKNAME, "get_lastseekname"}, +#ifndef MNG_NO_CURRENT_INFO + {MNG_FN_GET_TOTALFRAMES, "get_totalframes"}, + {MNG_FN_GET_TOTALLAYERS, "get_totallayers"}, + {MNG_FN_GET_TOTALPLAYTIME, "get_totalplaytime"}, +#endif + {MNG_FN_GET_CRCMODE, "get_crcmode"}, + {MNG_FN_GET_CURRFRAMDELAY, "get_currframdelay"}, + + {MNG_FN_STATUS_ERROR, "status_error"}, + {MNG_FN_STATUS_READING, "status_reading"}, + {MNG_FN_STATUS_SUSPENDBREAK, "status_suspendbreak"}, + {MNG_FN_STATUS_CREATING, "status_creating"}, + {MNG_FN_STATUS_WRITING, "status_writing"}, + {MNG_FN_STATUS_DISPLAYING, "status_displaying"}, + {MNG_FN_STATUS_RUNNING, "status_running"}, + {MNG_FN_STATUS_TIMERBREAK, "status_timerbreak"}, + {MNG_FN_STATUS_DYNAMIC, "status_dynamic"}, + {MNG_FN_STATUS_RUNNINGEVENT, "status_runningevent"}, + + {MNG_FN_ITERATE_CHUNKS, "iterate_chunks"}, + {MNG_FN_COPY_CHUNK, "copy_chunk"}, + + {MNG_FN_GETCHUNK_IHDR, "getchunk_ihdr"}, + {MNG_FN_GETCHUNK_PLTE, "getchunk_plte"}, + {MNG_FN_GETCHUNK_IDAT, "getchunk_idat"}, + {MNG_FN_GETCHUNK_IEND, "getchunk_iend"}, + {MNG_FN_GETCHUNK_TRNS, "getchunk_trns"}, +#ifndef MNG_SKIPCHUNK_gAMA + {MNG_FN_GETCHUNK_GAMA, "getchunk_gama"}, +#endif +#ifndef MNG_SKIPCHUNK_cHRM + {MNG_FN_GETCHUNK_CHRM, "getchunk_chrm"}, +#endif +#ifndef MNG_SKIPCHUNK_sRGB + {MNG_FN_GETCHUNK_SRGB, "getchunk_srgb"}, +#endif +#ifndef MNG_SKIPCHUNK_iCCP + {MNG_FN_GETCHUNK_ICCP, "getchunk_iccp"}, +#endif +#ifndef MNG_SKIPCHUNK_tEXt + {MNG_FN_GETCHUNK_TEXT, "getchunk_text"}, +#endif +#ifndef MNG_SKIPCHUNK_zTXt + {MNG_FN_GETCHUNK_ZTXT, "getchunk_ztxt"}, +#endif +#ifndef MNG_SKIPCHUNK_iTXt + {MNG_FN_GETCHUNK_ITXT, "getchunk_itxt"}, +#endif +#ifndef MNG_SKIPCHUNK_bKGD + {MNG_FN_GETCHUNK_BKGD, "getchunk_bkgd"}, +#endif +#ifndef MNG_SKIPCHUNK_pHYs + {MNG_FN_GETCHUNK_PHYS, "getchunk_phys"}, +#endif +#ifndef MNG_SKIPCHUNK_sBIT + {MNG_FN_GETCHUNK_SBIT, "getchunk_sbit"}, +#endif +#ifndef MNG_SKIPCHUNK_sPLT + {MNG_FN_GETCHUNK_SPLT, "getchunk_splt"}, +#endif +#ifndef MNG_SKIPCHUNK_hIST + {MNG_FN_GETCHUNK_HIST, "getchunk_hist"}, +#endif +#ifndef MNG_SKIPCHUNK_tIME + {MNG_FN_GETCHUNK_TIME, "getchunk_time"}, +#endif + {MNG_FN_GETCHUNK_MHDR, "getchunk_mhdr"}, + {MNG_FN_GETCHUNK_MEND, "getchunk_mend"}, +#ifndef MNG_SKIPCHUNK_LOOP + {MNG_FN_GETCHUNK_LOOP, "getchunk_loop"}, + {MNG_FN_GETCHUNK_ENDL, "getchunk_endl"}, +#endif + {MNG_FN_GETCHUNK_DEFI, "getchunk_defi"}, +#ifndef MNG_SKIPCHUNK_BASI + {MNG_FN_GETCHUNK_BASI, "getchunk_basi"}, +#endif + {MNG_FN_GETCHUNK_CLON, "getchunk_clon"}, +#ifndef MNG_SKIPCHUNK_PAST + {MNG_FN_GETCHUNK_PAST, "getchunk_past"}, +#endif + {MNG_FN_GETCHUNK_DISC, "getchunk_disc"}, + {MNG_FN_GETCHUNK_BACK, "getchunk_back"}, + {MNG_FN_GETCHUNK_FRAM, "getchunk_fram"}, + {MNG_FN_GETCHUNK_MOVE, "getchunk_move"}, + {MNG_FN_GETCHUNK_CLIP, "getchunk_clip"}, + {MNG_FN_GETCHUNK_SHOW, "getchunk_show"}, + {MNG_FN_GETCHUNK_TERM, "getchunk_term"}, +#ifndef MNG_SKIPCHUNK_SAVE + {MNG_FN_GETCHUNK_SAVE, "getchunk_save"}, +#endif +#ifndef MNG_SKIPCHUNK_SEEK + {MNG_FN_GETCHUNK_SEEK, "getchunk_seek"}, +#endif +#ifndef MNG_SKIPCHUNK_eXPI + {MNG_FN_GETCHUNK_EXPI, "getchunk_expi"}, +#endif +#ifndef MNG_SKIPCHUNK_fPRI + {MNG_FN_GETCHUNK_FPRI, "getchunk_fpri"}, +#endif +#ifndef MNG_SKIPCHUNK_nEED + {MNG_FN_GETCHUNK_NEED, "getchunk_need"}, +#endif +#ifndef MNG_SKIPCHUNK_pHYg + {MNG_FN_GETCHUNK_PHYG, "getchunk_phyg"}, +#endif +#ifdef MNG_INCLUDE_JNG + {MNG_FN_GETCHUNK_JHDR, "getchunk_jhdr"}, + {MNG_FN_GETCHUNK_JDAT, "getchunk_jdat"}, + {MNG_FN_GETCHUNK_JSEP, "getchunk_jsep"}, +#endif +#ifndef MNG_NO_DELTA_PNG + {MNG_FN_GETCHUNK_DHDR, "getchunk_dhdr"}, + {MNG_FN_GETCHUNK_PROM, "getchunk_prom"}, + {MNG_FN_GETCHUNK_IPNG, "getchunk_ipng"}, + {MNG_FN_GETCHUNK_PPLT, "getchunk_pplt"}, +#ifdef MNG_INCLUDE_JNG + {MNG_FN_GETCHUNK_IJNG, "getchunk_ijng"}, +#endif +#ifndef MNG_SKIPCHUNK_DROP + {MNG_FN_GETCHUNK_DROP, "getchunk_drop"}, +#endif +#ifndef MNG_SKIPCHUNK_DBYK + {MNG_FN_GETCHUNK_DBYK, "getchunk_dbyk"}, +#endif +#ifndef MNG_SKIPCHUNK_ORDR + {MNG_FN_GETCHUNK_ORDR, "getchunk_ordr"}, +#endif +#endif + {MNG_FN_GETCHUNK_UNKNOWN, "getchunk_unknown"}, + {MNG_FN_GETCHUNK_MAGN, "getchunk_magn"}, +#ifdef MNG_INCLUDE_JNG + {MNG_FN_GETCHUNK_JDAA, "getchunk_jdaa"}, +#endif +#ifndef MNG_SKIPCHUNK_evNT + {MNG_FN_GETCHUNK_EVNT, "getchunk_evnt"}, +#endif +#ifdef MNG_INCLUDE_MPNG_PROPOSAL + {MNG_FN_GETCHUNK_MPNG, "getchunk_mpng"}, +#endif + +#ifndef MNG_SKIPCHUNK_PAST + {MNG_FN_GETCHUNK_PAST_SRC, "getchunk_past_src"}, +#endif +#ifndef MNG_SKIPCHUNK_SAVE + {MNG_FN_GETCHUNK_SAVE_ENTRY, "getchunk_save_entry"}, +#endif +#ifndef MNG_NO_DELTA_PNG + {MNG_FN_GETCHUNK_PPLT_ENTRY, "getchunk_pplt_entry"}, + {MNG_FN_GETCHUNK_ORDR_ENTRY, "getchunk_ordr_entry"}, +#endif +#ifndef MNG_SKIPCHUNK_evNT + {MNG_FN_GETCHUNK_EVNT_ENTRY, "getchunk_evnt_entry"}, +#endif +#ifdef MNG_INCLUDE_MPNG_PROPOSAL + {MNG_FN_GETCHUNK_MPNG_FRAME, "getchunk_mpng_frame"}, +#endif + + {MNG_FN_PUTCHUNK_IHDR, "putchunk_ihdr"}, + {MNG_FN_PUTCHUNK_PLTE, "putchunk_plte"}, + {MNG_FN_PUTCHUNK_IDAT, "putchunk_idat"}, + {MNG_FN_PUTCHUNK_IEND, "putchunk_iend"}, + {MNG_FN_PUTCHUNK_TRNS, "putchunk_trns"}, +#ifndef MNG_SKIPCHUNK_gAMA + {MNG_FN_PUTCHUNK_GAMA, "putchunk_gama"}, +#endif +#ifndef MNG_SKIPCHUNK_cHRM + {MNG_FN_PUTCHUNK_CHRM, "putchunk_chrm"}, +#endif +#ifndef MNG_SKIPCHUNK_sRGB + {MNG_FN_PUTCHUNK_SRGB, "putchunk_srgb"}, +#endif +#ifndef MNG_SKIPCHUNK_iCCP + {MNG_FN_PUTCHUNK_ICCP, "putchunk_iccp"}, +#endif +#ifndef MNG_SKIPCHUNK_tEXt + {MNG_FN_PUTCHUNK_TEXT, "putchunk_text"}, +#endif +#ifndef MNG_SKIPCHUNK_zTXt + {MNG_FN_PUTCHUNK_ZTXT, "putchunk_ztxt"}, +#endif +#ifndef MNG_SKIPCHUNK_iTXt + {MNG_FN_PUTCHUNK_ITXT, "putchunk_itxt"}, +#endif +#ifndef MNG_SKIPCHUNK_bKGD + {MNG_FN_PUTCHUNK_BKGD, "putchunk_bkgd"}, +#endif +#ifndef MNG_SKIPCHUNK_pHYs + {MNG_FN_PUTCHUNK_PHYS, "putchunk_phys"}, +#endif +#ifndef MNG_SKIPCHUNK_sBIT + {MNG_FN_PUTCHUNK_SBIT, "putchunk_sbit"}, +#endif +#ifndef MNG_SKIPCHUNK_sPLT + {MNG_FN_PUTCHUNK_SPLT, "putchunk_splt"}, +#endif +#ifndef MNG_SKIPCHUNK_hIST + {MNG_FN_PUTCHUNK_HIST, "putchunk_hist"}, +#endif +#ifndef MNG_SKIPCHUNK_tIME + {MNG_FN_PUTCHUNK_TIME, "putchunk_time"}, +#endif + {MNG_FN_PUTCHUNK_MHDR, "putchunk_mhdr"}, + {MNG_FN_PUTCHUNK_MEND, "putchunk_mend"}, + {MNG_FN_PUTCHUNK_LOOP, "putchunk_loop"}, + {MNG_FN_PUTCHUNK_ENDL, "putchunk_endl"}, + {MNG_FN_PUTCHUNK_DEFI, "putchunk_defi"}, + {MNG_FN_PUTCHUNK_BASI, "putchunk_basi"}, + {MNG_FN_PUTCHUNK_CLON, "putchunk_clon"}, +#ifndef MNG_SKIPCHUNK_PAST + {MNG_FN_PUTCHUNK_PAST, "putchunk_past"}, +#endif + {MNG_FN_PUTCHUNK_DISC, "putchunk_disc"}, + {MNG_FN_PUTCHUNK_BACK, "putchunk_back"}, + {MNG_FN_PUTCHUNK_FRAM, "putchunk_fram"}, + {MNG_FN_PUTCHUNK_MOVE, "putchunk_move"}, + {MNG_FN_PUTCHUNK_CLIP, "putchunk_clip"}, + {MNG_FN_PUTCHUNK_SHOW, "putchunk_show"}, + {MNG_FN_PUTCHUNK_TERM, "putchunk_term"}, +#ifndef MNG_SKIPCHUNK_SAVE + {MNG_FN_PUTCHUNK_SAVE, "putchunk_save"}, +#endif +#ifndef MNG_SKIPCHUNK_SEEK + {MNG_FN_PUTCHUNK_SEEK, "putchunk_seek"}, +#endif +#ifndef MNG_SKIPCHUNK_eXPI + {MNG_FN_PUTCHUNK_EXPI, "putchunk_expi"}, +#endif +#ifndef MNG_SKIPCHUNK_fPRI + {MNG_FN_PUTCHUNK_FPRI, "putchunk_fpri"}, +#endif +#ifndef MNG_SKIPCHUNK_nEED + {MNG_FN_PUTCHUNK_NEED, "putchunk_need"}, +#endif +#ifndef MNG_SKIPCHUNK_pHYg + {MNG_FN_PUTCHUNK_PHYG, "putchunk_phyg"}, +#endif +#ifdef MNG_INCLUDE_JNG + {MNG_FN_PUTCHUNK_JHDR, "putchunk_jhdr"}, + {MNG_FN_PUTCHUNK_JDAT, "putchunk_jdat"}, + {MNG_FN_PUTCHUNK_JSEP, "putchunk_jsep"}, +#endif +#ifndef MNG_NO_DELTA_PNG + {MNG_FN_PUTCHUNK_DHDR, "putchunk_dhdr"}, + {MNG_FN_PUTCHUNK_PROM, "putchunk_prom"}, +#ifdef MNG_INCLUDE_JNG + {MNG_FN_PUTCHUNK_IPNG, "putchunk_ipng"}, +#endif + {MNG_FN_PUTCHUNK_PPLT, "putchunk_pplt"}, + {MNG_FN_PUTCHUNK_IJNG, "putchunk_ijng"}, +#ifndef MNG_SKIPCHUNK_DROP + {MNG_FN_PUTCHUNK_DROP, "putchunk_drop"}, +#endif +#ifndef MNG_SKIPCHUNK_DBYK + {MNG_FN_PUTCHUNK_DBYK, "putchunk_dbyk"}, +#endif +#ifndef MNG_SKIPCHUNK_ORDR + {MNG_FN_PUTCHUNK_ORDR, "putchunk_ordr"}, +#endif +#endif + {MNG_FN_PUTCHUNK_UNKNOWN, "putchunk_unknown"}, + {MNG_FN_PUTCHUNK_MAGN, "putchunk_magn"}, + {MNG_FN_PUTCHUNK_JDAA, "putchunk_jdaa"}, +#ifndef MNG_SKIPCHUNK_evNT + {MNG_FN_PUTCHUNK_EVNT, "putchunk_evnt"}, +#endif +#ifdef MNG_INCLUDE_MPNG_PROPOSAL + {MNG_FN_PUTCHUNK_MPNG, "putchunk_mpng"}, +#endif + +#ifndef MNG_SKIPCHUNK_PAST + {MNG_FN_PUTCHUNK_PAST_SRC, "putchunk_past_src"}, +#endif +#ifndef MNG_SKIPCHUNK_SAVE + {MNG_FN_PUTCHUNK_SAVE_ENTRY, "putchunk_save_entry"}, +#endif +#ifndef MNG_NO_DELTA_PNG + {MNG_FN_PUTCHUNK_PPLT_ENTRY, "putchunk_pplt_entry"}, +#ifndef MNG_SKIPCHUNK_ORDR + {MNG_FN_PUTCHUNK_ORDR_ENTRY, "putchunk_ordr_entry"}, +#endif +#endif +#ifndef MNG_SKIPCHUNK_evNT + {MNG_FN_PUTCHUNK_EVNT_ENTRY, "putchunk_evnt_entry"}, +#endif +#ifdef MNG_INCLUDE_MPNG_PROPOSAL + {MNG_FN_PUTCHUNK_MPNG_FRAME, "putchunk_mpng_frame"}, +#endif + + {MNG_FN_GETIMGDATA_SEQ, "getimgdata_seq"}, + {MNG_FN_GETIMGDATA_CHUNKSEQ, "getimgdata_chunkseq"}, + {MNG_FN_GETIMGDATA_CHUNK, "getimgdata_chunk"}, + + {MNG_FN_PUTIMGDATA_IHDR, "putimgdata_ihdr"}, +#ifdef MNG_INCLUDE_JNG + {MNG_FN_PUTIMGDATA_JHDR, "putimgdata_jhdr"}, + {MNG_FN_PUTIMGDATA_BASI, "putimgdata_basi"}, + {MNG_FN_PUTIMGDATA_DHDR, "putimgdata_dhdr"}, +#endif + + {MNG_FN_UPDATEMNGHEADER, "updatemngheader"}, + {MNG_FN_UPDATEMNGSIMPLICITY, "updatemngsimplicity"}, + + {MNG_FN_PROCESS_RAW_CHUNK, "process_raw_chunk"}, + {MNG_FN_READ_GRAPHIC, "read_graphic"}, + {MNG_FN_DROP_CHUNKS, "drop_chunks"}, + {MNG_FN_PROCESS_ERROR, "process_error"}, + {MNG_FN_CLEAR_CMS, "clear_cms"}, + {MNG_FN_DROP_OBJECTS, "drop_objects"}, + {MNG_FN_READ_CHUNK, "read_chunk"}, + {MNG_FN_LOAD_BKGDLAYER, "load_bkgdlayer"}, + {MNG_FN_NEXT_FRAME, "next_frame"}, + {MNG_FN_NEXT_LAYER, "next_layer"}, + {MNG_FN_INTERFRAME_DELAY, "interframe_delay"}, + {MNG_FN_DISPLAY_IMAGE, "display_image"}, + {MNG_FN_DROP_IMGOBJECTS, "drop_imgobjects"}, + {MNG_FN_DROP_ANIOBJECTS, "drop_aniobjects"}, + {MNG_FN_INFLATE_BUFFER, "inflate_buffer"}, + {MNG_FN_DEFLATE_BUFFER, "deflate_buffer"}, + {MNG_FN_WRITE_RAW_CHUNK, "write_raw_chunk"}, + {MNG_FN_WRITE_GRAPHIC, "write_graphic"}, + {MNG_FN_SAVE_STATE, "save_state"}, + {MNG_FN_RESTORE_STATE, "restore_state"}, + {MNG_FN_DROP_SAVEDATA, "drop_savedata"}, +#ifndef MNG_NO_DELTA_PNG + {MNG_FN_EXECUTE_DELTA_IMAGE, "execute_delta_image"}, +#endif + {MNG_FN_PROCESS_DISPLAY, "process_display"}, + {MNG_FN_CLEAR_CANVAS, "clear_canvas"}, + {MNG_FN_READ_DATABUFFER, "read_databuffer"}, + {MNG_FN_STORE_ERROR, "store_error"}, + {MNG_FN_DROP_INVALID_OBJECTS, "drop_invalid_objects"}, + {MNG_FN_RELEASE_PUSHDATA, "release_pushdata"}, + {MNG_FN_READ_DATA, "read_data"}, + {MNG_FN_READ_CHUNK_CRC, "read_chunk_crc"}, + {MNG_FN_RELEASE_PUSHCHUNK, "release_pushchunk"}, + + {MNG_FN_DISPLAY_RGB8, "display_rgb8"}, + {MNG_FN_DISPLAY_RGBA8, "display_rgba8"}, + {MNG_FN_DISPLAY_ARGB8, "display_argb8"}, + {MNG_FN_DISPLAY_BGR8, "display_bgr8"}, + {MNG_FN_DISPLAY_BGRA8, "display_bgra8"}, + {MNG_FN_DISPLAY_ABGR8, "display_abgr8"}, + {MNG_FN_DISPLAY_RGB16, "display_rgb16"}, + {MNG_FN_DISPLAY_RGBA16, "display_rgba16"}, + {MNG_FN_DISPLAY_ARGB16, "display_argb16"}, + {MNG_FN_DISPLAY_BGR16, "display_bgr16"}, + {MNG_FN_DISPLAY_BGRA16, "display_bgra16"}, + {MNG_FN_DISPLAY_ABGR16, "display_abgr16"}, + {MNG_FN_DISPLAY_INDEX8, "display_index8"}, + {MNG_FN_DISPLAY_INDEXA8, "display_indexa8"}, + {MNG_FN_DISPLAY_AINDEX8, "display_aindex8"}, + {MNG_FN_DISPLAY_GRAY8, "display_gray8"}, + {MNG_FN_DISPLAY_GRAY16, "display_gray16"}, + {MNG_FN_DISPLAY_GRAYA8, "display_graya8"}, + {MNG_FN_DISPLAY_GRAYA16, "display_graya16"}, + {MNG_FN_DISPLAY_AGRAY8, "display_agray8"}, + {MNG_FN_DISPLAY_AGRAY16, "display_agray16"}, + {MNG_FN_DISPLAY_DX15, "display_dx15"}, + {MNG_FN_DISPLAY_DX16, "display_dx16"}, + {MNG_FN_DISPLAY_RGB8_A8, "display_rgb8_a8"}, + {MNG_FN_DISPLAY_BGRA8PM, "display_bgra8_pm"}, + {MNG_FN_DISPLAY_BGRX8, "display_bgrx8"}, + {MNG_FN_DISPLAY_RGB565, "display_rgb565"}, + {MNG_FN_DISPLAY_RGBA565, "display_rgba565"}, + {MNG_FN_DISPLAY_BGR565, "display_bgr565"}, + {MNG_FN_DISPLAY_BGRA565, "display_bgra565"}, + {MNG_FN_DISPLAY_RGBA8_PM, "display_rgba8_pm"}, + {MNG_FN_DISPLAY_ARGB8_PM, "display_argb8_pm"}, + {MNG_FN_DISPLAY_ABGR8_PM, "display_abgr8_pm"}, + {MNG_FN_DISPLAY_BGR565_A8, "display_bgr565_a8"}, + + {MNG_FN_INIT_FULL_CMS, "init_full_cms"}, + {MNG_FN_CORRECT_FULL_CMS, "correct_full_cms"}, + {MNG_FN_INIT_GAMMA_ONLY, "init_gamma_only"}, + {MNG_FN_CORRECT_GAMMA_ONLY, "correct_gamma_only"}, + {MNG_FN_CORRECT_APP_CMS, "correct_app_cms"}, + {MNG_FN_INIT_FULL_CMS_OBJ, "init_full_cms_obj"}, + {MNG_FN_INIT_GAMMA_ONLY_OBJ, "init_gamma_only_obj"}, + {MNG_FN_INIT_APP_CMS, "init_app_cms"}, + {MNG_FN_INIT_APP_CMS_OBJ, "init_app_cms_obj"}, + + {MNG_FN_PROCESS_G1, "process_g1"}, + {MNG_FN_PROCESS_G2, "process_g2"}, + {MNG_FN_PROCESS_G4, "process_g4"}, + {MNG_FN_PROCESS_G8, "process_g8"}, + {MNG_FN_PROCESS_G16, "process_g16"}, + {MNG_FN_PROCESS_RGB8, "process_rgb8"}, + {MNG_FN_PROCESS_RGB16, "process_rgb16"}, + {MNG_FN_PROCESS_IDX1, "process_idx1"}, + {MNG_FN_PROCESS_IDX2, "process_idx2"}, + {MNG_FN_PROCESS_IDX4, "process_idx4"}, + {MNG_FN_PROCESS_IDX8, "process_idx8"}, + {MNG_FN_PROCESS_GA8, "process_ga8"}, + {MNG_FN_PROCESS_GA16, "process_ga16"}, + {MNG_FN_PROCESS_RGBA8, "process_rgba8"}, + {MNG_FN_PROCESS_RGBA16, "process_rgba16"}, + + {MNG_FN_INIT_G1_I, "init_g1_i"}, + {MNG_FN_INIT_G2_I, "init_g2_i"}, + {MNG_FN_INIT_G4_I, "init_g4_i"}, + {MNG_FN_INIT_G8_I, "init_g8_i"}, + {MNG_FN_INIT_G16_I, "init_g16_i"}, + {MNG_FN_INIT_RGB8_I, "init_rgb8_i"}, + {MNG_FN_INIT_RGB16_I, "init_rgb16_i"}, + {MNG_FN_INIT_IDX1_I, "init_idx1_i"}, + {MNG_FN_INIT_IDX2_I, "init_idx2_i"}, + {MNG_FN_INIT_IDX4_I, "init_idx4_i"}, + {MNG_FN_INIT_IDX8_I, "init_idx8_i"}, + {MNG_FN_INIT_GA8_I, "init_ga8_i"}, + {MNG_FN_INIT_GA16_I, "init_ga16_i"}, + {MNG_FN_INIT_RGBA8_I, "init_rgba8_i"}, + {MNG_FN_INIT_RGBA16_I, "init_rgba16_i"}, +#ifndef MNG_OPTIMIZE_FOOTPRINT_INIT + {MNG_FN_INIT_G1_NI, "init_g1_ni"}, + {MNG_FN_INIT_G2_NI, "init_g2_ni"}, + {MNG_FN_INIT_G4_NI, "init_g4_ni"}, + {MNG_FN_INIT_G8_NI, "init_g8_ni"}, + {MNG_FN_INIT_G16_NI, "init_g16_ni"}, + {MNG_FN_INIT_RGB8_NI, "init_rgb8_ni"}, + {MNG_FN_INIT_RGB16_NI, "init_rgb16_ni"}, + {MNG_FN_INIT_IDX1_NI, "init_idx1_ni"}, + {MNG_FN_INIT_IDX2_NI, "init_idx2_ni"}, + {MNG_FN_INIT_IDX4_NI, "init_idx4_ni"}, + {MNG_FN_INIT_IDX8_NI, "init_idx8_ni"}, + {MNG_FN_INIT_GA8_NI, "init_ga8_ni"}, + {MNG_FN_INIT_GA16_NI, "init_ga16_ni"}, + {MNG_FN_INIT_RGBA8_NI, "init_rgba8_ni"}, + {MNG_FN_INIT_RGBA16_NI, "init_rgba16_ni"}, +#endif + + {MNG_FN_INIT_ROWPROC, "init_rowproc"}, + {MNG_FN_NEXT_ROW, "next_row"}, + {MNG_FN_CLEANUP_ROWPROC, "cleanup_rowproc"}, + + {MNG_FN_FILTER_A_ROW, "filter_a_row"}, + {MNG_FN_FILTER_SUB, "filter_sub"}, + {MNG_FN_FILTER_UP, "filter_up"}, + {MNG_FN_FILTER_AVERAGE, "filter_average"}, + {MNG_FN_FILTER_PAETH, "filter_paeth"}, + + {MNG_FN_INIT_ROWDIFFERING, "init_rowdiffering"}, + {MNG_FN_DIFFER_G1, "differ_g1"}, + {MNG_FN_DIFFER_G2, "differ_g2"}, + {MNG_FN_DIFFER_G4, "differ_g4"}, + {MNG_FN_DIFFER_G8, "differ_g8"}, + {MNG_FN_DIFFER_G16, "differ_g16"}, + {MNG_FN_DIFFER_RGB8, "differ_rgb8"}, + {MNG_FN_DIFFER_RGB16, "differ_rgb16"}, + {MNG_FN_DIFFER_IDX1, "differ_idx1"}, + {MNG_FN_DIFFER_IDX2, "differ_idx2"}, + {MNG_FN_DIFFER_IDX4, "differ_idx4"}, + {MNG_FN_DIFFER_IDX8, "differ_idx8"}, + {MNG_FN_DIFFER_GA8, "differ_ga8"}, + {MNG_FN_DIFFER_GA16, "differ_ga16"}, + {MNG_FN_DIFFER_RGBA8, "differ_rgba8"}, + {MNG_FN_DIFFER_RGBA16, "differ_rgba16"}, + + {MNG_FN_CREATE_IMGDATAOBJECT, "create_imgdataobject"}, + {MNG_FN_FREE_IMGDATAOBJECT, "free_imgdataobject"}, + {MNG_FN_CLONE_IMGDATAOBJECT, "clone_imgdataobject"}, + {MNG_FN_CREATE_IMGOBJECT, "create_imgobject"}, + {MNG_FN_FREE_IMGOBJECT, "free_imgobject"}, + {MNG_FN_FIND_IMGOBJECT, "find_imgobject"}, + {MNG_FN_CLONE_IMGOBJECT, "clone_imgobject"}, + {MNG_FN_RESET_OBJECTDETAILS, "reset_objectdetails"}, + {MNG_FN_RENUM_IMGOBJECT, "renum_imgobject"}, + {MNG_FN_PROMOTE_IMGOBJECT, "promote_imgobject"}, + {MNG_FN_MAGNIFY_IMGOBJECT, "magnify_imgobject"}, + {MNG_FN_COLORCORRECT_OBJECT, "colorcorrect_object"}, + + {MNG_FN_STORE_G1, "store_g1"}, + {MNG_FN_STORE_G2, "store_g2"}, + {MNG_FN_STORE_G4, "store_g4"}, + {MNG_FN_STORE_G8, "store_g8"}, + {MNG_FN_STORE_G16, "store_g16"}, + {MNG_FN_STORE_RGB8, "store_rgb8"}, + {MNG_FN_STORE_RGB16, "store_rgb16"}, + {MNG_FN_STORE_IDX1, "store_idx1"}, + {MNG_FN_STORE_IDX2, "store_idx2"}, + {MNG_FN_STORE_IDX4, "store_idx4"}, + {MNG_FN_STORE_IDX8, "store_idx8"}, + {MNG_FN_STORE_GA8, "store_ga8"}, + {MNG_FN_STORE_GA16, "store_ga16"}, + {MNG_FN_STORE_RGBA8, "store_rgba8"}, + {MNG_FN_STORE_RGBA16, "store_rgba16"}, + + {MNG_FN_RETRIEVE_G8, "retrieve_g8"}, + {MNG_FN_RETRIEVE_G16, "retrieve_g16"}, + {MNG_FN_RETRIEVE_RGB8, "retrieve_rgb8"}, + {MNG_FN_RETRIEVE_RGB16, "retrieve_rgb16"}, + {MNG_FN_RETRIEVE_IDX8, "retrieve_idx8"}, + {MNG_FN_RETRIEVE_GA8, "retrieve_ga8"}, + {MNG_FN_RETRIEVE_GA16, "retrieve_ga16"}, + {MNG_FN_RETRIEVE_RGBA8, "retrieve_rgba8"}, + {MNG_FN_RETRIEVE_RGBA16, "retrieve_rgba16"}, + +#ifndef MNG_NO_DELTA_PNG + {MNG_FN_DELTA_G1, "delta_g1"}, + {MNG_FN_DELTA_G2, "delta_g2"}, + {MNG_FN_DELTA_G4, "delta_g4"}, + {MNG_FN_DELTA_G8, "delta_g8"}, + {MNG_FN_DELTA_G16, "delta_g16"}, + {MNG_FN_DELTA_RGB8, "delta_rgb8"}, + {MNG_FN_DELTA_RGB16, "delta_rgb16"}, + {MNG_FN_DELTA_IDX1, "delta_idx1"}, + {MNG_FN_DELTA_IDX2, "delta_idx2"}, + {MNG_FN_DELTA_IDX4, "delta_idx4"}, + {MNG_FN_DELTA_IDX8, "delta_idx8"}, + {MNG_FN_DELTA_GA8, "delta_ga8"}, + {MNG_FN_DELTA_GA16, "delta_ga16"}, + {MNG_FN_DELTA_RGBA8, "delta_rgba8"}, + {MNG_FN_DELTA_RGBA16, "delta_rgba16"}, +#endif + + {MNG_FN_CREATE_ANI_LOOP, "create_ani_loop"}, + {MNG_FN_CREATE_ANI_ENDL, "create_ani_endl"}, + {MNG_FN_CREATE_ANI_DEFI, "create_ani_defi"}, + {MNG_FN_CREATE_ANI_BASI, "create_ani_basi"}, + {MNG_FN_CREATE_ANI_CLON, "create_ani_clon"}, +#ifndef MNG_SKIPCHUNK_PAST + {MNG_FN_CREATE_ANI_PAST, "create_ani_past"}, +#endif + {MNG_FN_CREATE_ANI_DISC, "create_ani_disc"}, + {MNG_FN_CREATE_ANI_BACK, "create_ani_back"}, + {MNG_FN_CREATE_ANI_FRAM, "create_ani_fram"}, + {MNG_FN_CREATE_ANI_MOVE, "create_ani_move"}, + {MNG_FN_CREATE_ANI_CLIP, "create_ani_clip"}, + {MNG_FN_CREATE_ANI_SHOW, "create_ani_show"}, + {MNG_FN_CREATE_ANI_TERM, "create_ani_term"}, + {MNG_FN_CREATE_ANI_SAVE, "create_ani_save"}, + {MNG_FN_CREATE_ANI_SEEK, "create_ani_seek"}, + {MNG_FN_CREATE_ANI_GAMA, "create_ani_gama"}, + {MNG_FN_CREATE_ANI_CHRM, "create_ani_chrm"}, + {MNG_FN_CREATE_ANI_SRGB, "create_ani_srgb"}, + {MNG_FN_CREATE_ANI_ICCP, "create_ani_iccp"}, + {MNG_FN_CREATE_ANI_PLTE, "create_ani_plte"}, + {MNG_FN_CREATE_ANI_TRNS, "create_ani_trns"}, + {MNG_FN_CREATE_ANI_BKGD, "create_ani_bkgd"}, +#ifndef MNG_NO_DELTA_PNG + {MNG_FN_CREATE_ANI_DHDR, "create_ani_dhdr"}, + {MNG_FN_CREATE_ANI_PROM, "create_ani_prom"}, +#ifdef MNG_INCLUDE_JNG + {MNG_FN_CREATE_ANI_IPNG, "create_ani_ipng"}, +#endif + {MNG_FN_CREATE_ANI_IJNG, "create_ani_ijng"}, + {MNG_FN_CREATE_ANI_PPLT, "create_ani_pplt"}, +#endif + {MNG_FN_CREATE_ANI_MAGN, "create_ani_magn"}, + + {MNG_FN_CREATE_ANI_IMAGE, "create_ani_image"}, + {MNG_FN_CREATE_EVENT, "create_event"}, + + {MNG_FN_FREE_ANI_LOOP, "free_ani_loop"}, + {MNG_FN_FREE_ANI_ENDL, "free_ani_endl"}, + {MNG_FN_FREE_ANI_DEFI, "free_ani_defi"}, + {MNG_FN_FREE_ANI_BASI, "free_ani_basi"}, + {MNG_FN_FREE_ANI_CLON, "free_ani_clon"}, +#ifndef MNG_SKIPCHUNK_PAST + {MNG_FN_FREE_ANI_PAST, "free_ani_past"}, +#endif + {MNG_FN_FREE_ANI_DISC, "free_ani_disc"}, + {MNG_FN_FREE_ANI_BACK, "free_ani_back"}, + {MNG_FN_FREE_ANI_FRAM, "free_ani_fram"}, + {MNG_FN_FREE_ANI_MOVE, "free_ani_move"}, + {MNG_FN_FREE_ANI_CLIP, "free_ani_clip"}, + {MNG_FN_FREE_ANI_SHOW, "free_ani_show"}, + {MNG_FN_FREE_ANI_TERM, "free_ani_term"}, + {MNG_FN_FREE_ANI_SAVE, "free_ani_save"}, + {MNG_FN_FREE_ANI_SEEK, "free_ani_seek"}, + {MNG_FN_FREE_ANI_GAMA, "free_ani_gama"}, + {MNG_FN_FREE_ANI_CHRM, "free_ani_chrm"}, + {MNG_FN_FREE_ANI_SRGB, "free_ani_srgb"}, + {MNG_FN_FREE_ANI_ICCP, "free_ani_iccp"}, + {MNG_FN_FREE_ANI_PLTE, "free_ani_plte"}, + {MNG_FN_FREE_ANI_TRNS, "free_ani_trns"}, + {MNG_FN_FREE_ANI_BKGD, "free_ani_bkgd"}, +#ifndef MNG_NO_DELTA_PNG + {MNG_FN_FREE_ANI_DHDR, "free_ani_dhdr"}, + {MNG_FN_FREE_ANI_PROM, "free_ani_prom"}, +#ifdef MNG_INCLUDE_JNG + {MNG_FN_FREE_ANI_IPNG, "free_ani_ipng"}, +#endif + {MNG_FN_FREE_ANI_IJNG, "free_ani_ijng"}, + {MNG_FN_FREE_ANI_PPLT, "free_ani_pplt"}, +#endif + {MNG_FN_FREE_ANI_MAGN, "free_ani_magn"}, + + {MNG_FN_FREE_ANI_IMAGE, "free_ani_image"}, + {MNG_FN_FREE_EVENT, "free_event"}, + + {MNG_FN_PROCESS_ANI_LOOP, "process_ani_loop"}, + {MNG_FN_PROCESS_ANI_ENDL, "process_ani_endl"}, + {MNG_FN_PROCESS_ANI_DEFI, "process_ani_defi"}, + {MNG_FN_PROCESS_ANI_BASI, "process_ani_basi"}, + {MNG_FN_PROCESS_ANI_CLON, "process_ani_clon"}, +#ifndef MNG_SKIPCHUNK_PAST + {MNG_FN_PROCESS_ANI_PAST, "process_ani_past"}, +#endif + {MNG_FN_PROCESS_ANI_DISC, "process_ani_disc"}, + {MNG_FN_PROCESS_ANI_BACK, "process_ani_back"}, + {MNG_FN_PROCESS_ANI_FRAM, "process_ani_fram"}, + {MNG_FN_PROCESS_ANI_MOVE, "process_ani_move"}, + {MNG_FN_PROCESS_ANI_CLIP, "process_ani_clip"}, + {MNG_FN_PROCESS_ANI_SHOW, "process_ani_show"}, + {MNG_FN_PROCESS_ANI_TERM, "process_ani_term"}, + {MNG_FN_PROCESS_ANI_SAVE, "process_ani_save"}, + {MNG_FN_PROCESS_ANI_SEEK, "process_ani_seek"}, + {MNG_FN_PROCESS_ANI_GAMA, "process_ani_gama"}, + {MNG_FN_PROCESS_ANI_CHRM, "process_ani_chrm"}, + {MNG_FN_PROCESS_ANI_SRGB, "process_ani_srgb"}, + {MNG_FN_PROCESS_ANI_ICCP, "process_ani_iccp"}, + {MNG_FN_PROCESS_ANI_PLTE, "process_ani_plte"}, + {MNG_FN_PROCESS_ANI_TRNS, "process_ani_trns"}, + {MNG_FN_PROCESS_ANI_BKGD, "process_ani_bkgd"}, +#ifndef MNG_NO_DELTA_PNG + {MNG_FN_PROCESS_ANI_DHDR, "process_ani_dhdr"}, + {MNG_FN_PROCESS_ANI_PROM, "process_ani_prom"}, +#ifdef MNG_INCLUDE_JNG + {MNG_FN_PROCESS_ANI_IPNG, "process_ani_ipng"}, +#endif + {MNG_FN_PROCESS_ANI_IJNG, "process_ani_ijng"}, + {MNG_FN_PROCESS_ANI_PPLT, "process_ani_pplt"}, +#endif + {MNG_FN_PROCESS_ANI_MAGN, "process_ani_magn"}, + + {MNG_FN_PROCESS_ANI_IMAGE, "process_ani_image"}, + {MNG_FN_PROCESS_EVENT, "process_event"}, + + {MNG_FN_RESTORE_BACKIMAGE, "restore_backimage"}, + {MNG_FN_RESTORE_BACKCOLOR, "restore_backcolor"}, + {MNG_FN_RESTORE_BGCOLOR, "restore_bgcolor"}, + {MNG_FN_RESTORE_RGB8, "restore_rgb8"}, + {MNG_FN_RESTORE_BGR8, "restore_bgr8"}, + {MNG_FN_RESTORE_BKGD, "restore_bkgd"}, + {MNG_FN_RESTORE_BGRX8, "restore_bgrx8"}, + {MNG_FN_RESTORE_RGB565, "restore_rgb565"}, + + {MNG_FN_INIT_IHDR, "init_ihdr"}, + {MNG_FN_INIT_PLTE, "init_plte"}, + {MNG_FN_INIT_IDAT, "init_idat"}, + {MNG_FN_INIT_IEND, "init_iend"}, + {MNG_FN_INIT_TRNS, "init_trns"}, + {MNG_FN_INIT_GAMA, "init_gama"}, + {MNG_FN_INIT_CHRM, "init_chrm"}, + {MNG_FN_INIT_SRGB, "init_srgb"}, + {MNG_FN_INIT_ICCP, "init_iccp"}, + {MNG_FN_INIT_TEXT, "init_text"}, + {MNG_FN_INIT_ZTXT, "init_ztxt"}, + {MNG_FN_INIT_ITXT, "init_itxt"}, + {MNG_FN_INIT_BKGD, "init_bkgd"}, + {MNG_FN_INIT_PHYS, "init_phys"}, + {MNG_FN_INIT_SBIT, "init_sbit"}, + {MNG_FN_INIT_SPLT, "init_splt"}, + {MNG_FN_INIT_HIST, "init_hist"}, + {MNG_FN_INIT_TIME, "init_time"}, + {MNG_FN_INIT_MHDR, "init_mhdr"}, + {MNG_FN_INIT_MEND, "init_mend"}, + {MNG_FN_INIT_LOOP, "init_loop"}, + {MNG_FN_INIT_ENDL, "init_endl"}, + {MNG_FN_INIT_DEFI, "init_defi"}, + {MNG_FN_INIT_BASI, "init_basi"}, + {MNG_FN_INIT_CLON, "init_clon"}, +#ifndef MNG_SKIPCHUNK_PAST + {MNG_FN_INIT_PAST, "init_past"}, +#endif + {MNG_FN_INIT_DISC, "init_disc"}, + {MNG_FN_INIT_BACK, "init_back"}, + {MNG_FN_INIT_FRAM, "init_fram"}, + {MNG_FN_INIT_MOVE, "init_move"}, + {MNG_FN_INIT_CLIP, "init_clip"}, + {MNG_FN_INIT_SHOW, "init_show"}, + {MNG_FN_INIT_TERM, "init_term"}, + {MNG_FN_INIT_SAVE, "init_save"}, + {MNG_FN_INIT_SEEK, "init_seek"}, + {MNG_FN_INIT_EXPI, "init_expi"}, + {MNG_FN_INIT_FPRI, "init_fpri"}, + {MNG_FN_INIT_NEED, "init_need"}, + {MNG_FN_INIT_PHYG, "init_phyg"}, +#ifndef MNG_NO_DELTA_PNG + {MNG_FN_INIT_JHDR, "init_jhdr"}, + {MNG_FN_INIT_JDAT, "init_jdat"}, + {MNG_FN_INIT_JSEP, "init_jsep"}, +#endif +#ifndef MNG_NO_DELTA_PNG + {MNG_FN_INIT_DHDR, "init_dhdr"}, + {MNG_FN_INIT_PROM, "init_prom"}, +#ifdef MNG_INCLUDE_JNG + {MNG_FN_INIT_IPNG, "init_ipng"}, +#endif + {MNG_FN_INIT_PPLT, "init_pplt"}, + {MNG_FN_INIT_IJNG, "init_ijng"}, + {MNG_FN_INIT_DROP, "init_drop"}, + {MNG_FN_INIT_DBYK, "init_dbyk"}, + {MNG_FN_INIT_ORDR, "init_ordr"}, +#endif + {MNG_FN_INIT_UNKNOWN, "init_unknown"}, + {MNG_FN_INIT_MAGN, "init_magn"}, + {MNG_FN_INIT_JDAA, "init_jdaa"}, + {MNG_FN_INIT_EVNT, "init_evnt"}, +#ifdef MNG_INCLUDE_MPNG_PROPOSAL + {MNG_FN_INIT_MPNG, "init_mpng"}, +#endif + + {MNG_FN_ASSIGN_IHDR, "assign_ihdr"}, + {MNG_FN_ASSIGN_PLTE, "assign_plte"}, + {MNG_FN_ASSIGN_IDAT, "assign_idat"}, + {MNG_FN_ASSIGN_IEND, "assign_iend"}, + {MNG_FN_ASSIGN_TRNS, "assign_trns"}, + {MNG_FN_ASSIGN_GAMA, "assign_gama"}, + {MNG_FN_ASSIGN_CHRM, "assign_chrm"}, + {MNG_FN_ASSIGN_SRGB, "assign_srgb"}, + {MNG_FN_ASSIGN_ICCP, "assign_iccp"}, + {MNG_FN_ASSIGN_TEXT, "assign_text"}, + {MNG_FN_ASSIGN_ZTXT, "assign_ztxt"}, + {MNG_FN_ASSIGN_ITXT, "assign_itxt"}, + {MNG_FN_ASSIGN_BKGD, "assign_bkgd"}, + {MNG_FN_ASSIGN_PHYS, "assign_phys"}, + {MNG_FN_ASSIGN_SBIT, "assign_sbit"}, + {MNG_FN_ASSIGN_SPLT, "assign_splt"}, + {MNG_FN_ASSIGN_HIST, "assign_hist"}, + {MNG_FN_ASSIGN_TIME, "assign_time"}, + {MNG_FN_ASSIGN_MHDR, "assign_mhdr"}, + {MNG_FN_ASSIGN_MEND, "assign_mend"}, + {MNG_FN_ASSIGN_LOOP, "assign_loop"}, + {MNG_FN_ASSIGN_ENDL, "assign_endl"}, + {MNG_FN_ASSIGN_DEFI, "assign_defi"}, + {MNG_FN_ASSIGN_BASI, "assign_basi"}, + {MNG_FN_ASSIGN_CLON, "assign_clon"}, +#ifndef MNG_SKIPCHUNK_PAST + {MNG_FN_ASSIGN_PAST, "assign_past"}, +#endif + {MNG_FN_ASSIGN_DISC, "assign_disc"}, + {MNG_FN_ASSIGN_BACK, "assign_back"}, + {MNG_FN_ASSIGN_FRAM, "assign_fram"}, + {MNG_FN_ASSIGN_MOVE, "assign_move"}, + {MNG_FN_ASSIGN_CLIP, "assign_clip"}, + {MNG_FN_ASSIGN_SHOW, "assign_show"}, + {MNG_FN_ASSIGN_TERM, "assign_term"}, + {MNG_FN_ASSIGN_SAVE, "assign_save"}, + {MNG_FN_ASSIGN_SEEK, "assign_seek"}, + {MNG_FN_ASSIGN_EXPI, "assign_expi"}, + {MNG_FN_ASSIGN_FPRI, "assign_fpri"}, + {MNG_FN_ASSIGN_NEED, "assign_need"}, + {MNG_FN_ASSIGN_PHYG, "assign_phyg"}, +#ifdef MNG_INCLUDE_JNG + {MNG_FN_ASSIGN_JHDR, "assign_jhdr"}, + {MNG_FN_ASSIGN_JDAT, "assign_jdat"}, + {MNG_FN_ASSIGN_JSEP, "assign_jsep"}, +#endif +#ifndef MNG_NO_DELTA_PNG + {MNG_FN_ASSIGN_DHDR, "assign_dhdr"}, + {MNG_FN_ASSIGN_PROM, "assign_prom"}, +#ifdef MNG_INCLUDE_JNG + {MNG_FN_ASSIGN_IPNG, "assign_ipng"}, +#endif + {MNG_FN_ASSIGN_PPLT, "assign_pplt"}, + {MNG_FN_ASSIGN_IJNG, "assign_ijng"}, + {MNG_FN_ASSIGN_DROP, "assign_drop"}, + {MNG_FN_ASSIGN_DBYK, "assign_dbyk"}, + {MNG_FN_ASSIGN_ORDR, "assign_ordr"}, +#endif + {MNG_FN_ASSIGN_UNKNOWN, "assign_unknown"}, + {MNG_FN_ASSIGN_MAGN, "assign_magn"}, + {MNG_FN_ASSIGN_JDAA, "assign_jdaa"}, + {MNG_FN_ASSIGN_EVNT, "assign_evnt"}, +#ifdef MNG_INCLUDE_MPNG_PROPOSAL + {MNG_FN_ASSIGN_MPNG, "assign_mpng"}, +#endif + + {MNG_FN_FREE_IHDR, "free_ihdr"}, + {MNG_FN_FREE_PLTE, "free_plte"}, + {MNG_FN_FREE_IDAT, "free_idat"}, + {MNG_FN_FREE_IEND, "free_iend"}, + {MNG_FN_FREE_TRNS, "free_trns"}, + {MNG_FN_FREE_GAMA, "free_gama"}, + {MNG_FN_FREE_CHRM, "free_chrm"}, + {MNG_FN_FREE_SRGB, "free_srgb"}, + {MNG_FN_FREE_ICCP, "free_iccp"}, + {MNG_FN_FREE_TEXT, "free_text"}, + {MNG_FN_FREE_ZTXT, "free_ztxt"}, + {MNG_FN_FREE_ITXT, "free_itxt"}, + {MNG_FN_FREE_BKGD, "free_bkgd"}, + {MNG_FN_FREE_PHYS, "free_phys"}, + {MNG_FN_FREE_SBIT, "free_sbit"}, + {MNG_FN_FREE_SPLT, "free_splt"}, + {MNG_FN_FREE_HIST, "free_hist"}, + {MNG_FN_FREE_TIME, "free_time"}, + {MNG_FN_FREE_MHDR, "free_mhdr"}, + {MNG_FN_FREE_MEND, "free_mend"}, + {MNG_FN_FREE_LOOP, "free_loop"}, + {MNG_FN_FREE_ENDL, "free_endl"}, + {MNG_FN_FREE_DEFI, "free_defi"}, + {MNG_FN_FREE_BASI, "free_basi"}, + {MNG_FN_FREE_CLON, "free_clon"}, +#ifndef MNG_SKIPCHUNK_PAST + {MNG_FN_FREE_PAST, "free_past"}, +#endif + {MNG_FN_FREE_DISC, "free_disc"}, + {MNG_FN_FREE_BACK, "free_back"}, + {MNG_FN_FREE_FRAM, "free_fram"}, + {MNG_FN_FREE_MOVE, "free_move"}, + {MNG_FN_FREE_CLIP, "free_clip"}, + {MNG_FN_FREE_SHOW, "free_show"}, + {MNG_FN_FREE_TERM, "free_term"}, + {MNG_FN_FREE_SAVE, "free_save"}, + {MNG_FN_FREE_SEEK, "free_seek"}, + {MNG_FN_FREE_EXPI, "free_expi"}, + {MNG_FN_FREE_FPRI, "free_fpri"}, + {MNG_FN_FREE_NEED, "free_need"}, + {MNG_FN_FREE_PHYG, "free_phyg"}, +#ifdef MNG_INCLUDE_JNG + {MNG_FN_FREE_JHDR, "free_jhdr"}, + {MNG_FN_FREE_JDAT, "free_jdat"}, + {MNG_FN_FREE_JSEP, "free_jsep"}, +#endif +#ifndef MNG_NO_DELTA_PNG + {MNG_FN_FREE_DHDR, "free_dhdr"}, + {MNG_FN_FREE_PROM, "free_prom"}, +#ifdef MNG_INCLUDE_JNG + {MNG_FN_FREE_IPNG, "free_ipng"}, +#endif + {MNG_FN_FREE_PPLT, "free_pplt"}, + {MNG_FN_FREE_IJNG, "free_ijng"}, + {MNG_FN_FREE_DROP, "free_drop"}, + {MNG_FN_FREE_DBYK, "free_dbyk"}, + {MNG_FN_FREE_ORDR, "free_ordr"}, +#endif + {MNG_FN_FREE_UNKNOWN, "free_unknown"}, + {MNG_FN_FREE_MAGN, "free_magn"}, + {MNG_FN_FREE_JDAA, "free_jdaa"}, + {MNG_FN_FREE_EVNT, "free_evnt"}, +#ifdef MNG_INCLUDE_MPNG_PROPOSAL + {MNG_FN_FREE_MPNG, "free_mpng"}, +#endif + + {MNG_FN_READ_IHDR, "read_ihdr"}, + {MNG_FN_READ_PLTE, "read_plte"}, + {MNG_FN_READ_IDAT, "read_idat"}, + {MNG_FN_READ_IEND, "read_iend"}, + {MNG_FN_READ_TRNS, "read_trns"}, + {MNG_FN_READ_GAMA, "read_gama"}, + {MNG_FN_READ_CHRM, "read_chrm"}, + {MNG_FN_READ_SRGB, "read_srgb"}, + {MNG_FN_READ_ICCP, "read_iccp"}, + {MNG_FN_READ_TEXT, "read_text"}, + {MNG_FN_READ_ZTXT, "read_ztxt"}, + {MNG_FN_READ_ITXT, "read_itxt"}, + {MNG_FN_READ_BKGD, "read_bkgd"}, + {MNG_FN_READ_PHYS, "read_phys"}, + {MNG_FN_READ_SBIT, "read_sbit"}, + {MNG_FN_READ_SPLT, "read_splt"}, + {MNG_FN_READ_HIST, "read_hist"}, + {MNG_FN_READ_TIME, "read_time"}, + {MNG_FN_READ_MHDR, "read_mhdr"}, + {MNG_FN_READ_MEND, "read_mend"}, + {MNG_FN_READ_LOOP, "read_loop"}, + {MNG_FN_READ_ENDL, "read_endl"}, + {MNG_FN_READ_DEFI, "read_defi"}, + {MNG_FN_READ_BASI, "read_basi"}, + {MNG_FN_READ_CLON, "read_clon"}, +#ifndef MNG_SKIPCHUNK_PAST + {MNG_FN_READ_PAST, "read_past"}, +#endif + {MNG_FN_READ_DISC, "read_disc"}, + {MNG_FN_READ_BACK, "read_back"}, + {MNG_FN_READ_FRAM, "read_fram"}, + {MNG_FN_READ_MOVE, "read_move"}, + {MNG_FN_READ_CLIP, "read_clip"}, + {MNG_FN_READ_SHOW, "read_show"}, + {MNG_FN_READ_TERM, "read_term"}, + {MNG_FN_READ_SAVE, "read_save"}, + {MNG_FN_READ_SEEK, "read_seek"}, + {MNG_FN_READ_EXPI, "read_expi"}, + {MNG_FN_READ_FPRI, "read_fpri"}, + {MNG_FN_READ_NEED, "read_need"}, + {MNG_FN_READ_PHYG, "read_phyg"}, +#ifdef MNG_INCLUDE_JNG + {MNG_FN_READ_JHDR, "read_jhdr"}, + {MNG_FN_READ_JDAT, "read_jdat"}, + {MNG_FN_READ_JSEP, "read_jsep"}, +#endif +#ifndef MNG_NO_DELTA_PNG + {MNG_FN_READ_DHDR, "read_dhdr"}, + {MNG_FN_READ_PROM, "read_prom"}, + {MNG_FN_READ_IPNG, "read_ipng"}, + {MNG_FN_READ_PPLT, "read_pplt"}, +#ifdef MNG_INCLUDE_JNG + {MNG_FN_READ_IJNG, "read_ijng"}, +#endif + {MNG_FN_READ_DROP, "read_drop"}, + {MNG_FN_READ_DBYK, "read_dbyk"}, + {MNG_FN_READ_ORDR, "read_ordr"}, +#endif + {MNG_FN_READ_UNKNOWN, "read_unknown"}, + {MNG_FN_READ_MAGN, "read_magn"}, + {MNG_FN_READ_JDAA, "read_jdaa"}, + {MNG_FN_READ_EVNT, "read_evnt"}, +#ifdef MNG_INCLUDE_MPNG_PROPOSAL + {MNG_FN_READ_MPNG, "read_mpng"}, +#endif + + {MNG_FN_WRITE_IHDR, "write_ihdr"}, + {MNG_FN_WRITE_PLTE, "write_plte"}, + {MNG_FN_WRITE_IDAT, "write_idat"}, + {MNG_FN_WRITE_IEND, "write_iend"}, + {MNG_FN_WRITE_TRNS, "write_trns"}, + {MNG_FN_WRITE_GAMA, "write_gama"}, + {MNG_FN_WRITE_CHRM, "write_chrm"}, + {MNG_FN_WRITE_SRGB, "write_srgb"}, + {MNG_FN_WRITE_ICCP, "write_iccp"}, + {MNG_FN_WRITE_TEXT, "write_text"}, + {MNG_FN_WRITE_ZTXT, "write_ztxt"}, + {MNG_FN_WRITE_ITXT, "write_itxt"}, + {MNG_FN_WRITE_BKGD, "write_bkgd"}, + {MNG_FN_WRITE_PHYS, "write_phys"}, + {MNG_FN_WRITE_SBIT, "write_sbit"}, + {MNG_FN_WRITE_SPLT, "write_splt"}, + {MNG_FN_WRITE_HIST, "write_hist"}, + {MNG_FN_WRITE_TIME, "write_time"}, + {MNG_FN_WRITE_MHDR, "write_mhdr"}, + {MNG_FN_WRITE_MEND, "write_mend"}, + {MNG_FN_WRITE_LOOP, "write_loop"}, + {MNG_FN_WRITE_ENDL, "write_endl"}, + {MNG_FN_WRITE_DEFI, "write_defi"}, + {MNG_FN_WRITE_BASI, "write_basi"}, + {MNG_FN_WRITE_CLON, "write_clon"}, +#ifndef MNG_SKIPCHUNK_PAST + {MNG_FN_WRITE_PAST, "write_past"}, +#endif + {MNG_FN_WRITE_DISC, "write_disc"}, + {MNG_FN_WRITE_BACK, "write_back"}, + {MNG_FN_WRITE_FRAM, "write_fram"}, + {MNG_FN_WRITE_MOVE, "write_move"}, + {MNG_FN_WRITE_CLIP, "write_clip"}, + {MNG_FN_WRITE_SHOW, "write_show"}, + {MNG_FN_WRITE_TERM, "write_term"}, + {MNG_FN_WRITE_SAVE, "write_save"}, + {MNG_FN_WRITE_SEEK, "write_seek"}, + {MNG_FN_WRITE_EXPI, "write_expi"}, + {MNG_FN_WRITE_FPRI, "write_fpri"}, + {MNG_FN_WRITE_NEED, "write_need"}, + {MNG_FN_WRITE_PHYG, "write_phyg"}, +#ifdef MNG_INCLUDE_JNG + {MNG_FN_WRITE_JHDR, "write_jhdr"}, + {MNG_FN_WRITE_JDAT, "write_jdat"}, + {MNG_FN_WRITE_JSEP, "write_jsep"}, +#endif +#ifndef MNG_NO_DELTA_PNG + {MNG_FN_WRITE_DHDR, "write_dhdr"}, + {MNG_FN_WRITE_PROM, "write_prom"}, +#ifdef MNG_INCLUDE_JNG + {MNG_FN_WRITE_IPNG, "write_ipng"}, +#endif + {MNG_FN_WRITE_PPLT, "write_pplt"}, + {MNG_FN_WRITE_IJNG, "write_ijng"}, + {MNG_FN_WRITE_DROP, "write_drop"}, + {MNG_FN_WRITE_DBYK, "write_dbyk"}, + {MNG_FN_WRITE_ORDR, "write_ordr"}, +#endif + {MNG_FN_WRITE_UNKNOWN, "write_unknown"}, + {MNG_FN_WRITE_MAGN, "write_magn"}, + {MNG_FN_WRITE_JDAA, "write_jdaa"}, + {MNG_FN_WRITE_EVNT, "write_evnt"}, +#ifdef MNG_INCLUDE_MPNG_PROPOSAL + {MNG_FN_WRITE_MPNG, "write_mpng"}, +#endif + + {MNG_FN_ZLIB_INITIALIZE, "zlib_initialize"}, + {MNG_FN_ZLIB_CLEANUP, "zlib_cleanup"}, + {MNG_FN_ZLIB_INFLATEINIT, "zlib_inflateinit"}, + {MNG_FN_ZLIB_INFLATEROWS, "zlib_inflaterows"}, + {MNG_FN_ZLIB_INFLATEDATA, "zlib_inflatedata"}, + {MNG_FN_ZLIB_INFLATEFREE, "zlib_inflatefree"}, + {MNG_FN_ZLIB_DEFLATEINIT, "zlib_deflateinit"}, + {MNG_FN_ZLIB_DEFLATEROWS, "zlib_deflaterows"}, + {MNG_FN_ZLIB_DEFLATEDATA, "zlib_deflatedata"}, + {MNG_FN_ZLIB_DEFLATEFREE, "zlib_deflatefree"}, + + {MNG_FN_PROCESS_DISPLAY_IHDR, "process_display_ihdr"}, + {MNG_FN_PROCESS_DISPLAY_PLTE, "process_display_plte"}, + {MNG_FN_PROCESS_DISPLAY_IDAT, "process_display_idat"}, + {MNG_FN_PROCESS_DISPLAY_IEND, "process_display_iend"}, + {MNG_FN_PROCESS_DISPLAY_TRNS, "process_display_trns"}, + {MNG_FN_PROCESS_DISPLAY_GAMA, "process_display_gama"}, + {MNG_FN_PROCESS_DISPLAY_CHRM, "process_display_chrm"}, + {MNG_FN_PROCESS_DISPLAY_SRGB, "process_display_srgb"}, + {MNG_FN_PROCESS_DISPLAY_ICCP, "process_display_iccp"}, + {MNG_FN_PROCESS_DISPLAY_BKGD, "process_display_bkgd"}, + {MNG_FN_PROCESS_DISPLAY_PHYS, "process_display_phys"}, + {MNG_FN_PROCESS_DISPLAY_SBIT, "process_display_sbit"}, + {MNG_FN_PROCESS_DISPLAY_SPLT, "process_display_splt"}, + {MNG_FN_PROCESS_DISPLAY_HIST, "process_display_hist"}, + {MNG_FN_PROCESS_DISPLAY_MHDR, "process_display_mhdr"}, + {MNG_FN_PROCESS_DISPLAY_MEND, "process_display_mend"}, + {MNG_FN_PROCESS_DISPLAY_LOOP, "process_display_loop"}, + {MNG_FN_PROCESS_DISPLAY_ENDL, "process_display_endl"}, + {MNG_FN_PROCESS_DISPLAY_DEFI, "process_display_defi"}, + {MNG_FN_PROCESS_DISPLAY_BASI, "process_display_basi"}, + {MNG_FN_PROCESS_DISPLAY_CLON, "process_display_clon"}, +#ifndef MNG_SKIPCHUNK_PAST + {MNG_FN_PROCESS_DISPLAY_PAST, "process_display_past"}, +#endif + {MNG_FN_PROCESS_DISPLAY_DISC, "process_display_disc"}, + {MNG_FN_PROCESS_DISPLAY_BACK, "process_display_back"}, + {MNG_FN_PROCESS_DISPLAY_FRAM, "process_display_fram"}, + {MNG_FN_PROCESS_DISPLAY_MOVE, "process_display_move"}, + {MNG_FN_PROCESS_DISPLAY_CLIP, "process_display_clip"}, + {MNG_FN_PROCESS_DISPLAY_SHOW, "process_display_show"}, + {MNG_FN_PROCESS_DISPLAY_TERM, "process_display_term"}, + {MNG_FN_PROCESS_DISPLAY_SAVE, "process_display_save"}, + {MNG_FN_PROCESS_DISPLAY_SEEK, "process_display_seek"}, + {MNG_FN_PROCESS_DISPLAY_EXPI, "process_display_expi"}, + {MNG_FN_PROCESS_DISPLAY_FPRI, "process_display_fpri"}, + {MNG_FN_PROCESS_DISPLAY_NEED, "process_display_need"}, + {MNG_FN_PROCESS_DISPLAY_PHYG, "process_display_phyg"}, +#ifdef MNG_INCLUDE_JNG + {MNG_FN_PROCESS_DISPLAY_JHDR, "process_display_jhdr"}, + {MNG_FN_PROCESS_DISPLAY_JDAT, "process_display_jdat"}, + {MNG_FN_PROCESS_DISPLAY_JSEP, "process_display_jsep"}, +#endif +#ifndef MNG_NO_DELTA_PNG + {MNG_FN_PROCESS_DISPLAY_DHDR, "process_display_dhdr"}, + {MNG_FN_PROCESS_DISPLAY_PROM, "process_display_prom"}, +#ifdef MNG_INCLUDE_JNG + {MNG_FN_PROCESS_DISPLAY_IPNG, "process_display_ipng"}, +#endif + {MNG_FN_PROCESS_DISPLAY_PPLT, "process_display_pplt"}, + {MNG_FN_PROCESS_DISPLAY_IJNG, "process_display_ijng"}, + {MNG_FN_PROCESS_DISPLAY_DROP, "process_display_drop"}, + {MNG_FN_PROCESS_DISPLAY_DBYK, "process_display_dbyk"}, + {MNG_FN_PROCESS_DISPLAY_ORDR, "process_display_ordr"}, +#endif + {MNG_FN_PROCESS_DISPLAY_MAGN, "process_display_magn"}, + {MNG_FN_PROCESS_DISPLAY_JDAA, "process_display_jdaa"}, + + {MNG_FN_JPEG_INITIALIZE, "jpeg_initialize"}, + {MNG_FN_JPEG_CLEANUP, "jpeg_cleanup"}, + {MNG_FN_JPEG_DECOMPRESSINIT, "jpeg_decompressinit"}, + {MNG_FN_JPEG_DECOMPRESSDATA, "jpeg_decompressdata"}, + {MNG_FN_JPEG_DECOMPRESSFREE, "jpeg_decompressfree"}, + + {MNG_FN_STORE_JPEG_G8, "store_jpeg_g8"}, + {MNG_FN_STORE_JPEG_RGB8, "store_jpeg_rgb8"}, + {MNG_FN_STORE_JPEG_G12, "store_jpeg_g12"}, + {MNG_FN_STORE_JPEG_RGB12, "store_jpeg_rgb12"}, + {MNG_FN_STORE_JPEG_GA8, "store_jpeg_ga8"}, + {MNG_FN_STORE_JPEG_RGBA8, "store_jpeg_rgba8"}, + {MNG_FN_STORE_JPEG_GA12, "store_jpeg_ga12"}, + {MNG_FN_STORE_JPEG_RGBA12, "store_jpeg_rgba12"}, + {MNG_FN_STORE_JPEG_G8_ALPHA, "store_jpeg_g8_alpha"}, + {MNG_FN_STORE_JPEG_RGB8_ALPHA, "store_jpeg_rgb8_alpha"}, + + {MNG_FN_INIT_JPEG_A1_NI, "init_jpeg_a1_ni"}, + {MNG_FN_INIT_JPEG_A2_NI, "init_jpeg_a2_ni"}, + {MNG_FN_INIT_JPEG_A4_NI, "init_jpeg_a4_ni"}, + {MNG_FN_INIT_JPEG_A8_NI, "init_jpeg_a8_ni"}, + {MNG_FN_INIT_JPEG_A16_NI, "init_jpeg_a16_ni"}, + + {MNG_FN_STORE_JPEG_G8_A1, "store_jpeg_g8_a1"}, + {MNG_FN_STORE_JPEG_G8_A2, "store_jpeg_g8_a2"}, + {MNG_FN_STORE_JPEG_G8_A4, "store_jpeg_g8_a4"}, + {MNG_FN_STORE_JPEG_G8_A8, "store_jpeg_g8_a8"}, + {MNG_FN_STORE_JPEG_G8_A16, "store_jpeg_g8_a16"}, + + {MNG_FN_STORE_JPEG_RGB8_A1, "store_jpeg_rgb8_a1"}, + {MNG_FN_STORE_JPEG_RGB8_A2, "store_jpeg_rgb8_a2"}, + {MNG_FN_STORE_JPEG_RGB8_A4, "store_jpeg_rgb8_a4"}, + {MNG_FN_STORE_JPEG_RGB8_A8, "store_jpeg_rgb8_a8"}, + {MNG_FN_STORE_JPEG_RGB8_A16, "store_jpeg_rgb8_a16"}, + + {MNG_FN_STORE_JPEG_G12_A1, "store_jpeg_g12_a1"}, + {MNG_FN_STORE_JPEG_G12_A2, "store_jpeg_g12_a2"}, + {MNG_FN_STORE_JPEG_G12_A4, "store_jpeg_g12_a4"}, + {MNG_FN_STORE_JPEG_G12_A8, "store_jpeg_g12_a8"}, + {MNG_FN_STORE_JPEG_G12_A16, "store_jpeg_g12_a16"}, + + {MNG_FN_STORE_JPEG_RGB12_A1, "store_jpeg_rgb12_a1"}, + {MNG_FN_STORE_JPEG_RGB12_A2, "store_jpeg_rgb12_a2"}, + {MNG_FN_STORE_JPEG_RGB12_A4, "store_jpeg_rgb12_a4"}, + {MNG_FN_STORE_JPEG_RGB12_A8, "store_jpeg_rgb12_a8"}, + {MNG_FN_STORE_JPEG_RGB12_A16, "store_jpeg_rgb12_a16"}, + + {MNG_FN_NEXT_JPEG_ALPHAROW, "next_jpeg_alpharow"}, + {MNG_FN_NEXT_JPEG_ROW, "next_jpeg_row"}, + {MNG_FN_DISPLAY_JPEG_ROWS, "display_jpeg_rows"}, + + {MNG_FN_MAGNIFY_G8_X1, "magnify_g8_x1"}, + {MNG_FN_MAGNIFY_G8_X2, "magnify_g8_x2"}, + {MNG_FN_MAGNIFY_RGB8_X1, "magnify_rgb8_x1"}, + {MNG_FN_MAGNIFY_RGB8_X2, "magnify_rgb8_x2"}, + {MNG_FN_MAGNIFY_GA8_X1, "magnify_ga8_x1"}, + {MNG_FN_MAGNIFY_GA8_X2, "magnify_ga8_x2"}, + {MNG_FN_MAGNIFY_GA8_X3, "magnify_ga8_x3"}, + {MNG_FN_MAGNIFY_GA8_X4, "magnify_ga8_x4"}, + {MNG_FN_MAGNIFY_RGBA8_X1, "magnify_rgba8_x1"}, + {MNG_FN_MAGNIFY_RGBA8_X2, "magnify_rgba8_x2"}, + {MNG_FN_MAGNIFY_RGBA8_X3, "magnify_rgba8_x3"}, + {MNG_FN_MAGNIFY_RGBA8_X4, "magnify_rgba8_x4"}, + {MNG_FN_MAGNIFY_G8_X3, "magnify_g8_x3"}, + {MNG_FN_MAGNIFY_RGB8_X3, "magnify_rgb8_x3"}, + {MNG_FN_MAGNIFY_GA8_X5, "magnify_ga8_x5"}, + {MNG_FN_MAGNIFY_RGBA8_X5, "magnify_rgba8_x5"}, + + {MNG_FN_MAGNIFY_G8_Y1, "magnify_g8_y1"}, + {MNG_FN_MAGNIFY_G8_Y2, "magnify_g8_y2"}, + {MNG_FN_MAGNIFY_RGB8_Y1, "magnify_rgb8_y1"}, + {MNG_FN_MAGNIFY_RGB8_Y2, "magnify_rgb8_y2"}, + {MNG_FN_MAGNIFY_GA8_Y1, "magnify_ga8_y1"}, + {MNG_FN_MAGNIFY_GA8_Y2, "magnify_ga8_y2"}, + {MNG_FN_MAGNIFY_GA8_Y3, "magnify_ga8_y3"}, + {MNG_FN_MAGNIFY_GA8_Y4, "magnify_ga8_y4"}, + {MNG_FN_MAGNIFY_RGBA8_Y1, "magnify_rgba8_y1"}, + {MNG_FN_MAGNIFY_RGBA8_Y2, "magnify_rgba8_y2"}, + {MNG_FN_MAGNIFY_RGBA8_Y3, "magnify_rgba8_y3"}, + {MNG_FN_MAGNIFY_RGBA8_Y4, "magnify_rgba8_y4"}, + {MNG_FN_MAGNIFY_G8_Y3, "magnify_g8_y3"}, + {MNG_FN_MAGNIFY_RGB8_Y3, "magnify_rgb8_y3"}, + {MNG_FN_MAGNIFY_GA8_Y5, "magnify_ga8_y5"}, + {MNG_FN_MAGNIFY_RGBA8_Y5, "magnify_rgba8_y5"}, + + {MNG_FN_MAGNIFY_G8_X1, "magnify_g8_x1"}, + {MNG_FN_MAGNIFY_G8_X2, "magnify_g8_x2"}, + {MNG_FN_MAGNIFY_RGB8_X1, "magnify_rgb8_x1"}, + {MNG_FN_MAGNIFY_RGB8_X2, "magnify_rgb8_x2"}, + {MNG_FN_MAGNIFY_GA8_X1, "magnify_ga8_x1"}, + {MNG_FN_MAGNIFY_GA8_X2, "magnify_ga8_x2"}, + {MNG_FN_MAGNIFY_GA8_X3, "magnify_ga8_x3"}, + {MNG_FN_MAGNIFY_GA8_X4, "magnify_ga8_x4"}, + {MNG_FN_MAGNIFY_RGBA8_X1, "magnify_rgba8_x1"}, + {MNG_FN_MAGNIFY_RGBA8_X2, "magnify_rgba8_x2"}, + {MNG_FN_MAGNIFY_RGBA8_X3, "magnify_rgba8_x3"}, + {MNG_FN_MAGNIFY_RGBA8_X4, "magnify_rgba8_x4"}, + {MNG_FN_MAGNIFY_G8_X3, "magnify_g8_x3"}, + {MNG_FN_MAGNIFY_RGB8_X3, "magnify_rgb8_x3"}, + {MNG_FN_MAGNIFY_GA8_X5, "magnify_ga8_x5"}, + {MNG_FN_MAGNIFY_RGBA8_X5, "magnify_rgba8_x5"}, + + {MNG_FN_MAGNIFY_G8_Y1, "magnify_g8_y1"}, + {MNG_FN_MAGNIFY_G8_Y2, "magnify_g8_y2"}, + {MNG_FN_MAGNIFY_RGB8_Y1, "magnify_rgb8_y1"}, + {MNG_FN_MAGNIFY_RGB8_Y2, "magnify_rgb8_y2"}, + {MNG_FN_MAGNIFY_GA8_Y1, "magnify_ga8_y1"}, + {MNG_FN_MAGNIFY_GA8_Y2, "magnify_ga8_y2"}, + {MNG_FN_MAGNIFY_GA8_Y3, "magnify_ga8_y3"}, + {MNG_FN_MAGNIFY_GA8_Y4, "magnify_ga8_y4"}, + {MNG_FN_MAGNIFY_RGBA8_Y1, "magnify_rgba8_y1"}, + {MNG_FN_MAGNIFY_RGBA8_Y2, "magnify_rgba8_y2"}, + {MNG_FN_MAGNIFY_RGBA8_Y3, "magnify_rgba8_y3"}, + {MNG_FN_MAGNIFY_RGBA8_Y4, "magnify_rgba8_y4"}, + {MNG_FN_MAGNIFY_G8_Y3, "magnify_g8_y3"}, + {MNG_FN_MAGNIFY_RGB8_Y3, "magnify_rgb8_y3"}, + {MNG_FN_MAGNIFY_GA8_Y5, "magnify_ga8_y5"}, + {MNG_FN_MAGNIFY_RGBA8_Y5, "magnify_rgba8_y5"}, + + {MNG_FN_DELTA_G1_G1, "delta_g1_g1"}, + {MNG_FN_DELTA_G2_G2, "delta_g2_g2"}, + {MNG_FN_DELTA_G4_G4, "delta_g4_g4"}, + {MNG_FN_DELTA_G8_G8, "delta_g8_g8"}, + {MNG_FN_DELTA_G16_G16, "delta_g16_g16"}, + {MNG_FN_DELTA_RGB8_RGB8, "delta_rgb8_rgb8"}, + {MNG_FN_DELTA_RGB16_RGB16, "delta_rgb16_rgb16"}, + {MNG_FN_DELTA_GA8_GA8, "delta_ga8_ga8"}, + {MNG_FN_DELTA_GA8_G8, "delta_ga8_g8"}, + {MNG_FN_DELTA_GA8_A8, "delta_ga8_a8"}, + {MNG_FN_DELTA_GA16_GA16, "delta_ga16_ga16"}, + {MNG_FN_DELTA_GA16_G16, "delta_ga16_g16"}, + {MNG_FN_DELTA_GA16_A16, "delta_ga16_a16"}, + {MNG_FN_DELTA_RGBA8_RGBA8, "delta_rgba8_rgba8"}, + {MNG_FN_DELTA_RGBA8_RGB8, "delta_rgba8_rgb8"}, + {MNG_FN_DELTA_RGBA8_A8, "delta_rgba8_a8"}, + {MNG_FN_DELTA_RGBA16_RGBA16, "delta_rgba16_rgba16"}, + {MNG_FN_DELTA_RGBA16_RGB16, "delta_rgba16_rgb16"}, + {MNG_FN_DELTA_RGBA16_A16, "delta_rgba16_a16"}, + + {MNG_FN_PROMOTE_G8_G8, "promote_g8_g8"}, + {MNG_FN_PROMOTE_G8_G16, "promote_g8_g16"}, + {MNG_FN_PROMOTE_G16_G16, "promote_g8_g16"}, + {MNG_FN_PROMOTE_G8_GA8, "promote_g8_ga8"}, + {MNG_FN_PROMOTE_G8_GA16, "promote_g8_ga16"}, + {MNG_FN_PROMOTE_G16_GA16, "promote_g16_ga16"}, + {MNG_FN_PROMOTE_G8_RGB8, "promote_g8_rgb8"}, + {MNG_FN_PROMOTE_G8_RGB16, "promote_g8_rgb16"}, + {MNG_FN_PROMOTE_G16_RGB16, "promote_g16_rgb16"}, + {MNG_FN_PROMOTE_G8_RGBA8, "promote_g8_rgba8"}, + {MNG_FN_PROMOTE_G8_RGBA16, "promote_g8_rgba16"}, + {MNG_FN_PROMOTE_G16_RGBA16, "promote_g16_rgba16"}, + {MNG_FN_PROMOTE_GA8_GA16, "promote_ga8_ga16"}, + {MNG_FN_PROMOTE_GA8_RGBA8, "promote_ga8_rgba8"}, + {MNG_FN_PROMOTE_GA8_RGBA16, "promote_ga8_rgba16"}, + {MNG_FN_PROMOTE_GA16_RGBA16, "promote_ga16_rgba16"}, + {MNG_FN_PROMOTE_RGB8_RGB16, "promote_rgb8_rgb16"}, + {MNG_FN_PROMOTE_RGB8_RGBA8, "promote_rgb8_rgba8"}, + {MNG_FN_PROMOTE_RGB8_RGBA16, "promote_rgb8_rgba16"}, + {MNG_FN_PROMOTE_RGB16_RGBA16, "promote_rgb16_rgba16"}, + {MNG_FN_PROMOTE_RGBA8_RGBA16, "promote_rgba8_rgba16"}, + {MNG_FN_PROMOTE_IDX8_RGB8, "promote_idx8_rgb8"}, + {MNG_FN_PROMOTE_IDX8_RGB16, "promote_idx8_rgb16"}, + {MNG_FN_PROMOTE_IDX8_RGBA8, "promote_idx8_rgba8"}, + {MNG_FN_PROMOTE_IDX8_RGBA16, "promote_idx8_rgba16"}, + + {MNG_FN_SCALE_G1_G2, "scale_g1_g2"}, + {MNG_FN_SCALE_G1_G4, "scale_g1_g4"}, + {MNG_FN_SCALE_G1_G8, "scale_g1_g8"}, + {MNG_FN_SCALE_G1_G16, "scale_g1_g16"}, + {MNG_FN_SCALE_G2_G4, "scale_g2_g4"}, + {MNG_FN_SCALE_G2_G8, "scale_g2_g8"}, + {MNG_FN_SCALE_G2_G16, "scale_g2_g16"}, + {MNG_FN_SCALE_G4_G8, "scale_g4_g8"}, + {MNG_FN_SCALE_G4_G16, "scale_g4_g16"}, + {MNG_FN_SCALE_G8_G16, "scale_g8_g16"}, + {MNG_FN_SCALE_GA8_GA16, "scale_ga8_ga16"}, + {MNG_FN_SCALE_RGB8_RGB16, "scale_rgb8_rgb16"}, + {MNG_FN_SCALE_RGBA8_RGBA16, "scale_rgba8_rgba16"}, + + {MNG_FN_SCALE_G2_G1, "scale_g2_g1"}, + {MNG_FN_SCALE_G4_G1, "scale_g4_g1"}, + {MNG_FN_SCALE_G8_G1, "scale_g8_g1"}, + {MNG_FN_SCALE_G16_G1, "scale_g16_g1"}, + {MNG_FN_SCALE_G4_G2, "scale_g4_g2"}, + {MNG_FN_SCALE_G8_G2, "scale_g8_g2"}, + {MNG_FN_SCALE_G16_G2, "scale_g16_g2"}, + {MNG_FN_SCALE_G8_G4, "scale_g8_g4"}, + {MNG_FN_SCALE_G16_G4, "scale_g16_g4"}, + {MNG_FN_SCALE_G16_G8, "scale_g16_g8"}, + {MNG_FN_SCALE_GA16_GA8, "scale_ga16_ga8"}, + {MNG_FN_SCALE_RGB16_RGB8, "scale_rgb16_rgb8"}, + {MNG_FN_SCALE_RGBA16_RGBA8, "scale_rgba16_rgba8"}, + + {MNG_FN_COMPOSEOVER_RGBA8, "composeover_rgba8"}, + {MNG_FN_COMPOSEOVER_RGBA16, "composeover_rgba16"}, + {MNG_FN_COMPOSEUNDER_RGBA8, "composeunder_rgba8"}, + {MNG_FN_COMPOSEUNDER_RGBA16, "composeunder_rgba16"}, + + {MNG_FN_FLIP_RGBA8, "flip_rgba8"}, + {MNG_FN_FLIP_RGBA16, "flip_rgba16"}, + {MNG_FN_TILE_RGBA8, "tile_rgba8"}, + {MNG_FN_TILE_RGBA16, "tile_rgba16"} + + }; +#endif /* MNG_INCLUDE_TRACE_STINGS */ + +/* ************************************************************************** */ + +mng_retcode mng_trace (mng_datap pData, + mng_uint32 iFunction, + mng_uint32 iLocation) +{ + mng_pchar zName = 0; /* bufferptr for tracestring */ + + if ((pData == 0) || (pData->iMagic != MNG_MAGIC)) + return MNG_INVALIDHANDLE; /* no good if the handle is corrupt */ + + if (pData->fTraceproc) /* report back to user ? */ + { +#ifdef MNG_INCLUDE_TRACE_STRINGS + { /* binary search variables */ + mng_int32 iTop, iLower, iUpper, iMiddle; + mng_trace_entryp pEntry; /* pointer to found entry */ + /* determine max index of table */ + iTop = (sizeof (trace_table) / sizeof (trace_table [0])) - 1; + + iLower = 0; /* initialize binary search */ + iMiddle = iTop >> 1; /* start in the middle */ + iUpper = iTop; + pEntry = 0; /* no goods yet! */ + + do /* the binary search itself */ + { + if (trace_table [iMiddle].iFunction < iFunction) + iLower = iMiddle + 1; + else if (trace_table [iMiddle].iFunction > iFunction) + iUpper = iMiddle - 1; + else + { + pEntry = &trace_table [iMiddle]; + break; + }; + + iMiddle = (iLower + iUpper) >> 1; + } + while (iLower <= iUpper); + + if (pEntry) /* found it ? */ + zName = pEntry->zTracetext; + + } +#endif + /* oke, now tell */ + if (!pData->fTraceproc (((mng_handle)pData), iFunction, iLocation, zName)) + return MNG_APPTRACEABORT; + + } + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_TRACE_PROCS */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ + diff --git a/Engine/lib/lmng/libmng_trace.h b/Engine/lib/lmng/libmng_trace.h new file mode 100644 index 000000000..0c749d978 --- /dev/null +++ b/Engine/lib/lmng/libmng_trace.h @@ -0,0 +1,1474 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_trace.h copyright (c) 2000-2007 G.Juyn * */ +/* * version : 1.0.10 * */ +/* * * */ +/* * purpose : Trace functions (definition) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : Definition of the trace functions * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - added chunk-access function trace-codes * */ +/* * - changed strict-ANSI stuff * */ +/* * 0.5.1 - 05/12/2000 - G.Juyn * */ +/* * - changed trace to macro for callback error-reporting * */ +/* * 0.5.1 - 05/13/2000 - G.Juyn * */ +/* * - added save_state & restore_state trace-codes * */ +/* * 0.5.1 - 05/15/2000 - G.Juyn * */ +/* * - added getimgdata & putimgdata trace-codes * */ +/* * * */ +/* * 0.5.2 - 05/20/2000 - G.Juyn * */ +/* * - added JNG tracecodes * */ +/* * 0.5.2 - 05/23/2000 - G.Juyn * */ +/* * - added trace-table entry definition * */ +/* * 0.5.2 - 05/24/2000 - G.Juyn * */ +/* * - added tracecodes for global animation color-chunks * */ +/* * - added tracecodes for get/set of default ZLIB/IJG parms * */ +/* * - added tracecodes for global PLTE,tRNS,bKGD * */ +/* * 0.5.2 - 05/30/2000 - G.Juyn * */ +/* * - added tracecodes for image-object promotion * */ +/* * - added tracecodes for delta-image processing * */ +/* * 0.5.2 - 06/02/2000 - G.Juyn * */ +/* * - added tracecodes for getalphaline callback * */ +/* * 0.5.2 - 06/05/2000 - G.Juyn * */ +/* * - added tracecode for RGB8_A8 canvasstyle * */ +/* * 0.5.2 - 06/06/2000 - G.Juyn * */ +/* * - added tracecode for mng_read_resume HLAPI function * */ +/* * * */ +/* * 0.5.3 - 06/06/2000 - G.Juyn * */ +/* * - added tracecodes for tracing JPEG progression * */ +/* * 0.5.3 - 06/21/2000 - G.Juyn * */ +/* * - added tracecodes for get/set speedtype * */ +/* * - added tracecodes for get imagelevel * */ +/* * 0.5.3 - 06/22/2000 - G.Juyn * */ +/* * - added tracecode for delta-image processing * */ +/* * - added tracecodes for PPLT chunk processing * */ +/* * * */ +/* * 0.9.1 - 07/07/2000 - G.Juyn * */ +/* * - added tracecodes for special display processing * */ +/* * 0.9.1 - 07/08/2000 - G.Juyn * */ +/* * - added tracecode for get/set suspensionmode * */ +/* * - added tracecodes for get/set display variables * */ +/* * - added tracecode for read_databuffer (I/O-suspension) * */ +/* * 0.9.1 - 07/15/2000 - G.Juyn * */ +/* * - added tracecodes for SAVE/SEEK callbacks * */ +/* * - added tracecodes for get/set sectionbreaks * */ +/* * - added tracecode for special error routine * */ +/* * 0.9.1 - 07/19/2000 - G.Juyn * */ +/* * - added tracecode for updatemngheader * */ +/* * * */ +/* * 0.9.2 - 07/31/2000 - G.Juyn * */ +/* * - added tracecodes for status_xxxxx functions * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * - added tracecode for updatemngsimplicity * */ +/* * * */ +/* * 0.9.3 - 08/26/2000 - G.Juyn * */ +/* * - added MAGN chunk * */ +/* * 0.9.3 - 09/07/2000 - G.Juyn * */ +/* * - added support for new filter_types * */ +/* * 0.9.3 - 10/10/2000 - G.Juyn * */ +/* * - added support for alpha-depth prediction * */ +/* * 0.9.3 - 10/11/2000 - G.Juyn * */ +/* * - added JDAA chunk * */ +/* * - added support for nEED * */ +/* * 0.9.3 - 10/16/2000 - G.Juyn * */ +/* * - added functions to retrieve PNG/JNG specific header-info * */ +/* * - added optional support for bKGD for PNG images * */ +/* * 0.9.3 - 10/17/2000 - G.Juyn * */ +/* * - added callback to process non-critical unknown chunks * */ +/* * - added routine to discard "invalid" objects * */ +/* * 0.9.3 - 10/19/2000 - G.Juyn * */ +/* * - implemented delayed delta-processing * */ +/* * 0.9.3 - 10/20/2000 - G.Juyn * */ +/* * - added get/set for bKGD preference setting * */ +/* * 0.9.3 - 10/21/2000 - G.Juyn * */ +/* * - added get function for interlace/progressive display * */ +/* * * */ +/* * 0.9.4 - 1/18/2001 - G.Juyn * */ +/* * - added "new" MAGN methods 3, 4 & 5 * */ +/* * * */ +/* * 1.0.1 - 02/08/2001 - G.Juyn * */ +/* * - added MEND processing callback * */ +/* * 1.0.1 - 04/21/2001 - G.Juyn (code by G.Kelly) * */ +/* * - added BGRA8 canvas with premultiplied alpha * */ +/* * 1.0.1 - 05/02/2001 - G.Juyn * */ +/* * - added "default" sRGB generation (Thanks Marti!) * */ +/* * * */ +/* * 1.0.2 - 06/23/2001 - G.Juyn * */ +/* * - added optimization option for MNG-video playback * */ +/* * - added processterm callback * */ +/* * 1.0.2 - 06/25/2001 - G.Juyn * */ +/* * - added option to turn off progressive refresh * */ +/* * * */ +/* * 1.0.3 - 08/06/2001 - G.Juyn * */ +/* * - added get function for last processed BACK chunk * */ +/* * * */ +/* * 1.0.5 - 08/15/2002 - G.Juyn * */ +/* * - completed PROM support * */ +/* * - completed delta-image support * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * - added HLAPI function to copy chunks * */ +/* * 1.0.5 - 09/14/2002 - G.Juyn * */ +/* * - added event handling for dynamic MNG * */ +/* * 1.0.5 - 09/20/2002 - G.Juyn * */ +/* * - added support for PAST * */ +/* * 1.0.5 - 09/22/2002 - G.Juyn * */ +/* * - added bgrx8 canvas (filler byte) * */ +/* * 1.0.5 - 09/23/2002 - G.Juyn * */ +/* * - added in-memory color-correction of abstract images * */ +/* * - added compose over/under routines for PAST processing * */ +/* * - added flip & tile routines for PAST processing * */ +/* * 1.0.5 - 10/09/2002 - G.Juyn * */ +/* * - fixed trace-constants for PAST chunk * */ +/* * 1.0.5 - 11/07/2002 - G.Juyn * */ +/* * - added support to get totals after mng_read() * */ +/* * * */ +/* * 1.0.6 - 07/14/2003 - G.Randers-Pehrson * */ +/* * - added conditionals around rarely used features * */ +/* * * */ +/* * 1.0.7 - 11/27/2003 - R.A * */ +/* * - added CANVAS_RGB565 and CANVAS_BGR565 * */ +/* * 1.0.7 - 01/25/2004 - J.S * */ +/* * - added premultiplied alpha canvas' for RGBA, ARGB, ABGR * */ +/* * 1.0.7 - 03/10/2004 - G.R-P * */ +/* * - added conditionals around openstream/closestream * */ +/* * * */ +/* * 1.0.8 - 04/02/2004 - G.Juyn * */ +/* * - added CRC existence & checking flags * */ +/* * 1.0.8 - 04/11/2004 - G.Juyn * */ +/* * - added data-push mechanisms for specialized decoders * */ +/* * * */ +/* * 1.0.9 - 10/03/2004 - G.Juyn * */ +/* * - added function to retrieve current FRAM delay * */ +/* * 1.0.9 - 10/14/2004 - G.Juyn * */ +/* * - added bgr565_a8 canvas-style (thanks to J. Elvander) * */ +/* * * */ +/* * 1.0.10 - 04/08/2007 - G.Juyn * */ +/* * - added support for mPNG proposal * */ +/* * 1.0.10 - 07/06/2007 - G.R-P bugfix by Lucas Quintana * */ +/* * * */ +/* ************************************************************************** */ + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +#ifndef _libmng_trace_h_ +#define _libmng_trace_h_ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_TRACE_PROCS + +/* ************************************************************************** */ + +/* TODO: add a trace-mask so certain functions can be excluded */ + +mng_retcode mng_trace (mng_datap pData, + mng_uint32 iFunction, + mng_uint32 iLocation); + +/* ************************************************************************** */ + +#define MNG_TRACE(D,F,L) { mng_retcode iR = mng_trace (D,F,L); \ + if (iR) return iR; } + +#define MNG_TRACEB(D,F,L) { if (mng_trace (D,F,L)) return MNG_FALSE; } + +#define MNG_TRACEX(D,F,L) { if (mng_trace (D,F,L)) return 0; } + +/* ************************************************************************** */ + +#define MNG_LC_START 1 +#define MNG_LC_END 2 +#define MNG_LC_INITIALIZE 3 +#define MNG_LC_CLEANUP 4 + +/* ************************************************************************** */ + +#define MNG_LC_JPEG_CREATE_DECOMPRESS 101 +#define MNG_LC_JPEG_READ_HEADER 102 +#define MNG_LC_JPEG_START_DECOMPRESS 103 +#define MNG_LC_JPEG_START_OUTPUT 104 +#define MNG_LC_JPEG_READ_SCANLINES 105 +#define MNG_LC_JPEG_FINISH_OUTPUT 106 +#define MNG_LC_JPEG_FINISH_DECOMPRESS 107 +#define MNG_LC_JPEG_DESTROY_DECOMPRESS 108 + +/* ************************************************************************** */ + +#define MNG_FN_INITIALIZE 1 +#define MNG_FN_RESET 2 +#define MNG_FN_CLEANUP 3 +#define MNG_FN_READ 4 +#define MNG_FN_WRITE 5 +#define MNG_FN_CREATE 6 +#define MNG_FN_READDISPLAY 7 +#define MNG_FN_DISPLAY 8 +#define MNG_FN_DISPLAY_RESUME 9 +#define MNG_FN_DISPLAY_FREEZE 10 +#define MNG_FN_DISPLAY_RESET 11 +#ifndef MNG_NO_DISPLAY_GO_SUPPORTED +#define MNG_FN_DISPLAY_GOFRAME 12 +#define MNG_FN_DISPLAY_GOLAYER 13 +#define MNG_FN_DISPLAY_GOTIME 14 +#endif +#define MNG_FN_GETLASTERROR 15 +#define MNG_FN_READ_RESUME 16 +#define MNG_FN_TRAPEVENT 17 +#define MNG_FN_READ_PUSHDATA 18 +#define MNG_FN_READ_PUSHSIG 19 +#define MNG_FN_READ_PUSHCHUNK 20 + +#define MNG_FN_SETCB_MEMALLOC 101 +#define MNG_FN_SETCB_MEMFREE 102 +#define MNG_FN_SETCB_READDATA 103 +#define MNG_FN_SETCB_WRITEDATA 104 +#define MNG_FN_SETCB_ERRORPROC 105 +#define MNG_FN_SETCB_TRACEPROC 106 +#define MNG_FN_SETCB_PROCESSHEADER 107 +#define MNG_FN_SETCB_PROCESSTEXT 108 +#define MNG_FN_SETCB_GETCANVASLINE 109 +#define MNG_FN_SETCB_GETBKGDLINE 110 +#define MNG_FN_SETCB_REFRESH 111 +#define MNG_FN_SETCB_GETTICKCOUNT 112 +#define MNG_FN_SETCB_SETTIMER 113 +#define MNG_FN_SETCB_PROCESSGAMMA 114 +#define MNG_FN_SETCB_PROCESSCHROMA 115 +#define MNG_FN_SETCB_PROCESSSRGB 116 +#define MNG_FN_SETCB_PROCESSICCP 117 +#define MNG_FN_SETCB_PROCESSAROW 118 +#ifndef MNG_NO_OPEN_CLOSE_STREAM +#define MNG_FN_SETCB_OPENSTREAM 119 +#define MNG_FN_SETCB_CLOSESTREAM 120 +#endif +#define MNG_FN_SETCB_GETALPHALINE 121 +#define MNG_FN_SETCB_PROCESSSAVE 122 +#define MNG_FN_SETCB_PROCESSSEEK 123 +#define MNG_FN_SETCB_PROCESSNEED 124 +#define MNG_FN_SETCB_PROCESSUNKNOWN 125 +#define MNG_FN_SETCB_PROCESSMEND 126 +#define MNG_FN_SETCB_PROCESSTERM 127 +#define MNG_FN_SETCB_RELEASEDATA 128 + +#define MNG_FN_GETCB_MEMALLOC 201 +#define MNG_FN_GETCB_MEMFREE 202 +#define MNG_FN_GETCB_READDATA 203 +#define MNG_FN_GETCB_WRITEDATA 204 +#define MNG_FN_GETCB_ERRORPROC 205 +#define MNG_FN_GETCB_TRACEPROC 206 +#define MNG_FN_GETCB_PROCESSHEADER 207 +#define MNG_FN_GETCB_PROCESSTEXT 208 +#define MNG_FN_GETCB_GETCANVASLINE 209 +#define MNG_FN_GETCB_GETBKGDLINE 210 +#define MNG_FN_GETCB_REFRESH 211 +#define MNG_FN_GETCB_GETTICKCOUNT 212 +#define MNG_FN_GETCB_SETTIMER 213 +#define MNG_FN_GETCB_PROCESSGAMMA 214 +#define MNG_FN_GETCB_PROCESSCHROMA 215 +#define MNG_FN_GETCB_PROCESSSRGB 216 +#define MNG_FN_GETCB_PROCESSICCP 217 +#define MNG_FN_GETCB_PROCESSAROW 218 +#ifndef MNG_NO_OPEN_CLOSE_STREAM +#define MNG_FN_GETCB_OPENSTREAM 219 +#define MNG_FN_GETCB_CLOSESTREAM 220 +#endif +#define MNG_FN_GETCB_GETALPHALINE 221 +#define MNG_FN_GETCB_PROCESSSAVE 222 +#define MNG_FN_GETCB_PROCESSSEEK 223 +#define MNG_FN_GETCB_PROCESSNEED 224 +#define MNG_FN_GETCB_PROCESSUNKNOWN 225 +#define MNG_FN_GETCB_PROCESSMEND 226 +#define MNG_FN_GETCB_PROCESSTERM 227 +#define MNG_FN_GETCB_RELEASEDATA 228 + +#define MNG_FN_SET_USERDATA 301 +#define MNG_FN_SET_CANVASSTYLE 302 +#define MNG_FN_SET_BKGDSTYLE 303 +#define MNG_FN_SET_BGCOLOR 304 +#define MNG_FN_SET_STORECHUNKS 305 +#define MNG_FN_SET_VIEWGAMMA 306 +#define MNG_FN_SET_DISPLAYGAMMA 307 +#define MNG_FN_SET_DFLTIMGGAMMA 308 +#define MNG_FN_SET_SRGB 309 +#define MNG_FN_SET_OUTPUTPROFILE 310 +#define MNG_FN_SET_SRGBPROFILE 311 +#define MNG_FN_SET_MAXCANVASWIDTH 312 +#define MNG_FN_SET_MAXCANVASHEIGHT 313 +#define MNG_FN_SET_MAXCANVASSIZE 314 +#define MNG_FN_SET_ZLIB_LEVEL 315 +#define MNG_FN_SET_ZLIB_METHOD 316 +#define MNG_FN_SET_ZLIB_WINDOWBITS 317 +#define MNG_FN_SET_ZLIB_MEMLEVEL 318 +#define MNG_FN_SET_ZLIB_STRATEGY 319 +#define MNG_FN_SET_ZLIB_MAXIDAT 320 +#define MNG_FN_SET_JPEG_DCTMETHOD 321 +#define MNG_FN_SET_JPEG_QUALITY 322 +#define MNG_FN_SET_JPEG_SMOOTHING 323 +#define MNG_FN_SET_JPEG_PROGRESSIVE 324 +#define MNG_FN_SET_JPEG_OPTIMIZED 325 +#define MNG_FN_SET_JPEG_MAXJDAT 326 +#define MNG_FN_SET_SPEED 327 +#define MNG_FN_SET_SUSPENSIONMODE 328 +#define MNG_FN_SET_SECTIONBREAKS 329 +#define MNG_FN_SET_USEBKGD 330 +#define MNG_FN_SET_OUTPUTPROFILE2 331 +#define MNG_FN_SET_SRGBPROFILE2 332 +#define MNG_FN_SET_OUTPUTSRGB 333 +#define MNG_FN_SET_SRGBIMPLICIT 334 +#define MNG_FN_SET_CACHEPLAYBACK 335 +#define MNG_FN_SET_DOPROGRESSIVE 336 +#define MNG_FN_SET_CRCMODE 337 + +#define MNG_FN_GET_USERDATA 401 +#define MNG_FN_GET_SIGTYPE 402 +#define MNG_FN_GET_IMAGETYPE 403 +#define MNG_FN_GET_IMAGEWIDTH 404 +#define MNG_FN_GET_IMAGEHEIGHT 405 +#define MNG_FN_GET_TICKS 406 +#define MNG_FN_GET_FRAMECOUNT 407 +#define MNG_FN_GET_LAYERCOUNT 408 +#define MNG_FN_GET_PLAYTIME 409 +#define MNG_FN_GET_SIMPLICITY 410 +#define MNG_FN_GET_CANVASSTYLE 411 +#define MNG_FN_GET_BKGDSTYLE 412 +#define MNG_FN_GET_BGCOLOR 413 +#define MNG_FN_GET_STORECHUNKS 414 +#define MNG_FN_GET_VIEWGAMMA 415 +#define MNG_FN_GET_DISPLAYGAMMA 416 +#define MNG_FN_GET_DFLTIMGGAMMA 417 +#define MNG_FN_GET_SRGB 418 +#define MNG_FN_GET_MAXCANVASWIDTH 419 +#define MNG_FN_GET_MAXCANVASHEIGHT 420 +#define MNG_FN_GET_ZLIB_LEVEL 421 +#define MNG_FN_GET_ZLIB_METHOD 422 +#define MNG_FN_GET_ZLIB_WINDOWBITS 423 +#define MNG_FN_GET_ZLIB_MEMLEVEL 424 +#define MNG_FN_GET_ZLIB_STRATEGY 425 +#define MNG_FN_GET_ZLIB_MAXIDAT 426 +#define MNG_FN_GET_JPEG_DCTMETHOD 427 +#define MNG_FN_GET_JPEG_QUALITY 428 +#define MNG_FN_GET_JPEG_SMOOTHING 429 +#define MNG_FN_GET_JPEG_PROGRESSIVE 430 +#define MNG_FN_GET_JPEG_OPTIMIZED 431 +#define MNG_FN_GET_JPEG_MAXJDAT 432 +#define MNG_FN_GET_SPEED 433 +#define MNG_FN_GET_IMAGELEVEL 434 +#define MNG_FN_GET_SUSPENSIONMODE 435 +#define MNG_FN_GET_STARTTIME 436 +#define MNG_FN_GET_RUNTIME 437 +#define MNG_FN_GET_CURRENTFRAME 438 +#define MNG_FN_GET_CURRENTLAYER 439 +#define MNG_FN_GET_CURRENTPLAYTIME 440 +#define MNG_FN_GET_SECTIONBREAKS 441 +#define MNG_FN_GET_ALPHADEPTH 442 +#define MNG_FN_GET_BITDEPTH 443 +#define MNG_FN_GET_COLORTYPE 444 +#define MNG_FN_GET_COMPRESSION 445 +#define MNG_FN_GET_FILTER 446 +#define MNG_FN_GET_INTERLACE 447 +#define MNG_FN_GET_ALPHABITDEPTH 448 +#define MNG_FN_GET_ALPHACOMPRESSION 449 +#define MNG_FN_GET_ALPHAFILTER 450 +#define MNG_FN_GET_ALPHAINTERLACE 451 +#define MNG_FN_GET_USEBKGD 452 +#define MNG_FN_GET_REFRESHPASS 453 +#define MNG_FN_GET_CACHEPLAYBACK 454 +#define MNG_FN_GET_DOPROGRESSIVE 455 +#define MNG_FN_GET_LASTBACKCHUNK 456 +#define MNG_FN_GET_LASTSEEKNAME 457 +#define MNG_FN_GET_TOTALFRAMES 458 +#define MNG_FN_GET_TOTALLAYERS 459 +#define MNG_FN_GET_TOTALPLAYTIME 460 +#define MNG_FN_GET_CRCMODE 461 +#define MNG_FN_GET_CURRFRAMDELAY 462 + +#define MNG_FN_STATUS_ERROR 481 +#define MNG_FN_STATUS_READING 482 +#define MNG_FN_STATUS_SUSPENDBREAK 483 +#define MNG_FN_STATUS_CREATING 484 +#define MNG_FN_STATUS_WRITING 485 +#define MNG_FN_STATUS_DISPLAYING 486 +#define MNG_FN_STATUS_RUNNING 487 +#define MNG_FN_STATUS_TIMERBREAK 488 +#define MNG_FN_STATUS_DYNAMIC 489 +#define MNG_FN_STATUS_RUNNINGEVENT 490 + +/* ************************************************************************** */ + +#define MNG_FN_ITERATE_CHUNKS 601 +#define MNG_FN_COPY_CHUNK 602 + +#define MNG_FN_GETCHUNK_IHDR 701 +#define MNG_FN_GETCHUNK_PLTE 702 +#define MNG_FN_GETCHUNK_IDAT 703 +#define MNG_FN_GETCHUNK_IEND 704 +#define MNG_FN_GETCHUNK_TRNS 705 +#define MNG_FN_GETCHUNK_GAMA 706 +#define MNG_FN_GETCHUNK_CHRM 707 +#define MNG_FN_GETCHUNK_SRGB 708 +#define MNG_FN_GETCHUNK_ICCP 709 +#define MNG_FN_GETCHUNK_TEXT 710 +#define MNG_FN_GETCHUNK_ZTXT 711 +#define MNG_FN_GETCHUNK_ITXT 712 +#define MNG_FN_GETCHUNK_BKGD 713 +#define MNG_FN_GETCHUNK_PHYS 714 +#define MNG_FN_GETCHUNK_SBIT 715 +#define MNG_FN_GETCHUNK_SPLT 716 +#define MNG_FN_GETCHUNK_HIST 717 +#define MNG_FN_GETCHUNK_TIME 718 +#define MNG_FN_GETCHUNK_MHDR 719 +#define MNG_FN_GETCHUNK_MEND 720 +#define MNG_FN_GETCHUNK_LOOP 721 +#define MNG_FN_GETCHUNK_ENDL 722 +#define MNG_FN_GETCHUNK_DEFI 723 +#define MNG_FN_GETCHUNK_BASI 724 +#define MNG_FN_GETCHUNK_CLON 725 +#define MNG_FN_GETCHUNK_PAST 726 +#define MNG_FN_GETCHUNK_DISC 727 +#define MNG_FN_GETCHUNK_BACK 728 +#define MNG_FN_GETCHUNK_FRAM 729 +#define MNG_FN_GETCHUNK_MOVE 730 +#define MNG_FN_GETCHUNK_CLIP 731 +#define MNG_FN_GETCHUNK_SHOW 732 +#define MNG_FN_GETCHUNK_TERM 733 +#define MNG_FN_GETCHUNK_SAVE 734 +#define MNG_FN_GETCHUNK_SEEK 735 +#define MNG_FN_GETCHUNK_EXPI 736 +#define MNG_FN_GETCHUNK_FPRI 737 +#define MNG_FN_GETCHUNK_NEED 738 +#define MNG_FN_GETCHUNK_PHYG 739 +#define MNG_FN_GETCHUNK_JHDR 740 +#define MNG_FN_GETCHUNK_JDAT 741 +#define MNG_FN_GETCHUNK_JSEP 742 +#define MNG_FN_GETCHUNK_DHDR 743 +#define MNG_FN_GETCHUNK_PROM 744 +#define MNG_FN_GETCHUNK_IPNG 745 +#define MNG_FN_GETCHUNK_PPLT 746 +#define MNG_FN_GETCHUNK_IJNG 747 +#define MNG_FN_GETCHUNK_DROP 748 +#define MNG_FN_GETCHUNK_DBYK 749 +#define MNG_FN_GETCHUNK_ORDR 750 +#define MNG_FN_GETCHUNK_UNKNOWN 751 +#define MNG_FN_GETCHUNK_MAGN 752 +#define MNG_FN_GETCHUNK_JDAA 753 +#define MNG_FN_GETCHUNK_EVNT 754 +#define MNG_FN_GETCHUNK_MPNG 755 + +#define MNG_FN_GETCHUNK_PAST_SRC 781 +#define MNG_FN_GETCHUNK_SAVE_ENTRY 782 +#define MNG_FN_GETCHUNK_PPLT_ENTRY 783 +#define MNG_FN_GETCHUNK_ORDR_ENTRY 784 +#define MNG_FN_GETCHUNK_EVNT_ENTRY 785 +#define MNG_FN_GETCHUNK_MPNG_FRAME 786 + +#define MNG_FN_PUTCHUNK_IHDR 801 +#define MNG_FN_PUTCHUNK_PLTE 802 +#define MNG_FN_PUTCHUNK_IDAT 803 +#define MNG_FN_PUTCHUNK_IEND 804 +#define MNG_FN_PUTCHUNK_TRNS 805 +#define MNG_FN_PUTCHUNK_GAMA 806 +#define MNG_FN_PUTCHUNK_CHRM 807 +#define MNG_FN_PUTCHUNK_SRGB 808 +#define MNG_FN_PUTCHUNK_ICCP 809 +#define MNG_FN_PUTCHUNK_TEXT 810 +#define MNG_FN_PUTCHUNK_ZTXT 811 +#define MNG_FN_PUTCHUNK_ITXT 812 +#define MNG_FN_PUTCHUNK_BKGD 813 +#define MNG_FN_PUTCHUNK_PHYS 814 +#define MNG_FN_PUTCHUNK_SBIT 815 +#define MNG_FN_PUTCHUNK_SPLT 816 +#define MNG_FN_PUTCHUNK_HIST 817 +#define MNG_FN_PUTCHUNK_TIME 818 +#define MNG_FN_PUTCHUNK_MHDR 819 +#define MNG_FN_PUTCHUNK_MEND 820 +#define MNG_FN_PUTCHUNK_LOOP 821 +#define MNG_FN_PUTCHUNK_ENDL 822 +#define MNG_FN_PUTCHUNK_DEFI 823 +#define MNG_FN_PUTCHUNK_BASI 824 +#define MNG_FN_PUTCHUNK_CLON 825 +#define MNG_FN_PUTCHUNK_PAST 826 +#define MNG_FN_PUTCHUNK_DISC 827 +#define MNG_FN_PUTCHUNK_BACK 828 +#define MNG_FN_PUTCHUNK_FRAM 829 +#define MNG_FN_PUTCHUNK_MOVE 830 +#define MNG_FN_PUTCHUNK_CLIP 831 +#define MNG_FN_PUTCHUNK_SHOW 832 +#define MNG_FN_PUTCHUNK_TERM 833 +#define MNG_FN_PUTCHUNK_SAVE 834 +#define MNG_FN_PUTCHUNK_SEEK 835 +#define MNG_FN_PUTCHUNK_EXPI 836 +#define MNG_FN_PUTCHUNK_FPRI 837 +#define MNG_FN_PUTCHUNK_NEED 838 +#define MNG_FN_PUTCHUNK_PHYG 839 +#define MNG_FN_PUTCHUNK_JHDR 840 +#define MNG_FN_PUTCHUNK_JDAT 841 +#define MNG_FN_PUTCHUNK_JSEP 842 +#define MNG_FN_PUTCHUNK_DHDR 843 +#define MNG_FN_PUTCHUNK_PROM 844 +#define MNG_FN_PUTCHUNK_IPNG 845 +#define MNG_FN_PUTCHUNK_PPLT 846 +#define MNG_FN_PUTCHUNK_IJNG 847 +#define MNG_FN_PUTCHUNK_DROP 848 +#define MNG_FN_PUTCHUNK_DBYK 849 +#define MNG_FN_PUTCHUNK_ORDR 850 +#define MNG_FN_PUTCHUNK_UNKNOWN 851 +#define MNG_FN_PUTCHUNK_MAGN 852 +#define MNG_FN_PUTCHUNK_JDAA 853 +#define MNG_FN_PUTCHUNK_EVNT 854 +#define MNG_FN_PUTCHUNK_MPNG 855 + +#define MNG_FN_PUTCHUNK_PAST_SRC 881 +#define MNG_FN_PUTCHUNK_SAVE_ENTRY 882 +#define MNG_FN_PUTCHUNK_PPLT_ENTRY 883 +#define MNG_FN_PUTCHUNK_ORDR_ENTRY 884 +#define MNG_FN_PUTCHUNK_EVNT_ENTRY 885 +#define MNG_FN_PUTCHUNK_MPNG_FRAME 886 + +/* ************************************************************************** */ + +#define MNG_FN_GETIMGDATA_SEQ 901 +#define MNG_FN_GETIMGDATA_CHUNKSEQ 902 +#define MNG_FN_GETIMGDATA_CHUNK 903 + +#define MNG_FN_PUTIMGDATA_IHDR 951 +#define MNG_FN_PUTIMGDATA_JHDR 952 +#define MNG_FN_PUTIMGDATA_BASI 953 +#define MNG_FN_PUTIMGDATA_DHDR 954 + +#define MNG_FN_UPDATEMNGHEADER 981 +#define MNG_FN_UPDATEMNGSIMPLICITY 982 + +/* ************************************************************************** */ + +#define MNG_FN_PROCESS_RAW_CHUNK 1001 +#define MNG_FN_READ_GRAPHIC 1002 +#define MNG_FN_DROP_CHUNKS 1003 +#define MNG_FN_PROCESS_ERROR 1004 +#define MNG_FN_CLEAR_CMS 1005 +#define MNG_FN_DROP_OBJECTS 1006 +#define MNG_FN_READ_CHUNK 1007 +#define MNG_FN_LOAD_BKGDLAYER 1008 +#define MNG_FN_NEXT_FRAME 1009 +#define MNG_FN_NEXT_LAYER 1010 +#define MNG_FN_INTERFRAME_DELAY 1011 +#define MNG_FN_DISPLAY_IMAGE 1012 +#define MNG_FN_DROP_IMGOBJECTS 1013 +#define MNG_FN_DROP_ANIOBJECTS 1014 +#define MNG_FN_INFLATE_BUFFER 1015 +#define MNG_FN_DEFLATE_BUFFER 1016 +#define MNG_FN_WRITE_RAW_CHUNK 1017 +#define MNG_FN_WRITE_GRAPHIC 1018 +#define MNG_FN_SAVE_STATE 1019 +#define MNG_FN_RESTORE_STATE 1020 +#define MNG_FN_DROP_SAVEDATA 1021 +#define MNG_FN_EXECUTE_DELTA_IMAGE 1022 +#define MNG_FN_PROCESS_DISPLAY 1023 +#define MNG_FN_CLEAR_CANVAS 1024 +#define MNG_FN_READ_DATABUFFER 1025 +#define MNG_FN_STORE_ERROR 1026 +#define MNG_FN_DROP_INVALID_OBJECTS 1027 +#define MNG_FN_RELEASE_PUSHDATA 1028 +#define MNG_FN_READ_DATA 1029 +#define MNG_FN_READ_CHUNK_CRC 1030 +#define MNG_FN_RELEASE_PUSHCHUNK 1031 + +/* ************************************************************************** */ + +#define MNG_FN_DISPLAY_RGB8 1101 +#define MNG_FN_DISPLAY_RGBA8 1102 +#define MNG_FN_DISPLAY_ARGB8 1103 +#define MNG_FN_DISPLAY_BGR8 1104 +#define MNG_FN_DISPLAY_BGRA8 1105 +#define MNG_FN_DISPLAY_ABGR8 1106 +#define MNG_FN_DISPLAY_RGB16 1107 +#define MNG_FN_DISPLAY_RGBA16 1108 +#define MNG_FN_DISPLAY_ARGB16 1109 +#define MNG_FN_DISPLAY_BGR16 1110 +#define MNG_FN_DISPLAY_BGRA16 1111 +#define MNG_FN_DISPLAY_ABGR16 1112 +#define MNG_FN_DISPLAY_INDEX8 1113 +#define MNG_FN_DISPLAY_INDEXA8 1114 +#define MNG_FN_DISPLAY_AINDEX8 1115 +#define MNG_FN_DISPLAY_GRAY8 1116 +#define MNG_FN_DISPLAY_GRAY16 1117 +#define MNG_FN_DISPLAY_GRAYA8 1118 +#define MNG_FN_DISPLAY_GRAYA16 1119 +#define MNG_FN_DISPLAY_AGRAY8 1120 +#define MNG_FN_DISPLAY_AGRAY16 1121 +#define MNG_FN_DISPLAY_DX15 1122 +#define MNG_FN_DISPLAY_DX16 1123 +#define MNG_FN_DISPLAY_RGB8_A8 1124 +#define MNG_FN_DISPLAY_BGRA8PM 1125 +#define MNG_FN_DISPLAY_BGRX8 1126 +#define MNG_FN_DISPLAY_RGB565 1127 +#define MNG_FN_DISPLAY_RGBA565 1128 +#define MNG_FN_DISPLAY_BGR565 1129 +#define MNG_FN_DISPLAY_BGRA565 1130 +#define MNG_FN_DISPLAY_RGBA8_PM 1131 +#define MNG_FN_DISPLAY_ARGB8_PM 1132 +#define MNG_FN_DISPLAY_ABGR8_PM 1133 +#define MNG_FN_DISPLAY_BGR565_A8 1134 +#define MNG_FN_DISPLAY_RGB555 1135 +#define MNG_FN_DISPLAY_BGR555 1136 + +/* ************************************************************************** */ + +#define MNG_FN_INIT_FULL_CMS 1201 +#define MNG_FN_CORRECT_FULL_CMS 1202 +#define MNG_FN_INIT_GAMMA_ONLY 1204 +#define MNG_FN_CORRECT_GAMMA_ONLY 1205 +#define MNG_FN_CORRECT_APP_CMS 1206 +#define MNG_FN_INIT_FULL_CMS_OBJ 1207 +#define MNG_FN_INIT_GAMMA_ONLY_OBJ 1208 +#define MNG_FN_INIT_APP_CMS 1209 +#define MNG_FN_INIT_APP_CMS_OBJ 1210 + +/* ************************************************************************** */ + +#define MNG_FN_PROCESS_G1 1301 +#define MNG_FN_PROCESS_G2 1302 +#define MNG_FN_PROCESS_G4 1303 +#define MNG_FN_PROCESS_G8 1304 +#define MNG_FN_PROCESS_G16 1305 +#define MNG_FN_PROCESS_RGB8 1306 +#define MNG_FN_PROCESS_RGB16 1307 +#define MNG_FN_PROCESS_IDX1 1308 +#define MNG_FN_PROCESS_IDX2 1309 +#define MNG_FN_PROCESS_IDX4 1310 +#define MNG_FN_PROCESS_IDX8 1311 +#define MNG_FN_PROCESS_GA8 1312 +#define MNG_FN_PROCESS_GA16 1313 +#define MNG_FN_PROCESS_RGBA8 1314 +#define MNG_FN_PROCESS_RGBA16 1315 + +/* ************************************************************************** */ + +#define MNG_FN_INIT_G1_NI 1401 +#define MNG_FN_INIT_G1_I 1402 +#define MNG_FN_INIT_G2_NI 1403 +#define MNG_FN_INIT_G2_I 1404 +#define MNG_FN_INIT_G4_NI 1405 +#define MNG_FN_INIT_G4_I 1406 +#define MNG_FN_INIT_G8_NI 1407 +#define MNG_FN_INIT_G8_I 1408 +#define MNG_FN_INIT_G16_NI 1409 +#define MNG_FN_INIT_G16_I 1410 +#define MNG_FN_INIT_RGB8_NI 1411 +#define MNG_FN_INIT_RGB8_I 1412 +#define MNG_FN_INIT_RGB16_NI 1413 +#define MNG_FN_INIT_RGB16_I 1414 +#define MNG_FN_INIT_IDX1_NI 1415 +#define MNG_FN_INIT_IDX1_I 1416 +#define MNG_FN_INIT_IDX2_NI 1417 +#define MNG_FN_INIT_IDX2_I 1418 +#define MNG_FN_INIT_IDX4_NI 1419 +#define MNG_FN_INIT_IDX4_I 1420 +#define MNG_FN_INIT_IDX8_NI 1421 +#define MNG_FN_INIT_IDX8_I 1422 +#define MNG_FN_INIT_GA8_NI 1423 +#define MNG_FN_INIT_GA8_I 1424 +#define MNG_FN_INIT_GA16_NI 1425 +#define MNG_FN_INIT_GA16_I 1426 +#define MNG_FN_INIT_RGBA8_NI 1427 +#define MNG_FN_INIT_RGBA8_I 1428 +#define MNG_FN_INIT_RGBA16_NI 1429 +#define MNG_FN_INIT_RGBA16_I 1430 + +#define MNG_FN_INIT_ROWPROC 1497 +#define MNG_FN_NEXT_ROW 1498 +#define MNG_FN_CLEANUP_ROWPROC 1499 + +/* ************************************************************************** */ + +#define MNG_FN_FILTER_A_ROW 1501 +#define MNG_FN_FILTER_SUB 1502 +#define MNG_FN_FILTER_UP 1503 +#define MNG_FN_FILTER_AVERAGE 1504 +#define MNG_FN_FILTER_PAETH 1505 + +#define MNG_FN_INIT_ROWDIFFERING 1551 +#define MNG_FN_DIFFER_G1 1552 +#define MNG_FN_DIFFER_G2 1553 +#define MNG_FN_DIFFER_G4 1554 +#define MNG_FN_DIFFER_G8 1555 +#define MNG_FN_DIFFER_G16 1556 +#define MNG_FN_DIFFER_RGB8 1557 +#define MNG_FN_DIFFER_RGB16 1558 +#define MNG_FN_DIFFER_IDX1 1559 +#define MNG_FN_DIFFER_IDX2 1560 +#define MNG_FN_DIFFER_IDX4 1561 +#define MNG_FN_DIFFER_IDX8 1562 +#define MNG_FN_DIFFER_GA8 1563 +#define MNG_FN_DIFFER_GA16 1564 +#define MNG_FN_DIFFER_RGBA8 1565 +#define MNG_FN_DIFFER_RGBA16 1566 + +/* ************************************************************************** */ + +#define MNG_FN_CREATE_IMGDATAOBJECT 1601 +#define MNG_FN_FREE_IMGDATAOBJECT 1602 +#define MNG_FN_CLONE_IMGDATAOBJECT 1603 +#define MNG_FN_CREATE_IMGOBJECT 1604 +#define MNG_FN_FREE_IMGOBJECT 1605 +#define MNG_FN_FIND_IMGOBJECT 1606 +#define MNG_FN_CLONE_IMGOBJECT 1607 +#define MNG_FN_RESET_OBJECTDETAILS 1608 +#define MNG_FN_RENUM_IMGOBJECT 1609 +#define MNG_FN_PROMOTE_IMGOBJECT 1610 +#define MNG_FN_MAGNIFY_IMGOBJECT 1611 +#define MNG_FN_COLORCORRECT_OBJECT 1612 + +/* ************************************************************************** */ + +#define MNG_FN_STORE_G1 1701 +#define MNG_FN_STORE_G2 1702 +#define MNG_FN_STORE_G4 1703 +#define MNG_FN_STORE_G8 1704 +#define MNG_FN_STORE_G16 1705 +#define MNG_FN_STORE_RGB8 1706 +#define MNG_FN_STORE_RGB16 1707 +#define MNG_FN_STORE_IDX1 1708 +#define MNG_FN_STORE_IDX2 1709 +#define MNG_FN_STORE_IDX4 1710 +#define MNG_FN_STORE_IDX8 1711 +#define MNG_FN_STORE_GA8 1712 +#define MNG_FN_STORE_GA16 1713 +#define MNG_FN_STORE_RGBA8 1714 +#define MNG_FN_STORE_RGBA16 1715 + +#define MNG_FN_RETRIEVE_G8 1751 +#define MNG_FN_RETRIEVE_G16 1752 +#define MNG_FN_RETRIEVE_RGB8 1753 +#define MNG_FN_RETRIEVE_RGB16 1754 +#define MNG_FN_RETRIEVE_IDX8 1755 +#define MNG_FN_RETRIEVE_GA8 1756 +#define MNG_FN_RETRIEVE_GA16 1757 +#define MNG_FN_RETRIEVE_RGBA8 1758 +#define MNG_FN_RETRIEVE_RGBA16 1759 + +#define MNG_FN_DELTA_G1 1771 +#define MNG_FN_DELTA_G2 1772 +#define MNG_FN_DELTA_G4 1773 +#define MNG_FN_DELTA_G8 1774 +#define MNG_FN_DELTA_G16 1775 +#define MNG_FN_DELTA_RGB8 1776 +#define MNG_FN_DELTA_RGB16 1777 +#define MNG_FN_DELTA_IDX1 1778 +#define MNG_FN_DELTA_IDX2 1779 +#define MNG_FN_DELTA_IDX4 1780 +#define MNG_FN_DELTA_IDX8 1781 +#define MNG_FN_DELTA_GA8 1782 +#define MNG_FN_DELTA_GA16 1783 +#define MNG_FN_DELTA_RGBA8 1784 +#define MNG_FN_DELTA_RGBA16 1785 + +/* ************************************************************************** */ + +#define MNG_FN_CREATE_ANI_LOOP 1801 +#define MNG_FN_CREATE_ANI_ENDL 1802 +#define MNG_FN_CREATE_ANI_DEFI 1803 +#define MNG_FN_CREATE_ANI_BASI 1804 +#define MNG_FN_CREATE_ANI_CLON 1805 +#define MNG_FN_CREATE_ANI_PAST 1806 +#define MNG_FN_CREATE_ANI_DISC 1807 +#define MNG_FN_CREATE_ANI_BACK 1808 +#define MNG_FN_CREATE_ANI_FRAM 1809 +#define MNG_FN_CREATE_ANI_MOVE 1810 +#define MNG_FN_CREATE_ANI_CLIP 1811 +#define MNG_FN_CREATE_ANI_SHOW 1812 +#define MNG_FN_CREATE_ANI_TERM 1813 +#define MNG_FN_CREATE_ANI_SAVE 1814 +#define MNG_FN_CREATE_ANI_SEEK 1815 +#define MNG_FN_CREATE_ANI_GAMA 1816 +#define MNG_FN_CREATE_ANI_CHRM 1817 +#define MNG_FN_CREATE_ANI_SRGB 1818 +#define MNG_FN_CREATE_ANI_ICCP 1819 +#define MNG_FN_CREATE_ANI_PLTE 1820 +#define MNG_FN_CREATE_ANI_TRNS 1821 +#define MNG_FN_CREATE_ANI_BKGD 1822 +#define MNG_FN_CREATE_ANI_DHDR 1823 +#define MNG_FN_CREATE_ANI_PROM 1824 +#define MNG_FN_CREATE_ANI_IPNG 1825 +#define MNG_FN_CREATE_ANI_IJNG 1826 +#define MNG_FN_CREATE_ANI_PPLT 1827 +#define MNG_FN_CREATE_ANI_MAGN 1828 + +#define MNG_FN_CREATE_ANI_IMAGE 1891 +#define MNG_FN_CREATE_EVENT 1892 + +/* ************************************************************************** */ + +#define MNG_FN_FREE_ANI_LOOP 1901 +#define MNG_FN_FREE_ANI_ENDL 1902 +#define MNG_FN_FREE_ANI_DEFI 1903 +#define MNG_FN_FREE_ANI_BASI 1904 +#define MNG_FN_FREE_ANI_CLON 1905 +#define MNG_FN_FREE_ANI_PAST 1906 +#define MNG_FN_FREE_ANI_DISC 1907 +#define MNG_FN_FREE_ANI_BACK 1908 +#define MNG_FN_FREE_ANI_FRAM 1909 +#define MNG_FN_FREE_ANI_MOVE 1910 +#define MNG_FN_FREE_ANI_CLIP 1911 +#define MNG_FN_FREE_ANI_SHOW 1912 +#define MNG_FN_FREE_ANI_TERM 1913 +#define MNG_FN_FREE_ANI_SAVE 1914 +#define MNG_FN_FREE_ANI_SEEK 1915 +#define MNG_FN_FREE_ANI_GAMA 1916 +#define MNG_FN_FREE_ANI_CHRM 1917 +#define MNG_FN_FREE_ANI_SRGB 1918 +#define MNG_FN_FREE_ANI_ICCP 1919 +#define MNG_FN_FREE_ANI_PLTE 1920 +#define MNG_FN_FREE_ANI_TRNS 1921 +#define MNG_FN_FREE_ANI_BKGD 1922 +#define MNG_FN_FREE_ANI_DHDR 1923 +#define MNG_FN_FREE_ANI_PROM 1924 +#define MNG_FN_FREE_ANI_IPNG 1925 +#define MNG_FN_FREE_ANI_IJNG 1926 +#define MNG_FN_FREE_ANI_PPLT 1927 +#define MNG_FN_FREE_ANI_MAGN 1928 + +#define MNG_FN_FREE_ANI_IMAGE 1991 +#define MNG_FN_FREE_EVENT 1992 + +/* ************************************************************************** */ + +#define MNG_FN_PROCESS_ANI_LOOP 2001 +#define MNG_FN_PROCESS_ANI_ENDL 2002 +#define MNG_FN_PROCESS_ANI_DEFI 2003 +#define MNG_FN_PROCESS_ANI_BASI 2004 +#define MNG_FN_PROCESS_ANI_CLON 2005 +#define MNG_FN_PROCESS_ANI_PAST 2006 +#define MNG_FN_PROCESS_ANI_DISC 2007 +#define MNG_FN_PROCESS_ANI_BACK 2008 +#define MNG_FN_PROCESS_ANI_FRAM 2009 +#define MNG_FN_PROCESS_ANI_MOVE 2010 +#define MNG_FN_PROCESS_ANI_CLIP 2011 +#define MNG_FN_PROCESS_ANI_SHOW 2012 +#define MNG_FN_PROCESS_ANI_TERM 2013 +#define MNG_FN_PROCESS_ANI_SAVE 2014 +#define MNG_FN_PROCESS_ANI_SEEK 2015 +#define MNG_FN_PROCESS_ANI_GAMA 2016 +#define MNG_FN_PROCESS_ANI_CHRM 2017 +#define MNG_FN_PROCESS_ANI_SRGB 2018 +#define MNG_FN_PROCESS_ANI_ICCP 2019 +#define MNG_FN_PROCESS_ANI_PLTE 2020 +#define MNG_FN_PROCESS_ANI_TRNS 2021 +#define MNG_FN_PROCESS_ANI_BKGD 2022 +#define MNG_FN_PROCESS_ANI_DHDR 2023 +#define MNG_FN_PROCESS_ANI_PROM 2024 +#define MNG_FN_PROCESS_ANI_IPNG 2025 +#define MNG_FN_PROCESS_ANI_IJNG 2026 +#define MNG_FN_PROCESS_ANI_PPLT 2027 +#define MNG_FN_PROCESS_ANI_MAGN 2028 + +#define MNG_FN_PROCESS_ANI_IMAGE 2091 +#define MNG_FN_PROCESS_EVENT 2092 + +/* ************************************************************************** */ + +#define MNG_FN_RESTORE_BACKIMAGE 2101 +#define MNG_FN_RESTORE_BACKCOLOR 2102 +#define MNG_FN_RESTORE_BGCOLOR 2103 +#define MNG_FN_RESTORE_RGB8 2104 +#define MNG_FN_RESTORE_BGR8 2105 +#define MNG_FN_RESTORE_BKGD 2106 +#define MNG_FN_RESTORE_BGRX8 2107 +#define MNG_FN_RESTORE_RGB565 2108 +#define MNG_FN_RESTORE_BGR565 2109 + +/* ************************************************************************** */ + +#define MNG_FN_INIT_IHDR 2201 +#define MNG_FN_INIT_PLTE 2202 +#define MNG_FN_INIT_IDAT 2203 +#define MNG_FN_INIT_IEND 2204 +#define MNG_FN_INIT_TRNS 2205 +#define MNG_FN_INIT_GAMA 2206 +#define MNG_FN_INIT_CHRM 2207 +#define MNG_FN_INIT_SRGB 2208 +#define MNG_FN_INIT_ICCP 2209 +#define MNG_FN_INIT_TEXT 2210 +#define MNG_FN_INIT_ZTXT 2211 +#define MNG_FN_INIT_ITXT 2212 +#define MNG_FN_INIT_BKGD 2213 +#define MNG_FN_INIT_PHYS 2214 +#define MNG_FN_INIT_SBIT 2215 +#define MNG_FN_INIT_SPLT 2216 +#define MNG_FN_INIT_HIST 2217 +#define MNG_FN_INIT_TIME 2218 +#define MNG_FN_INIT_MHDR 2219 +#define MNG_FN_INIT_MEND 2220 +#define MNG_FN_INIT_LOOP 2221 +#define MNG_FN_INIT_ENDL 2222 +#define MNG_FN_INIT_DEFI 2223 +#define MNG_FN_INIT_BASI 2224 +#define MNG_FN_INIT_CLON 2225 +#define MNG_FN_INIT_PAST 2226 +#define MNG_FN_INIT_DISC 2227 +#define MNG_FN_INIT_BACK 2228 +#define MNG_FN_INIT_FRAM 2229 +#define MNG_FN_INIT_MOVE 2230 +#define MNG_FN_INIT_CLIP 2231 +#define MNG_FN_INIT_SHOW 2232 +#define MNG_FN_INIT_TERM 2233 +#define MNG_FN_INIT_SAVE 2234 +#define MNG_FN_INIT_SEEK 2235 +#define MNG_FN_INIT_EXPI 2236 +#define MNG_FN_INIT_FPRI 2237 +#define MNG_FN_INIT_NEED 2238 +#define MNG_FN_INIT_PHYG 2239 +#define MNG_FN_INIT_JHDR 2240 +#define MNG_FN_INIT_JDAT 2241 +#define MNG_FN_INIT_JSEP 2242 +#define MNG_FN_INIT_DHDR 2243 +#define MNG_FN_INIT_PROM 2244 +#define MNG_FN_INIT_IPNG 2245 +#define MNG_FN_INIT_PPLT 2246 +#define MNG_FN_INIT_IJNG 2247 +#define MNG_FN_INIT_DROP 2248 +#define MNG_FN_INIT_DBYK 2249 +#define MNG_FN_INIT_ORDR 2250 +#define MNG_FN_INIT_UNKNOWN 2251 +#define MNG_FN_INIT_MAGN 2252 +#define MNG_FN_INIT_JDAA 2253 +#define MNG_FN_INIT_EVNT 2254 +#define MNG_FN_INIT_MPNG 2255 + +/* ************************************************************************** */ + +#define MNG_FN_ASSIGN_IHDR 2301 +#define MNG_FN_ASSIGN_PLTE 2302 +#define MNG_FN_ASSIGN_IDAT 2303 +#define MNG_FN_ASSIGN_IEND 2304 +#define MNG_FN_ASSIGN_TRNS 2305 +#define MNG_FN_ASSIGN_GAMA 2306 +#define MNG_FN_ASSIGN_CHRM 2307 +#define MNG_FN_ASSIGN_SRGB 2308 +#define MNG_FN_ASSIGN_ICCP 2309 +#define MNG_FN_ASSIGN_TEXT 2310 +#define MNG_FN_ASSIGN_ZTXT 2311 +#define MNG_FN_ASSIGN_ITXT 2312 +#define MNG_FN_ASSIGN_BKGD 2313 +#define MNG_FN_ASSIGN_PHYS 2314 +#define MNG_FN_ASSIGN_SBIT 2315 +#define MNG_FN_ASSIGN_SPLT 2316 +#define MNG_FN_ASSIGN_HIST 2317 +#define MNG_FN_ASSIGN_TIME 2318 +#define MNG_FN_ASSIGN_MHDR 2319 +#define MNG_FN_ASSIGN_MEND 2320 +#define MNG_FN_ASSIGN_LOOP 2321 +#define MNG_FN_ASSIGN_ENDL 2322 +#define MNG_FN_ASSIGN_DEFI 2323 +#define MNG_FN_ASSIGN_BASI 2324 +#define MNG_FN_ASSIGN_CLON 2325 +#define MNG_FN_ASSIGN_PAST 2326 +#define MNG_FN_ASSIGN_DISC 2327 +#define MNG_FN_ASSIGN_BACK 2328 +#define MNG_FN_ASSIGN_FRAM 2329 +#define MNG_FN_ASSIGN_MOVE 2330 +#define MNG_FN_ASSIGN_CLIP 2331 +#define MNG_FN_ASSIGN_SHOW 2332 +#define MNG_FN_ASSIGN_TERM 2333 +#define MNG_FN_ASSIGN_SAVE 2334 +#define MNG_FN_ASSIGN_SEEK 2335 +#define MNG_FN_ASSIGN_EXPI 2336 +#define MNG_FN_ASSIGN_FPRI 2337 +#define MNG_FN_ASSIGN_NEED 2338 +#define MNG_FN_ASSIGN_PHYG 2339 +#define MNG_FN_ASSIGN_JHDR 2340 +#define MNG_FN_ASSIGN_JDAT 2341 +#define MNG_FN_ASSIGN_JSEP 2342 +#define MNG_FN_ASSIGN_DHDR 2343 +#define MNG_FN_ASSIGN_PROM 2344 +#define MNG_FN_ASSIGN_IPNG 2345 +#define MNG_FN_ASSIGN_PPLT 2346 +#define MNG_FN_ASSIGN_IJNG 2347 +#define MNG_FN_ASSIGN_DROP 2348 +#define MNG_FN_ASSIGN_DBYK 2349 +#define MNG_FN_ASSIGN_ORDR 2350 +#define MNG_FN_ASSIGN_UNKNOWN 2351 +#define MNG_FN_ASSIGN_MAGN 2352 +#define MNG_FN_ASSIGN_JDAA 2353 +#define MNG_FN_ASSIGN_EVNT 2354 +#define MNG_FN_ASSIGN_MPNG 2355 + +/* ************************************************************************** */ + +#define MNG_FN_FREE_IHDR 2401 +#define MNG_FN_FREE_PLTE 2402 +#define MNG_FN_FREE_IDAT 2403 +#define MNG_FN_FREE_IEND 2404 +#define MNG_FN_FREE_TRNS 2405 +#define MNG_FN_FREE_GAMA 2406 +#define MNG_FN_FREE_CHRM 2407 +#define MNG_FN_FREE_SRGB 2408 +#define MNG_FN_FREE_ICCP 2409 +#define MNG_FN_FREE_TEXT 2410 +#define MNG_FN_FREE_ZTXT 2411 +#define MNG_FN_FREE_ITXT 2412 +#define MNG_FN_FREE_BKGD 2413 +#define MNG_FN_FREE_PHYS 2414 +#define MNG_FN_FREE_SBIT 2415 +#define MNG_FN_FREE_SPLT 2416 +#define MNG_FN_FREE_HIST 2417 +#define MNG_FN_FREE_TIME 2418 +#define MNG_FN_FREE_MHDR 2419 +#define MNG_FN_FREE_MEND 2420 +#define MNG_FN_FREE_LOOP 2421 +#define MNG_FN_FREE_ENDL 2422 +#define MNG_FN_FREE_DEFI 2423 +#define MNG_FN_FREE_BASI 2424 +#define MNG_FN_FREE_CLON 2425 +#define MNG_FN_FREE_PAST 2426 +#define MNG_FN_FREE_DISC 2427 +#define MNG_FN_FREE_BACK 2428 +#define MNG_FN_FREE_FRAM 2429 +#define MNG_FN_FREE_MOVE 2430 +#define MNG_FN_FREE_CLIP 2431 +#define MNG_FN_FREE_SHOW 2432 +#define MNG_FN_FREE_TERM 2433 +#define MNG_FN_FREE_SAVE 2434 +#define MNG_FN_FREE_SEEK 2435 +#define MNG_FN_FREE_EXPI 2436 +#define MNG_FN_FREE_FPRI 2437 +#define MNG_FN_FREE_NEED 2438 +#define MNG_FN_FREE_PHYG 2439 +#define MNG_FN_FREE_JHDR 2440 +#define MNG_FN_FREE_JDAT 2441 +#define MNG_FN_FREE_JSEP 2442 +#define MNG_FN_FREE_DHDR 2443 +#define MNG_FN_FREE_PROM 2444 +#define MNG_FN_FREE_IPNG 2445 +#define MNG_FN_FREE_PPLT 2446 +#define MNG_FN_FREE_IJNG 2447 +#define MNG_FN_FREE_DROP 2448 +#define MNG_FN_FREE_DBYK 2449 +#define MNG_FN_FREE_ORDR 2450 +#define MNG_FN_FREE_UNKNOWN 2451 +#define MNG_FN_FREE_MAGN 2452 +#define MNG_FN_FREE_JDAA 2453 +#define MNG_FN_FREE_EVNT 2454 +#define MNG_FN_FREE_MPNG 2455 + +/* ************************************************************************** */ + +#define MNG_FN_READ_IHDR 2601 +#define MNG_FN_READ_PLTE 2602 +#define MNG_FN_READ_IDAT 2603 +#define MNG_FN_READ_IEND 2604 +#define MNG_FN_READ_TRNS 2605 +#define MNG_FN_READ_GAMA 2606 +#define MNG_FN_READ_CHRM 2607 +#define MNG_FN_READ_SRGB 2608 +#define MNG_FN_READ_ICCP 2609 +#define MNG_FN_READ_TEXT 2610 +#define MNG_FN_READ_ZTXT 2611 +#define MNG_FN_READ_ITXT 2612 +#define MNG_FN_READ_BKGD 2613 +#define MNG_FN_READ_PHYS 2614 +#define MNG_FN_READ_SBIT 2615 +#define MNG_FN_READ_SPLT 2616 +#define MNG_FN_READ_HIST 2617 +#define MNG_FN_READ_TIME 2618 +#define MNG_FN_READ_MHDR 2619 +#define MNG_FN_READ_MEND 2620 +#define MNG_FN_READ_LOOP 2621 +#define MNG_FN_READ_ENDL 2622 +#define MNG_FN_READ_DEFI 2623 +#define MNG_FN_READ_BASI 2624 +#define MNG_FN_READ_CLON 2625 +#define MNG_FN_READ_PAST 2626 +#define MNG_FN_READ_DISC 2627 +#define MNG_FN_READ_BACK 2628 +#define MNG_FN_READ_FRAM 2629 +#define MNG_FN_READ_MOVE 2630 +#define MNG_FN_READ_CLIP 2631 +#define MNG_FN_READ_SHOW 2632 +#define MNG_FN_READ_TERM 2633 +#define MNG_FN_READ_SAVE 2634 +#define MNG_FN_READ_SEEK 2635 +#define MNG_FN_READ_EXPI 2636 +#define MNG_FN_READ_FPRI 2637 +#define MNG_FN_READ_NEED 2638 +#define MNG_FN_READ_PHYG 2639 +#define MNG_FN_READ_JHDR 2640 +#define MNG_FN_READ_JDAT 2641 +#define MNG_FN_READ_JSEP 2642 +#define MNG_FN_READ_DHDR 2643 +#define MNG_FN_READ_PROM 2644 +#define MNG_FN_READ_IPNG 2645 +#define MNG_FN_READ_PPLT 2646 +#define MNG_FN_READ_IJNG 2647 +#define MNG_FN_READ_DROP 2648 +#define MNG_FN_READ_DBYK 2649 +#define MNG_FN_READ_ORDR 2650 +#define MNG_FN_READ_UNKNOWN 2651 +#define MNG_FN_READ_MAGN 2652 +#define MNG_FN_READ_JDAA 2653 +#define MNG_FN_READ_EVNT 2654 +#define MNG_FN_READ_MPNG 2655 + +/* ************************************************************************** */ + +#define MNG_FN_WRITE_IHDR 2801 +#define MNG_FN_WRITE_PLTE 2802 +#define MNG_FN_WRITE_IDAT 2803 +#define MNG_FN_WRITE_IEND 2804 +#define MNG_FN_WRITE_TRNS 2805 +#define MNG_FN_WRITE_GAMA 2806 +#define MNG_FN_WRITE_CHRM 2807 +#define MNG_FN_WRITE_SRGB 2808 +#define MNG_FN_WRITE_ICCP 2809 +#define MNG_FN_WRITE_TEXT 2810 +#define MNG_FN_WRITE_ZTXT 2811 +#define MNG_FN_WRITE_ITXT 2812 +#define MNG_FN_WRITE_BKGD 2813 +#define MNG_FN_WRITE_PHYS 2814 +#define MNG_FN_WRITE_SBIT 2815 +#define MNG_FN_WRITE_SPLT 2816 +#define MNG_FN_WRITE_HIST 2817 +#define MNG_FN_WRITE_TIME 2818 +#define MNG_FN_WRITE_MHDR 2819 +#define MNG_FN_WRITE_MEND 2820 +#define MNG_FN_WRITE_LOOP 2821 +#define MNG_FN_WRITE_ENDL 2822 +#define MNG_FN_WRITE_DEFI 2823 +#define MNG_FN_WRITE_BASI 2824 +#define MNG_FN_WRITE_CLON 2825 +#define MNG_FN_WRITE_PAST 2826 +#define MNG_FN_WRITE_DISC 2827 +#define MNG_FN_WRITE_BACK 2828 +#define MNG_FN_WRITE_FRAM 2829 +#define MNG_FN_WRITE_MOVE 2830 +#define MNG_FN_WRITE_CLIP 2831 +#define MNG_FN_WRITE_SHOW 2832 +#define MNG_FN_WRITE_TERM 2833 +#define MNG_FN_WRITE_SAVE 2834 +#define MNG_FN_WRITE_SEEK 2835 +#define MNG_FN_WRITE_EXPI 2836 +#define MNG_FN_WRITE_FPRI 2837 +#define MNG_FN_WRITE_NEED 2838 +#define MNG_FN_WRITE_PHYG 2839 +#define MNG_FN_WRITE_JHDR 2840 +#define MNG_FN_WRITE_JDAT 2841 +#define MNG_FN_WRITE_JSEP 2842 +#define MNG_FN_WRITE_DHDR 2843 +#define MNG_FN_WRITE_PROM 2844 +#define MNG_FN_WRITE_IPNG 2845 +#define MNG_FN_WRITE_PPLT 2846 +#define MNG_FN_WRITE_IJNG 2847 +#define MNG_FN_WRITE_DROP 2848 +#define MNG_FN_WRITE_DBYK 2849 +#define MNG_FN_WRITE_ORDR 2850 +#define MNG_FN_WRITE_UNKNOWN 2851 +#define MNG_FN_WRITE_MAGN 2852 +#define MNG_FN_WRITE_JDAA 2853 +#define MNG_FN_WRITE_EVNT 2854 +#define MNG_FN_WRITE_MPNG 2855 + +/* ************************************************************************** */ + +#define MNG_FN_ZLIB_INITIALIZE 3001 +#define MNG_FN_ZLIB_CLEANUP 3002 +#define MNG_FN_ZLIB_INFLATEINIT 3003 +#define MNG_FN_ZLIB_INFLATEROWS 3004 +#define MNG_FN_ZLIB_INFLATEDATA 3005 +#define MNG_FN_ZLIB_INFLATEFREE 3006 +#define MNG_FN_ZLIB_DEFLATEINIT 3007 +#define MNG_FN_ZLIB_DEFLATEROWS 3008 +#define MNG_FN_ZLIB_DEFLATEDATA 3009 +#define MNG_FN_ZLIB_DEFLATEFREE 3010 + +/* ************************************************************************** */ + +#define MNG_FN_PROCESS_DISPLAY_IHDR 3201 +#define MNG_FN_PROCESS_DISPLAY_PLTE 3202 +#define MNG_FN_PROCESS_DISPLAY_IDAT 3203 +#define MNG_FN_PROCESS_DISPLAY_IEND 3204 +#define MNG_FN_PROCESS_DISPLAY_TRNS 3205 +#define MNG_FN_PROCESS_DISPLAY_GAMA 3206 +#define MNG_FN_PROCESS_DISPLAY_CHRM 3207 +#define MNG_FN_PROCESS_DISPLAY_SRGB 3208 +#define MNG_FN_PROCESS_DISPLAY_ICCP 3209 +#define MNG_FN_PROCESS_DISPLAY_BKGD 3210 +#define MNG_FN_PROCESS_DISPLAY_PHYS 3211 +#define MNG_FN_PROCESS_DISPLAY_SBIT 3212 +#define MNG_FN_PROCESS_DISPLAY_SPLT 3213 +#define MNG_FN_PROCESS_DISPLAY_HIST 3214 +#define MNG_FN_PROCESS_DISPLAY_MHDR 3215 +#define MNG_FN_PROCESS_DISPLAY_MEND 3216 +#define MNG_FN_PROCESS_DISPLAY_LOOP 3217 +#define MNG_FN_PROCESS_DISPLAY_ENDL 3218 +#define MNG_FN_PROCESS_DISPLAY_DEFI 3219 +#define MNG_FN_PROCESS_DISPLAY_BASI 3220 +#define MNG_FN_PROCESS_DISPLAY_CLON 3221 +#define MNG_FN_PROCESS_DISPLAY_PAST 3222 +#define MNG_FN_PROCESS_DISPLAY_DISC 3223 +#define MNG_FN_PROCESS_DISPLAY_BACK 3224 +#define MNG_FN_PROCESS_DISPLAY_FRAM 3225 +#define MNG_FN_PROCESS_DISPLAY_MOVE 3226 +#define MNG_FN_PROCESS_DISPLAY_CLIP 3227 +#define MNG_FN_PROCESS_DISPLAY_SHOW 3228 +#define MNG_FN_PROCESS_DISPLAY_TERM 3229 +#define MNG_FN_PROCESS_DISPLAY_SAVE 3230 +#define MNG_FN_PROCESS_DISPLAY_SEEK 3231 +#define MNG_FN_PROCESS_DISPLAY_EXPI 3232 +#define MNG_FN_PROCESS_DISPLAY_FPRI 3233 +#define MNG_FN_PROCESS_DISPLAY_NEED 3234 +#define MNG_FN_PROCESS_DISPLAY_PHYG 3235 +#define MNG_FN_PROCESS_DISPLAY_JHDR 3236 +#define MNG_FN_PROCESS_DISPLAY_JDAT 3237 +#define MNG_FN_PROCESS_DISPLAY_JSEP 3238 +#define MNG_FN_PROCESS_DISPLAY_DHDR 3239 +#define MNG_FN_PROCESS_DISPLAY_PROM 3240 +#define MNG_FN_PROCESS_DISPLAY_IPNG 3241 +#define MNG_FN_PROCESS_DISPLAY_PPLT 3242 +#define MNG_FN_PROCESS_DISPLAY_IJNG 3243 +#define MNG_FN_PROCESS_DISPLAY_DROP 3244 +#define MNG_FN_PROCESS_DISPLAY_DBYK 3245 +#define MNG_FN_PROCESS_DISPLAY_ORDR 3246 +#define MNG_FN_PROCESS_DISPLAY_MAGN 3247 +#define MNG_FN_PROCESS_DISPLAY_JDAA 3248 + +/* ************************************************************************** */ + +#define MNG_FN_JPEG_INITIALIZE 3401 +#define MNG_FN_JPEG_CLEANUP 3402 +#define MNG_FN_JPEG_DECOMPRESSINIT 3403 +#define MNG_FN_JPEG_DECOMPRESSDATA 3404 +#define MNG_FN_JPEG_DECOMPRESSFREE 3405 + +#define MNG_FN_STORE_JPEG_G8 3501 +#define MNG_FN_STORE_JPEG_RGB8 3502 +#define MNG_FN_STORE_JPEG_G12 3503 +#define MNG_FN_STORE_JPEG_RGB12 3504 +#define MNG_FN_STORE_JPEG_GA8 3505 +#define MNG_FN_STORE_JPEG_RGBA8 3506 +#define MNG_FN_STORE_JPEG_GA12 3507 +#define MNG_FN_STORE_JPEG_RGBA12 3508 +#define MNG_FN_STORE_JPEG_G8_ALPHA 3509 +#define MNG_FN_STORE_JPEG_RGB8_ALPHA 3510 + +#define MNG_FN_INIT_JPEG_A1_NI 3511 +#define MNG_FN_INIT_JPEG_A2_NI 3512 +#define MNG_FN_INIT_JPEG_A4_NI 3513 +#define MNG_FN_INIT_JPEG_A8_NI 3514 +#define MNG_FN_INIT_JPEG_A16_NI 3515 + +#define MNG_FN_STORE_JPEG_G8_A1 3521 +#define MNG_FN_STORE_JPEG_G8_A2 3522 +#define MNG_FN_STORE_JPEG_G8_A4 3523 +#define MNG_FN_STORE_JPEG_G8_A8 3524 +#define MNG_FN_STORE_JPEG_G8_A16 3525 + +#define MNG_FN_STORE_JPEG_RGB8_A1 3531 +#define MNG_FN_STORE_JPEG_RGB8_A2 3532 +#define MNG_FN_STORE_JPEG_RGB8_A4 3533 +#define MNG_FN_STORE_JPEG_RGB8_A8 3534 +#define MNG_FN_STORE_JPEG_RGB8_A16 3535 + +#define MNG_FN_STORE_JPEG_G12_A1 3541 +#define MNG_FN_STORE_JPEG_G12_A2 3542 +#define MNG_FN_STORE_JPEG_G12_A4 3543 +#define MNG_FN_STORE_JPEG_G12_A8 3544 +#define MNG_FN_STORE_JPEG_G12_A16 3545 + +#define MNG_FN_STORE_JPEG_RGB12_A1 3551 +#define MNG_FN_STORE_JPEG_RGB12_A2 3552 +#define MNG_FN_STORE_JPEG_RGB12_A4 3553 +#define MNG_FN_STORE_JPEG_RGB12_A8 3554 +#define MNG_FN_STORE_JPEG_RGB12_A16 3555 + +#define MNG_FN_NEXT_JPEG_ALPHAROW 3591 +#define MNG_FN_NEXT_JPEG_ROW 3592 +#define MNG_FN_DISPLAY_JPEG_ROWS 3593 + +/* ************************************************************************** */ + +#define MNG_FN_MAGNIFY_G8_X1 3701 +#define MNG_FN_MAGNIFY_G8_X2 3702 +#define MNG_FN_MAGNIFY_RGB8_X1 3703 +#define MNG_FN_MAGNIFY_RGB8_X2 3704 +#define MNG_FN_MAGNIFY_GA8_X1 3705 +#define MNG_FN_MAGNIFY_GA8_X2 3706 +#define MNG_FN_MAGNIFY_GA8_X3 3707 +#define MNG_FN_MAGNIFY_GA8_X4 3708 +#define MNG_FN_MAGNIFY_RGBA8_X1 3709 +#define MNG_FN_MAGNIFY_RGBA8_X2 3710 +#define MNG_FN_MAGNIFY_RGBA8_X3 3711 +#define MNG_FN_MAGNIFY_RGBA8_X4 3712 +#define MNG_FN_MAGNIFY_G8_X3 3713 +#define MNG_FN_MAGNIFY_RGB8_X3 3714 +#define MNG_FN_MAGNIFY_GA8_X5 3715 +#define MNG_FN_MAGNIFY_RGBA8_X5 3716 + +#define MNG_FN_MAGNIFY_G16_X1 3725 +#define MNG_FN_MAGNIFY_G16_X2 3726 +#define MNG_FN_MAGNIFY_RGB16_X1 3727 +#define MNG_FN_MAGNIFY_RGB16_X2 3728 +#define MNG_FN_MAGNIFY_GA16_X1 3729 +#define MNG_FN_MAGNIFY_GA16_X2 3730 +#define MNG_FN_MAGNIFY_GA16_X3 3731 +#define MNG_FN_MAGNIFY_GA16_X4 3732 +#define MNG_FN_MAGNIFY_RGBA16_X1 3733 +#define MNG_FN_MAGNIFY_RGBA16_X2 3734 +#define MNG_FN_MAGNIFY_RGBA16_X3 3735 +#define MNG_FN_MAGNIFY_RGBA16_X4 3736 +#define MNG_FN_MAGNIFY_G16_X3 3737 +#define MNG_FN_MAGNIFY_RGB16_X3 3738 +#define MNG_FN_MAGNIFY_GA16_X5 3739 +#define MNG_FN_MAGNIFY_RGBA16_X5 3740 + +#define MNG_FN_MAGNIFY_G8_Y1 3751 +#define MNG_FN_MAGNIFY_G8_Y2 3752 +#define MNG_FN_MAGNIFY_RGB8_Y1 3753 +#define MNG_FN_MAGNIFY_RGB8_Y2 3754 +#define MNG_FN_MAGNIFY_GA8_Y1 3755 +#define MNG_FN_MAGNIFY_GA8_Y2 3756 +#define MNG_FN_MAGNIFY_GA8_Y3 3757 +#define MNG_FN_MAGNIFY_GA8_Y4 3758 +#define MNG_FN_MAGNIFY_RGBA8_Y1 3759 +#define MNG_FN_MAGNIFY_RGBA8_Y2 3760 +#define MNG_FN_MAGNIFY_RGBA8_Y3 3761 +#define MNG_FN_MAGNIFY_RGBA8_Y4 3762 +#define MNG_FN_MAGNIFY_G8_Y3 3763 +#define MNG_FN_MAGNIFY_RGB8_Y3 3764 +#define MNG_FN_MAGNIFY_GA8_Y5 3765 +#define MNG_FN_MAGNIFY_RGBA8_Y5 3766 + +#define MNG_FN_MAGNIFY_G16_Y1 3775 +#define MNG_FN_MAGNIFY_G16_Y2 3776 +#define MNG_FN_MAGNIFY_RGB16_Y1 3777 +#define MNG_FN_MAGNIFY_RGB16_Y2 3778 +#define MNG_FN_MAGNIFY_GA16_Y1 3779 +#define MNG_FN_MAGNIFY_GA16_Y2 3780 +#define MNG_FN_MAGNIFY_GA16_Y3 3781 +#define MNG_FN_MAGNIFY_GA16_Y4 3782 +#define MNG_FN_MAGNIFY_RGBA16_Y1 3783 +#define MNG_FN_MAGNIFY_RGBA16_Y2 3784 +#define MNG_FN_MAGNIFY_RGBA16_Y3 3785 +#define MNG_FN_MAGNIFY_RGBA16_Y4 3786 +#define MNG_FN_MAGNIFY_G16_Y3 3787 +#define MNG_FN_MAGNIFY_RGB16_Y3 3788 +#define MNG_FN_MAGNIFY_GA16_Y5 3789 +#define MNG_FN_MAGNIFY_RGBA16_Y5 3790 + +/* ************************************************************************** */ + +#define MNG_FN_DELTA_G1_G1 3801 +#define MNG_FN_DELTA_G2_G2 3802 +#define MNG_FN_DELTA_G4_G4 3803 +#define MNG_FN_DELTA_G8_G8 3804 +#define MNG_FN_DELTA_G16_G16 3805 +#define MNG_FN_DELTA_RGB8_RGB8 3806 +#define MNG_FN_DELTA_RGB16_RGB16 3807 +#define MNG_FN_DELTA_GA8_GA8 3808 +#define MNG_FN_DELTA_GA8_G8 3809 +#define MNG_FN_DELTA_GA8_A8 3810 +#define MNG_FN_DELTA_GA16_GA16 3811 +#define MNG_FN_DELTA_GA16_G16 3812 +#define MNG_FN_DELTA_GA16_A16 3813 +#define MNG_FN_DELTA_RGBA8_RGBA8 3814 +#define MNG_FN_DELTA_RGBA8_RGB8 3815 +#define MNG_FN_DELTA_RGBA8_A8 3816 +#define MNG_FN_DELTA_RGBA16_RGBA16 3817 +#define MNG_FN_DELTA_RGBA16_RGB16 3818 +#define MNG_FN_DELTA_RGBA16_A16 3819 + +#define MNG_FN_PROMOTE_G8_G8 3901 +#define MNG_FN_PROMOTE_G8_G16 3902 +#define MNG_FN_PROMOTE_G16_G16 3903 +#define MNG_FN_PROMOTE_G8_GA8 3904 +#define MNG_FN_PROMOTE_G8_GA16 3905 +#define MNG_FN_PROMOTE_G16_GA16 3906 +#define MNG_FN_PROMOTE_G8_RGB8 3907 +#define MNG_FN_PROMOTE_G8_RGB16 3908 +#define MNG_FN_PROMOTE_G16_RGB16 3909 +#define MNG_FN_PROMOTE_G8_RGBA8 3910 +#define MNG_FN_PROMOTE_G8_RGBA16 3911 +#define MNG_FN_PROMOTE_G16_RGBA16 3912 +#define MNG_FN_PROMOTE_GA8_GA16 3913 +#define MNG_FN_PROMOTE_GA8_RGBA8 3914 +#define MNG_FN_PROMOTE_GA8_RGBA16 3915 +#define MNG_FN_PROMOTE_GA16_RGBA16 3916 +#define MNG_FN_PROMOTE_RGB8_RGB16 3917 +#define MNG_FN_PROMOTE_RGB8_RGBA8 3918 +#define MNG_FN_PROMOTE_RGB8_RGBA16 3919 +#define MNG_FN_PROMOTE_RGB16_RGBA16 3920 +#define MNG_FN_PROMOTE_RGBA8_RGBA16 3921 +#define MNG_FN_PROMOTE_IDX8_RGB8 3922 +#define MNG_FN_PROMOTE_IDX8_RGB16 3923 +#define MNG_FN_PROMOTE_IDX8_RGBA8 3924 +#define MNG_FN_PROMOTE_IDX8_RGBA16 3925 + +#define MNG_FN_SCALE_G1_G2 4001 +#define MNG_FN_SCALE_G1_G4 4002 +#define MNG_FN_SCALE_G1_G8 4003 +#define MNG_FN_SCALE_G1_G16 4004 +#define MNG_FN_SCALE_G2_G4 4005 +#define MNG_FN_SCALE_G2_G8 4006 +#define MNG_FN_SCALE_G2_G16 4007 +#define MNG_FN_SCALE_G4_G8 4008 +#define MNG_FN_SCALE_G4_G16 4009 +#define MNG_FN_SCALE_G8_G16 4010 +#define MNG_FN_SCALE_GA8_GA16 4011 +#define MNG_FN_SCALE_RGB8_RGB16 4012 +#define MNG_FN_SCALE_RGBA8_RGBA16 4013 + +#define MNG_FN_SCALE_G2_G1 4021 +#define MNG_FN_SCALE_G4_G1 4022 +#define MNG_FN_SCALE_G8_G1 4023 +#define MNG_FN_SCALE_G16_G1 4024 +#define MNG_FN_SCALE_G4_G2 4025 +#define MNG_FN_SCALE_G8_G2 4026 +#define MNG_FN_SCALE_G16_G2 4027 +#define MNG_FN_SCALE_G8_G4 4028 +#define MNG_FN_SCALE_G16_G4 4029 +#define MNG_FN_SCALE_G16_G8 4030 +#define MNG_FN_SCALE_GA16_GA8 4031 +#define MNG_FN_SCALE_RGB16_RGB8 4032 +#define MNG_FN_SCALE_RGBA16_RGBA8 4033 + +#define MNG_FN_COMPOSEOVER_RGBA8 4501 +#define MNG_FN_COMPOSEOVER_RGBA16 4502 +#define MNG_FN_COMPOSEUNDER_RGBA8 4503 +#define MNG_FN_COMPOSEUNDER_RGBA16 4504 + +#define MNG_FN_FLIP_RGBA8 4521 +#define MNG_FN_FLIP_RGBA16 4522 +#define MNG_FN_TILE_RGBA8 4523 +#define MNG_FN_TILE_RGBA16 4524 + +/* ************************************************************************** */ +/* * * */ +/* * Trace string-table entry * */ +/* * * */ +/* ************************************************************************** */ + +typedef struct { + mng_uint32 iFunction; + mng_pchar zTracetext; + } mng_trace_entry; +typedef mng_trace_entry const * mng_trace_entryp; + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_TRACE_PROCS */ + +/* ************************************************************************** */ + +#endif /* _libmng_trace_h_ */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ + diff --git a/Engine/lib/lmng/libmng_types.h b/Engine/lib/lmng/libmng_types.h new file mode 100644 index 000000000..81fb29f52 --- /dev/null +++ b/Engine/lib/lmng/libmng_types.h @@ -0,0 +1,574 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_types.h copyright (c) 2000-2007 G.Juyn * */ +/* * version : 1.0.10 * */ +/* * * */ +/* * purpose : type specifications * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : Specification of the types used by the library * */ +/* * Creates platform-independant structure * */ +/* * * */ +/* * changes : 0.5.1 - 05/06/2000 - G.Juyn * */ +/* * - added iteratechunk callback definition * */ +/* * 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - improved definitions for DLL support * */ +/* * - added 8-bit palette definition * */ +/* * - added general array definitions * */ +/* * - added MNG_NULL definition * */ +/* * - changed strict-ANSI stuff * */ +/* * 0.5.1 - 05/11/2000 - G.Juyn * */ +/* * - changed most callback prototypes to allow the app * */ +/* * to report errors during callback processing * */ +/* * 0.5.1 - 05/16/2000 - G.Juyn * */ +/* * - moved standard header includes into this file * */ +/* * (stdlib/mem for mem-mngmt & math for fp gamma-calc) * */ +/* * * */ +/* * 0.5.2 - 05/18/2000 - G.Juyn * */ +/* * - B003 - fixed problem with being proprietary * */ +/* * to Borland platform * */ +/* * - added helper definitions for JNG (IJG-based) * */ +/* * - fixed support for IJGSRC6B * */ +/* * 0.5.2 - 05/24/2000 - G.Juyn * */ +/* * - added default IJG compression parameters and such * */ +/* * 0.5.2 - 05/31/2000 - G.Juyn * */ +/* * - fixed inclusion for memcpy (contributed by Tim Rowley) * */ +/* * - added mng_int32p (contributed by Tim Rowley) * */ +/* * 0.5.2 - 06/02/2000 - G.Juyn * */ +/* * - removed SWAP_ENDIAN reference (contributed by Tim Rowley)* */ +/* * - added getalphaline callback for RGB8_A8 canvasstyle * */ +/* * * */ +/* * 0.5.3 - 06/21/2000 - G.Juyn * */ +/* * - added speedtype to facilitate testing * */ +/* * 0.5.3 - 06/27/2000 - G.Juyn * */ +/* * - added typedef for mng_size_t * */ +/* * - changed size parameter for memory callbacks to * */ +/* * mng_size_t * */ +/* * 0.5.3 - 06/28/2000 - G.Juyn * */ +/* * - changed definition of 32-bit ints (64-bit platforms) * */ +/* * - changed definition of mng_handle (64-bit platforms) * */ +/* * 0.5.3 - 06/29/2000 - G.Juyn * */ +/* * - changed definition of mng_handle (again) * */ +/* * - swapped refresh parameters * */ +/* * - added inclusion of stdlib.h for abs() * */ +/* * * */ +/* * 0.9.0 - 06/30/2000 - G.Juyn * */ +/* * - changed refresh parameters to 'x,y,width,height' * */ +/* * 0.9.1 - 07/10/2000 - G.Juyn * */ +/* * - added suspendbuffer constants * */ +/* * 0.9.1 - 07/15/2000 - G.Juyn * */ +/* * - added callbacks for SAVE/SEEK processing * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 0.9.3 - 08/07/2000 - G.Juyn * */ +/* * - B111300 - fixup for improved portability * */ +/* * 0.9.3 - 08/12/2000 - G.Juyn * */ +/* * - added workaround for faulty PhotoShop iCCP chunk * */ +/* * 0.9.3 - 09/11/2000 - G.Juyn * */ +/* * - added export of zlib functions from windows dll * */ +/* * - fixed inclusion parameters once again to make those * */ +/* * external libs work together * */ +/* * - re-fixed fixed inclusion parameters * */ +/* * (these freeking libraries make me mad) * */ +/* * 0.9.3 - 10/11/2000 - G.Juyn * */ +/* * - added support for nEED * */ +/* * 0.9.3 - 10/17/2000 - G.Juyn * */ +/* * - added callback to process non-critical unknown chunks * */ +/* * * */ +/* * 0.9.4 - 11/20/2000 - R.Giles * */ +/* * - fixed inclusion of lcms header for non-windows platforms * */ +/* * 0.9.4 - 12/12/2000 - G.Juyn * */ +/* * - changed callback convention for MSVC (Thanks Chad) * */ +/* * 0.9.4 - 12/16/2000 - G.Juyn * */ +/* * - fixed mixup of data- & function-pointers (thanks Dimitri)* */ +/* * * */ +/* * 1.0.1 - 02/08/2001 - G.Juyn * */ +/* * - added MEND processing callback * */ +/* * * */ +/* * 1.0.2 - 06/23/2001 - G.Juyn * */ +/* * - added processterm callback * */ +/* * * */ +/* * 1.0.3 - 08/06/2001 - G.Juyn * */ +/* * - changed inclusion of lcms.h for Linux platforms * */ +/* * * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * * */ +/* * 1.0.6 - 04/11/2003 - G.Juyn * */ +/* * - B719420 - fixed several MNG_APP_CMS problems * */ +/* * 1.0.6 - 06/15/2003 - R.Giles * */ +/* * - lcms.h inclusion is generally no longer prefixed * */ +/* * 1.0.6 - 07/07/2003 - G. R-P. * */ +/* * - added png_imgtypes enumeration * */ +/* * * */ +/* * 1.0.7 - 03/10/2004 - G.R-P * */ +/* * - added conditionals around openstream/closestream * */ +/* * * */ +/* * 1.0.8 - 04/11/2004 - G.Juyn * */ +/* * - added data-push mechanisms for specialized decoders * */ +/* * 1.0.8 - 08/01/2004 - G.Juyn * */ +/* * - added support for 3+byte pixelsize for JPEG's * */ +/* * * */ +/* * 1.0.9 - 12/05/2004 - G.Juyn * */ +/* * - inclusion of zlib/lcms/ijgsrc6b with <> instead of "" * */ +/* * 1.0.9 - 12/06/2004 - G.Juyn * */ +/* * - added conditional MNG_OPTIMIZE_CHUNKREADER * */ +/* * * */ +/* * 1.0.10 - 04/08/2007 - G.Juyn * */ +/* * - added support for mPNG proposal * */ +/* * 1.0.10 - 04/12/2007 - G.Juyn * */ +/* * - added support for ANG proposal * */ +/* * * */ +/* ************************************************************************** */ + +#ifndef _libmng_types_h_ +#define _libmng_types_h_ + +/* ************************************************************************** */ + +#ifdef __BORLANDC__ +#pragma option -AT /* turn off strict ANSI-C for the moment */ +#endif + +#ifndef WIN32 +#if defined(_WIN32) || defined(__WIN32__) || defined(_Windows) || defined(_WINDOWS) +#define WIN32 /* gather them into a single define */ +#endif +#endif + +/* ************************************************************************** */ +/* * * */ +/* * Here's where the external & standard libs are embedded * */ +/* * * */ +/* * (it can be a bit of a pain in the lower-back to get them to work * */ +/* * together) * */ +/* * * */ +/* ************************************************************************** */ + +#ifdef WIN32 /* only include needed stuff */ +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#endif + +#ifdef MNG_USE_DLL +#ifdef MNG_SKIP_ZLIB +#undef MNG_INCLUDE_ZLIB +#endif +#ifdef MNG_SKIP_LCMS +#undef MNG_INCLUDE_LCMS +#endif +#ifdef MNG_SKIP_IJG6B +#undef MNG_INCLUDE_IJG6B +#endif +#endif + +#ifdef MNG_INCLUDE_ZLIB /* zlib by Mark Adler & Jean-loup Gailly */ +#include +#endif + +#ifdef MNG_INCLUDE_LCMS /* little cms by Marti Maria Saguer */ +#ifndef ZLIB_DLL +#undef FAR +#endif +#include +#endif /* MNG_INCLUDE_LCMS */ + +#ifdef MNG_INCLUDE_IJG6B /* IJG's jpgsrc6b */ +#include +#ifdef MNG_USE_SETJMP +#include /* needed for error-recovery (blergh) */ +#else +#ifdef WIN32 +#define USE_WINDOWS_MESSAGEBOX /* display a messagebox under Windoze */ +#endif +#endif /* MNG_USE_SETJMP */ +#ifdef FAR +#undef FAR /* possibly defined by zlib or lcms */ +#endif +#define JPEG_INTERNAL_OPTIONS /* for RGB_PIXELSIZE */ +#include /* all that for JPEG support :-) */ +#endif /* MNG_INCLUDE_IJG6B */ + +#if defined(MNG_INTERNAL_MEMMNGMT) || defined(MNG_INCLUDE_FILTERS) +#include /* "calloc" & "free" & "abs" */ +#endif + +#include /* get proper integer widths */ + +#ifdef WIN32 +#if defined __BORLANDC__ +#include /* defines "memcpy" for BCB */ +#else +#include /* defines "memcpy" for other win32 platforms */ +#endif +#include /* "strncmp" + "strcmp" */ +#else /* WIN32 */ +#ifdef BSD +#include /* defines "memcpy", etc for BSD (?) */ +#else +#include /* defines "memcpy", etc for all others (???) */ +#endif +#endif /* WIN32 */ + +#if defined(MNG_FULL_CMS) || defined(MNG_GAMMA_ONLY) || defined(MNG_APP_CMS) +#include /* fp gamma-calculation */ +#endif + +/* ************************************************************************** */ +/* * * */ +/* * Platform-dependant stuff * */ +/* * * */ +/* ************************************************************************** */ + +/* TODO: this may require some elaboration for other platforms; + only works with BCB for now */ + +#ifndef MNG_DLL +#if defined(MNG_BUILD_DLL) || defined(MNG_USE_DLL) +#define MNG_DLL +#endif +#endif + +#define MNG_LOCAL static + +#if defined(MNG_DLL) && defined(WIN32) /* setup DLL calling conventions */ +#define MNG_DECL __stdcall +#if defined(MNG_BUILD_DLL) +#define MNG_EXT __declspec(dllexport) +#elif defined(MNG_USE_DLL) +#define MNG_EXT __declspec(dllimport) +#else +#define MNG_EXT +#endif +#ifdef MNG_STRICT_ANSI +#undef MNG_STRICT_ANSI /* can't do strict-ANSI with this DLL-stuff */ +#endif +#else +#define MNG_DECL /* dummies for non-DLL */ +#define MNG_EXT +#endif /* MNG_DLL && WIN32 */ + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* now force ANSI-C from here on */ +#endif + +/* ************************************************************************** */ + +#if USHRT_MAX == 0xffffffffU /* get the proper 32-bit width !!! */ +typedef unsigned short mng_uint32; +typedef signed short mng_int32; +#elif UINT_MAX == 0xffffffffU +typedef unsigned int mng_uint32; +typedef signed int mng_int32; +#elif ULONG_MAX == 0xffffffffU +typedef unsigned long mng_uint32; +typedef signed long mng_int32; +#else +#error "Sorry, I can't find any 32-bit integers on this platform." +#endif + +typedef signed short mng_int16; /* other basic integers */ +typedef unsigned short mng_uint16; +typedef signed char mng_int8; +typedef unsigned char mng_uint8; + +typedef double mng_float; /* basic float */ + +typedef size_t mng_size_t; /* size field for memory allocation */ + +typedef char * mng_pchar; /* string */ +typedef void * mng_ptr; /* generic pointer */ +typedef void (*mng_fptr) (void); /* generic function pointer */ + +/* ************************************************************************** */ +/* * * */ +/* * Platform-independant from here * */ +/* * * */ +/* ************************************************************************** */ + +typedef mng_uint32 * mng_uint32p; /* pointer to unsigned longs */ +typedef mng_int32 * mng_int32p; /* pointer to longs */ +typedef mng_uint16 * mng_uint16p; /* pointer to unsigned words */ +typedef mng_uint8 * mng_uint8p; /* pointer to unsigned bytes */ + +typedef mng_int8 mng_bool; /* booleans */ + +struct mng_data_struct; +typedef struct mng_data_struct * mng_handle; /* generic handle */ + +typedef mng_int32 mng_retcode; /* generic return code */ +typedef mng_int32 mng_chunkid; /* 4-byte chunkname identifier */ +typedef mng_ptr mng_chunkp; /* pointer to a chunk-structure */ +typedef mng_ptr mng_objectp; /* pointer to an object-structure */ + +typedef mng_chunkid * mng_chunkidp; /* pointer to chunkid */ + +typedef struct { /* 8-bit palette element */ + mng_uint8 iRed; + mng_uint8 iGreen; + mng_uint8 iBlue; + } mng_palette8e; +typedef mng_palette8e mng_palette8[256]; /* 8-bit palette */ +typedef mng_palette8e * mng_palette8ep; + +typedef mng_uint8 mng_uint8arr[256]; /* generic arrays */ +typedef mng_uint8 mng_uint8arr4[4]; +typedef mng_uint16 mng_uint16arr[256]; +typedef mng_uint32 mng_uint32arr2[2]; + +/* ************************************************************************** */ + +#define MNG_FALSE 0 +#define MNG_TRUE 1 +#define MNG_NULL 0 + +#define MNG_SUSPENDBUFFERSIZE 32768 +#define MNG_SUSPENDREQUESTSIZE 1024 + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_ZLIB + +/* size of temporary zlib buffer for deflate processing */ +#define MNG_ZLIB_MAXBUF 8192 + +/* default zlib compression parameters for deflateinit2 */ +#define MNG_ZLIB_LEVEL 9 /* level */ +#define MNG_ZLIB_METHOD Z_DEFLATED /* method */ +#define MNG_ZLIB_WINDOWBITS 15 /* window size */ +#define MNG_ZLIB_MEMLEVEL 9 /* memory level */ +#define MNG_ZLIB_STRATEGY Z_DEFAULT_STRATEGY /* strategy */ + +#define MNG_MAX_IDAT_SIZE 4096 /* maximum size of IDAT data */ + +#endif /* MNG_INCLUDE_ZLIB */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_JNG + +#ifdef MNG_INCLUDE_IJG6B /* IJG helper defs */ +typedef struct jpeg_compress_struct mngjpeg_comp; +typedef struct jpeg_decompress_struct mngjpeg_decomp; +typedef struct jpeg_error_mgr mngjpeg_error; +typedef struct jpeg_source_mgr mngjpeg_source; + +typedef mngjpeg_comp * mngjpeg_compp; +typedef mngjpeg_decomp * mngjpeg_decompp; +typedef mngjpeg_error * mngjpeg_errorp; +typedef mngjpeg_source * mngjpeg_sourcep; + +typedef J_DCT_METHOD mngjpeg_dctmethod; + +/* default IJG parameters for compression */ +#define MNG_JPEG_DCT JDCT_DEFAULT /* DCT algorithm (JDCT_ISLOW) */ +#define MNG_JPEG_QUALITY 100 /* quality 0..100; 100=best */ +#define MNG_JPEG_SMOOTHING 0 /* default no smoothing */ +#define MNG_JPEG_PROGRESSIVE MNG_FALSE /* default is just baseline */ +#define MNG_JPEG_OPTIMIZED MNG_FALSE /* default is not optimized */ +#endif /* MNG_INCLUDE_IJG6B */ + +#define MNG_JPEG_MAXBUF 65500 /* max size of temp JPEG buffer */ +#define MNG_MAX_JDAT_SIZE 4096 /* maximum size of JDAT data */ + +#endif /* MNG_INCLUDE_JNG */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_LCMS +typedef cmsHPROFILE mng_cmsprof; /* little CMS helper defs */ +typedef cmsHTRANSFORM mng_cmstrans; +typedef cmsCIExyY mng_CIExyY; +typedef cmsCIExyYTRIPLE mng_CIExyYTRIPLE; +typedef LPGAMMATABLE mng_gammatabp; +#endif /* MNG_INCLUDE_LCMS */ + +/* ************************************************************************** */ + + /* enumeration of known graphics types */ +enum mng_imgtypes {mng_it_unknown, mng_it_png, mng_it_mng, mng_it_jng +#ifdef MNG_INCLUDE_MPNG_PROPOSAL + ,mng_it_mpng +#endif +#ifdef MNG_INCLUDE_ANG_PROPOSAL + ,mng_it_ang +#endif + }; +typedef enum mng_imgtypes mng_imgtype; + + /* enumeration of animation speed-types */ +enum mng_speedtypes {mng_st_normal, mng_st_fast, mng_st_slow, mng_st_slowest}; +typedef enum mng_speedtypes mng_speedtype; + +#ifdef MNG_OPTIMIZE_CHUNKREADER + /* enumeration object-creation indicators */ +enum mng_createobjtypes {mng_create_none, mng_create_always, mng_create_ifglobal}; +typedef enum mng_createobjtypes mng_createobjtype; +#endif + +/* ************************************************************************** */ + +/* enumeration of PNG image types */ +#ifdef MNG_OPTIMIZE_FOOTPRINT_INIT +enum png_imgtypes + { + png_g1, + png_g2, + png_g4, + png_g8, + png_rgb8, + png_idx1, + png_idx2, + png_idx4, + png_idx8, + png_ga8, + png_rgba8, +#ifdef MNG_INCLUDE_JNG + png_jpeg_a1, + png_jpeg_a2, + png_jpeg_a4, + png_jpeg_a8, +#endif +#ifndef MNG_NO_16BIT_SUPPORT + png_g16, + png_ga16, + png_rgb16, + png_rgba16, +#ifdef MNG_INCLUDE_JNG + png_jpeg_a16, +#endif +#endif + png_none + }; + +typedef enum png_imgtypes png_imgtype; +#endif +/* ************************************************************************** */ + + /* memory management callbacks */ +typedef mng_ptr (MNG_DECL *mng_memalloc) (mng_size_t iLen); +typedef void (MNG_DECL *mng_memfree) (mng_ptr iPtr, + mng_size_t iLen); + +typedef void (MNG_DECL *mng_releasedata) (mng_ptr pUserdata, + mng_ptr pData, + mng_size_t iLength); + + /* I/O management callbacks */ +#ifndef MNG_NO_OPEN_CLOSE_STREAM +typedef mng_bool (MNG_DECL *mng_openstream) (mng_handle hHandle); +typedef mng_bool (MNG_DECL *mng_closestream) (mng_handle hHandle); +#endif +typedef mng_bool (MNG_DECL *mng_readdata) (mng_handle hHandle, + mng_ptr pBuf, + mng_uint32 iBuflen, + mng_uint32p pRead); +typedef mng_bool (MNG_DECL *mng_writedata) (mng_handle hHandle, + mng_ptr pBuf, + mng_uint32 iBuflen, + mng_uint32p pWritten); + + /* error & trace processing callbacks */ +typedef mng_bool (MNG_DECL *mng_errorproc) (mng_handle hHandle, + mng_int32 iErrorcode, + mng_int8 iSeverity, + mng_chunkid iChunkname, + mng_uint32 iChunkseq, + mng_int32 iExtra1, + mng_int32 iExtra2, + mng_pchar zErrortext); +typedef mng_bool (MNG_DECL *mng_traceproc) (mng_handle hHandle, + mng_int32 iFuncnr, + mng_int32 iFuncseq, + mng_pchar zFuncname); + + /* read processing callbacks */ +typedef mng_bool (MNG_DECL *mng_processheader) (mng_handle hHandle, + mng_uint32 iWidth, + mng_uint32 iHeight); +typedef mng_bool (MNG_DECL *mng_processtext) (mng_handle hHandle, + mng_uint8 iType, + mng_pchar zKeyword, + mng_pchar zText, + mng_pchar zLanguage, + mng_pchar zTranslation); +typedef mng_bool (MNG_DECL *mng_processsave) (mng_handle hHandle); +typedef mng_bool (MNG_DECL *mng_processseek) (mng_handle hHandle, + mng_pchar zName); +typedef mng_bool (MNG_DECL *mng_processneed) (mng_handle hHandle, + mng_pchar zKeyword); +typedef mng_bool (MNG_DECL *mng_processmend) (mng_handle hHandle, + mng_uint32 iIterationsdone, + mng_uint32 iIterationsleft); +typedef mng_bool (MNG_DECL *mng_processunknown) (mng_handle hHandle, + mng_chunkid iChunkid, + mng_uint32 iRawlen, + mng_ptr pRawdata); +typedef mng_bool (MNG_DECL *mng_processterm) (mng_handle hHandle, + mng_uint8 iTermaction, + mng_uint8 iIteraction, + mng_uint32 iDelay, + mng_uint32 iItermax); + + /* display processing callbacks */ +typedef mng_ptr (MNG_DECL *mng_getcanvasline) (mng_handle hHandle, + mng_uint32 iLinenr); +typedef mng_ptr (MNG_DECL *mng_getbkgdline) (mng_handle hHandle, + mng_uint32 iLinenr); +typedef mng_ptr (MNG_DECL *mng_getalphaline) (mng_handle hHandle, + mng_uint32 iLinenr); +typedef mng_bool (MNG_DECL *mng_refresh) (mng_handle hHandle, + mng_uint32 iX, + mng_uint32 iY, + mng_uint32 iWidth, + mng_uint32 iHeight); + + /* timer management callbacks */ +typedef mng_uint32 (MNG_DECL *mng_gettickcount) (mng_handle hHandle); +typedef mng_bool (MNG_DECL *mng_settimer) (mng_handle hHandle, + mng_uint32 iMsecs); + + /* color management callbacks */ +typedef mng_bool (MNG_DECL *mng_processgamma) (mng_handle hHandle, + mng_uint32 iGamma); +typedef mng_bool (MNG_DECL *mng_processchroma) (mng_handle hHandle, + mng_uint32 iWhitepointx, + mng_uint32 iWhitepointy, + mng_uint32 iRedx, + mng_uint32 iRedy, + mng_uint32 iGreenx, + mng_uint32 iGreeny, + mng_uint32 iBluex, + mng_uint32 iBluey); +typedef mng_bool (MNG_DECL *mng_processsrgb) (mng_handle hHandle, + mng_uint8 iRenderingintent); +typedef mng_bool (MNG_DECL *mng_processiccp) (mng_handle hHandle, + mng_uint32 iProfilesize, + mng_ptr pProfile); +typedef mng_bool (MNG_DECL *mng_processarow) (mng_handle hHandle, + mng_uint32 iRowsamples, + mng_bool bIsRGBA16, + mng_ptr pRow); + + /* chunk access callback(s) */ +typedef mng_bool (MNG_DECL *mng_iteratechunk) (mng_handle hHandle, + mng_handle hChunk, + mng_chunkid iChunkid, + mng_uint32 iChunkseq); + +/* ************************************************************************** */ + +#endif /* _libmng_types_h_ */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ + diff --git a/Engine/lib/lmng/libmng_write.c b/Engine/lib/lmng/libmng_write.c new file mode 100644 index 000000000..79ff54438 --- /dev/null +++ b/Engine/lib/lmng/libmng_write.c @@ -0,0 +1,198 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_write.c copyright (c) 2000-2004 G.Juyn * */ +/* * version : 1.0.9 * */ +/* * * */ +/* * purpose : Write management (implementation) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : implementation of the write management routines * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed strict-ANSI stuff * */ +/* * 0.5.1 - 05/12/2000 - G.Juyn * */ +/* * - changed trace to macro for callback error-reporting * */ +/* * 0.5.1 - 05/16/2000 - G.Juyn * */ +/* * - moved the actual write_graphic functionality from * */ +/* * mng_hlapi to its appropriate function here * */ +/* * * */ +/* * 0.9.1 - 07/19/2000 - G.Juyn * */ +/* * - fixed writing of signature * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * * */ +/* * 1.0.8 - 07/06/2004 - G.R-P * */ +/* * - added conditionals around openstream/closestream * */ +/* * - defend against using undefined Open/Closestream function * */ +/* * 1.0.8 - 08/02/2004 - G.Juyn * */ +/* * - added conditional to allow easier writing of large MNG's * */ +/* * * */ +/* * 1.0.9 - 09/25/2004 - G.Juyn * */ +/* * - replaced MNG_TWEAK_LARGE_FILES with permanent solution * */ +/* * 1.0.9 - 12/20/2004 - G.Juyn * */ +/* * - cleaned up macro-invocations (thanks to D. Airlie) * */ +/* * * */ +/* ************************************************************************** */ + +#include "libmng.h" +#include "libmng_data.h" +#include "libmng_error.h" +#include "libmng_trace.h" +#ifdef __BORLANDC__ +#pragma hdrstop +#endif +#include "libmng_memory.h" +#include "libmng_chunks.h" +#include "libmng_chunk_io.h" +#include "libmng_write.h" + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +/* ************************************************************************** */ + +#if defined(MNG_SUPPORT_READ) || defined(MNG_SUPPORT_WRITE) +mng_retcode mng_drop_chunks (mng_datap pData) +{ + mng_chunkp pChunk; + mng_chunkp pNext; + mng_cleanupchunk fCleanup; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DROP_CHUNKS, MNG_LC_START); +#endif + + pChunk = pData->pFirstchunk; /* and get first stored chunk (if any) */ + + while (pChunk) /* more chunks to discard ? */ + { + pNext = ((mng_chunk_headerp)pChunk)->pNext; + /* call appropriate cleanup */ + fCleanup = ((mng_chunk_headerp)pChunk)->fCleanup; + fCleanup (pData, pChunk); + + pChunk = pNext; /* neeeext */ + } + + pData->pFirstchunk = MNG_NULL; + pData->pLastchunk = MNG_NULL; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_DROP_CHUNKS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_READ || MNG_SUPPORT_WRITE */ + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_WRITE_PROCS + +/* ************************************************************************** */ + +mng_retcode mng_write_graphic (mng_datap pData) +{ + mng_chunkp pChunk; + mng_retcode iRetcode; + mng_uint32 iWritten; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_GRAPHIC, MNG_LC_START); +#endif + + pChunk = pData->pFirstchunk; /* we'll start with the first, thank you */ + + if (pChunk) /* is there anything to write ? */ + { /* open the file */ + if (!pData->bWriting) + { +#ifndef MNG_NO_OPEN_CLOSE_STREAM + if (pData->fOpenstream && !pData->fOpenstream ((mng_handle)pData)) + MNG_ERROR (pData, MNG_APPIOERROR); +#endif + { + pData->bWriting = MNG_TRUE; /* indicate writing */ + pData->iWritebufsize = 32768; /* get a temporary write buffer */ + /* reserve 12 bytes for length, chunkname & crc */ + MNG_ALLOC (pData, pData->pWritebuf, pData->iWritebufsize+12); + + /* write the signature */ + if (((mng_chunk_headerp)pChunk)->iChunkname == MNG_UINT_IHDR) + mng_put_uint32 (pData->pWritebuf, PNG_SIG); + else + if (((mng_chunk_headerp)pChunk)->iChunkname == MNG_UINT_JHDR) + mng_put_uint32 (pData->pWritebuf, JNG_SIG); + else + mng_put_uint32 (pData->pWritebuf, MNG_SIG); + + mng_put_uint32 (pData->pWritebuf+4, POST_SIG); + + if (!pData->fWritedata ((mng_handle)pData, pData->pWritebuf, 8, &iWritten)) + { + MNG_FREE (pData, pData->pWritebuf, pData->iWritebufsize+12); + MNG_ERROR (pData, MNG_APPIOERROR); + } + + if (iWritten != 8) /* disk full ? */ + { + MNG_FREE (pData, pData->pWritebuf, pData->iWritebufsize+12); + MNG_ERROR (pData, MNG_OUTPUTERROR); + } + } + } + + while (pChunk) /* so long as there's something to write */ + { /* let's call its output routine */ + iRetcode = ((mng_chunk_headerp)pChunk)->fWrite (pData, pChunk); + if (iRetcode) /* on error bail out */ + return iRetcode; + /* neeeext */ + pChunk = ((mng_chunk_headerp)pChunk)->pNext; + } + + if (!pData->bCreating) + { /* free the temporary buffer */ + MNG_FREE (pData, pData->pWritebuf, pData->iWritebufsize+12); + + pData->bWriting = MNG_FALSE; /* done writing */ + /* close the stream now */ +#ifndef MNG_NO_OPEN_CLOSE_STREAM + if (pData->fClosestream && !pData->fClosestream ((mng_handle)pData)) + MNG_ERROR (pData, MNG_APPIOERROR); +#endif + + } else { + /* cleanup the written chunks */ + iRetcode = mng_drop_chunks (pData); + if (iRetcode) /* on error bail out */ + return iRetcode; + } + } + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_WRITE_GRAPHIC, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_WRITE_PROCS */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ + + diff --git a/Engine/lib/lmng/libmng_write.h b/Engine/lib/lmng/libmng_write.h new file mode 100644 index 000000000..df058fb77 --- /dev/null +++ b/Engine/lib/lmng/libmng_write.h @@ -0,0 +1,49 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_write.h copyright (c) 2000-2004 G.Juyn * */ +/* * version : 1.0.9 * */ +/* * * */ +/* * purpose : Write management (definition) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : Definition of the write management routines * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed strict-ANSI stuff * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * * */ +/* * 1.0.9 - 09/25/2004 - G.Juyn * */ +/* * - replaced MNG_TWEAK_LARGE_FILES with permanent solution * */ +/* * * */ +/* ************************************************************************** */ + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +#ifndef _libmng_write_h_ +#define _libmng_write_h_ + +/* ************************************************************************** */ + +mng_retcode mng_drop_chunks (mng_datap pData); + +mng_retcode mng_write_graphic (mng_datap pData); + +/* ************************************************************************** */ + +#endif /* _libmng_write_h_ */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ diff --git a/Engine/lib/lmng/libmng_zlib.c b/Engine/lib/lmng/libmng_zlib.c new file mode 100644 index 000000000..7d102e160 --- /dev/null +++ b/Engine/lib/lmng/libmng_zlib.c @@ -0,0 +1,607 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_zlib.c copyright (c) 2000-2004 G.Juyn * */ +/* * version : 1.0.9 * */ +/* * * */ +/* * purpose : ZLIB library interface (implementation) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : implementation of the ZLIB library interface * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed strict-ANSI stuff * */ +/* * 0.5.1 - 05/11/2000 - G.Juyn * */ +/* * - filled the deflatedata routine * */ +/* * 0.5.1 - 05/12/2000 - G.Juyn * */ +/* * - changed trace to macro for callback error-reporting * */ +/* * * */ +/* * 0.5.2 - 05/20/2000 - G.Juyn * */ +/* * - fixed for JNG alpha handling * */ +/* * 0.5.2 - 05/24/2000 - G.Juyn * */ +/* * - moved init of default zlib parms from here to * */ +/* * "mng_hlapi.c" * */ +/* * * */ +/* * 0.5.3 - 06/16/2000 - G.Juyn * */ +/* * - changed progressive-display processing * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* * 0.9.3 - 08/08/2000 - G.Juyn * */ +/* * - fixed compiler-warnings from Mozilla * */ +/* * 0.9.3 - 09/07/2000 - G.Juyn * */ +/* * - added support for new filter_types * */ +/* * * */ +/* * 1.0.5 - 08/07/2002 - G.Juyn * */ +/* * - added test-option for PNG filter method 193 (=no filter) * */ +/* * 1.0.5 - 08/19/2002 - G.Juyn * */ +/* * - B597134 - libmng pollutes the linker namespace * */ +/* * 1.0.5 - 09/19/2002 - G.Juyn * */ +/* * - added warning for too much IDAT data * */ +/* * * */ +/* * 1.0.6 - 07/07/2003 - G.R-P * */ +/* * - added MNG_NO_16BIT_SUPPORT support * */ +/* * * */ +/* * 1.0.9 - 10/09/2004 - G.R-P * */ +/* * - added MNG_NO_1_2_4BIT_SUPPORT support * */ +/* * * */ +/* ************************************************************************** */ + +#include "libmng.h" +#include "libmng_data.h" +#include "libmng_error.h" +#include "libmng_trace.h" +#ifdef __BORLANDC__ +#pragma hdrstop +#endif +#include "libmng_memory.h" +#include "libmng_pixels.h" +#include "libmng_filter.h" +#include "libmng_zlib.h" + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +/* ************************************************************************** */ + +#ifdef MNG_INCLUDE_ZLIB + +/* ************************************************************************** */ + +voidpf mngzlib_alloc (voidpf pData, + uInt iCount, + uInt iSize) +{ + voidpf pPtr; /* temporary space */ + +#ifdef MNG_INTERNAL_MEMMNGMT + pPtr = calloc (iCount, iSize); /* local allocation */ +#else + if (((mng_datap)pData)->fMemalloc) /* callback function set ? */ + pPtr = ((mng_datap)pData)->fMemalloc (iCount * iSize); + else + pPtr = Z_NULL; /* can't allocate! */ +#endif + + return pPtr; /* return the result */ +} + +/* ************************************************************************** */ + +void mngzlib_free (voidpf pData, + voidpf pAddress) +{ +#ifdef MNG_INTERNAL_MEMMNGMT + free (pAddress); /* free locally */ +#else + if (((mng_datap)pData)->fMemfree) /* callback set? */ + ((mng_datap)pData)->fMemfree (pAddress, 1); +#endif +} + +/* ************************************************************************** */ + +mng_retcode mngzlib_initialize (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ZLIB_INITIALIZE, MNG_LC_START); +#endif + +#ifdef MNG_INTERNAL_MEMMNGMT + pData->sZlib.zalloc = Z_NULL; /* let zlib figure out memory management */ + pData->sZlib.zfree = Z_NULL; + pData->sZlib.opaque = Z_NULL; +#else /* use user-provided callbacks */ + pData->sZlib.zalloc = mngzlib_alloc; + pData->sZlib.zfree = mngzlib_free; + pData->sZlib.opaque = (voidpf)pData; +#endif + + pData->bInflating = MNG_FALSE; /* not performing any action yet */ + pData->bDeflating = MNG_FALSE; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ZLIB_INITIALIZE, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} + +/* ************************************************************************** */ + +mng_retcode mngzlib_cleanup (mng_datap pData) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ZLIB_CLEANUP, MNG_LC_START); +#endif + + if (pData->bInflating) /* force zlib cleanup */ + mngzlib_inflatefree (pData); + if (pData->bDeflating) + mngzlib_deflatefree (pData); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ZLIB_CLEANUP, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} + +/* ************************************************************************** */ + +mng_retcode mngzlib_inflateinit (mng_datap pData) +{ + int iZrslt; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ZLIB_INFLATEINIT, MNG_LC_START); +#endif + /* initialize zlib structures and such */ + iZrslt = inflateInit (&pData->sZlib); + + if (iZrslt != Z_OK) /* on error bail out */ + MNG_ERRORZ (pData, (mng_uint32)iZrslt); + + pData->bInflating = MNG_TRUE; /* really inflating something now */ + pData->sZlib.next_out = 0; /* force JIT initialization */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ZLIB_INFLATEINIT, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} + +/* ************************************************************************** */ + +#ifdef MNG_SUPPORT_DISPLAY +mng_retcode mngzlib_inflaterows (mng_datap pData, + mng_uint32 iInlen, + mng_uint8p pIndata) +{ + int iZrslt; + mng_retcode iRslt; + mng_ptr pSwap; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ZLIB_INFLATEROWS, MNG_LC_START); +#endif + + pData->sZlib.next_in = pIndata; /* let zlib know where to get stuff */ + pData->sZlib.avail_in = (uInt)iInlen; + + if (pData->sZlib.next_out == 0) /* initialize output variables ? */ + { /* let zlib know where to store stuff */ + pData->sZlib.next_out = pData->pWorkrow; + pData->sZlib.avail_out = (uInt)(pData->iRowsize + pData->iPixelofs); +#ifdef MNG_NO_1_2_4BIT_SUPPORT + if (pData->iPNGdepth < 8) + pData->sZlib.avail_out = (uInt)((pData->iPNGdepth*pData->iRowsize + 7)/8 + + pData->iPixelofs); +#endif +#ifdef MNG_NO_16BIT_SUPPORT + if (pData->iPNGdepth > 8) + pData->sZlib.avail_out = (uInt)(2*pData->iRowsize + pData->iPixelofs); +#endif + } + + do + { /* now inflate a row */ + iZrslt = inflate (&pData->sZlib, Z_SYNC_FLUSH); + /* produced a full row ? */ + if (((iZrslt == Z_OK) || (iZrslt == Z_STREAM_END)) && + (pData->sZlib.avail_out == 0)) + { /* image not completed yet ? */ + if (pData->iRow < (mng_int32)pData->iDataheight) + { +#ifdef MNG_NO_1_2_4BIT_SUPPORT + if (pData->iPNGdepth == 1) + { + /* Inflate Workrow to 8-bit */ + mng_int32 iX; + mng_uint8p pSrc = pData->pWorkrow+1; + mng_uint8p pDest = pSrc + pData->iRowsize - (pData->iRowsize+7)/8; + + for (iX = ((pData->iRowsize+7)/8) ; iX > 0 ; iX--) + *pDest++ = *pSrc++; + + pDest = pData->pWorkrow+1; + pSrc = pDest + pData->iRowsize - (pData->iRowsize+7)/8; + for (iX = pData->iRowsize; ;) + { + *pDest++ = (((*pSrc)>>7)&1); + if (iX-- <= 0) + break; + *pDest++ = (((*pSrc)>>6)&1); + if (iX-- <= 0) + break; + *pDest++ = (((*pSrc)>>5)&1); + if (iX-- <= 0) + break; + *pDest++ = (((*pSrc)>>4)&1); + if (iX-- <= 0) + break; + *pDest++ = (((*pSrc)>>3)&1); + if (iX-- <= 0) + break; + *pDest++ = (((*pSrc)>>2)&1); + if (iX-- <= 0) + break; + *pDest++ = (((*pSrc)>>1)&1); + if (iX-- <= 0) + break; + *pDest++ = (((*pSrc) )&1); + if (iX-- <= 0) + break; + pSrc++; + } + } + else if (pData->iPNGdepth == 2) + { + /* Inflate Workrow to 8-bit */ + mng_int32 iX; + mng_uint8p pSrc = pData->pWorkrow+1; + mng_uint8p pDest = pSrc + pData->iRowsize - (2*pData->iRowsize+7)/8; + + for (iX = ((2*pData->iRowsize+7)/8) ; iX > 0 ; iX--) + *pDest++ = *pSrc++; + + pDest = pData->pWorkrow+1; + pSrc = pDest + pData->iRowsize - (2*pData->iRowsize+7)/8; + for (iX = pData->iRowsize; ;) + { + *pDest++ = (((*pSrc)>>6)&3); + if (iX-- <= 0) + break; + *pDest++ = (((*pSrc)>>4)&3); + if (iX-- <= 0) + break; + *pDest++ = (((*pSrc)>>2)&3); + if (iX-- <= 0) + break; + *pDest++ = (((*pSrc) )&3); + if (iX-- <= 0) + break; + pSrc++; + } + } + else if (pData->iPNGdepth == 4) + { + /* Inflate Workrow to 8-bit */ + mng_int32 iX; + mng_uint8p pSrc = pData->pWorkrow+1; + mng_uint8p pDest = pSrc + pData->iRowsize - (4*pData->iRowsize+7)/8; + + for (iX = ((4*pData->iRowsize+7)/8) ; iX > 0 ; iX--) + *pDest++ = *pSrc++; + + pDest = pData->pWorkrow+1; + pSrc = pDest + pData->iRowsize - (4*pData->iRowsize+7)/8; + for (iX = pData->iRowsize; ;) + { + *pDest++ = (((*pSrc)>>4)&0x0f); + if (iX-- <= 0) + break; + *pDest++ = (((*pSrc) )&0x0f); + if (iX-- <= 0) + break; + pSrc++; + } + } + if (pData->iPNGdepth < 8 && pData->iColortype == 0) + { + /* Expand samples to 8-bit by LBR */ + mng_int32 iX; + mng_uint8p pSrc = pData->pWorkrow+1; + mng_uint8 multiplier[]={0,255,85,0,17,0,0,0,1}; + + for (iX = pData->iRowsize; iX > 0; iX--) + *pSrc++ *= multiplier[pData->iPNGdepth]; + } +#endif +#ifdef MNG_NO_16BIT_SUPPORT + if (pData->iPNGdepth > 8) + { + /* Reduce Workrow to 8-bit */ + mng_int32 iX; + mng_uint8p pSrc = pData->pWorkrow+1; + mng_uint8p pDest = pSrc; + + for (iX = pData->iRowsize; iX > 0; iX--) + { + *pDest = *pSrc; + pDest++; + pSrc+=2; + } + } +#endif + +#ifdef FILTER192 /* has leveling info ? */ + if (pData->iFilterofs == MNG_FILTER_DIFFERING) + iRslt = init_rowdiffering (pData); + else +#endif + iRslt = MNG_NOERROR; + /* filter the row if necessary */ + if ((!iRslt) && (pData->iFilterofs < pData->iPixelofs ) && + (*(pData->pWorkrow + pData->iFilterofs)) ) + iRslt = mng_filter_a_row (pData); + else + iRslt = MNG_NOERROR; + /* additional leveling/differing ? */ + if ((!iRslt) && (pData->fDifferrow)) + { + iRslt = ((mng_differrow)pData->fDifferrow) (pData); + + pSwap = pData->pWorkrow; + pData->pWorkrow = pData->pPrevrow; + pData->pPrevrow = pSwap; /* make sure we're processing the right data */ + } + + if (!iRslt) + { +#ifdef MNG_INCLUDE_JNG + if (pData->bHasJHDR) /* is JNG alpha-channel ? */ + { /* just store in object ? */ + if ((!iRslt) && (pData->fStorerow)) + iRslt = ((mng_storerow)pData->fStorerow) (pData); + } + else +#endif /* MNG_INCLUDE_JNG */ + { /* process this row */ + if ((!iRslt) && (pData->fProcessrow)) + iRslt = ((mng_processrow)pData->fProcessrow) (pData); + /* store in object ? */ + if ((!iRslt) && (pData->fStorerow)) + iRslt = ((mng_storerow)pData->fStorerow) (pData); + /* color correction ? */ + if ((!iRslt) && (pData->fCorrectrow)) + iRslt = ((mng_correctrow)pData->fCorrectrow) (pData); + /* slap onto canvas ? */ + if ((!iRslt) && (pData->fDisplayrow)) + { + iRslt = ((mng_displayrow)pData->fDisplayrow) (pData); + + if (!iRslt) /* check progressive display refresh */ + iRslt = mng_display_progressive_check (pData); + + } + } + } + + if (iRslt) /* on error bail out */ + MNG_ERROR (pData, iRslt); + + if (!pData->fDifferrow) /* swap row-pointers */ + { + pSwap = pData->pWorkrow; + pData->pWorkrow = pData->pPrevrow; + pData->pPrevrow = pSwap; /* so prev points to the processed row! */ + } + + iRslt = mng_next_row (pData); /* adjust variables for next row */ + + if (iRslt) /* on error bail out */ + MNG_ERROR (pData, iRslt); + } + /* let zlib know where to store next output */ + pData->sZlib.next_out = pData->pWorkrow; + pData->sZlib.avail_out = (uInt)(pData->iRowsize + pData->iPixelofs); +#ifdef MNG_NO_1_2_4BIT_SUPPORT + if (pData->iPNGdepth < 8) + pData->sZlib.avail_out = (uInt)((pData->iPNGdepth*pData->iRowsize + 7)/8 + + pData->iPixelofs); +#endif +#ifdef MNG_NO_16BIT_SUPPORT + if (pData->iPNGdepth > 8) + pData->sZlib.avail_out = (uInt)(2*pData->iRowsize + pData->iPixelofs); +#endif + } + } /* until some error or EOI + or all pixels received */ + while ( (iZrslt == Z_OK) && (pData->sZlib.avail_in > 0) && + ( (pData->iRow < (mng_int32)pData->iDataheight) || + ( (pData->iPass >= 0) && (pData->iPass < 7) ) ) ); + /* on error bail out */ + if ((iZrslt != Z_OK) && (iZrslt != Z_STREAM_END)) + MNG_ERRORZ (pData, (mng_uint32)iZrslt); + /* too much data ? */ + if ((iZrslt == Z_OK) && (pData->sZlib.avail_in > 0)) + MNG_WARNING (pData, MNG_TOOMUCHIDAT); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ZLIB_INFLATEROWS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} +#endif /* MNG_SUPPORT_DISPLAY */ + +/* ************************************************************************** */ + +mng_retcode mngzlib_inflatedata (mng_datap pData, + mng_uint32 iInlen, + mng_uint8p pIndata) +{ + int iZrslt; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ZLIB_INFLATEDATA, MNG_LC_START); +#endif + /* let zlib know where to get stuff */ + pData->sZlib.next_in = pIndata; + pData->sZlib.avail_in = (uInt)iInlen; + /* now inflate the data in one go! */ + iZrslt = inflate (&pData->sZlib, Z_FINISH); + /* not enough room in output-buffer ? */ + if ((iZrslt == Z_BUF_ERROR) || (pData->sZlib.avail_in > 0)) + return MNG_BUFOVERFLOW; + /* on error bail out */ + if ((iZrslt != Z_OK) && (iZrslt != Z_STREAM_END)) + MNG_ERRORZ (pData, (mng_uint32)iZrslt); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ZLIB_INFLATEDATA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mngzlib_inflatefree (mng_datap pData) +{ + int iZrslt; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ZLIB_INFLATEFREE, MNG_LC_START); +#endif + + pData->bInflating = MNG_FALSE; /* stopped it */ + + iZrslt = inflateEnd (&pData->sZlib); /* let zlib cleanup its own stuff */ + + if (iZrslt != Z_OK) /* on error bail out */ + MNG_ERRORZ (pData, (mng_uint32)iZrslt); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ZLIB_INFLATEFREE, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} + +/* ************************************************************************** */ + +mng_retcode mngzlib_deflateinit (mng_datap pData) +{ + int iZrslt; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ZLIB_DEFLATEINIT, MNG_LC_START); +#endif + /* initialize zlib structures and such */ + iZrslt = deflateInit2 (&pData->sZlib, pData->iZlevel, pData->iZmethod, + pData->iZwindowbits, pData->iZmemlevel, + pData->iZstrategy); + + if (iZrslt != Z_OK) /* on error bail out */ + MNG_ERRORZ (pData, (mng_uint32)iZrslt); + + pData->bDeflating = MNG_TRUE; /* really deflating something now */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ZLIB_DEFLATEINIT, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} + +/* ************************************************************************** */ + +mng_retcode mngzlib_deflaterows (mng_datap pData, + mng_uint32 iInlen, + mng_uint8p pIndata) +{ +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ZLIB_DEFLATEROWS, MNG_LC_START); +#endif + + + + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ZLIB_DEFLATEROWS, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mngzlib_deflatedata (mng_datap pData, + mng_uint32 iInlen, + mng_uint8p pIndata) +{ + int iZrslt; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ZLIB_DEFLATEDATA, MNG_LC_START); +#endif + + pData->sZlib.next_in = pIndata; /* let zlib know where to get stuff */ + pData->sZlib.avail_in = (uInt)iInlen; + /* now deflate the data in one go! */ + iZrslt = deflate (&pData->sZlib, Z_FINISH); + /* not enough room in output-buffer ? */ + if ((iZrslt == Z_BUF_ERROR) || (pData->sZlib.avail_in > 0)) + return MNG_BUFOVERFLOW; + /* on error bail out */ + if ((iZrslt != Z_OK) && (iZrslt != Z_STREAM_END)) + MNG_ERRORZ (pData, (mng_uint32)iZrslt); + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ZLIB_DEFLATEDATA, MNG_LC_END); +#endif + + return MNG_NOERROR; +} + +/* ************************************************************************** */ + +mng_retcode mngzlib_deflatefree (mng_datap pData) +{ + int iZrslt; + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ZLIB_DEFLATEFREE, MNG_LC_START); +#endif + + iZrslt = deflateEnd (&pData->sZlib); /* let zlib cleanup its own stuff */ + + if (iZrslt != Z_OK) /* on error bail out */ + MNG_ERRORZ (pData, (mng_uint32)iZrslt); + + pData->bDeflating = MNG_FALSE; /* stopped it */ + +#ifdef MNG_SUPPORT_TRACE + MNG_TRACE (pData, MNG_FN_ZLIB_DEFLATEFREE, MNG_LC_END); +#endif + + return MNG_NOERROR; /* done */ +} + +/* ************************************************************************** */ + +#endif /* MNG_INCLUDE_ZLIB */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ + diff --git a/Engine/lib/lmng/libmng_zlib.h b/Engine/lib/lmng/libmng_zlib.h new file mode 100644 index 000000000..cfc391823 --- /dev/null +++ b/Engine/lib/lmng/libmng_zlib.h @@ -0,0 +1,60 @@ +/* ************************************************************************** */ +/* * For conditions of distribution and use, * */ +/* * see copyright notice in libmng.h * */ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : libmng_zlib.h copyright (c) 2000-2002 G.Juyn * */ +/* * version : 1.0.0 * */ +/* * * */ +/* * purpose : ZLIB package interface (definition) * */ +/* * * */ +/* * author : G.Juyn * */ +/* * * */ +/* * comment : Definition of the ZLIB package interface * */ +/* * * */ +/* * changes : 0.5.1 - 05/08/2000 - G.Juyn * */ +/* * - changed strict-ANSI stuff * */ +/* * * */ +/* * 0.9.2 - 08/05/2000 - G.Juyn * */ +/* * - changed file-prefixes * */ +/* * * */ +/* ************************************************************************** */ + +#if defined(__BORLANDC__) && defined(MNG_STRICT_ANSI) +#pragma option -A /* force ANSI-C */ +#endif + +#ifndef _libmng_zlib_h_ +#define _libmng_zlib_h_ + +/* ************************************************************************** */ + +mng_retcode mngzlib_initialize (mng_datap pData); +mng_retcode mngzlib_cleanup (mng_datap pData); + +mng_retcode mngzlib_inflateinit (mng_datap pData); +mng_retcode mngzlib_inflaterows (mng_datap pData, + mng_uint32 iInlen, + mng_uint8p pIndata); +mng_retcode mngzlib_inflatedata (mng_datap pData, + mng_uint32 iInlen, + mng_uint8p pIndata); +mng_retcode mngzlib_inflatefree (mng_datap pData); + +mng_retcode mngzlib_deflateinit (mng_datap pData); +mng_retcode mngzlib_deflaterows (mng_datap pData, + mng_uint32 iInlen, + mng_uint8p pIndata); +mng_retcode mngzlib_deflatedata (mng_datap pData, + mng_uint32 iInlen, + mng_uint8p pIndata); +mng_retcode mngzlib_deflatefree (mng_datap pData); + +/* ************************************************************************** */ + +#endif /* _libmng_zlib_h_ */ + +/* ************************************************************************** */ +/* * end of file * */ +/* ************************************************************************** */ diff --git a/Engine/lib/lmng/makefiles/Makefile.am b/Engine/lib/lmng/makefiles/Makefile.am new file mode 100644 index 000000000..b53523e9a --- /dev/null +++ b/Engine/lib/lmng/makefiles/Makefile.am @@ -0,0 +1,29 @@ +## Process this file with automake to produce Makefile.in + +AUTOMAKE_OPTIONS = 1.3 foreign no-dependencies + +# include the app subdirectories in the distribution +EXTRA_DIST = makefiles doc contrib + + +# libmng release @VERSION@ +libmng_la_LDFLAGS = -version-info 1:0:0 + +lib_LTLIBRARIES = libmng.la + +include_HEADERS = libmng.h libmng_conf.h libmng_types.h +noinst_HEADERS = libmng_chunk_io.h libmng_chunk_prc.h libmng_chunks.h \ + libmng_cms.h libmng_data.h libmng_display.h libmng_dither.h \ + libmng_error.h libmng_filter.h libmng_jpeg.h libmng_memory.h \ + libmng_object_prc.h libmng_objects.h libmng_pixels.h \ + libmng_read.h libmng_trace.h libmng_write.h libmng_zlib.h + +libmng_la_SOURCES = libmng_callback_xs.c libmng_chunk_io.c \ + libmng_chunk_prc.c libmng_chunk_xs.c libmng_cms.c \ + libmng_display.c libmng_dither.c libmng_error.c \ + libmng_filter.c libmng_hlapi.c libmng_jpeg.c \ + libmng_object_prc.c libmng_pixels.c libmng_prop_xs.c \ + libmng_read.c libmng_trace.c libmng_write.c libmng_zlib.c + +man_MANS = doc/man/libmng.3 doc/man/jng.5 doc/man/mng.5 + diff --git a/Engine/lib/lmng/makefiles/README b/Engine/lib/lmng/makefiles/README new file mode 100644 index 000000000..63faaafb0 --- /dev/null +++ b/Engine/lib/lmng/makefiles/README @@ -0,0 +1,27 @@ +For conditions of distribution and use, see copyright notice in libmng.h +or the file LICENSE in the top-level directory of the source distribution. + +This directory hosts the makefiles for a number of supported platforms. + +If you're using a system with POSIX shell capabilities, you can use the +'configure' script in the top-level directory, or generate it by running +'autogen.sh' if you have the necessary tools installed. + +Otherwise, copy the module for your environment (or the closest thing) +into the libmng source-directory and change it to your needs. If you +create a new file for a platform not on the list send it to me (gerard @ +libmng.com) and I'll be happy to include it in the next release! + + +Current files: + +makefile.bcb3 - Borland C++ Builder +makefile.vcwin32 - Microsoft Visual C++ +makefile.unix - generic Unix +makefile.linux - Linux ELF (builds shared library) +makefile.dj - DJGPP +makefile.mingw - builds a static library for mingw32 +makefile.mingwdll - builds a dynamic library for mingw32 +makefile.irix - builds a static library for SGI/IRIX (6.5.21) + +Makefile.am, configure.in and acinclude.m4 (if present) - automake/autoconf source diff --git a/Engine/lib/lmng/makefiles/configure.in b/Engine/lib/lmng/makefiles/configure.in new file mode 100644 index 000000000..e7cb75c00 --- /dev/null +++ b/Engine/lib/lmng/makefiles/configure.in @@ -0,0 +1,193 @@ +dnl Process this file with autoconf to produce a configure script. + +AC_INIT +AC_CONFIG_SRCDIR([libmng.h]) +AC_PREREQ(2.52) + +dnl this call will define PACKAGE and VERSION +dnl please use this as the primary reference for the version number +AM_INIT_AUTOMAKE(libmng, 1.0.9) + +dnl pass the version string on the the makefiles +AC_SUBST(PACKAGE) +AC_SUBST(VERSION) + +dnl Checks for programs. +AC_PROG_CC +AC_ISC_POSIX +AM_C_PROTOTYPES +if test "x$U" != "x"; then + AC_MSG_ERROR(Compiler not ANSI compliant) +fi +AM_PROG_LIBTOOL +AC_PROG_INSTALL + +dnl support for files >2GB +AC_SYS_LARGEFILE + +dnl Check for required header files +AC_HEADER_STDC + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST + +dnl need pow and fabs +AC_CHECK_FUNC(pow, , AC_CHECK_LIB(m, pow, LIBS="$LIBS -lm")) + + +dnl what functionality we want to add (read, write, display). +dnl all on by default. see libmng_conf.h for full descriptions + +dnl not building a standard shared object? +AC_ARG_ENABLE(buildso, +[ --disable-buildso disable building standard shared object]) +if test "x$enable_buildso" != "xno"; then + AC_DEFINE(MNG_BUILD_SO) +fi + +dnl we only support the full mng spec for now (no LC or VLC) +AC_DEFINE(MNG_SUPPORT_FULL) + +dnl remove support in library to read images? +AC_ARG_ENABLE(read, +[ --disable-read remove read support from library]) +if test "x$enable_read" != "xno"; then + AC_DEFINE(MNG_SUPPORT_READ) +fi + +dnl remove support in library to write images? +AC_ARG_ENABLE(write, +[ --disable-write remove write support from library]) +if test "x$enable_write" != "xno"; then + AC_DEFINE(MNG_SUPPORT_WRITE) +fi + +dnl remove support in library to display images? +AC_ARG_ENABLE(display, +[ --disable-display remove display support from library]) +if test "x$enable_display" != "xno"; then + AC_DEFINE(MNG_SUPPORT_DISPLAY) +fi + +dnl remove support for 'dynamic' MNG? +AC_ARG_ENABLE(dynamic, +[ --disable-dynamic remove dynamic MNG support from library]) +if test "x$enable_dynamic" != "xno"; then + AC_DEFINE(MNG_SUPPORT_DYNAMICMNG) +fi + +dnl remove support in library to access chunks? +AC_ARG_ENABLE(chunks, +[ --disable-chunks remove support for chunk access]) +if test "x$enable_chunks" != "xno"; then + AC_DEFINE(MNG_ACCESS_CHUNKS) +fi + +dnl disable support for accessing chunks that have been previously read? +AC_ARG_ENABLE(storechunks, +[ --disable-storechunks remove support for access of previous chunks]) +if test "x$enable_storechunks" != "xno"; then + AC_DEFINE(MNG_STORE_CHUNKS) +fi + +dnl enable support for debug tracing callbacks and messages? +AC_ARG_ENABLE(trace, +[ --enable-trace include support for debug tracing callbacks],[ +if test "x$enable_trace" = "xyes"; then + AC_DEFINE(MNG_SUPPORT_TRACE) + AC_DEFINE(MNG_TRACE_TELLTALE) +fi +]) + +dnl verbose error text +dnl this should always be on +AC_DEFINE(MNG_ERROR_TELLTALE) + + +dnl libz is required. +AC_ARG_WITH(zlib, +[ --with-zlib[=DIR] use zlib include/library files in DIR],[ + if test -d "$withval"; then + CPPFLAGS="$CPPFLAGS -I$withval/include" + LDFLAGS="$LDFLAGS -L$withval/lib" + fi +]) +AC_CHECK_HEADER(zlib.h, + AC_CHECK_LIB(z, gzread, , AC_MSG_ERROR(zlib library not found)), + AC_MSG_ERROR(zlib header not found) +) + +dnl check for jpeg library +AC_ARG_WITH(jpeg, +[ --with-jpeg[=DIR] use jpeg include/library files in DIR], +[with_jpeg=$withval],[with_jpeg=_auto]) + + if test "x$with_jpeg" != "xno" -a "x$with_jpeg" != "xyes" -a \ + "x$with_jpeg" != "x_auto"; then + # Save in case test with directory specified fails + _cppflags=${CPPFLAGS} + _ldflags=${LDFLAGS} + _restore=1 + + CPPFLAGS="${CPPFLAGS} -I$withval/include" + LDFLAGS="${LDFLAGS} -L$withval/lib" + else + _restore=0 + fi + + if test "x$with_jpeg" != "xno"; then + AC_CHECK_HEADER(jpeglib.h, + AC_CHECK_LIB(jpeg, jpeg_read_header, [ + LIBS="$LIBS -ljpeg" + AC_DEFINE(HAVE_LIBJPEG) + _restore=0 + ], + AC_MSG_WARN(jpeg library not found)), + AC_MSG_WARN(jpeg header not found) + ) + fi + + test $_restore -eq 1 && CPPFLAGS=$_cppflags LDFLAGS=$_ldflags + +dnl check for lcms library +AC_ARG_WITH(lcms, +[ --with-lcms[=DIR] use lcms include/library files in DIR], +[with_lcms=$withval],[with_lcms=_auto]) + + if test "x$with_lcms" != "xno" -a "x$with_lcms" != "xyes" -a \ + "x$with_lcms" != "x_auto"; then + # Save in case test with directory specified fails + _cppflags=$CPPFLAGS + _ldflags=$LDFLAGS + _restore=1 + + CPPFLAGS="$CPPFLAGS -I$withval/include" + LDFLAGS="$LDFLAGS -L$withval/lib" + else + _restore=0 + fi + + if test "x$with_lcms" != "xno"; then + AC_CHECK_HEADER(lcms.h, [ + have_lcms=yes + AC_CHECK_LIB(lcms, cmsCreateRGBProfile, [ + LIBS="$LIBS -llcms" + AC_DEFINE(HAVE_LIBLCMS) + dnl for now this implies MNG_INCLUDE_LCMS in the headers: + AC_DEFINE(MNG_FULL_CMS) + _restore=0 + have_lcms=yes + ],[ + have_lcms=no + ]) + ]) + dnl give feedback only if the user asked specifically for lcms + if test "x$with_lcms" != "x_auto" -a "x$have_lcms" != "xyes"; then + AC_MSG_WARN([lcms not found... disabling CMS support]) + fi + fi + + test $_restore -eq 1 && CPPFLAGS=$_cppflags LDFLAGS=$_ldflags + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/Engine/lib/lmng/makefiles/makefile.bcb3 b/Engine/lib/lmng/makefiles/makefile.bcb3 new file mode 100644 index 000000000..3ddcb2d13 --- /dev/null +++ b/Engine/lib/lmng/makefiles/makefile.bcb3 @@ -0,0 +1,108 @@ +# +# For conditions of distribution and use, see copyright notice in libmng.h +# +# makefile for libmng - THE MNG library +# this makefile is suitable for Borland C++ Builder. +# it works (at least) with Borland C++ Builder v3 + +# Configuration options are now in mng_conf.h +# this option forces dll compatibility +MNGOPT = -DMNG_BUILD_DLL + +# The name of your C compiler: +CC= bcc32 + +# compiler options: +CFLAGS= -WD -O2 -Hc -w-par -k -y -v -vi -c -tWD \ + -wuse -wucp -wstv -wstu -wsig -wpin -wnod -wnak -wdef -wcln -wbbf -wasm -wamp \ + -wamb -Tkh30000 -ff -5 -I.;..\zlib;..\jpgsrc6b;..\lcms\include $(MNGOPT) + +# source files +SOURCES= libmng_hlapi.c libmng_callback_xs.c libmng_prop_xs.c libmng_chunk_xs.c \ + libmng_chunk_descr.c libmng_read.c libmng_write.c libmng_display.c \ + libmng_object_prc.c libmng_chunk_prc.c libmng_chunk_io.c libmng_error.c \ + libmng_trace.c libmng_pixels.c libmng_filter.c libmng_dither.c \ + libmng_zlib.c libmng_jpeg.c libmng_cms.c + +# object files +OBJECTS= libmng_hlapi.obj libmng_callback_xs.obj libmng_prop_xs.obj libmng_chunk_xs.obj \ + libmng_chunk_descr.obj libmng_read.obj libmng_write.obj libmng_display.obj \ + libmng_object_prc.obj libmng_chunk_prc.obj libmng_chunk_io.obj libmng_error.obj \ + libmng_trace.obj libmng_pixels.obj libmng_filter.obj libmng_dither.obj \ + libmng_zlib.obj libmng_jpeg.obj libmng_cms.obj + +# type dependancies +.c.obj: + $(CC) $(CFLAGS) -c{ $<} + +# make options +all: libmng.lib + +clean: + - del *.obj + - del libmng.lib + +# file dependancies +libmng.lib: $(OBJECTS) + - del libmng.lib + tlib libmng.lib /E /C @&&| ++libmng_hlapi.obj +libmng_callback_xs.obj +libmng_prop_xs.obj +libmng_chunk_xs.obj & ++libmng_read.obj +libmng_write.obj +libmng_display.obj & ++libmng_object_prc.obj +libmng_chunk_prc.obj +libmng_chunk_io.obj +libmng_error.obj & ++libmng_trace.obj +libmng_pixels.obj +libmng_filter.obj +libmng_dither.obj & ++libmng_zlib.obj +libmng_jpeg.obj +libmng_cms.obj +| + +libmng_hlapi.obj: libmng_hlapi.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_objects.h libmng_object_prc.h \ + libmng_chunks.h libmng_memory.h libmng_error.h libmng_trace.h libmng_read.h \ + libmng_write.h libmng_display.h libmng_zlib.h libmng_cms.h libmng_zlib.h +libmng_callback_xs.obj: libmng_callback_xs.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h +libmng_prop_xs.obj: libmng_prop_xs.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h libmng_cms.h +libmng_chunk_xs.obj: libmng_chunk_xs.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_chunks.h libmng_chunk_prc.h libmng_error.h libmng_trace.h +libmng_read.obj: libmng_read.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_objects.h libmng_object_prc.h \ + libmng_chunks.h libmng_chunk_prc.h libmng_chunk_io.h libmng_memory.h \ + libmng_error.h libmng_trace.h libmng_read.h libmng_display.h +libmng_write.obj: libmng_write.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h libmng_write.h +libmng_display.obj: libmng_display.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_objects.h libmng_object_prc.h libmng_memory.h \ + libmng_error.h libmng_trace.h libmng_zlib.h libmng_cms.h \ + libmng_pixels.h libmng_display.h +libmng_object_prc.obj: libmng_object_prc.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_objects.h libmng_object_prc.h libmng_memory.h \ + libmng_error.h libmng_trace.h libmng_display.h libmng_pixels.h +libmng_chunk_descr.obj: libmng_chunk_descr.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_chunks.h libmng_chunk_prc.h libmng_memory.h \ + libmng_error.h libmng_trace.h +libmng_chunk_prc.obj: libmng_chunk_prc.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_chunks.h libmng_chunk_prc.h libmng_memory.h \ + libmng_error.h libmng_trace.h +libmng_chunk_io.obj: libmng_chunk_io.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_objects.h libmng_object_prc.h libmng_chunks.h \ + libmng_chunk_io.h libmng_chunk_prc libmng_memory.h libmng_error.h \ + libmng_trace.h libmng_display.h libmng_zlib.h libmng_pixels.h +libmng_error.obj: libmng_error.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h +libmng_trace.obj: libmng_trace.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h +libmng_pixels.obj: libmng_pixels.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_objects.h libmng_memory.h libmng_error.h libmng_trace.h \ + libmng_cms.h libmng_filter.h libmng_pixels.h +libmng_filter.obj: libmng_filter.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h libmng_filter.h +libmng_dither.obj: libmng_dither.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h libmng_dither.h +libmng_zlib.obj: libmng_zlib.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_memory.h libmng_error.h libmng_trace.h libmng_pixels.h \ + libmng_filter.h libmng_zlib.h +libmng_jpeg.obj: libmng_jpeg.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_memory.h libmng_error.h libmng_trace.h \ + libmng_pixels.h libmng_jpeg.h +libmng_cms.obj: libmng_cms.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_objects.h libmng_error.h libmng_trace.h libmng_cms.h + diff --git a/Engine/lib/lmng/makefiles/makefile.dj b/Engine/lib/lmng/makefiles/makefile.dj new file mode 100644 index 000000000..2cef08881 --- /dev/null +++ b/Engine/lib/lmng/makefiles/makefile.dj @@ -0,0 +1,155 @@ +# +# For conditions of distribution and use, see copyright notice in libmng.h +# +# makefile for libmng - THE MNG library +# This makefile have been tested on DJGPP v2 +# (Based on makefile.linux since both are GNU compilers) +# +# By Silvio Fonseca - gissi@sti.com.br + +#compiler +CC=gcc + +#default build options +OPTIONS= + +#DJGPP directory +prefix=C:/DJGPP +installprefix=C:\DJGPP + +#ZLIB Library and includes +ZLIBLIB=$(prefix)/lib +#ZLIBLIB=../zlib +ZLIBINC=$(prefix)/include +#ZLIBINC=../zlib + +#Jpeg library and includes +JPEGLIB=$(prefix)/lib +#JPEGLIB=../jpgsrc +JPEGINC=$(prefix)/include +#JPEGINC=../jpgsrc + +#Lcms library and includes +LCMSLIB=$(prefix)/lib +#LCMSLIB=../lcms +LCMSINC=$(prefix)/include +#LCMSINC=../lcms + +ALIGN= +# for i386: +#ALIGN=-malign-loops=2 -malign-functions=2 + +WARNMORE=-Wwrite-strings -Wpointer-arith -Wshadow \ + -Wmissing-declarations -Wtraditional -Wcast-align \ + -Wstrict-prototypes -Wmissing-prototypes #-Wconversion + +CFLAGS=-I$(ZLIBINC) -I$(JPEGINC) -I$(LCMSINC) -Wall -O3 -funroll-loops \ + $(OPTIONS) $(ALIGN) # $(WARNMORE) -g +LDFLAGS=-L. -Wl,-rpath,. \ + -L$(ZLIBLIB) -Wl,-rpath,$(ZLIBLIB) \ + -L$(JPEGLIB) -Wl,-rpath,$(JPEGLIB) \ + -L$(LCMSLIB) -Wl,-rpath,$(LCMSLIB) \ + -lmng -lz -ljpeg -llcms -lm +INCPATH=$(prefix)/include +LIBPATH=$(prefix)/lib + +OBJS = \ + libmng_callback_xs.o \ + libmng_chunk_io.o \ + libmng_chunk_descr.o \ + libmng_chunk_prc.o \ + libmng_chunk_xs.o \ + libmng_cms.o \ + libmng_display.o \ + libmng_dither.o \ + libmng_error.o \ + libmng_filter.o \ + libmng_hlapi.o \ + libmng_jpeg.o \ + libmng_object_prc.o \ + libmng_pixels.o \ + libmng_prop_xs.o \ + libmng_read.o \ + libmng_trace.o \ + libmng_write.o \ + libmng_zlib.o + +OBJSDLL = $(OBJS:.0=.pic.o) + +.SUFFIXES: .c .o .pic.o + +.c.pic.o: + $(CC) -c $(CFLAGS) -fPIC -o $@ $*.c + +all: libmng.a + +libmng.a: $(OBJS) + ar rc $@ $(OBJS) + ranlib $@ + +install: libmng.a + -@md $(installprefix)\include $(installprefix)\lib + copy libmng.h $(installprefix)\include + copy libmng_conf.h $(installprefix)\include + copy libmng_types.h $(installprefix)\include + copy libmng.a $(installprefix)\lib + +clean: + del *.o + del libmng.a + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +libmng_hlapi.o libmng_hlapi.pic.o: libmng_hlapi.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_objects.h libmng_object_prc.h \ + libmng_chunks.h libmng_memory.h libmng_error.h libmng_trace.h libmng_read.h \ + libmng_write.h libmng_display.h libmng_zlib.h libmng_cms.h libmng_zlib.h +libmng_callback_xs.o libmng_callback_xs.pic.o: libmng_callback_xs.c libmng.h \ + libmng_conf.h libmng_types.h libmng_data.h libmng_error.h libmng_trace.h +libmng_prop_xs.o libmng_prop_xs.pic.o: libmng_prop_xs.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_error.h libmng_trace.h libmng_cms.h +libmng_chunk_xs.o libmng_chunk_xs.pic.o: libmng_chunk_xs.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_chunks.h libmng_chunk_prc.h \ + libmng_error.h libmng_trace.h +libmng_read.o libmng_read.pic.o: libmng_read.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_objects.h libmng_object_prc.h \ + libmng_chunks.h libmng_chunk_prc.h libmng_chunk_io.h libmng_memory.h \ + libmng_error.h libmng_trace.h libmng_read.h libmng_display.h +libmng_write.o libmng_write.pic.o: libmng_write.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h libmng_write.h +libmng_display.o libmng_display.pic.o: libmng_display.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_objects.h libmng_object_prc.h libmng_memory.h \ + libmng_error.h libmng_trace.h libmng_zlib.h libmng_cms.h libmng_pixels.h \ + libmng_display.h +libmng_object_prc.o libmng_object_prc.pic.o: libmng_object_prc.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_objects.h libmng_object_prc.h libmng_memory.h \ + libmng_error.h libmng_trace.h libmng_display.h libmng_pixels.h +libmng_chunk_descr.o libmng_chunk_descr.pic.o: libmng_chunk_descr.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_chunks.h libmng_chunk_descr.h \ + libmng_chunk_prc.h libmng_memory.h libmng_error.h libmng_trace.h +libmng_chunk_prc.o libmng_chunk_prc.pic.o: libmng_chunk_prc.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_chunks.h libmng_chunk_prc.h libmng_memory.h \ + libmng_error.h libmng_trace.h +libmng_chunk_io.o libmng_chunk_io.pic.o: libmng_chunk_io.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_objects.h libmng_object_prc.h \ + libmng_chunks.h libmng_chunk_io.h libmng_chunk_prc.h libmng_memory.h libmng_error.h \ + libmng_trace.h libmng_display.h libmng_zlib.h libmng_pixels.h +libmng_error.o libmng_error.pic.o: libmng_error.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h +libmng_trace.o libmng_trace.pic.o: libmng_trace.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h +libmng_pixels.o libmng_pixels.pic.o: libmng_pixels.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_objects.h libmng_memory.h libmng_error.h libmng_trace.h \ + libmng_cms.h libmng_filter.h libmng_pixels.h +libmng_filter.o libmng_filter.pic.o: libmng_filter.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h libmng_filter.h +libmng_dither.o libmng_dither.pic.o: libmng_dither.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h libmng_dither.h +libmng_zlib.o libmng_zlib.pic.o: libmng_zlib.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_memory.h libmng_error.h libmng_trace.h libmng_pixels.h \ + libmng_filter.h libmng_zlib.h +libmng_jpeg.o libmng_jpeg.pic.o: libmng_jpeg.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_memory.h libmng_error.h libmng_trace.h libmng_pixels.h libmng_jpeg.h +libmng_cms.o libmng_cms.pic.o: libmng_cms.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_objects.h libmng_error.h libmng_trace.h libmng_cms.h + diff --git a/Engine/lib/lmng/makefiles/makefile.linux b/Engine/lib/lmng/makefiles/makefile.linux new file mode 100644 index 000000000..e06a8294c --- /dev/null +++ b/Engine/lib/lmng/makefiles/makefile.linux @@ -0,0 +1,180 @@ +# +# For conditions of distribution and use, see copyright notice in libmng.h +# +# makefile for libmng - THE MNG library +# this makefile is suitable for Linux ELF with gcc +# +# (this file is heavily copied from makefile.linux in the libpng package) + +# compiler +CC=gcc + +# default build options (this forces shared library compatibility!!) +#OPTIONS = -DMNG_BUILD_SO +OPTIONS = -DMNG_BUILD_SO -DMNG_FULL_CMS + +# where "make install" puts libmng.a,libmng.so*,libmng.h,libmng_conf.h,libmng_types.h +prefix=/usr/local + +# Where the zlib library and include files are located +#ZLIBLIB=../zlib +#ZLIBINC=../zlib +ZLIBLIB=/usr/local/lib +ZLIBINC=/usr/local/include + +# Where the jpeg library and include files are located +#JPEGLIB=../jpgsrc +#JPEGINC=../jpgsrc +JPEGLIB=/usr/local/lib +JPEGINC=/usr/local/include + +# Where the lcms library and include files are located +#LCMSLIB=../lcms/lib +#LCMSINC=../lcms/source +LCMSLIB=/usr/local/lib +LCMSINC=/usr/local/include + +ALIGN= +# for i386: +#ALIGN=-malign-loops=2 -malign-functions=2 + +WARNMORE=-Wwrite-strings -Wpointer-arith -Wshadow \ + -Wmissing-declarations -Wtraditional -Wcast-align \ + -Wstrict-prototypes -Wmissing-prototypes #-Wconversion + +# for pgcc version 2.95.1, -O3 is buggy; don't use it. + +CFLAGS=-I$(ZLIBINC) -I$(JPEGINC) -I$(LCMSINC) -Wall -O3 -funroll-loops \ + $(OPTIONS) $(ALIGN) # $(WARNMORE) -g +LDFLAGS=-L. -Wl,-rpath,. \ + -L$(ZLIBLIB) -Wl,-rpath,$(ZLIBLIB) \ + -L$(JPEGLIB) -Wl,-rpath,$(JPEGLIB) \ + -L$(LCMSLIB) -Wl,-rpath,$(LCMSLIB) \ + -lmng -lz -ljpeg -llcms -lm + +RANLIB=ranlib +#RANLIB=echo + +# current version numbers +MNGMAJ = 1 +MNGMIN = 1.0.9 +MNGVER = $(MNGMAJ).$(MNGMIN) + +INCPATH=$(prefix)/include +LIBPATH=$(prefix)/lib + +OBJS = \ + libmng_callback_xs.o \ + libmng_chunk_io.o \ + libmng_chunk_descr.o \ + libmng_chunk_prc.o \ + libmng_chunk_xs.o \ + libmng_cms.o \ + libmng_display.o \ + libmng_dither.o \ + libmng_error.o \ + libmng_filter.o \ + libmng_hlapi.o \ + libmng_jpeg.o \ + libmng_object_prc.o \ + libmng_pixels.o \ + libmng_prop_xs.o \ + libmng_read.o \ + libmng_trace.o \ + libmng_write.o \ + libmng_zlib.o + +OBJSDLL = $(OBJS:.0=.pic.o) + +.SUFFIXES: .c .o .pic.o + +.c.pic.o: + $(CC) -c $(CFLAGS) -fPIC -o $@ $*.c + +all: libmng.a libmng.so + +libmng.a: $(OBJS) + ar rc $@ $(OBJS) + $(RANLIB) $@ + +libmng.so: libmng.so.$(MNGMAJ) + ln -sf libmng.so.$(MNGMAJ) libmng.so + +libmng.so.$(MNGMAJ): libmng.so.$(MNGVER) + ln -sf libmng.so.$(MNGVER) libmng.so.$(MNGMAJ) + +libmng.so.$(MNGVER): $(OBJSDLL) +# $(CC) -shared -Wl,-soname,libmng.so.$(MNGMAJ) -o libmng.so.$(MNGVER) \ +# $(OBJSDLL) -L$(ZLIBLIB) -L$(JPEGLIB) -L$(LCMSLIB) -lz -lm -lc + $(CC) -shared -Wl,-soname,libmng.so.$(MNGMAJ) -o libmng.so.$(MNGVER) \ + $(OBJSDLL) -L$(ZLIBLIB) -L$(JPEGLIB) -ljpeg -L$(LCMSLIB) -llcms \ + -lz -lm -lc + +install: libmng.a libmng.so.$(MNGVER) + -@mkdir $(INCPATH) $(LIBPATH) + cp libmng.h libmng_conf.h libmng_types.h $(INCPATH) + chmod 644 $(INCPATH)/libmng.h $(INCPATH)/libmng_conf.h $(INCPATH)/libmng_types.h + cp libmng.a libmng.so.$(MNGVER) $(LIBPATH) + chmod 755 $(LIBPATH)/libmng.so.$(MNGVER) + -@/bin/rm -f $(LIBPATH)/libmng.so.$(MNGMAJ) $(LIBPATH)/libmng.so + (cd $(LIBPATH); ln -sf libmng.so.$(MNGVER) libmng.so.$(MNGMAJ); \ + ln -sf libmng.so.$(MNGMAJ) libmng.so) + +clean: + /bin/rm -f *.o libmng.a libmng.so* + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +libmng_hlapi.o libmng_hlapi.pic.o: libmng_hlapi.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_objects.h libmng_object_prc.h \ + libmng_chunks.h libmng_memory.h libmng_error.h libmng_trace.h libmng_read.h \ + libmng_write.h libmng_display.h libmng_zlib.h libmng_cms.h libmng_zlib.h +libmng_callback_xs.o libmng_callback_xs.pic.o: libmng_callback_xs.c libmng.h \ + libmng_conf.h libmng_types.h libmng_data.h libmng_error.h libmng_trace.h +libmng_prop_xs.o libmng_prop_xs.pic.o: libmng_prop_xs.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_error.h libmng_trace.h libmng_cms.h +libmng_chunk_xs.o libmng_chunk_xs.pic.o: libmng_chunk_xs.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_chunks.h libmng_chunk_prc.h \ + libmng_error.h libmng_trace.h +libmng_read.o libmng_read.pic.o: libmng_read.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_objects.h libmng_object_prc.h \ + libmng_chunks.h libmng_chunk_prc.h libmng_chunk_io.h libmng_memory.h \ + libmng_error.h libmng_trace.h libmng_read.h libmng_display.h +libmng_write.o libmng_write.pic.o: libmng_write.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h libmng_write.h +libmng_display.o libmng_display.pic.o: libmng_display.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_objects.h libmng_object_prc.h libmng_memory.h \ + libmng_error.h libmng_trace.h libmng_zlib.h libmng_cms.h libmng_pixels.h \ + libmng_display.h +libmng_object_prc.o libmng_object_prc.pic.o: libmng_object_prc.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_objects.h libmng_object_prc.h libmng_memory.h \ + libmng_error.h libmng_trace.h libmng_display.h libmng_pixels.h +libmng_chunk_descr.o libmng_chunk_descr.pic.o: libmng_chunk_descr.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_chunks.h libmng_chunk_descr.h libmng_memory.h \ + libmng_chunk_prc.h libmng_error.h libmng_trace.h +libmng_chunk_prc.o libmng_chunk_prc.pic.o: libmng_chunk_prc.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_chunks.h libmng_chunk_prc.h libmng_memory.h \ + libmng_error.h libmng_trace.h +libmng_chunk_io.o libmng_chunk_io.pic.o: libmng_chunk_io.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_objects.h libmng_object_prc.h \ + libmng_chunks.h libmng_chunk_io.h libmng_chunk_prc.h libmng_memory.h libmng_error.h \ + libmng_trace.h libmng_display.h libmng_zlib.h libmng_pixels.h +libmng_error.o libmng_error.pic.o: libmng_error.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h +libmng_trace.o libmng_trace.pic.o: libmng_trace.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h +libmng_pixels.o libmng_pixels.pic.o: libmng_pixels.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_objects.h libmng_memory.h libmng_error.h libmng_trace.h \ + libmng_cms.h libmng_filter.h libmng_pixels.h +libmng_filter.o libmng_filter.pic.o: libmng_filter.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h libmng_filter.h +libmng_dither.o libmng_dither.pic.o: libmng_dither.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h libmng_dither.h +libmng_zlib.o libmng_zlib.pic.o: libmng_zlib.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_memory.h libmng_error.h libmng_trace.h libmng_pixels.h \ + libmng_filter.h libmng_zlib.h +libmng_jpeg.o libmng_jpeg.pic.o: libmng_jpeg.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_memory.h libmng_error.h libmng_trace.h libmng_pixels.h libmng_jpeg.h +libmng_cms.o libmng_cms.pic.o: libmng_cms.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_objects.h libmng_error.h libmng_trace.h libmng_cms.h + diff --git a/Engine/lib/lmng/makefiles/makefile.mingw b/Engine/lib/lmng/makefiles/makefile.mingw new file mode 100644 index 000000000..703ae3059 --- /dev/null +++ b/Engine/lib/lmng/makefiles/makefile.mingw @@ -0,0 +1,164 @@ +# +# For conditions of distribution and use, see copyright notice in libmng.h +# +# makefile for libmng - THE MNG library +# this makefile is for MinGW32, it have been tested with gcc 2.95.3, +# binutils 2.11.90 and mingw-runtime 1.0 +# +# By Benoit Blanchon - benoit.blanchon@laposte.net +# +# Note : this makefile builds a static library; although it's seems to be +# possible to build working DLL and import lib, I didn't manage do to it. +# If you do, please let me know. + +# outputs +LIBMNG_A = libmng.a +INSTALL_PREFIX = C:/MinGW/ +# maybe you sould replace with anti-slashes + +# default build options +OPTIONS = -DMNG_NO_CMS -DMNG_ACCESS_CHUNKS -DMNG_STORE_CHUNKS + +# Where the zlib library and include files are located +ZLIBLIB=-lz +#ZLIBLIB=-L../zlib -lz +#ZLIBINC=-I../zlib + +# Where the jpeg library and include files are located +JPEGLIB=-ljpeg +#JPEGLIB=-L../jpgsrc -ljpeg +#JPEGINC=-I../jpgsrc + +# Where the lcms library and include files are located +#LCMSLIB=-llcms +#LCMSLIB=-L../lcms/lib -llcms +#LCMSINC=-I../lcms/source + +# file deletion command +RM=rm -f +#RM=del + +# directory creation command +MKDIR=mkdir -p + +# file copy command +COPY=cp +#COPY=copy + +# compiler +CC=gcc + +ALIGN= +# for i386: +#ALIGN=-malign-loops=2 -malign-functions=2 + +CFLAGS=$(ZLIBINC) $(JPEGINC) $(LCMSINC) -Wall -O3 -funroll-loops $(OPTIONS) $(ALIGN) +LDFLAGS=-L. -lmng $(ZLIBLIB) $(JPEGLIB) $(LCMSLIB) -lm + +# library (.a) file creation command +AR= ar rc +# second step in .a creation (use "touch" if not needed) +AR2= ranlib + +INCPATH=$(prefix)/include +LIBPATH=$(prefix)/lib + +OBJS = \ + libmng_callback_xs.o \ + libmng_chunk_io.o \ + libmng_chunk_descr.o \ + libmng_chunk_prc.o \ + libmng_chunk_xs.o \ + libmng_cms.o \ + libmng_display.o \ + libmng_dither.o \ + libmng_error.o \ + libmng_filter.o \ + libmng_hlapi.o \ + libmng_jpeg.o \ + libmng_object_prc.o \ + libmng_pixels.o \ + libmng_prop_xs.o \ + libmng_read.o \ + libmng_trace.o \ + libmng_write.o \ + libmng_zlib.o + +.SUFFIXES: .c .o + +.c.o: + $(CC) -c $(CFLAGS) -o $@ $*.c + +all: $(LIBMNG_A) + +$(LIBMNG_A) : $(OBJS) + $(RM) $@ + $(AR) $@ $(OBJS) + $(AR2) $@ + +install : $(LIBMNG_A) + $(MKDIR) $(INSTALL_PREFIX)include + $(COPY) libmng.h $(INSTALL_PREFIX)include + $(COPY) libmng_conf.h $(INSTALL_PREFIX)include + $(COPY) libmng_types.h $(INSTALL_PREFIX)include + $(MKDIR) $(INSTALL_PREFIX)lib + $(COPY) $(LIBMNG_A) $(INSTALL_PREFIX)lib + +clean: + $(RM) *.o + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +libmng_hlapi.o : libmng_hlapi.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_objects.h libmng_object_prc.h \ + libmng_chunks.h libmng_memory.h libmng_error.h libmng_trace.h libmng_read.h \ + libmng_write.h libmng_display.h libmng_zlib.h libmng_cms.h libmng_zlib.h +libmng_callback_xs.o : libmng_callback_xs.c libmng.h \ + libmng_conf.h libmng_types.h libmng_data.h libmng_error.h libmng_trace.h +libmng_prop_xs.o : libmng_prop_xs.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_error.h libmng_trace.h libmng_cms.h +libmng_chunk_xs.o : libmng_chunk_xs.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_chunks.h libmng_chunk_prc.h \ + libmng_error.h libmng_trace.h +libmng_read.o : libmng_read.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_objects.h libmng_object_prc.h \ + libmng_chunks.h libmng_chunk_prc.h libmng_chunk_io.h libmng_memory.h \ + libmng_error.h libmng_trace.h libmng_read.h libmng_display.h +libmng_write.o : libmng_write.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h libmng_write.h +libmng_display.o : libmng_display.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_objects.h libmng_object_prc.h libmng_memory.h \ + libmng_error.h libmng_trace.h libmng_zlib.h libmng_cms.h libmng_pixels.h \ + libmng_display.h +libmng_object_prc.o : libmng_object_prc.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_objects.h libmng_object_prc.h libmng_memory.h \ + libmng_error.h libmng_trace.h libmng_display.h libmng_pixels.h +libmng_chunk_descr.o : libmng_chunk_descr.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_chunks.h libmng_chunk_descr.h \ + libmng_chunk_prc.h libmng_memory.h libmng_error.h libmng_trace.h +libmng_chunk_prc.o : libmng_chunk_prc.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_chunks.h libmng_chunk_prc.h libmng_memory.h \ + libmng_error.h libmng_trace.h +libmng_chunk_io.o : libmng_chunk_io.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_objects.h libmng_object_prc.h \ + libmng_chunks.h libmng_chunk_io.h libmng_chunk_prc.h libmng_memory.h libmng_error.h \ + libmng_trace.h libmng_display.h libmng_zlib.h libmng_pixels.h +libmng_error.o : libmng_error.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h +libmng_trace.o : libmng_trace.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h +libmng_pixels.o : libmng_pixels.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_objects.h libmng_memory.h libmng_error.h libmng_trace.h \ + libmng_cms.h libmng_filter.h libmng_pixels.h +libmng_filter.o : libmng_filter.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h libmng_filter.h +libmng_dither.o : libmng_dither.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h libmng_dither.h +libmng_zlib.o : libmng_zlib.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_memory.h libmng_error.h libmng_trace.h libmng_pixels.h \ + libmng_filter.h libmng_zlib.h +libmng_jpeg.o : libmng_jpeg.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_memory.h libmng_error.h libmng_trace.h libmng_pixels.h libmng_jpeg.h +libmng_cms.o : libmng_cms.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_objects.h libmng_error.h libmng_trace.h libmng_cms.h + diff --git a/Engine/lib/lmng/makefiles/makefile.mingwdll b/Engine/lib/lmng/makefiles/makefile.mingwdll new file mode 100644 index 000000000..283721828 --- /dev/null +++ b/Engine/lib/lmng/makefiles/makefile.mingwdll @@ -0,0 +1,158 @@ +# +# For conditions of distribution and use, see copyright notice in libmng.h +# +# makefile for libmng - THE MNG library +# this makefile is for MinGW32, it has been tested with gcc 3.1, +# binutils 2.12.90 and mingw-runtime 2.0 +# +# By Benoit Blanchon - benoit.blanchon@laposte.net +# DLL mods by F. Richter +# + +# outputs +LIBMNG_A = libmng.a +LIBMNG_DLL = libmng.1.dll +INSTALL_PREFIX = C:/MinGW/ +# maybe you sould replace with anti-slashes + +# default build options +OPTIONS = -DMNG_BUILD_DLL -DMNG_ACCESS_CHUNKS -DMNG_STORE_CHUNKS + +# Where the zlib library and include files are located +ZLIBLIB=-lz +#ZLIBLIB=-L../zlib -lz +#ZLIBINC=-I../zlib + +# Where the jpeg library and include files are located +JPEGLIB=-ljpeg +#JPEGLIB=-L../jpgsrc -ljpeg +#JPEGINC=-I../jpgsrc + +# Where the lcms library and include files are located +#LCMSLIB=-llcms +LCMSLIB=-L../lcms/lib -llcms +LCMSINC=-I../lcms/source + +# file deletion command +RM=rm -f +#RM=del + +# directory creation command +MKDIR=mkdir -p + +# file copy command +COPY=cp +#COPY=copy + +# compiler +CC=gcc + +ALIGN= +# for i386: +#ALIGN=-malign-loops=2 -malign-functions=2 + +CFLAGS=$(ZLIBINC) $(JPEGINC) $(LCMSINC) -Wall -O3 -funroll-loops $(OPTIONS) $(ALIGN) -s +LDFLAGS=-L. -lmng $(ZLIBLIB) $(JPEGLIB) $(LCMSLIB) -lm -s + +INCPATH=$(prefix)/include +LIBPATH=$(prefix)/lib + +OBJS = \ + libmng_callback_xs.o \ + libmng_chunk_io.o \ + libmng_chunk_descr.o \ + libmng_chunk_prc.o \ + libmng_chunk_xs.o \ + libmng_cms.o \ + libmng_display.o \ + libmng_dither.o \ + libmng_error.o \ + libmng_filter.o \ + libmng_hlapi.o \ + libmng_jpeg.o \ + libmng_object_prc.o \ + libmng_pixels.o \ + libmng_prop_xs.o \ + libmng_read.o \ + libmng_trace.o \ + libmng_write.o \ + libmng_zlib.o + +.SUFFIXES: .c .o + +.c.o: + $(CC) -c $(CFLAGS) -o $@ $*.c + +all: $(LIBMNG_A) + +$(LIBMNG_A): $(LIBMNG_DLL) + +$(LIBMNG_DLL) : $(OBJS) + dllwrap --implib=$(LIBMNG_A) --dllname=$(LIBMNG_DLL) $(OBJS) $(LDFLAGS) + +install : $(LIBMNG_A) + $(MKDIR) $(INSTALL_PREFIX)include + $(COPY) libmng.h $(INSTALL_PREFIX)include + $(COPY) libmng_conf.h $(INSTALL_PREFIX)include + $(COPY) libmng_types.h $(INSTALL_PREFIX)include + $(MKDIR) $(INSTALL_PREFIX)lib + $(COPY) $(LIBMNG_A) $(INSTALL_PREFIX)lib + +clean: + $(RM) *.o + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +libmng_hlapi.o : libmng_hlapi.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_objects.h libmng_object_prc.h \ + libmng_chunks.h libmng_memory.h libmng_error.h libmng_trace.h libmng_read.h \ + libmng_write.h libmng_display.h libmng_zlib.h libmng_cms.h libmng_zlib.h +libmng_callback_xs.o : libmng_callback_xs.c libmng.h \ + libmng_conf.h libmng_types.h libmng_data.h libmng_error.h libmng_trace.h +libmng_prop_xs.o : libmng_prop_xs.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_error.h libmng_trace.h libmng_cms.h +libmng_chunk_xs.o : libmng_chunk_xs.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_chunks.h libmng_chunk_prc.h \ + libmng_error.h libmng_trace.h +libmng_read.o : libmng_read.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_objects.h libmng_object_prc.h \ + libmng_chunks.h libmng_chunk_prc.h libmng_chunk_io.h libmng_memory.h \ + libmng_error.h libmng_trace.h libmng_read.h libmng_display.h +libmng_write.o : libmng_write.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h libmng_write.h +libmng_display.o : libmng_display.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_objects.h libmng_object_prc.h libmng_memory.h \ + libmng_error.h libmng_trace.h libmng_zlib.h libmng_cms.h libmng_pixels.h \ + libmng_display.h +libmng_object_prc.o : libmng_object_prc.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_objects.h libmng_object_prc.h libmng_memory.h \ + libmng_error.h libmng_trace.h libmng_display.h libmng_pixels.h +libmng_chunk_descr.o : libmng_chunk_descr.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_chunks.h libmng_chunk_descr.h \ + libmng_chunk_prc.h libmng_memory.h libmng_error.h libmng_trace.h +libmng_chunk_prc.o : libmng_chunk_prc.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_chunks.h libmng_chunk_prc.h libmng_memory.h \ + libmng_error.h libmng_trace.h +libmng_chunk_io.o : libmng_chunk_io.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_objects.h libmng_object_prc.h \ + libmng_chunks.h libmng_chunk_io.h libmng_chunk_prc.h libmng_memory.h libmng_error.h \ + libmng_trace.h libmng_display.h libmng_zlib.h libmng_pixels.h +libmng_error.o : libmng_error.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h +libmng_trace.o : libmng_trace.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h +libmng_pixels.o : libmng_pixels.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_objects.h libmng_memory.h libmng_error.h libmng_trace.h \ + libmng_cms.h libmng_filter.h libmng_pixels.h +libmng_filter.o : libmng_filter.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h libmng_filter.h +libmng_dither.o : libmng_dither.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h libmng_dither.h +libmng_zlib.o : libmng_zlib.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_memory.h libmng_error.h libmng_trace.h libmng_pixels.h \ + libmng_filter.h libmng_zlib.h +libmng_jpeg.o : libmng_jpeg.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_memory.h libmng_error.h libmng_trace.h libmng_pixels.h libmng_jpeg.h +libmng_cms.o : libmng_cms.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_objects.h libmng_error.h libmng_trace.h libmng_cms.h + diff --git a/Engine/lib/lmng/makefiles/makefile.qnx b/Engine/lib/lmng/makefiles/makefile.qnx new file mode 100644 index 000000000..28f18d763 --- /dev/null +++ b/Engine/lib/lmng/makefiles/makefile.qnx @@ -0,0 +1,160 @@ +# +# For conditions of distribution and use, see copyright notice in libmng.h +# +# makefile for libmng - THE MNG library +# this makefile is suitable for QNX Neutrino + +# Configuration options are now in libmng_conf.h + +# The architecture of your target +# one of arm, mips, ppc, sh, x86 +ARCH = mips + +# The name of the library +LIBNAME = libmng +ARNAME = $(LIBNAME).a +SONAME = $(LIBNAME).so + +# current version numbers +MNGMAJ = 1 +MNGMIN = 0.9 +MNGVER = $(MNGMAJ).$(MNGMIN) + +# The artefact output folder +OBJDIR = bin + +# Location of jpeg header files +JPEG_INC = $(QNX_TARGET)/usr/include/jpeg + +# Location of zlib header files +ZLIB_INC = $(QNX_TARGET)/usr/include + +# Location of lcms header files +# (switch on MNG_FULL_CMS in libmng_conf.h if you want to use this) +LCMS_INC = $(QNX_TARGET)/usr/include/lcms + +# default build defines +DEF = +DEF_SO = -DMNG_BUILD_SO + +# compiler options: +CFLAGS = -O2 -funroll-loops + +# include paths +INC = -I$(ZLIB_INC) -I$(JPEG_INC) + +# The name of your C compiler: +CC = nto$(ARCH)-gcc + +# source files +SRC= \ + libmng_callback_xs.c \ + libmng_chunk_io.c \ + libmng_chunk_descr.c \ + libmng_chunk_prc.c \ + libmng_chunk_xs.c \ + libmng_cms.c \ + libmng_display.c \ + libmng_dither.c \ + libmng_error.c \ + libmng_filter.c \ + libmng_hlapi.c \ + libmng_jpeg.c \ + libmng_object_prc.c \ + libmng_pixels.c \ + libmng_prop_xs.c \ + libmng_read.c \ + libmng_trace.c \ + libmng_write.c \ + libmng_zlib.c + +# object files +OBJ=$(addprefix $(OBJDIR)/$(ARCH)/, $(SRC:%.c=%.o)) + +# object files for shared object +OBJ_SO=$(addprefix $(OBJDIR)/$(ARCH)/, $(SRC:%.c=%.pic.o)) + +# type dependancies +$(OBJDIR)/$(ARCH)/%.o: %.c + $(CC) $(CFLAGS) $(INC) $(DEF) -o $@ -c $< + +$(OBJDIR)/$(ARCH)/%.pic.o: %.c + $(CC) $(CFLAGS) $(INC) $(DEF_SO) -fPIC -o $@ -c $< + +all: init $(ARNAME) $(SONAME) + +init: + if [ ! -d $(OBJDIR)/$(ARCH) ]; then mkdir -p $(OBJDIR)/$(ARCH); fi + +$(ARNAME): $(OBJ) + ar r $(OBJDIR)/$(ARCH)/$(ARNAME) $(OBJ) + +$(SONAME): $(SONAME).$(MNGMAJ) + ln -sf $(OBJDIR)/$(ARCH)/$(SONAME).$(MNGMAJ) $(OBJDIR)/$(ARCH)/$(SONAME) + +$(SONAME).$(MNGMAJ): $(SONAME).$(MNGVER) + ln -sf $(OBJDIR)/$(ARCH)/$(SONAME).$(MNGVER) $(OBJDIR)/$(ARCH)/$(SONAME).$(MNGMAJ) + +$(SONAME).$(MNGVER): $(OBJ_SO) + $(CC) -shared -Wl,-soname,$(SONAME).$(MNGMAJ) -o $(OBJDIR)/$(ARCH)/$(SONAME).$(MNGVER) $(OBJ_SO) \ + -lz -lm -ljpeg +# -lz -lm -ljpeg -llcms + +clean: + rm -f $(OBJ) $(OBJ_SO) + rm -f $(OBJDIR)/$(ARCH)/$(ARNAME) $(OBJDIR)/$(ARCH)/$(SONAME)* + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +libmng_hlapi.o libmng_hlapi.pic.o: libmng_hlapi.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_objects.h libmng_object_prc.h \ + libmng_chunks.h libmng_memory.h libmng_error.h libmng_trace.h libmng_read.h \ + libmng_write.h libmng_display.h libmng_zlib.h libmng_cms.h libmng_zlib.h +libmng_callback_xs.o libmng_callback_xs.pic.o: libmng_callback_xs.c libmng.h \ + libmng_conf.h libmng_types.h libmng_data.h libmng_error.h libmng_trace.h +libmng_prop_xs.o libmng_prop_xs.pic.o: libmng_prop_xs.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_error.h libmng_trace.h libmng_cms.h +libmng_chunk_xs.o libmng_chunk_xs.pic.o: libmng_chunk_xs.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_chunks.h libmng_chunk_prc.h \ + libmng_error.h libmng_trace.h +libmng_read.o libmng_read.pic.o: libmng_read.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_objects.h libmng_object_prc.h \ + libmng_chunks.h libmng_chunk_prc.h libmng_chunk_io.h libmng_memory.h \ + libmng_error.h libmng_trace.h libmng_read.h libmng_display.h +libmng_write.o libmng_write.pic.o: libmng_write.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h libmng_write.h +libmng_display.o libmng_display.pic.o: libmng_display.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_objects.h libmng_object_prc.h libmng_memory.h \ + libmng_error.h libmng_trace.h libmng_zlib.h libmng_cms.h libmng_pixels.h \ + libmng_display.h +libmng_object_prc.o libmng_object_prc.pic.o: libmng_object_prc.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_objects.h libmng_object_prc.h libmng_memory.h \ + libmng_error.h libmng_trace.h libmng_display.h libmng_pixels.h +libmng_chunk_descr.o libmng_chunk_descr.pic.o: libmng_chunk_descr.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_chunks.h libmng_chunk_descr.h libmng_memory.h \ + libmng_chunk_prc.h libmng_error.h libmng_trace.h +libmng_chunk_prc.o libmng_chunk_prc.pic.o: libmng_chunk_prc.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_chunks.h libmng_chunk_prc.h libmng_memory.h \ + libmng_error.h libmng_trace.h +libmng_chunk_io.o libmng_chunk_io.pic.o: libmng_chunk_io.c libmng.h libmng_conf.h \ + libmng_types.h libmng_data.h libmng_objects.h libmng_object_prc.h \ + libmng_chunks.h libmng_chunk_io.h libmng_chunk_prc.h libmng_memory.h libmng_error.h \ + libmng_trace.h libmng_display.h libmng_zlib.h libmng_pixels.h +libmng_error.o libmng_error.pic.o: libmng_error.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h +libmng_trace.o libmng_trace.pic.o: libmng_trace.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h +libmng_pixels.o libmng_pixels.pic.o: libmng_pixels.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_objects.h libmng_memory.h libmng_error.h libmng_trace.h \ + libmng_cms.h libmng_filter.h libmng_pixels.h +libmng_filter.o libmng_filter.pic.o: libmng_filter.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h libmng_filter.h +libmng_dither.o libmng_dither.pic.o: libmng_dither.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_error.h libmng_trace.h libmng_dither.h +libmng_zlib.o libmng_zlib.pic.o: libmng_zlib.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_memory.h libmng_error.h libmng_trace.h libmng_pixels.h \ + libmng_filter.h libmng_zlib.h +libmng_jpeg.o libmng_jpeg.pic.o: libmng_jpeg.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_memory.h libmng_error.h libmng_trace.h libmng_pixels.h libmng_jpeg.h +libmng_cms.o libmng_cms.pic.o: libmng_cms.c libmng.h libmng_conf.h libmng_types.h \ + libmng_data.h libmng_objects.h libmng_error.h libmng_trace.h libmng_cms.h diff --git a/Engine/lib/lmng/makefiles/makefile.unix b/Engine/lib/lmng/makefiles/makefile.unix new file mode 100644 index 000000000..4d3fd97f4 --- /dev/null +++ b/Engine/lib/lmng/makefiles/makefile.unix @@ -0,0 +1,67 @@ +# +# For conditions of distribution and use, see copyright notice in libmng.h +# +# makefile for libmng - THE MNG library +# this makefile is suitable for generic unix + +# Configuration options are now in libmng_conf.h + +# The name of your C compiler: +CC= cc + +# Location of jpeg header files +JPEG_INC= /cs/include/jpeg + +# Location of zlib header files +ZLIB_INC= /cs/include + +# Location of lcms header files +# (switch on MNG_FULL_CMS in libmng_conf.h if you want to use this) +LCMS_INC= /ltmp/lcms-1.06/source + +# compiler options: +CFLAGS= -O -I. -I$(ZLIB_INC) -I$(JPEG_INC) -I$(LCMS_INC) + +# source files +SOURCES= \ + libmng_callback_xs.c \ + libmng_chunk_io.c \ + libmng_chunk_descr.c \ + libmng_chunk_prc.c \ + libmng_chunk_xs.c \ + libmng_cms.c \ + libmng_display.c \ + libmng_dither.c \ + libmng_error.c \ + libmng_filter.c \ + libmng_hlapi.c \ + libmng_jpeg.c \ + libmng_object_prc.c \ + libmng_pixels.c \ + libmng_prop_xs.c \ + libmng_read.c \ + libmng_trace.c \ + libmng_write.c \ + libmng_zlib.c + +# object files +OBJECTS= $(SOURCES:%.c=%.o) + +# type dependancies +.c.o: + $(CC) $(CFLAGS) -c $< + +all: libmng.a + +clean: + /bin/rm -f $(OBJECTS) + /bin/rm -f libmng.a + /bin/rm -f *~ core + +libmng.a: $(OBJECTS) + ar r libmng.a $(OBJECTS) + +depend: + makedepend -- $(CFLAGS) $(IFLAGS) -- *.c + +# DO NOT DELETE diff --git a/Engine/lib/lmng/makefiles/makefile.vcwin32 b/Engine/lib/lmng/makefiles/makefile.vcwin32 new file mode 100644 index 000000000..6f9c2c047 --- /dev/null +++ b/Engine/lib/lmng/makefiles/makefile.vcwin32 @@ -0,0 +1,99 @@ +# makefile for libmng +# Copyright (C) 2000 AM(s98t269@stmail.eng.kagawa-u.ac.jp) +# For conditions of distribution and use, see copyright notice in libmng.h +# Assumes that zlib.lib, zconf.h, and zlib.h have been copied to ..\zlib +# Assumes that libjpeg.lib, *.h have been copied to ..\jpgsrc6b +# Assumes that lcmsdll.lib and lcmsstat.lib have been copied to ..\lcms\lib\msvc +# To use, do "nmake /f makefiles\makefile.vcwin32" + +# -------- Microsoft Visual C++ 4.0 and later, no assembler code -------- + +CFLAGS= -Ox -GA3s -nologo -W3 -I..\zlib -I..\jpgsrc6b -I..\lcms\include + +CC=cl +LD=link +LDFLAGS= +O=.obj + +#uncomment next to put error messages in a file +#ERRFILE= >> mngerrs + +# variables +OBJS1 = libmng_callback_xs$(O) libmng_chunk_io$(O) libmng_chunk_prc$(O) libmng_chunk_descr$(0) +OBJS2 = libmng_chunk_xs$(O) libmng_cms$(O) libmng_display$(O) libmng_dither$(O) +OBJS3 = libmng_error$(O) libmng_filter$(O) libmng_hlapi$(O) libmng_jpeg$(O) +OBJS4 = libmng_object_prc$(O) libmng_pixels$(O) libmng_prop_xs$(O) +OBJS5 = libmng_read$(O) libmng_trace$(O) libmng_write$(O) libmng_zlib$(O) + +all: libmng.lib + +libmng_callback_xs$(O): libmng.h libmng_data.h libmng_error.h libmng_trace.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +libmng_chunk_io$(O): libmng.h libmng_data.h libmng_error.h libmng_trace.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +libmng_chunk_descr$(O): libmng.h libmng_data.h libmng_error.h libmng_trace.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +libmng_chunk_prc$(O): libmng.h libmng_data.h libmng_error.h libmng_trace.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +libmng_chunk_xs$(O): libmng.h libmng_data.h libmng_error.h libmng_trace.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +libmng_cms$(O): libmng.h libmng_data.h libmng_error.h libmng_trace.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +libmng_display$(O): libmng.h libmng_data.h libmng_error.h libmng_trace.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +libmng_dither$(O): libmng.h libmng_data.h libmng_error.h libmng_trace.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +libmng_error$(O): libmng.h libmng_data.h libmng_error.h libmng_trace.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +libmng_filter$(O): libmng.h libmng_data.h libmng_error.h libmng_trace.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +libmng_hlapi$(O): libmng.h libmng_data.h libmng_error.h libmng_trace.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +libmng_jpeg$(O): libmng.h libmng_data.h libmng_error.h libmng_trace.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +libmng_object_prc$(O): libmng.h libmng_data.h libmng_error.h libmng_trace.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +libmng_pixels$(O): libmng.h libmng_data.h libmng_error.h libmng_trace.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +libmng_prop_xs$(O): libmng.h libmng_data.h libmng_error.h libmng_trace.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +libmng_read$(O): libmng.h libmng_data.h libmng_error.h libmng_trace.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +libmng_trace$(O): libmng.h libmng_data.h libmng_error.h libmng_trace.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +libmng_write$(O): libmng.h libmng_data.h libmng_error.h libmng_trace.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +libmng_zlib$(O): libmng.h libmng_data.h libmng_error.h libmng_trace.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +libmng.lib: $(OBJS1) $(OBJS2) $(OBJS3) $(OBJS4) $(OBJS5) + echo something to del > libmng.lib + del libmng.lib + lib /OUT:libmng.lib $(OBJS1) $(OBJS2) $(OBJS3) $(OBJS4) $(OBJS5) + +mngtest.exe: mngtest.obj libmng.lib + $(LD) $(LDFLAGS) mngtest.obj libmng.lib ..\zlib\zlib.lib /OUT:mngtest.exe /SUBSYSTEM:CONSOLE + +test: mngtest.exe + mngtest + +# End of makefile for libmng + diff --git a/Engine/lib/lmng/special/mozcfg/mozlibmngconf.h b/Engine/lib/lmng/special/mozcfg/mozlibmngconf.h new file mode 100644 index 000000000..431b2eaf2 --- /dev/null +++ b/Engine/lib/lmng/special/mozcfg/mozlibmngconf.h @@ -0,0 +1,218 @@ +/* ************************************************************************** */ +/* * * */ +/* * project : libmng * */ +/* * file : mozlibmngconf.h copyright (c) G.R-P 2003-2005 * */ +/* * version : 1.0.10 * */ +/* * * */ +/* * purpose : special config file for Mozilla * */ +/* * * */ +/* * author : Glenn Randers-Pehrson * */ +/* * * */ +/* * comment : This is the configuration file designed to minimize * */ +/* * footprint for the integration with Mozilla. * */ +/* * * */ +/* * changes : * */ +/* * * */ +/* ************************************************************************** */ + +#ifndef _mozlibmng_conf_h_ +#define _mozlibmng_conf_h_ + +/* Mozilla defines */ + +/* One or none of these may be defined via MNG_CFLAGS in "configure" */ + +#if defined(MNG_BUILD_RAW_MNG) || \ + defined(MNG_BUILD_FULL_MNG) || \ + defined(MNG_BUILD_MOZ_MNG) || \ + defined(MNG_BUILD_MOZ_NO_JNG) || \ + defined(MNG_BUILD_WEB_MNG) || \ + defined(MNG_BUILD_WEB_NO_JNG) || \ + defined(MNG_BUILD_LC) || \ + defined(MNG_BUILD_LC_NO_JNG) || \ + defined(MNG_BUILD_VLC) +# define MNG_BUILD_DEFINED +#endif + +#ifndef MNG_BUILD_DEFINED +#define MNG_BUILD_FULL_MNG +#define MNG_BUILD_DEFINED +#endif + +#if defined(MNG_BUILD_FULL_MNG) +#define MNG_DISABLE_UNUSED +#endif + +#if defined(MNG_BUILD_MOZ_MNG) +#define MNG_DISABLE_UNUSED +#define MNG_ENABLE_FOOTPRINT +#endif + +#if defined(MNG_BUILD_MOZ_NO_JNG) +#define MNG_DISABLE_UNUSED +#define MNG_ENABLE_FOOTPRINT +#define MNG_DISABLE_JNG +#endif + +#if defined(MNG_BUILD_WEB_MNG) +#define MNG_DISABLE_UNUSED +#define MNG_DISABLE_DELTA_PNG +#define MNG_ENABLE_FOOTPRINT +#define MNG_SKIPCHUNK_MAGN +#endif + +#if defined(MNG_BUILD_WEB_NO_JNG) +#define MNG_DISABLE_UNUSED +#define MNG_DISABLE_DELTA_PNG +#define MNG_ENABLE_FOOTPRINT +#define MNG_SKIPCHUNK_MAGN +#define MNG_DISABLE_JNG +#endif + +#if defined(MNG_BUILD_LC) +#define MNG_DISABLE_DELTA_PNG +#define MNG_DISABLE_UNUSED +#define MNG_ENABLE_FOOTPRINT +#define MNG_DISABLE_16_BIT +#define MNG_DISABLE_NON_LC +#endif + +#if defined(MNG_BUILD_LC_NO_JNG) +#define MNG_DISABLE_DELTA_PNG +#define MNG_DISABLE_UNUSED +#define MNG_ENABLE_FOOTPRINT +#define MNG_DISABLE_16_BIT +#define MNG_DISABLE_JNG +#define MNG_DISABLE_NON_LC +#endif + +#if defined(MNG_BUILD_VLC) +#define MNG_DISABLE_DELTA_PNG +#define MNG_DISABLE_UNUSED +#define MNG_ENABLE_FOOTPRINT +#define MNG_DISABLE_16_BIT +#define MNG_DISABLE_JNG +#define MNG_DISABLE_NON_LC +#define MNG_DISABLE_NON_VLC +#endif + +#if defined(MNG_ENABLE_FOOTPRINT) +/* Perform footprint optimizations */ +#define MNG_OPTIMIZE_FOOTPRINT_COMPOSE +#define MNG_OPTIMIZE_FOOTPRINT_DIV +#define MNG_OPTIMIZE_FOOTPRINT_SWITCH +#define MNG_DECREMENT_LOOPS +#define MNG_USE_ZLIB_CRC +#define MNG_OPTIMIZE_FOOTPRINT_INIT +#define MNG_OPTIMIZE_FOOTPRINT_MAGN +#define MNG_OPTIMIZE_OBJCLEANUP +#define MNG_OPTIMIZE_CHUNKINITFREE +#define MNG_OPTIMIZE_CHUNKASSIGN +#endif + +#if defined(MNG_DISABLE_UNUSED) +/* Eliminate unused features from libmng */ +#define MNG_NO_VERSION_QUERY_SUPPORT +#define MNG_NO_OLD_VERSIONS + +#ifdef MOZ_CAIRO_GFX +#define MNG_SKIPCANVAS_RGB8 +#define MNG_SKIPCANVAS_RGB8_A8 +#else +#define MNG_SKIPCANVAS_BGRA8_PM +#endif + +#define MNG_SKIPCANVAS_ABGR8 +#define MNG_SKIPCANVAS_ARGB8 +#define MNG_SKIPCANVAS_BGR8 +#define MNG_SKIPCANVAS_BGRX8 +#define MNG_SKIPCANVAS_BGRA8 +#define MNG_SKIPCANVAS_RGBA8_PM +#define MNG_SKIPCANVAS_ARGB8_PM +#define MNG_SKIPCANVAS_ABGR8_PM +#define MNG_SKIPCANVAS_RGBA8 +#define MNG_SKIPCANVAS_RGB555 +#define MNG_SKIPCANVAS_BGR555 +#define MNG_SKIPCANVAS_RGB565 +#define MNG_SKIPCANVAS_BGR565 +#define MNG_SKIPCANVAS_RGBA565 +#define MNG_SKIPCANVAS_BGRA565 +#define MNG_SKIPCANVAS_BGR565_A8 +#define MNG_SKIP_MAXCANVAS +#define MNG_SKIPCHUNK_tEXt +#define MNG_SKIPCHUNK_zTXt +#define MNG_SKIPCHUNK_iTXt +#define MNG_SKIPCHUNK_bKGD +#define MNG_SKIPCHUNK_cHRM +#define MNG_SKIPCHUNK_hIST +#define MNG_SKIPCHUNK_iCCP +#define MNG_SKIPCHUNK_pHYs +#define MNG_SKIPCHUNK_sBIT +#define MNG_SKIPCHUNK_sPLT +#define MNG_SKIPCHUNK_tIME +#define MNG_SKIPCHUNK_evNT +#define MNG_SKIPCHUNK_eXPI +#define MNG_SKIPCHUNK_fPRI +#define MNG_SKIPCHUNK_nEED +#define MNG_SKIPCHUNK_pHYg +/* Eliminate "critical" but safe-to-ignore chunks (see mng_read_unknown()) */ +#define MNG_SKIPCHUNK_SAVE +#define MNG_SKIPCHUNK_SEEK +#define MNG_SKIPCHUNK_DBYK +#define MNG_SKIPCHUNK_ORDR +/* Eliminate unused zlib and jpeg "get" and "set" accessors */ +#define MNG_NO_ACCESS_ZLIB +#define MNG_NO_ACCESS_JPEG +/* Eliminate other unused features */ +#define MNG_NO_SUPPORT_FUNCQUERY +#define MNG_NO_DISPLAY_GO_SUPPORTED +#define MNG_NO_CURRENT_INFO +#define MNG_NO_DFLT_INFO +#define MNG_NO_LOOP_SIGNALS_SUPPORTED +#define MNG_NO_OPEN_CLOSE_STREAM +#endif + +#if defined(MNG_DISABLE_16_BIT) +/* Eliminate 16-bit support from libmng */ +#define MNG_NO_16BIT_SUPPORT +#endif + +#if defined(MNG_DISABLE_DELTA_PNG) +/* Eliminate Delta-PNG feature from libmng */ +#define MNG_NO_DELTA_PNG +#endif + +#if defined(MNG_DISABLE_NON_LC) +/* Eliminate non-MNG-LC chunks */ +#define MNG_SKIPCHUNK_BASI +#define MNG_SKIPCHUNK_CLIP +#define MNG_SKIPCHUNK_CLON +#define MNG_SKIPCHUNK_DISC +#define MNG_SKIPCHUNK_MOVE +#define MNG_SKIPCHUNK_SHOW +#define MNG_SKIPCHUNK_PAST +#endif + +#if defined(MNG_DISABLE_JNG) +/* If you change this you should also manually remove or restore + jng-recognition in mozilla/modules/libpr0n/src/imgLoader.cpp */ +#define MNG_NO_INCLUDE_JNG +#endif + +#if defined(MNG_DISABLE_NON_VLC) +/* Eliminate non-MNG-VLC chunks */ +#define MNG_SKIPCHUNK_DEFI +#define MNG_SKIPCHUNK_FRAM +#define MNG_SKIPCHUNK_LOOP +#define MNG_SKIPCHUNK_MAGN +#endif + +#if defined(MNG_DISABLE_OPTIONAL_VLC) +/* Eliminate optional MNG-VLC chunks */ +#define MNG_SKIPCHUNK_TERM +#define MNG_SKIPCHUNK_BACK +#define MNG_SKIPCHUNK_gAMA +#define MNG_SKIPCHUNK_sRGB +#endif + +#endif /* _mozlibmng_conf_h */ diff --git a/Engine/lib/lmng/unmaintained/autogen.sh b/Engine/lib/lmng/unmaintained/autogen.sh new file mode 100644 index 000000000..308ae3739 --- /dev/null +++ b/Engine/lib/lmng/unmaintained/autogen.sh @@ -0,0 +1,50 @@ +# autogen.sh +# +# invoke the auto* tools to create the configureation system + +# move out configure.in +if ! test -f configure.in; then + echo "copying configure.in" + ln -s makefiles/configure.in . +fi + +# move out the macros and run aclocal +if test ! -f acinclude.m4 -a -r makefiles/acinclude.m4; then + echo "copying configure macros" + ln -s makefiles/acinclude.m4 . +fi + +# copy up our Makefile template +if ! test -f Makefile.am; then + echo "copying automake template" + ln -s makefiles/Makefile.am . +fi + +echo "running aclocal" +aclocal + +# libtool is named glibtool on MacOS X +for LIBTOOLIZE in libtoolize glibtoolize nope; do + ($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 && break +done +if test x$LIBTOOLIZE = xnope; then + echo "error: Could not find libtoolize in the path!" + echo " You'll need to install a copy of libtool before continuing" + echo " with the generation of the build system." + echo + exit 1 +fi + +echo "running $LIBTOOLIZE" +$LIBTOOLIZE --automake + +echo "running automake" +automake --foreign --add-missing + +echo "building configure script" +autoconf + +# and finally invoke our new configure +./configure $* + +# end diff --git a/Engine/lib/lpng/ANNOUNCE b/Engine/lib/lpng/ANNOUNCE new file mode 100644 index 000000000..c49c7b6bd --- /dev/null +++ b/Engine/lib/lpng/ANNOUNCE @@ -0,0 +1,62 @@ + +Libpng 1.2.24 - December 14, 2007 + +This is a public release of libpng, intended for use in production codes. + +Files available for download: + +Source files with LF line endings (for Unix/Linux) and with a +"configure" script + + libpng-1.2.24.tar.gz + libpng-1.2.24.tar.bz2 + +Source files with LF line endings (for Unix/Linux) without the +"configure" script + + libpng-1.2.24-no-config.tar.gz + libpng-1.2.24-no-config.tar.bz2 + +Source files with CRLF line endings (for Windows), without the +"configure" script + + lpng1224.zip + lpng1224.tar.bz2 + +Project files + + libpng-1.2.24-project-netware.zip + libpng-1.2.24-project-wince.zip + +Other information: + + libpng-1.2.24-README.txt + libpng-1.2.24-KNOWNBUGS.txt + libpng-1.2.24-LICENSE.txt + libpng-1.2.24-Y2K-compliance.txt + +Changes since the last public release (1.2.23): + +version 1.2.24 [December 14, 2007] + + Moved misplaced test for malloc failure in png_set_sPLT(). This bug was + introduced in libpng-1.2.20. + Ifdef out avg_row etc from png.h and pngwrite.c when PNG_NO_WRITE_FILTER + Do not use png_ptr->free_fn and png_ptr->mem_fn in png_destroy_read_struct() + when png_ptr is NULL (Marshall Clow). + Updated handling of symbol prefixes in Makefile.am and configure.ac (Mike + Frysinger). + Removed a useless test and fixed incorrect test in png_set_cHRM_fixed() + (David Hill). + Make sure not to redefine _BSD_SOURCE in pngconf.h + Revised gather.sh and makefile.std in contrib/pngminim to avoid compiling + unused files. + + + +Send comments/corrections/commendations to png-mng-implement at lists.sf.net +(subscription required; visit +https://lists.sourceforge.net/lists/listinfo/png-mng-implement +to subscribe) or to glennrp at users.sourceforge.net + +Glenn R-P diff --git a/Engine/lib/lpng/CHANGES b/Engine/lib/lpng/CHANGES new file mode 100644 index 000000000..a6e5856aa --- /dev/null +++ b/Engine/lib/lpng/CHANGES @@ -0,0 +1,2041 @@ + +CHANGES - changes for libpng + +version 0.2 + added reader into png.h + fixed small problems in stub file + +version 0.3 + added pull reader + split up pngwrite.c to several files + added pnglib.txt + added example.c + cleaned up writer, adding a few new transformations + fixed some bugs in writer + interfaced with zlib 0.5 + added K&R support + added check for 64 KB blocks for 16 bit machines + +version 0.4 + cleaned up code and commented code + simplified time handling into png_time + created png_color_16 and png_color_8 to handle color needs + cleaned up color type defines + fixed various bugs + made various names more consistent + interfaced with zlib 0.71 + cleaned up zTXt reader and writer (using zlib's Reset functions) + split transformations into pngrtran.c and pngwtran.c + +version 0.5 + interfaced with zlib 0.8 + fixed many reading and writing bugs + saved using 3 spaces instead of tabs + +version 0.6 + added png_large_malloc() and png_large_free() + added png_size_t + cleaned up some compiler warnings + added png_start_read_image() + +version 0.7 + cleaned up lots of bugs + finished dithering and other stuff + added test program + changed name from pnglib to libpng + +version 0.71 [June, 1995] + changed pngtest.png for zlib 0.93 + fixed error in libpng.txt and example.c + +version 0.8 + cleaned up some bugs + added png_set_filler() + split up pngstub.c into pngmem.c, pngio.c, and pngerror.c + added #define's to remove unwanted code + moved png_info_init() to png.c + added old_size into png_realloc() + added functions to manually set filtering and compression info + changed compression parameters based on image type + optimized filter selection code + added version info + changed external functions passing floats to doubles (k&r problems?) + put all the configurable stuff in pngconf.h + enabled png_set_shift to work with paletted images on read + added png_read_update_info() - updates info structure with + transformations + +version 0.81 [August, 1995] + incorporated Tim Wegner's medium model code (thanks, Tim) + +version 0.82 [September, 1995] + [unspecified changes] + +version 0.85 [December, 1995] + added more medium model code (almost everything's a far) + added i/o, error, and memory callback functions + fixed some bugs (16 bit, 4 bit interlaced, etc.) + added first run progressive reader (barely tested) + +version 0.86 [January, 1996] + fixed bugs + improved documentation + +version 0.87 [January, 1996] + fixed medium model bugs + fixed other bugs introduced in 0.85 and 0.86 + added some minor documentation + +version 0.88 [January, 1996] + fixed progressive bugs + replaced tabs with spaces + cleaned up documentation + added callbacks for read/write and warning/error functions + +version 0.89 [July, 1996] + added new initialization API to make libpng work better with shared libs + we now have png_create_read_struct(), png_create_write_struct(), + png_create_info_struct(), png_destroy_read_struct(), and + png_destroy_write_struct() instead of the separate calls to + malloc and png_read_init(), png_info_init(), and png_write_init() + changed warning/error callback functions to fix bug - this means you + should use the new initialization API if you were using the old + png_set_message_fn() calls, and that the old API no longer exists + so that people are aware that they need to change their code + changed filter selection API to allow selection of multiple filters + since it didn't work in previous versions of libpng anyways + optimized filter selection code + fixed png_set_background() to allow using an arbitrary RGB color for + paletted images + fixed gamma and background correction for paletted images, so + png_correct_palette is not needed unless you are correcting an + external palette (you will need to #define PNG_CORRECT_PALETTE_SUPPORTED + in pngconf.h) - if nobody uses this, it may disappear in the future. + fixed bug with Borland 64K memory allocation (Alexander Lehmann) + fixed bug in interlace handling (Smarasderagd, I think) + added more error checking for writing and image to reduce invalid files + separated read and write functions so that they won't both be linked + into a binary when only reading or writing functionality is used + new pngtest image also has interlacing and zTXt + updated documentation to reflect new API + +version 0.90 [January, 1997] + made CRC errors/warnings on critical and ancillary chunks configurable + libpng will use the zlib CRC routines by (compile-time) default + changed DOS small/medium model memory support - needs zlib 1.04 (Tim Wegner) + added external C++ wrapper statements to png.h (Gilles Dauphin) + allow PNG file to be read when some or all of file signature has already + been read from the beginning of the stream. ****This affects the size + of info_struct and invalidates all programs that use a shared libpng**** + fixed png_filler() declarations + fixed? background color conversions + fixed order of error function pointers to match documentation + current chunk name is now available in png_struct to reduce the number + of nearly identical error messages (will simplify multi-lingual + support when available) + try to get ready for unknown-chunk callback functions: + - previously read critical chunks are flagged, so the chunk handling + routines can determine if the chunk is in the right place + - all chunk handling routines have the same prototypes, so we will + be able to handle all chunks via a callback mechanism + try to fix Linux "setjmp" buffer size problems + removed png_large_malloc, png_large_free, and png_realloc functions. + +version 0.95 [March, 1997] + fixed bug in pngwutil.c allocating "up_row" twice and "avg_row" never + fixed bug in PNG file signature compares when start != 0 + changed parameter type of png_set_filler(...filler...) from png_byte + to png_uint_32 + added test for MACOS to ensure that both math.h and fp.h are not #included + added macros for libpng to be compiled as a Windows DLL (Andreas Kupries) + added "packswap" transformation, which changes the endianness of + packed-pixel bytes (Kevin Bracey) + added "strip_alpha" transformation, which removes the alpha channel of + input images without using it (not necessarily a good idea) + added "swap_alpha" transformation, which puts the alpha channel in front + of the color bytes instead of after + removed all implicit variable tests which assume NULL == 0 (I think) + changed several variables to "png_size_t" to show 16/32-bit limitations + added new pCAL chunk read/write support + added experimental filter selection weighting (Greg Roelofs) + removed old png_set_rgbx() and png_set_xrgb() functions that have been + obsolete for about 2 years now (use png_set_filler() instead) + added macros to read 16- and 32-bit ints directly from buffer, to be + used only on those systems that support it (namely PowerPC and 680x0) + With some testing, this may become the default for MACOS/PPC systems. + only calculate CRC on data if we are going to use it + added macros for zTXt compression type PNG_zTXt_COMPRESSION_??? + added macros for simple libpng debugging output selectable at compile time + removed PNG_READ_END_MODE in progressive reader (Smarasderagd) + more description of info_struct in libpng.txt and png.h + more instructions in example.c + more chunk types tested in pngtest.c + renamed pngrcb.c to pngset.c, and all png_read_ functions to be + png_set_. We now have corresponding png_get_ + functions in pngget.c to get information in info_ptr. This isolates + the application from the internal organization of png_info_struct + (good for shared library implementations). + +version 0.96 [May, 1997] + fixed serious bug with < 8bpp images introduced in 0.95 + fixed 256-color transparency bug (Greg Roelofs) + fixed up documentation (Greg Roelofs, Laszlo Nyul) + fixed "error" in pngconf.h for Linux setjmp() behaviour + fixed DOS medium model support (Tim Wegner) + fixed png_check_keyword() for case with error in static string text + added read of CRC after IEND chunk for embedded PNGs (Laszlo Nyul) + added typecasts to quiet compiler errors + added more debugging info + +version 0.97 [January, 1998] + removed PNG_USE_OWN_CRC capability + relocated png_set_crc_action from pngrutil.c to pngrtran.c + fixed typecasts of "new_key", etc. (Andreas Dilger) + added RFC 1152 [sic] date support + fixed bug in gamma handling of 4-bit grayscale + added 2-bit grayscale gamma handling (Glenn R-P) + added more typecasts. 65536L becomes (png_uint_32)65536L, etc. (Glenn R-P) + minor corrections in libpng.txt + added simple sRGB support (Glenn R-P) + easier conditional compiling, e.g. define PNG_READ/WRITE_NOT_FULLY_SUPPORTED; + all configurable options can be selected from command-line instead + of having to edit pngconf.h (Glenn R-P) + fixed memory leak in pngwrite.c (free info_ptr->text) (Glenn R-P) + added more conditions for png_do_background, to avoid changing + black pixels to background when a background is supplied and + no pixels are transparent + repaired PNG_NO_STDIO behaviour + tested NODIV support and made it default behaviour (Greg Roelofs) + added "-m" option and PNGTEST_DEBUG_MEMORY to pngtest (John Bowler) + regularized version numbering scheme and bumped shared-library major + version number to 2 to avoid problems with libpng 0.89 apps (Greg Roelofs) + +version 0.98 [January, 1998] + cleaned up some typos in libpng.txt and in code documentation + fixed memory leaks in pCAL chunk processing (Glenn R-P and John Bowler) + cosmetic change "display_gamma" to "screen_gamma" in pngrtran.c + changed recommendation about file_gamma for PC images to .51 from .45, + in example.c and libpng.txt, added comments to distinguish between + screen_gamma, viewing_gamma, and display_gamma. + changed all references to RFC1152 to read RFC1123 and changed the + PNG_TIME_RFC1152_SUPPORTED macro to PNG_TIME_RFC1123_SUPPORTED + added png_invert_alpha capability (Glenn R-P -- suggestion by Jon Vincent) + changed srgb_intent from png_byte to int to avoid compiler bugs + +version 0.99 [January 30, 1998] + free info_ptr->text instead of end_info_ptr->text in pngread.c (John Bowler) + fixed a longstanding "packswap" bug in pngtrans.c + fixed some inconsistencies in pngconf.h that prevented compiling with + PNG_READ_GAMMA_SUPPORTED and PNG_READ_hIST_SUPPORTED undefined + fixed some typos and made other minor rearrangement of libpng.txt (Andreas) + changed recommendation about file_gamma for PC images to .50 from .51 in + example.c and libpng.txt, and changed file_gamma for sRGB images to .45 + added a number of functions to access information from the png structure + png_get_image_height(), etc. (Glenn R-P, suggestion by Brad Pettit) + added TARGET_MACOS similar to zlib-1.0.8 + define PNG_ALWAYS_EXTERN when __MWERKS__ && WIN32 are defined + added type casting to all png_malloc() function calls +version 0.99a [January 31, 1998] + Added type casts and parentheses to all returns that return a value.(Tim W.) +version 0.99b [February 4, 1998] + Added type cast png_uint_32 on malloc function calls where needed. + Changed type of num_hist from png_uint_32 to int (same as num_palette). + Added checks for rowbytes overflow, in case png_size_t is less than 32 bits. + Renamed makefile.elf to makefile.lnx. +version 0.99c [February 7, 1998] + More type casting. Removed erroneous overflow test in pngmem.c. + Added png_buffered_memcpy() and png_buffered_memset(), apply them to rowbytes. + Added UNIX manual pages libpng.3 (incorporating libpng.txt) and png.5. +version 0.99d [February 11, 1998] + Renamed "far_to_near()" "png_far_to_near()" + Revised libpng.3 + Version 99c "buffered" operations didn't work as intended. Replaced them + with png_memcpy_check() and png_memset_check(). + Added many "if (png_ptr == NULL) return" to quell compiler warnings about + unused png_ptr, mostly in pngget.c and pngset.c. + Check for overlength tRNS chunk present when indexed-color PLTE is read. + Cleaned up spelling errors in libpng.3/libpng.txt + Corrected a problem with png_get_tRNS() which returned undefined trans array +version 0.99e [February 28, 1998] + Corrected png_get_tRNS() again. + Add parentheses for easier reading of pngget.c, fixed "||" should be "&&". + Touched up example.c to make more of it compileable, although the entire + file still can't be compiled (Willem van Schaik) + Fixed a bug in png_do_shift() (Bryan Tsai) + Added a space in png.h prototype for png_write_chunk_start() + Replaced pngtest.png with one created with zlib 1.1.1 + Changed pngtest to report PASS even when file size is different (Jean-loup G.) + Corrected some logic errors in png_do_invert_alpha() (Chris Patterson) +version 0.99f [March 5, 1998] + Corrected a bug in pngpread() introduced in version 99c (Kevin Bracey) + Moved makefiles into a "scripts" directory, and added INSTALL instruction file + Added makefile.os2 and pngos2.def (A. Zabolotny) and makefile.s2x (W. Sebok) + Added pointers to "note on libpng versions" in makefile.lnx and README + Added row callback feature when reading and writing nonprogressive rows + and added a test of this feature in pngtest.c + Added user transform callbacks, with test of the feature in pngtest.c +version 0.99g [March 6, 1998, morning] + Minor changes to pngtest.c to suppress compiler warnings. + Removed "beta" language from documentation. +version 0.99h [March 6, 1998, evening] + Minor changes to previous minor changes to pngtest.c + Changed PNG_READ_NOT_FULLY_SUPPORTED to PNG_READ_TRANSFORMS_NOT_SUPPORTED + and added PNG_PROGRESSIVE_READ_NOT_SUPPORTED macro + Added user transform capability + +version 1.00 [March 7, 1998] + Changed several typedefs in pngrutil.c + Added makefile.wat (Pawel Mrochen), updated makefile.tc3 (Willem van Schaik) + replaced "while(1)" with "for(;;)" + added PNGARG() to prototypes in pngtest.c and removed some prototypes + updated some of the makefiles (Tom Lane) + changed some typedefs (s_start, etc.) in pngrutil.c + fixed dimensions of "short_months" array in pngwrite.c + Replaced ansi2knr.c with the one from jpeg-v6 + +version 1.0.0 [March 8, 1998] + Changed name from 1.00 to 1.0.0 (Adam Costello) + Added smakefile.ppc (with SCOPTIONS.ppc) for Amiga PPC (Andreas Kleinert) +version 1.0.0a [March 9, 1998] + Fixed three bugs in pngrtran.c to make gamma+background handling consistent + (Greg Roelofs) + Changed format of the PNG_LIBPNG_VER integer to xyyzz instead of xyz + for major, minor, and bugfix releases. This is 10001. (Adam Costello, + Tom Lane) + Make months range from 1-12 in png_convert_to_rfc1123 +version 1.0.0b [March 13, 1998] + Quieted compiler complaints about two empty "for" loops in pngrutil.c + Minor changes to makefile.s2x + Removed #ifdef/#endif around a png_free() in pngread.c + +version 1.0.1 [March 14, 1998] + Changed makefile.s2x to reduce security risk of using a relative pathname + Fixed some typos in the documentation (Greg). + Fixed a problem with value of "channels" returned by png_read_update_info() +version 1.0.1a [April 21, 1998] + Optimized Paeth calculations by replacing abs() function calls with intrinsics + plus other loop optimizations. Improves avg decoding speed by about 20%. + Commented out i386istic "align" compiler flags in makefile.lnx. + Reduced the default warning level in some makefiles, to make them consistent. + Removed references to IJG and JPEG in the ansi2knr.c copyright statement. + Fixed a bug in png_do_strip_filler with XXRRGGBB => RRGGBB transformation. + Added grayscale and 16-bit capability to png_do_read_filler(). + Fixed a bug in pngset.c, introduced in version 0.99c, that sets rowbytes + too large when writing an image with bit_depth < 8 (Bob Dellaca). + Corrected some bugs in the experimental weighted filtering heuristics. + Moved a misplaced pngrutil code block that truncates tRNS if it has more + than num_palette entries -- test was done before num_palette was defined. + Fixed a png_convert_to_rfc1123() bug that converts day 31 to 0 (Steve Eddins). + Changed compiler flags in makefile.wat for better optimization (Pawel Mrochen). +version 1.0.1b [May 2, 1998] + Relocated png_do_gray_to_rgb() within png_do_read_transformations() (Greg). + Relocated the png_composite macros from pngrtran.c to png.h (Greg). + Added makefile.sco (contributed by Mike Hopkirk). + Fixed two bugs (missing definitions of "istop") introduced in libpng-1.0.1a. + Fixed a bug in pngrtran.c that would set channels=5 under some circumstances. + More work on the Paeth-filtering, achieving imperceptible speedup (A Kleinert). + More work on loop optimization which may help when compiled with C++ compilers. + Added warnings when people try to use transforms they've defined out. + Collapsed 4 "i" and "c" loops into single "i" loops in pngrtran and pngwtran. + Revised paragraph about png_set_expand() in libpng.txt and libpng.3 (Greg) +version 1.0.1c [May 11, 1998] + Fixed a bug in pngrtran.c (introduced in libpng-1.0.1a) where the masks for + filler bytes should have been 0xff instead of 0xf. + Added max_pixel_depth=32 in pngrutil.c when using FILLER with palette images. + Moved PNG_WRITE_WEIGHTED_FILTER_SUPPORTED and PNG_WRITE_FLUSH_SUPPORTED + out of the PNG_WRITE_TRANSFORMS_NOT_SUPPORTED block of pngconf.h + Added "PNG_NO_WRITE_TRANSFORMS" etc., as alternatives for *_NOT_SUPPORTED, + for consistency, in pngconf.h + Added individual "ifndef PNG_NO_[CAPABILITY]" in pngconf.h to make it easier + to remove unwanted capabilities via the compile line + Made some corrections to grammar (which, it's) in documentation (Greg). + Corrected example.c, use of row_pointers in png_write_image(). +version 1.0.1d [May 24, 1998] + Corrected several statements that used side effects illegally in pngrutil.c + and pngtrans.c, that were introduced in version 1.0.1b + Revised png_read_rows() to avoid repeated if-testing for NULL (A Kleinert) + More corrections to example.c, use of row_pointers in png_write_image() + and png_read_rows(). + Added pngdll.mak and pngdef.pas to scripts directory, contributed by + Bob Dellaca, to make a png32bd.dll with Borland C++ 4.5 + Fixed error in example.c with png_set_text: num_text is 3, not 2 (Guido V.) + Changed several loops from count-down to count-up, for consistency. +version 1.0.1e [June 6, 1998] + Revised libpng.txt and libpng.3 description of png_set_read|write_fn(), and + added warnings when people try to set png_read_fn and png_write_fn in + the same structure. + Added a test such that png_do_gamma will be done when num_trans==0 + for truecolor images that have defined a background. This corrects an + error that was introduced in libpng-0.90 that can cause gamma processing + to be skipped. + Added tests in png.h to include "trans" and "trans_values" in structures + when PNG_READ_BACKGROUND_SUPPORTED or PNG_READ_EXPAND_SUPPORTED is defined. + Add png_free(png_ptr->time_buffer) in png_destroy_read_struct() + Moved png_convert_to_rfc_1123() from pngwrite.c to png.c + Added capability for user-provided malloc_fn() and free_fn() functions, + and revised pngtest.c to demonstrate their use, replacing the + PNGTEST_DEBUG_MEM feature. + Added makefile.w32, for Microsoft C++ 4.0 and later (Tim Wegner). + +version 1.0.2 [June 14, 1998] + Fixed two bugs in makefile.bor . +version 1.0.2a [December 30, 1998] + Replaced and extended code that was removed from png_set_filler() in 1.0.1a. + Fixed a bug in png_do_filler() that made it fail to write filler bytes in + the left-most pixel of each row (Kevin Bracey). + Changed "static pngcharp tIME_string" to "static char tIME_string[30]" + in pngtest.c (Duncan Simpson). + Fixed a bug in pngtest.c that caused pngtest to try to write a tIME chunk + even when no tIME chunk was present in the source file. + Fixed a problem in pngrutil.c: gray_to_rgb didn't always work with 16-bit. + Fixed a problem in png_read_push_finish_row(), which would not skip some + passes that it should skip, for images that are less than 3 pixels high. + Interchanged the order of calls to png_do_swap() and png_do_shift() + in pngwtran.c (John Cromer). + Added #ifdef PNG_DEBUG/#endif surrounding use of PNG_DEBUG in png.h . + Changed "bad adaptive filter type" from error to warning in pngrutil.c . + Fixed a documentation error about default filtering with 8-bit indexed-color. + Separated the PNG_NO_STDIO macro into PNG_NO_STDIO and PNG_NO_CONSOLE_IO + (L. Peter Deutsch). + Added png_set_rgb_to_gray() and png_get_rgb_to_gray_status() functions. + Added png_get_copyright() and png_get_header_version() functions. + Revised comments on png_set_progressive_read_fn() in libpng.txt and example.c + Added information about debugging in libpng.txt and libpng.3 . + Changed "ln -sf" to "ln -s -f" in makefile.s2x, makefile.lnx, and makefile.sco. + Removed lines after Dynamic Dependencies" in makefile.aco . + Revised makefile.dec to make a shared library (Jeremie Petit). + Removed trailing blanks from all files. +version 1.0.2a [January 6, 1999] + Removed misplaced #endif and #ifdef PNG_NO_EXTERN near the end of png.h + Added "if" tests to silence complaints about unused png_ptr in png.h and png.c + Changed "check_if_png" function in example.c to return true (nonzero) if PNG. + Changed libpng.txt to demonstrate png_sig_cmp() instead of png_check_sig() + which is obsolete. + +version 1.0.3 [January 14, 1999] + Added makefile.hux, for Hewlett Packard HPUX 10.20 and 11.00 (Jim Rice) + Added a statement of Y2K compliance in png.h, libpng.3, and Y2KINFO. +version 1.0.3a [August 12, 1999] + Added check for PNG_READ_INTERLACE_SUPPORTED in pngread.c; issue a warning + if an attempt is made to read an interlaced image when it's not supported. + Added check if png_ptr->trans is defined before freeing it in pngread.c + Modified the Y2K statement to include versions back to version 0.71 + Fixed a bug in the check for valid IHDR bit_depth/color_types in pngrutil.c + Modified makefile.wat (added -zp8 flag, ".symbolic", changed some comments) + Replaced leading blanks with tab characters in makefile.hux + Changed "dworkin.wustl.edu" to "ccrc.wustl.edu" in various documents. + Changed (float)red and (float)green to (double)red, (double)green + in png_set_rgb_to_gray() to avoid "promotion" problems in AIX. + Fixed a bug in pngconf.h that omitted when PNG_DEBUG==0 (K Bracey). + Reformatted libpng.3 and libpngpf.3 with proper fonts (script by J. vanZandt). + Updated documentation to refer to the PNG-1.2 specification. + Removed ansi2knr.c and left pointers to the latest source for ansi2knr.c + in makefile.knr, INSTALL, and README (L. Peter Deutsch) + Fixed bugs in calculation of the length of rowbytes when adding alpha + channels to 16-bit images, in pngrtran.c (Chris Nokleberg) + Added function png_set_user_transform_info() to store user_transform_ptr, + user_depth, and user_channels into the png_struct, and a function + png_get_user_transform_ptr() to retrieve the pointer (Chris Nokleberg) + Added function png_set_empty_plte_permitted() to make libpng useable + in MNG applications. + Corrected the typedef for png_free_ptr in png.h (Jesse Jones). + Correct gamma with srgb is 45455 instead of 45000 in pngrutil.c, to be + consistent with PNG-1.2, and allow variance of 500 before complaining. + Added assembler code contributed by Intel in file pngvcrd.c and modified + makefile.w32 to use it (Nirav Chhatrapati, INTEL Corporation, Gilles Vollant) + Changed "ln -s -f" to "ln -f -s" in the makefiles to make Solaris happy. + Added some aliases for png_set_expand() in pngrtran.c, namely + png_set_expand_PLTE(), png_set_expand_depth(), and png_set_expand_tRNS() + (Greg Roelofs, in "PNG: The Definitive Guide"). + Added makefile.beo for BEOS on X86, contributed by Sander Stok. +version 1.0.3b [August 26, 1999] + Replaced 2147483647L several places with PNG_MAX_UINT macro, defined in png.h + Changed leading blanks to tabs in all makefiles. + Define PNG_USE_PNGVCRD in makefile.w32, to get MMX assembler code. + Made alternate versions of png_set_expand() in pngrtran.c, namely + png_set_gray_1_2_4_to_8, png_set_palette_to_rgb, and png_set_tRNS_to_alpha + (Greg Roelofs, in "PNG: The Definitive Guide"). Deleted the 1.0.3a aliases. + Relocated start of 'extern "C"' block in png.h so it doesn't include pngconf.h + Revised calculation of num_blocks in pngmem.c to avoid a potentially + negative shift distance, whose results are undefined in the C language. + Added a check in pngset.c to prevent writing multiple tIME chunks. + Added a check in pngwrite.c to detect invalid small window_bits sizes. +version 1.0.3d [September 4, 1999] + Fixed type casting of igamma in pngrutil.c + Added new png_expand functions to scripts/pngdef.pas and pngos2.def + Added a demo read_user_transform_fn that examines the row filters in pngtest.c + +version 1.0.4 [September 24, 1999] + Define PNG_ALWAYS_EXTERN in pngconf.h if __STDC__ is defined + Delete #define PNG_INTERNAL and include "png.h" from pngasmrd.h + Made several minor corrections to pngtest.c + Renamed the makefiles with longer but more user friendly extensions. + Copied the PNG copyright and license to a separate LICENSE file. + Revised documentation, png.h, and example.c to remove reference to + "viewing_gamma" which no longer appears in the PNG specification. + Revised pngvcrd.c to use MMX code for interlacing only on the final pass. + Updated pngvcrd.c to use the faster C filter algorithms from libpng-1.0.1a + Split makefile.win32vc into two versions, makefile.vcawin32 (uses MMX + assembler code) and makefile.vcwin32 (doesn't). + Added a CPU timing report to pngtest.c (enabled by defining PNGTEST_TIMING) + Added a copy of pngnow.png to the distribution. +version 1.0.4a [September 25, 1999] + Increase max_pixel_depth in pngrutil.c if a user transform needs it. + Changed several division operations to right-shifts in pngvcrd.c +version 1.0.4b [September 30, 1999] + Added parentheses in line 3732 of pngvcrd.c + Added a comment in makefile.linux warning about buggy -O3 in pgcc 2.95.1 +version 1.0.4c [October 1, 1999] + Added a "png_check_version" function in png.c and pngtest.c that will generate + a helpful compiler error if an old png.h is found in the search path. + Changed type of png_user_transform_depth|channels from int to png_byte. +version 1.0.4d [October 6, 1999] + Changed 0.45 to 0.45455 in png_set_sRGB() + Removed unused PLTE entries from pngnow.png + Re-enabled some parts of pngvcrd.c (png_combine_row) that work properly. +version 1.0.4e [October 10, 1999] + Fixed sign error in pngvcrd.c (Greg Roelofs) + Replaced some instances of memcpy with simple assignments in pngvcrd (GR-P) +version 1.0.4f [October 15, 1999] + Surrounded example.c code with #if 0 .. #endif to prevent people from + inadvertently trying to compile it. + Changed png_get_header_version() from a function to a macro in png.h + Added type casting mostly in pngrtran.c and pngwtran.c + Removed some pointless "ptr = NULL" in pngmem.c + Added a "contrib" directory containing the source code from Greg's book. + +version 1.0.5 [October 15, 1999] + Minor editing of the INSTALL and README files. +version 1.0.5a [October 23, 1999] + Added contrib/pngsuite and contrib/pngminus (Willem van Schaik) + Fixed a typo in the png_set_sRGB() function call in example.c (Jan Nijtmans) + Further optimization and bugfix of pngvcrd.c + Revised pngset.c so that it does not allocate or free memory in the user's + text_ptr structure. Instead, it makes its own copy. + Created separate write_end_info_struct in pngtest.c for a more severe test. + Added code in pngwrite.c to free info_ptr->text[i].key to stop a memory leak. +version 1.0.5b [November 23, 1999] + Moved PNG_FLAG_HAVE_CHUNK_HEADER, PNG_FLAG_BACKGROUND_IS_GRAY and + PNG_FLAG_WROTE_tIME from flags to mode. + Added png_write_info_before_PLTE() function. + Fixed some typecasting in contrib/gregbook/*.c + Updated scripts/makevms.com and added makevms.com to contrib/gregbook + and contrib/pngminus (Martin Zinser) +version 1.0.5c [November 26, 1999] + Moved png_get_header_version from png.h to png.c, to accommodate ansi2knr. + Removed all global arrays (according to PNG_NO_GLOBAL_ARRAYS macro), to + accommodate making DLL's: Moved usr_png_ver from global variable to function + png_get_header_ver() in png.c. Moved png_sig to png_sig_bytes in png.c and + eliminated use of png_sig in pngwutil.c. Moved the various png_CHNK arrays + into pngtypes.h. Eliminated use of global png_pass arrays. Declared the + png_CHNK and png_pass arrays to be "const". Made the global arrays + available to applications (although none are used in libpng itself) when + PNG_NO_GLOBAL_ARRAYS is not defined or when PNG_GLOBAL_ARRAYS is defined. + Removed some extraneous "-I" from contrib/pngminus/makefile.std + Changed the PNG_sRGB_INTENT macros in png.h to be consistent with PNG-1.2. + Change PNG_SRGB_INTENT to PNG_sRGB_INTENT in libpng.txt and libpng.3 +version 1.0.5d [November 29, 1999] + Add type cast (png_const_charp) two places in png.c + Eliminated pngtypes.h; use macros instead to declare PNG_CHNK arrays. + Renamed "PNG_GLOBAL_ARRAYS" to "PNG_USE_GLOBAL_ARRAYS" and made available + to applications a macro "PNG_USE_LOCAL_ARRAYS". + #ifdef out all the new declarations when PNG_USE_GLOBAL_ARRAYS is defined. + Added PNG_EXPORT_VAR macro to accommodate making DLL's. +version 1.0.5e [November 30, 1999] + Added iCCP, iTXt, and sPLT support; added "lang" member to the png_text + structure; refactored the inflate/deflate support to make adding new chunks + with trailing compressed parts easier in the future, and added new functions + png_free_iCCP, png_free_pCAL, png_free_sPLT, png_free_text, png_get_iCCP, + png_get_spalettes, png_set_iCCP, png_set_spalettes (Eric S. Raymond). + NOTE: Applications that write text chunks MUST define png_text->lang + before calling png_set_text(). It must be set to NULL if you want to + write tEXt or zTXt chunks. If you want your application to be able to + run with older versions of libpng, use + + #ifdef PNG_iTXt_SUPPORTED + png_text[i].lang = NULL; + #endif + + Changed png_get_oFFs() and png_set_oFFs() to use signed rather than unsigned + offsets (Eric S. Raymond). + Combined PNG_READ_cHNK_SUPPORTED and PNG_WRITE_cHNK_SUPPORTED macros into + PNG_cHNK_SUPPORTED and combined the three types of PNG_text_SUPPORTED + macros, leaving the separate macros also available. + Removed comments on #endifs at the end of many short, non-nested #if-blocks. +version 1.0.5f [December 6, 1999] + Changed makefile.solaris to issue a warning about potential problems when + the ucb "ld" is in the path ahead of the ccs "ld". + Removed "- [date]" from the "synopsis" line in libpng.3 and libpngpf.3. + Added sCAL chunk support (Eric S. Raymond). +version 1.0.5g [December 7, 1999] + Fixed "png_free_spallettes" typo in png.h + Added code to handle new chunks in pngpread.c + Moved PNG_CHNK string macro definitions outside of PNG_NO_EXTERN block + Added "translated_key" to png_text structure and png_write_iTXt(). + Added code in pngwrite.c to work around a newly discovered zlib bug. +version 1.0.5h [December 10, 1999] + NOTE: regarding the note for version 1.0.5e, the following must also + be included in your code: + png_text[i].translated_key = NULL; + Unknown chunk handling is now supported. + Option to eliminate all floating point support was added. Some new + fixed-point functions such as png_set_gAMA_fixed() were added. + Expanded tabs and removed trailing blanks in source files. +version 1.0.5i [December 13, 1999] + Added some type casts to silence compiler warnings. + Renamed "png_free_spalette" to "png_free_spalettes" for consistency. + Removed leading blanks from a #define in pngvcrd.c + Added some parameters to the new png_set_keep_unknown_chunks() function. + Added a test for up->location != 0 in the first instance of writing + unknown chunks in pngwrite.c + Changed "num" to "i" in png_free_spalettes() and png_free_unknowns() to + prevent recursion. + Added png_free_hIST() function. + Various patches to fix bugs in the sCAL and integer cHRM processing, + and to add some convenience macros for use with sCAL. +version 1.0.5j [December 21, 1999] + Changed "unit" parameter of png_write_sCAL from png_byte to int, to work + around buggy compilers. + Added new type "png_fixed_point" for integers that hold float*100000 values + Restored backward compatibility of tEXt/zTXt chunk processing: + Restored the first four members of png_text to the same order as v.1.0.5d. + Added members "lang_key" and "itxt_length" to png_text struct. Set + text_length=0 when "text" contains iTXt data. Use the "compression" + member to distinguish among tEXt/zTXt/iTXt types. Added + PNG_ITXT_COMPRESSION_NONE (1) and PNG_ITXT_COMPRESSION_zTXt(2) macros. + The "Note" above, about backward incompatibility of libpng-1.0.5e, no + longer applies. + Fixed png_read|write_iTXt() to read|write parameters in the right order, + and to write the iTXt chunk after IDAT if it appears in the end_ptr. + Added pnggccrd.c, version of pngvcrd.c Intel assembler for gcc (Greg Roelofs) + Reversed the order of trying to write floating-point and fixed-point gAMA. +version 1.0.5k [December 27, 1999] + Added many parentheses, e.g., "if (a && b & c)" becomes "if (a && (b & c))" + Added png_handle_as_unknown() function (Glenn) + Added png_free_chunk_list() function and chunk_list and num_chunk_list members + of png_ptr. + Eliminated erroneous warnings about multiple sPLT chunks and sPLT-after-PLTE. + Fixed a libpng-1.0.5h bug in pngrutil.c that was issuing erroneous warnings + about ignoring incorrect gAMA with sRGB (gAMA was in fact not ignored) + Added png_free_tRNS(); png_set_tRNS() now malloc's its own trans array (ESR). + Define png_get_int_32 when oFFs chunk is supported as well as when pCAL is. + Changed type of proflen from png_int_32 to png_uint_32 in png_get_iCCP(). +version 1.0.5l [January 1, 2000] + Added functions png_set_read_user_chunk_fn() and png_get_user_chunk_ptr() + for setting a callback function to handle unknown chunks and for + retrieving the associated user pointer (Glenn). +version 1.0.5m [January 7, 2000] + Added high-level functions png_read_png(), png_write_png(), png_free_pixels(). +version 1.0.5n [January 9, 2000] + Added png_free_PLTE() function, and modified png_set_PLTE() to malloc its + own memory for info_ptr->palette. This makes it safe for the calling + application to free its copy of the palette any time after it calls + png_set_PLTE(). +version 1.0.5o [January 20, 2000] + Cosmetic changes only (removed some trailing blanks and TABs) +version 1.0.5p [January 31, 2000] + Renamed pngdll.mak to makefile.bd32 + Cosmetic changes in pngtest.c +version 1.0.5q [February 5, 2000] + Relocated the makefile.solaris warning about PATH problems. + Fixed pngvcrd.c bug by pushing/popping registers in mmxsupport (Bruce Oberg) + Revised makefile.gcmmx + Added PNG_SETJMP_SUPPORTED, PNG_SETJMP_NOT_SUPPORTED, and PNG_ABORT() macros +version 1.0.5r [February 7, 2000] + Removed superfluous prototype for png_get_itxt from png.h + Fixed a bug in pngrtran.c that improperly expanded the background color. + Return *num_text=0 from png_get_text() when appropriate, and fix documentation + of png_get_text() in libpng.txt/libpng.3. +version 1.0.5s [February 18, 2000] + Added "png_jmp_env()" macro to pngconf.h, to help people migrate to the + new error handler that's planned for the next libpng release, and changed + example.c, pngtest.c, and contrib programs to use this macro. + Revised some of the DLL-export macros in pngconf.h (Greg Roelofs) + Fixed a bug in png_read_png() that caused it to fail to expand some images + that it should have expanded. + Fixed some mistakes in the unused and undocumented INCH_CONVERSIONS functions + in pngget.c + Changed the allocation of palette, history, and trans arrays back to + the version 1.0.5 method (linking instead of copying) which restores + backward compatibility with version 1.0.5. Added some remarks about + that in example.c. Added "free_me" member to info_ptr and png_ptr + and added png_free_data() function. + Updated makefile.linux and makefile.gccmmx to make directories conditionally. + Made cosmetic changes to pngasmrd.h + Added png_set_rows() and png_get_rows(), for use with png_read|write_png(). + Modified png_read_png() to allocate info_ptr->row_pointers only if it + hasn't already been allocated. +version 1.0.5t [March 4, 2000] + Changed png_jmp_env() migration aiding macro to png_jmpbuf(). + Fixed "interlace" typo (should be "interlaced") in contrib/gregbook/read2-x.c + Fixed bug with use of PNG_BEFORE_IHDR bit in png_ptr->mode, introduced when + PNG_FLAG_HAVE_CHUNK_HEADER was moved into png_ptr->mode in version 1.0.5b + Files in contrib/gregbook were revised to use png_jmpbuf() and to select + a 24-bit visual if one is available, and to allow abbreviated options. + Files in contrib/pngminus were revised to use the png_jmpbuf() macro. + Removed spaces in makefile.linux and makefile.gcmmx, introduced in 1.0.5s +version 1.0.5u [March 5, 2000] + Simplified the code that detects old png.h in png.c and pngtest.c + Renamed png_spalette (_p, _pp) to png_sPLT_t (_tp, _tpp) + Increased precision of rgb_to_gray calculations from 8 to 15 bits and + added png_set_rgb_to_gray_fixed() function. + Added makefile.bc32 (32-bit Borland C++, C mode) +version 1.0.5v [March 11, 2000] + Added some parentheses to the png_jmpbuf macro definition. + Updated references to the zlib home page, which has moved to freesoftware.com. + Corrected bugs in documentation regarding png_read_row() and png_write_row(). + Updated documentation of png_rgb_to_gray calculations in libpng.3/libpng.txt. + Renamed makefile.borland,turboc3 back to makefile.bor,tc3 as in version 1.0.3, + revised borland makefiles; added makefile.ibmvac3 and makefile.gcc (Cosmin) + +version 1.0.6 [March 20, 2000] + Minor revisions of makefile.bor, libpng.txt, and gregbook/rpng2-win.c + Added makefile.sggcc (SGI IRIX with gcc) +version 1.0.6d [April 7, 2000] + Changed sprintf() to strcpy() in png_write_sCAL_s() to work without STDIO + Added data_length parameter to png_decompress_chunk() function + Revised documentation to remove reference to abandoned png_free_chnk functions + Fixed an error in png_rgb_to_gray_fixed() + Revised example.c, usage of png_destroy_write_struct(). + Renamed makefile.ibmvac3 to makefile.ibmc, added libpng.icc IBM project file + Added a check for info_ptr->free_me&PNG_FREE_TEXT when freeing text in png.c + Simplify png_sig_bytes() function to remove use of non-ISO-C strdup(). +version 1.0.6e [April 9, 2000] + Added png_data_freer() function. + In the code that checks for over-length tRNS chunks, added check of + info_ptr->num_trans as well as png_ptr->num_trans (Matthias Benckmann) + Minor revisions of libpng.txt/libpng.3. + Check for existing data and free it if the free_me flag is set, in png_set_*() + and png_handle_*(). + Only define PNG_WEIGHTED_FILTERS_SUPPORTED when PNG_FLOATING_POINT_SUPPORTED + is defined. + Changed several instances of PNG_NO_CONSOLE_ID to PNG_NO_STDIO in pngrutil.c + and mentioned the purposes of the two macros in libpng.txt/libpng.3. +version 1.0.6f [April 14, 2000] + Revised png_set_iCCP() and png_set_rows() to avoid prematurely freeing data. + Add checks in png_set_text() for NULL members of the input text structure. + Revised libpng.txt/libpng.3. + Removed superfluous prototype for png_set_itxt from png.h + Removed "else" from pngread.c, after png_error(), and changed "0" to "length". + Changed several png_errors about malformed ancillary chunks to png_warnings. +version 1.0.6g [April 24, 2000] + Added png_pass-* arrays to pnggccrd.c when PNG_USE_LOCAL_ARRAYS is defined. + Relocated paragraph about png_set_background() in libpng.3/libpng.txt + and other revisions (Matthias Benckmann) + Relocated info_ptr->free_me, png_ptr->free_me, and other info_ptr and + png_ptr members to restore binary compatibility with libpng-1.0.5 + (breaks compatibility with libpng-1.0.6). +version 1.0.6h [April 24, 2000] + Changed shared library so-number pattern from 2.x.y.z to xy.z (this builds + libpng.so.10 & libpng.so.10.6h instead of libpng.so.2 & libpng.so.2.1.0.6h) + This is a temporary change for test purposes. +version 1.0.6i [May 2, 2000] + Rearranged some members at the end of png_info and png_struct, to put + unknown_chunks_num and free_me within the original size of the png_structs + and free_me, png_read_user_fn, and png_free_fn within the original png_info, + because some old applications allocate the structs directly instead of + using png_create_*(). + Added documentation of user memory functions in libpng.txt/libpng.3 + Modified png_read_png so that it will use user_allocated row_pointers + if present, unless free_me directs that it be freed, and added description + of the use of png_set_rows() and png_get_rows() in libpng.txt/libpng.3. + Added PNG_LEGACY_SUPPORTED macro, and #ifdef out all new (since version + 1.00) members of png_struct and png_info, to regain binary compatibility + when you define this macro. Capabilities lost in this event + are user transforms (new in version 1.0.0),the user transform pointer + (new in version 1.0.2), rgb_to_gray (new in 1.0.5), iCCP, sCAL, sPLT, + the high-level interface, and unknown chunks support (all new in 1.0.6). + This was necessary because of old applications that allocate the structs + directly as authors were instructed to do in libpng-0.88 and earlier, + instead of using png_create_*(). + Added modes PNG_CREATED_READ_STRUCT and PNG_CREATED_WRITE_STRUCT which + can be used to detect codes that directly allocate the structs, and + code to check these modes in png_read_init() and png_write_init() and + generate a libpng error if the modes aren't set and PNG_LEGACY_SUPPORTED + was not defined. + Added makefile.intel and updated makefile.watcom (Pawel Mrochen) +version 1.0.6j [May 3, 2000] + Overloaded png_read_init() and png_write_init() with macros that convert + calls to png_read_init_2() or png_write_init_2() that check the version + and structure sizes. +version 1.0.7beta11 [May 7, 2000] + Removed the new PNG_CREATED_READ_STRUCT and PNG_CREATED_WRITE_STRUCT modes + which are no longer used. + Eliminated the three new members of png_text when PNG_LEGACY_SUPPORTED is + defined or when neither PNG_READ_iTXt_SUPPORTED nor PNG_WRITE_iTXT_SUPPORTED + is defined. + Made PNG_NO_READ|WRITE_iTXt the default setting, to avoid memory + overrun when old applications fill the info_ptr->text structure directly. + Added PNGAPI macro, and added it to the definitions of all exported functions. + Relocated version macro definitions ahead of the includes of zlib.h and + pngconf.h in png.h. +version 1.0.7beta12 [May 12, 2000] + Revised pngset.c to avoid a problem with expanding the png_debug macro. + Deleted some extraneous defines from pngconf.h + Made PNG_NO_CONSOLE_IO the default condition when PNG_BUILD_DLL is defined. + Use MSC _RPTn debugging instead of fprintf if _MSC_VER is defined. + Added png_access_version_number() function. + Check for mask&PNG_FREE_CHNK (for TEXT, SCAL, PCAL) in png_free_data(). + Expanded libpng.3/libpng.txt information about png_data_freer(). +version 1.0.7beta14 [May 17, 2000] (beta13 was not published) + Changed pnggccrd.c and pngvcrd.c to handle bad adaptive filter types as + warnings instead of errors, as pngrutil.c does. + Set the PNG_INFO_IDAT valid flag in png_set_rows() so png_write_png() + will actually write IDATs. + Made the default PNG_USE_LOCAL_ARRAYS depend on PNG_DLL instead of WIN32. + Make png_free_data() ignore its final parameter except when freeing data + that can have multiple instances (text, sPLT, unknowns). + Fixed a new bug in png_set_rows(). + Removed info_ptr->valid tests from png_free_data(), as in version 1.0.5. + Added png_set_invalid() function. + Fixed incorrect illustrations of png_destroy_write_struct() in example.c. +version 1.0.7beta15 [May 30, 2000] + Revised the deliberately erroneous Linux setjmp code in pngconf.h to produce + fewer error messages. + Rearranged checks for Z_OK to check the most likely path first in pngpread.c + and pngwutil.c. + Added checks in pngtest.c for png_create_*() returning NULL, and mentioned + in libpng.txt/libpng.3 the need for applications to check this. + Changed names of png_default_*() functions in pngtest to pngtest_*(). + Changed return type of png_get_x|y_offset_*() from png_uint_32 to png_int_32. + Fixed some bugs in the unused PNG_INCH_CONVERSIONS functions in pngget.c + Set each pointer to NULL after freeing it in png_free_data(). + Worked around a problem in pngconf.h; AIX's strings.h defines an "index" + macro that conflicts with libpng's png_color_16.index. (Dimitri Papadapoulos) + Added "msvc" directory with MSVC++ project files (Simon-Pierre Cadieux). +version 1.0.7beta16 [June 4, 2000] + Revised the workaround of AIX string.h "index" bug. + Added a check for overlength PLTE chunk in pngrutil.c. + Added PNG_NO_POINTER_INDEXING macro to use array-indexing instead of pointer + indexing in pngrutil.c and pngwutil.c to accommodate a buggy compiler. + Added a warning in png_decompress_chunk() when it runs out of data, e.g. + when it tries to read an erroneous PhotoShop iCCP chunk. + Added PNG_USE_DLL macro. + Revised the copyright/disclaimer/license notice. + Added contrib/msvctest directory +version 1.0.7rc1 [June 9, 2000] + Corrected the definition of PNG_TRANSFORM_INVERT_ALPHA (0x0400 not 0x0200) + Added contrib/visupng directory (Willem van Schaik) +version 1.0.7beta18 [June 23, 2000] + Revised PNGAPI definition, and pngvcrd.c to work with __GCC__ + and do not redefine PNGAPI if it is passed in via a compiler directive. + Revised visupng/PngFile.c to remove returns from within the Try block. + Removed leading underscores from "_PNG_H" and "_PNG_SAVE_BSD_SOURCE" macros. + Updated contrib/visupng/cexcept.h to version 1.0.0. + Fixed bugs in pngwrite.c and pngwutil.c that prevented writing iCCP chunks. +version 1.0.7rc2 [June 28, 2000] + Updated license to include disclaimers required by UCITA. + Fixed "DJBPP" typo in pnggccrd.c introduced in beta18. + +version 1.0.7 [July 1, 2000] + Revised the definition of "trans_values" in libpng.3/libpng.txt +version 1.0.8beta1 [July 8, 2000] + Added png_free(png_ptr, key) two places in pngpread.c to stop memory leaks. + Changed PNG_NO_STDIO to PNG_NO_CONSOLE_IO, several places in pngrutil.c and + pngwutil.c. + Changed PNG_EXPORT_VAR to use PNG_IMPEXP, in pngconf.h. + Removed unused "#include " from png.c + Added WindowsCE support. + Revised pnggccrd.c to work with gcc-2.95.2 and in the Cygwin environment. +version 1.0.8beta2 [July 10, 2000] + Added project files to the wince directory and made further revisions + of pngtest.c, pngrio.c, and pngwio.c in support of WindowsCE. +version 1.0.8beta3 [July 11, 2000] + Only set the PNG_FLAG_FREE_TRNS or PNG_FREE_TRNS flag in png_handle_tRNS() + for indexed-color input files to avoid potential double-freeing trans array + under some unusual conditions; problem was introduced in version 1.0.6f. + Further revisions to pngtest.c and files in the wince subdirectory. +version 1.0.8beta4 [July 14, 2000] + Added the files pngbar.png and pngbar.jpg to the distribution. + Added makefile.cygwin, and cygwin support in pngconf.h + Added PNG_NO_ZALLOC_ZERO macro (makes png_zalloc skip zeroing memory) +version 1.0.8rc1 [July 16, 2000] + Revised png_debug() macros and statements to eliminate compiler warnings. + +version 1.0.8 [July 24, 2000] + Added png_flush() in pngwrite.c, after png_write_IEND(). + Updated makefile.hpux to build a shared library. +version 1.0.9beta1 [November 10, 2000] + Fixed typo in scripts/makefile.hpux + Updated makevms.com in scripts and contrib/* and contrib/* (Martin Zinser) + Fixed seqence-point bug in contrib/pngminus/png2pnm (Martin Zinser) + Changed "cdrom.com" in documentation to "libpng.org" + Revised pnggccrd.c to get it all working, and updated makefile.gcmmx (Greg). + Changed type of "params" from voidp to png_voidp in png_read|write_png(). + Make sure PNGAPI and PNG_IMPEXP are defined in pngconf.h. + Revised the 3 instances of WRITEFILE in pngtest.c. + Relocated "msvc" and "wince" project subdirectories into "dll" subdirectory. + Updated png.rc in dll/msvc project + Revised makefile.dec to define and use LIBPATH and INCPATH + Increased size of global png_libpng_ver[] array from 12 to 18 chars. + Made global png_libpng_ver[], png_sig[] and png_pass_*[] arrays const. + Removed duplicate png_crc_finish() from png_handle_bKGD() function. + Added a warning when application calls png_read_update_info() multiple times. + Revised makefile.cygwin + Fixed bugs in iCCP support in pngrutil.c and pngwutil.c. + Replaced png_set_empty_plte_permitted() with png_permit_mng_features(). +version 1.0.9beta2 [November 19, 2000] + Renamed the "dll" subdirectory "projects". + Added borland project files to "projects" subdirectory. + Set VS_FF_PRERELEASE and VS_FF_PATCHED flags in msvc/png.rc when appropriate. + Add error message in png_set_compression_buffer_size() when malloc fails. +version 1.0.9beta3 [November 23, 2000] + Revised PNG_LIBPNG_BUILD_TYPE macro in png.h, used in the msvc project. + Removed the png_flush() in pngwrite.c that crashes some applications + that don't set png_output_flush_fn. + Added makefile.macosx and makefile.aix to scripts directory. +version 1.0.9beta4 [December 1, 2000] + Change png_chunk_warning to png_warning in png_check_keyword(). + Increased the first part of msg buffer from 16 to 18 in png_chunk_error(). +version 1.0.9beta5 [December 15, 2000] + Added support for filter method 64 (for PNG datastreams embedded in MNG). +version 1.0.9beta6 [December 18, 2000] + Revised png_set_filter() to accept filter method 64 when appropriate. + Added new PNG_HAVE_PNG_SIGNATURE bit to png_ptr->mode and use it to + help prevent applications from using MNG features in PNG datastreams. + Added png_permit_mng_features() function. + Revised libpng.3/libpng.txt. Changed "filter type" to "filter method". +version 1.0.9rc1 [December 23, 2000] + Revised test for PNG_HAVE_PNG_SIGNATURE in pngrutil.c + Fixed error handling of unknown compression type in png_decompress_chunk(). + In pngconf.h, define __cdecl when _MSC_VER is defined. +version 1.0.9beta7 [December 28, 2000] + Changed PNG_TEXT_COMPRESSION_zTXt to PNG_COMPRESSION_TYPE_BASE several places. + Revised memory management in png_set_hIST and png_handle_hIST in a backward + compatible manner. PLTE and tRNS were revised similarly. + Revised the iCCP chunk reader to ignore trailing garbage. +version 1.0.9beta8 [January 12, 2001] + Moved pngasmrd.h into pngconf.h. + Improved handling of out-of-spec garbage iCCP chunks generated by PhotoShop. +version 1.0.9beta9 [January 15, 2001] + Added png_set_invalid, png_permit_mng_features, and png_mmx_supported to + wince and msvc project module definition files. + Minor revision of makefile.cygwin. + Fixed bug with progressive reading of narrow interlaced images in pngpread.c +version 1.0.9beta10 [January 16, 2001] + Do not typedef png_FILE_p in pngconf.h when PNG_NO_STDIO is defined. + Fixed "png_mmx_supported" typo in project definition files. +version 1.0.9beta11 [January 19, 2001] + Updated makefile.sgi to make shared library. + Removed png_mmx_support() function and disabled PNG_MNG_FEATURES_SUPPORTED + by default, for the benefit of DLL forward compatibility. These will + be re-enabled in version 1.2.0. +version 1.0.9rc2 [January 22, 2001] + Revised cygwin support. + +version 1.0.9 [January 31, 2001] + Added check of cygwin's ALL_STATIC in pngconf.h + Added "-nommx" parameter to contrib/gregbook/rpng2-win and rpng2-x demos. +version 1.0.10beta1 [March 14, 2001] + Revised makefile.dec, makefile.sgi, and makefile.sggcc; added makefile.hpgcc. + Reformatted libpng.3 to eliminate bad line breaks. + Added checks for _mmx_supported in the read_filter_row function of pnggccrd.c + Added prototype for png_mmx_support() near the top of pnggccrd.c + Moved some error checking from png_handle_IHDR to png_set_IHDR. + Added PNG_NO_READ_SUPPORTED and PNG_NO_WRITE_SUPPORTED macros. + Revised png_mmx_support() function in pnggccrd.c + Restored version 1.0.8 PNG_WRITE_EMPTY_PLTE_SUPPORTED behavior in pngwutil.c + Fixed memory leak in contrib/visupng/PngFile.c + Fixed bugs in png_combine_row() in pnggccrd.c and pngvcrd.c (C version) + Added warnings when retrieving or setting gamma=0. + Increased the first part of msg buffer from 16 to 18 in png_chunk_warning(). +version 1.0.10rc1 [March 23, 2001] + Changed all instances of memcpy, strcpy, and strlen to png_memcpy, png_strcpy, + and png_strlen. + Revised png_mmx_supported() function in pnggccrd.c to return proper value. + Fixed bug in progressive reading (pngpread.c) with small images (height < 8). + +version 1.0.10 [March 30, 2001] + Deleted extraneous space (introduced in 1.0.9) from line 42 of makefile.cygwin + Added beos project files (Chris Herborth) +version 1.0.11beta1 [April 3, 2001] + Added type casts on several png_malloc() calls (Dimitri Papadapoulos). + Removed a no-longer needed AIX work-around from pngconf.h + Changed several "//" single-line comments to C-style in pnggccrd.c +version 1.0.11beta2 [April 11, 2001] + Removed PNGAPI from several functions whose prototypes did not have PNGAPI. + Updated scripts/pngos2.def +version 1.0.11beta3 [April 14, 2001] + Added checking the results of many instances of png_malloc() for NULL +version 1.0.11beta4 [April 20, 2001] + Undid the changes from version 1.0.11beta3. Added a check for NULL return + from user's malloc_fn(). + Removed some useless type casts of the NULL pointer. + Added makefile.netbsd + +version 1.0.11 [April 27, 2001] + Revised makefile.netbsd +version 1.0.12beta1 [May 14, 2001] + Test for Windows platform in pngconf.h when including malloc.h (Emmanuel Blot) + Updated makefile.cygwin and handling of Cygwin's ALL_STATIC in pngconf.h + Added some never-to-be-executed code in pnggccrd.c to quiet compiler warnings. + Eliminated the png_error about apps using png_read|write_init(). Instead, + libpng will reallocate the png_struct and info_struct if they are too small. + This retains future binary compatibility for old applications written for + libpng-0.88 and earlier. +version 1.2.0beta1 [May 6, 2001] + Bumped DLLNUM to 2. + Re-enabled PNG_MNG_FEATURES_SUPPORTED and enabled PNG_ASSEMBLER_CODE_SUPPORTED + by default. + Added runtime selection of MMX features. + Added png_set_strip_error_numbers function and related macros. +version 1.2.0beta2 [May 7, 2001] + Finished merging 1.2.0beta1 with version 1.0.11 + Added a check for attempts to read or write PLTE in grayscale PNG datastreams. +version 1.2.0beta3 [May 17, 2001] + Enabled user memory function by default. + Modified png_create_struct so it passes user mem_ptr to user memory allocator. + Increased png_mng_features flag from png_byte to png_uint_32. + Bumped shared-library (so-number) and dll-number to 3. +version 1.2.0beta4 [June 23, 2001] + Check for missing profile length field in iCCP chunk and free chunk_data + in case of truncated iCCP chunk. + Bumped shared-library number to 3 in makefile.sgi and makefile.sggcc + Bumped dll-number from 2 to 3 in makefile.cygwin + Revised contrib/gregbook/rpng*-x.c to avoid a memory leak and to exit cleanly + if user attempts to run it on an 8-bit display. + Updated contrib/gregbook + Use png_malloc instead of png_zalloc to allocate palette in pngset.c + Updated makefile.ibmc + Added some typecasts to eliminate gcc 3.0 warnings. Changed prototypes + of png_write_oFFS width and height from png_uint_32 to png_int_32. + Updated example.c + Revised prototypes for png_debug_malloc and png_debug_free in pngtest.c +version 1.2.0beta5 [August 8, 2001] + Revised contrib/gregbook + Revised makefile.gcmmx + Revised pnggccrd.c to conditionally compile some thread-unsafe code only + when PNG_THREAD_UNSAFE_OK is defined. + Added tests to prevent pngwutil.c from writing a bKGD or tRNS chunk with + value exceeding 2^bit_depth-1 + Revised makefile.sgi and makefile.sggcc + Replaced calls to fprintf(stderr,...) with png_warning() in pnggccrd.c + Removed restriction that do_invert_mono only operate on 1-bit opaque files + +version 1.2.0 [September 1, 2001] + Changed a png_warning() to png_debug() in pnggccrd.c + Fixed contrib/gregbook/rpng-x.c, rpng2-x.c to avoid crash with XFreeGC(). +version 1.2.1beta1 [October 19, 2001] + Revised makefile.std in contrib/pngminus + Include background_1 in png_struct regardless of gamma support. + Revised makefile.netbsd and makefile.macosx, added makefile.darwin. + Revised example.c to provide more details about using row_callback(). +version 1.2.1beta2 [October 25, 2001] + Added type cast to each NULL appearing in a function call, except for + WINCE functions. + Added makefile.so9. +version 1.2.1beta3 [October 27, 2001] + Removed type casts from all NULLs. + Simplified png_create_struct_2(). +version 1.2.1beta4 [November 7, 2001] + Revised png_create_info_struct() and png_creat_struct_2(). + Added error message if png_write_info() was omitted. + Type cast NULLs appearing in function calls when _NO_PROTO or + PNG_TYPECAST_NULL is defined. +version 1.2.1rc1 [November 24, 2001] + Type cast NULLs appearing in function calls except when PNG_NO_TYPECAST_NULL + is defined. + Changed typecast of "size" argument to png_size_t in pngmem.c calls to + the user malloc_fn, to agree with the prototype in png.h + Added a pop/push operation to pnggccrd.c, to preserve Eflag (Maxim Sobolev) + Updated makefile.sgi to recognize LIBPATH and INCPATH. + Updated various makefiles so "make clean" does not remove previous major + version of the shared library. +version 1.2.1rc2 [December 4, 2001] + Always allocate 256-entry internal palette, hist, and trans arrays, to + avoid out-of-bounds memory reference caused by invalid PNG datastreams. + Added a check for prefix_length > data_length in iCCP chunk handler. + +version 1.2.1 [December 7, 2001] + None. +version 1.2.2beta1 [February 22, 2002] + Fixed a bug with reading the length of iCCP profiles (Larry Reeves). + Revised makefile.linux, makefile.gcmmx, and makefile.sgi to generate + libpng.a, libpng12.so (not libpng.so.3), and libpng12/png.h + Revised makefile.darwin to remove "-undefined suppress" option. + Added checks for gamma and chromaticity values over 21474.83, which exceed + the limit for PNG unsigned 32-bit integers when encoded. + Revised calls to png_create_read_struct() and png_create_write_struct() + for simpler debugging. + Revised png_zalloc() so zlib handles errors (uses PNG_FLAG_MALLOC_NULL_MEM_OK) +version 1.2.2beta2 [February 23, 2002] + Check chunk_length and idat_size for invalid (over PNG_MAX_UINT) lengths. + Check for invalid image dimensions in png_get_IHDR. + Added missing "fi;" in the install target of the SGI makefiles. + Added install-static to all makefiles that make shared libraries. + Always do gamma compensation when image is partially transparent. +version 1.2.2beta3 [March 7, 2002] + Compute background.gray and background_1.gray even when color_type is RGB + in case image gets reduced to gray later. + Modified shared-library makefiles to install pkgconfig/libpngNN.pc. + Export (with PNGAPI) png_zalloc, png_zfree, and png_handle_as_unknown + Removed unused png_write_destroy_info prototype from png.h + Eliminated incorrect use of width_mmx from pnggccrd.c in pixel_bytes == 8 case + Added install-shared target to all makefiles that make shared libraries. + Stopped a double free of palette, hist, and trans when not using free_me. + Added makefile.32sunu for Sun Ultra 32 and makefile.64sunu for Sun Ultra 64. +version 1.2.2beta4 [March 8, 2002] + Compute background.gray and background_1.gray even when color_type is RGB + in case image gets reduced to gray later (Jason Summers). + Relocated a misplaced /bin/rm in the "install-shared" makefile targets + Added PNG_1_0_X macro which can be used to build a 1.0.x-compatible library. +version 1.2.2beta5 [March 26, 2002] + Added missing PNGAPI to several function definitions. + Check for invalid bit_depth or color_type in png_get_IHDR(), and + check for missing PLTE or IHDR in png_push_read_chunk() (Matthias Clasen). + Revised iTXt support to accept NULL for lang and lang_key. + Compute gamma for color components of background even when color_type is gray. + Changed "()" to "{}" in scripts/libpng.pc.in. + Revised makefiles to put png.h and pngconf.h only in $prefix/include/libpngNN + Revised makefiles to make symlink to libpng.so.NN in addition to libpngNN.so +version 1.2.2beta6 [March 31, 2002] +version 1.0.13beta1 [March 31, 2002] + Prevent png_zalloc() from trying to memset memory that it failed to acquire. + Add typecasts of PNG_MAX_UINT in pngset_cHRM_fixed() (Matt Holgate). + Ensure that the right function (user or default) is used to free the + png_struct after an error in png_create_read_struct_2(). +version 1.2.2rc1 [April 7, 2002] +version 1.0.13rc1 [April 7, 2002] + Save the ebx register in pnggccrd.c (Sami Farin) + Add "mem_ptr = png_ptr->mem_ptr" in png_destroy_write_struct() (Paul Gardner). + Updated makefiles to put headers in include/libpng and remove old include/*.h. + +version 1.2.2 [April 15, 2002] +version 1.0.13 [April 15, 2002] + Revised description of png_set_filter() in libpng.3/libpng.txt. + Revised makefile.netbsd and added makefile.neNNbsd and makefile.freebsd +version 1.0.13patch01 [April 17, 2002] +version 1.2.2patch01 [April 17, 2002] + Changed ${PNGMAJ}.${PNGVER} bug to ${PNGVER} in makefile.sgi and makefile.sggcc + Fixed VER -> PNGVER typo in makefile.macosx and added install-static to install + Added install: target to makefile.32sunu and makefile.64sunu +version 1.0.13patch03 [April 18, 2002] +version 1.2.2patch03 [April 18, 2002] + Revised 15 makefiles to link libpng.a to libpngNN.a and the include libpng + subdirectory to libpngNN subdirectory without the full pathname. + Moved generation of libpng.pc from "install" to "all" in 15 makefiles. +version 1.2.3rc1 [April 28, 2002] + Added install-man target to 15 makefiles (Dimitri Papadopolous-Orfanos). + Added $(DESTDIR) feature to 24 makefiles (Tim Mooney) + Fixed bug with $prefix, should be $(prefix) in makefile.hpux. + Updated cygwin-specific portion of pngconf.h and revised makefile.cygwin + Added a link from libpngNN.pc to libpng.pc in 15 makefiles. + Added links from include/libpngNN/*.h to include/*.h in 24 makefiles. + Revised makefile.darwin to make relative links without full pathname. + Added setjmp() at the end of png_create_*_struct_2() in case user forgets + to put one in their application. + Restored png_zalloc() and png_zfree() prototypes to version 1.2.1 and + removed them from module definition files. +version 1.2.3rc2 [May 1, 2002] + Fixed bug in reporting number of channels in pngget.c and pngset.c, + that was introduced in version 1.2.2beta5. + Exported png_zalloc(), png_zfree(), png_default_read(), png_default_write(), + png_default_flush(), and png_push_fill_buffer() and included them in + module definition files. + Added "libpng.pc" dependency to the "install-shared" target in 15 makefiles. +version 1.2.3rc3 [May 1, 2002] + Revised prototype for png_default_flush() + Remove old libpng.pc and libpngNN.pc before installing new ones. +version 1.2.3rc4 [May 2, 2002] + Typos in *.def files (png_default_read|write -> png_default_read|write_data) + In makefiles, changed rm libpng.NN.pc to rm libpngNN.pc + Added libpng-config and libpngNN-config and modified makefiles to install them. + Changed $(MANPATH) to $(DESTDIR)$(MANPATH) in makefiles + Added "Win32 DLL VB" configuration to projects/msvc/libpng.dsp +version 1.2.3rc5 [May 11, 2002] + Changed "error" and "message" in prototypes to "error_message" and + "warning_message" to avoid namespace conflict. + Revised 15 makefiles to build libpng-config from libpng-config-*.in + Once more restored png_zalloc and png_zfree to regular nonexported form. + Restored png_default_read|write_data, png_default_flush, png_read_fill_buffer + to nonexported form, but with PNGAPI, and removed them from module def files. +version 1.2.3rc6 [May 14, 2002] + Removed "PNGAPI" from png_zalloc() and png_zfree() in png.c + Changed "Gz" to "Gd" in projects/msvc/libpng.dsp and zlib.dsp. + Removed leftover libpng-config "sed" script from four makefiles. + Revised libpng-config creating script in 16 makefiles. + +version 1.2.3 [May 22, 2002] + Revised libpng-config target in makefile.cygwin. + Removed description of png_set_mem_fn() from documentation. + Revised makefile.freebsd. + Minor cosmetic changes to 15 makefiles, e.g., $(DI) = $(DESTDIR)/$(INCDIR). + Revised projects/msvc/README.txt + Changed -lpng to -lpngNN in LDFLAGS in several makefiles. +version 1.2.4beta1 [May 24, 2002] + Added libpng.pc and libpng-config to "all:" target in 16 makefiles. + Fixed bug in 16 makefiles: $(DESTDIR)/$(LIBPATH) to $(DESTDIR)$(LIBPATH) + Added missing "\" before closing double quote in makefile.gcmmx. + Plugged various memory leaks; added png_malloc_warn() and png_set_text_2() + functions. +version 1.2.4beta2 [June 25, 2002] + Plugged memory leak of png_ptr->current_text (Matt Holgate). + Check for buffer overflow before reading CRC in pngpread.c (Warwick Allison) + Added -soname to the loader flags in makefile.dec, makefile.sgi, and + makefile.sggcc. + Added "test-installed" target to makefile.linux, makefile.gcmmx, + makefile.sgi, and makefile.sggcc. +version 1.2.4beta3 [June 28, 2002] + Plugged memory leak of row_buf in pngtest.c when there is a png_error(). + Detect buffer overflow in pngpread.c when IDAT is corrupted with extra data. + Added "test-installed" target to makefile.32sunu, makefile.64sunu, + makefile.beos, makefile.darwin, makefile.dec, makefile.macosx, + makefile.solaris, makefile.hpux, makefile.hpgcc, and makefile.so9. +version 1.2.4rc1 and 1.0.14rc1 [July 2, 2002] + Added "test-installed" target to makefile.cygwin and makefile.sco. + Revised pnggccrd.c to be able to back out version 1.0.x via PNG_1_0_X macro. + +version 1.2.4 and 1.0.14 [July 8, 2002] + Changed png_warning() to png_error() when width is too large to process. +version 1.2.4patch01 [July 20, 2002] + Revised makefile.cygwin to use DLL number 12 instead of 13. +version 1.2.5beta1 [August 6, 2002] + Added code to contrib/gregbook/readpng2.c to ignore unused chunks. + Replaced toucan.png in contrib/gregbook (it has been corrupt since 1.0.11) + Removed some stray *.o files from contrib/gregbook. + Changed png_error() to png_warning() about "Too much data" in pngpread.c + and about "Extra compressed data" in pngrutil.c. + Prevent png_ptr->pass from exceeding 7 in png_push_finish_row(). + Updated makefile.hpgcc + Updated png.c and pnggccrd.c handling of return from png_mmx_support() +version 1.2.5beta2 [August 15, 2002] + Only issue png_warning() about "Too much data" in pngpread.c when avail_in + is nonzero. + Updated makefiles to install a separate libpng.so.3 with its own rpath. +version 1.2.5rc1 and 1.0.15rc1 [August 24, 2002] + Revised makefiles to not remove previous minor versions of shared libraries. +version 1.2.5rc2 and 1.0.15rc2 [September 16, 2002] + Revised 13 makefiles to remove "-lz" and "-L$(ZLIBLIB)", etc., from shared + library loader directive. + Added missing "$OBJSDLL" line to makefile.gcmmx. + Added missing "; fi" to makefile.32sunu. +version 1.2.5rc3 and 1.0.15rc3 [September 18, 2002] + Revised libpng-config script. + +version 1.2.5 and 1.0.15 [October 3, 2002] + Revised makefile.macosx, makefile.darwin, makefile.hpgcc, and makefile.hpux, + and makefile.aix. + Relocated two misplaced PNGAPI lines in pngtest.c +version 1.2.6beta1 [October 22, 2002] + Commented out warning about uninitialized mmx_support in pnggccrd.c. + Changed "IBMCPP__" flag to "__IBMCPP__" in pngconf.h. + Relocated two more misplaced PNGAPI lines in pngtest.c + Fixed memory overrun bug in png_do_read_filler() with 16-bit datastreams, + introduced in version 1.0.2. + Revised makefile.macosx, makefile.dec, makefile.aix, and makefile.32sunu. +version 1.2.6beta2 [November 1, 2002] + Added libpng-config "--ldopts" output. + Added "AR=ar" and "ARFLAGS=rc" and changed "ar rc" to "$(AR) $(ARFLAGS)" + in makefiles. +version 1.2.6beta3 [July 18, 2004] + Reverted makefile changes from version 1.2.6beta2 and some of the changes + from version 1.2.6beta1; these will be postponed until version 1.2.7. + Version 1.2.6 is going to be a simple bugfix release. + Changed the one instance of "ln -sf" to "ln -f -s" in each Sun makefile. + Fixed potential overrun in pngerror.c by using strncpy instead of memcpy. + Added "#!/bin/sh" at the top of configure, for recognition of the + 'x' flag under Cygwin (Cosmin). + Optimized vacuous tests that silence compiler warnings, in png.c (Cosmin). + Added support for PNG_USER_CONFIG, in pngconf.h (Cosmin). + Fixed the special memory handler for Borland C under DOS, in pngmem.c + (Cosmin). + Removed some spurious assignments in pngrutil.c (Cosmin). + Replaced 65536 with 65536L, and 0xffff with 0xffffL, to silence warnings + on 16-bit platforms (Cosmin). + Enclosed shift op expressions in parentheses, to silence warnings (Cosmin). + Used proper type png_fixed_point, to avoid problems on 16-bit platforms, + in png_handle_sRGB() (Cosmin). + Added compression_type to png_struct, and optimized the window size + inside the deflate stream (Cosmin). + Fixed definition of isnonalpha(), in pngerror.c and pngrutil.c (Cosmin). + Fixed handling of unknown chunks that come after IDAT (Cosmin). + Allowed png_error() and png_warning() to work even if png_ptr == NULL + (Cosmin). + Replaced row_info->rowbytes with row_bytes in png_write_find_filter() + (Cosmin). + Fixed definition of PNG_LIBPNG_VER_DLLNUM (Simon-Pierre). + Used PNG_LIBPNG_VER and PNG_LIBPNG_VER_STRING instead of the hardcoded + values in png.c (Simon-Pierre, Cosmin). + Initialized png_libpng_ver[] with PNG_LIBPNG_VER_STRING (Simon-Pierre). + Replaced PNG_LIBPNG_VER_MAJOR with PNG_LIBPNG_VER_DLLNUM in png.rc + (Simon-Pierre). + Moved the definition of PNG_HEADER_VERSION_STRING near the definitions + of the other PNG_LIBPNG_VER_... symbols in png.h (Cosmin). + Relocated #ifndef PNGAPI guards in pngconf.h (Simon-Pierre, Cosmin). + Updated scripts/makefile.vc(a)win32 (Cosmin). + Updated the MSVC project (Simon-Pierre, Cosmin). + Updated the Borland C++ Builder project (Cosmin). + Avoided access to asm_flags in pngvcrd.c, if PNG_1_0_X is defined (Cosmin). + Commented out warning about uninitialized mmx_support in pngvcrd.c (Cosmin). + Removed scripts/makefile.bd32 and scripts/pngdef.pas (Cosmin). + Added extra guard around inclusion of Turbo C memory headers, in pngconf.h + (Cosmin). + Renamed projects/msvc/ to projects/visualc6/, and projects/borland/ to + projects/cbuilder5/ (Cosmin). + Moved projects/visualc6/png32ms.def to scripts/pngw32.def, + and projects/visualc6/png.rc to scripts/pngw32.rc (Cosmin). + Added projects/visualc6/pngtest.dsp; removed contrib/msvctest/ (Cosmin). + Changed line endings to DOS style in cbuilder5 and visualc6 files, even + in the tar.* distributions (Cosmin). + Updated contrib/visupng/VisualPng.dsp (Cosmin). + Updated contrib/visupng/cexcept.h to version 2.0.0 (Cosmin). + Added a separate distribution with "configure" and supporting files (Junichi). +version 1.2.6beta4 [July 28, 2004] + Added user ability to change png_size_t via a PNG_SIZE_T macro. + Added png_sizeof() and png_convert_size() functions. + Added PNG_SIZE_MAX (maximum value of a png_size_t variable. + Added check in png_malloc_default() for (size_t)size != (png_uint_32)size + which would indicate an overflow. + Changed sPLT failure action from png_error to png_warning and abandon chunk. + Changed sCAL and iCCP failures from png_error to png_warning and abandon. + Added png_get_uint_31(png_ptr, buf) function. + Added PNG_UINT_32_MAX macro. + Renamed PNG_MAX_UINT to PNG_UINT_31_MAX. + Made png_zalloc() issue a png_warning and return NULL on potential + overflow. + Turn on PNG_NO_ZALLOC_ZERO by default in version 1.2.x + Revised "clobber list" in pnggccrd.c so it will compile under gcc-3.4. + Revised Borland portion of png_malloc() to return NULL or issue + png_error() according to setting of PNG_FLAG_MALLOC_NULL_MEM_OK. + Added PNG_NO_SEQUENTIAL_READ_SUPPORTED macro to conditionally remove + sequential read support. + Added some "#if PNG_WRITE_SUPPORTED" blocks. + #ifdef'ed out some redundancy in png_malloc_default(). + Use png_malloc instead of png_zalloc to allocate the pallete. +version 1.0.16rc1 and 1.2.6rc1 [August 4, 2004] + Fixed buffer overflow vulnerability in png_handle_tRNS() + Fixed integer arithmetic overflow vulnerability in png_read_png(). + Fixed some harmless bugs in png_handle_sBIT, etc, that would cause + duplicate chunk types to go undetected. + Fixed some timestamps in the -config version + Rearranged order of processing of color types in png_handle_tRNS(). + Added ROWBYTES macro to calculate rowbytes without integer overflow. + Updated makefile.darwin and removed makefile.macosx from scripts directory. + Imposed default one million column, one-million row limits on the image + dimensions, and added png_set_user_limits() function to override them. + Revised use of PNG_SET_USER_LIMITS_SUPPORTED macro. + Fixed wrong cast of returns from png_get_user_width|height_max(). + Changed some "keep the compiler happy" from empty statements to returns, + Revised libpng.txt to remove 1.2.x stuff from the 1.0.x distribution +version 1.0.16rc2 and 1.2.6rc2 [August 7, 2004] + Revised makefile.darwin and makefile.solaris. Removed makefile.macosx. + Revised pngtest's png_debug_malloc() to use png_malloc() instead of + png_malloc_default() which is not supposed to be exported. + Fixed off-by-one error in one of the conversions to PNG_ROWBYTES() in + pngpread.c. Bug was introduced in 1.2.6rc1. + Fixed bug in RGB to RGBX transformation introduced in 1.2.6rc1. + Fixed old bug in RGB to Gray transformation. + Fixed problem with 64-bit compilers by casting arguments to abs() + to png_int_32. + Changed "ln -sf" to "ln -f -s" in three makefiles (solaris, sco, so9). + Changed "HANDLE_CHUNK_*" to "PNG_HANDLE_CHUNK_*" (Cosmin) + Added "-@/bin/rm -f $(DL)/$(LIBNAME).so.$(PNGMAJ)" to 15 *NIX makefiles. + Added code to update the row_info->colortype in png_do_read_filler() (MSB). +version 1.0.16rc3 and 1.2.6rc3 [August 9, 2004] + Eliminated use of "abs()" in testing cHRM and gAMA values, to avoid + trouble with some 64-bit compilers. Created PNG_OUT_OF_RANGE() macro. + Revised documentation of png_set_keep_unknown_chunks(). + Check handle_as_unknown status in pngpread.c, as in pngread.c previously. + Moved "PNG_HANDLE_CHUNK_*" macros out of PNG_INTERNAL section of png.h + Added "rim" definitions for CONST4 and CONST6 in pnggccrd.c +version 1.0.16rc4 and 1.2.6rc4 [August 10, 2004] + Fixed mistake in pngtest.c introduced in 1.2.6rc2 (declaration of + "pinfo" was out of place). +version 1.0.16rc5 and 1.2.6rc5 [August 10, 2004] + Moved "PNG_HANDLE_CHUNK_*" macros out of PNG_ASSEMBLER_CODE_SUPPORTED + section of png.h where they were inadvertently placed in version rc3. + +version 1.2.6 and 1.0.16 [August 15, 2004] + Revised pngtest so memory allocation testing is only done when PNG_DEBUG==1. +version 1.2.7beta1 [August 26, 2004] + Removed unused pngasmrd.h file. + Removed references to uu.net for archived files. Added references to + PNG Spec (second edition) and the PNG ISO/IEC Standard. + Added "test-dd" target in 15 makefiles, to run pngtest in DESTDIR. + Fixed bug with "optimized window size" in the IDAT datastream, that + causes libpng to write PNG files with incorrect zlib header bytes. +version 1.2.7beta2 [August 28, 2004] + Fixed bug with sCAL chunk and big-endian machines (David Munro). + Undid new code added in 1.2.6rc2 to update the color_type in + png_set_filler(). + Added png_set_add_alpha() that updates color type. +version 1.0.17rc1 and 1.2.7rc1 [September 4, 2004] + Revised png_set_strip_filler() to not remove alpha if color_type has alpha. + +version 1.2.7 and 1.0.17 [September 12, 2004] + Added makefile.hp64 + Changed projects/msvc/png32ms.def to scripts/png32ms.def in makefile.cygwin +version 1.2.8beta1 [November 1, 2004] + Fixed bug in png_text_compress() that would fail to complete a large block. + Fixed bug, introduced in libpng-1.2.7, that overruns a buffer during + strip alpha operation in png_do_strip_filler(). + Added PNG_1_2_X definition in pngconf.h + #ifdef out png_info_init in png.c and png_read_init in pngread.c (as of 1.3.0) +version 1.2.8beta2 [November 2, 2004] + Reduce color_type to a nonalpha type after strip alpha operation in + png_do_strip_filler(). +version 1.2.8beta3 [November 3, 2004] + Revised definitions of PNG_MAX_UINT_32, PNG_MAX_SIZE, and PNG_MAXSUM +version 1.2.8beta4 [November 12, 2004] + Fixed (again) definition of PNG_LIBPNG_VER_DLLNUM in png.h (Cosmin). + Added PNG_LIBPNG_BUILD_PRIVATE in png.h (Cosmin). + Set png_ptr->zstream.data_type to Z_BINARY, to avoid unnecessary detection + of data type in deflate (Cosmin). + Deprecated but continue to support SPECIALBUILD and PRIVATEBUILD in favor of + PNG_LIBPNG_BUILD_SPECIAL_STRING and PNG_LIBPNG_BUILD_PRIVATE_STRING. +version 1.2.8beta5 [November 20, 2004] + Use png_ptr->flags instead of png_ptr->transformations to pass + PNG_STRIP_ALPHA info to png_do_strip_filler(), to preserve ABI + compatibility. + Revised handling of SPECIALBUILD, PRIVATEBUILD, + PNG_LIBPNG_BUILD_SPECIAL_STRING and PNG_LIBPNG_BUILD_PRIVATE_STRING. +version 1.2.8rc1 [November 24, 2004] + Moved handling of BUILD macros from pngconf.h to png.h + Added definition of PNG_LIBPNG_BASE_TYPE in png.h, inadvertently + omitted from beta5. + Revised scripts/pngw32.rc + Despammed mailing addresses by masking "@" with "at". + Inadvertently installed a supposedly faster test version of pngrutil.c +version 1.2.8rc2 [November 26, 2004] + Added two missing "\" in png.h + Change tests in pngread.c and pngpread.c to + if (png_ptr->transformations || (png_ptr->flags&PNG_FLAG_STRIP_ALPHA)) + png_do_read_transformations(png_ptr); +version 1.2.8rc3 [November 28, 2004] + Reverted pngrutil.c to version libpng-1.2.8beta5. + Added scripts/makefile.elf with supporting code in pngconf.h for symbol + versioning (John Bowler). +version 1.2.8rc4 [November 29, 2004] + Added projects/visualc7 (Simon-pierre). +version 1.2.8rc5 [November 29, 2004] + Fixed new typo in scripts/pngw32.rc + +version 1.2.8 [December 3, 2004] + Removed projects/visualc7, added projects/visualc71. + +version 1.2.9beta1 [February 21, 2006] + + Initialized some structure members in pngwutil.c to avoid gcc-4.0.0 complaints + Revised man page and libpng.txt to make it clear that one should not call + png_read_end or png_write_end after png_read_png or png_write_png. + Updated references to png-mng-implement mailing list. + Fixed an incorrect typecast in pngrutil.c + Added PNG_NO_READ_SUPPORTED conditional for making a write-only library. + Added PNG_NO_WRITE_INTERLACING_SUPPORTED conditional. + Optimized alpha-inversion loops in pngwtran.c + Moved test for nonzero gamma outside of png_build_gamma_table() in pngrtran.c + Make sure num_trans is <= 256 before copying data in png_set_tRNS(). + Make sure num_palette is <= 256 before copying data in png_set_PLTE(). + Interchanged order of write_swap_alpha and write_invert_alpha transforms. + Added parentheses in the definition of PNG_LIBPNG_BUILD_TYPE (Cosmin). + Optimized zlib window flag (CINFO) in contrib/pngsuite/*.png (Cosmin). + Updated scripts/makefile.bc32 for Borland C++ 5.6 (Cosmin). + Exported png_get_uint_32, png_save_uint_32, png_get_uint_16, png_save_uint_16, + png_get_int_32, png_save_int_32, png_get_uint_31 (Cosmin). + Added type cast (png_byte) in png_write_sCAL() (Cosmin). + Fixed scripts/makefile.cygwin (Christian Biesinger, Cosmin). + Default iTXt support was inadvertently enabled. + +version 1.2.9beta2 [February 21, 2006] + + Check for png_rgb_to_gray and png_gray_to_rgb read transformations before + checking for png_read_dither in pngrtran.c + Revised checking of chromaticity limits to accommodate extended RGB + colorspace (John Denker). + Changed line endings in some of the project files to CRLF, even in the + "Unix" tar distributions (Cosmin). + Made png_get_int_32 and png_save_int_32 always available (Cosmin). + Updated scripts/pngos2.def, scripts/pngw32.def and projects/wince/png32ce.def + with the newly exported functions. + Eliminated distributions without the "configure" script. + Updated INSTALL instructions. + +version 1.2.9beta3 [February 24, 2006] + + Fixed CRCRLF line endings in contrib/visupng/VisualPng.dsp + Made libpng.pc respect EXEC_PREFIX (D. P. Kreil, J. Bowler) + Removed reference to pngasmrd.h from Makefile.am + Renamed CHANGES to ChangeLog. + Renamed LICENSE to COPYING. + Renamed ANNOUNCE to NEWS. + Created AUTHORS file. + +version 1.2.9beta4 [March 3, 2006] + + Changed definition of PKGCONFIG from $prefix/lib to $libdir in configure.ac + Reverted to filenames LICENSE and ANNOUNCE; removed AUTHORS and COPYING. + Removed newline from the end of some error and warning messages. + Removed test for sqrt() from configure.ac and configure. + Made swap tables in pngtrans.c PNG_CONST (Carlo Bramix). + Disabled default iTXt support that was inadvertently enabled in + libpng-1.2.9beta1. + Added "OS2" to list of systems that don't need underscores, in pnggccrd.c + Removed libpng version and date from *.c files. + +version 1.2.9beta5 [March 4, 2006] + Removed trailing blanks from source files. + Put version and date of latest change in each source file, and changed + copyright year accordingly. + More cleanup of configure.ac, Makefile.ac, and associated scripts. + Restored scripts/makefile.elf which was inadvertently deleted. + +version 1.2.9beta6 [March 6, 2006] + Fixed typo (RELEASE) in configuration files. + +version 1.2.9beta7 [March 7, 2006] + Removed libpng.vers and libpng.sym from libpng12_la_SOURCES in Makefile.am + Fixed inconsistent #ifdef's around png_sig_bytes() and png_set_sCAL_s() + in png.h. + Updated makefile.elf as suggested by debian. + Made cosmetic changes to some makefiles, adding LN_SF and other macros. + Made some makefiles accept "exec_prefix". + +version 1.2.9beta8 [March 9, 2006] + Fixed some "#if defined (..." which should be "#if defined(..." + Bug introduced in libpng-1.2.8. + Fixed inconsistency in definition of png_default_read_data() + Restored blank that was lost from makefile.sggcc "clean" target in beta7. + Revised calculation of "current" and "major" for irix in ltmain.sh + Changed "mkdir" to "MKDIR_P" in some makefiles. + Separated PNG_EXPAND and PNG_EXPAND_tRNS. + Added png_set_expand_gray_1_2_4_to_8() and deprecated + png_set_gray_1_2_4_to_8() which also expands tRNS to alpha. + +version 1.2.9beta9 [March 10, 2006] + Include "config.h" in pngconf.h when available. + Added some checks for NULL png_ptr or NULL info_ptr (timeless) + +version 1.2.9beta10 [March 20, 2006] + Removed extra CR from contrib/visualpng/VisualPng.dsw (Cosmin) + Made pnggccrd.c PIC-compliant (Christian Aichinger). + Added makefile.mingw (Wolfgang Glas). + Revised pngconf.h MMX checking. + +version 1.2.9beta11 [March 22, 2006] + Fixed out-of-order declaration in pngwrite.c that was introduced in beta9 + Simplified some makefiles by using LIBSO, LIBSOMAJ, and LIBSOVER macros. + +version 1.2.9rc1 [March 31, 2006] + Defined PNG_USER_PRIVATEBUILD when including "pngusr.h" (Cosmin). + Removed nonsensical assertion check from pngtest.c (Cosmin). + +version 1.2.9 [April 14, 2006] + Revised makefile.beos and added "none" selector in ltmain.sh + +version 1.2.10beta1 [April 15, 2006] + Renamed "config.h" to "png_conf.h" and revised Makefile.am to add + -DPNG_BUILDING_LIBPNG to compile directive, and modified pngconf.h + to include png_conf.h only when PNG_BUILDING_LIBPNG is defined. + +version 1.2.10beta2 [April 15, 2006] + Manually updated Makefile.in and configure. Changed png_conf.h.in + back to config.h. + +version 1.2.10beta3 [April 15, 2006] + Change png_conf.h back to config.h in pngconf.h. + +version 1.2.10beta4 [April 16, 2006] + Change PNG_BUILDING_LIBPNG to PNG_CONFIGURE_LIBPNG in config/Makefile*. + +version 1.2.10beta5 [April 16, 2006] + Added a configure check for compiling assembler code in pnggccrd.c + +version 1.2.10beta6 [April 17, 2006] + Revised the configure check for pnggccrd.c + Moved -DPNG_CONFIGURE_LIBPNG into @LIBPNG_DEFINES@ + Added @LIBPNG_DEFINES@ to arguments when building libpng.sym + +version 1.2.10beta7 [April 18, 2006] + Change "exec_prefix=$prefix" to "exec_prefix=$(prefix)" in makefiles. + +version 1.2.10rc1 [April 19, 2006] + Ensure pngconf.h doesn't define both PNG_USE_PNGGCCRD and PNG_USE_PNGVCRD + Fixed "LN_FS" typo in makefile.sco and makefile.solaris. + +version 1.2.10rc2 [April 20, 2006] + Added a backslash between -DPNG_CONFIGURE_LIBPNG and -DPNG_NO_ASSEMBLER_CODE + in configure.ac and configure + Made the configure warning about versioned symbols less arrogant. + +version 1.2.10rc3 [April 21, 2006] + Added a note in libpng.txt that png_set_sig_bytes(8) can be used when + writing an embedded PNG without the 8-byte signature. + Revised makefiles and configure to avoid making links to libpng.so.* + +version 1.2.10 [April 23, 2006] + Reverted configure to "rc2" state. + +version 1.2.11beta1 [May 31, 2006] + scripts/libpng.pc.in contained "configure" style version info and would + not work with makefiles. + The shared-library makefiles were linking to libpng.so.0 instead of + libpng.so.3 compatibility as the library. + +version 1.2.11beta2 [June 2, 2006] + Increased sprintf buffer from 50 to 52 chars in pngrutil.c to avoid + buffer overflow. + Fixed bug in example.c (png_set_palette_rgb -> png_set_palette_to_rgb) + +version 1.2.11beta3 [June 5, 2006] + Prepended "#! /bin/sh" to ltmail.sh and contrib/pngminus/*.sh (Cosmin). + Removed the accidental leftover Makefile.in~ (Cosmin). + Avoided potential buffer overflow and optimized buffer in + png_write_sCAL(), png_write_sCAL_s() (Cosmin). + Removed the include directories and libraries from CFLAGS and LDFLAGS + in scripts/makefile.gcc (Nelson A. de Oliveira, Cosmin). + +version 1.2.11beta4 [June 6, 2006] + Allow zero-length IDAT chunks after the entire zlib datastream, but not + after another intervening chunk type. + +version 1.0.19rc1, 1.2.11rc1 [June 13, 2006] + Deleted extraneous square brackets from [config.h] in configure.ac + +version 1.0.19rc2, 1.2.11rc2 [June 14, 2006] + Added prototypes for PNG_INCH_CONVERSIONS functions to png.h + Revised INSTALL and autogen.sh + Fixed typo in several makefiles (-W1 should be -Wl) + Added typedef for png_int_32 and png_uint_32 on 64-bit systems. + +version 1.0.19rc3, 1.2.11rc3 [June 15, 2006] + Removed the new typedefs for 64-bit systems (delay until version 1.4.0) + Added one zero element to png_gamma_shift[] array in pngrtran.c to avoid + reading out of bounds. + +version 1.0.19rc4, 1.2.11rc4 [June 15, 2006] + Really removed the new typedefs for 64-bit systems. + +version 1.0.19rc5, 1.2.11rc5 [June 22, 2006] + Removed png_sig_bytes entry from scripts/pngw32.def + +version 1.0.19, 1.2.11 [June 26, 2006] + None. + +version 1.0.20, 1.2.12 [June 27, 2006] + Really increased sprintf buffer from 50 to 52 chars in pngrutil.c to avoid + buffer overflow. + +version 1.2.13beta1 [October 2, 2006] + Removed AC_FUNC_MALLOC from configure.ac + Work around Intel-Mac compiler bug by setting PNG_NO_MMX_CODE in pngconf.h + Change "logical" to "bitwise" throughout documentation. + Detect and fix attempt to write wrong iCCP profile length. + +version 1.0.21, 1.2.13 [November 14, 2006] + Fix potential buffer overflow in sPLT chunk handler. + Fix Makefile.am to not try to link to noexistent files. + Check all exported functions for NULL png_ptr. + +version 1.2.14beta1 [November 17, 2006] + Relocated three misplaced tests for NULL png_ptr. + Built Makefile.in with automake-1.9.6 instead of 1.9.2. + Build configure with autoconf-2.60 instead of 2.59 + +version 1.2.14beta2 [November 17, 2006] + Added some typecasts in png_zalloc(). + +version 1.2.14rc1 [November 20, 2006] + Changed "strtod" to "png_strtod" in pngrutil.c + +version 1.0.22, 1.2.14 [November 27, 2006] + Added missing "$(srcdir)" in Makefile.am and Makefile.in + +version 1.2.15beta1 [December 3, 2006] + Generated configure with autoconf-2.61 instead of 2.60 + Revised configure.ac to update libpng.pc and libpng-config. + +version 1.2.15beta2 [December 3, 2006] + Always export MMX asm functions, just stubs if not building pnggccrd.c + +version 1.2.15beta3 [December 4, 2006] + Add "png_bytep" typecast to profile while calculating length in pngwutil.c + +version 1.2.15beta4 [December 7, 2006] + Added scripts/CMakeLists.txt + Changed PNG_NO_ASSEMBLER_CODE to PNG_NO_MMX_CODE in scripts, like 1.4.0beta + +version 1.2.15beta5 [December 7, 2006] + Changed some instances of PNG_ASSEMBLER_* to PNG_MMX_* in pnggccrd.c + Revised scripts/CMakeLists.txt + +version 1.2.15beta6 [December 13, 2006] + Revised scripts/CMakeLists.txt and configure.ac + +version 1.2.15rc1 [December 18, 2006] + Revised scripts/CMakeLists.txt + +version 1.2.15rc2 [December 21, 2006] + Added conditional #undef jmpbuf in pngtest.c to undo #define in AIX headers. + Added scripts/makefile.nommx + +version 1.2.15rc3 [December 25, 2006] + Fixed shared library numbering error that was introduced in 1.2.15beta6. + +version 1.2.15rc4 [December 27, 2006] + Fixed handling of rgb_to_gray when png_ptr->color.gray isn't set. + +version 1.2.15rc5 [December 31, 2006] + Revised handling of rgb_to_gray. + +version 1.0.23, 1.2.15 [January 5, 2007] + Added some (unsigned long) typecasts in pngtest.c to avoid printing errors. + +version 1.2.16beta1 [January 6, 2007] + Fix bugs in makefile.nommx + +version 1.2.16beta2 [January 16, 2007] + Revised scripts/CMakeLists.txt + +version 1.0.24, 1.2.16 [January 31, 2007] + No changes. + +version 1.2.17beta1 [March 6, 2007] + Revised scripts/CMakeLists.txt to install both shared and static libraries. + Deleted a redundant line from pngset.c. + +version 1.2.17beta2 [April 26, 2007] + Relocated misplaced test for png_ptr == NULL in pngpread.c + Change "==" to "&" for testing PNG_RGB_TO_GRAY_ERR & PNG_RGB_TO_GRAY_WARN + flags. + Changed remaining instances of PNG_ASSEMBLER_* to PNG_MMX_* + Added pngerror() when write_IHDR fails in deflateInit2(). + Added "const" to some array declarations. + Mention examples of libpng usage in the libpng*.txt and libpng.3 documents. + +version 1.2.17rc1 [May 4, 2007] + No changes. + +version 1.2.17rc2 [May 8, 2007] + Moved several PNG_HAVE_* macros out of PNG_INTERNAL because applications + calling set_unknown_chunk_location() need them. + Changed transformation flag from PNG_EXPAND_tRNS to PNG_EXPAND in + png_set_expand_gray_1_2_4_to_8(). + Added png_ptr->unknown_chunk to hold working unknown chunk data, so it + can be free'ed in case of error. Revised unknown chunk handling in + pngrutil.c and pngpread.c to use this structure. + +version 1.2.17rc3 [May 8, 2007] + Revised symbol-handling in configure script. + +version 1.2.17rc4 [May 10, 2007] + Revised unknown chunk handling to avoid storing unknown critical chunks. + +version 1.0.25 [May 15, 2007] +version 1.2.17 [May 15, 2007] + Added "png_ptr->num_trans=0" before error return in png_handle_tRNS, + to eliminate a vulnerability (CVE-2007-2445, CERT VU#684664) + +version 1.0.26 [May 15, 2007] +version 1.2.18 [May 15, 2007] + Reverted the libpng-1.2.17rc3 change to symbol-handling in configure script + +version 1.2.19beta1 [May 18, 2007] + Changed "const static" to "static PNG_CONST" everywhere, mostly undoing + change of libpng-1.2.17beta2. Changed other "const" to "PNG_CONST" + Changed some handling of unused parameters, to avoid compiler warnings. + "if (unused == NULL) return;" becomes "unused = unused". + +version 1.2.19beta2 [May 18, 2007] + Only use the valid bits of tRNS value in png_do_expand() (Brian Cartier) + +version 1.2.19beta3 [May 19, 2007] + Add some "png_byte" typecasts in png_check_keyword() and write new_key + instead of key in zTXt chunk (Kevin Ryde). + +version 1.2.19beta4 [May 21, 2007] + Add png_snprintf() function and use it in place of sprint() for improved + defense against buffer overflows. + +version 1.2.19beta5 [May 21, 2007] + Fixed png_handle_tRNS() to only use the valid bits of tRNS value. + Changed handling of more unused parameters, to avoid compiler warnings. + Removed some PNG_CONST in pngwutil.c to avoid compiler warnings. + +version 1.2.19beta6 [May 22, 2007] + Added some #ifdef PNG_MMX_CODE_SUPPORTED where needed in pngvcrd.c + Added a special "_MSC_VER" case that defines png_snprintf to _snprintf + +version 1.2.19beta7 [May 22, 2007] + Squelched png_squelch_warnings() in pnggccrd.c and added an + #ifdef PNG_MMX_CODE_SUPPORTED block around the declarations that caused + the warnings that png_squelch_warnings was squelching. + +version 1.2.19beta8 [May 22, 2007] + Removed __MMX__ from test in pngconf.h. + +version 1.2.19beta9 [May 23, 2007] + Made png_squelch_warnings() available via PNG_SQUELCH_WARNINGS macro. + Revised png_squelch_warnings() so it might work. + Updated makefile.sgcc and makefile.solaris; added makefile.solaris-x86. + +version 1.2.19beta10 [May 24, 2007] + Resquelched png_squelch_warnings(), use "__attribute__((used))" instead. + +version 1.2.19beta11 [May 28, 2007] + Return 0 from png_get_sPLT() and png_get_unknown_chunks() if png_ptr is NULL; + changed three remaining instances of png_strcpy() to png_strncpy() (David + Hill). + Make test for NULL row_buf at the beginning of png_do_read_transformations + unconditional. + +version 1.2.19beta12 [May 28, 2007] + Revised pnggccrd.c. + +version 1.2.19beta13 [June 14, 2007] + Prefer PNG_USE_PNGVCRD when _MSC_VER is defined in pngconf.h + +version 1.2.19beta14 [June 16, 2007] + Fix bug with handling of 16-bit transparency, introduced in 1.2.19beta2 + +version 1.2.19beta15 [June 17, 2007] + Revised pnggccrd.c. + +version 1.2.19beta16 [June 18, 2007] + Revised pnggccrd.c again. + Updated contrib/gregbook. + Changed '#include "pnggccrd.c"' to 'include "$srcdir/pnggccrd.c"' + in configure.ac + +version 1.2.19beta17 [June 19, 2007] + Revised many of the makefiles, to set -DPNG_NO_MMX_CODE where needed + and to not use -O3 unless -DPNG_NO_MMX_CODE is also set. + +version 1.2.19beta18 [June 23, 2007] + Replaced some C++ style comments with C style comments in pnggccrd.c. + Copied optimized C code from pnggccrd.c to pngrutil.c, removed dependency + on pnggccrd.o from many makefiles. + Added sl and dylib to list of extensions be installed by Makefile.am + +version 1.2.19beta19 [June 28, 2007] + Fixed testing PNG_RGB_TO_GRAY_ERR & PNG_RGB_TO_GRAY_WARN in pngrtran.c + More cleanup of pnggccrd.c and pngvcrd.c + +version 1.2.19beta20 [June 29, 2007] + Rebuilt Makefile.in and configure using libtool-1.5.24. + Fixed typo in pnggccrd.c + +version 1.2.19beta21 [June 30, 2007] + More revision of pnggccrd.c + Added "test" target to Makefile.in and Makefile.am + +version 1.2.19beta22 [July 3, 2007] + Added info about pngrutil/pnggccrd/pngvcrd to png_get_header_version() + Fix type definition of dummy_value_a, b in pnggccrd.c + +version 1.2.19beta23 [July 10, 2007] + Revert change to type definition of dummy_value_a, b in pnggccrd.c + Make sure __PIC__ is defined in pnggccrd.c when PIC is defined. + Require gcc-4.1 or better to use PNG_HAVE_MMX_FILTER_ROW on x86_64 platforms + +version 1.2.19beta24 [July 14, 2007] + Added PNG_NO_READ_FILTER, PNG_NO_WRITE_FILTER, PNG_NO_WARNING macros. + Added contrib/pngminim to demonstrate building minimal encoder and decoder + +version 1.2.19beta25 [July 15, 2007] + Removed the new PNG_NO_READ_FILTER macro since it would make the library + unable to read valid PNG files, and filtering is at the heart of the + PNG format. + +version 1.2.19beta26 [July 16, 2007] + Changed "png_free(str)" to "png_free(png_ptr,str)" in pngrutil.c WinCE + code (Yves Piguet). This bug was introduced in libpng-1.2.14. + Updated scripts/CMakeLists.txt + Relocated a misplaced #endif in pnggccrd.c + +version 1.2.19beta27 [July 17, 2007] + Fixed incorrect stride and number of bytes copied (was 4 instead of + 6 bytes) in the cleanup loop of pnggccrd.c and pngvcrd.c for handling + the end of 48-bit interlaced rows (Glenn R-P). + +version 1.2.19beta28 [July 19, 2007] + Removed requirement for gcc-4.1 or better to use PNG_HAVE_MMX_FILTER_ROW + on x86_64 platforms + Added png_warning() in pngrutil.c for short iCCP, iTXt, sPLT, or zTXT chunks. + Revised pngtest.c so warnings are displayed regardless of PNG_NO_STDIO. + +version 1.2.19beta29 [July 20, 2007] + Fix typo in pnggccrd.c (%%eax should be %%ax in secondloop48) + +version 1.2.19beta30 [July 26, 2007] + Revised pnggccrd.c + +version 1.2.19beta31 [July 27, 2007] + Fix typos in pnggccrd.c + +version 1.0.27rc1 and 1.2.19rc1 [July 31, 2007] + Disable PNG_MMX_CODE_SUPPORTED when PNG_ASSEMBLER_CODE_SUPPORTED is off. + Enable PNG_MMX_READ_FILTER_* by default, except when gcc-3.x is being + used (they were inadvertently disabled in libpng-1.2.19beta23). + Fix some debugging statements in pnggccrd.c and pngrutil.c + Added information about disabling the MMX code in libpng documentation. + +version 1.0.27rc2 and 1.2.19rc2 [August 4, 2007] + Removed some "#if 0" blocks. + Made a global struct local in pngvcrd.c to make it thread safe. + Issue a png_error() if application attempts to transform a row tht + has not been initialized. + +version 1.0.27rc3 and 1.2.19rc3 [August 9, 2007] + Slightly revised pngvcrd.c + +version 1.0.27rc4 and 1.2.19rc4 [August 9, 2007] + Revised pnggccrd.c debugging change of rc1, which was broken. + Revised scripts/CMakeLists.txt + Change default to PNG_NO_GLOBAL_ARRAYS for MSVC. + Turn off PNG_FLAG_ROW_INIT flag when setting transforms that expand pixels. + +version 1.0.27rc5 and 1.2.19rc5 [August 10, 2007] + Fix typo (missing '"') in pnggccrd.c + Revise handling of png_strtod in recent versions of WINCE + +version 1.0.27rc6 and 1.2.19rc6 [August 15, 2007] + Fix typo (missing ',') in contrib/gregbook/readpng2.c + Undid row initialization error exit added to rc2 and rc4. + +version 1.0.27 and 1.2.19 [August 18, 2007] + Conditionally restored row initialization error exit. + +version 1.2.20beta01 [August 19, 2007] + Fixed problem with compiling pnggccrd.c on Intel-Apple platforms. + Changed png_malloc() to png_malloc_warn() in png_set_sPLT(). + Added PNG_NO_ERROR_TEXT feature, with demo in contrib/pngminim + Removed define PNG_WARN_UNINITIALIZED_ROW 1 /* 0: warning; 1: error */ + because it caused some trouble. + +version 1.2.20beta02 [August 20, 2007] + Avoid compiling pnggccrd.c on Intel-Apple platforms. + +version 1.2.20beta03 [August 20, 2007] + Added "/D PNG_NO_MMX_CODE" to the non-mmx builds of projects/visualc6 + and visualc71. + +version 1.2.20beta04 [August 21, 2007] + Revised pngvcrd.c for improved efficiency (Steve Snyder). + +version 1.2.20rc1 [August 23, 2007] + Revised pngconf.h to set PNG_NO_MMX_CODE for gcc-3.x compilers. + +version 1.2.20rc2 [August 27, 2007] + Revised scripts/CMakeLists.txt + Revised #ifdefs to ensure one and only one of pnggccrd.c, pngvcrd.c, + or part of pngrutil.c is selected. + +version 1.2.20rc3 [August 30, 2007] + Remove a little more code in pngwutil.c when PNG_NO_WRITE_FILTER is selected. + Added /D _CRT_SECURE_NO_WARNINGS to visual6c and visualc71 projects. + Compile png_mmx_support() in png.c even when PNG_NO_MMX_CODE is defined. + Restored a "superfluous" #ifdef that was removed from 1.2.20rc2 pnggccrd.c, + breaking the png_mmx_support() function. + +version 1.2.20rc4 [September 1, 2007] + Removed Intel contributions (MMX, Optimized C). + +version 1.2.20rc5 [September 2, 2007] + Restored configure and Makefile.in to rc3 and put a snippet of code in + pnggccrd.c, to ensure configure makes the same PNG_NO_MMX_CODE selection + +version 1.2.20rc6 [September 2, 2007] + Fixed bugs in scripts/CMakeLists.txt + Removed pngvcrd.c references from msvc projects. + +version 1.0.28 and 1.2.20 [September 8, 2007] + Removed "(NO READ SUPPORT)" from png_get_header_version() string. + +version 1.2.21beta1 [September 14, 2007] + Fixed various mistakes reported by George Cook and Jeff Phillips: + logical vs bitwise NOT in pngrtran.c, bug introduced in 1.2.19rc2 + 16-bit cheap transparency expansion, bug introduced in 1.2.19beta2 + errors with sizeof(unknown_chunk.name), bugs introduced in 1.2.19beta11 + <= compare with unsigned var in pngset.c, should be ==. + +version 1.2.21beta2 [September 18, 2007] + Removed some extraneous typecasts. + +version 1.2.21rc1 [September 25, 2007] + Fixed potential out-of-bounds reads in png_handle_pCAL() and + png_handle_ztXt() ("flayer" results reported by Tavis Ormandy). + +version 1.2.21rc2 [September 26, 2007] + Fixed potential out-of-bounds reads in png_handle_sCAL(), + png_handle_iTXt(), and png_push_read_tEXt(). + Remove some PNG_CONST declarations from pngwutil.c to avoid compiler warnings + Revised makefiles to update paths in libpng.pc properly. + +version 1.2.21rc3 [September 27, 2007] + Revised makefiles to update "Libs" in libpng.pc properly. + +version 1.0.29 and 1.2.21rc3 [October 4, 2007] + No changes. + +version 1.2.22beta1 [October 4, 2007] + Again, fixed logical vs bitwise NOT in pngrtran.c, bug introduced + in 1.2.19rc2 + +version 1.2.22beta2 [October 5, 2007] + Fixed string length error in pngset.c (caused crashes while decoding iCCP) + Add terminating NULL after each instance of png_strncpy(). + +version 1.2.22beta3 [October 6, 2007] + Fix two off-by-one terminating NULL after png_strncpy(). + +version 1.2.22beta4 [October 7, 2007] + Changed some 0 to '\0'. + +version 1.0.30rc1 and 1.2.22rc1 [October 8, 2007] + No changes. + +version 1.0.30 and 1.2.22 [October 13, 2007] + No changes. + +version 1.2.23beta01 [October 15, 2007] + Reduced number of invocations of png_strlen() in pngset.c. + Changed [azAZ09_] to [_abcde...89] in Makefile.am for better localization. + +version 1.2.23beta02 [October 16, 2007] + Eliminated png_strncpy() and png_strcpy() (Pierre Poissinger) + Changed $AN to $(AN) in Makefile.am. + +version 1.2.23beta03 [October 16, 2007] + Fixed off-by-one error in pngset.c + Restore statement to set last character of buffer to \0 in pngerror.c + +version 1.2.23beta04 [October 23, 2007] + Reject attempt to set all-zero cHRM values. + +version 1.2.23beta05 [October 26, 2007] + Add missing quotes in projects/visualc6, lost in version 1.2.20rc3 + +version 1.2.23rc01 [November 2, 2007] + No changes. + +version 1.2.23 [November 6, 2007] + No changes. + +version 1.2.24beta01 [November 19, 2007] + Moved misplaced test for malloc failure in png_set_sPLT(). This bug was + introduced in libpng-1.2.20beta01. + Ifdef out avg_row etc from png.h and pngwrite.c when PNG_NO_WRITE_FILTER + Do not use png_ptr->free_fn and png_ptr->mem_fn in png_destroy_read_struct() + when png_ptr is NULL (Marshall Clow). + Updated handling of symbol prefixes in Makefile.am and configure.ac (Mike + Frysinger). + +version 1.2.24beta02 [November 30, 2007] + Removed a useless test and fixed incorrect test in png_set_cHRM_fixed() + (David Hill). + +version 1.2.24rc01 [December 7, 2007] + No changes. + +version 1.2.24 [December 14, 2007] + Make sure not to redefine _BSD_SOURCE in pngconf.h + Revised gather.sh and makefile.std in contrib/pngminim to avoid compiling + unused files. + +Send comments/corrections/commendations to png-mng-implement at lists.sf.net +(subscription required; visit +https://lists.sourceforge.net/lists/listinfo/png-mng-implement +to subscribe) +or to glennrp at users.sourceforge.net + +Glenn R-P diff --git a/Engine/lib/lpng/INSTALL b/Engine/lib/lpng/INSTALL new file mode 100644 index 000000000..78c9e0293 --- /dev/null +++ b/Engine/lib/lpng/INSTALL @@ -0,0 +1,199 @@ + +Installing libpng version 1.2.24 - December 14, 2007 + +On Unix/Linux and similar systems, you can simply type + + ./configure [--prefix=/path] + make check + make install + +and ignore the rest of this document. + +If configure does not work on your system and you have a reasonably +up-to-date set of tools, running ./autogen.sh before running ./configure +may fix the problem. You can also run the individual commands in +autogen.sh with the --force option, if supported by your version of +the tools. If you run 'libtoolize --force', though, this will replace +the distributed, patched, version of ltmain.sh with an unpatched version +and your shared library builds may fail to produce libraries with the +correct version numbers. + +Instead, you can use one of the custom-built makefiles in the +"scripts" directory + + cp scripts/makefile.system makefile + make test + make install + +Or you can use one of the "projects" in the "projects" directory. + +If you want to use "cmake" (see www.cmake.org), copy CMakeLists.txt +from the "scripts" directory to this directory and type + + cmake . [-DPNG_MMX=YES] -DCMAKE_INSTALL_PREFIX=/path + make + make install + +Before installing libpng, you must first install zlib, if it +is not already on your system. zlib can usually be found +wherever you got libpng. zlib can be placed in another directory, +at the same level as libpng. + +If your system already has a preinstalled zlib you will still need +to have access to the zlib.h and zconf.h include files that +correspond to the version of zlib that's installed. + +You can rename the directories that you downloaded (they +might be called "libpng-1.2.24" or "lpng109" and "zlib-1.2.1" +or "zlib121") so that you have directories called "zlib" and "libpng". + +Your directory structure should look like this: + + .. (the parent directory) + libpng (this directory) + INSTALL (this file) + README + *.h + *.c + contrib + gregbook + pngminus + pngsuite + visupng + projects + beos + c5builder (Borland) + visualc6 (msvc) + netware.txt + wince.txt + scripts + makefile.* + pngtest.png + etc. + zlib + README + *.h + *.c + contrib + etc. + +If the line endings in the files look funny, you may wish to get the other +distribution of libpng. It is available in both tar.gz (UNIX style line +endings) and zip (DOS style line endings) formats. + +If you are building libpng with MSVC, you can enter the +libpng projects\visualc6 directory and follow the instructions in +projects\visualc6\README.txt. + +You can build libpng for WindowsCE by downloading and installing +the projects\wince directory as instructed in the projects\wince.txt file, and +then following the instructions in the README* files. Similarly, you can +build libpng for Netware or Beos as instructed in projects\netware.txt +or projects\beos. + +Else enter the zlib directory and follow the instructions in zlib/README, +then come back here and run "configure" or choose the appropriate +makefile.sys in the scripts directory. + +The files that are presently available in the scripts directory +include + + CMakeLists.txt => "cmake" script + makefile.std => Generic UNIX makefile (cc, creates static libpng.a) + makefile.elf => Linux/ELF makefile symbol versioning, + gcc, creates libpng12.so.0.1.2.24) + makefile.linux => Linux/ELF makefile + (gcc, creates libpng12.so.0.1.2.24) + makefile.gcc => Generic makefile (gcc, creates static libpng.a) + makefile.knr => Archaic UNIX Makefile that converts files with + ansi2knr (Requires ansi2knr.c from + ftp://ftp.cs.wisc.edu/ghost) + makefile.aix => AIX/gcc makefile + makefile.cygwin => Cygwin/gcc makefile + makefile.darwin => Darwin makefile, can use on MacosX + makefile.dec => DEC Alpha UNIX makefile + makefile.freebsd => FreeBSD makefile + makefile.hpgcc => HPUX makefile using gcc + makefile.hpux => HPUX (10.20 and 11.00) makefile + makefile.hp64 => HPUX (10.20 and 11.00) makefile, 64-bit + makefile.ibmc => IBM C/C++ version 3.x for Win32 and OS/2 (static) + makefile.intel => Intel C/C++ version 4.0 and later + libpng.icc => Project file for IBM VisualAge/C++ version 4.0 or later + makefile.netbsd => NetBSD/cc makefile, uses PNGGCCRD, makes libpng.so. + makefile.ne12bsd => NetBSD/cc makefile, uses PNGGCCRD, + makes libpng12.so + makefile.openbsd => OpenBSD makefile + makefile.sgi => Silicon Graphics IRIX makefile (cc, creates static lib) + makefile.sggcc => Silicon Graphics (gcc, + creates libpng12.so.0.1.2.24) + makefile.sunos => Sun makefile + makefile.solaris => Solaris 2.X makefile (gcc, + creates libpng12.so.0.1.2.24) + makefile.solaris-x86 => Solaris/intelMMX 2.X makefile (gcc, + creates libpng12.so.0.1.2.24) + makefile.so9 => Solaris 9 makefile (gcc, + creates libpng12.so.0.1.2.24) + makefile.32sunu => Sun Ultra 32-bit makefile + makefile.64sunu => Sun Ultra 64-bit makefile + makefile.sco => For SCO OSr5 ELF and Unixware 7 with Native cc + makefile.mips => MIPS makefile + makefile.acorn => Acorn makefile + makefile.amiga => Amiga makefile + smakefile.ppc => AMIGA smakefile for SAS C V6.58/7.00 PPC compiler + (Requires SCOPTIONS, copied from scripts/SCOPTIONS.ppc) + makefile.atari => Atari makefile + makefile.beos => BEOS makefile for X86 + makefile.bor => Borland makefile (uses bcc) + makefile.bc32 => 32-bit Borland C++ (all modules compiled in C mode) + makefile.tc3 => Turbo C 3.0 makefile + makefile.dj2 => DJGPP 2 makefile + makefile.msc => Microsoft C makefile + makefile.vcwin32 => makefile for Microsoft Visual C++ 4.0 and later + makefile.os2 => OS/2 Makefile (gcc and emx, requires pngos2.def) + pngos2.def => OS/2 module definition file used by makefile.os2 + makefile.watcom => Watcom 10a+ Makefile, 32-bit flat memory model + makevms.com => VMS build script + descrip.mms => VMS makefile for MMS or MMK + SCOPTIONS.ppc => Used with smakefile.ppc + +Copy the file (or files) that you need from the +scripts directory into this directory, for example + + MSDOS example: copy scripts\makefile.msc makefile + UNIX example: cp scripts/makefile.std makefile + +Read the makefile to see if you need to change any source or +target directories to match your preferences. + +Then read pngconf.h to see if you want to make any configuration +changes. + +Then just run "make" which will create the libpng library in +this directory and "make test" which will run a quick test that reads +the "pngtest.png" file and writes a "pngout.png" file that should be +identical to it. Look for "9782 zero samples" in the output of the +test. For more confidence, you can run another test by typing +"pngtest pngnow.png" and looking for "289 zero samples" in the output. +Also, you can run "pngtest -m contrib/pngsuite/*.png" and compare +your output with the result shown in contrib/pngsuite/README. + +Most of the makefiles will allow you to run "make install" to +put the library in its final resting place (if you want to +do that, run "make install" in the zlib directory first if necessary). +Some also allow you to run "make test-installed" after you have +run "make install". + +If you encounter a compiler error message complaining about the +lines + __png.h__ already includes setjmp.h; + __dont__ include it again.; +This means you have compiled another module that includes setjmp.h, +which is hazardous because the two modules might not include exactly +the same setjmp.h. If you are sure that you know what you are doing +and that they are exactly the same, then you can comment out or +delete the two lines. Better yet, use the cexcept interface +instead, as demonstrated in contrib/visupng of the libpng distribution. + +Further information can be found in the README and libpng.txt +files, in the individual makefiles, in png.h, and the manual pages +libpng.3 and png.5. diff --git a/Engine/lib/lpng/KNOWNBUG b/Engine/lib/lpng/KNOWNBUG new file mode 100644 index 000000000..d39dd1ec3 --- /dev/null +++ b/Engine/lib/lpng/KNOWNBUG @@ -0,0 +1,22 @@ + +Known bugs in libpng version 1.2.24 + +1. February 23, 2006: The custom makefiles don't build libpng with -lz. + + STATUS: This is a subject of debate. The change will probably be made + as a part of a major overhaul of the makefiles in libpng version 1.4.0. + +2. February 24, 2006: The Makefile generated by the "configure" script + fails to install symbolic links + libpng12.so => libpng12.so.0.1.2.9betaN + that are generated by the custom makefiles. + +3. September 4, 2007: There is a report that pngtest crashes on MacOS 10. + + STATUS: workarounds are + 1) Compile without optimization (crashes are observed with + -arch i386 and -O2 or -O3, using gcc-4.0.1). + 2) Compile pngtest.c with PNG_DEBUG defined (the bug goes away if + you try to look at it). + 3) Ignore the crash. The library itself seems to be OK. + diff --git a/Engine/lib/lpng/LICENSE b/Engine/lib/lpng/LICENSE new file mode 100644 index 000000000..6b81b55a6 --- /dev/null +++ b/Engine/lib/lpng/LICENSE @@ -0,0 +1,109 @@ + +This copy of the libpng notices is provided for your convenience. In case of +any discrepancy between this copy and the notices in the file png.h that is +included in the libpng distribution, the latter shall prevail. + +COPYRIGHT NOTICE, DISCLAIMER, and LICENSE: + +If you modify libpng you may insert additional notices immediately following +this sentence. + +libpng versions 1.2.6, August 15, 2004, through 1.2.24, December 14, 2007, are +Copyright (c) 2004, 2006-2007 Glenn Randers-Pehrson, and are +distributed according to the same disclaimer and license as libpng-1.2.5 +with the following individual added to the list of Contributing Authors + + Cosmin Truta + +libpng versions 1.0.7, July 1, 2000, through 1.2.5 - October 3, 2002, are +Copyright (c) 2000-2002 Glenn Randers-Pehrson, and are +distributed according to the same disclaimer and license as libpng-1.0.6 +with the following individuals added to the list of Contributing Authors + + Simon-Pierre Cadieux + Eric S. Raymond + Gilles Vollant + +and with the following additions to the disclaimer: + + There is no warranty against interference with your enjoyment of the + library or against infringement. There is no warranty that our + efforts or the library will fulfill any of your particular purposes + or needs. This library is provided with all faults, and the entire + risk of satisfactory quality, performance, accuracy, and effort is with + the user. + +libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are +Copyright (c) 1998, 1999 Glenn Randers-Pehrson, and are +distributed according to the same disclaimer and license as libpng-0.96, +with the following individuals added to the list of Contributing Authors: + + Tom Lane + Glenn Randers-Pehrson + Willem van Schaik + +libpng versions 0.89, June 1996, through 0.96, May 1997, are +Copyright (c) 1996, 1997 Andreas Dilger +Distributed according to the same disclaimer and license as libpng-0.88, +with the following individuals added to the list of Contributing Authors: + + John Bowler + Kevin Bracey + Sam Bushell + Magnus Holmgren + Greg Roelofs + Tom Tanner + +libpng versions 0.5, May 1995, through 0.88, January 1996, are +Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc. + +For the purposes of this copyright and license, "Contributing Authors" +is defined as the following set of individuals: + + Andreas Dilger + Dave Martindale + Guy Eric Schalnat + Paul Schmidt + Tim Wegner + +The PNG Reference Library is supplied "AS IS". The Contributing Authors +and Group 42, Inc. disclaim all warranties, expressed or implied, +including, without limitation, the warranties of merchantability and of +fitness for any purpose. The Contributing Authors and Group 42, Inc. +assume no liability for direct, indirect, incidental, special, exemplary, +or consequential damages, which may result from the use of the PNG +Reference Library, even if advised of the possibility of such damage. + +Permission is hereby granted to use, copy, modify, and distribute this +source code, or portions hereof, for any purpose, without fee, subject +to the following restrictions: + +1. The origin of this source code must not be misrepresented. + +2. Altered versions must be plainly marked as such and must not + be misrepresented as being the original source. + +3. This Copyright notice may not be removed or altered from any + source or altered source distribution. + +The Contributing Authors and Group 42, Inc. specifically permit, without +fee, and encourage the use of this source code as a component to +supporting the PNG file format in commercial products. If you use this +source code in a product, acknowledgment is not required but would be +appreciated. + + +A "png_get_copyright" function is available, for convenient use in "about" +boxes and the like: + + printf("%s",png_get_copyright(NULL)); + +Also, the PNG logo (in PNG format, of course) is supplied in the +files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31). + +Libpng is OSI Certified Open Source Software. OSI Certified Open Source is a +certification mark of the Open Source Initiative. + +Glenn Randers-Pehrson +glennrp at users.sourceforge.net +December 14, 2007 diff --git a/Engine/lib/lpng/README b/Engine/lib/lpng/README new file mode 100644 index 000000000..6f84c69f0 --- /dev/null +++ b/Engine/lib/lpng/README @@ -0,0 +1,263 @@ +README for libpng version 1.2.24 - December 14, 2007 (shared library 12.0) +See the note about version numbers near the top of png.h + +See INSTALL for instructions on how to install libpng. + +Libpng comes in several distribution formats. Get libpng-*.tar.gz +or libpng-*.tar.bz2 if you want UNIX-style line endings in the text +files, or lpng*.zip if you want DOS-style line endings. + +Version 0.89 was the first official release of libpng. Don't let the +fact that it's the first release fool you. The libpng library has been in +extensive use and testing since mid-1995. By late 1997 it had +finally gotten to the stage where there hadn't been significant +changes to the API in some time, and people have a bad feeling about +libraries with versions < 1.0. Version 1.0.0 was released in +March 1998. + +**** +Note that some of the changes to the png_info structure render this +version of the library binary incompatible with libpng-0.89 or +earlier versions if you are using a shared library. The type of the +"filler" parameter for png_set_filler() has changed from png_byte to +png_uint_32, which will affect shared-library applications that use +this function. + +To avoid problems with changes to the internals of png_info_struct, +new APIs have been made available in 0.95 to avoid direct application +access to info_ptr. These functions are the png_set_ and +png_get_ functions. These functions should be used when +accessing/storing the info_struct data, rather than manipulating it +directly, to avoid such problems in the future. + +It is important to note that the APIs do not make current programs +that access the info struct directly incompatible with the new +library. However, it is strongly suggested that new programs use +the new APIs (as shown in example.c and pngtest.c), and older programs +be converted to the new format, to facilitate upgrades in the future. +**** + +Additions since 0.90 include the ability to compile libpng as a +Windows DLL, and new APIs for accessing data in the info struct. +Experimental functions include the ability to set weighting and cost +factors for row filter selection, direct reads of integers from buffers +on big-endian processors that support misaligned data access, faster +methods of doing alpha composition, and more accurate 16->8 bit color +conversion. + +The additions since 0.89 include the ability to read from a PNG stream +which has had some (or all) of the signature bytes read by the calling +application. This also allows the reading of embedded PNG streams that +do not have the PNG file signature. As well, it is now possible to set +the library action on the detection of chunk CRC errors. It is possible +to set different actions based on whether the CRC error occurred in a +critical or an ancillary chunk. + +The changes made to the library, and bugs fixed are based on discussions +on the PNG-implement mailing list +and not on material submitted privately to Guy, Andreas, or Glenn. They will +forward any good suggestions to the list. + +For a detailed description on using libpng, read libpng.txt. For +examples of libpng in a program, see example.c and pngtest.c. For usage +information and restrictions (what little they are) on libpng, see +png.h. For a description on using zlib (the compression library used by +libpng) and zlib's restrictions, see zlib.h + +I have included a general makefile, as well as several machine and +compiler specific ones, but you may have to modify one for your own needs. + +You should use zlib 1.0.4 or later to run this, but it MAY work with +versions as old as zlib 0.95. Even so, there are bugs in older zlib +versions which can cause the output of invalid compression streams for +some images. You will definitely need zlib 1.0.4 or later if you are +taking advantage of the MS-DOS "far" structure allocation for the small +and medium memory models. You should also note that zlib is a +compression library that is useful for more things than just PNG files. +You can use zlib as a drop-in replacement for fread() and fwrite() if +you are so inclined. + +zlib should be available at the same place that libpng is, or at. +ftp://ftp.info-zip.org/pub/infozip/zlib + +You may also want a copy of the PNG specification. It is available +as an RFC, a W3C Recommendation, and an ISO/IEC Standard. You can find +these at http://www.libpng.org/pub/png/documents/ + +This code is currently being archived at libpng.sf.net in the +[DOWNLOAD] area, and on CompuServe, Lib 20 (PNG SUPPORT) +at GO GRAPHSUP. If you can't find it in any of those places, +e-mail me, and I'll help you find it. + +If you have any code changes, requests, problems, etc., please e-mail +them to me. Also, I'd appreciate any make files or project files, +and any modifications you needed to make to get libpng to compile, +along with a #define variable to tell what compiler/system you are on. +If you needed to add transformations to libpng, or wish libpng would +provide the image in a different way, drop me a note (and code, if +possible), so I can consider supporting the transformation. +Finally, if you get any warning messages when compiling libpng +(note: not zlib), and they are easy to fix, I'd appreciate the +fix. Please mention "libpng" somewhere in the subject line. Thanks. + +This release was created and will be supported by myself (of course +based in a large way on Guy's and Andreas' earlier work), and the PNG group. + +Send comments/corrections/commendations to png-mng-implement at lists.sf.net +(subscription required; visit +https://lists.sourceforge.net/lists/listinfo/png-mng-implement +to subscribe) or to glennrp at users.sourceforge.net + +You can't reach Guy, the original libpng author, at the addresses +given in previous versions of this document. He and Andreas will read mail +addressed to the png-implement list, however. + +Please do not send general questions about PNG. Send them to +the (png-mng-misc at lists.sourceforge.net, subscription required, visit +https://lists.sourceforge.net/lists/listinfo/png-mng-implement to subscribe) +On the other hand, +please do not send libpng questions to that address, send them to me +or to the png-implement list. I'll +get them in the end anyway. If you have a question about something +in the PNG specification that is related to using libpng, send it +to me. Send me any questions that start with "I was using libpng, +and ...". If in doubt, send questions to me. I'll bounce them +to others, if necessary. + +Please do not send suggestions on how to change PNG. We have +been discussing PNG for twelve years now, and it is official and +finished. If you have suggestions for libpng, however, I'll +gladly listen. Even if your suggestion is not used immediately, +it may be used later. + +Files in this distribution: + + ANNOUNCE => Announcement of this version, with recent changes + CHANGES => Description of changes between libpng versions + KNOWNBUG => List of known bugs and deficiencies + LICENSE => License to use and redistribute libpng + README => This file + TODO => Things not implemented in the current library + Y2KINFO => Statement of Y2K compliance + example.c => Example code for using libpng functions + libpng-*-*-diff.txt => Diff from previous release + libpng.3 => manual page for libpng (includes libpng.txt) + libpng.txt => Description of libpng and its functions + libpngpf.3 => manual page for libpng's private functions + png.5 => manual page for the PNG format + png.c => Basic interface functions common to library + png.h => Library function and interface declarations + pngconf.h => System specific library configuration + pngerror.c => Error/warning message I/O functions + pngget.c => Functions for retrieving info from struct + pngmem.c => Memory handling functions + pngbar.png => PNG logo, 88x31 + pngnow.png => PNG logo, 98x31 + pngpread.c => Progressive reading functions + pngread.c => Read data/helper high-level functions + pngrio.c => Lowest-level data read I/O functions + pngrtran.c => Read data transformation functions + pngrutil.c => Read data utility functions + pngset.c => Functions for storing data into the info_struct + pngtest.c => Library test program + pngtest.png => Library test sample image + pngtrans.c => Common data transformation functions + pngwio.c => Lowest-level write I/O functions + pngwrite.c => High-level write functions + pngwtran.c => Write data transformations + pngwutil.c => Write utility functions + contrib => Contributions + gregbook => source code for PNG reading and writing, from + Greg Roelofs' "PNG: The Definitive Guide", + O'Reilly, 1999 + msvctest => Builds and runs pngtest using a MSVC workspace + pngminus => Simple pnm2png and png2pnm programs + pngsuite => Test images + visupng => Contains a MSVC workspace for VisualPng + projects => Contains project files and workspaces for building DLL + beos => Contains a Beos workspace for building libpng + c5builder => Contains a Borland workspace for building libpng + and zlib + visualc6 => Contains a Microsoft Visual C++ (MSVC) workspace + for building libpng and zlib + netware.txt => Contains instructions for downloading a set of + project files for building libpng and zlib on + Netware. + wince.txt => Contains instructions for downloading a Microsoft + Visual C++ (Windows CD Toolkit) workspace for + building libpng and zlib on WindowsCE + scripts => Directory containing scripts for building libpng: + descrip.mms => VMS makefile for MMS or MMK + makefile.std => Generic UNIX makefile (cc, creates static libpng.a) + makefile.elf => Linux/ELF makefile symbol versioning, + gcc, creates libpng12.so.0.1.2.24) + makefile.linux => Linux/ELF makefile + (gcc, creates libpng12.so.0.1.2.24) + makefile.gcmmx => Linux/ELF makefile + (gcc, creates libpng12.so.0.1.2.24, + uses assembler code tuned for Intel MMX platform) + makefile.gcc => Generic makefile (gcc, creates static libpng.a) + makefile.knr => Archaic UNIX Makefile that converts files with + ansi2knr (Requires ansi2knr.c from + ftp://ftp.cs.wisc.edu/ghost) + makefile.aix => AIX makefile + makefile.cygwin => Cygwin/gcc makefile + makefile.darwin => Darwin makefile + makefile.dec => DEC Alpha UNIX makefile + makefile.freebsd => FreeBSD makefile + makefile.hpgcc => HPUX makefile using gcc + makefile.hpux => HPUX (10.20 and 11.00) makefile + makefile.hp64 => HPUX (10.20 and 11.00) makefile, 64 bit + makefile.ibmc => IBM C/C++ version 3.x for Win32 and OS/2 (static) + makefile.intel => Intel C/C++ version 4.0 and later + libpng.icc => Project file, IBM VisualAge/C++ 4.0 or later + makefile.netbsd => NetBSD/cc makefile, PNGGCCRD, makes libpng.so. + makefile.ne12bsd => NetBSD/cc makefile, PNGGCCRD, makes libpng12.so + makefile.openbsd => OpenBSD makefile + makefile.sgi => Silicon Graphics IRIX (cc, creates static lib) + makefile.sggcc => Silicon Graphics + (gcc, creates libpng12.so.0.1.2.24) + makefile.sunos => Sun makefile + makefile.solaris => Solaris 2.X makefile + (gcc, creates libpng12.so.0.1.2.24) + makefile.so9 => Solaris 9 makefile + (gcc, creates libpng12.so.0.1.2.24) + makefile.32sunu => Sun Ultra 32-bit makefile + makefile.64sunu => Sun Ultra 64-bit makefile + makefile.sco => For SCO OSr5 ELF and Unixware 7 with Native cc + makefile.mips => MIPS makefile + makefile.acorn => Acorn makefile + makefile.amiga => Amiga makefile + smakefile.ppc => AMIGA smakefile for SAS C V6.58/7.00 PPC + compiler (Requires SCOPTIONS, copied from + scripts/SCOPTIONS.ppc) + makefile.atari => Atari makefile + makefile.beos => BEOS makefile for X86 + makefile.bor => Borland makefile (uses bcc) + makefile.bc32 => 32-bit Borland C++ (all modules compiled in C mode) + makefile.tc3 => Turbo C 3.0 makefile + makefile.dj2 => DJGPP 2 makefile + makefile.msc => Microsoft C makefile + makefile.vcawin32=> makefile for Microsoft Visual C++ 5.0 and + later (uses assembler code tuned for Intel MMX + platform) + makefile.vcwin32 => makefile for Microsoft Visual C++ 4.0 and + later (does not use assembler code) + makefile.os2 => OS/2 Makefile (gcc and emx, requires pngos2.def) + pngos2.def => OS/2 module definition file used by makefile.os2 + makefile.watcom => Watcom 10a+ Makefile, 32-bit flat memory model + makevms.com => VMS build script + SCOPTIONS.ppc => Used with smakefile.ppc + +Good luck, and happy coding. + +-Glenn Randers-Pehrson (current maintainer) + Internet: glennrp at users.sourceforge.net + +-Andreas Eric Dilger (former maintainer, 1996-1997) + Internet: adilger at enel.ucalgary.ca + Web: http://www-mddsp.enel.ucalgary.ca/People/adilger/ + +-Guy Eric Schalnat (original author and former maintainer, 1995-1996) + (formerly of Group 42, Inc) + Internet: gschal at infinet.com diff --git a/Engine/lib/lpng/TODO b/Engine/lib/lpng/TODO new file mode 100644 index 000000000..a5f639577 --- /dev/null +++ b/Engine/lib/lpng/TODO @@ -0,0 +1,24 @@ +TODO - list of things to do for libpng: + +Final bug fixes. +Improve API by hiding the png_struct and png_info structs. +Finish work on the no-floating-point version (including gamma compensation) +Better C++ wrapper/full C++ implementation? +Fix problem with C++ and EXTERN "C". +cHRM transformation. +Improve setjmp/longjmp usage or remove it in favor of returning error codes. +Add "grayscale->palette" transformation and "palette->grayscale" detection. +Improved dithering. +Multi-lingual error and warning message support. +Complete sRGB transformation (presently it simply uses gamma=0.45455). +Man pages for function calls. +Better documentation. +Better filter selection + (counting huffman bits/precompression? filter inertia? filter costs?). +Histogram creation. +Text conversion between different code pages (Latin-1 -> Mac and DOS). +Should we always malloc 2^bit_depth PLTE/tRNS/hIST entries for safety? +Build gamma tables using fixed point (and do away with floating point entirely). +Use greater precision when changing to linear gamma for compositing against + background and doing rgb-to-gray transformation. +Investigate pre-incremented loop counters and other loop constructions. diff --git a/Engine/lib/lpng/Y2KINFO b/Engine/lib/lpng/Y2KINFO new file mode 100644 index 000000000..829b511e4 --- /dev/null +++ b/Engine/lib/lpng/Y2KINFO @@ -0,0 +1,55 @@ + Y2K compliance in libpng: + ========================= + + December 14, 2007 + + Since the PNG Development group is an ad-hoc body, we can't make + an official declaration. + + This is your unofficial assurance that libpng from version 0.71 and + upward through 1.2.24 are Y2K compliant. It is my belief that earlier + versions were also Y2K compliant. + + Libpng only has three year fields. One is a 2-byte unsigned integer + that will hold years up to 65535. The other two hold the date in text + format, and will hold years up to 9999. + + The integer is + "png_uint_16 year" in png_time_struct. + + The strings are + "png_charp time_buffer" in png_struct and + "near_time_buffer", which is a local character string in png.c. + + There are seven time-related functions: + + png_convert_to_rfc_1123() in png.c + (formerly png_convert_to_rfc_1152() in error) + png_convert_from_struct_tm() in pngwrite.c, called in pngwrite.c + png_convert_from_time_t() in pngwrite.c + png_get_tIME() in pngget.c + png_handle_tIME() in pngrutil.c, called in pngread.c + png_set_tIME() in pngset.c + png_write_tIME() in pngwutil.c, called in pngwrite.c + + All appear to handle dates properly in a Y2K environment. The + png_convert_from_time_t() function calls gmtime() to convert from system + clock time, which returns (year - 1900), which we properly convert to + the full 4-digit year. There is a possibility that applications using + libpng are not passing 4-digit years into the png_convert_to_rfc_1123() + function, or that they are incorrectly passing only a 2-digit year + instead of "year - 1900" into the png_convert_from_struct_tm() function, + but this is not under our control. The libpng documentation has always + stated that it works with 4-digit years, and the APIs have been + documented as such. + + The tIME chunk itself is also Y2K compliant. It uses a 2-byte unsigned + integer to hold the year, and can hold years as large as 65535. + + zlib, upon which libpng depends, is also Y2K compliant. It contains + no date-related code. + + + Glenn Randers-Pehrson + libpng maintainer + PNG Development Group diff --git a/Engine/lib/lpng/configure b/Engine/lib/lpng/configure new file mode 100644 index 000000000..b6b8d7bbb --- /dev/null +++ b/Engine/lib/lpng/configure @@ -0,0 +1,13 @@ +#!/bin/sh +echo " + There is no \"configure\" script in this distribution of + libpng-1.2.24. + + Instead, please copy the appropriate makefile for your system from the + \"scripts\" directory. Read the INSTALL file for more details. + + Update, July 2004: you can get a \"configure\" based distribution + from the libpng distribution sites. Download the file + libpng-1.2.24.tar.gz or libpng-1.2.24.tar.bz2 +" + diff --git a/Engine/lib/lpng/contrib/gregbook/COPYING b/Engine/lib/lpng/contrib/gregbook/COPYING new file mode 100644 index 000000000..d60c31a97 --- /dev/null +++ b/Engine/lib/lpng/contrib/gregbook/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Engine/lib/lpng/contrib/gregbook/LICENSE b/Engine/lib/lpng/contrib/gregbook/LICENSE new file mode 100644 index 000000000..a39264e85 --- /dev/null +++ b/Engine/lib/lpng/contrib/gregbook/LICENSE @@ -0,0 +1,50 @@ + --------------------------------------------------------------------------- + + Copyright (c) 1998-2007 Greg Roelofs. All rights reserved. + + This software is provided "as is," without warranty of any kind, + express or implied. In no event shall the author or contributors + be held liable for any damages arising in any way from the use of + this software. + + The contents of this file are DUAL-LICENSED. You may modify and/or + redistribute this software according to the terms of one of the + following two licenses (at your option): + + + LICENSE 1 ("BSD-like with advertising clause"): + + 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. Redistributions of source code must retain the above copyright + notice, disclaimer, and this list of conditions. + 2. Redistributions in binary form must reproduce the above copyright + notice, disclaimer, and this list of conditions in the documenta- + tion and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + + This product includes software developed by Greg Roelofs + and contributors for the book, "PNG: The Definitive Guide," + published by O'Reilly and Associates. + + + LICENSE 2 (GNU GPL v2 or later): + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + --------------------------------------------------------------------------- diff --git a/Engine/lib/lpng/contrib/gregbook/Makefile.mingw32 b/Engine/lib/lpng/contrib/gregbook/Makefile.mingw32 new file mode 100644 index 000000000..e70a59aef --- /dev/null +++ b/Engine/lib/lpng/contrib/gregbook/Makefile.mingw32 @@ -0,0 +1,130 @@ +# Sample makefile for rpng-win / rpng2-win / wpng using mingw32-gcc and make. +# Greg Roelofs +# Last modified: 2 June 2007 +# +# The programs built by this makefile are described in the book, +# "PNG: The Definitive Guide," by Greg Roelofs (O'Reilly and +# Associates, 1999). Go buy a copy, eh? Well, OK, it's not +# generally for sale anymore, but it's the thought that counts, +# right? (Hint: http://www.libpng.org/pub/png/book/ ) +# +# Invoke this makefile from a DOS-prompt window via: +# +# make -f Makefile.mingw32 +# +# This makefile assumes libpng and zlib have already been built or downloaded +# and are in subdirectories at the same level as the current subdirectory +# (as indicated by the PNGDIR and ZDIR macros below). It makes no assumptions +# at all about the mingw32 installation tree (W32DIR). Edit as appropriate. +# +# Note that the names of the dynamic and static libpng and zlib libraries +# used below may change in later releases of the libraries. This makefile +# builds both statically and dynamically linked executables by default. +# (You need only one set, but for testing it can be handy to have both.) + + +# macros -------------------------------------------------------------------- + +#PNGDIR = ../..# for libpng-x.y.z/contrib/gregbook builds +PNGDIR = ../libpng-win32 +PNGINC = -I$(PNGDIR) +PNGLIBd = $(PNGDIR)/libpng.dll.a # dynamically linked +PNGLIBs = $(PNGDIR)/libpng.a # statically linked, local libpng + +#ZDIR = ../../../zlib-win32# for libpng-x.y.z/contrib/gregbook builds +ZDIR = ../zlib-win32 +ZINC = -I$(ZDIR) +ZLIBd = $(ZDIR)/libzdll.a +ZLIBs = $(ZDIR)/libz.a + +# change this to be the path where mingw32 installs its stuff: +W32DIR = +#W32DIR = /usr/local/cross-tools/i386-mingw32msvc +W32INC = -I$(W32DIR)/include +W32LIB = $(W32DIR)/lib/libuser32.a $(W32DIR)/lib/libgdi32.a + +CC = gcc +#CC = i386-mingw32msvc-gcc # e.g., Linux -> Win32 cross-compilation +LD = $(CC) +RM = rm -f +CFLAGS = -O -Wall $(INCS) $(MINGW_CCFLAGS) +# [note that -Wall is a gcc-specific compilation flag ("most warnings on")] +# [-ansi, -pedantic and -W can also be used] +LDFLAGS = $(MINGW_LDFLAGS) +O = .o +E = .exe + +INCS = $(PNGINC) $(ZINC) $(W32INC) +RLIBSd = $(PNGLIBd) $(ZLIBd) $(W32LIB) -lm +RLIBSs = $(PNGLIBs) $(ZLIBs) $(W32LIB) -lm +WLIBSd = $(PNGLIBd) $(ZLIBd) +WLIBSs = $(PNGLIBs) $(ZLIBs) + +RPNG = rpng-win +RPNG2 = rpng2-win +WPNG = wpng + +ROBJSd = $(RPNG)$(O) readpng.pic$(O) +ROBJS2d = $(RPNG2)$(O) readpng2.pic$(O) +WOBJSd = $(WPNG)$(O) writepng.pic$(O) + +RPNGs = $(RPNG)-static +RPNG2s = $(RPNG2)-static +WPNGs = $(WPNG)-static + +ROBJSs = $(RPNG)$(O) readpng$(O) +ROBJS2s = $(RPNG2)$(O) readpng2$(O) +WOBJSs = $(WPNG)$(O) writepng$(O) + +STATIC_EXES = $(RPNGs)$(E) $(RPNG2s)$(E) $(WPNGs)$(E) +DYNAMIC_EXES = $(RPNG)$(E) $(RPNG2)$(E) $(WPNG)$(E) + +EXES = $(STATIC_EXES) $(DYNAMIC_EXES) + + +# implicit make rules ------------------------------------------------------- + +.c$(O): + $(CC) -c $(CFLAGS) $< + +%.pic$(O): %.c + $(CC) -c $(CFLAGS) -DPNG_BUILD_DLL -o $@ $< + + +# dependencies -------------------------------------------------------------- + +all: $(EXES) + +$(RPNGs)$(E): $(ROBJSs) + $(LD) $(LDFLAGS) -o $@ $(ROBJSs) $(RLIBSs) + +$(RPNG)$(E): $(ROBJSd) + $(LD) $(LDFLAGS) -o $@ $(ROBJSd) $(RLIBSd) + +$(RPNG2s)$(E): $(ROBJS2s) + $(LD) $(LDFLAGS) -o $@ $(ROBJS2s) $(RLIBSs) + +$(RPNG2)$(E): $(ROBJS2d) + $(LD) $(LDFLAGS) -o $@ $(ROBJS2d) $(RLIBSd) + +$(WPNGs)$(E): $(WOBJSs) + $(LD) $(LDFLAGS) -o $@ $(WOBJSs) $(WLIBSs) + +$(WPNG)$(E): $(WOBJSd) + $(LD) $(LDFLAGS) -o $@ $(WOBJSd) $(WLIBSd) + +$(RPNG)$(O): $(RPNG).c readpng.h +$(RPNG2)$(O): $(RPNG2).c readpng2.h +$(WPNG)$(O): $(WPNG).c writepng.h + +readpng$(O) readpng.pic$(O): readpng.c readpng.h +readpng2$(O) readpng2.pic$(O): readpng2.c readpng2.h +writepng$(O) writepng.pic$(O): writepng.c writepng.h + + +# maintenance --------------------------------------------------------------- + +clean: + $(RM) $(EXES) + $(RM) $(ROBJSs) $(ROBJS2s) $(WOBJSs) + $(RM) $(ROBJSd) $(ROBJS2d) $(WOBJSd) diff --git a/Engine/lib/lpng/contrib/gregbook/Makefile.sgi b/Engine/lib/lpng/contrib/gregbook/Makefile.sgi new file mode 100644 index 000000000..e3ca6ce4c --- /dev/null +++ b/Engine/lib/lpng/contrib/gregbook/Makefile.sgi @@ -0,0 +1,104 @@ +# Sample makefile for rpng-x / rpng2-x / wpng for SGI using cc and make. +# Greg Roelofs +# Last modified: 7 March 2002 +# +# The programs built by this makefile are described in the book, +# "PNG: The Definitive Guide," by Greg Roelofs (O'Reilly and +# Associates, 1999). Go buy a copy, eh? Buy some for friends +# and family, too. (Not that this is a blatant plug or anything.) +# +# Invoke this makefile from a shell prompt in the usual way; for example: +# +# make -f Makefile.sgi +# +# This makefile assumes libpng and zlib have already been built or downloaded +# and are both installed in /usr/local/{include,lib} (as indicated by the +# PNG* and Z* macros below). Edit as appropriate--choose only ONE each of +# the PNGINC, PNGLIB, ZINC and ZLIB lines. +# +# This makefile builds dynamically linked executables (against libpng and zlib, +# that is), but that can be changed by uncommenting the appropriate PNGLIB and +# ZLIB lines. + + +# macros -------------------------------------------------------------------- + +PNGINC = -I/usr/local/include/libpng12 +PNGLIB = -L/usr/local/lib -lpng12 # dynamically linked against libpng +#PNGLIB = /usr/local/lib/libpng12.a # statically linked against libpng +# or: +#PNGINC = -I../.. +#PNGLIB = -L../.. -lpng +#PNGLIB = ../../libpng.a + +ZINC = -I/usr/local/include +ZLIB = -L/usr/local/lib -lz # dynamically linked against zlib +#ZLIB = /usr/local/lib/libz.a # statically linked against zlib +#ZINC = -I../zlib +#ZLIB = -L../zlib -lz +#ZLIB = ../../../zlib/libz.a + +XINC = -I/usr/include/X11 # old-style, stock X distributions +XLIB = -L/usr/lib/X11 -lX11 +#XINC = -I/usr/openwin/include # Sun workstations (OpenWindows) +#XLIB = -L/usr/openwin/lib -lX11 +#XINC = -I/usr/X11R6/include # new X distributions (XFree86, etc.) +#XLIB = -L/usr/X11R6/lib -lX11 + +INCS = $(PNGINC) $(ZINC) $(XINC) +RLIBS = $(PNGLIB) $(ZLIB) $(XLIB) -lm +WLIBS = $(PNGLIB) $(ZLIB) + +CC = cc +LD = cc +RM = rm -f +# ABI must be the same as that used to build libpng. +ABI= +CFLAGS = $(ABI) -O -fullwarn $(INCS) +LDFLAGS = $(ABI) +O = .o +E = + +RPNG = rpng-x +RPNG2 = rpng2-x +WPNG = wpng + +ROBJS = $(RPNG)$(O) readpng$(O) +ROBJS2 = $(RPNG2)$(O) readpng2$(O) +WOBJS = $(WPNG)$(O) writepng$(O) + +EXES = $(RPNG)$(E) $(RPNG2)$(E) $(WPNG)$(E) + + +# implicit make rules ------------------------------------------------------- + +.c$(O): + $(CC) -c $(CFLAGS) $< + + +# dependencies -------------------------------------------------------------- + +all: $(EXES) + +$(RPNG)$(E): $(ROBJS) + $(LD) $(LDFLAGS) -o $@ $(ROBJS) $(RLIBS) + +$(RPNG2)$(E): $(ROBJS2) + $(LD) $(LDFLAGS) -o $@ $(ROBJS2) $(RLIBS) + +$(WPNG)$(E): $(WOBJS) + $(LD) $(LDFLAGS) -o $@ $(WOBJS) $(WLIBS) + +$(RPNG)$(O): $(RPNG).c readpng.h +$(RPNG2)$(O): $(RPNG2).c readpng2.h +$(WPNG)$(O): $(WPNG).c writepng.h + +readpng$(O): readpng.c readpng.h +readpng2$(O): readpng2.c readpng2.h +writepng$(O): writepng.c writepng.h + + +# maintenance --------------------------------------------------------------- + +clean: + $(RM) $(EXES) $(ROBJS) $(ROBJS2) $(WOBJS) diff --git a/Engine/lib/lpng/contrib/gregbook/Makefile.unx b/Engine/lib/lpng/contrib/gregbook/Makefile.unx new file mode 100644 index 000000000..7ff65bfb7 --- /dev/null +++ b/Engine/lib/lpng/contrib/gregbook/Makefile.unx @@ -0,0 +1,132 @@ +# Sample makefile for rpng-x / rpng2-x / wpng using gcc and make. +# Greg Roelofs +# Last modified: 2 June 2007 +# +# The programs built by this makefile are described in the book, +# "PNG: The Definitive Guide," by Greg Roelofs (O'Reilly and +# Associates, 1999). Go buy a copy, eh? Well, OK, it's not +# generally for sale anymore, but it's the thought that counts, +# right? (Hint: http://www.libpng.org/pub/png/book/ ) +# +# Invoke this makefile from a shell prompt in the usual way; for example: +# +# make -f Makefile.unx +# +# This makefile assumes libpng and zlib have already been built or downloaded +# and are installed in /usr/local/{include,lib} or as otherwise indicated by +# the PNG* and Z* macros below. Edit as appropriate--choose only ONE each of +# the PNGINC, PNGLIBd, PNGLIBs, ZINC, ZLIBd and ZLIBs lines. +# +# This makefile builds both dynamically and statically linked executables +# (against libpng and zlib, that is), but that can be changed by modifying +# the "EXES =" line. (You need only one set, but for testing it can be handy +# to have both.) + + +# macros -------------------------------------------------------------------- + +#PNGDIR = /usr/local/lib +#PNGINC = -I/usr/local/include/libpng12 +#PNGLIBd = -L$(PNGDIR) -lpng12 # dynamically linked, installed libpng +#PNGLIBs = $(PNGDIR)/libpng12.a # statically linked, installed libpng +# or: +PNGDIR = ../..# this one is for libpng-x.y.z/contrib/gregbook builds +#PNGDIR = ../libpng +PNGINC = -I$(PNGDIR) +PNGLIBd = -Wl,-rpath,$(PNGDIR) -L$(PNGDIR) -lpng12 # dynamically linked +PNGLIBs = $(PNGDIR)/libpng.a # statically linked, local libpng + +ZDIR = /usr/local/lib +#ZDIR = /usr/lib64 +ZINC = -I/usr/local/include +ZLIBd = -L$(ZDIR) -lz # dynamically linked against zlib +ZLIBs = $(ZDIR)/libz.a # statically linked against zlib +# or: +#ZDIR = ../zlib +#ZINC = -I$(ZDIR) +#ZLIBd = -Wl,-rpath,$(ZDIR) -L$(ZDIR) -lz # -rpath allows in-place testing +#ZLIBs = $(ZDIR)/libz.a + +#XINC = -I/usr/include # old-style, stock X distributions +#XLIB = -L/usr/lib/X11 -lX11 # (including SGI IRIX) +#XINC = -I/usr/openwin/include # Sun workstations (OpenWindows) +#XLIB = -L/usr/openwin/lib -lX11 +XINC = -I/usr/X11R6/include # new X distributions (X.org, etc.) +XLIB = -L/usr/X11R6/lib -lX11 +#XLIB = -L/usr/X11R6/lib64 -lX11 # e.g., Red Hat on AMD64 + +INCS = $(PNGINC) $(ZINC) $(XINC) +RLIBSd = $(PNGLIBd) $(ZLIBd) $(XLIB) -lm +RLIBSs = $(PNGLIBs) $(ZLIBs) $(XLIB) -lm +WLIBSd = $(PNGLIBd) $(ZLIBd) -lm +WLIBSs = $(PNGLIBs) $(ZLIBs) + +CC = gcc +LD = gcc +RM = rm -f +CFLAGS = -O -Wall $(INCS) -DFEATURE_LOOP +# [note that -Wall is a gcc-specific compilation flag ("most warnings on")] +# [-ansi, -pedantic and -W can also be used] +LDFLAGS = +O = .o +E = + +RPNG = rpng-x +RPNG2 = rpng2-x +WPNG = wpng + +RPNGs = $(RPNG)-static +RPNG2s = $(RPNG2)-static +WPNGs = $(WPNG)-static + +ROBJS = $(RPNG)$(O) readpng$(O) +ROBJS2 = $(RPNG2)$(O) readpng2$(O) +WOBJS = $(WPNG)$(O) writepng$(O) + +STATIC_EXES = $(RPNGs)$(E) $(RPNG2s)$(E) $(WPNGs)$(E) +DYNAMIC_EXES = $(RPNG)$(E) $(RPNG2)$(E) $(WPNG)$(E) + +EXES = $(STATIC_EXES) $(DYNAMIC_EXES) + + +# implicit make rules ------------------------------------------------------- + +.c$(O): + $(CC) -c $(CFLAGS) $< + + +# dependencies -------------------------------------------------------------- + +all: $(EXES) + +$(RPNGs)$(E): $(ROBJS) + $(LD) $(LDFLAGS) -o $@ $(ROBJS) $(RLIBSs) + +$(RPNG)$(E): $(ROBJS) + $(LD) $(LDFLAGS) -o $@ $(ROBJS) $(RLIBSd) + +$(RPNG2s)$(E): $(ROBJS2) + $(LD) $(LDFLAGS) -o $@ $(ROBJS2) $(RLIBSs) + +$(RPNG2)$(E): $(ROBJS2) + $(LD) $(LDFLAGS) -o $@ $(ROBJS2) $(RLIBSd) + +$(WPNGs)$(E): $(WOBJS) + $(LD) $(LDFLAGS) -o $@ $(WOBJS) $(WLIBSs) + +$(WPNG)$(E): $(WOBJS) + $(LD) $(LDFLAGS) -o $@ $(WOBJS) $(WLIBSd) + +$(RPNG)$(O): $(RPNG).c readpng.h +$(RPNG2)$(O): $(RPNG2).c readpng2.h +$(WPNG)$(O): $(WPNG).c writepng.h + +readpng$(O): readpng.c readpng.h +readpng2$(O): readpng2.c readpng2.h +writepng$(O): writepng.c writepng.h + + +# maintenance --------------------------------------------------------------- + +clean: + $(RM) $(EXES) $(ROBJS) $(ROBJS2) $(WOBJS) diff --git a/Engine/lib/lpng/contrib/gregbook/Makefile.w32 b/Engine/lib/lpng/contrib/gregbook/Makefile.w32 new file mode 100644 index 000000000..3c0808593 --- /dev/null +++ b/Engine/lib/lpng/contrib/gregbook/Makefile.w32 @@ -0,0 +1,113 @@ +# Sample makefile for rpng-win / rpng2-win / wpng using MSVC and NMAKE. +# Greg Roelofs +# Last modified: 2 June 2007 +# +# The programs built by this makefile are described in the book, +# "PNG: The Definitive Guide," by Greg Roelofs (O'Reilly and +# Associates, 1999). Go buy a copy, eh? Well, OK, it's not +# generally for sale anymore, but it's the thought that counts, +# right? (Hint: http://www.libpng.org/pub/png/book/ ) +# +# Invoke this makefile from a DOS prompt window via: +# +# %devstudio%\vc\bin\vcvars32.bat +# nmake -nologo -f Makefile.w32 +# +# where %devstudio% is the installation directory for MSVC / DevStudio. If +# you get "environment out of space" errors, create a desktop shortcut with +# "c:\windows\command.com /e:4096" as the program command line and set the +# working directory to this directory. Then double-click to open the new +# DOS-prompt window with a bigger environment and retry the commands above. +# +# This makefile assumes libpng and zlib have already been built or downloaded +# and are in subdirectories at the same level as the current subdirectory +# (as indicated by the PNGPATH and ZPATH macros below). Edit as appropriate. +# +# Note that the names of the dynamic and static libpng and zlib libraries +# used below may change in later releases of the libraries. This makefile +# builds statically linked executables, but that can be changed by uncom- +# menting the appropriate PNGLIB and ZLIB lines. + +!include + + +# macros -------------------------------------------------------------------- + +PNGPATH = ../libpng +PNGINC = -I$(PNGPATH) +#PNGLIB = $(PNGPATH)/pngdll.lib +PNGLIB = $(PNGPATH)/libpng.lib + +ZPATH = ../zlib +ZINC = -I$(ZPATH) +#ZLIB = $(ZPATH)/zlibdll.lib +ZLIB = $(ZPATH)/zlibstat.lib + +WINLIBS = -defaultlib:user32.lib gdi32.lib +# ["real" apps may also need comctl32.lib, comdlg32.lib, winmm.lib, etc.] + +INCS = $(PNGINC) $(ZINC) +RLIBS = $(PNGLIB) $(ZLIB) $(WINLIBS) +WLIBS = $(PNGLIB) $(ZLIB) + +CC = cl +LD = link +RM = del +CFLAGS = -nologo -O -W3 $(INCS) $(cvars) +# [note that -W3 is an MSVC-specific compilation flag ("all warnings on")] +# [see %devstudio%\vc\include\win32.mak for cvars macro definition] +O = .obj +E = .exe + +RLDFLAGS = -nologo -subsystem:windows +WLDFLAGS = -nologo + +RPNG = rpng-win +RPNG2 = rpng2-win +WPNG = wpng + +ROBJS = $(RPNG)$(O) readpng$(O) +ROBJS2 = $(RPNG2)$(O) readpng2$(O) +WOBJS = $(WPNG)$(O) writepng$(O) + +EXES = $(RPNG)$(E) $(RPNG2)$(E) $(WPNG)$(E) + + +# implicit make rules ------------------------------------------------------- + +.c$(O): + $(CC) -c $(CFLAGS) $< + + +# dependencies -------------------------------------------------------------- + +all: $(EXES) + +$(RPNG)$(E): $(ROBJS) + $(LD) $(RLDFLAGS) -out:$@ $(ROBJS) $(RLIBS) + +$(RPNG2)$(E): $(ROBJS2) + $(LD) $(RLDFLAGS) -out:$@ $(ROBJS2) $(RLIBS) + +$(WPNG)$(E): $(WOBJS) + $(LD) $(WLDFLAGS) -out:$@ $(WOBJS) $(WLIBS) + +$(RPNG)$(O): $(RPNG).c readpng.h +$(RPNG2)$(O): $(RPNG2).c readpng2.h +$(WPNG)$(O): $(WPNG).c writepng.h + +readpng$(O): readpng.c readpng.h +readpng2$(O): readpng2.c readpng2.h +writepng$(O): writepng.c writepng.h + + +# maintenance --------------------------------------------------------------- + +clean: +# ideally we could just do this: +# $(RM) $(EXES) $(ROBJS) $(ROBJS2) $(WOBJS) +# ...but the Windows "DEL" command is none too bright, so: + $(RM) r*$(E) + $(RM) w*$(E) + $(RM) r*$(O) + $(RM) w*$(O) diff --git a/Engine/lib/lpng/contrib/gregbook/README b/Engine/lib/lpng/contrib/gregbook/README new file mode 100644 index 000000000..5b08bc6f8 --- /dev/null +++ b/Engine/lib/lpng/contrib/gregbook/README @@ -0,0 +1,186 @@ + =========================== + PNG: The Definitive Guide + =========================== + + Source Code + +Chapters 13, 14 and 15 of "PNG: The Definitive Guide" discuss three free, +cross-platform demo programs that show how to use the libpng reference +library: rpng, rpng2 and wpng. rpng and rpng2 are viewers; the first is +a very simple example that that shows how a standard file-viewer might use +libpng, while the second is designed to process streaming data and shows +how a web browser might be written. wpng is a simple command-line program +that reads binary PGM and PPM files (the ``raw'' grayscale and RGB subsets +of PBMPLUS/NetPBM) and converts them to PNG. + +The source code for all three demo programs currently compiles under +Unix, OpenVMS, and 32-bit Windows. (Special thanks to Martin Zinser, +zinser@decus.de, for making the necessary changes for OpenVMS and for +providing an appropriate build script.) Build instructions can be found +below. + +Files: + + README this file + LICENSE terms of distribution and reuse (BSD-like or GNU GPL) + COPYING GNU General Public License (GPL) + + Makefile.unx Unix makefile + Makefile.w32 Windows (MSVC) makefile + makevms.com OpenVMS build script + + rpng-win.c Windows front end for the basic viewer + rpng-x.c X Window System (Unix, OpenVMS) front end + readpng.c generic back end for the basic viewer + readpng.h header file for the basic viewer + + rpng2-win.c Windows front end for the progressive viewer + rpng2-x.c X front end for the progressive viewer + readpng2.c generic back end for the progressive viewer + readpng2.h header file for the progressive viewer + + wpng.c generic (text) front end for the converter + writepng.c generic back end for the converter + writepng.h header file for the converter + + toucan.png transparent PNG for testing (by Stefan Schneider) + +Note that, although the programs are designed to be functional, their +primary purpose is to illustrate how to use libpng to add PNG support to +other programs. As such, their user interfaces are crude and definitely +are not intended for everyday use. + +Please see http://www.libpng.org/pub/png/pngbook.html for further infor- +mation and links to the latest version of the source code, and Chapters +13-15 of the book for detailed discussion of the three programs. + +Greg Roelofs +http://pobox.com/~newt/greg_contact.html +2 June 2007 + + +BUILD INSTRUCTIONS + + - Prerequisites (in order of compilation): + + - zlib http://zlib.net/ + - libpng http://www.libpng.org/pub/png/libpng.html + - pngbook http://www.libpng.org/pub/png/book/sources.html + + The pngbook demo programs are explicitly designed to demonstrate proper + coding techniques for using the libpng reference library. As a result, + you need to download and build both zlib (on which libpng depends) and + libpng. A common build setup is to place the zlib, libpng and pngbook + subdirectory trees ("folders") in the same parent directory. Then the + libpng build can refer to files in ../zlib (or ..\zlib or [-.zlib]), + and similarly for the pngbook build. + + Note that all three packages are designed to be built from a command + line by default; those who wish to use a graphical or other integrated + development environments are on their own. + + + - Unix: + + Unpack the latest pngbook sources (which should correspond to this + README file) into a directory and change into that directory. + + Copy Makefile.unx to Makefile and edit the PNG* and Z* variables + appropriately (possibly also the X* variables if necessary). + + make + + There is no "install" target, so copy the three executables somewhere + in your path or run them from the current directory. All three will + print a basic usage screen when run without any command-line arguments; + see the book for more details. + + + - Windows: + + Unpack the latest pngbook sources (which should correspond to this + README file) into a folder, open a "DOS shell" or "command prompt" + or equivalent command-line window, and cd into the folder where you + unpacked the source code. + + For MSVC, set up the necessary environment variables by invoking + + %devstudio%\vc\bin\vcvars32.bat + + where where %devstudio% is the installation directory for MSVC / + DevStudio. If you get "environment out of space" errors under 95/98, + create a desktop shortcut with "c:\windows\command.com /e:4096" as + the program command line and set the working directory to the pngbook + directory. Then double-click to open the new DOS-prompt window with + a bigger environment and retry the commands above. + + Copy Makefile.w32 to Makefile and edit the PNGPATH and ZPATH variables + appropriately (possibly also the "INC" and "LIB" variables if needed). + Note that the names of the dynamic and static libpng and zlib libraries + used in the makefile may change in later releases of the libraries. + Also note that, as of libpng version 1.0.5, MSVC DLL builds do not work. + This makefile therefore builds statically linked executables, but if + the DLL problems ever get fixed, uncommenting the appropriate PNGLIB + and ZLIB lines will build dynamically linked executables instead. + + Do the build by typing + + nmake + + The result should be three executables: rpng-win.exe, rpng2-win.exe, + and wpng.exe. Copy them somewhere in your PATH or run them from the + current folder. Like the Unix versions, the two windowed programs + (rpng and rpng2) now display a usage screen in a console window when + invoked without command-line arguments; this is new behavior as of + the June 2001 release. Note that the programs use the Unix-style "-" + character to specify options, instead of the more common DOS/Windows + "/" character. (For example: "rpng2-win -bgpat 4 foo.png", not + "rpng2-win /bgpat 4 foo.png") + + + - OpenVMS: + + Unpack the pngbook sources into a subdirectory and change into that + subdirectory. + + Edit makevms.com appropriately, specifically the zpath and pngpath + variables. + + @makevms + + To run the programs, they probably first need to be set up as "foreign + symbols," with "disk" and "dir" set appropriately: + + $ rpng == "$disk:[dir]rpng-x.exe" + $ rpng2 == "$disk:[dir]rpng2-x.exe" + $ wpng == "$disk:[dir]wpng.exe" + + All three will print a basic usage screen when run without any command- + line arguments; see the book for more details. Note that the options + style is Unix-like, i.e., preceded by "-" rather than "/". + + +RUNNING THE PROGRAMS: (VERY) BRIEF INTRO + + rpng is a simple PNG viewer that can display transparent PNGs with a + specified background color; for example, + + rpng -bgcolor #ff0000 toucan.png + + would display the image with a red background. rpng2 is a progressive + viewer that simulates a web browser in some respects; it can display + images against either a background color or a dynamically generated + background image. For example: + + rpng2 -bgpat 16 toucan.png + + wpng is a purely command-line image converter from binary PBMPLUS/NetPBM + format (.pgm or .ppm) to PNG; for example, + + wpng -time < toucan-notrans.ppm > toucan-notrans.png + + would convert the specified PPM file (using redirection) to PNG, auto- + matically setting the PNG modification-time chunk. + + All options can be abbreviated to the shortest unique value; for example, + "-bgc" for -bgcolor (versus "-bgp" for -bgpat), or "-g" for -gamma. diff --git a/Engine/lib/lpng/contrib/gregbook/makevms.com b/Engine/lib/lpng/contrib/gregbook/makevms.com new file mode 100644 index 000000000..bd37dc0d7 --- /dev/null +++ b/Engine/lib/lpng/contrib/gregbook/makevms.com @@ -0,0 +1,132 @@ +$!------------------------------------------------------------------------------ +$! make "PNG: The Definitive Guide" demo programs (for X) under OpenVMS +$! +$! Script created by Martin Zinser for libpng; modified by Greg Roelofs +$! for standalone pngbook source distribution. +$! +$! +$! Set locations where zlib and libpng sources live. +$! +$ zpath = "" +$ pngpath = "" +$! +$ if f$search("[---.zlib]zlib.h").nes."" then zpath = "[---.zlib]" +$ if f$search("[--]png.h").nes."" then pngpath = "[--]" +$! +$ if f$search("[-.zlib]zlib.h").nes."" then zpath = "[-.zlib]" +$ if f$search("[-.libpng]png.h").nes."" then pngpath = "[-.libpng]" +$! +$ if zpath .eqs. "" +$ then +$ write sys$output "zlib include not found. Exiting..." +$ exit 2 +$ endif +$! +$ if pngpath .eqs. "" +$ then +$ write sys$output "libpng include not found. Exiting..." +$ exit 2 +$ endif +$! +$! Look for the compiler used. +$! +$ ccopt="/include=(''zpath',''pngpath')" +$ if f$getsyi("HW_MODEL").ge.1024 +$ then +$ ccopt = "/prefix=all"+ccopt +$ comp = "__decc__=1" +$ if f$trnlnm("SYS").eqs."" then define sys sys$library: +$ else +$ if f$search("SYS$SYSTEM:DECC$COMPILER.EXE").eqs."" +$ then +$ if f$trnlnm("SYS").eqs."" then define sys sys$library: +$ if f$search("SYS$SYSTEM:VAXC.EXE").eqs."" +$ then +$ comp = "__gcc__=1" +$ CC :== GCC +$ else +$ comp = "__vaxc__=1" +$ endif +$ else +$ if f$trnlnm("SYS").eqs."" then define sys decc$library_include: +$ ccopt = "/decc/prefix=all"+ccopt +$ comp = "__decc__=1" +$ endif +$ endif +$ open/write lopt lib.opt +$ write lopt "''pngpath'libpng.olb/lib" +$ write lopt "''zpath'libz.olb/lib" +$ close lopt +$ open/write xopt x11.opt +$ write xopt "sys$library:decw$xlibshr.exe/share" +$ close xopt +$! +$! Build 'em. +$! +$ write sys$output "Compiling PNG book programs ..." +$ CALL MAKE readpng.OBJ "cc ''CCOPT' readpng" - + readpng.c readpng.h +$ CALL MAKE readpng2.OBJ "cc ''CCOPT' readpng2" - + readpng2.c readpng2.h +$ CALL MAKE writepng.OBJ "cc ''CCOPT' writepng" - + writepng.c writepng.h +$ write sys$output "Building rpng-x..." +$ CALL MAKE rpng-x.OBJ "cc ''CCOPT' rpng-x" - + rpng-x.c readpng.h +$ call make rpng-x.exe - + "LINK rpng-x,readpng,lib.opt/opt,x11.opt/opt" - + rpng-x.obj readpng.obj +$ write sys$output "Building rpng2-x..." +$ CALL MAKE rpng2-x.OBJ "cc ''CCOPT' rpng2-x" - + rpng2-x.c readpng2.h +$ call make rpng2-x.exe - + "LINK rpng2-x,readpng2,lib.opt/opt,x11.opt/opt" - + rpng2-x.obj readpng2.obj +$ write sys$output "Building wpng..." +$ CALL MAKE wpng.OBJ "cc ''CCOPT' wpng" - + wpng.c writepng.h +$ call make wpng.exe - + "LINK wpng,writepng,lib.opt/opt" - + wpng.obj writepng.obj +$ exit +$! +$! +$MAKE: SUBROUTINE !SUBROUTINE TO CHECK DEPENDENCIES +$ V = 'F$Verify(0) +$! P1 = What we are trying to make +$! P2 = Command to make it +$! P3 - P8 What it depends on +$ +$ If F$Search(P1) .Eqs. "" Then Goto Makeit +$ Time = F$CvTime(F$File(P1,"RDT")) +$arg=3 +$Loop: +$ Argument = P'arg +$ If Argument .Eqs. "" Then Goto Exit +$ El=0 +$Loop2: +$ File = F$Element(El," ",Argument) +$ If File .Eqs. " " Then Goto Endl +$ AFile = "" +$Loop3: +$ OFile = AFile +$ AFile = F$Search(File) +$ If AFile .Eqs. "" .Or. AFile .Eqs. OFile Then Goto NextEl +$ If F$CvTime(F$File(AFile,"RDT")) .Ges. Time Then Goto Makeit +$ Goto Loop3 +$NextEL: +$ El = El + 1 +$ Goto Loop2 +$EndL: +$ arg=arg+1 +$ If arg .Le. 8 Then Goto Loop +$ Goto Exit +$ +$Makeit: +$ VV=F$VERIFY(0) +$ write sys$output P2 +$ 'P2 +$ VV='F$Verify(VV) +$Exit: +$ If V Then Set Verify +$ENDSUBROUTINE diff --git a/Engine/lib/lpng/contrib/gregbook/readpng.c b/Engine/lib/lpng/contrib/gregbook/readpng.c new file mode 100644 index 000000000..d87f6c7ed --- /dev/null +++ b/Engine/lib/lpng/contrib/gregbook/readpng.c @@ -0,0 +1,304 @@ +/*--------------------------------------------------------------------------- + + rpng - simple PNG display program readpng.c + + --------------------------------------------------------------------------- + + Copyright (c) 1998-2007 Greg Roelofs. All rights reserved. + + This software is provided "as is," without warranty of any kind, + express or implied. In no event shall the author or contributors + be held liable for any damages arising in any way from the use of + this software. + + The contents of this file are DUAL-LICENSED. You may modify and/or + redistribute this software according to the terms of one of the + following two licenses (at your option): + + + LICENSE 1 ("BSD-like with advertising clause"): + + 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. Redistributions of source code must retain the above copyright + notice, disclaimer, and this list of conditions. + 2. Redistributions in binary form must reproduce the above copyright + notice, disclaimer, and this list of conditions in the documenta- + tion and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + + This product includes software developed by Greg Roelofs + and contributors for the book, "PNG: The Definitive Guide," + published by O'Reilly and Associates. + + + LICENSE 2 (GNU GPL v2 or later): + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ---------------------------------------------------------------------------*/ + +#include +#include + +#include "png.h" /* libpng header; includes zlib.h */ +#include "readpng.h" /* typedefs, common macros, public prototypes */ + +/* future versions of libpng will provide this macro: */ +#ifndef png_jmpbuf +# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) +#endif + + +static png_structp png_ptr = NULL; +static png_infop info_ptr = NULL; + +png_uint_32 width, height; +int bit_depth, color_type; +uch *image_data = NULL; + + +void readpng_version_info(void) +{ + fprintf(stderr, " Compiled with libpng %s; using libpng %s.\n", + PNG_LIBPNG_VER_STRING, png_libpng_ver); + fprintf(stderr, " Compiled with zlib %s; using zlib %s.\n", + ZLIB_VERSION, zlib_version); +} + + +/* return value = 0 for success, 1 for bad sig, 2 for bad IHDR, 4 for no mem */ + +int readpng_init(FILE *infile, ulg *pWidth, ulg *pHeight) +{ + uch sig[8]; + + + /* first do a quick check that the file really is a PNG image; could + * have used slightly more general png_sig_cmp() function instead */ + + fread(sig, 1, 8, infile); + if (!png_check_sig(sig, 8)) + return 1; /* bad signature */ + + + /* could pass pointers to user-defined error handlers instead of NULLs: */ + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + return 4; /* out of memory */ + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_read_struct(&png_ptr, NULL, NULL); + return 4; /* out of memory */ + } + + + /* we could create a second info struct here (end_info), but it's only + * useful if we want to keep pre- and post-IDAT chunk info separated + * (mainly for PNG-aware image editors and converters) */ + + + /* setjmp() must be called in every function that calls a PNG-reading + * libpng function */ + + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + return 2; + } + + + png_init_io(png_ptr, infile); + png_set_sig_bytes(png_ptr, 8); /* we already read the 8 signature bytes */ + + png_read_info(png_ptr, info_ptr); /* read all PNG info up to image data */ + + + /* alternatively, could make separate calls to png_get_image_width(), + * etc., but want bit_depth and color_type for later [don't care about + * compression_type and filter_type => NULLs] */ + + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, + NULL, NULL, NULL); + *pWidth = width; + *pHeight = height; + + + /* OK, that's all we need for now; return happy */ + + return 0; +} + + + + +/* returns 0 if succeeds, 1 if fails due to no bKGD chunk, 2 if libpng error; + * scales values to 8-bit if necessary */ + +int readpng_get_bgcolor(uch *red, uch *green, uch *blue) +{ + png_color_16p pBackground; + + + /* setjmp() must be called in every function that calls a PNG-reading + * libpng function */ + + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + return 2; + } + + + if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD)) + return 1; + + /* it is not obvious from the libpng documentation, but this function + * takes a pointer to a pointer, and it always returns valid red, green + * and blue values, regardless of color_type: */ + + png_get_bKGD(png_ptr, info_ptr, &pBackground); + + + /* however, it always returns the raw bKGD data, regardless of any + * bit-depth transformations, so check depth and adjust if necessary */ + + if (bit_depth == 16) { + *red = pBackground->red >> 8; + *green = pBackground->green >> 8; + *blue = pBackground->blue >> 8; + } else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { + if (bit_depth == 1) + *red = *green = *blue = pBackground->gray? 255 : 0; + else if (bit_depth == 2) + *red = *green = *blue = (255/3) * pBackground->gray; + else /* bit_depth == 4 */ + *red = *green = *blue = (255/15) * pBackground->gray; + } else { + *red = (uch)pBackground->red; + *green = (uch)pBackground->green; + *blue = (uch)pBackground->blue; + } + + return 0; +} + + + + +/* display_exponent == LUT_exponent * CRT_exponent */ + +uch *readpng_get_image(double display_exponent, int *pChannels, ulg *pRowbytes) +{ + double gamma; + png_uint_32 i, rowbytes; + png_bytepp row_pointers = NULL; + + + /* setjmp() must be called in every function that calls a PNG-reading + * libpng function */ + + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + return NULL; + } + + + /* expand palette images to RGB, low-bit-depth grayscale images to 8 bits, + * transparency chunks to full alpha channel; strip 16-bit-per-sample + * images to 8 bits per sample; and convert grayscale to RGB[A] */ + + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_expand(png_ptr); + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + png_set_expand(png_ptr); + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_expand(png_ptr); + if (bit_depth == 16) + png_set_strip_16(png_ptr); + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png_ptr); + + + /* unlike the example in the libpng documentation, we have *no* idea where + * this file may have come from--so if it doesn't have a file gamma, don't + * do any correction ("do no harm") */ + + if (png_get_gAMA(png_ptr, info_ptr, &gamma)) + png_set_gamma(png_ptr, display_exponent, gamma); + + + /* all transformations have been registered; now update info_ptr data, + * get rowbytes and channels, and allocate image memory */ + + png_read_update_info(png_ptr, info_ptr); + + *pRowbytes = rowbytes = png_get_rowbytes(png_ptr, info_ptr); + *pChannels = (int)png_get_channels(png_ptr, info_ptr); + + if ((image_data = (uch *)malloc(rowbytes*height)) == NULL) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + return NULL; + } + if ((row_pointers = (png_bytepp)malloc(height*sizeof(png_bytep))) == NULL) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + free(image_data); + image_data = NULL; + return NULL; + } + + Trace((stderr, "readpng_get_image: channels = %d, rowbytes = %ld, height = %ld\n", *pChannels, rowbytes, height)); + + + /* set the individual row_pointers to point at the correct offsets */ + + for (i = 0; i < height; ++i) + row_pointers[i] = image_data + i*rowbytes; + + + /* now we can go ahead and just read the whole image */ + + png_read_image(png_ptr, row_pointers); + + + /* and we're done! (png_read_end() can be omitted if no processing of + * post-IDAT text/time/etc. is desired) */ + + free(row_pointers); + row_pointers = NULL; + + png_read_end(png_ptr, NULL); + + return image_data; +} + + +void readpng_cleanup(int free_image_data) +{ + if (free_image_data && image_data) { + free(image_data); + image_data = NULL; + } + + if (png_ptr && info_ptr) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + png_ptr = NULL; + info_ptr = NULL; + } +} diff --git a/Engine/lib/lpng/contrib/gregbook/readpng.h b/Engine/lib/lpng/contrib/gregbook/readpng.h new file mode 100644 index 000000000..fad9fe3b4 --- /dev/null +++ b/Engine/lib/lpng/contrib/gregbook/readpng.h @@ -0,0 +1,88 @@ +/*--------------------------------------------------------------------------- + + rpng - simple PNG display program readpng.h + + --------------------------------------------------------------------------- + + Copyright (c) 1998-2007 Greg Roelofs. All rights reserved. + + This software is provided "as is," without warranty of any kind, + express or implied. In no event shall the author or contributors + be held liable for any damages arising in any way from the use of + this software. + + The contents of this file are DUAL-LICENSED. You may modify and/or + redistribute this software according to the terms of one of the + following two licenses (at your option): + + + LICENSE 1 ("BSD-like with advertising clause"): + + 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. Redistributions of source code must retain the above copyright + notice, disclaimer, and this list of conditions. + 2. Redistributions in binary form must reproduce the above copyright + notice, disclaimer, and this list of conditions in the documenta- + tion and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + + This product includes software developed by Greg Roelofs + and contributors for the book, "PNG: The Definitive Guide," + published by O'Reilly and Associates. + + + LICENSE 2 (GNU GPL v2 or later): + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ---------------------------------------------------------------------------*/ + +#ifndef TRUE +# define TRUE 1 +# define FALSE 0 +#endif + +#ifndef MAX +# define MAX(a,b) ((a) > (b)? (a) : (b)) +# define MIN(a,b) ((a) < (b)? (a) : (b)) +#endif + +#ifdef DEBUG +# define Trace(x) {fprintf x ; fflush(stderr); fflush(stdout);} +#else +# define Trace(x) ; +#endif + +typedef unsigned char uch; +typedef unsigned short ush; +typedef unsigned long ulg; + + +/* prototypes for public functions in readpng.c */ + +void readpng_version_info(void); + +int readpng_init(FILE *infile, ulg *pWidth, ulg *pHeight); + +int readpng_get_bgcolor(uch *bg_red, uch *bg_green, uch *bg_blue); + +uch *readpng_get_image(double display_exponent, int *pChannels, + ulg *pRowbytes); + +void readpng_cleanup(int free_image_data); diff --git a/Engine/lib/lpng/contrib/gregbook/readpng2.c b/Engine/lib/lpng/contrib/gregbook/readpng2.c new file mode 100644 index 000000000..921504285 --- /dev/null +++ b/Engine/lib/lpng/contrib/gregbook/readpng2.c @@ -0,0 +1,645 @@ +/*--------------------------------------------------------------------------- + + rpng2 - progressive-model PNG display program readpng2.c + + --------------------------------------------------------------------------- + + Copyright (c) 1998-2007 Greg Roelofs. All rights reserved. + + This software is provided "as is," without warranty of any kind, + express or implied. In no event shall the author or contributors + be held liable for any damages arising in any way from the use of + this software. + + The contents of this file are DUAL-LICENSED. You may modify and/or + redistribute this software according to the terms of one of the + following two licenses (at your option): + + + LICENSE 1 ("BSD-like with advertising clause"): + + 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. Redistributions of source code must retain the above copyright + notice, disclaimer, and this list of conditions. + 2. Redistributions in binary form must reproduce the above copyright + notice, disclaimer, and this list of conditions in the documenta- + tion and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + + This product includes software developed by Greg Roelofs + and contributors for the book, "PNG: The Definitive Guide," + published by O'Reilly and Associates. + + + LICENSE 2 (GNU GPL v2 or later): + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ---------------------------------------------------------------------------*/ + + +#include /* for exit() prototype */ + +#include "png.h" /* libpng header; includes zlib.h and setjmp.h */ +#include "readpng2.h" /* typedefs, common macros, public prototypes */ + + +/* local prototypes */ + +static void readpng2_info_callback(png_structp png_ptr, png_infop info_ptr); +static void readpng2_row_callback(png_structp png_ptr, png_bytep new_row, + png_uint_32 row_num, int pass); +static void readpng2_end_callback(png_structp png_ptr, png_infop info_ptr); +static void readpng2_error_handler(png_structp png_ptr, png_const_charp msg); + + + + +void readpng2_version_info(void) +{ +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \ + (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__)) && \ + defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200) + /* + * WARNING: This preprocessor approach means that the following code + * cannot be used with a libpng DLL older than 1.2.0--the + * compiled-in symbols for the new functions will not exist. + * (Could use dlopen() and dlsym() on Unix and corresponding + * calls for Windows, but not portable...) + */ + { + int mmxsupport = png_mmx_support(); + if (mmxsupport < 0) + fprintf(stderr, " Compiled with libpng %s; using libpng %s " + "without MMX support.\n", PNG_LIBPNG_VER_STRING, png_libpng_ver); + else { + int compilerID; + png_uint_32 mmx_mask = png_get_mmx_flagmask( + PNG_SELECT_READ | PNG_SELECT_WRITE, &compilerID); + + fprintf(stderr, " Compiled with libpng %s; using libpng %s " + "with MMX support\n (%s version).", PNG_LIBPNG_VER_STRING, + png_libpng_ver, compilerID == 1? "MSVC++" : + (compilerID == 2? "GNU C" : "unknown")); + fprintf(stderr, " Processor (x86%s) %s MMX instructions.\n", +#if defined(__x86_64__) + "_64", +#else + "", +#endif + mmxsupport? "supports" : "does not support"); + if (mmxsupport > 0) { + int num_optims = 0; + + fprintf(stderr, + " Potential MMX optimizations supported by libpng:\n"); + if (mmx_mask & PNG_ASM_FLAG_MMX_READ_FILTER_SUB) + ++num_optims; + if (mmx_mask & PNG_ASM_FLAG_MMX_READ_FILTER_UP) + ++num_optims; + if (mmx_mask & PNG_ASM_FLAG_MMX_READ_FILTER_AVG) + ++num_optims; + if (mmx_mask & PNG_ASM_FLAG_MMX_READ_FILTER_PAETH) + ++num_optims; + if (num_optims) + fprintf(stderr, + " decoding %s row filters (reading)\n", + (num_optims == 4)? "all non-trivial" : "some"); + if (mmx_mask & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW) { + fprintf(stderr, " combining rows (reading)\n"); + ++num_optims; + } + if (mmx_mask & PNG_ASM_FLAG_MMX_READ_INTERLACE) { + fprintf(stderr, + " expanding interlacing (reading)\n"); + ++num_optims; + } + mmx_mask &= ~( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \ + | PNG_ASM_FLAG_MMX_READ_INTERLACE \ + | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \ + | PNG_ASM_FLAG_MMX_READ_FILTER_UP \ + | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \ + | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ); + if (mmx_mask) { + fprintf(stderr, " other (unknown)\n"); + ++num_optims; + } + if (num_optims == 0) + fprintf(stderr, " (none)\n"); + } + } + } +#else + fprintf(stderr, " Compiled with libpng %s; using libpng %s " + "without MMX support.\n", PNG_LIBPNG_VER_STRING, png_libpng_ver); +#endif + + fprintf(stderr, " Compiled with zlib %s; using zlib %s.\n", + ZLIB_VERSION, zlib_version); +} + + + + +int readpng2_check_sig(uch *sig, int num) +{ + return png_check_sig(sig, num); +} + + + + +/* returns 0 for success, 2 for libpng problem, 4 for out of memory */ + +int readpng2_init(mainprog_info *mainprog_ptr) +{ + png_structp png_ptr; /* note: temporary variables! */ + png_infop info_ptr; + + + /* could also replace libpng warning-handler (final NULL), but no need: */ + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, mainprog_ptr, + readpng2_error_handler, NULL); + if (!png_ptr) + return 4; /* out of memory */ + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_read_struct(&png_ptr, NULL, NULL); + return 4; /* out of memory */ + } + + + /* we could create a second info struct here (end_info), but it's only + * useful if we want to keep pre- and post-IDAT chunk info separated + * (mainly for PNG-aware image editors and converters) */ + + + /* setjmp() must be called in every function that calls a PNG-reading + * libpng function, unless an alternate error handler was installed-- + * but compatible error handlers must either use longjmp() themselves + * (as in this program) or exit immediately, so here we are: */ + + if (setjmp(mainprog_ptr->jmpbuf)) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + return 2; + } + + +#ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED + /* prepare the reader to ignore all recognized chunks whose data won't be + * used, i.e., all chunks recognized by libpng except for IHDR, PLTE, IDAT, + * IEND, tRNS, bKGD, gAMA, and sRGB (small performance improvement) */ + { + /* These byte strings were copied from png.h. If a future libpng + * version recognizes more chunks, add them to this list. If a + * future version of readpng2.c recognizes more chunks, delete them + * from this list. */ + static const png_byte chunks_to_ignore[] = { + 99, 72, 82, 77, '\0', /* cHRM */ + 104, 73, 83, 84, '\0', /* hIST */ + 105, 67, 67, 80, '\0', /* iCCP */ + 105, 84, 88, 116, '\0', /* iTXt */ + 111, 70, 70, 115, '\0', /* oFFs */ + 112, 67, 65, 76, '\0', /* pCAL */ + 112, 72, 89, 115, '\0', /* pHYs */ + 115, 66, 73, 84, '\0', /* sBIT */ + 115, 67, 65, 76, '\0', /* sCAL */ + 115, 80, 76, 84, '\0', /* sPLT */ + 115, 84, 69, 82, '\0', /* sTER */ + 116, 69, 88, 116, '\0', /* tEXt */ + 116, 73, 77, 69, '\0', /* tIME */ + 122, 84, 88, 116, '\0' /* zTXt */ + }; + + png_set_keep_unknown_chunks(png_ptr, 1 /* PNG_HANDLE_CHUNK_NEVER */, + chunks_to_ignore, sizeof(chunks_to_ignore)/5); + } +#endif /* PNG_UNKNOWN_CHUNKS_SUPPORTED */ + + + /* instead of doing png_init_io() here, now we set up our callback + * functions for progressive decoding */ + + png_set_progressive_read_fn(png_ptr, mainprog_ptr, + readpng2_info_callback, readpng2_row_callback, readpng2_end_callback); + + + /* + * may as well enable or disable MMX routines here, if supported; + * + * to enable all: mask = png_get_mmx_flagmask ( + * PNG_SELECT_READ | PNG_SELECT_WRITE, &compilerID); + * flags = png_get_asm_flags (png_ptr); + * flags |= mask; + * png_set_asm_flags (png_ptr, flags); + * + * to disable all: mask = png_get_mmx_flagmask ( + * PNG_SELECT_READ | PNG_SELECT_WRITE, &compilerID); + * flags = png_get_asm_flags (png_ptr); + * flags &= ~mask; + * png_set_asm_flags (png_ptr, flags); + */ + +#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__)) && \ + defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10200) + /* + * WARNING: This preprocessor approach means that the following code + * cannot be used with a libpng DLL older than 1.2.0--the + * compiled-in symbols for the new functions will not exist. + * (Could use dlopen() and dlsym() on Unix and corresponding + * calls for Windows, but not portable...) + */ + { +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED + png_uint_32 mmx_disable_mask = 0; + png_uint_32 asm_flags, mmx_mask; + int compilerID; + + if (mainprog_ptr->nommxfilters) + mmx_disable_mask |= ( PNG_ASM_FLAG_MMX_READ_FILTER_SUB \ + | PNG_ASM_FLAG_MMX_READ_FILTER_UP \ + | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \ + | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ); + if (mainprog_ptr->nommxcombine) + mmx_disable_mask |= PNG_ASM_FLAG_MMX_READ_COMBINE_ROW; + if (mainprog_ptr->nommxinterlace) + mmx_disable_mask |= PNG_ASM_FLAG_MMX_READ_INTERLACE; + asm_flags = png_get_asm_flags(png_ptr); + png_set_asm_flags(png_ptr, asm_flags & ~mmx_disable_mask); + + + /* Now query libpng's asm settings, just for yuks. Note that this + * differs from the querying of its *potential* MMX capabilities + * in readpng2_version_info(); this is true runtime verification. */ + + asm_flags = png_get_asm_flags(png_ptr); + mmx_mask = png_get_mmx_flagmask(PNG_SELECT_READ | PNG_SELECT_WRITE, + &compilerID); + if (asm_flags & PNG_ASM_FLAG_MMX_SUPPORT_COMPILED) + fprintf(stderr, + " MMX support (%s version) is compiled into libpng\n", + compilerID == 1? "MSVC++" : + (compilerID == 2? "GNU C" : "unknown")); + else + fprintf(stderr, " MMX support is not compiled into libpng\n"); + fprintf(stderr, " MMX instructions are %ssupported by CPU\n", + (asm_flags & PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU)? "" : "not "); + fprintf(stderr, " MMX read support for combining rows is %sabled\n", + (asm_flags & PNG_ASM_FLAG_MMX_READ_COMBINE_ROW)? "en" : "dis"); + fprintf(stderr, + " MMX read support for expanding interlacing is %sabled\n", + (asm_flags & PNG_ASM_FLAG_MMX_READ_INTERLACE)? "en" : "dis"); + fprintf(stderr, " MMX read support for \"sub\" filter is %sabled\n", + (asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_SUB)? "en" : "dis"); + fprintf(stderr, " MMX read support for \"up\" filter is %sabled\n", + (asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_UP)? "en" : "dis"); + fprintf(stderr, " MMX read support for \"avg\" filter is %sabled\n", + (asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_AVG)? "en" : "dis"); + fprintf(stderr, " MMX read support for \"Paeth\" filter is %sabled\n", + (asm_flags & PNG_ASM_FLAG_MMX_READ_FILTER_PAETH)? "en" : "dis"); + asm_flags &= (mmx_mask & ~( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \ + | PNG_ASM_FLAG_MMX_READ_INTERLACE \ + | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \ + | PNG_ASM_FLAG_MMX_READ_FILTER_UP \ + | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \ + | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH )); + if (asm_flags) + fprintf(stderr, + " additional MMX support is also enabled (0x%02lx)\n", + asm_flags); +#else /* !PNG_ASSEMBLER_CODE_SUPPORTED */ + fprintf(stderr, " MMX querying is disabled in libpng.\n"); +#endif /* ?PNG_ASSEMBLER_CODE_SUPPORTED */ + } +#endif + + + /* make sure we save our pointers for use in readpng2_decode_data() */ + + mainprog_ptr->png_ptr = png_ptr; + mainprog_ptr->info_ptr = info_ptr; + + + /* and that's all there is to initialization */ + + return 0; +} + + + + +/* returns 0 for success, 2 for libpng (longjmp) problem */ + +int readpng2_decode_data(mainprog_info *mainprog_ptr, uch *rawbuf, ulg length) +{ + png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr; + png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr; + + + /* setjmp() must be called in every function that calls a PNG-reading + * libpng function */ + + if (setjmp(mainprog_ptr->jmpbuf)) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + mainprog_ptr->png_ptr = NULL; + mainprog_ptr->info_ptr = NULL; + return 2; + } + + + /* hand off the next chunk of input data to libpng for decoding */ + + png_process_data(png_ptr, info_ptr, rawbuf, length); + + return 0; +} + + + + +static void readpng2_info_callback(png_structp png_ptr, png_infop info_ptr) +{ + mainprog_info *mainprog_ptr; + int color_type, bit_depth; + double gamma; + + + /* setjmp() doesn't make sense here, because we'd either have to exit(), + * longjmp() ourselves, or return control to libpng, which doesn't want + * to see us again. By not doing anything here, libpng will instead jump + * to readpng2_decode_data(), which can return an error value to the main + * program. */ + + + /* retrieve the pointer to our special-purpose struct, using the png_ptr + * that libpng passed back to us (i.e., not a global this time--there's + * no real difference for a single image, but for a multithreaded browser + * decoding several PNG images at the same time, one needs to avoid mixing + * up different images' structs) */ + + mainprog_ptr = png_get_progressive_ptr(png_ptr); + + if (mainprog_ptr == NULL) { /* we be hosed */ + fprintf(stderr, + "readpng2 error: main struct not recoverable in info_callback.\n"); + fflush(stderr); + return; + /* + * Alternatively, we could call our error-handler just like libpng + * does, which would effectively terminate the program. Since this + * can only happen if png_ptr gets redirected somewhere odd or the + * main PNG struct gets wiped, we're probably toast anyway. (If + * png_ptr itself is NULL, we would not have been called.) + */ + } + + + /* this is just like in the non-progressive case */ + + png_get_IHDR(png_ptr, info_ptr, &mainprog_ptr->width, + &mainprog_ptr->height, &bit_depth, &color_type, NULL, NULL, NULL); + + + /* since we know we've read all of the PNG file's "header" (i.e., up + * to IDAT), we can check for a background color here */ + + if (mainprog_ptr->need_bgcolor && + png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD)) + { + png_color_16p pBackground; + + /* it is not obvious from the libpng documentation, but this function + * takes a pointer to a pointer, and it always returns valid red, + * green and blue values, regardless of color_type: */ + png_get_bKGD(png_ptr, info_ptr, &pBackground); + + /* however, it always returns the raw bKGD data, regardless of any + * bit-depth transformations, so check depth and adjust if necessary */ + if (bit_depth == 16) { + mainprog_ptr->bg_red = pBackground->red >> 8; + mainprog_ptr->bg_green = pBackground->green >> 8; + mainprog_ptr->bg_blue = pBackground->blue >> 8; + } else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { + if (bit_depth == 1) + mainprog_ptr->bg_red = mainprog_ptr->bg_green = + mainprog_ptr->bg_blue = pBackground->gray? 255 : 0; + else if (bit_depth == 2) + mainprog_ptr->bg_red = mainprog_ptr->bg_green = + mainprog_ptr->bg_blue = (255/3) * pBackground->gray; + else /* bit_depth == 4 */ + mainprog_ptr->bg_red = mainprog_ptr->bg_green = + mainprog_ptr->bg_blue = (255/15) * pBackground->gray; + } else { + mainprog_ptr->bg_red = (uch)pBackground->red; + mainprog_ptr->bg_green = (uch)pBackground->green; + mainprog_ptr->bg_blue = (uch)pBackground->blue; + } + } + + + /* as before, let libpng expand palette images to RGB, low-bit-depth + * grayscale images to 8 bits, transparency chunks to full alpha channel; + * strip 16-bit-per-sample images to 8 bits per sample; and convert + * grayscale to RGB[A] */ + + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_expand(png_ptr); + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + png_set_expand(png_ptr); + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_expand(png_ptr); + if (bit_depth == 16) + png_set_strip_16(png_ptr); + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png_ptr); + + + /* Unlike the basic viewer, which was designed to operate on local files, + * this program is intended to simulate a web browser--even though we + * actually read from a local file, too. But because we are pretending + * that most of the images originate on the Internet, we follow the recom- + * mendation of the sRGB proposal and treat unlabelled images (no gAMA + * chunk) as existing in the sRGB color space. That is, we assume that + * such images have a file gamma of 0.45455, which corresponds to a PC-like + * display system. This change in assumptions will have no effect on a + * PC-like system, but on a Mac, SGI, NeXT or other system with a non- + * identity lookup table, it will darken unlabelled images, which effec- + * tively favors images from PC-like systems over those originating on + * the local platform. Note that mainprog_ptr->display_exponent is the + * "gamma" value for the entire display system, i.e., the product of + * LUT_exponent and CRT_exponent. */ + + if (png_get_gAMA(png_ptr, info_ptr, &gamma)) + png_set_gamma(png_ptr, mainprog_ptr->display_exponent, gamma); + else + png_set_gamma(png_ptr, mainprog_ptr->display_exponent, 0.45455); + + + /* we'll let libpng expand interlaced images, too */ + + mainprog_ptr->passes = png_set_interlace_handling(png_ptr); + + + /* all transformations have been registered; now update info_ptr data and + * then get rowbytes and channels */ + + png_read_update_info(png_ptr, info_ptr); + + mainprog_ptr->rowbytes = (int)png_get_rowbytes(png_ptr, info_ptr); + mainprog_ptr->channels = png_get_channels(png_ptr, info_ptr); + + + /* Call the main program to allocate memory for the image buffer and + * initialize windows and whatnot. (The old-style function-pointer + * invocation is used for compatibility with a few supposedly ANSI + * compilers that nevertheless barf on "fn_ptr()"-style syntax.) */ + + (*mainprog_ptr->mainprog_init)(); + + + /* and that takes care of initialization */ + + return; +} + + + + + +static void readpng2_row_callback(png_structp png_ptr, png_bytep new_row, + png_uint_32 row_num, int pass) +{ + mainprog_info *mainprog_ptr; + + + /* first check whether the row differs from the previous pass; if not, + * nothing to combine or display */ + + if (!new_row) + return; + + + /* retrieve the pointer to our special-purpose struct so we can access + * the old rows and image-display callback function */ + + mainprog_ptr = png_get_progressive_ptr(png_ptr); + + + /* save the pass number for optional use by the front end */ + + mainprog_ptr->pass = pass; + + + /* have libpng either combine the new row data with the existing row data + * from previous passes (if interlaced) or else just copy the new row + * into the main program's image buffer */ + + png_progressive_combine_row(png_ptr, mainprog_ptr->row_pointers[row_num], + new_row); + + + /* finally, call the display routine in the main program with the number + * of the row we just updated */ + + (*mainprog_ptr->mainprog_display_row)(row_num); + + + /* and we're ready for more */ + + return; +} + + + + + +static void readpng2_end_callback(png_structp png_ptr, png_infop info_ptr) +{ + mainprog_info *mainprog_ptr; + + + /* retrieve the pointer to our special-purpose struct */ + + mainprog_ptr = png_get_progressive_ptr(png_ptr); + + + /* let the main program know that it should flush any buffered image + * data to the display now and set a "done" flag or whatever, but note + * that it SHOULD NOT DESTROY THE PNG STRUCTS YET--in other words, do + * NOT call readpng2_cleanup() either here or in the finish_display() + * routine; wait until control returns to the main program via + * readpng2_decode_data() */ + + (*mainprog_ptr->mainprog_finish_display)(); + + + /* all done */ + + return; +} + + + + + +void readpng2_cleanup(mainprog_info *mainprog_ptr) +{ + png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr; + png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr; + + if (png_ptr && info_ptr) + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + + mainprog_ptr->png_ptr = NULL; + mainprog_ptr->info_ptr = NULL; +} + + + + + +static void readpng2_error_handler(png_structp png_ptr, png_const_charp msg) +{ + mainprog_info *mainprog_ptr; + + /* This function, aside from the extra step of retrieving the "error + * pointer" (below) and the fact that it exists within the application + * rather than within libpng, is essentially identical to libpng's + * default error handler. The second point is critical: since both + * setjmp() and longjmp() are called from the same code, they are + * guaranteed to have compatible notions of how big a jmp_buf is, + * regardless of whether _BSD_SOURCE or anything else has (or has not) + * been defined. */ + + fprintf(stderr, "readpng2 libpng error: %s\n", msg); + fflush(stderr); + + mainprog_ptr = png_get_error_ptr(png_ptr); + if (mainprog_ptr == NULL) { /* we are completely hosed now */ + fprintf(stderr, + "readpng2 severe error: jmpbuf not recoverable; terminating.\n"); + fflush(stderr); + exit(99); + } + + longjmp(mainprog_ptr->jmpbuf, 1); +} diff --git a/Engine/lib/lpng/contrib/gregbook/readpng2.h b/Engine/lib/lpng/contrib/gregbook/readpng2.h new file mode 100644 index 000000000..386e5ee65 --- /dev/null +++ b/Engine/lib/lpng/contrib/gregbook/readpng2.h @@ -0,0 +1,115 @@ +/*--------------------------------------------------------------------------- + + rpng2 - progressive-model PNG display program readpng2.h + + --------------------------------------------------------------------------- + + Copyright (c) 1998-2007 Greg Roelofs. All rights reserved. + + This software is provided "as is," without warranty of any kind, + express or implied. In no event shall the author or contributors + be held liable for any damages arising in any way from the use of + this software. + + The contents of this file are DUAL-LICENSED. You may modify and/or + redistribute this software according to the terms of one of the + following two licenses (at your option): + + + LICENSE 1 ("BSD-like with advertising clause"): + + 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. Redistributions of source code must retain the above copyright + notice, disclaimer, and this list of conditions. + 2. Redistributions in binary form must reproduce the above copyright + notice, disclaimer, and this list of conditions in the documenta- + tion and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + + This product includes software developed by Greg Roelofs + and contributors for the book, "PNG: The Definitive Guide," + published by O'Reilly and Associates. + + + LICENSE 2 (GNU GPL v2 or later): + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ---------------------------------------------------------------------------*/ + +#ifndef TRUE +# define TRUE 1 +# define FALSE 0 +#endif + +#ifndef MAX +# define MAX(a,b) ((a) > (b)? (a) : (b)) +# define MIN(a,b) ((a) < (b)? (a) : (b)) +#endif + +#ifdef DEBUG +# define Trace(x) {fprintf x ; fflush(stderr); fflush(stdout);} +#else +# define Trace(x) ; +#endif + +typedef unsigned char uch; +typedef unsigned short ush; +typedef unsigned long ulg; + +typedef struct _mainprog_info { + double display_exponent; + ulg width; + ulg height; + void *png_ptr; + void *info_ptr; + void (*mainprog_init)(void); + void (*mainprog_display_row)(ulg row_num); + void (*mainprog_finish_display)(void); + uch *image_data; + uch **row_pointers; + jmp_buf jmpbuf; + int passes; /* not used */ + int pass; + int rowbytes; + int channels; + int need_bgcolor; +#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__)) + int nommxfilters; + int nommxcombine; + int nommxinterlace; +#endif + int done; + uch bg_red; + uch bg_green; + uch bg_blue; +} mainprog_info; + + +/* prototypes for public functions in readpng2.c */ + +void readpng2_version_info(void); + +int readpng2_check_sig(uch *sig, int num); + +int readpng2_init(mainprog_info *mainprog_ptr); + +int readpng2_decode_data(mainprog_info *mainprog_ptr, uch *rawbuf, ulg length); + +void readpng2_cleanup(mainprog_info *mainprog_ptr); diff --git a/Engine/lib/lpng/contrib/gregbook/readppm.c b/Engine/lib/lpng/contrib/gregbook/readppm.c new file mode 100644 index 000000000..be9a56d95 --- /dev/null +++ b/Engine/lib/lpng/contrib/gregbook/readppm.c @@ -0,0 +1,179 @@ +/*--------------------------------------------------------------------------- + + rpng - simple PNG display program readppm.c + + --------------------------------------------------------------------------- + + This is a special-purpose replacement for readpng.c that allows binary + PPM files to be used in place of PNG images. + + --------------------------------------------------------------------------- + + Copyright (c) 1998-2007 Greg Roelofs. All rights reserved. + + This software is provided "as is," without warranty of any kind, + express or implied. In no event shall the author or contributors + be held liable for any damages arising in any way from the use of + this software. + + The contents of this file are DUAL-LICENSED. You may modify and/or + redistribute this software according to the terms of one of the + following two licenses (at your option): + + + LICENSE 1 ("BSD-like with advertising clause"): + + 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. Redistributions of source code must retain the above copyright + notice, disclaimer, and this list of conditions. + 2. Redistributions in binary form must reproduce the above copyright + notice, disclaimer, and this list of conditions in the documenta- + tion and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + + This product includes software developed by Greg Roelofs + and contributors for the book, "PNG: The Definitive Guide," + published by O'Reilly and Associates. + + + LICENSE 2 (GNU GPL v2 or later): + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ---------------------------------------------------------------------------*/ + +#include +#include + +#include "readpng.h" /* typedefs, common macros, public prototypes */ + + +ulg width, height; +int bit_depth, color_type, channels; +uch *image_data = NULL; +FILE *saved_infile; + + +void readpng_version_info() +{ + fprintf(stderr, " Compiled without libpng, zlib or PBMPLUS/NetPBM.\n"); +} + + +/* return value = 0 for success, 1 for bad sig, 2 for bad IHDR, 4 for no mem */ + +int readpng_init(FILE *infile, ulg *pWidth, ulg *pHeight) +{ + static uch ppmline[256]; + int maxval; + + + saved_infile = infile; + + fgets(ppmline, 256, infile); + if (ppmline[0] != 'P' || ppmline[1] != '6') { + fprintf(stderr, "ERROR: not a PPM file\n"); + return 1; + } + /* possible color types: P5 = grayscale (0), P6 = RGB (2), P8 = RGBA (6) */ + if (ppmline[1] == '6') { + color_type = 2; + channels = 3; + } else if (ppmline[1] == '8') { + color_type = 6; + channels = 4; + } else /* if (ppmline[1] == '5') */ { + color_type = 0; + channels = 1; + } + + do { + fgets(ppmline, 256, infile); + } while (ppmline[0] == '#'); + sscanf(ppmline, "%lu %lu", &width, &height); + + do { + fgets(ppmline, 256, infile); + } while (ppmline[0] == '#'); + sscanf(ppmline, "%d", &maxval); + if (maxval != 255) { + fprintf(stderr, "ERROR: maxval = %d\n", maxval); + return 2; + } + bit_depth = 8; + + *pWidth = width; + *pHeight = height; + + return 0; +} + + + + +/* returns 0 if succeeds, 1 if fails due to no bKGD chunk, 2 if libpng error; + * scales values to 8-bit if necessary */ + +int readpng_get_bgcolor(uch *red, uch *green, uch *blue) +{ + return 1; +} + + + + +/* display_exponent == LUT_exponent * CRT_exponent */ + +uch *readpng_get_image(double display_exponent, int *pChannels, ulg *pRowbytes) +{ + ulg rowbytes; + + + /* expand palette images to RGB, low-bit-depth grayscale images to 8 bits, + * transparency chunks to full alpha channel; strip 16-bit-per-sample + * images to 8 bits per sample; and convert grayscale to RGB[A] */ + + /* GRR WARNING: grayscale needs to be expanded and channels reset! */ + + *pRowbytes = rowbytes = channels*width; + *pChannels = channels; + + if ((image_data = (uch *)malloc(rowbytes*height)) == NULL) { + return NULL; + } + + Trace((stderr, "readpng_get_image: rowbytes = %ld, height = %ld\n", rowbytes, height)); + + + /* now we can go ahead and just read the whole image */ + + fread(image_data, 1L, rowbytes*height, saved_infile); + + + return image_data; +} + + +void readpng_cleanup(int free_image_data) +{ + if (free_image_data && image_data) { + free(image_data); + image_data = NULL; + } +} diff --git a/Engine/lib/lpng/contrib/gregbook/rpng-win.c b/Engine/lib/lpng/contrib/gregbook/rpng-win.c new file mode 100644 index 000000000..b37c00aa7 --- /dev/null +++ b/Engine/lib/lpng/contrib/gregbook/rpng-win.c @@ -0,0 +1,671 @@ +/*--------------------------------------------------------------------------- + + rpng - simple PNG display program rpng-win.c + + This program decodes and displays PNG images, with gamma correction and + optionally with a user-specified background color (in case the image has + transparency). It is very nearly the most basic PNG viewer possible. + This version is for 32-bit Windows; it may compile under 16-bit Windows + with a little tweaking (or maybe not). + + to do: + - handle quoted command-line args (especially filenames with spaces) + - have minimum window width: oh well + - use %.1023s to simplify truncation of title-bar string? + + --------------------------------------------------------------------------- + + Changelog: + - 1.00: initial public release + - 1.01: modified to allow abbreviated options; fixed long/ulong mis- + match; switched to png_jmpbuf() macro + - 1.02: added extra set of parentheses to png_jmpbuf() macro; fixed + command-line parsing bug + - 1.10: enabled "message window"/console (thanks to David Geldreich) + - 2.00: dual-licensed (added GNU GPL) + + --------------------------------------------------------------------------- + + Copyright (c) 1998-2007 Greg Roelofs. All rights reserved. + + This software is provided "as is," without warranty of any kind, + express or implied. In no event shall the author or contributors + be held liable for any damages arising in any way from the use of + this software. + + The contents of this file are DUAL-LICENSED. You may modify and/or + redistribute this software according to the terms of one of the + following two licenses (at your option): + + + LICENSE 1 ("BSD-like with advertising clause"): + + 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. Redistributions of source code must retain the above copyright + notice, disclaimer, and this list of conditions. + 2. Redistributions in binary form must reproduce the above copyright + notice, disclaimer, and this list of conditions in the documenta- + tion and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + + This product includes software developed by Greg Roelofs + and contributors for the book, "PNG: The Definitive Guide," + published by O'Reilly and Associates. + + + LICENSE 2 (GNU GPL v2 or later): + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ---------------------------------------------------------------------------*/ + +#define PROGNAME "rpng-win" +#define LONGNAME "Simple PNG Viewer for Windows" +#define VERSION "2.00 of 2 June 2007" + +#include +#include +#include +#include +#include +#include /* only for _getch() */ + +/* #define DEBUG : this enables the Trace() macros */ + +#include "readpng.h" /* typedefs, common macros, readpng prototypes */ + + +/* could just include png.h, but this macro is the only thing we need + * (name and typedefs changed to local versions); note that side effects + * only happen with alpha (which could easily be avoided with + * "ush acopy = (alpha);") */ + +#define alpha_composite(composite, fg, alpha, bg) { \ + ush temp = ((ush)(fg)*(ush)(alpha) + \ + (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128); \ + (composite) = (uch)((temp + (temp >> 8)) >> 8); \ +} + + +/* local prototypes */ +static int rpng_win_create_window(HINSTANCE hInst, int showmode); +static int rpng_win_display_image(void); +static void rpng_win_cleanup(void); +LRESULT CALLBACK rpng_win_wndproc(HWND, UINT, WPARAM, LPARAM); + + +static char titlebar[1024]; +static char *progname = PROGNAME; +static char *appname = LONGNAME; +static char *filename; +static FILE *infile; + +static char *bgstr; +static uch bg_red=0, bg_green=0, bg_blue=0; + +static double display_exponent; + +static ulg image_width, image_height, image_rowbytes; +static int image_channels; +static uch *image_data; + +/* Windows-specific variables */ +static ulg wimage_rowbytes; +static uch *dib; +static uch *wimage_data; +static BITMAPINFOHEADER *bmih; + +static HWND global_hwnd; + + + + +int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, PSTR cmd, int showmode) +{ + char *args[1024]; /* arbitrary limit, but should suffice */ + char *p, *q, **argv = args; + int argc = 0; + int rc, alen, flen; + int error = 0; + int have_bg = FALSE; + double LUT_exponent; /* just the lookup table */ + double CRT_exponent = 2.2; /* just the monitor */ + double default_display_exponent; /* whole display system */ + MSG msg; + + + filename = (char *)NULL; + + + /* First reenable console output, which normally goes to the bit bucket + * for windowed apps. Closing the console window will terminate the + * app. Thanks to David.Geldreich@realviz.com for supplying the magical + * incantation. */ + + AllocConsole(); + freopen("CONOUT$", "a", stderr); + freopen("CONOUT$", "a", stdout); + + + /* Next set the default value for our display-system exponent, i.e., + * the product of the CRT exponent and the exponent corresponding to + * the frame-buffer's lookup table (LUT), if any. This is not an + * exhaustive list of LUT values (e.g., OpenStep has a lot of weird + * ones), but it should cover 99% of the current possibilities. And + * yes, these ifdefs are completely wasted in a Windows program... */ + +#if defined(NeXT) + LUT_exponent = 1.0 / 2.2; + /* + if (some_next_function_that_returns_gamma(&next_gamma)) + LUT_exponent = 1.0 / next_gamma; + */ +#elif defined(sgi) + LUT_exponent = 1.0 / 1.7; + /* there doesn't seem to be any documented function to get the + * "gamma" value, so we do it the hard way */ + infile = fopen("/etc/config/system.glGammaVal", "r"); + if (infile) { + double sgi_gamma; + + fgets(tmpline, 80, infile); + fclose(infile); + sgi_gamma = atof(tmpline); + if (sgi_gamma > 0.0) + LUT_exponent = 1.0 / sgi_gamma; + } +#elif defined(Macintosh) + LUT_exponent = 1.8 / 2.61; + /* + if (some_mac_function_that_returns_gamma(&mac_gamma)) + LUT_exponent = mac_gamma / 2.61; + */ +#else + LUT_exponent = 1.0; /* assume no LUT: most PCs */ +#endif + + /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */ + default_display_exponent = LUT_exponent * CRT_exponent; + + + /* If the user has set the SCREEN_GAMMA environment variable as suggested + * (somewhat imprecisely) in the libpng documentation, use that; otherwise + * use the default value we just calculated. Either way, the user may + * override this via a command-line option. */ + + if ((p = getenv("SCREEN_GAMMA")) != NULL) + display_exponent = atof(p); + else + display_exponent = default_display_exponent; + + + /* Windows really hates command lines, so we have to set up our own argv. + * Note that we do NOT bother with quoted arguments here, so don't use + * filenames with spaces in 'em! */ + + argv[argc++] = PROGNAME; + p = cmd; + for (;;) { + if (*p == ' ') + while (*++p == ' ') + ; + /* now p points at the first non-space after some spaces */ + if (*p == '\0') + break; /* nothing after the spaces: done */ + argv[argc++] = q = p; + while (*q && *q != ' ') + ++q; + /* now q points at a space or the end of the string */ + if (*q == '\0') + break; /* last argv already terminated; quit */ + *q = '\0'; /* change space to terminator */ + p = q + 1; + } + argv[argc] = NULL; /* terminate the argv array itself */ + + + /* Now parse the command line for options and the PNG filename. */ + + while (*++argv && !error) { + if (!strncmp(*argv, "-gamma", 2)) { + if (!*++argv) + ++error; + else { + display_exponent = atof(*argv); + if (display_exponent <= 0.0) + ++error; + } + } else if (!strncmp(*argv, "-bgcolor", 2)) { + if (!*++argv) + ++error; + else { + bgstr = *argv; + if (strlen(bgstr) != 7 || bgstr[0] != '#') + ++error; + else + have_bg = TRUE; + } + } else { + if (**argv != '-') { + filename = *argv; + if (argv[1]) /* shouldn't be any more args after filename */ + ++error; + } else + ++error; /* not expecting any other options */ + } + } + + if (!filename) { + ++error; + } else if (!(infile = fopen(filename, "rb"))) { + fprintf(stderr, PROGNAME ": can't open PNG file [%s]\n", filename); + ++error; + } else { + if ((rc = readpng_init(infile, &image_width, &image_height)) != 0) { + switch (rc) { + case 1: + fprintf(stderr, PROGNAME + ": [%s] is not a PNG file: incorrect signature\n", + filename); + break; + case 2: + fprintf(stderr, PROGNAME + ": [%s] has bad IHDR (libpng longjmp)\n", + filename); + break; + case 4: + fprintf(stderr, PROGNAME ": insufficient memory\n"); + break; + default: + fprintf(stderr, PROGNAME + ": unknown readpng_init() error\n"); + break; + } + ++error; + } + if (error) + fclose(infile); + } + + + /* usage screen */ + + if (error) { + int ch; + + fprintf(stderr, "\n%s %s: %s\n\n", PROGNAME, VERSION, appname); + readpng_version_info(); + fprintf(stderr, "\n" + "Usage: %s [-gamma exp] [-bgcolor bg] file.png\n" + " exp \ttransfer-function exponent (``gamma'') of the display\n" + "\t\t system in floating-point format (e.g., ``%.1f''); equal\n" + "\t\t to the product of the lookup-table exponent (varies)\n" + "\t\t and the CRT exponent (usually 2.2); must be positive\n" + " bg \tdesired background color in 7-character hex RGB format\n" + "\t\t (e.g., ``#ff7700'' for orange: same as HTML colors);\n" + "\t\t used with transparent images\n" + "\nPress Q, Esc or mouse button 1 after image is displayed to quit.\n" + "Press Q or Esc to quit this usage screen.\n" + "\n", PROGNAME, default_display_exponent); + do + ch = _getch(); + while (ch != 'q' && ch != 'Q' && ch != 0x1B); + exit(1); + } else { + fprintf(stderr, "\n%s %s: %s\n", PROGNAME, VERSION, appname); + fprintf(stderr, + "\n [console window: closing this window will terminate %s]\n\n", + PROGNAME); + } + + + /* set the title-bar string, but make sure buffer doesn't overflow */ + + alen = strlen(appname); + flen = strlen(filename); + if (alen + flen + 3 > 1023) + sprintf(titlebar, "%s: ...%s", appname, filename+(alen+flen+6-1023)); + else + sprintf(titlebar, "%s: %s", appname, filename); + + + /* if the user didn't specify a background color on the command line, + * check for one in the PNG file--if not, the initialized values of 0 + * (black) will be used */ + + if (have_bg) { + unsigned r, g, b; /* this approach quiets compiler warnings */ + + sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b); + bg_red = (uch)r; + bg_green = (uch)g; + bg_blue = (uch)b; + } else if (readpng_get_bgcolor(&bg_red, &bg_green, &bg_blue) > 1) { + readpng_cleanup(TRUE); + fprintf(stderr, PROGNAME + ": libpng error while checking for background color\n"); + exit(2); + } + + + /* do the basic Windows initialization stuff, make the window and fill it + * with the background color */ + + if (rpng_win_create_window(hInst, showmode)) + exit(2); + + + /* decode the image, all at once */ + + Trace((stderr, "calling readpng_get_image()\n")) + image_data = readpng_get_image(display_exponent, &image_channels, + &image_rowbytes); + Trace((stderr, "done with readpng_get_image()\n")) + + + /* done with PNG file, so clean up to minimize memory usage (but do NOT + * nuke image_data!) */ + + readpng_cleanup(FALSE); + fclose(infile); + + if (!image_data) { + fprintf(stderr, PROGNAME ": unable to decode PNG image\n"); + exit(3); + } + + + /* display image (composite with background if requested) */ + + Trace((stderr, "calling rpng_win_display_image()\n")) + if (rpng_win_display_image()) { + free(image_data); + exit(4); + } + Trace((stderr, "done with rpng_win_display_image()\n")) + + + /* wait for the user to tell us when to quit */ + + printf( + "Done. Press Q, Esc or mouse button 1 (within image window) to quit.\n"); + fflush(stdout); + + while (GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + + /* OK, we're done: clean up all image and Windows resources and go away */ + + rpng_win_cleanup(); + + return msg.wParam; +} + + + + + +static int rpng_win_create_window(HINSTANCE hInst, int showmode) +{ + uch *dest; + int extra_width, extra_height; + ulg i, j; + WNDCLASSEX wndclass; + + +/*--------------------------------------------------------------------------- + Allocate memory for the display-specific version of the image (round up + to multiple of 4 for Windows DIB). + ---------------------------------------------------------------------------*/ + + wimage_rowbytes = ((3*image_width + 3L) >> 2) << 2; + + if (!(dib = (uch *)malloc(sizeof(BITMAPINFOHEADER) + + wimage_rowbytes*image_height))) + { + return 4; /* fail */ + } + +/*--------------------------------------------------------------------------- + Initialize the DIB. Negative height means to use top-down BMP ordering + (must be uncompressed, but that's what we want). Bit count of 1, 4 or 8 + implies a colormap of RGBX quads, but 24-bit BMPs just use B,G,R values + directly => wimage_data begins immediately after BMP header. + ---------------------------------------------------------------------------*/ + + memset(dib, 0, sizeof(BITMAPINFOHEADER)); + bmih = (BITMAPINFOHEADER *)dib; + bmih->biSize = sizeof(BITMAPINFOHEADER); + bmih->biWidth = image_width; + bmih->biHeight = -((long)image_height); + bmih->biPlanes = 1; + bmih->biBitCount = 24; + bmih->biCompression = 0; + wimage_data = dib + sizeof(BITMAPINFOHEADER); + +/*--------------------------------------------------------------------------- + Fill in background color (black by default); data are in BGR order. + ---------------------------------------------------------------------------*/ + + for (j = 0; j < image_height; ++j) { + dest = wimage_data + j*wimage_rowbytes; + for (i = image_width; i > 0; --i) { + *dest++ = bg_blue; + *dest++ = bg_green; + *dest++ = bg_red; + } + } + +/*--------------------------------------------------------------------------- + Set the window parameters. + ---------------------------------------------------------------------------*/ + + memset(&wndclass, 0, sizeof(wndclass)); + + wndclass.cbSize = sizeof(wndclass); + wndclass.style = CS_HREDRAW | CS_VREDRAW; + wndclass.lpfnWndProc = rpng_win_wndproc; + wndclass.hInstance = hInst; + wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndclass.hbrBackground = (HBRUSH)GetStockObject(DKGRAY_BRUSH); + wndclass.lpszMenuName = NULL; + wndclass.lpszClassName = progname; + wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); + + RegisterClassEx(&wndclass); + +/*--------------------------------------------------------------------------- + Finally, create the window. + ---------------------------------------------------------------------------*/ + + extra_width = 2*(GetSystemMetrics(SM_CXBORDER) + + GetSystemMetrics(SM_CXDLGFRAME)); + extra_height = 2*(GetSystemMetrics(SM_CYBORDER) + + GetSystemMetrics(SM_CYDLGFRAME)) + + GetSystemMetrics(SM_CYCAPTION); + + global_hwnd = CreateWindow(progname, titlebar, WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, image_width+extra_width, + image_height+extra_height, NULL, NULL, hInst, NULL); + + ShowWindow(global_hwnd, showmode); + UpdateWindow(global_hwnd); + + return 0; + +} /* end function rpng_win_create_window() */ + + + + + +static int rpng_win_display_image() +{ + uch *src, *dest; + uch r, g, b, a; + ulg i, row, lastrow; + RECT rect; + + + Trace((stderr, "beginning display loop (image_channels == %d)\n", + image_channels)) + Trace((stderr, "(width = %ld, rowbytes = %ld, wimage_rowbytes = %d)\n", + image_width, image_rowbytes, wimage_rowbytes)) + + +/*--------------------------------------------------------------------------- + Blast image data to buffer. This whole routine takes place before the + message loop begins, so there's no real point in any pseudo-progressive + display... + ---------------------------------------------------------------------------*/ + + for (lastrow = row = 0; row < image_height; ++row) { + src = image_data + row*image_rowbytes; + dest = wimage_data + row*wimage_rowbytes; + if (image_channels == 3) { + for (i = image_width; i > 0; --i) { + r = *src++; + g = *src++; + b = *src++; + *dest++ = b; + *dest++ = g; /* note reverse order */ + *dest++ = r; + } + } else /* if (image_channels == 4) */ { + for (i = image_width; i > 0; --i) { + r = *src++; + g = *src++; + b = *src++; + a = *src++; + if (a == 255) { + *dest++ = b; + *dest++ = g; + *dest++ = r; + } else if (a == 0) { + *dest++ = bg_blue; + *dest++ = bg_green; + *dest++ = bg_red; + } else { + /* this macro (copied from png.h) composites the + * foreground and background values and puts the + * result into the first argument; there are no + * side effects with the first argument */ + alpha_composite(*dest++, b, a, bg_blue); + alpha_composite(*dest++, g, a, bg_green); + alpha_composite(*dest++, r, a, bg_red); + } + } + } + /* display after every 16 lines */ + if (((row+1) & 0xf) == 0) { + rect.left = 0L; + rect.top = (LONG)lastrow; + rect.right = (LONG)image_width; /* possibly off by one? */ + rect.bottom = (LONG)lastrow + 16L; /* possibly off by one? */ + InvalidateRect(global_hwnd, &rect, FALSE); + UpdateWindow(global_hwnd); /* similar to XFlush() */ + lastrow = row + 1; + } + } + + Trace((stderr, "calling final image-flush routine\n")) + if (lastrow < image_height) { + rect.left = 0L; + rect.top = (LONG)lastrow; + rect.right = (LONG)image_width; /* possibly off by one? */ + rect.bottom = (LONG)image_height; /* possibly off by one? */ + InvalidateRect(global_hwnd, &rect, FALSE); + UpdateWindow(global_hwnd); /* similar to XFlush() */ + } + +/* + last param determines whether or not background is wiped before paint + InvalidateRect(global_hwnd, NULL, TRUE); + UpdateWindow(global_hwnd); + */ + + return 0; +} + + + + + +static void rpng_win_cleanup() +{ + if (image_data) { + free(image_data); + image_data = NULL; + } + + if (dib) { + free(dib); + dib = NULL; + } +} + + + + + +LRESULT CALLBACK rpng_win_wndproc(HWND hwnd, UINT iMsg, WPARAM wP, LPARAM lP) +{ + HDC hdc; + PAINTSTRUCT ps; + int rc; + + switch (iMsg) { + case WM_CREATE: + /* one-time processing here, if any */ + return 0; + + case WM_PAINT: + hdc = BeginPaint(hwnd, &ps); + /* dest */ + rc = StretchDIBits(hdc, 0, 0, image_width, image_height, + /* source */ + 0, 0, image_width, image_height, + wimage_data, (BITMAPINFO *)bmih, + /* iUsage: no clue */ + 0, SRCCOPY); + EndPaint(hwnd, &ps); + return 0; + + /* wait for the user to tell us when to quit */ + case WM_CHAR: + switch (wP) { /* only need one, so ignore repeat count */ + case 'q': + case 'Q': + case 0x1B: /* Esc key */ + PostQuitMessage(0); + } + return 0; + + case WM_LBUTTONDOWN: /* another way of quitting */ + case WM_DESTROY: + PostQuitMessage(0); + return 0; + } + + return DefWindowProc(hwnd, iMsg, wP, lP); +} diff --git a/Engine/lib/lpng/contrib/gregbook/rpng-x.c b/Engine/lib/lpng/contrib/gregbook/rpng-x.c new file mode 100644 index 000000000..947726024 --- /dev/null +++ b/Engine/lib/lpng/contrib/gregbook/rpng-x.c @@ -0,0 +1,896 @@ +/*--------------------------------------------------------------------------- + + rpng - simple PNG display program rpng-x.c + + This program decodes and displays PNG images, with gamma correction and + optionally with a user-specified background color (in case the image has + transparency). It is very nearly the most basic PNG viewer possible. + This version is for the X Window System (tested by author under Unix and + by Martin Zinser under OpenVMS; may work under OS/2 with some tweaking). + + to do: + - 8-bit (colormapped) X support + - use %.1023s to simplify truncation of title-bar string? + + --------------------------------------------------------------------------- + + Changelog: + - 1.01: initial public release + - 1.02: modified to allow abbreviated options; fixed long/ulong mis- + match; switched to png_jmpbuf() macro + - 1.10: added support for non-default visuals; fixed X pixel-conversion + - 1.11: added extra set of parentheses to png_jmpbuf() macro; fixed + command-line parsing bug + - 1.12: fixed some small X memory leaks (thanks to François Petitjean) + - 1.13: fixed XFreeGC() crash bug (thanks to Patrick Welche) + - 1.14: added support for X resources (thanks to Gerhard Niklasch) + - 2.00: dual-licensed (added GNU GPL) + + --------------------------------------------------------------------------- + + Copyright (c) 1998-2007 Greg Roelofs. All rights reserved. + + This software is provided "as is," without warranty of any kind, + express or implied. In no event shall the author or contributors + be held liable for any damages arising in any way from the use of + this software. + + The contents of this file are DUAL-LICENSED. You may modify and/or + redistribute this software according to the terms of one of the + following two licenses (at your option): + + + LICENSE 1 ("BSD-like with advertising clause"): + + 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. Redistributions of source code must retain the above copyright + notice, disclaimer, and this list of conditions. + 2. Redistributions in binary form must reproduce the above copyright + notice, disclaimer, and this list of conditions in the documenta- + tion and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + + This product includes software developed by Greg Roelofs + and contributors for the book, "PNG: The Definitive Guide," + published by O'Reilly and Associates. + + + LICENSE 2 (GNU GPL v2 or later): + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ---------------------------------------------------------------------------*/ + +#define PROGNAME "rpng-x" +#define LONGNAME "Simple PNG Viewer for X" +#define VERSION "2.00 of 2 June 2007" +#define RESNAME "rpng" /* our X resource application name */ +#define RESCLASS "Rpng" /* our X resource class name */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* #define DEBUG : this enables the Trace() macros */ + +#include "readpng.h" /* typedefs, common macros, readpng prototypes */ + + +/* could just include png.h, but this macro is the only thing we need + * (name and typedefs changed to local versions); note that side effects + * only happen with alpha (which could easily be avoided with + * "ush acopy = (alpha);") */ + +#define alpha_composite(composite, fg, alpha, bg) { \ + ush temp = ((ush)(fg)*(ush)(alpha) + \ + (ush)(bg)*(ush)(255 - (ush)(alpha)) + (ush)128); \ + (composite) = (uch)((temp + (temp >> 8)) >> 8); \ +} + + +/* local prototypes */ +static int rpng_x_create_window(void); +static int rpng_x_display_image(void); +static void rpng_x_cleanup(void); +static int rpng_x_msb(ulg u32val); + + +static char titlebar[1024], *window_name = titlebar; +static char *appname = LONGNAME; +static char *icon_name = PROGNAME; +static char *res_name = RESNAME; +static char *res_class = RESCLASS; +static char *filename; +static FILE *infile; + +static char *bgstr; +static uch bg_red=0, bg_green=0, bg_blue=0; + +static double display_exponent; + +static ulg image_width, image_height, image_rowbytes; +static int image_channels; +static uch *image_data; + +/* X-specific variables */ +static char *displayname; +static XImage *ximage; +static Display *display; +static int depth; +static Visual *visual; +static XVisualInfo *visual_list; +static int RShift, GShift, BShift; +static ulg RMask, GMask, BMask; +static Window window; +static GC gc; +static Colormap colormap; + +static int have_nondefault_visual = FALSE; +static int have_colormap = FALSE; +static int have_window = FALSE; +static int have_gc = FALSE; +/* +ulg numcolors=0, pixels[256]; +ush reds[256], greens[256], blues[256]; + */ + + + + +int main(int argc, char **argv) +{ +#ifdef sgi + char tmpline[80]; +#endif + char *p; + int rc, alen, flen; + int error = 0; + int have_bg = FALSE; + double LUT_exponent; /* just the lookup table */ + double CRT_exponent = 2.2; /* just the monitor */ + double default_display_exponent; /* whole display system */ + XEvent e; + KeySym k; + + + displayname = (char *)NULL; + filename = (char *)NULL; + + + /* First set the default value for our display-system exponent, i.e., + * the product of the CRT exponent and the exponent corresponding to + * the frame-buffer's lookup table (LUT), if any. This is not an + * exhaustive list of LUT values (e.g., OpenStep has a lot of weird + * ones), but it should cover 99% of the current possibilities. */ + +#if defined(NeXT) + LUT_exponent = 1.0 / 2.2; + /* + if (some_next_function_that_returns_gamma(&next_gamma)) + LUT_exponent = 1.0 / next_gamma; + */ +#elif defined(sgi) + LUT_exponent = 1.0 / 1.7; + /* there doesn't seem to be any documented function to get the + * "gamma" value, so we do it the hard way */ + infile = fopen("/etc/config/system.glGammaVal", "r"); + if (infile) { + double sgi_gamma; + + fgets(tmpline, 80, infile); + fclose(infile); + sgi_gamma = atof(tmpline); + if (sgi_gamma > 0.0) + LUT_exponent = 1.0 / sgi_gamma; + } +#elif defined(Macintosh) + LUT_exponent = 1.8 / 2.61; + /* + if (some_mac_function_that_returns_gamma(&mac_gamma)) + LUT_exponent = mac_gamma / 2.61; + */ +#else + LUT_exponent = 1.0; /* assume no LUT: most PCs */ +#endif + + /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */ + default_display_exponent = LUT_exponent * CRT_exponent; + + + /* If the user has set the SCREEN_GAMMA environment variable as suggested + * (somewhat imprecisely) in the libpng documentation, use that; otherwise + * use the default value we just calculated. Either way, the user may + * override this via a command-line option. */ + + if ((p = getenv("SCREEN_GAMMA")) != NULL) + display_exponent = atof(p); + else + display_exponent = default_display_exponent; + + + /* Now parse the command line for options and the PNG filename. */ + + while (*++argv && !error) { + if (!strncmp(*argv, "-display", 2)) { + if (!*++argv) + ++error; + else + displayname = *argv; + } else if (!strncmp(*argv, "-gamma", 2)) { + if (!*++argv) + ++error; + else { + display_exponent = atof(*argv); + if (display_exponent <= 0.0) + ++error; + } + } else if (!strncmp(*argv, "-bgcolor", 2)) { + if (!*++argv) + ++error; + else { + bgstr = *argv; + if (strlen(bgstr) != 7 || bgstr[0] != '#') + ++error; + else + have_bg = TRUE; + } + } else { + if (**argv != '-') { + filename = *argv; + if (argv[1]) /* shouldn't be any more args after filename */ + ++error; + } else + ++error; /* not expecting any other options */ + } + } + + if (!filename) { + ++error; + } else if (!(infile = fopen(filename, "rb"))) { + fprintf(stderr, PROGNAME ": can't open PNG file [%s]\n", filename); + ++error; + } else { + if ((rc = readpng_init(infile, &image_width, &image_height)) != 0) { + switch (rc) { + case 1: + fprintf(stderr, PROGNAME + ": [%s] is not a PNG file: incorrect signature\n", + filename); + break; + case 2: + fprintf(stderr, PROGNAME + ": [%s] has bad IHDR (libpng longjmp)\n", + filename); + break; + case 4: + fprintf(stderr, PROGNAME ": insufficient memory\n"); + break; + default: + fprintf(stderr, PROGNAME + ": unknown readpng_init() error\n"); + break; + } + ++error; + } else { + display = XOpenDisplay(displayname); + if (!display) { + readpng_cleanup(TRUE); + fprintf(stderr, PROGNAME ": can't open X display [%s]\n", + displayname? displayname : "default"); + ++error; + } + } + if (error) + fclose(infile); + } + + + /* usage screen */ + + if (error) { + fprintf(stderr, "\n%s %s: %s\n", PROGNAME, VERSION, appname); + readpng_version_info(); + fprintf(stderr, "\n" + "Usage: %s [-display xdpy] [-gamma exp] [-bgcolor bg] file.png\n" + " xdpy\tname of the target X display (e.g., ``hostname:0'')\n" + " exp \ttransfer-function exponent (``gamma'') of the display\n" + "\t\t system in floating-point format (e.g., ``%.1f''); equal\n" + "\t\t to the product of the lookup-table exponent (varies)\n" + "\t\t and the CRT exponent (usually 2.2); must be positive\n" + " bg \tdesired background color in 7-character hex RGB format\n" + "\t\t (e.g., ``#ff7700'' for orange: same as HTML colors);\n" + "\t\t used with transparent images\n" + "\nPress Q, Esc or mouse button 1 (within image window, after image\n" + "is displayed) to quit.\n" + "\n", PROGNAME, default_display_exponent); + exit(1); + } + + + /* set the title-bar string, but make sure buffer doesn't overflow */ + + alen = strlen(appname); + flen = strlen(filename); + if (alen + flen + 3 > 1023) + sprintf(titlebar, "%s: ...%s", appname, filename+(alen+flen+6-1023)); + else + sprintf(titlebar, "%s: %s", appname, filename); + + + /* if the user didn't specify a background color on the command line, + * check for one in the PNG file--if not, the initialized values of 0 + * (black) will be used */ + + if (have_bg) { + unsigned r, g, b; /* this approach quiets compiler warnings */ + + sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b); + bg_red = (uch)r; + bg_green = (uch)g; + bg_blue = (uch)b; + } else if (readpng_get_bgcolor(&bg_red, &bg_green, &bg_blue) > 1) { + readpng_cleanup(TRUE); + fprintf(stderr, PROGNAME + ": libpng error while checking for background color\n"); + exit(2); + } + + + /* do the basic X initialization stuff, make the window and fill it + * with the background color */ + + if (rpng_x_create_window()) + exit(2); + + + /* decode the image, all at once */ + + Trace((stderr, "calling readpng_get_image()\n")) + image_data = readpng_get_image(display_exponent, &image_channels, + &image_rowbytes); + Trace((stderr, "done with readpng_get_image()\n")) + + + /* done with PNG file, so clean up to minimize memory usage (but do NOT + * nuke image_data!) */ + + readpng_cleanup(FALSE); + fclose(infile); + + if (!image_data) { + fprintf(stderr, PROGNAME ": unable to decode PNG image\n"); + exit(3); + } + + + /* display image (composite with background if requested) */ + + Trace((stderr, "calling rpng_x_display_image()\n")) + if (rpng_x_display_image()) { + free(image_data); + exit(4); + } + Trace((stderr, "done with rpng_x_display_image()\n")) + + + /* wait for the user to tell us when to quit */ + + printf( + "Done. Press Q, Esc or mouse button 1 (within image window) to quit.\n"); + fflush(stdout); + + do + XNextEvent(display, &e); + while (!(e.type == ButtonPress && e.xbutton.button == Button1) && + !(e.type == KeyPress && /* v--- or 1 for shifted keys */ + ((k = XLookupKeysym(&e.xkey, 0)) == XK_q || k == XK_Escape) )); + + + /* OK, we're done: clean up all image and X resources and go away */ + + rpng_x_cleanup(); + + return 0; +} + + + + + +static int rpng_x_create_window(void) +{ + uch *xdata; + int need_colormap = FALSE; + int screen, pad; + ulg bg_pixel = 0L; + ulg attrmask; + Window root; + XEvent e; + XGCValues gcvalues; + XSetWindowAttributes attr; + XTextProperty windowName, *pWindowName = &windowName; + XTextProperty iconName, *pIconName = &iconName; + XVisualInfo visual_info; + XSizeHints *size_hints; + XWMHints *wm_hints; + XClassHint *class_hints; + + + screen = DefaultScreen(display); + depth = DisplayPlanes(display, screen); + root = RootWindow(display, screen); + +#ifdef DEBUG + XSynchronize(display, True); +#endif + +#if 0 +/* GRR: add 8-bit support */ + if (/* depth != 8 && */ depth != 16 && depth != 24 && depth != 32) { + fprintf(stderr, + "screen depth %d not supported (only 16-, 24- or 32-bit TrueColor)\n", + depth); + return 2; + } + + XMatchVisualInfo(display, screen, depth, + (depth == 8)? PseudoColor : TrueColor, &visual_info); + visual = visual_info.visual; +#else + if (depth != 16 && depth != 24 && depth != 32) { + int visuals_matched = 0; + + Trace((stderr, "default depth is %d: checking other visuals\n", + depth)) + + /* 24-bit first */ + visual_info.screen = screen; + visual_info.depth = 24; + visual_list = XGetVisualInfo(display, + VisualScreenMask | VisualDepthMask, &visual_info, &visuals_matched); + if (visuals_matched == 0) { +/* GRR: add 15-, 16- and 32-bit TrueColor visuals (also DirectColor?) */ + fprintf(stderr, "default screen depth %d not supported, and no" + " 24-bit visuals found\n", depth); + return 2; + } + Trace((stderr, "XGetVisualInfo() returned %d 24-bit visuals\n", + visuals_matched)) + visual = visual_list[0].visual; + depth = visual_list[0].depth; +/* + colormap_size = visual_list[0].colormap_size; + visual_class = visual->class; + visualID = XVisualIDFromVisual(visual); + */ + have_nondefault_visual = TRUE; + need_colormap = TRUE; + } else { + XMatchVisualInfo(display, screen, depth, TrueColor, &visual_info); + visual = visual_info.visual; + } +#endif + + RMask = visual->red_mask; + GMask = visual->green_mask; + BMask = visual->blue_mask; + +/* GRR: add/check 8-bit support */ + if (depth == 8 || need_colormap) { + colormap = XCreateColormap(display, root, visual, AllocNone); + if (!colormap) { + fprintf(stderr, "XCreateColormap() failed\n"); + return 2; + } + have_colormap = TRUE; + } + if (depth == 15 || depth == 16) { + RShift = 15 - rpng_x_msb(RMask); /* these are right-shifts */ + GShift = 15 - rpng_x_msb(GMask); + BShift = 15 - rpng_x_msb(BMask); + } else if (depth > 16) { +#define NO_24BIT_MASKS +#ifdef NO_24BIT_MASKS + RShift = rpng_x_msb(RMask) - 7; /* these are left-shifts */ + GShift = rpng_x_msb(GMask) - 7; + BShift = rpng_x_msb(BMask) - 7; +#else + RShift = 7 - rpng_x_msb(RMask); /* these are right-shifts, too */ + GShift = 7 - rpng_x_msb(GMask); + BShift = 7 - rpng_x_msb(BMask); +#endif + } + if (depth >= 15 && (RShift < 0 || GShift < 0 || BShift < 0)) { + fprintf(stderr, "rpng internal logic error: negative X shift(s)!\n"); + return 2; + } + +/*--------------------------------------------------------------------------- + Finally, create the window. + ---------------------------------------------------------------------------*/ + + attr.backing_store = Always; + attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask; + attrmask = CWBackingStore | CWEventMask; + if (have_nondefault_visual) { + attr.colormap = colormap; + attr.background_pixel = 0; + attr.border_pixel = 1; + attrmask |= CWColormap | CWBackPixel | CWBorderPixel; + } + + window = XCreateWindow(display, root, 0, 0, image_width, image_height, 0, + depth, InputOutput, visual, attrmask, &attr); + + if (window == None) { + fprintf(stderr, "XCreateWindow() failed\n"); + return 2; + } else + have_window = TRUE; + + if (depth == 8) + XSetWindowColormap(display, window, colormap); + + if (!XStringListToTextProperty(&window_name, 1, pWindowName)) + pWindowName = NULL; + if (!XStringListToTextProperty(&icon_name, 1, pIconName)) + pIconName = NULL; + + /* OK if any hints allocation fails; XSetWMProperties() allows NULLs */ + + if ((size_hints = XAllocSizeHints()) != NULL) { + /* window will not be resizable */ + size_hints->flags = PMinSize | PMaxSize; + size_hints->min_width = size_hints->max_width = (int)image_width; + size_hints->min_height = size_hints->max_height = (int)image_height; + } + + if ((wm_hints = XAllocWMHints()) != NULL) { + wm_hints->initial_state = NormalState; + wm_hints->input = True; + /* wm_hints->icon_pixmap = icon_pixmap; */ + wm_hints->flags = StateHint | InputHint /* | IconPixmapHint */ ; + } + + if ((class_hints = XAllocClassHint()) != NULL) { + class_hints->res_name = res_name; + class_hints->res_class = res_class; + } + + XSetWMProperties(display, window, pWindowName, pIconName, NULL, 0, + size_hints, wm_hints, class_hints); + + /* various properties and hints no longer needed; free memory */ + if (pWindowName) + XFree(pWindowName->value); + if (pIconName) + XFree(pIconName->value); + if (size_hints) + XFree(size_hints); + if (wm_hints) + XFree(wm_hints); + if (class_hints) + XFree(class_hints); + + XMapWindow(display, window); + + gc = XCreateGC(display, window, 0, &gcvalues); + have_gc = TRUE; + +/*--------------------------------------------------------------------------- + Fill window with the specified background color. + ---------------------------------------------------------------------------*/ + + if (depth == 24 || depth == 32) { + bg_pixel = ((ulg)bg_red << RShift) | + ((ulg)bg_green << GShift) | + ((ulg)bg_blue << BShift); + } else if (depth == 16) { + bg_pixel = ((((ulg)bg_red << 8) >> RShift) & RMask) | + ((((ulg)bg_green << 8) >> GShift) & GMask) | + ((((ulg)bg_blue << 8) >> BShift) & BMask); + } else /* depth == 8 */ { + + /* GRR: add 8-bit support */ + + } + + XSetForeground(display, gc, bg_pixel); + XFillRectangle(display, window, gc, 0, 0, image_width, image_height); + +/*--------------------------------------------------------------------------- + Wait for first Expose event to do any drawing, then flush. + ---------------------------------------------------------------------------*/ + + do + XNextEvent(display, &e); + while (e.type != Expose || e.xexpose.count); + + XFlush(display); + +/*--------------------------------------------------------------------------- + Allocate memory for the X- and display-specific version of the image. + ---------------------------------------------------------------------------*/ + + if (depth == 24 || depth == 32) { + xdata = (uch *)malloc(4*image_width*image_height); + pad = 32; + } else if (depth == 16) { + xdata = (uch *)malloc(2*image_width*image_height); + pad = 16; + } else /* depth == 8 */ { + xdata = (uch *)malloc(image_width*image_height); + pad = 8; + } + + if (!xdata) { + fprintf(stderr, PROGNAME ": unable to allocate image memory\n"); + return 4; + } + + ximage = XCreateImage(display, visual, depth, ZPixmap, 0, + (char *)xdata, image_width, image_height, pad, 0); + + if (!ximage) { + fprintf(stderr, PROGNAME ": XCreateImage() failed\n"); + free(xdata); + return 3; + } + + /* to avoid testing the byte order every pixel (or doubling the size of + * the drawing routine with a giant if-test), we arbitrarily set the byte + * order to MSBFirst and let Xlib worry about inverting things on little- + * endian machines (like Linux/x86, old VAXen, etc.)--this is not the most + * efficient approach (the giant if-test would be better), but in the + * interest of clarity, we take the easy way out... */ + + ximage->byte_order = MSBFirst; + + return 0; + +} /* end function rpng_x_create_window() */ + + + + + +static int rpng_x_display_image(void) +{ + uch *src; + char *dest; + uch r, g, b, a; + ulg i, row, lastrow = 0; + ulg pixel; + int ximage_rowbytes = ximage->bytes_per_line; +/* int bpp = ximage->bits_per_pixel; */ + + + Trace((stderr, "beginning display loop (image_channels == %d)\n", + image_channels)) + Trace((stderr, " (width = %ld, rowbytes = %ld, ximage_rowbytes = %d)\n", + image_width, image_rowbytes, ximage_rowbytes)) + Trace((stderr, " (bpp = %d)\n", ximage->bits_per_pixel)) + Trace((stderr, " (byte_order = %s)\n", ximage->byte_order == MSBFirst? + "MSBFirst" : (ximage->byte_order == LSBFirst? "LSBFirst" : "unknown"))) + + if (depth == 24 || depth == 32) { + ulg red, green, blue; + + for (lastrow = row = 0; row < image_height; ++row) { + src = image_data + row*image_rowbytes; + dest = ximage->data + row*ximage_rowbytes; + if (image_channels == 3) { + for (i = image_width; i > 0; --i) { + red = *src++; + green = *src++; + blue = *src++; +#ifdef NO_24BIT_MASKS + pixel = (red << RShift) | + (green << GShift) | + (blue << BShift); + /* recall that we set ximage->byte_order = MSBFirst above */ + /* GRR BUG: this assumes bpp == 32, but may be 24: */ + *dest++ = (char)((pixel >> 24) & 0xff); + *dest++ = (char)((pixel >> 16) & 0xff); + *dest++ = (char)((pixel >> 8) & 0xff); + *dest++ = (char)( pixel & 0xff); +#else + red = (RShift < 0)? red << (-RShift) : red >> RShift; + green = (GShift < 0)? green << (-GShift) : green >> GShift; + blue = (BShift < 0)? blue << (-BShift) : blue >> BShift; + pixel = (red & RMask) | (green & GMask) | (blue & BMask); + /* recall that we set ximage->byte_order = MSBFirst above */ + *dest++ = (char)((pixel >> 24) & 0xff); + *dest++ = (char)((pixel >> 16) & 0xff); + *dest++ = (char)((pixel >> 8) & 0xff); + *dest++ = (char)( pixel & 0xff); +#endif + } + } else /* if (image_channels == 4) */ { + for (i = image_width; i > 0; --i) { + r = *src++; + g = *src++; + b = *src++; + a = *src++; + if (a == 255) { + red = r; + green = g; + blue = b; + } else if (a == 0) { + red = bg_red; + green = bg_green; + blue = bg_blue; + } else { + /* this macro (from png.h) composites the foreground + * and background values and puts the result into the + * first argument */ + alpha_composite(red, r, a, bg_red); + alpha_composite(green, g, a, bg_green); + alpha_composite(blue, b, a, bg_blue); + } + pixel = (red << RShift) | + (green << GShift) | + (blue << BShift); + /* recall that we set ximage->byte_order = MSBFirst above */ + *dest++ = (char)((pixel >> 24) & 0xff); + *dest++ = (char)((pixel >> 16) & 0xff); + *dest++ = (char)((pixel >> 8) & 0xff); + *dest++ = (char)( pixel & 0xff); + } + } + /* display after every 16 lines */ + if (((row+1) & 0xf) == 0) { + XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0, + (int)lastrow, image_width, 16); + XFlush(display); + lastrow = row + 1; + } + } + + } else if (depth == 16) { + ush red, green, blue; + + for (lastrow = row = 0; row < image_height; ++row) { + src = image_data + row*image_rowbytes; + dest = ximage->data + row*ximage_rowbytes; + if (image_channels == 3) { + for (i = image_width; i > 0; --i) { + red = ((ush)(*src) << 8); + ++src; + green = ((ush)(*src) << 8); + ++src; + blue = ((ush)(*src) << 8); + ++src; + pixel = ((red >> RShift) & RMask) | + ((green >> GShift) & GMask) | + ((blue >> BShift) & BMask); + /* recall that we set ximage->byte_order = MSBFirst above */ + *dest++ = (char)((pixel >> 8) & 0xff); + *dest++ = (char)( pixel & 0xff); + } + } else /* if (image_channels == 4) */ { + for (i = image_width; i > 0; --i) { + r = *src++; + g = *src++; + b = *src++; + a = *src++; + if (a == 255) { + red = ((ush)r << 8); + green = ((ush)g << 8); + blue = ((ush)b << 8); + } else if (a == 0) { + red = ((ush)bg_red << 8); + green = ((ush)bg_green << 8); + blue = ((ush)bg_blue << 8); + } else { + /* this macro (from png.h) composites the foreground + * and background values and puts the result back into + * the first argument (== fg byte here: safe) */ + alpha_composite(r, r, a, bg_red); + alpha_composite(g, g, a, bg_green); + alpha_composite(b, b, a, bg_blue); + red = ((ush)r << 8); + green = ((ush)g << 8); + blue = ((ush)b << 8); + } + pixel = ((red >> RShift) & RMask) | + ((green >> GShift) & GMask) | + ((blue >> BShift) & BMask); + /* recall that we set ximage->byte_order = MSBFirst above */ + *dest++ = (char)((pixel >> 8) & 0xff); + *dest++ = (char)( pixel & 0xff); + } + } + /* display after every 16 lines */ + if (((row+1) & 0xf) == 0) { + XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0, + (int)lastrow, image_width, 16); + XFlush(display); + lastrow = row + 1; + } + } + + } else /* depth == 8 */ { + + /* GRR: add 8-bit support */ + + } + + Trace((stderr, "calling final XPutImage()\n")) + if (lastrow < image_height) { + XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0, + (int)lastrow, image_width, image_height-lastrow); + XFlush(display); + } + + return 0; +} + + + + +static void rpng_x_cleanup(void) +{ + if (image_data) { + free(image_data); + image_data = NULL; + } + + if (ximage) { + if (ximage->data) { + free(ximage->data); /* we allocated it, so we free it */ + ximage->data = (char *)NULL; /* instead of XDestroyImage() */ + } + XDestroyImage(ximage); + ximage = NULL; + } + + if (have_gc) + XFreeGC(display, gc); + + if (have_window) + XDestroyWindow(display, window); + + if (have_colormap) + XFreeColormap(display, colormap); + + if (have_nondefault_visual) + XFree(visual_list); +} + + + + + +static int rpng_x_msb(ulg u32val) +{ + int i; + + for (i = 31; i >= 0; --i) { + if (u32val & 0x80000000L) + break; + u32val <<= 1; + } + return i; +} diff --git a/Engine/lib/lpng/contrib/gregbook/rpng2-win.c b/Engine/lib/lpng/contrib/gregbook/rpng2-win.c new file mode 100644 index 000000000..dfbd6b16b --- /dev/null +++ b/Engine/lib/lpng/contrib/gregbook/rpng2-win.c @@ -0,0 +1,1191 @@ +/*--------------------------------------------------------------------------- + + rpng2 - progressive-model PNG display program rpng2-win.c + + This program decodes and displays PNG files progressively, as if it were + a web browser (though the front end is only set up to read from files). + It supports gamma correction, user-specified background colors, and user- + specified background patterns (for transparent images). This version is + for 32-bit Windows; it may compile under 16-bit Windows with a little + tweaking (or maybe not). Thanks to Adam Costello and Pieter S. van der + Meulen for the "diamond" and "radial waves" patterns, respectively. + + to do: + - handle quoted command-line args (especially filenames with spaces) + - finish resizable checkerboard-gradient (sizes 4-128?) + - use %.1023s to simplify truncation of title-bar string? + - have minimum window width: oh well + + --------------------------------------------------------------------------- + + Changelog: + - 1.01: initial public release + - 1.02: fixed cut-and-paste error in usage screen (oops...) + - 1.03: modified to allow abbreviated options + - 1.04: removed bogus extra argument from usage fprintf() [Glenn R-P?]; + fixed command-line parsing bug + - 1.10: enabled "message window"/console (thanks to David Geldreich) + - 1.20: added runtime MMX-enabling/disabling and new -mmx* options + - 1.21: made minor tweak to usage screen to fit within 25-line console + - 1.22: added AMD64/EM64T support (__x86_64__) + - 2.00: dual-licensed (added GNU GPL) + + --------------------------------------------------------------------------- + + Copyright (c) 1998-2007 Greg Roelofs. All rights reserved. + + This software is provided "as is," without warranty of any kind, + express or implied. In no event shall the author or contributors + be held liable for any damages arising in any way from the use of + this software. + + The contents of this file are DUAL-LICENSED. You may modify and/or + redistribute this software according to the terms of one of the + following two licenses (at your option): + + + LICENSE 1 ("BSD-like with advertising clause"): + + 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. Redistributions of source code must retain the above copyright + notice, disclaimer, and this list of conditions. + 2. Redistributions in binary form must reproduce the above copyright + notice, disclaimer, and this list of conditions in the documenta- + tion and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + + This product includes software developed by Greg Roelofs + and contributors for the book, "PNG: The Definitive Guide," + published by O'Reilly and Associates. + + + LICENSE 2 (GNU GPL v2 or later): + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ---------------------------------------------------------------------------*/ + +#define PROGNAME "rpng2-win" +#define LONGNAME "Progressive PNG Viewer for Windows" +#define VERSION "2.00 of 2 June 2007" + +#include +#include +#include +#include /* for jmpbuf declaration in readpng2.h */ +#include +#include /* only for PvdM background code */ +#include +#include /* only for _getch() */ + +/* all for PvdM background code: */ +#ifndef PI +# define PI 3.141592653589793238 +#endif +#define PI_2 (PI*0.5) +#define INV_PI_360 (360.0 / PI) +#define MAX(a,b) (a>b?a:b) +#define MIN(a,b) (a> 8)) >> 8); \ +} + + +#define INBUFSIZE 4096 /* with pseudo-timing on (1 sec delay/block), this + * block size corresponds roughly to a download + * speed 10% faster than theoretical 33.6K maximum + * (assuming 8 data bits, 1 stop bit and no other + * overhead) */ + +/* local prototypes */ +static void rpng2_win_init(void); +static int rpng2_win_create_window(void); +static int rpng2_win_load_bg_image(void); +static void rpng2_win_display_row(ulg row); +static void rpng2_win_finish_display(void); +static void rpng2_win_cleanup(void); +LRESULT CALLBACK rpng2_win_wndproc(HWND, UINT, WPARAM, LPARAM); + + +static char titlebar[1024]; +static char *progname = PROGNAME; +static char *appname = LONGNAME; +static char *filename; +static FILE *infile; + +static mainprog_info rpng2_info; + +static uch inbuf[INBUFSIZE]; +static int incount; + +static int pat = 6; /* must be less than num_bgpat */ +static int bg_image = 0; +static int bgscale = 16; +static ulg bg_rowbytes; +static uch *bg_data; + +static struct rgb_color { + uch r, g, b; +} rgb[] = { + { 0, 0, 0}, /* 0: black */ + {255, 255, 255}, /* 1: white */ + {173, 132, 57}, /* 2: tan */ + { 64, 132, 0}, /* 3: medium green */ + {189, 117, 1}, /* 4: gold */ + {253, 249, 1}, /* 5: yellow */ + { 0, 0, 255}, /* 6: blue */ + { 0, 0, 120}, /* 7: medium blue */ + {255, 0, 255}, /* 8: magenta */ + { 64, 0, 64}, /* 9: dark magenta */ + {255, 0, 0}, /* 10: red */ + { 64, 0, 0}, /* 11: dark red */ + {255, 127, 0}, /* 12: orange */ + {192, 96, 0}, /* 13: darker orange */ + { 24, 60, 0}, /* 14: dark green-yellow */ + { 85, 125, 200} /* 15: ice blue */ +}; +/* not used for now, but should be for error-checking: +static int num_rgb = sizeof(rgb) / sizeof(struct rgb_color); + */ + +/* + This whole struct is a fairly cheesy way to keep the number of + command-line options to a minimum. The radial-waves background + type is a particularly poor fit to the integer elements of the + struct...but a few macros and a little fixed-point math will do + wonders for ya. + + type bits: + F E D C B A 9 8 7 6 5 4 3 2 1 0 + | | | | | + | | +-+-+-- 0 = sharp-edged checkerboard + | | 1 = soft diamonds + | | 2 = radial waves + | | 3-7 = undefined + | +-- gradient #2 inverted? + +-- alternating columns inverted? + */ +static struct background_pattern { + ush type; + int rgb1_max, rgb1_min; /* or bg_freq, bg_gray */ + int rgb2_max, rgb2_min; /* or bg_bsat, bg_brot (both scaled by 10)*/ +} bg[] = { + {0+8, 2,0, 1,15}, /* checkered: tan/black vs. white/ice blue */ + {0+24, 2,0, 1,0}, /* checkered: tan/black vs. white/black */ + {0+8, 4,5, 0,2}, /* checkered: gold/yellow vs. black/tan */ + {0+8, 4,5, 0,6}, /* checkered: gold/yellow vs. black/blue */ + {0, 7,0, 8,9}, /* checkered: deep blue/black vs. magenta */ + {0+8, 13,0, 5,14}, /* checkered: orange/black vs. yellow */ + {0+8, 12,0, 10,11}, /* checkered: orange/black vs. red */ + {1, 7,0, 8,0}, /* diamonds: deep blue/black vs. magenta */ + {1, 12,0, 11,0}, /* diamonds: orange vs. dark red */ + {1, 10,0, 7,0}, /* diamonds: red vs. medium blue */ + {1, 4,0, 5,0}, /* diamonds: gold vs. yellow */ + {1, 3,0, 0,0}, /* diamonds: medium green vs. black */ + {2, 16, 100, 20, 0}, /* radial: ~hard radial color-beams */ + {2, 18, 100, 10, 2}, /* radial: soft, curved radial color-beams */ + {2, 16, 256, 100, 250}, /* radial: very tight spiral */ + {2, 10000, 256, 11, 0} /* radial: dipole-moire' (almost fractal) */ +}; +static int num_bgpat = sizeof(bg) / sizeof(struct background_pattern); + + +/* Windows-specific global variables (could go in struct, but messy...) */ +static ulg wimage_rowbytes; +static uch *dib; +static uch *wimage_data; +static BITMAPINFOHEADER *bmih; + +static HWND global_hwnd; +static HINSTANCE global_hInst; +static int global_showmode; + + + + +int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, PSTR cmd, int showmode) +{ + char *args[1024]; /* arbitrary limit, but should suffice */ + char **argv = args; + char *p, *q, *bgstr = NULL; + int argc = 0; + int rc, alen, flen; + int error = 0; + int timing = FALSE; + int have_bg = FALSE; + double LUT_exponent; /* just the lookup table */ + double CRT_exponent = 2.2; /* just the monitor */ + double default_display_exponent; /* whole display system */ + MSG msg; + + + /* First initialize a few things, just to be sure--memset takes care of + * default background color (black), booleans (FALSE), pointers (NULL), + * etc. */ + + global_hInst = hInst; + global_showmode = showmode; + filename = (char *)NULL; + memset(&rpng2_info, 0, sizeof(mainprog_info)); + + + /* Next reenable console output, which normally goes to the bit bucket + * for windowed apps. Closing the console window will terminate the + * app. Thanks to David.Geldreich@realviz.com for supplying the magical + * incantation. */ + + AllocConsole(); + freopen("CONOUT$", "a", stderr); + freopen("CONOUT$", "a", stdout); + + + /* Set the default value for our display-system exponent, i.e., the + * product of the CRT exponent and the exponent corresponding to + * the frame-buffer's lookup table (LUT), if any. This is not an + * exhaustive list of LUT values (e.g., OpenStep has a lot of weird + * ones), but it should cover 99% of the current possibilities. And + * yes, these ifdefs are completely wasted in a Windows program... */ + +#if defined(NeXT) + /* third-party utilities can modify the default LUT exponent */ + LUT_exponent = 1.0 / 2.2; + /* + if (some_next_function_that_returns_gamma(&next_gamma)) + LUT_exponent = 1.0 / next_gamma; + */ +#elif defined(sgi) + LUT_exponent = 1.0 / 1.7; + /* there doesn't seem to be any documented function to + * get the "gamma" value, so we do it the hard way */ + infile = fopen("/etc/config/system.glGammaVal", "r"); + if (infile) { + double sgi_gamma; + + fgets(tmpline, 80, infile); + fclose(infile); + sgi_gamma = atof(tmpline); + if (sgi_gamma > 0.0) + LUT_exponent = 1.0 / sgi_gamma; + } +#elif defined(Macintosh) + LUT_exponent = 1.8 / 2.61; + /* + if (some_mac_function_that_returns_gamma(&mac_gamma)) + LUT_exponent = mac_gamma / 2.61; + */ +#else + LUT_exponent = 1.0; /* assume no LUT: most PCs */ +#endif + + /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */ + default_display_exponent = LUT_exponent * CRT_exponent; + + + /* If the user has set the SCREEN_GAMMA environment variable as suggested + * (somewhat imprecisely) in the libpng documentation, use that; otherwise + * use the default value we just calculated. Either way, the user may + * override this via a command-line option. */ + + if ((p = getenv("SCREEN_GAMMA")) != NULL) + rpng2_info.display_exponent = atof(p); + else + rpng2_info.display_exponent = default_display_exponent; + + + /* Windows really hates command lines, so we have to set up our own argv. + * Note that we do NOT bother with quoted arguments here, so don't use + * filenames with spaces in 'em! */ + + argv[argc++] = PROGNAME; + p = cmd; + for (;;) { + if (*p == ' ') + while (*++p == ' ') + ; + /* now p points at the first non-space after some spaces */ + if (*p == '\0') + break; /* nothing after the spaces: done */ + argv[argc++] = q = p; + while (*q && *q != ' ') + ++q; + /* now q points at a space or the end of the string */ + if (*q == '\0') + break; /* last argv already terminated; quit */ + *q = '\0'; /* change space to terminator */ + p = q + 1; + } + argv[argc] = NULL; /* terminate the argv array itself */ + + + /* Now parse the command line for options and the PNG filename. */ + + while (*++argv && !error) { + if (!strncmp(*argv, "-gamma", 2)) { + if (!*++argv) + ++error; + else { + rpng2_info.display_exponent = atof(*argv); + if (rpng2_info.display_exponent <= 0.0) + ++error; + } + } else if (!strncmp(*argv, "-bgcolor", 4)) { + if (!*++argv) + ++error; + else { + bgstr = *argv; + if (strlen(bgstr) != 7 || bgstr[0] != '#') + ++error; + else { + have_bg = TRUE; + bg_image = FALSE; + } + } + } else if (!strncmp(*argv, "-bgpat", 4)) { + if (!*++argv) + ++error; + else { + pat = atoi(*argv) - 1; + if (pat < 0 || pat >= num_bgpat) + ++error; + else { + bg_image = TRUE; + have_bg = FALSE; + } + } + } else if (!strncmp(*argv, "-timing", 2)) { + timing = TRUE; +#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__)) + } else if (!strncmp(*argv, "-nommxfilters", 7)) { + rpng2_info.nommxfilters = TRUE; + } else if (!strncmp(*argv, "-nommxcombine", 7)) { + rpng2_info.nommxcombine = TRUE; + } else if (!strncmp(*argv, "-nommxinterlace", 7)) { + rpng2_info.nommxinterlace = TRUE; + } else if (!strcmp(*argv, "-nommx")) { + rpng2_info.nommxfilters = TRUE; + rpng2_info.nommxcombine = TRUE; + rpng2_info.nommxinterlace = TRUE; +#endif + } else { + if (**argv != '-') { + filename = *argv; + if (argv[1]) /* shouldn't be any more args after filename */ + ++error; + } else + ++error; /* not expecting any other options */ + } + } + + if (!filename) { + ++error; + } else if (!(infile = fopen(filename, "rb"))) { + fprintf(stderr, PROGNAME ": can't open PNG file [%s]\n", filename); + ++error; + } else { + incount = fread(inbuf, 1, INBUFSIZE, infile); + if (incount < 8 || !readpng2_check_sig(inbuf, 8)) { + fprintf(stderr, PROGNAME + ": [%s] is not a PNG file: incorrect signature\n", + filename); + ++error; + } else if ((rc = readpng2_init(&rpng2_info)) != 0) { + switch (rc) { + case 2: + fprintf(stderr, PROGNAME + ": [%s] has bad IHDR (libpng longjmp)\n", + filename); + break; + case 4: + fprintf(stderr, PROGNAME ": insufficient memory\n"); + break; + default: + fprintf(stderr, PROGNAME + ": unknown readpng2_init() error\n"); + break; + } + ++error; + } + if (error) + fclose(infile); + } + + + /* usage screen */ + + if (error) { + int ch; + + fprintf(stderr, "\n%s %s: %s\n\n", PROGNAME, VERSION, appname); + readpng2_version_info(); + fprintf(stderr, "\n" + "Usage: %s [-gamma exp] [-bgcolor bg | -bgpat pat] [-timing]\n" +#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__)) + " %*s [[-nommxfilters] [-nommxcombine] [-nommxinterlace] | -nommx]\n" +#endif + " %*s file.png\n\n" + " exp \ttransfer-function exponent (``gamma'') of the display\n" + "\t\t system in floating-point format (e.g., ``%.1f''); equal\n" + "\t\t to the product of the lookup-table exponent (varies)\n" + "\t\t and the CRT exponent (usually 2.2); must be positive\n" + " bg \tdesired background color in 7-character hex RGB format\n" + "\t\t (e.g., ``#ff7700'' for orange: same as HTML colors);\n" + "\t\t used with transparent images; overrides -bgpat option\n" + " pat \tdesired background pattern number (1-%d); used with\n" + "\t\t transparent images; overrides -bgcolor option\n" + " -timing\tenables delay for every block read, to simulate modem\n" + "\t\t download of image (~36 Kbps)\n" +#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__)) + " -nommx*\tdisable optimized MMX routines for decoding row filters,\n" + "\t\t combining rows, and expanding interlacing, respectively\n" +#endif + "\nPress Q, Esc or mouse button 1 after image is displayed to quit.\n" + "Press Q or Esc to quit this usage screen. ", + PROGNAME, +#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__)) + (int)strlen(PROGNAME), " ", +#endif + (int)strlen(PROGNAME), " ", default_display_exponent, num_bgpat); + fflush(stderr); + do + ch = _getch(); + while (ch != 'q' && ch != 'Q' && ch != 0x1B); + exit(1); + } else { + fprintf(stderr, "\n%s %s: %s\n", PROGNAME, VERSION, appname); + fprintf(stderr, + "\n [console window: closing this window will terminate %s]\n\n", + PROGNAME); + fflush(stderr); + } + + + /* set the title-bar string, but make sure buffer doesn't overflow */ + + alen = strlen(appname); + flen = strlen(filename); + if (alen + flen + 3 > 1023) + sprintf(titlebar, "%s: ...%s", appname, filename+(alen+flen+6-1023)); + else + sprintf(titlebar, "%s: %s", appname, filename); + + + /* set some final rpng2_info variables before entering main data loop */ + + if (have_bg) { + unsigned r, g, b; /* this approach quiets compiler warnings */ + + sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b); + rpng2_info.bg_red = (uch)r; + rpng2_info.bg_green = (uch)g; + rpng2_info.bg_blue = (uch)b; + } else + rpng2_info.need_bgcolor = TRUE; + + rpng2_info.done = FALSE; + rpng2_info.mainprog_init = rpng2_win_init; + rpng2_info.mainprog_display_row = rpng2_win_display_row; + rpng2_info.mainprog_finish_display = rpng2_win_finish_display; + + + /* OK, this is the fun part: call readpng2_decode_data() at the start of + * the loop to deal with our first buffer of data (read in above to verify + * that the file is a PNG image), then loop through the file and continue + * calling the same routine to handle each chunk of data. It in turn + * passes the data to libpng, which will invoke one or more of our call- + * backs as decoded data become available. We optionally call Sleep() for + * one second per iteration to simulate downloading the image via an analog + * modem. */ + + for (;;) { + Trace((stderr, "about to call readpng2_decode_data()\n")) + if (readpng2_decode_data(&rpng2_info, inbuf, incount)) + ++error; + Trace((stderr, "done with readpng2_decode_data()\n")) + if (error || feof(infile) || rpng2_info.done) + break; + if (timing) + Sleep(1000L); + incount = fread(inbuf, 1, INBUFSIZE, infile); + } + + + /* clean up PNG stuff and report any decoding errors */ + + fclose(infile); + Trace((stderr, "about to call readpng2_cleanup()\n")) + readpng2_cleanup(&rpng2_info); + + if (error) { + fprintf(stderr, PROGNAME ": libpng error while decoding PNG image\n"); + exit(3); + } + + + /* wait for the user to tell us when to quit */ + + while (GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + + /* we're done: clean up all image and Windows resources and go away */ + + Trace((stderr, "about to call rpng2_win_cleanup()\n")) + rpng2_win_cleanup(); + + return msg.wParam; +} + + + + + +/* this function is called by readpng2_info_callback() in readpng2.c, which + * in turn is called by libpng after all of the pre-IDAT chunks have been + * read and processed--i.e., we now have enough info to finish initializing */ + +static void rpng2_win_init() +{ + ulg i; + ulg rowbytes = rpng2_info.rowbytes; + + Trace((stderr, "beginning rpng2_win_init()\n")) + Trace((stderr, " rowbytes = %ld\n", rpng2_info.rowbytes)) + Trace((stderr, " width = %ld\n", rpng2_info.width)) + Trace((stderr, " height = %ld\n", rpng2_info.height)) + + rpng2_info.image_data = (uch *)malloc(rowbytes * rpng2_info.height); + if (!rpng2_info.image_data) { + readpng2_cleanup(&rpng2_info); + return; + } + + rpng2_info.row_pointers = (uch **)malloc(rpng2_info.height * sizeof(uch *)); + if (!rpng2_info.row_pointers) { + free(rpng2_info.image_data); + rpng2_info.image_data = NULL; + readpng2_cleanup(&rpng2_info); + return; + } + + for (i = 0; i < rpng2_info.height; ++i) + rpng2_info.row_pointers[i] = rpng2_info.image_data + i*rowbytes; + +/*--------------------------------------------------------------------------- + Do the basic Windows initialization stuff, make the window, and fill it + with the user-specified, file-specified or default background color. + ---------------------------------------------------------------------------*/ + + if (rpng2_win_create_window()) { + readpng2_cleanup(&rpng2_info); + return; + } +} + + + + + +static int rpng2_win_create_window() +{ + uch bg_red = rpng2_info.bg_red; + uch bg_green = rpng2_info.bg_green; + uch bg_blue = rpng2_info.bg_blue; + uch *dest; + int extra_width, extra_height; + ulg i, j; + WNDCLASSEX wndclass; + RECT rect; + + +/*--------------------------------------------------------------------------- + Allocate memory for the display-specific version of the image (round up + to multiple of 4 for Windows DIB). + ---------------------------------------------------------------------------*/ + + wimage_rowbytes = ((3*rpng2_info.width + 3L) >> 2) << 2; + + if (!(dib = (uch *)malloc(sizeof(BITMAPINFOHEADER) + + wimage_rowbytes*rpng2_info.height))) + { + return 4; /* fail */ + } + +/*--------------------------------------------------------------------------- + Initialize the DIB. Negative height means to use top-down BMP ordering + (must be uncompressed, but that's what we want). Bit count of 1, 4 or 8 + implies a colormap of RGBX quads, but 24-bit BMPs just use B,G,R values + directly => wimage_data begins immediately after BMP header. + ---------------------------------------------------------------------------*/ + + memset(dib, 0, sizeof(BITMAPINFOHEADER)); + bmih = (BITMAPINFOHEADER *)dib; + bmih->biSize = sizeof(BITMAPINFOHEADER); + bmih->biWidth = rpng2_info.width; + bmih->biHeight = -((long)rpng2_info.height); + bmih->biPlanes = 1; + bmih->biBitCount = 24; + bmih->biCompression = 0; + wimage_data = dib + sizeof(BITMAPINFOHEADER); + +/*--------------------------------------------------------------------------- + Fill window with the specified background color (default is black), but + defer loading faked "background image" until window is displayed (may be + slow to compute). Data are in BGR order. + ---------------------------------------------------------------------------*/ + + if (bg_image) { /* just fill with black for now */ + memset(wimage_data, 0, wimage_rowbytes*rpng2_info.height); + } else { + for (j = 0; j < rpng2_info.height; ++j) { + dest = wimage_data + j*wimage_rowbytes; + for (i = rpng2_info.width; i > 0; --i) { + *dest++ = bg_blue; + *dest++ = bg_green; + *dest++ = bg_red; + } + } + } + +/*--------------------------------------------------------------------------- + Set the window parameters. + ---------------------------------------------------------------------------*/ + + memset(&wndclass, 0, sizeof(wndclass)); + + wndclass.cbSize = sizeof(wndclass); + wndclass.style = CS_HREDRAW | CS_VREDRAW; + wndclass.lpfnWndProc = rpng2_win_wndproc; + wndclass.hInstance = global_hInst; + wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndclass.hbrBackground = (HBRUSH)GetStockObject(DKGRAY_BRUSH); + wndclass.lpszMenuName = NULL; + wndclass.lpszClassName = progname; + wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); + + RegisterClassEx(&wndclass); + +/*--------------------------------------------------------------------------- + Finally, create the window. + ---------------------------------------------------------------------------*/ + + extra_width = 2*(GetSystemMetrics(SM_CXBORDER) + + GetSystemMetrics(SM_CXDLGFRAME)); + extra_height = 2*(GetSystemMetrics(SM_CYBORDER) + + GetSystemMetrics(SM_CYDLGFRAME)) + + GetSystemMetrics(SM_CYCAPTION); + + global_hwnd = CreateWindow(progname, titlebar, WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, rpng2_info.width+extra_width, + rpng2_info.height+extra_height, NULL, NULL, global_hInst, NULL); + + ShowWindow(global_hwnd, global_showmode); + UpdateWindow(global_hwnd); + +/*--------------------------------------------------------------------------- + Now compute the background image and display it. If it fails (memory + allocation), revert to a plain background color. + ---------------------------------------------------------------------------*/ + + if (bg_image) { + static const char *msg = "Computing background image..."; + int x, y, len = strlen(msg); + HDC hdc = GetDC(global_hwnd); + TEXTMETRIC tm; + + GetTextMetrics(hdc, &tm); + x = (rpng2_info.width - len*tm.tmAveCharWidth)/2; + y = (rpng2_info.height - tm.tmHeight)/2; + SetBkMode(hdc, TRANSPARENT); + SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); + /* this can still begin out of bounds even if x is positive (???): */ + TextOut(hdc, ((x < 0)? 0 : x), ((y < 0)? 0 : y), msg, len); + ReleaseDC(global_hwnd, hdc); + + rpng2_win_load_bg_image(); /* resets bg_image if fails */ + } + + if (!bg_image) { + for (j = 0; j < rpng2_info.height; ++j) { + dest = wimage_data + j*wimage_rowbytes; + for (i = rpng2_info.width; i > 0; --i) { + *dest++ = bg_blue; + *dest++ = bg_green; + *dest++ = bg_red; + } + } + } + + rect.left = 0L; + rect.top = 0L; + rect.right = (LONG)rpng2_info.width; /* possibly off by one? */ + rect.bottom = (LONG)rpng2_info.height; /* possibly off by one? */ + InvalidateRect(global_hwnd, &rect, FALSE); + UpdateWindow(global_hwnd); /* similar to XFlush() */ + + return 0; + +} /* end function rpng2_win_create_window() */ + + + + + +static int rpng2_win_load_bg_image() +{ + uch *src, *dest; + uch r1, r2, g1, g2, b1, b2; + uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv; + int k, hmax, max; + int xidx, yidx, yidx_max = (bgscale-1); + int even_odd_vert, even_odd_horiz, even_odd; + int invert_gradient2 = (bg[pat].type & 0x08); + int invert_column; + ulg i, row; + +/*--------------------------------------------------------------------------- + Allocate buffer for fake background image to be used with transparent + images; if this fails, revert to plain background color. + ---------------------------------------------------------------------------*/ + + bg_rowbytes = 3 * rpng2_info.width; + bg_data = (uch *)malloc(bg_rowbytes * rpng2_info.height); + if (!bg_data) { + fprintf(stderr, PROGNAME + ": unable to allocate memory for background image\n"); + bg_image = 0; + return 1; + } + +/*--------------------------------------------------------------------------- + Vertical gradients (ramps) in NxN squares, alternating direction and + colors (N == bgscale). + ---------------------------------------------------------------------------*/ + + if ((bg[pat].type & 0x07) == 0) { + uch r1_min = rgb[bg[pat].rgb1_min].r; + uch g1_min = rgb[bg[pat].rgb1_min].g; + uch b1_min = rgb[bg[pat].rgb1_min].b; + uch r2_min = rgb[bg[pat].rgb2_min].r; + uch g2_min = rgb[bg[pat].rgb2_min].g; + uch b2_min = rgb[bg[pat].rgb2_min].b; + int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min; + int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min; + int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min; + int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min; + int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min; + int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min; + + for (row = 0; row < rpng2_info.height; ++row) { + yidx = row % bgscale; + even_odd_vert = (row / bgscale) & 1; + + r1 = r1_min + (r1_diff * yidx) / yidx_max; + g1 = g1_min + (g1_diff * yidx) / yidx_max; + b1 = b1_min + (b1_diff * yidx) / yidx_max; + r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max; + g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max; + b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max; + + r2 = r2_min + (r2_diff * yidx) / yidx_max; + g2 = g2_min + (g2_diff * yidx) / yidx_max; + b2 = b2_min + (b2_diff * yidx) / yidx_max; + r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max; + g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max; + b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max; + + dest = bg_data + row*bg_rowbytes; + for (i = 0; i < rpng2_info.width; ++i) { + even_odd_horiz = (i / bgscale) & 1; + even_odd = even_odd_vert ^ even_odd_horiz; + invert_column = + (even_odd_horiz && (bg[pat].type & 0x10)); + if (even_odd == 0) { /* gradient #1 */ + if (invert_column) { + *dest++ = r1_inv; + *dest++ = g1_inv; + *dest++ = b1_inv; + } else { + *dest++ = r1; + *dest++ = g1; + *dest++ = b1; + } + } else { /* gradient #2 */ + if ((invert_column && invert_gradient2) || + (!invert_column && !invert_gradient2)) + { + *dest++ = r2; /* not inverted or */ + *dest++ = g2; /* doubly inverted */ + *dest++ = b2; + } else { + *dest++ = r2_inv; + *dest++ = g2_inv; /* singly inverted */ + *dest++ = b2_inv; + } + } + } + } + +/*--------------------------------------------------------------------------- + Soft gradient-diamonds with scale = bgscale. Code contributed by Adam + M. Costello. + ---------------------------------------------------------------------------*/ + + } else if ((bg[pat].type & 0x07) == 1) { + + hmax = (bgscale-1)/2; /* half the max weight of a color */ + max = 2*hmax; /* the max weight of a color */ + + r1 = rgb[bg[pat].rgb1_max].r; + g1 = rgb[bg[pat].rgb1_max].g; + b1 = rgb[bg[pat].rgb1_max].b; + r2 = rgb[bg[pat].rgb2_max].r; + g2 = rgb[bg[pat].rgb2_max].g; + b2 = rgb[bg[pat].rgb2_max].b; + + for (row = 0; row < rpng2_info.height; ++row) { + yidx = row % bgscale; + if (yidx > hmax) + yidx = bgscale-1 - yidx; + dest = bg_data + row*bg_rowbytes; + for (i = 0; i < rpng2_info.width; ++i) { + xidx = i % bgscale; + if (xidx > hmax) + xidx = bgscale-1 - xidx; + k = xidx + yidx; + *dest++ = (k*r1 + (max-k)*r2) / max; + *dest++ = (k*g1 + (max-k)*g2) / max; + *dest++ = (k*b1 + (max-k)*b2) / max; + } + } + +/*--------------------------------------------------------------------------- + Radial "starburst" with azimuthal sinusoids; [eventually number of sinu- + soids will equal bgscale?]. This one is slow but very cool. Code con- + tributed by Pieter S. van der Meulen (originally in Smalltalk). + ---------------------------------------------------------------------------*/ + + } else if ((bg[pat].type & 0x07) == 2) { + uch ch; + int ii, x, y, hw, hh, grayspot; + double freq, rotate, saturate, gray, intensity; + double angle=0.0, aoffset=0.0, maxDist, dist; + double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t; + + fprintf(stderr, "%s: computing radial background...", + PROGNAME); + fflush(stderr); + + hh = rpng2_info.height / 2; + hw = rpng2_info.width / 2; + + /* variables for radial waves: + * aoffset: number of degrees to rotate hue [CURRENTLY NOT USED] + * freq: number of color beams originating from the center + * grayspot: size of the graying center area (anti-alias) + * rotate: rotation of the beams as a function of radius + * saturate: saturation of beams' shape azimuthally + */ + angle = CLIP(angle, 0.0, 360.0); + grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw)); + freq = MAX((double)bg[pat].bg_freq, 0.0); + saturate = (double)bg[pat].bg_bsat * 0.1; + rotate = (double)bg[pat].bg_brot * 0.1; + gray = 0.0; + intensity = 0.0; + maxDist = (double)((hw*hw) + (hh*hh)); + + for (row = 0; row < rpng2_info.height; ++row) { + y = row - hh; + dest = bg_data + row*bg_rowbytes; + for (i = 0; i < rpng2_info.width; ++i) { + x = i - hw; + angle = (x == 0)? PI_2 : atan((double)y / (double)x); + gray = (double)MAX(ABS(y), ABS(x)) / grayspot; + gray = MIN(1.0, gray); + dist = (double)((x*x) + (y*y)) / maxDist; + intensity = cos((angle+(rotate*dist*PI)) * freq) * + gray * saturate; + intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5; + hue = (angle + PI) * INV_PI_360 + aoffset; + s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh)); + s = MIN(MAX(s,0.0), 1.0); + v = MIN(MAX(intensity,0.0), 1.0); + + if (s == 0.0) { + ch = (uch)(v * 255.0); + *dest++ = ch; + *dest++ = ch; + *dest++ = ch; + } else { + if ((hue < 0.0) || (hue >= 360.0)) + hue -= (((int)(hue / 360.0)) * 360.0); + hue /= 60.0; + ii = (int)hue; + f = hue - (double)ii; + p = (1.0 - s) * v; + q = (1.0 - (s * f)) * v; + t = (1.0 - (s * (1.0 - f))) * v; + if (ii == 0) { red = v; green = t; blue = p; } + else if (ii == 1) { red = q; green = v; blue = p; } + else if (ii == 2) { red = p; green = v; blue = t; } + else if (ii == 3) { red = p; green = q; blue = v; } + else if (ii == 4) { red = t; green = p; blue = v; } + else if (ii == 5) { red = v; green = p; blue = q; } + *dest++ = (uch)(red * 255.0); + *dest++ = (uch)(green * 255.0); + *dest++ = (uch)(blue * 255.0); + } + } + } + fprintf(stderr, "done.\n"); + fflush(stderr); + } + +/*--------------------------------------------------------------------------- + Blast background image to display buffer before beginning PNG decode; + calling function will handle invalidation and UpdateWindow() call. + ---------------------------------------------------------------------------*/ + + for (row = 0; row < rpng2_info.height; ++row) { + src = bg_data + row*bg_rowbytes; + dest = wimage_data + row*wimage_rowbytes; + for (i = rpng2_info.width; i > 0; --i) { + r1 = *src++; + g1 = *src++; + b1 = *src++; + *dest++ = b1; + *dest++ = g1; /* note reverse order */ + *dest++ = r1; + } + } + + return 0; + +} /* end function rpng2_win_load_bg_image() */ + + + + + +static void rpng2_win_display_row(ulg row) +{ + uch bg_red = rpng2_info.bg_red; + uch bg_green = rpng2_info.bg_green; + uch bg_blue = rpng2_info.bg_blue; + uch *src, *src2=NULL, *dest; + uch r, g, b, a; + ulg i; + static int rows=0; + static ulg firstrow; + +/*--------------------------------------------------------------------------- + rows and firstrow simply track how many rows (and which ones) have not + yet been displayed; alternatively, we could call InvalidateRect() for + every row and not bother with the records-keeping. + ---------------------------------------------------------------------------*/ + + Trace((stderr, "beginning rpng2_win_display_row()\n")) + + if (rows == 0) + firstrow = row; /* first row not yet displayed */ + + ++rows; /* count of rows received but not yet displayed */ + +/*--------------------------------------------------------------------------- + Aside from the use of the rpng2_info struct and the lack of an outer + loop (over rows), this routine is identical to rpng_win_display_image() + in the non-progressive version of the program. + ---------------------------------------------------------------------------*/ + + src = rpng2_info.image_data + row*rpng2_info.rowbytes; + if (bg_image) + src2 = bg_data + row*bg_rowbytes; + dest = wimage_data + row*wimage_rowbytes; + + if (rpng2_info.channels == 3) { + for (i = rpng2_info.width; i > 0; --i) { + r = *src++; + g = *src++; + b = *src++; + *dest++ = b; + *dest++ = g; /* note reverse order */ + *dest++ = r; + } + } else /* if (rpng2_info.channels == 4) */ { + for (i = rpng2_info.width; i > 0; --i) { + r = *src++; + g = *src++; + b = *src++; + a = *src++; + if (bg_image) { + bg_red = *src2++; + bg_green = *src2++; + bg_blue = *src2++; + } + if (a == 255) { + *dest++ = b; + *dest++ = g; + *dest++ = r; + } else if (a == 0) { + *dest++ = bg_blue; + *dest++ = bg_green; + *dest++ = bg_red; + } else { + /* this macro (copied from png.h) composites the + * foreground and background values and puts the + * result into the first argument; there are no + * side effects with the first argument */ + alpha_composite(*dest++, b, a, bg_blue); + alpha_composite(*dest++, g, a, bg_green); + alpha_composite(*dest++, r, a, bg_red); + } + } + } + +/*--------------------------------------------------------------------------- + Display after every 16 rows or when on last row. (Region may include + previously displayed lines due to interlacing--i.e., not contiguous.) + ---------------------------------------------------------------------------*/ + + if ((rows & 0xf) == 0 || row == rpng2_info.height-1) { + RECT rect; + + rect.left = 0L; + rect.top = (LONG)firstrow; + rect.right = (LONG)rpng2_info.width; /* possibly off by one? */ + rect.bottom = (LONG)row + 1L; /* possibly off by one? */ + InvalidateRect(global_hwnd, &rect, FALSE); + UpdateWindow(global_hwnd); /* similar to XFlush() */ + rows = 0; + } + +} /* end function rpng2_win_display_row() */ + + + + + +static void rpng2_win_finish_display() +{ + Trace((stderr, "beginning rpng2_win_finish_display()\n")) + + /* last row has already been displayed by rpng2_win_display_row(), so + * we have nothing to do here except set a flag and let the user know + * that the image is done */ + + rpng2_info.done = TRUE; + printf( + "Done. Press Q, Esc or mouse button 1 (within image window) to quit.\n"); + fflush(stdout); +} + + + + + +static void rpng2_win_cleanup() +{ + if (bg_image && bg_data) { + free(bg_data); + bg_data = NULL; + } + + if (rpng2_info.image_data) { + free(rpng2_info.image_data); + rpng2_info.image_data = NULL; + } + + if (rpng2_info.row_pointers) { + free(rpng2_info.row_pointers); + rpng2_info.row_pointers = NULL; + } + + if (dib) { + free(dib); + dib = NULL; + } +} + + + + + +LRESULT CALLBACK rpng2_win_wndproc(HWND hwnd, UINT iMsg, WPARAM wP, LPARAM lP) +{ + HDC hdc; + PAINTSTRUCT ps; + int rc; + + switch (iMsg) { + case WM_CREATE: + /* one-time processing here, if any */ + return 0; + + case WM_PAINT: + hdc = BeginPaint(hwnd, &ps); + rc = StretchDIBits(hdc, 0, 0, rpng2_info.width, rpng2_info.height, + 0, 0, rpng2_info.width, rpng2_info.height, + wimage_data, (BITMAPINFO *)bmih, + 0, SRCCOPY); + EndPaint(hwnd, &ps); + return 0; + + /* wait for the user to tell us when to quit */ + case WM_CHAR: + switch (wP) { /* only need one, so ignore repeat count */ + case 'q': + case 'Q': + case 0x1B: /* Esc key */ + PostQuitMessage(0); + } + return 0; + + case WM_LBUTTONDOWN: /* another way of quitting */ + case WM_DESTROY: + PostQuitMessage(0); + return 0; + } + + return DefWindowProc(hwnd, iMsg, wP, lP); +} diff --git a/Engine/lib/lpng/contrib/gregbook/rpng2-x.c b/Engine/lib/lpng/contrib/gregbook/rpng2-x.c new file mode 100644 index 000000000..6bba64908 --- /dev/null +++ b/Engine/lib/lpng/contrib/gregbook/rpng2-x.c @@ -0,0 +1,2088 @@ +/*--------------------------------------------------------------------------- + + rpng2 - progressive-model PNG display program rpng2-x.c + + This program decodes and displays PNG files progressively, as if it were + a web browser (though the front end is only set up to read from files). + It supports gamma correction, user-specified background colors, and user- + specified background patterns (for transparent images). This version is + for the X Window System (tested by the author under Unix and by Martin + Zinser under OpenVMS; may work under OS/2 with a little tweaking). + + Thanks to Adam Costello and Pieter S. van der Meulen for the "diamond" + and "radial waves" patterns, respectively. + + to do: + - fix expose/redraw code: don't draw entire row if only part exposed + - 8-bit (colormapped) X support + - finish resizable checkerboard-gradient (sizes 4-128?) + - use %.1023s to simplify truncation of title-bar string? + + --------------------------------------------------------------------------- + + Changelog: + - 1.01: initial public release + - 1.02: modified to allow abbreviated options; fixed char/uchar mismatch + - 1.10: added support for non-default visuals; fixed X pixel-conversion + - 1.11: added -usleep option for demos; fixed command-line parsing bug + - 1.12: added -pause option for demos and testing + - 1.20: added runtime MMX-enabling/disabling and new -mmx* options + - 1.21: fixed some small X memory leaks (thanks to François Petitjean) + - 1.22: fixed XFreeGC() crash bug (thanks to Patrick Welche) + - 1.23: added -bgpat 0 mode (std white/gray checkerboard, 8x8 squares) + - 1.30: added -loop option for -bgpat (ifdef FEATURE_LOOP); fixed bpp = + 24; added support for X resources (thanks to Gerhard Niklasch) + - 1.31: added code to skip unused chunks (thanks to Glenn Randers-Pehrson) + - 1.32: added AMD64/EM64T support (__x86_64__); added basic expose/redraw + handling + - 2.00: dual-licensed (added GNU GPL) + + --------------------------------------------------------------------------- + + Copyright (c) 1998-2007 Greg Roelofs. All rights reserved. + + This software is provided "as is," without warranty of any kind, + express or implied. In no event shall the author or contributors + be held liable for any damages arising in any way from the use of + this software. + + The contents of this file are DUAL-LICENSED. You may modify and/or + redistribute this software according to the terms of one of the + following two licenses (at your option): + + + LICENSE 1 ("BSD-like with advertising clause"): + + 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. Redistributions of source code must retain the above copyright + notice, disclaimer, and this list of conditions. + 2. Redistributions in binary form must reproduce the above copyright + notice, disclaimer, and this list of conditions in the documenta- + tion and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + + This product includes software developed by Greg Roelofs + and contributors for the book, "PNG: The Definitive Guide," + published by O'Reilly and Associates. + + + LICENSE 2 (GNU GPL v2 or later): + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ---------------------------------------------------------------------------*/ + +#define PROGNAME "rpng2-x" +#define LONGNAME "Progressive PNG Viewer for X" +#define VERSION "2.00 of 2 June 2007" +#define RESNAME "rpng2" /* our X resource application name */ +#define RESCLASS "Rpng" /* our X resource class name */ + +#include +#include +#include +#include +#include /* for jmpbuf declaration in readpng2.h */ +#include +#include /* only for PvdM background code */ +#include +#include +#include +#include /* defines XK_* macros */ + +#ifdef VMS +# include +#endif + +/* all for PvdM background code: */ +#ifndef PI +# define PI 3.141592653589793238 +#endif +#define PI_2 (PI*0.5) +#define INV_PI_360 (360.0 / PI) +#define MAX(a,b) (a>b?a:b) +#define MIN(a,b) (a> 8)) >> 8); \ +} + + +#define INBUFSIZE 4096 /* with pseudo-timing on (1 sec delay/block), this + * block size corresponds roughly to a download + * speed 10% faster than theoretical 33.6K maximum + * (assuming 8 data bits, 1 stop bit and no other + * overhead) */ + +/* local prototypes */ +static void rpng2_x_init (void); +static int rpng2_x_create_window (void); +static int rpng2_x_load_bg_image (void); +static void rpng2_x_display_row (ulg row); +static void rpng2_x_finish_display (void); +static void rpng2_x_redisplay_image (ulg startcol, ulg startrow, + ulg width, ulg height); +#ifdef FEATURE_LOOP +static void rpng2_x_reload_bg_image (void); +static int is_number (char *p); +#endif +static void rpng2_x_cleanup (void); +static int rpng2_x_msb (ulg u32val); + + +static char titlebar[1024], *window_name = titlebar; +static char *appname = LONGNAME; +static char *icon_name = PROGNAME; +static char *res_name = RESNAME; +static char *res_class = RESCLASS; +static char *filename; +static FILE *infile; + +static mainprog_info rpng2_info; + +static uch inbuf[INBUFSIZE]; +static int incount; + +static int pat = 6; /* must be less than num_bgpat */ +static int bg_image = 0; +static int bgscale, bgscale_default = 16; +static ulg bg_rowbytes; +static uch *bg_data; + +int pause_after_pass = FALSE; +int demo_timing = FALSE; +ulg usleep_duration = 0L; + +static struct rgb_color { + uch r, g, b; +} rgb[] = { + { 0, 0, 0}, /* 0: black */ + {255, 255, 255}, /* 1: white */ + {173, 132, 57}, /* 2: tan */ + { 64, 132, 0}, /* 3: medium green */ + {189, 117, 1}, /* 4: gold */ + {253, 249, 1}, /* 5: yellow */ + { 0, 0, 255}, /* 6: blue */ + { 0, 0, 120}, /* 7: medium blue */ + {255, 0, 255}, /* 8: magenta */ + { 64, 0, 64}, /* 9: dark magenta */ + {255, 0, 0}, /* 10: red */ + { 64, 0, 0}, /* 11: dark red */ + {255, 127, 0}, /* 12: orange */ + {192, 96, 0}, /* 13: darker orange */ + { 24, 60, 0}, /* 14: dark green-yellow */ + { 85, 125, 200}, /* 15: ice blue */ + {192, 192, 192} /* 16: Netscape/Mosaic gray */ +}; +/* not used for now, but should be for error-checking: +static int num_rgb = sizeof(rgb) / sizeof(struct rgb_color); + */ + +/* + This whole struct is a fairly cheesy way to keep the number of + command-line options to a minimum. The radial-waves background + type is a particularly poor fit to the integer elements of the + struct...but a few macros and a little fixed-point math will do + wonders for ya. + + type bits: + F E D C B A 9 8 7 6 5 4 3 2 1 0 + | | | | | + | | +-+-+-- 0 = sharp-edged checkerboard + | | 1 = soft diamonds + | | 2 = radial waves + | | 3-7 = undefined + | +-- gradient #2 inverted? + +-- alternating columns inverted? + */ +static struct background_pattern { + ush type; + int rgb1_max, rgb1_min; /* or bg_freq, bg_gray */ + int rgb2_max, rgb2_min; /* or bg_bsat, bg_brot (both scaled by 10)*/ +} bg[] = { + {0, 1,1, 16,16}, /* checkered: white vs. light gray (basic) */ + {0+8, 2,0, 1,15}, /* checkered: tan/black vs. white/ice blue */ + {0+24, 2,0, 1,0}, /* checkered: tan/black vs. white/black */ + {0+8, 4,5, 0,2}, /* checkered: gold/yellow vs. black/tan */ + {0+8, 4,5, 0,6}, /* checkered: gold/yellow vs. black/blue */ + {0, 7,0, 8,9}, /* checkered: deep blue/black vs. magenta */ + {0+8, 13,0, 5,14}, /* checkered: orange/black vs. yellow */ + {0+8, 12,0, 10,11}, /* checkered: orange/black vs. red */ + {1, 7,0, 8,0}, /* diamonds: deep blue/black vs. magenta */ + {1, 12,0, 11,0}, /* diamonds: orange vs. dark red */ + {1, 10,0, 7,0}, /* diamonds: red vs. medium blue */ + {1, 4,0, 5,0}, /* diamonds: gold vs. yellow */ + {1, 3,0, 0,0}, /* diamonds: medium green vs. black */ + {2, 16, 100, 20, 0}, /* radial: ~hard radial color-beams */ + {2, 18, 100, 10, 2}, /* radial: soft, curved radial color-beams */ + {2, 16, 256, 100, 250}, /* radial: very tight spiral */ + {2, 10000, 256, 11, 0} /* radial: dipole-moire' (almost fractal) */ +}; +static int num_bgpat = sizeof(bg) / sizeof(struct background_pattern); + + +/* X-specific variables */ +static char *displayname; +static XImage *ximage; +static Display *display; +static int depth; +static Visual *visual; +static XVisualInfo *visual_list; +static int RShift, GShift, BShift; +static ulg RMask, GMask, BMask; +static Window window; +static GC gc; +static Colormap colormap; + +static int have_nondefault_visual = FALSE; +static int have_colormap = FALSE; +static int have_window = FALSE; +static int have_gc = FALSE; + + + + +int main(int argc, char **argv) +{ +#ifdef sgi + char tmpline[80]; +#endif + char *p, *bgstr = NULL; + int rc, alen, flen; + int error = 0; + int timing = FALSE; + int have_bg = FALSE; +#ifdef FEATURE_LOOP + int loop = FALSE; + long loop_interval = -1; /* seconds (100,000 max) */ +#endif + double LUT_exponent; /* just the lookup table */ + double CRT_exponent = 2.2; /* just the monitor */ + double default_display_exponent; /* whole display system */ + XEvent e; + KeySym k; + + + /* First initialize a few things, just to be sure--memset takes care of + * default background color (black), booleans (FALSE), pointers (NULL), + * etc. */ + + displayname = (char *)NULL; + filename = (char *)NULL; + memset(&rpng2_info, 0, sizeof(mainprog_info)); + + + /* Set the default value for our display-system exponent, i.e., the + * product of the CRT exponent and the exponent corresponding to + * the frame-buffer's lookup table (LUT), if any. This is not an + * exhaustive list of LUT values (e.g., OpenStep has a lot of weird + * ones), but it should cover 99% of the current possibilities. */ + +#if defined(NeXT) + /* third-party utilities can modify the default LUT exponent */ + LUT_exponent = 1.0 / 2.2; + /* + if (some_next_function_that_returns_gamma(&next_gamma)) + LUT_exponent = 1.0 / next_gamma; + */ +#elif defined(sgi) + LUT_exponent = 1.0 / 1.7; + /* there doesn't seem to be any documented function to + * get the "gamma" value, so we do it the hard way */ + infile = fopen("/etc/config/system.glGammaVal", "r"); + if (infile) { + double sgi_gamma; + + fgets(tmpline, 80, infile); + fclose(infile); + sgi_gamma = atof(tmpline); + if (sgi_gamma > 0.0) + LUT_exponent = 1.0 / sgi_gamma; + } +#elif defined(Macintosh) + LUT_exponent = 1.8 / 2.61; + /* + if (some_mac_function_that_returns_gamma(&mac_gamma)) + LUT_exponent = mac_gamma / 2.61; + */ +#else + LUT_exponent = 1.0; /* assume no LUT: most PCs */ +#endif + + /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */ + default_display_exponent = LUT_exponent * CRT_exponent; + + + /* If the user has set the SCREEN_GAMMA environment variable as suggested + * (somewhat imprecisely) in the libpng documentation, use that; otherwise + * use the default value we just calculated. Either way, the user may + * override this via a command-line option. */ + + if ((p = getenv("SCREEN_GAMMA")) != NULL) + rpng2_info.display_exponent = atof(p); + else + rpng2_info.display_exponent = default_display_exponent; + + + /* Now parse the command line for options and the PNG filename. */ + + while (*++argv && !error) { + if (!strncmp(*argv, "-display", 2)) { + if (!*++argv) + ++error; + else + displayname = *argv; + } else if (!strncmp(*argv, "-gamma", 2)) { + if (!*++argv) + ++error; + else { + rpng2_info.display_exponent = atof(*argv); + if (rpng2_info.display_exponent <= 0.0) + ++error; + } + } else if (!strncmp(*argv, "-bgcolor", 4)) { + if (!*++argv) + ++error; + else { + bgstr = *argv; + if (strlen(bgstr) != 7 || bgstr[0] != '#') + ++error; + else { + have_bg = TRUE; + bg_image = FALSE; + } + } + } else if (!strncmp(*argv, "-bgpat", 4)) { + if (!*++argv) + ++error; + else { + pat = atoi(*argv); + if (pat >= 0 && pat < num_bgpat) { + bg_image = TRUE; + have_bg = FALSE; + } else + ++error; + } + } else if (!strncmp(*argv, "-usleep", 2)) { + if (!*++argv) + ++error; + else { + usleep_duration = (ulg)atol(*argv); + demo_timing = TRUE; + } + } else if (!strncmp(*argv, "-pause", 2)) { + pause_after_pass = TRUE; + } else if (!strncmp(*argv, "-timing", 2)) { + timing = TRUE; +#ifdef FEATURE_LOOP + } else if (!strncmp(*argv, "-loop", 2)) { + loop = TRUE; + if (!argv[1] || !is_number(argv[1])) + loop_interval = 2; + else { + ++argv; + loop_interval = atol(*argv); + if (loop_interval < 0) + loop_interval = 2; + else if (loop_interval > 100000) /* bit more than one day */ + loop_interval = 100000; + } +#endif +#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__)) + } else if (!strncmp(*argv, "-nommxfilters", 7)) { + rpng2_info.nommxfilters = TRUE; + } else if (!strncmp(*argv, "-nommxcombine", 7)) { + rpng2_info.nommxcombine = TRUE; + } else if (!strncmp(*argv, "-nommxinterlace", 7)) { + rpng2_info.nommxinterlace = TRUE; + } else if (!strcmp(*argv, "-nommx")) { + rpng2_info.nommxfilters = TRUE; + rpng2_info.nommxcombine = TRUE; + rpng2_info.nommxinterlace = TRUE; +#endif + } else { + if (**argv != '-') { + filename = *argv; + if (argv[1]) /* shouldn't be any more args after filename */ + ++error; + } else + ++error; /* not expecting any other options */ + } + } + + if (!filename) { + ++error; + } else if (!(infile = fopen(filename, "rb"))) { + fprintf(stderr, PROGNAME ": can't open PNG file [%s]\n", filename); + ++error; + } else { + incount = fread(inbuf, 1, INBUFSIZE, infile); + if (incount < 8 || !readpng2_check_sig(inbuf, 8)) { + fprintf(stderr, PROGNAME + ": [%s] is not a PNG file: incorrect signature\n", + filename); + ++error; + } else if ((rc = readpng2_init(&rpng2_info)) != 0) { + switch (rc) { + case 2: + fprintf(stderr, PROGNAME + ": [%s] has bad IHDR (libpng longjmp)\n", + filename); + break; + case 4: + fprintf(stderr, PROGNAME ": insufficient memory\n"); + break; + default: + fprintf(stderr, PROGNAME + ": unknown readpng2_init() error\n"); + break; + } + ++error; + } else { + display = XOpenDisplay(displayname); + if (!display) { + readpng2_cleanup(&rpng2_info); + fprintf(stderr, PROGNAME ": can't open X display [%s]\n", + displayname? displayname : "default"); + ++error; + } + } + if (error) + fclose(infile); + } + + + /* usage screen */ + + if (error) { + fprintf(stderr, "\n%s %s: %s\n\n", PROGNAME, VERSION, appname); + readpng2_version_info(); + fprintf(stderr, "\n" + "Usage: %s [-display xdpy] [-gamma exp] [-bgcolor bg | -bgpat pat]\n" +#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__)) + " %*s [[-nommxfilters] [-nommxcombine] [-nommxinterlace] | -nommx]\n" +#endif +#ifdef FEATURE_LOOP + " %*s [-usleep dur | -timing] [-pause] [-loop [sec]] file.png\n\n" +#else + " %*s [-usleep dur | -timing] [-pause] file.png\n\n" +#endif + " xdpy\tname of the target X display (e.g., ``hostname:0'')\n" + " exp \ttransfer-function exponent (``gamma'') of the display\n" + "\t\t system in floating-point format (e.g., ``%.1f''); equal\n" + "\t\t to the product of the lookup-table exponent (varies)\n" + "\t\t and the CRT exponent (usually 2.2); must be positive\n" + " bg \tdesired background color in 7-character hex RGB format\n" + "\t\t (e.g., ``#ff7700'' for orange: same as HTML colors);\n" + "\t\t used with transparent images; overrides -bgpat\n" + " pat \tdesired background pattern number (0-%d); used with\n" + "\t\t transparent images; overrides -bgcolor\n" +#ifdef FEATURE_LOOP + " -loop\tloops through background images after initial display\n" + "\t\t is complete (depends on -bgpat)\n" + " sec \tseconds to display each background image (default = 2)\n" +#endif +#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__)) + " -nommx*\tdisable optimized MMX routines for decoding row filters,\n" + "\t\t combining rows, and expanding interlacing, respectively\n" +#endif + " dur \tduration in microseconds to wait after displaying each\n" + "\t\t row (for demo purposes)\n" + " -timing\tenables delay for every block read, to simulate modem\n" + "\t\t download of image (~36 Kbps)\n" + " -pause\tpauses after displaying each pass until key pressed\n" + "\nPress Q, Esc or mouse button 1 (within image window, after image\n" + "is displayed) to quit.\n" + "\n", PROGNAME, +#if (defined(__i386__) || defined(_M_IX86) || defined(__x86_64__)) + (int)strlen(PROGNAME), " ", +#endif + (int)strlen(PROGNAME), " ", default_display_exponent, num_bgpat-1); + exit(1); + } + + + /* set the title-bar string, but make sure buffer doesn't overflow */ + + alen = strlen(appname); + flen = strlen(filename); + if (alen + flen + 3 > 1023) + sprintf(titlebar, "%s: ...%s", appname, filename+(alen+flen+6-1023)); + else + sprintf(titlebar, "%s: %s", appname, filename); + + + /* set some final rpng2_info variables before entering main data loop */ + + if (have_bg) { + unsigned r, g, b; /* this approach quiets compiler warnings */ + + sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b); + rpng2_info.bg_red = (uch)r; + rpng2_info.bg_green = (uch)g; + rpng2_info.bg_blue = (uch)b; + } else + rpng2_info.need_bgcolor = TRUE; + + rpng2_info.done = FALSE; + rpng2_info.mainprog_init = rpng2_x_init; + rpng2_info.mainprog_display_row = rpng2_x_display_row; + rpng2_info.mainprog_finish_display = rpng2_x_finish_display; + + + /* OK, this is the fun part: call readpng2_decode_data() at the start of + * the loop to deal with our first buffer of data (read in above to verify + * that the file is a PNG image), then loop through the file and continue + * calling the same routine to handle each chunk of data. It in turn + * passes the data to libpng, which will invoke one or more of our call- + * backs as decoded data become available. We optionally call sleep() for + * one second per iteration to simulate downloading the image via an analog + * modem. */ + + for (;;) { + Trace((stderr, "about to call readpng2_decode_data()\n")) + if (readpng2_decode_data(&rpng2_info, inbuf, incount)) + ++error; + Trace((stderr, "done with readpng2_decode_data()\n")) + if (error || feof(infile) || rpng2_info.done) + break; + if (timing) + sleep(1); + incount = fread(inbuf, 1, INBUFSIZE, infile); + } + + + /* clean up PNG stuff and report any decoding errors */ + + fclose(infile); + Trace((stderr, "about to call readpng2_cleanup()\n")) + readpng2_cleanup(&rpng2_info); + + if (error) { + fprintf(stderr, PROGNAME ": libpng error while decoding PNG image\n"); + exit(3); + } + + +#ifdef FEATURE_LOOP + + if (loop && bg_image) { + for (;;) { + int i, use_sleep; + struct timeval now, then; + + /* get current time and add loop_interval to get target time */ + if (gettimeofday(&then, NULL) == 0) { + then.tv_sec += loop_interval; + use_sleep = FALSE; + } else + use_sleep = TRUE; + + /* do quick check for a quit event but don't wait for it */ + /* GRR BUG: should also check for Expose events and redraw... */ + if (XCheckMaskEvent(display, KeyPressMask | ButtonPressMask, &e)) + if (QUIT(e,k)) + break; + + /* generate next background image */ + if (++pat >= num_bgpat) + pat = 0; + rpng2_x_reload_bg_image(); + + /* wait for timeout, using whatever means are available */ + if (use_sleep || gettimeofday(&now, NULL) != 0) { + for (i = loop_interval; i > 0; --i) { + sleep(1); + /* GRR BUG: also need to check for Expose (and redraw!) */ + if (XCheckMaskEvent(display, KeyPressMask | ButtonPressMask, + &e) && QUIT(e,k)) + break; + } + } else { + /* Y2038 BUG! */ + if (now.tv_sec < then.tv_sec || + (now.tv_sec == then.tv_sec && now.tv_usec < then.tv_usec)) + { + int quit = FALSE; + long seconds_to_go = then.tv_sec - now.tv_sec; + long usleep_usec; + + /* basically chew up most of remaining loop-interval with + * calls to sleep(1) interleaved with checks for quit + * events, but also recalc time-to-go periodically; when + * done, clean up any remaining time with usleep() call + * (could also use SIGALRM, but signals are a pain...) */ + while (seconds_to_go-- > 1) { + int seconds_done = 0; + + for (i = seconds_to_go; i > 0 && !quit; --i) { + sleep(1); + /* GRR BUG: need to check for Expose and redraw */ + if (XCheckMaskEvent(display, KeyPressMask | + ButtonPressMask, &e) && QUIT(e,k)) + quit = TRUE; + if (++seconds_done > 1000) + break; /* time to redo seconds_to_go meas. */ + } + if (quit) + break; + + /* OK, more than 1000 seconds since last check: + * correct the time-to-go measurement for drift */ + if (gettimeofday(&now, NULL) == 0) { + if (now.tv_sec >= then.tv_sec) + break; + seconds_to_go = then.tv_sec - now.tv_sec; + } else + ++seconds_to_go; /* restore what we subtracted */ + } + if (quit) + break; /* breaks outer do-loop, skips redisplay */ + + /* since difference between "now" and "then" is already + * eaten up to within a couple of seconds, don't need to + * worry about overflow--but might have overshot (neg.) */ + if (gettimeofday(&now, NULL) == 0) { + usleep_usec = 1000000L*(then.tv_sec - now.tv_sec) + + then.tv_usec - now.tv_usec; + if (usleep_usec > 0) + usleep((ulg)usleep_usec); + } + } + } + + /* composite image against new background and display (note that + * we do not take into account the time spent doing this...) */ + rpng2_x_redisplay_image (0, 0, rpng2_info.width, rpng2_info.height); + } + + } else /* FALL THROUGH and do the normal thing */ + +#endif /* FEATURE_LOOP */ + + /* wait for the user to tell us when to quit */ + + do { + XNextEvent(display, &e); + if (e.type == Expose) { + XExposeEvent *ex = (XExposeEvent *)&e; + rpng2_x_redisplay_image (ex->x, ex->y, ex->width, ex->height); + } + } while (!QUIT(e,k)); + + + /* we're done: clean up all image and X resources and go away */ + + Trace((stderr, "about to call rpng2_x_cleanup()\n")) + rpng2_x_cleanup(); + + return 0; +} + + + + + +/* this function is called by readpng2_info_callback() in readpng2.c, which + * in turn is called by libpng after all of the pre-IDAT chunks have been + * read and processed--i.e., we now have enough info to finish initializing */ + +static void rpng2_x_init(void) +{ + ulg i; + ulg rowbytes = rpng2_info.rowbytes; + + Trace((stderr, "beginning rpng2_x_init()\n")) + Trace((stderr, " rowbytes = %ld\n", rpng2_info.rowbytes)) + Trace((stderr, " width = %ld\n", rpng2_info.width)) + Trace((stderr, " height = %ld\n", rpng2_info.height)) + + rpng2_info.image_data = (uch *)malloc(rowbytes * rpng2_info.height); + if (!rpng2_info.image_data) { + readpng2_cleanup(&rpng2_info); + return; + } + + rpng2_info.row_pointers = (uch **)malloc(rpng2_info.height * sizeof(uch *)); + if (!rpng2_info.row_pointers) { + free(rpng2_info.image_data); + rpng2_info.image_data = NULL; + readpng2_cleanup(&rpng2_info); + return; + } + + for (i = 0; i < rpng2_info.height; ++i) + rpng2_info.row_pointers[i] = rpng2_info.image_data + i*rowbytes; + + + /* do the basic X initialization stuff, make the window, and fill it with + * the user-specified, file-specified or default background color or + * pattern */ + + if (rpng2_x_create_window()) { + + /* GRR TEMPORARY HACK: this is fundamentally no different from cases + * above; libpng should longjmp() back to us when png_ptr goes away. + * If we/it segfault instead, seems like a libpng bug... */ + + /* we're here via libpng callback, so if window fails, clean and bail */ + readpng2_cleanup(&rpng2_info); + rpng2_x_cleanup(); + exit(2); + } +} + + + + + +static int rpng2_x_create_window(void) +{ + ulg bg_red = rpng2_info.bg_red; + ulg bg_green = rpng2_info.bg_green; + ulg bg_blue = rpng2_info.bg_blue; + ulg bg_pixel = 0L; + ulg attrmask; + int need_colormap = FALSE; + int screen, pad; + uch *xdata; + Window root; + XEvent e; + XGCValues gcvalues; + XSetWindowAttributes attr; + XTextProperty windowName, *pWindowName = &windowName; + XTextProperty iconName, *pIconName = &iconName; + XVisualInfo visual_info; + XSizeHints *size_hints; + XWMHints *wm_hints; + XClassHint *class_hints; + + + Trace((stderr, "beginning rpng2_x_create_window()\n")) + + screen = DefaultScreen(display); + depth = DisplayPlanes(display, screen); + root = RootWindow(display, screen); + +#ifdef DEBUG + XSynchronize(display, True); +#endif + + if (depth != 16 && depth != 24 && depth != 32) { + int visuals_matched = 0; + + Trace((stderr, "default depth is %d: checking other visuals\n", + depth)) + + /* 24-bit first */ + visual_info.screen = screen; + visual_info.depth = 24; + visual_list = XGetVisualInfo(display, + VisualScreenMask | VisualDepthMask, &visual_info, &visuals_matched); + if (visuals_matched == 0) { +/* GRR: add 15-, 16- and 32-bit TrueColor visuals (also DirectColor?) */ + fprintf(stderr, "default screen depth %d not supported, and no" + " 24-bit visuals found\n", depth); + return 2; + } + Trace((stderr, "XGetVisualInfo() returned %d 24-bit visuals\n", + visuals_matched)) + visual = visual_list[0].visual; + depth = visual_list[0].depth; +/* + colormap_size = visual_list[0].colormap_size; + visual_class = visual->class; + visualID = XVisualIDFromVisual(visual); + */ + have_nondefault_visual = TRUE; + need_colormap = TRUE; + } else { + XMatchVisualInfo(display, screen, depth, TrueColor, &visual_info); + visual = visual_info.visual; + } + + RMask = visual->red_mask; + GMask = visual->green_mask; + BMask = visual->blue_mask; + +/* GRR: add/check 8-bit support */ + if (depth == 8 || need_colormap) { + colormap = XCreateColormap(display, root, visual, AllocNone); + if (!colormap) { + fprintf(stderr, "XCreateColormap() failed\n"); + return 2; + } + have_colormap = TRUE; + if (depth == 8) + bg_image = FALSE; /* gradient just wastes palette entries */ + } + if (depth == 15 || depth == 16) { + RShift = 15 - rpng2_x_msb(RMask); /* these are right-shifts */ + GShift = 15 - rpng2_x_msb(GMask); + BShift = 15 - rpng2_x_msb(BMask); + } else if (depth > 16) { + RShift = rpng2_x_msb(RMask) - 7; /* these are left-shifts */ + GShift = rpng2_x_msb(GMask) - 7; + BShift = rpng2_x_msb(BMask) - 7; + } + if (depth >= 15 && (RShift < 0 || GShift < 0 || BShift < 0)) { + fprintf(stderr, "rpng2 internal logic error: negative X shift(s)!\n"); + return 2; + } + +/*--------------------------------------------------------------------------- + Finally, create the window. + ---------------------------------------------------------------------------*/ + + attr.backing_store = Always; + attr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask; + attrmask = CWBackingStore | CWEventMask; + if (have_nondefault_visual) { + attr.colormap = colormap; + attr.background_pixel = 0; + attr.border_pixel = 1; + attrmask |= CWColormap | CWBackPixel | CWBorderPixel; + } + + window = XCreateWindow(display, root, 0, 0, rpng2_info.width, + rpng2_info.height, 0, depth, InputOutput, visual, attrmask, &attr); + + if (window == None) { + fprintf(stderr, "XCreateWindow() failed\n"); + return 2; + } else + have_window = TRUE; + + if (depth == 8) + XSetWindowColormap(display, window, colormap); + + if (!XStringListToTextProperty(&window_name, 1, pWindowName)) + pWindowName = NULL; + if (!XStringListToTextProperty(&icon_name, 1, pIconName)) + pIconName = NULL; + + /* OK if either hints allocation fails; XSetWMProperties() allows NULLs */ + + if ((size_hints = XAllocSizeHints()) != NULL) { + /* window will not be resizable */ + size_hints->flags = PMinSize | PMaxSize; + size_hints->min_width = size_hints->max_width = (int)rpng2_info.width; + size_hints->min_height = size_hints->max_height = + (int)rpng2_info.height; + } + + if ((wm_hints = XAllocWMHints()) != NULL) { + wm_hints->initial_state = NormalState; + wm_hints->input = True; + /* wm_hints->icon_pixmap = icon_pixmap; */ + wm_hints->flags = StateHint | InputHint /* | IconPixmapHint */ ; + } + + if ((class_hints = XAllocClassHint()) != NULL) { + class_hints->res_name = res_name; + class_hints->res_class = res_class; + } + + XSetWMProperties(display, window, pWindowName, pIconName, NULL, 0, + size_hints, wm_hints, class_hints); + + /* various properties and hints no longer needed; free memory */ + if (pWindowName) + XFree(pWindowName->value); + if (pIconName) + XFree(pIconName->value); + if (size_hints) + XFree(size_hints); + if (wm_hints) + XFree(wm_hints); + if (class_hints) + XFree(class_hints); + + XMapWindow(display, window); + + gc = XCreateGC(display, window, 0, &gcvalues); + have_gc = TRUE; + +/*--------------------------------------------------------------------------- + Allocate memory for the X- and display-specific version of the image. + ---------------------------------------------------------------------------*/ + + if (depth == 24 || depth == 32) { + xdata = (uch *)malloc(4*rpng2_info.width*rpng2_info.height); + pad = 32; + } else if (depth == 16) { + xdata = (uch *)malloc(2*rpng2_info.width*rpng2_info.height); + pad = 16; + } else /* depth == 8 */ { + xdata = (uch *)malloc(rpng2_info.width*rpng2_info.height); + pad = 8; + } + + if (!xdata) { + fprintf(stderr, PROGNAME ": unable to allocate image memory\n"); + return 4; + } + + ximage = XCreateImage(display, visual, depth, ZPixmap, 0, + (char *)xdata, rpng2_info.width, rpng2_info.height, pad, 0); + + if (!ximage) { + fprintf(stderr, PROGNAME ": XCreateImage() failed\n"); + free(xdata); + return 3; + } + + /* to avoid testing the byte order every pixel (or doubling the size of + * the drawing routine with a giant if-test), we arbitrarily set the byte + * order to MSBFirst and let Xlib worry about inverting things on little- + * endian machines (e.g., Linux/x86, old VAXen, etc.)--this is not the + * most efficient approach (the giant if-test would be better), but in + * the interest of clarity, we'll take the easy way out... */ + + ximage->byte_order = MSBFirst; + +/*--------------------------------------------------------------------------- + Fill window with the specified background color (default is black) or + faked "background image" (but latter is disabled if 8-bit; gradients + just waste palette entries). + ---------------------------------------------------------------------------*/ + + if (bg_image) + rpng2_x_load_bg_image(); /* resets bg_image if fails */ + + if (!bg_image) { + if (depth == 24 || depth == 32) { + bg_pixel = (bg_red << RShift) | + (bg_green << GShift) | + (bg_blue << BShift); + } else if (depth == 16) { + bg_pixel = (((bg_red << 8) >> RShift) & RMask) | + (((bg_green << 8) >> GShift) & GMask) | + (((bg_blue << 8) >> BShift) & BMask); + } else /* depth == 8 */ { + + /* GRR: add 8-bit support */ + + } + XSetForeground(display, gc, bg_pixel); + XFillRectangle(display, window, gc, 0, 0, rpng2_info.width, + rpng2_info.height); + } + +/*--------------------------------------------------------------------------- + Wait for first Expose event to do any drawing, then flush and return. + ---------------------------------------------------------------------------*/ + + do + XNextEvent(display, &e); + while (e.type != Expose || e.xexpose.count); + + XFlush(display); + + return 0; + +} /* end function rpng2_x_create_window() */ + + + + + +static int rpng2_x_load_bg_image(void) +{ + uch *src; + char *dest; + uch r1, r2, g1, g2, b1, b2; + uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv; + int k, hmax, max; + int xidx, yidx, yidx_max; + int even_odd_vert, even_odd_horiz, even_odd; + int invert_gradient2 = (bg[pat].type & 0x08); + int invert_column; + int ximage_rowbytes = ximage->bytes_per_line; + ulg i, row; + ulg pixel; + +/*--------------------------------------------------------------------------- + Allocate buffer for fake background image to be used with transparent + images; if this fails, revert to plain background color. + ---------------------------------------------------------------------------*/ + + bg_rowbytes = 3 * rpng2_info.width; + bg_data = (uch *)malloc(bg_rowbytes * rpng2_info.height); + if (!bg_data) { + fprintf(stderr, PROGNAME + ": unable to allocate memory for background image\n"); + bg_image = 0; + return 1; + } + + bgscale = (pat == 0)? 8 : bgscale_default; + yidx_max = bgscale - 1; + +/*--------------------------------------------------------------------------- + Vertical gradients (ramps) in NxN squares, alternating direction and + colors (N == bgscale). + ---------------------------------------------------------------------------*/ + + if ((bg[pat].type & 0x07) == 0) { + uch r1_min = rgb[bg[pat].rgb1_min].r; + uch g1_min = rgb[bg[pat].rgb1_min].g; + uch b1_min = rgb[bg[pat].rgb1_min].b; + uch r2_min = rgb[bg[pat].rgb2_min].r; + uch g2_min = rgb[bg[pat].rgb2_min].g; + uch b2_min = rgb[bg[pat].rgb2_min].b; + int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min; + int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min; + int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min; + int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min; + int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min; + int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min; + + for (row = 0; row < rpng2_info.height; ++row) { + yidx = (int)(row % bgscale); + even_odd_vert = (int)((row / bgscale) & 1); + + r1 = r1_min + (r1_diff * yidx) / yidx_max; + g1 = g1_min + (g1_diff * yidx) / yidx_max; + b1 = b1_min + (b1_diff * yidx) / yidx_max; + r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max; + g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max; + b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max; + + r2 = r2_min + (r2_diff * yidx) / yidx_max; + g2 = g2_min + (g2_diff * yidx) / yidx_max; + b2 = b2_min + (b2_diff * yidx) / yidx_max; + r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max; + g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max; + b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max; + + dest = (char *)bg_data + row*bg_rowbytes; + for (i = 0; i < rpng2_info.width; ++i) { + even_odd_horiz = (int)((i / bgscale) & 1); + even_odd = even_odd_vert ^ even_odd_horiz; + invert_column = + (even_odd_horiz && (bg[pat].type & 0x10)); + if (even_odd == 0) { /* gradient #1 */ + if (invert_column) { + *dest++ = r1_inv; + *dest++ = g1_inv; + *dest++ = b1_inv; + } else { + *dest++ = r1; + *dest++ = g1; + *dest++ = b1; + } + } else { /* gradient #2 */ + if ((invert_column && invert_gradient2) || + (!invert_column && !invert_gradient2)) + { + *dest++ = r2; /* not inverted or */ + *dest++ = g2; /* doubly inverted */ + *dest++ = b2; + } else { + *dest++ = r2_inv; + *dest++ = g2_inv; /* singly inverted */ + *dest++ = b2_inv; + } + } + } + } + +/*--------------------------------------------------------------------------- + Soft gradient-diamonds with scale = bgscale. Code contributed by Adam + M. Costello. + ---------------------------------------------------------------------------*/ + + } else if ((bg[pat].type & 0x07) == 1) { + + hmax = (bgscale-1)/2; /* half the max weight of a color */ + max = 2*hmax; /* the max weight of a color */ + + r1 = rgb[bg[pat].rgb1_max].r; + g1 = rgb[bg[pat].rgb1_max].g; + b1 = rgb[bg[pat].rgb1_max].b; + r2 = rgb[bg[pat].rgb2_max].r; + g2 = rgb[bg[pat].rgb2_max].g; + b2 = rgb[bg[pat].rgb2_max].b; + + for (row = 0; row < rpng2_info.height; ++row) { + yidx = (int)(row % bgscale); + if (yidx > hmax) + yidx = bgscale-1 - yidx; + dest = (char *)bg_data + row*bg_rowbytes; + for (i = 0; i < rpng2_info.width; ++i) { + xidx = (int)(i % bgscale); + if (xidx > hmax) + xidx = bgscale-1 - xidx; + k = xidx + yidx; + *dest++ = (k*r1 + (max-k)*r2) / max; + *dest++ = (k*g1 + (max-k)*g2) / max; + *dest++ = (k*b1 + (max-k)*b2) / max; + } + } + +/*--------------------------------------------------------------------------- + Radial "starburst" with azimuthal sinusoids; [eventually number of sinu- + soids will equal bgscale?]. This one is slow but very cool. Code con- + tributed by Pieter S. van der Meulen (originally in Smalltalk). + ---------------------------------------------------------------------------*/ + + } else if ((bg[pat].type & 0x07) == 2) { + uch ch; + int ii, x, y, hw, hh, grayspot; + double freq, rotate, saturate, gray, intensity; + double angle=0.0, aoffset=0.0, maxDist, dist; + double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t; + + fprintf(stderr, "%s: computing radial background...", + PROGNAME); + fflush(stderr); + + hh = (int)(rpng2_info.height / 2); + hw = (int)(rpng2_info.width / 2); + + /* variables for radial waves: + * aoffset: number of degrees to rotate hue [CURRENTLY NOT USED] + * freq: number of color beams originating from the center + * grayspot: size of the graying center area (anti-alias) + * rotate: rotation of the beams as a function of radius + * saturate: saturation of beams' shape azimuthally + */ + angle = CLIP(angle, 0.0, 360.0); + grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw)); + freq = MAX((double)bg[pat].bg_freq, 0.0); + saturate = (double)bg[pat].bg_bsat * 0.1; + rotate = (double)bg[pat].bg_brot * 0.1; + gray = 0.0; + intensity = 0.0; + maxDist = (double)((hw*hw) + (hh*hh)); + + for (row = 0; row < rpng2_info.height; ++row) { + y = (int)(row - hh); + dest = (char *)bg_data + row*bg_rowbytes; + for (i = 0; i < rpng2_info.width; ++i) { + x = (int)(i - hw); + angle = (x == 0)? PI_2 : atan((double)y / (double)x); + gray = (double)MAX(ABS(y), ABS(x)) / grayspot; + gray = MIN(1.0, gray); + dist = (double)((x*x) + (y*y)) / maxDist; + intensity = cos((angle+(rotate*dist*PI)) * freq) * + gray * saturate; + intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5; + hue = (angle + PI) * INV_PI_360 + aoffset; + s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh)); + s = MIN(MAX(s,0.0), 1.0); + v = MIN(MAX(intensity,0.0), 1.0); + + if (s == 0.0) { + ch = (uch)(v * 255.0); + *dest++ = ch; + *dest++ = ch; + *dest++ = ch; + } else { + if ((hue < 0.0) || (hue >= 360.0)) + hue -= (((int)(hue / 360.0)) * 360.0); + hue /= 60.0; + ii = (int)hue; + f = hue - (double)ii; + p = (1.0 - s) * v; + q = (1.0 - (s * f)) * v; + t = (1.0 - (s * (1.0 - f))) * v; + if (ii == 0) { red = v; green = t; blue = p; } + else if (ii == 1) { red = q; green = v; blue = p; } + else if (ii == 2) { red = p; green = v; blue = t; } + else if (ii == 3) { red = p; green = q; blue = v; } + else if (ii == 4) { red = t; green = p; blue = v; } + else if (ii == 5) { red = v; green = p; blue = q; } + *dest++ = (uch)(red * 255.0); + *dest++ = (uch)(green * 255.0); + *dest++ = (uch)(blue * 255.0); + } + } + } + fprintf(stderr, "done.\n"); + fflush(stderr); + } + +/*--------------------------------------------------------------------------- + Blast background image to display buffer before beginning PNG decode. + ---------------------------------------------------------------------------*/ + + if (depth == 24 || depth == 32) { + ulg red, green, blue; + int bpp = ximage->bits_per_pixel; + + for (row = 0; row < rpng2_info.height; ++row) { + src = bg_data + row*bg_rowbytes; + dest = ximage->data + row*ximage_rowbytes; + if (bpp == 32) { /* slightly optimized version */ + for (i = rpng2_info.width; i > 0; --i) { + red = *src++; + green = *src++; + blue = *src++; + pixel = (red << RShift) | + (green << GShift) | + (blue << BShift); + /* recall that we set ximage->byte_order = MSBFirst above */ + *dest++ = (char)((pixel >> 24) & 0xff); + *dest++ = (char)((pixel >> 16) & 0xff); + *dest++ = (char)((pixel >> 8) & 0xff); + *dest++ = (char)( pixel & 0xff); + } + } else { + for (i = rpng2_info.width; i > 0; --i) { + red = *src++; + green = *src++; + blue = *src++; + pixel = (red << RShift) | + (green << GShift) | + (blue << BShift); + /* recall that we set ximage->byte_order = MSBFirst above */ + /* GRR BUG? this assumes bpp == 24 & bits are packed low */ + /* (probably need to use RShift, RMask, etc.) */ + *dest++ = (char)((pixel >> 16) & 0xff); + *dest++ = (char)((pixel >> 8) & 0xff); + *dest++ = (char)( pixel & 0xff); + } + } + } + + } else if (depth == 16) { + ush red, green, blue; + + for (row = 0; row < rpng2_info.height; ++row) { + src = bg_data + row*bg_rowbytes; + dest = ximage->data + row*ximage_rowbytes; + for (i = rpng2_info.width; i > 0; --i) { + red = ((ush)(*src) << 8); ++src; + green = ((ush)(*src) << 8); ++src; + blue = ((ush)(*src) << 8); ++src; + pixel = ((red >> RShift) & RMask) | + ((green >> GShift) & GMask) | + ((blue >> BShift) & BMask); + /* recall that we set ximage->byte_order = MSBFirst above */ + *dest++ = (char)((pixel >> 8) & 0xff); + *dest++ = (char)( pixel & 0xff); + } + } + + } else /* depth == 8 */ { + + /* GRR: add 8-bit support */ + + } + + XPutImage(display, window, gc, ximage, 0, 0, 0, 0, rpng2_info.width, + rpng2_info.height); + + return 0; + +} /* end function rpng2_x_load_bg_image() */ + + + + + +static void rpng2_x_display_row(ulg row) +{ + uch bg_red = rpng2_info.bg_red; + uch bg_green = rpng2_info.bg_green; + uch bg_blue = rpng2_info.bg_blue; + uch *src, *src2=NULL; + char *dest; + uch r, g, b, a; + int ximage_rowbytes = ximage->bytes_per_line; + ulg i, pixel; + static int rows=0, prevpass=(-1); + static ulg firstrow; + +/*--------------------------------------------------------------------------- + rows and firstrow simply track how many rows (and which ones) have not + yet been displayed; alternatively, we could call XPutImage() for every + row and not bother with the records-keeping. + ---------------------------------------------------------------------------*/ + + Trace((stderr, "beginning rpng2_x_display_row()\n")) + + if (rpng2_info.pass != prevpass) { + if (pause_after_pass && rpng2_info.pass > 0) { + XEvent e; + KeySym k; + + fprintf(stderr, + "%s: end of pass %d of 7; click in image window to continue\n", + PROGNAME, prevpass + 1); + do + XNextEvent(display, &e); + while (!QUIT(e,k)); + } + fprintf(stderr, "%s: pass %d of 7\r", PROGNAME, rpng2_info.pass + 1); + fflush(stderr); + prevpass = rpng2_info.pass; + } + + if (rows == 0) + firstrow = row; /* first row that is not yet displayed */ + + ++rows; /* count of rows received but not yet displayed */ + +/*--------------------------------------------------------------------------- + Aside from the use of the rpng2_info struct, the lack of an outer loop + (over rows) and moving the XPutImage() call outside the "if (depth)" + tests, this routine is identical to rpng_x_display_image() in the non- + progressive version of the program. + ---------------------------------------------------------------------------*/ + + if (depth == 24 || depth == 32) { + ulg red, green, blue; + int bpp = ximage->bits_per_pixel; + + src = rpng2_info.image_data + row*rpng2_info.rowbytes; + if (bg_image) + src2 = bg_data + row*bg_rowbytes; + dest = ximage->data + row*ximage_rowbytes; + if (rpng2_info.channels == 3) { + for (i = rpng2_info.width; i > 0; --i) { + red = *src++; + green = *src++; + blue = *src++; + pixel = (red << RShift) | + (green << GShift) | + (blue << BShift); + /* recall that we set ximage->byte_order = MSBFirst above */ + if (bpp == 32) { + *dest++ = (char)((pixel >> 24) & 0xff); + *dest++ = (char)((pixel >> 16) & 0xff); + *dest++ = (char)((pixel >> 8) & 0xff); + *dest++ = (char)( pixel & 0xff); + } else { + /* GRR BUG? this assumes bpp == 24 & bits are packed low */ + /* (probably need to use RShift, RMask, etc.) */ + *dest++ = (char)((pixel >> 16) & 0xff); + *dest++ = (char)((pixel >> 8) & 0xff); + *dest++ = (char)( pixel & 0xff); + } + } + } else /* if (rpng2_info.channels == 4) */ { + for (i = rpng2_info.width; i > 0; --i) { + r = *src++; + g = *src++; + b = *src++; + a = *src++; + if (bg_image) { + bg_red = *src2++; + bg_green = *src2++; + bg_blue = *src2++; + } + if (a == 255) { + red = r; + green = g; + blue = b; + } else if (a == 0) { + red = bg_red; + green = bg_green; + blue = bg_blue; + } else { + /* this macro (from png.h) composites the foreground + * and background values and puts the result into the + * first argument */ + alpha_composite(red, r, a, bg_red); + alpha_composite(green, g, a, bg_green); + alpha_composite(blue, b, a, bg_blue); + } + pixel = (red << RShift) | + (green << GShift) | + (blue << BShift); + /* recall that we set ximage->byte_order = MSBFirst above */ + if (bpp == 32) { + *dest++ = (char)((pixel >> 24) & 0xff); + *dest++ = (char)((pixel >> 16) & 0xff); + *dest++ = (char)((pixel >> 8) & 0xff); + *dest++ = (char)( pixel & 0xff); + } else { + /* GRR BUG? this assumes bpp == 24 & bits are packed low */ + /* (probably need to use RShift, RMask, etc.) */ + *dest++ = (char)((pixel >> 16) & 0xff); + *dest++ = (char)((pixel >> 8) & 0xff); + *dest++ = (char)( pixel & 0xff); + } + } + } + + } else if (depth == 16) { + ush red, green, blue; + + src = rpng2_info.row_pointers[row]; + if (bg_image) + src2 = bg_data + row*bg_rowbytes; + dest = ximage->data + row*ximage_rowbytes; + if (rpng2_info.channels == 3) { + for (i = rpng2_info.width; i > 0; --i) { + red = ((ush)(*src) << 8); + ++src; + green = ((ush)(*src) << 8); + ++src; + blue = ((ush)(*src) << 8); + ++src; + pixel = ((red >> RShift) & RMask) | + ((green >> GShift) & GMask) | + ((blue >> BShift) & BMask); + /* recall that we set ximage->byte_order = MSBFirst above */ + *dest++ = (char)((pixel >> 8) & 0xff); + *dest++ = (char)( pixel & 0xff); + } + } else /* if (rpng2_info.channels == 4) */ { + for (i = rpng2_info.width; i > 0; --i) { + r = *src++; + g = *src++; + b = *src++; + a = *src++; + if (bg_image) { + bg_red = *src2++; + bg_green = *src2++; + bg_blue = *src2++; + } + if (a == 255) { + red = ((ush)r << 8); + green = ((ush)g << 8); + blue = ((ush)b << 8); + } else if (a == 0) { + red = ((ush)bg_red << 8); + green = ((ush)bg_green << 8); + blue = ((ush)bg_blue << 8); + } else { + /* this macro (from png.h) composites the foreground + * and background values and puts the result back into + * the first argument (== fg byte here: safe) */ + alpha_composite(r, r, a, bg_red); + alpha_composite(g, g, a, bg_green); + alpha_composite(b, b, a, bg_blue); + red = ((ush)r << 8); + green = ((ush)g << 8); + blue = ((ush)b << 8); + } + pixel = ((red >> RShift) & RMask) | + ((green >> GShift) & GMask) | + ((blue >> BShift) & BMask); + /* recall that we set ximage->byte_order = MSBFirst above */ + *dest++ = (char)((pixel >> 8) & 0xff); + *dest++ = (char)( pixel & 0xff); + } + } + + } else /* depth == 8 */ { + + /* GRR: add 8-bit support */ + + } + + +/*--------------------------------------------------------------------------- + Display after every 16 rows or when on one of last two rows. (Region + may include previously displayed lines due to interlacing--i.e., not + contiguous. Also, second-to-last row is final one in interlaced images + with odd number of rows.) For demos, flush (and delay) after every 16th + row so "sparse" passes don't go twice as fast. + ---------------------------------------------------------------------------*/ + + if (demo_timing && (row - firstrow >= 16 || row >= rpng2_info.height-2)) { + XPutImage(display, window, gc, ximage, 0, (int)firstrow, 0, + (int)firstrow, rpng2_info.width, row - firstrow + 1); + XFlush(display); + rows = 0; + usleep(usleep_duration); + } else + if (!demo_timing && ((rows & 0xf) == 0 || row >= rpng2_info.height-2)) { + XPutImage(display, window, gc, ximage, 0, (int)firstrow, 0, + (int)firstrow, rpng2_info.width, row - firstrow + 1); + XFlush(display); + rows = 0; + } + +} + + + + + +static void rpng2_x_finish_display(void) +{ + Trace((stderr, "beginning rpng2_x_finish_display()\n")) + + /* last row has already been displayed by rpng2_x_display_row(), so we + * have nothing to do here except set a flag and let the user know that + * the image is done */ + + rpng2_info.done = TRUE; + printf( + "Done. Press Q, Esc or mouse button 1 (within image window) to quit.\n"); + fflush(stdout); +} + + + + + +static void rpng2_x_redisplay_image(ulg startcol, ulg startrow, + ulg width, ulg height) +{ + uch bg_red = rpng2_info.bg_red; + uch bg_green = rpng2_info.bg_green; + uch bg_blue = rpng2_info.bg_blue; + uch *src, *src2=NULL; + char *dest; + uch r, g, b, a; + ulg i, row, lastrow = 0; + ulg pixel; + int ximage_rowbytes = ximage->bytes_per_line; + + + Trace((stderr, "beginning display loop (image_channels == %d)\n", + image_channels)) + Trace((stderr, " (width = %ld, rowbytes = %ld, ximage_rowbytes = %d)\n", + rpng2_info.width, image_rowbytes, ximage_rowbytes)) + Trace((stderr, " (bpp = %d)\n", ximage->bits_per_pixel)) + Trace((stderr, " (byte_order = %s)\n", ximage->byte_order == MSBFirst? + "MSBFirst" : (ximage->byte_order == LSBFirst? "LSBFirst" : "unknown"))) + +/*--------------------------------------------------------------------------- + Aside from the use of the rpng2_info struct and of src2 (for background + image), this routine is identical to rpng_x_display_image() in the non- + progressive version of the program--for the simple reason that redisplay + of the image against a new background happens after the image is fully + decoded and therefore is, by definition, non-progressive. + ---------------------------------------------------------------------------*/ + + if (depth == 24 || depth == 32) { + ulg red, green, blue; + int bpp = ximage->bits_per_pixel; + + for (lastrow = row = startrow; row < startrow+height; ++row) { + src = rpng2_info.image_data + row*rpng2_info.rowbytes; + if (bg_image) + src2 = bg_data + row*bg_rowbytes; + dest = ximage->data + row*ximage_rowbytes; + if (rpng2_info.channels == 3) { + for (i = rpng2_info.width; i > 0; --i) { + red = *src++; + green = *src++; + blue = *src++; +#ifdef NO_24BIT_MASKS + pixel = (red << RShift) | + (green << GShift) | + (blue << BShift); + /* recall that we set ximage->byte_order = MSBFirst above */ + if (bpp == 32) { + *dest++ = (char)((pixel >> 24) & 0xff); + *dest++ = (char)((pixel >> 16) & 0xff); + *dest++ = (char)((pixel >> 8) & 0xff); + *dest++ = (char)( pixel & 0xff); + } else { + /* this assumes bpp == 24 & bits are packed low */ + /* (probably need to use RShift, RMask, etc.) */ + *dest++ = (char)((pixel >> 16) & 0xff); + *dest++ = (char)((pixel >> 8) & 0xff); + *dest++ = (char)( pixel & 0xff); + } +#else + red = (RShift < 0)? red << (-RShift) : red >> RShift; + green = (GShift < 0)? green << (-GShift) : green >> GShift; + blue = (BShift < 0)? blue << (-BShift) : blue >> BShift; + pixel = (red & RMask) | (green & GMask) | (blue & BMask); + /* recall that we set ximage->byte_order = MSBFirst above */ + if (bpp == 32) { + *dest++ = (char)((pixel >> 24) & 0xff); + *dest++ = (char)((pixel >> 16) & 0xff); + *dest++ = (char)((pixel >> 8) & 0xff); + *dest++ = (char)( pixel & 0xff); + } else { + /* GRR BUG */ + /* this assumes bpp == 24 & bits are packed low */ + /* (probably need to use RShift/RMask/etc. here, too) */ + *dest++ = (char)((pixel >> 16) & 0xff); + *dest++ = (char)((pixel >> 8) & 0xff); + *dest++ = (char)( pixel & 0xff); + } +#endif + } + + } else /* if (rpng2_info.channels == 4) */ { + for (i = rpng2_info.width; i > 0; --i) { + r = *src++; + g = *src++; + b = *src++; + a = *src++; + if (bg_image) { + bg_red = *src2++; + bg_green = *src2++; + bg_blue = *src2++; + } + if (a == 255) { + red = r; + green = g; + blue = b; + } else if (a == 0) { + red = bg_red; + green = bg_green; + blue = bg_blue; + } else { + /* this macro (from png.h) composites the foreground + * and background values and puts the result into the + * first argument */ + alpha_composite(red, r, a, bg_red); + alpha_composite(green, g, a, bg_green); + alpha_composite(blue, b, a, bg_blue); + } +#ifdef NO_24BIT_MASKS + pixel = (red << RShift) | + (green << GShift) | + (blue << BShift); + /* recall that we set ximage->byte_order = MSBFirst above */ + if (bpp == 32) { + *dest++ = (char)((pixel >> 24) & 0xff); + *dest++ = (char)((pixel >> 16) & 0xff); + *dest++ = (char)((pixel >> 8) & 0xff); + *dest++ = (char)( pixel & 0xff); + } else { + /* this assumes bpp == 24 & bits are packed low */ + /* (probably need to use RShift, RMask, etc.) */ + *dest++ = (char)((pixel >> 16) & 0xff); + *dest++ = (char)((pixel >> 8) & 0xff); + *dest++ = (char)( pixel & 0xff); + } +#else + red = (RShift < 0)? red << (-RShift) : red >> RShift; + green = (GShift < 0)? green << (-GShift) : green >> GShift; + blue = (BShift < 0)? blue << (-BShift) : blue >> BShift; + pixel = (red & RMask) | (green & GMask) | (blue & BMask); + /* recall that we set ximage->byte_order = MSBFirst above */ + if (bpp == 32) { + *dest++ = (char)((pixel >> 24) & 0xff); + *dest++ = (char)((pixel >> 16) & 0xff); + *dest++ = (char)((pixel >> 8) & 0xff); + *dest++ = (char)( pixel & 0xff); + } else { + /* GRR BUG */ + /* this assumes bpp == 24 & bits are packed low */ + /* (probably need to use RShift/RMask/etc. here, too) */ + *dest++ = (char)((pixel >> 16) & 0xff); + *dest++ = (char)((pixel >> 8) & 0xff); + *dest++ = (char)( pixel & 0xff); + } +#endif + } + } + /* display after every 16 lines */ + if (((row+1) & 0xf) == 0) { + XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0, + (int)lastrow, rpng2_info.width, 16); + XFlush(display); + lastrow = row + 1; + } + } + + } else if (depth == 16) { + ush red, green, blue; + + for (lastrow = row = startrow; row < startrow+height; ++row) { + src = rpng2_info.row_pointers[row]; + if (bg_image) + src2 = bg_data + row*bg_rowbytes; + dest = ximage->data + row*ximage_rowbytes; + if (rpng2_info.channels == 3) { + for (i = rpng2_info.width; i > 0; --i) { + red = ((ush)(*src) << 8); + ++src; + green = ((ush)(*src) << 8); + ++src; + blue = ((ush)(*src) << 8); + ++src; + pixel = ((red >> RShift) & RMask) | + ((green >> GShift) & GMask) | + ((blue >> BShift) & BMask); + /* recall that we set ximage->byte_order = MSBFirst above */ + *dest++ = (char)((pixel >> 8) & 0xff); + *dest++ = (char)( pixel & 0xff); + } + } else /* if (rpng2_info.channels == 4) */ { + for (i = rpng2_info.width; i > 0; --i) { + r = *src++; + g = *src++; + b = *src++; + a = *src++; + if (bg_image) { + bg_red = *src2++; + bg_green = *src2++; + bg_blue = *src2++; + } + if (a == 255) { + red = ((ush)r << 8); + green = ((ush)g << 8); + blue = ((ush)b << 8); + } else if (a == 0) { + red = ((ush)bg_red << 8); + green = ((ush)bg_green << 8); + blue = ((ush)bg_blue << 8); + } else { + /* this macro (from png.h) composites the foreground + * and background values and puts the result back into + * the first argument (== fg byte here: safe) */ + alpha_composite(r, r, a, bg_red); + alpha_composite(g, g, a, bg_green); + alpha_composite(b, b, a, bg_blue); + red = ((ush)r << 8); + green = ((ush)g << 8); + blue = ((ush)b << 8); + } + pixel = ((red >> RShift) & RMask) | + ((green >> GShift) & GMask) | + ((blue >> BShift) & BMask); + /* recall that we set ximage->byte_order = MSBFirst above */ + *dest++ = (char)((pixel >> 8) & 0xff); + *dest++ = (char)( pixel & 0xff); + } + } + /* display after every 16 lines */ + if (((row+1) & 0xf) == 0) { + XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0, + (int)lastrow, rpng2_info.width, 16); + XFlush(display); + lastrow = row + 1; + } + } + + } else /* depth == 8 */ { + + /* GRR: add 8-bit support */ + + } + + Trace((stderr, "calling final XPutImage()\n")) + if (lastrow < startrow+height) { + XPutImage(display, window, gc, ximage, 0, (int)lastrow, 0, + (int)lastrow, rpng2_info.width, rpng2_info.height-lastrow); + XFlush(display); + } + +} /* end function rpng2_x_redisplay_image() */ + + + + + +#ifdef FEATURE_LOOP + +static void rpng2_x_reload_bg_image(void) +{ + char *dest; + uch r1, r2, g1, g2, b1, b2; + uch r1_inv, r2_inv, g1_inv, g2_inv, b1_inv, b2_inv; + int k, hmax, max; + int xidx, yidx, yidx_max; + int even_odd_vert, even_odd_horiz, even_odd; + int invert_gradient2 = (bg[pat].type & 0x08); + int invert_column; + ulg i, row; + + + bgscale = (pat == 0)? 8 : bgscale_default; + yidx_max = bgscale - 1; + +/*--------------------------------------------------------------------------- + Vertical gradients (ramps) in NxN squares, alternating direction and + colors (N == bgscale). + ---------------------------------------------------------------------------*/ + + if ((bg[pat].type & 0x07) == 0) { + uch r1_min = rgb[bg[pat].rgb1_min].r; + uch g1_min = rgb[bg[pat].rgb1_min].g; + uch b1_min = rgb[bg[pat].rgb1_min].b; + uch r2_min = rgb[bg[pat].rgb2_min].r; + uch g2_min = rgb[bg[pat].rgb2_min].g; + uch b2_min = rgb[bg[pat].rgb2_min].b; + int r1_diff = rgb[bg[pat].rgb1_max].r - r1_min; + int g1_diff = rgb[bg[pat].rgb1_max].g - g1_min; + int b1_diff = rgb[bg[pat].rgb1_max].b - b1_min; + int r2_diff = rgb[bg[pat].rgb2_max].r - r2_min; + int g2_diff = rgb[bg[pat].rgb2_max].g - g2_min; + int b2_diff = rgb[bg[pat].rgb2_max].b - b2_min; + + for (row = 0; row < rpng2_info.height; ++row) { + yidx = (int)(row % bgscale); + even_odd_vert = (int)((row / bgscale) & 1); + + r1 = r1_min + (r1_diff * yidx) / yidx_max; + g1 = g1_min + (g1_diff * yidx) / yidx_max; + b1 = b1_min + (b1_diff * yidx) / yidx_max; + r1_inv = r1_min + (r1_diff * (yidx_max-yidx)) / yidx_max; + g1_inv = g1_min + (g1_diff * (yidx_max-yidx)) / yidx_max; + b1_inv = b1_min + (b1_diff * (yidx_max-yidx)) / yidx_max; + + r2 = r2_min + (r2_diff * yidx) / yidx_max; + g2 = g2_min + (g2_diff * yidx) / yidx_max; + b2 = b2_min + (b2_diff * yidx) / yidx_max; + r2_inv = r2_min + (r2_diff * (yidx_max-yidx)) / yidx_max; + g2_inv = g2_min + (g2_diff * (yidx_max-yidx)) / yidx_max; + b2_inv = b2_min + (b2_diff * (yidx_max-yidx)) / yidx_max; + + dest = (char *)bg_data + row*bg_rowbytes; + for (i = 0; i < rpng2_info.width; ++i) { + even_odd_horiz = (int)((i / bgscale) & 1); + even_odd = even_odd_vert ^ even_odd_horiz; + invert_column = + (even_odd_horiz && (bg[pat].type & 0x10)); + if (even_odd == 0) { /* gradient #1 */ + if (invert_column) { + *dest++ = r1_inv; + *dest++ = g1_inv; + *dest++ = b1_inv; + } else { + *dest++ = r1; + *dest++ = g1; + *dest++ = b1; + } + } else { /* gradient #2 */ + if ((invert_column && invert_gradient2) || + (!invert_column && !invert_gradient2)) + { + *dest++ = r2; /* not inverted or */ + *dest++ = g2; /* doubly inverted */ + *dest++ = b2; + } else { + *dest++ = r2_inv; + *dest++ = g2_inv; /* singly inverted */ + *dest++ = b2_inv; + } + } + } + } + +/*--------------------------------------------------------------------------- + Soft gradient-diamonds with scale = bgscale. Code contributed by Adam + M. Costello. + ---------------------------------------------------------------------------*/ + + } else if ((bg[pat].type & 0x07) == 1) { + + hmax = (bgscale-1)/2; /* half the max weight of a color */ + max = 2*hmax; /* the max weight of a color */ + + r1 = rgb[bg[pat].rgb1_max].r; + g1 = rgb[bg[pat].rgb1_max].g; + b1 = rgb[bg[pat].rgb1_max].b; + r2 = rgb[bg[pat].rgb2_max].r; + g2 = rgb[bg[pat].rgb2_max].g; + b2 = rgb[bg[pat].rgb2_max].b; + + for (row = 0; row < rpng2_info.height; ++row) { + yidx = (int)(row % bgscale); + if (yidx > hmax) + yidx = bgscale-1 - yidx; + dest = (char *)bg_data + row*bg_rowbytes; + for (i = 0; i < rpng2_info.width; ++i) { + xidx = (int)(i % bgscale); + if (xidx > hmax) + xidx = bgscale-1 - xidx; + k = xidx + yidx; + *dest++ = (k*r1 + (max-k)*r2) / max; + *dest++ = (k*g1 + (max-k)*g2) / max; + *dest++ = (k*b1 + (max-k)*b2) / max; + } + } + +/*--------------------------------------------------------------------------- + Radial "starburst" with azimuthal sinusoids; [eventually number of sinu- + soids will equal bgscale?]. This one is slow but very cool. Code con- + tributed by Pieter S. van der Meulen (originally in Smalltalk). + ---------------------------------------------------------------------------*/ + + } else if ((bg[pat].type & 0x07) == 2) { + uch ch; + int ii, x, y, hw, hh, grayspot; + double freq, rotate, saturate, gray, intensity; + double angle=0.0, aoffset=0.0, maxDist, dist; + double red=0.0, green=0.0, blue=0.0, hue, s, v, f, p, q, t; + + hh = (int)(rpng2_info.height / 2); + hw = (int)(rpng2_info.width / 2); + + /* variables for radial waves: + * aoffset: number of degrees to rotate hue [CURRENTLY NOT USED] + * freq: number of color beams originating from the center + * grayspot: size of the graying center area (anti-alias) + * rotate: rotation of the beams as a function of radius + * saturate: saturation of beams' shape azimuthally + */ + angle = CLIP(angle, 0.0, 360.0); + grayspot = CLIP(bg[pat].bg_gray, 1, (hh + hw)); + freq = MAX((double)bg[pat].bg_freq, 0.0); + saturate = (double)bg[pat].bg_bsat * 0.1; + rotate = (double)bg[pat].bg_brot * 0.1; + gray = 0.0; + intensity = 0.0; + maxDist = (double)((hw*hw) + (hh*hh)); + + for (row = 0; row < rpng2_info.height; ++row) { + y = (int)(row - hh); + dest = (char *)bg_data + row*bg_rowbytes; + for (i = 0; i < rpng2_info.width; ++i) { + x = (int)(i - hw); + angle = (x == 0)? PI_2 : atan((double)y / (double)x); + gray = (double)MAX(ABS(y), ABS(x)) / grayspot; + gray = MIN(1.0, gray); + dist = (double)((x*x) + (y*y)) / maxDist; + intensity = cos((angle+(rotate*dist*PI)) * freq) * + gray * saturate; + intensity = (MAX(MIN(intensity,1.0),-1.0) + 1.0) * 0.5; + hue = (angle + PI) * INV_PI_360 + aoffset; + s = gray * ((double)(ABS(x)+ABS(y)) / (double)(hw + hh)); + s = MIN(MAX(s,0.0), 1.0); + v = MIN(MAX(intensity,0.0), 1.0); + + if (s == 0.0) { + ch = (uch)(v * 255.0); + *dest++ = ch; + *dest++ = ch; + *dest++ = ch; + } else { + if ((hue < 0.0) || (hue >= 360.0)) + hue -= (((int)(hue / 360.0)) * 360.0); + hue /= 60.0; + ii = (int)hue; + f = hue - (double)ii; + p = (1.0 - s) * v; + q = (1.0 - (s * f)) * v; + t = (1.0 - (s * (1.0 - f))) * v; + if (ii == 0) { red = v; green = t; blue = p; } + else if (ii == 1) { red = q; green = v; blue = p; } + else if (ii == 2) { red = p; green = v; blue = t; } + else if (ii == 3) { red = p; green = q; blue = v; } + else if (ii == 4) { red = t; green = p; blue = v; } + else if (ii == 5) { red = v; green = p; blue = q; } + *dest++ = (uch)(red * 255.0); + *dest++ = (uch)(green * 255.0); + *dest++ = (uch)(blue * 255.0); + } + } + } + } + +} /* end function rpng2_x_reload_bg_image() */ + + + + + +static int is_number(char *p) +{ + while (*p) { + if (!isdigit(*p)) + return FALSE; + ++p; + } + return TRUE; +} + +#endif /* FEATURE_LOOP */ + + + + + +static void rpng2_x_cleanup(void) +{ + if (bg_image && bg_data) { + free(bg_data); + bg_data = NULL; + } + + if (rpng2_info.image_data) { + free(rpng2_info.image_data); + rpng2_info.image_data = NULL; + } + + if (rpng2_info.row_pointers) { + free(rpng2_info.row_pointers); + rpng2_info.row_pointers = NULL; + } + + if (ximage) { + if (ximage->data) { + free(ximage->data); /* we allocated it, so we free it */ + ximage->data = (char *)NULL; /* instead of XDestroyImage() */ + } + XDestroyImage(ximage); + ximage = NULL; + } + + if (have_gc) + XFreeGC(display, gc); + + if (have_window) + XDestroyWindow(display, window); + + if (have_colormap) + XFreeColormap(display, colormap); + + if (have_nondefault_visual) + XFree(visual_list); +} + + + + + +static int rpng2_x_msb(ulg u32val) +{ + int i; + + for (i = 31; i >= 0; --i) { + if (u32val & 0x80000000L) + break; + u32val <<= 1; + } + return i; +} diff --git a/Engine/lib/lpng/contrib/gregbook/toucan.png b/Engine/lib/lpng/contrib/gregbook/toucan.png new file mode 100644 index 000000000..03960d493 Binary files /dev/null and b/Engine/lib/lpng/contrib/gregbook/toucan.png differ diff --git a/Engine/lib/lpng/contrib/gregbook/wpng.c b/Engine/lib/lpng/contrib/gregbook/wpng.c new file mode 100644 index 000000000..a06e3529e --- /dev/null +++ b/Engine/lib/lpng/contrib/gregbook/wpng.c @@ -0,0 +1,853 @@ +/*--------------------------------------------------------------------------- + + wpng - simple PNG-writing program wpng.c + + This program converts certain NetPBM binary files (grayscale and RGB, + maxval = 255) to PNG. Non-interlaced PNGs are written progressively; + interlaced PNGs are read and written in one memory-intensive blast. + + Thanks to Jean-loup Gailly for providing the necessary trick to read + interactive text from the keyboard while stdin is redirected. Thanks + to Cosmin Truta for Cygwin fixes. + + NOTE: includes provisional support for PNM type "8" (portable alphamap) + images, presumed to be a 32-bit interleaved RGBA format; no pro- + vision for possible interleaved grayscale+alpha (16-bit) format. + THIS IS UNLIKELY TO BECOME AN OFFICIAL NETPBM ALPHA FORMAT! + + to do: + - delete output file if quit before calling any writepng routines + - process backspace with -text option under DOS/Win? (currently get ^H) + + --------------------------------------------------------------------------- + + Changelog: + - 1.01: initial public release + - 1.02: modified to allow abbreviated options + - 1.03: removed extraneous character from usage screen; fixed bug in + command-line parsing + - 1.04: fixed DOS/OS2/Win32 detection, including partial Cygwin fix + (see http://home.att.net/~perlspinr/diffs/GregBook_cygwin.diff) + - 2.00: dual-licensed (added GNU GPL) + + [REPORTED BUG (win32 only): "contrib/gregbook/wpng.c - cmd line + dose not work! In order to do something useful I needed to redirect + both input and output, with cygwin and with bcc32 as well. Under + Linux, the same wpng appears to work fine. I don't know what is + the problem."] + + --------------------------------------------------------------------------- + + Copyright (c) 1998-2007 Greg Roelofs. All rights reserved. + + This software is provided "as is," without warranty of any kind, + express or implied. In no event shall the author or contributors + be held liable for any damages arising in any way from the use of + this software. + + The contents of this file are DUAL-LICENSED. You may modify and/or + redistribute this software according to the terms of one of the + following two licenses (at your option): + + + LICENSE 1 ("BSD-like with advertising clause"): + + 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. Redistributions of source code must retain the above copyright + notice, disclaimer, and this list of conditions. + 2. Redistributions in binary form must reproduce the above copyright + notice, disclaimer, and this list of conditions in the documenta- + tion and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + + This product includes software developed by Greg Roelofs + and contributors for the book, "PNG: The Definitive Guide," + published by O'Reilly and Associates. + + + LICENSE 2 (GNU GPL v2 or later): + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ---------------------------------------------------------------------------*/ + +#define PROGNAME "wpng" +#define VERSION "2.00 of 2 June 2007" +#define APPNAME "Simple PGM/PPM/PAM to PNG Converter" + +#if defined(__MSDOS__) || defined(__OS2__) +# define DOS_OS2_W32 +#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) +# ifndef __GNUC__ /* treat Win32 native ports of gcc as Unix environments */ +# define DOS_OS2_W32 +# endif +#endif + +#include +#include +#include +#include /* for jmpbuf declaration in writepng.h */ +#include + +#ifdef DOS_OS2_W32 +# include /* for isatty(), setmode() prototypes */ +# include /* O_BINARY for fdopen() without text translation */ +# ifdef __EMX__ +# ifndef getch +# define getch() _read_kbd(0, 1, 0) /* need getche() */ +# endif +# else /* !__EMX__ */ +# ifdef __GO32__ +# include +# define getch() getkey() /* GRR: need getche() */ +# else +# include /* for getche() console input */ +# endif +# endif /* ?__EMX__ */ +# define FGETS(buf,len,stream) dos_kbd_gets(buf,len) +#else +# include /* for isatty() prototype */ +# define FGETS fgets +#endif + +/* #define DEBUG : this enables the Trace() macros */ + +/* #define FORBID_LATIN1_CTRL : this requires the user to re-enter any + text that includes control characters discouraged by the PNG spec; text + that includes an escape character (27) must be re-entered regardless */ + +#include "writepng.h" /* typedefs, common macros, writepng prototypes */ + + + +/* local prototypes */ + +static int wpng_isvalid_latin1(uch *p, int len); +static void wpng_cleanup(void); + +#ifdef DOS_OS2_W32 + static char *dos_kbd_gets(char *buf, int len); +#endif + + + +static mainprog_info wpng_info; /* lone global */ + + + +int main(int argc, char **argv) +{ +#ifndef DOS_OS2_W32 + FILE *keybd; +#endif +#ifdef sgi + FILE *tmpfile; /* or we could just use keybd, since no overlap */ + char tmpline[80]; +#endif + char *inname = NULL, outname[256]; + char *p, pnmchar, pnmline[256]; + char *bgstr, *textbuf = NULL; + ulg rowbytes; + int rc, len = 0; + int error = 0; + int text = FALSE; + int maxval; + double LUT_exponent; /* just the lookup table */ + double CRT_exponent = 2.2; /* just the monitor */ + double default_display_exponent; /* whole display system */ + double default_gamma = 0.0; + + + wpng_info.infile = NULL; + wpng_info.outfile = NULL; + wpng_info.image_data = NULL; + wpng_info.row_pointers = NULL; + wpng_info.filter = FALSE; + wpng_info.interlaced = FALSE; + wpng_info.have_bg = FALSE; + wpng_info.have_time = FALSE; + wpng_info.have_text = 0; + wpng_info.gamma = 0.0; + + + /* First get the default value for our display-system exponent, i.e., + * the product of the CRT exponent and the exponent corresponding to + * the frame-buffer's lookup table (LUT), if any. If the PNM image + * looks correct on the user's display system, its file gamma is the + * inverse of this value. (Note that this is not an exhaustive list + * of LUT values--e.g., OpenStep has a lot of weird ones--but it should + * cover 99% of the current possibilities. This section must ensure + * that default_display_exponent is positive.) */ + +#if defined(NeXT) + /* third-party utilities can modify the default LUT exponent */ + LUT_exponent = 1.0 / 2.2; + /* + if (some_next_function_that_returns_gamma(&next_gamma)) + LUT_exponent = 1.0 / next_gamma; + */ +#elif defined(sgi) + LUT_exponent = 1.0 / 1.7; + /* there doesn't seem to be any documented function to + * get the "gamma" value, so we do it the hard way */ + tmpfile = fopen("/etc/config/system.glGammaVal", "r"); + if (tmpfile) { + double sgi_gamma; + + fgets(tmpline, 80, tmpfile); + fclose(tmpfile); + sgi_gamma = atof(tmpline); + if (sgi_gamma > 0.0) + LUT_exponent = 1.0 / sgi_gamma; + } +#elif defined(Macintosh) + LUT_exponent = 1.8 / 2.61; + /* + if (some_mac_function_that_returns_gamma(&mac_gamma)) + LUT_exponent = mac_gamma / 2.61; + */ +#else + LUT_exponent = 1.0; /* assume no LUT: most PCs */ +#endif + + /* the defaults above give 1.0, 1.3, 1.5 and 2.2, respectively: */ + default_display_exponent = LUT_exponent * CRT_exponent; + + + /* If the user has set the SCREEN_GAMMA environment variable as suggested + * (somewhat imprecisely) in the libpng documentation, use that; otherwise + * use the default value we just calculated. Either way, the user may + * override this via a command-line option. */ + + if ((p = getenv("SCREEN_GAMMA")) != NULL) { + double exponent = atof(p); + + if (exponent > 0.0) + default_gamma = 1.0 / exponent; + } + + if (default_gamma == 0.0) + default_gamma = 1.0 / default_display_exponent; + + + /* Now parse the command line for options and the PNM filename. */ + + while (*++argv && !error) { + if (!strncmp(*argv, "-i", 2)) { + wpng_info.interlaced = TRUE; + } else if (!strncmp(*argv, "-time", 3)) { + wpng_info.modtime = time(NULL); + wpng_info.have_time = TRUE; + } else if (!strncmp(*argv, "-text", 3)) { + text = TRUE; + } else if (!strncmp(*argv, "-gamma", 2)) { + if (!*++argv) + ++error; + else { + wpng_info.gamma = atof(*argv); + if (wpng_info.gamma <= 0.0) + ++error; + else if (wpng_info.gamma > 1.01) + fprintf(stderr, PROGNAME + " warning: file gammas are usually less than 1.0\n"); + } + } else if (!strncmp(*argv, "-bgcolor", 4)) { + if (!*++argv) + ++error; + else { + bgstr = *argv; + if (strlen(bgstr) != 7 || bgstr[0] != '#') + ++error; + else { + unsigned r, g, b; /* this way quiets compiler warnings */ + + sscanf(bgstr+1, "%2x%2x%2x", &r, &g, &b); + wpng_info.bg_red = (uch)r; + wpng_info.bg_green = (uch)g; + wpng_info.bg_blue = (uch)b; + wpng_info.have_bg = TRUE; + } + } + } else { + if (**argv != '-') { + inname = *argv; + if (argv[1]) /* shouldn't be any more args after filename */ + ++error; + } else + ++error; /* not expecting any other options */ + } + } + + + /* open the input and output files, or register an error and abort */ + + if (!inname) { + if (isatty(0)) { + fprintf(stderr, PROGNAME + ": must give input filename or provide image data via stdin\n"); + ++error; + } else { +#ifdef DOS_OS2_W32 + /* some buggy C libraries require BOTH setmode() and fdopen(bin) */ + setmode(fileno(stdin), O_BINARY); + setmode(fileno(stdout), O_BINARY); +#endif + if ((wpng_info.infile = fdopen(fileno(stdin), "rb")) == NULL) { + fprintf(stderr, PROGNAME + ": unable to reopen stdin in binary mode\n"); + ++error; + } else + if ((wpng_info.outfile = fdopen(fileno(stdout), "wb")) == NULL) { + fprintf(stderr, PROGNAME + ": unable to reopen stdout in binary mode\n"); + fclose(wpng_info.infile); + ++error; + } else + wpng_info.filter = TRUE; + } + } else if ((len = strlen(inname)) > 250) { + fprintf(stderr, PROGNAME ": input filename is too long [%d chars]\n", + len); + ++error; + } else if (!(wpng_info.infile = fopen(inname, "rb"))) { + fprintf(stderr, PROGNAME ": can't open input file [%s]\n", inname); + ++error; + } + + if (!error) { + fgets(pnmline, 256, wpng_info.infile); + if (pnmline[0] != 'P' || ((pnmchar = pnmline[1]) != '5' && + pnmchar != '6' && pnmchar != '8')) + { + fprintf(stderr, PROGNAME + ": input file [%s] is not a binary PGM, PPM or PAM file\n", + inname); + ++error; + } else { + wpng_info.pnmtype = (int)(pnmchar - '0'); + if (wpng_info.pnmtype != 8) + wpng_info.have_bg = FALSE; /* no need for bg if opaque */ + do { + fgets(pnmline, 256, wpng_info.infile); /* lose any comments */ + } while (pnmline[0] == '#'); + sscanf(pnmline, "%ld %ld", &wpng_info.width, &wpng_info.height); + do { + fgets(pnmline, 256, wpng_info.infile); /* more comment lines */ + } while (pnmline[0] == '#'); + sscanf(pnmline, "%d", &maxval); + if (wpng_info.width <= 0L || wpng_info.height <= 0L || + maxval != 255) + { + fprintf(stderr, PROGNAME + ": only positive width/height, maxval == 255 allowed \n"); + ++error; + } + wpng_info.sample_depth = 8; /* <==> maxval 255 */ + + if (!wpng_info.filter) { + /* make outname from inname */ + if ((p = strrchr(inname, '.')) == NULL || + (p - inname) != (len - 4)) + { + strcpy(outname, inname); + strcpy(outname+len, ".png"); + } else { + len -= 4; + strncpy(outname, inname, len); + strcpy(outname+len, ".png"); + } + /* check if outname already exists; if not, open */ + if ((wpng_info.outfile = fopen(outname, "rb")) != NULL) { + fprintf(stderr, PROGNAME ": output file exists [%s]\n", + outname); + fclose(wpng_info.outfile); + ++error; + } else if (!(wpng_info.outfile = fopen(outname, "wb"))) { + fprintf(stderr, PROGNAME ": can't open output file [%s]\n", + outname); + ++error; + } + } + } + if (error) { + fclose(wpng_info.infile); + wpng_info.infile = NULL; + if (wpng_info.filter) { + fclose(wpng_info.outfile); + wpng_info.outfile = NULL; + } + } + } + + + /* if we had any errors, print usage and die horrible death...arrr! */ + + if (error) { + fprintf(stderr, "\n%s %s: %s\n", PROGNAME, VERSION, APPNAME); + writepng_version_info(); + fprintf(stderr, "\n" +"Usage: %s [-gamma exp] [-bgcolor bg] [-text] [-time] [-interlace] pnmfile\n" +"or: ... | %s [-gamma exp] [-bgcolor bg] [-text] [-time] [-interlace] | ...\n" + " exp \ttransfer-function exponent (``gamma'') of the image in\n" + "\t\t floating-point format (e.g., ``%.5f''); if image looks\n" + "\t\t correct on given display system, image gamma is equal to\n" + "\t\t inverse of display-system exponent, i.e., 1 / (LUT * CRT)\n" + "\t\t (where LUT = lookup-table exponent and CRT = CRT exponent;\n" + "\t\t first varies, second is usually 2.2, all are positive)\n" + " bg \tdesired background color for alpha-channel images, in\n" + "\t\t 7-character hex RGB format (e.g., ``#ff7700'' for orange:\n" + "\t\t same as HTML colors)\n" + " -text\tprompt interactively for text info (tEXt chunks)\n" + " -time\tinclude a tIME chunk (last modification time)\n" + " -interlace\twrite interlaced PNG image\n" + "\n" +"pnmfile or stdin must be a binary PGM (`P5'), PPM (`P6') or (extremely\n" +"unofficial and unsupported!) PAM (`P8') file. Currently it is required\n" +"to have maxval == 255 (i.e., no scaling). If pnmfile is specified, it\n" +"is converted to the corresponding PNG file with the same base name but a\n" +"``.png'' extension; files read from stdin are converted and sent to stdout.\n" +"The conversion is progressive (low memory usage) unless interlacing is\n" +"requested; in that case the whole image will be buffered in memory and\n" +"written in one call.\n" + "\n", PROGNAME, PROGNAME, default_gamma); + exit(1); + } + + + /* prepare the text buffers for libpng's use; note that even though + * PNG's png_text struct includes a length field, we don't have to fill + * it out */ + + if (text && +#ifndef DOS_OS2_W32 + (keybd = fdopen(fileno(stderr), "r")) != NULL && +#endif + (textbuf = (char *)malloc((5 + 9)*75)) != NULL) + { + int i, valid, result; + + fprintf(stderr, + "Enter text info (no more than 72 characters per line);\n"); + fprintf(stderr, "to skip a field, hit the key.\n"); + /* note: just leaves len == 1 */ + + do { + valid = TRUE; + p = textbuf + TEXT_TITLE_OFFSET; + fprintf(stderr, " Title: "); + fflush(stderr); + if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) { + if (p[len-1] == '\n') + p[--len] = '\0'; + wpng_info.title = p; + wpng_info.have_text |= TEXT_TITLE; + if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) { + fprintf(stderr, " " PROGNAME " warning: character code" + " %u is %sdiscouraged by the PNG\n specification " + "[first occurrence was at character position #%d]\n", + (unsigned)p[result], (p[result] == 27)? "strongly " : "", + result+1); + fflush(stderr); +#ifdef FORBID_LATIN1_CTRL + wpng_info.have_text &= ~TEXT_TITLE; + valid = FALSE; +#else + if (p[result] == 27) { /* escape character */ + wpng_info.have_text &= ~TEXT_TITLE; + valid = FALSE; + } +#endif + } + } + } while (!valid); + + do { + valid = TRUE; + p = textbuf + TEXT_AUTHOR_OFFSET; + fprintf(stderr, " Author: "); + fflush(stderr); + if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) { + if (p[len-1] == '\n') + p[--len] = '\0'; + wpng_info.author = p; + wpng_info.have_text |= TEXT_AUTHOR; + if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) { + fprintf(stderr, " " PROGNAME " warning: character code" + " %u is %sdiscouraged by the PNG\n specification " + "[first occurrence was at character position #%d]\n", + (unsigned)p[result], (p[result] == 27)? "strongly " : "", + result+1); + fflush(stderr); +#ifdef FORBID_LATIN1_CTRL + wpng_info.have_text &= ~TEXT_AUTHOR; + valid = FALSE; +#else + if (p[result] == 27) { /* escape character */ + wpng_info.have_text &= ~TEXT_AUTHOR; + valid = FALSE; + } +#endif + } + } + } while (!valid); + + do { + valid = TRUE; + p = textbuf + TEXT_DESC_OFFSET; + fprintf(stderr, " Description (up to 9 lines):\n"); + for (i = 1; i < 10; ++i) { + fprintf(stderr, " [%d] ", i); + fflush(stderr); + if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) + p += len; /* now points at NULL; char before is newline */ + else + break; + } + if ((len = p - (textbuf + TEXT_DESC_OFFSET)) > 1) { + if (p[-1] == '\n') { + p[-1] = '\0'; + --len; + } + wpng_info.desc = textbuf + TEXT_DESC_OFFSET; + wpng_info.have_text |= TEXT_DESC; + p = textbuf + TEXT_DESC_OFFSET; + if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) { + fprintf(stderr, " " PROGNAME " warning: character code" + " %u is %sdiscouraged by the PNG\n specification " + "[first occurrence was at character position #%d]\n", + (unsigned)p[result], (p[result] == 27)? "strongly " : "", + result+1); + fflush(stderr); +#ifdef FORBID_LATIN1_CTRL + wpng_info.have_text &= ~TEXT_DESC; + valid = FALSE; +#else + if (p[result] == 27) { /* escape character */ + wpng_info.have_text &= ~TEXT_DESC; + valid = FALSE; + } +#endif + } + } + } while (!valid); + + do { + valid = TRUE; + p = textbuf + TEXT_COPY_OFFSET; + fprintf(stderr, " Copyright: "); + fflush(stderr); + if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) { + if (p[len-1] == '\n') + p[--len] = '\0'; + wpng_info.copyright = p; + wpng_info.have_text |= TEXT_COPY; + if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) { + fprintf(stderr, " " PROGNAME " warning: character code" + " %u is %sdiscouraged by the PNG\n specification " + "[first occurrence was at character position #%d]\n", + (unsigned)p[result], (p[result] == 27)? "strongly " : "", + result+1); + fflush(stderr); +#ifdef FORBID_LATIN1_CTRL + wpng_info.have_text &= ~TEXT_COPY; + valid = FALSE; +#else + if (p[result] == 27) { /* escape character */ + wpng_info.have_text &= ~TEXT_COPY; + valid = FALSE; + } +#endif + } + } + } while (!valid); + + do { + valid = TRUE; + p = textbuf + TEXT_EMAIL_OFFSET; + fprintf(stderr, " E-mail: "); + fflush(stderr); + if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) { + if (p[len-1] == '\n') + p[--len] = '\0'; + wpng_info.email = p; + wpng_info.have_text |= TEXT_EMAIL; + if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) { + fprintf(stderr, " " PROGNAME " warning: character code" + " %u is %sdiscouraged by the PNG\n specification " + "[first occurrence was at character position #%d]\n", + (unsigned)p[result], (p[result] == 27)? "strongly " : "", + result+1); + fflush(stderr); +#ifdef FORBID_LATIN1_CTRL + wpng_info.have_text &= ~TEXT_EMAIL; + valid = FALSE; +#else + if (p[result] == 27) { /* escape character */ + wpng_info.have_text &= ~TEXT_EMAIL; + valid = FALSE; + } +#endif + } + } + } while (!valid); + + do { + valid = TRUE; + p = textbuf + TEXT_URL_OFFSET; + fprintf(stderr, " URL: "); + fflush(stderr); + if (FGETS(p, 74, keybd) && (len = strlen(p)) > 1) { + if (p[len-1] == '\n') + p[--len] = '\0'; + wpng_info.url = p; + wpng_info.have_text |= TEXT_URL; + if ((result = wpng_isvalid_latin1((uch *)p, len)) >= 0) { + fprintf(stderr, " " PROGNAME " warning: character code" + " %u is %sdiscouraged by the PNG\n specification " + "[first occurrence was at character position #%d]\n", + (unsigned)p[result], (p[result] == 27)? "strongly " : "", + result+1); + fflush(stderr); +#ifdef FORBID_LATIN1_CTRL + wpng_info.have_text &= ~TEXT_URL; + valid = FALSE; +#else + if (p[result] == 27) { /* escape character */ + wpng_info.have_text &= ~TEXT_URL; + valid = FALSE; + } +#endif + } + } + } while (!valid); + +#ifndef DOS_OS2_W32 + fclose(keybd); +#endif + + } else if (text) { + fprintf(stderr, PROGNAME ": unable to allocate memory for text\n"); + text = FALSE; + wpng_info.have_text = 0; + } + + + /* allocate libpng stuff, initialize transformations, write pre-IDAT data */ + + if ((rc = writepng_init(&wpng_info)) != 0) { + switch (rc) { + case 2: + fprintf(stderr, PROGNAME + ": libpng initialization problem (longjmp)\n"); + break; + case 4: + fprintf(stderr, PROGNAME ": insufficient memory\n"); + break; + case 11: + fprintf(stderr, PROGNAME + ": internal logic error (unexpected PNM type)\n"); + break; + default: + fprintf(stderr, PROGNAME + ": unknown writepng_init() error\n"); + break; + } + exit(rc); + } + + + /* free textbuf, since it's a completely local variable and all text info + * has just been written to the PNG file */ + + if (text && textbuf) { + free(textbuf); + textbuf = NULL; + } + + + /* calculate rowbytes on basis of image type; note that this becomes much + * more complicated if we choose to support PBM type, ASCII PNM types, or + * 16-bit-per-sample binary data [currently not an official NetPBM type] */ + + if (wpng_info.pnmtype == 5) + rowbytes = wpng_info.width; + else if (wpng_info.pnmtype == 6) + rowbytes = wpng_info.width * 3; + else /* if (wpng_info.pnmtype == 8) */ + rowbytes = wpng_info.width * 4; + + + /* read and write the image, either in its entirety (if writing interlaced + * PNG) or row by row (if non-interlaced) */ + + fprintf(stderr, "Encoding image data...\n"); + fflush(stderr); + + if (wpng_info.interlaced) { + long i; + ulg bytes; + ulg image_bytes = rowbytes * wpng_info.height; /* overflow? */ + + wpng_info.image_data = (uch *)malloc(image_bytes); + wpng_info.row_pointers = (uch **)malloc(wpng_info.height*sizeof(uch *)); + if (wpng_info.image_data == NULL || wpng_info.row_pointers == NULL) { + fprintf(stderr, PROGNAME ": insufficient memory for image data\n"); + writepng_cleanup(&wpng_info); + wpng_cleanup(); + exit(5); + } + for (i = 0; i < wpng_info.height; ++i) + wpng_info.row_pointers[i] = wpng_info.image_data + i*rowbytes; + bytes = fread(wpng_info.image_data, 1, image_bytes, wpng_info.infile); + if (bytes != image_bytes) { + fprintf(stderr, PROGNAME ": expected %lu bytes, got %lu bytes\n", + image_bytes, bytes); + fprintf(stderr, " (continuing anyway)\n"); + } + if (writepng_encode_image(&wpng_info) != 0) { + fprintf(stderr, PROGNAME + ": libpng problem (longjmp) while writing image data\n"); + writepng_cleanup(&wpng_info); + wpng_cleanup(); + exit(2); + } + + } else /* not interlaced: write progressively (row by row) */ { + long j; + ulg bytes; + + wpng_info.image_data = (uch *)malloc(rowbytes); + if (wpng_info.image_data == NULL) { + fprintf(stderr, PROGNAME ": insufficient memory for row data\n"); + writepng_cleanup(&wpng_info); + wpng_cleanup(); + exit(5); + } + error = 0; + for (j = wpng_info.height; j > 0L; --j) { + bytes = fread(wpng_info.image_data, 1, rowbytes, wpng_info.infile); + if (bytes != rowbytes) { + fprintf(stderr, PROGNAME + ": expected %lu bytes, got %lu bytes (row %ld)\n", rowbytes, + bytes, wpng_info.height-j); + ++error; + break; + } + if (writepng_encode_row(&wpng_info) != 0) { + fprintf(stderr, PROGNAME + ": libpng problem (longjmp) while writing row %ld\n", + wpng_info.height-j); + ++error; + break; + } + } + if (error) { + writepng_cleanup(&wpng_info); + wpng_cleanup(); + exit(2); + } + if (writepng_encode_finish(&wpng_info) != 0) { + fprintf(stderr, PROGNAME ": error on final libpng call\n"); + writepng_cleanup(&wpng_info); + wpng_cleanup(); + exit(2); + } + } + + + /* OK, we're done (successfully): clean up all resources and quit */ + + fprintf(stderr, "Done.\n"); + fflush(stderr); + + writepng_cleanup(&wpng_info); + wpng_cleanup(); + + return 0; +} + + + + + +static int wpng_isvalid_latin1(uch *p, int len) +{ + int i, result = -1; + + for (i = 0; i < len; ++i) { + if (p[i] == 10 || (p[i] > 31 && p[i] < 127) || p[i] > 160) + continue; /* character is completely OK */ + if (result < 0 || (p[result] != 27 && p[i] == 27)) + result = i; /* mark location of first questionable one */ + } /* or of first escape character (bad) */ + + return result; +} + + + + + +static void wpng_cleanup(void) +{ + if (wpng_info.outfile) { + fclose(wpng_info.outfile); + wpng_info.outfile = NULL; + } + + if (wpng_info.infile) { + fclose(wpng_info.infile); + wpng_info.infile = NULL; + } + + if (wpng_info.image_data) { + free(wpng_info.image_data); + wpng_info.image_data = NULL; + } + + if (wpng_info.row_pointers) { + free(wpng_info.row_pointers); + wpng_info.row_pointers = NULL; + } +} + + + + +#ifdef DOS_OS2_W32 + +static char *dos_kbd_gets(char *buf, int len) +{ + int ch, count=0; + + do { + buf[count++] = ch = getche(); + } while (ch != '\r' && count < len-1); + + buf[count--] = '\0'; /* terminate string */ + if (buf[count] == '\r') /* Enter key makes CR, so change to newline */ + buf[count] = '\n'; + + fprintf(stderr, "\n"); /* Enter key does *not* cause a newline */ + fflush(stderr); + + return buf; +} + +#endif /* DOS_OS2_W32 */ diff --git a/Engine/lib/lpng/contrib/gregbook/writepng.c b/Engine/lib/lpng/contrib/gregbook/writepng.c new file mode 100644 index 000000000..e6d81ea40 --- /dev/null +++ b/Engine/lib/lpng/contrib/gregbook/writepng.c @@ -0,0 +1,392 @@ +/*--------------------------------------------------------------------------- + + wpng - simple PNG-writing program writepng.c + + --------------------------------------------------------------------------- + + Copyright (c) 1998-2007 Greg Roelofs. All rights reserved. + + This software is provided "as is," without warranty of any kind, + express or implied. In no event shall the author or contributors + be held liable for any damages arising in any way from the use of + this software. + + The contents of this file are DUAL-LICENSED. You may modify and/or + redistribute this software according to the terms of one of the + following two licenses (at your option): + + + LICENSE 1 ("BSD-like with advertising clause"): + + 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. Redistributions of source code must retain the above copyright + notice, disclaimer, and this list of conditions. + 2. Redistributions in binary form must reproduce the above copyright + notice, disclaimer, and this list of conditions in the documenta- + tion and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + + This product includes software developed by Greg Roelofs + and contributors for the book, "PNG: The Definitive Guide," + published by O'Reilly and Associates. + + + LICENSE 2 (GNU GPL v2 or later): + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ---------------------------------------------------------------------------*/ + + +#include /* for exit() prototype */ + +#include "png.h" /* libpng header; includes zlib.h and setjmp.h */ +#include "writepng.h" /* typedefs, common macros, public prototypes */ + + +/* local prototype */ + +static void writepng_error_handler(png_structp png_ptr, png_const_charp msg); + + + +void writepng_version_info(void) +{ + fprintf(stderr, " Compiled with libpng %s; using libpng %s.\n", + PNG_LIBPNG_VER_STRING, png_libpng_ver); + fprintf(stderr, " Compiled with zlib %s; using zlib %s.\n", + ZLIB_VERSION, zlib_version); +} + + + + +/* returns 0 for success, 2 for libpng problem, 4 for out of memory, 11 for + * unexpected pnmtype; note that outfile might be stdout */ + +int writepng_init(mainprog_info *mainprog_ptr) +{ + png_structp png_ptr; /* note: temporary variables! */ + png_infop info_ptr; + int color_type, interlace_type; + + + /* could also replace libpng warning-handler (final NULL), but no need: */ + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, mainprog_ptr, + writepng_error_handler, NULL); + if (!png_ptr) + return 4; /* out of memory */ + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_write_struct(&png_ptr, NULL); + return 4; /* out of memory */ + } + + + /* setjmp() must be called in every function that calls a PNG-writing + * libpng function, unless an alternate error handler was installed-- + * but compatible error handlers must either use longjmp() themselves + * (as in this program) or exit immediately, so here we go: */ + + if (setjmp(mainprog_ptr->jmpbuf)) { + png_destroy_write_struct(&png_ptr, &info_ptr); + return 2; + } + + + /* make sure outfile is (re)opened in BINARY mode */ + + png_init_io(png_ptr, mainprog_ptr->outfile); + + + /* set the compression levels--in general, always want to leave filtering + * turned on (except for palette images) and allow all of the filters, + * which is the default; want 32K zlib window, unless entire image buffer + * is 16K or smaller (unknown here)--also the default; usually want max + * compression (NOT the default); and remaining compression flags should + * be left alone */ + + png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); +/* + >> this is default for no filtering; Z_FILTERED is default otherwise: + png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY); + >> these are all defaults: + png_set_compression_mem_level(png_ptr, 8); + png_set_compression_window_bits(png_ptr, 15); + png_set_compression_method(png_ptr, 8); + */ + + + /* set the image parameters appropriately */ + + if (mainprog_ptr->pnmtype == 5) + color_type = PNG_COLOR_TYPE_GRAY; + else if (mainprog_ptr->pnmtype == 6) + color_type = PNG_COLOR_TYPE_RGB; + else if (mainprog_ptr->pnmtype == 8) + color_type = PNG_COLOR_TYPE_RGB_ALPHA; + else { + png_destroy_write_struct(&png_ptr, &info_ptr); + return 11; + } + + interlace_type = mainprog_ptr->interlaced? PNG_INTERLACE_ADAM7 : + PNG_INTERLACE_NONE; + + png_set_IHDR(png_ptr, info_ptr, mainprog_ptr->width, mainprog_ptr->height, + mainprog_ptr->sample_depth, color_type, interlace_type, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + if (mainprog_ptr->gamma > 0.0) + png_set_gAMA(png_ptr, info_ptr, mainprog_ptr->gamma); + + if (mainprog_ptr->have_bg) { /* we know it's RGBA, not gray+alpha */ + png_color_16 background; + + background.red = mainprog_ptr->bg_red; + background.green = mainprog_ptr->bg_green; + background.blue = mainprog_ptr->bg_blue; + png_set_bKGD(png_ptr, info_ptr, &background); + } + + if (mainprog_ptr->have_time) { + png_time modtime; + + png_convert_from_time_t(&modtime, mainprog_ptr->modtime); + png_set_tIME(png_ptr, info_ptr, &modtime); + } + + if (mainprog_ptr->have_text) { + png_text text[6]; + int num_text = 0; + + if (mainprog_ptr->have_text & TEXT_TITLE) { + text[num_text].compression = PNG_TEXT_COMPRESSION_NONE; + text[num_text].key = "Title"; + text[num_text].text = mainprog_ptr->title; + ++num_text; + } + if (mainprog_ptr->have_text & TEXT_AUTHOR) { + text[num_text].compression = PNG_TEXT_COMPRESSION_NONE; + text[num_text].key = "Author"; + text[num_text].text = mainprog_ptr->author; + ++num_text; + } + if (mainprog_ptr->have_text & TEXT_DESC) { + text[num_text].compression = PNG_TEXT_COMPRESSION_NONE; + text[num_text].key = "Description"; + text[num_text].text = mainprog_ptr->desc; + ++num_text; + } + if (mainprog_ptr->have_text & TEXT_COPY) { + text[num_text].compression = PNG_TEXT_COMPRESSION_NONE; + text[num_text].key = "Copyright"; + text[num_text].text = mainprog_ptr->copyright; + ++num_text; + } + if (mainprog_ptr->have_text & TEXT_EMAIL) { + text[num_text].compression = PNG_TEXT_COMPRESSION_NONE; + text[num_text].key = "E-mail"; + text[num_text].text = mainprog_ptr->email; + ++num_text; + } + if (mainprog_ptr->have_text & TEXT_URL) { + text[num_text].compression = PNG_TEXT_COMPRESSION_NONE; + text[num_text].key = "URL"; + text[num_text].text = mainprog_ptr->url; + ++num_text; + } + png_set_text(png_ptr, info_ptr, text, num_text); + } + + + /* write all chunks up to (but not including) first IDAT */ + + png_write_info(png_ptr, info_ptr); + + + /* if we wanted to write any more text info *after* the image data, we + * would set up text struct(s) here and call png_set_text() again, with + * just the new data; png_set_tIME() could also go here, but it would + * have no effect since we already called it above (only one tIME chunk + * allowed) */ + + + /* set up the transformations: for now, just pack low-bit-depth pixels + * into bytes (one, two or four pixels per byte) */ + + png_set_packing(png_ptr); +/* png_set_shift(png_ptr, &sig_bit); to scale low-bit-depth values */ + + + /* make sure we save our pointers for use in writepng_encode_image() */ + + mainprog_ptr->png_ptr = png_ptr; + mainprog_ptr->info_ptr = info_ptr; + + + /* OK, that's all we need to do for now; return happy */ + + return 0; +} + + + + + +/* returns 0 for success, 2 for libpng (longjmp) problem */ + +int writepng_encode_image(mainprog_info *mainprog_ptr) +{ + png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr; + png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr; + + + /* as always, setjmp() must be called in every function that calls a + * PNG-writing libpng function */ + + if (setjmp(mainprog_ptr->jmpbuf)) { + png_destroy_write_struct(&png_ptr, &info_ptr); + mainprog_ptr->png_ptr = NULL; + mainprog_ptr->info_ptr = NULL; + return 2; + } + + + /* and now we just write the whole image; libpng takes care of interlacing + * for us */ + + png_write_image(png_ptr, mainprog_ptr->row_pointers); + + + /* since that's it, we also close out the end of the PNG file now--if we + * had any text or time info to write after the IDATs, second argument + * would be info_ptr, but we optimize slightly by sending NULL pointer: */ + + png_write_end(png_ptr, NULL); + + return 0; +} + + + + + +/* returns 0 if succeeds, 2 if libpng problem */ + +int writepng_encode_row(mainprog_info *mainprog_ptr) /* NON-interlaced only! */ +{ + png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr; + png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr; + + + /* as always, setjmp() must be called in every function that calls a + * PNG-writing libpng function */ + + if (setjmp(mainprog_ptr->jmpbuf)) { + png_destroy_write_struct(&png_ptr, &info_ptr); + mainprog_ptr->png_ptr = NULL; + mainprog_ptr->info_ptr = NULL; + return 2; + } + + + /* image_data points at our one row of image data */ + + png_write_row(png_ptr, mainprog_ptr->image_data); + + return 0; +} + + + + + +/* returns 0 if succeeds, 2 if libpng problem */ + +int writepng_encode_finish(mainprog_info *mainprog_ptr) /* NON-interlaced! */ +{ + png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr; + png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr; + + + /* as always, setjmp() must be called in every function that calls a + * PNG-writing libpng function */ + + if (setjmp(mainprog_ptr->jmpbuf)) { + png_destroy_write_struct(&png_ptr, &info_ptr); + mainprog_ptr->png_ptr = NULL; + mainprog_ptr->info_ptr = NULL; + return 2; + } + + + /* close out PNG file; if we had any text or time info to write after + * the IDATs, second argument would be info_ptr: */ + + png_write_end(png_ptr, NULL); + + return 0; +} + + + + + +void writepng_cleanup(mainprog_info *mainprog_ptr) +{ + png_structp png_ptr = (png_structp)mainprog_ptr->png_ptr; + png_infop info_ptr = (png_infop)mainprog_ptr->info_ptr; + + if (png_ptr && info_ptr) + png_destroy_write_struct(&png_ptr, &info_ptr); +} + + + + + +static void writepng_error_handler(png_structp png_ptr, png_const_charp msg) +{ + mainprog_info *mainprog_ptr; + + /* This function, aside from the extra step of retrieving the "error + * pointer" (below) and the fact that it exists within the application + * rather than within libpng, is essentially identical to libpng's + * default error handler. The second point is critical: since both + * setjmp() and longjmp() are called from the same code, they are + * guaranteed to have compatible notions of how big a jmp_buf is, + * regardless of whether _BSD_SOURCE or anything else has (or has not) + * been defined. */ + + fprintf(stderr, "writepng libpng error: %s\n", msg); + fflush(stderr); + + mainprog_ptr = png_get_error_ptr(png_ptr); + if (mainprog_ptr == NULL) { /* we are completely hosed now */ + fprintf(stderr, + "writepng severe error: jmpbuf not recoverable; terminating.\n"); + fflush(stderr); + exit(99); + } + + longjmp(mainprog_ptr->jmpbuf, 1); +} diff --git a/Engine/lib/lpng/contrib/gregbook/writepng.h b/Engine/lib/lpng/contrib/gregbook/writepng.h new file mode 100644 index 000000000..78b966b58 --- /dev/null +++ b/Engine/lib/lpng/contrib/gregbook/writepng.h @@ -0,0 +1,133 @@ +/*--------------------------------------------------------------------------- + + wpng - simple PNG-writing program writepng.h + + --------------------------------------------------------------------------- + + Copyright (c) 1998-2007 Greg Roelofs. All rights reserved. + + This software is provided "as is," without warranty of any kind, + express or implied. In no event shall the author or contributors + be held liable for any damages arising in any way from the use of + this software. + + The contents of this file are DUAL-LICENSED. You may modify and/or + redistribute this software according to the terms of one of the + following two licenses (at your option): + + + LICENSE 1 ("BSD-like with advertising clause"): + + 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. Redistributions of source code must retain the above copyright + notice, disclaimer, and this list of conditions. + 2. Redistributions in binary form must reproduce the above copyright + notice, disclaimer, and this list of conditions in the documenta- + tion and/or other materials provided with the distribution. + 3. All advertising materials mentioning features or use of this + software must display the following acknowledgment: + + This product includes software developed by Greg Roelofs + and contributors for the book, "PNG: The Definitive Guide," + published by O'Reilly and Associates. + + + LICENSE 2 (GNU GPL v2 or later): + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ---------------------------------------------------------------------------*/ + +#ifndef TRUE +# define TRUE 1 +# define FALSE 0 +#endif + +#ifndef MAX +# define MAX(a,b) ((a) > (b)? (a) : (b)) +# define MIN(a,b) ((a) < (b)? (a) : (b)) +#endif + +#ifdef DEBUG +# define Trace(x) {fprintf x ; fflush(stderr); fflush(stdout);} +#else +# define Trace(x) ; +#endif + +#define TEXT_TITLE 0x01 +#define TEXT_AUTHOR 0x02 +#define TEXT_DESC 0x04 +#define TEXT_COPY 0x08 +#define TEXT_EMAIL 0x10 +#define TEXT_URL 0x20 + +#define TEXT_TITLE_OFFSET 0 +#define TEXT_AUTHOR_OFFSET 72 +#define TEXT_COPY_OFFSET (2*72) +#define TEXT_EMAIL_OFFSET (3*72) +#define TEXT_URL_OFFSET (4*72) +#define TEXT_DESC_OFFSET (5*72) + +typedef unsigned char uch; +typedef unsigned short ush; +typedef unsigned long ulg; + +typedef struct _mainprog_info { + double gamma; + long width; + long height; + time_t modtime; + FILE *infile; + FILE *outfile; + void *png_ptr; + void *info_ptr; + uch *image_data; + uch **row_pointers; + char *title; + char *author; + char *desc; + char *copyright; + char *email; + char *url; + int filter; /* command-line-filter flag, not PNG row filter! */ + int pnmtype; + int sample_depth; + int interlaced; + int have_bg; + int have_time; + int have_text; + jmp_buf jmpbuf; + uch bg_red; + uch bg_green; + uch bg_blue; +} mainprog_info; + + +/* prototypes for public functions in writepng.c */ + +void writepng_version_info(void); + +int writepng_init(mainprog_info *mainprog_ptr); + +int writepng_encode_image(mainprog_info *mainprog_ptr); + +int writepng_encode_row(mainprog_info *mainprog_ptr); + +int writepng_encode_finish(mainprog_info *mainprog_ptr); + +void writepng_cleanup(mainprog_info *mainprog_ptr); diff --git a/Engine/lib/lpng/contrib/pngminim/decoder/README b/Engine/lib/lpng/contrib/pngminim/decoder/README new file mode 100644 index 000000000..4f71082e4 --- /dev/null +++ b/Engine/lib/lpng/contrib/pngminim/decoder/README @@ -0,0 +1,6 @@ +This demonstrates the use of PNG_USER_CONFIG and pngusr.h + +To build a minimal read-only decoder, run + gather.sh # to collect needed files from pngminus, libpng, and zlib + make -f makefile.std + diff --git a/Engine/lib/lpng/contrib/pngminim/decoder/gather.sh b/Engine/lib/lpng/contrib/pngminim/decoder/gather.sh new file mode 100644 index 000000000..e28ddaf03 --- /dev/null +++ b/Engine/lib/lpng/contrib/pngminim/decoder/gather.sh @@ -0,0 +1,8 @@ +cp ../../pngminus/png2pnm.c pngm2pnm.c +cp ../../../*.h . +cp ../../../*.c . +rm example.c pnggccrd.c pngvcrd.c pngtest.c pngpread.c +# change the following 2 lines if zlib is somewhere else +cp ../../../../zlib/*.h . +cp ../../../../zlib/*.c . +rm minigzip.c example.c compress.c deflate.c diff --git a/Engine/lib/lpng/contrib/pngminim/decoder/makefile.std b/Engine/lib/lpng/contrib/pngminim/decoder/makefile.std new file mode 100644 index 000000000..27e04cbde --- /dev/null +++ b/Engine/lib/lpng/contrib/pngminim/decoder/makefile.std @@ -0,0 +1,44 @@ +# Makefile for PngMinus (pngm2pnm) +# Linux / Unix + +#CC=cc +CC=gcc +LD=$(CC) + +RM=rm -f + +CFLAGS=-DPNG_USER_CONFIG -DNO_GZCOMPRESS -DNO_GZIP \ + -DdeflateParams\(a,b,c\)=Z_OK -I. -O1 + +C=.c +O=.o +L=.a +E= + +ZOBJS = adler32$(O) crc32$(O) gzio$(O) \ + infback$(O) inffast$(O) inflate$(O) inftrees$(O) \ + trees$(O) uncompr$(O) zutil$(O) + +OBJS = pngm2pnm$(O) png$(O) pngerror$(O) pngget$(O) pngmem$(O) \ + pngread$(O) pngrio$(O) pngrtran$(O) pngrutil$(O) \ + pngset$(O) pngtrans$(O) $(ZOBJS) + +# implicit make rules ------------------------------------------------------- + +.c$(O): png.h pngconf.h pngusr.h zlib.h + $(CC) -c $(CFLAGS) $< + +# dependencies + +all: pngm2pnm$(E) + +pngm2pnm$(E): $(OBJS) + $(LD) -o pngm2pnm$(E) $(OBJS) + strip pngm2pnm$(E) + +clean: + $(RM) pngm2pnm$(O) + $(RM) pngm2pnm$(E) + $(RM) $(OBJS) + +# End of makefile for pngm2pnm diff --git a/Engine/lib/lpng/contrib/pngminim/decoder/pngusr.h b/Engine/lib/lpng/contrib/pngminim/decoder/pngusr.h new file mode 100644 index 000000000..f94b83a66 --- /dev/null +++ b/Engine/lib/lpng/contrib/pngminim/decoder/pngusr.h @@ -0,0 +1,67 @@ +/* minrdpngconf.h: headers to make a minimal png-read-only library + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 2007 Glenn Randers-Pehrson + * Derived from pngcrush.h, Copyright 1998-2007, Glenn Randers-Pehrson + */ + +#ifndef MINRDPNGCONF_H +#define MINRDPNGCONF_H + +#define PNG_NO_GLOBAL_ARRAYS + +#define PNG_NO_WARNINGS +#define png_warning(s1,s2) "" +#define png_chunk_warning(s1,s2) "" +#define PNG_NO_ERROR_TEXT +#define png_error(s1,s2) png_err(s1) +#define png_chunk_error(s1,s2) png_err(s1) + +#define PNG_NO_ASSEMBLER_CODE +#define PNG_NO_OPTIMIZED_CODE +#define PNG_NO_READ_GAMMA +#define PNG_NO_READ_BACKGROUND +#define PNG_NO_READ_DITHER +#define PNG_NO_READ_INVERT +#define PNG_NO_READ_SHIFT +#define PNG_NO_READ_PACK +#define PNG_NO_READ_PACKSWAP +#define PNG_NO_READ_FILLER +#define PNG_NO_READ_SWAP_ALPHA +#define PNG_NO_READ_INVERT_ALPHA +#define PNG_NO_READ_RGB_TO_GRAY +#define PNG_NO_READ_USER_TRANSFORM +#define PNG_NO_READ_bKGD +#define PNG_NO_READ_cHRM +#define PNG_NO_READ_gAMA +#define PNG_NO_READ_hIST +#define PNG_NO_READ_iCCP +#define PNG_NO_READ_pCAL +#define PNG_NO_READ_pHYs +#define PNG_NO_READ_sBIT +#define PNG_NO_READ_sCAL +#define PNG_NO_READ_sPLT +#define PNG_NO_READ_TEXT +#define PNG_NO_READ_tIME +#define PNG_NO_READ_UNKNOWN_CHUNKS +#define PNG_NO_READ_USER_CHUNKS +#define PNG_NO_READ_EMPTY_PLTE +#define PNG_NO_READ_OPT_PLTE +#define PNG_NO_READ_STRIP_ALPHA +#define PNG_NO_READ_oFFs +#define PNG_NO_WARN_UNINITIALIZED_ROW + +#define PNG_NO_WRITE_SUPPORTED + +#define PNG_NO_INFO_IMAGE +#define PNG_NO_USER_MEM +#define PNG_NO_FIXED_POINT_SUPPORTED +#define PNG_NO_MNG_FEATURES +#define PNG_NO_USER_TRANSFORM_PTR +#define PNG_NO_HANDLE_AS_UNKNOWN +#define PNG_NO_CONSOLE_IO +#define PNG_NO_ZALLOC_ZERO +#define PNG_NO_ERROR_NUMBERS +#define PNG_NO_EASY_ACCESS +#define PNG_NO_PROGRESSIVE_READ + +#endif /* MINRDPNGCONF_H */ diff --git a/Engine/lib/lpng/contrib/pngminim/encoder/README b/Engine/lib/lpng/contrib/pngminim/encoder/README new file mode 100644 index 000000000..4a68fc895 --- /dev/null +++ b/Engine/lib/lpng/contrib/pngminim/encoder/README @@ -0,0 +1,6 @@ +This demonstrates the use of PNG_USER_CONFIG and pngusr.h + +To build a minimal write-only encoder, run + gather.sh # to collect needed files from pngminus, libpng, and zlib + make -f makefile.std + diff --git a/Engine/lib/lpng/contrib/pngminim/encoder/dummy_inflate.c b/Engine/lib/lpng/contrib/pngminim/encoder/dummy_inflate.c new file mode 100644 index 000000000..1422edb43 --- /dev/null +++ b/Engine/lib/lpng/contrib/pngminim/encoder/dummy_inflate.c @@ -0,0 +1,27 @@ +#include "zlib.h" + +int ZEXPORT inflate(strm, flush) +z_streamp strm; +int flush; +{ return Z_OK ; } + +int ZEXPORT inflateReset(strm) +z_streamp strm; +{ return Z_OK ; } + +int ZEXPORT inflateEnd(strm) +z_streamp strm; +{ return Z_STREAM_ERROR ; } + +int ZEXPORT inflateInit_(strm, version, stream_size) +z_streamp strm; +const char *version; +int stream_size; +{ return Z_OK ; } + +int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) +z_streamp strm; +int windowBits; +const char *version; +int stream_size; +{ return Z_STREAM_ERROR ; } diff --git a/Engine/lib/lpng/contrib/pngminim/encoder/gather.sh b/Engine/lib/lpng/contrib/pngminim/encoder/gather.sh new file mode 100644 index 000000000..7b0c80365 --- /dev/null +++ b/Engine/lib/lpng/contrib/pngminim/encoder/gather.sh @@ -0,0 +1,9 @@ +cp ../../pngminus/pnm2png.c pnm2pngm.c +cp ../../../*.h . +cp ../../../*.c . +rm example.c pnggccrd.c pngvcrd.c pngtest.c pngr*.c +# Change the next 2 lines if zlib is somewhere else. +cp ../../../../zlib/*.h . +cp ../../../../zlib/*.c . +rm inf*.[ch] +rm minigzip.c example.c diff --git a/Engine/lib/lpng/contrib/pngminim/encoder/makefile.std b/Engine/lib/lpng/contrib/pngminim/encoder/makefile.std new file mode 100644 index 000000000..1182b5b23 --- /dev/null +++ b/Engine/lib/lpng/contrib/pngminim/encoder/makefile.std @@ -0,0 +1,43 @@ +# Makefile for PngMinus (pnm2pngm) +# Linux / Unix + +#CC=cc +CC=gcc +LD=$(CC) + +RM=rm -f + +CFLAGS=-DPNG_USER_CONFIG -DNO_GZIP -I. -O1 + +C=.c +O=.o +L=.a +E= + +ZOBJS = adler32$(O) compress$(O) crc32$(O) deflate$(O) gzio$(O) \ + dummy_inflate$(O) \ + trees$(O) uncompr$(O) zutil$(O) + +OBJS = pnm2pngm$(O) png$(O) pngerror$(O) pngget$(O) pngmem$(O) \ + pngset$(O) pngtrans$(O) pngwio$(O) pngwrite$(O) \ + pngwtran$(O) pngwutil$(O) $(ZOBJS) + +# implicit make rules ------------------------------------------------------- + +.c$(O): png.h pngconf.h pngusr.h zlib.h + $(CC) -c $(CFLAGS) $< + +# dependencies + +all: pnm2pngm$(E) + +pnm2pngm$(E): $(OBJS) + $(LD) -o pnm2pngm$(E) $(OBJS) + strip pnm2pngm$(E) + +clean: + $(RM) pnm2pngm$(O) + $(RM) pnm2pngm$(E) + $(RM) $(OBJS) + +# End of makefile for pnm2pngm diff --git a/Engine/lib/lpng/contrib/pngminim/encoder/pngusr.h b/Engine/lib/lpng/contrib/pngminim/encoder/pngusr.h new file mode 100644 index 000000000..904673abb --- /dev/null +++ b/Engine/lib/lpng/contrib/pngminim/encoder/pngusr.h @@ -0,0 +1,66 @@ +/* minwrpngconf.h: headers to make a minimal png-write-only library + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 2007 Glenn Randers-Pehrson + * Derived from pngcrush.h, Copyright 1998-2007, Glenn Randers-Pehrson + */ + +#ifndef MINWRPNGCONF_H +#define MINWRPNGCONF_H + +#define PNG_NO_GLOBAL_ARRAYS + +#define PNG_NO_READ_SUPPORTED + +#define PNG_NO_WARNINGS +#define png_warning(s1,s2) "" +#define png_chunk_warning(s1,s2) "" +#define PNG_NO_ERROR_TEXT +#define png_error(s1,s2) png_err(s1) +#define png_chunk_error(s1,s2) png_err(s1) + +#define PNG_NO_WRITE_BACKGROUND +#define PNG_NO_WRITE_GAMMA +#define PNG_NO_WRITE_DITHER +#define PNG_NO_WRITE_INVERT +#define PNG_NO_WRITE_SHIFT +#define PNG_NO_WRITE_PACK +#define PNG_NO_WRITE_PACKSWAP +#define PNG_NO_WRITE_FILLER +#define PNG_NO_WRITE_SWAP_ALPHA +#define PNG_NO_WRITE_INVERT_ALPHA +#define PNG_NO_WRITE_RGB_TO_GRAY +#define PNG_NO_WRITE_USER_TRANSFORM +#define PNG_NO_WRITE_bKGD +#define PNG_NO_WRITE_cHRM +#define PNG_NO_WRITE_gAMA +#define PNG_NO_WRITE_sRGB +#define PNG_NO_WRITE_hIST +#define PNG_NO_WRITE_iCCP +#define PNG_NO_WRITE_oFFs +#define PNG_NO_WRITE_pCAL +#define PNG_NO_WRITE_pHYs +#define PNG_NO_WRITE_sBIT +#define PNG_NO_WRITE_sCAL +#define PNG_NO_WRITE_sPLT +#define PNG_NO_WRITE_TEXT +#define PNG_NO_WRITE_tIME +#define PNG_NO_WRITE_UNKNOWN_CHUNKS +#define PNG_NO_WRITE_USER_CHUNKS +#define PNG_NO_WRITE_EMPTY_PLTE +#define PNG_NO_WRITE_OPT_PLTE +#define PNG_NO_WRITE_FILTER +#define PNG_NO_WRITE_WEIGHTED_FILTER +#define PNG_NO_WRITE_INTERLACING_SUPPORTED + +#define PNG_NO_INFO_IMAGE +#define PNG_NO_USER_MEM +#define PNG_NO_FIXED_POINT_SUPPORTED +#define PNG_NO_MNG_FEATURES +#define PNG_NO_USER_TRANSFORM_PTR +#define PNG_NO_HANDLE_AS_UNKNOWN +#define PNG_NO_CONSOLE_IO +#define PNG_NO_ZALLOC_ZERO +#define PNG_NO_ERROR_NUMBERS +#define PNG_NO_EASY_ACCESS + +#endif /* MINWRPNGCONF_H */ diff --git a/Engine/lib/lpng/contrib/pngminus/README b/Engine/lib/lpng/contrib/pngminus/README new file mode 100644 index 000000000..bbe7407ec --- /dev/null +++ b/Engine/lib/lpng/contrib/pngminus/README @@ -0,0 +1,153 @@ +PngMinus +-------- +(copyright Willem van Schaik, 1999) + + +License +------- + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and +that both that copyright notice and this permission notice appear in +supporting documentation. This software is provided "as is" without +express or implied warranty. + + +Some history +------------ +Soon after the creation of PNG in 1995, the need was felt for a set of +pnmtopng / pngtopnm utilities. Independantly Alexander Lehmann and I +(Willem van Schaik) started such a project. Luckily we discovered this +and merged the two together into pnmtopng.tar.gz, which is available +from a/o ftp://ftp.simplesystems.org/pub/libpng/png/. + +These two utilities have many, many options and make use of most of the +features of PNG, like gamma, alpha, sbit, text-chunks, etc. This makes +the utilities quite complex and by now not anymore very maintainable. +When we wrote these programs, libpng was still in an early stage. +Therefore, lots of the functionality that we put in our software can now +be done using transform-functions in libpng. + +Finally, to compile these programs, you need to have installed and +compiled three libraries: libpng, zlib and netpbm. Especially the latter +makes the whole setup a bit bulky. But that's unavoidable given the many +features of pnmtopng. + + +What now +-------- +At this moment libpng is in a very stable state and can do much of the +work done in pnmtopng. Also, pnmtopng needs to be upgraded to the new +interface of libpng. Hence, it is time for a rewrite from the ground up +of pnmtopng and pngtopnm. This will happen in the near future (stay +tuned). The new package will get a different name to distinguish it from +the old one: PngPlus. + +To experiment a bit with the new interface of libpng, I started off with +a small prototype that contains only the basic functionality. It doesn't +have any of the options to read or write special chunks and it will do +no gamma correction. But this makes it also a simple program that is +quite easy to understand and can serve well as a template for other +software developments. (By now there are of course a couple of programs, +like Greg Roelofs' rpng/wpng, that can be used just as good.) + + +Can and can not +--------------- +As this is the small brother of the future PngPlus, I called this fellow +PngMinus. Because I started this development in good-old Turbo-C, I +avoided the use the netpbm library, which requires DOS extenders. Again, +another reason to call it PngMinus (minus netpbm :-). So, part of the +program are some elementary routines to read / write pgm- and ppm-files. +It does not read b&w pbm-files. + +The downside of this approach is that you can not use them on images +that require blocks of memory bigger than 64k (the DOS version). For +larger images you will get an out-of-memory error. + +As said before, PngMinus doesn't correct for gamma. When reading +png-files you can do this just as well by piping the output of png2pnm +to pnmgamma, one of the standard PbmPlus tools. This same scenario will +most probably also be followed in the full-blown future PngPlus, with +the addition of course of the possibility to create gamma-chunks when +writing png-files. + +On the other hand it supports alpha-channels. When reading a png-image +you can write the alpha-channel into a pgm-file. And when creating an +RGB+A png-image, you just combine a ppm-file with a corresponding +pgm-file containing the alpha-channel. When reading, transparency chunks +are converted into an alpha-channel and from there on treated the same +way. + +Finally you can opt for writing ascii or binary pgm- and ppm-files. When +the bit-depth is 16, the format will always be ascii. + + +Using it +-------- +To distinguish them from pnmtopng and PngPlus, the utilities are named +png2pnm and pnm2png (2 instead of to). The input- and output-files can +be given as parameters or through redirection. Therefore the programs +can be part of a pipe. + +To list the options type "png2pnm -h" or "pnm2png -h". + + +Just like Scandinavian furniture +-------------------------------- +You have to put it together yourself. I did test the software under +MS-DOS with Turbo-C 3.0 and under RedHat Linux 4.2 with gcc. In both +cases I used libpng-1.0.4 and zlib-1.1.3. Later versions should be OK, +however some older libpng versions have a bug in pngmem.c when using +Turbo-C 3.0 (see below). + +You can build it using one of the two makefiles (make -f makefile.###) +or use the batch/script files pngminus.bat / pngminus.sh. This assumes +that you have built the libraries in ../libpng and ../zlib. Using Linux, +make sure that you have built libpng with makefile.std and not +makefile.linux (also called .lnx in earlier versions of libpng). The +latter creates a .so shared-library, while the PngMinus makefile assumes +a normal .a static library. + +If you create a ../pngsuite directory and then store the basn####.png +files from PngSuite (http://www.schaik.com/pngsuite/) in there, you can +test in one go the proper functioning of PngMinus, see png2pnm.bat and +pnm2png.bat (or the .sh versions). + + +Warranty +------- +Please, remember that this was just a small experiment to learn a few +things. It will have many unforeseen features . Who said bugs? Use +it when you are in need for something simple or when you want to start +developing your own stuff. + + +The Turbo bug +------------- +** pngmem.old + hptr = (png_byte huge *)((long)(hptr) & 0xfffffff0L); + hptr += 16L; +** pngmem.c + hptr = (png_byte huge *)((long)(hptr) & 0xfffffff0L); + hptr = hptr + 16L; +** + +** pngmem.old + png_ptr->offset_table_ptr[i] = (png_bytep)hptr; + hptr += (png_uint_32)65536L; +** pngmem.c + png_ptr->offset_table_ptr[i] = (png_bytep)hptr; + hptr = hptr + 65536L; +** + + +The end +------- +Willem van Schaik +mailto:willem@schaik.com +http://www.schaik.com/png/ +------- +Oct 1999 + diff --git a/Engine/lib/lpng/contrib/pngminus/makefile.std b/Engine/lib/lpng/contrib/pngminus/makefile.std new file mode 100644 index 000000000..2fb061bbe --- /dev/null +++ b/Engine/lib/lpng/contrib/pngminus/makefile.std @@ -0,0 +1,65 @@ +# Makefile for PngMinus (png2pnm and pnm2png) +# Linux / Unix + +#CC=cc +CC=gcc +LD=$(CC) + +RM=rm -f + +#PNGPATH = /usr/local +#PNGINC = -I$(PNGPATH)/include/libpng12 +#PNGLIB = -L$(PNGPATH)/lib -lpng12 +#PNGLIBS = $(PNGPATH)/lib/libpng12.a +PNGINC = -I../.. +PNGLIB = -L../.. -lpng +PNGLIBS = ../../libpng.a + +#ZPATH = /usr/local +#ZINC = -I$(ZPATH)/include +#ZLIB = -L$(ZPATH)/lib -lz +#ZLIBS = $(ZPATH)/lib/libz.a +ZINC = -I../../../zlib +ZLIB = -L../../../zlib -lz +ZLIBS = ../../../zlib/libz.a + +CFLAGS=-O3 $(PNGINC) $(ZINC) +LDFLAGS=$(PNGLIB) $(ZLIB) +LDFLAGSS=$(PNGLIBS) $(ZLIBS) +C=.c +O=.o +L=.a +E= + +# dependencies + +#all: png2pnm$(E) pnm2png$(E) +all: png2pnm$(E) pnm2png$(E) png2pnm-static$(E) pnm2png-static$(E) + +png2pnm$(O): png2pnm$(C) + $(CC) -c $(CFLAGS) png2pnm$(C) + +png2pnm$(E): png2pnm$(O) + $(LD) -o png2pnm$(E) png2pnm$(O) $(LDFLAGS) -lm + +png2pnm-static$(E): png2pnm$(O) + $(LD) -o png2pnm-static$(E) png2pnm$(O) $(LDFLAGSS) -lm + +pnm2png$(O): pnm2png$(C) + $(CC) -c $(CFLAGS) pnm2png$(C) + +pnm2png$(E): pnm2png$(O) + $(LD) -o pnm2png$(E) pnm2png$(O) $(LDFLAGS) -lm + +pnm2png-static$(E): pnm2png$(O) + $(LD) -o pnm2png-static$(E) pnm2png$(O) $(LDFLAGSS) -lm + +clean: + $(RM) png2pnm$(O) + $(RM) pnm2png$(O) + $(RM) png2pnm$(E) + $(RM) pnm2png$(E) + $(RM) png2pnm-static$(E) + $(RM) pnm2png-static$(E) + +# End of makefile for png2pnm / pnm2png diff --git a/Engine/lib/lpng/contrib/pngminus/makefile.tc3 b/Engine/lib/lpng/contrib/pngminus/makefile.tc3 new file mode 100644 index 000000000..404f18d5b --- /dev/null +++ b/Engine/lib/lpng/contrib/pngminus/makefile.tc3 @@ -0,0 +1,38 @@ +# Makefile for PngMinus (png2pnm and pnm2png) +# TurboC++ 3.0 + +CC=tcc -Ic:\tc3\inc +LD=tcc -Lc:\tc3\lib +LB=tlib +RM=del +CP=copy +MODEL=l +CCFLAGS=-O -m$(MODEL) -I..\libpng -I..\zlib +LDFLAGS=-m$(MODEL) -L..\libpng -L..\zlib +C=.c +O=.obj +L=.lib +E=.exe + +# dependencies + +all: png2pnm$(E) pnm2png$(E) + +png2pnm$(O): png2pnm$(C) + $(CC) -c $(CCFLAGS) png2pnm$(C) + +png2pnm$(E): png2pnm$(O) + $(LD) $(LDFLAGS) png2pnm$(O) libpng$(L) zlib$(L) + +pnm2png$(O): pnm2png$(C) + $(CC) -c $(CCFLAGS) pnm2png$(C) + +pnm2png$(E): pnm2png$(O) + $(LD) $(LDFLAGS) pnm2png$(O) libpng$(L) zlib$(L) + +clean: + $(RM) *$(O) + $(RM) *$(E) + +# End of makefile for png2pnm / pnm2png + diff --git a/Engine/lib/lpng/contrib/pngminus/makevms.com b/Engine/lib/lpng/contrib/pngminus/makevms.com new file mode 100644 index 000000000..00561bcd0 --- /dev/null +++ b/Engine/lib/lpng/contrib/pngminus/makevms.com @@ -0,0 +1,92 @@ +$!------------------------------------------------------------------------------ +$! make Contrib programs of libpng under OpenVMS +$! +$! +$! Look for the compiler used +$! +$ zlibsrc = "[---.zlib]" +$ ccopt="/include=(''zlibsrc',[--])" +$ if f$getsyi("HW_MODEL").ge.1024 +$ then +$ ccopt = "/prefix=all"+ccopt +$ comp = "__decc__=1" +$ if f$trnlnm("SYS").eqs."" then define sys sys$library: +$ else +$ if f$search("SYS$SYSTEM:DECC$COMPILER.EXE").eqs."" +$ then +$ if f$trnlnm("SYS").eqs."" then define sys sys$library: +$ if f$search("SYS$SYSTEM:VAXC.EXE").eqs."" +$ then +$ comp = "__gcc__=1" +$ CC :== GCC +$ else +$ comp = "__vaxc__=1" +$ endif +$ else +$ if f$trnlnm("SYS").eqs."" then define sys decc$library_include: +$ ccopt = "/decc/prefix=all"+ccopt +$ comp = "__decc__=1" +$ endif +$ endif +$ open/write lopt lib.opt +$ write lopt "[--]libpng.olb/lib" +$ write lopt "''zlibsrc'libz.olb/lib" +$ close lopt +$ open/write xopt x11.opt +$ write xopt "sys$library:decw$xlibshr.exe/share" +$ close xopt +$ write sys$output "Compiling PNG contrib programs ..." +$ write sys$output "Building pnm2png..." +$ CALL MAKE pnm2png.OBJ "cc ''CCOPT' pnm2png" - + pnm2png.c +$ call make pnm2png.exe - + "LINK pnm2png,lib.opt/opt" - + pnm2png.obj +$ write sys$output "Building png2pnm..." +$ CALL MAKE png2pnm.OBJ "cc ''CCOPT' png2pnm" - + png2pnm.c +$ call make png2pnm.exe - + "LINK png2pnm,lib.opt/opt" - + png2pnm.obj +$ exit +$! +$! +$MAKE: SUBROUTINE !SUBROUTINE TO CHECK DEPENDENCIES +$ V = 'F$Verify(0) +$! P1 = What we are trying to make +$! P2 = Command to make it +$! P3 - P8 What it depends on +$ +$ If F$Search(P1) .Eqs. "" Then Goto Makeit +$ Time = F$CvTime(F$File(P1,"RDT")) +$arg=3 +$Loop: +$ Argument = P'arg +$ If Argument .Eqs. "" Then Goto Exit +$ El=0 +$Loop2: +$ File = F$Element(El," ",Argument) +$ If File .Eqs. " " Then Goto Endl +$ AFile = "" +$Loop3: +$ OFile = AFile +$ AFile = F$Search(File) +$ If AFile .Eqs. "" .Or. AFile .Eqs. OFile Then Goto NextEl +$ If F$CvTime(F$File(AFile,"RDT")) .Ges. Time Then Goto Makeit +$ Goto Loop3 +$NextEL: +$ El = El + 1 +$ Goto Loop2 +$EndL: +$ arg=arg+1 +$ If arg .Le. 8 Then Goto Loop +$ Goto Exit +$ +$Makeit: +$ VV=F$VERIFY(0) +$ write sys$output P2 +$ 'P2 +$ VV='F$Verify(VV) +$Exit: +$ If V Then Set Verify +$ENDSUBROUTINE diff --git a/Engine/lib/lpng/contrib/pngminus/png2pnm.bat b/Engine/lib/lpng/contrib/pngminus/png2pnm.bat new file mode 100644 index 000000000..449cf3675 --- /dev/null +++ b/Engine/lib/lpng/contrib/pngminus/png2pnm.bat @@ -0,0 +1,41 @@ +REM -- grayscale +png2pnm.exe -noraw ..\pngsuite\basn0g01.png basn0g01.pgm +png2pnm.exe -noraw ..\pngsuite\basn0g02.png basn0g02.pgm +png2pnm.exe -noraw ..\pngsuite\basn0g04.png basn0g04.pgm +png2pnm.exe -noraw ..\pngsuite\basn0g08.png basn0g08.pgm +png2pnm.exe -noraw ..\pngsuite\basn0g16.png basn0g16.pgm +REM -- full-color +png2pnm.exe -noraw ..\pngsuite\basn2c08.png basn2c08.ppm +png2pnm.exe -noraw ..\pngsuite\basn2c16.png basn2c16.ppm +REM -- palletted +png2pnm.exe -noraw ..\pngsuite\basn3p01.png basn3p01.ppm +png2pnm.exe -noraw ..\pngsuite\basn3p02.png basn3p02.ppm +png2pnm.exe -noraw ..\pngsuite\basn3p04.png basn3p04.ppm +png2pnm.exe -noraw ..\pngsuite\basn3p08.png basn3p08.ppm +REM -- gray with alpha-channel +png2pnm.exe -noraw ..\pngsuite\basn4a08.png basn4a08.pgm +png2pnm.exe -noraw ..\pngsuite\basn4a16.png basn4a16.pgm +REM -- color with alpha-channel +png2pnm.exe -noraw -alpha basn6a08.pgm ..\pngsuite\basn6a08.png basn6a08.ppm +png2pnm.exe -noraw -alpha basn6a16.pgm ..\pngsuite\basn6a16.png basn6a16.ppm +REM -- grayscale +png2pnm.exe -raw ..\pngsuite\basn0g01.png rawn0g01.pgm +png2pnm.exe -raw ..\pngsuite\basn0g02.png rawn0g02.pgm +png2pnm.exe -raw ..\pngsuite\basn0g04.png rawn0g04.pgm +png2pnm.exe -raw ..\pngsuite\basn0g08.png rawn0g08.pgm +png2pnm.exe -raw ..\pngsuite\basn0g16.png rawn0g16.pgm +REM -- full-color +png2pnm.exe -raw ..\pngsuite\basn2c08.png rawn2c08.ppm +png2pnm.exe -raw ..\pngsuite\basn2c16.png rawn2c16.ppm +REM -- palletted +png2pnm.exe -raw ..\pngsuite\basn3p01.png rawn3p01.ppm +png2pnm.exe -raw ..\pngsuite\basn3p02.png rawn3p02.ppm +png2pnm.exe -raw ..\pngsuite\basn3p04.png rawn3p04.ppm +png2pnm.exe -raw ..\pngsuite\basn3p08.png rawn3p08.ppm +REM -- gray with alpha-channel +png2pnm.exe -raw ..\pngsuite\basn4a08.png rawn4a08.pgm +png2pnm.exe -raw ..\pngsuite\basn4a16.png rawn4a16.pgm +REM -- color with alpha-channel +png2pnm.exe -noraw -alpha rawn6a08.pgm ..\pngsuite\basn6a08.png rawn6a08.ppm +png2pnm.exe -noraw -alpha rawn6a16.pgm ..\pngsuite\basn6a16.png rawn6a16.ppm + diff --git a/Engine/lib/lpng/contrib/pngminus/png2pnm.c b/Engine/lib/lpng/contrib/pngminus/png2pnm.c new file mode 100644 index 000000000..010870a76 --- /dev/null +++ b/Engine/lib/lpng/contrib/pngminus/png2pnm.c @@ -0,0 +1,430 @@ +/* + * png2pnm.c --- conversion from PNG-file to PGM/PPM-file + * copyright (C) 1999 by Willem van Schaik + * + * version 1.0 - 1999.10.15 - First version. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation. This software is provided "as is" without + * express or implied warranty. + */ + +#include +#include +#ifdef __TURBOC__ +#include +#include +#endif + +#ifndef BOOL +#define BOOL unsigned char +#endif +#ifndef TRUE +#define TRUE (BOOL) 1 +#endif +#ifndef FALSE +#define FALSE (BOOL) 0 +#endif + +#ifdef __TURBOC__ +#define STDIN 0 +#define STDOUT 1 +#define STDERR 2 +#endif + +/* to make png2pnm verbose so we can find problems (needs to be before png.h) */ +#ifndef PNG_DEBUG +#define PNG_DEBUG 0 +#endif + +#include "png.h" + +/* Define png_jmpbuf() in case we are using a pre-1.0.6 version of libpng */ +#ifndef png_jmpbuf +# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) +#endif + +/* function prototypes */ + +int main (int argc, char *argv[]); +void usage (); +BOOL png2pnm (FILE *png_file, FILE *pnm_file, FILE *alpha_file, BOOL raw, BOOL alpha); + +/* + * main + */ + +int main(int argc, char *argv[]) +{ + FILE *fp_rd = stdin; + FILE *fp_wr = stdout; + FILE *fp_al = NULL; + BOOL raw = TRUE; + BOOL alpha = FALSE; + int argi; + + for (argi = 1; argi < argc; argi++) + { + if (argv[argi][0] == '-') + { + switch (argv[argi][1]) + { + case 'n': + raw = FALSE; + break; + case 'r': + raw = TRUE; + break; + case 'a': + alpha = TRUE; + argi++; + if ((fp_al = fopen (argv[argi], "wb")) == NULL) + { + fprintf (stderr, "PNM2PNG\n"); + fprintf (stderr, "Error: can not create alpha-channel file %s\n", argv[argi]); + exit (1); + } + break; + case 'h': + case '?': + usage(); + exit(0); + break; + default: + fprintf (stderr, "PNG2PNM\n"); + fprintf (stderr, "Error: unknown option %s\n", argv[argi]); + usage(); + exit(1); + break; + } /* end switch */ + } + else if (fp_rd == stdin) + { + if ((fp_rd = fopen (argv[argi], "rb")) == NULL) + { + fprintf (stderr, "PNG2PNM\n"); + fprintf (stderr, "Error: file %s does not exist\n", argv[argi]); + exit (1); + } + } + else if (fp_wr == stdout) + { + if ((fp_wr = fopen (argv[argi], "wb")) == NULL) + { + fprintf (stderr, "PNG2PNM\n"); + fprintf (stderr, "Error: can not create file %s\n", argv[argi]); + exit (1); + } + } + else + { + fprintf (stderr, "PNG2PNM\n"); + fprintf (stderr, "Error: too many parameters\n"); + usage(); + exit(1); + } + } /* end for */ + +#ifdef __TURBOC__ + /* set stdin/stdout if required to binary */ + if (fp_rd == stdin) + { + setmode (STDIN, O_BINARY); + } + if ((raw) && (fp_wr == stdout)) + { + setmode (STDOUT, O_BINARY); + } +#endif + + /* call the conversion program itself */ + if (png2pnm (fp_rd, fp_wr, fp_al, raw, alpha) == FALSE) + { + fprintf (stderr, "PNG2PNM\n"); + fprintf (stderr, "Error: unsuccessful convertion of PNG-image\n"); + exit(1); + } + + /* close input file */ + fclose (fp_rd); + /* close output file */ + fclose (fp_wr); + /* close alpha file */ + if (alpha) + fclose (fp_al); + + return 0; +} + +/* + * usage + */ + +void usage() +{ + fprintf (stderr, "PNG2PNM\n"); + fprintf (stderr, " by Willem van Schaik, 1999\n"); +#ifdef __TURBOC__ + fprintf (stderr, " for Turbo-C and Borland-C compilers\n"); +#else + fprintf (stderr, " for Linux (and Unix) compilers\n"); +#endif + fprintf (stderr, "Usage: png2pnm [options] .png [.pnm]\n"); + fprintf (stderr, " or: ... | png2pnm [options]\n"); + fprintf (stderr, "Options:\n"); + fprintf (stderr, " -r[aw] write pnm-file in binary format (P4/P5/P6) (default)\n"); + fprintf (stderr, " -n[oraw] write pnm-file in ascii format (P1/P2/P3)\n"); + fprintf (stderr, " -a[lpha] .pgm write PNG alpha channel as pgm-file\n"); + fprintf (stderr, " -h | -? print this help-information\n"); +} + +/* + * png2pnm + */ + +BOOL png2pnm (FILE *png_file, FILE *pnm_file, FILE *alpha_file, BOOL raw, BOOL alpha) +{ + png_struct *png_ptr = NULL; + png_info *info_ptr = NULL; + png_byte buf[8]; + png_byte *png_pixels = NULL; + png_byte **row_pointers = NULL; + png_byte *pix_ptr = NULL; + png_uint_32 row_bytes; + + png_uint_32 width; + png_uint_32 height; + int bit_depth; + int channels; + int color_type; + int alpha_present; + int row, col; + int ret; + int i; + long dep_16; + + /* read and check signature in PNG file */ + ret = fread (buf, 1, 8, png_file); + if (ret != 8) + return FALSE; + + ret = png_check_sig (buf, 8); + if (!ret) + return FALSE; + + /* create png and info structures */ + + png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, + NULL, NULL, NULL); + if (!png_ptr) + return FALSE; /* out of memory */ + + info_ptr = png_create_info_struct (png_ptr); + if (!info_ptr) + { + png_destroy_read_struct (&png_ptr, NULL, NULL); + return FALSE; /* out of memory */ + } + + if (setjmp (png_jmpbuf(png_ptr))) + { + png_destroy_read_struct (&png_ptr, &info_ptr, NULL); + return FALSE; + } + + /* set up the input control for C streams */ + png_init_io (png_ptr, png_file); + png_set_sig_bytes (png_ptr, 8); /* we already read the 8 signature bytes */ + + /* read the file information */ + png_read_info (png_ptr, info_ptr); + + /* get size and bit-depth of the PNG-image */ + png_get_IHDR (png_ptr, info_ptr, + &width, &height, &bit_depth, &color_type, + NULL, NULL, NULL); + + /* set-up the transformations */ + + /* transform paletted images into full-color rgb */ + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_expand (png_ptr); + /* expand images to bit-depth 8 (only applicable for grayscale images) */ + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + png_set_expand (png_ptr); + /* transform transparency maps into full alpha-channel */ + if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_expand (png_ptr); + +#ifdef NJET + /* downgrade 16-bit images to 8 bit */ + if (bit_depth == 16) + png_set_strip_16 (png_ptr); + /* transform grayscale images into full-color */ + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb (png_ptr); + /* only if file has a file gamma, we do a correction */ + if (png_get_gAMA (png_ptr, info_ptr, &file_gamma)) + png_set_gamma (png_ptr, (double) 2.2, file_gamma); +#endif + + /* all transformations have been registered; now update info_ptr data, + * get rowbytes and channels, and allocate image memory */ + + png_read_update_info (png_ptr, info_ptr); + + /* get the new color-type and bit-depth (after expansion/stripping) */ + png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, + NULL, NULL, NULL); + + /* check for 16-bit files */ + if (bit_depth == 16) + { + raw = FALSE; +#ifdef __TURBOC__ + pnm_file->flags &= ~((unsigned) _F_BIN); +#endif + } + + /* calculate new number of channels and store alpha-presence */ + if (color_type == PNG_COLOR_TYPE_GRAY) + channels = 1; + else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + channels = 2; + else if (color_type == PNG_COLOR_TYPE_RGB) + channels = 3; + else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) + channels = 4; + else + channels = 0; /* should never happen */ + alpha_present = (channels - 1) % 2; + + /* check if alpha is expected to be present in file */ + if (alpha && !alpha_present) + { + fprintf (stderr, "PNG2PNM\n"); + fprintf (stderr, "Error: PNG-file doesn't contain alpha channel\n"); + exit (1); + } + + /* row_bytes is the width x number of channels x (bit-depth / 8) */ + row_bytes = png_get_rowbytes (png_ptr, info_ptr); + + if ((png_pixels = (png_byte *) malloc (row_bytes * height * sizeof (png_byte))) == NULL) { + png_destroy_read_struct (&png_ptr, &info_ptr, NULL); + return FALSE; + } + + if ((row_pointers = (png_byte **) malloc (height * sizeof (png_bytep))) == NULL) + { + png_destroy_read_struct (&png_ptr, &info_ptr, NULL); + free (png_pixels); + png_pixels = NULL; + return FALSE; + } + + /* set the individual row_pointers to point at the correct offsets */ + for (i = 0; i < (height); i++) + row_pointers[i] = png_pixels + i * row_bytes; + + /* now we can go ahead and just read the whole image */ + png_read_image (png_ptr, row_pointers); + + /* read rest of file, and get additional chunks in info_ptr - REQUIRED */ + png_read_end (png_ptr, info_ptr); + + /* clean up after the read, and free any memory allocated - REQUIRED */ + png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp) NULL); + + /* write header of PNM file */ + + if ((color_type == PNG_COLOR_TYPE_GRAY) || + (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)) + { + fprintf (pnm_file, "%s\n", (raw) ? "P5" : "P2"); + fprintf (pnm_file, "%d %d\n", (int) width, (int) height); + fprintf (pnm_file, "%ld\n", ((1L << (int) bit_depth) - 1L)); + } + else if ((color_type == PNG_COLOR_TYPE_RGB) || + (color_type == PNG_COLOR_TYPE_RGB_ALPHA)) + { + fprintf (pnm_file, "%s\n", (raw) ? "P6" : "P3"); + fprintf (pnm_file, "%d %d\n", (int) width, (int) height); + fprintf (pnm_file, "%ld\n", ((1L << (int) bit_depth) - 1L)); + } + + /* write header of PGM file with alpha channel */ + + if ((alpha) && + ((color_type == PNG_COLOR_TYPE_GRAY_ALPHA) || + (color_type == PNG_COLOR_TYPE_RGB_ALPHA))) + { + fprintf (alpha_file, "%s\n", (raw) ? "P5" : "P2"); + fprintf (alpha_file, "%d %d\n", (int) width, (int) height); + fprintf (alpha_file, "%ld\n", ((1L << (int) bit_depth) - 1L)); + } + + /* write data to PNM file */ + pix_ptr = png_pixels; + + for (row = 0; row < height; row++) + { + for (col = 0; col < width; col++) + { + for (i = 0; i < (channels - alpha_present); i++) + { + if (raw) + fputc ((int) *pix_ptr++ , pnm_file); + else + if (bit_depth == 16){ + dep_16 = (long) *pix_ptr++; + fprintf (pnm_file, "%ld ", (dep_16 << 8) + ((long) *pix_ptr++)); + } + else + fprintf (pnm_file, "%ld ", (long) *pix_ptr++); + } + if (alpha_present) + { + if (!alpha) + { + pix_ptr++; /* alpha */ + if (bit_depth == 16) + pix_ptr++; + } + else /* output alpha-channel as pgm file */ + { + if (raw) + fputc ((int) *pix_ptr++ , alpha_file); + else + if (bit_depth == 16){ + dep_16 = (long) *pix_ptr++; + fprintf (alpha_file, "%ld ", (dep_16 << 8) + (long) *pix_ptr++); + } + else + fprintf (alpha_file, "%ld ", (long) *pix_ptr++); + } + } /* if alpha_present */ + + if (!raw) + if (col % 4 == 3) + fprintf (pnm_file, "\n"); + } /* end for col */ + + if (!raw) + if (col % 4 != 0) + fprintf (pnm_file, "\n"); + } /* end for row */ + + if (row_pointers != (unsigned char**) NULL) + free (row_pointers); + if (png_pixels != (unsigned char*) NULL) + free (png_pixels); + + return TRUE; + +} /* end of source */ + diff --git a/Engine/lib/lpng/contrib/pngminus/png2pnm.sh b/Engine/lib/lpng/contrib/pngminus/png2pnm.sh new file mode 100644 index 000000000..b1c05370d --- /dev/null +++ b/Engine/lib/lpng/contrib/pngminus/png2pnm.sh @@ -0,0 +1,42 @@ +#!/bin/sh +# -- grayscale +./png2pnm -noraw ../pngsuite/basn0g01.png basn0g01.pgm +./png2pnm -noraw ../pngsuite/basn0g02.png basn0g02.pgm +./png2pnm -noraw ../pngsuite/basn0g04.png basn0g04.pgm +./png2pnm -noraw ../pngsuite/basn0g08.png basn0g08.pgm +./png2pnm -noraw ../pngsuite/basn0g16.png basn0g16.pgm +# -- full-color +./png2pnm -noraw ../pngsuite/basn2c08.png basn2c08.ppm +./png2pnm -noraw ../pngsuite/basn2c16.png basn2c16.ppm +# -- palletted +./png2pnm -noraw ../pngsuite/basn3p01.png basn3p01.ppm +./png2pnm -noraw ../pngsuite/basn3p02.png basn3p02.ppm +./png2pnm -noraw ../pngsuite/basn3p04.png basn3p04.ppm +./png2pnm -noraw ../pngsuite/basn3p08.png basn3p08.ppm +# -- gray with alpha-channel +./png2pnm -noraw ../pngsuite/basn4a08.png basn4a08.pgm +./png2pnm -noraw ../pngsuite/basn4a16.png basn4a16.pgm +# -- color with alpha-channel +./png2pnm -noraw -alpha basn6a08.pgm ../pngsuite/basn6a08.png basn6a08.ppm +./png2pnm -noraw -alpha basn6a16.pgm ../pngsuite/basn6a16.png basn6a16.ppm +# -- grayscale +./png2pnm -raw ../pngsuite/basn0g01.png rawn0g01.pgm +./png2pnm -raw ../pngsuite/basn0g02.png rawn0g02.pgm +./png2pnm -raw ../pngsuite/basn0g04.png rawn0g04.pgm +./png2pnm -raw ../pngsuite/basn0g08.png rawn0g08.pgm +./png2pnm -raw ../pngsuite/basn0g16.png rawn0g16.pgm +# -- full-color +./png2pnm -raw ../pngsuite/basn2c08.png rawn2c08.ppm +./png2pnm -raw ../pngsuite/basn2c16.png rawn2c16.ppm +# -- palletted +./png2pnm -raw ../pngsuite/basn3p01.png rawn3p01.ppm +./png2pnm -raw ../pngsuite/basn3p02.png rawn3p02.ppm +./png2pnm -raw ../pngsuite/basn3p04.png rawn3p04.ppm +./png2pnm -raw ../pngsuite/basn3p08.png rawn3p08.ppm +# -- gray with alpha-channel +./png2pnm -raw ../pngsuite/basn4a08.png rawn4a08.pgm +./png2pnm -raw ../pngsuite/basn4a16.png rawn4a16.pgm +# -- color with alpha-channel +./png2pnm -noraw -alpha rawn6a08.pgm ../pngsuite/basn6a08.png rawn6a08.ppm +./png2pnm -noraw -alpha rawn6a16.pgm ../pngsuite/basn6a16.png rawn6a16.ppm + diff --git a/Engine/lib/lpng/contrib/pngminus/pngminus.bat b/Engine/lib/lpng/contrib/pngminus/pngminus.bat new file mode 100644 index 000000000..911bb8dff --- /dev/null +++ b/Engine/lib/lpng/contrib/pngminus/pngminus.bat @@ -0,0 +1,4 @@ +make -f makefile.tc3 +call png2pnm.bat +call pnm2png.bat + diff --git a/Engine/lib/lpng/contrib/pngminus/pngminus.sh b/Engine/lib/lpng/contrib/pngminus/pngminus.sh new file mode 100644 index 000000000..2a0a9d8fb --- /dev/null +++ b/Engine/lib/lpng/contrib/pngminus/pngminus.sh @@ -0,0 +1,5 @@ +#!/bin/sh +make -f makefile.std +sh png2pnm.sh +sh pnm2png.sh + diff --git a/Engine/lib/lpng/contrib/pngminus/pnm2png.bat b/Engine/lib/lpng/contrib/pngminus/pnm2png.bat new file mode 100644 index 000000000..f756cb84d --- /dev/null +++ b/Engine/lib/lpng/contrib/pngminus/pnm2png.bat @@ -0,0 +1,41 @@ +REM -- grayscale +pnm2png.exe basn0g01.pgm basn0g01.png +pnm2png.exe basn0g02.pgm basn0g02.png +pnm2png.exe basn0g04.pgm basn0g04.png +pnm2png.exe basn0g08.pgm basn0g08.png +pnm2png.exe basn0g16.pgm basn0g16.png +REM -- full-color +pnm2png.exe basn2c08.ppm basn2c08.png +pnm2png.exe basn2c16.ppm basn2c16.png +REM -- palletted +pnm2png.exe basn3p01.ppm basn3p01.png +pnm2png.exe basn3p02.ppm basn3p02.png +pnm2png.exe basn3p04.ppm basn3p04.png +pnm2png.exe basn3p08.ppm basn3p08.png +REM -- gray with alpha-channel +pnm2png.exe -alpha basn6a08.pgm basn4a08.pgm basn4a08.png +pnm2png.exe -alpha basn6a16.pgm basn4a16.pgm basn4a16.png +REM -- color with alpha-channel +pnm2png.exe -alpha basn6a08.pgm basn6a08.ppm basn6a08.png +pnm2png.exe -alpha basn6a16.pgm basn6a16.ppm basn6a16.png +REM -- grayscale +pnm2png.exe rawn0g01.pgm rawn0g01.png +pnm2png.exe rawn0g02.pgm rawn0g02.png +pnm2png.exe rawn0g04.pgm rawn0g04.png +pnm2png.exe rawn0g08.pgm rawn0g08.png +pnm2png.exe rawn0g16.pgm rawn0g16.png +REM -- full-color +pnm2png.exe rawn2c08.ppm rawn2c08.png +pnm2png.exe rawn2c16.ppm rawn2c16.png +REM -- palletted +pnm2png.exe rawn3p01.ppm rawn3p01.png +pnm2png.exe rawn3p02.ppm rawn3p02.png +pnm2png.exe rawn3p04.ppm rawn3p04.png +pnm2png.exe rawn3p08.ppm rawn3p08.png +REM -- gray with alpha-channel +pnm2png.exe -alpha rawn6a08.pgm rawn4a08.pgm rawn4a08.png +pnm2png.exe -alpha rawn6a16.pgm rawn4a16.pgm rawn4a16.png +REM -- color with alpha-channel +pnm2png.exe -alpha rawn6a08.pgm rawn6a08.ppm rawn6a08.png +pnm2png.exe -alpha rawn6a16.pgm rawn6a16.ppm rawn6a16.png + diff --git a/Engine/lib/lpng/contrib/pngminus/pnm2png.c b/Engine/lib/lpng/contrib/pngminus/pnm2png.c new file mode 100644 index 000000000..4cdfad831 --- /dev/null +++ b/Engine/lib/lpng/contrib/pngminus/pnm2png.c @@ -0,0 +1,533 @@ +/* + * pnm2png.c --- conversion from PBM/PGM/PPM-file to PNG-file + * copyright (C) 1999 by Willem van Schaik + * + * version 1.0 - 1999.10.15 - First version. + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby granted, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear in + * supporting documentation. This software is provided "as is" without + * express or implied warranty. + */ + +#include +#include +#ifdef __TURBOC__ +#include +#include +#endif + +#ifndef BOOL +#define BOOL unsigned char +#endif +#ifndef TRUE +#define TRUE (BOOL) 1 +#endif +#ifndef FALSE +#define FALSE (BOOL) 0 +#endif + +#define STDIN 0 +#define STDOUT 1 +#define STDERR 2 + +/* to make pnm2png verbose so we can find problems (needs to be before png.h) */ +#ifndef PNG_DEBUG +#define PNG_DEBUG 0 +#endif + +#include "png.h" + +/* Define png_jmpbuf() in case we are using a pre-1.0.6 version of libpng */ +#ifndef png_jmpbuf +# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) +#endif + +/* function prototypes */ + +int main (int argc, char *argv[]); +void usage (); +BOOL pnm2png (FILE *pnm_file, FILE *png_file, FILE *alpha_file, BOOL interlace, BOOL alpha); +void get_token(FILE *pnm_file, char *token); +png_uint_32 get_data (FILE *pnm_file, int depth); +png_uint_32 get_value (FILE *pnm_file, int depth); + +/* + * main + */ + +int main(int argc, char *argv[]) +{ + FILE *fp_rd = stdin; + FILE *fp_al = NULL; + FILE *fp_wr = stdout; + BOOL interlace = FALSE; + BOOL alpha = FALSE; + int argi; + + for (argi = 1; argi < argc; argi++) + { + if (argv[argi][0] == '-') + { + switch (argv[argi][1]) + { + case 'i': + interlace = TRUE; + break; + case 'a': + alpha = TRUE; + argi++; + if ((fp_al = fopen (argv[argi], "rb")) == NULL) + { + fprintf (stderr, "PNM2PNG\n"); + fprintf (stderr, "Error: alpha-channel file %s does not exist\n", + argv[argi]); + exit (1); + } + break; + case 'h': + case '?': + usage(); + exit(0); + break; + default: + fprintf (stderr, "PNM2PNG\n"); + fprintf (stderr, "Error: unknown option %s\n", argv[argi]); + usage(); + exit(1); + break; + } /* end switch */ + } + else if (fp_rd == stdin) + { + if ((fp_rd = fopen (argv[argi], "rb")) == NULL) + { + fprintf (stderr, "PNM2PNG\n"); + fprintf (stderr, "Error: file %s does not exist\n", argv[argi]); + exit (1); + } + } + else if (fp_wr == stdout) + { + if ((fp_wr = fopen (argv[argi], "wb")) == NULL) + { + fprintf (stderr, "PNM2PNG\n"); + fprintf (stderr, "Error: can not create PNG-file %s\n", argv[argi]); + exit (1); + } + } + else + { + fprintf (stderr, "PNM2PNG\n"); + fprintf (stderr, "Error: too many parameters\n"); + usage(); + exit (1); + } + } /* end for */ + +#ifdef __TURBOC__ + /* set stdin/stdout to binary, we're reading the PNM always! in binary format */ + if (fp_rd == stdin) + { + setmode (STDIN, O_BINARY); + } + if (fp_wr == stdout) + { + setmode (STDOUT, O_BINARY); + } +#endif + + /* call the conversion program itself */ + if (pnm2png (fp_rd, fp_wr, fp_al, interlace, alpha) == FALSE) + { + fprintf (stderr, "PNM2PNG\n"); + fprintf (stderr, "Error: unsuccessful converting to PNG-image\n"); + exit (1); + } + + /* close input file */ + fclose (fp_rd); + /* close output file */ + fclose (fp_wr); + /* close alpha file */ + if (alpha) + fclose (fp_al); + + return 0; +} + +/* + * usage + */ + +void usage() +{ + fprintf (stderr, "PNM2PNG\n"); + fprintf (stderr, " by Willem van Schaik, 1999\n"); +#ifdef __TURBOC__ + fprintf (stderr, " for Turbo-C and Borland-C compilers\n"); +#else + fprintf (stderr, " for Linux (and Unix) compilers\n"); +#endif + fprintf (stderr, "Usage: pnm2png [options] . [.png]\n"); + fprintf (stderr, " or: ... | pnm2png [options]\n"); + fprintf (stderr, "Options:\n"); + fprintf (stderr, " -i[nterlace] write png-file with interlacing on\n"); + fprintf (stderr, " -a[lpha] .pgm read PNG alpha channel as pgm-file\n"); + fprintf (stderr, " -h | -? print this help-information\n"); +} + +/* + * pnm2png + */ + +BOOL pnm2png (FILE *pnm_file, FILE *png_file, FILE *alpha_file, BOOL interlace, BOOL alpha) +{ + png_struct *png_ptr = NULL; + png_info *info_ptr = NULL; + png_byte *png_pixels = NULL; + png_byte **row_pointers = NULL; + png_byte *pix_ptr = NULL; + png_uint_32 row_bytes; + + char type_token[16]; + char width_token[16]; + char height_token[16]; + char maxval_token[16]; + int color_type; + png_uint_32 width, alpha_width; + png_uint_32 height, alpha_height; + png_uint_32 maxval; + int bit_depth = 0; + int channels; + int alpha_depth = 0; + int alpha_present; + int row, col; + BOOL raw, alpha_raw = FALSE; + png_uint_32 tmp16; + int i; + + /* read header of PNM file */ + + get_token(pnm_file, type_token); + if (type_token[0] != 'P') + { + return FALSE; + } + else if ((type_token[1] == '1') || (type_token[1] == '4')) + { + raw = (type_token[1] == '4'); + color_type = PNG_COLOR_TYPE_GRAY; + bit_depth = 1; + } + else if ((type_token[1] == '2') || (type_token[1] == '5')) + { + raw = (type_token[1] == '5'); + color_type = PNG_COLOR_TYPE_GRAY; + get_token(pnm_file, width_token); + sscanf (width_token, "%lu", &width); + get_token(pnm_file, height_token); + sscanf (height_token, "%lu", &height); + get_token(pnm_file, maxval_token); + sscanf (maxval_token, "%lu", &maxval); + if (maxval <= 1) + bit_depth = 1; + else if (maxval <= 3) + bit_depth = 2; + else if (maxval <= 15) + bit_depth = 4; + else if (maxval <= 255) + bit_depth = 8; + else /* if (maxval <= 65535) */ + bit_depth = 16; + } + else if ((type_token[1] == '3') || (type_token[1] == '6')) + { + raw = (type_token[1] == '6'); + color_type = PNG_COLOR_TYPE_RGB; + get_token(pnm_file, width_token); + sscanf (width_token, "%lu", &width); + get_token(pnm_file, height_token); + sscanf (height_token, "%lu", &height); + get_token(pnm_file, maxval_token); + sscanf (maxval_token, "%lu", &maxval); + if (maxval <= 1) + bit_depth = 1; + else if (maxval <= 3) + bit_depth = 2; + else if (maxval <= 15) + bit_depth = 4; + else if (maxval <= 255) + bit_depth = 8; + else /* if (maxval <= 65535) */ + bit_depth = 16; + } + else + { + return FALSE; + } + + /* read header of PGM file with alpha channel */ + + if (alpha) + { + if (color_type == PNG_COLOR_TYPE_GRAY) + color_type = PNG_COLOR_TYPE_GRAY_ALPHA; + if (color_type == PNG_COLOR_TYPE_RGB) + color_type = PNG_COLOR_TYPE_RGB_ALPHA; + + get_token(alpha_file, type_token); + if (type_token[0] != 'P') + { + return FALSE; + } + else if ((type_token[1] == '2') || (type_token[1] == '5')) + { + alpha_raw = (type_token[1] == '5'); + get_token(alpha_file, width_token); + sscanf (width_token, "%lu", &alpha_width); + if (alpha_width != width) + return FALSE; + get_token(alpha_file, height_token); + sscanf (height_token, "%lu", &alpha_height); + if (alpha_height != height) + return FALSE; + get_token(alpha_file, maxval_token); + sscanf (maxval_token, "%lu", &maxval); + if (maxval <= 1) + alpha_depth = 1; + else if (maxval <= 3) + alpha_depth = 2; + else if (maxval <= 15) + alpha_depth = 4; + else if (maxval <= 255) + alpha_depth = 8; + else /* if (maxval <= 65535) */ + alpha_depth = 16; + if (alpha_depth != bit_depth) + return FALSE; + } + else + { + return FALSE; + } + } /* end if alpha */ + + /* calculate the number of channels and store alpha-presence */ + if (color_type == PNG_COLOR_TYPE_GRAY) + channels = 1; + else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + channels = 2; + else if (color_type == PNG_COLOR_TYPE_RGB) + channels = 3; + else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) + channels = 4; + else + channels = 0; /* should not happen */ + + alpha_present = (channels - 1) % 2; + + /* row_bytes is the width x number of channels x (bit-depth / 8) */ + row_bytes = width * channels * ((bit_depth <= 8) ? 1 : 2); + + if ((png_pixels = (png_byte *) malloc (row_bytes * height * sizeof (png_byte))) == NULL) + return FALSE; + + /* read data from PNM file */ + pix_ptr = png_pixels; + + for (row = 0; row < height; row++) + { + for (col = 0; col < width; col++) + { + for (i = 0; i < (channels - alpha_present); i++) + { + if (raw) + *pix_ptr++ = get_data (pnm_file, bit_depth); + else + if (bit_depth <= 8) + *pix_ptr++ = get_value (pnm_file, bit_depth); + else + { + tmp16 = get_value (pnm_file, bit_depth); + *pix_ptr = (png_byte) ((tmp16 >> 8) & 0xFF); + pix_ptr++; + *pix_ptr = (png_byte) (tmp16 & 0xFF); + pix_ptr++; + } + } + + if (alpha) /* read alpha-channel from pgm file */ + { + if (alpha_raw) + *pix_ptr++ = get_data (alpha_file, alpha_depth); + else + if (alpha_depth <= 8) + *pix_ptr++ = get_value (alpha_file, bit_depth); + else + { + tmp16 = get_value (alpha_file, bit_depth); + *pix_ptr++ = (png_byte) ((tmp16 >> 8) & 0xFF); + *pix_ptr++ = (png_byte) (tmp16 & 0xFF); + } + } /* if alpha */ + + } /* end for col */ + } /* end for row */ + + /* prepare the standard PNG structures */ + png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + { + return FALSE; + } + info_ptr = png_create_info_struct (png_ptr); + if (!info_ptr) + { + png_destroy_write_struct (&png_ptr, (png_infopp) NULL); + return FALSE; + } + + /* setjmp() must be called in every function that calls a PNG-reading libpng function */ + if (setjmp (png_jmpbuf(png_ptr))) + { + png_destroy_write_struct (&png_ptr, (png_infopp) NULL); + return FALSE; + } + + /* initialize the png structure */ + png_init_io (png_ptr, png_file); + + /* we're going to write more or less the same PNG as the input file */ + png_set_IHDR (png_ptr, info_ptr, width, height, bit_depth, color_type, + (!interlace) ? PNG_INTERLACE_NONE : PNG_INTERLACE_ADAM7, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + + /* write the file header information */ + png_write_info (png_ptr, info_ptr); + + /* if needed we will allocate memory for an new array of row-pointers */ + if (row_pointers == (unsigned char**) NULL) + { + if ((row_pointers = (png_byte **) malloc (height * sizeof (png_bytep))) == NULL) + { + png_destroy_write_struct (&png_ptr, (png_infopp) NULL); + return FALSE; + } + } + + /* set the individual row_pointers to point at the correct offsets */ + for (i = 0; i < (height); i++) + row_pointers[i] = png_pixels + i * row_bytes; + + /* write out the entire image data in one call */ + png_write_image (png_ptr, row_pointers); + + /* write the additional chuncks to the PNG file (not really needed) */ + png_write_end (png_ptr, info_ptr); + + /* clean up after the write, and free any memory allocated */ + png_destroy_write_struct (&png_ptr, (png_infopp) NULL); + + if (row_pointers != (unsigned char**) NULL) + free (row_pointers); + if (png_pixels != (unsigned char*) NULL) + free (png_pixels); + + return TRUE; +} /* end of pnm2png */ + +/* + * get_token() - gets the first string after whitespace + */ + +void get_token(FILE *pnm_file, char *token) +{ + int i = 0; + + /* remove white-space */ + do + { + token[i] = (unsigned char) fgetc (pnm_file); + } + while ((token[i] == '\n') || (token[i] == '\r') || (token[i] == ' ')); + + /* read string */ + do + { + i++; + token[i] = (unsigned char) fgetc (pnm_file); + } + while ((token[i] != '\n') && (token[i] != '\r') && (token[i] != ' ')); + + token[i] = '\0'; + + return; +} + +/* + * get_data() - takes first byte and converts into next pixel value, + * taking as much bits as defined by bit-depth and + * using the bit-depth to fill up a byte (0Ah -> AAh) + */ + +png_uint_32 get_data (FILE *pnm_file, int depth) +{ + static int bits_left = 0; + static int old_value = 0; + static int mask = 0; + int i; + png_uint_32 ret_value; + + if (mask == 0) + for (i = 0; i < depth; i++) + mask = (mask >> 1) | 0x80; + + if (bits_left <= 0) + { + old_value = fgetc (pnm_file); + bits_left = 8; + } + + ret_value = old_value & mask; + for (i = 1; i < (8 / depth); i++) + ret_value = ret_value || (ret_value >> depth); + + old_value = (old_value << depth) & 0xFF; + bits_left -= depth; + + return ret_value; +} + +/* + * get_value() - takes first (numeric) string and converts into number, + * using the bit-depth to fill up a byte (0Ah -> AAh) + */ + +png_uint_32 get_value (FILE *pnm_file, int depth) +{ + static png_uint_32 mask = 0; + png_byte token[16]; + png_uint_32 ret_value; + int i = 0; + + if (mask == 0) + for (i = 0; i < depth; i++) + mask = (mask << 1) | 0x01; + + get_token (pnm_file, (char *) token); + sscanf ((const char *) token, "%lu", &ret_value); + + ret_value &= mask; + + if (depth < 8) + for (i = 0; i < (8 / depth); i++) + ret_value = (ret_value << depth) || ret_value; + + return ret_value; +} + +/* end of source */ + diff --git a/Engine/lib/lpng/contrib/pngminus/pnm2png.sh b/Engine/lib/lpng/contrib/pngminus/pnm2png.sh new file mode 100644 index 000000000..d79df2fae --- /dev/null +++ b/Engine/lib/lpng/contrib/pngminus/pnm2png.sh @@ -0,0 +1,42 @@ +#!/bin/sh +# -- grayscale +./pnm2png basn0g01.pgm basn0g01.png +./pnm2png basn0g02.pgm basn0g02.png +./pnm2png basn0g04.pgm basn0g04.png +./pnm2png basn0g08.pgm basn0g08.png +./pnm2png basn0g16.pgm basn0g16.png +# -- full-color +./pnm2png basn2c08.ppm basn2c08.png +./pnm2png basn2c16.ppm basn2c16.png +# -- palletted +./pnm2png basn3p01.ppm basn3p01.png +./pnm2png basn3p02.ppm basn3p02.png +./pnm2png basn3p04.ppm basn3p04.png +./pnm2png basn3p08.ppm basn3p08.png +# -- gray with alpha-channel +./pnm2png -alpha basn6a08.pgm basn4a08.pgm basn4a08.png +./pnm2png -alpha basn6a16.pgm basn4a16.pgm basn4a16.png +# -- color with alpha-channel +./pnm2png -alpha basn6a08.pgm basn6a08.ppm basn6a08.png +./pnm2png -alpha basn6a16.pgm basn6a16.ppm basn6a16.png +# -- grayscale +./pnm2png rawn0g01.pgm rawn0g01.png +./pnm2png rawn0g02.pgm rawn0g02.png +./pnm2png rawn0g04.pgm rawn0g04.png +./pnm2png rawn0g08.pgm rawn0g08.png +./pnm2png rawn0g16.pgm rawn0g16.png +# -- full-color +./pnm2png rawn2c08.ppm rawn2c08.png +./pnm2png rawn2c16.ppm rawn2c16.png +# -- palletted +./pnm2png rawn3p01.ppm rawn3p01.png +./pnm2png rawn3p02.ppm rawn3p02.png +./pnm2png rawn3p04.ppm rawn3p04.png +./pnm2png rawn3p08.ppm rawn3p08.png +# -- gray with alpha-channel +./pnm2png -alpha rawn6a08.pgm rawn4a08.pgm rawn4a08.png +./pnm2png -alpha rawn6a16.pgm rawn4a16.pgm rawn4a16.png +# -- color with alpha-channel +./pnm2png -alpha rawn6a08.pgm rawn6a08.ppm rawn6a08.png +./pnm2png -alpha rawn6a16.pgm rawn6a16.ppm rawn6a16.png + diff --git a/Engine/lib/lpng/contrib/pngsuite/basn0g01.png b/Engine/lib/lpng/contrib/pngsuite/basn0g01.png new file mode 100644 index 000000000..e31e1c7a6 Binary files /dev/null and b/Engine/lib/lpng/contrib/pngsuite/basn0g01.png differ diff --git a/Engine/lib/lpng/contrib/pngsuite/basn0g02.png b/Engine/lib/lpng/contrib/pngsuite/basn0g02.png new file mode 100644 index 000000000..68809dd8f Binary files /dev/null and b/Engine/lib/lpng/contrib/pngsuite/basn0g02.png differ diff --git a/Engine/lib/lpng/contrib/pngsuite/basn0g04.png b/Engine/lib/lpng/contrib/pngsuite/basn0g04.png new file mode 100644 index 000000000..6fa089cb8 Binary files /dev/null and b/Engine/lib/lpng/contrib/pngsuite/basn0g04.png differ diff --git a/Engine/lib/lpng/contrib/pngsuite/basn0g08.png b/Engine/lib/lpng/contrib/pngsuite/basn0g08.png new file mode 100644 index 000000000..bf522eef0 Binary files /dev/null and b/Engine/lib/lpng/contrib/pngsuite/basn0g08.png differ diff --git a/Engine/lib/lpng/contrib/pngsuite/basn0g16.png b/Engine/lib/lpng/contrib/pngsuite/basn0g16.png new file mode 100644 index 000000000..318ebcadf Binary files /dev/null and b/Engine/lib/lpng/contrib/pngsuite/basn0g16.png differ diff --git a/Engine/lib/lpng/contrib/pngsuite/basn2c08.png b/Engine/lib/lpng/contrib/pngsuite/basn2c08.png new file mode 100644 index 000000000..21d2f91a8 Binary files /dev/null and b/Engine/lib/lpng/contrib/pngsuite/basn2c08.png differ diff --git a/Engine/lib/lpng/contrib/pngsuite/basn2c16.png b/Engine/lib/lpng/contrib/pngsuite/basn2c16.png new file mode 100644 index 000000000..1bd4a4d0e Binary files /dev/null and b/Engine/lib/lpng/contrib/pngsuite/basn2c16.png differ diff --git a/Engine/lib/lpng/contrib/pngsuite/basn3p01.png b/Engine/lib/lpng/contrib/pngsuite/basn3p01.png new file mode 100644 index 000000000..a21db5977 Binary files /dev/null and b/Engine/lib/lpng/contrib/pngsuite/basn3p01.png differ diff --git a/Engine/lib/lpng/contrib/pngsuite/basn3p02.png b/Engine/lib/lpng/contrib/pngsuite/basn3p02.png new file mode 100644 index 000000000..1d0ab6197 Binary files /dev/null and b/Engine/lib/lpng/contrib/pngsuite/basn3p02.png differ diff --git a/Engine/lib/lpng/contrib/pngsuite/basn3p04.png b/Engine/lib/lpng/contrib/pngsuite/basn3p04.png new file mode 100644 index 000000000..6dc6eac83 Binary files /dev/null and b/Engine/lib/lpng/contrib/pngsuite/basn3p04.png differ diff --git a/Engine/lib/lpng/contrib/pngsuite/basn3p08.png b/Engine/lib/lpng/contrib/pngsuite/basn3p08.png new file mode 100644 index 000000000..0e07f483c Binary files /dev/null and b/Engine/lib/lpng/contrib/pngsuite/basn3p08.png differ diff --git a/Engine/lib/lpng/contrib/pngsuite/basn4a08.png b/Engine/lib/lpng/contrib/pngsuite/basn4a08.png new file mode 100644 index 000000000..3bb0dd06b Binary files /dev/null and b/Engine/lib/lpng/contrib/pngsuite/basn4a08.png differ diff --git a/Engine/lib/lpng/contrib/pngsuite/basn4a16.png b/Engine/lib/lpng/contrib/pngsuite/basn4a16.png new file mode 100644 index 000000000..6dbee9fbd Binary files /dev/null and b/Engine/lib/lpng/contrib/pngsuite/basn4a16.png differ diff --git a/Engine/lib/lpng/contrib/pngsuite/basn6a08.png b/Engine/lib/lpng/contrib/pngsuite/basn6a08.png new file mode 100644 index 000000000..610623085 Binary files /dev/null and b/Engine/lib/lpng/contrib/pngsuite/basn6a08.png differ diff --git a/Engine/lib/lpng/contrib/pngsuite/basn6a16.png b/Engine/lib/lpng/contrib/pngsuite/basn6a16.png new file mode 100644 index 000000000..a9bf3cb46 Binary files /dev/null and b/Engine/lib/lpng/contrib/pngsuite/basn6a16.png differ diff --git a/Engine/lib/lpng/contrib/visupng/PngFile.c b/Engine/lib/lpng/contrib/visupng/PngFile.c new file mode 100644 index 000000000..959afe9de --- /dev/null +++ b/Engine/lib/lpng/contrib/visupng/PngFile.c @@ -0,0 +1,439 @@ +//------------------------------------- +// PNGFILE.C -- Image File Functions +//------------------------------------- + +// Copyright 2000, Willem van Schaik. For conditions of distribution and +// use, see the copyright/license/disclaimer notice in png.h + +#include +#include +#include +#include + +#include "png.h" +#include "pngfile.h" +#include "cexcept.h" + +define_exception_type(const char *); +extern struct exception_context the_exception_context[1]; +struct exception_context the_exception_context[1]; +png_const_charp msg; + +static OPENFILENAME ofn; + +static png_structp png_ptr = NULL; +static png_infop info_ptr = NULL; + + +// cexcept interface + +static void +png_cexcept_error(png_structp png_ptr, png_const_charp msg) +{ + if(png_ptr) + ; +#ifndef PNG_NO_CONSOLE_IO + fprintf(stderr, "libpng error: %s\n", msg); +#endif + { + Throw msg; + } +} + +// Windows open-file functions + +void PngFileInitialize (HWND hwnd) +{ + static TCHAR szFilter[] = TEXT ("PNG Files (*.PNG)\0*.png\0") + TEXT ("All Files (*.*)\0*.*\0\0"); + + ofn.lStructSize = sizeof (OPENFILENAME); + ofn.hwndOwner = hwnd; + ofn.hInstance = NULL; + ofn.lpstrFilter = szFilter; + ofn.lpstrCustomFilter = NULL; + ofn.nMaxCustFilter = 0; + ofn.nFilterIndex = 0; + ofn.lpstrFile = NULL; // Set in Open and Close functions + ofn.nMaxFile = MAX_PATH; + ofn.lpstrFileTitle = NULL; // Set in Open and Close functions + ofn.nMaxFileTitle = MAX_PATH; + ofn.lpstrInitialDir = NULL; + ofn.lpstrTitle = NULL; + ofn.Flags = 0; // Set in Open and Close functions + ofn.nFileOffset = 0; + ofn.nFileExtension = 0; + ofn.lpstrDefExt = TEXT ("png"); + ofn.lCustData = 0; + ofn.lpfnHook = NULL; + ofn.lpTemplateName = NULL; +} + +BOOL PngFileOpenDlg (HWND hwnd, PTSTR pstrFileName, PTSTR pstrTitleName) +{ + ofn.hwndOwner = hwnd; + ofn.lpstrFile = pstrFileName; + ofn.lpstrFileTitle = pstrTitleName; + ofn.Flags = OFN_HIDEREADONLY; + + return GetOpenFileName (&ofn); +} + +BOOL PngFileSaveDlg (HWND hwnd, PTSTR pstrFileName, PTSTR pstrTitleName) +{ + ofn.hwndOwner = hwnd; + ofn.lpstrFile = pstrFileName; + ofn.lpstrFileTitle = pstrTitleName; + ofn.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT; + + return GetSaveFileName (&ofn); +} + +// PNG image handler functions + +BOOL PngLoadImage (PTSTR pstrFileName, png_byte **ppbImageData, + int *piWidth, int *piHeight, int *piChannels, png_color *pBkgColor) +{ + static FILE *pfFile; + png_byte pbSig[8]; + int iBitDepth; + int iColorType; + double dGamma; + png_color_16 *pBackground; + png_uint_32 ulChannels; + png_uint_32 ulRowBytes; + png_byte *pbImageData = *ppbImageData; + static png_byte **ppbRowPointers = NULL; + int i; + + // open the PNG input file + + if (!pstrFileName) + { + *ppbImageData = pbImageData = NULL; + return FALSE; + } + + if (!(pfFile = fopen(pstrFileName, "rb"))) + { + *ppbImageData = pbImageData = NULL; + return FALSE; + } + + // first check the eight byte PNG signature + + fread(pbSig, 1, 8, pfFile); + if (!png_check_sig(pbSig, 8)) + { + *ppbImageData = pbImageData = NULL; + return FALSE; + } + + // create the two png(-info) structures + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, + (png_error_ptr)png_cexcept_error, (png_error_ptr)NULL); + if (!png_ptr) + { + *ppbImageData = pbImageData = NULL; + return FALSE; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_read_struct(&png_ptr, NULL, NULL); + *ppbImageData = pbImageData = NULL; + return FALSE; + } + + Try + { + + // initialize the png structure + +#if !defined(PNG_NO_STDIO) + png_init_io(png_ptr, pfFile); +#else + png_set_read_fn(png_ptr, (png_voidp)pfFile, png_read_data); +#endif + + png_set_sig_bytes(png_ptr, 8); + + // read all PNG info up to image data + + png_read_info(png_ptr, info_ptr); + + // get width, height, bit-depth and color-type + + png_get_IHDR(png_ptr, info_ptr, piWidth, piHeight, &iBitDepth, + &iColorType, NULL, NULL, NULL); + + // expand images of all color-type and bit-depth to 3x8 bit RGB images + // let the library process things like alpha, transparency, background + + if (iBitDepth == 16) + png_set_strip_16(png_ptr); + if (iColorType == PNG_COLOR_TYPE_PALETTE) + png_set_expand(png_ptr); + if (iBitDepth < 8) + png_set_expand(png_ptr); + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_expand(png_ptr); + if (iColorType == PNG_COLOR_TYPE_GRAY || + iColorType == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png_ptr); + + // set the background color to draw transparent and alpha images over. + if (png_get_bKGD(png_ptr, info_ptr, &pBackground)) + { + png_set_background(png_ptr, pBackground, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0); + pBkgColor->red = (byte) pBackground->red; + pBkgColor->green = (byte) pBackground->green; + pBkgColor->blue = (byte) pBackground->blue; + } + else + { + pBkgColor = NULL; + } + + // if required set gamma conversion + if (png_get_gAMA(png_ptr, info_ptr, &dGamma)) + png_set_gamma(png_ptr, (double) 2.2, dGamma); + + // after the transformations have been registered update info_ptr data + + png_read_update_info(png_ptr, info_ptr); + + // get again width, height and the new bit-depth and color-type + + png_get_IHDR(png_ptr, info_ptr, piWidth, piHeight, &iBitDepth, + &iColorType, NULL, NULL, NULL); + + + // row_bytes is the width x number of channels + + ulRowBytes = png_get_rowbytes(png_ptr, info_ptr); + ulChannels = png_get_channels(png_ptr, info_ptr); + + *piChannels = ulChannels; + + // now we can allocate memory to store the image + + if (pbImageData) + { + free (pbImageData); + pbImageData = NULL; + } + if ((pbImageData = (png_byte *) malloc(ulRowBytes * (*piHeight) + * sizeof(png_byte))) == NULL) + { + png_error(png_ptr, "Visual PNG: out of memory"); + } + *ppbImageData = pbImageData; + + // and allocate memory for an array of row-pointers + + if ((ppbRowPointers = (png_bytepp) malloc((*piHeight) + * sizeof(png_bytep))) == NULL) + { + png_error(png_ptr, "Visual PNG: out of memory"); + } + + // set the individual row-pointers to point at the correct offsets + + for (i = 0; i < (*piHeight); i++) + ppbRowPointers[i] = pbImageData + i * ulRowBytes; + + // now we can go ahead and just read the whole image + + png_read_image(png_ptr, ppbRowPointers); + + // read the additional chunks in the PNG file (not really needed) + + png_read_end(png_ptr, NULL); + + // and we're done + + free (ppbRowPointers); + ppbRowPointers = NULL; + + // yepp, done + } + + Catch (msg) + { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + + *ppbImageData = pbImageData = NULL; + + if(ppbRowPointers) + free (ppbRowPointers); + + fclose(pfFile); + + return FALSE; + } + + fclose (pfFile); + + return TRUE; +} + + +BOOL PngSaveImage (PTSTR pstrFileName, png_byte *pDiData, + int iWidth, int iHeight, png_color bkgColor) +{ + const int ciBitDepth = 8; + const int ciChannels = 3; + + static FILE *pfFile; + png_uint_32 ulRowBytes; + static png_byte **ppbRowPointers = NULL; + int i; + + // open the PNG output file + + if (!pstrFileName) + return FALSE; + + if (!(pfFile = fopen(pstrFileName, "wb"))) + return FALSE; + + // prepare the standard PNG structures + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, + (png_error_ptr)png_cexcept_error, (png_error_ptr)NULL); + if (!png_ptr) + { + fclose(pfFile); + return FALSE; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + fclose(pfFile); + png_destroy_write_struct(&png_ptr, (png_infopp) NULL); + return FALSE; + } + + Try + { + // initialize the png structure + +#if !defined(PNG_NO_STDIO) + png_init_io(png_ptr, pfFile); +#else + png_set_write_fn(png_ptr, (png_voidp)pfFile, png_write_data, png_flush); +#endif + + // we're going to write a very simple 3x8 bit RGB image + + png_set_IHDR(png_ptr, info_ptr, iWidth, iHeight, ciBitDepth, + PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, + PNG_FILTER_TYPE_BASE); + + // write the file header information + + png_write_info(png_ptr, info_ptr); + + // swap the BGR pixels in the DiData structure to RGB + + png_set_bgr(png_ptr); + + // row_bytes is the width x number of channels + + ulRowBytes = iWidth * ciChannels; + + // we can allocate memory for an array of row-pointers + + if ((ppbRowPointers = (png_bytepp) malloc(iHeight * sizeof(png_bytep))) == NULL) + Throw "Visualpng: Out of memory"; + + // set the individual row-pointers to point at the correct offsets + + for (i = 0; i < iHeight; i++) + ppbRowPointers[i] = pDiData + i * (((ulRowBytes + 3) >> 2) << 2); + + // write out the entire image data in one call + + png_write_image (png_ptr, ppbRowPointers); + + // write the additional chunks to the PNG file (not really needed) + + png_write_end(png_ptr, info_ptr); + + // and we're done + + free (ppbRowPointers); + ppbRowPointers = NULL; + + // clean up after the write, and free any memory allocated + + png_destroy_write_struct(&png_ptr, (png_infopp) NULL); + + // yepp, done + } + + Catch (msg) + { + png_destroy_write_struct(&png_ptr, (png_infopp) NULL); + + if(ppbRowPointers) + free (ppbRowPointers); + + fclose(pfFile); + + return FALSE; + } + + fclose (pfFile); + + return TRUE; +} + +#ifdef PNG_NO_STDIO + +static void +png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_size_t check; + + /* fread() returns 0 on error, so it is OK to store this in a png_size_t + * instead of an int, which is what fread() actually returns. + */ + check = (png_size_t)fread(data, (png_size_t)1, length, + (FILE *)png_ptr->io_ptr); + + if (check != length) + { + png_error(png_ptr, "Read Error"); + } +} + +static void +png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + + check = fwrite(data, 1, length, (FILE *)(png_ptr->io_ptr)); + if (check != length) + { + png_error(png_ptr, "Write Error"); + } +} + +static void +png_flush(png_structp png_ptr) +{ + FILE *io_ptr; + io_ptr = (FILE *)CVT_PTR((png_ptr->io_ptr)); + if (io_ptr != NULL) + fflush(io_ptr); +} + +#endif + +//----------------- +// end of source +//----------------- diff --git a/Engine/lib/lpng/contrib/visupng/PngFile.h b/Engine/lib/lpng/contrib/visupng/PngFile.h new file mode 100644 index 000000000..a900fd4fd --- /dev/null +++ b/Engine/lib/lpng/contrib/visupng/PngFile.h @@ -0,0 +1,27 @@ +//------------------------------------------ +// PNGFILE.H -- Header File for pngfile.c +//------------------------------------------ + +// Copyright 2000, Willem van Schaik. For conditions of distribution and +// use, see the copyright/license/disclaimer notice in png.h + +#include +#include +#include +#include + +void PngFileInitialize (HWND hwnd) ; +BOOL PngFileOpenDlg (HWND hwnd, PTSTR pstrFileName, PTSTR pstrTitleName) ; +BOOL PngFileSaveDlg (HWND hwnd, PTSTR pstrFileName, PTSTR pstrTitleName) ; + +BOOL PngLoadImage (PTSTR pstrFileName, png_byte **ppbImageData, + int *piWidth, int *piHeight, int *piChannels, png_color *pBkgColor); +BOOL PngSaveImage (PTSTR pstrFileName, png_byte *pDiData, + int iWidth, int iHeight, png_color BkgColor); + +#if defined(PNG_NO_STDIO) +static void png_read_data(png_structp png_ptr, png_bytep data, png_size_t length); +static void png_write_data(png_structp png_ptr, png_bytep data, png_size_t length); +static void png_flush(png_structp png_ptr); +#endif + diff --git a/Engine/lib/lpng/contrib/visupng/README.txt b/Engine/lib/lpng/contrib/visupng/README.txt new file mode 100644 index 000000000..4a753d8e2 --- /dev/null +++ b/Engine/lib/lpng/contrib/visupng/README.txt @@ -0,0 +1,58 @@ +Microsoft Developer Studio Build File, Format Version 6.00 for VisualPng +------------------------------------------------------------------------ + +Copyright 2000, Willem van Schaik. For conditions of distribution and +use, see the copyright/license/disclaimer notice in png.h + +As a PNG .dll demo VisualPng is finished. More features would only hinder +the program's objective. However, further extensions (like support for other +graphics formats) are in development. To get these, or for pre-compiled +binaries, go to "http://www.schaik.com/png/visualpng.html". + +------------------------------------------------------------------------ + +Assumes that + + libpng DLLs and LIBs are in ..\..\projects\msvc\win32\libpng + zlib DLLs and LIBs are in ..\..\projects\msvc\win32\zlib + libpng header files are in ..\..\..\libpng + zlib header files are in ..\..\..\zlib + the pngsuite images are in ..\pngsuite + +To build: + +1) On the main menu Select "Build|Set Active configuration". + Choose the configuration that corresponds to the library you want to test. + This library must have been built using the libpng MS project located in + the "..\..\mscv" subdirectory. + +2) Select "Build|Clean" + +3) Select "Build|Rebuild All" + +4) After compiling and linking VisualPng will be started to view an image + from the PngSuite directory. Press Ctrl-N (and Ctrl-V) for other images. + + +To install: + +When distributing VisualPng (or a further development) the following options +are available: + +1) Build the program with the configuration "Win32 LIB" and you only need to + include the executable from the ./lib directory in your distribution. + +2) Build the program with the configuration "Win32 DLL" and you need to put + in your distribution the executable from the ./dll directory and the dll's + libpng1.dll, zlib.dll and msvcrt.dll. These need to be in the user's PATH. + + +Willem van Schaik +Calgary, June 6th 2000 + +P.S. VisualPng was written based on preliminary work of: + + - Simon-Pierre Cadieux + - Glenn Randers-Pehrson + - Greg Roelofs + diff --git a/Engine/lib/lpng/contrib/visupng/VisualPng.c b/Engine/lib/lpng/contrib/visupng/VisualPng.c new file mode 100644 index 000000000..f2cf6ee9a --- /dev/null +++ b/Engine/lib/lpng/contrib/visupng/VisualPng.c @@ -0,0 +1,961 @@ +//------------------------------------ +// VisualPng.C -- Shows a PNG image +//------------------------------------ + +// Copyright 2000, Willem van Schaik. For conditions of distribution and +// use, see the copyright/license/disclaimer notice in png.h + +// switches + +// defines + +#define PROGNAME "VisualPng" +#define LONGNAME "Win32 Viewer for PNG-files" +#define VERSION "1.0 of 2000 June 07" + +// constants + +#define MARGIN 8 + +// standard includes + +#include +#include +#include +#include + +// application includes + +#include "png.h" +#include "pngfile.h" +#include "resource.h" + +// macros + +// function prototypes + +LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM); +BOOL CALLBACK AboutDlgProc (HWND, UINT, WPARAM, LPARAM) ; + +BOOL CenterAbout (HWND hwndChild, HWND hwndParent); + +BOOL BuildPngList (PTSTR pstrPathName, TCHAR **ppFileList, int *pFileCount, + int *pFileIndex); + +BOOL SearchPngList (TCHAR *pFileList, int FileCount, int *pFileIndex, + PTSTR pstrPrevName, PTSTR pstrNextName); + +BOOL LoadImageFile(HWND hwnd, PTSTR pstrPathName, + png_byte **ppbImage, int *pxImgSize, int *pyImgSize, int *piChannels, + png_color *pBkgColor); + +BOOL DisplayImage (HWND hwnd, BYTE **ppDib, + BYTE **ppDiData, int cxWinSize, int cyWinSize, + BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels, + BOOL bStretched); + +BOOL InitBitmap ( + BYTE *pDiData, int cxWinSize, int cyWinSize); + +BOOL FillBitmap ( + BYTE *pDiData, int cxWinSize, int cyWinSize, + BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels, + BOOL bStretched); + +// a few global variables + +static char *szProgName = PROGNAME; +static char *szAppName = LONGNAME; +static char *szIconName = PROGNAME; +static char szCmdFileName [MAX_PATH]; + +// MAIN routine + +int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, + PSTR szCmdLine, int iCmdShow) +{ + HACCEL hAccel; + HWND hwnd; + MSG msg; + WNDCLASS wndclass; + int ixBorders, iyBorders; + + wndclass.style = CS_HREDRAW | CS_VREDRAW; + wndclass.lpfnWndProc = WndProc; + wndclass.cbClsExtra = 0; + wndclass.cbWndExtra = 0; + wndclass.hInstance = hInstance; + wndclass.hIcon = LoadIcon (hInstance, szIconName) ; + wndclass.hCursor = LoadCursor (NULL, IDC_ARROW); + wndclass.hbrBackground = NULL; // (HBRUSH) GetStockObject (GRAY_BRUSH); + wndclass.lpszMenuName = szProgName; + wndclass.lpszClassName = szProgName; + + if (!RegisterClass (&wndclass)) + { + MessageBox (NULL, TEXT ("Error: this program requires Windows NT!"), + szProgName, MB_ICONERROR); + return 0; + } + + // if filename given on commandline, store it + if ((szCmdLine != NULL) && (*szCmdLine != '\0')) + if (szCmdLine[0] == '"') + strncpy (szCmdFileName, szCmdLine + 1, strlen(szCmdLine) - 2); + else + strcpy (szCmdFileName, szCmdLine); + else + strcpy (szCmdFileName, ""); + + // calculate size of window-borders + ixBorders = 2 * (GetSystemMetrics (SM_CXBORDER) + + GetSystemMetrics (SM_CXDLGFRAME)); + iyBorders = 2 * (GetSystemMetrics (SM_CYBORDER) + + GetSystemMetrics (SM_CYDLGFRAME)) + + GetSystemMetrics (SM_CYCAPTION) + + GetSystemMetrics (SM_CYMENUSIZE) + + 1; /* WvS: don't ask me why? */ + + hwnd = CreateWindow (szProgName, szAppName, + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, + 512 + 2 * MARGIN + ixBorders, 384 + 2 * MARGIN + iyBorders, +// CW_USEDEFAULT, CW_USEDEFAULT, + NULL, NULL, hInstance, NULL); + + ShowWindow (hwnd, iCmdShow); + UpdateWindow (hwnd); + + hAccel = LoadAccelerators (hInstance, szProgName); + + while (GetMessage (&msg, NULL, 0, 0)) + { + if (!TranslateAccelerator (hwnd, hAccel, &msg)) + { + TranslateMessage (&msg); + DispatchMessage (&msg); + } + } + return msg.wParam; +} + +LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, + LPARAM lParam) +{ + static HINSTANCE hInstance ; + static HDC hdc; + static PAINTSTRUCT ps; + static HMENU hMenu; + + static BITMAPFILEHEADER *pbmfh; + static BITMAPINFOHEADER *pbmih; + static BYTE *pbImage; + static int cxWinSize, cyWinSize; + static int cxImgSize, cyImgSize; + static int cImgChannels; + static png_color bkgColor = {127, 127, 127}; + + static BOOL bStretched = TRUE; + + static BYTE *pDib = NULL; + static BYTE *pDiData = NULL; + + static TCHAR szImgPathName [MAX_PATH]; + static TCHAR szTitleName [MAX_PATH]; + + static TCHAR *pPngFileList = NULL; + static int iPngFileCount; + static int iPngFileIndex; + + BOOL bOk; + + switch (message) + { + case WM_CREATE: + hInstance = ((LPCREATESTRUCT) lParam)->hInstance ; + PngFileInitialize (hwnd); + + strcpy (szImgPathName, ""); + + // in case we process file given on command-line + + if (szCmdFileName[0] != '\0') + { + strcpy (szImgPathName, szCmdFileName); + + // read the other png-files in the directory for later + // next/previous commands + + BuildPngList (szImgPathName, &pPngFileList, &iPngFileCount, + &iPngFileIndex); + + // load the image from file + + if (!LoadImageFile (hwnd, szImgPathName, + &pbImage, &cxImgSize, &cyImgSize, &cImgChannels, &bkgColor)) + return 0; + + // invalidate the client area for later update + + InvalidateRect (hwnd, NULL, TRUE); + + // display the PNG into the DIBitmap + + DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize, + pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched); + } + + return 0; + + case WM_SIZE: + cxWinSize = LOWORD (lParam); + cyWinSize = HIWORD (lParam); + + // invalidate the client area for later update + + InvalidateRect (hwnd, NULL, TRUE); + + // display the PNG into the DIBitmap + + DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize, + pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched); + + return 0; + + case WM_INITMENUPOPUP: + hMenu = GetMenu (hwnd); + + if (pbImage) + EnableMenuItem (hMenu, IDM_FILE_SAVE, MF_ENABLED); + else + EnableMenuItem (hMenu, IDM_FILE_SAVE, MF_GRAYED); + + return 0; + + case WM_COMMAND: + hMenu = GetMenu (hwnd); + + switch (LOWORD (wParam)) + { + case IDM_FILE_OPEN: + + // show the File Open dialog box + + if (!PngFileOpenDlg (hwnd, szImgPathName, szTitleName)) + return 0; + + // read the other png-files in the directory for later + // next/previous commands + + BuildPngList (szImgPathName, &pPngFileList, &iPngFileCount, + &iPngFileIndex); + + // load the image from file + + if (!LoadImageFile (hwnd, szImgPathName, + &pbImage, &cxImgSize, &cyImgSize, &cImgChannels, &bkgColor)) + return 0; + + // invalidate the client area for later update + + InvalidateRect (hwnd, NULL, TRUE); + + // display the PNG into the DIBitmap + + DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize, + pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched); + + return 0; + + case IDM_FILE_SAVE: + + // show the File Save dialog box + + if (!PngFileSaveDlg (hwnd, szImgPathName, szTitleName)) + return 0; + + // save the PNG to a disk file + + SetCursor (LoadCursor (NULL, IDC_WAIT)); + ShowCursor (TRUE); + + bOk = PngSaveImage (szImgPathName, pDiData, cxWinSize, cyWinSize, + bkgColor); + + ShowCursor (FALSE); + SetCursor (LoadCursor (NULL, IDC_ARROW)); + + if (!bOk) + MessageBox (hwnd, TEXT ("Error in saving the PNG image"), + szProgName, MB_ICONEXCLAMATION | MB_OK); + return 0; + + case IDM_FILE_NEXT: + + // read next entry in the directory + + if (SearchPngList (pPngFileList, iPngFileCount, &iPngFileIndex, + NULL, szImgPathName)) + { + if (strcmp (szImgPathName, "") == 0) + return 0; + + // load the image from file + + if (!LoadImageFile (hwnd, szImgPathName, &pbImage, + &cxImgSize, &cyImgSize, &cImgChannels, &bkgColor)) + return 0; + + // invalidate the client area for later update + + InvalidateRect (hwnd, NULL, TRUE); + + // display the PNG into the DIBitmap + + DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize, + pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched); + } + + return 0; + + case IDM_FILE_PREVIOUS: + + // read previous entry in the directory + + if (SearchPngList (pPngFileList, iPngFileCount, &iPngFileIndex, + szImgPathName, NULL)) + { + + if (strcmp (szImgPathName, "") == 0) + return 0; + + // load the image from file + + if (!LoadImageFile (hwnd, szImgPathName, &pbImage, &cxImgSize, + &cyImgSize, &cImgChannels, &bkgColor)) + return 0; + + // invalidate the client area for later update + + InvalidateRect (hwnd, NULL, TRUE); + + // display the PNG into the DIBitmap + + DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize, + pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched); + } + + return 0; + + case IDM_FILE_EXIT: + + // more cleanup needed... + + // free image buffer + + if (pDib != NULL) + { + free (pDib); + pDib = NULL; + } + + // free file-list + + if (pPngFileList != NULL) + { + free (pPngFileList); + pPngFileList = NULL; + } + + // let's go ... + + exit (0); + + return 0; + + case IDM_OPTIONS_STRETCH: + bStretched = !bStretched; + if (bStretched) + CheckMenuItem (hMenu, IDM_OPTIONS_STRETCH, MF_CHECKED); + else + CheckMenuItem (hMenu, IDM_OPTIONS_STRETCH, MF_UNCHECKED); + + // invalidate the client area for later update + + InvalidateRect (hwnd, NULL, TRUE); + + // display the PNG into the DIBitmap + + DisplayImage (hwnd, &pDib, &pDiData, cxWinSize, cyWinSize, + pbImage, cxImgSize, cyImgSize, cImgChannels, bStretched); + + return 0; + + case IDM_HELP_ABOUT: + DialogBox (hInstance, TEXT ("AboutBox"), hwnd, AboutDlgProc) ; + return 0; + + } // end switch + + break; + + case WM_PAINT: + hdc = BeginPaint (hwnd, &ps); + + if (pDib) + SetDIBitsToDevice (hdc, 0, 0, cxWinSize, cyWinSize, 0, 0, + 0, cyWinSize, pDiData, (BITMAPINFO *) pDib, DIB_RGB_COLORS); + + EndPaint (hwnd, &ps); + return 0; + + case WM_DESTROY: + if (pbmfh) + { + free (pbmfh); + pbmfh = NULL; + } + + PostQuitMessage (0); + return 0; + } + + return DefWindowProc (hwnd, message, wParam, lParam); +} + +BOOL CALLBACK AboutDlgProc (HWND hDlg, UINT message, + WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG : + ShowWindow (hDlg, SW_HIDE); + CenterAbout (hDlg, GetWindow (hDlg, GW_OWNER)); + ShowWindow (hDlg, SW_SHOW); + return TRUE ; + + case WM_COMMAND : + switch (LOWORD (wParam)) + { + case IDOK : + case IDCANCEL : + EndDialog (hDlg, 0) ; + return TRUE ; + } + break ; + } + return FALSE ; +} + +//--------------- +// CenterAbout +//--------------- + +BOOL CenterAbout (HWND hwndChild, HWND hwndParent) +{ + RECT rChild, rParent, rWorkArea; + int wChild, hChild, wParent, hParent; + int xNew, yNew; + BOOL bResult; + + // Get the Height and Width of the child window + GetWindowRect (hwndChild, &rChild); + wChild = rChild.right - rChild.left; + hChild = rChild.bottom - rChild.top; + + // Get the Height and Width of the parent window + GetWindowRect (hwndParent, &rParent); + wParent = rParent.right - rParent.left; + hParent = rParent.bottom - rParent.top; + + // Get the limits of the 'workarea' + bResult = SystemParametersInfo( + SPI_GETWORKAREA, // system parameter to query or set + sizeof(RECT), + &rWorkArea, + 0); + if (!bResult) { + rWorkArea.left = rWorkArea.top = 0; + rWorkArea.right = GetSystemMetrics(SM_CXSCREEN); + rWorkArea.bottom = GetSystemMetrics(SM_CYSCREEN); + } + + // Calculate new X position, then adjust for workarea + xNew = rParent.left + ((wParent - wChild) /2); + if (xNew < rWorkArea.left) { + xNew = rWorkArea.left; + } else if ((xNew+wChild) > rWorkArea.right) { + xNew = rWorkArea.right - wChild; + } + + // Calculate new Y position, then adjust for workarea + yNew = rParent.top + ((hParent - hChild) /2); + if (yNew < rWorkArea.top) { + yNew = rWorkArea.top; + } else if ((yNew+hChild) > rWorkArea.bottom) { + yNew = rWorkArea.bottom - hChild; + } + + // Set it, and return + return SetWindowPos (hwndChild, NULL, xNew, yNew, 0, 0, SWP_NOSIZE | + SWP_NOZORDER); +} + +//---------------- +// BuildPngList +//---------------- + +BOOL BuildPngList (PTSTR pstrPathName, TCHAR **ppFileList, int *pFileCount, + int *pFileIndex) +{ + static TCHAR szImgPathName [MAX_PATH]; + static TCHAR szImgFileName [MAX_PATH]; + static TCHAR szImgFindName [MAX_PATH]; + + WIN32_FIND_DATA finddata; + HANDLE hFind; + + static TCHAR szTmp [MAX_PATH]; + BOOL bOk; + int i, ii; + int j, jj; + + // free previous file-list + + if (*ppFileList != NULL) + { + free (*ppFileList); + *ppFileList = NULL; + } + + // extract foldername, filename and search-name + + strcpy (szImgPathName, pstrPathName); + strcpy (szImgFileName, strrchr (pstrPathName, '\\') + 1); + + strcpy (szImgFindName, szImgPathName); + *(strrchr (szImgFindName, '\\') + 1) = '\0'; + strcat (szImgFindName, "*.png"); + + // first cycle: count number of files in directory for memory allocation + + *pFileCount = 0; + + hFind = FindFirstFile(szImgFindName, &finddata); + bOk = (hFind != (HANDLE) -1); + + while (bOk) + { + *pFileCount += 1; + bOk = FindNextFile(hFind, &finddata); + } + FindClose(hFind); + + // allocation memory for file-list + + *ppFileList = (TCHAR *) malloc (*pFileCount * MAX_PATH); + + // second cycle: read directory and store filenames in file-list + + hFind = FindFirstFile(szImgFindName, &finddata); + bOk = (hFind != (HANDLE) -1); + + i = 0; + ii = 0; + while (bOk) + { + strcpy (*ppFileList + ii, szImgPathName); + strcpy (strrchr(*ppFileList + ii, '\\') + 1, finddata.cFileName); + + if (strcmp(pstrPathName, *ppFileList + ii) == 0) + *pFileIndex = i; + + ii += MAX_PATH; + i++; + + bOk = FindNextFile(hFind, &finddata); + } + FindClose(hFind); + + // finally we must sort the file-list + + for (i = 0; i < *pFileCount - 1; i++) + { + ii = i * MAX_PATH; + for (j = i+1; j < *pFileCount; j++) + { + jj = j * MAX_PATH; + if (strcmp (*ppFileList + ii, *ppFileList + jj) > 0) + { + strcpy (szTmp, *ppFileList + jj); + strcpy (*ppFileList + jj, *ppFileList + ii); + strcpy (*ppFileList + ii, szTmp); + + // check if this was the current image that we moved + + if (*pFileIndex == i) + *pFileIndex = j; + else + if (*pFileIndex == j) + *pFileIndex = i; + } + } + } + + return TRUE; +} + +//---------------- +// SearchPngList +//---------------- + +BOOL SearchPngList ( + TCHAR *pFileList, int FileCount, int *pFileIndex, + PTSTR pstrPrevName, PTSTR pstrNextName) +{ + if (FileCount > 0) + { + // get previous entry + + if (pstrPrevName != NULL) + { + if (*pFileIndex > 0) + *pFileIndex -= 1; + else + *pFileIndex = FileCount - 1; + + strcpy (pstrPrevName, pFileList + (*pFileIndex * MAX_PATH)); + } + + // get next entry + + if (pstrNextName != NULL) + { + if (*pFileIndex < FileCount - 1) + *pFileIndex += 1; + else + *pFileIndex = 0; + + strcpy (pstrNextName, pFileList + (*pFileIndex * MAX_PATH)); + } + + return TRUE; + } + else + { + return FALSE; + } +} + +//----------------- +// LoadImageFile +//----------------- + +BOOL LoadImageFile (HWND hwnd, PTSTR pstrPathName, + png_byte **ppbImage, int *pxImgSize, int *pyImgSize, + int *piChannels, png_color *pBkgColor) +{ + static TCHAR szTmp [MAX_PATH]; + + // if there's an existing PNG, free the memory + + if (*ppbImage) + { + free (*ppbImage); + *ppbImage = NULL; + } + + // Load the entire PNG into memory + + SetCursor (LoadCursor (NULL, IDC_WAIT)); + ShowCursor (TRUE); + + PngLoadImage (pstrPathName, ppbImage, pxImgSize, pyImgSize, piChannels, + pBkgColor); + + ShowCursor (FALSE); + SetCursor (LoadCursor (NULL, IDC_ARROW)); + + if (*ppbImage != NULL) + { + sprintf (szTmp, "VisualPng - %s", strrchr(pstrPathName, '\\') + 1); + SetWindowText (hwnd, szTmp); + } + else + { + MessageBox (hwnd, TEXT ("Error in loading the PNG image"), + szProgName, MB_ICONEXCLAMATION | MB_OK); + return FALSE; + } + + return TRUE; +} + +//---------------- +// DisplayImage +//---------------- + +BOOL DisplayImage (HWND hwnd, BYTE **ppDib, + BYTE **ppDiData, int cxWinSize, int cyWinSize, + BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels, + BOOL bStretched) +{ + BYTE *pDib = *ppDib; + BYTE *pDiData = *ppDiData; + // BITMAPFILEHEADER *pbmfh; + BITMAPINFOHEADER *pbmih; + WORD wDIRowBytes; + png_color bkgBlack = {0, 0, 0}; + png_color bkgGray = {127, 127, 127}; + png_color bkgWhite = {255, 255, 255}; + + // allocate memory for the Device Independant bitmap + + wDIRowBytes = (WORD) ((3 * cxWinSize + 3L) >> 2) << 2; + + if (pDib) + { + free (pDib); + pDib = NULL; + } + + if (!(pDib = (BYTE *) malloc (sizeof(BITMAPINFOHEADER) + + wDIRowBytes * cyWinSize))) + { + MessageBox (hwnd, TEXT ("Error in displaying the PNG image"), + szProgName, MB_ICONEXCLAMATION | MB_OK); + *ppDib = pDib = NULL; + return FALSE; + } + *ppDib = pDib; + memset (pDib, 0, sizeof(BITMAPINFOHEADER)); + + // initialize the dib-structure + + pbmih = (BITMAPINFOHEADER *) pDib; + pbmih->biSize = sizeof(BITMAPINFOHEADER); + pbmih->biWidth = cxWinSize; + pbmih->biHeight = -((long) cyWinSize); + pbmih->biPlanes = 1; + pbmih->biBitCount = 24; + pbmih->biCompression = 0; + pDiData = pDib + sizeof(BITMAPINFOHEADER); + *ppDiData = pDiData; + + // first fill bitmap with gray and image border + + InitBitmap (pDiData, cxWinSize, cyWinSize); + + // then fill bitmap with image + + if (pbImage) + { + FillBitmap ( + pDiData, cxWinSize, cyWinSize, + pbImage, cxImgSize, cyImgSize, cImgChannels, + bStretched); + } + + return TRUE; +} + +//-------------- +// InitBitmap +//-------------- + +BOOL InitBitmap (BYTE *pDiData, int cxWinSize, int cyWinSize) +{ + BYTE *dst; + int x, y, col; + + // initialize the background with gray + + dst = pDiData; + for (y = 0; y < cyWinSize; y++) + { + col = 0; + for (x = 0; x < cxWinSize; x++) + { + // fill with GRAY + *dst++ = 127; + *dst++ = 127; + *dst++ = 127; + col += 3; + } + // rows start on 4 byte boundaries + while ((col % 4) != 0) + { + dst++; + col++; + } + } + + return TRUE; +} + +//-------------- +// FillBitmap +//-------------- + +BOOL FillBitmap ( + BYTE *pDiData, int cxWinSize, int cyWinSize, + BYTE *pbImage, int cxImgSize, int cyImgSize, int cImgChannels, + BOOL bStretched) +{ + BYTE *pStretchedImage; + BYTE *pImg; + BYTE *src, *dst; + BYTE r, g, b, a; + const int cDIChannels = 3; + WORD wImgRowBytes; + WORD wDIRowBytes; + int cxNewSize, cyNewSize; + int cxImgPos, cyImgPos; + int xImg, yImg; + int xWin, yWin; + int xOld, yOld; + int xNew, yNew; + + if (bStretched) + { + cxNewSize = cxWinSize - 2 * MARGIN; + cyNewSize = cyWinSize - 2 * MARGIN; + + // stretch the image to it's window determined size + + // the following two are the same, but the first has side-effects + // because of rounding +// if ((cyNewSize / cxNewSize) > (cyImgSize / cxImgSize)) + if ((cyNewSize * cxImgSize) > (cyImgSize * cxNewSize)) + { + cyNewSize = cxNewSize * cyImgSize / cxImgSize; + cxImgPos = MARGIN; + cyImgPos = (cyWinSize - cyNewSize) / 2; + } + else + { + cxNewSize = cyNewSize * cxImgSize / cyImgSize; + cyImgPos = MARGIN; + cxImgPos = (cxWinSize - cxNewSize) / 2; + } + + pStretchedImage = malloc (cImgChannels * cxNewSize * cyNewSize); + pImg = pStretchedImage; + + for (yNew = 0; yNew < cyNewSize; yNew++) + { + yOld = yNew * cyImgSize / cyNewSize; + for (xNew = 0; xNew < cxNewSize; xNew++) + { + xOld = xNew * cxImgSize / cxNewSize; + + r = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 0); + g = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 1); + b = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + 2); + *pImg++ = r; + *pImg++ = g; + *pImg++ = b; + if (cImgChannels == 4) + { + a = *(pbImage + cImgChannels * ((yOld * cxImgSize) + xOld) + + 3); + *pImg++ = a; + } + } + } + + // calculate row-bytes + + wImgRowBytes = cImgChannels * cxNewSize; + wDIRowBytes = (WORD) ((cDIChannels * cxWinSize + 3L) >> 2) << 2; + + // copy image to screen + + for (yImg = 0, yWin = cyImgPos; yImg < cyNewSize; yImg++, yWin++) + { + if (yWin >= cyWinSize - cyImgPos) + break; + src = pStretchedImage + yImg * wImgRowBytes; + dst = pDiData + yWin * wDIRowBytes + cxImgPos * cDIChannels; + + for (xImg = 0, xWin = cxImgPos; xImg < cxNewSize; xImg++, xWin++) + { + if (xWin >= cxWinSize - cxImgPos) + break; + r = *src++; + g = *src++; + b = *src++; + *dst++ = b; /* note the reverse order */ + *dst++ = g; + *dst++ = r; + if (cImgChannels == 4) + { + a = *src++; + } + } + } + + // free memory + + if (pStretchedImage != NULL) + { + free (pStretchedImage); + pStretchedImage = NULL; + } + + } + + // process the image not-stretched + + else + { + // calculate the central position + + cxImgPos = (cxWinSize - cxImgSize) / 2; + cyImgPos = (cyWinSize - cyImgSize) / 2; + + // check for image larger than window + + if (cxImgPos < MARGIN) + cxImgPos = MARGIN; + if (cyImgPos < MARGIN) + cyImgPos = MARGIN; + + // calculate both row-bytes + + wImgRowBytes = cImgChannels * cxImgSize; + wDIRowBytes = (WORD) ((cDIChannels * cxWinSize + 3L) >> 2) << 2; + + // copy image to screen + + for (yImg = 0, yWin = cyImgPos; yImg < cyImgSize; yImg++, yWin++) + { + if (yWin >= cyWinSize - MARGIN) + break; + src = pbImage + yImg * wImgRowBytes; + dst = pDiData + yWin * wDIRowBytes + cxImgPos * cDIChannels; + + for (xImg = 0, xWin = cxImgPos; xImg < cxImgSize; xImg++, xWin++) + { + if (xWin >= cxWinSize - MARGIN) + break; + r = *src++; + g = *src++; + b = *src++; + *dst++ = b; /* note the reverse order */ + *dst++ = g; + *dst++ = r; + if (cImgChannels == 4) + { + a = *src++; + } + } + } + } + + return TRUE; +} + +//----------------- +// end of source +//----------------- diff --git a/Engine/lib/lpng/contrib/visupng/VisualPng.dsp b/Engine/lib/lpng/contrib/visupng/VisualPng.dsp new file mode 100644 index 000000000..c13c5a723 --- /dev/null +++ b/Engine/lib/lpng/contrib/visupng/VisualPng.dsp @@ -0,0 +1,147 @@ +# Microsoft Developer Studio Project File - Name="VisualPng" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=VisualPng - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "VisualPng.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "VisualPng.mak" CFG="VisualPng - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "VisualPng - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "VisualPng - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "VisualPng - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /O2 /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "NDEBUG" /D "PNG_NO_STDIO" /D "PNG_NO_GLOBAL_ARRAYS" /FD /c +# SUBTRACT BASE CPP /YX +# ADD CPP /nologo /MD /W3 /O2 /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "NDEBUG" /D "PNG_NO_STDIO" /D "PNG_NO_GLOBAL_ARRAYS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 ..\..\projects\visualc6\Win32_LIB_Release\libpng.lib ..\..\..\zlib\projects\visualc6\Win32_LIB_Release\zlib.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# Begin Special Build Tool +OutDir=.\Release +SOURCE="$(InputPath)" +PostBuild_Cmds=$(outdir)\VisualPng.exe ..\..\contrib\pngsuite\basn6a16.png +# End Special Build Tool + +!ELSEIF "$(CFG)" == "VisualPng - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /ZI /Od /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "_DEBUG" /D "PNG_NO_STDIO" /D "PNG_NO_GLOBAL_ARRAYS" /FD /GZ /c +# SUBTRACT BASE CPP /YX +# ADD CPP /nologo /MDd /W3 /Gm /ZI /Od /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "_DEBUG" /D "PNG_NO_STDIO" /D "PNG_NO_GLOBAL_ARRAYS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 ..\..\projects\visualc6\Win32_LIB_Release\libpng.lib ..\..\..\zlib\projects\visualc6\Win32_LIB_Release\zlib.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /nodefaultlib:"msvcrt.lib" /pdbtype:sept +# Begin Special Build Tool +OutDir=.\Debug +SOURCE="$(InputPath)" +PostBuild_Cmds=$(outdir)\VisualPng.exe ..\..\contrib\pngsuite\basn6a16.png +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "VisualPng - Win32 Release" +# Name "VisualPng - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\PngFile.c +# End Source File +# Begin Source File + +SOURCE=.\VisualPng.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\cexcept.h +# End Source File +# Begin Source File + +SOURCE=.\PngFile.h +# End Source File +# Begin Source File + +SOURCE=.\resource.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\VisualPng.ico +# End Source File +# Begin Source File + +SOURCE=.\VisualPng.rc +# End Source File +# End Group +# End Target +# End Project diff --git a/Engine/lib/lpng/contrib/visupng/VisualPng.dsw b/Engine/lib/lpng/contrib/visupng/VisualPng.dsw new file mode 100644 index 000000000..17ad83ad3 --- /dev/null +++ b/Engine/lib/lpng/contrib/visupng/VisualPng.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "VisualPng"=.\VisualPng.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/Engine/lib/lpng/contrib/visupng/VisualPng.ico b/Engine/lib/lpng/contrib/visupng/VisualPng.ico new file mode 100644 index 000000000..68aa3719f Binary files /dev/null and b/Engine/lib/lpng/contrib/visupng/VisualPng.ico differ diff --git a/Engine/lib/lpng/contrib/visupng/VisualPng.png b/Engine/lib/lpng/contrib/visupng/VisualPng.png new file mode 100644 index 000000000..c6aa80a9b Binary files /dev/null and b/Engine/lib/lpng/contrib/visupng/VisualPng.png differ diff --git a/Engine/lib/lpng/contrib/visupng/VisualPng.rc b/Engine/lib/lpng/contrib/visupng/VisualPng.rc new file mode 100644 index 000000000..151c68c47 --- /dev/null +++ b/Engine/lib/lpng/contrib/visupng/VisualPng.rc @@ -0,0 +1,152 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +VISUALPNG MENU DISCARDABLE +BEGIN + POPUP "&File" + BEGIN + MENUITEM "&Open Image...\tCtrl+O", IDM_FILE_OPEN + MENUITEM "Save &As...", IDM_FILE_SAVE + MENUITEM SEPARATOR + MENUITEM "&Next Image\tCtrl+N", IDM_FILE_NEXT + MENUITEM "Pre&vious Image\tCtrl+V", IDM_FILE_PREVIOUS + MENUITEM SEPARATOR + MENUITEM "E&xit\tAlt+X", IDM_FILE_EXIT + END + POPUP "&Options" + BEGIN + MENUITEM "&Stretch", IDM_OPTIONS_STRETCH, CHECKED + END + POPUP "&Help" + BEGIN + MENUITEM "&About", IDM_HELP_ABOUT + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +VISUALPNG ACCELERATORS DISCARDABLE +BEGIN + "N", IDM_FILE_NEXT, VIRTKEY, CONTROL, NOINVERT + "O", IDM_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT + "P", IDM_FILE_PREVIOUS, VIRTKEY, CONTROL, NOINVERT + "V", IDM_FILE_PREVIOUS, VIRTKEY, CONTROL, NOINVERT + "X", IDM_FILE_EXIT, VIRTKEY, ALT, NOINVERT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +VISUALPNG ICON DISCARDABLE "VisualPng.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +ABOUTBOX DIALOG DISCARDABLE 0, 0, 186, 94 +STYLE DS_MODALFRAME | WS_POPUP +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,68,67,50,14 + CTEXT "VisualPng 1.0 - June 2000",IDC_STATIC,49,14,88,8 + LTEXT "a PNG image viewer",IDC_STATIC,60,30,66,8 + LTEXT "(c) Willem van Schaik, 2000",IDC_STATIC,48,52,90,8 + LTEXT "to demonstrate the use of libpng in Visual C", + IDC_STATIC,25,38,136,8 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + "ABOUTBOX", DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 87 + END +END +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Engine/lib/lpng/contrib/visupng/cexcept.h b/Engine/lib/lpng/contrib/visupng/cexcept.h new file mode 100644 index 000000000..dbea51ebe --- /dev/null +++ b/Engine/lib/lpng/contrib/visupng/cexcept.h @@ -0,0 +1,243 @@ +/*=== +cexcept.h 2.0.0 (2001-Jul-12-Thu) +Adam M. Costello + +An interface for exception-handling in ANSI C (C89 and subsequent ISO +standards), developed jointly with Cosmin Truta . + + Copyright (c) 2001 Adam M. Costello and Cosmin Truta. Everyone + is hereby granted permission to do whatever they like with this + file, provided that if they modify it they take reasonable steps to + avoid confusing or misleading people about the authors, version, + and terms of use of the derived file. The copyright holders make + no guarantees regarding this file, and are not responsible for any + damage resulting from its use. + +Only user-defined exceptions are supported, not "real" exceptions like +division by zero or memory segmentation violations. + +If this interface is used by multiple .c files, they shouldn't include +this header file directly. Instead, create a wrapper header file that +includes this header file and then invokes the define_exception_type +macro (see below), and let your .c files include that header file. + +The interface consists of one type, one well-known name, and six macros. + + +define_exception_type(type_name); + + This macro is used like an external declaration. It specifies + the type of object that gets copied from the exception thrower to + the exception catcher. The type_name can be any type that can be + assigned to, that is, a non-constant arithmetic type, struct, union, + or pointer. Examples: + + define_exception_type(int); + + enum exception { out_of_memory, bad_arguments, disk_full }; + define_exception_type(enum exception); + + struct exception { int code; const char *msg; }; + define_exception_type(struct exception); + + Because throwing an exception causes the object to be copied (not + just once, but twice), programmers may wish to consider size when + choosing the exception type. + + +struct exception_context; + + This type may be used after the define_exception_type() macro has + been invoked. A struct exception_context must be known to both + the thrower and the catcher. It is expected that there be one + context for each thread that uses exceptions. It would certainly + be dangerous for multiple threads to access the same context. + One thread can use multiple contexts, but that is likely to be + confusing and not typically useful. The application can allocate + this structure in any way it pleases--automatic, static, or dynamic. + The application programmer should pretend not to know the structure + members, which are subject to change. + + +struct exception_context *the_exception_context; + + The Try/Catch and Throw statements (described below) implicitly + refer to a context, using the name the_exception_context. It is + the application's responsibility to make sure that this name yields + the address of a mutable (non-constant) struct exception_context + wherever those statements are used. Subject to that constraint, the + application may declare a variable of this name anywhere it likes + (inside a function, in a parameter list, or externally), and may + use whatever storage class specifiers (static, extern, etc) or type + qualifiers (const, volatile, etc) it likes. Examples: + + static struct exception_context + * const the_exception_context = &foo; + + { struct exception_context *the_exception_context = bar; ... } + + int blah(struct exception_context *the_exception_context, ...); + + extern struct exception_context the_exception_context[1]; + + The last example illustrates a trick that avoids creating a pointer + object separate from the structure object. + + The name could even be a macro, for example: + + struct exception_context ec_array[numthreads]; + #define the_exception_context (ec_array + thread_id) + + Be aware that the_exception_context is used several times by the + Try/Catch/Throw macros, so it shouldn't be expensive or have side + effects. The expansion must be a drop-in replacement for an + identifier, so it's safest to put parentheses around it. + + +void init_exception_context(struct exception_context *ec); + + For context structures allocated statically (by an external + definition or using the "static" keyword), the implicit + initialization to all zeros is sufficient, but contexts allocated + by other means must be initialized using this macro before they + are used by a Try/Catch statement. It does no harm to initialize + a context more than once (by using this macro on a statically + allocated context, or using this macro twice on the same context), + but a context must not be re-initialized after it has been used by a + Try/Catch statement. + + +Try statement +Catch (expression) statement + + The Try/Catch/Throw macros are capitalized in order to avoid + confusion with the C++ keywords, which have subtly different + semantics. + + A Try/Catch statement has a syntax similar to an if/else statement, + except that the parenthesized expression goes after the second + keyword rather than the first. As with if/else, there are two + clauses, each of which may be a simple statement ending with a + semicolon or a brace-enclosed compound statement. But whereas + the else clause is optional, the Catch clause is required. The + expression must be a modifiable lvalue (something capable of being + assigned to) of the same type (disregarding type qualifiers) that + was passed to define_exception_type(). + + If a Throw that uses the same exception context as the Try/Catch is + executed within the Try clause (typically within a function called + by the Try clause), and the exception is not caught by a nested + Try/Catch statement, then a copy of the exception will be assigned + to the expression, and control will jump to the Catch clause. If no + such Throw is executed, then the assignment is not performed, and + the Catch clause is not executed. + + The expression is not evaluated unless and until the exception is + caught, which is significant if it has side effects, for example: + + Try foo(); + Catch (p[++i].e) { ... } + + IMPORTANT: Jumping into or out of a Try clause (for example via + return, break, continue, goto, longjmp) is forbidden--the compiler + will not complain, but bad things will happen at run-time. Jumping + into or out of a Catch clause is okay, and so is jumping around + inside a Try clause. In many cases where one is tempted to return + from a Try clause, it will suffice to use Throw, and then return + from the Catch clause. Another option is to set a flag variable and + use goto to jump to the end of the Try clause, then check the flag + after the Try/Catch statement. + + IMPORTANT: The values of any non-volatile automatic variables + changed within the Try clause are undefined after an exception is + caught. Therefore, variables modified inside the Try block whose + values are needed later outside the Try block must either use static + storage or be declared with the "volatile" type qualifier. + + +Throw expression; + + A Throw statement is very much like a return statement, except that + the expression is required. Whereas return jumps back to the place + where the current function was called, Throw jumps back to the Catch + clause of the innermost enclosing Try clause. The expression must + be compatible with the type passed to define_exception_type(). The + exception must be caught, otherwise the program may crash. + + Slight limitation: If the expression is a comma-expression it must + be enclosed in parentheses. + + +Try statement +Catch_anonymous statement + + When the value of the exception is not needed, a Try/Catch statement + can use Catch_anonymous instead of Catch (expression). + + +Everything below this point is for the benefit of the compiler. The +application programmer should pretend not to know any of it, because it +is subject to change. + +===*/ + + +#ifndef CEXCEPT_H +#define CEXCEPT_H + + +#include + +#define define_exception_type(etype) \ +struct exception_context { \ + jmp_buf *penv; \ + int caught; \ + volatile struct { etype etmp; } v; \ +} + +/* etmp must be volatile because the application might use automatic */ +/* storage for the_exception_context, and etmp is modified between */ +/* the calls to setjmp() and longjmp(). A wrapper struct is used to */ +/* avoid warnings about a duplicate volatile qualifier in case etype */ +/* already includes it. */ + +#define init_exception_context(ec) ((void)((ec)->penv = 0)) + +#define Try \ + { \ + jmp_buf *exception__prev, exception__env; \ + exception__prev = the_exception_context->penv; \ + the_exception_context->penv = &exception__env; \ + if (setjmp(exception__env) == 0) { \ + if (&exception__prev) + +#define exception__catch(action) \ + else { } \ + the_exception_context->caught = 0; \ + } \ + else { \ + the_exception_context->caught = 1; \ + } \ + the_exception_context->penv = exception__prev; \ + } \ + if (!the_exception_context->caught || action) { } \ + else + +#define Catch(e) exception__catch(((e) = the_exception_context->v.etmp, 0)) +#define Catch_anonymous exception__catch(0) + +/* Try ends with if(), and Catch begins and ends with else. This */ +/* ensures that the Try/Catch syntax is really the same as the */ +/* if/else syntax. */ +/* */ +/* We use &exception__prev instead of 1 to appease compilers that */ +/* warn about constant expressions inside if(). Most compilers */ +/* should still recognize that &exception__prev is never zero and */ +/* avoid generating test code. */ + +#define Throw \ + for (;; longjmp(*the_exception_context->penv, 1)) \ + the_exception_context->v.etmp = + + +#endif /* CEXCEPT_H */ diff --git a/Engine/lib/lpng/contrib/visupng/resource.h b/Engine/lib/lpng/contrib/visupng/resource.h new file mode 100644 index 000000000..611dd035f --- /dev/null +++ b/Engine/lib/lpng/contrib/visupng/resource.h @@ -0,0 +1,23 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by VisualPng.rc +// +#define IDM_FILE_OPEN 40001 +#define IDM_FILE_SAVE 40002 +#define IDM_FILE_NEXT 40003 +#define IDM_FILE_PREVIOUS 40004 +#define IDM_FILE_EXIT 40005 +#define IDM_OPTIONS_BACKGROUND 40006 +#define IDM_OPTIONS_STRETCH 40007 +#define IDM_HELP_ABOUT 40008 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 113 +#define _APS_NEXT_COMMAND_VALUE 40009 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Engine/lib/lpng/libpng-1.2.24.txt b/Engine/lib/lpng/libpng-1.2.24.txt new file mode 100644 index 000000000..5b8dc53b9 --- /dev/null +++ b/Engine/lib/lpng/libpng-1.2.24.txt @@ -0,0 +1,2851 @@ +libpng.txt - A description on how to use and modify libpng + + libpng version 1.2.24 - December 14, 2007 + Updated and distributed by Glenn Randers-Pehrson + + Copyright (c) 1998-2007 Glenn Randers-Pehrson + For conditions of distribution and use, see copyright + notice in png.h. + + based on: + + libpng 1.0 beta 6 version 0.96 May 28, 1997 + Updated and distributed by Andreas Dilger + Copyright (c) 1996, 1997 Andreas Dilger + + libpng 1.0 beta 2 - version 0.88 January 26, 1996 + For conditions of distribution and use, see copyright + notice in png.h. Copyright (c) 1995, 1996 Guy Eric + Schalnat, Group 42, Inc. + + Updated/rewritten per request in the libpng FAQ + Copyright (c) 1995, 1996 Frank J. T. Wojcik + December 18, 1995 & January 20, 1996 + +I. Introduction + +This file describes how to use and modify the PNG reference library +(known as libpng) for your own use. There are five sections to this +file: introduction, structures, reading, writing, and modification and +configuration notes for various special platforms. In addition to this +file, example.c is a good starting point for using the library, as +it is heavily commented and should include everything most people +will need. We assume that libpng is already installed; see the +INSTALL file for instructions on how to install libpng. + +For examples of libpng usage, see the files "example.c", "pngtest.c", +and the files in the "contrib" directory, all of which are included in the +libpng distribution. + +Libpng was written as a companion to the PNG specification, as a way +of reducing the amount of time and effort it takes to support the PNG +file format in application programs. + +The PNG specification (second edition), November 2003, is available as +a W3C Recommendation and as an ISO Standard (ISO/IEC 15948:2003 (E)) at + + +The PNG-1.0 specification is available +as RFC 2083 and as a +W3C Recommendation . Some +additional chunks are described in the special-purpose public chunks +documents at . + +Other information +about PNG, and the latest version of libpng, can be found at the PNG home +page, . + +Most users will not have to modify the library significantly; advanced +users may want to modify it more. All attempts were made to make it as +complete as possible, while keeping the code easy to understand. +Currently, this library only supports C. Support for other languages +is being considered. + +Libpng has been designed to handle multiple sessions at one time, +to be easily modifiable, to be portable to the vast majority of +machines (ANSI, K&R, 16-, 32-, and 64-bit) available, and to be easy +to use. The ultimate goal of libpng is to promote the acceptance of +the PNG file format in whatever way possible. While there is still +work to be done (see the TODO file), libpng should cover the +majority of the needs of its users. + +Libpng uses zlib for its compression and decompression of PNG files. +Further information about zlib, and the latest version of zlib, can +be found at the zlib home page, . +The zlib compression utility is a general purpose utility that is +useful for more than PNG files, and can be used without libpng. +See the documentation delivered with zlib for more details. +You can usually find the source files for the zlib utility wherever you +find the libpng source files. + +Libpng is thread safe, provided the threads are using different +instances of the structures. Each thread should have its own +png_struct and png_info instances, and thus its own image. +Libpng does not protect itself against two threads using the +same instance of a structure. + +II. Structures + +There are two main structures that are important to libpng, png_struct +and png_info. The first, png_struct, is an internal structure that +will not, for the most part, be used by a user except as the first +variable passed to every libpng function call. + +The png_info structure is designed to provide information about the +PNG file. At one time, the fields of png_info were intended to be +directly accessible to the user. However, this tended to cause problems +with applications using dynamically loaded libraries, and as a result +a set of interface functions for png_info (the png_get_*() and png_set_*() +functions) was developed. The fields of png_info are still available for +older applications, but it is suggested that applications use the new +interfaces if at all possible. + +Applications that do make direct access to the members of png_struct (except +for png_ptr->jmpbuf) must be recompiled whenever the library is updated, +and applications that make direct access to the members of png_info must +be recompiled if they were compiled or loaded with libpng version 1.0.6, +in which the members were in a different order. In version 1.0.7, the +members of the png_info structure reverted to the old order, as they were +in versions 0.97c through 1.0.5. Starting with version 2.0.0, both +structures are going to be hidden, and the contents of the structures will +only be accessible through the png_get/png_set functions. + +The png.h header file is an invaluable reference for programming with libpng. +And while I'm on the topic, make sure you include the libpng header file: + +#include + +III. Reading + +We'll now walk you through the possible functions to call when reading +in a PNG file sequentially, briefly explaining the syntax and purpose +of each one. See example.c and png.h for more detail. While +progressive reading is covered in the next section, you will still +need some of the functions discussed in this section to read a PNG +file. + +Setup + +You will want to do the I/O initialization(*) before you get into libpng, +so if it doesn't work, you don't have much to undo. Of course, you +will also want to insure that you are, in fact, dealing with a PNG +file. Libpng provides a simple check to see if a file is a PNG file. +To use it, pass in the first 1 to 8 bytes of the file to the function +png_sig_cmp(), and it will return 0 if the bytes match the corresponding +bytes of the PNG signature, or nonzero otherwise. Of course, the more bytes +you pass in, the greater the accuracy of the prediction. + +If you are intending to keep the file pointer open for use in libpng, +you must ensure you don't read more than 8 bytes from the beginning +of the file, and you also have to make a call to png_set_sig_bytes_read() +with the number of bytes you read from the beginning. Libpng will +then only check the bytes (if any) that your program didn't read. + +(*): If you are not using the standard I/O functions, you will need +to replace them with custom functions. See the discussion under +Customizing libpng. + + + FILE *fp = fopen(file_name, "rb"); + if (!fp) + { + return (ERROR); + } + fread(header, 1, number, fp); + is_png = !png_sig_cmp(header, 0, number); + if (!is_png) + { + return (NOT_PNG); + } + + +Next, png_struct and png_info need to be allocated and initialized. In +order to ensure that the size of these structures is correct even with a +dynamically linked libpng, there are functions to initialize and +allocate the structures. We also pass the library version, optional +pointers to error handling functions, and a pointer to a data struct for +use by the error functions, if necessary (the pointer and functions can +be NULL if the default error handlers are to be used). See the section +on Changes to Libpng below regarding the old initialization functions. +The structure allocation functions quietly return NULL if they fail to +create the structure, so your application should check for that. + + png_structp png_ptr = png_create_read_struct + (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, + user_error_fn, user_warning_fn); + if (!png_ptr) + return (ERROR); + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_read_struct(&png_ptr, + (png_infopp)NULL, (png_infopp)NULL); + return (ERROR); + } + + png_infop end_info = png_create_info_struct(png_ptr); + if (!end_info) + { + png_destroy_read_struct(&png_ptr, &info_ptr, + (png_infopp)NULL); + return (ERROR); + } + +If you want to use your own memory allocation routines, +define PNG_USER_MEM_SUPPORTED and use +png_create_read_struct_2() instead of png_create_read_struct(): + + png_structp png_ptr = png_create_read_struct_2 + (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, + user_error_fn, user_warning_fn, (png_voidp) + user_mem_ptr, user_malloc_fn, user_free_fn); + +The error handling routines passed to png_create_read_struct() +and the memory alloc/free routines passed to png_create_struct_2() +are only necessary if you are not using the libpng supplied error +handling and memory alloc/free functions. + +When libpng encounters an error, it expects to longjmp back +to your routine. Therefore, you will need to call setjmp and pass +your png_jmpbuf(png_ptr). If you read the file from different +routines, you will need to update the jmpbuf field every time you enter +a new routine that will call a png_*() function. + +See your documentation of setjmp/longjmp for your compiler for more +information on setjmp/longjmp. See the discussion on libpng error +handling in the Customizing Libpng section below for more information +on the libpng error handling. If an error occurs, and libpng longjmp's +back to your setjmp, you will want to call png_destroy_read_struct() to +free any memory. + + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr, &info_ptr, + &end_info); + fclose(fp); + return (ERROR); + } + +If you would rather avoid the complexity of setjmp/longjmp issues, +you can compile libpng with PNG_SETJMP_NOT_SUPPORTED, in which case +errors will result in a call to PNG_ABORT() which defaults to abort(). + +Now you need to set up the input code. The default for libpng is to +use the C function fread(). If you use this, you will need to pass a +valid FILE * in the function png_init_io(). Be sure that the file is +opened in binary mode. If you wish to handle reading data in another +way, you need not call the png_init_io() function, but you must then +implement the libpng I/O methods discussed in the Customizing Libpng +section below. + + png_init_io(png_ptr, fp); + +If you had previously opened the file and read any of the signature from +the beginning in order to see if this was a PNG file, you need to let +libpng know that there are some bytes missing from the start of the file. + + png_set_sig_bytes(png_ptr, number); + +Setting up callback code + +You can set up a callback function to handle any unknown chunks in the +input stream. You must supply the function + + read_chunk_callback(png_ptr ptr, + png_unknown_chunkp chunk); + { + /* The unknown chunk structure contains your + chunk data: */ + png_byte name[5]; + png_byte *data; + png_size_t size; + /* Note that libpng has already taken care of + the CRC handling */ + + /* put your code here. Return one of the + following: */ + + return (-n); /* chunk had an error */ + return (0); /* did not recognize */ + return (n); /* success */ + } + +(You can give your function another name that you like instead of +"read_chunk_callback") + +To inform libpng about your function, use + + png_set_read_user_chunk_fn(png_ptr, user_chunk_ptr, + read_chunk_callback); + +This names not only the callback function, but also a user pointer that +you can retrieve with + + png_get_user_chunk_ptr(png_ptr); + +At this point, you can set up a callback function that will be +called after each row has been read, which you can use to control +a progress meter or the like. It's demonstrated in pngtest.c. +You must supply a function + + void read_row_callback(png_ptr ptr, png_uint_32 row, + int pass); + { + /* put your code here */ + } + +(You can give it another name that you like instead of "read_row_callback") + +To inform libpng about your function, use + + png_set_read_status_fn(png_ptr, read_row_callback); + +Width and height limits + +The PNG specification allows the width and height of an image to be as +large as 2^31-1 (0x7fffffff), or about 2.147 billion rows and columns. +Since very few applications really need to process such large images, +we have imposed an arbitrary 1-million limit on rows and columns. +Larger images will be rejected immediately with a png_error() call. If +you wish to override this limit, you can use + + png_set_user_limits(png_ptr, width_max, height_max); + +to set your own limits, or use width_max = height_max = 0x7fffffffL +to allow all valid dimensions (libpng may reject some very large images +anyway because of potential buffer overflow conditions). + +You should put this statement after you create the PNG structure and +before calling png_read_info(), png_read_png(), or png_process_data(). +If you need to retrieve the limits that are being applied, use + + width_max = png_get_user_width_max(png_ptr); + height_max = png_get_user_height_max(png_ptr); + +Unknown-chunk handling + +Now you get to set the way the library processes unknown chunks in the +input PNG stream. Both known and unknown chunks will be read. Normal +behavior is that known chunks will be parsed into information in +various info_ptr members; unknown chunks will be discarded. To change +this, you can call: + + png_set_keep_unknown_chunks(png_ptr, keep, + chunk_list, num_chunks); + keep - 0: do not handle as unknown + 1: do not keep + 2: keep only if safe-to-copy + 3: keep even if unsafe-to-copy + You can use these definitions: + PNG_HANDLE_CHUNK_AS_DEFAULT 0 + PNG_HANDLE_CHUNK_NEVER 1 + PNG_HANDLE_CHUNK_IF_SAFE 2 + PNG_HANDLE_CHUNK_ALWAYS 3 + chunk_list - list of chunks affected (a byte string, + five bytes per chunk, NULL or '\0' if + num_chunks is 0) + num_chunks - number of chunks affected; if 0, all + unknown chunks are affected. If nonzero, + only the chunks in the list are affected + +Unknown chunks declared in this way will be saved as raw data onto a +list of png_unknown_chunk structures. If a chunk that is normally +known to libpng is named in the list, it will be handled as unknown, +according to the "keep" directive. If a chunk is named in successive +instances of png_set_keep_unknown_chunks(), the final instance will +take precedence. The IHDR and IEND chunks should not be named in +chunk_list; if they are, libpng will process them normally anyway. + +The high-level read interface + +At this point there are two ways to proceed; through the high-level +read interface, or through a sequence of low-level read operations. +You can use the high-level interface if (a) you are willing to read +the entire image into memory, and (b) the input transformations +you want to do are limited to the following set: + + PNG_TRANSFORM_IDENTITY No transformation + PNG_TRANSFORM_STRIP_16 Strip 16-bit samples to + 8 bits + PNG_TRANSFORM_STRIP_ALPHA Discard the alpha channel + PNG_TRANSFORM_PACKING Expand 1, 2 and 4-bit + samples to bytes + PNG_TRANSFORM_PACKSWAP Change order of packed + pixels to LSB first + PNG_TRANSFORM_EXPAND Perform set_expand() + PNG_TRANSFORM_INVERT_MONO Invert monochrome images + PNG_TRANSFORM_SHIFT Normalize pixels to the + sBIT depth + PNG_TRANSFORM_BGR Flip RGB to BGR, RGBA + to BGRA + PNG_TRANSFORM_SWAP_ALPHA Flip RGBA to ARGB or GA + to AG + PNG_TRANSFORM_INVERT_ALPHA Change alpha from opacity + to transparency + PNG_TRANSFORM_SWAP_ENDIAN Byte-swap 16-bit samples + +(This excludes setting a background color, doing gamma transformation, +dithering, and setting filler.) If this is the case, simply do this: + + png_read_png(png_ptr, info_ptr, png_transforms, NULL) + +where png_transforms is an integer containing the bitwise OR of +some set of transformation flags. This call is equivalent to png_read_info(), +followed the set of transformations indicated by the transform mask, +then png_read_image(), and finally png_read_end(). + +(The final parameter of this call is not yet used. Someday it might point +to transformation parameters required by some future input transform.) + +You must use png_transforms and not call any png_set_transform() functions +when you use png_read_png(). + +After you have called png_read_png(), you can retrieve the image data +with + + row_pointers = png_get_rows(png_ptr, info_ptr); + +where row_pointers is an array of pointers to the pixel data for each row: + + png_bytep row_pointers[height]; + +If you know your image size and pixel size ahead of time, you can allocate +row_pointers prior to calling png_read_png() with + + if (height > PNG_UINT_32_MAX/png_sizeof(png_byte)) + png_error (png_ptr, + "Image is too tall to process in memory"); + if (width > PNG_UINT_32_MAX/pixel_size) + png_error (png_ptr, + "Image is too wide to process in memory"); + row_pointers = png_malloc(png_ptr, + height*png_sizeof(png_bytep)); + for (int i=0; i) and +png_get_(png_ptr, info_ptr, ...) functions return non-zero if the +data has been read, or zero if it is missing. The parameters to the +png_get_ are set directly if they are simple data types, or a pointer +into the info_ptr is returned for any complex types. + + png_get_PLTE(png_ptr, info_ptr, &palette, + &num_palette); + palette - the palette for the file + (array of png_color) + num_palette - number of entries in the palette + + png_get_gAMA(png_ptr, info_ptr, &gamma); + gamma - the gamma the file is written + at (PNG_INFO_gAMA) + + png_get_sRGB(png_ptr, info_ptr, &srgb_intent); + srgb_intent - the rendering intent (PNG_INFO_sRGB) + The presence of the sRGB chunk + means that the pixel data is in the + sRGB color space. This chunk also + implies specific values of gAMA and + cHRM. + + png_get_iCCP(png_ptr, info_ptr, &name, + &compression_type, &profile, &proflen); + name - The profile name. + compression - The compression type; always + PNG_COMPRESSION_TYPE_BASE for PNG 1.0. + You may give NULL to this argument to + ignore it. + profile - International Color Consortium color + profile data. May contain NULs. + proflen - length of profile data in bytes. + + png_get_sBIT(png_ptr, info_ptr, &sig_bit); + sig_bit - the number of significant bits for + (PNG_INFO_sBIT) each of the gray, + red, green, and blue channels, + whichever are appropriate for the + given color type (png_color_16) + + png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, + &trans_values); + trans - array of transparent entries for + palette (PNG_INFO_tRNS) + trans_values - graylevel or color sample values of + the single transparent color for + non-paletted images (PNG_INFO_tRNS) + num_trans - number of transparent entries + (PNG_INFO_tRNS) + + png_get_hIST(png_ptr, info_ptr, &hist); + (PNG_INFO_hIST) + hist - histogram of palette (array of + png_uint_16) + + png_get_tIME(png_ptr, info_ptr, &mod_time); + mod_time - time image was last modified + (PNG_VALID_tIME) + + png_get_bKGD(png_ptr, info_ptr, &background); + background - background color (PNG_VALID_bKGD) + valid 16-bit red, green and blue + values, regardless of color_type + + num_comments = png_get_text(png_ptr, info_ptr, + &text_ptr, &num_text); + num_comments - number of comments + text_ptr - array of png_text holding image + comments + text_ptr[i].compression - type of compression used + on "text" PNG_TEXT_COMPRESSION_NONE + PNG_TEXT_COMPRESSION_zTXt + PNG_ITXT_COMPRESSION_NONE + PNG_ITXT_COMPRESSION_zTXt + text_ptr[i].key - keyword for comment. Must contain + 1-79 characters. + text_ptr[i].text - text comments for current + keyword. Can be empty. + text_ptr[i].text_length - length of text string, + after decompression, 0 for iTXt + text_ptr[i].itxt_length - length of itxt string, + after decompression, 0 for tEXt/zTXt + text_ptr[i].lang - language of comment (empty + string for unknown). + text_ptr[i].lang_key - keyword in UTF-8 + (empty string for unknown). + num_text - number of comments (same as + num_comments; you can put NULL here + to avoid the duplication) + Note while png_set_text() will accept text, language, + and translated keywords that can be NULL pointers, the + structure returned by png_get_text will always contain + regular zero-terminated C strings. They might be + empty strings but they will never be NULL pointers. + + num_spalettes = png_get_sPLT(png_ptr, info_ptr, + &palette_ptr); + palette_ptr - array of palette structures holding + contents of one or more sPLT chunks + read. + num_spalettes - number of sPLT chunks read. + + png_get_oFFs(png_ptr, info_ptr, &offset_x, &offset_y, + &unit_type); + offset_x - positive offset from the left edge + of the screen + offset_y - positive offset from the top edge + of the screen + unit_type - PNG_OFFSET_PIXEL, PNG_OFFSET_MICROMETER + + png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y, + &unit_type); + res_x - pixels/unit physical resolution in + x direction + res_y - pixels/unit physical resolution in + x direction + unit_type - PNG_RESOLUTION_UNKNOWN, + PNG_RESOLUTION_METER + + png_get_sCAL(png_ptr, info_ptr, &unit, &width, + &height) + unit - physical scale units (an integer) + width - width of a pixel in physical scale units + height - height of a pixel in physical scale units + (width and height are doubles) + + png_get_sCAL_s(png_ptr, info_ptr, &unit, &width, + &height) + unit - physical scale units (an integer) + width - width of a pixel in physical scale units + height - height of a pixel in physical scale units + (width and height are strings like "2.54") + + num_unknown_chunks = png_get_unknown_chunks(png_ptr, + info_ptr, &unknowns) + unknowns - array of png_unknown_chunk + structures holding unknown chunks + unknowns[i].name - name of unknown chunk + unknowns[i].data - data of unknown chunk + unknowns[i].size - size of unknown chunk's data + unknowns[i].location - position of chunk in file + + The value of "i" corresponds to the order in which the + chunks were read from the PNG file or inserted with the + png_set_unknown_chunks() function. + +The data from the pHYs chunk can be retrieved in several convenient +forms: + + res_x = png_get_x_pixels_per_meter(png_ptr, + info_ptr) + res_y = png_get_y_pixels_per_meter(png_ptr, + info_ptr) + res_x_and_y = png_get_pixels_per_meter(png_ptr, + info_ptr) + res_x = png_get_x_pixels_per_inch(png_ptr, + info_ptr) + res_y = png_get_y_pixels_per_inch(png_ptr, + info_ptr) + res_x_and_y = png_get_pixels_per_inch(png_ptr, + info_ptr) + aspect_ratio = png_get_pixel_aspect_ratio(png_ptr, + info_ptr) + + (Each of these returns 0 [signifying "unknown"] if + the data is not present or if res_x is 0; + res_x_and_y is 0 if res_x != res_y) + +The data from the oFFs chunk can be retrieved in several convenient +forms: + + x_offset = png_get_x_offset_microns(png_ptr, info_ptr); + y_offset = png_get_y_offset_microns(png_ptr, info_ptr); + x_offset = png_get_x_offset_inches(png_ptr, info_ptr); + y_offset = png_get_y_offset_inches(png_ptr, info_ptr); + + (Each of these returns 0 [signifying "unknown" if both + x and y are 0] if the data is not present or if the + chunk is present but the unit is the pixel) + +For more information, see the png_info definition in png.h and the +PNG specification for chunk contents. Be careful with trusting +rowbytes, as some of the transformations could increase the space +needed to hold a row (expand, filler, gray_to_rgb, etc.). +See png_read_update_info(), below. + +A quick word about text_ptr and num_text. PNG stores comments in +keyword/text pairs, one pair per chunk, with no limit on the number +of text chunks, and a 2^31 byte limit on their size. While there are +suggested keywords, there is no requirement to restrict the use to these +strings. It is strongly suggested that keywords and text be sensible +to humans (that's the point), so don't use abbreviations. Non-printing +symbols are not allowed. See the PNG specification for more details. +There is also no requirement to have text after the keyword. + +Keywords should be limited to 79 Latin-1 characters without leading or +trailing spaces, but non-consecutive spaces are allowed within the +keyword. It is possible to have the same keyword any number of times. +The text_ptr is an array of png_text structures, each holding a +pointer to a language string, a pointer to a keyword and a pointer to +a text string. The text string, language code, and translated +keyword may be empty or NULL pointers. The keyword/text +pairs are put into the array in the order that they are received. +However, some or all of the text chunks may be after the image, so, to +make sure you have read all the text chunks, don't mess with these +until after you read the stuff after the image. This will be +mentioned again below in the discussion that goes with png_read_end(). + +Input transformations + +After you've read the header information, you can set up the library +to handle any special transformations of the image data. The various +ways to transform the data will be described in the order that they +should occur. This is important, as some of these change the color +type and/or bit depth of the data, and some others only work on +certain color types and bit depths. Even though each transformation +checks to see if it has data that it can do something with, you should +make sure to only enable a transformation if it will be valid for the +data. For example, don't swap red and blue on grayscale data. + +The colors used for the background and transparency values should be +supplied in the same format/depth as the current image data. They +are stored in the same format/depth as the image data in a bKGD or tRNS +chunk, so this is what libpng expects for this data. The colors are +transformed to keep in sync with the image data when an application +calls the png_read_update_info() routine (see below). + +Data will be decoded into the supplied row buffers packed into bytes +unless the library has been told to transform it into another format. +For example, 4 bit/pixel paletted or grayscale data will be returned +2 pixels/byte with the leftmost pixel in the high-order bits of the +byte, unless png_set_packing() is called. 8-bit RGB data will be stored +in RGB RGB RGB format unless png_set_filler() or png_set_add_alpha() +is called to insert filler bytes, either before or after each RGB triplet. +16-bit RGB data will be returned RRGGBB RRGGBB, with the most significant +byte of the color value first, unless png_set_strip_16() is called to +transform it to regular RGB RGB triplets, or png_set_filler() or +png_set_add alpha() is called to insert filler bytes, either before or +after each RRGGBB triplet. Similarly, 8-bit or 16-bit grayscale data can +be modified with +png_set_filler(), png_set_add_alpha(), or png_set_strip_16(). + +The following code transforms grayscale images of less than 8 to 8 bits, +changes paletted images to RGB, and adds a full alpha channel if there is +transparency information in a tRNS chunk. This is most useful on +grayscale images with bit depths of 2 or 4 or if there is a multiple-image +viewing application that wishes to treat all images in the same way. + + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb(png_ptr); + + if (color_type == PNG_COLOR_TYPE_GRAY && + bit_depth < 8) png_set_expand_gray_1_2_4_to_8(png_ptr); + + if (png_get_valid(png_ptr, info_ptr, + PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr); + +These three functions are actually aliases for png_set_expand(), added +in libpng version 1.0.4, with the function names expanded to improve code +readability. In some future version they may actually do different +things. + +As of libpng version 1.2.9, png_set_expand_gray_1_2_4_to_8() was +added. It expands the sample depth without changing tRNS to alpha. +At the same time, png_set_gray_1_2_4_to_8() was deprecated, and it +will be removed from a future version. + +PNG can have files with 16 bits per channel. If you only can handle +8 bits per channel, this will strip the pixels down to 8 bit. + + if (bit_depth == 16) + png_set_strip_16(png_ptr); + +If, for some reason, you don't need the alpha channel on an image, +and you want to remove it rather than combining it with the background +(but the image author certainly had in mind that you *would* combine +it with the background, so that's what you should probably do): + + if (color_type & PNG_COLOR_MASK_ALPHA) + png_set_strip_alpha(png_ptr); + +In PNG files, the alpha channel in an image +is the level of opacity. If you need the alpha channel in an image to +be the level of transparency instead of opacity, you can invert the +alpha channel (or the tRNS chunk data) after it's read, so that 0 is +fully opaque and 255 (in 8-bit or paletted images) or 65535 (in 16-bit +images) is fully transparent, with + + png_set_invert_alpha(png_ptr); + +PNG files pack pixels of bit depths 1, 2, and 4 into bytes as small as +they can, resulting in, for example, 8 pixels per byte for 1 bit +files. This code expands to 1 pixel per byte without changing the +values of the pixels: + + if (bit_depth < 8) + png_set_packing(png_ptr); + +PNG files have possible bit depths of 1, 2, 4, 8, and 16. All pixels +stored in a PNG image have been "scaled" or "shifted" up to the next +higher possible bit depth (e.g. from 5 bits/sample in the range [0,31] to +8 bits/sample in the range [0, 255]). However, it is also possible to +convert the PNG pixel data back to the original bit depth of the image. +This call reduces the pixels back down to the original bit depth: + + png_color_8p sig_bit; + + if (png_get_sBIT(png_ptr, info_ptr, &sig_bit)) + png_set_shift(png_ptr, sig_bit); + +PNG files store 3-color pixels in red, green, blue order. This code +changes the storage of the pixels to blue, green, red: + + if (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) + png_set_bgr(png_ptr); + +PNG files store RGB pixels packed into 3 or 6 bytes. This code expands them +into 4 or 8 bytes for windowing systems that need them in this format: + + if (color_type == PNG_COLOR_TYPE_RGB) + png_set_filler(png_ptr, filler, PNG_FILLER_BEFORE); + +where "filler" is the 8 or 16-bit number to fill with, and the location is +either PNG_FILLER_BEFORE or PNG_FILLER_AFTER, depending upon whether +you want the filler before the RGB or after. This transformation +does not affect images that already have full alpha channels. To add an +opaque alpha channel, use filler=0xff or 0xffff and PNG_FILLER_AFTER which +will generate RGBA pixels. + +Note that png_set_filler() does not change the color type. If you want +to do that, you can add a true alpha channel with + + if (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_GRAY) + png_set_add_alpha(png_ptr, filler, PNG_FILLER_AFTER); + +where "filler" contains the alpha value to assign to each pixel. +This function was added in libpng-1.2.7. + +If you are reading an image with an alpha channel, and you need the +data as ARGB instead of the normal PNG format RGBA: + + if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) + png_set_swap_alpha(png_ptr); + +For some uses, you may want a grayscale image to be represented as +RGB. This code will do that conversion: + + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png_ptr); + +Conversely, you can convert an RGB or RGBA image to grayscale or grayscale +with alpha. + + if (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) + png_set_rgb_to_gray_fixed(png_ptr, error_action, + int red_weight, int green_weight); + + error_action = 1: silently do the conversion + error_action = 2: issue a warning if the original + image has any pixel where + red != green or red != blue + error_action = 3: issue an error and abort the + conversion if the original + image has any pixel where + red != green or red != blue + + red_weight: weight of red component times 100000 + green_weight: weight of green component times 100000 + If either weight is negative, default + weights (21268, 71514) are used. + +If you have set error_action = 1 or 2, you can +later check whether the image really was gray, after processing +the image rows, with the png_get_rgb_to_gray_status(png_ptr) function. +It will return a png_byte that is zero if the image was gray or +1 if there were any non-gray pixels. bKGD and sBIT data +will be silently converted to grayscale, using the green channel +data, regardless of the error_action setting. + +With red_weight+green_weight<=100000, +the normalized graylevel is computed: + + int rw = red_weight * 65536; + int gw = green_weight * 65536; + int bw = 65536 - (rw + gw); + gray = (rw*red + gw*green + bw*blue)/65536; + +The default values approximate those recommended in the Charles +Poynton's Color FAQ, +Copyright (c) 1998-01-04 Charles Poynton + + Y = 0.212671 * R + 0.715160 * G + 0.072169 * B + +Libpng approximates this with + + Y = 0.21268 * R + 0.7151 * G + 0.07217 * B + +which can be expressed with integers as + + Y = (6969 * R + 23434 * G + 2365 * B)/32768 + +The calculation is done in a linear colorspace, if the image gamma +is known. + +If you have a grayscale and you are using png_set_expand_depth(), +png_set_expand(), or png_set_gray_to_rgb to change to truecolor or to +a higher bit-depth, you must either supply the background color as a gray +value at the original file bit-depth (need_expand = 1) or else supply the +background color as an RGB triplet at the final, expanded bit depth +(need_expand = 0). Similarly, if you are reading a paletted image, you +must either supply the background color as a palette index (need_expand = 1) +or as an RGB triplet that may or may not be in the palette (need_expand = 0). + + png_color_16 my_background; + png_color_16p image_background; + + if (png_get_bKGD(png_ptr, info_ptr, &image_background)) + png_set_background(png_ptr, image_background, + PNG_BACKGROUND_GAMMA_FILE, 1, 1.0); + else + png_set_background(png_ptr, &my_background, + PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0); + +The png_set_background() function tells libpng to composite images +with alpha or simple transparency against the supplied background +color. If the PNG file contains a bKGD chunk (PNG_INFO_bKGD valid), +you may use this color, or supply another color more suitable for +the current display (e.g., the background color from a web page). You +need to tell libpng whether the color is in the gamma space of the +display (PNG_BACKGROUND_GAMMA_SCREEN for colors you supply), the file +(PNG_BACKGROUND_GAMMA_FILE for colors from the bKGD chunk), or one +that is neither of these gammas (PNG_BACKGROUND_GAMMA_UNIQUE - I don't +know why anyone would use this, but it's here). + +To properly display PNG images on any kind of system, the application needs +to know what the display gamma is. Ideally, the user will know this, and +the application will allow them to set it. One method of allowing the user +to set the display gamma separately for each system is to check for a +SCREEN_GAMMA or DISPLAY_GAMMA environment variable, which will hopefully be +correctly set. + +Note that display_gamma is the overall gamma correction required to produce +pleasing results, which depends on the lighting conditions in the surrounding +environment. In a dim or brightly lit room, no compensation other than +the physical gamma exponent of the monitor is needed, while in a dark room +a slightly smaller exponent is better. + + double gamma, screen_gamma; + + if (/* We have a user-defined screen + gamma value */) + { + screen_gamma = user_defined_screen_gamma; + } + /* One way that applications can share the same + screen gamma value */ + else if ((gamma_str = getenv("SCREEN_GAMMA")) + != NULL) + { + screen_gamma = (double)atof(gamma_str); + } + /* If we don't have another value */ + else + { + screen_gamma = 2.2; /* A good guess for a + PC monitor in a bright office or a dim room */ + screen_gamma = 2.0; /* A good guess for a + PC monitor in a dark room */ + screen_gamma = 1.7 or 1.0; /* A good + guess for Mac systems */ + } + +The png_set_gamma() function handles gamma transformations of the data. +Pass both the file gamma and the current screen_gamma. If the file does +not have a gamma value, you can pass one anyway if you have an idea what +it is (usually 0.45455 is a good guess for GIF images on PCs). Note +that file gammas are inverted from screen gammas. See the discussions +on gamma in the PNG specification for an excellent description of what +gamma is, and why all applications should support it. It is strongly +recommended that PNG viewers support gamma correction. + + if (png_get_gAMA(png_ptr, info_ptr, &gamma)) + png_set_gamma(png_ptr, screen_gamma, gamma); + else + png_set_gamma(png_ptr, screen_gamma, 0.45455); + +If you need to reduce an RGB file to a paletted file, or if a paletted +file has more entries then will fit on your screen, png_set_dither() +will do that. Note that this is a simple match dither that merely +finds the closest color available. This should work fairly well with +optimized palettes, and fairly badly with linear color cubes. If you +pass a palette that is larger then maximum_colors, the file will +reduce the number of colors in the palette so it will fit into +maximum_colors. If there is a histogram, it will use it to make +more intelligent choices when reducing the palette. If there is no +histogram, it may not do as good a job. + + if (color_type & PNG_COLOR_MASK_COLOR) + { + if (png_get_valid(png_ptr, info_ptr, + PNG_INFO_PLTE)) + { + png_uint_16p histogram = NULL; + + png_get_hIST(png_ptr, info_ptr, + &histogram); + png_set_dither(png_ptr, palette, num_palette, + max_screen_colors, histogram, 1); + } + else + { + png_color std_color_cube[MAX_SCREEN_COLORS] = + { ... colors ... }; + + png_set_dither(png_ptr, std_color_cube, + MAX_SCREEN_COLORS, MAX_SCREEN_COLORS, + NULL,0); + } + } + +PNG files describe monochrome as black being zero and white being one. +The following code will reverse this (make black be one and white be +zero): + + if (bit_depth == 1 && color_type == PNG_COLOR_TYPE_GRAY) + png_set_invert_mono(png_ptr); + +This function can also be used to invert grayscale and gray-alpha images: + + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_invert_mono(png_ptr); + +PNG files store 16 bit pixels in network byte order (big-endian, +ie. most significant bits first). This code changes the storage to the +other way (little-endian, i.e. least significant bits first, the +way PCs store them): + + if (bit_depth == 16) + png_set_swap(png_ptr); + +If you are using packed-pixel images (1, 2, or 4 bits/pixel), and you +need to change the order the pixels are packed into bytes, you can use: + + if (bit_depth < 8) + png_set_packswap(png_ptr); + +Finally, you can write your own transformation function if none of +the existing ones meets your needs. This is done by setting a callback +with + + png_set_read_user_transform_fn(png_ptr, + read_transform_fn); + +You must supply the function + + void read_transform_fn(png_ptr ptr, row_info_ptr + row_info, png_bytep data) + +See pngtest.c for a working example. Your function will be called +after all of the other transformations have been processed. + +You can also set up a pointer to a user structure for use by your +callback function, and you can inform libpng that your transform +function will change the number of channels or bit depth with the +function + + png_set_user_transform_info(png_ptr, user_ptr, + user_depth, user_channels); + +The user's application, not libpng, is responsible for allocating and +freeing any memory required for the user structure. + +You can retrieve the pointer via the function +png_get_user_transform_ptr(). For example: + + voidp read_user_transform_ptr = + png_get_user_transform_ptr(png_ptr); + +The last thing to handle is interlacing; this is covered in detail below, +but you must call the function here if you want libpng to handle expansion +of the interlaced image. + + number_of_passes = png_set_interlace_handling(png_ptr); + +After setting the transformations, libpng can update your png_info +structure to reflect any transformations you've requested with this +call. This is most useful to update the info structure's rowbytes +field so you can use it to allocate your image memory. This function +will also update your palette with the correct screen_gamma and +background if these have been given with the calls above. + + png_read_update_info(png_ptr, info_ptr); + +After you call png_read_update_info(), you can allocate any +memory you need to hold the image. The row data is simply +raw byte data for all forms of images. As the actual allocation +varies among applications, no example will be given. If you +are allocating one large chunk, you will need to build an +array of pointers to each row, as it will be needed for some +of the functions below. + +Reading image data + +After you've allocated memory, you can read the image data. +The simplest way to do this is in one function call. If you are +allocating enough memory to hold the whole image, you can just +call png_read_image() and libpng will read in all the image data +and put it in the memory area supplied. You will need to pass in +an array of pointers to each row. + +This function automatically handles interlacing, so you don't need +to call png_set_interlace_handling() or call this function multiple +times, or any of that other stuff necessary with png_read_rows(). + + png_read_image(png_ptr, row_pointers); + +where row_pointers is: + + png_bytep row_pointers[height]; + +You can point to void or char or whatever you use for pixels. + +If you don't want to read in the whole image at once, you can +use png_read_rows() instead. If there is no interlacing (check +interlace_type == PNG_INTERLACE_NONE), this is simple: + + png_read_rows(png_ptr, row_pointers, NULL, + number_of_rows); + +where row_pointers is the same as in the png_read_image() call. + +If you are doing this just one row at a time, you can do this with +a single row_pointer instead of an array of row_pointers: + + png_bytep row_pointer = row; + png_read_row(png_ptr, row_pointer, NULL); + +If the file is interlaced (interlace_type != 0 in the IHDR chunk), things +get somewhat harder. The only current (PNG Specification version 1.2) +interlacing type for PNG is (interlace_type == PNG_INTERLACE_ADAM7) +is a somewhat complicated 2D interlace scheme, known as Adam7, that +breaks down an image into seven smaller images of varying size, based +on an 8x8 grid. + +libpng can fill out those images or it can give them to you "as is". +If you want them filled out, there are two ways to do that. The one +mentioned in the PNG specification is to expand each pixel to cover +those pixels that have not been read yet (the "rectangle" method). +This results in a blocky image for the first pass, which gradually +smooths out as more pixels are read. The other method is the "sparkle" +method, where pixels are drawn only in their final locations, with the +rest of the image remaining whatever colors they were initialized to +before the start of the read. The first method usually looks better, +but tends to be slower, as there are more pixels to put in the rows. + +If you don't want libpng to handle the interlacing details, just call +png_read_rows() seven times to read in all seven images. Each of the +images is a valid image by itself, or they can all be combined on an +8x8 grid to form a single image (although if you intend to combine them +you would be far better off using the libpng interlace handling). + +The first pass will return an image 1/8 as wide as the entire image +(every 8th column starting in column 0) and 1/8 as high as the original +(every 8th row starting in row 0), the second will be 1/8 as wide +(starting in column 4) and 1/8 as high (also starting in row 0). The +third pass will be 1/4 as wide (every 4th pixel starting in column 0) and +1/8 as high (every 8th row starting in row 4), and the fourth pass will +be 1/4 as wide and 1/4 as high (every 4th column starting in column 2, +and every 4th row starting in row 0). The fifth pass will return an +image 1/2 as wide, and 1/4 as high (starting at column 0 and row 2), +while the sixth pass will be 1/2 as wide and 1/2 as high as the original +(starting in column 1 and row 0). The seventh and final pass will be as +wide as the original, and 1/2 as high, containing all of the odd +numbered scanlines. Phew! + +If you want libpng to expand the images, call this before calling +png_start_read_image() or png_read_update_info(): + + if (interlace_type == PNG_INTERLACE_ADAM7) + number_of_passes + = png_set_interlace_handling(png_ptr); + +This will return the number of passes needed. Currently, this +is seven, but may change if another interlace type is added. +This function can be called even if the file is not interlaced, +where it will return one pass. + +If you are not going to display the image after each pass, but are +going to wait until the entire image is read in, use the sparkle +effect. This effect is faster and the end result of either method +is exactly the same. If you are planning on displaying the image +after each pass, the "rectangle" effect is generally considered the +better looking one. + +If you only want the "sparkle" effect, just call png_read_rows() as +normal, with the third parameter NULL. Make sure you make pass over +the image number_of_passes times, and you don't change the data in the +rows between calls. You can change the locations of the data, just +not the data. Each pass only writes the pixels appropriate for that +pass, and assumes the data from previous passes is still valid. + + png_read_rows(png_ptr, row_pointers, NULL, + number_of_rows); + +If you only want the first effect (the rectangles), do the same as +before except pass the row buffer in the third parameter, and leave +the second parameter NULL. + + png_read_rows(png_ptr, NULL, row_pointers, + number_of_rows); + +Finishing a sequential read + +After you are finished reading the image through the +low-level interface, you can finish reading the file. If you are +interested in comments or time, which may be stored either before or +after the image data, you should pass the separate png_info struct if +you want to keep the comments from before and after the image +separate. If you are not interested, you can pass NULL. + + png_read_end(png_ptr, end_info); + +When you are done, you can free all memory allocated by libpng like this: + + png_destroy_read_struct(&png_ptr, &info_ptr, + &end_info); + +It is also possible to individually free the info_ptr members that +point to libpng-allocated storage with the following function: + + png_free_data(png_ptr, info_ptr, mask, seq) + mask - identifies data to be freed, a mask + containing the bitwise OR of one or + more of + PNG_FREE_PLTE, PNG_FREE_TRNS, + PNG_FREE_HIST, PNG_FREE_ICCP, + PNG_FREE_PCAL, PNG_FREE_ROWS, + PNG_FREE_SCAL, PNG_FREE_SPLT, + PNG_FREE_TEXT, PNG_FREE_UNKN, + or simply PNG_FREE_ALL + seq - sequence number of item to be freed + (-1 for all items) + +This function may be safely called when the relevant storage has +already been freed, or has not yet been allocated, or was allocated +by the user and not by libpng, and will in those +cases do nothing. The "seq" parameter is ignored if only one item +of the selected data type, such as PLTE, is allowed. If "seq" is not +-1, and multiple items are allowed for the data type identified in +the mask, such as text or sPLT, only the n'th item in the structure +is freed, where n is "seq". + +The default behavior is only to free data that was allocated internally +by libpng. This can be changed, so that libpng will not free the data, +or so that it will free data that was allocated by the user with png_malloc() +or png_zalloc() and passed in via a png_set_*() function, with + + png_data_freer(png_ptr, info_ptr, freer, mask) + mask - which data elements are affected + same choices as in png_free_data() + freer - one of + PNG_DESTROY_WILL_FREE_DATA + PNG_SET_WILL_FREE_DATA + PNG_USER_WILL_FREE_DATA + +This function only affects data that has already been allocated. +You can call this function after reading the PNG data but before calling +any png_set_*() functions, to control whether the user or the png_set_*() +function is responsible for freeing any existing data that might be present, +and again after the png_set_*() functions to control whether the user +or png_destroy_*() is supposed to free the data. When the user assumes +responsibility for libpng-allocated data, the application must use +png_free() to free it, and when the user transfers responsibility to libpng +for data that the user has allocated, the user must have used png_malloc() +or png_zalloc() to allocate it. + +If you allocated your row_pointers in a single block, as suggested above in +the description of the high level read interface, you must not transfer +responsibility for freeing it to the png_set_rows or png_read_destroy function, +because they would also try to free the individual row_pointers[i]. + +If you allocated text_ptr.text, text_ptr.lang, and text_ptr.translated_keyword +separately, do not transfer responsibility for freeing text_ptr to libpng, +because when libpng fills a png_text structure it combines these members with +the key member, and png_free_data() will free only text_ptr.key. Similarly, +if you transfer responsibility for free'ing text_ptr from libpng to your +application, your application must not separately free those members. + +The png_free_data() function will turn off the "valid" flag for anything +it frees. If you need to turn the flag off for a chunk that was freed by your +application instead of by libpng, you can use + + png_set_invalid(png_ptr, info_ptr, mask); + mask - identifies the chunks to be made invalid, + containing the bitwise OR of one or + more of + PNG_INFO_gAMA, PNG_INFO_sBIT, + PNG_INFO_cHRM, PNG_INFO_PLTE, + PNG_INFO_tRNS, PNG_INFO_bKGD, + PNG_INFO_hIST, PNG_INFO_pHYs, + PNG_INFO_oFFs, PNG_INFO_tIME, + PNG_INFO_pCAL, PNG_INFO_sRGB, + PNG_INFO_iCCP, PNG_INFO_sPLT, + PNG_INFO_sCAL, PNG_INFO_IDAT + +For a more compact example of reading a PNG image, see the file example.c. + +Reading PNG files progressively + +The progressive reader is slightly different then the non-progressive +reader. Instead of calling png_read_info(), png_read_rows(), and +png_read_end(), you make one call to png_process_data(), which calls +callbacks when it has the info, a row, or the end of the image. You +set up these callbacks with png_set_progressive_read_fn(). You don't +have to worry about the input/output functions of libpng, as you are +giving the library the data directly in png_process_data(). I will +assume that you have read the section on reading PNG files above, +so I will only highlight the differences (although I will show +all of the code). + +png_structp png_ptr; +png_infop info_ptr; + + /* An example code fragment of how you would + initialize the progressive reader in your + application. */ + int + initialize_png_reader() + { + png_ptr = png_create_read_struct + (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, + user_error_fn, user_warning_fn); + if (!png_ptr) + return (ERROR); + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, + (png_infopp)NULL); + return (ERROR); + } + + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr, &info_ptr, + (png_infopp)NULL); + return (ERROR); + } + + /* This one's new. You can provide functions + to be called when the header info is valid, + when each row is completed, and when the image + is finished. If you aren't using all functions, + you can specify NULL parameters. Even when all + three functions are NULL, you need to call + png_set_progressive_read_fn(). You can use + any struct as the user_ptr (cast to a void pointer + for the function call), and retrieve the pointer + from inside the callbacks using the function + + png_get_progressive_ptr(png_ptr); + + which will return a void pointer, which you have + to cast appropriately. + */ + png_set_progressive_read_fn(png_ptr, (void *)user_ptr, + info_callback, row_callback, end_callback); + + return 0; + } + + /* A code fragment that you call as you receive blocks + of data */ + int + process_data(png_bytep buffer, png_uint_32 length) + { + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr, &info_ptr, + (png_infopp)NULL); + return (ERROR); + } + + /* This one's new also. Simply give it a chunk + of data from the file stream (in order, of + course). On machines with segmented memory + models machines, don't give it any more than + 64K. The library seems to run fine with sizes + of 4K. Although you can give it much less if + necessary (I assume you can give it chunks of + 1 byte, I haven't tried less then 256 bytes + yet). When this function returns, you may + want to display any rows that were generated + in the row callback if you don't already do + so there. + */ + png_process_data(png_ptr, info_ptr, buffer, length); + return 0; + } + + /* This function is called (as set by + png_set_progressive_read_fn() above) when enough data + has been supplied so all of the header has been + read. + */ + void + info_callback(png_structp png_ptr, png_infop info) + { + /* Do any setup here, including setting any of + the transformations mentioned in the Reading + PNG files section. For now, you _must_ call + either png_start_read_image() or + png_read_update_info() after all the + transformations are set (even if you don't set + any). You may start getting rows before + png_process_data() returns, so this is your + last chance to prepare for that. + */ + } + + /* This function is called when each row of image + data is complete */ + void + row_callback(png_structp png_ptr, png_bytep new_row, + png_uint_32 row_num, int pass) + { + /* If the image is interlaced, and you turned + on the interlace handler, this function will + be called for every row in every pass. Some + of these rows will not be changed from the + previous pass. When the row is not changed, + the new_row variable will be NULL. The rows + and passes are called in order, so you don't + really need the row_num and pass, but I'm + supplying them because it may make your life + easier. + + For the non-NULL rows of interlaced images, + you must call png_progressive_combine_row() + passing in the row and the old row. You can + call this function for NULL rows (it will just + return) and for non-interlaced images (it just + does the memcpy for you) if it will make the + code easier. Thus, you can just do this for + all cases: + */ + + png_progressive_combine_row(png_ptr, old_row, + new_row); + + /* where old_row is what was displayed for + previously for the row. Note that the first + pass (pass == 0, really) will completely cover + the old row, so the rows do not have to be + initialized. After the first pass (and only + for interlaced images), you will have to pass + the current row, and the function will combine + the old row and the new row. + */ + } + + void + end_callback(png_structp png_ptr, png_infop info) + { + /* This function is called after the whole image + has been read, including any chunks after the + image (up to and including the IEND). You + will usually have the same info chunk as you + had in the header, although some data may have + been added to the comments and time fields. + + Most people won't do much here, perhaps setting + a flag that marks the image as finished. + */ + } + + + +IV. Writing + +Much of this is very similar to reading. However, everything of +importance is repeated here, so you won't have to constantly look +back up in the reading section to understand writing. + +Setup + +You will want to do the I/O initialization before you get into libpng, +so if it doesn't work, you don't have anything to undo. If you are not +using the standard I/O functions, you will need to replace them with +custom writing functions. See the discussion under Customizing libpng. + + FILE *fp = fopen(file_name, "wb"); + if (!fp) + { + return (ERROR); + } + +Next, png_struct and png_info need to be allocated and initialized. +As these can be both relatively large, you may not want to store these +on the stack, unless you have stack space to spare. Of course, you +will want to check if they return NULL. If you are also reading, +you won't want to name your read structure and your write structure +both "png_ptr"; you can call them anything you like, such as +"read_ptr" and "write_ptr". Look at pngtest.c, for example. + + png_structp png_ptr = png_create_write_struct + (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, + user_error_fn, user_warning_fn); + if (!png_ptr) + return (ERROR); + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_write_struct(&png_ptr, + (png_infopp)NULL); + return (ERROR); + } + +If you want to use your own memory allocation routines, +define PNG_USER_MEM_SUPPORTED and use +png_create_write_struct_2() instead of png_create_write_struct(): + + png_structp png_ptr = png_create_write_struct_2 + (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, + user_error_fn, user_warning_fn, (png_voidp) + user_mem_ptr, user_malloc_fn, user_free_fn); + +After you have these structures, you will need to set up the +error handling. When libpng encounters an error, it expects to +longjmp() back to your routine. Therefore, you will need to call +setjmp() and pass the png_jmpbuf(png_ptr). If you +write the file from different routines, you will need to update +the png_jmpbuf(png_ptr) every time you enter a new routine that will +call a png_*() function. See your documentation of setjmp/longjmp +for your compiler for more information on setjmp/longjmp. See +the discussion on libpng error handling in the Customizing Libpng +section below for more information on the libpng error handling. + + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_write_struct(&png_ptr, &info_ptr); + fclose(fp); + return (ERROR); + } + ... + return; + +If you would rather avoid the complexity of setjmp/longjmp issues, +you can compile libpng with PNG_SETJMP_NOT_SUPPORTED, in which case +errors will result in a call to PNG_ABORT() which defaults to abort(). + +Now you need to set up the output code. The default for libpng is to +use the C function fwrite(). If you use this, you will need to pass a +valid FILE * in the function png_init_io(). Be sure that the file is +opened in binary mode. Again, if you wish to handle writing data in +another way, see the discussion on libpng I/O handling in the Customizing +Libpng section below. + + png_init_io(png_ptr, fp); + +If you are embedding your PNG into a datastream such as MNG, and don't +want libpng to write the 8-byte signature, or if you have already +written the signature in your application, use + + png_set_sig_bytes(png_ptr, 8); + +to inform libpng that it should not write a signature. + +Write callbacks + +At this point, you can set up a callback function that will be +called after each row has been written, which you can use to control +a progress meter or the like. It's demonstrated in pngtest.c. +You must supply a function + + void write_row_callback(png_ptr, png_uint_32 row, + int pass); + { + /* put your code here */ + } + +(You can give it another name that you like instead of "write_row_callback") + +To inform libpng about your function, use + + png_set_write_status_fn(png_ptr, write_row_callback); + +You now have the option of modifying how the compression library will +run. The following functions are mainly for testing, but may be useful +in some cases, like if you need to write PNG files extremely fast and +are willing to give up some compression, or if you want to get the +maximum possible compression at the expense of slower writing. If you +have no special needs in this area, let the library do what it wants by +not calling this function at all, as it has been tuned to deliver a good +speed/compression ratio. The second parameter to png_set_filter() is +the filter method, for which the only valid values are 0 (as of the +July 1999 PNG specification, version 1.2) or 64 (if you are writing +a PNG datastream that is to be embedded in a MNG datastream). The third +parameter is a flag that indicates which filter type(s) are to be tested +for each scanline. See the PNG specification for details on the specific filter +types. + + + /* turn on or off filtering, and/or choose + specific filters. You can use either a single + PNG_FILTER_VALUE_NAME or the bitwise OR of one + or more PNG_FILTER_NAME masks. */ + png_set_filter(png_ptr, 0, + PNG_FILTER_NONE | PNG_FILTER_VALUE_NONE | + PNG_FILTER_SUB | PNG_FILTER_VALUE_SUB | + PNG_FILTER_UP | PNG_FILTER_VALUE_UP | + PNG_FILTER_AVE | PNG_FILTER_VALUE_AVE | + PNG_FILTER_PAETH | PNG_FILTER_VALUE_PAETH| + PNG_ALL_FILTERS); + +If an application +wants to start and stop using particular filters during compression, +it should start out with all of the filters (to ensure that the previous +row of pixels will be stored in case it's needed later), and then add +and remove them after the start of compression. + +If you are writing a PNG datastream that is to be embedded in a MNG +datastream, the second parameter can be either 0 or 64. + +The png_set_compression_*() functions interface to the zlib compression +library, and should mostly be ignored unless you really know what you are +doing. The only generally useful call is png_set_compression_level() +which changes how much time zlib spends on trying to compress the image +data. See the Compression Library (zlib.h and algorithm.txt, distributed +with zlib) for details on the compression levels. + + /* set the zlib compression level */ + png_set_compression_level(png_ptr, + Z_BEST_COMPRESSION); + + /* set other zlib parameters */ + png_set_compression_mem_level(png_ptr, 8); + png_set_compression_strategy(png_ptr, + Z_DEFAULT_STRATEGY); + png_set_compression_window_bits(png_ptr, 15); + png_set_compression_method(png_ptr, 8); + png_set_compression_buffer_size(png_ptr, 8192) + +extern PNG_EXPORT(void,png_set_zbuf_size) + +Setting the contents of info for output + +You now need to fill in the png_info structure with all the data you +wish to write before the actual image. Note that the only thing you +are allowed to write after the image is the text chunks and the time +chunk (as of PNG Specification 1.2, anyway). See png_write_end() and +the latest PNG specification for more information on that. If you +wish to write them before the image, fill them in now, and flag that +data as being valid. If you want to wait until after the data, don't +fill them until png_write_end(). For all the fields in png_info and +their data types, see png.h. For explanations of what the fields +contain, see the PNG specification. + +Some of the more important parts of the png_info are: + + png_set_IHDR(png_ptr, info_ptr, width, height, + bit_depth, color_type, interlace_type, + compression_type, filter_method) + width - holds the width of the image + in pixels (up to 2^31). + height - holds the height of the image + in pixels (up to 2^31). + bit_depth - holds the bit depth of one of the + image channels. + (valid values are 1, 2, 4, 8, 16 + and depend also on the + color_type. See also significant + bits (sBIT) below). + color_type - describes which color/alpha + channels are present. + PNG_COLOR_TYPE_GRAY + (bit depths 1, 2, 4, 8, 16) + PNG_COLOR_TYPE_GRAY_ALPHA + (bit depths 8, 16) + PNG_COLOR_TYPE_PALETTE + (bit depths 1, 2, 4, 8) + PNG_COLOR_TYPE_RGB + (bit_depths 8, 16) + PNG_COLOR_TYPE_RGB_ALPHA + (bit_depths 8, 16) + + PNG_COLOR_MASK_PALETTE + PNG_COLOR_MASK_COLOR + PNG_COLOR_MASK_ALPHA + + interlace_type - PNG_INTERLACE_NONE or + PNG_INTERLACE_ADAM7 + compression_type - (must be + PNG_COMPRESSION_TYPE_DEFAULT) + filter_method - (must be PNG_FILTER_TYPE_DEFAULT + or, if you are writing a PNG to + be embedded in a MNG datastream, + can also be + PNG_INTRAPIXEL_DIFFERENCING) + + png_set_PLTE(png_ptr, info_ptr, palette, + num_palette); + palette - the palette for the file + (array of png_color) + num_palette - number of entries in the palette + + png_set_gAMA(png_ptr, info_ptr, gamma); + gamma - the gamma the image was created + at (PNG_INFO_gAMA) + + png_set_sRGB(png_ptr, info_ptr, srgb_intent); + srgb_intent - the rendering intent + (PNG_INFO_sRGB) The presence of + the sRGB chunk means that the pixel + data is in the sRGB color space. + This chunk also implies specific + values of gAMA and cHRM. Rendering + intent is the CSS-1 property that + has been defined by the International + Color Consortium + (http://www.color.org). + It can be one of + PNG_sRGB_INTENT_SATURATION, + PNG_sRGB_INTENT_PERCEPTUAL, + PNG_sRGB_INTENT_ABSOLUTE, or + PNG_sRGB_INTENT_RELATIVE. + + + png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, + srgb_intent); + srgb_intent - the rendering intent + (PNG_INFO_sRGB) The presence of the + sRGB chunk means that the pixel + data is in the sRGB color space. + This function also causes gAMA and + cHRM chunks with the specific values + that are consistent with sRGB to be + written. + + png_set_iCCP(png_ptr, info_ptr, name, compression_type, + profile, proflen); + name - The profile name. + compression - The compression type; always + PNG_COMPRESSION_TYPE_BASE for PNG 1.0. + You may give NULL to this argument to + ignore it. + profile - International Color Consortium color + profile data. May contain NULs. + proflen - length of profile data in bytes. + + png_set_sBIT(png_ptr, info_ptr, sig_bit); + sig_bit - the number of significant bits for + (PNG_INFO_sBIT) each of the gray, red, + green, and blue channels, whichever are + appropriate for the given color type + (png_color_16) + + png_set_tRNS(png_ptr, info_ptr, trans, num_trans, + trans_values); + trans - array of transparent entries for + palette (PNG_INFO_tRNS) + trans_values - graylevel or color sample values of + the single transparent color for + non-paletted images (PNG_INFO_tRNS) + num_trans - number of transparent entries + (PNG_INFO_tRNS) + + png_set_hIST(png_ptr, info_ptr, hist); + (PNG_INFO_hIST) + hist - histogram of palette (array of + png_uint_16) + + png_set_tIME(png_ptr, info_ptr, mod_time); + mod_time - time image was last modified + (PNG_VALID_tIME) + + png_set_bKGD(png_ptr, info_ptr, background); + background - background color (PNG_VALID_bKGD) + + png_set_text(png_ptr, info_ptr, text_ptr, num_text); + text_ptr - array of png_text holding image + comments + text_ptr[i].compression - type of compression used + on "text" PNG_TEXT_COMPRESSION_NONE + PNG_TEXT_COMPRESSION_zTXt + PNG_ITXT_COMPRESSION_NONE + PNG_ITXT_COMPRESSION_zTXt + text_ptr[i].key - keyword for comment. Must contain + 1-79 characters. + text_ptr[i].text - text comments for current + keyword. Can be NULL or empty. + text_ptr[i].text_length - length of text string, + after decompression, 0 for iTXt + text_ptr[i].itxt_length - length of itxt string, + after decompression, 0 for tEXt/zTXt + text_ptr[i].lang - language of comment (NULL or + empty for unknown). + text_ptr[i].translated_keyword - keyword in UTF-8 (NULL + or empty for unknown). + num_text - number of comments + + png_set_sPLT(png_ptr, info_ptr, &palette_ptr, + num_spalettes); + palette_ptr - array of png_sPLT_struct structures + to be added to the list of palettes + in the info structure. + num_spalettes - number of palette structures to be + added. + + png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, + unit_type); + offset_x - positive offset from the left + edge of the screen + offset_y - positive offset from the top + edge of the screen + unit_type - PNG_OFFSET_PIXEL, PNG_OFFSET_MICROMETER + + png_set_pHYs(png_ptr, info_ptr, res_x, res_y, + unit_type); + res_x - pixels/unit physical resolution + in x direction + res_y - pixels/unit physical resolution + in y direction + unit_type - PNG_RESOLUTION_UNKNOWN, + PNG_RESOLUTION_METER + + png_set_sCAL(png_ptr, info_ptr, unit, width, height) + unit - physical scale units (an integer) + width - width of a pixel in physical scale units + height - height of a pixel in physical scale units + (width and height are doubles) + + png_set_sCAL_s(png_ptr, info_ptr, unit, width, height) + unit - physical scale units (an integer) + width - width of a pixel in physical scale units + height - height of a pixel in physical scale units + (width and height are strings like "2.54") + + png_set_unknown_chunks(png_ptr, info_ptr, &unknowns, + num_unknowns) + unknowns - array of png_unknown_chunk + structures holding unknown chunks + unknowns[i].name - name of unknown chunk + unknowns[i].data - data of unknown chunk + unknowns[i].size - size of unknown chunk's data + unknowns[i].location - position to write chunk in file + 0: do not write chunk + PNG_HAVE_IHDR: before PLTE + PNG_HAVE_PLTE: before IDAT + PNG_AFTER_IDAT: after IDAT + +The "location" member is set automatically according to +what part of the output file has already been written. +You can change its value after calling png_set_unknown_chunks() +as demonstrated in pngtest.c. Within each of the "locations", +the chunks are sequenced according to their position in the +structure (that is, the value of "i", which is the order in which +the chunk was either read from the input file or defined with +png_set_unknown_chunks). + +A quick word about text and num_text. text is an array of png_text +structures. num_text is the number of valid structures in the array. +Each png_text structure holds a language code, a keyword, a text value, +and a compression type. + +The compression types have the same valid numbers as the compression +types of the image data. Currently, the only valid number is zero. +However, you can store text either compressed or uncompressed, unlike +images, which always have to be compressed. So if you don't want the +text compressed, set the compression type to PNG_TEXT_COMPRESSION_NONE. +Because tEXt and zTXt chunks don't have a language field, if you +specify PNG_TEXT_COMPRESSION_NONE or PNG_TEXT_COMPRESSION_zTXt +any language code or translated keyword will not be written out. + +Until text gets around 1000 bytes, it is not worth compressing it. +After the text has been written out to the file, the compression type +is set to PNG_TEXT_COMPRESSION_NONE_WR or PNG_TEXT_COMPRESSION_zTXt_WR, +so that it isn't written out again at the end (in case you are calling +png_write_end() with the same struct. + +The keywords that are given in the PNG Specification are: + + Title Short (one line) title or + caption for image + Author Name of image's creator + Description Description of image (possibly long) + Copyright Copyright notice + Creation Time Time of original image creation + (usually RFC 1123 format, see below) + Software Software used to create the image + Disclaimer Legal disclaimer + Warning Warning of nature of content + Source Device used to create the image + Comment Miscellaneous comment; conversion + from other image format + +The keyword-text pairs work like this. Keywords should be short +simple descriptions of what the comment is about. Some typical +keywords are found in the PNG specification, as is some recommendations +on keywords. You can repeat keywords in a file. You can even write +some text before the image and some after. For example, you may want +to put a description of the image before the image, but leave the +disclaimer until after, so viewers working over modem connections +don't have to wait for the disclaimer to go over the modem before +they start seeing the image. Finally, keywords should be full +words, not abbreviations. Keywords and text are in the ISO 8859-1 +(Latin-1) character set (a superset of regular ASCII) and can not +contain NUL characters, and should not contain control or other +unprintable characters. To make the comments widely readable, stick +with basic ASCII, and avoid machine specific character set extensions +like the IBM-PC character set. The keyword must be present, but +you can leave off the text string on non-compressed pairs. +Compressed pairs must have a text string, as only the text string +is compressed anyway, so the compression would be meaningless. + +PNG supports modification time via the png_time structure. Two +conversion routines are provided, png_convert_from_time_t() for +time_t and png_convert_from_struct_tm() for struct tm. The +time_t routine uses gmtime(). You don't have to use either of +these, but if you wish to fill in the png_time structure directly, +you should provide the time in universal time (GMT) if possible +instead of your local time. Note that the year number is the full +year (e.g. 1998, rather than 98 - PNG is year 2000 compliant!), and +that months start with 1. + +If you want to store the time of the original image creation, you should +use a plain tEXt chunk with the "Creation Time" keyword. This is +necessary because the "creation time" of a PNG image is somewhat vague, +depending on whether you mean the PNG file, the time the image was +created in a non-PNG format, a still photo from which the image was +scanned, or possibly the subject matter itself. In order to facilitate +machine-readable dates, it is recommended that the "Creation Time" +tEXt chunk use RFC 1123 format dates (e.g. "22 May 1997 18:07:10 GMT"), +although this isn't a requirement. Unlike the tIME chunk, the +"Creation Time" tEXt chunk is not expected to be automatically changed +by the software. To facilitate the use of RFC 1123 dates, a function +png_convert_to_rfc1123(png_timep) is provided to convert from PNG +time to an RFC 1123 format string. + +Writing unknown chunks + +You can use the png_set_unknown_chunks function to queue up chunks +for writing. You give it a chunk name, raw data, and a size; that's +all there is to it. The chunks will be written by the next following +png_write_info_before_PLTE, png_write_info, or png_write_end function. +Any chunks previously read into the info structure's unknown-chunk +list will also be written out in a sequence that satisfies the PNG +specification's ordering rules. + +The high-level write interface + +At this point there are two ways to proceed; through the high-level +write interface, or through a sequence of low-level write operations. +You can use the high-level interface if your image data is present +in the info structure. All defined output +transformations are permitted, enabled by the following masks. + + PNG_TRANSFORM_IDENTITY No transformation + PNG_TRANSFORM_PACKING Pack 1, 2 and 4-bit samples + PNG_TRANSFORM_PACKSWAP Change order of packed + pixels to LSB first + PNG_TRANSFORM_INVERT_MONO Invert monochrome images + PNG_TRANSFORM_SHIFT Normalize pixels to the + sBIT depth + PNG_TRANSFORM_BGR Flip RGB to BGR, RGBA + to BGRA + PNG_TRANSFORM_SWAP_ALPHA Flip RGBA to ARGB or GA + to AG + PNG_TRANSFORM_INVERT_ALPHA Change alpha from opacity + to transparency + PNG_TRANSFORM_SWAP_ENDIAN Byte-swap 16-bit samples + PNG_TRANSFORM_STRIP_FILLER Strip out filler bytes. + +If you have valid image data in the info structure (you can use +png_set_rows() to put image data in the info structure), simply do this: + + png_write_png(png_ptr, info_ptr, png_transforms, NULL) + +where png_transforms is an integer containing the bitwise OR of some set of +transformation flags. This call is equivalent to png_write_info(), +followed the set of transformations indicated by the transform mask, +then png_write_image(), and finally png_write_end(). + +(The final parameter of this call is not yet used. Someday it might point +to transformation parameters required by some future output transform.) + +You must use png_transforms and not call any png_set_transform() functions +when you use png_write_png(). + +The low-level write interface + +If you are going the low-level route instead, you are now ready to +write all the file information up to the actual image data. You do +this with a call to png_write_info(). + + png_write_info(png_ptr, info_ptr); + +Note that there is one transformation you may need to do before +png_write_info(). In PNG files, the alpha channel in an image is the +level of opacity. If your data is supplied as a level of +transparency, you can invert the alpha channel before you write it, so +that 0 is fully transparent and 255 (in 8-bit or paletted images) or +65535 (in 16-bit images) is fully opaque, with + + png_set_invert_alpha(png_ptr); + +This must appear before png_write_info() instead of later with the +other transformations because in the case of paletted images the tRNS +chunk data has to be inverted before the tRNS chunk is written. If +your image is not a paletted image, the tRNS data (which in such cases +represents a single color to be rendered as transparent) won't need to +be changed, and you can safely do this transformation after your +png_write_info() call. + +If you need to write a private chunk that you want to appear before +the PLTE chunk when PLTE is present, you can write the PNG info in +two steps, and insert code to write your own chunk between them: + + png_write_info_before_PLTE(png_ptr, info_ptr); + png_set_unknown_chunks(png_ptr, info_ptr, ...); + png_write_info(png_ptr, info_ptr); + +After you've written the file information, you can set up the library +to handle any special transformations of the image data. The various +ways to transform the data will be described in the order that they +should occur. This is important, as some of these change the color +type and/or bit depth of the data, and some others only work on +certain color types and bit depths. Even though each transformation +checks to see if it has data that it can do something with, you should +make sure to only enable a transformation if it will be valid for the +data. For example, don't swap red and blue on grayscale data. + +PNG files store RGB pixels packed into 3 or 6 bytes. This code tells +the library to strip input data that has 4 or 8 bytes per pixel down +to 3 or 6 bytes (or strip 2 or 4-byte grayscale+filler data to 1 or 2 +bytes per pixel). + + png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE); + +where the 0 is unused, and the location is either PNG_FILLER_BEFORE or +PNG_FILLER_AFTER, depending upon whether the filler byte in the pixel +is stored XRGB or RGBX. + +PNG files pack pixels of bit depths 1, 2, and 4 into bytes as small as +they can, resulting in, for example, 8 pixels per byte for 1 bit files. +If the data is supplied at 1 pixel per byte, use this code, which will +correctly pack the pixels into a single byte: + + png_set_packing(png_ptr); + +PNG files reduce possible bit depths to 1, 2, 4, 8, and 16. If your +data is of another bit depth, you can write an sBIT chunk into the +file so that decoders can recover the original data if desired. + + /* Set the true bit depth of the image data */ + if (color_type & PNG_COLOR_MASK_COLOR) + { + sig_bit.red = true_bit_depth; + sig_bit.green = true_bit_depth; + sig_bit.blue = true_bit_depth; + } + else + { + sig_bit.gray = true_bit_depth; + } + if (color_type & PNG_COLOR_MASK_ALPHA) + { + sig_bit.alpha = true_bit_depth; + } + + png_set_sBIT(png_ptr, info_ptr, &sig_bit); + +If the data is stored in the row buffer in a bit depth other than +one supported by PNG (e.g. 3 bit data in the range 0-7 for a 4-bit PNG), +this will scale the values to appear to be the correct bit depth as +is required by PNG. + + png_set_shift(png_ptr, &sig_bit); + +PNG files store 16 bit pixels in network byte order (big-endian, +ie. most significant bits first). This code would be used if they are +supplied the other way (little-endian, i.e. least significant bits +first, the way PCs store them): + + if (bit_depth > 8) + png_set_swap(png_ptr); + +If you are using packed-pixel images (1, 2, or 4 bits/pixel), and you +need to change the order the pixels are packed into bytes, you can use: + + if (bit_depth < 8) + png_set_packswap(png_ptr); + +PNG files store 3 color pixels in red, green, blue order. This code +would be used if they are supplied as blue, green, red: + + png_set_bgr(png_ptr); + +PNG files describe monochrome as black being zero and white being +one. This code would be used if the pixels are supplied with this reversed +(black being one and white being zero): + + png_set_invert_mono(png_ptr); + +Finally, you can write your own transformation function if none of +the existing ones meets your needs. This is done by setting a callback +with + + png_set_write_user_transform_fn(png_ptr, + write_transform_fn); + +You must supply the function + + void write_transform_fn(png_ptr ptr, row_info_ptr + row_info, png_bytep data) + +See pngtest.c for a working example. Your function will be called +before any of the other transformations are processed. + +You can also set up a pointer to a user structure for use by your +callback function. + + png_set_user_transform_info(png_ptr, user_ptr, 0, 0); + +The user_channels and user_depth parameters of this function are ignored +when writing; you can set them to zero as shown. + +You can retrieve the pointer via the function png_get_user_transform_ptr(). +For example: + + voidp write_user_transform_ptr = + png_get_user_transform_ptr(png_ptr); + +It is possible to have libpng flush any pending output, either manually, +or automatically after a certain number of lines have been written. To +flush the output stream a single time call: + + png_write_flush(png_ptr); + +and to have libpng flush the output stream periodically after a certain +number of scanlines have been written, call: + + png_set_flush(png_ptr, nrows); + +Note that the distance between rows is from the last time png_write_flush() +was called, or the first row of the image if it has never been called. +So if you write 50 lines, and then png_set_flush 25, it will flush the +output on the next scanline, and every 25 lines thereafter, unless +png_write_flush() is called before 25 more lines have been written. +If nrows is too small (less than about 10 lines for a 640 pixel wide +RGB image) the image compression may decrease noticeably (although this +may be acceptable for real-time applications). Infrequent flushing will +only degrade the compression performance by a few percent over images +that do not use flushing. + +Writing the image data + +That's it for the transformations. Now you can write the image data. +The simplest way to do this is in one function call. If you have the +whole image in memory, you can just call png_write_image() and libpng +will write the image. You will need to pass in an array of pointers to +each row. This function automatically handles interlacing, so you don't +need to call png_set_interlace_handling() or call this function multiple +times, or any of that other stuff necessary with png_write_rows(). + + png_write_image(png_ptr, row_pointers); + +where row_pointers is: + + png_byte *row_pointers[height]; + +You can point to void or char or whatever you use for pixels. + +If you don't want to write the whole image at once, you can +use png_write_rows() instead. If the file is not interlaced, +this is simple: + + png_write_rows(png_ptr, row_pointers, + number_of_rows); + +row_pointers is the same as in the png_write_image() call. + +If you are just writing one row at a time, you can do this with +a single row_pointer instead of an array of row_pointers: + + png_bytep row_pointer = row; + + png_write_row(png_ptr, row_pointer); + +When the file is interlaced, things can get a good deal more +complicated. The only currently (as of the PNG Specification +version 1.2, dated July 1999) defined interlacing scheme for PNG files +is the "Adam7" interlace scheme, that breaks down an +image into seven smaller images of varying size. libpng will build +these images for you, or you can do them yourself. If you want to +build them yourself, see the PNG specification for details of which +pixels to write when. + +If you don't want libpng to handle the interlacing details, just +use png_set_interlace_handling() and call png_write_rows() the +correct number of times to write all seven sub-images. + +If you want libpng to build the sub-images, call this before you start +writing any rows: + + number_of_passes = + png_set_interlace_handling(png_ptr); + +This will return the number of passes needed. Currently, this +is seven, but may change if another interlace type is added. + +Then write the complete image number_of_passes times. + + png_write_rows(png_ptr, row_pointers, + number_of_rows); + +As some of these rows are not used, and thus return immediately, +you may want to read about interlacing in the PNG specification, +and only update the rows that are actually used. + +Finishing a sequential write + +After you are finished writing the image, you should finish writing +the file. If you are interested in writing comments or time, you should +pass an appropriately filled png_info pointer. If you are not interested, +you can pass NULL. + + png_write_end(png_ptr, info_ptr); + +When you are done, you can free all memory used by libpng like this: + + png_destroy_write_struct(&png_ptr, &info_ptr); + +It is also possible to individually free the info_ptr members that +point to libpng-allocated storage with the following function: + + png_free_data(png_ptr, info_ptr, mask, seq) + mask - identifies data to be freed, a mask + containing the bitwise OR of one or + more of + PNG_FREE_PLTE, PNG_FREE_TRNS, + PNG_FREE_HIST, PNG_FREE_ICCP, + PNG_FREE_PCAL, PNG_FREE_ROWS, + PNG_FREE_SCAL, PNG_FREE_SPLT, + PNG_FREE_TEXT, PNG_FREE_UNKN, + or simply PNG_FREE_ALL + seq - sequence number of item to be freed + (-1 for all items) + +This function may be safely called when the relevant storage has +already been freed, or has not yet been allocated, or was allocated +by the user and not by libpng, and will in those +cases do nothing. The "seq" parameter is ignored if only one item +of the selected data type, such as PLTE, is allowed. If "seq" is not +-1, and multiple items are allowed for the data type identified in +the mask, such as text or sPLT, only the n'th item in the structure +is freed, where n is "seq". + +If you allocated data such as a palette that you passed +in to libpng with png_set_*, you must not free it until just before the call to +png_destroy_write_struct(). + +The default behavior is only to free data that was allocated internally +by libpng. This can be changed, so that libpng will not free the data, +or so that it will free data that was allocated by the user with png_malloc() +or png_zalloc() and passed in via a png_set_*() function, with + + png_data_freer(png_ptr, info_ptr, freer, mask) + mask - which data elements are affected + same choices as in png_free_data() + freer - one of + PNG_DESTROY_WILL_FREE_DATA + PNG_SET_WILL_FREE_DATA + PNG_USER_WILL_FREE_DATA + +For example, to transfer responsibility for some data from a read structure +to a write structure, you could use + + png_data_freer(read_ptr, read_info_ptr, + PNG_USER_WILL_FREE_DATA, + PNG_FREE_PLTE|PNG_FREE_tRNS|PNG_FREE_hIST) + png_data_freer(write_ptr, write_info_ptr, + PNG_DESTROY_WILL_FREE_DATA, + PNG_FREE_PLTE|PNG_FREE_tRNS|PNG_FREE_hIST) + +thereby briefly reassigning responsibility for freeing to the user but +immediately afterwards reassigning it once more to the write_destroy +function. Having done this, it would then be safe to destroy the read +structure and continue to use the PLTE, tRNS, and hIST data in the write +structure. + +This function only affects data that has already been allocated. +You can call this function before calling after the png_set_*() functions +to control whether the user or png_destroy_*() is supposed to free the data. +When the user assumes responsibility for libpng-allocated data, the +application must use +png_free() to free it, and when the user transfers responsibility to libpng +for data that the user has allocated, the user must have used png_malloc() +or png_zalloc() to allocate it. + +If you allocated text_ptr.text, text_ptr.lang, and text_ptr.translated_keyword +separately, do not transfer responsibility for freeing text_ptr to libpng, +because when libpng fills a png_text structure it combines these members with +the key member, and png_free_data() will free only text_ptr.key. Similarly, +if you transfer responsibility for free'ing text_ptr from libpng to your +application, your application must not separately free those members. +For a more compact example of writing a PNG image, see the file example.c. + +V. Modifying/Customizing libpng: + +There are two issues here. The first is changing how libpng does +standard things like memory allocation, input/output, and error handling. +The second deals with more complicated things like adding new chunks, +adding new transformations, and generally changing how libpng works. +Both of those are compile-time issues; that is, they are generally +determined at the time the code is written, and there is rarely a need +to provide the user with a means of changing them. + +Memory allocation, input/output, and error handling + +All of the memory allocation, input/output, and error handling in libpng +goes through callbacks that are user-settable. The default routines are +in pngmem.c, pngrio.c, pngwio.c, and pngerror.c, respectively. To change +these functions, call the appropriate png_set_*_fn() function. + +Memory allocation is done through the functions png_malloc() +and png_free(). These currently just call the standard C functions. If +your pointers can't access more then 64K at a time, you will want to set +MAXSEG_64K in zlib.h. Since it is unlikely that the method of handling +memory allocation on a platform will change between applications, these +functions must be modified in the library at compile time. If you prefer +to use a different method of allocating and freeing data, you can use +png_create_read_struct_2() or png_create_write_struct_2() to register +your own functions as described above. +These functions also provide a void pointer that can be retrieved via + + mem_ptr=png_get_mem_ptr(png_ptr); + +Your replacement memory functions must have prototypes as follows: + + png_voidp malloc_fn(png_structp png_ptr, + png_size_t size); + void free_fn(png_structp png_ptr, png_voidp ptr); + +Your malloc_fn() must return NULL in case of failure. The png_malloc() +function will normally call png_error() if it receives a NULL from the +system memory allocator or from your replacement malloc_fn(). + +Input/Output in libpng is done through png_read() and png_write(), +which currently just call fread() and fwrite(). The FILE * is stored in +png_struct and is initialized via png_init_io(). If you wish to change +the method of I/O, the library supplies callbacks that you can set +through the function png_set_read_fn() and png_set_write_fn() at run +time, instead of calling the png_init_io() function. These functions +also provide a void pointer that can be retrieved via the function +png_get_io_ptr(). For example: + + png_set_read_fn(png_structp read_ptr, + voidp read_io_ptr, png_rw_ptr read_data_fn) + + png_set_write_fn(png_structp write_ptr, + voidp write_io_ptr, png_rw_ptr write_data_fn, + png_flush_ptr output_flush_fn); + + voidp read_io_ptr = png_get_io_ptr(read_ptr); + voidp write_io_ptr = png_get_io_ptr(write_ptr); + +The replacement I/O functions must have prototypes as follows: + + void user_read_data(png_structp png_ptr, + png_bytep data, png_size_t length); + void user_write_data(png_structp png_ptr, + png_bytep data, png_size_t length); + void user_flush_data(png_structp png_ptr); + +Supplying NULL for the read, write, or flush functions sets them back +to using the default C stream functions. It is an error to read from +a write stream, and vice versa. + +Error handling in libpng is done through png_error() and png_warning(). +Errors handled through png_error() are fatal, meaning that png_error() +should never return to its caller. Currently, this is handled via +setjmp() and longjmp() (unless you have compiled libpng with +PNG_SETJMP_NOT_SUPPORTED, in which case it is handled via PNG_ABORT()), +but you could change this to do things like exit() if you should wish. + +On non-fatal errors, png_warning() is called +to print a warning message, and then control returns to the calling code. +By default png_error() and png_warning() print a message on stderr via +fprintf() unless the library is compiled with PNG_NO_CONSOLE_IO defined +(because you don't want the messages) or PNG_NO_STDIO defined (because +fprintf() isn't available). If you wish to change the behavior of the error +functions, you will need to set up your own message callbacks. These +functions are normally supplied at the time that the png_struct is created. +It is also possible to redirect errors and warnings to your own replacement +functions after png_create_*_struct() has been called by calling: + + png_set_error_fn(png_structp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warning_fn); + + png_voidp error_ptr = png_get_error_ptr(png_ptr); + +If NULL is supplied for either error_fn or warning_fn, then the libpng +default function will be used, calling fprintf() and/or longjmp() if a +problem is encountered. The replacement error functions should have +parameters as follows: + + void user_error_fn(png_structp png_ptr, + png_const_charp error_msg); + void user_warning_fn(png_structp png_ptr, + png_const_charp warning_msg); + +The motivation behind using setjmp() and longjmp() is the C++ throw and +catch exception handling methods. This makes the code much easier to write, +as there is no need to check every return code of every function call. +However, there are some uncertainties about the status of local variables +after a longjmp, so the user may want to be careful about doing anything after +setjmp returns non-zero besides returning itself. Consult your compiler +documentation for more details. For an alternative approach, you may wish +to use the "cexcept" facility (see http://cexcept.sourceforge.net). + +Custom chunks + +If you need to read or write custom chunks, you may need to get deeper +into the libpng code. The library now has mechanisms for storing +and writing chunks of unknown type; you can even declare callbacks +for custom chunks. However, this may not be good enough if the +library code itself needs to know about interactions between your +chunk and existing `intrinsic' chunks. + +If you need to write a new intrinsic chunk, first read the PNG +specification. Acquire a first level of +understanding of how it works. Pay particular attention to the +sections that describe chunk names, and look at how other chunks were +designed, so you can do things similarly. Second, check out the +sections of libpng that read and write chunks. Try to find a chunk +that is similar to yours and use it as a template. More details can +be found in the comments inside the code. It is best to handle unknown +chunks in a generic method, via callback functions, instead of by +modifying libpng functions. + +If you wish to write your own transformation for the data, look through +the part of the code that does the transformations, and check out some of +the simpler ones to get an idea of how they work. Try to find a similar +transformation to the one you want to add and copy off of it. More details +can be found in the comments inside the code itself. + +Configuring for 16 bit platforms + +You will want to look into zconf.h to tell zlib (and thus libpng) that +it cannot allocate more then 64K at a time. Even if you can, the memory +won't be accessible. So limit zlib and libpng to 64K by defining MAXSEG_64K. + +Configuring for DOS + +For DOS users who only have access to the lower 640K, you will +have to limit zlib's memory usage via a png_set_compression_mem_level() +call. See zlib.h or zconf.h in the zlib library for more information. + +Configuring for Medium Model + +Libpng's support for medium model has been tested on most of the popular +compilers. Make sure MAXSEG_64K gets defined, USE_FAR_KEYWORD gets +defined, and FAR gets defined to far in pngconf.h, and you should be +all set. Everything in the library (except for zlib's structure) is +expecting far data. You must use the typedefs with the p or pp on +the end for pointers (or at least look at them and be careful). Make +note that the rows of data are defined as png_bytepp, which is an +unsigned char far * far *. + +Configuring for gui/windowing platforms: + +You will need to write new error and warning functions that use the GUI +interface, as described previously, and set them to be the error and +warning functions at the time that png_create_*_struct() is called, +in order to have them available during the structure initialization. +They can be changed later via png_set_error_fn(). On some compilers, +you may also have to change the memory allocators (png_malloc, etc.). + +Configuring for compiler xxx: + +All includes for libpng are in pngconf.h. If you need to add/change/delete +an include, this is the place to do it. The includes that are not +needed outside libpng are protected by the PNG_INTERNAL definition, +which is only defined for those routines inside libpng itself. The +files in libpng proper only include png.h, which includes pngconf.h. + +Configuring zlib: + +There are special functions to configure the compression. Perhaps the +most useful one changes the compression level, which currently uses +input compression values in the range 0 - 9. The library normally +uses the default compression level (Z_DEFAULT_COMPRESSION = 6). Tests +have shown that for a large majority of images, compression values in +the range 3-6 compress nearly as well as higher levels, and do so much +faster. For online applications it may be desirable to have maximum speed +(Z_BEST_SPEED = 1). With versions of zlib after v0.99, you can also +specify no compression (Z_NO_COMPRESSION = 0), but this would create +files larger than just storing the raw bitmap. You can specify the +compression level by calling: + + png_set_compression_level(png_ptr, level); + +Another useful one is to reduce the memory level used by the library. +The memory level defaults to 8, but it can be lowered if you are +short on memory (running DOS, for example, where you only have 640K). +Note that the memory level does have an effect on compression; among +other things, lower levels will result in sections of incompressible +data being emitted in smaller stored blocks, with a correspondingly +larger relative overhead of up to 15% in the worst case. + + png_set_compression_mem_level(png_ptr, level); + +The other functions are for configuring zlib. They are not recommended +for normal use and may result in writing an invalid PNG file. See +zlib.h for more information on what these mean. + + png_set_compression_strategy(png_ptr, + strategy); + png_set_compression_window_bits(png_ptr, + window_bits); + png_set_compression_method(png_ptr, method); + png_set_compression_buffer_size(png_ptr, size); + +Controlling row filtering + +If you want to control whether libpng uses filtering or not, which +filters are used, and how it goes about picking row filters, you +can call one of these functions. The selection and configuration +of row filters can have a significant impact on the size and +encoding speed and a somewhat lesser impact on the decoding speed +of an image. Filtering is enabled by default for RGB and grayscale +images (with and without alpha), but not for paletted images nor +for any images with bit depths less than 8 bits/pixel. + +The 'method' parameter sets the main filtering method, which is +currently only '0' in the PNG 1.2 specification. The 'filters' +parameter sets which filter(s), if any, should be used for each +scanline. Possible values are PNG_ALL_FILTERS and PNG_NO_FILTERS +to turn filtering on and off, respectively. + +Individual filter types are PNG_FILTER_NONE, PNG_FILTER_SUB, +PNG_FILTER_UP, PNG_FILTER_AVG, PNG_FILTER_PAETH, which can be bitwise +ORed together with '|' to specify one or more filters to use. +These filters are described in more detail in the PNG specification. +If you intend to change the filter type during the course of writing +the image, you should start with flags set for all of the filters +you intend to use so that libpng can initialize its internal +structures appropriately for all of the filter types. (Note that this +means the first row must always be adaptively filtered, because libpng +currently does not allocate the filter buffers until png_write_row() +is called for the first time.) + + filters = PNG_FILTER_NONE | PNG_FILTER_SUB + PNG_FILTER_UP | PNG_FILTER_AVE | + PNG_FILTER_PAETH | PNG_ALL_FILTERS; + + png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, + filters); + The second parameter can also be + PNG_INTRAPIXEL_DIFFERENCING if you are + writing a PNG to be embedded in a MNG + datastream. This parameter must be the + same as the value of filter_method used + in png_set_IHDR(). + +It is also possible to influence how libpng chooses from among the +available filters. This is done in one or both of two ways - by +telling it how important it is to keep the same filter for successive +rows, and by telling it the relative computational costs of the filters. + + double weights[3] = {1.5, 1.3, 1.1}, + costs[PNG_FILTER_VALUE_LAST] = + {1.0, 1.3, 1.3, 1.5, 1.7}; + + png_set_filter_heuristics(png_ptr, + PNG_FILTER_HEURISTIC_WEIGHTED, 3, + weights, costs); + +The weights are multiplying factors that indicate to libpng that the +row filter should be the same for successive rows unless another row filter +is that many times better than the previous filter. In the above example, +if the previous 3 filters were SUB, SUB, NONE, the SUB filter could have a +"sum of absolute differences" 1.5 x 1.3 times higher than other filters +and still be chosen, while the NONE filter could have a sum 1.1 times +higher than other filters and still be chosen. Unspecified weights are +taken to be 1.0, and the specified weights should probably be declining +like those above in order to emphasize recent filters over older filters. + +The filter costs specify for each filter type a relative decoding cost +to be considered when selecting row filters. This means that filters +with higher costs are less likely to be chosen over filters with lower +costs, unless their "sum of absolute differences" is that much smaller. +The costs do not necessarily reflect the exact computational speeds of +the various filters, since this would unduly influence the final image +size. + +Note that the numbers above were invented purely for this example and +are given only to help explain the function usage. Little testing has +been done to find optimum values for either the costs or the weights. + +Removing unwanted object code + +There are a bunch of #define's in pngconf.h that control what parts of +libpng are compiled. All the defines end in _SUPPORTED. If you are +never going to use a capability, you can change the #define to #undef +before recompiling libpng and save yourself code and data space, or +you can turn off individual capabilities with defines that begin with +PNG_NO_. + +You can also turn all of the transforms and ancillary chunk capabilities +off en masse with compiler directives that define +PNG_NO_READ[or WRITE]_TRANSFORMS, or PNG_NO_READ[or WRITE]_ANCILLARY_CHUNKS, +or all four, +along with directives to turn on any of the capabilities that you do +want. The PNG_NO_READ[or WRITE]_TRANSFORMS directives disable +the extra transformations but still leave the library fully capable of reading +and writing PNG files with all known public chunks +Use of the PNG_NO_READ[or WRITE]_ANCILLARY_CHUNKS directive +produces a library that is incapable of reading or writing ancillary chunks. +If you are not using the progressive reading capability, you can +turn that off with PNG_NO_PROGRESSIVE_READ (don't confuse +this with the INTERLACING capability, which you'll still have). + +All the reading and writing specific code are in separate files, so the +linker should only grab the files it needs. However, if you want to +make sure, or if you are building a stand alone library, all the +reading files start with pngr and all the writing files start with +pngw. The files that don't match either (like png.c, pngtrans.c, etc.) +are used for both reading and writing, and always need to be included. +The progressive reader is in pngpread.c + +If you are creating or distributing a dynamically linked library (a .so +or DLL file), you should not remove or disable any parts of the library, +as this will cause applications linked with different versions of the +library to fail if they call functions not available in your library. +The size of the library itself should not be an issue, because only +those sections that are actually used will be loaded into memory. + +Requesting debug printout + +The macro definition PNG_DEBUG can be used to request debugging +printout. Set it to an integer value in the range 0 to 3. Higher +numbers result in increasing amounts of debugging information. The +information is printed to the "stderr" file, unless another file +name is specified in the PNG_DEBUG_FILE macro definition. + +When PNG_DEBUG > 0, the following functions (macros) become available: + + png_debug(level, message) + png_debug1(level, message, p1) + png_debug2(level, message, p1, p2) + +in which "level" is compared to PNG_DEBUG to decide whether to print +the message, "message" is the formatted string to be printed, +and p1 and p2 are parameters that are to be embedded in the string +according to printf-style formatting directives. For example, + + png_debug1(2, "foo=%d\n", foo); + +is expanded to + + if(PNG_DEBUG > 2) + fprintf(PNG_DEBUG_FILE, "foo=%d\n", foo); + +When PNG_DEBUG is defined but is zero, the macros aren't defined, but you +can still use PNG_DEBUG to control your own debugging: + + #ifdef PNG_DEBUG + fprintf(stderr, ... + #endif + +When PNG_DEBUG = 1, the macros are defined, but only png_debug statements +having level = 0 will be printed. There aren't any such statements in +this version of libpng, but if you insert some they will be printed. + +VII. MNG support + +The MNG specification (available at http://www.libpng.org/pub/mng) allows +certain extensions to PNG for PNG images that are embedded in MNG datastreams. +Libpng can support some of these extensions. To enable them, use the +png_permit_mng_features() function: + + feature_set = png_permit_mng_features(png_ptr, mask) + mask is a png_uint_32 containing the bitwise OR of the + features you want to enable. These include + PNG_FLAG_MNG_EMPTY_PLTE + PNG_FLAG_MNG_FILTER_64 + PNG_ALL_MNG_FEATURES + feature_set is a png_uint_32 that is the bitwise AND of + your mask with the set of MNG features that is + supported by the version of libpng that you are using. + +It is an error to use this function when reading or writing a standalone +PNG file with the PNG 8-byte signature. The PNG datastream must be wrapped +in a MNG datastream. As a minimum, it must have the MNG 8-byte signature +and the MHDR and MEND chunks. Libpng does not provide support for these +or any other MNG chunks; your application must provide its own support for +them. You may wish to consider using libmng (available at +http://www.libmng.com) instead. + +VIII. Changes to Libpng from version 0.88 + +It should be noted that versions of libpng later than 0.96 are not +distributed by the original libpng author, Guy Schalnat, nor by +Andreas Dilger, who had taken over from Guy during 1996 and 1997, and +distributed versions 0.89 through 0.96, but rather by another member +of the original PNG Group, Glenn Randers-Pehrson. Guy and Andreas are +still alive and well, but they have moved on to other things. + +The old libpng functions png_read_init(), png_write_init(), +png_info_init(), png_read_destroy(), and png_write_destroy() have been +moved to PNG_INTERNAL in version 0.95 to discourage their use. These +functions will be removed from libpng version 2.0.0. + +The preferred method of creating and initializing the libpng structures is +via the png_create_read_struct(), png_create_write_struct(), and +png_create_info_struct() because they isolate the size of the structures +from the application, allow version error checking, and also allow the +use of custom error handling routines during the initialization, which +the old functions do not. The functions png_read_destroy() and +png_write_destroy() do not actually free the memory that libpng +allocated for these structs, but just reset the data structures, so they +can be used instead of png_destroy_read_struct() and +png_destroy_write_struct() if you feel there is too much system overhead +allocating and freeing the png_struct for each image read. + +Setting the error callbacks via png_set_message_fn() before +png_read_init() as was suggested in libpng-0.88 is no longer supported +because this caused applications that do not use custom error functions +to fail if the png_ptr was not initialized to zero. It is still possible +to set the error callbacks AFTER png_read_init(), or to change them with +png_set_error_fn(), which is essentially the same function, but with a new +name to force compilation errors with applications that try to use the old +method. + +Starting with version 1.0.7, you can find out which version of the library +you are using at run-time: + + png_uint_32 libpng_vn = png_access_version_number(); + +The number libpng_vn is constructed from the major version, minor +version with leading zero, and release number with leading zero, +(e.g., libpng_vn for version 1.0.7 is 10007). + +You can also check which version of png.h you used when compiling your +application: + + png_uint_32 application_vn = PNG_LIBPNG_VER; + +IX. Y2K Compliance in libpng + +December 14, 2007 + +Since the PNG Development group is an ad-hoc body, we can't make +an official declaration. + +This is your unofficial assurance that libpng from version 0.71 and +upward through 1.2.24 are Y2K compliant. It is my belief that earlier +versions were also Y2K compliant. + +Libpng only has three year fields. One is a 2-byte unsigned integer that +will hold years up to 65535. The other two hold the date in text +format, and will hold years up to 9999. + +The integer is + "png_uint_16 year" in png_time_struct. + +The strings are + "png_charp time_buffer" in png_struct and + "near_time_buffer", which is a local character string in png.c. + +There are seven time-related functions: + + png_convert_to_rfc_1123() in png.c + (formerly png_convert_to_rfc_1152() in error) + png_convert_from_struct_tm() in pngwrite.c, called + in pngwrite.c + png_convert_from_time_t() in pngwrite.c + png_get_tIME() in pngget.c + png_handle_tIME() in pngrutil.c, called in pngread.c + png_set_tIME() in pngset.c + png_write_tIME() in pngwutil.c, called in pngwrite.c + +All appear to handle dates properly in a Y2K environment. The +png_convert_from_time_t() function calls gmtime() to convert from system +clock time, which returns (year - 1900), which we properly convert to +the full 4-digit year. There is a possibility that applications using +libpng are not passing 4-digit years into the png_convert_to_rfc_1123() +function, or that they are incorrectly passing only a 2-digit year +instead of "year - 1900" into the png_convert_from_struct_tm() function, +but this is not under our control. The libpng documentation has always +stated that it works with 4-digit years, and the APIs have been +documented as such. + +The tIME chunk itself is also Y2K compliant. It uses a 2-byte unsigned +integer to hold the year, and can hold years as large as 65535. + +zlib, upon which libpng depends, is also Y2K compliant. It contains +no date-related code. + + + Glenn Randers-Pehrson + libpng maintainer + PNG Development Group diff --git a/Engine/lib/lpng/libpng.3 b/Engine/lib/lpng/libpng.3 new file mode 100644 index 000000000..060cdc142 --- /dev/null +++ b/Engine/lib/lpng/libpng.3 @@ -0,0 +1,3607 @@ +.TH LIBPNG 3 "December 14, 2007" +.SH NAME +libpng \- Portable Network Graphics (PNG) Reference Library 1.2.24 +.SH SYNOPSIS +\fB +#include \fP + +\fBpng_uint_32 png_access_version_number \fI(void\fP\fB);\fP + +\fBint png_check_sig (png_bytep \fP\fIsig\fP\fB, int \fInum\fP\fB);\fP + +\fBvoid png_chunk_error (png_structp \fP\fIpng_ptr\fP\fB, png_const_charp \fIerror\fP\fB);\fP + +\fBvoid png_chunk_warning (png_structp \fP\fIpng_ptr\fP\fB, png_const_charp \fImessage\fP\fB);\fP + +\fBvoid png_convert_from_struct_tm (png_timep \fP\fIptime\fP\fB, struct tm FAR * \fIttime\fP\fB);\fP + +\fBvoid png_convert_from_time_t (png_timep \fP\fIptime\fP\fB, time_t \fIttime\fP\fB);\fP + +\fBpng_charp png_convert_to_rfc1123 (png_structp \fP\fIpng_ptr\fP\fB, png_timep \fIptime\fP\fB);\fP + +\fBpng_infop png_create_info_struct (png_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_structp png_create_read_struct (png_const_charp \fP\fIuser_png_ver\fP\fB, png_voidp \fP\fIerror_ptr\fP\fB, png_error_ptr \fP\fIerror_fn\fP\fB, png_error_ptr \fIwarn_fn\fP\fB);\fP + +\fBpng_structp png_create_read_struct_2(png_const_charp \fP\fIuser_png_ver\fP\fB, png_voidp \fP\fIerror_ptr\fP\fB, png_error_ptr \fP\fIerror_fn\fP\fB, png_error_ptr \fP\fIwarn_fn\fP\fB, png_voidp \fP\fImem_ptr\fP\fB, png_malloc_ptr \fP\fImalloc_fn\fP\fB, png_free_ptr \fIfree_fn\fP\fB);\fP + +\fBpng_structp png_create_write_struct (png_const_charp \fP\fIuser_png_ver\fP\fB, png_voidp \fP\fIerror_ptr\fP\fB, png_error_ptr \fP\fIerror_fn\fP\fB, png_error_ptr \fIwarn_fn\fP\fB);\fP + +\fBpng_structp png_create_write_struct_2(png_const_charp \fP\fIuser_png_ver\fP\fB, png_voidp \fP\fIerror_ptr\fP\fB, png_error_ptr \fP\fIerror_fn\fP\fB, png_error_ptr \fP\fIwarn_fn\fP\fB, png_voidp \fP\fImem_ptr\fP\fB, png_malloc_ptr \fP\fImalloc_fn\fP\fB, png_free_ptr \fIfree_fn\fP\fB);\fP + +\fBint png_debug(int \fP\fIlevel\fP\fB, png_const_charp \fImessage\fP\fB);\fP + +\fBint png_debug1(int \fP\fIlevel\fP\fB, png_const_charp \fP\fImessage\fP\fB, \fIp1\fP\fB);\fP + +\fBint png_debug2(int \fP\fIlevel\fP\fB, png_const_charp \fP\fImessage\fP\fB, \fP\fIp1\fP\fB, \fIp2\fP\fB);\fP + +\fBvoid png_destroy_info_struct (png_structp \fP\fIpng_ptr\fP\fB, png_infopp \fIinfo_ptr_ptr\fP\fB);\fP + +\fBvoid png_destroy_read_struct (png_structpp \fP\fIpng_ptr_ptr\fP\fB, png_infopp \fP\fIinfo_ptr_ptr\fP\fB, png_infopp \fIend_info_ptr_ptr\fP\fB);\fP + +\fBvoid png_destroy_write_struct (png_structpp \fP\fIpng_ptr_ptr\fP\fB, png_infopp \fIinfo_ptr_ptr\fP\fB);\fP + +\fBvoid png_error (png_structp \fP\fIpng_ptr\fP\fB, png_const_charp \fIerror\fP\fB);\fP + +\fBvoid png_free (png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fIptr\fP\fB);\fP + +\fBvoid png_free_chunk_list (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_free_default(png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fIptr\fP\fB);\fP + +\fBvoid png_free_data (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, int \fInum\fP\fB);\fP + +\fBpng_byte png_get_bit_depth (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_bKGD (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_color_16p \fI*background\fP\fB);\fP + +\fBpng_byte png_get_channels (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_cHRM (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, double \fP\fI*white_x\fP\fB, double \fP\fI*white_y\fP\fB, double \fP\fI*red_x\fP\fB, double \fP\fI*red_y\fP\fB, double \fP\fI*green_x\fP\fB, double \fP\fI*green_y\fP\fB, double \fP\fI*blue_x\fP\fB, double \fI*blue_y\fP\fB);\fP + +\fBpng_uint_32 png_get_cHRM_fixed (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fP\fI*white_x\fP\fB, png_uint_32 \fP\fI*white_y\fP\fB, png_uint_32 \fP\fI*red_x\fP\fB, png_uint_32 \fP\fI*red_y\fP\fB, png_uint_32 \fP\fI*green_x\fP\fB, png_uint_32 \fP\fI*green_y\fP\fB, png_uint_32 \fP\fI*blue_x\fP\fB, png_uint_32 \fI*blue_y\fP\fB);\fP + +\fBpng_byte png_get_color_type (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_byte png_get_compression_type (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_byte png_get_copyright (png_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_voidp png_get_error_ptr (png_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_byte png_get_filter_type (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_gAMA (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, double \fI*file_gamma\fP\fB);\fP + +\fBpng_uint_32 png_get_gAMA_fixed (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fI*int_file_gamma\fP\fB);\fP + +\fBpng_byte png_get_header_ver (png_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_byte png_get_header_version (png_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_hIST (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_16p \fI*hist\fP\fB);\fP + +\fBpng_uint_32 png_get_iCCP (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_charpp \fP\fIname\fP\fB, int \fP\fI*compression_type\fP\fB, png_charpp \fP\fIprofile\fP\fB, png_uint_32 \fI*proflen\fP\fB);\fP + +\fBpng_uint_32 png_get_IHDR (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fP\fI*width\fP\fB, png_uint_32 \fP\fI*height\fP\fB, int \fP\fI*bit_depth\fP\fB, int \fP\fI*color_type\fP\fB, int \fP\fI*interlace_type\fP\fB, int \fP\fI*compression_type\fP\fB, int \fI*filter_type\fP\fB);\fP + +\fBpng_uint_32 png_get_image_height (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_image_width (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fB#if !defined(PNG_1_0_X) png_int_32 png_get_int_32 (png_bytep buf); \fI#endif + +\fBpng_byte png_get_interlace_type (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_voidp png_get_io_ptr (png_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_byte png_get_libpng_ver (png_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_voidp png_get_mem_ptr(png_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_oFFs (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fP\fI*offset_x\fP\fB, png_uint_32 \fP\fI*offset_y\fP\fB, int \fI*unit_type\fP\fB);\fP + +\fBpng_uint_32 png_get_pCAL (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_charp \fP\fI*purpose\fP\fB, png_int_32 \fP\fI*X0\fP\fB, png_int_32 \fP\fI*X1\fP\fB, int \fP\fI*type\fP\fB, int \fP\fI*nparams\fP\fB, png_charp \fP\fI*units\fP\fB, png_charpp \fI*params\fP\fB);\fP + +\fBpng_uint_32 png_get_pHYs (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fP\fI*res_x\fP\fB, png_uint_32 \fP\fI*res_y\fP\fB, int \fI*unit_type\fP\fB);\fP + +\fBfloat png_get_pixel_aspect_ratio (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_pixels_per_meter (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_voidp png_get_progressive_ptr (png_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_PLTE (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_colorp \fP\fI*palette\fP\fB, int \fI*num_palette\fP\fB);\fP + +\fBpng_byte png_get_rgb_to_gray_status (png_structp png_ptr) png_uint_32 png_get_rowbytes (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_bytepp png_get_rows (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_sBIT (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_color_8p \fI*sig_bit\fP\fB);\fP + +\fBpng_bytep png_get_signature (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_sPLT (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_spalette_p \fI*splt_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_sRGB (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, int \fI*intent\fP\fB);\fP + +\fBpng_uint_32 png_get_text (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_textp \fP\fI*text_ptr\fP\fB, int \fI*num_text\fP\fB);\fP + +\fBpng_uint_32 png_get_tIME (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_timep \fI*mod_time\fP\fB);\fP + +\fBpng_uint_32 png_get_tRNS (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_bytep \fP\fI*trans\fP\fB, int \fP\fI*num_trans\fP\fB, png_color_16p \fI*trans_values\fP\fB);\fP + +\fB#if !defined(PNG_1_0_X) png_uint_16 png_get_uint_16 (png_bytep \fIbuf\fP\fB);\fP + +\fBpng_uint_32 png_get_uint_31 (png_bytep \fIbuf\fP\fB);\fP + +\fBpng_uint_32 png_get_uint_32 (png_bytep buf); \fI#endif + +\fBpng_uint_32 png_get_unknown_chunks (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_unknown_chunkpp \fIunknowns\fP\fB);\fP + +\fBpng_voidp png_get_user_chunk_ptr (png_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_user_height_max( png_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_voidp png_get_user_transform_ptr (png_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_user_width_max (png_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_valid (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIflag\fP\fB);\fP + +\fBpng_int_32 png_get_x_offset_microns (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_int_32 png_get_x_offset_pixels (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_x_pixels_per_meter (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_int_32 png_get_y_offset_microns (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_int_32 png_get_y_offset_pixels (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_y_pixels_per_meter (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBpng_uint_32 png_get_compression_buffer_size (png_structp \fIpng_ptr\fP\fB);\fP + +\fBint png_handle_as_unknown (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fIchunk_name\fP\fB);\fP + +\fBvoid png_init_io (png_structp \fP\fIpng_ptr\fP\fB, FILE \fI*fp\fP\fB);\fP + +\fBDEPRECATED: void png_info_init (png_infop \fIinfo_ptr\fP\fB);\fP + +\fBDEPRECATED: void png_info_init_2 (png_infopp \fP\fIptr_ptr\fP\fB, png_size_t \fIpng_info_struct_size\fP\fB);\fP + +\fBpng_voidp png_malloc (png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fIsize\fP\fB);\fP + +\fBpng_voidp png_malloc_default(png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fIsize\fP\fB);\fP + +\fBvoidp png_memcpy (png_voidp \fP\fIs1\fP\fB, png_voidp \fP\fIs2\fP\fB, png_size_t \fIsize\fP\fB);\fP + +\fBpng_voidp png_memcpy_check (png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fP\fIs1\fP\fB, png_voidp \fP\fIs2\fP\fB, png_uint_32 \fIsize\fP\fB);\fP + +\fBvoidp png_memset (png_voidp \fP\fIs1\fP\fB, int \fP\fIvalue\fP\fB, png_size_t \fIsize\fP\fB);\fP + +\fBpng_voidp png_memset_check (png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fP\fIs1\fP\fB, int \fP\fIvalue\fP\fB, png_uint_32 \fIsize\fP\fB);\fP + +\fBDEPRECATED: void png_permit_empty_plte (png_structp \fP\fIpng_ptr\fP\fB, int \fIempty_plte_permitted\fP\fB);\fP + +\fBvoid png_process_data (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_bytep \fP\fIbuffer\fP\fB, png_size_t \fIbuffer_size\fP\fB);\fP + +\fBvoid png_progressive_combine_row (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fP\fIold_row\fP\fB, png_bytep \fInew_row\fP\fB);\fP + +\fBvoid png_read_destroy (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_infop \fIend_info_ptr\fP\fB);\fP + +\fBvoid png_read_end (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBvoid png_read_image (png_structp \fP\fIpng_ptr\fP\fB, png_bytepp \fIimage\fP\fB);\fP + +\fBDEPRECATED: void png_read_init (png_structp \fIpng_ptr\fP\fB);\fP + +\fBDEPRECATED: void png_read_init_2 (png_structpp \fP\fIptr_ptr\fP\fB, png_const_charp \fP\fIuser_png_ver\fP\fB, png_size_t \fP\fIpng_struct_size\fP\fB, png_size_t \fIpng_info_size\fP\fB);\fP + +\fBvoid png_read_info (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBvoid png_read_png (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, int \fP\fItransforms\fP\fB, png_voidp \fIparams\fP\fB);\fP + +\fBvoid png_read_row (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fP\fIrow\fP\fB, png_bytep \fIdisplay_row\fP\fB);\fP + +\fBvoid png_read_rows (png_structp \fP\fIpng_ptr\fP\fB, png_bytepp \fP\fIrow\fP\fB, png_bytepp \fP\fIdisplay_row\fP\fB, png_uint_32 \fInum_rows\fP\fB);\fP + +\fBvoid png_read_update_info (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fB#if !defined(PNG_1_0_X) png_save_int_32 (png_bytep \fP\fIbuf\fP\fB, png_int_32 \fIi\fP\fB);\fP + +\fBvoid png_save_uint_16 (png_bytep \fP\fIbuf\fP\fB, unsigned int \fIi\fP\fB);\fP + +\fBvoid png_save_uint_32 (png_bytep \fP\fIbuf\fP\fB, png_uint_32 \fIi\fP\fB);\fP + +\fBvoid png_set_add_alpha (png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fP\fIfiller\fP\fB, int flags); \fI#endif + +\fBvoid png_set_background (png_structp \fP\fIpng_ptr\fP\fB, png_color_16p \fP\fIbackground_color\fP\fB, int \fP\fIbackground_gamma_code\fP\fB, int \fP\fIneed_expand\fP\fB, double \fIbackground_gamma\fP\fB);\fP + +\fBvoid png_set_bgr (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_bKGD (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_color_16p \fIbackground\fP\fB);\fP + +\fBvoid png_set_cHRM (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, double \fP\fIwhite_x\fP\fB, double \fP\fIwhite_y\fP\fB, double \fP\fIred_x\fP\fB, double \fP\fIred_y\fP\fB, double \fP\fIgreen_x\fP\fB, double \fP\fIgreen_y\fP\fB, double \fP\fIblue_x\fP\fB, double \fIblue_y\fP\fB);\fP + +\fBvoid png_set_cHRM_fixed (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fP\fIwhite_x\fP\fB, png_uint_32 \fP\fIwhite_y\fP\fB, png_uint_32 \fP\fIred_x\fP\fB, png_uint_32 \fP\fIred_y\fP\fB, png_uint_32 \fP\fIgreen_x\fP\fB, png_uint_32 \fP\fIgreen_y\fP\fB, png_uint_32 \fP\fIblue_x\fP\fB, png_uint_32 \fIblue_y\fP\fB);\fP + +\fBvoid png_set_compression_level (png_structp \fP\fIpng_ptr\fP\fB, int \fIlevel\fP\fB);\fP + +\fBvoid png_set_compression_mem_level (png_structp \fP\fIpng_ptr\fP\fB, int \fImem_level\fP\fB);\fP + +\fBvoid png_set_compression_method (png_structp \fP\fIpng_ptr\fP\fB, int \fImethod\fP\fB);\fP + +\fBvoid png_set_compression_strategy (png_structp \fP\fIpng_ptr\fP\fB, int \fIstrategy\fP\fB);\fP + +\fBvoid png_set_compression_window_bits (png_structp \fP\fIpng_ptr\fP\fB, int \fIwindow_bits\fP\fB);\fP + +\fBvoid png_set_crc_action (png_structp \fP\fIpng_ptr\fP\fB, int \fP\fIcrit_action\fP\fB, int \fIancil_action\fP\fB);\fP + +\fBvoid png_set_dither (png_structp \fP\fIpng_ptr\fP\fB, png_colorp \fP\fIpalette\fP\fB, int \fP\fInum_palette\fP\fB, int \fP\fImaximum_colors\fP\fB, png_uint_16p \fP\fIhistogram\fP\fB, int \fIfull_dither\fP\fB);\fP + +\fBvoid png_set_error_fn (png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fP\fIerror_ptr\fP\fB, png_error_ptr \fP\fIerror_fn\fP\fB, png_error_ptr \fIwarning_fn\fP\fB);\fP + +\fBvoid png_set_expand (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_expand_gray_1_2_4_to_8(png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_filler (png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fP\fIfiller\fP\fB, int \fIflags\fP\fB);\fP + +\fBvoid png_set_filter (png_structp \fP\fIpng_ptr\fP\fB, int \fP\fImethod\fP\fB, int \fIfilters\fP\fB);\fP + +\fBvoid png_set_filter_heuristics (png_structp \fP\fIpng_ptr\fP\fB, int \fP\fIheuristic_method\fP\fB, int \fP\fInum_weights\fP\fB, png_doublep \fP\fIfilter_weights\fP\fB, png_doublep \fIfilter_costs\fP\fB);\fP + +\fBvoid png_set_flush (png_structp \fP\fIpng_ptr\fP\fB, int \fInrows\fP\fB);\fP + +\fBvoid png_set_gamma (png_structp \fP\fIpng_ptr\fP\fB, double \fP\fIscreen_gamma\fP\fB, double \fIdefault_file_gamma\fP\fB);\fP + +\fBvoid png_set_gAMA (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, double \fIfile_gamma\fP\fB);\fP + +\fBvoid png_set_gAMA_fixed (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIfile_gamma\fP\fB);\fP + +\fBvoid png_set_gray_1_2_4_to_8(png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_gray_to_rgb (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_hIST (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_16p \fIhist\fP\fB);\fP + +\fBvoid png_set_iCCP (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_charp \fP\fIname\fP\fB, int \fP\fIcompression_type\fP\fB, png_charp \fP\fIprofile\fP\fB, png_uint_32 \fIproflen\fP\fB);\fP + +\fBint png_set_interlace_handling (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_invalid (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, int \fImask\fP\fB);\fP + +\fBvoid png_set_invert_alpha (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_invert_mono (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_IHDR (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fP\fIwidth\fP\fB, png_uint_32 \fP\fIheight\fP\fB, int \fP\fIbit_depth\fP\fB, int \fP\fIcolor_type\fP\fB, int \fP\fIinterlace_type\fP\fB, int \fP\fIcompression_type\fP\fB, int \fIfilter_type\fP\fB);\fP + +\fBvoid png_set_keep_unknown_chunks (png_structp \fP\fIpng_ptr\fP\fB, int \fP\fIkeep\fP\fB, png_bytep \fP\fIchunk_list\fP\fB, int \fInum_chunks\fP\fB);\fP + +\fBvoid png_set_mem_fn(png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fP\fImem_ptr\fP\fB, png_malloc_ptr \fP\fImalloc_fn\fP\fB, png_free_ptr \fIfree_fn\fP\fB);\fP + +\fBvoid png_set_oFFs (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fP\fIoffset_x\fP\fB, png_uint_32 \fP\fIoffset_y\fP\fB, int \fIunit_type\fP\fB);\fP + +\fBvoid png_set_packing (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_packswap (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_palette_to_rgb(png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_pCAL (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_charp \fP\fIpurpose\fP\fB, png_int_32 \fP\fIX0\fP\fB, png_int_32 \fP\fIX1\fP\fB, int \fP\fItype\fP\fB, int \fP\fInparams\fP\fB, png_charp \fP\fIunits\fP\fB, png_charpp \fIparams\fP\fB);\fP + +\fBvoid png_set_pHYs (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fP\fIres_x\fP\fB, png_uint_32 \fP\fIres_y\fP\fB, int \fIunit_type\fP\fB);\fP + +\fBvoid png_set_progressive_read_fn (png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fP\fIprogressive_ptr\fP\fB, png_progressive_info_ptr \fP\fIinfo_fn\fP\fB, png_progressive_row_ptr \fP\fIrow_fn\fP\fB, png_progressive_end_ptr \fIend_fn\fP\fB);\fP + +\fBvoid png_set_PLTE (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_colorp \fP\fIpalette\fP\fB, int \fInum_palette\fP\fB);\fP + +\fBvoid png_set_read_fn (png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fP\fIio_ptr\fP\fB, png_rw_ptr \fIread_data_fn\fP\fB);\fP + +\fBvoid png_set_read_status_fn (png_structp \fP\fIpng_ptr\fP\fB, png_read_status_ptr \fIread_row_fn\fP\fB);\fP + +\fBvoid png_set_read_user_transform_fn (png_structp \fP\fIpng_ptr\fP\fB, png_user_transform_ptr \fIread_user_transform_fn\fP\fB);\fP + +\fBvoid png_set_rgb_to_gray (png_structp \fP\fIpng_ptr\fP\fB, int \fP\fIerror_action\fP\fB, double \fP\fIred\fP\fB, double \fIgreen\fP\fB);\fP + +\fBvoid png_set_rgb_to_gray_fixed (png_structp \fP\fIpng_ptr\fP\fB, int error_action png_fixed_point \fP\fIred\fP\fB, png_fixed_point \fIgreen\fP\fB);\fP + +\fBvoid png_set_rows (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_bytepp \fIrow_pointers\fP\fB);\fP + +\fBvoid png_set_sBIT (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_color_8p \fIsig_bit\fP\fB);\fP + +\fBvoid png_set_sCAL (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_charp \fP\fIunit\fP\fB, double \fP\fIwidth\fP\fB, double \fIheight\fP\fB);\fP + +\fBvoid png_set_shift (png_structp \fP\fIpng_ptr\fP\fB, png_color_8p \fItrue_bits\fP\fB);\fP + +\fBvoid png_set_sig_bytes (png_structp \fP\fIpng_ptr\fP\fB, int \fInum_bytes\fP\fB);\fP + +\fBvoid png_set_sPLT (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_spalette_p \fP\fIsplt_ptr\fP\fB, int \fInum_spalettes\fP\fB);\fP + +\fBvoid png_set_sRGB (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, int \fIintent\fP\fB);\fP + +\fBvoid png_set_sRGB_gAMA_and_cHRM (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, int \fIintent\fP\fB);\fP + +\fBvoid png_set_strip_16 (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_strip_alpha (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_swap (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_swap_alpha (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_set_text (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_textp \fP\fItext_ptr\fP\fB, int \fInum_text\fP\fB);\fP + +\fBvoid png_set_tIME (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_timep \fImod_time\fP\fB);\fP + +\fBvoid png_set_tRNS (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_bytep \fP\fItrans\fP\fB, int \fP\fInum_trans\fP\fB, png_color_16p \fItrans_values\fP\fB);\fP + +\fBvoid png_set_tRNS_to_alpha(png_structp \fIpng_ptr\fP\fB);\fP + +\fBpng_uint_32 png_set_unknown_chunks (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_unknown_chunkp \fP\fIunknowns\fP\fB, int \fP\fInum\fP\fB, int \fIlocation\fP\fB);\fP + +\fBvoid png_set_unknown_chunk_location(png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, int \fP\fIchunk\fP\fB, int \fIlocation\fP\fB);\fP + +\fBvoid png_set_read_user_chunk_fn (png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fP\fIuser_chunk_ptr\fP\fB, png_user_chunk_ptr \fIread_user_chunk_fn\fP\fB);\fP + +\fBvoid png_set_user_limits (png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fP\fIuser_width_max\fP\fB, png_uint_32 \fIuser_height_max\fP\fB);\fP + +\fBvoid png_set_user_transform_info (png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fP\fIuser_transform_ptr\fP\fB, int \fP\fIuser_transform_depth\fP\fB, int \fIuser_transform_channels\fP\fB);\fP + +\fBvoid png_set_write_fn (png_structp \fP\fIpng_ptr\fP\fB, png_voidp \fP\fIio_ptr\fP\fB, png_rw_ptr \fP\fIwrite_data_fn\fP\fB, png_flush_ptr \fIoutput_flush_fn\fP\fB);\fP + +\fBvoid png_set_write_status_fn (png_structp \fP\fIpng_ptr\fP\fB, png_write_status_ptr \fIwrite_row_fn\fP\fB);\fP + +\fBvoid png_set_write_user_transform_fn (png_structp \fP\fIpng_ptr\fP\fB, png_user_transform_ptr \fIwrite_user_transform_fn\fP\fB);\fP + +\fBvoid png_set_compression_buffer_size(png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fIsize\fP\fB);\fP + +\fBint png_sig_cmp (png_bytep \fP\fIsig\fP\fB, png_size_t \fP\fIstart\fP\fB, png_size_t \fInum_to_check\fP\fB);\fP + +\fBvoid png_start_read_image (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_warning (png_structp \fP\fIpng_ptr\fP\fB, png_const_charp \fImessage\fP\fB);\fP + +\fBvoid png_write_chunk (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fP\fIchunk_name\fP\fB, png_bytep \fP\fIdata\fP\fB, png_size_t \fIlength\fP\fB);\fP + +\fBvoid png_write_chunk_data (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fP\fIdata\fP\fB, png_size_t \fIlength\fP\fB);\fP + +\fBvoid png_write_chunk_end (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_write_chunk_start (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fP\fIchunk_name\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fBvoid png_write_destroy (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_write_end (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBvoid png_write_flush (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_write_image (png_structp \fP\fIpng_ptr\fP\fB, png_bytepp \fIimage\fP\fB);\fP + +\fBDEPRECATED: void png_write_init (png_structp \fIpng_ptr\fP\fB);\fP + +\fBDEPRECATED: void png_write_init_2 (png_structpp \fP\fIptr_ptr\fP\fB, png_const_charp \fP\fIuser_png_ver\fP\fB, png_size_t \fP\fIpng_struct_size\fP\fB, png_size_t \fIpng_info_size\fP\fB);\fP + +\fBvoid png_write_info (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBvoid png_write_info_before_PLTE (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBvoid png_write_png (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, int \fP\fItransforms\fP\fB, png_voidp \fIparams\fP\fB);\fP + +\fBvoid png_write_row (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fIrow\fP\fB);\fP + +\fBvoid png_write_rows (png_structp \fP\fIpng_ptr\fP\fB, png_bytepp \fP\fIrow\fP\fB, png_uint_32 \fInum_rows\fP\fB);\fP + +\fBvoidpf png_zalloc (voidpf \fP\fIpng_ptr\fP\fB, uInt \fP\fIitems\fP\fB, uInt \fIsize\fP\fB);\fP + +\fBvoid png_zfree (voidpf \fP\fIpng_ptr\fP\fB, voidpf \fIptr\fP\fB);\fP + +.SH DESCRIPTION +The +.I libpng +library supports encoding, decoding, and various manipulations of +the Portable Network Graphics (PNG) format image files. It uses the +.IR zlib(3) +compression library. +Following is a copy of the libpng.txt file that accompanies libpng. +.SH LIBPNG.TXT +libpng.txt - A description on how to use and modify libpng + + libpng version 1.2.24 - December 14, 2007 + Updated and distributed by Glenn Randers-Pehrson + + Copyright (c) 1998-2007 Glenn Randers-Pehrson + For conditions of distribution and use, see copyright + notice in png.h. + + based on: + + libpng 1.0 beta 6 version 0.96 May 28, 1997 + Updated and distributed by Andreas Dilger + Copyright (c) 1996, 1997 Andreas Dilger + + libpng 1.0 beta 2 - version 0.88 January 26, 1996 + For conditions of distribution and use, see copyright + notice in png.h. Copyright (c) 1995, 1996 Guy Eric + Schalnat, Group 42, Inc. + + Updated/rewritten per request in the libpng FAQ + Copyright (c) 1995, 1996 Frank J. T. Wojcik + December 18, 1995 & January 20, 1996 + +.SH I. Introduction + +This file describes how to use and modify the PNG reference library +(known as libpng) for your own use. There are five sections to this +file: introduction, structures, reading, writing, and modification and +configuration notes for various special platforms. In addition to this +file, example.c is a good starting point for using the library, as +it is heavily commented and should include everything most people +will need. We assume that libpng is already installed; see the +INSTALL file for instructions on how to install libpng. + +For examples of libpng usage, see the files "example.c", "pngtest.c", +and the files in the "contrib" directory, all of which are included in the +libpng distribution. + +Libpng was written as a companion to the PNG specification, as a way +of reducing the amount of time and effort it takes to support the PNG +file format in application programs. + +The PNG specification (second edition), November 2003, is available as +a W3C Recommendation and as an ISO Standard (ISO/IEC 15948:2003 (E)) at + + +The PNG-1.0 specification is available +as RFC 2083 and as a +W3C Recommendation . Some +additional chunks are described in the special-purpose public chunks +documents at . + +Other information +about PNG, and the latest version of libpng, can be found at the PNG home +page, . + +Most users will not have to modify the library significantly; advanced +users may want to modify it more. All attempts were made to make it as +complete as possible, while keeping the code easy to understand. +Currently, this library only supports C. Support for other languages +is being considered. + +Libpng has been designed to handle multiple sessions at one time, +to be easily modifiable, to be portable to the vast majority of +machines (ANSI, K&R, 16-, 32-, and 64-bit) available, and to be easy +to use. The ultimate goal of libpng is to promote the acceptance of +the PNG file format in whatever way possible. While there is still +work to be done (see the TODO file), libpng should cover the +majority of the needs of its users. + +Libpng uses zlib for its compression and decompression of PNG files. +Further information about zlib, and the latest version of zlib, can +be found at the zlib home page, . +The zlib compression utility is a general purpose utility that is +useful for more than PNG files, and can be used without libpng. +See the documentation delivered with zlib for more details. +You can usually find the source files for the zlib utility wherever you +find the libpng source files. + +Libpng is thread safe, provided the threads are using different +instances of the structures. Each thread should have its own +png_struct and png_info instances, and thus its own image. +Libpng does not protect itself against two threads using the +same instance of a structure. + +.SH II. Structures + +There are two main structures that are important to libpng, png_struct +and png_info. The first, png_struct, is an internal structure that +will not, for the most part, be used by a user except as the first +variable passed to every libpng function call. + +The png_info structure is designed to provide information about the +PNG file. At one time, the fields of png_info were intended to be +directly accessible to the user. However, this tended to cause problems +with applications using dynamically loaded libraries, and as a result +a set of interface functions for png_info (the png_get_*() and png_set_*() +functions) was developed. The fields of png_info are still available for +older applications, but it is suggested that applications use the new +interfaces if at all possible. + +Applications that do make direct access to the members of png_struct (except +for png_ptr->jmpbuf) must be recompiled whenever the library is updated, +and applications that make direct access to the members of png_info must +be recompiled if they were compiled or loaded with libpng version 1.0.6, +in which the members were in a different order. In version 1.0.7, the +members of the png_info structure reverted to the old order, as they were +in versions 0.97c through 1.0.5. Starting with version 2.0.0, both +structures are going to be hidden, and the contents of the structures will +only be accessible through the png_get/png_set functions. + +The png.h header file is an invaluable reference for programming with libpng. +And while I'm on the topic, make sure you include the libpng header file: + +#include + +.SH III. Reading + +We'll now walk you through the possible functions to call when reading +in a PNG file sequentially, briefly explaining the syntax and purpose +of each one. See example.c and png.h for more detail. While +progressive reading is covered in the next section, you will still +need some of the functions discussed in this section to read a PNG +file. + +.SS Setup + +You will want to do the I/O initialization(*) before you get into libpng, +so if it doesn't work, you don't have much to undo. Of course, you +will also want to insure that you are, in fact, dealing with a PNG +file. Libpng provides a simple check to see if a file is a PNG file. +To use it, pass in the first 1 to 8 bytes of the file to the function +png_sig_cmp(), and it will return 0 if the bytes match the corresponding +bytes of the PNG signature, or nonzero otherwise. Of course, the more bytes +you pass in, the greater the accuracy of the prediction. + +If you are intending to keep the file pointer open for use in libpng, +you must ensure you don't read more than 8 bytes from the beginning +of the file, and you also have to make a call to png_set_sig_bytes_read() +with the number of bytes you read from the beginning. Libpng will +then only check the bytes (if any) that your program didn't read. + +(*): If you are not using the standard I/O functions, you will need +to replace them with custom functions. See the discussion under +Customizing libpng. + + + FILE *fp = fopen(file_name, "rb"); + if (!fp) + { + return (ERROR); + } + fread(header, 1, number, fp); + is_png = !png_sig_cmp(header, 0, number); + if (!is_png) + { + return (NOT_PNG); + } + + +Next, png_struct and png_info need to be allocated and initialized. In +order to ensure that the size of these structures is correct even with a +dynamically linked libpng, there are functions to initialize and +allocate the structures. We also pass the library version, optional +pointers to error handling functions, and a pointer to a data struct for +use by the error functions, if necessary (the pointer and functions can +be NULL if the default error handlers are to be used). See the section +on Changes to Libpng below regarding the old initialization functions. +The structure allocation functions quietly return NULL if they fail to +create the structure, so your application should check for that. + + png_structp png_ptr = png_create_read_struct + (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, + user_error_fn, user_warning_fn); + if (!png_ptr) + return (ERROR); + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_read_struct(&png_ptr, + (png_infopp)NULL, (png_infopp)NULL); + return (ERROR); + } + + png_infop end_info = png_create_info_struct(png_ptr); + if (!end_info) + { + png_destroy_read_struct(&png_ptr, &info_ptr, + (png_infopp)NULL); + return (ERROR); + } + +If you want to use your own memory allocation routines, +define PNG_USER_MEM_SUPPORTED and use +png_create_read_struct_2() instead of png_create_read_struct(): + + png_structp png_ptr = png_create_read_struct_2 + (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, + user_error_fn, user_warning_fn, (png_voidp) + user_mem_ptr, user_malloc_fn, user_free_fn); + +The error handling routines passed to png_create_read_struct() +and the memory alloc/free routines passed to png_create_struct_2() +are only necessary if you are not using the libpng supplied error +handling and memory alloc/free functions. + +When libpng encounters an error, it expects to longjmp back +to your routine. Therefore, you will need to call setjmp and pass +your png_jmpbuf(png_ptr). If you read the file from different +routines, you will need to update the jmpbuf field every time you enter +a new routine that will call a png_*() function. + +See your documentation of setjmp/longjmp for your compiler for more +information on setjmp/longjmp. See the discussion on libpng error +handling in the Customizing Libpng section below for more information +on the libpng error handling. If an error occurs, and libpng longjmp's +back to your setjmp, you will want to call png_destroy_read_struct() to +free any memory. + + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr, &info_ptr, + &end_info); + fclose(fp); + return (ERROR); + } + +If you would rather avoid the complexity of setjmp/longjmp issues, +you can compile libpng with PNG_SETJMP_NOT_SUPPORTED, in which case +errors will result in a call to PNG_ABORT() which defaults to abort(). + +Now you need to set up the input code. The default for libpng is to +use the C function fread(). If you use this, you will need to pass a +valid FILE * in the function png_init_io(). Be sure that the file is +opened in binary mode. If you wish to handle reading data in another +way, you need not call the png_init_io() function, but you must then +implement the libpng I/O methods discussed in the Customizing Libpng +section below. + + png_init_io(png_ptr, fp); + +If you had previously opened the file and read any of the signature from +the beginning in order to see if this was a PNG file, you need to let +libpng know that there are some bytes missing from the start of the file. + + png_set_sig_bytes(png_ptr, number); + +.SS Setting up callback code + +You can set up a callback function to handle any unknown chunks in the +input stream. You must supply the function + + read_chunk_callback(png_ptr ptr, + png_unknown_chunkp chunk); + { + /* The unknown chunk structure contains your + chunk data: */ + png_byte name[5]; + png_byte *data; + png_size_t size; + /* Note that libpng has already taken care of + the CRC handling */ + + /* put your code here. Return one of the + following: */ + + return (-n); /* chunk had an error */ + return (0); /* did not recognize */ + return (n); /* success */ + } + +(You can give your function another name that you like instead of +"read_chunk_callback") + +To inform libpng about your function, use + + png_set_read_user_chunk_fn(png_ptr, user_chunk_ptr, + read_chunk_callback); + +This names not only the callback function, but also a user pointer that +you can retrieve with + + png_get_user_chunk_ptr(png_ptr); + +At this point, you can set up a callback function that will be +called after each row has been read, which you can use to control +a progress meter or the like. It's demonstrated in pngtest.c. +You must supply a function + + void read_row_callback(png_ptr ptr, png_uint_32 row, + int pass); + { + /* put your code here */ + } + +(You can give it another name that you like instead of "read_row_callback") + +To inform libpng about your function, use + + png_set_read_status_fn(png_ptr, read_row_callback); + +.SS Width and height limits + +The PNG specification allows the width and height of an image to be as +large as 2^31-1 (0x7fffffff), or about 2.147 billion rows and columns. +Since very few applications really need to process such large images, +we have imposed an arbitrary 1-million limit on rows and columns. +Larger images will be rejected immediately with a png_error() call. If +you wish to override this limit, you can use + + png_set_user_limits(png_ptr, width_max, height_max); + +to set your own limits, or use width_max = height_max = 0x7fffffffL +to allow all valid dimensions (libpng may reject some very large images +anyway because of potential buffer overflow conditions). + +You should put this statement after you create the PNG structure and +before calling png_read_info(), png_read_png(), or png_process_data(). +If you need to retrieve the limits that are being applied, use + + width_max = png_get_user_width_max(png_ptr); + height_max = png_get_user_height_max(png_ptr); + +.SS Unknown-chunk handling + +Now you get to set the way the library processes unknown chunks in the +input PNG stream. Both known and unknown chunks will be read. Normal +behavior is that known chunks will be parsed into information in +various info_ptr members; unknown chunks will be discarded. To change +this, you can call: + + png_set_keep_unknown_chunks(png_ptr, keep, + chunk_list, num_chunks); + keep - 0: do not handle as unknown + 1: do not keep + 2: keep only if safe-to-copy + 3: keep even if unsafe-to-copy + You can use these definitions: + PNG_HANDLE_CHUNK_AS_DEFAULT 0 + PNG_HANDLE_CHUNK_NEVER 1 + PNG_HANDLE_CHUNK_IF_SAFE 2 + PNG_HANDLE_CHUNK_ALWAYS 3 + chunk_list - list of chunks affected (a byte string, + five bytes per chunk, NULL or '\0' if + num_chunks is 0) + num_chunks - number of chunks affected; if 0, all + unknown chunks are affected. If nonzero, + only the chunks in the list are affected + +Unknown chunks declared in this way will be saved as raw data onto a +list of png_unknown_chunk structures. If a chunk that is normally +known to libpng is named in the list, it will be handled as unknown, +according to the "keep" directive. If a chunk is named in successive +instances of png_set_keep_unknown_chunks(), the final instance will +take precedence. The IHDR and IEND chunks should not be named in +chunk_list; if they are, libpng will process them normally anyway. + +.SS The high-level read interface + +At this point there are two ways to proceed; through the high-level +read interface, or through a sequence of low-level read operations. +You can use the high-level interface if (a) you are willing to read +the entire image into memory, and (b) the input transformations +you want to do are limited to the following set: + + PNG_TRANSFORM_IDENTITY No transformation + PNG_TRANSFORM_STRIP_16 Strip 16-bit samples to + 8 bits + PNG_TRANSFORM_STRIP_ALPHA Discard the alpha channel + PNG_TRANSFORM_PACKING Expand 1, 2 and 4-bit + samples to bytes + PNG_TRANSFORM_PACKSWAP Change order of packed + pixels to LSB first + PNG_TRANSFORM_EXPAND Perform set_expand() + PNG_TRANSFORM_INVERT_MONO Invert monochrome images + PNG_TRANSFORM_SHIFT Normalize pixels to the + sBIT depth + PNG_TRANSFORM_BGR Flip RGB to BGR, RGBA + to BGRA + PNG_TRANSFORM_SWAP_ALPHA Flip RGBA to ARGB or GA + to AG + PNG_TRANSFORM_INVERT_ALPHA Change alpha from opacity + to transparency + PNG_TRANSFORM_SWAP_ENDIAN Byte-swap 16-bit samples + +(This excludes setting a background color, doing gamma transformation, +dithering, and setting filler.) If this is the case, simply do this: + + png_read_png(png_ptr, info_ptr, png_transforms, NULL) + +where png_transforms is an integer containing the bitwise OR of +some set of transformation flags. This call is equivalent to png_read_info(), +followed the set of transformations indicated by the transform mask, +then png_read_image(), and finally png_read_end(). + +(The final parameter of this call is not yet used. Someday it might point +to transformation parameters required by some future input transform.) + +You must use png_transforms and not call any png_set_transform() functions +when you use png_read_png(). + +After you have called png_read_png(), you can retrieve the image data +with + + row_pointers = png_get_rows(png_ptr, info_ptr); + +where row_pointers is an array of pointers to the pixel data for each row: + + png_bytep row_pointers[height]; + +If you know your image size and pixel size ahead of time, you can allocate +row_pointers prior to calling png_read_png() with + + if (height > PNG_UINT_32_MAX/png_sizeof(png_byte)) + png_error (png_ptr, + "Image is too tall to process in memory"); + if (width > PNG_UINT_32_MAX/pixel_size) + png_error (png_ptr, + "Image is too wide to process in memory"); + row_pointers = png_malloc(png_ptr, + height*png_sizeof(png_bytep)); + for (int i=0; i) and +png_get_(png_ptr, info_ptr, ...) functions return non-zero if the +data has been read, or zero if it is missing. The parameters to the +png_get_ are set directly if they are simple data types, or a pointer +into the info_ptr is returned for any complex types. + + png_get_PLTE(png_ptr, info_ptr, &palette, + &num_palette); + palette - the palette for the file + (array of png_color) + num_palette - number of entries in the palette + + png_get_gAMA(png_ptr, info_ptr, &gamma); + gamma - the gamma the file is written + at (PNG_INFO_gAMA) + + png_get_sRGB(png_ptr, info_ptr, &srgb_intent); + srgb_intent - the rendering intent (PNG_INFO_sRGB) + The presence of the sRGB chunk + means that the pixel data is in the + sRGB color space. This chunk also + implies specific values of gAMA and + cHRM. + + png_get_iCCP(png_ptr, info_ptr, &name, + &compression_type, &profile, &proflen); + name - The profile name. + compression - The compression type; always + PNG_COMPRESSION_TYPE_BASE for PNG 1.0. + You may give NULL to this argument to + ignore it. + profile - International Color Consortium color + profile data. May contain NULs. + proflen - length of profile data in bytes. + + png_get_sBIT(png_ptr, info_ptr, &sig_bit); + sig_bit - the number of significant bits for + (PNG_INFO_sBIT) each of the gray, + red, green, and blue channels, + whichever are appropriate for the + given color type (png_color_16) + + png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, + &trans_values); + trans - array of transparent entries for + palette (PNG_INFO_tRNS) + trans_values - graylevel or color sample values of + the single transparent color for + non-paletted images (PNG_INFO_tRNS) + num_trans - number of transparent entries + (PNG_INFO_tRNS) + + png_get_hIST(png_ptr, info_ptr, &hist); + (PNG_INFO_hIST) + hist - histogram of palette (array of + png_uint_16) + + png_get_tIME(png_ptr, info_ptr, &mod_time); + mod_time - time image was last modified + (PNG_VALID_tIME) + + png_get_bKGD(png_ptr, info_ptr, &background); + background - background color (PNG_VALID_bKGD) + valid 16-bit red, green and blue + values, regardless of color_type + + num_comments = png_get_text(png_ptr, info_ptr, + &text_ptr, &num_text); + num_comments - number of comments + text_ptr - array of png_text holding image + comments + text_ptr[i].compression - type of compression used + on "text" PNG_TEXT_COMPRESSION_NONE + PNG_TEXT_COMPRESSION_zTXt + PNG_ITXT_COMPRESSION_NONE + PNG_ITXT_COMPRESSION_zTXt + text_ptr[i].key - keyword for comment. Must contain + 1-79 characters. + text_ptr[i].text - text comments for current + keyword. Can be empty. + text_ptr[i].text_length - length of text string, + after decompression, 0 for iTXt + text_ptr[i].itxt_length - length of itxt string, + after decompression, 0 for tEXt/zTXt + text_ptr[i].lang - language of comment (empty + string for unknown). + text_ptr[i].lang_key - keyword in UTF-8 + (empty string for unknown). + num_text - number of comments (same as + num_comments; you can put NULL here + to avoid the duplication) + Note while png_set_text() will accept text, language, + and translated keywords that can be NULL pointers, the + structure returned by png_get_text will always contain + regular zero-terminated C strings. They might be + empty strings but they will never be NULL pointers. + + num_spalettes = png_get_sPLT(png_ptr, info_ptr, + &palette_ptr); + palette_ptr - array of palette structures holding + contents of one or more sPLT chunks + read. + num_spalettes - number of sPLT chunks read. + + png_get_oFFs(png_ptr, info_ptr, &offset_x, &offset_y, + &unit_type); + offset_x - positive offset from the left edge + of the screen + offset_y - positive offset from the top edge + of the screen + unit_type - PNG_OFFSET_PIXEL, PNG_OFFSET_MICROMETER + + png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y, + &unit_type); + res_x - pixels/unit physical resolution in + x direction + res_y - pixels/unit physical resolution in + x direction + unit_type - PNG_RESOLUTION_UNKNOWN, + PNG_RESOLUTION_METER + + png_get_sCAL(png_ptr, info_ptr, &unit, &width, + &height) + unit - physical scale units (an integer) + width - width of a pixel in physical scale units + height - height of a pixel in physical scale units + (width and height are doubles) + + png_get_sCAL_s(png_ptr, info_ptr, &unit, &width, + &height) + unit - physical scale units (an integer) + width - width of a pixel in physical scale units + height - height of a pixel in physical scale units + (width and height are strings like "2.54") + + num_unknown_chunks = png_get_unknown_chunks(png_ptr, + info_ptr, &unknowns) + unknowns - array of png_unknown_chunk + structures holding unknown chunks + unknowns[i].name - name of unknown chunk + unknowns[i].data - data of unknown chunk + unknowns[i].size - size of unknown chunk's data + unknowns[i].location - position of chunk in file + + The value of "i" corresponds to the order in which the + chunks were read from the PNG file or inserted with the + png_set_unknown_chunks() function. + +The data from the pHYs chunk can be retrieved in several convenient +forms: + + res_x = png_get_x_pixels_per_meter(png_ptr, + info_ptr) + res_y = png_get_y_pixels_per_meter(png_ptr, + info_ptr) + res_x_and_y = png_get_pixels_per_meter(png_ptr, + info_ptr) + res_x = png_get_x_pixels_per_inch(png_ptr, + info_ptr) + res_y = png_get_y_pixels_per_inch(png_ptr, + info_ptr) + res_x_and_y = png_get_pixels_per_inch(png_ptr, + info_ptr) + aspect_ratio = png_get_pixel_aspect_ratio(png_ptr, + info_ptr) + + (Each of these returns 0 [signifying "unknown"] if + the data is not present or if res_x is 0; + res_x_and_y is 0 if res_x != res_y) + +The data from the oFFs chunk can be retrieved in several convenient +forms: + + x_offset = png_get_x_offset_microns(png_ptr, info_ptr); + y_offset = png_get_y_offset_microns(png_ptr, info_ptr); + x_offset = png_get_x_offset_inches(png_ptr, info_ptr); + y_offset = png_get_y_offset_inches(png_ptr, info_ptr); + + (Each of these returns 0 [signifying "unknown" if both + x and y are 0] if the data is not present or if the + chunk is present but the unit is the pixel) + +For more information, see the png_info definition in png.h and the +PNG specification for chunk contents. Be careful with trusting +rowbytes, as some of the transformations could increase the space +needed to hold a row (expand, filler, gray_to_rgb, etc.). +See png_read_update_info(), below. + +A quick word about text_ptr and num_text. PNG stores comments in +keyword/text pairs, one pair per chunk, with no limit on the number +of text chunks, and a 2^31 byte limit on their size. While there are +suggested keywords, there is no requirement to restrict the use to these +strings. It is strongly suggested that keywords and text be sensible +to humans (that's the point), so don't use abbreviations. Non-printing +symbols are not allowed. See the PNG specification for more details. +There is also no requirement to have text after the keyword. + +Keywords should be limited to 79 Latin-1 characters without leading or +trailing spaces, but non-consecutive spaces are allowed within the +keyword. It is possible to have the same keyword any number of times. +The text_ptr is an array of png_text structures, each holding a +pointer to a language string, a pointer to a keyword and a pointer to +a text string. The text string, language code, and translated +keyword may be empty or NULL pointers. The keyword/text +pairs are put into the array in the order that they are received. +However, some or all of the text chunks may be after the image, so, to +make sure you have read all the text chunks, don't mess with these +until after you read the stuff after the image. This will be +mentioned again below in the discussion that goes with png_read_end(). + +.SS Input transformations + +After you've read the header information, you can set up the library +to handle any special transformations of the image data. The various +ways to transform the data will be described in the order that they +should occur. This is important, as some of these change the color +type and/or bit depth of the data, and some others only work on +certain color types and bit depths. Even though each transformation +checks to see if it has data that it can do something with, you should +make sure to only enable a transformation if it will be valid for the +data. For example, don't swap red and blue on grayscale data. + +The colors used for the background and transparency values should be +supplied in the same format/depth as the current image data. They +are stored in the same format/depth as the image data in a bKGD or tRNS +chunk, so this is what libpng expects for this data. The colors are +transformed to keep in sync with the image data when an application +calls the png_read_update_info() routine (see below). + +Data will be decoded into the supplied row buffers packed into bytes +unless the library has been told to transform it into another format. +For example, 4 bit/pixel paletted or grayscale data will be returned +2 pixels/byte with the leftmost pixel in the high-order bits of the +byte, unless png_set_packing() is called. 8-bit RGB data will be stored +in RGB RGB RGB format unless png_set_filler() or png_set_add_alpha() +is called to insert filler bytes, either before or after each RGB triplet. +16-bit RGB data will be returned RRGGBB RRGGBB, with the most significant +byte of the color value first, unless png_set_strip_16() is called to +transform it to regular RGB RGB triplets, or png_set_filler() or +png_set_add alpha() is called to insert filler bytes, either before or +after each RRGGBB triplet. Similarly, 8-bit or 16-bit grayscale data can +be modified with +png_set_filler(), png_set_add_alpha(), or png_set_strip_16(). + +The following code transforms grayscale images of less than 8 to 8 bits, +changes paletted images to RGB, and adds a full alpha channel if there is +transparency information in a tRNS chunk. This is most useful on +grayscale images with bit depths of 2 or 4 or if there is a multiple-image +viewing application that wishes to treat all images in the same way. + + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb(png_ptr); + + if (color_type == PNG_COLOR_TYPE_GRAY && + bit_depth < 8) png_set_expand_gray_1_2_4_to_8(png_ptr); + + if (png_get_valid(png_ptr, info_ptr, + PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr); + +These three functions are actually aliases for png_set_expand(), added +in libpng version 1.0.4, with the function names expanded to improve code +readability. In some future version they may actually do different +things. + +As of libpng version 1.2.9, png_set_expand_gray_1_2_4_to_8() was +added. It expands the sample depth without changing tRNS to alpha. +At the same time, png_set_gray_1_2_4_to_8() was deprecated, and it +will be removed from a future version. + +PNG can have files with 16 bits per channel. If you only can handle +8 bits per channel, this will strip the pixels down to 8 bit. + + if (bit_depth == 16) + png_set_strip_16(png_ptr); + +If, for some reason, you don't need the alpha channel on an image, +and you want to remove it rather than combining it with the background +(but the image author certainly had in mind that you *would* combine +it with the background, so that's what you should probably do): + + if (color_type & PNG_COLOR_MASK_ALPHA) + png_set_strip_alpha(png_ptr); + +In PNG files, the alpha channel in an image +is the level of opacity. If you need the alpha channel in an image to +be the level of transparency instead of opacity, you can invert the +alpha channel (or the tRNS chunk data) after it's read, so that 0 is +fully opaque and 255 (in 8-bit or paletted images) or 65535 (in 16-bit +images) is fully transparent, with + + png_set_invert_alpha(png_ptr); + +PNG files pack pixels of bit depths 1, 2, and 4 into bytes as small as +they can, resulting in, for example, 8 pixels per byte for 1 bit +files. This code expands to 1 pixel per byte without changing the +values of the pixels: + + if (bit_depth < 8) + png_set_packing(png_ptr); + +PNG files have possible bit depths of 1, 2, 4, 8, and 16. All pixels +stored in a PNG image have been "scaled" or "shifted" up to the next +higher possible bit depth (e.g. from 5 bits/sample in the range [0,31] to +8 bits/sample in the range [0, 255]). However, it is also possible to +convert the PNG pixel data back to the original bit depth of the image. +This call reduces the pixels back down to the original bit depth: + + png_color_8p sig_bit; + + if (png_get_sBIT(png_ptr, info_ptr, &sig_bit)) + png_set_shift(png_ptr, sig_bit); + +PNG files store 3-color pixels in red, green, blue order. This code +changes the storage of the pixels to blue, green, red: + + if (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) + png_set_bgr(png_ptr); + +PNG files store RGB pixels packed into 3 or 6 bytes. This code expands them +into 4 or 8 bytes for windowing systems that need them in this format: + + if (color_type == PNG_COLOR_TYPE_RGB) + png_set_filler(png_ptr, filler, PNG_FILLER_BEFORE); + +where "filler" is the 8 or 16-bit number to fill with, and the location is +either PNG_FILLER_BEFORE or PNG_FILLER_AFTER, depending upon whether +you want the filler before the RGB or after. This transformation +does not affect images that already have full alpha channels. To add an +opaque alpha channel, use filler=0xff or 0xffff and PNG_FILLER_AFTER which +will generate RGBA pixels. + +Note that png_set_filler() does not change the color type. If you want +to do that, you can add a true alpha channel with + + if (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_GRAY) + png_set_add_alpha(png_ptr, filler, PNG_FILLER_AFTER); + +where "filler" contains the alpha value to assign to each pixel. +This function was added in libpng-1.2.7. + +If you are reading an image with an alpha channel, and you need the +data as ARGB instead of the normal PNG format RGBA: + + if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) + png_set_swap_alpha(png_ptr); + +For some uses, you may want a grayscale image to be represented as +RGB. This code will do that conversion: + + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png_ptr); + +Conversely, you can convert an RGB or RGBA image to grayscale or grayscale +with alpha. + + if (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) + png_set_rgb_to_gray_fixed(png_ptr, error_action, + int red_weight, int green_weight); + + error_action = 1: silently do the conversion + error_action = 2: issue a warning if the original + image has any pixel where + red != green or red != blue + error_action = 3: issue an error and abort the + conversion if the original + image has any pixel where + red != green or red != blue + + red_weight: weight of red component times 100000 + green_weight: weight of green component times 100000 + If either weight is negative, default + weights (21268, 71514) are used. + +If you have set error_action = 1 or 2, you can +later check whether the image really was gray, after processing +the image rows, with the png_get_rgb_to_gray_status(png_ptr) function. +It will return a png_byte that is zero if the image was gray or +1 if there were any non-gray pixels. bKGD and sBIT data +will be silently converted to grayscale, using the green channel +data, regardless of the error_action setting. + +With red_weight+green_weight<=100000, +the normalized graylevel is computed: + + int rw = red_weight * 65536; + int gw = green_weight * 65536; + int bw = 65536 - (rw + gw); + gray = (rw*red + gw*green + bw*blue)/65536; + +The default values approximate those recommended in the Charles +Poynton's Color FAQ, +Copyright (c) 1998-01-04 Charles Poynton + + Y = 0.212671 * R + 0.715160 * G + 0.072169 * B + +Libpng approximates this with + + Y = 0.21268 * R + 0.7151 * G + 0.07217 * B + +which can be expressed with integers as + + Y = (6969 * R + 23434 * G + 2365 * B)/32768 + +The calculation is done in a linear colorspace, if the image gamma +is known. + +If you have a grayscale and you are using png_set_expand_depth(), +png_set_expand(), or png_set_gray_to_rgb to change to truecolor or to +a higher bit-depth, you must either supply the background color as a gray +value at the original file bit-depth (need_expand = 1) or else supply the +background color as an RGB triplet at the final, expanded bit depth +(need_expand = 0). Similarly, if you are reading a paletted image, you +must either supply the background color as a palette index (need_expand = 1) +or as an RGB triplet that may or may not be in the palette (need_expand = 0). + + png_color_16 my_background; + png_color_16p image_background; + + if (png_get_bKGD(png_ptr, info_ptr, &image_background)) + png_set_background(png_ptr, image_background, + PNG_BACKGROUND_GAMMA_FILE, 1, 1.0); + else + png_set_background(png_ptr, &my_background, + PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0); + +The png_set_background() function tells libpng to composite images +with alpha or simple transparency against the supplied background +color. If the PNG file contains a bKGD chunk (PNG_INFO_bKGD valid), +you may use this color, or supply another color more suitable for +the current display (e.g., the background color from a web page). You +need to tell libpng whether the color is in the gamma space of the +display (PNG_BACKGROUND_GAMMA_SCREEN for colors you supply), the file +(PNG_BACKGROUND_GAMMA_FILE for colors from the bKGD chunk), or one +that is neither of these gammas (PNG_BACKGROUND_GAMMA_UNIQUE - I don't +know why anyone would use this, but it's here). + +To properly display PNG images on any kind of system, the application needs +to know what the display gamma is. Ideally, the user will know this, and +the application will allow them to set it. One method of allowing the user +to set the display gamma separately for each system is to check for a +SCREEN_GAMMA or DISPLAY_GAMMA environment variable, which will hopefully be +correctly set. + +Note that display_gamma is the overall gamma correction required to produce +pleasing results, which depends on the lighting conditions in the surrounding +environment. In a dim or brightly lit room, no compensation other than +the physical gamma exponent of the monitor is needed, while in a dark room +a slightly smaller exponent is better. + + double gamma, screen_gamma; + + if (/* We have a user-defined screen + gamma value */) + { + screen_gamma = user_defined_screen_gamma; + } + /* One way that applications can share the same + screen gamma value */ + else if ((gamma_str = getenv("SCREEN_GAMMA")) + != NULL) + { + screen_gamma = (double)atof(gamma_str); + } + /* If we don't have another value */ + else + { + screen_gamma = 2.2; /* A good guess for a + PC monitor in a bright office or a dim room */ + screen_gamma = 2.0; /* A good guess for a + PC monitor in a dark room */ + screen_gamma = 1.7 or 1.0; /* A good + guess for Mac systems */ + } + +The png_set_gamma() function handles gamma transformations of the data. +Pass both the file gamma and the current screen_gamma. If the file does +not have a gamma value, you can pass one anyway if you have an idea what +it is (usually 0.45455 is a good guess for GIF images on PCs). Note +that file gammas are inverted from screen gammas. See the discussions +on gamma in the PNG specification for an excellent description of what +gamma is, and why all applications should support it. It is strongly +recommended that PNG viewers support gamma correction. + + if (png_get_gAMA(png_ptr, info_ptr, &gamma)) + png_set_gamma(png_ptr, screen_gamma, gamma); + else + png_set_gamma(png_ptr, screen_gamma, 0.45455); + +If you need to reduce an RGB file to a paletted file, or if a paletted +file has more entries then will fit on your screen, png_set_dither() +will do that. Note that this is a simple match dither that merely +finds the closest color available. This should work fairly well with +optimized palettes, and fairly badly with linear color cubes. If you +pass a palette that is larger then maximum_colors, the file will +reduce the number of colors in the palette so it will fit into +maximum_colors. If there is a histogram, it will use it to make +more intelligent choices when reducing the palette. If there is no +histogram, it may not do as good a job. + + if (color_type & PNG_COLOR_MASK_COLOR) + { + if (png_get_valid(png_ptr, info_ptr, + PNG_INFO_PLTE)) + { + png_uint_16p histogram = NULL; + + png_get_hIST(png_ptr, info_ptr, + &histogram); + png_set_dither(png_ptr, palette, num_palette, + max_screen_colors, histogram, 1); + } + else + { + png_color std_color_cube[MAX_SCREEN_COLORS] = + { ... colors ... }; + + png_set_dither(png_ptr, std_color_cube, + MAX_SCREEN_COLORS, MAX_SCREEN_COLORS, + NULL,0); + } + } + +PNG files describe monochrome as black being zero and white being one. +The following code will reverse this (make black be one and white be +zero): + + if (bit_depth == 1 && color_type == PNG_COLOR_TYPE_GRAY) + png_set_invert_mono(png_ptr); + +This function can also be used to invert grayscale and gray-alpha images: + + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_invert_mono(png_ptr); + +PNG files store 16 bit pixels in network byte order (big-endian, +ie. most significant bits first). This code changes the storage to the +other way (little-endian, i.e. least significant bits first, the +way PCs store them): + + if (bit_depth == 16) + png_set_swap(png_ptr); + +If you are using packed-pixel images (1, 2, or 4 bits/pixel), and you +need to change the order the pixels are packed into bytes, you can use: + + if (bit_depth < 8) + png_set_packswap(png_ptr); + +Finally, you can write your own transformation function if none of +the existing ones meets your needs. This is done by setting a callback +with + + png_set_read_user_transform_fn(png_ptr, + read_transform_fn); + +You must supply the function + + void read_transform_fn(png_ptr ptr, row_info_ptr + row_info, png_bytep data) + +See pngtest.c for a working example. Your function will be called +after all of the other transformations have been processed. + +You can also set up a pointer to a user structure for use by your +callback function, and you can inform libpng that your transform +function will change the number of channels or bit depth with the +function + + png_set_user_transform_info(png_ptr, user_ptr, + user_depth, user_channels); + +The user's application, not libpng, is responsible for allocating and +freeing any memory required for the user structure. + +You can retrieve the pointer via the function +png_get_user_transform_ptr(). For example: + + voidp read_user_transform_ptr = + png_get_user_transform_ptr(png_ptr); + +The last thing to handle is interlacing; this is covered in detail below, +but you must call the function here if you want libpng to handle expansion +of the interlaced image. + + number_of_passes = png_set_interlace_handling(png_ptr); + +After setting the transformations, libpng can update your png_info +structure to reflect any transformations you've requested with this +call. This is most useful to update the info structure's rowbytes +field so you can use it to allocate your image memory. This function +will also update your palette with the correct screen_gamma and +background if these have been given with the calls above. + + png_read_update_info(png_ptr, info_ptr); + +After you call png_read_update_info(), you can allocate any +memory you need to hold the image. The row data is simply +raw byte data for all forms of images. As the actual allocation +varies among applications, no example will be given. If you +are allocating one large chunk, you will need to build an +array of pointers to each row, as it will be needed for some +of the functions below. + +.SS Reading image data + +After you've allocated memory, you can read the image data. +The simplest way to do this is in one function call. If you are +allocating enough memory to hold the whole image, you can just +call png_read_image() and libpng will read in all the image data +and put it in the memory area supplied. You will need to pass in +an array of pointers to each row. + +This function automatically handles interlacing, so you don't need +to call png_set_interlace_handling() or call this function multiple +times, or any of that other stuff necessary with png_read_rows(). + + png_read_image(png_ptr, row_pointers); + +where row_pointers is: + + png_bytep row_pointers[height]; + +You can point to void or char or whatever you use for pixels. + +If you don't want to read in the whole image at once, you can +use png_read_rows() instead. If there is no interlacing (check +interlace_type == PNG_INTERLACE_NONE), this is simple: + + png_read_rows(png_ptr, row_pointers, NULL, + number_of_rows); + +where row_pointers is the same as in the png_read_image() call. + +If you are doing this just one row at a time, you can do this with +a single row_pointer instead of an array of row_pointers: + + png_bytep row_pointer = row; + png_read_row(png_ptr, row_pointer, NULL); + +If the file is interlaced (interlace_type != 0 in the IHDR chunk), things +get somewhat harder. The only current (PNG Specification version 1.2) +interlacing type for PNG is (interlace_type == PNG_INTERLACE_ADAM7) +is a somewhat complicated 2D interlace scheme, known as Adam7, that +breaks down an image into seven smaller images of varying size, based +on an 8x8 grid. + +libpng can fill out those images or it can give them to you "as is". +If you want them filled out, there are two ways to do that. The one +mentioned in the PNG specification is to expand each pixel to cover +those pixels that have not been read yet (the "rectangle" method). +This results in a blocky image for the first pass, which gradually +smooths out as more pixels are read. The other method is the "sparkle" +method, where pixels are drawn only in their final locations, with the +rest of the image remaining whatever colors they were initialized to +before the start of the read. The first method usually looks better, +but tends to be slower, as there are more pixels to put in the rows. + +If you don't want libpng to handle the interlacing details, just call +png_read_rows() seven times to read in all seven images. Each of the +images is a valid image by itself, or they can all be combined on an +8x8 grid to form a single image (although if you intend to combine them +you would be far better off using the libpng interlace handling). + +The first pass will return an image 1/8 as wide as the entire image +(every 8th column starting in column 0) and 1/8 as high as the original +(every 8th row starting in row 0), the second will be 1/8 as wide +(starting in column 4) and 1/8 as high (also starting in row 0). The +third pass will be 1/4 as wide (every 4th pixel starting in column 0) and +1/8 as high (every 8th row starting in row 4), and the fourth pass will +be 1/4 as wide and 1/4 as high (every 4th column starting in column 2, +and every 4th row starting in row 0). The fifth pass will return an +image 1/2 as wide, and 1/4 as high (starting at column 0 and row 2), +while the sixth pass will be 1/2 as wide and 1/2 as high as the original +(starting in column 1 and row 0). The seventh and final pass will be as +wide as the original, and 1/2 as high, containing all of the odd +numbered scanlines. Phew! + +If you want libpng to expand the images, call this before calling +png_start_read_image() or png_read_update_info(): + + if (interlace_type == PNG_INTERLACE_ADAM7) + number_of_passes + = png_set_interlace_handling(png_ptr); + +This will return the number of passes needed. Currently, this +is seven, but may change if another interlace type is added. +This function can be called even if the file is not interlaced, +where it will return one pass. + +If you are not going to display the image after each pass, but are +going to wait until the entire image is read in, use the sparkle +effect. This effect is faster and the end result of either method +is exactly the same. If you are planning on displaying the image +after each pass, the "rectangle" effect is generally considered the +better looking one. + +If you only want the "sparkle" effect, just call png_read_rows() as +normal, with the third parameter NULL. Make sure you make pass over +the image number_of_passes times, and you don't change the data in the +rows between calls. You can change the locations of the data, just +not the data. Each pass only writes the pixels appropriate for that +pass, and assumes the data from previous passes is still valid. + + png_read_rows(png_ptr, row_pointers, NULL, + number_of_rows); + +If you only want the first effect (the rectangles), do the same as +before except pass the row buffer in the third parameter, and leave +the second parameter NULL. + + png_read_rows(png_ptr, NULL, row_pointers, + number_of_rows); + +.SS Finishing a sequential read + +After you are finished reading the image through the +low-level interface, you can finish reading the file. If you are +interested in comments or time, which may be stored either before or +after the image data, you should pass the separate png_info struct if +you want to keep the comments from before and after the image +separate. If you are not interested, you can pass NULL. + + png_read_end(png_ptr, end_info); + +When you are done, you can free all memory allocated by libpng like this: + + png_destroy_read_struct(&png_ptr, &info_ptr, + &end_info); + +It is also possible to individually free the info_ptr members that +point to libpng-allocated storage with the following function: + + png_free_data(png_ptr, info_ptr, mask, seq) + mask - identifies data to be freed, a mask + containing the bitwise OR of one or + more of + PNG_FREE_PLTE, PNG_FREE_TRNS, + PNG_FREE_HIST, PNG_FREE_ICCP, + PNG_FREE_PCAL, PNG_FREE_ROWS, + PNG_FREE_SCAL, PNG_FREE_SPLT, + PNG_FREE_TEXT, PNG_FREE_UNKN, + or simply PNG_FREE_ALL + seq - sequence number of item to be freed + (-1 for all items) + +This function may be safely called when the relevant storage has +already been freed, or has not yet been allocated, or was allocated +by the user and not by libpng, and will in those +cases do nothing. The "seq" parameter is ignored if only one item +of the selected data type, such as PLTE, is allowed. If "seq" is not +-1, and multiple items are allowed for the data type identified in +the mask, such as text or sPLT, only the n'th item in the structure +is freed, where n is "seq". + +The default behavior is only to free data that was allocated internally +by libpng. This can be changed, so that libpng will not free the data, +or so that it will free data that was allocated by the user with png_malloc() +or png_zalloc() and passed in via a png_set_*() function, with + + png_data_freer(png_ptr, info_ptr, freer, mask) + mask - which data elements are affected + same choices as in png_free_data() + freer - one of + PNG_DESTROY_WILL_FREE_DATA + PNG_SET_WILL_FREE_DATA + PNG_USER_WILL_FREE_DATA + +This function only affects data that has already been allocated. +You can call this function after reading the PNG data but before calling +any png_set_*() functions, to control whether the user or the png_set_*() +function is responsible for freeing any existing data that might be present, +and again after the png_set_*() functions to control whether the user +or png_destroy_*() is supposed to free the data. When the user assumes +responsibility for libpng-allocated data, the application must use +png_free() to free it, and when the user transfers responsibility to libpng +for data that the user has allocated, the user must have used png_malloc() +or png_zalloc() to allocate it. + +If you allocated your row_pointers in a single block, as suggested above in +the description of the high level read interface, you must not transfer +responsibility for freeing it to the png_set_rows or png_read_destroy function, +because they would also try to free the individual row_pointers[i]. + +If you allocated text_ptr.text, text_ptr.lang, and text_ptr.translated_keyword +separately, do not transfer responsibility for freeing text_ptr to libpng, +because when libpng fills a png_text structure it combines these members with +the key member, and png_free_data() will free only text_ptr.key. Similarly, +if you transfer responsibility for free'ing text_ptr from libpng to your +application, your application must not separately free those members. + +The png_free_data() function will turn off the "valid" flag for anything +it frees. If you need to turn the flag off for a chunk that was freed by your +application instead of by libpng, you can use + + png_set_invalid(png_ptr, info_ptr, mask); + mask - identifies the chunks to be made invalid, + containing the bitwise OR of one or + more of + PNG_INFO_gAMA, PNG_INFO_sBIT, + PNG_INFO_cHRM, PNG_INFO_PLTE, + PNG_INFO_tRNS, PNG_INFO_bKGD, + PNG_INFO_hIST, PNG_INFO_pHYs, + PNG_INFO_oFFs, PNG_INFO_tIME, + PNG_INFO_pCAL, PNG_INFO_sRGB, + PNG_INFO_iCCP, PNG_INFO_sPLT, + PNG_INFO_sCAL, PNG_INFO_IDAT + +For a more compact example of reading a PNG image, see the file example.c. + +.SS Reading PNG files progressively + +The progressive reader is slightly different then the non-progressive +reader. Instead of calling png_read_info(), png_read_rows(), and +png_read_end(), you make one call to png_process_data(), which calls +callbacks when it has the info, a row, or the end of the image. You +set up these callbacks with png_set_progressive_read_fn(). You don't +have to worry about the input/output functions of libpng, as you are +giving the library the data directly in png_process_data(). I will +assume that you have read the section on reading PNG files above, +so I will only highlight the differences (although I will show +all of the code). + +png_structp png_ptr; +png_infop info_ptr; + + /* An example code fragment of how you would + initialize the progressive reader in your + application. */ + int + initialize_png_reader() + { + png_ptr = png_create_read_struct + (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, + user_error_fn, user_warning_fn); + if (!png_ptr) + return (ERROR); + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, + (png_infopp)NULL); + return (ERROR); + } + + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr, &info_ptr, + (png_infopp)NULL); + return (ERROR); + } + + /* This one's new. You can provide functions + to be called when the header info is valid, + when each row is completed, and when the image + is finished. If you aren't using all functions, + you can specify NULL parameters. Even when all + three functions are NULL, you need to call + png_set_progressive_read_fn(). You can use + any struct as the user_ptr (cast to a void pointer + for the function call), and retrieve the pointer + from inside the callbacks using the function + + png_get_progressive_ptr(png_ptr); + + which will return a void pointer, which you have + to cast appropriately. + */ + png_set_progressive_read_fn(png_ptr, (void *)user_ptr, + info_callback, row_callback, end_callback); + + return 0; + } + + /* A code fragment that you call as you receive blocks + of data */ + int + process_data(png_bytep buffer, png_uint_32 length) + { + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_read_struct(&png_ptr, &info_ptr, + (png_infopp)NULL); + return (ERROR); + } + + /* This one's new also. Simply give it a chunk + of data from the file stream (in order, of + course). On machines with segmented memory + models machines, don't give it any more than + 64K. The library seems to run fine with sizes + of 4K. Although you can give it much less if + necessary (I assume you can give it chunks of + 1 byte, I haven't tried less then 256 bytes + yet). When this function returns, you may + want to display any rows that were generated + in the row callback if you don't already do + so there. + */ + png_process_data(png_ptr, info_ptr, buffer, length); + return 0; + } + + /* This function is called (as set by + png_set_progressive_read_fn() above) when enough data + has been supplied so all of the header has been + read. + */ + void + info_callback(png_structp png_ptr, png_infop info) + { + /* Do any setup here, including setting any of + the transformations mentioned in the Reading + PNG files section. For now, you _must_ call + either png_start_read_image() or + png_read_update_info() after all the + transformations are set (even if you don't set + any). You may start getting rows before + png_process_data() returns, so this is your + last chance to prepare for that. + */ + } + + /* This function is called when each row of image + data is complete */ + void + row_callback(png_structp png_ptr, png_bytep new_row, + png_uint_32 row_num, int pass) + { + /* If the image is interlaced, and you turned + on the interlace handler, this function will + be called for every row in every pass. Some + of these rows will not be changed from the + previous pass. When the row is not changed, + the new_row variable will be NULL. The rows + and passes are called in order, so you don't + really need the row_num and pass, but I'm + supplying them because it may make your life + easier. + + For the non-NULL rows of interlaced images, + you must call png_progressive_combine_row() + passing in the row and the old row. You can + call this function for NULL rows (it will just + return) and for non-interlaced images (it just + does the memcpy for you) if it will make the + code easier. Thus, you can just do this for + all cases: + */ + + png_progressive_combine_row(png_ptr, old_row, + new_row); + + /* where old_row is what was displayed for + previously for the row. Note that the first + pass (pass == 0, really) will completely cover + the old row, so the rows do not have to be + initialized. After the first pass (and only + for interlaced images), you will have to pass + the current row, and the function will combine + the old row and the new row. + */ + } + + void + end_callback(png_structp png_ptr, png_infop info) + { + /* This function is called after the whole image + has been read, including any chunks after the + image (up to and including the IEND). You + will usually have the same info chunk as you + had in the header, although some data may have + been added to the comments and time fields. + + Most people won't do much here, perhaps setting + a flag that marks the image as finished. + */ + } + + + +.SH IV. Writing + +Much of this is very similar to reading. However, everything of +importance is repeated here, so you won't have to constantly look +back up in the reading section to understand writing. + +.SS Setup + +You will want to do the I/O initialization before you get into libpng, +so if it doesn't work, you don't have anything to undo. If you are not +using the standard I/O functions, you will need to replace them with +custom writing functions. See the discussion under Customizing libpng. + + FILE *fp = fopen(file_name, "wb"); + if (!fp) + { + return (ERROR); + } + +Next, png_struct and png_info need to be allocated and initialized. +As these can be both relatively large, you may not want to store these +on the stack, unless you have stack space to spare. Of course, you +will want to check if they return NULL. If you are also reading, +you won't want to name your read structure and your write structure +both "png_ptr"; you can call them anything you like, such as +"read_ptr" and "write_ptr". Look at pngtest.c, for example. + + png_structp png_ptr = png_create_write_struct + (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, + user_error_fn, user_warning_fn); + if (!png_ptr) + return (ERROR); + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + { + png_destroy_write_struct(&png_ptr, + (png_infopp)NULL); + return (ERROR); + } + +If you want to use your own memory allocation routines, +define PNG_USER_MEM_SUPPORTED and use +png_create_write_struct_2() instead of png_create_write_struct(): + + png_structp png_ptr = png_create_write_struct_2 + (PNG_LIBPNG_VER_STRING, (png_voidp)user_error_ptr, + user_error_fn, user_warning_fn, (png_voidp) + user_mem_ptr, user_malloc_fn, user_free_fn); + +After you have these structures, you will need to set up the +error handling. When libpng encounters an error, it expects to +longjmp() back to your routine. Therefore, you will need to call +setjmp() and pass the png_jmpbuf(png_ptr). If you +write the file from different routines, you will need to update +the png_jmpbuf(png_ptr) every time you enter a new routine that will +call a png_*() function. See your documentation of setjmp/longjmp +for your compiler for more information on setjmp/longjmp. See +the discussion on libpng error handling in the Customizing Libpng +section below for more information on the libpng error handling. + + if (setjmp(png_jmpbuf(png_ptr))) + { + png_destroy_write_struct(&png_ptr, &info_ptr); + fclose(fp); + return (ERROR); + } + ... + return; + +If you would rather avoid the complexity of setjmp/longjmp issues, +you can compile libpng with PNG_SETJMP_NOT_SUPPORTED, in which case +errors will result in a call to PNG_ABORT() which defaults to abort(). + +Now you need to set up the output code. The default for libpng is to +use the C function fwrite(). If you use this, you will need to pass a +valid FILE * in the function png_init_io(). Be sure that the file is +opened in binary mode. Again, if you wish to handle writing data in +another way, see the discussion on libpng I/O handling in the Customizing +Libpng section below. + + png_init_io(png_ptr, fp); + +If you are embedding your PNG into a datastream such as MNG, and don't +want libpng to write the 8-byte signature, or if you have already +written the signature in your application, use + + png_set_sig_bytes(png_ptr, 8); + +to inform libpng that it should not write a signature. + +.SS Write callbacks + +At this point, you can set up a callback function that will be +called after each row has been written, which you can use to control +a progress meter or the like. It's demonstrated in pngtest.c. +You must supply a function + + void write_row_callback(png_ptr, png_uint_32 row, + int pass); + { + /* put your code here */ + } + +(You can give it another name that you like instead of "write_row_callback") + +To inform libpng about your function, use + + png_set_write_status_fn(png_ptr, write_row_callback); + +You now have the option of modifying how the compression library will +run. The following functions are mainly for testing, but may be useful +in some cases, like if you need to write PNG files extremely fast and +are willing to give up some compression, or if you want to get the +maximum possible compression at the expense of slower writing. If you +have no special needs in this area, let the library do what it wants by +not calling this function at all, as it has been tuned to deliver a good +speed/compression ratio. The second parameter to png_set_filter() is +the filter method, for which the only valid values are 0 (as of the +July 1999 PNG specification, version 1.2) or 64 (if you are writing +a PNG datastream that is to be embedded in a MNG datastream). The third +parameter is a flag that indicates which filter type(s) are to be tested +for each scanline. See the PNG specification for details on the specific filter +types. + + + /* turn on or off filtering, and/or choose + specific filters. You can use either a single + PNG_FILTER_VALUE_NAME or the bitwise OR of one + or more PNG_FILTER_NAME masks. */ + png_set_filter(png_ptr, 0, + PNG_FILTER_NONE | PNG_FILTER_VALUE_NONE | + PNG_FILTER_SUB | PNG_FILTER_VALUE_SUB | + PNG_FILTER_UP | PNG_FILTER_VALUE_UP | + PNG_FILTER_AVE | PNG_FILTER_VALUE_AVE | + PNG_FILTER_PAETH | PNG_FILTER_VALUE_PAETH| + PNG_ALL_FILTERS); + +If an application +wants to start and stop using particular filters during compression, +it should start out with all of the filters (to ensure that the previous +row of pixels will be stored in case it's needed later), and then add +and remove them after the start of compression. + +If you are writing a PNG datastream that is to be embedded in a MNG +datastream, the second parameter can be either 0 or 64. + +The png_set_compression_*() functions interface to the zlib compression +library, and should mostly be ignored unless you really know what you are +doing. The only generally useful call is png_set_compression_level() +which changes how much time zlib spends on trying to compress the image +data. See the Compression Library (zlib.h and algorithm.txt, distributed +with zlib) for details on the compression levels. + + /* set the zlib compression level */ + png_set_compression_level(png_ptr, + Z_BEST_COMPRESSION); + + /* set other zlib parameters */ + png_set_compression_mem_level(png_ptr, 8); + png_set_compression_strategy(png_ptr, + Z_DEFAULT_STRATEGY); + png_set_compression_window_bits(png_ptr, 15); + png_set_compression_method(png_ptr, 8); + png_set_compression_buffer_size(png_ptr, 8192) + +extern PNG_EXPORT(void,png_set_zbuf_size) + +.SS Setting the contents of info for output + +You now need to fill in the png_info structure with all the data you +wish to write before the actual image. Note that the only thing you +are allowed to write after the image is the text chunks and the time +chunk (as of PNG Specification 1.2, anyway). See png_write_end() and +the latest PNG specification for more information on that. If you +wish to write them before the image, fill them in now, and flag that +data as being valid. If you want to wait until after the data, don't +fill them until png_write_end(). For all the fields in png_info and +their data types, see png.h. For explanations of what the fields +contain, see the PNG specification. + +Some of the more important parts of the png_info are: + + png_set_IHDR(png_ptr, info_ptr, width, height, + bit_depth, color_type, interlace_type, + compression_type, filter_method) + width - holds the width of the image + in pixels (up to 2^31). + height - holds the height of the image + in pixels (up to 2^31). + bit_depth - holds the bit depth of one of the + image channels. + (valid values are 1, 2, 4, 8, 16 + and depend also on the + color_type. See also significant + bits (sBIT) below). + color_type - describes which color/alpha + channels are present. + PNG_COLOR_TYPE_GRAY + (bit depths 1, 2, 4, 8, 16) + PNG_COLOR_TYPE_GRAY_ALPHA + (bit depths 8, 16) + PNG_COLOR_TYPE_PALETTE + (bit depths 1, 2, 4, 8) + PNG_COLOR_TYPE_RGB + (bit_depths 8, 16) + PNG_COLOR_TYPE_RGB_ALPHA + (bit_depths 8, 16) + + PNG_COLOR_MASK_PALETTE + PNG_COLOR_MASK_COLOR + PNG_COLOR_MASK_ALPHA + + interlace_type - PNG_INTERLACE_NONE or + PNG_INTERLACE_ADAM7 + compression_type - (must be + PNG_COMPRESSION_TYPE_DEFAULT) + filter_method - (must be PNG_FILTER_TYPE_DEFAULT + or, if you are writing a PNG to + be embedded in a MNG datastream, + can also be + PNG_INTRAPIXEL_DIFFERENCING) + + png_set_PLTE(png_ptr, info_ptr, palette, + num_palette); + palette - the palette for the file + (array of png_color) + num_palette - number of entries in the palette + + png_set_gAMA(png_ptr, info_ptr, gamma); + gamma - the gamma the image was created + at (PNG_INFO_gAMA) + + png_set_sRGB(png_ptr, info_ptr, srgb_intent); + srgb_intent - the rendering intent + (PNG_INFO_sRGB) The presence of + the sRGB chunk means that the pixel + data is in the sRGB color space. + This chunk also implies specific + values of gAMA and cHRM. Rendering + intent is the CSS-1 property that + has been defined by the International + Color Consortium + (http://www.color.org). + It can be one of + PNG_sRGB_INTENT_SATURATION, + PNG_sRGB_INTENT_PERCEPTUAL, + PNG_sRGB_INTENT_ABSOLUTE, or + PNG_sRGB_INTENT_RELATIVE. + + + png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, + srgb_intent); + srgb_intent - the rendering intent + (PNG_INFO_sRGB) The presence of the + sRGB chunk means that the pixel + data is in the sRGB color space. + This function also causes gAMA and + cHRM chunks with the specific values + that are consistent with sRGB to be + written. + + png_set_iCCP(png_ptr, info_ptr, name, compression_type, + profile, proflen); + name - The profile name. + compression - The compression type; always + PNG_COMPRESSION_TYPE_BASE for PNG 1.0. + You may give NULL to this argument to + ignore it. + profile - International Color Consortium color + profile data. May contain NULs. + proflen - length of profile data in bytes. + + png_set_sBIT(png_ptr, info_ptr, sig_bit); + sig_bit - the number of significant bits for + (PNG_INFO_sBIT) each of the gray, red, + green, and blue channels, whichever are + appropriate for the given color type + (png_color_16) + + png_set_tRNS(png_ptr, info_ptr, trans, num_trans, + trans_values); + trans - array of transparent entries for + palette (PNG_INFO_tRNS) + trans_values - graylevel or color sample values of + the single transparent color for + non-paletted images (PNG_INFO_tRNS) + num_trans - number of transparent entries + (PNG_INFO_tRNS) + + png_set_hIST(png_ptr, info_ptr, hist); + (PNG_INFO_hIST) + hist - histogram of palette (array of + png_uint_16) + + png_set_tIME(png_ptr, info_ptr, mod_time); + mod_time - time image was last modified + (PNG_VALID_tIME) + + png_set_bKGD(png_ptr, info_ptr, background); + background - background color (PNG_VALID_bKGD) + + png_set_text(png_ptr, info_ptr, text_ptr, num_text); + text_ptr - array of png_text holding image + comments + text_ptr[i].compression - type of compression used + on "text" PNG_TEXT_COMPRESSION_NONE + PNG_TEXT_COMPRESSION_zTXt + PNG_ITXT_COMPRESSION_NONE + PNG_ITXT_COMPRESSION_zTXt + text_ptr[i].key - keyword for comment. Must contain + 1-79 characters. + text_ptr[i].text - text comments for current + keyword. Can be NULL or empty. + text_ptr[i].text_length - length of text string, + after decompression, 0 for iTXt + text_ptr[i].itxt_length - length of itxt string, + after decompression, 0 for tEXt/zTXt + text_ptr[i].lang - language of comment (NULL or + empty for unknown). + text_ptr[i].translated_keyword - keyword in UTF-8 (NULL + or empty for unknown). + num_text - number of comments + + png_set_sPLT(png_ptr, info_ptr, &palette_ptr, + num_spalettes); + palette_ptr - array of png_sPLT_struct structures + to be added to the list of palettes + in the info structure. + num_spalettes - number of palette structures to be + added. + + png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, + unit_type); + offset_x - positive offset from the left + edge of the screen + offset_y - positive offset from the top + edge of the screen + unit_type - PNG_OFFSET_PIXEL, PNG_OFFSET_MICROMETER + + png_set_pHYs(png_ptr, info_ptr, res_x, res_y, + unit_type); + res_x - pixels/unit physical resolution + in x direction + res_y - pixels/unit physical resolution + in y direction + unit_type - PNG_RESOLUTION_UNKNOWN, + PNG_RESOLUTION_METER + + png_set_sCAL(png_ptr, info_ptr, unit, width, height) + unit - physical scale units (an integer) + width - width of a pixel in physical scale units + height - height of a pixel in physical scale units + (width and height are doubles) + + png_set_sCAL_s(png_ptr, info_ptr, unit, width, height) + unit - physical scale units (an integer) + width - width of a pixel in physical scale units + height - height of a pixel in physical scale units + (width and height are strings like "2.54") + + png_set_unknown_chunks(png_ptr, info_ptr, &unknowns, + num_unknowns) + unknowns - array of png_unknown_chunk + structures holding unknown chunks + unknowns[i].name - name of unknown chunk + unknowns[i].data - data of unknown chunk + unknowns[i].size - size of unknown chunk's data + unknowns[i].location - position to write chunk in file + 0: do not write chunk + PNG_HAVE_IHDR: before PLTE + PNG_HAVE_PLTE: before IDAT + PNG_AFTER_IDAT: after IDAT + +The "location" member is set automatically according to +what part of the output file has already been written. +You can change its value after calling png_set_unknown_chunks() +as demonstrated in pngtest.c. Within each of the "locations", +the chunks are sequenced according to their position in the +structure (that is, the value of "i", which is the order in which +the chunk was either read from the input file or defined with +png_set_unknown_chunks). + +A quick word about text and num_text. text is an array of png_text +structures. num_text is the number of valid structures in the array. +Each png_text structure holds a language code, a keyword, a text value, +and a compression type. + +The compression types have the same valid numbers as the compression +types of the image data. Currently, the only valid number is zero. +However, you can store text either compressed or uncompressed, unlike +images, which always have to be compressed. So if you don't want the +text compressed, set the compression type to PNG_TEXT_COMPRESSION_NONE. +Because tEXt and zTXt chunks don't have a language field, if you +specify PNG_TEXT_COMPRESSION_NONE or PNG_TEXT_COMPRESSION_zTXt +any language code or translated keyword will not be written out. + +Until text gets around 1000 bytes, it is not worth compressing it. +After the text has been written out to the file, the compression type +is set to PNG_TEXT_COMPRESSION_NONE_WR or PNG_TEXT_COMPRESSION_zTXt_WR, +so that it isn't written out again at the end (in case you are calling +png_write_end() with the same struct. + +The keywords that are given in the PNG Specification are: + + Title Short (one line) title or + caption for image + Author Name of image's creator + Description Description of image (possibly long) + Copyright Copyright notice + Creation Time Time of original image creation + (usually RFC 1123 format, see below) + Software Software used to create the image + Disclaimer Legal disclaimer + Warning Warning of nature of content + Source Device used to create the image + Comment Miscellaneous comment; conversion + from other image format + +The keyword-text pairs work like this. Keywords should be short +simple descriptions of what the comment is about. Some typical +keywords are found in the PNG specification, as is some recommendations +on keywords. You can repeat keywords in a file. You can even write +some text before the image and some after. For example, you may want +to put a description of the image before the image, but leave the +disclaimer until after, so viewers working over modem connections +don't have to wait for the disclaimer to go over the modem before +they start seeing the image. Finally, keywords should be full +words, not abbreviations. Keywords and text are in the ISO 8859-1 +(Latin-1) character set (a superset of regular ASCII) and can not +contain NUL characters, and should not contain control or other +unprintable characters. To make the comments widely readable, stick +with basic ASCII, and avoid machine specific character set extensions +like the IBM-PC character set. The keyword must be present, but +you can leave off the text string on non-compressed pairs. +Compressed pairs must have a text string, as only the text string +is compressed anyway, so the compression would be meaningless. + +PNG supports modification time via the png_time structure. Two +conversion routines are provided, png_convert_from_time_t() for +time_t and png_convert_from_struct_tm() for struct tm. The +time_t routine uses gmtime(). You don't have to use either of +these, but if you wish to fill in the png_time structure directly, +you should provide the time in universal time (GMT) if possible +instead of your local time. Note that the year number is the full +year (e.g. 1998, rather than 98 - PNG is year 2000 compliant!), and +that months start with 1. + +If you want to store the time of the original image creation, you should +use a plain tEXt chunk with the "Creation Time" keyword. This is +necessary because the "creation time" of a PNG image is somewhat vague, +depending on whether you mean the PNG file, the time the image was +created in a non-PNG format, a still photo from which the image was +scanned, or possibly the subject matter itself. In order to facilitate +machine-readable dates, it is recommended that the "Creation Time" +tEXt chunk use RFC 1123 format dates (e.g. "22 May 1997 18:07:10 GMT"), +although this isn't a requirement. Unlike the tIME chunk, the +"Creation Time" tEXt chunk is not expected to be automatically changed +by the software. To facilitate the use of RFC 1123 dates, a function +png_convert_to_rfc1123(png_timep) is provided to convert from PNG +time to an RFC 1123 format string. + +.SS Writing unknown chunks + +You can use the png_set_unknown_chunks function to queue up chunks +for writing. You give it a chunk name, raw data, and a size; that's +all there is to it. The chunks will be written by the next following +png_write_info_before_PLTE, png_write_info, or png_write_end function. +Any chunks previously read into the info structure's unknown-chunk +list will also be written out in a sequence that satisfies the PNG +specification's ordering rules. + +.SS The high-level write interface + +At this point there are two ways to proceed; through the high-level +write interface, or through a sequence of low-level write operations. +You can use the high-level interface if your image data is present +in the info structure. All defined output +transformations are permitted, enabled by the following masks. + + PNG_TRANSFORM_IDENTITY No transformation + PNG_TRANSFORM_PACKING Pack 1, 2 and 4-bit samples + PNG_TRANSFORM_PACKSWAP Change order of packed + pixels to LSB first + PNG_TRANSFORM_INVERT_MONO Invert monochrome images + PNG_TRANSFORM_SHIFT Normalize pixels to the + sBIT depth + PNG_TRANSFORM_BGR Flip RGB to BGR, RGBA + to BGRA + PNG_TRANSFORM_SWAP_ALPHA Flip RGBA to ARGB or GA + to AG + PNG_TRANSFORM_INVERT_ALPHA Change alpha from opacity + to transparency + PNG_TRANSFORM_SWAP_ENDIAN Byte-swap 16-bit samples + PNG_TRANSFORM_STRIP_FILLER Strip out filler bytes. + +If you have valid image data in the info structure (you can use +png_set_rows() to put image data in the info structure), simply do this: + + png_write_png(png_ptr, info_ptr, png_transforms, NULL) + +where png_transforms is an integer containing the bitwise OR of some set of +transformation flags. This call is equivalent to png_write_info(), +followed the set of transformations indicated by the transform mask, +then png_write_image(), and finally png_write_end(). + +(The final parameter of this call is not yet used. Someday it might point +to transformation parameters required by some future output transform.) + +You must use png_transforms and not call any png_set_transform() functions +when you use png_write_png(). + +.SS The low-level write interface + +If you are going the low-level route instead, you are now ready to +write all the file information up to the actual image data. You do +this with a call to png_write_info(). + + png_write_info(png_ptr, info_ptr); + +Note that there is one transformation you may need to do before +png_write_info(). In PNG files, the alpha channel in an image is the +level of opacity. If your data is supplied as a level of +transparency, you can invert the alpha channel before you write it, so +that 0 is fully transparent and 255 (in 8-bit or paletted images) or +65535 (in 16-bit images) is fully opaque, with + + png_set_invert_alpha(png_ptr); + +This must appear before png_write_info() instead of later with the +other transformations because in the case of paletted images the tRNS +chunk data has to be inverted before the tRNS chunk is written. If +your image is not a paletted image, the tRNS data (which in such cases +represents a single color to be rendered as transparent) won't need to +be changed, and you can safely do this transformation after your +png_write_info() call. + +If you need to write a private chunk that you want to appear before +the PLTE chunk when PLTE is present, you can write the PNG info in +two steps, and insert code to write your own chunk between them: + + png_write_info_before_PLTE(png_ptr, info_ptr); + png_set_unknown_chunks(png_ptr, info_ptr, ...); + png_write_info(png_ptr, info_ptr); + +After you've written the file information, you can set up the library +to handle any special transformations of the image data. The various +ways to transform the data will be described in the order that they +should occur. This is important, as some of these change the color +type and/or bit depth of the data, and some others only work on +certain color types and bit depths. Even though each transformation +checks to see if it has data that it can do something with, you should +make sure to only enable a transformation if it will be valid for the +data. For example, don't swap red and blue on grayscale data. + +PNG files store RGB pixels packed into 3 or 6 bytes. This code tells +the library to strip input data that has 4 or 8 bytes per pixel down +to 3 or 6 bytes (or strip 2 or 4-byte grayscale+filler data to 1 or 2 +bytes per pixel). + + png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE); + +where the 0 is unused, and the location is either PNG_FILLER_BEFORE or +PNG_FILLER_AFTER, depending upon whether the filler byte in the pixel +is stored XRGB or RGBX. + +PNG files pack pixels of bit depths 1, 2, and 4 into bytes as small as +they can, resulting in, for example, 8 pixels per byte for 1 bit files. +If the data is supplied at 1 pixel per byte, use this code, which will +correctly pack the pixels into a single byte: + + png_set_packing(png_ptr); + +PNG files reduce possible bit depths to 1, 2, 4, 8, and 16. If your +data is of another bit depth, you can write an sBIT chunk into the +file so that decoders can recover the original data if desired. + + /* Set the true bit depth of the image data */ + if (color_type & PNG_COLOR_MASK_COLOR) + { + sig_bit.red = true_bit_depth; + sig_bit.green = true_bit_depth; + sig_bit.blue = true_bit_depth; + } + else + { + sig_bit.gray = true_bit_depth; + } + if (color_type & PNG_COLOR_MASK_ALPHA) + { + sig_bit.alpha = true_bit_depth; + } + + png_set_sBIT(png_ptr, info_ptr, &sig_bit); + +If the data is stored in the row buffer in a bit depth other than +one supported by PNG (e.g. 3 bit data in the range 0-7 for a 4-bit PNG), +this will scale the values to appear to be the correct bit depth as +is required by PNG. + + png_set_shift(png_ptr, &sig_bit); + +PNG files store 16 bit pixels in network byte order (big-endian, +ie. most significant bits first). This code would be used if they are +supplied the other way (little-endian, i.e. least significant bits +first, the way PCs store them): + + if (bit_depth > 8) + png_set_swap(png_ptr); + +If you are using packed-pixel images (1, 2, or 4 bits/pixel), and you +need to change the order the pixels are packed into bytes, you can use: + + if (bit_depth < 8) + png_set_packswap(png_ptr); + +PNG files store 3 color pixels in red, green, blue order. This code +would be used if they are supplied as blue, green, red: + + png_set_bgr(png_ptr); + +PNG files describe monochrome as black being zero and white being +one. This code would be used if the pixels are supplied with this reversed +(black being one and white being zero): + + png_set_invert_mono(png_ptr); + +Finally, you can write your own transformation function if none of +the existing ones meets your needs. This is done by setting a callback +with + + png_set_write_user_transform_fn(png_ptr, + write_transform_fn); + +You must supply the function + + void write_transform_fn(png_ptr ptr, row_info_ptr + row_info, png_bytep data) + +See pngtest.c for a working example. Your function will be called +before any of the other transformations are processed. + +You can also set up a pointer to a user structure for use by your +callback function. + + png_set_user_transform_info(png_ptr, user_ptr, 0, 0); + +The user_channels and user_depth parameters of this function are ignored +when writing; you can set them to zero as shown. + +You can retrieve the pointer via the function png_get_user_transform_ptr(). +For example: + + voidp write_user_transform_ptr = + png_get_user_transform_ptr(png_ptr); + +It is possible to have libpng flush any pending output, either manually, +or automatically after a certain number of lines have been written. To +flush the output stream a single time call: + + png_write_flush(png_ptr); + +and to have libpng flush the output stream periodically after a certain +number of scanlines have been written, call: + + png_set_flush(png_ptr, nrows); + +Note that the distance between rows is from the last time png_write_flush() +was called, or the first row of the image if it has never been called. +So if you write 50 lines, and then png_set_flush 25, it will flush the +output on the next scanline, and every 25 lines thereafter, unless +png_write_flush() is called before 25 more lines have been written. +If nrows is too small (less than about 10 lines for a 640 pixel wide +RGB image) the image compression may decrease noticeably (although this +may be acceptable for real-time applications). Infrequent flushing will +only degrade the compression performance by a few percent over images +that do not use flushing. + +.SS Writing the image data + +That's it for the transformations. Now you can write the image data. +The simplest way to do this is in one function call. If you have the +whole image in memory, you can just call png_write_image() and libpng +will write the image. You will need to pass in an array of pointers to +each row. This function automatically handles interlacing, so you don't +need to call png_set_interlace_handling() or call this function multiple +times, or any of that other stuff necessary with png_write_rows(). + + png_write_image(png_ptr, row_pointers); + +where row_pointers is: + + png_byte *row_pointers[height]; + +You can point to void or char or whatever you use for pixels. + +If you don't want to write the whole image at once, you can +use png_write_rows() instead. If the file is not interlaced, +this is simple: + + png_write_rows(png_ptr, row_pointers, + number_of_rows); + +row_pointers is the same as in the png_write_image() call. + +If you are just writing one row at a time, you can do this with +a single row_pointer instead of an array of row_pointers: + + png_bytep row_pointer = row; + + png_write_row(png_ptr, row_pointer); + +When the file is interlaced, things can get a good deal more +complicated. The only currently (as of the PNG Specification +version 1.2, dated July 1999) defined interlacing scheme for PNG files +is the "Adam7" interlace scheme, that breaks down an +image into seven smaller images of varying size. libpng will build +these images for you, or you can do them yourself. If you want to +build them yourself, see the PNG specification for details of which +pixels to write when. + +If you don't want libpng to handle the interlacing details, just +use png_set_interlace_handling() and call png_write_rows() the +correct number of times to write all seven sub-images. + +If you want libpng to build the sub-images, call this before you start +writing any rows: + + number_of_passes = + png_set_interlace_handling(png_ptr); + +This will return the number of passes needed. Currently, this +is seven, but may change if another interlace type is added. + +Then write the complete image number_of_passes times. + + png_write_rows(png_ptr, row_pointers, + number_of_rows); + +As some of these rows are not used, and thus return immediately, +you may want to read about interlacing in the PNG specification, +and only update the rows that are actually used. + +.SS Finishing a sequential write + +After you are finished writing the image, you should finish writing +the file. If you are interested in writing comments or time, you should +pass an appropriately filled png_info pointer. If you are not interested, +you can pass NULL. + + png_write_end(png_ptr, info_ptr); + +When you are done, you can free all memory used by libpng like this: + + png_destroy_write_struct(&png_ptr, &info_ptr); + +It is also possible to individually free the info_ptr members that +point to libpng-allocated storage with the following function: + + png_free_data(png_ptr, info_ptr, mask, seq) + mask - identifies data to be freed, a mask + containing the bitwise OR of one or + more of + PNG_FREE_PLTE, PNG_FREE_TRNS, + PNG_FREE_HIST, PNG_FREE_ICCP, + PNG_FREE_PCAL, PNG_FREE_ROWS, + PNG_FREE_SCAL, PNG_FREE_SPLT, + PNG_FREE_TEXT, PNG_FREE_UNKN, + or simply PNG_FREE_ALL + seq - sequence number of item to be freed + (-1 for all items) + +This function may be safely called when the relevant storage has +already been freed, or has not yet been allocated, or was allocated +by the user and not by libpng, and will in those +cases do nothing. The "seq" parameter is ignored if only one item +of the selected data type, such as PLTE, is allowed. If "seq" is not +-1, and multiple items are allowed for the data type identified in +the mask, such as text or sPLT, only the n'th item in the structure +is freed, where n is "seq". + +If you allocated data such as a palette that you passed +in to libpng with png_set_*, you must not free it until just before the call to +png_destroy_write_struct(). + +The default behavior is only to free data that was allocated internally +by libpng. This can be changed, so that libpng will not free the data, +or so that it will free data that was allocated by the user with png_malloc() +or png_zalloc() and passed in via a png_set_*() function, with + + png_data_freer(png_ptr, info_ptr, freer, mask) + mask - which data elements are affected + same choices as in png_free_data() + freer - one of + PNG_DESTROY_WILL_FREE_DATA + PNG_SET_WILL_FREE_DATA + PNG_USER_WILL_FREE_DATA + +For example, to transfer responsibility for some data from a read structure +to a write structure, you could use + + png_data_freer(read_ptr, read_info_ptr, + PNG_USER_WILL_FREE_DATA, + PNG_FREE_PLTE|PNG_FREE_tRNS|PNG_FREE_hIST) + png_data_freer(write_ptr, write_info_ptr, + PNG_DESTROY_WILL_FREE_DATA, + PNG_FREE_PLTE|PNG_FREE_tRNS|PNG_FREE_hIST) + +thereby briefly reassigning responsibility for freeing to the user but +immediately afterwards reassigning it once more to the write_destroy +function. Having done this, it would then be safe to destroy the read +structure and continue to use the PLTE, tRNS, and hIST data in the write +structure. + +This function only affects data that has already been allocated. +You can call this function before calling after the png_set_*() functions +to control whether the user or png_destroy_*() is supposed to free the data. +When the user assumes responsibility for libpng-allocated data, the +application must use +png_free() to free it, and when the user transfers responsibility to libpng +for data that the user has allocated, the user must have used png_malloc() +or png_zalloc() to allocate it. + +If you allocated text_ptr.text, text_ptr.lang, and text_ptr.translated_keyword +separately, do not transfer responsibility for freeing text_ptr to libpng, +because when libpng fills a png_text structure it combines these members with +the key member, and png_free_data() will free only text_ptr.key. Similarly, +if you transfer responsibility for free'ing text_ptr from libpng to your +application, your application must not separately free those members. +For a more compact example of writing a PNG image, see the file example.c. + +.SH V. Modifying/Customizing libpng: + +There are two issues here. The first is changing how libpng does +standard things like memory allocation, input/output, and error handling. +The second deals with more complicated things like adding new chunks, +adding new transformations, and generally changing how libpng works. +Both of those are compile-time issues; that is, they are generally +determined at the time the code is written, and there is rarely a need +to provide the user with a means of changing them. + +Memory allocation, input/output, and error handling + +All of the memory allocation, input/output, and error handling in libpng +goes through callbacks that are user-settable. The default routines are +in pngmem.c, pngrio.c, pngwio.c, and pngerror.c, respectively. To change +these functions, call the appropriate png_set_*_fn() function. + +Memory allocation is done through the functions png_malloc() +and png_free(). These currently just call the standard C functions. If +your pointers can't access more then 64K at a time, you will want to set +MAXSEG_64K in zlib.h. Since it is unlikely that the method of handling +memory allocation on a platform will change between applications, these +functions must be modified in the library at compile time. If you prefer +to use a different method of allocating and freeing data, you can use +png_create_read_struct_2() or png_create_write_struct_2() to register +your own functions as described above. +These functions also provide a void pointer that can be retrieved via + + mem_ptr=png_get_mem_ptr(png_ptr); + +Your replacement memory functions must have prototypes as follows: + + png_voidp malloc_fn(png_structp png_ptr, + png_size_t size); + void free_fn(png_structp png_ptr, png_voidp ptr); + +Your malloc_fn() must return NULL in case of failure. The png_malloc() +function will normally call png_error() if it receives a NULL from the +system memory allocator or from your replacement malloc_fn(). + +Input/Output in libpng is done through png_read() and png_write(), +which currently just call fread() and fwrite(). The FILE * is stored in +png_struct and is initialized via png_init_io(). If you wish to change +the method of I/O, the library supplies callbacks that you can set +through the function png_set_read_fn() and png_set_write_fn() at run +time, instead of calling the png_init_io() function. These functions +also provide a void pointer that can be retrieved via the function +png_get_io_ptr(). For example: + + png_set_read_fn(png_structp read_ptr, + voidp read_io_ptr, png_rw_ptr read_data_fn) + + png_set_write_fn(png_structp write_ptr, + voidp write_io_ptr, png_rw_ptr write_data_fn, + png_flush_ptr output_flush_fn); + + voidp read_io_ptr = png_get_io_ptr(read_ptr); + voidp write_io_ptr = png_get_io_ptr(write_ptr); + +The replacement I/O functions must have prototypes as follows: + + void user_read_data(png_structp png_ptr, + png_bytep data, png_size_t length); + void user_write_data(png_structp png_ptr, + png_bytep data, png_size_t length); + void user_flush_data(png_structp png_ptr); + +Supplying NULL for the read, write, or flush functions sets them back +to using the default C stream functions. It is an error to read from +a write stream, and vice versa. + +Error handling in libpng is done through png_error() and png_warning(). +Errors handled through png_error() are fatal, meaning that png_error() +should never return to its caller. Currently, this is handled via +setjmp() and longjmp() (unless you have compiled libpng with +PNG_SETJMP_NOT_SUPPORTED, in which case it is handled via PNG_ABORT()), +but you could change this to do things like exit() if you should wish. + +On non-fatal errors, png_warning() is called +to print a warning message, and then control returns to the calling code. +By default png_error() and png_warning() print a message on stderr via +fprintf() unless the library is compiled with PNG_NO_CONSOLE_IO defined +(because you don't want the messages) or PNG_NO_STDIO defined (because +fprintf() isn't available). If you wish to change the behavior of the error +functions, you will need to set up your own message callbacks. These +functions are normally supplied at the time that the png_struct is created. +It is also possible to redirect errors and warnings to your own replacement +functions after png_create_*_struct() has been called by calling: + + png_set_error_fn(png_structp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, + png_error_ptr warning_fn); + + png_voidp error_ptr = png_get_error_ptr(png_ptr); + +If NULL is supplied for either error_fn or warning_fn, then the libpng +default function will be used, calling fprintf() and/or longjmp() if a +problem is encountered. The replacement error functions should have +parameters as follows: + + void user_error_fn(png_structp png_ptr, + png_const_charp error_msg); + void user_warning_fn(png_structp png_ptr, + png_const_charp warning_msg); + +The motivation behind using setjmp() and longjmp() is the C++ throw and +catch exception handling methods. This makes the code much easier to write, +as there is no need to check every return code of every function call. +However, there are some uncertainties about the status of local variables +after a longjmp, so the user may want to be careful about doing anything after +setjmp returns non-zero besides returning itself. Consult your compiler +documentation for more details. For an alternative approach, you may wish +to use the "cexcept" facility (see http://cexcept.sourceforge.net). + +.SS Custom chunks + +If you need to read or write custom chunks, you may need to get deeper +into the libpng code. The library now has mechanisms for storing +and writing chunks of unknown type; you can even declare callbacks +for custom chunks. However, this may not be good enough if the +library code itself needs to know about interactions between your +chunk and existing `intrinsic' chunks. + +If you need to write a new intrinsic chunk, first read the PNG +specification. Acquire a first level of +understanding of how it works. Pay particular attention to the +sections that describe chunk names, and look at how other chunks were +designed, so you can do things similarly. Second, check out the +sections of libpng that read and write chunks. Try to find a chunk +that is similar to yours and use it as a template. More details can +be found in the comments inside the code. It is best to handle unknown +chunks in a generic method, via callback functions, instead of by +modifying libpng functions. + +If you wish to write your own transformation for the data, look through +the part of the code that does the transformations, and check out some of +the simpler ones to get an idea of how they work. Try to find a similar +transformation to the one you want to add and copy off of it. More details +can be found in the comments inside the code itself. + +.SS Configuring for 16 bit platforms + +You will want to look into zconf.h to tell zlib (and thus libpng) that +it cannot allocate more then 64K at a time. Even if you can, the memory +won't be accessible. So limit zlib and libpng to 64K by defining MAXSEG_64K. + +.SS Configuring for DOS + +For DOS users who only have access to the lower 640K, you will +have to limit zlib's memory usage via a png_set_compression_mem_level() +call. See zlib.h or zconf.h in the zlib library for more information. + +.SS Configuring for Medium Model + +Libpng's support for medium model has been tested on most of the popular +compilers. Make sure MAXSEG_64K gets defined, USE_FAR_KEYWORD gets +defined, and FAR gets defined to far in pngconf.h, and you should be +all set. Everything in the library (except for zlib's structure) is +expecting far data. You must use the typedefs with the p or pp on +the end for pointers (or at least look at them and be careful). Make +note that the rows of data are defined as png_bytepp, which is an +unsigned char far * far *. + +.SS Configuring for gui/windowing platforms: + +You will need to write new error and warning functions that use the GUI +interface, as described previously, and set them to be the error and +warning functions at the time that png_create_*_struct() is called, +in order to have them available during the structure initialization. +They can be changed later via png_set_error_fn(). On some compilers, +you may also have to change the memory allocators (png_malloc, etc.). + +.SS Configuring for compiler xxx: + +All includes for libpng are in pngconf.h. If you need to add/change/delete +an include, this is the place to do it. The includes that are not +needed outside libpng are protected by the PNG_INTERNAL definition, +which is only defined for those routines inside libpng itself. The +files in libpng proper only include png.h, which includes pngconf.h. + +.SS Configuring zlib: + +There are special functions to configure the compression. Perhaps the +most useful one changes the compression level, which currently uses +input compression values in the range 0 - 9. The library normally +uses the default compression level (Z_DEFAULT_COMPRESSION = 6). Tests +have shown that for a large majority of images, compression values in +the range 3-6 compress nearly as well as higher levels, and do so much +faster. For online applications it may be desirable to have maximum speed +(Z_BEST_SPEED = 1). With versions of zlib after v0.99, you can also +specify no compression (Z_NO_COMPRESSION = 0), but this would create +files larger than just storing the raw bitmap. You can specify the +compression level by calling: + + png_set_compression_level(png_ptr, level); + +Another useful one is to reduce the memory level used by the library. +The memory level defaults to 8, but it can be lowered if you are +short on memory (running DOS, for example, where you only have 640K). +Note that the memory level does have an effect on compression; among +other things, lower levels will result in sections of incompressible +data being emitted in smaller stored blocks, with a correspondingly +larger relative overhead of up to 15% in the worst case. + + png_set_compression_mem_level(png_ptr, level); + +The other functions are for configuring zlib. They are not recommended +for normal use and may result in writing an invalid PNG file. See +zlib.h for more information on what these mean. + + png_set_compression_strategy(png_ptr, + strategy); + png_set_compression_window_bits(png_ptr, + window_bits); + png_set_compression_method(png_ptr, method); + png_set_compression_buffer_size(png_ptr, size); + +.SS Controlling row filtering + +If you want to control whether libpng uses filtering or not, which +filters are used, and how it goes about picking row filters, you +can call one of these functions. The selection and configuration +of row filters can have a significant impact on the size and +encoding speed and a somewhat lesser impact on the decoding speed +of an image. Filtering is enabled by default for RGB and grayscale +images (with and without alpha), but not for paletted images nor +for any images with bit depths less than 8 bits/pixel. + +The 'method' parameter sets the main filtering method, which is +currently only '0' in the PNG 1.2 specification. The 'filters' +parameter sets which filter(s), if any, should be used for each +scanline. Possible values are PNG_ALL_FILTERS and PNG_NO_FILTERS +to turn filtering on and off, respectively. + +Individual filter types are PNG_FILTER_NONE, PNG_FILTER_SUB, +PNG_FILTER_UP, PNG_FILTER_AVG, PNG_FILTER_PAETH, which can be bitwise +ORed together with '|' to specify one or more filters to use. +These filters are described in more detail in the PNG specification. +If you intend to change the filter type during the course of writing +the image, you should start with flags set for all of the filters +you intend to use so that libpng can initialize its internal +structures appropriately for all of the filter types. (Note that this +means the first row must always be adaptively filtered, because libpng +currently does not allocate the filter buffers until png_write_row() +is called for the first time.) + + filters = PNG_FILTER_NONE | PNG_FILTER_SUB + PNG_FILTER_UP | PNG_FILTER_AVE | + PNG_FILTER_PAETH | PNG_ALL_FILTERS; + + png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, + filters); + The second parameter can also be + PNG_INTRAPIXEL_DIFFERENCING if you are + writing a PNG to be embedded in a MNG + datastream. This parameter must be the + same as the value of filter_method used + in png_set_IHDR(). + +It is also possible to influence how libpng chooses from among the +available filters. This is done in one or both of two ways - by +telling it how important it is to keep the same filter for successive +rows, and by telling it the relative computational costs of the filters. + + double weights[3] = {1.5, 1.3, 1.1}, + costs[PNG_FILTER_VALUE_LAST] = + {1.0, 1.3, 1.3, 1.5, 1.7}; + + png_set_filter_heuristics(png_ptr, + PNG_FILTER_HEURISTIC_WEIGHTED, 3, + weights, costs); + +The weights are multiplying factors that indicate to libpng that the +row filter should be the same for successive rows unless another row filter +is that many times better than the previous filter. In the above example, +if the previous 3 filters were SUB, SUB, NONE, the SUB filter could have a +"sum of absolute differences" 1.5 x 1.3 times higher than other filters +and still be chosen, while the NONE filter could have a sum 1.1 times +higher than other filters and still be chosen. Unspecified weights are +taken to be 1.0, and the specified weights should probably be declining +like those above in order to emphasize recent filters over older filters. + +The filter costs specify for each filter type a relative decoding cost +to be considered when selecting row filters. This means that filters +with higher costs are less likely to be chosen over filters with lower +costs, unless their "sum of absolute differences" is that much smaller. +The costs do not necessarily reflect the exact computational speeds of +the various filters, since this would unduly influence the final image +size. + +Note that the numbers above were invented purely for this example and +are given only to help explain the function usage. Little testing has +been done to find optimum values for either the costs or the weights. + +.SS Removing unwanted object code + +There are a bunch of #define's in pngconf.h that control what parts of +libpng are compiled. All the defines end in _SUPPORTED. If you are +never going to use a capability, you can change the #define to #undef +before recompiling libpng and save yourself code and data space, or +you can turn off individual capabilities with defines that begin with +PNG_NO_. + +You can also turn all of the transforms and ancillary chunk capabilities +off en masse with compiler directives that define +PNG_NO_READ[or WRITE]_TRANSFORMS, or PNG_NO_READ[or WRITE]_ANCILLARY_CHUNKS, +or all four, +along with directives to turn on any of the capabilities that you do +want. The PNG_NO_READ[or WRITE]_TRANSFORMS directives disable +the extra transformations but still leave the library fully capable of reading +and writing PNG files with all known public chunks +Use of the PNG_NO_READ[or WRITE]_ANCILLARY_CHUNKS directive +produces a library that is incapable of reading or writing ancillary chunks. +If you are not using the progressive reading capability, you can +turn that off with PNG_NO_PROGRESSIVE_READ (don't confuse +this with the INTERLACING capability, which you'll still have). + +All the reading and writing specific code are in separate files, so the +linker should only grab the files it needs. However, if you want to +make sure, or if you are building a stand alone library, all the +reading files start with pngr and all the writing files start with +pngw. The files that don't match either (like png.c, pngtrans.c, etc.) +are used for both reading and writing, and always need to be included. +The progressive reader is in pngpread.c + +If you are creating or distributing a dynamically linked library (a .so +or DLL file), you should not remove or disable any parts of the library, +as this will cause applications linked with different versions of the +library to fail if they call functions not available in your library. +The size of the library itself should not be an issue, because only +those sections that are actually used will be loaded into memory. + +.SS Requesting debug printout + +The macro definition PNG_DEBUG can be used to request debugging +printout. Set it to an integer value in the range 0 to 3. Higher +numbers result in increasing amounts of debugging information. The +information is printed to the "stderr" file, unless another file +name is specified in the PNG_DEBUG_FILE macro definition. + +When PNG_DEBUG > 0, the following functions (macros) become available: + + png_debug(level, message) + png_debug1(level, message, p1) + png_debug2(level, message, p1, p2) + +in which "level" is compared to PNG_DEBUG to decide whether to print +the message, "message" is the formatted string to be printed, +and p1 and p2 are parameters that are to be embedded in the string +according to printf-style formatting directives. For example, + + png_debug1(2, "foo=%d\n", foo); + +is expanded to + + if(PNG_DEBUG > 2) + fprintf(PNG_DEBUG_FILE, "foo=%d\n", foo); + +When PNG_DEBUG is defined but is zero, the macros aren't defined, but you +can still use PNG_DEBUG to control your own debugging: + + #ifdef PNG_DEBUG + fprintf(stderr, ... + #endif + +When PNG_DEBUG = 1, the macros are defined, but only png_debug statements +having level = 0 will be printed. There aren't any such statements in +this version of libpng, but if you insert some they will be printed. + +.SH VII. MNG support + +The MNG specification (available at http://www.libpng.org/pub/mng) allows +certain extensions to PNG for PNG images that are embedded in MNG datastreams. +Libpng can support some of these extensions. To enable them, use the +png_permit_mng_features() function: + + feature_set = png_permit_mng_features(png_ptr, mask) + mask is a png_uint_32 containing the bitwise OR of the + features you want to enable. These include + PNG_FLAG_MNG_EMPTY_PLTE + PNG_FLAG_MNG_FILTER_64 + PNG_ALL_MNG_FEATURES + feature_set is a png_uint_32 that is the bitwise AND of + your mask with the set of MNG features that is + supported by the version of libpng that you are using. + +It is an error to use this function when reading or writing a standalone +PNG file with the PNG 8-byte signature. The PNG datastream must be wrapped +in a MNG datastream. As a minimum, it must have the MNG 8-byte signature +and the MHDR and MEND chunks. Libpng does not provide support for these +or any other MNG chunks; your application must provide its own support for +them. You may wish to consider using libmng (available at +http://www.libmng.com) instead. + +.SH VIII. Changes to Libpng from version 0.88 + +It should be noted that versions of libpng later than 0.96 are not +distributed by the original libpng author, Guy Schalnat, nor by +Andreas Dilger, who had taken over from Guy during 1996 and 1997, and +distributed versions 0.89 through 0.96, but rather by another member +of the original PNG Group, Glenn Randers-Pehrson. Guy and Andreas are +still alive and well, but they have moved on to other things. + +The old libpng functions png_read_init(), png_write_init(), +png_info_init(), png_read_destroy(), and png_write_destroy() have been +moved to PNG_INTERNAL in version 0.95 to discourage their use. These +functions will be removed from libpng version 2.0.0. + +The preferred method of creating and initializing the libpng structures is +via the png_create_read_struct(), png_create_write_struct(), and +png_create_info_struct() because they isolate the size of the structures +from the application, allow version error checking, and also allow the +use of custom error handling routines during the initialization, which +the old functions do not. The functions png_read_destroy() and +png_write_destroy() do not actually free the memory that libpng +allocated for these structs, but just reset the data structures, so they +can be used instead of png_destroy_read_struct() and +png_destroy_write_struct() if you feel there is too much system overhead +allocating and freeing the png_struct for each image read. + +Setting the error callbacks via png_set_message_fn() before +png_read_init() as was suggested in libpng-0.88 is no longer supported +because this caused applications that do not use custom error functions +to fail if the png_ptr was not initialized to zero. It is still possible +to set the error callbacks AFTER png_read_init(), or to change them with +png_set_error_fn(), which is essentially the same function, but with a new +name to force compilation errors with applications that try to use the old +method. + +Starting with version 1.0.7, you can find out which version of the library +you are using at run-time: + + png_uint_32 libpng_vn = png_access_version_number(); + +The number libpng_vn is constructed from the major version, minor +version with leading zero, and release number with leading zero, +(e.g., libpng_vn for version 1.0.7 is 10007). + +You can also check which version of png.h you used when compiling your +application: + + png_uint_32 application_vn = PNG_LIBPNG_VER; + +.SH IX. Y2K Compliance in libpng + +December 14, 2007 + +Since the PNG Development group is an ad-hoc body, we can't make +an official declaration. + +This is your unofficial assurance that libpng from version 0.71 and +upward through 1.2.24 are Y2K compliant. It is my belief that earlier +versions were also Y2K compliant. + +Libpng only has three year fields. One is a 2-byte unsigned integer that +will hold years up to 65535. The other two hold the date in text +format, and will hold years up to 9999. + +The integer is + "png_uint_16 year" in png_time_struct. + +The strings are + "png_charp time_buffer" in png_struct and + "near_time_buffer", which is a local character string in png.c. + +There are seven time-related functions: + + png_convert_to_rfc_1123() in png.c + (formerly png_convert_to_rfc_1152() in error) + png_convert_from_struct_tm() in pngwrite.c, called + in pngwrite.c + png_convert_from_time_t() in pngwrite.c + png_get_tIME() in pngget.c + png_handle_tIME() in pngrutil.c, called in pngread.c + png_set_tIME() in pngset.c + png_write_tIME() in pngwutil.c, called in pngwrite.c + +All appear to handle dates properly in a Y2K environment. The +png_convert_from_time_t() function calls gmtime() to convert from system +clock time, which returns (year - 1900), which we properly convert to +the full 4-digit year. There is a possibility that applications using +libpng are not passing 4-digit years into the png_convert_to_rfc_1123() +function, or that they are incorrectly passing only a 2-digit year +instead of "year - 1900" into the png_convert_from_struct_tm() function, +but this is not under our control. The libpng documentation has always +stated that it works with 4-digit years, and the APIs have been +documented as such. + +The tIME chunk itself is also Y2K compliant. It uses a 2-byte unsigned +integer to hold the year, and can hold years as large as 65535. + +zlib, upon which libpng depends, is also Y2K compliant. It contains +no date-related code. + + + Glenn Randers-Pehrson + libpng maintainer + PNG Development Group + +.SH NOTE + +Note about libpng version numbers: + +Due to various miscommunications, unforeseen code incompatibilities +and occasional factors outside the authors' control, version numbering +on the library has not always been consistent and straightforward. +The following table summarizes matters since version 0.89c, which was +the first widely used release: + + source png.h png.h shared-lib + version string int version + ------- ------ ----- ---------- + 0.89c ("beta 3") 0.89 89 1.0.89 + 0.90 ("beta 4") 0.90 90 0.90 + 0.95 ("beta 5") 0.95 95 0.95 + 0.96 ("beta 6") 0.96 96 0.96 + 0.97b ("beta 7") 1.00.97 97 1.0.1 + 0.97c 0.97 97 2.0.97 + 0.98 0.98 98 2.0.98 + 0.99 0.99 98 2.0.99 + 0.99a-m 0.99 99 2.0.99 + 1.00 1.00 100 2.1.0 + 1.0.0 1.0.0 100 2.1.0 + 1.0.0 (from here on, the 100 2.1.0 + 1.0.1 png.h string is 10001 2.1.0 + 1.0.1a-e identical to the 10002 from here on, the + 1.0.2 source version) 10002 shared library is 2.V + 1.0.2a-b 10003 where V is the source + 1.0.1 10001 code version except as + 1.0.1a-e 10002 2.1.0.1a-e noted. + 1.0.2 10002 2.1.0.2 + 1.0.2a-b 10003 2.1.0.2a-b + 1.0.3 10003 2.1.0.3 + 1.0.3a-d 10004 2.1.0.3a-d + 1.0.4 10004 2.1.0.4 + 1.0.4a-f 10005 2.1.0.4a-f + 1.0.5 (+ 2 patches) 10005 2.1.0.5 + 1.0.5a-d 10006 2.1.0.5a-d + 1.0.5e-r 10100 2.1.0.5e-r + 1.0.5s-v 10006 2.1.0.5s-v + 1.0.6 (+ 3 patches) 10006 2.1.0.6 + 1.0.6d-g 10007 2.1.0.6d-g + 1.0.6h 10007 10.6h + 1.0.6i 10007 10.6i + 1.0.6j 10007 2.1.0.6j + 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 + 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 + 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 + 1.0.7 1 10007 2.1.0.7 + 1.0.8beta1-4 1 10008 2.1.0.8beta1-4 + 1.0.8rc1 1 10008 2.1.0.8rc1 + 1.0.8 1 10008 2.1.0.8 + 1.0.9beta1-6 1 10009 2.1.0.9beta1-6 + 1.0.9rc1 1 10009 2.1.0.9rc1 + 1.0.9beta7-10 1 10009 2.1.0.9beta7-10 + 1.0.9rc2 1 10009 2.1.0.9rc2 + 1.0.9 1 10009 2.1.0.9 + 1.0.10beta1 1 10010 2.1.0.10beta1 + 1.0.10rc1 1 10010 2.1.0.10rc1 + 1.0.10 1 10010 2.1.0.10 + 1.0.11beta1-3 1 10011 2.1.0.11beta1-3 + 1.0.11rc1 1 10011 2.1.0.11rc1 + 1.0.11 1 10011 2.1.0.11 + 1.0.12beta1-2 2 10012 2.1.0.12beta1-2 + 1.0.12rc1 2 10012 2.1.0.12rc1 + 1.0.12 2 10012 2.1.0.12 + 1.1.0a-f - 10100 2.1.1.0a-f abandoned + 1.2.0beta1-2 2 10200 2.1.2.0beta1-2 + 1.2.0beta3-5 3 10200 3.1.2.0beta3-5 + 1.2.0rc1 3 10200 3.1.2.0rc1 + 1.2.0 3 10200 3.1.2.0 + 1.2.1beta-4 3 10201 3.1.2.1beta1-4 + 1.2.1rc1-2 3 10201 3.1.2.1rc1-2 + 1.2.1 3 10201 3.1.2.1 + 1.2.2beta1-6 12 10202 12.so.0.1.2.2beta1-6 + 1.0.13beta1 10 10013 10.so.0.1.0.13beta1 + 1.0.13rc1 10 10013 10.so.0.1.0.13rc1 + 1.2.2rc1 12 10202 12.so.0.1.2.2rc1 + 1.0.13 10 10013 10.so.0.1.0.13 + 1.2.2 12 10202 12.so.0.1.2.2 + 1.2.3rc1-6 12 10203 12.so.0.1.2.3rc1-6 + 1.2.3 12 10203 12.so.0.1.2.3 + 1.2.4beta1-3 13 10204 12.so.0.1.2.4beta1-3 + 1.2.4rc1 13 10204 12.so.0.1.2.4rc1 + 1.0.14 10 10014 10.so.0.1.0.14 + 1.2.4 13 10204 12.so.0.1.2.4 + 1.2.5beta1-2 13 10205 12.so.0.1.2.5beta1-2 + 1.0.15rc1 10 10015 10.so.0.1.0.15rc1 + 1.0.15 10 10015 10.so.0.1.0.15 + 1.2.5 13 10205 12.so.0.1.2.5 + 1.2.6beta1-4 13 10206 12.so.0.1.2.6beta1-4 + 1.2.6rc1-5 13 10206 12.so.0.1.2.6rc1-5 + 1.0.16 10 10016 10.so.0.1.0.16 + 1.2.6 13 10206 12.so.0.1.2.6 + 1.2.7beta1-2 13 10207 12.so.0.1.2.7beta1-2 + 1.0.17rc1 10 10017 10.so.0.1.0.17rc1 + 1.2.7rc1 13 10207 12.so.0.1.2.7rc1 + 1.0.17 10 10017 10.so.0.1.0.17 + 1.2.7 13 10207 12.so.0.1.2.7 + 1.2.8beta1-5 13 10208 12.so.0.1.2.8beta1-5 + 1.0.18rc1-5 10 10018 10.so.0.1.0.18rc1-5 + 1.2.8rc1-5 13 10208 12.so.0.1.2.8rc1-5 + 1.0.18 10 10018 10.so.0.1.0.18 + 1.2.8 13 10208 12.so.0.1.2.8 + 1.2.9beta1-3 13 10209 12.so.0.1.2.9beta1-3 + 1.2.9beta4-11 13 10209 12.so.0.9[.0] + 1.2.9rc1 13 10209 12.so.0.9[.0] + 1.2.9 13 10209 12.so.0.9[.0] + 1.2.10beta1-8 13 10210 12.so.0.10[.0] + 1.2.10rc1-3 13 10210 12.so.0.10[.0] + 1.2.10 13 10210 12.so.0.10[.0] + 1.2.11beta1-4 13 10211 12.so.0.11[.0] + 1.0.19rc1-5 10 10019 10.so.0.19[.0] + 1.2.11rc1-5 13 10211 12.so.0.11[.0] + 1.0.19 10 10019 10.so.0.19[.0] + 1.2.11 13 10211 12.so.0.11[.0] + 1.0.20 10 10020 10.so.0.20[.0] + 1.2.12 13 10212 12.so.0.12[.0] + 1.2.13beta1 13 10213 12.so.0.13[.0] + 1.0.21 10 10021 10.so.0.21[.0] + 1.2.13 13 10213 12.so.0.13[.0] + 1.2.14beta1-2 13 10214 12.so.0.14[.0] + 1.0.22rc1 10 10022 10.so.0.22[.0] + 1.2.14rc1 13 10214 12.so.0.14[.0] + 1.2.15beta1-6 13 10215 12.so.0.15[.0] + 1.0.23rc1-5 10 10023 10.so.0.23[.0] + 1.2.15rc1-5 13 10215 12.so.0.15[.0] + 1.0.23 10 10023 10.so.0.23[.0] + 1.2.15 13 10215 12.so.0.15[.0] + 1.2.16beta1-2 13 10216 12.so.0.16[.0] + 1.2.16rc1 13 10216 12.so.0.16[.0] + 1.0.24 10 10024 10.so.0.24[.0] + 1.2.16 13 10216 12.so.0.16[.0] + 1.2.17beta1-2 13 10217 12.so.0.17[.0] + 1.0.25rc1 10 10025 10.so.0.25[.0] + 1.2.17rc1-3 13 10217 12.so.0.17[.0] + 1.0.25 10 10025 10.so.0.25[.0] + 1.2.17 13 10217 12.so.0.17[.0] + 1.0.26 10 10026 10.so.0.26[.0] + 1.2.18 13 10218 12.so.0.18[.0] + 1.2.19beta1-31 13 10219 12.so.0.19[.0] + 1.0.27rc1-6 10 10027 10.so.0.27[.0] + 1.2.19rc1-6 13 10219 12.so.0.19[.0] + 1.0.27 10 10027 10.so.0.27[.0] + 1.2.19 13 10219 12.so.0.19[.0] + 1.2.20beta01-04 13 10220 12.so.0.20[.0] + 1.0.28rc1-6 10 10028 10.so.0.28[.0] + 1.2.20rc1-6 13 10220 12.so.0.20[.0] + 1.0.28 10 10028 10.so.0.28[.0] + 1.2.20 13 10220 12.so.0.20[.0] + 1.2.21beta1-2 13 10221 12.so.0.21[.0] + 1.2.21rc1-3 13 10221 12.so.0.21[.0] + 1.0.29 10 10029 10.so.0.29[.0] + 1.2.21 13 10221 12.so.0.21[.0] + 1.2.22beta1-4 13 10222 12.so.0.22[.0] + 1.0.30rc1 13 10030 10.so.0.30[.0] + 1.2.22rc1 13 10222 12.so.0.22[.0] + 1.0.30 10 10030 10.so.0.30[.0] + 1.2.22 13 10222 12.so.0.22[.0] + 1.2.23beta01-05 13 10223 12.so.0.23[.0] + 1.2.23rc01 13 10223 12.so.0.23[.0] + 1.2.23 13 10223 12.so.0.23[.0] + 1.2.24beta01-02 13 10224 12.so.0.24[.0] + 1.2.24rc01 13 10224 12.so.0.24[.0] + 1.2.24 13 10224 12.so.0.24[.0] + +Henceforth the source version will match the shared-library minor +and patch numbers; the shared-library major version number will be +used for changes in backward compatibility, as it is intended. The +PNG_PNGLIB_VER macro, which is not used within libpng but is available +for applications, is an unsigned integer of the form xyyzz corresponding +to the source version x.y.z (leading zeros in y and z). Beta versions +were given the previous public release number plus a letter, until +version 1.0.6j; from then on they were given the upcoming public +release number plus "betaNN" or "rcN". + +.SH "SEE ALSO" +.IR libpngpf(3) ", " png(5) +.LP +.IR libpng : +.IP +http://libpng.sourceforge.net (follow the [DOWNLOAD] link) +http://www.libpng.org/pub/png + +.LP +.IR zlib : +.IP +(generally) at the same location as +.I libpng +or at +.br +ftp://ftp.info-zip.org/pub/infozip/zlib + +.LP +.IR PNG specification: RFC 2083 +.IP +(generally) at the same location as +.I libpng +or at +.br +ftp://ds.internic.net/rfc/rfc2083.txt +.br +or (as a W3C Recommendation) at +.br +http://www.w3.org/TR/REC-png.html + +.LP +In the case of any inconsistency between the PNG specification +and this library, the specification takes precedence. + +.SH AUTHORS +This man page: Glenn Randers-Pehrson + + +The contributing authors would like to thank all those who helped +with testing, bug fixes, and patience. This wouldn't have been +possible without all of you. + +Thanks to Frank J. T. Wojcik for helping with the documentation. + +Libpng version 1.2.24 - December 14, 2007: +Initially created in 1995 by Guy Eric Schalnat, then of Group 42, Inc. +Currently maintained by Glenn Randers-Pehrson (glennrp at users.sourceforge.net). + +Supported by the PNG development group +.br +png-mng-implement at lists.sf.net +(subscription required; visit +png-mng-implement at lists.sourceforge.net (subscription required; visit +https://lists.sourceforge.net/lists/listinfo/png-mng-implement +to subscribe). + +.SH COPYRIGHT NOTICE, DISCLAIMER, and LICENSE: + +(This copy of the libpng notices is provided for your convenience. In case of +any discrepancy between this copy and the notices in the file png.h that is +included in the libpng distribution, the latter shall prevail.) + +If you modify libpng you may insert additional notices immediately following +this sentence. + +libpng versions 1.2.6, August 15, 2004, through 1.2.24, December 14, 2007, are +Copyright (c) 2004,2006-2007 Glenn Randers-Pehrson, and are +distributed according to the same disclaimer and license as libpng-1.2.5 +with the following individual added to the list of Contributing Authors + + Cosmin Truta + +libpng versions 1.0.7, July 1, 2000, through 1.2.5 - October 3, 2002, are +Copyright (c) 2000-2002 Glenn Randers-Pehrson, and are +distributed according to the same disclaimer and license as libpng-1.0.6 +with the following individuals added to the list of Contributing Authors + + Simon-Pierre Cadieux + Eric S. Raymond + Gilles Vollant + +and with the following additions to the disclaimer: + + There is no warranty against interference with your + enjoyment of the library or against infringement. + There is no warranty that our efforts or the library + will fulfill any of your particular purposes or needs. + This library is provided with all faults, and the entire + risk of satisfactory quality, performance, accuracy, and + effort is with the user. + +libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are +Copyright (c) 1998, 1999 Glenn Randers-Pehrson +Distributed according to the same disclaimer and license as libpng-0.96, +with the following individuals added to the list of Contributing Authors: + + Tom Lane + Glenn Randers-Pehrson + Willem van Schaik + +libpng versions 0.89, June 1996, through 0.96, May 1997, are +Copyright (c) 1996, 1997 Andreas Dilger +Distributed according to the same disclaimer and license as libpng-0.88, +with the following individuals added to the list of Contributing Authors: + + John Bowler + Kevin Bracey + Sam Bushell + Magnus Holmgren + Greg Roelofs + Tom Tanner + +libpng versions 0.5, May 1995, through 0.88, January 1996, are +Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc. + +For the purposes of this copyright and license, "Contributing Authors" +is defined as the following set of individuals: + + Andreas Dilger + Dave Martindale + Guy Eric Schalnat + Paul Schmidt + Tim Wegner + +The PNG Reference Library is supplied "AS IS". The Contributing Authors +and Group 42, Inc. disclaim all warranties, expressed or implied, +including, without limitation, the warranties of merchantability and of +fitness for any purpose. The Contributing Authors and Group 42, Inc. +assume no liability for direct, indirect, incidental, special, exemplary, +or consequential damages, which may result from the use of the PNG +Reference Library, even if advised of the possibility of such damage. + +Permission is hereby granted to use, copy, modify, and distribute this +source code, or portions hereof, for any purpose, without fee, subject +to the following restrictions: + +1. The origin of this source code must not be misrepresented. + +2. Altered versions must be plainly marked as such and + must not be misrepresented as being the original source. + +3. This Copyright notice may not be removed or altered from + any source or altered source distribution. + +The Contributing Authors and Group 42, Inc. specifically permit, without +fee, and encourage the use of this source code as a component to +supporting the PNG file format in commercial products. If you use this +source code in a product, acknowledgment is not required but would be +appreciated. + + +A "png_get_copyright" function is available, for convenient use in "about" +boxes and the like: + + printf("%s",png_get_copyright(NULL)); + +Also, the PNG logo (in PNG format, of course) is supplied in the +files "pngbar.png" and "pngbar.jpg (88x31) and "pngnow.png" (98x31). + +Libpng is OSI Certified Open Source Software. OSI Certified Open Source is a +certification mark of the Open Source Initiative. + +Glenn Randers-Pehrson +glennrp at users.sourceforge.net +December 14, 2007 + +.\" end of man page + diff --git a/Engine/lib/lpng/libpngpf.3 b/Engine/lib/lpng/libpngpf.3 new file mode 100644 index 000000000..1e360de12 --- /dev/null +++ b/Engine/lib/lpng/libpngpf.3 @@ -0,0 +1,274 @@ +.TH LIBPNGPF 3 "December 14, 2007" +.SH NAME +libpng \- Portable Network Graphics (PNG) Reference Library 1.2.24 +(private functions) +.SH SYNOPSIS +\fB#include \fP + +\fBvoid png_build_gamma_table (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_build_grayscale_palette (int \fP\fIbit_depth\fP\fB, png_colorp \fIpalette\fP\fB);\fP + +\fBvoid png_calculate_crc (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fP\fIptr\fP\fB, png_size_t \fIlength\fP\fB);\fP + +\fBvoid png_check_chunk_name (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fIchunk_name\fP\fB);\fP + +\fBpng_size_t png_check_keyword (png_structp \fP\fIpng_ptr\fP\fB, png_charp \fP\fIkey\fP\fB, png_charpp \fInew_key\fP\fB);\fP + +\fBvoid png_combine_row (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fP\fIrow\fP\fB, int \fImask\fP\fB);\fP + +\fBvoid png_correct_palette (png_structp \fP\fIpng_ptr\fP\fB, png_colorp \fP\fIpalette\fP\fB, int \fInum_palette\fP\fB);\fP + +\fBint png_crc_error (png_structp \fIpng_ptr\fP\fB);\fP + +\fBint png_crc_finish (png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fIskip\fP\fB);\fP + +\fBvoid png_crc_read (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fP\fIbuf\fP\fB, png_size_t \fIlength\fP\fB);\fP + +\fBpng_voidp png_create_struct (int \fItype\fP\fB);\fP + +\fBpng_voidp png_create_struct_2 (int \fP\fItype\fP\fB, png_malloc_ptr \fP\fImalloc_fn\fP\fB, png_voidp \fImem_ptr\fP\fB);\fP + +\fBpng_charp png_decompress_chunk (png_structp \fP\fIpng_ptr\fP\fB, int \fP\fIcomp_type\fP\fB, png_charp \fP\fIchunkdata\fP\fB, png_size_t \fP\fIchunklength\fP\fB, png_size_t \fP\fIprefix_length\fP\fB, png_size_t \fI*data_length\fP\fB);\fP + +\fBvoid png_destroy_struct (png_voidp \fIstruct_ptr\fP\fB);\fP + +\fBvoid png_destroy_struct_2 (png_voidp \fP\fIstruct_ptr\fP\fB, png_free_ptr \fP\fIfree_fn\fP\fB, png_voidp \fImem_ptr\fP\fB);\fP + +\fBvoid png_do_background (png_row_infop \fP\fIrow_info\fP\fB, png_bytep \fP\fIrow\fP\fB, png_color_16p \fP\fItrans_values\fP\fB, png_color_16p \fP\fIbackground\fP\fB, png_color_16p \fP\fIbackground_1\fP\fB, png_bytep \fP\fIgamma_table\fP\fB, png_bytep \fP\fIgamma_from_1\fP\fB, png_bytep \fP\fIgamma_to_1\fP\fB, png_uint_16pp \fP\fIgamma_16\fP\fB, png_uint_16pp \fP\fIgamma_16_from_1\fP\fB, png_uint_16pp \fP\fIgamma_16_to_1\fP\fB, int \fIgamma_shift\fP\fB);\fP + +\fBvoid png_do_bgr (png_row_infop \fP\fIrow_info\fP\fB, png_bytep \fIrow\fP\fB);\fP + +\fBvoid png_do_chop (png_row_infop \fP\fIrow_info\fP\fB, png_bytep \fIrow\fP\fB);\fP + +\fBvoid png_do_dither (png_row_infop \fP\fIrow_info\fP\fB, png_bytep \fP\fIrow\fP\fB, png_bytep \fP\fIpalette_lookup\fP\fB, png_bytep \fIdither_lookup\fP\fB);\fP + +\fBvoid png_do_expand (png_row_infop \fP\fIrow_info\fP\fB, png_bytep \fP\fIrow\fP\fB, png_color_16p \fItrans_value\fP\fB);\fP + +\fBvoid png_do_expand_palette (png_row_infop \fP\fIrow_info\fP\fB, png_bytep \fP\fIrow\fP\fB, png_colorp \fP\fIpalette\fP\fB, png_bytep \fP\fItrans\fP\fB, int \fInum_trans\fP\fB);\fP + +\fBvoid png_do_gamma (png_row_infop \fP\fIrow_info\fP\fB, png_bytep \fP\fIrow\fP\fB, png_bytep \fP\fIgamma_table\fP\fB, png_uint_16pp \fP\fIgamma_16_table\fP\fB, int \fIgamma_shift\fP\fB);\fP + +\fBvoid png_do_gray_to_rgb (png_row_infop \fP\fIrow_info\fP\fB, png_bytep \fIrow\fP\fB);\fP + +\fBvoid png_do_invert (png_row_infop \fP\fIrow_info\fP\fB, png_bytep \fIrow\fP\fB);\fP + +\fBvoid png_do_pack (png_row_infop \fP\fIrow_info\fP\fB, png_bytep \fP\fIrow\fP\fB, png_uint_32 \fIbit_depth\fP\fB);\fP + +\fBvoid png_do_packswap (png_row_infop \fP\fIrow_info\fP\fB, png_bytep \fIrow\fP\fB);\fP + +\fBvoid png_do_read_filler (png_row_infop \fP\fIrow_info\fP\fB, png_bytep \fP\fIrow\fP\fB, png_uint_32 \fP\fIfiller\fP\fB, png_uint_32 \fIflags\fP\fB);\fP + +\fBvoid png_do_read_interlace (png_row_infop \fP\fIrow_info\fP\fB, png_bytep \fP\fIrow\fP\fB, int \fP\fIpass\fP\fB, png_uint_32 \fItransformations\fP\fB);\fP + +\fBvoid png_do_read_invert_alpha (png_row_infop \fP\fIrow_info\fP\fB, png_bytep \fIrow\fP\fB);\fP + +\fBvoid png_do_read_swap_alpha (png_row_infop \fP\fIrow_info\fP\fB, png_bytep \fIrow\fP\fB);\fP + +\fBvoid png_do_read_transformations (png_structp \fIpng_ptr\fP\fB);\fP + +\fBint png_do_rgb_to_gray (png_row_infop \fP\fIrow_info\fP\fB, png_bytep \fIrow\fP\fB);\fP + +\fBvoid png_do_shift (png_row_infop \fP\fIrow_info\fP\fB, png_bytep \fP\fIrow\fP\fB, png_color_8p \fIbit_depth\fP\fB);\fP + +\fBvoid png_do_strip_filler (png_row_infop \fP\fIrow_info\fP\fB, png_bytep \fP\fIrow\fP\fB, png_uint_32 \fIflags\fP\fB);\fP + +\fBvoid png_do_swap (png_row_infop \fP\fIrow_info\fP\fB, png_bytep \fIrow\fP\fB);\fP + +\fBvoid png_do_unpack (png_row_infop \fP\fIrow_info\fP\fB, png_bytep \fIrow\fP\fB);\fP + +\fBvoid png_do_unshift (png_row_infop \fP\fIrow_info\fP\fB, png_bytep \fP\fIrow\fP\fB, png_color_8p \fIsig_bits\fP\fB);\fP + +\fBvoid png_do_write_interlace (png_row_infop \fP\fIrow_info\fP\fB, png_bytep \fP\fIrow\fP\fB, int \fIpass\fP\fB);\fP + +\fBvoid png_do_write_invert_alpha (png_row_infop \fP\fIrow_info\fP\fB, png_bytep \fIrow\fP\fB);\fP + +\fBvoid png_do_write_swap_alpha (png_row_infop \fP\fIrow_info\fP\fB, png_bytep \fIrow\fP\fB);\fP + +\fBvoid png_do_write_transformations (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid *png_far_to_near (png_structp png_ptr,png_voidp \fP\fIptr\fP\fB, int \fIcheck\fP\fB);\fP + +\fBvoid png_flush (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_handle_bKGD (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fBvoid png_handle_cHRM (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fBvoid png_handle_gAMA (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fBvoid png_handle_hIST (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fBvoid png_handle_IEND (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fBvoid png_handle_IHDR (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fBvoid png_handle_iCCP (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fBvoid png_handle_iTXt (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fBvoid png_handle_oFFs (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fBvoid png_handle_pCAL (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fBvoid png_handle_pHYs (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fBvoid png_handle_PLTE (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fBvoid png_handle_sBIT (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fBvoid png_handle_sCAL (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fBvoid png_handle_sPLT (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fBvoid png_handle_sRGB (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fBvoid png_handle_tEXt (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fBvoid png_handle_tIME (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fBvoid png_handle_tRNS (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fBvoid png_handle_unknown (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fBvoid png_handle_zTXt (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fBvoid png_info_destroy (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBvoid png_init_mmx_flags (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_init_read_transformations (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_process_IDAT_data (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fP\fIbuffer\fP\fB, png_size_t \fIbuffer_length\fP\fB);\fP + +\fBvoid png_process_some_data (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBvoid png_push_check_crc (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_push_crc_finish (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_push_crc_skip (png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fBvoid png_push_fill_buffer (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fP\fIbuffer\fP\fB, png_size_t \fIlength\fP\fB);\fP + +\fBvoid png_push_handle_tEXt (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fBvoid png_push_handle_unknown (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fBvoid png_push_handle_zTXt (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_uint_32 \fIlength\fP\fB);\fP + +\fBvoid png_push_have_end (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBvoid png_push_have_info (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBvoid png_push_have_row (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fIrow\fP\fB);\fP + +\fBvoid png_push_process_row (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_push_read_chunk (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBvoid png_push_read_end (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBvoid png_push_read_IDAT (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_push_read_sig (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBvoid png_push_read_tEXt (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBvoid png_push_read_zTXt (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBvoid png_push_restore_buffer (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fP\fIbuffer\fP\fB, png_size_t \fIbuffer_length\fP\fB);\fP + +\fBvoid png_push_save_buffer (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_read_data (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fP\fIdata\fP\fB, png_size_t \fIlength\fP\fB);\fP + +\fBvoid png_read_filter_row (png_structp \fP\fIpng_ptr\fP\fB, png_row_infop \fP\fIrow_info\fP\fB, png_bytep \fP\fIrow\fP\fB, png_bytep \fP\fIprev_row\fP\fB, int \fIfilter\fP\fB);\fP + +\fBvoid png_read_finish_row (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_read_push_finish_row (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_read_start_row (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_read_transform_info (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fIinfo_ptr\fP\fB);\fP + +\fBvoid png_reset_crc (png_structp \fIpng_ptr\fP\fB);\fP + +\fBint png_set_text_2 (png_structp \fP\fIpng_ptr\fP\fB, png_infop \fP\fIinfo_ptr\fP\fB, png_textp \fP\fItext_ptr\fP\fB, int \fInum_text\fP\fB);\fP + +\fBvoid png_write_cHRM (png_structp \fP\fIpng_ptr\fP\fB, double \fP\fIwhite_x\fP\fB, double \fP\fIwhite_y\fP\fB, double \fP\fIred_x\fP\fB, double \fP\fIred_y\fP\fB, double \fP\fIgreen_x\fP\fB, double \fP\fIgreen_y\fP\fB, double \fP\fIblue_x\fP\fB, double \fIblue_y\fP\fB);\fP + +\fBvoid png_write_cHRM_fixed (png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fP\fIwhite_x\fP\fB, png_uint_32 \fP\fIwhite_y\fP\fB, png_uint_32 \fP\fIred_x\fP\fB, png_uint_32 \fP\fIred_y\fP\fB, png_uint_32 \fP\fIgreen_x\fP\fB, png_uint_32 \fP\fIgreen_y\fP\fB, png_uint_32 \fP\fIblue_x\fP\fB, png_uint_32 \fIblue_y\fP\fB);\fP + +\fBvoid png_write_data (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fP\fIdata\fP\fB, png_size_t \fIlength\fP\fB);\fP + +\fBvoid png_write_filtered_row (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fIfiltered_row\fP\fB);\fP + +\fBvoid png_write_find_filter (png_structp \fP\fIpng_ptr\fP\fB, png_row_infop \fIrow_info\fP\fB);\fP + +\fBvoid png_write_finish_row (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_write_gAMA (png_structp \fP\fIpng_ptr\fP\fB, double \fIfile_gamma\fP\fB);\fP + +\fBvoid png_write_gAMA_fixed (png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fIint_file_gamma\fP\fB);\fP + +\fBvoid png_write_hIST (png_structp \fP\fIpng_ptr\fP\fB, png_uint_16p \fP\fIhist\fP\fB, int \fInum_hist\fP\fB);\fP + +\fBvoid png_write_iCCP (png_structp \fP\fIpng_ptr\fP\fB, png_charp \fP\fIname\fP\fB, int \fP\fIcompression_type\fP\fB, png_charp \fP\fIprofile\fP\fB, int \fIproflen\fP\fB);\fP + +\fBvoid png_write_IDAT (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fP\fIdata\fP\fB, png_size_t \fIlength\fP\fB);\fP + +\fBvoid png_write_IEND (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_write_IHDR (png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fP\fIwidth\fP\fB, png_uint_32 \fP\fIheight\fP\fB, int \fP\fIbit_depth\fP\fB, int \fP\fIcolor_type\fP\fB, int \fP\fIcompression_type\fP\fB, int \fP\fIfilter_type\fP\fB, int \fIinterlace_type\fP\fB);\fP + +\fBvoid png_write_iTXt (png_structp \fP\fIpng_ptr\fP\fB, int \fP\fIcompression\fP\fB, png_charp \fP\fIkey\fP\fB, png_charp \fP\fIlang\fP\fB, png_charp \fP\fItranslated_key\fP\fB, png_charp \fItext\fP\fB);\fP + +\fBvoid png_write_oFFs (png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fP\fIx_offset\fP\fB, png_uint_32 \fP\fIy_offset\fP\fB, int \fIunit_type\fP\fB);\fP + +\fBvoid png_write_pCAL (png_structp \fP\fIpng_ptr\fP\fB, png_charp \fP\fIpurpose\fP\fB, png_int_32 \fP\fIX0\fP\fB, png_int_32 \fP\fIX1\fP\fB, int \fP\fItype\fP\fB, int \fP\fInparams\fP\fB, png_charp \fP\fIunits\fP\fB, png_charpp \fIparams\fP\fB);\fP + +\fBvoid png_write_pHYs (png_structp \fP\fIpng_ptr\fP\fB, png_uint_32 \fP\fIx_pixels_per_unit\fP\fB, png_uint_32 \fP\fIy_pixels_per_unit\fP\fB, int \fIunit_type\fP\fB);\fP + +\fBvoid png_write_PLTE (png_structp \fP\fIpng_ptr\fP\fB, png_colorp \fP\fIpalette\fP\fB, png_uint_32 \fInum_pal\fP\fB);\fP + +\fBvoid png_write_sBIT (png_structp \fP\fIpng_ptr\fP\fB, png_color_8p \fP\fIsbit\fP\fB, int \fIcolor_type\fP\fB);\fP + +\fBvoid png_write_sCAL (png_structp \fP\fIpng_ptr\fP\fB, png_charp \fP\fIunit\fP\fB, double \fP\fIwidth\fP\fB, double \fIheight\fP\fB);\fP + +\fBvoid png_write_sCAL_s (png_structp \fP\fIpng_ptr\fP\fB, png_charp \fP\fIunit\fP\fB, png_charp \fP\fIwidth\fP\fB, png_charp \fIheight\fP\fB);\fP + +\fBvoid png_write_sig (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_write_sRGB (png_structp \fP\fIpng_ptr\fP\fB, int \fIintent\fP\fB);\fP + +\fBvoid png_write_sPLT (png_structp \fP\fIpng_ptr\fP\fB, png_spalette_p \fIpalette\fP\fB);\fP + +\fBvoid png_write_start_row (png_structp \fIpng_ptr\fP\fB);\fP + +\fBvoid png_write_tEXt (png_structp \fP\fIpng_ptr\fP\fB, png_charp \fP\fIkey\fP\fB, png_charp \fP\fItext\fP\fB, png_size_t \fItext_len\fP\fB);\fP + +\fBvoid png_write_tIME (png_structp \fP\fIpng_ptr\fP\fB, png_timep \fImod_time\fP\fB);\fP + +\fBvoid png_write_tRNS (png_structp \fP\fIpng_ptr\fP\fB, png_bytep \fP\fItrans\fP\fB, png_color_16p \fP\fIvalues\fP\fB, int \fP\fInumber\fP\fB, int \fIcolor_type\fP\fB);\fP + +\fBvoid png_write_zTXt (png_structp \fP\fIpng_ptr\fP\fB, png_charp \fP\fIkey\fP\fB, png_charp \fP\fItext\fP\fB, png_size_t \fP\fItext_len\fP\fB, int \fIcompression\fP\fB);\fP + +\fBvoidpf png_zalloc (voidpf \fP\fIpng_ptr\fP\fB, uInt \fP\fIitems\fP\fB, uInt \fIsize\fP\fB);\fP + +\fBvoid png_zfree (voidpf \fP\fIpng_ptr\fP\fB, voidpf \fIptr\fP\fB);\fP + +\fI\fB + +.SH DESCRIPTION +The functions listed above are used privately by libpng +and are not recommended for use by applications. They are +not "exported" to applications using shared libraries. They +are listed alphabetically here as an aid to libpng maintainers. +See png.h for more information on these functions. + +.SH SEE ALSO +.IR libpng(3) ", " png(5) +.SH AUTHOR +Glenn Randers-Pehrson diff --git a/Engine/lib/lpng/png.5 b/Engine/lib/lpng/png.5 new file mode 100644 index 000000000..9f3346058 --- /dev/null +++ b/Engine/lib/lpng/png.5 @@ -0,0 +1,74 @@ +.TH PNG 5 "December 14, 2007" +.SH NAME +png \- Portable Network Graphics (PNG) format +.SH DESCRIPTION +PNG (Portable Network Graphics) is an extensible file format for the +lossless, portable, well-compressed storage of raster images. PNG provides +a patent-free replacement for GIF and can also replace many +common uses of TIFF. Indexed-color, grayscale, and truecolor images are +supported, plus an optional alpha channel. Sample depths range from +1 to 16 bits. +.br + +PNG is designed to work well in online viewing applications, such as the +World Wide Web, so it is fully streamable with a progressive display +option. PNG is robust, providing both full file integrity checking and +fast, simple detection of common transmission errors. Also, PNG can store +gamma and chromaticity data for improved color matching on heterogeneous +platforms. + +.SH "SEE ALSO" +.IR libpng(3) ", " zlib(3) ", " deflate(5) ", and " zlib(5) +.LP +PNG specification (second edition), November 2003: +.IP +.br + 8) + png_error(png_ptr, "Too many bytes for PNG signature."); + + png_ptr->sig_bytes = (png_byte)(num_bytes < 0 ? 0 : num_bytes); +} + +/* Checks whether the supplied bytes match the PNG signature. We allow + * checking less than the full 8-byte signature so that those apps that + * already read the first few bytes of a file to determine the file type + * can simply check the remaining bytes for extra assurance. Returns + * an integer less than, equal to, or greater than zero if sig is found, + * respectively, to be less than, to match, or be greater than the correct + * PNG signature (this is the same behaviour as strcmp, memcmp, etc). + */ +int PNGAPI +png_sig_cmp(png_bytep sig, png_size_t start, png_size_t num_to_check) +{ + png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + if (num_to_check > 8) + num_to_check = 8; + else if (num_to_check < 1) + return (-1); + + if (start > 7) + return (-1); + + if (start + num_to_check > 8) + num_to_check = 8 - start; + + return ((int)(png_memcmp(&sig[start], &png_signature[start], num_to_check))); +} + +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +/* (Obsolete) function to check signature bytes. It does not allow one + * to check a partial signature. This function might be removed in the + * future - use png_sig_cmp(). Returns true (nonzero) if the file is PNG. + */ +int PNGAPI +png_check_sig(png_bytep sig, int num) +{ + return ((int)!png_sig_cmp(sig, (png_size_t)0, (png_size_t)num)); +} +#endif +#endif /* PNG_READ_SUPPORTED */ + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +/* Function to allocate memory for zlib and clear it to 0. */ +#ifdef PNG_1_0_X +voidpf PNGAPI +#else +voidpf /* private */ +#endif +png_zalloc(voidpf png_ptr, uInt items, uInt size) +{ + png_voidp ptr; + png_structp p=(png_structp)png_ptr; + png_uint_32 save_flags=p->flags; + png_uint_32 num_bytes; + + if(png_ptr == NULL) return (NULL); + if (items > PNG_UINT_32_MAX/size) + { + png_warning (p, "Potential overflow in png_zalloc()"); + return (NULL); + } + num_bytes = (png_uint_32)items * size; + + p->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK; + ptr = (png_voidp)png_malloc((png_structp)png_ptr, num_bytes); + p->flags=save_flags; + +#if defined(PNG_1_0_X) && !defined(PNG_NO_ZALLOC_ZERO) + if (ptr == NULL) + return ((voidpf)ptr); + + if (num_bytes > (png_uint_32)0x8000L) + { + png_memset(ptr, 0, (png_size_t)0x8000L); + png_memset((png_bytep)ptr + (png_size_t)0x8000L, 0, + (png_size_t)(num_bytes - (png_uint_32)0x8000L)); + } + else + { + png_memset(ptr, 0, (png_size_t)num_bytes); + } +#endif + return ((voidpf)ptr); +} + +/* function to free memory for zlib */ +#ifdef PNG_1_0_X +void PNGAPI +#else +void /* private */ +#endif +png_zfree(voidpf png_ptr, voidpf ptr) +{ + png_free((png_structp)png_ptr, (png_voidp)ptr); +} + +/* Reset the CRC variable to 32 bits of 1's. Care must be taken + * in case CRC is > 32 bits to leave the top bits 0. + */ +void /* PRIVATE */ +png_reset_crc(png_structp png_ptr) +{ + png_ptr->crc = crc32(0, Z_NULL, 0); +} + +/* Calculate the CRC over a section of data. We can only pass as + * much data to this routine as the largest single buffer size. We + * also check that this data will actually be used before going to the + * trouble of calculating it. + */ +void /* PRIVATE */ +png_calculate_crc(png_structp png_ptr, png_bytep ptr, png_size_t length) +{ + int need_crc = 1; + + if (png_ptr->chunk_name[0] & 0x20) /* ancillary */ + { + if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == + (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) + need_crc = 0; + } + else /* critical */ + { + if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) + need_crc = 0; + } + + if (need_crc) + png_ptr->crc = crc32(png_ptr->crc, ptr, (uInt)length); +} + +/* Allocate the memory for an info_struct for the application. We don't + * really need the png_ptr, but it could potentially be useful in the + * future. This should be used in favour of malloc(png_sizeof(png_info)) + * and png_info_init() so that applications that want to use a shared + * libpng don't have to be recompiled if png_info changes size. + */ +png_infop PNGAPI +png_create_info_struct(png_structp png_ptr) +{ + png_infop info_ptr; + + png_debug(1, "in png_create_info_struct\n"); + if(png_ptr == NULL) return (NULL); +#ifdef PNG_USER_MEM_SUPPORTED + info_ptr = (png_infop)png_create_struct_2(PNG_STRUCT_INFO, + png_ptr->malloc_fn, png_ptr->mem_ptr); +#else + info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO); +#endif + if (info_ptr != NULL) + png_info_init_3(&info_ptr, png_sizeof(png_info)); + + return (info_ptr); +} + +/* This function frees the memory associated with a single info struct. + * Normally, one would use either png_destroy_read_struct() or + * png_destroy_write_struct() to free an info struct, but this may be + * useful for some applications. + */ +void PNGAPI +png_destroy_info_struct(png_structp png_ptr, png_infopp info_ptr_ptr) +{ + png_infop info_ptr = NULL; + if(png_ptr == NULL) return; + + png_debug(1, "in png_destroy_info_struct\n"); + if (info_ptr_ptr != NULL) + info_ptr = *info_ptr_ptr; + + if (info_ptr != NULL) + { + png_info_destroy(png_ptr, info_ptr); + +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)info_ptr, png_ptr->free_fn, + png_ptr->mem_ptr); +#else + png_destroy_struct((png_voidp)info_ptr); +#endif + *info_ptr_ptr = NULL; + } +} + +/* Initialize the info structure. This is now an internal function (0.89) + * and applications using it are urged to use png_create_info_struct() + * instead. + */ +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +#undef png_info_init +void PNGAPI +png_info_init(png_infop info_ptr) +{ + /* We only come here via pre-1.0.12-compiled applications */ + png_info_init_3(&info_ptr, 0); +} +#endif + +void PNGAPI +png_info_init_3(png_infopp ptr_ptr, png_size_t png_info_struct_size) +{ + png_infop info_ptr = *ptr_ptr; + + if(info_ptr == NULL) return; + + png_debug(1, "in png_info_init_3\n"); + + if(png_sizeof(png_info) > png_info_struct_size) + { + png_destroy_struct(info_ptr); + info_ptr = (png_infop)png_create_struct(PNG_STRUCT_INFO); + *ptr_ptr = info_ptr; + } + + /* set everything to 0 */ + png_memset(info_ptr, 0, png_sizeof (png_info)); +} + +#ifdef PNG_FREE_ME_SUPPORTED +void PNGAPI +png_data_freer(png_structp png_ptr, png_infop info_ptr, + int freer, png_uint_32 mask) +{ + png_debug(1, "in png_data_freer\n"); + if (png_ptr == NULL || info_ptr == NULL) + return; + if(freer == PNG_DESTROY_WILL_FREE_DATA) + info_ptr->free_me |= mask; + else if(freer == PNG_USER_WILL_FREE_DATA) + info_ptr->free_me &= ~mask; + else + png_warning(png_ptr, + "Unknown freer parameter in png_data_freer."); +} +#endif + +void PNGAPI +png_free_data(png_structp png_ptr, png_infop info_ptr, png_uint_32 mask, + int num) +{ + png_debug(1, "in png_free_data\n"); + if (png_ptr == NULL || info_ptr == NULL) + return; + +#if defined(PNG_TEXT_SUPPORTED) +/* free text item num or (if num == -1) all text items */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_TEXT) & info_ptr->free_me) +#else +if (mask & PNG_FREE_TEXT) +#endif +{ + if (num != -1) + { + if (info_ptr->text && info_ptr->text[num].key) + { + png_free(png_ptr, info_ptr->text[num].key); + info_ptr->text[num].key = NULL; + } + } + else + { + int i; + for (i = 0; i < info_ptr->num_text; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, i); + png_free(png_ptr, info_ptr->text); + info_ptr->text = NULL; + info_ptr->num_text=0; + } +} +#endif + +#if defined(PNG_tRNS_SUPPORTED) +/* free any tRNS entry */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_TRNS) & info_ptr->free_me) +#else +if ((mask & PNG_FREE_TRNS) && (png_ptr->flags & PNG_FLAG_FREE_TRNS)) +#endif +{ + png_free(png_ptr, info_ptr->trans); + info_ptr->valid &= ~PNG_INFO_tRNS; +#ifndef PNG_FREE_ME_SUPPORTED + png_ptr->flags &= ~PNG_FLAG_FREE_TRNS; +#endif + info_ptr->trans = NULL; +} +#endif + +#if defined(PNG_sCAL_SUPPORTED) +/* free any sCAL entry */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_SCAL) & info_ptr->free_me) +#else +if (mask & PNG_FREE_SCAL) +#endif +{ +#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, info_ptr->scal_s_width); + png_free(png_ptr, info_ptr->scal_s_height); + info_ptr->scal_s_width = NULL; + info_ptr->scal_s_height = NULL; +#endif + info_ptr->valid &= ~PNG_INFO_sCAL; +} +#endif + +#if defined(PNG_pCAL_SUPPORTED) +/* free any pCAL entry */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_PCAL) & info_ptr->free_me) +#else +if (mask & PNG_FREE_PCAL) +#endif +{ + png_free(png_ptr, info_ptr->pcal_purpose); + png_free(png_ptr, info_ptr->pcal_units); + info_ptr->pcal_purpose = NULL; + info_ptr->pcal_units = NULL; + if (info_ptr->pcal_params != NULL) + { + int i; + for (i = 0; i < (int)info_ptr->pcal_nparams; i++) + { + png_free(png_ptr, info_ptr->pcal_params[i]); + info_ptr->pcal_params[i]=NULL; + } + png_free(png_ptr, info_ptr->pcal_params); + info_ptr->pcal_params = NULL; + } + info_ptr->valid &= ~PNG_INFO_pCAL; +} +#endif + +#if defined(PNG_iCCP_SUPPORTED) +/* free any iCCP entry */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_ICCP) & info_ptr->free_me) +#else +if (mask & PNG_FREE_ICCP) +#endif +{ + png_free(png_ptr, info_ptr->iccp_name); + png_free(png_ptr, info_ptr->iccp_profile); + info_ptr->iccp_name = NULL; + info_ptr->iccp_profile = NULL; + info_ptr->valid &= ~PNG_INFO_iCCP; +} +#endif + +#if defined(PNG_sPLT_SUPPORTED) +/* free a given sPLT entry, or (if num == -1) all sPLT entries */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_SPLT) & info_ptr->free_me) +#else +if (mask & PNG_FREE_SPLT) +#endif +{ + if (num != -1) + { + if(info_ptr->splt_palettes) + { + png_free(png_ptr, info_ptr->splt_palettes[num].name); + png_free(png_ptr, info_ptr->splt_palettes[num].entries); + info_ptr->splt_palettes[num].name = NULL; + info_ptr->splt_palettes[num].entries = NULL; + } + } + else + { + if(info_ptr->splt_palettes_num) + { + int i; + for (i = 0; i < (int)info_ptr->splt_palettes_num; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_SPLT, i); + + png_free(png_ptr, info_ptr->splt_palettes); + info_ptr->splt_palettes = NULL; + info_ptr->splt_palettes_num = 0; + } + info_ptr->valid &= ~PNG_INFO_sPLT; + } +} +#endif + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + if(png_ptr->unknown_chunk.data) + { + png_free(png_ptr, png_ptr->unknown_chunk.data); + png_ptr->unknown_chunk.data = NULL; + } +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_UNKN) & info_ptr->free_me) +#else +if (mask & PNG_FREE_UNKN) +#endif +{ + if (num != -1) + { + if(info_ptr->unknown_chunks) + { + png_free(png_ptr, info_ptr->unknown_chunks[num].data); + info_ptr->unknown_chunks[num].data = NULL; + } + } + else + { + int i; + + if(info_ptr->unknown_chunks_num) + { + for (i = 0; i < (int)info_ptr->unknown_chunks_num; i++) + png_free_data(png_ptr, info_ptr, PNG_FREE_UNKN, i); + + png_free(png_ptr, info_ptr->unknown_chunks); + info_ptr->unknown_chunks = NULL; + info_ptr->unknown_chunks_num = 0; + } + } +} +#endif + +#if defined(PNG_hIST_SUPPORTED) +/* free any hIST entry */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_HIST) & info_ptr->free_me) +#else +if ((mask & PNG_FREE_HIST) && (png_ptr->flags & PNG_FLAG_FREE_HIST)) +#endif +{ + png_free(png_ptr, info_ptr->hist); + info_ptr->hist = NULL; + info_ptr->valid &= ~PNG_INFO_hIST; +#ifndef PNG_FREE_ME_SUPPORTED + png_ptr->flags &= ~PNG_FLAG_FREE_HIST; +#endif +} +#endif + +/* free any PLTE entry that was internally allocated */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_PLTE) & info_ptr->free_me) +#else +if ((mask & PNG_FREE_PLTE) && (png_ptr->flags & PNG_FLAG_FREE_PLTE)) +#endif +{ + png_zfree(png_ptr, info_ptr->palette); + info_ptr->palette = NULL; + info_ptr->valid &= ~PNG_INFO_PLTE; +#ifndef PNG_FREE_ME_SUPPORTED + png_ptr->flags &= ~PNG_FLAG_FREE_PLTE; +#endif + info_ptr->num_palette = 0; +} + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +/* free any image bits attached to the info structure */ +#ifdef PNG_FREE_ME_SUPPORTED +if ((mask & PNG_FREE_ROWS) & info_ptr->free_me) +#else +if (mask & PNG_FREE_ROWS) +#endif +{ + if(info_ptr->row_pointers) + { + int row; + for (row = 0; row < (int)info_ptr->height; row++) + { + png_free(png_ptr, info_ptr->row_pointers[row]); + info_ptr->row_pointers[row]=NULL; + } + png_free(png_ptr, info_ptr->row_pointers); + info_ptr->row_pointers=NULL; + } + info_ptr->valid &= ~PNG_INFO_IDAT; +} +#endif + +#ifdef PNG_FREE_ME_SUPPORTED + if(num == -1) + info_ptr->free_me &= ~mask; + else + info_ptr->free_me &= ~(mask & ~PNG_FREE_MUL); +#endif +} + +/* This is an internal routine to free any memory that the info struct is + * pointing to before re-using it or freeing the struct itself. Recall + * that png_free() checks for NULL pointers for us. + */ +void /* PRIVATE */ +png_info_destroy(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_info_destroy\n"); + + png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + if (png_ptr->num_chunk_list) + { + png_free(png_ptr, png_ptr->chunk_list); + png_ptr->chunk_list=NULL; + png_ptr->num_chunk_list=0; + } +#endif + + png_info_init_3(&info_ptr, png_sizeof(png_info)); +} +#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ + +/* This function returns a pointer to the io_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy() or png_read_destroy() are called. + */ +png_voidp PNGAPI +png_get_io_ptr(png_structp png_ptr) +{ + if(png_ptr == NULL) return (NULL); + return (png_ptr->io_ptr); +} + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#if !defined(PNG_NO_STDIO) +/* Initialize the default input/output functions for the PNG file. If you + * use your own read or write routines, you can call either png_set_read_fn() + * or png_set_write_fn() instead of png_init_io(). If you have defined + * PNG_NO_STDIO, you must use a function of your own because "FILE *" isn't + * necessarily available. + */ +void PNGAPI +png_init_io(png_structp png_ptr, png_FILE_p fp) +{ + png_debug(1, "in png_init_io\n"); + if(png_ptr == NULL) return; + png_ptr->io_ptr = (png_voidp)fp; +} +#endif + +#if defined(PNG_TIME_RFC1123_SUPPORTED) +/* Convert the supplied time into an RFC 1123 string suitable for use in + * a "Creation Time" or other text-based time string. + */ +png_charp PNGAPI +png_convert_to_rfc1123(png_structp png_ptr, png_timep ptime) +{ + static PNG_CONST char short_months[12][4] = + {"Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + + if(png_ptr == NULL) return (NULL); + if (png_ptr->time_buffer == NULL) + { + png_ptr->time_buffer = (png_charp)png_malloc(png_ptr, (png_uint_32)(29* + png_sizeof(char))); + } + +#if defined(_WIN32_WCE) + { + wchar_t time_buf[29]; + wsprintf(time_buf, TEXT("%d %S %d %02d:%02d:%02d +0000"), + ptime->day % 32, short_months[(ptime->month - 1) % 12], + ptime->year, ptime->hour % 24, ptime->minute % 60, + ptime->second % 61); + WideCharToMultiByte(CP_ACP, 0, time_buf, -1, png_ptr->time_buffer, 29, + NULL, NULL); + } +#else +#ifdef USE_FAR_KEYWORD + { + char near_time_buf[29]; + png_snprintf6(near_time_buf,29,"%d %s %d %02d:%02d:%02d +0000", + ptime->day % 32, short_months[(ptime->month - 1) % 12], + ptime->year, ptime->hour % 24, ptime->minute % 60, + ptime->second % 61); + png_memcpy(png_ptr->time_buffer, near_time_buf, + 29*png_sizeof(char)); + } +#else + png_snprintf6(png_ptr->time_buffer,29,"%d %s %d %02d:%02d:%02d +0000", + ptime->day % 32, short_months[(ptime->month - 1) % 12], + ptime->year, ptime->hour % 24, ptime->minute % 60, + ptime->second % 61); +#endif +#endif /* _WIN32_WCE */ + return ((png_charp)png_ptr->time_buffer); +} +#endif /* PNG_TIME_RFC1123_SUPPORTED */ + +#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ + +png_charp PNGAPI +png_get_copyright(png_structp png_ptr) +{ + png_ptr = png_ptr; /* silence compiler warning about unused png_ptr */ + return ((png_charp) "\n libpng version 1.2.24 - December 14, 2007\n\ + Copyright (c) 1998-2007 Glenn Randers-Pehrson\n\ + Copyright (c) 1996-1997 Andreas Dilger\n\ + Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.\n"); +} + +/* The following return the library version as a short string in the + * format 1.0.0 through 99.99.99zz. To get the version of *.h files + * used with your application, print out PNG_LIBPNG_VER_STRING, which + * is defined in png.h. + * Note: now there is no difference between png_get_libpng_ver() and + * png_get_header_ver(). Due to the version_nn_nn_nn typedef guard, + * it is guaranteed that png.c uses the correct version of png.h. + */ +png_charp PNGAPI +png_get_libpng_ver(png_structp png_ptr) +{ + /* Version of *.c files used when building libpng */ + png_ptr = png_ptr; /* silence compiler warning about unused png_ptr */ + return ((png_charp) PNG_LIBPNG_VER_STRING); +} + +png_charp PNGAPI +png_get_header_ver(png_structp png_ptr) +{ + /* Version of *.h files used when building libpng */ + png_ptr = png_ptr; /* silence compiler warning about unused png_ptr */ + return ((png_charp) PNG_LIBPNG_VER_STRING); +} + +png_charp PNGAPI +png_get_header_version(png_structp png_ptr) +{ + /* Returns longer string containing both version and date */ + png_ptr = png_ptr; /* silence compiler warning about unused png_ptr */ + return ((png_charp) PNG_HEADER_VERSION_STRING +#ifndef PNG_READ_SUPPORTED + " (NO READ SUPPORT)" +#endif + "\n"); +} + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +int PNGAPI +png_handle_as_unknown(png_structp png_ptr, png_bytep chunk_name) +{ + /* check chunk_name and return "keep" value if it's on the list, else 0 */ + int i; + png_bytep p; + if(png_ptr == NULL || chunk_name == NULL || png_ptr->num_chunk_list<=0) + return 0; + p=png_ptr->chunk_list+png_ptr->num_chunk_list*5-5; + for (i = png_ptr->num_chunk_list; i; i--, p-=5) + if (!png_memcmp(chunk_name, p, 4)) + return ((int)*(p+4)); + return 0; +} +#endif + +/* This function, added to libpng-1.0.6g, is untested. */ +int PNGAPI +png_reset_zstream(png_structp png_ptr) +{ + if (png_ptr == NULL) return Z_STREAM_ERROR; + return (inflateReset(&png_ptr->zstream)); +} +#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ + +/* This function was added to libpng-1.0.7 */ +png_uint_32 PNGAPI +png_access_version_number(void) +{ + /* Version of *.c files used when building libpng */ + return((png_uint_32) PNG_LIBPNG_VER); +} + + +#if defined(PNG_READ_SUPPORTED) && defined(PNG_ASSEMBLER_CODE_SUPPORTED) +#if !defined(PNG_1_0_X) +/* this function was added to libpng 1.2.0 */ +int PNGAPI +png_mmx_support(void) +{ + /* obsolete, to be removed from libpng-1.4.0 */ + return -1; +} +#endif /* PNG_1_0_X */ +#endif /* PNG_READ_SUPPORTED && PNG_ASSEMBLER_CODE_SUPPORTED */ + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#ifdef PNG_SIZE_T +/* Added at libpng version 1.2.6 */ + PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size)); +png_size_t PNGAPI +png_convert_size(size_t size) +{ + if (size > (png_size_t)-1) + PNG_ABORT(); /* We haven't got access to png_ptr, so no png_error() */ + return ((png_size_t)size); +} +#endif /* PNG_SIZE_T */ +#endif /* defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) */ diff --git a/Engine/lib/lpng/png.h b/Engine/lib/lpng/png.h new file mode 100644 index 000000000..3ac393ea3 --- /dev/null +++ b/Engine/lib/lpng/png.h @@ -0,0 +1,3549 @@ + +/* png.h - header file for PNG reference library + * + * libpng version 1.2.24 - December 14, 2007 + * Copyright (c) 1998-2007 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * Authors and maintainers: + * libpng versions 0.71, May 1995, through 0.88, January 1996: Guy Schalnat + * libpng versions 0.89c, June 1996, through 0.96, May 1997: Andreas Dilger + * libpng versions 0.97, January 1998, through 1.2.24 - December 14, 2007: Glenn + * See also "Contributing Authors", below. + * + * Note about libpng version numbers: + * + * Due to various miscommunications, unforeseen code incompatibilities + * and occasional factors outside the authors' control, version numbering + * on the library has not always been consistent and straightforward. + * The following table summarizes matters since version 0.89c, which was + * the first widely used release: + * + * source png.h png.h shared-lib + * version string int version + * ------- ------ ----- ---------- + * 0.89c "1.0 beta 3" 0.89 89 1.0.89 + * 0.90 "1.0 beta 4" 0.90 90 0.90 [should have been 2.0.90] + * 0.95 "1.0 beta 5" 0.95 95 0.95 [should have been 2.0.95] + * 0.96 "1.0 beta 6" 0.96 96 0.96 [should have been 2.0.96] + * 0.97b "1.00.97 beta 7" 1.00.97 97 1.0.1 [should have been 2.0.97] + * 0.97c 0.97 97 2.0.97 + * 0.98 0.98 98 2.0.98 + * 0.99 0.99 98 2.0.99 + * 0.99a-m 0.99 99 2.0.99 + * 1.00 1.00 100 2.1.0 [100 should be 10000] + * 1.0.0 (from here on, the 100 2.1.0 [100 should be 10000] + * 1.0.1 png.h string is 10001 2.1.0 + * 1.0.1a-e identical to the 10002 from here on, the shared library + * 1.0.2 source version) 10002 is 2.V where V is the source code + * 1.0.2a-b 10003 version, except as noted. + * 1.0.3 10003 + * 1.0.3a-d 10004 + * 1.0.4 10004 + * 1.0.4a-f 10005 + * 1.0.5 (+ 2 patches) 10005 + * 1.0.5a-d 10006 + * 1.0.5e-r 10100 (not source compatible) + * 1.0.5s-v 10006 (not binary compatible) + * 1.0.6 (+ 3 patches) 10006 (still binary incompatible) + * 1.0.6d-f 10007 (still binary incompatible) + * 1.0.6g 10007 + * 1.0.6h 10007 10.6h (testing xy.z so-numbering) + * 1.0.6i 10007 10.6i + * 1.0.6j 10007 2.1.0.6j (incompatible with 1.0.0) + * 1.0.7beta11-14 DLLNUM 10007 2.1.0.7beta11-14 (binary compatible) + * 1.0.7beta15-18 1 10007 2.1.0.7beta15-18 (binary compatible) + * 1.0.7rc1-2 1 10007 2.1.0.7rc1-2 (binary compatible) + * 1.0.7 1 10007 (still compatible) + * 1.0.8beta1-4 1 10008 2.1.0.8beta1-4 + * 1.0.8rc1 1 10008 2.1.0.8rc1 + * 1.0.8 1 10008 2.1.0.8 + * 1.0.9beta1-6 1 10009 2.1.0.9beta1-6 + * 1.0.9rc1 1 10009 2.1.0.9rc1 + * 1.0.9beta7-10 1 10009 2.1.0.9beta7-10 + * 1.0.9rc2 1 10009 2.1.0.9rc2 + * 1.0.9 1 10009 2.1.0.9 + * 1.0.10beta1 1 10010 2.1.0.10beta1 + * 1.0.10rc1 1 10010 2.1.0.10rc1 + * 1.0.10 1 10010 2.1.0.10 + * 1.0.11beta1-3 1 10011 2.1.0.11beta1-3 + * 1.0.11rc1 1 10011 2.1.0.11rc1 + * 1.0.11 1 10011 2.1.0.11 + * 1.0.12beta1-2 2 10012 2.1.0.12beta1-2 + * 1.0.12rc1 2 10012 2.1.0.12rc1 + * 1.0.12 2 10012 2.1.0.12 + * 1.1.0a-f - 10100 2.1.1.0a-f (branch abandoned) + * 1.2.0beta1-2 2 10200 2.1.2.0beta1-2 + * 1.2.0beta3-5 3 10200 3.1.2.0beta3-5 + * 1.2.0rc1 3 10200 3.1.2.0rc1 + * 1.2.0 3 10200 3.1.2.0 + * 1.2.1beta1-4 3 10201 3.1.2.1beta1-4 + * 1.2.1rc1-2 3 10201 3.1.2.1rc1-2 + * 1.2.1 3 10201 3.1.2.1 + * 1.2.2beta1-6 12 10202 12.so.0.1.2.2beta1-6 + * 1.0.13beta1 10 10013 10.so.0.1.0.13beta1 + * 1.0.13rc1 10 10013 10.so.0.1.0.13rc1 + * 1.2.2rc1 12 10202 12.so.0.1.2.2rc1 + * 1.0.13 10 10013 10.so.0.1.0.13 + * 1.2.2 12 10202 12.so.0.1.2.2 + * 1.2.3rc1-6 12 10203 12.so.0.1.2.3rc1-6 + * 1.2.3 12 10203 12.so.0.1.2.3 + * 1.2.4beta1-3 13 10204 12.so.0.1.2.4beta1-3 + * 1.0.14rc1 13 10014 10.so.0.1.0.14rc1 + * 1.2.4rc1 13 10204 12.so.0.1.2.4rc1 + * 1.0.14 10 10014 10.so.0.1.0.14 + * 1.2.4 13 10204 12.so.0.1.2.4 + * 1.2.5beta1-2 13 10205 12.so.0.1.2.5beta1-2 + * 1.0.15rc1-3 10 10015 10.so.0.1.0.15rc1-3 + * 1.2.5rc1-3 13 10205 12.so.0.1.2.5rc1-3 + * 1.0.15 10 10015 10.so.0.1.0.15 + * 1.2.5 13 10205 12.so.0.1.2.5 + * 1.2.6beta1-4 13 10206 12.so.0.1.2.6beta1-4 + * 1.0.16 10 10016 10.so.0.1.0.16 + * 1.2.6 13 10206 12.so.0.1.2.6 + * 1.2.7beta1-2 13 10207 12.so.0.1.2.7beta1-2 + * 1.0.17rc1 10 10017 10.so.0.1.0.17rc1 + * 1.2.7rc1 13 10207 12.so.0.1.2.7rc1 + * 1.0.17 10 10017 10.so.0.1.0.17 + * 1.2.7 13 10207 12.so.0.1.2.7 + * 1.2.8beta1-5 13 10208 12.so.0.1.2.8beta1-5 + * 1.0.18rc1-5 10 10018 10.so.0.1.0.18rc1-5 + * 1.2.8rc1-5 13 10208 12.so.0.1.2.8rc1-5 + * 1.0.18 10 10018 10.so.0.1.0.18 + * 1.2.8 13 10208 12.so.0.1.2.8 + * 1.2.9beta1-3 13 10209 12.so.0.1.2.9beta1-3 + * 1.2.9beta4-11 13 10209 12.so.0.9[.0] + * 1.2.9rc1 13 10209 12.so.0.9[.0] + * 1.2.9 13 10209 12.so.0.9[.0] + * 1.2.10beta1-8 13 10210 12.so.0.10[.0] + * 1.2.10rc1-3 13 10210 12.so.0.10[.0] + * 1.2.10 13 10210 12.so.0.10[.0] + * 1.2.11beta1-4 13 10211 12.so.0.11[.0] + * 1.0.19rc1-5 10 10019 10.so.0.19[.0] + * 1.2.11rc1-5 13 10211 12.so.0.11[.0] + * 1.0.19 10 10019 10.so.0.19[.0] + * 1.2.11 13 10211 12.so.0.11[.0] + * 1.0.20 10 10020 10.so.0.20[.0] + * 1.2.12 13 10212 12.so.0.12[.0] + * 1.2.13beta1 13 10213 12.so.0.13[.0] + * 1.0.21 10 10021 10.so.0.21[.0] + * 1.2.13 13 10213 12.so.0.13[.0] + * 1.2.14beta1-2 13 10214 12.so.0.14[.0] + * 1.0.22rc1 10 10022 10.so.0.22[.0] + * 1.2.14rc1 13 10214 12.so.0.14[.0] + * 1.0.22 10 10022 10.so.0.22[.0] + * 1.2.14 13 10214 12.so.0.14[.0] + * 1.2.15beta1-6 13 10215 12.so.0.15[.0] + * 1.0.23rc1-5 10 10023 10.so.0.23[.0] + * 1.2.15rc1-5 13 10215 12.so.0.15[.0] + * 1.0.23 10 10023 10.so.0.23[.0] + * 1.2.15 13 10215 12.so.0.15[.0] + * 1.2.16beta1-2 13 10216 12.so.0.16[.0] + * 1.2.16rc1 13 10216 12.so.0.16[.0] + * 1.0.24 10 10024 10.so.0.24[.0] + * 1.2.16 13 10216 12.so.0.16[.0] + * 1.2.17beta1-2 13 10217 12.so.0.17[.0] + * 1.0.25rc1 10 10025 10.so.0.25[.0] + * 1.2.17rc1-3 13 10217 12.so.0.17[.0] + * 1.0.25 10 10025 10.so.0.25[.0] + * 1.2.17 13 10217 12.so.0.17[.0] + * 1.0.26 10 10026 10.so.0.26[.0] + * 1.2.18 13 10218 12.so.0.18[.0] + * 1.2.19beta1-31 13 10219 12.so.0.19[.0] + * 1.0.27rc1-6 10 10027 10.so.0.27[.0] + * 1.2.19rc1-6 13 10219 12.so.0.19[.0] + * 1.0.27 10 10027 10.so.0.27[.0] + * 1.2.19 13 10219 12.so.0.19[.0] + * 1.2.20beta01-04 13 10220 12.so.0.20[.0] + * 1.0.28rc1-6 10 10028 10.so.0.28[.0] + * 1.2.20rc1-6 13 10220 12.so.0.20[.0] + * 1.0.28 10 10028 10.so.0.28[.0] + * 1.2.20 13 10220 12.so.0.20[.0] + * 1.2.21beta1-2 13 10221 12.so.0.21[.0] + * 1.2.21rc1-3 13 10221 12.so.0.21[.0] + * 1.0.29 10 10029 10.so.0.29[.0] + * 1.2.21 13 10221 12.so.0.21[.0] + * 1.2.22beta1-4 13 10222 12.so.0.22[.0] + * 1.0.30rc1 10 10030 10.so.0.30[.0] + * 1.2.22rc1 13 10222 12.so.0.22[.0] + * 1.0.30 10 10030 10.so.0.30[.0] + * 1.2.22 13 10222 12.so.0.22[.0] + * 1.2.23beta01-05 13 10223 12.so.0.23[.0] + * 1.2.23rc01 13 10223 12.so.0.23[.0] + * 1.2.23 13 10223 12.so.0.23[.0] + * 1.2.24beta01-02 13 10224 12.so.0.24[.0] + * 1.2.24rc01 13 10224 12.so.0.24[.0] + * 1.2.24 13 10224 12.so.0.24[.0] + * + * Henceforth the source version will match the shared-library major + * and minor numbers; the shared-library major version number will be + * used for changes in backward compatibility, as it is intended. The + * PNG_LIBPNG_VER macro, which is not used within libpng but is available + * for applications, is an unsigned integer of the form xyyzz corresponding + * to the source version x.y.z (leading zeros in y and z). Beta versions + * were given the previous public release number plus a letter, until + * version 1.0.6j; from then on they were given the upcoming public + * release number plus "betaNN" or "rcN". + * + * Binary incompatibility exists only when applications make direct access + * to the info_ptr or png_ptr members through png.h, and the compiled + * application is loaded with a different version of the library. + * + * DLLNUM will change each time there are forward or backward changes + * in binary compatibility (e.g., when a new feature is added). + * + * See libpng.txt or libpng.3 for more information. The PNG specification + * is available as a W3C Recommendation and as an ISO Specification, + * defines should NOT be changed. + */ +#define PNG_INFO_gAMA 0x0001 +#define PNG_INFO_sBIT 0x0002 +#define PNG_INFO_cHRM 0x0004 +#define PNG_INFO_PLTE 0x0008 +#define PNG_INFO_tRNS 0x0010 +#define PNG_INFO_bKGD 0x0020 +#define PNG_INFO_hIST 0x0040 +#define PNG_INFO_pHYs 0x0080 +#define PNG_INFO_oFFs 0x0100 +#define PNG_INFO_tIME 0x0200 +#define PNG_INFO_pCAL 0x0400 +#define PNG_INFO_sRGB 0x0800 /* GR-P, 0.96a */ +#define PNG_INFO_iCCP 0x1000 /* ESR, 1.0.6 */ +#define PNG_INFO_sPLT 0x2000 /* ESR, 1.0.6 */ +#define PNG_INFO_sCAL 0x4000 /* ESR, 1.0.6 */ +#define PNG_INFO_IDAT 0x8000L /* ESR, 1.0.6 */ + +/* This is used for the transformation routines, as some of them + * change these values for the row. It also should enable using + * the routines for other purposes. + */ +typedef struct png_row_info_struct +{ + png_uint_32 width; /* width of row */ + png_uint_32 rowbytes; /* number of bytes in row */ + png_byte color_type; /* color type of row */ + png_byte bit_depth; /* bit depth of row */ + png_byte channels; /* number of channels (1, 2, 3, or 4) */ + png_byte pixel_depth; /* bits per pixel (depth * channels) */ +} png_row_info; + +typedef png_row_info FAR * png_row_infop; +typedef png_row_info FAR * FAR * png_row_infopp; + +/* These are the function types for the I/O functions and for the functions + * that allow the user to override the default I/O functions with his or her + * own. The png_error_ptr type should match that of user-supplied warning + * and error functions, while the png_rw_ptr type should match that of the + * user read/write data functions. + */ +typedef struct png_struct_def png_struct; +typedef png_struct FAR * png_structp; + +typedef void (PNGAPI *png_error_ptr) PNGARG((png_structp, png_const_charp)); +typedef void (PNGAPI *png_rw_ptr) PNGARG((png_structp, png_bytep, png_size_t)); +typedef void (PNGAPI *png_flush_ptr) PNGARG((png_structp)); +typedef void (PNGAPI *png_read_status_ptr) PNGARG((png_structp, png_uint_32, + int)); +typedef void (PNGAPI *png_write_status_ptr) PNGARG((png_structp, png_uint_32, + int)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +typedef void (PNGAPI *png_progressive_info_ptr) PNGARG((png_structp, png_infop)); +typedef void (PNGAPI *png_progressive_end_ptr) PNGARG((png_structp, png_infop)); +typedef void (PNGAPI *png_progressive_row_ptr) PNGARG((png_structp, png_bytep, + png_uint_32, int)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +typedef void (PNGAPI *png_user_transform_ptr) PNGARG((png_structp, + png_row_infop, png_bytep)); +#endif + +#if defined(PNG_USER_CHUNKS_SUPPORTED) +typedef int (PNGAPI *png_user_chunk_ptr) PNGARG((png_structp, png_unknown_chunkp)); +#endif +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +typedef void (PNGAPI *png_unknown_chunk_ptr) PNGARG((png_structp)); +#endif + +/* Transform masks for the high-level interface */ +#define PNG_TRANSFORM_IDENTITY 0x0000 /* read and write */ +#define PNG_TRANSFORM_STRIP_16 0x0001 /* read only */ +#define PNG_TRANSFORM_STRIP_ALPHA 0x0002 /* read only */ +#define PNG_TRANSFORM_PACKING 0x0004 /* read and write */ +#define PNG_TRANSFORM_PACKSWAP 0x0008 /* read and write */ +#define PNG_TRANSFORM_EXPAND 0x0010 /* read only */ +#define PNG_TRANSFORM_INVERT_MONO 0x0020 /* read and write */ +#define PNG_TRANSFORM_SHIFT 0x0040 /* read and write */ +#define PNG_TRANSFORM_BGR 0x0080 /* read and write */ +#define PNG_TRANSFORM_SWAP_ALPHA 0x0100 /* read and write */ +#define PNG_TRANSFORM_SWAP_ENDIAN 0x0200 /* read and write */ +#define PNG_TRANSFORM_INVERT_ALPHA 0x0400 /* read and write */ +#define PNG_TRANSFORM_STRIP_FILLER 0x0800 /* WRITE only */ + +/* Flags for MNG supported features */ +#define PNG_FLAG_MNG_EMPTY_PLTE 0x01 +#define PNG_FLAG_MNG_FILTER_64 0x04 +#define PNG_ALL_MNG_FEATURES 0x05 + +typedef png_voidp (*png_malloc_ptr) PNGARG((png_structp, png_size_t)); +typedef void (*png_free_ptr) PNGARG((png_structp, png_voidp)); + +/* The structure that holds the information to read and write PNG files. + * The only people who need to care about what is inside of this are the + * people who will be modifying the library for their own special needs. + * It should NOT be accessed directly by an application, except to store + * the jmp_buf. + */ + +struct png_struct_def +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf jmpbuf; /* used in png_error */ +#endif + png_error_ptr error_fn; /* function for printing errors and aborting */ + png_error_ptr warning_fn; /* function for printing warnings */ + png_voidp error_ptr; /* user supplied struct for error functions */ + png_rw_ptr write_data_fn; /* function for writing output data */ + png_rw_ptr read_data_fn; /* function for reading input data */ + png_voidp io_ptr; /* ptr to application struct for I/O functions */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + png_user_transform_ptr read_user_transform_fn; /* user read transform */ +#endif + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + png_user_transform_ptr write_user_transform_fn; /* user write transform */ +#endif + +/* These were added in libpng-1.0.2 */ +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + png_voidp user_transform_ptr; /* user supplied struct for user transform */ + png_byte user_transform_depth; /* bit depth of user transformed pixels */ + png_byte user_transform_channels; /* channels in user transformed pixels */ +#endif +#endif + + png_uint_32 mode; /* tells us where we are in the PNG file */ + png_uint_32 flags; /* flags indicating various things to libpng */ + png_uint_32 transformations; /* which transformations to perform */ + + z_stream zstream; /* pointer to decompression structure (below) */ + png_bytep zbuf; /* buffer for zlib */ + png_size_t zbuf_size; /* size of zbuf */ + int zlib_level; /* holds zlib compression level */ + int zlib_method; /* holds zlib compression method */ + int zlib_window_bits; /* holds zlib compression window bits */ + int zlib_mem_level; /* holds zlib compression memory level */ + int zlib_strategy; /* holds zlib compression strategy */ + + png_uint_32 width; /* width of image in pixels */ + png_uint_32 height; /* height of image in pixels */ + png_uint_32 num_rows; /* number of rows in current pass */ + png_uint_32 usr_width; /* width of row at start of write */ + png_uint_32 rowbytes; /* size of row in bytes */ + png_uint_32 irowbytes; /* size of current interlaced row in bytes */ + png_uint_32 iwidth; /* width of current interlaced row in pixels */ + png_uint_32 row_number; /* current row in interlace pass */ + png_bytep prev_row; /* buffer to save previous (unfiltered) row */ + png_bytep row_buf; /* buffer to save current (unfiltered) row */ +#ifndef PNG_NO_WRITE_FILTERING + png_bytep sub_row; /* buffer to save "sub" row when filtering */ + png_bytep up_row; /* buffer to save "up" row when filtering */ + png_bytep avg_row; /* buffer to save "avg" row when filtering */ + png_bytep paeth_row; /* buffer to save "Paeth" row when filtering */ +#endif + png_row_info row_info; /* used for transformation routines */ + + png_uint_32 idat_size; /* current IDAT size for read */ + png_uint_32 crc; /* current chunk CRC value */ + png_colorp palette; /* palette from the input file */ + png_uint_16 num_palette; /* number of color entries in palette */ + png_uint_16 num_trans; /* number of transparency values */ + png_byte chunk_name[5]; /* null-terminated name of current chunk */ + png_byte compression; /* file compression type (always 0) */ + png_byte filter; /* file filter type (always 0) */ + png_byte interlaced; /* PNG_INTERLACE_NONE, PNG_INTERLACE_ADAM7 */ + png_byte pass; /* current interlace pass (0 - 6) */ + png_byte do_filter; /* row filter flags (see PNG_FILTER_ below ) */ + png_byte color_type; /* color type of file */ + png_byte bit_depth; /* bit depth of file */ + png_byte usr_bit_depth; /* bit depth of users row */ + png_byte pixel_depth; /* number of bits per pixel */ + png_byte channels; /* number of channels in file */ + png_byte usr_channels; /* channels at start of write */ + png_byte sig_bytes; /* magic bytes read/written from start of file */ + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +#ifdef PNG_LEGACY_SUPPORTED + png_byte filler; /* filler byte for pixel expansion */ +#else + png_uint_16 filler; /* filler bytes for pixel expansion */ +#endif +#endif + +#if defined(PNG_bKGD_SUPPORTED) + png_byte background_gamma_type; +# ifdef PNG_FLOATING_POINT_SUPPORTED + float background_gamma; +# endif + png_color_16 background; /* background color in screen gamma space */ +#if defined(PNG_READ_GAMMA_SUPPORTED) + png_color_16 background_1; /* background normalized to gamma 1.0 */ +#endif +#endif /* PNG_bKGD_SUPPORTED */ + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) + png_flush_ptr output_flush_fn;/* Function for flushing output */ + png_uint_32 flush_dist; /* how many rows apart to flush, 0 - no flush */ + png_uint_32 flush_rows; /* number of rows written since last flush */ +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + int gamma_shift; /* number of "insignificant" bits 16-bit gamma */ +#ifdef PNG_FLOATING_POINT_SUPPORTED + float gamma; /* file gamma value */ + float screen_gamma; /* screen gamma value (display_exponent) */ +#endif +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_bytep gamma_table; /* gamma table for 8-bit depth files */ + png_bytep gamma_from_1; /* converts from 1.0 to screen */ + png_bytep gamma_to_1; /* converts from file to 1.0 */ + png_uint_16pp gamma_16_table; /* gamma table for 16-bit depth files */ + png_uint_16pp gamma_16_from_1; /* converts from 1.0 to screen */ + png_uint_16pp gamma_16_to_1; /* converts from file to 1.0 */ +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_sBIT_SUPPORTED) + png_color_8 sig_bit; /* significant bits in each available channel */ +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) + png_color_8 shift; /* shift for significant bit tranformation */ +#endif + +#if defined(PNG_tRNS_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) \ + || defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_bytep trans; /* transparency values for paletted files */ + png_color_16 trans_values; /* transparency values for non-paletted files */ +#endif + + png_read_status_ptr read_row_fn; /* called after each row is decoded */ + png_write_status_ptr write_row_fn; /* called after each row is encoded */ +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + png_progressive_info_ptr info_fn; /* called after header data fully read */ + png_progressive_row_ptr row_fn; /* called after each prog. row is decoded */ + png_progressive_end_ptr end_fn; /* called after image is complete */ + png_bytep save_buffer_ptr; /* current location in save_buffer */ + png_bytep save_buffer; /* buffer for previously read data */ + png_bytep current_buffer_ptr; /* current location in current_buffer */ + png_bytep current_buffer; /* buffer for recently used data */ + png_uint_32 push_length; /* size of current input chunk */ + png_uint_32 skip_length; /* bytes to skip in input data */ + png_size_t save_buffer_size; /* amount of data now in save_buffer */ + png_size_t save_buffer_max; /* total size of save_buffer */ + png_size_t buffer_size; /* total amount of available input data */ + png_size_t current_buffer_size; /* amount of data now in current_buffer */ + int process_mode; /* what push library is currently doing */ + int cur_palette; /* current push library palette index */ + +# if defined(PNG_TEXT_SUPPORTED) + png_size_t current_text_size; /* current size of text input data */ + png_size_t current_text_left; /* how much text left to read in input */ + png_charp current_text; /* current text chunk buffer */ + png_charp current_text_ptr; /* current location in current_text */ +# endif /* PNG_TEXT_SUPPORTED */ +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__) +/* for the Borland special 64K segment handler */ + png_bytepp offset_table_ptr; + png_bytep offset_table; + png_uint_16 offset_table_number; + png_uint_16 offset_table_count; + png_uint_16 offset_table_count_free; +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) + png_bytep palette_lookup; /* lookup table for dithering */ + png_bytep dither_index; /* index translation for palette files */ +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) || defined(PNG_hIST_SUPPORTED) + png_uint_16p hist; /* histogram */ +#endif + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + png_byte heuristic_method; /* heuristic for row filter selection */ + png_byte num_prev_filters; /* number of weights for previous rows */ + png_bytep prev_filters; /* filter type(s) of previous row(s) */ + png_uint_16p filter_weights; /* weight(s) for previous line(s) */ + png_uint_16p inv_filter_weights; /* 1/weight(s) for previous line(s) */ + png_uint_16p filter_costs; /* relative filter calculation cost */ + png_uint_16p inv_filter_costs; /* 1/relative filter calculation cost */ +#endif + +#if defined(PNG_TIME_RFC1123_SUPPORTED) + png_charp time_buffer; /* String to hold RFC 1123 time text */ +#endif + +/* New members added in libpng-1.0.6 */ + +#ifdef PNG_FREE_ME_SUPPORTED + png_uint_32 free_me; /* flags items libpng is responsible for freeing */ +#endif + +#if defined(PNG_USER_CHUNKS_SUPPORTED) + png_voidp user_chunk_ptr; + png_user_chunk_ptr read_user_chunk_fn; /* user read chunk handler */ +#endif + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + int num_chunk_list; + png_bytep chunk_list; +#endif + +/* New members added in libpng-1.0.3 */ +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + png_byte rgb_to_gray_status; + /* These were changed from png_byte in libpng-1.0.6 */ + png_uint_16 rgb_to_gray_red_coeff; + png_uint_16 rgb_to_gray_green_coeff; + png_uint_16 rgb_to_gray_blue_coeff; +#endif + +/* New member added in libpng-1.0.4 (renamed in 1.0.9) */ +#if defined(PNG_MNG_FEATURES_SUPPORTED) || \ + defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ + defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) +/* changed from png_byte to png_uint_32 at version 1.2.0 */ +#ifdef PNG_1_0_X + png_byte mng_features_permitted; +#else + png_uint_32 mng_features_permitted; +#endif /* PNG_1_0_X */ +#endif + +/* New member added in libpng-1.0.7 */ +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + png_fixed_point int_gamma; +#endif + +/* New member added in libpng-1.0.9, ifdef'ed out in 1.0.12, enabled in 1.2.0 */ +#if defined(PNG_MNG_FEATURES_SUPPORTED) + png_byte filter_type; +#endif + +#if defined(PNG_1_0_X) +/* New member added in libpng-1.0.10, ifdef'ed out in 1.2.0 */ + png_uint_32 row_buf_size; +#endif + +/* New members added in libpng-1.2.0 */ +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) +# if !defined(PNG_1_0_X) +# if defined(PNG_MMX_CODE_SUPPORTED) + png_byte mmx_bitdepth_threshold; + png_uint_32 mmx_rowbytes_threshold; +# endif + png_uint_32 asm_flags; +# endif +#endif + +/* New members added in libpng-1.0.2 but first enabled by default in 1.2.0 */ +#ifdef PNG_USER_MEM_SUPPORTED + png_voidp mem_ptr; /* user supplied struct for mem functions */ + png_malloc_ptr malloc_fn; /* function for allocating memory */ + png_free_ptr free_fn; /* function for freeing memory */ +#endif + +/* New member added in libpng-1.0.13 and 1.2.0 */ + png_bytep big_row_buf; /* buffer to save current (unfiltered) row */ + +#if defined(PNG_READ_DITHER_SUPPORTED) +/* The following three members were added at version 1.0.14 and 1.2.4 */ + png_bytep dither_sort; /* working sort array */ + png_bytep index_to_palette; /* where the original index currently is */ + /* in the palette */ + png_bytep palette_to_index; /* which original index points to this */ + /* palette color */ +#endif + +/* New members added in libpng-1.0.16 and 1.2.6 */ + png_byte compression_type; + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_uint_32 user_width_max; + png_uint_32 user_height_max; +#endif + +/* New member added in libpng-1.0.25 and 1.2.17 */ +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + /* storage for unknown chunk that the library doesn't recognize. */ + png_unknown_chunk unknown_chunk; +#endif +}; + + +/* This triggers a compiler error in png.c, if png.c and png.h + * do not agree upon the version number. + */ +typedef png_structp version_1_2_24; + +typedef png_struct FAR * FAR * png_structpp; + +/* Here are the function definitions most commonly used. This is not + * the place to find out how to use libpng. See libpng.txt for the + * full explanation, see example.c for the summary. This just provides + * a simple one line description of the use of each function. + */ + +/* Returns the version number of the library */ +extern PNG_EXPORT(png_uint_32,png_access_version_number) PNGARG((void)); + +/* Tell lib we have already handled the first magic bytes. + * Handling more than 8 bytes from the beginning of the file is an error. + */ +extern PNG_EXPORT(void,png_set_sig_bytes) PNGARG((png_structp png_ptr, + int num_bytes)); + +/* Check sig[start] through sig[start + num_to_check - 1] to see if it's a + * PNG file. Returns zero if the supplied bytes match the 8-byte PNG + * signature, and non-zero otherwise. Having num_to_check == 0 or + * start > 7 will always fail (ie return non-zero). + */ +extern PNG_EXPORT(int,png_sig_cmp) PNGARG((png_bytep sig, png_size_t start, + png_size_t num_to_check)); + +/* Simple signature checking function. This is the same as calling + * png_check_sig(sig, n) := !png_sig_cmp(sig, 0, n). + */ +extern PNG_EXPORT(int,png_check_sig) PNGARG((png_bytep sig, int num)); + +/* Allocate and initialize png_ptr struct for reading, and any other memory. */ +extern PNG_EXPORT(png_structp,png_create_read_struct) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn)); + +/* Allocate and initialize png_ptr struct for writing, and any other memory */ +extern PNG_EXPORT(png_structp,png_create_write_struct) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn)); + +#ifdef PNG_WRITE_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_compression_buffer_size) + PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_WRITE_SUPPORTED +extern PNG_EXPORT(void,png_set_compression_buffer_size) + PNGARG((png_structp png_ptr, png_uint_32 size)); +#endif + +/* Reset the compression stream */ +extern PNG_EXPORT(int,png_reset_zstream) PNGARG((png_structp png_ptr)); + +/* New functions added in libpng-1.0.2 (not enabled by default until 1.2.0) */ +#ifdef PNG_USER_MEM_SUPPORTED +extern PNG_EXPORT(png_structp,png_create_read_struct_2) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +extern PNG_EXPORT(png_structp,png_create_write_struct_2) + PNGARG((png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +#endif + +/* Write a PNG chunk - size, type, (optional) data, CRC. */ +extern PNG_EXPORT(void,png_write_chunk) PNGARG((png_structp png_ptr, + png_bytep chunk_name, png_bytep data, png_size_t length)); + +/* Write the start of a PNG chunk - length and chunk name. */ +extern PNG_EXPORT(void,png_write_chunk_start) PNGARG((png_structp png_ptr, + png_bytep chunk_name, png_uint_32 length)); + +/* Write the data of a PNG chunk started with png_write_chunk_start(). */ +extern PNG_EXPORT(void,png_write_chunk_data) PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)); + +/* Finish a chunk started with png_write_chunk_start() (includes CRC). */ +extern PNG_EXPORT(void,png_write_chunk_end) PNGARG((png_structp png_ptr)); + +/* Allocate and initialize the info structure */ +extern PNG_EXPORT(png_infop,png_create_info_struct) + PNGARG((png_structp png_ptr)); + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Initialize the info structure (old interface - DEPRECATED) */ +extern PNG_EXPORT(void,png_info_init) PNGARG((png_infop info_ptr)); +#undef png_info_init +#define png_info_init(info_ptr) png_info_init_3(&info_ptr,\ + png_sizeof(png_info)); +#endif + +extern PNG_EXPORT(void,png_info_init_3) PNGARG((png_infopp info_ptr, + png_size_t png_info_struct_size)); + +/* Writes all the PNG information before the image. */ +extern PNG_EXPORT(void,png_write_info_before_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +extern PNG_EXPORT(void,png_write_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read the information before the actual image data. */ +extern PNG_EXPORT(void,png_read_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +#if defined(PNG_TIME_RFC1123_SUPPORTED) +extern PNG_EXPORT(png_charp,png_convert_to_rfc1123) + PNGARG((png_structp png_ptr, png_timep ptime)); +#endif + +#if !defined(_WIN32_WCE) +/* "time.h" functions are not supported on WindowsCE */ +#if defined(PNG_WRITE_tIME_SUPPORTED) +/* convert from a struct tm to png_time */ +extern PNG_EXPORT(void,png_convert_from_struct_tm) PNGARG((png_timep ptime, + struct tm FAR * ttime)); + +/* convert from time_t to png_time. Uses gmtime() */ +extern PNG_EXPORT(void,png_convert_from_time_t) PNGARG((png_timep ptime, + time_t ttime)); +#endif /* PNG_WRITE_tIME_SUPPORTED */ +#endif /* _WIN32_WCE */ + +#if defined(PNG_READ_EXPAND_SUPPORTED) +/* Expand data to 24-bit RGB, or 8-bit grayscale, with alpha if available. */ +extern PNG_EXPORT(void,png_set_expand) PNGARG((png_structp png_ptr)); +#if !defined(PNG_1_0_X) +extern PNG_EXPORT(void,png_set_expand_gray_1_2_4_to_8) PNGARG((png_structp + png_ptr)); +#endif +extern PNG_EXPORT(void,png_set_palette_to_rgb) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(void,png_set_tRNS_to_alpha) PNGARG((png_structp png_ptr)); +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Deprecated */ +extern PNG_EXPORT(void,png_set_gray_1_2_4_to_8) PNGARG((png_structp png_ptr)); +#endif +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* Use blue, green, red order for pixels. */ +extern PNG_EXPORT(void,png_set_bgr) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +/* Expand the grayscale to 24-bit RGB if necessary. */ +extern PNG_EXPORT(void,png_set_gray_to_rgb) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +/* Reduce RGB to grayscale. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_rgb_to_gray) PNGARG((png_structp png_ptr, + int error_action, double red, double green )); +#endif +extern PNG_EXPORT(void,png_set_rgb_to_gray_fixed) PNGARG((png_structp png_ptr, + int error_action, png_fixed_point red, png_fixed_point green )); +extern PNG_EXPORT(png_byte,png_get_rgb_to_gray_status) PNGARG((png_structp + png_ptr)); +#endif + +extern PNG_EXPORT(void,png_build_grayscale_palette) PNGARG((int bit_depth, + png_colorp palette)); + +#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +extern PNG_EXPORT(void,png_set_strip_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +extern PNG_EXPORT(void,png_set_swap_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +extern PNG_EXPORT(void,png_set_invert_alpha) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte to 8-bit Gray or 24-bit RGB images. */ +extern PNG_EXPORT(void,png_set_filler) PNGARG((png_structp png_ptr, + png_uint_32 filler, int flags)); +/* The values of the PNG_FILLER_ defines should NOT be changed */ +#define PNG_FILLER_BEFORE 0 +#define PNG_FILLER_AFTER 1 +/* Add an alpha byte to 8-bit Gray or 24-bit RGB images. */ +#if !defined(PNG_1_0_X) +extern PNG_EXPORT(void,png_set_add_alpha) PNGARG((png_structp png_ptr, + png_uint_32 filler, int flags)); +#endif +#endif /* PNG_READ_FILLER_SUPPORTED || PNG_WRITE_FILLER_SUPPORTED */ + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* Swap bytes in 16-bit depth files. */ +extern PNG_EXPORT(void,png_set_swap) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* Use 1 byte per pixel in 1, 2, or 4-bit depth files. */ +extern PNG_EXPORT(void,png_set_packing) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* Swap packing order of pixels in bytes. */ +extern PNG_EXPORT(void,png_set_packswap) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Converts files to legal bit depths. */ +extern PNG_EXPORT(void,png_set_shift) PNGARG((png_structp png_ptr, + png_color_8p true_bits)); +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Have the code handle the interlacing. Returns the number of passes. */ +extern PNG_EXPORT(int,png_set_interlace_handling) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +/* Invert monochrome files */ +extern PNG_EXPORT(void,png_set_invert_mono) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) +/* Handle alpha and tRNS by replacing with a background color. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_background) PNGARG((png_structp png_ptr, + png_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma)); +#endif +#define PNG_BACKGROUND_GAMMA_UNKNOWN 0 +#define PNG_BACKGROUND_GAMMA_SCREEN 1 +#define PNG_BACKGROUND_GAMMA_FILE 2 +#define PNG_BACKGROUND_GAMMA_UNIQUE 3 +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) +/* strip the second byte of information from a 16-bit depth file. */ +extern PNG_EXPORT(void,png_set_strip_16) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) +/* Turn on dithering, and reduce the palette to the number of colors available. */ +extern PNG_EXPORT(void,png_set_dither) PNGARG((png_structp png_ptr, + png_colorp palette, int num_palette, int maximum_colors, + png_uint_16p histogram, int full_dither)); +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) +/* Handle gamma correction. Screen_gamma=(display_exponent) */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_gamma) PNGARG((png_structp png_ptr, + double screen_gamma, double default_file_gamma)); +#endif +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ + defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) +/* Permit or disallow empty PLTE (0: not permitted, 1: permitted) */ +/* Deprecated and will be removed. Use png_permit_mng_features() instead. */ +extern PNG_EXPORT(void,png_permit_empty_plte) PNGARG((png_structp png_ptr, + int empty_plte_permitted)); +#endif +#endif + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +/* Set how many lines between output flushes - 0 for no flushing */ +extern PNG_EXPORT(void,png_set_flush) PNGARG((png_structp png_ptr, int nrows)); +/* Flush the current PNG output buffer */ +extern PNG_EXPORT(void,png_write_flush) PNGARG((png_structp png_ptr)); +#endif + +/* optional update palette with requested transformations */ +extern PNG_EXPORT(void,png_start_read_image) PNGARG((png_structp png_ptr)); + +/* optional call to update the users info structure */ +extern PNG_EXPORT(void,png_read_update_info) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read one or more rows of image data. */ +extern PNG_EXPORT(void,png_read_rows) PNGARG((png_structp png_ptr, + png_bytepp row, png_bytepp display_row, png_uint_32 num_rows)); +#endif + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read a row of data. */ +extern PNG_EXPORT(void,png_read_row) PNGARG((png_structp png_ptr, + png_bytep row, + png_bytep display_row)); +#endif + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read the whole image into memory at once. */ +extern PNG_EXPORT(void,png_read_image) PNGARG((png_structp png_ptr, + png_bytepp image)); +#endif + +/* write a row of image data */ +extern PNG_EXPORT(void,png_write_row) PNGARG((png_structp png_ptr, + png_bytep row)); + +/* write a few rows of image data */ +extern PNG_EXPORT(void,png_write_rows) PNGARG((png_structp png_ptr, + png_bytepp row, png_uint_32 num_rows)); + +/* write the image data */ +extern PNG_EXPORT(void,png_write_image) PNGARG((png_structp png_ptr, + png_bytepp image)); + +/* writes the end of the PNG file. */ +extern PNG_EXPORT(void,png_write_end) PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* read the end of the PNG file. */ +extern PNG_EXPORT(void,png_read_end) PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +/* free any memory associated with the png_info_struct */ +extern PNG_EXPORT(void,png_destroy_info_struct) PNGARG((png_structp png_ptr, + png_infopp info_ptr_ptr)); + +/* free any memory associated with the png_struct and the png_info_structs */ +extern PNG_EXPORT(void,png_destroy_read_struct) PNGARG((png_structpp + png_ptr_ptr, png_infopp info_ptr_ptr, png_infopp end_info_ptr_ptr)); + +/* free all memory used by the read (old method - NOT DLL EXPORTED) */ +extern void png_read_destroy PNGARG((png_structp png_ptr, png_infop info_ptr, + png_infop end_info_ptr)); + +/* free any memory associated with the png_struct and the png_info_structs */ +extern PNG_EXPORT(void,png_destroy_write_struct) + PNGARG((png_structpp png_ptr_ptr, png_infopp info_ptr_ptr)); + +/* free any memory used in png_ptr struct (old method - NOT DLL EXPORTED) */ +extern void png_write_destroy PNGARG((png_structp png_ptr)); + +/* set the libpng method of handling chunk CRC errors */ +extern PNG_EXPORT(void,png_set_crc_action) PNGARG((png_structp png_ptr, + int crit_action, int ancil_action)); + +/* Values for png_set_crc_action() to say how to handle CRC errors in + * ancillary and critical chunks, and whether to use the data contained + * therein. Note that it is impossible to "discard" data in a critical + * chunk. For versions prior to 0.90, the action was always error/quit, + * whereas in version 0.90 and later, the action for CRC errors in ancillary + * chunks is warn/discard. These values should NOT be changed. + * + * value action:critical action:ancillary + */ +#define PNG_CRC_DEFAULT 0 /* error/quit warn/discard data */ +#define PNG_CRC_ERROR_QUIT 1 /* error/quit error/quit */ +#define PNG_CRC_WARN_DISCARD 2 /* (INVALID) warn/discard data */ +#define PNG_CRC_WARN_USE 3 /* warn/use data warn/use data */ +#define PNG_CRC_QUIET_USE 4 /* quiet/use data quiet/use data */ +#define PNG_CRC_NO_CHANGE 5 /* use current value use current value */ + +/* These functions give the user control over the scan-line filtering in + * libpng and the compression methods used by zlib. These functions are + * mainly useful for testing, as the defaults should work with most users. + * Those users who are tight on memory or want faster performance at the + * expense of compression can modify them. See the compression library + * header file (zlib.h) for an explination of the compression functions. + */ + +/* set the filtering method(s) used by libpng. Currently, the only valid + * value for "method" is 0. + */ +extern PNG_EXPORT(void,png_set_filter) PNGARG((png_structp png_ptr, int method, + int filters)); + +/* Flags for png_set_filter() to say which filters to use. The flags + * are chosen so that they don't conflict with real filter types + * below, in case they are supplied instead of the #defined constants. + * These values should NOT be changed. + */ +#define PNG_NO_FILTERS 0x00 +#define PNG_FILTER_NONE 0x08 +#define PNG_FILTER_SUB 0x10 +#define PNG_FILTER_UP 0x20 +#define PNG_FILTER_AVG 0x40 +#define PNG_FILTER_PAETH 0x80 +#define PNG_ALL_FILTERS (PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_UP | \ + PNG_FILTER_AVG | PNG_FILTER_PAETH) + +/* Filter values (not flags) - used in pngwrite.c, pngwutil.c for now. + * These defines should NOT be changed. + */ +#define PNG_FILTER_VALUE_NONE 0 +#define PNG_FILTER_VALUE_SUB 1 +#define PNG_FILTER_VALUE_UP 2 +#define PNG_FILTER_VALUE_AVG 3 +#define PNG_FILTER_VALUE_PAETH 4 +#define PNG_FILTER_VALUE_LAST 5 + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) /* EXPERIMENTAL */ +/* The "heuristic_method" is given by one of the PNG_FILTER_HEURISTIC_ + * defines, either the default (minimum-sum-of-absolute-differences), or + * the experimental method (weighted-minimum-sum-of-absolute-differences). + * + * Weights are factors >= 1.0, indicating how important it is to keep the + * filter type consistent between rows. Larger numbers mean the current + * filter is that many times as likely to be the same as the "num_weights" + * previous filters. This is cumulative for each previous row with a weight. + * There needs to be "num_weights" values in "filter_weights", or it can be + * NULL if the weights aren't being specified. Weights have no influence on + * the selection of the first row filter. Well chosen weights can (in theory) + * improve the compression for a given image. + * + * Costs are factors >= 1.0 indicating the relative decoding costs of a + * filter type. Higher costs indicate more decoding expense, and are + * therefore less likely to be selected over a filter with lower computational + * costs. There needs to be a value in "filter_costs" for each valid filter + * type (given by PNG_FILTER_VALUE_LAST), or it can be NULL if you aren't + * setting the costs. Costs try to improve the speed of decompression without + * unduly increasing the compressed image size. + * + * A negative weight or cost indicates the default value is to be used, and + * values in the range [0.0, 1.0) indicate the value is to remain unchanged. + * The default values for both weights and costs are currently 1.0, but may + * change if good general weighting/cost heuristics can be found. If both + * the weights and costs are set to 1.0, this degenerates the WEIGHTED method + * to the UNWEIGHTED method, but with added encoding time/computation. + */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_filter_heuristics) PNGARG((png_structp png_ptr, + int heuristic_method, int num_weights, png_doublep filter_weights, + png_doublep filter_costs)); +#endif +#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ + +/* Heuristic used for row filter selection. These defines should NOT be + * changed. + */ +#define PNG_FILTER_HEURISTIC_DEFAULT 0 /* Currently "UNWEIGHTED" */ +#define PNG_FILTER_HEURISTIC_UNWEIGHTED 1 /* Used by libpng < 0.95 */ +#define PNG_FILTER_HEURISTIC_WEIGHTED 2 /* Experimental feature */ +#define PNG_FILTER_HEURISTIC_LAST 3 /* Not a valid value */ + +/* Set the library compression level. Currently, valid values range from + * 0 - 9, corresponding directly to the zlib compression levels 0 - 9 + * (0 - no compression, 9 - "maximal" compression). Note that tests have + * shown that zlib compression levels 3-6 usually perform as well as level 9 + * for PNG images, and do considerably fewer caclulations. In the future, + * these values may not correspond directly to the zlib compression levels. + */ +extern PNG_EXPORT(void,png_set_compression_level) PNGARG((png_structp png_ptr, + int level)); + +extern PNG_EXPORT(void,png_set_compression_mem_level) + PNGARG((png_structp png_ptr, int mem_level)); + +extern PNG_EXPORT(void,png_set_compression_strategy) + PNGARG((png_structp png_ptr, int strategy)); + +extern PNG_EXPORT(void,png_set_compression_window_bits) + PNGARG((png_structp png_ptr, int window_bits)); + +extern PNG_EXPORT(void,png_set_compression_method) PNGARG((png_structp png_ptr, + int method)); + +/* These next functions are called for input/output, memory, and error + * handling. They are in the file pngrio.c, pngwio.c, and pngerror.c, + * and call standard C I/O routines such as fread(), fwrite(), and + * fprintf(). These functions can be made to use other I/O routines + * at run time for those applications that need to handle I/O in a + * different manner by calling png_set_???_fn(). See libpng.txt for + * more information. + */ + +#if !defined(PNG_NO_STDIO) +/* Initialize the input/output for the PNG file to the default functions. */ +extern PNG_EXPORT(void,png_init_io) PNGARG((png_structp png_ptr, png_FILE_p fp)); +#endif + +/* Replace the (error and abort), and warning functions with user + * supplied functions. If no messages are to be printed you must still + * write and use replacement functions. The replacement error_fn should + * still do a longjmp to the last setjmp location if you are using this + * method of error handling. If error_fn or warning_fn is NULL, the + * default function will be used. + */ + +extern PNG_EXPORT(void,png_set_error_fn) PNGARG((png_structp png_ptr, + png_voidp error_ptr, png_error_ptr error_fn, png_error_ptr warning_fn)); + +/* Return the user pointer associated with the error functions */ +extern PNG_EXPORT(png_voidp,png_get_error_ptr) PNGARG((png_structp png_ptr)); + +/* Replace the default data output functions with a user supplied one(s). + * If buffered output is not used, then output_flush_fn can be set to NULL. + * If PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile time + * output_flush_fn will be ignored (and thus can be NULL). + */ +extern PNG_EXPORT(void,png_set_write_fn) PNGARG((png_structp png_ptr, + png_voidp io_ptr, png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn)); + +/* Replace the default data input function with a user supplied one. */ +extern PNG_EXPORT(void,png_set_read_fn) PNGARG((png_structp png_ptr, + png_voidp io_ptr, png_rw_ptr read_data_fn)); + +/* Return the user pointer associated with the I/O functions */ +extern PNG_EXPORT(png_voidp,png_get_io_ptr) PNGARG((png_structp png_ptr)); + +extern PNG_EXPORT(void,png_set_read_status_fn) PNGARG((png_structp png_ptr, + png_read_status_ptr read_row_fn)); + +extern PNG_EXPORT(void,png_set_write_status_fn) PNGARG((png_structp png_ptr, + png_write_status_ptr write_row_fn)); + +#ifdef PNG_USER_MEM_SUPPORTED +/* Replace the default memory allocation functions with user supplied one(s). */ +extern PNG_EXPORT(void,png_set_mem_fn) PNGARG((png_structp png_ptr, + png_voidp mem_ptr, png_malloc_ptr malloc_fn, png_free_ptr free_fn)); +/* Return the user pointer associated with the memory functions */ +extern PNG_EXPORT(png_voidp,png_get_mem_ptr) PNGARG((png_structp png_ptr)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +extern PNG_EXPORT(void,png_set_read_user_transform_fn) PNGARG((png_structp + png_ptr, png_user_transform_ptr read_user_transform_fn)); +#endif + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +extern PNG_EXPORT(void,png_set_write_user_transform_fn) PNGARG((png_structp + png_ptr, png_user_transform_ptr write_user_transform_fn)); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +extern PNG_EXPORT(void,png_set_user_transform_info) PNGARG((png_structp + png_ptr, png_voidp user_transform_ptr, int user_transform_depth, + int user_transform_channels)); +/* Return the user pointer associated with the user transform functions */ +extern PNG_EXPORT(png_voidp,png_get_user_transform_ptr) + PNGARG((png_structp png_ptr)); +#endif + +#ifdef PNG_USER_CHUNKS_SUPPORTED +extern PNG_EXPORT(void,png_set_read_user_chunk_fn) PNGARG((png_structp png_ptr, + png_voidp user_chunk_ptr, png_user_chunk_ptr read_user_chunk_fn)); +extern PNG_EXPORT(png_voidp,png_get_user_chunk_ptr) PNGARG((png_structp + png_ptr)); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +/* Sets the function callbacks for the push reader, and a pointer to a + * user-defined structure available to the callback functions. + */ +extern PNG_EXPORT(void,png_set_progressive_read_fn) PNGARG((png_structp png_ptr, + png_voidp progressive_ptr, + png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, + png_progressive_end_ptr end_fn)); + +/* returns the user pointer associated with the push read functions */ +extern PNG_EXPORT(png_voidp,png_get_progressive_ptr) + PNGARG((png_structp png_ptr)); + +/* function to be called when data becomes available */ +extern PNG_EXPORT(void,png_process_data) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep buffer, png_size_t buffer_size)); + +/* function that combines rows. Not very much different than the + * png_combine_row() call. Is this even used????? + */ +extern PNG_EXPORT(void,png_progressive_combine_row) PNGARG((png_structp png_ptr, + png_bytep old_row, png_bytep new_row)); +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +extern PNG_EXPORT(png_voidp,png_malloc) PNGARG((png_structp png_ptr, + png_uint_32 size)); + +#if defined(PNG_1_0_X) +# define png_malloc_warn png_malloc +#else +/* Added at libpng version 1.2.4 */ +extern PNG_EXPORT(png_voidp,png_malloc_warn) PNGARG((png_structp png_ptr, + png_uint_32 size)); +#endif + +/* frees a pointer allocated by png_malloc() */ +extern PNG_EXPORT(void,png_free) PNGARG((png_structp png_ptr, png_voidp ptr)); + +#if defined(PNG_1_0_X) +/* Function to allocate memory for zlib. */ +extern PNG_EXPORT(voidpf,png_zalloc) PNGARG((voidpf png_ptr, uInt items, + uInt size)); + +/* Function to free memory for zlib */ +extern PNG_EXPORT(void,png_zfree) PNGARG((voidpf png_ptr, voidpf ptr)); +#endif + +/* Free data that was allocated internally */ +extern PNG_EXPORT(void,png_free_data) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 free_me, int num)); +#ifdef PNG_FREE_ME_SUPPORTED +/* Reassign responsibility for freeing existing data, whether allocated + * by libpng or by the application */ +extern PNG_EXPORT(void,png_data_freer) PNGARG((png_structp png_ptr, + png_infop info_ptr, int freer, png_uint_32 mask)); +#endif +/* assignments for png_data_freer */ +#define PNG_DESTROY_WILL_FREE_DATA 1 +#define PNG_SET_WILL_FREE_DATA 1 +#define PNG_USER_WILL_FREE_DATA 2 +/* Flags for png_ptr->free_me and info_ptr->free_me */ +#define PNG_FREE_HIST 0x0008 +#define PNG_FREE_ICCP 0x0010 +#define PNG_FREE_SPLT 0x0020 +#define PNG_FREE_ROWS 0x0040 +#define PNG_FREE_PCAL 0x0080 +#define PNG_FREE_SCAL 0x0100 +#define PNG_FREE_UNKN 0x0200 +#define PNG_FREE_LIST 0x0400 +#define PNG_FREE_PLTE 0x1000 +#define PNG_FREE_TRNS 0x2000 +#define PNG_FREE_TEXT 0x4000 +#define PNG_FREE_ALL 0x7fff +#define PNG_FREE_MUL 0x4220 /* PNG_FREE_SPLT|PNG_FREE_TEXT|PNG_FREE_UNKN */ + +#ifdef PNG_USER_MEM_SUPPORTED +extern PNG_EXPORT(png_voidp,png_malloc_default) PNGARG((png_structp png_ptr, + png_uint_32 size)); +extern PNG_EXPORT(void,png_free_default) PNGARG((png_structp png_ptr, + png_voidp ptr)); +#endif + +extern PNG_EXPORT(png_voidp,png_memcpy_check) PNGARG((png_structp png_ptr, + png_voidp s1, png_voidp s2, png_uint_32 size)); + +extern PNG_EXPORT(png_voidp,png_memset_check) PNGARG((png_structp png_ptr, + png_voidp s1, int value, png_uint_32 size)); + +#if defined(USE_FAR_KEYWORD) /* memory model conversion function */ +extern void *png_far_to_near PNGARG((png_structp png_ptr,png_voidp ptr, + int check)); +#endif /* USE_FAR_KEYWORD */ + +#ifndef PNG_NO_ERROR_TEXT +/* Fatal error in PNG image of libpng - can't continue */ +extern PNG_EXPORT(void,png_error) PNGARG((png_structp png_ptr, + png_const_charp error_message)); + +/* The same, but the chunk name is prepended to the error string. */ +extern PNG_EXPORT(void,png_chunk_error) PNGARG((png_structp png_ptr, + png_const_charp error_message)); +#else +/* Fatal error in PNG image of libpng - can't continue */ +extern PNG_EXPORT(void,png_err) PNGARG((png_structp png_ptr)); +#endif + +#ifndef PNG_NO_WARNINGS +/* Non-fatal error in libpng. Can continue, but may have a problem. */ +extern PNG_EXPORT(void,png_warning) PNGARG((png_structp png_ptr, + png_const_charp warning_message)); + +#ifdef PNG_READ_SUPPORTED +/* Non-fatal error in libpng, chunk name is prepended to message. */ +extern PNG_EXPORT(void,png_chunk_warning) PNGARG((png_structp png_ptr, + png_const_charp warning_message)); +#endif /* PNG_READ_SUPPORTED */ +#endif /* PNG_NO_WARNINGS */ + +/* The png_set_ functions are for storing values in the png_info_struct. + * Similarly, the png_get_ calls are used to read values from the + * png_info_struct, either storing the parameters in the passed variables, or + * setting pointers into the png_info_struct where the data is stored. The + * png_get_ functions return a non-zero value if the data was available + * in info_ptr, or return zero and do not change any of the parameters if the + * data was not available. + * + * These functions should be used instead of directly accessing png_info + * to avoid problems with future changes in the size and internal layout of + * png_info_struct. + */ +/* Returns "flag" if chunk data is valid in info_ptr. */ +extern PNG_EXPORT(png_uint_32,png_get_valid) PNGARG((png_structp png_ptr, +png_infop info_ptr, png_uint_32 flag)); + +/* Returns number of bytes needed to hold a transformed row. */ +extern PNG_EXPORT(png_uint_32,png_get_rowbytes) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +/* Returns row_pointers, which is an array of pointers to scanlines that was +returned from png_read_png(). */ +extern PNG_EXPORT(png_bytepp,png_get_rows) PNGARG((png_structp png_ptr, +png_infop info_ptr)); +/* Set row_pointers, which is an array of pointers to scanlines for use +by png_write_png(). */ +extern PNG_EXPORT(void,png_set_rows) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytepp row_pointers)); +#endif + +/* Returns number of color channels in image. */ +extern PNG_EXPORT(png_byte,png_get_channels) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* Returns image width in pixels. */ +extern PNG_EXPORT(png_uint_32, png_get_image_width) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image height in pixels. */ +extern PNG_EXPORT(png_uint_32, png_get_image_height) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image bit_depth. */ +extern PNG_EXPORT(png_byte, png_get_bit_depth) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image color_type. */ +extern PNG_EXPORT(png_byte, png_get_color_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image filter_type. */ +extern PNG_EXPORT(png_byte, png_get_filter_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image interlace_type. */ +extern PNG_EXPORT(png_byte, png_get_interlace_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image compression_type. */ +extern PNG_EXPORT(png_byte, png_get_compression_type) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns image resolution in pixels per meter, from pHYs chunk data. */ +extern PNG_EXPORT(png_uint_32, png_get_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_uint_32, png_get_x_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_uint_32, png_get_y_pixels_per_meter) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +/* Returns pixel aspect ratio, computed from pHYs chunk data. */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(float, png_get_pixel_aspect_ratio) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +#endif + +/* Returns image x, y offset in pixels or microns, from oFFs chunk data. */ +extern PNG_EXPORT(png_int_32, png_get_x_offset_pixels) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_int_32, png_get_y_offset_pixels) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_int_32, png_get_x_offset_microns) PNGARG((png_structp +png_ptr, png_infop info_ptr)); +extern PNG_EXPORT(png_int_32, png_get_y_offset_microns) PNGARG((png_structp +png_ptr, png_infop info_ptr)); + +#endif /* PNG_EASY_ACCESS_SUPPORTED */ + +/* Returns pointer to signature string read from PNG header */ +extern PNG_EXPORT(png_bytep,png_get_signature) PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#if defined(PNG_bKGD_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_bKGD) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_16p *background)); +#endif + +#if defined(PNG_bKGD_SUPPORTED) +extern PNG_EXPORT(void,png_set_bKGD) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_16p background)); +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, double *white_x, double *white_y, double *red_x, + double *red_y, double *green_x, double *green_y, double *blue_x, + double *blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_cHRM_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point *int_white_x, png_fixed_point + *int_white_y, png_fixed_point *int_red_x, png_fixed_point *int_red_y, + png_fixed_point *int_green_x, png_fixed_point *int_green_y, png_fixed_point + *int_blue_x, png_fixed_point *int_blue_y)); +#endif +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, double white_x, double white_y, double red_x, + double red_y, double green_x, double green_y, double blue_x, double blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_cHRM_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point int_white_x, png_fixed_point int_white_y, + png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point + int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)); +#endif +#endif + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_gAMA) PNGARG((png_structp png_ptr, + png_infop info_ptr, double *file_gamma)); +#endif +extern PNG_EXPORT(png_uint_32,png_get_gAMA_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point *int_file_gamma)); +#endif + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_gAMA) PNGARG((png_structp png_ptr, + png_infop info_ptr, double file_gamma)); +#endif +extern PNG_EXPORT(void,png_set_gAMA_fixed) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_fixed_point int_file_gamma)); +#endif + +#if defined(PNG_hIST_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_hIST) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_16p *hist)); +#endif + +#if defined(PNG_hIST_SUPPORTED) +extern PNG_EXPORT(void,png_set_hIST) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_16p hist)); +#endif + +extern PNG_EXPORT(png_uint_32,png_get_IHDR) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 *width, png_uint_32 *height, + int *bit_depth, int *color_type, int *interlace_method, + int *compression_method, int *filter_method)); + +extern PNG_EXPORT(void,png_set_IHDR) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_method, int compression_method, + int filter_method)); + +#if defined(PNG_oFFs_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_oFFs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_int_32 *offset_x, png_int_32 *offset_y, + int *unit_type)); +#endif + +#if defined(PNG_oFFs_SUPPORTED) +extern PNG_EXPORT(void,png_set_oFFs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_int_32 offset_x, png_int_32 offset_y, + int unit_type)); +#endif + +#if defined(PNG_pCAL_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_pCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp *purpose, png_int_32 *X0, png_int_32 *X1, + int *type, int *nparams, png_charp *units, png_charpp *params)); +#endif + +#if defined(PNG_pCAL_SUPPORTED) +extern PNG_EXPORT(void,png_set_pCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp purpose, png_int_32 X0, png_int_32 X1, + int type, int nparams, png_charp units, png_charpp params)); +#endif + +#if defined(PNG_pHYs_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_pHYs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); +#endif + +#if defined(PNG_pHYs_SUPPORTED) +extern PNG_EXPORT(void,png_set_pHYs) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 res_x, png_uint_32 res_y, int unit_type)); +#endif + +extern PNG_EXPORT(png_uint_32,png_get_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_colorp *palette, int *num_palette)); + +extern PNG_EXPORT(void,png_set_PLTE) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_colorp palette, int num_palette)); + +#if defined(PNG_sBIT_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_sBIT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_8p *sig_bit)); +#endif + +#if defined(PNG_sBIT_SUPPORTED) +extern PNG_EXPORT(void,png_set_sBIT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_color_8p sig_bit)); +#endif + +#if defined(PNG_sRGB_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_sRGB) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *intent)); +#endif + +#if defined(PNG_sRGB_SUPPORTED) +extern PNG_EXPORT(void,png_set_sRGB) PNGARG((png_structp png_ptr, + png_infop info_ptr, int intent)); +extern PNG_EXPORT(void,png_set_sRGB_gAMA_and_cHRM) PNGARG((png_structp png_ptr, + png_infop info_ptr, int intent)); +#endif + +#if defined(PNG_iCCP_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_iCCP) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charpp name, int *compression_type, + png_charpp profile, png_uint_32 *proflen)); + /* Note to maintainer: profile should be png_bytepp */ +#endif + +#if defined(PNG_iCCP_SUPPORTED) +extern PNG_EXPORT(void,png_set_iCCP) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_charp name, int compression_type, + png_charp profile, png_uint_32 proflen)); + /* Note to maintainer: profile should be png_bytep */ +#endif + +#if defined(PNG_sPLT_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_sPLT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_sPLT_tpp entries)); +#endif + +#if defined(PNG_sPLT_SUPPORTED) +extern PNG_EXPORT(void,png_set_sPLT) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_sPLT_tp entries, int nentries)); +#endif + +#if defined(PNG_TEXT_SUPPORTED) +/* png_get_text also returns the number of text chunks in *num_text */ +extern PNG_EXPORT(png_uint_32,png_get_text) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp *text_ptr, int *num_text)); +#endif + +/* + * Note while png_set_text() will accept a structure whose text, + * language, and translated keywords are NULL pointers, the structure + * returned by png_get_text will always contain regular + * zero-terminated C strings. They might be empty strings but + * they will never be NULL pointers. + */ + +#if defined(PNG_TEXT_SUPPORTED) +extern PNG_EXPORT(void,png_set_text) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp text_ptr, int num_text)); +#endif + +#if defined(PNG_tIME_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_tIME) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_timep *mod_time)); +#endif + +#if defined(PNG_tIME_SUPPORTED) +extern PNG_EXPORT(void,png_set_tIME) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_timep mod_time)); +#endif + +#if defined(PNG_tRNS_SUPPORTED) +extern PNG_EXPORT(png_uint_32,png_get_tRNS) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep *trans, int *num_trans, + png_color_16p *trans_values)); +#endif + +#if defined(PNG_tRNS_SUPPORTED) +extern PNG_EXPORT(void,png_set_tRNS) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_bytep trans, int num_trans, + png_color_16p trans_values)); +#endif + +#if defined(PNG_tRNS_SUPPORTED) +#endif + +#if defined(PNG_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_sCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *unit, double *width, double *height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_get_sCAL_s) PNGARG((png_structp png_ptr, + png_infop info_ptr, int *unit, png_charpp swidth, png_charpp sheight)); +#endif +#endif +#endif /* PNG_sCAL_SUPPORTED */ + +#if defined(PNG_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_sCAL) PNGARG((png_structp png_ptr, + png_infop info_ptr, int unit, double width, double height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +extern PNG_EXPORT(void,png_set_sCAL_s) PNGARG((png_structp png_ptr, + png_infop info_ptr, int unit, png_charp swidth, png_charp sheight)); +#endif +#endif +#endif /* PNG_sCAL_SUPPORTED || PNG_WRITE_sCAL_SUPPORTED */ + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +/* provide a list of chunks and how they are to be handled, if the built-in + handling or default unknown chunk handling is not desired. Any chunks not + listed will be handled in the default manner. The IHDR and IEND chunks + must not be listed. + keep = 0: follow default behaviour + = 1: do not keep + = 2: keep only if safe-to-copy + = 3: keep even if unsafe-to-copy +*/ +extern PNG_EXPORT(void, png_set_keep_unknown_chunks) PNGARG((png_structp + png_ptr, int keep, png_bytep chunk_list, int num_chunks)); +extern PNG_EXPORT(void, png_set_unknown_chunks) PNGARG((png_structp png_ptr, + png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns)); +extern PNG_EXPORT(void, png_set_unknown_chunk_location) + PNGARG((png_structp png_ptr, png_infop info_ptr, int chunk, int location)); +extern PNG_EXPORT(png_uint_32,png_get_unknown_chunks) PNGARG((png_structp + png_ptr, png_infop info_ptr, png_unknown_chunkpp entries)); +#endif +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +PNG_EXPORT(int,png_handle_as_unknown) PNGARG((png_structp png_ptr, png_bytep + chunk_name)); +#endif + +/* Png_free_data() will turn off the "valid" flag for anything it frees. + If you need to turn it off for a chunk that your application has freed, + you can use png_set_invalid(png_ptr, info_ptr, PNG_INFO_CHNK); */ +extern PNG_EXPORT(void, png_set_invalid) PNGARG((png_structp png_ptr, + png_infop info_ptr, int mask)); + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +/* The "params" pointer is currently not used and is for future expansion. */ +extern PNG_EXPORT(void, png_read_png) PNGARG((png_structp png_ptr, + png_infop info_ptr, + int transforms, + png_voidp params)); +extern PNG_EXPORT(void, png_write_png) PNGARG((png_structp png_ptr, + png_infop info_ptr, + int transforms, + png_voidp params)); +#endif + +/* Define PNG_DEBUG at compile time for debugging information. Higher + * numbers for PNG_DEBUG mean more debugging information. This has + * only been added since version 0.95 so it is not implemented throughout + * libpng yet, but more support will be added as needed. + */ +#ifdef PNG_DEBUG +#if (PNG_DEBUG > 0) +#if !defined(PNG_DEBUG_FILE) && defined(_MSC_VER) +#include +#if (PNG_DEBUG > 1) +#define png_debug(l,m) _RPT0(_CRT_WARN,m) +#define png_debug1(l,m,p1) _RPT1(_CRT_WARN,m,p1) +#define png_debug2(l,m,p1,p2) _RPT2(_CRT_WARN,m,p1,p2) +#endif +#else /* PNG_DEBUG_FILE || !_MSC_VER */ +#ifndef PNG_DEBUG_FILE +#define PNG_DEBUG_FILE stderr +#endif /* PNG_DEBUG_FILE */ +#if (PNG_DEBUG > 1) +#define png_debug(l,m) \ +{ \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":"")))); \ +} +#define png_debug1(l,m,p1) \ +{ \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1); \ +} +#define png_debug2(l,m,p1,p2) \ +{ \ + int num_tabs=l; \ + fprintf(PNG_DEBUG_FILE,"%s"m,(num_tabs==1 ? "\t" : \ + (num_tabs==2 ? "\t\t":(num_tabs>2 ? "\t\t\t":""))),p1,p2); \ +} +#endif /* (PNG_DEBUG > 1) */ +#endif /* _MSC_VER */ +#endif /* (PNG_DEBUG > 0) */ +#endif /* PNG_DEBUG */ +#ifndef png_debug +#define png_debug(l, m) +#endif +#ifndef png_debug1 +#define png_debug1(l, m, p1) +#endif +#ifndef png_debug2 +#define png_debug2(l, m, p1, p2) +#endif + +extern PNG_EXPORT(png_charp,png_get_copyright) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(png_charp,png_get_header_ver) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(png_charp,png_get_header_version) PNGARG((png_structp png_ptr)); +extern PNG_EXPORT(png_charp,png_get_libpng_ver) PNGARG((png_structp png_ptr)); + +#ifdef PNG_MNG_FEATURES_SUPPORTED +extern PNG_EXPORT(png_uint_32,png_permit_mng_features) PNGARG((png_structp + png_ptr, png_uint_32 mng_features_permitted)); +#endif + +/* For use in png_set_keep_unknown, added to version 1.2.6 */ +#define PNG_HANDLE_CHUNK_AS_DEFAULT 0 +#define PNG_HANDLE_CHUNK_NEVER 1 +#define PNG_HANDLE_CHUNK_IF_SAFE 2 +#define PNG_HANDLE_CHUNK_ALWAYS 3 + +/* Added to version 1.2.0 */ +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) +#if defined(PNG_MMX_CODE_SUPPORTED) +#define PNG_ASM_FLAG_MMX_SUPPORT_COMPILED 0x01 /* not user-settable */ +#define PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU 0x02 /* not user-settable */ +#define PNG_ASM_FLAG_MMX_READ_COMBINE_ROW 0x04 +#define PNG_ASM_FLAG_MMX_READ_INTERLACE 0x08 +#define PNG_ASM_FLAG_MMX_READ_FILTER_SUB 0x10 +#define PNG_ASM_FLAG_MMX_READ_FILTER_UP 0x20 +#define PNG_ASM_FLAG_MMX_READ_FILTER_AVG 0x40 +#define PNG_ASM_FLAG_MMX_READ_FILTER_PAETH 0x80 +#define PNG_ASM_FLAGS_INITIALIZED 0x80000000 /* not user-settable */ + +#define PNG_MMX_READ_FLAGS ( PNG_ASM_FLAG_MMX_READ_COMBINE_ROW \ + | PNG_ASM_FLAG_MMX_READ_INTERLACE \ + | PNG_ASM_FLAG_MMX_READ_FILTER_SUB \ + | PNG_ASM_FLAG_MMX_READ_FILTER_UP \ + | PNG_ASM_FLAG_MMX_READ_FILTER_AVG \ + | PNG_ASM_FLAG_MMX_READ_FILTER_PAETH ) +#define PNG_MMX_WRITE_FLAGS ( 0 ) + +#define PNG_MMX_FLAGS ( PNG_ASM_FLAG_MMX_SUPPORT_COMPILED \ + | PNG_ASM_FLAG_MMX_SUPPORT_IN_CPU \ + | PNG_MMX_READ_FLAGS \ + | PNG_MMX_WRITE_FLAGS ) + +#define PNG_SELECT_READ 1 +#define PNG_SELECT_WRITE 2 +#endif /* PNG_MMX_CODE_SUPPORTED */ + +#if !defined(PNG_1_0_X) +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_mmx_flagmask) + PNGARG((int flag_select, int *compilerID)); + +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_asm_flagmask) + PNGARG((int flag_select)); + +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_asm_flags) + PNGARG((png_structp png_ptr)); + +/* pngget.c */ +extern PNG_EXPORT(png_byte,png_get_mmx_bitdepth_threshold) + PNGARG((png_structp png_ptr)); + +/* pngget.c */ +extern PNG_EXPORT(png_uint_32,png_get_mmx_rowbytes_threshold) + PNGARG((png_structp png_ptr)); + +/* pngset.c */ +extern PNG_EXPORT(void,png_set_asm_flags) + PNGARG((png_structp png_ptr, png_uint_32 asm_flags)); + +/* pngset.c */ +extern PNG_EXPORT(void,png_set_mmx_thresholds) + PNGARG((png_structp png_ptr, png_byte mmx_bitdepth_threshold, + png_uint_32 mmx_rowbytes_threshold)); + +#endif /* PNG_1_0_X */ + +#if !defined(PNG_1_0_X) +/* png.c, pnggccrd.c, or pngvcrd.c */ +extern PNG_EXPORT(int,png_mmx_support) PNGARG((void)); +#endif /* PNG_ASSEMBLER_CODE_SUPPORTED */ + +/* Strip the prepended error numbers ("#nnn ") from error and warning + * messages before passing them to the error or warning handler. */ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +extern PNG_EXPORT(void,png_set_strip_error_numbers) PNGARG((png_structp + png_ptr, png_uint_32 strip_mode)); +#endif + +#endif /* PNG_1_0_X */ + +/* Added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +extern PNG_EXPORT(void,png_set_user_limits) PNGARG((png_structp + png_ptr, png_uint_32 user_width_max, png_uint_32 user_height_max)); +extern PNG_EXPORT(png_uint_32,png_get_user_width_max) PNGARG((png_structp + png_ptr)); +extern PNG_EXPORT(png_uint_32,png_get_user_height_max) PNGARG((png_structp + png_ptr)); +#endif + +/* Maintainer: Put new public prototypes here ^, in libpng.3, and project defs */ + +#ifdef PNG_READ_COMPOSITE_NODIV_SUPPORTED +/* With these routines we avoid an integer divide, which will be slower on + * most machines. However, it does take more operations than the corresponding + * divide method, so it may be slower on a few RISC systems. There are two + * shifts (by 8 or 16 bits) and an addition, versus a single integer divide. + * + * Note that the rounding factors are NOT supposed to be the same! 128 and + * 32768 are correct for the NODIV code; 127 and 32767 are correct for the + * standard method. + * + * [Optimized code by Greg Roelofs and Mark Adler...blame us for bugs. :-) ] + */ + + /* fg and bg should be in `gamma 1.0' space; alpha is the opacity */ + +# define png_composite(composite, fg, alpha, bg) \ + { png_uint_16 temp = (png_uint_16)((png_uint_16)(fg) * (png_uint_16)(alpha) \ + + (png_uint_16)(bg)*(png_uint_16)(255 - \ + (png_uint_16)(alpha)) + (png_uint_16)128); \ + (composite) = (png_byte)((temp + (temp >> 8)) >> 8); } + +# define png_composite_16(composite, fg, alpha, bg) \ + { png_uint_32 temp = (png_uint_32)((png_uint_32)(fg) * (png_uint_32)(alpha) \ + + (png_uint_32)(bg)*(png_uint_32)(65535L - \ + (png_uint_32)(alpha)) + (png_uint_32)32768L); \ + (composite) = (png_uint_16)((temp + (temp >> 16)) >> 16); } + +#else /* standard method using integer division */ + +# define png_composite(composite, fg, alpha, bg) \ + (composite) = (png_byte)(((png_uint_16)(fg) * (png_uint_16)(alpha) + \ + (png_uint_16)(bg) * (png_uint_16)(255 - (png_uint_16)(alpha)) + \ + (png_uint_16)127) / 255) + +# define png_composite_16(composite, fg, alpha, bg) \ + (composite) = (png_uint_16)(((png_uint_32)(fg) * (png_uint_32)(alpha) + \ + (png_uint_32)(bg)*(png_uint_32)(65535L - (png_uint_32)(alpha)) + \ + (png_uint_32)32767) / (png_uint_32)65535L) + +#endif /* PNG_READ_COMPOSITE_NODIV_SUPPORTED */ + +/* Inline macros to do direct reads of bytes from the input buffer. These + * require that you are using an architecture that uses PNG byte ordering + * (MSB first) and supports unaligned data storage. I think that PowerPC + * in big-endian mode and 680x0 are the only ones that will support this. + * The x86 line of processors definitely do not. The png_get_int_32() + * routine also assumes we are using two's complement format for negative + * values, which is almost certainly true. + */ +#if defined(PNG_READ_BIG_ENDIAN_SUPPORTED) +# define png_get_uint_32(buf) ( *((png_uint_32p) (buf))) +# define png_get_uint_16(buf) ( *((png_uint_16p) (buf))) +# define png_get_int_32(buf) ( *((png_int_32p) (buf))) +#else +extern PNG_EXPORT(png_uint_32,png_get_uint_32) PNGARG((png_bytep buf)); +extern PNG_EXPORT(png_uint_16,png_get_uint_16) PNGARG((png_bytep buf)); +extern PNG_EXPORT(png_int_32,png_get_int_32) PNGARG((png_bytep buf)); +#endif /* !PNG_READ_BIG_ENDIAN_SUPPORTED */ +extern PNG_EXPORT(png_uint_32,png_get_uint_31) + PNGARG((png_structp png_ptr, png_bytep buf)); +/* No png_get_int_16 -- may be added if there's a real need for it. */ + +/* Place a 32-bit number into a buffer in PNG byte order (big-endian). + */ +extern PNG_EXPORT(void,png_save_uint_32) + PNGARG((png_bytep buf, png_uint_32 i)); +extern PNG_EXPORT(void,png_save_int_32) + PNGARG((png_bytep buf, png_int_32 i)); + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +extern PNG_EXPORT(void,png_save_uint_16) + PNGARG((png_bytep buf, unsigned int i)); +/* No png_save_int_16 -- may be added if there's a real need for it. */ + +/* ************************************************************************* */ + +/* These next functions are used internally in the code. They generally + * shouldn't be used unless you are writing code to add or replace some + * functionality in libpng. More information about most functions can + * be found in the files where the functions are located. + */ + + +/* Various modes of operation, that are visible to applications because + * they are used for unknown chunk location. + */ +#define PNG_HAVE_IHDR 0x01 +#define PNG_HAVE_PLTE 0x02 +#define PNG_HAVE_IDAT 0x04 +#define PNG_AFTER_IDAT 0x08 /* Have complete zlib datastream */ +#define PNG_HAVE_IEND 0x10 + +#if defined(PNG_INTERNAL) + +/* More modes of operation. Note that after an init, mode is set to + * zero automatically when the structure is created. + */ +#define PNG_HAVE_gAMA 0x20 +#define PNG_HAVE_cHRM 0x40 +#define PNG_HAVE_sRGB 0x80 +#define PNG_HAVE_CHUNK_HEADER 0x100 +#define PNG_WROTE_tIME 0x200 +#define PNG_WROTE_INFO_BEFORE_PLTE 0x400 +#define PNG_BACKGROUND_IS_GRAY 0x800 +#define PNG_HAVE_PNG_SIGNATURE 0x1000 +#define PNG_HAVE_CHUNK_AFTER_IDAT 0x2000 /* Have another chunk after IDAT */ + +/* flags for the transformations the PNG library does on the image data */ +#define PNG_BGR 0x0001 +#define PNG_INTERLACE 0x0002 +#define PNG_PACK 0x0004 +#define PNG_SHIFT 0x0008 +#define PNG_SWAP_BYTES 0x0010 +#define PNG_INVERT_MONO 0x0020 +#define PNG_DITHER 0x0040 +#define PNG_BACKGROUND 0x0080 +#define PNG_BACKGROUND_EXPAND 0x0100 + /* 0x0200 unused */ +#define PNG_16_TO_8 0x0400 +#define PNG_RGBA 0x0800 +#define PNG_EXPAND 0x1000 +#define PNG_GAMMA 0x2000 +#define PNG_GRAY_TO_RGB 0x4000 +#define PNG_FILLER 0x8000L +#define PNG_PACKSWAP 0x10000L +#define PNG_SWAP_ALPHA 0x20000L +#define PNG_STRIP_ALPHA 0x40000L +#define PNG_INVERT_ALPHA 0x80000L +#define PNG_USER_TRANSFORM 0x100000L +#define PNG_RGB_TO_GRAY_ERR 0x200000L +#define PNG_RGB_TO_GRAY_WARN 0x400000L +#define PNG_RGB_TO_GRAY 0x600000L /* two bits, RGB_TO_GRAY_ERR|WARN */ + /* 0x800000L Unused */ +#define PNG_ADD_ALPHA 0x1000000L /* Added to libpng-1.2.7 */ +#define PNG_EXPAND_tRNS 0x2000000L /* Added to libpng-1.2.9 */ + /* 0x4000000L unused */ + /* 0x8000000L unused */ + /* 0x10000000L unused */ + /* 0x20000000L unused */ + /* 0x40000000L unused */ + +/* flags for png_create_struct */ +#define PNG_STRUCT_PNG 0x0001 +#define PNG_STRUCT_INFO 0x0002 + +/* Scaling factor for filter heuristic weighting calculations */ +#define PNG_WEIGHT_SHIFT 8 +#define PNG_WEIGHT_FACTOR (1<<(PNG_WEIGHT_SHIFT)) +#define PNG_COST_SHIFT 3 +#define PNG_COST_FACTOR (1<<(PNG_COST_SHIFT)) + +/* flags for the png_ptr->flags rather than declaring a byte for each one */ +#define PNG_FLAG_ZLIB_CUSTOM_STRATEGY 0x0001 +#define PNG_FLAG_ZLIB_CUSTOM_LEVEL 0x0002 +#define PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL 0x0004 +#define PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS 0x0008 +#define PNG_FLAG_ZLIB_CUSTOM_METHOD 0x0010 +#define PNG_FLAG_ZLIB_FINISHED 0x0020 +#define PNG_FLAG_ROW_INIT 0x0040 +#define PNG_FLAG_FILLER_AFTER 0x0080 +#define PNG_FLAG_CRC_ANCILLARY_USE 0x0100 +#define PNG_FLAG_CRC_ANCILLARY_NOWARN 0x0200 +#define PNG_FLAG_CRC_CRITICAL_USE 0x0400 +#define PNG_FLAG_CRC_CRITICAL_IGNORE 0x0800 +#define PNG_FLAG_FREE_PLTE 0x1000 +#define PNG_FLAG_FREE_TRNS 0x2000 +#define PNG_FLAG_FREE_HIST 0x4000 +#define PNG_FLAG_KEEP_UNKNOWN_CHUNKS 0x8000L +#define PNG_FLAG_KEEP_UNSAFE_CHUNKS 0x10000L +#define PNG_FLAG_LIBRARY_MISMATCH 0x20000L +#define PNG_FLAG_STRIP_ERROR_NUMBERS 0x40000L +#define PNG_FLAG_STRIP_ERROR_TEXT 0x80000L +#define PNG_FLAG_MALLOC_NULL_MEM_OK 0x100000L +#define PNG_FLAG_ADD_ALPHA 0x200000L /* Added to libpng-1.2.8 */ +#define PNG_FLAG_STRIP_ALPHA 0x400000L /* Added to libpng-1.2.8 */ + /* 0x800000L unused */ + /* 0x1000000L unused */ + /* 0x2000000L unused */ + /* 0x4000000L unused */ + /* 0x8000000L unused */ + /* 0x10000000L unused */ + /* 0x20000000L unused */ + /* 0x40000000L unused */ + +#define PNG_FLAG_CRC_ANCILLARY_MASK (PNG_FLAG_CRC_ANCILLARY_USE | \ + PNG_FLAG_CRC_ANCILLARY_NOWARN) + +#define PNG_FLAG_CRC_CRITICAL_MASK (PNG_FLAG_CRC_CRITICAL_USE | \ + PNG_FLAG_CRC_CRITICAL_IGNORE) + +#define PNG_FLAG_CRC_MASK (PNG_FLAG_CRC_ANCILLARY_MASK | \ + PNG_FLAG_CRC_CRITICAL_MASK) + +/* save typing and make code easier to understand */ + +#define PNG_COLOR_DIST(c1, c2) (abs((int)((c1).red) - (int)((c2).red)) + \ + abs((int)((c1).green) - (int)((c2).green)) + \ + abs((int)((c1).blue) - (int)((c2).blue))) + +/* Added to libpng-1.2.6 JB */ +#define PNG_ROWBYTES(pixel_bits, width) \ + ((pixel_bits) >= 8 ? \ + ((width) * (((png_uint_32)(pixel_bits)) >> 3)) : \ + (( ((width) * ((png_uint_32)(pixel_bits))) + 7) >> 3) ) + +/* PNG_OUT_OF_RANGE returns true if value is outside the range + ideal-delta..ideal+delta. Each argument is evaluated twice. + "ideal" and "delta" should be constants, normally simple + integers, "value" a variable. Added to libpng-1.2.6 JB */ +#define PNG_OUT_OF_RANGE(value, ideal, delta) \ + ( (value) < (ideal)-(delta) || (value) > (ideal)+(delta) ) + +/* variables declared in png.c - only it needs to define PNG_NO_EXTERN */ +#if !defined(PNG_NO_EXTERN) || defined(PNG_ALWAYS_EXTERN) +/* place to hold the signature string for a PNG file. */ +#ifdef PNG_USE_GLOBAL_ARRAYS + PNG_EXPORT_VAR (PNG_CONST png_byte FARDATA) png_sig[8]; +#else +#endif +#endif /* PNG_NO_EXTERN */ + +/* Constant strings for known chunk types. If you need to add a chunk, + * define the name here, and add an invocation of the macro in png.c and + * wherever it's needed. + */ +#define PNG_IHDR png_byte png_IHDR[5] = { 73, 72, 68, 82, '\0'} +#define PNG_IDAT png_byte png_IDAT[5] = { 73, 68, 65, 84, '\0'} +#define PNG_IEND png_byte png_IEND[5] = { 73, 69, 78, 68, '\0'} +#define PNG_PLTE png_byte png_PLTE[5] = { 80, 76, 84, 69, '\0'} +#define PNG_bKGD png_byte png_bKGD[5] = { 98, 75, 71, 68, '\0'} +#define PNG_cHRM png_byte png_cHRM[5] = { 99, 72, 82, 77, '\0'} +#define PNG_gAMA png_byte png_gAMA[5] = {103, 65, 77, 65, '\0'} +#define PNG_hIST png_byte png_hIST[5] = {104, 73, 83, 84, '\0'} +#define PNG_iCCP png_byte png_iCCP[5] = {105, 67, 67, 80, '\0'} +#define PNG_iTXt png_byte png_iTXt[5] = {105, 84, 88, 116, '\0'} +#define PNG_oFFs png_byte png_oFFs[5] = {111, 70, 70, 115, '\0'} +#define PNG_pCAL png_byte png_pCAL[5] = {112, 67, 65, 76, '\0'} +#define PNG_sCAL png_byte png_sCAL[5] = {115, 67, 65, 76, '\0'} +#define PNG_pHYs png_byte png_pHYs[5] = {112, 72, 89, 115, '\0'} +#define PNG_sBIT png_byte png_sBIT[5] = {115, 66, 73, 84, '\0'} +#define PNG_sPLT png_byte png_sPLT[5] = {115, 80, 76, 84, '\0'} +#define PNG_sRGB png_byte png_sRGB[5] = {115, 82, 71, 66, '\0'} +#define PNG_tEXt png_byte png_tEXt[5] = {116, 69, 88, 116, '\0'} +#define PNG_tIME png_byte png_tIME[5] = {116, 73, 77, 69, '\0'} +#define PNG_tRNS png_byte png_tRNS[5] = {116, 82, 78, 83, '\0'} +#define PNG_zTXt png_byte png_zTXt[5] = {122, 84, 88, 116, '\0'} + +#ifdef PNG_USE_GLOBAL_ARRAYS +PNG_EXPORT_VAR (png_byte FARDATA) png_IHDR[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_IDAT[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_IEND[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_PLTE[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_bKGD[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_cHRM[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_gAMA[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_hIST[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_iCCP[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_iTXt[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_oFFs[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_pCAL[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_sCAL[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_pHYs[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_sBIT[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_sPLT[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_sRGB[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_tEXt[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_tIME[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_tRNS[5]; +PNG_EXPORT_VAR (png_byte FARDATA) png_zTXt[5]; +#endif /* PNG_USE_GLOBAL_ARRAYS */ + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Initialize png_ptr struct for reading, and allocate any other memory. + * (old interface - DEPRECATED - use png_create_read_struct instead). + */ +extern PNG_EXPORT(void,png_read_init) PNGARG((png_structp png_ptr)); +#undef png_read_init +#define png_read_init(png_ptr) png_read_init_3(&png_ptr, \ + PNG_LIBPNG_VER_STRING, png_sizeof(png_struct)); +#endif + +extern PNG_EXPORT(void,png_read_init_3) PNGARG((png_structpp ptr_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size)); +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +extern PNG_EXPORT(void,png_read_init_2) PNGARG((png_structp png_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size, png_size_t + png_info_size)); +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Initialize png_ptr struct for writing, and allocate any other memory. + * (old interface - DEPRECATED - use png_create_write_struct instead). + */ +extern PNG_EXPORT(void,png_write_init) PNGARG((png_structp png_ptr)); +#undef png_write_init +#define png_write_init(png_ptr) png_write_init_3(&png_ptr, \ + PNG_LIBPNG_VER_STRING, png_sizeof(png_struct)); +#endif + +extern PNG_EXPORT(void,png_write_init_3) PNGARG((png_structpp ptr_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size)); +extern PNG_EXPORT(void,png_write_init_2) PNGARG((png_structp png_ptr, + png_const_charp user_png_ver, png_size_t png_struct_size, png_size_t + png_info_size)); + +/* Allocate memory for an internal libpng struct */ +PNG_EXTERN png_voidp png_create_struct PNGARG((int type)); + +/* Free memory from internal libpng struct */ +PNG_EXTERN void png_destroy_struct PNGARG((png_voidp struct_ptr)); + +PNG_EXTERN png_voidp png_create_struct_2 PNGARG((int type, png_malloc_ptr + malloc_fn, png_voidp mem_ptr)); +PNG_EXTERN void png_destroy_struct_2 PNGARG((png_voidp struct_ptr, + png_free_ptr free_fn, png_voidp mem_ptr)); + +/* Free any memory that info_ptr points to and reset struct. */ +PNG_EXTERN void png_info_destroy PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +#ifndef PNG_1_0_X +/* Function to allocate memory for zlib. */ +PNG_EXTERN voidpf png_zalloc PNGARG((voidpf png_ptr, uInt items, uInt size)); + +/* Function to free memory for zlib */ +PNG_EXTERN void png_zfree PNGARG((voidpf png_ptr, voidpf ptr)); + +#ifdef PNG_SIZE_T +/* Function to convert a sizeof an item to png_sizeof item */ + PNG_EXTERN png_size_t PNGAPI png_convert_size PNGARG((size_t size)); +#endif + +/* Next four functions are used internally as callbacks. PNGAPI is required + * but not PNG_EXPORT. PNGAPI added at libpng version 1.2.3. */ + +PNG_EXTERN void PNGAPI png_default_read_data PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void PNGAPI png_push_fill_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t length)); +#endif + +PNG_EXTERN void PNGAPI png_default_write_data PNGARG((png_structp png_ptr, + png_bytep data, png_size_t length)); + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +#if !defined(PNG_NO_STDIO) +PNG_EXTERN void PNGAPI png_default_flush PNGARG((png_structp png_ptr)); +#endif +#endif +#else /* PNG_1_0_X */ +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void png_push_fill_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t length)); +#endif +#endif /* PNG_1_0_X */ + +/* Reset the CRC variable */ +PNG_EXTERN void png_reset_crc PNGARG((png_structp png_ptr)); + +/* Write the "data" buffer to whatever output you are using. */ +PNG_EXTERN void png_write_data PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)); + +/* Read data from whatever input you are using into the "data" buffer */ +PNG_EXTERN void png_read_data PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)); + +/* Read bytes into buf, and update png_ptr->crc */ +PNG_EXTERN void png_crc_read PNGARG((png_structp png_ptr, png_bytep buf, + png_size_t length)); + +/* Decompress data in a chunk that uses compression */ +#if defined(PNG_zTXt_SUPPORTED) || defined(PNG_iTXt_SUPPORTED) || \ + defined(PNG_iCCP_SUPPORTED) || defined(PNG_sPLT_SUPPORTED) +PNG_EXTERN png_charp png_decompress_chunk PNGARG((png_structp png_ptr, + int comp_type, png_charp chunkdata, png_size_t chunklength, + png_size_t prefix_length, png_size_t *data_length)); +#endif + +/* Read "skip" bytes, read the file crc, and (optionally) verify png_ptr->crc */ +PNG_EXTERN int png_crc_finish PNGARG((png_structp png_ptr, png_uint_32 skip)); + +/* Read the CRC from the file and compare it to the libpng calculated CRC */ +PNG_EXTERN int png_crc_error PNGARG((png_structp png_ptr)); + +/* Calculate the CRC over a section of data. Note that we are only + * passing a maximum of 64K on systems that have this as a memory limit, + * since this is the maximum buffer size we can specify. + */ +PNG_EXTERN void png_calculate_crc PNGARG((png_structp png_ptr, png_bytep ptr, + png_size_t length)); + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +PNG_EXTERN void png_flush PNGARG((png_structp png_ptr)); +#endif + +/* simple function to write the signature */ +PNG_EXTERN void png_write_sig PNGARG((png_structp png_ptr)); + +/* write various chunks */ + +/* Write the IHDR chunk, and update the png_struct with the necessary + * information. + */ +PNG_EXTERN void png_write_IHDR PNGARG((png_structp png_ptr, png_uint_32 width, + png_uint_32 height, + int bit_depth, int color_type, int compression_method, int filter_method, + int interlace_method)); + +PNG_EXTERN void png_write_PLTE PNGARG((png_structp png_ptr, png_colorp palette, + png_uint_32 num_pal)); + +PNG_EXTERN void png_write_IDAT PNGARG((png_structp png_ptr, png_bytep data, + png_size_t length)); + +PNG_EXTERN void png_write_IEND PNGARG((png_structp png_ptr)); + +#if defined(PNG_WRITE_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXTERN void png_write_gAMA PNGARG((png_structp png_ptr, double file_gamma)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_gAMA_fixed PNGARG((png_structp png_ptr, png_fixed_point + file_gamma)); +#endif +#endif + +#if defined(PNG_WRITE_sBIT_SUPPORTED) +PNG_EXTERN void png_write_sBIT PNGARG((png_structp png_ptr, png_color_8p sbit, + int color_type)); +#endif + +#if defined(PNG_WRITE_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +PNG_EXTERN void png_write_cHRM PNGARG((png_structp png_ptr, + double white_x, double white_y, + double red_x, double red_y, double green_x, double green_y, + double blue_x, double blue_y)); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_cHRM_fixed PNGARG((png_structp png_ptr, + png_fixed_point int_white_x, png_fixed_point int_white_y, + png_fixed_point int_red_x, png_fixed_point int_red_y, png_fixed_point + int_green_x, png_fixed_point int_green_y, png_fixed_point int_blue_x, + png_fixed_point int_blue_y)); +#endif +#endif + +#if defined(PNG_WRITE_sRGB_SUPPORTED) +PNG_EXTERN void png_write_sRGB PNGARG((png_structp png_ptr, + int intent)); +#endif + +#if defined(PNG_WRITE_iCCP_SUPPORTED) +PNG_EXTERN void png_write_iCCP PNGARG((png_structp png_ptr, + png_charp name, int compression_type, + png_charp profile, int proflen)); + /* Note to maintainer: profile should be png_bytep */ +#endif + +#if defined(PNG_WRITE_sPLT_SUPPORTED) +PNG_EXTERN void png_write_sPLT PNGARG((png_structp png_ptr, + png_sPLT_tp palette)); +#endif + +#if defined(PNG_WRITE_tRNS_SUPPORTED) +PNG_EXTERN void png_write_tRNS PNGARG((png_structp png_ptr, png_bytep trans, + png_color_16p values, int number, int color_type)); +#endif + +#if defined(PNG_WRITE_bKGD_SUPPORTED) +PNG_EXTERN void png_write_bKGD PNGARG((png_structp png_ptr, + png_color_16p values, int color_type)); +#endif + +#if defined(PNG_WRITE_hIST_SUPPORTED) +PNG_EXTERN void png_write_hIST PNGARG((png_structp png_ptr, png_uint_16p hist, + int num_hist)); +#endif + +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \ + defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) +PNG_EXTERN png_size_t png_check_keyword PNGARG((png_structp png_ptr, + png_charp key, png_charpp new_key)); +#endif + +#if defined(PNG_WRITE_tEXt_SUPPORTED) +PNG_EXTERN void png_write_tEXt PNGARG((png_structp png_ptr, png_charp key, + png_charp text, png_size_t text_len)); +#endif + +#if defined(PNG_WRITE_zTXt_SUPPORTED) +PNG_EXTERN void png_write_zTXt PNGARG((png_structp png_ptr, png_charp key, + png_charp text, png_size_t text_len, int compression)); +#endif + +#if defined(PNG_WRITE_iTXt_SUPPORTED) +PNG_EXTERN void png_write_iTXt PNGARG((png_structp png_ptr, + int compression, png_charp key, png_charp lang, png_charp lang_key, + png_charp text)); +#endif + +#if defined(PNG_TEXT_SUPPORTED) /* Added at version 1.0.14 and 1.2.4 */ +PNG_EXTERN int png_set_text_2 PNGARG((png_structp png_ptr, + png_infop info_ptr, png_textp text_ptr, int num_text)); +#endif + +#if defined(PNG_WRITE_oFFs_SUPPORTED) +PNG_EXTERN void png_write_oFFs PNGARG((png_structp png_ptr, + png_int_32 x_offset, png_int_32 y_offset, int unit_type)); +#endif + +#if defined(PNG_WRITE_pCAL_SUPPORTED) +PNG_EXTERN void png_write_pCAL PNGARG((png_structp png_ptr, png_charp purpose, + png_int_32 X0, png_int_32 X1, int type, int nparams, + png_charp units, png_charpp params)); +#endif + +#if defined(PNG_WRITE_pHYs_SUPPORTED) +PNG_EXTERN void png_write_pHYs PNGARG((png_structp png_ptr, + png_uint_32 x_pixels_per_unit, png_uint_32 y_pixels_per_unit, + int unit_type)); +#endif + +#if defined(PNG_WRITE_tIME_SUPPORTED) +PNG_EXTERN void png_write_tIME PNGARG((png_structp png_ptr, + png_timep mod_time)); +#endif + +#if defined(PNG_WRITE_sCAL_SUPPORTED) +#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO) +PNG_EXTERN void png_write_sCAL PNGARG((png_structp png_ptr, + int unit, double width, double height)); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +PNG_EXTERN void png_write_sCAL_s PNGARG((png_structp png_ptr, + int unit, png_charp width, png_charp height)); +#endif +#endif +#endif + +/* Called when finished processing a row of data */ +PNG_EXTERN void png_write_finish_row PNGARG((png_structp png_ptr)); + +/* Internal use only. Called before first row of data */ +PNG_EXTERN void png_write_start_row PNGARG((png_structp png_ptr)); + +#if defined(PNG_READ_GAMMA_SUPPORTED) +PNG_EXTERN void png_build_gamma_table PNGARG((png_structp png_ptr)); +#endif + +/* combine a row of data, dealing with alpha, etc. if requested */ +PNG_EXTERN void png_combine_row PNGARG((png_structp png_ptr, png_bytep row, + int mask)); + +#if defined(PNG_READ_INTERLACING_SUPPORTED) +/* expand an interlaced row */ +/* OLD pre-1.0.9 interface: +PNG_EXTERN void png_do_read_interlace PNGARG((png_row_infop row_info, + png_bytep row, int pass, png_uint_32 transformations)); + */ +PNG_EXTERN void png_do_read_interlace PNGARG((png_structp png_ptr)); +#endif + +/* GRR TO DO (2.0 or whenever): simplify other internal calling interfaces */ + +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* grab pixels out of a row for an interlaced pass */ +PNG_EXTERN void png_do_write_interlace PNGARG((png_row_infop row_info, + png_bytep row, int pass)); +#endif + +/* unfilter a row */ +PNG_EXTERN void png_read_filter_row PNGARG((png_structp png_ptr, + png_row_infop row_info, png_bytep row, png_bytep prev_row, int filter)); + +/* Choose the best filter to use and filter the row data */ +PNG_EXTERN void png_write_find_filter PNGARG((png_structp png_ptr, + png_row_infop row_info)); + +/* Write out the filtered row. */ +PNG_EXTERN void png_write_filtered_row PNGARG((png_structp png_ptr, + png_bytep filtered_row)); +/* finish a row while reading, dealing with interlacing passes, etc. */ +PNG_EXTERN void png_read_finish_row PNGARG((png_structp png_ptr)); + +/* initialize the row buffers, etc. */ +PNG_EXTERN void png_read_start_row PNGARG((png_structp png_ptr)); +/* optional call to update the users info structure */ +PNG_EXTERN void png_read_transform_info PNGARG((png_structp png_ptr, + png_infop info_ptr)); + +/* these are the functions that do the transformations */ +#if defined(PNG_READ_FILLER_SUPPORTED) +PNG_EXTERN void png_do_read_filler PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 filler, png_uint_32 flags)); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_read_swap_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_write_swap_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_read_invert_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_write_invert_alpha PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +PNG_EXTERN void png_do_strip_filler PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 flags)); +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +PNG_EXTERN void png_do_swap PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) || defined(PNG_WRITE_PACKSWAP_SUPPORTED) +PNG_EXTERN void png_do_packswap PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +PNG_EXTERN int png_do_rgb_to_gray PNGARG((png_structp png_ptr, png_row_infop + row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +PNG_EXTERN void png_do_gray_to_rgb PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) +PNG_EXTERN void png_do_unpack PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) +PNG_EXTERN void png_do_unshift PNGARG((png_row_infop row_info, png_bytep row, + png_color_8p sig_bits)); +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +PNG_EXTERN void png_do_invert PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) +PNG_EXTERN void png_do_chop PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) +PNG_EXTERN void png_do_dither PNGARG((png_row_infop row_info, + png_bytep row, png_bytep palette_lookup, png_bytep dither_lookup)); + +# if defined(PNG_CORRECT_PALETTE_SUPPORTED) +PNG_EXTERN void png_correct_palette PNGARG((png_structp png_ptr, + png_colorp palette, int num_palette)); +# endif +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +PNG_EXTERN void png_do_bgr PNGARG((png_row_infop row_info, png_bytep row)); +#endif + +#if defined(PNG_WRITE_PACK_SUPPORTED) +PNG_EXTERN void png_do_pack PNGARG((png_row_infop row_info, + png_bytep row, png_uint_32 bit_depth)); +#endif + +#if defined(PNG_WRITE_SHIFT_SUPPORTED) +PNG_EXTERN void png_do_shift PNGARG((png_row_infop row_info, png_bytep row, + png_color_8p bit_depth)); +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) +#if defined(PNG_READ_GAMMA_SUPPORTED) +PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row, + png_color_16p trans_values, png_color_16p background, + png_color_16p background_1, + png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1, + png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1, + png_uint_16pp gamma_16_to_1, int gamma_shift)); +#else +PNG_EXTERN void png_do_background PNGARG((png_row_infop row_info, png_bytep row, + png_color_16p trans_values, png_color_16p background)); +#endif +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) +PNG_EXTERN void png_do_gamma PNGARG((png_row_infop row_info, png_bytep row, + png_bytep gamma_table, png_uint_16pp gamma_16_table, + int gamma_shift)); +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) +PNG_EXTERN void png_do_expand_palette PNGARG((png_row_infop row_info, + png_bytep row, png_colorp palette, png_bytep trans, int num_trans)); +PNG_EXTERN void png_do_expand PNGARG((png_row_infop row_info, + png_bytep row, png_color_16p trans_value)); +#endif + +/* The following decodes the appropriate chunks, and does error correction, + * then calls the appropriate callback for the chunk if it is valid. + */ + +/* decode the IHDR chunk */ +PNG_EXTERN void png_handle_IHDR PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +PNG_EXTERN void png_handle_PLTE PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +PNG_EXTERN void png_handle_IEND PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); + +#if defined(PNG_READ_bKGD_SUPPORTED) +PNG_EXTERN void png_handle_bKGD PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_cHRM_SUPPORTED) +PNG_EXTERN void png_handle_cHRM PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_gAMA_SUPPORTED) +PNG_EXTERN void png_handle_gAMA PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_hIST_SUPPORTED) +PNG_EXTERN void png_handle_hIST PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_iCCP_SUPPORTED) +extern void png_handle_iCCP PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif /* PNG_READ_iCCP_SUPPORTED */ + +#if defined(PNG_READ_iTXt_SUPPORTED) +PNG_EXTERN void png_handle_iTXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_oFFs_SUPPORTED) +PNG_EXTERN void png_handle_oFFs PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_pCAL_SUPPORTED) +PNG_EXTERN void png_handle_pCAL PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_pHYs_SUPPORTED) +PNG_EXTERN void png_handle_pHYs PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_sBIT_SUPPORTED) +PNG_EXTERN void png_handle_sBIT PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_sCAL_SUPPORTED) +PNG_EXTERN void png_handle_sCAL PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_sPLT_SUPPORTED) +extern void png_handle_sPLT PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif /* PNG_READ_sPLT_SUPPORTED */ + +#if defined(PNG_READ_sRGB_SUPPORTED) +PNG_EXTERN void png_handle_sRGB PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_tEXt_SUPPORTED) +PNG_EXTERN void png_handle_tEXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_tIME_SUPPORTED) +PNG_EXTERN void png_handle_tIME PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_tRNS_SUPPORTED) +PNG_EXTERN void png_handle_tRNS PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +#if defined(PNG_READ_zTXt_SUPPORTED) +PNG_EXTERN void png_handle_zTXt PNGARG((png_structp png_ptr, png_infop info_ptr, + png_uint_32 length)); +#endif + +PNG_EXTERN void png_handle_unknown PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); + +PNG_EXTERN void png_check_chunk_name PNGARG((png_structp png_ptr, + png_bytep chunk_name)); + +/* handle the transformations for reading and writing */ +PNG_EXTERN void png_do_read_transformations PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_do_write_transformations PNGARG((png_structp png_ptr)); + +PNG_EXTERN void png_init_read_transformations PNGARG((png_structp png_ptr)); + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +PNG_EXTERN void png_push_read_chunk PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_read_sig PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_check_crc PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_crc_skip PNGARG((png_structp png_ptr, + png_uint_32 length)); +PNG_EXTERN void png_push_crc_finish PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_save_buffer PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_restore_buffer PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t buffer_length)); +PNG_EXTERN void png_push_read_IDAT PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_process_IDAT_data PNGARG((png_structp png_ptr, + png_bytep buffer, png_size_t buffer_length)); +PNG_EXTERN void png_push_process_row PNGARG((png_structp png_ptr)); +PNG_EXTERN void png_push_handle_unknown PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_have_info PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_have_end PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_push_have_row PNGARG((png_structp png_ptr, png_bytep row)); +PNG_EXTERN void png_push_read_end PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_process_some_data PNGARG((png_structp png_ptr, + png_infop info_ptr)); +PNG_EXTERN void png_read_push_finish_row PNGARG((png_structp png_ptr)); +#if defined(PNG_READ_tEXt_SUPPORTED) +PNG_EXTERN void png_push_handle_tEXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_read_tEXt PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) +PNG_EXTERN void png_push_handle_zTXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_read_zTXt PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) +PNG_EXTERN void png_push_handle_iTXt PNGARG((png_structp png_ptr, + png_infop info_ptr, png_uint_32 length)); +PNG_EXTERN void png_push_read_iTXt PNGARG((png_structp png_ptr, + png_infop info_ptr)); +#endif + +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + +#ifdef PNG_MNG_FEATURES_SUPPORTED +PNG_EXTERN void png_do_read_intrapixel PNGARG((png_row_infop row_info, + png_bytep row)); +PNG_EXTERN void png_do_write_intrapixel PNGARG((png_row_infop row_info, + png_bytep row)); +#endif + +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) +#if defined(PNG_MMX_CODE_SUPPORTED) +/* png.c */ /* PRIVATE */ +PNG_EXTERN void png_init_mmx_flags PNGARG((png_structp png_ptr)); +#endif +#endif + +#if defined(PNG_INCH_CONVERSIONS) && defined(PNG_FLOATING_POINT_SUPPORTED) +PNG_EXTERN png_uint_32 png_get_pixels_per_inch PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN png_uint_32 png_get_x_pixels_per_inch PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN png_uint_32 png_get_y_pixels_per_inch PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN float png_get_x_offset_inches PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +PNG_EXTERN float png_get_y_offset_inches PNGARG((png_structp png_ptr, +png_infop info_ptr)); + +#if defined(PNG_pHYs_SUPPORTED) +PNG_EXTERN png_uint_32 png_get_pHYs_dpi PNGARG((png_structp png_ptr, +png_infop info_ptr, png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type)); +#endif /* PNG_pHYs_SUPPORTED */ +#endif /* PNG_INCH_CONVERSIONS && PNG_FLOATING_POINT_SUPPORTED */ + +/* Maintainer: Put new private prototypes here ^ and in libpngpf.3 */ + +#endif /* PNG_INTERNAL */ + +#ifdef __cplusplus +} +#endif + +#endif /* PNG_VERSION_INFO_ONLY */ +/* do not put anything past this line */ +#endif /* PNG_H */ diff --git a/Engine/lib/lpng/pngasmrd.h b/Engine/lib/lpng/pngasmrd.h new file mode 100644 index 000000000..2db18524d --- /dev/null +++ b/Engine/lib/lpng/pngasmrd.h @@ -0,0 +1,11 @@ +/* pngasmrd.h - assembler version of utilities to read a PNG file + * + * libpng 1.0.12 - June 8, 2001 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 2001 Glenn Randers-Pehrson + * + */ + +/* This file is obsolete in libpng-1.0.9 and later; its contents now appear + * at the end of pngconf.h. + */ diff --git a/Engine/lib/lpng/pngbar.jpg b/Engine/lib/lpng/pngbar.jpg new file mode 100644 index 000000000..70ba8d817 Binary files /dev/null and b/Engine/lib/lpng/pngbar.jpg differ diff --git a/Engine/lib/lpng/pngbar.png b/Engine/lib/lpng/pngbar.png new file mode 100644 index 000000000..49798c8ed Binary files /dev/null and b/Engine/lib/lpng/pngbar.png differ diff --git a/Engine/lib/lpng/pngconf.h b/Engine/lib/lpng/pngconf.h new file mode 100644 index 000000000..c40507446 --- /dev/null +++ b/Engine/lib/lpng/pngconf.h @@ -0,0 +1,1481 @@ + +/* pngconf.h - machine configurable file for libpng + * + * libpng version 1.2.24 - December 14, 2007 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2007 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +/* Any machine specific code is near the front of this file, so if you + * are configuring libpng for a machine, you may want to read the section + * starting here down to where it starts to typedef png_color, png_text, + * and png_info. + */ + +#ifndef PNGCONF_H +#define PNGCONF_H + +#define PNG_1_2_X + +/* + * PNG_USER_CONFIG has to be defined on the compiler command line. This + * includes the resource compiler for Windows DLL configurations. + */ +#ifdef PNG_USER_CONFIG +# ifndef PNG_USER_PRIVATEBUILD +# define PNG_USER_PRIVATEBUILD +# endif +#include "pngusr.h" +#endif + +/* PNG_CONFIGURE_LIBPNG is set by the "configure" script. */ +#ifdef PNG_CONFIGURE_LIBPNG +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#endif + +/* + * Added at libpng-1.2.8 + * + * If you create a private DLL you need to define in "pngusr.h" the followings: + * #define PNG_USER_PRIVATEBUILD + * e.g. #define PNG_USER_PRIVATEBUILD "Build by MyCompany for xyz reasons." + * #define PNG_USER_DLLFNAME_POSTFIX + * e.g. // private DLL "libpng13gx.dll" + * #define PNG_USER_DLLFNAME_POSTFIX "gx" + * + * The following macros are also at your disposal if you want to complete the + * DLL VERSIONINFO structure. + * - PNG_USER_VERSIONINFO_COMMENTS + * - PNG_USER_VERSIONINFO_COMPANYNAME + * - PNG_USER_VERSIONINFO_LEGALTRADEMARKS + */ + +#ifdef __STDC__ +#ifdef SPECIALBUILD +# pragma message("PNG_LIBPNG_SPECIALBUILD (and deprecated SPECIALBUILD)\ + are now LIBPNG reserved macros. Use PNG_USER_PRIVATEBUILD instead.") +#endif + +#ifdef PRIVATEBUILD +# pragma message("PRIVATEBUILD is deprecated.\ + Use PNG_USER_PRIVATEBUILD instead.") +# define PNG_USER_PRIVATEBUILD PRIVATEBUILD +#endif +#endif /* __STDC__ */ + +#ifndef PNG_VERSION_INFO_ONLY + +/* End of material added to libpng-1.2.8 */ + +/* Added at libpng-1.2.19, removed at libpng-1.2.20 because it caused trouble + Restored at libpng-1.2.21 */ +#if !defined(PNG_NO_WARN_UNINITIALIZED_ROW) && \ + !defined(PNG_WARN_UNINITIALIZED_ROW) +# define PNG_WARN_UNINITIALIZED_ROW 1 +#endif +/* End of material added at libpng-1.2.19/1.2.21 */ + +/* This is the size of the compression buffer, and thus the size of + * an IDAT chunk. Make this whatever size you feel is best for your + * machine. One of these will be allocated per png_struct. When this + * is full, it writes the data to the disk, and does some other + * calculations. Making this an extremely small size will slow + * the library down, but you may want to experiment to determine + * where it becomes significant, if you are concerned with memory + * usage. Note that zlib allocates at least 32Kb also. For readers, + * this describes the size of the buffer available to read the data in. + * Unless this gets smaller than the size of a row (compressed), + * it should not make much difference how big this is. + */ + +#ifndef PNG_ZBUF_SIZE +# define PNG_ZBUF_SIZE 8192 +#endif + +/* Enable if you want a write-only libpng */ + +#ifndef PNG_NO_READ_SUPPORTED +# define PNG_READ_SUPPORTED +#endif + +/* Enable if you want a read-only libpng */ + +#ifndef PNG_NO_WRITE_SUPPORTED +# define PNG_WRITE_SUPPORTED +#endif + +/* Enabled by default in 1.2.0. You can disable this if you don't need to + support PNGs that are embedded in MNG datastreams */ +#if !defined(PNG_1_0_X) && !defined(PNG_NO_MNG_FEATURES) +# ifndef PNG_MNG_FEATURES_SUPPORTED +# define PNG_MNG_FEATURES_SUPPORTED +# endif +#endif + +#ifndef PNG_NO_FLOATING_POINT_SUPPORTED +# ifndef PNG_FLOATING_POINT_SUPPORTED +# define PNG_FLOATING_POINT_SUPPORTED +# endif +#endif + +/* If you are running on a machine where you cannot allocate more + * than 64K of memory at once, uncomment this. While libpng will not + * normally need that much memory in a chunk (unless you load up a very + * large file), zlib needs to know how big of a chunk it can use, and + * libpng thus makes sure to check any memory allocation to verify it + * will fit into memory. +#define PNG_MAX_MALLOC_64K + */ +#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K) +# define PNG_MAX_MALLOC_64K +#endif + +/* Special munging to support doing things the 'cygwin' way: + * 'Normal' png-on-win32 defines/defaults: + * PNG_BUILD_DLL -- building dll + * PNG_USE_DLL -- building an application, linking to dll + * (no define) -- building static library, or building an + * application and linking to the static lib + * 'Cygwin' defines/defaults: + * PNG_BUILD_DLL -- (ignored) building the dll + * (no define) -- (ignored) building an application, linking to the dll + * PNG_STATIC -- (ignored) building the static lib, or building an + * application that links to the static lib. + * ALL_STATIC -- (ignored) building various static libs, or building an + * application that links to the static libs. + * Thus, + * a cygwin user should define either PNG_BUILD_DLL or PNG_STATIC, and + * this bit of #ifdefs will define the 'correct' config variables based on + * that. If a cygwin user *wants* to define 'PNG_USE_DLL' that's okay, but + * unnecessary. + * + * Also, the precedence order is: + * ALL_STATIC (since we can't #undef something outside our namespace) + * PNG_BUILD_DLL + * PNG_STATIC + * (nothing) == PNG_USE_DLL + * + * CYGWIN (2002-01-20): The preceding is now obsolete. With the advent + * of auto-import in binutils, we no longer need to worry about + * __declspec(dllexport) / __declspec(dllimport) and friends. Therefore, + * we don't need to worry about PNG_STATIC or ALL_STATIC when it comes + * to __declspec() stuff. However, we DO need to worry about + * PNG_BUILD_DLL and PNG_STATIC because those change some defaults + * such as CONSOLE_IO and whether GLOBAL_ARRAYS are allowed. + */ +#if defined(__CYGWIN__) +# if defined(ALL_STATIC) +# if defined(PNG_BUILD_DLL) +# undef PNG_BUILD_DLL +# endif +# if defined(PNG_USE_DLL) +# undef PNG_USE_DLL +# endif +# if defined(PNG_DLL) +# undef PNG_DLL +# endif +# if !defined(PNG_STATIC) +# define PNG_STATIC +# endif +# else +# if defined (PNG_BUILD_DLL) +# if defined(PNG_STATIC) +# undef PNG_STATIC +# endif +# if defined(PNG_USE_DLL) +# undef PNG_USE_DLL +# endif +# if !defined(PNG_DLL) +# define PNG_DLL +# endif +# else +# if defined(PNG_STATIC) +# if defined(PNG_USE_DLL) +# undef PNG_USE_DLL +# endif +# if defined(PNG_DLL) +# undef PNG_DLL +# endif +# else +# if !defined(PNG_USE_DLL) +# define PNG_USE_DLL +# endif +# if !defined(PNG_DLL) +# define PNG_DLL +# endif +# endif +# endif +# endif +#endif + +/* This protects us against compilers that run on a windowing system + * and thus don't have or would rather us not use the stdio types: + * stdin, stdout, and stderr. The only one currently used is stderr + * in png_error() and png_warning(). #defining PNG_NO_CONSOLE_IO will + * prevent these from being compiled and used. #defining PNG_NO_STDIO + * will also prevent these, plus will prevent the entire set of stdio + * macros and functions (FILE *, printf, etc.) from being compiled and used, + * unless (PNG_DEBUG > 0) has been #defined. + * + * #define PNG_NO_CONSOLE_IO + * #define PNG_NO_STDIO + */ + +#if defined(_WIN32_WCE) +# include + /* Console I/O functions are not supported on WindowsCE */ +# define PNG_NO_CONSOLE_IO +# ifdef PNG_DEBUG +# undef PNG_DEBUG +# endif +#endif + +#ifdef PNG_BUILD_DLL +# ifndef PNG_CONSOLE_IO_SUPPORTED +# ifndef PNG_NO_CONSOLE_IO +# define PNG_NO_CONSOLE_IO +# endif +# endif +#endif + +# ifdef PNG_NO_STDIO +# ifndef PNG_NO_CONSOLE_IO +# define PNG_NO_CONSOLE_IO +# endif +# ifdef PNG_DEBUG +# if (PNG_DEBUG > 0) +# include +# endif +# endif +# else +# if !defined(_WIN32_WCE) +/* "stdio.h" functions are not supported on WindowsCE */ +# include +# endif +# endif + +/* This macro protects us against machines that don't have function + * prototypes (ie K&R style headers). If your compiler does not handle + * function prototypes, define this macro and use the included ansi2knr. + * I've always been able to use _NO_PROTO as the indicator, but you may + * need to drag the empty declaration out in front of here, or change the + * ifdef to suit your own needs. + */ +#ifndef PNGARG + +#ifdef OF /* zlib prototype munger */ +# define PNGARG(arglist) OF(arglist) +#else + +#ifdef _NO_PROTO +# define PNGARG(arglist) () +# ifndef PNG_TYPECAST_NULL +# define PNG_TYPECAST_NULL +# endif +#else +# define PNGARG(arglist) arglist +#endif /* _NO_PROTO */ + + +#endif /* OF */ + +#endif /* PNGARG */ + +/* Try to determine if we are compiling on a Mac. Note that testing for + * just __MWERKS__ is not good enough, because the Codewarrior is now used + * on non-Mac platforms. + */ +#ifndef MACOS +# if (defined(__MWERKS__) && defined(macintosh)) || defined(applec) || \ + defined(THINK_C) || defined(__SC__) || defined(TARGET_OS_MAC) +# define MACOS +# endif +#endif + +/* enough people need this for various reasons to include it here */ +#if !defined(MACOS) && !defined(RISCOS) && !defined(_WIN32_WCE) +# include +#endif + +#if !defined(PNG_SETJMP_NOT_SUPPORTED) && !defined(PNG_NO_SETJMP_SUPPORTED) +# define PNG_SETJMP_SUPPORTED +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* This is an attempt to force a single setjmp behaviour on Linux. If + * the X config stuff didn't define _BSD_SOURCE we wouldn't need this. + */ + +# ifdef __linux__ +# ifdef _BSD_SOURCE +# define PNG_SAVE_BSD_SOURCE +# undef _BSD_SOURCE +# endif +# ifdef _SETJMP_H + /* If you encounter a compiler error here, see the explanation + * near the end of INSTALL. + */ + __pngconf.h__ already includes setjmp.h; + __dont__ include it again.; +# endif +# endif /* __linux__ */ + + /* include setjmp.h for error handling */ +# include + +# ifdef __linux__ +# ifdef PNG_SAVE_BSD_SOURCE +# ifndef _BSD_SOURCE +# define _BSD_SOURCE +# endif +# undef PNG_SAVE_BSD_SOURCE +# endif +# endif /* __linux__ */ +#endif /* PNG_SETJMP_SUPPORTED */ + +#ifdef BSD +# include +#else +# include +#endif + +/* Other defines for things like memory and the like can go here. */ +#ifdef PNG_INTERNAL + +#include + +/* The functions exported by PNG_EXTERN are PNG_INTERNAL functions, which + * aren't usually used outside the library (as far as I know), so it is + * debatable if they should be exported at all. In the future, when it is + * possible to have run-time registry of chunk-handling functions, some of + * these will be made available again. +#define PNG_EXTERN extern + */ +#define PNG_EXTERN + +/* Other defines specific to compilers can go here. Try to keep + * them inside an appropriate ifdef/endif pair for portability. + */ + +#if defined(PNG_FLOATING_POINT_SUPPORTED) +# if defined(MACOS) + /* We need to check that hasn't already been included earlier + * as it seems it doesn't agree with , yet we should really use + * if possible. + */ +# if !defined(__MATH_H__) && !defined(__MATH_H) && !defined(__cmath__) +# include +# endif +# else +# include +# endif +# if defined(_AMIGA) && defined(__SASC) && defined(_M68881) + /* Amiga SAS/C: We must include builtin FPU functions when compiling using + * MATH=68881 + */ +# include +# endif +#endif + +/* Codewarrior on NT has linking problems without this. */ +#if (defined(__MWERKS__) && defined(WIN32)) || defined(__STDC__) +# define PNG_ALWAYS_EXTERN +#endif + +/* This provides the non-ANSI (far) memory allocation routines. */ +#if defined(__TURBOC__) && defined(__MSDOS__) +# include +# include +#endif + +/* I have no idea why is this necessary... */ +#if defined(_MSC_VER) && (defined(WIN32) || defined(_Windows) || \ + defined(_WINDOWS) || defined(_WIN32) || defined(__WIN32__)) +# include +#endif + +/* This controls how fine the dithering gets. As this allocates + * a largish chunk of memory (32K), those who are not as concerned + * with dithering quality can decrease some or all of these. + */ +#ifndef PNG_DITHER_RED_BITS +# define PNG_DITHER_RED_BITS 5 +#endif +#ifndef PNG_DITHER_GREEN_BITS +# define PNG_DITHER_GREEN_BITS 5 +#endif +#ifndef PNG_DITHER_BLUE_BITS +# define PNG_DITHER_BLUE_BITS 5 +#endif + +/* This controls how fine the gamma correction becomes when you + * are only interested in 8 bits anyway. Increasing this value + * results in more memory being used, and more pow() functions + * being called to fill in the gamma tables. Don't set this value + * less then 8, and even that may not work (I haven't tested it). + */ + +#ifndef PNG_MAX_GAMMA_8 +# define PNG_MAX_GAMMA_8 11 +#endif + +/* This controls how much a difference in gamma we can tolerate before + * we actually start doing gamma conversion. + */ +#ifndef PNG_GAMMA_THRESHOLD +# define PNG_GAMMA_THRESHOLD 0.05 +#endif + +#endif /* PNG_INTERNAL */ + +/* The following uses const char * instead of char * for error + * and warning message functions, so some compilers won't complain. + * If you do not want to use const, define PNG_NO_CONST here. + */ + +#ifndef PNG_NO_CONST +# define PNG_CONST const +#else +# define PNG_CONST +#endif + +/* The following defines give you the ability to remove code from the + * library that you will not be using. I wish I could figure out how to + * automate this, but I can't do that without making it seriously hard + * on the users. So if you are not using an ability, change the #define + * to and #undef, and that part of the library will not be compiled. If + * your linker can't find a function, you may want to make sure the + * ability is defined here. Some of these depend upon some others being + * defined. I haven't figured out all the interactions here, so you may + * have to experiment awhile to get everything to compile. If you are + * creating or using a shared library, you probably shouldn't touch this, + * as it will affect the size of the structures, and this will cause bad + * things to happen if the library and/or application ever change. + */ + +/* Any features you will not be using can be undef'ed here */ + +/* GR-P, 0.96a: Set "*TRANSFORMS_SUPPORTED as default but allow user + * to turn it off with "*TRANSFORMS_NOT_SUPPORTED" or *PNG_NO_*_TRANSFORMS + * on the compile line, then pick and choose which ones to define without + * having to edit this file. It is safe to use the *TRANSFORMS_NOT_SUPPORTED + * if you only want to have a png-compliant reader/writer but don't need + * any of the extra transformations. This saves about 80 kbytes in a + * typical installation of the library. (PNG_NO_* form added in version + * 1.0.1c, for consistency) + */ + +/* The size of the png_text structure changed in libpng-1.0.6 when + * iTXt support was added. iTXt support was turned off by default through + * libpng-1.2.x, to support old apps that malloc the png_text structure + * instead of calling png_set_text() and letting libpng malloc it. It + * was turned on by default in libpng-1.3.0. + */ + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +# ifndef PNG_NO_iTXt_SUPPORTED +# define PNG_NO_iTXt_SUPPORTED +# endif +# ifndef PNG_NO_READ_iTXt +# define PNG_NO_READ_iTXt +# endif +# ifndef PNG_NO_WRITE_iTXt +# define PNG_NO_WRITE_iTXt +# endif +#endif + +#if !defined(PNG_NO_iTXt_SUPPORTED) +# if !defined(PNG_READ_iTXt_SUPPORTED) && !defined(PNG_NO_READ_iTXt) +# define PNG_READ_iTXt +# endif +# if !defined(PNG_WRITE_iTXt_SUPPORTED) && !defined(PNG_NO_WRITE_iTXt) +# define PNG_WRITE_iTXt +# endif +#endif + +/* The following support, added after version 1.0.0, can be turned off here en + * masse by defining PNG_LEGACY_SUPPORTED in case you need binary compatibility + * with old applications that require the length of png_struct and png_info + * to remain unchanged. + */ + +#ifdef PNG_LEGACY_SUPPORTED +# define PNG_NO_FREE_ME +# define PNG_NO_READ_UNKNOWN_CHUNKS +# define PNG_NO_WRITE_UNKNOWN_CHUNKS +# define PNG_NO_READ_USER_CHUNKS +# define PNG_NO_READ_iCCP +# define PNG_NO_WRITE_iCCP +# define PNG_NO_READ_iTXt +# define PNG_NO_WRITE_iTXt +# define PNG_NO_READ_sCAL +# define PNG_NO_WRITE_sCAL +# define PNG_NO_READ_sPLT +# define PNG_NO_WRITE_sPLT +# define PNG_NO_INFO_IMAGE +# define PNG_NO_READ_RGB_TO_GRAY +# define PNG_NO_READ_USER_TRANSFORM +# define PNG_NO_WRITE_USER_TRANSFORM +# define PNG_NO_USER_MEM +# define PNG_NO_READ_EMPTY_PLTE +# define PNG_NO_MNG_FEATURES +# define PNG_NO_FIXED_POINT_SUPPORTED +#endif + +/* Ignore attempt to turn off both floating and fixed point support */ +#if !defined(PNG_FLOATING_POINT_SUPPORTED) || \ + !defined(PNG_NO_FIXED_POINT_SUPPORTED) +# define PNG_FIXED_POINT_SUPPORTED +#endif + +#ifndef PNG_NO_FREE_ME +# define PNG_FREE_ME_SUPPORTED +#endif + +#if defined(PNG_READ_SUPPORTED) + +#if !defined(PNG_READ_TRANSFORMS_NOT_SUPPORTED) && \ + !defined(PNG_NO_READ_TRANSFORMS) +# define PNG_READ_TRANSFORMS_SUPPORTED +#endif + +#ifdef PNG_READ_TRANSFORMS_SUPPORTED +# ifndef PNG_NO_READ_EXPAND +# define PNG_READ_EXPAND_SUPPORTED +# endif +# ifndef PNG_NO_READ_SHIFT +# define PNG_READ_SHIFT_SUPPORTED +# endif +# ifndef PNG_NO_READ_PACK +# define PNG_READ_PACK_SUPPORTED +# endif +# ifndef PNG_NO_READ_BGR +# define PNG_READ_BGR_SUPPORTED +# endif +# ifndef PNG_NO_READ_SWAP +# define PNG_READ_SWAP_SUPPORTED +# endif +# ifndef PNG_NO_READ_PACKSWAP +# define PNG_READ_PACKSWAP_SUPPORTED +# endif +# ifndef PNG_NO_READ_INVERT +# define PNG_READ_INVERT_SUPPORTED +# endif +# ifndef PNG_NO_READ_DITHER +# define PNG_READ_DITHER_SUPPORTED +# endif +# ifndef PNG_NO_READ_BACKGROUND +# define PNG_READ_BACKGROUND_SUPPORTED +# endif +# ifndef PNG_NO_READ_16_TO_8 +# define PNG_READ_16_TO_8_SUPPORTED +# endif +# ifndef PNG_NO_READ_FILLER +# define PNG_READ_FILLER_SUPPORTED +# endif +# ifndef PNG_NO_READ_GAMMA +# define PNG_READ_GAMMA_SUPPORTED +# endif +# ifndef PNG_NO_READ_GRAY_TO_RGB +# define PNG_READ_GRAY_TO_RGB_SUPPORTED +# endif +# ifndef PNG_NO_READ_SWAP_ALPHA +# define PNG_READ_SWAP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_INVERT_ALPHA +# define PNG_READ_INVERT_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_STRIP_ALPHA +# define PNG_READ_STRIP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_READ_USER_TRANSFORM +# define PNG_READ_USER_TRANSFORM_SUPPORTED +# endif +# ifndef PNG_NO_READ_RGB_TO_GRAY +# define PNG_READ_RGB_TO_GRAY_SUPPORTED +# endif +#endif /* PNG_READ_TRANSFORMS_SUPPORTED */ + +#if !defined(PNG_NO_PROGRESSIVE_READ) && \ + !defined(PNG_PROGRESSIVE_READ_SUPPORTED) /* if you don't do progressive */ +# define PNG_PROGRESSIVE_READ_SUPPORTED /* reading. This is not talking */ +#endif /* about interlacing capability! You'll */ + /* still have interlacing unless you change the following line: */ + +#define PNG_READ_INTERLACING_SUPPORTED /* required in PNG-compliant decoders */ + +#ifndef PNG_NO_READ_COMPOSITE_NODIV +# ifndef PNG_NO_READ_COMPOSITED_NODIV /* libpng-1.0.x misspelling */ +# define PNG_READ_COMPOSITE_NODIV_SUPPORTED /* well tested on Intel, SGI */ +# endif +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Deprecated, will be removed from version 2.0.0. + Use PNG_MNG_FEATURES_SUPPORTED instead. */ +#ifndef PNG_NO_READ_EMPTY_PLTE +# define PNG_READ_EMPTY_PLTE_SUPPORTED +#endif +#endif + +#endif /* PNG_READ_SUPPORTED */ + +#if defined(PNG_WRITE_SUPPORTED) + +# if !defined(PNG_WRITE_TRANSFORMS_NOT_SUPPORTED) && \ + !defined(PNG_NO_WRITE_TRANSFORMS) +# define PNG_WRITE_TRANSFORMS_SUPPORTED +#endif + +#ifdef PNG_WRITE_TRANSFORMS_SUPPORTED +# ifndef PNG_NO_WRITE_SHIFT +# define PNG_WRITE_SHIFT_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_PACK +# define PNG_WRITE_PACK_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_BGR +# define PNG_WRITE_BGR_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_SWAP +# define PNG_WRITE_SWAP_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_PACKSWAP +# define PNG_WRITE_PACKSWAP_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_INVERT +# define PNG_WRITE_INVERT_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_FILLER +# define PNG_WRITE_FILLER_SUPPORTED /* same as WRITE_STRIP_ALPHA */ +# endif +# ifndef PNG_NO_WRITE_SWAP_ALPHA +# define PNG_WRITE_SWAP_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_INVERT_ALPHA +# define PNG_WRITE_INVERT_ALPHA_SUPPORTED +# endif +# ifndef PNG_NO_WRITE_USER_TRANSFORM +# define PNG_WRITE_USER_TRANSFORM_SUPPORTED +# endif +#endif /* PNG_WRITE_TRANSFORMS_SUPPORTED */ + +#if !defined(PNG_NO_WRITE_INTERLACING_SUPPORTED) && \ + !defined(PNG_WRITE_INTERLACING_SUPPORTED) +#define PNG_WRITE_INTERLACING_SUPPORTED /* not required for PNG-compliant + encoders, but can cause trouble + if left undefined */ +#endif + +#if !defined(PNG_NO_WRITE_WEIGHTED_FILTER) && \ + !defined(PNG_WRITE_WEIGHTED_FILTER) && \ + defined(PNG_FLOATING_POINT_SUPPORTED) +# define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED +#endif + +#ifndef PNG_NO_WRITE_FLUSH +# define PNG_WRITE_FLUSH_SUPPORTED +#endif + +#if defined(PNG_1_0_X) || defined (PNG_1_2_X) +/* Deprecated, see PNG_MNG_FEATURES_SUPPORTED, above */ +#ifndef PNG_NO_WRITE_EMPTY_PLTE +# define PNG_WRITE_EMPTY_PLTE_SUPPORTED +#endif +#endif + +#endif /* PNG_WRITE_SUPPORTED */ + +#ifndef PNG_1_0_X +# ifndef PNG_NO_ERROR_NUMBERS +# define PNG_ERROR_NUMBERS_SUPPORTED +# endif +#endif /* PNG_1_0_X */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +# ifndef PNG_NO_USER_TRANSFORM_PTR +# define PNG_USER_TRANSFORM_PTR_SUPPORTED +# endif +#endif + +#ifndef PNG_NO_STDIO +# define PNG_TIME_RFC1123_SUPPORTED +#endif + +/* This adds extra functions in pngget.c for accessing data from the + * info pointer (added in version 0.99) + * png_get_image_width() + * png_get_image_height() + * png_get_bit_depth() + * png_get_color_type() + * png_get_compression_type() + * png_get_filter_type() + * png_get_interlace_type() + * png_get_pixel_aspect_ratio() + * png_get_pixels_per_meter() + * png_get_x_offset_pixels() + * png_get_y_offset_pixels() + * png_get_x_offset_microns() + * png_get_y_offset_microns() + */ +#if !defined(PNG_NO_EASY_ACCESS) && !defined(PNG_EASY_ACCESS_SUPPORTED) +# define PNG_EASY_ACCESS_SUPPORTED +#endif + +/* PNG_ASSEMBLER_CODE was enabled by default in version 1.2.0 + * and removed from version 1.2.20. The following will be removed + * from libpng-1.4.0 +*/ + +#if defined(PNG_READ_SUPPORTED) && !defined(PNG_NO_OPTIMIZED_CODE) +# ifndef PNG_OPTIMIZED_CODE_SUPPORTED +# define PNG_OPTIMIZED_CODE_SUPPORTED +# endif +#endif + +#if defined(PNG_READ_SUPPORTED) && !defined(PNG_NO_ASSEMBLER_CODE) +# ifndef PNG_ASSEMBLER_CODE_SUPPORTED +# define PNG_ASSEMBLER_CODE_SUPPORTED +# endif + +# if defined(__GNUC__) && defined(__x86_64__) && (__GNUC__ < 4) + /* work around 64-bit gcc compiler bugs in gcc-3.x */ +# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) +# define PNG_NO_MMX_CODE +# endif +# endif + +# if defined(__APPLE__) +# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) +# define PNG_NO_MMX_CODE +# endif +# endif + +# if (defined(__MWERKS__) && ((__MWERKS__ < 0x0900) || macintosh)) +# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) +# define PNG_NO_MMX_CODE +# endif +# endif + +# if !defined(PNG_MMX_CODE_SUPPORTED) && !defined(PNG_NO_MMX_CODE) +# define PNG_MMX_CODE_SUPPORTED +# endif + +#endif +/* end of obsolete code to be removed from libpng-1.4.0 */ + +#if !defined(PNG_1_0_X) +#if !defined(PNG_NO_USER_MEM) && !defined(PNG_USER_MEM_SUPPORTED) +# define PNG_USER_MEM_SUPPORTED +#endif +#endif /* PNG_1_0_X */ + +/* Added at libpng-1.2.6 */ +#if !defined(PNG_1_0_X) +#ifndef PNG_SET_USER_LIMITS_SUPPORTED +#if !defined(PNG_NO_SET_USER_LIMITS) && !defined(PNG_SET_USER_LIMITS_SUPPORTED) +# define PNG_SET_USER_LIMITS_SUPPORTED +#endif +#endif +#endif /* PNG_1_0_X */ + +/* Added at libpng-1.0.16 and 1.2.6. To accept all valid PNGS no matter + * how large, set these limits to 0x7fffffffL + */ +#ifndef PNG_USER_WIDTH_MAX +# define PNG_USER_WIDTH_MAX 1000000L +#endif +#ifndef PNG_USER_HEIGHT_MAX +# define PNG_USER_HEIGHT_MAX 1000000L +#endif + +/* These are currently experimental features, define them if you want */ + +/* very little testing */ +/* +#ifdef PNG_READ_SUPPORTED +# ifndef PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED +# define PNG_READ_16_TO_8_ACCURATE_SCALE_SUPPORTED +# endif +#endif +*/ + +/* This is only for PowerPC big-endian and 680x0 systems */ +/* some testing */ +/* +#ifndef PNG_READ_BIG_ENDIAN_SUPPORTED +# define PNG_READ_BIG_ENDIAN_SUPPORTED +#endif +*/ + +/* Buggy compilers (e.g., gcc 2.7.2.2) need this */ +/* +#define PNG_NO_POINTER_INDEXING +*/ + +/* These functions are turned off by default, as they will be phased out. */ +/* +#define PNG_USELESS_TESTS_SUPPORTED +#define PNG_CORRECT_PALETTE_SUPPORTED +*/ + +/* Any chunks you are not interested in, you can undef here. The + * ones that allocate memory may be expecially important (hIST, + * tEXt, zTXt, tRNS, pCAL). Others will just save time and make png_info + * a bit smaller. + */ + +#if defined(PNG_READ_SUPPORTED) && \ + !defined(PNG_READ_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \ + !defined(PNG_NO_READ_ANCILLARY_CHUNKS) +# define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED +#endif + +#if defined(PNG_WRITE_SUPPORTED) && \ + !defined(PNG_WRITE_ANCILLARY_CHUNKS_NOT_SUPPORTED) && \ + !defined(PNG_NO_WRITE_ANCILLARY_CHUNKS) +# define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED +#endif + +#ifdef PNG_READ_ANCILLARY_CHUNKS_SUPPORTED + +#ifdef PNG_NO_READ_TEXT +# define PNG_NO_READ_iTXt +# define PNG_NO_READ_tEXt +# define PNG_NO_READ_zTXt +#endif +#ifndef PNG_NO_READ_bKGD +# define PNG_READ_bKGD_SUPPORTED +# define PNG_bKGD_SUPPORTED +#endif +#ifndef PNG_NO_READ_cHRM +# define PNG_READ_cHRM_SUPPORTED +# define PNG_cHRM_SUPPORTED +#endif +#ifndef PNG_NO_READ_gAMA +# define PNG_READ_gAMA_SUPPORTED +# define PNG_gAMA_SUPPORTED +#endif +#ifndef PNG_NO_READ_hIST +# define PNG_READ_hIST_SUPPORTED +# define PNG_hIST_SUPPORTED +#endif +#ifndef PNG_NO_READ_iCCP +# define PNG_READ_iCCP_SUPPORTED +# define PNG_iCCP_SUPPORTED +#endif +#ifndef PNG_NO_READ_iTXt +# ifndef PNG_READ_iTXt_SUPPORTED +# define PNG_READ_iTXt_SUPPORTED +# endif +# ifndef PNG_iTXt_SUPPORTED +# define PNG_iTXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_READ_oFFs +# define PNG_READ_oFFs_SUPPORTED +# define PNG_oFFs_SUPPORTED +#endif +#ifndef PNG_NO_READ_pCAL +# define PNG_READ_pCAL_SUPPORTED +# define PNG_pCAL_SUPPORTED +#endif +#ifndef PNG_NO_READ_sCAL +# define PNG_READ_sCAL_SUPPORTED +# define PNG_sCAL_SUPPORTED +#endif +#ifndef PNG_NO_READ_pHYs +# define PNG_READ_pHYs_SUPPORTED +# define PNG_pHYs_SUPPORTED +#endif +#ifndef PNG_NO_READ_sBIT +# define PNG_READ_sBIT_SUPPORTED +# define PNG_sBIT_SUPPORTED +#endif +#ifndef PNG_NO_READ_sPLT +# define PNG_READ_sPLT_SUPPORTED +# define PNG_sPLT_SUPPORTED +#endif +#ifndef PNG_NO_READ_sRGB +# define PNG_READ_sRGB_SUPPORTED +# define PNG_sRGB_SUPPORTED +#endif +#ifndef PNG_NO_READ_tEXt +# define PNG_READ_tEXt_SUPPORTED +# define PNG_tEXt_SUPPORTED +#endif +#ifndef PNG_NO_READ_tIME +# define PNG_READ_tIME_SUPPORTED +# define PNG_tIME_SUPPORTED +#endif +#ifndef PNG_NO_READ_tRNS +# define PNG_READ_tRNS_SUPPORTED +# define PNG_tRNS_SUPPORTED +#endif +#ifndef PNG_NO_READ_zTXt +# define PNG_READ_zTXt_SUPPORTED +# define PNG_zTXt_SUPPORTED +#endif +#ifndef PNG_NO_READ_UNKNOWN_CHUNKS +# define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED +# ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_UNKNOWN_CHUNKS_SUPPORTED +# endif +# ifndef PNG_NO_HANDLE_AS_UNKNOWN +# define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# endif +#endif +#if !defined(PNG_NO_READ_USER_CHUNKS) && \ + defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) +# define PNG_READ_USER_CHUNKS_SUPPORTED +# define PNG_USER_CHUNKS_SUPPORTED +# ifdef PNG_NO_READ_UNKNOWN_CHUNKS +# undef PNG_NO_READ_UNKNOWN_CHUNKS +# endif +# ifdef PNG_NO_HANDLE_AS_UNKNOWN +# undef PNG_NO_HANDLE_AS_UNKNOWN +# endif +#endif +#ifndef PNG_NO_READ_OPT_PLTE +# define PNG_READ_OPT_PLTE_SUPPORTED /* only affects support of the */ +#endif /* optional PLTE chunk in RGB and RGBA images */ +#if defined(PNG_READ_iTXt_SUPPORTED) || defined(PNG_READ_tEXt_SUPPORTED) || \ + defined(PNG_READ_zTXt_SUPPORTED) +# define PNG_READ_TEXT_SUPPORTED +# define PNG_TEXT_SUPPORTED +#endif + +#endif /* PNG_READ_ANCILLARY_CHUNKS_SUPPORTED */ + +#ifdef PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED + +#ifdef PNG_NO_WRITE_TEXT +# define PNG_NO_WRITE_iTXt +# define PNG_NO_WRITE_tEXt +# define PNG_NO_WRITE_zTXt +#endif +#ifndef PNG_NO_WRITE_bKGD +# define PNG_WRITE_bKGD_SUPPORTED +# ifndef PNG_bKGD_SUPPORTED +# define PNG_bKGD_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_cHRM +# define PNG_WRITE_cHRM_SUPPORTED +# ifndef PNG_cHRM_SUPPORTED +# define PNG_cHRM_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_gAMA +# define PNG_WRITE_gAMA_SUPPORTED +# ifndef PNG_gAMA_SUPPORTED +# define PNG_gAMA_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_hIST +# define PNG_WRITE_hIST_SUPPORTED +# ifndef PNG_hIST_SUPPORTED +# define PNG_hIST_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_iCCP +# define PNG_WRITE_iCCP_SUPPORTED +# ifndef PNG_iCCP_SUPPORTED +# define PNG_iCCP_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_iTXt +# ifndef PNG_WRITE_iTXt_SUPPORTED +# define PNG_WRITE_iTXt_SUPPORTED +# endif +# ifndef PNG_iTXt_SUPPORTED +# define PNG_iTXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_oFFs +# define PNG_WRITE_oFFs_SUPPORTED +# ifndef PNG_oFFs_SUPPORTED +# define PNG_oFFs_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_pCAL +# define PNG_WRITE_pCAL_SUPPORTED +# ifndef PNG_pCAL_SUPPORTED +# define PNG_pCAL_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sCAL +# define PNG_WRITE_sCAL_SUPPORTED +# ifndef PNG_sCAL_SUPPORTED +# define PNG_sCAL_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_pHYs +# define PNG_WRITE_pHYs_SUPPORTED +# ifndef PNG_pHYs_SUPPORTED +# define PNG_pHYs_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sBIT +# define PNG_WRITE_sBIT_SUPPORTED +# ifndef PNG_sBIT_SUPPORTED +# define PNG_sBIT_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sPLT +# define PNG_WRITE_sPLT_SUPPORTED +# ifndef PNG_sPLT_SUPPORTED +# define PNG_sPLT_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_sRGB +# define PNG_WRITE_sRGB_SUPPORTED +# ifndef PNG_sRGB_SUPPORTED +# define PNG_sRGB_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tEXt +# define PNG_WRITE_tEXt_SUPPORTED +# ifndef PNG_tEXt_SUPPORTED +# define PNG_tEXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tIME +# define PNG_WRITE_tIME_SUPPORTED +# ifndef PNG_tIME_SUPPORTED +# define PNG_tIME_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_tRNS +# define PNG_WRITE_tRNS_SUPPORTED +# ifndef PNG_tRNS_SUPPORTED +# define PNG_tRNS_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_zTXt +# define PNG_WRITE_zTXt_SUPPORTED +# ifndef PNG_zTXt_SUPPORTED +# define PNG_zTXt_SUPPORTED +# endif +#endif +#ifndef PNG_NO_WRITE_UNKNOWN_CHUNKS +# define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED +# ifndef PNG_UNKNOWN_CHUNKS_SUPPORTED +# define PNG_UNKNOWN_CHUNKS_SUPPORTED +# endif +# ifndef PNG_NO_HANDLE_AS_UNKNOWN +# ifndef PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# define PNG_HANDLE_AS_UNKNOWN_SUPPORTED +# endif +# endif +#endif +#if defined(PNG_WRITE_iTXt_SUPPORTED) || defined(PNG_WRITE_tEXt_SUPPORTED) || \ + defined(PNG_WRITE_zTXt_SUPPORTED) +# define PNG_WRITE_TEXT_SUPPORTED +# ifndef PNG_TEXT_SUPPORTED +# define PNG_TEXT_SUPPORTED +# endif +#endif + +#endif /* PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED */ + +/* Turn this off to disable png_read_png() and + * png_write_png() and leave the row_pointers member + * out of the info structure. + */ +#ifndef PNG_NO_INFO_IMAGE +# define PNG_INFO_IMAGE_SUPPORTED +#endif + +/* need the time information for reading tIME chunks */ +#if defined(PNG_tIME_SUPPORTED) +# if !defined(_WIN32_WCE) + /* "time.h" functions are not supported on WindowsCE */ +# include +# endif +#endif + +/* Some typedefs to get us started. These should be safe on most of the + * common platforms. The typedefs should be at least as large as the + * numbers suggest (a png_uint_32 must be at least 32 bits long), but they + * don't have to be exactly that size. Some compilers dislike passing + * unsigned shorts as function parameters, so you may be better off using + * unsigned int for png_uint_16. Likewise, for 64-bit systems, you may + * want to have unsigned int for png_uint_32 instead of unsigned long. + */ + +typedef unsigned long png_uint_32; +typedef long png_int_32; +typedef unsigned short png_uint_16; +typedef short png_int_16; +typedef unsigned char png_byte; + +/* This is usually size_t. It is typedef'ed just in case you need it to + change (I'm not sure if you will or not, so I thought I'd be safe) */ +#ifdef PNG_SIZE_T + typedef PNG_SIZE_T png_size_t; +# define png_sizeof(x) png_convert_size(sizeof (x)) +#else + typedef size_t png_size_t; +# define png_sizeof(x) sizeof (x) +#endif + +/* The following is needed for medium model support. It cannot be in the + * PNG_INTERNAL section. Needs modification for other compilers besides + * MSC. Model independent support declares all arrays and pointers to be + * large using the far keyword. The zlib version used must also support + * model independent data. As of version zlib 1.0.4, the necessary changes + * have been made in zlib. The USE_FAR_KEYWORD define triggers other + * changes that are needed. (Tim Wegner) + */ + +/* Separate compiler dependencies (problem here is that zlib.h always + defines FAR. (SJT) */ +#ifdef __BORLANDC__ +# if defined(__LARGE__) || defined(__HUGE__) || defined(__COMPACT__) +# define LDATA 1 +# else +# define LDATA 0 +# endif + /* GRR: why is Cygwin in here? Cygwin is not Borland C... */ +# if !defined(__WIN32__) && !defined(__FLAT__) && !defined(__CYGWIN__) +# define PNG_MAX_MALLOC_64K +# if (LDATA != 1) +# ifndef FAR +# define FAR __far +# endif +# define USE_FAR_KEYWORD +# endif /* LDATA != 1 */ + /* Possibly useful for moving data out of default segment. + * Uncomment it if you want. Could also define FARDATA as + * const if your compiler supports it. (SJT) +# define FARDATA FAR + */ +# endif /* __WIN32__, __FLAT__, __CYGWIN__ */ +#endif /* __BORLANDC__ */ + + +/* Suggest testing for specific compiler first before testing for + * FAR. The Watcom compiler defines both __MEDIUM__ and M_I86MM, + * making reliance oncertain keywords suspect. (SJT) + */ + +/* MSC Medium model */ +#if defined(FAR) +# if defined(M_I86MM) +# define USE_FAR_KEYWORD +# define FARDATA FAR +# include +# endif +#endif + +/* SJT: default case */ +#ifndef FAR +# define FAR +#endif + +/* At this point FAR is always defined */ +#ifndef FARDATA +# define FARDATA +#endif + +/* Typedef for floating-point numbers that are converted + to fixed-point with a multiple of 100,000, e.g., int_gamma */ +typedef png_int_32 png_fixed_point; + +/* Add typedefs for pointers */ +typedef void FAR * png_voidp; +typedef png_byte FAR * png_bytep; +typedef png_uint_32 FAR * png_uint_32p; +typedef png_int_32 FAR * png_int_32p; +typedef png_uint_16 FAR * png_uint_16p; +typedef png_int_16 FAR * png_int_16p; +typedef PNG_CONST char FAR * png_const_charp; +typedef char FAR * png_charp; +typedef png_fixed_point FAR * png_fixed_point_p; + +#ifndef PNG_NO_STDIO +#if defined(_WIN32_WCE) +typedef HANDLE png_FILE_p; +#else +typedef FILE * png_FILE_p; +#endif +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double FAR * png_doublep; +#endif + +/* Pointers to pointers; i.e. arrays */ +typedef png_byte FAR * FAR * png_bytepp; +typedef png_uint_32 FAR * FAR * png_uint_32pp; +typedef png_int_32 FAR * FAR * png_int_32pp; +typedef png_uint_16 FAR * FAR * png_uint_16pp; +typedef png_int_16 FAR * FAR * png_int_16pp; +typedef PNG_CONST char FAR * FAR * png_const_charpp; +typedef char FAR * FAR * png_charpp; +typedef png_fixed_point FAR * FAR * png_fixed_point_pp; +#ifdef PNG_FLOATING_POINT_SUPPORTED +typedef double FAR * FAR * png_doublepp; +#endif + +/* Pointers to pointers to pointers; i.e., pointer to array */ +typedef char FAR * FAR * FAR * png_charppp; + +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +/* SPC - Is this stuff deprecated? */ +/* It'll be removed as of libpng-1.3.0 - GR-P */ +/* libpng typedefs for types in zlib. If zlib changes + * or another compression library is used, then change these. + * Eliminates need to change all the source files. + */ +typedef charf * png_zcharp; +typedef charf * FAR * png_zcharpp; +typedef z_stream FAR * png_zstreamp; +#endif /* (PNG_1_0_X) || defined(PNG_1_2_X) */ + +/* + * Define PNG_BUILD_DLL if the module being built is a Windows + * LIBPNG DLL. + * + * Define PNG_USE_DLL if you want to *link* to the Windows LIBPNG DLL. + * It is equivalent to Microsoft predefined macro _DLL that is + * automatically defined when you compile using the share + * version of the CRT (C Run-Time library) + * + * The cygwin mods make this behavior a little different: + * Define PNG_BUILD_DLL if you are building a dll for use with cygwin + * Define PNG_STATIC if you are building a static library for use with cygwin, + * -or- if you are building an application that you want to link to the + * static library. + * PNG_USE_DLL is defined by default (no user action needed) unless one of + * the other flags is defined. + */ + +#if !defined(PNG_DLL) && (defined(PNG_BUILD_DLL) || defined(PNG_USE_DLL)) +# define PNG_DLL +#endif +/* If CYGWIN, then disallow GLOBAL ARRAYS unless building a static lib. + * When building a static lib, default to no GLOBAL ARRAYS, but allow + * command-line override + */ +#if defined(__CYGWIN__) +# if !defined(PNG_STATIC) +# if defined(PNG_USE_GLOBAL_ARRAYS) +# undef PNG_USE_GLOBAL_ARRAYS +# endif +# if !defined(PNG_USE_LOCAL_ARRAYS) +# define PNG_USE_LOCAL_ARRAYS +# endif +# else +# if defined(PNG_USE_LOCAL_ARRAYS) || defined(PNG_NO_GLOBAL_ARRAYS) +# if defined(PNG_USE_GLOBAL_ARRAYS) +# undef PNG_USE_GLOBAL_ARRAYS +# endif +# endif +# endif +# if !defined(PNG_USE_LOCAL_ARRAYS) && !defined(PNG_USE_GLOBAL_ARRAYS) +# define PNG_USE_LOCAL_ARRAYS +# endif +#endif + +/* Do not use global arrays (helps with building DLL's) + * They are no longer used in libpng itself, since version 1.0.5c, + * but might be required for some pre-1.0.5c applications. + */ +#if !defined(PNG_USE_LOCAL_ARRAYS) && !defined(PNG_USE_GLOBAL_ARRAYS) +# if defined(PNG_NO_GLOBAL_ARRAYS) || \ + (defined(__GNUC__) && defined(PNG_DLL)) || defined(_MSC_VER) +# define PNG_USE_LOCAL_ARRAYS +# else +# define PNG_USE_GLOBAL_ARRAYS +# endif +#endif + +#if defined(__CYGWIN__) +# undef PNGAPI +# define PNGAPI __cdecl +# undef PNG_IMPEXP +# define PNG_IMPEXP +#endif + +/* If you define PNGAPI, e.g., with compiler option "-DPNGAPI=__stdcall", + * you may get warnings regarding the linkage of png_zalloc and png_zfree. + * Don't ignore those warnings; you must also reset the default calling + * convention in your compiler to match your PNGAPI, and you must build + * zlib and your applications the same way you build libpng. + */ + +#if defined(__MINGW32__) && !defined(PNG_MODULEDEF) +# ifndef PNG_NO_MODULEDEF +# define PNG_NO_MODULEDEF +# endif +#endif + +#if !defined(PNG_IMPEXP) && defined(PNG_BUILD_DLL) && !defined(PNG_NO_MODULEDEF) +# define PNG_IMPEXP +#endif + +#if defined(PNG_DLL) || defined(_DLL) || defined(__DLL__ ) || \ + (( defined(_Windows) || defined(_WINDOWS) || \ + defined(WIN32) || defined(_WIN32) || defined(__WIN32__) )) + +# ifndef PNGAPI +# if defined(__GNUC__) || (defined (_MSC_VER) && (_MSC_VER >= 800)) +# define PNGAPI __cdecl +# else +# define PNGAPI _cdecl +# endif +# endif + +# if !defined(PNG_IMPEXP) && (!defined(PNG_DLL) || \ + 0 /* WINCOMPILER_WITH_NO_SUPPORT_FOR_DECLIMPEXP */) +# define PNG_IMPEXP +# endif + +# if !defined(PNG_IMPEXP) + +# define PNG_EXPORT_TYPE1(type,symbol) PNG_IMPEXP type PNGAPI symbol +# define PNG_EXPORT_TYPE2(type,symbol) type PNG_IMPEXP PNGAPI symbol + + /* Borland/Microsoft */ +# if defined(_MSC_VER) || defined(__BORLANDC__) +# if (_MSC_VER >= 800) || (__BORLANDC__ >= 0x500) +# define PNG_EXPORT PNG_EXPORT_TYPE1 +# else +# define PNG_EXPORT PNG_EXPORT_TYPE2 +# if defined(PNG_BUILD_DLL) +# define PNG_IMPEXP __export +# else +# define PNG_IMPEXP /*__import */ /* doesn't exist AFAIK in + VC++ */ +# endif /* Exists in Borland C++ for + C++ classes (== huge) */ +# endif +# endif + +# if !defined(PNG_IMPEXP) +# if defined(PNG_BUILD_DLL) +# define PNG_IMPEXP __declspec(dllexport) +# else +# define PNG_IMPEXP __declspec(dllimport) +# endif +# endif +# endif /* PNG_IMPEXP */ +#else /* !(DLL || non-cygwin WINDOWS) */ +# if (defined(__IBMC__) || defined(__IBMCPP__)) && defined(__OS2__) +# ifndef PNGAPI +# define PNGAPI _System +# endif +# else +# if 0 /* ... other platforms, with other meanings */ +# endif +# endif +#endif + +#ifndef PNGAPI +# define PNGAPI +#endif +#ifndef PNG_IMPEXP +# define PNG_IMPEXP +#endif + +#ifdef PNG_BUILDSYMS +# ifndef PNG_EXPORT +# define PNG_EXPORT(type,symbol) PNG_FUNCTION_EXPORT symbol END +# endif +# ifdef PNG_USE_GLOBAL_ARRAYS +# ifndef PNG_EXPORT_VAR +# define PNG_EXPORT_VAR(type) PNG_DATA_EXPORT +# endif +# endif +#endif + +#ifndef PNG_EXPORT +# define PNG_EXPORT(type,symbol) PNG_IMPEXP type PNGAPI symbol +#endif + +#ifdef PNG_USE_GLOBAL_ARRAYS +# ifndef PNG_EXPORT_VAR +# define PNG_EXPORT_VAR(type) extern PNG_IMPEXP type +# endif +#endif + +/* User may want to use these so they are not in PNG_INTERNAL. Any library + * functions that are passed far data must be model independent. + */ + +#ifndef PNG_ABORT +# define PNG_ABORT() abort() +#endif + +#ifdef PNG_SETJMP_SUPPORTED +# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) +#else +# define png_jmpbuf(png_ptr) \ + (LIBPNG_WAS_COMPILED_WITH__PNG_SETJMP_NOT_SUPPORTED) +#endif + +#if defined(USE_FAR_KEYWORD) /* memory model independent fns */ +/* use this to make far-to-near assignments */ +# define CHECK 1 +# define NOCHECK 0 +# define CVT_PTR(ptr) (png_far_to_near(png_ptr,ptr,CHECK)) +# define CVT_PTR_NOCHECK(ptr) (png_far_to_near(png_ptr,ptr,NOCHECK)) +# define png_snprintf _fsnprintf /* Added to v 1.2.19 */ +# define png_strlen _fstrlen +# define png_memcmp _fmemcmp /* SJT: added */ +# define png_memcpy _fmemcpy +# define png_memset _fmemset +#else /* use the usual functions */ +# define CVT_PTR(ptr) (ptr) +# define CVT_PTR_NOCHECK(ptr) (ptr) +# ifndef PNG_NO_SNPRINTF +# ifdef _MSC_VER +# define png_snprintf _snprintf /* Added to v 1.2.19 */ +# define png_snprintf2 _snprintf +# define png_snprintf6 _snprintf +# else +# define png_snprintf snprintf /* Added to v 1.2.19 */ +# define png_snprintf2 snprintf +# define png_snprintf6 snprintf +# endif +# else + /* You don't have or don't want to use snprintf(). Caution: Using + * sprintf instead of snprintf exposes your application to accidental + * or malevolent buffer overflows. If you don't have snprintf() + * as a general rule you should provide one (you can get one from + * Portable OpenSSH). */ +# define png_snprintf(s1,n,fmt,x1) sprintf(s1,fmt,x1) +# define png_snprintf2(s1,n,fmt,x1,x2) sprintf(s1,fmt,x1,x2) +# define png_snprintf6(s1,n,fmt,x1,x2,x3,x4,x5,x6) \ + sprintf(s1,fmt,x1,x2,x3,x4,x5,x6) +# endif +# define png_strlen strlen +# define png_memcmp memcmp /* SJT: added */ +# define png_memcpy memcpy +# define png_memset memset +#endif +/* End of memory model independent support */ + +/* Just a little check that someone hasn't tried to define something + * contradictory. + */ +#if (PNG_ZBUF_SIZE > 65536L) && defined(PNG_MAX_MALLOC_64K) +# undef PNG_ZBUF_SIZE +# define PNG_ZBUF_SIZE 65536L +#endif + +/* Added at libpng-1.2.8 */ +#endif /* PNG_VERSION_INFO_ONLY */ + +#endif /* PNGCONF_H */ diff --git a/Engine/lib/lpng/pngerror.c b/Engine/lib/lpng/pngerror.c new file mode 100644 index 000000000..b364fc00a --- /dev/null +++ b/Engine/lib/lpng/pngerror.c @@ -0,0 +1,343 @@ + +/* pngerror.c - stub functions for i/o and memory allocation + * + * Last changed in libpng 1.2.22 [October 13, 2007] + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2007 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file provides a location for all error handling. Users who + * need special error handling are expected to write replacement functions + * and use png_set_error_fn() to use those functions. See the instructions + * at each function. + */ + +#define PNG_INTERNAL +#include "png.h" + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +static void /* PRIVATE */ +png_default_error PNGARG((png_structp png_ptr, + png_const_charp error_message)); +#ifndef PNG_NO_WARNINGS +static void /* PRIVATE */ +png_default_warning PNGARG((png_structp png_ptr, + png_const_charp warning_message)); +#endif /* PNG_NO_WARNINGS */ + +/* This function is called whenever there is a fatal error. This function + * should not be changed. If there is a need to handle errors differently, + * you should supply a replacement error function and use png_set_error_fn() + * to replace the error function at run-time. + */ +#ifndef PNG_NO_ERROR_TEXT +void PNGAPI +png_error(png_structp png_ptr, png_const_charp error_message) +{ +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + char msg[16]; + if (png_ptr != NULL) + { + if (png_ptr->flags& + (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) + { + if (*error_message == '#') + { + int offset; + for (offset=1; offset<15; offset++) + if (*(error_message+offset) == ' ') + break; + if (png_ptr->flags&PNG_FLAG_STRIP_ERROR_TEXT) + { + int i; + for (i=0; iflags&PNG_FLAG_STRIP_ERROR_TEXT) + { + msg[0]='0'; + msg[1]='\0'; + error_message=msg; + } + } + } + } +#endif + if (png_ptr != NULL && png_ptr->error_fn != NULL) + (*(png_ptr->error_fn))(png_ptr, error_message); + + /* If the custom handler doesn't exist, or if it returns, + use the default handler, which will not return. */ + png_default_error(png_ptr, error_message); +} +#else +void PNGAPI +png_err(png_structp png_ptr) +{ + if (png_ptr != NULL && png_ptr->error_fn != NULL) + (*(png_ptr->error_fn))(png_ptr, '\0'); + + /* If the custom handler doesn't exist, or if it returns, + use the default handler, which will not return. */ + png_default_error(png_ptr, '\0'); +} +#endif /* PNG_NO_ERROR_TEXT */ + +#ifndef PNG_NO_WARNINGS +/* This function is called whenever there is a non-fatal error. This function + * should not be changed. If there is a need to handle warnings differently, + * you should supply a replacement warning function and use + * png_set_error_fn() to replace the warning function at run-time. + */ +void PNGAPI +png_warning(png_structp png_ptr, png_const_charp warning_message) +{ + int offset = 0; + if (png_ptr != NULL) + { +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + if (png_ptr->flags& + (PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT)) +#endif + { + if (*warning_message == '#') + { + for (offset=1; offset<15; offset++) + if (*(warning_message+offset) == ' ') + break; + } + } + if (png_ptr != NULL && png_ptr->warning_fn != NULL) + (*(png_ptr->warning_fn))(png_ptr, warning_message+offset); + } + else + png_default_warning(png_ptr, warning_message+offset); +} +#endif /* PNG_NO_WARNINGS */ + + +/* These utilities are used internally to build an error message that relates + * to the current chunk. The chunk name comes from png_ptr->chunk_name, + * this is used to prefix the message. The message is limited in length + * to 63 bytes, the name characters are output as hex digits wrapped in [] + * if the character is invalid. + */ +#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) +static PNG_CONST char png_digit[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F' +}; + +#define PNG_MAX_ERROR_TEXT 64 + +#if !defined(PNG_NO_WARNINGS) || !defined(PNG_NO_ERROR_TEXT) +static void /* PRIVATE */ +png_format_buffer(png_structp png_ptr, png_charp buffer, png_const_charp + error_message) +{ + int iout = 0, iin = 0; + + while (iin < 4) + { + int c = png_ptr->chunk_name[iin++]; + if (isnonalpha(c)) + { + buffer[iout++] = '['; + buffer[iout++] = png_digit[(c & 0xf0) >> 4]; + buffer[iout++] = png_digit[c & 0x0f]; + buffer[iout++] = ']'; + } + else + { + buffer[iout++] = (png_byte)c; + } + } + + if (error_message == NULL) + buffer[iout] = '\0'; + else + { + buffer[iout++] = ':'; + buffer[iout++] = ' '; + png_memcpy(buffer+iout, error_message, PNG_MAX_ERROR_TEXT); + buffer[iout+PNG_MAX_ERROR_TEXT-1] = '\0'; + } +} + +#ifdef PNG_READ_SUPPORTED +void PNGAPI +png_chunk_error(png_structp png_ptr, png_const_charp error_message) +{ + char msg[18+PNG_MAX_ERROR_TEXT]; + if (png_ptr == NULL) + png_error(png_ptr, error_message); + else + { + png_format_buffer(png_ptr, msg, error_message); + png_error(png_ptr, msg); + } +} +#endif /* PNG_READ_SUPPORTED */ +#endif /* !defined(PNG_NO_WARNINGS) || !defined(PNG_NO_ERROR_TEXT) */ + +#ifndef PNG_NO_WARNINGS +void PNGAPI +png_chunk_warning(png_structp png_ptr, png_const_charp warning_message) +{ + char msg[18+PNG_MAX_ERROR_TEXT]; + if (png_ptr == NULL) + png_warning(png_ptr, warning_message); + else + { + png_format_buffer(png_ptr, msg, warning_message); + png_warning(png_ptr, msg); + } +} +#endif /* PNG_NO_WARNINGS */ + + +/* This is the default error handling function. Note that replacements for + * this function MUST NOT RETURN, or the program will likely crash. This + * function is used by default, or if the program supplies NULL for the + * error function pointer in png_set_error_fn(). + */ +static void /* PRIVATE */ +png_default_error(png_structp png_ptr, png_const_charp error_message) +{ +#ifndef PNG_NO_CONSOLE_IO +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + if (*error_message == '#') + { + int offset; + char error_number[16]; + for (offset=0; offset<15; offset++) + { + error_number[offset] = *(error_message+offset+1); + if (*(error_message+offset) == ' ') + break; + } + if((offset > 1) && (offset < 15)) + { + error_number[offset-1]='\0'; + fprintf(stderr, "libpng error no. %s: %s\n", error_number, + error_message+offset); + } + else + fprintf(stderr, "libpng error: %s, offset=%d\n", error_message,offset); + } + else +#endif + fprintf(stderr, "libpng error: %s\n", error_message); +#endif + +#ifdef PNG_SETJMP_SUPPORTED + if (png_ptr) + { +# ifdef USE_FAR_KEYWORD + { + jmp_buf jmpbuf; + png_memcpy(jmpbuf, png_ptr->jmpbuf, png_sizeof(jmp_buf)); + longjmp(jmpbuf, 1); + } +# else + longjmp(png_ptr->jmpbuf, 1); +# endif + } +#else + PNG_ABORT(); +#endif +#ifdef PNG_NO_CONSOLE_IO + error_message = error_message; /* make compiler happy */ +#endif +} + +#ifndef PNG_NO_WARNINGS +/* This function is called when there is a warning, but the library thinks + * it can continue anyway. Replacement functions don't have to do anything + * here if you don't want them to. In the default configuration, png_ptr is + * not used, but it is passed in case it may be useful. + */ +static void /* PRIVATE */ +png_default_warning(png_structp png_ptr, png_const_charp warning_message) +{ +#ifndef PNG_NO_CONSOLE_IO +# ifdef PNG_ERROR_NUMBERS_SUPPORTED + if (*warning_message == '#') + { + int offset; + char warning_number[16]; + for (offset=0; offset<15; offset++) + { + warning_number[offset]=*(warning_message+offset+1); + if (*(warning_message+offset) == ' ') + break; + } + if((offset > 1) && (offset < 15)) + { + warning_number[offset-1]='\0'; + fprintf(stderr, "libpng warning no. %s: %s\n", warning_number, + warning_message+offset); + } + else + fprintf(stderr, "libpng warning: %s\n", warning_message); + } + else +# endif + fprintf(stderr, "libpng warning: %s\n", warning_message); +#else + warning_message = warning_message; /* make compiler happy */ +#endif + png_ptr = png_ptr; /* make compiler happy */ +} +#endif /* PNG_NO_WARNINGS */ + +/* This function is called when the application wants to use another method + * of handling errors and warnings. Note that the error function MUST NOT + * return to the calling routine or serious problems will occur. The return + * method used in the default routine calls longjmp(png_ptr->jmpbuf, 1) + */ +void PNGAPI +png_set_error_fn(png_structp png_ptr, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warning_fn) +{ + if (png_ptr == NULL) + return; + png_ptr->error_ptr = error_ptr; + png_ptr->error_fn = error_fn; + png_ptr->warning_fn = warning_fn; +} + + +/* This function returns a pointer to the error_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy and png_read_destroy are called. + */ +png_voidp PNGAPI +png_get_error_ptr(png_structp png_ptr) +{ + if (png_ptr == NULL) + return NULL; + return ((png_voidp)png_ptr->error_ptr); +} + + +#ifdef PNG_ERROR_NUMBERS_SUPPORTED +void PNGAPI +png_set_strip_error_numbers(png_structp png_ptr, png_uint_32 strip_mode) +{ + if(png_ptr != NULL) + { + png_ptr->flags &= + ((~(PNG_FLAG_STRIP_ERROR_NUMBERS|PNG_FLAG_STRIP_ERROR_TEXT))&strip_mode); + } +} +#endif +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/Engine/lib/lpng/pnggccrd.c b/Engine/lib/lpng/pnggccrd.c new file mode 100644 index 000000000..a7248d6ca --- /dev/null +++ b/Engine/lib/lpng/pnggccrd.c @@ -0,0 +1,101 @@ +/* pnggccrd.c was removed from libpng-1.2.20. */ + +/* This code snippet is for use by configure's compilation test. */ + +#if defined(PNG_ASSEMBLER_CODE_SUPPORTED) && \ + defined(PNG_MMX_CODE_SUPPORTED) +int PNGAPI png_dummy_mmx_support(void); + +static int _mmx_supported = 2; // 0: no MMX; 1: MMX supported; 2: not tested + +int PNGAPI +png_dummy_mmx_support(void) __attribute__((noinline)); + +int PNGAPI +png_dummy_mmx_support(void) +{ + int result; +#if defined(PNG_MMX_CODE_SUPPORTED) // superfluous, but what the heck + __asm__ __volatile__ ( +#if defined(__x86_64__) + "pushq %%rbx \n\t" // rbx gets clobbered by CPUID instruction + "pushq %%rcx \n\t" // so does rcx... + "pushq %%rdx \n\t" // ...and rdx (but rcx & rdx safe on Linux) + "pushfq \n\t" // save Eflag to stack + "popq %%rax \n\t" // get Eflag from stack into rax + "movq %%rax, %%rcx \n\t" // make another copy of Eflag in rcx + "xorl $0x200000, %%eax \n\t" // toggle ID bit in Eflag (i.e., bit 21) + "pushq %%rax \n\t" // save modified Eflag back to stack + "popfq \n\t" // restore modified value to Eflag reg + "pushfq \n\t" // save Eflag to stack + "popq %%rax \n\t" // get Eflag from stack + "pushq %%rcx \n\t" // save original Eflag to stack + "popfq \n\t" // restore original Eflag +#else + "pushl %%ebx \n\t" // ebx gets clobbered by CPUID instruction + "pushl %%ecx \n\t" // so does ecx... + "pushl %%edx \n\t" // ...and edx (but ecx & edx safe on Linux) + "pushfl \n\t" // save Eflag to stack + "popl %%eax \n\t" // get Eflag from stack into eax + "movl %%eax, %%ecx \n\t" // make another copy of Eflag in ecx + "xorl $0x200000, %%eax \n\t" // toggle ID bit in Eflag (i.e., bit 21) + "pushl %%eax \n\t" // save modified Eflag back to stack + "popfl \n\t" // restore modified value to Eflag reg + "pushfl \n\t" // save Eflag to stack + "popl %%eax \n\t" // get Eflag from stack + "pushl %%ecx \n\t" // save original Eflag to stack + "popfl \n\t" // restore original Eflag +#endif + "xorl %%ecx, %%eax \n\t" // compare new Eflag with original Eflag + "jz 0f \n\t" // if same, CPUID instr. is not supported + + "xorl %%eax, %%eax \n\t" // set eax to zero +// ".byte 0x0f, 0xa2 \n\t" // CPUID instruction (two-byte opcode) + "cpuid \n\t" // get the CPU identification info + "cmpl $1, %%eax \n\t" // make sure eax return non-zero value + "jl 0f \n\t" // if eax is zero, MMX is not supported + + "xorl %%eax, %%eax \n\t" // set eax to zero and... + "incl %%eax \n\t" // ...increment eax to 1. This pair is + // faster than the instruction "mov eax, 1" + "cpuid \n\t" // get the CPU identification info again + "andl $0x800000, %%edx \n\t" // mask out all bits but MMX bit (23) + "cmpl $0, %%edx \n\t" // 0 = MMX not supported + "jz 0f \n\t" // non-zero = yes, MMX IS supported + + "movl $1, %%eax \n\t" // set return value to 1 + "jmp 1f \n\t" // DONE: have MMX support + + "0: \n\t" // .NOT_SUPPORTED: target label for jump instructions + "movl $0, %%eax \n\t" // set return value to 0 + "1: \n\t" // .RETURN: target label for jump instructions +#if defined(__x86_64__) + "popq %%rdx \n\t" // restore rdx + "popq %%rcx \n\t" // restore rcx + "popq %%rbx \n\t" // restore rbx +#else + "popl %%edx \n\t" // restore edx + "popl %%ecx \n\t" // restore ecx + "popl %%ebx \n\t" // restore ebx +#endif + +// "ret \n\t" // DONE: no MMX support + // (fall through to standard C "ret") + + : "=a" (result) // output list + + : // any variables used on input (none) + + // no clobber list +// , "%ebx", "%ecx", "%edx" // GRR: we handle these manually +// , "memory" // if write to a variable gcc thought was in a reg +// , "cc" // "condition codes" (flag bits) + ); + _mmx_supported = result; +#else + _mmx_supported = 0; +#endif /* PNG_MMX_CODE_SUPPORTED */ + + return _mmx_supported; +} +#endif diff --git a/Engine/lib/lpng/pngget.c b/Engine/lib/lpng/pngget.c new file mode 100644 index 000000000..a0e90bb6a --- /dev/null +++ b/Engine/lib/lpng/pngget.c @@ -0,0 +1,901 @@ + +/* pngget.c - retrieval of values from info struct + * + * Last changed in libpng 1.2.15 January 5, 2007 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2007 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL +#include "png.h" + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +png_uint_32 PNGAPI +png_get_valid(png_structp png_ptr, png_infop info_ptr, png_uint_32 flag) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->valid & flag); + else + return(0); +} + +png_uint_32 PNGAPI +png_get_rowbytes(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->rowbytes); + else + return(0); +} + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +png_bytepp PNGAPI +png_get_rows(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->row_pointers); + else + return(0); +} +#endif + +#ifdef PNG_EASY_ACCESS_SUPPORTED +/* easy access to info, added in libpng-0.99 */ +png_uint_32 PNGAPI +png_get_image_width(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->width; + } + return (0); +} + +png_uint_32 PNGAPI +png_get_image_height(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->height; + } + return (0); +} + +png_byte PNGAPI +png_get_bit_depth(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->bit_depth; + } + return (0); +} + +png_byte PNGAPI +png_get_color_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->color_type; + } + return (0); +} + +png_byte PNGAPI +png_get_filter_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->filter_type; + } + return (0); +} + +png_byte PNGAPI +png_get_interlace_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->interlace_type; + } + return (0); +} + +png_byte PNGAPI +png_get_compression_type(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + { + return info_ptr->compression_type; + } + return (0); +} + +png_uint_32 PNGAPI +png_get_x_pixels_per_meter(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_pHYs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_x_pixels_per_meter"); + if(info_ptr->phys_unit_type != PNG_RESOLUTION_METER) + return (0); + else return (info_ptr->x_pixels_per_unit); + } +#else + return (0); +#endif + return (0); +} + +png_uint_32 PNGAPI +png_get_y_pixels_per_meter(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_pHYs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_y_pixels_per_meter"); + if(info_ptr->phys_unit_type != PNG_RESOLUTION_METER) + return (0); + else return (info_ptr->y_pixels_per_unit); + } +#else + return (0); +#endif + return (0); +} + +png_uint_32 PNGAPI +png_get_pixels_per_meter(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_pHYs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_pixels_per_meter"); + if(info_ptr->phys_unit_type != PNG_RESOLUTION_METER || + info_ptr->x_pixels_per_unit != info_ptr->y_pixels_per_unit) + return (0); + else return (info_ptr->x_pixels_per_unit); + } +#else + return (0); +#endif + return (0); +} + +#ifdef PNG_FLOATING_POINT_SUPPORTED +float PNGAPI +png_get_pixel_aspect_ratio(png_structp png_ptr, png_infop info_ptr) + { + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_pHYs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pHYs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_aspect_ratio"); + if (info_ptr->x_pixels_per_unit == 0) + return ((float)0.0); + else + return ((float)((float)info_ptr->y_pixels_per_unit + /(float)info_ptr->x_pixels_per_unit)); + } +#else + return (0.0); +#endif + return ((float)0.0); +} +#endif + +png_int_32 PNGAPI +png_get_x_offset_microns(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_oFFs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_x_offset_microns"); + if(info_ptr->offset_unit_type != PNG_OFFSET_MICROMETER) + return (0); + else return (info_ptr->x_offset); + } +#else + return (0); +#endif + return (0); +} + +png_int_32 PNGAPI +png_get_y_offset_microns(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_oFFs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_y_offset_microns"); + if(info_ptr->offset_unit_type != PNG_OFFSET_MICROMETER) + return (0); + else return (info_ptr->y_offset); + } +#else + return (0); +#endif + return (0); +} + +png_int_32 PNGAPI +png_get_x_offset_pixels(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_oFFs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_x_offset_microns"); + if(info_ptr->offset_unit_type != PNG_OFFSET_PIXEL) + return (0); + else return (info_ptr->x_offset); + } +#else + return (0); +#endif + return (0); +} + +png_int_32 PNGAPI +png_get_y_offset_pixels(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) +#if defined(PNG_oFFs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_oFFs) + { + png_debug1(1, "in %s retrieval function\n", "png_get_y_offset_microns"); + if(info_ptr->offset_unit_type != PNG_OFFSET_PIXEL) + return (0); + else return (info_ptr->y_offset); + } +#else + return (0); +#endif + return (0); +} + +#if defined(PNG_INCH_CONVERSIONS) && defined(PNG_FLOATING_POINT_SUPPORTED) +png_uint_32 PNGAPI +png_get_pixels_per_inch(png_structp png_ptr, png_infop info_ptr) +{ + return ((png_uint_32)((float)png_get_pixels_per_meter(png_ptr, info_ptr) + *.0254 +.5)); +} + +png_uint_32 PNGAPI +png_get_x_pixels_per_inch(png_structp png_ptr, png_infop info_ptr) +{ + return ((png_uint_32)((float)png_get_x_pixels_per_meter(png_ptr, info_ptr) + *.0254 +.5)); +} + +png_uint_32 PNGAPI +png_get_y_pixels_per_inch(png_structp png_ptr, png_infop info_ptr) +{ + return ((png_uint_32)((float)png_get_y_pixels_per_meter(png_ptr, info_ptr) + *.0254 +.5)); +} + +float PNGAPI +png_get_x_offset_inches(png_structp png_ptr, png_infop info_ptr) +{ + return ((float)png_get_x_offset_microns(png_ptr, info_ptr) + *.00003937); +} + +float PNGAPI +png_get_y_offset_inches(png_structp png_ptr, png_infop info_ptr) +{ + return ((float)png_get_y_offset_microns(png_ptr, info_ptr) + *.00003937); +} + +#if defined(PNG_pHYs_SUPPORTED) +png_uint_32 PNGAPI +png_get_pHYs_dpi(png_structp png_ptr, png_infop info_ptr, + png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type) +{ + png_uint_32 retval = 0; + + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + { + png_debug1(1, "in %s retrieval function\n", "pHYs"); + if (res_x != NULL) + { + *res_x = info_ptr->x_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + if (res_y != NULL) + { + *res_y = info_ptr->y_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + if (unit_type != NULL) + { + *unit_type = (int)info_ptr->phys_unit_type; + retval |= PNG_INFO_pHYs; + if(*unit_type == 1) + { + if (res_x != NULL) *res_x = (png_uint_32)(*res_x * .0254 + .50); + if (res_y != NULL) *res_y = (png_uint_32)(*res_y * .0254 + .50); + } + } + } + return (retval); +} +#endif /* PNG_pHYs_SUPPORTED */ +#endif /* PNG_INCH_CONVERSIONS && PNG_FLOATING_POINT_SUPPORTED */ + +/* png_get_channels really belongs in here, too, but it's been around longer */ + +#endif /* PNG_EASY_ACCESS_SUPPORTED */ + +png_byte PNGAPI +png_get_channels(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->channels); + else + return (0); +} + +png_bytep PNGAPI +png_get_signature(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr != NULL && info_ptr != NULL) + return(info_ptr->signature); + else + return (NULL); +} + +#if defined(PNG_bKGD_SUPPORTED) +png_uint_32 PNGAPI +png_get_bKGD(png_structp png_ptr, png_infop info_ptr, + png_color_16p *background) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD) + && background != NULL) + { + png_debug1(1, "in %s retrieval function\n", "bKGD"); + *background = &(info_ptr->background); + return (PNG_INFO_bKGD); + } + return (0); +} +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_cHRM(png_structp png_ptr, png_infop info_ptr, + double *white_x, double *white_y, double *red_x, double *red_y, + double *green_x, double *green_y, double *blue_x, double *blue_y) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) + { + png_debug1(1, "in %s retrieval function\n", "cHRM"); + if (white_x != NULL) + *white_x = (double)info_ptr->x_white; + if (white_y != NULL) + *white_y = (double)info_ptr->y_white; + if (red_x != NULL) + *red_x = (double)info_ptr->x_red; + if (red_y != NULL) + *red_y = (double)info_ptr->y_red; + if (green_x != NULL) + *green_x = (double)info_ptr->x_green; + if (green_y != NULL) + *green_y = (double)info_ptr->y_green; + if (blue_x != NULL) + *blue_x = (double)info_ptr->x_blue; + if (blue_y != NULL) + *blue_y = (double)info_ptr->y_blue; + return (PNG_INFO_cHRM); + } + return (0); +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_cHRM_fixed(png_structp png_ptr, png_infop info_ptr, + png_fixed_point *white_x, png_fixed_point *white_y, png_fixed_point *red_x, + png_fixed_point *red_y, png_fixed_point *green_x, png_fixed_point *green_y, + png_fixed_point *blue_x, png_fixed_point *blue_y) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) + { + png_debug1(1, "in %s retrieval function\n", "cHRM"); + if (white_x != NULL) + *white_x = info_ptr->int_x_white; + if (white_y != NULL) + *white_y = info_ptr->int_y_white; + if (red_x != NULL) + *red_x = info_ptr->int_x_red; + if (red_y != NULL) + *red_y = info_ptr->int_y_red; + if (green_x != NULL) + *green_x = info_ptr->int_x_green; + if (green_y != NULL) + *green_y = info_ptr->int_y_green; + if (blue_x != NULL) + *blue_x = info_ptr->int_x_blue; + if (blue_y != NULL) + *blue_y = info_ptr->int_y_blue; + return (PNG_INFO_cHRM); + } + return (0); +} +#endif +#endif + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_gAMA(png_structp png_ptr, png_infop info_ptr, double *file_gamma) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) + && file_gamma != NULL) + { + png_debug1(1, "in %s retrieval function\n", "gAMA"); + *file_gamma = (double)info_ptr->gamma; + return (PNG_INFO_gAMA); + } + return (0); +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_gAMA_fixed(png_structp png_ptr, png_infop info_ptr, + png_fixed_point *int_file_gamma) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) + && int_file_gamma != NULL) + { + png_debug1(1, "in %s retrieval function\n", "gAMA"); + *int_file_gamma = info_ptr->int_gamma; + return (PNG_INFO_gAMA); + } + return (0); +} +#endif +#endif + +#if defined(PNG_sRGB_SUPPORTED) +png_uint_32 PNGAPI +png_get_sRGB(png_structp png_ptr, png_infop info_ptr, int *file_srgb_intent) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB) + && file_srgb_intent != NULL) + { + png_debug1(1, "in %s retrieval function\n", "sRGB"); + *file_srgb_intent = (int)info_ptr->srgb_intent; + return (PNG_INFO_sRGB); + } + return (0); +} +#endif + +#if defined(PNG_iCCP_SUPPORTED) +png_uint_32 PNGAPI +png_get_iCCP(png_structp png_ptr, png_infop info_ptr, + png_charpp name, int *compression_type, + png_charpp profile, png_uint_32 *proflen) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP) + && name != NULL && profile != NULL && proflen != NULL) + { + png_debug1(1, "in %s retrieval function\n", "iCCP"); + *name = info_ptr->iccp_name; + *profile = info_ptr->iccp_profile; + /* compression_type is a dummy so the API won't have to change + if we introduce multiple compression types later. */ + *proflen = (int)info_ptr->iccp_proflen; + *compression_type = (int)info_ptr->iccp_compression; + return (PNG_INFO_iCCP); + } + return (0); +} +#endif + +#if defined(PNG_sPLT_SUPPORTED) +png_uint_32 PNGAPI +png_get_sPLT(png_structp png_ptr, png_infop info_ptr, + png_sPLT_tpp spalettes) +{ + if (png_ptr != NULL && info_ptr != NULL && spalettes != NULL) + { + *spalettes = info_ptr->splt_palettes; + return ((png_uint_32)info_ptr->splt_palettes_num); + } + return (0); +} +#endif + +#if defined(PNG_hIST_SUPPORTED) +png_uint_32 PNGAPI +png_get_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p *hist) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST) + && hist != NULL) + { + png_debug1(1, "in %s retrieval function\n", "hIST"); + *hist = info_ptr->hist; + return (PNG_INFO_hIST); + } + return (0); +} +#endif + +png_uint_32 PNGAPI +png_get_IHDR(png_structp png_ptr, png_infop info_ptr, + png_uint_32 *width, png_uint_32 *height, int *bit_depth, + int *color_type, int *interlace_type, int *compression_type, + int *filter_type) + +{ + if (png_ptr != NULL && info_ptr != NULL && width != NULL && height != NULL && + bit_depth != NULL && color_type != NULL) + { + png_debug1(1, "in %s retrieval function\n", "IHDR"); + *width = info_ptr->width; + *height = info_ptr->height; + *bit_depth = info_ptr->bit_depth; + if (info_ptr->bit_depth < 1 || info_ptr->bit_depth > 16) + png_error(png_ptr, "Invalid bit depth"); + *color_type = info_ptr->color_type; + if (info_ptr->color_type > 6) + png_error(png_ptr, "Invalid color type"); + if (compression_type != NULL) + *compression_type = info_ptr->compression_type; + if (filter_type != NULL) + *filter_type = info_ptr->filter_type; + if (interlace_type != NULL) + *interlace_type = info_ptr->interlace_type; + + /* check for potential overflow of rowbytes */ + if (*width == 0 || *width > PNG_UINT_31_MAX) + png_error(png_ptr, "Invalid image width"); + if (*height == 0 || *height > PNG_UINT_31_MAX) + png_error(png_ptr, "Invalid image height"); + if (info_ptr->width > (PNG_UINT_32_MAX + >> 3) /* 8-byte RGBA pixels */ + - 64 /* bigrowbuf hack */ + - 1 /* filter byte */ + - 7*8 /* rounding of width to multiple of 8 pixels */ + - 8) /* extra max_pixel_depth pad */ + { + png_warning(png_ptr, + "Width too large for libpng to process image data."); + } + return (1); + } + return (0); +} + +#if defined(PNG_oFFs_SUPPORTED) +png_uint_32 PNGAPI +png_get_oFFs(png_structp png_ptr, png_infop info_ptr, + png_int_32 *offset_x, png_int_32 *offset_y, int *unit_type) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs) + && offset_x != NULL && offset_y != NULL && unit_type != NULL) + { + png_debug1(1, "in %s retrieval function\n", "oFFs"); + *offset_x = info_ptr->x_offset; + *offset_y = info_ptr->y_offset; + *unit_type = (int)info_ptr->offset_unit_type; + return (PNG_INFO_oFFs); + } + return (0); +} +#endif + +#if defined(PNG_pCAL_SUPPORTED) +png_uint_32 PNGAPI +png_get_pCAL(png_structp png_ptr, png_infop info_ptr, + png_charp *purpose, png_int_32 *X0, png_int_32 *X1, int *type, int *nparams, + png_charp *units, png_charpp *params) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL) + && purpose != NULL && X0 != NULL && X1 != NULL && type != NULL && + nparams != NULL && units != NULL && params != NULL) + { + png_debug1(1, "in %s retrieval function\n", "pCAL"); + *purpose = info_ptr->pcal_purpose; + *X0 = info_ptr->pcal_X0; + *X1 = info_ptr->pcal_X1; + *type = (int)info_ptr->pcal_type; + *nparams = (int)info_ptr->pcal_nparams; + *units = info_ptr->pcal_units; + *params = info_ptr->pcal_params; + return (PNG_INFO_pCAL); + } + return (0); +} +#endif + +#if defined(PNG_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_sCAL(png_structp png_ptr, png_infop info_ptr, + int *unit, double *width, double *height) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_sCAL)) + { + *unit = info_ptr->scal_unit; + *width = info_ptr->scal_pixel_width; + *height = info_ptr->scal_pixel_height; + return (PNG_INFO_sCAL); + } + return(0); +} +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +png_uint_32 PNGAPI +png_get_sCAL_s(png_structp png_ptr, png_infop info_ptr, + int *unit, png_charpp width, png_charpp height) +{ + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_sCAL)) + { + *unit = info_ptr->scal_unit; + *width = info_ptr->scal_s_width; + *height = info_ptr->scal_s_height; + return (PNG_INFO_sCAL); + } + return(0); +} +#endif +#endif +#endif + +#if defined(PNG_pHYs_SUPPORTED) +png_uint_32 PNGAPI +png_get_pHYs(png_structp png_ptr, png_infop info_ptr, + png_uint_32 *res_x, png_uint_32 *res_y, int *unit_type) +{ + png_uint_32 retval = 0; + + if (png_ptr != NULL && info_ptr != NULL && + (info_ptr->valid & PNG_INFO_pHYs)) + { + png_debug1(1, "in %s retrieval function\n", "pHYs"); + if (res_x != NULL) + { + *res_x = info_ptr->x_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + if (res_y != NULL) + { + *res_y = info_ptr->y_pixels_per_unit; + retval |= PNG_INFO_pHYs; + } + if (unit_type != NULL) + { + *unit_type = (int)info_ptr->phys_unit_type; + retval |= PNG_INFO_pHYs; + } + } + return (retval); +} +#endif + +png_uint_32 PNGAPI +png_get_PLTE(png_structp png_ptr, png_infop info_ptr, png_colorp *palette, + int *num_palette) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_PLTE) + && palette != NULL) + { + png_debug1(1, "in %s retrieval function\n", "PLTE"); + *palette = info_ptr->palette; + *num_palette = info_ptr->num_palette; + png_debug1(3, "num_palette = %d\n", *num_palette); + return (PNG_INFO_PLTE); + } + return (0); +} + +#if defined(PNG_sBIT_SUPPORTED) +png_uint_32 PNGAPI +png_get_sBIT(png_structp png_ptr, png_infop info_ptr, png_color_8p *sig_bit) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT) + && sig_bit != NULL) + { + png_debug1(1, "in %s retrieval function\n", "sBIT"); + *sig_bit = &(info_ptr->sig_bit); + return (PNG_INFO_sBIT); + } + return (0); +} +#endif + +#if defined(PNG_TEXT_SUPPORTED) +png_uint_32 PNGAPI +png_get_text(png_structp png_ptr, png_infop info_ptr, png_textp *text_ptr, + int *num_text) +{ + if (png_ptr != NULL && info_ptr != NULL && info_ptr->num_text > 0) + { + png_debug1(1, "in %s retrieval function\n", + (png_ptr->chunk_name[0] == '\0' ? "text" + : (png_const_charp)png_ptr->chunk_name)); + if (text_ptr != NULL) + *text_ptr = info_ptr->text; + if (num_text != NULL) + *num_text = info_ptr->num_text; + return ((png_uint_32)info_ptr->num_text); + } + if (num_text != NULL) + *num_text = 0; + return(0); +} +#endif + +#if defined(PNG_tIME_SUPPORTED) +png_uint_32 PNGAPI +png_get_tIME(png_structp png_ptr, png_infop info_ptr, png_timep *mod_time) +{ + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME) + && mod_time != NULL) + { + png_debug1(1, "in %s retrieval function\n", "tIME"); + *mod_time = &(info_ptr->mod_time); + return (PNG_INFO_tIME); + } + return (0); +} +#endif + +#if defined(PNG_tRNS_SUPPORTED) +png_uint_32 PNGAPI +png_get_tRNS(png_structp png_ptr, png_infop info_ptr, + png_bytep *trans, int *num_trans, png_color_16p *trans_values) +{ + png_uint_32 retval = 0; + if (png_ptr != NULL && info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) + { + png_debug1(1, "in %s retrieval function\n", "tRNS"); + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (trans != NULL) + { + *trans = info_ptr->trans; + retval |= PNG_INFO_tRNS; + } + if (trans_values != NULL) + *trans_values = &(info_ptr->trans_values); + } + else /* if (info_ptr->color_type != PNG_COLOR_TYPE_PALETTE) */ + { + if (trans_values != NULL) + { + *trans_values = &(info_ptr->trans_values); + retval |= PNG_INFO_tRNS; + } + if(trans != NULL) + *trans = NULL; + } + if(num_trans != NULL) + { + *num_trans = info_ptr->num_trans; + retval |= PNG_INFO_tRNS; + } + } + return (retval); +} +#endif + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +png_uint_32 PNGAPI +png_get_unknown_chunks(png_structp png_ptr, png_infop info_ptr, + png_unknown_chunkpp unknowns) +{ + if (png_ptr != NULL && info_ptr != NULL && unknowns != NULL) + { + *unknowns = info_ptr->unknown_chunks; + return ((png_uint_32)info_ptr->unknown_chunks_num); + } + return (0); +} +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +png_byte PNGAPI +png_get_rgb_to_gray_status (png_structp png_ptr) +{ + return (png_byte)(png_ptr? png_ptr->rgb_to_gray_status : 0); +} +#endif + +#if defined(PNG_USER_CHUNKS_SUPPORTED) +png_voidp PNGAPI +png_get_user_chunk_ptr(png_structp png_ptr) +{ + return (png_ptr? png_ptr->user_chunk_ptr : NULL); +} +#endif + +#ifdef PNG_WRITE_SUPPORTED +png_uint_32 PNGAPI +png_get_compression_buffer_size(png_structp png_ptr) +{ + return (png_uint_32)(png_ptr? png_ptr->zbuf_size : 0L); +} +#endif + +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED +#ifndef PNG_1_0_X +/* this function was added to libpng 1.2.0 and should exist by default */ +png_uint_32 PNGAPI +png_get_asm_flags (png_structp png_ptr) +{ + /* obsolete, to be removed from libpng-1.4.0 */ + return (png_ptr? 0L: 0L); +} + +/* this function was added to libpng 1.2.0 and should exist by default */ +png_uint_32 PNGAPI +png_get_asm_flagmask (int flag_select) +{ + /* obsolete, to be removed from libpng-1.4.0 */ + flag_select=flag_select; + return 0L; +} + + /* GRR: could add this: && defined(PNG_MMX_CODE_SUPPORTED) */ +/* this function was added to libpng 1.2.0 */ +png_uint_32 PNGAPI +png_get_mmx_flagmask (int flag_select, int *compilerID) +{ + /* obsolete, to be removed from libpng-1.4.0 */ + flag_select=flag_select; + *compilerID = -1; /* unknown (i.e., no asm/MMX code compiled) */ + return 0L; +} + +/* this function was added to libpng 1.2.0 */ +png_byte PNGAPI +png_get_mmx_bitdepth_threshold (png_structp png_ptr) +{ + /* obsolete, to be removed from libpng-1.4.0 */ + return (png_ptr? 0: 0); +} + +/* this function was added to libpng 1.2.0 */ +png_uint_32 PNGAPI +png_get_mmx_rowbytes_threshold (png_structp png_ptr) +{ + /* obsolete, to be removed from libpng-1.4.0 */ + return (png_ptr? 0L: 0L); +} +#endif /* ?PNG_1_0_X */ +#endif /* ?PNG_ASSEMBLER_CODE_SUPPORTED */ + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +/* these functions were added to libpng 1.2.6 */ +png_uint_32 PNGAPI +png_get_user_width_max (png_structp png_ptr) +{ + return (png_ptr? png_ptr->user_width_max : 0); +} +png_uint_32 PNGAPI +png_get_user_height_max (png_structp png_ptr) +{ + return (png_ptr? png_ptr->user_height_max : 0); +} +#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */ + + +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/Engine/lib/lpng/pngmem.c b/Engine/lib/lpng/pngmem.c new file mode 100644 index 000000000..248060f38 --- /dev/null +++ b/Engine/lib/lpng/pngmem.c @@ -0,0 +1,608 @@ + +/* pngmem.c - stub functions for memory allocation + * + * Last changed in libpng 1.2.13 November 13, 2006 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2006 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file provides a location for all memory allocation. Users who + * need special memory handling are expected to supply replacement + * functions for png_malloc() and png_free(), and to use + * png_create_read_struct_2() and png_create_write_struct_2() to + * identify the replacement functions. + */ + +#define PNG_INTERNAL +#include "png.h" + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +/* Borland DOS special memory handler */ +#if defined(__TURBOC__) && !defined(_Windows) && !defined(__FLAT__) +/* if you change this, be sure to change the one in png.h also */ + +/* Allocate memory for a png_struct. The malloc and memset can be replaced + by a single call to calloc() if this is thought to improve performance. */ +png_voidp /* PRIVATE */ +png_create_struct(int type) +{ +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_struct_2(type, png_malloc_ptr_NULL, png_voidp_NULL)); +} + +/* Alternate version of png_create_struct, for use with user-defined malloc. */ +png_voidp /* PRIVATE */ +png_create_struct_2(int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + png_size_t size; + png_voidp struct_ptr; + + if (type == PNG_STRUCT_INFO) + size = png_sizeof(png_info); + else if (type == PNG_STRUCT_PNG) + size = png_sizeof(png_struct); + else + return (png_get_copyright(NULL)); + +#ifdef PNG_USER_MEM_SUPPORTED + if(malloc_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + struct_ptr = (*(malloc_fn))(png_ptr, (png_uint_32)size); + } + else +#endif /* PNG_USER_MEM_SUPPORTED */ + struct_ptr = (png_voidp)farmalloc(size); + if (struct_ptr != NULL) + png_memset(struct_ptr, 0, size); + return (struct_ptr); +} + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct(png_voidp struct_ptr) +{ +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2(struct_ptr, png_free_ptr_NULL, png_voidp_NULL); +} + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn, + png_voidp mem_ptr) +{ +#endif + if (struct_ptr != NULL) + { +#ifdef PNG_USER_MEM_SUPPORTED + if(free_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + (*(free_fn))(png_ptr, struct_ptr); + return; + } +#endif /* PNG_USER_MEM_SUPPORTED */ + farfree (struct_ptr); + } +} + +/* Allocate memory. For reasonable files, size should never exceed + * 64K. However, zlib may allocate more then 64K if you don't tell + * it not to. See zconf.h and png.h for more information. zlib does + * need to allocate exactly 64K, so whatever you call here must + * have the ability to do that. + * + * Borland seems to have a problem in DOS mode for exactly 64K. + * It gives you a segment with an offset of 8 (perhaps to store its + * memory stuff). zlib doesn't like this at all, so we have to + * detect and deal with it. This code should not be needed in + * Windows or OS/2 modes, and only in 16 bit mode. This code has + * been updated by Alexander Lehmann for version 0.89 to waste less + * memory. + * + * Note that we can't use png_size_t for the "size" declaration, + * since on some systems a png_size_t is a 16-bit quantity, and as a + * result, we would be truncating potentially larger memory requests + * (which should cause a fatal error) and introducing major problems. + */ + +png_voidp PNGAPI +png_malloc(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; + + if (png_ptr == NULL || size == 0) + return (NULL); + +#ifdef PNG_USER_MEM_SUPPORTED + if(png_ptr->malloc_fn != NULL) + ret = ((png_voidp)(*(png_ptr->malloc_fn))(png_ptr, (png_size_t)size)); + else + ret = (png_malloc_default(png_ptr, size)); + if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of memory!"); + return (ret); +} + +png_voidp PNGAPI +png_malloc_default(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; +#endif /* PNG_USER_MEM_SUPPORTED */ + + if (png_ptr == NULL || size == 0) + return (NULL); + +#ifdef PNG_MAX_MALLOC_64K + if (size > (png_uint_32)65536L) + { + png_warning(png_ptr, "Cannot Allocate > 64K"); + ret = NULL; + } + else +#endif + + if (size != (size_t)size) + ret = NULL; + else if (size == (png_uint_32)65536L) + { + if (png_ptr->offset_table == NULL) + { + /* try to see if we need to do any of this fancy stuff */ + ret = farmalloc(size); + if (ret == NULL || ((png_size_t)ret & 0xffff)) + { + int num_blocks; + png_uint_32 total_size; + png_bytep table; + int i; + png_byte huge * hptr; + + if (ret != NULL) + { + farfree(ret); + ret = NULL; + } + + if(png_ptr->zlib_window_bits > 14) + num_blocks = (int)(1 << (png_ptr->zlib_window_bits - 14)); + else + num_blocks = 1; + if (png_ptr->zlib_mem_level >= 7) + num_blocks += (int)(1 << (png_ptr->zlib_mem_level - 7)); + else + num_blocks++; + + total_size = ((png_uint_32)65536L) * (png_uint_32)num_blocks+16; + + table = farmalloc(total_size); + + if (table == NULL) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out Of Memory."); /* Note "O" and "M" */ + else + png_warning(png_ptr, "Out Of Memory."); +#endif + return (NULL); + } + + if ((png_size_t)table & 0xfff0) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, + "Farmalloc didn't return normalized pointer"); + else + png_warning(png_ptr, + "Farmalloc didn't return normalized pointer"); +#endif + return (NULL); + } + + png_ptr->offset_table = table; + png_ptr->offset_table_ptr = farmalloc(num_blocks * + png_sizeof (png_bytep)); + + if (png_ptr->offset_table_ptr == NULL) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out Of memory."); /* Note "O" and "M" */ + else + png_warning(png_ptr, "Out Of memory."); +#endif + return (NULL); + } + + hptr = (png_byte huge *)table; + if ((png_size_t)hptr & 0xf) + { + hptr = (png_byte huge *)((long)(hptr) & 0xfffffff0L); + hptr = hptr + 16L; /* "hptr += 16L" fails on Turbo C++ 3.0 */ + } + for (i = 0; i < num_blocks; i++) + { + png_ptr->offset_table_ptr[i] = (png_bytep)hptr; + hptr = hptr + (png_uint_32)65536L; /* "+=" fails on TC++3.0 */ + } + + png_ptr->offset_table_number = num_blocks; + png_ptr->offset_table_count = 0; + png_ptr->offset_table_count_free = 0; + } + } + + if (png_ptr->offset_table_count >= png_ptr->offset_table_number) + { +#ifndef PNG_USER_MEM_SUPPORTED + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of Memory."); /* Note "o" and "M" */ + else + png_warning(png_ptr, "Out of Memory."); +#endif + return (NULL); + } + + ret = png_ptr->offset_table_ptr[png_ptr->offset_table_count++]; + } + else + ret = farmalloc(size); + +#ifndef PNG_USER_MEM_SUPPORTED + if (ret == NULL) + { + if ((png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of memory."); /* Note "o" and "m" */ + else + png_warning(png_ptr, "Out of memory."); /* Note "o" and "m" */ + } +#endif + + return (ret); +} + +/* free a pointer allocated by png_malloc(). In the default + configuration, png_ptr is not used, but is passed in case it + is needed. If ptr is NULL, return without taking any action. */ +void PNGAPI +png_free(png_structp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL || ptr == NULL) + return; + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr->free_fn != NULL) + { + (*(png_ptr->free_fn))(png_ptr, ptr); + return; + } + else png_free_default(png_ptr, ptr); +} + +void PNGAPI +png_free_default(png_structp png_ptr, png_voidp ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + + if(png_ptr == NULL) return; + + if (png_ptr->offset_table != NULL) + { + int i; + + for (i = 0; i < png_ptr->offset_table_count; i++) + { + if (ptr == png_ptr->offset_table_ptr[i]) + { + ptr = NULL; + png_ptr->offset_table_count_free++; + break; + } + } + if (png_ptr->offset_table_count_free == png_ptr->offset_table_count) + { + farfree(png_ptr->offset_table); + farfree(png_ptr->offset_table_ptr); + png_ptr->offset_table = NULL; + png_ptr->offset_table_ptr = NULL; + } + } + + if (ptr != NULL) + { + farfree(ptr); + } +} + +#else /* Not the Borland DOS special memory handler */ + +/* Allocate memory for a png_struct or a png_info. The malloc and + memset can be replaced by a single call to calloc() if this is thought + to improve performance noticably. */ +png_voidp /* PRIVATE */ +png_create_struct(int type) +{ +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_struct_2(type, png_malloc_ptr_NULL, png_voidp_NULL)); +} + +/* Allocate memory for a png_struct or a png_info. The malloc and + memset can be replaced by a single call to calloc() if this is thought + to improve performance noticably. */ +png_voidp /* PRIVATE */ +png_create_struct_2(int type, png_malloc_ptr malloc_fn, png_voidp mem_ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + png_size_t size; + png_voidp struct_ptr; + + if (type == PNG_STRUCT_INFO) + size = png_sizeof(png_info); + else if (type == PNG_STRUCT_PNG) + size = png_sizeof(png_struct); + else + return (NULL); + +#ifdef PNG_USER_MEM_SUPPORTED + if(malloc_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + struct_ptr = (*(malloc_fn))(png_ptr, size); + if (struct_ptr != NULL) + png_memset(struct_ptr, 0, size); + return (struct_ptr); + } +#endif /* PNG_USER_MEM_SUPPORTED */ + +#if defined(__TURBOC__) && !defined(__FLAT__) + struct_ptr = (png_voidp)farmalloc(size); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + struct_ptr = (png_voidp)halloc(size,1); +# else + struct_ptr = (png_voidp)malloc(size); +# endif +#endif + if (struct_ptr != NULL) + png_memset(struct_ptr, 0, size); + + return (struct_ptr); +} + + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct(png_voidp struct_ptr) +{ +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2(struct_ptr, png_free_ptr_NULL, png_voidp_NULL); +} + +/* Free memory allocated by a png_create_struct() call */ +void /* PRIVATE */ +png_destroy_struct_2(png_voidp struct_ptr, png_free_ptr free_fn, + png_voidp mem_ptr) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + if (struct_ptr != NULL) + { +#ifdef PNG_USER_MEM_SUPPORTED + if(free_fn != NULL) + { + png_struct dummy_struct; + png_structp png_ptr = &dummy_struct; + png_ptr->mem_ptr=mem_ptr; + (*(free_fn))(png_ptr, struct_ptr); + return; + } +#endif /* PNG_USER_MEM_SUPPORTED */ +#if defined(__TURBOC__) && !defined(__FLAT__) + farfree(struct_ptr); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + hfree(struct_ptr); +# else + free(struct_ptr); +# endif +#endif + } +} + +/* Allocate memory. For reasonable files, size should never exceed + 64K. However, zlib may allocate more then 64K if you don't tell + it not to. See zconf.h and png.h for more information. zlib does + need to allocate exactly 64K, so whatever you call here must + have the ability to do that. */ + +png_voidp PNGAPI +png_malloc(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr == NULL || size == 0) + return (NULL); + + if(png_ptr->malloc_fn != NULL) + ret = ((png_voidp)(*(png_ptr->malloc_fn))(png_ptr, (png_size_t)size)); + else + ret = (png_malloc_default(png_ptr, size)); + if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of Memory!"); + return (ret); +} + +png_voidp PNGAPI +png_malloc_default(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ret; +#endif /* PNG_USER_MEM_SUPPORTED */ + + if (png_ptr == NULL || size == 0) + return (NULL); + +#ifdef PNG_MAX_MALLOC_64K + if (size > (png_uint_32)65536L) + { +#ifndef PNG_USER_MEM_SUPPORTED + if(png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Cannot Allocate > 64K"); + else +#endif + return NULL; + } +#endif + + /* Check for overflow */ +#if defined(__TURBOC__) && !defined(__FLAT__) + if (size != (unsigned long)size) + ret = NULL; + else + ret = farmalloc(size); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + if (size != (unsigned long)size) + ret = NULL; + else + ret = halloc(size, 1); +# else + if (size != (size_t)size) + ret = NULL; + else + ret = malloc((size_t)size); +# endif +#endif + +#ifndef PNG_USER_MEM_SUPPORTED + if (ret == NULL && (png_ptr->flags&PNG_FLAG_MALLOC_NULL_MEM_OK) == 0) + png_error(png_ptr, "Out of Memory"); +#endif + + return (ret); +} + +/* Free a pointer allocated by png_malloc(). If ptr is NULL, return + without taking any action. */ +void PNGAPI +png_free(png_structp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL || ptr == NULL) + return; + +#ifdef PNG_USER_MEM_SUPPORTED + if (png_ptr->free_fn != NULL) + { + (*(png_ptr->free_fn))(png_ptr, ptr); + return; + } + else png_free_default(png_ptr, ptr); +} +void PNGAPI +png_free_default(png_structp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL || ptr == NULL) + return; + +#endif /* PNG_USER_MEM_SUPPORTED */ + +#if defined(__TURBOC__) && !defined(__FLAT__) + farfree(ptr); +#else +# if defined(_MSC_VER) && defined(MAXSEG_64K) + hfree(ptr); +# else + free(ptr); +# endif +#endif +} + +#endif /* Not Borland DOS special memory handler */ + +#if defined(PNG_1_0_X) +# define png_malloc_warn png_malloc +#else +/* This function was added at libpng version 1.2.3. The png_malloc_warn() + * function will set up png_malloc() to issue a png_warning and return NULL + * instead of issuing a png_error, if it fails to allocate the requested + * memory. + */ +png_voidp PNGAPI +png_malloc_warn(png_structp png_ptr, png_uint_32 size) +{ + png_voidp ptr; + png_uint_32 save_flags; + if(png_ptr == NULL) return (NULL); + + save_flags=png_ptr->flags; + png_ptr->flags|=PNG_FLAG_MALLOC_NULL_MEM_OK; + ptr = (png_voidp)png_malloc((png_structp)png_ptr, size); + png_ptr->flags=save_flags; + return(ptr); +} +#endif + +png_voidp PNGAPI +png_memcpy_check (png_structp png_ptr, png_voidp s1, png_voidp s2, + png_uint_32 length) +{ + png_size_t size; + + size = (png_size_t)length; + if ((png_uint_32)size != length) + png_error(png_ptr,"Overflow in png_memcpy_check."); + + return(png_memcpy (s1, s2, size)); +} + +png_voidp PNGAPI +png_memset_check (png_structp png_ptr, png_voidp s1, int value, + png_uint_32 length) +{ + png_size_t size; + + size = (png_size_t)length; + if ((png_uint_32)size != length) + png_error(png_ptr,"Overflow in png_memset_check."); + + return (png_memset (s1, value, size)); + +} + +#ifdef PNG_USER_MEM_SUPPORTED +/* This function is called when the application wants to use another method + * of allocating and freeing memory. + */ +void PNGAPI +png_set_mem_fn(png_structp png_ptr, png_voidp mem_ptr, png_malloc_ptr + malloc_fn, png_free_ptr free_fn) +{ + if(png_ptr != NULL) { + png_ptr->mem_ptr = mem_ptr; + png_ptr->malloc_fn = malloc_fn; + png_ptr->free_fn = free_fn; + } +} + +/* This function returns a pointer to the mem_ptr associated with the user + * functions. The application should free any memory associated with this + * pointer before png_write_destroy and png_read_destroy are called. + */ +png_voidp PNGAPI +png_get_mem_ptr(png_structp png_ptr) +{ + if(png_ptr == NULL) return (NULL); + return ((png_voidp)png_ptr->mem_ptr); +} +#endif /* PNG_USER_MEM_SUPPORTED */ +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/Engine/lib/lpng/pngnow.png b/Engine/lib/lpng/pngnow.png new file mode 100644 index 000000000..82793ebdd Binary files /dev/null and b/Engine/lib/lpng/pngnow.png differ diff --git a/Engine/lib/lpng/pngpread.c b/Engine/lib/lpng/pngpread.c new file mode 100644 index 000000000..8f4b7d179 --- /dev/null +++ b/Engine/lib/lpng/pngpread.c @@ -0,0 +1,1586 @@ + +/* pngpread.c - read a png file in push mode + * + * Last changed in libpng 1.2.23 [November 6, 2007] + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2007 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL +#include "png.h" + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + +/* push model modes */ +#define PNG_READ_SIG_MODE 0 +#define PNG_READ_CHUNK_MODE 1 +#define PNG_READ_IDAT_MODE 2 +#define PNG_SKIP_MODE 3 +#define PNG_READ_tEXt_MODE 4 +#define PNG_READ_zTXt_MODE 5 +#define PNG_READ_DONE_MODE 6 +#define PNG_READ_iTXt_MODE 7 +#define PNG_ERROR_MODE 8 + +void PNGAPI +png_process_data(png_structp png_ptr, png_infop info_ptr, + png_bytep buffer, png_size_t buffer_size) +{ + if(png_ptr == NULL) return; + png_push_restore_buffer(png_ptr, buffer, buffer_size); + + while (png_ptr->buffer_size) + { + png_process_some_data(png_ptr, info_ptr); + } +} + +/* What we do with the incoming data depends on what we were previously + * doing before we ran out of data... + */ +void /* PRIVATE */ +png_process_some_data(png_structp png_ptr, png_infop info_ptr) +{ + if(png_ptr == NULL) return; + switch (png_ptr->process_mode) + { + case PNG_READ_SIG_MODE: + { + png_push_read_sig(png_ptr, info_ptr); + break; + } + case PNG_READ_CHUNK_MODE: + { + png_push_read_chunk(png_ptr, info_ptr); + break; + } + case PNG_READ_IDAT_MODE: + { + png_push_read_IDAT(png_ptr); + break; + } +#if defined(PNG_READ_tEXt_SUPPORTED) + case PNG_READ_tEXt_MODE: + { + png_push_read_tEXt(png_ptr, info_ptr); + break; + } +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + case PNG_READ_zTXt_MODE: + { + png_push_read_zTXt(png_ptr, info_ptr); + break; + } +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + case PNG_READ_iTXt_MODE: + { + png_push_read_iTXt(png_ptr, info_ptr); + break; + } +#endif + case PNG_SKIP_MODE: + { + png_push_crc_finish(png_ptr); + break; + } + default: + { + png_ptr->buffer_size = 0; + break; + } + } +} + +/* Read any remaining signature bytes from the stream and compare them with + * the correct PNG signature. It is possible that this routine is called + * with bytes already read from the signature, either because they have been + * checked by the calling application, or because of multiple calls to this + * routine. + */ +void /* PRIVATE */ +png_push_read_sig(png_structp png_ptr, png_infop info_ptr) +{ + png_size_t num_checked = png_ptr->sig_bytes, + num_to_check = 8 - num_checked; + + if (png_ptr->buffer_size < num_to_check) + { + num_to_check = png_ptr->buffer_size; + } + + png_push_fill_buffer(png_ptr, &(info_ptr->signature[num_checked]), + num_to_check); + png_ptr->sig_bytes = (png_byte)(png_ptr->sig_bytes+num_to_check); + + if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check)) + { + if (num_checked < 4 && + png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4)) + png_error(png_ptr, "Not a PNG file"); + else + png_error(png_ptr, "PNG file corrupted by ASCII conversion"); + } + else + { + if (png_ptr->sig_bytes >= 8) + { + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + } + } +} + +void /* PRIVATE */ +png_push_read_chunk(png_structp png_ptr, png_infop info_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_CONST PNG_IHDR; + PNG_CONST PNG_IDAT; + PNG_CONST PNG_IEND; + PNG_CONST PNG_PLTE; +#if defined(PNG_READ_bKGD_SUPPORTED) + PNG_CONST PNG_bKGD; +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + PNG_CONST PNG_cHRM; +#endif +#if defined(PNG_READ_gAMA_SUPPORTED) + PNG_CONST PNG_gAMA; +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + PNG_CONST PNG_hIST; +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + PNG_CONST PNG_iCCP; +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + PNG_CONST PNG_iTXt; +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + PNG_CONST PNG_oFFs; +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + PNG_CONST PNG_pCAL; +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + PNG_CONST PNG_pHYs; +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + PNG_CONST PNG_sBIT; +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + PNG_CONST PNG_sCAL; +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + PNG_CONST PNG_sRGB; +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + PNG_CONST PNG_sPLT; +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + PNG_CONST PNG_tEXt; +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + PNG_CONST PNG_tIME; +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + PNG_CONST PNG_tRNS; +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + PNG_CONST PNG_zTXt; +#endif +#endif /* PNG_USE_LOCAL_ARRAYS */ + /* First we make sure we have enough data for the 4 byte chunk name + * and the 4 byte chunk length before proceeding with decoding the + * chunk data. To fully decode each of these chunks, we also make + * sure we have enough data in the buffer for the 4 byte CRC at the + * end of every chunk (except IDAT, which is handled separately). + */ + if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER)) + { + png_byte chunk_length[4]; + + if (png_ptr->buffer_size < 8) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_fill_buffer(png_ptr, chunk_length, 4); + png_ptr->push_length = png_get_uint_31(png_ptr,chunk_length); + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; + } + + if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + if(png_ptr->mode & PNG_AFTER_IDAT) + png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT; + + if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_IHDR(png_ptr, info_ptr, png_ptr->push_length); + } + else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_IEND(png_ptr, info_ptr, png_ptr->push_length); + + png_ptr->process_mode = PNG_READ_DONE_MODE; + png_push_have_end(png_ptr, info_ptr); + } +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + png_ptr->mode |= PNG_HAVE_IDAT; + png_handle_unknown(png_ptr, info_ptr, png_ptr->push_length); + if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_ptr->mode |= PNG_HAVE_PLTE; + else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + } + } +#endif + else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_PLTE(png_ptr, info_ptr, png_ptr->push_length); + } + else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + /* If we reach an IDAT chunk, this means we have read all of the + * header chunks, and we can start reading the image (or if this + * is called after the image has been read - we have an error). + */ + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + { + if (!(png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT)) + if (png_ptr->push_length == 0) + return; + + if (png_ptr->mode & PNG_AFTER_IDAT) + png_error(png_ptr, "Too many IDAT's found"); + } + + png_ptr->idat_size = png_ptr->push_length; + png_ptr->mode |= PNG_HAVE_IDAT; + png_ptr->process_mode = PNG_READ_IDAT_MODE; + png_push_have_info(png_ptr, info_ptr); + png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes; + png_ptr->zstream.next_out = png_ptr->row_buf; + return; + } +#if defined(PNG_READ_gAMA_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_gAMA(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_sBIT(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_cHRM(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_sRGB(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_iCCP(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_sPLT(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_tRNS(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_bKGD_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_bKGD(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_hIST(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_pHYs(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_oFFs(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_pCAL(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_sCAL(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_handle_tIME(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_push_handle_tEXt(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_push_handle_zTXt(png_ptr, info_ptr, png_ptr->push_length); + } +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iTXt, 4)) + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_push_handle_iTXt(png_ptr, info_ptr, png_ptr->push_length); + } +#endif + else + { + if (png_ptr->push_length + 4 > png_ptr->buffer_size) + { + png_push_save_buffer(png_ptr); + return; + } + png_push_handle_unknown(png_ptr, info_ptr, png_ptr->push_length); + } + + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; +} + +void /* PRIVATE */ +png_push_crc_skip(png_structp png_ptr, png_uint_32 skip) +{ + png_ptr->process_mode = PNG_SKIP_MODE; + png_ptr->skip_length = skip; +} + +void /* PRIVATE */ +png_push_crc_finish(png_structp png_ptr) +{ + if (png_ptr->skip_length && png_ptr->save_buffer_size) + { + png_size_t save_size; + + if (png_ptr->skip_length < (png_uint_32)png_ptr->save_buffer_size) + save_size = (png_size_t)png_ptr->skip_length; + else + save_size = png_ptr->save_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size); + + png_ptr->skip_length -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->save_buffer_size -= save_size; + png_ptr->save_buffer_ptr += save_size; + } + if (png_ptr->skip_length && png_ptr->current_buffer_size) + { + png_size_t save_size; + + if (png_ptr->skip_length < (png_uint_32)png_ptr->current_buffer_size) + save_size = (png_size_t)png_ptr->skip_length; + else + save_size = png_ptr->current_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size); + + png_ptr->skip_length -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->current_buffer_size -= save_size; + png_ptr->current_buffer_ptr += save_size; + } + if (!png_ptr->skip_length) + { + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_crc_finish(png_ptr, 0); + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + } +} + +void PNGAPI +png_push_fill_buffer(png_structp png_ptr, png_bytep buffer, png_size_t length) +{ + png_bytep ptr; + + if(png_ptr == NULL) return; + ptr = buffer; + if (png_ptr->save_buffer_size) + { + png_size_t save_size; + + if (length < png_ptr->save_buffer_size) + save_size = length; + else + save_size = png_ptr->save_buffer_size; + + png_memcpy(ptr, png_ptr->save_buffer_ptr, save_size); + length -= save_size; + ptr += save_size; + png_ptr->buffer_size -= save_size; + png_ptr->save_buffer_size -= save_size; + png_ptr->save_buffer_ptr += save_size; + } + if (length && png_ptr->current_buffer_size) + { + png_size_t save_size; + + if (length < png_ptr->current_buffer_size) + save_size = length; + else + save_size = png_ptr->current_buffer_size; + + png_memcpy(ptr, png_ptr->current_buffer_ptr, save_size); + png_ptr->buffer_size -= save_size; + png_ptr->current_buffer_size -= save_size; + png_ptr->current_buffer_ptr += save_size; + } +} + +void /* PRIVATE */ +png_push_save_buffer(png_structp png_ptr) +{ + if (png_ptr->save_buffer_size) + { + if (png_ptr->save_buffer_ptr != png_ptr->save_buffer) + { + png_size_t i,istop; + png_bytep sp; + png_bytep dp; + + istop = png_ptr->save_buffer_size; + for (i = 0, sp = png_ptr->save_buffer_ptr, dp = png_ptr->save_buffer; + i < istop; i++, sp++, dp++) + { + *dp = *sp; + } + } + } + if (png_ptr->save_buffer_size + png_ptr->current_buffer_size > + png_ptr->save_buffer_max) + { + png_size_t new_max; + png_bytep old_buffer; + + if (png_ptr->save_buffer_size > PNG_SIZE_MAX - + (png_ptr->current_buffer_size + 256)) + { + png_error(png_ptr, "Potential overflow of save_buffer"); + } + new_max = png_ptr->save_buffer_size + png_ptr->current_buffer_size + 256; + old_buffer = png_ptr->save_buffer; + png_ptr->save_buffer = (png_bytep)png_malloc(png_ptr, + (png_uint_32)new_max); + png_memcpy(png_ptr->save_buffer, old_buffer, png_ptr->save_buffer_size); + png_free(png_ptr, old_buffer); + png_ptr->save_buffer_max = new_max; + } + if (png_ptr->current_buffer_size) + { + png_memcpy(png_ptr->save_buffer + png_ptr->save_buffer_size, + png_ptr->current_buffer_ptr, png_ptr->current_buffer_size); + png_ptr->save_buffer_size += png_ptr->current_buffer_size; + png_ptr->current_buffer_size = 0; + } + png_ptr->save_buffer_ptr = png_ptr->save_buffer; + png_ptr->buffer_size = 0; +} + +void /* PRIVATE */ +png_push_restore_buffer(png_structp png_ptr, png_bytep buffer, + png_size_t buffer_length) +{ + png_ptr->current_buffer = buffer; + png_ptr->current_buffer_size = buffer_length; + png_ptr->buffer_size = buffer_length + png_ptr->save_buffer_size; + png_ptr->current_buffer_ptr = png_ptr->current_buffer; +} + +void /* PRIVATE */ +png_push_read_IDAT(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_CONST PNG_IDAT; +#endif + if (!(png_ptr->mode & PNG_HAVE_CHUNK_HEADER)) + { + png_byte chunk_length[4]; + + if (png_ptr->buffer_size < 8) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_fill_buffer(png_ptr, chunk_length, 4); + png_ptr->push_length = png_get_uint_31(png_ptr,chunk_length); + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + png_ptr->mode |= PNG_HAVE_CHUNK_HEADER; + + if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + png_ptr->process_mode = PNG_READ_CHUNK_MODE; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + png_error(png_ptr, "Not enough compressed data"); + return; + } + + png_ptr->idat_size = png_ptr->push_length; + } + if (png_ptr->idat_size && png_ptr->save_buffer_size) + { + png_size_t save_size; + + if (png_ptr->idat_size < (png_uint_32)png_ptr->save_buffer_size) + { + save_size = (png_size_t)png_ptr->idat_size; + /* check for overflow */ + if((png_uint_32)save_size != png_ptr->idat_size) + png_error(png_ptr, "save_size overflowed in pngpread"); + } + else + save_size = png_ptr->save_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->save_buffer_ptr, save_size); + if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + png_process_IDAT_data(png_ptr, png_ptr->save_buffer_ptr, save_size); + png_ptr->idat_size -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->save_buffer_size -= save_size; + png_ptr->save_buffer_ptr += save_size; + } + if (png_ptr->idat_size && png_ptr->current_buffer_size) + { + png_size_t save_size; + + if (png_ptr->idat_size < (png_uint_32)png_ptr->current_buffer_size) + { + save_size = (png_size_t)png_ptr->idat_size; + /* check for overflow */ + if((png_uint_32)save_size != png_ptr->idat_size) + png_error(png_ptr, "save_size overflowed in pngpread"); + } + else + save_size = png_ptr->current_buffer_size; + + png_calculate_crc(png_ptr, png_ptr->current_buffer_ptr, save_size); + if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + png_process_IDAT_data(png_ptr, png_ptr->current_buffer_ptr, save_size); + + png_ptr->idat_size -= save_size; + png_ptr->buffer_size -= save_size; + png_ptr->current_buffer_size -= save_size; + png_ptr->current_buffer_ptr += save_size; + } + if (!png_ptr->idat_size) + { + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_crc_finish(png_ptr, 0); + png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER; + png_ptr->mode |= PNG_AFTER_IDAT; + } +} + +void /* PRIVATE */ +png_process_IDAT_data(png_structp png_ptr, png_bytep buffer, + png_size_t buffer_length) +{ + int ret; + + if ((png_ptr->flags & PNG_FLAG_ZLIB_FINISHED) && buffer_length) + png_error(png_ptr, "Extra compression data"); + + png_ptr->zstream.next_in = buffer; + png_ptr->zstream.avail_in = (uInt)buffer_length; + for(;;) + { + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret != Z_OK) + { + if (ret == Z_STREAM_END) + { + if (png_ptr->zstream.avail_in) + png_error(png_ptr, "Extra compressed data"); + if (!(png_ptr->zstream.avail_out)) + { + png_push_process_row(png_ptr); + } + + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + else if (ret == Z_BUF_ERROR) + break; + else + png_error(png_ptr, "Decompression Error"); + } + if (!(png_ptr->zstream.avail_out)) + { + if (( +#if defined(PNG_READ_INTERLACING_SUPPORTED) + png_ptr->interlaced && png_ptr->pass > 6) || + (!png_ptr->interlaced && +#endif + png_ptr->row_number == png_ptr->num_rows)) + { + if (png_ptr->zstream.avail_in) + png_warning(png_ptr, "Too much data in IDAT chunks"); + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + png_push_process_row(png_ptr); + png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes; + png_ptr->zstream.next_out = png_ptr->row_buf; + } + else + break; + } +} + +void /* PRIVATE */ +png_push_process_row(png_structp png_ptr) +{ + png_ptr->row_info.color_type = png_ptr->color_type; + png_ptr->row_info.width = png_ptr->iwidth; + png_ptr->row_info.channels = png_ptr->channels; + png_ptr->row_info.bit_depth = png_ptr->bit_depth; + png_ptr->row_info.pixel_depth = png_ptr->pixel_depth; + + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + + png_read_filter_row(png_ptr, &(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->prev_row + 1, + (int)(png_ptr->row_buf[0])); + + png_memcpy_check(png_ptr, png_ptr->prev_row, png_ptr->row_buf, + png_ptr->rowbytes + 1); + + if (png_ptr->transformations || (png_ptr->flags&PNG_FLAG_STRIP_ALPHA)) + png_do_read_transformations(png_ptr); + +#if defined(PNG_READ_INTERLACING_SUPPORTED) + /* blow up interlaced rows to full size */ + if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) + { + if (png_ptr->pass < 6) +/* old interface (pre-1.0.9): + png_do_read_interlace(&(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->pass, png_ptr->transformations); + */ + png_do_read_interlace(png_ptr); + + switch (png_ptr->pass) + { + case 0: + { + int i; + for (i = 0; i < 8 && png_ptr->pass == 0; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); /* updates png_ptr->pass */ + } + if (png_ptr->pass == 2) /* pass 1 might be empty */ + { + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + if (png_ptr->pass == 4 && png_ptr->height <= 4) + { + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + if (png_ptr->pass == 6 && png_ptr->height <= 4) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + break; + } + case 1: + { + int i; + for (i = 0; i < 8 && png_ptr->pass == 1; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + if (png_ptr->pass == 2) /* skip top 4 generated rows */ + { + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + break; + } + case 2: + { + int i; + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + for (i = 0; i < 4 && png_ptr->pass == 2; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + if (png_ptr->pass == 4) /* pass 3 might be empty */ + { + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + break; + } + case 3: + { + int i; + for (i = 0; i < 4 && png_ptr->pass == 3; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + if (png_ptr->pass == 4) /* skip top two generated rows */ + { + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + break; + } + case 4: + { + int i; + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + for (i = 0; i < 2 && png_ptr->pass == 4; i++) + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + if (png_ptr->pass == 6) /* pass 5 might be empty */ + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + break; + } + case 5: + { + int i; + for (i = 0; i < 2 && png_ptr->pass == 5; i++) + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } + if (png_ptr->pass == 6) /* skip top generated row */ + { + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + break; + } + case 6: + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + if (png_ptr->pass != 6) + break; + png_push_have_row(png_ptr, png_bytep_NULL); + png_read_push_finish_row(png_ptr); + } + } + } + else +#endif + { + png_push_have_row(png_ptr, png_ptr->row_buf + 1); + png_read_push_finish_row(png_ptr); + } +} + +void /* PRIVATE */ +png_read_push_finish_row(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + PNG_CONST int FARDATA png_pass_start[] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + PNG_CONST int FARDATA png_pass_inc[] = {8, 8, 4, 4, 2, 2, 1}; + + /* start of interlace block in the y direction */ + PNG_CONST int FARDATA png_pass_ystart[] = {0, 0, 4, 0, 2, 0, 1}; + + /* offset to next interlace block in the y direction */ + PNG_CONST int FARDATA png_pass_yinc[] = {8, 8, 8, 4, 4, 2, 2}; + + /* Height of interlace block. This is not currently used - if you need + * it, uncomment it here and in png.h + PNG_CONST int FARDATA png_pass_height[] = {8, 8, 4, 4, 2, 2, 1}; + */ +#endif + + png_ptr->row_number++; + if (png_ptr->row_number < png_ptr->num_rows) + return; + + if (png_ptr->interlaced) + { + png_ptr->row_number = 0; + png_memset_check(png_ptr, png_ptr->prev_row, 0, + png_ptr->rowbytes + 1); + do + { + png_ptr->pass++; + if ((png_ptr->pass == 1 && png_ptr->width < 5) || + (png_ptr->pass == 3 && png_ptr->width < 3) || + (png_ptr->pass == 5 && png_ptr->width < 2)) + png_ptr->pass++; + + if (png_ptr->pass > 7) + png_ptr->pass--; + if (png_ptr->pass >= 7) + break; + + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + + png_ptr->irowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, + png_ptr->iwidth) + 1; + + if (png_ptr->transformations & PNG_INTERLACE) + break; + + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + + } while (png_ptr->iwidth == 0 || png_ptr->num_rows == 0); + } +} + +#if defined(PNG_READ_tEXt_SUPPORTED) +void /* PRIVATE */ +png_push_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 + length) +{ + if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND)) + { + png_error(png_ptr, "Out of place tEXt"); + info_ptr = info_ptr; /* to quiet some compiler warnings */ + } + +#ifdef PNG_MAX_MALLOC_64K + png_ptr->skip_length = 0; /* This may not be necessary */ + + if (length > (png_uint_32)65535L) /* Can't hold entire string in memory */ + { + png_warning(png_ptr, "tEXt chunk too large to fit in memory"); + png_ptr->skip_length = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + png_ptr->current_text = (png_charp)png_malloc(png_ptr, + (png_uint_32)(length+1)); + png_ptr->current_text[length] = '\0'; + png_ptr->current_text_ptr = png_ptr->current_text; + png_ptr->current_text_size = (png_size_t)length; + png_ptr->current_text_left = (png_size_t)length; + png_ptr->process_mode = PNG_READ_tEXt_MODE; +} + +void /* PRIVATE */ +png_push_read_tEXt(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr->buffer_size && png_ptr->current_text_left) + { + png_size_t text_size; + + if (png_ptr->buffer_size < png_ptr->current_text_left) + text_size = png_ptr->buffer_size; + else + text_size = png_ptr->current_text_left; + png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size); + png_ptr->current_text_left -= text_size; + png_ptr->current_text_ptr += text_size; + } + if (!(png_ptr->current_text_left)) + { + png_textp text_ptr; + png_charp text; + png_charp key; + int ret; + + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_crc_finish(png_ptr); + +#if defined(PNG_MAX_MALLOC_64K) + if (png_ptr->skip_length) + return; +#endif + + key = png_ptr->current_text; + + for (text = key; *text; text++) + /* empty loop */ ; + + if (text < key + png_ptr->current_text_size) + text++; + + text_ptr = (png_textp)png_malloc(png_ptr, + (png_uint_32)png_sizeof(png_text)); + text_ptr->compression = PNG_TEXT_COMPRESSION_NONE; + text_ptr->key = key; +#ifdef PNG_iTXt_SUPPORTED + text_ptr->lang = NULL; + text_ptr->lang_key = NULL; +#endif + text_ptr->text = text; + + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, key); + png_free(png_ptr, text_ptr); + png_ptr->current_text = NULL; + + if (ret) + png_warning(png_ptr, "Insufficient memory to store text chunk."); + } +} +#endif + +#if defined(PNG_READ_zTXt_SUPPORTED) +void /* PRIVATE */ +png_push_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 + length) +{ + if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND)) + { + png_error(png_ptr, "Out of place zTXt"); + info_ptr = info_ptr; /* to quiet some compiler warnings */ + } + +#ifdef PNG_MAX_MALLOC_64K + /* We can't handle zTXt chunks > 64K, since we don't have enough space + * to be able to store the uncompressed data. Actually, the threshold + * is probably around 32K, but it isn't as definite as 64K is. + */ + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "zTXt chunk too large to fit in memory"); + png_push_crc_skip(png_ptr, length); + return; + } +#endif + + png_ptr->current_text = (png_charp)png_malloc(png_ptr, + (png_uint_32)(length+1)); + png_ptr->current_text[length] = '\0'; + png_ptr->current_text_ptr = png_ptr->current_text; + png_ptr->current_text_size = (png_size_t)length; + png_ptr->current_text_left = (png_size_t)length; + png_ptr->process_mode = PNG_READ_zTXt_MODE; +} + +void /* PRIVATE */ +png_push_read_zTXt(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr->buffer_size && png_ptr->current_text_left) + { + png_size_t text_size; + + if (png_ptr->buffer_size < (png_uint_32)png_ptr->current_text_left) + text_size = png_ptr->buffer_size; + else + text_size = png_ptr->current_text_left; + png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size); + png_ptr->current_text_left -= text_size; + png_ptr->current_text_ptr += text_size; + } + if (!(png_ptr->current_text_left)) + { + png_textp text_ptr; + png_charp text; + png_charp key; + int ret; + png_size_t text_size, key_size; + + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_crc_finish(png_ptr); + + key = png_ptr->current_text; + + for (text = key; *text; text++) + /* empty loop */ ; + + /* zTXt can't have zero text */ + if (text >= key + png_ptr->current_text_size) + { + png_ptr->current_text = NULL; + png_free(png_ptr, key); + return; + } + + text++; + + if (*text != PNG_TEXT_COMPRESSION_zTXt) /* check compression byte */ + { + png_ptr->current_text = NULL; + png_free(png_ptr, key); + return; + } + + text++; + + png_ptr->zstream.next_in = (png_bytep )text; + png_ptr->zstream.avail_in = (uInt)(png_ptr->current_text_size - + (text - key)); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + + key_size = text - key; + text_size = 0; + text = NULL; + ret = Z_STREAM_END; + + while (png_ptr->zstream.avail_in) + { + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret != Z_OK && ret != Z_STREAM_END) + { + inflateReset(&png_ptr->zstream); + png_ptr->zstream.avail_in = 0; + png_ptr->current_text = NULL; + png_free(png_ptr, key); + png_free(png_ptr, text); + return; + } + if (!(png_ptr->zstream.avail_out) || ret == Z_STREAM_END) + { + if (text == NULL) + { + text = (png_charp)png_malloc(png_ptr, + (png_uint_32)(png_ptr->zbuf_size - png_ptr->zstream.avail_out + + key_size + 1)); + png_memcpy(text + key_size, png_ptr->zbuf, + png_ptr->zbuf_size - png_ptr->zstream.avail_out); + png_memcpy(text, key, key_size); + text_size = key_size + png_ptr->zbuf_size - + png_ptr->zstream.avail_out; + *(text + text_size) = '\0'; + } + else + { + png_charp tmp; + + tmp = text; + text = (png_charp)png_malloc(png_ptr, text_size + + (png_uint_32)(png_ptr->zbuf_size - png_ptr->zstream.avail_out + + 1)); + png_memcpy(text, tmp, text_size); + png_free(png_ptr, tmp); + png_memcpy(text + text_size, png_ptr->zbuf, + png_ptr->zbuf_size - png_ptr->zstream.avail_out); + text_size += png_ptr->zbuf_size - png_ptr->zstream.avail_out; + *(text + text_size) = '\0'; + } + if (ret != Z_STREAM_END) + { + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + } + else + { + break; + } + + if (ret == Z_STREAM_END) + break; + } + + inflateReset(&png_ptr->zstream); + png_ptr->zstream.avail_in = 0; + + if (ret != Z_STREAM_END) + { + png_ptr->current_text = NULL; + png_free(png_ptr, key); + png_free(png_ptr, text); + return; + } + + png_ptr->current_text = NULL; + png_free(png_ptr, key); + key = text; + text += key_size; + + text_ptr = (png_textp)png_malloc(png_ptr, + (png_uint_32)png_sizeof(png_text)); + text_ptr->compression = PNG_TEXT_COMPRESSION_zTXt; + text_ptr->key = key; +#ifdef PNG_iTXt_SUPPORTED + text_ptr->lang = NULL; + text_ptr->lang_key = NULL; +#endif + text_ptr->text = text; + + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, key); + png_free(png_ptr, text_ptr); + + if (ret) + png_warning(png_ptr, "Insufficient memory to store text chunk."); + } +} +#endif + +#if defined(PNG_READ_iTXt_SUPPORTED) +void /* PRIVATE */ +png_push_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 + length) +{ + if (!(png_ptr->mode & PNG_HAVE_IHDR) || (png_ptr->mode & PNG_HAVE_IEND)) + { + png_error(png_ptr, "Out of place iTXt"); + info_ptr = info_ptr; /* to quiet some compiler warnings */ + } + +#ifdef PNG_MAX_MALLOC_64K + png_ptr->skip_length = 0; /* This may not be necessary */ + + if (length > (png_uint_32)65535L) /* Can't hold entire string in memory */ + { + png_warning(png_ptr, "iTXt chunk too large to fit in memory"); + png_ptr->skip_length = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + png_ptr->current_text = (png_charp)png_malloc(png_ptr, + (png_uint_32)(length+1)); + png_ptr->current_text[length] = '\0'; + png_ptr->current_text_ptr = png_ptr->current_text; + png_ptr->current_text_size = (png_size_t)length; + png_ptr->current_text_left = (png_size_t)length; + png_ptr->process_mode = PNG_READ_iTXt_MODE; +} + +void /* PRIVATE */ +png_push_read_iTXt(png_structp png_ptr, png_infop info_ptr) +{ + + if (png_ptr->buffer_size && png_ptr->current_text_left) + { + png_size_t text_size; + + if (png_ptr->buffer_size < png_ptr->current_text_left) + text_size = png_ptr->buffer_size; + else + text_size = png_ptr->current_text_left; + png_crc_read(png_ptr, (png_bytep)png_ptr->current_text_ptr, text_size); + png_ptr->current_text_left -= text_size; + png_ptr->current_text_ptr += text_size; + } + if (!(png_ptr->current_text_left)) + { + png_textp text_ptr; + png_charp key; + int comp_flag; + png_charp lang; + png_charp lang_key; + png_charp text; + int ret; + + if (png_ptr->buffer_size < 4) + { + png_push_save_buffer(png_ptr); + return; + } + + png_push_crc_finish(png_ptr); + +#if defined(PNG_MAX_MALLOC_64K) + if (png_ptr->skip_length) + return; +#endif + + key = png_ptr->current_text; + + for (lang = key; *lang; lang++) + /* empty loop */ ; + + if (lang < key + png_ptr->current_text_size - 3) + lang++; + + comp_flag = *lang++; + lang++; /* skip comp_type, always zero */ + + for (lang_key = lang; *lang_key; lang_key++) + /* empty loop */ ; + lang_key++; /* skip NUL separator */ + + text=lang_key; + if (lang_key < key + png_ptr->current_text_size - 1) + { + for (; *text; text++) + /* empty loop */ ; + } + + if (text < key + png_ptr->current_text_size) + text++; + + text_ptr = (png_textp)png_malloc(png_ptr, + (png_uint_32)png_sizeof(png_text)); + text_ptr->compression = comp_flag + 2; + text_ptr->key = key; + text_ptr->lang = lang; + text_ptr->lang_key = lang_key; + text_ptr->text = text; + text_ptr->text_length = 0; + text_ptr->itxt_length = png_strlen(text); + + ret = png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_ptr->current_text = NULL; + + png_free(png_ptr, text_ptr); + if (ret) + png_warning(png_ptr, "Insufficient memory to store iTXt chunk."); + } +} +#endif + +/* This function is called when we haven't found a handler for this + * chunk. If there isn't a problem with the chunk itself (ie a bad chunk + * name or a critical chunk), the chunk is (currently) silently ignored. + */ +void /* PRIVATE */ +png_push_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 + length) +{ + png_uint_32 skip=0; + png_check_chunk_name(png_ptr, png_ptr->chunk_name); + + if (!(png_ptr->chunk_name[0] & 0x20)) + { +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != + PNG_HANDLE_CHUNK_ALWAYS +#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) + && png_ptr->read_user_chunk_fn == NULL +#endif + ) +#endif + png_chunk_error(png_ptr, "unknown critical chunk"); + + info_ptr = info_ptr; /* to quiet some compiler warnings */ + } + +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + if (png_ptr->flags & PNG_FLAG_KEEP_UNKNOWN_CHUNKS) + { +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "unknown chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + png_memcpy((png_charp)png_ptr->unknown_chunk.name, + (png_charp)png_ptr->chunk_name, + png_sizeof(png_ptr->unknown_chunk.name)); + png_ptr->unknown_chunk.name[png_sizeof(png_ptr->unknown_chunk.name)-1]='\0'; + + png_ptr->unknown_chunk.data = (png_bytep)png_malloc(png_ptr, length); + png_ptr->unknown_chunk.size = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)png_ptr->unknown_chunk.data, length); +#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) + if(png_ptr->read_user_chunk_fn != NULL) + { + /* callback to user unknown chunk handler */ + int ret; + ret = (*(png_ptr->read_user_chunk_fn)) + (png_ptr, &png_ptr->unknown_chunk); + if (ret < 0) + png_chunk_error(png_ptr, "error in user chunk"); + if (ret == 0) + { + if (!(png_ptr->chunk_name[0] & 0x20)) + if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != + PNG_HANDLE_CHUNK_ALWAYS) + png_chunk_error(png_ptr, "unknown critical chunk"); + png_set_unknown_chunks(png_ptr, info_ptr, + &png_ptr->unknown_chunk, 1); + } + } +#else + png_set_unknown_chunks(png_ptr, info_ptr, &png_ptr->unknown_chunk, 1); +#endif + png_free(png_ptr, png_ptr->unknown_chunk.data); + png_ptr->unknown_chunk.data = NULL; + } + else +#endif + skip=length; + png_push_crc_skip(png_ptr, skip); +} + +void /* PRIVATE */ +png_push_have_info(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr->info_fn != NULL) + (*(png_ptr->info_fn))(png_ptr, info_ptr); +} + +void /* PRIVATE */ +png_push_have_end(png_structp png_ptr, png_infop info_ptr) +{ + if (png_ptr->end_fn != NULL) + (*(png_ptr->end_fn))(png_ptr, info_ptr); +} + +void /* PRIVATE */ +png_push_have_row(png_structp png_ptr, png_bytep row) +{ + if (png_ptr->row_fn != NULL) + (*(png_ptr->row_fn))(png_ptr, row, png_ptr->row_number, + (int)png_ptr->pass); +} + +void PNGAPI +png_progressive_combine_row (png_structp png_ptr, + png_bytep old_row, png_bytep new_row) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_CONST int FARDATA png_pass_dsp_mask[7] = + {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, 0xff}; +#endif + if(png_ptr == NULL) return; + if (new_row != NULL) /* new_row must == png_ptr->row_buf here. */ + png_combine_row(png_ptr, old_row, png_pass_dsp_mask[png_ptr->pass]); +} + +void PNGAPI +png_set_progressive_read_fn(png_structp png_ptr, png_voidp progressive_ptr, + png_progressive_info_ptr info_fn, png_progressive_row_ptr row_fn, + png_progressive_end_ptr end_fn) +{ + if(png_ptr == NULL) return; + png_ptr->info_fn = info_fn; + png_ptr->row_fn = row_fn; + png_ptr->end_fn = end_fn; + + png_set_read_fn(png_ptr, progressive_ptr, png_push_fill_buffer); +} + +png_voidp PNGAPI +png_get_progressive_ptr(png_structp png_ptr) +{ + if(png_ptr == NULL) return (NULL); + return png_ptr->io_ptr; +} +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ diff --git a/Engine/lib/lpng/pngread.c b/Engine/lib/lpng/pngread.c new file mode 100644 index 000000000..5de5e5635 --- /dev/null +++ b/Engine/lib/lpng/pngread.c @@ -0,0 +1,1473 @@ + +/* pngread.c - read a PNG file + * + * Last changed in libpng 1.2.24 December 14, 2007 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2007 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file contains routines that an application calls directly to + * read a PNG file or stream. + */ + +#define PNG_INTERNAL +#include "png.h" + +#if defined(PNG_READ_SUPPORTED) + +/* Create a PNG structure for reading, and allocate any memory needed. */ +png_structp PNGAPI +png_create_read_struct(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn) +{ + +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_read_struct_2(user_png_ver, error_ptr, error_fn, + warn_fn, png_voidp_NULL, png_malloc_ptr_NULL, png_free_ptr_NULL)); +} + +/* Alternate create PNG structure for reading, and allocate any memory needed. */ +png_structp PNGAPI +png_create_read_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + + png_structp png_ptr; + +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + jmp_buf jmpbuf; +#endif +#endif + + int i; + + png_debug(1, "in png_create_read_struct\n"); +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG, + (png_malloc_ptr)malloc_fn, (png_voidp)mem_ptr); +#else + png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); +#endif + if (png_ptr == NULL) + return (NULL); + + /* added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_ptr->user_width_max=PNG_USER_WIDTH_MAX; + png_ptr->user_height_max=PNG_USER_HEIGHT_MAX; +#endif + +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + if (setjmp(png_ptr->jmpbuf)) +#endif + { + png_free(png_ptr, png_ptr->zbuf); + png_ptr->zbuf=NULL; +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)png_ptr, + (png_free_ptr)free_fn, (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)png_ptr); +#endif + return (NULL); + } +#ifdef USE_FAR_KEYWORD + png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf)); +#endif +#endif + +#ifdef PNG_USER_MEM_SUPPORTED + png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn); +#endif + + png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn); + + i=0; + do + { + if(user_png_ver[i] != png_libpng_ver[i]) + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; + } while (png_libpng_ver[i++]); + + if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH) + { + /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so + * we must recompile any applications that use any older library version. + * For versions after libpng 1.0, we will be compatible, so we need + * only check the first digit. + */ + if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] || + (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) || + (user_png_ver[0] == '0' && user_png_ver[2] < '9')) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char msg[80]; + if (user_png_ver) + { + png_snprintf(msg, 80, + "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + png_snprintf(msg, 80, + "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); +#endif +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "Incompatible libpng version in application and library"); + } + } + + /* initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + png_ptr->zstream.zalloc = png_zalloc; + png_ptr->zstream.zfree = png_zfree; + png_ptr->zstream.opaque = (voidpf)png_ptr; + + switch (inflateInit(&png_ptr->zstream)) + { + case Z_OK: /* Do nothing */ break; + case Z_MEM_ERROR: + case Z_STREAM_ERROR: png_error(png_ptr, "zlib memory error"); break; + case Z_VERSION_ERROR: png_error(png_ptr, "zlib version error"); break; + default: png_error(png_ptr, "Unknown zlib error"); + } + + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + + png_set_read_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL); + +#ifdef PNG_SETJMP_SUPPORTED +/* Applications that neglect to set up their own setjmp() and then encounter + a png_error() will longjmp here. Since the jmpbuf is then meaningless we + abort instead of returning. */ +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) + PNG_ABORT(); + png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf)); +#else + if (setjmp(png_ptr->jmpbuf)) + PNG_ABORT(); +#endif +#endif + return (png_ptr); +} + +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +/* Initialize PNG structure for reading, and allocate any memory needed. + This interface is deprecated in favour of the png_create_read_struct(), + and it will disappear as of libpng-1.3.0. */ +#undef png_read_init +void PNGAPI +png_read_init(png_structp png_ptr) +{ + /* We only come here via pre-1.0.7-compiled applications */ + png_read_init_2(png_ptr, "1.0.6 or earlier", 0, 0); +} + +void PNGAPI +png_read_init_2(png_structp png_ptr, png_const_charp user_png_ver, + png_size_t png_struct_size, png_size_t png_info_size) +{ + /* We only come here via pre-1.0.12-compiled applications */ + if(png_ptr == NULL) return; +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + if(png_sizeof(png_struct) > png_struct_size || + png_sizeof(png_info) > png_info_size) + { + char msg[80]; + png_ptr->warning_fn=NULL; + if (user_png_ver) + { + png_snprintf(msg, 80, + "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + png_snprintf(msg, 80, + "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); + } +#endif + if(png_sizeof(png_struct) > png_struct_size) + { + png_ptr->error_fn=NULL; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "The png struct allocated by the application for reading is too small."); + } + if(png_sizeof(png_info) > png_info_size) + { + png_ptr->error_fn=NULL; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "The info struct allocated by application for reading is too small."); + } + png_read_init_3(&png_ptr, user_png_ver, png_struct_size); +} +#endif /* PNG_1_0_X || PNG_1_2_X */ + +void PNGAPI +png_read_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver, + png_size_t png_struct_size) +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; /* to save current jump buffer */ +#endif + + int i=0; + + png_structp png_ptr=*ptr_ptr; + + if(png_ptr == NULL) return; + + do + { + if(user_png_ver[i] != png_libpng_ver[i]) + { +#ifdef PNG_LEGACY_SUPPORTED + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; +#else + png_ptr->warning_fn=NULL; + png_warning(png_ptr, + "Application uses deprecated png_read_init() and should be recompiled."); + break; +#endif + } + } while (png_libpng_ver[i++]); + + png_debug(1, "in png_read_init_3\n"); + +#ifdef PNG_SETJMP_SUPPORTED + /* save jump buffer and error functions */ + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf)); +#endif + + if(png_sizeof(png_struct) > png_struct_size) + { + png_destroy_struct(png_ptr); + *ptr_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); + png_ptr = *ptr_ptr; + } + + /* reset all variables to 0 */ + png_memset(png_ptr, 0, png_sizeof (png_struct)); + +#ifdef PNG_SETJMP_SUPPORTED + /* restore jump buffer */ + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf)); +#endif + + /* added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_ptr->user_width_max=PNG_USER_WIDTH_MAX; + png_ptr->user_height_max=PNG_USER_HEIGHT_MAX; +#endif + + /* initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + png_ptr->zstream.zalloc = png_zalloc; + png_ptr->zstream.zfree = png_zfree; + png_ptr->zstream.opaque = (voidpf)png_ptr; + + switch (inflateInit(&png_ptr->zstream)) + { + case Z_OK: /* Do nothing */ break; + case Z_MEM_ERROR: + case Z_STREAM_ERROR: png_error(png_ptr, "zlib memory"); break; + case Z_VERSION_ERROR: png_error(png_ptr, "zlib version"); break; + default: png_error(png_ptr, "Unknown zlib error"); + } + + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + + png_set_read_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL); +} + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read the information before the actual image data. This has been + * changed in v0.90 to allow reading a file that already has the magic + * bytes read from the stream. You can tell libpng how many bytes have + * been read from the beginning of the stream (up to the maximum of 8) + * via png_set_sig_bytes(), and we will only check the remaining bytes + * here. The application can then have access to the signature bytes we + * read if it is determined that this isn't a valid PNG file. + */ +void PNGAPI +png_read_info(png_structp png_ptr, png_infop info_ptr) +{ + if(png_ptr == NULL) return; + png_debug(1, "in png_read_info\n"); + /* If we haven't checked all of the PNG signature bytes, do so now. */ + if (png_ptr->sig_bytes < 8) + { + png_size_t num_checked = png_ptr->sig_bytes, + num_to_check = 8 - num_checked; + + png_read_data(png_ptr, &(info_ptr->signature[num_checked]), num_to_check); + png_ptr->sig_bytes = 8; + + if (png_sig_cmp(info_ptr->signature, num_checked, num_to_check)) + { + if (num_checked < 4 && + png_sig_cmp(info_ptr->signature, num_checked, num_to_check - 4)) + png_error(png_ptr, "Not a PNG file"); + else + png_error(png_ptr, "PNG file corrupted by ASCII conversion"); + } + if (num_checked < 3) + png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; + } + + for(;;) + { +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_CONST PNG_IHDR; + PNG_CONST PNG_IDAT; + PNG_CONST PNG_IEND; + PNG_CONST PNG_PLTE; +#if defined(PNG_READ_bKGD_SUPPORTED) + PNG_CONST PNG_bKGD; +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + PNG_CONST PNG_cHRM; +#endif +#if defined(PNG_READ_gAMA_SUPPORTED) + PNG_CONST PNG_gAMA; +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + PNG_CONST PNG_hIST; +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + PNG_CONST PNG_iCCP; +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + PNG_CONST PNG_iTXt; +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + PNG_CONST PNG_oFFs; +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + PNG_CONST PNG_pCAL; +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + PNG_CONST PNG_pHYs; +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + PNG_CONST PNG_sBIT; +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + PNG_CONST PNG_sCAL; +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + PNG_CONST PNG_sPLT; +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + PNG_CONST PNG_sRGB; +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + PNG_CONST PNG_tEXt; +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + PNG_CONST PNG_tIME; +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + PNG_CONST PNG_tRNS; +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + PNG_CONST PNG_zTXt; +#endif +#endif /* PNG_USE_LOCAL_ARRAYS */ + png_byte chunk_length[4]; + png_uint_32 length; + + png_read_data(png_ptr, chunk_length, 4); + length = png_get_uint_31(png_ptr,chunk_length); + + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + + png_debug2(0, "Reading %s chunk, length=%lu.\n", png_ptr->chunk_name, + length); + + /* This should be a binary subdivision search or a hash for + * matching the chunk name rather than a linear search. + */ + if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + if(png_ptr->mode & PNG_AFTER_IDAT) + png_ptr->mode |= PNG_HAVE_CHUNK_AFTER_IDAT; + + if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4)) + png_handle_IHDR(png_ptr, info_ptr, length); + else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4)) + png_handle_IEND(png_ptr, info_ptr, length); +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name)) + { + if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + png_ptr->mode |= PNG_HAVE_IDAT; + png_handle_unknown(png_ptr, info_ptr, length); + if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_ptr->mode |= PNG_HAVE_PLTE; + else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + break; + } + } +#endif + else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_handle_PLTE(png_ptr, info_ptr, length); + else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before IDAT"); + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + png_error(png_ptr, "Missing PLTE before IDAT"); + + png_ptr->idat_size = length; + png_ptr->mode |= PNG_HAVE_IDAT; + break; + } +#if defined(PNG_READ_bKGD_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4)) + png_handle_bKGD(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4)) + png_handle_cHRM(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_gAMA_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4)) + png_handle_gAMA(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4)) + png_handle_hIST(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4)) + png_handle_oFFs(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4)) + png_handle_pCAL(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4)) + png_handle_sCAL(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4)) + png_handle_pHYs(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4)) + png_handle_sBIT(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4)) + png_handle_sRGB(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4)) + png_handle_iCCP(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4)) + png_handle_sPLT(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4)) + png_handle_tEXt(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4)) + png_handle_tIME(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4)) + png_handle_tRNS(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4)) + png_handle_zTXt(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iTXt, 4)) + png_handle_iTXt(png_ptr, info_ptr, length); +#endif + else + png_handle_unknown(png_ptr, info_ptr, length); + } +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +/* optional call to update the users info_ptr structure */ +void PNGAPI +png_read_update_info(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_read_update_info\n"); + if(png_ptr == NULL) return; + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + png_read_start_row(png_ptr); + else + png_warning(png_ptr, + "Ignoring extra png_read_update_info() call; row buffer not reallocated"); + png_read_transform_info(png_ptr, info_ptr); +} + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Initialize palette, background, etc, after transformations + * are set, but before any reading takes place. This allows + * the user to obtain a gamma-corrected palette, for example. + * If the user doesn't call this, we will do it ourselves. + */ +void PNGAPI +png_start_read_image(png_structp png_ptr) +{ + png_debug(1, "in png_start_read_image\n"); + if(png_ptr == NULL) return; + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + png_read_start_row(png_ptr); +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +void PNGAPI +png_read_row(png_structp png_ptr, png_bytep row, png_bytep dsp_row) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_CONST PNG_IDAT; + PNG_CONST int png_pass_dsp_mask[7] = {0xff, 0x0f, 0xff, 0x33, 0xff, 0x55, + 0xff}; + PNG_CONST int png_pass_mask[7] = {0x80, 0x08, 0x88, 0x22, 0xaa, 0x55, 0xff}; +#endif + int ret; + if(png_ptr == NULL) return; + png_debug2(1, "in png_read_row (row %lu, pass %d)\n", + png_ptr->row_number, png_ptr->pass); + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + png_read_start_row(png_ptr); + if (png_ptr->row_number == 0 && png_ptr->pass == 0) + { + /* check for transforms that have been set but were defined out */ +#if defined(PNG_WRITE_INVERT_SUPPORTED) && !defined(PNG_READ_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_warning(png_ptr, "PNG_READ_INVERT_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_FILLER_SUPPORTED) && !defined(PNG_READ_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_warning(png_ptr, "PNG_READ_FILLER_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) && !defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_warning(png_ptr, "PNG_READ_PACKSWAP_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_PACK_SUPPORTED) && !defined(PNG_READ_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_warning(png_ptr, "PNG_READ_PACK_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_SHIFT_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_warning(png_ptr, "PNG_READ_SHIFT_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_BGR_SUPPORTED) && !defined(PNG_READ_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_warning(png_ptr, "PNG_READ_BGR_SUPPORTED is not defined."); +#endif +#if defined(PNG_WRITE_SWAP_SUPPORTED) && !defined(PNG_READ_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_warning(png_ptr, "PNG_READ_SWAP_SUPPORTED is not defined."); +#endif + } + +#if defined(PNG_READ_INTERLACING_SUPPORTED) + /* if interlaced and we do not need a new row, combine row and return */ + if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) + { + switch (png_ptr->pass) + { + case 0: + if (png_ptr->row_number & 0x07) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 1: + if ((png_ptr->row_number & 0x07) || png_ptr->width < 5) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 2: + if ((png_ptr->row_number & 0x07) != 4) + { + if (dsp_row != NULL && (png_ptr->row_number & 4)) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 3: + if ((png_ptr->row_number & 3) || png_ptr->width < 3) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 4: + if ((png_ptr->row_number & 3) != 2) + { + if (dsp_row != NULL && (png_ptr->row_number & 2)) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 5: + if ((png_ptr->row_number & 1) || png_ptr->width < 2) + { + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + png_read_finish_row(png_ptr); + return; + } + break; + case 6: + if (!(png_ptr->row_number & 1)) + { + png_read_finish_row(png_ptr); + return; + } + break; + } + } +#endif + + if (!(png_ptr->mode & PNG_HAVE_IDAT)) + png_error(png_ptr, "Invalid attempt to read row data"); + + png_ptr->zstream.next_out = png_ptr->row_buf; + png_ptr->zstream.avail_out = (uInt)png_ptr->irowbytes; + do + { + if (!(png_ptr->zstream.avail_in)) + { + while (!png_ptr->idat_size) + { + png_byte chunk_length[4]; + + png_crc_finish(png_ptr, 0); + + png_read_data(png_ptr, chunk_length, 4); + png_ptr->idat_size = png_get_uint_31(png_ptr,chunk_length); + + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + png_error(png_ptr, "Not enough image data"); + } + png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_in = png_ptr->zbuf; + if (png_ptr->zbuf_size > png_ptr->idat_size) + png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size; + png_crc_read(png_ptr, png_ptr->zbuf, + (png_size_t)png_ptr->zstream.avail_in); + png_ptr->idat_size -= png_ptr->zstream.avail_in; + } + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret == Z_STREAM_END) + { + if (png_ptr->zstream.avail_out || png_ptr->zstream.avail_in || + png_ptr->idat_size) + png_error(png_ptr, "Extra compressed data"); + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + if (ret != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg : + "Decompression error"); + + } while (png_ptr->zstream.avail_out); + + png_ptr->row_info.color_type = png_ptr->color_type; + png_ptr->row_info.width = png_ptr->iwidth; + png_ptr->row_info.channels = png_ptr->channels; + png_ptr->row_info.bit_depth = png_ptr->bit_depth; + png_ptr->row_info.pixel_depth = png_ptr->pixel_depth; + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + + if(png_ptr->row_buf[0]) + png_read_filter_row(png_ptr, &(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->prev_row + 1, + (int)(png_ptr->row_buf[0])); + + png_memcpy_check(png_ptr, png_ptr->prev_row, png_ptr->row_buf, + png_ptr->rowbytes + 1); + +#if defined(PNG_MNG_FEATURES_SUPPORTED) + if((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) + { + /* Intrapixel differencing */ + png_do_read_intrapixel(&(png_ptr->row_info), png_ptr->row_buf + 1); + } +#endif + + + if (png_ptr->transformations || (png_ptr->flags&PNG_FLAG_STRIP_ALPHA)) + png_do_read_transformations(png_ptr); + +#if defined(PNG_READ_INTERLACING_SUPPORTED) + /* blow up interlaced rows to full size */ + if (png_ptr->interlaced && + (png_ptr->transformations & PNG_INTERLACE)) + { + if (png_ptr->pass < 6) +/* old interface (pre-1.0.9): + png_do_read_interlace(&(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->pass, png_ptr->transformations); + */ + png_do_read_interlace(png_ptr); + + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, + png_pass_dsp_mask[png_ptr->pass]); + if (row != NULL) + png_combine_row(png_ptr, row, + png_pass_mask[png_ptr->pass]); + } + else +#endif + { + if (row != NULL) + png_combine_row(png_ptr, row, 0xff); + if (dsp_row != NULL) + png_combine_row(png_ptr, dsp_row, 0xff); + } + png_read_finish_row(png_ptr); + + if (png_ptr->read_row_fn != NULL) + (*(png_ptr->read_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read one or more rows of image data. If the image is interlaced, + * and png_set_interlace_handling() has been called, the rows need to + * contain the contents of the rows from the previous pass. If the + * image has alpha or transparency, and png_handle_alpha()[*] has been + * called, the rows contents must be initialized to the contents of the + * screen. + * + * "row" holds the actual image, and pixels are placed in it + * as they arrive. If the image is displayed after each pass, it will + * appear to "sparkle" in. "display_row" can be used to display a + * "chunky" progressive image, with finer detail added as it becomes + * available. If you do not want this "chunky" display, you may pass + * NULL for display_row. If you do not want the sparkle display, and + * you have not called png_handle_alpha(), you may pass NULL for rows. + * If you have called png_handle_alpha(), and the image has either an + * alpha channel or a transparency chunk, you must provide a buffer for + * rows. In this case, you do not have to provide a display_row buffer + * also, but you may. If the image is not interlaced, or if you have + * not called png_set_interlace_handling(), the display_row buffer will + * be ignored, so pass NULL to it. + * + * [*] png_handle_alpha() does not exist yet, as of this version of libpng + */ + +void PNGAPI +png_read_rows(png_structp png_ptr, png_bytepp row, + png_bytepp display_row, png_uint_32 num_rows) +{ + png_uint_32 i; + png_bytepp rp; + png_bytepp dp; + + png_debug(1, "in png_read_rows\n"); + if(png_ptr == NULL) return; + rp = row; + dp = display_row; + if (rp != NULL && dp != NULL) + for (i = 0; i < num_rows; i++) + { + png_bytep rptr = *rp++; + png_bytep dptr = *dp++; + + png_read_row(png_ptr, rptr, dptr); + } + else if(rp != NULL) + for (i = 0; i < num_rows; i++) + { + png_bytep rptr = *rp; + png_read_row(png_ptr, rptr, png_bytep_NULL); + rp++; + } + else if(dp != NULL) + for (i = 0; i < num_rows; i++) + { + png_bytep dptr = *dp; + png_read_row(png_ptr, png_bytep_NULL, dptr); + dp++; + } +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read the entire image. If the image has an alpha channel or a tRNS + * chunk, and you have called png_handle_alpha()[*], you will need to + * initialize the image to the current image that PNG will be overlaying. + * We set the num_rows again here, in case it was incorrectly set in + * png_read_start_row() by a call to png_read_update_info() or + * png_start_read_image() if png_set_interlace_handling() wasn't called + * prior to either of these functions like it should have been. You can + * only call this function once. If you desire to have an image for + * each pass of a interlaced image, use png_read_rows() instead. + * + * [*] png_handle_alpha() does not exist yet, as of this version of libpng + */ +void PNGAPI +png_read_image(png_structp png_ptr, png_bytepp image) +{ + png_uint_32 i,image_height; + int pass, j; + png_bytepp rp; + + png_debug(1, "in png_read_image\n"); + if(png_ptr == NULL) return; + +#ifdef PNG_READ_INTERLACING_SUPPORTED + pass = png_set_interlace_handling(png_ptr); +#else + if (png_ptr->interlaced) + png_error(png_ptr, + "Cannot read interlaced image -- interlace handler disabled."); + pass = 1; +#endif + + + image_height=png_ptr->height; + png_ptr->num_rows = image_height; /* Make sure this is set correctly */ + + for (j = 0; j < pass; j++) + { + rp = image; + for (i = 0; i < image_height; i++) + { + png_read_row(png_ptr, *rp, png_bytep_NULL); + rp++; + } + } +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +/* Read the end of the PNG file. Will not read past the end of the + * file, will verify the end is accurate, and will read any comments + * or time information at the end of the file, if info is not NULL. + */ +void PNGAPI +png_read_end(png_structp png_ptr, png_infop info_ptr) +{ + png_byte chunk_length[4]; + png_uint_32 length; + + png_debug(1, "in png_read_end\n"); + if(png_ptr == NULL) return; + png_crc_finish(png_ptr, 0); /* Finish off CRC from last IDAT chunk */ + + do + { +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_CONST PNG_IHDR; + PNG_CONST PNG_IDAT; + PNG_CONST PNG_IEND; + PNG_CONST PNG_PLTE; +#if defined(PNG_READ_bKGD_SUPPORTED) + PNG_CONST PNG_bKGD; +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + PNG_CONST PNG_cHRM; +#endif +#if defined(PNG_READ_gAMA_SUPPORTED) + PNG_CONST PNG_gAMA; +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + PNG_CONST PNG_hIST; +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + PNG_CONST PNG_iCCP; +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + PNG_CONST PNG_iTXt; +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + PNG_CONST PNG_oFFs; +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + PNG_CONST PNG_pCAL; +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + PNG_CONST PNG_pHYs; +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + PNG_CONST PNG_sBIT; +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + PNG_CONST PNG_sCAL; +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + PNG_CONST PNG_sPLT; +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + PNG_CONST PNG_sRGB; +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + PNG_CONST PNG_tEXt; +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + PNG_CONST PNG_tIME; +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + PNG_CONST PNG_tRNS; +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + PNG_CONST PNG_zTXt; +#endif +#endif /* PNG_USE_LOCAL_ARRAYS */ + + png_read_data(png_ptr, chunk_length, 4); + length = png_get_uint_31(png_ptr,chunk_length); + + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + + png_debug1(0, "Reading %s chunk.\n", png_ptr->chunk_name); + + if (!png_memcmp(png_ptr->chunk_name, png_IHDR, 4)) + png_handle_IHDR(png_ptr, info_ptr, length); + else if (!png_memcmp(png_ptr->chunk_name, png_IEND, 4)) + png_handle_IEND(png_ptr, info_ptr, length); +#ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED + else if (png_handle_as_unknown(png_ptr, png_ptr->chunk_name)) + { + if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + if ((length > 0) || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT)) + png_error(png_ptr, "Too many IDAT's found"); + } + png_handle_unknown(png_ptr, info_ptr, length); + if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_ptr->mode |= PNG_HAVE_PLTE; + } +#endif + else if (!png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + { + /* Zero length IDATs are legal after the last IDAT has been + * read, but not after other chunks have been read. + */ + if ((length > 0) || (png_ptr->mode & PNG_HAVE_CHUNK_AFTER_IDAT)) + png_error(png_ptr, "Too many IDAT's found"); + png_crc_finish(png_ptr, length); + } + else if (!png_memcmp(png_ptr->chunk_name, png_PLTE, 4)) + png_handle_PLTE(png_ptr, info_ptr, length); +#if defined(PNG_READ_bKGD_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_bKGD, 4)) + png_handle_bKGD(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_cHRM_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_cHRM, 4)) + png_handle_cHRM(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_gAMA_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_gAMA, 4)) + png_handle_gAMA(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_hIST_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_hIST, 4)) + png_handle_hIST(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_oFFs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_oFFs, 4)) + png_handle_oFFs(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_pCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pCAL, 4)) + png_handle_pCAL(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sCAL_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sCAL, 4)) + png_handle_sCAL(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_pHYs_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_pHYs, 4)) + png_handle_pHYs(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sBIT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sBIT, 4)) + png_handle_sBIT(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sRGB_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sRGB, 4)) + png_handle_sRGB(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_iCCP_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iCCP, 4)) + png_handle_iCCP(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_sPLT_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_sPLT, 4)) + png_handle_sPLT(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tEXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tEXt, 4)) + png_handle_tEXt(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tIME_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tIME, 4)) + png_handle_tIME(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_tRNS_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_tRNS, 4)) + png_handle_tRNS(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_zTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_zTXt, 4)) + png_handle_zTXt(png_ptr, info_ptr, length); +#endif +#if defined(PNG_READ_iTXt_SUPPORTED) + else if (!png_memcmp(png_ptr->chunk_name, png_iTXt, 4)) + png_handle_iTXt(png_ptr, info_ptr, length); +#endif + else + png_handle_unknown(png_ptr, info_ptr, length); + } while (!(png_ptr->mode & PNG_HAVE_IEND)); +} +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ + +/* free all memory used by the read */ +void PNGAPI +png_destroy_read_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr, + png_infopp end_info_ptr_ptr) +{ + png_structp png_ptr = NULL; + png_infop info_ptr = NULL, end_info_ptr = NULL; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn = NULL; + png_voidp mem_ptr = NULL; +#endif + + png_debug(1, "in png_destroy_read_struct\n"); + if (png_ptr_ptr != NULL) + { + png_ptr = *png_ptr_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; + mem_ptr = png_ptr->mem_ptr; +#endif + } + + if (info_ptr_ptr != NULL) + info_ptr = *info_ptr_ptr; + + if (end_info_ptr_ptr != NULL) + end_info_ptr = *end_info_ptr_ptr; + + png_read_destroy(png_ptr, info_ptr, end_info_ptr); + + if (info_ptr != NULL) + { +#if defined(PNG_TEXT_SUPPORTED) + png_free_data(png_ptr, info_ptr, PNG_FREE_TEXT, -1); +#endif + +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)info_ptr); +#endif + *info_ptr_ptr = NULL; + } + + if (end_info_ptr != NULL) + { +#if defined(PNG_READ_TEXT_SUPPORTED) + png_free_data(png_ptr, end_info_ptr, PNG_FREE_TEXT, -1); +#endif +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)end_info_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)end_info_ptr); +#endif + *end_info_ptr_ptr = NULL; + } + + if (png_ptr != NULL) + { +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)png_ptr); +#endif + *png_ptr_ptr = NULL; + } +} + +/* free all memory used by the read (old method) */ +void /* PRIVATE */ +png_read_destroy(png_structp png_ptr, png_infop info_ptr, png_infop end_info_ptr) +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; +#endif + png_error_ptr error_fn; + png_error_ptr warning_fn; + png_voidp error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn; +#endif + + png_debug(1, "in png_read_destroy\n"); + if (info_ptr != NULL) + png_info_destroy(png_ptr, info_ptr); + + if (end_info_ptr != NULL) + png_info_destroy(png_ptr, end_info_ptr); + + png_free(png_ptr, png_ptr->zbuf); + png_free(png_ptr, png_ptr->big_row_buf); + png_free(png_ptr, png_ptr->prev_row); +#if defined(PNG_READ_DITHER_SUPPORTED) + png_free(png_ptr, png_ptr->palette_lookup); + png_free(png_ptr, png_ptr->dither_index); +#endif +#if defined(PNG_READ_GAMMA_SUPPORTED) + png_free(png_ptr, png_ptr->gamma_table); +#endif +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + png_free(png_ptr, png_ptr->gamma_from_1); + png_free(png_ptr, png_ptr->gamma_to_1); +#endif +#ifdef PNG_FREE_ME_SUPPORTED + if (png_ptr->free_me & PNG_FREE_PLTE) + png_zfree(png_ptr, png_ptr->palette); + png_ptr->free_me &= ~PNG_FREE_PLTE; +#else + if (png_ptr->flags & PNG_FLAG_FREE_PLTE) + png_zfree(png_ptr, png_ptr->palette); + png_ptr->flags &= ~PNG_FLAG_FREE_PLTE; +#endif +#if defined(PNG_tRNS_SUPPORTED) || \ + defined(PNG_READ_EXPAND_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) +#ifdef PNG_FREE_ME_SUPPORTED + if (png_ptr->free_me & PNG_FREE_TRNS) + png_free(png_ptr, png_ptr->trans); + png_ptr->free_me &= ~PNG_FREE_TRNS; +#else + if (png_ptr->flags & PNG_FLAG_FREE_TRNS) + png_free(png_ptr, png_ptr->trans); + png_ptr->flags &= ~PNG_FLAG_FREE_TRNS; +#endif +#endif +#if defined(PNG_READ_hIST_SUPPORTED) +#ifdef PNG_FREE_ME_SUPPORTED + if (png_ptr->free_me & PNG_FREE_HIST) + png_free(png_ptr, png_ptr->hist); + png_ptr->free_me &= ~PNG_FREE_HIST; +#else + if (png_ptr->flags & PNG_FLAG_FREE_HIST) + png_free(png_ptr, png_ptr->hist); + png_ptr->flags &= ~PNG_FLAG_FREE_HIST; +#endif +#endif +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (png_ptr->gamma_16_table != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_table[i]); + } + png_free(png_ptr, png_ptr->gamma_16_table); + } +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_16_from_1 != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_from_1[i]); + } + png_free(png_ptr, png_ptr->gamma_16_from_1); + } + if (png_ptr->gamma_16_to_1 != NULL) + { + int i; + int istop = (1 << (8 - png_ptr->gamma_shift)); + for (i = 0; i < istop; i++) + { + png_free(png_ptr, png_ptr->gamma_16_to_1[i]); + } + png_free(png_ptr, png_ptr->gamma_16_to_1); + } +#endif +#endif +#if defined(PNG_TIME_RFC1123_SUPPORTED) + png_free(png_ptr, png_ptr->time_buffer); +#endif + + inflateEnd(&png_ptr->zstream); +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED + png_free(png_ptr, png_ptr->save_buffer); +#endif + +#ifdef PNG_PROGRESSIVE_READ_SUPPORTED +#ifdef PNG_TEXT_SUPPORTED + png_free(png_ptr, png_ptr->current_text); +#endif /* PNG_TEXT_SUPPORTED */ +#endif /* PNG_PROGRESSIVE_READ_SUPPORTED */ + + /* Save the important info out of the png_struct, in case it is + * being used again. + */ +#ifdef PNG_SETJMP_SUPPORTED + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf)); +#endif + + error_fn = png_ptr->error_fn; + warning_fn = png_ptr->warning_fn; + error_ptr = png_ptr->error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; +#endif + + png_memset(png_ptr, 0, png_sizeof (png_struct)); + + png_ptr->error_fn = error_fn; + png_ptr->warning_fn = warning_fn; + png_ptr->error_ptr = error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr->free_fn = free_fn; +#endif + +#ifdef PNG_SETJMP_SUPPORTED + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf)); +#endif + +} + +void PNGAPI +png_set_read_status_fn(png_structp png_ptr, png_read_status_ptr read_row_fn) +{ + if(png_ptr == NULL) return; + png_ptr->read_row_fn = read_row_fn; +} + + +#ifndef PNG_NO_SEQUENTIAL_READ_SUPPORTED +#if defined(PNG_INFO_IMAGE_SUPPORTED) +void PNGAPI +png_read_png(png_structp png_ptr, png_infop info_ptr, + int transforms, + voidp params) +{ + int row; + + if(png_ptr == NULL) return; +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) + /* invert the alpha channel from opacity to transparency + */ + if (transforms & PNG_TRANSFORM_INVERT_ALPHA) + png_set_invert_alpha(png_ptr); +#endif + + /* png_read_info() gives us all of the information from the + * PNG file before the first IDAT (image data chunk). + */ + png_read_info(png_ptr, info_ptr); + if (info_ptr->height > PNG_UINT_32_MAX/png_sizeof(png_bytep)) + png_error(png_ptr,"Image is too high to process with png_read_png()"); + + /* -------------- image transformations start here ------------------- */ + +#if defined(PNG_READ_16_TO_8_SUPPORTED) + /* tell libpng to strip 16 bit/color files down to 8 bits per color + */ + if (transforms & PNG_TRANSFORM_STRIP_16) + png_set_strip_16(png_ptr); +#endif + +#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) + /* Strip alpha bytes from the input data without combining with + * the background (not recommended). + */ + if (transforms & PNG_TRANSFORM_STRIP_ALPHA) + png_set_strip_alpha(png_ptr); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) && !defined(PNG_READ_EXPAND_SUPPORTED) + /* Extract multiple pixels with bit depths of 1, 2, or 4 from a single + * byte into separate bytes (useful for paletted and grayscale images). + */ + if (transforms & PNG_TRANSFORM_PACKING) + png_set_packing(png_ptr); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + /* Change the order of packed pixels to least significant bit first + * (not useful if you are using png_set_packing). + */ + if (transforms & PNG_TRANSFORM_PACKSWAP) + png_set_packswap(png_ptr); +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) + /* Expand paletted colors into true RGB triplets + * Expand grayscale images to full 8 bits from 1, 2, or 4 bits/pixel + * Expand paletted or RGB images with transparency to full alpha + * channels so the data will be available as RGBA quartets. + */ + if (transforms & PNG_TRANSFORM_EXPAND) + if ((png_ptr->bit_depth < 8) || + (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) || + (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))) + png_set_expand(png_ptr); +#endif + + /* We don't handle background color or gamma transformation or dithering. + */ + +#if defined(PNG_READ_INVERT_SUPPORTED) + /* invert monochrome files to have 0 as white and 1 as black + */ + if (transforms & PNG_TRANSFORM_INVERT_MONO) + png_set_invert_mono(png_ptr); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) + /* If you want to shift the pixel values from the range [0,255] or + * [0,65535] to the original [0,7] or [0,31], or whatever range the + * colors were originally in: + */ + if ((transforms & PNG_TRANSFORM_SHIFT) + && png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT)) + { + png_color_8p sig_bit; + + png_get_sBIT(png_ptr, info_ptr, &sig_bit); + png_set_shift(png_ptr, sig_bit); + } +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) + /* flip the RGB pixels to BGR (or RGBA to BGRA) + */ + if (transforms & PNG_TRANSFORM_BGR) + png_set_bgr(png_ptr); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) + /* swap the RGBA or GA data to ARGB or AG (or BGRA to ABGR) + */ + if (transforms & PNG_TRANSFORM_SWAP_ALPHA) + png_set_swap_alpha(png_ptr); +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) + /* swap bytes of 16 bit files to least significant byte first + */ + if (transforms & PNG_TRANSFORM_SWAP_ENDIAN) + png_set_swap(png_ptr); +#endif + + /* We don't handle adding filler bytes */ + + /* Optional call to gamma correct and add the background to the palette + * and update info structure. REQUIRED if you are expecting libpng to + * update the palette for you (i.e., you selected such a transform above). + */ + png_read_update_info(png_ptr, info_ptr); + + /* -------------- image transformations end here ------------------- */ + +#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0); +#endif + if(info_ptr->row_pointers == NULL) + { + info_ptr->row_pointers = (png_bytepp)png_malloc(png_ptr, + info_ptr->height * png_sizeof(png_bytep)); +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_ROWS; +#endif + for (row = 0; row < (int)info_ptr->height; row++) + { + info_ptr->row_pointers[row] = (png_bytep)png_malloc(png_ptr, + png_get_rowbytes(png_ptr, info_ptr)); + } + } + + png_read_image(png_ptr, info_ptr->row_pointers); + info_ptr->valid |= PNG_INFO_IDAT; + + /* read rest of file, and get additional chunks in info_ptr - REQUIRED */ + png_read_end(png_ptr, info_ptr); + + transforms = transforms; /* quiet compiler warnings */ + params = params; + +} +#endif /* PNG_INFO_IMAGE_SUPPORTED */ +#endif /* PNG_NO_SEQUENTIAL_READ_SUPPORTED */ +#endif /* PNG_READ_SUPPORTED */ diff --git a/Engine/lib/lpng/pngrio.c b/Engine/lib/lpng/pngrio.c new file mode 100644 index 000000000..7d2522f1f --- /dev/null +++ b/Engine/lib/lpng/pngrio.c @@ -0,0 +1,167 @@ + +/* pngrio.c - functions for data input + * + * Last changed in libpng 1.2.13 November 13, 2006 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2006 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file provides a location for all input. Users who need + * special handling are expected to write a function that has the same + * arguments as this and performs a similar function, but that possibly + * has a different input method. Note that you shouldn't change this + * function, but rather write a replacement function and then make + * libpng use it at run time with png_set_read_fn(...). + */ + +#define PNG_INTERNAL +#include "png.h" + +#if defined(PNG_READ_SUPPORTED) + +/* Read the data from whatever input you are using. The default routine + reads from a file pointer. Note that this routine sometimes gets called + with very small lengths, so you should implement some kind of simple + buffering if you are using unbuffered reads. This should never be asked + to read more then 64K on a 16 bit machine. */ +void /* PRIVATE */ +png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_debug1(4,"reading %d bytes\n", (int)length); + if (png_ptr->read_data_fn != NULL) + (*(png_ptr->read_data_fn))(png_ptr, data, length); + else + png_error(png_ptr, "Call to NULL read function"); +} + +#if !defined(PNG_NO_STDIO) +/* This is the function that does the actual reading of data. If you are + not reading from a standard C stream, you should create a replacement + read_data function and use it at run time with png_set_read_fn(), rather + than changing the library. */ +#ifndef USE_FAR_KEYWORD +void PNGAPI +png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_size_t check; + + if(png_ptr == NULL) return; + /* fread() returns 0 on error, so it is OK to store this in a png_size_t + * instead of an int, which is what fread() actually returns. + */ +#if defined(_WIN32_WCE) + if ( !ReadFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) ) + check = 0; +#else + check = (png_size_t)fread(data, (png_size_t)1, length, + (png_FILE_p)png_ptr->io_ptr); +#endif + + if (check != length) + png_error(png_ptr, "Read Error"); +} +#else +/* this is the model-independent version. Since the standard I/O library + can't handle far buffers in the medium and small models, we have to copy + the data. +*/ + +#define NEAR_BUF_SIZE 1024 +#define MIN(a,b) (a <= b ? a : b) + +static void PNGAPI +png_default_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + int check; + png_byte *n_data; + png_FILE_p io_ptr; + + if(png_ptr == NULL) return; + /* Check if data really is near. If so, use usual code. */ + n_data = (png_byte *)CVT_PTR_NOCHECK(data); + io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); + if ((png_bytep)n_data == data) + { +#if defined(_WIN32_WCE) + if ( !ReadFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) ) + check = 0; +#else + check = fread(n_data, 1, length, io_ptr); +#endif + } + else + { + png_byte buf[NEAR_BUF_SIZE]; + png_size_t read, remaining, err; + check = 0; + remaining = length; + do + { + read = MIN(NEAR_BUF_SIZE, remaining); +#if defined(_WIN32_WCE) + if ( !ReadFile((HANDLE)(io_ptr), buf, read, &err, NULL) ) + err = 0; +#else + err = fread(buf, (png_size_t)1, read, io_ptr); +#endif + png_memcpy(data, buf, read); /* copy far buffer to near buffer */ + if(err != read) + break; + else + check += err; + data += read; + remaining -= read; + } + while (remaining != 0); + } + if ((png_uint_32)check != (png_uint_32)length) + png_error(png_ptr, "read Error"); +} +#endif +#endif + +/* This function allows the application to supply a new input function + for libpng if standard C streams aren't being used. + + This function takes as its arguments: + png_ptr - pointer to a png input data structure + io_ptr - pointer to user supplied structure containing info about + the input functions. May be NULL. + read_data_fn - pointer to a new input function that takes as its + arguments a pointer to a png_struct, a pointer to + a location where input data can be stored, and a 32-bit + unsigned int that is the number of bytes to be read. + To exit and output any fatal error messages the new write + function should call png_error(png_ptr, "Error msg"). */ +void PNGAPI +png_set_read_fn(png_structp png_ptr, png_voidp io_ptr, + png_rw_ptr read_data_fn) +{ + if(png_ptr == NULL) return; + png_ptr->io_ptr = io_ptr; + +#if !defined(PNG_NO_STDIO) + if (read_data_fn != NULL) + png_ptr->read_data_fn = read_data_fn; + else + png_ptr->read_data_fn = png_default_read_data; +#else + png_ptr->read_data_fn = read_data_fn; +#endif + + /* It is an error to write to a read device */ + if (png_ptr->write_data_fn != NULL) + { + png_ptr->write_data_fn = NULL; + png_warning(png_ptr, + "It's an error to set both read_data_fn and write_data_fn in the "); + png_warning(png_ptr, + "same structure. Resetting write_data_fn to NULL."); + } + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) + png_ptr->output_flush_fn = NULL; +#endif +} +#endif /* PNG_READ_SUPPORTED */ diff --git a/Engine/lib/lpng/pngrtran.c b/Engine/lib/lpng/pngrtran.c new file mode 100644 index 000000000..cda392154 --- /dev/null +++ b/Engine/lib/lpng/pngrtran.c @@ -0,0 +1,4284 @@ + +/* pngrtran.c - transforms the data in a row for PNG readers + * + * Last changed in libpng 1.2.22 [October 13, 2007] + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2007 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file contains functions optionally called by an application + * in order to tell libpng how to handle data when reading a PNG. + * Transformations that are used in both reading and writing are + * in pngtrans.c. + */ + +#define PNG_INTERNAL +#include "png.h" + +#if defined(PNG_READ_SUPPORTED) + +/* Set the action on getting a CRC error for an ancillary or critical chunk. */ +void PNGAPI +png_set_crc_action(png_structp png_ptr, int crit_action, int ancil_action) +{ + png_debug(1, "in png_set_crc_action\n"); + /* Tell libpng how we react to CRC errors in critical chunks */ + if(png_ptr == NULL) return; + switch (crit_action) + { + case PNG_CRC_NO_CHANGE: /* leave setting as is */ + break; + case PNG_CRC_WARN_USE: /* warn/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; + png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE; + break; + case PNG_CRC_QUIET_USE: /* quiet/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; + png_ptr->flags |= PNG_FLAG_CRC_CRITICAL_USE | + PNG_FLAG_CRC_CRITICAL_IGNORE; + break; + case PNG_CRC_WARN_DISCARD: /* not a valid action for critical data */ + png_warning(png_ptr, "Can't discard critical data on CRC error."); + case PNG_CRC_ERROR_QUIT: /* error/quit */ + case PNG_CRC_DEFAULT: + default: + png_ptr->flags &= ~PNG_FLAG_CRC_CRITICAL_MASK; + break; + } + + switch (ancil_action) + { + case PNG_CRC_NO_CHANGE: /* leave setting as is */ + break; + case PNG_CRC_WARN_USE: /* warn/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE; + break; + case PNG_CRC_QUIET_USE: /* quiet/use data */ + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_USE | + PNG_FLAG_CRC_ANCILLARY_NOWARN; + break; + case PNG_CRC_ERROR_QUIT: /* error/quit */ + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + png_ptr->flags |= PNG_FLAG_CRC_ANCILLARY_NOWARN; + break; + case PNG_CRC_WARN_DISCARD: /* warn/discard data */ + case PNG_CRC_DEFAULT: + default: + png_ptr->flags &= ~PNG_FLAG_CRC_ANCILLARY_MASK; + break; + } +} + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \ + defined(PNG_FLOATING_POINT_SUPPORTED) +/* handle alpha and tRNS via a background color */ +void PNGAPI +png_set_background(png_structp png_ptr, + png_color_16p background_color, int background_gamma_code, + int need_expand, double background_gamma) +{ + png_debug(1, "in png_set_background\n"); + if(png_ptr == NULL) return; + if (background_gamma_code == PNG_BACKGROUND_GAMMA_UNKNOWN) + { + png_warning(png_ptr, "Application must supply a known background gamma"); + return; + } + + png_ptr->transformations |= PNG_BACKGROUND; + png_memcpy(&(png_ptr->background), background_color, + png_sizeof(png_color_16)); + png_ptr->background_gamma = (float)background_gamma; + png_ptr->background_gamma_type = (png_byte)(background_gamma_code); + png_ptr->transformations |= (need_expand ? PNG_BACKGROUND_EXPAND : 0); +} +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) +/* strip 16 bit depth files to 8 bit depth */ +void PNGAPI +png_set_strip_16(png_structp png_ptr) +{ + png_debug(1, "in png_set_strip_16\n"); + if(png_ptr == NULL) return; + png_ptr->transformations |= PNG_16_TO_8; +} +#endif + +#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +void PNGAPI +png_set_strip_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_strip_alpha\n"); + if(png_ptr == NULL) return; + png_ptr->flags |= PNG_FLAG_STRIP_ALPHA; +} +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) +/* Dither file to 8 bit. Supply a palette, the current number + * of elements in the palette, the maximum number of elements + * allowed, and a histogram if possible. If the current number + * of colors is greater then the maximum number, the palette will be + * modified to fit in the maximum number. "full_dither" indicates + * whether we need a dithering cube set up for RGB images, or if we + * simply are reducing the number of colors in a paletted image. + */ + +typedef struct png_dsort_struct +{ + struct png_dsort_struct FAR * next; + png_byte left; + png_byte right; +} png_dsort; +typedef png_dsort FAR * png_dsortp; +typedef png_dsort FAR * FAR * png_dsortpp; + +void PNGAPI +png_set_dither(png_structp png_ptr, png_colorp palette, + int num_palette, int maximum_colors, png_uint_16p histogram, + int full_dither) +{ + png_debug(1, "in png_set_dither\n"); + if(png_ptr == NULL) return; + png_ptr->transformations |= PNG_DITHER; + + if (!full_dither) + { + int i; + + png_ptr->dither_index = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * png_sizeof (png_byte))); + for (i = 0; i < num_palette; i++) + png_ptr->dither_index[i] = (png_byte)i; + } + + if (num_palette > maximum_colors) + { + if (histogram != NULL) + { + /* This is easy enough, just throw out the least used colors. + Perhaps not the best solution, but good enough. */ + + int i; + + /* initialize an array to sort colors */ + png_ptr->dither_sort = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * png_sizeof (png_byte))); + + /* initialize the dither_sort array */ + for (i = 0; i < num_palette; i++) + png_ptr->dither_sort[i] = (png_byte)i; + + /* Find the least used palette entries by starting a + bubble sort, and running it until we have sorted + out enough colors. Note that we don't care about + sorting all the colors, just finding which are + least used. */ + + for (i = num_palette - 1; i >= maximum_colors; i--) + { + int done; /* to stop early if the list is pre-sorted */ + int j; + + done = 1; + for (j = 0; j < i; j++) + { + if (histogram[png_ptr->dither_sort[j]] + < histogram[png_ptr->dither_sort[j + 1]]) + { + png_byte t; + + t = png_ptr->dither_sort[j]; + png_ptr->dither_sort[j] = png_ptr->dither_sort[j + 1]; + png_ptr->dither_sort[j + 1] = t; + done = 0; + } + } + if (done) + break; + } + + /* swap the palette around, and set up a table, if necessary */ + if (full_dither) + { + int j = num_palette; + + /* put all the useful colors within the max, but don't + move the others */ + for (i = 0; i < maximum_colors; i++) + { + if ((int)png_ptr->dither_sort[i] >= maximum_colors) + { + do + j--; + while ((int)png_ptr->dither_sort[j] >= maximum_colors); + palette[i] = palette[j]; + } + } + } + else + { + int j = num_palette; + + /* move all the used colors inside the max limit, and + develop a translation table */ + for (i = 0; i < maximum_colors; i++) + { + /* only move the colors we need to */ + if ((int)png_ptr->dither_sort[i] >= maximum_colors) + { + png_color tmp_color; + + do + j--; + while ((int)png_ptr->dither_sort[j] >= maximum_colors); + + tmp_color = palette[j]; + palette[j] = palette[i]; + palette[i] = tmp_color; + /* indicate where the color went */ + png_ptr->dither_index[j] = (png_byte)i; + png_ptr->dither_index[i] = (png_byte)j; + } + } + + /* find closest color for those colors we are not using */ + for (i = 0; i < num_palette; i++) + { + if ((int)png_ptr->dither_index[i] >= maximum_colors) + { + int min_d, k, min_k, d_index; + + /* find the closest color to one we threw out */ + d_index = png_ptr->dither_index[i]; + min_d = PNG_COLOR_DIST(palette[d_index], palette[0]); + for (k = 1, min_k = 0; k < maximum_colors; k++) + { + int d; + + d = PNG_COLOR_DIST(palette[d_index], palette[k]); + + if (d < min_d) + { + min_d = d; + min_k = k; + } + } + /* point to closest color */ + png_ptr->dither_index[i] = (png_byte)min_k; + } + } + } + png_free(png_ptr, png_ptr->dither_sort); + png_ptr->dither_sort=NULL; + } + else + { + /* This is much harder to do simply (and quickly). Perhaps + we need to go through a median cut routine, but those + don't always behave themselves with only a few colors + as input. So we will just find the closest two colors, + and throw out one of them (chosen somewhat randomly). + [We don't understand this at all, so if someone wants to + work on improving it, be our guest - AED, GRP] + */ + int i; + int max_d; + int num_new_palette; + png_dsortp t; + png_dsortpp hash; + + t=NULL; + + /* initialize palette index arrays */ + png_ptr->index_to_palette = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * png_sizeof (png_byte))); + png_ptr->palette_to_index = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(num_palette * png_sizeof (png_byte))); + + /* initialize the sort array */ + for (i = 0; i < num_palette; i++) + { + png_ptr->index_to_palette[i] = (png_byte)i; + png_ptr->palette_to_index[i] = (png_byte)i; + } + + hash = (png_dsortpp)png_malloc(png_ptr, (png_uint_32)(769 * + png_sizeof (png_dsortp))); + for (i = 0; i < 769; i++) + hash[i] = NULL; +/* png_memset(hash, 0, 769 * png_sizeof (png_dsortp)); */ + + num_new_palette = num_palette; + + /* initial wild guess at how far apart the farthest pixel + pair we will be eliminating will be. Larger + numbers mean more areas will be allocated, Smaller + numbers run the risk of not saving enough data, and + having to do this all over again. + + I have not done extensive checking on this number. + */ + max_d = 96; + + while (num_new_palette > maximum_colors) + { + for (i = 0; i < num_new_palette - 1; i++) + { + int j; + + for (j = i + 1; j < num_new_palette; j++) + { + int d; + + d = PNG_COLOR_DIST(palette[i], palette[j]); + + if (d <= max_d) + { + + t = (png_dsortp)png_malloc_warn(png_ptr, + (png_uint_32)(png_sizeof(png_dsort))); + if (t == NULL) + break; + t->next = hash[d]; + t->left = (png_byte)i; + t->right = (png_byte)j; + hash[d] = t; + } + } + if (t == NULL) + break; + } + + if (t != NULL) + for (i = 0; i <= max_d; i++) + { + if (hash[i] != NULL) + { + png_dsortp p; + + for (p = hash[i]; p; p = p->next) + { + if ((int)png_ptr->index_to_palette[p->left] + < num_new_palette && + (int)png_ptr->index_to_palette[p->right] + < num_new_palette) + { + int j, next_j; + + if (num_new_palette & 0x01) + { + j = p->left; + next_j = p->right; + } + else + { + j = p->right; + next_j = p->left; + } + + num_new_palette--; + palette[png_ptr->index_to_palette[j]] + = palette[num_new_palette]; + if (!full_dither) + { + int k; + + for (k = 0; k < num_palette; k++) + { + if (png_ptr->dither_index[k] == + png_ptr->index_to_palette[j]) + png_ptr->dither_index[k] = + png_ptr->index_to_palette[next_j]; + if ((int)png_ptr->dither_index[k] == + num_new_palette) + png_ptr->dither_index[k] = + png_ptr->index_to_palette[j]; + } + } + + png_ptr->index_to_palette[png_ptr->palette_to_index + [num_new_palette]] = png_ptr->index_to_palette[j]; + png_ptr->palette_to_index[png_ptr->index_to_palette[j]] + = png_ptr->palette_to_index[num_new_palette]; + + png_ptr->index_to_palette[j] = (png_byte)num_new_palette; + png_ptr->palette_to_index[num_new_palette] = (png_byte)j; + } + if (num_new_palette <= maximum_colors) + break; + } + if (num_new_palette <= maximum_colors) + break; + } + } + + for (i = 0; i < 769; i++) + { + if (hash[i] != NULL) + { + png_dsortp p = hash[i]; + while (p) + { + t = p->next; + png_free(png_ptr, p); + p = t; + } + } + hash[i] = 0; + } + max_d += 96; + } + png_free(png_ptr, hash); + png_free(png_ptr, png_ptr->palette_to_index); + png_free(png_ptr, png_ptr->index_to_palette); + png_ptr->palette_to_index=NULL; + png_ptr->index_to_palette=NULL; + } + num_palette = maximum_colors; + } + if (png_ptr->palette == NULL) + { + png_ptr->palette = palette; + } + png_ptr->num_palette = (png_uint_16)num_palette; + + if (full_dither) + { + int i; + png_bytep distance; + int total_bits = PNG_DITHER_RED_BITS + PNG_DITHER_GREEN_BITS + + PNG_DITHER_BLUE_BITS; + int num_red = (1 << PNG_DITHER_RED_BITS); + int num_green = (1 << PNG_DITHER_GREEN_BITS); + int num_blue = (1 << PNG_DITHER_BLUE_BITS); + png_size_t num_entries = ((png_size_t)1 << total_bits); + + png_ptr->palette_lookup = (png_bytep )png_malloc(png_ptr, + (png_uint_32)(num_entries * png_sizeof (png_byte))); + + png_memset(png_ptr->palette_lookup, 0, num_entries * + png_sizeof (png_byte)); + + distance = (png_bytep)png_malloc(png_ptr, (png_uint_32)(num_entries * + png_sizeof(png_byte))); + + png_memset(distance, 0xff, num_entries * png_sizeof(png_byte)); + + for (i = 0; i < num_palette; i++) + { + int ir, ig, ib; + int r = (palette[i].red >> (8 - PNG_DITHER_RED_BITS)); + int g = (palette[i].green >> (8 - PNG_DITHER_GREEN_BITS)); + int b = (palette[i].blue >> (8 - PNG_DITHER_BLUE_BITS)); + + for (ir = 0; ir < num_red; ir++) + { + /* int dr = abs(ir - r); */ + int dr = ((ir > r) ? ir - r : r - ir); + int index_r = (ir << (PNG_DITHER_BLUE_BITS + PNG_DITHER_GREEN_BITS)); + + for (ig = 0; ig < num_green; ig++) + { + /* int dg = abs(ig - g); */ + int dg = ((ig > g) ? ig - g : g - ig); + int dt = dr + dg; + int dm = ((dr > dg) ? dr : dg); + int index_g = index_r | (ig << PNG_DITHER_BLUE_BITS); + + for (ib = 0; ib < num_blue; ib++) + { + int d_index = index_g | ib; + /* int db = abs(ib - b); */ + int db = ((ib > b) ? ib - b : b - ib); + int dmax = ((dm > db) ? dm : db); + int d = dmax + dt + db; + + if (d < (int)distance[d_index]) + { + distance[d_index] = (png_byte)d; + png_ptr->palette_lookup[d_index] = (png_byte)i; + } + } + } + } + } + + png_free(png_ptr, distance); + } +} +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED) +/* Transform the image from the file_gamma to the screen_gamma. We + * only do transformations on images where the file_gamma and screen_gamma + * are not close reciprocals, otherwise it slows things down slightly, and + * also needlessly introduces small errors. + * + * We will turn off gamma transformation later if no semitransparent entries + * are present in the tRNS array for palette images. We can't do it here + * because we don't necessarily have the tRNS chunk yet. + */ +void PNGAPI +png_set_gamma(png_structp png_ptr, double scrn_gamma, double file_gamma) +{ + png_debug(1, "in png_set_gamma\n"); + if(png_ptr == NULL) return; + if ((fabs(scrn_gamma * file_gamma - 1.0) > PNG_GAMMA_THRESHOLD) || + (png_ptr->color_type & PNG_COLOR_MASK_ALPHA) || + (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE)) + png_ptr->transformations |= PNG_GAMMA; + png_ptr->gamma = (float)file_gamma; + png_ptr->screen_gamma = (float)scrn_gamma; +} +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) +/* Expand paletted images to RGB, expand grayscale images of + * less than 8-bit depth to 8-bit depth, and expand tRNS chunks + * to alpha channels. + */ +void PNGAPI +png_set_expand(png_structp png_ptr) +{ + png_debug(1, "in png_set_expand\n"); + if(png_ptr == NULL) return; + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); +#ifdef PNG_WARN_UNINITIALIZED_ROW + png_ptr->flags &= ~PNG_FLAG_ROW_INIT; +#endif +} + +/* GRR 19990627: the following three functions currently are identical + * to png_set_expand(). However, it is entirely reasonable that someone + * might wish to expand an indexed image to RGB but *not* expand a single, + * fully transparent palette entry to a full alpha channel--perhaps instead + * convert tRNS to the grayscale/RGB format (16-bit RGB value), or replace + * the transparent color with a particular RGB value, or drop tRNS entirely. + * IOW, a future version of the library may make the transformations flag + * a bit more fine-grained, with separate bits for each of these three + * functions. + * + * More to the point, these functions make it obvious what libpng will be + * doing, whereas "expand" can (and does) mean any number of things. + * + * GRP 20060307: In libpng-1.4.0, png_set_gray_1_2_4_to_8() was modified + * to expand only the sample depth but not to expand the tRNS to alpha. + */ + +/* Expand paletted images to RGB. */ +void PNGAPI +png_set_palette_to_rgb(png_structp png_ptr) +{ + png_debug(1, "in png_set_palette_to_rgb\n"); + if(png_ptr == NULL) return; + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); +#ifdef PNG_WARN_UNINITIALIZED_ROW + png_ptr->flags &= ~PNG_FLAG_ROW_INIT; +#endif +} + +#if !defined(PNG_1_0_X) +/* Expand grayscale images of less than 8-bit depth to 8 bits. */ +void PNGAPI +png_set_expand_gray_1_2_4_to_8(png_structp png_ptr) +{ + png_debug(1, "in png_set_expand_gray_1_2_4_to_8\n"); + if(png_ptr == NULL) return; + png_ptr->transformations |= PNG_EXPAND; +#ifdef PNG_WARN_UNINITIALIZED_ROW + png_ptr->flags &= ~PNG_FLAG_ROW_INIT; +#endif +} +#endif + +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +/* Expand grayscale images of less than 8-bit depth to 8 bits. */ +/* Deprecated as of libpng-1.2.9 */ +void PNGAPI +png_set_gray_1_2_4_to_8(png_structp png_ptr) +{ + png_debug(1, "in png_set_gray_1_2_4_to_8\n"); + if(png_ptr == NULL) return; + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); +} +#endif + + +/* Expand tRNS chunks to alpha channels. */ +void PNGAPI +png_set_tRNS_to_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_tRNS_to_alpha\n"); + png_ptr->transformations |= (PNG_EXPAND | PNG_EXPAND_tRNS); +#ifdef PNG_WARN_UNINITIALIZED_ROW + png_ptr->flags &= ~PNG_FLAG_ROW_INIT; +#endif +} +#endif /* defined(PNG_READ_EXPAND_SUPPORTED) */ + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +void PNGAPI +png_set_gray_to_rgb(png_structp png_ptr) +{ + png_debug(1, "in png_set_gray_to_rgb\n"); + png_ptr->transformations |= PNG_GRAY_TO_RGB; +#ifdef PNG_WARN_UNINITIALIZED_ROW + png_ptr->flags &= ~PNG_FLAG_ROW_INIT; +#endif +} +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +#if defined(PNG_FLOATING_POINT_SUPPORTED) +/* Convert a RGB image to a grayscale of the same width. This allows us, + * for example, to convert a 24 bpp RGB image into an 8 bpp grayscale image. + */ + +void PNGAPI +png_set_rgb_to_gray(png_structp png_ptr, int error_action, double red, + double green) +{ + int red_fixed = (int)((float)red*100000.0 + 0.5); + int green_fixed = (int)((float)green*100000.0 + 0.5); + if(png_ptr == NULL) return; + png_set_rgb_to_gray_fixed(png_ptr, error_action, red_fixed, green_fixed); +} +#endif + +void PNGAPI +png_set_rgb_to_gray_fixed(png_structp png_ptr, int error_action, + png_fixed_point red, png_fixed_point green) +{ + png_debug(1, "in png_set_rgb_to_gray\n"); + if(png_ptr == NULL) return; + switch(error_action) + { + case 1: png_ptr->transformations |= PNG_RGB_TO_GRAY; + break; + case 2: png_ptr->transformations |= PNG_RGB_TO_GRAY_WARN; + break; + case 3: png_ptr->transformations |= PNG_RGB_TO_GRAY_ERR; + } + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) +#if defined(PNG_READ_EXPAND_SUPPORTED) + png_ptr->transformations |= PNG_EXPAND; +#else + { + png_warning(png_ptr, "Cannot do RGB_TO_GRAY without EXPAND_SUPPORTED."); + png_ptr->transformations &= ~PNG_RGB_TO_GRAY; + } +#endif + { + png_uint_16 red_int, green_int; + if(red < 0 || green < 0) + { + red_int = 6968; /* .212671 * 32768 + .5 */ + green_int = 23434; /* .715160 * 32768 + .5 */ + } + else if(red + green < 100000L) + { + red_int = (png_uint_16)(((png_uint_32)red*32768L)/100000L); + green_int = (png_uint_16)(((png_uint_32)green*32768L)/100000L); + } + else + { + png_warning(png_ptr, "ignoring out of range rgb_to_gray coefficients"); + red_int = 6968; + green_int = 23434; + } + png_ptr->rgb_to_gray_red_coeff = red_int; + png_ptr->rgb_to_gray_green_coeff = green_int; + png_ptr->rgb_to_gray_blue_coeff = (png_uint_16)(32768-red_int-green_int); + } +} +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +void PNGAPI +png_set_read_user_transform_fn(png_structp png_ptr, png_user_transform_ptr + read_user_transform_fn) +{ + png_debug(1, "in png_set_read_user_transform_fn\n"); + if(png_ptr == NULL) return; +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + png_ptr->transformations |= PNG_USER_TRANSFORM; + png_ptr->read_user_transform_fn = read_user_transform_fn; +#endif +#ifdef PNG_LEGACY_SUPPORTED + if(read_user_transform_fn) + png_warning(png_ptr, + "This version of libpng does not support user transforms"); +#endif +} +#endif + +/* Initialize everything needed for the read. This includes modifying + * the palette. + */ +void /* PRIVATE */ +png_init_read_transformations(png_structp png_ptr) +{ + png_debug(1, "in png_init_read_transformations\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if(png_ptr != NULL) +#endif + { +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || defined(PNG_READ_SHIFT_SUPPORTED) \ + || defined(PNG_READ_GAMMA_SUPPORTED) + int color_type = png_ptr->color_type; +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) && defined(PNG_READ_BACKGROUND_SUPPORTED) + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) + /* Detect gray background and attempt to enable optimization + * for gray --> RGB case */ + /* Note: if PNG_BACKGROUND_EXPAND is set and color_type is either RGB or + * RGB_ALPHA (in which case need_expand is superfluous anyway), the + * background color might actually be gray yet not be flagged as such. + * This is not a problem for the current code, which uses + * PNG_BACKGROUND_IS_GRAY only to decide when to do the + * png_do_gray_to_rgb() transformation. + */ + if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) && + !(color_type & PNG_COLOR_MASK_COLOR)) + { + png_ptr->mode |= PNG_BACKGROUND_IS_GRAY; + } else if ((png_ptr->transformations & PNG_BACKGROUND) && + !(png_ptr->transformations & PNG_BACKGROUND_EXPAND) && + (png_ptr->transformations & PNG_GRAY_TO_RGB) && + png_ptr->background.red == png_ptr->background.green && + png_ptr->background.red == png_ptr->background.blue) + { + png_ptr->mode |= PNG_BACKGROUND_IS_GRAY; + png_ptr->background.gray = png_ptr->background.red; + } +#endif + + if ((png_ptr->transformations & PNG_BACKGROUND_EXPAND) && + (png_ptr->transformations & PNG_EXPAND)) + { + if (!(color_type & PNG_COLOR_MASK_COLOR)) /* i.e., GRAY or GRAY_ALPHA */ + { + /* expand background and tRNS chunks */ + switch (png_ptr->bit_depth) + { + case 1: + png_ptr->background.gray *= (png_uint_16)0xff; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) + { + png_ptr->trans_values.gray *= (png_uint_16)0xff; + png_ptr->trans_values.red = png_ptr->trans_values.green + = png_ptr->trans_values.blue = png_ptr->trans_values.gray; + } + break; + case 2: + png_ptr->background.gray *= (png_uint_16)0x55; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) + { + png_ptr->trans_values.gray *= (png_uint_16)0x55; + png_ptr->trans_values.red = png_ptr->trans_values.green + = png_ptr->trans_values.blue = png_ptr->trans_values.gray; + } + break; + case 4: + png_ptr->background.gray *= (png_uint_16)0x11; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) + { + png_ptr->trans_values.gray *= (png_uint_16)0x11; + png_ptr->trans_values.red = png_ptr->trans_values.green + = png_ptr->trans_values.blue = png_ptr->trans_values.gray; + } + break; + case 8: + case 16: + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + break; + } + } + else if (color_type == PNG_COLOR_TYPE_PALETTE) + { + png_ptr->background.red = + png_ptr->palette[png_ptr->background.index].red; + png_ptr->background.green = + png_ptr->palette[png_ptr->background.index].green; + png_ptr->background.blue = + png_ptr->palette[png_ptr->background.index].blue; + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_ALPHA) + { +#if defined(PNG_READ_EXPAND_SUPPORTED) + if (!(png_ptr->transformations & PNG_EXPAND_tRNS)) +#endif + { + /* invert the alpha channel (in tRNS) unless the pixels are + going to be expanded, in which case leave it for later */ + int i,istop; + istop=(int)png_ptr->num_trans; + for (i=0; itrans[i] = (png_byte)(255 - png_ptr->trans[i]); + } + } +#endif + + } + } +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED) + png_ptr->background_1 = png_ptr->background; +#endif +#if defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED) + + if ((color_type == PNG_COLOR_TYPE_PALETTE && png_ptr->num_trans != 0) + && (fabs(png_ptr->screen_gamma * png_ptr->gamma - 1.0) + < PNG_GAMMA_THRESHOLD)) + { + int i,k; + k=0; + for (i=0; inum_trans; i++) + { + if (png_ptr->trans[i] != 0 && png_ptr->trans[i] != 0xff) + k=1; /* partial transparency is present */ + } + if (k == 0) + png_ptr->transformations &= ~PNG_GAMMA; + } + + if ((png_ptr->transformations & (PNG_GAMMA | PNG_RGB_TO_GRAY)) && + png_ptr->gamma != 0.0) + { + png_build_gamma_table(png_ptr); +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->transformations & PNG_BACKGROUND) + { + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + /* could skip if no transparency and + */ + png_color back, back_1; + png_colorp palette = png_ptr->palette; + int num_palette = png_ptr->num_palette; + int i; + if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE) + { + back.red = png_ptr->gamma_table[png_ptr->background.red]; + back.green = png_ptr->gamma_table[png_ptr->background.green]; + back.blue = png_ptr->gamma_table[png_ptr->background.blue]; + + back_1.red = png_ptr->gamma_to_1[png_ptr->background.red]; + back_1.green = png_ptr->gamma_to_1[png_ptr->background.green]; + back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue]; + } + else + { + double g, gs; + + switch (png_ptr->background_gamma_type) + { + case PNG_BACKGROUND_GAMMA_SCREEN: + g = (png_ptr->screen_gamma); + gs = 1.0; + break; + case PNG_BACKGROUND_GAMMA_FILE: + g = 1.0 / (png_ptr->gamma); + gs = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + break; + case PNG_BACKGROUND_GAMMA_UNIQUE: + g = 1.0 / (png_ptr->background_gamma); + gs = 1.0 / (png_ptr->background_gamma * + png_ptr->screen_gamma); + break; + default: + g = 1.0; /* back_1 */ + gs = 1.0; /* back */ + } + + if ( fabs(gs - 1.0) < PNG_GAMMA_THRESHOLD) + { + back.red = (png_byte)png_ptr->background.red; + back.green = (png_byte)png_ptr->background.green; + back.blue = (png_byte)png_ptr->background.blue; + } + else + { + back.red = (png_byte)(pow( + (double)png_ptr->background.red/255, gs) * 255.0 + .5); + back.green = (png_byte)(pow( + (double)png_ptr->background.green/255, gs) * 255.0 + .5); + back.blue = (png_byte)(pow( + (double)png_ptr->background.blue/255, gs) * 255.0 + .5); + } + + back_1.red = (png_byte)(pow( + (double)png_ptr->background.red/255, g) * 255.0 + .5); + back_1.green = (png_byte)(pow( + (double)png_ptr->background.green/255, g) * 255.0 + .5); + back_1.blue = (png_byte)(pow( + (double)png_ptr->background.blue/255, g) * 255.0 + .5); + } + for (i = 0; i < num_palette; i++) + { + if (i < (int)png_ptr->num_trans && png_ptr->trans[i] != 0xff) + { + if (png_ptr->trans[i] == 0) + { + palette[i] = back; + } + else /* if (png_ptr->trans[i] != 0xff) */ + { + png_byte v, w; + + v = png_ptr->gamma_to_1[palette[i].red]; + png_composite(w, v, png_ptr->trans[i], back_1.red); + palette[i].red = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[palette[i].green]; + png_composite(w, v, png_ptr->trans[i], back_1.green); + palette[i].green = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[palette[i].blue]; + png_composite(w, v, png_ptr->trans[i], back_1.blue); + palette[i].blue = png_ptr->gamma_from_1[w]; + } + } + else + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } + } + /* if (png_ptr->background_gamma_type!=PNG_BACKGROUND_GAMMA_UNKNOWN) */ + else + /* color_type != PNG_COLOR_TYPE_PALETTE */ + { + double m = (double)(((png_uint_32)1 << png_ptr->bit_depth) - 1); + double g = 1.0; + double gs = 1.0; + + switch (png_ptr->background_gamma_type) + { + case PNG_BACKGROUND_GAMMA_SCREEN: + g = (png_ptr->screen_gamma); + gs = 1.0; + break; + case PNG_BACKGROUND_GAMMA_FILE: + g = 1.0 / (png_ptr->gamma); + gs = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + break; + case PNG_BACKGROUND_GAMMA_UNIQUE: + g = 1.0 / (png_ptr->background_gamma); + gs = 1.0 / (png_ptr->background_gamma * + png_ptr->screen_gamma); + break; + } + + png_ptr->background_1.gray = (png_uint_16)(pow( + (double)png_ptr->background.gray / m, g) * m + .5); + png_ptr->background.gray = (png_uint_16)(pow( + (double)png_ptr->background.gray / m, gs) * m + .5); + + if ((png_ptr->background.red != png_ptr->background.green) || + (png_ptr->background.red != png_ptr->background.blue) || + (png_ptr->background.red != png_ptr->background.gray)) + { + /* RGB or RGBA with color background */ + png_ptr->background_1.red = (png_uint_16)(pow( + (double)png_ptr->background.red / m, g) * m + .5); + png_ptr->background_1.green = (png_uint_16)(pow( + (double)png_ptr->background.green / m, g) * m + .5); + png_ptr->background_1.blue = (png_uint_16)(pow( + (double)png_ptr->background.blue / m, g) * m + .5); + png_ptr->background.red = (png_uint_16)(pow( + (double)png_ptr->background.red / m, gs) * m + .5); + png_ptr->background.green = (png_uint_16)(pow( + (double)png_ptr->background.green / m, gs) * m + .5); + png_ptr->background.blue = (png_uint_16)(pow( + (double)png_ptr->background.blue / m, gs) * m + .5); + } + else + { + /* GRAY, GRAY ALPHA, RGB, or RGBA with gray background */ + png_ptr->background_1.red = png_ptr->background_1.green + = png_ptr->background_1.blue = png_ptr->background_1.gray; + png_ptr->background.red = png_ptr->background.green + = png_ptr->background.blue = png_ptr->background.gray; + } + } + } + else + /* transformation does not include PNG_BACKGROUND */ +#endif /* PNG_READ_BACKGROUND_SUPPORTED */ + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + png_colorp palette = png_ptr->palette; + int num_palette = png_ptr->num_palette; + int i; + + for (i = 0; i < num_palette; i++) + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } + } +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + else +#endif +#endif /* PNG_READ_GAMMA_SUPPORTED && PNG_FLOATING_POINT_SUPPORTED */ +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + /* No GAMMA transformation */ + if ((png_ptr->transformations & PNG_BACKGROUND) && + (color_type == PNG_COLOR_TYPE_PALETTE)) + { + int i; + int istop = (int)png_ptr->num_trans; + png_color back; + png_colorp palette = png_ptr->palette; + + back.red = (png_byte)png_ptr->background.red; + back.green = (png_byte)png_ptr->background.green; + back.blue = (png_byte)png_ptr->background.blue; + + for (i = 0; i < istop; i++) + { + if (png_ptr->trans[i] == 0) + { + palette[i] = back; + } + else if (png_ptr->trans[i] != 0xff) + { + /* The png_composite() macro is defined in png.h */ + png_composite(palette[i].red, palette[i].red, + png_ptr->trans[i], back.red); + png_composite(palette[i].green, palette[i].green, + png_ptr->trans[i], back.green); + png_composite(palette[i].blue, palette[i].blue, + png_ptr->trans[i], back.blue); + } + } + } +#endif /* PNG_READ_BACKGROUND_SUPPORTED */ + +#if defined(PNG_READ_SHIFT_SUPPORTED) + if ((png_ptr->transformations & PNG_SHIFT) && + (color_type == PNG_COLOR_TYPE_PALETTE)) + { + png_uint_16 i; + png_uint_16 istop = png_ptr->num_palette; + int sr = 8 - png_ptr->sig_bit.red; + int sg = 8 - png_ptr->sig_bit.green; + int sb = 8 - png_ptr->sig_bit.blue; + + if (sr < 0 || sr > 8) + sr = 0; + if (sg < 0 || sg > 8) + sg = 0; + if (sb < 0 || sb > 8) + sb = 0; + for (i = 0; i < istop; i++) + { + png_ptr->palette[i].red >>= sr; + png_ptr->palette[i].green >>= sg; + png_ptr->palette[i].blue >>= sb; + } + } +#endif /* PNG_READ_SHIFT_SUPPORTED */ + } +#if !defined(PNG_READ_GAMMA_SUPPORTED) && !defined(PNG_READ_SHIFT_SUPPORTED) \ + && !defined(PNG_READ_BACKGROUND_SUPPORTED) + if(png_ptr) + return; +#endif +} + +/* Modify the info structure to reflect the transformations. The + * info should be updated so a PNG file could be written with it, + * assuming the transformations result in valid PNG data. + */ +void /* PRIVATE */ +png_read_transform_info(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_read_transform_info\n"); +#if defined(PNG_READ_EXPAND_SUPPORTED) + if (png_ptr->transformations & PNG_EXPAND) + { + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND_tRNS)) + info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA; + else + info_ptr->color_type = PNG_COLOR_TYPE_RGB; + info_ptr->bit_depth = 8; + info_ptr->num_trans = 0; + } + else + { + if (png_ptr->num_trans) + { + if (png_ptr->transformations & PNG_EXPAND_tRNS) + info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; + else + info_ptr->color_type |= PNG_COLOR_MASK_COLOR; + } + if (info_ptr->bit_depth < 8) + info_ptr->bit_depth = 8; + info_ptr->num_trans = 0; + } + } +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->transformations & PNG_BACKGROUND) + { + info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA; + info_ptr->num_trans = 0; + info_ptr->background = png_ptr->background; + } +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (png_ptr->transformations & PNG_GAMMA) + { +#ifdef PNG_FLOATING_POINT_SUPPORTED + info_ptr->gamma = png_ptr->gamma; +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_gamma = png_ptr->int_gamma; +#endif + } +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) + if ((png_ptr->transformations & PNG_16_TO_8) && (info_ptr->bit_depth == 16)) + info_ptr->bit_depth = 8; +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) + if (png_ptr->transformations & PNG_GRAY_TO_RGB) + info_ptr->color_type |= PNG_COLOR_MASK_COLOR; +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & PNG_RGB_TO_GRAY) + info_ptr->color_type &= ~PNG_COLOR_MASK_COLOR; +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) + if (png_ptr->transformations & PNG_DITHER) + { + if (((info_ptr->color_type == PNG_COLOR_TYPE_RGB) || + (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)) && + png_ptr->palette_lookup && info_ptr->bit_depth == 8) + { + info_ptr->color_type = PNG_COLOR_TYPE_PALETTE; + } + } +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) + if ((png_ptr->transformations & PNG_PACK) && (info_ptr->bit_depth < 8)) + info_ptr->bit_depth = 8; +#endif + + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + info_ptr->channels = 1; + else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) + info_ptr->channels = 3; + else + info_ptr->channels = 1; + +#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) + if (png_ptr->flags & PNG_FLAG_STRIP_ALPHA) + info_ptr->color_type &= ~PNG_COLOR_MASK_ALPHA; +#endif + + if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) + info_ptr->channels++; + +#if defined(PNG_READ_FILLER_SUPPORTED) + /* STRIP_ALPHA and FILLER allowed: MASK_ALPHA bit stripped above */ + if ((png_ptr->transformations & PNG_FILLER) && + ((info_ptr->color_type == PNG_COLOR_TYPE_RGB) || + (info_ptr->color_type == PNG_COLOR_TYPE_GRAY))) + { + info_ptr->channels++; + /* if adding a true alpha channel not just filler */ +#if !defined(PNG_1_0_X) + if (png_ptr->transformations & PNG_ADD_ALPHA) + info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; +#endif + } +#endif + +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) && \ +defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + if(png_ptr->transformations & PNG_USER_TRANSFORM) + { + if(info_ptr->bit_depth < png_ptr->user_transform_depth) + info_ptr->bit_depth = png_ptr->user_transform_depth; + if(info_ptr->channels < png_ptr->user_transform_channels) + info_ptr->channels = png_ptr->user_transform_channels; + } +#endif + + info_ptr->pixel_depth = (png_byte)(info_ptr->channels * + info_ptr->bit_depth); + + info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth,info_ptr->width); + +#if !defined(PNG_READ_EXPAND_SUPPORTED) + if(png_ptr) + return; +#endif +} + +/* Transform the row. The order of transformations is significant, + * and is very touchy. If you add a transformation, take care to + * decide how it fits in with the other transformations here. + */ +void /* PRIVATE */ +png_do_read_transformations(png_structp png_ptr) +{ + png_debug(1, "in png_do_read_transformations\n"); + if (png_ptr->row_buf == NULL) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char msg[50]; + + png_snprintf2(msg, 50, + "NULL row buffer for row %ld, pass %d", png_ptr->row_number, + png_ptr->pass); + png_error(png_ptr, msg); +#else + png_error(png_ptr, "NULL row buffer"); +#endif + } +#ifdef PNG_WARN_UNINITIALIZED_ROW + if (!(png_ptr->flags & PNG_FLAG_ROW_INIT)) + /* Application has failed to call either png_read_start_image() + * or png_read_update_info() after setting transforms that expand + * pixels. This check added to libpng-1.2.19 */ +#if (PNG_WARN_UNINITIALIZED_ROW==1) + png_error(png_ptr, "Uninitialized row"); +#else + png_warning(png_ptr, "Uninitialized row"); +#endif +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) + if (png_ptr->transformations & PNG_EXPAND) + { + if (png_ptr->row_info.color_type == PNG_COLOR_TYPE_PALETTE) + { + png_do_expand_palette(&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->palette, png_ptr->trans, png_ptr->num_trans); + } + else + { + if (png_ptr->num_trans && + (png_ptr->transformations & PNG_EXPAND_tRNS)) + png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->trans_values)); + else + png_do_expand(&(png_ptr->row_info), png_ptr->row_buf + 1, + NULL); + } + } +#endif + +#if defined(PNG_READ_STRIP_ALPHA_SUPPORTED) + if (png_ptr->flags & PNG_FLAG_STRIP_ALPHA) + png_do_strip_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, + PNG_FLAG_FILLER_AFTER | (png_ptr->flags & PNG_FLAG_STRIP_ALPHA)); +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & PNG_RGB_TO_GRAY) + { + int rgb_error = + png_do_rgb_to_gray(png_ptr, &(png_ptr->row_info), png_ptr->row_buf + 1); + if(rgb_error) + { + png_ptr->rgb_to_gray_status=1; + if((png_ptr->transformations & PNG_RGB_TO_GRAY) == + PNG_RGB_TO_GRAY_WARN) + png_warning(png_ptr, "png_do_rgb_to_gray found nongray pixel"); + if((png_ptr->transformations & PNG_RGB_TO_GRAY) == + PNG_RGB_TO_GRAY_ERR) + png_error(png_ptr, "png_do_rgb_to_gray found nongray pixel"); + } + } +#endif + +/* +From Andreas Dilger e-mail to png-implement, 26 March 1998: + + In most cases, the "simple transparency" should be done prior to doing + gray-to-RGB, or you will have to test 3x as many bytes to check if a + pixel is transparent. You would also need to make sure that the + transparency information is upgraded to RGB. + + To summarize, the current flow is: + - Gray + simple transparency -> compare 1 or 2 gray bytes and composite + with background "in place" if transparent, + convert to RGB if necessary + - Gray + alpha -> composite with gray background and remove alpha bytes, + convert to RGB if necessary + + To support RGB backgrounds for gray images we need: + - Gray + simple transparency -> convert to RGB + simple transparency, compare + 3 or 6 bytes and composite with background + "in place" if transparent (3x compare/pixel + compared to doing composite with gray bkgrnd) + - Gray + alpha -> convert to RGB + alpha, composite with background and + remove alpha bytes (3x float operations/pixel + compared with composite on gray background) + + Greg's change will do this. The reason it wasn't done before is for + performance, as this increases the per-pixel operations. If we would check + in advance if the background was gray or RGB, and position the gray-to-RGB + transform appropriately, then it would save a lot of work/time. + */ + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) + /* if gray -> RGB, do so now only if background is non-gray; else do later + * for performance reasons */ + if ((png_ptr->transformations & PNG_GRAY_TO_RGB) && + !(png_ptr->mode & PNG_BACKGROUND_IS_GRAY)) + png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + if ((png_ptr->transformations & PNG_BACKGROUND) && + ((png_ptr->num_trans != 0 ) || + (png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) + png_do_background(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->trans_values), &(png_ptr->background) +#if defined(PNG_READ_GAMMA_SUPPORTED) + , &(png_ptr->background_1), + png_ptr->gamma_table, png_ptr->gamma_from_1, + png_ptr->gamma_to_1, png_ptr->gamma_16_table, + png_ptr->gamma_16_from_1, png_ptr->gamma_16_to_1, + png_ptr->gamma_shift +#endif +); +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) + if ((png_ptr->transformations & PNG_GAMMA) && +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + !((png_ptr->transformations & PNG_BACKGROUND) && + ((png_ptr->num_trans != 0) || + (png_ptr->color_type & PNG_COLOR_MASK_ALPHA))) && +#endif + (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE)) + png_do_gamma(&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->gamma_table, png_ptr->gamma_16_table, + png_ptr->gamma_shift); +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) + if (png_ptr->transformations & PNG_16_TO_8) + png_do_chop(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) + if (png_ptr->transformations & PNG_DITHER) + { + png_do_dither((png_row_infop)&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->palette_lookup, png_ptr->dither_index); + if(png_ptr->row_info.rowbytes == (png_uint_32)0) + png_error(png_ptr, "png_do_dither returned rowbytes=0"); + } +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_do_unshift(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->shift)); +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_do_unpack(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) + /* if gray -> RGB, do so now only if we did not do so above */ + if ((png_ptr->transformations & PNG_GRAY_TO_RGB) && + (png_ptr->mode & PNG_BACKGROUND_IS_GRAY)) + png_do_gray_to_rgb(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_do_read_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, + (png_uint_32)png_ptr->filler, png_ptr->flags); +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_ALPHA) + png_do_read_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_ALPHA) + png_do_read_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + if (png_ptr->transformations & PNG_USER_TRANSFORM) + { + if(png_ptr->read_user_transform_fn != NULL) + (*(png_ptr->read_user_transform_fn)) /* user read transform function */ + (png_ptr, /* png_ptr */ + &(png_ptr->row_info), /* row_info: */ + /* png_uint_32 width; width of row */ + /* png_uint_32 rowbytes; number of bytes in row */ + /* png_byte color_type; color type of pixels */ + /* png_byte bit_depth; bit depth of samples */ + /* png_byte channels; number of channels (1-4) */ + /* png_byte pixel_depth; bits per pixel (depth*channels) */ + png_ptr->row_buf + 1); /* start of pixel data for row */ +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) + if(png_ptr->user_transform_depth) + png_ptr->row_info.bit_depth = png_ptr->user_transform_depth; + if(png_ptr->user_transform_channels) + png_ptr->row_info.channels = png_ptr->user_transform_channels; +#endif + png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth * + png_ptr->row_info.channels); + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + } +#endif + +} + +#if defined(PNG_READ_PACK_SUPPORTED) +/* Unpack pixels of 1, 2, or 4 bits per pixel into 1 byte per pixel, + * without changing the actual values. Thus, if you had a row with + * a bit depth of 1, you would end up with bytes that only contained + * the numbers 0 or 1. If you would rather they contain 0 and 255, use + * png_do_shift() after this. + */ +void /* PRIVATE */ +png_do_unpack(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_unpack\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL && row_info->bit_depth < 8) +#else + if (row_info->bit_depth < 8) +#endif + { + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + switch (row_info->bit_depth) + { + case 1: + { + png_bytep sp = row + (png_size_t)((row_width - 1) >> 3); + png_bytep dp = row + (png_size_t)row_width - 1; + png_uint_32 shift = 7 - (int)((row_width + 7) & 0x07); + for (i = 0; i < row_width; i++) + { + *dp = (png_byte)((*sp >> shift) & 0x01); + if (shift == 7) + { + shift = 0; + sp--; + } + else + shift++; + + dp--; + } + break; + } + case 2: + { + + png_bytep sp = row + (png_size_t)((row_width - 1) >> 2); + png_bytep dp = row + (png_size_t)row_width - 1; + png_uint_32 shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); + for (i = 0; i < row_width; i++) + { + *dp = (png_byte)((*sp >> shift) & 0x03); + if (shift == 6) + { + shift = 0; + sp--; + } + else + shift += 2; + + dp--; + } + break; + } + case 4: + { + png_bytep sp = row + (png_size_t)((row_width - 1) >> 1); + png_bytep dp = row + (png_size_t)row_width - 1; + png_uint_32 shift = (int)((1 - ((row_width + 1) & 0x01)) << 2); + for (i = 0; i < row_width; i++) + { + *dp = (png_byte)((*sp >> shift) & 0x0f); + if (shift == 4) + { + shift = 0; + sp--; + } + else + shift = 4; + + dp--; + } + break; + } + } + row_info->bit_depth = 8; + row_info->pixel_depth = (png_byte)(8 * row_info->channels); + row_info->rowbytes = row_width * row_info->channels; + } +} +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) +/* Reverse the effects of png_do_shift. This routine merely shifts the + * pixels back to their significant bits values. Thus, if you have + * a row of bit depth 8, but only 5 are significant, this will shift + * the values back to 0 through 31. + */ +void /* PRIVATE */ +png_do_unshift(png_row_infop row_info, png_bytep row, png_color_8p sig_bits) +{ + png_debug(1, "in png_do_unshift\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && sig_bits != NULL && +#endif + row_info->color_type != PNG_COLOR_TYPE_PALETTE) + { + int shift[4]; + int channels = 0; + int c; + png_uint_16 value = 0; + png_uint_32 row_width = row_info->width; + + if (row_info->color_type & PNG_COLOR_MASK_COLOR) + { + shift[channels++] = row_info->bit_depth - sig_bits->red; + shift[channels++] = row_info->bit_depth - sig_bits->green; + shift[channels++] = row_info->bit_depth - sig_bits->blue; + } + else + { + shift[channels++] = row_info->bit_depth - sig_bits->gray; + } + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) + { + shift[channels++] = row_info->bit_depth - sig_bits->alpha; + } + + for (c = 0; c < channels; c++) + { + if (shift[c] <= 0) + shift[c] = 0; + else + value = 1; + } + + if (!value) + return; + + switch (row_info->bit_depth) + { + case 2: + { + png_bytep bp; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (bp = row, i = 0; i < istop; i++) + { + *bp >>= 1; + *bp++ &= 0x55; + } + break; + } + case 4: + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + png_byte mask = (png_byte)((((int)0xf0 >> shift[0]) & (int)0xf0) | + (png_byte)((int)0xf >> shift[0])); + + for (i = 0; i < istop; i++) + { + *bp >>= shift[0]; + *bp++ &= mask; + } + break; + } + case 8: + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = row_width * channels; + + for (i = 0; i < istop; i++) + { + *bp++ >>= shift[i%channels]; + } + break; + } + case 16: + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = channels * row_width; + + for (i = 0; i < istop; i++) + { + value = (png_uint_16)((*bp << 8) + *(bp + 1)); + value >>= shift[i%channels]; + *bp++ = (png_byte)(value >> 8); + *bp++ = (png_byte)(value & 0xff); + } + break; + } + } + } +} +#endif + +#if defined(PNG_READ_16_TO_8_SUPPORTED) +/* chop rows of bit depth 16 down to 8 */ +void /* PRIVATE */ +png_do_chop(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_chop\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL && row_info->bit_depth == 16) +#else + if (row_info->bit_depth == 16) +#endif + { + png_bytep sp = row; + png_bytep dp = row; + png_uint_32 i; + png_uint_32 istop = row_info->width * row_info->channels; + + for (i = 0; i> 8)) >> 8; + * + * Approximate calculation with shift/add instead of multiply/divide: + * *dp = ((((png_uint_32)(*sp) << 8) | + * (png_uint_32)((int)(*(sp + 1)) - *sp)) + 128) >> 8; + * + * What we actually do to avoid extra shifting and conversion: + */ + + *dp = *sp + ((((int)(*(sp + 1)) - *sp) > 128) ? 1 : 0); +#else + /* Simply discard the low order byte */ + *dp = *sp; +#endif + } + row_info->bit_depth = 8; + row_info->pixel_depth = (png_byte)(8 * row_info->channels); + row_info->rowbytes = row_info->width * row_info->channels; + } +} +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) +void /* PRIVATE */ +png_do_read_swap_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_read_swap_alpha\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + png_uint_32 row_width = row_info->width; + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This converts from RGBA to ARGB */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save; + } + } + /* This converts from RRGGBBAA to AARRGGBB */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save[2]; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save[0] = *(--sp); + save[1] = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save[0]; + *(--dp) = save[1]; + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This converts from GA to AG */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save; + } + } + /* This converts from GGAA to AAGG */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_byte save[2]; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + save[0] = *(--sp); + save[1] = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = save[0]; + *(--dp) = save[1]; + } + } + } + } +} +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) +void /* PRIVATE */ +png_do_read_invert_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_read_invert_alpha\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + png_uint_32 row_width = row_info->width; + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This inverts the alpha channel in RGBA */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + +/* This does nothing: + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + We can replace it with: +*/ + sp-=3; + dp=sp; + } + } + /* This inverts the alpha channel in RRGGBBAA */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = (png_byte)(255 - *(--sp)); + +/* This does nothing: + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + We can replace it with: +*/ + sp-=6; + dp=sp; + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This inverts the alpha channel in GA */ + if (row_info->bit_depth == 8) + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = *(--sp); + } + } + /* This inverts the alpha channel in GGAA */ + else + { + png_bytep sp = row + row_info->rowbytes; + png_bytep dp = sp; + png_uint_32 i; + + for (i = 0; i < row_width; i++) + { + *(--dp) = (png_byte)(255 - *(--sp)); + *(--dp) = (png_byte)(255 - *(--sp)); +/* + *(--dp) = *(--sp); + *(--dp) = *(--sp); +*/ + sp-=2; + dp=sp; + } + } + } + } +} +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) +/* Add filler channel if we have RGB color */ +void /* PRIVATE */ +png_do_read_filler(png_row_infop row_info, png_bytep row, + png_uint_32 filler, png_uint_32 flags) +{ + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + png_byte hi_filler = (png_byte)((filler>>8) & 0xff); + png_byte lo_filler = (png_byte)(filler & 0xff); + + png_debug(1, "in png_do_read_filler\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + if(row_info->bit_depth == 8) + { + /* This changes the data from G to GX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 1; i < row_width; i++) + { + *(--dp) = lo_filler; + *(--dp) = *(--sp); + } + *(--dp) = lo_filler; + row_info->channels = 2; + row_info->pixel_depth = 16; + row_info->rowbytes = row_width * 2; + } + /* This changes the data from G to XG */ + else + { + png_bytep sp = row + (png_size_t)row_width; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = lo_filler; + } + row_info->channels = 2; + row_info->pixel_depth = 16; + row_info->rowbytes = row_width * 2; + } + } + else if(row_info->bit_depth == 16) + { + /* This changes the data from GG to GGXX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width * 2; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 1; i < row_width; i++) + { + *(--dp) = hi_filler; + *(--dp) = lo_filler; + *(--dp) = *(--sp); + *(--dp) = *(--sp); + } + *(--dp) = hi_filler; + *(--dp) = lo_filler; + row_info->channels = 2; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + /* This changes the data from GG to XXGG */ + else + { + png_bytep sp = row + (png_size_t)row_width * 2; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = hi_filler; + *(--dp) = lo_filler; + } + row_info->channels = 2; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + } + } /* COLOR_TYPE == GRAY */ + else if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + if(row_info->bit_depth == 8) + { + /* This changes the data from RGB to RGBX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width * 3; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 1; i < row_width; i++) + { + *(--dp) = lo_filler; + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + } + *(--dp) = lo_filler; + row_info->channels = 4; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + /* This changes the data from RGB to XRGB */ + else + { + png_bytep sp = row + (png_size_t)row_width * 3; + png_bytep dp = sp + (png_size_t)row_width; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = lo_filler; + } + row_info->channels = 4; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + } + } + else if(row_info->bit_depth == 16) + { + /* This changes the data from RRGGBB to RRGGBBXX */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + png_bytep sp = row + (png_size_t)row_width * 6; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 1; i < row_width; i++) + { + *(--dp) = hi_filler; + *(--dp) = lo_filler; + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + } + *(--dp) = hi_filler; + *(--dp) = lo_filler; + row_info->channels = 4; + row_info->pixel_depth = 64; + row_info->rowbytes = row_width * 8; + } + /* This changes the data from RRGGBB to XXRRGGBB */ + else + { + png_bytep sp = row + (png_size_t)row_width * 6; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = *(--sp); + *(--dp) = hi_filler; + *(--dp) = lo_filler; + } + row_info->channels = 4; + row_info->pixel_depth = 64; + row_info->rowbytes = row_width * 8; + } + } + } /* COLOR_TYPE == RGB */ +} +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) +/* expand grayscale files to RGB, with or without alpha */ +void /* PRIVATE */ +png_do_gray_to_rgb(png_row_infop row_info, png_bytep row) +{ + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + png_debug(1, "in png_do_gray_to_rgb\n"); + if (row_info->bit_depth >= 8 && +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + !(row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + if (row_info->bit_depth == 8) + { + png_bytep sp = row + (png_size_t)row_width - 1; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(dp--) = *sp; + *(dp--) = *sp; + *(dp--) = *(sp--); + } + } + else + { + png_bytep sp = row + (png_size_t)row_width * 2 - 1; + png_bytep dp = sp + (png_size_t)row_width * 4; + for (i = 0; i < row_width; i++) + { + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *(sp--); + *(dp--) = *(sp--); + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + if (row_info->bit_depth == 8) + { + png_bytep sp = row + (png_size_t)row_width * 2 - 1; + png_bytep dp = sp + (png_size_t)row_width * 2; + for (i = 0; i < row_width; i++) + { + *(dp--) = *(sp--); + *(dp--) = *sp; + *(dp--) = *sp; + *(dp--) = *(sp--); + } + } + else + { + png_bytep sp = row + (png_size_t)row_width * 4 - 1; + png_bytep dp = sp + (png_size_t)row_width * 4; + for (i = 0; i < row_width; i++) + { + *(dp--) = *(sp--); + *(dp--) = *(sp--); + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *sp; + *(dp--) = *(sp - 1); + *(dp--) = *(sp--); + *(dp--) = *(sp--); + } + } + } + row_info->channels += (png_byte)2; + row_info->color_type |= PNG_COLOR_MASK_COLOR; + row_info->pixel_depth = (png_byte)(row_info->channels * + row_info->bit_depth); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + } +} +#endif + +#if defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) +/* reduce RGB files to grayscale, with or without alpha + * using the equation given in Poynton's ColorFAQ at + * + * Copyright (c) 1998-01-04 Charles Poynton poynton at inforamp.net + * + * Y = 0.212671 * R + 0.715160 * G + 0.072169 * B + * + * We approximate this with + * + * Y = 0.21268 * R + 0.7151 * G + 0.07217 * B + * + * which can be expressed with integers as + * + * Y = (6969 * R + 23434 * G + 2365 * B)/32768 + * + * The calculation is to be done in a linear colorspace. + * + * Other integer coefficents can be used via png_set_rgb_to_gray(). + */ +int /* PRIVATE */ +png_do_rgb_to_gray(png_structp png_ptr, png_row_infop row_info, png_bytep row) + +{ + png_uint_32 i; + + png_uint_32 row_width = row_info->width; + int rgb_error = 0; + + png_debug(1, "in png_do_rgb_to_gray\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + png_uint_32 rc = png_ptr->rgb_to_gray_red_coeff; + png_uint_32 gc = png_ptr->rgb_to_gray_green_coeff; + png_uint_32 bc = png_ptr->rgb_to_gray_blue_coeff; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + + for (i = 0; i < row_width; i++) + { + png_byte red = png_ptr->gamma_to_1[*(sp++)]; + png_byte green = png_ptr->gamma_to_1[*(sp++)]; + png_byte blue = png_ptr->gamma_to_1[*(sp++)]; + if(red != green || red != blue) + { + rgb_error |= 1; + *(dp++) = png_ptr->gamma_from_1[ + (rc*red+gc*green+bc*blue)>>15]; + } + else + *(dp++) = *(sp-1); + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_byte red = *(sp++); + png_byte green = *(sp++); + png_byte blue = *(sp++); + if(red != green || red != blue) + { + rgb_error |= 1; + *(dp++) = (png_byte)((rc*red+gc*green+bc*blue)>>15); + } + else + *(dp++) = *(sp-1); + } + } + } + + else /* RGB bit_depth == 16 */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_16_to_1 != NULL && + png_ptr->gamma_16_from_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, w; + + red = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + + if(red == green && red == blue) + w = red; + else + { + png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff) >> + png_ptr->gamma_shift][red>>8]; + png_uint_16 green_1 = png_ptr->gamma_16_to_1[(green&0xff) >> + png_ptr->gamma_shift][green>>8]; + png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff) >> + png_ptr->gamma_shift][blue>>8]; + png_uint_16 gray16 = (png_uint_16)((rc*red_1 + gc*green_1 + + bc*blue_1)>>15); + w = png_ptr->gamma_16_from_1[(gray16&0xff) >> + png_ptr->gamma_shift][gray16 >> 8]; + rgb_error |= 1; + } + + *(dp++) = (png_byte)((w>>8) & 0xff); + *(dp++) = (png_byte)(w & 0xff); + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, gray16; + + red = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + + if(red != green || red != blue) + rgb_error |= 1; + gray16 = (png_uint_16)((rc*red + gc*green + bc*blue)>>15); + *(dp++) = (png_byte)((gray16>>8) & 0xff); + *(dp++) = (png_byte)(gray16 & 0xff); + } + } + } + } + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_from_1 != NULL && png_ptr->gamma_to_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_byte red = png_ptr->gamma_to_1[*(sp++)]; + png_byte green = png_ptr->gamma_to_1[*(sp++)]; + png_byte blue = png_ptr->gamma_to_1[*(sp++)]; + if(red != green || red != blue) + rgb_error |= 1; + *(dp++) = png_ptr->gamma_from_1 + [(rc*red + gc*green + bc*blue)>>15]; + *(dp++) = *(sp++); /* alpha */ + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_byte red = *(sp++); + png_byte green = *(sp++); + png_byte blue = *(sp++); + if(red != green || red != blue) + rgb_error |= 1; + *(dp++) = (png_byte)((rc*red + gc*green + bc*blue)>>15); + *(dp++) = *(sp++); /* alpha */ + } + } + } + else /* RGBA bit_depth == 16 */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) || defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->gamma_16_to_1 != NULL && + png_ptr->gamma_16_from_1 != NULL) + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, w; + + red = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)(((*(sp))<<8) | *(sp+1)); sp+=2; + + if(red == green && red == blue) + w = red; + else + { + png_uint_16 red_1 = png_ptr->gamma_16_to_1[(red&0xff) >> + png_ptr->gamma_shift][red>>8]; + png_uint_16 green_1 = png_ptr->gamma_16_to_1[(green&0xff) >> + png_ptr->gamma_shift][green>>8]; + png_uint_16 blue_1 = png_ptr->gamma_16_to_1[(blue&0xff) >> + png_ptr->gamma_shift][blue>>8]; + png_uint_16 gray16 = (png_uint_16)((rc * red_1 + + gc * green_1 + bc * blue_1)>>15); + w = png_ptr->gamma_16_from_1[(gray16&0xff) >> + png_ptr->gamma_shift][gray16 >> 8]; + rgb_error |= 1; + } + + *(dp++) = (png_byte)((w>>8) & 0xff); + *(dp++) = (png_byte)(w & 0xff); + *(dp++) = *(sp++); /* alpha */ + *(dp++) = *(sp++); + } + } + else +#endif + { + png_bytep sp = row; + png_bytep dp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 red, green, blue, gray16; + red = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2; + green = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2; + blue = (png_uint_16)((*(sp)<<8) | *(sp+1)); sp+=2; + if(red != green || red != blue) + rgb_error |= 1; + gray16 = (png_uint_16)((rc*red + gc*green + bc*blue)>>15); + *(dp++) = (png_byte)((gray16>>8) & 0xff); + *(dp++) = (png_byte)(gray16 & 0xff); + *(dp++) = *(sp++); /* alpha */ + *(dp++) = *(sp++); + } + } + } + } + row_info->channels -= (png_byte)2; + row_info->color_type &= ~PNG_COLOR_MASK_COLOR; + row_info->pixel_depth = (png_byte)(row_info->channels * + row_info->bit_depth); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + } + return rgb_error; +} +#endif + +/* Build a grayscale palette. Palette is assumed to be 1 << bit_depth + * large of png_color. This lets grayscale images be treated as + * paletted. Most useful for gamma correction and simplification + * of code. + */ +void PNGAPI +png_build_grayscale_palette(int bit_depth, png_colorp palette) +{ + int num_palette; + int color_inc; + int i; + int v; + + png_debug(1, "in png_do_build_grayscale_palette\n"); + if (palette == NULL) + return; + + switch (bit_depth) + { + case 1: + num_palette = 2; + color_inc = 0xff; + break; + case 2: + num_palette = 4; + color_inc = 0x55; + break; + case 4: + num_palette = 16; + color_inc = 0x11; + break; + case 8: + num_palette = 256; + color_inc = 1; + break; + default: + num_palette = 0; + color_inc = 0; + break; + } + + for (i = 0, v = 0; i < num_palette; i++, v += color_inc) + { + palette[i].red = (png_byte)v; + palette[i].green = (png_byte)v; + palette[i].blue = (png_byte)v; + } +} + +/* This function is currently unused. Do we really need it? */ +#if defined(PNG_READ_DITHER_SUPPORTED) && defined(PNG_CORRECT_PALETTE_SUPPORTED) +void /* PRIVATE */ +png_correct_palette(png_structp png_ptr, png_colorp palette, + int num_palette) +{ + png_debug(1, "in png_correct_palette\n"); +#if defined(PNG_READ_BACKGROUND_SUPPORTED) && \ + defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_FLOATING_POINT_SUPPORTED) + if (png_ptr->transformations & (PNG_GAMMA | PNG_BACKGROUND)) + { + png_color back, back_1; + + if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_FILE) + { + back.red = png_ptr->gamma_table[png_ptr->background.red]; + back.green = png_ptr->gamma_table[png_ptr->background.green]; + back.blue = png_ptr->gamma_table[png_ptr->background.blue]; + + back_1.red = png_ptr->gamma_to_1[png_ptr->background.red]; + back_1.green = png_ptr->gamma_to_1[png_ptr->background.green]; + back_1.blue = png_ptr->gamma_to_1[png_ptr->background.blue]; + } + else + { + double g; + + g = 1.0 / (png_ptr->background_gamma * png_ptr->screen_gamma); + + if (png_ptr->background_gamma_type == PNG_BACKGROUND_GAMMA_SCREEN || + fabs(g - 1.0) < PNG_GAMMA_THRESHOLD) + { + back.red = png_ptr->background.red; + back.green = png_ptr->background.green; + back.blue = png_ptr->background.blue; + } + else + { + back.red = + (png_byte)(pow((double)png_ptr->background.red/255, g) * + 255.0 + 0.5); + back.green = + (png_byte)(pow((double)png_ptr->background.green/255, g) * + 255.0 + 0.5); + back.blue = + (png_byte)(pow((double)png_ptr->background.blue/255, g) * + 255.0 + 0.5); + } + + g = 1.0 / png_ptr->background_gamma; + + back_1.red = + (png_byte)(pow((double)png_ptr->background.red/255, g) * + 255.0 + 0.5); + back_1.green = + (png_byte)(pow((double)png_ptr->background.green/255, g) * + 255.0 + 0.5); + back_1.blue = + (png_byte)(pow((double)png_ptr->background.blue/255, g) * + 255.0 + 0.5); + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_uint_32 i; + + for (i = 0; i < (png_uint_32)num_palette; i++) + { + if (i < png_ptr->num_trans && png_ptr->trans[i] == 0) + { + palette[i] = back; + } + else if (i < png_ptr->num_trans && png_ptr->trans[i] != 0xff) + { + png_byte v, w; + + v = png_ptr->gamma_to_1[png_ptr->palette[i].red]; + png_composite(w, v, png_ptr->trans[i], back_1.red); + palette[i].red = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[png_ptr->palette[i].green]; + png_composite(w, v, png_ptr->trans[i], back_1.green); + palette[i].green = png_ptr->gamma_from_1[w]; + + v = png_ptr->gamma_to_1[png_ptr->palette[i].blue]; + png_composite(w, v, png_ptr->trans[i], back_1.blue); + palette[i].blue = png_ptr->gamma_from_1[w]; + } + else + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } + } + else + { + int i; + + for (i = 0; i < num_palette; i++) + { + if (palette[i].red == (png_byte)png_ptr->trans_values.gray) + { + palette[i] = back; + } + else + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } + } + } + else +#endif +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (png_ptr->transformations & PNG_GAMMA) + { + int i; + + for (i = 0; i < num_palette; i++) + { + palette[i].red = png_ptr->gamma_table[palette[i].red]; + palette[i].green = png_ptr->gamma_table[palette[i].green]; + palette[i].blue = png_ptr->gamma_table[palette[i].blue]; + } + } +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + else +#endif +#endif +#if defined(PNG_READ_BACKGROUND_SUPPORTED) + if (png_ptr->transformations & PNG_BACKGROUND) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_color back; + + back.red = (png_byte)png_ptr->background.red; + back.green = (png_byte)png_ptr->background.green; + back.blue = (png_byte)png_ptr->background.blue; + + for (i = 0; i < (int)png_ptr->num_trans; i++) + { + if (png_ptr->trans[i] == 0) + { + palette[i].red = back.red; + palette[i].green = back.green; + palette[i].blue = back.blue; + } + else if (png_ptr->trans[i] != 0xff) + { + png_composite(palette[i].red, png_ptr->palette[i].red, + png_ptr->trans[i], back.red); + png_composite(palette[i].green, png_ptr->palette[i].green, + png_ptr->trans[i], back.green); + png_composite(palette[i].blue, png_ptr->palette[i].blue, + png_ptr->trans[i], back.blue); + } + } + } + else /* assume grayscale palette (what else could it be?) */ + { + int i; + + for (i = 0; i < num_palette; i++) + { + if (i == (png_byte)png_ptr->trans_values.gray) + { + palette[i].red = (png_byte)png_ptr->background.red; + palette[i].green = (png_byte)png_ptr->background.green; + palette[i].blue = (png_byte)png_ptr->background.blue; + } + } + } + } +#endif +} +#endif + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) +/* Replace any alpha or transparency with the supplied background color. + * "background" is already in the screen gamma, while "background_1" is + * at a gamma of 1.0. Paletted files have already been taken care of. + */ +void /* PRIVATE */ +png_do_background(png_row_infop row_info, png_bytep row, + png_color_16p trans_values, png_color_16p background +#if defined(PNG_READ_GAMMA_SUPPORTED) + , png_color_16p background_1, + png_bytep gamma_table, png_bytep gamma_from_1, png_bytep gamma_to_1, + png_uint_16pp gamma_16, png_uint_16pp gamma_16_from_1, + png_uint_16pp gamma_16_to_1, int gamma_shift +#endif + ) +{ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + int shift; + + png_debug(1, "in png_do_background\n"); + if (background != NULL && +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + (!(row_info->color_type & PNG_COLOR_MASK_ALPHA) || + (row_info->color_type != PNG_COLOR_TYPE_PALETTE && trans_values))) + { + switch (row_info->color_type) + { + case PNG_COLOR_TYPE_GRAY: + { + switch (row_info->bit_depth) + { + case 1: + { + sp = row; + shift = 7; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x01) + == trans_values->gray) + { + *sp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + if (!shift) + { + shift = 7; + sp++; + } + else + shift--; + } + break; + } + case 2: + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_table != NULL) + { + sp = row; + shift = 6; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x03) + == trans_values->gray) + { + *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + else + { + png_byte p = (png_byte)((*sp >> shift) & 0x03); + png_byte g = (png_byte)((gamma_table [p | (p << 2) | + (p << 4) | (p << 6)] >> 6) & 0x03); + *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *sp |= (png_byte)(g << shift); + } + if (!shift) + { + shift = 6; + sp++; + } + else + shift -= 2; + } + } + else +#endif + { + sp = row; + shift = 6; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x03) + == trans_values->gray) + { + *sp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + if (!shift) + { + shift = 6; + sp++; + } + else + shift -= 2; + } + } + break; + } + case 4: + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_table != NULL) + { + sp = row; + shift = 4; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x0f) + == trans_values->gray) + { + *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + else + { + png_byte p = (png_byte)((*sp >> shift) & 0x0f); + png_byte g = (png_byte)((gamma_table[p | + (p << 4)] >> 4) & 0x0f); + *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *sp |= (png_byte)(g << shift); + } + if (!shift) + { + shift = 4; + sp++; + } + else + shift -= 4; + } + } + else +#endif + { + sp = row; + shift = 4; + for (i = 0; i < row_width; i++) + { + if ((png_uint_16)((*sp >> shift) & 0x0f) + == trans_values->gray) + { + *sp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *sp |= (png_byte)(background->gray << shift); + } + if (!shift) + { + shift = 4; + sp++; + } + else + shift -= 4; + } + } + break; + } + case 8: + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_table != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp++) + { + if (*sp == trans_values->gray) + { + *sp = (png_byte)background->gray; + } + else + { + *sp = gamma_table[*sp]; + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp++) + { + if (*sp == trans_values->gray) + { + *sp = (png_byte)background->gray; + } + } + } + break; + } + case 16: + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_16 != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 2) + { + png_uint_16 v; + + v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + if (v == trans_values->gray) + { + /* background is already in screen gamma */ + *sp = (png_byte)((background->gray >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->gray & 0xff); + } + else + { + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 2) + { + png_uint_16 v; + + v = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + if (v == trans_values->gray) + { + *sp = (png_byte)((background->gray >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->gray & 0xff); + } + } + } + break; + } + } + break; + } + case PNG_COLOR_TYPE_RGB: + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_table != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 3) + { + if (*sp == trans_values->red && + *(sp + 1) == trans_values->green && + *(sp + 2) == trans_values->blue) + { + *sp = (png_byte)background->red; + *(sp + 1) = (png_byte)background->green; + *(sp + 2) = (png_byte)background->blue; + } + else + { + *sp = gamma_table[*sp]; + *(sp + 1) = gamma_table[*(sp + 1)]; + *(sp + 2) = gamma_table[*(sp + 2)]; + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 3) + { + if (*sp == trans_values->red && + *(sp + 1) == trans_values->green && + *(sp + 2) == trans_values->blue) + { + *sp = (png_byte)background->red; + *(sp + 1) = (png_byte)background->green; + *(sp + 2) = (png_byte)background->blue; + } + } + } + } + else /* if (row_info->bit_depth == 16) */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_16 != NULL) + { + sp = row; + for (i = 0; i < row_width; i++, sp += 6) + { + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + png_uint_16 g = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + png_uint_16 b = (png_uint_16)(((*(sp+4)) << 8) + *(sp+5)); + if (r == trans_values->red && g == trans_values->green && + b == trans_values->blue) + { + /* background is already in screen gamma */ + *sp = (png_byte)((background->red >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->red & 0xff); + *(sp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(sp + 3) = (png_byte)(background->green & 0xff); + *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(sp + 5) = (png_byte)(background->blue & 0xff); + } + else + { + png_uint_16 v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; + *(sp + 2) = (png_byte)((v >> 8) & 0xff); + *(sp + 3) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; + *(sp + 4) = (png_byte)((v >> 8) & 0xff); + *(sp + 5) = (png_byte)(v & 0xff); + } + } + } + else +#endif + { + sp = row; + for (i = 0; i < row_width; i++, sp += 6) + { + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp+1)); + png_uint_16 g = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + png_uint_16 b = (png_uint_16)(((*(sp+4)) << 8) + *(sp+5)); + + if (r == trans_values->red && g == trans_values->green && + b == trans_values->blue) + { + *sp = (png_byte)((background->red >> 8) & 0xff); + *(sp + 1) = (png_byte)(background->red & 0xff); + *(sp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(sp + 3) = (png_byte)(background->green & 0xff); + *(sp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(sp + 5) = (png_byte)(background->blue & 0xff); + } + } + } + } + break; + } + case PNG_COLOR_TYPE_GRAY_ALPHA: + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_to_1 != NULL && gamma_from_1 != NULL && + gamma_table != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 2, dp++) + { + png_uint_16 a = *(sp + 1); + + if (a == 0xff) + { + *dp = gamma_table[*sp]; + } + else if (a == 0) + { + /* background is already in screen gamma */ + *dp = (png_byte)background->gray; + } + else + { + png_byte v, w; + + v = gamma_to_1[*sp]; + png_composite(w, v, a, background_1->gray); + *dp = gamma_from_1[w]; + } + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 2, dp++) + { + png_byte a = *(sp + 1); + + if (a == 0xff) + { + *dp = *sp; + } +#if defined(PNG_READ_GAMMA_SUPPORTED) + else if (a == 0) + { + *dp = (png_byte)background->gray; + } + else + { + png_composite(*dp, *sp, a, background_1->gray); + } +#else + *dp = (png_byte)background->gray; +#endif + } + } + } + else /* if (png_ptr->bit_depth == 16) */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_16 != NULL && gamma_16_from_1 != NULL && + gamma_16_to_1 != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 2) + { + png_uint_16 a = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + + if (a == (png_uint_16)0xffff) + { + png_uint_16 v; + + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + } +#if defined(PNG_READ_GAMMA_SUPPORTED) + else if (a == 0) +#else + else +#endif + { + /* background is already in screen gamma */ + *dp = (png_byte)((background->gray >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->gray & 0xff); + } +#if defined(PNG_READ_GAMMA_SUPPORTED) + else + { + png_uint_16 g, v, w; + + g = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; + png_composite_16(v, g, a, background_1->gray); + w = gamma_16_from_1[(v&0xff) >> gamma_shift][v >> 8]; + *dp = (png_byte)((w >> 8) & 0xff); + *(dp + 1) = (png_byte)(w & 0xff); + } +#endif + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 2) + { + png_uint_16 a = (png_uint_16)(((*(sp+2)) << 8) + *(sp+3)); + if (a == (png_uint_16)0xffff) + { + png_memcpy(dp, sp, 2); + } +#if defined(PNG_READ_GAMMA_SUPPORTED) + else if (a == 0) +#else + else +#endif + { + *dp = (png_byte)((background->gray >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->gray & 0xff); + } +#if defined(PNG_READ_GAMMA_SUPPORTED) + else + { + png_uint_16 g, v; + + g = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + png_composite_16(v, g, a, background_1->gray); + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + } +#endif + } + } + } + break; + } + case PNG_COLOR_TYPE_RGB_ALPHA: + { + if (row_info->bit_depth == 8) + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_to_1 != NULL && gamma_from_1 != NULL && + gamma_table != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 3) + { + png_byte a = *(sp + 3); + + if (a == 0xff) + { + *dp = gamma_table[*sp]; + *(dp + 1) = gamma_table[*(sp + 1)]; + *(dp + 2) = gamma_table[*(sp + 2)]; + } + else if (a == 0) + { + /* background is already in screen gamma */ + *dp = (png_byte)background->red; + *(dp + 1) = (png_byte)background->green; + *(dp + 2) = (png_byte)background->blue; + } + else + { + png_byte v, w; + + v = gamma_to_1[*sp]; + png_composite(w, v, a, background_1->red); + *dp = gamma_from_1[w]; + v = gamma_to_1[*(sp + 1)]; + png_composite(w, v, a, background_1->green); + *(dp + 1) = gamma_from_1[w]; + v = gamma_to_1[*(sp + 2)]; + png_composite(w, v, a, background_1->blue); + *(dp + 2) = gamma_from_1[w]; + } + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 4, dp += 3) + { + png_byte a = *(sp + 3); + + if (a == 0xff) + { + *dp = *sp; + *(dp + 1) = *(sp + 1); + *(dp + 2) = *(sp + 2); + } + else if (a == 0) + { + *dp = (png_byte)background->red; + *(dp + 1) = (png_byte)background->green; + *(dp + 2) = (png_byte)background->blue; + } + else + { + png_composite(*dp, *sp, a, background->red); + png_composite(*(dp + 1), *(sp + 1), a, + background->green); + png_composite(*(dp + 2), *(sp + 2), a, + background->blue); + } + } + } + } + else /* if (row_info->bit_depth == 16) */ + { +#if defined(PNG_READ_GAMMA_SUPPORTED) + if (gamma_16 != NULL && gamma_16_from_1 != NULL && + gamma_16_to_1 != NULL) + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 8, dp += 6) + { + png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) + << 8) + (png_uint_16)(*(sp + 7))); + if (a == (png_uint_16)0xffff) + { + png_uint_16 v; + + v = gamma_16[*(sp + 1) >> gamma_shift][*sp]; + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 3) >> gamma_shift][*(sp + 2)]; + *(dp + 2) = (png_byte)((v >> 8) & 0xff); + *(dp + 3) = (png_byte)(v & 0xff); + v = gamma_16[*(sp + 5) >> gamma_shift][*(sp + 4)]; + *(dp + 4) = (png_byte)((v >> 8) & 0xff); + *(dp + 5) = (png_byte)(v & 0xff); + } + else if (a == 0) + { + /* background is already in screen gamma */ + *dp = (png_byte)((background->red >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->red & 0xff); + *(dp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(dp + 3) = (png_byte)(background->green & 0xff); + *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(dp + 5) = (png_byte)(background->blue & 0xff); + } + else + { + png_uint_16 v, w, x; + + v = gamma_16_to_1[*(sp + 1) >> gamma_shift][*sp]; + png_composite_16(w, v, a, background_1->red); + x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8]; + *dp = (png_byte)((x >> 8) & 0xff); + *(dp + 1) = (png_byte)(x & 0xff); + v = gamma_16_to_1[*(sp + 3) >> gamma_shift][*(sp + 2)]; + png_composite_16(w, v, a, background_1->green); + x = gamma_16_from_1[((w&0xff) >> gamma_shift)][w >> 8]; + *(dp + 2) = (png_byte)((x >> 8) & 0xff); + *(dp + 3) = (png_byte)(x & 0xff); + v = gamma_16_to_1[*(sp + 5) >> gamma_shift][*(sp + 4)]; + png_composite_16(w, v, a, background_1->blue); + x = gamma_16_from_1[(w & 0xff) >> gamma_shift][w >> 8]; + *(dp + 4) = (png_byte)((x >> 8) & 0xff); + *(dp + 5) = (png_byte)(x & 0xff); + } + } + } + else +#endif + { + sp = row; + dp = row; + for (i = 0; i < row_width; i++, sp += 8, dp += 6) + { + png_uint_16 a = (png_uint_16)(((png_uint_16)(*(sp + 6)) + << 8) + (png_uint_16)(*(sp + 7))); + if (a == (png_uint_16)0xffff) + { + png_memcpy(dp, sp, 6); + } + else if (a == 0) + { + *dp = (png_byte)((background->red >> 8) & 0xff); + *(dp + 1) = (png_byte)(background->red & 0xff); + *(dp + 2) = (png_byte)((background->green >> 8) & 0xff); + *(dp + 3) = (png_byte)(background->green & 0xff); + *(dp + 4) = (png_byte)((background->blue >> 8) & 0xff); + *(dp + 5) = (png_byte)(background->blue & 0xff); + } + else + { + png_uint_16 v; + + png_uint_16 r = (png_uint_16)(((*sp) << 8) + *(sp + 1)); + png_uint_16 g = (png_uint_16)(((*(sp + 2)) << 8) + + *(sp + 3)); + png_uint_16 b = (png_uint_16)(((*(sp + 4)) << 8) + + *(sp + 5)); + + png_composite_16(v, r, a, background->red); + *dp = (png_byte)((v >> 8) & 0xff); + *(dp + 1) = (png_byte)(v & 0xff); + png_composite_16(v, g, a, background->green); + *(dp + 2) = (png_byte)((v >> 8) & 0xff); + *(dp + 3) = (png_byte)(v & 0xff); + png_composite_16(v, b, a, background->blue); + *(dp + 4) = (png_byte)((v >> 8) & 0xff); + *(dp + 5) = (png_byte)(v & 0xff); + } + } + } + } + break; + } + } + + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) + { + row_info->color_type &= ~PNG_COLOR_MASK_ALPHA; + row_info->channels--; + row_info->pixel_depth = (png_byte)(row_info->channels * + row_info->bit_depth); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + } + } +} +#endif + +#if defined(PNG_READ_GAMMA_SUPPORTED) +/* Gamma correct the image, avoiding the alpha channel. Make sure + * you do this after you deal with the transparency issue on grayscale + * or RGB images. If your bit depth is 8, use gamma_table, if it + * is 16, use gamma_16_table and gamma_shift. Build these with + * build_gamma_table(). + */ +void /* PRIVATE */ +png_do_gamma(png_row_infop row_info, png_bytep row, + png_bytep gamma_table, png_uint_16pp gamma_16_table, + int gamma_shift) +{ + png_bytep sp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_gamma\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + ((row_info->bit_depth <= 8 && gamma_table != NULL) || + (row_info->bit_depth == 16 && gamma_16_table != NULL))) + { + switch (row_info->color_type) + { + case PNG_COLOR_TYPE_RGB: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + } + } + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v; + + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + } + } + break; + } + case PNG_COLOR_TYPE_RGB_ALPHA: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + *sp = gamma_table[*sp]; + sp++; + sp++; + } + } + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 4; + } + } + break; + } + case PNG_COLOR_TYPE_GRAY_ALPHA: + { + if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp += 2; + } + } + else /* if (row_info->bit_depth == 16) */ + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 4; + } + } + break; + } + case PNG_COLOR_TYPE_GRAY: + { + if (row_info->bit_depth == 2) + { + sp = row; + for (i = 0; i < row_width; i += 4) + { + int a = *sp & 0xc0; + int b = *sp & 0x30; + int c = *sp & 0x0c; + int d = *sp & 0x03; + + *sp = (png_byte)( + ((((int)gamma_table[a|(a>>2)|(a>>4)|(a>>6)]) ) & 0xc0)| + ((((int)gamma_table[(b<<2)|b|(b>>2)|(b>>4)])>>2) & 0x30)| + ((((int)gamma_table[(c<<4)|(c<<2)|c|(c>>2)])>>4) & 0x0c)| + ((((int)gamma_table[(d<<6)|(d<<4)|(d<<2)|d])>>6) )); + sp++; + } + } + if (row_info->bit_depth == 4) + { + sp = row; + for (i = 0; i < row_width; i += 2) + { + int msb = *sp & 0xf0; + int lsb = *sp & 0x0f; + + *sp = (png_byte)((((int)gamma_table[msb | (msb >> 4)]) & 0xf0) + | (((int)gamma_table[(lsb << 4) | lsb]) >> 4)); + sp++; + } + } + else if (row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++) + { + *sp = gamma_table[*sp]; + sp++; + } + } + else if (row_info->bit_depth == 16) + { + sp = row; + for (i = 0; i < row_width; i++) + { + png_uint_16 v = gamma_16_table[*(sp + 1) >> gamma_shift][*sp]; + *sp = (png_byte)((v >> 8) & 0xff); + *(sp + 1) = (png_byte)(v & 0xff); + sp += 2; + } + } + break; + } + } + } +} +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) +/* Expands a palette row to an RGB or RGBA row depending + * upon whether you supply trans and num_trans. + */ +void /* PRIVATE */ +png_do_expand_palette(png_row_infop row_info, png_bytep row, + png_colorp palette, png_bytep trans, int num_trans) +{ + int shift, value; + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_expand_palette\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (row_info->bit_depth < 8) + { + switch (row_info->bit_depth) + { + case 1: + { + sp = row + (png_size_t)((row_width - 1) >> 3); + dp = row + (png_size_t)row_width - 1; + shift = 7 - (int)((row_width + 7) & 0x07); + for (i = 0; i < row_width; i++) + { + if ((*sp >> shift) & 0x01) + *dp = 1; + else + *dp = 0; + if (shift == 7) + { + shift = 0; + sp--; + } + else + shift++; + + dp--; + } + break; + } + case 2: + { + sp = row + (png_size_t)((row_width - 1) >> 2); + dp = row + (png_size_t)row_width - 1; + shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x03; + *dp = (png_byte)value; + if (shift == 6) + { + shift = 0; + sp--; + } + else + shift += 2; + + dp--; + } + break; + } + case 4: + { + sp = row + (png_size_t)((row_width - 1) >> 1); + dp = row + (png_size_t)row_width - 1; + shift = (int)((row_width & 0x01) << 2); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x0f; + *dp = (png_byte)value; + if (shift == 4) + { + shift = 0; + sp--; + } + else + shift += 4; + + dp--; + } + break; + } + } + row_info->bit_depth = 8; + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; + } + switch (row_info->bit_depth) + { + case 8: + { + if (trans != NULL) + { + sp = row + (png_size_t)row_width - 1; + dp = row + (png_size_t)(row_width << 2) - 1; + + for (i = 0; i < row_width; i++) + { + if ((int)(*sp) >= num_trans) + *dp-- = 0xff; + else + *dp-- = trans[*sp]; + *dp-- = palette[*sp].blue; + *dp-- = palette[*sp].green; + *dp-- = palette[*sp].red; + sp--; + } + row_info->bit_depth = 8; + row_info->pixel_depth = 32; + row_info->rowbytes = row_width * 4; + row_info->color_type = 6; + row_info->channels = 4; + } + else + { + sp = row + (png_size_t)row_width - 1; + dp = row + (png_size_t)(row_width * 3) - 1; + + for (i = 0; i < row_width; i++) + { + *dp-- = palette[*sp].blue; + *dp-- = palette[*sp].green; + *dp-- = palette[*sp].red; + sp--; + } + row_info->bit_depth = 8; + row_info->pixel_depth = 24; + row_info->rowbytes = row_width * 3; + row_info->color_type = 2; + row_info->channels = 3; + } + break; + } + } + } +} + +/* If the bit depth < 8, it is expanded to 8. Also, if the already + * expanded transparency value is supplied, an alpha channel is built. + */ +void /* PRIVATE */ +png_do_expand(png_row_infop row_info, png_bytep row, + png_color_16p trans_value) +{ + int shift, value; + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_expand\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + png_uint_16 gray = (png_uint_16)(trans_value ? trans_value->gray : 0); + + if (row_info->bit_depth < 8) + { + switch (row_info->bit_depth) + { + case 1: + { + gray = (png_uint_16)((gray&0x01)*0xff); + sp = row + (png_size_t)((row_width - 1) >> 3); + dp = row + (png_size_t)row_width - 1; + shift = 7 - (int)((row_width + 7) & 0x07); + for (i = 0; i < row_width; i++) + { + if ((*sp >> shift) & 0x01) + *dp = 0xff; + else + *dp = 0; + if (shift == 7) + { + shift = 0; + sp--; + } + else + shift++; + + dp--; + } + break; + } + case 2: + { + gray = (png_uint_16)((gray&0x03)*0x55); + sp = row + (png_size_t)((row_width - 1) >> 2); + dp = row + (png_size_t)row_width - 1; + shift = (int)((3 - ((row_width + 3) & 0x03)) << 1); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x03; + *dp = (png_byte)(value | (value << 2) | (value << 4) | + (value << 6)); + if (shift == 6) + { + shift = 0; + sp--; + } + else + shift += 2; + + dp--; + } + break; + } + case 4: + { + gray = (png_uint_16)((gray&0x0f)*0x11); + sp = row + (png_size_t)((row_width - 1) >> 1); + dp = row + (png_size_t)row_width - 1; + shift = (int)((1 - ((row_width + 1) & 0x01)) << 2); + for (i = 0; i < row_width; i++) + { + value = (*sp >> shift) & 0x0f; + *dp = (png_byte)(value | (value << 4)); + if (shift == 4) + { + shift = 0; + sp--; + } + else + shift = 4; + + dp--; + } + break; + } + } + row_info->bit_depth = 8; + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; + } + + if (trans_value != NULL) + { + if (row_info->bit_depth == 8) + { + gray = gray & 0xff; + sp = row + (png_size_t)row_width - 1; + dp = row + (png_size_t)(row_width << 1) - 1; + for (i = 0; i < row_width; i++) + { + if (*sp == gray) + *dp-- = 0; + else + *dp-- = 0xff; + *dp-- = *sp--; + } + } + else if (row_info->bit_depth == 16) + { + png_byte gray_high = (gray >> 8) & 0xff; + png_byte gray_low = gray & 0xff; + sp = row + row_info->rowbytes - 1; + dp = row + (row_info->rowbytes << 1) - 1; + for (i = 0; i < row_width; i++) + { + if (*(sp-1) == gray_high && *(sp) == gray_low) + { + *dp-- = 0; + *dp-- = 0; + } + else + { + *dp-- = 0xff; + *dp-- = 0xff; + } + *dp-- = *sp--; + *dp-- = *sp--; + } + } + row_info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA; + row_info->channels = 2; + row_info->pixel_depth = (png_byte)(row_info->bit_depth << 1); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_width); + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB && trans_value) + { + if (row_info->bit_depth == 8) + { + png_byte red = trans_value->red & 0xff; + png_byte green = trans_value->green & 0xff; + png_byte blue = trans_value->blue & 0xff; + sp = row + (png_size_t)row_info->rowbytes - 1; + dp = row + (png_size_t)(row_width << 2) - 1; + for (i = 0; i < row_width; i++) + { + if (*(sp - 2) == red && *(sp - 1) == green && *(sp) == blue) + *dp-- = 0; + else + *dp-- = 0xff; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + } + } + else if (row_info->bit_depth == 16) + { + png_byte red_high = (trans_value->red >> 8) & 0xff; + png_byte green_high = (trans_value->green >> 8) & 0xff; + png_byte blue_high = (trans_value->blue >> 8) & 0xff; + png_byte red_low = trans_value->red & 0xff; + png_byte green_low = trans_value->green & 0xff; + png_byte blue_low = trans_value->blue & 0xff; + sp = row + row_info->rowbytes - 1; + dp = row + (png_size_t)(row_width << 3) - 1; + for (i = 0; i < row_width; i++) + { + if (*(sp - 5) == red_high && + *(sp - 4) == red_low && + *(sp - 3) == green_high && + *(sp - 2) == green_low && + *(sp - 1) == blue_high && + *(sp ) == blue_low) + { + *dp-- = 0; + *dp-- = 0; + } + else + { + *dp-- = 0xff; + *dp-- = 0xff; + } + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + *dp-- = *sp--; + } + } + row_info->color_type = PNG_COLOR_TYPE_RGB_ALPHA; + row_info->channels = 4; + row_info->pixel_depth = (png_byte)(row_info->bit_depth << 2); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + } + } +} +#endif + +#if defined(PNG_READ_DITHER_SUPPORTED) +void /* PRIVATE */ +png_do_dither(png_row_infop row_info, png_bytep row, + png_bytep palette_lookup, png_bytep dither_lookup) +{ + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width=row_info->width; + + png_debug(1, "in png_do_dither\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB && + palette_lookup && row_info->bit_depth == 8) + { + int r, g, b, p; + sp = row; + dp = row; + for (i = 0; i < row_width; i++) + { + r = *sp++; + g = *sp++; + b = *sp++; + + /* this looks real messy, but the compiler will reduce + it down to a reasonable formula. For example, with + 5 bits per color, we get: + p = (((r >> 3) & 0x1f) << 10) | + (((g >> 3) & 0x1f) << 5) | + ((b >> 3) & 0x1f); + */ + p = (((r >> (8 - PNG_DITHER_RED_BITS)) & + ((1 << PNG_DITHER_RED_BITS) - 1)) << + (PNG_DITHER_GREEN_BITS + PNG_DITHER_BLUE_BITS)) | + (((g >> (8 - PNG_DITHER_GREEN_BITS)) & + ((1 << PNG_DITHER_GREEN_BITS) - 1)) << + (PNG_DITHER_BLUE_BITS)) | + ((b >> (8 - PNG_DITHER_BLUE_BITS)) & + ((1 << PNG_DITHER_BLUE_BITS) - 1)); + + *dp++ = palette_lookup[p]; + } + row_info->color_type = PNG_COLOR_TYPE_PALETTE; + row_info->channels = 1; + row_info->pixel_depth = row_info->bit_depth; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA && + palette_lookup != NULL && row_info->bit_depth == 8) + { + int r, g, b, p; + sp = row; + dp = row; + for (i = 0; i < row_width; i++) + { + r = *sp++; + g = *sp++; + b = *sp++; + sp++; + + p = (((r >> (8 - PNG_DITHER_RED_BITS)) & + ((1 << PNG_DITHER_RED_BITS) - 1)) << + (PNG_DITHER_GREEN_BITS + PNG_DITHER_BLUE_BITS)) | + (((g >> (8 - PNG_DITHER_GREEN_BITS)) & + ((1 << PNG_DITHER_GREEN_BITS) - 1)) << + (PNG_DITHER_BLUE_BITS)) | + ((b >> (8 - PNG_DITHER_BLUE_BITS)) & + ((1 << PNG_DITHER_BLUE_BITS) - 1)); + + *dp++ = palette_lookup[p]; + } + row_info->color_type = PNG_COLOR_TYPE_PALETTE; + row_info->channels = 1; + row_info->pixel_depth = row_info->bit_depth; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,row_width); + } + else if (row_info->color_type == PNG_COLOR_TYPE_PALETTE && + dither_lookup && row_info->bit_depth == 8) + { + sp = row; + for (i = 0; i < row_width; i++, sp++) + { + *sp = dither_lookup[*sp]; + } + } + } +} +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +#if defined(PNG_READ_GAMMA_SUPPORTED) +static PNG_CONST int png_gamma_shift[] = + {0x10, 0x21, 0x42, 0x84, 0x110, 0x248, 0x550, 0xff0, 0x00}; + +/* We build the 8- or 16-bit gamma tables here. Note that for 16-bit + * tables, we don't make a full table if we are reducing to 8-bit in + * the future. Note also how the gamma_16 tables are segmented so that + * we don't need to allocate > 64K chunks for a full 16-bit table. + */ +void /* PRIVATE */ +png_build_gamma_table(png_structp png_ptr) +{ + png_debug(1, "in png_build_gamma_table\n"); + + if (png_ptr->bit_depth <= 8) + { + int i; + double g; + + if (png_ptr->screen_gamma > .000001) + g = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + else + g = 1.0; + + png_ptr->gamma_table = (png_bytep)png_malloc(png_ptr, + (png_uint_32)256); + + for (i = 0; i < 256; i++) + { + png_ptr->gamma_table[i] = (png_byte)(pow((double)i / 255.0, + g) * 255.0 + .5); + } + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & ((PNG_BACKGROUND) | PNG_RGB_TO_GRAY)) + { + + g = 1.0 / (png_ptr->gamma); + + png_ptr->gamma_to_1 = (png_bytep)png_malloc(png_ptr, + (png_uint_32)256); + + for (i = 0; i < 256; i++) + { + png_ptr->gamma_to_1[i] = (png_byte)(pow((double)i / 255.0, + g) * 255.0 + .5); + } + + + png_ptr->gamma_from_1 = (png_bytep)png_malloc(png_ptr, + (png_uint_32)256); + + if(png_ptr->screen_gamma > 0.000001) + g = 1.0 / png_ptr->screen_gamma; + else + g = png_ptr->gamma; /* probably doing rgb_to_gray */ + + for (i = 0; i < 256; i++) + { + png_ptr->gamma_from_1[i] = (png_byte)(pow((double)i / 255.0, + g) * 255.0 + .5); + + } + } +#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */ + } + else + { + double g; + int i, j, shift, num; + int sig_bit; + png_uint_32 ig; + + if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + { + sig_bit = (int)png_ptr->sig_bit.red; + if ((int)png_ptr->sig_bit.green > sig_bit) + sig_bit = png_ptr->sig_bit.green; + if ((int)png_ptr->sig_bit.blue > sig_bit) + sig_bit = png_ptr->sig_bit.blue; + } + else + { + sig_bit = (int)png_ptr->sig_bit.gray; + } + + if (sig_bit > 0) + shift = 16 - sig_bit; + else + shift = 0; + + if (png_ptr->transformations & PNG_16_TO_8) + { + if (shift < (16 - PNG_MAX_GAMMA_8)) + shift = (16 - PNG_MAX_GAMMA_8); + } + + if (shift > 8) + shift = 8; + if (shift < 0) + shift = 0; + + png_ptr->gamma_shift = (png_byte)shift; + + num = (1 << (8 - shift)); + + if (png_ptr->screen_gamma > .000001) + g = 1.0 / (png_ptr->gamma * png_ptr->screen_gamma); + else + g = 1.0; + + png_ptr->gamma_16_table = (png_uint_16pp)png_malloc(png_ptr, + (png_uint_32)(num * png_sizeof (png_uint_16p))); + + if (png_ptr->transformations & (PNG_16_TO_8 | PNG_BACKGROUND)) + { + double fin, fout; + png_uint_32 last, max; + + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_table[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof (png_uint_16))); + } + + g = 1.0 / g; + last = 0; + for (i = 0; i < 256; i++) + { + fout = ((double)i + 0.5) / 256.0; + fin = pow(fout, g); + max = (png_uint_32)(fin * (double)((png_uint_32)num << 8)); + while (last <= max) + { + png_ptr->gamma_16_table[(int)(last & (0xff >> shift))] + [(int)(last >> (8 - shift))] = (png_uint_16)( + (png_uint_16)i | ((png_uint_16)i << 8)); + last++; + } + } + while (last < ((png_uint_32)num << 8)) + { + png_ptr->gamma_16_table[(int)(last & (0xff >> shift))] + [(int)(last >> (8 - shift))] = (png_uint_16)65535L; + last++; + } + } + else + { + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_table[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof (png_uint_16))); + + ig = (((png_uint_32)i * (png_uint_32)png_gamma_shift[shift]) >> 4); + for (j = 0; j < 256; j++) + { + png_ptr->gamma_16_table[i][j] = + (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) / + 65535.0, g) * 65535.0 + .5); + } + } + } + +#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \ + defined(PNG_READ_RGB_TO_GRAY_SUPPORTED) + if (png_ptr->transformations & (PNG_BACKGROUND | PNG_RGB_TO_GRAY)) + { + + g = 1.0 / (png_ptr->gamma); + + png_ptr->gamma_16_to_1 = (png_uint_16pp)png_malloc(png_ptr, + (png_uint_32)(num * png_sizeof (png_uint_16p ))); + + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_to_1[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof (png_uint_16))); + + ig = (((png_uint_32)i * + (png_uint_32)png_gamma_shift[shift]) >> 4); + for (j = 0; j < 256; j++) + { + png_ptr->gamma_16_to_1[i][j] = + (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) / + 65535.0, g) * 65535.0 + .5); + } + } + + if(png_ptr->screen_gamma > 0.000001) + g = 1.0 / png_ptr->screen_gamma; + else + g = png_ptr->gamma; /* probably doing rgb_to_gray */ + + png_ptr->gamma_16_from_1 = (png_uint_16pp)png_malloc(png_ptr, + (png_uint_32)(num * png_sizeof (png_uint_16p))); + + for (i = 0; i < num; i++) + { + png_ptr->gamma_16_from_1[i] = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(256 * png_sizeof (png_uint_16))); + + ig = (((png_uint_32)i * + (png_uint_32)png_gamma_shift[shift]) >> 4); + for (j = 0; j < 256; j++) + { + png_ptr->gamma_16_from_1[i][j] = + (png_uint_16)(pow((double)(ig + ((png_uint_32)j << 8)) / + 65535.0, g) * 65535.0 + .5); + } + } + } +#endif /* PNG_READ_BACKGROUND_SUPPORTED || PNG_RGB_TO_GRAY_SUPPORTED */ + } +} +#endif +/* To do: install integer version of png_build_gamma_table here */ +#endif + +#if defined(PNG_MNG_FEATURES_SUPPORTED) +/* undoes intrapixel differencing */ +void /* PRIVATE */ +png_do_read_intrapixel(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_read_intrapixel\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + int bytes_per_pixel; + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 3; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 4; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + *(rp) = (png_byte)((256 + *rp + *(rp+1))&0xff); + *(rp+2) = (png_byte)((256 + *(rp+2) + *(rp+1))&0xff); + } + } + else if (row_info->bit_depth == 16) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 6; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 8; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + png_uint_32 s0 = (*(rp ) << 8) | *(rp+1); + png_uint_32 s1 = (*(rp+2) << 8) | *(rp+3); + png_uint_32 s2 = (*(rp+4) << 8) | *(rp+5); + png_uint_32 red = (png_uint_32)((s0+s1+65536L) & 0xffffL); + png_uint_32 blue = (png_uint_32)((s2+s1+65536L) & 0xffffL); + *(rp ) = (png_byte)((red >> 8) & 0xff); + *(rp+1) = (png_byte)(red & 0xff); + *(rp+4) = (png_byte)((blue >> 8) & 0xff); + *(rp+5) = (png_byte)(blue & 0xff); + } + } + } +} +#endif /* PNG_MNG_FEATURES_SUPPORTED */ +#endif /* PNG_READ_SUPPORTED */ diff --git a/Engine/lib/lpng/pngrutil.c b/Engine/lib/lpng/pngrutil.c new file mode 100644 index 000000000..5e40aca48 --- /dev/null +++ b/Engine/lib/lpng/pngrutil.c @@ -0,0 +1,3164 @@ + +/* pngrutil.c - utilities to read a PNG file + * + * Last changed in libpng 1.2.23 [November 6, 2007] + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2007 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file contains routines that are only called from within + * libpng itself during the course of reading an image. + */ + +#define PNG_INTERNAL +#include "png.h" + +#if defined(PNG_READ_SUPPORTED) + +#if defined(_WIN32_WCE) && (_WIN32_WCE<0x500) +# define WIN32_WCE_OLD +#endif + +#ifdef PNG_FLOATING_POINT_SUPPORTED +# if defined(WIN32_WCE_OLD) +/* strtod() function is not supported on WindowsCE */ +__inline double png_strtod(png_structp png_ptr, PNG_CONST char *nptr, char **endptr) +{ + double result = 0; + int len; + wchar_t *str, *end; + + len = MultiByteToWideChar(CP_ACP, 0, nptr, -1, NULL, 0); + str = (wchar_t *)png_malloc(png_ptr, len * sizeof(wchar_t)); + if ( NULL != str ) + { + MultiByteToWideChar(CP_ACP, 0, nptr, -1, str, len); + result = wcstod(str, &end); + len = WideCharToMultiByte(CP_ACP, 0, end, -1, NULL, 0, NULL, NULL); + *endptr = (char *)nptr + (png_strlen(nptr) - len + 1); + png_free(png_ptr, str); + } + return result; +} +# else +# define png_strtod(p,a,b) strtod(a,b) +# endif +#endif + +png_uint_32 PNGAPI +png_get_uint_31(png_structp png_ptr, png_bytep buf) +{ + png_uint_32 i = png_get_uint_32(buf); + if (i > PNG_UINT_31_MAX) + png_error(png_ptr, "PNG unsigned integer out of range."); + return (i); +} +#ifndef PNG_READ_BIG_ENDIAN_SUPPORTED +/* Grab an unsigned 32-bit integer from a buffer in big-endian format. */ +png_uint_32 PNGAPI +png_get_uint_32(png_bytep buf) +{ + png_uint_32 i = ((png_uint_32)(*buf) << 24) + + ((png_uint_32)(*(buf + 1)) << 16) + + ((png_uint_32)(*(buf + 2)) << 8) + + (png_uint_32)(*(buf + 3)); + + return (i); +} + +/* Grab a signed 32-bit integer from a buffer in big-endian format. The + * data is stored in the PNG file in two's complement format, and it is + * assumed that the machine format for signed integers is the same. */ +png_int_32 PNGAPI +png_get_int_32(png_bytep buf) +{ + png_int_32 i = ((png_int_32)(*buf) << 24) + + ((png_int_32)(*(buf + 1)) << 16) + + ((png_int_32)(*(buf + 2)) << 8) + + (png_int_32)(*(buf + 3)); + + return (i); +} + +/* Grab an unsigned 16-bit integer from a buffer in big-endian format. */ +png_uint_16 PNGAPI +png_get_uint_16(png_bytep buf) +{ + png_uint_16 i = (png_uint_16)(((png_uint_16)(*buf) << 8) + + (png_uint_16)(*(buf + 1))); + + return (i); +} +#endif /* PNG_READ_BIG_ENDIAN_SUPPORTED */ + +/* Read data, and (optionally) run it through the CRC. */ +void /* PRIVATE */ +png_crc_read(png_structp png_ptr, png_bytep buf, png_size_t length) +{ + if(png_ptr == NULL) return; + png_read_data(png_ptr, buf, length); + png_calculate_crc(png_ptr, buf, length); +} + +/* Optionally skip data and then check the CRC. Depending on whether we + are reading a ancillary or critical chunk, and how the program has set + things up, we may calculate the CRC on the data and print a message. + Returns '1' if there was a CRC error, '0' otherwise. */ +int /* PRIVATE */ +png_crc_finish(png_structp png_ptr, png_uint_32 skip) +{ + png_size_t i; + png_size_t istop = png_ptr->zbuf_size; + + for (i = (png_size_t)skip; i > istop; i -= istop) + { + png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); + } + if (i) + { + png_crc_read(png_ptr, png_ptr->zbuf, i); + } + + if (png_crc_error(png_ptr)) + { + if (((png_ptr->chunk_name[0] & 0x20) && /* Ancillary */ + !(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) || + (!(png_ptr->chunk_name[0] & 0x20) && /* Critical */ + (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_USE))) + { + png_chunk_warning(png_ptr, "CRC error"); + } + else + { + png_chunk_error(png_ptr, "CRC error"); + } + return (1); + } + + return (0); +} + +/* Compare the CRC stored in the PNG file with that calculated by libpng from + the data it has read thus far. */ +int /* PRIVATE */ +png_crc_error(png_structp png_ptr) +{ + png_byte crc_bytes[4]; + png_uint_32 crc; + int need_crc = 1; + + if (png_ptr->chunk_name[0] & 0x20) /* ancillary */ + { + if ((png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_MASK) == + (PNG_FLAG_CRC_ANCILLARY_USE | PNG_FLAG_CRC_ANCILLARY_NOWARN)) + need_crc = 0; + } + else /* critical */ + { + if (png_ptr->flags & PNG_FLAG_CRC_CRITICAL_IGNORE) + need_crc = 0; + } + + png_read_data(png_ptr, crc_bytes, 4); + + if (need_crc) + { + crc = png_get_uint_32(crc_bytes); + return ((int)(crc != png_ptr->crc)); + } + else + return (0); +} + +#if defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) || \ + defined(PNG_READ_iCCP_SUPPORTED) +/* + * Decompress trailing data in a chunk. The assumption is that chunkdata + * points at an allocated area holding the contents of a chunk with a + * trailing compressed part. What we get back is an allocated area + * holding the original prefix part and an uncompressed version of the + * trailing part (the malloc area passed in is freed). + */ +png_charp /* PRIVATE */ +png_decompress_chunk(png_structp png_ptr, int comp_type, + png_charp chunkdata, png_size_t chunklength, + png_size_t prefix_size, png_size_t *newlength) +{ + static PNG_CONST char msg[] = "Error decoding compressed text"; + png_charp text; + png_size_t text_size; + + if (comp_type == PNG_COMPRESSION_TYPE_BASE) + { + int ret = Z_OK; + png_ptr->zstream.next_in = (png_bytep)(chunkdata + prefix_size); + png_ptr->zstream.avail_in = (uInt)(chunklength - prefix_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + + text_size = 0; + text = NULL; + + while (png_ptr->zstream.avail_in) + { + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret != Z_OK && ret != Z_STREAM_END) + { + if (png_ptr->zstream.msg != NULL) + png_warning(png_ptr, png_ptr->zstream.msg); + else + png_warning(png_ptr, msg); + inflateReset(&png_ptr->zstream); + png_ptr->zstream.avail_in = 0; + + if (text == NULL) + { + text_size = prefix_size + png_sizeof(msg) + 1; + text = (png_charp)png_malloc_warn(png_ptr, text_size); + if (text == NULL) + { + png_free(png_ptr,chunkdata); + png_error(png_ptr,"Not enough memory to decompress chunk"); + } + png_memcpy(text, chunkdata, prefix_size); + } + + text[text_size - 1] = 0x00; + + /* Copy what we can of the error message into the text chunk */ + text_size = (png_size_t)(chunklength - (text - chunkdata) - 1); + text_size = png_sizeof(msg) > text_size ? text_size : + png_sizeof(msg); + png_memcpy(text + prefix_size, msg, text_size + 1); + break; + } + if (!png_ptr->zstream.avail_out || ret == Z_STREAM_END) + { + if (text == NULL) + { + text_size = prefix_size + + png_ptr->zbuf_size - png_ptr->zstream.avail_out; + text = (png_charp)png_malloc_warn(png_ptr, text_size + 1); + if (text == NULL) + { + png_free(png_ptr,chunkdata); + png_error(png_ptr,"Not enough memory to decompress chunk."); + } + png_memcpy(text + prefix_size, png_ptr->zbuf, + text_size - prefix_size); + png_memcpy(text, chunkdata, prefix_size); + *(text + text_size) = 0x00; + } + else + { + png_charp tmp; + + tmp = text; + text = (png_charp)png_malloc_warn(png_ptr, + (png_uint_32)(text_size + + png_ptr->zbuf_size - png_ptr->zstream.avail_out + 1)); + if (text == NULL) + { + png_free(png_ptr, tmp); + png_free(png_ptr, chunkdata); + png_error(png_ptr,"Not enough memory to decompress chunk.."); + } + png_memcpy(text, tmp, text_size); + png_free(png_ptr, tmp); + png_memcpy(text + text_size, png_ptr->zbuf, + (png_ptr->zbuf_size - png_ptr->zstream.avail_out)); + text_size += png_ptr->zbuf_size - png_ptr->zstream.avail_out; + *(text + text_size) = 0x00; + } + if (ret == Z_STREAM_END) + break; + else + { + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + } + } + if (ret != Z_STREAM_END) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char umsg[52]; + + if (ret == Z_BUF_ERROR) + png_snprintf(umsg, 52, + "Buffer error in compressed datastream in %s chunk", + png_ptr->chunk_name); + else if (ret == Z_DATA_ERROR) + png_snprintf(umsg, 52, + "Data error in compressed datastream in %s chunk", + png_ptr->chunk_name); + else + png_snprintf(umsg, 52, + "Incomplete compressed datastream in %s chunk", + png_ptr->chunk_name); + png_warning(png_ptr, umsg); +#else + png_warning(png_ptr, + "Incomplete compressed datastream in chunk other than IDAT"); +#endif + text_size=prefix_size; + if (text == NULL) + { + text = (png_charp)png_malloc_warn(png_ptr, text_size+1); + if (text == NULL) + { + png_free(png_ptr, chunkdata); + png_error(png_ptr,"Not enough memory for text."); + } + png_memcpy(text, chunkdata, prefix_size); + } + *(text + text_size) = 0x00; + } + + inflateReset(&png_ptr->zstream); + png_ptr->zstream.avail_in = 0; + + png_free(png_ptr, chunkdata); + chunkdata = text; + *newlength=text_size; + } + else /* if (comp_type != PNG_COMPRESSION_TYPE_BASE) */ + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char umsg[50]; + + png_snprintf(umsg, 50, + "Unknown zTXt compression type %d", comp_type); + png_warning(png_ptr, umsg); +#else + png_warning(png_ptr, "Unknown zTXt compression type"); +#endif + + *(chunkdata + prefix_size) = 0x00; + *newlength=prefix_size; + } + + return chunkdata; +} +#endif + +/* read and check the IDHR chunk */ +void /* PRIVATE */ +png_handle_IHDR(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[13]; + png_uint_32 width, height; + int bit_depth, color_type, compression_type, filter_type; + int interlace_type; + + png_debug(1, "in png_handle_IHDR\n"); + + if (png_ptr->mode & PNG_HAVE_IHDR) + png_error(png_ptr, "Out of place IHDR"); + + /* check the length */ + if (length != 13) + png_error(png_ptr, "Invalid IHDR chunk"); + + png_ptr->mode |= PNG_HAVE_IHDR; + + png_crc_read(png_ptr, buf, 13); + png_crc_finish(png_ptr, 0); + + width = png_get_uint_31(png_ptr, buf); + height = png_get_uint_31(png_ptr, buf + 4); + bit_depth = buf[8]; + color_type = buf[9]; + compression_type = buf[10]; + filter_type = buf[11]; + interlace_type = buf[12]; + + /* set internal variables */ + png_ptr->width = width; + png_ptr->height = height; + png_ptr->bit_depth = (png_byte)bit_depth; + png_ptr->interlaced = (png_byte)interlace_type; + png_ptr->color_type = (png_byte)color_type; +#if defined(PNG_MNG_FEATURES_SUPPORTED) + png_ptr->filter_type = (png_byte)filter_type; +#endif + png_ptr->compression_type = (png_byte)compression_type; + + /* find number of channels */ + switch (png_ptr->color_type) + { + case PNG_COLOR_TYPE_GRAY: + case PNG_COLOR_TYPE_PALETTE: + png_ptr->channels = 1; + break; + case PNG_COLOR_TYPE_RGB: + png_ptr->channels = 3; + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + png_ptr->channels = 2; + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + png_ptr->channels = 4; + break; + } + + /* set up other useful info */ + png_ptr->pixel_depth = (png_byte)(png_ptr->bit_depth * + png_ptr->channels); + png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth,png_ptr->width); + png_debug1(3,"bit_depth = %d\n", png_ptr->bit_depth); + png_debug1(3,"channels = %d\n", png_ptr->channels); + png_debug1(3,"rowbytes = %lu\n", png_ptr->rowbytes); + png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, + color_type, interlace_type, compression_type, filter_type); +} + +/* read and check the palette */ +void /* PRIVATE */ +png_handle_PLTE(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_color palette[PNG_MAX_PALETTE_LENGTH]; + int num, i; +#ifndef PNG_NO_POINTER_INDEXING + png_colorp pal_ptr; +#endif + + png_debug(1, "in png_handle_PLTE\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before PLTE"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid PLTE after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + png_error(png_ptr, "Duplicate PLTE chunk"); + + png_ptr->mode |= PNG_HAVE_PLTE; + + if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR)) + { + png_warning(png_ptr, + "Ignoring PLTE chunk in grayscale PNG"); + png_crc_finish(png_ptr, length); + return; + } +#if !defined(PNG_READ_OPT_PLTE_SUPPORTED) + if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) + { + png_crc_finish(png_ptr, length); + return; + } +#endif + + if (length > 3*PNG_MAX_PALETTE_LENGTH || length % 3) + { + if (png_ptr->color_type != PNG_COLOR_TYPE_PALETTE) + { + png_warning(png_ptr, "Invalid palette chunk"); + png_crc_finish(png_ptr, length); + return; + } + else + { + png_error(png_ptr, "Invalid palette chunk"); + } + } + + num = (int)length / 3; + +#ifndef PNG_NO_POINTER_INDEXING + for (i = 0, pal_ptr = palette; i < num; i++, pal_ptr++) + { + png_byte buf[3]; + + png_crc_read(png_ptr, buf, 3); + pal_ptr->red = buf[0]; + pal_ptr->green = buf[1]; + pal_ptr->blue = buf[2]; + } +#else + for (i = 0; i < num; i++) + { + png_byte buf[3]; + + png_crc_read(png_ptr, buf, 3); + /* don't depend upon png_color being any order */ + palette[i].red = buf[0]; + palette[i].green = buf[1]; + palette[i].blue = buf[2]; + } +#endif + + /* If we actually NEED the PLTE chunk (ie for a paletted image), we do + whatever the normal CRC configuration tells us. However, if we + have an RGB image, the PLTE can be considered ancillary, so + we will act as though it is. */ +#if !defined(PNG_READ_OPT_PLTE_SUPPORTED) + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) +#endif + { + png_crc_finish(png_ptr, 0); + } +#if !defined(PNG_READ_OPT_PLTE_SUPPORTED) + else if (png_crc_error(png_ptr)) /* Only if we have a CRC error */ + { + /* If we don't want to use the data from an ancillary chunk, + we have two options: an error abort, or a warning and we + ignore the data in this chunk (which should be OK, since + it's considered ancillary for a RGB or RGBA image). */ + if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_USE)) + { + if (png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN) + { + png_chunk_error(png_ptr, "CRC error"); + } + else + { + png_chunk_warning(png_ptr, "CRC error"); + return; + } + } + /* Otherwise, we (optionally) emit a warning and use the chunk. */ + else if (!(png_ptr->flags & PNG_FLAG_CRC_ANCILLARY_NOWARN)) + { + png_chunk_warning(png_ptr, "CRC error"); + } + } +#endif + + png_set_PLTE(png_ptr, info_ptr, palette, num); + +#if defined(PNG_READ_tRNS_SUPPORTED) + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) + { + if (png_ptr->num_trans > (png_uint_16)num) + { + png_warning(png_ptr, "Truncating incorrect tRNS chunk length"); + png_ptr->num_trans = (png_uint_16)num; + } + if (info_ptr->num_trans > (png_uint_16)num) + { + png_warning(png_ptr, "Truncating incorrect info tRNS chunk length"); + info_ptr->num_trans = (png_uint_16)num; + } + } + } +#endif + +} + +void /* PRIVATE */ +png_handle_IEND(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_debug(1, "in png_handle_IEND\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR) || !(png_ptr->mode & PNG_HAVE_IDAT)) + { + png_error(png_ptr, "No image in file"); + } + + png_ptr->mode |= (PNG_AFTER_IDAT | PNG_HAVE_IEND); + + if (length != 0) + { + png_warning(png_ptr, "Incorrect IEND chunk length"); + } + png_crc_finish(png_ptr, length); + + info_ptr =info_ptr; /* quiet compiler warnings about unused info_ptr */ +} + +#if defined(PNG_READ_gAMA_SUPPORTED) +void /* PRIVATE */ +png_handle_gAMA(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_fixed_point igamma; +#ifdef PNG_FLOATING_POINT_SUPPORTED + float file_gamma; +#endif + png_byte buf[4]; + + png_debug(1, "in png_handle_gAMA\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before gAMA"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid gAMA after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place gAMA chunk"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA) +#if defined(PNG_READ_sRGB_SUPPORTED) + && !(info_ptr->valid & PNG_INFO_sRGB) +#endif + ) + { + png_warning(png_ptr, "Duplicate gAMA chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 4) + { + png_warning(png_ptr, "Incorrect gAMA chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 4); + if (png_crc_finish(png_ptr, 0)) + return; + + igamma = (png_fixed_point)png_get_uint_32(buf); + /* check for zero gamma */ + if (igamma == 0) + { + png_warning(png_ptr, + "Ignoring gAMA chunk with gamma=0"); + return; + } + +#if defined(PNG_READ_sRGB_SUPPORTED) + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB)) + if (PNG_OUT_OF_RANGE(igamma, 45500L, 500)) + { + png_warning(png_ptr, + "Ignoring incorrect gAMA value when sRGB is also present"); +#ifndef PNG_NO_CONSOLE_IO + fprintf(stderr, "gamma = (%d/100000)\n", (int)igamma); +#endif + return; + } +#endif /* PNG_READ_sRGB_SUPPORTED */ + +#ifdef PNG_FLOATING_POINT_SUPPORTED + file_gamma = (float)igamma / (float)100000.0; +# ifdef PNG_READ_GAMMA_SUPPORTED + png_ptr->gamma = file_gamma; +# endif + png_set_gAMA(png_ptr, info_ptr, file_gamma); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_set_gAMA_fixed(png_ptr, info_ptr, igamma); +#endif +} +#endif + +#if defined(PNG_READ_sBIT_SUPPORTED) +void /* PRIVATE */ +png_handle_sBIT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_size_t truelen; + png_byte buf[4]; + + png_debug(1, "in png_handle_sBIT\n"); + + buf[0] = buf[1] = buf[2] = buf[3] = 0; + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sBIT"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sBIT after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + { + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place sBIT chunk"); + } + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sBIT)) + { + png_warning(png_ptr, "Duplicate sBIT chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + truelen = 3; + else + truelen = (png_size_t)png_ptr->channels; + + if (length != truelen || length > 4) + { + png_warning(png_ptr, "Incorrect sBIT chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, truelen); + if (png_crc_finish(png_ptr, 0)) + return; + + if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + { + png_ptr->sig_bit.red = buf[0]; + png_ptr->sig_bit.green = buf[1]; + png_ptr->sig_bit.blue = buf[2]; + png_ptr->sig_bit.alpha = buf[3]; + } + else + { + png_ptr->sig_bit.gray = buf[0]; + png_ptr->sig_bit.red = buf[0]; + png_ptr->sig_bit.green = buf[0]; + png_ptr->sig_bit.blue = buf[0]; + png_ptr->sig_bit.alpha = buf[1]; + } + png_set_sBIT(png_ptr, info_ptr, &(png_ptr->sig_bit)); +} +#endif + +#if defined(PNG_READ_cHRM_SUPPORTED) +void /* PRIVATE */ +png_handle_cHRM(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[4]; +#ifdef PNG_FLOATING_POINT_SUPPORTED + float white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y; +#endif + png_fixed_point int_x_white, int_y_white, int_x_red, int_y_red, int_x_green, + int_y_green, int_x_blue, int_y_blue; + + png_uint_32 uint_x, uint_y; + + png_debug(1, "in png_handle_cHRM\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before cHRM"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid cHRM after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Missing PLTE before cHRM"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM) +#if defined(PNG_READ_sRGB_SUPPORTED) + && !(info_ptr->valid & PNG_INFO_sRGB) +#endif + ) + { + png_warning(png_ptr, "Duplicate cHRM chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 32) + { + png_warning(png_ptr, "Incorrect cHRM chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 4); + uint_x = png_get_uint_32(buf); + + png_crc_read(png_ptr, buf, 4); + uint_y = png_get_uint_32(buf); + + if (uint_x > 80000L || uint_y > 80000L || + uint_x + uint_y > 100000L) + { + png_warning(png_ptr, "Invalid cHRM white point"); + png_crc_finish(png_ptr, 24); + return; + } + int_x_white = (png_fixed_point)uint_x; + int_y_white = (png_fixed_point)uint_y; + + png_crc_read(png_ptr, buf, 4); + uint_x = png_get_uint_32(buf); + + png_crc_read(png_ptr, buf, 4); + uint_y = png_get_uint_32(buf); + + if (uint_x + uint_y > 100000L) + { + png_warning(png_ptr, "Invalid cHRM red point"); + png_crc_finish(png_ptr, 16); + return; + } + int_x_red = (png_fixed_point)uint_x; + int_y_red = (png_fixed_point)uint_y; + + png_crc_read(png_ptr, buf, 4); + uint_x = png_get_uint_32(buf); + + png_crc_read(png_ptr, buf, 4); + uint_y = png_get_uint_32(buf); + + if (uint_x + uint_y > 100000L) + { + png_warning(png_ptr, "Invalid cHRM green point"); + png_crc_finish(png_ptr, 8); + return; + } + int_x_green = (png_fixed_point)uint_x; + int_y_green = (png_fixed_point)uint_y; + + png_crc_read(png_ptr, buf, 4); + uint_x = png_get_uint_32(buf); + + png_crc_read(png_ptr, buf, 4); + uint_y = png_get_uint_32(buf); + + if (uint_x + uint_y > 100000L) + { + png_warning(png_ptr, "Invalid cHRM blue point"); + png_crc_finish(png_ptr, 0); + return; + } + int_x_blue = (png_fixed_point)uint_x; + int_y_blue = (png_fixed_point)uint_y; + +#ifdef PNG_FLOATING_POINT_SUPPORTED + white_x = (float)int_x_white / (float)100000.0; + white_y = (float)int_y_white / (float)100000.0; + red_x = (float)int_x_red / (float)100000.0; + red_y = (float)int_y_red / (float)100000.0; + green_x = (float)int_x_green / (float)100000.0; + green_y = (float)int_y_green / (float)100000.0; + blue_x = (float)int_x_blue / (float)100000.0; + blue_y = (float)int_y_blue / (float)100000.0; +#endif + +#if defined(PNG_READ_sRGB_SUPPORTED) + if ((info_ptr != NULL) && (info_ptr->valid & PNG_INFO_sRGB)) + { + if (PNG_OUT_OF_RANGE(int_x_white, 31270, 1000) || + PNG_OUT_OF_RANGE(int_y_white, 32900, 1000) || + PNG_OUT_OF_RANGE(int_x_red, 64000L, 1000) || + PNG_OUT_OF_RANGE(int_y_red, 33000, 1000) || + PNG_OUT_OF_RANGE(int_x_green, 30000, 1000) || + PNG_OUT_OF_RANGE(int_y_green, 60000L, 1000) || + PNG_OUT_OF_RANGE(int_x_blue, 15000, 1000) || + PNG_OUT_OF_RANGE(int_y_blue, 6000, 1000)) + { + png_warning(png_ptr, + "Ignoring incorrect cHRM value when sRGB is also present"); +#ifndef PNG_NO_CONSOLE_IO +#ifdef PNG_FLOATING_POINT_SUPPORTED + fprintf(stderr,"wx=%f, wy=%f, rx=%f, ry=%f\n", + white_x, white_y, red_x, red_y); + fprintf(stderr,"gx=%f, gy=%f, bx=%f, by=%f\n", + green_x, green_y, blue_x, blue_y); +#else + fprintf(stderr,"wx=%ld, wy=%ld, rx=%ld, ry=%ld\n", + int_x_white, int_y_white, int_x_red, int_y_red); + fprintf(stderr,"gx=%ld, gy=%ld, bx=%ld, by=%ld\n", + int_x_green, int_y_green, int_x_blue, int_y_blue); +#endif +#endif /* PNG_NO_CONSOLE_IO */ + } + png_crc_finish(png_ptr, 0); + return; + } +#endif /* PNG_READ_sRGB_SUPPORTED */ + +#ifdef PNG_FLOATING_POINT_SUPPORTED + png_set_cHRM(png_ptr, info_ptr, + white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_set_cHRM_fixed(png_ptr, info_ptr, + int_x_white, int_y_white, int_x_red, int_y_red, int_x_green, + int_y_green, int_x_blue, int_y_blue); +#endif + if (png_crc_finish(png_ptr, 0)) + return; +} +#endif + +#if defined(PNG_READ_sRGB_SUPPORTED) +void /* PRIVATE */ +png_handle_sRGB(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + int intent; + png_byte buf[1]; + + png_debug(1, "in png_handle_sRGB\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sRGB"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sRGB after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place sRGB chunk"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sRGB)) + { + png_warning(png_ptr, "Duplicate sRGB chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 1) + { + png_warning(png_ptr, "Incorrect sRGB chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 1); + if (png_crc_finish(png_ptr, 0)) + return; + + intent = buf[0]; + /* check for bad intent */ + if (intent >= PNG_sRGB_INTENT_LAST) + { + png_warning(png_ptr, "Unknown sRGB intent"); + return; + } + +#if defined(PNG_READ_gAMA_SUPPORTED) && defined(PNG_READ_GAMMA_SUPPORTED) + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_gAMA)) + { + png_fixed_point igamma; +#ifdef PNG_FIXED_POINT_SUPPORTED + igamma=info_ptr->int_gamma; +#else +# ifdef PNG_FLOATING_POINT_SUPPORTED + igamma=(png_fixed_point)(info_ptr->gamma * 100000.); +# endif +#endif + if (PNG_OUT_OF_RANGE(igamma, 45500L, 500)) + { + png_warning(png_ptr, + "Ignoring incorrect gAMA value when sRGB is also present"); +#ifndef PNG_NO_CONSOLE_IO +# ifdef PNG_FIXED_POINT_SUPPORTED + fprintf(stderr,"incorrect gamma=(%d/100000)\n",(int)png_ptr->int_gamma); +# else +# ifdef PNG_FLOATING_POINT_SUPPORTED + fprintf(stderr,"incorrect gamma=%f\n",png_ptr->gamma); +# endif +# endif +#endif + } + } +#endif /* PNG_READ_gAMA_SUPPORTED */ + +#ifdef PNG_READ_cHRM_SUPPORTED +#ifdef PNG_FIXED_POINT_SUPPORTED + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_cHRM)) + if (PNG_OUT_OF_RANGE(info_ptr->int_x_white, 31270, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_white, 32900, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_x_red, 64000L, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_red, 33000, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_x_green, 30000, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_green, 60000L, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_x_blue, 15000, 1000) || + PNG_OUT_OF_RANGE(info_ptr->int_y_blue, 6000, 1000)) + { + png_warning(png_ptr, + "Ignoring incorrect cHRM value when sRGB is also present"); + } +#endif /* PNG_FIXED_POINT_SUPPORTED */ +#endif /* PNG_READ_cHRM_SUPPORTED */ + + png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, intent); +} +#endif /* PNG_READ_sRGB_SUPPORTED */ + +#if defined(PNG_READ_iCCP_SUPPORTED) +void /* PRIVATE */ +png_handle_iCCP(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +/* Note: this does not properly handle chunks that are > 64K under DOS */ +{ + png_charp chunkdata; + png_byte compression_type; + png_bytep pC; + png_charp profile; + png_uint_32 skip = 0; + png_uint_32 profile_size, profile_length; + png_size_t slength, prefix_length, data_length; + + png_debug(1, "in png_handle_iCCP\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before iCCP"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid iCCP after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->mode & PNG_HAVE_PLTE) + /* Should be an error, but we can cope with it */ + png_warning(png_ptr, "Out of place iCCP chunk"); + + if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_iCCP)) + { + png_warning(png_ptr, "Duplicate iCCP chunk"); + png_crc_finish(png_ptr, length); + return; + } + +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "iCCP chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + chunkdata = (png_charp)png_malloc(png_ptr, length + 1); + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)chunkdata, slength); + + if (png_crc_finish(png_ptr, skip)) + { + png_free(png_ptr, chunkdata); + return; + } + + chunkdata[slength] = 0x00; + + for (profile = chunkdata; *profile; profile++) + /* empty loop to find end of name */ ; + + ++profile; + + /* there should be at least one zero (the compression type byte) + following the separator, and we should be on it */ + if ( profile >= chunkdata + slength - 1) + { + png_free(png_ptr, chunkdata); + png_warning(png_ptr, "Malformed iCCP chunk"); + return; + } + + /* compression_type should always be zero */ + compression_type = *profile++; + if (compression_type) + { + png_warning(png_ptr, "Ignoring nonzero compression type in iCCP chunk"); + compression_type=0x00; /* Reset it to zero (libpng-1.0.6 through 1.0.8 + wrote nonzero) */ + } + + prefix_length = profile - chunkdata; + chunkdata = png_decompress_chunk(png_ptr, compression_type, chunkdata, + slength, prefix_length, &data_length); + + profile_length = data_length - prefix_length; + + if ( prefix_length > data_length || profile_length < 4) + { + png_free(png_ptr, chunkdata); + png_warning(png_ptr, "Profile size field missing from iCCP chunk"); + return; + } + + /* Check the profile_size recorded in the first 32 bits of the ICC profile */ + pC = (png_bytep)(chunkdata+prefix_length); + profile_size = ((*(pC ))<<24) | + ((*(pC+1))<<16) | + ((*(pC+2))<< 8) | + ((*(pC+3)) ); + + if(profile_size < profile_length) + profile_length = profile_size; + + if(profile_size > profile_length) + { + png_free(png_ptr, chunkdata); + png_warning(png_ptr, "Ignoring truncated iCCP profile."); + return; + } + + png_set_iCCP(png_ptr, info_ptr, chunkdata, compression_type, + chunkdata + prefix_length, profile_length); + png_free(png_ptr, chunkdata); +} +#endif /* PNG_READ_iCCP_SUPPORTED */ + +#if defined(PNG_READ_sPLT_SUPPORTED) +void /* PRIVATE */ +png_handle_sPLT(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +/* Note: this does not properly handle chunks that are > 64K under DOS */ +{ + png_bytep chunkdata; + png_bytep entry_start; + png_sPLT_t new_palette; +#ifdef PNG_NO_POINTER_INDEXING + png_sPLT_entryp pp; +#endif + int data_length, entry_size, i; + png_uint_32 skip = 0; + png_size_t slength; + + png_debug(1, "in png_handle_sPLT\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sPLT"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sPLT after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "sPLT chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + chunkdata = (png_bytep)png_malloc(png_ptr, length + 1); + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)chunkdata, slength); + + if (png_crc_finish(png_ptr, skip)) + { + png_free(png_ptr, chunkdata); + return; + } + + chunkdata[slength] = 0x00; + + for (entry_start = chunkdata; *entry_start; entry_start++) + /* empty loop to find end of name */ ; + ++entry_start; + + /* a sample depth should follow the separator, and we should be on it */ + if (entry_start > chunkdata + slength - 2) + { + png_free(png_ptr, chunkdata); + png_warning(png_ptr, "malformed sPLT chunk"); + return; + } + + new_palette.depth = *entry_start++; + entry_size = (new_palette.depth == 8 ? 6 : 10); + data_length = (slength - (entry_start - chunkdata)); + + /* integrity-check the data length */ + if (data_length % entry_size) + { + png_free(png_ptr, chunkdata); + png_warning(png_ptr, "sPLT chunk has bad length"); + return; + } + + new_palette.nentries = (png_int_32) ( data_length / entry_size); + if ((png_uint_32) new_palette.nentries > (png_uint_32) (PNG_SIZE_MAX / + png_sizeof(png_sPLT_entry))) + { + png_warning(png_ptr, "sPLT chunk too long"); + return; + } + new_palette.entries = (png_sPLT_entryp)png_malloc_warn( + png_ptr, new_palette.nentries * png_sizeof(png_sPLT_entry)); + if (new_palette.entries == NULL) + { + png_warning(png_ptr, "sPLT chunk requires too much memory"); + return; + } + +#ifndef PNG_NO_POINTER_INDEXING + for (i = 0; i < new_palette.nentries; i++) + { + png_sPLT_entryp pp = new_palette.entries + i; + + if (new_palette.depth == 8) + { + pp->red = *entry_start++; + pp->green = *entry_start++; + pp->blue = *entry_start++; + pp->alpha = *entry_start++; + } + else + { + pp->red = png_get_uint_16(entry_start); entry_start += 2; + pp->green = png_get_uint_16(entry_start); entry_start += 2; + pp->blue = png_get_uint_16(entry_start); entry_start += 2; + pp->alpha = png_get_uint_16(entry_start); entry_start += 2; + } + pp->frequency = png_get_uint_16(entry_start); entry_start += 2; + } +#else + pp = new_palette.entries; + for (i = 0; i < new_palette.nentries; i++) + { + + if (new_palette.depth == 8) + { + pp[i].red = *entry_start++; + pp[i].green = *entry_start++; + pp[i].blue = *entry_start++; + pp[i].alpha = *entry_start++; + } + else + { + pp[i].red = png_get_uint_16(entry_start); entry_start += 2; + pp[i].green = png_get_uint_16(entry_start); entry_start += 2; + pp[i].blue = png_get_uint_16(entry_start); entry_start += 2; + pp[i].alpha = png_get_uint_16(entry_start); entry_start += 2; + } + pp->frequency = png_get_uint_16(entry_start); entry_start += 2; + } +#endif + + /* discard all chunk data except the name and stash that */ + new_palette.name = (png_charp)chunkdata; + + png_set_sPLT(png_ptr, info_ptr, &new_palette, 1); + + png_free(png_ptr, chunkdata); + png_free(png_ptr, new_palette.entries); +} +#endif /* PNG_READ_sPLT_SUPPORTED */ + +#if defined(PNG_READ_tRNS_SUPPORTED) +void /* PRIVATE */ +png_handle_tRNS(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte readbuf[PNG_MAX_PALETTE_LENGTH]; + int bit_mask; + + png_debug(1, "in png_handle_tRNS\n"); + + /* For non-indexed color, mask off any bits in the tRNS value that + * exceed the bit depth. Some creators were writing extra bits there. + * This is not needed for indexed color. */ + bit_mask = (1 << png_ptr->bit_depth) - 1; + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before tRNS"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid tRNS after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tRNS)) + { + png_warning(png_ptr, "Duplicate tRNS chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + { + png_byte buf[2]; + + if (length != 2) + { + png_warning(png_ptr, "Incorrect tRNS chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 2); + png_ptr->num_trans = 1; + png_ptr->trans_values.gray = png_get_uint_16(buf) & bit_mask; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + png_byte buf[6]; + + if (length != 6) + { + png_warning(png_ptr, "Incorrect tRNS chunk length"); + png_crc_finish(png_ptr, length); + return; + } + png_crc_read(png_ptr, buf, (png_size_t)length); + png_ptr->num_trans = 1; + png_ptr->trans_values.red = png_get_uint_16(buf) & bit_mask; + png_ptr->trans_values.green = png_get_uint_16(buf + 2) & bit_mask; + png_ptr->trans_values.blue = png_get_uint_16(buf + 4) & bit_mask; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (!(png_ptr->mode & PNG_HAVE_PLTE)) + { + /* Should be an error, but we can cope with it. */ + png_warning(png_ptr, "Missing PLTE before tRNS"); + } + if (length > (png_uint_32)png_ptr->num_palette || + length > PNG_MAX_PALETTE_LENGTH) + { + png_warning(png_ptr, "Incorrect tRNS chunk length"); + png_crc_finish(png_ptr, length); + return; + } + if (length == 0) + { + png_warning(png_ptr, "Zero length tRNS chunk"); + png_crc_finish(png_ptr, length); + return; + } + png_crc_read(png_ptr, readbuf, (png_size_t)length); + png_ptr->num_trans = (png_uint_16)length; + } + else + { + png_warning(png_ptr, "tRNS chunk not allowed with alpha channel"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_crc_finish(png_ptr, 0)) + { + png_ptr->num_trans = 0; + return; + } + + png_set_tRNS(png_ptr, info_ptr, readbuf, png_ptr->num_trans, + &(png_ptr->trans_values)); +} +#endif + +#if defined(PNG_READ_bKGD_SUPPORTED) +void /* PRIVATE */ +png_handle_bKGD(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_size_t truelen; + png_byte buf[6]; + + png_debug(1, "in png_handle_bKGD\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before bKGD"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid bKGD after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE && + !(png_ptr->mode & PNG_HAVE_PLTE)) + { + png_warning(png_ptr, "Missing PLTE before bKGD"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_bKGD)) + { + png_warning(png_ptr, "Duplicate bKGD chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + truelen = 1; + else if (png_ptr->color_type & PNG_COLOR_MASK_COLOR) + truelen = 6; + else + truelen = 2; + + if (length != truelen) + { + png_warning(png_ptr, "Incorrect bKGD chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, truelen); + if (png_crc_finish(png_ptr, 0)) + return; + + /* We convert the index value into RGB components so that we can allow + * arbitrary RGB values for background when we have transparency, and + * so it is easy to determine the RGB values of the background color + * from the info_ptr struct. */ + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_ptr->background.index = buf[0]; + if(info_ptr->num_palette) + { + if(buf[0] > info_ptr->num_palette) + { + png_warning(png_ptr, "Incorrect bKGD chunk index value"); + return; + } + png_ptr->background.red = + (png_uint_16)png_ptr->palette[buf[0]].red; + png_ptr->background.green = + (png_uint_16)png_ptr->palette[buf[0]].green; + png_ptr->background.blue = + (png_uint_16)png_ptr->palette[buf[0]].blue; + } + } + else if (!(png_ptr->color_type & PNG_COLOR_MASK_COLOR)) /* GRAY */ + { + png_ptr->background.red = + png_ptr->background.green = + png_ptr->background.blue = + png_ptr->background.gray = png_get_uint_16(buf); + } + else + { + png_ptr->background.red = png_get_uint_16(buf); + png_ptr->background.green = png_get_uint_16(buf + 2); + png_ptr->background.blue = png_get_uint_16(buf + 4); + } + + png_set_bKGD(png_ptr, info_ptr, &(png_ptr->background)); +} +#endif + +#if defined(PNG_READ_hIST_SUPPORTED) +void /* PRIVATE */ +png_handle_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + unsigned int num, i; + png_uint_16 readbuf[PNG_MAX_PALETTE_LENGTH]; + + png_debug(1, "in png_handle_hIST\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before hIST"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid hIST after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (!(png_ptr->mode & PNG_HAVE_PLTE)) + { + png_warning(png_ptr, "Missing PLTE before hIST"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_hIST)) + { + png_warning(png_ptr, "Duplicate hIST chunk"); + png_crc_finish(png_ptr, length); + return; + } + + num = length / 2 ; + if (num != (unsigned int) png_ptr->num_palette || num > + (unsigned int) PNG_MAX_PALETTE_LENGTH) + { + png_warning(png_ptr, "Incorrect hIST chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + for (i = 0; i < num; i++) + { + png_byte buf[2]; + + png_crc_read(png_ptr, buf, 2); + readbuf[i] = png_get_uint_16(buf); + } + + if (png_crc_finish(png_ptr, 0)) + return; + + png_set_hIST(png_ptr, info_ptr, readbuf); +} +#endif + +#if defined(PNG_READ_pHYs_SUPPORTED) +void /* PRIVATE */ +png_handle_pHYs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[9]; + png_uint_32 res_x, res_y; + int unit_type; + + png_debug(1, "in png_handle_pHYs\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before pHYs"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid pHYs after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pHYs)) + { + png_warning(png_ptr, "Duplicate pHYs chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 9) + { + png_warning(png_ptr, "Incorrect pHYs chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 9); + if (png_crc_finish(png_ptr, 0)) + return; + + res_x = png_get_uint_32(buf); + res_y = png_get_uint_32(buf + 4); + unit_type = buf[8]; + png_set_pHYs(png_ptr, info_ptr, res_x, res_y, unit_type); +} +#endif + +#if defined(PNG_READ_oFFs_SUPPORTED) +void /* PRIVATE */ +png_handle_oFFs(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[9]; + png_int_32 offset_x, offset_y; + int unit_type; + + png_debug(1, "in png_handle_oFFs\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before oFFs"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid oFFs after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_oFFs)) + { + png_warning(png_ptr, "Duplicate oFFs chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (length != 9) + { + png_warning(png_ptr, "Incorrect oFFs chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 9); + if (png_crc_finish(png_ptr, 0)) + return; + + offset_x = png_get_int_32(buf); + offset_y = png_get_int_32(buf + 4); + unit_type = buf[8]; + png_set_oFFs(png_ptr, info_ptr, offset_x, offset_y, unit_type); +} +#endif + +#if defined(PNG_READ_pCAL_SUPPORTED) +/* read the pCAL chunk (described in the PNG Extensions document) */ +void /* PRIVATE */ +png_handle_pCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_charp purpose; + png_int_32 X0, X1; + png_byte type, nparams; + png_charp buf, units, endptr; + png_charpp params; + png_size_t slength; + int i; + + png_debug(1, "in png_handle_pCAL\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before pCAL"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid pCAL after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_pCAL)) + { + png_warning(png_ptr, "Duplicate pCAL chunk"); + png_crc_finish(png_ptr, length); + return; + } + + png_debug1(2, "Allocating and reading pCAL chunk data (%lu bytes)\n", + length + 1); + purpose = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (purpose == NULL) + { + png_warning(png_ptr, "No memory for pCAL purpose."); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)purpose, slength); + + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, purpose); + return; + } + + purpose[slength] = 0x00; /* null terminate the last string */ + + png_debug(3, "Finding end of pCAL purpose string\n"); + for (buf = purpose; *buf; buf++) + /* empty loop */ ; + + endptr = purpose + slength; + + /* We need to have at least 12 bytes after the purpose string + in order to get the parameter information. */ + if (endptr <= buf + 12) + { + png_warning(png_ptr, "Invalid pCAL data"); + png_free(png_ptr, purpose); + return; + } + + png_debug(3, "Reading pCAL X0, X1, type, nparams, and units\n"); + X0 = png_get_int_32((png_bytep)buf+1); + X1 = png_get_int_32((png_bytep)buf+5); + type = buf[9]; + nparams = buf[10]; + units = buf + 11; + + png_debug(3, "Checking pCAL equation type and number of parameters\n"); + /* Check that we have the right number of parameters for known + equation types. */ + if ((type == PNG_EQUATION_LINEAR && nparams != 2) || + (type == PNG_EQUATION_BASE_E && nparams != 3) || + (type == PNG_EQUATION_ARBITRARY && nparams != 3) || + (type == PNG_EQUATION_HYPERBOLIC && nparams != 4)) + { + png_warning(png_ptr, "Invalid pCAL parameters for equation type"); + png_free(png_ptr, purpose); + return; + } + else if (type >= PNG_EQUATION_LAST) + { + png_warning(png_ptr, "Unrecognized equation type for pCAL chunk"); + } + + for (buf = units; *buf; buf++) + /* Empty loop to move past the units string. */ ; + + png_debug(3, "Allocating pCAL parameters array\n"); + params = (png_charpp)png_malloc_warn(png_ptr, (png_uint_32)(nparams + *png_sizeof(png_charp))) ; + if (params == NULL) + { + png_free(png_ptr, purpose); + png_warning(png_ptr, "No memory for pCAL params."); + return; + } + + /* Get pointers to the start of each parameter string. */ + for (i = 0; i < (int)nparams; i++) + { + buf++; /* Skip the null string terminator from previous parameter. */ + + png_debug1(3, "Reading pCAL parameter %d\n", i); + for (params[i] = buf; buf <= endptr && *buf != 0x00; buf++) + /* Empty loop to move past each parameter string */ ; + + /* Make sure we haven't run out of data yet */ + if (buf > endptr) + { + png_warning(png_ptr, "Invalid pCAL data"); + png_free(png_ptr, purpose); + png_free(png_ptr, params); + return; + } + } + + png_set_pCAL(png_ptr, info_ptr, purpose, X0, X1, type, nparams, + units, params); + + png_free(png_ptr, purpose); + png_free(png_ptr, params); +} +#endif + +#if defined(PNG_READ_sCAL_SUPPORTED) +/* read the sCAL chunk */ +void /* PRIVATE */ +png_handle_sCAL(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_charp buffer, ep; +#ifdef PNG_FLOATING_POINT_SUPPORTED + double width, height; + png_charp vp; +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + png_charp swidth, sheight; +#endif +#endif + png_size_t slength; + + png_debug(1, "in png_handle_sCAL\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before sCAL"); + else if (png_ptr->mode & PNG_HAVE_IDAT) + { + png_warning(png_ptr, "Invalid sCAL after IDAT"); + png_crc_finish(png_ptr, length); + return; + } + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_sCAL)) + { + png_warning(png_ptr, "Duplicate sCAL chunk"); + png_crc_finish(png_ptr, length); + return; + } + + png_debug1(2, "Allocating and reading sCAL chunk data (%lu bytes)\n", + length + 1); + buffer = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (buffer == NULL) + { + png_warning(png_ptr, "Out of memory while processing sCAL chunk"); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)buffer, slength); + + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, buffer); + return; + } + + buffer[slength] = 0x00; /* null terminate the last string */ + + ep = buffer + 1; /* skip unit byte */ + +#ifdef PNG_FLOATING_POINT_SUPPORTED + width = png_strtod(png_ptr, ep, &vp); + if (*vp) + { + png_warning(png_ptr, "malformed width string in sCAL chunk"); + return; + } +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + swidth = (png_charp)png_malloc_warn(png_ptr, png_strlen(ep) + 1); + if (swidth == NULL) + { + png_warning(png_ptr, "Out of memory while processing sCAL chunk width"); + return; + } + png_memcpy(swidth, ep, (png_size_t)png_strlen(ep)); +#endif +#endif + + for (ep = buffer; *ep; ep++) + /* empty loop */ ; + ep++; + + if (buffer + slength < ep) + { + png_warning(png_ptr, "Truncated sCAL chunk"); +#if defined(PNG_FIXED_POINT_SUPPORTED) && \ + !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, swidth); +#endif + png_free(png_ptr, buffer); + return; + } + +#ifdef PNG_FLOATING_POINT_SUPPORTED + height = png_strtod(png_ptr, ep, &vp); + if (*vp) + { + png_warning(png_ptr, "malformed height string in sCAL chunk"); + return; + } +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + sheight = (png_charp)png_malloc_warn(png_ptr, png_strlen(ep) + 1); + if (swidth == NULL) + { + png_warning(png_ptr, "Out of memory while processing sCAL chunk height"); + return; + } + png_memcpy(sheight, ep, (png_size_t)png_strlen(ep)); +#endif +#endif + + if (buffer + slength < ep +#ifdef PNG_FLOATING_POINT_SUPPORTED + || width <= 0. || height <= 0. +#endif + ) + { + png_warning(png_ptr, "Invalid sCAL data"); + png_free(png_ptr, buffer); +#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, swidth); + png_free(png_ptr, sheight); +#endif + return; + } + + +#ifdef PNG_FLOATING_POINT_SUPPORTED + png_set_sCAL(png_ptr, info_ptr, buffer[0], width, height); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + png_set_sCAL_s(png_ptr, info_ptr, buffer[0], swidth, sheight); +#endif +#endif + + png_free(png_ptr, buffer); +#if defined(PNG_FIXED_POINT_SUPPORTED) && !defined(PNG_FLOATING_POINT_SUPPORTED) + png_free(png_ptr, swidth); + png_free(png_ptr, sheight); +#endif +} +#endif + +#if defined(PNG_READ_tIME_SUPPORTED) +void /* PRIVATE */ +png_handle_tIME(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_byte buf[7]; + png_time mod_time; + + png_debug(1, "in png_handle_tIME\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Out of place tIME chunk"); + else if (info_ptr != NULL && (info_ptr->valid & PNG_INFO_tIME)) + { + png_warning(png_ptr, "Duplicate tIME chunk"); + png_crc_finish(png_ptr, length); + return; + } + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + + if (length != 7) + { + png_warning(png_ptr, "Incorrect tIME chunk length"); + png_crc_finish(png_ptr, length); + return; + } + + png_crc_read(png_ptr, buf, 7); + if (png_crc_finish(png_ptr, 0)) + return; + + mod_time.second = buf[6]; + mod_time.minute = buf[5]; + mod_time.hour = buf[4]; + mod_time.day = buf[3]; + mod_time.month = buf[2]; + mod_time.year = png_get_uint_16(buf); + + png_set_tIME(png_ptr, info_ptr, &mod_time); +} +#endif + +#if defined(PNG_READ_tEXt_SUPPORTED) +/* Note: this does not properly handle chunks that are > 64K under DOS */ +void /* PRIVATE */ +png_handle_tEXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_textp text_ptr; + png_charp key; + png_charp text; + png_uint_32 skip = 0; + png_size_t slength; + int ret; + + png_debug(1, "in png_handle_tEXt\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before tEXt"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "tEXt chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + + key = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (key == NULL) + { + png_warning(png_ptr, "No memory to process text chunk."); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)key, slength); + + if (png_crc_finish(png_ptr, skip)) + { + png_free(png_ptr, key); + return; + } + + key[slength] = 0x00; + + for (text = key; *text; text++) + /* empty loop to find end of key */ ; + + if (text != key + slength) + text++; + + text_ptr = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)png_sizeof(png_text)); + if (text_ptr == NULL) + { + png_warning(png_ptr, "Not enough memory to process text chunk."); + png_free(png_ptr, key); + return; + } + text_ptr->compression = PNG_TEXT_COMPRESSION_NONE; + text_ptr->key = key; +#ifdef PNG_iTXt_SUPPORTED + text_ptr->lang = NULL; + text_ptr->lang_key = NULL; + text_ptr->itxt_length = 0; +#endif + text_ptr->text = text; + text_ptr->text_length = png_strlen(text); + + ret=png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, key); + png_free(png_ptr, text_ptr); + if (ret) + png_warning(png_ptr, "Insufficient memory to process text chunk."); +} +#endif + +#if defined(PNG_READ_zTXt_SUPPORTED) +/* note: this does not correctly handle chunks that are > 64K under DOS */ +void /* PRIVATE */ +png_handle_zTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_textp text_ptr; + png_charp chunkdata; + png_charp text; + int comp_type; + int ret; + png_size_t slength, prefix_len, data_len; + + png_debug(1, "in png_handle_zTXt\n"); + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before zTXt"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + +#ifdef PNG_MAX_MALLOC_64K + /* We will no doubt have problems with chunks even half this size, but + there is no hard and fast rule to tell us where to stop. */ + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr,"zTXt chunk too large to fit in memory"); + png_crc_finish(png_ptr, length); + return; + } +#endif + + chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (chunkdata == NULL) + { + png_warning(png_ptr,"Out of memory processing zTXt chunk."); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)chunkdata, slength); + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, chunkdata); + return; + } + + chunkdata[slength] = 0x00; + + for (text = chunkdata; *text; text++) + /* empty loop */ ; + + /* zTXt must have some text after the chunkdataword */ + if (text >= chunkdata + slength - 2) + { + png_warning(png_ptr, "Truncated zTXt chunk"); + png_free(png_ptr, chunkdata); + return; + } + else + { + comp_type = *(++text); + if (comp_type != PNG_TEXT_COMPRESSION_zTXt) + { + png_warning(png_ptr, "Unknown compression type in zTXt chunk"); + comp_type = PNG_TEXT_COMPRESSION_zTXt; + } + text++; /* skip the compression_method byte */ + } + prefix_len = text - chunkdata; + + chunkdata = (png_charp)png_decompress_chunk(png_ptr, comp_type, chunkdata, + (png_size_t)length, prefix_len, &data_len); + + text_ptr = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)png_sizeof(png_text)); + if (text_ptr == NULL) + { + png_warning(png_ptr,"Not enough memory to process zTXt chunk."); + png_free(png_ptr, chunkdata); + return; + } + text_ptr->compression = comp_type; + text_ptr->key = chunkdata; +#ifdef PNG_iTXt_SUPPORTED + text_ptr->lang = NULL; + text_ptr->lang_key = NULL; + text_ptr->itxt_length = 0; +#endif + text_ptr->text = chunkdata + prefix_len; + text_ptr->text_length = data_len; + + ret=png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, text_ptr); + png_free(png_ptr, chunkdata); + if (ret) + png_error(png_ptr, "Insufficient memory to store zTXt chunk."); +} +#endif + +#if defined(PNG_READ_iTXt_SUPPORTED) +/* note: this does not correctly handle chunks that are > 64K under DOS */ +void /* PRIVATE */ +png_handle_iTXt(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_textp text_ptr; + png_charp chunkdata; + png_charp key, lang, text, lang_key; + int comp_flag; + int comp_type = 0; + int ret; + png_size_t slength, prefix_len, data_len; + + png_debug(1, "in png_handle_iTXt\n"); + + if (!(png_ptr->mode & PNG_HAVE_IHDR)) + png_error(png_ptr, "Missing IHDR before iTXt"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + png_ptr->mode |= PNG_AFTER_IDAT; + +#ifdef PNG_MAX_MALLOC_64K + /* We will no doubt have problems with chunks even half this size, but + there is no hard and fast rule to tell us where to stop. */ + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr,"iTXt chunk too large to fit in memory"); + png_crc_finish(png_ptr, length); + return; + } +#endif + + chunkdata = (png_charp)png_malloc_warn(png_ptr, length + 1); + if (chunkdata == NULL) + { + png_warning(png_ptr, "No memory to process iTXt chunk."); + return; + } + slength = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)chunkdata, slength); + if (png_crc_finish(png_ptr, 0)) + { + png_free(png_ptr, chunkdata); + return; + } + + chunkdata[slength] = 0x00; + + for (lang = chunkdata; *lang; lang++) + /* empty loop */ ; + lang++; /* skip NUL separator */ + + /* iTXt must have a language tag (possibly empty), two compression bytes, + translated keyword (possibly empty), and possibly some text after the + keyword */ + + if (lang >= chunkdata + slength - 3) + { + png_warning(png_ptr, "Truncated iTXt chunk"); + png_free(png_ptr, chunkdata); + return; + } + else + { + comp_flag = *lang++; + comp_type = *lang++; + } + + for (lang_key = lang; *lang_key; lang_key++) + /* empty loop */ ; + lang_key++; /* skip NUL separator */ + + if (lang_key >= chunkdata + slength) + { + png_warning(png_ptr, "Truncated iTXt chunk"); + png_free(png_ptr, chunkdata); + return; + } + + for (text = lang_key; *text; text++) + /* empty loop */ ; + text++; /* skip NUL separator */ + if (text >= chunkdata + slength) + { + png_warning(png_ptr, "Malformed iTXt chunk"); + png_free(png_ptr, chunkdata); + return; + } + + prefix_len = text - chunkdata; + + key=chunkdata; + if (comp_flag) + chunkdata = png_decompress_chunk(png_ptr, comp_type, chunkdata, + (size_t)length, prefix_len, &data_len); + else + data_len=png_strlen(chunkdata + prefix_len); + text_ptr = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)png_sizeof(png_text)); + if (text_ptr == NULL) + { + png_warning(png_ptr,"Not enough memory to process iTXt chunk."); + png_free(png_ptr, chunkdata); + return; + } + text_ptr->compression = (int)comp_flag + 1; + text_ptr->lang_key = chunkdata+(lang_key-key); + text_ptr->lang = chunkdata+(lang-key); + text_ptr->itxt_length = data_len; + text_ptr->text_length = 0; + text_ptr->key = chunkdata; + text_ptr->text = chunkdata + prefix_len; + + ret=png_set_text_2(png_ptr, info_ptr, text_ptr, 1); + + png_free(png_ptr, text_ptr); + png_free(png_ptr, chunkdata); + if (ret) + png_error(png_ptr, "Insufficient memory to store iTXt chunk."); +} +#endif + +/* This function is called when we haven't found a handler for a + chunk. If there isn't a problem with the chunk itself (ie bad + chunk name, CRC, or a critical chunk), the chunk is silently ignored + -- unless the PNG_FLAG_UNKNOWN_CHUNKS_SUPPORTED flag is on in which + case it will be saved away to be written out later. */ +void /* PRIVATE */ +png_handle_unknown(png_structp png_ptr, png_infop info_ptr, png_uint_32 length) +{ + png_uint_32 skip = 0; + + png_debug(1, "in png_handle_unknown\n"); + + if (png_ptr->mode & PNG_HAVE_IDAT) + { +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_CONST PNG_IDAT; +#endif + if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) /* not an IDAT */ + png_ptr->mode |= PNG_AFTER_IDAT; + } + + png_check_chunk_name(png_ptr, png_ptr->chunk_name); + + if (!(png_ptr->chunk_name[0] & 0x20)) + { +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != + PNG_HANDLE_CHUNK_ALWAYS +#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) + && png_ptr->read_user_chunk_fn == NULL +#endif + ) +#endif + png_chunk_error(png_ptr, "unknown critical chunk"); + } + +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + if ((png_ptr->flags & PNG_FLAG_KEEP_UNKNOWN_CHUNKS) || + (png_ptr->read_user_chunk_fn != NULL)) + { +#ifdef PNG_MAX_MALLOC_64K + if (length > (png_uint_32)65535L) + { + png_warning(png_ptr, "unknown chunk too large to fit in memory"); + skip = length - (png_uint_32)65535L; + length = (png_uint_32)65535L; + } +#endif + png_memcpy((png_charp)png_ptr->unknown_chunk.name, + (png_charp)png_ptr->chunk_name, + png_sizeof(png_ptr->unknown_chunk.name)); + png_ptr->unknown_chunk.name[png_sizeof(png_ptr->unknown_chunk.name)-1] = '\0'; + png_ptr->unknown_chunk.data = (png_bytep)png_malloc(png_ptr, length); + png_ptr->unknown_chunk.size = (png_size_t)length; + png_crc_read(png_ptr, (png_bytep)png_ptr->unknown_chunk.data, length); +#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) + if(png_ptr->read_user_chunk_fn != NULL) + { + /* callback to user unknown chunk handler */ + int ret; + ret = (*(png_ptr->read_user_chunk_fn)) + (png_ptr, &png_ptr->unknown_chunk); + if (ret < 0) + png_chunk_error(png_ptr, "error in user chunk"); + if (ret == 0) + { + if (!(png_ptr->chunk_name[0] & 0x20)) + if(png_handle_as_unknown(png_ptr, png_ptr->chunk_name) != + PNG_HANDLE_CHUNK_ALWAYS) + png_chunk_error(png_ptr, "unknown critical chunk"); + png_set_unknown_chunks(png_ptr, info_ptr, + &png_ptr->unknown_chunk, 1); + } + } +#else + png_set_unknown_chunks(png_ptr, info_ptr, &png_ptr->unknown_chunk, 1); +#endif + png_free(png_ptr, png_ptr->unknown_chunk.data); + png_ptr->unknown_chunk.data = NULL; + } + else +#endif + skip = length; + + png_crc_finish(png_ptr, skip); + +#if !defined(PNG_READ_USER_CHUNKS_SUPPORTED) + info_ptr = info_ptr; /* quiet compiler warnings about unused info_ptr */ +#endif +} + +/* This function is called to verify that a chunk name is valid. + This function can't have the "critical chunk check" incorporated + into it, since in the future we will need to be able to call user + functions to handle unknown critical chunks after we check that + the chunk name itself is valid. */ + +#define isnonalpha(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) + +void /* PRIVATE */ +png_check_chunk_name(png_structp png_ptr, png_bytep chunk_name) +{ + png_debug(1, "in png_check_chunk_name\n"); + if (isnonalpha(chunk_name[0]) || isnonalpha(chunk_name[1]) || + isnonalpha(chunk_name[2]) || isnonalpha(chunk_name[3])) + { + png_chunk_error(png_ptr, "invalid chunk type"); + } +} + +/* Combines the row recently read in with the existing pixels in the + row. This routine takes care of alpha and transparency if requested. + This routine also handles the two methods of progressive display + of interlaced images, depending on the mask value. + The mask value describes which pixels are to be combined with + the row. The pattern always repeats every 8 pixels, so just 8 + bits are needed. A one indicates the pixel is to be combined, + a zero indicates the pixel is to be skipped. This is in addition + to any alpha or transparency value associated with the pixel. If + you want all pixels to be combined, pass 0xff (255) in mask. */ + +void /* PRIVATE */ +png_combine_row(png_structp png_ptr, png_bytep row, int mask) +{ + png_debug(1,"in png_combine_row\n"); + if (mask == 0xff) + { + png_memcpy(row, png_ptr->row_buf + 1, + PNG_ROWBYTES(png_ptr->row_info.pixel_depth, png_ptr->width)); + } + else + { + switch (png_ptr->row_info.pixel_depth) + { + case 1: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + int s_inc, s_start, s_end; + int m = 0x80; + int shift; + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 7; + s_inc = 1; + } + else +#endif + { + s_start = 7; + s_end = 0; + s_inc = -1; + } + + shift = s_start; + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + int value; + + value = (*sp >> shift) & 0x01; + *dp &= (png_byte)((0x7f7f >> (7 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + case 2: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + int s_start, s_end, s_inc; + int m = 0x80; + int shift; + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + int value; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 6; + s_inc = 2; + } + else +#endif + { + s_start = 6; + s_end = 0; + s_inc = -2; + } + + shift = s_start; + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + value = (*sp >> shift) & 0x03; + *dp &= (png_byte)((0x3f3f >> (6 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + case 4: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + int s_start, s_end, s_inc; + int m = 0x80; + int shift; + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + int value; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + { + s_start = 0; + s_end = 4; + s_inc = 4; + } + else +#endif + { + s_start = 4; + s_end = 0; + s_inc = -4; + } + shift = s_start; + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + value = (*sp >> shift) & 0xf; + *dp &= (png_byte)((0xf0f >> (4 - shift)) & 0xff); + *dp |= (png_byte)(value << shift); + } + + if (shift == s_end) + { + shift = s_start; + sp++; + dp++; + } + else + shift += s_inc; + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + default: + { + png_bytep sp = png_ptr->row_buf + 1; + png_bytep dp = row; + png_size_t pixel_bytes = (png_ptr->row_info.pixel_depth >> 3); + png_uint_32 i; + png_uint_32 row_width = png_ptr->width; + png_byte m = 0x80; + + + for (i = 0; i < row_width; i++) + { + if (m & mask) + { + png_memcpy(dp, sp, pixel_bytes); + } + + sp += pixel_bytes; + dp += pixel_bytes; + + if (m == 1) + m = 0x80; + else + m >>= 1; + } + break; + } + } + } +} + +#ifdef PNG_READ_INTERLACING_SUPPORTED +/* OLD pre-1.0.9 interface: +void png_do_read_interlace(png_row_infop row_info, png_bytep row, int pass, + png_uint_32 transformations) + */ +void /* PRIVATE */ +png_do_read_interlace(png_structp png_ptr) +{ + png_row_infop row_info = &(png_ptr->row_info); + png_bytep row = png_ptr->row_buf + 1; + int pass = png_ptr->pass; + png_uint_32 transformations = png_ptr->transformations; +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + /* offset to next interlace block */ + PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; +#endif + + png_debug(1,"in png_do_read_interlace\n"); + if (row != NULL && row_info != NULL) + { + png_uint_32 final_width; + + final_width = row_info->width * png_pass_inc[pass]; + + switch (row_info->pixel_depth) + { + case 1: + { + png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 3); + png_bytep dp = row + (png_size_t)((final_width - 1) >> 3); + int sshift, dshift; + int s_start, s_end, s_inc; + int jstop = png_pass_inc[pass]; + png_byte v; + png_uint_32 i; + int j; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (transformations & PNG_PACKSWAP) + { + sshift = (int)((row_info->width + 7) & 0x07); + dshift = (int)((final_width + 7) & 0x07); + s_start = 7; + s_end = 0; + s_inc = -1; + } + else +#endif + { + sshift = 7 - (int)((row_info->width + 7) & 0x07); + dshift = 7 - (int)((final_width + 7) & 0x07); + s_start = 0; + s_end = 7; + s_inc = 1; + } + + for (i = 0; i < row_info->width; i++) + { + v = (png_byte)((*sp >> sshift) & 0x01); + for (j = 0; j < jstop; j++) + { + *dp &= (png_byte)((0x7f7f >> (7 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + case 2: + { + png_bytep sp = row + (png_uint_32)((row_info->width - 1) >> 2); + png_bytep dp = row + (png_uint_32)((final_width - 1) >> 2); + int sshift, dshift; + int s_start, s_end, s_inc; + int jstop = png_pass_inc[pass]; + png_uint_32 i; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (transformations & PNG_PACKSWAP) + { + sshift = (int)(((row_info->width + 3) & 0x03) << 1); + dshift = (int)(((final_width + 3) & 0x03) << 1); + s_start = 6; + s_end = 0; + s_inc = -2; + } + else +#endif + { + sshift = (int)((3 - ((row_info->width + 3) & 0x03)) << 1); + dshift = (int)((3 - ((final_width + 3) & 0x03)) << 1); + s_start = 0; + s_end = 6; + s_inc = 2; + } + + for (i = 0; i < row_info->width; i++) + { + png_byte v; + int j; + + v = (png_byte)((*sp >> sshift) & 0x03); + for (j = 0; j < jstop; j++) + { + *dp &= (png_byte)((0x3f3f >> (6 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + case 4: + { + png_bytep sp = row + (png_size_t)((row_info->width - 1) >> 1); + png_bytep dp = row + (png_size_t)((final_width - 1) >> 1); + int sshift, dshift; + int s_start, s_end, s_inc; + png_uint_32 i; + int jstop = png_pass_inc[pass]; + +#if defined(PNG_READ_PACKSWAP_SUPPORTED) + if (transformations & PNG_PACKSWAP) + { + sshift = (int)(((row_info->width + 1) & 0x01) << 2); + dshift = (int)(((final_width + 1) & 0x01) << 2); + s_start = 4; + s_end = 0; + s_inc = -4; + } + else +#endif + { + sshift = (int)((1 - ((row_info->width + 1) & 0x01)) << 2); + dshift = (int)((1 - ((final_width + 1) & 0x01)) << 2); + s_start = 0; + s_end = 4; + s_inc = 4; + } + + for (i = 0; i < row_info->width; i++) + { + png_byte v = (png_byte)((*sp >> sshift) & 0xf); + int j; + + for (j = 0; j < jstop; j++) + { + *dp &= (png_byte)((0xf0f >> (4 - dshift)) & 0xff); + *dp |= (png_byte)(v << dshift); + if (dshift == s_end) + { + dshift = s_start; + dp--; + } + else + dshift += s_inc; + } + if (sshift == s_end) + { + sshift = s_start; + sp--; + } + else + sshift += s_inc; + } + break; + } + default: + { + png_size_t pixel_bytes = (row_info->pixel_depth >> 3); + png_bytep sp = row + (png_size_t)(row_info->width - 1) * pixel_bytes; + png_bytep dp = row + (png_size_t)(final_width - 1) * pixel_bytes; + + int jstop = png_pass_inc[pass]; + png_uint_32 i; + + for (i = 0; i < row_info->width; i++) + { + png_byte v[8]; + int j; + + png_memcpy(v, sp, pixel_bytes); + for (j = 0; j < jstop; j++) + { + png_memcpy(dp, v, pixel_bytes); + dp -= pixel_bytes; + } + sp -= pixel_bytes; + } + break; + } + } + row_info->width = final_width; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth,final_width); + } +#if !defined(PNG_READ_PACKSWAP_SUPPORTED) + transformations = transformations; /* silence compiler warning */ +#endif +} +#endif /* PNG_READ_INTERLACING_SUPPORTED */ + +void /* PRIVATE */ +png_read_filter_row(png_structp png_ptr, png_row_infop row_info, png_bytep row, + png_bytep prev_row, int filter) +{ + png_debug(1, "in png_read_filter_row\n"); + png_debug2(2,"row = %lu, filter = %d\n", png_ptr->row_number, filter); + switch (filter) + { + case PNG_FILTER_VALUE_NONE: + break; + case PNG_FILTER_VALUE_SUB: + { + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_bytep rp = row + bpp; + png_bytep lp = row; + + for (i = bpp; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*lp++)) & 0xff); + rp++; + } + break; + } + case PNG_FILTER_VALUE_UP: + { + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + png_bytep rp = row; + png_bytep pp = prev_row; + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + rp++; + } + break; + } + case PNG_FILTER_VALUE_AVG: + { + png_uint_32 i; + png_bytep rp = row; + png_bytep pp = prev_row; + png_bytep lp = row; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_uint_32 istop = row_info->rowbytes - bpp; + + for (i = 0; i < bpp; i++) + { + *rp = (png_byte)(((int)(*rp) + + ((int)(*pp++) / 2 )) & 0xff); + rp++; + } + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(((int)(*rp) + + (int)(*pp++ + *lp++) / 2 ) & 0xff); + rp++; + } + break; + } + case PNG_FILTER_VALUE_PAETH: + { + png_uint_32 i; + png_bytep rp = row; + png_bytep pp = prev_row; + png_bytep lp = row; + png_bytep cp = prev_row; + png_uint_32 bpp = (row_info->pixel_depth + 7) >> 3; + png_uint_32 istop=row_info->rowbytes - bpp; + + for (i = 0; i < bpp; i++) + { + *rp = (png_byte)(((int)(*rp) + (int)(*pp++)) & 0xff); + rp++; + } + + for (i = 0; i < istop; i++) /* use leftover rp,pp */ + { + int a, b, c, pa, pb, pc, p; + + a = *lp++; + b = *pp++; + c = *cp++; + + p = b - c; + pc = a - c; + +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + + /* + if (pa <= pb && pa <= pc) + p = a; + else if (pb <= pc) + p = b; + else + p = c; + */ + + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; + + *rp = (png_byte)(((int)(*rp) + p) & 0xff); + rp++; + } + break; + } + default: + png_warning(png_ptr, "Ignoring bad adaptive filter type"); + *row=0; + break; + } +} + +void /* PRIVATE */ +png_read_finish_row(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + PNG_CONST int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* start of interlace block in the y direction */ + PNG_CONST int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* offset to next interlace block in the y direction */ + PNG_CONST int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif + + png_debug(1, "in png_read_finish_row\n"); + png_ptr->row_number++; + if (png_ptr->row_number < png_ptr->num_rows) + return; + + if (png_ptr->interlaced) + { + png_ptr->row_number = 0; + png_memset_check(png_ptr, png_ptr->prev_row, 0, + png_ptr->rowbytes + 1); + do + { + png_ptr->pass++; + if (png_ptr->pass >= 7) + break; + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + + png_ptr->irowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, + png_ptr->iwidth) + 1; + + if (!(png_ptr->transformations & PNG_INTERLACE)) + { + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + if (!(png_ptr->num_rows)) + continue; + } + else /* if (png_ptr->transformations & PNG_INTERLACE) */ + break; + } while (png_ptr->iwidth == 0); + + if (png_ptr->pass < 7) + return; + } + + if (!(png_ptr->flags & PNG_FLAG_ZLIB_FINISHED)) + { +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_CONST PNG_IDAT; +#endif + char extra; + int ret; + + png_ptr->zstream.next_out = (Byte *)&extra; + png_ptr->zstream.avail_out = (uInt)1; + for(;;) + { + if (!(png_ptr->zstream.avail_in)) + { + while (!png_ptr->idat_size) + { + png_byte chunk_length[4]; + + png_crc_finish(png_ptr, 0); + + png_read_data(png_ptr, chunk_length, 4); + png_ptr->idat_size = png_get_uint_31(png_ptr, chunk_length); + png_reset_crc(png_ptr); + png_crc_read(png_ptr, png_ptr->chunk_name, 4); + if (png_memcmp(png_ptr->chunk_name, png_IDAT, 4)) + png_error(png_ptr, "Not enough image data"); + + } + png_ptr->zstream.avail_in = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_in = png_ptr->zbuf; + if (png_ptr->zbuf_size > png_ptr->idat_size) + png_ptr->zstream.avail_in = (uInt)png_ptr->idat_size; + png_crc_read(png_ptr, png_ptr->zbuf, png_ptr->zstream.avail_in); + png_ptr->idat_size -= png_ptr->zstream.avail_in; + } + ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH); + if (ret == Z_STREAM_END) + { + if (!(png_ptr->zstream.avail_out) || png_ptr->zstream.avail_in || + png_ptr->idat_size) + png_warning(png_ptr, "Extra compressed data"); + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + if (ret != Z_OK) + png_error(png_ptr, png_ptr->zstream.msg ? png_ptr->zstream.msg : + "Decompression Error"); + + if (!(png_ptr->zstream.avail_out)) + { + png_warning(png_ptr, "Extra compressed data."); + png_ptr->mode |= PNG_AFTER_IDAT; + png_ptr->flags |= PNG_FLAG_ZLIB_FINISHED; + break; + } + + } + png_ptr->zstream.avail_out = 0; + } + + if (png_ptr->idat_size || png_ptr->zstream.avail_in) + png_warning(png_ptr, "Extra compression data"); + + inflateReset(&png_ptr->zstream); + + png_ptr->mode |= PNG_AFTER_IDAT; +} + +void /* PRIVATE */ +png_read_start_row(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + PNG_CONST int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + PNG_CONST int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* start of interlace block in the y direction */ + PNG_CONST int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* offset to next interlace block in the y direction */ + PNG_CONST int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif + + int max_pixel_depth; + png_uint_32 row_bytes; + + png_debug(1, "in png_read_start_row\n"); + png_ptr->zstream.avail_in = 0; + png_init_read_transformations(png_ptr); + if (png_ptr->interlaced) + { + if (!(png_ptr->transformations & PNG_INTERLACE)) + png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - + png_pass_ystart[0]) / png_pass_yinc[0]; + else + png_ptr->num_rows = png_ptr->height; + + png_ptr->iwidth = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + + row_bytes = PNG_ROWBYTES(png_ptr->pixel_depth,png_ptr->iwidth) + 1; + + png_ptr->irowbytes = (png_size_t)row_bytes; + if((png_uint_32)png_ptr->irowbytes != row_bytes) + png_error(png_ptr, "Rowbytes overflow in png_read_start_row"); + } + else + { + png_ptr->num_rows = png_ptr->height; + png_ptr->iwidth = png_ptr->width; + png_ptr->irowbytes = png_ptr->rowbytes + 1; + } + max_pixel_depth = png_ptr->pixel_depth; + +#if defined(PNG_READ_PACK_SUPPORTED) + if ((png_ptr->transformations & PNG_PACK) && png_ptr->bit_depth < 8) + max_pixel_depth = 8; +#endif + +#if defined(PNG_READ_EXPAND_SUPPORTED) + if (png_ptr->transformations & PNG_EXPAND) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + if (png_ptr->num_trans) + max_pixel_depth = 32; + else + max_pixel_depth = 24; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + { + if (max_pixel_depth < 8) + max_pixel_depth = 8; + if (png_ptr->num_trans) + max_pixel_depth *= 2; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + if (png_ptr->num_trans) + { + max_pixel_depth *= 4; + max_pixel_depth /= 3; + } + } + } +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) + if (png_ptr->transformations & (PNG_FILLER)) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + max_pixel_depth = 32; + else if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY) + { + if (max_pixel_depth <= 8) + max_pixel_depth = 16; + else + max_pixel_depth = 32; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + if (max_pixel_depth <= 32) + max_pixel_depth = 32; + else + max_pixel_depth = 64; + } + } +#endif + +#if defined(PNG_READ_GRAY_TO_RGB_SUPPORTED) + if (png_ptr->transformations & PNG_GRAY_TO_RGB) + { + if ( +#if defined(PNG_READ_EXPAND_SUPPORTED) + (png_ptr->num_trans && (png_ptr->transformations & PNG_EXPAND)) || +#endif +#if defined(PNG_READ_FILLER_SUPPORTED) + (png_ptr->transformations & (PNG_FILLER)) || +#endif + png_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + if (max_pixel_depth <= 16) + max_pixel_depth = 32; + else + max_pixel_depth = 64; + } + else + { + if (max_pixel_depth <= 8) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + max_pixel_depth = 32; + else + max_pixel_depth = 24; + } + else if (png_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + max_pixel_depth = 64; + else + max_pixel_depth = 48; + } + } +#endif + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) && \ +defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) + if(png_ptr->transformations & PNG_USER_TRANSFORM) + { + int user_pixel_depth=png_ptr->user_transform_depth* + png_ptr->user_transform_channels; + if(user_pixel_depth > max_pixel_depth) + max_pixel_depth=user_pixel_depth; + } +#endif + + /* align the width on the next larger 8 pixels. Mainly used + for interlacing */ + row_bytes = ((png_ptr->width + 7) & ~((png_uint_32)7)); + /* calculate the maximum bytes needed, adding a byte and a pixel + for safety's sake */ + row_bytes = PNG_ROWBYTES(max_pixel_depth,row_bytes) + + 1 + ((max_pixel_depth + 7) >> 3); +#ifdef PNG_MAX_MALLOC_64K + if (row_bytes > (png_uint_32)65536L) + png_error(png_ptr, "This image requires a row greater than 64KB"); +#endif + png_ptr->big_row_buf = (png_bytep)png_malloc(png_ptr, row_bytes+64); + png_ptr->row_buf = png_ptr->big_row_buf+32; + +#ifdef PNG_MAX_MALLOC_64K + if ((png_uint_32)png_ptr->rowbytes + 1 > (png_uint_32)65536L) + png_error(png_ptr, "This image requires a row greater than 64KB"); +#endif + if ((png_uint_32)png_ptr->rowbytes > (png_uint_32)(PNG_SIZE_MAX - 1)) + png_error(png_ptr, "Row has too many bytes to allocate in memory."); + png_ptr->prev_row = (png_bytep)png_malloc(png_ptr, (png_uint_32)( + png_ptr->rowbytes + 1)); + + png_memset_check(png_ptr, png_ptr->prev_row, 0, png_ptr->rowbytes + 1); + + png_debug1(3, "width = %lu,\n", png_ptr->width); + png_debug1(3, "height = %lu,\n", png_ptr->height); + png_debug1(3, "iwidth = %lu,\n", png_ptr->iwidth); + png_debug1(3, "num_rows = %lu\n", png_ptr->num_rows); + png_debug1(3, "rowbytes = %lu,\n", png_ptr->rowbytes); + png_debug1(3, "irowbytes = %lu,\n", png_ptr->irowbytes); + + png_ptr->flags |= PNG_FLAG_ROW_INIT; +} +#endif /* PNG_READ_SUPPORTED */ diff --git a/Engine/lib/lpng/pngset.c b/Engine/lib/lpng/pngset.c new file mode 100644 index 000000000..f8f9b7e3b --- /dev/null +++ b/Engine/lib/lpng/pngset.c @@ -0,0 +1,1250 @@ + +/* pngset.c - storage of image information into info struct + * + * Last changed in libpng 1.2.24 [December 14, 2007] + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2007 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * The functions here are used during reads to store data from the file + * into the info struct, and during writes to store application data + * into the info struct for writing into the file. This abstracts the + * info struct and allows us to change the structure in the future. + */ + +#define PNG_INTERNAL +#include "png.h" + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) + +#if defined(PNG_bKGD_SUPPORTED) +void PNGAPI +png_set_bKGD(png_structp png_ptr, png_infop info_ptr, png_color_16p background) +{ + png_debug1(1, "in %s storage function\n", "bKGD"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_memcpy(&(info_ptr->background), background, png_sizeof(png_color_16)); + info_ptr->valid |= PNG_INFO_bKGD; +} +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_cHRM(png_structp png_ptr, png_infop info_ptr, + double white_x, double white_y, double red_x, double red_y, + double green_x, double green_y, double blue_x, double blue_y) +{ + png_debug1(1, "in %s storage function\n", "cHRM"); + if (png_ptr == NULL || info_ptr == NULL) + return; + if (!(white_x || white_y || red_x || red_y || green_x || green_y || + blue_x || blue_y)) + { + png_warning(png_ptr, + "Ignoring attempt to set all-zero chromaticity values"); + return; + } + if (white_x < 0.0 || white_y < 0.0 || + red_x < 0.0 || red_y < 0.0 || + green_x < 0.0 || green_y < 0.0 || + blue_x < 0.0 || blue_y < 0.0) + { + png_warning(png_ptr, + "Ignoring attempt to set negative chromaticity value"); + return; + } + if (white_x > 21474.83 || white_y > 21474.83 || + red_x > 21474.83 || red_y > 21474.83 || + green_x > 21474.83 || green_y > 21474.83 || + blue_x > 21474.83 || blue_y > 21474.83) + { + png_warning(png_ptr, + "Ignoring attempt to set chromaticity value exceeding 21474.83"); + return; + } + + info_ptr->x_white = (float)white_x; + info_ptr->y_white = (float)white_y; + info_ptr->x_red = (float)red_x; + info_ptr->y_red = (float)red_y; + info_ptr->x_green = (float)green_x; + info_ptr->y_green = (float)green_y; + info_ptr->x_blue = (float)blue_x; + info_ptr->y_blue = (float)blue_y; +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_x_white = (png_fixed_point)(white_x*100000.+0.5); + info_ptr->int_y_white = (png_fixed_point)(white_y*100000.+0.5); + info_ptr->int_x_red = (png_fixed_point)( red_x*100000.+0.5); + info_ptr->int_y_red = (png_fixed_point)( red_y*100000.+0.5); + info_ptr->int_x_green = (png_fixed_point)(green_x*100000.+0.5); + info_ptr->int_y_green = (png_fixed_point)(green_y*100000.+0.5); + info_ptr->int_x_blue = (png_fixed_point)( blue_x*100000.+0.5); + info_ptr->int_y_blue = (png_fixed_point)( blue_y*100000.+0.5); +#endif + info_ptr->valid |= PNG_INFO_cHRM; +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +void PNGAPI +png_set_cHRM_fixed(png_structp png_ptr, png_infop info_ptr, + png_fixed_point white_x, png_fixed_point white_y, png_fixed_point red_x, + png_fixed_point red_y, png_fixed_point green_x, png_fixed_point green_y, + png_fixed_point blue_x, png_fixed_point blue_y) +{ + png_debug1(1, "in %s storage function\n", "cHRM"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (!(white_x || white_y || red_x || red_y || green_x || green_y || + blue_x || blue_y)) + { + png_warning(png_ptr, + "Ignoring attempt to set all-zero chromaticity values"); + return; + } + if (white_x < 0 || white_y < 0 || + red_x < 0 || red_y < 0 || + green_x < 0 || green_y < 0 || + blue_x < 0 || blue_y < 0) + { + png_warning(png_ptr, + "Ignoring attempt to set negative chromaticity value"); + return; + } + if (white_x > (png_fixed_point) PNG_UINT_31_MAX || + white_y > (png_fixed_point) PNG_UINT_31_MAX || + red_x > (png_fixed_point) PNG_UINT_31_MAX || + red_y > (png_fixed_point) PNG_UINT_31_MAX || + green_x > (png_fixed_point) PNG_UINT_31_MAX || + green_y > (png_fixed_point) PNG_UINT_31_MAX || + blue_x > (png_fixed_point) PNG_UINT_31_MAX || + blue_y > (png_fixed_point) PNG_UINT_31_MAX ) + { + png_warning(png_ptr, + "Ignoring attempt to set chromaticity value exceeding 21474.83"); + return; + } + info_ptr->int_x_white = white_x; + info_ptr->int_y_white = white_y; + info_ptr->int_x_red = red_x; + info_ptr->int_y_red = red_y; + info_ptr->int_x_green = green_x; + info_ptr->int_y_green = green_y; + info_ptr->int_x_blue = blue_x; + info_ptr->int_y_blue = blue_y; +#ifdef PNG_FLOATING_POINT_SUPPORTED + info_ptr->x_white = (float)(white_x/100000.); + info_ptr->y_white = (float)(white_y/100000.); + info_ptr->x_red = (float)( red_x/100000.); + info_ptr->y_red = (float)( red_y/100000.); + info_ptr->x_green = (float)(green_x/100000.); + info_ptr->y_green = (float)(green_y/100000.); + info_ptr->x_blue = (float)( blue_x/100000.); + info_ptr->y_blue = (float)( blue_y/100000.); +#endif + info_ptr->valid |= PNG_INFO_cHRM; +} +#endif +#endif + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_gAMA(png_structp png_ptr, png_infop info_ptr, double file_gamma) +{ + double gamma; + png_debug1(1, "in %s storage function\n", "gAMA"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* Check for overflow */ + if (file_gamma > 21474.83) + { + png_warning(png_ptr, "Limiting gamma to 21474.83"); + gamma=21474.83; + } + else + gamma=file_gamma; + info_ptr->gamma = (float)gamma; +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_gamma = (int)(gamma*100000.+.5); +#endif + info_ptr->valid |= PNG_INFO_gAMA; + if(gamma == 0.0) + png_warning(png_ptr, "Setting gamma=0"); +} +#endif +void PNGAPI +png_set_gAMA_fixed(png_structp png_ptr, png_infop info_ptr, png_fixed_point + int_gamma) +{ + png_fixed_point gamma; + + png_debug1(1, "in %s storage function\n", "gAMA"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (int_gamma > (png_fixed_point) PNG_UINT_31_MAX) + { + png_warning(png_ptr, "Limiting gamma to 21474.83"); + gamma=PNG_UINT_31_MAX; + } + else + { + if (int_gamma < 0) + { + png_warning(png_ptr, "Setting negative gamma to zero"); + gamma=0; + } + else + gamma=int_gamma; + } +#ifdef PNG_FLOATING_POINT_SUPPORTED + info_ptr->gamma = (float)(gamma/100000.); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + info_ptr->int_gamma = gamma; +#endif + info_ptr->valid |= PNG_INFO_gAMA; + if(gamma == 0) + png_warning(png_ptr, "Setting gamma=0"); +} +#endif + +#if defined(PNG_hIST_SUPPORTED) +void PNGAPI +png_set_hIST(png_structp png_ptr, png_infop info_ptr, png_uint_16p hist) +{ + int i; + + png_debug1(1, "in %s storage function\n", "hIST"); + if (png_ptr == NULL || info_ptr == NULL) + return; + if (info_ptr->num_palette == 0 || info_ptr->num_palette + > PNG_MAX_PALETTE_LENGTH) + { + png_warning(png_ptr, + "Invalid palette size, hIST allocation skipped."); + return; + } + +#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_HIST, 0); +#endif + /* Changed from info->num_palette to PNG_MAX_PALETTE_LENGTH in version + 1.2.1 */ + png_ptr->hist = (png_uint_16p)png_malloc_warn(png_ptr, + (png_uint_32)(PNG_MAX_PALETTE_LENGTH * png_sizeof (png_uint_16))); + if (png_ptr->hist == NULL) + { + png_warning(png_ptr, "Insufficient memory for hIST chunk data."); + return; + } + + for (i = 0; i < info_ptr->num_palette; i++) + png_ptr->hist[i] = hist[i]; + info_ptr->hist = png_ptr->hist; + info_ptr->valid |= PNG_INFO_hIST; + +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_HIST; +#else + png_ptr->flags |= PNG_FLAG_FREE_HIST; +#endif +} +#endif + +void PNGAPI +png_set_IHDR(png_structp png_ptr, png_infop info_ptr, + png_uint_32 width, png_uint_32 height, int bit_depth, + int color_type, int interlace_type, int compression_type, + int filter_type) +{ + png_debug1(1, "in %s storage function\n", "IHDR"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + /* check for width and height valid values */ + if (width == 0 || height == 0) + png_error(png_ptr, "Image width or height is zero in IHDR"); +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + if (width > png_ptr->user_width_max || height > png_ptr->user_height_max) + png_error(png_ptr, "image size exceeds user limits in IHDR"); +#else + if (width > PNG_USER_WIDTH_MAX || height > PNG_USER_HEIGHT_MAX) + png_error(png_ptr, "image size exceeds user limits in IHDR"); +#endif + if (width > PNG_UINT_31_MAX || height > PNG_UINT_31_MAX) + png_error(png_ptr, "Invalid image size in IHDR"); + if ( width > (PNG_UINT_32_MAX + >> 3) /* 8-byte RGBA pixels */ + - 64 /* bigrowbuf hack */ + - 1 /* filter byte */ + - 7*8 /* rounding of width to multiple of 8 pixels */ + - 8) /* extra max_pixel_depth pad */ + png_warning(png_ptr, "Width is too large for libpng to process pixels"); + + /* check other values */ + if (bit_depth != 1 && bit_depth != 2 && bit_depth != 4 && + bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth in IHDR"); + + if (color_type < 0 || color_type == 1 || + color_type == 5 || color_type > 6) + png_error(png_ptr, "Invalid color type in IHDR"); + + if (((color_type == PNG_COLOR_TYPE_PALETTE) && bit_depth > 8) || + ((color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) && bit_depth < 8)) + png_error(png_ptr, "Invalid color type/bit depth combination in IHDR"); + + if (interlace_type >= PNG_INTERLACE_LAST) + png_error(png_ptr, "Unknown interlace method in IHDR"); + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + png_error(png_ptr, "Unknown compression method in IHDR"); + +#if defined(PNG_MNG_FEATURES_SUPPORTED) + /* Accept filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not read a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE)&&png_ptr->mng_features_permitted) + png_warning(png_ptr,"MNG features are not allowed in a PNG datastream"); + if(filter_type != PNG_FILTER_TYPE_BASE) + { + if(!((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (filter_type == PNG_INTRAPIXEL_DIFFERENCING) && + ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) && + (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA))) + png_error(png_ptr, "Unknown filter method in IHDR"); + if(png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) + png_warning(png_ptr, "Invalid filter method in IHDR"); + } +#else + if(filter_type != PNG_FILTER_TYPE_BASE) + png_error(png_ptr, "Unknown filter method in IHDR"); +#endif + + info_ptr->width = width; + info_ptr->height = height; + info_ptr->bit_depth = (png_byte)bit_depth; + info_ptr->color_type =(png_byte) color_type; + info_ptr->compression_type = (png_byte)compression_type; + info_ptr->filter_type = (png_byte)filter_type; + info_ptr->interlace_type = (png_byte)interlace_type; + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + info_ptr->channels = 1; + else if (info_ptr->color_type & PNG_COLOR_MASK_COLOR) + info_ptr->channels = 3; + else + info_ptr->channels = 1; + if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) + info_ptr->channels++; + info_ptr->pixel_depth = (png_byte)(info_ptr->channels * info_ptr->bit_depth); + + /* check for potential overflow */ + if (width > (PNG_UINT_32_MAX + >> 3) /* 8-byte RGBA pixels */ + - 64 /* bigrowbuf hack */ + - 1 /* filter byte */ + - 7*8 /* rounding of width to multiple of 8 pixels */ + - 8) /* extra max_pixel_depth pad */ + info_ptr->rowbytes = (png_size_t)0; + else + info_ptr->rowbytes = PNG_ROWBYTES(info_ptr->pixel_depth,width); +} + +#if defined(PNG_oFFs_SUPPORTED) +void PNGAPI +png_set_oFFs(png_structp png_ptr, png_infop info_ptr, + png_int_32 offset_x, png_int_32 offset_y, int unit_type) +{ + png_debug1(1, "in %s storage function\n", "oFFs"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->x_offset = offset_x; + info_ptr->y_offset = offset_y; + info_ptr->offset_unit_type = (png_byte)unit_type; + info_ptr->valid |= PNG_INFO_oFFs; +} +#endif + +#if defined(PNG_pCAL_SUPPORTED) +void PNGAPI +png_set_pCAL(png_structp png_ptr, png_infop info_ptr, + png_charp purpose, png_int_32 X0, png_int_32 X1, int type, int nparams, + png_charp units, png_charpp params) +{ + png_uint_32 length; + int i; + + png_debug1(1, "in %s storage function\n", "pCAL"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + length = png_strlen(purpose) + 1; + png_debug1(3, "allocating purpose for info (%lu bytes)\n", length); + info_ptr->pcal_purpose = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->pcal_purpose == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL purpose."); + return; + } + png_memcpy(info_ptr->pcal_purpose, purpose, (png_size_t)length); + + png_debug(3, "storing X0, X1, type, and nparams in info\n"); + info_ptr->pcal_X0 = X0; + info_ptr->pcal_X1 = X1; + info_ptr->pcal_type = (png_byte)type; + info_ptr->pcal_nparams = (png_byte)nparams; + + length = png_strlen(units) + 1; + png_debug1(3, "allocating units for info (%lu bytes)\n", length); + info_ptr->pcal_units = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->pcal_units == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL units."); + return; + } + png_memcpy(info_ptr->pcal_units, units, (png_size_t)length); + + info_ptr->pcal_params = (png_charpp)png_malloc_warn(png_ptr, + (png_uint_32)((nparams + 1) * png_sizeof(png_charp))); + if (info_ptr->pcal_params == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL params."); + return; + } + + info_ptr->pcal_params[nparams] = NULL; + + for (i = 0; i < nparams; i++) + { + length = png_strlen(params[i]) + 1; + png_debug2(3, "allocating parameter %d for info (%lu bytes)\n", i, length); + info_ptr->pcal_params[i] = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->pcal_params[i] == NULL) + { + png_warning(png_ptr, "Insufficient memory for pCAL parameter."); + return; + } + png_memcpy(info_ptr->pcal_params[i], params[i], (png_size_t)length); + } + + info_ptr->valid |= PNG_INFO_pCAL; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_PCAL; +#endif +} +#endif + +#if defined(PNG_READ_sCAL_SUPPORTED) || defined(PNG_WRITE_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED +void PNGAPI +png_set_sCAL(png_structp png_ptr, png_infop info_ptr, + int unit, double width, double height) +{ + png_debug1(1, "in %s storage function\n", "sCAL"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->scal_unit = (png_byte)unit; + info_ptr->scal_pixel_width = width; + info_ptr->scal_pixel_height = height; + + info_ptr->valid |= PNG_INFO_sCAL; +} +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +void PNGAPI +png_set_sCAL_s(png_structp png_ptr, png_infop info_ptr, + int unit, png_charp swidth, png_charp sheight) +{ + png_uint_32 length; + + png_debug1(1, "in %s storage function\n", "sCAL"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->scal_unit = (png_byte)unit; + + length = png_strlen(swidth) + 1; + png_debug1(3, "allocating unit for info (%d bytes)\n", length); + info_ptr->scal_s_width = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->scal_s_width == NULL) + { + png_warning(png_ptr, + "Memory allocation failed while processing sCAL."); + } + png_memcpy(info_ptr->scal_s_width, swidth, (png_size_t)length); + + length = png_strlen(sheight) + 1; + png_debug1(3, "allocating unit for info (%d bytes)\n", length); + info_ptr->scal_s_height = (png_charp)png_malloc_warn(png_ptr, length); + if (info_ptr->scal_s_height == NULL) + { + png_free (png_ptr, info_ptr->scal_s_width); + png_warning(png_ptr, + "Memory allocation failed while processing sCAL."); + } + png_memcpy(info_ptr->scal_s_height, sheight, (png_size_t)length); + + info_ptr->valid |= PNG_INFO_sCAL; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_SCAL; +#endif +} +#endif +#endif +#endif + +#if defined(PNG_pHYs_SUPPORTED) +void PNGAPI +png_set_pHYs(png_structp png_ptr, png_infop info_ptr, + png_uint_32 res_x, png_uint_32 res_y, int unit_type) +{ + png_debug1(1, "in %s storage function\n", "pHYs"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->x_pixels_per_unit = res_x; + info_ptr->y_pixels_per_unit = res_y; + info_ptr->phys_unit_type = (png_byte)unit_type; + info_ptr->valid |= PNG_INFO_pHYs; +} +#endif + +void PNGAPI +png_set_PLTE(png_structp png_ptr, png_infop info_ptr, + png_colorp palette, int num_palette) +{ + + png_debug1(1, "in %s storage function\n", "PLTE"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (num_palette < 0 || num_palette > PNG_MAX_PALETTE_LENGTH) + { + if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + png_error(png_ptr, "Invalid palette length"); + else + { + png_warning(png_ptr, "Invalid palette length"); + return; + } + } + + /* + * It may not actually be necessary to set png_ptr->palette here; + * we do it for backward compatibility with the way the png_handle_tRNS + * function used to do the allocation. + */ +#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_PLTE, 0); +#endif + + /* Changed in libpng-1.2.1 to allocate PNG_MAX_PALETTE_LENGTH instead + of num_palette entries, + in case of an invalid PNG file that has too-large sample values. */ + png_ptr->palette = (png_colorp)png_malloc(png_ptr, + PNG_MAX_PALETTE_LENGTH * png_sizeof(png_color)); + png_memset(png_ptr->palette, 0, PNG_MAX_PALETTE_LENGTH * + png_sizeof(png_color)); + png_memcpy(png_ptr->palette, palette, num_palette * png_sizeof (png_color)); + info_ptr->palette = png_ptr->palette; + info_ptr->num_palette = png_ptr->num_palette = (png_uint_16)num_palette; + +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_PLTE; +#else + png_ptr->flags |= PNG_FLAG_FREE_PLTE; +#endif + + info_ptr->valid |= PNG_INFO_PLTE; +} + +#if defined(PNG_sBIT_SUPPORTED) +void PNGAPI +png_set_sBIT(png_structp png_ptr, png_infop info_ptr, + png_color_8p sig_bit) +{ + png_debug1(1, "in %s storage function\n", "sBIT"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_memcpy(&(info_ptr->sig_bit), sig_bit, png_sizeof (png_color_8)); + info_ptr->valid |= PNG_INFO_sBIT; +} +#endif + +#if defined(PNG_sRGB_SUPPORTED) +void PNGAPI +png_set_sRGB(png_structp png_ptr, png_infop info_ptr, int intent) +{ + png_debug1(1, "in %s storage function\n", "sRGB"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + info_ptr->srgb_intent = (png_byte)intent; + info_ptr->valid |= PNG_INFO_sRGB; +} + +void PNGAPI +png_set_sRGB_gAMA_and_cHRM(png_structp png_ptr, png_infop info_ptr, + int intent) +{ +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED + float file_gamma; +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_fixed_point int_file_gamma; +#endif +#endif +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED + float white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y; +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + png_fixed_point int_white_x, int_white_y, int_red_x, int_red_y, int_green_x, + int_green_y, int_blue_x, int_blue_y; +#endif +#endif + png_debug1(1, "in %s storage function\n", "sRGB_gAMA_and_cHRM"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_set_sRGB(png_ptr, info_ptr, intent); + +#if defined(PNG_gAMA_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED + file_gamma = (float).45455; + png_set_gAMA(png_ptr, info_ptr, file_gamma); +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED + int_file_gamma = 45455L; + png_set_gAMA_fixed(png_ptr, info_ptr, int_file_gamma); +#endif +#endif + +#if defined(PNG_cHRM_SUPPORTED) +#ifdef PNG_FIXED_POINT_SUPPORTED + int_white_x = 31270L; + int_white_y = 32900L; + int_red_x = 64000L; + int_red_y = 33000L; + int_green_x = 30000L; + int_green_y = 60000L; + int_blue_x = 15000L; + int_blue_y = 6000L; + + png_set_cHRM_fixed(png_ptr, info_ptr, + int_white_x, int_white_y, int_red_x, int_red_y, int_green_x, int_green_y, + int_blue_x, int_blue_y); +#endif +#ifdef PNG_FLOATING_POINT_SUPPORTED + white_x = (float).3127; + white_y = (float).3290; + red_x = (float).64; + red_y = (float).33; + green_x = (float).30; + green_y = (float).60; + blue_x = (float).15; + blue_y = (float).06; + + png_set_cHRM(png_ptr, info_ptr, + white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y); +#endif +#endif +} +#endif + + +#if defined(PNG_iCCP_SUPPORTED) +void PNGAPI +png_set_iCCP(png_structp png_ptr, png_infop info_ptr, + png_charp name, int compression_type, + png_charp profile, png_uint_32 proflen) +{ + png_charp new_iccp_name; + png_charp new_iccp_profile; + png_uint_32 length; + + png_debug1(1, "in %s storage function\n", "iCCP"); + if (png_ptr == NULL || info_ptr == NULL || name == NULL || profile == NULL) + return; + + length = png_strlen(name)+1; + new_iccp_name = (png_charp)png_malloc_warn(png_ptr, length); + if (new_iccp_name == NULL) + { + png_warning(png_ptr, "Insufficient memory to process iCCP chunk."); + return; + } + png_memcpy(new_iccp_name, name, length); + new_iccp_profile = (png_charp)png_malloc_warn(png_ptr, proflen); + if (new_iccp_profile == NULL) + { + png_free (png_ptr, new_iccp_name); + png_warning(png_ptr, "Insufficient memory to process iCCP profile."); + return; + } + png_memcpy(new_iccp_profile, profile, (png_size_t)proflen); + + png_free_data(png_ptr, info_ptr, PNG_FREE_ICCP, 0); + + info_ptr->iccp_proflen = proflen; + info_ptr->iccp_name = new_iccp_name; + info_ptr->iccp_profile = new_iccp_profile; + /* Compression is always zero but is here so the API and info structure + * does not have to change if we introduce multiple compression types */ + info_ptr->iccp_compression = (png_byte)compression_type; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_ICCP; +#endif + info_ptr->valid |= PNG_INFO_iCCP; +} +#endif + +#if defined(PNG_TEXT_SUPPORTED) +void PNGAPI +png_set_text(png_structp png_ptr, png_infop info_ptr, png_textp text_ptr, + int num_text) +{ + int ret; + ret=png_set_text_2(png_ptr, info_ptr, text_ptr, num_text); + if (ret) + png_error(png_ptr, "Insufficient memory to store text"); +} + +int /* PRIVATE */ +png_set_text_2(png_structp png_ptr, png_infop info_ptr, png_textp text_ptr, + int num_text) +{ + int i; + + png_debug1(1, "in %s storage function\n", (png_ptr->chunk_name[0] == '\0' ? + "text" : (png_const_charp)png_ptr->chunk_name)); + + if (png_ptr == NULL || info_ptr == NULL || num_text == 0) + return(0); + + /* Make sure we have enough space in the "text" array in info_struct + * to hold all of the incoming text_ptr objects. + */ + if (info_ptr->num_text + num_text > info_ptr->max_text) + { + if (info_ptr->text != NULL) + { + png_textp old_text; + int old_max; + + old_max = info_ptr->max_text; + info_ptr->max_text = info_ptr->num_text + num_text + 8; + old_text = info_ptr->text; + info_ptr->text = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)(info_ptr->max_text * png_sizeof (png_text))); + if (info_ptr->text == NULL) + { + png_free(png_ptr, old_text); + return(1); + } + png_memcpy(info_ptr->text, old_text, (png_size_t)(old_max * + png_sizeof(png_text))); + png_free(png_ptr, old_text); + } + else + { + info_ptr->max_text = num_text + 8; + info_ptr->num_text = 0; + info_ptr->text = (png_textp)png_malloc_warn(png_ptr, + (png_uint_32)(info_ptr->max_text * png_sizeof (png_text))); + if (info_ptr->text == NULL) + return(1); +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_TEXT; +#endif + } + png_debug1(3, "allocated %d entries for info_ptr->text\n", + info_ptr->max_text); + } + for (i = 0; i < num_text; i++) + { + png_size_t text_length,key_len; + png_size_t lang_len,lang_key_len; + png_textp textp = &(info_ptr->text[info_ptr->num_text]); + + if (text_ptr[i].key == NULL) + continue; + + key_len = png_strlen(text_ptr[i].key); + + if(text_ptr[i].compression <= 0) + { + lang_len = 0; + lang_key_len = 0; + } + else +#ifdef PNG_iTXt_SUPPORTED + { + /* set iTXt data */ + if (text_ptr[i].lang != NULL) + lang_len = png_strlen(text_ptr[i].lang); + else + lang_len = 0; + if (text_ptr[i].lang_key != NULL) + lang_key_len = png_strlen(text_ptr[i].lang_key); + else + lang_key_len = 0; + } +#else + { + png_warning(png_ptr, "iTXt chunk not supported."); + continue; + } +#endif + + if (text_ptr[i].text == NULL || text_ptr[i].text[0] == '\0') + { + text_length = 0; +#ifdef PNG_iTXt_SUPPORTED + if(text_ptr[i].compression > 0) + textp->compression = PNG_ITXT_COMPRESSION_NONE; + else +#endif + textp->compression = PNG_TEXT_COMPRESSION_NONE; + } + else + { + text_length = png_strlen(text_ptr[i].text); + textp->compression = text_ptr[i].compression; + } + + textp->key = (png_charp)png_malloc_warn(png_ptr, + (png_uint_32)(key_len + text_length + lang_len + lang_key_len + 4)); + if (textp->key == NULL) + return(1); + png_debug2(2, "Allocated %lu bytes at %x in png_set_text\n", + (png_uint_32)(key_len + lang_len + lang_key_len + text_length + 4), + (int)textp->key); + + png_memcpy(textp->key, text_ptr[i].key, + (png_size_t)(key_len)); + *(textp->key+key_len) = '\0'; +#ifdef PNG_iTXt_SUPPORTED + if (text_ptr[i].compression > 0) + { + textp->lang=textp->key + key_len + 1; + png_memcpy(textp->lang, text_ptr[i].lang, lang_len); + *(textp->lang+lang_len) = '\0'; + textp->lang_key=textp->lang + lang_len + 1; + png_memcpy(textp->lang_key, text_ptr[i].lang_key, lang_key_len); + *(textp->lang_key+lang_key_len) = '\0'; + textp->text=textp->lang_key + lang_key_len + 1; + } + else +#endif + { +#ifdef PNG_iTXt_SUPPORTED + textp->lang=NULL; + textp->lang_key=NULL; +#endif + textp->text=textp->key + key_len + 1; + } + if(text_length) + png_memcpy(textp->text, text_ptr[i].text, + (png_size_t)(text_length)); + *(textp->text+text_length) = '\0'; + +#ifdef PNG_iTXt_SUPPORTED + if(textp->compression > 0) + { + textp->text_length = 0; + textp->itxt_length = text_length; + } + else +#endif + { + textp->text_length = text_length; +#ifdef PNG_iTXt_SUPPORTED + textp->itxt_length = 0; +#endif + } + info_ptr->num_text++; + png_debug1(3, "transferred text chunk %d\n", info_ptr->num_text); + } + return(0); +} +#endif + +#if defined(PNG_tIME_SUPPORTED) +void PNGAPI +png_set_tIME(png_structp png_ptr, png_infop info_ptr, png_timep mod_time) +{ + png_debug1(1, "in %s storage function\n", "tIME"); + if (png_ptr == NULL || info_ptr == NULL || + (png_ptr->mode & PNG_WROTE_tIME)) + return; + + png_memcpy(&(info_ptr->mod_time), mod_time, png_sizeof (png_time)); + info_ptr->valid |= PNG_INFO_tIME; +} +#endif + +#if defined(PNG_tRNS_SUPPORTED) +void PNGAPI +png_set_tRNS(png_structp png_ptr, png_infop info_ptr, + png_bytep trans, int num_trans, png_color_16p trans_values) +{ + png_debug1(1, "in %s storage function\n", "tRNS"); + if (png_ptr == NULL || info_ptr == NULL) + return; + + if (trans != NULL) + { + /* + * It may not actually be necessary to set png_ptr->trans here; + * we do it for backward compatibility with the way the png_handle_tRNS + * function used to do the allocation. + */ +#ifdef PNG_FREE_ME_SUPPORTED + png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0); +#endif + /* Changed from num_trans to PNG_MAX_PALETTE_LENGTH in version 1.2.1 */ + png_ptr->trans = info_ptr->trans = (png_bytep)png_malloc(png_ptr, + (png_uint_32)PNG_MAX_PALETTE_LENGTH); + if (num_trans <= PNG_MAX_PALETTE_LENGTH) + png_memcpy(info_ptr->trans, trans, (png_size_t)num_trans); +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_TRNS; +#else + png_ptr->flags |= PNG_FLAG_FREE_TRNS; +#endif + } + + if (trans_values != NULL) + { + png_memcpy(&(info_ptr->trans_values), trans_values, + png_sizeof(png_color_16)); + if (num_trans == 0) + num_trans = 1; + } + info_ptr->num_trans = (png_uint_16)num_trans; + info_ptr->valid |= PNG_INFO_tRNS; +} +#endif + +#if defined(PNG_sPLT_SUPPORTED) +void PNGAPI +png_set_sPLT(png_structp png_ptr, + png_infop info_ptr, png_sPLT_tp entries, int nentries) +{ + png_sPLT_tp np; + int i; + + if (png_ptr == NULL || info_ptr == NULL) + return; + + np = (png_sPLT_tp)png_malloc_warn(png_ptr, + (info_ptr->splt_palettes_num + nentries) * png_sizeof(png_sPLT_t)); + if (np == NULL) + { + png_warning(png_ptr, "No memory for sPLT palettes."); + return; + } + + png_memcpy(np, info_ptr->splt_palettes, + info_ptr->splt_palettes_num * png_sizeof(png_sPLT_t)); + png_free(png_ptr, info_ptr->splt_palettes); + info_ptr->splt_palettes=NULL; + + for (i = 0; i < nentries; i++) + { + png_sPLT_tp to = np + info_ptr->splt_palettes_num + i; + png_sPLT_tp from = entries + i; + png_uint_32 length; + + length = png_strlen(from->name) + 1; + to->name = (png_charp)png_malloc_warn(png_ptr, length); + if (to->name == NULL) + { + png_warning(png_ptr, + "Out of memory while processing sPLT chunk"); + } + png_memcpy(to->name, from->name, length); + to->entries = (png_sPLT_entryp)png_malloc_warn(png_ptr, + from->nentries * png_sizeof(png_sPLT_entry)); + if (to->entries == NULL) + { + png_warning(png_ptr, + "Out of memory while processing sPLT chunk"); + png_free(png_ptr,to->name); + to->name = NULL; + } + png_memcpy(to->entries, from->entries, + from->nentries * png_sizeof(png_sPLT_entry)); + to->nentries = from->nentries; + to->depth = from->depth; + } + + info_ptr->splt_palettes = np; + info_ptr->splt_palettes_num += nentries; + info_ptr->valid |= PNG_INFO_sPLT; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_SPLT; +#endif +} +#endif /* PNG_sPLT_SUPPORTED */ + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +void PNGAPI +png_set_unknown_chunks(png_structp png_ptr, + png_infop info_ptr, png_unknown_chunkp unknowns, int num_unknowns) +{ + png_unknown_chunkp np; + int i; + + if (png_ptr == NULL || info_ptr == NULL || num_unknowns == 0) + return; + + np = (png_unknown_chunkp)png_malloc_warn(png_ptr, + (info_ptr->unknown_chunks_num + num_unknowns) * + png_sizeof(png_unknown_chunk)); + if (np == NULL) + { + png_warning(png_ptr, + "Out of memory while processing unknown chunk."); + return; + } + + png_memcpy(np, info_ptr->unknown_chunks, + info_ptr->unknown_chunks_num * png_sizeof(png_unknown_chunk)); + png_free(png_ptr, info_ptr->unknown_chunks); + info_ptr->unknown_chunks=NULL; + + for (i = 0; i < num_unknowns; i++) + { + png_unknown_chunkp to = np + info_ptr->unknown_chunks_num + i; + png_unknown_chunkp from = unknowns + i; + + png_memcpy((png_charp)to->name, + (png_charp)from->name, + png_sizeof(from->name)); + to->name[png_sizeof(to->name)-1] = '\0'; + + to->data = (png_bytep)png_malloc_warn(png_ptr, from->size); + if (to->data == NULL) + { + png_warning(png_ptr, + "Out of memory while processing unknown chunk."); + } + else + { + png_memcpy(to->data, from->data, from->size); + to->size = from->size; + + /* note our location in the read or write sequence */ + to->location = (png_byte)(png_ptr->mode & 0xff); + } + } + + info_ptr->unknown_chunks = np; + info_ptr->unknown_chunks_num += num_unknowns; +#ifdef PNG_FREE_ME_SUPPORTED + info_ptr->free_me |= PNG_FREE_UNKN; +#endif +} +void PNGAPI +png_set_unknown_chunk_location(png_structp png_ptr, png_infop info_ptr, + int chunk, int location) +{ + if(png_ptr != NULL && info_ptr != NULL && chunk >= 0 && chunk < + (int)info_ptr->unknown_chunks_num) + info_ptr->unknown_chunks[chunk].location = (png_byte)location; +} +#endif + +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +#if defined(PNG_READ_EMPTY_PLTE_SUPPORTED) || \ + defined(PNG_WRITE_EMPTY_PLTE_SUPPORTED) +void PNGAPI +png_permit_empty_plte (png_structp png_ptr, int empty_plte_permitted) +{ + /* This function is deprecated in favor of png_permit_mng_features() + and will be removed from libpng-1.3.0 */ + png_debug(1, "in png_permit_empty_plte, DEPRECATED.\n"); + if (png_ptr == NULL) + return; + png_ptr->mng_features_permitted = (png_byte) + ((png_ptr->mng_features_permitted & (~PNG_FLAG_MNG_EMPTY_PLTE)) | + ((empty_plte_permitted & PNG_FLAG_MNG_EMPTY_PLTE))); +} +#endif +#endif + +#if defined(PNG_MNG_FEATURES_SUPPORTED) +png_uint_32 PNGAPI +png_permit_mng_features (png_structp png_ptr, png_uint_32 mng_features) +{ + png_debug(1, "in png_permit_mng_features\n"); + if (png_ptr == NULL) + return (png_uint_32)0; + png_ptr->mng_features_permitted = + (png_byte)(mng_features & PNG_ALL_MNG_FEATURES); + return (png_uint_32)png_ptr->mng_features_permitted; +} +#endif + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) +void PNGAPI +png_set_keep_unknown_chunks(png_structp png_ptr, int keep, png_bytep + chunk_list, int num_chunks) +{ + png_bytep new_list, p; + int i, old_num_chunks; + if (png_ptr == NULL) + return; + if (num_chunks == 0) + { + if(keep == PNG_HANDLE_CHUNK_ALWAYS || keep == PNG_HANDLE_CHUNK_IF_SAFE) + png_ptr->flags |= PNG_FLAG_KEEP_UNKNOWN_CHUNKS; + else + png_ptr->flags &= ~PNG_FLAG_KEEP_UNKNOWN_CHUNKS; + + if(keep == PNG_HANDLE_CHUNK_ALWAYS) + png_ptr->flags |= PNG_FLAG_KEEP_UNSAFE_CHUNKS; + else + png_ptr->flags &= ~PNG_FLAG_KEEP_UNSAFE_CHUNKS; + return; + } + if (chunk_list == NULL) + return; + old_num_chunks=png_ptr->num_chunk_list; + new_list=(png_bytep)png_malloc(png_ptr, + (png_uint_32)(5*(num_chunks+old_num_chunks))); + if(png_ptr->chunk_list != NULL) + { + png_memcpy(new_list, png_ptr->chunk_list, + (png_size_t)(5*old_num_chunks)); + png_free(png_ptr, png_ptr->chunk_list); + png_ptr->chunk_list=NULL; + } + png_memcpy(new_list+5*old_num_chunks, chunk_list, + (png_size_t)(5*num_chunks)); + for (p=new_list+5*old_num_chunks+4, i=0; inum_chunk_list=old_num_chunks+num_chunks; + png_ptr->chunk_list=new_list; +#ifdef PNG_FREE_ME_SUPPORTED + png_ptr->free_me |= PNG_FREE_LIST; +#endif +} +#endif + +#if defined(PNG_READ_USER_CHUNKS_SUPPORTED) +void PNGAPI +png_set_read_user_chunk_fn(png_structp png_ptr, png_voidp user_chunk_ptr, + png_user_chunk_ptr read_user_chunk_fn) +{ + png_debug(1, "in png_set_read_user_chunk_fn\n"); + if (png_ptr == NULL) + return; + png_ptr->read_user_chunk_fn = read_user_chunk_fn; + png_ptr->user_chunk_ptr = user_chunk_ptr; +} +#endif + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +void PNGAPI +png_set_rows(png_structp png_ptr, png_infop info_ptr, png_bytepp row_pointers) +{ + png_debug1(1, "in %s storage function\n", "rows"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + if(info_ptr->row_pointers && (info_ptr->row_pointers != row_pointers)) + png_free_data(png_ptr, info_ptr, PNG_FREE_ROWS, 0); + info_ptr->row_pointers = row_pointers; + if(row_pointers) + info_ptr->valid |= PNG_INFO_IDAT; +} +#endif + +#ifdef PNG_WRITE_SUPPORTED +void PNGAPI +png_set_compression_buffer_size(png_structp png_ptr, png_uint_32 size) +{ + if (png_ptr == NULL) + return; + if(png_ptr->zbuf) + png_free(png_ptr, png_ptr->zbuf); + png_ptr->zbuf_size = (png_size_t)size; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; +} +#endif + +void PNGAPI +png_set_invalid(png_structp png_ptr, png_infop info_ptr, int mask) +{ + if (png_ptr && info_ptr) + info_ptr->valid &= ~mask; +} + + +#ifndef PNG_1_0_X +#ifdef PNG_ASSEMBLER_CODE_SUPPORTED +/* function was added to libpng 1.2.0 and should always exist by default */ +void PNGAPI +png_set_asm_flags (png_structp png_ptr, png_uint_32 asm_flags) +{ +/* Obsolete as of libpng-1.2.20 and will be removed from libpng-1.4.0 */ + if (png_ptr != NULL) + png_ptr->asm_flags = 0; +} + +/* this function was added to libpng 1.2.0 */ +void PNGAPI +png_set_mmx_thresholds (png_structp png_ptr, + png_byte mmx_bitdepth_threshold, + png_uint_32 mmx_rowbytes_threshold) +{ +/* Obsolete as of libpng-1.2.20 and will be removed from libpng-1.4.0 */ + if (png_ptr == NULL) + return; +} +#endif /* ?PNG_ASSEMBLER_CODE_SUPPORTED */ + +#ifdef PNG_SET_USER_LIMITS_SUPPORTED +/* this function was added to libpng 1.2.6 */ +void PNGAPI +png_set_user_limits (png_structp png_ptr, png_uint_32 user_width_max, + png_uint_32 user_height_max) +{ + /* Images with dimensions larger than these limits will be + * rejected by png_set_IHDR(). To accept any PNG datastream + * regardless of dimensions, set both limits to 0x7ffffffL. + */ + if(png_ptr == NULL) return; + png_ptr->user_width_max = user_width_max; + png_ptr->user_height_max = user_height_max; +} +#endif /* ?PNG_SET_USER_LIMITS_SUPPORTED */ + +#endif /* ?PNG_1_0_X */ +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/Engine/lib/lpng/pngtest.png b/Engine/lib/lpng/pngtest.png new file mode 100644 index 000000000..f3a6df448 Binary files /dev/null and b/Engine/lib/lpng/pngtest.png differ diff --git a/Engine/lib/lpng/pngtrans.c b/Engine/lib/lpng/pngtrans.c new file mode 100644 index 000000000..164009502 --- /dev/null +++ b/Engine/lib/lpng/pngtrans.c @@ -0,0 +1,662 @@ + +/* pngtrans.c - transforms the data in a row (used by both readers and writers) + * + * Last changed in libpng 1.2.17 May 15, 2007 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2007 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL +#include "png.h" + +#if defined(PNG_READ_SUPPORTED) || defined(PNG_WRITE_SUPPORTED) +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* turn on BGR-to-RGB mapping */ +void PNGAPI +png_set_bgr(png_structp png_ptr) +{ + png_debug(1, "in png_set_bgr\n"); + if(png_ptr == NULL) return; + png_ptr->transformations |= PNG_BGR; +} +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* turn on 16 bit byte swapping */ +void PNGAPI +png_set_swap(png_structp png_ptr) +{ + png_debug(1, "in png_set_swap\n"); + if(png_ptr == NULL) return; + if (png_ptr->bit_depth == 16) + png_ptr->transformations |= PNG_SWAP_BYTES; +} +#endif + +#if defined(PNG_READ_PACK_SUPPORTED) || defined(PNG_WRITE_PACK_SUPPORTED) +/* turn on pixel packing */ +void PNGAPI +png_set_packing(png_structp png_ptr) +{ + png_debug(1, "in png_set_packing\n"); + if(png_ptr == NULL) return; + if (png_ptr->bit_depth < 8) + { + png_ptr->transformations |= PNG_PACK; + png_ptr->usr_bit_depth = 8; + } +} +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) +/* turn on packed pixel swapping */ +void PNGAPI +png_set_packswap(png_structp png_ptr) +{ + png_debug(1, "in png_set_packswap\n"); + if(png_ptr == NULL) return; + if (png_ptr->bit_depth < 8) + png_ptr->transformations |= PNG_PACKSWAP; +} +#endif + +#if defined(PNG_READ_SHIFT_SUPPORTED) || defined(PNG_WRITE_SHIFT_SUPPORTED) +void PNGAPI +png_set_shift(png_structp png_ptr, png_color_8p true_bits) +{ + png_debug(1, "in png_set_shift\n"); + if(png_ptr == NULL) return; + png_ptr->transformations |= PNG_SHIFT; + png_ptr->shift = *true_bits; +} +#endif + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) +int PNGAPI +png_set_interlace_handling(png_structp png_ptr) +{ + png_debug(1, "in png_set_interlace handling\n"); + if (png_ptr && png_ptr->interlaced) + { + png_ptr->transformations |= PNG_INTERLACE; + return (7); + } + + return (1); +} +#endif + +#if defined(PNG_READ_FILLER_SUPPORTED) || defined(PNG_WRITE_FILLER_SUPPORTED) +/* Add a filler byte on read, or remove a filler or alpha byte on write. + * The filler type has changed in v0.95 to allow future 2-byte fillers + * for 48-bit input data, as well as to avoid problems with some compilers + * that don't like bytes as parameters. + */ +void PNGAPI +png_set_filler(png_structp png_ptr, png_uint_32 filler, int filler_loc) +{ + png_debug(1, "in png_set_filler\n"); + if(png_ptr == NULL) return; + png_ptr->transformations |= PNG_FILLER; + png_ptr->filler = (png_byte)filler; + if (filler_loc == PNG_FILLER_AFTER) + png_ptr->flags |= PNG_FLAG_FILLER_AFTER; + else + png_ptr->flags &= ~PNG_FLAG_FILLER_AFTER; + + /* This should probably go in the "do_read_filler" routine. + * I attempted to do that in libpng-1.0.1a but that caused problems + * so I restored it in libpng-1.0.2a + */ + + if (png_ptr->color_type == PNG_COLOR_TYPE_RGB) + { + png_ptr->usr_channels = 4; + } + + /* Also I added this in libpng-1.0.2a (what happens when we expand + * a less-than-8-bit grayscale to GA? */ + + if (png_ptr->color_type == PNG_COLOR_TYPE_GRAY && png_ptr->bit_depth >= 8) + { + png_ptr->usr_channels = 2; + } +} + +#if !defined(PNG_1_0_X) +/* Added to libpng-1.2.7 */ +void PNGAPI +png_set_add_alpha(png_structp png_ptr, png_uint_32 filler, int filler_loc) +{ + png_debug(1, "in png_set_add_alpha\n"); + if(png_ptr == NULL) return; + png_set_filler(png_ptr, filler, filler_loc); + png_ptr->transformations |= PNG_ADD_ALPHA; +} +#endif + +#endif + +#if defined(PNG_READ_SWAP_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +void PNGAPI +png_set_swap_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_swap_alpha\n"); + if(png_ptr == NULL) return; + png_ptr->transformations |= PNG_SWAP_ALPHA; +} +#endif + +#if defined(PNG_READ_INVERT_ALPHA_SUPPORTED) || \ + defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +void PNGAPI +png_set_invert_alpha(png_structp png_ptr) +{ + png_debug(1, "in png_set_invert_alpha\n"); + if(png_ptr == NULL) return; + png_ptr->transformations |= PNG_INVERT_ALPHA; +} +#endif + +#if defined(PNG_READ_INVERT_SUPPORTED) || defined(PNG_WRITE_INVERT_SUPPORTED) +void PNGAPI +png_set_invert_mono(png_structp png_ptr) +{ + png_debug(1, "in png_set_invert_mono\n"); + if(png_ptr == NULL) return; + png_ptr->transformations |= PNG_INVERT_MONO; +} + +/* invert monochrome grayscale data */ +void /* PRIVATE */ +png_do_invert(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_invert\n"); + /* This test removed from libpng version 1.0.13 and 1.2.0: + * if (row_info->bit_depth == 1 && + */ +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row == NULL || row_info == NULL) + return; +#endif + if (row_info->color_type == PNG_COLOR_TYPE_GRAY) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (i = 0; i < istop; i++) + { + *rp = (png_byte)(~(*rp)); + rp++; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + row_info->bit_depth == 8) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (i = 0; i < istop; i+=2) + { + *rp = (png_byte)(~(*rp)); + rp+=2; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + row_info->bit_depth == 16) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop = row_info->rowbytes; + + for (i = 0; i < istop; i+=4) + { + *rp = (png_byte)(~(*rp)); + *(rp+1) = (png_byte)(~(*(rp+1))); + rp+=4; + } + } +} +#endif + +#if defined(PNG_READ_SWAP_SUPPORTED) || defined(PNG_WRITE_SWAP_SUPPORTED) +/* swaps byte order on 16 bit depth images */ +void /* PRIVATE */ +png_do_swap(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_swap\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->bit_depth == 16) + { + png_bytep rp = row; + png_uint_32 i; + png_uint_32 istop= row_info->width * row_info->channels; + + for (i = 0; i < istop; i++, rp += 2) + { + png_byte t = *rp; + *rp = *(rp + 1); + *(rp + 1) = t; + } + } +} +#endif + +#if defined(PNG_READ_PACKSWAP_SUPPORTED)||defined(PNG_WRITE_PACKSWAP_SUPPORTED) +static PNG_CONST png_byte onebppswaptable[256] = { + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, + 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, + 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, + 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, + 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, + 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, + 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, + 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, + 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, + 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, + 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, + 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF +}; + +static PNG_CONST png_byte twobppswaptable[256] = { + 0x00, 0x40, 0x80, 0xC0, 0x10, 0x50, 0x90, 0xD0, + 0x20, 0x60, 0xA0, 0xE0, 0x30, 0x70, 0xB0, 0xF0, + 0x04, 0x44, 0x84, 0xC4, 0x14, 0x54, 0x94, 0xD4, + 0x24, 0x64, 0xA4, 0xE4, 0x34, 0x74, 0xB4, 0xF4, + 0x08, 0x48, 0x88, 0xC8, 0x18, 0x58, 0x98, 0xD8, + 0x28, 0x68, 0xA8, 0xE8, 0x38, 0x78, 0xB8, 0xF8, + 0x0C, 0x4C, 0x8C, 0xCC, 0x1C, 0x5C, 0x9C, 0xDC, + 0x2C, 0x6C, 0xAC, 0xEC, 0x3C, 0x7C, 0xBC, 0xFC, + 0x01, 0x41, 0x81, 0xC1, 0x11, 0x51, 0x91, 0xD1, + 0x21, 0x61, 0xA1, 0xE1, 0x31, 0x71, 0xB1, 0xF1, + 0x05, 0x45, 0x85, 0xC5, 0x15, 0x55, 0x95, 0xD5, + 0x25, 0x65, 0xA5, 0xE5, 0x35, 0x75, 0xB5, 0xF5, + 0x09, 0x49, 0x89, 0xC9, 0x19, 0x59, 0x99, 0xD9, + 0x29, 0x69, 0xA9, 0xE9, 0x39, 0x79, 0xB9, 0xF9, + 0x0D, 0x4D, 0x8D, 0xCD, 0x1D, 0x5D, 0x9D, 0xDD, + 0x2D, 0x6D, 0xAD, 0xED, 0x3D, 0x7D, 0xBD, 0xFD, + 0x02, 0x42, 0x82, 0xC2, 0x12, 0x52, 0x92, 0xD2, + 0x22, 0x62, 0xA2, 0xE2, 0x32, 0x72, 0xB2, 0xF2, + 0x06, 0x46, 0x86, 0xC6, 0x16, 0x56, 0x96, 0xD6, + 0x26, 0x66, 0xA6, 0xE6, 0x36, 0x76, 0xB6, 0xF6, + 0x0A, 0x4A, 0x8A, 0xCA, 0x1A, 0x5A, 0x9A, 0xDA, + 0x2A, 0x6A, 0xAA, 0xEA, 0x3A, 0x7A, 0xBA, 0xFA, + 0x0E, 0x4E, 0x8E, 0xCE, 0x1E, 0x5E, 0x9E, 0xDE, + 0x2E, 0x6E, 0xAE, 0xEE, 0x3E, 0x7E, 0xBE, 0xFE, + 0x03, 0x43, 0x83, 0xC3, 0x13, 0x53, 0x93, 0xD3, + 0x23, 0x63, 0xA3, 0xE3, 0x33, 0x73, 0xB3, 0xF3, + 0x07, 0x47, 0x87, 0xC7, 0x17, 0x57, 0x97, 0xD7, + 0x27, 0x67, 0xA7, 0xE7, 0x37, 0x77, 0xB7, 0xF7, + 0x0B, 0x4B, 0x8B, 0xCB, 0x1B, 0x5B, 0x9B, 0xDB, + 0x2B, 0x6B, 0xAB, 0xEB, 0x3B, 0x7B, 0xBB, 0xFB, + 0x0F, 0x4F, 0x8F, 0xCF, 0x1F, 0x5F, 0x9F, 0xDF, + 0x2F, 0x6F, 0xAF, 0xEF, 0x3F, 0x7F, 0xBF, 0xFF +}; + +static PNG_CONST png_byte fourbppswaptable[256] = { + 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, + 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, + 0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, + 0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1, + 0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72, + 0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2, + 0x03, 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73, + 0x83, 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3, + 0x04, 0x14, 0x24, 0x34, 0x44, 0x54, 0x64, 0x74, + 0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4, + 0x05, 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, + 0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5, + 0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76, + 0x86, 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6, + 0x07, 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, + 0x87, 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, + 0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, + 0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8, + 0x09, 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79, + 0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9, + 0x0A, 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A, + 0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA, + 0x0B, 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B, + 0x8B, 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB, + 0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C, + 0x8C, 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC, + 0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D, + 0x8D, 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD, + 0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E, + 0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE, + 0x0F, 0x1F, 0x2F, 0x3F, 0x4F, 0x5F, 0x6F, 0x7F, + 0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF +}; + +/* swaps pixel packing order within bytes */ +void /* PRIVATE */ +png_do_packswap(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_packswap\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->bit_depth < 8) + { + png_bytep rp, end, table; + + end = row + row_info->rowbytes; + + if (row_info->bit_depth == 1) + table = (png_bytep)onebppswaptable; + else if (row_info->bit_depth == 2) + table = (png_bytep)twobppswaptable; + else if (row_info->bit_depth == 4) + table = (png_bytep)fourbppswaptable; + else + return; + + for (rp = row; rp < end; rp++) + *rp = table[*rp]; + } +} +#endif /* PNG_READ_PACKSWAP_SUPPORTED or PNG_WRITE_PACKSWAP_SUPPORTED */ + +#if defined(PNG_WRITE_FILLER_SUPPORTED) || \ + defined(PNG_READ_STRIP_ALPHA_SUPPORTED) +/* remove filler or alpha byte(s) */ +void /* PRIVATE */ +png_do_strip_filler(png_row_infop row_info, png_bytep row, png_uint_32 flags) +{ + png_debug(1, "in png_do_strip_filler\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + png_bytep sp=row; + png_bytep dp=row; + png_uint_32 row_width=row_info->width; + png_uint_32 i; + + if ((row_info->color_type == PNG_COLOR_TYPE_RGB || + (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA && + (flags & PNG_FLAG_STRIP_ALPHA))) && + row_info->channels == 4) + { + if (row_info->bit_depth == 8) + { + /* This converts from RGBX or RGBA to RGB */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + dp+=3; sp+=4; + for (i = 1; i < row_width; i++) + { + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + sp++; + } + } + /* This converts from XRGB or ARGB to RGB */ + else + { + for (i = 0; i < row_width; i++) + { + sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 24; + row_info->rowbytes = row_width * 3; + } + else /* if (row_info->bit_depth == 16) */ + { + if (flags & PNG_FLAG_FILLER_AFTER) + { + /* This converts from RRGGBBXX or RRGGBBAA to RRGGBB */ + sp += 8; dp += 6; + for (i = 1; i < row_width; i++) + { + /* This could be (although png_memcpy is probably slower): + png_memcpy(dp, sp, 6); + sp += 8; + dp += 6; + */ + + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + sp += 2; + } + } + else + { + /* This converts from XXRRGGBB or AARRGGBB to RRGGBB */ + for (i = 0; i < row_width; i++) + { + /* This could be (although png_memcpy is probably slower): + png_memcpy(dp, sp, 6); + sp += 8; + dp += 6; + */ + + sp+=2; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 48; + row_info->rowbytes = row_width * 6; + } + row_info->channels = 3; + } + else if ((row_info->color_type == PNG_COLOR_TYPE_GRAY || + (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA && + (flags & PNG_FLAG_STRIP_ALPHA))) && + row_info->channels == 2) + { + if (row_info->bit_depth == 8) + { + /* This converts from GX or GA to G */ + if (flags & PNG_FLAG_FILLER_AFTER) + { + for (i = 0; i < row_width; i++) + { + *dp++ = *sp++; + sp++; + } + } + /* This converts from XG or AG to G */ + else + { + for (i = 0; i < row_width; i++) + { + sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 8; + row_info->rowbytes = row_width; + } + else /* if (row_info->bit_depth == 16) */ + { + if (flags & PNG_FLAG_FILLER_AFTER) + { + /* This converts from GGXX or GGAA to GG */ + sp += 4; dp += 2; + for (i = 1; i < row_width; i++) + { + *dp++ = *sp++; + *dp++ = *sp++; + sp += 2; + } + } + else + { + /* This converts from XXGG or AAGG to GG */ + for (i = 0; i < row_width; i++) + { + sp += 2; + *dp++ = *sp++; + *dp++ = *sp++; + } + } + row_info->pixel_depth = 16; + row_info->rowbytes = row_width * 2; + } + row_info->channels = 1; + } + if (flags & PNG_FLAG_STRIP_ALPHA) + row_info->color_type &= ~PNG_COLOR_MASK_ALPHA; + } +} +#endif + +#if defined(PNG_READ_BGR_SUPPORTED) || defined(PNG_WRITE_BGR_SUPPORTED) +/* swaps red and blue bytes within a pixel */ +void /* PRIVATE */ +png_do_bgr(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_bgr\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 3) + { + png_byte save = *rp; + *rp = *(rp + 2); + *(rp + 2) = save; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 4) + { + png_byte save = *rp; + *rp = *(rp + 2); + *(rp + 2) = save; + } + } + } + else if (row_info->bit_depth == 16) + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 6) + { + png_byte save = *rp; + *rp = *(rp + 4); + *(rp + 4) = save; + save = *(rp + 1); + *(rp + 1) = *(rp + 5); + *(rp + 5) = save; + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + png_bytep rp; + png_uint_32 i; + + for (i = 0, rp = row; i < row_width; i++, rp += 8) + { + png_byte save = *rp; + *rp = *(rp + 4); + *(rp + 4) = save; + save = *(rp + 1); + *(rp + 1) = *(rp + 5); + *(rp + 5) = save; + } + } + } + } +} +#endif /* PNG_READ_BGR_SUPPORTED or PNG_WRITE_BGR_SUPPORTED */ + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) || \ + defined(PNG_LEGACY_SUPPORTED) +void PNGAPI +png_set_user_transform_info(png_structp png_ptr, png_voidp + user_transform_ptr, int user_transform_depth, int user_transform_channels) +{ + png_debug(1, "in png_set_user_transform_info\n"); + if(png_ptr == NULL) return; +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) + png_ptr->user_transform_ptr = user_transform_ptr; + png_ptr->user_transform_depth = (png_byte)user_transform_depth; + png_ptr->user_transform_channels = (png_byte)user_transform_channels; +#else + if(user_transform_ptr || user_transform_depth || user_transform_channels) + png_warning(png_ptr, + "This version of libpng does not support user transform info"); +#endif +} +#endif + +/* This function returns a pointer to the user_transform_ptr associated with + * the user transform functions. The application should free any memory + * associated with this pointer before png_write_destroy and png_read_destroy + * are called. + */ +png_voidp PNGAPI +png_get_user_transform_ptr(png_structp png_ptr) +{ +#if defined(PNG_USER_TRANSFORM_PTR_SUPPORTED) + if (png_ptr == NULL) return (NULL); + return ((png_voidp)png_ptr->user_transform_ptr); +#else + return (NULL); +#endif +} +#endif /* PNG_READ_SUPPORTED || PNG_WRITE_SUPPORTED */ diff --git a/Engine/lib/lpng/pngvcrd.c b/Engine/lib/lpng/pngvcrd.c new file mode 100644 index 000000000..ce4233efe --- /dev/null +++ b/Engine/lib/lpng/pngvcrd.c @@ -0,0 +1 @@ +/* pnggvrd.c was removed from libpng-1.2.20. */ diff --git a/Engine/lib/lpng/pngwio.c b/Engine/lib/lpng/pngwio.c new file mode 100644 index 000000000..371a4fad6 --- /dev/null +++ b/Engine/lib/lpng/pngwio.c @@ -0,0 +1,234 @@ + +/* pngwio.c - functions for data output + * + * Last changed in libpng 1.2.13 November 13, 2006 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2006 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This file provides a location for all output. Users who need + * special handling are expected to write functions that have the same + * arguments as these and perform similar functions, but that possibly + * use different output methods. Note that you shouldn't change these + * functions, but rather write replacement functions and then change + * them at run time with png_set_write_fn(...). + */ + +#define PNG_INTERNAL +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED + +/* Write the data to whatever output you are using. The default routine + writes to a file pointer. Note that this routine sometimes gets called + with very small lengths, so you should implement some kind of simple + buffering if you are using unbuffered writes. This should never be asked + to write more than 64K on a 16 bit machine. */ + +void /* PRIVATE */ +png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + if (png_ptr->write_data_fn != NULL ) + (*(png_ptr->write_data_fn))(png_ptr, data, length); + else + png_error(png_ptr, "Call to NULL write function"); +} + +#if !defined(PNG_NO_STDIO) +/* This is the function that does the actual writing of data. If you are + not writing to a standard C stream, you should create a replacement + write_data function and use it at run time with png_set_write_fn(), rather + than changing the library. */ +#ifndef USE_FAR_KEYWORD +void PNGAPI +png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + + if(png_ptr == NULL) return; +#if defined(_WIN32_WCE) + if ( !WriteFile((HANDLE)(png_ptr->io_ptr), data, length, &check, NULL) ) + check = 0; +#else + check = fwrite(data, 1, length, (png_FILE_p)(png_ptr->io_ptr)); +#endif + if (check != length) + png_error(png_ptr, "Write Error"); +} +#else +/* this is the model-independent version. Since the standard I/O library + can't handle far buffers in the medium and small models, we have to copy + the data. +*/ + +#define NEAR_BUF_SIZE 1024 +#define MIN(a,b) (a <= b ? a : b) + +void PNGAPI +png_default_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + png_byte *near_data; /* Needs to be "png_byte *" instead of "png_bytep" */ + png_FILE_p io_ptr; + + if(png_ptr == NULL) return; + /* Check if data really is near. If so, use usual code. */ + near_data = (png_byte *)CVT_PTR_NOCHECK(data); + io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); + if ((png_bytep)near_data == data) + { +#if defined(_WIN32_WCE) + if ( !WriteFile(io_ptr, near_data, length, &check, NULL) ) + check = 0; +#else + check = fwrite(near_data, 1, length, io_ptr); +#endif + } + else + { + png_byte buf[NEAR_BUF_SIZE]; + png_size_t written, remaining, err; + check = 0; + remaining = length; + do + { + written = MIN(NEAR_BUF_SIZE, remaining); + png_memcpy(buf, data, written); /* copy far buffer to near buffer */ +#if defined(_WIN32_WCE) + if ( !WriteFile(io_ptr, buf, written, &err, NULL) ) + err = 0; +#else + err = fwrite(buf, 1, written, io_ptr); +#endif + if (err != written) + break; + else + check += err; + data += written; + remaining -= written; + } + while (remaining != 0); + } + if (check != length) + png_error(png_ptr, "Write Error"); +} + +#endif +#endif + +/* This function is called to output any data pending writing (normally + to disk). After png_flush is called, there should be no data pending + writing in any buffers. */ +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +void /* PRIVATE */ +png_flush(png_structp png_ptr) +{ + if (png_ptr->output_flush_fn != NULL) + (*(png_ptr->output_flush_fn))(png_ptr); +} + +#if !defined(PNG_NO_STDIO) +void PNGAPI +png_default_flush(png_structp png_ptr) +{ +#if !defined(_WIN32_WCE) + png_FILE_p io_ptr; +#endif + if(png_ptr == NULL) return; +#if !defined(_WIN32_WCE) + io_ptr = (png_FILE_p)CVT_PTR((png_ptr->io_ptr)); + if (io_ptr != NULL) + fflush(io_ptr); +#endif +} +#endif +#endif + +/* This function allows the application to supply new output functions for + libpng if standard C streams aren't being used. + + This function takes as its arguments: + png_ptr - pointer to a png output data structure + io_ptr - pointer to user supplied structure containing info about + the output functions. May be NULL. + write_data_fn - pointer to a new output function that takes as its + arguments a pointer to a png_struct, a pointer to + data to be written, and a 32-bit unsigned int that is + the number of bytes to be written. The new write + function should call png_error(png_ptr, "Error msg") + to exit and output any fatal error messages. + flush_data_fn - pointer to a new flush function that takes as its + arguments a pointer to a png_struct. After a call to + the flush function, there should be no data in any buffers + or pending transmission. If the output method doesn't do + any buffering of ouput, a function prototype must still be + supplied although it doesn't have to do anything. If + PNG_WRITE_FLUSH_SUPPORTED is not defined at libpng compile + time, output_flush_fn will be ignored, although it must be + supplied for compatibility. */ +void PNGAPI +png_set_write_fn(png_structp png_ptr, png_voidp io_ptr, + png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn) +{ + if(png_ptr == NULL) return; + png_ptr->io_ptr = io_ptr; + +#if !defined(PNG_NO_STDIO) + if (write_data_fn != NULL) + png_ptr->write_data_fn = write_data_fn; + else + png_ptr->write_data_fn = png_default_write_data; +#else + png_ptr->write_data_fn = write_data_fn; +#endif + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +#if !defined(PNG_NO_STDIO) + if (output_flush_fn != NULL) + png_ptr->output_flush_fn = output_flush_fn; + else + png_ptr->output_flush_fn = png_default_flush; +#else + png_ptr->output_flush_fn = output_flush_fn; +#endif +#endif /* PNG_WRITE_FLUSH_SUPPORTED */ + + /* It is an error to read while writing a png file */ + if (png_ptr->read_data_fn != NULL) + { + png_ptr->read_data_fn = NULL; + png_warning(png_ptr, + "Attempted to set both read_data_fn and write_data_fn in"); + png_warning(png_ptr, + "the same structure. Resetting read_data_fn to NULL."); + } +} + +#if defined(USE_FAR_KEYWORD) +#if defined(_MSC_VER) +void *png_far_to_near(png_structp png_ptr,png_voidp ptr, int check) +{ + void *near_ptr; + void FAR *far_ptr; + FP_OFF(near_ptr) = FP_OFF(ptr); + far_ptr = (void FAR *)near_ptr; + if(check != 0) + if(FP_SEG(ptr) != FP_SEG(far_ptr)) + png_error(png_ptr,"segment lost in conversion"); + return(near_ptr); +} +# else +void *png_far_to_near(png_structp png_ptr,png_voidp ptr, int check) +{ + void *near_ptr; + void FAR *far_ptr; + near_ptr = (void FAR *)ptr; + far_ptr = (void FAR *)near_ptr; + if(check != 0) + if(far_ptr != ptr) + png_error(png_ptr,"segment lost in conversion"); + return(near_ptr); +} +# endif +# endif +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/Engine/lib/lpng/pngwrite.c b/Engine/lib/lpng/pngwrite.c new file mode 100644 index 000000000..b1f6a593a --- /dev/null +++ b/Engine/lib/lpng/pngwrite.c @@ -0,0 +1,1516 @@ + +/* pngwrite.c - general routines to write a PNG file + * + * Last changed in libpng 1.2.24 December 14, 2007 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2007 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +/* get internal access to png.h */ +#define PNG_INTERNAL +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED + +/* Writes all the PNG information. This is the suggested way to use the + * library. If you have a new chunk to add, make a function to write it, + * and put it in the correct location here. If you want the chunk written + * after the image data, put it in png_write_end(). I strongly encourage + * you to supply a PNG_INFO_ flag, and check info_ptr->valid before writing + * the chunk, as that will keep the code from breaking if you want to just + * write a plain PNG file. If you have long comments, I suggest writing + * them in png_write_end(), and compressing them. + */ +void PNGAPI +png_write_info_before_PLTE(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_write_info_before_PLTE\n"); + if (png_ptr == NULL || info_ptr == NULL) + return; + if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE)) + { + png_write_sig(png_ptr); /* write PNG signature */ +#if defined(PNG_MNG_FEATURES_SUPPORTED) + if((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE)&&(png_ptr->mng_features_permitted)) + { + png_warning(png_ptr,"MNG features are not allowed in a PNG datastream"); + png_ptr->mng_features_permitted=0; + } +#endif + /* write IHDR information. */ + png_write_IHDR(png_ptr, info_ptr->width, info_ptr->height, + info_ptr->bit_depth, info_ptr->color_type, info_ptr->compression_type, + info_ptr->filter_type, +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) + info_ptr->interlace_type); +#else + 0); +#endif + /* the rest of these check to see if the valid field has the appropriate + flag set, and if it does, writes the chunk. */ +#if defined(PNG_WRITE_gAMA_SUPPORTED) + if (info_ptr->valid & PNG_INFO_gAMA) + { +# ifdef PNG_FLOATING_POINT_SUPPORTED + png_write_gAMA(png_ptr, info_ptr->gamma); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + png_write_gAMA_fixed(png_ptr, info_ptr->int_gamma); +# endif +#endif + } +#endif +#if defined(PNG_WRITE_sRGB_SUPPORTED) + if (info_ptr->valid & PNG_INFO_sRGB) + png_write_sRGB(png_ptr, (int)info_ptr->srgb_intent); +#endif +#if defined(PNG_WRITE_iCCP_SUPPORTED) + if (info_ptr->valid & PNG_INFO_iCCP) + png_write_iCCP(png_ptr, info_ptr->iccp_name, PNG_COMPRESSION_TYPE_BASE, + info_ptr->iccp_profile, (int)info_ptr->iccp_proflen); +#endif +#if defined(PNG_WRITE_sBIT_SUPPORTED) + if (info_ptr->valid & PNG_INFO_sBIT) + png_write_sBIT(png_ptr, &(info_ptr->sig_bit), info_ptr->color_type); +#endif +#if defined(PNG_WRITE_cHRM_SUPPORTED) + if (info_ptr->valid & PNG_INFO_cHRM) + { +#ifdef PNG_FLOATING_POINT_SUPPORTED + png_write_cHRM(png_ptr, + info_ptr->x_white, info_ptr->y_white, + info_ptr->x_red, info_ptr->y_red, + info_ptr->x_green, info_ptr->y_green, + info_ptr->x_blue, info_ptr->y_blue); +#else +# ifdef PNG_FIXED_POINT_SUPPORTED + png_write_cHRM_fixed(png_ptr, + info_ptr->int_x_white, info_ptr->int_y_white, + info_ptr->int_x_red, info_ptr->int_y_red, + info_ptr->int_x_green, info_ptr->int_y_green, + info_ptr->int_x_blue, info_ptr->int_y_blue); +# endif +#endif + } +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) + if (info_ptr->unknown_chunks_num) + { + png_unknown_chunk *up; + + png_debug(5, "writing extra chunks\n"); + + for (up = info_ptr->unknown_chunks; + up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; + up++) + { + int keep=png_handle_as_unknown(png_ptr, up->name); + if (keep != PNG_HANDLE_CHUNK_NEVER && + up->location && !(up->location & PNG_HAVE_PLTE) && + !(up->location & PNG_HAVE_IDAT) && + ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || + (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) + { + png_write_chunk(png_ptr, up->name, up->data, up->size); + } + } + } +#endif + png_ptr->mode |= PNG_WROTE_INFO_BEFORE_PLTE; + } +} + +void PNGAPI +png_write_info(png_structp png_ptr, png_infop info_ptr) +{ +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) + int i; +#endif + + png_debug(1, "in png_write_info\n"); + + if (png_ptr == NULL || info_ptr == NULL) + return; + + png_write_info_before_PLTE(png_ptr, info_ptr); + + if (info_ptr->valid & PNG_INFO_PLTE) + png_write_PLTE(png_ptr, info_ptr->palette, + (png_uint_32)info_ptr->num_palette); + else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + png_error(png_ptr, "Valid palette required for paletted images"); + +#if defined(PNG_WRITE_tRNS_SUPPORTED) + if (info_ptr->valid & PNG_INFO_tRNS) + { +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) + /* invert the alpha channel (in tRNS) */ + if ((png_ptr->transformations & PNG_INVERT_ALPHA) && + info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + int j; + for (j=0; j<(int)info_ptr->num_trans; j++) + info_ptr->trans[j] = (png_byte)(255 - info_ptr->trans[j]); + } +#endif + png_write_tRNS(png_ptr, info_ptr->trans, &(info_ptr->trans_values), + info_ptr->num_trans, info_ptr->color_type); + } +#endif +#if defined(PNG_WRITE_bKGD_SUPPORTED) + if (info_ptr->valid & PNG_INFO_bKGD) + png_write_bKGD(png_ptr, &(info_ptr->background), info_ptr->color_type); +#endif +#if defined(PNG_WRITE_hIST_SUPPORTED) + if (info_ptr->valid & PNG_INFO_hIST) + png_write_hIST(png_ptr, info_ptr->hist, info_ptr->num_palette); +#endif +#if defined(PNG_WRITE_oFFs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_oFFs) + png_write_oFFs(png_ptr, info_ptr->x_offset, info_ptr->y_offset, + info_ptr->offset_unit_type); +#endif +#if defined(PNG_WRITE_pCAL_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pCAL) + png_write_pCAL(png_ptr, info_ptr->pcal_purpose, info_ptr->pcal_X0, + info_ptr->pcal_X1, info_ptr->pcal_type, info_ptr->pcal_nparams, + info_ptr->pcal_units, info_ptr->pcal_params); +#endif +#if defined(PNG_WRITE_sCAL_SUPPORTED) + if (info_ptr->valid & PNG_INFO_sCAL) +#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO) + png_write_sCAL(png_ptr, (int)info_ptr->scal_unit, + info_ptr->scal_pixel_width, info_ptr->scal_pixel_height); +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + png_write_sCAL_s(png_ptr, (int)info_ptr->scal_unit, + info_ptr->scal_s_width, info_ptr->scal_s_height); +#else + png_warning(png_ptr, + "png_write_sCAL not supported; sCAL chunk not written."); +#endif +#endif +#endif +#if defined(PNG_WRITE_pHYs_SUPPORTED) + if (info_ptr->valid & PNG_INFO_pHYs) + png_write_pHYs(png_ptr, info_ptr->x_pixels_per_unit, + info_ptr->y_pixels_per_unit, info_ptr->phys_unit_type); +#endif +#if defined(PNG_WRITE_tIME_SUPPORTED) + if (info_ptr->valid & PNG_INFO_tIME) + { + png_write_tIME(png_ptr, &(info_ptr->mod_time)); + png_ptr->mode |= PNG_WROTE_tIME; + } +#endif +#if defined(PNG_WRITE_sPLT_SUPPORTED) + if (info_ptr->valid & PNG_INFO_sPLT) + for (i = 0; i < (int)info_ptr->splt_palettes_num; i++) + png_write_sPLT(png_ptr, info_ptr->splt_palettes + i); +#endif +#if defined(PNG_WRITE_TEXT_SUPPORTED) + /* Check to see if we need to write text chunks */ + for (i = 0; i < info_ptr->num_text; i++) + { + png_debug2(2, "Writing header text chunk %d, type %d\n", i, + info_ptr->text[i].compression); + /* an internationalized chunk? */ + if (info_ptr->text[i].compression > 0) + { +#if defined(PNG_WRITE_iTXt_SUPPORTED) + /* write international chunk */ + png_write_iTXt(png_ptr, + info_ptr->text[i].compression, + info_ptr->text[i].key, + info_ptr->text[i].lang, + info_ptr->text[i].lang_key, + info_ptr->text[i].text); +#else + png_warning(png_ptr, "Unable to write international text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + /* If we want a compressed text chunk */ + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_zTXt) + { +#if defined(PNG_WRITE_zTXt_SUPPORTED) + /* write compressed chunk */ + png_write_zTXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0, + info_ptr->text[i].compression); +#else + png_warning(png_ptr, "Unable to write compressed text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; + } + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) + { +#if defined(PNG_WRITE_tEXt_SUPPORTED) + /* write uncompressed chunk */ + png_write_tEXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, + 0); +#else + png_warning(png_ptr, "Unable to write uncompressed text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + } +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) + if (info_ptr->unknown_chunks_num) + { + png_unknown_chunk *up; + + png_debug(5, "writing extra chunks\n"); + + for (up = info_ptr->unknown_chunks; + up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; + up++) + { + int keep=png_handle_as_unknown(png_ptr, up->name); + if (keep != PNG_HANDLE_CHUNK_NEVER && + up->location && (up->location & PNG_HAVE_PLTE) && + !(up->location & PNG_HAVE_IDAT) && + ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || + (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) + { + png_write_chunk(png_ptr, up->name, up->data, up->size); + } + } + } +#endif +} + +/* Writes the end of the PNG file. If you don't want to write comments or + * time information, you can pass NULL for info. If you already wrote these + * in png_write_info(), do not write them again here. If you have long + * comments, I suggest writing them here, and compressing them. + */ +void PNGAPI +png_write_end(png_structp png_ptr, png_infop info_ptr) +{ + png_debug(1, "in png_write_end\n"); + if (png_ptr == NULL) + return; + if (!(png_ptr->mode & PNG_HAVE_IDAT)) + png_error(png_ptr, "No IDATs written into file"); + + /* see if user wants us to write information chunks */ + if (info_ptr != NULL) + { +#if defined(PNG_WRITE_TEXT_SUPPORTED) + int i; /* local index variable */ +#endif +#if defined(PNG_WRITE_tIME_SUPPORTED) + /* check to see if user has supplied a time chunk */ + if ((info_ptr->valid & PNG_INFO_tIME) && + !(png_ptr->mode & PNG_WROTE_tIME)) + png_write_tIME(png_ptr, &(info_ptr->mod_time)); +#endif +#if defined(PNG_WRITE_TEXT_SUPPORTED) + /* loop through comment chunks */ + for (i = 0; i < info_ptr->num_text; i++) + { + png_debug2(2, "Writing trailer text chunk %d, type %d\n", i, + info_ptr->text[i].compression); + /* an internationalized chunk? */ + if (info_ptr->text[i].compression > 0) + { +#if defined(PNG_WRITE_iTXt_SUPPORTED) + /* write international chunk */ + png_write_iTXt(png_ptr, + info_ptr->text[i].compression, + info_ptr->text[i].key, + info_ptr->text[i].lang, + info_ptr->text[i].lang_key, + info_ptr->text[i].text); +#else + png_warning(png_ptr, "Unable to write international text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + else if (info_ptr->text[i].compression >= PNG_TEXT_COMPRESSION_zTXt) + { +#if defined(PNG_WRITE_zTXt_SUPPORTED) + /* write compressed chunk */ + png_write_zTXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0, + info_ptr->text[i].compression); +#else + png_warning(png_ptr, "Unable to write compressed text"); +#endif + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_zTXt_WR; + } + else if (info_ptr->text[i].compression == PNG_TEXT_COMPRESSION_NONE) + { +#if defined(PNG_WRITE_tEXt_SUPPORTED) + /* write uncompressed chunk */ + png_write_tEXt(png_ptr, info_ptr->text[i].key, + info_ptr->text[i].text, 0); +#else + png_warning(png_ptr, "Unable to write uncompressed text"); +#endif + + /* Mark this chunk as written */ + info_ptr->text[i].compression = PNG_TEXT_COMPRESSION_NONE_WR; + } + } +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) + if (info_ptr->unknown_chunks_num) + { + png_unknown_chunk *up; + + png_debug(5, "writing extra chunks\n"); + + for (up = info_ptr->unknown_chunks; + up < info_ptr->unknown_chunks + info_ptr->unknown_chunks_num; + up++) + { + int keep=png_handle_as_unknown(png_ptr, up->name); + if (keep != PNG_HANDLE_CHUNK_NEVER && + up->location && (up->location & PNG_AFTER_IDAT) && + ((up->name[3] & 0x20) || keep == PNG_HANDLE_CHUNK_ALWAYS || + (png_ptr->flags & PNG_FLAG_KEEP_UNSAFE_CHUNKS))) + { + png_write_chunk(png_ptr, up->name, up->data, up->size); + } + } + } +#endif + } + + png_ptr->mode |= PNG_AFTER_IDAT; + + /* write end of PNG file */ + png_write_IEND(png_ptr); +} + +#if defined(PNG_WRITE_tIME_SUPPORTED) +#if !defined(_WIN32_WCE) +/* "time.h" functions are not supported on WindowsCE */ +void PNGAPI +png_convert_from_struct_tm(png_timep ptime, struct tm FAR * ttime) +{ + png_debug(1, "in png_convert_from_struct_tm\n"); + ptime->year = (png_uint_16)(1900 + ttime->tm_year); + ptime->month = (png_byte)(ttime->tm_mon + 1); + ptime->day = (png_byte)ttime->tm_mday; + ptime->hour = (png_byte)ttime->tm_hour; + ptime->minute = (png_byte)ttime->tm_min; + ptime->second = (png_byte)ttime->tm_sec; +} + +void PNGAPI +png_convert_from_time_t(png_timep ptime, time_t ttime) +{ + struct tm *tbuf; + + png_debug(1, "in png_convert_from_time_t\n"); + tbuf = gmtime(&ttime); + png_convert_from_struct_tm(ptime, tbuf); +} +#endif +#endif + +/* Initialize png_ptr structure, and allocate any memory needed */ +png_structp PNGAPI +png_create_write_struct(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn) +{ +#ifdef PNG_USER_MEM_SUPPORTED + return (png_create_write_struct_2(user_png_ver, error_ptr, error_fn, + warn_fn, png_voidp_NULL, png_malloc_ptr_NULL, png_free_ptr_NULL)); +} + +/* Alternate initialize png_ptr structure, and allocate any memory needed */ +png_structp PNGAPI +png_create_write_struct_2(png_const_charp user_png_ver, png_voidp error_ptr, + png_error_ptr error_fn, png_error_ptr warn_fn, png_voidp mem_ptr, + png_malloc_ptr malloc_fn, png_free_ptr free_fn) +{ +#endif /* PNG_USER_MEM_SUPPORTED */ + png_structp png_ptr; +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + jmp_buf jmpbuf; +#endif +#endif + int i; + png_debug(1, "in png_create_write_struct\n"); +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr = (png_structp)png_create_struct_2(PNG_STRUCT_PNG, + (png_malloc_ptr)malloc_fn, (png_voidp)mem_ptr); +#else + png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); +#endif /* PNG_USER_MEM_SUPPORTED */ + if (png_ptr == NULL) + return (NULL); + + /* added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_ptr->user_width_max=PNG_USER_WIDTH_MAX; + png_ptr->user_height_max=PNG_USER_HEIGHT_MAX; +#endif + +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + if (setjmp(png_ptr->jmpbuf)) +#endif + { + png_free(png_ptr, png_ptr->zbuf); + png_ptr->zbuf=NULL; + png_destroy_struct(png_ptr); + return (NULL); + } +#ifdef USE_FAR_KEYWORD + png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf)); +#endif +#endif + +#ifdef PNG_USER_MEM_SUPPORTED + png_set_mem_fn(png_ptr, mem_ptr, malloc_fn, free_fn); +#endif /* PNG_USER_MEM_SUPPORTED */ + png_set_error_fn(png_ptr, error_ptr, error_fn, warn_fn); + + i=0; + do + { + if(user_png_ver[i] != png_libpng_ver[i]) + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; + } while (png_libpng_ver[i++]); + + if (png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH) + { + /* Libpng 0.90 and later are binary incompatible with libpng 0.89, so + * we must recompile any applications that use any older library version. + * For versions after libpng 1.0, we will be compatible, so we need + * only check the first digit. + */ + if (user_png_ver == NULL || user_png_ver[0] != png_libpng_ver[0] || + (user_png_ver[0] == '1' && user_png_ver[2] != png_libpng_ver[2]) || + (user_png_ver[0] == '0' && user_png_ver[2] < '9')) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char msg[80]; + if (user_png_ver) + { + png_snprintf(msg, 80, + "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + png_snprintf(msg, 80, + "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); +#endif +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "Incompatible libpng version in application and library"); + } + } + + /* initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + + png_set_write_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL, + png_flush_ptr_NULL); + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT, + 1, png_doublep_NULL, png_doublep_NULL); +#endif + +#ifdef PNG_SETJMP_SUPPORTED +/* Applications that neglect to set up their own setjmp() and then encounter + a png_error() will longjmp here. Since the jmpbuf is then meaningless we + abort instead of returning. */ +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) + PNG_ABORT(); + png_memcpy(png_ptr->jmpbuf,jmpbuf,png_sizeof(jmp_buf)); +#else + if (setjmp(png_ptr->jmpbuf)) + PNG_ABORT(); +#endif +#endif + return (png_ptr); +} + +/* Initialize png_ptr structure, and allocate any memory needed */ +#if defined(PNG_1_0_X) || defined(PNG_1_2_X) +/* Deprecated. */ +#undef png_write_init +void PNGAPI +png_write_init(png_structp png_ptr) +{ + /* We only come here via pre-1.0.7-compiled applications */ + png_write_init_2(png_ptr, "1.0.6 or earlier", 0, 0); +} + +void PNGAPI +png_write_init_2(png_structp png_ptr, png_const_charp user_png_ver, + png_size_t png_struct_size, png_size_t png_info_size) +{ + /* We only come here via pre-1.0.12-compiled applications */ + if(png_ptr == NULL) return; +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + if(png_sizeof(png_struct) > png_struct_size || + png_sizeof(png_info) > png_info_size) + { + char msg[80]; + png_ptr->warning_fn=NULL; + if (user_png_ver) + { + png_snprintf(msg, 80, + "Application was compiled with png.h from libpng-%.20s", + user_png_ver); + png_warning(png_ptr, msg); + } + png_snprintf(msg, 80, + "Application is running with png.c from libpng-%.20s", + png_libpng_ver); + png_warning(png_ptr, msg); + } +#endif + if(png_sizeof(png_struct) > png_struct_size) + { + png_ptr->error_fn=NULL; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "The png struct allocated by the application for writing is too small."); + } + if(png_sizeof(png_info) > png_info_size) + { + png_ptr->error_fn=NULL; +#ifdef PNG_ERROR_NUMBERS_SUPPORTED + png_ptr->flags=0; +#endif + png_error(png_ptr, + "The info struct allocated by the application for writing is too small."); + } + png_write_init_3(&png_ptr, user_png_ver, png_struct_size); +} +#endif /* PNG_1_0_X || PNG_1_2_X */ + + +void PNGAPI +png_write_init_3(png_structpp ptr_ptr, png_const_charp user_png_ver, + png_size_t png_struct_size) +{ + png_structp png_ptr=*ptr_ptr; +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; /* to save current jump buffer */ +#endif + + int i = 0; + + if (png_ptr == NULL) + return; + + do + { + if (user_png_ver[i] != png_libpng_ver[i]) + { +#ifdef PNG_LEGACY_SUPPORTED + png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH; +#else + png_ptr->warning_fn=NULL; + png_warning(png_ptr, + "Application uses deprecated png_write_init() and should be recompiled."); + break; +#endif + } + } while (png_libpng_ver[i++]); + + png_debug(1, "in png_write_init_3\n"); + +#ifdef PNG_SETJMP_SUPPORTED + /* save jump buffer and error functions */ + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf)); +#endif + + if (png_sizeof(png_struct) > png_struct_size) + { + png_destroy_struct(png_ptr); + png_ptr = (png_structp)png_create_struct(PNG_STRUCT_PNG); + *ptr_ptr = png_ptr; + } + + /* reset all variables to 0 */ + png_memset(png_ptr, 0, png_sizeof (png_struct)); + + /* added at libpng-1.2.6 */ +#ifdef PNG_SET_USER_LIMITS_SUPPORTED + png_ptr->user_width_max=PNG_USER_WIDTH_MAX; + png_ptr->user_height_max=PNG_USER_HEIGHT_MAX; +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* restore jump buffer */ + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf)); +#endif + + png_set_write_fn(png_ptr, png_voidp_NULL, png_rw_ptr_NULL, + png_flush_ptr_NULL); + + /* initialize zbuf - compression buffer */ + png_ptr->zbuf_size = PNG_ZBUF_SIZE; + png_ptr->zbuf = (png_bytep)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + png_set_filter_heuristics(png_ptr, PNG_FILTER_HEURISTIC_DEFAULT, + 1, png_doublep_NULL, png_doublep_NULL); +#endif +} + +/* Write a few rows of image data. If the image is interlaced, + * either you will have to write the 7 sub images, or, if you + * have called png_set_interlace_handling(), you will have to + * "write" the image seven times. + */ +void PNGAPI +png_write_rows(png_structp png_ptr, png_bytepp row, + png_uint_32 num_rows) +{ + png_uint_32 i; /* row counter */ + png_bytepp rp; /* row pointer */ + + png_debug(1, "in png_write_rows\n"); + + if (png_ptr == NULL) + return; + + /* loop through the rows */ + for (i = 0, rp = row; i < num_rows; i++, rp++) + { + png_write_row(png_ptr, *rp); + } +} + +/* Write the image. You only need to call this function once, even + * if you are writing an interlaced image. + */ +void PNGAPI +png_write_image(png_structp png_ptr, png_bytepp image) +{ + png_uint_32 i; /* row index */ + int pass, num_pass; /* pass variables */ + png_bytepp rp; /* points to current row */ + + if (png_ptr == NULL) + return; + + png_debug(1, "in png_write_image\n"); +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) + /* intialize interlace handling. If image is not interlaced, + this will set pass to 1 */ + num_pass = png_set_interlace_handling(png_ptr); +#else + num_pass = 1; +#endif + /* loop through passes */ + for (pass = 0; pass < num_pass; pass++) + { + /* loop through image */ + for (i = 0, rp = image; i < png_ptr->height; i++, rp++) + { + png_write_row(png_ptr, *rp); + } + } +} + +/* called by user to write a row of image data */ +void PNGAPI +png_write_row(png_structp png_ptr, png_bytep row) +{ + if (png_ptr == NULL) + return; + png_debug2(1, "in png_write_row (row %ld, pass %d)\n", + png_ptr->row_number, png_ptr->pass); + + /* initialize transformations and other stuff if first time */ + if (png_ptr->row_number == 0 && png_ptr->pass == 0) + { + /* make sure we wrote the header info */ + if (!(png_ptr->mode & PNG_WROTE_INFO_BEFORE_PLTE)) + png_error(png_ptr, + "png_write_info was never called before png_write_row."); + + /* check for transforms that have been set but were defined out */ +#if !defined(PNG_WRITE_INVERT_SUPPORTED) && defined(PNG_READ_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_warning(png_ptr, "PNG_WRITE_INVERT_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_FILLER_SUPPORTED) && defined(PNG_READ_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_warning(png_ptr, "PNG_WRITE_FILLER_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_PACKSWAP_SUPPORTED) && defined(PNG_READ_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_warning(png_ptr, "PNG_WRITE_PACKSWAP_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_PACK_SUPPORTED) && defined(PNG_READ_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_warning(png_ptr, "PNG_WRITE_PACK_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_SHIFT_SUPPORTED) && defined(PNG_READ_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_warning(png_ptr, "PNG_WRITE_SHIFT_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_BGR_SUPPORTED) && defined(PNG_READ_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_warning(png_ptr, "PNG_WRITE_BGR_SUPPORTED is not defined."); +#endif +#if !defined(PNG_WRITE_SWAP_SUPPORTED) && defined(PNG_READ_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_warning(png_ptr, "PNG_WRITE_SWAP_SUPPORTED is not defined."); +#endif + + png_write_start_row(png_ptr); + } + +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) + /* if interlaced and not interested in row, return */ + if (png_ptr->interlaced && (png_ptr->transformations & PNG_INTERLACE)) + { + switch (png_ptr->pass) + { + case 0: + if (png_ptr->row_number & 0x07) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 1: + if ((png_ptr->row_number & 0x07) || png_ptr->width < 5) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 2: + if ((png_ptr->row_number & 0x07) != 4) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 3: + if ((png_ptr->row_number & 0x03) || png_ptr->width < 3) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 4: + if ((png_ptr->row_number & 0x03) != 2) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 5: + if ((png_ptr->row_number & 0x01) || png_ptr->width < 2) + { + png_write_finish_row(png_ptr); + return; + } + break; + case 6: + if (!(png_ptr->row_number & 0x01)) + { + png_write_finish_row(png_ptr); + return; + } + break; + } + } +#endif + + /* set up row info for transformations */ + png_ptr->row_info.color_type = png_ptr->color_type; + png_ptr->row_info.width = png_ptr->usr_width; + png_ptr->row_info.channels = png_ptr->usr_channels; + png_ptr->row_info.bit_depth = png_ptr->usr_bit_depth; + png_ptr->row_info.pixel_depth = (png_byte)(png_ptr->row_info.bit_depth * + png_ptr->row_info.channels); + + png_ptr->row_info.rowbytes = PNG_ROWBYTES(png_ptr->row_info.pixel_depth, + png_ptr->row_info.width); + + png_debug1(3, "row_info->color_type = %d\n", png_ptr->row_info.color_type); + png_debug1(3, "row_info->width = %lu\n", png_ptr->row_info.width); + png_debug1(3, "row_info->channels = %d\n", png_ptr->row_info.channels); + png_debug1(3, "row_info->bit_depth = %d\n", png_ptr->row_info.bit_depth); + png_debug1(3, "row_info->pixel_depth = %d\n", png_ptr->row_info.pixel_depth); + png_debug1(3, "row_info->rowbytes = %lu\n", png_ptr->row_info.rowbytes); + + /* Copy user's row into buffer, leaving room for filter byte. */ + png_memcpy_check(png_ptr, png_ptr->row_buf + 1, row, + png_ptr->row_info.rowbytes); + +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) + /* handle interlacing */ + if (png_ptr->interlaced && png_ptr->pass < 6 && + (png_ptr->transformations & PNG_INTERLACE)) + { + png_do_write_interlace(&(png_ptr->row_info), + png_ptr->row_buf + 1, png_ptr->pass); + /* this should always get caught above, but still ... */ + if (!(png_ptr->row_info.width)) + { + png_write_finish_row(png_ptr); + return; + } + } +#endif + + /* handle other transformations */ + if (png_ptr->transformations) + png_do_write_transformations(png_ptr); + +#if defined(PNG_MNG_FEATURES_SUPPORTED) + /* Write filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not write a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (png_ptr->filter_type == PNG_INTRAPIXEL_DIFFERENCING)) + { + /* Intrapixel differencing */ + png_do_write_intrapixel(&(png_ptr->row_info), png_ptr->row_buf + 1); + } +#endif + + /* Find a filter if necessary, filter the row and write it out. */ + png_write_find_filter(png_ptr, &(png_ptr->row_info)); + + if (png_ptr->write_row_fn != NULL) + (*(png_ptr->write_row_fn))(png_ptr, png_ptr->row_number, png_ptr->pass); +} + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +/* Set the automatic flush interval or 0 to turn flushing off */ +void PNGAPI +png_set_flush(png_structp png_ptr, int nrows) +{ + png_debug(1, "in png_set_flush\n"); + if (png_ptr == NULL) + return; + png_ptr->flush_dist = (nrows < 0 ? 0 : nrows); +} + +/* flush the current output buffers now */ +void PNGAPI +png_write_flush(png_structp png_ptr) +{ + int wrote_IDAT; + + png_debug(1, "in png_write_flush\n"); + if (png_ptr == NULL) + return; + /* We have already written out all of the data */ + if (png_ptr->row_number >= png_ptr->num_rows) + return; + + do + { + int ret; + + /* compress the data */ + ret = deflate(&png_ptr->zstream, Z_SYNC_FLUSH); + wrote_IDAT = 0; + + /* check for compression errors */ + if (ret != Z_OK) + { + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + + if (!(png_ptr->zstream.avail_out)) + { + /* write the IDAT and reset the zlib output buffer */ + png_write_IDAT(png_ptr, png_ptr->zbuf, + png_ptr->zbuf_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + wrote_IDAT = 1; + } + } while(wrote_IDAT == 1); + + /* If there is any data left to be output, write it into a new IDAT */ + if (png_ptr->zbuf_size != png_ptr->zstream.avail_out) + { + /* write the IDAT and reset the zlib output buffer */ + png_write_IDAT(png_ptr, png_ptr->zbuf, + png_ptr->zbuf_size - png_ptr->zstream.avail_out); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + png_ptr->flush_rows = 0; + png_flush(png_ptr); +} +#endif /* PNG_WRITE_FLUSH_SUPPORTED */ + +/* free all memory used by the write */ +void PNGAPI +png_destroy_write_struct(png_structpp png_ptr_ptr, png_infopp info_ptr_ptr) +{ + png_structp png_ptr = NULL; + png_infop info_ptr = NULL; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn = NULL; + png_voidp mem_ptr = NULL; +#endif + + png_debug(1, "in png_destroy_write_struct\n"); + if (png_ptr_ptr != NULL) + { + png_ptr = *png_ptr_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; + mem_ptr = png_ptr->mem_ptr; +#endif + } + + if (info_ptr_ptr != NULL) + info_ptr = *info_ptr_ptr; + + if (info_ptr != NULL) + { + png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); + +#if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED) + if (png_ptr->num_chunk_list) + { + png_free(png_ptr, png_ptr->chunk_list); + png_ptr->chunk_list=NULL; + png_ptr->num_chunk_list=0; + } +#endif + +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)info_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)info_ptr); +#endif + *info_ptr_ptr = NULL; + } + + if (png_ptr != NULL) + { + png_write_destroy(png_ptr); +#ifdef PNG_USER_MEM_SUPPORTED + png_destroy_struct_2((png_voidp)png_ptr, (png_free_ptr)free_fn, + (png_voidp)mem_ptr); +#else + png_destroy_struct((png_voidp)png_ptr); +#endif + *png_ptr_ptr = NULL; + } +} + + +/* Free any memory used in png_ptr struct (old method) */ +void /* PRIVATE */ +png_write_destroy(png_structp png_ptr) +{ +#ifdef PNG_SETJMP_SUPPORTED + jmp_buf tmp_jmp; /* save jump buffer */ +#endif + png_error_ptr error_fn; + png_error_ptr warning_fn; + png_voidp error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_free_ptr free_fn; +#endif + + png_debug(1, "in png_write_destroy\n"); + /* free any memory zlib uses */ + deflateEnd(&png_ptr->zstream); + + /* free our memory. png_free checks NULL for us. */ + png_free(png_ptr, png_ptr->zbuf); + png_free(png_ptr, png_ptr->row_buf); +#ifndef PNG_NO_WRITE_FILTERING + png_free(png_ptr, png_ptr->prev_row); + png_free(png_ptr, png_ptr->sub_row); + png_free(png_ptr, png_ptr->up_row); + png_free(png_ptr, png_ptr->avg_row); + png_free(png_ptr, png_ptr->paeth_row); +#endif + +#if defined(PNG_TIME_RFC1123_SUPPORTED) + png_free(png_ptr, png_ptr->time_buffer); +#endif + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + png_free(png_ptr, png_ptr->prev_filters); + png_free(png_ptr, png_ptr->filter_weights); + png_free(png_ptr, png_ptr->inv_filter_weights); + png_free(png_ptr, png_ptr->filter_costs); + png_free(png_ptr, png_ptr->inv_filter_costs); +#endif + +#ifdef PNG_SETJMP_SUPPORTED + /* reset structure */ + png_memcpy(tmp_jmp, png_ptr->jmpbuf, png_sizeof (jmp_buf)); +#endif + + error_fn = png_ptr->error_fn; + warning_fn = png_ptr->warning_fn; + error_ptr = png_ptr->error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + free_fn = png_ptr->free_fn; +#endif + + png_memset(png_ptr, 0, png_sizeof (png_struct)); + + png_ptr->error_fn = error_fn; + png_ptr->warning_fn = warning_fn; + png_ptr->error_ptr = error_ptr; +#ifdef PNG_USER_MEM_SUPPORTED + png_ptr->free_fn = free_fn; +#endif + +#ifdef PNG_SETJMP_SUPPORTED + png_memcpy(png_ptr->jmpbuf, tmp_jmp, png_sizeof (jmp_buf)); +#endif +} + +/* Allow the application to select one or more row filters to use. */ +void PNGAPI +png_set_filter(png_structp png_ptr, int method, int filters) +{ + png_debug(1, "in png_set_filter\n"); + if (png_ptr == NULL) + return; +#if defined(PNG_MNG_FEATURES_SUPPORTED) + if((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + (method == PNG_INTRAPIXEL_DIFFERENCING)) + method = PNG_FILTER_TYPE_BASE; +#endif + if (method == PNG_FILTER_TYPE_BASE) + { + switch (filters & (PNG_ALL_FILTERS | 0x07)) + { +#ifndef PNG_NO_WRITE_FILTER + case 5: + case 6: + case 7: png_warning(png_ptr, "Unknown row filter for method 0"); +#endif /* PNG_NO_WRITE_FILTER */ + case PNG_FILTER_VALUE_NONE: + png_ptr->do_filter=PNG_FILTER_NONE; break; +#ifndef PNG_NO_WRITE_FILTER + case PNG_FILTER_VALUE_SUB: + png_ptr->do_filter=PNG_FILTER_SUB; break; + case PNG_FILTER_VALUE_UP: + png_ptr->do_filter=PNG_FILTER_UP; break; + case PNG_FILTER_VALUE_AVG: + png_ptr->do_filter=PNG_FILTER_AVG; break; + case PNG_FILTER_VALUE_PAETH: + png_ptr->do_filter=PNG_FILTER_PAETH; break; + default: png_ptr->do_filter = (png_byte)filters; break; +#else + default: png_warning(png_ptr, "Unknown row filter for method 0"); +#endif /* PNG_NO_WRITE_FILTER */ + } + + /* If we have allocated the row_buf, this means we have already started + * with the image and we should have allocated all of the filter buffers + * that have been selected. If prev_row isn't already allocated, then + * it is too late to start using the filters that need it, since we + * will be missing the data in the previous row. If an application + * wants to start and stop using particular filters during compression, + * it should start out with all of the filters, and then add and + * remove them after the start of compression. + */ + if (png_ptr->row_buf != NULL) + { +#ifndef PNG_NO_WRITE_FILTER + if ((png_ptr->do_filter & PNG_FILTER_SUB) && png_ptr->sub_row == NULL) + { + png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; + } + + if ((png_ptr->do_filter & PNG_FILTER_UP) && png_ptr->up_row == NULL) + { + if (png_ptr->prev_row == NULL) + { + png_warning(png_ptr, "Can't add Up filter after starting"); + png_ptr->do_filter &= ~PNG_FILTER_UP; + } + else + { + png_ptr->up_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; + } + } + + if ((png_ptr->do_filter & PNG_FILTER_AVG) && png_ptr->avg_row == NULL) + { + if (png_ptr->prev_row == NULL) + { + png_warning(png_ptr, "Can't add Average filter after starting"); + png_ptr->do_filter &= ~PNG_FILTER_AVG; + } + else + { + png_ptr->avg_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; + } + } + + if ((png_ptr->do_filter & PNG_FILTER_PAETH) && + png_ptr->paeth_row == NULL) + { + if (png_ptr->prev_row == NULL) + { + png_warning(png_ptr, "Can't add Paeth filter after starting"); + png_ptr->do_filter &= (png_byte)(~PNG_FILTER_PAETH); + } + else + { + png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; + } + } + + if (png_ptr->do_filter == PNG_NO_FILTERS) +#endif /* PNG_NO_WRITE_FILTER */ + png_ptr->do_filter = PNG_FILTER_NONE; + } + } + else + png_error(png_ptr, "Unknown custom filter method"); +} + +/* This allows us to influence the way in which libpng chooses the "best" + * filter for the current scanline. While the "minimum-sum-of-absolute- + * differences metric is relatively fast and effective, there is some + * question as to whether it can be improved upon by trying to keep the + * filtered data going to zlib more consistent, hopefully resulting in + * better compression. + */ +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) /* GRR 970116 */ +void PNGAPI +png_set_filter_heuristics(png_structp png_ptr, int heuristic_method, + int num_weights, png_doublep filter_weights, + png_doublep filter_costs) +{ + int i; + + png_debug(1, "in png_set_filter_heuristics\n"); + if (png_ptr == NULL) + return; + if (heuristic_method >= PNG_FILTER_HEURISTIC_LAST) + { + png_warning(png_ptr, "Unknown filter heuristic method"); + return; + } + + if (heuristic_method == PNG_FILTER_HEURISTIC_DEFAULT) + { + heuristic_method = PNG_FILTER_HEURISTIC_UNWEIGHTED; + } + + if (num_weights < 0 || filter_weights == NULL || + heuristic_method == PNG_FILTER_HEURISTIC_UNWEIGHTED) + { + num_weights = 0; + } + + png_ptr->num_prev_filters = (png_byte)num_weights; + png_ptr->heuristic_method = (png_byte)heuristic_method; + + if (num_weights > 0) + { + if (png_ptr->prev_filters == NULL) + { + png_ptr->prev_filters = (png_bytep)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_byte) * num_weights)); + + /* To make sure that the weighting starts out fairly */ + for (i = 0; i < num_weights; i++) + { + png_ptr->prev_filters[i] = 255; + } + } + + if (png_ptr->filter_weights == NULL) + { + png_ptr->filter_weights = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * num_weights)); + + png_ptr->inv_filter_weights = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * num_weights)); + for (i = 0; i < num_weights; i++) + { + png_ptr->inv_filter_weights[i] = + png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; + } + } + + for (i = 0; i < num_weights; i++) + { + if (filter_weights[i] < 0.0) + { + png_ptr->inv_filter_weights[i] = + png_ptr->filter_weights[i] = PNG_WEIGHT_FACTOR; + } + else + { + png_ptr->inv_filter_weights[i] = + (png_uint_16)((double)PNG_WEIGHT_FACTOR*filter_weights[i]+0.5); + png_ptr->filter_weights[i] = + (png_uint_16)((double)PNG_WEIGHT_FACTOR/filter_weights[i]+0.5); + } + } + } + + /* If, in the future, there are other filter methods, this would + * need to be based on png_ptr->filter. + */ + if (png_ptr->filter_costs == NULL) + { + png_ptr->filter_costs = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST)); + + png_ptr->inv_filter_costs = (png_uint_16p)png_malloc(png_ptr, + (png_uint_32)(png_sizeof(png_uint_16) * PNG_FILTER_VALUE_LAST)); + + for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) + { + png_ptr->inv_filter_costs[i] = + png_ptr->filter_costs[i] = PNG_COST_FACTOR; + } + } + + /* Here is where we set the relative costs of the different filters. We + * should take the desired compression level into account when setting + * the costs, so that Paeth, for instance, has a high relative cost at low + * compression levels, while it has a lower relative cost at higher + * compression settings. The filter types are in order of increasing + * relative cost, so it would be possible to do this with an algorithm. + */ + for (i = 0; i < PNG_FILTER_VALUE_LAST; i++) + { + if (filter_costs == NULL || filter_costs[i] < 0.0) + { + png_ptr->inv_filter_costs[i] = + png_ptr->filter_costs[i] = PNG_COST_FACTOR; + } + else if (filter_costs[i] >= 1.0) + { + png_ptr->inv_filter_costs[i] = + (png_uint_16)((double)PNG_COST_FACTOR / filter_costs[i] + 0.5); + png_ptr->filter_costs[i] = + (png_uint_16)((double)PNG_COST_FACTOR * filter_costs[i] + 0.5); + } + } +} +#endif /* PNG_WRITE_WEIGHTED_FILTER_SUPPORTED */ + +void PNGAPI +png_set_compression_level(png_structp png_ptr, int level) +{ + png_debug(1, "in png_set_compression_level\n"); + if (png_ptr == NULL) + return; + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_LEVEL; + png_ptr->zlib_level = level; +} + +void PNGAPI +png_set_compression_mem_level(png_structp png_ptr, int mem_level) +{ + png_debug(1, "in png_set_compression_mem_level\n"); + if (png_ptr == NULL) + return; + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL; + png_ptr->zlib_mem_level = mem_level; +} + +void PNGAPI +png_set_compression_strategy(png_structp png_ptr, int strategy) +{ + png_debug(1, "in png_set_compression_strategy\n"); + if (png_ptr == NULL) + return; + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY; + png_ptr->zlib_strategy = strategy; +} + +void PNGAPI +png_set_compression_window_bits(png_structp png_ptr, int window_bits) +{ + if (png_ptr == NULL) + return; + if (window_bits > 15) + png_warning(png_ptr, "Only compression windows <= 32k supported by PNG"); + else if (window_bits < 8) + png_warning(png_ptr, "Only compression windows >= 256 supported by PNG"); +#ifndef WBITS_8_OK + /* avoid libpng bug with 256-byte windows */ + if (window_bits == 8) + { + png_warning(png_ptr, "Compression window is being reset to 512"); + window_bits=9; + } +#endif + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS; + png_ptr->zlib_window_bits = window_bits; +} + +void PNGAPI +png_set_compression_method(png_structp png_ptr, int method) +{ + png_debug(1, "in png_set_compression_method\n"); + if (png_ptr == NULL) + return; + if (method != 8) + png_warning(png_ptr, "Only compression method 8 is supported by PNG"); + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_METHOD; + png_ptr->zlib_method = method; +} + +void PNGAPI +png_set_write_status_fn(png_structp png_ptr, png_write_status_ptr write_row_fn) +{ + if (png_ptr == NULL) + return; + png_ptr->write_row_fn = write_row_fn; +} + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +void PNGAPI +png_set_write_user_transform_fn(png_structp png_ptr, png_user_transform_ptr + write_user_transform_fn) +{ + png_debug(1, "in png_set_write_user_transform_fn\n"); + if (png_ptr == NULL) + return; + png_ptr->transformations |= PNG_USER_TRANSFORM; + png_ptr->write_user_transform_fn = write_user_transform_fn; +} +#endif + + +#if defined(PNG_INFO_IMAGE_SUPPORTED) +void PNGAPI +png_write_png(png_structp png_ptr, png_infop info_ptr, + int transforms, voidp params) +{ + if (png_ptr == NULL || info_ptr == NULL) + return; +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) + /* invert the alpha channel from opacity to transparency */ + if (transforms & PNG_TRANSFORM_INVERT_ALPHA) + png_set_invert_alpha(png_ptr); +#endif + + /* Write the file header information. */ + png_write_info(png_ptr, info_ptr); + + /* ------ these transformations don't touch the info structure ------- */ + +#if defined(PNG_WRITE_INVERT_SUPPORTED) + /* invert monochrome pixels */ + if (transforms & PNG_TRANSFORM_INVERT_MONO) + png_set_invert_mono(png_ptr); +#endif + +#if defined(PNG_WRITE_SHIFT_SUPPORTED) + /* Shift the pixels up to a legal bit depth and fill in + * as appropriate to correctly scale the image. + */ + if ((transforms & PNG_TRANSFORM_SHIFT) + && (info_ptr->valid & PNG_INFO_sBIT)) + png_set_shift(png_ptr, &info_ptr->sig_bit); +#endif + +#if defined(PNG_WRITE_PACK_SUPPORTED) + /* pack pixels into bytes */ + if (transforms & PNG_TRANSFORM_PACKING) + png_set_packing(png_ptr); +#endif + +#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) + /* swap location of alpha bytes from ARGB to RGBA */ + if (transforms & PNG_TRANSFORM_SWAP_ALPHA) + png_set_swap_alpha(png_ptr); +#endif + +#if defined(PNG_WRITE_FILLER_SUPPORTED) + /* Get rid of filler (OR ALPHA) bytes, pack XRGB/RGBX/ARGB/RGBA into + * RGB (4 channels -> 3 channels). The second parameter is not used. + */ + if (transforms & PNG_TRANSFORM_STRIP_FILLER) + png_set_filler(png_ptr, 0, PNG_FILLER_BEFORE); +#endif + +#if defined(PNG_WRITE_BGR_SUPPORTED) + /* flip BGR pixels to RGB */ + if (transforms & PNG_TRANSFORM_BGR) + png_set_bgr(png_ptr); +#endif + +#if defined(PNG_WRITE_SWAP_SUPPORTED) + /* swap bytes of 16-bit files to most significant byte first */ + if (transforms & PNG_TRANSFORM_SWAP_ENDIAN) + png_set_swap(png_ptr); +#endif + +#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) + /* swap bits of 1, 2, 4 bit packed pixel formats */ + if (transforms & PNG_TRANSFORM_PACKSWAP) + png_set_packswap(png_ptr); +#endif + + /* ----------------------- end of transformations ------------------- */ + + /* write the bits */ + if (info_ptr->valid & PNG_INFO_IDAT) + png_write_image(png_ptr, info_ptr->row_pointers); + + /* It is REQUIRED to call this to finish writing the rest of the file */ + png_write_end(png_ptr, info_ptr); + + transforms = transforms; /* quiet compiler warnings */ + params = params; +} +#endif +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/Engine/lib/lpng/pngwtran.c b/Engine/lib/lpng/pngwtran.c new file mode 100644 index 000000000..0372fe656 --- /dev/null +++ b/Engine/lib/lpng/pngwtran.c @@ -0,0 +1,572 @@ + +/* pngwtran.c - transforms the data in a row for PNG writers + * + * Last changed in libpng 1.2.9 April 14, 2006 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2006 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED + +/* Transform the data according to the user's wishes. The order of + * transformations is significant. + */ +void /* PRIVATE */ +png_do_write_transformations(png_structp png_ptr) +{ + png_debug(1, "in png_do_write_transformations\n"); + + if (png_ptr == NULL) + return; + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + if (png_ptr->transformations & PNG_USER_TRANSFORM) + if(png_ptr->write_user_transform_fn != NULL) + (*(png_ptr->write_user_transform_fn)) /* user write transform function */ + (png_ptr, /* png_ptr */ + &(png_ptr->row_info), /* row_info: */ + /* png_uint_32 width; width of row */ + /* png_uint_32 rowbytes; number of bytes in row */ + /* png_byte color_type; color type of pixels */ + /* png_byte bit_depth; bit depth of samples */ + /* png_byte channels; number of channels (1-4) */ + /* png_byte pixel_depth; bits per pixel (depth*channels) */ + png_ptr->row_buf + 1); /* start of pixel data for row */ +#endif +#if defined(PNG_WRITE_FILLER_SUPPORTED) + if (png_ptr->transformations & PNG_FILLER) + png_do_strip_filler(&(png_ptr->row_info), png_ptr->row_buf + 1, + png_ptr->flags); +#endif +#if defined(PNG_WRITE_PACKSWAP_SUPPORTED) + if (png_ptr->transformations & PNG_PACKSWAP) + png_do_packswap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_PACK_SUPPORTED) + if (png_ptr->transformations & PNG_PACK) + png_do_pack(&(png_ptr->row_info), png_ptr->row_buf + 1, + (png_uint_32)png_ptr->bit_depth); +#endif +#if defined(PNG_WRITE_SWAP_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_BYTES) + png_do_swap(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_SHIFT_SUPPORTED) + if (png_ptr->transformations & PNG_SHIFT) + png_do_shift(&(png_ptr->row_info), png_ptr->row_buf + 1, + &(png_ptr->shift)); +#endif +#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) + if (png_ptr->transformations & PNG_SWAP_ALPHA) + png_do_write_swap_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_ALPHA) + png_do_write_invert_alpha(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_BGR_SUPPORTED) + if (png_ptr->transformations & PNG_BGR) + png_do_bgr(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +#if defined(PNG_WRITE_INVERT_SUPPORTED) + if (png_ptr->transformations & PNG_INVERT_MONO) + png_do_invert(&(png_ptr->row_info), png_ptr->row_buf + 1); +#endif +} + +#if defined(PNG_WRITE_PACK_SUPPORTED) +/* Pack pixels into bytes. Pass the true bit depth in bit_depth. The + * row_info bit depth should be 8 (one pixel per byte). The channels + * should be 1 (this only happens on grayscale and paletted images). + */ +void /* PRIVATE */ +png_do_pack(png_row_infop row_info, png_bytep row, png_uint_32 bit_depth) +{ + png_debug(1, "in png_do_pack\n"); + if (row_info->bit_depth == 8 && +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + row_info->channels == 1) + { + switch ((int)bit_depth) + { + case 1: + { + png_bytep sp, dp; + int mask, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + mask = 0x80; + v = 0; + + for (i = 0; i < row_width; i++) + { + if (*sp != 0) + v |= mask; + sp++; + if (mask > 1) + mask >>= 1; + else + { + mask = 0x80; + *dp = (png_byte)v; + dp++; + v = 0; + } + } + if (mask != 0x80) + *dp = (png_byte)v; + break; + } + case 2: + { + png_bytep sp, dp; + int shift, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + shift = 6; + v = 0; + for (i = 0; i < row_width; i++) + { + png_byte value; + + value = (png_byte)(*sp & 0x03); + v |= (value << shift); + if (shift == 0) + { + shift = 6; + *dp = (png_byte)v; + dp++; + v = 0; + } + else + shift -= 2; + sp++; + } + if (shift != 6) + *dp = (png_byte)v; + break; + } + case 4: + { + png_bytep sp, dp; + int shift, v; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + sp = row; + dp = row; + shift = 4; + v = 0; + for (i = 0; i < row_width; i++) + { + png_byte value; + + value = (png_byte)(*sp & 0x0f); + v |= (value << shift); + + if (shift == 0) + { + shift = 4; + *dp = (png_byte)v; + dp++; + v = 0; + } + else + shift -= 4; + + sp++; + } + if (shift != 4) + *dp = (png_byte)v; + break; + } + } + row_info->bit_depth = (png_byte)bit_depth; + row_info->pixel_depth = (png_byte)(bit_depth * row_info->channels); + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_info->width); + } +} +#endif + +#if defined(PNG_WRITE_SHIFT_SUPPORTED) +/* Shift pixel values to take advantage of whole range. Pass the + * true number of bits in bit_depth. The row should be packed + * according to row_info->bit_depth. Thus, if you had a row of + * bit depth 4, but the pixels only had values from 0 to 7, you + * would pass 3 as bit_depth, and this routine would translate the + * data to 0 to 15. + */ +void /* PRIVATE */ +png_do_shift(png_row_infop row_info, png_bytep row, png_color_8p bit_depth) +{ + png_debug(1, "in png_do_shift\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL && +#else + if ( +#endif + row_info->color_type != PNG_COLOR_TYPE_PALETTE) + { + int shift_start[4], shift_dec[4]; + int channels = 0; + + if (row_info->color_type & PNG_COLOR_MASK_COLOR) + { + shift_start[channels] = row_info->bit_depth - bit_depth->red; + shift_dec[channels] = bit_depth->red; + channels++; + shift_start[channels] = row_info->bit_depth - bit_depth->green; + shift_dec[channels] = bit_depth->green; + channels++; + shift_start[channels] = row_info->bit_depth - bit_depth->blue; + shift_dec[channels] = bit_depth->blue; + channels++; + } + else + { + shift_start[channels] = row_info->bit_depth - bit_depth->gray; + shift_dec[channels] = bit_depth->gray; + channels++; + } + if (row_info->color_type & PNG_COLOR_MASK_ALPHA) + { + shift_start[channels] = row_info->bit_depth - bit_depth->alpha; + shift_dec[channels] = bit_depth->alpha; + channels++; + } + + /* with low row depths, could only be grayscale, so one channel */ + if (row_info->bit_depth < 8) + { + png_bytep bp = row; + png_uint_32 i; + png_byte mask; + png_uint_32 row_bytes = row_info->rowbytes; + + if (bit_depth->gray == 1 && row_info->bit_depth == 2) + mask = 0x55; + else if (row_info->bit_depth == 4 && bit_depth->gray == 3) + mask = 0x11; + else + mask = 0xff; + + for (i = 0; i < row_bytes; i++, bp++) + { + png_uint_16 v; + int j; + + v = *bp; + *bp = 0; + for (j = shift_start[0]; j > -shift_dec[0]; j -= shift_dec[0]) + { + if (j > 0) + *bp |= (png_byte)((v << j) & 0xff); + else + *bp |= (png_byte)((v >> (-j)) & mask); + } + } + } + else if (row_info->bit_depth == 8) + { + png_bytep bp = row; + png_uint_32 i; + png_uint_32 istop = channels * row_info->width; + + for (i = 0; i < istop; i++, bp++) + { + + png_uint_16 v; + int j; + int c = (int)(i%channels); + + v = *bp; + *bp = 0; + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) + { + if (j > 0) + *bp |= (png_byte)((v << j) & 0xff); + else + *bp |= (png_byte)((v >> (-j)) & 0xff); + } + } + } + else + { + png_bytep bp; + png_uint_32 i; + png_uint_32 istop = channels * row_info->width; + + for (bp = row, i = 0; i < istop; i++) + { + int c = (int)(i%channels); + png_uint_16 value, v; + int j; + + v = (png_uint_16)(((png_uint_16)(*bp) << 8) + *(bp + 1)); + value = 0; + for (j = shift_start[c]; j > -shift_dec[c]; j -= shift_dec[c]) + { + if (j > 0) + value |= (png_uint_16)((v << j) & (png_uint_16)0xffff); + else + value |= (png_uint_16)((v >> (-j)) & (png_uint_16)0xffff); + } + *bp++ = (png_byte)(value >> 8); + *bp++ = (png_byte)(value & 0xff); + } + } + } +} +#endif + +#if defined(PNG_WRITE_SWAP_ALPHA_SUPPORTED) +void /* PRIVATE */ +png_do_write_swap_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_swap_alpha\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This converts from ARGB to RGBA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save; + } + } + /* This converts from AARRGGBB to RRGGBBAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save[2]; + save[0] = *(sp++); + save[1] = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save[0]; + *(dp++) = save[1]; + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This converts from AG to GA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save; + } + } + /* This converts from AAGG to GGAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + png_byte save[2]; + save[0] = *(sp++); + save[1] = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = save[0]; + *(dp++) = save[1]; + } + } + } + } +} +#endif + +#if defined(PNG_WRITE_INVERT_ALPHA_SUPPORTED) +void /* PRIVATE */ +png_do_write_invert_alpha(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_invert_alpha\n"); +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL) +#endif + { + if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + /* This inverts the alpha channel in RGBA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + for (i = 0, sp = dp = row; i < row_width; i++) + { + /* does nothing + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + */ + sp+=3; dp = sp; + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + /* This inverts the alpha channel in RRGGBBAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + /* does nothing + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + *(dp++) = *(sp++); + */ + sp+=6; dp = sp; + *(dp++) = (png_byte)(255 - *(sp++)); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + } + else if (row_info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + /* This inverts the alpha channel in GA */ + if (row_info->bit_depth == 8) + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + *(dp++) = *(sp++); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + /* This inverts the alpha channel in GGAA */ + else + { + png_bytep sp, dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + for (i = 0, sp = dp = row; i < row_width; i++) + { + /* does nothing + *(dp++) = *(sp++); + *(dp++) = *(sp++); + */ + sp+=2; dp = sp; + *(dp++) = (png_byte)(255 - *(sp++)); + *(dp++) = (png_byte)(255 - *(sp++)); + } + } + } + } +} +#endif + +#if defined(PNG_MNG_FEATURES_SUPPORTED) +/* undoes intrapixel differencing */ +void /* PRIVATE */ +png_do_write_intrapixel(png_row_infop row_info, png_bytep row) +{ + png_debug(1, "in png_do_write_intrapixel\n"); + if ( +#if defined(PNG_USELESS_TESTS_SUPPORTED) + row != NULL && row_info != NULL && +#endif + (row_info->color_type & PNG_COLOR_MASK_COLOR)) + { + int bytes_per_pixel; + png_uint_32 row_width = row_info->width; + if (row_info->bit_depth == 8) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 3; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 4; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + *(rp) = (png_byte)((*rp - *(rp+1))&0xff); + *(rp+2) = (png_byte)((*(rp+2) - *(rp+1))&0xff); + } + } + else if (row_info->bit_depth == 16) + { + png_bytep rp; + png_uint_32 i; + + if (row_info->color_type == PNG_COLOR_TYPE_RGB) + bytes_per_pixel = 6; + else if (row_info->color_type == PNG_COLOR_TYPE_RGB_ALPHA) + bytes_per_pixel = 8; + else + return; + + for (i = 0, rp = row; i < row_width; i++, rp += bytes_per_pixel) + { + png_uint_32 s0 = (*(rp ) << 8) | *(rp+1); + png_uint_32 s1 = (*(rp+2) << 8) | *(rp+3); + png_uint_32 s2 = (*(rp+4) << 8) | *(rp+5); + png_uint_32 red = (png_uint_32)((s0-s1) & 0xffffL); + png_uint_32 blue = (png_uint_32)((s2-s1) & 0xffffL); + *(rp ) = (png_byte)((red >> 8) & 0xff); + *(rp+1) = (png_byte)(red & 0xff); + *(rp+4) = (png_byte)((blue >> 8) & 0xff); + *(rp+5) = (png_byte)(blue & 0xff); + } + } + } +} +#endif /* PNG_MNG_FEATURES_SUPPORTED */ +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/Engine/lib/lpng/pngwutil.c b/Engine/lib/lpng/pngwutil.c new file mode 100644 index 000000000..fef38aef9 --- /dev/null +++ b/Engine/lib/lpng/pngwutil.c @@ -0,0 +1,2792 @@ + +/* pngwutil.c - utilities to write a PNG file + * + * Last changed in libpng 1.2.20 Septhember 3, 2007 + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2007 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + */ + +#define PNG_INTERNAL +#include "png.h" +#ifdef PNG_WRITE_SUPPORTED + +/* Place a 32-bit number into a buffer in PNG byte order. We work + * with unsigned numbers for convenience, although one supported + * ancillary chunk uses signed (two's complement) numbers. + */ +void PNGAPI +png_save_uint_32(png_bytep buf, png_uint_32 i) +{ + buf[0] = (png_byte)((i >> 24) & 0xff); + buf[1] = (png_byte)((i >> 16) & 0xff); + buf[2] = (png_byte)((i >> 8) & 0xff); + buf[3] = (png_byte)(i & 0xff); +} + +/* The png_save_int_32 function assumes integers are stored in two's + * complement format. If this isn't the case, then this routine needs to + * be modified to write data in two's complement format. + */ +void PNGAPI +png_save_int_32(png_bytep buf, png_int_32 i) +{ + buf[0] = (png_byte)((i >> 24) & 0xff); + buf[1] = (png_byte)((i >> 16) & 0xff); + buf[2] = (png_byte)((i >> 8) & 0xff); + buf[3] = (png_byte)(i & 0xff); +} + +/* Place a 16-bit number into a buffer in PNG byte order. + * The parameter is declared unsigned int, not png_uint_16, + * just to avoid potential problems on pre-ANSI C compilers. + */ +void PNGAPI +png_save_uint_16(png_bytep buf, unsigned int i) +{ + buf[0] = (png_byte)((i >> 8) & 0xff); + buf[1] = (png_byte)(i & 0xff); +} + +/* Write a PNG chunk all at once. The type is an array of ASCII characters + * representing the chunk name. The array must be at least 4 bytes in + * length, and does not need to be null terminated. To be safe, pass the + * pre-defined chunk names here, and if you need a new one, define it + * where the others are defined. The length is the length of the data. + * All the data must be present. If that is not possible, use the + * png_write_chunk_start(), png_write_chunk_data(), and png_write_chunk_end() + * functions instead. + */ +void PNGAPI +png_write_chunk(png_structp png_ptr, png_bytep chunk_name, + png_bytep data, png_size_t length) +{ + if(png_ptr == NULL) return; + png_write_chunk_start(png_ptr, chunk_name, (png_uint_32)length); + png_write_chunk_data(png_ptr, data, length); + png_write_chunk_end(png_ptr); +} + +/* Write the start of a PNG chunk. The type is the chunk type. + * The total_length is the sum of the lengths of all the data you will be + * passing in png_write_chunk_data(). + */ +void PNGAPI +png_write_chunk_start(png_structp png_ptr, png_bytep chunk_name, + png_uint_32 length) +{ + png_byte buf[4]; + png_debug2(0, "Writing %s chunk (%lu bytes)\n", chunk_name, length); + if(png_ptr == NULL) return; + + /* write the length */ + png_save_uint_32(buf, length); + png_write_data(png_ptr, buf, (png_size_t)4); + + /* write the chunk name */ + png_write_data(png_ptr, chunk_name, (png_size_t)4); + /* reset the crc and run it over the chunk name */ + png_reset_crc(png_ptr); + png_calculate_crc(png_ptr, chunk_name, (png_size_t)4); +} + +/* Write the data of a PNG chunk started with png_write_chunk_start(). + * Note that multiple calls to this function are allowed, and that the + * sum of the lengths from these calls *must* add up to the total_length + * given to png_write_chunk_start(). + */ +void PNGAPI +png_write_chunk_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + /* write the data, and run the CRC over it */ + if(png_ptr == NULL) return; + if (data != NULL && length > 0) + { + png_calculate_crc(png_ptr, data, length); + png_write_data(png_ptr, data, length); + } +} + +/* Finish a chunk started with png_write_chunk_start(). */ +void PNGAPI +png_write_chunk_end(png_structp png_ptr) +{ + png_byte buf[4]; + + if(png_ptr == NULL) return; + + /* write the crc */ + png_save_uint_32(buf, png_ptr->crc); + + png_write_data(png_ptr, buf, (png_size_t)4); +} + +/* Simple function to write the signature. If we have already written + * the magic bytes of the signature, or more likely, the PNG stream is + * being embedded into another stream and doesn't need its own signature, + * we should call png_set_sig_bytes() to tell libpng how many of the + * bytes have already been written. + */ +void /* PRIVATE */ +png_write_sig(png_structp png_ptr) +{ + png_byte png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10}; + /* write the rest of the 8 byte signature */ + png_write_data(png_ptr, &png_signature[png_ptr->sig_bytes], + (png_size_t)8 - png_ptr->sig_bytes); + if(png_ptr->sig_bytes < 3) + png_ptr->mode |= PNG_HAVE_PNG_SIGNATURE; +} + +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_iCCP_SUPPORTED) +/* + * This pair of functions encapsulates the operation of (a) compressing a + * text string, and (b) issuing it later as a series of chunk data writes. + * The compression_state structure is shared context for these functions + * set up by the caller in order to make the whole mess thread-safe. + */ + +typedef struct +{ + char *input; /* the uncompressed input data */ + int input_len; /* its length */ + int num_output_ptr; /* number of output pointers used */ + int max_output_ptr; /* size of output_ptr */ + png_charpp output_ptr; /* array of pointers to output */ +} compression_state; + +/* compress given text into storage in the png_ptr structure */ +static int /* PRIVATE */ +png_text_compress(png_structp png_ptr, + png_charp text, png_size_t text_len, int compression, + compression_state *comp) +{ + int ret; + + comp->num_output_ptr = 0; + comp->max_output_ptr = 0; + comp->output_ptr = NULL; + comp->input = NULL; + comp->input_len = 0; + + /* we may just want to pass the text right through */ + if (compression == PNG_TEXT_COMPRESSION_NONE) + { + comp->input = text; + comp->input_len = text_len; + return((int)text_len); + } + + if (compression >= PNG_TEXT_COMPRESSION_LAST) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char msg[50]; + png_snprintf(msg, 50, "Unknown compression type %d", compression); + png_warning(png_ptr, msg); +#else + png_warning(png_ptr, "Unknown compression type"); +#endif + } + + /* We can't write the chunk until we find out how much data we have, + * which means we need to run the compressor first and save the + * output. This shouldn't be a problem, as the vast majority of + * comments should be reasonable, but we will set up an array of + * malloc'd pointers to be sure. + * + * If we knew the application was well behaved, we could simplify this + * greatly by assuming we can always malloc an output buffer large + * enough to hold the compressed text ((1001 * text_len / 1000) + 12) + * and malloc this directly. The only time this would be a bad idea is + * if we can't malloc more than 64K and we have 64K of random input + * data, or if the input string is incredibly large (although this + * wouldn't cause a failure, just a slowdown due to swapping). + */ + + /* set up the compression buffers */ + png_ptr->zstream.avail_in = (uInt)text_len; + png_ptr->zstream.next_in = (Bytef *)text; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = (Bytef *)png_ptr->zbuf; + + /* this is the same compression loop as in png_write_row() */ + do + { + /* compress the data */ + ret = deflate(&png_ptr->zstream, Z_NO_FLUSH); + if (ret != Z_OK) + { + /* error */ + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + /* check to see if we need more room */ + if (!(png_ptr->zstream.avail_out)) + { + /* make sure the output array has room */ + if (comp->num_output_ptr >= comp->max_output_ptr) + { + int old_max; + + old_max = comp->max_output_ptr; + comp->max_output_ptr = comp->num_output_ptr + 4; + if (comp->output_ptr != NULL) + { + png_charpp old_ptr; + + old_ptr = comp->output_ptr; + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32)(comp->max_output_ptr * + png_sizeof (png_charpp))); + png_memcpy(comp->output_ptr, old_ptr, old_max + * png_sizeof (png_charp)); + png_free(png_ptr, old_ptr); + } + else + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32)(comp->max_output_ptr * + png_sizeof (png_charp))); + } + + /* save the data */ + comp->output_ptr[comp->num_output_ptr] = (png_charp)png_malloc(png_ptr, + (png_uint_32)png_ptr->zbuf_size); + png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf, + png_ptr->zbuf_size); + comp->num_output_ptr++; + + /* and reset the buffer */ + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = png_ptr->zbuf; + } + /* continue until we don't have any more to compress */ + } while (png_ptr->zstream.avail_in); + + /* finish the compression */ + do + { + /* tell zlib we are finished */ + ret = deflate(&png_ptr->zstream, Z_FINISH); + + if (ret == Z_OK) + { + /* check to see if we need more room */ + if (!(png_ptr->zstream.avail_out)) + { + /* check to make sure our output array has room */ + if (comp->num_output_ptr >= comp->max_output_ptr) + { + int old_max; + + old_max = comp->max_output_ptr; + comp->max_output_ptr = comp->num_output_ptr + 4; + if (comp->output_ptr != NULL) + { + png_charpp old_ptr; + + old_ptr = comp->output_ptr; + /* This could be optimized to realloc() */ + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32)(comp->max_output_ptr * + png_sizeof (png_charpp))); + png_memcpy(comp->output_ptr, old_ptr, + old_max * png_sizeof (png_charp)); + png_free(png_ptr, old_ptr); + } + else + comp->output_ptr = (png_charpp)png_malloc(png_ptr, + (png_uint_32)(comp->max_output_ptr * + png_sizeof (png_charp))); + } + + /* save off the data */ + comp->output_ptr[comp->num_output_ptr] = + (png_charp)png_malloc(png_ptr, (png_uint_32)png_ptr->zbuf_size); + png_memcpy(comp->output_ptr[comp->num_output_ptr], png_ptr->zbuf, + png_ptr->zbuf_size); + comp->num_output_ptr++; + + /* and reset the buffer pointers */ + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = png_ptr->zbuf; + } + } + else if (ret != Z_STREAM_END) + { + /* we got an error */ + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + } while (ret != Z_STREAM_END); + + /* text length is number of buffers plus last buffer */ + text_len = png_ptr->zbuf_size * comp->num_output_ptr; + if (png_ptr->zstream.avail_out < png_ptr->zbuf_size) + text_len += png_ptr->zbuf_size - (png_size_t)png_ptr->zstream.avail_out; + + return((int)text_len); +} + +/* ship the compressed text out via chunk writes */ +static void /* PRIVATE */ +png_write_compressed_data_out(png_structp png_ptr, compression_state *comp) +{ + int i; + + /* handle the no-compression case */ + if (comp->input) + { + png_write_chunk_data(png_ptr, (png_bytep)comp->input, + (png_size_t)comp->input_len); + return; + } + + /* write saved output buffers, if any */ + for (i = 0; i < comp->num_output_ptr; i++) + { + png_write_chunk_data(png_ptr,(png_bytep)comp->output_ptr[i], + png_ptr->zbuf_size); + png_free(png_ptr, comp->output_ptr[i]); + comp->output_ptr[i]=NULL; + } + if (comp->max_output_ptr != 0) + png_free(png_ptr, comp->output_ptr); + comp->output_ptr=NULL; + /* write anything left in zbuf */ + if (png_ptr->zstream.avail_out < (png_uint_32)png_ptr->zbuf_size) + png_write_chunk_data(png_ptr, png_ptr->zbuf, + png_ptr->zbuf_size - png_ptr->zstream.avail_out); + + /* reset zlib for another zTXt/iTXt or image data */ + deflateReset(&png_ptr->zstream); + png_ptr->zstream.data_type = Z_BINARY; +} +#endif + +/* Write the IHDR chunk, and update the png_struct with the necessary + * information. Note that the rest of this code depends upon this + * information being correct. + */ +void /* PRIVATE */ +png_write_IHDR(png_structp png_ptr, png_uint_32 width, png_uint_32 height, + int bit_depth, int color_type, int compression_type, int filter_type, + int interlace_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IHDR; +#endif + png_byte buf[13]; /* buffer to store the IHDR info */ + + png_debug(1, "in png_write_IHDR\n"); + /* Check that we have valid input data from the application info */ + switch (color_type) + { + case PNG_COLOR_TYPE_GRAY: + switch (bit_depth) + { + case 1: + case 2: + case 4: + case 8: + case 16: png_ptr->channels = 1; break; + default: png_error(png_ptr,"Invalid bit depth for grayscale image"); + } + break; + case PNG_COLOR_TYPE_RGB: + if (bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth for RGB image"); + png_ptr->channels = 3; + break; + case PNG_COLOR_TYPE_PALETTE: + switch (bit_depth) + { + case 1: + case 2: + case 4: + case 8: png_ptr->channels = 1; break; + default: png_error(png_ptr, "Invalid bit depth for paletted image"); + } + break; + case PNG_COLOR_TYPE_GRAY_ALPHA: + if (bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth for grayscale+alpha image"); + png_ptr->channels = 2; + break; + case PNG_COLOR_TYPE_RGB_ALPHA: + if (bit_depth != 8 && bit_depth != 16) + png_error(png_ptr, "Invalid bit depth for RGBA image"); + png_ptr->channels = 4; + break; + default: + png_error(png_ptr, "Invalid image color type specified"); + } + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + { + png_warning(png_ptr, "Invalid compression type specified"); + compression_type = PNG_COMPRESSION_TYPE_BASE; + } + + /* Write filter_method 64 (intrapixel differencing) only if + * 1. Libpng was compiled with PNG_MNG_FEATURES_SUPPORTED and + * 2. Libpng did not write a PNG signature (this filter_method is only + * used in PNG datastreams that are embedded in MNG datastreams) and + * 3. The application called png_permit_mng_features with a mask that + * included PNG_FLAG_MNG_FILTER_64 and + * 4. The filter_method is 64 and + * 5. The color_type is RGB or RGBA + */ + if ( +#if defined(PNG_MNG_FEATURES_SUPPORTED) + !((png_ptr->mng_features_permitted & PNG_FLAG_MNG_FILTER_64) && + ((png_ptr->mode&PNG_HAVE_PNG_SIGNATURE) == 0) && + (color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_RGB_ALPHA) && + (filter_type == PNG_INTRAPIXEL_DIFFERENCING)) && +#endif + filter_type != PNG_FILTER_TYPE_BASE) + { + png_warning(png_ptr, "Invalid filter type specified"); + filter_type = PNG_FILTER_TYPE_BASE; + } + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + if (interlace_type != PNG_INTERLACE_NONE && + interlace_type != PNG_INTERLACE_ADAM7) + { + png_warning(png_ptr, "Invalid interlace type specified"); + interlace_type = PNG_INTERLACE_ADAM7; + } +#else + interlace_type=PNG_INTERLACE_NONE; +#endif + + /* save off the relevent information */ + png_ptr->bit_depth = (png_byte)bit_depth; + png_ptr->color_type = (png_byte)color_type; + png_ptr->interlaced = (png_byte)interlace_type; +#if defined(PNG_MNG_FEATURES_SUPPORTED) + png_ptr->filter_type = (png_byte)filter_type; +#endif + png_ptr->compression_type = (png_byte)compression_type; + png_ptr->width = width; + png_ptr->height = height; + + png_ptr->pixel_depth = (png_byte)(bit_depth * png_ptr->channels); + png_ptr->rowbytes = PNG_ROWBYTES(png_ptr->pixel_depth, width); + /* set the usr info, so any transformations can modify it */ + png_ptr->usr_width = png_ptr->width; + png_ptr->usr_bit_depth = png_ptr->bit_depth; + png_ptr->usr_channels = png_ptr->channels; + + /* pack the header information into the buffer */ + png_save_uint_32(buf, width); + png_save_uint_32(buf + 4, height); + buf[8] = (png_byte)bit_depth; + buf[9] = (png_byte)color_type; + buf[10] = (png_byte)compression_type; + buf[11] = (png_byte)filter_type; + buf[12] = (png_byte)interlace_type; + + /* write the chunk */ + png_write_chunk(png_ptr, png_IHDR, buf, (png_size_t)13); + + /* initialize zlib with PNG info */ + png_ptr->zstream.zalloc = png_zalloc; + png_ptr->zstream.zfree = png_zfree; + png_ptr->zstream.opaque = (voidpf)png_ptr; + if (!(png_ptr->do_filter)) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE || + png_ptr->bit_depth < 8) + png_ptr->do_filter = PNG_FILTER_NONE; + else + png_ptr->do_filter = PNG_ALL_FILTERS; + } + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_STRATEGY)) + { + if (png_ptr->do_filter != PNG_FILTER_NONE) + png_ptr->zlib_strategy = Z_FILTERED; + else + png_ptr->zlib_strategy = Z_DEFAULT_STRATEGY; + } + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_LEVEL)) + png_ptr->zlib_level = Z_DEFAULT_COMPRESSION; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_MEM_LEVEL)) + png_ptr->zlib_mem_level = 8; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_WINDOW_BITS)) + png_ptr->zlib_window_bits = 15; + if (!(png_ptr->flags & PNG_FLAG_ZLIB_CUSTOM_METHOD)) + png_ptr->zlib_method = 8; + if (deflateInit2(&png_ptr->zstream, png_ptr->zlib_level, + png_ptr->zlib_method, png_ptr->zlib_window_bits, + png_ptr->zlib_mem_level, png_ptr->zlib_strategy) != Z_OK) + png_error(png_ptr, "zlib failed to initialize compressor"); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + /* libpng is not interested in zstream.data_type */ + /* set it to a predefined value, to avoid its evaluation inside zlib */ + png_ptr->zstream.data_type = Z_BINARY; + + png_ptr->mode = PNG_HAVE_IHDR; +} + +/* write the palette. We are careful not to trust png_color to be in the + * correct order for PNG, so people can redefine it to any convenient + * structure. + */ +void /* PRIVATE */ +png_write_PLTE(png_structp png_ptr, png_colorp palette, png_uint_32 num_pal) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_PLTE; +#endif + png_uint_32 i; + png_colorp pal_ptr; + png_byte buf[3]; + + png_debug(1, "in png_write_PLTE\n"); + if (( +#if defined(PNG_MNG_FEATURES_SUPPORTED) + !(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE) && +#endif + num_pal == 0) || num_pal > 256) + { + if (png_ptr->color_type == PNG_COLOR_TYPE_PALETTE) + { + png_error(png_ptr, "Invalid number of colors in palette"); + } + else + { + png_warning(png_ptr, "Invalid number of colors in palette"); + return; + } + } + + if (!(png_ptr->color_type&PNG_COLOR_MASK_COLOR)) + { + png_warning(png_ptr, + "Ignoring request to write a PLTE chunk in grayscale PNG"); + return; + } + + png_ptr->num_palette = (png_uint_16)num_pal; + png_debug1(3, "num_palette = %d\n", png_ptr->num_palette); + + png_write_chunk_start(png_ptr, png_PLTE, num_pal * 3); +#ifndef PNG_NO_POINTER_INDEXING + for (i = 0, pal_ptr = palette; i < num_pal; i++, pal_ptr++) + { + buf[0] = pal_ptr->red; + buf[1] = pal_ptr->green; + buf[2] = pal_ptr->blue; + png_write_chunk_data(png_ptr, buf, (png_size_t)3); + } +#else + /* This is a little slower but some buggy compilers need to do this instead */ + pal_ptr=palette; + for (i = 0; i < num_pal; i++) + { + buf[0] = pal_ptr[i].red; + buf[1] = pal_ptr[i].green; + buf[2] = pal_ptr[i].blue; + png_write_chunk_data(png_ptr, buf, (png_size_t)3); + } +#endif + png_write_chunk_end(png_ptr); + png_ptr->mode |= PNG_HAVE_PLTE; +} + +/* write an IDAT chunk */ +void /* PRIVATE */ +png_write_IDAT(png_structp png_ptr, png_bytep data, png_size_t length) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IDAT; +#endif + png_debug(1, "in png_write_IDAT\n"); + + /* Optimize the CMF field in the zlib stream. */ + /* This hack of the zlib stream is compliant to the stream specification. */ + if (!(png_ptr->mode & PNG_HAVE_IDAT) && + png_ptr->compression_type == PNG_COMPRESSION_TYPE_BASE) + { + unsigned int z_cmf = data[0]; /* zlib compression method and flags */ + if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70) + { + /* Avoid memory underflows and multiplication overflows. */ + /* The conditions below are practically always satisfied; + however, they still must be checked. */ + if (length >= 2 && + png_ptr->height < 16384 && png_ptr->width < 16384) + { + png_uint_32 uncompressed_idat_size = png_ptr->height * + ((png_ptr->width * + png_ptr->channels * png_ptr->bit_depth + 15) >> 3); + unsigned int z_cinfo = z_cmf >> 4; + unsigned int half_z_window_size = 1 << (z_cinfo + 7); + while (uncompressed_idat_size <= half_z_window_size && + half_z_window_size >= 256) + { + z_cinfo--; + half_z_window_size >>= 1; + } + z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4); + if (data[0] != (png_byte)z_cmf) + { + data[0] = (png_byte)z_cmf; + data[1] &= 0xe0; + data[1] += (png_byte)(0x1f - ((z_cmf << 8) + data[1]) % 0x1f); + } + } + } + else + png_error(png_ptr, + "Invalid zlib compression method or flags in IDAT"); + } + + png_write_chunk(png_ptr, png_IDAT, data, length); + png_ptr->mode |= PNG_HAVE_IDAT; +} + +/* write an IEND chunk */ +void /* PRIVATE */ +png_write_IEND(png_structp png_ptr) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_IEND; +#endif + png_debug(1, "in png_write_IEND\n"); + png_write_chunk(png_ptr, png_IEND, png_bytep_NULL, + (png_size_t)0); + png_ptr->mode |= PNG_HAVE_IEND; +} + +#if defined(PNG_WRITE_gAMA_SUPPORTED) +/* write a gAMA chunk */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +void /* PRIVATE */ +png_write_gAMA(png_structp png_ptr, double file_gamma) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_gAMA; +#endif + png_uint_32 igamma; + png_byte buf[4]; + + png_debug(1, "in png_write_gAMA\n"); + /* file_gamma is saved in 1/100,000ths */ + igamma = (png_uint_32)(file_gamma * 100000.0 + 0.5); + png_save_uint_32(buf, igamma); + png_write_chunk(png_ptr, png_gAMA, buf, (png_size_t)4); +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +void /* PRIVATE */ +png_write_gAMA_fixed(png_structp png_ptr, png_fixed_point file_gamma) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_gAMA; +#endif + png_byte buf[4]; + + png_debug(1, "in png_write_gAMA\n"); + /* file_gamma is saved in 1/100,000ths */ + png_save_uint_32(buf, (png_uint_32)file_gamma); + png_write_chunk(png_ptr, png_gAMA, buf, (png_size_t)4); +} +#endif +#endif + +#if defined(PNG_WRITE_sRGB_SUPPORTED) +/* write a sRGB chunk */ +void /* PRIVATE */ +png_write_sRGB(png_structp png_ptr, int srgb_intent) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sRGB; +#endif + png_byte buf[1]; + + png_debug(1, "in png_write_sRGB\n"); + if(srgb_intent >= PNG_sRGB_INTENT_LAST) + png_warning(png_ptr, + "Invalid sRGB rendering intent specified"); + buf[0]=(png_byte)srgb_intent; + png_write_chunk(png_ptr, png_sRGB, buf, (png_size_t)1); +} +#endif + +#if defined(PNG_WRITE_iCCP_SUPPORTED) +/* write an iCCP chunk */ +void /* PRIVATE */ +png_write_iCCP(png_structp png_ptr, png_charp name, int compression_type, + png_charp profile, int profile_len) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_iCCP; +#endif + png_size_t name_len; + png_charp new_name; + compression_state comp; + int embedded_profile_len = 0; + + png_debug(1, "in png_write_iCCP\n"); + + comp.num_output_ptr = 0; + comp.max_output_ptr = 0; + comp.output_ptr = NULL; + comp.input = NULL; + comp.input_len = 0; + + if (name == NULL || (name_len = png_check_keyword(png_ptr, name, + &new_name)) == 0) + { + png_warning(png_ptr, "Empty keyword in iCCP chunk"); + return; + } + + if (compression_type != PNG_COMPRESSION_TYPE_BASE) + png_warning(png_ptr, "Unknown compression type in iCCP chunk"); + + if (profile == NULL) + profile_len = 0; + + if (profile_len > 3) + embedded_profile_len = + ((*( (png_bytep)profile ))<<24) | + ((*( (png_bytep)profile+1))<<16) | + ((*( (png_bytep)profile+2))<< 8) | + ((*( (png_bytep)profile+3)) ); + + if (profile_len < embedded_profile_len) + { + png_warning(png_ptr, + "Embedded profile length too large in iCCP chunk"); + return; + } + + if (profile_len > embedded_profile_len) + { + png_warning(png_ptr, + "Truncating profile to actual length in iCCP chunk"); + profile_len = embedded_profile_len; + } + + if (profile_len) + profile_len = png_text_compress(png_ptr, profile, (png_size_t)profile_len, + PNG_COMPRESSION_TYPE_BASE, &comp); + + /* make sure we include the NULL after the name and the compression type */ + png_write_chunk_start(png_ptr, png_iCCP, + (png_uint_32)name_len+profile_len+2); + new_name[name_len+1]=0x00; + png_write_chunk_data(png_ptr, (png_bytep)new_name, name_len + 2); + + if (profile_len) + png_write_compressed_data_out(png_ptr, &comp); + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_name); +} +#endif + +#if defined(PNG_WRITE_sPLT_SUPPORTED) +/* write a sPLT chunk */ +void /* PRIVATE */ +png_write_sPLT(png_structp png_ptr, png_sPLT_tp spalette) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sPLT; +#endif + png_size_t name_len; + png_charp new_name; + png_byte entrybuf[10]; + int entry_size = (spalette->depth == 8 ? 6 : 10); + int palette_size = entry_size * spalette->nentries; + png_sPLT_entryp ep; +#ifdef PNG_NO_POINTER_INDEXING + int i; +#endif + + png_debug(1, "in png_write_sPLT\n"); + if (spalette->name == NULL || (name_len = png_check_keyword(png_ptr, + spalette->name, &new_name))==0) + { + png_warning(png_ptr, "Empty keyword in sPLT chunk"); + return; + } + + /* make sure we include the NULL after the name */ + png_write_chunk_start(png_ptr, png_sPLT, + (png_uint_32)(name_len + 2 + palette_size)); + png_write_chunk_data(png_ptr, (png_bytep)new_name, name_len + 1); + png_write_chunk_data(png_ptr, (png_bytep)&spalette->depth, 1); + + /* loop through each palette entry, writing appropriately */ +#ifndef PNG_NO_POINTER_INDEXING + for (ep = spalette->entries; epentries+spalette->nentries; ep++) + { + if (spalette->depth == 8) + { + entrybuf[0] = (png_byte)ep->red; + entrybuf[1] = (png_byte)ep->green; + entrybuf[2] = (png_byte)ep->blue; + entrybuf[3] = (png_byte)ep->alpha; + png_save_uint_16(entrybuf + 4, ep->frequency); + } + else + { + png_save_uint_16(entrybuf + 0, ep->red); + png_save_uint_16(entrybuf + 2, ep->green); + png_save_uint_16(entrybuf + 4, ep->blue); + png_save_uint_16(entrybuf + 6, ep->alpha); + png_save_uint_16(entrybuf + 8, ep->frequency); + } + png_write_chunk_data(png_ptr, entrybuf, (png_size_t)entry_size); + } +#else + ep=spalette->entries; + for (i=0; i>spalette->nentries; i++) + { + if (spalette->depth == 8) + { + entrybuf[0] = (png_byte)ep[i].red; + entrybuf[1] = (png_byte)ep[i].green; + entrybuf[2] = (png_byte)ep[i].blue; + entrybuf[3] = (png_byte)ep[i].alpha; + png_save_uint_16(entrybuf + 4, ep[i].frequency); + } + else + { + png_save_uint_16(entrybuf + 0, ep[i].red); + png_save_uint_16(entrybuf + 2, ep[i].green); + png_save_uint_16(entrybuf + 4, ep[i].blue); + png_save_uint_16(entrybuf + 6, ep[i].alpha); + png_save_uint_16(entrybuf + 8, ep[i].frequency); + } + png_write_chunk_data(png_ptr, entrybuf, entry_size); + } +#endif + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_name); +} +#endif + +#if defined(PNG_WRITE_sBIT_SUPPORTED) +/* write the sBIT chunk */ +void /* PRIVATE */ +png_write_sBIT(png_structp png_ptr, png_color_8p sbit, int color_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sBIT; +#endif + png_byte buf[4]; + png_size_t size; + + png_debug(1, "in png_write_sBIT\n"); + /* make sure we don't depend upon the order of PNG_COLOR_8 */ + if (color_type & PNG_COLOR_MASK_COLOR) + { + png_byte maxbits; + + maxbits = (png_byte)(color_type==PNG_COLOR_TYPE_PALETTE ? 8 : + png_ptr->usr_bit_depth); + if (sbit->red == 0 || sbit->red > maxbits || + sbit->green == 0 || sbit->green > maxbits || + sbit->blue == 0 || sbit->blue > maxbits) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + buf[0] = sbit->red; + buf[1] = sbit->green; + buf[2] = sbit->blue; + size = 3; + } + else + { + if (sbit->gray == 0 || sbit->gray > png_ptr->usr_bit_depth) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + buf[0] = sbit->gray; + size = 1; + } + + if (color_type & PNG_COLOR_MASK_ALPHA) + { + if (sbit->alpha == 0 || sbit->alpha > png_ptr->usr_bit_depth) + { + png_warning(png_ptr, "Invalid sBIT depth specified"); + return; + } + buf[size++] = sbit->alpha; + } + + png_write_chunk(png_ptr, png_sBIT, buf, size); +} +#endif + +#if defined(PNG_WRITE_cHRM_SUPPORTED) +/* write the cHRM chunk */ +#ifdef PNG_FLOATING_POINT_SUPPORTED +void /* PRIVATE */ +png_write_cHRM(png_structp png_ptr, double white_x, double white_y, + double red_x, double red_y, double green_x, double green_y, + double blue_x, double blue_y) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_cHRM; +#endif + png_byte buf[32]; + png_uint_32 itemp; + + png_debug(1, "in png_write_cHRM\n"); + /* each value is saved in 1/100,000ths */ + if (white_x < 0 || white_x > 0.8 || white_y < 0 || white_y > 0.8 || + white_x + white_y > 1.0) + { + png_warning(png_ptr, "Invalid cHRM white point specified"); +#if !defined(PNG_NO_CONSOLE_IO) + fprintf(stderr,"white_x=%f, white_y=%f\n",white_x, white_y); +#endif + return; + } + itemp = (png_uint_32)(white_x * 100000.0 + 0.5); + png_save_uint_32(buf, itemp); + itemp = (png_uint_32)(white_y * 100000.0 + 0.5); + png_save_uint_32(buf + 4, itemp); + + if (red_x < 0 || red_y < 0 || red_x + red_y > 1.0) + { + png_warning(png_ptr, "Invalid cHRM red point specified"); + return; + } + itemp = (png_uint_32)(red_x * 100000.0 + 0.5); + png_save_uint_32(buf + 8, itemp); + itemp = (png_uint_32)(red_y * 100000.0 + 0.5); + png_save_uint_32(buf + 12, itemp); + + if (green_x < 0 || green_y < 0 || green_x + green_y > 1.0) + { + png_warning(png_ptr, "Invalid cHRM green point specified"); + return; + } + itemp = (png_uint_32)(green_x * 100000.0 + 0.5); + png_save_uint_32(buf + 16, itemp); + itemp = (png_uint_32)(green_y * 100000.0 + 0.5); + png_save_uint_32(buf + 20, itemp); + + if (blue_x < 0 || blue_y < 0 || blue_x + blue_y > 1.0) + { + png_warning(png_ptr, "Invalid cHRM blue point specified"); + return; + } + itemp = (png_uint_32)(blue_x * 100000.0 + 0.5); + png_save_uint_32(buf + 24, itemp); + itemp = (png_uint_32)(blue_y * 100000.0 + 0.5); + png_save_uint_32(buf + 28, itemp); + + png_write_chunk(png_ptr, png_cHRM, buf, (png_size_t)32); +} +#endif +#ifdef PNG_FIXED_POINT_SUPPORTED +void /* PRIVATE */ +png_write_cHRM_fixed(png_structp png_ptr, png_fixed_point white_x, + png_fixed_point white_y, png_fixed_point red_x, png_fixed_point red_y, + png_fixed_point green_x, png_fixed_point green_y, png_fixed_point blue_x, + png_fixed_point blue_y) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_cHRM; +#endif + png_byte buf[32]; + + png_debug(1, "in png_write_cHRM\n"); + /* each value is saved in 1/100,000ths */ + if (white_x > 80000L || white_y > 80000L || white_x + white_y > 100000L) + { + png_warning(png_ptr, "Invalid fixed cHRM white point specified"); +#if !defined(PNG_NO_CONSOLE_IO) + fprintf(stderr,"white_x=%ld, white_y=%ld\n",white_x, white_y); +#endif + return; + } + png_save_uint_32(buf, (png_uint_32)white_x); + png_save_uint_32(buf + 4, (png_uint_32)white_y); + + if (red_x + red_y > 100000L) + { + png_warning(png_ptr, "Invalid cHRM fixed red point specified"); + return; + } + png_save_uint_32(buf + 8, (png_uint_32)red_x); + png_save_uint_32(buf + 12, (png_uint_32)red_y); + + if (green_x + green_y > 100000L) + { + png_warning(png_ptr, "Invalid fixed cHRM green point specified"); + return; + } + png_save_uint_32(buf + 16, (png_uint_32)green_x); + png_save_uint_32(buf + 20, (png_uint_32)green_y); + + if (blue_x + blue_y > 100000L) + { + png_warning(png_ptr, "Invalid fixed cHRM blue point specified"); + return; + } + png_save_uint_32(buf + 24, (png_uint_32)blue_x); + png_save_uint_32(buf + 28, (png_uint_32)blue_y); + + png_write_chunk(png_ptr, png_cHRM, buf, (png_size_t)32); +} +#endif +#endif + +#if defined(PNG_WRITE_tRNS_SUPPORTED) +/* write the tRNS chunk */ +void /* PRIVATE */ +png_write_tRNS(png_structp png_ptr, png_bytep trans, png_color_16p tran, + int num_trans, int color_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_tRNS; +#endif + png_byte buf[6]; + + png_debug(1, "in png_write_tRNS\n"); + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + if (num_trans <= 0 || num_trans > (int)png_ptr->num_palette) + { + png_warning(png_ptr,"Invalid number of transparent colors specified"); + return; + } + /* write the chunk out as it is */ + png_write_chunk(png_ptr, png_tRNS, trans, (png_size_t)num_trans); + } + else if (color_type == PNG_COLOR_TYPE_GRAY) + { + /* one 16 bit value */ + if(tran->gray >= (1 << png_ptr->bit_depth)) + { + png_warning(png_ptr, + "Ignoring attempt to write tRNS chunk out-of-range for bit_depth"); + return; + } + png_save_uint_16(buf, tran->gray); + png_write_chunk(png_ptr, png_tRNS, buf, (png_size_t)2); + } + else if (color_type == PNG_COLOR_TYPE_RGB) + { + /* three 16 bit values */ + png_save_uint_16(buf, tran->red); + png_save_uint_16(buf + 2, tran->green); + png_save_uint_16(buf + 4, tran->blue); + if(png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4])) + { + png_warning(png_ptr, + "Ignoring attempt to write 16-bit tRNS chunk when bit_depth is 8"); + return; + } + png_write_chunk(png_ptr, png_tRNS, buf, (png_size_t)6); + } + else + { + png_warning(png_ptr, "Can't write tRNS with an alpha channel"); + } +} +#endif + +#if defined(PNG_WRITE_bKGD_SUPPORTED) +/* write the background chunk */ +void /* PRIVATE */ +png_write_bKGD(png_structp png_ptr, png_color_16p back, int color_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_bKGD; +#endif + png_byte buf[6]; + + png_debug(1, "in png_write_bKGD\n"); + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + if ( +#if defined(PNG_MNG_FEATURES_SUPPORTED) + (png_ptr->num_palette || + (!(png_ptr->mng_features_permitted & PNG_FLAG_MNG_EMPTY_PLTE))) && +#endif + back->index > png_ptr->num_palette) + { + png_warning(png_ptr, "Invalid background palette index"); + return; + } + buf[0] = back->index; + png_write_chunk(png_ptr, png_bKGD, buf, (png_size_t)1); + } + else if (color_type & PNG_COLOR_MASK_COLOR) + { + png_save_uint_16(buf, back->red); + png_save_uint_16(buf + 2, back->green); + png_save_uint_16(buf + 4, back->blue); + if(png_ptr->bit_depth == 8 && (buf[0] | buf[2] | buf[4])) + { + png_warning(png_ptr, + "Ignoring attempt to write 16-bit bKGD chunk when bit_depth is 8"); + return; + } + png_write_chunk(png_ptr, png_bKGD, buf, (png_size_t)6); + } + else + { + if(back->gray >= (1 << png_ptr->bit_depth)) + { + png_warning(png_ptr, + "Ignoring attempt to write bKGD chunk out-of-range for bit_depth"); + return; + } + png_save_uint_16(buf, back->gray); + png_write_chunk(png_ptr, png_bKGD, buf, (png_size_t)2); + } +} +#endif + +#if defined(PNG_WRITE_hIST_SUPPORTED) +/* write the histogram */ +void /* PRIVATE */ +png_write_hIST(png_structp png_ptr, png_uint_16p hist, int num_hist) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_hIST; +#endif + int i; + png_byte buf[3]; + + png_debug(1, "in png_write_hIST\n"); + if (num_hist > (int)png_ptr->num_palette) + { + png_debug2(3, "num_hist = %d, num_palette = %d\n", num_hist, + png_ptr->num_palette); + png_warning(png_ptr, "Invalid number of histogram entries specified"); + return; + } + + png_write_chunk_start(png_ptr, png_hIST, (png_uint_32)(num_hist * 2)); + for (i = 0; i < num_hist; i++) + { + png_save_uint_16(buf, hist[i]); + png_write_chunk_data(png_ptr, buf, (png_size_t)2); + } + png_write_chunk_end(png_ptr); +} +#endif + +#if defined(PNG_WRITE_TEXT_SUPPORTED) || defined(PNG_WRITE_pCAL_SUPPORTED) || \ + defined(PNG_WRITE_iCCP_SUPPORTED) || defined(PNG_WRITE_sPLT_SUPPORTED) +/* Check that the tEXt or zTXt keyword is valid per PNG 1.0 specification, + * and if invalid, correct the keyword rather than discarding the entire + * chunk. The PNG 1.0 specification requires keywords 1-79 characters in + * length, forbids leading or trailing whitespace, multiple internal spaces, + * and the non-break space (0x80) from ISO 8859-1. Returns keyword length. + * + * The new_key is allocated to hold the corrected keyword and must be freed + * by the calling routine. This avoids problems with trying to write to + * static keywords without having to have duplicate copies of the strings. + */ +png_size_t /* PRIVATE */ +png_check_keyword(png_structp png_ptr, png_charp key, png_charpp new_key) +{ + png_size_t key_len; + png_charp kp, dp; + int kflag; + int kwarn=0; + + png_debug(1, "in png_check_keyword\n"); + *new_key = NULL; + + if (key == NULL || (key_len = png_strlen(key)) == 0) + { + png_warning(png_ptr, "zero length keyword"); + return ((png_size_t)0); + } + + png_debug1(2, "Keyword to be checked is '%s'\n", key); + + *new_key = (png_charp)png_malloc_warn(png_ptr, (png_uint_32)(key_len + 2)); + if (*new_key == NULL) + { + png_warning(png_ptr, "Out of memory while procesing keyword"); + return ((png_size_t)0); + } + + /* Replace non-printing characters with a blank and print a warning */ + for (kp = key, dp = *new_key; *kp != '\0'; kp++, dp++) + { + if ((png_byte)*kp < 0x20 || + ((png_byte)*kp > 0x7E && (png_byte)*kp < 0xA1)) + { +#if !defined(PNG_NO_STDIO) && !defined(_WIN32_WCE) + char msg[40]; + + png_snprintf(msg, 40, + "invalid keyword character 0x%02X", (png_byte)*kp); + png_warning(png_ptr, msg); +#else + png_warning(png_ptr, "invalid character in keyword"); +#endif + *dp = ' '; + } + else + { + *dp = *kp; + } + } + *dp = '\0'; + + /* Remove any trailing white space. */ + kp = *new_key + key_len - 1; + if (*kp == ' ') + { + png_warning(png_ptr, "trailing spaces removed from keyword"); + + while (*kp == ' ') + { + *(kp--) = '\0'; + key_len--; + } + } + + /* Remove any leading white space. */ + kp = *new_key; + if (*kp == ' ') + { + png_warning(png_ptr, "leading spaces removed from keyword"); + + while (*kp == ' ') + { + kp++; + key_len--; + } + } + + png_debug1(2, "Checking for multiple internal spaces in '%s'\n", kp); + + /* Remove multiple internal spaces. */ + for (kflag = 0, dp = *new_key; *kp != '\0'; kp++) + { + if (*kp == ' ' && kflag == 0) + { + *(dp++) = *kp; + kflag = 1; + } + else if (*kp == ' ') + { + key_len--; + kwarn=1; + } + else + { + *(dp++) = *kp; + kflag = 0; + } + } + *dp = '\0'; + if(kwarn) + png_warning(png_ptr, "extra interior spaces removed from keyword"); + + if (key_len == 0) + { + png_free(png_ptr, *new_key); + *new_key=NULL; + png_warning(png_ptr, "Zero length keyword"); + } + + if (key_len > 79) + { + png_warning(png_ptr, "keyword length must be 1 - 79 characters"); + new_key[79] = '\0'; + key_len = 79; + } + + return (key_len); +} +#endif + +#if defined(PNG_WRITE_tEXt_SUPPORTED) +/* write a tEXt chunk */ +void /* PRIVATE */ +png_write_tEXt(png_structp png_ptr, png_charp key, png_charp text, + png_size_t text_len) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_tEXt; +#endif + png_size_t key_len; + png_charp new_key; + + png_debug(1, "in png_write_tEXt\n"); + if (key == NULL || (key_len = png_check_keyword(png_ptr, key, &new_key))==0) + { + png_warning(png_ptr, "Empty keyword in tEXt chunk"); + return; + } + + if (text == NULL || *text == '\0') + text_len = 0; + else + text_len = png_strlen(text); + + /* make sure we include the 0 after the key */ + png_write_chunk_start(png_ptr, png_tEXt, (png_uint_32)key_len+text_len+1); + /* + * We leave it to the application to meet PNG-1.0 requirements on the + * contents of the text. PNG-1.0 through PNG-1.2 discourage the use of + * any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them. + * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG. + */ + png_write_chunk_data(png_ptr, (png_bytep)new_key, key_len + 1); + if (text_len) + png_write_chunk_data(png_ptr, (png_bytep)text, text_len); + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_key); +} +#endif + +#if defined(PNG_WRITE_zTXt_SUPPORTED) +/* write a compressed text chunk */ +void /* PRIVATE */ +png_write_zTXt(png_structp png_ptr, png_charp key, png_charp text, + png_size_t text_len, int compression) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_zTXt; +#endif + png_size_t key_len; + char buf[1]; + png_charp new_key; + compression_state comp; + + png_debug(1, "in png_write_zTXt\n"); + + comp.num_output_ptr = 0; + comp.max_output_ptr = 0; + comp.output_ptr = NULL; + comp.input = NULL; + comp.input_len = 0; + + if (key == NULL || (key_len = png_check_keyword(png_ptr, key, &new_key))==0) + { + png_warning(png_ptr, "Empty keyword in zTXt chunk"); + return; + } + + if (text == NULL || *text == '\0' || compression==PNG_TEXT_COMPRESSION_NONE) + { + png_write_tEXt(png_ptr, new_key, text, (png_size_t)0); + png_free(png_ptr, new_key); + return; + } + + text_len = png_strlen(text); + + /* compute the compressed data; do it now for the length */ + text_len = png_text_compress(png_ptr, text, text_len, compression, + &comp); + + /* write start of chunk */ + png_write_chunk_start(png_ptr, png_zTXt, (png_uint_32) + (key_len+text_len+2)); + /* write key */ + png_write_chunk_data(png_ptr, (png_bytep)new_key, key_len + 1); + png_free(png_ptr, new_key); + + buf[0] = (png_byte)compression; + /* write compression */ + png_write_chunk_data(png_ptr, (png_bytep)buf, (png_size_t)1); + /* write the compressed data */ + png_write_compressed_data_out(png_ptr, &comp); + + /* close the chunk */ + png_write_chunk_end(png_ptr); +} +#endif + +#if defined(PNG_WRITE_iTXt_SUPPORTED) +/* write an iTXt chunk */ +void /* PRIVATE */ +png_write_iTXt(png_structp png_ptr, int compression, png_charp key, + png_charp lang, png_charp lang_key, png_charp text) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_iTXt; +#endif + png_size_t lang_len, key_len, lang_key_len, text_len; + png_charp new_lang, new_key; + png_byte cbuf[2]; + compression_state comp; + + png_debug(1, "in png_write_iTXt\n"); + + comp.num_output_ptr = 0; + comp.max_output_ptr = 0; + comp.output_ptr = NULL; + comp.input = NULL; + + if (key == NULL || (key_len = png_check_keyword(png_ptr, key, &new_key))==0) + { + png_warning(png_ptr, "Empty keyword in iTXt chunk"); + return; + } + if (lang == NULL || (lang_len = png_check_keyword(png_ptr, lang, &new_lang))==0) + { + png_warning(png_ptr, "Empty language field in iTXt chunk"); + new_lang = NULL; + lang_len = 0; + } + + if (lang_key == NULL) + lang_key_len = 0; + else + lang_key_len = png_strlen(lang_key); + + if (text == NULL) + text_len = 0; + else + text_len = png_strlen(text); + + /* compute the compressed data; do it now for the length */ + text_len = png_text_compress(png_ptr, text, text_len, compression-2, + &comp); + + + /* make sure we include the compression flag, the compression byte, + * and the NULs after the key, lang, and lang_key parts */ + + png_write_chunk_start(png_ptr, png_iTXt, + (png_uint_32)( + 5 /* comp byte, comp flag, terminators for key, lang and lang_key */ + + key_len + + lang_len + + lang_key_len + + text_len)); + + /* + * We leave it to the application to meet PNG-1.0 requirements on the + * contents of the text. PNG-1.0 through PNG-1.2 discourage the use of + * any non-Latin-1 characters except for NEWLINE. ISO PNG will forbid them. + * The NUL character is forbidden by PNG-1.0 through PNG-1.2 and ISO PNG. + */ + png_write_chunk_data(png_ptr, (png_bytep)new_key, key_len + 1); + + /* set the compression flag */ + if (compression == PNG_ITXT_COMPRESSION_NONE || \ + compression == PNG_TEXT_COMPRESSION_NONE) + cbuf[0] = 0; + else /* compression == PNG_ITXT_COMPRESSION_zTXt */ + cbuf[0] = 1; + /* set the compression method */ + cbuf[1] = 0; + png_write_chunk_data(png_ptr, cbuf, 2); + + cbuf[0] = 0; + png_write_chunk_data(png_ptr, (new_lang ? (png_bytep)new_lang : cbuf), lang_len + 1); + png_write_chunk_data(png_ptr, (lang_key ? (png_bytep)lang_key : cbuf), lang_key_len + 1); + png_write_compressed_data_out(png_ptr, &comp); + + png_write_chunk_end(png_ptr); + png_free(png_ptr, new_key); + if (new_lang) + png_free(png_ptr, new_lang); +} +#endif + +#if defined(PNG_WRITE_oFFs_SUPPORTED) +/* write the oFFs chunk */ +void /* PRIVATE */ +png_write_oFFs(png_structp png_ptr, png_int_32 x_offset, png_int_32 y_offset, + int unit_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_oFFs; +#endif + png_byte buf[9]; + + png_debug(1, "in png_write_oFFs\n"); + if (unit_type >= PNG_OFFSET_LAST) + png_warning(png_ptr, "Unrecognized unit type for oFFs chunk"); + + png_save_int_32(buf, x_offset); + png_save_int_32(buf + 4, y_offset); + buf[8] = (png_byte)unit_type; + + png_write_chunk(png_ptr, png_oFFs, buf, (png_size_t)9); +} +#endif +#if defined(PNG_WRITE_pCAL_SUPPORTED) +/* write the pCAL chunk (described in the PNG extensions document) */ +void /* PRIVATE */ +png_write_pCAL(png_structp png_ptr, png_charp purpose, png_int_32 X0, + png_int_32 X1, int type, int nparams, png_charp units, png_charpp params) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_pCAL; +#endif + png_size_t purpose_len, units_len, total_len; + png_uint_32p params_len; + png_byte buf[10]; + png_charp new_purpose; + int i; + + png_debug1(1, "in png_write_pCAL (%d parameters)\n", nparams); + if (type >= PNG_EQUATION_LAST) + png_warning(png_ptr, "Unrecognized equation type for pCAL chunk"); + + purpose_len = png_check_keyword(png_ptr, purpose, &new_purpose) + 1; + png_debug1(3, "pCAL purpose length = %d\n", (int)purpose_len); + units_len = png_strlen(units) + (nparams == 0 ? 0 : 1); + png_debug1(3, "pCAL units length = %d\n", (int)units_len); + total_len = purpose_len + units_len + 10; + + params_len = (png_uint_32p)png_malloc(png_ptr, (png_uint_32)(nparams + *png_sizeof(png_uint_32))); + + /* Find the length of each parameter, making sure we don't count the + null terminator for the last parameter. */ + for (i = 0; i < nparams; i++) + { + params_len[i] = png_strlen(params[i]) + (i == nparams - 1 ? 0 : 1); + png_debug2(3, "pCAL parameter %d length = %lu\n", i, params_len[i]); + total_len += (png_size_t)params_len[i]; + } + + png_debug1(3, "pCAL total length = %d\n", (int)total_len); + png_write_chunk_start(png_ptr, png_pCAL, (png_uint_32)total_len); + png_write_chunk_data(png_ptr, (png_bytep)new_purpose, purpose_len); + png_save_int_32(buf, X0); + png_save_int_32(buf + 4, X1); + buf[8] = (png_byte)type; + buf[9] = (png_byte)nparams; + png_write_chunk_data(png_ptr, buf, (png_size_t)10); + png_write_chunk_data(png_ptr, (png_bytep)units, (png_size_t)units_len); + + png_free(png_ptr, new_purpose); + + for (i = 0; i < nparams; i++) + { + png_write_chunk_data(png_ptr, (png_bytep)params[i], + (png_size_t)params_len[i]); + } + + png_free(png_ptr, params_len); + png_write_chunk_end(png_ptr); +} +#endif + +#if defined(PNG_WRITE_sCAL_SUPPORTED) +/* write the sCAL chunk */ +#if defined(PNG_FLOATING_POINT_SUPPORTED) && !defined(PNG_NO_STDIO) +void /* PRIVATE */ +png_write_sCAL(png_structp png_ptr, int unit, double width, double height) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sCAL; +#endif + char buf[64]; + png_size_t total_len; + + png_debug(1, "in png_write_sCAL\n"); + + buf[0] = (char)unit; +#if defined(_WIN32_WCE) +/* sprintf() function is not supported on WindowsCE */ + { + wchar_t wc_buf[32]; + size_t wc_len; + swprintf(wc_buf, TEXT("%12.12e"), width); + wc_len = wcslen(wc_buf); + WideCharToMultiByte(CP_ACP, 0, wc_buf, -1, buf + 1, wc_len, NULL, NULL); + total_len = wc_len + 2; + swprintf(wc_buf, TEXT("%12.12e"), height); + wc_len = wcslen(wc_buf); + WideCharToMultiByte(CP_ACP, 0, wc_buf, -1, buf + total_len, wc_len, + NULL, NULL); + total_len += wc_len; + } +#else + png_snprintf(buf + 1, 63, "%12.12e", width); + total_len = 1 + png_strlen(buf + 1) + 1; + png_snprintf(buf + total_len, 64-total_len, "%12.12e", height); + total_len += png_strlen(buf + total_len); +#endif + + png_debug1(3, "sCAL total length = %u\n", (unsigned int)total_len); + png_write_chunk(png_ptr, png_sCAL, (png_bytep)buf, total_len); +} +#else +#ifdef PNG_FIXED_POINT_SUPPORTED +void /* PRIVATE */ +png_write_sCAL_s(png_structp png_ptr, int unit, png_charp width, + png_charp height) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_sCAL; +#endif + png_byte buf[64]; + png_size_t wlen, hlen, total_len; + + png_debug(1, "in png_write_sCAL_s\n"); + + wlen = png_strlen(width); + hlen = png_strlen(height); + total_len = wlen + hlen + 2; + if (total_len > 64) + { + png_warning(png_ptr, "Can't write sCAL (buffer too small)"); + return; + } + + buf[0] = (png_byte)unit; + png_memcpy(buf + 1, width, wlen + 1); /* append the '\0' here */ + png_memcpy(buf + wlen + 2, height, hlen); /* do NOT append the '\0' here */ + + png_debug1(3, "sCAL total length = %u\n", (unsigned int)total_len); + png_write_chunk(png_ptr, png_sCAL, buf, total_len); +} +#endif +#endif +#endif + +#if defined(PNG_WRITE_pHYs_SUPPORTED) +/* write the pHYs chunk */ +void /* PRIVATE */ +png_write_pHYs(png_structp png_ptr, png_uint_32 x_pixels_per_unit, + png_uint_32 y_pixels_per_unit, + int unit_type) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_pHYs; +#endif + png_byte buf[9]; + + png_debug(1, "in png_write_pHYs\n"); + if (unit_type >= PNG_RESOLUTION_LAST) + png_warning(png_ptr, "Unrecognized unit type for pHYs chunk"); + + png_save_uint_32(buf, x_pixels_per_unit); + png_save_uint_32(buf + 4, y_pixels_per_unit); + buf[8] = (png_byte)unit_type; + + png_write_chunk(png_ptr, png_pHYs, buf, (png_size_t)9); +} +#endif + +#if defined(PNG_WRITE_tIME_SUPPORTED) +/* Write the tIME chunk. Use either png_convert_from_struct_tm() + * or png_convert_from_time_t(), or fill in the structure yourself. + */ +void /* PRIVATE */ +png_write_tIME(png_structp png_ptr, png_timep mod_time) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + PNG_tIME; +#endif + png_byte buf[7]; + + png_debug(1, "in png_write_tIME\n"); + if (mod_time->month > 12 || mod_time->month < 1 || + mod_time->day > 31 || mod_time->day < 1 || + mod_time->hour > 23 || mod_time->second > 60) + { + png_warning(png_ptr, "Invalid time specified for tIME chunk"); + return; + } + + png_save_uint_16(buf, mod_time->year); + buf[2] = mod_time->month; + buf[3] = mod_time->day; + buf[4] = mod_time->hour; + buf[5] = mod_time->minute; + buf[6] = mod_time->second; + + png_write_chunk(png_ptr, png_tIME, buf, (png_size_t)7); +} +#endif + +/* initializes the row writing capability of libpng */ +void /* PRIVATE */ +png_write_start_row(png_structp png_ptr) +{ +#ifdef PNG_WRITE_INTERLACING_SUPPORTED +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* start of interlace block in the y direction */ + int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* offset to next interlace block in the y direction */ + int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif +#endif + + png_size_t buf_size; + + png_debug(1, "in png_write_start_row\n"); + buf_size = (png_size_t)(PNG_ROWBYTES( + png_ptr->usr_channels*png_ptr->usr_bit_depth,png_ptr->width)+1); + + /* set up row buffer */ + png_ptr->row_buf = (png_bytep)png_malloc(png_ptr, (png_uint_32)buf_size); + png_ptr->row_buf[0] = PNG_FILTER_VALUE_NONE; + +#ifndef PNG_NO_WRITE_FILTERING + /* set up filtering buffer, if using this filter */ + if (png_ptr->do_filter & PNG_FILTER_SUB) + { + png_ptr->sub_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->sub_row[0] = PNG_FILTER_VALUE_SUB; + } + + /* We only need to keep the previous row if we are using one of these. */ + if (png_ptr->do_filter & (PNG_FILTER_AVG | PNG_FILTER_UP | PNG_FILTER_PAETH)) + { + /* set up previous row buffer */ + png_ptr->prev_row = (png_bytep)png_malloc(png_ptr, (png_uint_32)buf_size); + png_memset(png_ptr->prev_row, 0, buf_size); + + if (png_ptr->do_filter & PNG_FILTER_UP) + { + png_ptr->up_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->up_row[0] = PNG_FILTER_VALUE_UP; + } + + if (png_ptr->do_filter & PNG_FILTER_AVG) + { + png_ptr->avg_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->avg_row[0] = PNG_FILTER_VALUE_AVG; + } + + if (png_ptr->do_filter & PNG_FILTER_PAETH) + { + png_ptr->paeth_row = (png_bytep)png_malloc(png_ptr, + (png_ptr->rowbytes + 1)); + png_ptr->paeth_row[0] = PNG_FILTER_VALUE_PAETH; + } +#endif /* PNG_NO_WRITE_FILTERING */ + } + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* if interlaced, we need to set up width and height of pass */ + if (png_ptr->interlaced) + { + if (!(png_ptr->transformations & PNG_INTERLACE)) + { + png_ptr->num_rows = (png_ptr->height + png_pass_yinc[0] - 1 - + png_pass_ystart[0]) / png_pass_yinc[0]; + png_ptr->usr_width = (png_ptr->width + png_pass_inc[0] - 1 - + png_pass_start[0]) / png_pass_inc[0]; + } + else + { + png_ptr->num_rows = png_ptr->height; + png_ptr->usr_width = png_ptr->width; + } + } + else +#endif + { + png_ptr->num_rows = png_ptr->height; + png_ptr->usr_width = png_ptr->width; + } + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + png_ptr->zstream.next_out = png_ptr->zbuf; +} + +/* Internal use only. Called when finished processing a row of data. */ +void /* PRIVATE */ +png_write_finish_row(png_structp png_ptr) +{ +#ifdef PNG_WRITE_INTERLACING_SUPPORTED +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; + + /* start of interlace block in the y direction */ + int png_pass_ystart[7] = {0, 0, 4, 0, 2, 0, 1}; + + /* offset to next interlace block in the y direction */ + int png_pass_yinc[7] = {8, 8, 8, 4, 4, 2, 2}; +#endif +#endif + + int ret; + + png_debug(1, "in png_write_finish_row\n"); + /* next row */ + png_ptr->row_number++; + + /* see if we are done */ + if (png_ptr->row_number < png_ptr->num_rows) + return; + +#ifdef PNG_WRITE_INTERLACING_SUPPORTED + /* if interlaced, go to next pass */ + if (png_ptr->interlaced) + { + png_ptr->row_number = 0; + if (png_ptr->transformations & PNG_INTERLACE) + { + png_ptr->pass++; + } + else + { + /* loop until we find a non-zero width or height pass */ + do + { + png_ptr->pass++; + if (png_ptr->pass >= 7) + break; + png_ptr->usr_width = (png_ptr->width + + png_pass_inc[png_ptr->pass] - 1 - + png_pass_start[png_ptr->pass]) / + png_pass_inc[png_ptr->pass]; + png_ptr->num_rows = (png_ptr->height + + png_pass_yinc[png_ptr->pass] - 1 - + png_pass_ystart[png_ptr->pass]) / + png_pass_yinc[png_ptr->pass]; + if (png_ptr->transformations & PNG_INTERLACE) + break; + } while (png_ptr->usr_width == 0 || png_ptr->num_rows == 0); + + } + + /* reset the row above the image for the next pass */ + if (png_ptr->pass < 7) + { + if (png_ptr->prev_row != NULL) + png_memset(png_ptr->prev_row, 0, + (png_size_t)(PNG_ROWBYTES(png_ptr->usr_channels* + png_ptr->usr_bit_depth,png_ptr->width))+1); + return; + } + } +#endif + + /* if we get here, we've just written the last row, so we need + to flush the compressor */ + do + { + /* tell the compressor we are done */ + ret = deflate(&png_ptr->zstream, Z_FINISH); + /* check for an error */ + if (ret == Z_OK) + { + /* check to see if we need more room */ + if (!(png_ptr->zstream.avail_out)) + { + png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + } + else if (ret != Z_STREAM_END) + { + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + } while (ret != Z_STREAM_END); + + /* write any extra space */ + if (png_ptr->zstream.avail_out < png_ptr->zbuf_size) + { + png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size - + png_ptr->zstream.avail_out); + } + + deflateReset(&png_ptr->zstream); + png_ptr->zstream.data_type = Z_BINARY; +} + +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) +/* Pick out the correct pixels for the interlace pass. + * The basic idea here is to go through the row with a source + * pointer and a destination pointer (sp and dp), and copy the + * correct pixels for the pass. As the row gets compacted, + * sp will always be >= dp, so we should never overwrite anything. + * See the default: case for the easiest code to understand. + */ +void /* PRIVATE */ +png_do_write_interlace(png_row_infop row_info, png_bytep row, int pass) +{ +#ifdef PNG_USE_LOCAL_ARRAYS + /* arrays to facilitate easy interlacing - use pass (0 - 6) as index */ + + /* start of interlace block */ + int png_pass_start[7] = {0, 4, 0, 2, 0, 1, 0}; + + /* offset to next interlace block */ + int png_pass_inc[7] = {8, 8, 4, 4, 2, 2, 1}; +#endif + + png_debug(1, "in png_do_write_interlace\n"); + /* we don't have to do anything on the last pass (6) */ +#if defined(PNG_USELESS_TESTS_SUPPORTED) + if (row != NULL && row_info != NULL && pass < 6) +#else + if (pass < 6) +#endif + { + /* each pixel depth is handled separately */ + switch (row_info->pixel_depth) + { + case 1: + { + png_bytep sp; + png_bytep dp; + int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + d = 0; + shift = 7; + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 3); + value = (int)(*sp >> (7 - (int)(i & 0x07))) & 0x01; + d |= (value << shift); + + if (shift == 0) + { + shift = 7; + *dp++ = (png_byte)d; + d = 0; + } + else + shift--; + + } + if (shift != 7) + *dp = (png_byte)d; + break; + } + case 2: + { + png_bytep sp; + png_bytep dp; + int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + shift = 6; + d = 0; + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 2); + value = (*sp >> ((3 - (int)(i & 0x03)) << 1)) & 0x03; + d |= (value << shift); + + if (shift == 0) + { + shift = 6; + *dp++ = (png_byte)d; + d = 0; + } + else + shift -= 2; + } + if (shift != 6) + *dp = (png_byte)d; + break; + } + case 4: + { + png_bytep sp; + png_bytep dp; + int shift; + int d; + int value; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + + dp = row; + shift = 4; + d = 0; + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + sp = row + (png_size_t)(i >> 1); + value = (*sp >> ((1 - (int)(i & 0x01)) << 2)) & 0x0f; + d |= (value << shift); + + if (shift == 0) + { + shift = 4; + *dp++ = (png_byte)d; + d = 0; + } + else + shift -= 4; + } + if (shift != 4) + *dp = (png_byte)d; + break; + } + default: + { + png_bytep sp; + png_bytep dp; + png_uint_32 i; + png_uint_32 row_width = row_info->width; + png_size_t pixel_bytes; + + /* start at the beginning */ + dp = row; + /* find out how many bytes each pixel takes up */ + pixel_bytes = (row_info->pixel_depth >> 3); + /* loop through the row, only looking at the pixels that + matter */ + for (i = png_pass_start[pass]; i < row_width; + i += png_pass_inc[pass]) + { + /* find out where the original pixel is */ + sp = row + (png_size_t)i * pixel_bytes; + /* move the pixel */ + if (dp != sp) + png_memcpy(dp, sp, pixel_bytes); + /* next pixel */ + dp += pixel_bytes; + } + break; + } + } + /* set new row width */ + row_info->width = (row_info->width + + png_pass_inc[pass] - 1 - + png_pass_start[pass]) / + png_pass_inc[pass]; + row_info->rowbytes = PNG_ROWBYTES(row_info->pixel_depth, + row_info->width); + } +} +#endif + +/* This filters the row, chooses which filter to use, if it has not already + * been specified by the application, and then writes the row out with the + * chosen filter. + */ +#define PNG_MAXSUM (((png_uint_32)(-1)) >> 1) +#define PNG_HISHIFT 10 +#define PNG_LOMASK ((png_uint_32)0xffffL) +#define PNG_HIMASK ((png_uint_32)(~PNG_LOMASK >> PNG_HISHIFT)) +void /* PRIVATE */ +png_write_find_filter(png_structp png_ptr, png_row_infop row_info) +{ + png_bytep best_row; +#ifndef PNG_NO_WRITE_FILTER + png_bytep prev_row, row_buf; + png_uint_32 mins, bpp; + png_byte filter_to_do = png_ptr->do_filter; + png_uint_32 row_bytes = row_info->rowbytes; +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + int num_p_filters = (int)png_ptr->num_prev_filters; +#endif + + png_debug(1, "in png_write_find_filter\n"); + /* find out how many bytes offset each pixel is */ + bpp = (row_info->pixel_depth + 7) >> 3; + + prev_row = png_ptr->prev_row; +#endif + best_row = png_ptr->row_buf; +#ifndef PNG_NO_WRITE_FILTER + row_buf = best_row; + mins = PNG_MAXSUM; + + /* The prediction method we use is to find which method provides the + * smallest value when summing the absolute values of the distances + * from zero, using anything >= 128 as negative numbers. This is known + * as the "minimum sum of absolute differences" heuristic. Other + * heuristics are the "weighted minimum sum of absolute differences" + * (experimental and can in theory improve compression), and the "zlib + * predictive" method (not implemented yet), which does test compressions + * of lines using different filter methods, and then chooses the + * (series of) filter(s) that give minimum compressed data size (VERY + * computationally expensive). + * + * GRR 980525: consider also + * (1) minimum sum of absolute differences from running average (i.e., + * keep running sum of non-absolute differences & count of bytes) + * [track dispersion, too? restart average if dispersion too large?] + * (1b) minimum sum of absolute differences from sliding average, probably + * with window size <= deflate window (usually 32K) + * (2) minimum sum of squared differences from zero or running average + * (i.e., ~ root-mean-square approach) + */ + + + /* We don't need to test the 'no filter' case if this is the only filter + * that has been chosen, as it doesn't actually do anything to the data. + */ + if ((filter_to_do & PNG_FILTER_NONE) && + filter_to_do != PNG_FILTER_NONE) + { + png_bytep rp; + png_uint_32 sum = 0; + png_uint_32 i; + int v; + + for (i = 0, rp = row_buf + 1; i < row_bytes; i++, rp++) + { + v = *rp; + sum += (v < 128) ? v : 256 - v; + } + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + png_uint_32 sumhi, sumlo; + int j; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; /* Gives us some footroom */ + + /* Reduce the sum if we match any of the previous rows */ + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + /* Factor in the cost of this filter (this is here for completeness, + * but it makes no sense to have a "cost" for the NONE filter, as + * it has the minimum possible computational cost - none). + */ + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_NONE]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + mins = sum; + } + + /* sub filter */ + if (filter_to_do == PNG_FILTER_SUB) + /* it's the only filter so no testing is needed */ + { + png_bytep rp, lp, dp; + png_uint_32 i; + for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp; + i++, rp++, dp++) + { + *dp = *rp; + } + for (lp = row_buf + 1; i < row_bytes; + i++, rp++, lp++, dp++) + { + *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); + } + best_row = png_ptr->sub_row; + } + + else if (filter_to_do & PNG_FILTER_SUB) + { + png_bytep rp, dp, lp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + /* We temporarily increase the "minimum sum" by the factor we + * would reduce the sum of this filter, so that we can do the + * early exit comparison without scaling the sum each time. + */ + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->sub_row + 1; i < bpp; + i++, rp++, dp++) + { + v = *dp = *rp; + + sum += (v < 128) ? v : 256 - v; + } + for (lp = row_buf + 1; i < row_bytes; + i++, rp++, lp++, dp++) + { + v = *dp = (png_byte)(((int)*rp - (int)*lp) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_SUB) + { + sumlo = (sumlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_SUB]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->sub_row; + } + } + + /* up filter */ + if (filter_to_do == PNG_FILTER_UP) + { + png_bytep rp, dp, pp; + png_uint_32 i; + + for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1, + pp = prev_row + 1; i < row_bytes; + i++, rp++, pp++, dp++) + { + *dp = (png_byte)(((int)*rp - (int)*pp) & 0xff); + } + best_row = png_ptr->up_row; + } + + else if (filter_to_do & PNG_FILTER_UP) + { + png_bytep rp, dp, pp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->up_row + 1, + pp = prev_row + 1; i < row_bytes; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_UP) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_UP]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->up_row; + } + } + + /* avg filter */ + if (filter_to_do == PNG_FILTER_AVG) + { + png_bytep rp, dp, pp, lp; + png_uint_32 i; + for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); + } + for (lp = row_buf + 1; i < row_bytes; i++) + { + *dp++ = (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) + & 0xff); + } + best_row = png_ptr->avg_row; + } + + else if (filter_to_do & PNG_FILTER_AVG) + { + png_bytep rp, dp, pp, lp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_AVG) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->avg_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - ((int)*pp++ / 2)) & 0xff); + + sum += (v < 128) ? v : 256 - v; + } + for (lp = row_buf + 1; i < row_bytes; i++) + { + v = *dp++ = + (png_byte)(((int)*rp++ - (((int)*pp++ + (int)*lp++) / 2)) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_NONE) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_AVG]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + mins = sum; + best_row = png_ptr->avg_row; + } + } + + /* Paeth filter */ + if (filter_to_do == PNG_FILTER_PAETH) + { + png_bytep rp, dp, pp, cp, lp; + png_uint_32 i; + for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + } + + for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++) + { + int a, b, c, pa, pb, pc, p; + + b = *pp++; + c = *cp++; + a = *lp++; + + p = b - c; + pc = a - c; + +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; + + *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); + } + best_row = png_ptr->paeth_row; + } + + else if (filter_to_do & PNG_FILTER_PAETH) + { + png_bytep rp, dp, pp, cp, lp; + png_uint_32 sum = 0, lmins = mins; + png_uint_32 i; + int v; + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 lmhi, lmlo; + lmlo = lmins & PNG_LOMASK; + lmhi = (lmins >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH) + { + lmlo = (lmlo * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + lmlo = (lmlo * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + lmhi = (lmhi * png_ptr->inv_filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + + if (lmhi > PNG_HIMASK) + lmins = PNG_MAXSUM; + else + lmins = (lmhi << PNG_HISHIFT) + lmlo; + } +#endif + + for (i = 0, rp = row_buf + 1, dp = png_ptr->paeth_row + 1, + pp = prev_row + 1; i < bpp; i++) + { + v = *dp++ = (png_byte)(((int)*rp++ - (int)*pp++) & 0xff); + + sum += (v < 128) ? v : 256 - v; + } + + for (lp = row_buf + 1, cp = prev_row + 1; i < row_bytes; i++) + { + int a, b, c, pa, pb, pc, p; + + b = *pp++; + c = *cp++; + a = *lp++; + +#ifndef PNG_SLOW_PAETH + p = b - c; + pc = a - c; +#ifdef PNG_USE_ABS + pa = abs(p); + pb = abs(pc); + pc = abs(p + pc); +#else + pa = p < 0 ? -p : p; + pb = pc < 0 ? -pc : pc; + pc = (p + pc) < 0 ? -(p + pc) : p + pc; +#endif + p = (pa <= pb && pa <=pc) ? a : (pb <= pc) ? b : c; +#else /* PNG_SLOW_PAETH */ + p = a + b - c; + pa = abs(p - a); + pb = abs(p - b); + pc = abs(p - c); + if (pa <= pb && pa <= pc) + p = a; + else if (pb <= pc) + p = b; + else + p = c; +#endif /* PNG_SLOW_PAETH */ + + v = *dp++ = (png_byte)(((int)*rp++ - p) & 0xff); + + sum += (v < 128) ? v : 256 - v; + + if (sum > lmins) /* We are already worse, don't continue. */ + break; + } + +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + if (png_ptr->heuristic_method == PNG_FILTER_HEURISTIC_WEIGHTED) + { + int j; + png_uint_32 sumhi, sumlo; + sumlo = sum & PNG_LOMASK; + sumhi = (sum >> PNG_HISHIFT) & PNG_HIMASK; + + for (j = 0; j < num_p_filters; j++) + { + if (png_ptr->prev_filters[j] == PNG_FILTER_VALUE_PAETH) + { + sumlo = (sumlo * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + sumhi = (sumhi * png_ptr->filter_weights[j]) >> + PNG_WEIGHT_SHIFT; + } + } + + sumlo = (sumlo * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + sumhi = (sumhi * png_ptr->filter_costs[PNG_FILTER_VALUE_PAETH]) >> + PNG_COST_SHIFT; + + if (sumhi > PNG_HIMASK) + sum = PNG_MAXSUM; + else + sum = (sumhi << PNG_HISHIFT) + sumlo; + } +#endif + + if (sum < mins) + { + best_row = png_ptr->paeth_row; + } + } +#endif /* PNG_NO_WRITE_FILTER */ + /* Do the actual writing of the filtered row data from the chosen filter. */ + + png_write_filtered_row(png_ptr, best_row); + +#ifndef PNG_NO_WRITE_FILTER +#if defined(PNG_WRITE_WEIGHTED_FILTER_SUPPORTED) + /* Save the type of filter we picked this time for future calculations */ + if (png_ptr->num_prev_filters > 0) + { + int j; + for (j = 1; j < num_p_filters; j++) + { + png_ptr->prev_filters[j] = png_ptr->prev_filters[j - 1]; + } + png_ptr->prev_filters[j] = best_row[0]; + } +#endif +#endif /* PNG_NO_WRITE_FILTER */ +} + + +/* Do the actual writing of a previously filtered row. */ +void /* PRIVATE */ +png_write_filtered_row(png_structp png_ptr, png_bytep filtered_row) +{ + png_debug(1, "in png_write_filtered_row\n"); + png_debug1(2, "filter = %d\n", filtered_row[0]); + /* set up the zlib input buffer */ + + png_ptr->zstream.next_in = filtered_row; + png_ptr->zstream.avail_in = (uInt)png_ptr->row_info.rowbytes + 1; + /* repeat until we have compressed all the data */ + do + { + int ret; /* return of zlib */ + + /* compress the data */ + ret = deflate(&png_ptr->zstream, Z_NO_FLUSH); + /* check for compression errors */ + if (ret != Z_OK) + { + if (png_ptr->zstream.msg != NULL) + png_error(png_ptr, png_ptr->zstream.msg); + else + png_error(png_ptr, "zlib error"); + } + + /* see if it is time to write another IDAT */ + if (!(png_ptr->zstream.avail_out)) + { + /* write the IDAT and reset the zlib output buffer */ + png_write_IDAT(png_ptr, png_ptr->zbuf, png_ptr->zbuf_size); + png_ptr->zstream.next_out = png_ptr->zbuf; + png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size; + } + /* repeat until all data has been compressed */ + } while (png_ptr->zstream.avail_in); + + /* swap the current and previous rows */ + if (png_ptr->prev_row != NULL) + { + png_bytep tptr; + + tptr = png_ptr->prev_row; + png_ptr->prev_row = png_ptr->row_buf; + png_ptr->row_buf = tptr; + } + + /* finish row - updates counters and flushes zlib if last row */ + png_write_finish_row(png_ptr); + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) + png_ptr->flush_rows++; + + if (png_ptr->flush_dist > 0 && + png_ptr->flush_rows >= png_ptr->flush_dist) + { + png_write_flush(png_ptr); + } +#endif +} +#endif /* PNG_WRITE_SUPPORTED */ diff --git a/Engine/lib/lpng/projects/beos/x86-shared.proj b/Engine/lib/lpng/projects/beos/x86-shared.proj new file mode 100644 index 000000000..6d2e3c319 Binary files /dev/null and b/Engine/lib/lpng/projects/beos/x86-shared.proj differ diff --git a/Engine/lib/lpng/projects/beos/x86-shared.txt b/Engine/lib/lpng/projects/beos/x86-shared.txt new file mode 100644 index 000000000..0cd4d9db4 --- /dev/null +++ b/Engine/lib/lpng/projects/beos/x86-shared.txt @@ -0,0 +1,22 @@ +This project builds a shared library version of libpng on x86 BeOS. + +It defines PNG_USE_PNGGCCRD, which activates the assembly code in +pnggccrd.c; this hasn't been extensively tested on BeOS. + +To install: + +1) build + + Note: As of version 1.0.10, you'll get a fair number of warnings when + you compile pnggccrd.c. As far as I know, these are harmless, + but it would be better if someone fixed them. + +2) copy and png.h, pngconf.h somewhere; /boot/home/config/include (which + you'll have to make) is a good choice + +3) copy libpng.so to /boot/home/config/lib + +4) build your libpng.so applications (remember to include libz.a as + well when you link) + +- Chris Herborth, March 27, 2001 diff --git a/Engine/lib/lpng/projects/beos/x86-static.proj b/Engine/lib/lpng/projects/beos/x86-static.proj new file mode 100644 index 000000000..37c075366 Binary files /dev/null and b/Engine/lib/lpng/projects/beos/x86-static.proj differ diff --git a/Engine/lib/lpng/projects/beos/x86-static.txt b/Engine/lib/lpng/projects/beos/x86-static.txt new file mode 100644 index 000000000..bb80aaa5f --- /dev/null +++ b/Engine/lib/lpng/projects/beos/x86-static.txt @@ -0,0 +1,22 @@ +This project builds a static library version of libpng on x86 BeOS. + +It defines PNG_USE_PNGGCCRD, which activates the assembly code in +pnggccrd.c; this hasn't been extensively tested on BeOS. + +To install: + +1) build + + Note: As of version 1.0.10, you'll get a fair number of warnings when + you compile pnggccrd.c. As far as I know, these are harmless, + but it would be better if someone fixed them. + +2) copy and png.h, pngconf.h somewhere; /boot/home/config/include (which + you'll have to make) is a good choice + +3) copy libpng.a to /boot/home/config/lib + +4) build your libpng.a applications (remember to include libz.a as + well when you link) + +- Chris Herborth, March 27, 2001 diff --git a/Engine/lib/lpng/projects/cbuilder5/libpng.bpf b/Engine/lib/lpng/projects/cbuilder5/libpng.bpf new file mode 100644 index 000000000..2af81e77c --- /dev/null +++ b/Engine/lib/lpng/projects/cbuilder5/libpng.bpf @@ -0,0 +1,22 @@ +USEUNIT("libpng.cpp"); +USEUNIT("..\..\png.c"); +USEUNIT("..\..\pngerror.c"); +USEUNIT("..\..\pngget.c"); +USEUNIT("..\..\pngmem.c"); +USEUNIT("..\..\pngpread.c"); +USEUNIT("..\..\pngread.c"); +USEUNIT("..\..\pngrio.c"); +USEUNIT("..\..\pngrtran.c"); +USEUNIT("..\..\pngrutil.c"); +USEUNIT("..\..\pngset.c"); +USEUNIT("..\..\pngtrans.c"); +USEUNIT("..\..\pngwio.c"); +USEUNIT("..\..\pngwrite.c"); +USEUNIT("..\..\pngwtran.c"); +USEUNIT("..\..\pngwutil.c"); +USELIB("..\..\..\zlib\zlib.lib"); +//--------------------------------------------------------------------------- +This file is used by the project manager only and should be treated like the project file + + +DllEntryPoint diff --git a/Engine/lib/lpng/projects/cbuilder5/libpng.bpg b/Engine/lib/lpng/projects/cbuilder5/libpng.bpg new file mode 100644 index 000000000..80c197719 --- /dev/null +++ b/Engine/lib/lpng/projects/cbuilder5/libpng.bpg @@ -0,0 +1,25 @@ +#------------------------------------------------------------------------------ +VERSION = BWS.01 +#------------------------------------------------------------------------------ +!ifndef ROOT +ROOT = $(MAKEDIR)\.. +!endif +#------------------------------------------------------------------------------ +MAKE = $(ROOT)\bin\make.exe -$(MAKEFLAGS) -f$** +DCC = $(ROOT)\bin\dcc32.exe $** +BRCC = $(ROOT)\bin\brcc32.exe $** +#------------------------------------------------------------------------------ +PROJECTS = libpngstat.lib libpng.dll +#------------------------------------------------------------------------------ +default: $(PROJECTS) +#------------------------------------------------------------------------------ + +libpngstat.lib: libpngstat.bpr + $(ROOT)\bin\bpr2mak -t$(ROOT)\bin\deflib.bmk $** + $(ROOT)\bin\make -$(MAKEFLAGS) -f$*.mak + +libpng.dll: libpng.bpr + $(ROOT)\bin\bpr2mak $** + $(ROOT)\bin\make -$(MAKEFLAGS) -f$*.mak + + diff --git a/Engine/lib/lpng/projects/cbuilder5/libpng.bpr b/Engine/lib/lpng/projects/cbuilder5/libpng.bpr new file mode 100644 index 000000000..b9fdbe4d0 --- /dev/null +++ b/Engine/lib/lpng/projects/cbuilder5/libpng.bpr @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +[Version Info] +IncludeVerInfo=0 +AutoIncBuild=0 +MajorVer=1 +MinorVer=0 +Release=0 +Build=0 +Debug=0 +PreRelease=0 +Special=0 +Private=0 +DLL=1 +Locale=2057 +CodePage=1252 + +[Version Info Keys] +CompanyName= +FileDescription= +FileVersion=1.0.0.0 +InternalName= +LegalCopyright= +LegalTrademarks= +OriginalFilename= +ProductName= +ProductVersion= +Comments= + +[HistoryLists\hlIncludePath] +Count=18 +Item0=..\..;..\..\..\zlib;$(BCB)\include +Item1=..\..;P:\My Documents\Source\PNG+ZLib\zlib;$(BCB)\include +Item2=..\..;..\Source\ThirdParty\PortableNetworkGraphics\external;..\Source\ThirdParty\PortableNetworkGraphics;..\Source\ThirdParty\ZLibCompression\external;$(BCB)\include +Item3=..\Source\ThirdParty\PortableNetworkGraphics\external;..\Source\ThirdParty\PortableNetworkGraphics;..\Source\ThirdParty\ZLibCompression\external;$(BCB)\include +Item4=..\Source\ThirdParty\PortableNetworkGraphics\external;..\Source\ThirdParty\PortableNetworkGraphics;..\Source\ThirdParty\ZLibCompression;$(BCB)\include +Item5=..\Source\ThirdParty\PortableNetworkGraphics;..\Source\ThirdParty\ZLibCompression;$(BCB)\include +Item6=..\Source\ThirdParty\PortableNetworkGraphics;P:\Development\Source\ThirdParty\ZLibCompression;$(BCB)\include +Item7=..\Source\ThirdParty\PortableNetworkGraphics;$(BCB)\include +Item8=$(BCB)\include +Item9=..\Source;..\Source\General\Templates;..\Source\SIMUtilities;$(BCB)\include;$(BCB)\include\vcl +Item10=P:\Development\Source\;P:\Development\Source\General\Templates\;P:\Development\Source\SIMUtilities\;$(BCB)\include;$(BCB)\include\vcl +Item11=P:\Development\Source;P:\Development\Source\General\Templates\;P:\Development\Source\SIMUtilities\;$(BCB)\include;$(BCB)\include\vcl +Item12=P:\Development\Source\General\Templates\;P:\Development\Source\SIMUtilities\;$(BCB)\include;$(BCB)\include\vcl +Item13=P:\Development\Source\General\Templates\;P:\Development\Source\SIMUtilities;$(BCB)\include;$(BCB)\include\vcl +Item14=P:\Development\Source\General\Templates\;$(BCB)\include;$(BCB)\include\vcl +Item15=P:\Development\Source\General\Templates;$(BCB)\include;$(BCB)\include\vcl +Item16=P:\Development\Source;$(BCB)\include;$(BCB)\include\vcl +Item17=$(BCB)\include;$(BCB)\include\vcl + +[HistoryLists\hlLibraryPath] +Count=10 +Item0=..\..;$(BCB)\lib\obj;$(BCB)\lib +Item1=..\..;..\Source\ThirdParty\PortableNetworkGraphics\external;..\Source\ThirdParty\PortableNetworkGraphics;$(BCB)\lib\obj;$(BCB)\lib +Item2=..\Source\ThirdParty\PortableNetworkGraphics\external;..\Source\ThirdParty\PortableNetworkGraphics;$(BCB)\lib\obj;$(BCB)\lib +Item3=..\Source\ThirdParty\PortableNetworkGraphics;$(BCB)\lib\obj;$(BCB)\lib +Item4=$(BCB)\lib\obj;$(BCB)\lib +Item5=..\Source\SIMUtilities;..\Source;$(BCB)\lib\obj;$(BCB)\lib +Item6=P:\Development\Source\SIMUtilities\;P:\Development\Source\;$(BCB)\lib\obj;$(BCB)\lib +Item7=P:\Development\Source\SIMUtilities;P:\Development\Source\;$(BCB)\lib\obj;$(BCB)\lib +Item8=P:\Development\Source\;$(BCB)\lib\obj;$(BCB)\lib +Item9=P:\Development\Source;$(BCB)\lib\obj;$(BCB)\lib + +[HistoryLists\hlDebugSourcePath] +Count=1 +Item0=$(BCB)\source\vcl + +[HistoryLists\hlConditionals] +Count=20 +Item0=ZLIB_DLL;Z_PREFIX;PNG_BUILD_DLL;PNG_NO_MODULEDEF +Item1=_DEBUG;ZLIB_DLL;Z_PREFIX;PNG_BUILD_DLL;PNG_NO_MODULEDEF +Item2=PNG_BUILD_DLL;ZLIB_DLL;_DEBUG;PNG_NO_MODULEDEF +Item3=PNG_BUILD_DLL;ZLIB_DLL;_DEBUG;PNG_DEBUG=5;PNG_NO_MODULEDEF;PNG_NO_GLOBAL_ARRAYS +Item4=PNG_BUILD_DLL;ZLIB_DLL;_DEBUG;PNG_DEBUG=5;PNG_NO_MODULEDEF;PNG_SETJMP_NOT_SUPPORTED;PNG_DEBUG_FILE=stderr +Item5=PNG_BUILD_DLL;ZLIB_DLL;_DEBUG;PNG_DEBUG;PNG_NO_MODULEDEF;PNG_SETJMP_NOT_SUPPORTED +Item6=PNG_BUILD_DLL;ZLIB_DLL;_DEBUG;PNG_DEBUG=5;PNG_NO_MODULEDEF;PNG_SETJMP_NOT_SUPPORTED +Item7=PNG_BUILD_DLL;ZLIB_DLL;_DEBUG;PNG_DEBUG=5;PNG_NO_MODULEDEF +Item8=PNG_BUILD_DLL;ZLIB_DLL;_DEBUG;PNG_DEBUG=5 +Item9=PNG_BUILD_DLL;ZLIB_DLL;_DEBUG +Item10=PNG_BUILD_DLL;ZLIB_DLL +Item11=PNG_BUILD_DLL +Item12=PNG_DLL;PNG_BUILD_DLL;ZLIB_DLL +Item13=PNG_DLL;PNG_BUILD_DLL;PNG_NO_GLOBAL_ARRAYS;ZLIB_DLL +Item14=PNG_DLL;PNG_BUILD_DLL;PNG_NO_GLOBAL_ARRAYS +Item15=PNG_DLL;PNG_BUILD_DLL +Item16=PNG_DLL;PNG_BUILD_DLL;PNG_MODULEDEF +Item17=_HTML_FORM +Item18=_DEBUG;_HTML_FORM +Item19=_DEBUG + +[HistoryLists\hlIntOutputDir] +Count=2 +Item0=..\Obj +Item1=P:\Development\Obj + +[Debugging] +DebugSourceDirs= + +[Parameters] +RunParams= +HostApplication=P:\Development\Executables\LibPNGTestApp.exe +RemoteHost= +RemotePath= +RemoteDebug=0 + +[Compiler] +ShowInfoMsgs=0 +LinkDebugVcl=0 +LinkCGLIB=0 + + \ No newline at end of file diff --git a/Engine/lib/lpng/projects/cbuilder5/libpng.cpp b/Engine/lib/lpng/projects/cbuilder5/libpng.cpp new file mode 100644 index 000000000..4e2f274d4 --- /dev/null +++ b/Engine/lib/lpng/projects/cbuilder5/libpng.cpp @@ -0,0 +1,29 @@ +//--------------------------------------------------------------------------- +#include +//--------------------------------------------------------------------------- +// Important note about DLL memory management when your DLL uses the +// static version of the RunTime Library: +// +// If your DLL exports any functions that pass String objects (or structs/ +// classes containing nested Strings) as parameter or function results, +// you will need to add the library MEMMGR.LIB to both the DLL project and +// any other projects that use the DLL. You will also need to use MEMMGR.LIB +// if any other projects which use the DLL will be performing new or delete +// operations on any non-TObject-derived classes which are exported from the +// DLL. Adding MEMMGR.LIB to your project will change the DLL and its calling +// EXE's to use the BORLNDMM.DLL as their memory manager. In these cases, +// the file BORLNDMM.DLL should be deployed along with your DLL. +// +// To avoid using BORLNDMM.DLL, pass string information using "char *" or +// ShortString parameters. +// +// If your DLL uses the dynamic version of the RTL, you do not need to +// explicitly add MEMMGR.LIB as this will be done implicitly for you +//--------------------------------------------------------------------------- + +int WINAPI DllEntryPoint(HINSTANCE, unsigned long, void*) +{ + return 1; +} +//--------------------------------------------------------------------------- + \ No newline at end of file diff --git a/Engine/lib/lpng/projects/cbuilder5/libpng.readme.txt b/Engine/lib/lpng/projects/cbuilder5/libpng.readme.txt new file mode 100644 index 000000000..c99c1e9a7 --- /dev/null +++ b/Engine/lib/lpng/projects/cbuilder5/libpng.readme.txt @@ -0,0 +1,25 @@ +Project files to build libpng using Borland C++ Builder v5.0 + +In order to build and use libpng, please follow these steps: + + 1). Install zlib in a directory at the same level with libpng. + + 2). In a console window, go to the zlib directory and type: + make -f win32\Makefile.bor + After performing this step, you should have a file named + zlib.lib in the zlib directory. + + 3). Add the following conditional define to your project: + PNG_USE_DLL + + 4). Add libpng.lib or libpngstat.lib to the project. + Build the project. + + 5). If the build fails, add the paths to png.h and zlib.h to + your include path, and restart the build. + +By default, the libpng project uses zlib as a static library. If +you wish to use zlib as a DLL, please read the important notes from +the zlib DLL FAQ, found inside the zlib distribution. + +See the libpng documentation for instructions on how to use the code. diff --git a/Engine/lib/lpng/projects/cbuilder5/libpngstat.bpf b/Engine/lib/lpng/projects/cbuilder5/libpngstat.bpf new file mode 100644 index 000000000..fbc5c406c --- /dev/null +++ b/Engine/lib/lpng/projects/cbuilder5/libpngstat.bpf @@ -0,0 +1,22 @@ +USEUNIT("..\..\png.c"); +USEUNIT("..\..\pngerror.c"); +USEUNIT("..\..\pngget.c"); +USEUNIT("..\..\pngmem.c"); +USEUNIT("..\..\pngpread.c"); +USEUNIT("..\..\pngread.c"); +USEUNIT("..\..\pngrio.c"); +USEUNIT("..\..\pngrtran.c"); +USEUNIT("..\..\pngrutil.c"); +USEUNIT("..\..\pngset.c"); +USEUNIT("..\..\pngtrans.c"); +USEUNIT("..\..\pngwio.c"); +USEUNIT("..\..\pngwrite.c"); +USEUNIT("..\..\pngwtran.c"); +USEUNIT("..\..\pngwutil.c"); +USELIB("..\..\..\zlib\zlib.lib"); +//--------------------------------------------------------------------------- +#define Library + +// To add a file to the library use the Project menu 'Add to Project'. + + \ No newline at end of file diff --git a/Engine/lib/lpng/projects/cbuilder5/libpngstat.bpr b/Engine/lib/lpng/projects/cbuilder5/libpngstat.bpr new file mode 100644 index 000000000..9a51ecc88 --- /dev/null +++ b/Engine/lib/lpng/projects/cbuilder5/libpngstat.bpr @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +[Version Info] +IncludeVerInfo=0 +AutoIncBuild=0 +MajorVer=1 +MinorVer=0 +Release=0 +Build=0 +Debug=0 +PreRelease=0 +Special=0 +Private=0 +DLL=0 +Locale=2057 +CodePage=1252 + +[Version Info Keys] +CompanyName= +FileDescription= +FileVersion=1.0.0.0 +InternalName= +LegalCopyright= +LegalTrademarks= +OriginalFilename= +ProductName= +ProductVersion=1.0.0.0 +Comments= + +[HistoryLists\hlIncludePath] +Count=2 +Item0=..\..;P:\My Documents\Source\PNG+ZLib\zlib;$(BCB)\include +Item1=..\..;$(BCB)\include;$(BCB)\include\vcl + +[HistoryLists\hlLibraryPath] +Count=1 +Item0=..\..;$(BCB)\lib\obj;$(BCB)\lib + +[HistoryLists\hlDebugSourcePath] +Count=1 +Item0=$(BCB)\source\vcl + +[HistoryLists\hlConditionals] +Count=1 +Item0=_DEBUG + +[HistoryLists\hlTlibPageSize] +Count=1 +Item0=0x0010 + +[Debugging] +DebugSourceDirs=$(BCB)\source\vcl + +[Parameters] +RunParams= +HostApplication= +RemoteHost= +RemotePath= +RemoteDebug=0 + +[Compiler] +ShowInfoMsgs=0 +LinkDebugVcl=0 +LinkCGLIB=0 + +[Language] +ActiveLang= +ProjectLang= +RootDir= + + \ No newline at end of file diff --git a/Engine/lib/lpng/projects/cbuilder5/zlib.readme.txt b/Engine/lib/lpng/projects/cbuilder5/zlib.readme.txt new file mode 100644 index 000000000..cb6a7a81e --- /dev/null +++ b/Engine/lib/lpng/projects/cbuilder5/zlib.readme.txt @@ -0,0 +1,14 @@ +The project that builds libpng under Borland C++ Builder does not +explicitly build zlib. By taking this decision, there is no need +to update the libpng project each time when there is a change in +the list of zlib source files. After all, this list is private to +zlib, and applications (such as libpng) should not assume anything +about it. + +If you wish to contribute a project that builds zlib under Borland +C++ Builder, please submit it to the zlib developers, not to the +libpng developers. + +By default, the libpng project uses zlib as a static library. If +you wish to use zlib as a DLL, please read the important notes from +the zlib DLL FAQ, found inside the zlib distribution. diff --git a/Engine/lib/lpng/projects/netware.txt b/Engine/lib/lpng/projects/netware.txt new file mode 100644 index 000000000..178361da8 --- /dev/null +++ b/Engine/lib/lpng/projects/netware.txt @@ -0,0 +1,6 @@ +A set of project files is available for Netware. Get +libpng-1.2.5-project-netware.zip from a libpng distribution +site such as http://libpng.sourceforge.net + +Put the zip file in this directory (projects) and then run +"unzip -a libpng-1.2.5-project-netware.zip" diff --git a/Engine/lib/lpng/projects/visualc6/README.txt b/Engine/lib/lpng/projects/visualc6/README.txt new file mode 100644 index 000000000..d34980d57 --- /dev/null +++ b/Engine/lib/lpng/projects/visualc6/README.txt @@ -0,0 +1,57 @@ +Microsoft Developer Studio Project File, Format Version 6.00 for libpng. + +Copyright (C) 2000-2004 Simon-Pierre Cadieux. +Copyright (C) 2004 Cosmin Truta. +For conditions of distribution and use, see copyright notice in png.h + + +Assumptions: +* The libpng source files are in ..\.. +* The zlib source files are in ..\..\..\zlib +* The zlib project files are in ..\..\..\zlib\projects\visualc6 + + +To use: + +1) On the main menu, select "File | Open Workspace". + Open "libpng.dsw". + +2) Select "Build | Set Active Configuration". + Choose the configuration you wish to build. + (Choose libpng or pngtest; zlib will be built automatically.) + +3) Select "Build | Clean". + +4) Select "Build | Build ... (F7)". Ignore warning messages about + not being able to find certain include files (e.g. alloc.h). + +5) If you built the sample program (pngtest), + select "Build | Execute ... (Ctrl+F5)". + + +This project builds the libpng binaries as follows: + +* Win32_DLL_Release\libpng13.dll DLL build +* Win32_DLL_Debug\libpng13d.dll DLL build (debug version) +* Win32_DLL_ASM_Release\libpng13.dll DLL build using ASM code +* Win32_DLL_ASM_Debug\libpng13d.dll DLL build using ASM (debug version) +* Win32_DLL_VB\libpng13vb.dll DLL build for Visual Basic, using stdcall +* Win32_LIB_Release\libpng.lib static build +* Win32_LIB_Debug\libpngd.lib static build (debug version) +* Win32_LIB_ASM_Release\libpng.lib static build using ASM code +* Win32_LIB_ASM_Debug\libpngd.lib static build using ASM (debug version) + + +Notes: + +If you change anything in the source files, or select different compiler +settings, please change the DLL name to something different than any of +the above names. Also, make sure that in your "pngusr.h" you define +PNG_USER_PRIVATEBUILD and PNG_USER_DLLFNAME_POSTFIX according to the +instructions provided in "pngconf.h". + +All DLLs built by this project use the Microsoft dynamic C runtime library +MSVCRT.DLL (MSVCRTD.DLL for debug versions). If you distribute any of the +above mentioned libraries you should also include this DLL in your package. +For a list of files that are redistributable in Visual C++ 6.0, see +Common\Redist\Redist.txt on Disc 1 of the Visual C++ 6.0 product CDs. diff --git a/Engine/lib/lpng/projects/visualc6/libpng.dsp b/Engine/lib/lpng/projects/visualc6/libpng.dsp new file mode 100644 index 000000000..2a77cc30b --- /dev/null +++ b/Engine/lib/lpng/projects/visualc6/libpng.dsp @@ -0,0 +1,472 @@ +# Microsoft Developer Studio Project File - Name="libpng" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=libpng - Win32 DLL Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "libpng.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "libpng.mak" CFG="libpng - Win32 DLL Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "libpng - Win32 DLL Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "libpng - Win32 DLL Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "libpng - Win32 DLL ASM Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "libpng - Win32 DLL ASM Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "libpng - Win32 DLL VB" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "libpng - Win32 LIB Release" (based on "Win32 (x86) Static Library") +!MESSAGE "libpng - Win32 LIB Debug" (based on "Win32 (x86) Static Library") +!MESSAGE "libpng - Win32 LIB ASM Release" (based on "Win32 (x86) Static Library") +!MESSAGE "libpng - Win32 LIB ASM Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" + +!IF "$(CFG)" == "libpng - Win32 DLL Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "libpng___Win32_DLL_Release" +# PROP BASE Intermediate_Dir "libpng___Win32_DLL_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Win32_DLL_Release" +# PROP Intermediate_Dir "Win32_DLL_Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +CPP=cl.exe +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /FD /c +# SUBTRACT BASE CPP /YX /Yc /Yu +# ADD CPP /nologo /MD /W3 /O2 /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "NDEBUG" /D "PNG_BUILD_DLL" /D "PNG_NO_MMX_CODE" /D "ZLIB_DLL" /D "_CRT_SECURE_NO_WARNINGS" /FD /c +# SUBTRACT CPP /YX /Yc /Yu +MTL=midl.exe +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +RSC=rc.exe +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /i "..\.." /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /dll /machine:I386 +# ADD LINK32 zlib1.lib /nologo /dll /machine:I386 /out:"Win32_DLL_Release\libpng13.dll" /libpath:"..\..\..\zlib\projects\visualc6\Win32_DLL_Release" + +!ELSEIF "$(CFG)" == "libpng - Win32 DLL Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "libpng___Win32_DLL_Release" +# PROP BASE Intermediate_Dir "libpng___Win32_DLL_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Win32_DLL_Debug" +# PROP Intermediate_Dir "Win32_DLL_Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +CPP=cl.exe +# ADD BASE CPP /nologo /MDd /W3 /Gm /ZI /Od /D "WIN32" /D "_DEBUG" /FD /GZ /c +# SUBTRACT BASE CPP /YX /Yc /Yu +# ADD CPP /nologo /MDd /W3 /Gm /ZI /Od /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "_DEBUG" /D "DEBUG" /D "PNG_NO_MMX_CODE" /D PNG_DEBUG=1 /D "PNG_BUILD_DLL" /D "ZLIB_DLL" /D "_CRT_SECURE_NO_WARNINGS" /FD /GZ /c +# SUBTRACT CPP /YX /Yc /Yu +MTL=midl.exe +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +RSC=rc.exe +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /i "..\.." /d "_DEBUG" /d PNG_DEBUG=1 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 zlib1d.lib /nologo /dll /debug /machine:I386 /out:"Win32_DLL_Debug\libpng13d.dll" /libpath:"..\..\..\zlib\projects\visualc6\Win32_DLL_Debug" + +!ELSEIF "$(CFG)" == "libpng - Win32 DLL ASM Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "libpng___Win32_DLL_ASM_Release" +# PROP BASE Intermediate_Dir "libpng___Win32_DLL_ASM_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Win32_DLL_ASM_Release" +# PROP Intermediate_Dir "Win32_DLL_ASM_Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +CPP=cl.exe +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /FD /c +# SUBTRACT BASE CPP /YX /Yc /Yu +# ADD CPP /nologo /MD /W3 /O2 /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "NDEBUG" /D "PNG_USE_PNGVCRD" /D "PNG_BUILD_DLL" /D "ZLIB_DLL" /D "PNG_LIBPNG_SPECIALBUILD" /D "_CRT_SECURE_NO_WARNINGS" /FD /c +# SUBTRACT CPP /YX /Yc /Yu +MTL=midl.exe +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +RSC=rc.exe +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /i "..\.." /d "NDEBUG" /d PNG_LIBPNG_SPECIALBUILD=""""Use MMX instructions"""" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /dll /machine:I386 +# ADD LINK32 zlib1.lib /nologo /dll /machine:I386 /out:"Win32_DLL_ASM_Release\libpng13.dll" /libpath:"..\..\..\zlib\projects\visualc6\Win32_DLL_ASM_Release" + +!ELSEIF "$(CFG)" == "libpng - Win32 DLL ASM Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "libpng___Win32_DLL_ASM_Debug" +# PROP BASE Intermediate_Dir "libpng___Win32_DLL_ASM_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Win32_DLL_ASM_Debug" +# PROP Intermediate_Dir "Win32_DLL_ASM_Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +CPP=cl.exe +# ADD BASE CPP /nologo /MDd /W3 /Gm /ZI /Od /D "WIN32" /D "_DEBUG" /FD /GZ /c +# SUBTRACT BASE CPP /YX /Yc /Yu +# ADD CPP /nologo /MDd /W3 /Gm /ZI /Od /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "_DEBUG" /D "DEBUG" /D PNG_DEBUG=1 /D "PNG_USE_PNGVCRD" /D "PNG_BUILD_DLL" /D "ZLIB_DLL" /D "PNG_LIBPNG_SPECIALBUILD" /D "_CRT_SECURE_NO_WARNINGS" /FD /GZ /c +# SUBTRACT CPP /YX /Yc /Yu +MTL=midl.exe +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +RSC=rc.exe +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /i "..\.." /d "_DEBUG" /d PNG_DEBUG=1 /d PNG_LIBPNG_SPECIALBUILD=""""Use MMX instructions"""" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 zlib1d.lib /nologo /dll /debug /machine:I386 /out:"Win32_DLL_ASM_Debug\libpng13d.dll" /libpath:"..\..\..\zlib\projects\visualc6\Win32_DLL_ASM_Debug" + +!ELSEIF "$(CFG)" == "libpng - Win32 DLL VB" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "libpng___Win32_DLL_VB" +# PROP BASE Intermediate_Dir "libpng___Win32_DLL_VB" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Win32_DLL_VB" +# PROP Intermediate_Dir "Win32_DLL_VB" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +CPP=cl.exe +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /FD /c +# SUBTRACT BASE CPP /YX /Yc /Yu +# ADD CPP /nologo /MD /W3 /O2 /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "NDEBUG" /D "PNG_BUILD_DLL" /D "ZLIB_DLL" /D PNGAPI=__stdcall /D "PNG_NO_MODULEDEF" /D "PNG_LIBPNG_SPECIALBUILD" /D "_CRT_SECURE_NO_WARNINGS" /FD /c +# SUBTRACT CPP /YX /Yc /Yu +MTL=midl.exe +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +RSC=rc.exe +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /i "..\.." /d "NDEBUG" /dPNG_LIBPNG_DLLFNAME_POSTFIX=""""VB"""" /dPNG_LIBPNG_SPECIALBUILD=""""__stdcall calling convention used for exported functions"""" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /dll /machine:I386 +# ADD LINK32 zlib1.lib /nologo /dll /machine:I386 /out:"Win32_DLL_VB\libpng13vb.dll" /libpath:"..\..\..\zlib\projects\visualc6\Win32_DLL_Release" +# Begin Special Build Tool +OutDir=.\Win32_DLL_VB +TargetName=libpng13vb +SOURCE="$(InputPath)" +PostBuild_Cmds=echo Deleting $(targetname) import library and export file (Not required for VB projects) del $(outdir)\$(targetname).lib del $(outdir)\$(targetname).exp +# End Special Build Tool + +!ELSEIF "$(CFG)" == "libpng - Win32 LIB Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "libpng___Win32_LIB_Release" +# PROP BASE Intermediate_Dir "libpng___Win32_LIB_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Win32_LIB_Release" +# PROP Intermediate_Dir "Win32_LIB_Release" +# PROP Target_Dir "" +CPP=cl.exe +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /FD /c +# SUBTRACT BASE CPP /YX /Yc /Yu +# ADD CPP /nologo /MD /W3 /O2 /I "..\.." /I "..\..\..\zlib" /D "PNG_NO_MMX_CODE" /D "WIN32" /D "NDEBUG" /D "_CRT_SECURE_NO_WARNINGS" /FD /c +# SUBTRACT CPP /YX /Yc /Yu +RSC=rc.exe +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /i "..\.." /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "libpng - Win32 LIB Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "libpng___Win32_LIB_Debug" +# PROP BASE Intermediate_Dir "libpng___Win32_LIB_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Win32_LIB_Debug" +# PROP Intermediate_Dir "Win32_LIB_Debug" +# PROP Target_Dir "" +CPP=cl.exe +# ADD BASE CPP /nologo /MDd /W3 /Gm /ZI /Od /D "WIN32" /D "_DEBUG" /FD /GZ /c +# SUBTRACT BASE CPP /YX /Yc /Yu +# ADD CPP /nologo /MDd /W3 /Gm /ZI /Od /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "_DEBUG" /D "DEBUG" /D "PNG_NO_MMX_CODE" /D PNG_DEBUG=1 /D "_CRT_SECURE_NO_WARNINGS" /FD /GZ /c +# SUBTRACT CPP /YX /Yc /Yu +RSC=rc.exe +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"Win32_LIB_Debug\libpngd.lib" + +!ELSEIF "$(CFG)" == "libpng - Win32 LIB ASM Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "libpng___Win32_LIB_ASM_Release" +# PROP BASE Intermediate_Dir "libpng___Win32_LIB_ASM_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Win32_LIB_ASM_Release" +# PROP Intermediate_Dir "Win32_LIB_ASM_Release" +# PROP Target_Dir "" +CPP=cl.exe +# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /FD /c +# SUBTRACT BASE CPP /YX /Yc /Yu +# ADD CPP /nologo /MD /W3 /O2 /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "NDEBUG" /D "PNG_USE_PNGVCRD" /D "PNG_LIBPNG_SPECIALBUILD" /D "_CRT_SECURE_NO_WARNINGS" /FD /c +# SUBTRACT CPP /YX /Yc /Yu +RSC=rc.exe +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /i "..\.." /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "libpng - Win32 LIB ASM Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "libpng___Win32_LIB_ASM_Debug" +# PROP BASE Intermediate_Dir "libpng___Win32_LIB_ASM_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Win32_LIB_ASM_Debug" +# PROP Intermediate_Dir "Win32_LIB_ASM_Debug" +# PROP Target_Dir "" +CPP=cl.exe +# ADD BASE CPP /nologo /MDd /W3 /Gm /ZI /Od /D "WIN32" /D "_DEBUG" /FD /GZ /c +# SUBTRACT BASE CPP /YX /Yc /Yu +# ADD CPP /nologo /MDd /W3 /Gm /ZI /Od /I "..\.." /I "..\..\..\zlib" /D "WIN32" /D "_DEBUG" /D "DEBUG" /D PNG_DEBUG=1 /D "PNG_USE_PNGVCRD" /D "PNG_LIBPNG_SPECIALBUILD" /D "_CRT_SECURE_NO_WARNINGS" /FD /GZ /c +# SUBTRACT CPP /YX /Yc /Yu +RSC=rc.exe +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"Win32_LIB_ASM_Debug\libpngd.lib" + +!ENDIF + +# Begin Target + +# Name "libpng - Win32 DLL Release" +# Name "libpng - Win32 DLL Debug" +# Name "libpng - Win32 DLL ASM Release" +# Name "libpng - Win32 DLL ASM Debug" +# Name "libpng - Win32 DLL VB" +# Name "libpng - Win32 LIB Release" +# Name "libpng - Win32 LIB Debug" +# Name "libpng - Win32 LIB ASM Release" +# Name "libpng - Win32 LIB ASM Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\png.c +# End Source File +# Begin Source File + +SOURCE=..\..\pngerror.c +# End Source File +# Begin Source File + +SOURCE=..\..\pngget.c +# End Source File +# Begin Source File + +SOURCE=..\..\pngmem.c +# End Source File +# Begin Source File + +SOURCE=..\..\pngpread.c +# End Source File +# Begin Source File + +SOURCE=..\..\pngread.c +# End Source File +# Begin Source File + +SOURCE=..\..\pngrio.c +# End Source File +# Begin Source File + +SOURCE=..\..\pngrtran.c +# End Source File +# Begin Source File + +SOURCE=..\..\pngrutil.c +# End Source File +# Begin Source File + +SOURCE=..\..\pngset.c +# End Source File +# Begin Source File + +SOURCE=..\..\pngtrans.c +# End Source File +# Begin Source File + +SOURCE=..\..\scripts\pngw32.def + +!IF "$(CFG)" == "libpng - Win32 DLL Release" + +!ELSEIF "$(CFG)" == "libpng - Win32 DLL Debug" + +!ELSEIF "$(CFG)" == "libpng - Win32 DLL ASM Release" + +!ELSEIF "$(CFG)" == "libpng - Win32 DLL ASM Debug" + +!ELSEIF "$(CFG)" == "libpng - Win32 DLL VB" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "libpng - Win32 LIB Release" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "libpng - Win32 LIB Debug" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "libpng - Win32 LIB ASM Release" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "libpng - Win32 LIB ASM Debug" + +# PROP Exclude_From_Build 1 + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\..\pngwio.c +# End Source File +# Begin Source File + +SOURCE=..\..\pngwrite.c +# End Source File +# Begin Source File + +SOURCE=..\..\pngwtran.c +# End Source File +# Begin Source File + +SOURCE=..\..\pngwutil.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\..\png.h +# End Source File +# Begin Source File + +SOURCE=..\..\pngconf.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=..\..\scripts\pngw32.rc + +!IF "$(CFG)" == "libpng - Win32 DLL Release" + +!ELSEIF "$(CFG)" == "libpng - Win32 DLL Debug" + +!ELSEIF "$(CFG)" == "libpng - Win32 DLL ASM Release" + +!ELSEIF "$(CFG)" == "libpng - Win32 DLL ASM Debug" + +!ELSEIF "$(CFG)" == "libpng - Win32 DLL VB" + +!ELSEIF "$(CFG)" == "libpng - Win32 LIB Release" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "libpng - Win32 LIB Debug" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "libpng - Win32 LIB ASM Release" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "libpng - Win32 LIB ASM Debug" + +# PROP Exclude_From_Build 1 + +!ENDIF + +# End Source File +# End Group +# Begin Source File + +SOURCE=.\README.txt +# End Source File +# End Target +# End Project diff --git a/Engine/lib/lpng/projects/visualc6/libpng.dsw b/Engine/lib/lpng/projects/visualc6/libpng.dsw new file mode 100644 index 000000000..4f3856fbb --- /dev/null +++ b/Engine/lib/lpng/projects/visualc6/libpng.dsw @@ -0,0 +1,59 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "libpng"=".\libpng.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name zlib + End Project Dependency +}}} + +############################################################################### + +Project: "pngtest"=".\pngtest.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ + Begin Project Dependency + Project_Dep_Name libpng + End Project Dependency +}}} + +############################################################################### + +Project: "zlib"="..\..\..\zlib\projects\visualc6\zlib.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/Engine/lib/lpng/projects/visualc6/pngtest.dsp b/Engine/lib/lpng/projects/visualc6/pngtest.dsp new file mode 100644 index 000000000..a1be73cd0 --- /dev/null +++ b/Engine/lib/lpng/projects/visualc6/pngtest.dsp @@ -0,0 +1,314 @@ +# Microsoft Developer Studio Project File - Name="pngtest" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=pngtest - Win32 DLL Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "pngtest.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "pngtest.mak" CFG="pngtest - Win32 DLL Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "pngtest - Win32 DLL Release" (based on "Win32 (x86) Console Application") +!MESSAGE "pngtest - Win32 DLL Debug" (based on "Win32 (x86) Console Application") +!MESSAGE "pngtest - Win32 DLL ASM Release" (based on "Win32 (x86) Console Application") +!MESSAGE "pngtest - Win32 DLL ASM Debug" (based on "Win32 (x86) Console Application") +!MESSAGE "pngtest - Win32 LIB Release" (based on "Win32 (x86) Console Application") +!MESSAGE "pngtest - Win32 LIB Debug" (based on "Win32 (x86) Console Application") +!MESSAGE "pngtest - Win32 LIB ASM Release" (based on "Win32 (x86) Console Application") +!MESSAGE "pngtest - Win32 LIB ASM Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "pngtest - Win32 DLL Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "pngtest___Win32_DLL_Release" +# PROP BASE Intermediate_Dir "pngtest___Win32_DLL_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Win32_DLL_Release" +# PROP Intermediate_Dir "Win32_DLL_Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /O2 /D "WIN32" /D "NDEBUG" /FD /c +# SUBTRACT BASE CPP /YX +# ADD CPP /nologo /MD /W3 /O2 /I "..\..\..\zlib" /D "WIN32" /D "NDEBUG" /D "PNG_DLL" /D "PNG_NO_STDIO" /D "PNG_NO_GLOBAL_ARRAYS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:console /machine:I386 +# ADD LINK32 Win32_DLL_Release\libpng13.lib ..\..\..\zlib\projects\visualc6\Win32_DLL_Release\zlib1.lib /nologo /subsystem:console /machine:I386 +# Begin Special Build Tool +OutDir=.\Win32_DLL_Release +SOURCE="$(InputPath)" +PostBuild_Desc=[Run Test] +PostBuild_Cmds=set path=$(outdir);..\..\..\zlib\projects\visualc6\Win32_DLL_Release; $(outdir)\pngtest.exe ..\..\pngtest.png +# End Special Build Tool + +!ELSEIF "$(CFG)" == "pngtest - Win32 DLL Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "pngtest___Win32_DLL_Debug" +# PROP BASE Intermediate_Dir "pngtest___Win32_DLL_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Win32_DLL_Debug" +# PROP Intermediate_Dir "Win32_DLL_Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /ZI /Od /D "WIN32" /D "_DEBUG" /FD /GZ /c +# SUBTRACT BASE CPP /YX +# ADD CPP /nologo /MDd /W3 /Gm /ZI /Od /I "..\..\..\zlib" /D "WIN32" /D "_DEBUG" /D "PNG_DLL" /D "PNG_NO_STDIO" /D "PNG_NO_GLOBAL_ARRAYS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 Win32_DLL_Debug\libpng13d.lib ..\..\..\zlib\projects\visualc6\Win32_DLL_Debug\zlib1d.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# Begin Special Build Tool +OutDir=.\Win32_DLL_Debug +SOURCE="$(InputPath)" +PostBuild_Desc=[Run Test] +PostBuild_Cmds=set path=$(outdir);..\..\..\zlib\projects\visualc6\Win32_DLL_Debug; $(outdir)\pngtest.exe ..\..\pngtest.png +# End Special Build Tool + +!ELSEIF "$(CFG)" == "pngtest - Win32 DLL ASM Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "pngtest___Win32_DLL_ASM_Release" +# PROP BASE Intermediate_Dir "pngtest___Win32_DLL_ASM_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Win32_DLL_ASM_Release" +# PROP Intermediate_Dir "Win32_DLL_ASM_Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /O2 /D "WIN32" /D "NDEBUG" /FD /c +# SUBTRACT BASE CPP /YX +# ADD CPP /nologo /MD /W3 /O2 /I "..\..\..\zlib" /D "WIN32" /D "NDEBUG" /D "PNG_DLL" /D "PNG_NO_STDIO" /D "PNG_NO_GLOBAL_ARRAYS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:console /machine:I386 +# ADD LINK32 Win32_DLL_ASM_Release\libpng13.lib ..\..\..\zlib\projects\visualc6\Win32_DLL_ASM_Release\zlib1.lib /nologo /subsystem:console /machine:I386 +# Begin Special Build Tool +OutDir=.\Win32_DLL_ASM_Release +SOURCE="$(InputPath)" +PostBuild_Desc=[Run Test] +PostBuild_Cmds=set path=$(outdir);..\..\..\zlib\projects\visualc6\Win32_DLL_ASM_Release; $(outdir)\pngtest.exe ..\..\pngtest.png +# End Special Build Tool + +!ELSEIF "$(CFG)" == "pngtest - Win32 DLL ASM Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "pngtest___Win32_DLL_ASM_Debug" +# PROP BASE Intermediate_Dir "pngtest___Win32_DLL_ASM_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Win32_DLL_ASM_Debug" +# PROP Intermediate_Dir "Win32_DLL_ASM_Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /ZI /Od /D "WIN32" /D "_DEBUG" /FD /GZ /c +# SUBTRACT BASE CPP /YX +# ADD CPP /nologo /MDd /W3 /Gm /ZI /Od /I "..\..\..\zlib" /D "WIN32" /D "_DEBUG" /D "PNG_DLL" /D "PNG_NO_STDIO" /D "PNG_NO_GLOBAL_ARRAYS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 Win32_DLL_ASM_Debug\libpng13d.lib ..\..\..\zlib\projects\visualc6\Win32_DLL_ASM_Debug\zlib1d.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# Begin Special Build Tool +OutDir=.\Win32_DLL_ASM_Debug +SOURCE="$(InputPath)" +PostBuild_Desc=[Run Test] +PostBuild_Cmds=set path=$(outdir);..\..\..\zlib\projects\visualc6\Win32_DLL_ASM_Debug; $(outdir)\pngtest.exe ..\..\pngtest.png +# End Special Build Tool + +!ELSEIF "$(CFG)" == "pngtest - Win32 LIB Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "pngtest___Win32_LIB_Release" +# PROP BASE Intermediate_Dir "pngtest___Win32_LIB_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Win32_LIB_Release" +# PROP Intermediate_Dir "Win32_LIB_Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /O2 /D "WIN32" /D "NDEBUG" /FD /c +# SUBTRACT BASE CPP /YX +# ADD CPP /nologo /MD /W3 /O2 /I "..\..\..\zlib" /D "WIN32" /D "NDEBUG" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:console /machine:I386 +# ADD LINK32 Win32_LIB_Release\libpng.lib ..\..\..\zlib\projects\visualc6\Win32_LIB_Release\zlib.lib /nologo /subsystem:console /machine:I386 +# Begin Special Build Tool +OutDir=.\Win32_LIB_Release +SOURCE="$(InputPath)" +PostBuild_Desc=[Run Test] +PostBuild_Cmds=$(outdir)\pngtest.exe ..\..\pngtest.png +# End Special Build Tool + +!ELSEIF "$(CFG)" == "pngtest - Win32 LIB Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "pngtest___Win32_LIB_Debug" +# PROP BASE Intermediate_Dir "pngtest___Win32_LIB_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Win32_LIB_Debug" +# PROP Intermediate_Dir "Win32_LIB_Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /ZI /Od /D "WIN32" /D "_DEBUG" /FD /GZ /c +# SUBTRACT BASE CPP /YX +# ADD CPP /nologo /MDd /W3 /Gm /ZI /Od /I "..\..\..\zlib" /D "WIN32" /D "_DEBUG" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 Win32_LIB_Debug\libpngd.lib ..\..\..\zlib\projects\visualc6\Win32_LIB_Debug\zlibd.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# Begin Special Build Tool +OutDir=.\Win32_LIB_Debug +SOURCE="$(InputPath)" +PostBuild_Desc=[Run Test] +PostBuild_Cmds=$(outdir)\pngtest.exe ..\..\pngtest.png +# End Special Build Tool + +!ELSEIF "$(CFG)" == "pngtest - Win32 LIB ASM Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "pngtest___Win32_LIB_ASM_Release" +# PROP BASE Intermediate_Dir "pngtest___Win32_LIB_ASM_Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Win32_LIB_ASM_Release" +# PROP Intermediate_Dir "Win32_LIB_ASM_Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /O2 /D "WIN32" /D "NDEBUG" /FD /c +# SUBTRACT BASE CPP /YX +# ADD CPP /nologo /MD /W3 /O2 /I "..\..\..\zlib" /D "WIN32" /D "NDEBUG" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:console /machine:I386 +# ADD LINK32 Win32_LIB_ASM_Release\libpng.lib ..\..\..\zlib\projects\visualc6\Win32_LIB_ASM_Release\zlib.lib /nologo /subsystem:console /machine:I386 +# Begin Special Build Tool +OutDir=.\Win32_LIB_ASM_Release +SOURCE="$(InputPath)" +PostBuild_Desc=[Run Test] +PostBuild_Cmds=$(outdir)\pngtest.exe ..\..\pngtest.png +# End Special Build Tool + +!ELSEIF "$(CFG)" == "pngtest - Win32 LIB ASM Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "pngtest___Win32_LIB_ASM_Debug" +# PROP BASE Intermediate_Dir "pngtest___Win32_LIB_ASM_Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Win32_LIB_ASM_Debug" +# PROP Intermediate_Dir "Win32_LIB_ASM_Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /ZI /Od /D "WIN32" /D "_DEBUG" /FD /GZ /c +# SUBTRACT BASE CPP /YX +# ADD CPP /nologo /MDd /W3 /Gm /ZI /Od /I "..\..\..\zlib" /D "WIN32" /D "_DEBUG" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 Win32_LIB_ASM_Debug\libpngd.lib ..\..\..\zlib\projects\visualc6\Win32_LIB_ASM_Debug\zlibd.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# Begin Special Build Tool +OutDir=.\Win32_LIB_ASM_Debug +SOURCE="$(InputPath)" +PostBuild_Desc=[Run Test] +PostBuild_Cmds=$(outdir)\pngtest.exe ..\..\pngtest.png +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "pngtest - Win32 DLL Release" +# Name "pngtest - Win32 DLL Debug" +# Name "pngtest - Win32 DLL ASM Release" +# Name "pngtest - Win32 DLL ASM Debug" +# Name "pngtest - Win32 LIB Release" +# Name "pngtest - Win32 LIB Debug" +# Name "pngtest - Win32 LIB ASM Release" +# Name "pngtest - Win32 LIB ASM Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\pngtest.c +# End Source File +# End Group +# End Target +# End Project diff --git a/Engine/lib/lpng/projects/visualc71/PRJ0041.mak b/Engine/lib/lpng/projects/visualc71/PRJ0041.mak new file mode 100644 index 000000000..3a597b023 --- /dev/null +++ b/Engine/lib/lpng/projects/visualc71/PRJ0041.mak @@ -0,0 +1,21 @@ +# Prevent "Cannot find missing dependency..." warnings while compiling +# pngw32.rc (PRJ0041). + +all: $(IntDir)\alloc.h \ + $(IntDir)\fp.h \ + $(IntDir)\m68881.h \ + $(IntDir)\mem.h \ + $(IntDir)\pngusr.h \ + $(IntDir)\strings.h \ + $(IntDir)\unistd.h \ + $(IntDir)\unixio.h + +$(IntDir)\alloc.h \ +$(IntDir)\fp.h \ +$(IntDir)\m68881.h \ +$(IntDir)\mem.h \ +$(IntDir)\pngusr.h \ +$(IntDir)\strings.h \ +$(IntDir)\unistd.h \ +$(IntDir)\unixio.h: + @!echo.>$@ diff --git a/Engine/lib/lpng/projects/visualc71/README.txt b/Engine/lib/lpng/projects/visualc71/README.txt new file mode 100644 index 000000000..7bd63327e --- /dev/null +++ b/Engine/lib/lpng/projects/visualc71/README.txt @@ -0,0 +1,57 @@ +Microsoft Developer Studio Project File, Format Version 7.10 for libpng. + +Copyright (C) 2004 Simon-Pierre Cadieux. +For conditions of distribution and use, see copyright notice in png.h + +Assumptions: +* The libpng source files are in ..\.. +* The zlib source files are in ..\..\..\zlib +* The zlib project file is in . /* Warning: This is until the zlib project + files get intergrated into the next zlib release. The final zlib project + directory will then be ..\..\..\zlib\projects\visualc71. */ + +To use: + +1) On the main menu, select "File | Open Solution". + Open "libpng.sln". + +2) Display the Solution Explorer view (Ctrl+Alt+L) + +3) Set one of the project as the StartUp project. If you just want to build the + binaries set "libpng" as the startup project (Select "libpng" tree view + item + Project | Set as StartUp project). If you want to build and test the + binaries set it to "pngtest" (Select "pngtest" tree view item + + Project | Set as StartUp project) + +4) Select "Build | Configuration Manager...". + Choose the configuration you wish to build. + +5) Select "Build | Clean Solution". + +6) Select "Build | Build Solution (Ctrl-Shift-B)" + +This project builds the libpng binaries as follows: + +* Win32_DLL_Release\libpng13.dll DLL build +* Win32_DLL_Debug\libpng13d.dll DLL build (debug version) +* Win32_DLL_ASM_Release\libpng13.dll DLL build using ASM code +* Win32_DLL_ASM_Debug\libpng13d.dll DLL build using ASM (debug version) +* Win32_DLL_VB\libpng13vb.dll DLL build for Visual Basic, using stdcall +* Win32_LIB_Release\libpng.lib static build +* Win32_LIB_Debug\libpngd.lib static build (debug version) +* Win32_LIB_ASM_Release\libpng.lib static build using ASM code +* Win32_LIB_ASM_Debug\libpngd.lib static build using ASM (debug version) + +Notes: + +If you change anything in the source files, or select different compiler +settings, please change the DLL name to something different than any of +the above names. Also, make sure that in your "pngusr.h" you define +PNG_USER_PRIVATEBUILD and PNG_USER_DLLFNAME_POSTFIX according to the +instructions provided in "pngconf.h". + +All DLLs built by this project use the Microsoft dynamic C runtime library +MSVCR71.DLL (MSVCR71D.DLL for debug versions). If you distribute any of the +above mentioned libraries you may have to include this DLL in your package. +For a list of files that are redistributable in Visual Studio see +$(VCINSTALLDIR)\redist.txt. diff --git a/Engine/lib/lpng/projects/visualc71/README_zlib.txt b/Engine/lib/lpng/projects/visualc71/README_zlib.txt new file mode 100644 index 000000000..ff6a5c7cb --- /dev/null +++ b/Engine/lib/lpng/projects/visualc71/README_zlib.txt @@ -0,0 +1,44 @@ +/* WARNING: This file was put in the LibPNG distribution for convenience only. + It is expected to be part of the next zlib release under + "projects\visualc71\README.txt." */ + +Microsoft Developer Studio Project File, Format Version 7.10 for zlib. + +Copyright (C) 2004 Simon-Pierre Cadieux. +Copyright (C) 2004 Cosmin Truta. +For conditions of distribution and use, see copyright notice in zlib.h. + + +To use: + +1) On the main menu, select "File | Open Solution". + Open "zlib.sln". + +2) Display the Solution Explorer view (Ctrl+Alt+L) + +3) Set one of the project as the StartUp project. If you just want to build the + binaries set "zlib" as the startup project (Select "zlib" tree view item + + Project | Set as StartUp project). If you want to build and test the + binaries set it to "example" (Select "example" tree view item + Project | + Set as StartUp project), If you want to build the minigzip utility set it to + "minigzip" (Select "minigzip" tree view item + Project | Set as StartUp + project + +4) Select "Build | Configuration Manager...". + Choose the configuration you wish to build. + +5) Select "Build | Clean Solution". + +6) Select "Build | Build Solution (Ctrl-Shift-B)" + +This project builds the zlib binaries as follows: + +* Win32_DLL_Release\zlib1.dll DLL build +* Win32_DLL_Debug\zlib1d.dll DLL build (debug version) +* Win32_DLL_ASM_Release\zlib1.dll DLL build using ASM code +* Win32_DLL_ASM_Debug\zlib1d.dll DLL build using ASM code (debug version) +* Win32_LIB_Release\zlib.lib static build +* Win32_LIB_Debug\zlibd.lib static build (debug version) +* Win32_LIB_ASM_Release\zlib.lib static build using ASM code +* Win32_LIB_ASM_Debug\zlibd.lib static build using ASM code (debug version) + diff --git a/Engine/lib/lpng/projects/visualc71/libpng.sln b/Engine/lib/lpng/projects/visualc71/libpng.sln new file mode 100644 index 000000000..d49277eb0 --- /dev/null +++ b/Engine/lib/lpng/projects/visualc71/libpng.sln @@ -0,0 +1,88 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libpng", "libpng.vcproj", "{0008960E-E0DD-41A6-8265-00B31DDB4C21}" + ProjectSection(ProjectDependencies) = postProject + {2D4F8105-7D21-454C-9932-B47CAB71A5C0} = {2D4F8105-7D21-454C-9932-B47CAB71A5C0} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pngtest", "pngtest.vcproj", "{FD1C2F86-9EEF-47BD-95A4-530917E17FDA}" + ProjectSection(ProjectDependencies) = postProject + {0008960E-E0DD-41A6-8265-00B31DDB4C21} = {0008960E-E0DD-41A6-8265-00B31DDB4C21} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "zlib.vcproj", "{2D4F8105-7D21-454C-9932-B47CAB71A5C0}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + DLL ASM Debug = DLL ASM Debug + DLL ASM Release = DLL ASM Release + DLL Debug = DLL Debug + DLL Release = DLL Release + DLL VB = DLL VB + LIB ASM Debug = LIB ASM Debug + LIB ASM Release = LIB ASM Release + LIB Debug = LIB Debug + LIB Release = LIB Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {0008960E-E0DD-41A6-8265-00B31DDB4C21}.DLL ASM Debug.ActiveCfg = DLL ASM Debug|Win32 + {0008960E-E0DD-41A6-8265-00B31DDB4C21}.DLL ASM Debug.Build.0 = DLL ASM Debug|Win32 + {0008960E-E0DD-41A6-8265-00B31DDB4C21}.DLL ASM Release.ActiveCfg = DLL ASM Release|Win32 + {0008960E-E0DD-41A6-8265-00B31DDB4C21}.DLL ASM Release.Build.0 = DLL ASM Release|Win32 + {0008960E-E0DD-41A6-8265-00B31DDB4C21}.DLL Debug.ActiveCfg = DLL Debug|Win32 + {0008960E-E0DD-41A6-8265-00B31DDB4C21}.DLL Debug.Build.0 = DLL Debug|Win32 + {0008960E-E0DD-41A6-8265-00B31DDB4C21}.DLL Release.ActiveCfg = DLL Release|Win32 + {0008960E-E0DD-41A6-8265-00B31DDB4C21}.DLL Release.Build.0 = DLL Release|Win32 + {0008960E-E0DD-41A6-8265-00B31DDB4C21}.DLL VB.ActiveCfg = DLL VB|Win32 + {0008960E-E0DD-41A6-8265-00B31DDB4C21}.DLL VB.Build.0 = DLL VB|Win32 + {0008960E-E0DD-41A6-8265-00B31DDB4C21}.LIB ASM Debug.ActiveCfg = LIB ASM Debug|Win32 + {0008960E-E0DD-41A6-8265-00B31DDB4C21}.LIB ASM Debug.Build.0 = LIB ASM Debug|Win32 + {0008960E-E0DD-41A6-8265-00B31DDB4C21}.LIB ASM Release.ActiveCfg = LIB ASM Release|Win32 + {0008960E-E0DD-41A6-8265-00B31DDB4C21}.LIB ASM Release.Build.0 = LIB ASM Release|Win32 + {0008960E-E0DD-41A6-8265-00B31DDB4C21}.LIB Debug.ActiveCfg = LIB Debug|Win32 + {0008960E-E0DD-41A6-8265-00B31DDB4C21}.LIB Debug.Build.0 = LIB Debug|Win32 + {0008960E-E0DD-41A6-8265-00B31DDB4C21}.LIB Release.ActiveCfg = LIB Release|Win32 + {0008960E-E0DD-41A6-8265-00B31DDB4C21}.LIB Release.Build.0 = LIB Release|Win32 + {FD1C2F86-9EEF-47BD-95A4-530917E17FDA}.DLL ASM Debug.ActiveCfg = DLL ASM Debug|Win32 + {FD1C2F86-9EEF-47BD-95A4-530917E17FDA}.DLL ASM Debug.Build.0 = DLL ASM Debug|Win32 + {FD1C2F86-9EEF-47BD-95A4-530917E17FDA}.DLL ASM Release.ActiveCfg = DLL ASM Release|Win32 + {FD1C2F86-9EEF-47BD-95A4-530917E17FDA}.DLL ASM Release.Build.0 = DLL ASM Release|Win32 + {FD1C2F86-9EEF-47BD-95A4-530917E17FDA}.DLL Debug.ActiveCfg = DLL Debug|Win32 + {FD1C2F86-9EEF-47BD-95A4-530917E17FDA}.DLL Debug.Build.0 = DLL Debug|Win32 + {FD1C2F86-9EEF-47BD-95A4-530917E17FDA}.DLL Release.ActiveCfg = DLL Release|Win32 + {FD1C2F86-9EEF-47BD-95A4-530917E17FDA}.DLL Release.Build.0 = DLL Release|Win32 + {FD1C2F86-9EEF-47BD-95A4-530917E17FDA}.DLL VB.ActiveCfg = DLL VB|Win32 + {FD1C2F86-9EEF-47BD-95A4-530917E17FDA}.DLL VB.Build.0 = DLL VB|Win32 + {FD1C2F86-9EEF-47BD-95A4-530917E17FDA}.LIB ASM Debug.ActiveCfg = LIB ASM Debug|Win32 + {FD1C2F86-9EEF-47BD-95A4-530917E17FDA}.LIB ASM Debug.Build.0 = LIB ASM Debug|Win32 + {FD1C2F86-9EEF-47BD-95A4-530917E17FDA}.LIB ASM Release.ActiveCfg = LIB ASM Release|Win32 + {FD1C2F86-9EEF-47BD-95A4-530917E17FDA}.LIB ASM Release.Build.0 = LIB ASM Release|Win32 + {FD1C2F86-9EEF-47BD-95A4-530917E17FDA}.LIB Debug.ActiveCfg = LIB Debug|Win32 + {FD1C2F86-9EEF-47BD-95A4-530917E17FDA}.LIB Debug.Build.0 = LIB Debug|Win32 + {FD1C2F86-9EEF-47BD-95A4-530917E17FDA}.LIB Release.ActiveCfg = LIB Release|Win32 + {FD1C2F86-9EEF-47BD-95A4-530917E17FDA}.LIB Release.Build.0 = LIB Release|Win32 + {2D4F8105-7D21-454C-9932-B47CAB71A5C0}.DLL ASM Debug.ActiveCfg = DLL ASM Debug|Win32 + {2D4F8105-7D21-454C-9932-B47CAB71A5C0}.DLL ASM Debug.Build.0 = DLL ASM Debug|Win32 + {2D4F8105-7D21-454C-9932-B47CAB71A5C0}.DLL ASM Release.ActiveCfg = DLL ASM Release|Win32 + {2D4F8105-7D21-454C-9932-B47CAB71A5C0}.DLL ASM Release.Build.0 = DLL ASM Release|Win32 + {2D4F8105-7D21-454C-9932-B47CAB71A5C0}.DLL Debug.ActiveCfg = DLL Debug|Win32 + {2D4F8105-7D21-454C-9932-B47CAB71A5C0}.DLL Debug.Build.0 = DLL Debug|Win32 + {2D4F8105-7D21-454C-9932-B47CAB71A5C0}.DLL Release.ActiveCfg = DLL Release|Win32 + {2D4F8105-7D21-454C-9932-B47CAB71A5C0}.DLL Release.Build.0 = DLL Release|Win32 + {2D4F8105-7D21-454C-9932-B47CAB71A5C0}.DLL VB.ActiveCfg = DLL Release|Win32 + {2D4F8105-7D21-454C-9932-B47CAB71A5C0}.DLL VB.Build.0 = DLL Release|Win32 + {2D4F8105-7D21-454C-9932-B47CAB71A5C0}.LIB ASM Debug.ActiveCfg = LIB ASM Debug|Win32 + {2D4F8105-7D21-454C-9932-B47CAB71A5C0}.LIB ASM Debug.Build.0 = LIB ASM Debug|Win32 + {2D4F8105-7D21-454C-9932-B47CAB71A5C0}.LIB ASM Release.ActiveCfg = LIB ASM Release|Win32 + {2D4F8105-7D21-454C-9932-B47CAB71A5C0}.LIB ASM Release.Build.0 = LIB ASM Release|Win32 + {2D4F8105-7D21-454C-9932-B47CAB71A5C0}.LIB Debug.ActiveCfg = LIB Debug|Win32 + {2D4F8105-7D21-454C-9932-B47CAB71A5C0}.LIB Debug.Build.0 = LIB Debug|Win32 + {2D4F8105-7D21-454C-9932-B47CAB71A5C0}.LIB Release.ActiveCfg = LIB Release|Win32 + {2D4F8105-7D21-454C-9932-B47CAB71A5C0}.LIB Release.Build.0 = LIB Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/Engine/lib/lpng/projects/visualc71/libpng.vcproj b/Engine/lib/lpng/projects/visualc71/libpng.vcproj new file mode 100644 index 000000000..926e37bd9 --- /dev/null +++ b/Engine/lib/lpng/projects/visualc71/libpng.vcproj @@ -0,0 +1,702 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Engine/lib/lpng/projects/visualc71/pngtest.vcproj b/Engine/lib/lpng/projects/visualc71/pngtest.vcproj new file mode 100644 index 000000000..e7a29277d --- /dev/null +++ b/Engine/lib/lpng/projects/visualc71/pngtest.vcproj @@ -0,0 +1,459 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Engine/lib/lpng/projects/visualc71/zlib.vcproj b/Engine/lib/lpng/projects/visualc71/zlib.vcproj new file mode 100644 index 000000000..5574ae38e --- /dev/null +++ b/Engine/lib/lpng/projects/visualc71/zlib.vcproj @@ -0,0 +1,670 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Engine/lib/lpng/projects/wince.txt b/Engine/lib/lpng/projects/wince.txt new file mode 100644 index 000000000..a1a26c0bc --- /dev/null +++ b/Engine/lib/lpng/projects/wince.txt @@ -0,0 +1,6 @@ +A set of project files is available for WinCE. Get +libpng-1.2.5-project-wince.zip from a libpng distribution +site such as http://libpng.sourceforge.net + +Put the zip file in this directory (projects) and then run +"unzip -a libpng-1.2.5-project-wince.zip" diff --git a/Engine/lib/lpng/scripts/CMakeLists.txt b/Engine/lib/lpng/scripts/CMakeLists.txt new file mode 100644 index 000000000..398a47051 --- /dev/null +++ b/Engine/lib/lpng/scripts/CMakeLists.txt @@ -0,0 +1,210 @@ + +project(PNG) + +# Copyright (C) 2007 Glenn Randers-Pehrson +# For conditions of distribution and use, see copyright notice in png.h + +set(PNGLIB_MAJOR 1) +set(PNGLIB_MINOR 2) +set(PNGLIB_RELEASE 24) +set(PNGLIB_NAME libpng${PNGLIB_MAJOR}${PNGLIB_MINOR}) +set(PNGLIB_VERSION ${PNGLIB_MAJOR}.${PNGLIB_MINOR}.${PNGLIB_RELEASE}) + +# needed packages +find_package(ZLIB REQUIRED) +if(NOT WIN32) + find_library(M_LIBRARY + NAMES m + PATHS /usr/lib /usr/local/lib + ) + if(NOT M_LIBRARY) + message(STATUS + "math library 'libm' not found - floating point support disabled") + endif(NOT M_LIBRARY) +else(NOT WIN32) + # not needed on windows + set(M_LIBRARY "") +endif(NOT WIN32) + + +# COMMAND LINE OPTIONS +option(PNG_SHARED "Build shared lib" YES) +option(PNG_STATIC "Build static lib" YES) +if(MINGW) + option(PNG_TESTS "Build pngtest" NO) +else(MINGW) + option(PNG_TESTS "Build pngtest" YES) +endif(MINGW) +option(PNG_NO_CONSOLE_IO "FIXME" YES) +option(PNG_NO_STDIO "FIXME" YES) +option(PNG_DEBUG "Build with debug output" YES) +option(PNGARG "FIXME" YES) +#TODO: +# PNG_CONSOLE_IO_SUPPORTED + +# maybe needs improving, but currently I don't know when we can enable what :) +set(png_asm_tmp "OFF") +if(NOT WIN32) + find_program(uname_executable NAMES uname PATHS /bin /usr/bin /usr/local/bin) + if(uname_executable) + EXEC_PROGRAM(${uname_executable} ARGS --machine OUTPUT_VARIABLE uname_output) + if("uname_output" MATCHES "^.*i[1-9]86.*$") + set(png_asm_tmp "ON") + else("uname_output" MATCHES "^.*i[1-9]86.*$") + set(png_asm_tmp "OFF") + endif("uname_output" MATCHES "^.*i[1-9]86.*$") + endif(uname_executable) +else(NOT WIN32) + # this env var is normally only set on win64 + SET(TEXT "ProgramFiles(x86)") + if("$ENV{${TEXT}}" STREQUAL "") + set(png_asm_tmp "ON") + endif("$ENV{${TEXT}}" STREQUAL "") +endif(NOT WIN32) + +# SET LIBNAME +# msvc does not append 'lib' - do it here to have consistent name +if(MSVC) + set(PNG_LIB_NAME lib) +endif(MSVC) +set(PNG_LIB_NAME ${PNG_LIB_NAME}png${PNGLIB_MAJOR}${PNGLIB_MINOR}) + +# to distinguish between debug and release lib +set(CMAKE_DEBUG_POSTFIX "d") + + +# OUR SOURCES +set(libpng_sources + png.h + pngconf.h + png.c + pngerror.c + pngget.c + pngmem.c + pngpread.c + pngread.c + pngrio.c + pngrtran.c + pngrutil.c + pngset.c + pngtrans.c + pngwio.c + pngwrite.c + pngwtran.c + pngwutil.c +) +set(pngtest_sources + pngtest.c +) +# SOME NEEDED DEFINITIONS +if(MSVC) + add_definitions(-DPNG_NO_MODULEDEF -D_CRT_SECURE_NO_DEPRECATE) +endif(MSVC) + +add_definitions(-DZLIB_DLL) + +add_definitions(-DLIBPNG_NO_MMX) +add_definitions(-DPNG_NO_MMX_CODE) + +if(PNG_CONSOLE_IO_SUPPORTED) + add_definitions(-DPNG_CONSOLE_IO_SUPPORTED) +endif(PNG_CONSOLE_IO_SUPPORTED) + +if(PNG_NO_CONSOLE_IO) + add_definitions(-DPNG_NO_CONSOLE_IO) +endif(PNG_NO_CONSOLE_IO) + +if(PNG_NO_STDIO) + add_definitions(-DPNG_NO_STDIO) +endif(PNG_NO_STDIO) + +if(PNG_DEBUG) + add_definitions(-DPNG_DEBUG) +endif(PNG_DEBUG) + +if(NOT M_LIBRARY AND NOT WIN32) + add_definitions(-DPNG_NO_FLOATING_POINT_SUPPORTED) +endif(NOT M_LIBRARY AND NOT WIN32) + +# NOW BUILD OUR TARGET +include_directories(${PNG_SOURCE_DIR} ${ZLIB_INCLUDE_DIR}) + +if(PNG_SHARED) + add_library(${PNG_LIB_NAME} SHARED ${libpng_sources}) + target_link_libraries(${PNG_LIB_NAME} ${ZLIB_LIBRARY} ${M_LIBRARY}) +endif(PNG_SHARED) +if(PNG_STATIC) +# does not work without changing name + set(PNG_LIB_NAME_STATIC ${PNG_LIB_NAME}_static) + add_library(${PNG_LIB_NAME_STATIC} STATIC ${libpng_sources}) +endif(PNG_STATIC) + +if(PNG_SHARED AND WIN32) + set_target_properties(${PNG_LIB_NAME} PROPERTIES DEFINE_SYMBOL PNG_BUILD_DLL) +endif(PNG_SHARED AND WIN32) + +if(PNG_TESTS) +# does not work with msvc due to png_lib_ver issue + add_executable(pngtest ${pngtest_sources}) + target_link_libraries(pngtest ${PNG_LIB_NAME}) +# add_test(pngtest ${PNG_SOURCE_DIR}/pngtest.png) +endif(PNG_TESTS) + + +# CREATE PKGCONFIG FILES +# we use the same files like ./configure, so we have to set its vars +set(prefix ${CMAKE_INSTALL_PREFIX}) +set(exec_prefix ${CMAKE_INSTALL_PREFIX}) +set(libdir ${CMAKE_INSTALL_PREFIX}/lib) +set(includedir ${CMAKE_INSTALL_PREFIX}/include) + +configure_file(${PNG_SOURCE_DIR}/scripts/libpng.pc.in + ${PNG_BINARY_DIR}/libpng.pc) +configure_file(${PNG_SOURCE_DIR}/scripts/libpng-config.in + ${PNG_BINARY_DIR}/libpng-config) +configure_file(${PNG_SOURCE_DIR}/scripts/libpng.pc.in + ${PNG_BINARY_DIR}/${PNGLIB_NAME}.pc) +configure_file(${PNG_SOURCE_DIR}/scripts/libpng-config.in + ${PNG_BINARY_DIR}/${PNGLIB_NAME}-config) + +# SET UP LINKS +set_target_properties(${PNG_LIB_NAME} PROPERTIES +# VERSION 0.${PNGLIB_RELEASE}.1.2.24 + VERSION 0.${PNGLIB_RELEASE}.0 + SOVERSION 0 + CLEAN_DIRECT_OUTPUT 1) +if(NOT WIN32) + # that's uncool on win32 - it overwrites our static import lib... + set_target_properties(${PNG_LIB_NAME_STATIC} PROPERTIES + OUTPUT_NAME ${PNG_LIB_NAME} + CLEAN_DIRECT_OUTPUT 1) +endif(NOT WIN32) +# INSTALL +install_targets(/lib ${PNG_LIB_NAME}) +if(PNG_STATIC) + install_targets(/lib ${PNG_LIB_NAME_STATIC}) +endif(PNG_STATIC) + +install(FILES png.h pngconf.h DESTINATION include) +install(FILES png.h pngconf.h DESTINATION include/${PNGLIB_NAME}) +install(FILES libpng.3 libpngpf.3 DESTINATION man/man3) +install(FILES png.5 DESTINATION man/man5) +install(FILES ${PNG_BINARY_DIR}/libpng.pc DESTINATION lib/pkgconfig) +install(FILES ${PNG_BINARY_DIR}/libpng-config DESTINATION bin) +install(FILES ${PNG_BINARY_DIR}/${PNGLIB_NAME}.pc DESTINATION lib/pkgconfig) +install(FILES ${PNG_BINARY_DIR}/${PNGLIB_NAME}-config DESTINATION bin) + +# what's with libpng.txt and all the extra files? + + +# UNINSTALL +# do we need this? + + +# DIST +# do we need this? + +# to create msvc import lib for mingw compiled shared lib +# pexports libpng.dll > libpng.def +# lib /def:libpng.def /machine:x86 + diff --git a/Engine/lib/lpng/scripts/SCOPTIONS.ppc b/Engine/lib/lpng/scripts/SCOPTIONS.ppc new file mode 100644 index 000000000..2c3503e9e --- /dev/null +++ b/Engine/lib/lpng/scripts/SCOPTIONS.ppc @@ -0,0 +1,7 @@ +OPTIMIZE +OPTPEEP +OPTTIME +OPTSCHED +AUTOREGISTER +PARMS=REGISTERS +INCLUDEDIR=hlp:ppc/include diff --git a/Engine/lib/lpng/scripts/descrip.mms b/Engine/lib/lpng/scripts/descrip.mms new file mode 100644 index 000000000..3584b0d78 --- /dev/null +++ b/Engine/lib/lpng/scripts/descrip.mms @@ -0,0 +1,52 @@ + +cc_defs = /inc=$(ZLIBSRC) +c_deb = + +.ifdef __DECC__ +pref = /prefix=all +.endif + + + +OBJS = png.obj, pngset.obj, pngget.obj, pngrutil.obj, pngtrans.obj,\ + pngwutil.obj, pngread.obj, pngmem.obj, pngwrite.obj, pngrtran.obj,\ + pngwtran.obj, pngrio.obj, pngwio.obj, pngerror.obj, pngpread.obj + + +CFLAGS= $(C_DEB) $(CC_DEFS) $(PREF) + +all : pngtest.exe libpng.olb + @ write sys$output " pngtest available" + +libpng.olb : libpng.olb($(OBJS)) + @ write sys$output " Libpng available" + + +pngtest.exe : pngtest.obj libpng.olb + link pngtest,libpng.olb/lib,$(ZLIBSRC)libz.olb/lib + +test : pngtest.exe + run pngtest + +clean : + delete *.obj;*,*.exe;* + + +# Other dependencies. +png.obj : png.h, pngconf.h +pngpread.obj : png.h, pngconf.h +pngset.obj : png.h, pngconf.h +pngget.obj : png.h, pngconf.h +pngread.obj : png.h, pngconf.h +pngrtran.obj : png.h, pngconf.h +pngrutil.obj : png.h, pngconf.h +pngerror.obj : png.h, pngconf.h +pngmem.obj : png.h, pngconf.h +pngrio.obj : png.h, pngconf.h +pngwio.obj : png.h, pngconf.h +pngtest.obj : png.h, pngconf.h +pngtrans.obj : png.h, pngconf.h +pngwrite.obj : png.h, pngconf.h +pngwtran.obj : png.h, pngconf.h +pngwutil.obj : png.h, pngconf.h + diff --git a/Engine/lib/lpng/scripts/libpng-config-body.in b/Engine/lib/lpng/scripts/libpng-config-body.in new file mode 100644 index 000000000..b466432d5 --- /dev/null +++ b/Engine/lib/lpng/scripts/libpng-config-body.in @@ -0,0 +1,96 @@ + +usage() +{ + cat < libpng.pc + +libpng-config: + ( cat scripts/libpng-config-head.in; \ + echo prefix=\"$(prefix)\"; \ + echo libdir=\"$(LIBPATH)\"; \ + echo I_opts=\"-I$(INCPATH)/$(LIBNAME)\"; \ + echo L_opts=\"-L$(LIBPATH)\"; \ + echo R_opts=\"-R$(LIBPATH)\"; \ + echo ccopts=\"-xtarget=ultra\"; \ + echo ldopts=\"-xtarget=ultra\"; \ + echo libs=\"-lpng12 -lz -lm\"; \ + cat scripts/libpng-config-body.in ) > libpng-config + chmod +x libpng-config + +$(LIBSO): $(LIBSOMAJ) + $(LN_SF) $(LIBSOMAJ) $(LIBSO) + +$(LIBSOMAJ): $(LIBSOVER) + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ) + +$(LIBSOVER): $(OBJSDLL) + @case "`type ld`" in *ucb*) \ + echo; \ + echo '## WARNING:'; \ + echo '## The commands "CC" and "LD" must NOT refer to /usr/ucb/cc'; \ + echo '## and /usr/ucb/ld. If they do, you need to adjust your PATH'; \ + echo '## environment variable to put /usr/ccs/bin ahead of /usr/ucb.'; \ + echo '## The environment variable LD_LIBRARY_PATH should not be set'; \ + echo '## at all. If it is, things are likely to break because of'; \ + echo '## the libucb dependency that is created.'; \ + echo; \ + ;; \ + esac + $(LD) -G -L$(ZLIBLIB) -R$(ZLIBLIB) -h $(LIBSOMAJ) \ + -o $(LIBSOVER) $(OBJSDLL) + +$(OLDSOVER): $(OBJSDLL) + $(LD) -G -L$(ZLIBLIB) -R$(ZLIBLIB) -h $(OLDSOMAJ) \ + -o $(OLDSOVER) $(OBJSDLL) + +pngtest: pngtest.o $(LIBSO) + $(CC) -o pngtest $(CFLAGS) pngtest.o $(LDFLAGS) + +test: pngtest + ./pngtest + +install-headers: png.h pngconf.h + -@if [ ! -d $(DI) ]; then $(MKDIR_P) $(DI); fi + -@if [ ! -d $(DI)/$(LIBNAME) ]; then $(MKDIR_P) $(DI)/$(LIBNAME); fi + cp png.h pngconf.h $(DI)/$(LIBNAME) + chmod 644 $(DI)/$(LIBNAME)/png.h $(DI)/$(LIBNAME)/pngconf.h + -@$(RM_F) $(DI)/png.h $(DI)/pngconf.h + -@$(RM_F) $(DI)/libpng + (cd $(DI); $(LN_SF) $(LIBNAME) libpng; $(LN_SF) $(LIBNAME)/* .) + +install-static: install-headers libpng.a + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + cp libpng.a $(DL)/$(LIBNAME).a + chmod 644 $(DL)/$(LIBNAME).a + -@$(RM_F) $(DL)/libpng.a + (cd $(DL); $(LN_SF) $(LIBNAME).a libpng.a) + +install-shared: install-headers $(LIBSOVER) libpng.pc \ + $(OLDSOVER) + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + -@$(RM_F) $(DL)/$(LIBSOVER)* $(DL)/$(LIBSO) + -@$(RM_F) $(DL)/$(LIBSOMAJ) + -@$(RM_F) $(DL)/$(LIBSO) + -@$(RM_F) $(DL)/$(OLDSO) + -@$(RM_F) $(DL)/$(OLDSOMAJ) + -@$(RM_F) $(DL)/$(OLDSOMAJ).$(PNGVER)* + cp $(LIBSOVER) $(DL) + cp $(OLDSOVER) $(DL) + chmod 755 $(DL)/$(LIBSOVER) + chmod 755 $(DL)/$(OLDSOVER) + (cd $(DL); \ + $(LN_SF) $(OLDSOVER) $(OLDSOMAJ); \ + $(LN_SF) $(OLDSOMAJ) $(OLDSO); \ + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ); \ + $(LN_SF) $(LIBSOMAJ) $(LIBSO)) + -@if [ ! -d $(DL)/pkgconfig ]; then $(MKDIR_P) $(DL)/pkgconfig; fi + -@$(RM_F) $(DL)/pkgconfig/$(LIBNAME).pc + -@$(RM_F) $(DL)/pkgconfig/libpng.pc + cp libpng.pc $(DL)/pkgconfig/$(LIBNAME).pc + chmod 644 $(DL)/pkgconfig/$(LIBNAME).pc + (cd $(DL)/pkgconfig; $(LN_SF) $(LIBNAME).pc libpng.pc) + +install-man: libpng.3 libpngpf.3 png.5 + -@if [ ! -d $(DM) ]; then $(MKDIR_P) $(DM); fi + -@if [ ! -d $(DM)/man3 ]; then $(MKDIR_P) $(DM)/man3; fi + -@$(RM_F) $(DM)/man3/libpng.3 + -@$(RM_F) $(DM)/man3/libpngpf.3 + cp libpng.3 $(DM)/man3 + cp libpngpf.3 $(DM)/man3 + -@if [ ! -d $(DM)/man5 ]; then $(MKDIR_P) $(DM)/man5; fi + -@$(RM_F) $(DM)/man5/png.5 + cp png.5 $(DM)/man5 + +install-config: libpng-config + -@if [ ! -d $(DB) ]; then $(MKDIR_P) $(DB); fi + -@$(RM_F) $(DB)/libpng-config + -@$(RM_F) $(DB)/$(LIBNAME)-config + cp libpng-config $(DB)/$(LIBNAME)-config + chmod 755 $(DB)/$(LIBNAME)-config + (cd $(DB); $(LN_SF) $(LIBNAME)-config libpng-config) + +install: install-static install-shared install-man install-config + +# If you installed in $(DESTDIR), test-installed won't work until you +# move the library to its final location. Use test-dd to test it +# before then. + +test-dd: + echo + echo Testing installed dynamic shared library in $(DL). + $(CC) $(SUN_CC_FLAGS) -I$(DI) -I$(ZLIBINC) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -o pngtestd -L$(DL) -R$(DL) `$(BINPATH)/$(LIBNAME)-config --ldflags` \ + $(SUN_LD_FLAGS) -L$(ZLIBLIB) -R$(ZLIBLIB) + ./pngtestd pngtest.png + +test-installed: + echo + echo Testing installed dynamic shared library. + $(CC) $(SUN_CC_FLAGS) -I$(ZLIBINC) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -o pngtesti `$(BINPATH)/$(LIBNAME)-config --ldflags` \ + $(SUN_LD_FLAGS) -L$(ZLIBLIB) -R$(ZLIBLIB) + ./pngtesti pngtest.png + +clean: + $(RM_F) *.o libpng.a pngtest pngtesti pngout.png \ + libpng-config $(LIBSO) $(LIBSOMAJ)* \ + $(OLDSOVER) \ + libpng.pc + +DOCS = ANNOUNCE CHANGES INSTALL KNOWNBUG LICENSE README TODO Y2KINFO +writelock: + chmod a-w *.[ch35] $(DOCS) scripts/* + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +png.o png.pic.o: png.h pngconf.h +pngerror.o pngerror.pic.o: png.h pngconf.h +pngrio.o pngrio.pic.o: png.h pngconf.h +pngwio.o pngwio.pic.o: png.h pngconf.h +pngmem.o pngmem.pic.o: png.h pngconf.h +pngset.o pngset.pic.o: png.h pngconf.h +pngget.o pngget.pic.o: png.h pngconf.h +pngread.o pngread.pic.o: png.h pngconf.h +pngrtran.o pngrtran.pic.o: png.h pngconf.h +pngrutil.o pngrutil.pic.o: png.h pngconf.h +pngtrans.o pngtrans.pic.o: png.h pngconf.h +pngwrite.o pngwrite.pic.o: png.h pngconf.h +pngwtran.o pngwtran.pic.o: png.h pngconf.h +pngwutil.o pngwutil.pic.o: png.h pngconf.h +pngpread.o pngpread.pic.o: png.h pngconf.h + +pngtest.o: png.h pngconf.h diff --git a/Engine/lib/lpng/scripts/makefile.64sunu b/Engine/lib/lpng/scripts/makefile.64sunu new file mode 100644 index 000000000..b46c68a9a --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.64sunu @@ -0,0 +1,254 @@ +# makefile for libpng on Solaris 2.x with cc +# Contributed by William L. Sebok, based on makefile.linux +# Copyright (C) 2002, 2006 Glenn Randers-Pehrson +# Copyright (C) 1998 Greg Roelofs +# Copyright (C) 1996, 1997 Andreas Dilger +# For conditions of distribution and use, see copyright notice in png.h + +# Library name: +LIBNAME=libpng12 +PNGMAJ = 0 +PNGMIN = 1.2.24 +PNGVER = $(PNGMAJ).$(PNGMIN) + +# Shared library names: +LIBSO=$(LIBNAME).so +LIBSOMAJ=$(LIBNAME).so.$(PNGMAJ) +LIBSOVER=$(LIBNAME).so.$(PNGVER) +OLDSO=libpng.so +OLDSOMAJ=libpng.so.3 +OLDSOVER=libpng.so.3.$(PNGMIN) + +# Utilities: +CC=cc +AR_RC=ar rc +MKDIR_P=mkdir -p +LN_SF=ln -f -s +RANLIB=echo +RM_F=/bin/rm -f + +SUN_CC_FLAGS=-fast -xtarget=ultra -xarch=v9 +SUN_LD_FLAGS=-fast -xtarget=ultra -xarch=v9 + +# where make install puts libpng.a, libpng12.so and libpng12/png.h +prefix=/a +exec_prefix=$(prefix) + +# Where the zlib library and include files are located +# Changing these to ../zlib poses a security risk. If you want +# to have zlib in an adjacent directory, specify the full path instead of "..". +#ZLIBLIB=../zlib +#ZLIBINC=../zlib + +ZLIBLIB=/usr/lib +ZLIBINC=/usr/include + +WARNMORE=-Wwrite-strings -Wpointer-arith -Wshadow \ + -Wmissing-declarations -Wtraditional -Wcast-align \ + -Wstrict-prototypes -Wmissing-prototypes #-Wconversion +CFLAGS=-I$(ZLIBINC) $(SUN_CC_FLAGS) \ + # $(WARNMORE) -g -DPNG_DEBUG=5 +LDFLAGS=-L. -R. $(SUN_LD_FLAGS) -L$(ZLIBLIB) -R$(ZLIBLIB) -lpng12 -lz -lm + +INCPATH=$(prefix)/include +LIBPATH=$(exec_prefix)/lib +MANPATH=$(prefix)/man +BINPATH=$(exec_prefix)/bin + +# override DESTDIR= on the make install command line to easily support +# installing into a temporary location. Example: +# +# make install DESTDIR=/tmp/build/libpng +# +# If you're going to install into a temporary location +# via DESTDIR, $(DESTDIR)$(prefix) must already exist before +# you execute make install. +DESTDIR= + +DB=$(DESTDIR)$(BINPATH) +DI=$(DESTDIR)$(INCPATH) +DL=$(DESTDIR)$(LIBPATH) +DM=$(DESTDIR)$(MANPATH) + +OBJS = png.o pngset.o pngget.o pngrutil.o pngtrans.o pngwutil.o \ + pngread.o pngrio.o pngwio.o pngwrite.o pngrtran.o \ + pngwtran.o pngmem.o pngerror.o pngpread.o + +OBJSDLL = $(OBJS:.o=.pic.o) + +.SUFFIXES: .c .o .pic.o + +.c.pic.o: + $(CC) -c $(CFLAGS) -KPIC -o $@ $*.c + +all: libpng.a $(LIBSO) pngtest libpng.pc libpng-config + +libpng.a: $(OBJS) + $(AR_RC) $@ $(OBJS) + $(RANLIB) $@ + +libpng.pc: + cat scripts/libpng.pc.in | sed -e s!@prefix@!$(prefix)! \ + -e s!@exec_prefix@!$(exec_prefix)! \ + -e s!@libdir@!$(LIBPATH)! \ + -e s!@includedir@!$(INCPATH)! \ + -e s!-lpng12!-lpng12\ -lz\ -lm! > libpng.pc + +libpng-config: + ( cat scripts/libpng-config-head.in; \ + echo prefix=\"$(prefix)\"; \ + echo libdir=\"$(LIBPATH)\"; \ + echo I_opts=\"-I$(INCPATH)/$(LIBNAME)\"; \ + echo L_opts=\"-L$(LIBPATH)\"; \ + echo R_opts=\"-R$(LIBPATH)\"; \ + echo ccopts=\"-xtarget=ultra -xarch=v9\"; \ + echo ldopts=\"-xtarget=ultra -xarch=v9\"; \ + echo libs=\"-lpng12 -lz -lm\"; \ + cat scripts/libpng-config-body.in ) > libpng-config + chmod +x libpng-config + +$(LIBSO): $(LIBSOMAJ) + $(LN_SF) $(LIBSOMAJ) $(LIBSO) + +$(LIBSOMAJ): $(LIBSOVER) + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ) + +$(LIBSOVER): $(OBJSDLL) + @case "`type ld`" in *ucb*) \ + echo; \ + echo '## WARNING:'; \ + echo '## The commands "CC" and "LD" must NOT refer to /usr/ucb/cc'; \ + echo '## and /usr/ucb/ld. If they do, you need to adjust your PATH'; \ + echo '## environment variable to put /usr/ccs/bin ahead of /usr/ucb.'; \ + echo '## The environment variable LD_LIBRARY_PATH should not be set'; \ + echo '## at all. If it is, things are likely to break because of'; \ + echo '## the libucb dependency that is created.'; \ + echo; \ + ;; \ + esac + $(LD) -G -L$(ZLIBLIB) -R$(ZLIBLIB) -h $(LIBSOMAJ) \ + -o $(LIBSOVER) $(OBJSDLL) + +$(OLDSOVER): $(OBJSDLL) + $(LD) -G -L$(ZLIBLIB) -R$(ZLIBLIB) -h $(OLDSOMAJ) \ + -o $(OLDSOVER) $(OBJSDLL) + +pngtest: pngtest.o $(LIBSO) + $(CC) -o pngtest $(CFLAGS) pngtest.o $(LDFLAGS) + +test: pngtest + ./pngtest + +install-headers: png.h pngconf.h + -@if [ ! -d $(DI) ]; then $(MKDIR_P) $(DI); fi + -@if [ ! -d $(DI)/$(LIBNAME) ]; then $(MKDIR_P) $(DI)/$(LIBNAME); fi + cp png.h pngconf.h $(DI)/$(LIBNAME) + chmod 644 $(DI)/$(LIBNAME)/png.h $(DI)/$(LIBNAME)/pngconf.h + -@$(RM_F) $(DI)/png.h $(DI)/pngconf.h + -@$(RM_F) $(DI)/libpng + (cd $(DI); $(LN_SF) $(LIBNAME) libpng; $(LN_SF) $(LIBNAME)/* .) + +install-static: install-headers libpng.a + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + cp libpng.a $(DL)/$(LIBNAME).a + chmod 644 $(DL)/$(LIBNAME).a + -@$(RM_F) $(DL)/libpng.a + (cd $(DL); $(LN_SF) $(LIBNAME).a libpng.a) + +install-shared: install-headers $(LIBSOVER) libpng.pc \ + $(OLDSOVER) + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + -@$(RM_F) $(DL)/$(LIBSOVER)* $(DL)/$(LIBSO) + -@$(RM_F) $(DL)/$(LIBSOMAJ) + -@$(RM_F) $(DL)/$(LIBSO) + -@$(RM_F) $(DL)/$(OLDSO) + -@$(RM_F) $(DL)/$(OLDSOMAJ) + -@$(RM_F) $(DL)/$(OLDSOMAJ).$(PNGVER)* + cp $(LIBSOVER) $(DL) + cp $(OLDSOVER) $(DL) + chmod 755 $(DL)/$(LIBSOVER) + chmod 755 $(DL)/$(OLDSOVER) + (cd $(DL); \ + $(LN_SF) $(OLDSOVER) $(OLDSOMAJ); \ + $(LN_SF) $(OLDSOMAJ) $(OLDSO); \ + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ); \ + $(LN_SF) $(LIBSOMAJ) $(LIBSO)) + -@if [ ! -d $(DL)/pkgconfig ]; then $(MKDIR_P) $(DL)/pkgconfig; fi + -@$(RM_F) $(DL)/pkgconfig/$(LIBNAME).pc + -@$(RM_F) $(DL)/pkgconfig/libpng.pc + cp libpng.pc $(DL)/pkgconfig/$(LIBNAME).pc + chmod 644 $(DL)/pkgconfig/$(LIBNAME).pc + (cd $(DL)/pkgconfig; $(LN_SF) $(LIBNAME).pc libpng.pc) + +install-man: libpng.3 libpngpf.3 png.5 + -@if [ ! -d $(DM) ]; then $(MKDIR_P) $(DM); fi + -@if [ ! -d $(DM)/man3 ]; then $(MKDIR_P) $(DM)/man3; fi + -@$(RM_F) $(DM)/man3/libpng.3 + -@$(RM_F) $(DM)/man3/libpngpf.3 + cp libpng.3 $(DM)/man3 + cp libpngpf.3 $(DM)/man3 + -@if [ ! -d $(DM)/man5 ]; then $(MKDIR_P) $(DM)/man5; fi + -@$(RM_F) $(DM)/man5/png.5 + cp png.5 $(DM)/man5 + +install-config: libpng-config + -@if [ ! -d $(DB) ]; then $(MKDIR_P) $(DB); fi + -@$(RM_F) $(DB)/libpng-config + -@$(RM_F) $(DB)/$(LIBNAME)-config + cp libpng-config $(DB)/$(LIBNAME)-config + chmod 755 $(DB)/$(LIBNAME)-config + (cd $(DB); $(LN_SF) $(LIBNAME)-config libpng-config) + +install: install-static install-shared install-man install-config + +# If you installed in $(DESTDIR), test-installed won't work until you +# move the library to its final location. Use test-dd to test it +# before then. + +test-dd: + echo + echo Testing installed dynamic shared library in $(DL). + $(CC) $(SUN_CC_FLAGS) -I$(DI) -I$(ZLIBINC) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -o pngtestd -L$(DL) -R$(DL) `$(BINPATH)/$(LIBNAME)-config --ldflags` \ + $(SUN_LD_FLAGS) -L$(ZLIBLIB) -R$(ZLIBLIB) + ./pngtestd pngtest.png + +test-installed: + echo + echo Testing installed dynamic shared library. + $(CC) $(SUN_CC_FLAGS) -I$(ZLIBINC) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -o pngtesti `$(BINPATH)/$(LIBNAME)-config --ldflags` \ + $(SUN_LD_FLAGS) -L$(ZLIBLIB) -R$(ZLIBLIB) + ./pngtesti pngtest.png + +clean: + $(RM_F) *.o libpng.a pngtest pngtesti pngout.png \ + libpng-config $(LIBSO) $(LIBSOMAJ)* \ + $(OLDSOVER) \ + libpng.pc + +DOCS = ANNOUNCE CHANGES INSTALL KNOWNBUG LICENSE README TODO Y2KINFO +writelock: + chmod a-w *.[ch35] $(DOCS) scripts/* + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +png.o png.pic.o: png.h pngconf.h +pngerror.o pngerror.pic.o: png.h pngconf.h +pngrio.o pngrio.pic.o: png.h pngconf.h +pngwio.o pngwio.pic.o: png.h pngconf.h +pngmem.o pngmem.pic.o: png.h pngconf.h +pngset.o pngset.pic.o: png.h pngconf.h +pngget.o pngget.pic.o: png.h pngconf.h +pngread.o pngread.pic.o: png.h pngconf.h +pngrtran.o pngrtran.pic.o: png.h pngconf.h +pngrutil.o pngrutil.pic.o: png.h pngconf.h +pngtrans.o pngtrans.pic.o: png.h pngconf.h +pngwrite.o pngwrite.pic.o: png.h pngconf.h +pngwtran.o pngwtran.pic.o: png.h pngconf.h +pngwutil.o pngwutil.pic.o: png.h pngconf.h +pngpread.o pngpread.pic.o: png.h pngconf.h + +pngtest.o: png.h pngconf.h diff --git a/Engine/lib/lpng/scripts/makefile.acorn b/Engine/lib/lpng/scripts/makefile.acorn new file mode 100644 index 000000000..470cf89b1 --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.acorn @@ -0,0 +1,51 @@ +# Project: libpng + + +# Toolflags: +CCflags = -c -depend !Depend -IC:,Zlib: -g -throwback -DRISCOS -fnah +C++flags = -c -depend !Depend -IC: -throwback +Linkflags = -aif -c++ -o $@ +ObjAsmflags = -throwback -NoCache -depend !Depend +CMHGflags = +LibFileflags = -c -l -o $@ +Squeezeflags = -o $@ + + +# Final targets: +@.libpng-lib: @.o.png @.o.pngerror @.o.pngrio @.o.pngwio @.o.pngmem \ + @.o.pngpread @.o.pngset @.o.pngget @.o.pngread @.o.pngrtran \ + @.o.pngrutil @.o.pngtrans @.o.pngwrite @.o.pngwtran @.o.pngwutil + LibFile $(LibFileflags) @.o.png @.o.pngerror @.o.pngrio @.o.pngrtran \ + @.o.pngmem @.o.pngpread @.o.pngset @.o.pngget @.o.pngread @.o.pngwio \ + @.o.pngrutil @.o.pngtrans @.o.pngwrite @.o.pngwtran @.o.pngwutil +@.mm-libpng-lib: @.mm.png @.mm.pngerror @.mm.pngrio @.mm.pngwio @.mm.pngmem \ + @.mm.pngpread @.mm.pngset @.mm.pngget @.mm.pngread @.mm.pngrtran \ + @.mm.pngrutil @.mm.pngtrans @.mm.pngwrite @.mm.pngwtran @.mm.pngwutil + LibFile $(LibFileflags) @.mm.png @.mm.pngerror @.mm.pngrio \ + @.mm.pngwio @.mm.pngmem @.mm.pngpread @.mm.pngset @.mm.pngget \ + @.mm.pngread @.mm.pngrtran @.mm.pngrutil @.mm.pngtrans @.mm.pngwrite \ + @.mm.pngwtran @.mm.pngwutil + + +# User-editable dependencies: +# (C) Copyright 1997 Tom Tanner +Test: @.pngtest + .pngtest + @remove .pngtest + +#It would be nice if you could stop "make" listing from here on! +@.pngtest: @.o.pngtest @.libpng-lib C:o.Stubs Zlib:zlib_lib + Link $(Linkflags) @.o.pngtest @.libpng-lib C:o.Stubs Zlib:zlib_lib + +.SUFFIXES: .o .mm .c + +.c.mm: + MemCheck.CC cc $(ccflags) -o $@ LibPng:$< +.c.o: + cc $(ccflags) -o $@ $< + + +# Static dependencies: + + +# Dynamic dependencies: diff --git a/Engine/lib/lpng/scripts/makefile.aix b/Engine/lib/lpng/scripts/makefile.aix new file mode 100644 index 000000000..e94812761 --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.aix @@ -0,0 +1,113 @@ +# makefile for libpng using gcc (generic, static library) +# Copyright (C) 2002, 2006 Glenn Randers-Pehrson +# Copyright (C) 2000 Cosmin Truta +# Copyright (C) 2000 Marc O. Gloor (AIX support added, from makefile.gcc) +# Copyright (C) 1995 Guy Eric Schalnat, Group 42, Inc. +# For conditions of distribution and use, see copyright notice in png.h + +# Location of the zlib library and include files +ZLIBINC = ../zlib +ZLIBLIB = ../zlib + +# Compiler, linker, lib and other tools +CC = gcc +LD = $(CC) +AR_RC = ar rcs +MKDIR_P = mkdir -p +RANLIB = ranlib +RM_F = rm -f +LN_SF = ln -f -s + +LIBNAME=libpng12 +PNGMAJ = 0 +PNGMIN = 1.2.24 +PNGVER = $(PNGMAJ).$(PNGMIN) + +prefix=/usr/local +INCPATH=$(prefix)/include +LIBPATH=$(prefix)/lib + +# override DESTDIR= on the make install command line to easily support +# installing into a temporary location. Example: +# +# make install DESTDIR=/tmp/build/libpng +# +# If you're going to install into a temporary location +# via DESTDIR, $(DESTDIR)$(prefix) must already exist before +# you execute make install. +DESTDIR= + +DI=$(DESTDIR)$(INCPATH) +DL=$(DESTDIR)$(LIBPATH) + +CDEBUG = -g -DPNG_DEBUG=5 +LDDEBUG = +CRELEASE = -O2 +LDRELEASE = -s +WARNMORE=-Wall +CFLAGS = -I$(ZLIBINC) $(WARNMORE) $(CRELEASE) +LDFLAGS = -L. -L$(ZLIBLIB) -lpng12 -lz -lm $(LDRELEASE) + +# File extensions +O=.o +A=.a +E= + +# Variables +OBJS = png$(O) pngerror$(O) pngget$(O) pngmem$(O) pngpread$(O) \ + pngread$(O) pngrio$(O) pngrtran$(O) pngrutil$(O) pngset$(O) \ + pngtrans$(O) pngwio$(O) pngwrite$(O) pngwtran$(O) pngwutil$(O) + +# Targets +all: $(LIBNAME)$(A) pngtest$(E) + +$(LIBNAME)$(A): $(OBJS) + $(AR_RC) $@ $(OBJS) + $(RANLIB) $@ + +test: pngtest$(E) + ./pngtest$(E) + +pngtest$(E): pngtest$(O) $(LIBNAME)$(A) + $(LD) -o $@ pngtest$(O) $(LDFLAGS) + +install: $(LIBNAME)$(A) + -@if [ ! -d $(DI) ]; then $(MKDIR_P) $(DI); fi + -@if [ ! -d $(DI)/$(LIBNAME) ]; then $(MKDIR_P) $(DI)/$(LIBNAME); fi + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + -@$(RM_F) $(DI)/$(LIBNAME)/png.h + -@$(RM_F) $(DI)/$(LIBNAME)/pngconf.h + -@$(RM_F) $(DI)/png.h + -@$(RM_F) $(DI)/pngconf.h + cp png.h pngconf.h $(DI)/$(LIBNAME) + chmod 644 $(DI)/$(LIBNAME)/png.h \ + $(DI)/$(LIBNAME)/pngconf.h + -@$(RM_F) -r $(DI)/libpng + (cd $(DI); $(LN_SF) $(LIBNAME) libpng; $(LN_SF) $(LIBNAME)/* .) + -@$(RM_F) $(DL)/$(LIBNAME)$(A) + -@$(RM_F) $(DL)/libpng$(A) + cp $(LIBNAME)$(A) $(DL)/$(LIBNAME)$(A) + chmod 644 $(DL)/$(LIBNAME)$(A) + (cd $(DL); $(LN_SF) $(LIBNAME)$(A) libpng$(A)) + (cd $(DI); $(LN_SF) libpng/* .;) + +clean: + $(RM_F) *.o $(LIBNAME)$(A) pngtest pngout.png + +png$(O): png.h pngconf.h +pngerror$(O): png.h pngconf.h +pngget$(O): png.h pngconf.h +pngmem$(O): png.h pngconf.h +pngpread$(O): png.h pngconf.h +pngread$(O): png.h pngconf.h +pngrio$(O): png.h pngconf.h +pngrtran$(O): png.h pngconf.h +pngrutil$(O): png.h pngconf.h +pngset$(O): png.h pngconf.h +pngtest$(O): png.h pngconf.h +pngtrans$(O): png.h pngconf.h +pngwio$(O): png.h pngconf.h +pngwrite$(O): png.h pngconf.h +pngwtran$(O): png.h pngconf.h +pngwutil$(O): png.h pngconf.h + diff --git a/Engine/lib/lpng/scripts/makefile.amiga b/Engine/lib/lpng/scripts/makefile.amiga new file mode 100644 index 000000000..79cb42499 --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.amiga @@ -0,0 +1,48 @@ +# Commodore Amiga Makefile +# makefile for libpng and SAS C V6.5x compiler +# Copyright (C) 1995-2000 Wolf Faust +# For conditions of distribution and use, see copyright notice in png.h +# +# Note: Use #define PNG_READ_BIG_ENDIAN_SUPPORTED in pngconf.h +# +# Location/path of zlib include files +ZLIB=/zlib +#compiler +CC=sc +#compiler flags +# WARNING: a bug in V6.51 causes bad code with OPTGO +# So use V6.55 or set NOOPTGO!!!!!!!!! +CFLAGS= NOSTKCHK PARMS=REG OPTIMIZE OPTGO OPTPEEP OPTINLOCAL OPTINL\ + OPTLOOP OPTRDEP=4 OPTDEP=4 OPTCOMP=4 INCLUDEDIR=$(ZLIB) \ + DEFINE=PNG_INTERNAL +#linker flags +LDFLAGS= SD ND BATCH +#link libs +LDLIBS= libpng.lib libgz.lib LIB:scm.lib LIB:sc.lib Lib:amiga.lib +# linker +LN= slink +# file deletion command +RM= delete quiet +# library (.lib) file creation command +AR= oml +# make directory command +MKDIR= makedir + +OBJS = png.o pngset.o pngget.o pngrutil.o pngtrans.o pngwutil.o \ + pngread.o pngrio.o pngwio.o pngwrite.o pngrtran.o \ + pngwtran.o pngmem.o pngerror.o pngpread.o + +all: libpng.lib pngtest + +libpng.lib: $(OBJS) +-$(RM) libpng.lib +$(AR) libpng.lib r $(OBJS) + +pngtest: pngtest.o libpng.lib +$(LN) libpng.pc + +libpng-config: + ( cat scripts/libpng-config-head.in; \ + echo prefix=\"$(prefix)\"; \ + echo I_opts=\"-I$(INCPATH)/$(LIBNAME)\"; \ + echo libs=\"-lpng12 -lz \"; \ + cat scripts/libpng-config-body.in ) > libpng-config + chmod +x libpng-config + +$(LIBSO): $(LIBSOMAJ) + $(LN_SF) $(LIBSOMAJ) $(LIBSO) + cp $(LIBSO)* /boot/home/config/lib + +$(LIBSOMAJ): $(LIBSOVER) + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ) + +$(LIBSOVER): $(OBJSDLL) + $(CC) -nostart -Wl,-soname,$(LIBSOMAJ) -o \ + $(LIBSOVER) $(OBJSDLL) $(LDFLAGS) + +$(OLDSOVER): $(OBJSDLL) + $(CC) -nostart -Wl,-soname,$(OLDSOMAJ) -o \ + $(OLDSOVER) $(OBJSDLL) $(LDFLAGS) + +pngtest: pngtest.o $(LIBSO) + $(CC) -L$(ZLIBLIB) -L. -lz -lpng12 -o pngtest pngtest.o + +test: pngtest + ./pngtest + +install-headers: png.h pngconf.h + -@if [ ! -d $(DI) ]; then $(MKDIR_P) $(DI); fi + -@if [ ! -d $(DI)/$(LIBNAME) ]; then $(MKDIR_P) $(DI)/$(LIBNAME); fi + cp png.h pngconf.h $(DI)/$(LIBNAME) + chmod 644 $(DI)/$(LIBNAME)/png.h $(DI)/$(LIBNAME)/pngconf.h + -@$(RM_F) $(DI)/png.h $(DI)/pngconf.h + -@$(RM_F) $(DI)/libpng + (cd $(DI); $(LN_SF) $(LIBNAME) libpng; $(LN_SF) $(LIBNAME)/* .) + +install-static: install-headers libpng.a + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + cp libpng.a $(DL)/$(LIBNAME).a + chmod 644 $(DL)/$(LIBNAME).a + -@$(RM_F) $(DL)/libpng.a + (cd $(DL); $(LN_SF) $(LIBNAME).a libpng.a) + +install-shared: install-headers $(LIBSOVER) libpng.pc \ + $(OLDSOVER) + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + -@$(RM_F) $(DL)/$(LIBSOVER)* $(DL)/$(LIBSO) + -@$(RM_F) $(DL)/$(LIBSOMAJ) + -@$(RM_F) $(DL)/$(OLDSO) + -@$(RM_F) $(DL)/$(OLDSOMAJ) + -@$(RM_F) $(DL)/$(OLDSOVER)* + cp $(LIBSOVER) $(DL) + cp $(OLDSOVER) $(DL) + chmod 755 $(DL)/$(LIBSOVER) + chmod 755 $(DL)/$(OLDSOVER) + (cd $(DL); \ + $(LN_SF) $(OLDSOVER) $(OLDSOMAJ); \ + $(LN_SF) $(OLDSOMAJ) $(OLDSO); \ + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ); \ + $(LN_SF) $(LIBSOMAJ) $(LIBSO)) + -@if [ ! -d $(DL)/pkgconfig ]; then $(MKDIR_P) $(DL)/pkgconfig; fi + -@$(RM_F) $(DL)/pkgconfig/$(LIBNAME).pc + -@$(RM_F) $(DL)/pkgconfig/libpng.pc + cp libpng.pc $(DL)/pkgconfig/$(LIBNAME).pc + chmod 644 $(DL)/pkgconfig/$(LIBNAME).pc + (cd $(DL)/pkgconfig; $(LN_SF) $(LIBNAME).pc libpng.pc) + +install-man: libpng.3 libpngpf.3 png.5 + -@if [ ! -d $(DM) ]; then $(MKDIR_P) $(DM); fi + -@if [ ! -d $(DM)/man3 ]; then $(MKDIR_P) $(DM)/man3; fi + -@$(RM_F) $(DM)/man3/libpng.3 + -@$(RM_F) $(DM)/man3/libpngpf.3 + cp libpng.3 $(DM)/man3 + cp libpngpf.3 $(DM)/man3 + -@if [ ! -d $(DM)/man5 ]; then $(MKDIR_P) $(DM)/man5; fi + -@$(RM_F) $(DM)/man5/png.5 + cp png.5 $(DM)/man5 + +install-config: libpng-config + -@if [ ! -d $(DB) ]; then $(MKDIR_P) $(DB); fi + -@$(RM_F) $(DB)/libpng-config + -@$(RM_F) $(DB)/$(LIBNAME)-config + cp libpng-config $(DB)/$(LIBNAME)-config + chmod 755 $(DB)/$(LIBNAME)-config + (cd $(DB); $(LN_SF) $(LIBNAME)-config libpng-config) + +install: install-static install-shared install-man install-config + +# If you installed in $(DESTDIR), test-installed won't work until you +# move the library to its final location. Use test-dd to test it +# before then. + +test-dd: + echo + echo Testing installed dynamic shared library in $(DL). + $(CC) -I$(DI) $(CFLAGS) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -L$(DL) -L$(ZLIBLIB) -Wl,-rpath $(ZLIBLIB):$(DL) \ + -o pngtestd `$(BINPATH)/$(LIBNAME)-config --ldflags` + ./pngtestd pngtest.png + +test-installed: + $(CC) $(CFLAGS) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -L$(ZLIBLIB) -Wl,-rpath,$(ZLIBLIB) \ + -o pngtesti `$(BINPATH)/$(LIBNAME)-config --ldflags` + ./pngtesti pngtest.png + +clean: + $(RM_F) *.o libpng.a pngtest pngout.png libpng-config \ + $(LIBSO) $(LIBSOMAJ)* pngtesti \ + $(OLDSOVER) \ + libpng.pc + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +png.o png.pic.o: png.h pngconf.h +pngerror.o pngerror.pic.o: png.h pngconf.h +pngrio.o pngrio.pic.o: png.h pngconf.h +pngwio.o pngwio.pic.o: png.h pngconf.h +pngmem.o pngmem.pic.o: png.h pngconf.h +pngset.o pngset.pic.o: png.h pngconf.h +pngget.o pngget.pic.o: png.h pngconf.h +pngread.o pngread.pic.o: png.h pngconf.h +pngrtran.o pngrtran.pic.o: png.h pngconf.h +pngrutil.o pngrutil.pic.o: png.h pngconf.h +pngtrans.o pngtrans.pic.o: png.h pngconf.h +pngwrite.o pngwrite.pic.o: png.h pngconf.h +pngwtran.o pngwtran.pic.o: png.h pngconf.h +pngwutil.o pngwutil.pic.o: png.h pngconf.h +pngpread.o pngpread.pic.o: png.h pngconf.h +pngtest.o: png.h pngconf.h diff --git a/Engine/lib/lpng/scripts/makefile.bor b/Engine/lib/lpng/scripts/makefile.bor new file mode 100644 index 000000000..a5651aa3f --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.bor @@ -0,0 +1,162 @@ +# Makefile for libpng +# 16-bit Borland C++ (Note: All modules are compiled in C mode) +# To build the library, do: +# "make -fmakefile.bor -DMODEL=c" +# or: "make -fmakefile.bor -DMODEL=l" +# +# ------------ Borland C++ ------------ + +### Absolutely necessary for this makefile to work +.AUTODEPEND + +## Where zlib.h, zconf.h and zlib_MODEL.lib are +ZLIB_DIR=..\zlib + + +## Compiler, linker and lib stuff +CC=bcc +LD=bcc +LIB=tlib + +!ifndef MODEL +MODEL=l +!endif + +MODEL_ARG=-m$(MODEL) + +#TARGET_CPU=3 +# 2 = 286, 3 = 386, etc. +!ifndef TARGET_CPU +TARGET_CPU=2 +!endif + +# Use this if you don't want Borland's fancy exception handling +# (for Borland C++ 4.0 or later) +#NOEHLIB=noeh$(MODEL).lib + +!ifdef DEBUG +CDEBUG=-v +LDEBUG=-v +!else +CDEBUG= +LDEBUG= +!endif + +# STACKOFLOW=1 +!ifdef STACKOFLOW +CDEBUG=$(CDEBUG) -N +LDEBUG=$(LDEBUG) -N +!endif + +# -X- turn on dependency generation in the object file +# -w set all warnings on +# -O2 optimize for speed +# -Z global optimization +CFLAGS=-O2 -Z -X- -w -I$(ZLIB_DIR) -$(TARGET_CPU) $(MODEL_ARG) $(CDEBUG) + +# -M generate map file +LDFLAGS=-M -L$(ZLIB_DIR) $(MODEL_ARG) $(LDEBUG) + + +## Variables +OBJS = \ + png.obj \ + pngerror.obj \ + pngget.obj \ + pngmem.obj \ + pngpread.obj \ + pngread.obj \ + pngrio.obj \ + pngrtran.obj \ + pngrutil.obj \ + pngset.obj \ + pngtrans.obj \ + pngwio.obj \ + pngwrite.obj \ + pngwtran.obj \ + pngwutil.obj + +LIBOBJS = \ + +png.obj \ + +pngerror.obj \ + +pngget.obj \ + +pngmem.obj \ + +pngpread.obj \ + +pngread.obj \ + +pngrio.obj \ + +pngrtran.obj \ + +pngrutil.obj \ + +pngset.obj \ + +pngtrans.obj \ + +pngwio.obj \ + +pngwrite.obj \ + +pngwtran.obj \ + +pngwutil.obj + +LIBNAME=libpng$(MODEL).lib + + +## Implicit rules +# Braces let make "batch" calls to the compiler, +# 2 calls instead of 12; space is important. +.c.obj: + $(CC) $(CFLAGS) -c {$*.c } + +.c.exe: + $(CC) $(CFLAGS) $(LDFLAGS) $*.c $(LIBNAME) zlib_$(MODEL).lib $(NOEHLIB) + + +## Major targets +all: libpng pngtest + +libpng: $(LIBNAME) + +pngtest: pngtest$(MODEL).exe + +test: pngtest$(MODEL).exe + pngtest$(MODEL) + + +## Minor Targets + +png.obj: png.c +pngerror.obj: pngerror.c +pngget.obj: pngget.c +pngmem.obj: pngmem.c +pngpread.obj: pngpread.c +pngread.obj: pngread.c +pngrio.obj: pngrio.c +pngrtran.obj: pngrtran.c +pngrutil.obj: pngrutil.c +pngset.obj: pngset.c +pngtrans.obj: pngtrans.c +pngwio.obj: pngwio.c +pngwrite.obj: pngwrite.c +pngwtran.obj: pngwtran.c +pngwutil.obj: pngwutil.c + + +$(LIBNAME): $(OBJS) + -del $(LIBNAME) + $(LIB) $(LIBNAME) @&&| +$(LIBOBJS), libpng$(MODEL) +| + + +pngtest$(MODEL).obj: pngtest.c + $(CC) $(CFLAGS) -opngtest$(MODEL) -c pngtest.c + +pngtest$(MODEL).exe: pngtest$(MODEL).obj + $(LD) $(LDFLAGS) pngtest$(MODEL).obj $(LIBNAME) zlib_$(MODEL).lib $(NOEHLIB) + + +# Clean up anything else you want +clean: + -del *.obj + -del *.exe + -del *.lib + -del *.lst + -del *.map + + +# End of makefile for libpng diff --git a/Engine/lib/lpng/scripts/makefile.cygwin b/Engine/lib/lpng/scripts/makefile.cygwin new file mode 100644 index 000000000..adfd8c09c --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.cygwin @@ -0,0 +1,299 @@ +# makefile for cygwin on x86 +# Builds both dll (with import lib) and static lib versions +# of the library, and builds two copies of pngtest: one +# statically linked and one dynamically linked. +# +# Copyright (C) 2002, 2006, 2007 Soren Anderson, Charles Wilson, +# and Glenn Randers-Pehrson, based on makefile for linux-elf w/mmx by: +# Copyright (C) 1998-2000 Greg Roelofs +# Copyright (C) 1996, 1997 Andreas Dilger +# For conditions of distribution and use, see copyright notice in png.h + +# This makefile intends to support building outside the src directory +# if desired. When invoking it, specify an argument to SRCDIR on the +# command line that points to the top of the directory where your source +# is located. + +ifdef SRCDIR +VPATH = $(SRCDIR) +else +SRCDIR = . +endif + +# Override DESTDIR= on the make install command line to easily support +# installing into a temporary location. Example: +# +# make install DESTDIR=/tmp/build/libpng +# +# If you're going to install into a temporary location +# via DESTDIR, $(DESTDIR)$(prefix) must already exist before +# you execute make install. + +DESTDIR= + +CC=gcc +ifdef MINGW +MINGW_CCFLAGS=-mno-cygwin -I/usr/include/mingw +MINGW_LDFLAGS=-mno-cygwin -L/usr/lib/mingw +endif + +# Where "make install" puts libpng*.a, *png*.dll, png.h, and pngconf.h +ifndef prefix +prefix=/usr +$(warning You haven't specified a 'prefix=' location. Defaulting to "/usr") +endif +exec_prefix=$(prefix) + +# Where the zlib library and include files are located +ZLIBLIB= /usr/lib +ZLIBINC= +#ZLIBLIB=../zlib +#ZLIBINC=../zlib + +ALIGN= +# for i386: +#ALIGN=-malign-loops=2 -malign-functions=2 + +WARNMORE=-Wwrite-strings -Wpointer-arith -Wshadow \ + -Wmissing-declarations -Wtraditional -Wcast-align \ + -Wstrict-prototypes -Wmissing-prototypes #-Wconversion + +### if you don't need thread safety, but want the asm accel +#CFLAGS= $(strip $(MINGW_CCFLAGS) -DPNG_THREAD_UNSAFE_OK \ +# $(addprefix -I,$(ZLIBINC)) -Wall -O $(ALIGN) -funroll-loops \ +# -fomit-frame-pointer) # $(WARNMORE) -g -DPNG_DEBUG=5 +### if you need thread safety and want (minimal) asm accel +#CFLAGS= $(strip $(MINGW_CCFLAGS) $(addprefix -I,$(ZLIBINC)) \ +# -Wall -O $(ALIGN) -funroll-loops \ +# -fomit-frame-pointer) # $(WARNMORE) -g -DPNG_DEBUG=5 +### Normal (non-asm) compilation +CFLAGS= $(strip $(MINGW_CCFLAGS) $(addprefix -I,$(ZLIBINC)) \ + -Wall -O3 $(ALIGN) -funroll-loops -DPNG_NO_MMX_CODE \ + -fomit-frame-pointer) # $(WARNMORE) -g -DPNG_DEBUG=5 + +LIBNAME = libpng12 +PNGMAJ = 0 +CYGDLL = 12 +PNGMIN = 1.2.24 +PNGVER = $(PNGMAJ).$(PNGMIN) + +SHAREDLIB=cygpng$(CYGDLL).dll +STATLIB=libpng.a +IMPLIB=libpng.dll.a +SHAREDDEF=libpng.def +LIBS=$(SHAREDLIB) $(STATLIB) +EXE=.exe + +LDFLAGS=$(strip -L. $(MINGW_LDFLAGS) -lpng $(addprefix -L,$(ZLIBLIB)) -lz) +LDSFLAGS=$(strip -shared -L. $(MINGW_LDFLAGS) -Wl,--export-all) +LDEXTRA=-Wl,--out-implib=$(IMPLIB) $(addprefix -L,$(ZLIBLIB)) -lz + +MKDIR_P=/bin/mkdir -pv +RANLIB=ranlib +#RANLIB=echo + +INCPATH=$(prefix)/include +LIBPATH=$(exec_prefix)/lib + +BINPATH=$(exec_prefix)/bin +MANPATH=$(prefix)/man +MAN3PATH=$(MANPATH)/man3 +MAN5PATH=$(MANPATH)/man5 + +# cosmetic: shortened strings: +S =$(SRCDIR) +D =$(DESTDIR) +DB =$(D)$(BINPATH) +DI =$(D)$(INCPATH) +DL =$(D)$(LIBPATH) + +OBJS = png.o pngset.o pngget.o pngrutil.o pngtrans.o pngwutil.o \ + pngread.o pngrio.o pngwio.o pngwrite.o pngrtran.o \ + pngwtran.o pngmem.o pngerror.o pngpread.o + +OBJSDLL = $(OBJS:.o=.pic.o) + +.SUFFIXES: .c .o .pic.o + +%.o : %.c + $(CC) -c $(CFLAGS) -o $@ $< +%.pic.o : CFLAGS += -DPNG_BUILD_DLL +%.pic.o : %.c + $(CC) -c $(CFLAGS) -o $@ $< + +all: all-static all-shared libpng.pc libpng-config libpng.pc libpng-config + +# Make this to verify that "make [...] install" will do what you want. +buildsetup-tell: + @echo VPATH is set to: \"$(VPATH)\" + @echo prefix is set to: \"$(prefix)\" + @echo -e INCPATH,LIBPATH, etc. are set to:'\n' \ + $(addprefix $(D),$(INCPATH)'\n' $(LIBPATH)'\n' $(BINPATH)'\n' \ + $(MANPATH)'\n' $(MAN3PATH)'\n' $(MAN5PATH)'\n')'\n' + +libpng.pc: scripts/libpng.pc.in + @echo -e Making pkg-config file for this libpng installation..'\n' \ + using PREFIX=\"$(prefix)\"'\n' + cat scripts/libpng.pc.in | sed -e s!@prefix@!$(prefix)! \ + -e s!@exec_prefix@!$(exec_prefix)! \ + -e s!@libdir@!$(LIBPATH)! \ + -e s!@includedir@!$(INCPATH)! \ + -e s!-lpng12!-lpng12\ -lz! > libpng.pc + +libpng-config: scripts/libpng-config-head.in scripts/libpng-config-body.in + @echo -e Making $(LIBNAME) libpng-config file for this libpng \ + installation..'\n' using PREFIX=\"$(prefix)\"'\n' + ( cat $(S)/scripts/libpng-config-head.in; \ + echo prefix=\"$(prefix)\"; \ + echo I_opts=\"-I$(INCPATH)/$(LIBNAME)\"; \ + echo L_opts=\"-L$(LIBPATH)\"; \ + echo libs=\"-lpng$(CYGDLL) -lz\"; \ + cat $(S)/scripts/libpng-config-body.in ) > libpng-config + chmod +x libpng-config + +static: all-static +shared: all-shared +all-static: $(STATLIB) pngtest-stat$(EXE) +all-shared: $(SHAREDLIB) pngtest$(EXE) + +$(STATLIB): $(OBJS) + ar rc $@ $(OBJS) + $(RANLIB) $@ + +$(SHAREDDEF): scripts/pngw32.def + cat $< | sed -e '1{G;s/^\(.*\)\(\n\)/EXPORTS/;};2,/^EXPORTS/d' | \ + sed -e 's/\([^;]*\);/;/' > $@ + +$(SHAREDLIB): $(OBJSDLL) $(SHAREDDEF) + $(CC) $(LDSFLAGS) -o $@ $(OBJSDLL) -L. $(LDEXTRA) + +pngtest$(EXE): pngtest.pic.o $(SHAREDLIB) + $(CC) $(CFLAGS) $< $(LDFLAGS) -o $@ + +pngtest-stat$(EXE): pngtest.o $(STATLIB) + $(CC) -static $(CFLAGS) $< $(LDFLAGS) -o $@ + +pngtest.pic.o: pngtest.c + $(CC) $(CFLAGS) -c $< -o $@ + +pngtest.o: pngtest.c + $(CC) $(CFLAGS) -c $< -o $@ + +test: test-static test-shared + +test-static: pngtest-stat$(EXE) + ./pngtest-stat $(S)/pngtest.png + +test-shared: pngtest$(EXE) + ./pngtest $(S)/pngtest.png + +install-static: $(STATLIB) install-headers install-man + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + install -m 644 $(STATLIB) $(DL)/$(LIBNAME).a + -@rm -f $(DL)/$(STATLIB) + (cd $(DL); ln -sf $(LIBNAME).a $(STATLIB)) + +install-shared: $(SHAREDLIB) libpng.pc libpng-config install-headers install-man + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + -@if [ ! -d $(DB) ]; then $(MKDIR_P) $(DB); fi + -@if [ ! -d $(DL)/pkgconfig ]; then $(MKDIR_P) $(DL)/pkgconfig; fi + -@/bin/rm -f $(DL)/pkgconfig/$(LIBNAME).pc + -@/bin/rm -f $(DL)/pkgconfig/libpng.pc + install -m 644 $(IMPLIB) $(DL)/$(LIBNAME).dll.a + -@rm -f $(DL)/$(IMPLIB) + (cd $(DL); ln -sf $(LIBNAME).dll.a $(IMPLIB)) + install -s -m 755 $(SHAREDLIB) $(DB) + install -m 644 libpng.pc $(DL)/pkgconfig/$(LIBNAME).pc + (cd $(DL)/pkgconfig; ln -sf $(LIBNAME).pc libpng.pc) + +install-headers: + -@if [ ! -d $(DI) ]; then $(MKDIR_P) $(DI); fi + -@if [ ! -d $(DI)/$(LIBNAME) ]; then $(MKDIR_P) $(DI)/$(LIBNAME); fi + -@rm -f $(DI)/png.h + -@rm -f $(DI)/pngconf.h + install -m 644 $(S)/png.h $(S)/pngconf.h $(DI)/$(LIBNAME) + -@rm -f $(DI)/libpng + (cd $(DI); ln -sf $(LIBNAME) libpng; ln -sf $(LIBNAME)/* .) + +install-man: + -@if [ ! -d $(D)$(MAN3PATH) ]; then $(MKDIR_P) $(D)$(MAN3PATH); fi + -@if [ ! -d $(D)$(MAN5PATH) ]; then $(MKDIR_P) $(D)$(MAN5PATH); fi + install -m 644 $(S)/libpngpf.3 $(S)/libpng.3 $(D)$(MAN3PATH) + install -m 644 $(S)/png.5 $(D)$(MAN5PATH) + +install-config: libpng-config + -@if [ ! -d $(DB) ]; then $(MKDIR_P) $(DB); fi + -@/bin/rm -f $(DB)/libpng-config + -@/bin/rm -f $(DB)/$(LIBNAME)-config + cp libpng-config $(DB)/$(LIBNAME)-config + chmod 755 $(DB)/$(LIBNAME)-config + (cd $(DB); ln -sf $(LIBNAME)-config libpng-config) + +# Run this to verify that a future `configure' run will pick up the settings +# you want. +test-config-install: SHELL=/bin/bash +test-config-install: $(DB)/libpng-config + @echo -e Testing libpng-config functions...'\n' + @ for TYRA in LDFLAGS CPPFLAGS CFLAGS LIBS VERSION; \ + do \ + printf "(%d)\t %10s =%s\n" $$(($$gytiu + 1)) $$TYRA \ + "$$($(DB)/libpng-config `echo --$$TYRA |tr '[:upper:]' '[:lower:]'`)"; \ + gytiu=$$(( $$gytiu + 1 )); \ + done + +install: install-static install-shared install-man install-config + +# If you installed in $(DESTDIR), test-installed won't work until you +# move the library to its final location. Use test-dd to test it +# before then. + +test-dd: + echo + echo Testing installed dynamic shared library in $(DL). + $(CC) -I$(DI) $(CFLAGS) \ + `$(BINPATH)/libpng12-config --cflags` pngtest.c \ + -L$(DL) -L$(ZLIBLIB) \ + -o pngtestd `$(BINPATH)/libpng12-config --ldflags` + ./pngtestd pngtest.png + +test-installed: + $(CC) $(CFLAGS) \ + `$(BINPATH)/libpng12-config --cflags` pngtest.c \ + -L$(ZLIBLIB) \ + -o pngtesti$(EXE) `$(BINPATH)/libpng12-config --ldflags` + ./pngtesti$(EXE) pngtest.png + +clean: + /bin/rm -f *.pic.o *.o $(STATLIB) $(IMPLIB) $(SHAREDLIB) \ + pngtest-stat$(EXE) pngtest$(EXE) pngout.png $(SHAREDDEF) \ + libpng-config libpng.pc pngtesti$(EXE) + +DOCS = ANNOUNCE CHANGES INSTALL KNOWNBUG LICENSE README TODO Y2KINFO +writelock: + chmod a-w *.[ch35] $(DOCS) scripts/* + +.PHONY: buildsetup-tell libpng.pc libpng-config test-config-install clean + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +png.o png.pic.o: png.h pngconf.h png.c +pngerror.o pngerror.pic.o: png.h pngconf.h pngerror.c +pngrio.o pngrio.pic.o: png.h pngconf.h pngrio.c +pngwio.o pngwio.pic.o: png.h pngconf.h pngwio.c +pngmem.o pngmem.pic.o: png.h pngconf.h pngmem.c +pngset.o pngset.pic.o: png.h pngconf.h pngset.c +pngget.o pngget.pic.o: png.h pngconf.h pngget.c +pngread.o pngread.pic.o: png.h pngconf.h pngread.c +pngrtran.o pngrtran.pic.o: png.h pngconf.h pngrtran.c +pngrutil.o pngrutil.pic.o: png.h pngconf.h pngrutil.c +pngtrans.o pngtrans.pic.o: png.h pngconf.h pngtrans.c +pngwrite.o pngwrite.pic.o: png.h pngconf.h pngwrite.c +pngwtran.o pngwtran.pic.o: png.h pngconf.h pngwtran.c +pngwutil.o pngwutil.pic.o: png.h pngconf.h pngwutil.c +pngpread.o pngpread.pic.o: png.h pngconf.h pngpread.c + +pngtest.o: png.h pngconf.h pngtest.c +pngtest-stat.o: png.h pngconf.h pngtest.c + + + diff --git a/Engine/lib/lpng/scripts/makefile.darwin b/Engine/lib/lpng/scripts/makefile.darwin new file mode 100644 index 000000000..5cd98e024 --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.darwin @@ -0,0 +1,234 @@ +# makefile for libpng on Darwin / Mac OS X +# Copyright (C) 2002, 2004, 2006 Glenn Randers-Pehrson +# Copyright (C) 2001 Christoph Pfisterer +# derived from makefile.linux: +# Copyright (C) 1998, 1999 Greg Roelofs +# Copyright (C) 1996, 1997 Andreas Dilger +# For conditions of distribution and use, see copyright notice in png.h + +# where "make install" puts libpng.a, libpng12.dylib, png.h and pngconf.h +prefix=/usr/local +exec_prefix=$(prefix) + +# Where the zlib library and include files are located +#ZLIBLIB=/usr/local/lib +#ZLIBINC=/usr/local/include +ZLIBLIB=../zlib +ZLIBINC=../zlib + +# Library name: +LIBNAME = libpng12 +PNGMAJ = 0 +PNGMIN = 1.2.24 +PNGVER = $(PNGMAJ).$(PNGMIN) + +# Shared library names: +LIBSO=$(LIBNAME).dylib +LIBSOMAJ=$(LIBNAME).$(PNGMAJ).dylib +LIBSOVER=$(LIBNAME).$(PNGVER).dylib +OLDSO=libpng.dylib +OLDSOMAJ=libpng.3.dylib +OLDSOVER=libpng.3.$(PNGMIN).dylib + +# Utilities: +CC=cc +AR_RC=ar rc +MKDIR_P=mkdir -p +LN_SF=ln -sf +RANLIB=ranlib +RM_F=/bin/rm -f + +# CFLAGS=-I$(ZLIBINC) -Wall -O3 -funroll-loops -DPNG_NO_MMX_CODE +CFLAGS=-I$(ZLIBINC) -Wall -O -funroll-loops +LDFLAGS=-L. -L$(ZLIBLIB) -lpng12 -lz + +INCPATH=$(prefix)/include +LIBPATH=$(exec_prefix)/lib +MANPATH=$(prefix)/man +BINPATH=$(exec_prefix)/bin + +# override DESTDIR= on the make install command line to easily support +# installing into a temporary location. Example: +# +# make install DESTDIR=/tmp/build/libpng +# +# If you're going to install into a temporary location +# via DESTDIR, $(DESTDIR)$(prefix) must already exist before +# you execute make install. +DESTDIR= + +DB=$(DESTDIR)$(BINPATH) +DI=$(DESTDIR)$(INCPATH) +DL=$(DESTDIR)$(LIBPATH) +DM=$(DESTDIR)$(MANPATH) + +OBJS = png.o pngset.o pngget.o pngrutil.o pngtrans.o pngwutil.o \ + pngread.o pngrio.o pngwio.o pngwrite.o pngrtran.o \ + pngwtran.o pngmem.o pngerror.o pngpread.o + +OBJSDLL = $(OBJS:.o=.pic.o) + +.SUFFIXES: .c .o .pic.o + +.c.pic.o: + $(CC) -c $(CFLAGS) -fno-common -o $@ $*.c + +all: libpng.a $(LIBSO) pngtest libpng.pc libpng-config + +libpng.a: $(OBJS) + $(AR_RC) $@ $(OBJS) + $(RANLIB) $@ + +libpng.pc: + cat scripts/libpng.pc.in | sed -e s!@prefix@!$(prefix)! \ + -e s!@exec_prefix@!$(exec_prefix)! \ + -e s!@libdir@!$(LIBPATH)! \ + -e s!@includedir@!$(INCPATH)! \ + -e s!-lpng12!-lpng12\ -lz! > libpng.pc + +libpng-config: + ( cat scripts/libpng-config-head.in; \ + echo prefix=\"$(prefix)\"; \ + echo I_opts=\"-I$(INCPATH)/$(LIBNAME)\"; \ + echo L_opts=\"-L$(LIBPATH)\"; \ + echo libs=\"-lpng12 -lz\"; \ + cat scripts/libpng-config-body.in ) > libpng-config + chmod +x libpng-config + +$(LIBSO): $(LIBSOMAJ) + $(LN_SF) $(LIBSOMAJ) $(LIBSO) + +$(LIBSOMAJ): $(LIBSOVER) + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ) + +$(LIBSOVER): $(OBJSDLL) + $(CC) -dynamiclib \ + -install_name $(LIBPATH)/$(LIBSOMAJ) \ + -current_version $(PNGVER) -compatibility_version $(PNGVER) \ + -o $(LIBSOVER) \ + $(OBJSDLL) -L$(ZLIBLIB) -lz + +$(OLDSOVER): $(OBJSDLL) + $(CC) -dynamiclib \ + -install_name $(LIBPATH)/$(OLDSOMAJ) \ + -current_version 3 -compatibility_version 3 \ + -o $(OLDSOVER) \ + $(OBJSDLL) -L$(ZLIBLIB) -lz + +pngtest: pngtest.o $(LIBSO) + $(CC) -o pngtest $(CFLAGS) pngtest.o $(LDFLAGS) + +test: pngtest + ./pngtest + +install-headers: png.h pngconf.h + -@if [ ! -d $(DI) ]; then $(MKDIR_P) $(DI); fi + -@if [ ! -d $(DI)/$(LIBNAME) ]; then $(MKDIR_P) $(DI)/$(LIBNAME); fi + cp png.h pngconf.h $(DI)/$(LIBNAME) + chmod 644 $(DI)/$(LIBNAME)/png.h $(DI)/$(LIBNAME)/pngconf.h + -@$(RM_F) $(DI)/png.h $(DI)/pngconf.h + -@$(RM_F) $(DI)/libpng + (cd $(DI); $(LN_SF) $(LIBNAME) libpng; $(LN_SF) $(LIBNAME)/* .) + +install-static: install-headers libpng.a + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + cp libpng.a $(DL)/$(LIBNAME).a + chmod 644 $(DL)/$(LIBNAME).a + $(RANLIB) $(DL)/$(LIBNAME).a + -@$(RM_F) $(DL)/libpng.a + (cd $(DL); $(LN_SF) $(LIBNAME).a libpng.a) + +install-shared: install-headers $(LIBSO) libpng.pc \ + $(OLDSOVER) + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + -@$(RM_F) $(DL)/$(LIBNAME).$(PNGVER)*.dylib + -@$(RM_F) $(DL)/$(LIBNAME).$(PNGMAJ)*.dylib + -@$(RM_F) $(DL)/$(LIBSO) + -@$(RM_F) $(DL)/$(OLDSO) + -@$(RM_F) $(DL)/$(OLDSOMAJ) + -@$(RM_F) $(DL)/libpng.3.$(PNGMIN)*.dylib + cp $(LIBSOVER) $(DL) + cp $(OLDSOVER) $(DL) + chmod 755 $(DL)/$(LIBSOVER) + chmod 755 $(DL)/$(OLDSOVER) + (cd $(DL); \ + $(LN_SF) $(OLDSOVER) $(OLDSOMAJ); \ + $(LN_SF) $(OLDSOMAJ) $(OLDSO); \ + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ); \ + $(LN_SF) $(LIBSOMAJ) $(LIBSO)) + -@if [ ! -d $(DL)/pkgconfig ]; then $(MKDIR_P) $(DL)/pkgconfig; fi + -@$(RM_F) $(DL)/pkgconfig/$(LIBNAME).pc + -@$(RM_F) $(DL)/pkgconfig/libpng.pc + cp libpng.pc $(DL)/pkgconfig/$(LIBNAME).pc + chmod 644 $(DL)/pkgconfig/$(LIBNAME).pc + (cd $(DL)/pkgconfig; $(LN_SF) $(LIBNAME).pc libpng.pc) + +install-man: libpng.3 libpngpf.3 png.5 + -@if [ ! -d $(DM) ]; then $(MKDIR_P) $(DM); fi + -@if [ ! -d $(DM)/man3 ]; then $(MKDIR_P) $(DM)/man3; fi + -@$(RM_F) $(DM)/man3/libpng.3 + -@$(RM_F) $(DM)/man3/libpngpf.3 + cp libpng.3 $(DM)/man3 + cp libpngpf.3 $(DM)/man3 + -@if [ ! -d $(DM)/man5 ]; then $(MKDIR_P) $(DM)/man5; fi + -@$(RM_F) $(DM)/man5/png.5 + cp png.5 $(DM)/man5 + +install-config: libpng-config + -@if [ ! -d $(DB) ]; then $(MKDIR_P) $(DB); fi + -@$(RM_F) $(DB)/libpng-config + -@$(RM_F) $(DB)/$(LIBNAME)-config + cp libpng-config $(DB)/$(LIBNAME)-config + chmod 755 $(DB)/$(LIBNAME)-config + (cd $(DB); $(LN_SF) $(LIBNAME)-config libpng-config) + +install: install-static install-shared install-man install-config + +# If you installed in $(DESTDIR), test-installed won't work until you +# move the library to its final location. Use test-dd to test it +# before then. + +test-dd: + echo + echo Testing installed dynamic shared library in $(DL). + $(CC) -I$(DI) -I$(ZLIBINC) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -L$(DL) -L$(ZLIBLIB) \ + -o pngtestd `$(BINPATH)/$(LIBNAME)-config --ldflags` + ./pngtestd pngtest.png + +test-installed: + $(CC) $(CFLAGS) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -L$(ZLIBLIB) \ + -o pngtesti `$(BINPATH)/$(LIBNAME)-config --ldflags` + ./pngtesti pngtest.png + +clean: + $(RM_F) *.o libpng.a pngtest pngout.png libpng-config \ + $(OLDSOVER) \ + libpng.pc $(LIBNAME).*dylib pngtesti + +DOCS = ANNOUNCE CHANGES INSTALL KNOWNBUG LICENSE README TODO Y2KINFO +writelock: + chmod a-w *.[ch35] $(DOCS) scripts/* + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +png.o png.pic.o: png.h pngconf.h +pngerror.o pngerror.pic.o: png.h pngconf.h +pngrio.o pngrio.pic.o: png.h pngconf.h +pngwio.o pngwio.pic.o: png.h pngconf.h +pngmem.o pngmem.pic.o: png.h pngconf.h +pngset.o pngset.pic.o: png.h pngconf.h +pngget.o pngget.pic.o: png.h pngconf.h +pngread.o pngread.pic.o: png.h pngconf.h +pngrtran.o pngrtran.pic.o: png.h pngconf.h +pngrutil.o pngrutil.pic.o: png.h pngconf.h +pngtrans.o pngtrans.pic.o: png.h pngconf.h +pngwrite.o pngwrite.pic.o: png.h pngconf.h +pngwtran.o pngwtran.pic.o: png.h pngconf.h +pngwutil.o pngwutil.pic.o: png.h pngconf.h +pngpread.o pngpread.pic.o: png.h pngconf.h + +pngtest.o: png.h pngconf.h diff --git a/Engine/lib/lpng/scripts/makefile.dec b/Engine/lib/lpng/scripts/makefile.dec new file mode 100644 index 000000000..baded2c97 --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.dec @@ -0,0 +1,214 @@ +# makefile for libpng on DEC Alpha Unix +# Copyright (C) 2000-2002, 2006 Glenn Randers-Pehrson +# Copyright (C) 1995 Guy Eric Schalnat, Group 42, Inc. +# For conditions of distribution and use, see copyright notice in png.h + +# Library name: +PNGMAJ = 0 +PNGMIN = 1.2.24 +PNGVER = $(PNGMAJ).$(PNGMIN) +LIBNAME = libpng12 + +# Shared library names: +LIBSO=$(LIBNAME).so +LIBSOMAJ=$(LIBNAME).so.$(PNGMAJ) +LIBSOVER=$(LIBNAME).so.$(PNGVER) +OLDSO=libpng.so +OLDSOMAJ=libpng.so.3 +OLDSOVER=libpng.so.3.$(PNGMIN) + +# Utilities: +AR_RC=ar rc +CC=cc +MKDIR_P=mkdir +LN_SF=ln -f -s +RANLIB=ranlib +RM_F=/bin/rm -f + +# where make install puts libpng.a and png.h +prefix=/usr/local +exec_prefix=$(prefix) +INCPATH=$(prefix)/include +LIBPATH=$(exec_prefix)/lib +MANPATH=$(prefix)/man +BINPATH=$(exec_prefix)/bin + +# override DESTDIR= on the make install command line to easily support +# installing into a temporary location. Example: +# +# make install DESTDIR=/tmp/build/libpng +# +# If you're going to install into a temporary location +# via DESTDIR, $(DESTDIR)$(prefix) must already exist before +# you execute make install. +DESTDIR= + +DB=$(DESTDIR)$(BINPATH) +DI=$(DESTDIR)$(INCPATH) +DL=$(DESTDIR)$(LIBPATH) +DM=$(DESTDIR)$(MANPATH) + +# Where the zlib library and include files are located +#ZLIBLIB=/usr/local/lib +#ZLIBINC=/usr/local/include +ZLIBLIB=../zlib +ZLIBINC=../zlib + +CFLAGS=-std -w1 -I$(ZLIBINC) -O # -g -DPNG_DEBUG=1 +LDFLAGS=-L$(ZLIBLIB) -rpath $(ZLIBLIB) libpng.a -lz -lm + +OBJS = png.o pngset.o pngget.o pngrutil.o pngtrans.o pngwutil.o \ + pngread.o pngrio.o pngwio.o pngwrite.o pngrtran.o \ + pngwtran.o pngmem.o pngerror.o pngpread.o + +all: $(LIBSO) libpng.a pngtest libpng.pc libpng-config + +libpng.a: $(OBJS) + $(AR_RC) $@ $(OBJS) + $(RANLIB) $@ + +libpng.pc: + cat scripts/libpng.pc.in | sed -e s!@prefix@!$(prefix)! \ + -e s!@exec_prefix@!$(exec_prefix)! \ + -e s!@libdir@!$(LIBPATH)! \ + -e s!@includedir@!$(INCPATH)! \ + -e s!-lpng12!-lpng12\ -lz\ -lm! > libpng.pc + +libpng-config: + ( cat scripts/libpng-config-head.in; \ + echo prefix=\"$(prefix)\"; \ + echo I_opts=\"-I$(INCPATH)/$(LIBNAME)\"; \ + echo ccopts=\"-std\"; \ + echo L_opts=\"-L$(LIBPATH)\"; \ + echo libs=\"-lpng12 -lz -lm\"; \ + cat scripts/libpng-config-body.in ) > libpng-config + chmod +x libpng-config + +$(LIBSO): $(LIBSOMAJ) + $(LN_SF) $(LIBSOMAJ) $(LIBSO) + +$(LIBSOMAJ): $(LIBSOVER) + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ) + +$(LIBSOVER): $(OBJS) + $(CC) -shared -o $@ $(OBJS) -L$(ZLIBLIB) \ + -soname $(LIBSOMAJ) + +$(OLDSOVER): $(OBJS) + $(CC) -shared -o $@ $(OBJS) -L$(ZLIBLIB) \ + -soname $(OLDSOMAJ) + +pngtest: pngtest.o libpng.a + $(CC) -o pngtest $(CFLAGS) pngtest.o $(LDFLAGS) + +test: pngtest + ./pngtest + +install-headers: png.h pngconf.h + -@if [ ! -d $(DI) ]; then $(MKDIR_P) $(DI); fi + -@if [ ! -d $(DI)/$(LIBNAME) ]; then $(MKDIR_P) $(DI)/$(LIBNAME); fi + cp png.h pngconf.h $(DI)/$(LIBNAME) + chmod 644 $(DI)/$(LIBNAME)/png.h $(DI)/$(LIBNAME)/pngconf.h + -@/bin/rm -f $(DI)/png.h $(DI)/pngconf.h + -@/bin/rm -f $(DI)/libpng + (cd $(DI); $(LN_SF)(LIBNAME) libpng; $(LN_SF)(LIBNAME)/* .) + +install-static: install-headers libpng.a + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + cp libpng.a $(DL)/$(LIBNAME).a + chmod 644 $(DL)/$(LIBNAME).a + -@/bin/rm -f $(DL)/libpng.a + (cd $(DL); $(LN_SF)(LIBNAME).a libpng.a) + +install-shared: install-headers $(LIBSOVER) libpng.pc \ + $(OLDSOVER) + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + -@/bin/rm -f $(DL)/$(LIBSOVER)* $(DL)/$(LIBSO) + -@/bin/rm -f $(DL)/$(LIBSOMAJ) + -@/bin/rm -f $(DL)/$(OLDSO) + -@/bin/rm -f $(DL)/$(OLDSOMAJ) + -@/bin/rm -f $(DL)/$(OLDSOVER)* + cp $(LIBSOVER) $(DL) + cp $(OLDSOVER) $(DL) + chmod 755 $(DL)/$(LIBSOVER) + chmod 755 $(DL)/$(OLDSOVER) + (cd $(DL); \ + $(LN_SF) $(OLDSOVER) $(OLDSOMAJ); \ + $(LN_SF) $(OLDSOMAJ) $(OLDSO); \ + $(LN_SF)(LIBSOVER) $(LIBSOMAJ); \ + $(LN_SF)(LIBSOMAJ) $(LIBSO)) + -@if [ ! -d $(DL)/pkgconfig ]; then $(MKDIR_P) $(DL)/pkgconfig; fi + -@/bin/rm -f $(DL)/pkgconfig/$(LIBNAME).pc + -@/bin/rm -f $(DL)/pkgconfig/libpng.pc + cp libpng.pc $(DL)/pkgconfig/$(LIBNAME).pc + chmod 644 $(DL)/pkgconfig/$(LIBNAME).pc + (cd $(DL)/pkgconfig; $(LN_SF)(LIBNAME).pc libpng.pc) + +install-man: libpng.3 libpngpf.3 png.5 + -@if [ ! -d $(DM) ]; then $(MKDIR_P) $(DM); fi + -@if [ ! -d $(DM)/man3 ]; then $(MKDIR_P) $(DM)/man3; fi + -@/bin/rm -f $(DM)/man3/libpng.3 + -@/bin/rm -f $(DM)/man3/libpngpf.3 + cp libpng.3 $(DM)/man3 + cp libpngpf.3 $(DM)/man3 + -@if [ ! -d $(DM)/man5 ]; then $(MKDIR_P) $(DM)/man5; fi + -@/bin/rm -f $(DM)/man5/png.5 + cp png.5 $(DM)/man5 + +install-config: libpng-config + -@if [ ! -d $(DB) ]; then $(MKDIR_P) $(DB); fi + -@/bin/rm -f $(DB)/libpng-config + -@/bin/rm -f $(DB)/$(LIBNAME)-config + cp libpng-config $(DB)/$(LIBNAME)-config + chmod 755 $(DB)/$(LIBNAME)-config + (cd $(DB); $(LN_SF)(LIBNAME)-config libpng-config) + +install: install-static install-shared install-man install-config + +# If you installed in $(DESTDIR), test-installed won't work until you +# move the library to its final location. Use test-dd to test it +# before then. + +test-dd: + echo + echo Testing installed dynamic shared library in $(DL). + $(CC) -w1 -I$(DI) -I$(ZLIBINC) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -L$(DL) -L$(ZLIBLIB) -R$(ZLIBLIB) -R$(DL) \ + -o pngtestd `$(BINPATH)/$(LIBNAME)-config --ldflags` + ./pngtestd pngtest.png + +test-installed: + echo + echo Testing installed dynamic shared library. + $(CC) -w1 -I$(ZLIBINC) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -L$(ZLIBLIB) -R$(ZLIBLIB) \ + -o pngtesti `$(BINPATH)/$(LIBNAME)-config --ldflags` + ./pngtesti pngtest.png + +clean: + /bin/rm -f *.o libpng.a pngtest pngtesti pngout.png \ + libpng-config $(LIBSO) $(LIBSOMAJ)* \ + $(OLDSOVER) \ + libpng.pc + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +png.o: png.h pngconf.h +pngerror.o: png.h pngconf.h +pngrio.o: png.h pngconf.h +pngwio.o: png.h pngconf.h +pngmem.o: png.h pngconf.h +pngset.o: png.h pngconf.h +pngget.o: png.h pngconf.h +pngread.o: png.h pngconf.h +pngrtran.o: png.h pngconf.h +pngrutil.o: png.h pngconf.h +pngtest.o: png.h pngconf.h +pngtrans.o: png.h pngconf.h +pngwrite.o: png.h pngconf.h +pngwtran.o: png.h pngconf.h +pngwutil.o: png.h pngconf.h +pngpread.o: png.h pngconf.h + diff --git a/Engine/lib/lpng/scripts/makefile.dj2 b/Engine/lib/lpng/scripts/makefile.dj2 new file mode 100644 index 000000000..09045c275 --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.dj2 @@ -0,0 +1,55 @@ +# DJGPP (DOS gcc) makefile for libpng +# Copyright (C) 2002 Glenn Randers-Pehrson +# Copyright (C) 1995 Guy Eric Schalnat, Group 42, Inc. +# For conditions of distribution and use, see copyright notice in png.h + +# where make install will put libpng.a and png.h +#prefix=/usr/local +prefix=. +INCPATH=$(prefix)/include +LIBPATH=$(prefix)/lib + +CC=gcc +CFLAGS=-I../zlib -O +LDFLAGS=-L. -L../zlib/ -lpng -lz -lm + +RANLIB=ranlib + +OBJS = png.o pngset.o pngget.o pngrutil.o pngtrans.o pngwutil.o \ + pngread.o pngrio.o pngwio.o pngwrite.o pngrtran.o pngwtran.o \ + pngmem.o pngerror.o pngpread.o + +all: libpng.a pngtest + +libpng.a: $(OBJS) + ar rc $@ $(OBJS) + $(RANLIB) $@ + +pngtest: pngtest.o libpng.a + $(CC) -o pngtest $(CFLAGS) pngtest.o $(LDFLAGS) + coff2exe pngtest + +test: pngtest + ./pngtest +clean: + rm -f *.o libpng.a pngtest pngout.png + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +png.o: png.h pngconf.h +pngerror.o: png.h pngconf.h +pngrio.o: png.h pngconf.h +pngwio.o: png.h pngconf.h +pngmem.o: png.h pngconf.h +pngset.o: png.h pngconf.h +pngget.o: png.h pngconf.h +pngread.o: png.h pngconf.h +pngpread.o: png.h pngconf.h +pngrtran.o: png.h pngconf.h +pngrutil.o: png.h pngconf.h +pngtest.o: png.h pngconf.h +pngtrans.o: png.h pngconf.h +pngwrite.o: png.h pngconf.h +pngwtran.o: png.h pngconf.h +pngwutil.o: png.h pngconf.h + diff --git a/Engine/lib/lpng/scripts/makefile.elf b/Engine/lib/lpng/scripts/makefile.elf new file mode 100644 index 000000000..a6cae7ed3 --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.elf @@ -0,0 +1,275 @@ +# makefile for libpng.a and libpng12.so on Linux ELF with gcc +# Copyright (C) 1998, 1999, 2002, 2006 Greg Roelofs and Glenn Randers-Pehrson +# Copyright (C) 1996, 1997 Andreas Dilger +# For conditions of distribution and use, see copyright notice in png.h + +# Modified for Debian by Junichi Uekawa and Josselin Mouette +# Major modifications are: +# * link libpng explicitly with libz and libm +# * $(OLDSO).3 is a symlink rather than a different library +# * versioned symbols + +# Library name: +LIBNAME = libpng12 +PNGMAJ = 0 +PNGMIN = 1.2.24 +PNGVER = $(PNGMAJ).$(PNGMIN) + +# Shared library names: +LIBSO=$(LIBNAME).so +LIBSOMAJ=$(LIBNAME).so.$(PNGMAJ) +LIBSOVER=$(LIBNAME).so.$(PNGVER) +OLDSO=libpng.so +OLDSOMAJ=libpng.so.3 +OLDSOVER=libpng.so.3.$(PNGMIN) + +# Utilities: +AR_RC=ar rc +CC=gcc +MKDIR_P=mkdir -p +LN_SF=ln -sf +RANLIB=ranlib +RM_F=/bin/rm -f + +# where "make install" puts libpng12.a, libpng12.so*, +# libpng12/png.h and libpng12/pngconf.h +# Prefix must be a full pathname. +prefix=/usr/local +exec_prefix=$(prefix) + +# Where the zlib library and include files are located. +ZLIBLIB=/usr/local/lib +ZLIBINC=/usr/local/include +# ZLIBLIB=../zlib +# ZLIBINC=../zlib + +ALIGN= +# for i386: +#ALIGN=-malign-loops=2 -malign-functions=2 + +WARNMORE=-Wwrite-strings -Wpointer-arith -Wshadow \ + -Wmissing-declarations -Wtraditional -Wcast-align \ + -Wstrict-prototypes -Wmissing-prototypes #-Wconversion + +# for pgcc version 2.95.1, -O3 is buggy; don't use it. + +CFLAGS=-Wall -D_REENTRANT -O2 \ + $(ALIGN) # $(WARNMORE) -g -DPNG_DEBUG=5 + +LDFLAGS=-L. -lpng12 +LDFLAGS_A=libpng.a -lz -lm +LIBADDFLAGS=-lz -lm + + +INCPATH=$(prefix)/include +LIBPATH=$(exec_prefix)/lib +MANPATH=$(prefix)/man +BINPATH=$(exec_prefix)/bin + +# override DESTDIR= on the make install command line to easily support +# installing into a temporary location. Example: +# +# make install DESTDIR=/tmp/build/libpng +# +# If you're going to install into a temporary location +# via DESTDIR, $(DESTDIR)$(prefix) must already exist before +# you execute make install. +DESTDIR= + +DB=$(DESTDIR)$(BINPATH) +DI=$(DESTDIR)$(INCPATH) +DL=$(DESTDIR)$(LIBPATH) +DM=$(DESTDIR)$(MANPATH) + +OBJS = png.o pngset.o pngget.o pngrutil.o pngtrans.o pngwutil.o \ + pngread.o pngrio.o pngwio.o pngwrite.o pngrtran.o \ + pngwtran.o pngmem.o pngerror.o pngpread.o + +OBJSDLL = $(OBJS:.o=.pic.o) + +.SUFFIXES: .c .o .pic.o + +.c.pic.o: + $(CC) -c $(CFLAGS) -fPIC -o $@ $*.c + +all: libpng.a $(LIBSO) pngtest pngtest-static libpng.pc libpng-config + +libpng.a: $(OBJS) + $(AR_RC) $@ $(OBJS) + $(RANLIB) $@ + +libpng.pc: + cat scripts/libpng.pc.in | sed -e s!@prefix@!$(prefix)! \ + -e s!@exec_prefix@!$(exec_prefix)! \ + -e s!@libdir@!$(LIBPATH)! \ + -e s!@includedir@!$(INCPATH)! \ + -e s!-lpng12!-lpng12\ -lz\ -lm! > libpng.pc + +libpng.syms: png.h pngconf.h + $(CC) $(CFLAGS) -E -DPNG_BUILDSYMS -DPNG_INTERNAL png.h |\ + awk -F '[\t [\\]();]' -v PNGMAJ=$(PNGMAJ) 'BEGIN{printf("PNG12_%s {global:\n",PNGMAJ)}\ + { for (i=1;i+2<=NF;++i)\ + if ($$(i)=="PNG_FUNCTION_EXPORT" && $$(i+2)=="END")\ + print $$(i+1) ";";\ + for (i=1;i+1<=NF;++i)\ + if ($$(i)=="PNG_DATA_EXPORT")\ + print $$(i+1) ";";}\ + END{print "local: *; };"}' >$@.new + $(RM_F) $@ + mv $@.new $@ + +libpng-config: + ( cat scripts/libpng-config-head.in; \ + echo prefix=\"$(prefix)\"; \ + echo I_opts=\"-I$(INCPATH)/$(LIBNAME)\"; \ + echo L_opts=\"\"; \ + echo R_opts=\"\"; \ + echo libs=\"-lpng12\"; \ + echo all_libs=\"-lpng12 $(LIBADDFLAGS)\"; \ + cat scripts/libpng-config-body.in ) > libpng-config + chmod +x libpng-config + +$(LIBSO): $(LIBSOMAJ) + $(LN_SF) $(LIBSOMAJ) $(LIBSO) + +$(LIBSOMAJ): $(LIBSOVER) + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ) + +$(LIBSOVER): $(OBJSDLL) libpng.syms + $(CC) -shared -Wl,-soname,$(LIBSOMAJ) \ + -Wl,-version-script,libpng.syms \ + -o $(LIBSOVER) \ + $(OBJSDLL) + +$(OLDSOVER): $(OBJSDLL) libpng.syms + $(CC) -shared -Wl,-soname,$(OLDSOMAJ) \ + -Wl,-version-script,libpng.syms \ + -o $(OLDSOVER) \ + $(OBJSDLL) + +pngtest: pngtest.o $(LIBSO) + $(CC) -o pngtest $(CFLAGS) pngtest.o $(LDFLAGS) + +pngtest-static: pngtest.o libpng.a + $(CC) -o pngtest-static $(CFLAGS) pngtest.o $(LDFLAGS_A) + +test: pngtest pngtest-static + @echo "" + @echo " Running pngtest dynamically linked with $(LIBSO):" + @echo "" + LD_LIBRARY_PATH=".:${LD_LIBRARY_PATH}" ./pngtest + @echo "" + @echo " Running pngtest statically linked with libpng.a:" + @echo "" + ./pngtest-static + +install-headers: png.h pngconf.h + -@if [ ! -d $(DI) ]; then $(MKDIR_P) $(DI); fi + -@if [ ! -d $(DI)/$(LIBNAME) ]; then $(MKDIR_P) $(DI)/$(LIBNAME); fi + cp png.h pngconf.h $(DI)/$(LIBNAME) + chmod 644 $(DI)/$(LIBNAME)/png.h $(DI)/$(LIBNAME)/pngconf.h + -@$(RM_F) $(DI)/png.h $(DI)/pngconf.h + -@$(RM_F) $(DI)/libpng + (cd $(DI); $(LN_SF) $(LIBNAME) libpng; $(LN_SF) $(LIBNAME)/* .) + +install-static: install-headers libpng.a + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + cp libpng.a $(DL)/$(LIBNAME).a + chmod 644 $(DL)/$(LIBNAME).a + -@$(RM_F) $(DL)/libpng.a + (cd $(DL); $(LN_SF) $(LIBNAME).a libpng.a) + +install-shared: install-headers $(LIBSOVER) libpng.pc \ + $(OLDSOVER) + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + -@$(RM_F) $(DL)/$(LIBSOVER)* $(DL)/$(LIBSO) + -@$(RM_F) $(DL)/$(LIBSOMAJ) + -@$(RM_F) $(DL)/$(OLDSO) + -@$(RM_F) $(DL)/$(OLDSOMAJ) + -@$(RM_F) $(DL)/$(OLDSOVER)* + cp $(LIBSOVER) $(DL) + cp $(OLDSOVER) $(DL) + chmod 755 $(DL)/$(LIBSOVER) + chmod 755 $(DL)/$(OLDSOVER) + (cd $(DL); \ + $(LN_SF) $(OLDSOVER) $(OLDSOMAJ); \ + $(LN_SF) $(OLDSOMAJ) $(OLDSO); \ + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ); \ + $(LN_SF) $(LIBSOMAJ) $(LIBSO)) + -@if [ ! -d $(DL)/pkgconfig ]; then $(MKDIR_P) $(DL)/pkgconfig; fi + -@$(RM_F) $(DL)/pkgconfig/$(LIBNAME).pc + -@$(RM_F) $(DL)/pkgconfig/libpng.pc + cp libpng.pc $(DL)/pkgconfig/$(LIBNAME).pc + chmod 644 $(DL)/pkgconfig/$(LIBNAME).pc + (cd $(DL)/pkgconfig; $(LN_SF) $(LIBNAME).pc libpng.pc) + +install-man: libpng.3 libpngpf.3 png.5 + -@if [ ! -d $(DM) ]; then $(MKDIR_P) $(DM); fi + -@if [ ! -d $(DM)/man3 ]; then $(MKDIR_P) $(DM)/man3; fi + -@$(RM_F) $(DM)/man3/libpng.3 + -@$(RM_F) $(DM)/man3/libpngpf.3 + cp libpng.3 $(DM)/man3 + cp libpngpf.3 $(DM)/man3 + -@if [ ! -d $(DM)/man5 ]; then $(MKDIR_P) $(DM)/man5; fi + -@$(RM_F) $(DM)/man5/png.5 + cp png.5 $(DM)/man5 + +install-config: libpng-config + -@if [ ! -d $(DB) ]; then $(MKDIR_P) $(DB); fi + -@$(RM_F) $(DB)/libpng-config + -@$(RM_F) $(DB)/$(LIBNAME)-config + cp libpng-config $(DB)/$(LIBNAME)-config + chmod 755 $(DB)/$(LIBNAME)-config + (cd $(DB); $(LN_SF) $(LIBNAME)-config libpng-config) + +install: install-static install-shared install-man install-config + +# If you installed in $(DESTDIR), test-installed won't work until you +# move the library to its final location. Use test-dd to test it +# before then. + +test-dd: + echo + echo Testing installed dynamic shared library in $(DL). + $(CC) -I$(DI) -I$(ZLIBINC) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -L$(DL) -L$(ZLIBLIB) -Wl, -rpath,$(DL) -Wl,-rpath,$(ZLIBLIB) \ + -o pngtestd `$(BINPATH)/$(LIBNAME)-config --ldflags` + ./pngtestd pngtest.png + +test-installed: + $(CC) -I$(ZLIBINC) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -L$(ZLIBLIB) -Wl,-rpath,$(ZLIBLIB) \ + -o pngtesti `$(BINPATH)/$(LIBNAME)-config --ldflags` + ./pngtesti pngtest.png + +clean: + $(RM_F) *.o libpng.a libpng.syms pngtest pngout.png libpng-config \ + $(LIBSO) $(LIBSOMAJ)* pngtest-static pngtesti \ + $(OLDSOVER) \ + libpng.pc + +DOCS = ANNOUNCE CHANGES INSTALL KNOWNBUG LICENSE README TODO Y2KINFO +writelock: + chmod a-w *.[ch35] $(DOCS) scripts/* + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +png.o png.pic.o: png.h pngconf.h +pngerror.o pngerror.pic.o: png.h pngconf.h +pngrio.o pngrio.pic.o: png.h pngconf.h +pngwio.o pngwio.pic.o: png.h pngconf.h +pngmem.o pngmem.pic.o: png.h pngconf.h +pngset.o pngset.pic.o: png.h pngconf.h +pngget.o pngget.pic.o: png.h pngconf.h +pngread.o pngread.pic.o: png.h pngconf.h +pngrtran.o pngrtran.pic.o: png.h pngconf.h +pngrutil.o pngrutil.pic.o: png.h pngconf.h +pngtrans.o pngtrans.pic.o: png.h pngconf.h +pngwrite.o pngwrite.pic.o: png.h pngconf.h +pngwtran.o pngwtran.pic.o: png.h pngconf.h +pngwutil.o pngwutil.pic.o: png.h pngconf.h +pngpread.o pngpread.pic.o: png.h pngconf.h + +pngtest.o: png.h pngconf.h diff --git a/Engine/lib/lpng/scripts/makefile.freebsd b/Engine/lib/lpng/scripts/makefile.freebsd new file mode 100644 index 000000000..59f36444f --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.freebsd @@ -0,0 +1,48 @@ +# makefile for libpng under FreeBSD +# Copyright (C) 2002, 2007 Glenn Randers-Pehrson and Andrey A. Chernov +# For conditions of distribution and use, see copyright notice in png.h + +PREFIX?= /usr/local +SHLIB_VER?= 5 + +LIB= png +SHLIB_MAJOR= ${SHLIB_VER} +SHLIB_MINOR= 0 +NOPROFILE= YES +NOOBJ= YES + +# where make install puts libpng.a and png.h +DESTDIR= ${PREFIX} +LIBDIR= /lib +INCS= png.h pngconf.h +INCSDIR= /include/libpng +INCDIR= ${INCSDIR} # for 4.x bsd.lib.mk +MAN= libpng.3 libpngpf.3 png.5 +MANDIR= /man/man +SYMLINKS= libpng/png.h ${INCSDIR}/../png.h \ + libpng/pngconf.h ${INCSDIR}/../pngconf.h +LDADD+= -lm -lz +DPADD+= ${LIBM} ${LIBZ} + +CFLAGS+= -I. +.if (${MACHINE_ARCH} != "i386") +CFLAGS+= -DPNG_NO_MMX_CODE +.endif + +SRCS= png.c pngset.c pngget.c pngrutil.c pngtrans.c pngwutil.c \ + pngread.c pngrio.c pngwio.c pngwrite.c pngrtran.c \ + pngwtran.c pngmem.c pngerror.c pngpread.c + +pngtest: pngtest.o libpng.a + ${CC} ${CFLAGS} -L. -static -o pngtest pngtest.o -lpng -lz -lm + +CLEANFILES= pngtest pngtest.o pngout.png + +test: pngtest + ./pngtest + +DOCS = ANNOUNCE CHANGES INSTALL KNOWNBUG LICENSE README TODO Y2KINFO +writelock: + chmod a-w *.[ch35] $(DOCS) scripts/* + +.include diff --git a/Engine/lib/lpng/scripts/makefile.gcc b/Engine/lib/lpng/scripts/makefile.gcc new file mode 100644 index 000000000..e899b108e --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.gcc @@ -0,0 +1,79 @@ +# makefile for libpng using gcc (generic, static library) +# Copyright (C) 2000 Cosmin Truta +# Copyright (C) 1995 Guy Eric Schalnat, Group 42, Inc. +# For conditions of distribution and use, see copyright notice in png.h + +# Location of the zlib library and include files +ZLIBINC = ../zlib +ZLIBLIB = ../zlib + +# Compiler, linker, lib and other tools +CC = gcc +LD = $(CC) +AR_RC = ar rcs +RANLIB = ranlib +RM_F = rm -f + +CDEBUG = -g -DPNG_DEBUG=5 +LDDEBUG = +CRELEASE = -O2 +LDRELEASE = -s +#CFLAGS = -Wall $(CDEBUG) +CFLAGS = -Wall $(CRELEASE) +#LDFLAGS = $(LDDEBUG) +LDFLAGS = $(LDRELEASE) +LIBS = -lz -lm + +# File extensions +O=.o +A=.a +EXE= + +# Variables +OBJS = png$(O) pngerror$(O) pngget$(O) pngmem$(O) pngpread$(O) \ + pngread$(O) pngrio$(O) pngrtran$(O) pngrutil$(O) pngset$(O) \ + pngtrans$(O) pngwio$(O) pngwrite$(O) pngwtran$(O) pngwutil$(O) + +# Targets +all: static + +.c$(O): + $(CC) -c $(CFLAGS) -I$(ZLIBINC) $< + +static: libpng$(A) pngtest$(EXE) + +shared: + @echo This is a generic makefile that cannot create shared libraries. + @echo Please use a configuration that is specific to your platform. + @false + +libpng$(A): $(OBJS) + $(AR_RC) $@ $(OBJS) + $(RANLIB) $@ + +test: pngtest$(EXE) + ./pngtest$(EXE) + +pngtest$(EXE): pngtest$(O) libpng$(A) + $(LD) $(LDFLAGS) -L$(ZLIBLIB) -o $@ pngtest$(O) libpng$(A) $(LIBS) + +clean: + $(RM_F) *$(O) libpng$(A) pngtest$(EXE) pngout.png + +png$(O): png.h pngconf.h +pngerror$(O): png.h pngconf.h +pngget$(O): png.h pngconf.h +pngmem$(O): png.h pngconf.h +pngpread$(O): png.h pngconf.h +pngread$(O): png.h pngconf.h +pngrio$(O): png.h pngconf.h +pngrtran$(O): png.h pngconf.h +pngrutil$(O): png.h pngconf.h +pngset$(O): png.h pngconf.h +pngtest$(O): png.h pngconf.h +pngtrans$(O): png.h pngconf.h +pngwio$(O): png.h pngconf.h +pngwrite$(O): png.h pngconf.h +pngwtran$(O): png.h pngconf.h +pngwutil$(O): png.h pngconf.h + diff --git a/Engine/lib/lpng/scripts/makefile.gcmmx b/Engine/lib/lpng/scripts/makefile.gcmmx new file mode 100644 index 000000000..c5ca4bc07 --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.gcmmx @@ -0,0 +1,271 @@ +# makefile for libpng.a and libpng12.so on Linux ELF with gcc using MMX +# assembler code +# Copyright 2002, 2006 Greg Roelofs and Glenn Randers-Pehrson +# Copyright 1998-2001 Greg Roelofs +# Copyright 1996-1997 Andreas Dilger +# For conditions of distribution and use, see copyright notice in png.h + +# CAUTION: Do not use this makefile with gcc versions 2.7.2.2 and earlier. + +# NOTE: When testing MMX performance on a multitasking system, make sure +# there are no floating-point programs (e.g., SETI@Home) running in +# the background! Context switches between MMX and FPU are expensive. + +# Library name: +LIBNAME = libpng12 +PNGMAJ = 0 +PNGMIN = 1.2.24 +PNGVER = $(PNGMAJ).$(PNGMIN) + +# Shared library names: +LIBSO=$(LIBNAME).so +LIBSOMAJ=$(LIBNAME).so.$(PNGMAJ) +LIBSOVER=$(LIBNAME).so.$(PNGVER) +OLDSO=libpng.so +OLDSOMAJ=libpng.so.3 +OLDSOVER=libpng.so.3.$(PNGMIN) + +# Utilities: +CC = gcc +LD = $(CC) +AR_RC = ar rc +LN_SF = ln -sf +MKDIR_P = mkdir -p +RANLIB = ranlib +RM_F = /bin/rm -f + +# where "make install" puts libpng12.a, libpng12.so*, +# libpng12/png.h and libpng12/pngconf.h +# Prefix must be a full pathname. +prefix=/usr/local +exec_prefix=$(prefix) + +# Where the zlib library and include files are located. +#ZLIBLIB=/usr/local/lib +#ZLIBINC=/usr/local/include +ZLIBLIB=../zlib +ZLIBINC=../zlib + +ALIGN= +# for i386: +#ALIGN=-malign-loops=2 -malign-functions=2 + +WARNMORE=-Wwrite-strings -Wpointer-arith -Wshadow \ + -Wmissing-declarations -Wtraditional -Wcast-align \ + -Wstrict-prototypes -Wmissing-prototypes #-Wconversion + +# for pgcc version 2.95.1, -O3 is buggy; don't use it. + +# Remove -DPNG_THREAD_UNSAFE_OK if you need thread safety +### for generic gcc: +CFLAGS=-DPNG_THREAD_UNSAFE_OK -I$(ZLIBINC) -Wall -O \ + $(ALIGN) -funroll-loops \ + -fomit-frame-pointer # $(WARNMORE) -g -DPNG_DEBUG=5 +### for gcc 2.95.2 on 686: +#CFLAGS=-DPNG_THREAD_UNSAFE_OK -I$(ZLIBINC) -Wall -O \ +# -mcpu=i686 -malign-double -ffast-math -fstrict-aliasing \ +# $(ALIGN) -funroll-loops -funroll-all-loops -fomit-frame-pointer +### for gcc 2.7.2.3 on 486 and up: +#CFLAGS=-DPNG_THREAD_UNSAFE_OK -I$(ZLIBINC) -Wall -O \ +# -m486 -malign-double -ffast-math \ +# $(ALIGN) -funroll-loops -funroll-all-loops -fomit-frame-pointer + +LDFLAGS=-L. -Wl,-rpath,. -L$(ZLIBLIB) -Wl,-rpath,$(ZLIBLIB) -lpng12 -lz -lm +LDFLAGS_A=-L$(ZLIBLIB) -Wl,-rpath,$(ZLIBLIB) libpng.a -lz -lm + + +INCPATH=$(prefix)/include +LIBPATH=$(exec_prefix)/lib +MANPATH=$(prefix)/man +BINPATH=$(exec_prefix)/bin + +# override DESTDIR= on the make install command line to easily support +# installing into a temporary location. Example: +# +# make install DESTDIR=/tmp/build/libpng +# +# If you're going to install into a temporary location +# via DESTDIR, $(DESTDIR)$(prefix) must already exist before +# you execute make install. +DESTDIR= + +DB=$(DESTDIR)$(BINPATH) +DI=$(DESTDIR)$(INCPATH) +DL=$(DESTDIR)$(LIBPATH) +DM=$(DESTDIR)$(MANPATH) + +OBJS = png.o pngset.o pngget.o pngrutil.o pngtrans.o pngwutil.o \ + pngread.o pngrio.o pngwio.o pngwrite.o pngrtran.o \ + pngwtran.o pngmem.o pngerror.o pngpread.o + +OBJSDLL = $(OBJS:.o=.pic.o) + +.SUFFIXES: .c .o .pic.o + +.c.pic.o: + $(CC) -c $(CFLAGS) -fPIC -o $@ $*.c + +all: libpng.a $(LIBSO) pngtest pngtest-static libpng.pc libpng-config + +libpng.a: $(OBJS) + $(AR_RC) $@ $(OBJS) + $(RANLIB) $@ + +libpng.pc: + cat scripts/libpng.pc.in | sed -e s!@prefix@!$(prefix)! \ + -e s!@exec_prefix@!$(exec_prefix)! \ + -e s!@libdir@!$(LIBPATH)! \ + -e s!@includedir@!$(INCPATH)! \ + -e s!-lpng12!-lpng12\ -lz\ -lm! > libpng.pc + +libpng-config: + ( cat scripts/libpng-config-head.in; \ + echo prefix=\"$(prefix)\"; \ + echo I_opts=\"-I$(INCPATH)/$(LIBNAME)\"; \ + echo cppflags=\"-DPNG_THREAD_UNSAFE_OK \"; \ + echo L_opts=\"-L$(LIBPATH)\"; \ + echo R_opts=\"-Wl,-rpath,$(LIBPATH)\"; \ + echo libs=\"-lpng12 -lz -lm\"; \ + cat scripts/libpng-config-body.in ) > libpng-config + chmod +x libpng-config + +$(LIBSO): $(LIBSOMAJ) + $(LN_SF) $(LIBSOMAJ) $(LIBSO) + +$(LIBSOMAJ): $(LIBSOVER) + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ) + +$(LIBSOVER): $(OBJSDLL) + $(CC) -shared -Wl,-soname,$(LIBSOMAJ) \ + -o $(LIBSOVER) \ + $(OBJSDLL) + +$(OLDSOVER): $(OBJSDLL) + $(CC) -shared -Wl,-soname,$(OLDSOMAJ) \ + -o $(OLDSOVER) \ + $(OBJSDLL) + +pngtest: pngtest.o $(LIBSO) + $(CC) -o pngtest $(CFLAGS) pngtest.o $(LDFLAGS) + +pngtest-static: pngtest.o libpng.a + $(CC) -o pngtest-static $(CFLAGS) pngtest.o $(LDFLAGS_A) + +test: pngtest pngtest-static + @echo "" + @echo " Running pngtest dynamically linked with $(LIBSO):" + @echo "" + ./pngtest + @echo "" + @echo " Running pngtest statically linked with libpng.a:" + @echo "" + ./pngtest-static + +install-headers: png.h pngconf.h + -@if [ ! -d $(DI) ]; then $(MKDIR_P) $(DI); fi + -@if [ ! -d $(DI)/$(LIBNAME) ]; then $(MKDIR_P) $(DI)/$(LIBNAME); fi + cp png.h pngconf.h $(DI)/$(LIBNAME) + chmod 644 $(DI)/$(LIBNAME)/png.h $(DI)/$(LIBNAME)/pngconf.h + -@$(RM_F) $(DI)/png.h $(DI)/pngconf.h + -@$(RM_F) $(DI)/libpng + (cd $(DI); $(LN_SF) $(LIBNAME) libpng; $(LN_SF) $(LIBNAME)/* .) + +install-static: install-headers libpng.a + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + cp libpng.a $(DL)/$(LIBNAME).a + chmod 644 $(DL)/$(LIBNAME).a + -@$(RM_F) $(DL)/libpng.a + (cd $(DL); $(LN_SF) $(LIBNAME).a libpng.a) + +install-shared: install-headers $(LIBSOVER) libpng.pc \ + $(OLDSOVER) + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + -@$(RM_F) $(DL)/$(LIBSOVER)* $(DL)/$(LIBSO) + -@$(RM_F) $(DL)/$(LIBSOMAJ) + -@$(RM_F) $(DL)/$(OLDSO) + -@$(RM_F) $(DL)/$(OLDSOMAJ) + -@$(RM_F) $(DL)/$(OLDSOVER)* + cp $(LIBSOVER) $(DL) + cp $(OLDSOVER) $(DL) + chmod 755 $(DL)/$(LIBSOVER) + chmod 755 $(DL)/$(OLDSOVER) + (cd $(DL); \ + $(LN_SF) $(OLDSOVER) $(OLDSOMAJ); \ + $(LN_SF) $(OLDSOMAJ) $(OLDSO); \ + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ); \ + $(LN_SF) $(LIBSOMAJ) $(LIBSO)) + -@if [ ! -d $(DL)/pkgconfig ]; then $(MKDIR_P) $(DL)/pkgconfig; fi + -@$(RM_F) $(DL)/pkgconfig/$(LIBNAME).pc + -@$(RM_F) $(DL)/pkgconfig/libpng.pc + cp libpng.pc $(DL)/pkgconfig/$(LIBNAME).pc + chmod 644 $(DL)/pkgconfig/$(LIBNAME).pc + (cd $(DL)/pkgconfig; $(LN_SF) $(LIBNAME).pc libpng.pc) + +install-man: libpng.3 libpngpf.3 png.5 + -@if [ ! -d $(DM) ]; then $(MKDIR_P) $(DM); fi + -@if [ ! -d $(DM)/man3 ]; then $(MKDIR_P) $(DM)/man3; fi + -@$(RM_F) $(DM)/man3/libpng.3 + -@$(RM_F) $(DM)/man3/libpngpf.3 + cp libpng.3 $(DM)/man3 + cp libpngpf.3 $(DM)/man3 + -@if [ ! -d $(DM)/man5 ]; then $(MKDIR_P) $(DM)/man5; fi + -@$(RM_F) $(DM)/man5/png.5 + cp png.5 $(DM)/man5 + +install-config: libpng-config + -@if [ ! -d $(DB) ]; then $(MKDIR_P) $(DB); fi + -@$(RM_F) $(DB)/libpng-config + -@$(RM_F) $(DB)/$(LIBNAME)-config + cp libpng-config $(DB)/$(LIBNAME)-config + chmod 755 $(DB)/$(LIBNAME)-config + (cd $(DB); $(LN_SF) $(LIBNAME)-config libpng-config) + +install: install-static install-shared install-man install-config + +# If you installed in $(DESTDIR), test-installed won't work until you +# move the library to its final location. Use test-dd to test it +# before then. + +test-dd: + echo + echo Testing installed dynamic shared library in $(DL). + $(CC) -I$(DI) -I$(ZLIBINC) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -L$(DL) -L$(ZLIBLIB) -Wl, -rpath,$(DL) -Wl,-rpath,$(ZLIBLIB) \ + -o pngtestd `$(BINPATH)/$(LIBNAME)-config --ldflags` + ./pngtestd pngtest.png + +test-installed: + $(CC) -I$(ZLIBINC) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -L$(ZLIBLIB) -Wl,-rpath,$(ZLIBLIB) \ + -o pngtesti `$(BINPATH)/$(LIBNAME)-config --ldflags` + ./pngtesti pngtest.png + +clean: + $(RM_F) *.o libpng.a pngtest pngout.png libpng-config \ + $(LIBSO) $(LIBSOMAJ)* pngtest-static pngtesti \ + $(OLDSOVER) \ + libpng.pc + +DOCS = ANNOUNCE CHANGES INSTALL KNOWNBUG LICENSE README TODO Y2KINFO +writelock: + chmod a-w *.[ch35] $(DOCS) scripts/* + +png.o png.pic.o: png.h pngconf.h png.c +pngerror.o pngerror.pic.o: png.h pngconf.h pngerror.c +pngrio.o pngrio.pic.o: png.h pngconf.h pngrio.c +pngwio.o pngwio.pic.o: png.h pngconf.h pngwio.c +pngmem.o pngmem.pic.o: png.h pngconf.h pngmem.c +pngset.o pngset.pic.o: png.h pngconf.h pngset.c +pngget.o pngget.pic.o: png.h pngconf.h pngget.c +pngread.o pngread.pic.o: png.h pngconf.h pngread.c +pngrtran.o pngrtran.pic.o: png.h pngconf.h pngrtran.c +pngrutil.o pngrutil.pic.o: png.h pngconf.h pngrutil.c +pngtrans.o pngtrans.pic.o: png.h pngconf.h pngtrans.c +pngwrite.o pngwrite.pic.o: png.h pngconf.h pngwrite.c +pngwtran.o pngwtran.pic.o: png.h pngconf.h pngwtran.c +pngwutil.o pngwutil.pic.o: png.h pngconf.h pngwutil.c +pngpread.o pngpread.pic.o: png.h pngconf.h pngpread.c + +pngtest.o: png.h pngconf.h pngtest.c diff --git a/Engine/lib/lpng/scripts/makefile.hp64 b/Engine/lib/lpng/scripts/makefile.hp64 new file mode 100644 index 000000000..e0a69304b --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.hp64 @@ -0,0 +1,235 @@ +# makefile for libpng, HPUX (10.20 and 11.00) using the ANSI/C product. +# Copyright (C) 1999-2002 Glenn Randers-Pehrson +# Copyright (C) 1995 Guy Eric Schalnat, Group 42 +# contributed by Jim Rice and updated by Chris Schleicher, Hewlett Packard +# For conditions of distribution and use, see copyright notice in png.h + +# Where the zlib library and include files are located +ZLIBLIB=/opt/zlib/lib +ZLIBINC=/opt/zlib/include + +# Note that if you plan to build a libpng shared library, zlib must also +# be a shared library, which zlib's configure does not do. After running +# zlib's configure, edit the appropriate lines of makefile to read: +# CFLAGS=-O1 -DHAVE_UNISTD -DUSE_MAP -fPIC \ +# LDSHARED=ld -b +# SHAREDLIB=libz.sl + +# Library name: +LIBNAME = libpng12 +PNGMAJ = 0 +PNGMIN = 1.2.24 +PNGVER = $(PNGMAJ).$(PNGMIN) + +# Shared library names: +LIBSO=$(LIBNAME).sl +LIBSOMAJ=$(LIBNAME).sl.$(PNGMAJ) +LIBSOVER=$(LIBNAME).sl.$(PNGVER) +OLDSO=libpng.sl +OLDSOMAJ=libpng.sl.3 +OLDSOVER=libpng.sl.3.$(PNGMIN) + +# Utilities: +AR_RC=ar rc +CC=cc +MKDIR_P=mkdir -p +LN_SF=ln -sf +RANLIB=ranlib +RM_F=/bin/rm -f + +CFLAGS=-I$(ZLIBINC) -O -Ae -Wl,+vnocompatwarnings +DD64 \ +-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 +Z -DHAVE_UNISTD_H -DUSE_MMAP +# Caution: be sure you have built zlib with the same CFLAGS. +CCFLAGS=-I$(ZLIBINC) -O -Ae -Wl,+vnocompatwarnings +DD64 \ +-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 +Z -DHAVE_UNISTD_H -DUSE_MMAP + +LDFLAGS=-L. -L$(ZLIBLIB) -lpng -lz -lm + +# where make install puts libpng.a, libpng12.sl, and png.h +prefix=/opt/libpng +exec_prefix=$(prefix) +INCPATH=$(prefix)/include +LIBPATH=$(exec_prefix)/lib +MANPATH=$(prefix)/man +BINPATH=$(exec_prefix)/bin + +# override DESTDIR= on the make install command line to easily support +# installing into a temporary location. Example: +# +# make install DESTDIR=/tmp/build/libpng +# +# If you're going to install into a temporary location +# via DESTDIR, $(DESTDIR)$(prefix) must already exist before +# you execute make install. +DESTDIR= + +DB=$(DESTDIR)$(BINPATH) +DI=$(DESTDIR)$(INCPATH) +DL=$(DESTDIR)$(LIBPATH) +DM=$(DESTDIR)$(MANPATH) + +OBJS = png.o pngset.o pngget.o pngrutil.o pngtrans.o pngwutil.o \ + pngread.o pngrio.o pngwio.o pngwrite.o pngrtran.o \ + pngwtran.o pngmem.o pngerror.o pngpread.o + +OBJSDLL = $(OBJS:.o=.pic.o) + +.SUFFIXES: .c .o .pic.o + +.c.pic.o: + $(CC) -c $(CFLAGS) +z -o $@ $*.c + +all: libpng.a $(LIBSO) pngtest libpng.pc libpng-config + +libpng.a: $(OBJS) + $(AR_RC) $@ $(OBJS) + $(RANLIB) $@ + +libpng.pc: + cat scripts/libpng.pc.in | sed -e s!@prefix@!$(prefix)! \ + -e s!@exec_prefix@!$(exec_prefix)! \ + -e s!@libdir@!$(LIBPATH)! \ + -e s!@includedir@!$(INCPATH)! \ + -e s!-lpng12!-lpng12\ -lz\ -lm! > libpng.pc + +libpng-config: + ( cat scripts/libpng-config-head.in; \ + echo prefix=\"$(prefix)\"; \ + echo I_opts=\"-I$(INCPATH)/$(LIBNAME)\"; \ + echo ccopts=\"-Ae +DA1.1 +DS2.0\"; \ + echo L_opts=\"-L$(LIBPATH)\"; \ + echo libs=\"-lpng12 -lz -lm\"; \ + cat scripts/libpng-config-body.in ) > libpng-config + chmod +x libpng-config + +$(LIBSO): $(LIBSOMAJ) + $(LN_SF) $(LIBSOMAJ) $(LIBSO) + +$(LIBSOMAJ): $(LIBSOVER) + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ) + +$(LIBSOVER): $(OBJSDLL) + $(LD) -b +s \ + +h $(LIBSOMAJ) -o $(LIBSOVER) $(OBJSDLL) + +$(OLDSOVER): $(OBJSDLL) + $(LD) -b +s \ + +h $(OLDSOMAJ) -o $(OLDSOVER) $(OBJSDLL) + +pngtest: pngtest.o libpng.a + $(CC) -o pngtest $(CCFLAGS) pngtest.o $(LDFLAGS) + +test: pngtest + ./pngtest + +install-headers: png.h pngconf.h + -@if [ ! -d $(DI) ]; then $(MKDIR_P) $(DI); fi + -@if [ ! -d $(DI)/$(LIBNAME) ]; then $(MKDIR_P) $(DI)/$(LIBNAME); fi + cp png.h pngconf.h $(DI)/$(LIBNAME) + chmod 644 $(DI)/$(LIBNAME)/png.h $(DI)/$(LIBNAME)/pngconf.h + -@$(RM_F) $(DI)/png.h $(DI)/pngconf.h + -@$(RM_F) $(DI)/libpng + (cd $(DI); $(LN_SF) $(LIBNAME) libpng; $(LN_SF) $(LIBNAME)/* .) + +install-static: install-headers libpng.a + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + cp libpng.a $(DL)/$(LIBNAME).a + chmod 644 $(DL)/$(LIBNAME).a + -@$(RM_F) $(DL)/libpng.a + (cd $(DL); $(LN_SF) $(LIBNAME).a libpng.a) + +install-shared: install-headers $(LIBSOVER) libpng.pc \ + $(OLDSOVER) + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + -@$(RM_F) $(DL)/$(LIBSOVER)* $(DL)/$(LIBSO) + -@$(RM_F) $(DL)/$(LIBSOMAJ) + -@$(RM_F) $(DL)/$(OLDSO) + -@$(RM_F) $(DL)/$(OLDSOMAJ) + -@$(RM_F) $(DL)/$(OLDSOVER)* + cp $(LIBSOVER) $(DL) + cp $(OLDSOVER) $(DL) + chmod 755 $(DL)/$(LIBSOVER) + chmod 755 $(DL)/$(OLDSOVER) + (cd $(DL); \ + $(LN_SF) $(OLDSOVER) $(OLDSOMAJ); \ + $(LN_SF) $(OLDSOMAJ) $(OLDSO); \ + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ); \ + $(LN_SF) $(LIBSOMAJ) $(LIBSO)) + -@if [ ! -d $(DL)/pkgconfig ]; then $(MKDIR_P) $(DL)/pkgconfig; fi + -@$(RM_F) $(DL)/pkgconfig/$(LIBNAME).pc + -@$(RM_F) $(DL)/pkgconfig/libpng.pc + cp libpng.pc $(DL)/pkgconfig/$(LIBNAME).pc + chmod 644 $(DL)/pkgconfig/$(LIBNAME).pc + (cd $(DL)/pkgconfig; $(LN_SF) $(LIBNAME).pc libpng.pc) + +install-man: libpng.3 libpngpf.3 png.5 + -@if [ ! -d $(DM) ]; then $(MKDIR_P) $(DM); fi + -@if [ ! -d $(DM)/man3 ]; then $(MKDIR_P) $(DM)/man3; fi + -@$(RM_F) $(DM)/man3/libpng.3 + -@$(RM_F) $(DM)/man3/libpngpf.3 + cp libpng.3 $(DM)/man3 + cp libpngpf.3 $(DM)/man3 + -@if [ ! -d $(DM)/man5 ]; then $(MKDIR_P) $(DM)/man5; fi + -@$(RM_F) $(DM)/man5/png.5 + cp png.5 $(DM)/man5 + +install-config: libpng-config + -@if [ ! -d $(DB) ]; then $(MKDIR_P) $(DB); fi + -@$(RM_F) $(DB)/libpng-config + -@$(RM_F) $(DB)/$(LIBNAME)-config + cp libpng-config $(DB)/$(LIBNAME)-config + chmod 755 $(DB)/$(LIBNAME)-config + (cd $(DB); $(LN_SF) $(LIBNAME)-config libpng-config) + +install: install-static install-shared install-man install-config + +# If you installed in $(DESTDIR), test-installed won't work until you +# move the library to its final location. Use test-dd to test it +# before then. + +test-dd: + echo + echo Testing installed dynamic shared library in $(DL). + $(CC) -I$(DI) -I$(ZLIBINC) $(CCFLAGS) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -L$(DL) -L$(ZLIBLIB) \ + -o pngtestd `$(BINPATH)/$(LIBNAME)-config --ldflags` + ./pngtestd pngtest.png + +test-installed: + echo + echo Testing installed dynamic shared library. + $(CC) $(CCFLAGS) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -L$(ZLIBLIB) \ + -o pngtesti `$(BINPATH)/$(LIBNAME)-config --ldflags` + ./pngtesti pngtest.png + +clean: + $(RM_F) *.o libpng.a pngtest pngtesti pngout.png \ + libpng-config $(LIBSO) $(LIBSOMAJ)* \ + $(OLDSOVER) \ + libpng.pc + +DOCS = ANNOUNCE CHANGES INSTALL KNOWNBUG LICENSE README TODO Y2KINFO +writelock: + chmod a-w *.[ch35] $(DOCS) scripts/* + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +png.o: png.h pngconf.h +pngerror.o: png.h pngconf.h +pngrio.o: png.h pngconf.h +pngwio.o: png.h pngconf.h +pngmem.o: png.h pngconf.h +pngset.o: png.h pngconf.h +pngget.o: png.h pngconf.h +pngread.o: png.h pngconf.h +pngrtran.o: png.h pngconf.h +pngrutil.o: png.h pngconf.h +pngtest.o: png.h pngconf.h +pngtrans.o: png.h pngconf.h +pngwrite.o: png.h pngconf.h +pngwtran.o: png.h pngconf.h +pngwutil.o: png.h pngconf.h +pngpread.o: png.h pngconf.h diff --git a/Engine/lib/lpng/scripts/makefile.hpgcc b/Engine/lib/lpng/scripts/makefile.hpgcc new file mode 100644 index 000000000..793d2659a --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.hpgcc @@ -0,0 +1,245 @@ +# makefile for libpng on HP-UX using GCC with the HP ANSI/C linker. +# Copyright (C) 2002, 2006, 2007 Glenn Randers-Pehrson +# Copyright (C) 2001, Laurent faillie +# Copyright (C) 1998, 1999 Greg Roelofs +# Copyright (C) 1996, 1997 Andreas Dilger +# For conditions of distribution and use, see copyright notice in png.h + +# Library name: +LIBNAME = libpng12 +PNGMAJ = 0 +PNGMIN = 1.2.24 +PNGVER = $(PNGMAJ).$(PNGMIN) + +# Shared library names: +LIBSO=$(LIBNAME).sl +LIBSOMAJ=$(LIBNAME).sl.$(PNGMAJ) +LIBSOVER=$(LIBNAME).sl.$(PNGVER) +OLDSO=libpng.sl +OLDSOMAJ=libpng.sl.3 +OLDSOVER=libpng.sl.3.$(PNGMIN) + +# Utilities: +CC=gcc +LD=ld +AR_RC=ar rc +MKDIR_P=mkdir -p +LN_SF=ln -sf +RANLIB=ranlib +RM_F=/bin/rm -f + +# where "make install" puts libpng.a, $(OLDSO)*, png.h and pngconf.h +prefix=/usr/local +exec_prefix=$(prefix) + +# Where the zlib library and include files are located +ZLIBLIB=/opt/zlib/lib +ZLIBINC=/opt/zlib/include + +# Note that if you plan to build a libpng shared library, zlib must also +# be a shared library, which zlib's configure does not do. After running +# zlib's configure, edit the appropriate lines of makefile to read: +# CFLAGS=-O1 -DHAVE_UNISTD -DUSE_MAP -fPIC \ +# LDSHARED=ld -b +# SHAREDLIB=libz.sl + +ALIGN= +# for i386: +#ALIGN=-malign-loops=2 -malign-functions=2 + +WARNMORE=-Wwrite-strings -Wpointer-arith -Wshadow \ + -Wmissing-declarations -Wtraditional -Wcast-align \ + -Wstrict-prototypes -Wmissing-prototypes #-Wconversion + +# for pgcc version 2.95.1, -O3 is buggy; don't use it. + +CFLAGS=-I$(ZLIBINC) -Wall -O3 -funroll-loops -DPNG_NO_MMX_CODE \ + $(ALIGN) # $(WARNMORE) -g -DPNG_DEBUG=5 +#LDFLAGS=-L. -Wl,-rpath,. -L$(ZLIBLIB) -Wl,-rpath,$(ZLIBLIB) -lpng12 -lz -lm +LDFLAGS=-L. -L$(ZLIBLIB) -lpng12 -lz -lm + +INCPATH=$(prefix)/include +LIBPATH=$(exec_prefix)/lib +MANPATH=$(prefix)/man +BINPATH=$(exec_prefix)/bin + +# override DESTDIR= on the make install command line to easily support +# installing into a temporary location. Example: +# +# make install DESTDIR=/tmp/build/libpng +# +# If you're going to install into a temporary location +# via DESTDIR, $(DESTDIR)$(prefix) must already exist before +# you execute make install. +DESTDIR= + +DB=$(DESTDIR)$(BINPATH) +DI=$(DESTDIR)$(INCPATH) +DL=$(DESTDIR)$(LIBPATH) +DM=$(DESTDIR)$(MANPATH) + +OBJS = png.o pngset.o pngget.o pngrutil.o pngtrans.o pngwutil.o \ + pngread.o pngrio.o pngwio.o pngwrite.o pngrtran.o \ + pngwtran.o pngmem.o pngerror.o pngpread.o + +OBJSDLL = $(OBJS:.o=.pic.o) + +.SUFFIXES: .c .o .pic.o + +.c.pic.o: + $(CC) -c $(CFLAGS) -fPIC -o $@ $*.c + +all: libpng.a $(LIBSO) pngtest libpng.pc libpng-config + +libpng.a: $(OBJS) + $(AR_RC) $@ $(OBJS) + $(RANLIB) $@ + +libpng.pc: + cat scripts/libpng.pc.in | sed -e s!@prefix@!$(prefix)! \ + -e s!@exec_prefix@!$(exec_prefix)! \ + -e s!@libdir@!$(LIBPATH)! \ + -e s!@includedir@!$(INCPATH)! \ + -e s!-lpng12!-lpng12\ -lz\ -lm! > libpng.pc + +libpng-config: + ( cat scripts/libpng-config-head.in; \ + echo prefix=\"$(prefix)\"; \ + echo I_opts=\"-I$(INCPATH)/$(LIBNAME)\"; \ + echo libs=\"-lpng12 -lz -lm\"; \ + cat scripts/libpng-config-body.in ) > libpng-config + chmod +x libpng-config + +$(LIBSO): $(LIBSOMAJ) + $(LN_SF) $(LIBSOMAJ) $(LIBSO) + +$(LIBSOMAJ): $(LIBSOVER) + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ) + +$(LIBSOVER): $(OBJSDLL) + $(LD) -b +s \ + +h $(LIBSOMAJ) -o $(LIBSOVER) $(OBJSDLL) + +$(OLDSOVER): $(OBJSDLL) + $(LD) -b +s \ + +h $(OLDSOMAJ) -o $(OLDSOVER) $(OBJSDLL) + +pngtest: pngtest.o $(LIBSO) + $(CC) -o pngtest $(CFLAGS) pngtest.o $(LDFLAGS) + +test: pngtest + ./pngtest + + +install-headers: png.h pngconf.h + -@if [ ! -d $(DI) ]; then $(MKDIR_P) $(DI); fi + -@if [ ! -d $(DI)/$(LIBNAME) ]; then $(MKDIR_P) $(DI)/$(LIBNAME); fi + cp png.h pngconf.h $(DI)/$(LIBNAME) + chmod 644 $(DI)/$(LIBNAME)/png.h $(DI)/$(LIBNAME)/pngconf.h + -@$(RM_F) $(DI)/png.h $(DI)/pngconf.h + -@$(RM_F) $(DI)/libpng + (cd $(DI); $(LN_SF) $(LIBNAME) libpng; $(LN_SF) $(LIBNAME)/* .) + +install-static: install-headers libpng.a + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + cp libpng.a $(DL)/$(LIBNAME).a + chmod 644 $(DL)/$(LIBNAME).a + -@$(RM_F) $(DL)/libpng.a + (cd $(DL); $(LN_SF) $(LIBNAME).a libpng.a) + +install-shared: install-headers $(LIBSOVER) libpng.pc \ + $(OLDSOVER) + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + -@$(RM_F) $(DL)/$(LIBSOVER)* $(DL)/$(LIBSO) + -@$(RM_F) $(DL)/$(LIBSOMAJ) + -@$(RM_F) $(DL)/$(OLDSO) + -@$(RM_F) $(DL)/$(OLDSOMAJ) + -@$(RM_F) $(DL)/$(OLDSOVER)* + cp $(LIBSOVER) $(DL) + cp $(OLDSOVER) $(DL) + chmod 755 $(DL)/$(LIBSOVER) + chmod 755 $(DL)/$(OLDSOVER) + (cd $(DL); \ + $(LN_SF) $(OLDSOVER) $(OLDSOMAJ); \ + $(LN_SF) $(OLDSOMAJ) $(OLDSO); \ + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ); \ + $(LN_SF) $(LIBSOMAJ) $(LIBSO)) + -@if [ ! -d $(DL)/pkgconfig ]; then $(MKDIR_P) $(DL)/pkgconfig; fi + -@$(RM_F) $(DL)/pkgconfig/$(LIBNAME).pc + -@$(RM_F) $(DL)/pkgconfig/libpng.pc + cp libpng.pc $(DL)/pkgconfig/$(LIBNAME).pc + chmod 644 $(DL)/pkgconfig/$(LIBNAME).pc + (cd $(DL)/pkgconfig; $(LN_SF) $(LIBNAME).pc libpng.pc) + +install-man: libpng.3 libpngpf.3 png.5 + -@if [ ! -d $(DM) ]; then $(MKDIR_P) $(DM); fi + -@if [ ! -d $(DM)/man3 ]; then $(MKDIR_P) $(DM)/man3; fi + -@$(RM_F) $(DM)/man3/libpng.3 + -@$(RM_F) $(DM)/man3/libpngpf.3 + cp libpng.3 $(DM)/man3 + cp libpngpf.3 $(DM)/man3 + -@if [ ! -d $(DM)/man5 ]; then $(MKDIR_P) $(DM)/man5; fi + -@$(RM_F) $(DM)/man5/png.5 + cp png.5 $(DM)/man5 + +install-config: libpng-config + -@if [ ! -d $(DB) ]; then $(MKDIR_P) $(DB); fi + -@$(RM_F) $(DB)/libpng-config + -@$(RM_F) $(DB)/$(LIBNAME)-config + cp libpng-config $(DB)/$(LIBNAME)-config + chmod 755 $(DB)/$(LIBNAME)-config + (cd $(DB); $(LN_SF) $(LIBNAME)-config libpng-config) + +install: install-static install-shared install-man install-config + +# If you installed in $(DESTDIR), test-installed won't work until you +# move the library to its final location. Use test-dd to test it +# before then. + +test-dd: + echo + echo Testing installed dynamic shared library in $(DL). + $(CC) -I$(DI) -I$(ZLIBINC) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -L$(DL) -L$(ZLIBLIB) -Wl,-rpath,$(DL) -Wl,-rpath,$(ZLIBLIB) \ + -o pngtestd `$(BINPATH)/$(LIBNAME)-config --ldflags` + ./pngtestd pngtest.png + +test-installed: + echo + echo Testing installed dynamic shared library. + $(CC) -I$(ZLIBINC) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -L$(ZLIBLIB) -Wl,-rpath,$(ZLIBLIB) \ + -o pngtesti `$(BINPATH)/$(LIBNAME)-config --ldflags` + ./pngtesti pngtest.png + +clean: + $(RM_F) *.o libpng.a pngtest pngtesti pngout.png \ + libpng-config $(LIBSO) $(LIBSOMAJ)* \ + $(OLDSOVER) \ + libpng.pc + +DOCS = ANNOUNCE CHANGES INSTALL KNOWNBUG LICENSE README TODO Y2KINFO +writelock: + chmod a-w *.[ch35] $(DOCS) scripts/* + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +png.o png.pic.o: png.h pngconf.h +pngerror.o pngerror.pic.o: png.h pngconf.h +pngrio.o pngrio.pic.o: png.h pngconf.h +pngwio.o pngwio.pic.o: png.h pngconf.h +pngmem.o pngmem.pic.o: png.h pngconf.h +pngset.o pngset.pic.o: png.h pngconf.h +pngget.o pngget.pic.o: png.h pngconf.h +pngread.o pngread.pic.o: png.h pngconf.h +pngrtran.o pngrtran.pic.o: png.h pngconf.h +pngrutil.o pngrutil.pic.o: png.h pngconf.h +pngtrans.o pngtrans.pic.o: png.h pngconf.h +pngwrite.o pngwrite.pic.o: png.h pngconf.h +pngwtran.o pngwtran.pic.o: png.h pngconf.h +pngwutil.o pngwutil.pic.o: png.h pngconf.h +pngpread.o pngpread.pic.o: png.h pngconf.h + +pngtest.o: png.h pngconf.h diff --git a/Engine/lib/lpng/scripts/makefile.hpux b/Engine/lib/lpng/scripts/makefile.hpux new file mode 100644 index 000000000..4a4669d0f --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.hpux @@ -0,0 +1,232 @@ +# makefile for libpng, HPUX (10.20 and 11.00) using the ANSI/C product. +# Copyright (C) 1999-2002, 2006 Glenn Randers-Pehrson +# Copyright (C) 1995 Guy Eric Schalnat, Group 42 +# contributed by Jim Rice and updated by Chris Schleicher, Hewlett Packard +# For conditions of distribution and use, see copyright notice in png.h + +# Where the zlib library and include files are located +ZLIBLIB=/opt/zlib/lib +ZLIBINC=/opt/zlib/include + +# Note that if you plan to build a libpng shared library, zlib must also +# be a shared library, which zlib's configure does not do. After running +# zlib's configure, edit the appropriate lines of makefile to read: +# CFLAGS=-O1 -DHAVE_UNISTD -DUSE_MAP -fPIC \ +# LDSHARED=ld -b +# SHAREDLIB=libz.sl + +# Library name: +LIBNAME = libpng12 +PNGMAJ = 0 +PNGMIN = 1.2.24 +PNGVER = $(PNGMAJ).$(PNGMIN) + +# Shared library names: +LIBSO=$(LIBNAME).sl +LIBSOMAJ=$(LIBNAME).sl.$(PNGMAJ) +LIBSOVER=$(LIBNAME).sl.$(PNGVER) +OLDSO=libpng.sl +OLDSOMAJ=libpng.sl.3 +OLDSOVER=libpng.sl.3.$(PNGMIN) + +# Utilities: +AR_RC=ar rc +CC=cc +MKDIR_P=mkdir -p +LN_SF=ln -sf +RANLIB=ranlib +RM_F=/bin/rm -f + +# where make install puts libpng.a, libpng12.sl, and png.h +prefix=/opt/libpng +exec_prefix=$(prefix) +INCPATH=$(prefix)/include +LIBPATH=$(exec_prefix)/lib +MANPATH=$(prefix)/man +BINPATH=$(exec_prefix)/bin + +CFLAGS=-I$(ZLIBINC) -O -Ae +DA1.1 +DS2.0 -DPNG_NO_MMX_CODE +# Caution: be sure you have built zlib with the same CFLAGS. +CCFLAGS=-I$(ZLIBINC) -O -Ae +DA1.1 +DS2.0 +LDFLAGS=-L. -L$(ZLIBLIB) -lpng -lz -lm + +# override DESTDIR= on the make install command line to easily support +# installing into a temporary location. Example: +# +# make install DESTDIR=/tmp/build/libpng +# +# If you're going to install into a temporary location +# via DESTDIR, $(DESTDIR)$(prefix) must already exist before +# you execute make install. +DESTDIR= + +DB=$(DESTDIR)$(BINPATH) +DI=$(DESTDIR)$(INCPATH) +DL=$(DESTDIR)$(LIBPATH) +DM=$(DESTDIR)$(MANPATH) + +OBJS = png.o pngset.o pngget.o pngrutil.o pngtrans.o pngwutil.o \ + pngread.o pngrio.o pngwio.o pngwrite.o pngrtran.o \ + pngwtran.o pngmem.o pngerror.o pngpread.o + +OBJSDLL = $(OBJS:.o=.pic.o) + +.SUFFIXES: .c .o .pic.o + +.c.pic.o: + $(CC) -c $(CFLAGS) +z -o $@ $*.c + +all: libpng.a $(LIBSO) pngtest libpng.pc libpng-config + +libpng.a: $(OBJS) + $(AR_RC) $@ $(OBJS) + $(RANLIB) $@ + +libpng.pc: + cat scripts/libpng.pc.in | sed -e s!@prefix@!$(prefix)! \ + -e s!@exec_prefix@!$(exec_prefix)! \ + -e s!@libdir@!$(LIBPATH)! \ + -e s!@includedir@!$(INCPATH)! \ + -e s!-lpng12!-lpng12\ -lz\ -lm! > libpng.pc + +libpng-config: + ( cat scripts/libpng-config-head.in; \ + echo prefix=\"$(prefix)\"; \ + echo I_opts=\"-I$(INCPATH)/$(LIBNAME)\"; \ + echo ccopts=\"-Ae +DA1.1 +DS2.0\"; \ + echo L_opts=\"-L$(LIBPATH)\"; \ + echo libs=\"-lpng12 -lz -lm\"; \ + cat scripts/libpng-config-body.in ) > libpng-config + chmod +x libpng-config + +$(LIBSO): $(LIBSOMAJ) + $(LN_SF) $(LIBSOMAJ) $(LIBSO) + +$(LIBSOMAJ): $(LIBSOVER) + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ) + +$(LIBSOVER): $(OBJSDLL) + $(LD) -b +s \ + +h $(LIBSOMAJ) -o $(LIBSOVER) $(OBJSDLL) + +$(OLDSOVER): $(OBJSDLL) + $(LD) -b +s \ + +h $(OLDSOMAJ) -o $(OLDSOVER) $(OBJSDLL) + +pngtest: pngtest.o libpng.a + $(CC) -o pngtest $(CCFLAGS) pngtest.o $(LDFLAGS) + +test: pngtest + ./pngtest + +install-headers: png.h pngconf.h + -@if [ ! -d $(DI) ]; then $(MKDIR_P) $(DI); fi + -@if [ ! -d $(DI)/$(LIBNAME) ]; then $(MKDIR_P) $(DI)/$(LIBNAME); fi + cp png.h pngconf.h $(DI)/$(LIBNAME) + chmod 644 $(DI)/$(LIBNAME)/png.h $(DI)/$(LIBNAME)/pngconf.h + -@$(RM_F) $(DI)/png.h $(DI)/pngconf.h + -@$(RM_F) $(DI)/libpng + (cd $(DI); $(LN_SF) $(LIBNAME) libpng; $(LN_SF) $(LIBNAME)/* .) + +install-static: install-headers libpng.a + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + cp libpng.a $(DL)/$(LIBNAME).a + chmod 644 $(DL)/$(LIBNAME).a + -@$(RM_F) $(DL)/libpng.a + (cd $(DL); $(LN_SF) $(LIBNAME).a libpng.a) + +install-shared: install-headers $(LIBSOVER) libpng.pc \ + $(OLDSOVER) + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + -@$(RM_F) $(DL)/$(LIBSOVER)* $(DL)/$(LIBSO) + -@$(RM_F) $(DL)/$(LIBSOMAJ) + -@$(RM_F) $(DL)/$(OLDSO) + -@$(RM_F) $(DL)/$(OLDSOMAJ) + -@$(RM_F) $(DL)/$(OLDSOVER)* + cp $(LIBSOVER) $(DL) + cp $(OLDSOVER) $(DL) + chmod 755 $(DL)/$(LIBSOVER) + chmod 755 $(DL)/$(OLDSOVER) + (cd $(DL); \ + $(LN_SF) $(OLDSOVER) $(OLDSOMAJ); \ + $(LN_SF) $(OLDSOMAJ) $(OLDSO); \ + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ); \ + $(LN_SF) $(LIBSOMAJ) $(LIBSO)) + -@if [ ! -d $(DL)/pkgconfig ]; then $(MKDIR_P) $(DL)/pkgconfig; fi + -@$(RM_F) $(DL)/pkgconfig/$(LIBNAME).pc + -@$(RM_F) $(DL)/pkgconfig/libpng.pc + cp libpng.pc $(DL)/pkgconfig/$(LIBNAME).pc + chmod 644 $(DL)/pkgconfig/$(LIBNAME).pc + (cd $(DL)/pkgconfig; $(LN_SF) $(LIBNAME).pc libpng.pc) + +install-man: libpng.3 libpngpf.3 png.5 + -@if [ ! -d $(DM) ]; then $(MKDIR_P) $(DM); fi + -@if [ ! -d $(DM)/man3 ]; then $(MKDIR_P) $(DM)/man3; fi + -@$(RM_F) $(DM)/man3/libpng.3 + -@$(RM_F) $(DM)/man3/libpngpf.3 + cp libpng.3 $(DM)/man3 + cp libpngpf.3 $(DM)/man3 + -@if [ ! -d $(DM)/man5 ]; then $(MKDIR_P) $(DM)/man5; fi + -@$(RM_F) $(DM)/man5/png.5 + cp png.5 $(DM)/man5 + +install-config: libpng-config + -@if [ ! -d $(DB) ]; then $(MKDIR_P) $(DB); fi + -@$(RM_F) $(DB)/libpng-config + -@$(RM_F) $(DB)/$(LIBNAME)-config + cp libpng-config $(DB)/$(LIBNAME)-config + chmod 755 $(DB)/$(LIBNAME)-config + (cd $(DB); $(LN_SF) $(LIBNAME)-config libpng-config) + +install: install-static install-shared install-man install-config + +# If you installed in $(DESTDIR), test-installed won't work until you +# move the library to its final location. Use test-dd to test it +# before then. + +test-dd: + echo + echo Testing installed dynamic shared library in $(DL). + $(CC) -I$(DI) -I$(ZLIBINC) $(CCFLAGS) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -L$(DL) -L$(ZLIBLIB) \ + -o pngtestd `$(BINPATH)/$(LIBNAME)-config --ldflags` + ./pngtestd pngtest.png + +test-installed: + echo + echo Testing installed dynamic shared library. + $(CC) $(CCFLAGS) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -L$(ZLIBLIB) \ + -o pngtesti `$(BINPATH)/$(LIBNAME)-config --ldflags` + ./pngtesti pngtest.png + +clean: + $(RM_F) *.o libpng.a pngtest pngtesti pngout.png \ + libpng-config $(LIBSO) $(LIBSOMAJ)* \ + $(OLDSOVER) \ + libpng.pc + +DOCS = ANNOUNCE CHANGES INSTALL KNOWNBUG LICENSE README TODO Y2KINFO +writelock: + chmod a-w *.[ch35] $(DOCS) scripts/* + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +png.o: png.h pngconf.h +pngerror.o: png.h pngconf.h +pngrio.o: png.h pngconf.h +pngwio.o: png.h pngconf.h +pngmem.o: png.h pngconf.h +pngset.o: png.h pngconf.h +pngget.o: png.h pngconf.h +pngread.o: png.h pngconf.h +pngrtran.o: png.h pngconf.h +pngrutil.o: png.h pngconf.h +pngtest.o: png.h pngconf.h +pngtrans.o: png.h pngconf.h +pngwrite.o: png.h pngconf.h +pngwtran.o: png.h pngconf.h +pngwutil.o: png.h pngconf.h +pngpread.o: png.h pngconf.h diff --git a/Engine/lib/lpng/scripts/makefile.ibmc b/Engine/lib/lpng/scripts/makefile.ibmc new file mode 100644 index 000000000..f09a62c9e --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.ibmc @@ -0,0 +1,71 @@ +# Makefile for libpng (static) +# IBM C version 3.x for Win32 and OS/2 +# Copyright (C) 2000 Cosmin Truta +# For conditions of distribution and use, see copyright notice in png.h +# Notes: +# Derived from makefile.std +# All modules are compiled in C mode +# Tested under Win32, expected to work under OS/2 +# Can be easily adapted for IBM VisualAge/C++ for AIX + +# Location of the zlib library and include files +ZLIBINC = ../zlib +ZLIBLIB = ../zlib + +# Compiler, linker, lib and other tools +CC = icc +LD = ilink +AR = ilib +RM = del + +CFLAGS = -I$(ZLIBINC) -Mc -O2 -W3 +LDFLAGS = + +# File extensions +O=.obj +A=.lib +E=.exe + +# Variables +OBJS = png$(O) pngerror$(O) pngget$(O) pngmem$(O) pngpread$(O) \ + pngread$(O) pngrio$(O) pngrtran$(O) pngrutil$(O) pngset$(O) \ + pngtrans$(O) pngwio$(O) pngwrite$(O) pngwtran$(O) pngwutil$(O) + +LIBS = libpng$(A) $(ZLIBLIB)/zlib$(A) + +# Targets +all: libpng$(A) pngtest$(E) + +libpng$(A): $(OBJS) + $(AR) -out:$@ $(OBJS) + +test: pngtest$(E) + pngtest$(E) + +pngtest: pngtest$(E) + +pngtest$(E): pngtest$(O) libpng$(A) + $(LD) $(LDFLAGS) pngtest$(O) $(LIBS) + +clean: + $(RM) *$(O) + $(RM) libpng$(A) + $(RM) pngtest$(E) + $(RM) pngout.png + +png$(O): png.h pngconf.h +pngerror$(O): png.h pngconf.h +pngget$(O): png.h pngconf.h +pngmem$(O): png.h pngconf.h +pngpread$(O): png.h pngconf.h +pngread$(O): png.h pngconf.h +pngrio$(O): png.h pngconf.h +pngrtran$(O): png.h pngconf.h +pngrutil$(O): png.h pngconf.h +pngset$(O): png.h pngconf.h +pngtest$(O): png.h pngconf.h +pngtrans$(O): png.h pngconf.h +pngwio$(O): png.h pngconf.h +pngwrite$(O): png.h pngconf.h +pngwtran$(O): png.h pngconf.h +pngwutil$(O): png.h pngconf.h diff --git a/Engine/lib/lpng/scripts/makefile.intel b/Engine/lib/lpng/scripts/makefile.intel new file mode 100644 index 000000000..b0b523af0 --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.intel @@ -0,0 +1,102 @@ +# Makefile for libpng +# Microsoft Visual C++ with Intel C/C++ Compiler 4.0 and later + +# Copyright (C) 2000, Pawel Mrochen, based on makefile.msc which is +# copyright 1995 Guy Eric Schalnat, Group 42, Inc. +# For conditions of distribution and use, see copyright notice in png.h + +# To use, do "nmake /f scripts\makefile.intel" + + +# Where the zlib library and include files are located +ZLIBLIB=..\zlib +ZLIBINC=..\zlib + +# Target CPU +CPU=6 # Pentium II +#CPU=5 # Pentium + +# Calling convention +CALLING=r # __fastcall +#CALLING=z # __stdcall +#CALLING=d # __cdecl + +# Uncomment next to put error messages in a file +#ERRFILE=>>pngerrs + +# -------------------------------------------------------------------------- + + +CC=icl -c +CFLAGS=-O2 -G$(CPU)$(CALLING) -Qip -Qunroll4 -I$(ZLIBINC) -nologo +LD=link +LDFLAGS=/SUBSYSTEM:CONSOLE /NOLOGO + +O=.obj + +OBJS=png$(O) pngset$(O) pngget$(O) pngrutil$(O) pngtrans$(O) pngwutil$(O) \ +pngmem$(O) pngpread$(O) pngread$(O) pngerror$(O) pngwrite$(O) \ +pngrtran$(O) pngwtran$(O) pngrio$(O) pngwio$(O) + +all: test + +png$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngset$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngget$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngread$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngpread$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngrtran$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngrutil$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngerror$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngmem$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngrio$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngwio$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngtest$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngtrans$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngwrite$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngwtran$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngwutil$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +libpng.lib: $(OBJS) + if exist libpng.lib del libpng.lib + lib /NOLOGO /OUT:libpng.lib $(OBJS) + +pngtest.exe: pngtest.obj libpng.lib + $(LD) $(LDFLAGS) /OUT:pngtest.exe pngtest.obj libpng.lib $(ZLIBLIB)\zlib.lib + +test: pngtest.exe + pngtest.exe + + +# End of makefile for libpng diff --git a/Engine/lib/lpng/scripts/makefile.knr b/Engine/lib/lpng/scripts/makefile.knr new file mode 100644 index 000000000..44ee538a3 --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.knr @@ -0,0 +1,99 @@ +# makefile for libpng +# Copyright (C) 2002 Glenn Randers-Pehrson +# Copyright (C) 1995 Guy Eric Schalnat, Group 42, Inc. +# For conditions of distribution and use, see copyright notice in png.h + +# This makefile requires the file ansi2knr.c, which you can get +# from the Ghostscript ftp site at ftp://ftp.cs.wisc.edu/ghost/ +# If you have libjpeg, you probably already have ansi2knr.c in the jpeg +# source distribution. + +# where make install puts libpng.a and png.h +prefix=/usr/local +INCPATH=$(prefix)/include +LIBPATH=$(prefix)/lib + +# override DESTDIR= on the make install command line to easily support +# installing into a temporary location. Example: +# +# make install DESTDIR=/tmp/build/libpng +# +# If you're going to install into a temporary location +# via DESTDIR, $(DESTDIR)$(prefix) must already exist before +# you execute make install. +DESTDIR= + +CC=cc +CFLAGS=-I../zlib -O +LDFLAGS=-L. -L../zlib/ -lpng -lz -lm +# flags for ansi2knr +ANSI2KNRFLAGS= + +RANLIB=ranlib +#RANLIB=echo + +OBJS = png.o pngset.o pngget.o pngrutil.o pngtrans.o pngwutil.o \ + pngread.o pngrio.o pngwio.o pngwrite.o pngrtran.o \ + pngwtran.o pngmem.o pngerror.o pngpread.o + +all: ansi2knr libpng.a pngtest + +# general rule to allow ansi2knr to work +.c.o: + ./ansi2knr $*.c T$*.c + $(CC) $(CFLAGS) -c T$*.c + rm -f T$*.c $*.o + mv T$*.o $*.o + +ansi2knr: ansi2knr.c + $(CC) $(CFLAGS) $(ANSI2KNRFLAGS) -o ansi2knr ansi2knr.c + +libpng.a: ansi2knr $(OBJS) + ar rc $@ $(OBJS) + $(RANLIB) $@ + +pngtest: pngtest.o libpng.a + $(CC) -o pngtest $(CFLAGS) pngtest.o $(LDFLAGS) + +test: pngtest + ./pngtest + +install: libpng.a + -@mkdir $(DESTDIR)$(INCPATH) + -@mkdir $(DESTDIR)$(INCPATH)/libpng + -@mkdir $(DESTDIR)$(LIBPATH) + -@rm -f $(DESTDIR)$(INCPATH)/png.h + -@rm -f $(DESTDIR)$(INCPATH)/pngconf.h + cp png.h $(DESTDIR)$(INCPATH)/libpng + cp pngconf.h $(DESTDIR)$(INCPATH)/libpng + chmod 644 $(DESTDIR)$(INCPATH)/libpng/png.h + chmod 644 $(DESTDIR)$(INCPATH)/libpng/pngconf.h + (cd $(DESTDIR)$(INCPATH); ln -f -s libpng/* .) + cp libpng.a $(DESTDIR)$(LIBPATH) + chmod 644 $(DESTDIR)$(LIBPATH)/libpng.a + +clean: + rm -f *.o libpng.a pngtest pngout.png ansi2knr + +DOCS = ANNOUNCE CHANGES INSTALL KNOWNBUG LICENSE README TODO Y2KINFO +writelock: + chmod a-w *.[ch35] $(DOCS) scripts/* + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +png.o: png.h pngconf.h +pngerror.o: png.h pngconf.h +pngrio.o: png.h pngconf.h +pngwio.o: png.h pngconf.h +pngmem.o: png.h pngconf.h +pngset.o: png.h pngconf.h +pngget.o: png.h pngconf.h +pngread.o: png.h pngconf.h +pngpread.o: png.h pngconf.h +pngrtran.o: png.h pngconf.h +pngrutil.o: png.h pngconf.h +pngtest.o: png.h pngconf.h +pngtrans.o: png.h pngconf.h +pngwrite.o: png.h pngconf.h +pngwtran.o: png.h pngconf.h +pngwutil.o: png.h pngconf.h diff --git a/Engine/lib/lpng/scripts/makefile.linux b/Engine/lib/lpng/scripts/makefile.linux new file mode 100644 index 000000000..645e2fe42 --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.linux @@ -0,0 +1,249 @@ +# makefile for libpng.a and libpng12.so on Linux ELF with gcc +# Copyright (C) 1998, 1999, 2002, 2006 Greg Roelofs and Glenn Randers-Pehrson +# Copyright (C) 1996, 1997 Andreas Dilger +# For conditions of distribution and use, see copyright notice in png.h + +# Library name: +LIBNAME = libpng12 +PNGMAJ = 0 +PNGMIN = 1.2.24 +PNGVER = $(PNGMAJ).$(PNGMIN) + +# Shared library names: +LIBSO=$(LIBNAME).so +LIBSOMAJ=$(LIBNAME).so.$(PNGMAJ) +LIBSOVER=$(LIBNAME).so.$(PNGVER) +OLDSO=libpng.so +OLDSOMAJ=libpng.so.3 +OLDSOVER=libpng.so.3.$(PNGMIN) + +# Utilities: +AR_RC=ar rc +CC=gcc +MKDIR_P=mkdir -p +LN_SF=ln -sf +RANLIB=ranlib +RM_F=/bin/rm -f + +# where "make install" puts libpng12.a, libpng12.so*, +# libpng12/png.h and libpng12/pngconf.h +# Prefix must be a full pathname. +prefix=/usr/local +exec_prefix=$(prefix) + +# Where the zlib library and include files are located. +#ZLIBLIB=/usr/local/lib +#ZLIBINC=/usr/local/include +ZLIBLIB=../zlib +ZLIBINC=../zlib + +ALIGN= +# for i386: +#ALIGN=-malign-loops=2 -malign-functions=2 + +WARNMORE=-Wwrite-strings -Wpointer-arith -Wshadow \ + -Wmissing-declarations -Wtraditional -Wcast-align \ + -Wstrict-prototypes -Wmissing-prototypes #-Wconversion + +# for pgcc version 2.95.1, -O3 is buggy; don't use it. + +CFLAGS=-I$(ZLIBINC) -Wall -O3 -funroll-loops -DPNG_NO_MMX_CODE \ + $(ALIGN) # $(WARNMORE) -g -DPNG_DEBUG=5 + +LDFLAGS=-L. -Wl,-rpath,. -L$(ZLIBLIB) -Wl,-rpath,$(ZLIBLIB) -lpng12 -lz -lm +LDFLAGS_A=-L$(ZLIBLIB) -Wl,-rpath,$(ZLIBLIB) libpng.a -lz -lm + +INCPATH=$(prefix)/include +LIBPATH=$(exec_prefix)/lib +MANPATH=$(prefix)/man +BINPATH=$(exec_prefix)/bin + +# override DESTDIR= on the make install command line to easily support +# installing into a temporary location. Example: +# +# make install DESTDIR=/tmp/build/libpng +# +# If you're going to install into a temporary location +# via DESTDIR, $(DESTDIR)$(prefix) must already exist before +# you execute make install. +DESTDIR= + +DB=$(DESTDIR)$(BINPATH) +DI=$(DESTDIR)$(INCPATH) +DL=$(DESTDIR)$(LIBPATH) +DM=$(DESTDIR)$(MANPATH) + +OBJS = png.o pngset.o pngget.o pngrutil.o pngtrans.o pngwutil.o \ + pngread.o pngrio.o pngwio.o pngwrite.o pngrtran.o \ + pngwtran.o pngmem.o pngerror.o pngpread.o + +OBJSDLL = $(OBJS:.o=.pic.o) + +.SUFFIXES: .c .o .pic.o + +.c.pic.o: + $(CC) -c $(CFLAGS) -fPIC -o $@ $*.c + +all: libpng.a $(LIBSO) pngtest pngtest-static libpng.pc libpng-config + +libpng.a: $(OBJS) + $(AR_RC) $@ $(OBJS) + $(RANLIB) $@ + +libpng.pc: + cat scripts/libpng.pc.in | sed -e s!@prefix@!$(prefix)! \ + -e s!@exec_prefix@!$(exec_prefix)! \ + -e s!@libdir@!$(LIBPATH)! \ + -e s!@includedir@!$(INCPATH)! \ + -e s!-lpng12!-lpng12\ -lz\ -lm! > libpng.pc + +libpng-config: + ( cat scripts/libpng-config-head.in; \ + echo prefix=\"$(prefix)\"; \ + echo I_opts=\"-I$(INCPATH)/$(LIBNAME)\"; \ + echo L_opts=\"-L$(LIBPATH)\"; \ + echo R_opts=\"-Wl,-rpath,$(LIBPATH)\"; \ + echo libs=\"-lpng12 -lz -lm\"; \ + cat scripts/libpng-config-body.in ) > libpng-config + chmod +x libpng-config + +$(LIBSO): $(LIBSOMAJ) + $(LN_SF) $(LIBSOMAJ) $(LIBSO) + +$(LIBSOMAJ): $(LIBSOVER) + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ) + +$(LIBSOVER): $(OBJSDLL) + $(CC) -shared -Wl,-soname,$(LIBSOMAJ) -o $(LIBSOVER) $(OBJSDLL) + +$(OLDSOVER): $(OBJSDLL) + $(CC) -shared -Wl,-soname,$(OLDSOMAJ) \ + -o $(OLDSOVER) \ + $(OBJSDLL) + +pngtest: pngtest.o $(LIBSO) + $(CC) -o pngtest $(CFLAGS) pngtest.o $(LDFLAGS) + +pngtest-static: pngtest.o libpng.a + $(CC) -o pngtest-static $(CFLAGS) pngtest.o $(LDFLAGS_A) + +test: pngtest pngtest-static + @echo "" + @echo " Running pngtest dynamically linked with $(LIBSO):" + @echo "" + ./pngtest + @echo "" + @echo " Running pngtest statically linked with libpng.a:" + @echo "" + ./pngtest-static + +install-headers: png.h pngconf.h + -@if [ ! -d $(DI) ]; then $(MKDIR_P) $(DI); fi + -@if [ ! -d $(DI)/$(LIBNAME) ]; then $(MKDIR_P) $(DI)/$(LIBNAME); fi + cp png.h pngconf.h $(DI)/$(LIBNAME) + chmod 644 $(DI)/$(LIBNAME)/png.h $(DI)/$(LIBNAME)/pngconf.h + -@$(RM_F) $(DI)/png.h $(DI)/pngconf.h + -@$(RM_F) $(DI)/libpng + (cd $(DI); $(LN_SF) $(LIBNAME) libpng; $(LN_SF) $(LIBNAME)/* .) + +install-static: install-headers libpng.a + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + cp libpng.a $(DL)/$(LIBNAME).a + chmod 644 $(DL)/$(LIBNAME).a + -@$(RM_F) $(DL)/libpng.a + (cd $(DL); $(LN_SF) $(LIBNAME).a libpng.a) + +install-shared: install-headers $(LIBSOVER) libpng.pc \ + $(OLDSOVER) + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + -@$(RM_F) $(DL)/$(LIBSOVER)* $(DL)/$(LIBSO) + -@$(RM_F) $(DL)/$(LIBSOMAJ) + -@$(RM_F) $(DL)/$(OLDSO) + -@$(RM_F) $(DL)/$(OLDSOMAJ) + -@$(RM_F) $(DL)/$(OLDSOVER)* + cp $(LIBSOVER) $(DL) + cp $(OLDSOVER) $(DL) + chmod 755 $(DL)/$(LIBSOVER) + chmod 755 $(DL)/$(OLDSOVER) + (cd $(DL); \ + $(LN_SF) $(OLDSOVER) $(OLDSOMAJ); \ + $(LN_SF) $(OLDSOMAJ) $(OLDSO); \ + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ); \ + $(LN_SF) $(LIBSOMAJ) $(LIBSO)) + -@if [ ! -d $(DL)/pkgconfig ]; then $(MKDIR_P) $(DL)/pkgconfig; fi + -@$(RM_F) $(DL)/pkgconfig/$(LIBNAME).pc + -@$(RM_F) $(DL)/pkgconfig/libpng.pc + cp libpng.pc $(DL)/pkgconfig/$(LIBNAME).pc + chmod 644 $(DL)/pkgconfig/$(LIBNAME).pc + (cd $(DL)/pkgconfig; $(LN_SF) $(LIBNAME).pc libpng.pc) + +install-man: libpng.3 libpngpf.3 png.5 + -@if [ ! -d $(DM) ]; then $(MKDIR_P) $(DM); fi + -@if [ ! -d $(DM)/man3 ]; then $(MKDIR_P) $(DM)/man3; fi + -@$(RM_F) $(DM)/man3/libpng.3 + -@$(RM_F) $(DM)/man3/libpngpf.3 + cp libpng.3 $(DM)/man3 + cp libpngpf.3 $(DM)/man3 + -@if [ ! -d $(DM)/man5 ]; then $(MKDIR_P) $(DM)/man5; fi + -@$(RM_F) $(DM)/man5/png.5 + cp png.5 $(DM)/man5 + +install-config: libpng-config + -@if [ ! -d $(DB) ]; then $(MKDIR_P) $(DB); fi + -@$(RM_F) $(DB)/libpng-config + -@$(RM_F) $(DB)/$(LIBNAME)-config + cp libpng-config $(DB)/$(LIBNAME)-config + chmod 755 $(DB)/$(LIBNAME)-config + (cd $(DB); $(LN_SF) $(LIBNAME)-config libpng-config) + +install: install-static install-shared install-man install-config + +# If you installed in $(DESTDIR), test-installed won't work until you +# move the library to its final location. Use test-dd to test it +# before then. + +test-dd: + echo + echo Testing installed dynamic shared library in $(DL). + $(CC) -I$(DI) -I$(ZLIBINC) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -L$(DL) -L$(ZLIBLIB) -Wl, -rpath,$(DL) -Wl,-rpath,$(ZLIBLIB) \ + -o pngtestd `$(BINPATH)/$(LIBNAME)-config --ldflags` + ./pngtestd pngtest.png + +test-installed: + $(CC) -I$(ZLIBINC) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -L$(ZLIBLIB) -Wl,-rpath,$(ZLIBLIB) \ + -o pngtesti `$(BINPATH)/$(LIBNAME)-config --ldflags` + ./pngtesti pngtest.png + +clean: + $(RM_F) *.o libpng.a pngtest pngout.png libpng-config \ + $(LIBSO) $(LIBSOMAJ)* pngtest-static pngtesti \ + $(OLDSOVER) \ + libpng.pc + +DOCS = ANNOUNCE CHANGES INSTALL KNOWNBUG LICENSE README TODO Y2KINFO +writelock: + chmod a-w *.[ch35] $(DOCS) scripts/* + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +png.o png.pic.o: png.h pngconf.h +pngerror.o pngerror.pic.o: png.h pngconf.h +pngrio.o pngrio.pic.o: png.h pngconf.h +pngwio.o pngwio.pic.o: png.h pngconf.h +pngmem.o pngmem.pic.o: png.h pngconf.h +pngset.o pngset.pic.o: png.h pngconf.h +pngget.o pngget.pic.o: png.h pngconf.h +pngread.o pngread.pic.o: png.h pngconf.h +pngrtran.o pngrtran.pic.o: png.h pngconf.h +pngrutil.o pngrutil.pic.o: png.h pngconf.h +pngtrans.o pngtrans.pic.o: png.h pngconf.h +pngwrite.o pngwrite.pic.o: png.h pngconf.h +pngwtran.o pngwtran.pic.o: png.h pngconf.h +pngwutil.o pngwutil.pic.o: png.h pngconf.h +pngpread.o pngpread.pic.o: png.h pngconf.h + +pngtest.o: png.h pngconf.h diff --git a/Engine/lib/lpng/scripts/makefile.mingw b/Engine/lib/lpng/scripts/makefile.mingw new file mode 100644 index 000000000..c13021445 --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.mingw @@ -0,0 +1,289 @@ +# makefile for mingw on x86 +# Builds both dll (with import lib) and static lib versions +# of the library, and builds two copies of pngtest: one +# statically linked and one dynamically linked. +# +# Built from makefile.cygwin +# Copyright (C) 2002, 2006 Soren Anderson, Charles Wilson, +# and Glenn Randers-Pehrson, based on makefile for linux-elf w/mmx by: +# Copyright (C) 1998-2000, 2007 Greg Roelofs +# Copyright (C) 1996, 1997 Andreas Dilger +# For conditions of distribution and use, see copyright notice in png.h + + +# This makefile intends to support building outside the src directory +# if desired. When invoking it, specify an argument to SRCDIR on the +# command line that points to the top of the directory where your source +# is located. +ifdef SRCDIR +VPATH = $(SRCDIR) +else +SRCDIR = . +endif + +# Override DESTDIR= on the make install command line to easily support +# installing into a temporary location. Example: +# +# make install DESTDIR=/tmp/build/libpng +# +# If you're going to install into a temporary location +# via DESTDIR, $(DESTDIR)$(prefix) must already exist before +# you execute make install. +DESTDIR= + +# If you're using a cross-compiler, add the appropriate prefix (e.g., +# "i386-mingw32msvc-") to the following three commands: +CC=gcc +AR=ar +RANLIB=ranlib + +MKDIR_P=/bin/mkdir -pv + +# Where "make install" puts libpng*.a, *png*.dll, png.h, and pngconf.h +ifndef prefix +prefix=/usr +$(warning "You haven't specified a 'prefix=' location. Defaulting to '/usr'") +endif +exec_prefix=$(prefix) + +# Where the zlib library and include files are located +ZLIBLIB= /usr/lib +ZLIBINC= + +ALIGN= +# for i386: +#ALIGN=-malign-loops=2 -malign-functions=2 + +WARNMORE=-Wwrite-strings -Wpointer-arith -Wshadow \ + -Wmissing-declarations -Wtraditional -Wcast-align \ + -Wstrict-prototypes -Wmissing-prototypes #-Wconversion + +### if you don't need thread safety, but want the asm accel +#CFLAGS= $(strip $(MINGW_CCFLAGS) -DPNG_THREAD_UNSAFE_OK \ +# $(addprefix -I,$(ZLIBINC)) -Wall -O $(ALIGN) -funroll-loops \ +# -fomit-frame-pointer) # $(WARNMORE) -g -DPNG_DEBUG=5 +### if you need thread safety and want (minimal) asm accel +#CFLAGS= $(strip $(MINGW_CCFLAGS) $(addprefix -I,$(ZLIBINC)) \ +# -Wall -O $(ALIGN) -funroll-loops \ +# -fomit-frame-pointer) # $(WARNMORE) -g -DPNG_DEBUG=5 +### Normal (non-asm) compilation +CFLAGS= $(strip $(MINGW_CCFLAGS) $(addprefix -I,$(ZLIBINC)) \ + -Wall -O3 $(ALIGN) -funroll-loops -DPNG_NO_MMX_CODE \ + -fomit-frame-pointer) # $(WARNMORE) -g -DPNG_DEBUG=5 + +LIBNAME = libpng12 +PNGMAJ = 0 +MINGDLL = 12 +PNGMIN = 1.2.24 +PNGVER = $(PNGMAJ).$(PNGMIN) + +SHAREDLIB=libpng$(MINGDLL).dll +STATLIB=libpng.a +IMPLIB=libpng.dll.a +SHAREDDEF=libpng.def +LIBS=$(SHAREDLIB) $(STATLIB) +EXE=.exe + +LDFLAGS=$(strip -L. $(MINGW_LDFLAGS) -lpng $(addprefix -L,$(ZLIBLIB)) -lz) +LDSFLAGS=$(strip -shared -L. $(MINGW_LDFLAGS)) +LDEXTRA=-Wl,--out-implib=$(IMPLIB) $(addprefix -L,$(ZLIBLIB)) -lz + +INCPATH=$(prefix)/include +LIBPATH=$(exec_prefix)/lib + +BINPATH=$(exec_prefix)/bin +MANPATH=$(prefix)/man +MAN3PATH=$(MANPATH)/man3 +MAN5PATH=$(MANPATH)/man5 + +# cosmetic: shortened strings: +S =$(SRCDIR) +D =$(DESTDIR) +DB =$(D)$(BINPATH) +DI =$(D)$(INCPATH) +DL =$(D)$(LIBPATH) + +OBJS = png.o pngset.o pngget.o pngrutil.o pngtrans.o pngwutil.o \ + pngread.o pngrio.o pngwio.o pngwrite.o pngrtran.o \ + pngwtran.o pngmem.o pngerror.o pngpread.o + +OBJSDLL = $(OBJS:.o=.pic.o) + +.SUFFIXES: .c .o .pic.o + +%.o : %.c + $(CC) -c $(CFLAGS) -o $@ $< +%.pic.o : CFLAGS += -DPNG_BUILD_DLL +%.pic.o : %.c + $(CC) -c $(CFLAGS) -o $@ $< + +all: all-static all-shared libpng.pc libpng-config libpng.pc libpng-config + +# Make this to verify that "make [...] install" will do what you want. +buildsetup-tell: + @echo VPATH is set to: \"$(VPATH)\" + @echo prefix is set to: \"$(prefix)\" + @echo -e INCPATH,LIBPATH, etc. are set to:'\n' \ + $(addprefix $(D),$(INCPATH)'\n' $(LIBPATH)'\n' $(BINPATH)'\n' \ + $(MANPATH)'\n' $(MAN3PATH)'\n' $(MAN5PATH)'\n')'\n' + +libpng.pc: scripts/libpng.pc.in + @echo -e Making pkg-config file for this libpng installation..'\n' \ + using PREFIX=\"$(prefix)\"'\n' + cat scripts/libpng.pc.in | sed -e s!@prefix@!$(prefix)! \ + -e s!@exec_prefix@!$(exec_prefix)! \ + -e s!@libdir@!$(LIBPATH)! \ + -e s!@includedir@!$(INCPATH)! \ + -e s!@includedir@!$(INCPATH)! \ + -e s!-lpng12!-lpng12\ -lz\ -lm! > libpng.pc + +libpng-config: scripts/libpng-config-head.in scripts/libpng-config-body.in + @echo -e Making $(LIBNAME) libpng-config file for this libpng \ + installation..'\n' using PREFIX=\"$(prefix)\"'\n' + ( cat $(S)/scripts/libpng-config-head.in; \ + echo prefix=\"$(prefix)\"; \ + echo I_opts=\"-I$(INCPATH)/$(LIBNAME)\"; \ + echo L_opts=\"-L$(LIBPATH)\"; \ + echo libs=\"-lpng$(MINGDLL) -lz\"; \ + cat $(S)/scripts/libpng-config-body.in ) > libpng-config + chmod +x libpng-config + +static: all-static +shared: all-shared +all-static: $(STATLIB) pngtest-stat$(EXE) +all-shared: $(SHAREDLIB) pngtest$(EXE) + +$(STATLIB): $(OBJS) + $(AR) rc $@ $(OBJS) + $(RANLIB) $@ + +$(SHAREDDEF): scripts/pngw32.def + cat $< | sed -e '1{G;s/^\(.*\)\(\n\)/EXPORTS/;};2,/^EXPORTS/d' | \ + sed -e 's/\([^;]*\);/;/' > $@ + +$(SHAREDLIB): $(OBJSDLL) $(SHAREDDEF) + $(CC) $(LDSFLAGS) -o $@ $(OBJSDLL) -L. $(LDEXTRA) + +pngtest$(EXE): pngtest.pic.o $(SHAREDLIB) + $(CC) $(CFLAGS) $< $(LDFLAGS) -o $@ + +pngtest-stat$(EXE): pngtest.o $(STATLIB) + $(CC) -static $(CFLAGS) $< $(LDFLAGS) -o $@ + +test: test-static test-shared + +test-static: pngtest-stat$(EXE) + ./pngtest-stat $(S)/pngtest.png + +test-shared: pngtest$(EXE) + ./pngtest $(S)/pngtest.png + +install-static: $(STATLIB) install-headers install-man + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + install -m 644 $(STATLIB) $(DL)/$(LIBNAME).a + -@rm -f $(DL)/$(STATLIB) + (cd $(DL); ln -sf $(LIBNAME).a $(STATLIB)) + +install-shared: $(SHAREDLIB) libpng.pc libpng-config install-headers install-man + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + -@if [ ! -d $(DB) ]; then $(MKDIR_P) $(DB); fi + -@if [ ! -d $(DL)/pkgconfig ]; then $(MKDIR_P) $(DL)/pkgconfig; fi + -@/bin/rm -f $(DL)/pkgconfig/$(LIBNAME).pc + -@/bin/rm -f $(DL)/pkgconfig/libpng.pc + install -m 644 $(IMPLIB) $(DL)/$(LIBNAME).dll.a + -@rm -f $(DL)/$(IMPLIB) + (cd $(DL); ln -sf $(LIBNAME).dll.a $(IMPLIB)) + install -s -m 755 $(SHAREDLIB) $(DB) + install -m 644 libpng.pc $(DL)/pkgconfig/$(LIBNAME).pc + (cd $(DL)/pkgconfig; ln -sf $(LIBNAME).pc libpng.pc) + +install-headers: + -@if [ ! -d $(DI) ]; then $(MKDIR_P) $(DI); fi + -@if [ ! -d $(DI)/$(LIBNAME) ]; then $(MKDIR_P) $(DI)/$(LIBNAME); fi + -@rm -f $(DI)/png.h + -@rm -f $(DI)/pngconf.h + install -m 644 $(S)/png.h $(S)/pngconf.h $(DI)/$(LIBNAME) + -@rm -f $(DI)/libpng + (cd $(DI); ln -sf $(LIBNAME) libpng; ln -sf $(LIBNAME)/* .) + +install-man: + -@if [ ! -d $(D)$(MAN3PATH) ]; then $(MKDIR_P) $(D)$(MAN3PATH); fi + -@if [ ! -d $(D)$(MAN5PATH) ]; then $(MKDIR_P) $(D)$(MAN5PATH); fi + install -m 644 $(S)/libpngpf.3 $(S)/libpng.3 $(D)$(MAN3PATH) + install -m 644 $(S)/png.5 $(D)$(MAN5PATH) + +install-config: libpng-config + -@if [ ! -d $(DB) ]; then $(MKDIR_P) $(DB); fi + -@/bin/rm -f $(DB)/libpng-config + -@/bin/rm -f $(DB)/$(LIBNAME)-config + cp libpng-config $(DB)/$(LIBNAME)-config + chmod 755 $(DB)/$(LIBNAME)-config + (cd $(DB); ln -sf $(LIBNAME)-config libpng-config) + +# Run this to verify that a future `configure' run will pick up the settings +# you want. +test-config-install: SHELL=/bin/bash +test-config-install: $(DB)/libpng-config + @echo -e Testing libpng-config functions...'\n' + @ for TYRA in LDFLAGS CPPFLAGS CFLAGS LIBS VERSION; \ + do \ + printf "(%d)\t %10s =%s\n" $$(($$gytiu + 1)) $$TYRA \ + "$$($(DB)/libpng-config `echo --$$TYRA |tr '[:upper:]' '[:lower:]'`)"; \ + gytiu=$$(( $$gytiu + 1 )); \ + done + +install: install-static install-shared install-man install-config + +# If you installed in $(DESTDIR), test-installed won't work until you +# move the library to its final location. Use test-dd to test it +# before then. + +test-dd: + echo + echo Testing installed dynamic shared library in $(DL). + $(CC) -I$(DI) $(CFLAGS) \ + `$(BINPATH)/libpng12-config --cflags` pngtest.c \ + -L$(DL) -L$(ZLIBLIB) \ + -o pngtestd `$(BINPATH)/libpng12-config --ldflags` + ./pngtestd pngtest.png + +test-installed: + $(CC) $(CFLAGS) \ + `$(BINPATH)/libpng12-config --cflags` pngtest.c \ + -L$(ZLIBLIB) \ + -o pngtesti$(EXE) `$(BINPATH)/libpng12-config --ldflags` + ./pngtesti$(EXE) pngtest.png + +clean: + /bin/rm -f *.pic.o *.o $(STATLIB) $(IMPLIB) $(SHAREDLIB) \ + pngtest-stat$(EXE) pngtest$(EXE) pngout.png $(SHAREDDEF) \ + libpng-config libpng.pc pngtesti$(EXE) + +DOCS = ANNOUNCE CHANGES INSTALL KNOWNBUG LICENSE README TODO Y2KINFO +writelock: + chmod a-w *.[ch35] $(DOCS) scripts/* + +.PHONY: buildsetup-tell libpng.pc libpng-config test-config-install clean + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +png.o png.pic.o: png.h pngconf.h png.c +pngerror.o pngerror.pic.o: png.h pngconf.h pngerror.c +pngrio.o pngrio.pic.o: png.h pngconf.h pngrio.c +pngwio.o pngwio.pic.o: png.h pngconf.h pngwio.c +pngmem.o pngmem.pic.o: png.h pngconf.h pngmem.c +pngset.o pngset.pic.o: png.h pngconf.h pngset.c +pngget.o pngget.pic.o: png.h pngconf.h pngget.c +pngread.o pngread.pic.o: png.h pngconf.h pngread.c +pngrtran.o pngrtran.pic.o: png.h pngconf.h pngrtran.c +pngrutil.o pngrutil.pic.o: png.h pngconf.h pngrutil.c +pngtrans.o pngtrans.pic.o: png.h pngconf.h pngtrans.c +pngwrite.o pngwrite.pic.o: png.h pngconf.h pngwrite.c +pngwtran.o pngwtran.pic.o: png.h pngconf.h pngwtran.c +pngwutil.o pngwutil.pic.o: png.h pngconf.h pngwutil.c +pngpread.o pngpread.pic.o: png.h pngconf.h pngpread.c + +pngtest.o pngtest.pic.o: png.h pngconf.h pngtest.c + + + diff --git a/Engine/lib/lpng/scripts/makefile.mips b/Engine/lib/lpng/scripts/makefile.mips new file mode 100644 index 000000000..f1a557df7 --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.mips @@ -0,0 +1,83 @@ +# makefile for libpng +# Copyright (C) Glenn Randers-Pehrson +# Copyright (C) 1995 Guy Eric Schalnat, Group 42, Inc. +# For conditions of distribution and use, see copyright notice in png.h + +# where make install puts libpng.a and png.h +prefix=/usr/local +INCPATH=$(prefix)/include +LIBPATH=$(prefix)/lib + +# override DESTDIR= on the make install command line to easily support +# installing into a temporary location. Example: +# +# make install DESTDIR=/tmp/build/libpng +# +# If you're going to install into a temporary location +# via DESTDIR, $(DESTDIR)$(prefix) must already exist before +# you execute make install. +DESTDIR= + +CC=cc +CFLAGS=-I../zlib -O -systype sysv -DSYSV -w -Dmips +#CFLAGS=-O +LDFLAGS=-L. -L../zlib/ -lpng -lz -lm + +#RANLIB=ranlib +RANLIB=echo + +OBJS = png.o pngset.o pngget.o pngrutil.o pngtrans.o pngwutil.o \ + pngread.o pngrio.o pngwio.o pngwrite.o pngrtran.o \ + pngwtran.o pngmem.o pngerror.o pngpread.o + +all: libpng.a pngtest + +libpng.a: $(OBJS) + ar rc $@ $(OBJS) + $(RANLIB) $@ + +pngtest: pngtest.o libpng.a + $(CC) -o pngtest $(CFLAGS) pngtest.o $(LDFLAGS) + +test: pngtest + ./pngtest + +install: libpng.a + -@mkdir $(DESTDIR)$(INCPATH) + -@mkdir $(DESTDIR)$(INCPATH)/libpng + -@mkdir $(DESTDIR)$(LIBPATH) + -@rm -f $(DESTDIR)$(INCPATH)/png.h + -@rm -f $(DESTDIR)$(INCPATH)/pngconf.h + cp png.h $(DESTDIR)$(INCPATH)/libpng + cp pngconf.h $(DESTDIR)$(INCPATH)/libpng + chmod 644 $(DESTDIR)$(INCPATH)/libpng/png.h + chmod 644 $(DESTDIR)$(INCPATH)/libpng/pngconf.h + (cd $(DESTDIR)$(INCPATH); ln -f -s libpng/* .) + cp libpng.a $(DESTDIR)$(LIBPATH) + chmod 644 $(DESTDIR)$(LIBPATH)/libpng.a + +clean: + rm -f *.o libpng.a pngtest pngout.png + +DOCS = ANNOUNCE CHANGES INSTALL KNOWNBUG LICENSE README TODO Y2KINFO +writelock: + chmod a-w *.[ch35] $(DOCS) scripts/* + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +png.o: png.h pngconf.h +pngerror.o: png.h pngconf.h +pngrio.o: png.h pngconf.h +pngwio.o: png.h pngconf.h +pngmem.o: png.h pngconf.h +pngset.o: png.h pngconf.h +pngget.o: png.h pngconf.h +pngread.o: png.h pngconf.h +pngpread.o: png.h pngconf.h +pngrtran.o: png.h pngconf.h +pngrutil.o: png.h pngconf.h +pngtest.o: png.h pngconf.h +pngtrans.o: png.h pngconf.h +pngwrite.o: png.h pngconf.h +pngwtran.o: png.h pngconf.h +pngwutil.o: png.h pngconf.h diff --git a/Engine/lib/lpng/scripts/makefile.msc b/Engine/lib/lpng/scripts/makefile.msc new file mode 100644 index 000000000..1cbfd9149 --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.msc @@ -0,0 +1,86 @@ +# makefile for libpng +# Copyright (C) 1995 Guy Eric Schalnat, Group 42, Inc. +# For conditions of distribution and use, see copyright notice in png.h +# Assumes that zlib.lib, zconf.h, and zlib.h have been copied to ..\zlib + +# -------- Microsoft C 5.1 and later, does not use assembler code -------- +MODEL=L +CFLAGS=-Oait -Gs -nologo -W3 -A$(MODEL) -I..\zlib +#-Ox generates bad code with MSC 5.1 +CC=cl +LD=link +LDFLAGS=/e/st:0x1500/noe +O=.obj + +#uncomment next to put error messages in a file +ERRFILE= >> pngerrs + +# variables +OBJS1 = png$(O) pngset$(O) pngget$(O) pngrutil$(O) pngtrans$(O) pngwutil$(O) +OBJS2 = pngmem$(O) pngpread$(O) pngread$(O) pngerror$(O) pngwrite$(O) +OBJS3 = pngrtran$(O) pngwtran$(O) pngrio$(O) pngwio$(O) + +all: libpng.lib + +png$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngset$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngget$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngread$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngpread$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngrtran$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngrutil$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngerror$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngmem$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngrio$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngwio$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngtest$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngtrans$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngwrite$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngwtran$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngwutil$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +libpng.lib: $(OBJS1) $(OBJS2) $(OBJS3) + del libpng.lib + lib libpng $(OBJS1); + lib libpng $(OBJS2); + lib libpng $(OBJS3); + +pngtest.exe: pngtest.obj libpng.lib + $(LD) $(LDFLAGS) pngtest.obj,,,libpng.lib ..\zlib\zlib.lib ; + +test: pngtest.exe + pngtest + +# End of makefile for libpng + diff --git a/Engine/lib/lpng/scripts/makefile.ne12bsd b/Engine/lib/lpng/scripts/makefile.ne12bsd new file mode 100644 index 000000000..b1a85dcde --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.ne12bsd @@ -0,0 +1,45 @@ +# makefile for libpng for NetBSD for the standard +# make obj && make depend && make && make test +# make includes && make install +# Copyright (C) 2002 Patrick R.L. Welche +# Copyright (C) 2007 Glenn Randers-Pehrson +# For conditions of distribution and use, see copyright notice in png.h + +# You should also run makefile.netbsd + +LOCALBASE?=/usr/local +LIBDIR= ${LOCALBASE}/lib +MANDIR= ${LOCALBASE}/man +INCSDIR=${LOCALBASE}/include/libpng12 + +LIB= png12 +SHLIB_MAJOR= 0 +SHLIB_MINOR= 1.2.24 +SRCS= png.c pngset.c pngget.c pngrutil.c pngtrans.c pngwutil.c \ + pngread.c pngrio.c pngwio.c pngwrite.c pngrtran.c \ + pngwtran.c pngmem.c pngerror.c pngpread.c +INCS= png.h pngconf.h +MAN= libpng.3 libpngpf.3 png.5 + +CPPFLAGS+=-I${.CURDIR} + +# something like this for mmx assembler, but it core dumps for me at the moment +# .if ${MACHINE_ARCH} == "i386" +# CPPFLAGS+=-DPNG_THREAD_UNSAFE_OK +# MKLINT= no +# .else + CPPFLAGS+=-DPNG_NO_MMX_CODE +# .endif + +CLEANFILES+=pngtest.o pngtest + +pngtest.o: pngtest.c + ${CC} -c ${CPPFLAGS} ${CFLAGS} ${.ALLSRC} -o ${.TARGET} + +pngtest: pngtest.o libpng.a + ${CC} ${LDFLAGS} ${.ALLSRC} -o${.TARGET} -lz -lm + +test: pngtest + cd ${.CURDIR} && ${.OBJDIR}/pngtest + +.include diff --git a/Engine/lib/lpng/scripts/makefile.netbsd b/Engine/lib/lpng/scripts/makefile.netbsd new file mode 100644 index 000000000..fd6f6170f --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.netbsd @@ -0,0 +1,45 @@ +# makefile for libpng for NetBSD for the standard +# make obj && make depend && make && make test +# make includes && make install +# Copyright (C) 2002 Patrick R.L. Welche +# Copyright (C) 2007 Glenn Randers-Pehrson +# For conditions of distribution and use, see copyright notice in png.h + +# You should also run makefile.ne0bsd + +LOCALBASE?=/usr/local +LIBDIR= ${LOCALBASE}/lib +MANDIR= ${LOCALBASE}/man +INCSDIR=${LOCALBASE}/include/libpng + +LIB= png +SHLIB_MAJOR= 3 +SHLIB_MINOR= 1.2.24 +SRCS= png.c pngset.c pngget.c pngrutil.c pngtrans.c pngwutil.c \ + pngread.c pngrio.c pngwio.c pngwrite.c pngrtran.c \ + pngwtran.c pngmem.c pngerror.c pngpread.c +INCS= png.h pngconf.h +MAN= libpng.3 libpngpf.3 png.5 + +CPPFLAGS+=-I${.CURDIR} + +# something like this for mmx assembler, but it core dumps for me at the moment +# .if ${MACHINE_ARCH} == "i386" +# CPPFLAGS+=-DPNG_THREAD_UNSAFE_OK +# MKLINT= no +# .else + CPPFLAGS+=-DPNG_NO_MMX_CODE +# .endif + +CLEANFILES+=pngtest.o pngtest + +pngtest.o: pngtest.c + ${CC} -c ${CPPFLAGS} ${CFLAGS} ${.ALLSRC} -o ${.TARGET} + +pngtest: pngtest.o libpng.a + ${CC} ${LDFLAGS} ${.ALLSRC} -o${.TARGET} -lz -lm + +test: pngtest + cd ${.CURDIR} && ${.OBJDIR}/pngtest + +.include diff --git a/Engine/lib/lpng/scripts/makefile.nommx b/Engine/lib/lpng/scripts/makefile.nommx new file mode 100644 index 000000000..6be112b93 --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.nommx @@ -0,0 +1,252 @@ +# makefile for libpng.a and libpng12.so on Linux ELF with gcc +# Copyright (C) 1998, 1999, 2002, 2006, 2007 Greg Roelofs and +# Glenn Randers-Pehrson +# Copyright (C) 1996, 1997 Andreas Dilger +# For conditions of distribution and use, see copyright notice in png.h + +# Library name: +LIBNAME = libpng12 +PNGMAJ = 0 +PNGMIN = 1.2.24 +PNGVER = $(PNGMAJ).$(PNGMIN) + +# Shared library names: +LIBSO=$(LIBNAME).so +LIBSOMAJ=$(LIBNAME).so.$(PNGMAJ) +LIBSOVER=$(LIBNAME).so.$(PNGVER) +OLDSO=libpng.so +OLDSOMAJ=libpng.so.3 +OLDSOVER=libpng.so.3.$(PNGMIN) + +# Utilities: +AR_RC=ar rc +CC=gcc +MKDIR_P=mkdir -p +LN_SF=ln -sf +RANLIB=ranlib +RM_F=/bin/rm -f + +# where "make install" puts libpng12.a, libpng12.so*, +# libpng12/png.h and libpng12/pngconf.h +# Prefix must be a full pathname. +prefix=/usr/local +exec_prefix=$(prefix) + +# Where the zlib library and include files are located. +#ZLIBLIB=/usr/local/lib +#ZLIBINC=/usr/local/include +ZLIBLIB=../zlib +ZLIBINC=../zlib + +ALIGN= +# for i386: +#ALIGN=-malign-loops=2 -malign-functions=2 + +WARNMORE=-Wwrite-strings -Wpointer-arith -Wshadow \ + -Wmissing-declarations -Wtraditional -Wcast-align \ + -Wstrict-prototypes -Wmissing-prototypes #-Wconversion + +# for pgcc version 2.95.1, -O3 is buggy; don't use it. + +CFLAGS=-I$(ZLIBINC) -Wall -O3 -funroll-loops -DPNG_NO_MMX_CODE \ + $(ALIGN) # $(WARNMORE) -g -DPNG_DEBUG=5 + +LDFLAGS=-L. -Wl,-rpath,. -L$(ZLIBLIB) -Wl,-rpath,$(ZLIBLIB) -lpng12 -lz -lm +LDFLAGS_A=-L$(ZLIBLIB) -Wl,-rpath,$(ZLIBLIB) libpng.a -lz -lm + +INCPATH=$(prefix)/include +LIBPATH=$(exec_prefix)/lib +MANPATH=$(prefix)/man +BINPATH=$(exec_prefix)/bin + +# override DESTDIR= on the make install command line to easily support +# installing into a temporary location. Example: +# +# make install DESTDIR=/tmp/build/libpng +# +# If you're going to install into a temporary location +# via DESTDIR, $(DESTDIR)$(prefix) must already exist before +# you execute make install. +DESTDIR= + +DB=$(DESTDIR)$(BINPATH) +DI=$(DESTDIR)$(INCPATH) +DL=$(DESTDIR)$(LIBPATH) +DM=$(DESTDIR)$(MANPATH) + +OBJS = png.o pngset.o pngget.o pngrutil.o pngtrans.o pngwutil.o \ + pngread.o pngrio.o pngwio.o pngwrite.o pngrtran.o \ + pngwtran.o pngmem.o pngerror.o pngpread.o + +OBJSDLL = $(OBJS:.o=.pic.o) + +.SUFFIXES: .c .o .pic.o + +.c.pic.o: + $(CC) -c $(CFLAGS) -fPIC -o $@ $*.c + +all: libpng.a $(LIBSO) pngtest pngtest-static libpng.pc libpng-config + +libpng.a: $(OBJS) + $(AR_RC) $@ $(OBJS) + $(RANLIB) $@ + +libpng.pc: + cat scripts/libpng.pc.in | sed -e s!@prefix@!$(prefix)! \ + -e s!@exec_prefix@!$(exec_prefix)! \ + -e s!@libdir@!$(LIBPATH)! \ + -e s!@includedir@!$(INCPATH)! \ + -e s!@includedir@!$(INCPATH)! \ + -e s!-lpng12!-lpng12\ -lz\ -lm! \ + -e s!Cflags: !Cflags:\ -DPNG_NO_MMX_CODE!> libpng.pc + +libpng-config: + ( cat scripts/libpng-config-head.in; \ + echo prefix=\"$(prefix)\"; \ + echo I_opts=\"-I$(INCPATH)/$(LIBNAME) -DPNG_NO_MMX_CODE\"; \ + echo L_opts=\"-L$(LIBPATH)\"; \ + echo R_opts=\"-Wl,-rpath,$(LIBPATH)\"; \ + echo libs=\"-lpng12 -lz -lm\"; \ + cat scripts/libpng-config-body.in ) > libpng-config + chmod +x libpng-config + +$(LIBSO): $(LIBSOMAJ) + $(LN_SF) $(LIBSOMAJ) $(LIBSO) + +$(LIBSOMAJ): $(LIBSOVER) + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ) + +$(LIBSOVER): $(OBJSDLL) + $(CC) -shared -Wl,-soname,$(LIBSOMAJ) -o $(LIBSOVER) $(OBJSDLL) + +$(OLDSOVER): $(OBJSDLL) + $(CC) -shared -Wl,-soname,$(OLDSOMAJ) \ + -o $(OLDSOVER) \ + $(OBJSDLL) + +pngtest: pngtest.o $(LIBSO) + $(CC) -o pngtest $(CFLAGS) pngtest.o $(LDFLAGS) + +pngtest-static: pngtest.o libpng.a + $(CC) -o pngtest-static $(CFLAGS) pngtest.o $(LDFLAGS_A) + +test: pngtest pngtest-static + @echo "" + @echo " Running pngtest dynamically linked with $(LIBSO):" + @echo "" + ./pngtest + @echo "" + @echo " Running pngtest statically linked with libpng.a:" + @echo "" + ./pngtest-static + +install-headers: png.h pngconf.h + -@if [ ! -d $(DI) ]; then $(MKDIR_P) $(DI); fi + -@if [ ! -d $(DI)/$(LIBNAME) ]; then $(MKDIR_P) $(DI)/$(LIBNAME); fi + cp png.h pngconf.h $(DI)/$(LIBNAME) + chmod 644 $(DI)/$(LIBNAME)/png.h $(DI)/$(LIBNAME)/pngconf.h + -@$(RM_F) $(DI)/png.h $(DI)/pngconf.h + -@$(RM_F) $(DI)/libpng + (cd $(DI); $(LN_SF) $(LIBNAME) libpng; $(LN_SF) $(LIBNAME)/* .) + +install-static: install-headers libpng.a + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + cp libpng.a $(DL)/$(LIBNAME).a + chmod 644 $(DL)/$(LIBNAME).a + -@$(RM_F) $(DL)/libpng.a + (cd $(DL); $(LN_SF) $(LIBNAME).a libpng.a) + +install-shared: install-headers $(LIBSOVER) libpng.pc \ + $(OLDSOVER) + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + -@$(RM_F) $(DL)/$(LIBSOVER)* $(DL)/$(LIBSO) + -@$(RM_F) $(DL)/$(LIBSOMAJ) + -@$(RM_F) $(DL)/$(OLDSO) + -@$(RM_F) $(DL)/$(OLDSOMAJ) + -@$(RM_F) $(DL)/$(OLDSOVER)* + cp $(LIBSOVER) $(DL) + cp $(OLDSOVER) $(DL) + chmod 755 $(DL)/$(LIBSOVER) + chmod 755 $(DL)/$(OLDSOVER) + (cd $(DL); \ + $(LN_SF) $(OLDSOVER) $(OLDSOMAJ); \ + $(LN_SF) $(OLDSOMAJ) $(OLDSO); \ + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ); \ + $(LN_SF) $(LIBSOMAJ) $(LIBSO)) + -@if [ ! -d $(DL)/pkgconfig ]; then $(MKDIR_P) $(DL)/pkgconfig; fi + -@$(RM_F) $(DL)/pkgconfig/$(LIBNAME).pc + -@$(RM_F) $(DL)/pkgconfig/libpng.pc + cp libpng.pc $(DL)/pkgconfig/$(LIBNAME).pc + chmod 644 $(DL)/pkgconfig/$(LIBNAME).pc + (cd $(DL)/pkgconfig; $(LN_SF) $(LIBNAME).pc libpng.pc) + +install-man: libpng.3 libpngpf.3 png.5 + -@if [ ! -d $(DM) ]; then $(MKDIR_P) $(DM); fi + -@if [ ! -d $(DM)/man3 ]; then $(MKDIR_P) $(DM)/man3; fi + -@$(RM_F) $(DM)/man3/libpng.3 + -@$(RM_F) $(DM)/man3/libpngpf.3 + cp libpng.3 $(DM)/man3 + cp libpngpf.3 $(DM)/man3 + -@if [ ! -d $(DM)/man5 ]; then $(MKDIR_P) $(DM)/man5; fi + -@$(RM_F) $(DM)/man5/png.5 + cp png.5 $(DM)/man5 + +install-config: libpng-config + -@if [ ! -d $(DB) ]; then $(MKDIR_P) $(DB); fi + -@$(RM_F) $(DB)/libpng-config + -@$(RM_F) $(DB)/$(LIBNAME)-config + cp libpng-config $(DB)/$(LIBNAME)-config + chmod 755 $(DB)/$(LIBNAME)-config + (cd $(DB); $(LN_SF) $(LIBNAME)-config libpng-config) + +install: install-static install-shared install-man install-config + +# If you installed in $(DESTDIR), test-installed won't work until you +# move the library to its final location. Use test-dd to test it +# before then. + +test-dd: + echo + echo Testing installed dynamic shared library in $(DL). + $(CC) -I$(DI) -I$(ZLIBINC) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -L$(DL) -L$(ZLIBLIB) -Wl, -rpath,$(DL) -Wl,-rpath,$(ZLIBLIB) \ + -o pngtestd `$(BINPATH)/$(LIBNAME)-config --ldflags` + ./pngtestd pngtest.png + +test-installed: + $(CC) -I$(ZLIBINC) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -L$(ZLIBLIB) -Wl,-rpath,$(ZLIBLIB) \ + -o pngtesti `$(BINPATH)/$(LIBNAME)-config --ldflags` + ./pngtesti pngtest.png + +clean: + $(RM_F) *.o libpng.a pngtest pngout.png libpng-config \ + $(LIBSO) $(LIBSOMAJ)* pngtest-static pngtesti \ + $(OLDSOVER) \ + libpng.pc + +DOCS = ANNOUNCE CHANGES INSTALL KNOWNBUG LICENSE README TODO Y2KINFO +writelock: + chmod a-w *.[ch35] $(DOCS) scripts/* + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +png.o png.pic.o: png.h pngconf.h +pngerror.o pngerror.pic.o: png.h pngconf.h +pngrio.o pngrio.pic.o: png.h pngconf.h +pngwio.o pngwio.pic.o: png.h pngconf.h +pngmem.o pngmem.pic.o: png.h pngconf.h +pngset.o pngset.pic.o: png.h pngconf.h +pngget.o pngget.pic.o: png.h pngconf.h +pngread.o pngread.pic.o: png.h pngconf.h +pngrtran.o pngrtran.pic.o: png.h pngconf.h +pngrutil.o pngrutil.pic.o: png.h pngconf.h +pngtrans.o pngtrans.pic.o: png.h pngconf.h +pngwrite.o pngwrite.pic.o: png.h pngconf.h +pngwtran.o pngwtran.pic.o: png.h pngconf.h +pngwutil.o pngwutil.pic.o: png.h pngconf.h +pngpread.o pngpread.pic.o: png.h pngconf.h + +pngtest.o: png.h pngconf.h diff --git a/Engine/lib/lpng/scripts/makefile.openbsd b/Engine/lib/lpng/scripts/makefile.openbsd new file mode 100644 index 000000000..85855f0ed --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.openbsd @@ -0,0 +1,73 @@ +# makefile for libpng +# Copyright (C) 1995 Guy Eric Schalnat, Group 42, Inc. +# Copyright (C) 2007 Glenn Randers-Pehrson +# For conditions of distribution and use, see copyright notice in png.h + +PREFIX?= /usr/local +LIBDIR= ${PREFIX}/lib +MANDIR= ${PREFIX}/man/cat + +SHLIB_MAJOR= 0 +SHLIB_MINOR= 1.2.24 + +LIB= png +SRCS= png.c pngerror.c pngget.c pngmem.c pngpread.c \ + pngread.c pngrio.c pngrtran.c pngrutil.c pngset.c pngtrans.c \ + pngwio.c pngwrite.c pngwtran.c pngwutil.c + +HDRS= png.h pngconf.h + +CFLAGS+= -Wall +CPPFLAGS+= -I${.CURDIR} -DPNG_NO_MMX_CODE + +NOPROFILE= Yes + +CLEANFILES+= pngtest.o pngtest + +MAN= libpng.3 libpngpf.3 png.5 +DOCS = ANNOUNCE CHANGES INSTALL KNOWNBUG LICENSE README TODO Y2KINFO libpng.txt + +pngtest.o: pngtest.c + ${CC} ${CPPFLAGS} ${CFLAGS} -c ${.ALLSRC} -o ${.TARGET} + +pngtest: pngtest.o + ${CC} ${LDFLAGS} ${.ALLSRC} -o ${.TARGET} -L${.OBJDIR} -lpng -lz -lm + +test: pngtest + cd ${.OBJDIR} && env \ + LD_LIBRARY_PATH="${.OBJDIR}" ${.OBJDIR}/pngtest + +beforeinstall: + if [ ! -d ${DESTDIR}${PREFIX}/include/libpng ]; then \ + ${INSTALL} -d -o root -g wheel ${DESTDIR}${PREFIX}/include/libpng; \ + fi + if [ ! -d ${DESTDIR}${LIBDIR} ]; then \ + ${INSTALL} -d -o root -g wheel ${DESTDIR}${LIBDIR}; \ + fi + if [ ! -d ${DESTDIR}${LIBDIR}/debug ]; then \ + ${INSTALL} -d -o root -g wheel ${DESTDIR}${LIBDIR}/debug; \ + fi + if [ ! -d ${DESTDIR}${MANDIR}3 ]; then \ + ${INSTALL} -d -o root -g wheel ${DESTDIR}${MANDIR}3; \ + fi + if [ ! -d ${DESTDIR}${MANDIR}5 ]; then \ + ${INSTALL} -d -o root -g wheel ${DESTDIR}${MANDIR}5; \ + fi + if [ ! -d ${DESTDIR}${PREFIX}/share/doc/png ]; then \ + ${INSTALL} -d -o root -g wheel ${DESTDIR}${PREFIX}/share/doc/png; \ + fi + +afterinstall: + @rm -f ${DESTDIR}${LIBDIR}/libpng_pic.a + @rm -f ${DESTDIR}${LIBDIR}/debug/libpng.a + @rm -f ${DESTDIR}${PREFIX}/include/png.h + @rm -f ${DESTDIR}${PREFIX}/include/pngconf.h + @rmdir ${DESTDIR}${LIBDIR}/debug 2>/dev/null || true + ${INSTALL} ${INSTALL_COPY} -o ${SHAREOWN} -g ${SHAREGRP} \ + -m ${NONBINMODE} ${HDRS} ${DESTDIR}${PREFIX}/include/libpng + ${INSTALL} ${INSTALL_COPY} -o ${SHAREOWN} -g ${SHAREGRP} \ + -m ${NONBINMODE} ${HDRS} ${DESTDIR}${PREFIX}/include + ${INSTALL} ${INSTALL_COPY} -o ${SHAREOWN} -g ${SHAREGRP} \ + -m ${NONBINMODE} ${DOCS} ${DESTDIR}${PREFIX}/share/doc/png + +.include diff --git a/Engine/lib/lpng/scripts/makefile.os2 b/Engine/lib/lpng/scripts/makefile.os2 new file mode 100644 index 000000000..588067d25 --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.os2 @@ -0,0 +1,69 @@ +# makefile for libpng on OS/2 with gcc +# For conditions of distribution and use, see copyright notice in png.h + +# Related files: pngos2.def + +CC=gcc -Zomf -s + +# Where the zlib library and include files are located +ZLIBLIB=../zlib +ZLIBINC=../zlib + +WARNMORE=-Wwrite-strings -Wpointer-arith -Wshadow \ + -Wmissing-declarations -Wtraditional -Wcast-align \ + -Wstrict-prototypes -Wmissing-prototypes #-Wconversion +CFLAGS=-I$(ZLIBINC) -Wall -O6 -funroll-loops -malign-loops=2 \ + -malign-functions=2 #$(WARNMORE) -g -DPNG_DEBUG=5 +LDFLAGS=-L. -L$(ZLIBLIB) -lpng -lzdll -Zcrtdll +AR=emxomfar + +PNGLIB=png.lib +IMPLIB=emximp +SHAREDLIB=png.dll +SHAREDLIBIMP=pngdll.lib + +OBJS = png.o pngset.o pngget.o pngrutil.o pngtrans.o pngwutil.o \ + pngread.o pngrio.o pngwio.o pngwrite.o pngrtran.o \ + pngwtran.o pngmem.o pngerror.o pngpread.o + +.SUFFIXES: .c .o + +all: $(PNGLIB) $(SHAREDLIB) $(SHAREDLIBIMP) + +$(PNGLIB): $(OBJS) + $(AR) rc $@ $(OBJS) + +$(SHAREDLIB): $(OBJS) pngos2.def + $(CC) $(LDFLAGS) -Zdll -o $@ $^ + +$(SHAREDLIBIMP): pngos2.def + $(IMPLIB) -o $@ $^ + +pngtest.exe: pngtest.o png.dll pngdll.lib + $(CC) -o $@ $(CFLAGS) $< $(LDFLAGS) + +test: pngtest.exe + ./pngtest.exe + +clean: + rm -f *.o $(PNGLIB) png.dll pngdll.lib pngtest.exe pngout.png + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +png.o png.pic.o: png.h pngconf.h +pngerror.o pngerror.pic.o: png.h pngconf.h +pngrio.o pngrio.pic.o: png.h pngconf.h +pngwio.o pngwio.pic.o: png.h pngconf.h +pngmem.o pngmem.pic.o: png.h pngconf.h +pngset.o pngset.pic.o: png.h pngconf.h +pngget.o pngget.pic.o: png.h pngconf.h +pngread.o pngread.pic.o: png.h pngconf.h +pngrtran.o pngrtran.pic.o: png.h pngconf.h +pngrutil.o pngrutil.pic.o: png.h pngconf.h +pngtrans.o pngtrans.pic.o: png.h pngconf.h +pngwrite.o pngwrite.pic.o: png.h pngconf.h +pngwtran.o pngwtran.pic.o: png.h pngconf.h +pngwutil.o pngwutil.pic.o: png.h pngconf.h +pngpread.o pngpread.pic.o: png.h pngconf.h + +pngtest.o: png.h pngconf.h diff --git a/Engine/lib/lpng/scripts/makefile.sco b/Engine/lib/lpng/scripts/makefile.sco new file mode 100644 index 000000000..7ab2b66d4 --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.sco @@ -0,0 +1,229 @@ +# makefile for SCO OSr5 ELF and Unixware 7 with Native cc +# Contributed by Mike Hopkirk (hops@sco.com) modified from Makefile.lnx +# force ELF build dynamic linking, SONAME setting in lib and RPATH in app +# Copyright (C) 2002, 2006 Glenn Randers-Pehrson +# Copyright (C) 1998 Greg Roelofs +# Copyright (C) 1996, 1997 Andreas Dilger +# For conditions of distribution and use, see copyright notice in png.h + +# Library name: +LIBNAME = libpng12 +PNGMAJ = 0 +PNGMIN = 1.2.24 +PNGVER = $(PNGMAJ).$(PNGMIN) + +# Shared library names: +LIBSO=$(LIBNAME).so +LIBSOMAJ=$(LIBNAME).so.$(PNGMAJ) +LIBSOVER=$(LIBNAME).so.$(PNGVER) +OLDSO=libpng.so +OLDSOMAJ=libpng.so.3 +OLDSOVER=libpng.so.3.$(PNGMIN) + +# Utilities: +CC=cc +AR_RC=ar rc +MKDIR_P=mkdir +LN_SF=ln -f -s +RANLIB=echo +RM_F=/bin/rm -f + +# where make install puts libpng.a, $(OLDSO)*, and png.h +prefix=/usr/local +exec_prefix=$(prefix) + +# Where the zlib library and include files are located +#ZLIBLIB=/usr/local/lib +#ZLIBINC=/usr/local/include +ZLIBLIB=../zlib +ZLIBINC=../zlib + +CFLAGS= -dy -belf -I$(ZLIBINC) -O3 -DPNG_NO_MMX_CODE +LDFLAGS=-L. -L$(ZLIBLIB) -lpng12 -lz -lm + +INCPATH=$(prefix)/include/libpng +LIBPATH=$(exec_prefix)/lib +MANPATH=$(prefix)/man +BINPATH=$(exec_prefix)/bin + +# override DESTDIR= on the make install command line to easily support +# installing into a temporary location. Example: +# +# make install DESTDIR=/tmp/build/libpng +# +# If you're going to install into a temporary location +# via DESTDIR, $(DESTDIR)$(prefix) must already exist before +# you execute make install. +DESTDIR= + +DB=$(DESTDIR)$(BINPATH) +DI=$(DESTDIR)$(INCPATH) +DL=$(DESTDIR)$(LIBPATH) +DM=$(DESTDIR)$(MANPATH) + +OBJS = png.o pngset.o pngget.o pngrutil.o pngtrans.o pngwutil.o \ + pngread.o pngrio.o pngwio.o pngwrite.o pngrtran.o \ + pngwtran.o pngmem.o pngerror.o pngpread.o + +OBJSDLL = $(OBJS:.o=.pic.o) + +.SUFFIXES: .c .o .pic.o + +.c.pic.o: + $(CC) -c $(CFLAGS) -KPIC -o $@ $*.c + +all: libpng.a $(LIBSO) pngtest libpng.pc libpng-config + +libpng.a: $(OBJS) + $(AR_RC) $@ $(OBJS) + $(RANLIB) $@ + +libpng.pc: + cat scripts/libpng.pc.in | sed -e s!@prefix@!$(prefix)! \ + -e s!@exec_prefix@!$(exec_prefix)! \ + -e s!@libdir@!$(LIBPATH)! \ + -e s!@includedir@!$(INCPATH)! \ + -e s!-lpng12!-lpng12\ -lz\ -lm! > libpng.pc + +libpng-config: + ( cat scripts/libpng-config-head.in; \ + echo prefix=\"$(prefix)\"; \ + echo I_opts=\"-I$(INCPATH)/$(LIBNAME)\"; \ + echo ccopts=\"-belf\"; \ + echo L_opts=\"-L$(LIBPATH)\"; \ + echo libs=\"-lpng12 -lz -lm\"; \ + cat scripts/libpng-config-body.in ) > libpng-config + chmod +x libpng-config + +$(LIBSO): $(LIBSOMAJ) + $(LN_SF) $(LIBSOMAJ) $(LIBSO) + +$(LIBSOMAJ): $(LIBSOVER) + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ) + +$(LIBSOVER): $(OBJSDLL) + $(CC) -G -Wl,-h,$(LIBSOMAJ) -o $(LIBSOVER) \ + $(OBJSDLL) + +$(OLDSOVER): $(OBJSDLL) + $(CC) -G -Wl,-h,$(OLDSOMAJ) -o $(OLDSOVER) \ + $(OBJSDLL) + +pngtest: pngtest.o $(LIBSO) + LD_RUN_PATH=.:$(ZLIBLIB) $(CC) -o pngtest $(CFLAGS) pngtest.o $(LDFLAGS) + +test: pngtest + ./pngtest + +install-headers: png.h pngconf.h + -@if [ ! -d $(DI) ]; then $(MKDIR_P) $(DI); fi + -@if [ ! -d $(DI)/$(LIBNAME) ]; then $(MKDIR_P) $(DI)/$(LIBNAME); fi + -@$(RM_F) $(DI)/png.h + -@$(RM_F) $(DI)/pngconf.h + cp png.h pngconf.h $(DI)/$(LIBNAME) + chmod 644 $(DI)/$(LIBNAME)/png.h $(DI)/$(LIBNAME)/pngconf.h + -@$(RM_F) $(DI)/png.h $(DI)/pngconf.h + -@$(RM_F) $(DI)/libpng + (cd $(DI); $(LN_SF) $(LIBNAME) libpng; $(LN_SF) $(LIBNAME)/* .) + +install-static: install-headers libpng.a + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + cp libpng.a $(DL)/$(LIBNAME).a + chmod 644 $(DL)/$(LIBNAME).a + -@$(RM_F) $(DL)/libpng.a + (cd $(DL); $(LN_SF) $(LIBNAME).a libpng.a) + +install-shared: install-headers $(LIBSOVER) libpng.pc \ + $(OLDSOVER) + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + -@$(RM_F) $(DL)/$(LIBSOVER)* $(DL)/$(LIBSO) + -@$(RM_F) $(DL)/$(LIBSOMAJ) + -@$(RM_F) $(DL)/$(OLDSO) + -@$(RM_F) $(DL)/$(OLDSOMAJ) + -@$(RM_F) $(DL)/$(OLDSOVER)* + cp $(LIBSOVER) $(DL) + cp $(OLDSOVER) $(DL) + chmod 755 $(DL)/$(LIBSOVER) + chmod 755 $(DL)/$(OLDSOVER) + (cd $(DL); \ + $(LN_SF) $(OLDSOVER) $(OLDSOMAJ); \ + $(LN_SF) $(OLDSOMAJ) $(OLDSO); \ + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ); \ + $(LN_SF) $(LIBSOMAJ) $(LIBSO)) + -@if [ ! -d $(DL)/pkgconfig ]; then $(MKDIR_P) $(DL)/pkgconfig; fi + -@$(RM_F) $(DL)/pkgconfig/$(LIBNAME).pc + -@$(RM_F) $(DL)/pkgconfig/libpng.pc + cp libpng.pc $(DL)/pkgconfig/$(LIBNAME).pc + chmod 644 $(DL)/pkgconfig/$(LIBNAME).pc + (cd $(DL)/pkgconfig; $(LN_SF) $(LIBNAME).pc libpng.pc) + +install-man: libpng.3 libpngpf.3 png.5 + -@if [ ! -d $(DM) ]; then $(MKDIR_P) $(DM); fi + -@if [ ! -d $(DM)/man3 ]; then $(MKDIR_P) $(DM)/man3; fi + -@$(RM_F) $(DM)/man3/libpng.3 + -@$(RM_F) $(DM)/man3/libpngpf.3 + cp libpng.3 $(DM)/man3 + cp libpngpf.3 $(DM)/man3 + -@if [ ! -d $(DM)/man5 ]; then $(MKDIR_P) $(DM)/man5; fi + -@$(RM_F) $(DM)/man5/png.5 + cp png.5 $(DM)/man5 + +install-config: libpng-config + -@if [ ! -d $(DB) ]; then $(MKDIR_P) $(DB); fi + -@$(RM_F) $(DB)/libpng-config + -@$(RM_F) $(DB)/$(LIBNAME)-config + cp libpng-config $(DB)/$(LIBNAME)-config + chmod 755 $(DB)/$(LIBNAME)-config + (cd $(DB); $(LN_SF) $(LIBNAME)-config libpng-config) + +install: install-static install-shared install-man install-config + +# If you installed in $(DESTDIR), test-installed won't work until you +# move the library to its final location. Use test-dd to test it +# before then. + +test-dd: + echo + echo Testing installed dynamic shared library in $(DL). + $(CC) -I$(DI) $(CFLAGS) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -L$(DL) -L$(ZLIBLIB) \ + -o pngtestd `$(BINPATH)/$(LIBNAME)-config --ldflags` + ./pngtestd pngtest.png + +test-installed: + $(CC) $(CFLAGS) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -L$(ZLIBLIB) \ + -o pngtesti `$(BINPATH)/$(LIBNAME)-config --ldflags` + ./pngtesti pngtest.png + +clean: + $(RM_F) *.o libpng.a pngtest pngout.png libpng-config \ + $(LIBSO) $(LIBSOMAJ)* pngtest-static pngtesti \ + $(OLDSOVER) \ + libpng.pc + +DOCS = ANNOUNCE CHANGES INSTALL KNOWNBUG LICENSE README TODO Y2KINFO +writelock: + chmod a-w *.[ch35] $(DOCS) scripts/* + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +png.o png.pic.o: png.h pngconf.h +pngerror.o pngerror.pic.o: png.h pngconf.h +pngrio.o pngrio.pic.o: png.h pngconf.h +pngwio.o pngwio.pic.o: png.h pngconf.h +pngmem.o pngmem.pic.o: png.h pngconf.h +pngset.o pngset.pic.o: png.h pngconf.h +pngget.o pngget.pic.o: png.h pngconf.h +pngread.o pngread.pic.o: png.h pngconf.h +pngrtran.o pngrtran.pic.o: png.h pngconf.h +pngrutil.o pngrutil.pic.o: png.h pngconf.h +pngtrans.o pngtrans.pic.o: png.h pngconf.h +pngwrite.o pngwrite.pic.o: png.h pngconf.h +pngwtran.o pngwtran.pic.o: png.h pngconf.h +pngwutil.o pngwutil.pic.o: png.h pngconf.h +pngpread.o pngpread.pic.o: png.h pngconf.h + +pngtest.o: png.h pngconf.h diff --git a/Engine/lib/lpng/scripts/makefile.sggcc b/Engine/lib/lpng/scripts/makefile.sggcc new file mode 100644 index 000000000..b7ee63e0e --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.sggcc @@ -0,0 +1,242 @@ +# makefile for libpng.a and libpng12.so, SGI IRIX with 'cc' +# Copyright (C) 2001-2002, 2006 Glenn Randers-Pehrson +# Copyright (C) 1995 Guy Eric Schalnat, Group 42, Inc. +# For conditions of distribution and use, see copyright notice in png.h + +# Library name: +LIBNAME=libpng12 +PNGMAJ = 0 +PNGMIN = 1.2.24 +PNGVER = $(PNGMAJ).$(PNGMIN) + +# Shared library names: +LIBSO=$(LIBNAME).so +LIBSOMAJ=$(LIBNAME).so.$(PNGMAJ) +LIBSOVER=$(LIBNAME).so.$(PNGVER) +OLDSO=libpng.so +OLDSOMAJ=libpng.so.3 +OLDSOVER=libpng.so.3.$(PNGMIN) + +# Utilities: +AR_RC=ar rc +CC=gcc +MKDIR_P=mkdir -p +LN_SF=ln -sf +RANLIB=echo +RM_F=/bin/rm -f + +# Where make install puts libpng.a, libpng12.so, and libpng12/png.h +# Prefix must be a full pathname. + +prefix=/usr/local +exec_prefix=$(prefix) + +# Where the zlib library and include files are located +#ZLIBLIB=/usr/local/lib32 +#ZLIBINC=/usr/local/include +#ZLIBLIB=/usr/local/lib +#ZLIBINC=/usr/local/include +ZLIBLIB=../zlib +ZLIBINC=../zlib + +# ABI can be blank to use default for your system, -32, -o32, -n32, or -64 +# See "man abi". zlib must be built with the same ABI. +ABI= + +WARNMORE= # -g -DPNG_DEBUG=5 +CFLAGS=$(ABI) -I$(ZLIBINC) -O $(WARNMORE) -fPIC -mabi=n32 -DPNG_NO_MMX_CODE +LDFLAGS=$(ABI) -L. -L$(ZLIBLIB) -lpng -lz -lm +LDSHARED=cc $(ABI) -shared -soname $(LIBSOMAJ) \ + -set_version sgi$(PNGMAJ).0 +LDLEGACY=cc $(ABI) -shared -soname $(OLDSOMAJ) \ + -set_version sgi$3.0 +# See "man dso" for info about shared objects + +INCPATH=$(prefix)/include +LIBPATH=$(exec_prefix)/lib +#LIBPATH=$(exec_prefix)/lib32 +MANPATH=$(prefix)/man +BINPATH=$(exec_prefix)/bin + +# override DESTDIR= on the make install command line to easily support +# installing into a temporary location. Example: +# +# make install DESTDIR=/tmp/build/libpng +# +# If you're going to install into a temporary location +# via DESTDIR, $(DESTDIR)$(prefix) must already exist before +# you execute make install. +DESTDIR= + +DB=$(DESTDIR)$(BINPATH) +DI=$(DESTDIR)$(INCPATH) +DL=$(DESTDIR)$(LIBPATH) +DM=$(DESTDIR)$(MANPATH) + +OBJS = png.o pngset.o pngget.o pngrutil.o pngtrans.o pngwutil.o \ + pngread.o pngrio.o pngwio.o pngwrite.o pngrtran.o \ + pngwtran.o pngmem.o pngerror.o pngpread.o + +all: libpng.a pngtest shared libpng.pc libpng-config + +libpng.a: $(OBJS) + $(AR_RC) $@ $(OBJS) + $(RANLIB) $@ + +shared: $(LIBSOVER) + +libpng.pc: + cat scripts/libpng.pc.in | sed -e s!@prefix@!$(prefix)! \ + -e s!@exec_prefix@!$(exec_prefix)! \ + -e s!@libdir@!$(LIBPATH)! \ + -e s!@includedir@!$(INCPATH)! \ + -e s!-lpng12!-lpng12\ -lz\ -lm! > libpng.pc + +libpng-config: + ( cat scripts/libpng-config-head.in; \ + echo prefix=\"$(prefix)\"; \ + echo I_opts=\"-I$(INCPATH)/$(LIBNAME)\"; \ + echo ccopts=\"$(ABI)\"; \ + echo cppflags=\"-DPNG_NO_MMX_CODE\"; \ + echo ldopts=\"$(ABI)\"; \ + echo L_opts=\"-L$(LIBPATH)\"; \ + echo libdir=\"$(LIBPATH)\"; \ + echo libs=\"-lpng12 -lz -lm\"; \ + cat scripts/libpng-config-body.in ) > libpng-config + chmod +x libpng-config + +$(LIBSO): $(LIBSOMAJ) + $(LN_SF) $(LIBSOMAJ) $(LIBSO) + +$(LIBSOMAJ): $(LIBSOVER) + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ) + +$(LIBSOVER): $(OBJS) + $(LDSHARED) -o $@ $(OBJS) + $(RM_F) $(LIBSO) $(LIBSOMAJ) + +$(OLDSOVER): $(OBJS) + $(LDLEGACY) -o $@ $(OBJS) + +pngtest: pngtest.o libpng.a + $(CC) -o pngtest $(CFLAGS) pngtest.o $(LDFLAGS) + +test: pngtest + echo + echo Testing local static library. + ./pngtest + +install-headers: png.h pngconf.h + -@if [ ! -d $(DI) ]; then $(MKDIR_P) $(DI); fi + -@if [ ! -d $(DI)/$(LIBNAME) ]; then $(MKDIR_P) $(DI)/$(LIBNAME); fi + cp png.h pngconf.h $(DI)/$(LIBNAME) + chmod 644 $(DI)/$(LIBNAME)/png.h $(DI)/$(LIBNAME)/pngconf.h + -@$(RM_F) $(DI)/png.h $(DI)/pngconf.h + -@$(RM_F) $(DI)/libpng + (cd $(DI); $(LN_SF) $(LIBNAME) libpng; $(LN_SF) $(LIBNAME)/* .) + +install-static: install-headers libpng.a + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + cp libpng.a $(DL)/$(LIBNAME).a + chmod 644 $(DL)/$(LIBNAME).a + -@$(RM_F) $(DL)/libpng.a + (cd $(DL); $(LN_SF) $(LIBNAME).a libpng.a) + +install-shared: install-headers $(LIBSOVER) libpng.pc \ + $(OLDSOVER) + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + -@$(RM_F) $(DL)/$(LIBSOVER)* $(DL)/$(LIBSO) + -@$(RM_F) $(DL)/$(LIBSOMAJ) + -@$(RM_F) $(DL)/$(OLDSO) + -@$(RM_F) $(DL)/$(OLDSOMAJ) + -@$(RM_F) $(DL)/$(OLDSOVER)* + cp $(LIBSOVER) $(DL) + cp $(OLDSOVER) $(DL) + chmod 755 $(DL)/$(LIBSOVER) + chmod 755 $(DL)/$(OLDSOVER) + (cd $(DL); \ + $(LN_SF) $(OLDSOVER) $(OLDSOMAJ); \ + $(LN_SF) $(OLDSOMAJ) $(OLDSO); \ + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ); \ + $(LN_SF) $(LIBSOMAJ) $(LIBSO)) + -@if [ ! -d $(DL)/pkgconfig ]; then $(MKDIR_P) $(DL)/pkgconfig; fi + -@$(RM_F) $(DL)/pkgconfig/$(LIBNAME).pc + -@$(RM_F) $(DL)/pkgconfig/libpng.pc + cp libpng.pc $(DL)/pkgconfig/$(LIBNAME).pc + chmod 644 $(DL)/pkgconfig/$(LIBNAME).pc + (cd $(DL)/pkgconfig; $(LN_SF) $(LIBNAME).pc libpng.pc) + +install-man: libpng.3 libpngpf.3 png.5 + -@if [ ! -d $(DM) ]; then $(MKDIR_P) $(DM); fi + -@if [ ! -d $(DM)/man3 ]; then $(MKDIR_P) $(DM)/man3; fi + -@$(RM_F) $(DM)/man3/libpng.3 + -@$(RM_F) $(DM)/man3/libpngpf.3 + cp libpng.3 $(DM)/man3 + cp libpngpf.3 $(DM)/man3 + -@if [ ! -d $(DM)/man5 ]; then $(MKDIR_P) $(DM)/man5; fi + -@$(RM_F) $(DM)/man5/png.5 + cp png.5 $(DM)/man5 + +install-config: libpng-config + -@if [ ! -d $(DB) ]; then $(MKDIR_P) $(DB); fi + -@$(RM_F) $(DB)/libpng-config + -@$(RM_F) $(DB)/$(LIBNAME)-config + cp libpng-config $(DB)/$(LIBNAME)-config + chmod 755 $(DB)/$(LIBNAME)-config + (cd $(DB); $(LN_SF) $(LIBNAME)-config libpng-config) + +install: install-static install-shared install-man install-config + +# If you installed in $(DESTDIR), test-installed won't work until you +# move the library to its final location. Use test-dd to test it +# before then. + +test-dd: + echo + echo Testing installed dynamic shared library in $(DL). + $(CC) -I$(DI) -I$(ZLIBINC) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -L$(DL) -L$(ZLIBLIB) \ + -rpath $(ZLIBLIB):$(DL) \ + -o pngtestd `$(BINPATH)/$(LIBNAME)-config --ldflags` + ./pngtestd pngtest.png + +test-installed: + echo + echo Testing installed dynamic shared library. + $(CC) -I$(ZLIBINC) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -L$(ZLIBLIB) \ + -rpath $(ZLIBLIB):`$(BINPATH)/$(LIBNAME)-config --libdir` \ + -o pngtesti `$(BINPATH)/$(LIBNAME)-config --ldflags` + ./pngtesti pngtest.png + +clean: + $(RM_F) libpng.a pngtest pngtesti pngout.png libpng.pc libpng-config \ + $(LIBSO) $(LIBSOMAJ)* \ + $(OLDSOVER) \ + so_locations + +DOCS = ANNOUNCE CHANGES INSTALL KNOWNBUG LICENSE README TODO Y2KINFO +writelock: + chmod a-w *.[ch35] $(DOCS) scripts/* + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +png.o: png.h pngconf.h +pngerror.o: png.h pngconf.h +pngrio.o: png.h pngconf.h +pngwio.o: png.h pngconf.h +pngmem.o: png.h pngconf.h +pngset.o: png.h pngconf.h +pngget.o: png.h pngconf.h +pngread.o: png.h pngconf.h +pngrtran.o: png.h pngconf.h +pngrutil.o: png.h pngconf.h +pngtest.o: png.h pngconf.h +pngtrans.o: png.h pngconf.h +pngwrite.o: png.h pngconf.h +pngwtran.o: png.h pngconf.h +pngwutil.o: png.h pngconf.h +pngpread.o: png.h pngconf.h + diff --git a/Engine/lib/lpng/scripts/makefile.sgi b/Engine/lib/lpng/scripts/makefile.sgi new file mode 100644 index 000000000..803aac861 --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.sgi @@ -0,0 +1,245 @@ +# makefile for libpng.a and libpng12.so, SGI IRIX with 'cc' +# Copyright (C) 2001-2002, 2006, 2007 Glenn Randers-Pehrson +# Copyright (C) 1995 Guy Eric Schalnat, Group 42, Inc. +# For conditions of distribution and use, see copyright notice in png.h + +# Library name: +LIBNAME=libpng12 +PNGMAJ = 0 +PNGMIN = 1.2.24 +PNGVER = $(PNGMAJ).$(PNGMIN) + +# Shared library names: +LIBSO=$(LIBNAME).so +LIBSOMAJ=$(LIBNAME).so.$(PNGMAJ) +LIBSOVER=$(LIBNAME).so.$(PNGVER) +OLDSO=libpng.so +OLDSOMAJ=libpng.so.3 +OLDSOVER=libpng.so.3.$(PNGMIN) + +# Utilities: +AR_RC=ar rc +CC=cc +MKDIR_P=mkdir -p +LN_SF=ln -sf +RANLIB=echo +RM_F=/bin/rm -f + +# Where make install puts libpng.a, libpng12.so, and libpng12/png.h +# Prefix must be a full pathname. + +prefix=/usr/local +exec_prefix=$(prefix) + +# Where the zlib library and include files are located +#ZLIBLIB=/usr/local/lib32 +#ZLIBINC=/usr/local/include +#ZLIBLIB=/usr/local/lib +#ZLIBINC=/usr/local/include +ZLIBLIB=../zlib +ZLIBINC=../zlib + +# ABI can be blank to use default for your system, -32, -o32, -n32, or -64 +# See "man abi". zlib must be built with the same ABI. +ABI= + +WARNMORE=-fullwarn +# Note: -KPIC is the default anyhow +#CFLAGS= $(ABI) -I$(ZLIBINC) -O $(WARNMORE) -KPIC -DPNG_NO_MMX_CODE # -g -DPNG_DEBUG=5 +CFLAGS=$(ABI) -I$(ZLIBINC) -O $(WARNMORE) -DPNG_NO_MMX_CODE +LDFLAGS_A=$(ABI) -L. -L$(ZLIBLIB) -lpng12 -lz -lm +LDFLAGS=$(ABI) -L. -L$(ZLIBLIB) -lpng -lz -lm +LDSHARED=cc $(ABI) -shared -soname $(LIBSOMAJ) \ + -set_version sgi$(PNGMAJ).0 +LDLEGACY=cc $(ABI) -shared -soname $(OLDSOMAJ) \ + -set_version sgi$3.0 +# See "man dso" for info about shared objects + +INCPATH=$(prefix)/include +LIBPATH=$(exec_prefix)/lib +#LIBPATH=$(exec_prefix)/lib32 +MANPATH=$(prefix)/man +BINPATH=$(exec_prefix)/bin + +# override DESTDIR= on the make install command line to easily support +# installing into a temporary location. Example: +# +# make install DESTDIR=/tmp/build/libpng +# +# If you're going to install into a temporary location +# via DESTDIR, $(DESTDIR)$(prefix) must already exist before +# you execute make install. +DESTDIR= + +DB=$(DESTDIR)$(BINPATH) +DI=$(DESTDIR)$(INCPATH) +DL=$(DESTDIR)$(LIBPATH) +DM=$(DESTDIR)$(MANPATH) + +OBJS = png.o pngset.o pngget.o pngrutil.o pngtrans.o pngwutil.o \ + pngread.o pngrio.o pngwio.o pngwrite.o pngrtran.o \ + pngwtran.o pngmem.o pngerror.o pngpread.o + +all: libpng.a pngtest shared libpng.pc libpng-config + +libpng.a: $(OBJS) + $(AR_RC) $@ $(OBJS) + $(RANLIB) $@ + +shared: $(LIBSOVER) + +libpng.pc: + cat scripts/libpng.pc.in | sed -e s!@prefix@!$(prefix)! \ + -e s!@exec_prefix@!$(exec_prefix)! \ + -e s!@libdir@!$(LIBPATH)! \ + -e s!@includedir@!$(INCPATH)! \ + -e s!-lpng12!-lpng12\ -lz\ -lm! > libpng.pc + +libpng-config: + ( cat scripts/libpng-config-head.in; \ + echo prefix=\"$(prefix)\"; \ + echo I_opts=\"-I$(INCPATH)/$(LIBNAME)\"; \ + echo cppflags=\"-DPNG_NO_MMX_CODE\"; \ + echo ccopts=\"$(ABI)\"; \ + echo ldopts=\"$(ABI)\"; \ + echo L_opts=\"-L$(LIBPATH)\"; \ + echo libdir=\"$(LIBPATH)\"; \ + echo libs=\"-lpng12 -lz -lm\"; \ + cat scripts/libpng-config-body.in ) > libpng-config + chmod +x libpng-config + +$(LIBSO): $(LIBSOMAJ) + $(LN_SF) $(LIBSOMAJ) $(LIBSO) + +$(LIBSOMAJ): $(LIBSOVER) + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ) + +$(LIBSOVER): $(OBJS) + $(LDSHARED) -o $@ $(OBJS) + $(RM_F) $(LIBSO) $(LIBSOMAJ) + +$(OLDSOVER): $(OBJS) + $(LDLEGACY) -o $@ $(OBJS) + +pngtest: pngtest.o libpng.a + $(CC) -o pngtest $(CFLAGS) pngtest.o $(LDFLAGS) + +test: pngtest + echo + echo Testing local static library. + ./pngtest + +install-headers: png.h pngconf.h + -@if [ ! -d $(DI) ]; then $(MKDIR_P) $(DI); fi + -@if [ ! -d $(DI)/$(LIBNAME) ]; then $(MKDIR_P) $(DI)/$(LIBNAME); fi + cp png.h pngconf.h $(DI)/$(LIBNAME) + chmod 644 $(DI)/$(LIBNAME)/png.h $(DI)/$(LIBNAME)/pngconf.h + -@$(RM_F) $(DI)/png.h $(DI)/pngconf.h + -@$(RM_F) $(DI)/libpng + (cd $(DI); $(LN_SF) $(LIBNAME) libpng; $(LN_SF) $(LIBNAME)/* .) + +install-static: install-headers libpng.a + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + cp libpng.a $(DL)/$(LIBNAME).a + chmod 644 $(DL)/$(LIBNAME).a + -@$(RM_F) $(DL)/libpng.a + (cd $(DL); $(LN_SF) $(LIBNAME).a libpng.a) + +install-shared: install-headers $(LIBSOVER) libpng.pc \ + $(OLDSOVER) + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + -@$(RM_F) $(DL)/$(LIBSOVER)* $(DL)/$(LIBSO) + -@$(RM_F) $(DL)/$(LIBSOMAJ) + -@$(RM_F) $(DL)/$(OLDSO) + -@$(RM_F) $(DL)/$(OLDSOMAJ) + -@$(RM_F) $(DL)/$(OLDSOVER)* + cp $(LIBSOVER) $(DL) + cp $(OLDSOVER) $(DL) + chmod 755 $(DL)/$(LIBSOVER) + chmod 755 $(DL)/$(OLDSOVER) + (cd $(DL); \ + $(LN_SF) $(OLDSOVER) $(OLDSOMAJ); \ + $(LN_SF) $(OLDSOMAJ) $(OLDSO); \ + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ); \ + $(LN_SF) $(LIBSOMAJ) $(LIBSO)) + -@if [ ! -d $(DL)/pkgconfig ]; then $(MKDIR_P) $(DL)/pkgconfig; fi + -@$(RM_F) $(DL)/pkgconfig/$(LIBNAME).pc + -@$(RM_F) $(DL)/pkgconfig/libpng.pc + cp libpng.pc $(DL)/pkgconfig/$(LIBNAME).pc + chmod 644 $(DL)/pkgconfig/$(LIBNAME).pc + (cd $(DL)/pkgconfig; $(LN_SF) $(LIBNAME).pc libpng.pc) + +install-man: libpng.3 libpngpf.3 png.5 + -@if [ ! -d $(DM) ]; then $(MKDIR_P) $(DM); fi + -@if [ ! -d $(DM)/man3 ]; then $(MKDIR_P) $(DM)/man3; fi + -@$(RM_F) $(DM)/man3/libpng.3 + -@$(RM_F) $(DM)/man3/libpngpf.3 + cp libpng.3 $(DM)/man3 + cp libpngpf.3 $(DM)/man3 + -@if [ ! -d $(DM)/man5 ]; then $(MKDIR_P) $(DM)/man5; fi + -@$(RM_F) $(DM)/man5/png.5 + cp png.5 $(DM)/man5 + +install-config: libpng-config + -@if [ ! -d $(DB) ]; then $(MKDIR_P) $(DB); fi + -@$(RM_F) $(DB)/libpng-config + -@$(RM_F) $(DB)/$(LIBNAME)-config + cp libpng-config $(DB)/$(LIBNAME)-config + chmod 755 $(DB)/$(LIBNAME)-config + (cd $(DB); $(LN_SF) $(LIBNAME)-config libpng-config) + +install: install-static install-shared install-man install-config + +# If you installed in $(DESTDIR), test-installed won't work until you +# move the library to its final location. Use test-dd to test it +# before then. + +test-dd: + echo + echo Testing installed dynamic shared library in $(DL). + $(CC) -I$(DI) -I$(ZLIBINC) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -L$(DL) -L$(ZLIBLIB) \ + -rpath $(ZLIBLIB):$(DL) \ + -o pngtestd `$(BINPATH)/$(LIBNAME)-config --ldflags` + ./pngtestd pngtest.png + +test-installed: + echo + echo Testing installed dynamic shared library. + $(CC) -I$(ZLIBINC) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -L$(ZLIBLIB) \ + -rpath $(ZLIBLIB):`$(BINPATH)/$(LIBNAME)-config --libdir` \ + -o pngtesti `$(BINPATH)/$(LIBNAME)-config --ldflags` + ./pngtesti pngtest.png + +clean: + $(RM_F) *.o libpng.a pngtest pngtesti pngout.png libpng.pc libpng-config \ + $(LIBSO) $(LIBSOMAJ)* \ + $(OLDSOVER) \ + so_locations + +DOCS = ANNOUNCE CHANGES INSTALL KNOWNBUG LICENSE README TODO Y2KINFO +writelock: + chmod a-w *.[ch35] $(DOCS) scripts/* + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +png.o: png.h pngconf.h +pngerror.o: png.h pngconf.h +pngrio.o: png.h pngconf.h +pngwio.o: png.h pngconf.h +pngmem.o: png.h pngconf.h +pngset.o: png.h pngconf.h +pngget.o: png.h pngconf.h +pngread.o: png.h pngconf.h +pngrtran.o: png.h pngconf.h +pngrutil.o: png.h pngconf.h +pngtest.o: png.h pngconf.h +pngtrans.o: png.h pngconf.h +pngwrite.o: png.h pngconf.h +pngwtran.o: png.h pngconf.h +pngwutil.o: png.h pngconf.h +pngpread.o: png.h pngconf.h + diff --git a/Engine/lib/lpng/scripts/makefile.so9 b/Engine/lib/lpng/scripts/makefile.so9 new file mode 100644 index 000000000..69e21b305 --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.so9 @@ -0,0 +1,251 @@ +# makefile for libpng on Solaris 9 (beta) with Forte cc +# Updated by Chad Schrock for Solaris 9 +# Contributed by William L. Sebok, based on makefile.linux +# Copyright (C) 2002, 2006 Glenn Randers-Pehrson +# Copyright (C) 1998-2001 Greg Roelofs +# Copyright (C) 1996-1997 Andreas Dilger +# For conditions of distribution and use, see copyright notice in png.h + +# Library name: +PNGMAJ = 0 +PNGMIN = 1.2.24 +PNGVER = $(PNGMAJ).$(PNGMIN) +LIBNAME = libpng12 + +# Shared library names: +LIBSO=$(LIBNAME).so +LIBSOMAJ=$(LIBNAME).so.$(PNGMAJ) +LIBSOVER=$(LIBNAME).so.$(PNGVER) +OLDSO=libpng.so +OLDSOMAJ=libpng.so.3 +OLDSOVER=libpng.so.3.$(PNGMIN) + +# Utilities: +# gcc 2.95 doesn't work. +CC=cc +AR_RC=ar rc +MKDIR_P=mkdir -p +LN_SF=ln -f -s +RANLIB=echo +RM_F=/bin/rm -f + +# Where make install puts libpng.a, $(OLDSO)*, and png.h +prefix=/usr/local +exec_prefix=$(prefix) + +# Where the zlib library and include files are located +# Changing these to ../zlib poses a security risk. If you want +# to have zlib in an adjacent directory, specify the full path instead of "..". +#ZLIBLIB=../zlib +#ZLIBINC=../zlib +#ZLIBLIB=/usr/local/lib +#ZLIBINC=/usr/local/include +#Use the preinstalled zlib that comes with Solaris 9: +ZLIBLIB=/usr/lib +ZLIBINC=/usr/include + +#WARNMORE=-Wwrite-strings -Wpointer-arith -Wshadow \ + -Wmissing-declarations -Wtraditional -Wcast-align \ + -Wstrict-prototypes -Wmissing-prototypes #-Wconversion +#CFLAGS=-I$(ZLIBINC) -Wall -O3 $(WARNMORE) -g -DPNG_DEBUG=5 -DPNG_NO_MMX_CODE +CFLAGS=-I$(ZLIBINC) -O3 -DPNG_NO_MMX_CODE +LDFLAGS=-L. -R. -L$(ZLIBLIB) -R$(ZLIBLIB) -lpng12 -lz -lm + +INCPATH=$(prefix)/include +LIBPATH=$(exec_prefix)/lib +MANPATH=$(prefix)/man +BINPATH=$(exec_prefix)/bin + +# override DESTDIR= on the make install command line to easily support +# installing into a temporary location. Example: +# +# make install DESTDIR=/tmp/build/libpng +# +# If you're going to install into a temporary location +# via DESTDIR, $(DESTDIR)$(prefix) must already exist before +# you execute make install. +DESTDIR= + +DB=$(DESTDIR)$(BINPATH) +DI=$(DESTDIR)$(INCPATH) +DL=$(DESTDIR)$(LIBPATH) +DM=$(DESTDIR)$(MANPATH) + +OBJS = png.o pngset.o pngget.o pngrutil.o pngtrans.o pngwutil.o \ + pngread.o pngrio.o pngwio.o pngwrite.o pngrtran.o \ + pngwtran.o pngmem.o pngerror.o pngpread.o + +OBJSDLL = $(OBJS:.o=.pic.o) + +.SUFFIXES: .c .o .pic.o + +.c.pic.o: + $(CC) -c $(CFLAGS) -KPIC -o $@ $*.c + +all: libpng.a $(LIBSO) pngtest libpng.pc libpng-config + +libpng.a: $(OBJS) + $(AR_RC) $@ $(OBJS) + $(RANLIB) $@ + +libpng.pc: + cat scripts/libpng.pc.in | sed -e s!@prefix@!$(prefix)! \ + -e s!@exec_prefix@!$(exec_prefix)! \ + -e s!@libdir@!$(LIBPATH)! \ + -e s!@includedir@!$(INCPATH)! \ + -e s!-lpng12!-lpng12\ -lz\ -lm! > libpng.pc + +libpng-config: + ( cat scripts/libpng-config-head.in; \ + echo prefix=\"$(prefix)\"; \ + echo I_opts=\"-I$(INCPATH)/$(LIBNAME)\"; \ + echo L_opts=\"-L$(LIBPATH)\"; \ + echo R_opts=\"-R$(LIBPATH)\"; \ + echo libs=\"-lpng12 -lz -lm\"; \ + cat scripts/libpng-config-body.in ) > libpng-config + chmod +x libpng-config + +$(LIBSO): $(LIBSOMAJ) + $(LN_SF) $(LIBSOMAJ) $(LIBSO) + +$(LIBSOMAJ): $(LIBSOVER) + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ) + +$(LIBSOVER): $(OBJSDLL) + @case "`type ld`" in *ucb*) \ + echo; \ + echo '## WARNING:'; \ + echo '## The commands "CC" and "LD" must NOT refer to /usr/ucb/cc'; \ + echo '## and /usr/ucb/ld. If they do, you need to adjust your PATH'; \ + echo '## environment variable to put /usr/ccs/bin ahead of /usr/ucb.'; \ + echo '## The environment variable LD_LIBRARY_PATH should not be set'; \ + echo '## at all. If it is, things are likely to break because of'; \ + echo '## the libucb dependency that is created.'; \ + echo; \ + ;; \ + esac + $(LD) -G -h $(LIBSOMAJ) \ + -o $(LIBSOVER) $(OBJSDLL) + +$(OLDSOVER): $(OBJS) + $(LD) -G -h $(OLDSOMAJ) \ + -o $(OLDSOVER) $(OBJSDLL) + +pngtest: pngtest.o $(LIBSO) + $(CC) -o pngtest $(CFLAGS) pngtest.o $(LDFLAGS) + +test: pngtest + ./pngtest + +install-headers: png.h pngconf.h + -@if [ ! -d $(DI) ]; then $(MKDIR_P) $(DI); fi + -@if [ ! -d $(DI)/$(LIBNAME) ]; then $(MKDIR_P) $(DI)/$(LIBNAME); fi + cp png.h pngconf.h $(DI)/$(LIBNAME) + chmod 644 $(DI)/$(LIBNAME)/png.h $(DI)/$(LIBNAME)/pngconf.h + -@$(RM_F) $(DI)/png.h $(DI)/pngconf.h + -@$(RM_F) $(DI)/libpng + (cd $(DI); $(LN_SF) $(LIBNAME) libpng; $(LN_SF) $(LIBNAME)/* .) + +install-static: install-headers libpng.a + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + cp libpng.a $(DL)/$(LIBNAME).a + chmod 644 $(DL)/$(LIBNAME).a + -@$(RM_F) $(DL)/libpng.a + (cd $(DL); $(LN_SF) $(LIBNAME).a libpng.a) + +install-shared: install-headers $(LIBSOVER) libpng.pc \ + $(OLDSOVER) + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + -@$(RM_F) $(DL)/$(LIBSOVER)* $(DL)/$(LIBSO) + -@$(RM_F) $(DL)/$(LIBSOMAJ) + -@$(RM_F) $(DL)/$(OLDSO) + -@$(RM_F) $(DL)/$(OLDSOMAJ) + -@$(RM_F) $(DL)/$(OLDSOVER)* + cp $(LIBSOVER) $(DL) + cp $(OLDSOVER) $(DL) + chmod 755 $(DL)/$(LIBSOVER) + chmod 755 $(DL)/$(OLDSOVER) + (cd $(DL); \ + $(LN_SF) $(OLDSOVER) $(OLDSOMAJ); \ + $(LN_SF) $(OLDSOMAJ) $(OLDSO); \ + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ); \ + $(LN_SF) $(LIBSOMAJ) $(LIBSO)) + -@if [ ! -d $(DL)/pkgconfig ]; then $(MKDIR_P) $(DL)/pkgconfig; fi + -@$(RM_F) $(DL)/pkgconfig/$(LIBNAME).pc + -@$(RM_F) $(DL)/pkgconfig/libpng.pc + cp libpng.pc $(DL)/pkgconfig/$(LIBNAME).pc + chmod 644 $(DL)/pkgconfig/$(LIBNAME).pc + (cd $(DL)/pkgconfig; $(LN_SF) $(LIBNAME).pc libpng.pc) + +install-man: libpng.3 libpngpf.3 png.5 + -@if [ ! -d $(DM) ]; then $(MKDIR_P) $(DM); fi + -@if [ ! -d $(DM)/man3 ]; then $(MKDIR_P) $(DM)/man3; fi + -@$(RM_F) $(DM)/man3/libpng.3 + -@$(RM_F) $(DM)/man3/libpngpf.3 + cp libpng.3 $(DM)/man3 + cp libpngpf.3 $(DM)/man3 + -@if [ ! -d $(DM)/man5 ]; then $(MKDIR_P) $(DM)/man5; fi + -@$(RM_F) $(DM)/man5/png.5 + cp png.5 $(DM)/man5 + +install-config: libpng-config + -@if [ ! -d $(DB) ]; then $(MKDIR_P) $(DB); fi + -@$(RM_F) $(DB)/libpng-config + -@$(RM_F) $(DB)/$(LIBNAME)-config + cp libpng-config $(DB)/$(LIBNAME)-config + chmod 755 $(DB)/$(LIBNAME)-config + (cd $(DB); $(LN_SF) $(LIBNAME)-config libpng-config) + +install: install-static install-shared install-man install-config + +# If you installed in $(DESTDIR), test-installed won't work until you +# move the library to its final location. Use test-dd to test it +# before then. + +test-dd: + echo + echo Testing installed dynamic shared library in $(DL). + $(CC) -I$(DI) -I$(ZLIBINC) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -o pngtestd `$(BINPATH)/$(LIBNAME)-config --ldflags` \ + -L$(DL) -L$(ZLIBLIB) -R$(ZLIBLIB) -R$(DL) + ./pngtestd pngtest.png + +test-installed: + echo + echo Testing installed dynamic shared library. + $(CC) -I$(ZLIBINC) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -o pngtesti `$(BINPATH)/$(LIBNAME)-config --ldflags` \ + -L$(ZLIBLIB) -R$(ZLIBLIB) + ./pngtesti pngtest.png + +clean: + $(RM_F) *.o libpng.a pngtest pngtesti pngout.png \ + libpng-config $(LIBSO) $(LIBSOMAJ)* \ + $(OLDSOVER) \ + libpng.pc + +DOCS = ANNOUNCE CHANGES INSTALL KNOWNBUG LICENSE README TODO Y2KINFO +writelock: + chmod a-w *.[ch35] $(DOCS) scripts/* + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +png.o png.pic.o: png.h pngconf.h +pngerror.o pngerror.pic.o: png.h pngconf.h +pngrio.o pngrio.pic.o: png.h pngconf.h +pngwio.o pngwio.pic.o: png.h pngconf.h +pngmem.o pngmem.pic.o: png.h pngconf.h +pngset.o pngset.pic.o: png.h pngconf.h +pngget.o pngget.pic.o: png.h pngconf.h +pngread.o pngread.pic.o: png.h pngconf.h +pngrtran.o pngrtran.pic.o: png.h pngconf.h +pngrutil.o pngrutil.pic.o: png.h pngconf.h +pngtrans.o pngtrans.pic.o: png.h pngconf.h +pngwrite.o pngwrite.pic.o: png.h pngconf.h +pngwtran.o pngwtran.pic.o: png.h pngconf.h +pngwutil.o pngwutil.pic.o: png.h pngconf.h +pngpread.o pngpread.pic.o: png.h pngconf.h + +pngtest.o: png.h pngconf.h diff --git a/Engine/lib/lpng/scripts/makefile.solaris b/Engine/lib/lpng/scripts/makefile.solaris new file mode 100644 index 000000000..b9054e203 --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.solaris @@ -0,0 +1,249 @@ +# makefile for libpng on Solaris 2.x with gcc +# Copyright (C) 2004, 2006, 2007 Glenn Randers-Pehrson +# Contributed by William L. Sebok, based on makefile.linux +# Copyright (C) 1998 Greg Roelofs +# Copyright (C) 1996, 1997 Andreas Dilger +# For conditions of distribution and use, see copyright notice in png.h + +# Library name: +LIBNAME = libpng12 +PNGMAJ = 0 +PNGMIN = 1.2.24 +PNGVER = $(PNGMAJ).$(PNGMIN) + +# Shared library names: +LIBSO=$(LIBNAME).so +LIBSOMAJ=$(LIBNAME).so.$(PNGMAJ) +LIBSOVER=$(LIBNAME).so.$(PNGVER) +OLDSO=libpng.so +OLDSOMAJ=libpng.so.3 +OLDSOVER=libpng.so.3.$(PNGMIN) + +# Utilities: +AR_RC=ar rc +CC=gcc +MKDIR_P=mkdir -p +LN_SF=ln -f -s +RANLIB=echo +RM_F=/bin/rm -f + +# Where make install puts libpng.a, libpng12.so*, and png.h +prefix=/usr/local +exec_prefix=$(prefix) + +# Where the zlib library and include files are located +# Changing these to ../zlib poses a security risk. If you want +# to have zlib in an adjacent directory, specify the full path instead of "..". +#ZLIBLIB=../zlib +#ZLIBINC=../zlib + +ZLIBLIB=/usr/local/lib +ZLIBINC=/usr/local/include + +WARNMORE=-Wwrite-strings -Wpointer-arith -Wshadow \ + -Wmissing-declarations -Wtraditional -Wcast-align \ + -Wstrict-prototypes -Wmissing-prototypes #-Wconversion +CFLAGS=-I$(ZLIBINC) -Wall -O \ + -DPNG_NO_MMX_CODE; \ + # $(WARNMORE) -g -DPNG_DEBUG=5 +LDFLAGS=-L. -R. -L$(ZLIBLIB) -R$(ZLIBLIB) -lpng12 -lz -lm + +INCPATH=$(prefix)/include +LIBPATH=$(exec_prefix)/lib +MANPATH=$(prefix)/man +BINPATH=$(exec_prefix)/bin + +# override DESTDIR= on the make install command line to easily support +# installing into a temporary location. Example: +# +# make install DESTDIR=/tmp/build/libpng +# +# If you're going to install into a temporary location +# via DESTDIR, $(DESTDIR)$(prefix) must already exist before +# you execute make install. +DESTDIR= + +DB=$(DESTDIR)$(BINPATH) +DI=$(DESTDIR)$(INCPATH) +DL=$(DESTDIR)$(LIBPATH) +DM=$(DESTDIR)$(MANPATH) + +OBJS = png.o pngset.o pngget.o pngrutil.o pngtrans.o pngwutil.o \ + pngread.o pngrio.o pngwio.o pngwrite.o pngrtran.o \ + pngwtran.o pngmem.o pngerror.o pngpread.o + +OBJSDLL = $(OBJS:.o=.pic.o) + +.SUFFIXES: .c .o .pic.o + +.c.pic.o: + $(CC) -c $(CFLAGS) -fPIC -o $@ $*.c + +all: libpng.a $(LIBSO) pngtest libpng.pc libpng-config + +libpng.a: $(OBJS) + $(AR_RC) $@ $(OBJS) + $(RANLIB) $@ + +libpng.pc: + cat scripts/libpng.pc.in | sed -e s!@prefix@!$(prefix)! \ + -e s!@exec_prefix@!$(exec_prefix)! \ + -e s!@libdir@!$(LIBPATH)! \ + -e s!@includedir@!$(INCPATH)! \ + -e s!-lpng12!-lpng12\ -lz\ -lm! > libpng.pc + +libpng-config: + ( cat scripts/libpng-config-head.in; \ + echo prefix=\"$(prefix)\"; \ + echo I_opts=\"-I$(INCPATH)/$(LIBNAME)\"; \ + echo cppflags=\"-DPNG_NO_MMX_CODE\"; \ + echo L_opts=\"-L$(LIBPATH)\"; \ + echo R_opts=\"-R$(LIBPATH)\"; \ + echo libs=\"-lpng12 -lz -lm\"; \ + cat scripts/libpng-config-body.in ) > libpng-config + chmod +x libpng-config + +$(LIBSO): $(LIBSOMAJ) + $(LN_SF) $(LIBSOMAJ) $(LIBSO) + +$(LIBSOMAJ): $(LIBSOVER) + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ) + +$(LIBSOVER): $(OBJSDLL) + @case "`type ld`" in *ucb*) \ + echo; \ + echo '## WARNING:'; \ + echo '## The commands "CC" and "LD" must NOT refer to /usr/ucb/cc'; \ + echo '## and /usr/ucb/ld. If they do, you need to adjust your PATH'; \ + echo '## environment variable to put /usr/ccs/bin ahead of /usr/ucb.'; \ + echo '## The environment variable LD_LIBRARY_PATH should not be set'; \ + echo '## at all. If it is, things are likely to break because of'; \ + echo '## the libucb dependency that is created.'; \ + echo; \ + ;; \ + esac + $(LD) -G -h $(LIBSOMAJ) \ + -o $(LIBSOVER) $(OBJSDLL) + +$(OLDSOVER): $(OBJS) + $(LD) -G -h $(OLDSOMAJ) \ + -o $(OLDSOVER) $(OBJSDLL) + +pngtest: pngtest.o $(LIBSO) + $(CC) -o pngtest $(CFLAGS) pngtest.o $(LDFLAGS) + +test: pngtest + ./pngtest + +install-headers: png.h pngconf.h + -@if [ ! -d $(DI) ]; then $(MKDIR_P) $(DI); fi + -@if [ ! -d $(DI)/$(LIBNAME) ]; then $(MKDIR_P) $(DI)/$(LIBNAME); fi + cp png.h pngconf.h $(DI)/$(LIBNAME) + chmod 644 $(DI)/$(LIBNAME)/png.h $(DI)/$(LIBNAME)/pngconf.h + -@$(RM_F) $(DI)/png.h $(DI)/pngconf.h + -@$(RM_F) $(DI)/libpng + (cd $(DI); $(LN_SF) $(LIBNAME) libpng; $(LN_SF) $(LIBNAME)/* .) + +install-static: install-headers libpng.a + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + cp libpng.a $(DL)/$(LIBNAME).a + chmod 644 $(DL)/$(LIBNAME).a + -@$(RM_F) $(DL)/libpng.a + (cd $(DL); $(LN_SF) $(LIBNAME).a libpng.a) + +install-shared: install-headers $(LIBSOVER) libpng.pc \ + $(OLDSOVER) + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + -@$(RM_F) $(DL)/$(LIBSOVER)* $(DL)/$(LIBSO) + -@$(RM_F) $(DL)/$(LIBSOMAJ) + -@$(RM_F) $(DL)/$(OLDSO) + -@$(RM_F) $(DL)/$(OLDSOMAJ) + -@$(RM_F) $(DL)/$(OLDSOVER)* + cp $(LIBSOVER) $(DL) + cp $(OLDSOVER) $(DL) + chmod 755 $(DL)/$(LIBSOVER) + chmod 755 $(DL)/$(OLDSOVER) + (cd $(DL); \ + $(LN_SF) $(OLDSOVER) $(OLDSOMAJ); \ + $(LN_SF) $(OLDSOMAJ) $(OLDSO); \ + $(LN_SF) $(LIBSOVER) $(LIBSO); \ + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ)) + -@if [ ! -d $(DL)/pkgconfig ]; then $(MKDIR_P) $(DL)/pkgconfig; fi + -@$(RM_F) $(DL)/pkgconfig/$(LIBNAME).pc + -@$(RM_F) $(DL)/pkgconfig/libpng.pc + cp libpng.pc $(DL)/pkgconfig/$(LIBNAME).pc + chmod 644 $(DL)/pkgconfig/$(LIBNAME).pc + (cd $(DL)/pkgconfig; $(LN_SF) $(LIBNAME).pc libpng.pc) + +install-man: libpng.3 libpngpf.3 png.5 + -@if [ ! -d $(DM) ]; then $(MKDIR_P) $(DM); fi + -@if [ ! -d $(DM)/man3 ]; then $(MKDIR_P) $(DM)/man3; fi + -@$(RM_F) $(DM)/man3/libpng.3 + -@$(RM_F) $(DM)/man3/libpngpf.3 + cp libpng.3 $(DM)/man3 + cp libpngpf.3 $(DM)/man3 + -@if [ ! -d $(DM)/man5 ]; then $(MKDIR_P) $(DM)/man5; fi + -@$(RM_F) $(DM)/man5/png.5 + cp png.5 $(DM)/man5 + +install-config: libpng-config + -@if [ ! -d $(DB) ]; then $(MKDIR_P) $(DB); fi + -@$(RM_F) $(DB)/libpng-config + -@$(RM_F) $(DB)/$(LIBNAME)-config + cp libpng-config $(DB)/$(LIBNAME)-config + chmod 755 $(DB)/$(LIBNAME)-config + (cd $(DB); $(LN_SF) $(LIBNAME)-config libpng-config) + +install: install-static install-shared install-man install-config + +# If you installed in $(DESTDIR), test-installed won't work until you +# move the library to its final location. Use test-dd to test it +# before then. + +test-dd: + echo + echo Testing installed dynamic shared library in $(DL). + $(CC) -I$(DI) -I$(ZLIBINC) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -o pngtestd `$(BINPATH)/$(LIBNAME)-config --ldflags` \ + -L$(DL) -L$(ZLIBLIB) -R$(ZLIBLIB) -R$(DL) + ./pngtestd pngtest.png + +test-installed: + echo + echo Testing installed dynamic shared library. + $(CC) -I$(ZLIBINC) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -o pngtesti `$(BINPATH)/$(LIBNAME)-config --ldflags` \ + -L$(ZLIBLIB) -R$(ZLIBLIB) + ./pngtesti pngtest.png + +clean: + $(RM_F) *.o libpng.a pngtest pngtesti pngout.png \ + libpng-config $(LIBSO) $(LIBSOMAJ)* \ + $(OLDSOVER) \ + libpng.pc + +DOCS = ANNOUNCE CHANGES INSTALL KNOWNBUG LICENSE README TODO Y2KINFO +writelock: + chmod a-w *.[ch35] $(DOCS) scripts/* + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +png.o png.pic.o: png.h pngconf.h +pngerror.o pngerror.pic.o: png.h pngconf.h +pngrio.o pngrio.pic.o: png.h pngconf.h +pngwio.o pngwio.pic.o: png.h pngconf.h +pngmem.o pngmem.pic.o: png.h pngconf.h +pngset.o pngset.pic.o: png.h pngconf.h +pngget.o pngget.pic.o: png.h pngconf.h +pngread.o pngread.pic.o: png.h pngconf.h +pngrtran.o pngrtran.pic.o: png.h pngconf.h +pngrutil.o pngrutil.pic.o: png.h pngconf.h +pngtrans.o pngtrans.pic.o: png.h pngconf.h +pngwrite.o pngwrite.pic.o: png.h pngconf.h +pngwtran.o pngwtran.pic.o: png.h pngconf.h +pngwutil.o pngwutil.pic.o: png.h pngconf.h +pngpread.o pngpread.pic.o: png.h pngconf.h + +pngtest.o: png.h pngconf.h diff --git a/Engine/lib/lpng/scripts/makefile.solaris-x86 b/Engine/lib/lpng/scripts/makefile.solaris-x86 new file mode 100644 index 000000000..7d2bb6b6c --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.solaris-x86 @@ -0,0 +1,248 @@ +# makefile for libpng on Solaris 2.x with gcc +# Copyright (C) 2004, 2006, 2007 Glenn Randers-Pehrson +# Contributed by William L. Sebok, based on makefile.linux +# Copyright (C) 1998 Greg Roelofs +# Copyright (C) 1996, 1997 Andreas Dilger +# For conditions of distribution and use, see copyright notice in png.h + +# Library name: +LIBNAME = libpng12 +PNGMAJ = 0 +PNGMIN = 1.2.24 +PNGVER = $(PNGMAJ).$(PNGMIN) + +# Shared library names: +LIBSO=$(LIBNAME).so +LIBSOMAJ=$(LIBNAME).so.$(PNGMAJ) +LIBSOVER=$(LIBNAME).so.$(PNGVER) +OLDSO=libpng.so +OLDSOMAJ=libpng.so.3 +OLDSOVER=libpng.so.3.$(PNGMIN) + +# Utilities: +AR_RC=ar rc +CC=gcc +MKDIR_P=mkdir -p +LN_SF=ln -f -s +RANLIB=echo +RM_F=/bin/rm -f + +# Where make install puts libpng.a, libpng12.so*, and png.h +prefix=/usr/local +exec_prefix=$(prefix) + +# Where the zlib library and include files are located +# Changing these to ../zlib poses a security risk. If you want +# to have zlib in an adjacent directory, specify the full path instead of "..". +#ZLIBLIB=../zlib +#ZLIBINC=../zlib + +ZLIBLIB=/usr/local/lib +ZLIBINC=/usr/local/include + +WARNMORE=-Wwrite-strings -Wpointer-arith -Wshadow \ + -Wmissing-declarations -Wtraditional -Wcast-align \ + -Wstrict-prototypes -Wmissing-prototypes #-Wconversion +CFLAGS=-I$(ZLIBINC) -Wall -O \ + # $(WARNMORE) -g -DPNG_DEBUG=5 +LDFLAGS=-L. -R. -L$(ZLIBLIB) -R$(ZLIBLIB) -lpng12 -lz -lm + +INCPATH=$(prefix)/include +LIBPATH=$(exec_prefix)/lib +MANPATH=$(prefix)/man +BINPATH=$(exec_prefix)/bin + +# override DESTDIR= on the make install command line to easily support +# installing into a temporary location. Example: +# +# make install DESTDIR=/tmp/build/libpng +# +# If you're going to install into a temporary location +# via DESTDIR, $(DESTDIR)$(prefix) must already exist before +# you execute make install. +DESTDIR= + +DB=$(DESTDIR)$(BINPATH) +DI=$(DESTDIR)$(INCPATH) +DL=$(DESTDIR)$(LIBPATH) +DM=$(DESTDIR)$(MANPATH) + +OBJS = png.o pngset.o pngget.o pngrutil.o pngtrans.o pngwutil.o \ + pngread.o pngrio.o pngwio.o pngwrite.o pngrtran.o \ + pngwtran.o pngmem.o pngerror.o pngpread.o + +OBJSDLL = $(OBJS:.o=.pic.o) + +.SUFFIXES: .c .o .pic.o + +.c.pic.o: + $(CC) -c $(CFLAGS) -fPIC -o $@ $*.c + +all: libpng.a $(LIBSO) pngtest libpng.pc libpng-config + +libpng.a: $(OBJS) + $(AR_RC) $@ $(OBJS) + $(RANLIB) $@ + +libpng.pc: + cat scripts/libpng.pc.in | sed -e s!@prefix@!$(prefix)! \ + -e s!@exec_prefix@!$(exec_prefix)! \ + -e s!@libdir@!$(LIBPATH)! \ + -e s!@includedir@!$(INCPATH)! \ + -e s!-lpng12!-lpng12\ -lz\ -lm! > libpng.pc + +libpng-config: + ( cat scripts/libpng-config-head.in; \ + echo prefix=\"$(prefix)\"; \ + echo I_opts=\"-I$(INCPATH)/$(LIBNAME)\"; \ + echo cppflags=\""; \ + echo L_opts=\"-L$(LIBPATH)\"; \ + echo R_opts=\"-R$(LIBPATH)\"; \ + echo libs=\"-lpng12 -lz -lm\"; \ + cat scripts/libpng-config-body.in ) > libpng-config + chmod +x libpng-config + +$(LIBSO): $(LIBSOMAJ) + $(LN_SF) $(LIBSOMAJ) $(LIBSO) + +$(LIBSOMAJ): $(LIBSOVER) + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ) + +$(LIBSOVER): $(OBJSDLL) + @case "`type ld`" in *ucb*) \ + echo; \ + echo '## WARNING:'; \ + echo '## The commands "CC" and "LD" must NOT refer to /usr/ucb/cc'; \ + echo '## and /usr/ucb/ld. If they do, you need to adjust your PATH'; \ + echo '## environment variable to put /usr/ccs/bin ahead of /usr/ucb.'; \ + echo '## The environment variable LD_LIBRARY_PATH should not be set'; \ + echo '## at all. If it is, things are likely to break because of'; \ + echo '## the libucb dependency that is created.'; \ + echo; \ + ;; \ + esac + $(LD) -G -h $(LIBSOMAJ) \ + -o $(LIBSOVER) $(OBJSDLL) + +$(OLDSOVER): $(OBJS) + $(LD) -G -h $(OLDSOMAJ) \ + -o $(OLDSOVER) $(OBJSDLL) + +pngtest: pngtest.o $(LIBSO) + $(CC) -o pngtest $(CFLAGS) pngtest.o $(LDFLAGS) + +test: pngtest + ./pngtest + +install-headers: png.h pngconf.h + -@if [ ! -d $(DI) ]; then $(MKDIR_P) $(DI); fi + -@if [ ! -d $(DI)/$(LIBNAME) ]; then $(MKDIR_P) $(DI)/$(LIBNAME); fi + cp png.h pngconf.h $(DI)/$(LIBNAME) + chmod 644 $(DI)/$(LIBNAME)/png.h $(DI)/$(LIBNAME)/pngconf.h + -@$(RM_F) $(DI)/png.h $(DI)/pngconf.h + -@$(RM_F) $(DI)/libpng + (cd $(DI); $(LN_SF) $(LIBNAME) libpng; $(LN_SF) $(LIBNAME)/* .) + +install-static: install-headers libpng.a + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + cp libpng.a $(DL)/$(LIBNAME).a + chmod 644 $(DL)/$(LIBNAME).a + -@$(RM_F) $(DL)/libpng.a + (cd $(DL); $(LN_SF) $(LIBNAME).a libpng.a) + +install-shared: install-headers $(LIBSOVER) libpng.pc \ + $(OLDSOVER) + -@if [ ! -d $(DL) ]; then $(MKDIR_P) $(DL); fi + -@$(RM_F) $(DL)/$(LIBSOVER)* $(DL)/$(LIBSO) + -@$(RM_F) $(DL)/$(LIBSOMAJ) + -@$(RM_F) $(DL)/$(OLDSO) + -@$(RM_F) $(DL)/$(OLDSOMAJ) + -@$(RM_F) $(DL)/$(OLDSOVER)* + cp $(LIBSOVER) $(DL) + cp $(OLDSOVER) $(DL) + chmod 755 $(DL)/$(LIBSOVER) + chmod 755 $(DL)/$(OLDSOVER) + (cd $(DL); \ + $(LN_SF) $(OLDSOVER) $(OLDSOMAJ); \ + $(LN_SF) $(OLDSOMAJ) $(OLDSO); \ + $(LN_SF) $(LIBSOVER) $(LIBSO); \ + $(LN_SF) $(LIBSOVER) $(LIBSOMAJ)) + -@if [ ! -d $(DL)/pkgconfig ]; then $(MKDIR_P) $(DL)/pkgconfig; fi + -@$(RM_F) $(DL)/pkgconfig/$(LIBNAME).pc + -@$(RM_F) $(DL)/pkgconfig/libpng.pc + cp libpng.pc $(DL)/pkgconfig/$(LIBNAME).pc + chmod 644 $(DL)/pkgconfig/$(LIBNAME).pc + (cd $(DL)/pkgconfig; $(LN_SF) $(LIBNAME).pc libpng.pc) + +install-man: libpng.3 libpngpf.3 png.5 + -@if [ ! -d $(DM) ]; then $(MKDIR_P) $(DM); fi + -@if [ ! -d $(DM)/man3 ]; then $(MKDIR_P) $(DM)/man3; fi + -@$(RM_F) $(DM)/man3/libpng.3 + -@$(RM_F) $(DM)/man3/libpngpf.3 + cp libpng.3 $(DM)/man3 + cp libpngpf.3 $(DM)/man3 + -@if [ ! -d $(DM)/man5 ]; then $(MKDIR_P) $(DM)/man5; fi + -@$(RM_F) $(DM)/man5/png.5 + cp png.5 $(DM)/man5 + +install-config: libpng-config + -@if [ ! -d $(DB) ]; then $(MKDIR_P) $(DB); fi + -@$(RM_F) $(DB)/libpng-config + -@$(RM_F) $(DB)/$(LIBNAME)-config + cp libpng-config $(DB)/$(LIBNAME)-config + chmod 755 $(DB)/$(LIBNAME)-config + (cd $(DB); $(LN_SF) $(LIBNAME)-config libpng-config) + +install: install-static install-shared install-man install-config + +# If you installed in $(DESTDIR), test-installed won't work until you +# move the library to its final location. Use test-dd to test it +# before then. + +test-dd: + echo + echo Testing installed dynamic shared library in $(DL). + $(CC) -I$(DI) -I$(ZLIBINC) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -o pngtestd `$(BINPATH)/$(LIBNAME)-config --ldflags` \ + -L$(DL) -L$(ZLIBLIB) -R$(ZLIBLIB) -R$(DL) + ./pngtestd pngtest.png + +test-installed: + echo + echo Testing installed dynamic shared library. + $(CC) -I$(ZLIBINC) \ + `$(BINPATH)/$(LIBNAME)-config --cflags` pngtest.c \ + -o pngtesti `$(BINPATH)/$(LIBNAME)-config --ldflags` \ + -L$(ZLIBLIB) -R$(ZLIBLIB) + ./pngtesti pngtest.png + +clean: + $(RM_F) *.o libpng.a pngtest pngtesti pngout.png \ + libpng-config $(LIBSO) $(LIBSOMAJ)* \ + $(OLDSOVER) \ + libpng.pc + +DOCS = ANNOUNCE CHANGES INSTALL KNOWNBUG LICENSE README TODO Y2KINFO +writelock: + chmod a-w *.[ch35] $(DOCS) scripts/* + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +png.o png.pic.o: png.h pngconf.h +pngerror.o pngerror.pic.o: png.h pngconf.h +pngrio.o pngrio.pic.o: png.h pngconf.h +pngwio.o pngwio.pic.o: png.h pngconf.h +pngmem.o pngmem.pic.o: png.h pngconf.h +pngset.o pngset.pic.o: png.h pngconf.h +pngget.o pngget.pic.o: png.h pngconf.h +pngread.o pngread.pic.o: png.h pngconf.h +pngrtran.o pngrtran.pic.o: png.h pngconf.h +pngrutil.o pngrutil.pic.o: png.h pngconf.h +pngtrans.o pngtrans.pic.o: png.h pngconf.h +pngwrite.o pngwrite.pic.o: png.h pngconf.h +pngwtran.o pngwtran.pic.o: png.h pngconf.h +pngwutil.o pngwutil.pic.o: png.h pngconf.h +pngpread.o pngpread.pic.o: png.h pngconf.h + +pngtest.o: png.h pngconf.h diff --git a/Engine/lib/lpng/scripts/makefile.std b/Engine/lib/lpng/scripts/makefile.std new file mode 100644 index 000000000..9b1925d6a --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.std @@ -0,0 +1,92 @@ +# makefile for libpng +# Copyright (C) 2002, 2006 Glenn Randers-Pehrson +# Copyright (C) 1995 Guy Eric Schalnat, Group 42, Inc. +# For conditions of distribution and use, see copyright notice in png.h + +# where make install puts libpng.a and png.h +prefix=/usr/local +INCPATH=$(prefix)/include +LIBPATH=$(prefix)/lib + +# override DESTDIR= on the make install command line to easily support +# installing into a temporary location. Example: +# +# make install DESTDIR=/tmp/build/libpng +# +# If you're going to install into a temporary location +# via DESTDIR, $(DESTDIR)$(prefix) must already exist before +# you execute make install. +DESTDIR= + +# Where the zlib library and include files are located +#ZLIBLIB=/usr/local/lib +#ZLIBINC=/usr/local/include +ZLIBLIB=../zlib +ZLIBINC=../zlib + +CC=cc +AR_RC=ar rc +MKDIR_P=mkdir +LN_SF=ln -sf +RANLIB=ranlib +RM_F=rm -f + +CFLAGS=-I$(ZLIBINC) -O # -g -DPNG_DEBUG=5 +LDFLAGS=-L. -L$(ZLIBLIB) -lpng -lz -lm + +OBJS = png.o pngset.o pngget.o pngrutil.o pngtrans.o pngwutil.o \ + pngread.o pngrio.o pngwio.o pngwrite.o pngrtran.o \ + pngwtran.o pngmem.o pngerror.o pngpread.o + +all: libpng.a pngtest + +libpng.a: $(OBJS) + $(AR_RC) $@ $(OBJS) + $(RANLIB) $@ + +pngtest: pngtest.o libpng.a + $(CC) -o pngtest $(CFLAGS) pngtest.o $(LDFLAGS) + +test: pngtest + ./pngtest + +install: libpng.a + -@$(MKDIR_P) $(DESTDIR)$(INCPATH) + -@$(MKDIR_P) $(DESTDIR)$(INCPATH)/libpng + -@$(MKDIR_P) $(DESTDIR)$(LIBPATH) + -@$(RM_F) $(DESTDIR)$(INCPATH)/png.h + -@$(RM_F) $(DESTDIR)$(INCPATH)/pngconf.h + cp png.h $(DESTDIR)$(INCPATH)/libpng + cp pngconf.h $(DESTDIR)$(INCPATH)/libpng + chmod 644 $(DESTDIR)$(INCPATH)/libpng/png.h + chmod 644 $(DESTDIR)$(INCPATH)/libpng/pngconf.h + (cd $(DESTDIR)$(INCPATH); ln -f -s libpng/* .) + cp libpng.a $(DESTDIR)$(LIBPATH) + chmod 644 $(DESTDIR)$(LIBPATH)/libpng.a + +clean: + $(RM_F) *.o libpng.a pngtest pngout.png + +DOCS = ANNOUNCE CHANGES INSTALL KNOWNBUG LICENSE README TODO Y2KINFO +writelock: + chmod a-w *.[ch35] $(DOCS) scripts/* + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +png.o: png.h pngconf.h +pngerror.o: png.h pngconf.h +pngrio.o: png.h pngconf.h +pngwio.o: png.h pngconf.h +pngmem.o: png.h pngconf.h +pngset.o: png.h pngconf.h +pngget.o: png.h pngconf.h +pngread.o: png.h pngconf.h +pngrtran.o: png.h pngconf.h +pngrutil.o: png.h pngconf.h +pngtest.o: png.h pngconf.h +pngtrans.o: png.h pngconf.h +pngwrite.o: png.h pngconf.h +pngwtran.o: png.h pngconf.h +pngwutil.o: png.h pngconf.h +pngpread.o: png.h pngconf.h + diff --git a/Engine/lib/lpng/scripts/makefile.sunos b/Engine/lib/lpng/scripts/makefile.sunos new file mode 100644 index 000000000..ff1959120 --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.sunos @@ -0,0 +1,97 @@ +# makefile for libpng +# Copyright (C) 2002, 2006 Glenn Randers-Pehrson +# Copyright (C) 1995 Guy Eric Schalnat, Group 42, Inc. +# For conditions of distribution and use, see copyright notice in png.h + +# where make install puts libpng.a and png.h +prefix=/usr/local +INCPATH=$(prefix)/include +LIBPATH=$(prefix)/lib + +# override DESTDIR= on the make install command line to easily support +# installing into a temporary location. Example: +# +# make install DESTDIR=/tmp/build/libpng +# +# If you're going to install into a temporary location +# via DESTDIR, $(DESTDIR)$(prefix) must already exist before +# you execute make install. +DESTDIR= + +# Where the zlib library and include files are located +#ZLIBLIB=/usr/local/lib +#ZLIBINC=/usr/local/include +ZLIBLIB=../zlib +ZLIBINC=../zlib + + +WARNMORE=-Wwrite-strings -Wpointer-arith -Wshadow -Wconversion \ + -Wmissing-declarations -Wtraditional -Wcast-align \ + -Wstrict-prototypes -Wmissing-prototypes + +CC=gcc +AR_RC=ar rc +MKDIR_P=mkdir -p +LN_SF=ln -f -s +RANLIB=ranlib +RM_F=/bin/rm -f + +CFLAGS=-I$(ZLIBINC) -O # $(WARNMORE) -DPNG_DEBUG=5 +LDFLAGS=-L. -L$(ZLIBLIB) -lpng -lz -lm + +OBJS = png.o pngset.o pngget.o pngrutil.o pngtrans.o pngwutil.o \ + pngread.o pngrio.o pngwio.o pngwrite.o pngrtran.o \ + pngwtran.o pngmem.o pngerror.o pngpread.o + +all: libpng.a pngtest + +libpng.a: $(OBJS) + $(AR_RC) $@ $(OBJS) + $(RANLIB) $@ + +pngtest: pngtest.o libpng.a + $(CC) -o pngtest $(CFLAGS) pngtest.o $(LDFLAGS) + +test: pngtest + ./pngtest + +install: libpng.a + -@$(MKDIR_P) $(DESTDIR)$(INCPATH) + -@$(MKDIR_P) $(DESTDIR)$(INCPATH)/libpng + -@$(MKDIR_P) $(DESTDIR)$(LIBPATH) + -@$(RM_F) $(DESTDIR)$(INCPATH)/png.h + -@$(RM_F) $(DESTDIR)$(INCPATH)/pngconf.h + cp png.h $(DESTDIR)$(INCPATH)/libpng + cp pngconf.h $(DESTDIR)$(INCPATH)/libpng + chmod 644 $(DESTDIR)$(INCPATH)/libpng/png.h + chmod 644 $(DESTDIR)$(INCPATH)/libpng/pngconf.h + (cd $(DESTDIR)$(INCPATH); $(LN_SF) libpng/* .) + cp libpng.a $(DESTDIR)$(LIBPATH) + chmod 644 $(DESTDIR)$(LIBPATH)/libpng.a + +clean: + $(RM_F) *.o libpng.a pngtest pngout.png + +DOCS = ANNOUNCE CHANGES INSTALL KNOWNBUG LICENSE README TODO Y2KINFO +writelock: + chmod a-w *.[ch35] $(DOCS) scripts/* + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +png.o: png.h pngconf.h +pngerror.o: png.h pngconf.h +pngrio.o: png.h pngconf.h +pngwio.o: png.h pngconf.h +pngmem.o: png.h pngconf.h +pngset.o: png.h pngconf.h +pngget.o: png.h pngconf.h +pngread.o: png.h pngconf.h +pngrtran.o: png.h pngconf.h +pngrutil.o: png.h pngconf.h +pngtest.o: png.h pngconf.h +pngtrans.o: png.h pngconf.h +pngwrite.o: png.h pngconf.h +pngwtran.o: png.h pngconf.h +pngwutil.o: png.h pngconf.h +pngpread.o: png.h pngconf.h + diff --git a/Engine/lib/lpng/scripts/makefile.tc3 b/Engine/lib/lpng/scripts/makefile.tc3 new file mode 100644 index 000000000..21435a68e --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.tc3 @@ -0,0 +1,89 @@ +# Makefile for libpng +# TurboC/C++ (Note: All modules are compiled in C mode) + +# To use, do "make -fmakefile.tc3" + +# ----- Turbo C 3.00 (can be modified to work with earlier versions) ----- + +MODEL=l +CFLAGS=-O2 -Z -m$(MODEL) -I..\zlib +#CFLAGS=-D_NO_PROTO -O2 -Z -m$(MODEL) -I..\zlib # Turbo C older than 3.00 +CC=tcc +LD=tcc +LIB=tlib +LDFLAGS=-m$(MODEL) -L..\zlib +O=.obj +E=.exe + +# variables +OBJS1 = png$(O) pngset$(O) pngget$(O) pngrutil$(O) pngtrans$(O) pngwutil$(O) +OBJS2 = pngmem$(O) pngpread$(O) pngread$(O) pngerror$(O) pngwrite$(O) +OBJS3 = pngrtran$(O) pngwtran$(O) pngrio$(O) pngwio$(O) +OBJSL1 = +png$(O) +pngset$(O) +pngget$(O) +pngrutil$(O) +pngtrans$(O) +OBJSL2 = +pngwutil$(O) +pngmem$(O) +pngpread$(O) +pngread$(O) +pngerror$(O) +OBJSL3 = +pngwrite$(O) +pngrtran$(O) +pngwtran$(O) +pngrio$(O) +pngwio$(O) + +all: libpng$(MODEL).lib pngtest$(E) + +pngtest: pngtest$(E) + +test: pngtest$(E) + pngtest$(E) + +png$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c + +pngset$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c + +pngget$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c + +pngread$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c + +pngpread$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c + +pngrtran$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c + +pngrutil$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c + +pngerror$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c + +pngmem$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c + +pngrio$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c + +pngwio$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c + +pngtest$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c + +pngtrans$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c + +pngwrite$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c + +pngwtran$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c + +pngwutil$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c + +libpng$(MODEL).lib: $(OBJS1) $(OBJS2) $(OBJS3) + $(LIB) libpng$(MODEL) +$(OBJSL1) + $(LIB) libpng$(MODEL) +$(OBJSL2) + $(LIB) libpng$(MODEL) +$(OBJSL3) + +pngtest$(E): pngtest$(O) libpng$(MODEL).lib + $(LD) $(LDFLAGS) pngtest.obj libpng$(MODEL).lib zlib_$(MODEL).lib + +# End of makefile for libpng diff --git a/Engine/lib/lpng/scripts/makefile.vcawin32 b/Engine/lib/lpng/scripts/makefile.vcawin32 new file mode 100644 index 000000000..99f543053 --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.vcawin32 @@ -0,0 +1,99 @@ +# makefile for libpng +# Copyright (C) 1998 Tim Wegner +# For conditions of distribution and use, see copyright notice in png.h +# Assumes that zlib.lib, zconf.h, and zlib.h have been copied to ..\zlib +# To use, do "nmake /f scripts\makefile.vcawin32" + +# -------- Microsoft Visual C++ 5.0 and later, uses assembler code -------- +# If you don't want to use assembler (MMX) code, use makefile.vcwin32 instead. + +# Compiler, linker, librarian, and other tools +CC = cl +LD = link +AR = lib +CFLAGS = -DPNG_USE_PNGVCRD -nologo -MD -O2 -W3 -I..\zlib +LDFLAGS = -nologo +ARFLAGS = -nologo +RM = del + +# File extensions +O=.obj + +#uncomment next to put error messages in a file +#ERRFILE= >> pngerrs.log + +# Variables +OBJS1 = png$(O) pngerror$(O) pngget$(O) pngmem$(O) pngpread$(O) +OBJS2 = pngread$(O) pngrio$(O) pngrtran$(O) pngrutil$(O) pngset$(O) +OBJS3 = pngtrans$(O) pngwio$(O) pngwrite$(O) pngwtran$(O) pngwutil$(O) +OBJS = $(OBJS1) $(OBJS2) $(OBJS3) + +# Targets +all: libpng.lib + +png$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngset$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngget$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngread$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngpread$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngrtran$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngrutil$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngerror$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngmem$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngrio$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngwio$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngtest$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngtrans$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngwrite$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngwtran$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngwutil$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +libpng.lib: $(OBJS) + -$(RM) $@ + $(AR) $(ARFLAGS) -out:$@ $(OBJS) $(ERRFILE) + +pngtest.exe: pngtest$(O) libpng.lib + $(LD) $(LDFLAGS) -out:$@ pngtest$(O) libpng.lib ..\zlib\zlib.lib $(ERRFILE) + +test: pngtest.exe + pngtest + +clean: + -$(RM) *$(O) + -$(RM) libpng.lib + -$(RM) pngtest.exe + -$(RM) pngout.png + +# End of makefile for libpng + diff --git a/Engine/lib/lpng/scripts/makefile.vcwin32 b/Engine/lib/lpng/scripts/makefile.vcwin32 new file mode 100644 index 000000000..fc6ece67d --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.vcwin32 @@ -0,0 +1,99 @@ +# makefile for libpng +# Copyright (C) 1998 Tim Wegner +# For conditions of distribution and use, see copyright notice in png.h +# Assumes that zlib.lib, zconf.h, and zlib.h have been copied to ..\zlib +# To use, do "nmake /f scripts\makefile.vcwin32" + +# -------- Microsoft Visual C++ 2.0 and later, no assembler code -------- +# If you want to use assembler (MMX) code, use makefile.vcawin32 instead. + +# Compiler, linker, librarian, and other tools +CC = cl +LD = link +AR = lib +CFLAGS = -nologo -DPNG_NO_MMX_CODE -MD -O2 -W3 -I..\zlib +LDFLAGS = -nologo +ARFLAGS = -nologo +RM = del + +# File extensions +O=.obj + +#uncomment next to put error messages in a file +#ERRFILE= >> pngerrs.log + +# Variables +OBJS1 = png$(O) pngerror$(O) pngget$(O) pngmem$(O) pngpread$(O) +OBJS2 = pngread$(O) pngrio$(O) pngrtran$(O) pngrutil$(O) pngset$(O) +OBJS3 = pngtrans$(O) pngwio$(O) pngwrite$(O) pngwtran$(O) pngwutil$(O) +OBJS = $(OBJS1) $(OBJS2) $(OBJS3) + +# Targets +all: libpng.lib + +png$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngset$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngget$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngread$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngpread$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngrtran$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngrutil$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngerror$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngmem$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngrio$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngwio$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngtest$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngtrans$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngwrite$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngwtran$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +pngwutil$(O): png.h pngconf.h + $(CC) -c $(CFLAGS) $*.c $(ERRFILE) + +libpng.lib: $(OBJS) + -$(RM) $@ + $(AR) $(ARFLAGS) -out:$@ $(OBJS) $(ERRFILE) + +pngtest.exe: pngtest$(O) libpng.lib + $(LD) $(LDFLAGS) -out:$@ pngtest$(O) libpng.lib ..\zlib\zlib.lib $(ERRFILE) + +test: pngtest.exe + pngtest + +clean: + -$(RM) *$(O) + -$(RM) libpng.lib + -$(RM) pngtest.exe + -$(RM) pngout.png + +# End of makefile for libpng + diff --git a/Engine/lib/lpng/scripts/makefile.watcom b/Engine/lib/lpng/scripts/makefile.watcom new file mode 100644 index 000000000..5e860fc06 --- /dev/null +++ b/Engine/lib/lpng/scripts/makefile.watcom @@ -0,0 +1,109 @@ +# Makefile for libpng +# Watcom C/C++ 10.0 and later, 32-bit protected mode, flat memory model + +# Copyright (C) 2000, Pawel Mrochen, based on makefile.msc which is +# copyright 1995 Guy Eric Schalnat, Group 42, Inc. +# For conditions of distribution and use, see copyright notice in png.h + +# To use, do "wmake /f scripts\makefile.watcom" + + +# ---------------------- Watcom C/C++ 10.0 and later ----------------------- + +# Where the zlib library and include files are located +ZLIBLIB=..\zlib +ZLIBINC=..\zlib + +# Target OS +OS=DOS +#OS=NT + +# Target CPU +CPU=6 # Pentium Pro +#CPU=5 # Pentium + +# Calling convention +CALLING=r # registers +#CALLING=s # stack + +# Uncomment next to put error messages in a file +#ERRFILE=>>pngerrs + +# -------------------------------------------------------------------------- + + +CC=wcc386 +CFLAGS=-$(CPU)$(CALLING) -fp$(CPU) -fpi87 -oneatx -mf -bt=$(OS) -i=$(ZLIBINC) -zq +LD=wcl386 +LDFLAGS=-zq + +O=.obj + +OBJS1=png$(O) pngset$(O) pngget$(O) pngrutil$(O) pngtrans$(O) pngwutil$(O) +OBJS2=pngmem$(O) pngpread$(O) pngread$(O) pngerror$(O) pngwrite$(O) +OBJS3=pngrtran$(O) pngwtran$(O) pngrio$(O) pngwio$(O) + + +all: test + +png$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngset$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngget$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngread$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngpread$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngrtran$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngrutil$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngerror$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngmem$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngrio$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngwio$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngtest$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngtrans$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngwrite$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngwtran$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +pngwutil$(O): png.h pngconf.h + $(CC) $(CFLAGS) $*.c $(ERRFILE) + +libpng.lib: $(OBJS1) $(OBJS2) $(OBJS3) + wlib -b -c -n -q libpng.lib $(OBJS1) + wlib -b -c -q libpng.lib $(OBJS2) + wlib -b -c -q libpng.lib $(OBJS3) + +pngtest.exe: pngtest.obj libpng.lib + $(LD) $(LDFLAGS) pngtest.obj libpng.lib $(ZLIBLIB)\zlib.lib + +test: pngtest.exe .symbolic + pngtest.exe + + +# End of makefile for libpng diff --git a/Engine/lib/lpng/scripts/makevms.com b/Engine/lib/lpng/scripts/makevms.com new file mode 100644 index 000000000..b9e389555 --- /dev/null +++ b/Engine/lib/lpng/scripts/makevms.com @@ -0,0 +1,144 @@ +$! make libpng under VMS +$! +$! +$! Check for MMK/MMS +$! +$! This procedure accepts one parameter (contrib), which causes it to build +$! the programs from the contrib directory instead of libpng. +$! +$ p1 = f$edit(p1,"UPCASE") +$ if p1 .eqs. "CONTRIB" +$ then +$ set def [.contrib.gregbook] +$ @makevms +$ set def [-.pngminus] +$ @makevms +$ set def [--] +$ exit +$ endif +$ Make = "" +$ If F$Search ("Sys$System:MMS.EXE") .nes. "" Then Make = "MMS" +$ If F$Type (MMK) .eqs. "STRING" Then Make = "MMK" +$! +$! Look for the compiler used +$! +$ zlibsrc = "[-.zlib]" +$ ccopt="/include=''zlibsrc'" +$ if f$getsyi("HW_MODEL").ge.1024 +$ then +$ ccopt = "/prefix=all"+ccopt +$ comp = "__decc__=1" +$ if f$trnlnm("SYS").eqs."" then define sys sys$library: +$ else +$ if f$search("SYS$SYSTEM:DECC$COMPILER.EXE").eqs."" +$ then +$ if f$trnlnm("SYS").eqs."" then define sys sys$library: +$ if f$search("SYS$SYSTEM:VAXC.EXE").eqs."" +$ then +$ comp = "__gcc__=1" +$ CC :== GCC +$ else +$ comp = "__vaxc__=1" +$ endif +$ else +$ if f$trnlnm("SYS").eqs."" then define sys decc$library_include: +$ ccopt = "/decc/prefix=all"+ccopt +$ comp = "__decc__=1" +$ endif +$ endif +$! +$! Build the thing plain or with mms/mmk +$! +$ write sys$output "Compiling Libpng sources ..." +$ if make.eqs."" +$ then +$ dele pngtest.obj;* +$ CALL MAKE png.OBJ "cc ''CCOPT' png" - + png.c png.h pngconf.h +$ CALL MAKE pngpread.OBJ "cc ''CCOPT' pngpread" - + pngpread.c png.h pngconf.h +$ CALL MAKE pngset.OBJ "cc ''CCOPT' pngset" - + pngset.c png.h pngconf.h +$ CALL MAKE pngget.OBJ "cc ''CCOPT' pngget" - + pngget.c png.h pngconf.h +$ CALL MAKE pngread.OBJ "cc ''CCOPT' pngread" - + pngread.c png.h pngconf.h +$ CALL MAKE pngpread.OBJ "cc ''CCOPT' pngpread" - + pngpread.c png.h pngconf.h +$ CALL MAKE pngrtran.OBJ "cc ''CCOPT' pngrtran" - + pngrtran.c png.h pngconf.h +$ CALL MAKE pngrutil.OBJ "cc ''CCOPT' pngrutil" - + pngrutil.c png.h pngconf.h +$ CALL MAKE pngerror.OBJ "cc ''CCOPT' pngerror" - + pngerror.c png.h pngconf.h +$ CALL MAKE pngmem.OBJ "cc ''CCOPT' pngmem" - + pngmem.c png.h pngconf.h +$ CALL MAKE pngrio.OBJ "cc ''CCOPT' pngrio" - + pngrio.c png.h pngconf.h +$ CALL MAKE pngwio.OBJ "cc ''CCOPT' pngwio" - + pngwio.c png.h pngconf.h +$ CALL MAKE pngtrans.OBJ "cc ''CCOPT' pngtrans" - + pngtrans.c png.h pngconf.h +$ CALL MAKE pngwrite.OBJ "cc ''CCOPT' pngwrite" - + pngwrite.c png.h pngconf.h +$ CALL MAKE pngwtran.OBJ "cc ''CCOPT' pngwtran" - + pngwtran.c png.h pngconf.h +$ CALL MAKE pngwutil.OBJ "cc ''CCOPT' pngwutil" - + pngwutil.c png.h pngconf.h +$ write sys$output "Building Libpng ..." +$ CALL MAKE libpng.OLB "lib/crea libpng.olb *.obj" *.OBJ +$ write sys$output "Building pngtest..." +$ CALL MAKE pngtest.OBJ "cc ''CCOPT' pngtest" - + pngtest.c png.h pngconf.h +$ call make pngtest.exe - + "LINK pngtest,libpng.olb/lib,''zlibsrc'libz.olb/lib" - + pngtest.obj libpng.olb +$ write sys$output "Testing Libpng..." +$ run pngtest +$ else +$ if f$search("DESCRIP.MMS") .eqs. "" then copy/nolog [.SCRIPTS]DESCRIP.MMS [] +$ 'make'/macro=('comp',zlibsrc='zlibsrc') +$ endif +$ write sys$output "Libpng build completed" +$ exit +$! +$! +$MAKE: SUBROUTINE !SUBROUTINE TO CHECK DEPENDENCIES +$ V = 'F$Verify(0) +$! P1 = What we are trying to make +$! P2 = Command to make it +$! P3 - P8 What it depends on +$ +$ If F$Search(P1) .Eqs. "" Then Goto Makeit +$ Time = F$CvTime(F$File(P1,"RDT")) +$arg=3 +$Loop: +$ Argument = P'arg +$ If Argument .Eqs. "" Then Goto Exit +$ El=0 +$Loop2: +$ File = F$Element(El," ",Argument) +$ If File .Eqs. " " Then Goto Endl +$ AFile = "" +$Loop3: +$ OFile = AFile +$ AFile = F$Search(File) +$ If AFile .Eqs. "" .Or. AFile .Eqs. OFile Then Goto NextEl +$ If F$CvTime(F$File(AFile,"RDT")) .Ges. Time Then Goto Makeit +$ Goto Loop3 +$NextEL: +$ El = El + 1 +$ Goto Loop2 +$EndL: +$ arg=arg+1 +$ If arg .Le. 8 Then Goto Loop +$ Goto Exit +$ +$Makeit: +$ VV=F$VERIFY(0) +$ write sys$output P2 +$ 'P2 +$ VV='F$Verify(VV) +$Exit: +$ If V Then Set Verify +$ENDSUBROUTINE diff --git a/Engine/lib/lpng/scripts/pngos2.def b/Engine/lib/lpng/scripts/pngos2.def new file mode 100644 index 000000000..b97317c54 --- /dev/null +++ b/Engine/lib/lpng/scripts/pngos2.def @@ -0,0 +1,257 @@ +;---------------------------------------- +; PNG.LIB module definition file for OS/2 +;---------------------------------------- + +; Version 1.2.24 + +LIBRARY PNG +DESCRIPTION "PNG image compression library for OS/2" +CODE PRELOAD MOVEABLE DISCARDABLE +DATA PRELOAD MOVEABLE MULTIPLE + +EXPORTS + + + png_build_grayscale_palette + png_check_sig + png_chunk_error + png_chunk_warning + png_convert_from_struct_tm + png_convert_from_time_t + png_create_info_struct + png_create_read_struct + png_create_write_struct + png_data_freer + png_destroy_info_struct + png_destroy_read_struct + png_destroy_write_struct + png_error + png_free + png_free_data + png_get_IHDR + png_get_PLTE + png_get_bKGD + png_get_bit_depth + png_get_cHRM + png_get_cHRM_fixed + png_get_channels + png_get_color_type + png_get_compression_buffer_size + png_get_compression_type + png_get_copyright + png_get_error_ptr + png_get_filter_type + png_get_gAMA + png_get_gAMA_fixed + png_get_hIST + png_get_header_ver + png_get_header_version + png_get_iCCP + png_get_image_height + png_get_image_width + png_get_interlace_type + png_get_io_ptr + png_get_libpng_ver + png_get_oFFs + png_get_pCAL + png_get_pHYs + png_get_pixel_aspect_ratio + png_get_pixels_per_meter + png_get_progressive_ptr + png_get_rgb_to_gray_status + png_get_rowbytes + png_get_rows + png_get_sBIT + png_get_sCAL + png_get_sPLT + png_get_sRGB + png_get_signature + png_get_tIME + png_get_tRNS + png_get_text + png_get_unknown_chunks + png_get_user_chunk_ptr + png_get_user_transform_ptr + png_get_valid + png_get_x_offset_microns + png_get_x_offset_pixels + png_get_x_pixels_per_meter + png_get_y_offset_microns + png_get_y_offset_pixels + png_get_y_pixels_per_meter + png_malloc + png_memcpy_check + png_memset_check + png_permit_empty_plte + png_process_data + png_progressive_combine_row + png_read_end + png_read_image + png_read_info +; png_read_init ; deprecated + png_read_png + png_read_row + png_read_rows + png_read_update_info + png_reset_zstream + png_set_IHDR + png_set_PLTE + png_set_bKGD + png_set_background + png_set_bgr + png_set_cHRM + png_set_cHRM_fixed + png_set_compression_buffer_size + png_set_compression_level + png_set_compression_mem_level + png_set_compression_method + png_set_compression_strategy + png_set_compression_window_bits + png_set_crc_action + png_set_dither + png_set_error_fn + png_set_expand + png_set_filler + png_set_filter + png_set_filter_heuristics + png_set_flush + png_set_gAMA + png_set_gAMA_fixed + png_set_gamma +; png_set_gray_1_2_4_to_8 ; deprecated as of libpng-1.2.9 + png_set_gray_to_rgb + png_set_hIST + png_set_iCCP + png_set_interlace_handling + png_set_invert_alpha + png_set_invert_mono + png_set_keep_unknown_chunks + png_set_oFFs + png_set_pCAL + png_set_pHYs + png_set_packing + png_set_packswap + png_set_palette_to_rgb + png_set_progressive_read_fn + png_set_read_fn + png_set_read_status_fn + png_set_read_user_chunk_fn + png_set_read_user_transform_fn + png_set_rgb_to_gray + png_set_rgb_to_gray_fixed + png_set_rows + png_set_sBIT + png_set_sCAL + png_set_sPLT + png_set_sRGB + png_set_sRGB_gAMA_and_cHRM + png_set_shift + png_set_sig_bytes + png_set_strip_16 + png_set_strip_alpha + png_set_swap + png_set_swap_alpha + png_set_tIME + png_set_tRNS + png_set_tRNS_to_alpha + png_set_text + png_set_unknown_chunk_location + png_set_unknown_chunks + png_set_user_transform_info + png_set_write_fn + png_set_write_status_fn + png_set_write_user_transform_fn + png_sig_cmp + png_start_read_image + png_warning + png_write_chunk + png_write_chunk_data + png_write_chunk_end + png_write_chunk_start + png_write_end + png_write_flush + png_write_image + png_write_info + png_write_info_before_PLTE +; png_write_init ; deprecated + png_write_png + png_write_row + png_write_rows + png_read_init_2 + png_write_init_2 + png_access_version_number + png_init_io + png_convert_to_rfc1123 + png_set_invalid + +; Added at version 1.2.0: + png_mmx_support + png_permit_empty_plte + png_permit_mng_features + png_get_mmx_flagmask + png_get_asm_flagmask + png_get_asm_flags + png_get_mmx_bitdepth_threshold + png_get_mmx_rowbytes_threshold + png_set_asm_flags + png_init_mmx_flags + +; Added at version 1.2.2: + png_handle_as_unknown + +; Added at version 1.2.2 and deleted from 1.2.3: +; png_zalloc +; png_zfree + +; Added at version 1.2.4 + png_malloc_warn + +; Added at version 1.2.6 + png_set_user_limits + png_get_user_height_max + png_get_user_width_max +; Added at version 1.2.7 + png_set_add_alpha + +; Added at version 1.2.9 + png_get_uint_32 + png_save_uint_32 + png_get_uint_16 + png_save_uint_16 + png_get_int_32 + png_save_int_32 + png_get_uint_31 + png_set_expand_gray_1_2_4_to_8 + +; These are not present when libpng is compiled with PNG_NO_GLOBAL_ARRAYS + png_pass_start + png_pass_inc + png_pass_ystart + png_pass_yinc + png_pass_mask + png_pass_dsp_mask +; png_pass_width +; png_pass_height + +; These are not present when libpng is compiled with PNG_NO_GLOBAL_ARRAYS + png_IHDR + png_IDAT + png_IEND + png_PLTE + png_bKGD + png_cHRM + png_gAMA + png_hIST + png_iCCP + png_iTXt + png_oFFs + png_pCAL + png_pHYs + png_sBIT + png_sCAL + png_sPLT + png_sRGB + png_tEXt + png_tIME + png_tRNS + png_zTXt diff --git a/Engine/lib/lpng/scripts/pngw32.def b/Engine/lib/lpng/scripts/pngw32.def new file mode 100644 index 000000000..8cf1f31b1 --- /dev/null +++ b/Engine/lib/lpng/scripts/pngw32.def @@ -0,0 +1,238 @@ +;------------------------------------------ +; LIBPNG module definition file for Windows +;------------------------------------------ + +LIBRARY + +EXPORTS +;Version 1.2.24 + png_build_grayscale_palette @1 + png_check_sig @2 + png_chunk_error @3 + png_chunk_warning @4 + png_convert_from_struct_tm @5 + png_convert_from_time_t @6 + png_create_info_struct @7 + png_create_read_struct @8 + png_create_write_struct @9 + png_data_freer @10 + png_destroy_info_struct @11 + png_destroy_read_struct @12 + png_destroy_write_struct @13 + png_error @14 + png_free @15 + png_free_data @16 + png_get_IHDR @17 + png_get_PLTE @18 + png_get_bKGD @19 + png_get_bit_depth @20 + png_get_cHRM @21 + png_get_cHRM_fixed @22 + png_get_channels @23 + png_get_color_type @24 + png_get_compression_buffer_size @25 + png_get_compression_type @26 + png_get_copyright @27 + png_get_error_ptr @28 + png_get_filter_type @29 + png_get_gAMA @30 + png_get_gAMA_fixed @31 + png_get_hIST @32 + png_get_header_ver @33 + png_get_header_version @34 + png_get_iCCP @35 + png_get_image_height @36 + png_get_image_width @37 + png_get_interlace_type @38 + png_get_io_ptr @39 + ; png_get_libpng_ver @40 + png_get_oFFs @41 + png_get_pCAL @42 + png_get_pHYs @43 + png_get_pixel_aspect_ratio @44 + png_get_pixels_per_meter @45 + png_get_progressive_ptr @46 + png_get_rgb_to_gray_status @47 + png_get_rowbytes @48 + png_get_rows @49 + png_get_sBIT @50 + png_get_sCAL @51 + png_get_sPLT @52 + png_get_sRGB @53 + png_get_signature @54 + png_get_tIME @55 + png_get_tRNS @56 + png_get_text @57 + png_get_unknown_chunks @58 + png_get_user_chunk_ptr @59 + png_get_user_transform_ptr @60 + png_get_valid @61 + png_get_x_offset_microns @62 + png_get_x_offset_pixels @63 + png_get_x_pixels_per_meter @64 + png_get_y_offset_microns @65 + png_get_y_offset_pixels @66 + png_get_y_pixels_per_meter @67 + png_malloc @68 + png_memcpy_check @69 + png_memset_check @70 +; png_permit_empty_plte is deprecated + png_permit_empty_plte @71 + png_process_data @72 + png_progressive_combine_row @73 + png_read_end @74 + png_read_image @75 + png_read_info @76 +; png_read_init is deprecated + png_read_init @77 + png_read_png @78 + png_read_row @79 + png_read_rows @80 + png_read_update_info @81 + png_reset_zstream @82 + png_set_IHDR @83 + png_set_PLTE @84 + png_set_bKGD @85 + png_set_background @86 + png_set_bgr @87 + png_set_cHRM @88 + png_set_cHRM_fixed @89 + png_set_compression_buffer_size @90 + png_set_compression_level @91 + png_set_compression_mem_level @92 + png_set_compression_method @93 + png_set_compression_strategy @94 + png_set_compression_window_bits @95 + png_set_crc_action @96 + png_set_dither @97 + png_set_error_fn @98 + png_set_expand @99 + png_set_filler @100 + png_set_filter @101 + png_set_filter_heuristics @102 + png_set_flush @103 + png_set_gAMA @104 + png_set_gAMA_fixed @105 + png_set_gamma @106 +; png_set_gray_1_2_4_to_8 is deprecated + png_set_gray_1_2_4_to_8 @107 + png_set_gray_to_rgb @108 + png_set_hIST @109 + png_set_iCCP @110 + png_set_interlace_handling @111 + png_set_invert_alpha @112 + png_set_invert_mono @113 + png_set_keep_unknown_chunks @114 + png_set_oFFs @115 + png_set_pCAL @116 + png_set_pHYs @117 + png_set_packing @118 + png_set_packswap @119 + png_set_palette_to_rgb @120 + png_set_progressive_read_fn @121 + png_set_read_fn @122 + png_set_read_status_fn @123 + png_set_read_user_chunk_fn @124 + png_set_read_user_transform_fn @125 + png_set_rgb_to_gray @126 + png_set_rgb_to_gray_fixed @127 + png_set_rows @128 + png_set_sBIT @129 + png_set_sCAL @130 + png_set_sPLT @131 + png_set_sRGB @132 + png_set_sRGB_gAMA_and_cHRM @133 + png_set_shift @134 + png_set_sig_bytes @135 + png_set_strip_16 @136 + png_set_strip_alpha @137 + png_set_swap @138 + png_set_swap_alpha @139 + png_set_tIME @140 + png_set_tRNS @141 + png_set_tRNS_to_alpha @142 + png_set_text @143 + png_set_unknown_chunk_location @144 + png_set_unknown_chunks @145 + png_set_user_transform_info @146 + png_set_write_fn @147 + png_set_write_status_fn @148 + png_set_write_user_transform_fn @149 + png_sig_cmp @150 + png_start_read_image @151 + png_warning @152 + png_write_chunk @153 + png_write_chunk_data @154 + png_write_chunk_end @155 + png_write_chunk_start @156 + png_write_end @157 + png_write_flush @158 + png_write_image @159 + png_write_info @160 + png_write_info_before_PLTE @161 +; png_write_init is deprecated + png_write_init @162 + png_write_png @163 + png_write_row @164 + png_write_rows @165 +; png_read_init_2 and png_write_init_2 are deprecated. + png_read_init_2 @166 + png_write_init_2 @167 + png_access_version_number @168 +; png_sig_bytes @169 +; Removed from version 1.2.20 +; png_libpng_ver @170 +; + png_init_io @171 + png_convert_to_rfc1123 @172 + png_set_invalid @173 +; Added at version 1.0.12 +; For compatibility with 1.0.7-1.0.11 +; png_info_init @174 + png_read_init_3 @175 + png_write_init_3 @176 + png_info_init_3 @177 + png_destroy_struct @178 +; Added at version 1.2.0 +; For use with PNG_USER_MEM_SUPPORTED + png_destroy_struct_2 @179 + png_create_read_struct_2 @180 + png_create_write_struct_2 @181 + png_malloc_default @182 + png_free_default @183 +; MNG features + png_permit_mng_features @184 +; MMX support + png_mmx_support @185 +; png_get_mmx_flagmask @186 + png_get_asm_flagmask @187 + png_get_asm_flags @188 +; png_get_mmx_bitdepth_threshold @189 +; png_get_mmx_rowbytes_threshold @190 + png_set_asm_flags @191 +; png_init_mmx_flags @192 +; Strip error numbers + png_set_strip_error_numbers @193 +; Added at version 1.2.2 + png_handle_as_unknown @194 +; Added at version 1.2.2 and deleted from 1.2.3 +; png_zalloc @195 +; png_zfree @196 +; Added at version 1.2.4 + png_malloc_warn @195 +; Added at version 1.2.6 + png_malloc_warn @195 + png_get_user_height_max @196 + png_get_user_width_max @197 + png_set_user_limits @198 +; Added at version 1.2.7 + png_set_add_alpha @199 +; Added at version 1.2.9 + png_get_uint_32 @200 + png_save_uint_32 @201 + png_get_uint_16 @202 + png_save_uint_16 @203 + png_get_int_32 @204 + png_save_int_32 @205 + png_get_uint_31 @206 + png_set_expand_gray_1_2_4_to_8 @207 diff --git a/Engine/lib/lpng/scripts/pngw32.rc b/Engine/lib/lpng/scripts/pngw32.rc new file mode 100644 index 000000000..02e30e299 --- /dev/null +++ b/Engine/lib/lpng/scripts/pngw32.rc @@ -0,0 +1,112 @@ +#define PNG_VERSION_INFO_ONLY + +#include +#include "../png.h" + +#define _QUOTE(x) # x +#define QUOTE(x) _QUOTE(x) + +#define PNG_LIBPNG_DLLFNAME "LIBPNG" + +/* Support deprecated PRIVATEBUILD macro */ +#if defined(PRIVATEBUILD) && !defined(PNG_USER_PRIVATEBUILD) +# define PNG_USER_PRIVATEBUILD PRIVATEBUILD +#endif + +#if defined(PNG_USER_DLLFNAME_POSTFIX) && !defined(PNG_USER_PRIVATEBUILD) +# error "PNG_USER_PRIVATEBUILD must be defined as a string describing the\ + custom changes made to the library." +#endif + +/* Prioritize PNG_USER_x over PNG_LIBPNG_x */ +#ifdef PNG_USER_DLLFNAME_POSTFIX +# undef PNG_LIBPNG_DLLFNAME_POSTFIX +# define PNG_LIBPNG_DLLFNAME_POSTFIX PNG_USER_DLLFNAME_POSTFIX +#endif + +#ifdef PNG_USER_VERSIONINFO_COMMENTS +# undef PNG_LIBPNG_VERSIONINFO_COMMENTS +# define PNG_LIBPNG_VERSIONINFO_COMMENTS PNG_USER_VERSIONINFO_COMMENTS +#endif + +#if defined(PNG_DEBUG) && (PNG_DEBUG > 0) +# define VS_DEBUG VS_FF_DEBUG +# ifndef PNG_LIBPNG_DLLFNAME_POSTFIX +# define PNG_LIBPNG_DLLFNAME_POSTFIX "D" +# endif /* PNG_LIBPNG_DLLFNAME_POSTFIX */ +# ifndef PNG_LIBPNG_VERSIONINFO_COMMENTS +# define PNG_LIBPNG_VERSIONINFO_COMMENTS "PNG_DEBUG=" QUOTE(PNG_DEBUG) +# endif /* PNG_LIBPNG_VERSIONINFO_COMMENTS */ +#else +# define VS_DEBUG 0 +# ifndef PNG_LIBPNG_DLLFNAME_POSTFIX +# define PNG_LIBPNG_DLLFNAME_POSTFIX +# endif /* PNG_LIBPNG_DLLFNAME_POSTFIX */ +#endif /* defined(DEBUG)... */ + +#ifdef PNG_USER_PRIVATEBUILD +# define VS_PRIVATEBUILD VS_FF_PRIVATEBUILD +#else +# define VS_PRIVATEBUILD 0 +#endif /* PNG_USER_PRIVATEBUILD */ + +#ifdef PNG_LIBPNG_SPECIALBUILD +# define VS_SPECIALBUILD VS_FF_SPECIALBUILD +#else +# define VS_SPECIALBUILD 0 +#endif /* PNG_LIBPNG_BUILD_SPECIAL */ + +#if ((PNG_LIBPNG_BUILD_BASE_TYPE & PNG_LIBPNG_RELEASE_STATUS_MASK) !=\ + PNG_LIBPNG_BUILD_STABLE) +# define VS_PRERELEASE VS_FF_PRERELEASE +# define VS_PATCHED 0 +#else +# define VS_PRERELEASE 0 +# if (PNG_LIBPNG_BUILD_BASE_TYPE & PNG_LIBPNG_BUILD_PATCHED) +# define VS_PATCHED VS_FF_PATCHED +# else +# define VS_PATCHED 0 +# endif +#endif + +VS_VERSION_INFO VERSIONINFO +FILEVERSION PNG_LIBPNG_VER_MAJOR, PNG_LIBPNG_VER_MINOR, PNG_LIBPNG_VER_RELEASE, PNG_LIBPNG_VER_BUILD +PRODUCTVERSION PNG_LIBPNG_VER_MAJOR, PNG_LIBPNG_VER_MINOR, PNG_LIBPNG_VER_RELEASE, PNG_LIBPNG_VER_BUILD +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +FILEFLAGS VS_DEBUG | VS_PRIVATEBUILD | VS_SPECIALBUILD | VS_PRERELEASE | VS_PATCHED +FILEOS VOS__WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN BLOCK "040904E4" /* Language type = U.S English(0x0409) and Character Set = Windows, Multilingual(0x04E4) */ + BEGIN +#ifdef PNG_LIBPNG_VERSIONINFO_COMMENTS + VALUE "Comments", PNG_LIBPNG_VERSIONINFO_COMMENTS "\000" +#endif /* PNG_LIBPNG_VERSIONINFO_COMMENTS */ +#ifdef PNG_USER_VERSIONINFO_COMPANYNAME + VALUE "CompanyName", PNG_USER_VERSIONINFO_COMPANYNAME "\000" +#endif /* PNG_USER_VERSIONINFO_COMPANYNAME */ + VALUE "FileDescription", "PNG image compression library\000" + VALUE "FileVersion", PNG_LIBPNG_VER_STRING "\000" + VALUE "InternalName", PNG_LIBPNG_DLLFNAME QUOTE(PNG_LIBPNG_VER_DLLNUM) PNG_LIBPNG_DLLFNAME_POSTFIX " (Windows 32 bit)\000" + VALUE "LegalCopyright", "\251 1998-2004 Glenn Randers-Pehrson et al.\000" +#ifdef PNG_USER_VERSIONINFO_LEGALTRADEMARKS + VALUE "LegalTrademarks", PNG_USER_VERSIONINFO_LEGALTRADEMARKS "\000" +#endif /* PNG_USER_VERSIONINFO_LEGALTRADEMARKS */ + VALUE "OriginalFilename", PNG_LIBPNG_DLLFNAME QUOTE(PNG_LIBPNG_VER_DLLNUM) PNG_LIBPNG_DLLFNAME_POSTFIX ".DLL\000" +#ifdef PNG_USER_PRIVATEBUILD + VALUE "PrivateBuild", PNG_USER_PRIVATEBUILD "\000" +#endif /* PNG_USER_PRIVATEBUILD */ + VALUE "ProductName", "LibPNG\000" + VALUE "ProductVersion", "1\000" +#ifdef PNG_LIBPNG_SPECIALBUILD + VALUE "SpecialBuild", PNG_LIBPNG_SPECIALBUILD "\000" +#endif /* PNG_LIBPNG_SPECIALBUILD */ + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 0x04E4 + END +END diff --git a/Engine/lib/lpng/scripts/smakefile.ppc b/Engine/lib/lpng/scripts/smakefile.ppc new file mode 100644 index 000000000..e5c027841 --- /dev/null +++ b/Engine/lib/lpng/scripts/smakefile.ppc @@ -0,0 +1,30 @@ +# Amiga powerUP (TM) Makefile +# makefile for libpng and SAS C V6.58/7.00 PPC compiler +# Copyright (C) 1998 by Andreas R. Kleinert +# For conditions of distribution and use, see copyright notice in png.h + +CC = scppc +CFLAGS = NOSTKCHK NOSINT OPTIMIZE OPTGO OPTPEEP OPTINLOCAL OPTINL IDIR /zlib \ + OPTLOOP OPTRDEP=8 OPTDEP=8 OPTCOMP=8 +LIBNAME = libpng.a +AR = ppc-amigaos-ar +AR_FLAGS = cr +RANLIB = ppc-amigaos-ranlib +LDFLAGS = -r -o +LDLIBS = ../zlib/libzip.a LIB:scppc.a +LN = ppc-amigaos-ld +RM = delete quiet +MKDIR = makedir + +OBJS = png.o pngset.o pngget.o pngrutil.o pngtrans.o pngwutil.o pngread.o \ +pngerror.o pngpread.o pngwrite.o pngrtran.o pngwtran.o pngrio.o pngwio.o pngmem.o + +all: $(LIBNAME) pngtest + +$(LIBNAME): $(OBJS) + $(AR) $(AR_FLAGS) $@ $(OBJS) + $(RANLIB) $@ + +pngtest: pngtest.o $(LIBNAME) + $(LN) $(LDFLAGS) pngtest LIB:c_ppc.o pngtest.o $(LIBNAME) $(LDLIBS) \ +LIB:end.o diff --git a/Engine/lib/lpng/test/pngtest.c b/Engine/lib/lpng/test/pngtest.c new file mode 100644 index 000000000..dd2946bf4 --- /dev/null +++ b/Engine/lib/lpng/test/pngtest.c @@ -0,0 +1,1556 @@ + +/* pngtest.c - a simple test program to test libpng + * + * Last changed in libpng 1.2.23 - [November 6, 2007] + * For conditions of distribution and use, see copyright notice in png.h + * Copyright (c) 1998-2007 Glenn Randers-Pehrson + * (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger) + * (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.) + * + * This program reads in a PNG image, writes it out again, and then + * compares the two files. If the files are identical, this shows that + * the basic chunk handling, filtering, and (de)compression code is working + * properly. It does not currently test all of the transforms, although + * it probably should. + * + * The program will report "FAIL" in certain legitimate cases: + * 1) when the compression level or filter selection method is changed. + * 2) when the maximum IDAT size (PNG_ZBUF_SIZE in pngconf.h) is not 8192. + * 3) unknown unsafe-to-copy ancillary chunks or unknown critical chunks + * exist in the input file. + * 4) others not listed here... + * In these cases, it is best to check with another tool such as "pngcheck" + * to see what the differences between the two files are. + * + * If a filename is given on the command-line, then this file is used + * for the input, rather than the default "pngtest.png". This allows + * testing a wide variety of files easily. You can also test a number + * of files at once by typing "pngtest -m file1.png file2.png ..." + */ + +#include "png.h" + +#if defined(_WIN32_WCE) +# if _WIN32_WCE < 211 + __error__ (f|w)printf functions are not supported on old WindowsCE.; +# endif +# include +# include +# define READFILE(file, data, length, check) \ + if (ReadFile(file, data, length, &check,NULL)) check = 0 +# define WRITEFILE(file, data, length, check)) \ + if (WriteFile(file, data, length, &check, NULL)) check = 0 +# define FCLOSE(file) CloseHandle(file) +#else +# include +# include +# define READFILE(file, data, length, check) \ + check=(png_size_t)fread(data,(png_size_t)1,length,file) +# define WRITEFILE(file, data, length, check) \ + check=(png_size_t)fwrite(data,(png_size_t)1, length, file) +# define FCLOSE(file) fclose(file) +#endif + +#if defined(PNG_NO_STDIO) +# if defined(_WIN32_WCE) + typedef HANDLE png_FILE_p; +# else + typedef FILE * png_FILE_p; +# endif +#endif + +/* Makes pngtest verbose so we can find problems (needs to be before png.h) */ +#ifndef PNG_DEBUG +# define PNG_DEBUG 0 +#endif + +#if !PNG_DEBUG +# define SINGLE_ROWBUF_ALLOC /* makes buffer overruns easier to nail */ +#endif + +/* Turn on CPU timing +#define PNGTEST_TIMING +*/ + +#ifdef PNG_NO_FLOATING_POINT_SUPPORTED +#undef PNGTEST_TIMING +#endif + +#ifdef PNGTEST_TIMING +static float t_start, t_stop, t_decode, t_encode, t_misc; +#include +#endif + +#if defined(PNG_TIME_RFC1123_SUPPORTED) +#define PNG_tIME_STRING_LENGTH 30 +static int tIME_chunk_present=0; +static char tIME_string[PNG_tIME_STRING_LENGTH] = "no tIME chunk present in file"; +#endif + +static int verbose = 0; + +int test_one_file PNGARG((PNG_CONST char *inname, PNG_CONST char *outname)); + +#ifdef __TURBOC__ +#include +#endif + +/* defined so I can write to a file on gui/windowing platforms */ +/* #define STDERR stderr */ +#define STDERR stdout /* for DOS */ + +/* example of using row callbacks to make a simple progress meter */ +static int status_pass=1; +static int status_dots_requested=0; +static int status_dots=1; + +/* In case a system header (e.g., on AIX) defined jmpbuf */ +#ifdef jmpbuf +# undef jmpbuf +#endif + +/* Define png_jmpbuf() in case we are using a pre-1.0.6 version of libpng */ +#ifndef png_jmpbuf +# define png_jmpbuf(png_ptr) png_ptr->jmpbuf +#endif + +void +#ifdef PNG_1_0_X +PNGAPI +#endif +read_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass); +void +#ifdef PNG_1_0_X +PNGAPI +#endif +read_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass) +{ + if(png_ptr == NULL || row_number > PNG_UINT_31_MAX) return; + if(status_pass != pass) + { + fprintf(stdout,"\n Pass %d: ",pass); + status_pass = pass; + status_dots = 31; + } + status_dots--; + if(status_dots == 0) + { + fprintf(stdout, "\n "); + status_dots=30; + } + fprintf(stdout, "r"); +} + +void +#ifdef PNG_1_0_X +PNGAPI +#endif +write_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass); +void +#ifdef PNG_1_0_X +PNGAPI +#endif +write_row_callback(png_structp png_ptr, png_uint_32 row_number, int pass) +{ + if(png_ptr == NULL || row_number > PNG_UINT_31_MAX || pass > 7) return; + fprintf(stdout, "w"); +} + + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) +/* Example of using user transform callback (we don't transform anything, + but merely examine the row filters. We set this to 256 rather than + 5 in case illegal filter values are present.) */ +static png_uint_32 filters_used[256]; +void +#ifdef PNG_1_0_X +PNGAPI +#endif +count_filters(png_structp png_ptr, png_row_infop row_info, png_bytep data); +void +#ifdef PNG_1_0_X +PNGAPI +#endif +count_filters(png_structp png_ptr, png_row_infop row_info, png_bytep data) +{ + if(png_ptr != NULL && row_info != NULL) + ++filters_used[*(data-1)]; +} +#endif + +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) +/* example of using user transform callback (we don't transform anything, + but merely count the zero samples) */ + +static png_uint_32 zero_samples; + +void +#ifdef PNG_1_0_X +PNGAPI +#endif +count_zero_samples(png_structp png_ptr, png_row_infop row_info, png_bytep data); +void +#ifdef PNG_1_0_X +PNGAPI +#endif +count_zero_samples(png_structp png_ptr, png_row_infop row_info, png_bytep data) +{ + png_bytep dp = data; + if(png_ptr == NULL)return; + + /* contents of row_info: + * png_uint_32 width width of row + * png_uint_32 rowbytes number of bytes in row + * png_byte color_type color type of pixels + * png_byte bit_depth bit depth of samples + * png_byte channels number of channels (1-4) + * png_byte pixel_depth bits per pixel (depth*channels) + */ + + + /* counts the number of zero samples (or zero pixels if color_type is 3 */ + + if(row_info->color_type == 0 || row_info->color_type == 3) + { + int pos=0; + png_uint_32 n, nstop; + for (n=0, nstop=row_info->width; nbit_depth == 1) + { + if(((*dp << pos++ ) & 0x80) == 0) zero_samples++; + if(pos == 8) + { + pos = 0; + dp++; + } + } + if(row_info->bit_depth == 2) + { + if(((*dp << (pos+=2)) & 0xc0) == 0) zero_samples++; + if(pos == 8) + { + pos = 0; + dp++; + } + } + if(row_info->bit_depth == 4) + { + if(((*dp << (pos+=4)) & 0xf0) == 0) zero_samples++; + if(pos == 8) + { + pos = 0; + dp++; + } + } + if(row_info->bit_depth == 8) + if(*dp++ == 0) zero_samples++; + if(row_info->bit_depth == 16) + { + if((*dp | *(dp+1)) == 0) zero_samples++; + dp+=2; + } + } + } + else /* other color types */ + { + png_uint_32 n, nstop; + int channel; + int color_channels = row_info->channels; + if(row_info->color_type > 3)color_channels--; + + for (n=0, nstop=row_info->width; nbit_depth == 8) + if(*dp++ == 0) zero_samples++; + if(row_info->bit_depth == 16) + { + if((*dp | *(dp+1)) == 0) zero_samples++; + dp+=2; + } + } + if(row_info->color_type > 3) + { + dp++; + if(row_info->bit_depth == 16)dp++; + } + } + } +} +#endif /* PNG_WRITE_USER_TRANSFORM_SUPPORTED */ + +static int wrote_question = 0; + +#if defined(PNG_NO_STDIO) +/* START of code to validate stdio-free compilation */ +/* These copies of the default read/write functions come from pngrio.c and */ +/* pngwio.c. They allow "don't include stdio" testing of the library. */ +/* This is the function that does the actual reading of data. If you are + not reading from a standard C stream, you should create a replacement + read_data function and use it at run time with png_set_read_fn(), rather + than changing the library. */ + +#ifndef USE_FAR_KEYWORD +static void +pngtest_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_size_t check; + + /* fread() returns 0 on error, so it is OK to store this in a png_size_t + * instead of an int, which is what fread() actually returns. + */ + READFILE((png_FILE_p)png_ptr->io_ptr, data, length, check); + + if (check != length) + { + png_error(png_ptr, "Read Error!"); + } +} +#else +/* this is the model-independent version. Since the standard I/O library + can't handle far buffers in the medium and small models, we have to copy + the data. +*/ + +#define NEAR_BUF_SIZE 1024 +#define MIN(a,b) (a <= b ? a : b) + +static void +pngtest_read_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + int check; + png_byte *n_data; + png_FILE_p io_ptr; + + /* Check if data really is near. If so, use usual code. */ + n_data = (png_byte *)CVT_PTR_NOCHECK(data); + io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); + if ((png_bytep)n_data == data) + { + READFILE(io_ptr, n_data, length, check); + } + else + { + png_byte buf[NEAR_BUF_SIZE]; + png_size_t read, remaining, err; + check = 0; + remaining = length; + do + { + read = MIN(NEAR_BUF_SIZE, remaining); + READFILE(io_ptr, buf, 1, err); + png_memcpy(data, buf, read); /* copy far buffer to near buffer */ + if(err != read) + break; + else + check += err; + data += read; + remaining -= read; + } + while (remaining != 0); + } + if (check != length) + { + png_error(png_ptr, "read Error"); + } +} +#endif /* USE_FAR_KEYWORD */ + +#if defined(PNG_WRITE_FLUSH_SUPPORTED) +static void +pngtest_flush(png_structp png_ptr) +{ +#if !defined(_WIN32_WCE) + png_FILE_p io_ptr; + io_ptr = (png_FILE_p)CVT_PTR((png_ptr->io_ptr)); + if (io_ptr != NULL) + fflush(io_ptr); +#endif +} +#endif + +/* This is the function that does the actual writing of data. If you are + not writing to a standard C stream, you should create a replacement + write_data function and use it at run time with png_set_write_fn(), rather + than changing the library. */ +#ifndef USE_FAR_KEYWORD +static void +pngtest_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + + WRITEFILE((png_FILE_p)png_ptr->io_ptr, data, length, check); + if (check != length) + { + png_error(png_ptr, "Write Error"); + } +} +#else +/* this is the model-independent version. Since the standard I/O library + can't handle far buffers in the medium and small models, we have to copy + the data. +*/ + +#define NEAR_BUF_SIZE 1024 +#define MIN(a,b) (a <= b ? a : b) + +static void +pngtest_write_data(png_structp png_ptr, png_bytep data, png_size_t length) +{ + png_uint_32 check; + png_byte *near_data; /* Needs to be "png_byte *" instead of "png_bytep" */ + png_FILE_p io_ptr; + + /* Check if data really is near. If so, use usual code. */ + near_data = (png_byte *)CVT_PTR_NOCHECK(data); + io_ptr = (png_FILE_p)CVT_PTR(png_ptr->io_ptr); + if ((png_bytep)near_data == data) + { + WRITEFILE(io_ptr, near_data, length, check); + } + else + { + png_byte buf[NEAR_BUF_SIZE]; + png_size_t written, remaining, err; + check = 0; + remaining = length; + do + { + written = MIN(NEAR_BUF_SIZE, remaining); + png_memcpy(buf, data, written); /* copy far buffer to near buffer */ + WRITEFILE(io_ptr, buf, written, err); + if (err != written) + break; + else + check += err; + data += written; + remaining -= written; + } + while (remaining != 0); + } + if (check != length) + { + png_error(png_ptr, "Write Error"); + } +} +#endif /* USE_FAR_KEYWORD */ +#endif /* PNG_NO_STDIO */ +/* END of code to validate stdio-free compilation */ + +/* This function is called when there is a warning, but the library thinks + * it can continue anyway. Replacement functions don't have to do anything + * here if you don't want to. In the default configuration, png_ptr is + * not used, but it is passed in case it may be useful. + */ +static void +pngtest_warning(png_structp png_ptr, png_const_charp message) +{ + PNG_CONST char *name = "UNKNOWN (ERROR!)"; + if (png_ptr != NULL && png_ptr->error_ptr != NULL) + name = png_ptr->error_ptr; + fprintf(STDERR, "%s: libpng warning: %s\n", name, message); +} + +/* This is the default error handling function. Note that replacements for + * this function MUST NOT RETURN, or the program will likely crash. This + * function is used by default, or if the program supplies NULL for the + * error function pointer in png_set_error_fn(). + */ +static void +pngtest_error(png_structp png_ptr, png_const_charp message) +{ + pngtest_warning(png_ptr, message); + /* We can return because png_error calls the default handler, which is + * actually OK in this case. */ +} + +/* START of code to validate memory allocation and deallocation */ +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + +/* Allocate memory. For reasonable files, size should never exceed + 64K. However, zlib may allocate more then 64K if you don't tell + it not to. See zconf.h and png.h for more information. zlib does + need to allocate exactly 64K, so whatever you call here must + have the ability to do that. + + This piece of code can be compiled to validate max 64K allocations + by setting MAXSEG_64K in zlib zconf.h *or* PNG_MAX_MALLOC_64K. */ +typedef struct memory_information +{ + png_uint_32 size; + png_voidp pointer; + struct memory_information FAR *next; +} memory_information; +typedef memory_information FAR *memory_infop; + +static memory_infop pinformation = NULL; +static int current_allocation = 0; +static int maximum_allocation = 0; +static int total_allocation = 0; +static int num_allocations = 0; + +png_voidp png_debug_malloc PNGARG((png_structp png_ptr, png_uint_32 size)); +void png_debug_free PNGARG((png_structp png_ptr, png_voidp ptr)); + +png_voidp +png_debug_malloc(png_structp png_ptr, png_uint_32 size) +{ + + /* png_malloc has already tested for NULL; png_create_struct calls + png_debug_malloc directly, with png_ptr == NULL which is OK */ + + if (size == 0) + return (NULL); + + /* This calls the library allocator twice, once to get the requested + buffer and once to get a new free list entry. */ + { + /* Disable malloc_fn and free_fn */ + memory_infop pinfo; + png_set_mem_fn(png_ptr, NULL, NULL, NULL); + pinfo = (memory_infop)png_malloc(png_ptr, + (png_uint_32)png_sizeof (*pinfo)); + pinfo->size = size; + current_allocation += size; + total_allocation += size; + num_allocations ++; + if (current_allocation > maximum_allocation) + maximum_allocation = current_allocation; + pinfo->pointer = (png_voidp)png_malloc(png_ptr, size); + /* Restore malloc_fn and free_fn */ + png_set_mem_fn(png_ptr, png_voidp_NULL, (png_malloc_ptr)png_debug_malloc, + (png_free_ptr)png_debug_free); + if (size != 0 && pinfo->pointer == NULL) + { + current_allocation -= size; + total_allocation -= size; + png_error(png_ptr, + "out of memory in pngtest->png_debug_malloc."); + } + pinfo->next = pinformation; + pinformation = pinfo; + /* Make sure the caller isn't assuming zeroed memory. */ + png_memset(pinfo->pointer, 0xdd, pinfo->size); + if(verbose) + printf("png_malloc %lu bytes at %x\n",(unsigned long)size, + pinfo->pointer); + return (png_voidp)(pinfo->pointer); + } +} + +/* Free a pointer. It is removed from the list at the same time. */ +void +png_debug_free(png_structp png_ptr, png_voidp ptr) +{ + if (png_ptr == NULL) + fprintf(STDERR, "NULL pointer to png_debug_free.\n"); + if (ptr == 0) + { +#if 0 /* This happens all the time. */ + fprintf(STDERR, "WARNING: freeing NULL pointer\n"); +#endif + return; + } + + /* Unlink the element from the list. */ + { + memory_infop FAR *ppinfo = &pinformation; + for (;;) + { + memory_infop pinfo = *ppinfo; + if (pinfo->pointer == ptr) + { + *ppinfo = pinfo->next; + current_allocation -= pinfo->size; + if (current_allocation < 0) + fprintf(STDERR, "Duplicate free of memory\n"); + /* We must free the list element too, but first kill + the memory that is to be freed. */ + png_memset(ptr, 0x55, pinfo->size); + png_free_default(png_ptr, pinfo); + pinfo=NULL; + break; + } + if (pinfo->next == NULL) + { + fprintf(STDERR, "Pointer %x not found\n", (unsigned int)ptr); + break; + } + ppinfo = &pinfo->next; + } + } + + /* Finally free the data. */ + if(verbose) + printf("Freeing %x\n",ptr); + png_free_default(png_ptr, ptr); + ptr=NULL; +} +#endif /* PNG_USER_MEM_SUPPORTED && PNG_DEBUG */ +/* END of code to test memory allocation/deallocation */ + +/* Test one file */ +int +test_one_file(PNG_CONST char *inname, PNG_CONST char *outname) +{ + static png_FILE_p fpin; + static png_FILE_p fpout; /* "static" prevents setjmp corruption */ + png_structp read_ptr; + png_infop read_info_ptr, end_info_ptr; +#ifdef PNG_WRITE_SUPPORTED + png_structp write_ptr; + png_infop write_info_ptr; + png_infop write_end_info_ptr; +#else + png_structp write_ptr = NULL; + png_infop write_info_ptr = NULL; + png_infop write_end_info_ptr = NULL; +#endif + png_bytep row_buf; + png_uint_32 y; + png_uint_32 width, height; + int num_pass, pass; + int bit_depth, color_type; +#ifdef PNG_SETJMP_SUPPORTED +#ifdef USE_FAR_KEYWORD + jmp_buf jmpbuf; +#endif +#endif + +#if defined(_WIN32_WCE) + TCHAR path[MAX_PATH]; +#endif + char inbuf[256], outbuf[256]; + + row_buf = NULL; + +#if defined(_WIN32_WCE) + MultiByteToWideChar(CP_ACP, 0, inname, -1, path, MAX_PATH); + if ((fpin = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) +#else + if ((fpin = fopen(inname, "rb")) == NULL) +#endif + { + fprintf(STDERR, "Could not find input file %s\n", inname); + return (1); + } + +#if defined(_WIN32_WCE) + MultiByteToWideChar(CP_ACP, 0, outname, -1, path, MAX_PATH); + if ((fpout = CreateFile(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL)) == INVALID_HANDLE_VALUE) +#else + if ((fpout = fopen(outname, "wb")) == NULL) +#endif + { + fprintf(STDERR, "Could not open output file %s\n", outname); + FCLOSE(fpin); + return (1); + } + + png_debug(0, "Allocating read and write structures\n"); +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + read_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, png_voidp_NULL, + png_error_ptr_NULL, png_error_ptr_NULL, png_voidp_NULL, + (png_malloc_ptr)png_debug_malloc, (png_free_ptr)png_debug_free); +#else + read_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, png_voidp_NULL, + png_error_ptr_NULL, png_error_ptr_NULL); +#endif + png_set_error_fn(read_ptr, (png_voidp)inname, pngtest_error, + pngtest_warning); +#ifdef PNG_WRITE_SUPPORTED +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + write_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, png_voidp_NULL, + png_error_ptr_NULL, png_error_ptr_NULL, png_voidp_NULL, + (png_malloc_ptr)png_debug_malloc, (png_free_ptr)png_debug_free); +#else + write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, png_voidp_NULL, + png_error_ptr_NULL, png_error_ptr_NULL); +#endif + png_set_error_fn(write_ptr, (png_voidp)inname, pngtest_error, + pngtest_warning); +#endif + png_debug(0, "Allocating read_info, write_info and end_info structures\n"); + read_info_ptr = png_create_info_struct(read_ptr); + end_info_ptr = png_create_info_struct(read_ptr); +#ifdef PNG_WRITE_SUPPORTED + write_info_ptr = png_create_info_struct(write_ptr); + write_end_info_ptr = png_create_info_struct(write_ptr); +#endif + +#ifdef PNG_SETJMP_SUPPORTED + png_debug(0, "Setting jmpbuf for read struct\n"); +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + if (setjmp(png_jmpbuf(read_ptr))) +#endif + { + fprintf(STDERR, "%s -> %s: libpng read error\n", inname, outname); + if (row_buf) + png_free(read_ptr, row_buf); + png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); +#ifdef PNG_WRITE_SUPPORTED + png_destroy_info_struct(write_ptr, &write_end_info_ptr); + png_destroy_write_struct(&write_ptr, &write_info_ptr); +#endif + FCLOSE(fpin); + FCLOSE(fpout); + return (1); + } +#ifdef USE_FAR_KEYWORD + png_memcpy(png_jmpbuf(read_ptr),jmpbuf,png_sizeof(jmp_buf)); +#endif + +#ifdef PNG_WRITE_SUPPORTED + png_debug(0, "Setting jmpbuf for write struct\n"); +#ifdef USE_FAR_KEYWORD + if (setjmp(jmpbuf)) +#else + if (setjmp(png_jmpbuf(write_ptr))) +#endif + { + fprintf(STDERR, "%s -> %s: libpng write error\n", inname, outname); + png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); + png_destroy_info_struct(write_ptr, &write_end_info_ptr); +#ifdef PNG_WRITE_SUPPORTED + png_destroy_write_struct(&write_ptr, &write_info_ptr); +#endif + FCLOSE(fpin); + FCLOSE(fpout); + return (1); + } +#ifdef USE_FAR_KEYWORD + png_memcpy(png_jmpbuf(write_ptr),jmpbuf,png_sizeof(jmp_buf)); +#endif +#endif +#endif + + png_debug(0, "Initializing input and output streams\n"); +#if !defined(PNG_NO_STDIO) + png_init_io(read_ptr, fpin); +# ifdef PNG_WRITE_SUPPORTED + png_init_io(write_ptr, fpout); +# endif +#else + png_set_read_fn(read_ptr, (png_voidp)fpin, pngtest_read_data); +# ifdef PNG_WRITE_SUPPORTED + png_set_write_fn(write_ptr, (png_voidp)fpout, pngtest_write_data, +# if defined(PNG_WRITE_FLUSH_SUPPORTED) + pngtest_flush); +# else + NULL); +# endif +# endif +#endif + if(status_dots_requested == 1) + { +#ifdef PNG_WRITE_SUPPORTED + png_set_write_status_fn(write_ptr, write_row_callback); +#endif + png_set_read_status_fn(read_ptr, read_row_callback); + } + else + { +#ifdef PNG_WRITE_SUPPORTED + png_set_write_status_fn(write_ptr, png_write_status_ptr_NULL); +#endif + png_set_read_status_fn(read_ptr, png_read_status_ptr_NULL); + } + +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + { + int i; + for(i=0; i<256; i++) + filters_used[i]=0; + png_set_read_user_transform_fn(read_ptr, count_filters); + } +#endif +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + zero_samples=0; + png_set_write_user_transform_fn(write_ptr, count_zero_samples); +#endif + +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) +# ifndef PNG_HANDLE_CHUNK_ALWAYS +# define PNG_HANDLE_CHUNK_ALWAYS 3 +# endif + png_set_keep_unknown_chunks(read_ptr, PNG_HANDLE_CHUNK_ALWAYS, + png_bytep_NULL, 0); +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) +# ifndef PNG_HANDLE_CHUNK_IF_SAFE +# define PNG_HANDLE_CHUNK_IF_SAFE 2 +# endif + png_set_keep_unknown_chunks(write_ptr, PNG_HANDLE_CHUNK_IF_SAFE, + png_bytep_NULL, 0); +#endif + + png_debug(0, "Reading info struct\n"); + png_read_info(read_ptr, read_info_ptr); + + png_debug(0, "Transferring info struct\n"); + { + int interlace_type, compression_type, filter_type; + + if (png_get_IHDR(read_ptr, read_info_ptr, &width, &height, &bit_depth, + &color_type, &interlace_type, &compression_type, &filter_type)) + { + png_set_IHDR(write_ptr, write_info_ptr, width, height, bit_depth, +#if defined(PNG_WRITE_INTERLACING_SUPPORTED) + color_type, interlace_type, compression_type, filter_type); +#else + color_type, PNG_INTERLACE_NONE, compression_type, filter_type); +#endif + } + } +#if defined(PNG_FIXED_POINT_SUPPORTED) +#if defined(PNG_cHRM_SUPPORTED) + { + png_fixed_point white_x, white_y, red_x, red_y, green_x, green_y, blue_x, + blue_y; + if (png_get_cHRM_fixed(read_ptr, read_info_ptr, &white_x, &white_y, &red_x, + &red_y, &green_x, &green_y, &blue_x, &blue_y)) + { + png_set_cHRM_fixed(write_ptr, write_info_ptr, white_x, white_y, red_x, + red_y, green_x, green_y, blue_x, blue_y); + } + } +#endif +#if defined(PNG_gAMA_SUPPORTED) + { + png_fixed_point gamma; + + if (png_get_gAMA_fixed(read_ptr, read_info_ptr, &gamma)) + { + png_set_gAMA_fixed(write_ptr, write_info_ptr, gamma); + } + } +#endif +#else /* Use floating point versions */ +#if defined(PNG_FLOATING_POINT_SUPPORTED) +#if defined(PNG_cHRM_SUPPORTED) + { + double white_x, white_y, red_x, red_y, green_x, green_y, blue_x, + blue_y; + if (png_get_cHRM(read_ptr, read_info_ptr, &white_x, &white_y, &red_x, + &red_y, &green_x, &green_y, &blue_x, &blue_y)) + { + png_set_cHRM(write_ptr, write_info_ptr, white_x, white_y, red_x, + red_y, green_x, green_y, blue_x, blue_y); + } + } +#endif +#if defined(PNG_gAMA_SUPPORTED) + { + double gamma; + + if (png_get_gAMA(read_ptr, read_info_ptr, &gamma)) + { + png_set_gAMA(write_ptr, write_info_ptr, gamma); + } + } +#endif +#endif /* floating point */ +#endif /* fixed point */ +#if defined(PNG_iCCP_SUPPORTED) + { + png_charp name; + png_charp profile; + png_uint_32 proflen; + int compression_type; + + if (png_get_iCCP(read_ptr, read_info_ptr, &name, &compression_type, + &profile, &proflen)) + { + png_set_iCCP(write_ptr, write_info_ptr, name, compression_type, + profile, proflen); + } + } +#endif +#if defined(PNG_sRGB_SUPPORTED) + { + int intent; + + if (png_get_sRGB(read_ptr, read_info_ptr, &intent)) + { + png_set_sRGB(write_ptr, write_info_ptr, intent); + } + } +#endif + { + png_colorp palette; + int num_palette; + + if (png_get_PLTE(read_ptr, read_info_ptr, &palette, &num_palette)) + { + png_set_PLTE(write_ptr, write_info_ptr, palette, num_palette); + } + } +#if defined(PNG_bKGD_SUPPORTED) + { + png_color_16p background; + + if (png_get_bKGD(read_ptr, read_info_ptr, &background)) + { + png_set_bKGD(write_ptr, write_info_ptr, background); + } + } +#endif +#if defined(PNG_hIST_SUPPORTED) + { + png_uint_16p hist; + + if (png_get_hIST(read_ptr, read_info_ptr, &hist)) + { + png_set_hIST(write_ptr, write_info_ptr, hist); + } + } +#endif +#if defined(PNG_oFFs_SUPPORTED) + { + png_int_32 offset_x, offset_y; + int unit_type; + + if (png_get_oFFs(read_ptr, read_info_ptr,&offset_x,&offset_y,&unit_type)) + { + png_set_oFFs(write_ptr, write_info_ptr, offset_x, offset_y, unit_type); + } + } +#endif +#if defined(PNG_pCAL_SUPPORTED) + { + png_charp purpose, units; + png_charpp params; + png_int_32 X0, X1; + int type, nparams; + + if (png_get_pCAL(read_ptr, read_info_ptr, &purpose, &X0, &X1, &type, + &nparams, &units, ¶ms)) + { + png_set_pCAL(write_ptr, write_info_ptr, purpose, X0, X1, type, + nparams, units, params); + } + } +#endif +#if defined(PNG_pHYs_SUPPORTED) + { + png_uint_32 res_x, res_y; + int unit_type; + + if (png_get_pHYs(read_ptr, read_info_ptr, &res_x, &res_y, &unit_type)) + { + png_set_pHYs(write_ptr, write_info_ptr, res_x, res_y, unit_type); + } + } +#endif +#if defined(PNG_sBIT_SUPPORTED) + { + png_color_8p sig_bit; + + if (png_get_sBIT(read_ptr, read_info_ptr, &sig_bit)) + { + png_set_sBIT(write_ptr, write_info_ptr, sig_bit); + } + } +#endif +#if defined(PNG_sCAL_SUPPORTED) +#ifdef PNG_FLOATING_POINT_SUPPORTED + { + int unit; + double scal_width, scal_height; + + if (png_get_sCAL(read_ptr, read_info_ptr, &unit, &scal_width, + &scal_height)) + { + png_set_sCAL(write_ptr, write_info_ptr, unit, scal_width, scal_height); + } + } +#else +#ifdef PNG_FIXED_POINT_SUPPORTED + { + int unit; + png_charp scal_width, scal_height; + + if (png_get_sCAL_s(read_ptr, read_info_ptr, &unit, &scal_width, + &scal_height)) + { + png_set_sCAL_s(write_ptr, write_info_ptr, unit, scal_width, scal_height); + } + } +#endif +#endif +#endif +#if defined(PNG_TEXT_SUPPORTED) + { + png_textp text_ptr; + int num_text; + + if (png_get_text(read_ptr, read_info_ptr, &text_ptr, &num_text) > 0) + { + png_debug1(0, "Handling %d iTXt/tEXt/zTXt chunks\n", num_text); + png_set_text(write_ptr, write_info_ptr, text_ptr, num_text); + } + } +#endif +#if defined(PNG_tIME_SUPPORTED) + { + png_timep mod_time; + + if (png_get_tIME(read_ptr, read_info_ptr, &mod_time)) + { + png_set_tIME(write_ptr, write_info_ptr, mod_time); +#if defined(PNG_TIME_RFC1123_SUPPORTED) + /* we have to use png_memcpy instead of "=" because the string + pointed to by png_convert_to_rfc1123() gets free'ed before + we use it */ + png_memcpy(tIME_string, + png_convert_to_rfc1123(read_ptr, mod_time), + png_sizeof(tIME_string)); + tIME_string[png_sizeof(tIME_string)-1] = '\0'; + tIME_chunk_present++; +#endif /* PNG_TIME_RFC1123_SUPPORTED */ + } + } +#endif +#if defined(PNG_tRNS_SUPPORTED) + { + png_bytep trans; + int num_trans; + png_color_16p trans_values; + + if (png_get_tRNS(read_ptr, read_info_ptr, &trans, &num_trans, + &trans_values)) + { + png_set_tRNS(write_ptr, write_info_ptr, trans, num_trans, + trans_values); + } + } +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) + { + png_unknown_chunkp unknowns; + int num_unknowns = (int)png_get_unknown_chunks(read_ptr, read_info_ptr, + &unknowns); + if (num_unknowns) + { + png_size_t i; + png_set_unknown_chunks(write_ptr, write_info_ptr, unknowns, + num_unknowns); + /* copy the locations from the read_info_ptr. The automatically + generated locations in write_info_ptr are wrong because we + haven't written anything yet */ + for (i = 0; i < (png_size_t)num_unknowns; i++) + png_set_unknown_chunk_location(write_ptr, write_info_ptr, i, + unknowns[i].location); + } + } +#endif + +#ifdef PNG_WRITE_SUPPORTED + png_debug(0, "\nWriting info struct\n"); + +/* If we wanted, we could write info in two steps: + png_write_info_before_PLTE(write_ptr, write_info_ptr); + */ + png_write_info(write_ptr, write_info_ptr); +#endif + +#ifdef SINGLE_ROWBUF_ALLOC + png_debug(0, "\nAllocating row buffer..."); + row_buf = (png_bytep)png_malloc(read_ptr, + png_get_rowbytes(read_ptr, read_info_ptr)); + png_debug1(0, "0x%08lx\n\n", (unsigned long)row_buf); +#endif /* SINGLE_ROWBUF_ALLOC */ + png_debug(0, "Writing row data\n"); + +#if defined(PNG_READ_INTERLACING_SUPPORTED) || \ + defined(PNG_WRITE_INTERLACING_SUPPORTED) + num_pass = png_set_interlace_handling(read_ptr); +# ifdef PNG_WRITE_SUPPORTED + png_set_interlace_handling(write_ptr); +# endif +#else + num_pass=1; +#endif + +#ifdef PNGTEST_TIMING + t_stop = (float)clock(); + t_misc += (t_stop - t_start); + t_start = t_stop; +#endif + for (pass = 0; pass < num_pass; pass++) + { + png_debug1(0, "Writing row data for pass %d\n",pass); + for (y = 0; y < height; y++) + { +#ifndef SINGLE_ROWBUF_ALLOC + png_debug2(0, "\nAllocating row buffer (pass %d, y = %ld)...", pass,y); + row_buf = (png_bytep)png_malloc(read_ptr, + png_get_rowbytes(read_ptr, read_info_ptr)); + png_debug2(0, "0x%08lx (%ld bytes)\n", (unsigned long)row_buf, + png_get_rowbytes(read_ptr, read_info_ptr)); +#endif /* !SINGLE_ROWBUF_ALLOC */ + png_read_rows(read_ptr, (png_bytepp)&row_buf, png_bytepp_NULL, 1); + +#ifdef PNG_WRITE_SUPPORTED +#ifdef PNGTEST_TIMING + t_stop = (float)clock(); + t_decode += (t_stop - t_start); + t_start = t_stop; +#endif + png_write_rows(write_ptr, (png_bytepp)&row_buf, 1); +#ifdef PNGTEST_TIMING + t_stop = (float)clock(); + t_encode += (t_stop - t_start); + t_start = t_stop; +#endif +#endif /* PNG_WRITE_SUPPORTED */ + +#ifndef SINGLE_ROWBUF_ALLOC + png_debug2(0, "Freeing row buffer (pass %d, y = %ld)\n\n", pass, y); + png_free(read_ptr, row_buf); +#endif /* !SINGLE_ROWBUF_ALLOC */ + } + } + +#if defined(PNG_READ_UNKNOWN_CHUNKS_SUPPORTED) + png_free_data(read_ptr, read_info_ptr, PNG_FREE_UNKN, -1); +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) + png_free_data(write_ptr, write_info_ptr, PNG_FREE_UNKN, -1); +#endif + + png_debug(0, "Reading and writing end_info data\n"); + + png_read_end(read_ptr, end_info_ptr); +#if defined(PNG_TEXT_SUPPORTED) + { + png_textp text_ptr; + int num_text; + + if (png_get_text(read_ptr, end_info_ptr, &text_ptr, &num_text) > 0) + { + png_debug1(0, "Handling %d iTXt/tEXt/zTXt chunks\n", num_text); + png_set_text(write_ptr, write_end_info_ptr, text_ptr, num_text); + } + } +#endif +#if defined(PNG_tIME_SUPPORTED) + { + png_timep mod_time; + + if (png_get_tIME(read_ptr, end_info_ptr, &mod_time)) + { + png_set_tIME(write_ptr, write_end_info_ptr, mod_time); +#if defined(PNG_TIME_RFC1123_SUPPORTED) + /* we have to use png_memcpy instead of "=" because the string + pointed to by png_convert_to_rfc1123() gets free'ed before + we use it */ + png_memcpy(tIME_string, + png_convert_to_rfc1123(read_ptr, mod_time), + png_sizeof(tIME_string)); + tIME_string[png_sizeof(tIME_string)-1] = '\0'; + tIME_chunk_present++; +#endif /* PNG_TIME_RFC1123_SUPPORTED */ + } + } +#endif +#if defined(PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED) + { + png_unknown_chunkp unknowns; + int num_unknowns; + num_unknowns = (int)png_get_unknown_chunks(read_ptr, end_info_ptr, + &unknowns); + if (num_unknowns) + { + png_size_t i; + png_set_unknown_chunks(write_ptr, write_end_info_ptr, unknowns, + num_unknowns); + /* copy the locations from the read_info_ptr. The automatically + generated locations in write_end_info_ptr are wrong because we + haven't written the end_info yet */ + for (i = 0; i < (png_size_t)num_unknowns; i++) + png_set_unknown_chunk_location(write_ptr, write_end_info_ptr, i, + unknowns[i].location); + } + } +#endif +#ifdef PNG_WRITE_SUPPORTED + png_write_end(write_ptr, write_end_info_ptr); +#endif + +#ifdef PNG_EASY_ACCESS_SUPPORTED + if(verbose) + { + png_uint_32 iwidth, iheight; + iwidth = png_get_image_width(write_ptr, write_info_ptr); + iheight = png_get_image_height(write_ptr, write_info_ptr); + fprintf(STDERR, "Image width = %lu, height = %lu\n", + (unsigned long)iwidth, (unsigned long)iheight); + } +#endif + + png_debug(0, "Destroying data structs\n"); +#ifdef SINGLE_ROWBUF_ALLOC + png_debug(1, "destroying row_buf for read_ptr\n"); + png_free(read_ptr, row_buf); + row_buf=NULL; +#endif /* SINGLE_ROWBUF_ALLOC */ + png_debug(1, "destroying read_ptr, read_info_ptr, end_info_ptr\n"); + png_destroy_read_struct(&read_ptr, &read_info_ptr, &end_info_ptr); +#ifdef PNG_WRITE_SUPPORTED + png_debug(1, "destroying write_end_info_ptr\n"); + png_destroy_info_struct(write_ptr, &write_end_info_ptr); + png_debug(1, "destroying write_ptr, write_info_ptr\n"); + png_destroy_write_struct(&write_ptr, &write_info_ptr); +#endif + png_debug(0, "Destruction complete.\n"); + + FCLOSE(fpin); + FCLOSE(fpout); + + png_debug(0, "Opening files for comparison\n"); +#if defined(_WIN32_WCE) + MultiByteToWideChar(CP_ACP, 0, inname, -1, path, MAX_PATH); + if ((fpin = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) +#else + if ((fpin = fopen(inname, "rb")) == NULL) +#endif + { + fprintf(STDERR, "Could not find file %s\n", inname); + return (1); + } + +#if defined(_WIN32_WCE) + MultiByteToWideChar(CP_ACP, 0, outname, -1, path, MAX_PATH); + if ((fpout = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) +#else + if ((fpout = fopen(outname, "rb")) == NULL) +#endif + { + fprintf(STDERR, "Could not find file %s\n", outname); + FCLOSE(fpin); + return (1); + } + + for(;;) + { + png_size_t num_in, num_out; + + READFILE(fpin, inbuf, 1, num_in); + READFILE(fpout, outbuf, 1, num_out); + + if (num_in != num_out) + { + fprintf(STDERR, "\nFiles %s and %s are of a different size\n", + inname, outname); + if(wrote_question == 0) + { + fprintf(STDERR, + " Was %s written with the same maximum IDAT chunk size (%d bytes),", + inname,PNG_ZBUF_SIZE); + fprintf(STDERR, + "\n filtering heuristic (libpng default), compression"); + fprintf(STDERR, + " level (zlib default),\n and zlib version (%s)?\n\n", + ZLIB_VERSION); + wrote_question=1; + } + FCLOSE(fpin); + FCLOSE(fpout); + return (0); + } + + if (!num_in) + break; + + if (png_memcmp(inbuf, outbuf, num_in)) + { + fprintf(STDERR, "\nFiles %s and %s are different\n", inname, outname); + if(wrote_question == 0) + { + fprintf(STDERR, + " Was %s written with the same maximum IDAT chunk size (%d bytes),", + inname,PNG_ZBUF_SIZE); + fprintf(STDERR, + "\n filtering heuristic (libpng default), compression"); + fprintf(STDERR, + " level (zlib default),\n and zlib version (%s)?\n\n", + ZLIB_VERSION); + wrote_question=1; + } + FCLOSE(fpin); + FCLOSE(fpout); + return (0); + } + } + + FCLOSE(fpin); + FCLOSE(fpout); + + return (0); +} + +/* input and output filenames */ +#ifdef RISCOS +static PNG_CONST char *inname = "pngtest/png"; +static PNG_CONST char *outname = "pngout/png"; +#else +static PNG_CONST char *inname = "pngtest.png"; +static PNG_CONST char *outname = "pngout.png"; +#endif + +int +main(int argc, char *argv[]) +{ + int multiple = 0; + int ierror = 0; + + fprintf(STDERR, "Testing libpng version %s\n", PNG_LIBPNG_VER_STRING); + fprintf(STDERR, " with zlib version %s\n", ZLIB_VERSION); + fprintf(STDERR,"%s",png_get_copyright(NULL)); + /* Show the version of libpng used in building the library */ + fprintf(STDERR," library (%lu):%s", + (unsigned long)png_access_version_number(), + png_get_header_version(NULL)); + /* Show the version of libpng used in building the application */ + fprintf(STDERR," pngtest (%lu):%s", (unsigned long)PNG_LIBPNG_VER, + PNG_HEADER_VERSION_STRING); + fprintf(STDERR," png_sizeof(png_struct)=%ld, png_sizeof(png_info)=%ld\n", + (long)png_sizeof(png_struct), (long)png_sizeof(png_info)); + + /* Do some consistency checking on the memory allocation settings, I'm + not sure this matters, but it is nice to know, the first of these + tests should be impossible because of the way the macros are set + in pngconf.h */ +#if defined(MAXSEG_64K) && !defined(PNG_MAX_MALLOC_64K) + fprintf(STDERR, " NOTE: Zlib compiled for max 64k, libpng not\n"); +#endif + /* I think the following can happen. */ +#if !defined(MAXSEG_64K) && defined(PNG_MAX_MALLOC_64K) + fprintf(STDERR, " NOTE: libpng compiled for max 64k, zlib not\n"); +#endif + + if (strcmp(png_libpng_ver, PNG_LIBPNG_VER_STRING)) + { + fprintf(STDERR, + "Warning: versions are different between png.h and png.c\n"); + fprintf(STDERR, " png.h version: %s\n", PNG_LIBPNG_VER_STRING); + fprintf(STDERR, " png.c version: %s\n\n", png_libpng_ver); + ++ierror; + } + + if (argc > 1) + { + if (strcmp(argv[1], "-m") == 0) + { + multiple = 1; + status_dots_requested = 0; + } + else if (strcmp(argv[1], "-mv") == 0 || + strcmp(argv[1], "-vm") == 0 ) + { + multiple = 1; + verbose = 1; + status_dots_requested = 1; + } + else if (strcmp(argv[1], "-v") == 0) + { + verbose = 1; + status_dots_requested = 1; + inname = argv[2]; + } + else + { + inname = argv[1]; + status_dots_requested = 0; + } + } + + if (!multiple && argc == 3+verbose) + outname = argv[2+verbose]; + + if ((!multiple && argc > 3+verbose) || (multiple && argc < 2)) + { + fprintf(STDERR, + "usage: %s [infile.png] [outfile.png]\n\t%s -m {infile.png}\n", + argv[0], argv[0]); + fprintf(STDERR, + " reads/writes one PNG file (without -m) or multiple files (-m)\n"); + fprintf(STDERR, + " with -m %s is used as a temporary file\n", outname); + exit(1); + } + + if (multiple) + { + int i; +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + int allocation_now = current_allocation; +#endif + for (i=2; isize, + (unsigned int) pinfo->pointer); + pinfo = pinfo->next; + } + } +#endif + } +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + fprintf(STDERR, " Current memory allocation: %10d bytes\n", + current_allocation); + fprintf(STDERR, " Maximum memory allocation: %10d bytes\n", + maximum_allocation); + fprintf(STDERR, " Total memory allocation: %10d bytes\n", + total_allocation); + fprintf(STDERR, " Number of allocations: %10d\n", + num_allocations); +#endif + } + else + { + int i; + for (i=0; i<3; ++i) + { + int kerror; +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + int allocation_now = current_allocation; +#endif + if (i == 1) status_dots_requested = 1; + else if(verbose == 0)status_dots_requested = 0; + if (i == 0 || verbose == 1 || ierror != 0) + fprintf(STDERR, "Testing %s:",inname); + kerror = test_one_file(inname, outname); + if(kerror == 0) + { + if(verbose == 1 || i == 2) + { +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + int k; +#endif +#if defined(PNG_WRITE_USER_TRANSFORM_SUPPORTED) + fprintf(STDERR, "\n PASS (%lu zero samples)\n", + (unsigned long)zero_samples); +#else + fprintf(STDERR, " PASS\n"); +#endif +#if defined(PNG_READ_USER_TRANSFORM_SUPPORTED) + for (k=0; k<256; k++) + if(filters_used[k]) + fprintf(STDERR, " Filter %d was used %lu times\n", + k,(unsigned long)filters_used[k]); +#endif +#if defined(PNG_TIME_RFC1123_SUPPORTED) + if(tIME_chunk_present != 0) + fprintf(STDERR, " tIME = %s\n",tIME_string); +#endif /* PNG_TIME_RFC1123_SUPPORTED */ + } + } + else + { + if(verbose == 0 && i != 2) + fprintf(STDERR, "Testing %s:",inname); + fprintf(STDERR, " FAIL\n"); + ierror += kerror; + } +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + if (allocation_now != current_allocation) + fprintf(STDERR, "MEMORY ERROR: %d bytes lost\n", + current_allocation-allocation_now); + if (current_allocation != 0) + { + memory_infop pinfo = pinformation; + + fprintf(STDERR, "MEMORY ERROR: %d bytes still allocated\n", + current_allocation); + while (pinfo != NULL) + { + fprintf(STDERR," %lu bytes at %x\n", + (unsigned long)pinfo->size, (unsigned int)pinfo->pointer); + pinfo = pinfo->next; + } + } +#endif + } +#if defined(PNG_USER_MEM_SUPPORTED) && PNG_DEBUG + fprintf(STDERR, " Current memory allocation: %10d bytes\n", + current_allocation); + fprintf(STDERR, " Maximum memory allocation: %10d bytes\n", + maximum_allocation); + fprintf(STDERR, " Total memory allocation: %10d bytes\n", + total_allocation); + fprintf(STDERR, " Number of allocations: %10d\n", + num_allocations); +#endif + } + +#ifdef PNGTEST_TIMING + t_stop = (float)clock(); + t_misc += (t_stop - t_start); + t_start = t_stop; + fprintf(STDERR," CPU time used = %.3f seconds", + (t_misc+t_decode+t_encode)/(float)CLOCKS_PER_SEC); + fprintf(STDERR," (decoding %.3f,\n", + t_decode/(float)CLOCKS_PER_SEC); + fprintf(STDERR," encoding %.3f ,", + t_encode/(float)CLOCKS_PER_SEC); + fprintf(STDERR," other %.3f seconds)\n\n", + t_misc/(float)CLOCKS_PER_SEC); +#endif + + if (ierror == 0) + fprintf(STDERR, "libpng passes test\n"); + else + fprintf(STDERR, "libpng FAILS test\n"); + return (int)(ierror != 0); +} + +/* Generate a compiler error if there is an old png.h in the search path. */ +typedef version_1_2_24 your_png_h_is_not_version_1_2_24; diff --git a/Engine/lib/lungif/dgif_lib.c b/Engine/lib/lungif/dgif_lib.c new file mode 100644 index 000000000..8a8794840 --- /dev/null +++ b/Engine/lib/lungif/dgif_lib.c @@ -0,0 +1,1096 @@ +/****************************************************************************** + * "Gif-Lib" - Yet another gif library. + * + * Written by: Gershon Elber IBM PC Ver 1.1, Aug. 1990 + ****************************************************************************** + * The kernel of the GIF Decoding process can be found here. + ****************************************************************************** + * History: + * 16 Jun 89 - Version 1.0 by Gershon Elber. + * 3 Sep 90 - Version 1.1 by Gershon Elber (Support for Gif89, Unique names). + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#if defined (__MSDOS__) && !defined(__DJGPP__) && !defined(__GNUC__) +#include +#include +#include +#else +#include +#include +#endif /* __MSDOS__ */ + +#ifdef __MACOSX__ +#define HAVE_FCNTL_H +#define HAVE_UNISTD_H +#endif + +#ifdef HAVE_IO_H +#include +#endif + + +#ifdef HAVE_FCNTL_H +#include +#endif /* HAVE_FCNTL_H */ +#ifdef HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ +#include +#include +#include "gif_lib.h" +#include "gif_lib_private.h" + +#define COMMENT_EXT_FUNC_CODE 0xfe /* Extension function code for + comment. */ + +/* avoid extra function call in case we use fread (TVT) */ +#define READ(_gif,_buf,_len) \ + (((GifFilePrivateType*)_gif->Private)->Read ? \ + ((GifFilePrivateType*)_gif->Private)->Read(_gif,_buf,_len) : \ + fread(_buf,1,_len,((GifFilePrivateType*)_gif->Private)->File)) + +static int DGifGetWord(GifFileType *GifFile, GifWord *Word); +static int DGifSetupDecompress(GifFileType *GifFile); +static int DGifDecompressLine(GifFileType *GifFile, GifPixelType *Line, + int LineLen); +static int DGifGetPrefixChar(GifPrefixType *Prefix, int Code, int ClearCode); +static int DGifDecompressInput(GifFileType *GifFile, int *Code); +static int DGifBufferedInput(GifFileType *GifFile, GifByteType *Buf, + GifByteType *NextByte); +#ifndef _GBA_NO_FILEIO + +/****************************************************************************** + * Open a new gif file for read, given by its name. + * Returns GifFileType pointer dynamically allocated which serves as the gif + * info record. _GifError is cleared if succesfull. + *****************************************************************************/ +GifFileType * +DGifOpenFileName(const char *FileName) { + int FileHandle; + GifFileType *GifFile; + + if ((FileHandle = open(FileName, O_RDONLY +#if defined(__MSDOS__) || defined(_OPEN_BINARY) + | O_BINARY +#endif /* __MSDOS__ || _OPEN_BINARY */ + )) == -1) { + _GifError = D_GIF_ERR_OPEN_FAILED; + return NULL; + } + + GifFile = DGifOpenFileHandle(FileHandle); + if (GifFile == (GifFileType *)NULL) + close(FileHandle); + return GifFile; +} + +/****************************************************************************** + * Update a new gif file, given its file handle. + * Returns GifFileType pointer dynamically allocated which serves as the gif + * info record. _GifError is cleared if succesfull. + *****************************************************************************/ +GifFileType * +DGifOpenFileHandle(int FileHandle) { + + unsigned char Buf[GIF_STAMP_LEN + 1]; + GifFileType *GifFile; + GifFilePrivateType *Private; + FILE *f; + + GifFile = (GifFileType *)malloc(sizeof(GifFileType)); + if (GifFile == NULL) { + _GifError = D_GIF_ERR_NOT_ENOUGH_MEM; + return NULL; + } + + memset(GifFile, '\0', sizeof(GifFileType)); + + Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType)); + if (Private == NULL) { + _GifError = D_GIF_ERR_NOT_ENOUGH_MEM; + free((char *)GifFile); + return NULL; + } +#ifdef __MSDOS__ + setmode(FileHandle, O_BINARY); /* Make sure it is in binary mode. */ +#endif /* __MSDOS__ */ + + f = fdopen(FileHandle, "rb"); /* Make it into a stream: */ + +#ifdef __MSDOS__ + setvbuf(f, NULL, _IOFBF, GIF_FILE_BUFFER_SIZE); /* And inc. stream + buffer. */ +#endif /* __MSDOS__ */ + + GifFile->Private = (VoidPtr)Private; + Private->FileHandle = FileHandle; + Private->File = f; + Private->FileState = FILE_STATE_READ; + Private->Read = 0; /* don't use alternate input method (TVT) */ + GifFile->UserData = 0; /* TVT */ + + /* Lets see if this is a GIF file: */ + if (READ(GifFile, Buf, GIF_STAMP_LEN) != GIF_STAMP_LEN) { + _GifError = D_GIF_ERR_READ_FAILED; + fclose(f); + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + /* The GIF Version number is ignored at this time. Maybe we should do + * something more useful with it. */ + Buf[GIF_STAMP_LEN] = 0; + if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) { + _GifError = D_GIF_ERR_NOT_GIF_FILE; + fclose(f); + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + if (DGifGetScreenDesc(GifFile) == GIF_ERROR) { + fclose(f); + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + _GifError = 0; + + return GifFile; +} + +#endif /* _GBA_NO_FILEIO */ + +/****************************************************************************** + * GifFileType constructor with user supplied input function (TVT) + *****************************************************************************/ +GifFileType * +DGifOpen(void *userData, + InputFunc readFunc) { + + unsigned char Buf[GIF_STAMP_LEN + 1]; + GifFileType *GifFile; + GifFilePrivateType *Private; + + GifFile = (GifFileType *)malloc(sizeof(GifFileType)); + if (GifFile == NULL) { + _GifError = D_GIF_ERR_NOT_ENOUGH_MEM; + return NULL; + } + + memset(GifFile, '\0', sizeof(GifFileType)); + + Private = (GifFilePrivateType *)malloc(sizeof(GifFilePrivateType)); + if (!Private) { + _GifError = D_GIF_ERR_NOT_ENOUGH_MEM; + free((char *)GifFile); + return NULL; + } + + GifFile->Private = (VoidPtr)Private; + Private->FileHandle = 0; + Private->File = 0; + Private->FileState = FILE_STATE_READ; + + Private->Read = readFunc; /* TVT */ + GifFile->UserData = userData; /* TVT */ + + /* Lets see if this is a GIF file: */ + if (READ(GifFile, Buf, GIF_STAMP_LEN) != GIF_STAMP_LEN) { + _GifError = D_GIF_ERR_READ_FAILED; + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + /* The GIF Version number is ignored at this time. Maybe we should do + * something more useful with it. */ + Buf[GIF_STAMP_LEN] = 0; + if (strncmp(GIF_STAMP, Buf, GIF_VERSION_POS) != 0) { + _GifError = D_GIF_ERR_NOT_GIF_FILE; + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + if (DGifGetScreenDesc(GifFile) == GIF_ERROR) { + free((char *)Private); + free((char *)GifFile); + return NULL; + } + + _GifError = 0; + + return GifFile; +} + +/****************************************************************************** + * This routine should be called before any other DGif calls. Note that + * this routine is called automatically from DGif file open routines. + *****************************************************************************/ +int +DGifGetScreenDesc(GifFileType * GifFile) { + + int i, BitsPerPixel; + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + _GifError = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + /* Put the screen descriptor into the file: */ + if (DGifGetWord(GifFile, &GifFile->SWidth) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->SHeight) == GIF_ERROR) + return GIF_ERROR; + + if (READ(GifFile, Buf, 3) != 3) { + _GifError = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + GifFile->SColorResolution = (((Buf[0] & 0x70) + 1) >> 4) + 1; + BitsPerPixel = (Buf[0] & 0x07) + 1; + GifFile->SBackGroundColor = Buf[1]; + if (Buf[0] & 0x80) { /* Do we have global color map? */ + + GifFile->SColorMap = MakeMapObject(1 << BitsPerPixel, NULL); + if (GifFile->SColorMap == NULL) { + _GifError = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + + /* Get the global color map: */ + for (i = 0; i < GifFile->SColorMap->ColorCount; i++) { + if (READ(GifFile, Buf, 3) != 3) { + FreeMapObject(GifFile->SColorMap); + GifFile->SColorMap = NULL; + _GifError = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + GifFile->SColorMap->Colors[i].Red = Buf[0]; + GifFile->SColorMap->Colors[i].Green = Buf[1]; + GifFile->SColorMap->Colors[i].Blue = Buf[2]; + } + } else { + GifFile->SColorMap = NULL; + } + + return GIF_OK; +} + +/****************************************************************************** + * This routine should be called before any attempt to read an image. + *****************************************************************************/ +int +DGifGetRecordType(GifFileType * GifFile, + GifRecordType * Type) { + + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + _GifError = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (READ(GifFile, &Buf, 1) != 1) { + _GifError = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + + switch (Buf) { + case ',': + *Type = IMAGE_DESC_RECORD_TYPE; + break; + case '!': + *Type = EXTENSION_RECORD_TYPE; + break; + case ';': + *Type = TERMINATE_RECORD_TYPE; + break; + default: + *Type = UNDEFINED_RECORD_TYPE; + _GifError = D_GIF_ERR_WRONG_RECORD; + return GIF_ERROR; + } + + return GIF_OK; +} + +/****************************************************************************** + * This routine should be called before any attempt to read an image. + * Note it is assumed the Image desc. header (',') has been read. + *****************************************************************************/ +int +DGifGetImageDesc(GifFileType * GifFile) { + + int i, BitsPerPixel; + GifByteType Buf[3]; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + SavedImage *sp; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + _GifError = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (DGifGetWord(GifFile, &GifFile->Image.Left) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->Image.Top) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->Image.Width) == GIF_ERROR || + DGifGetWord(GifFile, &GifFile->Image.Height) == GIF_ERROR) + return GIF_ERROR; + if (READ(GifFile, Buf, 1) != 1) { + _GifError = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + BitsPerPixel = (Buf[0] & 0x07) + 1; + GifFile->Image.Interlace = (Buf[0] & 0x40); + if (Buf[0] & 0x80) { /* Does this image have local color map? */ + + /*** FIXME: Why do we check both of these in order to do this? + * Why do we have both Image and SavedImages? */ + if (GifFile->Image.ColorMap && GifFile->SavedImages == NULL) + FreeMapObject(GifFile->Image.ColorMap); + + GifFile->Image.ColorMap = MakeMapObject(1 << BitsPerPixel, NULL); + if (GifFile->Image.ColorMap == NULL) { + _GifError = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + + /* Get the image local color map: */ + for (i = 0; i < GifFile->Image.ColorMap->ColorCount; i++) { + if (READ(GifFile, Buf, 3) != 3) { + FreeMapObject(GifFile->Image.ColorMap); + _GifError = D_GIF_ERR_READ_FAILED; + GifFile->Image.ColorMap = NULL; + return GIF_ERROR; + } + GifFile->Image.ColorMap->Colors[i].Red = Buf[0]; + GifFile->Image.ColorMap->Colors[i].Green = Buf[1]; + GifFile->Image.ColorMap->Colors[i].Blue = Buf[2]; + } + } else if (GifFile->Image.ColorMap) { + FreeMapObject(GifFile->Image.ColorMap); + GifFile->Image.ColorMap = NULL; + } + + if (GifFile->SavedImages) { + if ((GifFile->SavedImages = (SavedImage *)realloc(GifFile->SavedImages, + sizeof(SavedImage) * + (GifFile->ImageCount + 1))) == NULL) { + _GifError = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + } else { + if ((GifFile->SavedImages = + (SavedImage *) malloc(sizeof(SavedImage))) == NULL) { + _GifError = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + } + + sp = &GifFile->SavedImages[GifFile->ImageCount]; + memcpy(&sp->ImageDesc, &GifFile->Image, sizeof(GifImageDesc)); + if (GifFile->Image.ColorMap != NULL) { + sp->ImageDesc.ColorMap = MakeMapObject( + GifFile->Image.ColorMap->ColorCount, + GifFile->Image.ColorMap->Colors); + if (sp->ImageDesc.ColorMap == NULL) { + _GifError = D_GIF_ERR_NOT_ENOUGH_MEM; + return GIF_ERROR; + } + } + sp->RasterBits = (unsigned char *)NULL; + sp->ExtensionBlockCount = 0; + sp->ExtensionBlocks = (ExtensionBlock *) NULL; + + GifFile->ImageCount++; + + Private->PixelCount = (long)GifFile->Image.Width * + (long)GifFile->Image.Height; + + DGifSetupDecompress(GifFile); /* Reset decompress algorithm parameters. */ + + return GIF_OK; +} + +/****************************************************************************** + * Get one full scanned line (Line) of length LineLen from GIF file. + *****************************************************************************/ +int +DGifGetLine(GifFileType * GifFile, + GifPixelType * Line, + int LineLen) { + + GifByteType *Dummy; + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + _GifError = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (!LineLen) + LineLen = GifFile->Image.Width; + +#if defined(__MSDOS__) || defined(__GNUC__) + if ((Private->PixelCount -= LineLen) > 0xffff0000UL) { +#else + if ((Private->PixelCount -= LineLen) > 0xffff0000) { +#endif /* __MSDOS__ */ + _GifError = D_GIF_ERR_DATA_TOO_BIG; + return GIF_ERROR; + } + + if (DGifDecompressLine(GifFile, Line, LineLen) == GIF_OK) { + if (Private->PixelCount == 0) { + /* We probably would not be called any more, so lets clean + * everything before we return: need to flush out all rest of + * image until empty block (size 0) detected. We use GetCodeNext. */ + do + if (DGifGetCodeNext(GifFile, &Dummy) == GIF_ERROR) + return GIF_ERROR; + while (Dummy != NULL) ; + } + return GIF_OK; + } else + return GIF_ERROR; +} + +/****************************************************************************** + * Put one pixel (Pixel) into GIF file. + *****************************************************************************/ +int +DGifGetPixel(GifFileType * GifFile, + GifPixelType Pixel) { + + GifByteType *Dummy; + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + _GifError = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } +#if defined(__MSDOS__) || defined(__GNUC__) + if (--Private->PixelCount > 0xffff0000UL) +#else + if (--Private->PixelCount > 0xffff0000) +#endif /* __MSDOS__ */ + { + _GifError = D_GIF_ERR_DATA_TOO_BIG; + return GIF_ERROR; + } + + if (DGifDecompressLine(GifFile, &Pixel, 1) == GIF_OK) { + if (Private->PixelCount == 0) { + /* We probably would not be called any more, so lets clean + * everything before we return: need to flush out all rest of + * image until empty block (size 0) detected. We use GetCodeNext. */ + do + if (DGifGetCodeNext(GifFile, &Dummy) == GIF_ERROR) + return GIF_ERROR; + while (Dummy != NULL) ; + } + return GIF_OK; + } else + return GIF_ERROR; +} + +/****************************************************************************** + * Get an extension block (see GIF manual) from gif file. This routine only + * returns the first data block, and DGifGetExtensionNext should be called + * after this one until NULL extension is returned. + * The Extension should NOT be freed by the user (not dynamically allocated). + * Note it is assumed the Extension desc. header ('!') has been read. + *****************************************************************************/ +int +DGifGetExtension(GifFileType * GifFile, + int *ExtCode, + GifByteType ** Extension) { + + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + _GifError = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (READ(GifFile, &Buf, 1) != 1) { + _GifError = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + *ExtCode = Buf; + + return DGifGetExtensionNext(GifFile, Extension); +} + +/****************************************************************************** + * Get a following extension block (see GIF manual) from gif file. This + * routine should be called until NULL Extension is returned. + * The Extension should NOT be freed by the user (not dynamically allocated). + *****************************************************************************/ +int +DGifGetExtensionNext(GifFileType * GifFile, + GifByteType ** Extension) { + + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (READ(GifFile, &Buf, 1) != 1) { + _GifError = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + if (Buf > 0) { + *Extension = Private->Buf; /* Use private unused buffer. */ + (*Extension)[0] = Buf; /* Pascal strings notation (pos. 0 is len.). */ + if (READ(GifFile, &((*Extension)[1]), Buf) != Buf) { + _GifError = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + } else + *Extension = NULL; + + return GIF_OK; +} + +/****************************************************************************** + * This routine should be called last, to close the GIF file. + *****************************************************************************/ +int +DGifCloseFile(GifFileType * GifFile) { + + GifFilePrivateType *Private; + FILE *File; + + if (GifFile == NULL) + return GIF_ERROR; + + Private = (GifFilePrivateType *) GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + _GifError = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + File = Private->File; + + if (GifFile->Image.ColorMap) { + FreeMapObject(GifFile->Image.ColorMap); + GifFile->Image.ColorMap = NULL; + } + + if (GifFile->SColorMap) { + FreeMapObject(GifFile->SColorMap); + GifFile->SColorMap = NULL; + } + + if (Private) { + free((char *)Private); + Private = NULL; + } + + if (GifFile->SavedImages) { + FreeSavedImages(GifFile); + GifFile->SavedImages = NULL; + } + + free(GifFile); + + if (File && (fclose(File) != 0)) { + _GifError = D_GIF_ERR_CLOSE_FAILED; + return GIF_ERROR; + } + return GIF_OK; +} + +/****************************************************************************** + * Get 2 bytes (word) from the given file: + *****************************************************************************/ +static int +DGifGetWord(GifFileType * GifFile, + GifWord *Word) { + + unsigned char c[2]; + + if (READ(GifFile, c, 2) != 2) { + _GifError = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + + *Word = (((unsigned int)c[1]) << 8) + c[0]; + return GIF_OK; +} + +/****************************************************************************** + * Get the image code in compressed form. This routine can be called if the + * information needed to be piped out as is. Obviously this is much faster + * than decoding and encoding again. This routine should be followed by calls + * to DGifGetCodeNext, until NULL block is returned. + * The block should NOT be freed by the user (not dynamically allocated). + *****************************************************************************/ +int +DGifGetCode(GifFileType * GifFile, + int *CodeSize, + GifByteType ** CodeBlock) { + + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + _GifError = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + *CodeSize = Private->BitsPerPixel; + + return DGifGetCodeNext(GifFile, CodeBlock); +} + +/****************************************************************************** + * Continue to get the image code in compressed form. This routine should be + * called until NULL block is returned. + * The block should NOT be freed by the user (not dynamically allocated). + *****************************************************************************/ +int +DGifGetCodeNext(GifFileType * GifFile, + GifByteType ** CodeBlock) { + + GifByteType Buf; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (READ(GifFile, &Buf, 1) != 1) { + _GifError = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + + if (Buf > 0) { + *CodeBlock = Private->Buf; /* Use private unused buffer. */ + (*CodeBlock)[0] = Buf; /* Pascal strings notation (pos. 0 is len.). */ + if (READ(GifFile, &((*CodeBlock)[1]), Buf) != Buf) { + _GifError = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + } else { + *CodeBlock = NULL; + Private->Buf[0] = 0; /* Make sure the buffer is empty! */ + Private->PixelCount = 0; /* And local info. indicate image read. */ + } + + return GIF_OK; +} + +/****************************************************************************** + * Setup the LZ decompression for this image: + *****************************************************************************/ +static int +DGifSetupDecompress(GifFileType * GifFile) { + + int i, BitsPerPixel; + GifByteType CodeSize; + GifPrefixType *Prefix; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + READ(GifFile, &CodeSize, 1); /* Read Code size from file. */ + BitsPerPixel = CodeSize; + + Private->Buf[0] = 0; /* Input Buffer empty. */ + Private->BitsPerPixel = BitsPerPixel; + Private->ClearCode = (1 << BitsPerPixel); + Private->EOFCode = Private->ClearCode + 1; + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = BitsPerPixel + 1; /* Number of bits per code. */ + Private->MaxCode1 = 1 << Private->RunningBits; /* Max. code + 1. */ + Private->StackPtr = 0; /* No pixels on the pixel stack. */ + Private->LastCode = NO_SUCH_CODE; + Private->CrntShiftState = 0; /* No information in CrntShiftDWord. */ + Private->CrntShiftDWord = 0; + + Prefix = Private->Prefix; + for (i = 0; i <= LZ_MAX_CODE; i++) + Prefix[i] = NO_SUCH_CODE; + + return GIF_OK; +} + +/****************************************************************************** + * The LZ decompression routine: + * This version decompress the given gif file into Line of length LineLen. + * This routine can be called few times (one per scan line, for example), in + * order the complete the whole image. + *****************************************************************************/ +static int +DGifDecompressLine(GifFileType * GifFile, + GifPixelType * Line, + int LineLen) { + + int i = 0; + int j, CrntCode, EOFCode, ClearCode, CrntPrefix, LastCode, StackPtr; + GifByteType *Stack, *Suffix; + GifPrefixType *Prefix; + GifFilePrivateType *Private = (GifFilePrivateType *) GifFile->Private; + + StackPtr = Private->StackPtr; + Prefix = Private->Prefix; + Suffix = Private->Suffix; + Stack = Private->Stack; + EOFCode = Private->EOFCode; + ClearCode = Private->ClearCode; + LastCode = Private->LastCode; + + if (StackPtr != 0) { + /* Let pop the stack off before continueing to read the gif file: */ + while (StackPtr != 0 && i < LineLen) + Line[i++] = Stack[--StackPtr]; + } + + while (i < LineLen) { /* Decode LineLen items. */ + if (DGifDecompressInput(GifFile, &CrntCode) == GIF_ERROR) + return GIF_ERROR; + + if (CrntCode == EOFCode) { + /* Note however that usually we will not be here as we will stop + * decoding as soon as we got all the pixel, or EOF code will + * not be read at all, and DGifGetLine/Pixel clean everything. */ + if (i != LineLen - 1 || Private->PixelCount != 0) { + _GifError = D_GIF_ERR_EOF_TOO_SOON; + return GIF_ERROR; + } + i++; + } else if (CrntCode == ClearCode) { + /* We need to start over again: */ + for (j = 0; j <= LZ_MAX_CODE; j++) + Prefix[j] = NO_SUCH_CODE; + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = Private->BitsPerPixel + 1; + Private->MaxCode1 = 1 << Private->RunningBits; + LastCode = Private->LastCode = NO_SUCH_CODE; + } else { + /* Its regular code - if in pixel range simply add it to output + * stream, otherwise trace to codes linked list until the prefix + * is in pixel range: */ + if (CrntCode < ClearCode) { + /* This is simple - its pixel scalar, so add it to output: */ + Line[i++] = CrntCode; + } else { + /* Its a code to needed to be traced: trace the linked list + * until the prefix is a pixel, while pushing the suffix + * pixels on our stack. If we done, pop the stack in reverse + * (thats what stack is good for!) order to output. */ + if (Prefix[CrntCode] == NO_SUCH_CODE) { + /* Only allowed if CrntCode is exactly the running code: + * In that case CrntCode = XXXCode, CrntCode or the + * prefix code is last code and the suffix char is + * exactly the prefix of last code! */ + if (CrntCode == Private->RunningCode - 2) { + CrntPrefix = LastCode; + Suffix[Private->RunningCode - 2] = + Stack[StackPtr++] = DGifGetPrefixChar(Prefix, + LastCode, + ClearCode); + } else { + _GifError = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + } else + CrntPrefix = CrntCode; + + /* Now (if image is O.K.) we should not get an NO_SUCH_CODE + * During the trace. As we might loop forever, in case of + * defective image, we count the number of loops we trace + * and stop if we got LZ_MAX_CODE. obviously we can not + * loop more than that. */ + j = 0; + while (j++ <= LZ_MAX_CODE && + CrntPrefix > ClearCode && CrntPrefix <= LZ_MAX_CODE) { + Stack[StackPtr++] = Suffix[CrntPrefix]; + CrntPrefix = Prefix[CrntPrefix]; + } + if (j >= LZ_MAX_CODE || CrntPrefix > LZ_MAX_CODE) { + _GifError = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + /* Push the last character on stack: */ + Stack[StackPtr++] = CrntPrefix; + + /* Now lets pop all the stack into output: */ + while (StackPtr != 0 && i < LineLen) + Line[i++] = Stack[--StackPtr]; + } + if (LastCode != NO_SUCH_CODE) { + Prefix[Private->RunningCode - 2] = LastCode; + + if (CrntCode == Private->RunningCode - 2) { + /* Only allowed if CrntCode is exactly the running code: + * In that case CrntCode = XXXCode, CrntCode or the + * prefix code is last code and the suffix char is + * exactly the prefix of last code! */ + Suffix[Private->RunningCode - 2] = + DGifGetPrefixChar(Prefix, LastCode, ClearCode); + } else { + Suffix[Private->RunningCode - 2] = + DGifGetPrefixChar(Prefix, CrntCode, ClearCode); + } + } + LastCode = CrntCode; + } + } + + Private->LastCode = LastCode; + Private->StackPtr = StackPtr; + + return GIF_OK; +} + +/****************************************************************************** + * Routine to trace the Prefixes linked list until we get a prefix which is + * not code, but a pixel value (less than ClearCode). Returns that pixel value. + * If image is defective, we might loop here forever, so we limit the loops to + * the maximum possible if image O.k. - LZ_MAX_CODE times. + *****************************************************************************/ +static int +DGifGetPrefixChar(GifPrefixType *Prefix, + int Code, + int ClearCode) { + + int i = 0; + + while (Code > ClearCode && i++ <= LZ_MAX_CODE) + Code = Prefix[Code]; + return Code; +} + +/****************************************************************************** + * Interface for accessing the LZ codes directly. Set Code to the real code + * (12bits), or to -1 if EOF code is returned. + *****************************************************************************/ +int +DGifGetLZCodes(GifFileType * GifFile, + int *Code) { + + GifByteType *CodeBlock; + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + if (!IS_READABLE(Private)) { + /* This file was NOT open for reading: */ + _GifError = D_GIF_ERR_NOT_READABLE; + return GIF_ERROR; + } + + if (DGifDecompressInput(GifFile, Code) == GIF_ERROR) + return GIF_ERROR; + + if (*Code == Private->EOFCode) { + /* Skip rest of codes (hopefully only NULL terminating block): */ + do { + if (DGifGetCodeNext(GifFile, &CodeBlock) == GIF_ERROR) + return GIF_ERROR; + } while (CodeBlock != NULL) ; + + *Code = -1; + } else if (*Code == Private->ClearCode) { + /* We need to start over again: */ + Private->RunningCode = Private->EOFCode + 1; + Private->RunningBits = Private->BitsPerPixel + 1; + Private->MaxCode1 = 1 << Private->RunningBits; + } + + return GIF_OK; +} + +/****************************************************************************** + * The LZ decompression input routine: + * This routine is responsable for the decompression of the bit stream from + * 8 bits (bytes) packets, into the real codes. + * Returns GIF_OK if read succesfully. + *****************************************************************************/ +static int +DGifDecompressInput(GifFileType * GifFile, + int *Code) { + + GifFilePrivateType *Private = (GifFilePrivateType *)GifFile->Private; + + GifByteType NextByte; + static unsigned short CodeMasks[] = { + 0x0000, 0x0001, 0x0003, 0x0007, + 0x000f, 0x001f, 0x003f, 0x007f, + 0x00ff, 0x01ff, 0x03ff, 0x07ff, + 0x0fff + }; + /* The image can't contain more than LZ_BITS per code. */ + if (Private->RunningBits > LZ_BITS) { + _GifError = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + + while (Private->CrntShiftState < Private->RunningBits) { + /* Needs to get more bytes from input stream for next code: */ + if (DGifBufferedInput(GifFile, Private->Buf, &NextByte) == GIF_ERROR) { + return GIF_ERROR; + } + Private->CrntShiftDWord |= + ((unsigned long)NextByte) << Private->CrntShiftState; + Private->CrntShiftState += 8; + } + *Code = Private->CrntShiftDWord & CodeMasks[Private->RunningBits]; + + Private->CrntShiftDWord >>= Private->RunningBits; + Private->CrntShiftState -= Private->RunningBits; + + /* If code cannot fit into RunningBits bits, must raise its size. Note + * however that codes above 4095 are used for special signaling. + * If we're using LZ_BITS bits already and we're at the max code, just + * keep using the table as it is, don't increment Private->RunningCode. + */ + if (Private->RunningCode < LZ_MAX_CODE + 2 && + ++Private->RunningCode > Private->MaxCode1 && + Private->RunningBits < LZ_BITS) { + Private->MaxCode1 <<= 1; + Private->RunningBits++; + } + return GIF_OK; +} + +/****************************************************************************** + * This routines read one gif data block at a time and buffers it internally + * so that the decompression routine could access it. + * The routine returns the next byte from its internal buffer (or read next + * block in if buffer empty) and returns GIF_OK if succesful. + *****************************************************************************/ +static int +DGifBufferedInput(GifFileType * GifFile, + GifByteType * Buf, + GifByteType * NextByte) { + + if (Buf[0] == 0) { + /* Needs to read the next buffer - this one is empty: */ + if (READ(GifFile, Buf, 1) != 1) { + _GifError = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + /* There shouldn't be any empty data blocks here as the LZW spec + * says the LZW termination code should come first. Therefore we + * shouldn't be inside this routine at that point. + */ + if (Buf[0] == 0) { + _GifError = D_GIF_ERR_IMAGE_DEFECT; + return GIF_ERROR; + } + if (READ(GifFile, &Buf[1], Buf[0]) != Buf[0]) { + _GifError = D_GIF_ERR_READ_FAILED; + return GIF_ERROR; + } + *NextByte = Buf[1]; + Buf[1] = 2; /* We use now the second place as last char read! */ + Buf[0]--; + } else { + *NextByte = Buf[Buf[1]++]; + Buf[0]--; + } + + return GIF_OK; +} +#ifndef _GBA_NO_FILEIO + +/****************************************************************************** + * This routine reads an entire GIF into core, hanging all its state info off + * the GifFileType pointer. Call DGifOpenFileName() or DGifOpenFileHandle() + * first to initialize I/O. Its inverse is EGifSpew(). + ******************************************************************************/ +int +DGifSlurp(GifFileType * GifFile) { + + int ImageSize; + GifRecordType RecordType; + SavedImage *sp; + GifByteType *ExtData; + SavedImage temp_save; + + temp_save.ExtensionBlocks = NULL; + temp_save.ExtensionBlockCount = 0; + + do { + if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) + return (GIF_ERROR); + + switch (RecordType) { + case IMAGE_DESC_RECORD_TYPE: + if (DGifGetImageDesc(GifFile) == GIF_ERROR) + return (GIF_ERROR); + + sp = &GifFile->SavedImages[GifFile->ImageCount - 1]; + ImageSize = sp->ImageDesc.Width * sp->ImageDesc.Height; + + sp->RasterBits = (unsigned char *)malloc(ImageSize * + sizeof(GifPixelType)); + if (sp->RasterBits == NULL) { + return GIF_ERROR; + } + if (DGifGetLine(GifFile, sp->RasterBits, ImageSize) == + GIF_ERROR) + return (GIF_ERROR); + if (temp_save.ExtensionBlocks) { + sp->ExtensionBlocks = temp_save.ExtensionBlocks; + sp->ExtensionBlockCount = temp_save.ExtensionBlockCount; + + temp_save.ExtensionBlocks = NULL; + temp_save.ExtensionBlockCount = 0; + + /* FIXME: The following is wrong. It is left in only for + * backwards compatibility. Someday it should go away. Use + * the sp->ExtensionBlocks->Function variable instead. */ + sp->Function = sp->ExtensionBlocks[0].Function; + } + break; + + case EXTENSION_RECORD_TYPE: + if (DGifGetExtension(GifFile, &temp_save.Function, &ExtData) == + GIF_ERROR) + return (GIF_ERROR); + while (ExtData != NULL) { + + /* Create an extension block with our data */ + if (AddExtensionBlock(&temp_save, ExtData[0], &ExtData[1]) + == GIF_ERROR) + return (GIF_ERROR); + + if (DGifGetExtensionNext(GifFile, &ExtData) == GIF_ERROR) + return (GIF_ERROR); + temp_save.Function = 0; + } + break; + + case TERMINATE_RECORD_TYPE: + break; + + default: /* Should be trapped by DGifGetRecordType */ + break; + } + } while (RecordType != TERMINATE_RECORD_TYPE); + + /* Just in case the Gif has an extension block without an associated + * image... (Should we save this into a savefile structure with no image + * instead? Have to check if the present writing code can handle that as + * well.... */ + if (temp_save.ExtensionBlocks) + FreeExtension(&temp_save); + + return (GIF_OK); +} +#endif /* _GBA_NO_FILEIO */ diff --git a/Engine/lib/lungif/gif_err.c b/Engine/lib/lungif/gif_err.c new file mode 100644 index 000000000..ea977bdf9 --- /dev/null +++ b/Engine/lib/lungif/gif_err.c @@ -0,0 +1,120 @@ +/***************************************************************************** + * "Gif-Lib" - Yet another gif library. + * + * Written by: Gershon Elber IBM PC Ver 0.1, Jun. 1989 + ***************************************************************************** + * Handle error reporting for the GIF library. + ***************************************************************************** + * History: + * 17 Jun 89 - Version 1.0 by Gershon Elber. + ****************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include "gif_lib.h" + +int _GifError = 0; + +/***************************************************************************** + * Return the last GIF error (0 if none) and reset the error. + ****************************************************************************/ +int +GifLastError(void) { + int i = _GifError; + + _GifError = 0; + + return i; +} +#ifndef _GBA_NO_FILEIO + +/***************************************************************************** + * Print the last GIF error to stderr. + ****************************************************************************/ +void +PrintGifError(void) { + char *Err; + + switch (_GifError) { + case E_GIF_ERR_OPEN_FAILED: + Err = "Failed to open given file"; + break; + case E_GIF_ERR_WRITE_FAILED: + Err = "Failed to Write to given file"; + break; + case E_GIF_ERR_HAS_SCRN_DSCR: + Err = "Screen Descriptor already been set"; + break; + case E_GIF_ERR_HAS_IMAG_DSCR: + Err = "Image Descriptor is still active"; + break; + case E_GIF_ERR_NO_COLOR_MAP: + Err = "Neither Global Nor Local color map"; + break; + case E_GIF_ERR_DATA_TOO_BIG: + Err = "#Pixels bigger than Width * Height"; + break; + case E_GIF_ERR_NOT_ENOUGH_MEM: + Err = "Fail to allocate required memory"; + break; + case E_GIF_ERR_DISK_IS_FULL: + Err = "Write failed (disk full?)"; + break; + case E_GIF_ERR_CLOSE_FAILED: + Err = "Failed to close given file"; + break; + case E_GIF_ERR_NOT_WRITEABLE: + Err = "Given file was not opened for write"; + break; + case D_GIF_ERR_OPEN_FAILED: + Err = "Failed to open given file"; + break; + case D_GIF_ERR_READ_FAILED: + Err = "Failed to Read from given file"; + break; + case D_GIF_ERR_NOT_GIF_FILE: + Err = "Given file is NOT GIF file"; + break; + case D_GIF_ERR_NO_SCRN_DSCR: + Err = "No Screen Descriptor detected"; + break; + case D_GIF_ERR_NO_IMAG_DSCR: + Err = "No Image Descriptor detected"; + break; + case D_GIF_ERR_NO_COLOR_MAP: + Err = "Neither Global Nor Local color map"; + break; + case D_GIF_ERR_WRONG_RECORD: + Err = "Wrong record type detected"; + break; + case D_GIF_ERR_DATA_TOO_BIG: + Err = "#Pixels bigger than Width * Height"; + break; + case D_GIF_ERR_NOT_ENOUGH_MEM: + Err = "Fail to allocate required memory"; + break; + case D_GIF_ERR_CLOSE_FAILED: + Err = "Failed to close given file"; + break; + case D_GIF_ERR_NOT_READABLE: + Err = "Given file was not opened for read"; + break; + case D_GIF_ERR_IMAGE_DEFECT: + Err = "Image is defective, decoding aborted"; + break; + case D_GIF_ERR_EOF_TOO_SOON: + Err = "Image EOF detected, before image complete"; + break; + default: + Err = NULL; + break; + } + if (Err != NULL) + fprintf(stderr, "\nGIF-LIB error: %s.\n", Err); + else + fprintf(stderr, "\nGIF-LIB undefined error %d.\n", _GifError); +} +#endif /* _GBA_NO_FILEIO */ diff --git a/Engine/lib/lungif/gif_lib.h b/Engine/lib/lungif/gif_lib.h new file mode 100644 index 000000000..54acd91b4 --- /dev/null +++ b/Engine/lib/lungif/gif_lib.h @@ -0,0 +1,336 @@ +/****************************************************************************** + * In order to make life a little bit easier when using the GIF file format, + * this library was written, and which does all the dirty work... + * + * Written by Gershon Elber, Jun. 1989 + * Hacks by Eric S. Raymond, Sep. 1992 + ****************************************************************************** + * History: + * 14 Jun 89 - Version 1.0 by Gershon Elber. + * 3 Sep 90 - Version 1.1 by Gershon Elber (Support for Gif89, Unique names) + * 15 Sep 90 - Version 2.0 by Eric S. Raymond (Changes to suoport GIF slurp) + * 26 Jun 96 - Version 3.0 by Eric S. Raymond (Full GIF89 support) + * 17 Dec 98 - Version 4.0 by Toshio Kuratomi (Fix extension writing code) + *****************************************************************************/ + +#ifndef _GIF_LIB_H_ +#define _GIF_LIB_H_ 1 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GIF_LIB_VERSION " Version 4.1, " + +#define GIF_ERROR 0 +#define GIF_OK 1 + +#ifndef TRUE +#define TRUE 1 +#endif /* TRUE */ +#ifndef FALSE +#define FALSE 0 +#endif /* FALSE */ + +#ifndef NULL +#define NULL 0 +#endif /* NULL */ + +#define GIF_STAMP "GIFVER" /* First chars in file - GIF stamp. */ +#define GIF_STAMP_LEN sizeof(GIF_STAMP) - 1 +#define GIF_VERSION_POS 3 /* Version first character in stamp. */ +#define GIF87_STAMP "GIF87a" /* First chars in file - GIF stamp. */ +#define GIF89_STAMP "GIF89a" /* First chars in file - GIF stamp. */ + +#define GIF_FILE_BUFFER_SIZE 16384 /* Files uses bigger buffers than usual. */ + +typedef int GifBooleanType; +typedef unsigned char GifPixelType; +typedef unsigned char *GifRowType; +typedef unsigned char GifByteType; +#ifdef _GBA_OPTMEM + typedef unsigned short GifPrefixType; + typedef short GifWord; +#else + typedef unsigned int GifPrefixType; + typedef int GifWord; +#endif + +#define GIF_MESSAGE(Msg) fprintf(stderr, "\n%s: %s\n", PROGRAM_NAME, Msg) +#define GIF_EXIT(Msg) { GIF_MESSAGE(Msg); exit(-3); } + +#ifdef SYSV +#define VoidPtr char * +#else +#define VoidPtr void * +#endif /* SYSV */ + +typedef struct GifColorType { + GifByteType Red, Green, Blue; +} GifColorType; + +typedef struct ColorMapObject { + int ColorCount; + int BitsPerPixel; + GifColorType *Colors; /* on malloc(3) heap */ +} ColorMapObject; + +typedef struct GifImageDesc { + GifWord Left, Top, Width, Height, /* Current image dimensions. */ + Interlace; /* Sequential/Interlaced lines. */ + ColorMapObject *ColorMap; /* The local color map */ +} GifImageDesc; + +typedef struct GifFileType { + GifWord SWidth, SHeight, /* Screen dimensions. */ + SColorResolution, /* How many colors can we generate? */ + SBackGroundColor; /* I hope you understand this one... */ + ColorMapObject *SColorMap; /* NULL if not exists. */ + int ImageCount; /* Number of current image */ + GifImageDesc Image; /* Block describing current image */ + struct SavedImage *SavedImages; /* Use this to accumulate file state */ + VoidPtr UserData; /* hook to attach user data (TVT) */ + VoidPtr Private; /* Don't mess with this! */ +} GifFileType; + +typedef enum { + UNDEFINED_RECORD_TYPE, + SCREEN_DESC_RECORD_TYPE, + IMAGE_DESC_RECORD_TYPE, /* Begin with ',' */ + EXTENSION_RECORD_TYPE, /* Begin with '!' */ + TERMINATE_RECORD_TYPE /* Begin with ';' */ +} GifRecordType; + +/* DumpScreen2Gif routine constants identify type of window/screen to dump. + * Note all values below 1000 are reserved for the IBMPC different display + * devices (it has many!) and are compatible with the numbering TC2.0 + * (Turbo C 2.0 compiler for IBM PC) gives to these devices. + */ +typedef enum { + GIF_DUMP_SGI_WINDOW = 1000, + GIF_DUMP_X_WINDOW = 1001 +} GifScreenDumpType; + +/* func type to read gif data from arbitrary sources (TVT) */ +typedef int (*InputFunc) (GifFileType *, GifByteType *, int); + +/* func type to write gif data ro arbitrary targets. + * Returns count of bytes written. (MRB) + */ +typedef int (*OutputFunc) (GifFileType *, const GifByteType *, int); + +/****************************************************************************** + * GIF89 extension function codes +******************************************************************************/ + +#define COMMENT_EXT_FUNC_CODE 0xfe /* comment */ +#define GRAPHICS_EXT_FUNC_CODE 0xf9 /* graphics control */ +#define PLAINTEXT_EXT_FUNC_CODE 0x01 /* plaintext */ +#define APPLICATION_EXT_FUNC_CODE 0xff /* application block */ + +/****************************************************************************** + * O.K., here are the routines one can access in order to encode GIF file: + * (GIF_LIB file EGIF_LIB.C). +******************************************************************************/ + +GifFileType *EGifOpenFileName(const char *GifFileName, + int GifTestExistance); +GifFileType *EGifOpenFileHandle(int GifFileHandle); +GifFileType *EGifOpen(void *userPtr, OutputFunc writeFunc); + +int EGifSpew(GifFileType * GifFile); +void EGifSetGifVersion(const char *Version); +int EGifPutScreenDesc(GifFileType * GifFile, + int GifWidth, int GifHeight, int GifColorRes, + int GifBackGround, + const ColorMapObject * GifColorMap); +int EGifPutImageDesc(GifFileType * GifFile, int GifLeft, int GifTop, + int Width, int GifHeight, int GifInterlace, + const ColorMapObject * GifColorMap); +int EGifPutLine(GifFileType * GifFile, GifPixelType * GifLine, + int GifLineLen); +int EGifPutPixel(GifFileType * GifFile, GifPixelType GifPixel); +int EGifPutComment(GifFileType * GifFile, const char *GifComment); +int EGifPutExtensionFirst(GifFileType * GifFile, int GifExtCode, + int GifExtLen, const VoidPtr GifExtension); +int EGifPutExtensionNext(GifFileType * GifFile, int GifExtCode, + int GifExtLen, const VoidPtr GifExtension); +int EGifPutExtensionLast(GifFileType * GifFile, int GifExtCode, + int GifExtLen, const VoidPtr GifExtension); +int EGifPutExtension(GifFileType * GifFile, int GifExtCode, int GifExtLen, + const VoidPtr GifExtension); +int EGifPutCode(GifFileType * GifFile, int GifCodeSize, + const GifByteType * GifCodeBlock); +int EGifPutCodeNext(GifFileType * GifFile, + const GifByteType * GifCodeBlock); +int EGifCloseFile(GifFileType * GifFile); + +#define E_GIF_ERR_OPEN_FAILED 1 /* And EGif possible errors. */ +#define E_GIF_ERR_WRITE_FAILED 2 +#define E_GIF_ERR_HAS_SCRN_DSCR 3 +#define E_GIF_ERR_HAS_IMAG_DSCR 4 +#define E_GIF_ERR_NO_COLOR_MAP 5 +#define E_GIF_ERR_DATA_TOO_BIG 6 +#define E_GIF_ERR_NOT_ENOUGH_MEM 7 +#define E_GIF_ERR_DISK_IS_FULL 8 +#define E_GIF_ERR_CLOSE_FAILED 9 +#define E_GIF_ERR_NOT_WRITEABLE 10 + +/****************************************************************************** + * O.K., here are the routines one can access in order to decode GIF file: + * (GIF_LIB file DGIF_LIB.C). + *****************************************************************************/ +#ifndef _GBA_NO_FILEIO +GifFileType *DGifOpenFileName(const char *GifFileName); +GifFileType *DGifOpenFileHandle(int GifFileHandle); +int DGifSlurp(GifFileType * GifFile); +#endif /* _GBA_NO_FILEIO */ +GifFileType *DGifOpen(void *userPtr, InputFunc readFunc); /* new one + * (TVT) */ +int DGifGetScreenDesc(GifFileType * GifFile); +int DGifGetRecordType(GifFileType * GifFile, GifRecordType * GifType); +int DGifGetImageDesc(GifFileType * GifFile); +int DGifGetLine(GifFileType * GifFile, GifPixelType * GifLine, int GifLineLen); +int DGifGetPixel(GifFileType * GifFile, GifPixelType GifPixel); +int DGifGetComment(GifFileType * GifFile, char *GifComment); +int DGifGetExtension(GifFileType * GifFile, int *GifExtCode, + GifByteType ** GifExtension); +int DGifGetExtensionNext(GifFileType * GifFile, GifByteType ** GifExtension); +int DGifGetCode(GifFileType * GifFile, int *GifCodeSize, + GifByteType ** GifCodeBlock); +int DGifGetCodeNext(GifFileType * GifFile, GifByteType ** GifCodeBlock); +int DGifGetLZCodes(GifFileType * GifFile, int *GifCode); +int DGifCloseFile(GifFileType * GifFile); + +#define D_GIF_ERR_OPEN_FAILED 101 /* And DGif possible errors. */ +#define D_GIF_ERR_READ_FAILED 102 +#define D_GIF_ERR_NOT_GIF_FILE 103 +#define D_GIF_ERR_NO_SCRN_DSCR 104 +#define D_GIF_ERR_NO_IMAG_DSCR 105 +#define D_GIF_ERR_NO_COLOR_MAP 106 +#define D_GIF_ERR_WRONG_RECORD 107 +#define D_GIF_ERR_DATA_TOO_BIG 108 +#define D_GIF_ERR_NOT_ENOUGH_MEM 109 +#define D_GIF_ERR_CLOSE_FAILED 110 +#define D_GIF_ERR_NOT_READABLE 111 +#define D_GIF_ERR_IMAGE_DEFECT 112 +#define D_GIF_ERR_EOF_TOO_SOON 113 + +/****************************************************************************** + * O.K., here are the routines from GIF_LIB file QUANTIZE.C. +******************************************************************************/ +int QuantizeBuffer(unsigned int Width, unsigned int Height, + int *ColorMapSize, GifByteType * RedInput, + GifByteType * GreenInput, GifByteType * BlueInput, + GifByteType * OutputBuffer, + GifColorType * OutputColorMap); + +/****************************************************************************** + * O.K., here are the routines from GIF_LIB file QPRINTF.C. +******************************************************************************/ +extern int GifQuietPrint; + +#ifdef HAVE_STDARG_H + extern void GifQprintf(char *Format, ...); +#elif defined (HAVE_VARARGS_H) + extern void GifQprintf(); +#endif /* HAVE_STDARG_H */ + +/****************************************************************************** + * O.K., here are the routines from GIF_LIB file GIF_ERR.C. +******************************************************************************/ +#ifndef _GBA_NO_FILEIO +extern void PrintGifError(void); +#endif /* _GBA_NO_FILEIO */ +extern int GifLastError(void); + +/****************************************************************************** + * O.K., here are the routines from GIF_LIB file DEV2GIF.C. +******************************************************************************/ +extern int DumpScreen2Gif(const char *FileName, + int ReqGraphDriver, + long ReqGraphMode1, + long ReqGraphMode2, + long ReqGraphMode3); + +/***************************************************************************** + * + * Everything below this point is new after version 1.2, supporting `slurp + * mode' for doing I/O in two big belts with all the image-bashing in core. + * + *****************************************************************************/ + +/****************************************************************************** + * Color Map handling from ALLOCGIF.C + *****************************************************************************/ + +extern ColorMapObject *MakeMapObject(int ColorCount, + const GifColorType * ColorMap); +extern void FreeMapObject(ColorMapObject * Object); +extern ColorMapObject *UnionColorMap(const ColorMapObject * ColorIn1, + const ColorMapObject * ColorIn2, + GifPixelType ColorTransIn2[]); +extern int BitSize(int n); + +/****************************************************************************** + * Support for the in-core structures allocation (slurp mode). + *****************************************************************************/ + +/* This is the in-core version of an extension record */ +typedef struct { + int ByteCount; + char *Bytes; /* on malloc(3) heap */ + int Function; /* Holds the type of the Extension block. */ +} ExtensionBlock; + +/* This holds an image header, its unpacked raster bits, and extensions */ +typedef struct SavedImage { + GifImageDesc ImageDesc; + unsigned char *RasterBits; /* on malloc(3) heap */ + int Function; /* DEPRECATED: Use ExtensionBlocks[x].Function instead */ + int ExtensionBlockCount; + ExtensionBlock *ExtensionBlocks; /* on malloc(3) heap */ +} SavedImage; + +extern void ApplyTranslation(SavedImage * Image, GifPixelType Translation[]); +extern void MakeExtension(SavedImage * New, int Function); +extern int AddExtensionBlock(SavedImage * New, int Len, + unsigned char ExtData[]); +extern void FreeExtension(SavedImage * Image); +extern SavedImage *MakeSavedImage(GifFileType * GifFile, + const SavedImage * CopyFrom); +extern void FreeSavedImages(GifFileType * GifFile); + +/****************************************************************************** + * The library's internal utility font + *****************************************************************************/ + +#define GIF_FONT_WIDTH 8 +#define GIF_FONT_HEIGHT 8 +extern unsigned char AsciiTable[][GIF_FONT_WIDTH]; + +#ifdef _WIN32 + extern void DrawGifText(SavedImage * Image, +#else + extern void DrawText(SavedImage * Image, +#endif + const int x, const int y, + const char *legend, const int color); + +extern void DrawBox(SavedImage * Image, + const int x, const int y, + const int w, const int d, const int color); + +void DrawRectangle(SavedImage * Image, + const int x, const int y, + const int w, const int d, const int color); + +extern void DrawBoxedText(SavedImage * Image, + const int x, const int y, + const char *legend, + const int border, const int bg, const int fg); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* _GIF_LIB_H */ diff --git a/Engine/lib/lungif/gif_lib_private.h b/Engine/lib/lungif/gif_lib_private.h new file mode 100644 index 000000000..84e3738ba --- /dev/null +++ b/Engine/lib/lungif/gif_lib_private.h @@ -0,0 +1,57 @@ +#ifndef _GIF_LIB_PRIVATE_H +#define _GIF_LIB_PRIVATE_H + +#include "gif_lib.h" + +#define PROGRAM_NAME "LIBUNGIF" + +#ifdef SYSV +#define VersionStr "Gif library module,\t\tEric S. Raymond\n\ + (C) Copyright 1997 Eric S. Raymond\n" +#else +#define VersionStr PROGRAM_NAME " IBMPC " GIF_LIB_VERSION \ + " Eric S. Raymond, " __DATE__ ", " \ + __TIME__ "\n" "(C) Copyright 1997 Eric S. Raymond\n" +#endif /* SYSV */ + +#define LZ_MAX_CODE 4095 /* Biggest code possible in 12 bits. */ +#define LZ_BITS 12 + +#define FLUSH_OUTPUT 4096 /* Impossible code, to signal flush. */ +#define FIRST_CODE 4097 /* Impossible code, to signal first. */ +#define NO_SUCH_CODE 4098 /* Impossible code, to signal empty. */ + +#define FILE_STATE_WRITE 0x01 +#define FILE_STATE_SCREEN 0x02 +#define FILE_STATE_IMAGE 0x04 +#define FILE_STATE_READ 0x08 + +#define IS_READABLE(Private) (Private->FileState & FILE_STATE_READ) +#define IS_WRITEABLE(Private) (Private->FileState & FILE_STATE_WRITE) + +typedef struct GifFilePrivateType { + GifWord FileState, FileHandle, /* Where all this data goes to! */ + BitsPerPixel, /* Bits per pixel (Codes uses at least this + 1). */ + ClearCode, /* The CLEAR LZ code. */ + EOFCode, /* The EOF LZ code. */ + RunningCode, /* The next code algorithm can generate. */ + RunningBits, /* The number of bits required to represent RunningCode. */ + MaxCode1, /* 1 bigger than max. possible code, in RunningBits bits. */ + LastCode, /* The code before the current code. */ + CrntCode, /* Current algorithm code. */ + StackPtr, /* For character stack (see below). */ + CrntShiftState; /* Number of bits in CrntShiftDWord. */ + unsigned long CrntShiftDWord; /* For bytes decomposition into codes. */ + unsigned long PixelCount; /* Number of pixels in image. */ + FILE *File; /* File as stream. */ + InputFunc Read; /* function to read gif input (TVT) */ + OutputFunc Write; /* function to write gif output (MRB) */ + GifByteType Buf[256]; /* Compressed input is buffered here. */ + GifByteType Stack[LZ_MAX_CODE]; /* Decoded pixels are stacked here. */ + GifByteType Suffix[LZ_MAX_CODE + 1]; /* So we can trace the codes. */ + GifPrefixType Prefix[LZ_MAX_CODE + 1]; +} GifFilePrivateType; + +extern int _GifError; + +#endif /* _GIF_LIB_PRIVATE_H */ diff --git a/Engine/lib/lungif/gifalloc.c b/Engine/lib/lungif/gifalloc.c new file mode 100644 index 000000000..79d23325b --- /dev/null +++ b/Engine/lib/lungif/gifalloc.c @@ -0,0 +1,443 @@ +/***************************************************************************** + * "Gif-Lib" - Yet another gif library. + * + * Written by: Gershon Elber Ver 0.1, Jun. 1989 + * Extensively hacked by: Eric S. Raymond Ver 1.?, Sep 1992 + ***************************************************************************** + * GIF construction tools + ***************************************************************************** + * History: + * 15 Sep 92 - Version 1.0 by Eric Raymond. + ****************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include "gif_lib.h" + +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) + +/****************************************************************************** + * Miscellaneous utility functions + *****************************************************************************/ + +/* return smallest bitfield size n will fit in */ +int +BitSize(int n) { + + register int i; + + for (i = 1; i <= 8; i++) + if ((1 << i) >= n) + break; + return (i); +} + +/****************************************************************************** + * Color map object functions + *****************************************************************************/ + +/* + * Allocate a color map of given size; initialize with contents of + * ColorMap if that pointer is non-NULL. + */ +ColorMapObject * +MakeMapObject(int ColorCount, + const GifColorType * ColorMap) { + + ColorMapObject *Object; + + /*** FIXME: Our ColorCount has to be a power of two. Is it necessary to + * make the user know that or should we automatically round up instead? */ + if (ColorCount != (1 << BitSize(ColorCount))) { + return ((ColorMapObject *) NULL); + } + + Object = (ColorMapObject *)malloc(sizeof(ColorMapObject)); + if (Object == (ColorMapObject *) NULL) { + return ((ColorMapObject *) NULL); + } + + Object->Colors = (GifColorType *)calloc(ColorCount, sizeof(GifColorType)); + if (Object->Colors == (GifColorType *) NULL) { + return ((ColorMapObject *) NULL); + } + + Object->ColorCount = ColorCount; + Object->BitsPerPixel = BitSize(ColorCount); + + if (ColorMap) { + memcpy((char *)Object->Colors, + (char *)ColorMap, ColorCount * sizeof(GifColorType)); + } + + return (Object); +} + +/* + * Free a color map object + */ +void +FreeMapObject(ColorMapObject * Object) { + + if (Object != NULL) { + free(Object->Colors); + free(Object); + /*** FIXME: + * When we are willing to break API we need to make this function + * FreeMapObject(ColorMapObject **Object) + * and do this assignment to NULL here: + * *Object = NULL; + */ + } +} + +#ifdef DEBUG +void +DumpColorMap(ColorMapObject * Object, + FILE * fp) { + + if (Object) { + int i, j, Len = Object->ColorCount; + + for (i = 0; i < Len; i += 4) { + for (j = 0; j < 4 && j < Len; j++) { + fprintf(fp, "%3d: %02x %02x %02x ", i + j, + Object->Colors[i + j].Red, + Object->Colors[i + j].Green, + Object->Colors[i + j].Blue); + } + fprintf(fp, "\n"); + } + } +} +#endif /* DEBUG */ + +/* + * Compute the union of two given color maps and return it. If result can't + * fit into 256 colors, NULL is returned, the allocated union otherwise. + * ColorIn1 is copied as is to ColorUnion, while colors from ColorIn2 are + * copied iff they didn't exist before. ColorTransIn2 maps the old + * ColorIn2 into ColorUnion color map table. + */ +ColorMapObject * +UnionColorMap(const ColorMapObject * ColorIn1, + const ColorMapObject * ColorIn2, + GifPixelType ColorTransIn2[]) { + + int i, j, CrntSlot, RoundUpTo, NewBitSize; + ColorMapObject *ColorUnion; + + /* + * Allocate table which will hold the result for sure. + */ + ColorUnion = MakeMapObject(MAX(ColorIn1->ColorCount, + ColorIn2->ColorCount) * 2, NULL); + + if (ColorUnion == NULL) + return (NULL); + + /* Copy ColorIn1 to ColorUnionSize; */ + /*** FIXME: What if there are duplicate entries into the colormap to begin + * with? */ + for (i = 0; i < ColorIn1->ColorCount; i++) + ColorUnion->Colors[i] = ColorIn1->Colors[i]; + CrntSlot = ColorIn1->ColorCount; + + /* + * Potentially obnoxious hack: + * + * Back CrntSlot down past all contiguous {0, 0, 0} slots at the end + * of table 1. This is very useful if your display is limited to + * 16 colors. + */ + while (ColorIn1->Colors[CrntSlot - 1].Red == 0 + && ColorIn1->Colors[CrntSlot - 1].Green == 0 + && ColorIn1->Colors[CrntSlot - 1].Blue == 0) + CrntSlot--; + + /* Copy ColorIn2 to ColorUnionSize (use old colors if they exist): */ + for (i = 0; i < ColorIn2->ColorCount && CrntSlot <= 256; i++) { + /* Let's see if this color already exists: */ + /*** FIXME: Will it ever occur that ColorIn2 will contain duplicate + * entries? So we should search from 0 to CrntSlot rather than + * ColorIn1->ColorCount? + */ + for (j = 0; j < ColorIn1->ColorCount; j++) + if (memcmp (&ColorIn1->Colors[j], &ColorIn2->Colors[i], + sizeof(GifColorType)) == 0) + break; + + if (j < ColorIn1->ColorCount) + ColorTransIn2[i] = j; /* color exists in Color1 */ + else { + /* Color is new - copy it to a new slot: */ + ColorUnion->Colors[CrntSlot] = ColorIn2->Colors[i]; + ColorTransIn2[i] = CrntSlot++; + } + } + + if (CrntSlot > 256) { + FreeMapObject(ColorUnion); + return ((ColorMapObject *) NULL); + } + + NewBitSize = BitSize(CrntSlot); + RoundUpTo = (1 << NewBitSize); + + if (RoundUpTo != ColorUnion->ColorCount) { + register GifColorType *Map = ColorUnion->Colors; + + /* + * Zero out slots up to next power of 2. + * We know these slots exist because of the way ColorUnion's + * start dimension was computed. + */ + for (j = CrntSlot; j < RoundUpTo; j++) + Map[j].Red = Map[j].Green = Map[j].Blue = 0; + + /* perhaps we can shrink the map? */ + if (RoundUpTo < ColorUnion->ColorCount) + ColorUnion->Colors = (GifColorType *)realloc(Map, + sizeof(GifColorType) * RoundUpTo); + } + + ColorUnion->ColorCount = RoundUpTo; + ColorUnion->BitsPerPixel = NewBitSize; + + return (ColorUnion); +} + +/* + * Apply a given color translation to the raster bits of an image + */ +void +ApplyTranslation(SavedImage * Image, + GifPixelType Translation[]) { + + register int i; + register int RasterSize = Image->ImageDesc.Height * Image->ImageDesc.Width; + + for (i = 0; i < RasterSize; i++) + Image->RasterBits[i] = Translation[Image->RasterBits[i]]; +} + +/****************************************************************************** + * Extension record functions + *****************************************************************************/ + +void +MakeExtension(SavedImage * New, + int Function) { + + New->Function = Function; + /*** FIXME: + * Someday we might have to deal with multiple extensions. + * ??? Was this a note from Gershon or from me? Does the multiple + * extension blocks solve this or do we need multiple Functions? Or is + * this an obsolete function? (People should use AddExtensionBlock + * instead?) + * Looks like AddExtensionBlock needs to take the int Function argument + * then it can take the place of this function. Right now people have to + * use both. Fix AddExtensionBlock and add this to the deprecation list. + */ +} + +int +AddExtensionBlock(SavedImage * New, + int Len, + unsigned char ExtData[]) { + + ExtensionBlock *ep; + + if (New->ExtensionBlocks == NULL) + New->ExtensionBlocks=(ExtensionBlock *)malloc(sizeof(ExtensionBlock)); + else + New->ExtensionBlocks = (ExtensionBlock *)realloc(New->ExtensionBlocks, + sizeof(ExtensionBlock) * + (New->ExtensionBlockCount + 1)); + + if (New->ExtensionBlocks == NULL) + return (GIF_ERROR); + + ep = &New->ExtensionBlocks[New->ExtensionBlockCount++]; + + ep->ByteCount=Len; + ep->Bytes = (char *)malloc(ep->ByteCount); + if (ep->Bytes == NULL) + return (GIF_ERROR); + + if (ExtData) { + memcpy(ep->Bytes, ExtData, Len); + ep->Function = New->Function; + } + + return (GIF_OK); +} + +void +FreeExtension(SavedImage * Image) +{ + ExtensionBlock *ep; + + if ((Image == NULL) || (Image->ExtensionBlocks == NULL)) { + return; + } + for (ep = Image->ExtensionBlocks; + ep < (Image->ExtensionBlocks + Image->ExtensionBlockCount); ep++) + (void)free((char *)ep->Bytes); + free((char *)Image->ExtensionBlocks); + Image->ExtensionBlocks = NULL; +} + +/****************************************************************************** + * Image block allocation functions +******************************************************************************/ + +/* Private Function: + * Frees the last image in the GifFile->SavedImages array + */ +void +FreeLastSavedImage(GifFileType *GifFile) { + + SavedImage *sp; + + if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) + return; + + /* Remove one SavedImage from the GifFile */ + GifFile->ImageCount--; + sp = &GifFile->SavedImages[GifFile->ImageCount]; + + /* Deallocate its Colormap */ + if (sp->ImageDesc.ColorMap) { + FreeMapObject(sp->ImageDesc.ColorMap); + sp->ImageDesc.ColorMap = NULL; + } + + /* Deallocate the image data */ + if (sp->RasterBits) + free((char *)sp->RasterBits); + + /* Deallocate any extensions */ + if (sp->ExtensionBlocks) + FreeExtension(sp); + + /*** FIXME: We could realloc the GifFile->SavedImages structure but is + * there a point to it? Saves some memory but we'd have to do it every + * time. If this is used in FreeSavedImages then it would be inefficient + * (The whole array is going to be deallocated.) If we just use it when + * we want to free the last Image it's convenient to do it here. + */ +} + +/* + * Append an image block to the SavedImages array + */ +SavedImage * +MakeSavedImage(GifFileType * GifFile, + const SavedImage * CopyFrom) { + + SavedImage *sp; + + if (GifFile->SavedImages == NULL) + GifFile->SavedImages = (SavedImage *)malloc(sizeof(SavedImage)); + else + GifFile->SavedImages = (SavedImage *)realloc(GifFile->SavedImages, + sizeof(SavedImage) * (GifFile->ImageCount + 1)); + + if (GifFile->SavedImages == NULL) + return ((SavedImage *)NULL); + else { + sp = &GifFile->SavedImages[GifFile->ImageCount++]; + memset((char *)sp, '\0', sizeof(SavedImage)); + + if (CopyFrom) { + memcpy((char *)sp, CopyFrom, sizeof(SavedImage)); + + /* + * Make our own allocated copies of the heap fields in the + * copied record. This guards against potential aliasing + * problems. + */ + + /* first, the local color map */ + if (sp->ImageDesc.ColorMap) { + sp->ImageDesc.ColorMap = MakeMapObject( + CopyFrom->ImageDesc.ColorMap->ColorCount, + CopyFrom->ImageDesc.ColorMap->Colors); + if (sp->ImageDesc.ColorMap == NULL) { + FreeLastSavedImage(GifFile); + return (SavedImage *)(NULL); + } + } + + /* next, the raster */ + sp->RasterBits = (unsigned char *)malloc(sizeof(GifPixelType) * + CopyFrom->ImageDesc.Height * + CopyFrom->ImageDesc.Width); + if (sp->RasterBits == NULL) { + FreeLastSavedImage(GifFile); + return (SavedImage *)(NULL); + } + memcpy(sp->RasterBits, CopyFrom->RasterBits, + sizeof(GifPixelType) * CopyFrom->ImageDesc.Height * + CopyFrom->ImageDesc.Width); + + /* finally, the extension blocks */ + if (sp->ExtensionBlocks) { + sp->ExtensionBlocks = (ExtensionBlock *)malloc( + sizeof(ExtensionBlock) * + CopyFrom->ExtensionBlockCount); + if (sp->ExtensionBlocks == NULL) { + FreeLastSavedImage(GifFile); + return (SavedImage *)(NULL); + } + memcpy(sp->ExtensionBlocks, CopyFrom->ExtensionBlocks, + sizeof(ExtensionBlock) * CopyFrom->ExtensionBlockCount); + + /* + * For the moment, the actual blocks can take their + * chances with free(). We'll fix this later. + *** FIXME: [Better check this out... Toshio] + * 2004 May 27: Looks like this was an ESR note. + * It means the blocks are shallow copied from InFile to + * OutFile. However, I don't see that in this code.... + * Did ESR fix it but never remove this note (And other notes + * in gifspnge?) + */ + } + } + + return (sp); + } +} + +void +FreeSavedImages(GifFileType * GifFile) { + + SavedImage *sp; + + if ((GifFile == NULL) || (GifFile->SavedImages == NULL)) { + return; + } + for (sp = GifFile->SavedImages; + sp < GifFile->SavedImages + GifFile->ImageCount; sp++) { + if (sp->ImageDesc.ColorMap) { + FreeMapObject(sp->ImageDesc.ColorMap); + sp->ImageDesc.ColorMap = NULL; + } + + if (sp->RasterBits) + free((char *)sp->RasterBits); + + if (sp->ExtensionBlocks) + FreeExtension(sp); + } + free((char *)GifFile->SavedImages); + GifFile->SavedImages=NULL; +} diff --git a/Engine/lib/opcode/Ice/IceAABB.cpp b/Engine/lib/opcode/Ice/IceAABB.cpp new file mode 100644 index 000000000..1955c48a3 --- /dev/null +++ b/Engine/lib/opcode/Ice/IceAABB.cpp @@ -0,0 +1,404 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains AABB-related code. + * \file IceAABB.cpp + * \author Pierre Terdiman + * \date January, 29, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * AABB class. + * \class AABB + * \author Pierre Terdiman + * \version 1.0 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "../Opcode.h" + +using namespace IceMaths; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the sum of two AABBs. + * \param aabb [in] the other AABB + * \return Self-Reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABB& AABB::Add(const AABB& aabb) +{ + // Compute new min & max values + Point Min; GetMin(Min); + Point Tmp; aabb.GetMin(Tmp); + Min.Min(Tmp); + + Point Max; GetMax(Max); + aabb.GetMax(Tmp); + Max.Max(Tmp); + + // Update this + SetMinMax(Min, Max); + return *this; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Makes a cube from the AABB. + * \param cube [out] the cube AABB + * \return cube edge length + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float AABB::MakeCube(AABB& cube) const +{ + Point Ext; GetExtents(Ext); + float Max = Ext.Max(); + + Point Cnt; GetCenter(Cnt); + cube.SetCenterExtents(Cnt, Point(Max, Max, Max)); + return Max; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Makes a sphere from the AABB. + * \param sphere [out] sphere containing the AABB + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABB::MakeSphere(Sphere& sphere) const +{ + GetExtents(sphere.mCenter); + sphere.mRadius = sphere.mCenter.Magnitude() * 1.00001f; // To make sure sphere::Contains(*this) succeeds + GetCenter(sphere.mCenter); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks a box is inside another box. + * \param box [in] the other AABB + * \return true if current box is inside input box + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABB::IsInside(const AABB& box) const +{ + if(box.GetMin(0)>GetMin(0)) return false; + if(box.GetMin(1)>GetMin(1)) return false; + if(box.GetMin(2)>GetMin(2)) return false; + if(box.GetMax(0) max.x) ? 2 : 0) // 2 = right + + ((local_eye.y < min.y) ? 4 : 0) // 4 = bottom + + ((local_eye.y > max.y) ? 8 : 0) // 8 = top + + ((local_eye.z < min.z) ? 16 : 0) // 16 = front + + ((local_eye.z > max.z) ? 32 : 0); // 32 = back + + // Look up number of vertices in outline + num = (sdword)gIndexList[pos][7]; + // Zero indicates invalid case + if(!num) return null; + + return &gIndexList[pos][0]; +} + +// calculateBoxArea: computes the screen-projected 2D area of an oriented 3D bounding box + +//const Point& eye, //eye point (in bbox object coordinates) +//const AABB& box, //3d bbox +//const Matrix4x4& mat, //free transformation for bbox +//float width, float height, int& num) +float AABB::ComputeBoxArea(const Point& eye, const Matrix4x4& mat, float width, float height, sdword& num) const +{ + const sbyte* Outline = ComputeOutline(eye, num); + if(!Outline) return -1.0f; + + // Compute box vertices + Point vertexBox[8], dst[8]; + ComputePoints(vertexBox); + + // Transform all outline corners into 2D screen space + for(sdword i=0;i GetMax(0) || p.x < GetMin(0)) return FALSE; \ + if(p.y > GetMax(1) || p.y < GetMin(1)) return FALSE; \ + if(p.z > GetMax(2) || p.z < GetMin(2)) return FALSE; \ + return TRUE; \ + } + + enum AABBType + { + AABB_RENDER = 0, //!< AABB used for rendering. Not visible == not rendered. + AABB_UPDATE = 1, //!< AABB used for dynamic updates. Not visible == not updated. + + AABB_FORCE_DWORD = 0x7fffffff, + }; + +#ifdef USE_MINMAX + + struct ICEMATHS_API ShadowAABB + { + Point mMin; + Point mMax; + }; + + class ICEMATHS_API AABB + { + public: + //! Constructor + inline_ AABB() {} + //! Destructor + inline_ ~AABB() {} + + //! Type-independent methods + AABB_COMMON_METHODS; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups an AABB from min & max vectors. + * \param min [in] the min point + * \param max [in] the max point + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetMinMax(const Point& min, const Point& max) { mMin = min; mMax = max; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups an AABB from center & extents vectors. + * \param c [in] the center point + * \param e [in] the extents vector + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetCenterExtents(const Point& c, const Point& e) { mMin = c - e; mMax = c + e; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups an empty AABB. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetEmpty() { Point p(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); mMin = -p; mMax = p;} + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups a point AABB. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetPoint(const Point& pt) { mMin = mMax = pt; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the size of the AABB. The size is defined as the longest extent. + * \return the size of the AABB + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + float GetSize() const { Point e; GetExtents(e); return e.Max(); } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Extends the AABB. + * \param p [in] the next point + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void Extend(const Point& p) + { + if(p.x > mMax.x) mMax.x = p.x; + if(p.x < mMin.x) mMin.x = p.x; + + if(p.y > mMax.y) mMax.y = p.y; + if(p.y < mMin.y) mMin.y = p.y; + + if(p.z > mMax.z) mMax.z = p.z; + if(p.z < mMin.z) mMin.z = p.z; + } + // Data access + + //! Get min point of the box + inline_ void GetMin(Point& min) const { min = mMin; } + //! Get max point of the box + inline_ void GetMax(Point& max) const { max = mMax; } + + //! Get component of the box's min point along a given axis + inline_ float GetMin(udword axis) const { return mMin[axis]; } + //! Get component of the box's max point along a given axis + inline_ float GetMax(udword axis) const { return mMax[axis]; } + + //! Get box center + inline_ void GetCenter(Point& center) const { center = (mMax + mMin)*0.5f; } + //! Get box extents + inline_ void GetExtents(Point& extents) const { extents = (mMax - mMin)*0.5f; } + + //! Get component of the box's center along a given axis + inline_ float GetCenter(udword axis) const { return (mMax[axis] + mMin[axis])*0.5f; } + //! Get component of the box's extents along a given axis + inline_ float GetExtents(udword axis) const { return (mMax[axis] - mMin[axis])*0.5f; } + + //! Get box diagonal + inline_ void GetDiagonal(Point& diagonal) const { diagonal = mMax - mMin; } + inline_ float GetWidth() const { return mMax.x - mMin.x; } + inline_ float GetHeight() const { return mMax.y - mMin.y; } + inline_ float GetDepth() const { return mMax.z - mMin.z; } + + //! Volume + inline_ float GetVolume() const { return GetWidth() * GetHeight() * GetDepth(); } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes the intersection between two AABBs. + * \param a [in] the other AABB + * \return true on intersection + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL Intersect(const AABB& a) const + { + if(mMax.x < a.mMin.x + || a.mMax.x < mMin.x + || mMax.y < a.mMin.y + || a.mMax.y < mMin.y + || mMax.z < a.mMin.z + || a.mMax.z < mMin.z) return FALSE; + + return TRUE; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes the 1D-intersection between two AABBs, on a given axis. + * \param a [in] the other AABB + * \param axis [in] the axis (0, 1, 2) + * \return true on intersection + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL Intersect(const AABB& a, udword axis) const + { + if(mMax[axis] < a.mMin[axis] || a.mMax[axis] < mMin[axis]) return FALSE; + return TRUE; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Recomputes the AABB after an arbitrary transform by a 4x4 matrix. + * Original code by Charles Bloom on the GD-Algorithm list. (I slightly modified it) + * \param mtx [in] the transform matrix + * \param aabb [out] the transformed AABB [can be *this] + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void Rotate(const Matrix4x4& mtx, AABB& aabb) const + { + // The three edges transformed: you can efficiently transform an X-only vector + // by just getting the "X" column of the matrix + Point vx,vy,vz; + mtx.GetRow(0, vx); vx *= (mMax.x - mMin.x); + mtx.GetRow(1, vy); vy *= (mMax.y - mMin.y); + mtx.GetRow(2, vz); vz *= (mMax.z - mMin.z); + + // Transform the min point + aabb.mMin = aabb.mMax = mMin * mtx; + + // Take the transformed min & axes and find new extents + // Using CPU code in the right place is faster... + if(IS_NEGATIVE_FLOAT(vx.x)) aabb.mMin.x += vx.x; else aabb.mMax.x += vx.x; + if(IS_NEGATIVE_FLOAT(vx.y)) aabb.mMin.y += vx.y; else aabb.mMax.y += vx.y; + if(IS_NEGATIVE_FLOAT(vx.z)) aabb.mMin.z += vx.z; else aabb.mMax.z += vx.z; + if(IS_NEGATIVE_FLOAT(vy.x)) aabb.mMin.x += vy.x; else aabb.mMax.x += vy.x; + if(IS_NEGATIVE_FLOAT(vy.y)) aabb.mMin.y += vy.y; else aabb.mMax.y += vy.y; + if(IS_NEGATIVE_FLOAT(vy.z)) aabb.mMin.z += vy.z; else aabb.mMax.z += vy.z; + if(IS_NEGATIVE_FLOAT(vz.x)) aabb.mMin.x += vz.x; else aabb.mMax.x += vz.x; + if(IS_NEGATIVE_FLOAT(vz.y)) aabb.mMin.y += vz.y; else aabb.mMax.y += vz.y; + if(IS_NEGATIVE_FLOAT(vz.z)) aabb.mMin.z += vz.z; else aabb.mMax.z += vz.z; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks the AABB is valid. + * \return true if the box is valid + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL IsValid() const + { + // Consistency condition for (Min, Max) boxes: min < max + if(mMin.x > mMax.x) return FALSE; + if(mMin.y > mMax.y) return FALSE; + if(mMin.z > mMax.z) return FALSE; + return TRUE; + } + + //! Operator for AABB *= float. Scales the extents, keeps same center. + inline_ AABB& operator*=(float s) + { + Point Center; GetCenter(Center); + Point Extents; GetExtents(Extents); + SetCenterExtents(Center, Extents * s); + return *this; + } + + //! Operator for AABB /= float. Scales the extents, keeps same center. + inline_ AABB& operator/=(float s) + { + Point Center; GetCenter(Center); + Point Extents; GetExtents(Extents); + SetCenterExtents(Center, Extents / s); + return *this; + } + + //! Operator for AABB += Point. Translates the box. + inline_ AABB& operator+=(const Point& trans) + { + mMin+=trans; + mMax+=trans; + return *this; + } + private: + Point mMin; //!< Min point + Point mMax; //!< Max point + }; + +#else + + class ICEMATHS_API AABB + { + public: + //! Constructor + inline_ AABB() {} + //! Destructor + inline_ ~AABB() {} + + //! Type-independent methods + AABB_COMMON_METHODS; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups an AABB from min & max vectors. + * \param min [in] the min point + * \param max [in] the max point + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetMinMax(const Point& min, const Point& max) { mCenter = (max + min)*0.5f; mExtents = (max - min)*0.5f; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups an AABB from center & extents vectors. + * \param c [in] the center point + * \param e [in] the extents vector + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetCenterExtents(const Point& c, const Point& e) { mCenter = c; mExtents = e; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups an empty AABB. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetEmpty() { mCenter.Zero(); mExtents.Set(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT);} + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups a point AABB. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetPoint(const Point& pt) { mCenter = pt; mExtents.Zero(); } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the size of the AABB. The size is defined as the longest extent. + * \return the size of the AABB + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + float GetSize() const { return mExtents.Max(); } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Extends the AABB. + * \param p [in] the next point + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void Extend(const Point& p) + { + Point Max = mCenter + mExtents; + Point Min = mCenter - mExtents; + + if(p.x > Max.x) Max.x = p.x; + if(p.x < Min.x) Min.x = p.x; + + if(p.y > Max.y) Max.y = p.y; + if(p.y < Min.y) Min.y = p.y; + + if(p.z > Max.z) Max.z = p.z; + if(p.z < Min.z) Min.z = p.z; + + SetMinMax(Min, Max); + } + // Data access + + //! Get min point of the box + inline_ void GetMin(Point& min) const { min = mCenter - mExtents; } + //! Get max point of the box + inline_ void GetMax(Point& max) const { max = mCenter + mExtents; } + + //! Get component of the box's min point along a given axis + inline_ float GetMin(udword axis) const { return mCenter[axis] - mExtents[axis]; } + //! Get component of the box's max point along a given axis + inline_ float GetMax(udword axis) const { return mCenter[axis] + mExtents[axis]; } + + //! Get box center + inline_ void GetCenter(Point& center) const { center = mCenter; } + //! Get box extents + inline_ void GetExtents(Point& extents) const { extents = mExtents; } + + //! Get component of the box's center along a given axis + inline_ float GetCenter(udword axis) const { return mCenter[axis]; } + //! Get component of the box's extents along a given axis + inline_ float GetExtents(udword axis) const { return mExtents[axis]; } + + //! Get box diagonal + inline_ void GetDiagonal(Point& diagonal) const { diagonal = mExtents * 2.0f; } + inline_ float GetWidth() const { return mExtents.x * 2.0f; } + inline_ float GetHeight() const { return mExtents.y * 2.0f; } + inline_ float GetDepth() const { return mExtents.z * 2.0f; } + + //! Volume + inline_ float GetVolume() const { return mExtents.x * mExtents.y * mExtents.z * 8.0f; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes the intersection between two AABBs. + * \param a [in] the other AABB + * \return true on intersection + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL Intersect(const AABB& a) const + { + float tx = mCenter.x - a.mCenter.x; float ex = a.mExtents.x + mExtents.x; if(AIR(tx) > IR(ex)) return FALSE; + float ty = mCenter.y - a.mCenter.y; float ey = a.mExtents.y + mExtents.y; if(AIR(ty) > IR(ey)) return FALSE; + float tz = mCenter.z - a.mCenter.z; float ez = a.mExtents.z + mExtents.z; if(AIR(tz) > IR(ez)) return FALSE; + return TRUE; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * The standard intersection method from Gamasutra. Just here to check its speed against the one above. + * \param a [in] the other AABB + * \return true on intersection + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ bool GomezIntersect(const AABB& a) + { + Point T = mCenter - a.mCenter; // Vector from A to B + return ((fabsf(T.x) <= (a.mExtents.x + mExtents.x)) + && (fabsf(T.y) <= (a.mExtents.y + mExtents.y)) + && (fabsf(T.z) <= (a.mExtents.z + mExtents.z))); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes the 1D-intersection between two AABBs, on a given axis. + * \param a [in] the other AABB + * \param axis [in] the axis (0, 1, 2) + * \return true on intersection + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL Intersect(const AABB& a, udword axis) const + { + float t = mCenter[axis] - a.mCenter[axis]; + float e = a.mExtents[axis] + mExtents[axis]; + if(AIR(t) > IR(e)) return FALSE; + return TRUE; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Recomputes the AABB after an arbitrary transform by a 4x4 matrix. + * \param mtx [in] the transform matrix + * \param aabb [out] the transformed AABB [can be *this] + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void Rotate(const Matrix4x4& mtx, AABB& aabb) const + { + // Compute new center + aabb.mCenter = mCenter * mtx; + + // Compute new extents. FPU code & CPU code have been interleaved for improved performance. + Point Ex(mtx.m[0][0] * mExtents.x, mtx.m[0][1] * mExtents.x, mtx.m[0][2] * mExtents.x); + IR(Ex.x)&=0x7fffffff; IR(Ex.y)&=0x7fffffff; IR(Ex.z)&=0x7fffffff; + + Point Ey(mtx.m[1][0] * mExtents.y, mtx.m[1][1] * mExtents.y, mtx.m[1][2] * mExtents.y); + IR(Ey.x)&=0x7fffffff; IR(Ey.y)&=0x7fffffff; IR(Ey.z)&=0x7fffffff; + + Point Ez(mtx.m[2][0] * mExtents.z, mtx.m[2][1] * mExtents.z, mtx.m[2][2] * mExtents.z); + IR(Ez.x)&=0x7fffffff; IR(Ez.y)&=0x7fffffff; IR(Ez.z)&=0x7fffffff; + + aabb.mExtents.x = Ex.x + Ey.x + Ez.x; + aabb.mExtents.y = Ex.y + Ey.y + Ez.y; + aabb.mExtents.z = Ex.z + Ey.z + Ez.z; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks the AABB is valid. + * \return true if the box is valid + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL IsValid() const + { + // Consistency condition for (Center, Extents) boxes: Extents >= 0 + if(IS_NEGATIVE_FLOAT(mExtents.x)) return FALSE; + if(IS_NEGATIVE_FLOAT(mExtents.y)) return FALSE; + if(IS_NEGATIVE_FLOAT(mExtents.z)) return FALSE; + return TRUE; + } + + //! Operator for AABB *= float. Scales the extents, keeps same center. + inline_ AABB& operator*=(float s) { mExtents*=s; return *this; } + + //! Operator for AABB /= float. Scales the extents, keeps same center. + inline_ AABB& operator/=(float s) { mExtents/=s; return *this; } + + //! Operator for AABB += Point. Translates the box. + inline_ AABB& operator+=(const Point& trans) + { + mCenter+=trans; + return *this; + } + private: + Point mCenter; //!< AABB Center + Point mExtents; //!< x, y and z extents + }; + +#endif + + inline_ void ComputeMinMax(const Point& p, Point& min, Point& max) + { + if(p.x > max.x) max.x = p.x; + if(p.x < min.x) min.x = p.x; + + if(p.y > max.y) max.y = p.y; + if(p.y < min.y) min.y = p.y; + + if(p.z > max.z) max.z = p.z; + if(p.z < min.z) min.z = p.z; + } + + inline_ void ComputeAABB(AABB& aabb, const Point* list, udword nb_pts) + { + if(list) + { + Point Maxi(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); + Point Mini(MAX_FLOAT, MAX_FLOAT, MAX_FLOAT); + while(nb_pts--) + { +// _prefetch(list+1); // off by one ? + ComputeMinMax(*list++, Mini, Maxi); + } + aabb.SetMinMax(Mini, Maxi); + } + } + +#endif // __ICEAABB_H__ diff --git a/Engine/lib/opcode/Ice/IceAxes.h b/Engine/lib/opcode/Ice/IceAxes.h new file mode 100644 index 000000000..842b55e02 --- /dev/null +++ b/Engine/lib/opcode/Ice/IceAxes.h @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains axes definition. + * \file IceAxes.h + * \author Pierre Terdiman + * \date January, 29, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEAXES_H__ +#define __ICEAXES_H__ + + enum PointComponent + { + _X = 0, + _Y = 1, + _Z = 2, + _W = 3, + + _FORCE_DWORD = 0x7fffffff + }; + + enum AxisOrder + { + AXES_XYZ = (_X)|(_Y<<2)|(_Z<<4), + AXES_XZY = (_X)|(_Z<<2)|(_Y<<4), + AXES_YXZ = (_Y)|(_X<<2)|(_Z<<4), + AXES_YZX = (_Y)|(_Z<<2)|(_X<<4), + AXES_ZXY = (_Z)|(_X<<2)|(_Y<<4), + AXES_ZYX = (_Z)|(_Y<<2)|(_X<<4), + + AXES_FORCE_DWORD = 0x7fffffff + }; + + class ICEMATHS_API Axes + { + public: + + inline_ Axes(AxisOrder order) + { + mAxis0 = (order ) & 3; + mAxis1 = (order>>2) & 3; + mAxis2 = (order>>4) & 3; + } + inline_ ~Axes() {} + + udword mAxis0; + udword mAxis1; + udword mAxis2; + }; + +#endif // __ICEAXES_H__ diff --git a/Engine/lib/opcode/Ice/IceBoundingSphere.h b/Engine/lib/opcode/Ice/IceBoundingSphere.h new file mode 100644 index 000000000..945d38cf8 --- /dev/null +++ b/Engine/lib/opcode/Ice/IceBoundingSphere.h @@ -0,0 +1,142 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code to compute the minimal bounding sphere. + * \file IceBoundingSphere.h + * \author Pierre Terdiman + * \date January, 29, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEBOUNDINGSPHERE_H__ +#define __ICEBOUNDINGSPHERE_H__ + + enum BSphereMethod + { + BS_NONE, + BS_GEMS, + BS_MINIBALL, + + BS_FORCE_DWORD = 0x7fffffff + }; + + class ICEMATHS_API Sphere + { + public: + //! Constructor + inline_ Sphere() {} + //! Constructor + inline_ Sphere(const Point& center, float radius) : mCenter(center), mRadius(radius) {} + //! Constructor + Sphere(udword nb_verts, const Point* verts); + //! Copy constructor + inline_ Sphere(const Sphere& sphere) : mCenter(sphere.mCenter), mRadius(sphere.mRadius) {} + //! Destructor + inline_ ~Sphere() {} + + BSphereMethod Compute(udword nb_verts, const Point* verts); + bool FastCompute(udword nb_verts, const Point* verts); + + // Access methods + inline_ const Point& GetCenter() const { return mCenter; } + inline_ float GetRadius() const { return mRadius; } + + inline_ const Point& Center() const { return mCenter; } + inline_ float Radius() const { return mRadius; } + + inline_ Sphere& Set(const Point& center, float radius) { mCenter = center; mRadius = radius; return *this; } + inline_ Sphere& SetCenter(const Point& center) { mCenter = center; return *this; } + inline_ Sphere& SetRadius(float radius) { mRadius = radius; return *this; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Tests if a point is contained within the sphere. + * \param p [in] the point to test + * \return true if inside the sphere + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ bool Contains(const Point& p) const + { + return mCenter.SquareDistance(p) <= mRadius*mRadius; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Tests if a sphere is contained within the sphere. + * \param sphere [in] the sphere to test + * \return true if inside the sphere + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ bool Contains(const Sphere& sphere) const + { + // If our radius is the smallest, we can't possibly contain the other sphere + if(mRadius < sphere.mRadius) return false; + // So r is always positive or null now + float r = mRadius - sphere.mRadius; + return mCenter.SquareDistance(sphere.mCenter) <= r*r; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Tests if a box is contained within the sphere. + * \param aabb [in] the box to test + * \return true if inside the sphere + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL Contains(const AABB& aabb) const + { + // I assume if all 8 box vertices are inside the sphere, so does the whole box. + // Sounds ok but maybe there's a better way? + float R2 = mRadius * mRadius; +#ifdef USE_MIN_MAX + const Point& Max = ((ShadowAABB&)&aabb).mMax; + const Point& Min = ((ShadowAABB&)&aabb).mMin; +#else + Point Max; aabb.GetMax(Max); + Point Min; aabb.GetMin(Min); +#endif + Point p; + p.x=Max.x; p.y=Max.y; p.z=Max.z; if(mCenter.SquareDistance(p)>=R2) return FALSE; + p.x=Min.x; if(mCenter.SquareDistance(p)>=R2) return FALSE; + p.x=Max.x; p.y=Min.y; if(mCenter.SquareDistance(p)>=R2) return FALSE; + p.x=Min.x; if(mCenter.SquareDistance(p)>=R2) return FALSE; + p.x=Max.x; p.y=Max.y; p.z=Min.z; if(mCenter.SquareDistance(p)>=R2) return FALSE; + p.x=Min.x; if(mCenter.SquareDistance(p)>=R2) return FALSE; + p.x=Max.x; p.y=Min.y; if(mCenter.SquareDistance(p)>=R2) return FALSE; + p.x=Min.x; if(mCenter.SquareDistance(p)>=R2) return FALSE; + + return TRUE; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Tests if the sphere intersects another sphere + * \param sphere [in] the other sphere + * \return true if spheres overlap + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ bool Intersect(const Sphere& sphere) const + { + float r = mRadius + sphere.mRadius; + return mCenter.SquareDistance(sphere.mCenter) <= r*r; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks the sphere is valid. + * \return true if the box is valid + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL IsValid() const + { + // Consistency condition for spheres: Radius >= 0.0f + if(mRadius < 0.0f) return FALSE; + return TRUE; + } + public: + Point mCenter; //!< Sphere center + float mRadius; //!< Sphere radius + }; + +#endif // __ICEBOUNDINGSPHERE_H__ diff --git a/Engine/lib/opcode/Ice/IceContainer.cpp b/Engine/lib/opcode/Ice/IceContainer.cpp new file mode 100644 index 000000000..daebbb68b --- /dev/null +++ b/Engine/lib/opcode/Ice/IceContainer.cpp @@ -0,0 +1,344 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a simple container class. + * \file IceContainer.cpp + * \author Pierre Terdiman + * \date February, 5, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a list of 32-bits values. + * Use this class when you need to store an unknown number of values. The list is automatically + * resized and can contains 32-bits entities (dwords or floats) + * + * \class OPC_Container + * \author Pierre Terdiman + * \version 1.0 + * \date 08.15.98 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "../Opcode.h" + +using namespace IceCore; + +// Static members +#ifdef CONTAINER_STATS +udword OPC_Container::mNbContainers = 0; +udword OPC_Container::mUsedRam = 0; +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. No entries allocated there. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +OPC_Container::OPC_Container() : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGrowthFactor(2.0f) +{ +#ifdef CONTAINER_STATS + mNbContainers++; + mUsedRam+=sizeof(OPC_Container); +#endif +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. Also allocates a given number of entries. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +OPC_Container::OPC_Container(udword size, float growth_factor) : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGrowthFactor(growth_factor) +{ +#ifdef CONTAINER_STATS + mNbContainers++; + mUsedRam+=sizeof(OPC_Container); +#endif + SetSize(size); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Copy constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +OPC_Container::OPC_Container(const OPC_Container& object) : mMaxNbEntries(0), mCurNbEntries(0), mEntries(null), mGrowthFactor(2.0f) +{ +#ifdef CONTAINER_STATS + mNbContainers++; + mUsedRam+=sizeof(OPC_Container); +#endif + *this = object; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. Frees everything and leaves. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +OPC_Container::~OPC_Container() +{ + Empty(); +#ifdef CONTAINER_STATS + mNbContainers--; + mUsedRam-=GetUsedRam(); +#endif +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Clears the container. All stored values are deleted, and it frees used ram. + * \see Reset() + * \return Self-Reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +OPC_Container& OPC_Container::Empty() +{ +#ifdef CONTAINER_STATS + mUsedRam-=mMaxNbEntries*sizeof(udword); +#endif + DELETEARRAY(mEntries); + mCurNbEntries = mMaxNbEntries = 0; + return *this; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Resizes the container. + * \param needed [in] assume the container can be added at least "needed" values + * \return true if success. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool OPC_Container::Resize(udword needed) +{ +#ifdef CONTAINER_STATS + // Subtract previous amount of bytes + mUsedRam-=mMaxNbEntries*sizeof(udword); +#endif + + // Get more entries + mMaxNbEntries = mMaxNbEntries ? udword(float(mMaxNbEntries)*mGrowthFactor) : 2; // Default nb Entries = 2 + if(mMaxNbEntriesmMaxNbEntries) Resize(nb); + + // Add new entry + CopyMemory(&mEntries[mCurNbEntries], entries, nb*sizeof(udword)); + mCurNbEntries+=nb; + return *this; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * A O(1) method to add a value in the container. The container is automatically resized if needed. + * The method is inline, not the resize. The call overhead happens on resizes only, which is not a problem since the resizing operation + * costs a lot more than the call overhead... + * + * \param entry [in] a float to store in the container + * \see Add(udword entry) + * \see Empty() + * \see Contains(udword entry) + * \return Self-Reference + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ OPC_Container& Add(float entry) + { + // Resize if needed + if(mCurNbEntries==mMaxNbEntries) Resize(); + + // Add new entry + mEntries[mCurNbEntries++] = IR(entry); + return *this; + } + + inline_ OPC_Container& Add(const float* entries, udword nb) + { + // Resize if needed + if(mCurNbEntries+nb>mMaxNbEntries) Resize(nb); + + // Add new entry + CopyMemory(&mEntries[mCurNbEntries], entries, nb*sizeof(float)); + mCurNbEntries+=nb; + return *this; + } + + //! Add unique [slow] + inline_ OPC_Container& AddUnique(udword entry) + { + if(!Contains(entry)) Add(entry); + return *this; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Clears the container. All stored values are deleted, and it frees used ram. + * \see Reset() + * \return Self-Reference + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + OPC_Container& Empty(); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Resets the container. Stored values are discarded but the buffer is kept so that further calls don't need resizing again. + * That's a kind of temporal coherence. + * \see Empty() + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void Reset() + { + // Avoid the write if possible + // ### CMOV + if(mCurNbEntries) mCurNbEntries = 0; + } + + // HANDLE WITH CARE + inline_ void ForceSize(udword size) + { + mCurNbEntries = size; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Sets the initial size of the container. If it already contains something, it's discarded. + * \param nb [in] Number of entries + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool SetSize(udword nb); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Refits the container and get rid of unused bytes. + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool Refit(); + + // Checks whether the container already contains a given value. + bool Contains(udword entry, udword* location=null) const; + // Deletes an entry - doesn't preserve insertion order. + bool Delete(udword entry); + // Deletes an entry - does preserve insertion order. + bool DeleteKeepingOrder(udword entry); + //! Deletes the very last entry. + inline_ void DeleteLastEntry() { if(mCurNbEntries) mCurNbEntries--; } + //! Deletes the entry whose index is given + inline_ void DeleteIndex(udword index) { mEntries[index] = mEntries[--mCurNbEntries]; } + + // Helpers + OPC_Container& FindNext(udword& entry, FindMode find_mode=FIND_CLAMP); + OPC_Container& FindPrev(udword& entry, FindMode find_mode=FIND_CLAMP); + // Data access. + inline_ udword GetNbEntries() const { return mCurNbEntries; } //!< Returns the current number of entries. + inline_ udword GetEntry(udword i) const { return mEntries[i]; } //!< Returns ith entry + inline_ udword* GetEntries() const { return mEntries; } //!< Returns the list of entries. + + inline_ udword GetFirst() const { return mEntries[0]; } + inline_ udword GetLast() const { return mEntries[mCurNbEntries-1]; } + + // Growth control + inline_ float GetGrowthFactor() const { return mGrowthFactor; } //!< Returns the growth factor + inline_ void SetGrowthFactor(float growth) { mGrowthFactor = growth; } //!< Sets the growth factor + inline_ bool IsFull() const { return mCurNbEntries==mMaxNbEntries; } //!< Checks the container is full + inline_ BOOL IsNotEmpty() const { return mCurNbEntries; } //!< Checks the container is empty + + //! Read-access as an array + inline_ udword operator[](udword i) const { ASSERT(i>=0 && i=0 && i +#endif + + #define SIGN_BITMASK 0x80000000 + + //! Integer representation of a floating-point value. + #define IR(x) ((udword&)(x)) + + //! Signed integer representation of a floating-point value. + #define SIR(x) ((sdword&)(x)) + + //! Absolute integer representation of a floating-point value + #define AIR(x) (IR(x)&0x7fffffff) + + //! Floating-point representation of an integer value. + #define FR(x) ((float&)(x)) + + //! Integer-based comparison of a floating point value. + //! Don't use it blindly, it can be faster or slower than the FPU comparison, depends on the context. + #define IS_NEGATIVE_FLOAT(x) (IR(x)&0x80000000) + + //! Fast fabs for floating-point values. It just clears the sign bit. + //! Don't use it blindy, it can be faster or slower than the FPU comparison, depends on the context. + inline_ float FastFabs(float x) + { + udword FloatBits = IR(x)&0x7fffffff; + return FR(FloatBits); + } + + //! Fast square root for floating-point values. + inline_ float FastSqrt(float square) + { +#if defined( PLATFORM_WINDOWS ) + float retval; + + __asm { + mov eax, square + sub eax, 0x3F800000 + sar eax, 1 + add eax, 0x3F800000 + mov [retval], eax + } + return retval; +#elif defined( PLATFORM_XENON ) + return __fsqrts( square ); +#else + return( sqrtf( square ) ); +#endif + } + + //! Saturates positive to zero. + inline_ float fsat(float f) + { + udword y = (udword&)f & ~((sdword&)f >>31); + return (float&)y; + } + + //! Computes 1.0f / sqrtf(x). + inline_ float frsqrt(float f) + { + float x = f * 0.5f; + udword y = 0x5f3759df - ((udword&)f >> 1); + // Iteration... + (float&)y = (float&)y * ( 1.5f - ( x * (float&)y * (float&)y ) ); + // Result + return (float&)y; + } + + //! Computes 1.0f / sqrtf(x). Comes from NVIDIA. + inline_ float InvSqrt(const float& x) + { + udword tmp = (udword(IEEE_1_0 << 1) + IEEE_1_0 - *(udword*)&x) >> 1; + float y = *(float*)&tmp; + return y * (1.47f - 0.47f * x * y * y); + } + + //! Computes 1.0f / sqrtf(x). Comes from Quake3. Looks like the first one I had above. + //! See http://www.magic-software.com/3DGEDInvSqrt.html + inline_ float RSqrt(float number) + { + long i; + float x2, y; + const float threehalfs = 1.5f; + + x2 = number * 0.5f; + y = number; + i = * (long *) &y; + i = 0x5f3759df - (i >> 1); + y = * (float *) &i; + y = y * (threehalfs - (x2 * y * y)); + + return y; + } + + //! TO BE DOCUMENTED + inline_ float fsqrt(float f) + { + udword y = ( ( (sdword&)f - 0x3f800000 ) >> 1 ) + 0x3f800000; + // Iteration...? + // (float&)y = (3.0f - ((float&)y * (float&)y) / f) * (float&)y * 0.5f; + // Result + return (float&)y; + } + + //! Returns the float ranged espilon value. + inline_ float fepsilon(float f) + { + udword b = (udword&)f & 0xff800000; + udword a = b | 0x00000001; + (float&)a -= (float&)b; + // Result + return (float&)a; + } + + //! Is the float valid ? + inline_ bool IsNAN(float value) { return (IR(value)&0x7f800000) == 0x7f800000; } + inline_ bool IsIndeterminate(float value) { return IR(value) == 0xffc00000; } + inline_ bool IsPlusInf(float value) { return IR(value) == 0x7f800000; } + inline_ bool IsMinusInf(float value) { return IR(value) == 0xff800000; } + + inline_ bool IsValidFloat(float value) + { + if(IsNAN(value)) return false; + if(IsIndeterminate(value)) return false; + if(IsPlusInf(value)) return false; + if(IsMinusInf(value)) return false; + return true; + } + + #define CHECK_VALID_FLOAT(x) ASSERT(IsValidFloat(x)); + +/* + //! FPU precision setting function. + inline_ void SetFPU() + { + // This function evaluates whether the floating-point + // control word is set to single precision/round to nearest/ + // exceptions disabled. If these conditions don't hold, the + // function changes the control word to set them and returns + // TRUE, putting the old control word value in the passback + // location pointed to by pwOldCW. + { + uword wTemp, wSave; + + __asm fstcw wSave + if (wSave & 0x300 || // Not single mode + 0x3f != (wSave & 0x3f) || // Exceptions enabled + wSave & 0xC00) // Not round to nearest mode + { + __asm + { + mov ax, wSave + and ax, not 300h ;; single mode + or ax, 3fh ;; disable all exceptions + and ax, not 0xC00 ;; round to nearest mode + mov wTemp, ax + fldcw wTemp + } + } + } + } +*/ + //! This function computes the slowest possible floating-point value (you can also directly use FLT_EPSILON) + inline_ float ComputeFloatEpsilon() + { + float f = 1.0f; + ((udword&)f)^=1; + return f - 1.0f; // You can check it's the same as FLT_EPSILON + } + + inline_ bool IsFloatZero(float x, float epsilon=1e-6f) + { + return x*x < epsilon; + } + +#ifdef PLATFORM_WINDOWS + #define FCOMI_ST0 _asm _emit 0xdb _asm _emit 0xf0 + #define FCOMIP_ST0 _asm _emit 0xdf _asm _emit 0xf0 + #define FCMOVB_ST0 _asm _emit 0xda _asm _emit 0xc0 + #define FCMOVNB_ST0 _asm _emit 0xdb _asm _emit 0xc0 + + #define FCOMI_ST1 _asm _emit 0xdb _asm _emit 0xf1 + #define FCOMIP_ST1 _asm _emit 0xdf _asm _emit 0xf1 + #define FCMOVB_ST1 _asm _emit 0xda _asm _emit 0xc1 + #define FCMOVNB_ST1 _asm _emit 0xdb _asm _emit 0xc1 + + #define FCOMI_ST2 _asm _emit 0xdb _asm _emit 0xf2 + #define FCOMIP_ST2 _asm _emit 0xdf _asm _emit 0xf2 + #define FCMOVB_ST2 _asm _emit 0xda _asm _emit 0xc2 + #define FCMOVNB_ST2 _asm _emit 0xdb _asm _emit 0xc2 + + #define FCOMI_ST3 _asm _emit 0xdb _asm _emit 0xf3 + #define FCOMIP_ST3 _asm _emit 0xdf _asm _emit 0xf3 + #define FCMOVB_ST3 _asm _emit 0xda _asm _emit 0xc3 + #define FCMOVNB_ST3 _asm _emit 0xdb _asm _emit 0xc3 + + #define FCOMI_ST4 _asm _emit 0xdb _asm _emit 0xf4 + #define FCOMIP_ST4 _asm _emit 0xdf _asm _emit 0xf4 + #define FCMOVB_ST4 _asm _emit 0xda _asm _emit 0xc4 + #define FCMOVNB_ST4 _asm _emit 0xdb _asm _emit 0xc4 + + #define FCOMI_ST5 _asm _emit 0xdb _asm _emit 0xf5 + #define FCOMIP_ST5 _asm _emit 0xdf _asm _emit 0xf5 + #define FCMOVB_ST5 _asm _emit 0xda _asm _emit 0xc5 + #define FCMOVNB_ST5 _asm _emit 0xdb _asm _emit 0xc5 + + #define FCOMI_ST6 _asm _emit 0xdb _asm _emit 0xf6 + #define FCOMIP_ST6 _asm _emit 0xdf _asm _emit 0xf6 + #define FCMOVB_ST6 _asm _emit 0xda _asm _emit 0xc6 + #define FCMOVNB_ST6 _asm _emit 0xdb _asm _emit 0xc6 + + #define FCOMI_ST7 _asm _emit 0xdb _asm _emit 0xf7 + #define FCOMIP_ST7 _asm _emit 0xdf _asm _emit 0xf7 + #define FCMOVB_ST7 _asm _emit 0xda _asm _emit 0xc7 + #define FCMOVNB_ST7 _asm _emit 0xdb _asm _emit 0xc7 + + //! A global function to find MAX(a,b) using FCOMI/FCMOV + inline_ float FCMax2(float a, float b) + { + float Res; + _asm fld [a] + _asm fld [b] + FCOMI_ST1 + FCMOVB_ST1 + _asm fstp [Res] + _asm fcomp + return Res; + } + + //! A global function to find MIN(a,b) using FCOMI/FCMOV + inline_ float FCMin2(float a, float b) + { + float Res; + _asm fld [a] + _asm fld [b] + FCOMI_ST1 + FCMOVNB_ST1 + _asm fstp [Res] + _asm fcomp + return Res; + } + + //! A global function to find MAX(a,b,c) using FCOMI/FCMOV + inline_ float FCMax3(float a, float b, float c) + { + float Res; + _asm fld [a] + _asm fld [b] + _asm fld [c] + FCOMI_ST1 + FCMOVB_ST1 + FCOMI_ST2 + FCMOVB_ST2 + _asm fstp [Res] + _asm fcompp + return Res; + } + + //! A global function to find MIN(a,b,c) using FCOMI/FCMOV + inline_ float FCMin3(float a, float b, float c) + { + float Res; + _asm fld [a] + _asm fld [b] + _asm fld [c] + FCOMI_ST1 + FCMOVNB_ST1 + FCOMI_ST2 + FCMOVNB_ST2 + _asm fstp [Res] + _asm fcompp + return Res; + } +#elif defined( PLATFORM_XENON ) + +inline float FCMax2(float a, float b) { return (float)__fsel((a) - (b), a, b ); } +inline float FCMin2(float a, float b) { return (float)__fsel((a) - (b), b, a ); } + +inline float FCMax3(float a, float b, float c) +{ + return (float)__fsel( (a) - (b), __fsel( (a) - (c), a, c ), __fsel( (b) - (c), b, c ) ); +} + +inline float FCMin3(float a, float b, float c) +{ + return (float)__fsel( (a) - (b), __fsel( (b) - (c), c, b ), __fsel( (a) - (c), c, a ) ); +} + +#else +inline float FCMax2(float a, float b) { return a>b ? a : b; } +inline float FCMin2(float a, float b) { return a>b ? b : a; } +inline float FCMax3(float a, float b, float c) +{ + if (a >= b) + return( FCMax2( a, c ) ); + + return( FCMax2( b, c ) ); +} +inline float FCMin3(float a, float b, float c) +{ + if (a >= b) + return( FCMin2( b, c ) ); + + return( FCMin2( a, c ) ); +} +#endif + + inline_ int ConvertToSortable(float f) + { + int& Fi = (int&)f; + int Fmask = (Fi>>31); + Fi ^= Fmask; + Fmask &= ~(1<<31); + Fi -= Fmask; + return Fi; + } + + enum FPUMode + { + FPU_FLOOR = 0, + FPU_CEIL = 1, + FPU_BEST = 2, + + FPU_FORCE_DWORD = 0x7fffffff + }; + + FUNCTION ICECORE_API FPUMode GetFPUMode(); + FUNCTION ICECORE_API void SaveFPU(); + FUNCTION ICECORE_API void RestoreFPU(); + FUNCTION ICECORE_API void SetFPUFloorMode(); + FUNCTION ICECORE_API void SetFPUCeilMode(); + FUNCTION ICECORE_API void SetFPUBestMode(); + + FUNCTION ICECORE_API void SetFPUPrecision24(); + FUNCTION ICECORE_API void SetFPUPrecision53(); + FUNCTION ICECORE_API void SetFPUPrecision64(); + FUNCTION ICECORE_API void SetFPURoundingChop(); + FUNCTION ICECORE_API void SetFPURoundingUp(); + FUNCTION ICECORE_API void SetFPURoundingDown(); + FUNCTION ICECORE_API void SetFPURoundingNear(); + + FUNCTION ICECORE_API int intChop(const float& f); + FUNCTION ICECORE_API int intFloor(const float& f); + FUNCTION ICECORE_API int intCeil(const float& f); + +#endif // __ICEFPU_H__ diff --git a/Engine/lib/opcode/Ice/IceHPoint.cpp b/Engine/lib/opcode/Ice/IceHPoint.cpp new file mode 100644 index 000000000..2a51dd9b6 --- /dev/null +++ b/Engine/lib/opcode/Ice/IceHPoint.cpp @@ -0,0 +1,69 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for homogeneous points. + * \file IceHPoint.cpp + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Homogeneous point. + * + * Use it: + * - for clipping in homogeneous space (standard way) + * - to differentiate between points (w=1) and vectors (w=0). + * - in some cases you can also use it instead of Point for padding reasons. + * + * \class HPoint + * \author Pierre Terdiman + * \version 1.0 + * \warning No cross-product in 4D. + * \warning HPoint *= Matrix3x3 doesn't exist, the matrix is first casted to a 4x4 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "../Opcode.h" + +using namespace IceMaths; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Point Mul = HPoint * Matrix3x3; +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Point HPoint::operator*(const Matrix3x3& mat) const +{ + return Point( + x * mat.m[0][0] + y * mat.m[1][0] + z * mat.m[2][0], + x * mat.m[0][1] + y * mat.m[1][1] + z * mat.m[2][1], + x * mat.m[0][2] + y * mat.m[1][2] + z * mat.m[2][2] ); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// HPoint Mul = HPoint * Matrix4x4; +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HPoint HPoint::operator*(const Matrix4x4& mat) const +{ + return HPoint( + x * mat.m[0][0] + y * mat.m[1][0] + z * mat.m[2][0] + w * mat.m[3][0], + x * mat.m[0][1] + y * mat.m[1][1] + z * mat.m[2][1] + w * mat.m[3][1], + x * mat.m[0][2] + y * mat.m[1][2] + z * mat.m[2][2] + w * mat.m[3][2], + x * mat.m[0][3] + y * mat.m[1][3] + z * mat.m[2][3] + w * mat.m[3][3]); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// HPoint *= Matrix4x4 +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HPoint& HPoint::operator*=(const Matrix4x4& mat) +{ + float xp = x * mat.m[0][0] + y * mat.m[1][0] + z * mat.m[2][0] + w * mat.m[3][0]; + float yp = x * mat.m[0][1] + y * mat.m[1][1] + z * mat.m[2][1] + w * mat.m[3][1]; + float zp = x * mat.m[0][2] + y * mat.m[1][2] + z * mat.m[2][2] + w * mat.m[3][2]; + float wp = x * mat.m[0][3] + y * mat.m[1][3] + z * mat.m[2][3] + w * mat.m[3][3]; + + x = xp; y = yp; z = zp; w = wp; + + return *this; +} + diff --git a/Engine/lib/opcode/Ice/IceHPoint.h b/Engine/lib/opcode/Ice/IceHPoint.h new file mode 100644 index 000000000..fc5c84408 --- /dev/null +++ b/Engine/lib/opcode/Ice/IceHPoint.h @@ -0,0 +1,157 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for homogeneous points. + * \file IceHPoint.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEHPOINT_H__ +#define __ICEHPOINT_H__ + + class ICEMATHS_API HPoint : public Point + { + public: + + //! Empty constructor + inline_ HPoint() {} + //! Constructor from floats + inline_ HPoint(float _x, float _y, float _z, float _w=0.0f) : Point(_x, _y, _z), w(_w) {} + //! Constructor from array + inline_ HPoint(const float f[4]) : Point(f), w(f[3]) {} + //! Constructor from a Point + inline_ HPoint(const Point& p, float _w=0.0f) : Point(p), w(_w) {} + //! Destructor + inline_ ~HPoint() {} + + //! Clear the point + inline_ HPoint& Zero() { x = y = z = w = 0.0f; return *this; } + + //! Assignment from values + inline_ HPoint& Set(float _x, float _y, float _z, float _w ) { x = _x; y = _y; z = _z; w = _w; return *this; } + //! Assignment from array + inline_ HPoint& Set(const float f[4]) { x = f[_X]; y = f[_Y]; z = f[_Z]; w = f[_W]; return *this; } + //! Assignment from another h-point + inline_ HPoint& Set(const HPoint& src) { x = src.x; y = src.y; z = src.z; w = src.w; return *this; } + + //! Add a vector + inline_ HPoint& Add(float _x, float _y, float _z, float _w ) { x += _x; y += _y; z += _z; w += _w; return *this; } + //! Add a vector + inline_ HPoint& Add(const float f[4]) { x += f[_X]; y += f[_Y]; z += f[_Z]; w += f[_W]; return *this; } + + //! Subtract a vector + inline_ HPoint& Sub(float _x, float _y, float _z, float _w ) { x -= _x; y -= _y; z -= _z; w -= _w; return *this; } + //! Subtract a vector + inline_ HPoint& Sub(const float f[4]) { x -= f[_X]; y -= f[_Y]; z -= f[_Z]; w -= f[_W]; return *this; } + + //! Multiplies by a scalar + inline_ HPoint& Mul(float s) { x *= s; y *= s; z *= s; w *= s; return *this; } + + //! Returns MIN(x, y, z, w); + float Min() const { return MIN(x, MIN(y, MIN(z, w))); } + //! Returns MAX(x, y, z, w); + float Max() const { return MAX(x, MAX(y, MAX(z, w))); } + //! Sets each element to be componentwise minimum + HPoint& Min(const HPoint& p) { x = MIN(x, p.x); y = MIN(y, p.y); z = MIN(z, p.z); w = MIN(w, p.w); return *this; } + //! Sets each element to be componentwise maximum + HPoint& Max(const HPoint& p) { x = MAX(x, p.x); y = MAX(y, p.y); z = MAX(z, p.z); w = MAX(w, p.w); return *this; } + + //! Computes square magnitude + inline_ float SquareMagnitude() const { return x*x + y*y + z*z + w*w; } + //! Computes magnitude + inline_ float Magnitude() const { return sqrtf(x*x + y*y + z*z + w*w); } + + //! Normalize the vector + inline_ HPoint& Normalize() + { + float M = Magnitude(); + if(M) + { + M = 1.0f / M; + x *= M; + y *= M; + z *= M; + w *= M; + } + return *this; + } + + // Arithmetic operators + //! Operator for HPoint Negate = - HPoint; + inline_ HPoint operator-() const { return HPoint(-x, -y, -z, -w); } + + //! Operator for HPoint Plus = HPoint + HPoint; + inline_ HPoint operator+(const HPoint& p) const { return HPoint(x + p.x, y + p.y, z + p.z, w + p.w); } + //! Operator for HPoint Minus = HPoint - HPoint; + inline_ HPoint operator-(const HPoint& p) const { return HPoint(x - p.x, y - p.y, z - p.z, w - p.w); } + + //! Operator for HPoint Mul = HPoint * HPoint; + inline_ HPoint operator*(const HPoint& p) const { return HPoint(x * p.x, y * p.y, z * p.z, w * p.w); } + //! Operator for HPoint Scale = HPoint * float; + inline_ HPoint operator*(float s) const { return HPoint(x * s, y * s, z * s, w * s); } + //! Operator for HPoint Scale = float * HPoint; + inline_ friend HPoint operator*(float s, const HPoint& p) { return HPoint(s * p.x, s * p.y, s * p.z, s * p.w); } + + //! Operator for HPoint Div = HPoint / HPoint; + inline_ HPoint operator/(const HPoint& p) const { return HPoint(x / p.x, y / p.y, z / p.z, w / p.w); } + //! Operator for HPoint Scale = HPoint / float; + inline_ HPoint operator/(float s) const { s = 1.0f / s; return HPoint(x * s, y * s, z * s, w * s); } + //! Operator for HPoint Scale = float / HPoint; + inline_ friend HPoint operator/(float s, const HPoint& p) { return HPoint(s / p.x, s / p.y, s / p.z, s / p.w); } + + //! Operator for float DotProd = HPoint | HPoint; + inline_ float operator|(const HPoint& p) const { return x*p.x + y*p.y + z*p.z + w*p.w; } + // No cross-product in 4D + + //! Operator for HPoint += HPoint; + inline_ HPoint& operator+=(const HPoint& p) { x += p.x; y += p.y; z += p.z; w += p.w; return *this; } + //! Operator for HPoint += float; + inline_ HPoint& operator+=(float s) { x += s; y += s; z += s; w += s; return *this; } + + //! Operator for HPoint -= HPoint; + inline_ HPoint& operator-=(const HPoint& p) { x -= p.x; y -= p.y; z -= p.z; w -= p.w; return *this; } + //! Operator for HPoint -= float; + inline_ HPoint& operator-=(float s) { x -= s; y -= s; z -= s; w -= s; return *this; } + + //! Operator for HPoint *= HPoint; + inline_ HPoint& operator*=(const HPoint& p) { x *= p.x; y *= p.y; z *= p.z; w *= p.w; return *this; } + //! Operator for HPoint *= float; + inline_ HPoint& operator*=(float s) { x*=s; y*=s; z*=s; w*=s; return *this; } + + //! Operator for HPoint /= HPoint; + inline_ HPoint& operator/=(const HPoint& p) { x /= p.x; y /= p.y; z /= p.z; w /= p.w; return *this; } + //! Operator for HPoint /= float; + inline_ HPoint& operator/=(float s) { s = 1.0f / s; x*=s; y*=s; z*=s; w*=s; return *this; } + + // Arithmetic operators + + //! Operator for Point Mul = HPoint * Matrix3x3; + Point operator*(const Matrix3x3& mat) const; + //! Operator for HPoint Mul = HPoint * Matrix4x4; + HPoint operator*(const Matrix4x4& mat) const; + + // HPoint *= Matrix3x3 doesn't exist, the matrix is first casted to a 4x4 + //! Operator for HPoint *= Matrix4x4 + HPoint& operator*=(const Matrix4x4& mat); + + // Logical operators + + //! Operator for "if(HPoint==HPoint)" + inline_ bool operator==(const HPoint& p) const { return ( (x==p.x)&&(y==p.y)&&(z==p.z)&&(w==p.w)); } + //! Operator for "if(HPoint!=HPoint)" + inline_ bool operator!=(const HPoint& p) const { return ( (x!=p.x)||(y!=p.y)||(z!=p.z)||(w!=p.w)); } + + // Cast operators + + //! Cast a HPoint to a Point. w is discarded. + inline_ operator Point() const { return Point(x, y, z); } + + public: + float w; + }; + +#endif // __ICEHPOINT_H__ + diff --git a/Engine/lib/opcode/Ice/IceIndexedTriangle.cpp b/Engine/lib/opcode/Ice/IceIndexedTriangle.cpp new file mode 100644 index 000000000..cee5dc1de --- /dev/null +++ b/Engine/lib/opcode/Ice/IceIndexedTriangle.cpp @@ -0,0 +1,547 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a handy indexed triangle class. + * \file IceIndexedTriangle.cpp + * \author Pierre Terdiman + * \date January, 17, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "../Opcode.h" + +using namespace IceMaths; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains an indexed triangle class. + * + * \class Triangle + * \author Pierre Terdiman + * \version 1.0 + * \date 08.15.98 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Flips the winding order. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void IndexedTriangle::Flip() +{ + Swap(mVRef[1], mVRef[2]); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle area. + * \param verts [in] the list of indexed vertices + * \return the area + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float IndexedTriangle::Area(const Point* verts) const +{ + if(!verts) return 0.0f; + const Point& p0 = verts[0]; + const Point& p1 = verts[1]; + const Point& p2 = verts[2]; + return ((p0-p1)^(p0-p2)).Magnitude() * 0.5f; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle perimeter. + * \param verts [in] the list of indexed vertices + * \return the perimeter + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float IndexedTriangle::Perimeter(const Point* verts) const +{ + if(!verts) return 0.0f; + const Point& p0 = verts[0]; + const Point& p1 = verts[1]; + const Point& p2 = verts[2]; + return p0.Distance(p1) + + p0.Distance(p2) + + p1.Distance(p2); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle compacity. + * \param verts [in] the list of indexed vertices + * \return the compacity + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float IndexedTriangle::Compacity(const Point* verts) const +{ + if(!verts) return 0.0f; + float P = Perimeter(verts); + if(P==0.0f) return 0.0f; + return (4.0f*PI*Area(verts)/(P*P)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle normal. + * \param verts [in] the list of indexed vertices + * \param normal [out] the computed normal + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void IndexedTriangle::Normal(const Point* verts, Point& normal) const +{ + if(!verts) return; + + const Point& p0 = verts[mVRef[0]]; + const Point& p1 = verts[mVRef[1]]; + const Point& p2 = verts[mVRef[2]]; + normal = ((p2-p1)^(p0-p1)).Normalize(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle denormalized normal. + * \param verts [in] the list of indexed vertices + * \param normal [out] the computed normal + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void IndexedTriangle::DenormalizedNormal(const Point* verts, Point& normal) const +{ + if(!verts) return; + + const Point& p0 = verts[mVRef[0]]; + const Point& p1 = verts[mVRef[1]]; + const Point& p2 = verts[mVRef[2]]; + normal = ((p2-p1)^(p0-p1)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle center. + * \param verts [in] the list of indexed vertices + * \param center [out] the computed center + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void IndexedTriangle::Center(const Point* verts, Point& center) const +{ + if(!verts) return; + + const Point& p0 = verts[mVRef[0]]; + const Point& p1 = verts[mVRef[1]]; + const Point& p2 = verts[mVRef[2]]; + center = (p0+p1+p2)*INV3; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the centered normal + * \param verts [in] the list of indexed vertices + * \param normal [out] the computed centered normal + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void IndexedTriangle::CenteredNormal(const Point* verts, Point& normal) const +{ + if(!verts) return; + + const Point& p0 = verts[mVRef[0]]; + const Point& p1 = verts[mVRef[1]]; + const Point& p2 = verts[mVRef[2]]; + Point Center = (p0+p1+p2)*INV3; + normal = Center + ((p2-p1)^(p0-p1)).Normalize(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes a random point within the triangle. + * \param verts [in] the list of indexed vertices + * \param normal [out] the computed centered normal + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void IndexedTriangle::RandomPoint(const Point* verts, Point& random) const +{ + if(!verts) return; + + // Random barycentric coords + float Alpha = UnitRandomFloat(); + float Beta = UnitRandomFloat(); + float Gamma = UnitRandomFloat(); + float OneOverTotal = 1.0f / (Alpha + Beta + Gamma); + Alpha *= OneOverTotal; + Beta *= OneOverTotal; + Gamma *= OneOverTotal; + + const Point& p0 = verts[mVRef[0]]; + const Point& p1 = verts[mVRef[1]]; + const Point& p2 = verts[mVRef[2]]; + random = Alpha*p0 + Beta*p1 + Gamma*p2; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes backface culling. + * \param verts [in] the list of indexed vertices + * \param source [in] source point (in local space) from which culling must be computed + * \return true if the triangle is visible from the source point + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool IndexedTriangle::IsVisible(const Point* verts, const Point& source) const +{ + // Checkings + if(!verts) return false; + + const Point& p0 = verts[mVRef[0]]; + const Point& p1 = verts[mVRef[1]]; + const Point& p2 = verts[mVRef[2]]; + + // Compute denormalized normal + Point Normal = (p2 - p1)^(p0 - p1); + + // Backface culling + return (Normal | source) >= 0.0f; + +// Same as: +// Plane PL(verts[mVRef[0]], verts[mVRef[1]], verts[mVRef[2]]); +// return PL.Distance(source) > PL.d; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes backface culling. + * \param verts [in] the list of indexed vertices + * \param source [in] source point (in local space) from which culling must be computed + * \return true if the triangle is visible from the source point + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool IndexedTriangle::BackfaceCulling(const Point* verts, const Point& source) const +{ + // Checkings + if(!verts) return false; + + const Point& p0 = verts[mVRef[0]]; + const Point& p1 = verts[mVRef[1]]; + const Point& p2 = verts[mVRef[2]]; + + // Compute base +// Point Base = (p0 + p1 + p2)*INV3; + + // Compute denormalized normal + Point Normal = (p2 - p1)^(p0 - p1); + + // Backface culling +// return (Normal | (source - Base)) >= 0.0f; + return (Normal | (source - p0)) >= 0.0f; + +// Same as: (but a bit faster) +// Plane PL(verts[mVRef[0]], verts[mVRef[1]], verts[mVRef[2]]); +// return PL.Distance(source)>0.0f; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the occlusion potential of the triangle. + * \param verts [in] the list of indexed vertices + * \param source [in] source point (in local space) from which occlusion potential must be computed + * \return the occlusion potential + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float IndexedTriangle::ComputeOcclusionPotential(const Point* verts, const Point& view) const +{ + if(!verts) return 0.0f; + // Occlusion potential: -(A * (N|V) / d^2) + // A = polygon area + // N = polygon normal + // V = view vector + // d = distance viewpoint-center of polygon + + float A = Area(verts); + Point N; Normal(verts, N); + Point C; Center(verts, C); + float d = view.Distance(C); + return -(A*(N|view))/(d*d); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Replaces a vertex reference with another one. + * \param oldref [in] the vertex reference to replace + * \param newref [in] the new vertex reference + * \return true if success, else false if the input vertex reference doesn't belong to the triangle + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool IndexedTriangle::ReplaceVertex(udword oldref, udword newref) +{ + if(mVRef[0]==oldref) { mVRef[0] = newref; return true; } + else if(mVRef[1]==oldref) { mVRef[1] = newref; return true; } + else if(mVRef[2]==oldref) { mVRef[2] = newref; return true; } + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks whether the triangle is degenerate or not. A degenerate triangle has two common vertex references. This is a zero-area triangle. + * \return true if the triangle is degenerate + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool IndexedTriangle::IsDegenerate() const +{ + if(mVRef[0]==mVRef[1]) return true; + if(mVRef[1]==mVRef[2]) return true; + if(mVRef[2]==mVRef[0]) return true; + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks whether the input vertex reference belongs to the triangle or not. + * \param ref [in] the vertex reference to look for + * \return true if the triangle contains the vertex reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool IndexedTriangle::HasVertex(udword ref) const +{ + if(mVRef[0]==ref) return true; + if(mVRef[1]==ref) return true; + if(mVRef[2]==ref) return true; + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks whether the input vertex reference belongs to the triangle or not. + * \param ref [in] the vertex reference to look for + * \param index [out] the corresponding index in the triangle + * \return true if the triangle contains the vertex reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool IndexedTriangle::HasVertex(udword ref, udword* index) const +{ + if(mVRef[0]==ref) { *index = 0; return true; } + if(mVRef[1]==ref) { *index = 1; return true; } + if(mVRef[2]==ref) { *index = 2; return true; } + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Finds an edge in a tri, given two vertex references. + * \param vref0 [in] the edge's first vertex reference + * \param vref1 [in] the edge's second vertex reference + * \return the edge number between 0 and 2, or 0xff if input refs are wrong. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +ubyte IndexedTriangle::FindEdge(udword vref0, udword vref1) const +{ + if(mVRef[0]==vref0 && mVRef[1]==vref1) return 0; + else if(mVRef[0]==vref1 && mVRef[1]==vref0) return 0; + else if(mVRef[0]==vref0 && mVRef[2]==vref1) return 1; + else if(mVRef[0]==vref1 && mVRef[2]==vref0) return 1; + else if(mVRef[1]==vref0 && mVRef[2]==vref1) return 2; + else if(mVRef[1]==vref1 && mVRef[2]==vref0) return 2; + return 0xff; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Gets the last reference given the first two. + * \param vref0 [in] the first vertex reference + * \param vref1 [in] the second vertex reference + * \return the last reference, or INVALID_ID_OPC if input refs are wrong. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +udword IndexedTriangle::OppositeVertex(udword vref0, udword vref1) const +{ + if(mVRef[0]==vref0 && mVRef[1]==vref1) return mVRef[2]; + else if(mVRef[0]==vref1 && mVRef[1]==vref0) return mVRef[2]; + else if(mVRef[0]==vref0 && mVRef[2]==vref1) return mVRef[1]; + else if(mVRef[0]==vref1 && mVRef[2]==vref0) return mVRef[1]; + else if(mVRef[1]==vref0 && mVRef[2]==vref1) return mVRef[0]; + else if(mVRef[1]==vref1 && mVRef[2]==vref0) return mVRef[0]; + return INVALID_ID_OPC; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Gets the three sorted vertex references according to an edge number. + * edgenb = 0 => edge 0-1, returns references 0, 1, 2 + * edgenb = 1 => edge 0-2, returns references 0, 2, 1 + * edgenb = 2 => edge 1-2, returns references 1, 2, 0 + * + * \param edgenb [in] the edge number, 0, 1 or 2 + * \param vref0 [out] the returned first vertex reference + * \param vref1 [out] the returned second vertex reference + * \param vref2 [out] the returned third vertex reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void IndexedTriangle::GetVRefs(ubyte edgenb, udword& vref0, udword& vref1, udword& vref2) const +{ + if(edgenb==0) + { + vref0 = mVRef[0]; + vref1 = mVRef[1]; + vref2 = mVRef[2]; + } + else if(edgenb==1) + { + vref0 = mVRef[0]; + vref1 = mVRef[2]; + vref2 = mVRef[1]; + } + else if(edgenb==2) + { + vref0 = mVRef[1]; + vref1 = mVRef[2]; + vref2 = mVRef[0]; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle's smallest edge length. + * \param verts [in] the list of indexed vertices + * \return the smallest edge length + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float IndexedTriangle::MinEdgeLength(const Point* verts) const +{ + if(!verts) return 0.0f; + + float Min = MAX_FLOAT; + float Length01 = verts[0].Distance(verts[1]); + float Length02 = verts[0].Distance(verts[2]); + float Length12 = verts[1].Distance(verts[2]); + if(Length01 < Min) Min = Length01; + if(Length02 < Min) Min = Length02; + if(Length12 < Min) Min = Length12; + return Min; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle's largest edge length. + * \param verts [in] the list of indexed vertices + * \return the largest edge length + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float IndexedTriangle::MaxEdgeLength(const Point* verts) const +{ + if(!verts) return 0.0f; + + float Max = MIN_FLOAT; + float Length01 = verts[0].Distance(verts[1]); + float Length02 = verts[0].Distance(verts[2]); + float Length12 = verts[1].Distance(verts[2]); + if(Length01 > Max) Max = Length01; + if(Length02 > Max) Max = Length02; + if(Length12 > Max) Max = Length12; + return Max; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes a point on the triangle according to the stabbing information. + * \param verts [in] the list of indexed vertices + * \param u,v [in] point's barycentric coordinates + * \param pt [out] point on triangle + * \param nearvtx [out] index of nearest vertex + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void IndexedTriangle::ComputePoint(const Point* verts, float u, float v, Point& pt, udword* nearvtx) const +{ + // Checkings + if(!verts) return; + + // Get face in local or global space + const Point& p0 = verts[mVRef[0]]; + const Point& p1 = verts[mVRef[1]]; + const Point& p2 = verts[mVRef[2]]; + + // Compute point coordinates + pt = (1.0f - u - v)*p0 + u*p1 + v*p2; + + // Compute nearest vertex if needed + if(nearvtx) + { + // Compute distance vector + Point d(p0.SquareDistance(pt), // Distance^2 from vertex 0 to point on the face + p1.SquareDistance(pt), // Distance^2 from vertex 1 to point on the face + p2.SquareDistance(pt)); // Distance^2 from vertex 2 to point on the face + + // Get smallest distance + *nearvtx = mVRef[d.SmallestAxis()]; + } +} + + //************************************** + // Angle between two vectors (in radians) + // we use this formula + // uv = |u||v| cos(u,v) + // u ^ v = w + // |w| = |u||v| |sin(u,v)| + //************************************** + float Angle(const Point& u, const Point& v) + { + float NormU = u.Magnitude(); // |u| + float NormV = v.Magnitude(); // |v| + float Product = NormU*NormV; // |u||v| + if(Product==0.0f) return 0.0f; + float OneOverProduct = 1.0f / Product; + + // Cosinus + float Cosinus = (u|v) * OneOverProduct; + + // Sinus + Point w = u^v; + float NormW = w.Magnitude(); + + float AbsSinus = NormW * OneOverProduct; + + // Remove degeneracy + if(AbsSinus > 1.0f) AbsSinus = 1.0f; + if(AbsSinus < -1.0f) AbsSinus = -1.0f; + + if(Cosinus>=0.0f) return asinf(AbsSinus); + else return (PI-asinf(AbsSinus)); + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the angle between two triangles. + * \param tri [in] the other triangle + * \param verts [in] the list of indexed vertices + * \return the angle in radians + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float IndexedTriangle::Angle(const IndexedTriangle& tri, const Point* verts) const +{ + // Checkings + if(!verts) return 0.0f; + + // Compute face normals + Point n0, n1; + Normal(verts, n0); + tri.Normal(verts, n1); + + // Compute angle + float dp = n0|n1; + if(dp>1.0f) return 0.0f; + if(dp<-1.0f) return PI; + return acosf(dp); + +// return ::Angle(n0,n1); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks a triangle is the same as another one. + * \param tri [in] the other triangle + * \return true if same triangle + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool IndexedTriangle::Equal(const IndexedTriangle& tri) const +{ + // Test all vertex references + return (HasVertex(tri.mVRef[0]) && + HasVertex(tri.mVRef[1]) && + HasVertex(tri.mVRef[2])); +} diff --git a/Engine/lib/opcode/Ice/IceIndexedTriangle.h b/Engine/lib/opcode/Ice/IceIndexedTriangle.h new file mode 100644 index 000000000..33bc2b956 --- /dev/null +++ b/Engine/lib/opcode/Ice/IceIndexedTriangle.h @@ -0,0 +1,67 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a handy indexed triangle class. + * \file IceIndexedTriangle.h + * \author Pierre Terdiman + * \date January, 17, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEINDEXEDTRIANGLE_H__ +#define __ICEINDEXEDTRIANGLE_H__ + + // An indexed triangle class. + class ICEMATHS_API IndexedTriangle + { + public: + //! Constructor + inline_ IndexedTriangle() { mMatIdx=-1; } + //! Constructor + inline_ IndexedTriangle(udword r0, udword r1, udword r2) { mVRef[0]=r0; mVRef[1]=r1; mVRef[2]=r2; mMatIdx=-1; } + //! Copy constructor + inline_ IndexedTriangle(const IndexedTriangle& triangle) + { + mVRef[0] = triangle.mVRef[0]; + mVRef[1] = triangle.mVRef[1]; + mVRef[2] = triangle.mVRef[2]; + mMatIdx = -1; + } + //! Destructor + inline_ ~IndexedTriangle() {} + //! Vertex-references + udword mVRef[3]; + //! Material-reference + udword mMatIdx; + + // Methods + void Flip(); + float Area(const Point* verts) const; + float Perimeter(const Point* verts) const; + float Compacity(const Point* verts) const; + void Normal(const Point* verts, Point& normal) const; + void DenormalizedNormal(const Point* verts, Point& normal) const; + void Center(const Point* verts, Point& center) const; + void CenteredNormal(const Point* verts, Point& normal) const; + void RandomPoint(const Point* verts, Point& random) const; + bool IsVisible(const Point* verts, const Point& source) const; + bool BackfaceCulling(const Point* verts, const Point& source) const; + float ComputeOcclusionPotential(const Point* verts, const Point& view) const; + bool ReplaceVertex(udword oldref, udword newref); + bool IsDegenerate() const; + bool HasVertex(udword ref) const; + bool HasVertex(udword ref, udword* index) const; + ubyte FindEdge(udword vref0, udword vref1) const; + udword OppositeVertex(udword vref0, udword vref1) const; + inline_ udword OppositeVertex(ubyte edgenb) const { return mVRef[2-edgenb]; } + void GetVRefs(ubyte edgenb, udword& vref0, udword& vref1, udword& vref2) const; + float MinEdgeLength(const Point* verts) const; + float MaxEdgeLength(const Point* verts) const; + void ComputePoint(const Point* verts, float u, float v, Point& pt, udword* nearvtx=null) const; + float Angle(const IndexedTriangle& tri, const Point* verts) const; + inline_ Plane PlaneEquation(const Point* verts) const { return Plane(verts[mVRef[0]], verts[mVRef[1]], verts[mVRef[2]]); } + bool Equal(const IndexedTriangle& tri) const; + }; + +#endif // __ICEINDEXEDTRIANGLE_H__ diff --git a/Engine/lib/opcode/Ice/IceLSS.h b/Engine/lib/opcode/Ice/IceLSS.h new file mode 100644 index 000000000..bd260c1e5 --- /dev/null +++ b/Engine/lib/opcode/Ice/IceLSS.h @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for line-swept spheres. + * \file IceLSS.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICELSS_H__ +#define __ICELSS_H__ + + class ICEMATHS_API LSS : public Segment + { + public: + //! Constructor + inline_ LSS() {} + //! Constructor + inline_ LSS(const Segment& seg, float radius) : Segment(seg), mRadius(radius) {} + //! Destructor + inline_ ~LSS() {} + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes an OBB surrounding the LSS. + * \param box [out] the OBB + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void ComputeOBB(OBB& box); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Tests if a point is contained within the LSS. + * \param pt [in] the point to test + * \return true if inside the LSS + * \warning point and LSS must be in same space + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ bool Contains(const Point& pt) const { return SquareDistance(pt) <= mRadius*mRadius; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Tests if a sphere is contained within the LSS. + * \param sphere [in] the sphere to test + * \return true if inside the LSS + * \warning sphere and LSS must be in same space + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ bool Contains(const Sphere& sphere) + { + float d = mRadius - sphere.mRadius; + if(d>=0.0f) return SquareDistance(sphere.mCenter) <= d*d; + else return false; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Tests if an LSS is contained within the LSS. + * \param lss [in] the LSS to test + * \return true if inside the LSS + * \warning both LSS must be in same space + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ bool Contains(const LSS& lss) + { + // We check the LSS contains the two spheres at the start and end of the sweep + return Contains(Sphere(lss.mP0, lss.mRadius)) && Contains(Sphere(lss.mP0, lss.mRadius)); + } + + float mRadius; //!< Sphere radius + }; + +#endif // __ICELSS_H__ diff --git a/Engine/lib/opcode/Ice/IceMatrix3x3.cpp b/Engine/lib/opcode/Ice/IceMatrix3x3.cpp new file mode 100644 index 000000000..53999e6ae --- /dev/null +++ b/Engine/lib/opcode/Ice/IceMatrix3x3.cpp @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for 3x3 matrices. + * \file IceMatrix3x3.cpp + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * 3x3 matrix. + * DirectX-compliant, ie row-column order, ie m[Row][Col]. + * Same as: + * m11 m12 m13 first row. + * m21 m22 m23 second row. + * m31 m32 m33 third row. + * Stored in memory as m11 m12 m13 m21... + * + * Multiplication rules: + * + * [x'y'z'] = [xyz][M] + * + * x' = x*m11 + y*m21 + z*m31 + * y' = x*m12 + y*m22 + z*m32 + * z' = x*m13 + y*m23 + z*m33 + * + * \class Matrix3x3 + * \author Pierre Terdiman + * \version 1.0 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "../Opcode.h" + +using namespace IceMaths; + +// Cast operator +Matrix3x3::operator Matrix4x4() const +{ + return Matrix4x4( + m[0][0], m[0][1], m[0][2], 0.0f, + m[1][0], m[1][1], m[1][2], 0.0f, + m[2][0], m[2][1], m[2][2], 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f); +} diff --git a/Engine/lib/opcode/Ice/IceMatrix3x3.h b/Engine/lib/opcode/Ice/IceMatrix3x3.h new file mode 100644 index 000000000..a30680da6 --- /dev/null +++ b/Engine/lib/opcode/Ice/IceMatrix3x3.h @@ -0,0 +1,496 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for 3x3 matrices. + * \file IceMatrix3x3.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEMATRIX3X3_H__ +#define __ICEMATRIX3X3_H__ + + // Forward declarations + class Quat; + + #define MATRIX3X3_EPSILON (1.0e-7f) + + class ICEMATHS_API Matrix3x3 + { + public: + //! Empty constructor + inline_ Matrix3x3() {} + //! Constructor from 9 values + inline_ Matrix3x3(float m00, float m01, float m02, float m10, float m11, float m12, float m20, float m21, float m22) + { + m[0][0] = m00; m[0][1] = m01; m[0][2] = m02; + m[1][0] = m10; m[1][1] = m11; m[1][2] = m12; + m[2][0] = m20; m[2][1] = m21; m[2][2] = m22; + } + //! Copy constructor + inline_ Matrix3x3(const Matrix3x3& mat) { CopyMemory(m, &mat.m, 9*sizeof(float)); } + //! Destructor + inline_ ~Matrix3x3() {} + + //! Assign values + inline_ void Set(float m00, float m01, float m02, float m10, float m11, float m12, float m20, float m21, float m22) + { + m[0][0] = m00; m[0][1] = m01; m[0][2] = m02; + m[1][0] = m10; m[1][1] = m11; m[1][2] = m12; + m[2][0] = m20; m[2][1] = m21; m[2][2] = m22; + } + + //! Sets the scale from a Point. The point is put on the diagonal. + inline_ void SetScale(const Point& p) { m[0][0] = p.x; m[1][1] = p.y; m[2][2] = p.z; } + + //! Sets the scale from floats. Values are put on the diagonal. + inline_ void SetScale(float sx, float sy, float sz) { m[0][0] = sx; m[1][1] = sy; m[2][2] = sz; } + + //! Scales from a Point. Each row is multiplied by a component. + inline_ void Scale(const Point& p) + { + m[0][0] *= p.x; m[0][1] *= p.x; m[0][2] *= p.x; + m[1][0] *= p.y; m[1][1] *= p.y; m[1][2] *= p.y; + m[2][0] *= p.z; m[2][1] *= p.z; m[2][2] *= p.z; + } + + //! Scales from floats. Each row is multiplied by a value. + inline_ void Scale(float sx, float sy, float sz) + { + m[0][0] *= sx; m[0][1] *= sx; m[0][2] *= sx; + m[1][0] *= sy; m[1][1] *= sy; m[1][2] *= sy; + m[2][0] *= sz; m[2][1] *= sz; m[2][2] *= sz; + } + + //! Copy from a Matrix3x3 + inline_ void Copy(const Matrix3x3& source) { CopyMemory(m, source.m, 9*sizeof(float)); } + + // Row-column access + //! Returns a row. + inline_ void GetRow(const udword r, Point& p) const { p.x = m[r][0]; p.y = m[r][1]; p.z = m[r][2]; } + //! Returns a row. + inline_ const Point& GetRow(const udword r) const { return *(const Point*)&m[r][0]; } + //! Returns a row. + inline_ Point& GetRow(const udword r) { return *(Point*)&m[r][0]; } + //! Sets a row. + inline_ void SetRow(const udword r, const Point& p) { m[r][0] = p.x; m[r][1] = p.y; m[r][2] = p.z; } + //! Returns a column. + inline_ void GetCol(const udword c, Point& p) const { p.x = m[0][c]; p.y = m[1][c]; p.z = m[2][c]; } + //! Sets a column. + inline_ void SetCol(const udword c, const Point& p) { m[0][c] = p.x; m[1][c] = p.y; m[2][c] = p.z; } + + //! Computes the trace. The trace is the sum of the 3 diagonal components. + inline_ float Trace() const { return m[0][0] + m[1][1] + m[2][2]; } + //! Clears the matrix. + inline_ void Zero() { ZeroMemory(&m, sizeof(m)); } + //! Sets the identity matrix. + inline_ void Identity() { Zero(); m[0][0] = m[1][1] = m[2][2] = 1.0f; } + //! Checks for identity + inline_ bool IsIdentity() const + { + if(IR(m[0][0])!=IEEE_1_0) return false; + if(IR(m[0][1])!=0) return false; + if(IR(m[0][2])!=0) return false; + + if(IR(m[1][0])!=0) return false; + if(IR(m[1][1])!=IEEE_1_0) return false; + if(IR(m[1][2])!=0) return false; + + if(IR(m[2][0])!=0) return false; + if(IR(m[2][1])!=0) return false; + if(IR(m[2][2])!=IEEE_1_0) return false; + + return true; + } + + //! Checks matrix validity + inline_ BOOL IsValid() const + { + for(udword j=0;j<3;j++) + { + for(udword i=0;i<3;i++) + { + if(!IsValidFloat(m[j][i])) return FALSE; + } + } + return TRUE; + } + + //! Makes a skew-symmetric matrix (a.k.a. Star(*) Matrix) + //! [ 0.0 -a.z a.y ] + //! [ a.z 0.0 -a.x ] + //! [ -a.y a.x 0.0 ] + //! This is also called a "cross matrix" since for any vectors A and B, + //! A^B = Skew(A) * B = - B * Skew(A); + inline_ void SkewSymmetric(const Point& a) + { + m[0][0] = 0.0f; + m[0][1] = -a.z; + m[0][2] = a.y; + + m[1][0] = a.z; + m[1][1] = 0.0f; + m[1][2] = -a.x; + + m[2][0] = -a.y; + m[2][1] = a.x; + m[2][2] = 0.0f; + } + + //! Negates the matrix + inline_ void Neg() + { + m[0][0] = -m[0][0]; m[0][1] = -m[0][1]; m[0][2] = -m[0][2]; + m[1][0] = -m[1][0]; m[1][1] = -m[1][1]; m[1][2] = -m[1][2]; + m[2][0] = -m[2][0]; m[2][1] = -m[2][1]; m[2][2] = -m[2][2]; + } + + //! Neg from another matrix + inline_ void Neg(const Matrix3x3& mat) + { + m[0][0] = -mat.m[0][0]; m[0][1] = -mat.m[0][1]; m[0][2] = -mat.m[0][2]; + m[1][0] = -mat.m[1][0]; m[1][1] = -mat.m[1][1]; m[1][2] = -mat.m[1][2]; + m[2][0] = -mat.m[2][0]; m[2][1] = -mat.m[2][1]; m[2][2] = -mat.m[2][2]; + } + + //! Add another matrix + inline_ void Add(const Matrix3x3& mat) + { + m[0][0] += mat.m[0][0]; m[0][1] += mat.m[0][1]; m[0][2] += mat.m[0][2]; + m[1][0] += mat.m[1][0]; m[1][1] += mat.m[1][1]; m[1][2] += mat.m[1][2]; + m[2][0] += mat.m[2][0]; m[2][1] += mat.m[2][1]; m[2][2] += mat.m[2][2]; + } + + //! Sub another matrix + inline_ void Sub(const Matrix3x3& mat) + { + m[0][0] -= mat.m[0][0]; m[0][1] -= mat.m[0][1]; m[0][2] -= mat.m[0][2]; + m[1][0] -= mat.m[1][0]; m[1][1] -= mat.m[1][1]; m[1][2] -= mat.m[1][2]; + m[2][0] -= mat.m[2][0]; m[2][1] -= mat.m[2][1]; m[2][2] -= mat.m[2][2]; + } + //! Mac + inline_ void Mac(const Matrix3x3& a, const Matrix3x3& b, float s) + { + m[0][0] = a.m[0][0] + b.m[0][0] * s; + m[0][1] = a.m[0][1] + b.m[0][1] * s; + m[0][2] = a.m[0][2] + b.m[0][2] * s; + + m[1][0] = a.m[1][0] + b.m[1][0] * s; + m[1][1] = a.m[1][1] + b.m[1][1] * s; + m[1][2] = a.m[1][2] + b.m[1][2] * s; + + m[2][0] = a.m[2][0] + b.m[2][0] * s; + m[2][1] = a.m[2][1] + b.m[2][1] * s; + m[2][2] = a.m[2][2] + b.m[2][2] * s; + } + //! Mac + inline_ void Mac(const Matrix3x3& a, float s) + { + m[0][0] += a.m[0][0] * s; m[0][1] += a.m[0][1] * s; m[0][2] += a.m[0][2] * s; + m[1][0] += a.m[1][0] * s; m[1][1] += a.m[1][1] * s; m[1][2] += a.m[1][2] * s; + m[2][0] += a.m[2][0] * s; m[2][1] += a.m[2][1] * s; m[2][2] += a.m[2][2] * s; + } + + //! this = A * s + inline_ void Mult(const Matrix3x3& a, float s) + { + m[0][0] = a.m[0][0] * s; m[0][1] = a.m[0][1] * s; m[0][2] = a.m[0][2] * s; + m[1][0] = a.m[1][0] * s; m[1][1] = a.m[1][1] * s; m[1][2] = a.m[1][2] * s; + m[2][0] = a.m[2][0] * s; m[2][1] = a.m[2][1] * s; m[2][2] = a.m[2][2] * s; + } + + inline_ void Add(const Matrix3x3& a, const Matrix3x3& b) + { + m[0][0] = a.m[0][0] + b.m[0][0]; m[0][1] = a.m[0][1] + b.m[0][1]; m[0][2] = a.m[0][2] + b.m[0][2]; + m[1][0] = a.m[1][0] + b.m[1][0]; m[1][1] = a.m[1][1] + b.m[1][1]; m[1][2] = a.m[1][2] + b.m[1][2]; + m[2][0] = a.m[2][0] + b.m[2][0]; m[2][1] = a.m[2][1] + b.m[2][1]; m[2][2] = a.m[2][2] + b.m[2][2]; + } + + inline_ void Sub(const Matrix3x3& a, const Matrix3x3& b) + { + m[0][0] = a.m[0][0] - b.m[0][0]; m[0][1] = a.m[0][1] - b.m[0][1]; m[0][2] = a.m[0][2] - b.m[0][2]; + m[1][0] = a.m[1][0] - b.m[1][0]; m[1][1] = a.m[1][1] - b.m[1][1]; m[1][2] = a.m[1][2] - b.m[1][2]; + m[2][0] = a.m[2][0] - b.m[2][0]; m[2][1] = a.m[2][1] - b.m[2][1]; m[2][2] = a.m[2][2] - b.m[2][2]; + } + + //! this = a * b + inline_ void Mult(const Matrix3x3& a, const Matrix3x3& b) + { + m[0][0] = a.m[0][0] * b.m[0][0] + a.m[0][1] * b.m[1][0] + a.m[0][2] * b.m[2][0]; + m[0][1] = a.m[0][0] * b.m[0][1] + a.m[0][1] * b.m[1][1] + a.m[0][2] * b.m[2][1]; + m[0][2] = a.m[0][0] * b.m[0][2] + a.m[0][1] * b.m[1][2] + a.m[0][2] * b.m[2][2]; + m[1][0] = a.m[1][0] * b.m[0][0] + a.m[1][1] * b.m[1][0] + a.m[1][2] * b.m[2][0]; + m[1][1] = a.m[1][0] * b.m[0][1] + a.m[1][1] * b.m[1][1] + a.m[1][2] * b.m[2][1]; + m[1][2] = a.m[1][0] * b.m[0][2] + a.m[1][1] * b.m[1][2] + a.m[1][2] * b.m[2][2]; + m[2][0] = a.m[2][0] * b.m[0][0] + a.m[2][1] * b.m[1][0] + a.m[2][2] * b.m[2][0]; + m[2][1] = a.m[2][0] * b.m[0][1] + a.m[2][1] * b.m[1][1] + a.m[2][2] * b.m[2][1]; + m[2][2] = a.m[2][0] * b.m[0][2] + a.m[2][1] * b.m[1][2] + a.m[2][2] * b.m[2][2]; + } + + //! this = transpose(a) * b + inline_ void MultAtB(const Matrix3x3& a, const Matrix3x3& b) + { + m[0][0] = a.m[0][0] * b.m[0][0] + a.m[1][0] * b.m[1][0] + a.m[2][0] * b.m[2][0]; + m[0][1] = a.m[0][0] * b.m[0][1] + a.m[1][0] * b.m[1][1] + a.m[2][0] * b.m[2][1]; + m[0][2] = a.m[0][0] * b.m[0][2] + a.m[1][0] * b.m[1][2] + a.m[2][0] * b.m[2][2]; + m[1][0] = a.m[0][1] * b.m[0][0] + a.m[1][1] * b.m[1][0] + a.m[2][1] * b.m[2][0]; + m[1][1] = a.m[0][1] * b.m[0][1] + a.m[1][1] * b.m[1][1] + a.m[2][1] * b.m[2][1]; + m[1][2] = a.m[0][1] * b.m[0][2] + a.m[1][1] * b.m[1][2] + a.m[2][1] * b.m[2][2]; + m[2][0] = a.m[0][2] * b.m[0][0] + a.m[1][2] * b.m[1][0] + a.m[2][2] * b.m[2][0]; + m[2][1] = a.m[0][2] * b.m[0][1] + a.m[1][2] * b.m[1][1] + a.m[2][2] * b.m[2][1]; + m[2][2] = a.m[0][2] * b.m[0][2] + a.m[1][2] * b.m[1][2] + a.m[2][2] * b.m[2][2]; + } + + //! this = a * transpose(b) + inline_ void MultABt(const Matrix3x3& a, const Matrix3x3& b) + { + m[0][0] = a.m[0][0] * b.m[0][0] + a.m[0][1] * b.m[0][1] + a.m[0][2] * b.m[0][2]; + m[0][1] = a.m[0][0] * b.m[1][0] + a.m[0][1] * b.m[1][1] + a.m[0][2] * b.m[1][2]; + m[0][2] = a.m[0][0] * b.m[2][0] + a.m[0][1] * b.m[2][1] + a.m[0][2] * b.m[2][2]; + m[1][0] = a.m[1][0] * b.m[0][0] + a.m[1][1] * b.m[0][1] + a.m[1][2] * b.m[0][2]; + m[1][1] = a.m[1][0] * b.m[1][0] + a.m[1][1] * b.m[1][1] + a.m[1][2] * b.m[1][2]; + m[1][2] = a.m[1][0] * b.m[2][0] + a.m[1][1] * b.m[2][1] + a.m[1][2] * b.m[2][2]; + m[2][0] = a.m[2][0] * b.m[0][0] + a.m[2][1] * b.m[0][1] + a.m[2][2] * b.m[0][2]; + m[2][1] = a.m[2][0] * b.m[1][0] + a.m[2][1] * b.m[1][1] + a.m[2][2] * b.m[1][2]; + m[2][2] = a.m[2][0] * b.m[2][0] + a.m[2][1] * b.m[2][1] + a.m[2][2] * b.m[2][2]; + } + + //! Makes a rotation matrix mapping vector "from" to vector "to". + Matrix3x3& FromTo(const Point& from, const Point& to); + + //! Set a rotation matrix around the X axis. + //! 1 0 0 + //! RX = 0 cx sx + //! 0 -sx cx + void RotX(float angle); + //! Set a rotation matrix around the Y axis. + //! cy 0 -sy + //! RY = 0 1 0 + //! sy 0 cy + void RotY(float angle); + //! Set a rotation matrix around the Z axis. + //! cz sz 0 + //! RZ = -sz cz 0 + //! 0 0 1 + void RotZ(float angle); + //! cy sx.sy -sy.cx + //! RY.RX 0 cx sx + //! sy -sx.cy cx.cy + void RotYX(float y, float x); + + //! Make a rotation matrix about an arbitrary axis + Matrix3x3& Rot(float angle, const Point& axis); + + //! Transpose the matrix. + void Transpose() + { + IR(m[1][0]) ^= IR(m[0][1]); IR(m[0][1]) ^= IR(m[1][0]); IR(m[1][0]) ^= IR(m[0][1]); + IR(m[2][0]) ^= IR(m[0][2]); IR(m[0][2]) ^= IR(m[2][0]); IR(m[2][0]) ^= IR(m[0][2]); + IR(m[2][1]) ^= IR(m[1][2]); IR(m[1][2]) ^= IR(m[2][1]); IR(m[2][1]) ^= IR(m[1][2]); + } + + //! this = Transpose(a) + void Transpose(const Matrix3x3& a) + { + m[0][0] = a.m[0][0]; m[0][1] = a.m[1][0]; m[0][2] = a.m[2][0]; + m[1][0] = a.m[0][1]; m[1][1] = a.m[1][1]; m[1][2] = a.m[2][1]; + m[2][0] = a.m[0][2]; m[2][1] = a.m[1][2]; m[2][2] = a.m[2][2]; + } + + //! Compute the determinant of the matrix. We use the rule of Sarrus. + float Determinant() const + { + return (m[0][0]*m[1][1]*m[2][2] + m[0][1]*m[1][2]*m[2][0] + m[0][2]*m[1][0]*m[2][1]) + - (m[2][0]*m[1][1]*m[0][2] + m[2][1]*m[1][2]*m[0][0] + m[2][2]*m[1][0]*m[0][1]); + } +/* + //! Compute a cofactor. Used for matrix inversion. + float CoFactor(ubyte row, ubyte column) const + { + static sdword gIndex[3+2] = { 0, 1, 2, 0, 1 }; + return (m[gIndex[row+1]][gIndex[column+1]]*m[gIndex[row+2]][gIndex[column+2]] - m[gIndex[row+2]][gIndex[column+1]]*m[gIndex[row+1]][gIndex[column+2]]); + } +*/ + //! Invert the matrix. Determinant must be different from zero, else matrix can't be inverted. + Matrix3x3& Invert() + { + float Det = Determinant(); // Must be !=0 + float OneOverDet = 1.0f / Det; + + Matrix3x3 Temp; + Temp.m[0][0] = +(m[1][1] * m[2][2] - m[2][1] * m[1][2]) * OneOverDet; + Temp.m[1][0] = -(m[1][0] * m[2][2] - m[2][0] * m[1][2]) * OneOverDet; + Temp.m[2][0] = +(m[1][0] * m[2][1] - m[2][0] * m[1][1]) * OneOverDet; + Temp.m[0][1] = -(m[0][1] * m[2][2] - m[2][1] * m[0][2]) * OneOverDet; + Temp.m[1][1] = +(m[0][0] * m[2][2] - m[2][0] * m[0][2]) * OneOverDet; + Temp.m[2][1] = -(m[0][0] * m[2][1] - m[2][0] * m[0][1]) * OneOverDet; + Temp.m[0][2] = +(m[0][1] * m[1][2] - m[1][1] * m[0][2]) * OneOverDet; + Temp.m[1][2] = -(m[0][0] * m[1][2] - m[1][0] * m[0][2]) * OneOverDet; + Temp.m[2][2] = +(m[0][0] * m[1][1] - m[1][0] * m[0][1]) * OneOverDet; + + *this = Temp; + + return *this; + } + + Matrix3x3& Normalize(); + + //! this = exp(a) + Matrix3x3& Exp(const Matrix3x3& a); + +void FromQuat(const Quat &q); +void FromQuatL2(const Quat &q, float l2); + + // Arithmetic operators + //! Operator for Matrix3x3 Plus = Matrix3x3 + Matrix3x3; + inline_ Matrix3x3 operator+(const Matrix3x3& mat) const + { + return Matrix3x3( + m[0][0] + mat.m[0][0], m[0][1] + mat.m[0][1], m[0][2] + mat.m[0][2], + m[1][0] + mat.m[1][0], m[1][1] + mat.m[1][1], m[1][2] + mat.m[1][2], + m[2][0] + mat.m[2][0], m[2][1] + mat.m[2][1], m[2][2] + mat.m[2][2]); + } + + //! Operator for Matrix3x3 Minus = Matrix3x3 - Matrix3x3; + inline_ Matrix3x3 operator-(const Matrix3x3& mat) const + { + return Matrix3x3( + m[0][0] - mat.m[0][0], m[0][1] - mat.m[0][1], m[0][2] - mat.m[0][2], + m[1][0] - mat.m[1][0], m[1][1] - mat.m[1][1], m[1][2] - mat.m[1][2], + m[2][0] - mat.m[2][0], m[2][1] - mat.m[2][1], m[2][2] - mat.m[2][2]); + } + + //! Operator for Matrix3x3 Mul = Matrix3x3 * Matrix3x3; + inline_ Matrix3x3 operator*(const Matrix3x3& mat) const + { + return Matrix3x3( + m[0][0]*mat.m[0][0] + m[0][1]*mat.m[1][0] + m[0][2]*mat.m[2][0], + m[0][0]*mat.m[0][1] + m[0][1]*mat.m[1][1] + m[0][2]*mat.m[2][1], + m[0][0]*mat.m[0][2] + m[0][1]*mat.m[1][2] + m[0][2]*mat.m[2][2], + + m[1][0]*mat.m[0][0] + m[1][1]*mat.m[1][0] + m[1][2]*mat.m[2][0], + m[1][0]*mat.m[0][1] + m[1][1]*mat.m[1][1] + m[1][2]*mat.m[2][1], + m[1][0]*mat.m[0][2] + m[1][1]*mat.m[1][2] + m[1][2]*mat.m[2][2], + + m[2][0]*mat.m[0][0] + m[2][1]*mat.m[1][0] + m[2][2]*mat.m[2][0], + m[2][0]*mat.m[0][1] + m[2][1]*mat.m[1][1] + m[2][2]*mat.m[2][1], + m[2][0]*mat.m[0][2] + m[2][1]*mat.m[1][2] + m[2][2]*mat.m[2][2]); + } + + //! Operator for Point Mul = Matrix3x3 * Point; + inline_ Point operator*(const Point& v) const { return Point(GetRow(0)|v, GetRow(1)|v, GetRow(2)|v); } + + //! Operator for Matrix3x3 Mul = Matrix3x3 * float; + inline_ Matrix3x3 operator*(float s) const + { + return Matrix3x3( + m[0][0]*s, m[0][1]*s, m[0][2]*s, + m[1][0]*s, m[1][1]*s, m[1][2]*s, + m[2][0]*s, m[2][1]*s, m[2][2]*s); + } + + //! Operator for Matrix3x3 Mul = float * Matrix3x3; + inline_ friend Matrix3x3 operator*(float s, const Matrix3x3& mat) + { + return Matrix3x3( + s*mat.m[0][0], s*mat.m[0][1], s*mat.m[0][2], + s*mat.m[1][0], s*mat.m[1][1], s*mat.m[1][2], + s*mat.m[2][0], s*mat.m[2][1], s*mat.m[2][2]); + } + + //! Operator for Matrix3x3 Div = Matrix3x3 / float; + inline_ Matrix3x3 operator/(float s) const + { + if (s) s = 1.0f / s; + return Matrix3x3( + m[0][0]*s, m[0][1]*s, m[0][2]*s, + m[1][0]*s, m[1][1]*s, m[1][2]*s, + m[2][0]*s, m[2][1]*s, m[2][2]*s); + } + + //! Operator for Matrix3x3 Div = float / Matrix3x3; + inline_ friend Matrix3x3 operator/(float s, const Matrix3x3& mat) + { + return Matrix3x3( + s/mat.m[0][0], s/mat.m[0][1], s/mat.m[0][2], + s/mat.m[1][0], s/mat.m[1][1], s/mat.m[1][2], + s/mat.m[2][0], s/mat.m[2][1], s/mat.m[2][2]); + } + + //! Operator for Matrix3x3 += Matrix3x3 + inline_ Matrix3x3& operator+=(const Matrix3x3& mat) + { + m[0][0] += mat.m[0][0]; m[0][1] += mat.m[0][1]; m[0][2] += mat.m[0][2]; + m[1][0] += mat.m[1][0]; m[1][1] += mat.m[1][1]; m[1][2] += mat.m[1][2]; + m[2][0] += mat.m[2][0]; m[2][1] += mat.m[2][1]; m[2][2] += mat.m[2][2]; + return *this; + } + + //! Operator for Matrix3x3 -= Matrix3x3 + inline_ Matrix3x3& operator-=(const Matrix3x3& mat) + { + m[0][0] -= mat.m[0][0]; m[0][1] -= mat.m[0][1]; m[0][2] -= mat.m[0][2]; + m[1][0] -= mat.m[1][0]; m[1][1] -= mat.m[1][1]; m[1][2] -= mat.m[1][2]; + m[2][0] -= mat.m[2][0]; m[2][1] -= mat.m[2][1]; m[2][2] -= mat.m[2][2]; + return *this; + } + + //! Operator for Matrix3x3 *= Matrix3x3 + inline_ Matrix3x3& operator*=(const Matrix3x3& mat) + { + Point TempRow; + + GetRow(0, TempRow); + m[0][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0]; + m[0][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1]; + m[0][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2]; + + GetRow(1, TempRow); + m[1][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0]; + m[1][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1]; + m[1][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2]; + + GetRow(2, TempRow); + m[2][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0]; + m[2][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1]; + m[2][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2]; + return *this; + } + + //! Operator for Matrix3x3 *= float + inline_ Matrix3x3& operator*=(float s) + { + m[0][0] *= s; m[0][1] *= s; m[0][2] *= s; + m[1][0] *= s; m[1][1] *= s; m[1][2] *= s; + m[2][0] *= s; m[2][1] *= s; m[2][2] *= s; + return *this; + } + + //! Operator for Matrix3x3 /= float + inline_ Matrix3x3& operator/=(float s) + { + if (s) s = 1.0f / s; + m[0][0] *= s; m[0][1] *= s; m[0][2] *= s; + m[1][0] *= s; m[1][1] *= s; m[1][2] *= s; + m[2][0] *= s; m[2][1] *= s; m[2][2] *= s; + return *this; + } + + // Cast operators + //! Cast a Matrix3x3 to a Matrix4x4. + operator Matrix4x4() const; + //! Cast a Matrix3x3 to a Quat. + operator Quat() const; + + inline_ const Point& operator[](int row) const { return *(const Point*)&m[row][0]; } + inline_ Point& operator[](int row) { return *(Point*)&m[row][0]; } + + public: + + float m[3][3]; + }; + +#endif // __ICEMATRIX3X3_H__ + diff --git a/Engine/lib/opcode/Ice/IceMatrix4x4.cpp b/Engine/lib/opcode/Ice/IceMatrix4x4.cpp new file mode 100644 index 000000000..aab9b7fe5 --- /dev/null +++ b/Engine/lib/opcode/Ice/IceMatrix4x4.cpp @@ -0,0 +1,134 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for 4x4 matrices. + * \file IceMatrix4x4.cpp + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * 4x4 matrix. + * DirectX-compliant, ie row-column order, ie m[Row][Col]. + * Same as: + * m11 m12 m13 m14 first row. + * m21 m22 m23 m24 second row. + * m31 m32 m33 m34 third row. + * m41 m42 m43 m44 fourth row. + * Translation is (m41, m42, m43), (m14, m24, m34, m44) = (0, 0, 0, 1). + * Stored in memory as m11 m12 m13 m14 m21... + * + * Multiplication rules: + * + * [x'y'z'1] = [xyz1][M] + * + * x' = x*m11 + y*m21 + z*m31 + m41 + * y' = x*m12 + y*m22 + z*m32 + m42 + * z' = x*m13 + y*m23 + z*m33 + m43 + * 1' = 0 + 0 + 0 + m44 + * + * \class Matrix4x4 + * \author Pierre Terdiman + * \version 1.0 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "../Opcode.h" + +using namespace IceMaths; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Inverts a PR matrix. (which only contains a rotation and a translation) + * This is faster and less subject to FPU errors than the generic inversion code. + * + * \relates Matrix4x4 + * \fn InvertPRMatrix(Matrix4x4& dest, const Matrix4x4& src) + * \param dest [out] destination matrix + * \param src [in] source matrix + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +ICEMATHS_API void IceMaths::InvertPRMatrix(Matrix4x4& dest, const Matrix4x4& src) +{ + dest.m[0][0] = src.m[0][0]; + dest.m[1][0] = src.m[0][1]; + dest.m[2][0] = src.m[0][2]; + dest.m[3][0] = -(src.m[3][0]*src.m[0][0] + src.m[3][1]*src.m[0][1] + src.m[3][2]*src.m[0][2]); + + dest.m[0][1] = src.m[1][0]; + dest.m[1][1] = src.m[1][1]; + dest.m[2][1] = src.m[1][2]; + dest.m[3][1] = -(src.m[3][0]*src.m[1][0] + src.m[3][1]*src.m[1][1] + src.m[3][2]*src.m[1][2]); + + dest.m[0][2] = src.m[2][0]; + dest.m[1][2] = src.m[2][1]; + dest.m[2][2] = src.m[2][2]; + dest.m[3][2] = -(src.m[3][0]*src.m[2][0] + src.m[3][1]*src.m[2][1] + src.m[3][2]*src.m[2][2]); + + dest.m[0][3] = 0.0f; + dest.m[1][3] = 0.0f; + dest.m[2][3] = 0.0f; + dest.m[3][3] = 1.0f; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Compute the cofactor of the Matrix at a specified location +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float Matrix4x4::CoFactor(udword row, udword col) const +{ + return (( m[(row+1)&3][(col+1)&3]*m[(row+2)&3][(col+2)&3]*m[(row+3)&3][(col+3)&3] + + m[(row+1)&3][(col+2)&3]*m[(row+2)&3][(col+3)&3]*m[(row+3)&3][(col+1)&3] + + m[(row+1)&3][(col+3)&3]*m[(row+2)&3][(col+1)&3]*m[(row+3)&3][(col+2)&3]) + - (m[(row+3)&3][(col+1)&3]*m[(row+2)&3][(col+2)&3]*m[(row+1)&3][(col+3)&3] + + m[(row+3)&3][(col+2)&3]*m[(row+2)&3][(col+3)&3]*m[(row+1)&3][(col+1)&3] + + m[(row+3)&3][(col+3)&3]*m[(row+2)&3][(col+1)&3]*m[(row+1)&3][(col+2)&3])) * ((row + col) & 1 ? -1.0f : +1.0f); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Compute the determinant of the Matrix +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float Matrix4x4::Determinant() const +{ + return m[0][0] * CoFactor(0, 0) + + m[0][1] * CoFactor(0, 1) + + m[0][2] * CoFactor(0, 2) + + m[0][3] * CoFactor(0, 3); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Compute the inverse of the matrix +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Matrix4x4& Matrix4x4::Invert() +{ + float Det = Determinant(); + Matrix4x4 Temp; + + if(fabsf(Det) < MATRIX4X4_EPSILON) + return *this; // The matrix is not invertible! Singular case! + + float IDet = 1.0f / Det; + + Temp.m[0][0] = CoFactor(0,0) * IDet; + Temp.m[1][0] = CoFactor(0,1) * IDet; + Temp.m[2][0] = CoFactor(0,2) * IDet; + Temp.m[3][0] = CoFactor(0,3) * IDet; + Temp.m[0][1] = CoFactor(1,0) * IDet; + Temp.m[1][1] = CoFactor(1,1) * IDet; + Temp.m[2][1] = CoFactor(1,2) * IDet; + Temp.m[3][1] = CoFactor(1,3) * IDet; + Temp.m[0][2] = CoFactor(2,0) * IDet; + Temp.m[1][2] = CoFactor(2,1) * IDet; + Temp.m[2][2] = CoFactor(2,2) * IDet; + Temp.m[3][2] = CoFactor(2,3) * IDet; + Temp.m[0][3] = CoFactor(3,0) * IDet; + Temp.m[1][3] = CoFactor(3,1) * IDet; + Temp.m[2][3] = CoFactor(3,2) * IDet; + Temp.m[3][3] = CoFactor(3,3) * IDet; + + *this = Temp; + + return *this; +} + diff --git a/Engine/lib/opcode/Ice/IceMatrix4x4.h b/Engine/lib/opcode/Ice/IceMatrix4x4.h new file mode 100644 index 000000000..45919be79 --- /dev/null +++ b/Engine/lib/opcode/Ice/IceMatrix4x4.h @@ -0,0 +1,455 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for 4x4 matrices. + * \file IceMatrix4x4.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEMATRIX4X4_H__ +#define __ICEMATRIX4X4_H__ + + // Forward declarations + class PRS; + class PR; + + #define MATRIX4X4_EPSILON (1.0e-7f) + + class ICEMATHS_API Matrix4x4 + { +// void LUBackwardSubstitution( sdword *indx, float* b ); +// void LUDecomposition( sdword* indx, float* d ); + + public: + //! Empty constructor. + inline_ Matrix4x4() {} + //! Constructor from 16 values + inline_ Matrix4x4( float m00, float m01, float m02, float m03, + float m10, float m11, float m12, float m13, + float m20, float m21, float m22, float m23, + float m30, float m31, float m32, float m33) + { + m[0][0] = m00; m[0][1] = m01; m[0][2] = m02; m[0][3] = m03; + m[1][0] = m10; m[1][1] = m11; m[1][2] = m12; m[1][3] = m13; + m[2][0] = m20; m[2][1] = m21; m[2][2] = m22; m[2][3] = m23; + m[3][0] = m30; m[3][1] = m31; m[3][2] = m32; m[3][3] = m33; + } + //! Copy constructor + inline_ Matrix4x4(const Matrix4x4& mat) { CopyMemory(m, &mat.m, 16*sizeof(float)); } + //! Destructor. + inline_ ~Matrix4x4() {} + + //! Assign values (rotation only) + inline_ Matrix4x4& Set( float m00, float m01, float m02, + float m10, float m11, float m12, + float m20, float m21, float m22) + { + m[0][0] = m00; m[0][1] = m01; m[0][2] = m02; + m[1][0] = m10; m[1][1] = m11; m[1][2] = m12; + m[2][0] = m20; m[2][1] = m21; m[2][2] = m22; + return *this; + } + //! Assign values + inline_ Matrix4x4& Set( float m00, float m01, float m02, float m03, + float m10, float m11, float m12, float m13, + float m20, float m21, float m22, float m23, + float m30, float m31, float m32, float m33) + { + m[0][0] = m00; m[0][1] = m01; m[0][2] = m02; m[0][3] = m03; + m[1][0] = m10; m[1][1] = m11; m[1][2] = m12; m[1][3] = m13; + m[2][0] = m20; m[2][1] = m21; m[2][2] = m22; m[2][3] = m23; + m[3][0] = m30; m[3][1] = m31; m[3][2] = m32; m[3][3] = m33; + return *this; + } + + //! Copy from a Matrix4x4 + inline_ void Copy(const Matrix4x4& source) { CopyMemory(m, source.m, 16*sizeof(float)); } + + // Row-column access + //! Returns a row. + inline_ void GetRow(const udword r, HPoint& p) const { p.x=m[r][0]; p.y=m[r][1]; p.z=m[r][2]; p.w=m[r][3]; } + //! Returns a row. + inline_ void GetRow(const udword r, Point& p) const { p.x=m[r][0]; p.y=m[r][1]; p.z=m[r][2]; } + //! Returns a row. + inline_ const HPoint& GetRow(const udword r) const { return *(const HPoint*)&m[r][0]; } + //! Returns a row. + inline_ HPoint& GetRow(const udword r) { return *(HPoint*)&m[r][0]; } + //! Sets a row. + inline_ void SetRow(const udword r, const HPoint& p) { m[r][0]=p.x; m[r][1]=p.y; m[r][2]=p.z; m[r][3]=p.w; } + //! Sets a row. + inline_ void SetRow(const udword r, const Point& p) { m[r][0]=p.x; m[r][1]=p.y; m[r][2]=p.z; m[r][3]= (r!=3) ? 0.0f : 1.0f; } + //! Returns a column. + inline_ void GetCol(const udword c, HPoint& p) const { p.x=m[0][c]; p.y=m[1][c]; p.z=m[2][c]; p.w=m[3][c]; } + //! Returns a column. + inline_ void GetCol(const udword c, Point& p) const { p.x=m[0][c]; p.y=m[1][c]; p.z=m[2][c]; } + //! Sets a column. + inline_ void SetCol(const udword c, const HPoint& p) { m[0][c]=p.x; m[1][c]=p.y; m[2][c]=p.z; m[3][c]=p.w; } + //! Sets a column. + inline_ void SetCol(const udword c, const Point& p) { m[0][c]=p.x; m[1][c]=p.y; m[2][c]=p.z; m[3][c]= (c!=3) ? 0.0f : 1.0f; } + + // Translation + //! Returns the translation part of the matrix. + inline_ const HPoint& GetTrans() const { return GetRow(3); } + //! Gets the translation part of the matrix + inline_ void GetTrans(Point& p) const { p.x=m[3][0]; p.y=m[3][1]; p.z=m[3][2]; } + //! Sets the translation part of the matrix, from a Point. + inline_ void SetTrans(const Point& p) { m[3][0]=p.x; m[3][1]=p.y; m[3][2]=p.z; } + //! Sets the translation part of the matrix, from a HPoint. + inline_ void SetTrans(const HPoint& p) { m[3][0]=p.x; m[3][1]=p.y; m[3][2]=p.z; m[3][3]=p.w; } + //! Sets the translation part of the matrix, from floats. + inline_ void SetTrans(float tx, float ty, float tz) { m[3][0]=tx; m[3][1]=ty; m[3][2]=tz; } + + // Scale + //! Sets the scale from a Point. The point is put on the diagonal. + inline_ void SetScale(const Point& p) { m[0][0]=p.x; m[1][1]=p.y; m[2][2]=p.z; } + //! Sets the scale from floats. Values are put on the diagonal. + inline_ void SetScale(float sx, float sy, float sz) { m[0][0]=sx; m[1][1]=sy; m[2][2]=sz; } + //! Scales from a Point. Each row is multiplied by a component. + void Scale(const Point& p) + { + m[0][0] *= p.x; m[1][0] *= p.y; m[2][0] *= p.z; + m[0][1] *= p.x; m[1][1] *= p.y; m[2][1] *= p.z; + m[0][2] *= p.x; m[1][2] *= p.y; m[2][2] *= p.z; + } + //! Scales from floats. Each row is multiplied by a value. + void Scale(float sx, float sy, float sz) + { + m[0][0] *= sx; m[1][0] *= sy; m[2][0] *= sz; + m[0][1] *= sx; m[1][1] *= sy; m[2][1] *= sz; + m[0][2] *= sx; m[1][2] *= sy; m[2][2] *= sz; + } +/* + //! Returns a row. + inline_ HPoint GetRow(const udword row) const { return mRow[row]; } + //! Sets a row. + inline_ Matrix4x4& SetRow(const udword row, const HPoint& p) { mRow[row] = p; return *this; } + //! Sets a row. + Matrix4x4& SetRow(const udword row, const Point& p) + { + m[row][0] = p.x; + m[row][1] = p.y; + m[row][2] = p.z; + m[row][3] = (row != 3) ? 0.0f : 1.0f; + return *this; + } + //! Returns a column. + HPoint GetCol(const udword col) const + { + HPoint Res; + Res.x = m[0][col]; + Res.y = m[1][col]; + Res.z = m[2][col]; + Res.w = m[3][col]; + return Res; + } + //! Sets a column. + Matrix4x4& SetCol(const udword col, const HPoint& p) + { + m[0][col] = p.x; + m[1][col] = p.y; + m[2][col] = p.z; + m[3][col] = p.w; + return *this; + } + //! Sets a column. + Matrix4x4& SetCol(const udword col, const Point& p) + { + m[0][col] = p.x; + m[1][col] = p.y; + m[2][col] = p.z; + m[3][col] = (col != 3) ? 0.0f : 1.0f; + return *this; + } +*/ + //! Computes the trace. The trace is the sum of the 4 diagonal components. + inline_ float Trace() const { return m[0][0] + m[1][1] + m[2][2] + m[3][3]; } + //! Computes the trace of the upper 3x3 matrix. + inline_ float Trace3x3() const { return m[0][0] + m[1][1] + m[2][2]; } + //! Clears the matrix. + inline_ void Zero() { ZeroMemory(&m, sizeof(m)); } + //! Sets the identity matrix. + inline_ void Identity() { Zero(); m[0][0] = m[1][1] = m[2][2] = m[3][3] = 1.0f; } + //! Checks for identity + inline_ bool IsIdentity() const + { + if(IR(m[0][0])!=IEEE_1_0) return false; + if(IR(m[0][1])!=0) return false; + if(IR(m[0][2])!=0) return false; + if(IR(m[0][3])!=0) return false; + + if(IR(m[1][0])!=0) return false; + if(IR(m[1][1])!=IEEE_1_0) return false; + if(IR(m[1][2])!=0) return false; + if(IR(m[1][3])!=0) return false; + + if(IR(m[2][0])!=0) return false; + if(IR(m[2][1])!=0) return false; + if(IR(m[2][2])!=IEEE_1_0) return false; + if(IR(m[2][3])!=0) return false; + + if(IR(m[3][0])!=0) return false; + if(IR(m[3][1])!=0) return false; + if(IR(m[3][2])!=0) return false; + if(IR(m[3][3])!=IEEE_1_0) return false; + return true; + } + + //! Checks matrix validity + inline_ BOOL IsValid() const + { + for(udword j=0;j<4;j++) + { + for(udword i=0;i<4;i++) + { + if(!IsValidFloat(m[j][i])) return FALSE; + } + } + return TRUE; + } + + //! Sets a rotation matrix around the X axis. + void RotX(float angle) { float Cos = cosf(angle), Sin = sinf(angle); Identity(); m[1][1] = m[2][2] = Cos; m[2][1] = -Sin; m[1][2] = Sin; } + //! Sets a rotation matrix around the Y axis. + void RotY(float angle) { float Cos = cosf(angle), Sin = sinf(angle); Identity(); m[0][0] = m[2][2] = Cos; m[2][0] = Sin; m[0][2] = -Sin; } + //! Sets a rotation matrix around the Z axis. + void RotZ(float angle) { float Cos = cosf(angle), Sin = sinf(angle); Identity(); m[0][0] = m[1][1] = Cos; m[1][0] = -Sin; m[0][1] = Sin; } + + //! Makes a rotation matrix about an arbitrary axis + Matrix4x4& Rot(float angle, Point& p1, Point& p2); + + //! Transposes the matrix. + void Transpose() + { + IR(m[1][0]) ^= IR(m[0][1]); IR(m[0][1]) ^= IR(m[1][0]); IR(m[1][0]) ^= IR(m[0][1]); + IR(m[2][0]) ^= IR(m[0][2]); IR(m[0][2]) ^= IR(m[2][0]); IR(m[2][0]) ^= IR(m[0][2]); + IR(m[3][0]) ^= IR(m[0][3]); IR(m[0][3]) ^= IR(m[3][0]); IR(m[3][0]) ^= IR(m[0][3]); + IR(m[1][2]) ^= IR(m[2][1]); IR(m[2][1]) ^= IR(m[1][2]); IR(m[1][2]) ^= IR(m[2][1]); + IR(m[1][3]) ^= IR(m[3][1]); IR(m[3][1]) ^= IR(m[1][3]); IR(m[1][3]) ^= IR(m[3][1]); + IR(m[2][3]) ^= IR(m[3][2]); IR(m[3][2]) ^= IR(m[2][3]); IR(m[2][3]) ^= IR(m[3][2]); + } + + //! Computes a cofactor. Used for matrix inversion. + float CoFactor(udword row, udword col) const; + //! Computes the determinant of the matrix. + float Determinant() const; + //! Inverts the matrix. Determinant must be different from zero, else matrix can't be inverted. + Matrix4x4& Invert(); +// Matrix& ComputeAxisMatrix(Point& axis, float angle); + + // Cast operators + //! Casts a Matrix4x4 to a Matrix3x3. + inline_ operator Matrix3x3() const + { + return Matrix3x3( + m[0][0], m[0][1], m[0][2], + m[1][0], m[1][1], m[1][2], + m[2][0], m[2][1], m[2][2]); + } + //! Casts a Matrix4x4 to a Quat. + operator Quat() const; + //! Casts a Matrix4x4 to a PR. + operator PR() const; + + // Arithmetic operators + //! Operator for Matrix4x4 Plus = Matrix4x4 + Matrix4x4; + inline_ Matrix4x4 operator+(const Matrix4x4& mat) const + { + return Matrix4x4( + m[0][0]+mat.m[0][0], m[0][1]+mat.m[0][1], m[0][2]+mat.m[0][2], m[0][3]+mat.m[0][3], + m[1][0]+mat.m[1][0], m[1][1]+mat.m[1][1], m[1][2]+mat.m[1][2], m[1][3]+mat.m[1][3], + m[2][0]+mat.m[2][0], m[2][1]+mat.m[2][1], m[2][2]+mat.m[2][2], m[2][3]+mat.m[2][3], + m[3][0]+mat.m[3][0], m[3][1]+mat.m[3][1], m[3][2]+mat.m[3][2], m[3][3]+mat.m[3][3]); + } + + //! Operator for Matrix4x4 Minus = Matrix4x4 - Matrix4x4; + inline_ Matrix4x4 operator-(const Matrix4x4& mat) const + { + return Matrix4x4( + m[0][0]-mat.m[0][0], m[0][1]-mat.m[0][1], m[0][2]-mat.m[0][2], m[0][3]-mat.m[0][3], + m[1][0]-mat.m[1][0], m[1][1]-mat.m[1][1], m[1][2]-mat.m[1][2], m[1][3]-mat.m[1][3], + m[2][0]-mat.m[2][0], m[2][1]-mat.m[2][1], m[2][2]-mat.m[2][2], m[2][3]-mat.m[2][3], + m[3][0]-mat.m[3][0], m[3][1]-mat.m[3][1], m[3][2]-mat.m[3][2], m[3][3]-mat.m[3][3]); + } + + //! Operator for Matrix4x4 Mul = Matrix4x4 * Matrix4x4; + inline_ Matrix4x4 operator*(const Matrix4x4& mat) const + { + return Matrix4x4( + m[0][0]*mat.m[0][0] + m[0][1]*mat.m[1][0] + m[0][2]*mat.m[2][0] + m[0][3]*mat.m[3][0], + m[0][0]*mat.m[0][1] + m[0][1]*mat.m[1][1] + m[0][2]*mat.m[2][1] + m[0][3]*mat.m[3][1], + m[0][0]*mat.m[0][2] + m[0][1]*mat.m[1][2] + m[0][2]*mat.m[2][2] + m[0][3]*mat.m[3][2], + m[0][0]*mat.m[0][3] + m[0][1]*mat.m[1][3] + m[0][2]*mat.m[2][3] + m[0][3]*mat.m[3][3], + + m[1][0]*mat.m[0][0] + m[1][1]*mat.m[1][0] + m[1][2]*mat.m[2][0] + m[1][3]*mat.m[3][0], + m[1][0]*mat.m[0][1] + m[1][1]*mat.m[1][1] + m[1][2]*mat.m[2][1] + m[1][3]*mat.m[3][1], + m[1][0]*mat.m[0][2] + m[1][1]*mat.m[1][2] + m[1][2]*mat.m[2][2] + m[1][3]*mat.m[3][2], + m[1][0]*mat.m[0][3] + m[1][1]*mat.m[1][3] + m[1][2]*mat.m[2][3] + m[1][3]*mat.m[3][3], + + m[2][0]*mat.m[0][0] + m[2][1]*mat.m[1][0] + m[2][2]*mat.m[2][0] + m[2][3]*mat.m[3][0], + m[2][0]*mat.m[0][1] + m[2][1]*mat.m[1][1] + m[2][2]*mat.m[2][1] + m[2][3]*mat.m[3][1], + m[2][0]*mat.m[0][2] + m[2][1]*mat.m[1][2] + m[2][2]*mat.m[2][2] + m[2][3]*mat.m[3][2], + m[2][0]*mat.m[0][3] + m[2][1]*mat.m[1][3] + m[2][2]*mat.m[2][3] + m[2][3]*mat.m[3][3], + + m[3][0]*mat.m[0][0] + m[3][1]*mat.m[1][0] + m[3][2]*mat.m[2][0] + m[3][3]*mat.m[3][0], + m[3][0]*mat.m[0][1] + m[3][1]*mat.m[1][1] + m[3][2]*mat.m[2][1] + m[3][3]*mat.m[3][1], + m[3][0]*mat.m[0][2] + m[3][1]*mat.m[1][2] + m[3][2]*mat.m[2][2] + m[3][3]*mat.m[3][2], + m[3][0]*mat.m[0][3] + m[3][1]*mat.m[1][3] + m[3][2]*mat.m[2][3] + m[3][3]*mat.m[3][3]); + } + + //! Operator for HPoint Mul = Matrix4x4 * HPoint; + inline_ HPoint operator*(const HPoint& v) const { return HPoint(GetRow(0)|v, GetRow(1)|v, GetRow(2)|v, GetRow(3)|v); } + + //! Operator for Point Mul = Matrix4x4 * Point; + inline_ Point operator*(const Point& v) const + { + return Point( m[0][0]*v.x + m[0][1]*v.y + m[0][2]*v.z + m[0][3], + m[1][0]*v.x + m[1][1]*v.y + m[1][2]*v.z + m[1][3], + m[2][0]*v.x + m[2][1]*v.y + m[2][2]*v.z + m[2][3] ); + } + + //! Operator for Matrix4x4 Scale = Matrix4x4 * float; + inline_ Matrix4x4 operator*(float s) const + { + return Matrix4x4( + m[0][0]*s, m[0][1]*s, m[0][2]*s, m[0][3]*s, + m[1][0]*s, m[1][1]*s, m[1][2]*s, m[1][3]*s, + m[2][0]*s, m[2][1]*s, m[2][2]*s, m[2][3]*s, + m[3][0]*s, m[3][1]*s, m[3][2]*s, m[3][3]*s); + } + + //! Operator for Matrix4x4 Scale = float * Matrix4x4; + inline_ friend Matrix4x4 operator*(float s, const Matrix4x4& mat) + { + return Matrix4x4( + s*mat.m[0][0], s*mat.m[0][1], s*mat.m[0][2], s*mat.m[0][3], + s*mat.m[1][0], s*mat.m[1][1], s*mat.m[1][2], s*mat.m[1][3], + s*mat.m[2][0], s*mat.m[2][1], s*mat.m[2][2], s*mat.m[2][3], + s*mat.m[3][0], s*mat.m[3][1], s*mat.m[3][2], s*mat.m[3][3]); + } + + //! Operator for Matrix4x4 Div = Matrix4x4 / float; + inline_ Matrix4x4 operator/(float s) const + { + if(s) s = 1.0f / s; + + return Matrix4x4( + m[0][0]*s, m[0][1]*s, m[0][2]*s, m[0][3]*s, + m[1][0]*s, m[1][1]*s, m[1][2]*s, m[1][3]*s, + m[2][0]*s, m[2][1]*s, m[2][2]*s, m[2][3]*s, + m[3][0]*s, m[3][1]*s, m[3][2]*s, m[3][3]*s); + } + + //! Operator for Matrix4x4 Div = float / Matrix4x4; + inline_ friend Matrix4x4 operator/(float s, const Matrix4x4& mat) + { + return Matrix4x4( + s/mat.m[0][0], s/mat.m[0][1], s/mat.m[0][2], s/mat.m[0][3], + s/mat.m[1][0], s/mat.m[1][1], s/mat.m[1][2], s/mat.m[1][3], + s/mat.m[2][0], s/mat.m[2][1], s/mat.m[2][2], s/mat.m[2][3], + s/mat.m[3][0], s/mat.m[3][1], s/mat.m[3][2], s/mat.m[3][3]); + } + + //! Operator for Matrix4x4 += Matrix4x4; + inline_ Matrix4x4& operator+=(const Matrix4x4& mat) + { + m[0][0]+=mat.m[0][0]; m[0][1]+=mat.m[0][1]; m[0][2]+=mat.m[0][2]; m[0][3]+=mat.m[0][3]; + m[1][0]+=mat.m[1][0]; m[1][1]+=mat.m[1][1]; m[1][2]+=mat.m[1][2]; m[1][3]+=mat.m[1][3]; + m[2][0]+=mat.m[2][0]; m[2][1]+=mat.m[2][1]; m[2][2]+=mat.m[2][2]; m[2][3]+=mat.m[2][3]; + m[3][0]+=mat.m[3][0]; m[3][1]+=mat.m[3][1]; m[3][2]+=mat.m[3][2]; m[3][3]+=mat.m[3][3]; + return *this; + } + + //! Operator for Matrix4x4 -= Matrix4x4; + inline_ Matrix4x4& operator-=(const Matrix4x4& mat) + { + m[0][0]-=mat.m[0][0]; m[0][1]-=mat.m[0][1]; m[0][2]-=mat.m[0][2]; m[0][3]-=mat.m[0][3]; + m[1][0]-=mat.m[1][0]; m[1][1]-=mat.m[1][1]; m[1][2]-=mat.m[1][2]; m[1][3]-=mat.m[1][3]; + m[2][0]-=mat.m[2][0]; m[2][1]-=mat.m[2][1]; m[2][2]-=mat.m[2][2]; m[2][3]-=mat.m[2][3]; + m[3][0]-=mat.m[3][0]; m[3][1]-=mat.m[3][1]; m[3][2]-=mat.m[3][2]; m[3][3]-=mat.m[3][3]; + return *this; + } + + //! Operator for Matrix4x4 *= Matrix4x4; + Matrix4x4& operator*=(const Matrix4x4& mat) + { + HPoint TempRow; + + GetRow(0, TempRow); + m[0][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0] + TempRow.w*mat.m[3][0]; + m[0][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1] + TempRow.w*mat.m[3][1]; + m[0][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2] + TempRow.w*mat.m[3][2]; + m[0][3] = TempRow.x*mat.m[0][3] + TempRow.y*mat.m[1][3] + TempRow.z*mat.m[2][3] + TempRow.w*mat.m[3][3]; + + GetRow(1, TempRow); + m[1][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0] + TempRow.w*mat.m[3][0]; + m[1][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1] + TempRow.w*mat.m[3][1]; + m[1][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2] + TempRow.w*mat.m[3][2]; + m[1][3] = TempRow.x*mat.m[0][3] + TempRow.y*mat.m[1][3] + TempRow.z*mat.m[2][3] + TempRow.w*mat.m[3][3]; + + GetRow(2, TempRow); + m[2][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0] + TempRow.w*mat.m[3][0]; + m[2][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1] + TempRow.w*mat.m[3][1]; + m[2][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2] + TempRow.w*mat.m[3][2]; + m[2][3] = TempRow.x*mat.m[0][3] + TempRow.y*mat.m[1][3] + TempRow.z*mat.m[2][3] + TempRow.w*mat.m[3][3]; + + GetRow(3, TempRow); + m[3][0] = TempRow.x*mat.m[0][0] + TempRow.y*mat.m[1][0] + TempRow.z*mat.m[2][0] + TempRow.w*mat.m[3][0]; + m[3][1] = TempRow.x*mat.m[0][1] + TempRow.y*mat.m[1][1] + TempRow.z*mat.m[2][1] + TempRow.w*mat.m[3][1]; + m[3][2] = TempRow.x*mat.m[0][2] + TempRow.y*mat.m[1][2] + TempRow.z*mat.m[2][2] + TempRow.w*mat.m[3][2]; + m[3][3] = TempRow.x*mat.m[0][3] + TempRow.y*mat.m[1][3] + TempRow.z*mat.m[2][3] + TempRow.w*mat.m[3][3]; + + return *this; + } + + //! Operator for Matrix4x4 *= float; + inline_ Matrix4x4& operator*=(float s) + { + m[0][0]*=s; m[0][1]*=s; m[0][2]*=s; m[0][3]*=s; + m[1][0]*=s; m[1][1]*=s; m[1][2]*=s; m[1][3]*=s; + m[2][0]*=s; m[2][1]*=s; m[2][2]*=s; m[2][3]*=s; + m[3][0]*=s; m[3][1]*=s; m[3][2]*=s; m[3][3]*=s; + return *this; + } + + //! Operator for Matrix4x4 /= float; + inline_ Matrix4x4& operator/=(float s) + { + if(s) s = 1.0f / s; + m[0][0]*=s; m[0][1]*=s; m[0][2]*=s; m[0][3]*=s; + m[1][0]*=s; m[1][1]*=s; m[1][2]*=s; m[1][3]*=s; + m[2][0]*=s; m[2][1]*=s; m[2][2]*=s; m[2][3]*=s; + m[3][0]*=s; m[3][1]*=s; m[3][2]*=s; m[3][3]*=s; + return *this; + } + + inline_ const HPoint& operator[](int row) const { return *(const HPoint*)&m[row][0]; } + inline_ HPoint& operator[](int row) { return *(HPoint*)&m[row][0]; } + + public: + + float m[4][4]; + }; + + //! Quickly rotates & translates a vector, using the 4x3 part of a 4x4 matrix + inline_ void TransformPoint4x3(Point& dest, const Point& source, const Matrix4x4& rot) + { + dest.x = rot.m[3][0] + source.x * rot.m[0][0] + source.y * rot.m[1][0] + source.z * rot.m[2][0]; + dest.y = rot.m[3][1] + source.x * rot.m[0][1] + source.y * rot.m[1][1] + source.z * rot.m[2][1]; + dest.z = rot.m[3][2] + source.x * rot.m[0][2] + source.y * rot.m[1][2] + source.z * rot.m[2][2]; + } + + //! Quickly rotates a vector, using the 3x3 part of a 4x4 matrix + inline_ void TransformPoint3x3(Point& dest, const Point& source, const Matrix4x4& rot) + { + dest.x = source.x * rot.m[0][0] + source.y * rot.m[1][0] + source.z * rot.m[2][0]; + dest.y = source.x * rot.m[0][1] + source.y * rot.m[1][1] + source.z * rot.m[2][1]; + dest.z = source.x * rot.m[0][2] + source.y * rot.m[1][2] + source.z * rot.m[2][2]; + } + + ICEMATHS_API void InvertPRMatrix(Matrix4x4& dest, const Matrix4x4& src); + +#endif // __ICEMATRIX4X4_H__ + diff --git a/Engine/lib/opcode/Ice/IceMemoryMacros.h b/Engine/lib/opcode/Ice/IceMemoryMacros.h new file mode 100644 index 000000000..f0f27d5ed --- /dev/null +++ b/Engine/lib/opcode/Ice/IceMemoryMacros.h @@ -0,0 +1,110 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains all memory macros. + * \file IceMemoryMacros.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEMEMORYMACROS_H__ +#define __ICEMEMORYMACROS_H__ + +#undef ZeroMemory +#undef CopyMemory +#undef MoveMemory +#undef FillMemory + + //! Clears a buffer. + //! \param addr [in] buffer address + //! \param size [in] buffer length + //! \see FillMemory + //! \see StoreDwords + //! \see CopyMemory + //! \see MoveMemory + inline_ void ZeroMemory(void* addr, udword size) { memset(addr, 0, size); } + + //! Fills a buffer with a given byte. + //! \param addr [in] buffer address + //! \param size [in] buffer length + //! \param val [in] the byte value + //! \see StoreDwords + //! \see ZeroMemory + //! \see CopyMemory + //! \see MoveMemory + inline_ void FillMemory(void* dest, udword size, ubyte val) { memset(dest, val, size); } + + //! Fills a buffer with a given dword. + //! \param addr [in] buffer address + //! \param nb [in] number of dwords to write + //! \param value [in] the dword value + //! \see FillMemory + //! \see ZeroMemory + //! \see CopyMemory + //! \see MoveMemory + //! \warning writes nb*4 bytes ! + inline_ void StoreDwords(udword* dest, udword nb, udword value) + { +#ifdef _WIN32 + // The asm code below **SHOULD** be equivalent to one of those C versions + // or the other if your compiled is good: (checked on VC++ 6.0) + // + // 1) while(nb--) *dest++ = value; + // + // 2) for(udword i=0;iRelease(); (x) = null; } //!< Safe D3D-style release + #define SAFE_DESTRUCT(x) if (x) { (x)->SelfDestruct(); (x) = null; } //!< Safe ICE-style release + +#ifdef __ICEERROR_H__ + #define CHECKALLOC(x) if(!x) return SetIceError("Out of memory.", EC_OUT_OF_MEMORY); //!< Standard alloc checking. HANDLE WITH CARE. +#else + #define CHECKALLOC(x) if(!x) return false; +#endif + + //! Standard allocation cycle + #define SAFE_ALLOC(ptr, type, count) DELETEARRAY(ptr); ptr = new type[count]; CHECKALLOC(ptr); + +#endif // __ICEMEMORYMACROS_H__ diff --git a/Engine/lib/opcode/Ice/IceOBB.cpp b/Engine/lib/opcode/Ice/IceOBB.cpp new file mode 100644 index 000000000..f644dcd4f --- /dev/null +++ b/Engine/lib/opcode/Ice/IceOBB.cpp @@ -0,0 +1,322 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains OBB-related code. + * \file IceOBB.cpp + * \author Pierre Terdiman + * \date January, 29, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * An Oriented Bounding Box (OBB). + * \class OBB + * \author Pierre Terdiman + * \version 1.0 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "../Opcode.h" + +using namespace IceMaths; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Tests if a point is contained within the OBB. + * \param p [in] the world point to test + * \return true if inside the OBB + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool OBB::ContainsPoint(const Point& p) const +{ + // Point in OBB test using lazy evaluation and early exits + + // Translate to box space + Point RelPoint = p - mCenter; + + // Point * mRot maps from box space to world space + // mRot * Point maps from world space to box space (what we need here) + + float f = mRot.m[0][0] * RelPoint.x + mRot.m[0][1] * RelPoint.y + mRot.m[0][2] * RelPoint.z; + if(f >= mExtents.x || f <= -mExtents.x) return false; + + f = mRot.m[1][0] * RelPoint.x + mRot.m[1][1] * RelPoint.y + mRot.m[1][2] * RelPoint.z; + if(f >= mExtents.y || f <= -mExtents.y) return false; + + f = mRot.m[2][0] * RelPoint.x + mRot.m[2][1] * RelPoint.y + mRot.m[2][2] * RelPoint.z; + if(f >= mExtents.z || f <= -mExtents.z) return false; + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Builds an OBB from an AABB and a world transform. + * \param aabb [in] the aabb + * \param mat [in] the world transform + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBB::Create(const AABB& aabb, const Matrix4x4& mat) +{ + // Note: must be coherent with Rotate() + + aabb.GetCenter(mCenter); + aabb.GetExtents(mExtents); + // Here we have the same as OBB::Rotate(mat) where the obb is (mCenter, mExtents, Identity). + + // So following what's done in Rotate: + // - x-form the center + mCenter *= mat; + // - combine rotation with identity, i.e. just use given matrix + mRot = mat; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the obb planes. + * \param planes [out] 6 box planes + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool OBB::ComputePlanes(Plane* planes) const +{ + // Checkings + if(!planes) return false; + + Point Axis0 = mRot[0]; + Point Axis1 = mRot[1]; + Point Axis2 = mRot[2]; + + // Writes normals + planes[0].n = Axis0; + planes[1].n = -Axis0; + planes[2].n = Axis1; + planes[3].n = -Axis1; + planes[4].n = Axis2; + planes[5].n = -Axis2; + + // Compute a point on each plane + Point p0 = mCenter + Axis0 * mExtents.x; + Point p1 = mCenter - Axis0 * mExtents.x; + Point p2 = mCenter + Axis1 * mExtents.y; + Point p3 = mCenter - Axis1 * mExtents.y; + Point p4 = mCenter + Axis2 * mExtents.z; + Point p5 = mCenter - Axis2 * mExtents.z; + + // Compute d + planes[0].d = -(planes[0].n|p0); + planes[1].d = -(planes[1].n|p1); + planes[2].d = -(planes[2].n|p2); + planes[3].d = -(planes[3].n|p3); + planes[4].d = -(planes[4].n|p4); + planes[5].d = -(planes[5].n|p5); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the obb points. + * \param pts [out] 8 box points + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool OBB::ComputePoints(Point* pts) const +{ + // Checkings + if(!pts) return false; + + Point Axis0 = mRot[0]; + Point Axis1 = mRot[1]; + Point Axis2 = mRot[2]; + + Axis0 *= mExtents.x; + Axis1 *= mExtents.y; + Axis2 *= mExtents.z; + + // 7+------+6 0 = --- + // /| /| 1 = +-- + // / | / | 2 = ++- + // / 4+---/--+5 3 = -+- + // 3+------+2 / y z 4 = --+ + // | / | / | / 5 = +-+ + // |/ |/ |/ 6 = +++ + // 0+------+1 *---x 7 = -++ + + pts[0] = mCenter - Axis0 - Axis1 - Axis2; + pts[1] = mCenter + Axis0 - Axis1 - Axis2; + pts[2] = mCenter + Axis0 + Axis1 - Axis2; + pts[3] = mCenter - Axis0 + Axis1 - Axis2; + pts[4] = mCenter - Axis0 - Axis1 + Axis2; + pts[5] = mCenter + Axis0 - Axis1 + Axis2; + pts[6] = mCenter + Axis0 + Axis1 + Axis2; + pts[7] = mCenter - Axis0 + Axis1 + Axis2; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes vertex normals. + * \param pts [out] 8 box points + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool OBB::ComputeVertexNormals(Point* pts) const +{ + static float VertexNormals[] = + { + -INVSQRT3, -INVSQRT3, -INVSQRT3, + INVSQRT3, -INVSQRT3, -INVSQRT3, + INVSQRT3, INVSQRT3, -INVSQRT3, + -INVSQRT3, INVSQRT3, -INVSQRT3, + -INVSQRT3, -INVSQRT3, INVSQRT3, + INVSQRT3, -INVSQRT3, INVSQRT3, + INVSQRT3, INVSQRT3, INVSQRT3, + -INVSQRT3, INVSQRT3, INVSQRT3 + }; + + if(!pts) return false; + + const Point* VN = (const Point*)VertexNormals; + for(udword i=0;i<8;i++) + { + pts[i] = VN[i] * mRot; + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Returns edges. + * \return 24 indices (12 edges) indexing the list returned by ComputePoints() + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +const udword* OBB::GetEdges() const +{ + static udword Indices[] = { + 0, 1, 1, 2, 2, 3, 3, 0, + 7, 6, 6, 5, 5, 4, 4, 7, + 1, 5, 6, 2, + 3, 7, 4, 0 + }; + return Indices; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Returns local edge normals. + * \return edge normals in local space + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +const Point* OBB::GetLocalEdgeNormals() const +{ + static float EdgeNormals[] = + { + 0, -INVSQRT2, -INVSQRT2, // 0-1 + INVSQRT2, 0, -INVSQRT2, // 1-2 + 0, INVSQRT2, -INVSQRT2, // 2-3 + -INVSQRT2, 0, -INVSQRT2, // 3-0 + + 0, INVSQRT2, INVSQRT2, // 7-6 + INVSQRT2, 0, INVSQRT2, // 6-5 + 0, -INVSQRT2, INVSQRT2, // 5-4 + -INVSQRT2, 0, INVSQRT2, // 4-7 + + INVSQRT2, -INVSQRT2, 0, // 1-5 + INVSQRT2, INVSQRT2, 0, // 6-2 + -INVSQRT2, INVSQRT2, 0, // 3-7 + -INVSQRT2, -INVSQRT2, 0 // 4-0 + }; + return (const Point*)EdgeNormals; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Returns world edge normal + * \param edge_index [in] 0 <= edge index < 12 + * \param world_normal [out] edge normal in world space + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBB::ComputeWorldEdgeNormal(udword edge_index, Point& world_normal) const +{ + ASSERT(edge_index<12); + world_normal = GetLocalEdgeNormals()[edge_index] * mRot; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes an LSS surrounding the OBB. + * \param lss [out] the LSS + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBB::ComputeLSS(LSS& lss) const +{ + Point Axis0 = mRot[0]; + Point Axis1 = mRot[1]; + Point Axis2 = mRot[2]; + + switch(mExtents.LargestAxis()) + { + case 0: + lss.mRadius = (mExtents.y + mExtents.z)*0.5f; + lss.mP0 = mCenter + Axis0 * (mExtents.x - lss.mRadius); + lss.mP1 = mCenter - Axis0 * (mExtents.x - lss.mRadius); + break; + case 1: + lss.mRadius = (mExtents.x + mExtents.z)*0.5f; + lss.mP0 = mCenter + Axis1 * (mExtents.y - lss.mRadius); + lss.mP1 = mCenter - Axis1 * (mExtents.y - lss.mRadius); + break; + case 2: + lss.mRadius = (mExtents.x + mExtents.y)*0.5f; + lss.mP0 = mCenter + Axis2 * (mExtents.z - lss.mRadius); + lss.mP1 = mCenter - Axis2 * (mExtents.z - lss.mRadius); + break; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks the OBB is inside another OBB. + * \param box [in] the other OBB + * \return TRUE if we're inside the other box + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +BOOL OBB::IsInside(const OBB& box) const +{ + // Make a 4x4 from the box & inverse it + Matrix4x4 M0Inv; + { + Matrix4x4 M0 = box.mRot; + M0.SetTrans(box.mCenter); + InvertPRMatrix(M0Inv, M0); + } + + // With our inversed 4x4, create box1 in space of box0 + OBB _1in0; + Rotate(M0Inv, _1in0); + + // This should cancel out box0's rotation, i.e. it's now an AABB. + // => Center(0,0,0), Rot(identity) + + // The two boxes are in the same space so now we can compare them. + + // Create the AABB of (box1 in space of box0) + const Matrix3x3& mtx = _1in0.mRot; + + float f = fabsf(mtx.m[0][0] * mExtents.x) + fabsf(mtx.m[1][0] * mExtents.y) + fabsf(mtx.m[2][0] * mExtents.z) - box.mExtents.x; + if(f > _1in0.mCenter.x) return FALSE; + if(-f < _1in0.mCenter.x) return FALSE; + + f = fabsf(mtx.m[0][1] * mExtents.x) + fabsf(mtx.m[1][1] * mExtents.y) + fabsf(mtx.m[2][1] * mExtents.z) - box.mExtents.y; + if(f > _1in0.mCenter.y) return FALSE; + if(-f < _1in0.mCenter.y) return FALSE; + + f = fabsf(mtx.m[0][2] * mExtents.x) + fabsf(mtx.m[1][2] * mExtents.y) + fabsf(mtx.m[2][2] * mExtents.z) - box.mExtents.z; + if(f > _1in0.mCenter.z) return FALSE; + if(-f < _1in0.mCenter.z) return FALSE; + + return TRUE; +} diff --git a/Engine/lib/opcode/Ice/IceOBB.h b/Engine/lib/opcode/Ice/IceOBB.h new file mode 100644 index 000000000..d6cf43e06 --- /dev/null +++ b/Engine/lib/opcode/Ice/IceOBB.h @@ -0,0 +1,177 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains OBB-related code. (oriented bounding box) + * \file IceOBB.h + * \author Pierre Terdiman + * \date January, 13, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEOBB_H__ +#define __ICEOBB_H__ + + // Forward declarations + class LSS; + + class ICEMATHS_API OBB + { + public: + //! Constructor + inline_ OBB() {} + //! Constructor + inline_ OBB(const Point& center, const Point& extents, const Matrix3x3& rot) : mCenter(center), mExtents(extents), mRot(rot) {} + //! Destructor + inline_ ~OBB() {} + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups an empty OBB. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void SetEmpty() + { + mCenter.Zero(); + mExtents.Set(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); + mRot.Identity(); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Tests if a point is contained within the OBB. + * \param p [in] the world point to test + * \return true if inside the OBB + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool ContainsPoint(const Point& p) const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Builds an OBB from an AABB and a world transform. + * \param aabb [in] the aabb + * \param mat [in] the world transform + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void Create(const AABB& aabb, const Matrix4x4& mat); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Recomputes the OBB after an arbitrary transform by a 4x4 matrix. + * \param mtx [in] the transform matrix + * \param obb [out] the transformed OBB + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void Rotate(const Matrix4x4& mtx, OBB& obb) const + { + // The extents remain constant + obb.mExtents = mExtents; + // The center gets x-formed + obb.mCenter = mCenter * mtx; + // Combine rotations + obb.mRot = mRot * Matrix3x3(mtx); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks the OBB is valid. + * \return true if the box is valid + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL IsValid() const + { + // Consistency condition for (Center, Extents) boxes: Extents >= 0.0f + if(mExtents.x < 0.0f) return FALSE; + if(mExtents.y < 0.0f) return FALSE; + if(mExtents.z < 0.0f) return FALSE; + return TRUE; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes the obb planes. + * \param planes [out] 6 box planes + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool ComputePlanes(Plane* planes) const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes the obb points. + * \param pts [out] 8 box points + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool ComputePoints(Point* pts) const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes vertex normals. + * \param pts [out] 8 box points + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool ComputeVertexNormals(Point* pts) const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Returns edges. + * \return 24 indices (12 edges) indexing the list returned by ComputePoints() + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + const udword* GetEdges() const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Returns local edge normals. + * \return edge normals in local space + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + const Point* GetLocalEdgeNormals() const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Returns world edge normal + * \param edge_index [in] 0 <= edge index < 12 + * \param world_normal [out] edge normal in world space + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void ComputeWorldEdgeNormal(udword edge_index, Point& world_normal) const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes an LSS surrounding the OBB. + * \param lss [out] the LSS + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + void ComputeLSS(LSS& lss) const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks the OBB is inside another OBB. + * \param box [in] the other OBB + * \return TRUE if we're inside the other box + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + BOOL IsInside(const OBB& box) const; + + inline_ const Point& GetCenter() const { return mCenter; } + inline_ const Point& GetExtents() const { return mExtents; } + inline_ const Matrix3x3& GetRot() const { return mRot; } + + inline_ void GetRotatedExtents(Matrix3x3& extents) const + { + extents = mRot; + extents.Scale(mExtents); + } + + Point mCenter; //!< B for Box + Point mExtents; //!< B for Bounding + Matrix3x3 mRot; //!< O for Oriented + + // Orientation is stored in row-major format, + // i.e. rows = eigen vectors of the covariance matrix + }; + +#endif // __ICEOBB_H__ diff --git a/Engine/lib/opcode/Ice/IcePairs.h b/Engine/lib/opcode/Ice/IcePairs.h new file mode 100644 index 000000000..3807cf069 --- /dev/null +++ b/Engine/lib/opcode/Ice/IcePairs.h @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a simple pair class. + * \file IcePairs.h + * \author Pierre Terdiman + * \date January, 13, 2003 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEPAIRS_H__ +#define __ICEPAIRS_H__ + + //! A generic couple structure + struct ICECORE_API Pair + { + inline_ Pair() {} + inline_ Pair(udword i0, udword i1) : id0(i0), id1(i1) {} + + udword id0; //!< First index of the pair + udword id1; //!< Second index of the pair + }; + + class ICECORE_API Pairs : private OPC_Container + { + public: + // Constructor / Destructor + Pairs() {} + ~Pairs() {} + + inline_ udword GetNbPairs() const { return GetNbEntries()>>1; } + inline_ const Pair* GetPairs() const { return (const Pair*)GetEntries(); } + inline_ const Pair* GetPair(udword i) const { return (const Pair*)&GetEntries()[i+i]; } + + inline_ BOOL HasPairs() const { return IsNotEmpty(); } + + inline_ void ResetPairs() { Reset(); } + inline_ void DeleteLastPair() { DeleteLastEntry(); DeleteLastEntry(); } + + inline_ void AddPair(const Pair& p) { Add(p.id0).Add(p.id1); } + inline_ void AddPair(udword id0, udword id1) { Add(id0).Add(id1); } + }; + +#endif // __ICEPAIRS_H__ diff --git a/Engine/lib/opcode/Ice/IcePlane.cpp b/Engine/lib/opcode/Ice/IcePlane.cpp new file mode 100644 index 000000000..cce43927d --- /dev/null +++ b/Engine/lib/opcode/Ice/IcePlane.cpp @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for planes. + * \file IcePlane.cpp + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Plane class. + * \class Plane + * \author Pierre Terdiman + * \version 1.0 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "../Opcode.h" + +using namespace IceMaths; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the plane equation from 3 points. + * \param p0 [in] first point + * \param p1 [in] second point + * \param p2 [in] third point + * \return Self-reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Plane& Plane::Set(const Point& p0, const Point& p1, const Point& p2) +{ + Point Edge0 = p1 - p0; + Point Edge1 = p2 - p0; + + n = Edge0 ^ Edge1; + n.Normalize(); + + d = -(p0 | n); + + return *this; +} diff --git a/Engine/lib/opcode/Ice/IcePlane.h b/Engine/lib/opcode/Ice/IcePlane.h new file mode 100644 index 000000000..4d4708141 --- /dev/null +++ b/Engine/lib/opcode/Ice/IcePlane.h @@ -0,0 +1,113 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for planes. + * \file IcePlane.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEPLANE_H__ +#define __ICEPLANE_H__ + + #define PLANE_EPSILON (1.0e-7f) + + class ICEMATHS_API Plane + { + public: + //! Constructor + inline_ Plane() { } + //! Constructor from a normal and a distance + inline_ Plane(float nx, float ny, float nz, float d) { Set(nx, ny, nz, d); } + //! Constructor from a point on the plane and a normal + inline_ Plane(const Point& p, const Point& n) { Set(p, n); } + //! Constructor from three points + inline_ Plane(const Point& p0, const Point& p1, const Point& p2) { Set(p0, p1, p2); } + //! Constructor from a normal and a distance + inline_ Plane(const Point& _n, float _d) { n = _n; d = _d; } + //! Copy constructor + inline_ Plane(const Plane& plane) : n(plane.n), d(plane.d) { } + //! Destructor + inline_ ~Plane() { } + + inline_ Plane& Zero() { n.Zero(); d = 0.0f; return *this; } + inline_ Plane& Set(float nx, float ny, float nz, float _d) { n.Set(nx, ny, nz); d = _d; return *this; } + inline_ Plane& Set(const Point& p, const Point& _n) { n = _n; d = - p | _n; return *this; } + Plane& Set(const Point& p0, const Point& p1, const Point& p2); + + inline_ float Distance(const Point& p) const { return (p | n) + d; } + inline_ bool Belongs(const Point& p) const { return fabsf(Distance(p)) < PLANE_EPSILON; } + + inline_ void Normalize() + { + float Denom = 1.0f / n.Magnitude(); + n.x *= Denom; + n.y *= Denom; + n.z *= Denom; + d *= Denom; + } + public: + // Members + Point n; //!< The normal to the plane + float d; //!< The distance from the origin + + // Cast operators + inline_ operator Point() const { return n; } + inline_ operator HPoint() const { return HPoint(n, d); } + + // Arithmetic operators + inline_ Plane operator*(const Matrix4x4& m) const + { + // Old code from Irion. Kept for reference. + Plane Ret(*this); + return Ret *= m; + } + + inline_ Plane& operator*=(const Matrix4x4& m) + { + // Old code from Irion. Kept for reference. + Point n2 = HPoint(n, 0.0f) * m; + d = -((Point) (HPoint( -d*n, 1.0f ) * m) | n2); + n = n2; + return *this; + } + }; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Transforms a plane by a 4x4 matrix. Same as Plane * Matrix4x4 operator, but faster. + * \param transformed [out] transformed plane + * \param plane [in] source plane + * \param transform [in] transform matrix + * \warning the plane normal must be unit-length + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void TransformPlane(Plane& transformed, const Plane& plane, const Matrix4x4& transform) + { + // Rotate the normal using the rotation part of the 4x4 matrix + transformed.n = plane.n * Matrix3x3(transform); + + // Compute new d + transformed.d = plane.d - (Point(transform.GetTrans())|transformed.n); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Transforms a plane by a 4x4 matrix. Same as Plane * Matrix4x4 operator, but faster. + * \param plane [in/out] source plane (transformed on return) + * \param transform [in] transform matrix + * \warning the plane normal must be unit-length + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void TransformPlane(Plane& plane, const Matrix4x4& transform) + { + // Rotate the normal using the rotation part of the 4x4 matrix + plane.n *= Matrix3x3(transform); + + // Compute new d + plane.d -= Point(transform.GetTrans())|plane.n; + } + +#endif // __ICEPLANE_H__ diff --git a/Engine/lib/opcode/Ice/IcePoint.cpp b/Engine/lib/opcode/Ice/IcePoint.cpp new file mode 100644 index 000000000..327f324ce --- /dev/null +++ b/Engine/lib/opcode/Ice/IcePoint.cpp @@ -0,0 +1,192 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for 3D vectors. + * \file IcePoint.cpp + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * 3D point. + * + * The name is "Point" instead of "Vector" since a vector is N-dimensional, whereas a point is an implicit "vector of dimension 3". + * So the choice was between "Point" and "Vector3", the first one looked better (IMHO). + * + * Some people, then, use a typedef to handle both points & vectors using the same class: typedef Point Vector3; + * This is bad since it opens the door to a lot of confusion while reading the code. I know it may sounds weird but check this out: + * + * \code + * Point P0,P1 = some 3D points; + * Point Delta = P1 - P0; + * \endcode + * + * This compiles fine, although you should have written: + * + * \code + * Point P0,P1 = some 3D points; + * Vector3 Delta = P1 - P0; + * \endcode + * + * Subtle things like this are not caught at compile-time, and when you find one in the code, you never know whether it's a mistake + * from the author or something you don't get. + * + * One way to handle it at compile-time would be to use different classes for Point & Vector3, only overloading operator "-" for vectors. + * But then, you get a lot of redundant code in thoses classes, and basically it's really a lot of useless work. + * + * Another way would be to use homogeneous points: w=1 for points, w=0 for vectors. That's why the HPoint class exists. Now, to store + * your model's vertices and in most cases, you really want to use Points to save ram. + * + * \class Point + * \author Pierre Terdiman + * \version 1.0 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "../Opcode.h" + +using namespace IceMaths; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Creates a positive unit random vector. + * \return Self-reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Point& Point::PositiveUnitRandomVector() +{ + x = UnitRandomFloat(); + y = UnitRandomFloat(); + z = UnitRandomFloat(); + Normalize(); + return *this; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Creates a unit random vector. + * \return Self-reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Point& Point::UnitRandomVector() +{ + x = UnitRandomFloat() - 0.5f; + y = UnitRandomFloat() - 0.5f; + z = UnitRandomFloat() - 0.5f; + Normalize(); + return *this; +} + +// Cast operator +// WARNING: not inlined +Point::operator HPoint() const { return HPoint(x, y, z, 0.0f); } + +Point& Point::Refract(const Point& eye, const Point& n, float refractindex, Point& refracted) +{ + // Point EyePt = eye position + // Point p = current vertex + // Point n = vertex normal + // Point rv = refracted vector + // Eye vector - doesn't need to be normalized + Point Env; + Env.x = eye.x - x; + Env.y = eye.y - y; + Env.z = eye.z - z; + + float NDotE = n|Env; + float NDotN = n|n; + NDotE /= refractindex; + + // Refracted vector + refracted = n*NDotE - Env*NDotN; + + return *this; +} + +Point& Point::ProjectToPlane(const Plane& p) +{ + *this-= (p.d + (*this|p.n))*p.n; + return *this; +} + +void Point::ProjectToScreen(float halfrenderwidth, float halfrenderheight, const Matrix4x4& mat, HPoint& projected) const +{ + projected = HPoint(x, y, z, 1.0f) * mat; + projected.w = 1.0f / projected.w; + + projected.x*=projected.w; + projected.y*=projected.w; + projected.z*=projected.w; + + projected.x *= halfrenderwidth; projected.x += halfrenderwidth; + projected.y *= -halfrenderheight; projected.y += halfrenderheight; +} + +void Point::SetNotUsed() +{ + // We use a particular integer pattern : 0xffffffff everywhere. This is a NAN. + IR(x) = 0xffffffff; + IR(y) = 0xffffffff; + IR(z) = 0xffffffff; +} + +BOOL Point::IsNotUsed() const +{ + if(IR(x)!=0xffffffff) return FALSE; + if(IR(y)!=0xffffffff) return FALSE; + if(IR(z)!=0xffffffff) return FALSE; + return TRUE; +} + +Point& Point::Mult(const Matrix3x3& mat, const Point& a) +{ + x = a.x * mat.m[0][0] + a.y * mat.m[0][1] + a.z * mat.m[0][2]; + y = a.x * mat.m[1][0] + a.y * mat.m[1][1] + a.z * mat.m[1][2]; + z = a.x * mat.m[2][0] + a.y * mat.m[2][1] + a.z * mat.m[2][2]; + return *this; +} + +Point& Point::Mult2(const Matrix3x3& mat1, const Point& a1, const Matrix3x3& mat2, const Point& a2) +{ + x = a1.x * mat1.m[0][0] + a1.y * mat1.m[0][1] + a1.z * mat1.m[0][2] + a2.x * mat2.m[0][0] + a2.y * mat2.m[0][1] + a2.z * mat2.m[0][2]; + y = a1.x * mat1.m[1][0] + a1.y * mat1.m[1][1] + a1.z * mat1.m[1][2] + a2.x * mat2.m[1][0] + a2.y * mat2.m[1][1] + a2.z * mat2.m[1][2]; + z = a1.x * mat1.m[2][0] + a1.y * mat1.m[2][1] + a1.z * mat1.m[2][2] + a2.x * mat2.m[2][0] + a2.y * mat2.m[2][1] + a2.z * mat2.m[2][2]; + return *this; +} + +Point& Point::Mac(const Matrix3x3& mat, const Point& a) +{ + x += a.x * mat.m[0][0] + a.y * mat.m[0][1] + a.z * mat.m[0][2]; + y += a.x * mat.m[1][0] + a.y * mat.m[1][1] + a.z * mat.m[1][2]; + z += a.x * mat.m[2][0] + a.y * mat.m[2][1] + a.z * mat.m[2][2]; + return *this; +} + +Point& Point::TransMult(const Matrix3x3& mat, const Point& a) +{ + x = a.x * mat.m[0][0] + a.y * mat.m[1][0] + a.z * mat.m[2][0]; + y = a.x * mat.m[0][1] + a.y * mat.m[1][1] + a.z * mat.m[2][1]; + z = a.x * mat.m[0][2] + a.y * mat.m[1][2] + a.z * mat.m[2][2]; + return *this; +} + +Point& Point::Transform(const Point& r, const Matrix3x3& rotpos, const Point& linpos) +{ + x = r.x * rotpos.m[0][0] + r.y * rotpos.m[0][1] + r.z * rotpos.m[0][2] + linpos.x; + y = r.x * rotpos.m[1][0] + r.y * rotpos.m[1][1] + r.z * rotpos.m[1][2] + linpos.y; + z = r.x * rotpos.m[2][0] + r.y * rotpos.m[2][1] + r.z * rotpos.m[2][2] + linpos.z; + return *this; +} + +Point& Point::InvTransform(const Point& r, const Matrix3x3& rotpos, const Point& linpos) +{ + float sx = r.x - linpos.x; + float sy = r.y - linpos.y; + float sz = r.z - linpos.z; + x = sx * rotpos.m[0][0] + sy * rotpos.m[1][0] + sz * rotpos.m[2][0]; + y = sx * rotpos.m[0][1] + sy * rotpos.m[1][1] + sz * rotpos.m[2][1]; + z = sx * rotpos.m[0][2] + sy * rotpos.m[1][2] + sz * rotpos.m[2][2]; + return *this; +} diff --git a/Engine/lib/opcode/Ice/IcePoint.h b/Engine/lib/opcode/Ice/IcePoint.h new file mode 100644 index 000000000..dd8d28ece --- /dev/null +++ b/Engine/lib/opcode/Ice/IcePoint.h @@ -0,0 +1,528 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for 3D vectors. + * \file IcePoint.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEPOINT_H__ +#define __ICEPOINT_H__ + + // Forward declarations + class HPoint; + class Plane; + class Matrix3x3; + class Matrix4x4; + + #define CROSS2D(a, b) (a.x*b.y - b.x*a.y) + + const float EPSILON2 = 1.0e-20f; + + class ICEMATHS_API Point + { + public: + + //! Empty constructor + inline_ Point() {} + //! Constructor from a single float +// inline_ Point(float val) : x(val), y(val), z(val) {} +// Removed since it introduced the nasty "Point T = *Matrix4x4.GetTrans();" bug....... + //! Constructor from floats + inline_ Point(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {} + //! Constructor from array + inline_ Point(const float f[3]) : x(f[_X]), y(f[_Y]), z(f[_Z]) {} + //! Copy constructor + inline_ Point(const Point& p) : x(p.x), y(p.y), z(p.z) {} + //! Destructor + inline_ ~Point() {} + + //! Clears the vector + inline_ Point& Zero() { x = y = z = 0.0f; return *this; } + + //! + infinity + inline_ Point& SetPlusInfinity() { x = y = z = MAX_FLOAT; return *this; } + //! - infinity + inline_ Point& SetMinusInfinity() { x = y = z = MIN_FLOAT; return *this; } + + //! Sets positive unit random vector + Point& PositiveUnitRandomVector(); + //! Sets unit random vector + Point& UnitRandomVector(); + + //! Assignment from values + inline_ Point& Set(float _x, float _y, float _z) { x = _x; y = _y; z = _z; return *this; } + //! Assignment from array + inline_ Point& Set(const float f[3]) { x = f[_X]; y = f[_Y]; z = f[_Z]; return *this; } + //! Assignment from another point + inline_ Point& Set(const Point& src) { x = src.x; y = src.y; z = src.z; return *this; } + + //! Adds a vector + inline_ Point& Add(const Point& p) { x += p.x; y += p.y; z += p.z; return *this; } + //! Adds a vector + inline_ Point& Add(float _x, float _y, float _z) { x += _x; y += _y; z += _z; return *this; } + //! Adds a vector + inline_ Point& Add(const float f[3]) { x += f[_X]; y += f[_Y]; z += f[_Z]; return *this; } + //! Adds vectors + inline_ Point& Add(const Point& p, const Point& q) { x = p.x+q.x; y = p.y+q.y; z = p.z+q.z; return *this; } + + //! Subtracts a vector + inline_ Point& Sub(const Point& p) { x -= p.x; y -= p.y; z -= p.z; return *this; } + //! Subtracts a vector + inline_ Point& Sub(float _x, float _y, float _z) { x -= _x; y -= _y; z -= _z; return *this; } + //! Subtracts a vector + inline_ Point& Sub(const float f[3]) { x -= f[_X]; y -= f[_Y]; z -= f[_Z]; return *this; } + //! Subtracts vectors + inline_ Point& Sub(const Point& p, const Point& q) { x = p.x-q.x; y = p.y-q.y; z = p.z-q.z; return *this; } + + //! this = -this + inline_ Point& Neg() { x = -x; y = -y; z = -z; return *this; } + //! this = -a + inline_ Point& Neg(const Point& a) { x = -a.x; y = -a.y; z = -a.z; return *this; } + + //! Multiplies by a scalar + inline_ Point& Mult(float s) { x *= s; y *= s; z *= s; return *this; } + + //! this = a * scalar + inline_ Point& Mult(const Point& a, float scalar) + { + x = a.x * scalar; + y = a.y * scalar; + z = a.z * scalar; + return *this; + } + + //! this = a + b * scalar + inline_ Point& Mac(const Point& a, const Point& b, float scalar) + { + x = a.x + b.x * scalar; + y = a.y + b.y * scalar; + z = a.z + b.z * scalar; + return *this; + } + + //! this = this + a * scalar + inline_ Point& Mac(const Point& a, float scalar) + { + x += a.x * scalar; + y += a.y * scalar; + z += a.z * scalar; + return *this; + } + + //! this = a - b * scalar + inline_ Point& Msc(const Point& a, const Point& b, float scalar) + { + x = a.x - b.x * scalar; + y = a.y - b.y * scalar; + z = a.z - b.z * scalar; + return *this; + } + + //! this = this - a * scalar + inline_ Point& Msc(const Point& a, float scalar) + { + x -= a.x * scalar; + y -= a.y * scalar; + z -= a.z * scalar; + return *this; + } + + //! this = a + b * scalarb + c * scalarc + inline_ Point& Mac2(const Point& a, const Point& b, float scalarb, const Point& c, float scalarc) + { + x = a.x + b.x * scalarb + c.x * scalarc; + y = a.y + b.y * scalarb + c.y * scalarc; + z = a.z + b.z * scalarb + c.z * scalarc; + return *this; + } + + //! this = a - b * scalarb - c * scalarc + inline_ Point& Msc2(const Point& a, const Point& b, float scalarb, const Point& c, float scalarc) + { + x = a.x - b.x * scalarb - c.x * scalarc; + y = a.y - b.y * scalarb - c.y * scalarc; + z = a.z - b.z * scalarb - c.z * scalarc; + return *this; + } + + //! this = mat * a + inline_ Point& Mult(const Matrix3x3& mat, const Point& a); + + //! this = mat1 * a1 + mat2 * a2 + inline_ Point& Mult2(const Matrix3x3& mat1, const Point& a1, const Matrix3x3& mat2, const Point& a2); + + //! this = this + mat * a + inline_ Point& Mac(const Matrix3x3& mat, const Point& a); + + //! this = transpose(mat) * a + inline_ Point& TransMult(const Matrix3x3& mat, const Point& a); + + //! Linear interpolate between two vectors: this = a + t * (b - a) + inline_ Point& Lerp(const Point& a, const Point& b, float t) + { + x = a.x + t * (b.x - a.x); + y = a.y + t * (b.y - a.y); + z = a.z + t * (b.z - a.z); + return *this; + } + + //! Hermite interpolate between p1 and p2. p0 and p3 are used for finding gradient at p1 and p2. + //! this = p0 * (2t^2 - t^3 - t)/2 + //! + p1 * (3t^3 - 5t^2 + 2)/2 + //! + p2 * (4t^2 - 3t^3 + t)/2 + //! + p3 * (t^3 - t^2)/2 + inline_ Point& Herp(const Point& p0, const Point& p1, const Point& p2, const Point& p3, float t) + { + float t2 = t * t; + float t3 = t2 * t; + float kp0 = (2.0f * t2 - t3 - t) * 0.5f; + float kp1 = (3.0f * t3 - 5.0f * t2 + 2.0f) * 0.5f; + float kp2 = (4.0f * t2 - 3.0f * t3 + t) * 0.5f; + float kp3 = (t3 - t2) * 0.5f; + x = p0.x * kp0 + p1.x * kp1 + p2.x * kp2 + p3.x * kp3; + y = p0.y * kp0 + p1.y * kp1 + p2.y * kp2 + p3.y * kp3; + z = p0.z * kp0 + p1.z * kp1 + p2.z * kp2 + p3.z * kp3; + return *this; + } + + //! this = rotpos * r + linpos + inline_ Point& Transform(const Point& r, const Matrix3x3& rotpos, const Point& linpos); + + //! this = trans(rotpos) * (r - linpos) + inline_ Point& InvTransform(const Point& r, const Matrix3x3& rotpos, const Point& linpos); + + //! Returns MIN(x, y, z); + inline_ float Min() const { return MIN(x, MIN(y, z)); } + //! Returns MAX(x, y, z); + inline_ float Max() const { return MAX(x, MAX(y, z)); } + //! Sets each element to be componentwise minimum + inline_ Point& Min(const Point& p) { x = MIN(x, p.x); y = MIN(y, p.y); z = MIN(z, p.z); return *this; } + //! Sets each element to be componentwise maximum + inline_ Point& Max(const Point& p) { x = MAX(x, p.x); y = MAX(y, p.y); z = MAX(z, p.z); return *this; } + + //! Clamps each element + inline_ Point& Clamp(float min, float max) + { + if(xmax) x=max; + if(ymax) y=max; + if(zmax) z=max; + return *this; + } + + //! Computes square magnitude + inline_ float SquareMagnitude() const { return x*x + y*y + z*z; } + //! Computes magnitude + inline_ float Magnitude() const { return sqrtf(x*x + y*y + z*z); } + //! Computes volume + inline_ float Volume() const { return x * y * z; } + + //! Checks the point is near zero + inline_ bool ApproxZero() const { return SquareMagnitude() < EPSILON2; } + + //! Tests for exact zero vector + inline_ BOOL IsZero() const + { + if(IR(x) || IR(y) || IR(z)) return FALSE; + return TRUE; + } + + //! Checks point validity + inline_ BOOL IsValid() const + { + if(!IsValidFloat(x)) return FALSE; + if(!IsValidFloat(y)) return FALSE; + if(!IsValidFloat(z)) return FALSE; + return TRUE; + } + + //! Slighty moves the point + void Tweak(udword coord_mask, udword tweak_mask) + { + if(coord_mask&1) { udword Dummy = IR(x); Dummy^=tweak_mask; x = FR(Dummy); } + if(coord_mask&2) { udword Dummy = IR(y); Dummy^=tweak_mask; y = FR(Dummy); } + if(coord_mask&4) { udword Dummy = IR(z); Dummy^=tweak_mask; z = FR(Dummy); } + } + + #define TWEAKMASK 0x3fffff + #define TWEAKNOTMASK ~TWEAKMASK + //! Slighty moves the point out + inline_ void TweakBigger() + { + udword Dummy = (IR(x)&TWEAKNOTMASK); if(!IS_NEGATIVE_FLOAT(x)) Dummy+=TWEAKMASK+1; x = FR(Dummy); + Dummy = (IR(y)&TWEAKNOTMASK); if(!IS_NEGATIVE_FLOAT(y)) Dummy+=TWEAKMASK+1; y = FR(Dummy); + Dummy = (IR(z)&TWEAKNOTMASK); if(!IS_NEGATIVE_FLOAT(z)) Dummy+=TWEAKMASK+1; z = FR(Dummy); + } + + //! Slighty moves the point in + inline_ void TweakSmaller() + { + udword Dummy = (IR(x)&TWEAKNOTMASK); if(IS_NEGATIVE_FLOAT(x)) Dummy+=TWEAKMASK+1; x = FR(Dummy); + Dummy = (IR(y)&TWEAKNOTMASK); if(IS_NEGATIVE_FLOAT(y)) Dummy+=TWEAKMASK+1; y = FR(Dummy); + Dummy = (IR(z)&TWEAKNOTMASK); if(IS_NEGATIVE_FLOAT(z)) Dummy+=TWEAKMASK+1; z = FR(Dummy); + } + + //! Normalizes the vector + inline_ Point& Normalize() + { + float M = x*x + y*y + z*z; + if(M) + { + M = 1.0f / sqrtf(M); + x *= M; + y *= M; + z *= M; + } + return *this; + } + + //! Sets vector length + inline_ Point& SetLength(float length) + { + float NewLength = length / Magnitude(); + x *= NewLength; + y *= NewLength; + z *= NewLength; + return *this; + } + + //! Clamps vector length + inline_ Point& ClampLength(float limit_length) + { + if(limit_length>=0.0f) // Magnitude must be positive + { + float CurrentSquareLength = SquareMagnitude(); + + if(CurrentSquareLength > limit_length * limit_length) + { + float Coeff = limit_length / sqrtf(CurrentSquareLength); + x *= Coeff; + y *= Coeff; + z *= Coeff; + } + } + return *this; + } + + //! Computes distance to another point + inline_ float Distance(const Point& b) const + { + return sqrtf((x - b.x)*(x - b.x) + (y - b.y)*(y - b.y) + (z - b.z)*(z - b.z)); + } + + //! Computes square distance to another point + inline_ float SquareDistance(const Point& b) const + { + return ((x - b.x)*(x - b.x) + (y - b.y)*(y - b.y) + (z - b.z)*(z - b.z)); + } + + //! Dot product dp = this|a + inline_ float Dot(const Point& p) const { return p.x * x + p.y * y + p.z * z; } + + //! Cross product this = a x b + inline_ Point& Cross(const Point& a, const Point& b) + { + x = a.y * b.z - a.z * b.y; + y = a.z * b.x - a.x * b.z; + z = a.x * b.y - a.y * b.x; + return *this; + } + + //! Vector code ( bitmask = sign(z) | sign(y) | sign(x) ) + inline_ udword VectorCode() const + { + return (IR(x)>>31) | ((IR(y)&SIGN_BITMASK)>>30) | ((IR(z)&SIGN_BITMASK)>>29); + } + + //! Returns largest axis + inline_ PointComponent LargestAxis() const + { + const float* Vals = &x; + PointComponent m = _X; + if(Vals[_Y] > Vals[m]) m = _Y; + if(Vals[_Z] > Vals[m]) m = _Z; + return m; + } + + //! Returns closest axis + inline_ PointComponent ClosestAxis() const + { + const float* Vals = &x; + PointComponent m = _X; + if(AIR(Vals[_Y]) > AIR(Vals[m])) m = _Y; + if(AIR(Vals[_Z]) > AIR(Vals[m])) m = _Z; + return m; + } + + //! Returns smallest axis + inline_ PointComponent SmallestAxis() const + { + const float* Vals = &x; + PointComponent m = _X; + if(Vals[_Y] < Vals[m]) m = _Y; + if(Vals[_Z] < Vals[m]) m = _Z; + return m; + } + + //! Refracts the point + Point& Refract(const Point& eye, const Point& n, float refractindex, Point& refracted); + + //! Projects the point onto a plane + Point& ProjectToPlane(const Plane& p); + + //! Projects the point onto the screen + void ProjectToScreen(float halfrenderwidth, float halfrenderheight, const Matrix4x4& mat, HPoint& projected) const; + + //! Unfolds the point onto a plane according to edge(a,b) + Point& Unfold(Plane& p, Point& a, Point& b); + + //! Hash function from Ville Miettinen + inline_ udword GetHashValue() const + { + const udword* h = (const udword*)(this); + udword f = (h[0]+h[1]*11-(h[2]*17)) & 0x7fffffff; // avoid problems with +-0 + return (f>>22)^(f>>12)^(f); + } + + //! Stuff magic values in the point, marking it as explicitely not used. + void SetNotUsed(); + //! Checks the point is marked as not used + BOOL IsNotUsed() const; + + // Arithmetic operators + + //! Unary operator for Point Negate = - Point + inline_ Point operator-() const { return Point(-x, -y, -z); } + + //! Operator for Point Plus = Point + Point. + inline_ Point operator+(const Point& p) const { return Point(x + p.x, y + p.y, z + p.z); } + //! Operator for Point Minus = Point - Point. + inline_ Point operator-(const Point& p) const { return Point(x - p.x, y - p.y, z - p.z); } + + //! Operator for Point Mul = Point * Point. + inline_ Point operator*(const Point& p) const { return Point(x * p.x, y * p.y, z * p.z); } + //! Operator for Point Scale = Point * float. + inline_ Point operator*(float s) const { return Point(x * s, y * s, z * s ); } + //! Operator for Point Scale = float * Point. + inline_ friend Point operator*(float s, const Point& p) { return Point(s * p.x, s * p.y, s * p.z); } + + //! Operator for Point Div = Point / Point. + inline_ Point operator/(const Point& p) const { return Point(x / p.x, y / p.y, z / p.z); } + //! Operator for Point Scale = Point / float. + inline_ Point operator/(float s) const { s = 1.0f / s; return Point(x * s, y * s, z * s); } + //! Operator for Point Scale = float / Point. + inline_ friend Point operator/(float s, const Point& p) { return Point(s / p.x, s / p.y, s / p.z); } + + //! Operator for float DotProd = Point | Point. + inline_ float operator|(const Point& p) const { return x*p.x + y*p.y + z*p.z; } + //! Operator for Point VecProd = Point ^ Point. + inline_ Point operator^(const Point& p) const + { + return Point( + y * p.z - z * p.y, + z * p.x - x * p.z, + x * p.y - y * p.x ); + } + + //! Operator for Point += Point. + inline_ Point& operator+=(const Point& p) { x += p.x; y += p.y; z += p.z; return *this; } + //! Operator for Point += float. + inline_ Point& operator+=(float s) { x += s; y += s; z += s; return *this; } + + //! Operator for Point -= Point. + inline_ Point& operator-=(const Point& p) { x -= p.x; y -= p.y; z -= p.z; return *this; } + //! Operator for Point -= float. + inline_ Point& operator-=(float s) { x -= s; y -= s; z -= s; return *this; } + + //! Operator for Point *= Point. + inline_ Point& operator*=(const Point& p) { x *= p.x; y *= p.y; z *= p.z; return *this; } + //! Operator for Point *= float. + inline_ Point& operator*=(float s) { x *= s; y *= s; z *= s; return *this; } + + //! Operator for Point /= Point. + inline_ Point& operator/=(const Point& p) { x /= p.x; y /= p.y; z /= p.z; return *this; } + //! Operator for Point /= float. + inline_ Point& operator/=(float s) { s = 1.0f/s; x *= s; y *= s; z *= s; return *this; } + + // Logical operators + + //! Operator for "if(Point==Point)" + inline_ bool operator==(const Point& p) const { return ( (IR(x)==IR(p.x))&&(IR(y)==IR(p.y))&&(IR(z)==IR(p.z))); } + //! Operator for "if(Point!=Point)" + inline_ bool operator!=(const Point& p) const { return ( (IR(x)!=IR(p.x))||(IR(y)!=IR(p.y))||(IR(z)!=IR(p.z))); } + + // Arithmetic operators + + //! Operator for Point Mul = Point * Matrix3x3. + inline_ Point operator*(const Matrix3x3& mat) const + { + class ShadowMatrix3x3{ public: float m[3][3]; }; // To allow inlining + const ShadowMatrix3x3* Mat = (const ShadowMatrix3x3*)&mat; + + return Point( + x * Mat->m[0][0] + y * Mat->m[1][0] + z * Mat->m[2][0], + x * Mat->m[0][1] + y * Mat->m[1][1] + z * Mat->m[2][1], + x * Mat->m[0][2] + y * Mat->m[1][2] + z * Mat->m[2][2] ); + } + + //! Operator for Point Mul = Point * Matrix4x4. + inline_ Point operator*(const Matrix4x4& mat) const + { + class ShadowMatrix4x4{ public: float m[4][4]; }; // To allow inlining + const ShadowMatrix4x4* Mat = (const ShadowMatrix4x4*)&mat; + + return Point( + x * Mat->m[0][0] + y * Mat->m[1][0] + z * Mat->m[2][0] + Mat->m[3][0], + x * Mat->m[0][1] + y * Mat->m[1][1] + z * Mat->m[2][1] + Mat->m[3][1], + x * Mat->m[0][2] + y * Mat->m[1][2] + z * Mat->m[2][2] + Mat->m[3][2]); + } + + //! Operator for Point *= Matrix3x3. + inline_ Point& operator*=(const Matrix3x3& mat) + { + class ShadowMatrix3x3{ public: float m[3][3]; }; // To allow inlining + const ShadowMatrix3x3* Mat = (const ShadowMatrix3x3*)&mat; + + float xp = x * Mat->m[0][0] + y * Mat->m[1][0] + z * Mat->m[2][0]; + float yp = x * Mat->m[0][1] + y * Mat->m[1][1] + z * Mat->m[2][1]; + float zp = x * Mat->m[0][2] + y * Mat->m[1][2] + z * Mat->m[2][2]; + + x = xp; y = yp; z = zp; + + return *this; + } + + //! Operator for Point *= Matrix4x4. + inline_ Point& operator*=(const Matrix4x4& mat) + { + class ShadowMatrix4x4{ public: float m[4][4]; }; // To allow inlining + const ShadowMatrix4x4* Mat = (const ShadowMatrix4x4*)&mat; + + float xp = x * Mat->m[0][0] + y * Mat->m[1][0] + z * Mat->m[2][0] + Mat->m[3][0]; + float yp = x * Mat->m[0][1] + y * Mat->m[1][1] + z * Mat->m[2][1] + Mat->m[3][1]; + float zp = x * Mat->m[0][2] + y * Mat->m[1][2] + z * Mat->m[2][2] + Mat->m[3][2]; + + x = xp; y = yp; z = zp; + + return *this; + } + + // Cast operators + + //! Cast a Point to a HPoint. w is set to zero. + operator HPoint() const; + + inline_ operator const float*() const { return &x; } + inline_ operator float*() { return &x; } + + public: + float x, y, z; + }; + + FUNCTION ICEMATHS_API void Normalize1(Point& a); + FUNCTION ICEMATHS_API void Normalize2(Point& a); + +#endif //__ICEPOINT_H__ diff --git a/Engine/lib/opcode/Ice/IcePreprocessor.h b/Engine/lib/opcode/Ice/IcePreprocessor.h new file mode 100644 index 000000000..572626a50 --- /dev/null +++ b/Engine/lib/opcode/Ice/IcePreprocessor.h @@ -0,0 +1,133 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains preprocessor stuff. This should be the first included header. + * \file IcePreprocessor.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICEPREPROCESSOR_H__ +#define __ICEPREPROCESSOR_H__ + // Check platform + #if _XBOX_VER >= 200 + #pragma message("Compiling on Xbox 360...") + #define PLATFORM_XENON + #elif defined( _WIN32 ) || defined( WIN32 ) + #pragma message("Compiling on Windows...") + #define PLATFORM_WINDOWS + #elif defined(SN_TARGET_PS3) + #pragma message("Compiling on PS3...") + #define PLATFORM_PS3 + #else + #pragma message("Compiling on unknown platform...") + #endif + + // Check compiler + #if defined(_MSC_VER) + #pragma message("Compiling with VC++...") + #define COMPILER_VISUAL_CPP + #else + #pragma message("Compiling with unknown compiler...") + #endif + + // Check compiler options. If this file is included in user-apps, this + // shouldn't be needed, so that they can use what they like best. + #ifndef ICE_DONT_CHECK_COMPILER_OPTIONS + #ifdef COMPILER_VISUAL_CPP + #if defined(_CHAR_UNSIGNED) + #endif + + #if defined(_CPPRTTI) + #error Please disable RTTI... + #endif + + #if defined(_CPPUNWIND) + #error Please disable exceptions... + #endif + + #if defined(_MT) + // Multithreading + #endif + #endif + #endif + + // Check debug mode + #ifdef DEBUG // May be defined instead of _DEBUG. Let's fix it. + #ifndef _DEBUG + #define _DEBUG + #endif + #endif + + #ifdef _DEBUG + // Here you may define items for debug builds + #endif + + #ifndef THIS_FILE + #define THIS_FILE __FILE__ + #endif + + #ifndef ICE_NO_DLL + #ifdef ICECORE_EXPORTS + #define ICECORE_API __declspec(dllexport) + #else + #define ICECORE_API __declspec(dllimport) + #endif + #else + #define ICECORE_API + #endif + + // Don't override new/delete +// #define DEFAULT_NEWDELETE + #define DONT_TRACK_MEMORY_LEAKS + + #define FUNCTION extern "C" + + // Cosmetic stuff [mainly useful with multiple inheritance] + #define override(base_class) virtual + + // Our own inline keyword, so that: + // - we can switch to __forceinline to check it's really better or not + // - we can remove __forceinline if the compiler doesn't support it +// #define inline_ __forceinline +// #define inline_ inline + + // Contributed by Bruce Mitchener + #if defined(COMPILER_VISUAL_CPP) +// #define inline_ __forceinline + #define inline_ inline + #elif defined(__GNUC__) && __GNUC__ < 3 + #define inline_ inline + #elif defined(__GNUC__) + #define inline_ inline __attribute__ ((always_inline)) + #else + #define inline_ inline + #endif + + // Down the hatch + #pragma inline_depth( 255 ) + + #ifdef COMPILER_VISUAL_CPP + #pragma intrinsic(memcmp) + #pragma intrinsic(memcpy) + #pragma intrinsic(memset) + #pragma intrinsic(strcat) + #pragma intrinsic(strcmp) + #pragma intrinsic(strcpy) + #pragma intrinsic(strlen) + #pragma intrinsic(abs) + #pragma intrinsic(labs) + #endif + + // ANSI compliance +/* #ifdef _DEBUG + // Remove painful warning in debug + inline_ bool __False__(){ return false; } + #define for if(__False__()){} else for + #else + #define for if(0){} else for + #endif */ + +#endif // __ICEPREPROCESSOR_H__ diff --git a/Engine/lib/opcode/Ice/IceRandom.cpp b/Engine/lib/opcode/Ice/IceRandom.cpp new file mode 100644 index 000000000..d0b73d233 --- /dev/null +++ b/Engine/lib/opcode/Ice/IceRandom.cpp @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for random generators. + * \file IceRandom.cpp + * \author Pierre Terdiman + * \date August, 9, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "../Opcode.h" + +using namespace IceCore; + +void IceCore:: SRand(udword seed) +{ + srand(seed); +} + +udword IceCore::Rand() +{ + return rand(); +} + + +static BasicRandom gRandomGenerator(42); + +udword IceCore::GetRandomIndex(udword max_index) +{ + // We don't use rand() since it's limited to RAND_MAX + udword Index = gRandomGenerator.Randomize(); + return Index % max_index; +} + diff --git a/Engine/lib/opcode/Ice/IceRandom.h b/Engine/lib/opcode/Ice/IceRandom.h new file mode 100644 index 000000000..3170b33de --- /dev/null +++ b/Engine/lib/opcode/Ice/IceRandom.h @@ -0,0 +1,42 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for random generators. + * \file IceRandom.h + * \author Pierre Terdiman + * \date August, 9, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICERANDOM_H__ +#define __ICERANDOM_H__ + + FUNCTION ICECORE_API void SRand(udword seed); + FUNCTION ICECORE_API udword Rand(); + + //! Returns a unit random floating-point value + inline_ float UnitRandomFloat() { return float(Rand()) * ONE_OVER_RAND_MAX; } + + //! Returns a random index so that 0<= index < max_index + ICECORE_API udword GetRandomIndex(udword max_index); + + class ICECORE_API BasicRandom + { + public: + + //! Constructor + inline_ BasicRandom(udword seed=0) : mRnd(seed) {} + //! Destructor + inline_ ~BasicRandom() {} + + inline_ void SetSeed(udword seed) { mRnd = seed; } + inline_ udword GetCurrentValue() const { return mRnd; } + inline_ udword Randomize() { mRnd = mRnd * 2147001325 + 715136305; return mRnd; } + + private: + udword mRnd; + }; + +#endif // __ICERANDOM_H__ + diff --git a/Engine/lib/opcode/Ice/IceRay.cpp b/Engine/lib/opcode/Ice/IceRay.cpp new file mode 100644 index 000000000..5a78aa48f --- /dev/null +++ b/Engine/lib/opcode/Ice/IceRay.cpp @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for rays. + * \file IceRay.cpp + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Ray class. + * A ray is a half-line P(t) = mOrig + mDir * t, with 0 <= t <= +infinity + * \class Ray + * \author Pierre Terdiman + * \version 1.0 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/* + O = Origin = impact point + i = normalized vector along the x axis + j = normalized vector along the y axis = actually the normal vector in O + D = Direction vector, norm |D| = 1 + N = Projection of D on y axis, norm |N| = normal reaction + T = Projection of D on x axis, norm |T| = tangential reaction + R = Reflexion vector + + ^y + | + | + | + _ _ _| _ _ _ + * * *| + \ | / + \ |N / | + R\ | /D + \ | / | + \ | / + _________\|/______*_______>x + O T + + Let define theta = angle between D and N. Then cos(theta) = |N| / |D| = |N| since D is normalized. + + j|D = |j|*|D|*cos(theta) => |N| = j|D + + Then we simply have: + + D = N + T + + To compute tangential reaction : + + T = D - N + + To compute reflexion vector : + + R = N - T = N - (D-N) = 2*N - D +*/ + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "../Opcode.h" + +using namespace IceMaths; + +float Ray::SquareDistance(const Point& point, float* t) const +{ + Point Diff = point - mOrig; + float fT = Diff | mDir; + + if(fT<=0.0f) + { + fT = 0.0f; + } + else + { + fT /= mDir.SquareMagnitude(); + Diff -= fT*mDir; + } + + if(t) *t = fT; + + return Diff.SquareMagnitude(); +} diff --git a/Engine/lib/opcode/Ice/IceRay.h b/Engine/lib/opcode/Ice/IceRay.h new file mode 100644 index 000000000..026828767 --- /dev/null +++ b/Engine/lib/opcode/Ice/IceRay.h @@ -0,0 +1,98 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for rays. + * \file IceRay.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICERAY_H__ +#define __ICERAY_H__ + + class ICEMATHS_API Ray + { + public: + //! Constructor + inline_ Ray() {} + //! Constructor + inline_ Ray(const Point& orig, const Point& dir) : mOrig(orig), mDir(dir) {} + //! Copy constructor + inline_ Ray(const Ray& ray) : mOrig(ray.mOrig), mDir(ray.mDir) {} + //! Destructor + inline_ ~Ray() {} + + float SquareDistance(const Point& point, float* t=null) const; + inline_ float Distance(const Point& point, float* t=null) const { return sqrtf(SquareDistance(point, t)); } + + Point mOrig; //!< Ray origin + Point mDir; //!< Normalized direction + }; + + inline_ void ComputeReflexionVector(Point& reflected, const Point& incoming_dir, const Point& outward_normal) + { + reflected = incoming_dir - outward_normal * 2.0f * (incoming_dir|outward_normal); + } + + inline_ void ComputeReflexionVector(Point& reflected, const Point& source, const Point& impact, const Point& normal) + { + Point V = impact - source; + reflected = V - normal * 2.0f * (V|normal); + } + + inline_ void DecomposeVector(Point& normal_compo, Point& tangent_compo, const Point& outward_dir, const Point& outward_normal) + { + normal_compo = outward_normal * (outward_dir|outward_normal); + tangent_compo = outward_dir - normal_compo; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Transforms a direction vector from world space to local space + * \param local_dir [out] direction vector in local space + * \param world_dir [in] direction vector in world space + * \param world [in] world transform + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void ComputeLocalDirection(Point& local_dir, const Point& world_dir, const Matrix4x4& world) + { + // Get world direction back in local space +// Matrix3x3 InvWorld = world; +// local_dir = InvWorld * world_dir; + local_dir = Matrix3x3(world) * world_dir; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Transforms a position vector from world space to local space + * \param local_pt [out] position vector in local space + * \param world_pt [in] position vector in world space + * \param world [in] world transform + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void ComputeLocalPoint(Point& local_pt, const Point& world_pt, const Matrix4x4& world) + { + // Get world vertex back in local space + Matrix4x4 InvWorld = world; + InvWorld.Invert(); + local_pt = world_pt * InvWorld; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Transforms a ray from world space to local space + * \param local_ray [out] ray in local space + * \param world_ray [in] ray in world space + * \param world [in] world transform + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void ComputeLocalRay(Ray& local_ray, const Ray& world_ray, const Matrix4x4& world) + { + // Get world ray back in local space + ComputeLocalDirection(local_ray.mDir, world_ray.mDir, world); + ComputeLocalPoint(local_ray.mOrig, world_ray.mOrig, world); + } + +#endif // __ICERAY_H__ diff --git a/Engine/lib/opcode/Ice/IceRevisitedRadix.cpp b/Engine/lib/opcode/Ice/IceRevisitedRadix.cpp new file mode 100644 index 000000000..79d258362 --- /dev/null +++ b/Engine/lib/opcode/Ice/IceRevisitedRadix.cpp @@ -0,0 +1,519 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains source code from the article "Radix Sort Revisited". + * \file IceRevisitedRadix.cpp + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Revisited Radix Sort. + * This is my new radix routine: + * - it uses indices and doesn't recopy the values anymore, hence wasting less ram + * - it creates all the histograms in one run instead of four + * - it sorts words faster than dwords and bytes faster than words + * - it correctly sorts negative floating-point values by patching the offsets + * - it automatically takes advantage of temporal coherence + * - multiple keys support is a side effect of temporal coherence + * - it may be worth recoding in asm... (mainly to use FCOMI, FCMOV, etc) [it's probably memory-bound anyway] + * + * History: + * - 08.15.98: very first version + * - 04.04.00: recoded for the radix article + * - 12.xx.00: code lifting + * - 09.18.01: faster CHECK_PASS_VALIDITY thanks to Mark D. Shattuck (who provided other tips, not included here) + * - 10.11.01: added local ram support + * - 01.20.02: bugfix! In very particular cases the last pass was skipped in the float code-path, leading to incorrect sorting...... + * - 01.02.02: - "mIndices" renamed => "mRanks". That's a rank sorter after all. + * - ranks are not "reset" anymore, but implicit on first calls + * - 07.05.02: - offsets rewritten with one less indirection. + * - 11.03.02: - "bool" replaced with RadixHint enum + * + * \class RadixSort + * \author Pierre Terdiman + * \version 1.4 + * \date August, 15, 1998 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/* +To do: + - add an offset parameter between two input values (avoid some data recopy sometimes) + - unroll ? asm ? + - 11 bits trick & 3 passes as Michael did + - prefetch stuff the day I have a P3 + - make a version with 16-bits indices ? +*/ + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "../Opcode.h" + +using namespace IceCore; + +#define INVALIDATE_RANKS mCurrentSize|=0x80000000 +#define VALIDATE_RANKS mCurrentSize&=0x7fffffff +#define CURRENT_SIZE (mCurrentSize&0x7fffffff) +#define INVALID_RANKS (mCurrentSize&0x80000000) + +#define CHECK_RESIZE(n) \ + if(n!=mPreviousSize) \ + { \ + if(n>mCurrentSize) Resize(n); \ + else ResetRanks(); \ + mPreviousSize = n; \ + } + +#define CREATE_HISTOGRAMS(type, buffer) \ + /* Clear counters/histograms */ \ + ZeroMemory(mHistogram, 256*4*sizeof(udword)); \ + \ + /* Prepare to count */ \ + ubyte* p = (ubyte*)input; \ + ubyte* pe = &p[nb*4]; \ + udword* h0= &mHistogram[0]; /* Histogram for first pass (LSB) */ \ + udword* h1= &mHistogram[256]; /* Histogram for second pass */ \ + udword* h2= &mHistogram[512]; /* Histogram for third pass */ \ + udword* h3= &mHistogram[768]; /* Histogram for last pass (MSB) */ \ + \ + bool AlreadySorted = true; /* Optimism... */ \ + \ + if(INVALID_RANKS) \ + { \ + /* Prepare for temporal coherence */ \ + type* Running = (type*)buffer; \ + type PrevVal = *Running; \ + \ + while(p!=pe) \ + { \ + /* Read input buffer in previous sorted order */ \ + type Val = *Running++; \ + /* Check whether already sorted or not */ \ + if(ValCurSize) Resize(nb); + mCurrentSize = nb; + INVALIDATE_RANKS; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Main sort routine. + * This one is for integer values. After the call, mRanks contains a list of indices in sorted order, i.e. in the order you may process your data. + * \param input [in] a list of integer values to sort + * \param nb [in] number of values to sort, must be < 2^31 + * \param hint [in] RADIX_SIGNED to handle negative values, RADIX_UNSIGNED if you know your input buffer only contains positive values + * \return Self-Reference + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +RadixSort& RadixSort::Sort(const udword* input, udword nb, RadixHint hint) +{ + // Checkings + if(!input || !nb || nb&0x80000000) return *this; + + // Stats + mTotalCalls++; + + // Resize lists if needed + CheckResize(nb); + +#ifdef RADIX_LOCAL_RAM + // Allocate histograms & offsets on the stack + udword mHistogram[256*4]; +// udword mOffset[256]; + udword* mLink[256]; +#endif + + // Create histograms (counters). Counters for all passes are created in one run. + // Pros: read input buffer once instead of four times + // Cons: mHistogram is 4Kb instead of 1Kb + // We must take care of signed/unsigned values for temporal coherence.... I just + // have 2 code paths even if just a single opcode changes. Self-modifying code, someone? + if(hint==RADIX_UNSIGNED) { CREATE_HISTOGRAMS(udword, input); } + else { CREATE_HISTOGRAMS(sdword, input); } + + // Compute #negative values involved if needed + udword NbNegativeValues = 0; + if(hint==RADIX_SIGNED) + { + // An efficient way to compute the number of negatives values we'll have to deal with is simply to sum the 128 + // last values of the last histogram. Last histogram because that's the one for the Most Significant Byte, + // responsible for the sign. 128 last values because the 128 first ones are related to positive numbers. + udword* h3= &mHistogram[768]; + for(udword i=128;i<256;i++) NbNegativeValues += h3[i]; // 768 for last histogram, 128 for negative part + } + + // Radix sort, j is the pass number (0=LSB, 3=MSB) + for(udword j=0;j<4;j++) + { + CHECK_PASS_VALIDITY(j); + + // Sometimes the fourth (negative) pass is skipped because all numbers are negative and the MSB is 0xFF (for example). This is + // not a problem, numbers are correctly sorted anyway. + if(PerformPass) + { + // Should we care about negative values? + if(j!=3 || hint==RADIX_UNSIGNED) + { + // Here we deal with positive values only + + // Create offsets +// mOffset[0] = 0; +// for(udword i=1;i<256;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1]; + mLink[0] = mRanks2; + for(udword i=1;i<256;i++) mLink[i] = mLink[i-1] + CurCount[i-1]; + } + else + { + // This is a special case to correctly handle negative integers. They're sorted in the right order but at the wrong place. + + // Create biased offsets, in order for negative numbers to be sorted as well +// mOffset[0] = NbNegativeValues; // First positive number takes place after the negative ones + mLink[0] = &mRanks2[NbNegativeValues]; // First positive number takes place after the negative ones +// for(udword i=1;i<128;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers + for(udword i=1;i<128;i++) mLink[i] = mLink[i-1] + CurCount[i-1]; // 1 to 128 for positive numbers + + // Fixing the wrong place for negative values +// mOffset[128] = 0; + mLink[128] = mRanks2; +// for(i=129;i<256;i++) mOffset[i] = mOffset[i-1] + CurCount[i-1]; + for(udword i=129;i<256;i++) mLink[i] = mLink[i-1] + CurCount[i-1]; + } + + // Perform Radix Sort + ubyte* InputBytes = (ubyte*)input; + InputBytes += j; + if(INVALID_RANKS) + { +// for(udword i=0;i>24; // Radix byte, same as above. AND is useless here (udword). + // ### cmp to be killed. Not good. Later. +// if(Radix<128) mRanks2[mOffset[Radix]++] = i; // Number is positive, same as above +// else mRanks2[--mOffset[Radix]] = i; // Number is negative, flip the sorting order + if(Radix<128) *mLink[Radix]++ = i; // Number is positive, same as above + else *(--mLink[Radix]) = i; // Number is negative, flip the sorting order + } + VALIDATE_RANKS; + } + else + { + for(udword i=0;i>24; // Radix byte, same as above. AND is useless here (udword). + // ### cmp to be killed. Not good. Later. +// if(Radix<128) mRanks2[mOffset[Radix]++] = mRanks[i]; // Number is positive, same as above +// else mRanks2[--mOffset[Radix]] = mRanks[i]; // Number is negative, flip the sorting order + if(Radix<128) *mLink[Radix]++ = mRanks[i]; // Number is positive, same as above + else *(--mLink[Radix]) = mRanks[i]; // Number is negative, flip the sorting order + } + } + // Swap pointers for next pass. Valid indices - the most recent ones - are in mRanks after the swap. + udword* Tmp = mRanks; mRanks = mRanks2; mRanks2 = Tmp; + } + else + { + // The pass is useless, yet we still have to reverse the order of current list if all values are negative. + if(UniqueVal>=128) + { + if(INVALID_RANKS) + { + // ###Possible? + for(udword i=0;i=SqrLen) + { + fT = 1.0f; + Diff -= Dir; + } + else + { + fT /= SqrLen; + Diff -= fT*Dir; + } + } + + if(t) *t = fT; + + return Diff.SquareMagnitude(); +} diff --git a/Engine/lib/opcode/Ice/IceSegment.h b/Engine/lib/opcode/Ice/IceSegment.h new file mode 100644 index 000000000..8d6632268 --- /dev/null +++ b/Engine/lib/opcode/Ice/IceSegment.h @@ -0,0 +1,55 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for segments. + * \file IceSegment.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICESEGMENT_H__ +#define __ICESEGMENT_H__ + + class ICEMATHS_API Segment + { + public: + //! Constructor + inline_ Segment() {} + //! Constructor + inline_ Segment(const Point& p0, const Point& p1) : mP0(p0), mP1(p1) {} + //! Copy constructor + inline_ Segment(const Segment& seg) : mP0(seg.mP0), mP1(seg.mP1) {} + //! Destructor + inline_ ~Segment() {} + + inline_ const Point& GetOrigin() const { return mP0; } + inline_ Point ComputeDirection() const { return mP1 - mP0; } + inline_ void ComputeDirection(Point& dir) const { dir = mP1 - mP0; } + inline_ float ComputeLength() const { return mP1.Distance(mP0); } + inline_ float ComputeSquareLength() const { return mP1.SquareDistance(mP0); } + + inline_ void SetOriginDirection(const Point& origin, const Point& direction) + { + mP0 = mP1 = origin; + mP1 += direction; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Computes a point on the segment + * \param pt [out] point on segment + * \param t [in] point's parameter [t=0 => pt = mP0, t=1 => pt = mP1] + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void ComputePoint(Point& pt, float t) const { pt = mP0 + t * (mP1 - mP0); } + + float SquareDistance(const Point& point, float* t=null) const; + inline_ float Distance(const Point& point, float* t=null) const { return sqrtf(SquareDistance(point, t)); } + + Point mP0; //!< Start of segment + Point mP1; //!< End of segment + }; + +#endif // __ICESEGMENT_H__ diff --git a/Engine/lib/opcode/Ice/IceTriList.h b/Engine/lib/opcode/Ice/IceTriList.h new file mode 100644 index 000000000..c7ecd9b03 --- /dev/null +++ b/Engine/lib/opcode/Ice/IceTriList.h @@ -0,0 +1,61 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for a triangle container. + * \file IceTrilist.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICETRILIST_H__ +#define __ICETRILIST_H__ + + class ICEMATHS_API TriList : public OPC_Container + { + public: + // Constructor / Destructor + TriList() {} + ~TriList() {} + + inline_ udword GetNbTriangles() const { return GetNbEntries()/9; } + inline_ Triangle* GetTriangles() const { return (Triangle*)GetEntries(); } + + void AddTri(const Triangle& tri) + { + Add(tri.mVerts[0].x).Add(tri.mVerts[0].y).Add(tri.mVerts[0].z); + Add(tri.mVerts[1].x).Add(tri.mVerts[1].y).Add(tri.mVerts[1].z); + Add(tri.mVerts[2].x).Add(tri.mVerts[2].y).Add(tri.mVerts[2].z); + } + + void AddTri(const Point& p0, const Point& p1, const Point& p2) + { + Add(p0.x).Add(p0.y).Add(p0.z); + Add(p1.x).Add(p1.y).Add(p1.z); + Add(p2.x).Add(p2.y).Add(p2.z); + } + }; + + class ICEMATHS_API TriangleList : public OPC_Container + { + public: + // Constructor / Destructor + TriangleList() {} + ~TriangleList() {} + + inline_ udword GetNbTriangles() const { return GetNbEntries()/3; } + inline_ IndexedTriangle* GetTriangles() const { return (IndexedTriangle*)GetEntries();} + + void AddTriangle(const IndexedTriangle& tri) + { + Add(tri.mVRef[0]).Add(tri.mVRef[1]).Add(tri.mVRef[2]); + } + + void AddTriangle(udword vref0, udword vref1, udword vref2) + { + Add(vref0).Add(vref1).Add(vref2); + } + }; + +#endif //__ICETRILIST_H__ diff --git a/Engine/lib/opcode/Ice/IceTriangle.cpp b/Engine/lib/opcode/Ice/IceTriangle.cpp new file mode 100644 index 000000000..571a0b368 --- /dev/null +++ b/Engine/lib/opcode/Ice/IceTriangle.cpp @@ -0,0 +1,285 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a handy triangle class. + * \file IceTriangle.cpp + * \author Pierre Terdiman + * \date January, 17, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "../Opcode.h" + +using namespace IceMaths; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a triangle class. + * + * \class Tri + * \author Pierre Terdiman + * \version 1.0 + * \date 08.15.98 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static sdword VPlaneSideEps(const Point& v, const Plane& plane, float epsilon) +{ + // Compute distance from current vertex to the plane + float Dist = plane.Distance(v); + // Compute side: + // 1 = the vertex is on the positive side of the plane + // -1 = the vertex is on the negative side of the plane + // 0 = the vertex is on the plane (within epsilon) + return Dist > epsilon ? 1 : Dist < -epsilon ? -1 : 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Flips the winding order. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Triangle::Flip() +{ + Point Tmp = mVerts[1]; + mVerts[1] = mVerts[2]; + mVerts[2] = Tmp; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle area. + * \return the area + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float Triangle::Area() const +{ + const Point& p0 = mVerts[0]; + const Point& p1 = mVerts[1]; + const Point& p2 = mVerts[2]; + return ((p0 - p1)^(p0 - p2)).Magnitude() * 0.5f; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle perimeter. + * \return the perimeter + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float Triangle::Perimeter() const +{ + const Point& p0 = mVerts[0]; + const Point& p1 = mVerts[1]; + const Point& p2 = mVerts[2]; + return p0.Distance(p1) + + p0.Distance(p2) + + p1.Distance(p2); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle compacity. + * \return the compacity + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float Triangle::Compacity() const +{ + float P = Perimeter(); + if(P==0.0f) return 0.0f; + return (4.0f*PI*Area()/(P*P)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle normal. + * \param normal [out] the computed normal + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Triangle::Normal(Point& normal) const +{ + const Point& p0 = mVerts[0]; + const Point& p1 = mVerts[1]; + const Point& p2 = mVerts[2]; + normal = ((p0 - p1)^(p0 - p2)).Normalize(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle denormalized normal. + * \param normal [out] the computed normal + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Triangle::DenormalizedNormal(Point& normal) const +{ + const Point& p0 = mVerts[0]; + const Point& p1 = mVerts[1]; + const Point& p2 = mVerts[2]; + normal = ((p0 - p1)^(p0 - p2)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle center. + * \param center [out] the computed center + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Triangle::Center(Point& center) const +{ + const Point& p0 = mVerts[0]; + const Point& p1 = mVerts[1]; + const Point& p2 = mVerts[2]; + center = (p0 + p1 + p2)*INV3; +} + +PartVal Triangle::TestAgainstPlane(const Plane& plane, float epsilon) const +{ + bool Pos = false, Neg = false; + + // Loop through all vertices + for(udword i=0;i<3;i++) + { + // Compute side: + sdword Side = VPlaneSideEps(mVerts[i], plane, epsilon); + + if (Side < 0) Neg = true; + else if (Side > 0) Pos = true; + } + + if (!Pos && !Neg) return TRI_ON_PLANE; + else if (Pos && Neg) return TRI_INTERSECT; + else if (Pos && !Neg) return TRI_PLUS_SPACE; + else if (!Pos && Neg) return TRI_MINUS_SPACE; + + // What?! + return TRI_FORCEDWORD; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle moment. + * \param m [out] the moment + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* +void Triangle::ComputeMoment(Moment& m) +{ + // Compute the area of the triangle + m.mArea = Area(); + + // Compute the centroid + Center(m.mCentroid); + + // Second-order components. Handle zero-area faces. + Point& p = mVerts[0]; + Point& q = mVerts[1]; + Point& r = mVerts[2]; + if(m.mArea==0.0f) + { + // This triangle has zero area. The second order components would be eliminated with the usual formula, so, for the + // sake of robustness we use an alternative form. These are the centroid and second-order components of the triangle's vertices. + m.mCovariance.m[0][0] = (p.x*p.x + q.x*q.x + r.x*r.x); + m.mCovariance.m[0][1] = (p.x*p.y + q.x*q.y + r.x*r.y); + m.mCovariance.m[0][2] = (p.x*p.z + q.x*q.z + r.x*r.z); + m.mCovariance.m[1][1] = (p.y*p.y + q.y*q.y + r.y*r.y); + m.mCovariance.m[1][2] = (p.y*p.z + q.y*q.z + r.y*r.z); + m.mCovariance.m[2][2] = (p.z*p.z + q.z*q.z + r.z*r.z); + m.mCovariance.m[2][1] = m.mCovariance.m[1][2]; + m.mCovariance.m[1][0] = m.mCovariance.m[0][1]; + m.mCovariance.m[2][0] = m.mCovariance.m[0][2]; + } + else + { + const float OneOverTwelve = 1.0f / 12.0f; + m.mCovariance.m[0][0] = m.mArea * (9.0f * m.mCentroid.x*m.mCentroid.x + p.x*p.x + q.x*q.x + r.x*r.x) * OneOverTwelve; + m.mCovariance.m[0][1] = m.mArea * (9.0f * m.mCentroid.x*m.mCentroid.y + p.x*p.y + q.x*q.y + r.x*r.y) * OneOverTwelve; + m.mCovariance.m[1][1] = m.mArea * (9.0f * m.mCentroid.y*m.mCentroid.y + p.y*p.y + q.y*q.y + r.y*r.y) * OneOverTwelve; + m.mCovariance.m[0][2] = m.mArea * (9.0f * m.mCentroid.x*m.mCentroid.z + p.x*p.z + q.x*q.z + r.x*r.z) * OneOverTwelve; + m.mCovariance.m[1][2] = m.mArea * (9.0f * m.mCentroid.y*m.mCentroid.z + p.y*p.z + q.y*q.z + r.y*r.z) * OneOverTwelve; + m.mCovariance.m[2][2] = m.mArea * (9.0f * m.mCentroid.z*m.mCentroid.z + p.z*p.z + q.z*q.z + r.z*r.z) * OneOverTwelve; + m.mCovariance.m[2][1] = m.mCovariance.m[1][2]; + m.mCovariance.m[1][0] = m.mCovariance.m[0][1]; + m.mCovariance.m[2][0] = m.mCovariance.m[0][2]; + } +} +*/ + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle's smallest edge length. + * \return the smallest edge length + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float Triangle::MinEdgeLength() const +{ + float Min = MAX_FLOAT; + float Length01 = mVerts[0].Distance(mVerts[1]); + float Length02 = mVerts[0].Distance(mVerts[2]); + float Length12 = mVerts[1].Distance(mVerts[2]); + if(Length01 < Min) Min = Length01; + if(Length02 < Min) Min = Length02; + if(Length12 < Min) Min = Length12; + return Min; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the triangle's largest edge length. + * \return the largest edge length + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float Triangle::MaxEdgeLength() const +{ + float Max = MIN_FLOAT; + float Length01 = mVerts[0].Distance(mVerts[1]); + float Length02 = mVerts[0].Distance(mVerts[2]); + float Length12 = mVerts[1].Distance(mVerts[2]); + if(Length01 > Max) Max = Length01; + if(Length02 > Max) Max = Length02; + if(Length12 > Max) Max = Length12; + return Max; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes a point on the triangle according to the stabbing information. + * \param u,v [in] point's barycentric coordinates + * \param pt [out] point on triangle + * \param nearvtx [out] index of nearest vertex + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Triangle::ComputePoint(float u, float v, Point& pt, udword* nearvtx) const +{ + // Compute point coordinates + pt = (1.0f - u - v)*mVerts[0] + u*mVerts[1] + v*mVerts[2]; + + // Compute nearest vertex if needed + if(nearvtx) + { + // Compute distance vector + Point d(mVerts[0].SquareDistance(pt), // Distance^2 from vertex 0 to point on the face + mVerts[1].SquareDistance(pt), // Distance^2 from vertex 1 to point on the face + mVerts[2].SquareDistance(pt)); // Distance^2 from vertex 2 to point on the face + + // Get smallest distance + *nearvtx = d.SmallestAxis(); + } +} + +void Triangle::Inflate(float fat_coeff, bool constant_border) +{ + // Compute triangle center + Point TriangleCenter; + Center(TriangleCenter); + + // Don't normalize? + // Normalize => add a constant border, regardless of triangle size + // Don't => add more to big triangles + for(udword i=0;i<3;i++) + { + Point v = mVerts[i] - TriangleCenter; + + if(constant_border) v.Normalize(); + + mVerts[i] += v * fat_coeff; + } +} diff --git a/Engine/lib/opcode/Ice/IceTriangle.h b/Engine/lib/opcode/Ice/IceTriangle.h new file mode 100644 index 000000000..a984db835 --- /dev/null +++ b/Engine/lib/opcode/Ice/IceTriangle.h @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a handy triangle class. + * \file IceTriangle.h + * \author Pierre Terdiman + * \date January, 17, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICETRIANGLE_H__ +#define __ICETRIANGLE_H__ + + // Forward declarations + class Moment; + + // Partitioning values + enum PartVal + { + TRI_MINUS_SPACE = 0, //!< Triangle is in the negative space + TRI_PLUS_SPACE = 1, //!< Triangle is in the positive space + TRI_INTERSECT = 2, //!< Triangle intersects plane + TRI_ON_PLANE = 3, //!< Triangle and plane are coplanar + + TRI_FORCEDWORD = 0x7fffffff + }; + + // A triangle class. + class ICEMATHS_API Triangle + { + public: + //! Constructor + inline_ Triangle() {} + //! Constructor + inline_ Triangle(const Point& p0, const Point& p1, const Point& p2) { mVerts[0]=p0; mVerts[1]=p1; mVerts[2]=p2; } + //! Copy constructor + inline_ Triangle(const Triangle& triangle) + { + mVerts[0] = triangle.mVerts[0]; + mVerts[1] = triangle.mVerts[1]; + mVerts[2] = triangle.mVerts[2]; + } + //! Destructor + inline_ ~Triangle() {} + //! Vertices + Point mVerts[3]; + + // Methods + void Flip(); + float Area() const; + float Perimeter() const; + float Compacity() const; + void Normal(Point& normal) const; + void DenormalizedNormal(Point& normal) const; + void Center(Point& center) const; + inline_ Plane PlaneEquation() const { return Plane(mVerts[0], mVerts[1], mVerts[2]); } + + PartVal TestAgainstPlane(const Plane& plane, float epsilon) const; +// float Distance(Point& cp, Point& cq, Tri& tri); + void ComputeMoment(Moment& m); + float MinEdgeLength() const; + float MaxEdgeLength() const; + void ComputePoint(float u, float v, Point& pt, udword* nearvtx=null) const; + void Inflate(float fat_coeff, bool constant_border); + }; + +#endif // __ICETRIANGLE_H__ diff --git a/Engine/lib/opcode/Ice/IceTypes.h b/Engine/lib/opcode/Ice/IceTypes.h new file mode 100644 index 000000000..fc1f0575a --- /dev/null +++ b/Engine/lib/opcode/Ice/IceTypes.h @@ -0,0 +1,168 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains custom types. + * \file IceTypes.h + * \author Pierre Terdiman + * \date April, 4, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __ICETYPES_H__ +#define __ICETYPES_H__ + + #define USE_HANDLE_MANAGER + + // Constants + #define PI 3.1415926535897932384626433832795028841971693993751f //!< PI + #define HALFPI 1.57079632679489661923f //!< 0.5 * PI + #define TWOPI 6.28318530717958647692f //!< 2.0 * PI + #define INVPI 0.31830988618379067154f //!< 1.0 / PI + + #define RADTODEG 57.2957795130823208768f //!< 180.0 / PI, convert radians to degrees + #define DEGTORAD 0.01745329251994329577f //!< PI / 180.0, convert degrees to radians + + #define EXP 2.71828182845904523536f //!< e + #define INVLOG2 3.32192809488736234787f //!< 1.0 / log10(2) + #define LN2 0.693147180559945f //!< ln(2) + #define INVLN2 1.44269504089f //!< 1.0f / ln(2) + + #define INV3 0.33333333333333333333f //!< 1/3 + #define INV6 0.16666666666666666666f //!< 1/6 + #define INV7 0.14285714285714285714f //!< 1/7 + #define INV9 0.11111111111111111111f //!< 1/9 + #define INV255 0.00392156862745098039f //!< 1/255 + + #define SQRT2 1.41421356237f //!< sqrt(2) + #define INVSQRT2 0.707106781188f //!< 1 / sqrt(2) + + #define SQRT3 1.73205080757f //!< sqrt(3) + #define INVSQRT3 0.577350269189f //!< 1 / sqrt(3) + + #define null 0 //!< our own NULL pointer + + // Custom types used in ICE + typedef signed char sbyte; //!< sizeof(sbyte) must be 1 + typedef unsigned char ubyte; //!< sizeof(ubyte) must be 1 + typedef signed short sword; //!< sizeof(sword) must be 2 + typedef unsigned short uword; //!< sizeof(uword) must be 2 + typedef signed int sdword; //!< sizeof(sdword) must be 4 + typedef unsigned int udword; //!< sizeof(udword) must be 4 +#ifdef _WIN32 + typedef signed __int64 sqword; //!< sizeof(sqword) must be 8 + typedef unsigned __int64 uqword; //!< sizeof(uqword) must be 8 +#else + typedef long long sqword; + typedef unsigned long long uqword; +#endif + typedef float float32; //!< sizeof(float32) must be 4 + typedef double float64; //!< sizeof(float64) must be 4 + +#ifdef _WIN32 + ICE_COMPILE_TIME_ASSERT(sizeof(bool)==1); // ...otherwise things might fail with VC++ 4.2 ! +#endif + ICE_COMPILE_TIME_ASSERT(sizeof(ubyte)==1); + ICE_COMPILE_TIME_ASSERT(sizeof(sbyte)==1); + ICE_COMPILE_TIME_ASSERT(sizeof(sword)==2); + ICE_COMPILE_TIME_ASSERT(sizeof(uword)==2); + ICE_COMPILE_TIME_ASSERT(sizeof(udword)==4); + ICE_COMPILE_TIME_ASSERT(sizeof(sdword)==4); + ICE_COMPILE_TIME_ASSERT(sizeof(uqword)==8); + ICE_COMPILE_TIME_ASSERT(sizeof(sqword)==8); + + //! TO BE DOCUMENTED + #define DECLARE_ICE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name + + typedef udword DynID; //!< Dynamic identifier +#ifdef USE_HANDLE_MANAGER + typedef udword KID; //!< Kernel ID +// DECLARE_ICE_HANDLE(KID); +#else + typedef uword KID; //!< Kernel ID +#endif + typedef udword RTYPE; //!< Relationship-type (!) between owners and references + #define INVALID_ID_OPC 0xffffffff //!< Invalid dword ID (counterpart of null pointers) +#ifdef USE_HANDLE_MANAGER + #define INVALID_KID 0xffffffff //!< Invalid Kernel ID +#else + #define INVALID_KID 0xffff //!< Invalid Kernel ID +#endif + #define INVALID_NUMBER 0xDEADBEEF //!< Standard junk value + + // Define BOOL if needed + #ifndef BOOL + typedef int BOOL; //!< Another boolean type. + #endif + + //! Union of a float and a sdword + typedef union { + float f; //!< The float + sdword d; //!< The integer + }scell; + + //! Union of a float and a udword + typedef union { + float f; //!< The float + udword d; //!< The integer + }ucell; + + // Type ranges + #define MAX_SBYTE 0x7f //!< max possible sbyte value + #define MIN_SBYTE 0x80 //!< min possible sbyte value + #define MAX_UBYTE 0xff //!< max possible ubyte value + #define MIN_UBYTE 0x00 //!< min possible ubyte value + #define MAX_SWORD 0x7fff //!< max possible sword value + #define MIN_SWORD 0x8000 //!< min possible sword value + #define MAX_UWORD 0xffff //!< max possible uword value + #define MIN_UWORD 0x0000 //!< min possible uword value + #define MAX_SDWORD 0x7fffffff //!< max possible sdword value + #define MIN_SDWORD 0x80000000 //!< min possible sdword value + #define MAX_UDWORD 0xffffffff //!< max possible udword value + #define MIN_UDWORD 0x00000000 //!< min possible udword value + #define MAX_FLOAT FLT_MAX //!< max possible float value + #define MIN_FLOAT (-FLT_MAX) //!< min possible loat value + #define IEEE_1_0 0x3f800000 //!< integer representation of 1.0 + #define IEEE_255_0 0x437f0000 //!< integer representation of 255.0 + #define IEEE_MAX_FLOAT 0x7f7fffff //!< integer representation of MAX_FLOAT + #define IEEE_MIN_FLOAT 0xff7fffff //!< integer representation of MIN_FLOAT + #define IEEE_UNDERFLOW_LIMIT 0x1a000000 + + #define ONE_OVER_RAND_MAX (1.0f / float(RAND_MAX)) //!< Inverse of the max possible value returned by rand() + +#ifdef _WIN32 + typedef int (__stdcall* PROC)(); //!< A standard procedure call. +#else + typedef int (* PROC)(); //!< A standard procedure call. +#endif + typedef bool (*ENUMERATION)(udword value, udword param, udword context); //!< ICE standard enumeration call + typedef void** VTABLE; //!< A V-Table. + + #undef MIN + #undef MAX + #define MIN(a, b) ((a) < (b) ? (a) : (b)) //!< Returns the min value between a and b + #define MAX(a, b) ((a) > (b) ? (a) : (b)) //!< Returns the max value between a and b + #define MAXMAX(a,b,c) ((a) > (b) ? MAX (a,c) : MAX (b,c)) //!< Returns the max value between a, b and c + + template inline_ const T& TMin (const T& a, const T& b) { return b < a ? b : a; } + template inline_ const T& TMax (const T& a, const T& b) { return a < b ? b : a; } + template inline_ void TSetMin (T& a, const T& b) { if(a>b) a = b; } + template inline_ void TSetMax (T& a, const T& b) { if(a> 1) & 0x55555555) | ((n << 1) & 0xaaaaaaaa); + n = ((n >> 2) & 0x33333333) | ((n << 2) & 0xcccccccc); + n = ((n >> 4) & 0x0f0f0f0f) | ((n << 4) & 0xf0f0f0f0); + n = ((n >> 8) & 0x00ff00ff) | ((n << 8) & 0xff00ff00); + n = ((n >> 16) & 0x0000ffff) | ((n << 16) & 0xffff0000); + // Etc for larger intergers (64 bits in Java) + // NOTE: the >> operation must be unsigned! (>>> in java) + } + + //! Count the number of '1' bits in a 32 bit word (from Steve Baker's Cute Code Collection) + inline_ udword CountBits(udword n) + { + // This relies of the fact that the count of n bits can NOT overflow + // an n bit interger. EG: 1 bit count takes a 1 bit interger, 2 bit counts + // 2 bit interger, 3 bit count requires only a 2 bit interger. + // So we add all bit pairs, then each nible, then each byte etc... + n = (n & 0x55555555) + ((n & 0xaaaaaaaa) >> 1); + n = (n & 0x33333333) + ((n & 0xcccccccc) >> 2); + n = (n & 0x0f0f0f0f) + ((n & 0xf0f0f0f0) >> 4); + n = (n & 0x00ff00ff) + ((n & 0xff00ff00) >> 8); + n = (n & 0x0000ffff) + ((n & 0xffff0000) >> 16); + // Etc for larger intergers (64 bits in Java) + // NOTE: the >> operation must be unsigned! (>>> in java) + return n; + } + + //! Even faster? + inline_ udword CountBits2(udword bits) + { + bits = bits - ((bits >> 1) & 0x55555555); + bits = ((bits >> 2) & 0x33333333) + (bits & 0x33333333); + bits = ((bits >> 4) + bits) & 0x0F0F0F0F; + return (bits * 0x01010101) >> 24; + } + + //! Spread out bits. EG 00001111 -> 0101010101 + //! 00001010 -> 0100010000 + //! This is used to interleve to intergers to produce a `Morten Key' + //! used in Space Filling Curves (See DrDobbs Journal, July 1999) + //! Order is important. + inline_ void SpreadBits(udword& n) + { + n = ( n & 0x0000ffff) | (( n & 0xffff0000) << 16); + n = ( n & 0x000000ff) | (( n & 0x0000ff00) << 8); + n = ( n & 0x000f000f) | (( n & 0x00f000f0) << 4); + n = ( n & 0x03030303) | (( n & 0x0c0c0c0c) << 2); + n = ( n & 0x11111111) | (( n & 0x22222222) << 1); + } + + // Next Largest Power of 2 + // Given a binary integer value x, the next largest power of 2 can be computed by a SWAR algorithm + // that recursively "folds" the upper bits into the lower bits. This process yields a bit vector with + // the same most significant 1 as x, but all 1's below it. Adding 1 to that value yields the next + // largest power of 2. For a 32-bit value: + inline_ udword nlpo2(udword x) + { + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + return x+1; + } + + //! Test to see if a number is an exact power of two (from Steve Baker's Cute Code Collection) + inline_ bool IsPowerOfTwo(udword n) { return ((n&(n-1))==0); } + + //! Zero the least significant '1' bit in a word. (from Steve Baker's Cute Code Collection) + inline_ void ZeroLeastSetBit(udword& n) { n&=(n-1); } + + //! Set the least significant N bits in a word. (from Steve Baker's Cute Code Collection) + inline_ void SetLeastNBits(udword& x, udword n) { x|=~(~0<> 31; return (x^y)-y; } + + //!< Alternative min function + inline_ sdword min_(sdword a, sdword b) { sdword delta = b-a; return a + (delta&(delta>>31)); } + + // Determine if one of the bytes in a 4 byte word is zero + inline_ BOOL HasNullByte(udword x) { return ((x + 0xfefefeff) & (~x) & 0x80808080); } + + // To find the smallest 1 bit in a word EG: ~~~~~~10---0 => 0----010---0 + inline_ udword LowestOneBit(udword w) { return ((w) & (~(w)+1)); } +// inline_ udword LowestOneBit_(udword w) { return ((w) & (-(w))); } + + // Most Significant 1 Bit + // Given a binary integer value x, the most significant 1 bit (highest numbered element of a bit set) + // can be computed using a SWAR algorithm that recursively "folds" the upper bits into the lower bits. + // This process yields a bit vector with the same most significant 1 as x, but all 1's below it. + // Bitwise AND of the original value with the complement of the "folded" value shifted down by one + // yields the most significant bit. For a 32-bit value: + inline_ udword msb32(udword x) + { + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + return (x & ~(x >> 1)); + } + + /* + "Just call it repeatedly with various input values and always with the same variable as "memory". + The sharpness determines the degree of filtering, where 0 completely filters out the input, and 1 + does no filtering at all. + + I seem to recall from college that this is called an IIR (Infinite Impulse Response) filter. As opposed + to the more typical FIR (Finite Impulse Response). + + Also, I'd say that you can make more intelligent and interesting filters than this, for example filters + that remove wrong responses from the mouse because it's being moved too fast. You'd want such a filter + to be applied before this one, of course." + + (JCAB on Flipcode) + */ + inline_ float FeedbackFilter(float val, float& memory, float sharpness) + { + ASSERT(sharpness>=0.0f && sharpness<=1.0f && "Invalid sharpness value in feedback filter"); + if(sharpness<0.0f) sharpness = 0.0f; + else if(sharpness>1.0f) sharpness = 1.0f; + return memory = val * sharpness + memory * (1.0f - sharpness); + } + + //! If you can guarantee that your input domain (i.e. value of x) is slightly + //! limited (abs(x) must be < ((1<<31u)-32767)), then you can use the + //! following code to clamp the resulting value into [-32768,+32767] range: + inline_ int ClampToInt16(int x) + { +// ASSERT(abs(x) < (int)((1<<31u)-32767)); + + int delta = 32767 - x; + x += (delta>>31) & delta; + delta = x + 32768; + x -= (delta>>31) & delta; + return x; + } + + // Generic functions + template inline_ void TSwap(Type& a, Type& b) { const Type c = a; a = b; b = c; } + template inline_ Type TClamp(const Type& x, const Type& lo, const Type& hi) { return ((xhi) ? hi : x); } + + template inline_ void TSort(Type& a, Type& b) + { + if(a>b) TSwap(a, b); + } + + template inline_ void TSort(Type& a, Type& b, Type& c) + { + if(a>b) TSwap(a, b); + if(b>c) TSwap(b, c); + if(a>b) TSwap(a, b); + if(b>c) TSwap(b, c); + } + + // Prevent nasty user-manipulations (strategy borrowed from Charles Bloom) +// #define PREVENT_COPY(curclass) void operator = (const curclass& object) { ASSERT(!"Bad use of operator ="); } + // ... actually this is better ! + #define PREVENT_COPY(cur_class) private: cur_class(const cur_class& object); cur_class& operator=(const cur_class& object); + + //! TO BE DOCUMENTED + #define OFFSET_OF(Class, Member) (size_t)&(((Class*)0)->Member) + //! TO BE DOCUMENTED + #define ARRAYSIZE(p) (sizeof(p)/sizeof(p[0])) + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Returns the alignment of the input address. + * \fn Alignment() + * \param address [in] address to check + * \return the best alignment (e.g. 1 for odd addresses, etc) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + FUNCTION ICECORE_API udword Alignment(udword address); + + #define IS_ALIGNED_2(x) ((x&1)==0) + #define IS_ALIGNED_4(x) ((x&3)==0) + #define IS_ALIGNED_8(x) ((x&7)==0) + + inline_ void _prefetch(void const* ptr) { (void)*(char const volatile *)ptr; } + + // Compute implicit coords from an index: + // The idea is to get back 2D coords from a 1D index. + // For example: + // + // 0 1 2 ... nbu-1 + // nbu nbu+1 i ... + // + // We have i, we're looking for the equivalent (u=2, v=1) location. + // i = u + v*nbu + // <=> i/nbu = u/nbu + v + // Since 0 <= u < nbu, u/nbu = 0 (integer) + // Hence: v = i/nbu + // Then we simply put it back in the original equation to compute u = i - v*nbu + inline_ void Compute2DCoords(udword& u, udword& v, udword i, udword nbu) + { + v = i / nbu; + u = i - (v * nbu); + } + + // In 3D: i = u + v*nbu + w*nbu*nbv + // <=> i/(nbu*nbv) = u/(nbu*nbv) + v/nbv + w + // u/(nbu*nbv) is null since u/nbu was null already. + // v/nbv is null as well for the same reason. + // Hence w = i/(nbu*nbv) + // Then we're left with a 2D problem: i' = i - w*nbu*nbv = u + v*nbu + inline_ void Compute3DCoords(udword& u, udword& v, udword& w, udword i, udword nbu, udword nbu_nbv) + { + w = i / (nbu_nbv); + Compute2DCoords(u, v, i - (w * nbu_nbv), nbu); + } + +#endif // __ICEUTILS_H__ diff --git a/Engine/lib/opcode/OPC_AABBCollider.cpp b/Engine/lib/opcode/OPC_AABBCollider.cpp new file mode 100644 index 000000000..ecb5d4e1e --- /dev/null +++ b/Engine/lib/opcode/OPC_AABBCollider.cpp @@ -0,0 +1,695 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for an AABB collider. + * \file OPC_AABBCollider.cpp + * \author Pierre Terdiman + * \date January, 1st, 2002 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains an AABB-vs-tree collider. + * + * \class AABBCollider + * \author Pierre Terdiman + * \version 1.3 + * \date January, 1st, 2002 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "Opcode.h" + +using namespace Opcode; + +#include "OPC_BoxBoxOverlap.h" +#include "OPC_TriBoxOverlap.h" + +#define SET_CONTACT(prim_index, flag) \ + /* Set contact status */ \ + mFlags |= flag; \ + mTouchedPrimitives->Add(prim_index); + +//! AABB-triangle test +#define AABB_PRIM(prim_index, flag) \ + /* Request vertices from the app */ \ + VertexPointers VP; mIMesh->GetTriangle(VP, prim_index);\ + mLeafVerts[0] = *VP.Vertex[0]; \ + mLeafVerts[1] = *VP.Vertex[1]; \ + mLeafVerts[2] = *VP.Vertex[2]; \ + /* Perform triangle-box overlap test */ \ + if(TriBoxOverlap()) \ + { \ + SET_CONTACT(prim_index, flag) \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBCollider::AABBCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBCollider::~AABBCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Generic collision query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - with GetNbTouchedPrimitives() + * - with GetTouchedPrimitives() + * + * \param cache [in/out] a box cache + * \param box [in] collision AABB in world space + * \param model [in] Opcode model to collide with + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBCollider::Collide(AABBCache& cache, const CollisionAABB& box, const Model& model) +{ + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, box)) return true; + + if(!model.HasLeafNodes()) + { + if(model.IsQuantized()) + { + const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + else + { + const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree(); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + else + { + const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree(); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Initializes a collision query : + * - reset stats & contact status + * - check temporal coherence + * + * \param cache [in/out] a box cache + * \param box [in] AABB in world space + * \return TRUE if we can return immediately + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +BOOL AABBCollider::InitQuery(AABBCache& cache, const CollisionAABB& box) +{ + // 1) Call the base method + VolumeCollider::InitQuery(); + + // 2) Keep track of the query box + mBox = box; + + // 3) Setup destination pointer + mTouchedPrimitives = &cache.TouchedPrimitives; + + // 4) Special case: 1-triangle meshes [Opcode 1.3] + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + if(!SkipPrimitiveTests()) + { + // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0. + mTouchedPrimitives->Reset(); + + // Perform overlap test between the unique triangle and the box (and set contact status if needed) + AABB_PRIM(udword(0), OPC_CONTACT) + + // Return immediately regardless of status + return TRUE; + } + } + + // 5) Check temporal coherence : + if(TemporalCoherenceEnabled()) + { + // Here we use temporal coherence + // => check results from previous frame before performing the collision query + if(FirstContactEnabled()) + { + // We're only interested in the first contact found => test the unique previously touched face + if(mTouchedPrimitives->GetNbEntries()) + { + // Get index of previously touched face = the first entry in the array + udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0); + + // Then reset the array: + // - if the overlap test below is successful, the index we'll get added back anyway + // - if it isn't, then the array should be reset anyway for the normal query + mTouchedPrimitives->Reset(); + + // Perform overlap test between the cached triangle and the box (and set contact status if needed) + AABB_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT) + + // Return immediately if possible + if(GetContactStatus()) return TRUE; + } + // else no face has been touched during previous query + // => we'll have to perform a normal query + } + else + { + // We're interested in all contacts =>test the new real box N(ew) against the previous fat box P(revious): + if(IsCacheValid(cache) && mBox.IsInside(cache.FatBox)) + { + // - if N is included in P, return previous list + // => we simply leave the list (mTouchedFaces) unchanged + + // Set contact status if needed + if(mTouchedPrimitives->GetNbEntries()) mFlags |= OPC_TEMPORAL_CONTACT; + + // In any case we don't need to do a query + return TRUE; + } + else + { + // - else do the query using a fat N + + // Reset cache since we'll about to perform a real query + mTouchedPrimitives->Reset(); + + // Make a fat box so that coherence will work for subsequent frames + mBox.mExtents *= cache.FatCoeff; + + // Update cache with query data (signature for cached faces) + cache.FatBox = mBox; + } + } + } + else + { + // Here we don't use temporal coherence => do a normal query + mTouchedPrimitives->Reset(); + } + + // 5) Precompute min & max bounds if needed + mMin = box.mCenter - box.mExtents; + mMax = box.mCenter + box.mExtents; + + return FALSE; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Collision query for vanilla AABB trees. + * \param cache [in/out] a box cache + * \param box [in] collision AABB in world space + * \param tree [in] AABB tree + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBCollider::Collide(AABBCache& cache, const CollisionAABB& box, const AABBTree* tree) +{ + // This is typically called for a scene tree, full of -AABBs-, not full of triangles. + // So we don't really have "primitives" to deal with. Hence it doesn't work with + // "FirstContact" + "TemporalCoherence". + ASSERT( !(FirstContactEnabled() && TemporalCoherenceEnabled()) ); + + // Checkings + if(!tree) return false; + + // Init collision query + if(InitQuery(cache, box)) return true; + + // Perform collision query + _Collide(tree); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks the AABB completely contains the box. In which case we can end the query sooner. + * \param bc [in] box center + * \param be [in] box extents + * \return true if the AABB contains the whole box + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ BOOL AABBCollider::AABBContainsBox(const Point& bc, const Point& be) +{ + if(mMin.x > bc.x - be.x) return FALSE; + if(mMin.y > bc.y - be.y) return FALSE; + if(mMin.z > bc.z - be.z) return FALSE; + + if(mMax.x < bc.x + be.x) return FALSE; + if(mMax.y < bc.y + be.y) return FALSE; + if(mMax.z < bc.z + be.z) return FALSE; + + return TRUE; +} + +#define TEST_BOX_IN_AABB(center, extents) \ + if(AABBContainsBox(center, extents)) \ + { \ + /* Set contact status */ \ + mFlags |= OPC_CONTACT; \ + _Dump(node); \ + return; \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBCollider::_Collide(const AABBCollisionNode* node) +{ + // Perform AABB-AABB overlap test + if(!AABBAABBOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return; + + TEST_BOX_IN_AABB(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->IsLeaf()) + { + AABB_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _Collide(node->GetPos()); + + if(ContactFound()) return; + + _Collide(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBCollider::_CollideNoPrimitiveTest(const AABBCollisionNode* node) +{ + // Perform AABB-AABB overlap test + if(!AABBAABBOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return; + + TEST_BOX_IN_AABB(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBCollider::_Collide(const AABBQuantizedNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform AABB-AABB overlap test + if(!AABBAABBOverlap(Extents, Center)) return; + + TEST_BOX_IN_AABB(Center, Extents) + + if(node->IsLeaf()) + { + AABB_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _Collide(node->GetPos()); + + if(ContactFound()) return; + + _Collide(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBCollider::_CollideNoPrimitiveTest(const AABBQuantizedNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform AABB-AABB overlap test + if(!AABBAABBOverlap(Extents, Center)) return; + + TEST_BOX_IN_AABB(Center, Extents) + + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBCollider::_Collide(const AABBNoLeafNode* node) +{ + // Perform AABB-AABB overlap test + if(!AABBAABBOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return; + + TEST_BOX_IN_AABB(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->HasPosLeaf()) { AABB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } + else _Collide(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { AABB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } + else _Collide(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBCollider::_CollideNoPrimitiveTest(const AABBNoLeafNode* node) +{ + // Perform AABB-AABB overlap test + if(!AABBAABBOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return; + + TEST_BOX_IN_AABB(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBCollider::_Collide(const AABBQuantizedNoLeafNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform AABB-AABB overlap test + if(!AABBAABBOverlap(Extents, Center)) return; + + TEST_BOX_IN_AABB(Center, Extents) + + if(node->HasPosLeaf()) { AABB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } + else _Collide(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { AABB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } + else _Collide(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBCollider::_CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform AABB-AABB overlap test + if(!AABBAABBOverlap(Extents, Center)) return; + + TEST_BOX_IN_AABB(Center, Extents) + + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for vanilla AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBCollider::_Collide(const AABBTreeNode* node) +{ + // Perform AABB-AABB overlap test + Point Center, Extents; + node->GetAABB()->GetCenter(Center); + node->GetAABB()->GetExtents(Extents); + if(!AABBAABBOverlap(Center, Extents)) return; + + if(node->IsLeaf() || AABBContainsBox(Center, Extents)) + { + mFlags |= OPC_CONTACT; + mTouchedPrimitives->Add(node->GetPrimitives(), node->GetNbPrimitives()); + } + else + { + _Collide(node->GetPos()); + _Collide(node->GetNeg()); + } +} + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridAABBCollider::HybridAABBCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridAABBCollider::~HybridAABBCollider() +{ +} + +bool HybridAABBCollider::Collide(AABBCache& cache, const CollisionAABB& box, const HybridModel& model) +{ + // We don't want primitive tests here! + mFlags |= OPC_NO_PRIMITIVE_TESTS; + + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, box)) return true; + + // Special case for 1-leaf trees + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles + udword Nb = mIMesh->GetNbTriangles(); + + // Loop through all triangles + for(udword i=0;imCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + else + { + const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree(); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + else + { + const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree(); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + } + + // We only have a list of boxes so far + if(GetContactStatus()) + { + // Reset contact status, since it currently only reflects collisions with leaf boxes + Collider::InitQuery(); + + // Change dest container so that we can use built-in overlap tests and get collided primitives + cache.TouchedPrimitives.Reset(); + mTouchedPrimitives = &cache.TouchedPrimitives; + + // Read touched leaf boxes + udword Nb = mTouchedBoxes.GetNbEntries(); + const udword* Touched = mTouchedBoxes.GetEntries(); + + const LeafTriangles* LT = model.GetLeafTriangles(); + const udword* Indices = model.GetIndices(); + + // Loop through touched leaves + while(Nb--) + { + const LeafTriangles& CurrentLeaf = LT[*Touched++]; + + // Each leaf box has a set of triangles + udword NbTris = CurrentLeaf.GetNbTriangles(); + if(Indices) + { + const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()]; + + // Loop through triangles and test each of them + while(NbTris--) + { + udword TriangleIndex = *T++; + AABB_PRIM(TriangleIndex, OPC_CONTACT) + } + } + else + { + udword BaseIndex = CurrentLeaf.GetTriangleIndex(); + + // Loop through triangles and test each of them + while(NbTris--) + { + udword TriangleIndex = BaseIndex++; + AABB_PRIM(TriangleIndex, OPC_CONTACT) + } + } + } + } + + return true; +} diff --git a/Engine/lib/opcode/OPC_AABBCollider.h b/Engine/lib/opcode/OPC_AABBCollider.h new file mode 100644 index 000000000..34c6c7371 --- /dev/null +++ b/Engine/lib/opcode/OPC_AABBCollider.h @@ -0,0 +1,97 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for an AABB collider. + * \file OPC_AABBCollider.h + * \author Pierre Terdiman + * \date January, 1st, 2002 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_AABBCOLLIDER_H__ +#define __OPC_AABBCOLLIDER_H__ + + struct OPCODE_API AABBCache : VolumeCache + { + AABBCache() : FatCoeff(1.1f) + { + FatBox.mCenter.Zero(); + FatBox.mExtents.Zero(); + } + + // Cached faces signature + CollisionAABB FatBox; //!< Box used when performing the query resulting in cached faces + // User settings + float FatCoeff; //!< mRadius2 multiplier used to create a fat sphere + }; + + class OPCODE_API AABBCollider : public VolumeCollider + { + public: + // Constructor / Destructor + AABBCollider(); + virtual ~AABBCollider(); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Generic collision query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - with GetNbTouchedPrimitives() + * - with GetTouchedPrimitives() + * + * \param cache [in/out] a box cache + * \param box [in] collision AABB in world space + * \param model [in] Opcode model to collide with + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool Collide(AABBCache& cache, const CollisionAABB& box, const Model& model); + // + bool Collide(AABBCache& cache, const CollisionAABB& box, const AABBTree* tree); + protected: + CollisionAABB mBox; //!< Query box in (center, extents) form + Point mMin; //!< Query box min point + Point mMax; //!< Query box max point + // Leaf description + Point mLeafVerts[3]; //!< Triangle vertices + // Internal methods + void _Collide(const AABBCollisionNode* node); + void _Collide(const AABBNoLeafNode* node); + void _Collide(const AABBQuantizedNode* node); + void _Collide(const AABBQuantizedNoLeafNode* node); + void _Collide(const AABBTreeNode* node); + void _CollideNoPrimitiveTest(const AABBCollisionNode* node); + void _CollideNoPrimitiveTest(const AABBNoLeafNode* node); + void _CollideNoPrimitiveTest(const AABBQuantizedNode* node); + void _CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node); + // Overlap tests + inline_ BOOL AABBContainsBox(const Point& bc, const Point& be); + inline_ BOOL AABBAABBOverlap(const Point& b, const Point& Pb); + inline_ BOOL TriBoxOverlap(); + // Init methods + BOOL InitQuery(AABBCache& cache, const CollisionAABB& box); + }; + + class OPCODE_API HybridAABBCollider : public AABBCollider + { + public: + // Constructor / Destructor + HybridAABBCollider(); + virtual ~HybridAABBCollider(); + + bool Collide(AABBCache& cache, const CollisionAABB& box, const HybridModel& model); + protected: + OPC_Container mTouchedBoxes; + }; + +#endif // __OPC_AABBCOLLIDER_H__ diff --git a/Engine/lib/opcode/OPC_AABBTree.cpp b/Engine/lib/opcode/OPC_AABBTree.cpp new file mode 100644 index 000000000..0ad32a6e1 --- /dev/null +++ b/Engine/lib/opcode/OPC_AABBTree.cpp @@ -0,0 +1,572 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for a versatile AABB tree. + * \file OPC_AABBTree.cpp + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a generic AABB tree node. + * + * \class AABBTreeNode + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a generic AABB tree. + * This is a vanilla AABB tree, without any particular optimization. It contains anonymous references to + * user-provided primitives, which can theoretically be anything - triangles, boxes, etc. Each primitive + * is surrounded by an AABB, regardless of the primitive's nature. When the primitive is a triangle, the + * resulting tree can be converted into an optimized tree. If the primitive is a box, the resulting tree + * can be used for culling - VFC or occlusion -, assuming you cull on a mesh-by-mesh basis (modern way). + * + * \class AABBTree + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "Opcode.h" + +using namespace Opcode; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBTreeNode::AABBTreeNode() : + mPos (null), +#ifndef OPC_NO_NEG_VANILLA_TREE + mNeg (null), +#endif + mNbPrimitives (0), + mNodePrimitives (null) +{ +#ifdef OPC_USE_TREE_COHERENCE + mBitmask = 0; +#endif +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBTreeNode::~AABBTreeNode() +{ + // Opcode 1.3: + const AABBTreeNode* Pos = GetPos(); + const AABBTreeNode* Neg = GetNeg(); +#ifndef OPC_NO_NEG_VANILLA_TREE + if(!(mPos&1)) DELETESINGLE(Pos); + if(!(mNeg&1)) DELETESINGLE(Neg); +#else + if(!(mPos&1)) DELETEARRAY(Pos); +#endif + mNodePrimitives = null; // This was just a shortcut to the global list => no release + mNbPrimitives = 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Splits the node along a given axis. + * The list of indices is reorganized according to the split values. + * \param axis [in] splitting axis index + * \param builder [in] the tree builder + * \return the number of primitives assigned to the first child + * \warning this method reorganizes the internal list of primitives + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +udword AABBTreeNode::Split(udword axis, AABBTreeBuilder* builder) +{ + // Get node split value + float SplitValue = builder->GetSplittingValue(mNodePrimitives, mNbPrimitives, mBV, axis); + + udword NbPos = 0; + // Loop through all node-related primitives. Their indices range from mNodePrimitives[0] to mNodePrimitives[mNbPrimitives-1]. + // Those indices map the global list in the tree builder. + for(udword i=0;iGetSplittingValue(Index, axis); + + // Reorganize the list of indices in this order: positive - negative. + if(PrimitiveValue > SplitValue) + { + // Swap entries + udword Tmp = mNodePrimitives[i]; + mNodePrimitives[i] = mNodePrimitives[NbPos]; + mNodePrimitives[NbPos] = Tmp; + // Count primitives assigned to positive space + NbPos++; + } + } + return NbPos; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Subdivides the node. + * + * N + * / \ + * / \ + * N/2 N/2 + * / \ / \ + * N/4 N/4 N/4 N/4 + * (etc) + * + * A well-balanced tree should have a O(log n) depth. + * A degenerate tree would have a O(n) depth. + * Note a perfectly-balanced tree is not well-suited to collision detection anyway. + * + * \param builder [in] the tree builder + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTreeNode::Subdivide(AABBTreeBuilder* builder) +{ + // Checkings + if(!builder) return false; + + // Stop subdividing if we reach a leaf node. This is always performed here, + // else we could end in trouble if user overrides this. + if(mNbPrimitives==1) return true; + + // Let the user validate the subdivision + if(!builder->ValidateSubdivision(mNodePrimitives, mNbPrimitives, mBV)) return true; + + bool ValidSplit = true; // Optimism... + udword NbPos; + if(builder->mSettings.mRules & SPLIT_LARGEST_AXIS) + { + // Find the largest axis to split along + Point Extents; mBV.GetExtents(Extents); // Box extents + udword Axis = Extents.LargestAxis(); // Index of largest axis + + // Split along the axis + NbPos = Split(Axis, builder); + + // Check split validity + if(!NbPos || NbPos==mNbPrimitives) ValidSplit = false; + } + else if(builder->mSettings.mRules & SPLIT_SPLATTER_POINTS) + { + // Compute the means + Point Means(0.0f, 0.0f, 0.0f); + for(udword i=0;iGetSplittingValue(Index, 0); + Means.y+=builder->GetSplittingValue(Index, 1); + Means.z+=builder->GetSplittingValue(Index, 2); + } + Means/=float(mNbPrimitives); + + // Compute variances + Point Vars(0.0f, 0.0f, 0.0f); + for(udword i=0;iGetSplittingValue(Index, 0); + float Cy = builder->GetSplittingValue(Index, 1); + float Cz = builder->GetSplittingValue(Index, 2); + Vars.x += (Cx - Means.x)*(Cx - Means.x); + Vars.y += (Cy - Means.y)*(Cy - Means.y); + Vars.z += (Cz - Means.z)*(Cz - Means.z); + } + Vars/=float(mNbPrimitives-1); + + // Choose axis with greatest variance + udword Axis = Vars.LargestAxis(); + + // Split along the axis + NbPos = Split(Axis, builder); + + // Check split validity + if(!NbPos || NbPos==mNbPrimitives) ValidSplit = false; + } + else if(builder->mSettings.mRules & SPLIT_BALANCED) + { + // Test 3 axis, take the best + float Results[3]; + NbPos = Split(0, builder); Results[0] = float(NbPos)/float(mNbPrimitives); + NbPos = Split(1, builder); Results[1] = float(NbPos)/float(mNbPrimitives); + NbPos = Split(2, builder); Results[2] = float(NbPos)/float(mNbPrimitives); + Results[0]-=0.5f; Results[0]*=Results[0]; + Results[1]-=0.5f; Results[1]*=Results[1]; + Results[2]-=0.5f; Results[2]*=Results[2]; + udword Min=0; + if(Results[1]mSettings.mRules & SPLIT_BEST_AXIS) + { + // Test largest, then middle, then smallest axis... + + // Sort axis + Point Extents; mBV.GetExtents(Extents); // Box extents + udword SortedAxis[] = { 0, 1, 2 }; + float* Keys = (float*)&Extents.x; + for(udword j=0;j<3;j++) + { + for(udword i=0;i<2;i++) + { + if(Keys[SortedAxis[i]]mSettings.mRules & SPLIT_FIFTY) + { + // Don't even bother splitting (mainly a performance test) + NbPos = mNbPrimitives>>1; + } + else return false; // Unknown splitting rules + + // Check the subdivision has been successful + if(!ValidSplit) + { + // Here, all boxes lie in the same sub-space. Two strategies: + // - if the tree *must* be complete, make an arbitrary 50-50 split + // - else stop subdividing +// if(builder->mSettings.mRules&SPLIT_COMPLETE) + if(builder->mSettings.mLimit==1) + { + builder->IncreaseNbInvalidSplits(); + NbPos = mNbPrimitives>>1; + } + else return true; + } + + // Now create children and assign their pointers. + if(builder->mNodeBase) + { + // We use a pre-allocated linear pool for complete trees [Opcode 1.3] + AABBTreeNode* Pool = (AABBTreeNode*)builder->mNodeBase; + udword Count = builder->GetCount() - 1; // Count begins to 1... + // Set last bit to tell it shouldn't be freed ### pretty ugly, find a better way. Maybe one bit in mNbPrimitives + ASSERT(!(udword(&Pool[Count+0])&1)); + ASSERT(!(udword(&Pool[Count+1])&1)); + mPos = udword(&Pool[Count+0])|1; +#ifndef OPC_NO_NEG_VANILLA_TREE + mNeg = udword(&Pool[Count+1])|1; +#endif + } + else + { + // Non-complete trees and/or Opcode 1.2 allocate nodes on-the-fly +#ifndef OPC_NO_NEG_VANILLA_TREE + mPos = (udword)new AABBTreeNode; CHECKALLOC(mPos); + mNeg = (udword)new AABBTreeNode; CHECKALLOC(mNeg); +#else + AABBTreeNode* PosNeg = new AABBTreeNode[2]; + CHECKALLOC(PosNeg); + mPos = (udword)PosNeg; +#endif + } + + // Update stats + builder->IncreaseCount(2); + + // Assign children + AABBTreeNode* Pos = (AABBTreeNode*)GetPos(); + AABBTreeNode* Neg = (AABBTreeNode*)GetNeg(); + Pos->mNodePrimitives = &mNodePrimitives[0]; + Pos->mNbPrimitives = NbPos; + Neg->mNodePrimitives = &mNodePrimitives[NbPos]; + Neg->mNbPrimitives = mNbPrimitives - NbPos; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive hierarchy building in a top-down fashion. + * \param builder [in] the tree builder + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTreeNode::_BuildHierarchy(AABBTreeBuilder* builder) +{ + // 1) Compute the global box for current node. The box is stored in mBV. + builder->ComputeGlobalBox(mNodePrimitives, mNbPrimitives, mBV); + + // 2) Subdivide current node + Subdivide(builder); + + // 3) Recurse + AABBTreeNode* Pos = (AABBTreeNode*)GetPos(); + AABBTreeNode* Neg = (AABBTreeNode*)GetNeg(); + if(Pos) Pos->_BuildHierarchy(builder); + if(Neg) Neg->_BuildHierarchy(builder); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Refits the tree (top-down). + * \param builder [in] the tree builder + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTreeNode::_Refit(AABBTreeBuilder* builder) +{ + // 1) Recompute the new global box for current node + builder->ComputeGlobalBox(mNodePrimitives, mNbPrimitives, mBV); + + // 2) Recurse + AABBTreeNode* Pos = (AABBTreeNode*)GetPos(); + AABBTreeNode* Neg = (AABBTreeNode*)GetNeg(); + if(Pos) Pos->_Refit(builder); + if(Neg) Neg->_Refit(builder); +} + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBTree::AABBTree() : mIndices(null), mTotalNbNodes(0), mPool(null) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBTree::~AABBTree() +{ + Release(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Releases the tree. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTree::Release() +{ + DELETEARRAY(mPool); + DELETEARRAY(mIndices); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Builds a generic AABB tree from a tree builder. + * \param builder [in] the tree builder + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTree::Build(AABBTreeBuilder* builder) +{ + // Checkings + if(!builder || !builder->mNbPrimitives) return false; + + // Release previous tree + Release(); + + // Init stats + builder->SetCount(1); + builder->SetNbInvalidSplits(0); + + // Initialize indices. This list will be modified during build. + mIndices = new udword[builder->mNbPrimitives]; + CHECKALLOC(mIndices); + // Identity permutation + for(udword i=0;imNbPrimitives;i++) mIndices[i] = i; + + // Setup initial node. Here we have a complete permutation of the app's primitives. + mNodePrimitives = mIndices; + mNbPrimitives = builder->mNbPrimitives; + + // Use a linear array for complete trees (since we can predict the final number of nodes) [Opcode 1.3] +// if(builder->mRules&SPLIT_COMPLETE) + if(builder->mSettings.mLimit==1) + { + // Allocate a pool of nodes + mPool = new AABBTreeNode[builder->mNbPrimitives*2 - 1]; + + builder->mNodeBase = mPool; // ### ugly ! + } + + // Build the hierarchy + _BuildHierarchy(builder); + + // Get back total number of nodes + mTotalNbNodes = builder->GetCount(); + + // For complete trees, check the correct number of nodes has been created [Opcode 1.3] + if(mPool) ASSERT(mTotalNbNodes==builder->mNbPrimitives*2 - 1); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the depth of the tree. + * A well-balanced tree should have a log(n) depth. A degenerate tree O(n) depth. + * \return depth of the tree + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +udword AABBTree::ComputeDepth() const +{ + return Walk(null, null); // Use the walking code without callback +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Walks the tree, calling the user back for each node. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +udword AABBTree::Walk(WalkingCallback callback, void* user_data) const +{ + // Call it without callback to compute max depth + udword MaxDepth = 0; + udword CurrentDepth = 0; + + struct Local + { + static void _Walk(const AABBTreeNode* current_node, udword& max_depth, udword& current_depth, WalkingCallback callback, void* user_data) + { + // Checkings + if(!current_node) return; + // Entering a new node => increase depth + current_depth++; + // Keep track of max depth + if(current_depth>max_depth) max_depth = current_depth; + + // Callback + if(callback && !(callback)(current_node, current_depth, user_data)) return; + + // Recurse + if(current_node->GetPos()) { _Walk(current_node->GetPos(), max_depth, current_depth, callback, user_data); current_depth--; } + if(current_node->GetNeg()) { _Walk(current_node->GetNeg(), max_depth, current_depth, callback, user_data); current_depth--; } + } + }; + Local::_Walk(this, MaxDepth, CurrentDepth, callback, user_data); + return MaxDepth; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Refits the tree in a top-down way. + * \param builder [in] the tree builder + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTree::Refit(AABBTreeBuilder* builder) +{ + if(!builder) return false; + _Refit(builder); + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Refits the tree in a bottom-up way. + * \param builder [in] the tree builder + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTree::Refit2(AABBTreeBuilder* builder) +{ + // Checkings + if(!builder) return false; + + ASSERT(mPool); + + // Bottom-up update + Point Min,Max; + Point Min_,Max_; + udword Index = mTotalNbNodes; + while(Index--) + { + AABBTreeNode& Current = mPool[Index]; + + if(Current.IsLeaf()) + { + builder->ComputeGlobalBox(Current.GetPrimitives(), Current.GetNbPrimitives(), *(AABB*)Current.GetAABB()); + } + else + { + Current.GetPos()->GetAABB()->GetMin(Min); + Current.GetPos()->GetAABB()->GetMax(Max); + + Current.GetNeg()->GetAABB()->GetMin(Min_); + Current.GetNeg()->GetAABB()->GetMax(Max_); + + Min.Min(Min_); + Max.Max(Max_); + + ((AABB*)Current.GetAABB())->SetMinMax(Min, Max); + } + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the number of bytes used by the tree. + * \return number of bytes used + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +udword AABBTree::GetUsedBytes() const +{ + udword TotalSize = mTotalNbNodes*GetNodeSize(); + if(mIndices) TotalSize+=mNbPrimitives*sizeof(udword); + return TotalSize; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks the tree is a complete tree or not. + * A complete tree is made of 2*N-1 nodes, where N is the number of primitives in the tree. + * \return true for complete trees + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTree::IsComplete() const +{ + return (GetNbNodes()==GetNbPrimitives()*2-1); +} diff --git a/Engine/lib/opcode/OPC_AABBTree.h b/Engine/lib/opcode/OPC_AABBTree.h new file mode 100644 index 000000000..298f4827a --- /dev/null +++ b/Engine/lib/opcode/OPC_AABBTree.h @@ -0,0 +1,137 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for a versatile AABB tree. + * \file OPC_AABBTree.h + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_AABBTREE_H__ +#define __OPC_AABBTREE_H__ + +#ifdef OPC_NO_NEG_VANILLA_TREE + //! TO BE DOCUMENTED + #define IMPLEMENT_TREE(base_class, volume) \ + public: \ + /* Constructor / Destructor */ \ + base_class(); \ + ~base_class(); \ + /* Data access */ \ + inline_ const volume* Get##volume() const { return &mBV; } \ + /* Clear the last bit */ \ + inline_ const base_class* GetPos() const { return (const base_class*)(mPos&~1); } \ + inline_ const base_class* GetNeg() const { const base_class* P = GetPos(); return P ? P+1 : null;} \ + \ + /* We don't need to test both nodes since we can't have one without the other */ \ + inline_ bool IsLeaf() const { return !GetPos(); } \ + \ + /* Stats */ \ + inline_ udword GetNodeSize() const { return SIZEOFOBJECT; } \ + protected: \ + /* Tree-independent data */ \ + /* Following data always belong to the BV-tree, regardless of what the tree actually contains.*/ \ + /* Whatever happens we need the two children and the enclosing volume.*/ \ + volume mBV; /* Global bounding-volume enclosing all the node-related primitives */ \ + udword mPos; /* "Positive" & "Negative" children */ +#else + //! TO BE DOCUMENTED + #define IMPLEMENT_TREE(base_class, volume) \ + public: \ + /* Constructor / Destructor */ \ + base_class(); \ + ~base_class(); \ + /* Data access */ \ + inline_ const volume* Get##volume() const { return &mBV; } \ + /* Clear the last bit */ \ + inline_ const base_class* GetPos() const { return (const base_class*)(mPos&~1); } \ + inline_ const base_class* GetNeg() const { return (const base_class*)(mNeg&~1); } \ + \ +/* inline_ bool IsLeaf() const { return (!GetPos() && !GetNeg()); } */ \ + /* We don't need to test both nodes since we can't have one without the other */ \ + inline_ bool IsLeaf() const { return !GetPos(); } \ + \ + /* Stats */ \ + inline_ udword GetNodeSize() const { return SIZEOFOBJECT; } \ + protected: \ + /* Tree-independent data */ \ + /* Following data always belong to the BV-tree, regardless of what the tree actually contains.*/ \ + /* Whatever happens we need the two children and the enclosing volume.*/ \ + volume mBV; /* Global bounding-volume enclosing all the node-related primitives */ \ + udword mPos; /* "Positive" child */ \ + udword mNeg; /* "Negative" child */ +#endif + + typedef void (*CullingCallback) (udword nb_primitives, udword* node_primitives, BOOL need_clipping, void* user_data); + + class OPCODE_API AABBTreeNode + { + IMPLEMENT_TREE(AABBTreeNode, AABB) + public: + // Data access + inline_ const udword* GetPrimitives() const { return mNodePrimitives; } + inline_ udword GetNbPrimitives() const { return mNbPrimitives; } + + protected: + // Tree-dependent data + udword* mNodePrimitives; //!< Node-related primitives (shortcut to a position in mIndices below) + udword mNbPrimitives; //!< Number of primitives for this node + // Internal methods + udword Split(udword axis, AABBTreeBuilder* builder); + bool Subdivide(AABBTreeBuilder* builder); + void _BuildHierarchy(AABBTreeBuilder* builder); + void _Refit(AABBTreeBuilder* builder); + }; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * User-callback, called for each node by the walking code. + * \param current [in] current node + * \param depth [in] current node's depth + * \param user_data [in] user-defined data + * \return true to recurse through children, else false to bypass them + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + typedef bool (*WalkingCallback) (const AABBTreeNode* current, udword depth, void* user_data); + + class OPCODE_API AABBTree : public AABBTreeNode + { + public: + // Constructor / Destructor + AABBTree(); + ~AABBTree(); + // Build + bool Build(AABBTreeBuilder* builder); + void Release(); + + // Data access + inline_ const udword* GetIndices() const { return mIndices; } //!< Catch the indices + inline_ udword GetNbNodes() const { return mTotalNbNodes; } //!< Catch the number of nodes + + // Infos + bool IsComplete() const; + // Stats + udword ComputeDepth() const; + udword GetUsedBytes() const; + udword Walk(WalkingCallback callback, void* user_data) const; + + bool Refit(AABBTreeBuilder* builder); + bool Refit2(AABBTreeBuilder* builder); + private: + udword* mIndices; //!< Indices in the app list. Indices are reorganized during build (permutation). + AABBTreeNode* mPool; //!< Linear pool of nodes for complete trees. Null otherwise. [Opcode 1.3] + // Stats + udword mTotalNbNodes; //!< Number of nodes in the tree. + }; + +#endif // __OPC_AABBTREE_H__ diff --git a/Engine/lib/opcode/OPC_BaseModel.cpp b/Engine/lib/opcode/OPC_BaseModel.cpp new file mode 100644 index 000000000..d7078efbd --- /dev/null +++ b/Engine/lib/opcode/OPC_BaseModel.cpp @@ -0,0 +1,137 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains base model interface. + * \file OPC_BaseModel.cpp + * \author Pierre Terdiman + * \date May, 18, 2003 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * The base class for collision models. + * + * \class BaseModel + * \author Pierre Terdiman + * \version 1.3 + * \date May, 18, 2003 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "Opcode.h" + +using namespace Opcode; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +OPCODECREATE::OPCODECREATE() +{ + mIMesh = null; + mSettings.mRules = SPLIT_SPLATTER_POINTS | SPLIT_GEOM_CENTER; + mSettings.mLimit = 1; // Mandatory for complete trees + mNoLeaf = true; + mQuantized = true; +#ifdef __MESHMERIZER_H__ + mCollisionHull = false; +#endif // __MESHMERIZER_H__ + mKeepOriginal = false; + mCanRemap = false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +BaseModel::BaseModel() : mIMesh(null), mModelCode(0), mSource(null), mTree(null) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +BaseModel::~BaseModel() +{ + ReleaseBase(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Releases everything. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void BaseModel::ReleaseBase() +{ + DELETESINGLE(mSource); + DELETESINGLE(mTree); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Creates an optimized tree according to user-settings, and setups mModelCode. + * \param no_leaf [in] true for "no leaf" tree + * \param quantized [in] true for quantized tree + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool BaseModel::CreateTree(bool no_leaf, bool quantized) +{ + DELETESINGLE(mTree); + + // Setup model code + if(no_leaf) mModelCode |= OPC_NO_LEAF; + else mModelCode &= ~OPC_NO_LEAF; + + if(quantized) mModelCode |= OPC_QUANTIZED; + else mModelCode &= ~OPC_QUANTIZED; + + // Create the correct class + if(mModelCode & OPC_NO_LEAF) + { + if(mModelCode & OPC_QUANTIZED) mTree = new AABBQuantizedNoLeafTree; + else mTree = new AABBNoLeafTree; + } + else + { + if(mModelCode & OPC_QUANTIZED) mTree = new AABBQuantizedTree; + else mTree = new AABBCollisionTree; + } + CHECKALLOC(mTree); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Refits the collision model. This can be used to handle dynamic meshes. Usage is: + * 1. modify your mesh vertices (keep the topology constant!) + * 2. refit the tree (call this method) + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool BaseModel::Refit() +{ + // Refit the optimized tree + return mTree->Refit(mIMesh); + +// Old code kept for reference : refit the source tree then rebuild ! +// if(!mSource) return false; +// // Ouch... +// mSource->Refit(&mTB); +// // Ouch... +// return mTree->Build(mSource); +} diff --git a/Engine/lib/opcode/OPC_BaseModel.h b/Engine/lib/opcode/OPC_BaseModel.h new file mode 100644 index 000000000..3c2e58c6c --- /dev/null +++ b/Engine/lib/opcode/OPC_BaseModel.h @@ -0,0 +1,175 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains base model interface. + * \file OPC_BaseModel.h + * \author Pierre Terdiman + * \date May, 18, 2003 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_BASEMODEL_H__ +#define __OPC_BASEMODEL_H__ + + //! Model creation structure + struct OPCODE_API OPCODECREATE + { + //! Constructor + OPCODECREATE(); + + MeshInterface* mIMesh; //!< Mesh interface (access to triangles & vertices) (*) + BuildSettings mSettings; //!< Builder's settings + bool mNoLeaf; //!< true => discard leaf nodes (else use a normal tree) + bool mQuantized; //!< true => quantize the tree (else use a normal tree) +#ifdef __MESHMERIZER_H__ + bool mCollisionHull; //!< true => use convex hull + GJK +#endif // __MESHMERIZER_H__ + bool mKeepOriginal; //!< true => keep a copy of the original tree (debug purpose) + bool mCanRemap; //!< true => allows OPCODE to reorganize client arrays + + // (*) This pointer is saved internally and used by OPCODE until collision structures are released, + // so beware of the object's lifetime. + }; + + enum ModelFlag + { + OPC_QUANTIZED = (1<<0), //!< Compressed/uncompressed tree + OPC_NO_LEAF = (1<<1), //!< Leaf/NoLeaf tree + OPC_SINGLE_NODE = (1<<2) //!< Special case for 1-node models + }; + + class OPCODE_API BaseModel + { + public: + // Constructor/Destructor + BaseModel(); + virtual ~BaseModel(); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Builds a collision model. + * \param create [in] model creation structure + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual bool Build(const OPCODECREATE& create) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the number of bytes used by the tree. + * \return amount of bytes used + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual udword GetUsedBytes() const = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Refits the collision model. This can be used to handle dynamic meshes. Usage is: + * 1. modify your mesh vertices (keep the topology constant!) + * 2. refit the tree (call this method) + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual bool Refit(); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the source tree. + * \return generic tree + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ const AABBTree* GetSourceTree() const { return mSource; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the tree. + * \return the collision tree + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ const AABBOptimizedTree* GetTree() const { return mTree; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the tree. + * \return the collision tree + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ AABBOptimizedTree* GetTree() { return mTree; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the number of nodes in the tree. + * Should be 2*N-1 for normal trees and N-1 for optimized ones. + * \return number of nodes + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetNbNodes() const { return mTree->GetNbNodes(); } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks whether the tree has leaf nodes or not. + * \return true if the tree has leaf nodes (normal tree), else false (optimized tree) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL HasLeafNodes() const { return !(mModelCode & OPC_NO_LEAF); } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks whether the tree is quantized or not. + * \return true if the tree is quantized + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL IsQuantized() const { return mModelCode & OPC_QUANTIZED; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks whether the model has a single node or not. This special case must be handled separately. + * \return true if the model has only 1 node + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL HasSingleNode() const { return mModelCode & OPC_SINGLE_NODE; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the model's code. + * \return model's code + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetModelCode() const { return mModelCode; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the mesh interface. + * \return mesh interface + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ const MeshInterface* GetMeshInterface() const { return mIMesh; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Sets the mesh interface. + * \param imesh [in] mesh interface + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetMeshInterface(const MeshInterface* imesh) { mIMesh = imesh; } + + protected: + const MeshInterface* mIMesh; //!< User-defined mesh interface + udword mModelCode; //!< Model code = combination of ModelFlag(s) + AABBTree* mSource; //!< Original source tree + AABBOptimizedTree* mTree; //!< Optimized tree owned by the model + // Internal methods + void ReleaseBase(); + bool CreateTree(bool no_leaf, bool quantized); + }; + +#endif //__OPC_BASEMODEL_H__ \ No newline at end of file diff --git a/Engine/lib/opcode/OPC_BoxBoxOverlap.h b/Engine/lib/opcode/OPC_BoxBoxOverlap.h new file mode 100644 index 000000000..757a17dd2 --- /dev/null +++ b/Engine/lib/opcode/OPC_BoxBoxOverlap.h @@ -0,0 +1,122 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * OBB-OBB overlap test using the separating axis theorem. + * - original code by Gomez / Gamasutra (similar to Gottschalk's one in RAPID) + * - optimized for AABB trees by computing the rotation matrix once (SOLID-fashion) + * - the fabs matrix is precomputed as well and epsilon-tweaked (RAPID-style, we found this almost mandatory) + * - Class III axes can be disabled... (SOLID & Intel fashion) + * - ...or enabled to perform some profiling + * - CPU comparisons used when appropriate + * - lazy evaluation sometimes saves some work in case of early exits (unlike SOLID) + * + * \param ea [in] extents from box A + * \param ca [in] center from box A + * \param eb [in] extents from box B + * \param cb [in] center from box B + * \return true if boxes overlap + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ BOOL AABBTreeCollider::BoxBoxOverlap(const Point& ea, const Point& ca, const Point& eb, const Point& cb) +{ + // Stats + mNbBVBVTests++; + + float t,t2; + + // Class I : A's basis vectors + float Tx = (mR1to0.m[0][0]*cb.x + mR1to0.m[1][0]*cb.y + mR1to0.m[2][0]*cb.z) + mT1to0.x - ca.x; + t = ea.x + eb.x*mAR.m[0][0] + eb.y*mAR.m[1][0] + eb.z*mAR.m[2][0]; + if(GREATER(Tx, t)) return FALSE; + + float Ty = (mR1to0.m[0][1]*cb.x + mR1to0.m[1][1]*cb.y + mR1to0.m[2][1]*cb.z) + mT1to0.y - ca.y; + t = ea.y + eb.x*mAR.m[0][1] + eb.y*mAR.m[1][1] + eb.z*mAR.m[2][1]; + if(GREATER(Ty, t)) return FALSE; + + float Tz = (mR1to0.m[0][2]*cb.x + mR1to0.m[1][2]*cb.y + mR1to0.m[2][2]*cb.z) + mT1to0.z - ca.z; + t = ea.z + eb.x*mAR.m[0][2] + eb.y*mAR.m[1][2] + eb.z*mAR.m[2][2]; + if(GREATER(Tz, t)) return FALSE; + + // Class II : B's basis vectors + t = Tx*mR1to0.m[0][0] + Ty*mR1to0.m[0][1] + Tz*mR1to0.m[0][2]; t2 = ea.x*mAR.m[0][0] + ea.y*mAR.m[0][1] + ea.z*mAR.m[0][2] + eb.x; + if(GREATER(t, t2)) return FALSE; + + t = Tx*mR1to0.m[1][0] + Ty*mR1to0.m[1][1] + Tz*mR1to0.m[1][2]; t2 = ea.x*mAR.m[1][0] + ea.y*mAR.m[1][1] + ea.z*mAR.m[1][2] + eb.y; + if(GREATER(t, t2)) return FALSE; + + t = Tx*mR1to0.m[2][0] + Ty*mR1to0.m[2][1] + Tz*mR1to0.m[2][2]; t2 = ea.x*mAR.m[2][0] + ea.y*mAR.m[2][1] + ea.z*mAR.m[2][2] + eb.z; + if(GREATER(t, t2)) return FALSE; + + // Class III : 9 cross products + // Cool trick: always perform the full test for first level, regardless of settings. + // That way pathological cases (such as the pencils scene) are quickly rejected anyway ! + if(mFullBoxBoxTest || mNbBVBVTests==1) + { + t = Tz*mR1to0.m[0][1] - Ty*mR1to0.m[0][2]; t2 = ea.y*mAR.m[0][2] + ea.z*mAR.m[0][1] + eb.y*mAR.m[2][0] + eb.z*mAR.m[1][0]; if(GREATER(t, t2)) return FALSE; // L = A0 x B0 + t = Tz*mR1to0.m[1][1] - Ty*mR1to0.m[1][2]; t2 = ea.y*mAR.m[1][2] + ea.z*mAR.m[1][1] + eb.x*mAR.m[2][0] + eb.z*mAR.m[0][0]; if(GREATER(t, t2)) return FALSE; // L = A0 x B1 + t = Tz*mR1to0.m[2][1] - Ty*mR1to0.m[2][2]; t2 = ea.y*mAR.m[2][2] + ea.z*mAR.m[2][1] + eb.x*mAR.m[1][0] + eb.y*mAR.m[0][0]; if(GREATER(t, t2)) return FALSE; // L = A0 x B2 + t = Tx*mR1to0.m[0][2] - Tz*mR1to0.m[0][0]; t2 = ea.x*mAR.m[0][2] + ea.z*mAR.m[0][0] + eb.y*mAR.m[2][1] + eb.z*mAR.m[1][1]; if(GREATER(t, t2)) return FALSE; // L = A1 x B0 + t = Tx*mR1to0.m[1][2] - Tz*mR1to0.m[1][0]; t2 = ea.x*mAR.m[1][2] + ea.z*mAR.m[1][0] + eb.x*mAR.m[2][1] + eb.z*mAR.m[0][1]; if(GREATER(t, t2)) return FALSE; // L = A1 x B1 + t = Tx*mR1to0.m[2][2] - Tz*mR1to0.m[2][0]; t2 = ea.x*mAR.m[2][2] + ea.z*mAR.m[2][0] + eb.x*mAR.m[1][1] + eb.y*mAR.m[0][1]; if(GREATER(t, t2)) return FALSE; // L = A1 x B2 + t = Ty*mR1to0.m[0][0] - Tx*mR1to0.m[0][1]; t2 = ea.x*mAR.m[0][1] + ea.y*mAR.m[0][0] + eb.y*mAR.m[2][2] + eb.z*mAR.m[1][2]; if(GREATER(t, t2)) return FALSE; // L = A2 x B0 + t = Ty*mR1to0.m[1][0] - Tx*mR1to0.m[1][1]; t2 = ea.x*mAR.m[1][1] + ea.y*mAR.m[1][0] + eb.x*mAR.m[2][2] + eb.z*mAR.m[0][2]; if(GREATER(t, t2)) return FALSE; // L = A2 x B1 + t = Ty*mR1to0.m[2][0] - Tx*mR1to0.m[2][1]; t2 = ea.x*mAR.m[2][1] + ea.y*mAR.m[2][0] + eb.x*mAR.m[1][2] + eb.y*mAR.m[0][2]; if(GREATER(t, t2)) return FALSE; // L = A2 x B2 + } + return TRUE; +} + +//! A dedicated version when one box is constant +inline_ BOOL OBBCollider::BoxBoxOverlap(const Point& extents, const Point& center) +{ + // Stats + mNbVolumeBVTests++; + + float t,t2; + + // Class I : A's basis vectors + float Tx = mTBoxToModel.x - center.x; t = extents.x + mBBx1; if(GREATER(Tx, t)) return FALSE; + float Ty = mTBoxToModel.y - center.y; t = extents.y + mBBy1; if(GREATER(Ty, t)) return FALSE; + float Tz = mTBoxToModel.z - center.z; t = extents.z + mBBz1; if(GREATER(Tz, t)) return FALSE; + + // Class II : B's basis vectors + t = Tx*mRBoxToModel.m[0][0] + Ty*mRBoxToModel.m[0][1] + Tz*mRBoxToModel.m[0][2]; + t2 = extents.x*mAR.m[0][0] + extents.y*mAR.m[0][1] + extents.z*mAR.m[0][2] + mBoxExtents.x; + if(GREATER(t, t2)) return FALSE; + + t = Tx*mRBoxToModel.m[1][0] + Ty*mRBoxToModel.m[1][1] + Tz*mRBoxToModel.m[1][2]; + t2 = extents.x*mAR.m[1][0] + extents.y*mAR.m[1][1] + extents.z*mAR.m[1][2] + mBoxExtents.y; + if(GREATER(t, t2)) return FALSE; + + t = Tx*mRBoxToModel.m[2][0] + Ty*mRBoxToModel.m[2][1] + Tz*mRBoxToModel.m[2][2]; + t2 = extents.x*mAR.m[2][0] + extents.y*mAR.m[2][1] + extents.z*mAR.m[2][2] + mBoxExtents.z; + if(GREATER(t, t2)) return FALSE; + + // Class III : 9 cross products + // Cool trick: always perform the full test for first level, regardless of settings. + // That way pathological cases (such as the pencils scene) are quickly rejected anyway ! + if(mFullBoxBoxTest || mNbVolumeBVTests==1) + { + t = Tz*mRBoxToModel.m[0][1] - Ty*mRBoxToModel.m[0][2]; t2 = extents.y*mAR.m[0][2] + extents.z*mAR.m[0][1] + mBB_1; if(GREATER(t, t2)) return FALSE; // L = A0 x B0 + t = Tz*mRBoxToModel.m[1][1] - Ty*mRBoxToModel.m[1][2]; t2 = extents.y*mAR.m[1][2] + extents.z*mAR.m[1][1] + mBB_2; if(GREATER(t, t2)) return FALSE; // L = A0 x B1 + t = Tz*mRBoxToModel.m[2][1] - Ty*mRBoxToModel.m[2][2]; t2 = extents.y*mAR.m[2][2] + extents.z*mAR.m[2][1] + mBB_3; if(GREATER(t, t2)) return FALSE; // L = A0 x B2 + t = Tx*mRBoxToModel.m[0][2] - Tz*mRBoxToModel.m[0][0]; t2 = extents.x*mAR.m[0][2] + extents.z*mAR.m[0][0] + mBB_4; if(GREATER(t, t2)) return FALSE; // L = A1 x B0 + t = Tx*mRBoxToModel.m[1][2] - Tz*mRBoxToModel.m[1][0]; t2 = extents.x*mAR.m[1][2] + extents.z*mAR.m[1][0] + mBB_5; if(GREATER(t, t2)) return FALSE; // L = A1 x B1 + t = Tx*mRBoxToModel.m[2][2] - Tz*mRBoxToModel.m[2][0]; t2 = extents.x*mAR.m[2][2] + extents.z*mAR.m[2][0] + mBB_6; if(GREATER(t, t2)) return FALSE; // L = A1 x B2 + t = Ty*mRBoxToModel.m[0][0] - Tx*mRBoxToModel.m[0][1]; t2 = extents.x*mAR.m[0][1] + extents.y*mAR.m[0][0] + mBB_7; if(GREATER(t, t2)) return FALSE; // L = A2 x B0 + t = Ty*mRBoxToModel.m[1][0] - Tx*mRBoxToModel.m[1][1]; t2 = extents.x*mAR.m[1][1] + extents.y*mAR.m[1][0] + mBB_8; if(GREATER(t, t2)) return FALSE; // L = A2 x B1 + t = Ty*mRBoxToModel.m[2][0] - Tx*mRBoxToModel.m[2][1]; t2 = extents.x*mAR.m[2][1] + extents.y*mAR.m[2][0] + mBB_9; if(GREATER(t, t2)) return FALSE; // L = A2 x B2 + } + return TRUE; +} + +//! A special version for 2 axis-aligned boxes +inline_ BOOL AABBCollider::AABBAABBOverlap(const Point& extents, const Point& center) +{ + // Stats + mNbVolumeBVTests++; + + float tx = mBox.mCenter.x - center.x; float ex = extents.x + mBox.mExtents.x; if(GREATER(tx, ex)) return FALSE; + float ty = mBox.mCenter.y - center.y; float ey = extents.y + mBox.mExtents.y; if(GREATER(ty, ey)) return FALSE; + float tz = mBox.mCenter.z - center.z; float ez = extents.z + mBox.mExtents.z; if(GREATER(tz, ez)) return FALSE; + + return TRUE; +} diff --git a/Engine/lib/opcode/OPC_BoxPruning.cpp b/Engine/lib/opcode/OPC_BoxPruning.cpp new file mode 100644 index 000000000..c2c0c19c6 --- /dev/null +++ b/Engine/lib/opcode/OPC_BoxPruning.cpp @@ -0,0 +1,366 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for box pruning. + * \file IceBoxPruning.cpp + * \author Pierre Terdiman + * \date January, 29, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/* +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + You could use a complex sweep-and-prune as implemented in I-Collide. + You could use a complex hashing scheme as implemented in V-Clip or recently in ODE it seems. + You could use a "Recursive Dimensional Clustering" algorithm as implemented in GPG2. + + Or you could use this. + Faster ? I don't know. Probably not. It would be a shame. But who knows ? + Easier ? Definitely. Enjoy the sheer simplicity. +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +*/ + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "Opcode.h" + +using namespace Opcode; + + inline_ void FindRunningIndex(udword& index, float* array, udword* sorted, int last, float max) + { + int First=index; + while(First<=last) + { + index = (First+last)>>1; + + if(max>array[sorted[index]]) First = index+1; + else last = index-1; + } + } +// ### could be log(n) ! +// and maybe use cmp integers + +// InsertionSort has better coherence, RadixSort is better for one-shot queries. +#define PRUNING_SORTER RadixSort +//#define PRUNING_SORTER InsertionSort + +// Static for coherence +static PRUNING_SORTER* gCompletePruningSorter = null; +static PRUNING_SORTER* gBipartitePruningSorter0 = null; +static PRUNING_SORTER* gBipartitePruningSorter1 = null; +inline_ PRUNING_SORTER* GetCompletePruningSorter() +{ + if(!gCompletePruningSorter) gCompletePruningSorter = new PRUNING_SORTER; + return gCompletePruningSorter; +} +inline_ PRUNING_SORTER* GetBipartitePruningSorter0() +{ + if(!gBipartitePruningSorter0) gBipartitePruningSorter0 = new PRUNING_SORTER; + return gBipartitePruningSorter0; +} +inline_ PRUNING_SORTER* GetBipartitePruningSorter1() +{ + if(!gBipartitePruningSorter1) gBipartitePruningSorter1 = new PRUNING_SORTER; + return gBipartitePruningSorter1; +} +void ReleasePruningSorters() +{ + DELETESINGLE(gBipartitePruningSorter1); + DELETESINGLE(gBipartitePruningSorter0); + DELETESINGLE(gCompletePruningSorter); +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Bipartite box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to a different set. + * \param nb0 [in] number of boxes in the first set + * \param array0 [in] array of boxes for the first set + * \param nb1 [in] number of boxes in the second set + * \param array1 [in] array of boxes for the second set + * \param pairs [out] array of overlapping pairs + * \param axes [in] projection order (0,2,1 is often best) + * \return true if success. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool Opcode::BipartiteBoxPruning(udword nb0, const AABB** array0, udword nb1, const AABB** array1, Pairs& pairs, const Axes& axes) +{ + // Checkings + if(!nb0 || !array0 || !nb1 || !array1) return false; + + // Catch axes + udword Axis0 = axes.mAxis0; + udword Axis1 = axes.mAxis1; + udword Axis2 = axes.mAxis2; + + // Allocate some temporary data + float* MinPosList0 = new float[nb0]; + float* MinPosList1 = new float[nb1]; + + // 1) Build main lists using the primary axis + for(udword i=0;iGetMin(Axis0); + for(udword i=0;iGetMin(Axis0); + + // 2) Sort the lists + PRUNING_SORTER* RS0 = GetBipartitePruningSorter0(); + PRUNING_SORTER* RS1 = GetBipartitePruningSorter1(); + const udword* Sorted0 = RS0->Sort(MinPosList0, nb0).GetRanks(); + const udword* Sorted1 = RS1->Sort(MinPosList1, nb1).GetRanks(); + + // 3) Prune the lists + udword Index0, Index1; + + const udword* const LastSorted0 = &Sorted0[nb0]; + const udword* const LastSorted1 = &Sorted1[nb1]; + const udword* RunningAddress0 = Sorted0; + const udword* RunningAddress1 = Sorted1; + + while(RunningAddress1GetMax(Axis0)) + { + if(array0[Index0]->Intersect(*array1[Index1], Axis1)) + { + if(array0[Index0]->Intersect(*array1[Index1], Axis2)) + { + pairs.AddPair(Index0, Index1); + } + } + } + } + + //// + + while(RunningAddress0GetMax(Axis0)) + { + if(array0[Index1]->Intersect(*array1[Index0], Axis1)) + { + if(array0[Index1]->Intersect(*array1[Index0], Axis2)) + { + pairs.AddPair(Index1, Index0); + } + } + + } + } + + DELETEARRAY(MinPosList1); + DELETEARRAY(MinPosList0); + + return true; +} + +#define ORIGINAL_VERSION +//#define JOAKIM + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Complete box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to the same set. + * \param nb [in] number of boxes + * \param array [in] array of boxes + * \param pairs [out] array of overlapping pairs + * \param axes [in] projection order (0,2,1 is often best) + * \return true if success. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool Opcode::CompleteBoxPruning(udword nb, const AABB** array, Pairs& pairs, const Axes& axes) +{ + // Checkings + if(!nb || !array) return false; + + // Catch axes + udword Axis0 = axes.mAxis0; + udword Axis1 = axes.mAxis1; + udword Axis2 = axes.mAxis2; + +#ifdef ORIGINAL_VERSION + // Allocate some temporary data +// float* PosList = new float[nb]; + float* PosList = new float[nb+1]; + + // 1) Build main list using the primary axis + for(udword i=0;iGetMin(Axis0); +PosList[nb++] = MAX_FLOAT; + + // 2) Sort the list + PRUNING_SORTER* RS = GetCompletePruningSorter(); + const udword* Sorted = RS->Sort(PosList, nb).GetRanks(); + + // 3) Prune the list + const udword* const LastSorted = &Sorted[nb]; + const udword* RunningAddress = Sorted; + udword Index0, Index1; + while(RunningAddressGetMax(Axis0)) + while(PosList[Index1 = *RunningAddress2++]<=array[Index0]->GetMax(Axis0)) + { +// if(Index0!=Index1) +// { + if(array[Index0]->Intersect(*array[Index1], Axis1)) + { + if(array[Index0]->Intersect(*array[Index1], Axis2)) + { + pairs.AddPair(Index0, Index1); + } + } +// } + } + } + } + + DELETEARRAY(PosList); +#endif + +#ifdef JOAKIM + // Allocate some temporary data +// float* PosList = new float[nb]; + float* MinList = new float[nb+1]; + + // 1) Build main list using the primary axis + for(udword i=0;iGetMin(Axis0); + MinList[nb] = MAX_FLOAT; + + // 2) Sort the list + PRUNING_SORTER* RS = GetCompletePruningSorter(); + udword* Sorted = RS->Sort(MinList, nb+1).GetRanks(); + + // 3) Prune the list +// const udword* const LastSorted = &Sorted[nb]; +// const udword* const LastSorted = &Sorted[nb-1]; + const udword* RunningAddress = Sorted; + udword Index0, Index1; + +// while(RunningAddressGetMax(Axis0)) + +// float CurrentMin = array[Index0]->GetMin(Axis0); + float CurrentMax = array[Index0]->GetMax(Axis0); + + while(MinList[Index1 = *RunningAddress2] <= CurrentMax) +// while(PosList[Index1 = *RunningAddress] <= CurrentMax) + { +// if(Index0!=Index1) +// { + if(array[Index0]->Intersect(*array[Index1], Axis1)) + { + if(array[Index0]->Intersect(*array[Index1], Axis2)) + { + pairs.AddPair(Index0, Index1); + } + } +// } + + RunningAddress2++; +// RunningAddress++; + } + } + } + + DELETEARRAY(MinList); +#endif + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Brute-force versions are kept: +// - to check the optimized versions return the correct list of intersections +// - to check the speed of the optimized code against the brute-force one +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Brute-force bipartite box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to a different set. + * \param nb0 [in] number of boxes in the first set + * \param array0 [in] array of boxes for the first set + * \param nb1 [in] number of boxes in the second set + * \param array1 [in] array of boxes for the second set + * \param pairs [out] array of overlapping pairs + * \return true if success. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool Opcode::BruteForceBipartiteBoxTest(udword nb0, const AABB** array0, udword nb1, const AABB** array1, Pairs& pairs) +{ + // Checkings + if(!nb0 || !array0 || !nb1 || !array1) return false; + + // Brute-force nb0*nb1 overlap tests + for(udword i=0;iIntersect(*array1[j])) pairs.AddPair(i, j); + } + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Complete box pruning. Returns a list of overlapping pairs of boxes, each box of the pair belongs to the same set. + * \param nb [in] number of boxes + * \param array [in] array of boxes + * \param pairs [out] array of overlapping pairs + * \return true if success. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool Opcode::BruteForceCompleteBoxTest(udword nb, const AABB** array, Pairs& pairs) +{ + // Checkings + if(!nb || !array) return false; + + // Brute-force n(n-1)/2 overlap tests + for(udword i=0;iIntersect(*array[j])) pairs.AddPair(i, j); + } + } + return true; +} diff --git a/Engine/lib/opcode/OPC_BoxPruning.h b/Engine/lib/opcode/OPC_BoxPruning.h new file mode 100644 index 000000000..b4319461d --- /dev/null +++ b/Engine/lib/opcode/OPC_BoxPruning.h @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for box pruning. + * \file IceBoxPruning.h + * \author Pierre Terdiman + * \date January, 29, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_BOXPRUNING_H__ +#define __OPC_BOXPRUNING_H__ + + // Optimized versions + FUNCTION OPCODE_API bool CompleteBoxPruning(udword nb, const AABB** array, Pairs& pairs, const Axes& axes); + FUNCTION OPCODE_API bool BipartiteBoxPruning(udword nb0, const AABB** array0, udword nb1, const AABB** array1, Pairs& pairs, const Axes& axes); + + // Brute-force versions + FUNCTION OPCODE_API bool BruteForceCompleteBoxTest(udword nb, const AABB** array, Pairs& pairs); + FUNCTION OPCODE_API bool BruteForceBipartiteBoxTest(udword nb0, const AABB** array0, udword nb1, const AABB** array1, Pairs& pairs); + +#endif //__OPC_BOXPRUNING_H__ \ No newline at end of file diff --git a/Engine/lib/opcode/OPC_Collider.cpp b/Engine/lib/opcode/OPC_Collider.cpp new file mode 100644 index 000000000..7f8c50161 --- /dev/null +++ b/Engine/lib/opcode/OPC_Collider.cpp @@ -0,0 +1,53 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains base collider class. + * \file OPC_Collider.cpp + * \author Pierre Terdiman + * \date June, 2, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains the abstract class for colliders. + * + * \class Collider + * \author Pierre Terdiman + * \version 1.3 + * \date June, 2, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "Opcode.h" + +using namespace Opcode; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Collider::Collider() : + mFlags (0), + mCurrentModel (null), + mIMesh (null) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Collider::~Collider() +{ +} diff --git a/Engine/lib/opcode/OPC_Collider.h b/Engine/lib/opcode/OPC_Collider.h new file mode 100644 index 000000000..d718e0298 --- /dev/null +++ b/Engine/lib/opcode/OPC_Collider.h @@ -0,0 +1,176 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains base collider class. + * \file OPC_Collider.h + * \author Pierre Terdiman + * \date June, 2, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_COLLIDER_H__ +#define __OPC_COLLIDER_H__ + + enum CollisionFlag + { + OPC_FIRST_CONTACT = (1<<0), //!< Report all contacts (false) or only first one (true) + OPC_TEMPORAL_COHERENCE = (1<<1), //!< Use temporal coherence or not + OPC_CONTACT = (1<<2), //!< Final contact status after a collision query + OPC_TEMPORAL_HIT = (1<<3), //!< There has been an early exit due to temporal coherence + OPC_NO_PRIMITIVE_TESTS = (1<<4), //!< Keep or discard primitive-bv tests in leaf nodes (volume-mesh queries) + + OPC_CONTACT_FOUND = OPC_FIRST_CONTACT | OPC_CONTACT, + OPC_TEMPORAL_CONTACT = OPC_TEMPORAL_HIT | OPC_CONTACT, + + OPC_FORCE_DWORD = 0x7fffffff + }; + + class OPCODE_API Collider + { + public: + // Constructor / Destructor + Collider(); + virtual ~Collider(); + + // Collision report + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the last collision status after a collision query. + * \return true if a collision occured + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL GetContactStatus() const { return mFlags & OPC_CONTACT; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the "first contact" mode. + * \return true if "first contact" mode is on + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL FirstContactEnabled() const { return mFlags & OPC_FIRST_CONTACT; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the temporal coherence mode. + * \return true if temporal coherence is on + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL TemporalCoherenceEnabled() const { return mFlags & OPC_TEMPORAL_COHERENCE; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks a first contact has already been found. + * \return true if a first contact has been found and we can stop a query + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL ContactFound() const { return (mFlags&OPC_CONTACT_FOUND)==OPC_CONTACT_FOUND; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks there's been an early exit due to temporal coherence; + * \return true if a temporal hit has occured + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL TemporalHit() const { return mFlags & OPC_TEMPORAL_HIT; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks primitive tests are enabled; + * \return true if primitive tests must be skipped + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL SkipPrimitiveTests() const { return mFlags & OPC_NO_PRIMITIVE_TESTS; } + + // Settings + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Reports all contacts (false) or first contact only (true) + * \param flag [in] true for first contact, false for all contacts + * \see SetTemporalCoherence(bool flag) + * \see ValidateSettings() + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetFirstContact(bool flag) + { + if(flag) mFlags |= OPC_FIRST_CONTACT; + else mFlags &= ~OPC_FIRST_CONTACT; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Enable/disable temporal coherence. + * \param flag [in] true to enable temporal coherence, false to discard it + * \see SetFirstContact(bool flag) + * \see ValidateSettings() + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetTemporalCoherence(bool flag) + { + if(flag) mFlags |= OPC_TEMPORAL_COHERENCE; + else mFlags &= ~OPC_TEMPORAL_COHERENCE; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Enable/disable primitive tests. + * \param flag [in] true to enable primitive tests, false to discard them + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetPrimitiveTests(bool flag) + { + if(!flag) mFlags |= OPC_NO_PRIMITIVE_TESTS; + else mFlags &= ~OPC_NO_PRIMITIVE_TESTS; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Validates current settings. You should call this method after all the settings / callbacks have been defined for a collider. + * \return null if everything is ok, else a string describing the problem + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual const char* ValidateSettings() = 0; + + protected: + udword mFlags; //!< Bit flags + const BaseModel* mCurrentModel; //!< Current model for collision query (owner of touched faces) + // User mesh interface + const MeshInterface* mIMesh; //!< User-defined mesh interface + + // Internal methods + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups current collision model + * \param model [in] current collision model + * \return TRUE if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL Setup(const BaseModel* model) + { + // Keep track of current model + mCurrentModel = model; + if(!mCurrentModel) return FALSE; + + mIMesh = model->GetMeshInterface(); + return mIMesh!=null; + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Initializes a query + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual inline_ void InitQuery() { mFlags &= ~OPC_TEMPORAL_CONTACT; } + }; + +#endif // __OPC_COLLIDER_H__ diff --git a/Engine/lib/opcode/OPC_Common.cpp b/Engine/lib/opcode/OPC_Common.cpp new file mode 100644 index 000000000..cdda9b36c --- /dev/null +++ b/Engine/lib/opcode/OPC_Common.cpp @@ -0,0 +1,47 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains common classes & defs used in OPCODE. + * \file OPC_Common.cpp + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * An AABB dedicated to collision detection. + * We don't use the generic AABB class included in ICE, since it can be a Min/Max or a Center/Extents one (depends + * on compilation flags). Since the Center/Extents model is more efficient in collision detection, it was worth + * using an extra special class. + * + * \class CollisionAABB + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * A quantized AABB. + * Center/Extent model, using 16-bits integers. + * + * \class QuantizedAABB + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "Opcode.h" + +using namespace Opcode; diff --git a/Engine/lib/opcode/OPC_Common.h b/Engine/lib/opcode/OPC_Common.h new file mode 100644 index 000000000..a8689f0b0 --- /dev/null +++ b/Engine/lib/opcode/OPC_Common.h @@ -0,0 +1,101 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains common classes & defs used in OPCODE. + * \file OPC_Common.h + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_COMMON_H__ +#define __OPC_COMMON_H__ + +// [GOTTFRIED]: Just a small change for readability. +#ifdef OPC_CPU_COMPARE + #define GREATER(x, y) AIR(x) > IR(y) +#else + #define GREATER(x, y) fabsf(x) > (y) +#endif + + class OPCODE_API CollisionAABB + { + public: + //! Constructor + inline_ CollisionAABB() {} + //! Constructor + inline_ CollisionAABB(const AABB& b) { b.GetCenter(mCenter); b.GetExtents(mExtents); } + //! Destructor + inline_ ~CollisionAABB() {} + + //! Get min point of the box + inline_ void GetMin(Point& min) const { min = mCenter - mExtents; } + //! Get max point of the box + inline_ void GetMax(Point& max) const { max = mCenter + mExtents; } + + //! Get component of the box's min point along a given axis + inline_ float GetMin(udword axis) const { return mCenter[axis] - mExtents[axis]; } + //! Get component of the box's max point along a given axis + inline_ float GetMax(udword axis) const { return mCenter[axis] + mExtents[axis]; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Setups an AABB from min & max vectors. + * \param min [in] the min point + * \param max [in] the max point + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetMinMax(const Point& min, const Point& max) { mCenter = (max + min)*0.5f; mExtents = (max - min)*0.5f; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks a box is inside another box. + * \param box [in] the other box + * \return true if current box is inside input box + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ BOOL IsInside(const CollisionAABB& box) const + { + if(box.GetMin(0)>GetMin(0)) return FALSE; + if(box.GetMin(1)>GetMin(1)) return FALSE; + if(box.GetMin(2)>GetMin(2)) return FALSE; + if(box.GetMax(0)IsValid()) return false; + + // Look for degenerate faces. + udword NbDegenerate = create.mIMesh->CheckTopology(); + if(NbDegenerate) Log("OPCODE WARNING: found %d degenerate faces in model! Collision might report wrong results!\n", NbDegenerate); + // We continue nonetheless.... + + Release(); // Make sure previous tree has been discarded + + // 1-1) Setup mesh interface automatically + SetMeshInterface(create.mIMesh); + + bool Status = false; + AABBTree* LeafTree = null; + Internal Data; + + // 2) Build a generic AABB Tree. + mSource = new AABBTree; + CHECKALLOC(mSource); + + // 2-1) Setup a builder. Our primitives here are triangles from input mesh, + // so we use an AABBTreeOfTrianglesBuilder..... + { + AABBTreeOfTrianglesBuilder TB; + TB.mIMesh = create.mIMesh; + TB.mNbPrimitives = create.mIMesh->GetNbTriangles(); + TB.mSettings = create.mSettings; + TB.mSettings.mLimit = 16; // ### Hardcoded, but maybe we could let the user choose 8 / 16 / 32 ... + if(!mSource->Build(&TB)) goto FreeAndExit; + } + + // 2-2) Here's the trick : create *another* AABB tree using the leaves of the first one (which are boxes, this time) + struct Local + { + // A callback to count leaf nodes + static bool CountLeaves(const AABBTreeNode* current, udword depth, void* user_data) + { + if(current->IsLeaf()) + { + Internal* Data = (Internal*)user_data; + Data->mNbLeaves++; + } + return true; + } + + // A callback to setup leaf nodes in our internal structures + static bool SetupLeafData(const AABBTreeNode* current, udword depth, void* user_data) + { + if(current->IsLeaf()) + { + Internal* Data = (Internal*)user_data; + + // Get current leaf's box + Data->mLeaves[Data->mNbLeaves] = *current->GetAABB(); + + // Setup leaf data + udword Index = (udword(current->GetPrimitives()) - udword(Data->mBase))/sizeof(udword); + Data->mTriangles[Data->mNbLeaves].SetData(current->GetNbPrimitives(), Index); + + Data->mNbLeaves++; + } + return true; + } + }; + + // Walk the tree & count number of leaves + Data.mNbLeaves = 0; + mSource->Walk(Local::CountLeaves, &Data); + mNbLeaves = Data.mNbLeaves; // Keep track of it + + // Special case for 1-leaf meshes + if(mNbLeaves==1) + { + mModelCode |= OPC_SINGLE_NODE; + Status = true; + goto FreeAndExit; + } + + // Allocate our structures + Data.mLeaves = new AABB[Data.mNbLeaves]; CHECKALLOC(Data.mLeaves); + mTriangles = new LeafTriangles[Data.mNbLeaves]; CHECKALLOC(mTriangles); + + // Walk the tree again & setup leaf data + Data.mTriangles = mTriangles; + Data.mBase = mSource->GetIndices(); + Data.mNbLeaves = 0; // Reset for incoming walk + mSource->Walk(Local::SetupLeafData, &Data); + + // Handle source indices + { + bool MustKeepIndices = true; + if(create.mCanRemap) + { + // We try to get rid of source indices (saving more ram!) by reorganizing triangle arrays... + // Remap can fail when we use callbacks => keep track of indices in that case (it still + // works, only using more memory) + if(create.mIMesh->RemapClient(mSource->GetNbPrimitives(), mSource->GetIndices())) + { + MustKeepIndices = false; + } + } + + if(MustKeepIndices) + { + // Keep track of source indices (from vanilla tree) + mNbPrimitives = mSource->GetNbPrimitives(); + mIndices = new udword[mNbPrimitives]; + CopyMemory(mIndices, mSource->GetIndices(), mNbPrimitives*sizeof(udword)); + } + } + + // Now, create our optimized tree using previous leaf nodes + LeafTree = new AABBTree; + CHECKALLOC(LeafTree); + { + AABBTreeOfAABBsBuilder TB; // Now using boxes ! + TB.mSettings = create.mSettings; + TB.mSettings.mLimit = 1; // We now want a complete tree so that we can "optimize" it + TB.mNbPrimitives = Data.mNbLeaves; + TB.mAABBArray = Data.mLeaves; + if(!LeafTree->Build(&TB)) goto FreeAndExit; + } + + // 3) Create an optimized tree according to user-settings + if(!CreateTree(create.mNoLeaf, create.mQuantized)) goto FreeAndExit; + + // 3-2) Create optimized tree + if(!mTree->Build(LeafTree)) goto FreeAndExit; + + // Finally ok... + Status = true; + +FreeAndExit: // Allow me this one... + DELETESINGLE(LeafTree); + + // 3-3) Delete generic tree if needed + if(!create.mKeepOriginal) DELETESINGLE(mSource); + + return Status; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Gets the number of bytes used by the tree. + * \return amount of bytes used + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +udword HybridModel::GetUsedBytes() const +{ + udword UsedBytes = 0; + if(mTree) UsedBytes += mTree->GetUsedBytes(); + if(mIndices) UsedBytes += mNbPrimitives * sizeof(udword); // mIndices + if(mTriangles) UsedBytes += mNbLeaves * sizeof(LeafTriangles); // mTriangles + return UsedBytes; +} + +inline_ void ComputeMinMax(Point& min, Point& max, const VertexPointers& vp) +{ + // Compute triangle's AABB = a leaf box +#ifdef OPC_USE_FCOMI // a 15% speedup on my machine, not much + min.x = FCMin3(vp.Vertex[0]->x, vp.Vertex[1]->x, vp.Vertex[2]->x); + max.x = FCMax3(vp.Vertex[0]->x, vp.Vertex[1]->x, vp.Vertex[2]->x); + + min.y = FCMin3(vp.Vertex[0]->y, vp.Vertex[1]->y, vp.Vertex[2]->y); + max.y = FCMax3(vp.Vertex[0]->y, vp.Vertex[1]->y, vp.Vertex[2]->y); + + min.z = FCMin3(vp.Vertex[0]->z, vp.Vertex[1]->z, vp.Vertex[2]->z); + max.z = FCMax3(vp.Vertex[0]->z, vp.Vertex[1]->z, vp.Vertex[2]->z); +#else + min = *vp.Vertex[0]; + max = *vp.Vertex[0]; + min.Min(*vp.Vertex[1]); + max.Max(*vp.Vertex[1]); + min.Min(*vp.Vertex[2]); + max.Max(*vp.Vertex[2]); +#endif +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Refits the collision model. This can be used to handle dynamic meshes. Usage is: + * 1. modify your mesh vertices (keep the topology constant!) + * 2. refit the tree (call this method) + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool HybridModel::Refit() +{ + if(!mIMesh) return false; + if(!mTree) return false; + + if(IsQuantized()) return false; + if(HasLeafNodes()) return false; + + const LeafTriangles* LT = GetLeafTriangles(); + const udword* Indices = GetIndices(); + + // Bottom-up update + VertexPointers VP; + Point Min,Max; + Point Min_,Max_; + udword Index = mTree->GetNbNodes(); + AABBNoLeafNode* Nodes = (AABBNoLeafNode*)((AABBNoLeafTree*)mTree)->GetNodes(); + while(Index--) + { + AABBNoLeafNode& Current = Nodes[Index]; + + if(Current.HasPosLeaf()) + { + const LeafTriangles& CurrentLeaf = LT[Current.GetPosPrimitive()]; + + Min.SetPlusInfinity(); + Max.SetMinusInfinity(); + + Point TmpMin, TmpMax; + + // Each leaf box has a set of triangles + udword NbTris = CurrentLeaf.GetNbTriangles(); + if(Indices) + { + const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()]; + + // Loop through triangles and test each of them + while(NbTris--) + { + mIMesh->GetTriangle(VP, *T++); + ComputeMinMax(TmpMin, TmpMax, VP); + Min.Min(TmpMin); + Max.Max(TmpMax); + } + } + else + { + udword BaseIndex = CurrentLeaf.GetTriangleIndex(); + + // Loop through triangles and test each of them + while(NbTris--) + { + mIMesh->GetTriangle(VP, BaseIndex++); + ComputeMinMax(TmpMin, TmpMax, VP); + Min.Min(TmpMin); + Max.Max(TmpMax); + } + } + } + else + { + const CollisionAABB& CurrentBox = Current.GetPos()->mAABB; + CurrentBox.GetMin(Min); + CurrentBox.GetMax(Max); + } + + if(Current.HasNegLeaf()) + { + const LeafTriangles& CurrentLeaf = LT[Current.GetNegPrimitive()]; + + Min_.SetPlusInfinity(); + Max_.SetMinusInfinity(); + + Point TmpMin, TmpMax; + + // Each leaf box has a set of triangles + udword NbTris = CurrentLeaf.GetNbTriangles(); + if(Indices) + { + const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()]; + + // Loop through triangles and test each of them + while(NbTris--) + { + mIMesh->GetTriangle(VP, *T++); + ComputeMinMax(TmpMin, TmpMax, VP); + Min_.Min(TmpMin); + Max_.Max(TmpMax); + } + } + else + { + udword BaseIndex = CurrentLeaf.GetTriangleIndex(); + + // Loop through triangles and test each of them + while(NbTris--) + { + mIMesh->GetTriangle(VP, BaseIndex++); + ComputeMinMax(TmpMin, TmpMax, VP); + Min_.Min(TmpMin); + Max_.Max(TmpMax); + } + } + } + else + { + const CollisionAABB& CurrentBox = Current.GetNeg()->mAABB; + CurrentBox.GetMin(Min_); + CurrentBox.GetMax(Max_); + } +#ifdef OPC_USE_FCOMI + Min.x = FCMin2(Min.x, Min_.x); + Max.x = FCMax2(Max.x, Max_.x); + Min.y = FCMin2(Min.y, Min_.y); + Max.y = FCMax2(Max.y, Max_.y); + Min.z = FCMin2(Min.z, Min_.z); + Max.z = FCMax2(Max.z, Max_.z); +#else + Min.Min(Min_); + Max.Max(Max_); +#endif + Current.mAABB.SetMinMax(Min, Max); + } + return true; +} diff --git a/Engine/lib/opcode/OPC_HybridModel.h b/Engine/lib/opcode/OPC_HybridModel.h new file mode 100644 index 000000000..c7eb59d4a --- /dev/null +++ b/Engine/lib/opcode/OPC_HybridModel.h @@ -0,0 +1,106 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for hybrid models. + * \file OPC_HybridModel.h + * \author Pierre Terdiman + * \date May, 18, 2003 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_HYBRIDMODEL_H__ +#define __OPC_HYBRIDMODEL_H__ + + //! Leaf descriptor + struct LeafTriangles + { + udword Data; //!< Packed data + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets number of triangles in the leaf. + * \return number of triangles N, with 0 < N <= 16 + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetNbTriangles() const { return (Data & 15)+1; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets triangle index for this leaf. Indexed model's array of indices retrieved with HybridModel::GetIndices() + * \return triangle index + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetTriangleIndex() const { return Data>>4; } + inline_ void SetData(udword nb, udword index) { ASSERT(nb>0 && nb<=16); nb--; Data = (index<<4)|(nb&15); } + }; + + class OPCODE_API HybridModel : public BaseModel + { + public: + // Constructor/Destructor + HybridModel(); + virtual ~HybridModel(); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Builds a collision model. + * \param create [in] model creation structure + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + override(BaseModel) bool Build(const OPCODECREATE& create); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the number of bytes used by the tree. + * \return amount of bytes used + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + override(BaseModel) udword GetUsedBytes() const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Refits the collision model. This can be used to handle dynamic meshes. Usage is: + * 1. modify your mesh vertices (keep the topology constant!) + * 2. refit the tree (call this method) + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + override(BaseModel) bool Refit(); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets array of triangles. + * \return array of triangles + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ const LeafTriangles* GetLeafTriangles() const { return mTriangles; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets array of indices. + * \return array of indices + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ const udword* GetIndices() const { return mIndices; } + + private: + udword mNbLeaves; //!< Number of leaf nodes in the model + LeafTriangles* mTriangles; //!< Array of mNbLeaves leaf descriptors + udword mNbPrimitives; //!< Number of primitives in the model + udword* mIndices; //!< Array of primitive indices + + // Internal methods + void Release(); + }; + +#endif // __OPC_HYBRIDMODEL_H__ diff --git a/Engine/lib/opcode/OPC_IceHook.h b/Engine/lib/opcode/OPC_IceHook.h new file mode 100644 index 000000000..872967049 --- /dev/null +++ b/Engine/lib/opcode/OPC_IceHook.h @@ -0,0 +1,70 @@ + +// Should be included by Opcode.h if needed + + #define ICE_DONT_CHECK_COMPILER_OPTIONS + + // From Windows... + typedef int BOOL; + #ifndef FALSE + #define FALSE 0 + #endif + + #ifndef TRUE + #define TRUE 1 + #endif + + #include + #include + #include + #include + #include + #include + + #ifndef ASSERT + #define ASSERT(exp) {} + #endif + #define ICE_COMPILE_TIME_ASSERT(exp) extern char ICE_Dummy[ (exp) ? 1 : -1 ] + + #define Log {} + inline bool SetIceError( const char *inUnused1, const char *inUnused2 = NULL ) { return( false ); } + #define EC_OUTOFMEMORY "Out of memory" + + #include "./Ice/IcePreprocessor.h" + + #undef ICECORE_API + #define ICECORE_API OPCODE_API + + #include "./Ice/IceTypes.h" + #include "./Ice/IceFPU.h" + #include "./Ice/IceMemoryMacros.h" + + namespace IceCore + { + #include "./Ice/IceUtils.h" + #include "./Ice/IceContainer.h" + #include "./Ice/IcePairs.h" + #include "./Ice/IceRevisitedRadix.h" + #include "./Ice/IceRandom.h" + } + using namespace IceCore; + + #define ICEMATHS_API OPCODE_API + namespace IceMaths + { + #include "./Ice/IceAxes.h" + #include "./Ice/IcePoint.h" + #include "./Ice/IceHPoint.h" + #include "./Ice/IceMatrix3x3.h" + #include "./Ice/IceMatrix4x4.h" + #include "./Ice/IcePlane.h" + #include "./Ice/IceRay.h" + #include "./Ice/IceIndexedTriangle.h" + #include "./Ice/IceTriangle.h" + #include "./Ice/IceTriList.h" + #include "./Ice/IceAABB.h" + #include "./Ice/IceOBB.h" + #include "./Ice/IceBoundingSphere.h" + #include "./Ice/IceSegment.h" + #include "./Ice/IceLSS.h" + } + using namespace IceMaths; diff --git a/Engine/lib/opcode/OPC_LSSAABBOverlap.h b/Engine/lib/opcode/OPC_LSSAABBOverlap.h new file mode 100644 index 000000000..5cc50b58d --- /dev/null +++ b/Engine/lib/opcode/OPC_LSSAABBOverlap.h @@ -0,0 +1,523 @@ + +// Following code from Magic-Software (http://www.magic-software.com/) +// A bit modified for Opcode + +inline_ float OPC_PointAABBSqrDist(const Point& point, const Point& center, const Point& extents) +{ + // Compute coordinates of point in box coordinate system + Point Closest = point - center; + + float SqrDistance = 0.0f; + + if(Closest.x < -extents.x) + { + float Delta = Closest.x + extents.x; + SqrDistance += Delta*Delta; + } + else if(Closest.x > extents.x) + { + float Delta = Closest.x - extents.x; + SqrDistance += Delta*Delta; + } + + if(Closest.y < -extents.y) + { + float Delta = Closest.y + extents.y; + SqrDistance += Delta*Delta; + } + else if(Closest.y > extents.y) + { + float Delta = Closest.y - extents.y; + SqrDistance += Delta*Delta; + } + + if(Closest.z < -extents.z) + { + float Delta = Closest.z + extents.z; + SqrDistance += Delta*Delta; + } + else if(Closest.z > extents.z) + { + float Delta = Closest.z - extents.z; + SqrDistance += Delta*Delta; + } + return SqrDistance; +} + +static void Face(int i0, int i1, int i2, Point& rkPnt, const Point& rkDir, const Point& extents, const Point& rkPmE, float* pfLParam, float& rfSqrDistance) +{ + Point kPpE; + float fLSqr, fInv, fTmp, fParam, fT, fDelta; + + kPpE[i1] = rkPnt[i1] + extents[i1]; + kPpE[i2] = rkPnt[i2] + extents[i2]; + if(rkDir[i0]*kPpE[i1] >= rkDir[i1]*rkPmE[i0]) + { + if(rkDir[i0]*kPpE[i2] >= rkDir[i2]*rkPmE[i0]) + { + // v[i1] >= -e[i1], v[i2] >= -e[i2] (distance = 0) + if(pfLParam) + { + rkPnt[i0] = extents[i0]; + fInv = 1.0f/rkDir[i0]; + rkPnt[i1] -= rkDir[i1]*rkPmE[i0]*fInv; + rkPnt[i2] -= rkDir[i2]*rkPmE[i0]*fInv; + *pfLParam = -rkPmE[i0]*fInv; + } + } + else + { + // v[i1] >= -e[i1], v[i2] < -e[i2] + fLSqr = rkDir[i0]*rkDir[i0] + rkDir[i2]*rkDir[i2]; + fTmp = fLSqr*kPpE[i1] - rkDir[i1]*(rkDir[i0]*rkPmE[i0] + rkDir[i2]*kPpE[i2]); + if(fTmp <= 2.0f*fLSqr*extents[i1]) + { + fT = fTmp/fLSqr; + fLSqr += rkDir[i1]*rkDir[i1]; + fTmp = kPpE[i1] - fT; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*fTmp + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + fTmp*fTmp + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = fT - extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + else + { + fLSqr += rkDir[i1]*rkDir[i1]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*rkPmE[i1] + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + rkPmE[i1]*rkPmE[i1] + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + } + } + else + { + if ( rkDir[i0]*kPpE[i2] >= rkDir[i2]*rkPmE[i0] ) + { + // v[i1] < -e[i1], v[i2] >= -e[i2] + fLSqr = rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]; + fTmp = fLSqr*kPpE[i2] - rkDir[i2]*(rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1]); + if(fTmp <= 2.0f*fLSqr*extents[i2]) + { + fT = fTmp/fLSqr; + fLSqr += rkDir[i2]*rkDir[i2]; + fTmp = kPpE[i2] - fT; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*fTmp; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + fTmp*fTmp + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = fT - extents[i2]; + } + } + else + { + fLSqr += rkDir[i2]*rkDir[i2]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*rkPmE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + rkPmE[i2]*rkPmE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = extents[i2]; + } + } + } + else + { + // v[i1] < -e[i1], v[i2] < -e[i2] + fLSqr = rkDir[i0]*rkDir[i0]+rkDir[i2]*rkDir[i2]; + fTmp = fLSqr*kPpE[i1] - rkDir[i1]*(rkDir[i0]*rkPmE[i0] + rkDir[i2]*kPpE[i2]); + if(fTmp >= 0.0f) + { + // v[i1]-edge is closest + if ( fTmp <= 2.0f*fLSqr*extents[i1] ) + { + fT = fTmp/fLSqr; + fLSqr += rkDir[i1]*rkDir[i1]; + fTmp = kPpE[i1] - fT; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*fTmp + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + fTmp*fTmp + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = fT - extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + else + { + fLSqr += rkDir[i1]*rkDir[i1]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*rkPmE[i1] + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + rkPmE[i1]*rkPmE[i1] + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + return; + } + + fLSqr = rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]; + fTmp = fLSqr*kPpE[i2] - rkDir[i2]*(rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1]); + if(fTmp >= 0.0f) + { + // v[i2]-edge is closest + if(fTmp <= 2.0f*fLSqr*extents[i2]) + { + fT = fTmp/fLSqr; + fLSqr += rkDir[i2]*rkDir[i2]; + fTmp = kPpE[i2] - fT; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*fTmp; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + fTmp*fTmp + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = fT - extents[i2]; + } + } + else + { + fLSqr += rkDir[i2]*rkDir[i2]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*rkPmE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + rkPmE[i2]*rkPmE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = extents[i2]; + } + } + return; + } + + // (v[i1],v[i2])-corner is closest + fLSqr += rkDir[i2]*rkDir[i2]; + fDelta = rkDir[i0]*rkPmE[i0] + rkDir[i1]*kPpE[i1] + rkDir[i2]*kPpE[i2]; + fParam = -fDelta/fLSqr; + rfSqrDistance += rkPmE[i0]*rkPmE[i0] + kPpE[i1]*kPpE[i1] + kPpE[i2]*kPpE[i2] + fDelta*fParam; + + if(pfLParam) + { + *pfLParam = fParam; + rkPnt[i0] = extents[i0]; + rkPnt[i1] = -extents[i1]; + rkPnt[i2] = -extents[i2]; + } + } + } +} + +static void CaseNoZeros(Point& rkPnt, const Point& rkDir, const Point& extents, float* pfLParam, float& rfSqrDistance) +{ + Point kPmE(rkPnt.x - extents.x, rkPnt.y - extents.y, rkPnt.z - extents.z); + + float fProdDxPy, fProdDyPx, fProdDzPx, fProdDxPz, fProdDzPy, fProdDyPz; + + fProdDxPy = rkDir.x*kPmE.y; + fProdDyPx = rkDir.y*kPmE.x; + if(fProdDyPx >= fProdDxPy) + { + fProdDzPx = rkDir.z*kPmE.x; + fProdDxPz = rkDir.x*kPmE.z; + if(fProdDzPx >= fProdDxPz) + { + // line intersects x = e0 + Face(0, 1, 2, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance); + } + else + { + // line intersects z = e2 + Face(2, 0, 1, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance); + } + } + else + { + fProdDzPy = rkDir.z*kPmE.y; + fProdDyPz = rkDir.y*kPmE.z; + if(fProdDzPy >= fProdDyPz) + { + // line intersects y = e1 + Face(1, 2, 0, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance); + } + else + { + // line intersects z = e2 + Face(2, 0, 1, rkPnt, rkDir, extents, kPmE, pfLParam, rfSqrDistance); + } + } +} + +static void Case0(int i0, int i1, int i2, Point& rkPnt, const Point& rkDir, const Point& extents, float* pfLParam, float& rfSqrDistance) +{ + float fPmE0 = rkPnt[i0] - extents[i0]; + float fPmE1 = rkPnt[i1] - extents[i1]; + float fProd0 = rkDir[i1]*fPmE0; + float fProd1 = rkDir[i0]*fPmE1; + float fDelta, fInvLSqr, fInv; + + if(fProd0 >= fProd1) + { + // line intersects P[i0] = e[i0] + rkPnt[i0] = extents[i0]; + + float fPpE1 = rkPnt[i1] + extents[i1]; + fDelta = fProd0 - rkDir[i0]*fPpE1; + if(fDelta >= 0.0f) + { + fInvLSqr = 1.0f/(rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]); + rfSqrDistance += fDelta*fDelta*fInvLSqr; + if(pfLParam) + { + rkPnt[i1] = -extents[i1]; + *pfLParam = -(rkDir[i0]*fPmE0+rkDir[i1]*fPpE1)*fInvLSqr; + } + } + else + { + if(pfLParam) + { + fInv = 1.0f/rkDir[i0]; + rkPnt[i1] -= fProd0*fInv; + *pfLParam = -fPmE0*fInv; + } + } + } + else + { + // line intersects P[i1] = e[i1] + rkPnt[i1] = extents[i1]; + + float fPpE0 = rkPnt[i0] + extents[i0]; + fDelta = fProd1 - rkDir[i1]*fPpE0; + if(fDelta >= 0.0f) + { + fInvLSqr = 1.0f/(rkDir[i0]*rkDir[i0] + rkDir[i1]*rkDir[i1]); + rfSqrDistance += fDelta*fDelta*fInvLSqr; + if(pfLParam) + { + rkPnt[i0] = -extents[i0]; + *pfLParam = -(rkDir[i0]*fPpE0+rkDir[i1]*fPmE1)*fInvLSqr; + } + } + else + { + if(pfLParam) + { + fInv = 1.0f/rkDir[i1]; + rkPnt[i0] -= fProd1*fInv; + *pfLParam = -fPmE1*fInv; + } + } + } + + if(rkPnt[i2] < -extents[i2]) + { + fDelta = rkPnt[i2] + extents[i2]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i2] = -extents[i2]; + } + else if ( rkPnt[i2] > extents[i2] ) + { + fDelta = rkPnt[i2] - extents[i2]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i2] = extents[i2]; + } +} + +static void Case00(int i0, int i1, int i2, Point& rkPnt, const Point& rkDir, const Point& extents, float* pfLParam, float& rfSqrDistance) +{ + float fDelta; + + if(pfLParam) + *pfLParam = (extents[i0] - rkPnt[i0])/rkDir[i0]; + + rkPnt[i0] = extents[i0]; + + if(rkPnt[i1] < -extents[i1]) + { + fDelta = rkPnt[i1] + extents[i1]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i1] = -extents[i1]; + } + else if(rkPnt[i1] > extents[i1]) + { + fDelta = rkPnt[i1] - extents[i1]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i1] = extents[i1]; + } + + if(rkPnt[i2] < -extents[i2]) + { + fDelta = rkPnt[i2] + extents[i2]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i1] = -extents[i2]; + } + else if(rkPnt[i2] > extents[i2]) + { + fDelta = rkPnt[i2] - extents[i2]; + rfSqrDistance += fDelta*fDelta; + rkPnt[i2] = extents[i2]; + } +} + +static void Case000(Point& rkPnt, const Point& extents, float& rfSqrDistance) +{ + float fDelta; + + if(rkPnt.x < -extents.x) + { + fDelta = rkPnt.x + extents.x; + rfSqrDistance += fDelta*fDelta; + rkPnt.x = -extents.x; + } + else if(rkPnt.x > extents.x) + { + fDelta = rkPnt.x - extents.x; + rfSqrDistance += fDelta*fDelta; + rkPnt.x = extents.x; + } + + if(rkPnt.y < -extents.y) + { + fDelta = rkPnt.y + extents.y; + rfSqrDistance += fDelta*fDelta; + rkPnt.y = -extents.y; + } + else if(rkPnt.y > extents.y) + { + fDelta = rkPnt.y - extents.y; + rfSqrDistance += fDelta*fDelta; + rkPnt.y = extents.y; + } + + if(rkPnt.z < -extents.z) + { + fDelta = rkPnt.z + extents.z; + rfSqrDistance += fDelta*fDelta; + rkPnt.z = -extents.z; + } + else if(rkPnt.z > extents.z) + { + fDelta = rkPnt.z - extents.z; + rfSqrDistance += fDelta*fDelta; + rkPnt.z = extents.z; + } +} + +static float SqrDistance(const Ray& rkLine, const Point& center, const Point& extents, float* pfLParam) +{ + // compute coordinates of line in box coordinate system + Point kDiff = rkLine.mOrig - center; + Point kPnt = kDiff; + Point kDir = rkLine.mDir; + + // Apply reflections so that direction vector has nonnegative components. + bool bReflect[3]; + for(int i=0;i<3;i++) + { + if(kDir[i]<0.0f) + { + kPnt[i] = -kPnt[i]; + kDir[i] = -kDir[i]; + bReflect[i] = true; + } + else + { + bReflect[i] = false; + } + } + + float fSqrDistance = 0.0f; + + if(kDir.x>0.0f) + { + if(kDir.y>0.0f) + { + if(kDir.z>0.0f) CaseNoZeros(kPnt, kDir, extents, pfLParam, fSqrDistance); // (+,+,+) + else Case0(0, 1, 2, kPnt, kDir, extents, pfLParam, fSqrDistance); // (+,+,0) + } + else + { + if(kDir.z>0.0f) Case0(0, 2, 1, kPnt, kDir, extents, pfLParam, fSqrDistance); // (+,0,+) + else Case00(0, 1, 2, kPnt, kDir, extents, pfLParam, fSqrDistance); // (+,0,0) + } + } + else + { + if(kDir.y>0.0f) + { + if(kDir.z>0.0f) Case0(1, 2, 0, kPnt, kDir, extents, pfLParam, fSqrDistance); // (0,+,+) + else Case00(1, 0, 2, kPnt, kDir, extents, pfLParam, fSqrDistance); // (0,+,0) + } + else + { + if(kDir.z>0.0f) Case00(2, 0, 1, kPnt, kDir, extents, pfLParam, fSqrDistance); // (0,0,+) + else + { + Case000(kPnt, extents, fSqrDistance); // (0,0,0) + if(pfLParam) *pfLParam = 0.0f; + } + } + } + return fSqrDistance; +} + +inline_ float OPC_SegmentOBBSqrDist(const Segment& segment, const Point& c0, const Point& e0) +{ + float fLP; + float fSqrDistance = SqrDistance(Ray(segment.GetOrigin(), segment.ComputeDirection()), c0, e0, &fLP); + if(fLP>=0.0f) + { + if(fLP<=1.0f) return fSqrDistance; + else return OPC_PointAABBSqrDist(segment.mP1, c0, e0); + } + else return OPC_PointAABBSqrDist(segment.mP0, c0, e0); +} + +inline_ BOOL LSSCollider::LSSAABBOverlap(const Point& center, const Point& extents) +{ + // Stats + mNbVolumeBVTests++; + + float s2 = OPC_SegmentOBBSqrDist(mSeg, center, extents); + if(s2Add(prim_index); + +//! LSS-triangle overlap test +#define LSS_PRIM(prim_index, flag) \ + /* Request vertices from the app */ \ + VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \ + \ + /* Perform LSS-tri overlap test */ \ + if(LSSTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \ + { \ + SET_CONTACT(prim_index, flag) \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +LSSCollider::LSSCollider() +{ +// mCenter.Zero(); +// mRadius2 = 0.0f; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +LSSCollider::~LSSCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Generic collision query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - with GetNbTouchedPrimitives() + * - with GetTouchedPrimitives() + * + * \param cache [in/out] an lss cache + * \param lss [in] collision lss in local space + * \param model [in] Opcode model to collide with + * \param worldl [in] lss world matrix, or null + * \param worldm [in] model's world matrix, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool LSSCollider::Collide(LSSCache& cache, const LSS& lss, const Model& model, const Matrix4x4* worldl, const Matrix4x4* worldm) +{ + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, lss, worldl, worldm)) return true; + + if(!model.HasLeafNodes()) + { + if(model.IsQuantized()) + { + const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + else + { + const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree(); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + else + { + const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree(); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Initializes a collision query : + * - reset stats & contact status + * - setup matrices + * - check temporal coherence + * + * \param cache [in/out] an lss cache + * \param lss [in] lss in local space + * \param worldl [in] lss world matrix, or null + * \param worldm [in] model's world matrix, or null + * \return TRUE if we can return immediately + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +BOOL LSSCollider::InitQuery(LSSCache& cache, const LSS& lss, const Matrix4x4* worldl, const Matrix4x4* worldm) +{ + // 1) Call the base method + VolumeCollider::InitQuery(); + + // 2) Compute LSS in model space: + // - Precompute R^2 + mRadius2 = lss.mRadius * lss.mRadius; + // - Compute segment + mSeg.mP0 = lss.mP0; + mSeg.mP1 = lss.mP1; + // -> to world space + if(worldl) + { + mSeg.mP0 *= *worldl; + mSeg.mP1 *= *worldl; + } + // -> to model space + if(worldm) + { + // Invert model matrix + Matrix4x4 InvWorldM; + InvertPRMatrix(InvWorldM, *worldm); + + mSeg.mP0 *= InvWorldM; + mSeg.mP1 *= InvWorldM; + } + + // 3) Setup destination pointer + mTouchedPrimitives = &cache.TouchedPrimitives; + + // 4) Special case: 1-triangle meshes [Opcode 1.3] + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + if(!SkipPrimitiveTests()) + { + // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0. + mTouchedPrimitives->Reset(); + + // Perform overlap test between the unique triangle and the LSS (and set contact status if needed) + LSS_PRIM(udword(0), OPC_CONTACT) + + // Return immediately regardless of status + return TRUE; + } + } + + // 5) Check temporal coherence : + if(TemporalCoherenceEnabled()) + { + // Here we use temporal coherence + // => check results from previous frame before performing the collision query + if(FirstContactEnabled()) + { + // We're only interested in the first contact found => test the unique previously touched face + if(mTouchedPrimitives->GetNbEntries()) + { + // Get index of previously touched face = the first entry in the array + udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0); + + // Then reset the array: + // - if the overlap test below is successful, the index we'll get added back anyway + // - if it isn't, then the array should be reset anyway for the normal query + mTouchedPrimitives->Reset(); + + // Perform overlap test between the cached triangle and the LSS (and set contact status if needed) + LSS_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT) + + // Return immediately if possible + if(GetContactStatus()) return TRUE; + } + // else no face has been touched during previous query + // => we'll have to perform a normal query + } + else + { + // We're interested in all contacts =>test the new real LSS N(ew) against the previous fat LSS P(revious): + + // ### rewrite this + + LSS Test(mSeg, lss.mRadius); // in model space + LSS Previous(cache.Previous, sqrtf(cache.Previous.mRadius)); + +// if(cache.Previous.Contains(Test)) + if(IsCacheValid(cache) && Previous.Contains(Test)) + { + // - if N is included in P, return previous list + // => we simply leave the list (mTouchedFaces) unchanged + + // Set contact status if needed + if(mTouchedPrimitives->GetNbEntries()) mFlags |= OPC_TEMPORAL_CONTACT; + + // In any case we don't need to do a query + return TRUE; + } + else + { + // - else do the query using a fat N + + // Reset cache since we'll about to perform a real query + mTouchedPrimitives->Reset(); + + // Make a fat sphere so that coherence will work for subsequent frames + mRadius2 *= cache.FatCoeff; +// mRadius2 = (lss.mRadius * cache.FatCoeff)*(lss.mRadius * cache.FatCoeff); + + + // Update cache with query data (signature for cached faces) + cache.Previous.mP0 = mSeg.mP0; + cache.Previous.mP1 = mSeg.mP1; + cache.Previous.mRadius = mRadius2; + } + } + } + else + { + // Here we don't use temporal coherence => do a normal query + mTouchedPrimitives->Reset(); + } + + return FALSE; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Collision query for vanilla AABB trees. + * \param cache [in/out] an lss cache + * \param lss [in] collision lss in world space + * \param tree [in] AABB tree + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool LSSCollider::Collide(LSSCache& cache, const LSS& lss, const AABBTree* tree) +{ + // This is typically called for a scene tree, full of -AABBs-, not full of triangles. + // So we don't really have "primitives" to deal with. Hence it doesn't work with + // "FirstContact" + "TemporalCoherence". + ASSERT( !(FirstContactEnabled() && TemporalCoherenceEnabled()) ); + + // Checkings + if(!tree) return false; + + // Init collision query + if(InitQuery(cache, lss)) return true; + + // Perform collision query + _Collide(tree); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks the LSS completely contains the box. In which case we can end the query sooner. + * \param bc [in] box center + * \param be [in] box extents + * \return true if the LSS contains the whole box + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ BOOL LSSCollider::LSSContainsBox(const Point& bc, const Point& be) +{ + // Not implemented + return FALSE; +} + +#define TEST_BOX_IN_LSS(center, extents) \ + if(LSSContainsBox(center, extents)) \ + { \ + /* Set contact status */ \ + mFlags |= OPC_CONTACT; \ + _Dump(node); \ + return; \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void LSSCollider::_Collide(const AABBCollisionNode* node) +{ + // Perform LSS-AABB overlap test + if(!LSSAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + TEST_BOX_IN_LSS(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->IsLeaf()) + { + LSS_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _Collide(node->GetPos()); + + if(ContactFound()) return; + + _Collide(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void LSSCollider::_CollideNoPrimitiveTest(const AABBCollisionNode* node) +{ + // Perform LSS-AABB overlap test + if(!LSSAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + TEST_BOX_IN_LSS(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void LSSCollider::_Collide(const AABBQuantizedNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform LSS-AABB overlap test + if(!LSSAABBOverlap(Center, Extents)) return; + + TEST_BOX_IN_LSS(Center, Extents) + + if(node->IsLeaf()) + { + LSS_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _Collide(node->GetPos()); + + if(ContactFound()) return; + + _Collide(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void LSSCollider::_CollideNoPrimitiveTest(const AABBQuantizedNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform LSS-AABB overlap test + if(!LSSAABBOverlap(Center, Extents)) return; + + TEST_BOX_IN_LSS(Center, Extents) + + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void LSSCollider::_Collide(const AABBNoLeafNode* node) +{ + // Perform LSS-AABB overlap test + if(!LSSAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + TEST_BOX_IN_LSS(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->HasPosLeaf()) { LSS_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } + else _Collide(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { LSS_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } + else _Collide(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void LSSCollider::_CollideNoPrimitiveTest(const AABBNoLeafNode* node) +{ + // Perform LSS-AABB overlap test + if(!LSSAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + TEST_BOX_IN_LSS(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void LSSCollider::_Collide(const AABBQuantizedNoLeafNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform LSS-AABB overlap test + if(!LSSAABBOverlap(Center, Extents)) return; + + TEST_BOX_IN_LSS(Center, Extents) + + if(node->HasPosLeaf()) { LSS_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } + else _Collide(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { LSS_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } + else _Collide(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void LSSCollider::_CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform LSS-AABB overlap test + if(!LSSAABBOverlap(Center, Extents)) return; + + TEST_BOX_IN_LSS(Center, Extents) + + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for vanilla AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void LSSCollider::_Collide(const AABBTreeNode* node) +{ + // Perform LSS-AABB overlap test + Point Center, Extents; + node->GetAABB()->GetCenter(Center); + node->GetAABB()->GetExtents(Extents); + if(!LSSAABBOverlap(Center, Extents)) return; + + if(node->IsLeaf() || LSSContainsBox(Center, Extents)) + { + mFlags |= OPC_CONTACT; + mTouchedPrimitives->Add(node->GetPrimitives(), node->GetNbPrimitives()); + } + else + { + _Collide(node->GetPos()); + _Collide(node->GetNeg()); + } +} + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridLSSCollider::HybridLSSCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridLSSCollider::~HybridLSSCollider() +{ +} + +bool HybridLSSCollider::Collide(LSSCache& cache, const LSS& lss, const HybridModel& model, const Matrix4x4* worldl, const Matrix4x4* worldm) +{ + // We don't want primitive tests here! + mFlags |= OPC_NO_PRIMITIVE_TESTS; + + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, lss, worldl, worldm)) return true; + + // Special case for 1-leaf trees + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles + udword Nb = mIMesh->GetNbTriangles(); + + // Loop through all triangles + for(udword i=0;imCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + else + { + const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree(); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + else + { + const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree(); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + } + + // We only have a list of boxes so far + if(GetContactStatus()) + { + // Reset contact status, since it currently only reflects collisions with leaf boxes + Collider::InitQuery(); + + // Change dest container so that we can use built-in overlap tests and get collided primitives + cache.TouchedPrimitives.Reset(); + mTouchedPrimitives = &cache.TouchedPrimitives; + + // Read touched leaf boxes + udword Nb = mTouchedBoxes.GetNbEntries(); + const udword* Touched = mTouchedBoxes.GetEntries(); + + const LeafTriangles* LT = model.GetLeafTriangles(); + const udword* Indices = model.GetIndices(); + + // Loop through touched leaves + while(Nb--) + { + const LeafTriangles& CurrentLeaf = LT[*Touched++]; + + // Each leaf box has a set of triangles + udword NbTris = CurrentLeaf.GetNbTriangles(); + if(Indices) + { + const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()]; + + // Loop through triangles and test each of them + while(NbTris--) + { + udword TriangleIndex = *T++; + LSS_PRIM(TriangleIndex, OPC_CONTACT) + } + } + else + { + udword BaseIndex = CurrentLeaf.GetTriangleIndex(); + + // Loop through triangles and test each of them + while(NbTris--) + { + udword TriangleIndex = BaseIndex++; + LSS_PRIM(TriangleIndex, OPC_CONTACT) + } + } + } + } + + return true; +} diff --git a/Engine/lib/opcode/OPC_LSSCollider.h b/Engine/lib/opcode/OPC_LSSCollider.h new file mode 100644 index 000000000..458b3f8d2 --- /dev/null +++ b/Engine/lib/opcode/OPC_LSSCollider.h @@ -0,0 +1,99 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for an LSS collider. + * \file OPC_LSSCollider.h + * \author Pierre Terdiman + * \date December, 28, 2002 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_LSSCOLLIDER_H__ +#define __OPC_LSSCOLLIDER_H__ + + struct OPCODE_API LSSCache : VolumeCache + { + LSSCache() + { + Previous.mP0 = Point(0.0f, 0.0f, 0.0f); + Previous.mP1 = Point(0.0f, 0.0f, 0.0f); + Previous.mRadius = 0.0f; + FatCoeff = 1.1f; + } + + // Cached faces signature + LSS Previous; //!< LSS used when performing the query resulting in cached faces + // User settings + float FatCoeff; //!< mRadius2 multiplier used to create a fat LSS + }; + + class OPCODE_API LSSCollider : public VolumeCollider + { + public: + // Constructor / Destructor + LSSCollider(); + virtual ~LSSCollider(); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Generic collision query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - with GetNbTouchedPrimitives() + * - with GetTouchedPrimitives() + * + * \param cache [in/out] an lss cache + * \param lss [in] collision lss in local space + * \param model [in] Opcode model to collide with + * \param worldl [in] lss world matrix, or null + * \param worldm [in] model's world matrix, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool Collide(LSSCache& cache, const LSS& lss, const Model& model, const Matrix4x4* worldl=null, const Matrix4x4* worldm=null); + // + bool Collide(LSSCache& cache, const LSS& lss, const AABBTree* tree); + protected: + // LSS in model space + Segment mSeg; //!< Segment + float mRadius2; //!< LSS radius squared + // Internal methods + void _Collide(const AABBCollisionNode* node); + void _Collide(const AABBNoLeafNode* node); + void _Collide(const AABBQuantizedNode* node); + void _Collide(const AABBQuantizedNoLeafNode* node); + void _Collide(const AABBTreeNode* node); + void _CollideNoPrimitiveTest(const AABBCollisionNode* node); + void _CollideNoPrimitiveTest(const AABBNoLeafNode* node); + void _CollideNoPrimitiveTest(const AABBQuantizedNode* node); + void _CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node); + // Overlap tests + inline_ BOOL LSSContainsBox(const Point& bc, const Point& be); + inline_ BOOL LSSAABBOverlap(const Point& center, const Point& extents); + inline_ BOOL LSSTriOverlap(const Point& vert0, const Point& vert1, const Point& vert2); + // Init methods + BOOL InitQuery(LSSCache& cache, const LSS& lss, const Matrix4x4* worldl=null, const Matrix4x4* worldm=null); + }; + + class OPCODE_API HybridLSSCollider : public LSSCollider + { + public: + // Constructor / Destructor + HybridLSSCollider(); + virtual ~HybridLSSCollider(); + + bool Collide(LSSCache& cache, const LSS& lss, const HybridModel& model, const Matrix4x4* worldl=null, const Matrix4x4* worldm=null); + protected: + OPC_Container mTouchedBoxes; + }; + +#endif // __OPC_LSSCOLLIDER_H__ diff --git a/Engine/lib/opcode/OPC_LSSTriOverlap.h b/Engine/lib/opcode/OPC_LSSTriOverlap.h new file mode 100644 index 000000000..f1d17e4ae --- /dev/null +++ b/Engine/lib/opcode/OPC_LSSTriOverlap.h @@ -0,0 +1,679 @@ +// Following code from Magic-Software (http://www.magic-software.com/) +// A bit modified for Opcode + +static const float gs_fTolerance = 1e-05f; + +static float OPC_PointTriangleSqrDist(const Point& point, const Point& p0, const Point& p1, const Point& p2) +{ + // Hook + Point TriEdge0 = p1 - p0; + Point TriEdge1 = p2 - p0; + + Point kDiff = p0 - point; + float fA00 = TriEdge0.SquareMagnitude(); + float fA01 = TriEdge0 | TriEdge1; + float fA11 = TriEdge1.SquareMagnitude(); + float fB0 = kDiff | TriEdge0; + float fB1 = kDiff | TriEdge1; + float fC = kDiff.SquareMagnitude(); + float fDet = fabsf(fA00*fA11 - fA01*fA01); + float fS = fA01*fB1-fA11*fB0; + float fT = fA01*fB0-fA00*fB1; + float fSqrDist; + + if(fS + fT <= fDet) + { + if(fS < 0.0f) + { + if(fT < 0.0f) // region 4 + { + if(fB0 < 0.0f) + { + if(-fB0 >= fA00) fSqrDist = fA00+2.0f*fB0+fC; + else fSqrDist = fB0*(-fB0/fA00)+fC; + } + else + { + if(fB1 >= 0.0f) fSqrDist = fC; + else if(-fB1 >= fA11) fSqrDist = fA11+2.0f*fB1+fC; + else fSqrDist = fB1*(-fB1/fA11)+fC; + } + } + else // region 3 + { + if(fB1 >= 0.0f) fSqrDist = fC; + else if(-fB1 >= fA11) fSqrDist = fA11+2.0f*fB1+fC; + else fSqrDist = fB1*(-fB1/fA11)+fC; + } + } + else if(fT < 0.0f) // region 5 + { + if(fB0 >= 0.0f) fSqrDist = fC; + else if(-fB0 >= fA00) fSqrDist = fA00+2.0f*fB0+fC; + else fSqrDist = fB0*(-fB0/fA00)+fC; + } + else // region 0 + { + // minimum at interior point + if(fDet==0.0f) + { + fSqrDist = MAX_FLOAT; + } + else + { + float fInvDet = 1.0f/fDet; + fS *= fInvDet; + fT *= fInvDet; + fSqrDist = fS*(fA00*fS+fA01*fT+2.0f*fB0) + fT*(fA01*fS+fA11*fT+2.0f*fB1)+fC; + } + } + } + else + { + float fTmp0, fTmp1, fNumer, fDenom; + + if(fS < 0.0f) // region 2 + { + fTmp0 = fA01 + fB0; + fTmp1 = fA11 + fB1; + if(fTmp1 > fTmp0) + { + fNumer = fTmp1 - fTmp0; + fDenom = fA00-2.0f*fA01+fA11; + if(fNumer >= fDenom) + { + fSqrDist = fA00+2.0f*fB0+fC; + } + else + { + fS = fNumer/fDenom; + fT = 1.0f - fS; + fSqrDist = fS*(fA00*fS+fA01*fT+2.0f*fB0) + fT*(fA01*fS+fA11*fT+2.0f*fB1)+fC; + } + } + else + { + if(fTmp1 <= 0.0f) fSqrDist = fA11+2.0f*fB1+fC; + else if(fB1 >= 0.0f) fSqrDist = fC; + else fSqrDist = fB1*(-fB1/fA11)+fC; + } + } + else if(fT < 0.0f) // region 6 + { + fTmp0 = fA01 + fB1; + fTmp1 = fA00 + fB0; + if(fTmp1 > fTmp0) + { + fNumer = fTmp1 - fTmp0; + fDenom = fA00-2.0f*fA01+fA11; + if(fNumer >= fDenom) + { + fSqrDist = fA11+2.0f*fB1+fC; + } + else + { + fT = fNumer/fDenom; + fS = 1.0f - fT; + fSqrDist = fS*(fA00*fS+fA01*fT+2.0f*fB0) + fT*(fA01*fS+fA11*fT+2.0f*fB1)+fC; + } + } + else + { + if(fTmp1 <= 0.0f) fSqrDist = fA00+2.0f*fB0+fC; + else if(fB0 >= 0.0f) fSqrDist = fC; + else fSqrDist = fB0*(-fB0/fA00)+fC; + } + } + else // region 1 + { + fNumer = fA11 + fB1 - fA01 - fB0; + if(fNumer <= 0.0f) + { + fSqrDist = fA11+2.0f*fB1+fC; + } + else + { + fDenom = fA00-2.0f*fA01+fA11; + if(fNumer >= fDenom) + { + fSqrDist = fA00+2.0f*fB0+fC; + } + else + { + fS = fNumer/fDenom; + fT = 1.0f - fS; + fSqrDist = fS*(fA00*fS+fA01*fT+2.0f*fB0) + fT*(fA01*fS+fA11*fT+2.0f*fB1)+fC; + } + } + } + } + return fabsf(fSqrDist); +} + +static float OPC_SegmentSegmentSqrDist(const Segment& rkSeg0, const Segment& rkSeg1) +{ + // Hook + Point rkSeg0Direction = rkSeg0.ComputeDirection(); + Point rkSeg1Direction = rkSeg1.ComputeDirection(); + + Point kDiff = rkSeg0.mP0 - rkSeg1.mP0; + float fA00 = rkSeg0Direction.SquareMagnitude(); + float fA01 = -rkSeg0Direction.Dot(rkSeg1Direction); + float fA11 = rkSeg1Direction.SquareMagnitude(); + float fB0 = kDiff.Dot(rkSeg0Direction); + float fC = kDiff.SquareMagnitude(); + float fDet = fabsf(fA00*fA11-fA01*fA01); + + float fB1, fS, fT, fSqrDist, fTmp; + + if(fDet>=gs_fTolerance) + { + // line segments are not parallel + fB1 = -kDiff.Dot(rkSeg1Direction); + fS = fA01*fB1-fA11*fB0; + fT = fA01*fB0-fA00*fB1; + + if(fS >= 0.0f) + { + if(fS <= fDet) + { + if(fT >= 0.0f) + { + if(fT <= fDet) // region 0 (interior) + { + // minimum at two interior points of 3D lines + float fInvDet = 1.0f/fDet; + fS *= fInvDet; + fT *= fInvDet; + fSqrDist = fS*(fA00*fS+fA01*fT+2.0f*fB0) + fT*(fA01*fS+fA11*fT+2.0f*fB1)+fC; + } + else // region 3 (side) + { + fTmp = fA01+fB0; + if(fTmp>=0.0f) fSqrDist = fA11+2.0f*fB1+fC; + else if(-fTmp>=fA00) fSqrDist = fA00+fA11+fC+2.0f*(fB1+fTmp); + else fSqrDist = fTmp*(-fTmp/fA00)+fA11+2.0f*fB1+fC; + } + } + else // region 7 (side) + { + if(fB0>=0.0f) fSqrDist = fC; + else if(-fB0>=fA00) fSqrDist = fA00+2.0f*fB0+fC; + else fSqrDist = fB0*(-fB0/fA00)+fC; + } + } + else + { + if ( fT >= 0.0 ) + { + if ( fT <= fDet ) // region 1 (side) + { + fTmp = fA01+fB1; + if(fTmp>=0.0f) fSqrDist = fA00+2.0f*fB0+fC; + else if(-fTmp>=fA11) fSqrDist = fA00+fA11+fC+2.0f*(fB0+fTmp); + else fSqrDist = fTmp*(-fTmp/fA11)+fA00+2.0f*fB0+fC; + } + else // region 2 (corner) + { + fTmp = fA01+fB0; + if ( -fTmp <= fA00 ) + { + if(fTmp>=0.0f) fSqrDist = fA11+2.0f*fB1+fC; + else fSqrDist = fTmp*(-fTmp/fA00)+fA11+2.0f*fB1+fC; + } + else + { + fTmp = fA01+fB1; + if(fTmp>=0.0f) fSqrDist = fA00+2.0f*fB0+fC; + else if(-fTmp>=fA11) fSqrDist = fA00+fA11+fC+2.0f*(fB0+fTmp); + else fSqrDist = fTmp*(-fTmp/fA11)+fA00+2.0f*fB0+fC; + } + } + } + else // region 8 (corner) + { + if ( -fB0 < fA00 ) + { + if(fB0>=0.0f) fSqrDist = fC; + else fSqrDist = fB0*(-fB0/fA00)+fC; + } + else + { + fTmp = fA01+fB1; + if(fTmp>=0.0f) fSqrDist = fA00+2.0f*fB0+fC; + else if(-fTmp>=fA11) fSqrDist = fA00+fA11+fC+2.0f*(fB0+fTmp); + else fSqrDist = fTmp*(-fTmp/fA11)+fA00+2.0f*fB0+fC; + } + } + } + } + else + { + if ( fT >= 0.0f ) + { + if ( fT <= fDet ) // region 5 (side) + { + if(fB1>=0.0f) fSqrDist = fC; + else if(-fB1>=fA11) fSqrDist = fA11+2.0f*fB1+fC; + else fSqrDist = fB1*(-fB1/fA11)+fC; + } + else // region 4 (corner) + { + fTmp = fA01+fB0; + if ( fTmp < 0.0f ) + { + if(-fTmp>=fA00) fSqrDist = fA00+fA11+fC+2.0f*(fB1+fTmp); + else fSqrDist = fTmp*(-fTmp/fA00)+fA11+2.0f*fB1+fC; + } + else + { + if(fB1>=0.0f) fSqrDist = fC; + else if(-fB1>=fA11) fSqrDist = fA11+2.0f*fB1+fC; + else fSqrDist = fB1*(-fB1/fA11)+fC; + } + } + } + else // region 6 (corner) + { + if ( fB0 < 0.0f ) + { + if(-fB0>=fA00) fSqrDist = fA00+2.0f*fB0+fC; + else fSqrDist = fB0*(-fB0/fA00)+fC; + } + else + { + if(fB1>=0.0f) fSqrDist = fC; + else if(-fB1>=fA11) fSqrDist = fA11+2.0f*fB1+fC; + else fSqrDist = fB1*(-fB1/fA11)+fC; + } + } + } + } + else + { + // line segments are parallel + if ( fA01 > 0.0f ) + { + // direction vectors form an obtuse angle + if ( fB0 >= 0.0f ) + { + fSqrDist = fC; + } + else if ( -fB0 <= fA00 ) + { + fSqrDist = fB0*(-fB0/fA00)+fC; + } + else + { + fB1 = -kDiff.Dot(rkSeg1Direction); + fTmp = fA00+fB0; + if ( -fTmp >= fA01 ) + { + fSqrDist = fA00+fA11+fC+2.0f*(fA01+fB0+fB1); + } + else + { + fT = -fTmp/fA01; + fSqrDist = fA00+2.0f*fB0+fC+fT*(fA11*fT+2.0f*(fA01+fB1)); + } + } + } + else + { + // direction vectors form an acute angle + if ( -fB0 >= fA00 ) + { + fSqrDist = fA00+2.0f*fB0+fC; + } + else if ( fB0 <= 0.0f ) + { + fSqrDist = fB0*(-fB0/fA00)+fC; + } + else + { + fB1 = -kDiff.Dot(rkSeg1Direction); + if ( fB0 >= -fA01 ) + { + fSqrDist = fA11+2.0f*fB1+fC; + } + else + { + fT = -fB0/fA01; + fSqrDist = fC+fT*(2.0f*fB1+fA11*fT); + } + } + } + } + return fabsf(fSqrDist); +} + +inline_ float OPC_SegmentRaySqrDist(const Segment& rkSeg0, const Ray& rkSeg1) +{ + return OPC_SegmentSegmentSqrDist(rkSeg0, Segment(rkSeg1.mOrig, rkSeg1.mOrig + rkSeg1.mDir)); +} + +static float OPC_SegmentTriangleSqrDist(const Segment& segment, const Point& p0, const Point& p1, const Point& p2) +{ + // Hook + const Point TriEdge0 = p1 - p0; + const Point TriEdge1 = p2 - p0; + + const Point& rkSegOrigin = segment.GetOrigin(); + Point rkSegDirection = segment.ComputeDirection(); + + Point kDiff = p0 - rkSegOrigin; + float fA00 = rkSegDirection.SquareMagnitude(); + float fA01 = -rkSegDirection.Dot(TriEdge0); + float fA02 = -rkSegDirection.Dot(TriEdge1); + float fA11 = TriEdge0.SquareMagnitude(); + float fA12 = TriEdge0.Dot(TriEdge1); + float fA22 = TriEdge1.Dot(TriEdge1); + float fB0 = -kDiff.Dot(rkSegDirection); + float fB1 = kDiff.Dot(TriEdge0); + float fB2 = kDiff.Dot(TriEdge1); + float fCof00 = fA11*fA22-fA12*fA12; + float fCof01 = fA02*fA12-fA01*fA22; + float fCof02 = fA01*fA12-fA02*fA11; + float fDet = fA00*fCof00+fA01*fCof01+fA02*fCof02; + + Ray kTriSeg; + Point kPt; + float fSqrDist, fSqrDist0; + + if(fabsf(fDet)>=gs_fTolerance) + { + float fCof11 = fA00*fA22-fA02*fA02; + float fCof12 = fA02*fA01-fA00*fA12; + float fCof22 = fA00*fA11-fA01*fA01; + float fInvDet = 1.0f/fDet; + float fRhs0 = -fB0*fInvDet; + float fRhs1 = -fB1*fInvDet; + float fRhs2 = -fB2*fInvDet; + + float fR = fCof00*fRhs0+fCof01*fRhs1+fCof02*fRhs2; + float fS = fCof01*fRhs0+fCof11*fRhs1+fCof12*fRhs2; + float fT = fCof02*fRhs0+fCof12*fRhs1+fCof22*fRhs2; + + if ( fR < 0.0f ) + { + if ( fS+fT <= 1.0f ) + { + if ( fS < 0.0f ) + { + if ( fT < 0.0f ) // region 4m + { + // min on face s=0 or t=0 or r=0 + kTriSeg.mOrig = p0; + kTriSeg.mDir = TriEdge1; + fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg); + kTriSeg.mOrig = p0; + kTriSeg.mDir = TriEdge0; + fSqrDist0 = OPC_SegmentRaySqrDist(segment, kTriSeg); + if(fSqrDist0 1 + { + if ( fS+fT <= 1.0f ) + { + if ( fS < 0.0f ) + { + if ( fT < 0.0f ) // region 4p + { + // min on face s=0 or t=0 or r=1 + kTriSeg.mOrig = p0; + kTriSeg.mDir = TriEdge1; + fSqrDist = OPC_SegmentRaySqrDist(segment, kTriSeg); + kTriSeg.mOrig = p0; + kTriSeg.mDir = TriEdge0; + fSqrDist0 = OPC_SegmentRaySqrDist(segment, kTriSeg); + if(fSqrDist0GetTriangle(triangle_index); + * // Setup pointers to vertices for the collision system + * triangle.Vertex[0] = MyMesh->GetVertex(Tri->mVRef[0]); + * triangle.Vertex[1] = MyMesh->GetVertex(Tri->mVRef[1]); + * triangle.Vertex[2] = MyMesh->GetVertex(Tri->mVRef[2]); + * } + * + * // Setup callbacks + * MeshInterface0->SetCallback(ColCallback, udword(Mesh0)); + * MeshInterface1->SetCallback(ColCallback, udword(Mesh1)); + * \endcode + * + * Of course, you should make this callback as fast as possible. And you're also not supposed + * to modify the geometry *after* the collision trees have been built. The alternative was to + * store the geometry & topology in the collision system as well (as in RAPID) but we have found + * this approach to waste a lot of ram in many cases. + * + * + * POINTERS: + * + * If you're internally using the following canonical structures: + * - a vertex made of three 32-bits floating point values + * - a triangle made of three 32-bits integer vertex references + * ...then you may want to use pointers instead of callbacks. This is the same, except OPCODE will directly + * use provided pointers to access the topology and geometry, without using a callback. It might be faster, + * but probably not as safe. Pointers have been introduced in OPCODE 1.2. + * + * Ex: + * + * \code + * // Setup pointers + * MeshInterface0->SetPointers(Mesh0->GetFaces(), Mesh0->GetVerts()); + * MeshInterface1->SetPointers(Mesh1->GetFaces(), Mesh1->GetVerts()); + * \endcode + * + * + * STRIDES: + * + * If your vertices are D3D-like entities interleaving a position, a normal and/or texture coordinates + * (i.e. if your vertices are FVFs), you might want to use a vertex stride to skip extra data OPCODE + * doesn't need. Using a stride shouldn't be notably slower than not using it, but it might increase + * cache misses. Please also note that you *shouldn't* read from AGP or video-memory buffers ! + * + * + * In any case, compilation flags are here to select callbacks/pointers/strides at compile time, so + * choose what's best for your application. All of this has been wrapped into this MeshInterface. + * + * \class MeshInterface + * \author Pierre Terdiman + * \version 1.3 + * \date November, 27, 2002 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "Opcode.h" + +using namespace Opcode; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +MeshInterface::MeshInterface() : +#ifdef OPC_USE_CALLBACKS + mUserData (null), + mObjCallback (null), +#else + mTris (null), + mVerts (null), + #ifdef OPC_USE_STRIDE + mTriStride (sizeof(IndexedTriangle)), + mVertexStride (sizeof(Point)), + #endif +#endif + mNbTris (0), + mNbVerts (0) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +MeshInterface::~MeshInterface() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks the mesh interface is valid, i.e. things have been setup correctly. + * \return true if valid + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool MeshInterface::IsValid() const +{ + if(!mNbTris || !mNbVerts) return false; +#ifdef OPC_USE_CALLBACKS + if(!mObjCallback) return false; +#else + if(!mTris || !mVerts) return false; +#endif + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks the mesh itself is valid. + * Currently we only look for degenerate faces. + * \return number of degenerate faces + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +udword MeshInterface::CheckTopology() const +{ + // Check topology. If the model contains degenerate faces, collision report can be wrong in some cases. + // e.g. it happens with the standard MAX teapot. So clean your meshes first... If you don't have a mesh cleaner + // you can try this: www.codercorner.com/Consolidation.zip + + udword NbDegenerate = 0; + + VertexPointers VP; + + // Using callbacks, we don't have access to vertex indices. Nevertheless we still can check for + // redundant vertex pointers, which cover all possibilities (callbacks/pointers/strides). + for(udword i=0;i= 0.0f; + } + }; + +#ifdef OPC_USE_CALLBACKS + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * User-callback, called by OPCODE to request vertices from the app. + * \param triangle_index [in] face index for which the system is requesting the vertices + * \param triangle [out] triangle's vertices (must be provided by the user) + * \param user_data [in] user-defined data from SetCallback() + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + typedef void (*RequestCallback) (udword triangle_index, VertexPointers& triangle, void* user_data); +#endif + + class OPCODE_API MeshInterface + { + public: + // Constructor / Destructor + MeshInterface(); + ~MeshInterface(); + // Common settings + inline_ udword GetNbTriangles() const { return mNbTris; } + inline_ udword GetNbVertices() const { return mNbVerts; } + inline_ void SetNbTriangles(udword nb) { mNbTris = nb; } + inline_ void SetNbVertices(udword nb) { mNbVerts = nb; } + +#ifdef OPC_USE_CALLBACKS + // Callback settings + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Callback control: setups object callback. Must provide triangle-vertices for a given triangle index. + * \param callback [in] user-defined callback + * \param user_data [in] user-defined data + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool SetCallback(RequestCallback callback, void* user_data); + inline_ void* GetUserData() const { return mUserData; } + inline_ RequestCallback GetCallback() const { return mObjCallback; } +#else + // Pointers settings + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Pointers control: setups object pointers. Must provide access to faces and vertices for a given object. + * \param tris [in] pointer to triangles + * \param verts [in] pointer to vertices + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool SetPointers(const IndexedTriangle* tris, const Point* verts); + inline_ const IndexedTriangle* GetTris() const { return mTris; } + inline_ const Point* GetVerts() const { return mVerts; } + + #ifdef OPC_USE_STRIDE + // Strides settings + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Strides control + * \param tri_stride [in] size of a triangle in bytes. The first sizeof(IndexedTriangle) bytes are used to get vertex indices. + * \param vertex_stride [in] size of a vertex in bytes. The first sizeof(Point) bytes are used to get vertex position. + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool SetStrides(udword tri_stride=sizeof(IndexedTriangle), udword vertex_stride=sizeof(Point)); + inline_ udword GetTriStride() const { return mTriStride; } + inline_ udword GetVertexStride() const { return mVertexStride; } + #endif +#endif + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Fetches a triangle given a triangle index. + * \param vp [out] required triangle's vertex pointers + * \param index [in] triangle index + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void GetTriangle(VertexPointers& vp, udword index) const + { +#ifdef OPC_USE_CALLBACKS + (mObjCallback)(index, vp, mUserData); +#else + #ifdef OPC_USE_STRIDE + const IndexedTriangle* T = (const IndexedTriangle*)(((ubyte*)mTris) + index * mTriStride); + vp.Vertex[0] = (const Point*)(((ubyte*)mVerts) + T->mVRef[0] * mVertexStride); + vp.Vertex[1] = (const Point*)(((ubyte*)mVerts) + T->mVRef[1] * mVertexStride); + vp.Vertex[2] = (const Point*)(((ubyte*)mVerts) + T->mVRef[2] * mVertexStride); + + vp.MatIdx = T->mMatIdx; + #else + const IndexedTriangle* T = &mTris[index]; + vp.Vertex[0] = &mVerts[T->mVRef[0]]; + vp.Vertex[1] = &mVerts[T->mVRef[1]]; + vp.Vertex[2] = &mVerts[T->mVRef[2]]; + + vp.MatIdx = T->mMatIdx; + #endif +#endif + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Remaps client's mesh according to a permutation. + * \param nb_indices [in] number of indices in the permutation (will be checked against number of triangles) + * \param permutation [in] list of triangle indices + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool RemapClient(udword nb_indices, const udword* permutation) const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks the mesh interface is valid, i.e. things have been setup correctly. + * \return true if valid + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool IsValid() const; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Checks the mesh itself is valid. + * Currently we only look for degenerate faces. + * \return number of degenerate faces + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + udword CheckTopology() const; + private: + + udword mNbTris; //!< Number of triangles in the input model + udword mNbVerts; //!< Number of vertices in the input model +#ifdef OPC_USE_CALLBACKS + // User callback + void* mUserData; //!< User-defined data sent to callback + RequestCallback mObjCallback; //!< Object callback +#else + // User pointers + const IndexedTriangle* mTris; //!< Array of indexed triangles + const Point* mVerts; //!< Array of vertices + #ifdef OPC_USE_STRIDE + udword mTriStride; //!< Possible triangle stride in bytes [Opcode 1.3] + udword mVertexStride; //!< Possible vertex stride in bytes [Opcode 1.3] + #endif +#endif + }; + +#endif //__OPC_MESHINTERFACE_H__ \ No newline at end of file diff --git a/Engine/lib/opcode/OPC_Model.cpp b/Engine/lib/opcode/OPC_Model.cpp new file mode 100644 index 000000000..6c6ae68ec --- /dev/null +++ b/Engine/lib/opcode/OPC_Model.cpp @@ -0,0 +1,221 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for OPCODE models. + * \file OPC_Model.cpp + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * The main collision wrapper, for all trees. Supported trees are: + * - Normal trees (2*N-1 nodes, full size) + * - No-leaf trees (N-1 nodes, full size) + * - Quantized trees (2*N-1 nodes, half size) + * - Quantized no-leaf trees (N-1 nodes, half size) + * + * Usage: + * + * 1) Create a static mesh interface using callbacks or pointers. (see OPC_MeshInterface.cpp). + * Keep it around in your app, since a pointer to this interface is saved internally and + * used until you release the collision structures. + * + * 2) Build a Model using a creation structure: + * + * \code + * Model Sample; + * + * OPCODECREATE OPCC; + * OPCC.IMesh = ...; + * OPCC.Rules = ...; + * OPCC.NoLeaf = ...; + * OPCC.Quantized = ...; + * OPCC.KeepOriginal = ...; + * bool Status = Sample.Build(OPCC); + * \endcode + * + * 3) Create a tree collider and set it up: + * + * \code + * AABBTreeCollider TC; + * TC.SetFirstContact(...); + * TC.SetFullBoxBoxTest(...); + * TC.SetFullPrimBoxTest(...); + * TC.SetTemporalCoherence(...); + * \endcode + * + * 4) Perform a collision query + * + * \code + * // Setup cache + * static BVTCache ColCache; + * ColCache.Model0 = &Model0; + * ColCache.Model1 = &Model1; + * + * // Collision query + * bool IsOk = TC.Collide(ColCache, World0, World1); + * + * // Get collision status => if true, objects overlap + * BOOL Status = TC.GetContactStatus(); + * + * // Number of colliding pairs and list of pairs + * udword NbPairs = TC.GetNbPairs(); + * const Pair* p = TC.GetPairs() + * \endcode + * + * 5) Stats + * + * \code + * Model0.GetUsedBytes() = number of bytes used for this collision tree + * TC.GetNbBVBVTests() = number of BV-BV overlap tests performed during last query + * TC.GetNbPrimPrimTests() = number of Triangle-Triangle overlap tests performed during last query + * TC.GetNbBVPrimTests() = number of Triangle-BV overlap tests performed during last query + * \endcode + * + * \class Model + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "Opcode.h" + +using namespace Opcode; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Model::Model() +{ +#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE ! + mHull = null; +#endif // __MESHMERIZER_H__ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +Model::~Model() +{ + Release(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Releases the model. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void Model::Release() +{ + ReleaseBase(); +#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE ! + DELETESINGLE(mHull); +#endif // __MESHMERIZER_H__ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Builds a collision model. + * \param create [in] model creation structure + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool Model::Build(const OPCODECREATE& create) +{ + // 1) Checkings + if(!create.mIMesh || !create.mIMesh->IsValid()) return false; + + // For this model, we only support complete trees + if(create.mSettings.mLimit!=1) return SetIceError("OPCODE WARNING: supports complete trees only! Use mLimit = 1.\n", null); + + // Look for degenerate faces. + udword NbDegenerate = create.mIMesh->CheckTopology(); + if(NbDegenerate) Log("OPCODE WARNING: found %d degenerate faces in model! Collision might report wrong results!\n", NbDegenerate); + // We continue nonetheless.... + + Release(); // Make sure previous tree has been discarded [Opcode 1.3, thanks Adam] + + // 1-1) Setup mesh interface automatically [Opcode 1.3] + SetMeshInterface(create.mIMesh); + + // Special case for 1-triangle meshes [Opcode 1.3] + udword NbTris = create.mIMesh->GetNbTriangles(); + if(NbTris==1) + { + // We don't need to actually create a tree here, since we'll only have a single triangle to deal with anyway. + // It's a waste to use a "model" for this but at least it will work. + mModelCode |= OPC_SINGLE_NODE; + return true; + } + + // 2) Build a generic AABB Tree. + mSource = new AABBTree; + CHECKALLOC(mSource); + + // 2-1) Setup a builder. Our primitives here are triangles from input mesh, + // so we use an AABBTreeOfTrianglesBuilder..... + { + AABBTreeOfTrianglesBuilder TB; + TB.mIMesh = create.mIMesh; + TB.mSettings = create.mSettings; + TB.mNbPrimitives = NbTris; + if(!mSource->Build(&TB)) return false; + } + + // 3) Create an optimized tree according to user-settings + if(!CreateTree(create.mNoLeaf, create.mQuantized)) return false; + + // 3-2) Create optimized tree + if(!mTree->Build(mSource)) return false; + + // 3-3) Delete generic tree if needed + if(!create.mKeepOriginal) DELETESINGLE(mSource); + +#ifdef __MESHMERIZER_H__ + // 4) Convex hull + if(create.mCollisionHull) + { + // Create hull + mHull = new CollisionHull; + CHECKALLOC(mHull); + + CONVEXHULLCREATE CHC; + // ### doesn't work with strides + CHC.NbVerts = create.mIMesh->GetNbVertices(); + CHC.Vertices = create.mIMesh->GetVerts(); + CHC.UnifyNormals = true; + CHC.ReduceVertices = true; + CHC.WordFaces = false; + mHull->Compute(CHC); + } +#endif // __MESHMERIZER_H__ + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Gets the number of bytes used by the tree. + * \return amount of bytes used + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +udword Model::GetUsedBytes() const +{ + if(!mTree) return 0; + return mTree->GetUsedBytes(); +} diff --git a/Engine/lib/opcode/OPC_Model.h b/Engine/lib/opcode/OPC_Model.h new file mode 100644 index 000000000..1b39e6009 --- /dev/null +++ b/Engine/lib/opcode/OPC_Model.h @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for OPCODE models. + * \file OPC_Model.h + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_MODEL_H__ +#define __OPC_MODEL_H__ + + class OPCODE_API Model : public BaseModel + { + public: + // Constructor/Destructor + Model(); + virtual ~Model(); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Builds a collision model. + * \param create [in] model creation structure + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + override(BaseModel) bool Build(const OPCODECREATE& create); + +#ifdef __MESHMERIZER_H__ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the collision hull. + * \return the collision hull if it exists + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ const CollisionHull* GetHull() const { return mHull; } +#endif // __MESHMERIZER_H__ + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the number of bytes used by the tree. + * \return amount of bytes used + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + override(BaseModel) udword GetUsedBytes() const; + + private: +#ifdef __MESHMERIZER_H__ + CollisionHull* mHull; //!< Possible convex hull +#endif // __MESHMERIZER_H__ + // Internal methods + void Release(); + }; + +#endif //__OPC_MODEL_H__ \ No newline at end of file diff --git a/Engine/lib/opcode/OPC_OBBCollider.cpp b/Engine/lib/opcode/OPC_OBBCollider.cpp new file mode 100644 index 000000000..6907c1668 --- /dev/null +++ b/Engine/lib/opcode/OPC_OBBCollider.cpp @@ -0,0 +1,766 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for an OBB collider. + * \file OPC_OBBCollider.cpp + * \author Pierre Terdiman + * \date January, 1st, 2002 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains an OBB-vs-tree collider. + * + * \class OBBCollider + * \author Pierre Terdiman + * \version 1.3 + * \date January, 1st, 2002 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "Opcode.h" + +using namespace Opcode; + +#include "OPC_BoxBoxOverlap.h" +#include "OPC_TriBoxOverlap.h" + +#define SET_CONTACT(prim_index, flag) \ + /* Set contact status */ \ + mFlags |= flag; \ + mTouchedPrimitives->Add(prim_index); + +//! OBB-triangle test +#define OBB_PRIM(prim_index, flag) \ + /* Request vertices from the app */ \ + VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \ + /* Transform them in a common space */ \ + TransformPoint(mLeafVerts[0], *VP.Vertex[0], mRModelToBox, mTModelToBox); \ + TransformPoint(mLeafVerts[1], *VP.Vertex[1], mRModelToBox, mTModelToBox); \ + TransformPoint(mLeafVerts[2], *VP.Vertex[2], mRModelToBox, mTModelToBox); \ + /* Perform triangle-box overlap test */ \ + if(TriBoxOverlap()) \ + { \ + SET_CONTACT(prim_index, flag) \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +OBBCollider::OBBCollider() : mFullBoxBoxTest(true) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +OBBCollider::~OBBCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Validates current settings. You should call this method after all the settings and callbacks have been defined. + * \return null if everything is ok, else a string describing the problem + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +const char* OBBCollider::ValidateSettings() +{ + if(TemporalCoherenceEnabled() && !FirstContactEnabled()) return "Temporal coherence only works with ""First contact"" mode!"; + + return VolumeCollider::ValidateSettings(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Generic collision query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - with GetNbTouchedPrimitives() + * - with GetTouchedPrimitives() + * + * \param cache [in/out] a box cache + * \param box [in] collision OBB in local space + * \param model [in] Opcode model to collide with + * \param worldb [in] OBB's world matrix, or null + * \param worldm [in] model's world matrix, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool OBBCollider::Collide(OBBCache& cache, const OBB& box, const Model& model, const Matrix4x4* worldb, const Matrix4x4* worldm) +{ + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, box, worldb, worldm)) return true; + + if(!model.HasLeafNodes()) + { + if(model.IsQuantized()) + { + const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + else + { + const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree(); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + else + { + const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree(); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Initializes a collision query : + * - reset stats & contact status + * - setup matrices + * - check temporal coherence + * + * \param cache [in/out] a box cache + * \param box [in] obb in local space + * \param worldb [in] obb's world matrix, or null + * \param worldm [in] model's world matrix, or null + * \return TRUE if we can return immediately + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +BOOL OBBCollider::InitQuery(OBBCache& cache, const OBB& box, const Matrix4x4* worldb, const Matrix4x4* worldm) +{ + // 1) Call the base method + VolumeCollider::InitQuery(); + + // 2) Compute obb in world space + mBoxExtents = box.mExtents; + + Matrix4x4 WorldB; + + if(worldb) + { + WorldB = Matrix4x4( box.mRot * Matrix3x3(*worldb) ); + WorldB.SetTrans(box.mCenter * *worldb); + } + else + { + WorldB = box.mRot; + WorldB.SetTrans(box.mCenter); + } + + // Setup matrices + Matrix4x4 InvWorldB; + InvertPRMatrix(InvWorldB, WorldB); + + if(worldm) + { + Matrix4x4 InvWorldM; + InvertPRMatrix(InvWorldM, *worldm); + + Matrix4x4 WorldBtoM = WorldB * InvWorldM; + Matrix4x4 WorldMtoB = *worldm * InvWorldB; + + mRModelToBox = WorldMtoB; WorldMtoB.GetTrans(mTModelToBox); + mRBoxToModel = WorldBtoM; WorldBtoM.GetTrans(mTBoxToModel); + } + else + { + mRModelToBox = InvWorldB; InvWorldB.GetTrans(mTModelToBox); + mRBoxToModel = WorldB; WorldB.GetTrans(mTBoxToModel); + } + + // 3) Setup destination pointer + mTouchedPrimitives = &cache.TouchedPrimitives; + + // 4) Special case: 1-triangle meshes [Opcode 1.3] + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + if(!SkipPrimitiveTests()) + { + // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0. + mTouchedPrimitives->Reset(); + + // Perform overlap test between the unique triangle and the box (and set contact status if needed) + OBB_PRIM(udword(0), OPC_CONTACT) + + // Return immediately regardless of status + return TRUE; + } + } + + // 5) Check temporal coherence: + if(TemporalCoherenceEnabled()) + { + // Here we use temporal coherence + // => check results from previous frame before performing the collision query + if(FirstContactEnabled()) + { + // We're only interested in the first contact found => test the unique previously touched face + if(mTouchedPrimitives->GetNbEntries()) + { + // Get index of previously touched face = the first entry in the array + udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0); + + // Then reset the array: + // - if the overlap test below is successful, the index we'll get added back anyway + // - if it isn't, then the array should be reset anyway for the normal query + mTouchedPrimitives->Reset(); + + // Perform overlap test between the cached triangle and the box (and set contact status if needed) + OBB_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT) + + // Return immediately if possible + if(GetContactStatus()) return TRUE; + } + // else no face has been touched during previous query + // => we'll have to perform a normal query + } + else + { + // ### rewrite this + OBB TestBox(mTBoxToModel, mBoxExtents, mRBoxToModel); + + // We're interested in all contacts =>test the new real box N(ew) against the previous fat box P(revious): + if(IsCacheValid(cache) && TestBox.IsInside(cache.FatBox)) + { + // - if N is included in P, return previous list + // => we simply leave the list (mTouchedFaces) unchanged + + // Set contact status if needed + if(mTouchedPrimitives->GetNbEntries()) mFlags |= OPC_TEMPORAL_CONTACT; + + // In any case we don't need to do a query + return TRUE; + } + else + { + // - else do the query using a fat N + + // Reset cache since we'll about to perform a real query + mTouchedPrimitives->Reset(); + + // Make a fat box so that coherence will work for subsequent frames + TestBox.mExtents *= cache.FatCoeff; + mBoxExtents *= cache.FatCoeff; + + // Update cache with query data (signature for cached faces) + cache.FatBox = TestBox; + } + } + } + else + { + // Here we don't use temporal coherence => do a normal query + mTouchedPrimitives->Reset(); + } + + // Now we can precompute box-box data + + // Precompute absolute box-to-model rotation matrix + for(udword i=0;i<3;i++) + { + for(udword j=0;j<3;j++) + { + // Epsilon value prevents floating-point inaccuracies (strategy borrowed from RAPID) + mAR.m[i][j] = 1e-6f + fabsf(mRBoxToModel.m[i][j]); + } + } + + // Precompute bounds for box-in-box test + mB0 = mBoxExtents - mTModelToBox; + mB1 = - mBoxExtents - mTModelToBox; + + // Precompute box-box data - Courtesy of Erwin de Vries + mBBx1 = mBoxExtents.x*mAR.m[0][0] + mBoxExtents.y*mAR.m[1][0] + mBoxExtents.z*mAR.m[2][0]; + mBBy1 = mBoxExtents.x*mAR.m[0][1] + mBoxExtents.y*mAR.m[1][1] + mBoxExtents.z*mAR.m[2][1]; + mBBz1 = mBoxExtents.x*mAR.m[0][2] + mBoxExtents.y*mAR.m[1][2] + mBoxExtents.z*mAR.m[2][2]; + + mBB_1 = mBoxExtents.y*mAR.m[2][0] + mBoxExtents.z*mAR.m[1][0]; + mBB_2 = mBoxExtents.x*mAR.m[2][0] + mBoxExtents.z*mAR.m[0][0]; + mBB_3 = mBoxExtents.x*mAR.m[1][0] + mBoxExtents.y*mAR.m[0][0]; + mBB_4 = mBoxExtents.y*mAR.m[2][1] + mBoxExtents.z*mAR.m[1][1]; + mBB_5 = mBoxExtents.x*mAR.m[2][1] + mBoxExtents.z*mAR.m[0][1]; + mBB_6 = mBoxExtents.x*mAR.m[1][1] + mBoxExtents.y*mAR.m[0][1]; + mBB_7 = mBoxExtents.y*mAR.m[2][2] + mBoxExtents.z*mAR.m[1][2]; + mBB_8 = mBoxExtents.x*mAR.m[2][2] + mBoxExtents.z*mAR.m[0][2]; + mBB_9 = mBoxExtents.x*mAR.m[1][2] + mBoxExtents.y*mAR.m[0][2]; + + return FALSE; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks the OBB completely contains the box. In which case we can end the query sooner. + * \param bc [in] box center + * \param be [in] box extents + * \return true if the OBB contains the whole box + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ BOOL OBBCollider::OBBContainsBox(const Point& bc, const Point& be) +{ + // I assume if all 8 box vertices are inside the OBB, so does the whole box. + // Sounds ok but maybe there's a better way? +/* +#define TEST_PT(a,b,c) \ + p.x=a; p.y=b; p.z=c; p+=bc; \ + f = p.x * mRModelToBox.m[0][0] + p.y * mRModelToBox.m[1][0] + p.z * mRModelToBox.m[2][0]; if(f>mB0.x || fmB0.y || fmB0.z || f NCx-NEx) return FALSE; + + float NCy = bc.x * mRModelToBox.m[0][1] + bc.y * mRModelToBox.m[1][1] + bc.z * mRModelToBox.m[2][1]; + float NEy = fabsf(mRModelToBox.m[0][1] * be.x) + fabsf(mRModelToBox.m[1][1] * be.y) + fabsf(mRModelToBox.m[2][1] * be.z); + + if(mB0.y < NCy+NEy) return FALSE; + if(mB1.y > NCy-NEy) return FALSE; + + float NCz = bc.x * mRModelToBox.m[0][2] + bc.y * mRModelToBox.m[1][2] + bc.z * mRModelToBox.m[2][2]; + float NEz = fabsf(mRModelToBox.m[0][2] * be.x) + fabsf(mRModelToBox.m[1][2] * be.y) + fabsf(mRModelToBox.m[2][2] * be.z); + + if(mB0.z < NCz+NEz) return FALSE; + if(mB1.z > NCz-NEz) return FALSE; + + return TRUE; +} + +#define TEST_BOX_IN_OBB(center, extents) \ + if(OBBContainsBox(center, extents)) \ + { \ + /* Set contact status */ \ + mFlags |= OPC_CONTACT; \ + _Dump(node); \ + return; \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBBCollider::_Collide(const AABBCollisionNode* node) +{ + // Perform OBB-AABB overlap test + if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return; + + TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->IsLeaf()) + { + OBB_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _Collide(node->GetPos()); + + if(ContactFound()) return; + + _Collide(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBBCollider::_CollideNoPrimitiveTest(const AABBCollisionNode* node) +{ + // Perform OBB-AABB overlap test + if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return; + + TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBBCollider::_Collide(const AABBQuantizedNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform OBB-AABB overlap test + if(!BoxBoxOverlap(Extents, Center)) return; + + TEST_BOX_IN_OBB(Center, Extents) + + if(node->IsLeaf()) + { + OBB_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _Collide(node->GetPos()); + + if(ContactFound()) return; + + _Collide(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBBCollider::_CollideNoPrimitiveTest(const AABBQuantizedNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform OBB-AABB overlap test + if(!BoxBoxOverlap(Extents, Center)) return; + + TEST_BOX_IN_OBB(Center, Extents) + + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBBCollider::_Collide(const AABBNoLeafNode* node) +{ + // Perform OBB-AABB overlap test + if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return; + + TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->HasPosLeaf()) { OBB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } + else _Collide(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { OBB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } + else _Collide(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBBCollider::_CollideNoPrimitiveTest(const AABBNoLeafNode* node) +{ + // Perform OBB-AABB overlap test + if(!BoxBoxOverlap(node->mAABB.mExtents, node->mAABB.mCenter)) return; + + TEST_BOX_IN_OBB(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBBCollider::_Collide(const AABBQuantizedNoLeafNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform OBB-AABB overlap test + if(!BoxBoxOverlap(Extents, Center)) return; + + TEST_BOX_IN_OBB(Center, Extents) + + if(node->HasPosLeaf()) { OBB_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } + else _Collide(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { OBB_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } + else _Collide(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void OBBCollider::_CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform OBB-AABB overlap test + if(!BoxBoxOverlap(Extents, Center)) return; + + TEST_BOX_IN_OBB(Center, Extents) + + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg()); +} + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridOBBCollider::HybridOBBCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridOBBCollider::~HybridOBBCollider() +{ +} + +bool HybridOBBCollider::Collide(OBBCache& cache, const OBB& box, const HybridModel& model, const Matrix4x4* worldb, const Matrix4x4* worldm) +{ + // We don't want primitive tests here! + mFlags |= OPC_NO_PRIMITIVE_TESTS; + + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, box, worldb, worldm)) return true; + + // Special case for 1-leaf trees + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles + udword Nb = mIMesh->GetNbTriangles(); + + // Loop through all triangles + for(udword i=0;imCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + else + { + const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree(); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + else + { + const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree(); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + } + + // We only have a list of boxes so far + if(GetContactStatus()) + { + // Reset contact status, since it currently only reflects collisions with leaf boxes + Collider::InitQuery(); + + // Change dest container so that we can use built-in overlap tests and get collided primitives + cache.TouchedPrimitives.Reset(); + mTouchedPrimitives = &cache.TouchedPrimitives; + + // Read touched leaf boxes + udword Nb = mTouchedBoxes.GetNbEntries(); + const udword* Touched = mTouchedBoxes.GetEntries(); + + const LeafTriangles* LT = model.GetLeafTriangles(); + const udword* Indices = model.GetIndices(); + + // Loop through touched leaves + while(Nb--) + { + const LeafTriangles& CurrentLeaf = LT[*Touched++]; + + // Each leaf box has a set of triangles + udword NbTris = CurrentLeaf.GetNbTriangles(); + if(Indices) + { + const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()]; + + // Loop through triangles and test each of them + while(NbTris--) + { + udword TriangleIndex = *T++; + OBB_PRIM(TriangleIndex, OPC_CONTACT) + } + } + else + { + udword BaseIndex = CurrentLeaf.GetTriangleIndex(); + + // Loop through triangles and test each of them + while(NbTris--) + { + udword TriangleIndex = BaseIndex++; + OBB_PRIM(TriangleIndex, OPC_CONTACT) + } + } + } + } + + return true; +} diff --git a/Engine/lib/opcode/OPC_OBBCollider.h b/Engine/lib/opcode/OPC_OBBCollider.h new file mode 100644 index 000000000..95325abee --- /dev/null +++ b/Engine/lib/opcode/OPC_OBBCollider.h @@ -0,0 +1,142 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for an OBB collider. + * \file OPC_OBBCollider.h + * \author Pierre Terdiman + * \date January, 1st, 2002 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_OBBCOLLIDER_H__ +#define __OPC_OBBCOLLIDER_H__ + + struct OPCODE_API OBBCache : VolumeCache + { + OBBCache() : FatCoeff(1.1f) + { + FatBox.mCenter.Zero(); + FatBox.mExtents.Zero(); + FatBox.mRot.Identity(); + } + + // Cached faces signature + OBB FatBox; //!< Box used when performing the query resulting in cached faces + // User settings + float FatCoeff; //!< extents multiplier used to create a fat box + }; + + class OPCODE_API OBBCollider : public VolumeCollider + { + public: + // Constructor / Destructor + OBBCollider(); + virtual ~OBBCollider(); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Generic collision query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - with GetNbTouchedPrimitives() + * - with GetTouchedPrimitives() + * + * \param cache [in/out] a box cache + * \param box [in] collision OBB in local space + * \param model [in] Opcode model to collide with + * \param worldb [in] OBB's world matrix, or null + * \param worldm [in] model's world matrix, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool Collide(OBBCache& cache, const OBB& box, const Model& model, const Matrix4x4* worldb=null, const Matrix4x4* worldm=null); + + // Settings + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Settings: select between full box-box tests or "SAT-lite" tests (where Class III axes are discarded) + * \param flag [in] true for full tests, false for coarse tests + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetFullBoxBoxTest(bool flag) { mFullBoxBoxTest = flag; } + + // Settings + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Validates current settings. You should call this method after all the settings and callbacks have been defined for a collider. + * \return null if everything is ok, else a string describing the problem + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + override(Collider) const char* ValidateSettings(); + + protected: + // Precomputed data + Matrix3x3 mAR; //!< Absolute rotation matrix + Matrix3x3 mRModelToBox; //!< Rotation from model space to obb space + Matrix3x3 mRBoxToModel; //!< Rotation from obb space to model space + Point mTModelToBox; //!< Translation from model space to obb space + Point mTBoxToModel; //!< Translation from obb space to model space + + Point mBoxExtents; + Point mB0; //!< - mTModelToBox + mBoxExtents + Point mB1; //!< - mTModelToBox - mBoxExtents + + float mBBx1; + float mBBy1; + float mBBz1; + + float mBB_1; + float mBB_2; + float mBB_3; + float mBB_4; + float mBB_5; + float mBB_6; + float mBB_7; + float mBB_8; + float mBB_9; + + // Leaf description + Point mLeafVerts[3]; //!< Triangle vertices + // Settings + bool mFullBoxBoxTest; //!< Perform full BV-BV tests (true) or SAT-lite tests (false) + // Internal methods + void _Collide(const AABBCollisionNode* node); + void _Collide(const AABBNoLeafNode* node); + void _Collide(const AABBQuantizedNode* node); + void _Collide(const AABBQuantizedNoLeafNode* node); + void _CollideNoPrimitiveTest(const AABBCollisionNode* node); + void _CollideNoPrimitiveTest(const AABBNoLeafNode* node); + void _CollideNoPrimitiveTest(const AABBQuantizedNode* node); + void _CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node); + // Overlap tests + inline_ BOOL OBBContainsBox(const Point& bc, const Point& be); + inline_ BOOL BoxBoxOverlap(const Point& extents, const Point& center); + inline_ BOOL TriBoxOverlap(); + // Init methods + BOOL InitQuery(OBBCache& cache, const OBB& box, const Matrix4x4* worldb=null, const Matrix4x4* worldm=null); + }; + + class OPCODE_API HybridOBBCollider : public OBBCollider + { + public: + // Constructor / Destructor + HybridOBBCollider(); + virtual ~HybridOBBCollider(); + + bool Collide(OBBCache& cache, const OBB& box, const HybridModel& model, const Matrix4x4* worldb=null, const Matrix4x4* worldm=null); + protected: + OPC_Container mTouchedBoxes; + }; + +#endif // __OPC_OBBCOLLIDER_H__ diff --git a/Engine/lib/opcode/OPC_OptimizedTree.cpp b/Engine/lib/opcode/OPC_OptimizedTree.cpp new file mode 100644 index 000000000..81b3a2462 --- /dev/null +++ b/Engine/lib/opcode/OPC_OptimizedTree.cpp @@ -0,0 +1,781 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for optimized trees. Implements 4 trees: + * - normal + * - no leaf + * - quantized + * - no leaf / quantized + * + * \file OPC_OptimizedTree.cpp + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * A standard AABB tree. + * + * \class AABBCollisionTree + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * A no-leaf AABB tree. + * + * \class AABBNoLeafTree + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * A quantized AABB tree. + * + * \class AABBQuantizedTree + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * A quantized no-leaf AABB tree. + * + * \class AABBQuantizedNoLeafTree + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "Opcode.h" + +using namespace Opcode; + +//! Compilation flag: +//! - true to fix quantized boxes (i.e. make sure they enclose the original ones) +//! - false to see the effects of quantization errors (faster, but wrong results in some cases) +static bool gFixQuantized = true; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Builds an implicit tree from a standard one. An implicit tree is a complete tree (2*N-1 nodes) whose negative + * box pointers and primitive pointers have been made implicit, hence packing 3 pointers in one. + * + * Layout for implicit trees: + * Node: + * - box + * - data (32-bits value) + * + * if data's LSB = 1 => remaining bits are a primitive pointer + * else remaining bits are a P-node pointer, and N = P + 1 + * + * \relates AABBCollisionNode + * \fn _BuildCollisionTree(AABBCollisionNode* linear, const udword box_id, udword& current_id, const AABBTreeNode* current_node) + * \param linear [in] base address of destination nodes + * \param box_id [in] index of destination node + * \param current_id [in] current running index + * \param current_node [in] current node from input tree + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +static void _BuildCollisionTree(AABBCollisionNode* linear, const udword box_id, udword& current_id, const AABBTreeNode* current_node) +{ + // Current node from input tree is "current_node". Must be flattened into "linear[boxid]". + + // Store the AABB + current_node->GetAABB()->GetCenter(linear[box_id].mAABB.mCenter); + current_node->GetAABB()->GetExtents(linear[box_id].mAABB.mExtents); + // Store remaining info + if(current_node->IsLeaf()) + { + // The input tree must be complete => i.e. one primitive/leaf + ASSERT(current_node->GetNbPrimitives()==1); + // Get the primitive index from the input tree + udword PrimitiveIndex = current_node->GetPrimitives()[0]; + // Setup box data as the primitive index, marked as leaf + linear[box_id].mData = (PrimitiveIndex<<1)|1; + } + else + { + // To make the negative one implicit, we must store P and N in successive order + udword PosID = current_id++; // Get a new id for positive child + udword NegID = current_id++; // Get a new id for negative child + // Setup box data as the forthcoming new P pointer + linear[box_id].mData = (udword)&linear[PosID]; + // Make sure it's not marked as leaf + ASSERT(!(linear[box_id].mData&1)); + // Recurse with new IDs + _BuildCollisionTree(linear, PosID, current_id, current_node->GetPos()); + _BuildCollisionTree(linear, NegID, current_id, current_node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Builds a "no-leaf" tree from a standard one. This is a tree whose leaf nodes have been removed. + * + * Layout for no-leaf trees: + * + * Node: + * - box + * - P pointer => a node (LSB=0) or a primitive (LSB=1) + * - N pointer => a node (LSB=0) or a primitive (LSB=1) + * + * \relates AABBNoLeafNode + * \fn _BuildNoLeafTree(AABBNoLeafNode* linear, const udword box_id, udword& current_id, const AABBTreeNode* current_node) + * \param linear [in] base address of destination nodes + * \param box_id [in] index of destination node + * \param current_id [in] current running index + * \param current_node [in] current node from input tree + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +static void _BuildNoLeafTree(AABBNoLeafNode* linear, const udword box_id, udword& current_id, const AABBTreeNode* current_node) +{ + const AABBTreeNode* P = current_node->GetPos(); + const AABBTreeNode* N = current_node->GetNeg(); + // Leaf nodes here?! + ASSERT(P); + ASSERT(N); + // Internal node => keep the box + current_node->GetAABB()->GetCenter(linear[box_id].mAABB.mCenter); + current_node->GetAABB()->GetExtents(linear[box_id].mAABB.mExtents); + + if(P->IsLeaf()) + { + // The input tree must be complete => i.e. one primitive/leaf + ASSERT(P->GetNbPrimitives()==1); + // Get the primitive index from the input tree + udword PrimitiveIndex = P->GetPrimitives()[0]; + // Setup prev box data as the primitive index, marked as leaf + linear[box_id].mPosData = (PrimitiveIndex<<1)|1; + } + else + { + // Get a new id for positive child + udword PosID = current_id++; + // Setup box data + linear[box_id].mPosData = (udword)&linear[PosID]; + // Make sure it's not marked as leaf + ASSERT(!(linear[box_id].mPosData&1)); + // Recurse + _BuildNoLeafTree(linear, PosID, current_id, P); + } + + if(N->IsLeaf()) + { + // The input tree must be complete => i.e. one primitive/leaf + ASSERT(N->GetNbPrimitives()==1); + // Get the primitive index from the input tree + udword PrimitiveIndex = N->GetPrimitives()[0]; + // Setup prev box data as the primitive index, marked as leaf + linear[box_id].mNegData = (PrimitiveIndex<<1)|1; + } + else + { + // Get a new id for negative child + udword NegID = current_id++; + // Setup box data + linear[box_id].mNegData = (udword)&linear[NegID]; + // Make sure it's not marked as leaf + ASSERT(!(linear[box_id].mNegData&1)); + // Recurse + _BuildNoLeafTree(linear, NegID, current_id, N); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBCollisionTree::AABBCollisionTree() : mNodes(null) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBCollisionTree::~AABBCollisionTree() +{ + DELETEARRAY(mNodes); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Builds the collision tree from a generic AABB tree. + * \param tree [in] generic AABB tree + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBCollisionTree::Build(AABBTree* tree) +{ + // Checkings + if(!tree) return false; + // Check the input tree is complete + udword NbTriangles = tree->GetNbPrimitives(); + udword NbNodes = tree->GetNbNodes(); + if(NbNodes!=NbTriangles*2-1) return false; + + // Get nodes + if(mNbNodes!=NbNodes) // Same number of nodes => keep moving + { + mNbNodes = NbNodes; + DELETEARRAY(mNodes); + mNodes = new AABBCollisionNode[mNbNodes]; + CHECKALLOC(mNodes); + } + + // Build the tree + udword CurID = 1; + _BuildCollisionTree(mNodes, 0, CurID, tree); + ASSERT(CurID==mNbNodes); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Refits the collision tree after vertices have been modified. + * \param mesh_interface [in] mesh interface for current model + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBCollisionTree::Refit(const MeshInterface* mesh_interface) +{ + ASSERT(!"Not implemented since AABBCollisionTrees have twice as more nodes to refit as AABBNoLeafTrees!"); + return false; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Walks the tree and call the user back for each node. + * \param callback [in] walking callback + * \param user_data [in] callback's user data + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBCollisionTree::Walk(GenericWalkingCallback callback, void* user_data) const +{ + if(!callback) return false; + + struct Local + { + static void _Walk(const AABBCollisionNode* current_node, GenericWalkingCallback callback, void* user_data) + { + if(!current_node || !(callback)(current_node, user_data)) return; + + if(!current_node->IsLeaf()) + { + _Walk(current_node->GetPos(), callback, user_data); + _Walk(current_node->GetNeg(), callback, user_data); + } + } + }; + Local::_Walk(mNodes, callback, user_data); + return true; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBNoLeafTree::AABBNoLeafTree() : mNodes(null) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBNoLeafTree::~AABBNoLeafTree() +{ + DELETEARRAY(mNodes); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Builds the collision tree from a generic AABB tree. + * \param tree [in] generic AABB tree + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBNoLeafTree::Build(AABBTree* tree) +{ + // Checkings + if(!tree) return false; + // Check the input tree is complete + udword NbTriangles = tree->GetNbPrimitives(); + udword NbNodes = tree->GetNbNodes(); + if(NbNodes!=NbTriangles*2-1) return false; + + // Get nodes + if(mNbNodes!=NbTriangles-1) // Same number of nodes => keep moving + { + mNbNodes = NbTriangles-1; + DELETEARRAY(mNodes); + mNodes = new AABBNoLeafNode[mNbNodes]; + CHECKALLOC(mNodes); + } + + // Build the tree + udword CurID = 1; + _BuildNoLeafTree(mNodes, 0, CurID, tree); + ASSERT(CurID==mNbNodes); + + return true; +} + +inline_ void ComputeMinMax(Point& min, Point& max, const VertexPointers& vp) +{ + // Compute triangle's AABB = a leaf box +#ifdef OPC_USE_FCOMI // a 15% speedup on my machine, not much + min.x = FCMin3(vp.Vertex[0]->x, vp.Vertex[1]->x, vp.Vertex[2]->x); + max.x = FCMax3(vp.Vertex[0]->x, vp.Vertex[1]->x, vp.Vertex[2]->x); + + min.y = FCMin3(vp.Vertex[0]->y, vp.Vertex[1]->y, vp.Vertex[2]->y); + max.y = FCMax3(vp.Vertex[0]->y, vp.Vertex[1]->y, vp.Vertex[2]->y); + + min.z = FCMin3(vp.Vertex[0]->z, vp.Vertex[1]->z, vp.Vertex[2]->z); + max.z = FCMax3(vp.Vertex[0]->z, vp.Vertex[1]->z, vp.Vertex[2]->z); +#else + min = *vp.Vertex[0]; + max = *vp.Vertex[0]; + min.Min(*vp.Vertex[1]); + max.Max(*vp.Vertex[1]); + min.Min(*vp.Vertex[2]); + max.Max(*vp.Vertex[2]); +#endif +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Refits the collision tree after vertices have been modified. + * \param mesh_interface [in] mesh interface for current model + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBNoLeafTree::Refit(const MeshInterface* mesh_interface) +{ + // Checkings + if(!mesh_interface) return false; + + // Bottom-up update + VertexPointers VP; + Point Min,Max; + Point Min_,Max_; + udword Index = mNbNodes; + while(Index--) + { + AABBNoLeafNode& Current = mNodes[Index]; + + if(Current.HasPosLeaf()) + { + mesh_interface->GetTriangle(VP, Current.GetPosPrimitive()); + ComputeMinMax(Min, Max, VP); + } + else + { + const CollisionAABB& CurrentBox = Current.GetPos()->mAABB; + CurrentBox.GetMin(Min); + CurrentBox.GetMax(Max); + } + + if(Current.HasNegLeaf()) + { + mesh_interface->GetTriangle(VP, Current.GetNegPrimitive()); + ComputeMinMax(Min_, Max_, VP); + } + else + { + const CollisionAABB& CurrentBox = Current.GetNeg()->mAABB; + CurrentBox.GetMin(Min_); + CurrentBox.GetMax(Max_); + } +#ifdef OPC_USE_FCOMI + Min.x = FCMin2(Min.x, Min_.x); + Max.x = FCMax2(Max.x, Max_.x); + Min.y = FCMin2(Min.y, Min_.y); + Max.y = FCMax2(Max.y, Max_.y); + Min.z = FCMin2(Min.z, Min_.z); + Max.z = FCMax2(Max.z, Max_.z); +#else + Min.Min(Min_); + Max.Max(Max_); +#endif + Current.mAABB.SetMinMax(Min, Max); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Walks the tree and call the user back for each node. + * \param callback [in] walking callback + * \param user_data [in] callback's user data + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBNoLeafTree::Walk(GenericWalkingCallback callback, void* user_data) const +{ + if(!callback) return false; + + struct Local + { + static void _Walk(const AABBNoLeafNode* current_node, GenericWalkingCallback callback, void* user_data) + { + if(!current_node || !(callback)(current_node, user_data)) return; + + if(!current_node->HasPosLeaf()) _Walk(current_node->GetPos(), callback, user_data); + if(!current_node->HasNegLeaf()) _Walk(current_node->GetNeg(), callback, user_data); + } + }; + Local::_Walk(mNodes, callback, user_data); + return true; +} + +// Quantization notes: +// - We could use the highest bits of mData to store some more quantized bits. Dequantization code +// would be slightly more complex, but number of overlap tests would be reduced (and anyhow those +// bits are currently wasted). Of course it's not possible if we move to 16 bits mData. +// - Something like "16 bits floats" could be tested, to bypass the int-to-float conversion. +// - A dedicated BV-BV test could be used, dequantizing while testing for overlap. (i.e. it's some +// lazy-dequantization which may save some work in case of early exits). At the very least some +// muls could be saved by precomputing several more matrices. But maybe not worth the pain. +// - Do we need to dequantize anyway? Not doing the extents-related muls only implies the box has +// been scaled, for example. +// - The deeper we move into the hierarchy, the smaller the extents should be. May not need a fixed +// number of quantization bits. Even better, could probably be best delta-encoded. + + +// Find max values. Some people asked why I wasn't simply using the first node. Well, I can't. +// I'm not looking for (min, max) values like in a standard AABB, I'm looking for the extremal +// centers/extents in order to quantize them. The first node would only give a single center and +// a single extents. While extents would be the biggest, the center wouldn't. +#define FIND_MAX_VALUES \ + /* Get max values */ \ + Point CMax(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); \ + Point EMax(MIN_FLOAT, MIN_FLOAT, MIN_FLOAT); \ + for(udword i=0;iCMax.x) CMax.x = fabsf(Nodes[i].mAABB.mCenter.x); \ + if(fabsf(Nodes[i].mAABB.mCenter.y)>CMax.y) CMax.y = fabsf(Nodes[i].mAABB.mCenter.y); \ + if(fabsf(Nodes[i].mAABB.mCenter.z)>CMax.z) CMax.z = fabsf(Nodes[i].mAABB.mCenter.z); \ + if(fabsf(Nodes[i].mAABB.mExtents.x)>EMax.x) EMax.x = fabsf(Nodes[i].mAABB.mExtents.x); \ + if(fabsf(Nodes[i].mAABB.mExtents.y)>EMax.y) EMax.y = fabsf(Nodes[i].mAABB.mExtents.y); \ + if(fabsf(Nodes[i].mAABB.mExtents.z)>EMax.z) EMax.z = fabsf(Nodes[i].mAABB.mExtents.z); \ + } + +#define INIT_QUANTIZATION \ + udword nbc=15; /* Keep one bit for sign */ \ + udword nbe=15; /* Keep one bit for fix */ \ + if(!gFixQuantized) nbe++; \ + \ + /* Compute quantization coeffs */ \ + Point CQuantCoeff, EQuantCoeff; \ + CQuantCoeff.x = CMax.x!=0.0f ? float((1<Min[j]) mNodes[i].mAABB.mExtents[j]++; \ + else FixMe=false; \ + /* Prevent wrapping */ \ + if(!mNodes[i].mAABB.mExtents[j]) \ + { \ + mNodes[i].mAABB.mExtents[j]=0xffff; \ + FixMe=false; \ + } \ + }while(FixMe); \ + } \ + } + +#define REMAP_DATA(member) \ + /* Fix data */ \ + Data = Nodes[i].member; \ + if(!(Data&1)) \ + { \ + /* Compute box number */ \ + udword Nb = (Data - udword(Nodes))/Nodes[i].GetNodeSize(); \ + Data = udword(&mNodes[Nb]); \ + } \ + /* ...remapped */ \ + mNodes[i].member = Data; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBQuantizedTree::AABBQuantizedTree() : mNodes(null) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBQuantizedTree::~AABBQuantizedTree() +{ + DELETEARRAY(mNodes); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Builds the collision tree from a generic AABB tree. + * \param tree [in] generic AABB tree + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBQuantizedTree::Build(AABBTree* tree) +{ + // Checkings + if(!tree) return false; + // Check the input tree is complete + udword NbTriangles = tree->GetNbPrimitives(); + udword NbNodes = tree->GetNbNodes(); + if(NbNodes!=NbTriangles*2-1) return false; + + // Get nodes + mNbNodes = NbNodes; + DELETEARRAY(mNodes); + AABBCollisionNode* Nodes = new AABBCollisionNode[mNbNodes]; + CHECKALLOC(Nodes); + + // Build the tree + udword CurID = 1; + _BuildCollisionTree(Nodes, 0, CurID, tree); + + // Quantize + { + mNodes = new AABBQuantizedNode[mNbNodes]; + CHECKALLOC(mNodes); + + // Get max values + FIND_MAX_VALUES + + // Quantization + INIT_QUANTIZATION + + // Quantize + udword Data; + for(udword i=0;iIsLeaf()) + { + _Walk(current_node->GetPos(), callback, user_data); + _Walk(current_node->GetNeg(), callback, user_data); + } + } + }; + Local::_Walk(mNodes, callback, user_data); + return true; +} + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBQuantizedNoLeafTree::AABBQuantizedNoLeafTree() : mNodes(null) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +AABBQuantizedNoLeafTree::~AABBQuantizedNoLeafTree() +{ + DELETEARRAY(mNodes); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Builds the collision tree from a generic AABB tree. + * \param tree [in] generic AABB tree + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBQuantizedNoLeafTree::Build(AABBTree* tree) +{ + // Checkings + if(!tree) return false; + // Check the input tree is complete + udword NbTriangles = tree->GetNbPrimitives(); + udword NbNodes = tree->GetNbNodes(); + if(NbNodes!=NbTriangles*2-1) return false; + + // Get nodes + mNbNodes = NbTriangles-1; + DELETEARRAY(mNodes); + AABBNoLeafNode* Nodes = new AABBNoLeafNode[mNbNodes]; + CHECKALLOC(Nodes); + + // Build the tree + udword CurID = 1; + _BuildNoLeafTree(Nodes, 0, CurID, tree); + ASSERT(CurID==mNbNodes); + + // Quantize + { + mNodes = new AABBQuantizedNoLeafNode[mNbNodes]; + CHECKALLOC(mNodes); + + // Get max values + FIND_MAX_VALUES + + // Quantization + INIT_QUANTIZATION + + // Quantize + udword Data; + for(udword i=0;iHasPosLeaf()) _Walk(current_node->GetPos(), callback, user_data); + if(!current_node->HasNegLeaf()) _Walk(current_node->GetNeg(), callback, user_data); + } + }; + Local::_Walk(mNodes, callback, user_data); + return true; +} diff --git a/Engine/lib/opcode/OPC_OptimizedTree.h b/Engine/lib/opcode/OPC_OptimizedTree.h new file mode 100644 index 000000000..78fc4ef37 --- /dev/null +++ b/Engine/lib/opcode/OPC_OptimizedTree.h @@ -0,0 +1,206 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for optimized trees. + * \file OPC_OptimizedTree.h + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_OPTIMIZEDTREE_H__ +#define __OPC_OPTIMIZEDTREE_H__ + + //! Common interface for a node of an implicit tree + #define IMPLEMENT_IMPLICIT_NODE(base_class, volume) \ + public: \ + /* Constructor / Destructor */ \ + inline_ base_class() : mData(0) {} \ + inline_ ~base_class() {} \ + /* Leaf test */ \ + inline_ BOOL IsLeaf() const { return mData&1; } \ + /* Data access */ \ + inline_ const base_class* GetPos() const { return (base_class*)mData; } \ + inline_ const base_class* GetNeg() const { return ((base_class*)mData)+1; } \ + inline_ udword GetPrimitive() const { return (mData>>1); } \ + /* Stats */ \ + inline_ udword GetNodeSize() const { return SIZEOFOBJECT; } \ + \ + volume mAABB; \ + udword mData; + + //! Common interface for a node of a no-leaf tree + #define IMPLEMENT_NOLEAF_NODE(base_class, volume) \ + public: \ + /* Constructor / Destructor */ \ + inline_ base_class() : mPosData(0), mNegData(0) {} \ + inline_ ~base_class() {} \ + /* Leaf tests */ \ + inline_ BOOL HasPosLeaf() const { return mPosData&1; } \ + inline_ BOOL HasNegLeaf() const { return mNegData&1; } \ + /* Data access */ \ + inline_ const base_class* GetPos() const { return (base_class*)mPosData; } \ + inline_ const base_class* GetNeg() const { return (base_class*)mNegData; } \ + inline_ udword GetPosPrimitive() const { return (mPosData>>1); } \ + inline_ udword GetNegPrimitive() const { return (mNegData>>1); } \ + /* Stats */ \ + inline_ udword GetNodeSize() const { return SIZEOFOBJECT; } \ + \ + volume mAABB; \ + udword mPosData; \ + udword mNegData; + + class OPCODE_API AABBCollisionNode + { + IMPLEMENT_IMPLICIT_NODE(AABBCollisionNode, CollisionAABB) + + inline_ float GetVolume() const { return mAABB.mExtents.x * mAABB.mExtents.y * mAABB.mExtents.z; } + inline_ float GetSize() const { return mAABB.mExtents.SquareMagnitude(); } + inline_ udword GetRadius() const + { + udword* Bits = (udword*)&mAABB.mExtents.x; + udword Max = Bits[0]; + if(Bits[1]>Max) Max = Bits[1]; + if(Bits[2]>Max) Max = Bits[2]; + return Max; + } + + // NB: using the square-magnitude or the true volume of the box, seems to yield better results + // (assuming UNC-like informed traversal methods). I borrowed this idea from PQP. The usual "size" + // otherwise, is the largest box extent. In SOLID that extent is computed on-the-fly each time it's + // needed (the best approach IMHO). In RAPID the rotation matrix is permuted so that Extent[0] is + // always the greatest, which saves looking for it at runtime. On the other hand, it yields matrices + // whose determinant is not 1, i.e. you can't encode them anymore as unit quaternions. Not a very + // good strategy. + }; + + class OPCODE_API AABBQuantizedNode + { + IMPLEMENT_IMPLICIT_NODE(AABBQuantizedNode, QuantizedAABB) + + inline_ uword GetSize() const + { + const uword* Bits = mAABB.mExtents; + uword Max = Bits[0]; + if(Bits[1]>Max) Max = Bits[1]; + if(Bits[2]>Max) Max = Bits[2]; + return Max; + } + // NB: for quantized nodes I don't feel like computing a square-magnitude with integers all + // over the place.......! + }; + + class OPCODE_API AABBNoLeafNode + { + IMPLEMENT_NOLEAF_NODE(AABBNoLeafNode, CollisionAABB) + }; + + class OPCODE_API AABBQuantizedNoLeafNode + { + IMPLEMENT_NOLEAF_NODE(AABBQuantizedNoLeafNode, QuantizedAABB) + }; + + //! Common interface for a collision tree + #define IMPLEMENT_COLLISION_TREE(base_class, node) \ + public: \ + /* Constructor / Destructor */ \ + base_class(); \ + virtual ~base_class(); \ + /* Builds from a standard tree */ \ + override(AABBOptimizedTree) bool Build(AABBTree* tree); \ + /* Refits the tree */ \ + override(AABBOptimizedTree) bool Refit(const MeshInterface* mesh_interface); \ + /* Walks the tree */ \ + override(AABBOptimizedTree) bool Walk(GenericWalkingCallback callback, void* user_data) const; \ + /* Data access */ \ + inline_ const node* GetNodes() const { return mNodes; } \ + /* Stats */ \ + override(AABBOptimizedTree) udword GetUsedBytes() const { return mNbNodes*sizeof(node); } \ + private: \ + node* mNodes; + + typedef bool (*GenericWalkingCallback) (const void* current, void* user_data); + + class OPCODE_API AABBOptimizedTree + { + public: + // Constructor / Destructor + AABBOptimizedTree() : + mNbNodes (0) + {} + virtual ~AABBOptimizedTree() {} + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Builds the collision tree from a generic AABB tree. + * \param tree [in] generic AABB tree + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual bool Build(AABBTree* tree) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Refits the collision tree after vertices have been modified. + * \param mesh_interface [in] mesh interface for current model + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual bool Refit(const MeshInterface* mesh_interface) = 0; + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Walks the tree and call the user back for each node. + * \param callback [in] walking callback + * \param user_data [in] callback's user data + * \return true if success + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + virtual bool Walk(GenericWalkingCallback callback, void* user_data) const = 0; + + // Data access + virtual udword GetUsedBytes() const = 0; + inline_ udword GetNbNodes() const { return mNbNodes; } + + protected: + udword mNbNodes; + }; + + class OPCODE_API AABBCollisionTree : public AABBOptimizedTree + { + IMPLEMENT_COLLISION_TREE(AABBCollisionTree, AABBCollisionNode) + }; + + class OPCODE_API AABBNoLeafTree : public AABBOptimizedTree + { + IMPLEMENT_COLLISION_TREE(AABBNoLeafTree, AABBNoLeafNode) + }; + + class OPCODE_API AABBQuantizedTree : public AABBOptimizedTree + { + IMPLEMENT_COLLISION_TREE(AABBQuantizedTree, AABBQuantizedNode) + + public: + Point mCenterCoeff; + Point mExtentsCoeff; + }; + + class OPCODE_API AABBQuantizedNoLeafTree : public AABBOptimizedTree + { + IMPLEMENT_COLLISION_TREE(AABBQuantizedNoLeafTree, AABBQuantizedNoLeafNode) + + public: + Point mCenterCoeff; + Point mExtentsCoeff; + }; + +#endif // __OPC_OPTIMIZEDTREE_H__ diff --git a/Engine/lib/opcode/OPC_Picking.cpp b/Engine/lib/opcode/OPC_Picking.cpp new file mode 100644 index 000000000..9b99fad9f --- /dev/null +++ b/Engine/lib/opcode/OPC_Picking.cpp @@ -0,0 +1,181 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code to perform "picking". + * \file OPC_Picking.cpp + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "Opcode.h" + +using namespace Opcode; + +#ifdef OPC_RAYHIT_CALLBACK + +/* + Possible RayCollider usages: + - boolean query (shadow feeler) + - closest hit + - all hits + - number of intersection (boolean) + +*/ + +bool Opcode::SetupAllHits(RayCollider& collider, CollisionFaces& contacts) +{ + struct Local + { + static void AllContacts(const CollisionFace& hit, void* user_data) + { + CollisionFaces* CF = (CollisionFaces*)user_data; + CF->AddFace(hit); + } + }; + + collider.SetFirstContact(false); + collider.SetHitCallback(Local::AllContacts); + collider.SetUserData(&contacts); + return true; +} + +bool Opcode::SetupClosestHit(RayCollider& collider, CollisionFace& closest_contact) +{ + struct Local + { + static void ClosestContact(const CollisionFace& hit, void* user_data) + { + CollisionFace* CF = (CollisionFace*)user_data; + if(hit.mDistancemDistance) *CF = hit; + } + }; + + collider.SetFirstContact(false); + collider.SetHitCallback(Local::ClosestContact); + collider.SetUserData(&closest_contact); + closest_contact.mDistance = MAX_FLOAT; + return true; +} + +bool Opcode::SetupShadowFeeler(RayCollider& collider) +{ + collider.SetFirstContact(true); + collider.SetHitCallback(null); + return true; +} + +bool Opcode::SetupInOutTest(RayCollider& collider) +{ + collider.SetFirstContact(false); + collider.SetHitCallback(null); + // Results with collider.GetNbIntersections() + return true; +} + +bool Opcode::Picking( +CollisionFace& picked_face, +const Ray& world_ray, const Model& model, const Matrix4x4* world, +float min_dist, float max_dist, const Point& view_point, CullModeCallback callback, void* user_data) +{ + struct Local + { + struct CullData + { + CollisionFace* Closest; + float MinLimit; + CullModeCallback Callback; + void* UserData; + Point ViewPoint; + const MeshInterface* IMesh; + }; + + // Called for each stabbed face + static void RenderCullingCallback(const CollisionFace& hit, void* user_data) + { + CullData* Data = (CullData*)user_data; + + // Discard face if we already have a closer hit + if(hit.mDistance>=Data->Closest->mDistance) return; + + // Discard face if hit point is smaller than min limit. This mainly happens when the face is in front + // of the near clip plane (or straddles it). If we keep the face nonetheless, the user can select an + // object that he may not even be able to see, which is very annoying. + if(hit.mDistance<=Data->MinLimit) return; + + // This is the index of currently stabbed triangle. + udword StabbedFaceIndex = hit.mFaceID; + + // We may keep it or not, depending on backface culling + bool KeepIt = true; + + // Catch *render* cull mode for this face + CullMode CM = (Data->Callback)(StabbedFaceIndex, Data->UserData); + + if(CM!=CULLMODE_NONE) // Don't even compute culling for double-sided triangles + { + // Compute backface culling for current face + + VertexPointers VP; + Data->IMesh->GetTriangle(VP, StabbedFaceIndex); + if(VP.BackfaceCulling(Data->ViewPoint)) + { + if(CM==CULLMODE_CW) KeepIt = false; + } + else + { + if(CM==CULLMODE_CCW) KeepIt = false; + } + } + + if(KeepIt) *Data->Closest = hit; + } + }; + + RayCollider RC; + RC.SetMaxDist(max_dist); + RC.SetTemporalCoherence(false); + RC.SetCulling(false); // We need all faces since some of them can be double-sided + RC.SetFirstContact(false); + RC.SetHitCallback(Local::RenderCullingCallback); + + picked_face.mFaceID = INVALID_ID_OPC; + picked_face.mDistance = MAX_FLOAT; + picked_face.mU = 0.0f; + picked_face.mV = 0.0f; + + Local::CullData Data; + Data.Closest = &picked_face; + Data.MinLimit = min_dist; + Data.Callback = callback; + Data.UserData = user_data; + Data.ViewPoint = view_point; + Data.IMesh = model.GetMeshInterface(); + + if(world) + { + // Get matrices + Matrix4x4 InvWorld; + InvertPRMatrix(InvWorld, *world); + + // Compute camera position in mesh space + Data.ViewPoint *= InvWorld; + } + + RC.SetUserData(&Data); + if(RC.Collide(world_ray, model, world)) + { + return picked_face.mFaceID!=INVALID_ID_OPC; + } + return false; +} + +#endif diff --git a/Engine/lib/opcode/OPC_Picking.h b/Engine/lib/opcode/OPC_Picking.h new file mode 100644 index 000000000..46dbbecb9 --- /dev/null +++ b/Engine/lib/opcode/OPC_Picking.h @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code to perform "picking". + * \file OPC_Picking.h + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_PICKING_H__ +#define __OPC_PICKING_H__ + +#ifdef OPC_RAYHIT_CALLBACK + + enum CullMode + { + CULLMODE_NONE = 0, + CULLMODE_CW = 1, + CULLMODE_CCW = 2 + }; + + typedef CullMode (*CullModeCallback)(udword triangle_index, void* user_data); + + OPCODE_API bool SetupAllHits (RayCollider& collider, CollisionFaces& contacts); + OPCODE_API bool SetupClosestHit (RayCollider& collider, CollisionFace& closest_contact); + OPCODE_API bool SetupShadowFeeler (RayCollider& collider); + OPCODE_API bool SetupInOutTest (RayCollider& collider); + + OPCODE_API bool Picking( + CollisionFace& picked_face, + const Ray& world_ray, const Model& model, const Matrix4x4* world, + float min_dist, float max_dist, const Point& view_point, CullModeCallback callback, void* user_data); +#endif + +#endif //__OPC_PICKING_H__ \ No newline at end of file diff --git a/Engine/lib/opcode/OPC_PlanesAABBOverlap.h b/Engine/lib/opcode/OPC_PlanesAABBOverlap.h new file mode 100644 index 000000000..5d7576e50 --- /dev/null +++ b/Engine/lib/opcode/OPC_PlanesAABBOverlap.h @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Planes-AABB overlap test. + * - original code by Ville Miettinen, from Umbra/dPVS (released on the GD-Algorithms mailing list) + * - almost used "as-is", I even left the comments (hence the frustum-related notes) + * + * \param center [in] box center + * \param extents [in] box extents + * \param out_clip_mask [out] bitmask for active planes + * \param in_clip_mask [in] bitmask for active planes + * \return TRUE if boxes overlap planes + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ BOOL PlanesCollider::PlanesAABBOverlap(const Point& center, const Point& extents, udword& out_clip_mask, udword in_clip_mask) +{ + // Stats + mNbVolumeBVTests++; + + const Plane* p = mPlanes; + + // Evaluate through all active frustum planes. We determine the relation + // between the AABB and a plane by using the concept of "near" and "far" + // vertices originally described by Zhang (and later by Möller). Our + // variant here uses 3 fabs ops, 6 muls, 7 adds and two floating point + // comparisons per plane. The routine early-exits if the AABB is found + // to be outside any of the planes. The loop also constructs a new output + // clip mask. Most FPUs have a native single-cycle fabsf() operation. + + udword Mask = 1; // current mask index (1,2,4,8,..) + udword TmpOutClipMask = 0; // initialize output clip mask into empty. + + while(Mask<=in_clip_mask) // keep looping while we have active planes left... + { + if(in_clip_mask & Mask) // if clip plane is active, process it.. + { + float NP = extents.x*fabsf(p->n.x) + extents.y*fabsf(p->n.y) + extents.z*fabsf(p->n.z); // ### fabsf could be precomputed + float MP = center.x*p->n.x + center.y*p->n.y + center.z*p->n.z + p->d; + + if(NP < MP) // near vertex behind the clip plane... + return FALSE; // .. so there is no intersection.. + if((-NP) < MP) // near and far vertices on different sides of plane.. + TmpOutClipMask |= Mask; // .. so update the clip mask... + } + Mask+=Mask; // mk = (1<Add(prim_index); + +//! Planes-triangle test +#define PLANES_PRIM(prim_index, flag) \ + /* Request vertices from the app */ \ + mIMesh->GetTriangle(mVP, prim_index); \ + /* Perform triangle-box overlap test */ \ + if(PlanesTriOverlap(clip_mask)) \ + { \ + SET_CONTACT(prim_index, flag) \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +PlanesCollider::PlanesCollider() : + mPlanes (null), + mNbPlanes (0) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +PlanesCollider::~PlanesCollider() +{ + DELETEARRAY(mPlanes); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Validates current settings. You should call this method after all the settings and callbacks have been defined. + * \return null if everything is ok, else a string describing the problem + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +const char* PlanesCollider::ValidateSettings() +{ + if(TemporalCoherenceEnabled() && !FirstContactEnabled()) return "Temporal coherence only works with ""First contact"" mode!"; + + return VolumeCollider::ValidateSettings(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Generic collision query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - with GetNbTouchedPrimitives() + * - with GetTouchedPrimitives() + * + * \param cache [in/out] a planes cache + * \param planes [in] list of planes in world space + * \param nb_planes [in] number of planes + * \param model [in] Opcode model to collide with + * \param worldm [in] model's world matrix, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool PlanesCollider::Collide(PlanesCache& cache, const Plane* planes, udword nb_planes, const Model& model, const Matrix4x4* worldm) +{ + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, planes, nb_planes, worldm)) return true; + + udword PlaneMask = (1<mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask); + else _Collide(Tree->GetNodes(), PlaneMask); + } + else + { + const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree(); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask); + else _Collide(Tree->GetNodes(), PlaneMask); + } + } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask); + else _Collide(Tree->GetNodes(), PlaneMask); + } + else + { + const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree(); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask); + else _Collide(Tree->GetNodes(), PlaneMask); + } + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Initializes a collision query : + * - reset stats & contact status + * - compute planes in model space + * - check temporal coherence + * + * \param cache [in/out] a planes cache + * \param planes [in] list of planes + * \param nb_planes [in] number of planes + * \param worldm [in] model's world matrix, or null + * \return TRUE if we can return immediately + * \warning SCALE NOT SUPPORTED. The matrix must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +BOOL PlanesCollider::InitQuery(PlanesCache& cache, const Plane* planes, udword nb_planes, const Matrix4x4* worldm) +{ + // 1) Call the base method + VolumeCollider::InitQuery(); + + // 2) Compute planes in model space + if(nb_planes>mNbPlanes) + { + DELETEARRAY(mPlanes); + mPlanes = new Plane[nb_planes]; + } + mNbPlanes = nb_planes; + + if(worldm) + { + Matrix4x4 InvWorldM; + InvertPRMatrix(InvWorldM, *worldm); + +// for(udword i=0;iHasSingleNode()) + { + if(!SkipPrimitiveTests()) + { + // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0. + mTouchedPrimitives->Reset(); + + // Perform overlap test between the unique triangle and the planes (and set contact status if needed) + udword clip_mask = (1< check results from previous frame before performing the collision query + if(FirstContactEnabled()) + { + // We're only interested in the first contact found => test the unique previously touched face + if(mTouchedPrimitives->GetNbEntries()) + { + // Get index of previously touched face = the first entry in the array + udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0); + + // Then reset the array: + // - if the overlap test below is successful, the index we'll get added back anyway + // - if it isn't, then the array should be reset anyway for the normal query + mTouchedPrimitives->Reset(); + + // Perform overlap test between the cached triangle and the planes (and set contact status if needed) + udword clip_mask = (1< we'll have to perform a normal query + } + else mTouchedPrimitives->Reset(); + } + else + { + // Here we don't use temporal coherence => do a normal query + mTouchedPrimitives->Reset(); + } + + return FALSE; +} + +#define TEST_CLIP_MASK \ + /* If the box is completely included, so are its children. We don't need to do extra tests, we */ \ + /* can immediately output a list of visible children. Those ones won't need to be clipped. */ \ + if(!OutClipMask) \ + { \ + /* Set contact status */ \ + mFlags |= OPC_CONTACT; \ + _Dump(node); \ + return; \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void PlanesCollider::_Collide(const AABBCollisionNode* node, udword clip_mask) +{ + // Test the box against the planes. If the box is completely culled, so are its children, hence we exit. + udword OutClipMask; + if(!PlanesAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents, OutClipMask, clip_mask)) return; + + TEST_CLIP_MASK + + // Else the box straddles one or several planes, so we need to recurse down the tree. + if(node->IsLeaf()) + { + PLANES_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _Collide(node->GetPos(), OutClipMask); + + if(ContactFound()) return; + + _Collide(node->GetNeg(), OutClipMask); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void PlanesCollider::_CollideNoPrimitiveTest(const AABBCollisionNode* node, udword clip_mask) +{ + // Test the box against the planes. If the box is completely culled, so are its children, hence we exit. + udword OutClipMask; + if(!PlanesAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents, OutClipMask, clip_mask)) return; + + TEST_CLIP_MASK + + // Else the box straddles one or several planes, so we need to recurse down the tree. + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos(), OutClipMask); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg(), OutClipMask); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void PlanesCollider::_Collide(const AABBQuantizedNode* node, udword clip_mask) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Test the box against the planes. If the box is completely culled, so are its children, hence we exit. + udword OutClipMask; + if(!PlanesAABBOverlap(Center, Extents, OutClipMask, clip_mask)) return; + + TEST_CLIP_MASK + + // Else the box straddles one or several planes, so we need to recurse down the tree. + if(node->IsLeaf()) + { + PLANES_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _Collide(node->GetPos(), OutClipMask); + + if(ContactFound()) return; + + _Collide(node->GetNeg(), OutClipMask); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void PlanesCollider::_CollideNoPrimitiveTest(const AABBQuantizedNode* node, udword clip_mask) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Test the box against the planes. If the box is completely culled, so are its children, hence we exit. + udword OutClipMask; + if(!PlanesAABBOverlap(Center, Extents, OutClipMask, clip_mask)) return; + + TEST_CLIP_MASK + + // Else the box straddles one or several planes, so we need to recurse down the tree. + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos(), OutClipMask); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg(), OutClipMask); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void PlanesCollider::_Collide(const AABBNoLeafNode* node, udword clip_mask) +{ + // Test the box against the planes. If the box is completely culled, so are its children, hence we exit. + udword OutClipMask; + if(!PlanesAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents, OutClipMask, clip_mask)) return; + + TEST_CLIP_MASK + + // Else the box straddles one or several planes, so we need to recurse down the tree. + if(node->HasPosLeaf()) { PLANES_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } + else _Collide(node->GetPos(), OutClipMask); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { PLANES_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } + else _Collide(node->GetNeg(), OutClipMask); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void PlanesCollider::_CollideNoPrimitiveTest(const AABBNoLeafNode* node, udword clip_mask) +{ + // Test the box against the planes. If the box is completely culled, so are its children, hence we exit. + udword OutClipMask; + if(!PlanesAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents, OutClipMask, clip_mask)) return; + + TEST_CLIP_MASK + + // Else the box straddles one or several planes, so we need to recurse down the tree. + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos(), OutClipMask); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg(), OutClipMask); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void PlanesCollider::_Collide(const AABBQuantizedNoLeafNode* node, udword clip_mask) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Test the box against the planes. If the box is completely culled, so are its children, hence we exit. + udword OutClipMask; + if(!PlanesAABBOverlap(Center, Extents, OutClipMask, clip_mask)) return; + + TEST_CLIP_MASK + + // Else the box straddles one or several planes, so we need to recurse down the tree. + if(node->HasPosLeaf()) { PLANES_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } + else _Collide(node->GetPos(), OutClipMask); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { PLANES_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } + else _Collide(node->GetNeg(), OutClipMask); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void PlanesCollider::_CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node, udword clip_mask) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Test the box against the planes. If the box is completely culled, so are its children, hence we exit. + udword OutClipMask; + if(!PlanesAABBOverlap(Center, Extents, OutClipMask, clip_mask)) return; + + TEST_CLIP_MASK + + // Else the box straddles one or several planes, so we need to recurse down the tree. + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos(), OutClipMask); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg(), OutClipMask); +} + + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridPlanesCollider::HybridPlanesCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridPlanesCollider::~HybridPlanesCollider() +{ +} + +bool HybridPlanesCollider::Collide(PlanesCache& cache, const Plane* planes, udword nb_planes, const HybridModel& model, const Matrix4x4* worldm) +{ + // We don't want primitive tests here! + mFlags |= OPC_NO_PRIMITIVE_TESTS; + + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, planes, nb_planes, worldm)) return true; + + // Special case for 1-leaf trees + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles + udword Nb = mIMesh->GetNbTriangles(); + + // Loop through all triangles + udword clip_mask = (1<mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask); + } + else + { + const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree(); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask); + } + } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask); + } + else + { + const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree(); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes(), PlaneMask); + } + } + + // We only have a list of boxes so far + if(GetContactStatus()) + { + // Reset contact status, since it currently only reflects collisions with leaf boxes + Collider::InitQuery(); + + // Change dest container so that we can use built-in overlap tests and get collided primitives + cache.TouchedPrimitives.Reset(); + mTouchedPrimitives = &cache.TouchedPrimitives; + + // Read touched leaf boxes + udword Nb = mTouchedBoxes.GetNbEntries(); + const udword* Touched = mTouchedBoxes.GetEntries(); + + const LeafTriangles* LT = model.GetLeafTriangles(); + const udword* Indices = model.GetIndices(); + + // Loop through touched leaves + udword clip_mask = (1<Distance(*mVP.Vertex[0]); + float d1 = p->Distance(*mVP.Vertex[1]); + float d2 = p->Distance(*mVP.Vertex[2]); + if(d0>0.0f && d1>0.0f && d2>0.0f) return FALSE; +// if(!(IR(d0)&SIGN_BITMASK) && !(IR(d1)&SIGN_BITMASK) && !(IR(d2)&SIGN_BITMASK)) return FALSE; + } + Mask+=Mask; + p++; + } +/* + for(udword i=0;i<6;i++) + { + float d0 = p[i].Distance(mLeafVerts[0]); + float d1 = p[i].Distance(mLeafVerts[1]); + float d2 = p[i].Distance(mLeafVerts[2]); + if(d0>0.0f && d1>0.0f && d2>0.0f) return false; + } +*/ + return TRUE; +} diff --git a/Engine/lib/opcode/OPC_RayAABBOverlap.h b/Engine/lib/opcode/OPC_RayAABBOverlap.h new file mode 100644 index 000000000..a8162bf03 --- /dev/null +++ b/Engine/lib/opcode/OPC_RayAABBOverlap.h @@ -0,0 +1,63 @@ +// Opcode 1.1: ray-AABB overlap tests based on Woo's code +// Opcode 1.2: ray-AABB overlap tests based on the separating axis theorem +// +// The point of intersection is not computed anymore. The distance to impact is not needed anymore +// since we now have two different queries for segments or rays. + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes a segment-AABB overlap test using the separating axis theorem. Segment is cached within the class. + * \param center [in] AABB center + * \param extents [in] AABB extents + * \return true on overlap + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ BOOL RayCollider::SegmentAABBOverlap(const Point& center, const Point& extents) +{ + // Stats + mNbRayBVTests++; + + float Dx = mData2.x - center.x; if(fabsf(Dx) > extents.x + mFDir.x) return FALSE; + float Dy = mData2.y - center.y; if(fabsf(Dy) > extents.y + mFDir.y) return FALSE; + float Dz = mData2.z - center.z; if(fabsf(Dz) > extents.z + mFDir.z) return FALSE; + + float f; + f = mData.y * Dz - mData.z * Dy; if(fabsf(f) > extents.y*mFDir.z + extents.z*mFDir.y) return FALSE; + f = mData.z * Dx - mData.x * Dz; if(fabsf(f) > extents.x*mFDir.z + extents.z*mFDir.x) return FALSE; + f = mData.x * Dy - mData.y * Dx; if(fabsf(f) > extents.x*mFDir.y + extents.y*mFDir.x) return FALSE; + + return TRUE; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes a ray-AABB overlap test using the separating axis theorem. Ray is cached within the class. + * \param center [in] AABB center + * \param extents [in] AABB extents + * \return true on overlap + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ BOOL RayCollider::RayAABBOverlap(const Point& center, const Point& extents) +{ + // Stats + mNbRayBVTests++; + +// float Dx = mOrigin.x - center.x; if(fabsf(Dx) > extents.x && Dx*mDir.x>=0.0f) return FALSE; +// float Dy = mOrigin.y - center.y; if(fabsf(Dy) > extents.y && Dy*mDir.y>=0.0f) return FALSE; +// float Dz = mOrigin.z - center.z; if(fabsf(Dz) > extents.z && Dz*mDir.z>=0.0f) return FALSE; + + float Dx = mOrigin.x - center.x; if(GREATER(Dx, extents.x) && Dx*mDir.x>=0.0f) return FALSE; + float Dy = mOrigin.y - center.y; if(GREATER(Dy, extents.y) && Dy*mDir.y>=0.0f) return FALSE; + float Dz = mOrigin.z - center.z; if(GREATER(Dz, extents.z) && Dz*mDir.z>=0.0f) return FALSE; + +// float Dx = mOrigin.x - center.x; if(GREATER(Dx, extents.x) && ((SIR(Dx)-1)^SIR(mDir.x))>=0.0f) return FALSE; +// float Dy = mOrigin.y - center.y; if(GREATER(Dy, extents.y) && ((SIR(Dy)-1)^SIR(mDir.y))>=0.0f) return FALSE; +// float Dz = mOrigin.z - center.z; if(GREATER(Dz, extents.z) && ((SIR(Dz)-1)^SIR(mDir.z))>=0.0f) return FALSE; + + float f; + f = mDir.y * Dz - mDir.z * Dy; if(fabsf(f) > extents.y*mFDir.z + extents.z*mFDir.y) return FALSE; + f = mDir.z * Dx - mDir.x * Dz; if(fabsf(f) > extents.x*mFDir.z + extents.z*mFDir.x) return FALSE; + f = mDir.x * Dy - mDir.y * Dx; if(fabsf(f) > extents.x*mFDir.y + extents.y*mFDir.x) return FALSE; + + return TRUE; +} diff --git a/Engine/lib/opcode/OPC_RayCollider.cpp b/Engine/lib/opcode/OPC_RayCollider.cpp new file mode 100644 index 000000000..77799ebcd --- /dev/null +++ b/Engine/lib/opcode/OPC_RayCollider.cpp @@ -0,0 +1,761 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for a ray collider. + * \file OPC_RayCollider.cpp + * \author Pierre Terdiman + * \date June, 2, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a ray-vs-tree collider. + * This class performs a stabbing query on an AABB tree, i.e. does a ray-mesh collision. + * + * HIGHER DISTANCE BOUND: + * + * If P0 and P1 are two 3D points, let's define: + * - d = distance between P0 and P1 + * - Origin = P0 + * - Direction = (P1 - P0) / d = normalized direction vector + * - A parameter t such as a point P on the line (P0,P1) is P = Origin + t * Direction + * - t = 0 --> P = P0 + * - t = d --> P = P1 + * + * Then we can define a general "ray" as: + * + * struct Ray + * { + * Point Origin; + * Point Direction; + * }; + * + * But it actually maps three different things: + * - a segment, when 0 <= t <= d + * - a half-line, when 0 <= t < +infinity, or -infinity < t <= d + * - a line, when -infinity < t < +infinity + * + * In Opcode, we support segment queries, which yield half-line queries by setting d = +infinity. + * We don't support line-queries. If you need them, shift the origin along the ray by an appropriate margin. + * + * In short, the lower bound is always 0, and you can setup the higher bound "d" with RayCollider::SetMaxDist(). + * + * Query |segment |half-line |line + * --------|-------------------|---------------|---------------- + * Usages |-shadow feelers |-raytracing |- + * |-sweep tests |-in/out tests | + * + * FIRST CONTACT: + * + * - You can setup "first contact" mode or "all contacts" mode with RayCollider::SetFirstContact(). + * - In "first contact" mode we return as soon as the ray hits one face. If can be useful e.g. for shadow feelers, where + * you want to know whether the path to the light is free or not (a boolean answer is enough). + * - In "all contacts" mode we return all faces hit by the ray. + * + * TEMPORAL COHERENCE: + * + * - You can enable or disable temporal coherence with RayCollider::SetTemporalCoherence(). + * - It currently only works in "first contact" mode. + * - If temporal coherence is enabled, the previously hit triangle is cached during the first query. Then, next queries + * start by colliding the ray against the cached triangle. If they still collide, we return immediately. + * + * CLOSEST HIT: + * + * - You can enable or disable "closest hit" with RayCollider::SetClosestHit(). + * - It currently only works in "all contacts" mode. + * - If closest hit is enabled, faces are sorted by distance on-the-fly and the closest one only is reported. + * + * BACKFACE CULLING: + * + * - You can enable or disable backface culling with RayCollider::SetCulling(). + * - If culling is enabled, ray will not hit back faces (only front faces). + * + * + * + * \class RayCollider + * \author Pierre Terdiman + * \version 1.3 + * \date June, 2, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * This class describes a face hit by a ray or segment. + * This is a particular class dedicated to stabbing queries. + * + * \class CollisionFace + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * This class is a dedicated collection of CollisionFace. + * + * \class CollisionFaces + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "Opcode.h" + +using namespace Opcode; + +#include "OPC_RayAABBOverlap.h" +#include "OPC_RayTriOverlap.h" + +#define SET_CONTACT(prim_index, flag) \ + mNbIntersections++; \ + /* Set contact status */ \ + mFlags |= flag; \ + /* In any case the contact has been found and recorded in mStabbedFace */ \ + mStabbedFace.mFaceID = prim_index; + +#ifdef OPC_RAYHIT_CALLBACK + + #define HANDLE_CONTACT(prim_index, flag) \ + SET_CONTACT(prim_index, flag) \ + \ + if(mHitCallback) (mHitCallback)(mStabbedFace, mUserData); + + #define UPDATE_CACHE \ + if(cache && GetContactStatus()) \ + { \ + *cache = mStabbedFace.mFaceID; \ + } +#else + + #define HANDLE_CONTACT(prim_index, flag) \ + SET_CONTACT(prim_index, flag) \ + \ + /* Now we can also record it in mStabbedFaces if available */ \ + if(mStabbedFaces) \ + { \ + /* If we want all faces or if that's the first one we hit */ \ + if(!mClosestHit || !mStabbedFaces->GetNbFaces()) \ + { \ + mStabbedFaces->AddFace(mStabbedFace); \ + } \ + else \ + { \ + /* We only keep closest hit */ \ + CollisionFace* Current = const_cast(mStabbedFaces->GetFaces()); \ + if(Current && mStabbedFace.mDistancemDistance) \ + { \ + *Current = mStabbedFace; \ + } \ + } \ + } + + #define UPDATE_CACHE \ + if(cache && GetContactStatus() && mStabbedFaces) \ + { \ + const CollisionFace* Current = mStabbedFaces->GetFaces(); \ + if(Current) *cache = Current->mFaceID; \ + else *cache = INVALID_ID_OPC; \ + } +#endif + +#define SEGMENT_PRIM(prim_index, flag) \ + /* Request vertices from the app */ \ + VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \ + \ + /* Perform ray-tri overlap test and return */ \ + if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \ + { \ + /* Intersection point is valid if dist < segment's length */ \ + /* We know dist>0 so we can use integers */ \ + if(IR(mStabbedFace.mDistance)GetTriangle(VP, prim_index); \ + \ + /* Perform ray-tri overlap test and return */ \ + if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \ + { \ + HANDLE_CONTACT(prim_index, flag) \ + } + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +RayCollider::RayCollider() : + mNbRayBVTests (0), + mNbRayPrimTests (0), + mNbIntersections (0), + mCulling (true), +#ifdef OPC_RAYHIT_CALLBACK + mHitCallback (null), + mUserData (0), +#else + mClosestHit (false), + mStabbedFaces (null), +#endif + mMaxDist (MAX_FLOAT) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +RayCollider::~RayCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Validates current settings. You should call this method after all the settings and callbacks have been defined. + * \return null if everything is ok, else a string describing the problem + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +const char* RayCollider::ValidateSettings() +{ + if(mMaxDist<0.0f) return "Higher distance bound must be positive!"; + if(TemporalCoherenceEnabled() && !FirstContactEnabled()) return "Temporal coherence only works with ""First contact"" mode!"; +#ifndef OPC_RAYHIT_CALLBACK + if(mClosestHit && FirstContactEnabled()) return "Closest hit doesn't work with ""First contact"" mode!"; + if(TemporalCoherenceEnabled() && mClosestHit) return "Temporal coherence can't guarantee to report closest hit!"; +#endif + if(SkipPrimitiveTests()) return "SkipPrimitiveTests not possible for RayCollider ! (not implemented)"; + return null; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Generic stabbing query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - in the user-provided destination array + * + * \param world_ray [in] stabbing ray in world space + * \param model [in] Opcode model to collide with + * \param world [in] model's world matrix, or null + * \param cache [in] a possibly cached face index, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool RayCollider::Collide(const Ray& world_ray, const Model& model, const Matrix4x4* world, udword* cache) +{ + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(world_ray, world, cache)) return true; + + if(!model.HasLeafNodes()) + { + if(model.IsQuantized()) + { + const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform stabbing query + if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes()); + else _RayStab(Tree->GetNodes()); + } + else + { + const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree(); + + // Perform stabbing query + if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes()); + else _RayStab(Tree->GetNodes()); + } + } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform stabbing query + if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes()); + else _RayStab(Tree->GetNodes()); + } + else + { + const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree(); + + // Perform stabbing query + if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(Tree->GetNodes()); + else _RayStab(Tree->GetNodes()); + } + } + + // Update cache if needed + UPDATE_CACHE + return true; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Initializes a stabbing query : + * - reset stats & contact status + * - compute ray in local space + * - check temporal coherence + * + * \param world_ray [in] stabbing ray in world space + * \param world [in] object's world matrix, or null + * \param face_id [in] index of previously stabbed triangle + * \return TRUE if we can return immediately + * \warning SCALE NOT SUPPORTED. The matrix must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +BOOL RayCollider::InitQuery(const Ray& world_ray, const Matrix4x4* world, udword* face_id) +{ + // Reset stats & contact status + Collider::InitQuery(); + mNbRayBVTests = 0; + mNbRayPrimTests = 0; + mNbIntersections = 0; +#ifndef OPC_RAYHIT_CALLBACK + if(mStabbedFaces) mStabbedFaces->Reset(); +#endif + + // Compute ray in local space + // The (Origin/Dir) form is needed for the ray-triangle test anyway (even for segment tests) + if(world) + { + Matrix3x3 InvWorld = *world; + mDir = InvWorld * world_ray.mDir; + + Matrix4x4 World; + InvertPRMatrix(World, *world); + mOrigin = world_ray.mOrig * World; + } + else + { + mDir = world_ray.mDir; + mOrigin = world_ray.mOrig; + } + + // 4) Special case: 1-triangle meshes [Opcode 1.3] + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0. + if(!SkipPrimitiveTests()) + { + // Perform overlap test between the unique triangle and the ray (and set contact status if needed) + SEGMENT_PRIM(udword(0), OPC_CONTACT) + + // Return immediately regardless of status + return TRUE; + } + } + + // Check temporal coherence : + + // Test previously colliding primitives first + if(TemporalCoherenceEnabled() && FirstContactEnabled() && face_id && *face_id!=INVALID_ID_OPC) + { +#ifdef OLD_CODE +#ifndef OPC_RAYHIT_CALLBACK + if(!mClosestHit) +#endif + { + // Request vertices from the app + VertexPointers VP; + mIMesh->GetTriangle(VP, *face_id); + // Perform ray-cached tri overlap test + if(RayTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) + { + // Intersection point is valid if: + // - distance is positive (else it can just be a face behind the orig point) + // - distance is smaller than a given max distance (useful for shadow feelers) +// if(mStabbedFace.mDistance>0.0f && mStabbedFace.mDistanceAddFace(mStabbedFace); +#endif + return TRUE; + } + } + } +#else + // New code + // We handle both Segment/ray queries with the same segment code, and a possible infinite limit + SEGMENT_PRIM(*face_id, OPC_TEMPORAL_CONTACT) + + // Return immediately if possible + if(GetContactStatus()) return TRUE; +#endif + } + + // Precompute data (moved after temporal coherence since only needed for ray-AABB) + if(IR(mMaxDist)!=IEEE_MAX_FLOAT) + { + // For Segment-AABB overlap + mData = 0.5f * mDir * mMaxDist; + mData2 = mOrigin + mData; + + // Precompute mFDir; + mFDir.x = fabsf(mData.x); + mFDir.y = fabsf(mData.y); + mFDir.z = fabsf(mData.z); + } + else + { + // For Ray-AABB overlap +// udword x = SIR(mDir.x)-1; +// udword y = SIR(mDir.y)-1; +// udword z = SIR(mDir.z)-1; +// mData.x = FR(x); +// mData.y = FR(y); +// mData.z = FR(z); + + // Precompute mFDir; + mFDir.x = fabsf(mDir.x); + mFDir.y = fabsf(mDir.y); + mFDir.z = fabsf(mDir.z); + } + + return FALSE; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Stabbing query for vanilla AABB trees. + * \param world_ray [in] stabbing ray in world space + * \param tree [in] AABB tree + * \param box_indices [out] indices of stabbed boxes + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool RayCollider::Collide(const Ray& world_ray, const AABBTree* tree, OPC_Container& box_indices) +{ + // ### bad design here + + // This is typically called for a scene tree, full of -AABBs-, not full of triangles. + // So we don't really have "primitives" to deal with. Hence it doesn't work with + // "FirstContact" + "TemporalCoherence". + ASSERT( !(FirstContactEnabled() && TemporalCoherenceEnabled()) ); + + // Checkings + if(!tree) return false; + + // Init collision query + // Basically this is only called to initialize precomputed data + if(InitQuery(world_ray)) return true; + + // Perform stabbing query + if(IR(mMaxDist)!=IEEE_MAX_FLOAT) _SegmentStab(tree, box_indices); + else _RayStab(tree, box_indices); + + return true; +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive stabbing query for normal AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void RayCollider::_SegmentStab(const AABBCollisionNode* node) +{ + // Perform Segment-AABB overlap test + if(!SegmentAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + if(node->IsLeaf()) + { + SEGMENT_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _SegmentStab(node->GetPos()); + + if(ContactFound()) return; + + _SegmentStab(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive stabbing query for quantized AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void RayCollider::_SegmentStab(const AABBQuantizedNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform Segment-AABB overlap test + if(!SegmentAABBOverlap(Center, Extents)) return; + + if(node->IsLeaf()) + { + SEGMENT_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _SegmentStab(node->GetPos()); + + if(ContactFound()) return; + + _SegmentStab(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive stabbing query for no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void RayCollider::_SegmentStab(const AABBNoLeafNode* node) +{ + // Perform Segment-AABB overlap test + if(!SegmentAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + if(node->HasPosLeaf()) + { + SEGMENT_PRIM(node->GetPosPrimitive(), OPC_CONTACT) + } + else _SegmentStab(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) + { + SEGMENT_PRIM(node->GetNegPrimitive(), OPC_CONTACT) + } + else _SegmentStab(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive stabbing query for quantized no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void RayCollider::_SegmentStab(const AABBQuantizedNoLeafNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform Segment-AABB overlap test + if(!SegmentAABBOverlap(Center, Extents)) return; + + if(node->HasPosLeaf()) + { + SEGMENT_PRIM(node->GetPosPrimitive(), OPC_CONTACT) + } + else _SegmentStab(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) + { + SEGMENT_PRIM(node->GetNegPrimitive(), OPC_CONTACT) + } + else _SegmentStab(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive stabbing query for vanilla AABB trees. + * \param node [in] current collision node + * \param box_indices [out] indices of stabbed boxes + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void RayCollider::_SegmentStab(const AABBTreeNode* node, OPC_Container& box_indices) +{ + // Test the box against the segment + Point Center, Extents; + node->GetAABB()->GetCenter(Center); + node->GetAABB()->GetExtents(Extents); + if(!SegmentAABBOverlap(Center, Extents)) return; + + if(node->IsLeaf()) + { + box_indices.Add(node->GetPrimitives(), node->GetNbPrimitives()); + } + else + { + _SegmentStab(node->GetPos(), box_indices); + _SegmentStab(node->GetNeg(), box_indices); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive stabbing query for normal AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void RayCollider::_RayStab(const AABBCollisionNode* node) +{ + // Perform Ray-AABB overlap test + if(!RayAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + if(node->IsLeaf()) + { + RAY_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _RayStab(node->GetPos()); + + if(ContactFound()) return; + + _RayStab(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive stabbing query for quantized AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void RayCollider::_RayStab(const AABBQuantizedNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform Ray-AABB overlap test + if(!RayAABBOverlap(Center, Extents)) return; + + if(node->IsLeaf()) + { + RAY_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _RayStab(node->GetPos()); + + if(ContactFound()) return; + + _RayStab(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive stabbing query for no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void RayCollider::_RayStab(const AABBNoLeafNode* node) +{ + // Perform Ray-AABB overlap test + if(!RayAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + if(node->HasPosLeaf()) + { + RAY_PRIM(node->GetPosPrimitive(), OPC_CONTACT) + } + else _RayStab(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) + { + RAY_PRIM(node->GetNegPrimitive(), OPC_CONTACT) + } + else _RayStab(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive stabbing query for quantized no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void RayCollider::_RayStab(const AABBQuantizedNoLeafNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform Ray-AABB overlap test + if(!RayAABBOverlap(Center, Extents)) return; + + if(node->HasPosLeaf()) + { + RAY_PRIM(node->GetPosPrimitive(), OPC_CONTACT) + } + else _RayStab(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) + { + RAY_PRIM(node->GetNegPrimitive(), OPC_CONTACT) + } + else _RayStab(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive stabbing query for vanilla AABB trees. + * \param node [in] current collision node + * \param box_indices [out] indices of stabbed boxes + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void RayCollider::_RayStab(const AABBTreeNode* node, OPC_Container& box_indices) +{ + // Test the box against the ray + Point Center, Extents; + node->GetAABB()->GetCenter(Center); + node->GetAABB()->GetExtents(Extents); + if(!RayAABBOverlap(Center, Extents)) return; + + if(node->IsLeaf()) + { + mFlags |= OPC_CONTACT; + box_indices.Add(node->GetPrimitives(), node->GetNbPrimitives()); + } + else + { + _RayStab(node->GetPos(), box_indices); + _RayStab(node->GetNeg(), box_indices); + } +} diff --git a/Engine/lib/opcode/OPC_RayCollider.h b/Engine/lib/opcode/OPC_RayCollider.h new file mode 100644 index 000000000..e3931b03a --- /dev/null +++ b/Engine/lib/opcode/OPC_RayCollider.h @@ -0,0 +1,225 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for a ray collider. + * \file OPC_RayCollider.h + * \author Pierre Terdiman + * \date June, 2, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_RAYCOLLIDER_H__ +#define __OPC_RAYCOLLIDER_H__ + + class OPCODE_API CollisionFace + { + public: + //! Constructor + inline_ CollisionFace() {} + //! Destructor + inline_ ~CollisionFace() {} + + udword mFaceID; //!< Index of touched face + float mDistance; //!< Distance from collider to hitpoint + float mU, mV; //!< Impact barycentric coordinates + }; + + class OPCODE_API CollisionFaces : private OPC_Container + { + public: + //! Constructor + CollisionFaces() {} + //! Destructor + ~CollisionFaces() {} + + inline_ udword GetNbFaces() const { return GetNbEntries()>>2; } + inline_ const CollisionFace* GetFaces() const { return (const CollisionFace*)GetEntries(); } + + inline_ void Reset() { OPC_Container::Reset(); } + + inline_ void AddFace(const CollisionFace& face) { Add(face.mFaceID).Add(face.mDistance).Add(face.mU).Add(face.mV); } + }; + +#ifdef OPC_RAYHIT_CALLBACK + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * User-callback, called by OPCODE to record a hit. + * \param hit [in] current hit + * \param user_data [in] user-defined data from SetCallback() + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + typedef void (*HitCallback) (const CollisionFace& hit, void* user_data); +#endif + + class OPCODE_API RayCollider : public Collider + { + public: + // Constructor / Destructor + RayCollider(); + virtual ~RayCollider(); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Generic stabbing query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - in the user-provided destination array + * + * \param world_ray [in] stabbing ray in world space + * \param model [in] Opcode model to collide with + * \param world [in] model's world matrix, or null + * \param cache [in] a possibly cached face index, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool Collide(const Ray& world_ray, const Model& model, const Matrix4x4* world=null, udword* cache=null); + // + bool Collide(const Ray& world_ray, const AABBTree* tree, OPC_Container& box_indices); + // Settings + +#ifndef OPC_RAYHIT_CALLBACK + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Settings: enable or disable "closest hit" mode. + * \param flag [in] true to report closest hit only + * \see SetCulling(bool flag) + * \see SetMaxDist(float max_dist) + * \see SetDestination(StabbedFaces* sf) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetClosestHit(bool flag) { mClosestHit = flag; } +#endif + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Settings: enable or disable backface culling. + * \param flag [in] true to enable backface culling + * \see SetClosestHit(bool flag) + * \see SetMaxDist(float max_dist) + * \see SetDestination(StabbedFaces* sf) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetCulling(bool flag) { mCulling = flag; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Settings: sets the higher distance bound. + * \param max_dist [in] higher distance bound. Default = maximal value, for ray queries (else segment) + * \see SetClosestHit(bool flag) + * \see SetCulling(bool flag) + * \see SetDestination(StabbedFaces* sf) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetMaxDist(float max_dist=MAX_FLOAT) { mMaxDist = max_dist; } + +#ifdef OPC_RAYHIT_CALLBACK + inline_ void SetHitCallback(HitCallback cb) { mHitCallback = cb; } + inline_ void SetUserData(void* user_data) { mUserData = user_data; } +#else + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Settings: sets the destination array for stabbed faces. + * \param cf [in] destination array, filled during queries + * \see SetClosestHit(bool flag) + * \see SetCulling(bool flag) + * \see SetMaxDist(float max_dist) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetDestination(CollisionFaces* cf) { mStabbedFaces = cf; } +#endif + // Stats + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Stats: gets the number of Ray-BV overlap tests after a collision query. + * \see GetNbRayPrimTests() + * \see GetNbIntersections() + * \return the number of Ray-BV tests performed during last query + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetNbRayBVTests() const { return mNbRayBVTests; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Stats: gets the number of Ray-Triangle overlap tests after a collision query. + * \see GetNbRayBVTests() + * \see GetNbIntersections() + * \return the number of Ray-Triangle tests performed during last query + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetNbRayPrimTests() const { return mNbRayPrimTests; } + + // In-out test + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Stats: gets the number of intersection found after a collision query. Can be used for in/out tests. + * \see GetNbRayBVTests() + * \see GetNbRayPrimTests() + * \return the number of valid intersections during last query + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetNbIntersections() const { return mNbIntersections; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Validates current settings. You should call this method after all the settings and callbacks have been defined for a collider. + * \return null if everything is ok, else a string describing the problem + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + override(Collider) const char* ValidateSettings(); + + protected: + // Ray in local space + Point mOrigin; //!< Ray origin + Point mDir; //!< Ray direction (normalized) + Point mFDir; //!< fabsf(mDir) + Point mData, mData2; + // Stabbed faces + CollisionFace mStabbedFace; //!< Current stabbed face +#ifdef OPC_RAYHIT_CALLBACK + HitCallback mHitCallback; //!< Callback used to record a hit + void* mUserData; //!< User-defined data +#else + CollisionFaces* mStabbedFaces; //!< List of stabbed faces +#endif + // Stats + udword mNbRayBVTests; //!< Number of Ray-BV tests + udword mNbRayPrimTests; //!< Number of Ray-Primitive tests + // In-out test + udword mNbIntersections; //!< Number of valid intersections + // Dequantization coeffs + Point mCenterCoeff; + Point mExtentsCoeff; + // Settings + float mMaxDist; //!< Valid segment on the ray +#ifndef OPC_RAYHIT_CALLBACK + bool mClosestHit; //!< Report closest hit only +#endif + bool mCulling; //!< Stab culled faces or not + // Internal methods + void _SegmentStab(const AABBCollisionNode* node); + void _SegmentStab(const AABBNoLeafNode* node); + void _SegmentStab(const AABBQuantizedNode* node); + void _SegmentStab(const AABBQuantizedNoLeafNode* node); + void _SegmentStab(const AABBTreeNode* node, OPC_Container& box_indices); + void _RayStab(const AABBCollisionNode* node); + void _RayStab(const AABBNoLeafNode* node); + void _RayStab(const AABBQuantizedNode* node); + void _RayStab(const AABBQuantizedNoLeafNode* node); + void _RayStab(const AABBTreeNode* node, OPC_Container& box_indices); + // Overlap tests + inline_ BOOL RayAABBOverlap(const Point& center, const Point& extents); + inline_ BOOL SegmentAABBOverlap(const Point& center, const Point& extents); + inline_ BOOL RayTriOverlap(const Point& vert0, const Point& vert1, const Point& vert2); + // Init methods + BOOL InitQuery(const Ray& world_ray, const Matrix4x4* world=null, udword* face_id=null); + }; + +#endif // __OPC_RAYCOLLIDER_H__ diff --git a/Engine/lib/opcode/OPC_RayTriOverlap.h b/Engine/lib/opcode/OPC_RayTriOverlap.h new file mode 100644 index 000000000..7fe37c985 --- /dev/null +++ b/Engine/lib/opcode/OPC_RayTriOverlap.h @@ -0,0 +1,89 @@ +#define LOCAL_EPSILON 0.000001f + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes a ray-triangle intersection test. + * Original code from Tomas Möller's "Fast Minimum Storage Ray-Triangle Intersection". + * It's been optimized a bit with integer code, and modified to return a non-intersection if distance from + * ray origin to triangle is negative. + * + * \param vert0 [in] triangle vertex + * \param vert1 [in] triangle vertex + * \param vert2 [in] triangle vertex + * \return true on overlap. mStabbedFace is filled with relevant info. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ BOOL RayCollider::RayTriOverlap(const Point& vert0, const Point& vert1, const Point& vert2) +{ + // Stats + mNbRayPrimTests++; + + // Find vectors for two edges sharing vert0 + Point edge1 = vert1 - vert0; + Point edge2 = vert2 - vert0; + + // Begin calculating determinant - also used to calculate U parameter + Point pvec = mDir^edge2; + + // If determinant is near zero, ray lies in plane of triangle + float det = edge1|pvec; + + if(mCulling) + { + if(det 0. So we can use integer cmp. + + // Calculate distance from vert0 to ray origin + Point tvec = mOrigin - vert0; + + // Calculate U parameter and test bounds + mStabbedFace.mU = tvec|pvec; +// if(IR(u)&0x80000000 || u>det) return FALSE; + if(IS_NEGATIVE_FLOAT(mStabbedFace.mU) || IR(mStabbedFace.mU)>IR(det)) return FALSE; + + // Prepare to test V parameter + Point qvec = tvec^edge1; + + // Calculate V parameter and test bounds + mStabbedFace.mV = mDir|qvec; + if(IS_NEGATIVE_FLOAT(mStabbedFace.mV) || mStabbedFace.mU+mStabbedFace.mV>det) return FALSE; + + // Calculate t, scale parameters, ray intersects triangle + mStabbedFace.mDistance = edge2|qvec; + // Det > 0 so we can early exit here + // Intersection point is valid if distance is positive (else it can just be a face behind the orig point) + if(IS_NEGATIVE_FLOAT(mStabbedFace.mDistance)) return FALSE; + // Else go on + float OneOverDet = 1.0f / det; + mStabbedFace.mDistance *= OneOverDet; + mStabbedFace.mU *= OneOverDet; + mStabbedFace.mV *= OneOverDet; + } + else + { + // the non-culling branch + if(det>-LOCAL_EPSILON && det1.0f) return FALSE; + if(IS_NEGATIVE_FLOAT(mStabbedFace.mU) || IR(mStabbedFace.mU)>IEEE_1_0) return FALSE; + + // prepare to test V parameter + Point qvec = tvec^edge1; + + // Calculate V parameter and test bounds + mStabbedFace.mV = (mDir|qvec) * OneOverDet; + if(IS_NEGATIVE_FLOAT(mStabbedFace.mV) || mStabbedFace.mU+mStabbedFace.mV>1.0f) return FALSE; + + // Calculate t, ray intersects triangle + mStabbedFace.mDistance = (edge2|qvec) * OneOverDet; + // Intersection point is valid if distance is positive (else it can just be a face behind the orig point) + if(IS_NEGATIVE_FLOAT(mStabbedFace.mDistance)) return FALSE; + } + return TRUE; +} diff --git a/Engine/lib/opcode/OPC_Settings.h b/Engine/lib/opcode/OPC_Settings.h new file mode 100644 index 000000000..9dd761d19 --- /dev/null +++ b/Engine/lib/opcode/OPC_Settings.h @@ -0,0 +1,49 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains compilation flags. + * \file OPC_Settings.h + * \author Pierre Terdiman + * \date May, 12, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_SETTINGS_H__ +#define __OPC_SETTINGS_H__ + + //! Use CPU comparisons (comment that line to use standard FPU compares) + #define OPC_CPU_COMPARE + + //! Use FCOMI / FCMOV on Pentium-Pro based processors (comment that line to use plain C++) + #define OPC_USE_FCOMI + + //! Use epsilon value in tri-tri overlap test + #define OPC_TRITRI_EPSILON_TEST + + //! Use tree-coherence or not [not implemented yet] +// #define OPC_USE_TREE_COHERENCE + + //! Use callbacks or direct pointers. Using callbacks might be a bit slower (but probably not much) +// #define OPC_USE_CALLBACKS + + //! Support triangle and vertex strides or not. Using strides might be a bit slower (but probably not much) +// #define OPC_USE_STRIDE + + //! Discard negative pointer in vanilla trees + #define OPC_NO_NEG_VANILLA_TREE + + //! Use a callback in the ray collider + // #define OPC_RAYHIT_CALLBACK + + // NB: no compilation flag to enable/disable stats since they're actually needed in the box/box overlap test + +#endif //__OPC_SETTINGS_H__ \ No newline at end of file diff --git a/Engine/lib/opcode/OPC_SphereAABBOverlap.h b/Engine/lib/opcode/OPC_SphereAABBOverlap.h new file mode 100644 index 000000000..2278bc01f --- /dev/null +++ b/Engine/lib/opcode/OPC_SphereAABBOverlap.h @@ -0,0 +1,128 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Sphere-AABB overlap test, based on Jim Arvo's code. + * \param center [in] box center + * \param extents [in] box extents + * \return TRUE on overlap + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ BOOL SphereCollider::SphereAABBOverlap(const Point& center, const Point& extents) +{ + // Stats + mNbVolumeBVTests++; + + float d = 0.0f; + + //find the square of the distance + //from the sphere to the box +#ifdef OLDIES + for(udword i=0;i<3;i++) + { + float tmp = mCenter[i] - center[i]; + float s = tmp + extents[i]; + + if(s<0.0f) d += s*s; + else + { + s = tmp - extents[i]; + if(s>0.0f) d += s*s; + } + } +#endif + +//#ifdef NEW_TEST + +// float tmp = mCenter.x - center.x; +// float s = tmp + extents.x; + + float tmp,s; + + tmp = mCenter.x - center.x; + s = tmp + extents.x; + + if(s<0.0f) + { + d += s*s; + if(d>mRadius2) return FALSE; + } + else + { + s = tmp - extents.x; + if(s>0.0f) + { + d += s*s; + if(d>mRadius2) return FALSE; + } + } + + tmp = mCenter.y - center.y; + s = tmp + extents.y; + + if(s<0.0f) + { + d += s*s; + if(d>mRadius2) return FALSE; + } + else + { + s = tmp - extents.y; + if(s>0.0f) + { + d += s*s; + if(d>mRadius2) return FALSE; + } + } + + tmp = mCenter.z - center.z; + s = tmp + extents.z; + + if(s<0.0f) + { + d += s*s; + if(d>mRadius2) return FALSE; + } + else + { + s = tmp - extents.z; + if(s>0.0f) + { + d += s*s; + if(d>mRadius2) return FALSE; + } + } +//#endif + +#ifdef OLDIES +// Point Min = center - extents; +// Point Max = center + extents; + + float d = 0.0f; + + //find the square of the distance + //from the sphere to the box + for(udword i=0;i<3;i++) + { +float Min = center[i] - extents[i]; + +// if(mCenter[i]Max[i]) + if(mCenter[i]>Max) + { + float s = mCenter[i] - Max; + d += s*s; + } + } + } +#endif + return d <= mRadius2; +} diff --git a/Engine/lib/opcode/OPC_SphereCollider.cpp b/Engine/lib/opcode/OPC_SphereCollider.cpp new file mode 100644 index 000000000..ad27a3a05 --- /dev/null +++ b/Engine/lib/opcode/OPC_SphereCollider.cpp @@ -0,0 +1,725 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for a sphere collider. + * \file OPC_SphereCollider.cpp + * \author Pierre Terdiman + * \date June, 2, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains a sphere-vs-tree collider. + * This class performs a collision test between a sphere and an AABB tree. You can use this to do a standard player vs world collision, + * in a Nettle/Telemachos way. It doesn't suffer from all reported bugs in those two classic codes - the "new" one by Paul Nettle is a + * debuggued version I think. Collision response can be driven by reported collision data - it works extremely well for me. In sake of + * efficiency, all meshes (that is, all AABB trees) should of course also be kept in an extra hierarchical structure (octree, whatever). + * + * \class SphereCollider + * \author Pierre Terdiman + * \version 1.3 + * \date June, 2, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "Opcode.h" + +using namespace Opcode; + +#include "OPC_SphereAABBOverlap.h" +#include "OPC_SphereTriOverlap.h" + +#define SET_CONTACT(prim_index, flag) \ + /* Set contact status */ \ + mFlags |= flag; \ + mTouchedPrimitives->Add(prim_index); + +//! Sphere-triangle overlap test +#define SPHERE_PRIM(prim_index, flag) \ + /* Request vertices from the app */ \ + VertexPointers VP; mIMesh->GetTriangle(VP, prim_index); \ + \ + /* Perform sphere-tri overlap test */ \ + if(SphereTriOverlap(*VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) \ + { \ + SET_CONTACT(prim_index, flag) \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +SphereCollider::SphereCollider() +{ + mCenter.Zero(); + mRadius2 = 0.0f; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +SphereCollider::~SphereCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Generic collision query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - with GetNbTouchedPrimitives() + * - with GetTouchedPrimitives() + * + * \param cache [in/out] a sphere cache + * \param sphere [in] collision sphere in local space + * \param model [in] Opcode model to collide with + * \param worlds [in] sphere's world matrix, or null + * \param worldm [in] model's world matrix, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool SphereCollider::Collide(SphereCache& cache, const Sphere& sphere, const Model& model, const Matrix4x4* worlds, const Matrix4x4* worldm) +{ + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, sphere, worlds, worldm)) return true; + + if(!model.HasLeafNodes()) + { + if(model.IsQuantized()) + { + const AABBQuantizedNoLeafTree* Tree = (const AABBQuantizedNoLeafTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + else + { + const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree(); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + else + { + const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree(); + + // Perform collision query + if(SkipPrimitiveTests()) _CollideNoPrimitiveTest(Tree->GetNodes()); + else _Collide(Tree->GetNodes()); + } + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Initializes a collision query : + * - reset stats & contact status + * - setup matrices + * - check temporal coherence + * + * \param cache [in/out] a sphere cache + * \param sphere [in] sphere in local space + * \param worlds [in] sphere's world matrix, or null + * \param worldm [in] model's world matrix, or null + * \return TRUE if we can return immediately + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +BOOL SphereCollider::InitQuery(SphereCache& cache, const Sphere& sphere, const Matrix4x4* worlds, const Matrix4x4* worldm) +{ + // 1) Call the base method + VolumeCollider::InitQuery(); + + // 2) Compute sphere in model space: + // - Precompute R^2 + mRadius2 = sphere.mRadius * sphere.mRadius; + // - Compute center position + mCenter = sphere.mCenter; + // -> to world space + if(worlds) mCenter *= *worlds; + // -> to model space + if(worldm) + { + // Invert model matrix + Matrix4x4 InvWorldM; + InvertPRMatrix(InvWorldM, *worldm); + + mCenter *= InvWorldM; + } + + // 3) Setup destination pointer + mTouchedPrimitives = &cache.TouchedPrimitives; + + // 4) Special case: 1-triangle meshes [Opcode 1.3] + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + if(!SkipPrimitiveTests()) + { + // We simply perform the BV-Prim overlap test each time. We assume single triangle has index 0. + mTouchedPrimitives->Reset(); + + // Perform overlap test between the unique triangle and the sphere (and set contact status if needed) + SPHERE_PRIM(udword(0), OPC_CONTACT) + + // Return immediately regardless of status + return TRUE; + } + } + + // 5) Check temporal coherence : + if(TemporalCoherenceEnabled()) + { + // Here we use temporal coherence + // => check results from previous frame before performing the collision query + if(FirstContactEnabled()) + { + // We're only interested in the first contact found => test the unique previously touched face + if(mTouchedPrimitives->GetNbEntries()) + { + // Get index of previously touched face = the first entry in the array + udword PreviouslyTouchedFace = mTouchedPrimitives->GetEntry(0); + + // Then reset the array: + // - if the overlap test below is successful, the index we'll get added back anyway + // - if it isn't, then the array should be reset anyway for the normal query + mTouchedPrimitives->Reset(); + + // Perform overlap test between the cached triangle and the sphere (and set contact status if needed) + SPHERE_PRIM(PreviouslyTouchedFace, OPC_TEMPORAL_CONTACT) + + // Return immediately if possible + if(GetContactStatus()) return TRUE; + } + // else no face has been touched during previous query + // => we'll have to perform a normal query + } + else + { + // We're interested in all contacts =>test the new real sphere N(ew) against the previous fat sphere P(revious): + float r = sqrtf(cache.FatRadius2) - sphere.mRadius; + if(IsCacheValid(cache) && cache.Center.SquareDistance(mCenter) < r*r) + { + // - if N is included in P, return previous list + // => we simply leave the list (mTouchedFaces) unchanged + + // Set contact status if needed + if(mTouchedPrimitives->GetNbEntries()) mFlags |= OPC_TEMPORAL_CONTACT; + + // In any case we don't need to do a query + return TRUE; + } + else + { + // - else do the query using a fat N + + // Reset cache since we'll about to perform a real query + mTouchedPrimitives->Reset(); + + // Make a fat sphere so that coherence will work for subsequent frames + mRadius2 *= cache.FatCoeff; +// mRadius2 = (sphere.mRadius * cache.FatCoeff)*(sphere.mRadius * cache.FatCoeff); + + // Update cache with query data (signature for cached faces) + cache.Center = mCenter; + cache.FatRadius2 = mRadius2; + } + } + } + else + { + // Here we don't use temporal coherence => do a normal query + mTouchedPrimitives->Reset(); + } + + return FALSE; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Collision query for vanilla AABB trees. + * \param cache [in/out] a sphere cache + * \param sphere [in] collision sphere in world space + * \param tree [in] AABB tree + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool SphereCollider::Collide(SphereCache& cache, const Sphere& sphere, const AABBTree* tree) +{ + // This is typically called for a scene tree, full of -AABBs-, not full of triangles. + // So we don't really have "primitives" to deal with. Hence it doesn't work with + // "FirstContact" + "TemporalCoherence". + ASSERT( !(FirstContactEnabled() && TemporalCoherenceEnabled()) ); + + // Checkings + if(!tree) return false; + + // Init collision query + if(InitQuery(cache, sphere)) return true; + + // Perform collision query + _Collide(tree); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Checks the sphere completely contains the box. In which case we can end the query sooner. + * \param bc [in] box center + * \param be [in] box extents + * \return true if the sphere contains the whole box + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ BOOL SphereCollider::SphereContainsBox(const Point& bc, const Point& be) +{ + // I assume if all 8 box vertices are inside the sphere, so does the whole box. + // Sounds ok but maybe there's a better way? + Point p; + p.x=bc.x+be.x; p.y=bc.y+be.y; p.z=bc.z+be.z; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE; + p.x=bc.x-be.x; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE; + p.x=bc.x+be.x; p.y=bc.y-be.y; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE; + p.x=bc.x-be.x; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE; + p.x=bc.x+be.x; p.y=bc.y+be.y; p.z=bc.z-be.z; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE; + p.x=bc.x-be.x; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE; + p.x=bc.x+be.x; p.y=bc.y-be.y; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE; + p.x=bc.x-be.x; if(mCenter.SquareDistance(p)>=mRadius2) return FALSE; + + return TRUE; +} + +#define TEST_BOX_IN_SPHERE(center, extents) \ + if(SphereContainsBox(center, extents)) \ + { \ + /* Set contact status */ \ + mFlags |= OPC_CONTACT; \ + _Dump(node); \ + return; \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void SphereCollider::_Collide(const AABBCollisionNode* node) +{ + // Perform Sphere-AABB overlap test + if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->IsLeaf()) + { + SPHERE_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _Collide(node->GetPos()); + + if(ContactFound()) return; + + _Collide(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void SphereCollider::_CollideNoPrimitiveTest(const AABBCollisionNode* node) +{ + // Perform Sphere-AABB overlap test + if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void SphereCollider::_Collide(const AABBQuantizedNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform Sphere-AABB overlap test + if(!SphereAABBOverlap(Center, Extents)) return; + + TEST_BOX_IN_SPHERE(Center, Extents) + + if(node->IsLeaf()) + { + SPHERE_PRIM(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _Collide(node->GetPos()); + + if(ContactFound()) return; + + _Collide(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void SphereCollider::_CollideNoPrimitiveTest(const AABBQuantizedNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform Sphere-AABB overlap test + if(!SphereAABBOverlap(Center, Extents)) return; + + TEST_BOX_IN_SPHERE(Center, Extents) + + if(node->IsLeaf()) + { + SET_CONTACT(node->GetPrimitive(), OPC_CONTACT) + } + else + { + _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + _CollideNoPrimitiveTest(node->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void SphereCollider::_Collide(const AABBNoLeafNode* node) +{ + // Perform Sphere-AABB overlap test + if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->HasPosLeaf()) { SPHERE_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } + else _Collide(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SPHERE_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } + else _Collide(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void SphereCollider::_CollideNoPrimitiveTest(const AABBNoLeafNode* node) +{ + // Perform Sphere-AABB overlap test + if(!SphereAABBOverlap(node->mAABB.mCenter, node->mAABB.mExtents)) return; + + TEST_BOX_IN_SPHERE(node->mAABB.mCenter, node->mAABB.mExtents) + + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void SphereCollider::_Collide(const AABBQuantizedNoLeafNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform Sphere-AABB overlap test + if(!SphereAABBOverlap(Center, Extents)) return; + + TEST_BOX_IN_SPHERE(Center, Extents) + + if(node->HasPosLeaf()) { SPHERE_PRIM(node->GetPosPrimitive(), OPC_CONTACT) } + else _Collide(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SPHERE_PRIM(node->GetNegPrimitive(), OPC_CONTACT) } + else _Collide(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees, without primitive tests. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void SphereCollider::_CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node) +{ + // Dequantize box + const QuantizedAABB& Box = node->mAABB; + const Point Center(float(Box.mCenter[0]) * mCenterCoeff.x, float(Box.mCenter[1]) * mCenterCoeff.y, float(Box.mCenter[2]) * mCenterCoeff.z); + const Point Extents(float(Box.mExtents[0]) * mExtentsCoeff.x, float(Box.mExtents[1]) * mExtentsCoeff.y, float(Box.mExtents[2]) * mExtentsCoeff.z); + + // Perform Sphere-AABB overlap test + if(!SphereAABBOverlap(Center, Extents)) return; + + TEST_BOX_IN_SPHERE(Center, Extents) + + if(node->HasPosLeaf()) { SET_CONTACT(node->GetPosPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetPos()); + + if(ContactFound()) return; + + if(node->HasNegLeaf()) { SET_CONTACT(node->GetNegPrimitive(), OPC_CONTACT) } + else _CollideNoPrimitiveTest(node->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for vanilla AABB trees. + * \param node [in] current collision node + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void SphereCollider::_Collide(const AABBTreeNode* node) +{ + // Perform Sphere-AABB overlap test + Point Center, Extents; + node->GetAABB()->GetCenter(Center); + node->GetAABB()->GetExtents(Extents); + if(!SphereAABBOverlap(Center, Extents)) return; + + if(node->IsLeaf() || SphereContainsBox(Center, Extents)) + { + mFlags |= OPC_CONTACT; + mTouchedPrimitives->Add(node->GetPrimitives(), node->GetNbPrimitives()); + } + else + { + _Collide(node->GetPos()); + _Collide(node->GetNeg()); + } +} + + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridSphereCollider::HybridSphereCollider() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +HybridSphereCollider::~HybridSphereCollider() +{ +} + +bool HybridSphereCollider::Collide(SphereCache& cache, const Sphere& sphere, const HybridModel& model, const Matrix4x4* worlds, const Matrix4x4* worldm) +{ + // We don't want primitive tests here! + mFlags |= OPC_NO_PRIMITIVE_TESTS; + + // Checkings + if(!Setup(&model)) return false; + + // Init collision query + if(InitQuery(cache, sphere, worlds, worldm)) return true; + + // Special case for 1-leaf trees + if(mCurrentModel && mCurrentModel->HasSingleNode()) + { + // Here we're supposed to perform a normal query, except our tree has a single node, i.e. just a few triangles + udword Nb = mIMesh->GetNbTriangles(); + + // Loop through all triangles + for(udword i=0;imCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + else + { + const AABBNoLeafTree* Tree = (const AABBNoLeafTree*)model.GetTree(); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + } + else + { + if(model.IsQuantized()) + { + const AABBQuantizedTree* Tree = (const AABBQuantizedTree*)model.GetTree(); + + // Setup dequantization coeffs + mCenterCoeff = Tree->mCenterCoeff; + mExtentsCoeff = Tree->mExtentsCoeff; + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + else + { + const AABBCollisionTree* Tree = (const AABBCollisionTree*)model.GetTree(); + + // Perform collision query - we don't want primitive tests here! + _CollideNoPrimitiveTest(Tree->GetNodes()); + } + } + + // We only have a list of boxes so far + if(GetContactStatus()) + { + // Reset contact status, since it currently only reflects collisions with leaf boxes + Collider::InitQuery(); + + // Change dest container so that we can use built-in overlap tests and get collided primitives + cache.TouchedPrimitives.Reset(); + mTouchedPrimitives = &cache.TouchedPrimitives; + + // Read touched leaf boxes + udword Nb = mTouchedBoxes.GetNbEntries(); + const udword* Touched = mTouchedBoxes.GetEntries(); + + const LeafTriangles* LT = model.GetLeafTriangles(); + const udword* Indices = model.GetIndices(); + + // Loop through touched leaves + while(Nb--) + { + const LeafTriangles& CurrentLeaf = LT[*Touched++]; + + // Each leaf box has a set of triangles + udword NbTris = CurrentLeaf.GetNbTriangles(); + if(Indices) + { + const udword* T = &Indices[CurrentLeaf.GetTriangleIndex()]; + + // Loop through triangles and test each of them + while(NbTris--) + { + udword TriangleIndex = *T++; + SPHERE_PRIM(TriangleIndex, OPC_CONTACT) + } + } + else + { + udword BaseIndex = CurrentLeaf.GetTriangleIndex(); + + // Loop through triangles and test each of them + while(NbTris--) + { + udword TriangleIndex = BaseIndex++; + SPHERE_PRIM(TriangleIndex, OPC_CONTACT) + } + } + } + } + + return true; +} diff --git a/Engine/lib/opcode/OPC_SphereCollider.h b/Engine/lib/opcode/OPC_SphereCollider.h new file mode 100644 index 000000000..d176b3417 --- /dev/null +++ b/Engine/lib/opcode/OPC_SphereCollider.h @@ -0,0 +1,96 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for a sphere collider. + * \file OPC_SphereCollider.h + * \author Pierre Terdiman + * \date June, 2, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_SPHERECOLLIDER_H__ +#define __OPC_SPHERECOLLIDER_H__ + + struct OPCODE_API SphereCache : VolumeCache + { + SphereCache() : Center(0.0f,0.0f,0.0f), FatRadius2(0.0f), FatCoeff(1.1f) {} + ~SphereCache() {} + + // Cached faces signature + Point Center; //!< Sphere used when performing the query resulting in cached faces + float FatRadius2; //!< Sphere used when performing the query resulting in cached faces + // User settings + float FatCoeff; //!< mRadius2 multiplier used to create a fat sphere + }; + + class OPCODE_API SphereCollider : public VolumeCollider + { + public: + // Constructor / Destructor + SphereCollider(); + virtual ~SphereCollider(); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Generic collision query for generic OPCODE models. After the call, access the results: + * - with GetContactStatus() + * - with GetNbTouchedPrimitives() + * - with GetTouchedPrimitives() + * + * \param cache [in/out] a sphere cache + * \param sphere [in] collision sphere in local space + * \param model [in] Opcode model to collide with + * \param worlds [in] sphere's world matrix, or null + * \param worldm [in] model's world matrix, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool Collide(SphereCache& cache, const Sphere& sphere, const Model& model, const Matrix4x4* worlds=null, const Matrix4x4* worldm=null); + + // + bool Collide(SphereCache& cache, const Sphere& sphere, const AABBTree* tree); + protected: + // Sphere in model space + Point mCenter; //!< Sphere center + float mRadius2; //!< Sphere radius squared + // Internal methods + void _Collide(const AABBCollisionNode* node); + void _Collide(const AABBNoLeafNode* node); + void _Collide(const AABBQuantizedNode* node); + void _Collide(const AABBQuantizedNoLeafNode* node); + void _Collide(const AABBTreeNode* node); + void _CollideNoPrimitiveTest(const AABBCollisionNode* node); + void _CollideNoPrimitiveTest(const AABBNoLeafNode* node); + void _CollideNoPrimitiveTest(const AABBQuantizedNode* node); + void _CollideNoPrimitiveTest(const AABBQuantizedNoLeafNode* node); + // Overlap tests + inline_ BOOL SphereContainsBox(const Point& bc, const Point& be); + inline_ BOOL SphereAABBOverlap(const Point& center, const Point& extents); + BOOL SphereTriOverlap(const Point& vert0, const Point& vert1, const Point& vert2); + // Init methods + BOOL InitQuery(SphereCache& cache, const Sphere& sphere, const Matrix4x4* worlds=null, const Matrix4x4* worldm=null); + }; + + class OPCODE_API HybridSphereCollider : public SphereCollider + { + public: + // Constructor / Destructor + HybridSphereCollider(); + virtual ~HybridSphereCollider(); + + bool Collide(SphereCache& cache, const Sphere& sphere, const HybridModel& model, const Matrix4x4* worlds=null, const Matrix4x4* worldm=null); + protected: + OPC_Container mTouchedBoxes; + }; + +#endif // __OPC_SPHERECOLLIDER_H__ diff --git a/Engine/lib/opcode/OPC_SphereTriOverlap.h b/Engine/lib/opcode/OPC_SphereTriOverlap.h new file mode 100644 index 000000000..77e59f371 --- /dev/null +++ b/Engine/lib/opcode/OPC_SphereTriOverlap.h @@ -0,0 +1,187 @@ + +// This is collision detection. If you do another distance test for collision *response*, +// if might be useful to simply *skip* the test below completely, and report a collision. +// - if sphere-triangle overlap, result is ok +// - if they don't, we'll discard them during collision response with a similar test anyway +// Overall this approach should run faster. + +// Original code by David Eberly in Magic. +BOOL SphereCollider::SphereTriOverlap(const Point& vert0, const Point& vert1, const Point& vert2) +{ + // Stats + mNbVolumePrimTests++; + + // Early exit if one of the vertices is inside the sphere + Point kDiff = vert2 - mCenter; + float fC = kDiff.SquareMagnitude(); + if(fC <= mRadius2) return TRUE; + + kDiff = vert1 - mCenter; + fC = kDiff.SquareMagnitude(); + if(fC <= mRadius2) return TRUE; + + kDiff = vert0 - mCenter; + fC = kDiff.SquareMagnitude(); + if(fC <= mRadius2) return TRUE; + + // Else do the full distance test + Point TriEdge0 = vert1 - vert0; + Point TriEdge1 = vert2 - vert0; + +//Point kDiff = vert0 - mCenter; + float fA00 = TriEdge0.SquareMagnitude(); + float fA01 = TriEdge0 | TriEdge1; + float fA11 = TriEdge1.SquareMagnitude(); + float fB0 = kDiff | TriEdge0; + float fB1 = kDiff | TriEdge1; +//float fC = kDiff.SquareMagnitude(); + float fDet = fabsf(fA00*fA11 - fA01*fA01); + float u = fA01*fB1-fA11*fB0; + float v = fA01*fB0-fA00*fB1; + float SqrDist; + + if(u + v <= fDet) + { + if(u < 0.0f) + { + if(v < 0.0f) // region 4 + { + if(fB0 < 0.0f) + { +// v = 0.0f; + if(-fB0>=fA00) { /*u = 1.0f;*/ SqrDist = fA00+2.0f*fB0+fC; } + else { u = -fB0/fA00; SqrDist = fB0*u+fC; } + } + else + { +// u = 0.0f; + if(fB1>=0.0f) { /*v = 0.0f;*/ SqrDist = fC; } + else if(-fB1>=fA11) { /*v = 1.0f;*/ SqrDist = fA11+2.0f*fB1+fC; } + else { v = -fB1/fA11; SqrDist = fB1*v+fC; } + } + } + else // region 3 + { +// u = 0.0f; + if(fB1>=0.0f) { /*v = 0.0f;*/ SqrDist = fC; } + else if(-fB1>=fA11) { /*v = 1.0f;*/ SqrDist = fA11+2.0f*fB1+fC; } + else { v = -fB1/fA11; SqrDist = fB1*v+fC; } + } + } + else if(v < 0.0f) // region 5 + { +// v = 0.0f; + if(fB0>=0.0f) { /*u = 0.0f;*/ SqrDist = fC; } + else if(-fB0>=fA00) { /*u = 1.0f;*/ SqrDist = fA00+2.0f*fB0+fC; } + else { u = -fB0/fA00; SqrDist = fB0*u+fC; } + } + else // region 0 + { + // minimum at interior point + if(fDet==0.0f) + { +// u = 0.0f; +// v = 0.0f; + SqrDist = MAX_FLOAT; + } + else + { + float fInvDet = 1.0f/fDet; + u *= fInvDet; + v *= fInvDet; + SqrDist = u*(fA00*u+fA01*v+2.0f*fB0) + v*(fA01*u+fA11*v+2.0f*fB1)+fC; + } + } + } + else + { + float fTmp0, fTmp1, fNumer, fDenom; + + if(u < 0.0f) // region 2 + { + fTmp0 = fA01 + fB0; + fTmp1 = fA11 + fB1; + if(fTmp1 > fTmp0) + { + fNumer = fTmp1 - fTmp0; + fDenom = fA00-2.0f*fA01+fA11; + if(fNumer >= fDenom) + { +// u = 1.0f; +// v = 0.0f; + SqrDist = fA00+2.0f*fB0+fC; + } + else + { + u = fNumer/fDenom; + v = 1.0f - u; + SqrDist = u*(fA00*u+fA01*v+2.0f*fB0) + v*(fA01*u+fA11*v+2.0f*fB1)+fC; + } + } + else + { +// u = 0.0f; + if(fTmp1 <= 0.0f) { /*v = 1.0f;*/ SqrDist = fA11+2.0f*fB1+fC; } + else if(fB1 >= 0.0f) { /*v = 0.0f;*/ SqrDist = fC; } + else { v = -fB1/fA11; SqrDist = fB1*v+fC; } + } + } + else if(v < 0.0f) // region 6 + { + fTmp0 = fA01 + fB1; + fTmp1 = fA00 + fB0; + if(fTmp1 > fTmp0) + { + fNumer = fTmp1 - fTmp0; + fDenom = fA00-2.0f*fA01+fA11; + if(fNumer >= fDenom) + { +// v = 1.0f; +// u = 0.0f; + SqrDist = fA11+2.0f*fB1+fC; + } + else + { + v = fNumer/fDenom; + u = 1.0f - v; + SqrDist = u*(fA00*u+fA01*v+2.0f*fB0) + v*(fA01*u+fA11*v+2.0f*fB1)+fC; + } + } + else + { +// v = 0.0f; + if(fTmp1 <= 0.0f) { /*u = 1.0f;*/ SqrDist = fA00+2.0f*fB0+fC; } + else if(fB0 >= 0.0f) { /*u = 0.0f;*/ SqrDist = fC; } + else { u = -fB0/fA00; SqrDist = fB0*u+fC; } + } + } + else // region 1 + { + fNumer = fA11 + fB1 - fA01 - fB0; + if(fNumer <= 0.0f) + { +// u = 0.0f; +// v = 1.0f; + SqrDist = fA11+2.0f*fB1+fC; + } + else + { + fDenom = fA00-2.0f*fA01+fA11; + if(fNumer >= fDenom) + { +// u = 1.0f; +// v = 0.0f; + SqrDist = fA00+2.0f*fB0+fC; + } + else + { + u = fNumer/fDenom; + v = 1.0f - u; + SqrDist = u*(fA00*u+fA01*v+2.0f*fB0) + v*(fA01*u+fA11*v+2.0f*fB1)+fC; + } + } + } + } + + return fabsf(SqrDist) < mRadius2; +} diff --git a/Engine/lib/opcode/OPC_SweepAndPrune.cpp b/Engine/lib/opcode/OPC_SweepAndPrune.cpp new file mode 100644 index 000000000..7cb7702e4 --- /dev/null +++ b/Engine/lib/opcode/OPC_SweepAndPrune.cpp @@ -0,0 +1,663 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains an implementation of the sweep-and-prune algorithm (moved from Z-Collide) + * \file OPC_SweepAndPrune.cpp + * \author Pierre Terdiman + * \date January, 29, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "Opcode.h" + +using namespace Opcode; + +inline_ void Sort(udword& id0, udword& id1) +{ + if(id0>id1) Swap(id0, id1); +} + + class Opcode::SAP_Element + { + public: + inline_ SAP_Element() {} + inline_ SAP_Element(udword id, SAP_Element* next) : mID(id), mNext(next) {} + inline_ ~SAP_Element() {} + + udword mID; + SAP_Element* mNext; + }; + + class Opcode::SAP_Box + { + public: + SAP_EndPoint* Min[3]; + SAP_EndPoint* Max[3]; + }; + + class Opcode::SAP_EndPoint + { + public: + float Value; // Min or Max value + SAP_EndPoint* Previous; // Previous EndPoint whose Value is smaller than ours (or null) + SAP_EndPoint* Next; // Next EndPoint whose Value is greater than ours (or null) + udword Data; // Parent box ID *2 | MinMax flag + + inline_ void SetData(udword box_id, BOOL is_max) { Data = (box_id<<1)|is_max; } + inline_ BOOL IsMax() const { return Data & 1; } + inline_ udword GetBoxID() const { return Data>>1; } + + inline_ void InsertAfter(SAP_EndPoint* element) + { + if(this!=element && this!=element->Next) + { + // Remove + if(Previous) Previous->Next = Next; + if(Next) Next->Previous = Previous; + + // Insert + Next = element->Next; + if(Next) Next->Previous = this; + + element->Next = this; + Previous = element; + } + } + + inline_ void InsertBefore(SAP_EndPoint* element) + { + if(this!=element && this!=element->Previous) + { + // Remove + if(Previous) Previous->Next = Next; + if(Next) Next->Previous = Previous; + + // Insert + Previous = element->Previous; + element->Previous = this; + + Next = element; + if(Previous) Previous->Next = this; + } + } + }; + + + + + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +SAP_PairData::SAP_PairData() : + mNbElements (0), + mNbUsedElements (0), + mElementPool (null), + mFirstFree (null), + mNbObjects (0), + mArray (null) +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +SAP_PairData::~SAP_PairData() +{ + Release(); +} + +void SAP_PairData::Release() +{ + mNbElements = 0; + mNbUsedElements = 0; + mNbObjects = 0; + DELETEARRAY(mElementPool); + DELETEARRAY(mArray); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Initializes. + * \param nb_objects [in] + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool SAP_PairData::Init(udword nb_objects) +{ + // Make sure everything has been released + Release(); + if(!nb_objects) return false; + + mArray = new SAP_Element*[nb_objects]; + CHECKALLOC(mArray); + ZeroMemory(mArray, nb_objects*sizeof(SAP_Element*)); + mNbObjects = nb_objects; + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Remaps a pointer when pool gets resized. + * \param element [in/out] remapped element + * \param delta [in] offset in bytes + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ void Remap(SAP_Element*& element, udword delta) +{ + if(element) element = (SAP_Element*)(udword(element) + delta); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Gets a free element in the pool. + * \param id [in] element id + * \param next [in] next element + * \param remap [out] possible remapping offset + * \return the new element + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +SAP_Element* SAP_PairData::GetFreeElem(udword id, SAP_Element* next, udword* remap) +{ + if(remap) *remap = 0; + + SAP_Element* FreeElem; + if(mFirstFree) + { + // Recycle + FreeElem = mFirstFree; + mFirstFree = mFirstFree->mNext; // First free = next free (or null) + } + else + { + if(mNbUsedElements==mNbElements) + { + // Resize + mNbElements = mNbElements ? (mNbElements<<1) : 2; + + SAP_Element* NewElems = new SAP_Element[mNbElements]; + + if(mNbUsedElements) CopyMemory(NewElems, mElementPool, mNbUsedElements*sizeof(SAP_Element)); + + // Remap everything + { + udword Delta = udword(NewElems) - udword(mElementPool); + + for(udword i=0;imID = id; + FreeElem->mNext = next; + + return FreeElem; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Frees an element of the pool. + * \param elem [in] element to free/recycle + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ void SAP_PairData::FreeElem(SAP_Element* elem) +{ + elem->mNext = mFirstFree; // Next free + mFirstFree = elem; +} + +// Add a pair to the set. +void SAP_PairData::AddPair(udword id1, udword id2) +{ + // Order the ids + Sort(id1, id2); + + ASSERT(id1=mNbObjects) return; + + // Select the right list from "mArray". + SAP_Element* Current = mArray[id1]; + + if(!Current) + { + // Empty slot => create new element + mArray[id1] = GetFreeElem(id2, null); + } + else if(Current->mID>id2) + { + // The list is not empty but all elements are greater than id2 => insert id2 in the front. + mArray[id1] = GetFreeElem(id2, mArray[id1]); + } + else + { + // Else find the correct location in the sorted list (ascending order) and insert id2 there. + while(Current->mNext) + { + if(Current->mNext->mID > id2) break; + + Current = Current->mNext; + } + + if(Current->mID==id2) return; // The pair already exists + +// Current->mNext = GetFreeElem(id2, Current->mNext); + udword Delta; + SAP_Element* E = GetFreeElem(id2, Current->mNext, &Delta); + if(Delta) Remap(Current, Delta); + Current->mNext = E; + } +} + +// Delete a pair from the set. +void SAP_PairData::RemovePair(udword id1, udword id2) +{ + // Order the ids. + Sort(id1, id2); + + // Exit if the pair doesn't exist in the set + if(id1>=mNbObjects) return; + + // Otherwise, select the correct list. + SAP_Element* Current = mArray[id1]; + + // If this list is empty, the pair doesn't exist. + if(!Current) return; + + // Otherwise, if id2 is the first element, delete it. + if(Current->mID==id2) + { + mArray[id1] = Current->mNext; + FreeElem(Current); + } + else + { + // If id2 is not the first element, start traversing the sorted list. + while(Current->mNext) + { + // If we have moved too far away without hitting id2, then the pair doesn't exist + if(Current->mNext->mID > id2) return; + + // Otherwise, delete id2. + if(Current->mNext->mID == id2) + { + SAP_Element* Temp = Current->mNext; + Current->mNext = Temp->mNext; + FreeElem(Temp); + return; + } + Current = Current->mNext; + } + } +} + +void SAP_PairData::DumpPairs(Pairs& pairs) const +{ + // ### Ugly and slow + for(udword i=0;imIDmID); + Current = Current->mNext; + } + } +} + +void SAP_PairData::DumpPairs(PairCallback callback, void* user_data) const +{ + if(!callback) return; + + // ### Ugly and slow + for(udword i=0;imIDmID, user_data)) return; + Current = Current->mNext; + } + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Constructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +SweepAndPrune::SweepAndPrune() +{ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Destructor. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +SweepAndPrune::~SweepAndPrune() +{ +} + +void SweepAndPrune::GetPairs(Pairs& pairs) const +{ + mPairs.DumpPairs(pairs); +} + +void SweepAndPrune::GetPairs(PairCallback callback, void* user_data) const +{ + mPairs.DumpPairs(callback, user_data); +} + +bool SweepAndPrune::Init(udword nb_objects, const AABB** boxes) +{ + // 1) Create sorted lists + mNbObjects = nb_objects; + + mBoxes = new SAP_Box[nb_objects]; +// for(udword i=0;iGetMin(Axis); + Data[i*2+1] = boxes[i]->GetMax(Axis); + } + RadixSort RS; + const udword* Sorted = RS.Sort(Data, nb_objects*2).GetRanks(); + + SAP_EndPoint* PreviousEndPoint = null; + + for(udword i=0;i>1; + + ASSERT(BoxIndexValue = SortedCoord; +// CurrentEndPoint->IsMax = SortedIndex&1; // ### could be implicit ? +// CurrentEndPoint->ID = BoxIndex; // ### could be implicit ? + CurrentEndPoint->SetData(BoxIndex, SortedIndex&1); // ### could be implicit ? + CurrentEndPoint->Previous = PreviousEndPoint; + CurrentEndPoint->Next = null; + if(PreviousEndPoint) PreviousEndPoint->Next = CurrentEndPoint; + + if(CurrentEndPoint->IsMax()) mBoxes[BoxIndex].Max[Axis] = CurrentEndPoint; + else mBoxes[BoxIndex].Min[Axis] = CurrentEndPoint; + + PreviousEndPoint = CurrentEndPoint; + } + } + + DELETEARRAY(Data); + + CheckListsIntegrity(); + + // 2) Quickly find starting pairs + + mPairs.Init(nb_objects); + + { + Pairs P; + CompleteBoxPruning(nb_objects, boxes, P, Axes(AXES_XZY)); + for(udword i=0;iid0; + udword id1 = PP->id1; + + if(id0!=id1 && boxes[id0]->Intersect(*boxes[id1])) + { + mPairs.AddPair(id0, id1); + } + else ASSERT(0); + } + } + + return true; +} + +bool SweepAndPrune::CheckListsIntegrity() +{ + for(udword Axis=0;Axis<3;Axis++) + { + // Find list head + SAP_EndPoint* Current = mList[Axis]; + while(Current->Previous) Current = Current->Previous; + + udword Nb = 0; + + SAP_EndPoint* Previous = null; + while(Current) + { + Nb++; + + if(Previous) + { + ASSERT(Previous->Value <= Current->Value); + if(Previous->Value > Current->Value) return false; + } + + ASSERT(Current->Previous==Previous); + if(Current->Previous!=Previous) return false; + + Previous = Current; + Current = Current->Next; + } + + ASSERT(Nb==mNbObjects*2); + } + return true; +} + +inline_ BOOL Intersect(const AABB& a, const SAP_Box& b) +{ + if(b.Max[0]->Value < a.GetMin(0) || a.GetMax(0) < b.Min[0]->Value + || b.Max[1]->Value < a.GetMin(1) || a.GetMax(1) < b.Min[1]->Value + || b.Max[2]->Value < a.GetMin(2) || a.GetMax(2) < b.Min[2]->Value) return FALSE; + + return TRUE; +} + + + +bool SweepAndPrune::UpdateObject(udword i, const AABB& box) +{ + for(udword Axis=0;Axis<3;Axis++) + { +// udword Base = (udword)&mList[Axis][0]; + + // Update min + { + SAP_EndPoint* const CurrentMin = mBoxes[i].Min[Axis]; + ASSERT(!CurrentMin->IsMax()); + + const float Limit = box.GetMin(Axis); + if(Limit == CurrentMin->Value) + { + } + else if(Limit < CurrentMin->Value) + { + CurrentMin->Value = Limit; + + // Min is moving left: + SAP_EndPoint* NewPos = CurrentMin; + ASSERT(NewPos); + + SAP_EndPoint* tmp; + while((tmp = NewPos->Previous) && tmp->Value > Limit) + { + NewPos = tmp; + + if(NewPos->IsMax()) + { + // Our min passed a max => start overlap + //udword SortedIndex = (udword(CurrentMin) - Base)/sizeof(NS_EndPoint); + const udword id0 = CurrentMin->GetBoxID(); + const udword id1 = NewPos->GetBoxID(); + + if(id0!=id1 && Intersect(box, mBoxes[id1])) mPairs.AddPair(id0, id1); + } + } + + CurrentMin->InsertBefore(NewPos); + } + else// if(Limit > CurrentMin->Value) + { + CurrentMin->Value = Limit; + + // Min is moving right: + SAP_EndPoint* NewPos = CurrentMin; + ASSERT(NewPos); + + SAP_EndPoint* tmp; + while((tmp = NewPos->Next) && tmp->Value < Limit) + { + NewPos = tmp; + + if(NewPos->IsMax()) + { + // Our min passed a max => stop overlap + const udword id0 = CurrentMin->GetBoxID(); + const udword id1 = NewPos->GetBoxID(); + + if(id0!=id1) mPairs.RemovePair(id0, id1); + } + } + + CurrentMin->InsertAfter(NewPos); + } + } + + // Update max + { + SAP_EndPoint* const CurrentMax = mBoxes[i].Max[Axis]; + ASSERT(CurrentMax->IsMax()); + + const float Limit = box.GetMax(Axis); + if(Limit == CurrentMax->Value) + { + } + else if(Limit > CurrentMax->Value) + { + CurrentMax->Value = Limit; + + // Max is moving right: + SAP_EndPoint* NewPos = CurrentMax; + ASSERT(NewPos); + + SAP_EndPoint* tmp; + while((tmp = NewPos->Next) && tmp->Value < Limit) + { + NewPos = tmp; + + if(!NewPos->IsMax()) + { + // Our max passed a min => start overlap + const udword id0 = CurrentMax->GetBoxID(); + const udword id1 = NewPos->GetBoxID(); + + if(id0!=id1 && Intersect(box, mBoxes[id1])) mPairs.AddPair(id0, id1); + } + } + + CurrentMax->InsertAfter(NewPos); + } + else// if(Limit < CurrentMax->Value) + { + CurrentMax->Value = Limit; + + // Max is moving left: + SAP_EndPoint* NewPos = CurrentMax; + ASSERT(NewPos); + + SAP_EndPoint* tmp; + while((tmp = NewPos->Previous) && tmp->Value > Limit) + { + NewPos = tmp; + + if(!NewPos->IsMax()) + { + // Our max passed a min => stop overlap + const udword id0 = CurrentMax->GetBoxID(); + const udword id1 = NewPos->GetBoxID(); + + if(id0!=id1) mPairs.RemovePair(id0, id1); + } + } + + CurrentMax->InsertBefore(NewPos); + } + } + } + + return true; +} diff --git a/Engine/lib/opcode/OPC_SweepAndPrune.h b/Engine/lib/opcode/OPC_SweepAndPrune.h new file mode 100644 index 000000000..cbb87ac64 --- /dev/null +++ b/Engine/lib/opcode/OPC_SweepAndPrune.h @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains an implementation of the sweep-and-prune algorithm (moved from Z-Collide) + * \file OPC_SweepAndPrune.h + * \author Pierre Terdiman + * \date January, 29, 2000 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_SWEEPANDPRUNE_H__ +#define __OPC_SWEEPANDPRUNE_H__ + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * User-callback, called by OPCODE for each colliding pairs. + * \param id0 [in] id of colliding object + * \param id1 [in] id of colliding object + * \param user_data [in] user-defined data + * \return TRUE to continue enumeration + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + typedef BOOL (*PairCallback) (udword id0, udword id1, void* user_data); + + class SAP_Element; + class SAP_EndPoint; + class SAP_Box; + + class OPCODE_API SAP_PairData + { + public: + SAP_PairData(); + ~SAP_PairData(); + + bool Init(udword nb_objects); + + void AddPair(udword id1, udword id2); + void RemovePair(udword id1, udword id2); + + void DumpPairs(Pairs& pairs) const; + void DumpPairs(PairCallback callback, void* user_data) const; + private: + udword mNbElements; //!< Total number of elements in the pool + udword mNbUsedElements; //!< Number of used elements + SAP_Element* mElementPool; //!< Array of mNbElements elements + SAP_Element* mFirstFree; //!< First free element in the pool + + udword mNbObjects; //!< Max number of objects we can handle + SAP_Element** mArray; //!< Pointers to pool + // Internal methods + SAP_Element* GetFreeElem(udword id, SAP_Element* next, udword* remap=null); + inline_ void FreeElem(SAP_Element* elem); + void Release(); + }; + + class OPCODE_API SweepAndPrune + { + public: + SweepAndPrune(); + ~SweepAndPrune(); + + bool Init(udword nb_objects, const AABB** boxes); + bool UpdateObject(udword i, const AABB& box); + + void GetPairs(Pairs& pairs) const; + void GetPairs(PairCallback callback, void* user_data) const; + private: + SAP_PairData mPairs; + + udword mNbObjects; + SAP_Box* mBoxes; + SAP_EndPoint* mList[3]; + // Internal methods + bool CheckListsIntegrity(); + }; + +#endif //__OPC_SWEEPANDPRUNE_H__ \ No newline at end of file diff --git a/Engine/lib/opcode/OPC_TreeBuilders.cpp b/Engine/lib/opcode/OPC_TreeBuilders.cpp new file mode 100644 index 000000000..947524b2c --- /dev/null +++ b/Engine/lib/opcode/OPC_TreeBuilders.cpp @@ -0,0 +1,254 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for tree builders. + * \file OPC_TreeBuilders.cpp + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * A builder for AABB-trees of vertices. + * + * \class AABBTreeOfVerticesBuilder + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * A builder for AABB-trees of AABBs. + * + * \class AABBTreeOfAABBsBuilder + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * A builder for AABB-trees of triangles. + * + * \class AABBTreeOfTrianglesBuilder + * \author Pierre Terdiman + * \version 1.3 + * \date March, 20, 2001 +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "Opcode.h" + +using namespace Opcode; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the AABB of a set of primitives. + * \param primitives [in] list of indices of primitives + * \param nb_prims [in] number of indices + * \param global_box [out] global AABB enclosing the set of input primitives + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTreeOfAABBsBuilder::ComputeGlobalBox(const udword* primitives, udword nb_prims, AABB& global_box) const +{ + // Checkings + if(!primitives || !nb_prims) return false; + + // Initialize global box + global_box = mAABBArray[primitives[0]]; + + // Loop through boxes + for(udword i=1;iGetTriangle(VP, *primitives++); + // Update global box + Min.Min(*VP.Vertex[0]).Min(*VP.Vertex[1]).Min(*VP.Vertex[2]); + Max.Max(*VP.Vertex[0]).Max(*VP.Vertex[1]).Max(*VP.Vertex[2]); + } + global_box.SetMinMax(Min, Max); + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the splitting value along a given axis for a given primitive. + * \param index [in] index of the primitive to split + * \param axis [in] axis index (0,1,2) + * \return splitting value + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float AABBTreeOfTrianglesBuilder::GetSplittingValue(udword index, udword axis) const +{ +/* // Compute center of triangle + Point Center; + mTriList[index].Center(mVerts, Center); + // Return value + return Center[axis];*/ + + // Compute correct component from center of triangle +// return (mVerts[mTriList[index].mVRef[0]][axis] +// +mVerts[mTriList[index].mVRef[1]][axis] +// +mVerts[mTriList[index].mVRef[2]][axis])*INV3; + + VertexPointers VP; + mIMesh->GetTriangle(VP, index); + + // Compute correct component from center of triangle + return ((*VP.Vertex[0])[axis] + +(*VP.Vertex[1])[axis] + +(*VP.Vertex[2])[axis])*INV3; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the splitting value along a given axis for a given node. + * \param primitives [in] list of indices of primitives + * \param nb_prims [in] number of indices + * \param global_box [in] global AABB enclosing the set of input primitives + * \param axis [in] axis index (0,1,2) + * \return splitting value + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +float AABBTreeOfTrianglesBuilder::GetSplittingValue(const udword* primitives, udword nb_prims, const AABB& global_box, udword axis) const +{ + if(mSettings.mRules&SPLIT_GEOM_CENTER) + { + // Loop through triangles + float SplitValue = 0.0f; + VertexPointers VP; + for(udword i=0;iGetTriangle(VP, primitives[i]); + // Update split value + SplitValue += (*VP.Vertex[0])[axis]; + SplitValue += (*VP.Vertex[1])[axis]; + SplitValue += (*VP.Vertex[2])[axis]; + } + return SplitValue / float(nb_prims*3); + } + else return AABBTreeBuilder::GetSplittingValue(primitives, nb_prims, global_box, axis); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Computes the AABB of a set of primitives. + * \param primitives [in] list of indices of primitives + * \param nb_prims [in] number of indices + * \param global_box [out] global AABB enclosing the set of input primitives + * \return true if success + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTreeOfVerticesBuilder::ComputeGlobalBox(const udword* primitives, udword nb_prims, AABB& global_box) const +{ + // Checkings + if(!primitives || !nb_prims) return false; + + // Initialize global box + global_box.SetEmpty(); + + // Loop through vertices + for(udword i=0;iHasLeafNodes()!=cache.Model1->HasLeafNodes()) return false; + if(cache.Model0->IsQuantized()!=cache.Model1->IsQuantized()) return false; + + /* + + Rules: + - perform hull test + - when hulls collide, disable hull test + - if meshes overlap, reset countdown + - if countdown reaches 0, enable hull test + + */ + +#ifdef __MESHMERIZER_H__ + // Handle hulls + if(cache.HullTest) + { + if(cache.Model0->GetHull() && cache.Model1->GetHull()) + { + struct Local + { + static Point* SVCallback(const Point& sv, udword& previndex, udword user_data) + { + CollisionHull* Hull = (CollisionHull*)user_data; + previndex = Hull->ComputeSupportingVertex(sv, previndex); + return (Point*)&Hull->GetVerts()[previndex]; + } + }; + + bool Collide; + + if(0) + { + static GJKEngine GJK; + static bool GJKInitDone=false; + if(!GJKInitDone) + { + GJK.Enable(GJK_BACKUP_PROCEDURE); + GJK.Enable(GJK_DEGENERATE); + GJK.Enable(GJK_HILLCLIMBING); + GJKInitDone = true; + } + GJK.SetCallbackObj0(Local::SVCallback); + GJK.SetCallbackObj1(Local::SVCallback); + GJK.SetUserData0(udword(cache.Model0->GetHull())); + GJK.SetUserData1(udword(cache.Model1->GetHull())); + Collide = GJK.Collide(*world0, *world1, &cache.SepVector); + } + else + { + static SVEngine SVE; + SVE.SetCallbackObj0(Local::SVCallback); + SVE.SetCallbackObj1(Local::SVCallback); + SVE.SetUserData0(udword(cache.Model0->GetHull())); + SVE.SetUserData1(udword(cache.Model1->GetHull())); + Collide = SVE.Collide(*world0, *world1, &cache.SepVector); + } + + if(!Collide) + { + // Reset stats & contact status + mFlags &= ~OPC_CONTACT; + mNbBVBVTests = 0; + mNbPrimPrimTests = 0; + mNbBVPrimTests = 0; + mPairs.Reset(); + return true; + } + } + } + + // Here, hulls collide + cache.HullTest = false; +#endif // __MESHMERIZER_H__ + + // Checkings + if(!Setup(cache.Model0->GetMeshInterface(), cache.Model1->GetMeshInterface())) return false; + + // Simple double-dispatch + bool Status; + if(!cache.Model0->HasLeafNodes()) + { + if(cache.Model0->IsQuantized()) + { + const AABBQuantizedNoLeafTree* T0 = (const AABBQuantizedNoLeafTree*)cache.Model0->GetTree(); + const AABBQuantizedNoLeafTree* T1 = (const AABBQuantizedNoLeafTree*)cache.Model1->GetTree(); + Status = Collide(T0, T1, world0, world1, &cache); + } + else + { + const AABBNoLeafTree* T0 = (const AABBNoLeafTree*)cache.Model0->GetTree(); + const AABBNoLeafTree* T1 = (const AABBNoLeafTree*)cache.Model1->GetTree(); + Status = Collide(T0, T1, world0, world1, &cache); + } + } + else + { + if(cache.Model0->IsQuantized()) + { + const AABBQuantizedTree* T0 = (const AABBQuantizedTree*)cache.Model0->GetTree(); + const AABBQuantizedTree* T1 = (const AABBQuantizedTree*)cache.Model1->GetTree(); + Status = Collide(T0, T1, world0, world1, &cache); + } + else + { + const AABBCollisionTree* T0 = (const AABBCollisionTree*)cache.Model0->GetTree(); + const AABBCollisionTree* T1 = (const AABBCollisionTree*)cache.Model1->GetTree(); + Status = Collide(T0, T1, world0, world1, &cache); + } + } + +#ifdef __MESHMERIZER_H__ + if(Status) + { + // Reset counter as long as overlap occurs + if(GetContactStatus()) cache.ResetCountDown(); + + // Enable hull test again when counter reaches zero + cache.CountDown--; + if(!cache.CountDown) + { + cache.ResetCountDown(); + cache.HullTest = true; + } + } +#endif + return Status; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Initializes a collision query : + * - reset stats & contact status + * - setup matrices + * + * \param world0 [in] world matrix for first object + * \param world1 [in] world matrix for second object + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTreeCollider::InitQuery(const Matrix4x4* world0, const Matrix4x4* world1) +{ + // Reset stats & contact status + Collider::InitQuery(); + mNbBVBVTests = 0; + mNbPrimPrimTests = 0; + mNbBVPrimTests = 0; + mPairs.Reset(); + + // Setup matrices + Matrix4x4 InvWorld0, InvWorld1; + if(world0) InvertPRMatrix(InvWorld0, *world0); + else InvWorld0.Identity(); + + if(world1) InvertPRMatrix(InvWorld1, *world1); + else InvWorld1.Identity(); + + Matrix4x4 World0to1 = world0 ? (*world0 * InvWorld1) : InvWorld1; + Matrix4x4 World1to0 = world1 ? (*world1 * InvWorld0) : InvWorld0; + + mR0to1 = World0to1; World0to1.GetTrans(mT0to1); + mR1to0 = World1to0; World1to0.GetTrans(mT1to0); + + // Precompute absolute 1-to-0 rotation matrix + for(udword i=0;i<3;i++) + { + for(udword j=0;j<3;j++) + { + // Epsilon value prevents floating-point inaccuracies (strategy borrowed from RAPID) + mAR.m[i][j] = 1e-6f + fabsf(mR1to0.m[i][j]); + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Takes advantage of temporal coherence. + * \param cache [in] cache for a pair of previously colliding primitives + * \return true if we can return immediately + * \warning only works for "First Contact" mode + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTreeCollider::CheckTemporalCoherence(Pair* cache) +{ + // Checkings + if(!cache) return false; + + // Test previously colliding primitives first + if(TemporalCoherenceEnabled() && FirstContactEnabled()) + { + PrimTest(cache->id0, cache->id1); + if(GetContactStatus()) return true; + } + return false; +} + +#define UPDATE_CACHE \ + if(cache && GetContactStatus()) \ + { \ + cache->id0 = mPairs.GetEntry(0); \ + cache->id1 = mPairs.GetEntry(1); \ + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Collision query for normal AABB trees. + * \param tree0 [in] AABB tree from first object + * \param tree1 [in] AABB tree from second object + * \param world0 [in] world matrix for first object + * \param world1 [in] world matrix for second object + * \param cache [in/out] cache for a pair of previously colliding primitives + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTreeCollider::Collide(const AABBCollisionTree* tree0, const AABBCollisionTree* tree1, const Matrix4x4* world0, const Matrix4x4* world1, Pair* cache) +{ + // Init collision query + InitQuery(world0, world1); + + // Check previous state + if(CheckTemporalCoherence(cache)) return true; + + // Perform collision query + _Collide(tree0->GetNodes(), tree1->GetNodes()); + + UPDATE_CACHE + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Collision query for no-leaf AABB trees. + * \param tree0 [in] AABB tree from first object + * \param tree1 [in] AABB tree from second object + * \param world0 [in] world matrix for first object + * \param world1 [in] world matrix for second object + * \param cache [in/out] cache for a pair of previously colliding primitives + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTreeCollider::Collide(const AABBNoLeafTree* tree0, const AABBNoLeafTree* tree1, const Matrix4x4* world0, const Matrix4x4* world1, Pair* cache) +{ + // Init collision query + InitQuery(world0, world1); + + // Check previous state + if(CheckTemporalCoherence(cache)) return true; + + // Perform collision query + _Collide(tree0->GetNodes(), tree1->GetNodes()); + + UPDATE_CACHE + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Collision query for quantized AABB trees. + * \param tree0 [in] AABB tree from first object + * \param tree1 [in] AABB tree from second object + * \param world0 [in] world matrix for first object + * \param world1 [in] world matrix for second object + * \param cache [in/out] cache for a pair of previously colliding primitives + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTreeCollider::Collide(const AABBQuantizedTree* tree0, const AABBQuantizedTree* tree1, const Matrix4x4* world0, const Matrix4x4* world1, Pair* cache) +{ + // Init collision query + InitQuery(world0, world1); + + // Check previous state + if(CheckTemporalCoherence(cache)) return true; + + // Setup dequantization coeffs + mCenterCoeff0 = tree0->mCenterCoeff; + mExtentsCoeff0 = tree0->mExtentsCoeff; + mCenterCoeff1 = tree1->mCenterCoeff; + mExtentsCoeff1 = tree1->mExtentsCoeff; + + // Dequantize box A + const AABBQuantizedNode* N0 = tree0->GetNodes(); + const Point a(float(N0->mAABB.mExtents[0]) * mExtentsCoeff0.x, float(N0->mAABB.mExtents[1]) * mExtentsCoeff0.y, float(N0->mAABB.mExtents[2]) * mExtentsCoeff0.z); + const Point Pa(float(N0->mAABB.mCenter[0]) * mCenterCoeff0.x, float(N0->mAABB.mCenter[1]) * mCenterCoeff0.y, float(N0->mAABB.mCenter[2]) * mCenterCoeff0.z); + // Dequantize box B + const AABBQuantizedNode* N1 = tree1->GetNodes(); + const Point b(float(N1->mAABB.mExtents[0]) * mExtentsCoeff1.x, float(N1->mAABB.mExtents[1]) * mExtentsCoeff1.y, float(N1->mAABB.mExtents[2]) * mExtentsCoeff1.z); + const Point Pb(float(N1->mAABB.mCenter[0]) * mCenterCoeff1.x, float(N1->mAABB.mCenter[1]) * mCenterCoeff1.y, float(N1->mAABB.mCenter[2]) * mCenterCoeff1.z); + + // Perform collision query + _Collide(N0, N1, a, Pa, b, Pb); + + UPDATE_CACHE + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Collision query for quantized no-leaf AABB trees. + * \param tree0 [in] AABB tree from first object + * \param tree1 [in] AABB tree from second object + * \param world0 [in] world matrix for first object + * \param world1 [in] world matrix for second object + * \param cache [in/out] cache for a pair of previously colliding primitives + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +bool AABBTreeCollider::Collide(const AABBQuantizedNoLeafTree* tree0, const AABBQuantizedNoLeafTree* tree1, const Matrix4x4* world0, const Matrix4x4* world1, Pair* cache) +{ + // Init collision query + InitQuery(world0, world1); + + // Check previous state + if(CheckTemporalCoherence(cache)) return true; + + // Setup dequantization coeffs + mCenterCoeff0 = tree0->mCenterCoeff; + mExtentsCoeff0 = tree0->mExtentsCoeff; + mCenterCoeff1 = tree1->mCenterCoeff; + mExtentsCoeff1 = tree1->mExtentsCoeff; + + // Perform collision query + _Collide(tree0->GetNodes(), tree1->GetNodes()); + + UPDATE_CACHE + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Standard trees +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// The normal AABB tree can use 2 different descent rules (with different performances) +//#define ORIGINAL_CODE //!< UNC-like descent rules +#define ALTERNATIVE_CODE //!< Alternative descent rules + +#ifdef ORIGINAL_CODE +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees. + * \param b0 [in] collision node from first tree + * \param b1 [in] collision node from second tree + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTreeCollider::_Collide(const AABBCollisionNode* b0, const AABBCollisionNode* b1) +{ + // Perform BV-BV overlap test + if(!BoxBoxOverlap(b0->mAABB.mExtents, b0->mAABB.mCenter, b1->mAABB.mExtents, b1->mAABB.mCenter)) return; + + if(b0->IsLeaf() && b1->IsLeaf()) { PrimTest(b0->GetPrimitive(), b1->GetPrimitive()); return; } + + if(b1->IsLeaf() || (!b0->IsLeaf() && (b0->GetSize() > b1->GetSize()))) + { + _Collide(b0->GetNeg(), b1); + if(ContactFound()) return; + _Collide(b0->GetPos(), b1); + } + else + { + _Collide(b0, b1->GetNeg()); + if(ContactFound()) return; + _Collide(b0, b1->GetPos()); + } +} +#endif + +#ifdef ALTERNATIVE_CODE +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for normal AABB trees. + * \param b0 [in] collision node from first tree + * \param b1 [in] collision node from second tree + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTreeCollider::_Collide(const AABBCollisionNode* b0, const AABBCollisionNode* b1) +{ + // Perform BV-BV overlap test + if(!BoxBoxOverlap(b0->mAABB.mExtents, b0->mAABB.mCenter, b1->mAABB.mExtents, b1->mAABB.mCenter)) + { + return; + } + + if(b0->IsLeaf()) + { + if(b1->IsLeaf()) + { + PrimTest(b0->GetPrimitive(), b1->GetPrimitive()); + } + else + { + _Collide(b0, b1->GetNeg()); + if(ContactFound()) return; + _Collide(b0, b1->GetPos()); + } + } + else if(b1->IsLeaf()) + { + _Collide(b0->GetNeg(), b1); + if(ContactFound()) return; + _Collide(b0->GetPos(), b1); + } + else + { + _Collide(b0->GetNeg(), b1->GetNeg()); + if(ContactFound()) return; + _Collide(b0->GetNeg(), b1->GetPos()); + if(ContactFound()) return; + _Collide(b0->GetPos(), b1->GetNeg()); + if(ContactFound()) return; + _Collide(b0->GetPos(), b1->GetPos()); + } +} +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// No-leaf trees +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Leaf-leaf test for two primitive indices. + * \param id0 [in] index from first leaf-triangle + * \param id1 [in] index from second leaf-triangle + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTreeCollider::PrimTest(udword id0, udword id1) +{ + // Request vertices from the app + VertexPointers VP0; + VertexPointers VP1; + mIMesh0->GetTriangle(VP0, id0); + mIMesh1->GetTriangle(VP1, id1); + + // Transform from space 1 to space 0 + Point u0,u1,u2; + TransformPoint(u0, *VP1.Vertex[0], mR1to0, mT1to0); + TransformPoint(u1, *VP1.Vertex[1], mR1to0, mT1to0); + TransformPoint(u2, *VP1.Vertex[2], mR1to0, mT1to0); + + // Perform triangle-triangle overlap test + if(TriTriOverlap(*VP0.Vertex[0], *VP0.Vertex[1], *VP0.Vertex[2], u0, u1, u2)) + { + // Keep track of colliding pairs + mPairs.Add(id0).Add(id1); + // Set contact status + mFlags |= OPC_CONTACT; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Leaf-leaf test for a previously fetched triangle from tree A (in B's space) and a new leaf from B. + * \param id1 [in] leaf-triangle index from tree B + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ void AABBTreeCollider::PrimTestTriIndex(udword id1) +{ + // Request vertices from the app + VertexPointers VP; + mIMesh1->GetTriangle(VP, id1); + + // Perform triangle-triangle overlap test + if(TriTriOverlap(mLeafVerts[0], mLeafVerts[1], mLeafVerts[2], *VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) + { + // Keep track of colliding pairs + mPairs.Add(mLeafIndex).Add(id1); + // Set contact status + mFlags |= OPC_CONTACT; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Leaf-leaf test for a previously fetched triangle from tree B (in A's space) and a new leaf from A. + * \param id0 [in] leaf-triangle index from tree A + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ void AABBTreeCollider::PrimTestIndexTri(udword id0) +{ + // Request vertices from the app + VertexPointers VP; + mIMesh0->GetTriangle(VP, id0); + + // Perform triangle-triangle overlap test + if(TriTriOverlap(mLeafVerts[0], mLeafVerts[1], mLeafVerts[2], *VP.Vertex[0], *VP.Vertex[1], *VP.Vertex[2])) + { + // Keep track of colliding pairs + mPairs.Add(id0).Add(mLeafIndex); + // Set contact status + mFlags |= OPC_CONTACT; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision of a leaf node from A and a branch from B. + * \param b [in] collision node from second tree + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTreeCollider::_CollideTriBox(const AABBNoLeafNode* b) +{ + // Perform triangle-box overlap test + if(!TriBoxOverlap(b->mAABB.mCenter, b->mAABB.mExtents)) return; + + // Keep same triangle, deal with first child + if(b->HasPosLeaf()) PrimTestTriIndex(b->GetPosPrimitive()); + else _CollideTriBox(b->GetPos()); + + if(ContactFound()) return; + + // Keep same triangle, deal with second child + if(b->HasNegLeaf()) PrimTestTriIndex(b->GetNegPrimitive()); + else _CollideTriBox(b->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision of a leaf node from B and a branch from A. + * \param b [in] collision node from first tree + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTreeCollider::_CollideBoxTri(const AABBNoLeafNode* b) +{ + // Perform triangle-box overlap test + if(!TriBoxOverlap(b->mAABB.mCenter, b->mAABB.mExtents)) return; + + // Keep same triangle, deal with first child + if(b->HasPosLeaf()) PrimTestIndexTri(b->GetPosPrimitive()); + else _CollideBoxTri(b->GetPos()); + + if(ContactFound()) return; + + // Keep same triangle, deal with second child + if(b->HasNegLeaf()) PrimTestIndexTri(b->GetNegPrimitive()); + else _CollideBoxTri(b->GetNeg()); +} + +//! Request triangle vertices from the app and transform them +#define FETCH_LEAF(prim_index, imesh, rot, trans) \ + mLeafIndex = prim_index; \ + /* Request vertices from the app */ \ + VertexPointers VP; imesh->GetTriangle(VP, prim_index); \ + /* Transform them in a common space */ \ + TransformPoint(mLeafVerts[0], *VP.Vertex[0], rot, trans); \ + TransformPoint(mLeafVerts[1], *VP.Vertex[1], rot, trans); \ + TransformPoint(mLeafVerts[2], *VP.Vertex[2], rot, trans); + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for no-leaf AABB trees. + * \param a [in] collision node from first tree + * \param b [in] collision node from second tree + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTreeCollider::_Collide(const AABBNoLeafNode* a, const AABBNoLeafNode* b) +{ + // Perform BV-BV overlap test + if(!BoxBoxOverlap(a->mAABB.mExtents, a->mAABB.mCenter, b->mAABB.mExtents, b->mAABB.mCenter)) return; + + // Catch leaf status + BOOL BHasPosLeaf = b->HasPosLeaf(); + BOOL BHasNegLeaf = b->HasNegLeaf(); + + if(a->HasPosLeaf()) + { + FETCH_LEAF(a->GetPosPrimitive(), mIMesh0, mR0to1, mT0to1) + + if(BHasPosLeaf) PrimTestTriIndex(b->GetPosPrimitive()); + else _CollideTriBox(b->GetPos()); + + if(ContactFound()) return; + + if(BHasNegLeaf) PrimTestTriIndex(b->GetNegPrimitive()); + else _CollideTriBox(b->GetNeg()); + } + else + { + if(BHasPosLeaf) + { + FETCH_LEAF(b->GetPosPrimitive(), mIMesh1, mR1to0, mT1to0) + + _CollideBoxTri(a->GetPos()); + } + else _Collide(a->GetPos(), b->GetPos()); + + if(ContactFound()) return; + + if(BHasNegLeaf) + { + FETCH_LEAF(b->GetNegPrimitive(), mIMesh1, mR1to0, mT1to0) + + _CollideBoxTri(a->GetPos()); + } + else _Collide(a->GetPos(), b->GetNeg()); + } + + if(ContactFound()) return; + + if(a->HasNegLeaf()) + { + FETCH_LEAF(a->GetNegPrimitive(), mIMesh0, mR0to1, mT0to1) + + if(BHasPosLeaf) PrimTestTriIndex(b->GetPosPrimitive()); + else _CollideTriBox(b->GetPos()); + + if(ContactFound()) return; + + if(BHasNegLeaf) PrimTestTriIndex(b->GetNegPrimitive()); + else _CollideTriBox(b->GetNeg()); + } + else + { + if(BHasPosLeaf) + { + // ### That leaf has possibly already been fetched + FETCH_LEAF(b->GetPosPrimitive(), mIMesh1, mR1to0, mT1to0) + + _CollideBoxTri(a->GetNeg()); + } + else _Collide(a->GetNeg(), b->GetPos()); + + if(ContactFound()) return; + + if(BHasNegLeaf) + { + // ### That leaf has possibly already been fetched + FETCH_LEAF(b->GetNegPrimitive(), mIMesh1, mR1to0, mT1to0) + + _CollideBoxTri(a->GetNeg()); + } + else _Collide(a->GetNeg(), b->GetNeg()); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Quantized trees +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized AABB trees. + * \param b0 [in] collision node from first tree + * \param b1 [in] collision node from second tree + * \param a [in] extent from box A + * \param Pa [in] center from box A + * \param b [in] extent from box B + * \param Pb [in] center from box B + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTreeCollider::_Collide(const AABBQuantizedNode* b0, const AABBQuantizedNode* b1, const Point& a, const Point& Pa, const Point& b, const Point& Pb) +{ + // Perform BV-BV overlap test + if(!BoxBoxOverlap(a, Pa, b, Pb)) return; + + if(b0->IsLeaf() && b1->IsLeaf()) { PrimTest(b0->GetPrimitive(), b1->GetPrimitive()); return; } + + if(b1->IsLeaf() || (!b0->IsLeaf() && (b0->GetSize() > b1->GetSize()))) + { + // Dequantize box + const QuantizedAABB* Box = &b0->GetNeg()->mAABB; + const Point negPa(float(Box->mCenter[0]) * mCenterCoeff0.x, float(Box->mCenter[1]) * mCenterCoeff0.y, float(Box->mCenter[2]) * mCenterCoeff0.z); + const Point nega(float(Box->mExtents[0]) * mExtentsCoeff0.x, float(Box->mExtents[1]) * mExtentsCoeff0.y, float(Box->mExtents[2]) * mExtentsCoeff0.z); + _Collide(b0->GetNeg(), b1, nega, negPa, b, Pb); + + if(ContactFound()) return; + + // Dequantize box + Box = &b0->GetPos()->mAABB; + const Point posPa(float(Box->mCenter[0]) * mCenterCoeff0.x, float(Box->mCenter[1]) * mCenterCoeff0.y, float(Box->mCenter[2]) * mCenterCoeff0.z); + const Point posa(float(Box->mExtents[0]) * mExtentsCoeff0.x, float(Box->mExtents[1]) * mExtentsCoeff0.y, float(Box->mExtents[2]) * mExtentsCoeff0.z); + _Collide(b0->GetPos(), b1, posa, posPa, b, Pb); + } + else + { + // Dequantize box + const QuantizedAABB* Box = &b1->GetNeg()->mAABB; + const Point negPb(float(Box->mCenter[0]) * mCenterCoeff1.x, float(Box->mCenter[1]) * mCenterCoeff1.y, float(Box->mCenter[2]) * mCenterCoeff1.z); + const Point negb(float(Box->mExtents[0]) * mExtentsCoeff1.x, float(Box->mExtents[1]) * mExtentsCoeff1.y, float(Box->mExtents[2]) * mExtentsCoeff1.z); + _Collide(b0, b1->GetNeg(), a, Pa, negb, negPb); + + if(ContactFound()) return; + + // Dequantize box + Box = &b1->GetPos()->mAABB; + const Point posPb(float(Box->mCenter[0]) * mCenterCoeff1.x, float(Box->mCenter[1]) * mCenterCoeff1.y, float(Box->mCenter[2]) * mCenterCoeff1.z); + const Point posb(float(Box->mExtents[0]) * mExtentsCoeff1.x, float(Box->mExtents[1]) * mExtentsCoeff1.y, float(Box->mExtents[2]) * mExtentsCoeff1.z); + _Collide(b0, b1->GetPos(), a, Pa, posb, posPb); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Quantized no-leaf trees +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision of a leaf node from A and a quantized branch from B. + * \param leaf [in] leaf triangle from first tree + * \param b [in] collision node from second tree + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTreeCollider::_CollideTriBox(const AABBQuantizedNoLeafNode* b) +{ + // Dequantize box + const QuantizedAABB* bb = &b->mAABB; + const Point Pb(float(bb->mCenter[0]) * mCenterCoeff1.x, float(bb->mCenter[1]) * mCenterCoeff1.y, float(bb->mCenter[2]) * mCenterCoeff1.z); + const Point eb(float(bb->mExtents[0]) * mExtentsCoeff1.x, float(bb->mExtents[1]) * mExtentsCoeff1.y, float(bb->mExtents[2]) * mExtentsCoeff1.z); + + // Perform triangle-box overlap test + if(!TriBoxOverlap(Pb, eb)) return; + + if(b->HasPosLeaf()) PrimTestTriIndex(b->GetPosPrimitive()); + else _CollideTriBox(b->GetPos()); + + if(ContactFound()) return; + + if(b->HasNegLeaf()) PrimTestTriIndex(b->GetNegPrimitive()); + else _CollideTriBox(b->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision of a leaf node from B and a quantized branch from A. + * \param b [in] collision node from first tree + * \param leaf [in] leaf triangle from second tree + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTreeCollider::_CollideBoxTri(const AABBQuantizedNoLeafNode* b) +{ + // Dequantize box + const QuantizedAABB* bb = &b->mAABB; + const Point Pa(float(bb->mCenter[0]) * mCenterCoeff0.x, float(bb->mCenter[1]) * mCenterCoeff0.y, float(bb->mCenter[2]) * mCenterCoeff0.z); + const Point ea(float(bb->mExtents[0]) * mExtentsCoeff0.x, float(bb->mExtents[1]) * mExtentsCoeff0.y, float(bb->mExtents[2]) * mExtentsCoeff0.z); + + // Perform triangle-box overlap test + if(!TriBoxOverlap(Pa, ea)) return; + + if(b->HasPosLeaf()) PrimTestIndexTri(b->GetPosPrimitive()); + else _CollideBoxTri(b->GetPos()); + + if(ContactFound()) return; + + if(b->HasNegLeaf()) PrimTestIndexTri(b->GetNegPrimitive()); + else _CollideBoxTri(b->GetNeg()); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Recursive collision query for quantized no-leaf AABB trees. + * \param a [in] collision node from first tree + * \param b [in] collision node from second tree + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void AABBTreeCollider::_Collide(const AABBQuantizedNoLeafNode* a, const AABBQuantizedNoLeafNode* b) +{ + // Dequantize box A + const QuantizedAABB* ab = &a->mAABB; + const Point Pa(float(ab->mCenter[0]) * mCenterCoeff0.x, float(ab->mCenter[1]) * mCenterCoeff0.y, float(ab->mCenter[2]) * mCenterCoeff0.z); + const Point ea(float(ab->mExtents[0]) * mExtentsCoeff0.x, float(ab->mExtents[1]) * mExtentsCoeff0.y, float(ab->mExtents[2]) * mExtentsCoeff0.z); + // Dequantize box B + const QuantizedAABB* bb = &b->mAABB; + const Point Pb(float(bb->mCenter[0]) * mCenterCoeff1.x, float(bb->mCenter[1]) * mCenterCoeff1.y, float(bb->mCenter[2]) * mCenterCoeff1.z); + const Point eb(float(bb->mExtents[0]) * mExtentsCoeff1.x, float(bb->mExtents[1]) * mExtentsCoeff1.y, float(bb->mExtents[2]) * mExtentsCoeff1.z); + + // Perform BV-BV overlap test + if(!BoxBoxOverlap(ea, Pa, eb, Pb)) return; + + // Catch leaf status + BOOL BHasPosLeaf = b->HasPosLeaf(); + BOOL BHasNegLeaf = b->HasNegLeaf(); + + if(a->HasPosLeaf()) + { + FETCH_LEAF(a->GetPosPrimitive(), mIMesh0, mR0to1, mT0to1) + + if(BHasPosLeaf) PrimTestTriIndex(b->GetPosPrimitive()); + else _CollideTriBox(b->GetPos()); + + if(ContactFound()) return; + + if(BHasNegLeaf) PrimTestTriIndex(b->GetNegPrimitive()); + else _CollideTriBox(b->GetNeg()); + } + else + { + if(BHasPosLeaf) + { + FETCH_LEAF(b->GetPosPrimitive(), mIMesh1, mR1to0, mT1to0) + + _CollideBoxTri(a->GetPos()); + } + else _Collide(a->GetPos(), b->GetPos()); + + if(ContactFound()) return; + + if(BHasNegLeaf) + { + FETCH_LEAF(b->GetNegPrimitive(), mIMesh1, mR1to0, mT1to0) + + _CollideBoxTri(a->GetPos()); + } + else _Collide(a->GetPos(), b->GetNeg()); + } + + if(ContactFound()) return; + + if(a->HasNegLeaf()) + { + FETCH_LEAF(a->GetNegPrimitive(), mIMesh0, mR0to1, mT0to1) + + if(BHasPosLeaf) PrimTestTriIndex(b->GetPosPrimitive()); + else _CollideTriBox(b->GetPos()); + + if(ContactFound()) return; + + if(BHasNegLeaf) PrimTestTriIndex(b->GetNegPrimitive()); + else _CollideTriBox(b->GetNeg()); + } + else + { + if(BHasPosLeaf) + { + // ### That leaf has possibly already been fetched + FETCH_LEAF(b->GetPosPrimitive(), mIMesh1, mR1to0, mT1to0) + + _CollideBoxTri(a->GetNeg()); + } + else _Collide(a->GetNeg(), b->GetPos()); + + if(ContactFound()) return; + + if(BHasNegLeaf) + { + // ### That leaf has possibly already been fetched + FETCH_LEAF(b->GetNegPrimitive(), mIMesh1, mR1to0, mT1to0) + + _CollideBoxTri(a->GetNeg()); + } + else _Collide(a->GetNeg(), b->GetNeg()); + } +} diff --git a/Engine/lib/opcode/OPC_TreeCollider.h b/Engine/lib/opcode/OPC_TreeCollider.h new file mode 100644 index 000000000..a887983c3 --- /dev/null +++ b/Engine/lib/opcode/OPC_TreeCollider.h @@ -0,0 +1,244 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains code for a tree collider. + * \file OPC_TreeCollider.h + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_TREECOLLIDER_H__ +#define __OPC_TREECOLLIDER_H__ + + //! This structure holds cached information used by the algorithm. + //! Two model pointers and two colliding primitives are cached. Model pointers are assigned + //! to their respective meshes, and the pair of colliding primitives is used for temporal + //! coherence. That is, in case temporal coherence is enabled, those two primitives are + //! tested for overlap before everything else. If they still collide, we're done before + //! even entering the recursive collision code. + struct OPCODE_API BVTCache : Pair + { + //! Constructor + inline_ BVTCache() + { + ResetCache(); + ResetCountDown(); + } + + void ResetCache() + { + Model0 = null; + Model1 = null; + id0 = 0; + id1 = 1; +#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE ! + HullTest = true; + SepVector.pid = 0; + SepVector.qid = 0; + SepVector.SV = Point(1.0f, 0.0f, 0.0f); +#endif // __MESHMERIZER_H__ + } + + inline_ void ResetCountDown() + { +#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE ! + CountDown = 50; +#endif // __MESHMERIZER_H__ + } + + const Model* Model0; //!< Model for first object + const Model* Model1; //!< Model for second object + +#ifdef __MESHMERIZER_H__ // Collision hulls only supported within ICE ! + SVCache SepVector; + udword CountDown; + bool HullTest; +#endif // __MESHMERIZER_H__ + }; + + class OPCODE_API AABBTreeCollider : public Collider + { + public: + // Constructor / Destructor + AABBTreeCollider(); + virtual ~AABBTreeCollider(); + // Generic collision query + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Generic collision query for generic OPCODE models. After the call, access the results with: + * - GetContactStatus() + * - GetNbPairs() + * - GetPairs() + * + * \param cache [in] collision cache for model pointers and a colliding pair of primitives + * \param world0 [in] world matrix for first object, or null + * \param world1 [in] world matrix for second object, or null + * \return true if success + * \warning SCALE NOT SUPPORTED. The matrices must contain rotation & translation parts only. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + bool Collide(BVTCache& cache, const Matrix4x4* world0=null, const Matrix4x4* world1=null); + + // Collision queries + bool Collide(const AABBCollisionTree* tree0, const AABBCollisionTree* tree1, const Matrix4x4* world0=null, const Matrix4x4* world1=null, Pair* cache=null); + bool Collide(const AABBNoLeafTree* tree0, const AABBNoLeafTree* tree1, const Matrix4x4* world0=null, const Matrix4x4* world1=null, Pair* cache=null); + bool Collide(const AABBQuantizedTree* tree0, const AABBQuantizedTree* tree1, const Matrix4x4* world0=null, const Matrix4x4* world1=null, Pair* cache=null); + bool Collide(const AABBQuantizedNoLeafTree* tree0, const AABBQuantizedNoLeafTree* tree1, const Matrix4x4* world0=null, const Matrix4x4* world1=null, Pair* cache=null); + // Settings + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Settings: selects between full box-box tests or "SAT-lite" tests (where Class III axes are discarded) + * \param flag [in] true for full tests, false for coarse tests + * \see SetFullPrimBoxTest(bool flag) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetFullBoxBoxTest(bool flag) { mFullBoxBoxTest = flag; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Settings: selects between full triangle-box tests or "SAT-lite" tests (where Class III axes are discarded) + * \param flag [in] true for full tests, false for coarse tests + * \see SetFullBoxBoxTest(bool flag) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ void SetFullPrimBoxTest(bool flag) { mFullPrimBoxTest = flag; } + + // Stats + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Stats: gets the number of BV-BV overlap tests after a collision query. + * \see GetNbPrimPrimTests() + * \see GetNbBVPrimTests() + * \return the number of BV-BV tests performed during last query + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetNbBVBVTests() const { return mNbBVBVTests; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Stats: gets the number of Triangle-Triangle overlap tests after a collision query. + * \see GetNbBVBVTests() + * \see GetNbBVPrimTests() + * \return the number of Triangle-Triangle tests performed during last query + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetNbPrimPrimTests() const { return mNbPrimPrimTests; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Stats: gets the number of BV-Triangle overlap tests after a collision query. + * \see GetNbBVBVTests() + * \see GetNbPrimPrimTests() + * \return the number of BV-Triangle tests performed during last query + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetNbBVPrimTests() const { return mNbBVPrimTests; } + + // Data access + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the number of contacts after a collision query. + * \see GetContactStatus() + * \see GetPairs() + * \return the number of contacts / colliding pairs. + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetNbPairs() const { return mPairs.GetNbEntries()>>1; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the pairs of colliding triangles after a collision query. + * \see GetContactStatus() + * \see GetNbPairs() + * \return the list of colliding pairs (triangle indices) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ const Pair* GetPairs() const { return (const Pair*)mPairs.GetEntries(); } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Validates current settings. You should call this method after all the settings and callbacks have been defined for a collider. + * \return null if everything is ok, else a string describing the problem + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + override(Collider) const char* ValidateSettings(); + + protected: + // Colliding pairs + OPC_Container mPairs; //!< Pairs of colliding primitives + // User mesh interfaces + const MeshInterface* mIMesh0; //!< User-defined mesh interface for object0 + const MeshInterface* mIMesh1; //!< User-defined mesh interface for object1 + // Stats + udword mNbBVBVTests; //!< Number of BV-BV tests + udword mNbPrimPrimTests; //!< Number of Primitive-Primitive tests + udword mNbBVPrimTests; //!< Number of BV-Primitive tests + // Precomputed data + Matrix3x3 mAR; //!< Absolute rotation matrix + Matrix3x3 mR0to1; //!< Rotation from object0 to object1 + Matrix3x3 mR1to0; //!< Rotation from object1 to object0 + Point mT0to1; //!< Translation from object0 to object1 + Point mT1to0; //!< Translation from object1 to object0 + // Dequantization coeffs + Point mCenterCoeff0; + Point mExtentsCoeff0; + Point mCenterCoeff1; + Point mExtentsCoeff1; + // Leaf description + Point mLeafVerts[3]; //!< Triangle vertices + udword mLeafIndex; //!< Triangle index + // Settings + bool mFullBoxBoxTest; //!< Perform full BV-BV tests (true) or SAT-lite tests (false) + bool mFullPrimBoxTest; //!< Perform full Primitive-BV tests (true) or SAT-lite tests (false) + // Internal methods + + // Standard AABB trees + void _Collide(const AABBCollisionNode* b0, const AABBCollisionNode* b1); + // Quantized AABB trees + void _Collide(const AABBQuantizedNode* b0, const AABBQuantizedNode* b1, const Point& a, const Point& Pa, const Point& b, const Point& Pb); + // No-leaf AABB trees + void _CollideTriBox(const AABBNoLeafNode* b); + void _CollideBoxTri(const AABBNoLeafNode* b); + void _Collide(const AABBNoLeafNode* a, const AABBNoLeafNode* b); + // Quantized no-leaf AABB trees + void _CollideTriBox(const AABBQuantizedNoLeafNode* b); + void _CollideBoxTri(const AABBQuantizedNoLeafNode* b); + void _Collide(const AABBQuantizedNoLeafNode* a, const AABBQuantizedNoLeafNode* b); + // Overlap tests + void PrimTest(udword id0, udword id1); + inline_ void PrimTestTriIndex(udword id1); + inline_ void PrimTestIndexTri(udword id0); + + inline_ BOOL BoxBoxOverlap(const Point& ea, const Point& ca, const Point& eb, const Point& cb); + inline_ BOOL TriBoxOverlap(const Point& center, const Point& extents); + inline_ BOOL TriTriOverlap(const Point& V0, const Point& V1, const Point& V2, const Point& U0, const Point& U1, const Point& U2); + // Init methods + void InitQuery(const Matrix4x4* world0=null, const Matrix4x4* world1=null); + bool CheckTemporalCoherence(Pair* cache); + + inline_ BOOL Setup(const MeshInterface* mi0, const MeshInterface* mi1) + { + mIMesh0 = mi0; + mIMesh1 = mi1; + + if(!mIMesh0 || !mIMesh1) return FALSE; + + return TRUE; + } + }; + +#endif // __OPC_TREECOLLIDER_H__ diff --git a/Engine/lib/opcode/OPC_TriBoxOverlap.h b/Engine/lib/opcode/OPC_TriBoxOverlap.h new file mode 100644 index 000000000..b3a9bdee7 --- /dev/null +++ b/Engine/lib/opcode/OPC_TriBoxOverlap.h @@ -0,0 +1,339 @@ + +//! This macro quickly finds the min & max values among 3 variables +#define FINDMINMAX(x0, x1, x2, min, max) \ + min = max = x0; \ + if(x1max) max=x1; \ + if(x2max) max=x2; + +//! TO BE DOCUMENTED +inline_ BOOL planeBoxOverlap(const Point& normal, const float d, const Point& maxbox) +{ + Point vmin, vmax; + for(udword q=0;q<=2;q++) + { + if(normal[q]>0.0f) { vmin[q]=-maxbox[q]; vmax[q]=maxbox[q]; } + else { vmin[q]=maxbox[q]; vmax[q]=-maxbox[q]; } + } + if((normal|vmin)+d>0.0f) return FALSE; + if((normal|vmax)+d>=0.0f) return TRUE; + + return FALSE; +} + +//! TO BE DOCUMENTED +#define AXISTEST_X01(a, b, fa, fb) \ + min = a*v0.y - b*v0.z; \ + max = a*v2.y - b*v2.z; \ + if(min>max) {const float tmp=max; max=min; min=tmp; } \ + rad = fa * extents.y + fb * extents.z; \ + if(min>rad || max<-rad) return FALSE; + +//! TO BE DOCUMENTED +#define AXISTEST_X2(a, b, fa, fb) \ + min = a*v0.y - b*v0.z; \ + max = a*v1.y - b*v1.z; \ + if(min>max) {const float tmp=max; max=min; min=tmp; } \ + rad = fa * extents.y + fb * extents.z; \ + if(min>rad || max<-rad) return FALSE; + +//! TO BE DOCUMENTED +#define AXISTEST_Y02(a, b, fa, fb) \ + min = b*v0.z - a*v0.x; \ + max = b*v2.z - a*v2.x; \ + if(min>max) {const float tmp=max; max=min; min=tmp; } \ + rad = fa * extents.x + fb * extents.z; \ + if(min>rad || max<-rad) return FALSE; + +//! TO BE DOCUMENTED +#define AXISTEST_Y1(a, b, fa, fb) \ + min = b*v0.z - a*v0.x; \ + max = b*v1.z - a*v1.x; \ + if(min>max) {const float tmp=max; max=min; min=tmp; } \ + rad = fa * extents.x + fb * extents.z; \ + if(min>rad || max<-rad) return FALSE; + +//! TO BE DOCUMENTED +#define AXISTEST_Z12(a, b, fa, fb) \ + min = a*v1.x - b*v1.y; \ + max = a*v2.x - b*v2.y; \ + if(min>max) {const float tmp=max; max=min; min=tmp; } \ + rad = fa * extents.x + fb * extents.y; \ + if(min>rad || max<-rad) return FALSE; + +//! TO BE DOCUMENTED +#define AXISTEST_Z0(a, b, fa, fb) \ + min = a*v0.x - b*v0.y; \ + max = a*v1.x - b*v1.y; \ + if(min>max) {const float tmp=max; max=min; min=tmp; } \ + rad = fa * extents.x + fb * extents.y; \ + if(min>rad || max<-rad) return FALSE; + +// compute triangle edges +// - edges lazy evaluated to take advantage of early exits +// - fabs precomputed (half less work, possible since extents are always >0) +// - customized macros to take advantage of the null component +// - axis vector discarded, possibly saves useless movs +#define IMPLEMENT_CLASS3_TESTS \ + float rad; \ + float min, max; \ + \ + const float fey0 = fabsf(e0.y); \ + const float fez0 = fabsf(e0.z); \ + AXISTEST_X01(e0.z, e0.y, fez0, fey0); \ + const float fex0 = fabsf(e0.x); \ + AXISTEST_Y02(e0.z, e0.x, fez0, fex0); \ + AXISTEST_Z12(e0.y, e0.x, fey0, fex0); \ + \ + const float fey1 = fabsf(e1.y); \ + const float fez1 = fabsf(e1.z); \ + AXISTEST_X01(e1.z, e1.y, fez1, fey1); \ + const float fex1 = fabsf(e1.x); \ + AXISTEST_Y02(e1.z, e1.x, fez1, fex1); \ + AXISTEST_Z0(e1.y, e1.x, fey1, fex1); \ + \ + const Point e2 = mLeafVerts[0] - mLeafVerts[2]; \ + const float fey2 = fabsf(e2.y); \ + const float fez2 = fabsf(e2.z); \ + AXISTEST_X2(e2.z, e2.y, fez2, fey2); \ + const float fex2 = fabsf(e2.x); \ + AXISTEST_Y1(e2.z, e2.x, fez2, fex2); \ + AXISTEST_Z12(e2.y, e2.x, fey2, fex2); + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Triangle-Box overlap test using the separating axis theorem. + * This is the code from Tomas Möller, a bit optimized: + * - with some more lazy evaluation (faster path on PC) + * - with a tiny bit of assembly + * - with "SAT-lite" applied if needed + * - and perhaps with some more minor modifs... + * + * \param center [in] box center + * \param extents [in] box extents + * \return true if triangle & box overlap + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ BOOL AABBTreeCollider::TriBoxOverlap(const Point& center, const Point& extents) +{ + // Stats + mNbBVPrimTests++; + + // use separating axis theorem to test overlap between triangle and box + // need to test for overlap in these directions: + // 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle + // we do not even need to test these) + // 2) normal of the triangle + // 3) crossproduct(edge from tri, {x,y,z}-directin) + // this gives 3x3=9 more tests + + // move everything so that the boxcenter is in (0,0,0) + Point v0, v1, v2; + v0.x = mLeafVerts[0].x - center.x; + v1.x = mLeafVerts[1].x - center.x; + v2.x = mLeafVerts[2].x - center.x; + + // First, test overlap in the {x,y,z}-directions +#ifdef OPC_USE_FCOMI + // find min, max of the triangle in x-direction, and test for overlap in X + if(FCMin3(v0.x, v1.x, v2.x)>extents.x) return FALSE; + if(FCMax3(v0.x, v1.x, v2.x)<-extents.x) return FALSE; + + // same for Y + v0.y = mLeafVerts[0].y - center.y; + v1.y = mLeafVerts[1].y - center.y; + v2.y = mLeafVerts[2].y - center.y; + + if(FCMin3(v0.y, v1.y, v2.y)>extents.y) return FALSE; + if(FCMax3(v0.y, v1.y, v2.y)<-extents.y) return FALSE; + + // same for Z + v0.z = mLeafVerts[0].z - center.z; + v1.z = mLeafVerts[1].z - center.z; + v2.z = mLeafVerts[2].z - center.z; + + if(FCMin3(v0.z, v1.z, v2.z)>extents.z) return FALSE; + if(FCMax3(v0.z, v1.z, v2.z)<-extents.z) return FALSE; +#else + float min,max; + // Find min, max of the triangle in x-direction, and test for overlap in X + FINDMINMAX(v0.x, v1.x, v2.x, min, max); + if(min>extents.x || max<-extents.x) return FALSE; + + // Same for Y + v0.y = mLeafVerts[0].y - center.y; + v1.y = mLeafVerts[1].y - center.y; + v2.y = mLeafVerts[2].y - center.y; + + FINDMINMAX(v0.y, v1.y, v2.y, min, max); + if(min>extents.y || max<-extents.y) return FALSE; + + // Same for Z + v0.z = mLeafVerts[0].z - center.z; + v1.z = mLeafVerts[1].z - center.z; + v2.z = mLeafVerts[2].z - center.z; + + FINDMINMAX(v0.z, v1.z, v2.z, min, max); + if(min>extents.z || max<-extents.z) return FALSE; +#endif + // 2) Test if the box intersects the plane of the triangle + // compute plane equation of triangle: normal*x+d=0 + // ### could be precomputed since we use the same leaf triangle several times + const Point e0 = v1 - v0; + const Point e1 = v2 - v1; + const Point normal = e0 ^ e1; + const float d = -normal|v0; + if(!planeBoxOverlap(normal, d, extents)) return FALSE; + + // 3) "Class III" tests + if(mFullPrimBoxTest) + { + IMPLEMENT_CLASS3_TESTS + } + return TRUE; +} + +//! A dedicated version where the box is constant +inline_ BOOL OBBCollider::TriBoxOverlap() +{ + // Stats + mNbVolumePrimTests++; + + // Hook + const Point& extents = mBoxExtents; + const Point& v0 = mLeafVerts[0]; + const Point& v1 = mLeafVerts[1]; + const Point& v2 = mLeafVerts[2]; + + // use separating axis theorem to test overlap between triangle and box + // need to test for overlap in these directions: + // 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle + // we do not even need to test these) + // 2) normal of the triangle + // 3) crossproduct(edge from tri, {x,y,z}-directin) + // this gives 3x3=9 more tests + + // Box center is already in (0,0,0) + + // First, test overlap in the {x,y,z}-directions +#ifdef OPC_USE_FCOMI + // find min, max of the triangle in x-direction, and test for overlap in X + if(FCMin3(v0.x, v1.x, v2.x)>mBoxExtents.x) return FALSE; + if(FCMax3(v0.x, v1.x, v2.x)<-mBoxExtents.x) return FALSE; + + if(FCMin3(v0.y, v1.y, v2.y)>mBoxExtents.y) return FALSE; + if(FCMax3(v0.y, v1.y, v2.y)<-mBoxExtents.y) return FALSE; + + if(FCMin3(v0.z, v1.z, v2.z)>mBoxExtents.z) return FALSE; + if(FCMax3(v0.z, v1.z, v2.z)<-mBoxExtents.z) return FALSE; +#else + float min,max; + // Find min, max of the triangle in x-direction, and test for overlap in X + FINDMINMAX(v0.x, v1.x, v2.x, min, max); + if(min>mBoxExtents.x || max<-mBoxExtents.x) return FALSE; + + FINDMINMAX(v0.y, v1.y, v2.y, min, max); + if(min>mBoxExtents.y || max<-mBoxExtents.y) return FALSE; + + FINDMINMAX(v0.z, v1.z, v2.z, min, max); + if(min>mBoxExtents.z || max<-mBoxExtents.z) return FALSE; +#endif + // 2) Test if the box intersects the plane of the triangle + // compute plane equation of triangle: normal*x+d=0 + // ### could be precomputed since we use the same leaf triangle several times + const Point e0 = v1 - v0; + const Point e1 = v2 - v1; + const Point normal = e0 ^ e1; + const float d = -normal|v0; + if(!planeBoxOverlap(normal, d, mBoxExtents)) return FALSE; + + // 3) "Class III" tests - here we always do full tests since the box is a primitive (not a BV) + { + IMPLEMENT_CLASS3_TESTS + } + return TRUE; +} + +//! ...and another one, jeez +inline_ BOOL AABBCollider::TriBoxOverlap() +{ + // Stats + mNbVolumePrimTests++; + + // Hook + const Point& center = mBox.mCenter; + const Point& extents = mBox.mExtents; + + // use separating axis theorem to test overlap between triangle and box + // need to test for overlap in these directions: + // 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle + // we do not even need to test these) + // 2) normal of the triangle + // 3) crossproduct(edge from tri, {x,y,z}-directin) + // this gives 3x3=9 more tests + + // move everything so that the boxcenter is in (0,0,0) + Point v0, v1, v2; + v0.x = mLeafVerts[0].x - center.x; + v1.x = mLeafVerts[1].x - center.x; + v2.x = mLeafVerts[2].x - center.x; + + // First, test overlap in the {x,y,z}-directions +#ifdef OPC_USE_FCOMI + // find min, max of the triangle in x-direction, and test for overlap in X + if(FCMin3(v0.x, v1.x, v2.x)>extents.x) return FALSE; + if(FCMax3(v0.x, v1.x, v2.x)<-extents.x) return FALSE; + + // same for Y + v0.y = mLeafVerts[0].y - center.y; + v1.y = mLeafVerts[1].y - center.y; + v2.y = mLeafVerts[2].y - center.y; + + if(FCMin3(v0.y, v1.y, v2.y)>extents.y) return FALSE; + if(FCMax3(v0.y, v1.y, v2.y)<-extents.y) return FALSE; + + // same for Z + v0.z = mLeafVerts[0].z - center.z; + v1.z = mLeafVerts[1].z - center.z; + v2.z = mLeafVerts[2].z - center.z; + + if(FCMin3(v0.z, v1.z, v2.z)>extents.z) return FALSE; + if(FCMax3(v0.z, v1.z, v2.z)<-extents.z) return FALSE; +#else + float min,max; + // Find min, max of the triangle in x-direction, and test for overlap in X + FINDMINMAX(v0.x, v1.x, v2.x, min, max); + if(min>extents.x || max<-extents.x) return FALSE; + + // Same for Y + v0.y = mLeafVerts[0].y - center.y; + v1.y = mLeafVerts[1].y - center.y; + v2.y = mLeafVerts[2].y - center.y; + + FINDMINMAX(v0.y, v1.y, v2.y, min, max); + if(min>extents.y || max<-extents.y) return FALSE; + + // Same for Z + v0.z = mLeafVerts[0].z - center.z; + v1.z = mLeafVerts[1].z - center.z; + v2.z = mLeafVerts[2].z - center.z; + + FINDMINMAX(v0.z, v1.z, v2.z, min, max); + if(min>extents.z || max<-extents.z) return FALSE; +#endif + // 2) Test if the box intersects the plane of the triangle + // compute plane equation of triangle: normal*x+d=0 + // ### could be precomputed since we use the same leaf triangle several times + const Point e0 = v1 - v0; + const Point e1 = v2 - v1; + const Point normal = e0 ^ e1; + const float d = -normal|v0; + if(!planeBoxOverlap(normal, d, extents)) return FALSE; + + // 3) "Class III" tests - here we always do full tests since the box is a primitive (not a BV) + { + IMPLEMENT_CLASS3_TESTS + } + return TRUE; +} diff --git a/Engine/lib/opcode/OPC_TriTriOverlap.h b/Engine/lib/opcode/OPC_TriTriOverlap.h new file mode 100644 index 000000000..1e71c6a5c --- /dev/null +++ b/Engine/lib/opcode/OPC_TriTriOverlap.h @@ -0,0 +1,279 @@ + +//! if OPC_TRITRI_EPSILON_TEST is true then we do a check (if |dv|b) \ + { \ + const float c=a; \ + a=b; \ + b=c; \ + } + +//! Edge to edge test based on Franlin Antonio's gem: "Faster Line Segment Intersection", in Graphics Gems III, pp. 199-202 +#define EDGE_EDGE_TEST(V0, U0, U1) \ + Bx = U0[i0] - U1[i0]; \ + By = U0[i1] - U1[i1]; \ + Cx = V0[i0] - U0[i0]; \ + Cy = V0[i1] - U0[i1]; \ + f = Ay*Bx - Ax*By; \ + d = By*Cx - Bx*Cy; \ + if((f>0.0f && d>=0.0f && d<=f) || (f<0.0f && d<=0.0f && d>=f)) \ + { \ + const float e=Ax*Cy - Ay*Cx; \ + if(f>0.0f) \ + { \ + if(e>=0.0f && e<=f) return TRUE; \ + } \ + else \ + { \ + if(e<=0.0f && e>=f) return TRUE; \ + } \ + } + +//! TO BE DOCUMENTED +#define EDGE_AGAINST_TRI_EDGES(V0, V1, U0, U1, U2) \ +{ \ + float Bx,By,Cx,Cy,d,f; \ + const float Ax = V1[i0] - V0[i0]; \ + const float Ay = V1[i1] - V0[i1]; \ + /* test edge U0,U1 against V0,V1 */ \ + EDGE_EDGE_TEST(V0, U0, U1); \ + /* test edge U1,U2 against V0,V1 */ \ + EDGE_EDGE_TEST(V0, U1, U2); \ + /* test edge U2,U1 against V0,V1 */ \ + EDGE_EDGE_TEST(V0, U2, U0); \ +} + +//! TO BE DOCUMENTED +#define POINT_IN_TRI(V0, U0, U1, U2) \ +{ \ + /* is T1 completly inside T2? */ \ + /* check if V0 is inside tri(U0,U1,U2) */ \ + float a = U1[i1] - U0[i1]; \ + float b = -(U1[i0] - U0[i0]); \ + float c = -a*U0[i0] - b*U0[i1]; \ + float d0 = a*V0[i0] + b*V0[i1] + c; \ + \ + a = U2[i1] - U1[i1]; \ + b = -(U2[i0] - U1[i0]); \ + c = -a*U1[i0] - b*U1[i1]; \ + const float d1 = a*V0[i0] + b*V0[i1] + c; \ + \ + a = U0[i1] - U2[i1]; \ + b = -(U0[i0] - U2[i0]); \ + c = -a*U2[i0] - b*U2[i1]; \ + const float d2 = a*V0[i0] + b*V0[i1] + c; \ + if(d0*d1>0.0f) \ + { \ + if(d0*d2>0.0f) return TRUE; \ + } \ +} + +//! TO BE DOCUMENTED +BOOL CoplanarTriTri(const Point& n, const Point& v0, const Point& v1, const Point& v2, const Point& u0, const Point& u1, const Point& u2) +{ + float A[3]; + short i0,i1; + /* first project onto an axis-aligned plane, that maximizes the area */ + /* of the triangles, compute indices: i0,i1. */ + A[0] = fabsf(n[0]); + A[1] = fabsf(n[1]); + A[2] = fabsf(n[2]); + if(A[0]>A[1]) + { + if(A[0]>A[2]) + { + i0=1; /* A[0] is greatest */ + i1=2; + } + else + { + i0=0; /* A[2] is greatest */ + i1=1; + } + } + else /* A[0]<=A[1] */ + { + if(A[2]>A[1]) + { + i0=0; /* A[2] is greatest */ + i1=1; + } + else + { + i0=0; /* A[1] is greatest */ + i1=2; + } + } + + /* test all edges of triangle 1 against the edges of triangle 2 */ + EDGE_AGAINST_TRI_EDGES(v0, v1, u0, u1, u2); + EDGE_AGAINST_TRI_EDGES(v1, v2, u0, u1, u2); + EDGE_AGAINST_TRI_EDGES(v2, v0, u0, u1, u2); + + /* finally, test if tri1 is totally contained in tri2 or vice versa */ + POINT_IN_TRI(v0, u0, u1, u2); + POINT_IN_TRI(u0, v0, v1, v2); + + return FALSE; +} + +//! TO BE DOCUMENTED +#define NEWCOMPUTE_INTERVALS(VV0, VV1, VV2, D0, D1, D2, D0D1, D0D2, A, B, C, X0, X1) \ +{ \ + if(D0D1>0.0f) \ + { \ + /* here we know that D0D2<=0.0 */ \ + /* that is D0, D1 are on the same side, D2 on the other or on the plane */ \ + A=VV2; B=(VV0 - VV2)*D2; C=(VV1 - VV2)*D2; X0=D2 - D0; X1=D2 - D1; \ + } \ + else if(D0D2>0.0f) \ + { \ + /* here we know that d0d1<=0.0 */ \ + A=VV1; B=(VV0 - VV1)*D1; C=(VV2 - VV1)*D1; X0=D1 - D0; X1=D1 - D2; \ + } \ + else if(D1*D2>0.0f || D0!=0.0f) \ + { \ + /* here we know that d0d1<=0.0 or that D0!=0.0 */ \ + A=VV0; B=(VV1 - VV0)*D0; C=(VV2 - VV0)*D0; X0=D0 - D1; X1=D0 - D2; \ + } \ + else if(D1!=0.0f) \ + { \ + A=VV1; B=(VV0 - VV1)*D1; C=(VV2 - VV1)*D1; X0=D1 - D0; X1=D1 - D2; \ + } \ + else if(D2!=0.0f) \ + { \ + A=VV2; B=(VV0 - VV2)*D2; C=(VV1 - VV2)*D2; X0=D2 - D0; X1=D2 - D1; \ + } \ + else \ + { \ + /* triangles are coplanar */ \ + return CoplanarTriTri(N1, V0, V1, V2, U0, U1, U2); \ + } \ +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Triangle/triangle intersection test routine, + * by Tomas Moller, 1997. + * See article "A Fast Triangle-Triangle Intersection Test", + * Journal of Graphics Tools, 2(2), 1997 + * + * Updated June 1999: removed the divisions -- a little faster now! + * Updated October 1999: added {} to CROSS and SUB macros + * + * int NoDivTriTriIsect(float V0[3],float V1[3],float V2[3], + * float U0[3],float U1[3],float U2[3]) + * + * \param V0 [in] triangle 0, vertex 0 + * \param V1 [in] triangle 0, vertex 1 + * \param V2 [in] triangle 0, vertex 2 + * \param U0 [in] triangle 1, vertex 0 + * \param U1 [in] triangle 1, vertex 1 + * \param U2 [in] triangle 1, vertex 2 + * \return true if triangles overlap + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +inline_ BOOL AABBTreeCollider::TriTriOverlap(const Point& V0, const Point& V1, const Point& V2, const Point& U0, const Point& U1, const Point& U2) +{ + // Stats + mNbPrimPrimTests++; + + // Compute plane equation of triangle(V0,V1,V2) + Point E1 = V1 - V0; + Point E2 = V2 - V0; + const Point N1 = E1 ^ E2; + const float d1 =-N1 | V0; + // Plane equation 1: N1.X+d1=0 + + // Put U0,U1,U2 into plane equation 1 to compute signed distances to the plane + float du0 = (N1|U0) + d1; + float du1 = (N1|U1) + d1; + float du2 = (N1|U2) + d1; + + // Coplanarity robustness check +#ifdef OPC_TRITRI_EPSILON_TEST + if(fabsf(du0)0.0f && du0du2>0.0f) // same sign on all of them + not equal 0 ? + return FALSE; // no intersection occurs + + // Compute plane of triangle (U0,U1,U2) + E1 = U1 - U0; + E2 = U2 - U0; + const Point N2 = E1 ^ E2; + const float d2=-N2 | U0; + // plane equation 2: N2.X+d2=0 + + // put V0,V1,V2 into plane equation 2 + float dv0 = (N2|V0) + d2; + float dv1 = (N2|V1) + d2; + float dv2 = (N2|V2) + d2; + +#ifdef OPC_TRITRI_EPSILON_TEST + if(fabsf(dv0)0.0f && dv0dv2>0.0f) // same sign on all of them + not equal 0 ? + return FALSE; // no intersection occurs + + // Compute direction of intersection line + const Point D = N1^N2; + + // Compute and index to the largest component of D + float max=fabsf(D[0]); + short index=0; + float bb=fabsf(D[1]); + float cc=fabsf(D[2]); + if(bb>max) max=bb,index=1; + if(cc>max) max=cc,index=2; + + // This is the simplified projection onto L + const float vp0 = V0[index]; + const float vp1 = V1[index]; + const float vp2 = V2[index]; + + const float up0 = U0[index]; + const float up1 = U1[index]; + const float up2 = U2[index]; + + // Compute interval for triangle 1 + float a,b,c,x0,x1; + NEWCOMPUTE_INTERVALS(vp0,vp1,vp2,dv0,dv1,dv2,dv0dv1,dv0dv2,a,b,c,x0,x1); + + // Compute interval for triangle 2 + float d,e,f,y0,y1; + NEWCOMPUTE_INTERVALS(up0,up1,up2,du0,du1,du2,du0du1,du0du2,d,e,f,y0,y1); + + const float xx=x0*x1; + const float yy=y0*y1; + const float xxyy=xx*yy; + + float isect1[2], isect2[2]; + + float tmp=a*xxyy; + isect1[0]=tmp+b*x1*yy; + isect1[1]=tmp+c*x0*yy; + + tmp=d*xxyy; + isect2[0]=tmp+e*xx*y1; + isect2[1]=tmp+f*xx*y0; + + SORT(isect1[0],isect1[1]); + SORT(isect2[0],isect2[1]); + + if(isect1[1]HasPosLeaf()) mTouchedPrimitives->Add(node->GetPosPrimitive()); \ + else _Dump(node->GetPos()); \ + \ + if(ContactFound()) return; \ + \ + if(node->HasNegLeaf()) mTouchedPrimitives->Add(node->GetNegPrimitive()); \ + else _Dump(node->GetNeg()); \ +} + +#define IMPLEMENT_LEAFDUMP(type) \ +void VolumeCollider::_Dump(const type* node) \ +{ \ + if(node->IsLeaf()) \ + { \ + mTouchedPrimitives->Add(node->GetPrimitive()); \ + } \ + else \ + { \ + _Dump(node->GetPos()); \ + \ + if(ContactFound()) return; \ + \ + _Dump(node->GetNeg()); \ + } \ +} + +IMPLEMENT_NOLEAFDUMP(AABBNoLeafNode) +IMPLEMENT_NOLEAFDUMP(AABBQuantizedNoLeafNode) + +IMPLEMENT_LEAFDUMP(AABBCollisionNode) +IMPLEMENT_LEAFDUMP(AABBQuantizedNode) diff --git a/Engine/lib/opcode/OPC_VolumeCollider.h b/Engine/lib/opcode/OPC_VolumeCollider.h new file mode 100644 index 000000000..3dfa9a524 --- /dev/null +++ b/Engine/lib/opcode/OPC_VolumeCollider.h @@ -0,0 +1,138 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Contains base volume collider class. + * \file OPC_VolumeCollider.h + * \author Pierre Terdiman + * \date June, 2, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPC_VOLUMECOLLIDER_H__ +#define __OPC_VOLUMECOLLIDER_H__ + + struct OPCODE_API VolumeCache + { + VolumeCache() : Model(null) {} + ~VolumeCache() {} + + OPC_Container TouchedPrimitives; //!< Indices of touched primitives + const BaseModel* Model; //!< Owner + }; + + class OPCODE_API VolumeCollider : public Collider + { + public: + // Constructor / Destructor + VolumeCollider(); + virtual ~VolumeCollider() = 0; + + // Collision report + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the number of touched primitives after a collision query. + * \see GetContactStatus() + * \see GetTouchedPrimitives() + * \return the number of touched primitives + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetNbTouchedPrimitives() const { return mTouchedPrimitives ? mTouchedPrimitives->GetNbEntries() : 0; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Gets the list of touched primitives after a collision query. + * \see GetContactStatus() + * \see GetNbTouchedPrimitives() + * \return the list of touched primitives (primitive indices) + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ const udword* GetTouchedPrimitives() const { return mTouchedPrimitives ? mTouchedPrimitives->GetEntries() : null; } + + // Stats + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Stats: gets the number of Volume-BV overlap tests after a collision query. + * \see GetNbVolumePrimTests() + * \return the number of Volume-BV tests performed during last query + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetNbVolumeBVTests() const { return mNbVolumeBVTests; } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Stats: gets the number of Volume-Triangle overlap tests after a collision query. + * \see GetNbVolumeBVTests() + * \return the number of Volume-Triangle tests performed during last query + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + inline_ udword GetNbVolumePrimTests() const { return mNbVolumePrimTests; } + + // Settings + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Validates current settings. You should call this method after all the settings / callbacks have been defined for a collider. + * \return null if everything is ok, else a string describing the problem + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + override(Collider) const char* ValidateSettings(); + + protected: + // Touched primitives + OPC_Container* mTouchedPrimitives; //!< List of touched primitives + + // Dequantization coeffs + Point mCenterCoeff; + Point mExtentsCoeff; + // Stats + udword mNbVolumeBVTests; //!< Number of Volume-BV tests + udword mNbVolumePrimTests; //!< Number of Volume-Primitive tests + // Internal methods + void _Dump(const AABBCollisionNode* node); + void _Dump(const AABBNoLeafNode* node); + void _Dump(const AABBQuantizedNode* node); + void _Dump(const AABBQuantizedNoLeafNode* node); + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * Initializes a query + */ + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + override(Collider) inline_ void InitQuery() + { + // Reset stats & contact status + mNbVolumeBVTests = 0; + mNbVolumePrimTests = 0; + Collider::InitQuery(); + } + + inline_ BOOL IsCacheValid(VolumeCache& cache) + { + // We're going to do a volume-vs-model query. + if(cache.Model!=mCurrentModel) + { + // Cached list was for another model so we can't keep it + // Keep track of new owner and reset cache + cache.Model = mCurrentModel; + return FALSE; + } + else + { + // Same models, no problem + return TRUE; + } + } + }; + +#endif // __OPC_VOLUMECOLLIDER_H__ diff --git a/Engine/lib/opcode/Opcode.cpp b/Engine/lib/opcode/Opcode.cpp new file mode 100644 index 000000000..beb414057 --- /dev/null +++ b/Engine/lib/opcode/Opcode.cpp @@ -0,0 +1,64 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Main file for Opcode.dll. + * \file Opcode.cpp + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/* + Finding a good name is difficult! + Here's the draft for this lib.... Spooky, uh? + + VOID? Very Optimized Interference Detection + ZOID? Zappy's Optimized Interference Detection + CID? Custom/Clever Interference Detection + AID / ACID! Accurate Interference Detection + QUID? Quick Interference Detection + RIDE? Realtime Interference DEtection + WIDE? Wicked Interference DEtection (....) + GUID! + KID ! k-dop interference detection :) + OPCODE! OPtimized COllision DEtection +*/ + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#include "Opcode.h" + +bool Opcode::InitOpcode() +{ + Log("// Initializing OPCODE\n\n"); +// LogAPIInfo(); + return true; +} + +void ReleasePruningSorters(); +bool Opcode::CloseOpcode() +{ + Log("// Closing OPCODE\n\n"); + + ReleasePruningSorters(); + + return true; +} + +#ifdef ICE_MAIN + +void ModuleAttach(HINSTANCE hinstance) +{ +} + +void ModuleDetach() +{ +} + +#endif \ No newline at end of file diff --git a/Engine/lib/opcode/Opcode.h b/Engine/lib/opcode/Opcode.h new file mode 100644 index 000000000..9069a0ac5 --- /dev/null +++ b/Engine/lib/opcode/Opcode.h @@ -0,0 +1,80 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * OPCODE - Optimized Collision Detection + * Copyright (C) 2001 Pierre Terdiman + * Homepage: http://www.codercorner.com/Opcode.htm + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/** + * Main file for Opcode.dll. + * \file Opcode.h + * \author Pierre Terdiman + * \date March, 20, 2001 + */ +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Include Guard +#ifndef __OPCODE_H__ +#define __OPCODE_H__ + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Compilation messages +#if defined(OPCODE_EXPORTS) + #pragma message("Compiling OPCODE") +#elif !defined(OPCODE_EXPORTS) + #pragma message("Using OPCODE") +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Preprocessor +#ifndef ICE_NO_DLL + #ifdef OPCODE_EXPORTS + #define OPCODE_API __declspec(dllexport) + #else + #define OPCODE_API __declspec(dllimport) + #endif +#else + #define OPCODE_API +#endif + + #include "OPC_IceHook.h" + + namespace Opcode + { + // Bulk-of-the-work + #include "OPC_Settings.h" + #include "OPC_Common.h" + #include "OPC_MeshInterface.h" + // Builders + #include "OPC_TreeBuilders.h" + // Trees + #include "OPC_AABBTree.h" + #include "OPC_OptimizedTree.h" + // Models + #include "OPC_BaseModel.h" + #include "OPC_Model.h" + #include "OPC_HybridModel.h" + // Colliders + #include "OPC_Collider.h" + #include "OPC_VolumeCollider.h" + #include "OPC_TreeCollider.h" + #include "OPC_RayCollider.h" + #include "OPC_SphereCollider.h" + #include "OPC_OBBCollider.h" + #include "OPC_AABBCollider.h" + #include "OPC_LSSCollider.h" + #include "OPC_PlanesCollider.h" + // Usages + #include "OPC_Picking.h" + // Sweep-and-prune + #include "OPC_BoxPruning.h" + #include "OPC_SweepAndPrune.h" + + FUNCTION OPCODE_API bool InitOpcode(); + FUNCTION OPCODE_API bool CloseOpcode(); + } + +#endif // __OPCODE_H__ diff --git a/Engine/lib/opcode/ReadMe.txt b/Engine/lib/opcode/ReadMe.txt new file mode 100644 index 000000000..8a39eff43 --- /dev/null +++ b/Engine/lib/opcode/ReadMe.txt @@ -0,0 +1,171 @@ + + OPCODE distribution 1.3 (june 2003) + ----------------------- + + New in Opcode 1.3: + - fixed the divide by 0 bug that was happening when all centers where located on a coordinate axis (thanks to Jorrit T) + - linearized "complete" vanilla AABB trees + - ANSI-compliant "for" loops (for the ones porting it to Linux...) + - callbacks & pointers moved to mesh interface + - support for triangle & vertex strides + - optimized the sphere-triangle overlap code a bit + - dynamic trees (refit) + - more builders + - ValidateSubdivision in builders + - LSS collider + - primitive-bv tests can now be skipped in most volume queries + - temporal coherence now also works for airborne objects + - temporal coherence completed for boxes / all contacts, LSS, etc + - ray-collider now uses a callback + - some common "usages" have been introduced (only picking for now) + - SPLIT_COMPLETE removed (now implicitely using mLimit = 1) + - hybrid collision models + - sweep-and-prune code added, moved from my old Z-Collide lib + - it now works with meshes made of only 1 triangle (except in mesh-mesh case!) + + Disclaimer: + + - I forced myself to actually *do* the release today no matter what. Else it would never have been done. That's + why the code may not be very polished. I also removed a *lot* of things (more usages, distance queries, etc...) + that weren't ready for prime-time (or that were linked to too many of my supporting libs) + + - Some comments may also be obsolete here and there. The old User Manual for Opcode 1.2 may not fit version 1.3 + either, since there's a new "mesh interface" to support strides, etc. + + - Everything in the "Ice" directory has been hacked out of my engine and edited until everything compiled. Don't + expect anything out there to be cute or something. In particular, some CPP files are not even included when not + needed, so you can expect some linker errors if you try messing around with them... + + Otherwise, it should be just like previous version, only better. In particular, hybrid models can be very + memory-friendly (sometimes using like 10 times less ram than the best trees from version 1.2). The possible + speed hit is often invisible (if it even exists), especially using temporal coherence in "all contacts" mode. + (Admittedly, this depends on your particular usage pattern / what you do on collided triangles). + + The sweep-and-prune code is similar to the "vanilla" version found in V-Collide (but that one's better IMHO...) + The simple "radix" version is often just as good, see for yourself. + + OPCODE distribution 1.2 (august 2002) + ----------------------- + + New in Opcode 1.2: + - new VolumeCollider base class + - simplified callback setup + - you can now use callbacks or pointers (setup at compile time) + - destination array not needed anymore in the RayCollider (faster in-out tests) + - renamed classes: AABBRayCollider => RayCollider, AABBSphereCollider => SphereCollider + - the sphere query now only returns a list of faces (extra info discarded). On the other hand it's a lot faster. + - OBB, AABB and planes queries. Original OBB and AABB queries contributed by Erwin de Vries. + - cosmetic changes in OPC_BoxBoxOverlap.h contributed by Gottfried Chen + - some inlining problems fixed + - faster ray-mesh tests using the separating axis theorem + - new split value in AABB tree construction (contributed by Igor Kravtchenko). Provides faster queries most of the time. + - improved temporal coherence for sphere & AABB queries (works in "All contacts" mode) + + Notes: + + - Everything in the "Ice code" directory (in VC++) is basically copy-pasted from my engine, with a lot + of code removed until there was no link error anymore. Don't expect those files to be cute or anything, + they've never been meant to be released and they're often updated/modified/messy. + - Some experimental features have been removed as well. Else I would never have released the 1.2... + - Not as polished/optimal as I would like it to be, but that's life. I promised myself to release it + before october 2002 (one YEAR later ?!).... That's the only reason why it's there. + - Some people reported ColDet was faster. Uh, come on. They were using Opcode in + "All contacts" mode whereas ColDet was doing "first contact"... + + OPCODE distribution 1.1 (october 2001) + ----------------------- + + New in Opcode 1.1: + - stabbing queries + - sphere queries + - abtract base class for colliders + - settings validation methods + - compilation flags now grouped in OPC_Settings.h + - smaller files, new VC++ virtual dirs (cleaner) + + Notes: + + - "override(baseclass)" is a personal cosmetic thing. It's the same as "virtual", but provides more info. + - I code in 1600*1200, so some lines may look a bit long.. + - This version is not as polished as the previous one due to lack of time. The stabbing & sphere queries + can still be optimized: for example by trying other atomic overlap tests. I'm using my first ray-AABB + code, but the newer one seems better. Tim Schröder's one is good as well. See: www.codercorner.com/RayAABB.cpp + - The trees can easily be compressed even more, I save this for later (lack of time, lack of time!) + - I removed various tests before releasing this one: + - a separation line, a.k.a. "front" in QuickCD, because gains were unclear + - distance queries in a PQP style, because it was way too slow + - support for deformable models, too slow as well + - You can easily use Opcode to do your player-vs-world collision detection, in a Nettle/Telemachos way. + If someone out there wants to donate some art / level for the cause, I'd be glad to release a demo. (current + demo uses copyrighted art I'm not allowed to spread) + - Sorry for the lack of real docs and/or solid examples. I just don't have enough time. + + OPCODE distribution 1.0 (march 2001) + ----------------------- + + - First release + + =============================================================================== + + WHAT ? + + OPCODE means OPtimized COllision DEtection. + So this is a collision detection package similar to RAPID. Here's a + quick list of features: + + - C++ interface, developed for Windows systems using VC++ 6.0 + - Works on arbitrary meshes (convex or non-convex), even polygon soups + - Current implementation uses AABB-trees + - Introduces Primitive-BV overlap tests during recursive collision queries (whereas + standard libraries only rely on Primitive-Primitive and BV-BV tests) + - Introduces no-leaf trees, i.e. collision trees whose leaf nodes have been removed + - Supports collision queries on quantized trees (decompressed on-the-fly) + - Supports "first contact" or "all contacts" modes (ŕ la RAPID) + - Uses temporal coherence for "first contact" mode (~10 to 20 times faster, useful + in rigid body simulation during bisection) + - Memory footprint is 7.2 times smaller than RAPID's one, which is ideal for console + games with limited ram (actually, if you use the unmodified RAPID code using double + precision, it's more like 13 times smaller...) + - And yet it often runs faster than RAPID (according to RDTSC, sometimes more than 5 + times faster when objects are deeply overlapping) + - Performance is usually close to RAPID's one in close-proximity situations + - Stabbing, planes & volume queries (sphere, AABB, OBB, LSS) + - Sweep-and-prune + - Now works with deformable meshes + - Hybrid trees + + + What it can be used for: + - standard mesh-mesh collision detection (similar to RAPID, SOLID, QuickCD, PQP, ColDet...) + - N-body collisions (similar to V-Collide) + - camera-vs-world collisions (similar to Telemachos/Paul Nettle/Stan Melax articles) + - shadow feelers to speed up lightmap computations + - in-out tests to speed up voxelization processes + - picking + - rigid body simulation + - view frustum culling + - etc + + WHY ? + + - Because RAPID uses too many bytes. + - Because the idea was nice... + + WHEN ? + + It's been coded in march 2001 following a thread on the GD-Algorithms list. + + GDAlgorithms-list mailing list + GDAlgorithms-list@lists.sourceforge.net + http://lists.sourceforge.net/lists/listinfo/gdalgorithms-list + + WHO ? + + Pierre Terdiman + June, 1, 2003 + + p.terdiman@wanadoo.fr + p.terdiman@codercorner.com + + http://www.codercorner.com + http://www.codercorner.com/Opcode.htm diff --git a/Engine/lib/opcode/TemporalCoherence.txt b/Engine/lib/opcode/TemporalCoherence.txt new file mode 100644 index 000000000..fb8593188 --- /dev/null +++ b/Engine/lib/opcode/TemporalCoherence.txt @@ -0,0 +1,32 @@ + +> Hi John, +> +> I know I'll forget to tell you this if I don't write it right now.... +> +> >(2) How is the receiving geometry for the shadow decided? +> +> I wrote about an LSS-test but actually performing a new VFC test (from the +> light's view) is the same. In both cases, here's a trick to take advantage +> of temporal coherence : test the world against a slightly larger than +> necessary LSS or frustum. Keep the list of touched surfaces. Then next +> frame, if the new volume is still contained within the previous one used +for +> the query, you can reuse the same list immediately. Actually it's a bit +> similar to what you did in your sphere-tree, I think. Anyway, now the +O(log +> N) VFC is O(1) for some frames. It's not worth it for the "real" VFC, but +> when you have N virtual frustum to test to drop N shadows, that's another +> story. +> +> Two downsides: +> - You need more ram to keep track of one list of meshes / shadow, but +> usually it's not a lot. +> - By using a larger volume for the query you possibly touch more +> faces/surfaces, which will be rendered in the shadow pass. Usually it's +not +> a problem either since rendering is simply faster than geometric queries +> those days. But of course, "your mileage may vary". +> +> Happy new year ! +> +> Pierre diff --git a/Engine/lib/openal/FreeBSD/al/al.h b/Engine/lib/openal/FreeBSD/al/al.h new file mode 100644 index 000000000..a4132f0a0 --- /dev/null +++ b/Engine/lib/openal/FreeBSD/al/al.h @@ -0,0 +1,491 @@ +#ifndef _AL_H_ +#define _AL_H_ + +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2000 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include "altypes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 + #ifdef _LIB + #define ALAPI __declspec(dllexport) + #else + #define ALAPI __declspec(dllimport) + #endif + #define ALAPIENTRY __cdecl + #define AL_CALLBACK +#else + #ifdef TARGET_OS_MAC + #if TARGET_OS_MAC + #pragma export on + #endif + #endif + #define ALAPI + #define ALAPIENTRY __cdecl + #define AL_CALLBACK +#endif + +#define OPENAL + +#ifndef AL_NO_PROTOTYPES + +/** + * OpenAL Maintenance Functions + * Initialization and exiting. + * State Management and Query. + * Error Handling. + * Extension Support. + */ + +/** State management. */ +ALAPI ALvoid ALAPIENTRY alEnable( ALenum capability ); +ALAPI ALvoid ALAPIENTRY alDisable( ALenum capability ); +ALAPI ALboolean ALAPIENTRY alIsEnabled( ALenum capability ); + +/** Application preferences for driver performance choices. */ +ALAPI ALvoid ALAPIENTRY alHint( ALenum target, ALenum mode ); + +/** State retrieval. */ +ALAPI ALboolean ALAPIENTRY alGetBoolean( ALenum param ); +ALAPI ALint ALAPIENTRY alGetInteger( ALenum param ); +ALAPI ALfloat ALAPIENTRY alGetFloat( ALenum param ); +ALAPI ALdouble ALAPIENTRY alGetDouble( ALenum param ); +ALAPI ALvoid ALAPIENTRY alGetBooleanv( ALenum param, ALboolean* data ); +ALAPI ALvoid ALAPIENTRY alGetIntegerv( ALenum param, ALint* data ); +ALAPI ALvoid ALAPIENTRY alGetFloatv( ALenum param, ALfloat* data ); +ALAPI ALvoid ALAPIENTRY alGetDoublev( ALenum param, ALdouble* data ); +ALAPI ALubyte* ALAPIENTRY alGetString( ALenum param ); + +/** + * Error support. + * Obtain the most recent error generated in the AL state machine. + */ +ALAPI ALenum ALAPIENTRY alGetError( ALvoid ); + + +/** + * Extension support. + * Obtain the address of a function (usually an extension) + * with the name fname. All addresses are context-independent. + */ +ALAPI ALboolean ALAPIENTRY alIsExtensionPresent( ALubyte* fname ); + + +/** + * Extension support. + * Obtain the address of a function (usually an extension) + * with the name fname. All addresses are context-independent. + */ +ALAPI ALvoid* ALAPIENTRY alGetProcAddress( ALubyte* fname ); + + +/** + * Extension support. + * Obtain the integer value of an enumeration (usually an extension) with the name ename. + */ +ALAPI ALenum ALAPIENTRY alGetEnumValue( ALubyte* ename ); + + + + +/** + * LISTENER + * Listener is the sample position for a given context. + * The multi-channel (usually stereo) output stream generated + * by the mixer is parametrized by this Listener object: + * its position and velocity relative to Sources, within + * occluder and reflector geometry. + */ + + + +/** + * + * Listener Environment: default 0. + */ +ALAPI ALvoid ALAPIENTRY alListeneri( ALenum param, ALint value ); + + +/** + * + * Listener Gain: default 1.0f. + */ +ALAPI ALvoid ALAPIENTRY alListenerf( ALenum param, ALfloat value ); + + +/** + * + * Listener Position. + * Listener Velocity. + */ +ALAPI ALvoid ALAPIENTRY alListener3f( ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 ); + + +/** + * + * Listener Position: ALfloat[3] + * Listener Velocity: ALfloat[3] + * Listener Orientation: ALfloat[6] (forward and up vector). + */ +ALAPI ALvoid ALAPIENTRY alListenerfv( ALenum param, ALfloat* values ); + +ALAPI ALvoid ALAPIENTRY alGetListeneri( ALenum param, ALint* value ); +ALAPI ALvoid ALAPIENTRY alGetListenerf( ALenum param, ALfloat* value ); +ALAPI ALvoid ALAPIENTRY alGetListener3f( ALenum param, ALfloat* v1, ALfloat* v2, ALfloat* v3 ); +ALAPI ALvoid ALAPIENTRY alGetListenerfv( ALenum param, ALfloat* values ); + + +/** + * SOURCE + * Source objects are by default localized. Sources + * take the PCM data provided in the specified Buffer, + * apply Source-specific modifications, and then + * submit them to be mixed according to spatial + * arrangement etc. + */ + + + +/** Create Source objects. */ +ALAPI ALvoid ALAPIENTRY alGenSources( ALsizei n, ALuint* sources ); + +/** Delete Source objects. */ +ALAPI ALvoid ALAPIENTRY alDeleteSources( ALsizei n, ALuint* sources ); + +/** Verify a handle is a valid Source. */ +ALAPI ALboolean ALAPIENTRY alIsSource( ALuint id ); + +/** Set an integer parameter for a Source object. */ +ALAPI ALvoid ALAPIENTRY alSourcei( ALuint source, ALenum param, ALint value ); +ALAPI ALvoid ALAPIENTRY alSourcef( ALuint source, ALenum param, ALfloat value ); +ALAPI ALvoid ALAPIENTRY alSource3f( ALuint source, ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 ); +ALAPI ALvoid ALAPIENTRY alSourcefv( ALuint source, ALenum param, ALfloat* values ); + +/** Get an integer parameter for a Source object. */ +ALAPI ALvoid ALAPIENTRY alGetSourcei( ALuint source, ALenum param, ALint* value ); +ALAPI ALvoid ALAPIENTRY alGetSourcef( ALuint source, ALenum param, ALfloat* value ); +ALAPI ALvoid ALAPIENTRY alGetSource3f( ALuint source, ALenum param, ALfloat* v1, ALfloat* v2, ALfloat* v3 ); +ALAPI ALvoid ALAPIENTRY alGetSourcefv( ALuint source, ALenum param, ALfloat* values ); + +ALAPI ALvoid ALAPIENTRY alSourcePlayv( ALsizei n, ALuint *sources ); +ALAPI ALvoid ALAPIENTRY alSourcePausev( ALsizei n, ALuint *sources ); +ALAPI ALvoid ALAPIENTRY alSourceStopv( ALsizei n, ALuint *sources ); +ALAPI ALvoid ALAPIENTRY alSourceRewindv(ALsizei n,ALuint *sources); + +/** Activate a source, start replay. */ +ALAPI ALvoid ALAPIENTRY alSourcePlay( ALuint source ); + +/** + * Pause a source, + * temporarily remove it from the mixer list. + */ +ALAPI ALvoid ALAPIENTRY alSourcePause( ALuint source ); + +/** + * Stop a source, + * temporarily remove it from the mixer list, + * and reset its internal state to pre-Play. + * To remove a Source completely, it has to be + * deleted following Stop, or before Play. + */ +ALAPI ALvoid ALAPIENTRY alSourceStop( ALuint source ); + +/** + * Rewinds a source, + * temporarily remove it from the mixer list, + * and reset its internal state to pre-Play. + */ +ALAPI ALvoid ALAPIENTRY alSourceRewind( ALuint source ); + + + +/** + * BUFFER + * Buffer objects are storage space for sample data. + * Buffers are referred to by Sources. There can be more than + * one Source using the same Buffer data. If Buffers have + * to be duplicated on a per-Source basis, the driver has to + * take care of allocation, copying, and deallocation as well + * as propagating buffer data changes. + */ + + + + +/** Buffer object generation. */ +ALAPI ALvoid ALAPIENTRY alGenBuffers( ALsizei n, ALuint* buffers ); +ALAPI ALvoid ALAPIENTRY alDeleteBuffers( ALsizei n, ALuint* buffers ); +ALAPI ALboolean ALAPIENTRY alIsBuffer( ALuint buffer ); + +/** + * Specify the data to be filled into a buffer. + */ +ALAPI ALvoid ALAPIENTRY alBufferData( ALuint buffer, + ALenum format, + ALvoid* data, + ALsizei size, + ALsizei freq ); + + +ALAPI ALvoid ALAPIENTRY alGetBufferi( ALuint buffer, ALenum param, ALint* value ); +ALAPI ALvoid ALAPIENTRY alGetBufferf( ALuint buffer, ALenum param, ALfloat* value ); + + + + +/** + * Queue stuff + */ + +ALAPI ALvoid ALAPIENTRY alSourceQueueBuffers( ALuint source, ALsizei n, ALuint* buffers ); +ALAPI ALvoid ALAPIENTRY alSourceUnqueueBuffers( ALuint source, ALsizei n, ALuint* buffers ); + +/** + * Knobs and dials + */ +ALAPI ALvoid ALAPIENTRY alDistanceModel( ALenum value ); +ALAPI ALvoid ALAPIENTRY alDopplerFactor( ALfloat value ); +ALAPI ALvoid ALAPIENTRY alDopplerVelocity( ALfloat value ); + +#else /* AL_NO_PROTOTYPES */ + +/** + * OpenAL Maintenance Functions + * Initialization and exiting. + * State Management and Query. + * Error Handling. + * Extension Support. + */ + +/** State management. */ +ALAPI ALvoid ALAPIENTRY (*alEnable)( ALenum capability ); +ALAPI ALvoid ALAPIENTRY (*alDisable)( ALenum capability ); +ALAPI ALboolean ALAPIENTRY (*alIsEnabled)( ALenum capability ); + +/** Application preferences for driver performance choices. */ +ALAPI ALvoid ALAPIENTRY (*alHint)( ALenum target, ALenum mode ); + +/** State retrieval. */ +ALAPI ALboolean ALAPIENTRY (*alGetBoolean)( ALenum param ); +ALAPI ALint ALAPIENTRY (*alGetInteger)( ALenum param ); +ALAPI ALfloat ALAPIENTRY (*alGetFloat)( ALenum param ); +ALAPI ALdouble ALAPIENTRY (*alGetDouble)( ALenum param ); +ALAPI ALvoid ALAPIENTRY (*alGetBooleanv)( ALenum param, ALboolean* data ); +ALAPI ALvoid ALAPIENTRY (*alGetIntegerv)( ALenum param, ALint* data ); +ALAPI ALvoid ALAPIENTRY (*alGetFloatv)( ALenum param, ALfloat* data ); +ALAPI ALvoid ALAPIENTRY (*alGetDoublev)( ALenum param, ALdouble* data ); +ALAPI ALubyte* ALAPIENTRY (*alGetString)( ALenum param ); + +/** + * Error support. + * Obtain the most recent error generated in the AL state machine. + */ +ALAPI ALenum ALAPIENTRY (*alGetError)( ALvoid ); + + +/** + * Extension support. + * Obtain the address of a function (usually an extension) + * with the name fname. All addresses are context-independent. + */ +ALAPI ALboolean ALAPIENTRY (*alIsExtensionPresent)( ALubyte* fname ); + + +/** + * Extension support. + * Obtain the address of a function (usually an extension) + * with the name fname. All addresses are context-independent. + */ +ALAPI ALvoid* ALAPIENTRY (*alGetProcAddress)( ALubyte* fname ); + + +/** + * Extension support. + * Obtain the integer value of an enumeration (usually an extension) with the name ename. + */ +ALAPI ALenum ALAPIENTRY (*alGetEnumValue)( ALubyte* ename ); + + + + +/** + * LISTENER + * Listener is the sample position for a given context. + * The multi-channel (usually stereo) output stream generated + * by the mixer is parametrized by this Listener object: + * its position and velocity relative to Sources, within + * occluder and reflector geometry. + */ + + + +/** + * + * Listener Environment: default 0. + */ +ALAPI ALvoid ALAPIENTRY (*alListeneri)( ALenum param, ALint value ); + + +/** + * + * Listener Gain: default 1.0f. + */ +ALAPI ALvoid ALAPIENTRY (*alListenerf)( ALenum param, ALfloat value ); + + +/** + * + * Listener Position. + * Listener Velocity. + */ +ALAPI ALvoid ALAPIENTRY (*alListener3f)( ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 ); + + +/** + * + * Listener Position: ALfloat[3] + * Listener Velocity: ALfloat[3] + * Listener Orientation: ALfloat[6] (forward and up vector). + */ +ALAPI ALvoid ALAPIENTRY (*alListenerfv)( ALenum param, ALfloat* values ); + +ALAPI ALvoid ALAPIENTRY (*alGetListeneri)( ALenum param, ALint* value ); +ALAPI ALvoid ALAPIENTRY (*alGetListenerf)( ALenum param, ALfloat* value ); +ALAPI ALvoid ALAPIENTRY (*alGetListener3f)( ALenum param, ALfloat* v1, ALfloat* v2, ALfloat* v3 ); +ALAPI ALvoid ALAPIENTRY (*alGetListenerfv)( ALenum param, ALfloat* values ); + + +/** + * SOURCE + * Source objects are by default localized. Sources + * take the PCM data provided in the specified Buffer, + * apply Source-specific modifications, and then + * submit them to be mixed according to spatial + * arrangement etc. + */ + + + +/** Create Source objects. */ +ALAPI ALvoid ALAPIENTRY (*alGenSources)( ALsizei n, ALuint* sources ); + +/** Delete Source objects. */ +ALAPI ALvoid ALAPIENTRY (*alDeleteSources)( ALsizei n, ALuint* sources ); + +/** Verify a handle is a valid Source. */ +ALAPI ALboolean ALAPIENTRY (*alIsSource)( ALuint id ); + +/** Set an integer parameter for a Source object. */ +ALAPI ALvoid ALAPIENTRY (*alSourcei)( ALuint source, ALenum param, ALint value ); +ALAPI ALvoid ALAPIENTRY (*alSourcef)( ALuint source, ALenum param, ALfloat value ); +ALAPI ALvoid ALAPIENTRY (*alSource3f)( ALuint source, ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 ); +ALAPI ALvoid ALAPIENTRY (*alSourcefv)( ALuint source, ALenum param, ALfloat* values ); + +/** Get an integer parameter for a Source object. */ +ALAPI ALvoid ALAPIENTRY (*alGetSourcei)( ALuint source, ALenum param, ALint* value ); +ALAPI ALvoid ALAPIENTRY (*alGetSourcef)( ALuint source, ALenum param, ALfloat* value ); +ALAPI ALvoid ALAPIENTRY (*alGetSourcefv)( ALuint source, ALenum param, ALfloat* values ); + +ALAPI ALvoid ALAPIENTRY (*alSourcePlayv)( ALsizei n, ALuint *sources ); +ALAPI ALvoid ALAPIENTRY (*alSourceStopv)( ALsizei n, ALuint *sources ); + +/** Activate a source, start replay. */ +ALAPI ALvoid ALAPIENTRY (*alSourcePlay)( ALuint source ); + +/** + * Pause a source, + * temporarily remove it from the mixer list. + */ +ALAPI ALvoid ALAPIENTRY (*alSourcePause)( ALuint source ); + +/** + * Stop a source, + * temporarily remove it from the mixer list, + * and reset its internal state to pre-Play. + * To remove a Source completely, it has to be + * deleted following Stop, or before Play. + */ +ALAPI ALvoid ALAPIENTRY (*alSourceStop)( ALuint source ); + + + +/** + * BUFFER + * Buffer objects are storage space for sample data. + * Buffers are referred to by Sources. There can be more than + * one Source using the same Buffer data. If Buffers have + * to be duplicated on a per-Source basis, the driver has to + * take care of allocation, copying, and deallocation as well + * as propagating buffer data changes. + */ + + + + +/** Buffer object generation. */ +ALAPI ALvoid ALAPIENTRY (*alGenBuffers)( ALsizei n, ALuint* buffers ); +ALAPI ALvoid ALAPIENTRY (*alDeleteBuffers)( ALsizei n, ALuint* buffers ); +ALAPI ALboolean ALAPIENTRY (*alIsBuffer)( ALuint buffer ); + +/** + * Specify the data to be filled into a buffer. + */ +ALAPI ALvoid ALAPIENTRY (*alBufferData)( ALuint buffer, + ALenum format, + ALvoid* data, + ALsizei size, + ALsizei freq ); + +ALAPI ALvoid ALAPIENTRY (*alGetBufferi)( ALuint buffer, ALenum param, ALint* value ); +ALAPI ALvoid ALAPIENTRY (*alGetBufferf)( ALuint buffer, ALenum param, ALfloat* value ); + + + + +/** + * Queue stuff + */ +ALAPI ALvoid ALAPIENTRY (*alSourceQueueBuffers)( ALuint source, ALsizei n, ALuint* buffers ); +ALAPI ALvoid ALAPIENTRY (*alSourceUnqueueBuffers)( ALuint source, ALsizei n, ALuint* buffers ); + +/** + * Knobs and dials + */ +ALAPI ALvoid ALAPIENTRY (*alDistanceModel)( ALenum value ); +ALAPI ALvoid ALAPIENTRY (*alDopplerFactor)( ALfloat value ); +ALAPI ALvoid ALAPIENTRY (*alDopplerVelocity)( ALfloat value ); + +#endif /* AL_NO_PROTOTYPES */ + +#ifdef TARGET_OS_MAC + #if TARGET_OS_MAC + #pragma export off + #endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Engine/lib/openal/FreeBSD/al/al_func.h b/Engine/lib/openal/FreeBSD/al/al_func.h new file mode 100644 index 000000000..9a280ad2a --- /dev/null +++ b/Engine/lib/openal/FreeBSD/al/al_func.h @@ -0,0 +1,69 @@ + +AL_FUNCTION(ALvoid, alEnable, ( ALenum capability ), return; ) +AL_FUNCTION(ALvoid, alDisable, ( ALenum capability ), return; ) +AL_FUNCTION(ALboolean, alIsEnabled, ( ALenum capability ), return AL_FALSE; ) + +//AL_FUNCTION(ALvoid, alHint, ( ALenum target, ALenum mode ), return; ) + +AL_FUNCTION(ALboolean, alGetBoolean, ( ALenum param ), return AL_FALSE; ) +AL_FUNCTION(ALint, alGetInteger, ( ALenum param ), return 0; ) +AL_FUNCTION(ALfloat, alGetFloat, ( ALenum param ), return 0.0f; ) +AL_FUNCTION(ALdouble, alGetDouble, ( ALenum param ), return 0.0; ) +AL_FUNCTION(ALvoid, alGetBooleanv, ( ALenum param, ALboolean* data ), return; ) +AL_FUNCTION(ALvoid, alGetIntegerv, ( ALenum param, ALint* data ), return; ) +AL_FUNCTION(ALvoid, alGetFloatv, ( ALenum param, ALfloat* data ), return; ) +AL_FUNCTION(ALvoid, alGetDoublev, ( ALenum param, ALdouble* data ), return; ) +AL_FUNCTION(ALubyte*, alGetString, ( ALenum param ), return NULL; ) + +AL_FUNCTION(ALenum, alGetError, ( ALvoid ), return AL_INVALID_VALUE; ) +AL_FUNCTION(ALboolean, alIsExtensionPresent, ( ALubyte* fname ), return AL_FALSE; ) +AL_FUNCTION(ALvoid*, alGetProcAddress, ( ALubyte* fname ), return NULL; ) +AL_FUNCTION(ALenum, alGetEnumValue, ( ALubyte* ename ), return AL_INVALID_ENUM; ) + +AL_FUNCTION(ALvoid, alListeneri, ( ALenum param, ALint value ), return; ) +AL_FUNCTION(ALvoid, alListenerf, ( ALenum param, ALfloat value ), return; ) +AL_FUNCTION(ALvoid, alListener3f, ( ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 ), return; ) +AL_FUNCTION(ALvoid, alListenerfv, ( ALenum param, ALfloat* values ), return; ) + +AL_FUNCTION(ALvoid, alGetListeneri, ( ALenum param, ALint* value ), return; ) +AL_FUNCTION(ALvoid, alGetListenerf, ( ALenum param, ALfloat* value ), return; ) +AL_FUNCTION(ALvoid, alGetListener3f, ( ALenum param, ALfloat* v1, ALfloat* v2, ALfloat* v3 ), return; ) +AL_FUNCTION(ALvoid, alGetListenerfv, ( ALenum param, ALfloat* values ), return; ) + +AL_FUNCTION(ALvoid, alGenSources, ( ALsizei n, ALuint* sources ), return; ) +AL_FUNCTION(ALvoid, alDeleteSources, ( ALsizei n, ALuint* sources ), return; ) +AL_FUNCTION(ALboolean, alIsSource, ( ALuint id ), return AL_FALSE; ) + +AL_FUNCTION(ALvoid, alSourcei, ( ALuint source, ALenum param, ALint value ), return; ) +AL_FUNCTION(ALvoid, alSourcef, ( ALuint source, ALenum param, ALfloat value ), return; ) +AL_FUNCTION(ALvoid, alSource3f, ( ALuint source, ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 ), return; ) +AL_FUNCTION(ALvoid, alSourcefv, ( ALuint source, ALenum param, ALfloat* values ), return; ) +AL_FUNCTION(ALvoid, alGetSourcei, ( ALuint source, ALenum param, ALint* value ), return; ) +AL_FUNCTION(ALvoid, alGetSourcef, ( ALuint source, ALenum param, ALfloat* value ), return; ) +//AL_FUNCTION(ALvoid, alGetSource3f, ( ALuint source, ALenum param, ALfloat* v1, ALfloat* v2, ALfloat* v3 ), return; ) +AL_FUNCTION(ALvoid, alGetSourcefv, ( ALuint source, ALenum param, ALfloat* values ), return; ) + +AL_FUNCTION(ALvoid, alSourcePlayv, ( ALsizei n, ALuint *sources ), return; ) +AL_FUNCTION(ALvoid, alSourcePausev, ( ALsizei n, ALuint *sources ), return; ) +AL_FUNCTION(ALvoid, alSourceStopv, ( ALsizei n, ALuint *sources ), return; ) +AL_FUNCTION(ALvoid, alSourceRewindv, (ALsizei n,ALuint *sources), return; ) +AL_FUNCTION(ALvoid, alSourcePlay, ( ALuint source ), return; ) +AL_FUNCTION(ALvoid, alSourcePause, ( ALuint source ), return; ) +AL_FUNCTION(ALvoid, alSourceStop, ( ALuint source ), return; ) +AL_FUNCTION(ALvoid, alSourceRewind, ( ALuint source ), return; ) + +AL_FUNCTION(ALvoid, alGenBuffers, ( ALsizei n, ALuint* buffers ), return; ) +AL_FUNCTION(ALvoid, alDeleteBuffers, ( ALsizei n, ALuint* buffers ), return; ) +AL_FUNCTION(ALboolean, alIsBuffer, ( ALuint buffer ), return AL_FALSE; ) +AL_FUNCTION(ALvoid, alBufferData, ( ALuint buffer, ALenum format, ALvoid* data, ALsizei size, ALsizei freq ), return; ) +AL_FUNCTION(ALvoid, alGetBufferi, ( ALuint buffer, ALenum param, ALint* value ), return; ) +AL_FUNCTION(ALvoid, alGetBufferf, ( ALuint buffer, ALenum param, ALfloat* value ), return; ) + +AL_FUNCTION(ALvoid, alSourceQueueBuffers, ( ALuint source, ALsizei n, ALuint* buffers ), return; ) +AL_FUNCTION(ALvoid, alSourceUnqueueBuffers, ( ALuint source, ALsizei n, ALuint* buffers ), return; ) + +AL_FUNCTION(ALvoid, alDistanceModel, ( ALenum value ), return; ) +AL_FUNCTION(ALvoid, alDopplerFactor, ( ALfloat value ), return; ) +AL_FUNCTION(ALvoid, alDopplerVelocity, ( ALfloat value ), return; ) + + diff --git a/Engine/lib/openal/FreeBSD/al/alc.h b/Engine/lib/openal/FreeBSD/al/alc.h new file mode 100644 index 000000000..7804227d4 --- /dev/null +++ b/Engine/lib/openal/FreeBSD/al/alc.h @@ -0,0 +1,88 @@ +#ifndef _ALC_H_ +#define _ALC_H_ + +#include "altypes.h" +#include "alctypes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 + #ifdef _LIB + #define ALCAPI __declspec(dllexport) + #else + #define ALCAPI __declspec(dllimport) + typedef ALCvoid ALCdevice; + typedef ALCvoid ALCcontext; + #endif + #define ALCAPIENTRY __cdecl +#else + #ifdef TARGET_OS_MAC + #if TARGET_OS_MAC + #pragma export on + #endif + #endif + #define ALCAPI + #define ALCAPIENTRY __cdecl +#endif + + + +#ifndef ALC_NO_PROTOTYPES + +ALCAPI ALCubyte* ALCAPIENTRY alcGetString(ALCdevice *device,ALCenum param); +ALCAPI ALCvoid ALCAPIENTRY alcGetIntegerv(ALCdevice *device,ALCenum param,ALCsizei size,ALCint *data); + +ALCAPI ALCdevice* ALCAPIENTRY alcOpenDevice(ALCubyte *deviceName); +ALCAPI ALCvoid ALCAPIENTRY alcCloseDevice(ALCdevice *device); + +ALCAPI ALCcontext*ALCAPIENTRY alcCreateContext(ALCdevice *device,ALCint *attrList); +ALCAPI ALCboolean ALCAPIENTRY alcMakeContextCurrent(ALCcontext *context); +ALCAPI ALCvoid ALCAPIENTRY alcProcessContext(ALCcontext *context); +ALCAPI ALCcontext*ALCAPIENTRY alcGetCurrentContext(ALCvoid); +ALCAPI ALCdevice* ALCAPIENTRY alcGetContextsDevice(ALCcontext *context); +ALCAPI ALCvoid ALCAPIENTRY alcSuspendContext(ALCcontext *context); +ALCAPI ALCvoid ALCAPIENTRY alcDestroyContext(ALCcontext *context); + +ALCAPI ALCenum ALCAPIENTRY alcGetError(ALCdevice *device); + +ALCAPI ALCboolean ALCAPIENTRY alcIsExtensionPresent(ALCdevice *device,ALCubyte *extName); +ALCAPI ALCvoid * ALCAPIENTRY alcGetProcAddress(ALCdevice *device,ALCubyte *funcName); +ALCAPI ALCenum ALCAPIENTRY alcGetEnumValue(ALCdevice *device,ALCubyte *enumName); + +#else /* AL_NO_PROTOTYPES */ + +ALCAPI ALCubyte* ALCAPIENTRY (*alcGetString)(ALCdevice *device,ALCenum param); +ALCAPI ALCvoid ALCAPIENTRY (*alcGetIntegerv)(ALCdevice * device,ALCenum param,ALCsizei size,ALCint *data); + +ALCAPI ALCdevice* ALCAPIENTRY (*alcOpenDevice)(ALubyte *deviceName); +ALCAPI ALCvoid ALCAPIENTRY (*alcCloseDevice)(ALCdevice *device); + +ALCAPI ALCcontext*ALCAPIENTRY (*alcCreateContext)(ALCdevice *device,ALCint *attrList); +ALCAPI ALCboolean ALCAPIENTRY (*alcMakeContextCurrent)(ALCcontext *context); +ALCAPI ALCvoid ALCAPIENTRY (*alcProcessContext)(ALCcontext *context); +ALCAPI ALCcontext*ALCAPIENTRY (*alcGetCurrentContext)(ALCvoid); +ALCAPI ALCdevice* ALCAPIENTRY (*alcGetContextsDevice)(ALCcontext *context); +ALCAPI ALCvoid ALCAPIENTRY (*alcSuspendContext)(ALCcontext *context); +ALCAPI ALCvoid ALCAPIENTRY (*alcDestroyContext)(ALCcontext *context); + +ALCAPI ALCenum ALCAPIENTRY (*alcGetError)(ALCdevice *device); + +ALCAPI ALCboolean ALCAPIENTRY (*alcIsExtensionPresent)(ALCdevice *device,ALCubyte *extName); +ALCAPI ALCvoid * ALCAPIENTRY (*alcGetProcAddress)(ALCdevice *device,ALCubyte *funcName); +ALCAPI ALCenum ALCAPIENTRY (*alcGetEnumValue)(ALCdevice *device,ALCubyte *enumName); + +#endif /* AL_NO_PROTOTYPES */ + +#ifdef TARGET_OS_MAC + #if TARGET_OS_MAC + #pragma export off + #endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Engine/lib/openal/FreeBSD/al/alc_func.h b/Engine/lib/openal/FreeBSD/al/alc_func.h new file mode 100644 index 000000000..22da893e0 --- /dev/null +++ b/Engine/lib/openal/FreeBSD/al/alc_func.h @@ -0,0 +1,20 @@ + +//AL_FUNCTION(ALCubyte*, alcGetString, (ALCdevice *device,ALCenum param), return NULL; ) +//AL_FUNCTION(ALCvoid, alcGetIntegerv, (ALCdevice * device,ALCenum param,ALCsizei size,ALCint *data), return; ) + +AL_FUNCTION(ALCdevice*, alcOpenDevice, (ALubyte *deviceName), return NULL; ) +AL_FUNCTION(ALCvoid, alcCloseDevice, (ALCdevice *device), return; ) + +AL_FUNCTION(ALCcontext*, alcCreateContext, (ALCdevice *device,ALCint *attrList), return NULL; ) +AL_FUNCTION(ALCboolean, alcMakeContextCurrent, (ALCcontext *context), return AL_FALSE; ) +AL_FUNCTION(ALCvoid, alcProcessContext, (ALCcontext *context), return; ) +AL_FUNCTION(ALCcontext*, alcGetCurrentContext, (ALCvoid), return NULL; ) +AL_FUNCTION(ALCdevice*, alcGetContextsDevice, (ALCcontext *context), return NULL; ) +AL_FUNCTION(ALCvoid, alcSuspendContext, (ALCcontext *context), return; ) +AL_FUNCTION(ALCvoid, alcDestroyContext, (ALCcontext *context), return; ) + +AL_FUNCTION(ALCenum, alcGetError, (ALCdevice *device), return ALC_INVALID_DEVICE; ) + +//AL_FUNCTION(ALCboolean, alcIsExtensionPresent, (ALCdevice *device,ALCubyte *extName), return AL_FALSE; ) +//AL_FUNCTION(ALCvoid*, alcGetProcAddress, (ALCdevice *device,ALCubyte *funcName), return NULL; ) +//AL_FUNCTION(ALCenum, alcGetEnumValue, (ALCdevice *device,ALCubyte *enumName), return ALC_INVALID_ENUM; ) diff --git a/Engine/lib/openal/FreeBSD/al/alctypes.h b/Engine/lib/openal/FreeBSD/al/alctypes.h new file mode 100644 index 000000000..72ff49689 --- /dev/null +++ b/Engine/lib/openal/FreeBSD/al/alctypes.h @@ -0,0 +1,130 @@ +#ifndef _ALCTYPES_H_ +#define _ALCTYPES_H_ + +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2000 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + + +#ifdef __cplusplus +extern "C" { +#endif + +/** ALC boolean type. */ +typedef char ALCboolean; + +/** ALC 8bit signed byte. */ +typedef char ALCbyte; + +/** ALC 8bit unsigned byte. */ +typedef unsigned char ALCubyte; + +/** ALC 16bit signed short integer type. */ +typedef short ALCshort; + +/** ALC 16bit unsigned short integer type. */ +typedef unsigned short ALCushort; + +/** ALC 32bit unsigned integer type. */ +typedef unsigned ALCuint; + +/** ALC 32bit signed integer type. */ +typedef int ALCint; + +/** ALC 32bit floating point type. */ +typedef float ALCfloat; + +/** ALC 64bit double point type. */ +typedef double ALCdouble; + +/** ALC 32bit type. */ +typedef unsigned int ALCsizei; + +/** ALC void type */ +typedef void ALCvoid; + +/** ALC enumerations. */ +typedef int ALCenum; + + +typedef ALCvoid ALCdevice; +typedef ALCvoid ALCcontext; + + +/* Bad value. */ +#define ALC_INVALID (-1) + +/* Boolean False. */ +#define ALC_FALSE 0 + +/* Boolean True. */ +#define ALC_TRUE 1 + +/** Errors: No Error. */ +#define ALC_NO_ERROR ALC_FALSE + +#define ALC_MAJOR_VERSION 0x1000 +#define ALC_MINOR_VERSION 0x1001 +#define ALC_ATTRIBUTES_SIZE 0x1002 +#define ALC_ALL_ATTRIBUTES 0x1003 + +#define ALC_DEFAULT_DEVICE_SPECIFIER 0x1004 +#define ALC_DEVICE_SPECIFIER 0x1005 +#define ALC_EXTENSIONS 0x1006 + +#define ALC_FREQUENCY 0x1007 +#define ALC_REFRESH 0x1008 +#define ALC_SYNC 0x1009 + +/** + * The device argument does not name a valid dvice. + */ +#define ALC_INVALID_DEVICE 0xA001 + +/** + * The context argument does not name a valid context. + */ +#define ALC_INVALID_CONTEXT 0xA002 + +/** + * A function was called at inappropriate time, + * or in an inappropriate way, causing an illegal state. + * This can be an incompatible ALenum, object ID, + * and/or function. + */ +#define ALC_INVALID_ENUM 0xA003 + +/** + * Illegal value passed as an argument to an AL call. + * Applies to parameter values, but not to enumerations. + */ +#define ALC_INVALID_VALUE 0xA004 + +/** + * A function could not be completed, + * because there is not enough memory available. + */ +#define ALC_OUT_OF_MEMORY 0xA005 + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/Engine/lib/openal/FreeBSD/al/altypes.h b/Engine/lib/openal/FreeBSD/al/altypes.h new file mode 100644 index 000000000..8aa99c608 --- /dev/null +++ b/Engine/lib/openal/FreeBSD/al/altypes.h @@ -0,0 +1,332 @@ +#ifndef _ALTYPES_H_ +#define _ALTYPES_H_ + +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2000 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + + +#ifdef __cplusplus +extern "C" { +#endif + +/** OpenAL boolean type. */ +typedef char ALboolean; + +/** OpenAL 8bit signed byte. */ +typedef char ALbyte; + +/** OpenAL 8bit unsigned byte. */ +typedef unsigned char ALubyte; + +/** OpenAL 16bit signed short integer type. */ +typedef short ALshort; + +/** OpenAL 16bit unsigned short integer type. */ +typedef unsigned short ALushort; + +/** OpenAL 32bit unsigned integer type. */ +typedef unsigned ALuint; + +/** OpenAL 32bit signed integer type. */ +typedef int ALint; + +/** OpenAL 32bit floating point type. */ +typedef float ALfloat; + +/** OpenAL 64bit double point type. */ +typedef double ALdouble; + +/** OpenAL 32bit type. */ +typedef unsigned int ALsizei; + +/** OpenAL void type */ +typedef void ALvoid; + +/** OpenAL enumerations. */ +typedef int ALenum; + +/* Bad value. */ +#define AL_INVALID (-1) + +/* Disable value. */ +#define AL_NONE 0 + +/* Boolean False. */ +#define AL_FALSE 0 + +/* Boolean True. */ +#define AL_TRUE 1 + +/** + * Indicate the type of AL_SOURCE. + * Sources can be spatialized + */ +#define AL_SOURCE_TYPE 0x200 + +/** Indicate source has absolute coordinates. */ +#define AL_SOURCE_ABSOLUTE 0x201 + +/** Indicate Source has listener relative coordinates. */ +#define AL_SOURCE_RELATIVE 0x202 + +/** + * Directional source, inner cone angle, in degrees. + * Range: [0-360] + * Default: 360 + */ +#define AL_CONE_INNER_ANGLE 0x1001 + +/** + * Directional source, outer cone angle, in degrees. + * Range: [0-360] + * Default: 360 + */ +#define AL_CONE_OUTER_ANGLE 0x1002 + +/** + * Specify the pitch to be applied, either at source, + * or on mixer results, at listener. + * Range: [0.5-2.0] + * Default: 1.0 + */ +#define AL_PITCH 0x1003 + +/** + * Specify the current location in three dimensional space. + * OpenAL, like OpenGL, uses a right handed coordinate system, + * where in a frontal default view X (thumb) points right, + * Y points up (index finger), and Z points towards the + * viewer/camera (middle finger). + * To switch from a left handed coordinate system, flip the + * sign on the Z coordinate. + * Listener position is always in the world coordinate system. + */ +#define AL_POSITION 0x1004 + +/** Specify the current direction as forward vector. */ +#define AL_DIRECTION 0x1005 + +/** Specify the current velocity in three dimensional space. */ +#define AL_VELOCITY 0x1006 + +/** + * Indicate whether source has to loop infinite. + * Type: ALboolean + * Range: [AL_TRUE, AL_FALSE] + * Default: AL_FALSE + */ +#define AL_LOOPING 0x1007 + +/** + * Indicate the buffer to provide sound samples. + * Type: ALuint. + * Range: any valid Buffer id. + */ +#define AL_BUFFER 0x1009 + +/** + * Indicate the gain (volume amplification) applied. + * Type: ALfloat. + * Range: ]0.0- ] + * A value of 1.0 means un-attenuated/unchanged. + * Each division by 2 equals an attenuation of -6dB. + * Each multiplicaton with 2 equals an amplification of +6dB. + * A value of 0.0 is meaningless with respect to a logarithmic + * scale; it is interpreted as zero volume - the channel + * is effectively disabled. + */ +#define AL_GAIN 0x100A + +/** + * Indicate minimum source attenuation. + * Type: ALfloat + * Range: [0.0 - 1.0] + */ +#define AL_MIN_GAIN 0x100D + +/** + * Indicate maximum source attenuation. + * Type: ALfloat + * Range: [0.0 - 1.0] + */ +#define AL_MAX_GAIN 0x100E + +/** + * Specify the current orientation. + * Type: ALfv6 (at/up) + * Range: N/A + */ +#define AL_ORIENTATION 0x100F + +/* byte offset into source (in canon format). -1 if source + * is not playing. Don't set this, get this. + * + * Type: ALfloat + * Range: [0.0 - ] + * Default: 1.0 + */ +#define AL_REFERENCE_DISTANCE 0x1020 + + /** + * Indicate the rolloff factor for the source. + * Type: ALfloat + * Range: [0.0 - ] + * Default: 1.0 + */ +#define AL_ROLLOFF_FACTOR 0x1021 + +/** + * Indicate the gain (volume amplification) applied. + * Type: ALfloat. + * Range: ]0.0- ] + * A value of 1.0 means un-attenuated/unchanged. + * Each division by 2 equals an attenuation of -6dB. + * Each multiplicaton with 2 equals an amplification of +6dB. + * A value of 0.0 is meaningless with respect to a logarithmic + * scale; it is interpreted as zero volume - the channel + * is effectively disabled. + */ +#define AL_CONE_OUTER_GAIN 0x1022 + +/** + * Specify the maximum distance. + * Type: ALfloat + * Range: [0.0 - ] + */ +#define AL_MAX_DISTANCE 0x1023 + +/** + * Specify the channel mask. (Creative) + * Type: ALuint + * Range: [0 - 255] + */ +#define AL_CHANNEL_MASK 0x3000 + +/** + * Source state information + */ +#define AL_SOURCE_STATE 0x1010 +#define AL_INITIAL 0x1011 +#define AL_PLAYING 0x1012 +#define AL_PAUSED 0x1013 +#define AL_STOPPED 0x1014 + +/** + * Buffer Queue params + */ +#define AL_BUFFERS_QUEUED 0x1015 +#define AL_BUFFERS_PROCESSED 0x1016 + +/** Sound buffers: format specifier. */ +#define AL_FORMAT_MONO8 0x1100 +#define AL_FORMAT_MONO16 0x1101 +#define AL_FORMAT_STEREO8 0x1102 +#define AL_FORMAT_STEREO16 0x1103 + +/** + * Sound buffers: frequency, in units of Hertz [Hz]. + * This is the number of samples per second. Half of the + * sample frequency marks the maximum significant + * frequency component. + */ +#define AL_FREQUENCY 0x2001 +#define AL_BITS 0x2002 +#define AL_CHANNELS 0x2003 +#define AL_SIZE 0x2004 +#define AL_DATA 0x2005 + +/** + * Buffer state. + * + * Not supported for public use (yet). + */ +#define AL_UNUSED 0x2010 +#define AL_PENDING 0x2011 +#define AL_PROCESSED 0x2012 + +/** Errors: No Error. */ +#define AL_NO_ERROR AL_FALSE + +/** + * Illegal name passed as an argument to an AL call. + */ +#define AL_INVALID_NAME 0xA001 + +/** + * Illegal enum passed as an argument to an AL call. + */ +#define AL_INVALID_ENUM 0xA002 +/** + * Illegal value passed as an argument to an AL call. + * Applies to parameter values, but not to enumerations. + */ +#define AL_INVALID_VALUE 0xA003 + +/** + * A function was called at inappropriate time, + * or in an inappropriate way, causing an illegal state. + * This can be an incompatible ALenum, object ID, + * and/or function. + */ +#define AL_INVALID_OPERATION 0xA004 + +/** + * A function could not be completed, + * because there is not enough memory available. + */ +#define AL_OUT_OF_MEMORY 0xA005 + +/** Context strings: Vendor Name. */ +#define AL_VENDOR 0xB001 +#define AL_VERSION 0xB002 +#define AL_RENDERER 0xB003 +#define AL_EXTENSIONS 0xB004 + +/** Global tweakage. */ + +/** + * Doppler scale. Default 1.0 + */ +#define AL_DOPPLER_FACTOR 0xC000 + +/** + * Doppler velocity. Default 1.0 + */ +#define AL_DOPPLER_VELOCITY 0xC001 + +/** + * Distance model. Default AL_INVERSE_DISTANCE_CLAMPED + */ +#define AL_DISTANCE_MODEL 0xD000 + +/** Distance models. */ + +#define AL_INVERSE_DISTANCE 0xD001 +#define AL_INVERSE_DISTANCE_CLAMPED 0xD002 + + /** + * enables + */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Engine/lib/openal/FreeBSD/al/alu.h b/Engine/lib/openal/FreeBSD/al/alu.h new file mode 100644 index 000000000..c6adff62c --- /dev/null +++ b/Engine/lib/openal/FreeBSD/al/alu.h @@ -0,0 +1,34 @@ +#ifndef _ALU_H_ +#define _ALU_H_ + +#define ALUAPI +#define ALUAPIENTRY __cdecl + +#define BUFFERSIZE 48000 +#define FRACTIONBITS 14 +#define FRACTIONMASK ((1L< + + /* + * EAX Wrapper Interface (using Direct X 7) {4FF53B81-1CE0-11d3-AAB8-00A0C95949D5} + */ + DEFINE_GUID(CLSID_EAXDirectSound, + 0x4ff53b81, + 0x1ce0, + 0x11d3, + 0xaa, 0xb8, 0x0, 0xa0, 0xc9, 0x59, 0x49, 0xd5); + + /* + * EAX Wrapper Interface (using Direct X 8) {CA503B60-B176-11d4-A094-D0C0BF3A560C} + */ + DEFINE_GUID(CLSID_EAXDirectSound8, + 0xca503b60, + 0xb176, + 0x11d4, + 0xa0, 0x94, 0xd0, 0xc0, 0xbf, 0x3a, 0x56, 0xc); + + + +#ifdef DIRECTSOUND_VERSION +#if DIRECTSOUND_VERSION == 0x0800 + __declspec(dllimport) HRESULT WINAPI EAXDirectSoundCreate8(GUID*, LPDIRECTSOUND8*, IUnknown FAR *); + typedef HRESULT (FAR PASCAL *LPEAXDIRECTSOUNDCREATE8)(GUID*, LPDIRECTSOUND8*, IUnknown FAR*); +#endif +#endif + + __declspec(dllimport) HRESULT WINAPI EAXDirectSoundCreate(GUID*, LPDIRECTSOUND*, IUnknown FAR *); + typedef HRESULT (FAR PASCAL *LPEAXDIRECTSOUNDCREATE)(GUID*, LPDIRECTSOUND*, IUnknown FAR*); + +#else // OPENAL + #include + + #ifndef GUID_DEFINED + #define GUID_DEFINED + typedef struct _GUID + { + unsigned long Data1; + unsigned short Data2; + unsigned short Data3; + unsigned char Data4[8]; + } GUID; + #endif // !GUID_DEFINED + + #ifndef DEFINE_GUID + #ifndef INITGUID + #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const GUID FAR name + #else + #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } + #endif // INITGUID + #endif // DEFINE_GUID + + + /* + * EAX OpenAL Extension + */ + typedef ALenum (*EAXSet)(const GUID*, ALuint, ALuint, ALvoid*, ALuint); + typedef ALenum (*EAXGet)(const GUID*, ALuint, ALuint, ALvoid*, ALuint); +#endif + +#pragma pack(push, 4) + +/* + * EAX 3.0 listener property set {A8FA6880-B476-11d3-BDB9-00C0F02DDF87} + */ +DEFINE_GUID(DSPROPSETID_EAX30_ListenerProperties, + 0xa8fa6882, + 0xb476, + 0x11d3, + 0xbd, 0xb9, 0x00, 0xc0, 0xf0, 0x2d, 0xdf, 0x87); + +// For compatibility with future EAX versions: +#define DSPROPSETID_EAX_ListenerProperties DSPROPSETID_EAX30_ListenerProperties + +typedef enum +{ + DSPROPERTY_EAXLISTENER_NONE, + DSPROPERTY_EAXLISTENER_ALLPARAMETERS, + DSPROPERTY_EAXLISTENER_ENVIRONMENT, + DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE, + DSPROPERTY_EAXLISTENER_ENVIRONMENTDIFFUSION, + DSPROPERTY_EAXLISTENER_ROOM, + DSPROPERTY_EAXLISTENER_ROOMHF, + DSPROPERTY_EAXLISTENER_ROOMLF, + DSPROPERTY_EAXLISTENER_DECAYTIME, + DSPROPERTY_EAXLISTENER_DECAYHFRATIO, + DSPROPERTY_EAXLISTENER_DECAYLFRATIO, + DSPROPERTY_EAXLISTENER_REFLECTIONS, + DSPROPERTY_EAXLISTENER_REFLECTIONSDELAY, + DSPROPERTY_EAXLISTENER_REFLECTIONSPAN, + DSPROPERTY_EAXLISTENER_REVERB, + DSPROPERTY_EAXLISTENER_REVERBDELAY, + DSPROPERTY_EAXLISTENER_REVERBPAN, + DSPROPERTY_EAXLISTENER_ECHOTIME, + DSPROPERTY_EAXLISTENER_ECHODEPTH, + DSPROPERTY_EAXLISTENER_MODULATIONTIME, + DSPROPERTY_EAXLISTENER_MODULATIONDEPTH, + DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF, + DSPROPERTY_EAXLISTENER_HFREFERENCE, + DSPROPERTY_EAXLISTENER_LFREFERENCE, + DSPROPERTY_EAXLISTENER_ROOMROLLOFFFACTOR, + DSPROPERTY_EAXLISTENER_FLAGS +} DSPROPERTY_EAX_LISTENERPROPERTY; + +// OR these flags with property id +#define DSPROPERTY_EAXLISTENER_IMMEDIATE 0x00000000 // changes take effect immediately +#define DSPROPERTY_EAXLISTENER_DEFERRED 0x80000000 // changes take effect later +#define DSPROPERTY_EAXLISTENER_COMMITDEFERREDSETTINGS (DSPROPERTY_EAXLISTENER_NONE | \ + DSPROPERTY_EAXLISTENER_IMMEDIATE) + +typedef struct _EAXVECTOR { + float x; + float y; + float z; +} EAXVECTOR; + +// Use this structure for DSPROPERTY_EAXLISTENER_ALLPARAMETERS +// - all levels are hundredths of decibels +// - all times and delays are in seconds +// +// NOTE: This structure may change in future EAX versions. +// It is recommended to initialize fields by name: +// myListener.lRoom = -1000; +// myListener.lRoomHF = -100; +// ... +// myListener.dwFlags = myFlags /* see EAXLISTENERFLAGS below */ ; +// instead of: +// myListener = { -1000, -100, ... , 0x00000009 }; +// If you want to save and load presets in binary form, you +// should define your own structure to insure future compatibility. +// +typedef struct _EAXLISTENERPROPERTIES +{ + unsigned long ulEnvironment; // sets all listener properties + float flEnvironmentSize; // environment size in meters + float flEnvironmentDiffusion; // environment diffusion + long lRoom; // room effect level (at mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lRoomLF; // relative room effect level at low frequencies + float flDecayTime; // reverberation decay time at mid frequencies + float flDecayHFRatio; // high-frequency to mid-frequency decay time ratio + float flDecayLFRatio; // low-frequency to mid-frequency decay time ratio + long lReflections; // early reflections level relative to room effect + float flReflectionsDelay; // initial reflection delay time + EAXVECTOR vReflectionsPan; // early reflections panning vector + long lReverb; // late reverberation level relative to room effect + float flReverbDelay; // late reverberation delay time relative to initial reflection + EAXVECTOR vReverbPan; // late reverberation panning vector + float flEchoTime; // echo time + float flEchoDepth; // echo depth + float flModulationTime; // modulation time + float flModulationDepth; // modulation depth + float flAirAbsorptionHF; // change in level per meter at high frequencies + float flHFReference; // reference high frequency + float flLFReference; // reference low frequency + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + unsigned long ulFlags; // modifies the behavior of properties +} EAXLISTENERPROPERTIES, *LPEAXLISTENERPROPERTIES; + +// used by DSPROPERTY_EAXLISTENER_ENVIRONMENT +enum +{ + EAX_ENVIRONMENT_GENERIC, + EAX_ENVIRONMENT_PADDEDCELL, + EAX_ENVIRONMENT_ROOM, + EAX_ENVIRONMENT_BATHROOM, + EAX_ENVIRONMENT_LIVINGROOM, + EAX_ENVIRONMENT_STONEROOM, + EAX_ENVIRONMENT_AUDITORIUM, + EAX_ENVIRONMENT_CONCERTHALL, + EAX_ENVIRONMENT_CAVE, + EAX_ENVIRONMENT_ARENA, + EAX_ENVIRONMENT_HANGAR, + EAX_ENVIRONMENT_CARPETEDHALLWAY, + EAX_ENVIRONMENT_HALLWAY, + EAX_ENVIRONMENT_STONECORRIDOR, + EAX_ENVIRONMENT_ALLEY, + EAX_ENVIRONMENT_FOREST, + EAX_ENVIRONMENT_CITY, + EAX_ENVIRONMENT_MOUNTAINS, + EAX_ENVIRONMENT_QUARRY, + EAX_ENVIRONMENT_PLAIN, + EAX_ENVIRONMENT_PARKINGLOT, + EAX_ENVIRONMENT_SEWERPIPE, + EAX_ENVIRONMENT_UNDERWATER, + EAX_ENVIRONMENT_DRUGGED, + EAX_ENVIRONMENT_DIZZY, + EAX_ENVIRONMENT_PSYCHOTIC, + + EAX_ENVIRONMENT_UNDEFINED, + + EAX_ENVIRONMENT_COUNT +}; + +// Used by DSPROPERTY_EAXLISTENER_FLAGS +// +// Note: The number and order of flags may change in future EAX versions. +// It is recommended to use the flag defines as follows: +// myFlags = EAXLISTENERFLAGS_DECAYTIMESCALE | EAXLISTENERFLAGS_REVERBSCALE; +// instead of: +// myFlags = 0x00000009; +// +// These flags determine what properties are affected by environment size. +#define EAXLISTENERFLAGS_DECAYTIMESCALE 0x00000001 // reverberation decay time +#define EAXLISTENERFLAGS_REFLECTIONSSCALE 0x00000002 // reflection level +#define EAXLISTENERFLAGS_REFLECTIONSDELAYSCALE 0x00000004 // initial reflection delay time +#define EAXLISTENERFLAGS_REVERBSCALE 0x00000008 // reflections level +#define EAXLISTENERFLAGS_REVERBDELAYSCALE 0x00000010 // late reverberation delay time +#define EAXLISTENERFLAGS_ECHOTIMESCALE 0x00000040 // echo time +#define EAXLISTENERFLAGS_MODULATIONTIMESCALE 0x00000080 // modulation time + +// This flag limits high-frequency decay time according to air absorption. +#define EAXLISTENERFLAGS_DECAYHFLIMIT 0x00000020 + +#define EAXLISTENERFLAGS_RESERVED 0xFFFFFF00 // reserved future use + +// Property ranges and defaults: + +#define EAXLISTENER_MINENVIRONMENT 0 +#define EAXLISTENER_MAXENVIRONMENT (EAX_ENVIRONMENT_COUNT-1) +#define EAXLISTENER_DEFAULTENVIRONMENT EAX_ENVIRONMENT_GENERIC + +#define EAXLISTENER_MINENVIRONMENTSIZE 1.0f +#define EAXLISTENER_MAXENVIRONMENTSIZE 100.0f +#define EAXLISTENER_DEFAULTENVIRONMENTSIZE 7.5f + +#define EAXLISTENER_MINENVIRONMENTDIFFUSION 0.0f +#define EAXLISTENER_MAXENVIRONMENTDIFFUSION 1.0f +#define EAXLISTENER_DEFAULTENVIRONMENTDIFFUSION 1.0f + +#define EAXLISTENER_MINROOM (-10000) +#define EAXLISTENER_MAXROOM 0 +#define EAXLISTENER_DEFAULTROOM (-1000) + +#define EAXLISTENER_MINROOMHF (-10000) +#define EAXLISTENER_MAXROOMHF 0 +#define EAXLISTENER_DEFAULTROOMHF (-100) + +#define EAXLISTENER_MINROOMLF (-10000) +#define EAXLISTENER_MAXROOMLF 0 +#define EAXLISTENER_DEFAULTROOMLF 0 + +#define EAXLISTENER_MINDECAYTIME 0.1f +#define EAXLISTENER_MAXDECAYTIME 20.0f +#define EAXLISTENER_DEFAULTDECAYTIME 1.49f + +#define EAXLISTENER_MINDECAYHFRATIO 0.1f +#define EAXLISTENER_MAXDECAYHFRATIO 2.0f +#define EAXLISTENER_DEFAULTDECAYHFRATIO 0.83f + +#define EAXLISTENER_MINDECAYLFRATIO 0.1f +#define EAXLISTENER_MAXDECAYLFRATIO 2.0f +#define EAXLISTENER_DEFAULTDECAYLFRATIO 1.00f + +#define EAXLISTENER_MINREFLECTIONS (-10000) +#define EAXLISTENER_MAXREFLECTIONS 1000 +#define EAXLISTENER_DEFAULTREFLECTIONS (-2602) + +#define EAXLISTENER_MINREFLECTIONSDELAY 0.0f +#define EAXLISTENER_MAXREFLECTIONSDELAY 0.3f +#define EAXLISTENER_DEFAULTREFLECTIONSDELAY 0.007f + +#define EAXLISTENER_MINREVERB (-10000) +#define EAXLISTENER_MAXREVERB 2000 +#define EAXLISTENER_DEFAULTREVERB 200 + +#define EAXLISTENER_MINREVERBDELAY 0.0f +#define EAXLISTENER_MAXREVERBDELAY 0.1f +#define EAXLISTENER_DEFAULTREVERBDELAY 0.011f + +#define EAXLISTENER_MINECHOTIME 0.075f +#define EAXLISTENER_MAXECHOTIME 0.25f +#define EAXLISTENER_DEFAULTECHOTIME 0.25f + +#define EAXLISTENER_MINECHODEPTH 0.0f +#define EAXLISTENER_MAXECHODEPTH 1.0f +#define EAXLISTENER_DEFAULTECHODEPTH 0.0f + +#define EAXLISTENER_MINMODULATIONTIME 0.04f +#define EAXLISTENER_MAXMODULATIONTIME 4.0f +#define EAXLISTENER_DEFAULTMODULATIONTIME 0.25f + +#define EAXLISTENER_MINMODULATIONDEPTH 0.0f +#define EAXLISTENER_MAXMODULATIONDEPTH 1.0f +#define EAXLISTENER_DEFAULTMODULATIONDEPTH 0.0f + +#define EAXLISTENER_MINAIRABSORPTIONHF (-100.0f) +#define EAXLISTENER_MAXAIRABSORPTIONHF 0.0f +#define EAXLISTENER_DEFAULTAIRABSORPTIONHF (-5.0f) + +#define EAXLISTENER_MINHFREFERENCE 1000.0f +#define EAXLISTENER_MAXHFREFERENCE 20000.0f +#define EAXLISTENER_DEFAULTHFREFERENCE 5000.0f + +#define EAXLISTENER_MINLFREFERENCE 20.0f +#define EAXLISTENER_MAXLFREFERENCE 1000.0f +#define EAXLISTENER_DEFAULTLFREFERENCE 250.0f + +#define EAXLISTENER_MINROOMROLLOFFFACTOR 0.0f +#define EAXLISTENER_MAXROOMROLLOFFFACTOR 10.0f +#define EAXLISTENER_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXLISTENER_DEFAULTFLAGS (EAXLISTENERFLAGS_DECAYTIMESCALE | \ + EAXLISTENERFLAGS_REFLECTIONSSCALE | \ + EAXLISTENERFLAGS_REFLECTIONSDELAYSCALE | \ + EAXLISTENERFLAGS_REVERBSCALE | \ + EAXLISTENERFLAGS_REVERBDELAYSCALE | \ + EAXLISTENERFLAGS_DECAYHFLIMIT) + + + +/* +* EAX 3.0 buffer property set {A8FA6881-B476-11d3-BDB9-00C0F02DDF87} +*/ +DEFINE_GUID(DSPROPSETID_EAX30_BufferProperties, + 0xa8fa6881, + 0xb476, + 0x11d3, + 0xbd, 0xb9, 0x0, 0xc0, 0xf0, 0x2d, 0xdf, 0x87); + +// For compatibility with future EAX versions: +#define DSPROPSETID_EAX_BufferProperties DSPROPSETID_EAX30_BufferProperties +#define DSPROPSETID_EAX_SourceProperties DSPROPSETID_EAX30_BufferProperties + +typedef enum +{ + DSPROPERTY_EAXBUFFER_NONE, + DSPROPERTY_EAXBUFFER_ALLPARAMETERS, + DSPROPERTY_EAXBUFFER_OBSTRUCTIONPARAMETERS, + DSPROPERTY_EAXBUFFER_OCCLUSIONPARAMETERS, + DSPROPERTY_EAXBUFFER_EXCLUSIONPARAMETERS, + DSPROPERTY_EAXBUFFER_DIRECT, + DSPROPERTY_EAXBUFFER_DIRECTHF, + DSPROPERTY_EAXBUFFER_ROOM, + DSPROPERTY_EAXBUFFER_ROOMHF, + DSPROPERTY_EAXBUFFER_OBSTRUCTION, + DSPROPERTY_EAXBUFFER_OBSTRUCTIONLFRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSION, + DSPROPERTY_EAXBUFFER_OCCLUSIONLFRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSIONROOMRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSIONDIRECTRATIO, + DSPROPERTY_EAXBUFFER_EXCLUSION, + DSPROPERTY_EAXBUFFER_EXCLUSIONLFRATIO, + DSPROPERTY_EAXBUFFER_OUTSIDEVOLUMEHF, + DSPROPERTY_EAXBUFFER_DOPPLERFACTOR, + DSPROPERTY_EAXBUFFER_ROLLOFFFACTOR, + DSPROPERTY_EAXBUFFER_ROOMROLLOFFFACTOR, + DSPROPERTY_EAXBUFFER_AIRABSORPTIONFACTOR, + DSPROPERTY_EAXBUFFER_FLAGS +} DSPROPERTY_EAX_BUFFERPROPERTY; + +// OR these flags with property id +#define DSPROPERTY_EAXBUFFER_IMMEDIATE 0x00000000 // changes take effect immediately +#define DSPROPERTY_EAXBUFFER_DEFERRED 0x80000000 // changes take effect later +#define DSPROPERTY_EAXBUFFER_COMMITDEFERREDSETTINGS (DSPROPERTY_EAXBUFFER_NONE | \ + DSPROPERTY_EAXBUFFER_IMMEDIATE) + +// Use this structure for DSPROPERTY_EAXBUFFER_ALLPARAMETERS +// - all levels are hundredths of decibels +// - all delays are in seconds +// +// NOTE: This structure may change in future EAX versions. +// It is recommended to initialize fields by name: +// myBuffer.lDirect = 0; +// myBuffer.lDirectHF = -200; +// ... +// myBuffer.dwFlags = myFlags /* see EAXBUFFERFLAGS below */ ; +// instead of: +// myBuffer = { 0, -200, ... , 0x00000003 }; +// +typedef struct _EAXBUFFERPROPERTIES +{ + long lDirect; // direct path level (at low and mid frequencies) + long lDirectHF; // relative direct path level at high frequencies + long lRoom; // room effect level (at low and mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lObstruction; // main obstruction control (attenuation at high frequencies) + float flObstructionLFRatio; // obstruction low-frequency level re. main control + long lOcclusion; // main occlusion control (attenuation at high frequencies) + float flOcclusionLFRatio; // occlusion low-frequency level re. main control + float flOcclusionRoomRatio; // relative occlusion control for room effect + float flOcclusionDirectRatio; // relative occlusion control for direct path + long lExclusion; // main exlusion control (attenuation at high frequencies) + float flExclusionLFRatio; // exclusion low-frequency level re. main control + long lOutsideVolumeHF; // outside sound cone level at high frequencies + float flDopplerFactor; // like DS3D flDopplerFactor but per source + float flRolloffFactor; // like DS3D flRolloffFactor but per source + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + float flAirAbsorptionFactor; // multiplies DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF + unsigned long ulFlags; // modifies the behavior of properties +} EAXBUFFERPROPERTIES, *LPEAXBUFFERPROPERTIES; + +// Use this structure for DSPROPERTY_EAXBUFFER_OBSTRUCTION, +typedef struct _EAXOBSTRUCTIONPROPERTIES +{ + long lObstruction; + float flObstructionLFRatio; +} EAXOBSTRUCTIONPROPERTIES, *LPEAXOBSTRUCTIONPROPERTIES; + +// Use this structure for DSPROPERTY_EAXBUFFER_OCCLUSION +typedef struct _EAXOCCLUSIONPROPERTIES +{ + long lOcclusion; + float flOcclusionLFRatio; + float flOcclusionRoomRatio; + float flOcclusionDirectRatio; +} EAXOCCLUSIONPROPERTIES, *LPEAXOCCLUSIONPROPERTIES; + +// Use this structure for DSPROPERTY_EAXBUFFER_EXCLUSION +typedef struct _EAXEXCLUSIONPROPERTIES +{ + long lExclusion; + float flExclusionLFRatio; +} EAXEXCLUSIONPROPERTIES, *LPEAXEXCLUSIONPROPERTIES; + +// Used by DSPROPERTY_EAXBUFFER_FLAGS +// TRUE: value is computed automatically - property is an offset +// FALSE: value is used directly +// +// Note: The number and order of flags may change in future EAX versions. +// To insure future compatibility, use flag defines as follows: +// myFlags = EAXBUFFERFLAGS_DIRECTHFAUTO | EAXBUFFERFLAGS_ROOMAUTO; +// instead of: +// myFlags = 0x00000003; +// +#define EAXBUFFERFLAGS_DIRECTHFAUTO 0x00000001 // affects DSPROPERTY_EAXBUFFER_DIRECTHF +#define EAXBUFFERFLAGS_ROOMAUTO 0x00000002 // affects DSPROPERTY_EAXBUFFER_ROOM +#define EAXBUFFERFLAGS_ROOMHFAUTO 0x00000004 // affects DSPROPERTY_EAXBUFFER_ROOMHF + +#define EAXBUFFERFLAGS_RESERVED 0xFFFFFFF8 // reserved future use + +// Property ranges and defaults: + +#define EAXBUFFER_MINDIRECT (-10000) +#define EAXBUFFER_MAXDIRECT 1000 +#define EAXBUFFER_DEFAULTDIRECT 0 + +#define EAXBUFFER_MINDIRECTHF (-10000) +#define EAXBUFFER_MAXDIRECTHF 0 +#define EAXBUFFER_DEFAULTDIRECTHF 0 + +#define EAXBUFFER_MINROOM (-10000) +#define EAXBUFFER_MAXROOM 1000 +#define EAXBUFFER_DEFAULTROOM 0 + +#define EAXBUFFER_MINROOMHF (-10000) +#define EAXBUFFER_MAXROOMHF 0 +#define EAXBUFFER_DEFAULTROOMHF 0 + +#define EAXBUFFER_MINOBSTRUCTION (-10000) +#define EAXBUFFER_MAXOBSTRUCTION 0 +#define EAXBUFFER_DEFAULTOBSTRUCTION 0 + +#define EAXBUFFER_MINOBSTRUCTIONLFRATIO 0.0f +#define EAXBUFFER_MAXOBSTRUCTIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTOBSTRUCTIONLFRATIO 0.0f + +#define EAXBUFFER_MINOCCLUSION (-10000) +#define EAXBUFFER_MAXOCCLUSION 0 +#define EAXBUFFER_DEFAULTOCCLUSION 0 + +#define EAXBUFFER_MINOCCLUSIONLFRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTOCCLUSIONLFRATIO 0.25f + +#define EAXBUFFER_MINOCCLUSIONROOMRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONROOMRATIO 10.0f +#define EAXBUFFER_DEFAULTOCCLUSIONROOMRATIO 1.5f + +#define EAXBUFFER_MINOCCLUSIONDIRECTRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONDIRECTRATIO 10.0f +#define EAXBUFFER_DEFAULTOCCLUSIONDIRECTRATIO 1.0f + +#define EAXBUFFER_MINEXCLUSION (-10000) +#define EAXBUFFER_MAXEXCLUSION 0 +#define EAXBUFFER_DEFAULTEXCLUSION 0 + +#define EAXBUFFER_MINEXCLUSIONLFRATIO 0.0f +#define EAXBUFFER_MAXEXCLUSIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTEXCLUSIONLFRATIO 1.0f + +#define EAXBUFFER_MINOUTSIDEVOLUMEHF (-10000) +#define EAXBUFFER_MAXOUTSIDEVOLUMEHF 0 +#define EAXBUFFER_DEFAULTOUTSIDEVOLUMEHF 0 + +#define EAXBUFFER_MINDOPPLERFACTOR 0.0f +#define EAXBUFFER_MAXDOPPLERFACTOR 10.f +#define EAXBUFFER_DEFAULTDOPPLERFACTOR 0.0f + +#define EAXBUFFER_MINROLLOFFFACTOR 0.0f +#define EAXBUFFER_MAXROLLOFFFACTOR 10.f +#define EAXBUFFER_DEFAULTROLLOFFFACTOR 0.0f + +#define EAXBUFFER_MINROOMROLLOFFFACTOR 0.0f +#define EAXBUFFER_MAXROOMROLLOFFFACTOR 10.f +#define EAXBUFFER_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXBUFFER_MINAIRABSORPTIONFACTOR 0.0f +#define EAXBUFFER_MAXAIRABSORPTIONFACTOR 10.0f +#define EAXBUFFER_DEFAULTAIRABSORPTIONFACTOR 1.0f + +#define EAXBUFFER_DEFAULTFLAGS (EAXBUFFERFLAGS_DIRECTHFAUTO | \ + EAXBUFFERFLAGS_ROOMAUTO | \ + EAXBUFFERFLAGS_ROOMHFAUTO ) + +#pragma pack(pop) + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif diff --git a/Engine/lib/openal/FreeBSD/al/eax_func.h b/Engine/lib/openal/FreeBSD/al/eax_func.h new file mode 100644 index 000000000..8c36c7471 --- /dev/null +++ b/Engine/lib/openal/FreeBSD/al/eax_func.h @@ -0,0 +1,3 @@ + +AL_FUNCTION(ALenum, EAXSet, (const ALGUID*, ALuint, ALuint, ALvoid*, ALuint), return 0; ) +AL_FUNCTION(ALenum, EAXGet, (const ALGUID*, ALuint, ALuint, ALvoid*, ALuint), return 0; ) diff --git a/Engine/lib/openal/FreeBSD/al/eaxtypes.h b/Engine/lib/openal/FreeBSD/al/eaxtypes.h new file mode 100644 index 000000000..15ba59afc --- /dev/null +++ b/Engine/lib/openal/FreeBSD/al/eaxtypes.h @@ -0,0 +1,462 @@ + + +typedef struct _ALGUID +{ + unsigned long Data1; + unsigned short Data2; + unsigned short Data3; + unsigned char Data4[8]; +}ALGUID; + +#ifndef INITGUID + #define DEFINE_ALGUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const ALGUID name +#else + #define DEFINE_ALGUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const ALGUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } +#endif // INITGUID + + + +/* + * EAX 3.0 listener property set {A8FA6880-B476-11d3-BDB9-00C0F02DDF87} + */ +DEFINE_ALGUID(DSPROPSETID_EAX30_ListenerProperties, + 0xa8fa6882, + 0xb476, + 0x11d3, + 0xbd, 0xb9, 0x00, 0xc0, 0xf0, 0x2d, 0xdf, 0x87); + +// For compatibility with future EAX versions: +#define DSPROPSETID_EAX_ListenerProperties DSPROPSETID_EAX30_ListenerProperties + +typedef enum +{ + DSPROPERTY_EAXLISTENER_NONE, + DSPROPERTY_EAXLISTENER_ALLPARAMETERS, + DSPROPERTY_EAXLISTENER_ENVIRONMENT, + DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE, + DSPROPERTY_EAXLISTENER_ENVIRONMENTDIFFUSION, + DSPROPERTY_EAXLISTENER_ROOM, + DSPROPERTY_EAXLISTENER_ROOMHF, + DSPROPERTY_EAXLISTENER_ROOMLF, + DSPROPERTY_EAXLISTENER_DECAYTIME, + DSPROPERTY_EAXLISTENER_DECAYHFRATIO, + DSPROPERTY_EAXLISTENER_DECAYLFRATIO, + DSPROPERTY_EAXLISTENER_REFLECTIONS, + DSPROPERTY_EAXLISTENER_REFLECTIONSDELAY, + DSPROPERTY_EAXLISTENER_REFLECTIONSPAN, + DSPROPERTY_EAXLISTENER_REVERB, + DSPROPERTY_EAXLISTENER_REVERBDELAY, + DSPROPERTY_EAXLISTENER_REVERBPAN, + DSPROPERTY_EAXLISTENER_ECHOTIME, + DSPROPERTY_EAXLISTENER_ECHODEPTH, + DSPROPERTY_EAXLISTENER_MODULATIONTIME, + DSPROPERTY_EAXLISTENER_MODULATIONDEPTH, + DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF, + DSPROPERTY_EAXLISTENER_HFREFERENCE, + DSPROPERTY_EAXLISTENER_LFREFERENCE, + DSPROPERTY_EAXLISTENER_ROOMROLLOFFFACTOR, + DSPROPERTY_EAXLISTENER_FLAGS +} DSPROPERTY_EAX_LISTENERPROPERTY; + +// OR these flags with property id +#define DSPROPERTY_EAXLISTENER_IMMEDIATE 0x00000000 // changes take effect immediately +#define DSPROPERTY_EAXLISTENER_DEFERRED 0x80000000 // changes take effect later +#define DSPROPERTY_EAXLISTENER_COMMITDEFERREDSETTINGS (DSPROPERTY_EAXLISTENER_NONE | \ + DSPROPERTY_EAXLISTENER_IMMEDIATE) + +typedef struct _EAXVECTOR { + float x; + float y; + float z; +} EAXVECTOR; + +// Use this structure for DSPROPERTY_EAXLISTENER_ALLPARAMETERS +// - all levels are hundredths of decibels +// - all times and delays are in seconds +// +// NOTE: This structure may change in future EAX versions. +// It is recommended to initialize fields by name: +// myListener.lRoom = -1000; +// myListener.lRoomHF = -100; +// ... +// myListener.dwFlags = myFlags /* see EAXLISTENERFLAGS below */ ; +// instead of: +// myListener = { -1000, -100, ... , 0x00000009 }; +// If you want to save and load presets in binary form, you +// should define your own structure to insure future compatibility. +// +typedef struct _EAXLISTENERPROPERTIES +{ + unsigned long ulEnvironment; // sets all listener properties + float flEnvironmentSize; // environment size in meters + float flEnvironmentDiffusion; // environment diffusion + long lRoom; // room effect level (at mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lRoomLF; // relative room effect level at low frequencies + float flDecayTime; // reverberation decay time at mid frequencies + float flDecayHFRatio; // high-frequency to mid-frequency decay time ratio + float flDecayLFRatio; // low-frequency to mid-frequency decay time ratio + long lReflections; // early reflections level relative to room effect + float flReflectionsDelay; // initial reflection delay time + EAXVECTOR vReflectionsPan; // early reflections panning vector + long lReverb; // late reverberation level relative to room effect + float flReverbDelay; // late reverberation delay time relative to initial reflection + EAXVECTOR vReverbPan; // late reverberation panning vector + float flEchoTime; // echo time + float flEchoDepth; // echo depth + float flModulationTime; // modulation time + float flModulationDepth; // modulation depth + float flAirAbsorptionHF; // change in level per meter at high frequencies + float flHFReference; // reference high frequency + float flLFReference; // reference low frequency + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + unsigned long ulFlags; // modifies the behavior of properties +} EAXLISTENERPROPERTIES, *LPEAXLISTENERPROPERTIES; + +// used by DSPROPERTY_EAXLISTENER_ENVIRONMENT +enum +{ + EAX_ENVIRONMENT_GENERIC, + EAX_ENVIRONMENT_PADDEDCELL, + EAX_ENVIRONMENT_ROOM, + EAX_ENVIRONMENT_BATHROOM, + EAX_ENVIRONMENT_LIVINGROOM, + EAX_ENVIRONMENT_STONEROOM, + EAX_ENVIRONMENT_AUDITORIUM, + EAX_ENVIRONMENT_CONCERTHALL, + EAX_ENVIRONMENT_CAVE, + EAX_ENVIRONMENT_ARENA, + EAX_ENVIRONMENT_HANGAR, + EAX_ENVIRONMENT_CARPETEDHALLWAY, + EAX_ENVIRONMENT_HALLWAY, + EAX_ENVIRONMENT_STONECORRIDOR, + EAX_ENVIRONMENT_ALLEY, + EAX_ENVIRONMENT_FOREST, + EAX_ENVIRONMENT_CITY, + EAX_ENVIRONMENT_MOUNTAINS, + EAX_ENVIRONMENT_QUARRY, + EAX_ENVIRONMENT_PLAIN, + EAX_ENVIRONMENT_PARKINGLOT, + EAX_ENVIRONMENT_SEWERPIPE, + EAX_ENVIRONMENT_UNDERWATER, + EAX_ENVIRONMENT_DRUGGED, + EAX_ENVIRONMENT_DIZZY, + EAX_ENVIRONMENT_PSYCHOTIC, + + EAX_ENVIRONMENT_UNDEFINED, + + EAX_ENVIRONMENT_COUNT +}; + +// Used by DSPROPERTY_EAXLISTENER_FLAGS +// +// Note: The number and order of flags may change in future EAX versions. +// It is recommended to use the flag defines as follows: +// myFlags = EAXLISTENERFLAGS_DECAYTIMESCALE | EAXLISTENERFLAGS_REVERBSCALE; +// instead of: +// myFlags = 0x00000009; +// +// These flags determine what properties are affected by environment size. +#define EAXLISTENERFLAGS_DECAYTIMESCALE 0x00000001 // reverberation decay time +#define EAXLISTENERFLAGS_REFLECTIONSSCALE 0x00000002 // reflection level +#define EAXLISTENERFLAGS_REFLECTIONSDELAYSCALE 0x00000004 // initial reflection delay time +#define EAXLISTENERFLAGS_REVERBSCALE 0x00000008 // reflections level +#define EAXLISTENERFLAGS_REVERBDELAYSCALE 0x00000010 // late reverberation delay time +#define EAXLISTENERFLAGS_ECHOTIMESCALE 0x00000040 // echo time +#define EAXLISTENERFLAGS_MODULATIONTIMESCALE 0x00000080 // modulation time + +// This flag limits high-frequency decay time according to air absorption. +#define EAXLISTENERFLAGS_DECAYHFLIMIT 0x00000020 + +#define EAXLISTENERFLAGS_RESERVED 0xFFFFFF00 // reserved future use + +// Property ranges and defaults: + +#define EAXLISTENER_MINENVIRONMENT 0 +#define EAXLISTENER_MAXENVIRONMENT (EAX_ENVIRONMENT_COUNT-1) +#define EAXLISTENER_DEFAULTENVIRONMENT EAX_ENVIRONMENT_GENERIC + +#define EAXLISTENER_MINENVIRONMENTSIZE 1.0f +#define EAXLISTENER_MAXENVIRONMENTSIZE 100.0f +#define EAXLISTENER_DEFAULTENVIRONMENTSIZE 7.5f + +#define EAXLISTENER_MINENVIRONMENTDIFFUSION 0.0f +#define EAXLISTENER_MAXENVIRONMENTDIFFUSION 1.0f +#define EAXLISTENER_DEFAULTENVIRONMENTDIFFUSION 1.0f + +#define EAXLISTENER_MINROOM (-10000) +#define EAXLISTENER_MAXROOM 0 +#define EAXLISTENER_DEFAULTROOM (-1000) + +#define EAXLISTENER_MINROOMHF (-10000) +#define EAXLISTENER_MAXROOMHF 0 +#define EAXLISTENER_DEFAULTROOMHF (-100) + +#define EAXLISTENER_MINROOMLF (-10000) +#define EAXLISTENER_MAXROOMLF 0 +#define EAXLISTENER_DEFAULTROOMLF 0 + +#define EAXLISTENER_MINDECAYTIME 0.1f +#define EAXLISTENER_MAXDECAYTIME 20.0f +#define EAXLISTENER_DEFAULTDECAYTIME 1.49f + +#define EAXLISTENER_MINDECAYHFRATIO 0.1f +#define EAXLISTENER_MAXDECAYHFRATIO 2.0f +#define EAXLISTENER_DEFAULTDECAYHFRATIO 0.83f + +#define EAXLISTENER_MINDECAYLFRATIO 0.1f +#define EAXLISTENER_MAXDECAYLFRATIO 2.0f +#define EAXLISTENER_DEFAULTDECAYLFRATIO 1.00f + +#define EAXLISTENER_MINREFLECTIONS (-10000) +#define EAXLISTENER_MAXREFLECTIONS 1000 +#define EAXLISTENER_DEFAULTREFLECTIONS (-2602) + +#define EAXLISTENER_MINREFLECTIONSDELAY 0.0f +#define EAXLISTENER_MAXREFLECTIONSDELAY 0.3f +#define EAXLISTENER_DEFAULTREFLECTIONSDELAY 0.007f + +#define EAXLISTENER_MINREVERB (-10000) +#define EAXLISTENER_MAXREVERB 2000 +#define EAXLISTENER_DEFAULTREVERB 200 + +#define EAXLISTENER_MINREVERBDELAY 0.0f +#define EAXLISTENER_MAXREVERBDELAY 0.1f +#define EAXLISTENER_DEFAULTREVERBDELAY 0.011f + +#define EAXLISTENER_MINECHOTIME 0.075f +#define EAXLISTENER_MAXECHOTIME 0.25f +#define EAXLISTENER_DEFAULTECHOTIME 0.25f + +#define EAXLISTENER_MINECHODEPTH 0.0f +#define EAXLISTENER_MAXECHODEPTH 1.0f +#define EAXLISTENER_DEFAULTECHODEPTH 0.0f + +#define EAXLISTENER_MINMODULATIONTIME 0.04f +#define EAXLISTENER_MAXMODULATIONTIME 4.0f +#define EAXLISTENER_DEFAULTMODULATIONTIME 0.25f + +#define EAXLISTENER_MINMODULATIONDEPTH 0.0f +#define EAXLISTENER_MAXMODULATIONDEPTH 1.0f +#define EAXLISTENER_DEFAULTMODULATIONDEPTH 0.0f + +#define EAXLISTENER_MINAIRABSORPTIONHF (-100.0f) +#define EAXLISTENER_MAXAIRABSORPTIONHF 0.0f +#define EAXLISTENER_DEFAULTAIRABSORPTIONHF (-5.0f) + +#define EAXLISTENER_MINHFREFERENCE 1000.0f +#define EAXLISTENER_MAXHFREFERENCE 20000.0f +#define EAXLISTENER_DEFAULTHFREFERENCE 5000.0f + +#define EAXLISTENER_MINLFREFERENCE 20.0f +#define EAXLISTENER_MAXLFREFERENCE 1000.0f +#define EAXLISTENER_DEFAULTLFREFERENCE 250.0f + +#define EAXLISTENER_MINROOMROLLOFFFACTOR 0.0f +#define EAXLISTENER_MAXROOMROLLOFFFACTOR 10.0f +#define EAXLISTENER_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXLISTENER_DEFAULTFLAGS (EAXLISTENERFLAGS_DECAYTIMESCALE | \ + EAXLISTENERFLAGS_REFLECTIONSSCALE | \ + EAXLISTENERFLAGS_REFLECTIONSDELAYSCALE | \ + EAXLISTENERFLAGS_REVERBSCALE | \ + EAXLISTENERFLAGS_REVERBDELAYSCALE | \ + EAXLISTENERFLAGS_DECAYHFLIMIT) + + + +/* +* EAX 3.0 buffer property set {A8FA6881-B476-11d3-BDB9-00C0F02DDF87} +*/ +DEFINE_ALGUID(DSPROPSETID_EAX30_BufferProperties, + 0xa8fa6881, + 0xb476, + 0x11d3, + 0xbd, 0xb9, 0x0, 0xc0, 0xf0, 0x2d, 0xdf, 0x87); + +// For compatibility with future EAX versions: +#define DSPROPSETID_EAX_BufferProperties DSPROPSETID_EAX30_BufferProperties +#define DSPROPSETID_EAX_SourceProperties DSPROPSETID_EAX30_BufferProperties + +typedef enum +{ + DSPROPERTY_EAXBUFFER_NONE, + DSPROPERTY_EAXBUFFER_ALLPARAMETERS, + DSPROPERTY_EAXBUFFER_OBSTRUCTIONPARAMETERS, + DSPROPERTY_EAXBUFFER_OCCLUSIONPARAMETERS, + DSPROPERTY_EAXBUFFER_EXCLUSIONPARAMETERS, + DSPROPERTY_EAXBUFFER_DIRECT, + DSPROPERTY_EAXBUFFER_DIRECTHF, + DSPROPERTY_EAXBUFFER_ROOM, + DSPROPERTY_EAXBUFFER_ROOMHF, + DSPROPERTY_EAXBUFFER_OBSTRUCTION, + DSPROPERTY_EAXBUFFER_OBSTRUCTIONLFRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSION, + DSPROPERTY_EAXBUFFER_OCCLUSIONLFRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSIONROOMRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSIONDIRECTRATIO, + DSPROPERTY_EAXBUFFER_EXCLUSION, + DSPROPERTY_EAXBUFFER_EXCLUSIONLFRATIO, + DSPROPERTY_EAXBUFFER_OUTSIDEVOLUMEHF, + DSPROPERTY_EAXBUFFER_DOPPLERFACTOR, + DSPROPERTY_EAXBUFFER_ROLLOFFFACTOR, + DSPROPERTY_EAXBUFFER_ROOMROLLOFFFACTOR, + DSPROPERTY_EAXBUFFER_AIRABSORPTIONFACTOR, + DSPROPERTY_EAXBUFFER_FLAGS +} DSPROPERTY_EAX_BUFFERPROPERTY; + +// OR these flags with property id +#define DSPROPERTY_EAXBUFFER_IMMEDIATE 0x00000000 // changes take effect immediately +#define DSPROPERTY_EAXBUFFER_DEFERRED 0x80000000 // changes take effect later +#define DSPROPERTY_EAXBUFFER_COMMITDEFERREDSETTINGS (DSPROPERTY_EAXBUFFER_NONE | \ + DSPROPERTY_EAXBUFFER_IMMEDIATE) + +// Use this structure for DSPROPERTY_EAXBUFFER_ALLPARAMETERS +// - all levels are hundredths of decibels +// - all delays are in seconds +// +// NOTE: This structure may change in future EAX versions. +// It is recommended to initialize fields by name: +// myBuffer.lDirect = 0; +// myBuffer.lDirectHF = -200; +// ... +// myBuffer.dwFlags = myFlags /* see EAXBUFFERFLAGS below */ ; +// instead of: +// myBuffer = { 0, -200, ... , 0x00000003 }; +// +typedef struct _EAXBUFFERPROPERTIES +{ + long lDirect; // direct path level (at low and mid frequencies) + long lDirectHF; // relative direct path level at high frequencies + long lRoom; // room effect level (at low and mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lObstruction; // main obstruction control (attenuation at high frequencies) + float flObstructionLFRatio; // obstruction low-frequency level re. main control + long lOcclusion; // main occlusion control (attenuation at high frequencies) + float flOcclusionLFRatio; // occlusion low-frequency level re. main control + float flOcclusionRoomRatio; // relative occlusion control for room effect + float flOcclusionDirectRatio; // relative occlusion control for direct path + long lExclusion; // main exlusion control (attenuation at high frequencies) + float flExclusionLFRatio; // exclusion low-frequency level re. main control + long lOutsideVolumeHF; // outside sound cone level at high frequencies + float flDopplerFactor; // like DS3D flDopplerFactor but per source + float flRolloffFactor; // like DS3D flRolloffFactor but per source + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + float flAirAbsorptionFactor; // multiplies DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF + unsigned long ulFlags; // modifies the behavior of properties +} EAXBUFFERPROPERTIES, *LPEAXBUFFERPROPERTIES; + +// Use this structure for DSPROPERTY_EAXBUFFER_OBSTRUCTION, +typedef struct _EAXOBSTRUCTIONPROPERTIES +{ + long lObstruction; + float flObstructionLFRatio; +} EAXOBSTRUCTIONPROPERTIES, *LPEAXOBSTRUCTIONPROPERTIES; + +// Use this structure for DSPROPERTY_EAXBUFFER_OCCLUSION +typedef struct _EAXOCCLUSIONPROPERTIES +{ + long lOcclusion; + float flOcclusionLFRatio; + float flOcclusionRoomRatio; + float flOcclusionDirectRatio; +} EAXOCCLUSIONPROPERTIES, *LPEAXOCCLUSIONPROPERTIES; + +// Use this structure for DSPROPERTY_EAXBUFFER_EXCLUSION +typedef struct _EAXEXCLUSIONPROPERTIES +{ + long lExclusion; + float flExclusionLFRatio; +} EAXEXCLUSIONPROPERTIES, *LPEAXEXCLUSIONPROPERTIES; + +// Used by DSPROPERTY_EAXBUFFER_FLAGS +// TRUE: value is computed automatically - property is an offset +// FALSE: value is used directly +// +// Note: The number and order of flags may change in future EAX versions. +// To insure future compatibility, use flag defines as follows: +// myFlags = EAXBUFFERFLAGS_DIRECTHFAUTO | EAXBUFFERFLAGS_ROOMAUTO; +// instead of: +// myFlags = 0x00000003; +// +#define EAXBUFFERFLAGS_DIRECTHFAUTO 0x00000001 // affects DSPROPERTY_EAXBUFFER_DIRECTHF +#define EAXBUFFERFLAGS_ROOMAUTO 0x00000002 // affects DSPROPERTY_EAXBUFFER_ROOM +#define EAXBUFFERFLAGS_ROOMHFAUTO 0x00000004 // affects DSPROPERTY_EAXBUFFER_ROOMHF + +#define EAXBUFFERFLAGS_RESERVED 0xFFFFFFF8 // reserved future use + +// Property ranges and defaults: + +#define EAXBUFFER_MINDIRECT (-10000) +#define EAXBUFFER_MAXDIRECT 1000 +#define EAXBUFFER_DEFAULTDIRECT 0 + +#define EAXBUFFER_MINDIRECTHF (-10000) +#define EAXBUFFER_MAXDIRECTHF 0 +#define EAXBUFFER_DEFAULTDIRECTHF 0 + +#define EAXBUFFER_MINROOM (-10000) +#define EAXBUFFER_MAXROOM 1000 +#define EAXBUFFER_DEFAULTROOM 0 + +#define EAXBUFFER_MINROOMHF (-10000) +#define EAXBUFFER_MAXROOMHF 0 +#define EAXBUFFER_DEFAULTROOMHF 0 + +#define EAXBUFFER_MINOBSTRUCTION (-10000) +#define EAXBUFFER_MAXOBSTRUCTION 0 +#define EAXBUFFER_DEFAULTOBSTRUCTION 0 + +#define EAXBUFFER_MINOBSTRUCTIONLFRATIO 0.0f +#define EAXBUFFER_MAXOBSTRUCTIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTOBSTRUCTIONLFRATIO 0.0f + +#define EAXBUFFER_MINOCCLUSION (-10000) +#define EAXBUFFER_MAXOCCLUSION 0 +#define EAXBUFFER_DEFAULTOCCLUSION 0 + +#define EAXBUFFER_MINOCCLUSIONLFRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTOCCLUSIONLFRATIO 0.25f + +#define EAXBUFFER_MINOCCLUSIONROOMRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONROOMRATIO 10.0f +#define EAXBUFFER_DEFAULTOCCLUSIONROOMRATIO 1.5f + +#define EAXBUFFER_MINOCCLUSIONDIRECTRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONDIRECTRATIO 10.0f +#define EAXBUFFER_DEFAULTOCCLUSIONDIRECTRATIO 1.0f + +#define EAXBUFFER_MINEXCLUSION (-10000) +#define EAXBUFFER_MAXEXCLUSION 0 +#define EAXBUFFER_DEFAULTEXCLUSION 0 + +#define EAXBUFFER_MINEXCLUSIONLFRATIO 0.0f +#define EAXBUFFER_MAXEXCLUSIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTEXCLUSIONLFRATIO 1.0f + +#define EAXBUFFER_MINOUTSIDEVOLUMEHF (-10000) +#define EAXBUFFER_MAXOUTSIDEVOLUMEHF 0 +#define EAXBUFFER_DEFAULTOUTSIDEVOLUMEHF 0 + +#define EAXBUFFER_MINDOPPLERFACTOR 0.0f +#define EAXBUFFER_MAXDOPPLERFACTOR 10.f +#define EAXBUFFER_DEFAULTDOPPLERFACTOR 0.0f + +#define EAXBUFFER_MINROLLOFFFACTOR 0.0f +#define EAXBUFFER_MAXROLLOFFFACTOR 10.f +#define EAXBUFFER_DEFAULTROLLOFFFACTOR 0.0f + +#define EAXBUFFER_MINROOMROLLOFFFACTOR 0.0f +#define EAXBUFFER_MAXROOMROLLOFFFACTOR 10.f +#define EAXBUFFER_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXBUFFER_MINAIRABSORPTIONFACTOR 0.0f +#define EAXBUFFER_MAXAIRABSORPTIONFACTOR 10.0f +#define EAXBUFFER_DEFAULTAIRABSORPTIONFACTOR 1.0f + +#define EAXBUFFER_DEFAULTFLAGS (EAXBUFFERFLAGS_DIRECTHFAUTO | \ + EAXBUFFERFLAGS_ROOMAUTO | \ + EAXBUFFERFLAGS_ROOMHFAUTO ) diff --git a/Engine/lib/openal/FreeBSD/openALFn.h b/Engine/lib/openal/FreeBSD/openALFn.h new file mode 100644 index 000000000..0f0b00d48 --- /dev/null +++ b/Engine/lib/openal/FreeBSD/openALFn.h @@ -0,0 +1,101 @@ +#ifndef AL_FUNCTION +#define AL_FUNCTION(fn_return, fn_name, fn_args); +#endif + +#ifndef AL_EXTENSION +#define AL_EXTENSION(ext_name) +#endif + +#ifndef AL_EXT_FUNCTION +#define AL_EXT_FUNCTION(ext_name, fn_return, fn_name, fn_args) +#endif + +// AL functions +AL_FUNCTION(ALvoid, alEnable, ( ALenum capability )) +AL_FUNCTION(ALvoid, alDisable, ( ALenum capability )) +AL_FUNCTION(ALboolean, alIsEnabled, ( ALenum capability )) +AL_FUNCTION(ALvoid, alHint, ( ALenum target, ALenum mode )) +AL_FUNCTION(ALboolean, alGetBoolean, ( ALenum param )) +AL_FUNCTION(ALint, alGetInteger, ( ALenum param )) +AL_FUNCTION(ALfloat, alGetFloat, ( ALenum param )) +AL_FUNCTION(ALdouble, alGetDouble, ( ALenum param )) +AL_FUNCTION(ALvoid, alGetBooleanv, ( ALenum param, ALboolean* data )) +AL_FUNCTION(ALvoid, alGetIntegerv, ( ALenum param, ALint* data )) +AL_FUNCTION(ALvoid, alGetFloatv, ( ALenum param, ALfloat* data )) +AL_FUNCTION(ALvoid, alGetDoublev, ( ALenum param, ALdouble* data )) +AL_FUNCTION(const ALubyte*, alGetString, ( ALenum param )) +AL_FUNCTION(ALenum, alGetError, ( ALvoid )) +AL_FUNCTION(ALboolean, alIsExtensionPresent, ( const ALubyte* fname )) +AL_FUNCTION(ALvoid*, alGetProcAddress, ( const ALubyte* fname )) +AL_FUNCTION(ALenum, alGetEnumValue, ( const ALubyte* ename )) +AL_FUNCTION(ALvoid, alListenerf, ( ALenum pname, ALfloat param )) +AL_FUNCTION(ALvoid, alListener3f, ( ALenum pname, ALfloat param1, ALfloat param2, ALfloat param3 )) +AL_FUNCTION(ALvoid, alListenerfv, ( ALenum pname, ALfloat* param )) +AL_FUNCTION(ALvoid, alGetListeneri, ( ALenum pname, ALint* value )) +AL_FUNCTION(ALvoid, alGetListenerf, ( ALenum pname, ALfloat* values )) +AL_FUNCTION(ALvoid, alGetListenerfv, ( ALenum pname, ALfloat* values )) +AL_FUNCTION(ALvoid, alGenSources, ( ALsizei n, ALuint* sources )) +AL_FUNCTION(ALvoid, alDeleteSources, ( ALsizei n, ALuint* sources )) +AL_FUNCTION(ALboolean, alIsSource, ( ALuint sid )) +AL_FUNCTION(ALvoid, alSourcei, ( ALuint sid, ALenum param, ALint value )) +AL_FUNCTION(ALvoid, alSourcef, ( ALuint sid, ALenum param, ALfloat value )) +AL_FUNCTION(ALvoid, alSource3f, ( ALuint sid, ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 )) +AL_FUNCTION(ALvoid, alSourcefv, ( ALuint sid, ALenum param, ALfloat* values )) +AL_FUNCTION(ALvoid, alGetSourcei, ( ALuint sid, ALenum pname, ALint* value )) +AL_FUNCTION(ALvoid, alGetSourcef, ( ALuint sid, ALenum pname, ALfloat* value )) +AL_FUNCTION(ALvoid, alGetSourcefv, ( ALuint sid, ALenum pname, ALfloat* values )) +AL_FUNCTION(ALvoid, alSourcePlayv, ( ALuint ns, ALuint* ids )) +AL_FUNCTION(ALvoid, alSourceStopv, ( ALuint ns, ALuint* ids )) +AL_FUNCTION(ALvoid, alSourcePlay, ( ALuint sid )) +AL_FUNCTION(ALvoid, alSourcePause, ( ALuint sid )) +AL_FUNCTION(ALvoid, alSourceStop, ( ALuint sid )) +AL_FUNCTION(ALvoid, alGenBuffers, ( ALsizei n, ALuint* samples )) +AL_FUNCTION(ALvoid, alDeleteBuffers, ( ALsizei n, ALuint* samples )) +AL_FUNCTION(ALboolean, alIsBuffer, ( ALuint buffer )) +AL_FUNCTION(ALvoid, alBufferData, ( ALuint buffer, ALenum format, ALvoid* data, ALsizei size, ALsizei freq )) +AL_FUNCTION(ALsizei, alBufferAppendData, ( ALuint buffer, ALenum format, ALvoid* data, ALsizei size, ALsizei freq )) +AL_FUNCTION(ALvoid, alGetBufferi, ( ALuint buffer, ALenum param, ALint* value )) +AL_FUNCTION(ALvoid, alGetBufferf, ( ALuint buffer, ALenum param, ALfloat* value )) + +// ALC functions +AL_FUNCTION(ALvoid*, alcCreateContext, ( ALint* attrlist )) +AL_FUNCTION(ALCenum, alcMakeContextCurrent, ( ALvoid* context )) +AL_FUNCTION(ALvoid*, alcUpdateContext, ( ALvoid* context )) +AL_FUNCTION(ALCenum, alcDestroyContext, ( ALvoid* context )) +AL_FUNCTION(ALCenum, alcGetError, ( ALvoid )) +AL_FUNCTION(const ALubyte *, alcGetErrorString, ( ALvoid )) +AL_FUNCTION(ALvoid*, alcGetCurrentContext, ( ALvoid )) + +// ALUT functions +AL_FUNCTION(void, alutInit, ( int* argc, char** argv )) +AL_FUNCTION(void, alutExit, ( ALvoid )) +AL_FUNCTION(ALboolean, alutLoadWAV, ( const char* fname, ALvoid** data, ALsizei* format, ALsizei* size, ALsizei* bits, ALsizei* freq )) + +// Extensions +AL_EXTENSION(AL_EXT_IASIG) +AL_EXT_FUNCTION(AL_EXT_IASIG, ALvoid, alGenEnvironmentIASIG, ( ALsizei n, ALuint* environs )) +AL_EXT_FUNCTION(AL_EXT_IASIG, ALvoid, alDeleteEnvironmentIASIG, ( ALsizei n, ALuint* environs )) +AL_EXT_FUNCTION(AL_EXT_IASIG, ALboolean, alIsEnvironmentIASIG, ( ALuint environment )) +AL_EXT_FUNCTION(AL_EXT_IASIG, ALvoid, alEnvironmentiIASIG, ( ALuint eid, ALenum param, ALint value )) + +AL_EXTENSION(AL_EXT_DYNAMIX) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alBufferi_EXT, ( ALuint buffer, ALenum pname, ALint value )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alBufferSyncData_EXT, ( ALuint buffer, ALenum format, ALvoid* data, ALsizei size, ALsizei freq )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alBufferStreamFile_EXT, ( ALuint buffer, const ALubyte* filename )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alSourceCallback_EXT, ( ALuint source, ALvoid* callback )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALvoid, alSourceResetEnvironment_EXT, ( ALuint source )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alContexti_EXT, ( ALenum pname, ALint value )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alGetContexti_EXT, ( ALenum pname, ALint* value )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alGetContextstr_EXT, ( ALenum pname, ALuint idx, ALubyte** value )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alCaptureInit_EXT, ( ALenum format, ALuint rate, ALsizei bufferSize )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alCaptureDestroy_EXT, ( ALvoid )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alCaptureStart_EXT, ( ALvoid )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alCaptureStop_EXT, ( ALvoid )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALsizei, alCaptureGetData_EXT, ( ALvoid* data, ALsizei n, ALenum format, ALuint rate )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALvoid, alEnvironmentfIASIG, ( ALuint eid, ALenum param, ALfloat value )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALvoid, alGetEnvironmentiIASIG_EXT, ( ALuint eid, ALenum param, ALint * value )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALvoid, alGetEnvironmentfIASIG_EXT, ( ALuint eid, ALenum param, ALfloat * value )) + +#undef AL_EXTENSION +#undef AL_FUNCTION +#undef AL_EXT_FUNCTION diff --git a/Engine/lib/openal/LINUX/al/al.h b/Engine/lib/openal/LINUX/al/al.h new file mode 100644 index 000000000..a4132f0a0 --- /dev/null +++ b/Engine/lib/openal/LINUX/al/al.h @@ -0,0 +1,491 @@ +#ifndef _AL_H_ +#define _AL_H_ + +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2000 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include "altypes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 + #ifdef _LIB + #define ALAPI __declspec(dllexport) + #else + #define ALAPI __declspec(dllimport) + #endif + #define ALAPIENTRY __cdecl + #define AL_CALLBACK +#else + #ifdef TARGET_OS_MAC + #if TARGET_OS_MAC + #pragma export on + #endif + #endif + #define ALAPI + #define ALAPIENTRY __cdecl + #define AL_CALLBACK +#endif + +#define OPENAL + +#ifndef AL_NO_PROTOTYPES + +/** + * OpenAL Maintenance Functions + * Initialization and exiting. + * State Management and Query. + * Error Handling. + * Extension Support. + */ + +/** State management. */ +ALAPI ALvoid ALAPIENTRY alEnable( ALenum capability ); +ALAPI ALvoid ALAPIENTRY alDisable( ALenum capability ); +ALAPI ALboolean ALAPIENTRY alIsEnabled( ALenum capability ); + +/** Application preferences for driver performance choices. */ +ALAPI ALvoid ALAPIENTRY alHint( ALenum target, ALenum mode ); + +/** State retrieval. */ +ALAPI ALboolean ALAPIENTRY alGetBoolean( ALenum param ); +ALAPI ALint ALAPIENTRY alGetInteger( ALenum param ); +ALAPI ALfloat ALAPIENTRY alGetFloat( ALenum param ); +ALAPI ALdouble ALAPIENTRY alGetDouble( ALenum param ); +ALAPI ALvoid ALAPIENTRY alGetBooleanv( ALenum param, ALboolean* data ); +ALAPI ALvoid ALAPIENTRY alGetIntegerv( ALenum param, ALint* data ); +ALAPI ALvoid ALAPIENTRY alGetFloatv( ALenum param, ALfloat* data ); +ALAPI ALvoid ALAPIENTRY alGetDoublev( ALenum param, ALdouble* data ); +ALAPI ALubyte* ALAPIENTRY alGetString( ALenum param ); + +/** + * Error support. + * Obtain the most recent error generated in the AL state machine. + */ +ALAPI ALenum ALAPIENTRY alGetError( ALvoid ); + + +/** + * Extension support. + * Obtain the address of a function (usually an extension) + * with the name fname. All addresses are context-independent. + */ +ALAPI ALboolean ALAPIENTRY alIsExtensionPresent( ALubyte* fname ); + + +/** + * Extension support. + * Obtain the address of a function (usually an extension) + * with the name fname. All addresses are context-independent. + */ +ALAPI ALvoid* ALAPIENTRY alGetProcAddress( ALubyte* fname ); + + +/** + * Extension support. + * Obtain the integer value of an enumeration (usually an extension) with the name ename. + */ +ALAPI ALenum ALAPIENTRY alGetEnumValue( ALubyte* ename ); + + + + +/** + * LISTENER + * Listener is the sample position for a given context. + * The multi-channel (usually stereo) output stream generated + * by the mixer is parametrized by this Listener object: + * its position and velocity relative to Sources, within + * occluder and reflector geometry. + */ + + + +/** + * + * Listener Environment: default 0. + */ +ALAPI ALvoid ALAPIENTRY alListeneri( ALenum param, ALint value ); + + +/** + * + * Listener Gain: default 1.0f. + */ +ALAPI ALvoid ALAPIENTRY alListenerf( ALenum param, ALfloat value ); + + +/** + * + * Listener Position. + * Listener Velocity. + */ +ALAPI ALvoid ALAPIENTRY alListener3f( ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 ); + + +/** + * + * Listener Position: ALfloat[3] + * Listener Velocity: ALfloat[3] + * Listener Orientation: ALfloat[6] (forward and up vector). + */ +ALAPI ALvoid ALAPIENTRY alListenerfv( ALenum param, ALfloat* values ); + +ALAPI ALvoid ALAPIENTRY alGetListeneri( ALenum param, ALint* value ); +ALAPI ALvoid ALAPIENTRY alGetListenerf( ALenum param, ALfloat* value ); +ALAPI ALvoid ALAPIENTRY alGetListener3f( ALenum param, ALfloat* v1, ALfloat* v2, ALfloat* v3 ); +ALAPI ALvoid ALAPIENTRY alGetListenerfv( ALenum param, ALfloat* values ); + + +/** + * SOURCE + * Source objects are by default localized. Sources + * take the PCM data provided in the specified Buffer, + * apply Source-specific modifications, and then + * submit them to be mixed according to spatial + * arrangement etc. + */ + + + +/** Create Source objects. */ +ALAPI ALvoid ALAPIENTRY alGenSources( ALsizei n, ALuint* sources ); + +/** Delete Source objects. */ +ALAPI ALvoid ALAPIENTRY alDeleteSources( ALsizei n, ALuint* sources ); + +/** Verify a handle is a valid Source. */ +ALAPI ALboolean ALAPIENTRY alIsSource( ALuint id ); + +/** Set an integer parameter for a Source object. */ +ALAPI ALvoid ALAPIENTRY alSourcei( ALuint source, ALenum param, ALint value ); +ALAPI ALvoid ALAPIENTRY alSourcef( ALuint source, ALenum param, ALfloat value ); +ALAPI ALvoid ALAPIENTRY alSource3f( ALuint source, ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 ); +ALAPI ALvoid ALAPIENTRY alSourcefv( ALuint source, ALenum param, ALfloat* values ); + +/** Get an integer parameter for a Source object. */ +ALAPI ALvoid ALAPIENTRY alGetSourcei( ALuint source, ALenum param, ALint* value ); +ALAPI ALvoid ALAPIENTRY alGetSourcef( ALuint source, ALenum param, ALfloat* value ); +ALAPI ALvoid ALAPIENTRY alGetSource3f( ALuint source, ALenum param, ALfloat* v1, ALfloat* v2, ALfloat* v3 ); +ALAPI ALvoid ALAPIENTRY alGetSourcefv( ALuint source, ALenum param, ALfloat* values ); + +ALAPI ALvoid ALAPIENTRY alSourcePlayv( ALsizei n, ALuint *sources ); +ALAPI ALvoid ALAPIENTRY alSourcePausev( ALsizei n, ALuint *sources ); +ALAPI ALvoid ALAPIENTRY alSourceStopv( ALsizei n, ALuint *sources ); +ALAPI ALvoid ALAPIENTRY alSourceRewindv(ALsizei n,ALuint *sources); + +/** Activate a source, start replay. */ +ALAPI ALvoid ALAPIENTRY alSourcePlay( ALuint source ); + +/** + * Pause a source, + * temporarily remove it from the mixer list. + */ +ALAPI ALvoid ALAPIENTRY alSourcePause( ALuint source ); + +/** + * Stop a source, + * temporarily remove it from the mixer list, + * and reset its internal state to pre-Play. + * To remove a Source completely, it has to be + * deleted following Stop, or before Play. + */ +ALAPI ALvoid ALAPIENTRY alSourceStop( ALuint source ); + +/** + * Rewinds a source, + * temporarily remove it from the mixer list, + * and reset its internal state to pre-Play. + */ +ALAPI ALvoid ALAPIENTRY alSourceRewind( ALuint source ); + + + +/** + * BUFFER + * Buffer objects are storage space for sample data. + * Buffers are referred to by Sources. There can be more than + * one Source using the same Buffer data. If Buffers have + * to be duplicated on a per-Source basis, the driver has to + * take care of allocation, copying, and deallocation as well + * as propagating buffer data changes. + */ + + + + +/** Buffer object generation. */ +ALAPI ALvoid ALAPIENTRY alGenBuffers( ALsizei n, ALuint* buffers ); +ALAPI ALvoid ALAPIENTRY alDeleteBuffers( ALsizei n, ALuint* buffers ); +ALAPI ALboolean ALAPIENTRY alIsBuffer( ALuint buffer ); + +/** + * Specify the data to be filled into a buffer. + */ +ALAPI ALvoid ALAPIENTRY alBufferData( ALuint buffer, + ALenum format, + ALvoid* data, + ALsizei size, + ALsizei freq ); + + +ALAPI ALvoid ALAPIENTRY alGetBufferi( ALuint buffer, ALenum param, ALint* value ); +ALAPI ALvoid ALAPIENTRY alGetBufferf( ALuint buffer, ALenum param, ALfloat* value ); + + + + +/** + * Queue stuff + */ + +ALAPI ALvoid ALAPIENTRY alSourceQueueBuffers( ALuint source, ALsizei n, ALuint* buffers ); +ALAPI ALvoid ALAPIENTRY alSourceUnqueueBuffers( ALuint source, ALsizei n, ALuint* buffers ); + +/** + * Knobs and dials + */ +ALAPI ALvoid ALAPIENTRY alDistanceModel( ALenum value ); +ALAPI ALvoid ALAPIENTRY alDopplerFactor( ALfloat value ); +ALAPI ALvoid ALAPIENTRY alDopplerVelocity( ALfloat value ); + +#else /* AL_NO_PROTOTYPES */ + +/** + * OpenAL Maintenance Functions + * Initialization and exiting. + * State Management and Query. + * Error Handling. + * Extension Support. + */ + +/** State management. */ +ALAPI ALvoid ALAPIENTRY (*alEnable)( ALenum capability ); +ALAPI ALvoid ALAPIENTRY (*alDisable)( ALenum capability ); +ALAPI ALboolean ALAPIENTRY (*alIsEnabled)( ALenum capability ); + +/** Application preferences for driver performance choices. */ +ALAPI ALvoid ALAPIENTRY (*alHint)( ALenum target, ALenum mode ); + +/** State retrieval. */ +ALAPI ALboolean ALAPIENTRY (*alGetBoolean)( ALenum param ); +ALAPI ALint ALAPIENTRY (*alGetInteger)( ALenum param ); +ALAPI ALfloat ALAPIENTRY (*alGetFloat)( ALenum param ); +ALAPI ALdouble ALAPIENTRY (*alGetDouble)( ALenum param ); +ALAPI ALvoid ALAPIENTRY (*alGetBooleanv)( ALenum param, ALboolean* data ); +ALAPI ALvoid ALAPIENTRY (*alGetIntegerv)( ALenum param, ALint* data ); +ALAPI ALvoid ALAPIENTRY (*alGetFloatv)( ALenum param, ALfloat* data ); +ALAPI ALvoid ALAPIENTRY (*alGetDoublev)( ALenum param, ALdouble* data ); +ALAPI ALubyte* ALAPIENTRY (*alGetString)( ALenum param ); + +/** + * Error support. + * Obtain the most recent error generated in the AL state machine. + */ +ALAPI ALenum ALAPIENTRY (*alGetError)( ALvoid ); + + +/** + * Extension support. + * Obtain the address of a function (usually an extension) + * with the name fname. All addresses are context-independent. + */ +ALAPI ALboolean ALAPIENTRY (*alIsExtensionPresent)( ALubyte* fname ); + + +/** + * Extension support. + * Obtain the address of a function (usually an extension) + * with the name fname. All addresses are context-independent. + */ +ALAPI ALvoid* ALAPIENTRY (*alGetProcAddress)( ALubyte* fname ); + + +/** + * Extension support. + * Obtain the integer value of an enumeration (usually an extension) with the name ename. + */ +ALAPI ALenum ALAPIENTRY (*alGetEnumValue)( ALubyte* ename ); + + + + +/** + * LISTENER + * Listener is the sample position for a given context. + * The multi-channel (usually stereo) output stream generated + * by the mixer is parametrized by this Listener object: + * its position and velocity relative to Sources, within + * occluder and reflector geometry. + */ + + + +/** + * + * Listener Environment: default 0. + */ +ALAPI ALvoid ALAPIENTRY (*alListeneri)( ALenum param, ALint value ); + + +/** + * + * Listener Gain: default 1.0f. + */ +ALAPI ALvoid ALAPIENTRY (*alListenerf)( ALenum param, ALfloat value ); + + +/** + * + * Listener Position. + * Listener Velocity. + */ +ALAPI ALvoid ALAPIENTRY (*alListener3f)( ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 ); + + +/** + * + * Listener Position: ALfloat[3] + * Listener Velocity: ALfloat[3] + * Listener Orientation: ALfloat[6] (forward and up vector). + */ +ALAPI ALvoid ALAPIENTRY (*alListenerfv)( ALenum param, ALfloat* values ); + +ALAPI ALvoid ALAPIENTRY (*alGetListeneri)( ALenum param, ALint* value ); +ALAPI ALvoid ALAPIENTRY (*alGetListenerf)( ALenum param, ALfloat* value ); +ALAPI ALvoid ALAPIENTRY (*alGetListener3f)( ALenum param, ALfloat* v1, ALfloat* v2, ALfloat* v3 ); +ALAPI ALvoid ALAPIENTRY (*alGetListenerfv)( ALenum param, ALfloat* values ); + + +/** + * SOURCE + * Source objects are by default localized. Sources + * take the PCM data provided in the specified Buffer, + * apply Source-specific modifications, and then + * submit them to be mixed according to spatial + * arrangement etc. + */ + + + +/** Create Source objects. */ +ALAPI ALvoid ALAPIENTRY (*alGenSources)( ALsizei n, ALuint* sources ); + +/** Delete Source objects. */ +ALAPI ALvoid ALAPIENTRY (*alDeleteSources)( ALsizei n, ALuint* sources ); + +/** Verify a handle is a valid Source. */ +ALAPI ALboolean ALAPIENTRY (*alIsSource)( ALuint id ); + +/** Set an integer parameter for a Source object. */ +ALAPI ALvoid ALAPIENTRY (*alSourcei)( ALuint source, ALenum param, ALint value ); +ALAPI ALvoid ALAPIENTRY (*alSourcef)( ALuint source, ALenum param, ALfloat value ); +ALAPI ALvoid ALAPIENTRY (*alSource3f)( ALuint source, ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 ); +ALAPI ALvoid ALAPIENTRY (*alSourcefv)( ALuint source, ALenum param, ALfloat* values ); + +/** Get an integer parameter for a Source object. */ +ALAPI ALvoid ALAPIENTRY (*alGetSourcei)( ALuint source, ALenum param, ALint* value ); +ALAPI ALvoid ALAPIENTRY (*alGetSourcef)( ALuint source, ALenum param, ALfloat* value ); +ALAPI ALvoid ALAPIENTRY (*alGetSourcefv)( ALuint source, ALenum param, ALfloat* values ); + +ALAPI ALvoid ALAPIENTRY (*alSourcePlayv)( ALsizei n, ALuint *sources ); +ALAPI ALvoid ALAPIENTRY (*alSourceStopv)( ALsizei n, ALuint *sources ); + +/** Activate a source, start replay. */ +ALAPI ALvoid ALAPIENTRY (*alSourcePlay)( ALuint source ); + +/** + * Pause a source, + * temporarily remove it from the mixer list. + */ +ALAPI ALvoid ALAPIENTRY (*alSourcePause)( ALuint source ); + +/** + * Stop a source, + * temporarily remove it from the mixer list, + * and reset its internal state to pre-Play. + * To remove a Source completely, it has to be + * deleted following Stop, or before Play. + */ +ALAPI ALvoid ALAPIENTRY (*alSourceStop)( ALuint source ); + + + +/** + * BUFFER + * Buffer objects are storage space for sample data. + * Buffers are referred to by Sources. There can be more than + * one Source using the same Buffer data. If Buffers have + * to be duplicated on a per-Source basis, the driver has to + * take care of allocation, copying, and deallocation as well + * as propagating buffer data changes. + */ + + + + +/** Buffer object generation. */ +ALAPI ALvoid ALAPIENTRY (*alGenBuffers)( ALsizei n, ALuint* buffers ); +ALAPI ALvoid ALAPIENTRY (*alDeleteBuffers)( ALsizei n, ALuint* buffers ); +ALAPI ALboolean ALAPIENTRY (*alIsBuffer)( ALuint buffer ); + +/** + * Specify the data to be filled into a buffer. + */ +ALAPI ALvoid ALAPIENTRY (*alBufferData)( ALuint buffer, + ALenum format, + ALvoid* data, + ALsizei size, + ALsizei freq ); + +ALAPI ALvoid ALAPIENTRY (*alGetBufferi)( ALuint buffer, ALenum param, ALint* value ); +ALAPI ALvoid ALAPIENTRY (*alGetBufferf)( ALuint buffer, ALenum param, ALfloat* value ); + + + + +/** + * Queue stuff + */ +ALAPI ALvoid ALAPIENTRY (*alSourceQueueBuffers)( ALuint source, ALsizei n, ALuint* buffers ); +ALAPI ALvoid ALAPIENTRY (*alSourceUnqueueBuffers)( ALuint source, ALsizei n, ALuint* buffers ); + +/** + * Knobs and dials + */ +ALAPI ALvoid ALAPIENTRY (*alDistanceModel)( ALenum value ); +ALAPI ALvoid ALAPIENTRY (*alDopplerFactor)( ALfloat value ); +ALAPI ALvoid ALAPIENTRY (*alDopplerVelocity)( ALfloat value ); + +#endif /* AL_NO_PROTOTYPES */ + +#ifdef TARGET_OS_MAC + #if TARGET_OS_MAC + #pragma export off + #endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Engine/lib/openal/LINUX/al/al_func.h b/Engine/lib/openal/LINUX/al/al_func.h new file mode 100644 index 000000000..9a280ad2a --- /dev/null +++ b/Engine/lib/openal/LINUX/al/al_func.h @@ -0,0 +1,69 @@ + +AL_FUNCTION(ALvoid, alEnable, ( ALenum capability ), return; ) +AL_FUNCTION(ALvoid, alDisable, ( ALenum capability ), return; ) +AL_FUNCTION(ALboolean, alIsEnabled, ( ALenum capability ), return AL_FALSE; ) + +//AL_FUNCTION(ALvoid, alHint, ( ALenum target, ALenum mode ), return; ) + +AL_FUNCTION(ALboolean, alGetBoolean, ( ALenum param ), return AL_FALSE; ) +AL_FUNCTION(ALint, alGetInteger, ( ALenum param ), return 0; ) +AL_FUNCTION(ALfloat, alGetFloat, ( ALenum param ), return 0.0f; ) +AL_FUNCTION(ALdouble, alGetDouble, ( ALenum param ), return 0.0; ) +AL_FUNCTION(ALvoid, alGetBooleanv, ( ALenum param, ALboolean* data ), return; ) +AL_FUNCTION(ALvoid, alGetIntegerv, ( ALenum param, ALint* data ), return; ) +AL_FUNCTION(ALvoid, alGetFloatv, ( ALenum param, ALfloat* data ), return; ) +AL_FUNCTION(ALvoid, alGetDoublev, ( ALenum param, ALdouble* data ), return; ) +AL_FUNCTION(ALubyte*, alGetString, ( ALenum param ), return NULL; ) + +AL_FUNCTION(ALenum, alGetError, ( ALvoid ), return AL_INVALID_VALUE; ) +AL_FUNCTION(ALboolean, alIsExtensionPresent, ( ALubyte* fname ), return AL_FALSE; ) +AL_FUNCTION(ALvoid*, alGetProcAddress, ( ALubyte* fname ), return NULL; ) +AL_FUNCTION(ALenum, alGetEnumValue, ( ALubyte* ename ), return AL_INVALID_ENUM; ) + +AL_FUNCTION(ALvoid, alListeneri, ( ALenum param, ALint value ), return; ) +AL_FUNCTION(ALvoid, alListenerf, ( ALenum param, ALfloat value ), return; ) +AL_FUNCTION(ALvoid, alListener3f, ( ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 ), return; ) +AL_FUNCTION(ALvoid, alListenerfv, ( ALenum param, ALfloat* values ), return; ) + +AL_FUNCTION(ALvoid, alGetListeneri, ( ALenum param, ALint* value ), return; ) +AL_FUNCTION(ALvoid, alGetListenerf, ( ALenum param, ALfloat* value ), return; ) +AL_FUNCTION(ALvoid, alGetListener3f, ( ALenum param, ALfloat* v1, ALfloat* v2, ALfloat* v3 ), return; ) +AL_FUNCTION(ALvoid, alGetListenerfv, ( ALenum param, ALfloat* values ), return; ) + +AL_FUNCTION(ALvoid, alGenSources, ( ALsizei n, ALuint* sources ), return; ) +AL_FUNCTION(ALvoid, alDeleteSources, ( ALsizei n, ALuint* sources ), return; ) +AL_FUNCTION(ALboolean, alIsSource, ( ALuint id ), return AL_FALSE; ) + +AL_FUNCTION(ALvoid, alSourcei, ( ALuint source, ALenum param, ALint value ), return; ) +AL_FUNCTION(ALvoid, alSourcef, ( ALuint source, ALenum param, ALfloat value ), return; ) +AL_FUNCTION(ALvoid, alSource3f, ( ALuint source, ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 ), return; ) +AL_FUNCTION(ALvoid, alSourcefv, ( ALuint source, ALenum param, ALfloat* values ), return; ) +AL_FUNCTION(ALvoid, alGetSourcei, ( ALuint source, ALenum param, ALint* value ), return; ) +AL_FUNCTION(ALvoid, alGetSourcef, ( ALuint source, ALenum param, ALfloat* value ), return; ) +//AL_FUNCTION(ALvoid, alGetSource3f, ( ALuint source, ALenum param, ALfloat* v1, ALfloat* v2, ALfloat* v3 ), return; ) +AL_FUNCTION(ALvoid, alGetSourcefv, ( ALuint source, ALenum param, ALfloat* values ), return; ) + +AL_FUNCTION(ALvoid, alSourcePlayv, ( ALsizei n, ALuint *sources ), return; ) +AL_FUNCTION(ALvoid, alSourcePausev, ( ALsizei n, ALuint *sources ), return; ) +AL_FUNCTION(ALvoid, alSourceStopv, ( ALsizei n, ALuint *sources ), return; ) +AL_FUNCTION(ALvoid, alSourceRewindv, (ALsizei n,ALuint *sources), return; ) +AL_FUNCTION(ALvoid, alSourcePlay, ( ALuint source ), return; ) +AL_FUNCTION(ALvoid, alSourcePause, ( ALuint source ), return; ) +AL_FUNCTION(ALvoid, alSourceStop, ( ALuint source ), return; ) +AL_FUNCTION(ALvoid, alSourceRewind, ( ALuint source ), return; ) + +AL_FUNCTION(ALvoid, alGenBuffers, ( ALsizei n, ALuint* buffers ), return; ) +AL_FUNCTION(ALvoid, alDeleteBuffers, ( ALsizei n, ALuint* buffers ), return; ) +AL_FUNCTION(ALboolean, alIsBuffer, ( ALuint buffer ), return AL_FALSE; ) +AL_FUNCTION(ALvoid, alBufferData, ( ALuint buffer, ALenum format, ALvoid* data, ALsizei size, ALsizei freq ), return; ) +AL_FUNCTION(ALvoid, alGetBufferi, ( ALuint buffer, ALenum param, ALint* value ), return; ) +AL_FUNCTION(ALvoid, alGetBufferf, ( ALuint buffer, ALenum param, ALfloat* value ), return; ) + +AL_FUNCTION(ALvoid, alSourceQueueBuffers, ( ALuint source, ALsizei n, ALuint* buffers ), return; ) +AL_FUNCTION(ALvoid, alSourceUnqueueBuffers, ( ALuint source, ALsizei n, ALuint* buffers ), return; ) + +AL_FUNCTION(ALvoid, alDistanceModel, ( ALenum value ), return; ) +AL_FUNCTION(ALvoid, alDopplerFactor, ( ALfloat value ), return; ) +AL_FUNCTION(ALvoid, alDopplerVelocity, ( ALfloat value ), return; ) + + diff --git a/Engine/lib/openal/LINUX/al/alc.h b/Engine/lib/openal/LINUX/al/alc.h new file mode 100644 index 000000000..7804227d4 --- /dev/null +++ b/Engine/lib/openal/LINUX/al/alc.h @@ -0,0 +1,88 @@ +#ifndef _ALC_H_ +#define _ALC_H_ + +#include "altypes.h" +#include "alctypes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 + #ifdef _LIB + #define ALCAPI __declspec(dllexport) + #else + #define ALCAPI __declspec(dllimport) + typedef ALCvoid ALCdevice; + typedef ALCvoid ALCcontext; + #endif + #define ALCAPIENTRY __cdecl +#else + #ifdef TARGET_OS_MAC + #if TARGET_OS_MAC + #pragma export on + #endif + #endif + #define ALCAPI + #define ALCAPIENTRY __cdecl +#endif + + + +#ifndef ALC_NO_PROTOTYPES + +ALCAPI ALCubyte* ALCAPIENTRY alcGetString(ALCdevice *device,ALCenum param); +ALCAPI ALCvoid ALCAPIENTRY alcGetIntegerv(ALCdevice *device,ALCenum param,ALCsizei size,ALCint *data); + +ALCAPI ALCdevice* ALCAPIENTRY alcOpenDevice(ALCubyte *deviceName); +ALCAPI ALCvoid ALCAPIENTRY alcCloseDevice(ALCdevice *device); + +ALCAPI ALCcontext*ALCAPIENTRY alcCreateContext(ALCdevice *device,ALCint *attrList); +ALCAPI ALCboolean ALCAPIENTRY alcMakeContextCurrent(ALCcontext *context); +ALCAPI ALCvoid ALCAPIENTRY alcProcessContext(ALCcontext *context); +ALCAPI ALCcontext*ALCAPIENTRY alcGetCurrentContext(ALCvoid); +ALCAPI ALCdevice* ALCAPIENTRY alcGetContextsDevice(ALCcontext *context); +ALCAPI ALCvoid ALCAPIENTRY alcSuspendContext(ALCcontext *context); +ALCAPI ALCvoid ALCAPIENTRY alcDestroyContext(ALCcontext *context); + +ALCAPI ALCenum ALCAPIENTRY alcGetError(ALCdevice *device); + +ALCAPI ALCboolean ALCAPIENTRY alcIsExtensionPresent(ALCdevice *device,ALCubyte *extName); +ALCAPI ALCvoid * ALCAPIENTRY alcGetProcAddress(ALCdevice *device,ALCubyte *funcName); +ALCAPI ALCenum ALCAPIENTRY alcGetEnumValue(ALCdevice *device,ALCubyte *enumName); + +#else /* AL_NO_PROTOTYPES */ + +ALCAPI ALCubyte* ALCAPIENTRY (*alcGetString)(ALCdevice *device,ALCenum param); +ALCAPI ALCvoid ALCAPIENTRY (*alcGetIntegerv)(ALCdevice * device,ALCenum param,ALCsizei size,ALCint *data); + +ALCAPI ALCdevice* ALCAPIENTRY (*alcOpenDevice)(ALubyte *deviceName); +ALCAPI ALCvoid ALCAPIENTRY (*alcCloseDevice)(ALCdevice *device); + +ALCAPI ALCcontext*ALCAPIENTRY (*alcCreateContext)(ALCdevice *device,ALCint *attrList); +ALCAPI ALCboolean ALCAPIENTRY (*alcMakeContextCurrent)(ALCcontext *context); +ALCAPI ALCvoid ALCAPIENTRY (*alcProcessContext)(ALCcontext *context); +ALCAPI ALCcontext*ALCAPIENTRY (*alcGetCurrentContext)(ALCvoid); +ALCAPI ALCdevice* ALCAPIENTRY (*alcGetContextsDevice)(ALCcontext *context); +ALCAPI ALCvoid ALCAPIENTRY (*alcSuspendContext)(ALCcontext *context); +ALCAPI ALCvoid ALCAPIENTRY (*alcDestroyContext)(ALCcontext *context); + +ALCAPI ALCenum ALCAPIENTRY (*alcGetError)(ALCdevice *device); + +ALCAPI ALCboolean ALCAPIENTRY (*alcIsExtensionPresent)(ALCdevice *device,ALCubyte *extName); +ALCAPI ALCvoid * ALCAPIENTRY (*alcGetProcAddress)(ALCdevice *device,ALCubyte *funcName); +ALCAPI ALCenum ALCAPIENTRY (*alcGetEnumValue)(ALCdevice *device,ALCubyte *enumName); + +#endif /* AL_NO_PROTOTYPES */ + +#ifdef TARGET_OS_MAC + #if TARGET_OS_MAC + #pragma export off + #endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Engine/lib/openal/LINUX/al/alc_func.h b/Engine/lib/openal/LINUX/al/alc_func.h new file mode 100644 index 000000000..22da893e0 --- /dev/null +++ b/Engine/lib/openal/LINUX/al/alc_func.h @@ -0,0 +1,20 @@ + +//AL_FUNCTION(ALCubyte*, alcGetString, (ALCdevice *device,ALCenum param), return NULL; ) +//AL_FUNCTION(ALCvoid, alcGetIntegerv, (ALCdevice * device,ALCenum param,ALCsizei size,ALCint *data), return; ) + +AL_FUNCTION(ALCdevice*, alcOpenDevice, (ALubyte *deviceName), return NULL; ) +AL_FUNCTION(ALCvoid, alcCloseDevice, (ALCdevice *device), return; ) + +AL_FUNCTION(ALCcontext*, alcCreateContext, (ALCdevice *device,ALCint *attrList), return NULL; ) +AL_FUNCTION(ALCboolean, alcMakeContextCurrent, (ALCcontext *context), return AL_FALSE; ) +AL_FUNCTION(ALCvoid, alcProcessContext, (ALCcontext *context), return; ) +AL_FUNCTION(ALCcontext*, alcGetCurrentContext, (ALCvoid), return NULL; ) +AL_FUNCTION(ALCdevice*, alcGetContextsDevice, (ALCcontext *context), return NULL; ) +AL_FUNCTION(ALCvoid, alcSuspendContext, (ALCcontext *context), return; ) +AL_FUNCTION(ALCvoid, alcDestroyContext, (ALCcontext *context), return; ) + +AL_FUNCTION(ALCenum, alcGetError, (ALCdevice *device), return ALC_INVALID_DEVICE; ) + +//AL_FUNCTION(ALCboolean, alcIsExtensionPresent, (ALCdevice *device,ALCubyte *extName), return AL_FALSE; ) +//AL_FUNCTION(ALCvoid*, alcGetProcAddress, (ALCdevice *device,ALCubyte *funcName), return NULL; ) +//AL_FUNCTION(ALCenum, alcGetEnumValue, (ALCdevice *device,ALCubyte *enumName), return ALC_INVALID_ENUM; ) diff --git a/Engine/lib/openal/LINUX/al/alctypes.h b/Engine/lib/openal/LINUX/al/alctypes.h new file mode 100644 index 000000000..72ff49689 --- /dev/null +++ b/Engine/lib/openal/LINUX/al/alctypes.h @@ -0,0 +1,130 @@ +#ifndef _ALCTYPES_H_ +#define _ALCTYPES_H_ + +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2000 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + + +#ifdef __cplusplus +extern "C" { +#endif + +/** ALC boolean type. */ +typedef char ALCboolean; + +/** ALC 8bit signed byte. */ +typedef char ALCbyte; + +/** ALC 8bit unsigned byte. */ +typedef unsigned char ALCubyte; + +/** ALC 16bit signed short integer type. */ +typedef short ALCshort; + +/** ALC 16bit unsigned short integer type. */ +typedef unsigned short ALCushort; + +/** ALC 32bit unsigned integer type. */ +typedef unsigned ALCuint; + +/** ALC 32bit signed integer type. */ +typedef int ALCint; + +/** ALC 32bit floating point type. */ +typedef float ALCfloat; + +/** ALC 64bit double point type. */ +typedef double ALCdouble; + +/** ALC 32bit type. */ +typedef unsigned int ALCsizei; + +/** ALC void type */ +typedef void ALCvoid; + +/** ALC enumerations. */ +typedef int ALCenum; + + +typedef ALCvoid ALCdevice; +typedef ALCvoid ALCcontext; + + +/* Bad value. */ +#define ALC_INVALID (-1) + +/* Boolean False. */ +#define ALC_FALSE 0 + +/* Boolean True. */ +#define ALC_TRUE 1 + +/** Errors: No Error. */ +#define ALC_NO_ERROR ALC_FALSE + +#define ALC_MAJOR_VERSION 0x1000 +#define ALC_MINOR_VERSION 0x1001 +#define ALC_ATTRIBUTES_SIZE 0x1002 +#define ALC_ALL_ATTRIBUTES 0x1003 + +#define ALC_DEFAULT_DEVICE_SPECIFIER 0x1004 +#define ALC_DEVICE_SPECIFIER 0x1005 +#define ALC_EXTENSIONS 0x1006 + +#define ALC_FREQUENCY 0x1007 +#define ALC_REFRESH 0x1008 +#define ALC_SYNC 0x1009 + +/** + * The device argument does not name a valid dvice. + */ +#define ALC_INVALID_DEVICE 0xA001 + +/** + * The context argument does not name a valid context. + */ +#define ALC_INVALID_CONTEXT 0xA002 + +/** + * A function was called at inappropriate time, + * or in an inappropriate way, causing an illegal state. + * This can be an incompatible ALenum, object ID, + * and/or function. + */ +#define ALC_INVALID_ENUM 0xA003 + +/** + * Illegal value passed as an argument to an AL call. + * Applies to parameter values, but not to enumerations. + */ +#define ALC_INVALID_VALUE 0xA004 + +/** + * A function could not be completed, + * because there is not enough memory available. + */ +#define ALC_OUT_OF_MEMORY 0xA005 + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/Engine/lib/openal/LINUX/al/altypes.h b/Engine/lib/openal/LINUX/al/altypes.h new file mode 100644 index 000000000..8aa99c608 --- /dev/null +++ b/Engine/lib/openal/LINUX/al/altypes.h @@ -0,0 +1,332 @@ +#ifndef _ALTYPES_H_ +#define _ALTYPES_H_ + +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2000 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + + +#ifdef __cplusplus +extern "C" { +#endif + +/** OpenAL boolean type. */ +typedef char ALboolean; + +/** OpenAL 8bit signed byte. */ +typedef char ALbyte; + +/** OpenAL 8bit unsigned byte. */ +typedef unsigned char ALubyte; + +/** OpenAL 16bit signed short integer type. */ +typedef short ALshort; + +/** OpenAL 16bit unsigned short integer type. */ +typedef unsigned short ALushort; + +/** OpenAL 32bit unsigned integer type. */ +typedef unsigned ALuint; + +/** OpenAL 32bit signed integer type. */ +typedef int ALint; + +/** OpenAL 32bit floating point type. */ +typedef float ALfloat; + +/** OpenAL 64bit double point type. */ +typedef double ALdouble; + +/** OpenAL 32bit type. */ +typedef unsigned int ALsizei; + +/** OpenAL void type */ +typedef void ALvoid; + +/** OpenAL enumerations. */ +typedef int ALenum; + +/* Bad value. */ +#define AL_INVALID (-1) + +/* Disable value. */ +#define AL_NONE 0 + +/* Boolean False. */ +#define AL_FALSE 0 + +/* Boolean True. */ +#define AL_TRUE 1 + +/** + * Indicate the type of AL_SOURCE. + * Sources can be spatialized + */ +#define AL_SOURCE_TYPE 0x200 + +/** Indicate source has absolute coordinates. */ +#define AL_SOURCE_ABSOLUTE 0x201 + +/** Indicate Source has listener relative coordinates. */ +#define AL_SOURCE_RELATIVE 0x202 + +/** + * Directional source, inner cone angle, in degrees. + * Range: [0-360] + * Default: 360 + */ +#define AL_CONE_INNER_ANGLE 0x1001 + +/** + * Directional source, outer cone angle, in degrees. + * Range: [0-360] + * Default: 360 + */ +#define AL_CONE_OUTER_ANGLE 0x1002 + +/** + * Specify the pitch to be applied, either at source, + * or on mixer results, at listener. + * Range: [0.5-2.0] + * Default: 1.0 + */ +#define AL_PITCH 0x1003 + +/** + * Specify the current location in three dimensional space. + * OpenAL, like OpenGL, uses a right handed coordinate system, + * where in a frontal default view X (thumb) points right, + * Y points up (index finger), and Z points towards the + * viewer/camera (middle finger). + * To switch from a left handed coordinate system, flip the + * sign on the Z coordinate. + * Listener position is always in the world coordinate system. + */ +#define AL_POSITION 0x1004 + +/** Specify the current direction as forward vector. */ +#define AL_DIRECTION 0x1005 + +/** Specify the current velocity in three dimensional space. */ +#define AL_VELOCITY 0x1006 + +/** + * Indicate whether source has to loop infinite. + * Type: ALboolean + * Range: [AL_TRUE, AL_FALSE] + * Default: AL_FALSE + */ +#define AL_LOOPING 0x1007 + +/** + * Indicate the buffer to provide sound samples. + * Type: ALuint. + * Range: any valid Buffer id. + */ +#define AL_BUFFER 0x1009 + +/** + * Indicate the gain (volume amplification) applied. + * Type: ALfloat. + * Range: ]0.0- ] + * A value of 1.0 means un-attenuated/unchanged. + * Each division by 2 equals an attenuation of -6dB. + * Each multiplicaton with 2 equals an amplification of +6dB. + * A value of 0.0 is meaningless with respect to a logarithmic + * scale; it is interpreted as zero volume - the channel + * is effectively disabled. + */ +#define AL_GAIN 0x100A + +/** + * Indicate minimum source attenuation. + * Type: ALfloat + * Range: [0.0 - 1.0] + */ +#define AL_MIN_GAIN 0x100D + +/** + * Indicate maximum source attenuation. + * Type: ALfloat + * Range: [0.0 - 1.0] + */ +#define AL_MAX_GAIN 0x100E + +/** + * Specify the current orientation. + * Type: ALfv6 (at/up) + * Range: N/A + */ +#define AL_ORIENTATION 0x100F + +/* byte offset into source (in canon format). -1 if source + * is not playing. Don't set this, get this. + * + * Type: ALfloat + * Range: [0.0 - ] + * Default: 1.0 + */ +#define AL_REFERENCE_DISTANCE 0x1020 + + /** + * Indicate the rolloff factor for the source. + * Type: ALfloat + * Range: [0.0 - ] + * Default: 1.0 + */ +#define AL_ROLLOFF_FACTOR 0x1021 + +/** + * Indicate the gain (volume amplification) applied. + * Type: ALfloat. + * Range: ]0.0- ] + * A value of 1.0 means un-attenuated/unchanged. + * Each division by 2 equals an attenuation of -6dB. + * Each multiplicaton with 2 equals an amplification of +6dB. + * A value of 0.0 is meaningless with respect to a logarithmic + * scale; it is interpreted as zero volume - the channel + * is effectively disabled. + */ +#define AL_CONE_OUTER_GAIN 0x1022 + +/** + * Specify the maximum distance. + * Type: ALfloat + * Range: [0.0 - ] + */ +#define AL_MAX_DISTANCE 0x1023 + +/** + * Specify the channel mask. (Creative) + * Type: ALuint + * Range: [0 - 255] + */ +#define AL_CHANNEL_MASK 0x3000 + +/** + * Source state information + */ +#define AL_SOURCE_STATE 0x1010 +#define AL_INITIAL 0x1011 +#define AL_PLAYING 0x1012 +#define AL_PAUSED 0x1013 +#define AL_STOPPED 0x1014 + +/** + * Buffer Queue params + */ +#define AL_BUFFERS_QUEUED 0x1015 +#define AL_BUFFERS_PROCESSED 0x1016 + +/** Sound buffers: format specifier. */ +#define AL_FORMAT_MONO8 0x1100 +#define AL_FORMAT_MONO16 0x1101 +#define AL_FORMAT_STEREO8 0x1102 +#define AL_FORMAT_STEREO16 0x1103 + +/** + * Sound buffers: frequency, in units of Hertz [Hz]. + * This is the number of samples per second. Half of the + * sample frequency marks the maximum significant + * frequency component. + */ +#define AL_FREQUENCY 0x2001 +#define AL_BITS 0x2002 +#define AL_CHANNELS 0x2003 +#define AL_SIZE 0x2004 +#define AL_DATA 0x2005 + +/** + * Buffer state. + * + * Not supported for public use (yet). + */ +#define AL_UNUSED 0x2010 +#define AL_PENDING 0x2011 +#define AL_PROCESSED 0x2012 + +/** Errors: No Error. */ +#define AL_NO_ERROR AL_FALSE + +/** + * Illegal name passed as an argument to an AL call. + */ +#define AL_INVALID_NAME 0xA001 + +/** + * Illegal enum passed as an argument to an AL call. + */ +#define AL_INVALID_ENUM 0xA002 +/** + * Illegal value passed as an argument to an AL call. + * Applies to parameter values, but not to enumerations. + */ +#define AL_INVALID_VALUE 0xA003 + +/** + * A function was called at inappropriate time, + * or in an inappropriate way, causing an illegal state. + * This can be an incompatible ALenum, object ID, + * and/or function. + */ +#define AL_INVALID_OPERATION 0xA004 + +/** + * A function could not be completed, + * because there is not enough memory available. + */ +#define AL_OUT_OF_MEMORY 0xA005 + +/** Context strings: Vendor Name. */ +#define AL_VENDOR 0xB001 +#define AL_VERSION 0xB002 +#define AL_RENDERER 0xB003 +#define AL_EXTENSIONS 0xB004 + +/** Global tweakage. */ + +/** + * Doppler scale. Default 1.0 + */ +#define AL_DOPPLER_FACTOR 0xC000 + +/** + * Doppler velocity. Default 1.0 + */ +#define AL_DOPPLER_VELOCITY 0xC001 + +/** + * Distance model. Default AL_INVERSE_DISTANCE_CLAMPED + */ +#define AL_DISTANCE_MODEL 0xD000 + +/** Distance models. */ + +#define AL_INVERSE_DISTANCE 0xD001 +#define AL_INVERSE_DISTANCE_CLAMPED 0xD002 + + /** + * enables + */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Engine/lib/openal/LINUX/al/alu.h b/Engine/lib/openal/LINUX/al/alu.h new file mode 100644 index 000000000..c6adff62c --- /dev/null +++ b/Engine/lib/openal/LINUX/al/alu.h @@ -0,0 +1,34 @@ +#ifndef _ALU_H_ +#define _ALU_H_ + +#define ALUAPI +#define ALUAPIENTRY __cdecl + +#define BUFFERSIZE 48000 +#define FRACTIONBITS 14 +#define FRACTIONMASK ((1L< + + /* + * EAX Wrapper Interface (using Direct X 7) {4FF53B81-1CE0-11d3-AAB8-00A0C95949D5} + */ + DEFINE_GUID(CLSID_EAXDirectSound, + 0x4ff53b81, + 0x1ce0, + 0x11d3, + 0xaa, 0xb8, 0x0, 0xa0, 0xc9, 0x59, 0x49, 0xd5); + + /* + * EAX Wrapper Interface (using Direct X 8) {CA503B60-B176-11d4-A094-D0C0BF3A560C} + */ + DEFINE_GUID(CLSID_EAXDirectSound8, + 0xca503b60, + 0xb176, + 0x11d4, + 0xa0, 0x94, 0xd0, 0xc0, 0xbf, 0x3a, 0x56, 0xc); + + + +#ifdef DIRECTSOUND_VERSION +#if DIRECTSOUND_VERSION == 0x0800 + __declspec(dllimport) HRESULT WINAPI EAXDirectSoundCreate8(GUID*, LPDIRECTSOUND8*, IUnknown FAR *); + typedef HRESULT (FAR PASCAL *LPEAXDIRECTSOUNDCREATE8)(GUID*, LPDIRECTSOUND8*, IUnknown FAR*); +#endif +#endif + + __declspec(dllimport) HRESULT WINAPI EAXDirectSoundCreate(GUID*, LPDIRECTSOUND*, IUnknown FAR *); + typedef HRESULT (FAR PASCAL *LPEAXDIRECTSOUNDCREATE)(GUID*, LPDIRECTSOUND*, IUnknown FAR*); + +#else // OPENAL + #include + + #ifndef GUID_DEFINED + #define GUID_DEFINED + typedef struct _GUID + { + unsigned long Data1; + unsigned short Data2; + unsigned short Data3; + unsigned char Data4[8]; + } GUID; + #endif // !GUID_DEFINED + + #ifndef DEFINE_GUID + #ifndef INITGUID + #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const GUID FAR name + #else + #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } + #endif // INITGUID + #endif // DEFINE_GUID + + + /* + * EAX OpenAL Extension + */ + typedef ALenum (*EAXSet)(const GUID*, ALuint, ALuint, ALvoid*, ALuint); + typedef ALenum (*EAXGet)(const GUID*, ALuint, ALuint, ALvoid*, ALuint); +#endif + +#pragma pack(push, 4) + +/* + * EAX 3.0 listener property set {A8FA6880-B476-11d3-BDB9-00C0F02DDF87} + */ +DEFINE_GUID(DSPROPSETID_EAX30_ListenerProperties, + 0xa8fa6882, + 0xb476, + 0x11d3, + 0xbd, 0xb9, 0x00, 0xc0, 0xf0, 0x2d, 0xdf, 0x87); + +// For compatibility with future EAX versions: +#define DSPROPSETID_EAX_ListenerProperties DSPROPSETID_EAX30_ListenerProperties + +typedef enum +{ + DSPROPERTY_EAXLISTENER_NONE, + DSPROPERTY_EAXLISTENER_ALLPARAMETERS, + DSPROPERTY_EAXLISTENER_ENVIRONMENT, + DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE, + DSPROPERTY_EAXLISTENER_ENVIRONMENTDIFFUSION, + DSPROPERTY_EAXLISTENER_ROOM, + DSPROPERTY_EAXLISTENER_ROOMHF, + DSPROPERTY_EAXLISTENER_ROOMLF, + DSPROPERTY_EAXLISTENER_DECAYTIME, + DSPROPERTY_EAXLISTENER_DECAYHFRATIO, + DSPROPERTY_EAXLISTENER_DECAYLFRATIO, + DSPROPERTY_EAXLISTENER_REFLECTIONS, + DSPROPERTY_EAXLISTENER_REFLECTIONSDELAY, + DSPROPERTY_EAXLISTENER_REFLECTIONSPAN, + DSPROPERTY_EAXLISTENER_REVERB, + DSPROPERTY_EAXLISTENER_REVERBDELAY, + DSPROPERTY_EAXLISTENER_REVERBPAN, + DSPROPERTY_EAXLISTENER_ECHOTIME, + DSPROPERTY_EAXLISTENER_ECHODEPTH, + DSPROPERTY_EAXLISTENER_MODULATIONTIME, + DSPROPERTY_EAXLISTENER_MODULATIONDEPTH, + DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF, + DSPROPERTY_EAXLISTENER_HFREFERENCE, + DSPROPERTY_EAXLISTENER_LFREFERENCE, + DSPROPERTY_EAXLISTENER_ROOMROLLOFFFACTOR, + DSPROPERTY_EAXLISTENER_FLAGS +} DSPROPERTY_EAX_LISTENERPROPERTY; + +// OR these flags with property id +#define DSPROPERTY_EAXLISTENER_IMMEDIATE 0x00000000 // changes take effect immediately +#define DSPROPERTY_EAXLISTENER_DEFERRED 0x80000000 // changes take effect later +#define DSPROPERTY_EAXLISTENER_COMMITDEFERREDSETTINGS (DSPROPERTY_EAXLISTENER_NONE | \ + DSPROPERTY_EAXLISTENER_IMMEDIATE) + +typedef struct _EAXVECTOR { + float x; + float y; + float z; +} EAXVECTOR; + +// Use this structure for DSPROPERTY_EAXLISTENER_ALLPARAMETERS +// - all levels are hundredths of decibels +// - all times and delays are in seconds +// +// NOTE: This structure may change in future EAX versions. +// It is recommended to initialize fields by name: +// myListener.lRoom = -1000; +// myListener.lRoomHF = -100; +// ... +// myListener.dwFlags = myFlags /* see EAXLISTENERFLAGS below */ ; +// instead of: +// myListener = { -1000, -100, ... , 0x00000009 }; +// If you want to save and load presets in binary form, you +// should define your own structure to insure future compatibility. +// +typedef struct _EAXLISTENERPROPERTIES +{ + unsigned long ulEnvironment; // sets all listener properties + float flEnvironmentSize; // environment size in meters + float flEnvironmentDiffusion; // environment diffusion + long lRoom; // room effect level (at mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lRoomLF; // relative room effect level at low frequencies + float flDecayTime; // reverberation decay time at mid frequencies + float flDecayHFRatio; // high-frequency to mid-frequency decay time ratio + float flDecayLFRatio; // low-frequency to mid-frequency decay time ratio + long lReflections; // early reflections level relative to room effect + float flReflectionsDelay; // initial reflection delay time + EAXVECTOR vReflectionsPan; // early reflections panning vector + long lReverb; // late reverberation level relative to room effect + float flReverbDelay; // late reverberation delay time relative to initial reflection + EAXVECTOR vReverbPan; // late reverberation panning vector + float flEchoTime; // echo time + float flEchoDepth; // echo depth + float flModulationTime; // modulation time + float flModulationDepth; // modulation depth + float flAirAbsorptionHF; // change in level per meter at high frequencies + float flHFReference; // reference high frequency + float flLFReference; // reference low frequency + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + unsigned long ulFlags; // modifies the behavior of properties +} EAXLISTENERPROPERTIES, *LPEAXLISTENERPROPERTIES; + +// used by DSPROPERTY_EAXLISTENER_ENVIRONMENT +enum +{ + EAX_ENVIRONMENT_GENERIC, + EAX_ENVIRONMENT_PADDEDCELL, + EAX_ENVIRONMENT_ROOM, + EAX_ENVIRONMENT_BATHROOM, + EAX_ENVIRONMENT_LIVINGROOM, + EAX_ENVIRONMENT_STONEROOM, + EAX_ENVIRONMENT_AUDITORIUM, + EAX_ENVIRONMENT_CONCERTHALL, + EAX_ENVIRONMENT_CAVE, + EAX_ENVIRONMENT_ARENA, + EAX_ENVIRONMENT_HANGAR, + EAX_ENVIRONMENT_CARPETEDHALLWAY, + EAX_ENVIRONMENT_HALLWAY, + EAX_ENVIRONMENT_STONECORRIDOR, + EAX_ENVIRONMENT_ALLEY, + EAX_ENVIRONMENT_FOREST, + EAX_ENVIRONMENT_CITY, + EAX_ENVIRONMENT_MOUNTAINS, + EAX_ENVIRONMENT_QUARRY, + EAX_ENVIRONMENT_PLAIN, + EAX_ENVIRONMENT_PARKINGLOT, + EAX_ENVIRONMENT_SEWERPIPE, + EAX_ENVIRONMENT_UNDERWATER, + EAX_ENVIRONMENT_DRUGGED, + EAX_ENVIRONMENT_DIZZY, + EAX_ENVIRONMENT_PSYCHOTIC, + + EAX_ENVIRONMENT_UNDEFINED, + + EAX_ENVIRONMENT_COUNT +}; + +// Used by DSPROPERTY_EAXLISTENER_FLAGS +// +// Note: The number and order of flags may change in future EAX versions. +// It is recommended to use the flag defines as follows: +// myFlags = EAXLISTENERFLAGS_DECAYTIMESCALE | EAXLISTENERFLAGS_REVERBSCALE; +// instead of: +// myFlags = 0x00000009; +// +// These flags determine what properties are affected by environment size. +#define EAXLISTENERFLAGS_DECAYTIMESCALE 0x00000001 // reverberation decay time +#define EAXLISTENERFLAGS_REFLECTIONSSCALE 0x00000002 // reflection level +#define EAXLISTENERFLAGS_REFLECTIONSDELAYSCALE 0x00000004 // initial reflection delay time +#define EAXLISTENERFLAGS_REVERBSCALE 0x00000008 // reflections level +#define EAXLISTENERFLAGS_REVERBDELAYSCALE 0x00000010 // late reverberation delay time +#define EAXLISTENERFLAGS_ECHOTIMESCALE 0x00000040 // echo time +#define EAXLISTENERFLAGS_MODULATIONTIMESCALE 0x00000080 // modulation time + +// This flag limits high-frequency decay time according to air absorption. +#define EAXLISTENERFLAGS_DECAYHFLIMIT 0x00000020 + +#define EAXLISTENERFLAGS_RESERVED 0xFFFFFF00 // reserved future use + +// Property ranges and defaults: + +#define EAXLISTENER_MINENVIRONMENT 0 +#define EAXLISTENER_MAXENVIRONMENT (EAX_ENVIRONMENT_COUNT-1) +#define EAXLISTENER_DEFAULTENVIRONMENT EAX_ENVIRONMENT_GENERIC + +#define EAXLISTENER_MINENVIRONMENTSIZE 1.0f +#define EAXLISTENER_MAXENVIRONMENTSIZE 100.0f +#define EAXLISTENER_DEFAULTENVIRONMENTSIZE 7.5f + +#define EAXLISTENER_MINENVIRONMENTDIFFUSION 0.0f +#define EAXLISTENER_MAXENVIRONMENTDIFFUSION 1.0f +#define EAXLISTENER_DEFAULTENVIRONMENTDIFFUSION 1.0f + +#define EAXLISTENER_MINROOM (-10000) +#define EAXLISTENER_MAXROOM 0 +#define EAXLISTENER_DEFAULTROOM (-1000) + +#define EAXLISTENER_MINROOMHF (-10000) +#define EAXLISTENER_MAXROOMHF 0 +#define EAXLISTENER_DEFAULTROOMHF (-100) + +#define EAXLISTENER_MINROOMLF (-10000) +#define EAXLISTENER_MAXROOMLF 0 +#define EAXLISTENER_DEFAULTROOMLF 0 + +#define EAXLISTENER_MINDECAYTIME 0.1f +#define EAXLISTENER_MAXDECAYTIME 20.0f +#define EAXLISTENER_DEFAULTDECAYTIME 1.49f + +#define EAXLISTENER_MINDECAYHFRATIO 0.1f +#define EAXLISTENER_MAXDECAYHFRATIO 2.0f +#define EAXLISTENER_DEFAULTDECAYHFRATIO 0.83f + +#define EAXLISTENER_MINDECAYLFRATIO 0.1f +#define EAXLISTENER_MAXDECAYLFRATIO 2.0f +#define EAXLISTENER_DEFAULTDECAYLFRATIO 1.00f + +#define EAXLISTENER_MINREFLECTIONS (-10000) +#define EAXLISTENER_MAXREFLECTIONS 1000 +#define EAXLISTENER_DEFAULTREFLECTIONS (-2602) + +#define EAXLISTENER_MINREFLECTIONSDELAY 0.0f +#define EAXLISTENER_MAXREFLECTIONSDELAY 0.3f +#define EAXLISTENER_DEFAULTREFLECTIONSDELAY 0.007f + +#define EAXLISTENER_MINREVERB (-10000) +#define EAXLISTENER_MAXREVERB 2000 +#define EAXLISTENER_DEFAULTREVERB 200 + +#define EAXLISTENER_MINREVERBDELAY 0.0f +#define EAXLISTENER_MAXREVERBDELAY 0.1f +#define EAXLISTENER_DEFAULTREVERBDELAY 0.011f + +#define EAXLISTENER_MINECHOTIME 0.075f +#define EAXLISTENER_MAXECHOTIME 0.25f +#define EAXLISTENER_DEFAULTECHOTIME 0.25f + +#define EAXLISTENER_MINECHODEPTH 0.0f +#define EAXLISTENER_MAXECHODEPTH 1.0f +#define EAXLISTENER_DEFAULTECHODEPTH 0.0f + +#define EAXLISTENER_MINMODULATIONTIME 0.04f +#define EAXLISTENER_MAXMODULATIONTIME 4.0f +#define EAXLISTENER_DEFAULTMODULATIONTIME 0.25f + +#define EAXLISTENER_MINMODULATIONDEPTH 0.0f +#define EAXLISTENER_MAXMODULATIONDEPTH 1.0f +#define EAXLISTENER_DEFAULTMODULATIONDEPTH 0.0f + +#define EAXLISTENER_MINAIRABSORPTIONHF (-100.0f) +#define EAXLISTENER_MAXAIRABSORPTIONHF 0.0f +#define EAXLISTENER_DEFAULTAIRABSORPTIONHF (-5.0f) + +#define EAXLISTENER_MINHFREFERENCE 1000.0f +#define EAXLISTENER_MAXHFREFERENCE 20000.0f +#define EAXLISTENER_DEFAULTHFREFERENCE 5000.0f + +#define EAXLISTENER_MINLFREFERENCE 20.0f +#define EAXLISTENER_MAXLFREFERENCE 1000.0f +#define EAXLISTENER_DEFAULTLFREFERENCE 250.0f + +#define EAXLISTENER_MINROOMROLLOFFFACTOR 0.0f +#define EAXLISTENER_MAXROOMROLLOFFFACTOR 10.0f +#define EAXLISTENER_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXLISTENER_DEFAULTFLAGS (EAXLISTENERFLAGS_DECAYTIMESCALE | \ + EAXLISTENERFLAGS_REFLECTIONSSCALE | \ + EAXLISTENERFLAGS_REFLECTIONSDELAYSCALE | \ + EAXLISTENERFLAGS_REVERBSCALE | \ + EAXLISTENERFLAGS_REVERBDELAYSCALE | \ + EAXLISTENERFLAGS_DECAYHFLIMIT) + + + +/* +* EAX 3.0 buffer property set {A8FA6881-B476-11d3-BDB9-00C0F02DDF87} +*/ +DEFINE_GUID(DSPROPSETID_EAX30_BufferProperties, + 0xa8fa6881, + 0xb476, + 0x11d3, + 0xbd, 0xb9, 0x0, 0xc0, 0xf0, 0x2d, 0xdf, 0x87); + +// For compatibility with future EAX versions: +#define DSPROPSETID_EAX_BufferProperties DSPROPSETID_EAX30_BufferProperties +#define DSPROPSETID_EAX_SourceProperties DSPROPSETID_EAX30_BufferProperties + +typedef enum +{ + DSPROPERTY_EAXBUFFER_NONE, + DSPROPERTY_EAXBUFFER_ALLPARAMETERS, + DSPROPERTY_EAXBUFFER_OBSTRUCTIONPARAMETERS, + DSPROPERTY_EAXBUFFER_OCCLUSIONPARAMETERS, + DSPROPERTY_EAXBUFFER_EXCLUSIONPARAMETERS, + DSPROPERTY_EAXBUFFER_DIRECT, + DSPROPERTY_EAXBUFFER_DIRECTHF, + DSPROPERTY_EAXBUFFER_ROOM, + DSPROPERTY_EAXBUFFER_ROOMHF, + DSPROPERTY_EAXBUFFER_OBSTRUCTION, + DSPROPERTY_EAXBUFFER_OBSTRUCTIONLFRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSION, + DSPROPERTY_EAXBUFFER_OCCLUSIONLFRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSIONROOMRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSIONDIRECTRATIO, + DSPROPERTY_EAXBUFFER_EXCLUSION, + DSPROPERTY_EAXBUFFER_EXCLUSIONLFRATIO, + DSPROPERTY_EAXBUFFER_OUTSIDEVOLUMEHF, + DSPROPERTY_EAXBUFFER_DOPPLERFACTOR, + DSPROPERTY_EAXBUFFER_ROLLOFFFACTOR, + DSPROPERTY_EAXBUFFER_ROOMROLLOFFFACTOR, + DSPROPERTY_EAXBUFFER_AIRABSORPTIONFACTOR, + DSPROPERTY_EAXBUFFER_FLAGS +} DSPROPERTY_EAX_BUFFERPROPERTY; + +// OR these flags with property id +#define DSPROPERTY_EAXBUFFER_IMMEDIATE 0x00000000 // changes take effect immediately +#define DSPROPERTY_EAXBUFFER_DEFERRED 0x80000000 // changes take effect later +#define DSPROPERTY_EAXBUFFER_COMMITDEFERREDSETTINGS (DSPROPERTY_EAXBUFFER_NONE | \ + DSPROPERTY_EAXBUFFER_IMMEDIATE) + +// Use this structure for DSPROPERTY_EAXBUFFER_ALLPARAMETERS +// - all levels are hundredths of decibels +// - all delays are in seconds +// +// NOTE: This structure may change in future EAX versions. +// It is recommended to initialize fields by name: +// myBuffer.lDirect = 0; +// myBuffer.lDirectHF = -200; +// ... +// myBuffer.dwFlags = myFlags /* see EAXBUFFERFLAGS below */ ; +// instead of: +// myBuffer = { 0, -200, ... , 0x00000003 }; +// +typedef struct _EAXBUFFERPROPERTIES +{ + long lDirect; // direct path level (at low and mid frequencies) + long lDirectHF; // relative direct path level at high frequencies + long lRoom; // room effect level (at low and mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lObstruction; // main obstruction control (attenuation at high frequencies) + float flObstructionLFRatio; // obstruction low-frequency level re. main control + long lOcclusion; // main occlusion control (attenuation at high frequencies) + float flOcclusionLFRatio; // occlusion low-frequency level re. main control + float flOcclusionRoomRatio; // relative occlusion control for room effect + float flOcclusionDirectRatio; // relative occlusion control for direct path + long lExclusion; // main exlusion control (attenuation at high frequencies) + float flExclusionLFRatio; // exclusion low-frequency level re. main control + long lOutsideVolumeHF; // outside sound cone level at high frequencies + float flDopplerFactor; // like DS3D flDopplerFactor but per source + float flRolloffFactor; // like DS3D flRolloffFactor but per source + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + float flAirAbsorptionFactor; // multiplies DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF + unsigned long ulFlags; // modifies the behavior of properties +} EAXBUFFERPROPERTIES, *LPEAXBUFFERPROPERTIES; + +// Use this structure for DSPROPERTY_EAXBUFFER_OBSTRUCTION, +typedef struct _EAXOBSTRUCTIONPROPERTIES +{ + long lObstruction; + float flObstructionLFRatio; +} EAXOBSTRUCTIONPROPERTIES, *LPEAXOBSTRUCTIONPROPERTIES; + +// Use this structure for DSPROPERTY_EAXBUFFER_OCCLUSION +typedef struct _EAXOCCLUSIONPROPERTIES +{ + long lOcclusion; + float flOcclusionLFRatio; + float flOcclusionRoomRatio; + float flOcclusionDirectRatio; +} EAXOCCLUSIONPROPERTIES, *LPEAXOCCLUSIONPROPERTIES; + +// Use this structure for DSPROPERTY_EAXBUFFER_EXCLUSION +typedef struct _EAXEXCLUSIONPROPERTIES +{ + long lExclusion; + float flExclusionLFRatio; +} EAXEXCLUSIONPROPERTIES, *LPEAXEXCLUSIONPROPERTIES; + +// Used by DSPROPERTY_EAXBUFFER_FLAGS +// TRUE: value is computed automatically - property is an offset +// FALSE: value is used directly +// +// Note: The number and order of flags may change in future EAX versions. +// To insure future compatibility, use flag defines as follows: +// myFlags = EAXBUFFERFLAGS_DIRECTHFAUTO | EAXBUFFERFLAGS_ROOMAUTO; +// instead of: +// myFlags = 0x00000003; +// +#define EAXBUFFERFLAGS_DIRECTHFAUTO 0x00000001 // affects DSPROPERTY_EAXBUFFER_DIRECTHF +#define EAXBUFFERFLAGS_ROOMAUTO 0x00000002 // affects DSPROPERTY_EAXBUFFER_ROOM +#define EAXBUFFERFLAGS_ROOMHFAUTO 0x00000004 // affects DSPROPERTY_EAXBUFFER_ROOMHF + +#define EAXBUFFERFLAGS_RESERVED 0xFFFFFFF8 // reserved future use + +// Property ranges and defaults: + +#define EAXBUFFER_MINDIRECT (-10000) +#define EAXBUFFER_MAXDIRECT 1000 +#define EAXBUFFER_DEFAULTDIRECT 0 + +#define EAXBUFFER_MINDIRECTHF (-10000) +#define EAXBUFFER_MAXDIRECTHF 0 +#define EAXBUFFER_DEFAULTDIRECTHF 0 + +#define EAXBUFFER_MINROOM (-10000) +#define EAXBUFFER_MAXROOM 1000 +#define EAXBUFFER_DEFAULTROOM 0 + +#define EAXBUFFER_MINROOMHF (-10000) +#define EAXBUFFER_MAXROOMHF 0 +#define EAXBUFFER_DEFAULTROOMHF 0 + +#define EAXBUFFER_MINOBSTRUCTION (-10000) +#define EAXBUFFER_MAXOBSTRUCTION 0 +#define EAXBUFFER_DEFAULTOBSTRUCTION 0 + +#define EAXBUFFER_MINOBSTRUCTIONLFRATIO 0.0f +#define EAXBUFFER_MAXOBSTRUCTIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTOBSTRUCTIONLFRATIO 0.0f + +#define EAXBUFFER_MINOCCLUSION (-10000) +#define EAXBUFFER_MAXOCCLUSION 0 +#define EAXBUFFER_DEFAULTOCCLUSION 0 + +#define EAXBUFFER_MINOCCLUSIONLFRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTOCCLUSIONLFRATIO 0.25f + +#define EAXBUFFER_MINOCCLUSIONROOMRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONROOMRATIO 10.0f +#define EAXBUFFER_DEFAULTOCCLUSIONROOMRATIO 1.5f + +#define EAXBUFFER_MINOCCLUSIONDIRECTRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONDIRECTRATIO 10.0f +#define EAXBUFFER_DEFAULTOCCLUSIONDIRECTRATIO 1.0f + +#define EAXBUFFER_MINEXCLUSION (-10000) +#define EAXBUFFER_MAXEXCLUSION 0 +#define EAXBUFFER_DEFAULTEXCLUSION 0 + +#define EAXBUFFER_MINEXCLUSIONLFRATIO 0.0f +#define EAXBUFFER_MAXEXCLUSIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTEXCLUSIONLFRATIO 1.0f + +#define EAXBUFFER_MINOUTSIDEVOLUMEHF (-10000) +#define EAXBUFFER_MAXOUTSIDEVOLUMEHF 0 +#define EAXBUFFER_DEFAULTOUTSIDEVOLUMEHF 0 + +#define EAXBUFFER_MINDOPPLERFACTOR 0.0f +#define EAXBUFFER_MAXDOPPLERFACTOR 10.f +#define EAXBUFFER_DEFAULTDOPPLERFACTOR 0.0f + +#define EAXBUFFER_MINROLLOFFFACTOR 0.0f +#define EAXBUFFER_MAXROLLOFFFACTOR 10.f +#define EAXBUFFER_DEFAULTROLLOFFFACTOR 0.0f + +#define EAXBUFFER_MINROOMROLLOFFFACTOR 0.0f +#define EAXBUFFER_MAXROOMROLLOFFFACTOR 10.f +#define EAXBUFFER_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXBUFFER_MINAIRABSORPTIONFACTOR 0.0f +#define EAXBUFFER_MAXAIRABSORPTIONFACTOR 10.0f +#define EAXBUFFER_DEFAULTAIRABSORPTIONFACTOR 1.0f + +#define EAXBUFFER_DEFAULTFLAGS (EAXBUFFERFLAGS_DIRECTHFAUTO | \ + EAXBUFFERFLAGS_ROOMAUTO | \ + EAXBUFFERFLAGS_ROOMHFAUTO ) + +#pragma pack(pop) + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif diff --git a/Engine/lib/openal/LINUX/al/eax_func.h b/Engine/lib/openal/LINUX/al/eax_func.h new file mode 100644 index 000000000..8c36c7471 --- /dev/null +++ b/Engine/lib/openal/LINUX/al/eax_func.h @@ -0,0 +1,3 @@ + +AL_FUNCTION(ALenum, EAXSet, (const ALGUID*, ALuint, ALuint, ALvoid*, ALuint), return 0; ) +AL_FUNCTION(ALenum, EAXGet, (const ALGUID*, ALuint, ALuint, ALvoid*, ALuint), return 0; ) diff --git a/Engine/lib/openal/LINUX/al/eaxtypes.h b/Engine/lib/openal/LINUX/al/eaxtypes.h new file mode 100644 index 000000000..15ba59afc --- /dev/null +++ b/Engine/lib/openal/LINUX/al/eaxtypes.h @@ -0,0 +1,462 @@ + + +typedef struct _ALGUID +{ + unsigned long Data1; + unsigned short Data2; + unsigned short Data3; + unsigned char Data4[8]; +}ALGUID; + +#ifndef INITGUID + #define DEFINE_ALGUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const ALGUID name +#else + #define DEFINE_ALGUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const ALGUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } +#endif // INITGUID + + + +/* + * EAX 3.0 listener property set {A8FA6880-B476-11d3-BDB9-00C0F02DDF87} + */ +DEFINE_ALGUID(DSPROPSETID_EAX30_ListenerProperties, + 0xa8fa6882, + 0xb476, + 0x11d3, + 0xbd, 0xb9, 0x00, 0xc0, 0xf0, 0x2d, 0xdf, 0x87); + +// For compatibility with future EAX versions: +#define DSPROPSETID_EAX_ListenerProperties DSPROPSETID_EAX30_ListenerProperties + +typedef enum +{ + DSPROPERTY_EAXLISTENER_NONE, + DSPROPERTY_EAXLISTENER_ALLPARAMETERS, + DSPROPERTY_EAXLISTENER_ENVIRONMENT, + DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE, + DSPROPERTY_EAXLISTENER_ENVIRONMENTDIFFUSION, + DSPROPERTY_EAXLISTENER_ROOM, + DSPROPERTY_EAXLISTENER_ROOMHF, + DSPROPERTY_EAXLISTENER_ROOMLF, + DSPROPERTY_EAXLISTENER_DECAYTIME, + DSPROPERTY_EAXLISTENER_DECAYHFRATIO, + DSPROPERTY_EAXLISTENER_DECAYLFRATIO, + DSPROPERTY_EAXLISTENER_REFLECTIONS, + DSPROPERTY_EAXLISTENER_REFLECTIONSDELAY, + DSPROPERTY_EAXLISTENER_REFLECTIONSPAN, + DSPROPERTY_EAXLISTENER_REVERB, + DSPROPERTY_EAXLISTENER_REVERBDELAY, + DSPROPERTY_EAXLISTENER_REVERBPAN, + DSPROPERTY_EAXLISTENER_ECHOTIME, + DSPROPERTY_EAXLISTENER_ECHODEPTH, + DSPROPERTY_EAXLISTENER_MODULATIONTIME, + DSPROPERTY_EAXLISTENER_MODULATIONDEPTH, + DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF, + DSPROPERTY_EAXLISTENER_HFREFERENCE, + DSPROPERTY_EAXLISTENER_LFREFERENCE, + DSPROPERTY_EAXLISTENER_ROOMROLLOFFFACTOR, + DSPROPERTY_EAXLISTENER_FLAGS +} DSPROPERTY_EAX_LISTENERPROPERTY; + +// OR these flags with property id +#define DSPROPERTY_EAXLISTENER_IMMEDIATE 0x00000000 // changes take effect immediately +#define DSPROPERTY_EAXLISTENER_DEFERRED 0x80000000 // changes take effect later +#define DSPROPERTY_EAXLISTENER_COMMITDEFERREDSETTINGS (DSPROPERTY_EAXLISTENER_NONE | \ + DSPROPERTY_EAXLISTENER_IMMEDIATE) + +typedef struct _EAXVECTOR { + float x; + float y; + float z; +} EAXVECTOR; + +// Use this structure for DSPROPERTY_EAXLISTENER_ALLPARAMETERS +// - all levels are hundredths of decibels +// - all times and delays are in seconds +// +// NOTE: This structure may change in future EAX versions. +// It is recommended to initialize fields by name: +// myListener.lRoom = -1000; +// myListener.lRoomHF = -100; +// ... +// myListener.dwFlags = myFlags /* see EAXLISTENERFLAGS below */ ; +// instead of: +// myListener = { -1000, -100, ... , 0x00000009 }; +// If you want to save and load presets in binary form, you +// should define your own structure to insure future compatibility. +// +typedef struct _EAXLISTENERPROPERTIES +{ + unsigned long ulEnvironment; // sets all listener properties + float flEnvironmentSize; // environment size in meters + float flEnvironmentDiffusion; // environment diffusion + long lRoom; // room effect level (at mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lRoomLF; // relative room effect level at low frequencies + float flDecayTime; // reverberation decay time at mid frequencies + float flDecayHFRatio; // high-frequency to mid-frequency decay time ratio + float flDecayLFRatio; // low-frequency to mid-frequency decay time ratio + long lReflections; // early reflections level relative to room effect + float flReflectionsDelay; // initial reflection delay time + EAXVECTOR vReflectionsPan; // early reflections panning vector + long lReverb; // late reverberation level relative to room effect + float flReverbDelay; // late reverberation delay time relative to initial reflection + EAXVECTOR vReverbPan; // late reverberation panning vector + float flEchoTime; // echo time + float flEchoDepth; // echo depth + float flModulationTime; // modulation time + float flModulationDepth; // modulation depth + float flAirAbsorptionHF; // change in level per meter at high frequencies + float flHFReference; // reference high frequency + float flLFReference; // reference low frequency + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + unsigned long ulFlags; // modifies the behavior of properties +} EAXLISTENERPROPERTIES, *LPEAXLISTENERPROPERTIES; + +// used by DSPROPERTY_EAXLISTENER_ENVIRONMENT +enum +{ + EAX_ENVIRONMENT_GENERIC, + EAX_ENVIRONMENT_PADDEDCELL, + EAX_ENVIRONMENT_ROOM, + EAX_ENVIRONMENT_BATHROOM, + EAX_ENVIRONMENT_LIVINGROOM, + EAX_ENVIRONMENT_STONEROOM, + EAX_ENVIRONMENT_AUDITORIUM, + EAX_ENVIRONMENT_CONCERTHALL, + EAX_ENVIRONMENT_CAVE, + EAX_ENVIRONMENT_ARENA, + EAX_ENVIRONMENT_HANGAR, + EAX_ENVIRONMENT_CARPETEDHALLWAY, + EAX_ENVIRONMENT_HALLWAY, + EAX_ENVIRONMENT_STONECORRIDOR, + EAX_ENVIRONMENT_ALLEY, + EAX_ENVIRONMENT_FOREST, + EAX_ENVIRONMENT_CITY, + EAX_ENVIRONMENT_MOUNTAINS, + EAX_ENVIRONMENT_QUARRY, + EAX_ENVIRONMENT_PLAIN, + EAX_ENVIRONMENT_PARKINGLOT, + EAX_ENVIRONMENT_SEWERPIPE, + EAX_ENVIRONMENT_UNDERWATER, + EAX_ENVIRONMENT_DRUGGED, + EAX_ENVIRONMENT_DIZZY, + EAX_ENVIRONMENT_PSYCHOTIC, + + EAX_ENVIRONMENT_UNDEFINED, + + EAX_ENVIRONMENT_COUNT +}; + +// Used by DSPROPERTY_EAXLISTENER_FLAGS +// +// Note: The number and order of flags may change in future EAX versions. +// It is recommended to use the flag defines as follows: +// myFlags = EAXLISTENERFLAGS_DECAYTIMESCALE | EAXLISTENERFLAGS_REVERBSCALE; +// instead of: +// myFlags = 0x00000009; +// +// These flags determine what properties are affected by environment size. +#define EAXLISTENERFLAGS_DECAYTIMESCALE 0x00000001 // reverberation decay time +#define EAXLISTENERFLAGS_REFLECTIONSSCALE 0x00000002 // reflection level +#define EAXLISTENERFLAGS_REFLECTIONSDELAYSCALE 0x00000004 // initial reflection delay time +#define EAXLISTENERFLAGS_REVERBSCALE 0x00000008 // reflections level +#define EAXLISTENERFLAGS_REVERBDELAYSCALE 0x00000010 // late reverberation delay time +#define EAXLISTENERFLAGS_ECHOTIMESCALE 0x00000040 // echo time +#define EAXLISTENERFLAGS_MODULATIONTIMESCALE 0x00000080 // modulation time + +// This flag limits high-frequency decay time according to air absorption. +#define EAXLISTENERFLAGS_DECAYHFLIMIT 0x00000020 + +#define EAXLISTENERFLAGS_RESERVED 0xFFFFFF00 // reserved future use + +// Property ranges and defaults: + +#define EAXLISTENER_MINENVIRONMENT 0 +#define EAXLISTENER_MAXENVIRONMENT (EAX_ENVIRONMENT_COUNT-1) +#define EAXLISTENER_DEFAULTENVIRONMENT EAX_ENVIRONMENT_GENERIC + +#define EAXLISTENER_MINENVIRONMENTSIZE 1.0f +#define EAXLISTENER_MAXENVIRONMENTSIZE 100.0f +#define EAXLISTENER_DEFAULTENVIRONMENTSIZE 7.5f + +#define EAXLISTENER_MINENVIRONMENTDIFFUSION 0.0f +#define EAXLISTENER_MAXENVIRONMENTDIFFUSION 1.0f +#define EAXLISTENER_DEFAULTENVIRONMENTDIFFUSION 1.0f + +#define EAXLISTENER_MINROOM (-10000) +#define EAXLISTENER_MAXROOM 0 +#define EAXLISTENER_DEFAULTROOM (-1000) + +#define EAXLISTENER_MINROOMHF (-10000) +#define EAXLISTENER_MAXROOMHF 0 +#define EAXLISTENER_DEFAULTROOMHF (-100) + +#define EAXLISTENER_MINROOMLF (-10000) +#define EAXLISTENER_MAXROOMLF 0 +#define EAXLISTENER_DEFAULTROOMLF 0 + +#define EAXLISTENER_MINDECAYTIME 0.1f +#define EAXLISTENER_MAXDECAYTIME 20.0f +#define EAXLISTENER_DEFAULTDECAYTIME 1.49f + +#define EAXLISTENER_MINDECAYHFRATIO 0.1f +#define EAXLISTENER_MAXDECAYHFRATIO 2.0f +#define EAXLISTENER_DEFAULTDECAYHFRATIO 0.83f + +#define EAXLISTENER_MINDECAYLFRATIO 0.1f +#define EAXLISTENER_MAXDECAYLFRATIO 2.0f +#define EAXLISTENER_DEFAULTDECAYLFRATIO 1.00f + +#define EAXLISTENER_MINREFLECTIONS (-10000) +#define EAXLISTENER_MAXREFLECTIONS 1000 +#define EAXLISTENER_DEFAULTREFLECTIONS (-2602) + +#define EAXLISTENER_MINREFLECTIONSDELAY 0.0f +#define EAXLISTENER_MAXREFLECTIONSDELAY 0.3f +#define EAXLISTENER_DEFAULTREFLECTIONSDELAY 0.007f + +#define EAXLISTENER_MINREVERB (-10000) +#define EAXLISTENER_MAXREVERB 2000 +#define EAXLISTENER_DEFAULTREVERB 200 + +#define EAXLISTENER_MINREVERBDELAY 0.0f +#define EAXLISTENER_MAXREVERBDELAY 0.1f +#define EAXLISTENER_DEFAULTREVERBDELAY 0.011f + +#define EAXLISTENER_MINECHOTIME 0.075f +#define EAXLISTENER_MAXECHOTIME 0.25f +#define EAXLISTENER_DEFAULTECHOTIME 0.25f + +#define EAXLISTENER_MINECHODEPTH 0.0f +#define EAXLISTENER_MAXECHODEPTH 1.0f +#define EAXLISTENER_DEFAULTECHODEPTH 0.0f + +#define EAXLISTENER_MINMODULATIONTIME 0.04f +#define EAXLISTENER_MAXMODULATIONTIME 4.0f +#define EAXLISTENER_DEFAULTMODULATIONTIME 0.25f + +#define EAXLISTENER_MINMODULATIONDEPTH 0.0f +#define EAXLISTENER_MAXMODULATIONDEPTH 1.0f +#define EAXLISTENER_DEFAULTMODULATIONDEPTH 0.0f + +#define EAXLISTENER_MINAIRABSORPTIONHF (-100.0f) +#define EAXLISTENER_MAXAIRABSORPTIONHF 0.0f +#define EAXLISTENER_DEFAULTAIRABSORPTIONHF (-5.0f) + +#define EAXLISTENER_MINHFREFERENCE 1000.0f +#define EAXLISTENER_MAXHFREFERENCE 20000.0f +#define EAXLISTENER_DEFAULTHFREFERENCE 5000.0f + +#define EAXLISTENER_MINLFREFERENCE 20.0f +#define EAXLISTENER_MAXLFREFERENCE 1000.0f +#define EAXLISTENER_DEFAULTLFREFERENCE 250.0f + +#define EAXLISTENER_MINROOMROLLOFFFACTOR 0.0f +#define EAXLISTENER_MAXROOMROLLOFFFACTOR 10.0f +#define EAXLISTENER_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXLISTENER_DEFAULTFLAGS (EAXLISTENERFLAGS_DECAYTIMESCALE | \ + EAXLISTENERFLAGS_REFLECTIONSSCALE | \ + EAXLISTENERFLAGS_REFLECTIONSDELAYSCALE | \ + EAXLISTENERFLAGS_REVERBSCALE | \ + EAXLISTENERFLAGS_REVERBDELAYSCALE | \ + EAXLISTENERFLAGS_DECAYHFLIMIT) + + + +/* +* EAX 3.0 buffer property set {A8FA6881-B476-11d3-BDB9-00C0F02DDF87} +*/ +DEFINE_ALGUID(DSPROPSETID_EAX30_BufferProperties, + 0xa8fa6881, + 0xb476, + 0x11d3, + 0xbd, 0xb9, 0x0, 0xc0, 0xf0, 0x2d, 0xdf, 0x87); + +// For compatibility with future EAX versions: +#define DSPROPSETID_EAX_BufferProperties DSPROPSETID_EAX30_BufferProperties +#define DSPROPSETID_EAX_SourceProperties DSPROPSETID_EAX30_BufferProperties + +typedef enum +{ + DSPROPERTY_EAXBUFFER_NONE, + DSPROPERTY_EAXBUFFER_ALLPARAMETERS, + DSPROPERTY_EAXBUFFER_OBSTRUCTIONPARAMETERS, + DSPROPERTY_EAXBUFFER_OCCLUSIONPARAMETERS, + DSPROPERTY_EAXBUFFER_EXCLUSIONPARAMETERS, + DSPROPERTY_EAXBUFFER_DIRECT, + DSPROPERTY_EAXBUFFER_DIRECTHF, + DSPROPERTY_EAXBUFFER_ROOM, + DSPROPERTY_EAXBUFFER_ROOMHF, + DSPROPERTY_EAXBUFFER_OBSTRUCTION, + DSPROPERTY_EAXBUFFER_OBSTRUCTIONLFRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSION, + DSPROPERTY_EAXBUFFER_OCCLUSIONLFRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSIONROOMRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSIONDIRECTRATIO, + DSPROPERTY_EAXBUFFER_EXCLUSION, + DSPROPERTY_EAXBUFFER_EXCLUSIONLFRATIO, + DSPROPERTY_EAXBUFFER_OUTSIDEVOLUMEHF, + DSPROPERTY_EAXBUFFER_DOPPLERFACTOR, + DSPROPERTY_EAXBUFFER_ROLLOFFFACTOR, + DSPROPERTY_EAXBUFFER_ROOMROLLOFFFACTOR, + DSPROPERTY_EAXBUFFER_AIRABSORPTIONFACTOR, + DSPROPERTY_EAXBUFFER_FLAGS +} DSPROPERTY_EAX_BUFFERPROPERTY; + +// OR these flags with property id +#define DSPROPERTY_EAXBUFFER_IMMEDIATE 0x00000000 // changes take effect immediately +#define DSPROPERTY_EAXBUFFER_DEFERRED 0x80000000 // changes take effect later +#define DSPROPERTY_EAXBUFFER_COMMITDEFERREDSETTINGS (DSPROPERTY_EAXBUFFER_NONE | \ + DSPROPERTY_EAXBUFFER_IMMEDIATE) + +// Use this structure for DSPROPERTY_EAXBUFFER_ALLPARAMETERS +// - all levels are hundredths of decibels +// - all delays are in seconds +// +// NOTE: This structure may change in future EAX versions. +// It is recommended to initialize fields by name: +// myBuffer.lDirect = 0; +// myBuffer.lDirectHF = -200; +// ... +// myBuffer.dwFlags = myFlags /* see EAXBUFFERFLAGS below */ ; +// instead of: +// myBuffer = { 0, -200, ... , 0x00000003 }; +// +typedef struct _EAXBUFFERPROPERTIES +{ + long lDirect; // direct path level (at low and mid frequencies) + long lDirectHF; // relative direct path level at high frequencies + long lRoom; // room effect level (at low and mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lObstruction; // main obstruction control (attenuation at high frequencies) + float flObstructionLFRatio; // obstruction low-frequency level re. main control + long lOcclusion; // main occlusion control (attenuation at high frequencies) + float flOcclusionLFRatio; // occlusion low-frequency level re. main control + float flOcclusionRoomRatio; // relative occlusion control for room effect + float flOcclusionDirectRatio; // relative occlusion control for direct path + long lExclusion; // main exlusion control (attenuation at high frequencies) + float flExclusionLFRatio; // exclusion low-frequency level re. main control + long lOutsideVolumeHF; // outside sound cone level at high frequencies + float flDopplerFactor; // like DS3D flDopplerFactor but per source + float flRolloffFactor; // like DS3D flRolloffFactor but per source + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + float flAirAbsorptionFactor; // multiplies DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF + unsigned long ulFlags; // modifies the behavior of properties +} EAXBUFFERPROPERTIES, *LPEAXBUFFERPROPERTIES; + +// Use this structure for DSPROPERTY_EAXBUFFER_OBSTRUCTION, +typedef struct _EAXOBSTRUCTIONPROPERTIES +{ + long lObstruction; + float flObstructionLFRatio; +} EAXOBSTRUCTIONPROPERTIES, *LPEAXOBSTRUCTIONPROPERTIES; + +// Use this structure for DSPROPERTY_EAXBUFFER_OCCLUSION +typedef struct _EAXOCCLUSIONPROPERTIES +{ + long lOcclusion; + float flOcclusionLFRatio; + float flOcclusionRoomRatio; + float flOcclusionDirectRatio; +} EAXOCCLUSIONPROPERTIES, *LPEAXOCCLUSIONPROPERTIES; + +// Use this structure for DSPROPERTY_EAXBUFFER_EXCLUSION +typedef struct _EAXEXCLUSIONPROPERTIES +{ + long lExclusion; + float flExclusionLFRatio; +} EAXEXCLUSIONPROPERTIES, *LPEAXEXCLUSIONPROPERTIES; + +// Used by DSPROPERTY_EAXBUFFER_FLAGS +// TRUE: value is computed automatically - property is an offset +// FALSE: value is used directly +// +// Note: The number and order of flags may change in future EAX versions. +// To insure future compatibility, use flag defines as follows: +// myFlags = EAXBUFFERFLAGS_DIRECTHFAUTO | EAXBUFFERFLAGS_ROOMAUTO; +// instead of: +// myFlags = 0x00000003; +// +#define EAXBUFFERFLAGS_DIRECTHFAUTO 0x00000001 // affects DSPROPERTY_EAXBUFFER_DIRECTHF +#define EAXBUFFERFLAGS_ROOMAUTO 0x00000002 // affects DSPROPERTY_EAXBUFFER_ROOM +#define EAXBUFFERFLAGS_ROOMHFAUTO 0x00000004 // affects DSPROPERTY_EAXBUFFER_ROOMHF + +#define EAXBUFFERFLAGS_RESERVED 0xFFFFFFF8 // reserved future use + +// Property ranges and defaults: + +#define EAXBUFFER_MINDIRECT (-10000) +#define EAXBUFFER_MAXDIRECT 1000 +#define EAXBUFFER_DEFAULTDIRECT 0 + +#define EAXBUFFER_MINDIRECTHF (-10000) +#define EAXBUFFER_MAXDIRECTHF 0 +#define EAXBUFFER_DEFAULTDIRECTHF 0 + +#define EAXBUFFER_MINROOM (-10000) +#define EAXBUFFER_MAXROOM 1000 +#define EAXBUFFER_DEFAULTROOM 0 + +#define EAXBUFFER_MINROOMHF (-10000) +#define EAXBUFFER_MAXROOMHF 0 +#define EAXBUFFER_DEFAULTROOMHF 0 + +#define EAXBUFFER_MINOBSTRUCTION (-10000) +#define EAXBUFFER_MAXOBSTRUCTION 0 +#define EAXBUFFER_DEFAULTOBSTRUCTION 0 + +#define EAXBUFFER_MINOBSTRUCTIONLFRATIO 0.0f +#define EAXBUFFER_MAXOBSTRUCTIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTOBSTRUCTIONLFRATIO 0.0f + +#define EAXBUFFER_MINOCCLUSION (-10000) +#define EAXBUFFER_MAXOCCLUSION 0 +#define EAXBUFFER_DEFAULTOCCLUSION 0 + +#define EAXBUFFER_MINOCCLUSIONLFRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTOCCLUSIONLFRATIO 0.25f + +#define EAXBUFFER_MINOCCLUSIONROOMRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONROOMRATIO 10.0f +#define EAXBUFFER_DEFAULTOCCLUSIONROOMRATIO 1.5f + +#define EAXBUFFER_MINOCCLUSIONDIRECTRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONDIRECTRATIO 10.0f +#define EAXBUFFER_DEFAULTOCCLUSIONDIRECTRATIO 1.0f + +#define EAXBUFFER_MINEXCLUSION (-10000) +#define EAXBUFFER_MAXEXCLUSION 0 +#define EAXBUFFER_DEFAULTEXCLUSION 0 + +#define EAXBUFFER_MINEXCLUSIONLFRATIO 0.0f +#define EAXBUFFER_MAXEXCLUSIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTEXCLUSIONLFRATIO 1.0f + +#define EAXBUFFER_MINOUTSIDEVOLUMEHF (-10000) +#define EAXBUFFER_MAXOUTSIDEVOLUMEHF 0 +#define EAXBUFFER_DEFAULTOUTSIDEVOLUMEHF 0 + +#define EAXBUFFER_MINDOPPLERFACTOR 0.0f +#define EAXBUFFER_MAXDOPPLERFACTOR 10.f +#define EAXBUFFER_DEFAULTDOPPLERFACTOR 0.0f + +#define EAXBUFFER_MINROLLOFFFACTOR 0.0f +#define EAXBUFFER_MAXROLLOFFFACTOR 10.f +#define EAXBUFFER_DEFAULTROLLOFFFACTOR 0.0f + +#define EAXBUFFER_MINROOMROLLOFFFACTOR 0.0f +#define EAXBUFFER_MAXROOMROLLOFFFACTOR 10.f +#define EAXBUFFER_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXBUFFER_MINAIRABSORPTIONFACTOR 0.0f +#define EAXBUFFER_MAXAIRABSORPTIONFACTOR 10.0f +#define EAXBUFFER_DEFAULTAIRABSORPTIONFACTOR 1.0f + +#define EAXBUFFER_DEFAULTFLAGS (EAXBUFFERFLAGS_DIRECTHFAUTO | \ + EAXBUFFERFLAGS_ROOMAUTO | \ + EAXBUFFERFLAGS_ROOMHFAUTO ) diff --git a/Engine/lib/openal/LINUX/libopenal.so b/Engine/lib/openal/LINUX/libopenal.so new file mode 100644 index 000000000..ed65305a0 Binary files /dev/null and b/Engine/lib/openal/LINUX/libopenal.so differ diff --git a/Engine/lib/openal/LINUX/openALFn.h b/Engine/lib/openal/LINUX/openALFn.h new file mode 100644 index 000000000..0f0b00d48 --- /dev/null +++ b/Engine/lib/openal/LINUX/openALFn.h @@ -0,0 +1,101 @@ +#ifndef AL_FUNCTION +#define AL_FUNCTION(fn_return, fn_name, fn_args); +#endif + +#ifndef AL_EXTENSION +#define AL_EXTENSION(ext_name) +#endif + +#ifndef AL_EXT_FUNCTION +#define AL_EXT_FUNCTION(ext_name, fn_return, fn_name, fn_args) +#endif + +// AL functions +AL_FUNCTION(ALvoid, alEnable, ( ALenum capability )) +AL_FUNCTION(ALvoid, alDisable, ( ALenum capability )) +AL_FUNCTION(ALboolean, alIsEnabled, ( ALenum capability )) +AL_FUNCTION(ALvoid, alHint, ( ALenum target, ALenum mode )) +AL_FUNCTION(ALboolean, alGetBoolean, ( ALenum param )) +AL_FUNCTION(ALint, alGetInteger, ( ALenum param )) +AL_FUNCTION(ALfloat, alGetFloat, ( ALenum param )) +AL_FUNCTION(ALdouble, alGetDouble, ( ALenum param )) +AL_FUNCTION(ALvoid, alGetBooleanv, ( ALenum param, ALboolean* data )) +AL_FUNCTION(ALvoid, alGetIntegerv, ( ALenum param, ALint* data )) +AL_FUNCTION(ALvoid, alGetFloatv, ( ALenum param, ALfloat* data )) +AL_FUNCTION(ALvoid, alGetDoublev, ( ALenum param, ALdouble* data )) +AL_FUNCTION(const ALubyte*, alGetString, ( ALenum param )) +AL_FUNCTION(ALenum, alGetError, ( ALvoid )) +AL_FUNCTION(ALboolean, alIsExtensionPresent, ( const ALubyte* fname )) +AL_FUNCTION(ALvoid*, alGetProcAddress, ( const ALubyte* fname )) +AL_FUNCTION(ALenum, alGetEnumValue, ( const ALubyte* ename )) +AL_FUNCTION(ALvoid, alListenerf, ( ALenum pname, ALfloat param )) +AL_FUNCTION(ALvoid, alListener3f, ( ALenum pname, ALfloat param1, ALfloat param2, ALfloat param3 )) +AL_FUNCTION(ALvoid, alListenerfv, ( ALenum pname, ALfloat* param )) +AL_FUNCTION(ALvoid, alGetListeneri, ( ALenum pname, ALint* value )) +AL_FUNCTION(ALvoid, alGetListenerf, ( ALenum pname, ALfloat* values )) +AL_FUNCTION(ALvoid, alGetListenerfv, ( ALenum pname, ALfloat* values )) +AL_FUNCTION(ALvoid, alGenSources, ( ALsizei n, ALuint* sources )) +AL_FUNCTION(ALvoid, alDeleteSources, ( ALsizei n, ALuint* sources )) +AL_FUNCTION(ALboolean, alIsSource, ( ALuint sid )) +AL_FUNCTION(ALvoid, alSourcei, ( ALuint sid, ALenum param, ALint value )) +AL_FUNCTION(ALvoid, alSourcef, ( ALuint sid, ALenum param, ALfloat value )) +AL_FUNCTION(ALvoid, alSource3f, ( ALuint sid, ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 )) +AL_FUNCTION(ALvoid, alSourcefv, ( ALuint sid, ALenum param, ALfloat* values )) +AL_FUNCTION(ALvoid, alGetSourcei, ( ALuint sid, ALenum pname, ALint* value )) +AL_FUNCTION(ALvoid, alGetSourcef, ( ALuint sid, ALenum pname, ALfloat* value )) +AL_FUNCTION(ALvoid, alGetSourcefv, ( ALuint sid, ALenum pname, ALfloat* values )) +AL_FUNCTION(ALvoid, alSourcePlayv, ( ALuint ns, ALuint* ids )) +AL_FUNCTION(ALvoid, alSourceStopv, ( ALuint ns, ALuint* ids )) +AL_FUNCTION(ALvoid, alSourcePlay, ( ALuint sid )) +AL_FUNCTION(ALvoid, alSourcePause, ( ALuint sid )) +AL_FUNCTION(ALvoid, alSourceStop, ( ALuint sid )) +AL_FUNCTION(ALvoid, alGenBuffers, ( ALsizei n, ALuint* samples )) +AL_FUNCTION(ALvoid, alDeleteBuffers, ( ALsizei n, ALuint* samples )) +AL_FUNCTION(ALboolean, alIsBuffer, ( ALuint buffer )) +AL_FUNCTION(ALvoid, alBufferData, ( ALuint buffer, ALenum format, ALvoid* data, ALsizei size, ALsizei freq )) +AL_FUNCTION(ALsizei, alBufferAppendData, ( ALuint buffer, ALenum format, ALvoid* data, ALsizei size, ALsizei freq )) +AL_FUNCTION(ALvoid, alGetBufferi, ( ALuint buffer, ALenum param, ALint* value )) +AL_FUNCTION(ALvoid, alGetBufferf, ( ALuint buffer, ALenum param, ALfloat* value )) + +// ALC functions +AL_FUNCTION(ALvoid*, alcCreateContext, ( ALint* attrlist )) +AL_FUNCTION(ALCenum, alcMakeContextCurrent, ( ALvoid* context )) +AL_FUNCTION(ALvoid*, alcUpdateContext, ( ALvoid* context )) +AL_FUNCTION(ALCenum, alcDestroyContext, ( ALvoid* context )) +AL_FUNCTION(ALCenum, alcGetError, ( ALvoid )) +AL_FUNCTION(const ALubyte *, alcGetErrorString, ( ALvoid )) +AL_FUNCTION(ALvoid*, alcGetCurrentContext, ( ALvoid )) + +// ALUT functions +AL_FUNCTION(void, alutInit, ( int* argc, char** argv )) +AL_FUNCTION(void, alutExit, ( ALvoid )) +AL_FUNCTION(ALboolean, alutLoadWAV, ( const char* fname, ALvoid** data, ALsizei* format, ALsizei* size, ALsizei* bits, ALsizei* freq )) + +// Extensions +AL_EXTENSION(AL_EXT_IASIG) +AL_EXT_FUNCTION(AL_EXT_IASIG, ALvoid, alGenEnvironmentIASIG, ( ALsizei n, ALuint* environs )) +AL_EXT_FUNCTION(AL_EXT_IASIG, ALvoid, alDeleteEnvironmentIASIG, ( ALsizei n, ALuint* environs )) +AL_EXT_FUNCTION(AL_EXT_IASIG, ALboolean, alIsEnvironmentIASIG, ( ALuint environment )) +AL_EXT_FUNCTION(AL_EXT_IASIG, ALvoid, alEnvironmentiIASIG, ( ALuint eid, ALenum param, ALint value )) + +AL_EXTENSION(AL_EXT_DYNAMIX) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alBufferi_EXT, ( ALuint buffer, ALenum pname, ALint value )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alBufferSyncData_EXT, ( ALuint buffer, ALenum format, ALvoid* data, ALsizei size, ALsizei freq )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alBufferStreamFile_EXT, ( ALuint buffer, const ALubyte* filename )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alSourceCallback_EXT, ( ALuint source, ALvoid* callback )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALvoid, alSourceResetEnvironment_EXT, ( ALuint source )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alContexti_EXT, ( ALenum pname, ALint value )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alGetContexti_EXT, ( ALenum pname, ALint* value )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alGetContextstr_EXT, ( ALenum pname, ALuint idx, ALubyte** value )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alCaptureInit_EXT, ( ALenum format, ALuint rate, ALsizei bufferSize )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alCaptureDestroy_EXT, ( ALvoid )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alCaptureStart_EXT, ( ALvoid )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alCaptureStop_EXT, ( ALvoid )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALsizei, alCaptureGetData_EXT, ( ALvoid* data, ALsizei n, ALenum format, ALuint rate )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALvoid, alEnvironmentfIASIG, ( ALuint eid, ALenum param, ALfloat value )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALvoid, alGetEnvironmentiIASIG_EXT, ( ALuint eid, ALenum param, ALint * value )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALvoid, alGetEnvironmentfIASIG_EXT, ( ALuint eid, ALenum param, ALfloat * value )) + +#undef AL_EXTENSION +#undef AL_FUNCTION +#undef AL_EXT_FUNCTION diff --git a/Engine/lib/openal/LINUX/readme.txt b/Engine/lib/openal/LINUX/readme.txt new file mode 100644 index 000000000..eaba63059 --- /dev/null +++ b/Engine/lib/openal/LINUX/readme.txt @@ -0,0 +1,10 @@ +This is an i586 optimized openal library that is suitable for shipping +games. It is glibc 2.2.5+ compatible. Note that currently this library is +not used when building the engine source code (it is only used by the +install_build scripts). + +Built on RH 7.3 with gcc296. + +Configure command: + +./configure --target=i586-pc-linux-gnu --enable-optimization --enable-sdl=yes diff --git a/Engine/lib/openal/OpenBSD/AL/al.h b/Engine/lib/openal/OpenBSD/AL/al.h new file mode 100644 index 000000000..6bbc1bb9e --- /dev/null +++ b/Engine/lib/openal/OpenBSD/AL/al.h @@ -0,0 +1,523 @@ +#ifndef __al_h_ +#define __al_h_ + +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2000 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 +#define ALAPI __declspec(dllexport) +#define ALAPIENTRY __cdecl +#define AL_CALLBACK +#else /* _WIN32 */ +#define ALAPI +#define ALAPIENTRY +#define AL_CALLBACK +#endif /* _WIN32 */ + +#ifndef AL_NO_PROTOTYPES + +/** + * OpenAL Maintenance Functions + * State Management and Query. + * Error Handling. + * Extension Support. + */ + + +/** Renderer State management. */ +ALAPI void ALAPIENTRY alEnable( ALenum capability ); + +ALAPI void ALAPIENTRY alDisable( ALenum capability ); + +ALAPI ALboolean ALAPIENTRY alIsEnabled( ALenum capability ); + +/** Application preferences for driver performance choices. */ +ALAPI void ALAPIENTRY alHint( ALenum target, ALenum mode ); + +/** State retrieval. */ +ALAPI ALboolean ALAPIENTRY alGetBoolean( ALenum param ); + +/** State retrieval. */ +ALAPI ALint ALAPIENTRY alGetInteger( ALenum param ); + +/** State retrieval. */ +ALAPI ALfloat ALAPIENTRY alGetFloat( ALenum param ); + +/** State retrieval. */ +ALAPI ALdouble ALAPIENTRY alGetDouble( ALenum param ); + +/** State retrieval. */ +ALAPI void ALAPIENTRY alGetBooleanv( ALenum param, ALboolean* data ); + +/** State retrieval. */ +ALAPI void ALAPIENTRY alGetIntegerv( ALenum param, ALint* data ); + +/** State retrieval. */ +ALAPI void ALAPIENTRY alGetFloatv( ALenum param, ALfloat* data ); + +/** State retrieval. */ +ALAPI void ALAPIENTRY alGetDoublev( ALenum param, ALdouble* data ); + +/** State retrieval. */ +ALAPI const ALubyte* ALAPIENTRY alGetString( ALenum param ); + + +/** + * Error support. + * Obtain the most recent error generated in the AL state machine. + */ +ALAPI ALenum ALAPIENTRY alGetError( ALvoid ); + +/** + * Extension support. + * Obtain the address of a function (usually an extension) + * with the name fname. All addresses are context-independent. + */ +ALAPI ALboolean ALAPIENTRY alIsExtensionPresent( const ALubyte* fname ); + + +/** + * Extension support. + * Obtain the address of a function (usually an extension) + * with the name fname. All addresses are context-independent. + */ +ALAPI void* ALAPIENTRY alGetProcAddress( const ALubyte* fname ); + + +/** + * Extension support. + * Obtain the integer value of an enumeration (usually an extension) with the name ename. + */ +ALAPI ALenum ALAPIENTRY alGetEnumValue( const ALubyte* ename ); + + + + + + +/** + * LISTENER + * Listener is the sample position for a given context. + * The multi-channel (usually stereo) output stream generated + * by the mixer is parametrized by this Listener object: + * its position and velocity relative to Sources, within + * occluder and reflector geometry. + */ + + + +/** + * + * Listener Gain: default 1.0f. + */ +ALAPI void ALAPIENTRY alListenerf( ALenum pname, ALfloat param ); + +/** + * + * Listener Position. + * Listener Velocity. + */ +ALAPI void ALAPIENTRY alListener3f( ALenum pname, ALfloat param1, + ALfloat param2, + ALfloat param3 ); + +/** + * + * Listener Position: ALfloat[3] + * Listener Velocity: ALfloat[3] + * Listener Orientation: ALfloat[6] (forward and up vector). + */ +ALAPI void ALAPIENTRY alListenerfv( ALenum pname, ALfloat* param ); + +/* + * Retrieve listener information. + */ +ALAPI void ALAPIENTRY alGetListeneri( ALenum pname, ALint* value ); +ALAPI void ALAPIENTRY alGetListenerf( ALenum pname, ALfloat* values ); +ALAPI void ALAPIENTRY alGetListenerfv( ALenum pname, ALfloat* values ); + +/** + * SOURCE + * Source objects are by default localized. Sources + * take the PCM data provided in the specified Buffer, + * apply Source-specific modifications, and then + * submit them to be mixed according to spatial + * arrangement etc. + */ + + + +/** Create Source objects. */ +ALAPI void ALAPIENTRY alGenSources( ALsizei n, ALuint* sources ); + +/** Delete Source objects. */ +ALAPI void ALAPIENTRY alDeleteSources( ALsizei n, ALuint* sources ); + +/** Verify a handle is a valid Source. */ +ALAPI ALboolean ALAPIENTRY alIsSource( ALuint sid ); + + +/** Set an integer parameter for a Source object. */ +ALAPI void ALAPIENTRY alSourcei( ALuint sid, ALenum param, ALint value ); +ALAPI void ALAPIENTRY alSourcef( ALuint sid, ALenum param, ALfloat value ); +ALAPI void ALAPIENTRY alSource3f( ALuint sid, ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 ); +ALAPI void ALAPIENTRY alSourcefv( ALuint sid, ALenum param, ALfloat* values ); + +/** Get an integer parameter for a Source object. */ +ALAPI void ALAPIENTRY alGetSourcei( ALuint sid, ALenum pname, ALint* value ); +ALAPI void ALAPIENTRY alGetSourcef( ALuint sid, ALenum pname, ALfloat* value ); +ALAPI void ALAPIENTRY alGetSourcefv( ALuint sid, ALenum pname, ALfloat* values ); + +ALAPI void ALAPIENTRY alSourcePlayv( ALuint ns, ALuint *ids ); +ALAPI void ALAPIENTRY alSourceStopv( ALuint ns, ALuint *ids ); + +/** Activate a source, start replay. */ +ALAPI void ALAPIENTRY alSourcePlay( ALuint sid ); + +/** + * Pause a source, + * temporarily remove it from the mixer list. + */ +ALAPI void ALAPIENTRY alSourcePause( ALuint sid ); + +/** + * Stop a source, + * temporarily remove it from the mixer list, + * and reset its internal state to pre-Play. + * To remove a Source completely, it has to be + * deleted following Stop, or before Play. + */ +ALAPI void ALAPIENTRY alSourceStop( ALuint sid ); + + + + +/** + * BUFFER + * Buffer objects are storage space for sample data. + * Buffers are referred to by Sources. There can be more than + * one Source using the same Buffer data. If Buffers have + * to be duplicated on a per-Source basis, the driver has to + * take care of allocation, copying, and deallocation as well + * as propagating buffer data changes. + */ + + + + +/** Buffer object generation. */ +ALAPI void ALAPIENTRY alGenBuffers( ALsizei n, ALuint* samples ); + +ALAPI void ALAPIENTRY alDeleteBuffers( ALsizei n, ALuint* samples ); + + +ALAPI ALboolean ALAPIENTRY alIsBuffer( ALuint buffer ); + +/** + * Specify the data to be filled into a buffer. + */ +ALAPI void ALAPIENTRY alBufferData( ALuint buffer, + ALenum format, + ALvoid* data, + ALsizei size, + ALsizei freq ); + + +/** + * Specify data to be filled into a looping buffer. + * This takes the current position at the time of the + * call, and returns the number of samples written. + */ +ALsizei ALAPIENTRY alBufferAppendData( ALuint buffer, + ALenum format, + ALvoid* data, + ALsizei size, + ALsizei freq ); + + + + +ALAPI void ALAPIENTRY alGetBufferi( ALuint buffer, ALenum param, ALint* value ); +ALAPI void ALAPIENTRY alGetBufferf( ALuint buffer, ALenum param, ALfloat* value ); + + + + + +/** + * Frequency Domain Filters are band filters. + * Attenuation in Media (distance based) + * Reflection Material + * Occlusion Material (separating surface) + * + * Temporal Domain Filters: + * Early Reflections + * Late Reverb + * + */ + + + + +/** + * EXTENSION: IASIG Level 2 Environment. + * Environment object generation. + * This is an EXTension that describes the Environment/Reverb + * properties according to IASIG Level 2 specifications. + */ + + + + + +/** + * Allocate n environment ids and store them in the array environs. + * Returns the number of environments actually allocated. + */ +ALAPI ALsizei ALAPIENTRY alGenEnvironmentIASIG( ALsizei n, ALuint* environs ); + +ALAPI void ALAPIENTRY alDeleteEnvironmentIASIG( ALsizei n, ALuint* environs ); + +ALAPI ALboolean ALAPIENTRY alIsEnvironmentIASIG( ALuint environ ); + +ALAPI void ALAPIENTRY alEnvironmentiIASIG( ALuint eid, ALenum param, ALint value ); + +ALAPI void ALAPIENTRY alEnvironmentfIASIG( ALuint eid, ALenum param, ALfloat value ); + + + + +#else /* AL_NO_PROTOTYPES */ +// +// +///** OpenAL Maintenance Functions */ +// +// void (*alEnable)( ALenum capability ); +// void (*alDisable)( ALenum capability ); +// ALboolean (*alIsEnabled)( ALenum capability ); +// void (*alHint)( ALenum target, ALenum mode ); +// ALboolean (*alGetBoolean)( ALenum param ); +// ALint (*alGetInteger)( ALenum param ); +// ALfloat (*alGetFloat)( ALenum param ); +// ALdouble (*alGetDouble)( ALenum param ); +// void (*alGetBooleanv)( ALenum param, +// ALboolean* data ); +// void (*alGetIntegerv)( ALenum param, +// ALint* data ); +// void (*alGetFloatv)( ALenum param, +// ALfloat* data ); +// void (*alGetDoublev)( ALenum param, +// ALdouble* data ); +// const ALubyte* (*GetString)( ALenum param ); +// ALenum (*alGetError)( ALvoid ); +// +// /** +// * Extension support. +// * Query existance of extension +// */ +// ALboolean (*alIsExtensionPresent)(const ALubyte* fname ); +// +// /** +// * Extension support. +// * Obtain the address of a function (usually an extension) +// * with the name fname. All addresses are context-independent. +// */ +// void* (*alGetProcAddress)( const ALubyte* fname ); +// +// +// /** +// * Extension support. +// * Obtain the integer value of an enumeration (usually an extension) with the name ename. +// */ +// ALenum (*alGetEnumValue)( const ALubyte* ename ); +// +///** +// * LISTENER +// * Listener is the sample position for a given context. +// * The multi-channel (usually stereo) output stream generated +// * by the mixer is parametrized by this Listener object: +// * its position and velocity relative to Sources, within +// * occluder and reflector geometry. +// */ +// /** +// * +// * Listener Gain: default 1.0f. +// */ +// void (*alListenerf)( ALenum pname, ALfloat param ); +// +// /** +// * +// * Listener Position. +// * Listener Velocity. +// */ +// void (*alListener3f)( ALenum pname, +// ALfloat param1, ALfloat param2, ALfloat param3); +// +// /** +// * +// * Listener Position: ALfloat[3] +// * Listener Velocity: ALfloat[3] +// * Listener Orientation: ALfloat[6] (forward and up vector). +// */ +// void (*alListenerfv)( ALenum pname, ALfloat* param ); +// +///** +// * SOURCE +// * Source objects are by default localized. Sources +// * take the PCM data provided in the specified Buffer, +// * apply Source-specific modifications, and then +// * submit them to be mixed according to spatial +// * arrangement etc. +// */ +// +// /** Create Source objects. */ +// void (*alGenSources)( ALsizei n, ALuint* sources ); +// +// /** Delete Source objects. */ +// void (*alDeleteSources)( ALsizei n, ALuint* sources ); +// +// /** Verify a handle is a valid Source. */ +// ALboolean (*alIsSource)( ALuint sid ); +// +// /** Set an integer parameter for a Source object. */ +// void (*alSourcei)( ALuint sid, ALenum param, ALint value); +// +// /** Set a float parameter for a Source object. */ +// void (*alSourcef)( ALuint sid, ALenum param, ALfloat value); +// +// /** Set a 3-float parameter for a Source object. */ +// void (*alSource3f)( ALuint sid, ALenum param, +// ALfloat v1, ALfloat v2, ALfloat v3 ); +// +// /** Set a float vector parameter for a Source object. */ +// void (*alSourcefv)( ALuint sid, ALenum param, +// ALfloat* values ); +// +// /** Get an integer parameter for a Source object. */ +// void (*alGetSourcei)( ALuint sid, +// ALenum pname, ALint* value ); +// /** Get a float parameter for a Source object. */ +// void (*alGetSourcef)( ALuint sid, +// ALenum pname, ALfloat* value ); +// /** Get a float vector parameter for a Source object. */ +// void (*alGetSourcefv)( ALuint sid, +// ALenum pname, ALfloat* values ); +// +// /** Activate a source, start replay. */ +// void (*alSourcePlay)( ALuint sid ); +// +// /** +// * Pause a source, +// * temporarily remove it from the mixer list. +// */ +// void (*alSourcePause)( ALuint sid ); +// +// /** +// * Stop a source, +// * temporarily remove it from the mixer list, +// * and reset its internal state to pre-Play. +// * To remove a Source completely, it has to be +// * deleted following Stop, or before Play. +// */ +// void (*alSourceStop)( ALuint sid ); +// +///** +// * BUFFER +// * Buffer objects are storage space for sample data. +// * Buffers are referred to by Sources. There can be more than +// * one Source using the same Buffer data. If Buffers have +// * to be duplicated on a per-Source basis, the driver has to +// * take care of allocation, copying, and deallocation as well +// * as propagating buffer data changes. +// */ +// +// /** Buffer object generation. */ +// void (*alGenBuffers)( ALsizei n, ALuint* samples ); +// void (*alDeleteBuffers)( ALsizei n, ALuint* samples ); +// ALboolean (*alIsBuffer)( ALuint buffer ); +// +// /** +// * Specify the data to be filled into a buffer. +// */ +// void (*alBufferData)( ALuint buffer, +// ALenum format, +// ALvoid* data, +// ALsizei size, +// ALsizei freq ); +// +// /** +// * Specify data to be filled into a looping buffer. +// * This takes the current position at the time of the +// * call, and returns the number of samples written. +// */ +// ALsizei (*alBufferAppendData)( ALuint buffer, +// ALenum format, +// ALvoid* data, +// ALsizei size, +// ALsizei freq ); +// void (*alGetBufferi)( ALuint buffer, +// ALenum param, ALint* value ); +// void (*alGetBufferf)( ALuint buffer, +// ALenum param, ALfloat* value ); +// +///** +// * EXTENSION: IASIG Level 2 Environment. +// * Environment object generation. +// * This is an EXTension that describes the Environment/Reverb +// * properties according to IASIG Level 2 specifications. +// */ +// /** +// * Allocate n environment ids and store them in the array environs. +// * Returns the number of environments actually allocated. +// */ +// ALsizei (*alGenEnvironmentIASIG)( ALsizei n, ALuint* environs ); +// void (*alDeleteEnvironmentIASIG)(ALsizei n, +// ALuint* environs); +// ALboolean (*alIsEnvironmentIASIG)( ALuint environ ); +// void (*alEnvironmentiIASIG)( ALuint eid, +// ALenum param, ALint value ); +// void (*alEnvironmentfIASIG)( ALuint eid, +// ALenum param, ALuint value ); +// +///** +// * Frequency Domain Filters are band filters. +// * Attenuation in Media (distance based) +// * Reflection Material +// * Occlusion Material (separating surface) +// * +// * Temporal Domain Filters: +// * Early Reflections +// * Late Reverb +// * +// */ +// +#endif /* AL_NO_PROTOTYPES */ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __al_h_ */ diff --git a/Engine/lib/openal/OpenBSD/AL/alc.h b/Engine/lib/openal/OpenBSD/AL/alc.h new file mode 100644 index 000000000..21b6e1666 --- /dev/null +++ b/Engine/lib/openal/OpenBSD/AL/alc.h @@ -0,0 +1,71 @@ +#ifndef ALC_CONTEXT_H_ +#define ALC_CONTEXT_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ALC_VERSION_0_1 1 + +#ifdef _WIN32 +#define ALAPI __declspec(dllexport) +#define ALAPIENTRY __cdecl +#else /* _WIN32 */ +#define ALAPI +#define ALAPIENTRY +#define AL_CALLBACK +#endif /* _WIN32 */ + +#ifndef AL_NO_PROTOTYPES + +ALAPI void * ALAPIENTRY alcCreateContext( ALint* attrlist ); + +/** + * There is no current context, as we can mix + * several active contexts. But al* calls + * only affect the current context. + */ +ALAPI ALCenum ALAPIENTRY alcMakeContextCurrent( ALvoid *alcHandle ); + +/** ??? */ +ALAPI void * ALAPIENTRY alcUpdateContext( ALvoid *alcHandle ); + +ALAPI ALCenum ALAPIENTRY alcDestroyContext( ALvoid *alcHandle ); + +ALAPI ALCenum ALAPIENTRY alcGetError( ALvoid ); + +ALAPI const ALubyte * ALAPIENTRY alcGetErrorString(ALenum param); + +ALAPI void * ALAPIENTRY alcGetCurrentContext( ALvoid ); + +#else +// +// void * (*alcCreateContext)( ALint* attrlist ); +// +// /** +// * There is no current context, as we can mix +// * several active contexts. But al* calls +// * only affect the current context. +// */ +// ALCenum (*alcMakeContextCurrent)( ALvoid *alcHandle ); +// +// /** ??? */ +// void * (*alcUpdateContext)( ALvoid *alcHandle ); +// +// ALCenum (*alcDestroyContext)( ALvoid *alcHandle ); +// +// ALCenum (*alcGetError) ( ALvoid ); +// +// const ALubyte *(*alcGetErrorString)(ALenum param); +// +// void * (*alcGetCurrentContext)( ALvoid ); +// +#endif /* AL_NO_PROTOTYPES */ + +#ifdef __cplusplus +} +#endif + +#endif /* ALC_CONTEXT_H_ */ diff --git a/Engine/lib/openal/OpenBSD/AL/alctypes.h b/Engine/lib/openal/OpenBSD/AL/alctypes.h new file mode 100644 index 000000000..5cade199b --- /dev/null +++ b/Engine/lib/openal/OpenBSD/AL/alctypes.h @@ -0,0 +1,30 @@ +#ifndef _ALCTYPES_H_ +#define _ALCTYPES_H_ + +typedef enum { + ALC_INVALID, + + ALC_FREQUENCY, /* followed by Hz */ + ALC_RESOLUTION, /* followed by bits */ + + ALC_BUFFERSIZE, /* followed by bytes */ + ALC_CHANNELS, /* followed by hardware channels */ + /* Angst: differentiate channels by categories */ + + ALC_REFRESH, /* followed by Hz */ + ALC_MIXAHEAD, /* followed by msec */ + + ALC_SOURCES, /* followed by ### of sources */ + ALC_BUFFERS, /* followed by ### of buffers */ + + ALC_CD, /* do we want to control the CD? */ + + ALC_SYNC, /* synchronous (need alcUpdateContext) */ + + /* errors */ + ALC_NO_ERROR, + ALC_INVALID_DEVICE, /* No device */ + ALC_INVALID_CONTEXT /* invalid context ID */ +} ALCenum; + +#endif /* _ALCTYPES_H */ diff --git a/Engine/lib/openal/OpenBSD/AL/altypes.h b/Engine/lib/openal/OpenBSD/AL/altypes.h new file mode 100644 index 000000000..45722994c --- /dev/null +++ b/Engine/lib/openal/OpenBSD/AL/altypes.h @@ -0,0 +1,344 @@ +#ifndef _AL_TYPES_H_ +#define _AL_TYPES_H_ + +/** OpenAL bool type. */ +typedef char ALboolean; + +/** OpenAL 8bit signed byte. */ +typedef signed char ALbyte; + +/** OpenAL 8bit unsigned byte. */ +typedef unsigned char ALubyte; + +/** OpenAL 16bit signed short integer type. */ +typedef short ALshort; + +/** OpenAL 16bit unsigned short integer type. */ +typedef unsigned short ALushort; + +/** OpenAL 32bit unsigned integer type. */ +typedef unsigned int ALuint; + +/** OpenAL 32bit signed integer type. */ +typedef int ALint; + +/** OpenAL 32bit floating point type. */ +typedef float ALfloat; + +/** OpenAL 64bit double point type. */ +typedef double ALdouble; + +/** OpenAL 32bit type. */ +typedef signed int ALsizei; + +/** OpenAL void type (for params, not returns). */ +typedef void ALvoid; + +/** OpenAL enumerations. */ +typedef int ALenum; + +/* Enumerant values begin at column 50. No tabs. */ + +/* bad value */ +#define AL_INVALID -1 + +/* Boolean False. */ +#define AL_FALSE 0 + +/** Boolean True. */ +#define AL_TRUE 1 + +/** + * Indicate the type of AL_SOURCE. + * Sources can be spatialized + */ +#define AL_SOURCE_TYPE 0x0200 + +/** Indicate Source has relative coordinates. */ +#define AL_SOURCE_RELATIVE 0x0202 + +/** + * Directional source, inner cone angle, in degrees. + * Range: [0-360] + * Default: 360 + */ +#define AL_CONE_INNER_ANGLE 0x1001 + +/** + * Directional source, outer cone angle, in degrees. + * Range: [0-360] + * Default: 360 + */ +#define AL_CONE_OUTER_ANGLE 0x1002 + +/** + * Specify the pitch to be applied, either at source, + * or on mixer results, at listener. + * Range: [0.5-2.0] + * Default: 1.0 + */ +#define AL_PITCH 0x1003 + +/** + * Specify the current location in three dimensional space. + * OpenAL, like OpenGL, uses a right handed coordinate system, + * where in a frontal default view X (thumb) points right, + * Y points up (index finger), and Z points towards the + * viewer/camera (middle finger). + * To switch from a left handed coordinate system, flip the + * sign on the Z coordinate. + * Listener position is always in the world coordinate system. + */ +#define AL_POSITION 0x1004 + +/** Specify the current direction. */ +#define AL_DIRECTION 0x1005 + +/** Specify the current velocity in three dimensional space. */ +#define AL_VELOCITY 0x1006 + +/** + * Indicate whether source is looping. + * Type: ALboolean? + * Range: [AL_TRUE, AL_FALSE] + * Default: FALSE. + */ +#define AL_SOURCE_LOOPING 0x1007 + +/** + * Indicate whether source is meant to be streaming. + * Type: ALboolean? + * Range: [AL_TRUE, AL_FALSE] + * Default: FALSE. + */ +#define AL_STREAMING 0x1008 + +/** + * Indicate the buffer to provide sound samples. + * Type: ALuint. + * Range: any valid Buffer id. + */ +#define AL_BUFFER 0x1009 + +/** + * Indicate the gain (volume amplification) applied. + * Type: ALfloat. + * Range: ]0.0- ] + * A value of 1.0 means un-attenuated/unchanged. + * Each division by 2 equals an attenuation of -6dB. + * Each multiplicaton with 2 equals an amplification of +6dB. + * A value of 0.0 is meaningless with respect to a logarithmic + * scale; it is interpreted as zero volume - the channel + * is effectively disabled. + */ +#define AL_GAIN 0x100A + +/** + * Indicate the gain (volume amplification) applied, in a + * normalized linear scale. This affects the value retrieved + * by AL_GAIN. + * + * Type: ALfloat. + * Range: ]0.0- ] + * A value of 1.0 means un-attenuated/unchanged. + * A value of 0.0 is interpreted as zero volume - the channel + * is effectively disabled. + */ +#define AL_GAIN_LINEAR 0x100B + +/* byte offset into source (in canon format). -1 if source + * is not playing. Don't set this, get this. + * + * Type: ALint + * Range: -1 - +inf + */ +#define AL_BYTE_LOKI 0x100C + +/* + * Indicate minimum source attenuation + * Type: ALfloat + * Range: [0.0 - 1.0] + * + */ +#define AL_SOURCE_ATTENUATION_MIN 0x100D + +/* + * Indicate maximum source attenuation + * Type: ALfloat + * Range: [0.0 - 1.0] + * + */ +#define AL_SOURCE_ATTENUATION_MAX 0x100E + +/* + * Indicate listener orientation. + * + * at/up + */ +#define AL_ORIENTATION 0x100F + + +/* + * Source state information. + */ +#define AL_SOURCE_STATE 0x1010 +#define AL_INITIAL 0x1011 +#define AL_PLAYING 0x1012 +#define AL_PAUSED 0x1013 +#define AL_STOPPED 0x1014 + + +/** Sound samples: format specifier. */ +#define AL_FORMAT_MONO8 0x1100 +#define AL_FORMAT_MONO16 0x1101 +#define AL_FORMAT_STEREO8 0x1102 +#define AL_FORMAT_STEREO16 0x1103 + + +/** + * Sound samples: frequency, in units of Hertz [Hz]. + * This is the number of samples per second. Half of the + * sample frequency marks the maximum significant + * frequency component. + */ +#define AL_FREQUENCY 0x2001 +#define AL_BITS 0x2002 +#define AL_CHANNELS 0x2003 +#define AL_SIZE 0x2004 + + +/** Errors: No Error. */ +#define AL_NO_ERROR AL_FALSE + +/** + * Invalid Name paramater passed to AL call. + */ +#define AL_INVALID_NAME 0xA001 + +/** + * Invalid parameter passed to AL call. + */ +#define AL_ILLEGAL_ENUM 0xA002 + +/** + * Invalid enum parameter value. + */ +#define AL_INVALID_VALUE 0xA003 + +/** + * Illegal call. + */ +#define AL_ILLEGAL_COMMAND 0xA004 + +/** + * No mojo. + */ +#define AL_OUT_OF_MEMORY 0xA005 + + +/** Context strings: Vendor Name. */ +#define AL_VENDOR 0xB001 +#define AL_VERSION 0xB002 +#define AL_RENDERER 0xB003 +#define AL_EXTENSIONS 0xB004 + + +/** IASIG Level 2 Environment. */ + +/** + * Parameter: IASIG ROOM blah + * Type: intgeger + * Range: [-10000, 0] + * Default: -10000 + */ +#define AL_ENV_ROOM_IASIG 0x3001 + +/** + * Parameter: IASIG ROOM_HIGH_FREQUENCY + * Type: integer + * Range: [-10000, 0] + * Default: 0 + */ +#define AL_ENV_ROOM_HIGH_FREQUENCY_IASIG 0x3002 + +/** + * Parameter: IASIG ROOM_ROLLOFF_FACTOR + * Type: float + * Range: [0.0, 10.0] + * Default: 0.0 + */ +#define AL_ENV_ROOM_ROLLOFF_FACTOR_IASIG 0x3003 + +/** + * Parameter: IASIG DECAY_TIME + * Type: float + * Range: [0.1, 20.0] + * Default: 1.0 + */ +#define AL_ENV_DECAY_TIME_IASIG 0x3004 + +/** + * Parameter: IASIG DECAY_HIGH_FREQUENCY_RATIO + * Type: float + * Range: [0.1, 2.0] + * Default: 0.5 + */ +#define AL_ENV_DECAY_HIGH_FREQUENCY_RATIO_IASIG 0x3005 + +/** + * Parameter: IASIG REFLECTIONS + * Type: integer + * Range: [-10000, 1000] + * Default: -10000 + */ +#define AL_ENV_REFLECTIONS_IASIG 0x3006 + +/** + * Parameter: IASIG REFLECTIONS_DELAY + * Type: float + * Range: [0.0, 0.3] + * Default: 0.02 + */ +#define AL_ENV_REFLECTIONS_DELAY_IASIG 0x3006 + +/** + * Parameter: IASIG REVERB + * Type: integer + * Range: [-10000,2000] + * Default: -10000 + */ +#define AL_ENV_REVERB_IASIG 0x3007 + +/** + * Parameter: IASIG REVERB_DELAY + * Type: float + * Range: [0.0, 0.1] + * Default: 0.04 + */ +#define AL_ENV_REVERB_DELAY_IASIG 0x3008 + +/** + * Parameter: IASIG DIFFUSION + * Type: float + * Range: [0.0, 100.0] + * Default: 100.0 + */ +#define AL_ENV_DIFFUSION_IASIG 0x3009 + +/** + * Parameter: IASIG DENSITY + * Type: float + * Range: [0.0, 100.0] + * Default: 100.0 + */ +#define AL_ENV_DENSITY_IASIG 0x300A + + /** + * Parameter: IASIG HIGH_FREQUENCY_REFERENCE + * Type: float + * Range: [20.0, 20000.0] + * Default: 5000.0 + */ +#define AL_ENV_HIGH_FREQUENCY_REFERENCE_IASIG 0x300B + +#endif diff --git a/Engine/lib/openal/OpenBSD/AL/alu.h b/Engine/lib/openal/OpenBSD/AL/alu.h new file mode 100644 index 000000000..169d5fb03 --- /dev/null +++ b/Engine/lib/openal/OpenBSD/AL/alu.h @@ -0,0 +1,37 @@ +#ifndef __alu_h_ +#define __alu_h_ + +#ifdef _WIN32 +#define ALAPI __declspec(dllexport) +#define ALAPIENTRY __cdecl +#else /* _WIN32 */ +#define ALAPI +#define ALAPIENTRY +#define AL_CALLBACK +#endif /* _WIN32 */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef AL_NO_PROTOTYPES + + + +#else + + + + + +#endif /* AL_NO_PROTOTYPES */ + +#ifdef __cplusplus +} +#endif + +#endif /* __alu_h_ */ + diff --git a/Engine/lib/openal/OpenBSD/AL/alut.h b/Engine/lib/openal/OpenBSD/AL/alut.h new file mode 100644 index 000000000..b43789030 --- /dev/null +++ b/Engine/lib/openal/OpenBSD/AL/alut.h @@ -0,0 +1,53 @@ +#ifndef _ALUT_H_ +#define _ALUT_H_ + +#include +#include + +#ifdef _WIN32 +#define ALAPI __declspec(dllexport) +#define ALAPIENTRY __cdecl +#define AL_CALLBACK +#else /* _WIN32 */ +#define ALAPI +#define ALAPIENTRY +#define AL_CALLBACK +#endif /* _WIN32 */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef AL_NO_PROTOTYPES + +ALAPI void ALAPIENTRY alutInit(int *argc, char *argv[]); +ALAPI void ALAPIENTRY alutExit(ALvoid); + +ALAPI ALboolean ALAPIENTRY alutLoadWAV( const char *fname, + ALvoid **wave, + ALsizei *format, + ALsizei *size, + ALsizei *bits, + ALsizei *freq ); + +#else +// +// void (*alutInit)(int *argc, char *argv[]); +// void (*alutExit)(ALvoid); +// +// ALboolean (*alutLoadWAV)( const char *fname, +// ALvoid **wave, +// ALsizei *format, +// ALsizei *size, +// ALsizei *bits, +// ALsizei *freq ); +// +// +#endif /* AL_NO_PROTOTYPES */ + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Engine/lib/openal/OpenBSD/AL/aluttypes.h b/Engine/lib/openal/OpenBSD/AL/aluttypes.h new file mode 100644 index 000000000..252f622f6 --- /dev/null +++ b/Engine/lib/openal/OpenBSD/AL/aluttypes.h @@ -0,0 +1,5 @@ +#ifndef _ALUTTYPES_H_ +#define _ALUTTYPES_H_ + + +#endif /* _ALUTTYPES_H_ */ diff --git a/Engine/lib/openal/OpenBSD/AL/alutypes.h b/Engine/lib/openal/OpenBSD/AL/alutypes.h new file mode 100644 index 000000000..82356c671 --- /dev/null +++ b/Engine/lib/openal/OpenBSD/AL/alutypes.h @@ -0,0 +1,5 @@ +#ifndef _ALUTYPES_H_ +#define _ALUTYPES_H_ + + +#endif /* _ALUTYPES_H_ */ diff --git a/Engine/lib/openal/OpenBSD/openALFn.h b/Engine/lib/openal/OpenBSD/openALFn.h new file mode 100644 index 000000000..0f0b00d48 --- /dev/null +++ b/Engine/lib/openal/OpenBSD/openALFn.h @@ -0,0 +1,101 @@ +#ifndef AL_FUNCTION +#define AL_FUNCTION(fn_return, fn_name, fn_args); +#endif + +#ifndef AL_EXTENSION +#define AL_EXTENSION(ext_name) +#endif + +#ifndef AL_EXT_FUNCTION +#define AL_EXT_FUNCTION(ext_name, fn_return, fn_name, fn_args) +#endif + +// AL functions +AL_FUNCTION(ALvoid, alEnable, ( ALenum capability )) +AL_FUNCTION(ALvoid, alDisable, ( ALenum capability )) +AL_FUNCTION(ALboolean, alIsEnabled, ( ALenum capability )) +AL_FUNCTION(ALvoid, alHint, ( ALenum target, ALenum mode )) +AL_FUNCTION(ALboolean, alGetBoolean, ( ALenum param )) +AL_FUNCTION(ALint, alGetInteger, ( ALenum param )) +AL_FUNCTION(ALfloat, alGetFloat, ( ALenum param )) +AL_FUNCTION(ALdouble, alGetDouble, ( ALenum param )) +AL_FUNCTION(ALvoid, alGetBooleanv, ( ALenum param, ALboolean* data )) +AL_FUNCTION(ALvoid, alGetIntegerv, ( ALenum param, ALint* data )) +AL_FUNCTION(ALvoid, alGetFloatv, ( ALenum param, ALfloat* data )) +AL_FUNCTION(ALvoid, alGetDoublev, ( ALenum param, ALdouble* data )) +AL_FUNCTION(const ALubyte*, alGetString, ( ALenum param )) +AL_FUNCTION(ALenum, alGetError, ( ALvoid )) +AL_FUNCTION(ALboolean, alIsExtensionPresent, ( const ALubyte* fname )) +AL_FUNCTION(ALvoid*, alGetProcAddress, ( const ALubyte* fname )) +AL_FUNCTION(ALenum, alGetEnumValue, ( const ALubyte* ename )) +AL_FUNCTION(ALvoid, alListenerf, ( ALenum pname, ALfloat param )) +AL_FUNCTION(ALvoid, alListener3f, ( ALenum pname, ALfloat param1, ALfloat param2, ALfloat param3 )) +AL_FUNCTION(ALvoid, alListenerfv, ( ALenum pname, ALfloat* param )) +AL_FUNCTION(ALvoid, alGetListeneri, ( ALenum pname, ALint* value )) +AL_FUNCTION(ALvoid, alGetListenerf, ( ALenum pname, ALfloat* values )) +AL_FUNCTION(ALvoid, alGetListenerfv, ( ALenum pname, ALfloat* values )) +AL_FUNCTION(ALvoid, alGenSources, ( ALsizei n, ALuint* sources )) +AL_FUNCTION(ALvoid, alDeleteSources, ( ALsizei n, ALuint* sources )) +AL_FUNCTION(ALboolean, alIsSource, ( ALuint sid )) +AL_FUNCTION(ALvoid, alSourcei, ( ALuint sid, ALenum param, ALint value )) +AL_FUNCTION(ALvoid, alSourcef, ( ALuint sid, ALenum param, ALfloat value )) +AL_FUNCTION(ALvoid, alSource3f, ( ALuint sid, ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 )) +AL_FUNCTION(ALvoid, alSourcefv, ( ALuint sid, ALenum param, ALfloat* values )) +AL_FUNCTION(ALvoid, alGetSourcei, ( ALuint sid, ALenum pname, ALint* value )) +AL_FUNCTION(ALvoid, alGetSourcef, ( ALuint sid, ALenum pname, ALfloat* value )) +AL_FUNCTION(ALvoid, alGetSourcefv, ( ALuint sid, ALenum pname, ALfloat* values )) +AL_FUNCTION(ALvoid, alSourcePlayv, ( ALuint ns, ALuint* ids )) +AL_FUNCTION(ALvoid, alSourceStopv, ( ALuint ns, ALuint* ids )) +AL_FUNCTION(ALvoid, alSourcePlay, ( ALuint sid )) +AL_FUNCTION(ALvoid, alSourcePause, ( ALuint sid )) +AL_FUNCTION(ALvoid, alSourceStop, ( ALuint sid )) +AL_FUNCTION(ALvoid, alGenBuffers, ( ALsizei n, ALuint* samples )) +AL_FUNCTION(ALvoid, alDeleteBuffers, ( ALsizei n, ALuint* samples )) +AL_FUNCTION(ALboolean, alIsBuffer, ( ALuint buffer )) +AL_FUNCTION(ALvoid, alBufferData, ( ALuint buffer, ALenum format, ALvoid* data, ALsizei size, ALsizei freq )) +AL_FUNCTION(ALsizei, alBufferAppendData, ( ALuint buffer, ALenum format, ALvoid* data, ALsizei size, ALsizei freq )) +AL_FUNCTION(ALvoid, alGetBufferi, ( ALuint buffer, ALenum param, ALint* value )) +AL_FUNCTION(ALvoid, alGetBufferf, ( ALuint buffer, ALenum param, ALfloat* value )) + +// ALC functions +AL_FUNCTION(ALvoid*, alcCreateContext, ( ALint* attrlist )) +AL_FUNCTION(ALCenum, alcMakeContextCurrent, ( ALvoid* context )) +AL_FUNCTION(ALvoid*, alcUpdateContext, ( ALvoid* context )) +AL_FUNCTION(ALCenum, alcDestroyContext, ( ALvoid* context )) +AL_FUNCTION(ALCenum, alcGetError, ( ALvoid )) +AL_FUNCTION(const ALubyte *, alcGetErrorString, ( ALvoid )) +AL_FUNCTION(ALvoid*, alcGetCurrentContext, ( ALvoid )) + +// ALUT functions +AL_FUNCTION(void, alutInit, ( int* argc, char** argv )) +AL_FUNCTION(void, alutExit, ( ALvoid )) +AL_FUNCTION(ALboolean, alutLoadWAV, ( const char* fname, ALvoid** data, ALsizei* format, ALsizei* size, ALsizei* bits, ALsizei* freq )) + +// Extensions +AL_EXTENSION(AL_EXT_IASIG) +AL_EXT_FUNCTION(AL_EXT_IASIG, ALvoid, alGenEnvironmentIASIG, ( ALsizei n, ALuint* environs )) +AL_EXT_FUNCTION(AL_EXT_IASIG, ALvoid, alDeleteEnvironmentIASIG, ( ALsizei n, ALuint* environs )) +AL_EXT_FUNCTION(AL_EXT_IASIG, ALboolean, alIsEnvironmentIASIG, ( ALuint environment )) +AL_EXT_FUNCTION(AL_EXT_IASIG, ALvoid, alEnvironmentiIASIG, ( ALuint eid, ALenum param, ALint value )) + +AL_EXTENSION(AL_EXT_DYNAMIX) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alBufferi_EXT, ( ALuint buffer, ALenum pname, ALint value )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alBufferSyncData_EXT, ( ALuint buffer, ALenum format, ALvoid* data, ALsizei size, ALsizei freq )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alBufferStreamFile_EXT, ( ALuint buffer, const ALubyte* filename )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alSourceCallback_EXT, ( ALuint source, ALvoid* callback )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALvoid, alSourceResetEnvironment_EXT, ( ALuint source )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alContexti_EXT, ( ALenum pname, ALint value )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alGetContexti_EXT, ( ALenum pname, ALint* value )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alGetContextstr_EXT, ( ALenum pname, ALuint idx, ALubyte** value )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alCaptureInit_EXT, ( ALenum format, ALuint rate, ALsizei bufferSize )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alCaptureDestroy_EXT, ( ALvoid )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alCaptureStart_EXT, ( ALvoid )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alCaptureStop_EXT, ( ALvoid )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALsizei, alCaptureGetData_EXT, ( ALvoid* data, ALsizei n, ALenum format, ALuint rate )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALvoid, alEnvironmentfIASIG, ( ALuint eid, ALenum param, ALfloat value )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALvoid, alGetEnvironmentiIASIG_EXT, ( ALuint eid, ALenum param, ALint * value )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALvoid, alGetEnvironmentfIASIG_EXT, ( ALuint eid, ALenum param, ALfloat * value )) + +#undef AL_EXTENSION +#undef AL_FUNCTION +#undef AL_EXT_FUNCTION diff --git a/Engine/lib/openal/X86UNIX/al/al.h b/Engine/lib/openal/X86UNIX/al/al.h new file mode 100644 index 000000000..6bbc1bb9e --- /dev/null +++ b/Engine/lib/openal/X86UNIX/al/al.h @@ -0,0 +1,523 @@ +#ifndef __al_h_ +#define __al_h_ + +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2000 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 +#define ALAPI __declspec(dllexport) +#define ALAPIENTRY __cdecl +#define AL_CALLBACK +#else /* _WIN32 */ +#define ALAPI +#define ALAPIENTRY +#define AL_CALLBACK +#endif /* _WIN32 */ + +#ifndef AL_NO_PROTOTYPES + +/** + * OpenAL Maintenance Functions + * State Management and Query. + * Error Handling. + * Extension Support. + */ + + +/** Renderer State management. */ +ALAPI void ALAPIENTRY alEnable( ALenum capability ); + +ALAPI void ALAPIENTRY alDisable( ALenum capability ); + +ALAPI ALboolean ALAPIENTRY alIsEnabled( ALenum capability ); + +/** Application preferences for driver performance choices. */ +ALAPI void ALAPIENTRY alHint( ALenum target, ALenum mode ); + +/** State retrieval. */ +ALAPI ALboolean ALAPIENTRY alGetBoolean( ALenum param ); + +/** State retrieval. */ +ALAPI ALint ALAPIENTRY alGetInteger( ALenum param ); + +/** State retrieval. */ +ALAPI ALfloat ALAPIENTRY alGetFloat( ALenum param ); + +/** State retrieval. */ +ALAPI ALdouble ALAPIENTRY alGetDouble( ALenum param ); + +/** State retrieval. */ +ALAPI void ALAPIENTRY alGetBooleanv( ALenum param, ALboolean* data ); + +/** State retrieval. */ +ALAPI void ALAPIENTRY alGetIntegerv( ALenum param, ALint* data ); + +/** State retrieval. */ +ALAPI void ALAPIENTRY alGetFloatv( ALenum param, ALfloat* data ); + +/** State retrieval. */ +ALAPI void ALAPIENTRY alGetDoublev( ALenum param, ALdouble* data ); + +/** State retrieval. */ +ALAPI const ALubyte* ALAPIENTRY alGetString( ALenum param ); + + +/** + * Error support. + * Obtain the most recent error generated in the AL state machine. + */ +ALAPI ALenum ALAPIENTRY alGetError( ALvoid ); + +/** + * Extension support. + * Obtain the address of a function (usually an extension) + * with the name fname. All addresses are context-independent. + */ +ALAPI ALboolean ALAPIENTRY alIsExtensionPresent( const ALubyte* fname ); + + +/** + * Extension support. + * Obtain the address of a function (usually an extension) + * with the name fname. All addresses are context-independent. + */ +ALAPI void* ALAPIENTRY alGetProcAddress( const ALubyte* fname ); + + +/** + * Extension support. + * Obtain the integer value of an enumeration (usually an extension) with the name ename. + */ +ALAPI ALenum ALAPIENTRY alGetEnumValue( const ALubyte* ename ); + + + + + + +/** + * LISTENER + * Listener is the sample position for a given context. + * The multi-channel (usually stereo) output stream generated + * by the mixer is parametrized by this Listener object: + * its position and velocity relative to Sources, within + * occluder and reflector geometry. + */ + + + +/** + * + * Listener Gain: default 1.0f. + */ +ALAPI void ALAPIENTRY alListenerf( ALenum pname, ALfloat param ); + +/** + * + * Listener Position. + * Listener Velocity. + */ +ALAPI void ALAPIENTRY alListener3f( ALenum pname, ALfloat param1, + ALfloat param2, + ALfloat param3 ); + +/** + * + * Listener Position: ALfloat[3] + * Listener Velocity: ALfloat[3] + * Listener Orientation: ALfloat[6] (forward and up vector). + */ +ALAPI void ALAPIENTRY alListenerfv( ALenum pname, ALfloat* param ); + +/* + * Retrieve listener information. + */ +ALAPI void ALAPIENTRY alGetListeneri( ALenum pname, ALint* value ); +ALAPI void ALAPIENTRY alGetListenerf( ALenum pname, ALfloat* values ); +ALAPI void ALAPIENTRY alGetListenerfv( ALenum pname, ALfloat* values ); + +/** + * SOURCE + * Source objects are by default localized. Sources + * take the PCM data provided in the specified Buffer, + * apply Source-specific modifications, and then + * submit them to be mixed according to spatial + * arrangement etc. + */ + + + +/** Create Source objects. */ +ALAPI void ALAPIENTRY alGenSources( ALsizei n, ALuint* sources ); + +/** Delete Source objects. */ +ALAPI void ALAPIENTRY alDeleteSources( ALsizei n, ALuint* sources ); + +/** Verify a handle is a valid Source. */ +ALAPI ALboolean ALAPIENTRY alIsSource( ALuint sid ); + + +/** Set an integer parameter for a Source object. */ +ALAPI void ALAPIENTRY alSourcei( ALuint sid, ALenum param, ALint value ); +ALAPI void ALAPIENTRY alSourcef( ALuint sid, ALenum param, ALfloat value ); +ALAPI void ALAPIENTRY alSource3f( ALuint sid, ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 ); +ALAPI void ALAPIENTRY alSourcefv( ALuint sid, ALenum param, ALfloat* values ); + +/** Get an integer parameter for a Source object. */ +ALAPI void ALAPIENTRY alGetSourcei( ALuint sid, ALenum pname, ALint* value ); +ALAPI void ALAPIENTRY alGetSourcef( ALuint sid, ALenum pname, ALfloat* value ); +ALAPI void ALAPIENTRY alGetSourcefv( ALuint sid, ALenum pname, ALfloat* values ); + +ALAPI void ALAPIENTRY alSourcePlayv( ALuint ns, ALuint *ids ); +ALAPI void ALAPIENTRY alSourceStopv( ALuint ns, ALuint *ids ); + +/** Activate a source, start replay. */ +ALAPI void ALAPIENTRY alSourcePlay( ALuint sid ); + +/** + * Pause a source, + * temporarily remove it from the mixer list. + */ +ALAPI void ALAPIENTRY alSourcePause( ALuint sid ); + +/** + * Stop a source, + * temporarily remove it from the mixer list, + * and reset its internal state to pre-Play. + * To remove a Source completely, it has to be + * deleted following Stop, or before Play. + */ +ALAPI void ALAPIENTRY alSourceStop( ALuint sid ); + + + + +/** + * BUFFER + * Buffer objects are storage space for sample data. + * Buffers are referred to by Sources. There can be more than + * one Source using the same Buffer data. If Buffers have + * to be duplicated on a per-Source basis, the driver has to + * take care of allocation, copying, and deallocation as well + * as propagating buffer data changes. + */ + + + + +/** Buffer object generation. */ +ALAPI void ALAPIENTRY alGenBuffers( ALsizei n, ALuint* samples ); + +ALAPI void ALAPIENTRY alDeleteBuffers( ALsizei n, ALuint* samples ); + + +ALAPI ALboolean ALAPIENTRY alIsBuffer( ALuint buffer ); + +/** + * Specify the data to be filled into a buffer. + */ +ALAPI void ALAPIENTRY alBufferData( ALuint buffer, + ALenum format, + ALvoid* data, + ALsizei size, + ALsizei freq ); + + +/** + * Specify data to be filled into a looping buffer. + * This takes the current position at the time of the + * call, and returns the number of samples written. + */ +ALsizei ALAPIENTRY alBufferAppendData( ALuint buffer, + ALenum format, + ALvoid* data, + ALsizei size, + ALsizei freq ); + + + + +ALAPI void ALAPIENTRY alGetBufferi( ALuint buffer, ALenum param, ALint* value ); +ALAPI void ALAPIENTRY alGetBufferf( ALuint buffer, ALenum param, ALfloat* value ); + + + + + +/** + * Frequency Domain Filters are band filters. + * Attenuation in Media (distance based) + * Reflection Material + * Occlusion Material (separating surface) + * + * Temporal Domain Filters: + * Early Reflections + * Late Reverb + * + */ + + + + +/** + * EXTENSION: IASIG Level 2 Environment. + * Environment object generation. + * This is an EXTension that describes the Environment/Reverb + * properties according to IASIG Level 2 specifications. + */ + + + + + +/** + * Allocate n environment ids and store them in the array environs. + * Returns the number of environments actually allocated. + */ +ALAPI ALsizei ALAPIENTRY alGenEnvironmentIASIG( ALsizei n, ALuint* environs ); + +ALAPI void ALAPIENTRY alDeleteEnvironmentIASIG( ALsizei n, ALuint* environs ); + +ALAPI ALboolean ALAPIENTRY alIsEnvironmentIASIG( ALuint environ ); + +ALAPI void ALAPIENTRY alEnvironmentiIASIG( ALuint eid, ALenum param, ALint value ); + +ALAPI void ALAPIENTRY alEnvironmentfIASIG( ALuint eid, ALenum param, ALfloat value ); + + + + +#else /* AL_NO_PROTOTYPES */ +// +// +///** OpenAL Maintenance Functions */ +// +// void (*alEnable)( ALenum capability ); +// void (*alDisable)( ALenum capability ); +// ALboolean (*alIsEnabled)( ALenum capability ); +// void (*alHint)( ALenum target, ALenum mode ); +// ALboolean (*alGetBoolean)( ALenum param ); +// ALint (*alGetInteger)( ALenum param ); +// ALfloat (*alGetFloat)( ALenum param ); +// ALdouble (*alGetDouble)( ALenum param ); +// void (*alGetBooleanv)( ALenum param, +// ALboolean* data ); +// void (*alGetIntegerv)( ALenum param, +// ALint* data ); +// void (*alGetFloatv)( ALenum param, +// ALfloat* data ); +// void (*alGetDoublev)( ALenum param, +// ALdouble* data ); +// const ALubyte* (*GetString)( ALenum param ); +// ALenum (*alGetError)( ALvoid ); +// +// /** +// * Extension support. +// * Query existance of extension +// */ +// ALboolean (*alIsExtensionPresent)(const ALubyte* fname ); +// +// /** +// * Extension support. +// * Obtain the address of a function (usually an extension) +// * with the name fname. All addresses are context-independent. +// */ +// void* (*alGetProcAddress)( const ALubyte* fname ); +// +// +// /** +// * Extension support. +// * Obtain the integer value of an enumeration (usually an extension) with the name ename. +// */ +// ALenum (*alGetEnumValue)( const ALubyte* ename ); +// +///** +// * LISTENER +// * Listener is the sample position for a given context. +// * The multi-channel (usually stereo) output stream generated +// * by the mixer is parametrized by this Listener object: +// * its position and velocity relative to Sources, within +// * occluder and reflector geometry. +// */ +// /** +// * +// * Listener Gain: default 1.0f. +// */ +// void (*alListenerf)( ALenum pname, ALfloat param ); +// +// /** +// * +// * Listener Position. +// * Listener Velocity. +// */ +// void (*alListener3f)( ALenum pname, +// ALfloat param1, ALfloat param2, ALfloat param3); +// +// /** +// * +// * Listener Position: ALfloat[3] +// * Listener Velocity: ALfloat[3] +// * Listener Orientation: ALfloat[6] (forward and up vector). +// */ +// void (*alListenerfv)( ALenum pname, ALfloat* param ); +// +///** +// * SOURCE +// * Source objects are by default localized. Sources +// * take the PCM data provided in the specified Buffer, +// * apply Source-specific modifications, and then +// * submit them to be mixed according to spatial +// * arrangement etc. +// */ +// +// /** Create Source objects. */ +// void (*alGenSources)( ALsizei n, ALuint* sources ); +// +// /** Delete Source objects. */ +// void (*alDeleteSources)( ALsizei n, ALuint* sources ); +// +// /** Verify a handle is a valid Source. */ +// ALboolean (*alIsSource)( ALuint sid ); +// +// /** Set an integer parameter for a Source object. */ +// void (*alSourcei)( ALuint sid, ALenum param, ALint value); +// +// /** Set a float parameter for a Source object. */ +// void (*alSourcef)( ALuint sid, ALenum param, ALfloat value); +// +// /** Set a 3-float parameter for a Source object. */ +// void (*alSource3f)( ALuint sid, ALenum param, +// ALfloat v1, ALfloat v2, ALfloat v3 ); +// +// /** Set a float vector parameter for a Source object. */ +// void (*alSourcefv)( ALuint sid, ALenum param, +// ALfloat* values ); +// +// /** Get an integer parameter for a Source object. */ +// void (*alGetSourcei)( ALuint sid, +// ALenum pname, ALint* value ); +// /** Get a float parameter for a Source object. */ +// void (*alGetSourcef)( ALuint sid, +// ALenum pname, ALfloat* value ); +// /** Get a float vector parameter for a Source object. */ +// void (*alGetSourcefv)( ALuint sid, +// ALenum pname, ALfloat* values ); +// +// /** Activate a source, start replay. */ +// void (*alSourcePlay)( ALuint sid ); +// +// /** +// * Pause a source, +// * temporarily remove it from the mixer list. +// */ +// void (*alSourcePause)( ALuint sid ); +// +// /** +// * Stop a source, +// * temporarily remove it from the mixer list, +// * and reset its internal state to pre-Play. +// * To remove a Source completely, it has to be +// * deleted following Stop, or before Play. +// */ +// void (*alSourceStop)( ALuint sid ); +// +///** +// * BUFFER +// * Buffer objects are storage space for sample data. +// * Buffers are referred to by Sources. There can be more than +// * one Source using the same Buffer data. If Buffers have +// * to be duplicated on a per-Source basis, the driver has to +// * take care of allocation, copying, and deallocation as well +// * as propagating buffer data changes. +// */ +// +// /** Buffer object generation. */ +// void (*alGenBuffers)( ALsizei n, ALuint* samples ); +// void (*alDeleteBuffers)( ALsizei n, ALuint* samples ); +// ALboolean (*alIsBuffer)( ALuint buffer ); +// +// /** +// * Specify the data to be filled into a buffer. +// */ +// void (*alBufferData)( ALuint buffer, +// ALenum format, +// ALvoid* data, +// ALsizei size, +// ALsizei freq ); +// +// /** +// * Specify data to be filled into a looping buffer. +// * This takes the current position at the time of the +// * call, and returns the number of samples written. +// */ +// ALsizei (*alBufferAppendData)( ALuint buffer, +// ALenum format, +// ALvoid* data, +// ALsizei size, +// ALsizei freq ); +// void (*alGetBufferi)( ALuint buffer, +// ALenum param, ALint* value ); +// void (*alGetBufferf)( ALuint buffer, +// ALenum param, ALfloat* value ); +// +///** +// * EXTENSION: IASIG Level 2 Environment. +// * Environment object generation. +// * This is an EXTension that describes the Environment/Reverb +// * properties according to IASIG Level 2 specifications. +// */ +// /** +// * Allocate n environment ids and store them in the array environs. +// * Returns the number of environments actually allocated. +// */ +// ALsizei (*alGenEnvironmentIASIG)( ALsizei n, ALuint* environs ); +// void (*alDeleteEnvironmentIASIG)(ALsizei n, +// ALuint* environs); +// ALboolean (*alIsEnvironmentIASIG)( ALuint environ ); +// void (*alEnvironmentiIASIG)( ALuint eid, +// ALenum param, ALint value ); +// void (*alEnvironmentfIASIG)( ALuint eid, +// ALenum param, ALuint value ); +// +///** +// * Frequency Domain Filters are band filters. +// * Attenuation in Media (distance based) +// * Reflection Material +// * Occlusion Material (separating surface) +// * +// * Temporal Domain Filters: +// * Early Reflections +// * Late Reverb +// * +// */ +// +#endif /* AL_NO_PROTOTYPES */ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __al_h_ */ diff --git a/Engine/lib/openal/X86UNIX/al/alc.h b/Engine/lib/openal/X86UNIX/al/alc.h new file mode 100644 index 000000000..21b6e1666 --- /dev/null +++ b/Engine/lib/openal/X86UNIX/al/alc.h @@ -0,0 +1,71 @@ +#ifndef ALC_CONTEXT_H_ +#define ALC_CONTEXT_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ALC_VERSION_0_1 1 + +#ifdef _WIN32 +#define ALAPI __declspec(dllexport) +#define ALAPIENTRY __cdecl +#else /* _WIN32 */ +#define ALAPI +#define ALAPIENTRY +#define AL_CALLBACK +#endif /* _WIN32 */ + +#ifndef AL_NO_PROTOTYPES + +ALAPI void * ALAPIENTRY alcCreateContext( ALint* attrlist ); + +/** + * There is no current context, as we can mix + * several active contexts. But al* calls + * only affect the current context. + */ +ALAPI ALCenum ALAPIENTRY alcMakeContextCurrent( ALvoid *alcHandle ); + +/** ??? */ +ALAPI void * ALAPIENTRY alcUpdateContext( ALvoid *alcHandle ); + +ALAPI ALCenum ALAPIENTRY alcDestroyContext( ALvoid *alcHandle ); + +ALAPI ALCenum ALAPIENTRY alcGetError( ALvoid ); + +ALAPI const ALubyte * ALAPIENTRY alcGetErrorString(ALenum param); + +ALAPI void * ALAPIENTRY alcGetCurrentContext( ALvoid ); + +#else +// +// void * (*alcCreateContext)( ALint* attrlist ); +// +// /** +// * There is no current context, as we can mix +// * several active contexts. But al* calls +// * only affect the current context. +// */ +// ALCenum (*alcMakeContextCurrent)( ALvoid *alcHandle ); +// +// /** ??? */ +// void * (*alcUpdateContext)( ALvoid *alcHandle ); +// +// ALCenum (*alcDestroyContext)( ALvoid *alcHandle ); +// +// ALCenum (*alcGetError) ( ALvoid ); +// +// const ALubyte *(*alcGetErrorString)(ALenum param); +// +// void * (*alcGetCurrentContext)( ALvoid ); +// +#endif /* AL_NO_PROTOTYPES */ + +#ifdef __cplusplus +} +#endif + +#endif /* ALC_CONTEXT_H_ */ diff --git a/Engine/lib/openal/X86UNIX/al/alctypes.h b/Engine/lib/openal/X86UNIX/al/alctypes.h new file mode 100644 index 000000000..5cade199b --- /dev/null +++ b/Engine/lib/openal/X86UNIX/al/alctypes.h @@ -0,0 +1,30 @@ +#ifndef _ALCTYPES_H_ +#define _ALCTYPES_H_ + +typedef enum { + ALC_INVALID, + + ALC_FREQUENCY, /* followed by Hz */ + ALC_RESOLUTION, /* followed by bits */ + + ALC_BUFFERSIZE, /* followed by bytes */ + ALC_CHANNELS, /* followed by hardware channels */ + /* Angst: differentiate channels by categories */ + + ALC_REFRESH, /* followed by Hz */ + ALC_MIXAHEAD, /* followed by msec */ + + ALC_SOURCES, /* followed by ### of sources */ + ALC_BUFFERS, /* followed by ### of buffers */ + + ALC_CD, /* do we want to control the CD? */ + + ALC_SYNC, /* synchronous (need alcUpdateContext) */ + + /* errors */ + ALC_NO_ERROR, + ALC_INVALID_DEVICE, /* No device */ + ALC_INVALID_CONTEXT /* invalid context ID */ +} ALCenum; + +#endif /* _ALCTYPES_H */ diff --git a/Engine/lib/openal/X86UNIX/al/altypes.h b/Engine/lib/openal/X86UNIX/al/altypes.h new file mode 100644 index 000000000..45722994c --- /dev/null +++ b/Engine/lib/openal/X86UNIX/al/altypes.h @@ -0,0 +1,344 @@ +#ifndef _AL_TYPES_H_ +#define _AL_TYPES_H_ + +/** OpenAL bool type. */ +typedef char ALboolean; + +/** OpenAL 8bit signed byte. */ +typedef signed char ALbyte; + +/** OpenAL 8bit unsigned byte. */ +typedef unsigned char ALubyte; + +/** OpenAL 16bit signed short integer type. */ +typedef short ALshort; + +/** OpenAL 16bit unsigned short integer type. */ +typedef unsigned short ALushort; + +/** OpenAL 32bit unsigned integer type. */ +typedef unsigned int ALuint; + +/** OpenAL 32bit signed integer type. */ +typedef int ALint; + +/** OpenAL 32bit floating point type. */ +typedef float ALfloat; + +/** OpenAL 64bit double point type. */ +typedef double ALdouble; + +/** OpenAL 32bit type. */ +typedef signed int ALsizei; + +/** OpenAL void type (for params, not returns). */ +typedef void ALvoid; + +/** OpenAL enumerations. */ +typedef int ALenum; + +/* Enumerant values begin at column 50. No tabs. */ + +/* bad value */ +#define AL_INVALID -1 + +/* Boolean False. */ +#define AL_FALSE 0 + +/** Boolean True. */ +#define AL_TRUE 1 + +/** + * Indicate the type of AL_SOURCE. + * Sources can be spatialized + */ +#define AL_SOURCE_TYPE 0x0200 + +/** Indicate Source has relative coordinates. */ +#define AL_SOURCE_RELATIVE 0x0202 + +/** + * Directional source, inner cone angle, in degrees. + * Range: [0-360] + * Default: 360 + */ +#define AL_CONE_INNER_ANGLE 0x1001 + +/** + * Directional source, outer cone angle, in degrees. + * Range: [0-360] + * Default: 360 + */ +#define AL_CONE_OUTER_ANGLE 0x1002 + +/** + * Specify the pitch to be applied, either at source, + * or on mixer results, at listener. + * Range: [0.5-2.0] + * Default: 1.0 + */ +#define AL_PITCH 0x1003 + +/** + * Specify the current location in three dimensional space. + * OpenAL, like OpenGL, uses a right handed coordinate system, + * where in a frontal default view X (thumb) points right, + * Y points up (index finger), and Z points towards the + * viewer/camera (middle finger). + * To switch from a left handed coordinate system, flip the + * sign on the Z coordinate. + * Listener position is always in the world coordinate system. + */ +#define AL_POSITION 0x1004 + +/** Specify the current direction. */ +#define AL_DIRECTION 0x1005 + +/** Specify the current velocity in three dimensional space. */ +#define AL_VELOCITY 0x1006 + +/** + * Indicate whether source is looping. + * Type: ALboolean? + * Range: [AL_TRUE, AL_FALSE] + * Default: FALSE. + */ +#define AL_SOURCE_LOOPING 0x1007 + +/** + * Indicate whether source is meant to be streaming. + * Type: ALboolean? + * Range: [AL_TRUE, AL_FALSE] + * Default: FALSE. + */ +#define AL_STREAMING 0x1008 + +/** + * Indicate the buffer to provide sound samples. + * Type: ALuint. + * Range: any valid Buffer id. + */ +#define AL_BUFFER 0x1009 + +/** + * Indicate the gain (volume amplification) applied. + * Type: ALfloat. + * Range: ]0.0- ] + * A value of 1.0 means un-attenuated/unchanged. + * Each division by 2 equals an attenuation of -6dB. + * Each multiplicaton with 2 equals an amplification of +6dB. + * A value of 0.0 is meaningless with respect to a logarithmic + * scale; it is interpreted as zero volume - the channel + * is effectively disabled. + */ +#define AL_GAIN 0x100A + +/** + * Indicate the gain (volume amplification) applied, in a + * normalized linear scale. This affects the value retrieved + * by AL_GAIN. + * + * Type: ALfloat. + * Range: ]0.0- ] + * A value of 1.0 means un-attenuated/unchanged. + * A value of 0.0 is interpreted as zero volume - the channel + * is effectively disabled. + */ +#define AL_GAIN_LINEAR 0x100B + +/* byte offset into source (in canon format). -1 if source + * is not playing. Don't set this, get this. + * + * Type: ALint + * Range: -1 - +inf + */ +#define AL_BYTE_LOKI 0x100C + +/* + * Indicate minimum source attenuation + * Type: ALfloat + * Range: [0.0 - 1.0] + * + */ +#define AL_SOURCE_ATTENUATION_MIN 0x100D + +/* + * Indicate maximum source attenuation + * Type: ALfloat + * Range: [0.0 - 1.0] + * + */ +#define AL_SOURCE_ATTENUATION_MAX 0x100E + +/* + * Indicate listener orientation. + * + * at/up + */ +#define AL_ORIENTATION 0x100F + + +/* + * Source state information. + */ +#define AL_SOURCE_STATE 0x1010 +#define AL_INITIAL 0x1011 +#define AL_PLAYING 0x1012 +#define AL_PAUSED 0x1013 +#define AL_STOPPED 0x1014 + + +/** Sound samples: format specifier. */ +#define AL_FORMAT_MONO8 0x1100 +#define AL_FORMAT_MONO16 0x1101 +#define AL_FORMAT_STEREO8 0x1102 +#define AL_FORMAT_STEREO16 0x1103 + + +/** + * Sound samples: frequency, in units of Hertz [Hz]. + * This is the number of samples per second. Half of the + * sample frequency marks the maximum significant + * frequency component. + */ +#define AL_FREQUENCY 0x2001 +#define AL_BITS 0x2002 +#define AL_CHANNELS 0x2003 +#define AL_SIZE 0x2004 + + +/** Errors: No Error. */ +#define AL_NO_ERROR AL_FALSE + +/** + * Invalid Name paramater passed to AL call. + */ +#define AL_INVALID_NAME 0xA001 + +/** + * Invalid parameter passed to AL call. + */ +#define AL_ILLEGAL_ENUM 0xA002 + +/** + * Invalid enum parameter value. + */ +#define AL_INVALID_VALUE 0xA003 + +/** + * Illegal call. + */ +#define AL_ILLEGAL_COMMAND 0xA004 + +/** + * No mojo. + */ +#define AL_OUT_OF_MEMORY 0xA005 + + +/** Context strings: Vendor Name. */ +#define AL_VENDOR 0xB001 +#define AL_VERSION 0xB002 +#define AL_RENDERER 0xB003 +#define AL_EXTENSIONS 0xB004 + + +/** IASIG Level 2 Environment. */ + +/** + * Parameter: IASIG ROOM blah + * Type: intgeger + * Range: [-10000, 0] + * Default: -10000 + */ +#define AL_ENV_ROOM_IASIG 0x3001 + +/** + * Parameter: IASIG ROOM_HIGH_FREQUENCY + * Type: integer + * Range: [-10000, 0] + * Default: 0 + */ +#define AL_ENV_ROOM_HIGH_FREQUENCY_IASIG 0x3002 + +/** + * Parameter: IASIG ROOM_ROLLOFF_FACTOR + * Type: float + * Range: [0.0, 10.0] + * Default: 0.0 + */ +#define AL_ENV_ROOM_ROLLOFF_FACTOR_IASIG 0x3003 + +/** + * Parameter: IASIG DECAY_TIME + * Type: float + * Range: [0.1, 20.0] + * Default: 1.0 + */ +#define AL_ENV_DECAY_TIME_IASIG 0x3004 + +/** + * Parameter: IASIG DECAY_HIGH_FREQUENCY_RATIO + * Type: float + * Range: [0.1, 2.0] + * Default: 0.5 + */ +#define AL_ENV_DECAY_HIGH_FREQUENCY_RATIO_IASIG 0x3005 + +/** + * Parameter: IASIG REFLECTIONS + * Type: integer + * Range: [-10000, 1000] + * Default: -10000 + */ +#define AL_ENV_REFLECTIONS_IASIG 0x3006 + +/** + * Parameter: IASIG REFLECTIONS_DELAY + * Type: float + * Range: [0.0, 0.3] + * Default: 0.02 + */ +#define AL_ENV_REFLECTIONS_DELAY_IASIG 0x3006 + +/** + * Parameter: IASIG REVERB + * Type: integer + * Range: [-10000,2000] + * Default: -10000 + */ +#define AL_ENV_REVERB_IASIG 0x3007 + +/** + * Parameter: IASIG REVERB_DELAY + * Type: float + * Range: [0.0, 0.1] + * Default: 0.04 + */ +#define AL_ENV_REVERB_DELAY_IASIG 0x3008 + +/** + * Parameter: IASIG DIFFUSION + * Type: float + * Range: [0.0, 100.0] + * Default: 100.0 + */ +#define AL_ENV_DIFFUSION_IASIG 0x3009 + +/** + * Parameter: IASIG DENSITY + * Type: float + * Range: [0.0, 100.0] + * Default: 100.0 + */ +#define AL_ENV_DENSITY_IASIG 0x300A + + /** + * Parameter: IASIG HIGH_FREQUENCY_REFERENCE + * Type: float + * Range: [20.0, 20000.0] + * Default: 5000.0 + */ +#define AL_ENV_HIGH_FREQUENCY_REFERENCE_IASIG 0x300B + +#endif diff --git a/Engine/lib/openal/X86UNIX/al/alu.h b/Engine/lib/openal/X86UNIX/al/alu.h new file mode 100644 index 000000000..169d5fb03 --- /dev/null +++ b/Engine/lib/openal/X86UNIX/al/alu.h @@ -0,0 +1,37 @@ +#ifndef __alu_h_ +#define __alu_h_ + +#ifdef _WIN32 +#define ALAPI __declspec(dllexport) +#define ALAPIENTRY __cdecl +#else /* _WIN32 */ +#define ALAPI +#define ALAPIENTRY +#define AL_CALLBACK +#endif /* _WIN32 */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef AL_NO_PROTOTYPES + + + +#else + + + + + +#endif /* AL_NO_PROTOTYPES */ + +#ifdef __cplusplus +} +#endif + +#endif /* __alu_h_ */ + diff --git a/Engine/lib/openal/X86UNIX/al/alut.h b/Engine/lib/openal/X86UNIX/al/alut.h new file mode 100644 index 000000000..b43789030 --- /dev/null +++ b/Engine/lib/openal/X86UNIX/al/alut.h @@ -0,0 +1,53 @@ +#ifndef _ALUT_H_ +#define _ALUT_H_ + +#include +#include + +#ifdef _WIN32 +#define ALAPI __declspec(dllexport) +#define ALAPIENTRY __cdecl +#define AL_CALLBACK +#else /* _WIN32 */ +#define ALAPI +#define ALAPIENTRY +#define AL_CALLBACK +#endif /* _WIN32 */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef AL_NO_PROTOTYPES + +ALAPI void ALAPIENTRY alutInit(int *argc, char *argv[]); +ALAPI void ALAPIENTRY alutExit(ALvoid); + +ALAPI ALboolean ALAPIENTRY alutLoadWAV( const char *fname, + ALvoid **wave, + ALsizei *format, + ALsizei *size, + ALsizei *bits, + ALsizei *freq ); + +#else +// +// void (*alutInit)(int *argc, char *argv[]); +// void (*alutExit)(ALvoid); +// +// ALboolean (*alutLoadWAV)( const char *fname, +// ALvoid **wave, +// ALsizei *format, +// ALsizei *size, +// ALsizei *bits, +// ALsizei *freq ); +// +// +#endif /* AL_NO_PROTOTYPES */ + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Engine/lib/openal/X86UNIX/al/aluttypes.h b/Engine/lib/openal/X86UNIX/al/aluttypes.h new file mode 100644 index 000000000..252f622f6 --- /dev/null +++ b/Engine/lib/openal/X86UNIX/al/aluttypes.h @@ -0,0 +1,5 @@ +#ifndef _ALUTTYPES_H_ +#define _ALUTTYPES_H_ + + +#endif /* _ALUTTYPES_H_ */ diff --git a/Engine/lib/openal/X86UNIX/al/alutypes.h b/Engine/lib/openal/X86UNIX/al/alutypes.h new file mode 100644 index 000000000..82356c671 --- /dev/null +++ b/Engine/lib/openal/X86UNIX/al/alutypes.h @@ -0,0 +1,5 @@ +#ifndef _ALUTYPES_H_ +#define _ALUTYPES_H_ + + +#endif /* _ALUTYPES_H_ */ diff --git a/Engine/lib/openal/X86UNIX/openALFn.h b/Engine/lib/openal/X86UNIX/openALFn.h new file mode 100644 index 000000000..0f0b00d48 --- /dev/null +++ b/Engine/lib/openal/X86UNIX/openALFn.h @@ -0,0 +1,101 @@ +#ifndef AL_FUNCTION +#define AL_FUNCTION(fn_return, fn_name, fn_args); +#endif + +#ifndef AL_EXTENSION +#define AL_EXTENSION(ext_name) +#endif + +#ifndef AL_EXT_FUNCTION +#define AL_EXT_FUNCTION(ext_name, fn_return, fn_name, fn_args) +#endif + +// AL functions +AL_FUNCTION(ALvoid, alEnable, ( ALenum capability )) +AL_FUNCTION(ALvoid, alDisable, ( ALenum capability )) +AL_FUNCTION(ALboolean, alIsEnabled, ( ALenum capability )) +AL_FUNCTION(ALvoid, alHint, ( ALenum target, ALenum mode )) +AL_FUNCTION(ALboolean, alGetBoolean, ( ALenum param )) +AL_FUNCTION(ALint, alGetInteger, ( ALenum param )) +AL_FUNCTION(ALfloat, alGetFloat, ( ALenum param )) +AL_FUNCTION(ALdouble, alGetDouble, ( ALenum param )) +AL_FUNCTION(ALvoid, alGetBooleanv, ( ALenum param, ALboolean* data )) +AL_FUNCTION(ALvoid, alGetIntegerv, ( ALenum param, ALint* data )) +AL_FUNCTION(ALvoid, alGetFloatv, ( ALenum param, ALfloat* data )) +AL_FUNCTION(ALvoid, alGetDoublev, ( ALenum param, ALdouble* data )) +AL_FUNCTION(const ALubyte*, alGetString, ( ALenum param )) +AL_FUNCTION(ALenum, alGetError, ( ALvoid )) +AL_FUNCTION(ALboolean, alIsExtensionPresent, ( const ALubyte* fname )) +AL_FUNCTION(ALvoid*, alGetProcAddress, ( const ALubyte* fname )) +AL_FUNCTION(ALenum, alGetEnumValue, ( const ALubyte* ename )) +AL_FUNCTION(ALvoid, alListenerf, ( ALenum pname, ALfloat param )) +AL_FUNCTION(ALvoid, alListener3f, ( ALenum pname, ALfloat param1, ALfloat param2, ALfloat param3 )) +AL_FUNCTION(ALvoid, alListenerfv, ( ALenum pname, ALfloat* param )) +AL_FUNCTION(ALvoid, alGetListeneri, ( ALenum pname, ALint* value )) +AL_FUNCTION(ALvoid, alGetListenerf, ( ALenum pname, ALfloat* values )) +AL_FUNCTION(ALvoid, alGetListenerfv, ( ALenum pname, ALfloat* values )) +AL_FUNCTION(ALvoid, alGenSources, ( ALsizei n, ALuint* sources )) +AL_FUNCTION(ALvoid, alDeleteSources, ( ALsizei n, ALuint* sources )) +AL_FUNCTION(ALboolean, alIsSource, ( ALuint sid )) +AL_FUNCTION(ALvoid, alSourcei, ( ALuint sid, ALenum param, ALint value )) +AL_FUNCTION(ALvoid, alSourcef, ( ALuint sid, ALenum param, ALfloat value )) +AL_FUNCTION(ALvoid, alSource3f, ( ALuint sid, ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 )) +AL_FUNCTION(ALvoid, alSourcefv, ( ALuint sid, ALenum param, ALfloat* values )) +AL_FUNCTION(ALvoid, alGetSourcei, ( ALuint sid, ALenum pname, ALint* value )) +AL_FUNCTION(ALvoid, alGetSourcef, ( ALuint sid, ALenum pname, ALfloat* value )) +AL_FUNCTION(ALvoid, alGetSourcefv, ( ALuint sid, ALenum pname, ALfloat* values )) +AL_FUNCTION(ALvoid, alSourcePlayv, ( ALuint ns, ALuint* ids )) +AL_FUNCTION(ALvoid, alSourceStopv, ( ALuint ns, ALuint* ids )) +AL_FUNCTION(ALvoid, alSourcePlay, ( ALuint sid )) +AL_FUNCTION(ALvoid, alSourcePause, ( ALuint sid )) +AL_FUNCTION(ALvoid, alSourceStop, ( ALuint sid )) +AL_FUNCTION(ALvoid, alGenBuffers, ( ALsizei n, ALuint* samples )) +AL_FUNCTION(ALvoid, alDeleteBuffers, ( ALsizei n, ALuint* samples )) +AL_FUNCTION(ALboolean, alIsBuffer, ( ALuint buffer )) +AL_FUNCTION(ALvoid, alBufferData, ( ALuint buffer, ALenum format, ALvoid* data, ALsizei size, ALsizei freq )) +AL_FUNCTION(ALsizei, alBufferAppendData, ( ALuint buffer, ALenum format, ALvoid* data, ALsizei size, ALsizei freq )) +AL_FUNCTION(ALvoid, alGetBufferi, ( ALuint buffer, ALenum param, ALint* value )) +AL_FUNCTION(ALvoid, alGetBufferf, ( ALuint buffer, ALenum param, ALfloat* value )) + +// ALC functions +AL_FUNCTION(ALvoid*, alcCreateContext, ( ALint* attrlist )) +AL_FUNCTION(ALCenum, alcMakeContextCurrent, ( ALvoid* context )) +AL_FUNCTION(ALvoid*, alcUpdateContext, ( ALvoid* context )) +AL_FUNCTION(ALCenum, alcDestroyContext, ( ALvoid* context )) +AL_FUNCTION(ALCenum, alcGetError, ( ALvoid )) +AL_FUNCTION(const ALubyte *, alcGetErrorString, ( ALvoid )) +AL_FUNCTION(ALvoid*, alcGetCurrentContext, ( ALvoid )) + +// ALUT functions +AL_FUNCTION(void, alutInit, ( int* argc, char** argv )) +AL_FUNCTION(void, alutExit, ( ALvoid )) +AL_FUNCTION(ALboolean, alutLoadWAV, ( const char* fname, ALvoid** data, ALsizei* format, ALsizei* size, ALsizei* bits, ALsizei* freq )) + +// Extensions +AL_EXTENSION(AL_EXT_IASIG) +AL_EXT_FUNCTION(AL_EXT_IASIG, ALvoid, alGenEnvironmentIASIG, ( ALsizei n, ALuint* environs )) +AL_EXT_FUNCTION(AL_EXT_IASIG, ALvoid, alDeleteEnvironmentIASIG, ( ALsizei n, ALuint* environs )) +AL_EXT_FUNCTION(AL_EXT_IASIG, ALboolean, alIsEnvironmentIASIG, ( ALuint environment )) +AL_EXT_FUNCTION(AL_EXT_IASIG, ALvoid, alEnvironmentiIASIG, ( ALuint eid, ALenum param, ALint value )) + +AL_EXTENSION(AL_EXT_DYNAMIX) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alBufferi_EXT, ( ALuint buffer, ALenum pname, ALint value )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alBufferSyncData_EXT, ( ALuint buffer, ALenum format, ALvoid* data, ALsizei size, ALsizei freq )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alBufferStreamFile_EXT, ( ALuint buffer, const ALubyte* filename )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alSourceCallback_EXT, ( ALuint source, ALvoid* callback )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALvoid, alSourceResetEnvironment_EXT, ( ALuint source )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alContexti_EXT, ( ALenum pname, ALint value )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alGetContexti_EXT, ( ALenum pname, ALint* value )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alGetContextstr_EXT, ( ALenum pname, ALuint idx, ALubyte** value )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alCaptureInit_EXT, ( ALenum format, ALuint rate, ALsizei bufferSize )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alCaptureDestroy_EXT, ( ALvoid )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alCaptureStart_EXT, ( ALvoid )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALboolean, alCaptureStop_EXT, ( ALvoid )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALsizei, alCaptureGetData_EXT, ( ALvoid* data, ALsizei n, ALenum format, ALuint rate )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALvoid, alEnvironmentfIASIG, ( ALuint eid, ALenum param, ALfloat value )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALvoid, alGetEnvironmentiIASIG_EXT, ( ALuint eid, ALenum param, ALint * value )) +AL_EXT_FUNCTION(AL_EXT_DYNAMIX, ALvoid, alGetEnvironmentfIASIG_EXT, ( ALuint eid, ALenum param, ALfloat * value )) + +#undef AL_EXTENSION +#undef AL_FUNCTION +#undef AL_EXT_FUNCTION diff --git a/Engine/lib/openal/macCarb/al/al.h b/Engine/lib/openal/macCarb/al/al.h new file mode 100644 index 000000000..a4132f0a0 --- /dev/null +++ b/Engine/lib/openal/macCarb/al/al.h @@ -0,0 +1,491 @@ +#ifndef _AL_H_ +#define _AL_H_ + +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2000 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include "altypes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 + #ifdef _LIB + #define ALAPI __declspec(dllexport) + #else + #define ALAPI __declspec(dllimport) + #endif + #define ALAPIENTRY __cdecl + #define AL_CALLBACK +#else + #ifdef TARGET_OS_MAC + #if TARGET_OS_MAC + #pragma export on + #endif + #endif + #define ALAPI + #define ALAPIENTRY __cdecl + #define AL_CALLBACK +#endif + +#define OPENAL + +#ifndef AL_NO_PROTOTYPES + +/** + * OpenAL Maintenance Functions + * Initialization and exiting. + * State Management and Query. + * Error Handling. + * Extension Support. + */ + +/** State management. */ +ALAPI ALvoid ALAPIENTRY alEnable( ALenum capability ); +ALAPI ALvoid ALAPIENTRY alDisable( ALenum capability ); +ALAPI ALboolean ALAPIENTRY alIsEnabled( ALenum capability ); + +/** Application preferences for driver performance choices. */ +ALAPI ALvoid ALAPIENTRY alHint( ALenum target, ALenum mode ); + +/** State retrieval. */ +ALAPI ALboolean ALAPIENTRY alGetBoolean( ALenum param ); +ALAPI ALint ALAPIENTRY alGetInteger( ALenum param ); +ALAPI ALfloat ALAPIENTRY alGetFloat( ALenum param ); +ALAPI ALdouble ALAPIENTRY alGetDouble( ALenum param ); +ALAPI ALvoid ALAPIENTRY alGetBooleanv( ALenum param, ALboolean* data ); +ALAPI ALvoid ALAPIENTRY alGetIntegerv( ALenum param, ALint* data ); +ALAPI ALvoid ALAPIENTRY alGetFloatv( ALenum param, ALfloat* data ); +ALAPI ALvoid ALAPIENTRY alGetDoublev( ALenum param, ALdouble* data ); +ALAPI ALubyte* ALAPIENTRY alGetString( ALenum param ); + +/** + * Error support. + * Obtain the most recent error generated in the AL state machine. + */ +ALAPI ALenum ALAPIENTRY alGetError( ALvoid ); + + +/** + * Extension support. + * Obtain the address of a function (usually an extension) + * with the name fname. All addresses are context-independent. + */ +ALAPI ALboolean ALAPIENTRY alIsExtensionPresent( ALubyte* fname ); + + +/** + * Extension support. + * Obtain the address of a function (usually an extension) + * with the name fname. All addresses are context-independent. + */ +ALAPI ALvoid* ALAPIENTRY alGetProcAddress( ALubyte* fname ); + + +/** + * Extension support. + * Obtain the integer value of an enumeration (usually an extension) with the name ename. + */ +ALAPI ALenum ALAPIENTRY alGetEnumValue( ALubyte* ename ); + + + + +/** + * LISTENER + * Listener is the sample position for a given context. + * The multi-channel (usually stereo) output stream generated + * by the mixer is parametrized by this Listener object: + * its position and velocity relative to Sources, within + * occluder and reflector geometry. + */ + + + +/** + * + * Listener Environment: default 0. + */ +ALAPI ALvoid ALAPIENTRY alListeneri( ALenum param, ALint value ); + + +/** + * + * Listener Gain: default 1.0f. + */ +ALAPI ALvoid ALAPIENTRY alListenerf( ALenum param, ALfloat value ); + + +/** + * + * Listener Position. + * Listener Velocity. + */ +ALAPI ALvoid ALAPIENTRY alListener3f( ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 ); + + +/** + * + * Listener Position: ALfloat[3] + * Listener Velocity: ALfloat[3] + * Listener Orientation: ALfloat[6] (forward and up vector). + */ +ALAPI ALvoid ALAPIENTRY alListenerfv( ALenum param, ALfloat* values ); + +ALAPI ALvoid ALAPIENTRY alGetListeneri( ALenum param, ALint* value ); +ALAPI ALvoid ALAPIENTRY alGetListenerf( ALenum param, ALfloat* value ); +ALAPI ALvoid ALAPIENTRY alGetListener3f( ALenum param, ALfloat* v1, ALfloat* v2, ALfloat* v3 ); +ALAPI ALvoid ALAPIENTRY alGetListenerfv( ALenum param, ALfloat* values ); + + +/** + * SOURCE + * Source objects are by default localized. Sources + * take the PCM data provided in the specified Buffer, + * apply Source-specific modifications, and then + * submit them to be mixed according to spatial + * arrangement etc. + */ + + + +/** Create Source objects. */ +ALAPI ALvoid ALAPIENTRY alGenSources( ALsizei n, ALuint* sources ); + +/** Delete Source objects. */ +ALAPI ALvoid ALAPIENTRY alDeleteSources( ALsizei n, ALuint* sources ); + +/** Verify a handle is a valid Source. */ +ALAPI ALboolean ALAPIENTRY alIsSource( ALuint id ); + +/** Set an integer parameter for a Source object. */ +ALAPI ALvoid ALAPIENTRY alSourcei( ALuint source, ALenum param, ALint value ); +ALAPI ALvoid ALAPIENTRY alSourcef( ALuint source, ALenum param, ALfloat value ); +ALAPI ALvoid ALAPIENTRY alSource3f( ALuint source, ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 ); +ALAPI ALvoid ALAPIENTRY alSourcefv( ALuint source, ALenum param, ALfloat* values ); + +/** Get an integer parameter for a Source object. */ +ALAPI ALvoid ALAPIENTRY alGetSourcei( ALuint source, ALenum param, ALint* value ); +ALAPI ALvoid ALAPIENTRY alGetSourcef( ALuint source, ALenum param, ALfloat* value ); +ALAPI ALvoid ALAPIENTRY alGetSource3f( ALuint source, ALenum param, ALfloat* v1, ALfloat* v2, ALfloat* v3 ); +ALAPI ALvoid ALAPIENTRY alGetSourcefv( ALuint source, ALenum param, ALfloat* values ); + +ALAPI ALvoid ALAPIENTRY alSourcePlayv( ALsizei n, ALuint *sources ); +ALAPI ALvoid ALAPIENTRY alSourcePausev( ALsizei n, ALuint *sources ); +ALAPI ALvoid ALAPIENTRY alSourceStopv( ALsizei n, ALuint *sources ); +ALAPI ALvoid ALAPIENTRY alSourceRewindv(ALsizei n,ALuint *sources); + +/** Activate a source, start replay. */ +ALAPI ALvoid ALAPIENTRY alSourcePlay( ALuint source ); + +/** + * Pause a source, + * temporarily remove it from the mixer list. + */ +ALAPI ALvoid ALAPIENTRY alSourcePause( ALuint source ); + +/** + * Stop a source, + * temporarily remove it from the mixer list, + * and reset its internal state to pre-Play. + * To remove a Source completely, it has to be + * deleted following Stop, or before Play. + */ +ALAPI ALvoid ALAPIENTRY alSourceStop( ALuint source ); + +/** + * Rewinds a source, + * temporarily remove it from the mixer list, + * and reset its internal state to pre-Play. + */ +ALAPI ALvoid ALAPIENTRY alSourceRewind( ALuint source ); + + + +/** + * BUFFER + * Buffer objects are storage space for sample data. + * Buffers are referred to by Sources. There can be more than + * one Source using the same Buffer data. If Buffers have + * to be duplicated on a per-Source basis, the driver has to + * take care of allocation, copying, and deallocation as well + * as propagating buffer data changes. + */ + + + + +/** Buffer object generation. */ +ALAPI ALvoid ALAPIENTRY alGenBuffers( ALsizei n, ALuint* buffers ); +ALAPI ALvoid ALAPIENTRY alDeleteBuffers( ALsizei n, ALuint* buffers ); +ALAPI ALboolean ALAPIENTRY alIsBuffer( ALuint buffer ); + +/** + * Specify the data to be filled into a buffer. + */ +ALAPI ALvoid ALAPIENTRY alBufferData( ALuint buffer, + ALenum format, + ALvoid* data, + ALsizei size, + ALsizei freq ); + + +ALAPI ALvoid ALAPIENTRY alGetBufferi( ALuint buffer, ALenum param, ALint* value ); +ALAPI ALvoid ALAPIENTRY alGetBufferf( ALuint buffer, ALenum param, ALfloat* value ); + + + + +/** + * Queue stuff + */ + +ALAPI ALvoid ALAPIENTRY alSourceQueueBuffers( ALuint source, ALsizei n, ALuint* buffers ); +ALAPI ALvoid ALAPIENTRY alSourceUnqueueBuffers( ALuint source, ALsizei n, ALuint* buffers ); + +/** + * Knobs and dials + */ +ALAPI ALvoid ALAPIENTRY alDistanceModel( ALenum value ); +ALAPI ALvoid ALAPIENTRY alDopplerFactor( ALfloat value ); +ALAPI ALvoid ALAPIENTRY alDopplerVelocity( ALfloat value ); + +#else /* AL_NO_PROTOTYPES */ + +/** + * OpenAL Maintenance Functions + * Initialization and exiting. + * State Management and Query. + * Error Handling. + * Extension Support. + */ + +/** State management. */ +ALAPI ALvoid ALAPIENTRY (*alEnable)( ALenum capability ); +ALAPI ALvoid ALAPIENTRY (*alDisable)( ALenum capability ); +ALAPI ALboolean ALAPIENTRY (*alIsEnabled)( ALenum capability ); + +/** Application preferences for driver performance choices. */ +ALAPI ALvoid ALAPIENTRY (*alHint)( ALenum target, ALenum mode ); + +/** State retrieval. */ +ALAPI ALboolean ALAPIENTRY (*alGetBoolean)( ALenum param ); +ALAPI ALint ALAPIENTRY (*alGetInteger)( ALenum param ); +ALAPI ALfloat ALAPIENTRY (*alGetFloat)( ALenum param ); +ALAPI ALdouble ALAPIENTRY (*alGetDouble)( ALenum param ); +ALAPI ALvoid ALAPIENTRY (*alGetBooleanv)( ALenum param, ALboolean* data ); +ALAPI ALvoid ALAPIENTRY (*alGetIntegerv)( ALenum param, ALint* data ); +ALAPI ALvoid ALAPIENTRY (*alGetFloatv)( ALenum param, ALfloat* data ); +ALAPI ALvoid ALAPIENTRY (*alGetDoublev)( ALenum param, ALdouble* data ); +ALAPI ALubyte* ALAPIENTRY (*alGetString)( ALenum param ); + +/** + * Error support. + * Obtain the most recent error generated in the AL state machine. + */ +ALAPI ALenum ALAPIENTRY (*alGetError)( ALvoid ); + + +/** + * Extension support. + * Obtain the address of a function (usually an extension) + * with the name fname. All addresses are context-independent. + */ +ALAPI ALboolean ALAPIENTRY (*alIsExtensionPresent)( ALubyte* fname ); + + +/** + * Extension support. + * Obtain the address of a function (usually an extension) + * with the name fname. All addresses are context-independent. + */ +ALAPI ALvoid* ALAPIENTRY (*alGetProcAddress)( ALubyte* fname ); + + +/** + * Extension support. + * Obtain the integer value of an enumeration (usually an extension) with the name ename. + */ +ALAPI ALenum ALAPIENTRY (*alGetEnumValue)( ALubyte* ename ); + + + + +/** + * LISTENER + * Listener is the sample position for a given context. + * The multi-channel (usually stereo) output stream generated + * by the mixer is parametrized by this Listener object: + * its position and velocity relative to Sources, within + * occluder and reflector geometry. + */ + + + +/** + * + * Listener Environment: default 0. + */ +ALAPI ALvoid ALAPIENTRY (*alListeneri)( ALenum param, ALint value ); + + +/** + * + * Listener Gain: default 1.0f. + */ +ALAPI ALvoid ALAPIENTRY (*alListenerf)( ALenum param, ALfloat value ); + + +/** + * + * Listener Position. + * Listener Velocity. + */ +ALAPI ALvoid ALAPIENTRY (*alListener3f)( ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 ); + + +/** + * + * Listener Position: ALfloat[3] + * Listener Velocity: ALfloat[3] + * Listener Orientation: ALfloat[6] (forward and up vector). + */ +ALAPI ALvoid ALAPIENTRY (*alListenerfv)( ALenum param, ALfloat* values ); + +ALAPI ALvoid ALAPIENTRY (*alGetListeneri)( ALenum param, ALint* value ); +ALAPI ALvoid ALAPIENTRY (*alGetListenerf)( ALenum param, ALfloat* value ); +ALAPI ALvoid ALAPIENTRY (*alGetListener3f)( ALenum param, ALfloat* v1, ALfloat* v2, ALfloat* v3 ); +ALAPI ALvoid ALAPIENTRY (*alGetListenerfv)( ALenum param, ALfloat* values ); + + +/** + * SOURCE + * Source objects are by default localized. Sources + * take the PCM data provided in the specified Buffer, + * apply Source-specific modifications, and then + * submit them to be mixed according to spatial + * arrangement etc. + */ + + + +/** Create Source objects. */ +ALAPI ALvoid ALAPIENTRY (*alGenSources)( ALsizei n, ALuint* sources ); + +/** Delete Source objects. */ +ALAPI ALvoid ALAPIENTRY (*alDeleteSources)( ALsizei n, ALuint* sources ); + +/** Verify a handle is a valid Source. */ +ALAPI ALboolean ALAPIENTRY (*alIsSource)( ALuint id ); + +/** Set an integer parameter for a Source object. */ +ALAPI ALvoid ALAPIENTRY (*alSourcei)( ALuint source, ALenum param, ALint value ); +ALAPI ALvoid ALAPIENTRY (*alSourcef)( ALuint source, ALenum param, ALfloat value ); +ALAPI ALvoid ALAPIENTRY (*alSource3f)( ALuint source, ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 ); +ALAPI ALvoid ALAPIENTRY (*alSourcefv)( ALuint source, ALenum param, ALfloat* values ); + +/** Get an integer parameter for a Source object. */ +ALAPI ALvoid ALAPIENTRY (*alGetSourcei)( ALuint source, ALenum param, ALint* value ); +ALAPI ALvoid ALAPIENTRY (*alGetSourcef)( ALuint source, ALenum param, ALfloat* value ); +ALAPI ALvoid ALAPIENTRY (*alGetSourcefv)( ALuint source, ALenum param, ALfloat* values ); + +ALAPI ALvoid ALAPIENTRY (*alSourcePlayv)( ALsizei n, ALuint *sources ); +ALAPI ALvoid ALAPIENTRY (*alSourceStopv)( ALsizei n, ALuint *sources ); + +/** Activate a source, start replay. */ +ALAPI ALvoid ALAPIENTRY (*alSourcePlay)( ALuint source ); + +/** + * Pause a source, + * temporarily remove it from the mixer list. + */ +ALAPI ALvoid ALAPIENTRY (*alSourcePause)( ALuint source ); + +/** + * Stop a source, + * temporarily remove it from the mixer list, + * and reset its internal state to pre-Play. + * To remove a Source completely, it has to be + * deleted following Stop, or before Play. + */ +ALAPI ALvoid ALAPIENTRY (*alSourceStop)( ALuint source ); + + + +/** + * BUFFER + * Buffer objects are storage space for sample data. + * Buffers are referred to by Sources. There can be more than + * one Source using the same Buffer data. If Buffers have + * to be duplicated on a per-Source basis, the driver has to + * take care of allocation, copying, and deallocation as well + * as propagating buffer data changes. + */ + + + + +/** Buffer object generation. */ +ALAPI ALvoid ALAPIENTRY (*alGenBuffers)( ALsizei n, ALuint* buffers ); +ALAPI ALvoid ALAPIENTRY (*alDeleteBuffers)( ALsizei n, ALuint* buffers ); +ALAPI ALboolean ALAPIENTRY (*alIsBuffer)( ALuint buffer ); + +/** + * Specify the data to be filled into a buffer. + */ +ALAPI ALvoid ALAPIENTRY (*alBufferData)( ALuint buffer, + ALenum format, + ALvoid* data, + ALsizei size, + ALsizei freq ); + +ALAPI ALvoid ALAPIENTRY (*alGetBufferi)( ALuint buffer, ALenum param, ALint* value ); +ALAPI ALvoid ALAPIENTRY (*alGetBufferf)( ALuint buffer, ALenum param, ALfloat* value ); + + + + +/** + * Queue stuff + */ +ALAPI ALvoid ALAPIENTRY (*alSourceQueueBuffers)( ALuint source, ALsizei n, ALuint* buffers ); +ALAPI ALvoid ALAPIENTRY (*alSourceUnqueueBuffers)( ALuint source, ALsizei n, ALuint* buffers ); + +/** + * Knobs and dials + */ +ALAPI ALvoid ALAPIENTRY (*alDistanceModel)( ALenum value ); +ALAPI ALvoid ALAPIENTRY (*alDopplerFactor)( ALfloat value ); +ALAPI ALvoid ALAPIENTRY (*alDopplerVelocity)( ALfloat value ); + +#endif /* AL_NO_PROTOTYPES */ + +#ifdef TARGET_OS_MAC + #if TARGET_OS_MAC + #pragma export off + #endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Engine/lib/openal/macCarb/al/al_func.h b/Engine/lib/openal/macCarb/al/al_func.h new file mode 100644 index 000000000..9a280ad2a --- /dev/null +++ b/Engine/lib/openal/macCarb/al/al_func.h @@ -0,0 +1,69 @@ + +AL_FUNCTION(ALvoid, alEnable, ( ALenum capability ), return; ) +AL_FUNCTION(ALvoid, alDisable, ( ALenum capability ), return; ) +AL_FUNCTION(ALboolean, alIsEnabled, ( ALenum capability ), return AL_FALSE; ) + +//AL_FUNCTION(ALvoid, alHint, ( ALenum target, ALenum mode ), return; ) + +AL_FUNCTION(ALboolean, alGetBoolean, ( ALenum param ), return AL_FALSE; ) +AL_FUNCTION(ALint, alGetInteger, ( ALenum param ), return 0; ) +AL_FUNCTION(ALfloat, alGetFloat, ( ALenum param ), return 0.0f; ) +AL_FUNCTION(ALdouble, alGetDouble, ( ALenum param ), return 0.0; ) +AL_FUNCTION(ALvoid, alGetBooleanv, ( ALenum param, ALboolean* data ), return; ) +AL_FUNCTION(ALvoid, alGetIntegerv, ( ALenum param, ALint* data ), return; ) +AL_FUNCTION(ALvoid, alGetFloatv, ( ALenum param, ALfloat* data ), return; ) +AL_FUNCTION(ALvoid, alGetDoublev, ( ALenum param, ALdouble* data ), return; ) +AL_FUNCTION(ALubyte*, alGetString, ( ALenum param ), return NULL; ) + +AL_FUNCTION(ALenum, alGetError, ( ALvoid ), return AL_INVALID_VALUE; ) +AL_FUNCTION(ALboolean, alIsExtensionPresent, ( ALubyte* fname ), return AL_FALSE; ) +AL_FUNCTION(ALvoid*, alGetProcAddress, ( ALubyte* fname ), return NULL; ) +AL_FUNCTION(ALenum, alGetEnumValue, ( ALubyte* ename ), return AL_INVALID_ENUM; ) + +AL_FUNCTION(ALvoid, alListeneri, ( ALenum param, ALint value ), return; ) +AL_FUNCTION(ALvoid, alListenerf, ( ALenum param, ALfloat value ), return; ) +AL_FUNCTION(ALvoid, alListener3f, ( ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 ), return; ) +AL_FUNCTION(ALvoid, alListenerfv, ( ALenum param, ALfloat* values ), return; ) + +AL_FUNCTION(ALvoid, alGetListeneri, ( ALenum param, ALint* value ), return; ) +AL_FUNCTION(ALvoid, alGetListenerf, ( ALenum param, ALfloat* value ), return; ) +AL_FUNCTION(ALvoid, alGetListener3f, ( ALenum param, ALfloat* v1, ALfloat* v2, ALfloat* v3 ), return; ) +AL_FUNCTION(ALvoid, alGetListenerfv, ( ALenum param, ALfloat* values ), return; ) + +AL_FUNCTION(ALvoid, alGenSources, ( ALsizei n, ALuint* sources ), return; ) +AL_FUNCTION(ALvoid, alDeleteSources, ( ALsizei n, ALuint* sources ), return; ) +AL_FUNCTION(ALboolean, alIsSource, ( ALuint id ), return AL_FALSE; ) + +AL_FUNCTION(ALvoid, alSourcei, ( ALuint source, ALenum param, ALint value ), return; ) +AL_FUNCTION(ALvoid, alSourcef, ( ALuint source, ALenum param, ALfloat value ), return; ) +AL_FUNCTION(ALvoid, alSource3f, ( ALuint source, ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 ), return; ) +AL_FUNCTION(ALvoid, alSourcefv, ( ALuint source, ALenum param, ALfloat* values ), return; ) +AL_FUNCTION(ALvoid, alGetSourcei, ( ALuint source, ALenum param, ALint* value ), return; ) +AL_FUNCTION(ALvoid, alGetSourcef, ( ALuint source, ALenum param, ALfloat* value ), return; ) +//AL_FUNCTION(ALvoid, alGetSource3f, ( ALuint source, ALenum param, ALfloat* v1, ALfloat* v2, ALfloat* v3 ), return; ) +AL_FUNCTION(ALvoid, alGetSourcefv, ( ALuint source, ALenum param, ALfloat* values ), return; ) + +AL_FUNCTION(ALvoid, alSourcePlayv, ( ALsizei n, ALuint *sources ), return; ) +AL_FUNCTION(ALvoid, alSourcePausev, ( ALsizei n, ALuint *sources ), return; ) +AL_FUNCTION(ALvoid, alSourceStopv, ( ALsizei n, ALuint *sources ), return; ) +AL_FUNCTION(ALvoid, alSourceRewindv, (ALsizei n,ALuint *sources), return; ) +AL_FUNCTION(ALvoid, alSourcePlay, ( ALuint source ), return; ) +AL_FUNCTION(ALvoid, alSourcePause, ( ALuint source ), return; ) +AL_FUNCTION(ALvoid, alSourceStop, ( ALuint source ), return; ) +AL_FUNCTION(ALvoid, alSourceRewind, ( ALuint source ), return; ) + +AL_FUNCTION(ALvoid, alGenBuffers, ( ALsizei n, ALuint* buffers ), return; ) +AL_FUNCTION(ALvoid, alDeleteBuffers, ( ALsizei n, ALuint* buffers ), return; ) +AL_FUNCTION(ALboolean, alIsBuffer, ( ALuint buffer ), return AL_FALSE; ) +AL_FUNCTION(ALvoid, alBufferData, ( ALuint buffer, ALenum format, ALvoid* data, ALsizei size, ALsizei freq ), return; ) +AL_FUNCTION(ALvoid, alGetBufferi, ( ALuint buffer, ALenum param, ALint* value ), return; ) +AL_FUNCTION(ALvoid, alGetBufferf, ( ALuint buffer, ALenum param, ALfloat* value ), return; ) + +AL_FUNCTION(ALvoid, alSourceQueueBuffers, ( ALuint source, ALsizei n, ALuint* buffers ), return; ) +AL_FUNCTION(ALvoid, alSourceUnqueueBuffers, ( ALuint source, ALsizei n, ALuint* buffers ), return; ) + +AL_FUNCTION(ALvoid, alDistanceModel, ( ALenum value ), return; ) +AL_FUNCTION(ALvoid, alDopplerFactor, ( ALfloat value ), return; ) +AL_FUNCTION(ALvoid, alDopplerVelocity, ( ALfloat value ), return; ) + + diff --git a/Engine/lib/openal/macCarb/al/alc.h b/Engine/lib/openal/macCarb/al/alc.h new file mode 100644 index 000000000..7804227d4 --- /dev/null +++ b/Engine/lib/openal/macCarb/al/alc.h @@ -0,0 +1,88 @@ +#ifndef _ALC_H_ +#define _ALC_H_ + +#include "altypes.h" +#include "alctypes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 + #ifdef _LIB + #define ALCAPI __declspec(dllexport) + #else + #define ALCAPI __declspec(dllimport) + typedef ALCvoid ALCdevice; + typedef ALCvoid ALCcontext; + #endif + #define ALCAPIENTRY __cdecl +#else + #ifdef TARGET_OS_MAC + #if TARGET_OS_MAC + #pragma export on + #endif + #endif + #define ALCAPI + #define ALCAPIENTRY __cdecl +#endif + + + +#ifndef ALC_NO_PROTOTYPES + +ALCAPI ALCubyte* ALCAPIENTRY alcGetString(ALCdevice *device,ALCenum param); +ALCAPI ALCvoid ALCAPIENTRY alcGetIntegerv(ALCdevice *device,ALCenum param,ALCsizei size,ALCint *data); + +ALCAPI ALCdevice* ALCAPIENTRY alcOpenDevice(ALCubyte *deviceName); +ALCAPI ALCvoid ALCAPIENTRY alcCloseDevice(ALCdevice *device); + +ALCAPI ALCcontext*ALCAPIENTRY alcCreateContext(ALCdevice *device,ALCint *attrList); +ALCAPI ALCboolean ALCAPIENTRY alcMakeContextCurrent(ALCcontext *context); +ALCAPI ALCvoid ALCAPIENTRY alcProcessContext(ALCcontext *context); +ALCAPI ALCcontext*ALCAPIENTRY alcGetCurrentContext(ALCvoid); +ALCAPI ALCdevice* ALCAPIENTRY alcGetContextsDevice(ALCcontext *context); +ALCAPI ALCvoid ALCAPIENTRY alcSuspendContext(ALCcontext *context); +ALCAPI ALCvoid ALCAPIENTRY alcDestroyContext(ALCcontext *context); + +ALCAPI ALCenum ALCAPIENTRY alcGetError(ALCdevice *device); + +ALCAPI ALCboolean ALCAPIENTRY alcIsExtensionPresent(ALCdevice *device,ALCubyte *extName); +ALCAPI ALCvoid * ALCAPIENTRY alcGetProcAddress(ALCdevice *device,ALCubyte *funcName); +ALCAPI ALCenum ALCAPIENTRY alcGetEnumValue(ALCdevice *device,ALCubyte *enumName); + +#else /* AL_NO_PROTOTYPES */ + +ALCAPI ALCubyte* ALCAPIENTRY (*alcGetString)(ALCdevice *device,ALCenum param); +ALCAPI ALCvoid ALCAPIENTRY (*alcGetIntegerv)(ALCdevice * device,ALCenum param,ALCsizei size,ALCint *data); + +ALCAPI ALCdevice* ALCAPIENTRY (*alcOpenDevice)(ALubyte *deviceName); +ALCAPI ALCvoid ALCAPIENTRY (*alcCloseDevice)(ALCdevice *device); + +ALCAPI ALCcontext*ALCAPIENTRY (*alcCreateContext)(ALCdevice *device,ALCint *attrList); +ALCAPI ALCboolean ALCAPIENTRY (*alcMakeContextCurrent)(ALCcontext *context); +ALCAPI ALCvoid ALCAPIENTRY (*alcProcessContext)(ALCcontext *context); +ALCAPI ALCcontext*ALCAPIENTRY (*alcGetCurrentContext)(ALCvoid); +ALCAPI ALCdevice* ALCAPIENTRY (*alcGetContextsDevice)(ALCcontext *context); +ALCAPI ALCvoid ALCAPIENTRY (*alcSuspendContext)(ALCcontext *context); +ALCAPI ALCvoid ALCAPIENTRY (*alcDestroyContext)(ALCcontext *context); + +ALCAPI ALCenum ALCAPIENTRY (*alcGetError)(ALCdevice *device); + +ALCAPI ALCboolean ALCAPIENTRY (*alcIsExtensionPresent)(ALCdevice *device,ALCubyte *extName); +ALCAPI ALCvoid * ALCAPIENTRY (*alcGetProcAddress)(ALCdevice *device,ALCubyte *funcName); +ALCAPI ALCenum ALCAPIENTRY (*alcGetEnumValue)(ALCdevice *device,ALCubyte *enumName); + +#endif /* AL_NO_PROTOTYPES */ + +#ifdef TARGET_OS_MAC + #if TARGET_OS_MAC + #pragma export off + #endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Engine/lib/openal/macCarb/al/alc_func.h b/Engine/lib/openal/macCarb/al/alc_func.h new file mode 100644 index 000000000..22da893e0 --- /dev/null +++ b/Engine/lib/openal/macCarb/al/alc_func.h @@ -0,0 +1,20 @@ + +//AL_FUNCTION(ALCubyte*, alcGetString, (ALCdevice *device,ALCenum param), return NULL; ) +//AL_FUNCTION(ALCvoid, alcGetIntegerv, (ALCdevice * device,ALCenum param,ALCsizei size,ALCint *data), return; ) + +AL_FUNCTION(ALCdevice*, alcOpenDevice, (ALubyte *deviceName), return NULL; ) +AL_FUNCTION(ALCvoid, alcCloseDevice, (ALCdevice *device), return; ) + +AL_FUNCTION(ALCcontext*, alcCreateContext, (ALCdevice *device,ALCint *attrList), return NULL; ) +AL_FUNCTION(ALCboolean, alcMakeContextCurrent, (ALCcontext *context), return AL_FALSE; ) +AL_FUNCTION(ALCvoid, alcProcessContext, (ALCcontext *context), return; ) +AL_FUNCTION(ALCcontext*, alcGetCurrentContext, (ALCvoid), return NULL; ) +AL_FUNCTION(ALCdevice*, alcGetContextsDevice, (ALCcontext *context), return NULL; ) +AL_FUNCTION(ALCvoid, alcSuspendContext, (ALCcontext *context), return; ) +AL_FUNCTION(ALCvoid, alcDestroyContext, (ALCcontext *context), return; ) + +AL_FUNCTION(ALCenum, alcGetError, (ALCdevice *device), return ALC_INVALID_DEVICE; ) + +//AL_FUNCTION(ALCboolean, alcIsExtensionPresent, (ALCdevice *device,ALCubyte *extName), return AL_FALSE; ) +//AL_FUNCTION(ALCvoid*, alcGetProcAddress, (ALCdevice *device,ALCubyte *funcName), return NULL; ) +//AL_FUNCTION(ALCenum, alcGetEnumValue, (ALCdevice *device,ALCubyte *enumName), return ALC_INVALID_ENUM; ) diff --git a/Engine/lib/openal/macCarb/al/alctypes.h b/Engine/lib/openal/macCarb/al/alctypes.h new file mode 100644 index 000000000..72ff49689 --- /dev/null +++ b/Engine/lib/openal/macCarb/al/alctypes.h @@ -0,0 +1,130 @@ +#ifndef _ALCTYPES_H_ +#define _ALCTYPES_H_ + +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2000 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + + +#ifdef __cplusplus +extern "C" { +#endif + +/** ALC boolean type. */ +typedef char ALCboolean; + +/** ALC 8bit signed byte. */ +typedef char ALCbyte; + +/** ALC 8bit unsigned byte. */ +typedef unsigned char ALCubyte; + +/** ALC 16bit signed short integer type. */ +typedef short ALCshort; + +/** ALC 16bit unsigned short integer type. */ +typedef unsigned short ALCushort; + +/** ALC 32bit unsigned integer type. */ +typedef unsigned ALCuint; + +/** ALC 32bit signed integer type. */ +typedef int ALCint; + +/** ALC 32bit floating point type. */ +typedef float ALCfloat; + +/** ALC 64bit double point type. */ +typedef double ALCdouble; + +/** ALC 32bit type. */ +typedef unsigned int ALCsizei; + +/** ALC void type */ +typedef void ALCvoid; + +/** ALC enumerations. */ +typedef int ALCenum; + + +typedef ALCvoid ALCdevice; +typedef ALCvoid ALCcontext; + + +/* Bad value. */ +#define ALC_INVALID (-1) + +/* Boolean False. */ +#define ALC_FALSE 0 + +/* Boolean True. */ +#define ALC_TRUE 1 + +/** Errors: No Error. */ +#define ALC_NO_ERROR ALC_FALSE + +#define ALC_MAJOR_VERSION 0x1000 +#define ALC_MINOR_VERSION 0x1001 +#define ALC_ATTRIBUTES_SIZE 0x1002 +#define ALC_ALL_ATTRIBUTES 0x1003 + +#define ALC_DEFAULT_DEVICE_SPECIFIER 0x1004 +#define ALC_DEVICE_SPECIFIER 0x1005 +#define ALC_EXTENSIONS 0x1006 + +#define ALC_FREQUENCY 0x1007 +#define ALC_REFRESH 0x1008 +#define ALC_SYNC 0x1009 + +/** + * The device argument does not name a valid dvice. + */ +#define ALC_INVALID_DEVICE 0xA001 + +/** + * The context argument does not name a valid context. + */ +#define ALC_INVALID_CONTEXT 0xA002 + +/** + * A function was called at inappropriate time, + * or in an inappropriate way, causing an illegal state. + * This can be an incompatible ALenum, object ID, + * and/or function. + */ +#define ALC_INVALID_ENUM 0xA003 + +/** + * Illegal value passed as an argument to an AL call. + * Applies to parameter values, but not to enumerations. + */ +#define ALC_INVALID_VALUE 0xA004 + +/** + * A function could not be completed, + * because there is not enough memory available. + */ +#define ALC_OUT_OF_MEMORY 0xA005 + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/Engine/lib/openal/macCarb/al/altypes.h b/Engine/lib/openal/macCarb/al/altypes.h new file mode 100644 index 000000000..eb7c17301 --- /dev/null +++ b/Engine/lib/openal/macCarb/al/altypes.h @@ -0,0 +1,336 @@ +#ifndef _ALTYPES_H_ +#define _ALTYPES_H_ + +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2000 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + + +#ifdef __cplusplus +extern "C" { +#endif + +/** OpenAL boolean type. */ +typedef char ALboolean; + +/** OpenAL 8bit signed byte. */ +typedef char ALbyte; + +/** OpenAL 8bit unsigned byte. */ +typedef unsigned char ALubyte; + +/** OpenAL 16bit signed short integer type. */ +typedef short ALshort; + +/** OpenAL 16bit unsigned short integer type. */ +typedef unsigned short ALushort; + +/** OpenAL 32bit unsigned integer type. */ +typedef unsigned ALuint; + +/** OpenAL 32bit signed integer type. */ +typedef int ALint; + +/** OpenAL 32bit floating point type. */ +typedef float ALfloat; + +/** OpenAL 64bit double point type. */ +typedef double ALdouble; + +/** OpenAL 32bit type. */ +typedef unsigned int ALsizei; + +/** OpenAL void type */ +typedef void ALvoid; + +/** OpenAL enumerations. */ +typedef int ALenum; + +/* Bad value. */ +#define AL_INVALID (-1) + +/* Disable value. */ +#define AL_NONE 0 + +/* Boolean False. */ +#define AL_FALSE 0 + +/* Boolean True. */ +#define AL_TRUE 1 + +/** + * Indicate the type of AL_SOURCE. + * Sources can be spatialized + */ +#define AL_SOURCE_TYPE 0x200 + +/** Indicate source has absolute coordinates. */ +#define AL_SOURCE_ABSOLUTE 0x201 + +/** Indicate Source has listener relative coordinates. */ +#define AL_SOURCE_RELATIVE 0x202 + +/** + * Directional source, inner cone angle, in degrees. + * Range: [0-360] + * Default: 360 + */ +#define AL_CONE_INNER_ANGLE 0x1001 + +/** + * Directional source, outer cone angle, in degrees. + * Range: [0-360] + * Default: 360 + */ +#define AL_CONE_OUTER_ANGLE 0x1002 + +/** + * Specify the pitch to be applied, either at source, + * or on mixer results, at listener. + * Range: [0.5-2.0] + * Default: 1.0 + */ +#define AL_PITCH 0x1003 + +/** + * Specify the current location in three dimensional space. + * OpenAL, like OpenGL, uses a right handed coordinate system, + * where in a frontal default view X (thumb) points right, + * Y points up (index finger), and Z points towards the + * viewer/camera (middle finger). + * To switch from a left handed coordinate system, flip the + * sign on the Z coordinate. + * Listener position is always in the world coordinate system. + */ +#define AL_POSITION 0x1004 + +/** Specify the current direction as forward vector. */ +#define AL_DIRECTION 0x1005 + +/** Specify the current velocity in three dimensional space. */ +#define AL_VELOCITY 0x1006 + +/** + * Indicate whether source has to loop infinite. + * Type: ALboolean + * Range: [AL_TRUE, AL_FALSE] + * Default: AL_FALSE + */ +#define AL_LOOPING 0x1007 + +/** + * Indicate the buffer to provide sound samples. + * Type: ALuint. + * Range: any valid Buffer id. + */ +#define AL_BUFFER 0x1009 + +/** + * Indicate the gain (volume amplification) applied. + * Type: ALfloat. + * Range: ]0.0- ] + * A value of 1.0 means un-attenuated/unchanged. + * Each division by 2 equals an attenuation of -6dB. + * Each multiplicaton with 2 equals an amplification of +6dB. + * A value of 0.0 is meaningless with respect to a logarithmic + * scale; it is interpreted as zero volume - the channel + * is effectively disabled. + */ +#define AL_GAIN 0x100A + +/** + * Indicate minimum source attenuation. + * Type: ALfloat + * Range: [0.0 - 1.0] + */ +#define AL_MIN_GAIN 0x100D + +/** + * Indicate maximum source attenuation. + * Type: ALfloat + * Range: [0.0 - 1.0] + */ +#define AL_MAX_GAIN 0x100E + +/** + * Specify the current orientation. + * Type: ALfv6 (at/up) + * Range: N/A + */ +#define AL_ORIENTATION 0x100F + +/* byte offset into source (in canon format). -1 if source + * is not playing. Don't set this, get this. + * + * Type: ALfloat + * Range: [0.0 - ] + * Default: 1.0 + */ +#define AL_REFERENCE_DISTANCE 0x1020 + + /** + * Indicate the rolloff factor for the source. + * Type: ALfloat + * Range: [0.0 - ] + * Default: 1.0 + */ +#define AL_ROLLOFF_FACTOR 0x1021 + +/** + * Indicate the gain (volume amplification) applied. + * Type: ALfloat. + * Range: ]0.0- ] + * A value of 1.0 means un-attenuated/unchanged. + * Each division by 2 equals an attenuation of -6dB. + * Each multiplicaton with 2 equals an amplification of +6dB. + * A value of 0.0 is meaningless with respect to a logarithmic + * scale; it is interpreted as zero volume - the channel + * is effectively disabled. + */ +#define AL_CONE_OUTER_GAIN 0x1022 + +/** + * Specify the maximum distance. + * Type: ALfloat + * Range: [0.0 - ] + */ +#define AL_MAX_DISTANCE 0x1023 + +/** + * Specify the channel mask. (Creative) + * Type: ALuint + * Range: [0 - 255] + */ +#define AL_CHANNEL_MASK 0x3000 + +/** + * Source state information + */ +#define AL_SOURCE_STATE 0x1010 +#define AL_INITIAL 0x1011 +#define AL_PLAYING 0x1012 +#define AL_PAUSED 0x1013 +#define AL_STOPPED 0x1014 + +/** + * Buffer Queue params + */ +#define AL_BUFFERS_QUEUED 0x1015 +#define AL_BUFFERS_PROCESSED 0x1016 + +/** Sound buffers: format specifier. */ +#define AL_FORMAT_MONO8 0x1100 +#define AL_FORMAT_MONO16 0x1101 +#define AL_FORMAT_STEREO8 0x1102 +#define AL_FORMAT_STEREO16 0x1103 + +/** + * Sound buffers: frequency, in units of Hertz [Hz]. + * This is the number of samples per second. Half of the + * sample frequency marks the maximum significant + * frequency component. + */ +#define AL_FREQUENCY 0x2001 +#define AL_BITS 0x2002 +#define AL_CHANNELS 0x2003 +#define AL_SIZE 0x2004 +#define AL_DATA 0x2005 + +/** + * Buffer state. + * + * Not supported for public use (yet). + */ +#define AL_UNUSED 0x2010 +#define AL_PENDING 0x2011 +#define AL_PROCESSED 0x2012 + +/** Errors: No Error. */ +#define AL_NO_ERROR AL_FALSE + +/** + * Illegal name passed as an argument to an AL call. + */ +#define AL_INVALID_NAME 0xA001 + +/** + * Illegal enum passed as an argument to an AL call. + */ +#define AL_INVALID_ENUM 0xA002 +/** + * Illegal value passed as an argument to an AL call. + * Applies to parameter values, but not to enumerations. + */ +#define AL_INVALID_VALUE 0xA003 + +/** + * A function was called at inappropriate time, + * or in an inappropriate way, causing an illegal state. + * This can be an incompatible ALenum, object ID, + * and/or function. + */ +#define AL_INVALID_OPERATION 0xA004 + +/** + * A function could not be completed, + * because there is not enough memory available. + */ +#define AL_OUT_OF_MEMORY 0xA005 + +/** Context strings: Vendor Name. */ +#define AL_VENDOR 0xB001 +#define AL_VERSION 0xB002 +#define AL_RENDERER 0xB003 +#define AL_EXTENSIONS 0xB004 + +/** Global tweakage. */ + +/** + * Doppler scale. Default 1.0 + */ +#define AL_DOPPLER_FACTOR 0xC000 + +/** + * Doppler velocity. Default 1.0 + */ +#define AL_DOPPLER_VELOCITY 0xC001 + +/** + * Distance model. Default AL_INVERSE_DISTANCE_CLAMPED + */ +#define AL_DISTANCE_MODEL 0xD000 + +/** Distance models. */ + +#define AL_INVERSE_DISTANCE 0xD001 +#define AL_INVERSE_DISTANCE_CLAMPED 0xD002 +#define AL_LINEAR_DISTANCE 0xD003 +#define AL_LINEAR_DISTANCE_CLAMPED 0xD004 +#define AL_EXPONENT_DISTANCE 0xD005 +#define AL_EXPONENT_DISTANCE_CLAMPED 0xD006 + + /** + * enables + */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Engine/lib/openal/macCarb/al/alu.h b/Engine/lib/openal/macCarb/al/alu.h new file mode 100644 index 000000000..c6adff62c --- /dev/null +++ b/Engine/lib/openal/macCarb/al/alu.h @@ -0,0 +1,34 @@ +#ifndef _ALU_H_ +#define _ALU_H_ + +#define ALUAPI +#define ALUAPIENTRY __cdecl + +#define BUFFERSIZE 48000 +#define FRACTIONBITS 14 +#define FRACTIONMASK ((1L< + + /* + * EAX Wrapper Interface (using Direct X 7) {4FF53B81-1CE0-11d3-AAB8-00A0C95949D5} + */ + DEFINE_GUID(CLSID_EAXDirectSound, + 0x4ff53b81, + 0x1ce0, + 0x11d3, + 0xaa, 0xb8, 0x0, 0xa0, 0xc9, 0x59, 0x49, 0xd5); + + /* + * EAX Wrapper Interface (using Direct X 8) {CA503B60-B176-11d4-A094-D0C0BF3A560C} + */ + DEFINE_GUID(CLSID_EAXDirectSound8, + 0xca503b60, + 0xb176, + 0x11d4, + 0xa0, 0x94, 0xd0, 0xc0, 0xbf, 0x3a, 0x56, 0xc); + + + +#ifdef DIRECTSOUND_VERSION +#if DIRECTSOUND_VERSION == 0x0800 + __declspec(dllimport) HRESULT WINAPI EAXDirectSoundCreate8(GUID*, LPDIRECTSOUND8*, IUnknown FAR *); + typedef HRESULT (FAR PASCAL *LPEAXDIRECTSOUNDCREATE8)(GUID*, LPDIRECTSOUND8*, IUnknown FAR*); +#endif +#endif + + __declspec(dllimport) HRESULT WINAPI EAXDirectSoundCreate(GUID*, LPDIRECTSOUND*, IUnknown FAR *); + typedef HRESULT (FAR PASCAL *LPEAXDIRECTSOUNDCREATE)(GUID*, LPDIRECTSOUND*, IUnknown FAR*); + +#else // OPENAL + #include + + #ifndef GUID_DEFINED + #define GUID_DEFINED + typedef struct _GUID + { + unsigned long Data1; + unsigned short Data2; + unsigned short Data3; + unsigned char Data4[8]; + } GUID; + #endif // !GUID_DEFINED + + #ifndef DEFINE_GUID + #ifndef INITGUID + #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const GUID FAR name + #else + #define DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const GUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } + #endif // INITGUID + #endif // DEFINE_GUID + + + /* + * EAX OpenAL Extension + */ + typedef ALenum (*EAXSet)(const GUID*, ALuint, ALuint, ALvoid*, ALuint); + typedef ALenum (*EAXGet)(const GUID*, ALuint, ALuint, ALvoid*, ALuint); +#endif + +#pragma pack(push, 4) + +/* + * EAX 3.0 listener property set {A8FA6880-B476-11d3-BDB9-00C0F02DDF87} + */ +DEFINE_GUID(DSPROPSETID_EAX30_ListenerProperties, + 0xa8fa6882, + 0xb476, + 0x11d3, + 0xbd, 0xb9, 0x00, 0xc0, 0xf0, 0x2d, 0xdf, 0x87); + +// For compatibility with future EAX versions: +#define DSPROPSETID_EAX_ListenerProperties DSPROPSETID_EAX30_ListenerProperties + +typedef enum +{ + DSPROPERTY_EAXLISTENER_NONE, + DSPROPERTY_EAXLISTENER_ALLPARAMETERS, + DSPROPERTY_EAXLISTENER_ENVIRONMENT, + DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE, + DSPROPERTY_EAXLISTENER_ENVIRONMENTDIFFUSION, + DSPROPERTY_EAXLISTENER_ROOM, + DSPROPERTY_EAXLISTENER_ROOMHF, + DSPROPERTY_EAXLISTENER_ROOMLF, + DSPROPERTY_EAXLISTENER_DECAYTIME, + DSPROPERTY_EAXLISTENER_DECAYHFRATIO, + DSPROPERTY_EAXLISTENER_DECAYLFRATIO, + DSPROPERTY_EAXLISTENER_REFLECTIONS, + DSPROPERTY_EAXLISTENER_REFLECTIONSDELAY, + DSPROPERTY_EAXLISTENER_REFLECTIONSPAN, + DSPROPERTY_EAXLISTENER_REVERB, + DSPROPERTY_EAXLISTENER_REVERBDELAY, + DSPROPERTY_EAXLISTENER_REVERBPAN, + DSPROPERTY_EAXLISTENER_ECHOTIME, + DSPROPERTY_EAXLISTENER_ECHODEPTH, + DSPROPERTY_EAXLISTENER_MODULATIONTIME, + DSPROPERTY_EAXLISTENER_MODULATIONDEPTH, + DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF, + DSPROPERTY_EAXLISTENER_HFREFERENCE, + DSPROPERTY_EAXLISTENER_LFREFERENCE, + DSPROPERTY_EAXLISTENER_ROOMROLLOFFFACTOR, + DSPROPERTY_EAXLISTENER_FLAGS +} DSPROPERTY_EAX_LISTENERPROPERTY; + +// OR these flags with property id +#define DSPROPERTY_EAXLISTENER_IMMEDIATE 0x00000000 // changes take effect immediately +#define DSPROPERTY_EAXLISTENER_DEFERRED 0x80000000 // changes take effect later +#define DSPROPERTY_EAXLISTENER_COMMITDEFERREDSETTINGS (DSPROPERTY_EAXLISTENER_NONE | \ + DSPROPERTY_EAXLISTENER_IMMEDIATE) + +typedef struct _EAXVECTOR { + float x; + float y; + float z; +} EAXVECTOR; + +// Use this structure for DSPROPERTY_EAXLISTENER_ALLPARAMETERS +// - all levels are hundredths of decibels +// - all times and delays are in seconds +// +// NOTE: This structure may change in future EAX versions. +// It is recommended to initialize fields by name: +// myListener.lRoom = -1000; +// myListener.lRoomHF = -100; +// ... +// myListener.dwFlags = myFlags /* see EAXLISTENERFLAGS below */ ; +// instead of: +// myListener = { -1000, -100, ... , 0x00000009 }; +// If you want to save and load presets in binary form, you +// should define your own structure to insure future compatibility. +// +typedef struct _EAXLISTENERPROPERTIES +{ + unsigned long ulEnvironment; // sets all listener properties + float flEnvironmentSize; // environment size in meters + float flEnvironmentDiffusion; // environment diffusion + long lRoom; // room effect level (at mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lRoomLF; // relative room effect level at low frequencies + float flDecayTime; // reverberation decay time at mid frequencies + float flDecayHFRatio; // high-frequency to mid-frequency decay time ratio + float flDecayLFRatio; // low-frequency to mid-frequency decay time ratio + long lReflections; // early reflections level relative to room effect + float flReflectionsDelay; // initial reflection delay time + EAXVECTOR vReflectionsPan; // early reflections panning vector + long lReverb; // late reverberation level relative to room effect + float flReverbDelay; // late reverberation delay time relative to initial reflection + EAXVECTOR vReverbPan; // late reverberation panning vector + float flEchoTime; // echo time + float flEchoDepth; // echo depth + float flModulationTime; // modulation time + float flModulationDepth; // modulation depth + float flAirAbsorptionHF; // change in level per meter at high frequencies + float flHFReference; // reference high frequency + float flLFReference; // reference low frequency + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + unsigned long ulFlags; // modifies the behavior of properties +} EAXLISTENERPROPERTIES, *LPEAXLISTENERPROPERTIES; + +// used by DSPROPERTY_EAXLISTENER_ENVIRONMENT +enum +{ + EAX_ENVIRONMENT_GENERIC, + EAX_ENVIRONMENT_PADDEDCELL, + EAX_ENVIRONMENT_ROOM, + EAX_ENVIRONMENT_BATHROOM, + EAX_ENVIRONMENT_LIVINGROOM, + EAX_ENVIRONMENT_STONEROOM, + EAX_ENVIRONMENT_AUDITORIUM, + EAX_ENVIRONMENT_CONCERTHALL, + EAX_ENVIRONMENT_CAVE, + EAX_ENVIRONMENT_ARENA, + EAX_ENVIRONMENT_HANGAR, + EAX_ENVIRONMENT_CARPETEDHALLWAY, + EAX_ENVIRONMENT_HALLWAY, + EAX_ENVIRONMENT_STONECORRIDOR, + EAX_ENVIRONMENT_ALLEY, + EAX_ENVIRONMENT_FOREST, + EAX_ENVIRONMENT_CITY, + EAX_ENVIRONMENT_MOUNTAINS, + EAX_ENVIRONMENT_QUARRY, + EAX_ENVIRONMENT_PLAIN, + EAX_ENVIRONMENT_PARKINGLOT, + EAX_ENVIRONMENT_SEWERPIPE, + EAX_ENVIRONMENT_UNDERWATER, + EAX_ENVIRONMENT_DRUGGED, + EAX_ENVIRONMENT_DIZZY, + EAX_ENVIRONMENT_PSYCHOTIC, + + EAX_ENVIRONMENT_UNDEFINED, + + EAX_ENVIRONMENT_COUNT +}; + +// Used by DSPROPERTY_EAXLISTENER_FLAGS +// +// Note: The number and order of flags may change in future EAX versions. +// It is recommended to use the flag defines as follows: +// myFlags = EAXLISTENERFLAGS_DECAYTIMESCALE | EAXLISTENERFLAGS_REVERBSCALE; +// instead of: +// myFlags = 0x00000009; +// +// These flags determine what properties are affected by environment size. +#define EAXLISTENERFLAGS_DECAYTIMESCALE 0x00000001 // reverberation decay time +#define EAXLISTENERFLAGS_REFLECTIONSSCALE 0x00000002 // reflection level +#define EAXLISTENERFLAGS_REFLECTIONSDELAYSCALE 0x00000004 // initial reflection delay time +#define EAXLISTENERFLAGS_REVERBSCALE 0x00000008 // reflections level +#define EAXLISTENERFLAGS_REVERBDELAYSCALE 0x00000010 // late reverberation delay time +#define EAXLISTENERFLAGS_ECHOTIMESCALE 0x00000040 // echo time +#define EAXLISTENERFLAGS_MODULATIONTIMESCALE 0x00000080 // modulation time + +// This flag limits high-frequency decay time according to air absorption. +#define EAXLISTENERFLAGS_DECAYHFLIMIT 0x00000020 + +#define EAXLISTENERFLAGS_RESERVED 0xFFFFFF00 // reserved future use + +// Property ranges and defaults: + +#define EAXLISTENER_MINENVIRONMENT 0 +#define EAXLISTENER_MAXENVIRONMENT (EAX_ENVIRONMENT_COUNT-1) +#define EAXLISTENER_DEFAULTENVIRONMENT EAX_ENVIRONMENT_GENERIC + +#define EAXLISTENER_MINENVIRONMENTSIZE 1.0f +#define EAXLISTENER_MAXENVIRONMENTSIZE 100.0f +#define EAXLISTENER_DEFAULTENVIRONMENTSIZE 7.5f + +#define EAXLISTENER_MINENVIRONMENTDIFFUSION 0.0f +#define EAXLISTENER_MAXENVIRONMENTDIFFUSION 1.0f +#define EAXLISTENER_DEFAULTENVIRONMENTDIFFUSION 1.0f + +#define EAXLISTENER_MINROOM (-10000) +#define EAXLISTENER_MAXROOM 0 +#define EAXLISTENER_DEFAULTROOM (-1000) + +#define EAXLISTENER_MINROOMHF (-10000) +#define EAXLISTENER_MAXROOMHF 0 +#define EAXLISTENER_DEFAULTROOMHF (-100) + +#define EAXLISTENER_MINROOMLF (-10000) +#define EAXLISTENER_MAXROOMLF 0 +#define EAXLISTENER_DEFAULTROOMLF 0 + +#define EAXLISTENER_MINDECAYTIME 0.1f +#define EAXLISTENER_MAXDECAYTIME 20.0f +#define EAXLISTENER_DEFAULTDECAYTIME 1.49f + +#define EAXLISTENER_MINDECAYHFRATIO 0.1f +#define EAXLISTENER_MAXDECAYHFRATIO 2.0f +#define EAXLISTENER_DEFAULTDECAYHFRATIO 0.83f + +#define EAXLISTENER_MINDECAYLFRATIO 0.1f +#define EAXLISTENER_MAXDECAYLFRATIO 2.0f +#define EAXLISTENER_DEFAULTDECAYLFRATIO 1.00f + +#define EAXLISTENER_MINREFLECTIONS (-10000) +#define EAXLISTENER_MAXREFLECTIONS 1000 +#define EAXLISTENER_DEFAULTREFLECTIONS (-2602) + +#define EAXLISTENER_MINREFLECTIONSDELAY 0.0f +#define EAXLISTENER_MAXREFLECTIONSDELAY 0.3f +#define EAXLISTENER_DEFAULTREFLECTIONSDELAY 0.007f + +#define EAXLISTENER_MINREVERB (-10000) +#define EAXLISTENER_MAXREVERB 2000 +#define EAXLISTENER_DEFAULTREVERB 200 + +#define EAXLISTENER_MINREVERBDELAY 0.0f +#define EAXLISTENER_MAXREVERBDELAY 0.1f +#define EAXLISTENER_DEFAULTREVERBDELAY 0.011f + +#define EAXLISTENER_MINECHOTIME 0.075f +#define EAXLISTENER_MAXECHOTIME 0.25f +#define EAXLISTENER_DEFAULTECHOTIME 0.25f + +#define EAXLISTENER_MINECHODEPTH 0.0f +#define EAXLISTENER_MAXECHODEPTH 1.0f +#define EAXLISTENER_DEFAULTECHODEPTH 0.0f + +#define EAXLISTENER_MINMODULATIONTIME 0.04f +#define EAXLISTENER_MAXMODULATIONTIME 4.0f +#define EAXLISTENER_DEFAULTMODULATIONTIME 0.25f + +#define EAXLISTENER_MINMODULATIONDEPTH 0.0f +#define EAXLISTENER_MAXMODULATIONDEPTH 1.0f +#define EAXLISTENER_DEFAULTMODULATIONDEPTH 0.0f + +#define EAXLISTENER_MINAIRABSORPTIONHF (-100.0f) +#define EAXLISTENER_MAXAIRABSORPTIONHF 0.0f +#define EAXLISTENER_DEFAULTAIRABSORPTIONHF (-5.0f) + +#define EAXLISTENER_MINHFREFERENCE 1000.0f +#define EAXLISTENER_MAXHFREFERENCE 20000.0f +#define EAXLISTENER_DEFAULTHFREFERENCE 5000.0f + +#define EAXLISTENER_MINLFREFERENCE 20.0f +#define EAXLISTENER_MAXLFREFERENCE 1000.0f +#define EAXLISTENER_DEFAULTLFREFERENCE 250.0f + +#define EAXLISTENER_MINROOMROLLOFFFACTOR 0.0f +#define EAXLISTENER_MAXROOMROLLOFFFACTOR 10.0f +#define EAXLISTENER_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXLISTENER_DEFAULTFLAGS (EAXLISTENERFLAGS_DECAYTIMESCALE | \ + EAXLISTENERFLAGS_REFLECTIONSSCALE | \ + EAXLISTENERFLAGS_REFLECTIONSDELAYSCALE | \ + EAXLISTENERFLAGS_REVERBSCALE | \ + EAXLISTENERFLAGS_REVERBDELAYSCALE | \ + EAXLISTENERFLAGS_DECAYHFLIMIT) + + + +/* +* EAX 3.0 buffer property set {A8FA6881-B476-11d3-BDB9-00C0F02DDF87} +*/ +DEFINE_GUID(DSPROPSETID_EAX30_BufferProperties, + 0xa8fa6881, + 0xb476, + 0x11d3, + 0xbd, 0xb9, 0x0, 0xc0, 0xf0, 0x2d, 0xdf, 0x87); + +// For compatibility with future EAX versions: +#define DSPROPSETID_EAX_BufferProperties DSPROPSETID_EAX30_BufferProperties +#define DSPROPSETID_EAX_SourceProperties DSPROPSETID_EAX30_BufferProperties + +typedef enum +{ + DSPROPERTY_EAXBUFFER_NONE, + DSPROPERTY_EAXBUFFER_ALLPARAMETERS, + DSPROPERTY_EAXBUFFER_OBSTRUCTIONPARAMETERS, + DSPROPERTY_EAXBUFFER_OCCLUSIONPARAMETERS, + DSPROPERTY_EAXBUFFER_EXCLUSIONPARAMETERS, + DSPROPERTY_EAXBUFFER_DIRECT, + DSPROPERTY_EAXBUFFER_DIRECTHF, + DSPROPERTY_EAXBUFFER_ROOM, + DSPROPERTY_EAXBUFFER_ROOMHF, + DSPROPERTY_EAXBUFFER_OBSTRUCTION, + DSPROPERTY_EAXBUFFER_OBSTRUCTIONLFRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSION, + DSPROPERTY_EAXBUFFER_OCCLUSIONLFRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSIONROOMRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSIONDIRECTRATIO, + DSPROPERTY_EAXBUFFER_EXCLUSION, + DSPROPERTY_EAXBUFFER_EXCLUSIONLFRATIO, + DSPROPERTY_EAXBUFFER_OUTSIDEVOLUMEHF, + DSPROPERTY_EAXBUFFER_DOPPLERFACTOR, + DSPROPERTY_EAXBUFFER_ROLLOFFFACTOR, + DSPROPERTY_EAXBUFFER_ROOMROLLOFFFACTOR, + DSPROPERTY_EAXBUFFER_AIRABSORPTIONFACTOR, + DSPROPERTY_EAXBUFFER_FLAGS +} DSPROPERTY_EAX_BUFFERPROPERTY; + +// OR these flags with property id +#define DSPROPERTY_EAXBUFFER_IMMEDIATE 0x00000000 // changes take effect immediately +#define DSPROPERTY_EAXBUFFER_DEFERRED 0x80000000 // changes take effect later +#define DSPROPERTY_EAXBUFFER_COMMITDEFERREDSETTINGS (DSPROPERTY_EAXBUFFER_NONE | \ + DSPROPERTY_EAXBUFFER_IMMEDIATE) + +// Use this structure for DSPROPERTY_EAXBUFFER_ALLPARAMETERS +// - all levels are hundredths of decibels +// - all delays are in seconds +// +// NOTE: This structure may change in future EAX versions. +// It is recommended to initialize fields by name: +// myBuffer.lDirect = 0; +// myBuffer.lDirectHF = -200; +// ... +// myBuffer.dwFlags = myFlags /* see EAXBUFFERFLAGS below */ ; +// instead of: +// myBuffer = { 0, -200, ... , 0x00000003 }; +// +typedef struct _EAXBUFFERPROPERTIES +{ + long lDirect; // direct path level (at low and mid frequencies) + long lDirectHF; // relative direct path level at high frequencies + long lRoom; // room effect level (at low and mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lObstruction; // main obstruction control (attenuation at high frequencies) + float flObstructionLFRatio; // obstruction low-frequency level re. main control + long lOcclusion; // main occlusion control (attenuation at high frequencies) + float flOcclusionLFRatio; // occlusion low-frequency level re. main control + float flOcclusionRoomRatio; // relative occlusion control for room effect + float flOcclusionDirectRatio; // relative occlusion control for direct path + long lExclusion; // main exlusion control (attenuation at high frequencies) + float flExclusionLFRatio; // exclusion low-frequency level re. main control + long lOutsideVolumeHF; // outside sound cone level at high frequencies + float flDopplerFactor; // like DS3D flDopplerFactor but per source + float flRolloffFactor; // like DS3D flRolloffFactor but per source + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + float flAirAbsorptionFactor; // multiplies DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF + unsigned long ulFlags; // modifies the behavior of properties +} EAXBUFFERPROPERTIES, *LPEAXBUFFERPROPERTIES; + +// Use this structure for DSPROPERTY_EAXBUFFER_OBSTRUCTION, +typedef struct _EAXOBSTRUCTIONPROPERTIES +{ + long lObstruction; + float flObstructionLFRatio; +} EAXOBSTRUCTIONPROPERTIES, *LPEAXOBSTRUCTIONPROPERTIES; + +// Use this structure for DSPROPERTY_EAXBUFFER_OCCLUSION +typedef struct _EAXOCCLUSIONPROPERTIES +{ + long lOcclusion; + float flOcclusionLFRatio; + float flOcclusionRoomRatio; + float flOcclusionDirectRatio; +} EAXOCCLUSIONPROPERTIES, *LPEAXOCCLUSIONPROPERTIES; + +// Use this structure for DSPROPERTY_EAXBUFFER_EXCLUSION +typedef struct _EAXEXCLUSIONPROPERTIES +{ + long lExclusion; + float flExclusionLFRatio; +} EAXEXCLUSIONPROPERTIES, *LPEAXEXCLUSIONPROPERTIES; + +// Used by DSPROPERTY_EAXBUFFER_FLAGS +// TRUE: value is computed automatically - property is an offset +// FALSE: value is used directly +// +// Note: The number and order of flags may change in future EAX versions. +// To insure future compatibility, use flag defines as follows: +// myFlags = EAXBUFFERFLAGS_DIRECTHFAUTO | EAXBUFFERFLAGS_ROOMAUTO; +// instead of: +// myFlags = 0x00000003; +// +#define EAXBUFFERFLAGS_DIRECTHFAUTO 0x00000001 // affects DSPROPERTY_EAXBUFFER_DIRECTHF +#define EAXBUFFERFLAGS_ROOMAUTO 0x00000002 // affects DSPROPERTY_EAXBUFFER_ROOM +#define EAXBUFFERFLAGS_ROOMHFAUTO 0x00000004 // affects DSPROPERTY_EAXBUFFER_ROOMHF + +#define EAXBUFFERFLAGS_RESERVED 0xFFFFFFF8 // reserved future use + +// Property ranges and defaults: + +#define EAXBUFFER_MINDIRECT (-10000) +#define EAXBUFFER_MAXDIRECT 1000 +#define EAXBUFFER_DEFAULTDIRECT 0 + +#define EAXBUFFER_MINDIRECTHF (-10000) +#define EAXBUFFER_MAXDIRECTHF 0 +#define EAXBUFFER_DEFAULTDIRECTHF 0 + +#define EAXBUFFER_MINROOM (-10000) +#define EAXBUFFER_MAXROOM 1000 +#define EAXBUFFER_DEFAULTROOM 0 + +#define EAXBUFFER_MINROOMHF (-10000) +#define EAXBUFFER_MAXROOMHF 0 +#define EAXBUFFER_DEFAULTROOMHF 0 + +#define EAXBUFFER_MINOBSTRUCTION (-10000) +#define EAXBUFFER_MAXOBSTRUCTION 0 +#define EAXBUFFER_DEFAULTOBSTRUCTION 0 + +#define EAXBUFFER_MINOBSTRUCTIONLFRATIO 0.0f +#define EAXBUFFER_MAXOBSTRUCTIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTOBSTRUCTIONLFRATIO 0.0f + +#define EAXBUFFER_MINOCCLUSION (-10000) +#define EAXBUFFER_MAXOCCLUSION 0 +#define EAXBUFFER_DEFAULTOCCLUSION 0 + +#define EAXBUFFER_MINOCCLUSIONLFRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTOCCLUSIONLFRATIO 0.25f + +#define EAXBUFFER_MINOCCLUSIONROOMRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONROOMRATIO 10.0f +#define EAXBUFFER_DEFAULTOCCLUSIONROOMRATIO 1.5f + +#define EAXBUFFER_MINOCCLUSIONDIRECTRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONDIRECTRATIO 10.0f +#define EAXBUFFER_DEFAULTOCCLUSIONDIRECTRATIO 1.0f + +#define EAXBUFFER_MINEXCLUSION (-10000) +#define EAXBUFFER_MAXEXCLUSION 0 +#define EAXBUFFER_DEFAULTEXCLUSION 0 + +#define EAXBUFFER_MINEXCLUSIONLFRATIO 0.0f +#define EAXBUFFER_MAXEXCLUSIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTEXCLUSIONLFRATIO 1.0f + +#define EAXBUFFER_MINOUTSIDEVOLUMEHF (-10000) +#define EAXBUFFER_MAXOUTSIDEVOLUMEHF 0 +#define EAXBUFFER_DEFAULTOUTSIDEVOLUMEHF 0 + +#define EAXBUFFER_MINDOPPLERFACTOR 0.0f +#define EAXBUFFER_MAXDOPPLERFACTOR 10.f +#define EAXBUFFER_DEFAULTDOPPLERFACTOR 0.0f + +#define EAXBUFFER_MINROLLOFFFACTOR 0.0f +#define EAXBUFFER_MAXROLLOFFFACTOR 10.f +#define EAXBUFFER_DEFAULTROLLOFFFACTOR 0.0f + +#define EAXBUFFER_MINROOMROLLOFFFACTOR 0.0f +#define EAXBUFFER_MAXROOMROLLOFFFACTOR 10.f +#define EAXBUFFER_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXBUFFER_MINAIRABSORPTIONFACTOR 0.0f +#define EAXBUFFER_MAXAIRABSORPTIONFACTOR 10.0f +#define EAXBUFFER_DEFAULTAIRABSORPTIONFACTOR 1.0f + +#define EAXBUFFER_DEFAULTFLAGS (EAXBUFFERFLAGS_DIRECTHFAUTO | \ + EAXBUFFERFLAGS_ROOMAUTO | \ + EAXBUFFERFLAGS_ROOMHFAUTO ) + +#pragma pack(pop) + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif diff --git a/Engine/lib/openal/macCarb/al/eax_func.h b/Engine/lib/openal/macCarb/al/eax_func.h new file mode 100644 index 000000000..8c36c7471 --- /dev/null +++ b/Engine/lib/openal/macCarb/al/eax_func.h @@ -0,0 +1,3 @@ + +AL_FUNCTION(ALenum, EAXSet, (const ALGUID*, ALuint, ALuint, ALvoid*, ALuint), return 0; ) +AL_FUNCTION(ALenum, EAXGet, (const ALGUID*, ALuint, ALuint, ALvoid*, ALuint), return 0; ) diff --git a/Engine/lib/openal/macCarb/al/eaxtypes.h b/Engine/lib/openal/macCarb/al/eaxtypes.h new file mode 100644 index 000000000..15ba59afc --- /dev/null +++ b/Engine/lib/openal/macCarb/al/eaxtypes.h @@ -0,0 +1,462 @@ + + +typedef struct _ALGUID +{ + unsigned long Data1; + unsigned short Data2; + unsigned short Data3; + unsigned char Data4[8]; +}ALGUID; + +#ifndef INITGUID + #define DEFINE_ALGUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const ALGUID name +#else + #define DEFINE_ALGUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const ALGUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } +#endif // INITGUID + + + +/* + * EAX 3.0 listener property set {A8FA6880-B476-11d3-BDB9-00C0F02DDF87} + */ +DEFINE_ALGUID(DSPROPSETID_EAX30_ListenerProperties, + 0xa8fa6882, + 0xb476, + 0x11d3, + 0xbd, 0xb9, 0x00, 0xc0, 0xf0, 0x2d, 0xdf, 0x87); + +// For compatibility with future EAX versions: +#define DSPROPSETID_EAX_ListenerProperties DSPROPSETID_EAX30_ListenerProperties + +typedef enum +{ + DSPROPERTY_EAXLISTENER_NONE, + DSPROPERTY_EAXLISTENER_ALLPARAMETERS, + DSPROPERTY_EAXLISTENER_ENVIRONMENT, + DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE, + DSPROPERTY_EAXLISTENER_ENVIRONMENTDIFFUSION, + DSPROPERTY_EAXLISTENER_ROOM, + DSPROPERTY_EAXLISTENER_ROOMHF, + DSPROPERTY_EAXLISTENER_ROOMLF, + DSPROPERTY_EAXLISTENER_DECAYTIME, + DSPROPERTY_EAXLISTENER_DECAYHFRATIO, + DSPROPERTY_EAXLISTENER_DECAYLFRATIO, + DSPROPERTY_EAXLISTENER_REFLECTIONS, + DSPROPERTY_EAXLISTENER_REFLECTIONSDELAY, + DSPROPERTY_EAXLISTENER_REFLECTIONSPAN, + DSPROPERTY_EAXLISTENER_REVERB, + DSPROPERTY_EAXLISTENER_REVERBDELAY, + DSPROPERTY_EAXLISTENER_REVERBPAN, + DSPROPERTY_EAXLISTENER_ECHOTIME, + DSPROPERTY_EAXLISTENER_ECHODEPTH, + DSPROPERTY_EAXLISTENER_MODULATIONTIME, + DSPROPERTY_EAXLISTENER_MODULATIONDEPTH, + DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF, + DSPROPERTY_EAXLISTENER_HFREFERENCE, + DSPROPERTY_EAXLISTENER_LFREFERENCE, + DSPROPERTY_EAXLISTENER_ROOMROLLOFFFACTOR, + DSPROPERTY_EAXLISTENER_FLAGS +} DSPROPERTY_EAX_LISTENERPROPERTY; + +// OR these flags with property id +#define DSPROPERTY_EAXLISTENER_IMMEDIATE 0x00000000 // changes take effect immediately +#define DSPROPERTY_EAXLISTENER_DEFERRED 0x80000000 // changes take effect later +#define DSPROPERTY_EAXLISTENER_COMMITDEFERREDSETTINGS (DSPROPERTY_EAXLISTENER_NONE | \ + DSPROPERTY_EAXLISTENER_IMMEDIATE) + +typedef struct _EAXVECTOR { + float x; + float y; + float z; +} EAXVECTOR; + +// Use this structure for DSPROPERTY_EAXLISTENER_ALLPARAMETERS +// - all levels are hundredths of decibels +// - all times and delays are in seconds +// +// NOTE: This structure may change in future EAX versions. +// It is recommended to initialize fields by name: +// myListener.lRoom = -1000; +// myListener.lRoomHF = -100; +// ... +// myListener.dwFlags = myFlags /* see EAXLISTENERFLAGS below */ ; +// instead of: +// myListener = { -1000, -100, ... , 0x00000009 }; +// If you want to save and load presets in binary form, you +// should define your own structure to insure future compatibility. +// +typedef struct _EAXLISTENERPROPERTIES +{ + unsigned long ulEnvironment; // sets all listener properties + float flEnvironmentSize; // environment size in meters + float flEnvironmentDiffusion; // environment diffusion + long lRoom; // room effect level (at mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lRoomLF; // relative room effect level at low frequencies + float flDecayTime; // reverberation decay time at mid frequencies + float flDecayHFRatio; // high-frequency to mid-frequency decay time ratio + float flDecayLFRatio; // low-frequency to mid-frequency decay time ratio + long lReflections; // early reflections level relative to room effect + float flReflectionsDelay; // initial reflection delay time + EAXVECTOR vReflectionsPan; // early reflections panning vector + long lReverb; // late reverberation level relative to room effect + float flReverbDelay; // late reverberation delay time relative to initial reflection + EAXVECTOR vReverbPan; // late reverberation panning vector + float flEchoTime; // echo time + float flEchoDepth; // echo depth + float flModulationTime; // modulation time + float flModulationDepth; // modulation depth + float flAirAbsorptionHF; // change in level per meter at high frequencies + float flHFReference; // reference high frequency + float flLFReference; // reference low frequency + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + unsigned long ulFlags; // modifies the behavior of properties +} EAXLISTENERPROPERTIES, *LPEAXLISTENERPROPERTIES; + +// used by DSPROPERTY_EAXLISTENER_ENVIRONMENT +enum +{ + EAX_ENVIRONMENT_GENERIC, + EAX_ENVIRONMENT_PADDEDCELL, + EAX_ENVIRONMENT_ROOM, + EAX_ENVIRONMENT_BATHROOM, + EAX_ENVIRONMENT_LIVINGROOM, + EAX_ENVIRONMENT_STONEROOM, + EAX_ENVIRONMENT_AUDITORIUM, + EAX_ENVIRONMENT_CONCERTHALL, + EAX_ENVIRONMENT_CAVE, + EAX_ENVIRONMENT_ARENA, + EAX_ENVIRONMENT_HANGAR, + EAX_ENVIRONMENT_CARPETEDHALLWAY, + EAX_ENVIRONMENT_HALLWAY, + EAX_ENVIRONMENT_STONECORRIDOR, + EAX_ENVIRONMENT_ALLEY, + EAX_ENVIRONMENT_FOREST, + EAX_ENVIRONMENT_CITY, + EAX_ENVIRONMENT_MOUNTAINS, + EAX_ENVIRONMENT_QUARRY, + EAX_ENVIRONMENT_PLAIN, + EAX_ENVIRONMENT_PARKINGLOT, + EAX_ENVIRONMENT_SEWERPIPE, + EAX_ENVIRONMENT_UNDERWATER, + EAX_ENVIRONMENT_DRUGGED, + EAX_ENVIRONMENT_DIZZY, + EAX_ENVIRONMENT_PSYCHOTIC, + + EAX_ENVIRONMENT_UNDEFINED, + + EAX_ENVIRONMENT_COUNT +}; + +// Used by DSPROPERTY_EAXLISTENER_FLAGS +// +// Note: The number and order of flags may change in future EAX versions. +// It is recommended to use the flag defines as follows: +// myFlags = EAXLISTENERFLAGS_DECAYTIMESCALE | EAXLISTENERFLAGS_REVERBSCALE; +// instead of: +// myFlags = 0x00000009; +// +// These flags determine what properties are affected by environment size. +#define EAXLISTENERFLAGS_DECAYTIMESCALE 0x00000001 // reverberation decay time +#define EAXLISTENERFLAGS_REFLECTIONSSCALE 0x00000002 // reflection level +#define EAXLISTENERFLAGS_REFLECTIONSDELAYSCALE 0x00000004 // initial reflection delay time +#define EAXLISTENERFLAGS_REVERBSCALE 0x00000008 // reflections level +#define EAXLISTENERFLAGS_REVERBDELAYSCALE 0x00000010 // late reverberation delay time +#define EAXLISTENERFLAGS_ECHOTIMESCALE 0x00000040 // echo time +#define EAXLISTENERFLAGS_MODULATIONTIMESCALE 0x00000080 // modulation time + +// This flag limits high-frequency decay time according to air absorption. +#define EAXLISTENERFLAGS_DECAYHFLIMIT 0x00000020 + +#define EAXLISTENERFLAGS_RESERVED 0xFFFFFF00 // reserved future use + +// Property ranges and defaults: + +#define EAXLISTENER_MINENVIRONMENT 0 +#define EAXLISTENER_MAXENVIRONMENT (EAX_ENVIRONMENT_COUNT-1) +#define EAXLISTENER_DEFAULTENVIRONMENT EAX_ENVIRONMENT_GENERIC + +#define EAXLISTENER_MINENVIRONMENTSIZE 1.0f +#define EAXLISTENER_MAXENVIRONMENTSIZE 100.0f +#define EAXLISTENER_DEFAULTENVIRONMENTSIZE 7.5f + +#define EAXLISTENER_MINENVIRONMENTDIFFUSION 0.0f +#define EAXLISTENER_MAXENVIRONMENTDIFFUSION 1.0f +#define EAXLISTENER_DEFAULTENVIRONMENTDIFFUSION 1.0f + +#define EAXLISTENER_MINROOM (-10000) +#define EAXLISTENER_MAXROOM 0 +#define EAXLISTENER_DEFAULTROOM (-1000) + +#define EAXLISTENER_MINROOMHF (-10000) +#define EAXLISTENER_MAXROOMHF 0 +#define EAXLISTENER_DEFAULTROOMHF (-100) + +#define EAXLISTENER_MINROOMLF (-10000) +#define EAXLISTENER_MAXROOMLF 0 +#define EAXLISTENER_DEFAULTROOMLF 0 + +#define EAXLISTENER_MINDECAYTIME 0.1f +#define EAXLISTENER_MAXDECAYTIME 20.0f +#define EAXLISTENER_DEFAULTDECAYTIME 1.49f + +#define EAXLISTENER_MINDECAYHFRATIO 0.1f +#define EAXLISTENER_MAXDECAYHFRATIO 2.0f +#define EAXLISTENER_DEFAULTDECAYHFRATIO 0.83f + +#define EAXLISTENER_MINDECAYLFRATIO 0.1f +#define EAXLISTENER_MAXDECAYLFRATIO 2.0f +#define EAXLISTENER_DEFAULTDECAYLFRATIO 1.00f + +#define EAXLISTENER_MINREFLECTIONS (-10000) +#define EAXLISTENER_MAXREFLECTIONS 1000 +#define EAXLISTENER_DEFAULTREFLECTIONS (-2602) + +#define EAXLISTENER_MINREFLECTIONSDELAY 0.0f +#define EAXLISTENER_MAXREFLECTIONSDELAY 0.3f +#define EAXLISTENER_DEFAULTREFLECTIONSDELAY 0.007f + +#define EAXLISTENER_MINREVERB (-10000) +#define EAXLISTENER_MAXREVERB 2000 +#define EAXLISTENER_DEFAULTREVERB 200 + +#define EAXLISTENER_MINREVERBDELAY 0.0f +#define EAXLISTENER_MAXREVERBDELAY 0.1f +#define EAXLISTENER_DEFAULTREVERBDELAY 0.011f + +#define EAXLISTENER_MINECHOTIME 0.075f +#define EAXLISTENER_MAXECHOTIME 0.25f +#define EAXLISTENER_DEFAULTECHOTIME 0.25f + +#define EAXLISTENER_MINECHODEPTH 0.0f +#define EAXLISTENER_MAXECHODEPTH 1.0f +#define EAXLISTENER_DEFAULTECHODEPTH 0.0f + +#define EAXLISTENER_MINMODULATIONTIME 0.04f +#define EAXLISTENER_MAXMODULATIONTIME 4.0f +#define EAXLISTENER_DEFAULTMODULATIONTIME 0.25f + +#define EAXLISTENER_MINMODULATIONDEPTH 0.0f +#define EAXLISTENER_MAXMODULATIONDEPTH 1.0f +#define EAXLISTENER_DEFAULTMODULATIONDEPTH 0.0f + +#define EAXLISTENER_MINAIRABSORPTIONHF (-100.0f) +#define EAXLISTENER_MAXAIRABSORPTIONHF 0.0f +#define EAXLISTENER_DEFAULTAIRABSORPTIONHF (-5.0f) + +#define EAXLISTENER_MINHFREFERENCE 1000.0f +#define EAXLISTENER_MAXHFREFERENCE 20000.0f +#define EAXLISTENER_DEFAULTHFREFERENCE 5000.0f + +#define EAXLISTENER_MINLFREFERENCE 20.0f +#define EAXLISTENER_MAXLFREFERENCE 1000.0f +#define EAXLISTENER_DEFAULTLFREFERENCE 250.0f + +#define EAXLISTENER_MINROOMROLLOFFFACTOR 0.0f +#define EAXLISTENER_MAXROOMROLLOFFFACTOR 10.0f +#define EAXLISTENER_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXLISTENER_DEFAULTFLAGS (EAXLISTENERFLAGS_DECAYTIMESCALE | \ + EAXLISTENERFLAGS_REFLECTIONSSCALE | \ + EAXLISTENERFLAGS_REFLECTIONSDELAYSCALE | \ + EAXLISTENERFLAGS_REVERBSCALE | \ + EAXLISTENERFLAGS_REVERBDELAYSCALE | \ + EAXLISTENERFLAGS_DECAYHFLIMIT) + + + +/* +* EAX 3.0 buffer property set {A8FA6881-B476-11d3-BDB9-00C0F02DDF87} +*/ +DEFINE_ALGUID(DSPROPSETID_EAX30_BufferProperties, + 0xa8fa6881, + 0xb476, + 0x11d3, + 0xbd, 0xb9, 0x0, 0xc0, 0xf0, 0x2d, 0xdf, 0x87); + +// For compatibility with future EAX versions: +#define DSPROPSETID_EAX_BufferProperties DSPROPSETID_EAX30_BufferProperties +#define DSPROPSETID_EAX_SourceProperties DSPROPSETID_EAX30_BufferProperties + +typedef enum +{ + DSPROPERTY_EAXBUFFER_NONE, + DSPROPERTY_EAXBUFFER_ALLPARAMETERS, + DSPROPERTY_EAXBUFFER_OBSTRUCTIONPARAMETERS, + DSPROPERTY_EAXBUFFER_OCCLUSIONPARAMETERS, + DSPROPERTY_EAXBUFFER_EXCLUSIONPARAMETERS, + DSPROPERTY_EAXBUFFER_DIRECT, + DSPROPERTY_EAXBUFFER_DIRECTHF, + DSPROPERTY_EAXBUFFER_ROOM, + DSPROPERTY_EAXBUFFER_ROOMHF, + DSPROPERTY_EAXBUFFER_OBSTRUCTION, + DSPROPERTY_EAXBUFFER_OBSTRUCTIONLFRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSION, + DSPROPERTY_EAXBUFFER_OCCLUSIONLFRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSIONROOMRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSIONDIRECTRATIO, + DSPROPERTY_EAXBUFFER_EXCLUSION, + DSPROPERTY_EAXBUFFER_EXCLUSIONLFRATIO, + DSPROPERTY_EAXBUFFER_OUTSIDEVOLUMEHF, + DSPROPERTY_EAXBUFFER_DOPPLERFACTOR, + DSPROPERTY_EAXBUFFER_ROLLOFFFACTOR, + DSPROPERTY_EAXBUFFER_ROOMROLLOFFFACTOR, + DSPROPERTY_EAXBUFFER_AIRABSORPTIONFACTOR, + DSPROPERTY_EAXBUFFER_FLAGS +} DSPROPERTY_EAX_BUFFERPROPERTY; + +// OR these flags with property id +#define DSPROPERTY_EAXBUFFER_IMMEDIATE 0x00000000 // changes take effect immediately +#define DSPROPERTY_EAXBUFFER_DEFERRED 0x80000000 // changes take effect later +#define DSPROPERTY_EAXBUFFER_COMMITDEFERREDSETTINGS (DSPROPERTY_EAXBUFFER_NONE | \ + DSPROPERTY_EAXBUFFER_IMMEDIATE) + +// Use this structure for DSPROPERTY_EAXBUFFER_ALLPARAMETERS +// - all levels are hundredths of decibels +// - all delays are in seconds +// +// NOTE: This structure may change in future EAX versions. +// It is recommended to initialize fields by name: +// myBuffer.lDirect = 0; +// myBuffer.lDirectHF = -200; +// ... +// myBuffer.dwFlags = myFlags /* see EAXBUFFERFLAGS below */ ; +// instead of: +// myBuffer = { 0, -200, ... , 0x00000003 }; +// +typedef struct _EAXBUFFERPROPERTIES +{ + long lDirect; // direct path level (at low and mid frequencies) + long lDirectHF; // relative direct path level at high frequencies + long lRoom; // room effect level (at low and mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lObstruction; // main obstruction control (attenuation at high frequencies) + float flObstructionLFRatio; // obstruction low-frequency level re. main control + long lOcclusion; // main occlusion control (attenuation at high frequencies) + float flOcclusionLFRatio; // occlusion low-frequency level re. main control + float flOcclusionRoomRatio; // relative occlusion control for room effect + float flOcclusionDirectRatio; // relative occlusion control for direct path + long lExclusion; // main exlusion control (attenuation at high frequencies) + float flExclusionLFRatio; // exclusion low-frequency level re. main control + long lOutsideVolumeHF; // outside sound cone level at high frequencies + float flDopplerFactor; // like DS3D flDopplerFactor but per source + float flRolloffFactor; // like DS3D flRolloffFactor but per source + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + float flAirAbsorptionFactor; // multiplies DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF + unsigned long ulFlags; // modifies the behavior of properties +} EAXBUFFERPROPERTIES, *LPEAXBUFFERPROPERTIES; + +// Use this structure for DSPROPERTY_EAXBUFFER_OBSTRUCTION, +typedef struct _EAXOBSTRUCTIONPROPERTIES +{ + long lObstruction; + float flObstructionLFRatio; +} EAXOBSTRUCTIONPROPERTIES, *LPEAXOBSTRUCTIONPROPERTIES; + +// Use this structure for DSPROPERTY_EAXBUFFER_OCCLUSION +typedef struct _EAXOCCLUSIONPROPERTIES +{ + long lOcclusion; + float flOcclusionLFRatio; + float flOcclusionRoomRatio; + float flOcclusionDirectRatio; +} EAXOCCLUSIONPROPERTIES, *LPEAXOCCLUSIONPROPERTIES; + +// Use this structure for DSPROPERTY_EAXBUFFER_EXCLUSION +typedef struct _EAXEXCLUSIONPROPERTIES +{ + long lExclusion; + float flExclusionLFRatio; +} EAXEXCLUSIONPROPERTIES, *LPEAXEXCLUSIONPROPERTIES; + +// Used by DSPROPERTY_EAXBUFFER_FLAGS +// TRUE: value is computed automatically - property is an offset +// FALSE: value is used directly +// +// Note: The number and order of flags may change in future EAX versions. +// To insure future compatibility, use flag defines as follows: +// myFlags = EAXBUFFERFLAGS_DIRECTHFAUTO | EAXBUFFERFLAGS_ROOMAUTO; +// instead of: +// myFlags = 0x00000003; +// +#define EAXBUFFERFLAGS_DIRECTHFAUTO 0x00000001 // affects DSPROPERTY_EAXBUFFER_DIRECTHF +#define EAXBUFFERFLAGS_ROOMAUTO 0x00000002 // affects DSPROPERTY_EAXBUFFER_ROOM +#define EAXBUFFERFLAGS_ROOMHFAUTO 0x00000004 // affects DSPROPERTY_EAXBUFFER_ROOMHF + +#define EAXBUFFERFLAGS_RESERVED 0xFFFFFFF8 // reserved future use + +// Property ranges and defaults: + +#define EAXBUFFER_MINDIRECT (-10000) +#define EAXBUFFER_MAXDIRECT 1000 +#define EAXBUFFER_DEFAULTDIRECT 0 + +#define EAXBUFFER_MINDIRECTHF (-10000) +#define EAXBUFFER_MAXDIRECTHF 0 +#define EAXBUFFER_DEFAULTDIRECTHF 0 + +#define EAXBUFFER_MINROOM (-10000) +#define EAXBUFFER_MAXROOM 1000 +#define EAXBUFFER_DEFAULTROOM 0 + +#define EAXBUFFER_MINROOMHF (-10000) +#define EAXBUFFER_MAXROOMHF 0 +#define EAXBUFFER_DEFAULTROOMHF 0 + +#define EAXBUFFER_MINOBSTRUCTION (-10000) +#define EAXBUFFER_MAXOBSTRUCTION 0 +#define EAXBUFFER_DEFAULTOBSTRUCTION 0 + +#define EAXBUFFER_MINOBSTRUCTIONLFRATIO 0.0f +#define EAXBUFFER_MAXOBSTRUCTIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTOBSTRUCTIONLFRATIO 0.0f + +#define EAXBUFFER_MINOCCLUSION (-10000) +#define EAXBUFFER_MAXOCCLUSION 0 +#define EAXBUFFER_DEFAULTOCCLUSION 0 + +#define EAXBUFFER_MINOCCLUSIONLFRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTOCCLUSIONLFRATIO 0.25f + +#define EAXBUFFER_MINOCCLUSIONROOMRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONROOMRATIO 10.0f +#define EAXBUFFER_DEFAULTOCCLUSIONROOMRATIO 1.5f + +#define EAXBUFFER_MINOCCLUSIONDIRECTRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONDIRECTRATIO 10.0f +#define EAXBUFFER_DEFAULTOCCLUSIONDIRECTRATIO 1.0f + +#define EAXBUFFER_MINEXCLUSION (-10000) +#define EAXBUFFER_MAXEXCLUSION 0 +#define EAXBUFFER_DEFAULTEXCLUSION 0 + +#define EAXBUFFER_MINEXCLUSIONLFRATIO 0.0f +#define EAXBUFFER_MAXEXCLUSIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTEXCLUSIONLFRATIO 1.0f + +#define EAXBUFFER_MINOUTSIDEVOLUMEHF (-10000) +#define EAXBUFFER_MAXOUTSIDEVOLUMEHF 0 +#define EAXBUFFER_DEFAULTOUTSIDEVOLUMEHF 0 + +#define EAXBUFFER_MINDOPPLERFACTOR 0.0f +#define EAXBUFFER_MAXDOPPLERFACTOR 10.f +#define EAXBUFFER_DEFAULTDOPPLERFACTOR 0.0f + +#define EAXBUFFER_MINROLLOFFFACTOR 0.0f +#define EAXBUFFER_MAXROLLOFFFACTOR 10.f +#define EAXBUFFER_DEFAULTROLLOFFFACTOR 0.0f + +#define EAXBUFFER_MINROOMROLLOFFFACTOR 0.0f +#define EAXBUFFER_MAXROOMROLLOFFFACTOR 10.f +#define EAXBUFFER_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXBUFFER_MINAIRABSORPTIONFACTOR 0.0f +#define EAXBUFFER_MAXAIRABSORPTIONFACTOR 10.0f +#define EAXBUFFER_DEFAULTAIRABSORPTIONFACTOR 1.0f + +#define EAXBUFFER_DEFAULTFLAGS (EAXBUFFERFLAGS_DIRECTHFAUTO | \ + EAXBUFFERFLAGS_ROOMAUTO | \ + EAXBUFFERFLAGS_ROOMHFAUTO ) diff --git a/Engine/lib/openal/macCarb/openALFn.h b/Engine/lib/openal/macCarb/openALFn.h new file mode 100644 index 000000000..0061ec502 --- /dev/null +++ b/Engine/lib/openal/macCarb/openALFn.h @@ -0,0 +1,80 @@ +#ifndef AL_FUNCTION +#define AL_FUNCTION(fn_name) +#endif + +// AL Functions +AL_FUNCTION( alEnable ); +AL_FUNCTION( alDisable ); +AL_FUNCTION( alIsEnabled ); +AL_FUNCTION( alHint ); +AL_FUNCTION( alGetBoolean ); +AL_FUNCTION( alGetInteger ); +AL_FUNCTION( alGetFloat ); +AL_FUNCTION( alGetDouble ); +AL_FUNCTION( alGetBooleanv ); +AL_FUNCTION( alGetIntegerv ); +AL_FUNCTION( alGetFloatv ); +AL_FUNCTION( alGetDoublev ); +AL_FUNCTION( alGetString ); +AL_FUNCTION( alGetError ); +AL_FUNCTION( alIsExtensionPresent ); +AL_FUNCTION( alGetProcAddress ); +AL_FUNCTION( alGetEnumValue ); +AL_FUNCTION( alListeneri ); +AL_FUNCTION( alListenerf ); +AL_FUNCTION( alListener3f ); +AL_FUNCTION( alListenerfv ); +AL_FUNCTION( alGetListeneri ); +AL_FUNCTION( alGetListenerf ); +AL_FUNCTION( alGetListener3f ); +AL_FUNCTION( alGetListenerfv ); +AL_FUNCTION( alGenSources ); +AL_FUNCTION( alDeleteSources ); +AL_FUNCTION( alIsSource ); +AL_FUNCTION( alSourcei ); +AL_FUNCTION( alSourcef ); +AL_FUNCTION( alSource3f ); +AL_FUNCTION( alSourcefv ); +AL_FUNCTION( alGetSourcei ); +AL_FUNCTION( alGetSourcef ); +AL_FUNCTION( alGetSource3f ); +AL_FUNCTION( alGetSourcefv ); +AL_FUNCTION( alSourcePlayv ); +AL_FUNCTION( alSourcePausev ); +AL_FUNCTION( alSourceStopv ); +AL_FUNCTION( alSourceRewindv ); +AL_FUNCTION( alSourcePlay ); +AL_FUNCTION( alSourcePause ); +AL_FUNCTION( alSourceStop ); +AL_FUNCTION( alSourceRewind ); +AL_FUNCTION( alGenBuffers ); +AL_FUNCTION( alDeleteBuffers ); +AL_FUNCTION( alIsBuffer ); +AL_FUNCTION( alBufferData ); +AL_FUNCTION( alGetBufferi ); +AL_FUNCTION( alGetBufferf ); +AL_FUNCTION( alSourceQueueBuffers ); +AL_FUNCTION( alSourceUnqueueBuffers ); +AL_FUNCTION( alDistanceModel ); +AL_FUNCTION( alDopplerFactor ); +AL_FUNCTION( alDopplerVelocity ); + +// ALC Functions +AL_FUNCTION( alcGetString ); +AL_FUNCTION( alcGetIntegerv ); +AL_FUNCTION( alcOpenDevice ); +AL_FUNCTION( alcCloseDevice ); +AL_FUNCTION( alcCreateContext ); +AL_FUNCTION( alcMakeContextCurrent ); +AL_FUNCTION( alcProcessContext ); +AL_FUNCTION( alcGetCurrentContext ); +AL_FUNCTION( alcGetContextsDevice ); +AL_FUNCTION( alcSuspendContext ); +AL_FUNCTION( alcDestroyContext ); +AL_FUNCTION( alcGetError ); +AL_FUNCTION( alcIsExtensionPresent ); +AL_FUNCTION( alcGetProcAddress ); +AL_FUNCTION( alcGetEnumValue ); + + +#undef AL_FUNCTION diff --git a/Engine/lib/openal/macosx/OpenAL.framework/Headers b/Engine/lib/openal/macosx/OpenAL.framework/Headers new file mode 100644 index 000000000..e1e95c912 --- /dev/null +++ b/Engine/lib/openal/macosx/OpenAL.framework/Headers @@ -0,0 +1 @@ +link Versions/Current/Headers/ \ No newline at end of file diff --git a/Engine/lib/openal/macosx/OpenAL.framework/OpenAL b/Engine/lib/openal/macosx/OpenAL.framework/OpenAL new file mode 100644 index 000000000..f960e8d7e --- /dev/null +++ b/Engine/lib/openal/macosx/OpenAL.framework/OpenAL @@ -0,0 +1 @@ +link Versions/Current/OpenAL \ No newline at end of file diff --git a/Engine/lib/openal/macosx/OpenAL.framework/Resources b/Engine/lib/openal/macosx/OpenAL.framework/Resources new file mode 100644 index 000000000..73f3c1806 --- /dev/null +++ b/Engine/lib/openal/macosx/OpenAL.framework/Resources @@ -0,0 +1 @@ +link Versions/Current/Resources/ \ No newline at end of file diff --git a/Engine/lib/openal/macosx/OpenAL.framework/Versions/A/Headers/al.h b/Engine/lib/openal/macosx/OpenAL.framework/Versions/A/Headers/al.h new file mode 100644 index 000000000..1e7a8bd80 --- /dev/null +++ b/Engine/lib/openal/macosx/OpenAL.framework/Versions/A/Headers/al.h @@ -0,0 +1,498 @@ +#ifndef _AL_H_ +#define _AL_H_ + +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2000 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#include "altypes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 + #ifdef _LIB + #define ALAPI __declspec(dllexport) + #else + #define ALAPI __declspec(dllimport) + #endif + #define ALAPIENTRY __cdecl + #define AL_CALLBACK +#else + #ifdef TARGET_OS_MAC + #if TARGET_OS_MAC + #pragma export on + #endif + #endif + #define ALAPI + #define ALAPIENTRY + #define AL_CALLBACK +#endif + +#define OPENAL + +#ifndef AL_NO_PROTOTYPES + +/** + * OpenAL Maintenance Functions + * Initialization and exiting. + * State Management and Query. + * Error Handling. + * Extension Support. + */ + +/** State management. */ +ALAPI ALvoid ALAPIENTRY alEnable( ALenum capability ); +ALAPI ALvoid ALAPIENTRY alDisable( ALenum capability ); +ALAPI ALboolean ALAPIENTRY alIsEnabled( ALenum capability ); + +/** Application preferences for driver performance choices. */ +ALAPI ALvoid ALAPIENTRY alHint( ALenum target, ALenum mode ); + +/** State retrieval. */ +ALAPI ALboolean ALAPIENTRY alGetBoolean( ALenum param ); +ALAPI ALint ALAPIENTRY alGetInteger( ALenum param ); +ALAPI ALfloat ALAPIENTRY alGetFloat( ALenum param ); +ALAPI ALdouble ALAPIENTRY alGetDouble( ALenum param ); +ALAPI ALvoid ALAPIENTRY alGetBooleanv( ALenum param, ALboolean* data ); +ALAPI ALvoid ALAPIENTRY alGetIntegerv( ALenum param, ALint* data ); +ALAPI ALvoid ALAPIENTRY alGetFloatv( ALenum param, ALfloat* data ); +ALAPI ALvoid ALAPIENTRY alGetDoublev( ALenum param, ALdouble* data ); +ALAPI ALubyte* ALAPIENTRY alGetString( ALenum param ); + +ALAPI ALvoid ALAPIENTRY alSetInteger( ALenum pname, ALint value ); +ALAPI ALvoid ALAPIENTRY alSetDouble( ALenum pname, ALdouble value ); + +/** + * Error support. + * Obtain the most recent error generated in the AL state machine. + */ +ALAPI ALenum ALAPIENTRY alGetError( void ); + + +/** + * Extension support. + * Obtain the address of a function (usually an extension) + * with the name fname. All addresses are context-independent. + */ +ALAPI ALboolean ALAPIENTRY alIsExtensionPresent( ALubyte* fname ); + + +/** + * Extension support. + * Obtain the address of a function (usually an extension) + * with the name fname. All addresses are context-independent. + */ +ALAPI ALvoid* ALAPIENTRY alGetProcAddress( ALubyte* fname ); + + +/** + * Extension support. + * Obtain the integer value of an enumeration (usually an extension) with the name ename. + */ +ALAPI ALenum ALAPIENTRY alGetEnumValue( ALubyte* ename ); + + + + +/** + * LISTENER + * Listener is the sample position for a given context. + * The multi-channel (usually stereo) output stream generated + * by the mixer is parametrized by this Listener object: + * its position and velocity relative to Sources, within + * occluder and reflector geometry. + */ + + + +/** + * + * Listener Environment: default 0. + */ +ALAPI ALvoid ALAPIENTRY alListeneri( ALenum param, ALint value ); + + +/** + * + * Listener Gain: default 1.0f. + */ +ALAPI ALvoid ALAPIENTRY alListenerf( ALenum param, ALfloat value ); + + +/** + * + * Listener Position. + * Listener Velocity. + */ +ALAPI ALvoid ALAPIENTRY alListener3f( ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 ); + + +/** + * + * Listener Position: ALfloat[3] + * Listener Velocity: ALfloat[3] + * Listener Orientation: ALfloat[6] (forward and up vector). + */ +ALAPI ALvoid ALAPIENTRY alListenerfv( ALenum param, ALfloat* values ); + +ALAPI ALvoid ALAPIENTRY alGetListeneri( ALenum param, ALint* value ); +ALAPI ALvoid ALAPIENTRY alGetListenerf( ALenum param, ALfloat* value ); +ALAPI ALvoid ALAPIENTRY alGetListener3f( ALenum param, ALfloat* v1, ALfloat* v2, ALfloat* v3 ); +ALAPI ALvoid ALAPIENTRY alGetListenerfv( ALenum param, ALfloat* values ); + + +/** + * SOURCE + * Source objects are by default localized. Sources + * take the PCM data provided in the specified Buffer, + * apply Source-specific modifications, and then + * submit them to be mixed according to spatial + * arrangement etc. + */ + + + +/** Create Source objects. */ +ALAPI ALvoid ALAPIENTRY alGenSources( ALsizei n, ALuint* sources ); + +/** Delete Source objects. */ +ALAPI ALvoid ALAPIENTRY alDeleteSources( ALsizei n, ALuint* sources ); + +/** Verify a handle is a valid Source. */ +ALAPI ALboolean ALAPIENTRY alIsSource( ALuint id ); + +/** Set an integer parameter for a Source object. */ +ALAPI ALvoid ALAPIENTRY alSourcei( ALuint source, ALenum param, ALint value ); +ALAPI ALvoid ALAPIENTRY alSourcef( ALuint source, ALenum param, ALfloat value ); +ALAPI ALvoid ALAPIENTRY alSource3f( ALuint source, ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 ); +ALAPI ALvoid ALAPIENTRY alSourcefv( ALuint source, ALenum param, ALfloat* values ); + +/** Get an integer parameter for a Source object. */ +ALAPI ALvoid ALAPIENTRY alGetSourcei( ALuint source, ALenum param, ALint* value ); +ALAPI ALvoid ALAPIENTRY alGetSourcef( ALuint source, ALenum param, ALfloat* value ); +ALAPI ALvoid ALAPIENTRY alGetSource3f( ALuint source, ALenum param, ALfloat* v1, ALfloat* v2, ALfloat* v3 ); +ALAPI ALvoid ALAPIENTRY alGetSourcefv( ALuint source, ALenum param, ALfloat* values ); + +ALAPI ALvoid ALAPIENTRY alSourcePlayv( ALsizei n, ALuint *sources ); +ALAPI ALvoid ALAPIENTRY alSourcePausev( ALsizei n, ALuint *sources ); +ALAPI ALvoid ALAPIENTRY alSourceStopv( ALsizei n, ALuint *sources ); +ALAPI ALvoid ALAPIENTRY alSourceRewindv(ALsizei n,ALuint *sources); + +/** Activate a source, start replay. */ +ALAPI ALvoid ALAPIENTRY alSourcePlay( ALuint source ); + +/** + * Pause a source, + * temporarily remove it from the mixer list. + */ +ALAPI ALvoid ALAPIENTRY alSourcePause( ALuint source ); + +/** + * Stop a source, + * temporarily remove it from the mixer list, + * and reset its internal state to pre-Play. + * To remove a Source completely, it has to be + * deleted following Stop, or before Play. + */ +ALAPI ALvoid ALAPIENTRY alSourceStop( ALuint source ); + +/** + * Rewinds a source, + * temporarily remove it from the mixer list, + * and reset its internal state to pre-Play. + */ +ALAPI ALvoid ALAPIENTRY alSourceRewind( ALuint source ); + + + +/** + * BUFFER + * Buffer objects are storage space for sample data. + * Buffers are referred to by Sources. There can be more than + * one Source using the same Buffer data. If Buffers have + * to be duplicated on a per-Source basis, the driver has to + * take care of allocation, copying, and deallocation as well + * as propagating buffer data changes. + */ + + + + +/** Buffer object generation. */ +ALAPI ALvoid ALAPIENTRY alGenBuffers( ALsizei n, ALuint* buffers ); +ALAPI ALvoid ALAPIENTRY alDeleteBuffers( ALsizei n, ALuint* buffers ); +ALAPI ALboolean ALAPIENTRY alIsBuffer( ALuint buffer ); + +/** + * Specify the data to be filled into a buffer. + */ +ALAPI ALvoid ALAPIENTRY alBufferData( ALuint buffer, + ALenum format, + ALvoid* data, + ALsizei size, + ALsizei freq ); + + +ALAPI ALvoid ALAPIENTRY alGetBufferi( ALuint buffer, ALenum param, ALint* value ); +ALAPI ALvoid ALAPIENTRY alGetBufferf( ALuint buffer, ALenum param, ALfloat* value ); + + + + +/** + * Queue stuff + */ + +ALAPI ALvoid ALAPIENTRY alSourceQueueBuffers( ALuint source, ALsizei n, ALuint* buffers ); +ALAPI ALvoid ALAPIENTRY alSourceUnqueueBuffers( ALuint source, ALsizei n, ALuint* buffers ); + +/** + * Knobs and dials + */ +ALAPI ALvoid ALAPIENTRY alDistanceModel( ALenum value ); +ALAPI ALvoid ALAPIENTRY alDopplerFactor( ALfloat value ); +ALAPI ALvoid ALAPIENTRY alDopplerVelocity( ALfloat value ); + +#else /* AL_NO_PROTOTYPES */ + +/** + * OpenAL Maintenance Functions + * Initialization and exiting. + * State Management and Query. + * Error Handling. + * Extension Support. + */ + +/** State management. */ +ALAPI ALvoid ALAPIENTRY (*alEnable)( ALenum capability ); +ALAPI ALvoid ALAPIENTRY (*alDisable)( ALenum capability ); +ALAPI ALboolean ALAPIENTRY (*alIsEnabled)( ALenum capability ); + +/** Application preferences for driver performance choices. */ +ALAPI ALvoid ALAPIENTRY (*alHint)( ALenum target, ALenum mode ); + +/** State retrieval. */ +ALAPI ALboolean ALAPIENTRY (*alGetBoolean)( ALenum param ); +ALAPI ALint ALAPIENTRY (*alGetInteger)( ALenum param ); +ALAPI ALfloat ALAPIENTRY (*alGetFloat)( ALenum param ); +ALAPI ALdouble ALAPIENTRY (*alGetDouble)( ALenum param ); +ALAPI ALvoid ALAPIENTRY (*alGetBooleanv)( ALenum param, ALboolean* data ); +ALAPI ALvoid ALAPIENTRY (*alGetIntegerv)( ALenum param, ALint* data ); +ALAPI ALvoid ALAPIENTRY (*alGetFloatv)( ALenum param, ALfloat* data ); +ALAPI ALvoid ALAPIENTRY (*alGetDoublev)( ALenum param, ALdouble* data ); +ALAPI ALubyte* ALAPIENTRY (*alGetString)( ALenum param ); + +ALAPI ALvoid ALAPIENTRY (*alSetInteger)( ALenum pname, ALint value ); +ALAPI ALvoid ALAPIENTRY (*alSetDouble)( ALenum pname, ALdouble value ); + +/** + * Error support. + * Obtain the most recent error generated in the AL state machine. + */ +ALAPI ALenum ALAPIENTRY (*alGetError)( ALvoid ); + + +/** + * Extension support. + * Obtain the address of a function (usually an extension) + * with the name fname. All addresses are context-independent. + */ +ALAPI ALboolean ALAPIENTRY (*alIsExtensionPresent)( ALubyte* fname ); + + +/** + * Extension support. + * Obtain the address of a function (usually an extension) + * with the name fname. All addresses are context-independent. + */ +ALAPI ALvoid* ALAPIENTRY (*alGetProcAddress)( ALubyte* fname ); + + +/** + * Extension support. + * Obtain the integer value of an enumeration (usually an extension) with the name ename. + */ +ALAPI ALenum ALAPIENTRY (*alGetEnumValue)( ALubyte* ename ); + + + + +/** + * LISTENER + * Listener is the sample position for a given context. + * The multi-channel (usually stereo) output stream generated + * by the mixer is parametrized by this Listener object: + * its position and velocity relative to Sources, within + * occluder and reflector geometry. + */ + + + +/** + * + * Listener Environment: default 0. + */ +ALAPI ALvoid ALAPIENTRY (*alListeneri)( ALenum param, ALint value ); + + +/** + * + * Listener Gain: default 1.0f. + */ +ALAPI ALvoid ALAPIENTRY (*alListenerf)( ALenum param, ALfloat value ); + + +/** + * + * Listener Position. + * Listener Velocity. + */ +ALAPI ALvoid ALAPIENTRY (*alListener3f)( ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 ); + + +/** + * + * Listener Position: ALfloat[3] + * Listener Velocity: ALfloat[3] + * Listener Orientation: ALfloat[6] (forward and up vector). + */ +ALAPI ALvoid ALAPIENTRY (*alListenerfv)( ALenum param, ALfloat* values ); + +ALAPI ALvoid ALAPIENTRY (*alGetListeneri)( ALenum param, ALint* value ); +ALAPI ALvoid ALAPIENTRY (*alGetListenerf)( ALenum param, ALfloat* value ); +ALAPI ALvoid ALAPIENTRY (*alGetListener3f)( ALenum param, ALfloat* v1, ALfloat* v2, ALfloat* v3 ); +ALAPI ALvoid ALAPIENTRY (*alGetListenerfv)( ALenum param, ALfloat* values ); + + +/** + * SOURCE + * Source objects are by default localized. Sources + * take the PCM data provided in the specified Buffer, + * apply Source-specific modifications, and then + * submit them to be mixed according to spatial + * arrangement etc. + */ + + + +/** Create Source objects. */ +ALAPI ALvoid ALAPIENTRY (*alGenSources)( ALsizei n, ALuint* sources ); + +/** Delete Source objects. */ +ALAPI ALvoid ALAPIENTRY (*alDeleteSources)( ALsizei n, ALuint* sources ); + +/** Verify a handle is a valid Source. */ +ALAPI ALboolean ALAPIENTRY (*alIsSource)( ALuint id ); + +/** Set an integer parameter for a Source object. */ +ALAPI ALvoid ALAPIENTRY (*alSourcei)( ALuint source, ALenum param, ALint value ); +ALAPI ALvoid ALAPIENTRY (*alSourcef)( ALuint source, ALenum param, ALfloat value ); +ALAPI ALvoid ALAPIENTRY (*alSource3f)( ALuint source, ALenum param, ALfloat v1, ALfloat v2, ALfloat v3 ); +ALAPI ALvoid ALAPIENTRY (*alSourcefv)( ALuint source, ALenum param, ALfloat* values ); + +/** Get an integer parameter for a Source object. */ +ALAPI ALvoid ALAPIENTRY (*alGetSourcei)( ALuint source, ALenum param, ALint* value ); +ALAPI ALvoid ALAPIENTRY (*alGetSourcef)( ALuint source, ALenum param, ALfloat* value ); +ALAPI ALvoid ALAPIENTRY (*alGetSourcefv)( ALuint source, ALenum param, ALfloat* values ); + +ALAPI ALvoid ALAPIENTRY (*alSourcePlayv)( ALsizei n, ALuint *sources ); +ALAPI ALvoid ALAPIENTRY (*alSourceStopv)( ALsizei n, ALuint *sources ); + +/** Activate a source, start replay. */ +ALAPI ALvoid ALAPIENTRY (*alSourcePlay)( ALuint source ); + +/** + * Pause a source, + * temporarily remove it from the mixer list. + */ +ALAPI ALvoid ALAPIENTRY (*alSourcePause)( ALuint source ); + +/** + * Stop a source, + * temporarily remove it from the mixer list, + * and reset its internal state to pre-Play. + * To remove a Source completely, it has to be + * deleted following Stop, or before Play. + */ +ALAPI ALvoid ALAPIENTRY (*alSourceStop)( ALuint source ); + + + +/** + * BUFFER + * Buffer objects are storage space for sample data. + * Buffers are referred to by Sources. There can be more than + * one Source using the same Buffer data. If Buffers have + * to be duplicated on a per-Source basis, the driver has to + * take care of allocation, copying, and deallocation as well + * as propagating buffer data changes. + */ + + + + +/** Buffer object generation. */ +ALAPI ALvoid ALAPIENTRY (*alGenBuffers)( ALsizei n, ALuint* buffers ); +ALAPI ALvoid ALAPIENTRY (*alDeleteBuffers)( ALsizei n, ALuint* buffers ); +ALAPI ALboolean ALAPIENTRY (*alIsBuffer)( ALuint buffer ); + +/** + * Specify the data to be filled into a buffer. + */ +ALAPI ALvoid ALAPIENTRY (*alBufferData)( ALuint buffer, + ALenum format, + ALvoid* data, + ALsizei size, + ALsizei freq ); + +ALAPI ALvoid ALAPIENTRY (*alGetBufferi)( ALuint buffer, ALenum param, ALint* value ); +ALAPI ALvoid ALAPIENTRY (*alGetBufferf)( ALuint buffer, ALenum param, ALfloat* value ); + + + + +/** + * Queue stuff + */ +ALAPI ALvoid ALAPIENTRY (*alSourceQueueBuffers)( ALuint source, ALsizei n, ALuint* buffers ); +ALAPI ALvoid ALAPIENTRY (*alSourceUnqueueBuffers)( ALuint source, ALsizei n, ALuint* buffers ); + +/** + * Knobs and dials + */ +ALAPI ALvoid ALAPIENTRY (*alDistanceModel)( ALenum value ); +ALAPI ALvoid ALAPIENTRY (*alDopplerFactor)( ALfloat value ); +ALAPI ALvoid ALAPIENTRY (*alDopplerVelocity)( ALfloat value ); + + +#endif /* AL_NO_PROTOTYPES */ + +#ifdef TARGET_OS_MAC + #if TARGET_OS_MAC + #pragma export off + #endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Engine/lib/openal/macosx/OpenAL.framework/Versions/A/Headers/alc.h b/Engine/lib/openal/macosx/OpenAL.framework/Versions/A/Headers/alc.h new file mode 100644 index 000000000..62601d1d2 --- /dev/null +++ b/Engine/lib/openal/macosx/OpenAL.framework/Versions/A/Headers/alc.h @@ -0,0 +1,88 @@ +#ifndef _ALC_H_ +#define _ALC_H_ + +#include "altypes.h" +#include "alctypes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 + #ifdef _LIB + #define ALCAPI __declspec(dllexport) + #else + #define ALCAPI __declspec(dllimport) + typedef ALCvoid ALCdevice; + typedef ALCvoid ALCcontext; + #endif + #define ALCAPIENTRY __cdecl +#else + #ifdef TARGET_OS_MAC + #if TARGET_OS_MAC + #pragma export on + #endif + #endif + #define ALCAPI + #define ALCAPIENTRY + typedef ALCvoid ALCdevice; + typedef ALCvoid ALCcontext; +#endif + +#ifndef ALC_NO_PROTOTYPES + +ALCAPI ALCubyte* ALCAPIENTRY alcGetString(ALCdevice *device,ALCenum param); +ALCAPI ALCvoid ALCAPIENTRY alcGetIntegerv(ALCdevice *device,ALCenum param,ALCsizei size,ALCint *data); + +ALCAPI ALCdevice* ALCAPIENTRY alcOpenDevice(ALCubyte *deviceName); +ALCAPI ALCvoid ALCAPIENTRY alcCloseDevice(ALCdevice *device); + +ALCAPI ALCcontext*ALCAPIENTRY alcCreateContext(ALCdevice *device,ALCint *attrList); +ALCAPI ALCboolean ALCAPIENTRY alcMakeContextCurrent(ALCcontext *context); +ALCAPI ALCvoid ALCAPIENTRY alcProcessContext(ALCcontext *context); +ALCAPI ALCcontext*ALCAPIENTRY alcGetCurrentContext(void); +ALCAPI ALCdevice* ALCAPIENTRY alcGetContextsDevice(ALCcontext *context); +ALCAPI ALCvoid ALCAPIENTRY alcSuspendContext(ALCcontext *context); +ALCAPI ALCvoid ALCAPIENTRY alcDestroyContext(ALCcontext *context); + +ALCAPI ALCenum ALCAPIENTRY alcGetError(ALCdevice *device); + +ALCAPI ALCboolean ALCAPIENTRY alcIsExtensionPresent(ALCdevice *device,ALCubyte *extName); +ALCAPI ALCvoid * ALCAPIENTRY alcGetProcAddress(ALCdevice *device,ALCubyte *funcName); +ALCAPI ALCenum ALCAPIENTRY alcGetEnumValue(ALCdevice *device,ALCubyte *enumName); + +#else /* AL_NO_PROTOTYPES */ + +ALCAPI ALCubyte* ALCAPIENTRY (*alcGetString)(ALCdevice *device,ALCenum param); +ALCAPI ALCvoid ALCAPIENTRY (*alcGetIntegerv)(ALCdevice * device,ALCenum param,ALCsizei size,ALCint *data); + +ALCAPI ALCdevice* ALCAPIENTRY (*alcOpenDevice)(ALubyte *deviceName); +ALCAPI ALCvoid ALCAPIENTRY (*alcCloseDevice)(ALCdevice *device); + +ALCAPI ALCcontext*ALCAPIENTRY (*alcCreateContext)(ALCdevice *device,ALCint *attrList); +ALCAPI ALCboolean ALCAPIENTRY (*alcMakeContextCurrent)(ALCcontext *context); +ALCAPI ALCvoid ALCAPIENTRY (*alcProcessContext)(ALCcontext *context); +ALCAPI ALCcontext*ALCAPIENTRY (*alcGetCurrentContext)(ALCvoid); +ALCAPI ALCdevice* ALCAPIENTRY (*alcGetContextsDevice)(ALCcontext *context); +ALCAPI ALCvoid ALCAPIENTRY (*alcSuspendContext)(ALCcontext *context); +ALCAPI ALCvoid ALCAPIENTRY (*alcDestroyContext)(ALCcontext *context); + +ALCAPI ALCenum ALCAPIENTRY (*alcGetError)(ALCdevice *device); + +ALCAPI ALCboolean ALCAPIENTRY (*alcIsExtensionPresent)(ALCdevice *device,ALCubyte *extName); +ALCAPI ALCvoid * ALCAPIENTRY (*alcGetProcAddress)(ALCdevice *device,ALCubyte *funcName); +ALCAPI ALCenum ALCAPIENTRY (*alcGetEnumValue)(ALCdevice *device,ALCubyte *enumName); + +#endif /* AL_NO_PROTOTYPES */ + +#ifdef TARGET_OS_MAC + #if TARGET_OS_MAC + #pragma export off + #endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Engine/lib/openal/macosx/OpenAL.framework/Versions/A/Headers/alctypes.h b/Engine/lib/openal/macosx/OpenAL.framework/Versions/A/Headers/alctypes.h new file mode 100644 index 000000000..90364609d --- /dev/null +++ b/Engine/lib/openal/macosx/OpenAL.framework/Versions/A/Headers/alctypes.h @@ -0,0 +1,165 @@ +#ifndef _ALCTYPES_H_ +#define _ALCTYPES_H_ + +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2000 by authors. + * Portions Copyright (C) 2004 by Apple Computer Inc. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + + +#ifdef __cplusplus +extern "C" { +#endif + +/** ALC boolean type. */ +typedef char ALCboolean; + +/** ALC 8bit signed byte. */ +typedef char ALCbyte; + +/** ALC 8bit unsigned byte. */ +typedef unsigned char ALCubyte; + +/** ALC 16bit signed short integer type. */ +typedef short ALCshort; + +/** ALC 16bit unsigned short integer type. */ +typedef unsigned short ALCushort; + +/** ALC 32bit unsigned integer type. */ +typedef unsigned ALCuint; + +/** ALC 32bit signed integer type. */ +typedef int ALCint; + +/** ALC 32bit floating point type. */ +typedef float ALCfloat; + +/** ALC 64bit double point type. */ +typedef double ALCdouble; + +/** ALC 32bit type. */ +typedef unsigned int ALCsizei; + +/** ALC void type */ +typedef void ALCvoid; + +/** ALC enumerations. */ +typedef int ALCenum; + +/* Bad value. */ +#define ALC_INVALID (-1) + +/* Boolean False. */ +#define ALC_FALSE 0 + +/* Boolean True. */ +#define ALC_TRUE 1 + +/** Errors: No Error. */ +#define ALC_NO_ERROR ALC_FALSE + +#define ALC_MAJOR_VERSION 0x1000 +#define ALC_MINOR_VERSION 0x1001 +#define ALC_ATTRIBUTES_SIZE 0x1002 +#define ALC_ALL_ATTRIBUTES 0x1003 + +#define ALC_DEFAULT_DEVICE_SPECIFIER 0x1004 +#define ALC_DEVICE_SPECIFIER 0x1005 +#define ALC_EXTENSIONS 0x1006 + +#define ALC_FREQUENCY 0x1007 +#define ALC_REFRESH 0x1008 +#define ALC_SYNC 0x1009 + +/** + * The device argument does not name a valid dvice. + */ +#define ALC_INVALID_DEVICE 0xA001 + +/** + * The context argument does not name a valid context. + */ +#define ALC_INVALID_CONTEXT 0xA002 + +/** + * A function was called at inappropriate time, + * or in an inappropriate way, causing an illegal state. + * This can be an incompatible ALenum, object ID, + * and/or function. + */ +#define ALC_INVALID_ENUM 0xA003 + +/** + * Illegal value passed as an argument to an AL call. + * Applies to parameter values, but not to enumerations. + */ +#define ALC_INVALID_VALUE 0xA004 + +/** + * A function could not be completed, + * because there is not enough memory available. + */ +#define ALC_OUT_OF_MEMORY 0xA005 + + +//********************************************************************************* +// OSX Specific Properties +//********************************************************************************* + +/** + * Convert Data When Loading. Default false, currently applies only to monophonic sounds + */ +#define ALC_CONVERT_DATA_UPON_LOADING 0xF001 + +/** + * Render Quality. + */ +#define ALC_SPATIAL_RENDERING_QUALITY 0xF002 + #define ALC_SPATIAL_RENDERING_QUALITY_HIGH 'rqhi' + #define ALC_SPATIAL_RENDERING_QUALITY_LOW 'rdlo' + +/** + * Mixer Output Rate. + */ +#define ALC_MIXER_OUTPUT_RATE 0xF003 + +/** + * Maximum Mixer Busses. + * Set this before opening a new OAL device to indicate how many busses on the mixer + * are desired. Get returns either the current devices bus count value, or the value + * that will be used to open a device + */ +#define ALC_MIXER_MAXIMUM_BUSSES 0xF004 + +/** + * Render Channels. + * Allows a user to force OpenAL to render to stereo, regardless of the audio hardware being used + */ +#define ALC_RENDER_CHANNEL_COUNT 0xF005 + #define ALC_RENDER_CHANNEL_COUNT_STEREO 'rcst' + #define ALC_RENDER_CHANNEL_COUNT_MULTICHANNEL 'rcmc' + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/Engine/lib/openal/macosx/OpenAL.framework/Versions/A/Headers/altypes.h b/Engine/lib/openal/macosx/OpenAL.framework/Versions/A/Headers/altypes.h new file mode 100644 index 000000000..f15bcd351 --- /dev/null +++ b/Engine/lib/openal/macosx/OpenAL.framework/Versions/A/Headers/altypes.h @@ -0,0 +1,336 @@ +#ifndef _ALTYPES_H_ +#define _ALTYPES_H_ + +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2000 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + + +#ifdef __cplusplus +extern "C" { +#endif + +/** OpenAL boolean type. */ +typedef char ALboolean; + +/** OpenAL 8bit signed byte. */ +typedef char ALbyte; + +/** OpenAL 8bit unsigned byte. */ +typedef unsigned char ALubyte; + +/** OpenAL 16bit signed short integer type. */ +typedef short ALshort; + +/** OpenAL 16bit unsigned short integer type. */ +typedef unsigned short ALushort; + +/** OpenAL 32bit unsigned integer type. */ +typedef unsigned ALuint; + +/** OpenAL 32bit signed integer type. */ +typedef int ALint; + +/** OpenAL 32bit floating point type. */ +typedef float ALfloat; + +/** OpenAL 64bit double point type. */ +typedef double ALdouble; + +/** OpenAL 32bit type. */ +typedef unsigned int ALsizei; + +/** OpenAL void type */ +typedef void ALvoid; + +/** OpenAL enumerations. */ +typedef int ALenum; + +/* Bad value. */ +#define AL_INVALID (-1) + +/* Disable value. */ +#define AL_NONE 0 + +/* Boolean False. */ +#define AL_FALSE 0 + +/* Boolean True. */ +#define AL_TRUE 1 + +/** + * Indicate the type of AL_SOURCE. + * Sources can be spatialized + */ +#define AL_SOURCE_TYPE 0x200 + +/** Indicate source has absolute coordinates. */ +#define AL_SOURCE_ABSOLUTE 0x201 + +/** Indicate Source has listener relative coordinates. */ +#define AL_SOURCE_RELATIVE 0x202 + +/** + * Directional source, inner cone angle, in degrees. + * Range: [0-360] + * Default: 360 + */ +#define AL_CONE_INNER_ANGLE 0x1001 + +/** + * Directional source, outer cone angle, in degrees. + * Range: [0-360] + * Default: 360 + */ +#define AL_CONE_OUTER_ANGLE 0x1002 + +/** + * Specify the pitch to be applied, either at source, + * or on mixer results, at listener. + * Range: [0.5-2.0] + * Default: 1.0 + */ +#define AL_PITCH 0x1003 + +/** + * Specify the current location in three dimensional space. + * OpenAL, like OpenGL, uses a right handed coordinate system, + * where in a frontal default view X (thumb) points right, + * Y points up (index finger), and Z points towards the + * viewer/camera (middle finger). + * To switch from a left handed coordinate system, flip the + * sign on the Z coordinate. + * Listener position is always in the world coordinate system. + */ +#define AL_POSITION 0x1004 + +/** Specify the current direction as forward vector. */ +#define AL_DIRECTION 0x1005 + +/** Specify the current velocity in three dimensional space. */ +#define AL_VELOCITY 0x1006 + +/** + * Indicate whether source has to loop infinite. + * Type: ALboolean + * Range: [AL_TRUE, AL_FALSE] + * Default: AL_FALSE + */ +#define AL_LOOPING 0x1007 + +/** + * Indicate the buffer to provide sound samples. + * Type: ALuint. + * Range: any valid Buffer id. + */ +#define AL_BUFFER 0x1009 + +/** + * Indicate the gain (volume amplification) applied. + * Type: ALfloat. + * Range: ]0.0- ] + * A value of 1.0 means un-attenuated/unchanged. + * Each division by 2 equals an attenuation of -6dB. + * Each multiplicaton with 2 equals an amplification of +6dB. + * A value of 0.0 is meaningless with respect to a logarithmic + * scale; it is interpreted as zero volume - the channel + * is effectively disabled. + */ +#define AL_GAIN 0x100A + +/** + * Indicate minimum source attenuation. + * Type: ALfloat + * Range: [0.0 - 1.0] + */ +#define AL_MIN_GAIN 0x100D + +/** + * Indicate maximum source attenuation. + * Type: ALfloat + * Range: [0.0 - 1.0] + */ +#define AL_MAX_GAIN 0x100E + +/** + * Specify the current orientation. + * Type: ALfv6 (at/up) + * Range: N/A + */ +#define AL_ORIENTATION 0x100F + +/* byte offset into source (in canon format). -1 if source + * is not playing. Don't set this, get this. + * + * Type: ALfloat + * Range: [0.0 - ] + * Default: 1.0 + */ +#define AL_REFERENCE_DISTANCE 0x1020 + + /** + * Indicate the rolloff factor for the source. + * Type: ALfloat + * Range: [0.0 - ] + * Default: 1.0 + */ +#define AL_ROLLOFF_FACTOR 0x1021 + +/** + * Indicate the gain (volume amplification) applied. + * Type: ALfloat. + * Range: ]0.0- ] + * A value of 1.0 means un-attenuated/unchanged. + * Each division by 2 equals an attenuation of -6dB. + * Each multiplicaton with 2 equals an amplification of +6dB. + * A value of 0.0 is meaningless with respect to a logarithmic + * scale; it is interpreted as zero volume - the channel + * is effectively disabled. + */ +#define AL_CONE_OUTER_GAIN 0x1022 + +/** + * Specify the maximum distance. + * Type: ALfloat + * Range: [0.0 - ] + */ +#define AL_MAX_DISTANCE 0x1023 + +/** + * Source state information + */ +#define AL_SOURCE_STATE 0x1010 +#define AL_INITIAL 0x1011 +#define AL_PLAYING 0x1012 +#define AL_PAUSED 0x1013 +#define AL_STOPPED 0x1014 + +/** + * Buffer Queue params + */ +#define AL_BUFFERS_QUEUED 0x1015 +#define AL_BUFFERS_PROCESSED 0x1016 + +/** + * Source buffer position information + */ +#define AL_SEC_OFFSET 0x1024 +#define AL_SAMPLE_OFFSET 0x1025 +#define AL_BYTE_OFFSET 0x1026 + +/** Sound buffers: format specifier. */ +#define AL_FORMAT_MONO8 0x1100 +#define AL_FORMAT_MONO16 0x1101 +#define AL_FORMAT_STEREO8 0x1102 +#define AL_FORMAT_STEREO16 0x1103 + +/** + * Sound buffers: frequency, in units of Hertz [Hz]. + * This is the number of samples per second. Half of the + * sample frequency marks the maximum significant + * frequency component. + */ +#define AL_FREQUENCY 0x2001 +#define AL_BITS 0x2002 +#define AL_CHANNELS 0x2003 +#define AL_SIZE 0x2004 + +/** + * Buffer state. + * + * Not supported for public use (yet). + */ +#define AL_UNUSED 0x2010 +#define AL_PENDING 0x2011 +#define AL_PROCESSED 0x2012 + +/** Errors: No Error. */ +#define AL_NO_ERROR AL_FALSE + +/** + * Illegal name passed as an argument to an AL call. + */ +#define AL_INVALID_NAME 0xA001 + +/** + * Illegal enum passed as an argument to an AL call. + */ +#define AL_INVALID_ENUM 0xA002 +/** + * Illegal value passed as an argument to an AL call. + * Applies to parameter values, but not to enumerations. + */ +#define AL_INVALID_VALUE 0xA003 + +/** + * A function was called at inappropriate time, + * or in an inappropriate way, causing an illegal state. + * This can be an incompatible ALenum, object ID, + * and/or function. + */ +#define AL_INVALID_OPERATION 0xA004 + +/** + * A function could not be completed, + * because there is not enough memory available. + */ +#define AL_OUT_OF_MEMORY 0xA005 + +/** Context strings: Vendor Name. */ +#define AL_VENDOR 0xB001 +#define AL_VERSION 0xB002 +#define AL_RENDERER 0xB003 +#define AL_EXTENSIONS 0xB004 + +/** Global tweakage. */ + +/** + * Doppler scale. Default 1.0 + */ +#define AL_DOPPLER_FACTOR 0xC000 + +/** + * Doppler velocity. Default 1.0 + */ +#define AL_DOPPLER_VELOCITY 0xC001 + +/** + * Distance model. Default AL_INVERSE_DISTANCE_CLAMPED + */ +#define AL_DISTANCE_MODEL 0xD000 + +/** Distance models. */ + +#define AL_INVERSE_DISTANCE 0xD001 +#define AL_INVERSE_DISTANCE_CLAMPED 0xD002 +#define AL_LINEAR_DISTANCE 0xD003 +#define AL_LINEAR_DISTANCE_CLAMPED 0xD004 +#define AL_EXPONENT_DISTANCE 0xD005 +#define AL_EXPONENT_DISTANCE_CLAMPED 0xD006 + + /** + * enables + */ + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/Engine/lib/openal/macosx/OpenAL.framework/Versions/A/Headers/alut.h b/Engine/lib/openal/macosx/OpenAL.framework/Versions/A/Headers/alut.h new file mode 100644 index 000000000..0b1baea41 --- /dev/null +++ b/Engine/lib/openal/macosx/OpenAL.framework/Versions/A/Headers/alut.h @@ -0,0 +1,55 @@ +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2000 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#ifndef _ALUT_H_ +#define _ALUT_H_ + +#define ALUTAPI +#define ALUTAPIENTRY + +#include "al.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef TARGET_OS_MAC + #if TARGET_OS_MAC + #pragma export on + #endif +#endif + +ALUTAPI ALvoid ALUTAPIENTRY alutInit(ALint *argc,ALbyte **argv); +ALUTAPI ALvoid ALUTAPIENTRY alutExit(ALvoid); +ALUTAPI ALvoid ALUTAPIENTRY alutLoadWAVFile(ALbyte *file,ALenum *format,ALvoid **data,ALsizei *size,ALsizei *freq); +ALUTAPI ALvoid ALUTAPIENTRY alutLoadWAVMemory(ALbyte *memory,ALenum *format,ALvoid **data,ALsizei *size,ALsizei *freq); +ALUTAPI ALvoid ALUTAPIENTRY alutUnloadWAV(ALenum format,ALvoid *data,ALsizei size,ALsizei freq); + +#ifdef TARGET_OS_MAC + #if TARGET_OS_MAC + #pragma export off + #endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Engine/lib/openal/macosx/OpenAL.framework/Versions/A/Headers/eaxtypes.h b/Engine/lib/openal/macosx/OpenAL.framework/Versions/A/Headers/eaxtypes.h new file mode 100644 index 000000000..15ba59afc --- /dev/null +++ b/Engine/lib/openal/macosx/OpenAL.framework/Versions/A/Headers/eaxtypes.h @@ -0,0 +1,462 @@ + + +typedef struct _ALGUID +{ + unsigned long Data1; + unsigned short Data2; + unsigned short Data3; + unsigned char Data4[8]; +}ALGUID; + +#ifndef INITGUID + #define DEFINE_ALGUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const ALGUID name +#else + #define DEFINE_ALGUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const ALGUID name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } +#endif // INITGUID + + + +/* + * EAX 3.0 listener property set {A8FA6880-B476-11d3-BDB9-00C0F02DDF87} + */ +DEFINE_ALGUID(DSPROPSETID_EAX30_ListenerProperties, + 0xa8fa6882, + 0xb476, + 0x11d3, + 0xbd, 0xb9, 0x00, 0xc0, 0xf0, 0x2d, 0xdf, 0x87); + +// For compatibility with future EAX versions: +#define DSPROPSETID_EAX_ListenerProperties DSPROPSETID_EAX30_ListenerProperties + +typedef enum +{ + DSPROPERTY_EAXLISTENER_NONE, + DSPROPERTY_EAXLISTENER_ALLPARAMETERS, + DSPROPERTY_EAXLISTENER_ENVIRONMENT, + DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE, + DSPROPERTY_EAXLISTENER_ENVIRONMENTDIFFUSION, + DSPROPERTY_EAXLISTENER_ROOM, + DSPROPERTY_EAXLISTENER_ROOMHF, + DSPROPERTY_EAXLISTENER_ROOMLF, + DSPROPERTY_EAXLISTENER_DECAYTIME, + DSPROPERTY_EAXLISTENER_DECAYHFRATIO, + DSPROPERTY_EAXLISTENER_DECAYLFRATIO, + DSPROPERTY_EAXLISTENER_REFLECTIONS, + DSPROPERTY_EAXLISTENER_REFLECTIONSDELAY, + DSPROPERTY_EAXLISTENER_REFLECTIONSPAN, + DSPROPERTY_EAXLISTENER_REVERB, + DSPROPERTY_EAXLISTENER_REVERBDELAY, + DSPROPERTY_EAXLISTENER_REVERBPAN, + DSPROPERTY_EAXLISTENER_ECHOTIME, + DSPROPERTY_EAXLISTENER_ECHODEPTH, + DSPROPERTY_EAXLISTENER_MODULATIONTIME, + DSPROPERTY_EAXLISTENER_MODULATIONDEPTH, + DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF, + DSPROPERTY_EAXLISTENER_HFREFERENCE, + DSPROPERTY_EAXLISTENER_LFREFERENCE, + DSPROPERTY_EAXLISTENER_ROOMROLLOFFFACTOR, + DSPROPERTY_EAXLISTENER_FLAGS +} DSPROPERTY_EAX_LISTENERPROPERTY; + +// OR these flags with property id +#define DSPROPERTY_EAXLISTENER_IMMEDIATE 0x00000000 // changes take effect immediately +#define DSPROPERTY_EAXLISTENER_DEFERRED 0x80000000 // changes take effect later +#define DSPROPERTY_EAXLISTENER_COMMITDEFERREDSETTINGS (DSPROPERTY_EAXLISTENER_NONE | \ + DSPROPERTY_EAXLISTENER_IMMEDIATE) + +typedef struct _EAXVECTOR { + float x; + float y; + float z; +} EAXVECTOR; + +// Use this structure for DSPROPERTY_EAXLISTENER_ALLPARAMETERS +// - all levels are hundredths of decibels +// - all times and delays are in seconds +// +// NOTE: This structure may change in future EAX versions. +// It is recommended to initialize fields by name: +// myListener.lRoom = -1000; +// myListener.lRoomHF = -100; +// ... +// myListener.dwFlags = myFlags /* see EAXLISTENERFLAGS below */ ; +// instead of: +// myListener = { -1000, -100, ... , 0x00000009 }; +// If you want to save and load presets in binary form, you +// should define your own structure to insure future compatibility. +// +typedef struct _EAXLISTENERPROPERTIES +{ + unsigned long ulEnvironment; // sets all listener properties + float flEnvironmentSize; // environment size in meters + float flEnvironmentDiffusion; // environment diffusion + long lRoom; // room effect level (at mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lRoomLF; // relative room effect level at low frequencies + float flDecayTime; // reverberation decay time at mid frequencies + float flDecayHFRatio; // high-frequency to mid-frequency decay time ratio + float flDecayLFRatio; // low-frequency to mid-frequency decay time ratio + long lReflections; // early reflections level relative to room effect + float flReflectionsDelay; // initial reflection delay time + EAXVECTOR vReflectionsPan; // early reflections panning vector + long lReverb; // late reverberation level relative to room effect + float flReverbDelay; // late reverberation delay time relative to initial reflection + EAXVECTOR vReverbPan; // late reverberation panning vector + float flEchoTime; // echo time + float flEchoDepth; // echo depth + float flModulationTime; // modulation time + float flModulationDepth; // modulation depth + float flAirAbsorptionHF; // change in level per meter at high frequencies + float flHFReference; // reference high frequency + float flLFReference; // reference low frequency + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + unsigned long ulFlags; // modifies the behavior of properties +} EAXLISTENERPROPERTIES, *LPEAXLISTENERPROPERTIES; + +// used by DSPROPERTY_EAXLISTENER_ENVIRONMENT +enum +{ + EAX_ENVIRONMENT_GENERIC, + EAX_ENVIRONMENT_PADDEDCELL, + EAX_ENVIRONMENT_ROOM, + EAX_ENVIRONMENT_BATHROOM, + EAX_ENVIRONMENT_LIVINGROOM, + EAX_ENVIRONMENT_STONEROOM, + EAX_ENVIRONMENT_AUDITORIUM, + EAX_ENVIRONMENT_CONCERTHALL, + EAX_ENVIRONMENT_CAVE, + EAX_ENVIRONMENT_ARENA, + EAX_ENVIRONMENT_HANGAR, + EAX_ENVIRONMENT_CARPETEDHALLWAY, + EAX_ENVIRONMENT_HALLWAY, + EAX_ENVIRONMENT_STONECORRIDOR, + EAX_ENVIRONMENT_ALLEY, + EAX_ENVIRONMENT_FOREST, + EAX_ENVIRONMENT_CITY, + EAX_ENVIRONMENT_MOUNTAINS, + EAX_ENVIRONMENT_QUARRY, + EAX_ENVIRONMENT_PLAIN, + EAX_ENVIRONMENT_PARKINGLOT, + EAX_ENVIRONMENT_SEWERPIPE, + EAX_ENVIRONMENT_UNDERWATER, + EAX_ENVIRONMENT_DRUGGED, + EAX_ENVIRONMENT_DIZZY, + EAX_ENVIRONMENT_PSYCHOTIC, + + EAX_ENVIRONMENT_UNDEFINED, + + EAX_ENVIRONMENT_COUNT +}; + +// Used by DSPROPERTY_EAXLISTENER_FLAGS +// +// Note: The number and order of flags may change in future EAX versions. +// It is recommended to use the flag defines as follows: +// myFlags = EAXLISTENERFLAGS_DECAYTIMESCALE | EAXLISTENERFLAGS_REVERBSCALE; +// instead of: +// myFlags = 0x00000009; +// +// These flags determine what properties are affected by environment size. +#define EAXLISTENERFLAGS_DECAYTIMESCALE 0x00000001 // reverberation decay time +#define EAXLISTENERFLAGS_REFLECTIONSSCALE 0x00000002 // reflection level +#define EAXLISTENERFLAGS_REFLECTIONSDELAYSCALE 0x00000004 // initial reflection delay time +#define EAXLISTENERFLAGS_REVERBSCALE 0x00000008 // reflections level +#define EAXLISTENERFLAGS_REVERBDELAYSCALE 0x00000010 // late reverberation delay time +#define EAXLISTENERFLAGS_ECHOTIMESCALE 0x00000040 // echo time +#define EAXLISTENERFLAGS_MODULATIONTIMESCALE 0x00000080 // modulation time + +// This flag limits high-frequency decay time according to air absorption. +#define EAXLISTENERFLAGS_DECAYHFLIMIT 0x00000020 + +#define EAXLISTENERFLAGS_RESERVED 0xFFFFFF00 // reserved future use + +// Property ranges and defaults: + +#define EAXLISTENER_MINENVIRONMENT 0 +#define EAXLISTENER_MAXENVIRONMENT (EAX_ENVIRONMENT_COUNT-1) +#define EAXLISTENER_DEFAULTENVIRONMENT EAX_ENVIRONMENT_GENERIC + +#define EAXLISTENER_MINENVIRONMENTSIZE 1.0f +#define EAXLISTENER_MAXENVIRONMENTSIZE 100.0f +#define EAXLISTENER_DEFAULTENVIRONMENTSIZE 7.5f + +#define EAXLISTENER_MINENVIRONMENTDIFFUSION 0.0f +#define EAXLISTENER_MAXENVIRONMENTDIFFUSION 1.0f +#define EAXLISTENER_DEFAULTENVIRONMENTDIFFUSION 1.0f + +#define EAXLISTENER_MINROOM (-10000) +#define EAXLISTENER_MAXROOM 0 +#define EAXLISTENER_DEFAULTROOM (-1000) + +#define EAXLISTENER_MINROOMHF (-10000) +#define EAXLISTENER_MAXROOMHF 0 +#define EAXLISTENER_DEFAULTROOMHF (-100) + +#define EAXLISTENER_MINROOMLF (-10000) +#define EAXLISTENER_MAXROOMLF 0 +#define EAXLISTENER_DEFAULTROOMLF 0 + +#define EAXLISTENER_MINDECAYTIME 0.1f +#define EAXLISTENER_MAXDECAYTIME 20.0f +#define EAXLISTENER_DEFAULTDECAYTIME 1.49f + +#define EAXLISTENER_MINDECAYHFRATIO 0.1f +#define EAXLISTENER_MAXDECAYHFRATIO 2.0f +#define EAXLISTENER_DEFAULTDECAYHFRATIO 0.83f + +#define EAXLISTENER_MINDECAYLFRATIO 0.1f +#define EAXLISTENER_MAXDECAYLFRATIO 2.0f +#define EAXLISTENER_DEFAULTDECAYLFRATIO 1.00f + +#define EAXLISTENER_MINREFLECTIONS (-10000) +#define EAXLISTENER_MAXREFLECTIONS 1000 +#define EAXLISTENER_DEFAULTREFLECTIONS (-2602) + +#define EAXLISTENER_MINREFLECTIONSDELAY 0.0f +#define EAXLISTENER_MAXREFLECTIONSDELAY 0.3f +#define EAXLISTENER_DEFAULTREFLECTIONSDELAY 0.007f + +#define EAXLISTENER_MINREVERB (-10000) +#define EAXLISTENER_MAXREVERB 2000 +#define EAXLISTENER_DEFAULTREVERB 200 + +#define EAXLISTENER_MINREVERBDELAY 0.0f +#define EAXLISTENER_MAXREVERBDELAY 0.1f +#define EAXLISTENER_DEFAULTREVERBDELAY 0.011f + +#define EAXLISTENER_MINECHOTIME 0.075f +#define EAXLISTENER_MAXECHOTIME 0.25f +#define EAXLISTENER_DEFAULTECHOTIME 0.25f + +#define EAXLISTENER_MINECHODEPTH 0.0f +#define EAXLISTENER_MAXECHODEPTH 1.0f +#define EAXLISTENER_DEFAULTECHODEPTH 0.0f + +#define EAXLISTENER_MINMODULATIONTIME 0.04f +#define EAXLISTENER_MAXMODULATIONTIME 4.0f +#define EAXLISTENER_DEFAULTMODULATIONTIME 0.25f + +#define EAXLISTENER_MINMODULATIONDEPTH 0.0f +#define EAXLISTENER_MAXMODULATIONDEPTH 1.0f +#define EAXLISTENER_DEFAULTMODULATIONDEPTH 0.0f + +#define EAXLISTENER_MINAIRABSORPTIONHF (-100.0f) +#define EAXLISTENER_MAXAIRABSORPTIONHF 0.0f +#define EAXLISTENER_DEFAULTAIRABSORPTIONHF (-5.0f) + +#define EAXLISTENER_MINHFREFERENCE 1000.0f +#define EAXLISTENER_MAXHFREFERENCE 20000.0f +#define EAXLISTENER_DEFAULTHFREFERENCE 5000.0f + +#define EAXLISTENER_MINLFREFERENCE 20.0f +#define EAXLISTENER_MAXLFREFERENCE 1000.0f +#define EAXLISTENER_DEFAULTLFREFERENCE 250.0f + +#define EAXLISTENER_MINROOMROLLOFFFACTOR 0.0f +#define EAXLISTENER_MAXROOMROLLOFFFACTOR 10.0f +#define EAXLISTENER_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXLISTENER_DEFAULTFLAGS (EAXLISTENERFLAGS_DECAYTIMESCALE | \ + EAXLISTENERFLAGS_REFLECTIONSSCALE | \ + EAXLISTENERFLAGS_REFLECTIONSDELAYSCALE | \ + EAXLISTENERFLAGS_REVERBSCALE | \ + EAXLISTENERFLAGS_REVERBDELAYSCALE | \ + EAXLISTENERFLAGS_DECAYHFLIMIT) + + + +/* +* EAX 3.0 buffer property set {A8FA6881-B476-11d3-BDB9-00C0F02DDF87} +*/ +DEFINE_ALGUID(DSPROPSETID_EAX30_BufferProperties, + 0xa8fa6881, + 0xb476, + 0x11d3, + 0xbd, 0xb9, 0x0, 0xc0, 0xf0, 0x2d, 0xdf, 0x87); + +// For compatibility with future EAX versions: +#define DSPROPSETID_EAX_BufferProperties DSPROPSETID_EAX30_BufferProperties +#define DSPROPSETID_EAX_SourceProperties DSPROPSETID_EAX30_BufferProperties + +typedef enum +{ + DSPROPERTY_EAXBUFFER_NONE, + DSPROPERTY_EAXBUFFER_ALLPARAMETERS, + DSPROPERTY_EAXBUFFER_OBSTRUCTIONPARAMETERS, + DSPROPERTY_EAXBUFFER_OCCLUSIONPARAMETERS, + DSPROPERTY_EAXBUFFER_EXCLUSIONPARAMETERS, + DSPROPERTY_EAXBUFFER_DIRECT, + DSPROPERTY_EAXBUFFER_DIRECTHF, + DSPROPERTY_EAXBUFFER_ROOM, + DSPROPERTY_EAXBUFFER_ROOMHF, + DSPROPERTY_EAXBUFFER_OBSTRUCTION, + DSPROPERTY_EAXBUFFER_OBSTRUCTIONLFRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSION, + DSPROPERTY_EAXBUFFER_OCCLUSIONLFRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSIONROOMRATIO, + DSPROPERTY_EAXBUFFER_OCCLUSIONDIRECTRATIO, + DSPROPERTY_EAXBUFFER_EXCLUSION, + DSPROPERTY_EAXBUFFER_EXCLUSIONLFRATIO, + DSPROPERTY_EAXBUFFER_OUTSIDEVOLUMEHF, + DSPROPERTY_EAXBUFFER_DOPPLERFACTOR, + DSPROPERTY_EAXBUFFER_ROLLOFFFACTOR, + DSPROPERTY_EAXBUFFER_ROOMROLLOFFFACTOR, + DSPROPERTY_EAXBUFFER_AIRABSORPTIONFACTOR, + DSPROPERTY_EAXBUFFER_FLAGS +} DSPROPERTY_EAX_BUFFERPROPERTY; + +// OR these flags with property id +#define DSPROPERTY_EAXBUFFER_IMMEDIATE 0x00000000 // changes take effect immediately +#define DSPROPERTY_EAXBUFFER_DEFERRED 0x80000000 // changes take effect later +#define DSPROPERTY_EAXBUFFER_COMMITDEFERREDSETTINGS (DSPROPERTY_EAXBUFFER_NONE | \ + DSPROPERTY_EAXBUFFER_IMMEDIATE) + +// Use this structure for DSPROPERTY_EAXBUFFER_ALLPARAMETERS +// - all levels are hundredths of decibels +// - all delays are in seconds +// +// NOTE: This structure may change in future EAX versions. +// It is recommended to initialize fields by name: +// myBuffer.lDirect = 0; +// myBuffer.lDirectHF = -200; +// ... +// myBuffer.dwFlags = myFlags /* see EAXBUFFERFLAGS below */ ; +// instead of: +// myBuffer = { 0, -200, ... , 0x00000003 }; +// +typedef struct _EAXBUFFERPROPERTIES +{ + long lDirect; // direct path level (at low and mid frequencies) + long lDirectHF; // relative direct path level at high frequencies + long lRoom; // room effect level (at low and mid frequencies) + long lRoomHF; // relative room effect level at high frequencies + long lObstruction; // main obstruction control (attenuation at high frequencies) + float flObstructionLFRatio; // obstruction low-frequency level re. main control + long lOcclusion; // main occlusion control (attenuation at high frequencies) + float flOcclusionLFRatio; // occlusion low-frequency level re. main control + float flOcclusionRoomRatio; // relative occlusion control for room effect + float flOcclusionDirectRatio; // relative occlusion control for direct path + long lExclusion; // main exlusion control (attenuation at high frequencies) + float flExclusionLFRatio; // exclusion low-frequency level re. main control + long lOutsideVolumeHF; // outside sound cone level at high frequencies + float flDopplerFactor; // like DS3D flDopplerFactor but per source + float flRolloffFactor; // like DS3D flRolloffFactor but per source + float flRoomRolloffFactor; // like DS3D flRolloffFactor but for room effect + float flAirAbsorptionFactor; // multiplies DSPROPERTY_EAXLISTENER_AIRABSORPTIONHF + unsigned long ulFlags; // modifies the behavior of properties +} EAXBUFFERPROPERTIES, *LPEAXBUFFERPROPERTIES; + +// Use this structure for DSPROPERTY_EAXBUFFER_OBSTRUCTION, +typedef struct _EAXOBSTRUCTIONPROPERTIES +{ + long lObstruction; + float flObstructionLFRatio; +} EAXOBSTRUCTIONPROPERTIES, *LPEAXOBSTRUCTIONPROPERTIES; + +// Use this structure for DSPROPERTY_EAXBUFFER_OCCLUSION +typedef struct _EAXOCCLUSIONPROPERTIES +{ + long lOcclusion; + float flOcclusionLFRatio; + float flOcclusionRoomRatio; + float flOcclusionDirectRatio; +} EAXOCCLUSIONPROPERTIES, *LPEAXOCCLUSIONPROPERTIES; + +// Use this structure for DSPROPERTY_EAXBUFFER_EXCLUSION +typedef struct _EAXEXCLUSIONPROPERTIES +{ + long lExclusion; + float flExclusionLFRatio; +} EAXEXCLUSIONPROPERTIES, *LPEAXEXCLUSIONPROPERTIES; + +// Used by DSPROPERTY_EAXBUFFER_FLAGS +// TRUE: value is computed automatically - property is an offset +// FALSE: value is used directly +// +// Note: The number and order of flags may change in future EAX versions. +// To insure future compatibility, use flag defines as follows: +// myFlags = EAXBUFFERFLAGS_DIRECTHFAUTO | EAXBUFFERFLAGS_ROOMAUTO; +// instead of: +// myFlags = 0x00000003; +// +#define EAXBUFFERFLAGS_DIRECTHFAUTO 0x00000001 // affects DSPROPERTY_EAXBUFFER_DIRECTHF +#define EAXBUFFERFLAGS_ROOMAUTO 0x00000002 // affects DSPROPERTY_EAXBUFFER_ROOM +#define EAXBUFFERFLAGS_ROOMHFAUTO 0x00000004 // affects DSPROPERTY_EAXBUFFER_ROOMHF + +#define EAXBUFFERFLAGS_RESERVED 0xFFFFFFF8 // reserved future use + +// Property ranges and defaults: + +#define EAXBUFFER_MINDIRECT (-10000) +#define EAXBUFFER_MAXDIRECT 1000 +#define EAXBUFFER_DEFAULTDIRECT 0 + +#define EAXBUFFER_MINDIRECTHF (-10000) +#define EAXBUFFER_MAXDIRECTHF 0 +#define EAXBUFFER_DEFAULTDIRECTHF 0 + +#define EAXBUFFER_MINROOM (-10000) +#define EAXBUFFER_MAXROOM 1000 +#define EAXBUFFER_DEFAULTROOM 0 + +#define EAXBUFFER_MINROOMHF (-10000) +#define EAXBUFFER_MAXROOMHF 0 +#define EAXBUFFER_DEFAULTROOMHF 0 + +#define EAXBUFFER_MINOBSTRUCTION (-10000) +#define EAXBUFFER_MAXOBSTRUCTION 0 +#define EAXBUFFER_DEFAULTOBSTRUCTION 0 + +#define EAXBUFFER_MINOBSTRUCTIONLFRATIO 0.0f +#define EAXBUFFER_MAXOBSTRUCTIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTOBSTRUCTIONLFRATIO 0.0f + +#define EAXBUFFER_MINOCCLUSION (-10000) +#define EAXBUFFER_MAXOCCLUSION 0 +#define EAXBUFFER_DEFAULTOCCLUSION 0 + +#define EAXBUFFER_MINOCCLUSIONLFRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTOCCLUSIONLFRATIO 0.25f + +#define EAXBUFFER_MINOCCLUSIONROOMRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONROOMRATIO 10.0f +#define EAXBUFFER_DEFAULTOCCLUSIONROOMRATIO 1.5f + +#define EAXBUFFER_MINOCCLUSIONDIRECTRATIO 0.0f +#define EAXBUFFER_MAXOCCLUSIONDIRECTRATIO 10.0f +#define EAXBUFFER_DEFAULTOCCLUSIONDIRECTRATIO 1.0f + +#define EAXBUFFER_MINEXCLUSION (-10000) +#define EAXBUFFER_MAXEXCLUSION 0 +#define EAXBUFFER_DEFAULTEXCLUSION 0 + +#define EAXBUFFER_MINEXCLUSIONLFRATIO 0.0f +#define EAXBUFFER_MAXEXCLUSIONLFRATIO 1.0f +#define EAXBUFFER_DEFAULTEXCLUSIONLFRATIO 1.0f + +#define EAXBUFFER_MINOUTSIDEVOLUMEHF (-10000) +#define EAXBUFFER_MAXOUTSIDEVOLUMEHF 0 +#define EAXBUFFER_DEFAULTOUTSIDEVOLUMEHF 0 + +#define EAXBUFFER_MINDOPPLERFACTOR 0.0f +#define EAXBUFFER_MAXDOPPLERFACTOR 10.f +#define EAXBUFFER_DEFAULTDOPPLERFACTOR 0.0f + +#define EAXBUFFER_MINROLLOFFFACTOR 0.0f +#define EAXBUFFER_MAXROLLOFFFACTOR 10.f +#define EAXBUFFER_DEFAULTROLLOFFFACTOR 0.0f + +#define EAXBUFFER_MINROOMROLLOFFFACTOR 0.0f +#define EAXBUFFER_MAXROOMROLLOFFFACTOR 10.f +#define EAXBUFFER_DEFAULTROOMROLLOFFFACTOR 0.0f + +#define EAXBUFFER_MINAIRABSORPTIONFACTOR 0.0f +#define EAXBUFFER_MAXAIRABSORPTIONFACTOR 10.0f +#define EAXBUFFER_DEFAULTAIRABSORPTIONFACTOR 1.0f + +#define EAXBUFFER_DEFAULTFLAGS (EAXBUFFERFLAGS_DIRECTHFAUTO | \ + EAXBUFFERFLAGS_ROOMAUTO | \ + EAXBUFFERFLAGS_ROOMHFAUTO ) diff --git a/Engine/lib/openal/macosx/OpenAL.framework/Versions/A/OpenAL b/Engine/lib/openal/macosx/OpenAL.framework/Versions/A/OpenAL new file mode 100644 index 000000000..40c63cbf6 Binary files /dev/null and b/Engine/lib/openal/macosx/OpenAL.framework/Versions/A/OpenAL differ diff --git a/Engine/lib/openal/macosx/OpenAL.framework/Versions/A/Resources/English.lproj/InfoPlist.strings b/Engine/lib/openal/macosx/OpenAL.framework/Versions/A/Resources/English.lproj/InfoPlist.strings new file mode 100644 index 000000000..201c6ba89 Binary files /dev/null and b/Engine/lib/openal/macosx/OpenAL.framework/Versions/A/Resources/English.lproj/InfoPlist.strings differ diff --git a/Engine/lib/openal/macosx/OpenAL.framework/Versions/A/Resources/Info.plist b/Engine/lib/openal/macosx/OpenAL.framework/Versions/A/Resources/Info.plist new file mode 100644 index 000000000..a811c8bf9 --- /dev/null +++ b/Engine/lib/openal/macosx/OpenAL.framework/Versions/A/Resources/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + OpenAL + CFBundleGetInfoString + OpenAL + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + OpenAL + CFBundlePackageType + FMWK + CFBundleShortVersionString + OpenAL + CFBundleSignature + ???? + CFBundleVersion + 1.0.1d1 + CSResourcesFileMapped + + + diff --git a/Engine/lib/openal/macosx/OpenAL.framework/Versions/Current b/Engine/lib/openal/macosx/OpenAL.framework/Versions/Current new file mode 100644 index 000000000..dcf3361c8 --- /dev/null +++ b/Engine/lib/openal/macosx/OpenAL.framework/Versions/Current @@ -0,0 +1 @@ +link A/ \ No newline at end of file diff --git a/Engine/lib/openal/win32/al/EFX-Util.h b/Engine/lib/openal/win32/al/EFX-Util.h new file mode 100644 index 000000000..b4a6b4e2d --- /dev/null +++ b/Engine/lib/openal/win32/al/EFX-Util.h @@ -0,0 +1,422 @@ +/*******************************************************************\ +* * +* EFX-UTIL.H - EFX Utilities functions and Reverb Presets * +* * +* File revision 1.0 * +* * +\*******************************************************************/ + +#ifndef EAXVECTOR_DEFINED +#define EAXVECTOR_DEFINED +typedef struct _EAXVECTOR { + float x; + float y; + float z; +} EAXVECTOR; +#endif + +#ifndef EAXREVERBPROPERTIES_DEFINED +#define EAXREVERBPROPERTIES_DEFINED +typedef struct _EAXREVERBPROPERTIES +{ + unsigned long ulEnvironment; + float flEnvironmentSize; + float flEnvironmentDiffusion; + long lRoom; + long lRoomHF; + long lRoomLF; + float flDecayTime; + float flDecayHFRatio; + float flDecayLFRatio; + long lReflections; + float flReflectionsDelay; + EAXVECTOR vReflectionsPan; + long lReverb; + float flReverbDelay; + EAXVECTOR vReverbPan; + float flEchoTime; + float flEchoDepth; + float flModulationTime; + float flModulationDepth; + float flAirAbsorptionHF; + float flHFReference; + float flLFReference; + float flRoomRolloffFactor; + unsigned long ulFlags; +} EAXREVERBPROPERTIES, *LPEAXREVERBPROPERTIES; +#endif + +#ifndef EFXEAXREVERBPROPERTIES_DEFINED +#define EFXEAXREVERBPROPERTIES_DEFINED +typedef struct +{ + float flDensity; + float flDiffusion; + float flGain; + float flGainHF; + float flGainLF; + float flDecayTime; + float flDecayHFRatio; + float flDecayLFRatio; + float flReflectionsGain; + float flReflectionsDelay; + float flReflectionsPan[3]; + float flLateReverbGain; + float flLateReverbDelay; + float flLateReverbPan[3]; + float flEchoTime; + float flEchoDepth; + float flModulationTime; + float flModulationDepth; + float flAirAbsorptionGainHF; + float flHFReference; + float flLFReference; + float flRoomRolloffFactor; + int iDecayHFLimit; +} EFXEAXREVERBPROPERTIES, *LPEFXEAXREVERBPROPERTIES; +#endif + +#ifndef EAXOBSTRUCTIONPROPERTIES_DEFINED +#define EAXOBSTRUCTIONPROPERTIES_DEFINED +typedef struct _EAXOBSTRUCTIONPROPERTIES +{ + long lObstruction; + float flObstructionLFRatio; +} EAXOBSTRUCTIONPROPERTIES, *LPEAXOBSTRUCTIONPROPERTIES; +#endif + +#ifndef EAXOCCLUSIONPROPERTIES_DEFINED +#define EAXOCCLUSIONPROPERTIES_DEFINED +typedef struct _EAXOCCLUSIONPROPERTIES +{ + long lOcclusion; + float flOcclusionLFRatio; + float flOcclusionRoomRatio; + float flOcclusionDirectRatio; +} EAXOCCLUSIONPROPERTIES, *LPEAXOCCLUSIONPROPERTIES; +#endif + +#ifndef EAXEXCLUSIONPROPERTIES_DEFINED +#define EAXEXCLUSIONPROPERTIES_DEFINED +typedef struct _EAXEXCLUSIONPROPERTIES +{ + long lExclusion; + float flExclusionLFRatio; +} EAXEXCLUSIONPROPERTIES, *LPEAXEXCLUSIONPROPERTIES; +#endif + +#ifndef EFXLOWPASSFILTER_DEFINED +#define EFXLOWPASSFILTER_DEFINED +typedef struct _EFXLOWPASSFILTER +{ + float flGain; + float flGainHF; +} EFXLOWPASSFILTER, *LPEFXLOWPASSFILTER; +#endif + +void ConvertReverbParameters(EAXREVERBPROPERTIES *pEAXProp, EFXEAXREVERBPROPERTIES *pEFXEAXReverb); +void ConvertObstructionParameters(EAXOBSTRUCTIONPROPERTIES *pObProp, EFXLOWPASSFILTER *pDirectLowPassFilter); +void ConvertExclusionParameters(EAXEXCLUSIONPROPERTIES *pExProp, EFXLOWPASSFILTER *pSendLowPassFilter); +void ConvertOcclusionParameters(EAXOCCLUSIONPROPERTIES *pOcProp, EFXLOWPASSFILTER *pDirectLowPassFilter, EFXLOWPASSFILTER *pSendLowPassFilter); + + +/***********************************************************************************************\ +* +* EAX Reverb Presets in legacy format - use ConvertReverbParameters() to convert to +* EFX EAX Reverb Presets for use with the OpenAL Effects Extension. +* +************************************************************************************************/ + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define REVERB_PRESET_GENERIC \ + {0, 7.5f, 1.000f, -1000, -100, 0, 1.49f, 0.83f, 1.00f, -2602, 0.007f, 0.00f,0.00f,0.00f, 200, 0.011f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define REVERB_PRESET_PADDEDCELL \ + {1, 1.4f, 1.000f, -1000, -6000, 0, 0.17f, 0.10f, 1.00f, -1204, 0.001f, 0.00f,0.00f,0.00f, 207, 0.002f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define REVERB_PRESET_ROOM \ + {2, 1.9f, 1.000f, -1000, -454, 0, 0.40f, 0.83f, 1.00f, -1646, 0.002f, 0.00f,0.00f,0.00f, 53, 0.003f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define REVERB_PRESET_BATHROOM \ + {3, 1.4f, 1.000f, -1000, -1200, 0, 1.49f, 0.54f, 1.00f, -370, 0.007f, 0.00f,0.00f,0.00f, 1030, 0.011f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define REVERB_PRESET_LIVINGROOM \ + {4, 2.5f, 1.000f, -1000, -6000, 0, 0.50f, 0.10f, 1.00f, -1376, 0.003f, 0.00f,0.00f,0.00f, -1104, 0.004f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define REVERB_PRESET_STONEROOM \ + {5, 11.6f, 1.000f, -1000, -300, 0, 2.31f, 0.64f, 1.00f, -711, 0.012f, 0.00f,0.00f,0.00f, 83, 0.017f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define REVERB_PRESET_AUDITORIUM \ + {6, 21.6f, 1.000f, -1000, -476, 0, 4.32f, 0.59f, 1.00f, -789, 0.020f, 0.00f,0.00f,0.00f, -289, 0.030f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define REVERB_PRESET_CONCERTHALL \ + {7, 19.6f, 1.000f, -1000, -500, 0, 3.92f, 0.70f, 1.00f, -1230, 0.020f, 0.00f,0.00f,0.00f, -02, 0.029f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define REVERB_PRESET_CAVE \ + {8, 14.6f, 1.000f, -1000, 0, 0, 2.91f, 1.30f, 1.00f, -602, 0.015f, 0.00f,0.00f,0.00f, -302, 0.022f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x1f } +#define REVERB_PRESET_ARENA \ + {9, 36.2f, 1.000f, -1000, -698, 0, 7.24f, 0.33f, 1.00f, -1166, 0.020f, 0.00f,0.00f,0.00f, 16, 0.030f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define REVERB_PRESET_HANGAR \ + {10, 50.3f, 1.000f, -1000, -1000, 0, 10.05f, 0.23f, 1.00f, -602, 0.020f, 0.00f,0.00f,0.00f, 198, 0.030f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define REVERB_PRESET_CARPETTEDHALLWAY \ + {11, 1.9f, 1.000f, -1000, -4000, 0, 0.30f, 0.10f, 1.00f, -1831, 0.002f, 0.00f,0.00f,0.00f, -1630, 0.030f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define REVERB_PRESET_HALLWAY \ + {12, 1.8f, 1.000f, -1000, -300, 0, 1.49f, 0.59f, 1.00f, -1219, 0.007f, 0.00f,0.00f,0.00f, 441, 0.011f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define REVERB_PRESET_STONECORRIDOR \ + {13, 13.5f, 1.000f, -1000, -237, 0, 2.70f, 0.79f, 1.00f, -1214, 0.013f, 0.00f,0.00f,0.00f, 395, 0.020f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define REVERB_PRESET_ALLEY \ + {14, 7.5f, 0.300f, -1000, -270, 0, 1.49f, 0.86f, 1.00f, -1204, 0.007f, 0.00f,0.00f,0.00f, -4, 0.011f, 0.00f,0.00f,0.00f, 0.125f, 0.950f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define REVERB_PRESET_FOREST \ + {15, 38.0f, 0.300f, -1000, -3300, 0, 1.49f, 0.54f, 1.00f, -2560, 0.162f, 0.00f,0.00f,0.00f, -229, 0.088f, 0.00f,0.00f,0.00f, 0.125f, 1.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define REVERB_PRESET_CITY \ + {16, 7.5f, 0.500f, -1000, -800, 0, 1.49f, 0.67f, 1.00f, -2273, 0.007f, 0.00f,0.00f,0.00f, -1691, 0.011f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define REVERB_PRESET_MOUNTAINS \ + {17, 100.0f, 0.270f, -1000, -2500, 0, 1.49f, 0.21f, 1.00f, -2780, 0.300f, 0.00f,0.00f,0.00f, -1434, 0.100f, 0.00f,0.00f,0.00f, 0.250f, 1.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x1f } +#define REVERB_PRESET_QUARRY \ + {18, 17.5f, 1.000f, -1000, -1000, 0, 1.49f, 0.83f, 1.00f, -10000, 0.061f, 0.00f,0.00f,0.00f, 500, 0.025f, 0.00f,0.00f,0.00f, 0.125f, 0.700f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define REVERB_PRESET_PLAIN \ + {19, 42.5f, 0.210f, -1000, -2000, 0, 1.49f, 0.50f, 1.00f, -2466, 0.179f, 0.00f,0.00f,0.00f, -1926, 0.100f, 0.00f,0.00f,0.00f, 0.250f, 1.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define REVERB_PRESET_PARKINGLOT \ + {20, 8.3f, 1.000f, -1000, 0, 0, 1.65f, 1.50f, 1.00f, -1363, 0.008f, 0.00f,0.00f,0.00f, -1153, 0.012f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x1f } +#define REVERB_PRESET_SEWERPIPE \ + {21, 1.7f, 0.800f, -1000, -1000, 0, 2.81f, 0.14f, 1.00f, 429, 0.014f, 0.00f,0.00f,0.00f, 1023, 0.021f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define REVERB_PRESET_UNDERWATER \ + {22, 1.8f, 1.000f, -1000, -4000, 0, 1.49f, 0.10f, 1.00f, -449, 0.007f, 0.00f,0.00f,0.00f, 1700, 0.011f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 1.180f, 0.348f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define REVERB_PRESET_DRUGGED \ + {23, 1.9f, 0.500f, -1000, 0, 0, 8.39f, 1.39f, 1.00f, -115, 0.002f, 0.00f,0.00f,0.00f, 985, 0.030f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 1.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x1f } +#define REVERB_PRESET_DIZZY \ + {24, 1.8f, 0.600f, -1000, -400, 0, 17.23f, 0.56f, 1.00f, -1713, 0.020f, 0.00f,0.00f,0.00f, -613, 0.030f, 0.00f,0.00f,0.00f, 0.250f, 1.000f, 0.810f, 0.310f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x1f } +#define REVERB_PRESET_PSYCHOTIC \ + {25, 1.0f, 0.500f, -1000, -151, 0, 7.56f, 0.91f, 1.00f, -626, 0.020f, 0.00f,0.00f,0.00f, 774, 0.030f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 4.000f, 1.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x1f } + + +// CASTLE PRESETS + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define REVERB_PRESET_CASTLE_SMALLROOM \ + { 26, 8.3f, 0.890f, -1000, -800, -2000, 1.22f, 0.83f, 0.31f, -100, 0.022f, 0.00f,0.00f,0.00f, 600, 0.011f, 0.00f,0.00f,0.00f, 0.138f, 0.080f, 0.250f, 0.000f, -5.0f, 5168.6f, 139.5f, 0.00f, 0x20 } +#define REVERB_PRESET_CASTLE_SHORTPASSAGE \ + { 26, 8.3f, 0.890f, -1000, -1000, -2000, 2.32f, 0.83f, 0.31f, -100, 0.007f, 0.00f,0.00f,0.00f, 200, 0.023f, 0.00f,0.00f,0.00f, 0.138f, 0.080f, 0.250f, 0.000f, -5.0f, 5168.6f, 139.5f, 0.00f, 0x20 } +#define REVERB_PRESET_CASTLE_MEDIUMROOM \ + { 26, 8.3f, 0.930f, -1000, -1100, -2000, 2.04f, 0.83f, 0.46f, -400, 0.022f, 0.00f,0.00f,0.00f, 400, 0.011f, 0.00f,0.00f,0.00f, 0.155f, 0.030f, 0.250f, 0.000f, -5.0f, 5168.6f, 139.5f, 0.00f, 0x20 } +#define REVERB_PRESET_CASTLE_LONGPASSAGE \ + { 26, 8.3f, 0.890f, -1000, -800, -2000, 3.42f, 0.83f, 0.31f, -100, 0.007f, 0.00f,0.00f,0.00f, 300, 0.023f, 0.00f,0.00f,0.00f, 0.138f, 0.080f, 0.250f, 0.000f, -5.0f, 5168.6f, 139.5f, 0.00f, 0x20 } +#define REVERB_PRESET_CASTLE_LARGEROOM \ + { 26, 8.3f, 0.820f, -1000, -1100, -1800, 2.53f, 0.83f, 0.50f, -700, 0.034f, 0.00f,0.00f,0.00f, 200, 0.016f, 0.00f,0.00f,0.00f, 0.185f, 0.070f, 0.250f, 0.000f, -5.0f, 5168.6f, 139.5f, 0.00f, 0x20 } +#define REVERB_PRESET_CASTLE_HALL \ + { 26, 8.3f, 0.810f, -1000, -1100, -1500, 3.14f, 0.79f, 0.62f, -1500, 0.056f, 0.00f,0.00f,0.00f, 100, 0.024f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5168.6f, 139.5f, 0.00f, 0x20 } +#define REVERB_PRESET_CASTLE_CUPBOARD \ + { 26, 8.3f, 0.890f, -1000, -1100, -2000, 0.67f, 0.87f, 0.31f, 300, 0.010f, 0.00f,0.00f,0.00f, 1100, 0.007f, 0.00f,0.00f,0.00f, 0.138f, 0.080f, 0.250f, 0.000f, -5.0f, 5168.6f, 139.5f, 0.00f, 0x20 } +#define REVERB_PRESET_CASTLE_COURTYARD \ + { 26, 8.3f, 0.420f, -1000, -700, -1400, 2.13f, 0.61f, 0.23f, -1300, 0.160f, 0.00f,0.00f,0.00f, -300, 0.036f, 0.00f,0.00f,0.00f, 0.250f, 0.370f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x1f } +#define REVERB_PRESET_CASTLE_ALCOVE \ + { 26, 8.3f, 0.890f, -1000, -600, -2000, 1.64f, 0.87f, 0.31f, 00, 0.007f, 0.00f,0.00f,0.00f, 300, 0.034f, 0.00f,0.00f,0.00f, 0.138f, 0.080f, 0.250f, 0.000f, -5.0f, 5168.6f, 139.5f, 0.00f, 0x20 } + + +// FACTORY PRESETS + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define REVERB_PRESET_FACTORY_ALCOVE \ + { 26, 1.8f, 0.590f, -1200, -200, -600, 3.14f, 0.65f, 1.31f, 300, 0.010f, 0.00f,0.00f,0.00f, 000, 0.038f, 0.00f,0.00f,0.00f, 0.114f, 0.100f, 0.250f, 0.000f, -5.0f, 3762.6f, 362.5f, 0.00f, 0x20 } +#define REVERB_PRESET_FACTORY_SHORTPASSAGE \ + { 26, 1.8f, 0.640f, -1200, -200, -600, 2.53f, 0.65f, 1.31f, 0, 0.010f, 0.00f,0.00f,0.00f, 200, 0.038f, 0.00f,0.00f,0.00f, 0.135f, 0.230f, 0.250f, 0.000f, -5.0f, 3762.6f, 362.5f, 0.00f, 0x20 } +#define REVERB_PRESET_FACTORY_MEDIUMROOM \ + { 26, 1.9f, 0.820f, -1200, -200, -600, 2.76f, 0.65f, 1.31f, -1100, 0.022f, 0.00f,0.00f,0.00f, 300, 0.023f, 0.00f,0.00f,0.00f, 0.174f, 0.070f, 0.250f, 0.000f, -5.0f, 3762.6f, 362.5f, 0.00f, 0x20 } +#define REVERB_PRESET_FACTORY_LONGPASSAGE \ + { 26, 1.8f, 0.640f, -1200, -200, -600, 4.06f, 0.65f, 1.31f, 0, 0.020f, 0.00f,0.00f,0.00f, 200, 0.037f, 0.00f,0.00f,0.00f, 0.135f, 0.230f, 0.250f, 0.000f, -5.0f, 3762.6f, 362.5f, 0.00f, 0x20 } +#define REVERB_PRESET_FACTORY_LARGEROOM \ + { 26, 1.9f, 0.750f, -1200, -300, -400, 4.24f, 0.51f, 1.31f, -1500, 0.039f, 0.00f,0.00f,0.00f, 100, 0.023f, 0.00f,0.00f,0.00f, 0.231f, 0.070f, 0.250f, 0.000f, -5.0f, 3762.6f, 362.5f, 0.00f, 0x20 } +#define REVERB_PRESET_FACTORY_HALL \ + { 26, 1.9f, 0.750f, -1000, -300, -400, 7.43f, 0.51f, 1.31f, -2400, 0.073f, 0.00f,0.00f,0.00f, -100, 0.027f, 0.00f,0.00f,0.00f, 0.250f, 0.070f, 0.250f, 0.000f, -5.0f, 3762.6f, 362.5f, 0.00f, 0x20 } +#define REVERB_PRESET_FACTORY_CUPBOARD \ + { 26, 1.7f, 0.630f, -1200, -200, -600, 0.49f, 0.65f, 1.31f, 200, 0.010f, 0.00f,0.00f,0.00f, 600, 0.032f, 0.00f,0.00f,0.00f, 0.107f, 0.070f, 0.250f, 0.000f, -5.0f, 3762.6f, 362.5f, 0.00f, 0x20 } +#define REVERB_PRESET_FACTORY_COURTYARD \ + { 26, 1.7f, 0.570f, -1000, -1000, -400, 2.32f, 0.29f, 0.56f, -1300, 0.140f, 0.00f,0.00f,0.00f, -800, 0.039f, 0.00f,0.00f,0.00f, 0.250f, 0.290f, 0.250f, 0.000f, -5.0f, 3762.6f, 362.5f, 0.00f, 0x20 } +#define REVERB_PRESET_FACTORY_SMALLROOM \ + { 26, 1.8f, 0.820f, -1000, -200, -600, 1.72f, 0.65f, 1.31f, -300, 0.010f, 0.00f,0.00f,0.00f, 500, 0.024f, 0.00f,0.00f,0.00f, 0.119f, 0.070f, 0.250f, 0.000f, -5.0f, 3762.6f, 362.5f, 0.00f, 0x20 } + + +// ICE PALACE PRESETS + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define REVERB_PRESET_ICEPALACE_ALCOVE \ + { 26, 2.7f, 0.840f, -1000, -500, -1100, 2.76f, 1.46f, 0.28f, 100, 0.010f, 0.00f,0.00f,0.00f, -100, 0.030f, 0.00f,0.00f,0.00f, 0.161f, 0.090f, 0.250f, 0.000f, -5.0f, 12428.5f, 99.6f, 0.00f, 0x20 } +#define REVERB_PRESET_ICEPALACE_SHORTPASSAGE \ + { 26, 2.7f, 0.750f, -1000, -500, -1100, 1.79f, 1.46f, 0.28f, -600, 0.010f, 0.00f,0.00f,0.00f, 100, 0.019f, 0.00f,0.00f,0.00f, 0.177f, 0.090f, 0.250f, 0.000f, -5.0f, 12428.5f, 99.6f, 0.00f, 0x20 } +#define REVERB_PRESET_ICEPALACE_MEDIUMROOM \ + { 26, 2.7f, 0.870f, -1000, -500, -700, 2.22f, 1.53f, 0.32f, -800, 0.039f, 0.00f,0.00f,0.00f, 100, 0.027f, 0.00f,0.00f,0.00f, 0.186f, 0.120f, 0.250f, 0.000f, -5.0f, 12428.5f, 99.6f, 0.00f, 0x20 } +#define REVERB_PRESET_ICEPALACE_LONGPASSAGE \ + { 26, 2.7f, 0.770f, -1000, -500, -800, 3.01f, 1.46f, 0.28f, -200, 0.012f, 0.00f,0.00f,0.00f, 200, 0.025f, 0.00f,0.00f,0.00f, 0.186f, 0.040f, 0.250f, 0.000f, -5.0f, 12428.5f, 99.6f, 0.00f, 0x20 } +#define REVERB_PRESET_ICEPALACE_LARGEROOM \ + { 26, 2.9f, 0.810f, -1000, -500, -700, 3.14f, 1.53f, 0.32f, -1200, 0.039f, 0.00f,0.00f,0.00f, 000, 0.027f, 0.00f,0.00f,0.00f, 0.214f, 0.110f, 0.250f, 0.000f, -5.0f, 12428.5f, 99.6f, 0.00f, 0x20 } +#define REVERB_PRESET_ICEPALACE_HALL \ + { 26, 2.9f, 0.760f, -1000, -700, -500, 5.49f, 1.53f, 0.38f, -1900, 0.054f, 0.00f,0.00f,0.00f, -400, 0.052f, 0.00f,0.00f,0.00f, 0.226f, 0.110f, 0.250f, 0.000f, -5.0f, 12428.5f, 99.6f, 0.00f, 0x20 } +#define REVERB_PRESET_ICEPALACE_CUPBOARD \ + { 26, 2.7f, 0.830f, -1000, -600, -1300, 0.76f, 1.53f, 0.26f, 100, 0.012f, 0.00f,0.00f,0.00f, 600, 0.016f, 0.00f,0.00f,0.00f, 0.143f, 0.080f, 0.250f, 0.000f, -5.0f, 12428.5f, 99.6f, 0.00f, 0x20 } +#define REVERB_PRESET_ICEPALACE_COURTYARD \ + { 26, 2.9f, 0.590f, -1000, -1100, -1000, 2.04f, 1.20f, 0.38f, -1000, 0.173f, 0.00f,0.00f,0.00f, -1000, 0.043f, 0.00f,0.00f,0.00f, 0.235f, 0.480f, 0.250f, 0.000f, -5.0f, 12428.5f, 99.6f, 0.00f, 0x20 } +#define REVERB_PRESET_ICEPALACE_SMALLROOM \ + { 26, 2.7f, 0.840f, -1000, -500, -1100, 1.51f, 1.53f, 0.27f, -100, 0.010f, 0.00f,0.00f,0.00f, 300, 0.011f, 0.00f,0.00f,0.00f, 0.164f, 0.140f, 0.250f, 0.000f, -5.0f, 12428.5f, 99.6f, 0.00f, 0x20 } + + +// SPACE STATION PRESETS + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define REVERB_PRESET_SPACESTATION_ALCOVE \ + { 26, 1.5f, 0.780f, -1000, -300, -100, 1.16f, 0.81f, 0.55f, 300, 0.007f, 0.00f,0.00f,0.00f, 000, 0.018f, 0.00f,0.00f,0.00f, 0.192f, 0.210f, 0.250f, 0.000f, -5.0f, 3316.1f, 458.2f, 0.00f, 0x20 } +#define REVERB_PRESET_SPACESTATION_MEDIUMROOM \ + { 26, 1.5f, 0.750f, -1000, -400, -100, 3.01f, 0.50f, 0.55f, -800, 0.034f, 0.00f,0.00f,0.00f, 100, 0.035f, 0.00f,0.00f,0.00f, 0.209f, 0.310f, 0.250f, 0.000f, -5.0f, 3316.1f, 458.2f, 0.00f, 0x20 } +#define REVERB_PRESET_SPACESTATION_SHORTPASSAGE \ + { 26, 1.5f, 0.870f, -1000, -400, -100, 3.57f, 0.50f, 0.55f, 0, 0.012f, 0.00f,0.00f,0.00f, 100, 0.016f, 0.00f,0.00f,0.00f, 0.172f, 0.200f, 0.250f, 0.000f, -5.0f, 3316.1f, 458.2f, 0.00f, 0x20 } +#define REVERB_PRESET_SPACESTATION_LONGPASSAGE \ + { 26, 1.9f, 0.820f, -1000, -400, -100, 4.62f, 0.62f, 0.55f, 0, 0.012f, 0.00f,0.00f,0.00f, 200, 0.031f, 0.00f,0.00f,0.00f, 0.250f, 0.230f, 0.250f, 0.000f, -5.0f, 3316.1f, 458.2f, 0.00f, 0x20 } +#define REVERB_PRESET_SPACESTATION_LARGEROOM \ + { 26, 1.8f, 0.810f, -1000, -400, -100, 3.89f, 0.38f, 0.61f, -1000, 0.056f, 0.00f,0.00f,0.00f, -100, 0.035f, 0.00f,0.00f,0.00f, 0.233f, 0.280f, 0.250f, 0.000f, -5.0f, 3316.1f, 458.2f, 0.00f, 0x20 } +#define REVERB_PRESET_SPACESTATION_HALL \ + { 26, 1.9f, 0.870f, -1000, -400, -100, 7.11f, 0.38f, 0.61f, -1500, 0.100f, 0.00f,0.00f,0.00f, -400, 0.047f, 0.00f,0.00f,0.00f, 0.250f, 0.250f, 0.250f, 0.000f, -5.0f, 3316.1f, 458.2f, 0.00f, 0x20 } +#define REVERB_PRESET_SPACESTATION_CUPBOARD \ + { 26, 1.4f, 0.560f, -1000, -300, -100, 0.79f, 0.81f, 0.55f, 300, 0.007f, 0.00f,0.00f,0.00f, 500, 0.018f, 0.00f,0.00f,0.00f, 0.181f, 0.310f, 0.250f, 0.000f, -5.0f, 3316.1f, 458.2f, 0.00f, 0x20 } +#define REVERB_PRESET_SPACESTATION_SMALLROOM \ + { 26, 1.5f, 0.700f, -1000, -300, -100, 1.72f, 0.82f, 0.55f, -200, 0.007f, 0.00f,0.00f,0.00f, 300, 0.013f, 0.00f,0.00f,0.00f, 0.188f, 0.260f, 0.250f, 0.000f, -5.0f, 3316.1f, 458.2f, 0.00f, 0x20 } + + +// WOODEN GALLEON PRESETS + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define REVERB_PRESET_WOODEN_ALCOVE \ + { 26, 7.5f, 1.000f, -1000, -1800, -1000, 1.22f, 0.62f, 0.91f, 100, 0.012f, 0.00f,0.00f,0.00f, -300, 0.024f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 4705.0f, 99.6f, 0.00f, 0x3f } +#define REVERB_PRESET_WOODEN_SHORTPASSAGE \ + { 26, 7.5f, 1.000f, -1000, -1800, -1000, 1.75f, 0.50f, 0.87f, -100, 0.012f, 0.00f,0.00f,0.00f, -400, 0.024f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 4705.0f, 99.6f, 0.00f, 0x3f } +#define REVERB_PRESET_WOODEN_MEDIUMROOM \ + { 26, 7.5f, 1.000f, -1000, -2000, -1100, 1.47f, 0.42f, 0.82f, -100, 0.049f, 0.00f,0.00f,0.00f, -100, 0.029f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 4705.0f, 99.6f, 0.00f, 0x3f } +#define REVERB_PRESET_WOODEN_LONGPASSAGE \ + { 26, 7.5f, 1.000f, -1000, -2000, -1000, 1.99f, 0.40f, 0.79f, 000, 0.020f, 0.00f,0.00f,0.00f, -700, 0.036f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 4705.0f, 99.6f, 0.00f, 0x3f } +#define REVERB_PRESET_WOODEN_LARGEROOM \ + { 26, 7.5f, 1.000f, -1000, -2100, -1100, 2.65f, 0.33f, 0.82f, -100, 0.066f, 0.00f,0.00f,0.00f, -200, 0.049f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 4705.0f, 99.6f, 0.00f, 0x3f } +#define REVERB_PRESET_WOODEN_HALL \ + { 26, 7.5f, 1.000f, -1000, -2200, -1100, 3.45f, 0.30f, 0.82f, -100, 0.088f, 0.00f,0.00f,0.00f, -200, 0.063f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 4705.0f, 99.6f, 0.00f, 0x3f } +#define REVERB_PRESET_WOODEN_CUPBOARD \ + { 26, 7.5f, 1.000f, -1000, -1700, -1000, 0.56f, 0.46f, 0.91f, 100, 0.012f, 0.00f,0.00f,0.00f, 100, 0.028f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 4705.0f, 99.6f, 0.00f, 0x3f } +#define REVERB_PRESET_WOODEN_SMALLROOM \ + { 26, 7.5f, 1.000f, -1000, -1900, -1000, 0.79f, 0.32f, 0.87f, 00, 0.032f, 0.00f,0.00f,0.00f, -100, 0.029f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 4705.0f, 99.6f, 0.00f, 0x3f } +#define REVERB_PRESET_WOODEN_COURTYARD \ + { 26, 7.5f, 0.650f, -1000, -2200, -1000, 1.79f, 0.35f, 0.79f, -500, 0.123f, 0.00f,0.00f,0.00f, -2000, 0.032f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 4705.0f, 99.6f, 0.00f, 0x3f } + + +// SPORTS PRESETS + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define REVERB_PRESET_SPORT_EMPTYSTADIUM \ + { 26, 7.2f, 1.000f, -1000, -700, -200, 6.26f, 0.51f, 1.10f, -2400, 0.183f, 0.00f,0.00f,0.00f, -800, 0.038f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x20 } +#define REVERB_PRESET_SPORT_SQUASHCOURT \ + { 26, 7.5f, 0.750f, -1000, -1000, -200, 2.22f, 0.91f, 1.16f, -700, 0.007f, 0.00f,0.00f,0.00f, -200, 0.011f, 0.00f,0.00f,0.00f, 0.126f, 0.190f, 0.250f, 0.000f, -5.0f, 7176.9f, 211.2f, 0.00f, 0x20 } +#define REVERB_PRESET_SPORT_SMALLSWIMMINGPOOL \ + { 26, 36.2f, 0.700f, -1000, -200, -100, 2.76f, 1.25f, 1.14f, -400, 0.020f, 0.00f,0.00f,0.00f, -200, 0.030f, 0.00f,0.00f,0.00f, 0.179f, 0.150f, 0.895f, 0.190f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x0 } +#define REVERB_PRESET_SPORT_LARGESWIMMINGPOOL\ + { 26, 36.2f, 0.820f, -1000, -200, 0, 5.49f, 1.31f, 1.14f, -700, 0.039f, 0.00f,0.00f,0.00f, -600, 0.049f, 0.00f,0.00f,0.00f, 0.222f, 0.550f, 1.159f, 0.210f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x0 } +#define REVERB_PRESET_SPORT_GYMNASIUM \ + { 26, 7.5f, 0.810f, -1000, -700, -100, 3.14f, 1.06f, 1.35f, -800, 0.029f, 0.00f,0.00f,0.00f, -500, 0.045f, 0.00f,0.00f,0.00f, 0.146f, 0.140f, 0.250f, 0.000f, -5.0f, 7176.9f, 211.2f, 0.00f, 0x20 } +#define REVERB_PRESET_SPORT_FULLSTADIUM \ + { 26, 7.2f, 1.000f, -1000, -2300, -200, 5.25f, 0.17f, 0.80f, -2000, 0.188f, 0.00f,0.00f,0.00f, -1100, 0.038f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x20 } +#define REVERB_PRESET_SPORT_STADIUMTANNOY \ + { 26, 3.0f, 0.780f, -1000, -500, -600, 2.53f, 0.88f, 0.68f, -1100, 0.230f, 0.00f,0.00f,0.00f, -600, 0.063f, 0.00f,0.00f,0.00f, 0.250f, 0.200f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x20 } + + +// PREFAB PRESETS + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define REVERB_PRESET_PREFAB_WORKSHOP \ + { 26, 1.9f, 1.000f, -1000, -1700, -800, 0.76f, 1.00f, 1.00f, 0, 0.012f, 0.00f,0.00f,0.00f, 100, 0.012f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x0 } +#define REVERB_PRESET_PREFAB_SCHOOLROOM \ + { 26, 1.86f, 0.690f, -1000, -400, -600, 0.98f, 0.45f, 0.18f, 300, 0.017f, 0.00f,0.00f,0.00f, 300, 0.015f, 0.00f,0.00f,0.00f, 0.095f, 0.140f, 0.250f, 0.000f, -5.0f, 7176.9f, 211.2f, 0.00f, 0x20 } +#define REVERB_PRESET_PREFAB_PRACTISEROOM \ + { 26, 1.86f, 0.870f, -1000, -800, -600, 1.12f, 0.56f, 0.18f, 200, 0.010f, 0.00f,0.00f,0.00f, 300, 0.011f, 0.00f,0.00f,0.00f, 0.095f, 0.140f, 0.250f, 0.000f, -5.0f, 7176.9f, 211.2f, 0.00f, 0x20 } +#define REVERB_PRESET_PREFAB_OUTHOUSE \ + { 26, 80.3f, 0.820f, -1000, -1900, -1600, 1.38f, 0.38f, 0.35f, -100, 0.024f, 0.00f,0.00f,-0.00f, -400, 0.044f, 0.00f,0.00f,0.00f, 0.121f, 0.170f, 0.250f, 0.000f, -5.0f, 2854.4f, 107.5f, 0.00f, 0x0 } +#define REVERB_PRESET_PREFAB_CARAVAN \ + { 26, 8.3f, 1.000f, -1000, -2100, -1800, 0.43f, 1.50f, 1.00f, 0, 0.012f, 0.00f,0.00f,0.00f, 600, 0.012f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x1f } + // for US developers, a caravan is the same as a trailer =o) + + +// DOME AND PIPE PRESETS + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define REVERB_PRESET_DOME_TOMB \ + { 26, 51.8f, 0.790f, -1000, -900, -1300, 4.18f, 0.21f, 0.10f, -825, 0.030f, 0.00f,0.00f,0.00f, 450, 0.022f, 0.00f,0.00f,0.00f, 0.177f, 0.190f, 0.250f, 0.000f, -5.0f, 2854.4f, 20.0f, 0.00f, 0x0 } +#define REVERB_PRESET_PIPE_SMALL \ + { 26, 50.3f, 1.000f, -1000, -900, -1300, 5.04f, 0.10f, 0.10f, -600, 0.032f, 0.00f,0.00f,0.00f, 800, 0.015f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 2854.4f, 20.0f, 0.00f, 0x3f } +#define REVERB_PRESET_DOME_SAINTPAULS \ + { 26, 50.3f, 0.870f, -1000, -900, -1300, 10.48f, 0.19f, 0.10f, -1500, 0.090f, 0.00f,0.00f,0.00f, 200, 0.042f, 0.00f,0.00f,0.00f, 0.250f, 0.120f, 0.250f, 0.000f, -5.0f, 2854.4f, 20.0f, 0.00f, 0x3f } +#define REVERB_PRESET_PIPE_LONGTHIN \ + { 26, 1.6f, 0.910f, -1000, -700, -1100, 9.21f, 0.18f, 0.10f, -300, 0.010f, 0.00f,0.00f,0.00f, -300, 0.022f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 2854.4f, 20.0f, 0.00f, 0x0 } +#define REVERB_PRESET_PIPE_LARGE \ + { 26, 50.3f, 1.000f, -1000, -900, -1300, 8.45f, 0.10f, 0.10f, -800, 0.046f, 0.00f,0.00f,0.00f, 400, 0.032f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 2854.4f, 20.0f, 0.00f, 0x3f } +#define REVERB_PRESET_PIPE_RESONANT \ + { 26, 1.3f, 0.910f, -1000, -700, -1100, 6.81f, 0.18f, 0.10f, -300, 0.010f, 0.00f,0.00f,0.00f, 00, 0.022f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 2854.4f, 20.0f, 0.00f, 0x0 } + + +// OUTDOORS PRESETS + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define REVERB_PRESET_OUTDOORS_BACKYARD \ + { 26, 80.3f, 0.450f, -1000, -1200, -600, 1.12f, 0.34f, 0.46f, -700, 0.069f, 0.00f,0.00f,-0.00f, -300, 0.023f, 0.00f,0.00f,0.00f, 0.218f, 0.340f, 0.250f, 0.000f, -5.0f, 4399.1f, 242.9f, 0.00f, 0x0 } +#define REVERB_PRESET_OUTDOORS_ROLLINGPLAINS \ + { 26, 80.3f, 0.000f, -1000, -3900, -400, 2.13f, 0.21f, 0.46f, -1500, 0.300f, 0.00f,0.00f,-0.00f, -700, 0.019f, 0.00f,0.00f,0.00f, 0.250f, 1.000f, 0.250f, 0.000f, -5.0f, 4399.1f, 242.9f, 0.00f, 0x0 } +#define REVERB_PRESET_OUTDOORS_DEEPCANYON \ + { 26, 80.3f, 0.740f, -1000, -1500, -400, 3.89f, 0.21f, 0.46f, -1000, 0.223f, 0.00f,0.00f,-0.00f, -900, 0.019f, 0.00f,0.00f,0.00f, 0.250f, 1.000f, 0.250f, 0.000f, -5.0f, 4399.1f, 242.9f, 0.00f, 0x0 } +#define REVERB_PRESET_OUTDOORS_CREEK \ + { 26, 80.3f, 0.350f, -1000, -1500, -600, 2.13f, 0.21f, 0.46f, -800, 0.115f, 0.00f,0.00f,-0.00f, -1400, 0.031f, 0.00f,0.00f,0.00f, 0.218f, 0.340f, 0.250f, 0.000f, -5.0f, 4399.1f, 242.9f, 0.00f, 0x0 } +#define REVERB_PRESET_OUTDOORS_VALLEY \ + { 26, 80.3f, 0.280f, -1000, -3100, -1600, 2.88f, 0.26f, 0.35f, -1700, 0.263f, 0.00f,0.00f,-0.00f, -800, 0.100f, 0.00f,0.00f,0.00f, 0.250f, 0.340f, 0.250f, 0.000f, -5.0f, 2854.4f, 107.5f, 0.00f, 0x0 } + + +// MOOD PRESETS + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define REVERB_PRESET_MOOD_HEAVEN \ + { 26, 19.6f, 0.940f, -1000, -200, -700, 5.04f, 1.12f, 0.56f, -1230, 0.020f, 0.00f,0.00f,0.00f, 200, 0.029f, 0.00f,0.00f,0.00f, 0.250f, 0.080f, 2.742f, 0.050f, -2.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define REVERB_PRESET_MOOD_HELL \ + { 26, 100.0f, 0.570f, -1000, -900, -700, 3.57f, 0.49f, 2.00f, -10000, 0.020f, 0.00f,0.00f,0.00f, 300, 0.030f, 0.00f,0.00f,0.00f, 0.110f, 0.040f, 2.109f, 0.520f, -5.0f, 5000.0f, 139.5f, 0.00f, 0x40 } +#define REVERB_PRESET_MOOD_MEMORY \ + { 26, 8.0f, 0.850f, -1000, -400, -900, 4.06f, 0.82f, 0.56f, -2800, 0.000f, 0.00f,0.00f,0.00f, 100, 0.000f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.474f, 0.450f, -10.0f, 5000.0f, 250.0f, 0.00f, 0x0 } + + +// DRIVING SIMULATION PRESETS + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define REVERB_PRESET_DRIVING_COMMENTATOR \ + { 26, 3.0f, 0.000f, 1000, -500, -600, 2.42f, 0.88f, 0.68f, -1400, 0.093f, 0.00f,0.00f,0.00f, -1200, 0.017f, 0.00f,0.00f,0.00f, 0.250f, 1.000f, 0.250f, 0.000f, -10.0f, 5000.0f, 250.0f, 0.00f, 0x20 } +#define REVERB_PRESET_DRIVING_PITGARAGE \ + { 26, 1.9f, 0.590f, -1000, -300, -500, 1.72f, 0.93f, 0.87f, -500, 0.000f, 0.00f,0.00f,0.00f, 200, 0.016f, 0.00f,0.00f,0.00f, 0.250f, 0.110f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x0 } +#define REVERB_PRESET_DRIVING_INCAR_RACER \ + { 26, 1.1f, 0.800f, -1000, 0, -200, 0.17f, 2.00f, 0.41f, 500, 0.007f, 0.00f,0.00f,0.00f, -300, 0.015f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 10268.2f, 251.0f, 0.00f, 0x20 } +#define REVERB_PRESET_DRIVING_INCAR_SPORTS \ + { 26, 1.1f, 0.800f, -1000, -400, 0, 0.17f, 0.75f, 0.41f, 0, 0.010f, 0.00f,0.00f,0.00f, -500, 0.000f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 10268.2f, 251.0f, 0.00f, 0x20 } +#define REVERB_PRESET_DRIVING_INCAR_LUXURY \ + { 26, 1.6f, 1.000f, -1000, -2000, -600, 0.13f, 0.41f, 0.46f, -200, 0.010f, 0.00f,0.00f,0.00f, 400, 0.010f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 10268.2f, 251.0f, 0.00f, 0x20 } +#define REVERB_PRESET_DRIVING_FULLGRANDSTAND \ + { 26, 8.3f, 1.000f, -1000, -1100, -400, 3.01f, 1.37f, 1.28f, -900, 0.090f, 0.00f,0.00f,0.00f, -1500, 0.049f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 10420.2f, 250.0f, 0.00f, 0x1f } +#define REVERB_PRESET_DRIVING_EMPTYGRANDSTAND \ + { 26, 8.3f, 1.000f, -1000, 0, -200, 4.62f, 1.75f, 1.40f, -1363, 0.090f, 0.00f,0.00f,0.00f, -1200, 0.049f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.000f, -5.0f, 10420.2f, 250.0f, 0.00f, 0x1f } +#define REVERB_PRESET_DRIVING_TUNNEL \ + { 26, 3.1f, 0.810f, -1000, -800, -100, 3.42f, 0.94f, 1.31f, -300, 0.051f, 0.00f,0.00f,0.00f, -300, 0.047f, 0.00f,0.00f,0.00f, 0.214f, 0.050f, 0.250f, 0.000f, -5.0f, 5000.0f, 155.3f, 0.00f, 0x20 } + + +// CITY PRESETS + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define REVERB_PRESET_CITY_STREETS \ + { 26, 3.0f, 0.780f, -1000, -300, -100, 1.79f, 1.12f, 0.91f, -1100, 0.046f, 0.00f,0.00f,0.00f, -1400, 0.028f, 0.00f,0.00f,0.00f, 0.250f, 0.200f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x20 } +#define REVERB_PRESET_CITY_SUBWAY \ + { 26, 3.0f, 0.740f, -1000, -300, -100, 3.01f, 1.23f, 0.91f, -300, 0.046f, 0.00f,0.00f,0.00f, 200, 0.028f, 0.00f,0.00f,0.00f, 0.125f, 0.210f, 0.250f, 0.000f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x20 } +#define REVERB_PRESET_CITY_MUSEUM \ + { 26, 80.3f, 0.820f, -1000, -1500, -1500, 3.28f, 1.40f, 0.57f, -1200, 0.039f, 0.00f,0.00f,-0.00f, -100, 0.034f, 0.00f,0.00f,0.00f, 0.130f, 0.170f, 0.250f, 0.000f, -5.0f, 2854.4f, 107.5f, 0.00f, 0x0 } +#define REVERB_PRESET_CITY_LIBRARY \ + { 26, 80.3f, 0.820f, -1000, -1100, -2100, 2.76f, 0.89f, 0.41f, -900, 0.029f, 0.00f,0.00f,-0.00f, -100, 0.020f, 0.00f,0.00f,0.00f, 0.130f, 0.170f, 0.250f, 0.000f, -5.0f, 2854.4f, 107.5f, 0.00f, 0x0 } +#define REVERB_PRESET_CITY_UNDERPASS \ + { 26, 3.0f, 0.820f, -1000, -700, -100, 3.57f, 1.12f, 0.91f, -800, 0.059f, 0.00f,0.00f,0.00f, -100, 0.037f, 0.00f,0.00f,0.00f, 0.250f, 0.140f, 0.250f, 0.000f, -7.0f, 5000.0f, 250.0f, 0.00f, 0x20 } +#define REVERB_PRESET_CITY_ABANDONED \ + { 26, 3.0f, 0.690f, -1000, -200, -100, 3.28f, 1.17f, 0.91f, -700, 0.044f, 0.00f,0.00f,0.00f, -1100, 0.024f, 0.00f,0.00f,0.00f, 0.250f, 0.200f, 0.250f, 0.000f, -3.0f, 5000.0f, 250.0f, 0.00f, 0x20 } + + +// MISC ROOMS + +// Env Size Diffus Room RoomHF RoomLF DecTm DcHF DcLF Refl RefDel Ref Pan Revb RevDel Rev Pan EchTm EchDp ModTm ModDp AirAbs HFRef LFRef RRlOff FLAGS +#define REVERB_PRESET_DUSTYROOM \ + { 26, 1.8f, 0.560f, -1000, -200, -300, 1.79f, 0.38f, 0.21f, -600, 0.002f, 0.00f,0.00f,0.00f, 200, 0.006f, 0.00f,0.00f,0.00f, 0.202f, 0.050f, 0.250f, 0.000f, -10.0f, 13046.0f, 163.3f, 0.00f, 0x20 } +#define REVERB_PRESET_CHAPEL \ + { 26, 19.6f, 0.840f, -1000, -500, 0, 4.62f, 0.64f, 1.23f, -700, 0.032f, 0.00f,0.00f,0.00f, -200, 0.049f, 0.00f,0.00f,0.00f, 0.250f, 0.000f, 0.250f, 0.110f, -5.0f, 5000.0f, 250.0f, 0.00f, 0x3f } +#define REVERB_PRESET_SMALLWATERROOM \ + { 26, 36.2f, 0.700f, -1000, -698, 0, 1.51f, 1.25f, 1.14f, -100, 0.020f, 0.00f,0.00f,0.00f, 300, 0.030f, 0.00f,0.00f,0.00f, 0.179f, 0.150f, 0.895f, 0.190f, -7.0f, 5000.0f, 250.0f, 0.00f, 0x0 } diff --git a/Engine/lib/openal/win32/al/al.h b/Engine/lib/openal/win32/al/al.h new file mode 100644 index 000000000..981b0890b --- /dev/null +++ b/Engine/lib/openal/win32/al/al.h @@ -0,0 +1,750 @@ +#ifndef AL_AL_H +#define AL_AL_H + +/** + * OpenAL cross platform audio library + * Copyright (C) 1999-2000 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#if defined(__cplusplus) +extern "C" { +#endif + +#if defined(_WIN32) && !defined(_XBOX) + /* _OPENAL32LIB is deprecated */ + #if defined(AL_BUILD_LIBRARY) || defined (_OPENAL32LIB) + #define AL_API __declspec(dllexport) + #else + #define AL_API __declspec(dllimport) + #endif +#else + #define AL_API extern +#endif + +#if defined(_WIN32) + #define AL_APIENTRY __cdecl +#else + #define AL_APIENTRY +#endif + +#if TARGET_OS_MAC + #pragma export on +#endif + +/* The OPENAL, ALAPI, and ALAPIENTRY macros are deprecated, but are included for applications porting code + from AL 1.0 */ +#define OPENAL +#define ALAPI AL_API +#define ALAPIENTRY AL_APIENTRY + +#define AL_VERSION_1_0 +#define AL_VERSION_1_1 + + +/** 8-bit boolean */ +typedef char ALboolean; + +/** character */ +typedef char ALchar; + +/** signed 8-bit 2's complement integer */ +typedef char ALbyte; + +/** unsigned 8-bit integer */ +typedef unsigned char ALubyte; + +/** signed 16-bit 2's complement integer */ +typedef short ALshort; + +/** unsigned 16-bit integer */ +typedef unsigned short ALushort; + +/** signed 32-bit 2's complement integer */ +typedef int ALint; + +/** unsigned 32-bit integer */ +typedef unsigned int ALuint; + +/** non-negative 32-bit binary integer size */ +typedef int ALsizei; + +/** enumerated 32-bit value */ +typedef int ALenum; + +/** 32-bit IEEE754 floating-point */ +typedef float ALfloat; + +/** 64-bit IEEE754 floating-point */ +typedef double ALdouble; + +/** void type (for opaque pointers only) */ +typedef void ALvoid; + + +/* Enumerant values begin at column 50. No tabs. */ + +/* bad value */ +#define AL_INVALID -1 + +#define AL_NONE 0 + +/* Boolean False. */ +#define AL_FALSE 0 + +/** Boolean True. */ +#define AL_TRUE 1 + +/** Indicate Source has relative coordinates. */ +#define AL_SOURCE_RELATIVE 0x202 + + + +/** + * Directional source, inner cone angle, in degrees. + * Range: [0-360] + * Default: 360 + */ +#define AL_CONE_INNER_ANGLE 0x1001 + +/** + * Directional source, outer cone angle, in degrees. + * Range: [0-360] + * Default: 360 + */ +#define AL_CONE_OUTER_ANGLE 0x1002 + +/** + * Specify the pitch to be applied, either at source, + * or on mixer results, at listener. + * Range: [0.5-2.0] + * Default: 1.0 + */ +#define AL_PITCH 0x1003 + +/** + * Specify the current location in three dimensional space. + * OpenAL, like OpenGL, uses a right handed coordinate system, + * where in a frontal default view X (thumb) points right, + * Y points up (index finger), and Z points towards the + * viewer/camera (middle finger). + * To switch from a left handed coordinate system, flip the + * sign on the Z coordinate. + * Listener position is always in the world coordinate system. + */ +#define AL_POSITION 0x1004 + +/** Specify the current direction. */ +#define AL_DIRECTION 0x1005 + +/** Specify the current velocity in three dimensional space. */ +#define AL_VELOCITY 0x1006 + +/** + * Indicate whether source is looping. + * Type: ALboolean? + * Range: [AL_TRUE, AL_FALSE] + * Default: FALSE. + */ +#define AL_LOOPING 0x1007 + +/** + * Indicate the buffer to provide sound samples. + * Type: ALuint. + * Range: any valid Buffer id. + */ +#define AL_BUFFER 0x1009 + +/** + * Indicate the gain (volume amplification) applied. + * Type: ALfloat. + * Range: ]0.0- ] + * A value of 1.0 means un-attenuated/unchanged. + * Each division by 2 equals an attenuation of -6dB. + * Each multiplicaton with 2 equals an amplification of +6dB. + * A value of 0.0 is meaningless with respect to a logarithmic + * scale; it is interpreted as zero volume - the channel + * is effectively disabled. + */ +#define AL_GAIN 0x100A + +/* + * Indicate minimum source attenuation + * Type: ALfloat + * Range: [0.0 - 1.0] + * + * Logarthmic + */ +#define AL_MIN_GAIN 0x100D + +/** + * Indicate maximum source attenuation + * Type: ALfloat + * Range: [0.0 - 1.0] + * + * Logarthmic + */ +#define AL_MAX_GAIN 0x100E + +/** + * Indicate listener orientation. + * + * at/up + */ +#define AL_ORIENTATION 0x100F + +/** + * Specify the channel mask. (Creative) + * Type: ALuint + * Range: [0 - 255] + */ +#define AL_CHANNEL_MASK 0x3000 + + +/** + * Source state information. + */ +#define AL_SOURCE_STATE 0x1010 +#define AL_INITIAL 0x1011 +#define AL_PLAYING 0x1012 +#define AL_PAUSED 0x1013 +#define AL_STOPPED 0x1014 + +/** + * Buffer Queue params + */ +#define AL_BUFFERS_QUEUED 0x1015 +#define AL_BUFFERS_PROCESSED 0x1016 + +/** + * Source buffer position information + */ +#define AL_SEC_OFFSET 0x1024 +#define AL_SAMPLE_OFFSET 0x1025 +#define AL_BYTE_OFFSET 0x1026 + +/* + * Source type (Static, Streaming or undetermined) + * Source is Static if a Buffer has been attached using AL_BUFFER + * Source is Streaming if one or more Buffers have been attached using alSourceQueueBuffers + * Source is undetermined when it has the NULL buffer attached + */ +#define AL_SOURCE_TYPE 0x1027 +#define AL_STATIC 0x1028 +#define AL_STREAMING 0x1029 +#define AL_UNDETERMINED 0x1030 + +/** Sound samples: format specifier. */ +#define AL_FORMAT_MONO8 0x1100 +#define AL_FORMAT_MONO16 0x1101 +#define AL_FORMAT_STEREO8 0x1102 +#define AL_FORMAT_STEREO16 0x1103 + +/** + * source specific reference distance + * Type: ALfloat + * Range: 0.0 - +inf + * + * At 0.0, no distance attenuation occurs. Default is + * 1.0. + */ +#define AL_REFERENCE_DISTANCE 0x1020 + +/** + * source specific rolloff factor + * Type: ALfloat + * Range: 0.0 - +inf + * + */ +#define AL_ROLLOFF_FACTOR 0x1021 + +/** + * Directional source, outer cone gain. + * + * Default: 0.0 + * Range: [0.0 - 1.0] + * Logarithmic + */ +#define AL_CONE_OUTER_GAIN 0x1022 + +/** + * Indicate distance above which sources are not + * attenuated using the inverse clamped distance model. + * + * Default: +inf + * Type: ALfloat + * Range: 0.0 - +inf + */ +#define AL_MAX_DISTANCE 0x1023 + +/** + * Sound samples: frequency, in units of Hertz [Hz]. + * This is the number of samples per second. Half of the + * sample frequency marks the maximum significant + * frequency component. + */ +#define AL_FREQUENCY 0x2001 +#define AL_BITS 0x2002 +#define AL_CHANNELS 0x2003 +#define AL_SIZE 0x2004 + +/** + * Buffer state. + * + * Not supported for public use (yet). + */ +#define AL_UNUSED 0x2010 +#define AL_PENDING 0x2011 +#define AL_PROCESSED 0x2012 + + +/** Errors: No Error. */ +#define AL_NO_ERROR AL_FALSE + +/** + * Invalid Name paramater passed to AL call. + */ +#define AL_INVALID_NAME 0xA001 + +/** + * Invalid parameter passed to AL call. + */ +#define AL_ILLEGAL_ENUM 0xA002 +#define AL_INVALID_ENUM 0xA002 + +/** + * Invalid enum parameter value. + */ +#define AL_INVALID_VALUE 0xA003 + +/** + * Illegal call. + */ +#define AL_ILLEGAL_COMMAND 0xA004 +#define AL_INVALID_OPERATION 0xA004 + + +/** + * No mojo. + */ +#define AL_OUT_OF_MEMORY 0xA005 + + +/** Context strings: Vendor Name. */ +#define AL_VENDOR 0xB001 +#define AL_VERSION 0xB002 +#define AL_RENDERER 0xB003 +#define AL_EXTENSIONS 0xB004 + +/** Global tweakage. */ + +/** + * Doppler scale. Default 1.0 + */ +#define AL_DOPPLER_FACTOR 0xC000 + +/** + * Tweaks speed of propagation. + */ +#define AL_DOPPLER_VELOCITY 0xC001 + +/** + * Speed of Sound in units per second + */ +#define AL_SPEED_OF_SOUND 0xC003 + +/** + * Distance models + * + * used in conjunction with DistanceModel + * + * implicit: NONE, which disances distance attenuation. + */ +#define AL_DISTANCE_MODEL 0xD000 +#define AL_INVERSE_DISTANCE 0xD001 +#define AL_INVERSE_DISTANCE_CLAMPED 0xD002 +#define AL_LINEAR_DISTANCE 0xD003 +#define AL_LINEAR_DISTANCE_CLAMPED 0xD004 +#define AL_EXPONENT_DISTANCE 0xD005 +#define AL_EXPONENT_DISTANCE_CLAMPED 0xD006 + + +#if !defined(AL_NO_PROTOTYPES) + +/* + * Renderer State management + */ +AL_API void AL_APIENTRY alEnable( ALenum capability ); + +AL_API void AL_APIENTRY alDisable( ALenum capability ); + +AL_API ALboolean AL_APIENTRY alIsEnabled( ALenum capability ); + + +/* + * State retrieval + */ +AL_API const ALchar* AL_APIENTRY alGetString( ALenum param ); + +AL_API void AL_APIENTRY alGetBooleanv( ALenum param, ALboolean* data ); + +AL_API void AL_APIENTRY alGetIntegerv( ALenum param, ALint* data ); + +AL_API void AL_APIENTRY alGetFloatv( ALenum param, ALfloat* data ); + +AL_API void AL_APIENTRY alGetDoublev( ALenum param, ALdouble* data ); + +AL_API ALboolean AL_APIENTRY alGetBoolean( ALenum param ); + +AL_API ALint AL_APIENTRY alGetInteger( ALenum param ); + +AL_API ALfloat AL_APIENTRY alGetFloat( ALenum param ); + +AL_API ALdouble AL_APIENTRY alGetDouble( ALenum param ); + + +/* + * Error support. + * Obtain the most recent error generated in the AL state machine. + */ +AL_API ALenum AL_APIENTRY alGetError( void ); + + +/* + * Extension support. + * Query for the presence of an extension, and obtain any appropriate + * function pointers and enum values. + */ +AL_API ALboolean AL_APIENTRY alIsExtensionPresent( const ALchar* extname ); + +AL_API void* AL_APIENTRY alGetProcAddress( const ALchar* fname ); + +AL_API ALenum AL_APIENTRY alGetEnumValue( const ALchar* ename ); + + +/* + * LISTENER + * Listener represents the location and orientation of the + * 'user' in 3D-space. + * + * Properties include: - + * + * Gain AL_GAIN ALfloat + * Position AL_POSITION ALfloat[3] + * Velocity AL_VELOCITY ALfloat[3] + * Orientation AL_ORIENTATION ALfloat[6] (Forward then Up vectors) +*/ + +/* + * Set Listener parameters + */ +AL_API void AL_APIENTRY alListenerf( ALenum param, ALfloat value ); + +AL_API void AL_APIENTRY alListener3f( ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ); + +AL_API void AL_APIENTRY alListenerfv( ALenum param, const ALfloat* values ); + +AL_API void AL_APIENTRY alListeneri( ALenum param, ALint value ); + +AL_API void AL_APIENTRY alListener3i( ALenum param, ALint value1, ALint value2, ALint value3 ); + +AL_API void AL_APIENTRY alListeneriv( ALenum param, const ALint* values ); + +/* + * Get Listener parameters + */ +AL_API void AL_APIENTRY alGetListenerf( ALenum param, ALfloat* value ); + +AL_API void AL_APIENTRY alGetListener3f( ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3 ); + +AL_API void AL_APIENTRY alGetListenerfv( ALenum param, ALfloat* values ); + +AL_API void AL_APIENTRY alGetListeneri( ALenum param, ALint* value ); + +AL_API void AL_APIENTRY alGetListener3i( ALenum param, ALint *value1, ALint *value2, ALint *value3 ); + +AL_API void AL_APIENTRY alGetListeneriv( ALenum param, ALint* values ); + + +/** + * SOURCE + * Sources represent individual sound objects in 3D-space. + * Sources take the PCM data provided in the specified Buffer, + * apply Source-specific modifications, and then + * submit them to be mixed according to spatial arrangement etc. + * + * Properties include: - + * + * Gain AL_GAIN ALfloat + * Min Gain AL_MIN_GAIN ALfloat + * Max Gain AL_MAX_GAIN ALfloat + * Position AL_POSITION ALfloat[3] + * Velocity AL_VELOCITY ALfloat[3] + * Direction AL_DIRECTION ALfloat[3] + * Head Relative Mode AL_SOURCE_RELATIVE ALint (AL_TRUE or AL_FALSE) + * Reference Distance AL_REFERENCE_DISTANCE ALfloat + * Max Distance AL_MAX_DISTANCE ALfloat + * RollOff Factor AL_ROLLOFF_FACTOR ALfloat + * Inner Angle AL_CONE_INNER_ANGLE ALint or ALfloat + * Outer Angle AL_CONE_OUTER_ANGLE ALint or ALfloat + * Cone Outer Gain AL_CONE_OUTER_GAIN ALint or ALfloat + * Pitch AL_PITCH ALfloat + * Looping AL_LOOPING ALint (AL_TRUE or AL_FALSE) + * MS Offset AL_MSEC_OFFSET ALint or ALfloat + * Byte Offset AL_BYTE_OFFSET ALint or ALfloat + * Sample Offset AL_SAMPLE_OFFSET ALint or ALfloat + * Attached Buffer AL_BUFFER ALint + * State (Query only) AL_SOURCE_STATE ALint + * Buffers Queued (Query only) AL_BUFFERS_QUEUED ALint + * Buffers Processed (Query only) AL_BUFFERS_PROCESSED ALint + */ + +/* Create Source objects */ +AL_API void AL_APIENTRY alGenSources( ALsizei n, ALuint* sources ); + +/* Delete Source objects */ +AL_API void AL_APIENTRY alDeleteSources( ALsizei n, const ALuint* sources ); + +/* Verify a handle is a valid Source */ +AL_API ALboolean AL_APIENTRY alIsSource( ALuint sid ); + +/* + * Set Source parameters + */ +AL_API void AL_APIENTRY alSourcef( ALuint sid, ALenum param, ALfloat value ); + +AL_API void AL_APIENTRY alSource3f( ALuint sid, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ); + +AL_API void AL_APIENTRY alSourcefv( ALuint sid, ALenum param, const ALfloat* values ); + +AL_API void AL_APIENTRY alSourcei( ALuint sid, ALenum param, ALint value ); + +AL_API void AL_APIENTRY alSource3i( ALuint sid, ALenum param, ALint value1, ALint value2, ALint value3 ); + +AL_API void AL_APIENTRY alSourceiv( ALuint sid, ALenum param, const ALint* values ); + +/* + * Get Source parameters + */ +AL_API void AL_APIENTRY alGetSourcef( ALuint sid, ALenum param, ALfloat* value ); + +AL_API void AL_APIENTRY alGetSource3f( ALuint sid, ALenum param, ALfloat* value1, ALfloat* value2, ALfloat* value3); + +AL_API void AL_APIENTRY alGetSourcefv( ALuint sid, ALenum param, ALfloat* values ); + +AL_API void AL_APIENTRY alGetSourcei( ALuint sid, ALenum param, ALint* value ); + +AL_API void AL_APIENTRY alGetSource3i( ALuint sid, ALenum param, ALint* value1, ALint* value2, ALint* value3); + +AL_API void AL_APIENTRY alGetSourceiv( ALuint sid, ALenum param, ALint* values ); + + +/* + * Source vector based playback calls + */ + +/* Play, replay, or resume (if paused) a list of Sources */ +AL_API void AL_APIENTRY alSourcePlayv( ALsizei ns, const ALuint *sids ); + +/* Stop a list of Sources */ +AL_API void AL_APIENTRY alSourceStopv( ALsizei ns, const ALuint *sids ); + +/* Rewind a list of Sources */ +AL_API void AL_APIENTRY alSourceRewindv( ALsizei ns, const ALuint *sids ); + +/* Pause a list of Sources */ +AL_API void AL_APIENTRY alSourcePausev( ALsizei ns, const ALuint *sids ); + +/* + * Source based playback calls + */ + +/* Play, replay, or resume a Source */ +AL_API void AL_APIENTRY alSourcePlay( ALuint sid ); + +/* Stop a Source */ +AL_API void AL_APIENTRY alSourceStop( ALuint sid ); + +/* Rewind a Source (set playback postiton to beginning) */ +AL_API void AL_APIENTRY alSourceRewind( ALuint sid ); + +/* Pause a Source */ +AL_API void AL_APIENTRY alSourcePause( ALuint sid ); + +/* + * Source Queuing + */ +AL_API void AL_APIENTRY alSourceQueueBuffers( ALuint sid, ALsizei numEntries, const ALuint *bids ); + +AL_API void AL_APIENTRY alSourceUnqueueBuffers( ALuint sid, ALsizei numEntries, ALuint *bids ); + + +/** + * BUFFER + * Buffer objects are storage space for sample data. + * Buffers are referred to by Sources. One Buffer can be used + * by multiple Sources. + * + * Properties include: - + * + * Frequency (Query only) AL_FREQUENCY ALint + * Size (Query only) AL_SIZE ALint + * Bits (Query only) AL_BITS ALint + * Channels (Query only) AL_CHANNELS ALint + */ + +/* Create Buffer objects */ +AL_API void AL_APIENTRY alGenBuffers( ALsizei n, ALuint* buffers ); + +/* Delete Buffer objects */ +AL_API void AL_APIENTRY alDeleteBuffers( ALsizei n, const ALuint* buffers ); + +/* Verify a handle is a valid Buffer */ +AL_API ALboolean AL_APIENTRY alIsBuffer( ALuint bid ); + +/* Specify the data to be copied into a buffer */ +AL_API void AL_APIENTRY alBufferData( ALuint bid, ALenum format, const ALvoid* data, ALsizei size, ALsizei freq ); + +/* + * Set Buffer parameters + */ +AL_API void AL_APIENTRY alBufferf( ALuint bid, ALenum param, ALfloat value ); + +AL_API void AL_APIENTRY alBuffer3f( ALuint bid, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ); + +AL_API void AL_APIENTRY alBufferfv( ALuint bid, ALenum param, const ALfloat* values ); + +AL_API void AL_APIENTRY alBufferi( ALuint bid, ALenum param, ALint value ); + +AL_API void AL_APIENTRY alBuffer3i( ALuint bid, ALenum param, ALint value1, ALint value2, ALint value3 ); + +AL_API void AL_APIENTRY alBufferiv( ALuint bid, ALenum param, const ALint* values ); + +/* + * Get Buffer parameters + */ +AL_API void AL_APIENTRY alGetBufferf( ALuint bid, ALenum param, ALfloat* value ); + +AL_API void AL_APIENTRY alGetBuffer3f( ALuint bid, ALenum param, ALfloat* value1, ALfloat* value2, ALfloat* value3); + +AL_API void AL_APIENTRY alGetBufferfv( ALuint bid, ALenum param, ALfloat* values ); + +AL_API void AL_APIENTRY alGetBufferi( ALuint bid, ALenum param, ALint* value ); + +AL_API void AL_APIENTRY alGetBuffer3i( ALuint bid, ALenum param, ALint* value1, ALint* value2, ALint* value3); + +AL_API void AL_APIENTRY alGetBufferiv( ALuint bid, ALenum param, ALint* values ); + + +/* + * Global Parameters + */ +AL_API void AL_APIENTRY alDopplerFactor( ALfloat value ); + +AL_API void AL_APIENTRY alDopplerVelocity( ALfloat value ); + +AL_API void AL_APIENTRY alSpeedOfSound( ALfloat value ); + +AL_API void AL_APIENTRY alDistanceModel( ALenum distanceModel ); + +#else /* AL_NO_PROTOTYPES */ + +typedef void (AL_APIENTRY *LPALENABLE)( ALenum capability ); +typedef void (AL_APIENTRY *LPALDISABLE)( ALenum capability ); +typedef ALboolean (AL_APIENTRY *LPALISENABLED)( ALenum capability ); +typedef const ALchar* (AL_APIENTRY *LPALGETSTRING)( ALenum param ); +typedef void (AL_APIENTRY *LPALGETBOOLEANV)( ALenum param, ALboolean* data ); +typedef void (AL_APIENTRY *LPALGETINTEGERV)( ALenum param, ALint* data ); +typedef void (AL_APIENTRY *LPALGETFLOATV)( ALenum param, ALfloat* data ); +typedef void (AL_APIENTRY *LPALGETDOUBLEV)( ALenum param, ALdouble* data ); +typedef ALboolean (AL_APIENTRY *LPALGETBOOLEAN)( ALenum param ); +typedef ALint (AL_APIENTRY *LPALGETINTEGER)( ALenum param ); +typedef ALfloat (AL_APIENTRY *LPALGETFLOAT)( ALenum param ); +typedef ALdouble (AL_APIENTRY *LPALGETDOUBLE)( ALenum param ); +typedef ALenum (AL_APIENTRY *LPALGETERROR)( void ); +typedef ALboolean (AL_APIENTRY *LPALISEXTENSIONPRESENT)(const ALchar* extname ); +typedef void* (AL_APIENTRY *LPALGETPROCADDRESS)( const ALchar* fname ); +typedef ALenum (AL_APIENTRY *LPALGETENUMVALUE)( const ALchar* ename ); +typedef void (AL_APIENTRY *LPALLISTENERF)( ALenum param, ALfloat value ); +typedef void (AL_APIENTRY *LPALLISTENER3F)( ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ); +typedef void (AL_APIENTRY *LPALLISTENERFV)( ALenum param, const ALfloat* values ); +typedef void (AL_APIENTRY *LPALLISTENERI)( ALenum param, ALint value ); +typedef void (AL_APIENTRY *LPALLISTENER3I)( ALenum param, ALint value1, ALint value2, ALint value3 ); +typedef void (AL_APIENTRY *LPALLISTENERIV)( ALenum param, const ALint* values ); +typedef void (AL_APIENTRY *LPALGETLISTENERF)( ALenum param, ALfloat* value ); +typedef void (AL_APIENTRY *LPALGETLISTENER3F)( ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3 ); +typedef void (AL_APIENTRY *LPALGETLISTENERFV)( ALenum param, ALfloat* values ); +typedef void (AL_APIENTRY *LPALGETLISTENERI)( ALenum param, ALint* value ); +typedef void (AL_APIENTRY *LPALGETLISTENER3I)( ALenum param, ALint *value1, ALint *value2, ALint *value3 ); +typedef void (AL_APIENTRY *LPALGETLISTENERIV)( ALenum param, ALint* values ); +typedef void (AL_APIENTRY *LPALGENSOURCES)( ALsizei n, ALuint* sources ); +typedef void (AL_APIENTRY *LPALDELETESOURCES)( ALsizei n, const ALuint* sources ); +typedef ALboolean (AL_APIENTRY *LPALISSOURCE)( ALuint sid ); +typedef void (AL_APIENTRY *LPALSOURCEF)( ALuint sid, ALenum param, ALfloat value); +typedef void (AL_APIENTRY *LPALSOURCE3F)( ALuint sid, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ); +typedef void (AL_APIENTRY *LPALSOURCEFV)( ALuint sid, ALenum param, const ALfloat* values ); +typedef void (AL_APIENTRY *LPALSOURCEI)( ALuint sid, ALenum param, ALint value); +typedef void (AL_APIENTRY *LPALSOURCE3I)( ALuint sid, ALenum param, ALint value1, ALint value2, ALint value3 ); +typedef void (AL_APIENTRY *LPALSOURCEIV)( ALuint sid, ALenum param, const ALint* values ); +typedef void (AL_APIENTRY *LPALGETSOURCEF)( ALuint sid, ALenum param, ALfloat* value ); +typedef void (AL_APIENTRY *LPALGETSOURCE3F)( ALuint sid, ALenum param, ALfloat* value1, ALfloat* value2, ALfloat* value3); +typedef void (AL_APIENTRY *LPALGETSOURCEFV)( ALuint sid, ALenum param, ALfloat* values ); +typedef void (AL_APIENTRY *LPALGETSOURCEI)( ALuint sid, ALenum param, ALint* value ); +typedef void (AL_APIENTRY *LPALGETSOURCE3I)( ALuint sid, ALenum param, ALint* value1, ALint* value2, ALint* value3); +typedef void (AL_APIENTRY *LPALGETSOURCEIV)( ALuint sid, ALenum param, ALint* values ); +typedef void (AL_APIENTRY *LPALSOURCEPLAYV)( ALsizei ns, const ALuint *sids ); +typedef void (AL_APIENTRY *LPALSOURCESTOPV)( ALsizei ns, const ALuint *sids ); +typedef void (AL_APIENTRY *LPALSOURCEREWINDV)( ALsizei ns, const ALuint *sids ); +typedef void (AL_APIENTRY *LPALSOURCEPAUSEV)( ALsizei ns, const ALuint *sids ); +typedef void (AL_APIENTRY *LPALSOURCEPLAY)( ALuint sid ); +typedef void (AL_APIENTRY *LPALSOURCESTOP)( ALuint sid ); +typedef void (AL_APIENTRY *LPALSOURCEREWIND)( ALuint sid ); +typedef void (AL_APIENTRY *LPALSOURCEPAUSE)( ALuint sid ); +typedef void (AL_APIENTRY *LPALSOURCEQUEUEBUFFERS)(ALuint sid, ALsizei numEntries, const ALuint *bids ); +typedef void (AL_APIENTRY *LPALSOURCEUNQUEUEBUFFERS)(ALuint sid, ALsizei numEntries, ALuint *bids ); +typedef void (AL_APIENTRY *LPALGENBUFFERS)( ALsizei n, ALuint* buffers ); +typedef void (AL_APIENTRY *LPALDELETEBUFFERS)( ALsizei n, const ALuint* buffers ); +typedef ALboolean (AL_APIENTRY *LPALISBUFFER)( ALuint bid ); +typedef void (AL_APIENTRY *LPALBUFFERDATA)( ALuint bid, ALenum format, const ALvoid* data, ALsizei size, ALsizei freq ); +typedef void (AL_APIENTRY *LPALBUFFERF)( ALuint bid, ALenum param, ALfloat value); +typedef void (AL_APIENTRY *LPALBUFFER3F)( ALuint bid, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ); +typedef void (AL_APIENTRY *LPALBUFFERFV)( ALuint bid, ALenum param, const ALfloat* values ); +typedef void (AL_APIENTRY *LPALBUFFERI)( ALuint bid, ALenum param, ALint value); +typedef void (AL_APIENTRY *LPALBUFFER3I)( ALuint bid, ALenum param, ALint value1, ALint value2, ALint value3 ); +typedef void (AL_APIENTRY *LPALBUFFERIV)( ALuint bid, ALenum param, const ALint* values ); +typedef void (AL_APIENTRY *LPALGETBUFFERF)( ALuint bid, ALenum param, ALfloat* value ); +typedef void (AL_APIENTRY *LPALGETBUFFER3F)( ALuint bid, ALenum param, ALfloat* value1, ALfloat* value2, ALfloat* value3); +typedef void (AL_APIENTRY *LPALGETBUFFERFV)( ALuint bid, ALenum param, ALfloat* values ); +typedef void (AL_APIENTRY *LPALGETBUFFERI)( ALuint bid, ALenum param, ALint* value ); +typedef void (AL_APIENTRY *LPALGETBUFFER3I)( ALuint bid, ALenum param, ALint* value1, ALint* value2, ALint* value3); +typedef void (AL_APIENTRY *LPALGETBUFFERIV)( ALuint bid, ALenum param, ALint* values ); +typedef void (AL_APIENTRY *LPALDOPPLERFACTOR)( ALfloat value ); +typedef void (AL_APIENTRY *LPALDOPPLERVELOCITY)( ALfloat value ); +typedef void (AL_APIENTRY *LPALSPEEDOFSOUND)( ALfloat value ); +typedef void (AL_APIENTRY *LPALDISTANCEMODEL)( ALenum distanceModel ); + +#endif /* AL_NO_PROTOTYPES */ + +#if TARGET_OS_MAC + #pragma export off +#endif + +#if defined(__cplusplus) +} /* extern "C" */ +#endif + +#endif /* AL_AL_H */ diff --git a/Engine/lib/openal/win32/al/alc.h b/Engine/lib/openal/win32/al/alc.h new file mode 100644 index 000000000..07e287dac --- /dev/null +++ b/Engine/lib/openal/win32/al/alc.h @@ -0,0 +1,273 @@ +#ifndef AL_ALC_H +#define AL_ALC_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#if defined(_WIN32) && !defined(_XBOX) + /* _OPENAL32LIB is deprecated */ + #if defined(AL_BUILD_LIBRARY) || defined (_OPENAL32LIB) + #define ALC_API __declspec(dllexport) + #else + #define ALC_API __declspec(dllimport) + #endif +#else + #define ALC_API extern +#endif + +#if defined(_WIN32) + #define ALC_APIENTRY __cdecl +#else + #define ALC_APIENTRY +#endif + +#if TARGET_OS_MAC + #pragma export on +#endif + +/* The ALCAPI, and ALCAPIENTRY macros are deprecated, but are included for applications porting code + from AL 1.0 */ +#define ALCAPI ALC_API +#define ALCAPIENTRY ALC_APIENTRY + +#define ALC_VERSION_0_1 1 + +typedef struct ALCdevice_struct ALCdevice; +typedef struct ALCcontext_struct ALCcontext; + + +/** 8-bit boolean */ +typedef char ALCboolean; + +/** character */ +typedef char ALCchar; + +/** signed 8-bit 2's complement integer */ +typedef char ALCbyte; + +/** unsigned 8-bit integer */ +typedef unsigned char ALCubyte; + +/** signed 16-bit 2's complement integer */ +typedef short ALCshort; + +/** unsigned 16-bit integer */ +typedef unsigned short ALCushort; + +/** signed 32-bit 2's complement integer */ +typedef int ALCint; + +/** unsigned 32-bit integer */ +typedef unsigned int ALCuint; + +/** non-negative 32-bit binary integer size */ +typedef int ALCsizei; + +/** enumerated 32-bit value */ +typedef int ALCenum; + +/** 32-bit IEEE754 floating-point */ +typedef float ALCfloat; + +/** 64-bit IEEE754 floating-point */ +typedef double ALCdouble; + +/** void type (for opaque pointers only) */ +typedef void ALCvoid; + + +/* Enumerant values begin at column 50. No tabs. */ + +/* bad value */ +#define ALC_INVALID 0 + +/* Boolean False. */ +#define ALC_FALSE 0 + +/* Boolean True. */ +#define ALC_TRUE 1 + +/** + * followed by Hz + */ +#define ALC_FREQUENCY 0x1007 + +/** + * followed by Hz + */ +#define ALC_REFRESH 0x1008 + +/** + * followed by AL_TRUE, AL_FALSE + */ +#define ALC_SYNC 0x1009 + +/** + * followed by Num of requested Mono (3D) Sources + */ +#define ALC_MONO_SOURCES 0x1010 + +/** + * followed by Num of requested Stereo Sources + */ +#define ALC_STEREO_SOURCES 0x1011 + +/** + * errors + */ + +/** + * No error + */ +#define ALC_NO_ERROR ALC_FALSE + +/** + * No device + */ +#define ALC_INVALID_DEVICE 0xA001 + +/** + * invalid context ID + */ +#define ALC_INVALID_CONTEXT 0xA002 + +/** + * bad enum + */ +#define ALC_INVALID_ENUM 0xA003 + +/** + * bad value + */ +#define ALC_INVALID_VALUE 0xA004 + +/** + * Out of memory. + */ +#define ALC_OUT_OF_MEMORY 0xA005 + + +/** + * The Specifier string for default device + */ +#define ALC_DEFAULT_DEVICE_SPECIFIER 0x1004 +#define ALC_DEVICE_SPECIFIER 0x1005 +#define ALC_EXTENSIONS 0x1006 + +#define ALC_MAJOR_VERSION 0x1000 +#define ALC_MINOR_VERSION 0x1001 + +#define ALC_ATTRIBUTES_SIZE 0x1002 +#define ALC_ALL_ATTRIBUTES 0x1003 + +/** + * Capture extension + */ +#define ALC_CAPTURE_DEVICE_SPECIFIER 0x310 +#define ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER 0x311 +#define ALC_CAPTURE_SAMPLES 0x312 + + +#if !defined(ALC_NO_PROTOTYPES) + +/* + * Context Management + */ +ALC_API ALCcontext * ALC_APIENTRY alcCreateContext( ALCdevice *device, const ALCint* attrlist ); + +ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent( ALCcontext *context ); + +ALC_API void ALC_APIENTRY alcProcessContext( ALCcontext *context ); + +ALC_API void ALC_APIENTRY alcSuspendContext( ALCcontext *context ); + +ALC_API void ALC_APIENTRY alcDestroyContext( ALCcontext *context ); + +ALC_API ALCcontext * ALC_APIENTRY alcGetCurrentContext( ALCvoid ); + +ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice( ALCcontext *context ); + + +/* + * Device Management + */ +ALC_API ALCdevice * ALC_APIENTRY alcOpenDevice( const ALCchar *devicename ); + +ALC_API ALCboolean ALC_APIENTRY alcCloseDevice( ALCdevice *device ); + + +/* + * Error support. + * Obtain the most recent Context error + */ +ALC_API ALCenum ALC_APIENTRY alcGetError( ALCdevice *device ); + + +/* + * Extension support. + * Query for the presence of an extension, and obtain any appropriate + * function pointers and enum values. + */ +ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent( ALCdevice *device, const ALCchar *extname ); + +ALC_API void * ALC_APIENTRY alcGetProcAddress( ALCdevice *device, const ALCchar *funcname ); + +ALC_API ALCenum ALC_APIENTRY alcGetEnumValue( ALCdevice *device, const ALCchar *enumname ); + + +/* + * Query functions + */ +ALC_API const ALCchar * ALC_APIENTRY alcGetString( ALCdevice *device, ALCenum param ); + +ALC_API void ALC_APIENTRY alcGetIntegerv( ALCdevice *device, ALCenum param, ALCsizei size, ALCint *data ); + + +/* + * Capture functions + */ +ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice( const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize ); + +ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice( ALCdevice *device ); + +ALC_API void ALC_APIENTRY alcCaptureStart( ALCdevice *device ); + +ALC_API void ALC_APIENTRY alcCaptureStop( ALCdevice *device ); + +ALC_API void ALC_APIENTRY alcCaptureSamples( ALCdevice *device, ALCvoid *buffer, ALCsizei samples ); + +#else /* ALC_NO_PROTOTYPES */ + +typedef ALCcontext * (ALC_APIENTRY *LPALCCREATECONTEXT) (ALCdevice *device, const ALCint *attrlist); +typedef ALCboolean (ALC_APIENTRY *LPALCMAKECONTEXTCURRENT)( ALCcontext *context ); +typedef void (ALC_APIENTRY *LPALCPROCESSCONTEXT)( ALCcontext *context ); +typedef void (ALC_APIENTRY *LPALCSUSPENDCONTEXT)( ALCcontext *context ); +typedef void (ALC_APIENTRY *LPALCDESTROYCONTEXT)( ALCcontext *context ); +typedef ALCcontext * (ALC_APIENTRY *LPALCGETCURRENTCONTEXT)( ALCvoid ); +typedef ALCdevice * (ALC_APIENTRY *LPALCGETCONTEXTSDEVICE)( ALCcontext *context ); +typedef ALCdevice * (ALC_APIENTRY *LPALCOPENDEVICE)( const ALCchar *devicename ); +typedef ALCboolean (ALC_APIENTRY *LPALCCLOSEDEVICE)( ALCdevice *device ); +typedef ALCenum (ALC_APIENTRY *LPALCGETERROR)( ALCdevice *device ); +typedef ALCboolean (ALC_APIENTRY *LPALCISEXTENSIONPRESENT)( ALCdevice *device, const ALCchar *extname ); +typedef void * (ALC_APIENTRY *LPALCGETPROCADDRESS)(ALCdevice *device, const ALCchar *funcname ); +typedef ALCenum (ALC_APIENTRY *LPALCGETENUMVALUE)(ALCdevice *device, const ALCchar *enumname ); +typedef const ALCchar* (ALC_APIENTRY *LPALCGETSTRING)( ALCdevice *device, ALCenum param ); +typedef void (ALC_APIENTRY *LPALCGETINTEGERV)( ALCdevice *device, ALCenum param, ALCsizei size, ALCint *dest ); +typedef ALCdevice * (ALC_APIENTRY *LPALCCAPTUREOPENDEVICE)( const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize ); +typedef ALCboolean (ALC_APIENTRY *LPALCCAPTURECLOSEDEVICE)( ALCdevice *device ); +typedef void (ALC_APIENTRY *LPALCCAPTURESTART)( ALCdevice *device ); +typedef void (ALC_APIENTRY *LPALCCAPTURESTOP)( ALCdevice *device ); +typedef void (ALC_APIENTRY *LPALCCAPTURESAMPLES)( ALCdevice *device, ALCvoid *buffer, ALCsizei samples ); + +#endif /* ALC_NO_PROTOTYPES */ + +#if TARGET_OS_MAC + #pragma export off +#endif + +#if defined(__cplusplus) +} +#endif + +#endif /* AL_ALC_H */ diff --git a/Engine/lib/openal/win32/al/efx-creative.h b/Engine/lib/openal/win32/al/efx-creative.h new file mode 100644 index 000000000..4ea9da6b7 --- /dev/null +++ b/Engine/lib/openal/win32/al/efx-creative.h @@ -0,0 +1,151 @@ +#ifndef __efxcreative_h_ +#define __efxcreative_h_ + +/** + * efx-creative.h - Environmental Audio Extensions + * for OpenAL Effects Extension. + * + */ +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * Effect object definitions to be used with alEffect functions. + * + * Effect parameter value definitions, ranges, and defaults + * appear farther down in this file. + */ + +/* AL EAXReverb effect parameters. */ +#define AL_EAXREVERB_DENSITY 0x0001 +#define AL_EAXREVERB_DIFFUSION 0x0002 +#define AL_EAXREVERB_GAIN 0x0003 +#define AL_EAXREVERB_GAINHF 0x0004 +#define AL_EAXREVERB_GAINLF 0x0005 +#define AL_EAXREVERB_DECAY_TIME 0x0006 +#define AL_EAXREVERB_DECAY_HFRATIO 0x0007 +#define AL_EAXREVERB_DECAY_LFRATIO 0x0008 +#define AL_EAXREVERB_REFLECTIONS_GAIN 0x0009 +#define AL_EAXREVERB_REFLECTIONS_DELAY 0x000A +#define AL_EAXREVERB_REFLECTIONS_PAN 0x000B +#define AL_EAXREVERB_LATE_REVERB_GAIN 0x000C +#define AL_EAXREVERB_LATE_REVERB_DELAY 0x000D +#define AL_EAXREVERB_LATE_REVERB_PAN 0x000E +#define AL_EAXREVERB_ECHO_TIME 0x000F +#define AL_EAXREVERB_ECHO_DEPTH 0x0010 +#define AL_EAXREVERB_MODULATION_TIME 0x0011 +#define AL_EAXREVERB_MODULATION_DEPTH 0x0012 +#define AL_EAXREVERB_AIR_ABSORPTION_GAINHF 0x0013 +#define AL_EAXREVERB_HFREFERENCE 0x0014 +#define AL_EAXREVERB_LFREFERENCE 0x0015 +#define AL_EAXREVERB_ROOM_ROLLOFF_FACTOR 0x0016 +#define AL_EAXREVERB_DECAY_HFLIMIT 0x0017 + +/* Effect type definitions to be used with AL_EFFECT_TYPE. */ +#define AL_EFFECT_EAXREVERB 0x8000 + + + + /********************************************************** + * Effect parameter structures, value definitions, ranges and defaults. + */ + +/** + * AL reverb effect parameter ranges and defaults + */ +#define AL_EAXREVERB_MIN_DENSITY 0.0f +#define AL_EAXREVERB_MAX_DENSITY 1.0f +#define AL_EAXREVERB_DEFAULT_DENSITY 1.0f + +#define AL_EAXREVERB_MIN_DIFFUSION 0.0f +#define AL_EAXREVERB_MAX_DIFFUSION 1.0f +#define AL_EAXREVERB_DEFAULT_DIFFUSION 1.0f + +#define AL_EAXREVERB_MIN_GAIN 0.0f +#define AL_EAXREVERB_MAX_GAIN 1.0f +#define AL_EAXREVERB_DEFAULT_GAIN 0.32f + +#define AL_EAXREVERB_MIN_GAINHF 0.0f +#define AL_EAXREVERB_MAX_GAINHF 1.0f +#define AL_EAXREVERB_DEFAULT_GAINHF 0.89f + +#define AL_EAXREVERB_MIN_GAINLF 0.0f +#define AL_EAXREVERB_MAX_GAINLF 1.0f +#define AL_EAXREVERB_DEFAULT_GAINLF 1.0f + +#define AL_EAXREVERB_MIN_DECAY_TIME 0.1f +#define AL_EAXREVERB_MAX_DECAY_TIME 20.0f +#define AL_EAXREVERB_DEFAULT_DECAY_TIME 1.49f + +#define AL_EAXREVERB_MIN_DECAY_HFRATIO 0.1f +#define AL_EAXREVERB_MAX_DECAY_HFRATIO 2.0f +#define AL_EAXREVERB_DEFAULT_DECAY_HFRATIO 0.83f + +#define AL_EAXREVERB_MIN_DECAY_LFRATIO 0.1f +#define AL_EAXREVERB_MAX_DECAY_LFRATIO 2.0f +#define AL_EAXREVERB_DEFAULT_DECAY_LFRATIO 1.0f + +#define AL_EAXREVERB_MIN_REFLECTIONS_GAIN 0.0f +#define AL_EAXREVERB_MAX_REFLECTIONS_GAIN 3.16f +#define AL_EAXREVERB_DEFAULT_REFLECTIONS_GAIN 0.05f + +#define AL_EAXREVERB_MIN_REFLECTIONS_DELAY 0.0f +#define AL_EAXREVERB_MAX_REFLECTIONS_DELAY 0.3f +#define AL_EAXREVERB_DEFAULT_REFLECTIONS_DELAY 0.007f + +#define AL_EAXREVERB_DEFAULT_REFLECTIONS_PAN {0.0f, 0.0f, 0.0f} + +#define AL_EAXREVERB_MIN_LATE_REVERB_GAIN 0.0f +#define AL_EAXREVERB_MAX_LATE_REVERB_GAIN 10.0f +#define AL_EAXREVERB_DEFAULT_LATE_REVERB_GAIN 1.26f + +#define AL_EAXREVERB_MIN_LATE_REVERB_DELAY 0.0f +#define AL_EAXREVERB_MAX_LATE_REVERB_DELAY 0.1f +#define AL_EAXREVERB_DEFAULT_LATE_REVERB_DELAY 0.011f + +#define AL_EAXREVERB_DEFAULT_LATE_REVERB_PAN {0.0f, 0.0f, 0.0f} + +#define AL_EAXREVERB_MIN_ECHO_TIME 0.075f +#define AL_EAXREVERB_MAX_ECHO_TIME 0.25f +#define AL_EAXREVERB_DEFAULT_ECHO_TIME 0.25f + +#define AL_EAXREVERB_MIN_ECHO_DEPTH 0.0f +#define AL_EAXREVERB_MAX_ECHO_DEPTH 1.0f +#define AL_EAXREVERB_DEFAULT_ECHO_DEPTH 0.0f + +#define AL_EAXREVERB_MIN_MODULATION_TIME 0.04f +#define AL_EAXREVERB_MAX_MODULATION_TIME 4.0f +#define AL_EAXREVERB_DEFAULT_MODULATION_TIME 0.25f + +#define AL_EAXREVERB_MIN_MODULATION_DEPTH 0.0f +#define AL_EAXREVERB_MAX_MODULATION_DEPTH 1.0f +#define AL_EAXREVERB_DEFAULT_MODULATION_DEPTH 0.0f + +#define AL_EAXREVERB_MIN_AIR_ABSORPTION_GAINHF 0.892f +#define AL_EAXREVERB_MAX_AIR_ABSORPTION_GAINHF 1.0f +#define AL_EAXREVERB_DEFAULT_AIR_ABSORPTION_GAINHF 0.994f + +#define AL_EAXREVERB_MIN_HFREFERENCE 1000.0f +#define AL_EAXREVERB_MAX_HFREFERENCE 20000.0f +#define AL_EAXREVERB_DEFAULT_HFREFERENCE 5000.0f + +#define AL_EAXREVERB_MIN_LFREFERENCE 20.0f +#define AL_EAXREVERB_MAX_LFREFERENCE 1000.0f +#define AL_EAXREVERB_DEFAULT_LFREFERENCE 250.0f + +#define AL_EAXREVERB_MIN_ROOM_ROLLOFF_FACTOR 0.0f +#define AL_EAXREVERB_MAX_ROOM_ROLLOFF_FACTOR 10.0f +#define AL_EAXREVERB_DEFAULT_ROOM_ROLLOFF_FACTOR 0.0f + +#define AL_EAXREVERB_MIN_DECAY_HFLIMIT AL_FALSE +#define AL_EAXREVERB_MAX_DECAY_HFLIMIT AL_TRUE +#define AL_EAXREVERB_DEFAULT_DECAY_HFLIMIT AL_TRUE + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __efxcreative_h_ */ diff --git a/Engine/lib/openal/win32/al/efx.h b/Engine/lib/openal/win32/al/efx.h new file mode 100644 index 000000000..6b58a1796 --- /dev/null +++ b/Engine/lib/openal/win32/al/efx.h @@ -0,0 +1,756 @@ +#ifndef __efx_h_ +#define __efx_h_ + +/** + * OpenAL cross platform effects extension audio library + * Copyright (C) 2005-2006 by authors. + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * Or go to http://www.gnu.org/copyleft/lgpl.html + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#define ALC_EXT_EFX_NAME "ALC_EXT_EFX" + +/** + * Context definitions to be used with alcCreateContext. + * These values must be unique and not conflict with other + * al context values. + */ +#define ALC_EFX_MAJOR_VERSION 0x20001 +#define ALC_EFX_MINOR_VERSION 0x20002 +#define ALC_MAX_AUXILIARY_SENDS 0x20003 + + + + +/** + * Listener definitions to be used with alListener functions. + * These values must be unique and not conflict with other + * al listener values. + */ +#define AL_METERS_PER_UNIT 0x20004 + + + + +/** + * Source definitions to be used with alSource functions. + * These values must be unique and not conflict with other + * al source values. + */ +#define AL_DIRECT_FILTER 0x20005 +#define AL_AUXILIARY_SEND_FILTER 0x20006 +#define AL_AIR_ABSORPTION_FACTOR 0x20007 +#define AL_ROOM_ROLLOFF_FACTOR 0x20008 +#define AL_CONE_OUTER_GAINHF 0x20009 +#define AL_DIRECT_FILTER_GAINHF_AUTO 0x2000A +#define AL_AUXILIARY_SEND_FILTER_GAIN_AUTO 0x2000B +#define AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO 0x2000C + + + + +/** + * Effect object definitions to be used with alEffect functions. + * + * Effect parameter value definitions, ranges, and defaults + * appear farther down in this file. + */ + +/* Reverb Parameters */ +#define AL_REVERB_DENSITY 0x0001 +#define AL_REVERB_DIFFUSION 0x0002 +#define AL_REVERB_GAIN 0x0003 +#define AL_REVERB_GAINHF 0x0004 +#define AL_REVERB_DECAY_TIME 0x0005 +#define AL_REVERB_DECAY_HFRATIO 0x0006 +#define AL_REVERB_REFLECTIONS_GAIN 0x0007 +#define AL_REVERB_REFLECTIONS_DELAY 0x0008 +#define AL_REVERB_LATE_REVERB_GAIN 0x0009 +#define AL_REVERB_LATE_REVERB_DELAY 0x000A +#define AL_REVERB_AIR_ABSORPTION_GAINHF 0x000B +#define AL_REVERB_ROOM_ROLLOFF_FACTOR 0x000C +#define AL_REVERB_DECAY_HFLIMIT 0x000D + +/* Chorus Parameters */ +#define AL_CHORUS_WAVEFORM 0x0001 +#define AL_CHORUS_PHASE 0x0002 +#define AL_CHORUS_RATE 0x0003 +#define AL_CHORUS_DEPTH 0x0004 +#define AL_CHORUS_FEEDBACK 0x0005 +#define AL_CHORUS_DELAY 0x0006 + +/* Distortion Parameters */ +#define AL_DISTORTION_EDGE 0x0001 +#define AL_DISTORTION_GAIN 0x0002 +#define AL_DISTORTION_LOWPASS_CUTOFF 0x0003 +#define AL_DISTORTION_EQCENTER 0x0004 +#define AL_DISTORTION_EQBANDWIDTH 0x0005 + +/* Echo Parameters */ +#define AL_ECHO_DELAY 0x0001 +#define AL_ECHO_LRDELAY 0x0002 +#define AL_ECHO_DAMPING 0x0003 +#define AL_ECHO_FEEDBACK 0x0004 +#define AL_ECHO_SPREAD 0x0005 + +/* Flanger Parameters */ +#define AL_FLANGER_WAVEFORM 0x0001 +#define AL_FLANGER_PHASE 0x0002 +#define AL_FLANGER_RATE 0x0003 +#define AL_FLANGER_DEPTH 0x0004 +#define AL_FLANGER_FEEDBACK 0x0005 +#define AL_FLANGER_DELAY 0x0006 + +/* Frequencyshifter Parameters */ +#define AL_FREQUENCY_SHIFTER_FREQUENCY 0x0001 +#define AL_FREQUENCY_SHIFTER_LEFT_DIRECTION 0x0002 +#define AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION 0x0003 + +/* Vocalmorpher Parameters */ +#define AL_VOCAL_MORPHER_PHONEMEA 0x0001 +#define AL_VOCAL_MORPHER_PHONEMEA_COARSE_TUNING 0x0002 +#define AL_VOCAL_MORPHER_PHONEMEB 0x0003 +#define AL_VOCAL_MORPHER_PHONEMEB_COARSE_TUNING 0x0004 +#define AL_VOCAL_MORPHER_WAVEFORM 0x0005 +#define AL_VOCAL_MORPHER_RATE 0x0006 + +/* Pitchshifter Parameters */ +#define AL_PITCH_SHIFTER_COARSE_TUNE 0x0001 +#define AL_PITCH_SHIFTER_FINE_TUNE 0x0002 + +/* Ringmodulator Parameters */ +#define AL_RING_MODULATOR_FREQUENCY 0x0001 +#define AL_RING_MODULATOR_HIGHPASS_CUTOFF 0x0002 +#define AL_RING_MODULATOR_WAVEFORM 0x0003 + +/* Autowah Parameters */ +#define AL_AUTOWAH_ATTACK_TIME 0x0001 +#define AL_AUTOWAH_RELEASE_TIME 0x0002 +#define AL_AUTOWAH_RESONANCE 0x0003 +#define AL_AUTOWAH_PEAK_GAIN 0x0004 + +/* Compressor Parameters */ +#define AL_COMPRESSOR_ONOFF 0x0001 + +/* Equalizer Parameters */ +#define AL_EQUALIZER_LOW_GAIN 0x0001 +#define AL_EQUALIZER_LOW_CUTOFF 0x0002 +#define AL_EQUALIZER_MID1_GAIN 0x0003 +#define AL_EQUALIZER_MID1_CENTER 0x0004 +#define AL_EQUALIZER_MID1_WIDTH 0x0005 +#define AL_EQUALIZER_MID2_GAIN 0x0006 +#define AL_EQUALIZER_MID2_CENTER 0x0007 +#define AL_EQUALIZER_MID2_WIDTH 0x0008 +#define AL_EQUALIZER_HIGH_GAIN 0x0009 +#define AL_EQUALIZER_HIGH_CUTOFF 0x000A + +/* Effect type */ +#define AL_EFFECT_FIRST_PARAMETER 0x0000 +#define AL_EFFECT_LAST_PARAMETER 0x8000 +#define AL_EFFECT_TYPE 0x8001 + +/* Effect type definitions to be used with AL_EFFECT_TYPE. */ +#define AL_EFFECT_NULL 0x0000 /* Can also be used as an Effect Object ID */ +#define AL_EFFECT_REVERB 0x0001 +#define AL_EFFECT_CHORUS 0x0002 +#define AL_EFFECT_DISTORTION 0x0003 +#define AL_EFFECT_ECHO 0x0004 +#define AL_EFFECT_FLANGER 0x0005 +#define AL_EFFECT_FREQUENCY_SHIFTER 0x0006 +#define AL_EFFECT_VOCAL_MORPHER 0x0007 +#define AL_EFFECT_PITCH_SHIFTER 0x0008 +#define AL_EFFECT_RING_MODULATOR 0x0009 +#define AL_EFFECT_AUTOWAH 0x000A +#define AL_EFFECT_COMPRESSOR 0x000B +#define AL_EFFECT_EQUALIZER 0x000C + +/** + * Auxiliary Slot object definitions to be used with alAuxiliaryEffectSlot functions. + */ +#define AL_EFFECTSLOT_EFFECT 0x0001 +#define AL_EFFECTSLOT_GAIN 0x0002 +#define AL_EFFECTSLOT_AUXILIARY_SEND_AUTO 0x0003 + +/** + * Value to be used as an Auxiliary Slot ID to disable a source send.. + */ +#define AL_EFFECTSLOT_NULL 0x0000 + + + +/** + * Filter object definitions to be used with alFilter functions. + */ + +/* Lowpass parameters. */ +#define AL_LOWPASS_GAIN 0x0001 +#define AL_LOWPASS_GAINHF 0x0002 + +/* Highpass Parameters */ +#define AL_HIGHPASS_GAIN 0x0001 +#define AL_HIGHPASS_GAINLF 0x0002 + +/* Bandpass Parameters */ +#define AL_BANDPASS_GAIN 0x0001 +#define AL_BANDPASS_GAINLF 0x0002 +#define AL_BANDPASS_GAINHF 0x0003 + +/* Filter type */ +#define AL_FILTER_FIRST_PARAMETER 0x0000 +#define AL_FILTER_LAST_PARAMETER 0x8000 +#define AL_FILTER_TYPE 0x8001 + +/* Filter type definitions to be used with AL_FILTER_TYPE. */ +#define AL_FILTER_NULL 0x0000 /* Can also be used as a Filter Object ID */ +#define AL_FILTER_LOWPASS 0x0001 +#define AL_FILTER_HIGHPASS 0x0002 +#define AL_FILTER_BANDPASS 0x0003 + + +/** + * Effect object functions. + */ + +/* Create Effect objects. */ +typedef void (__cdecl *LPALGENEFFECTS)( ALsizei n, ALuint* effects ); + +/* Delete Effect objects. */ +typedef void (__cdecl *LPALDELETEEFFECTS)( ALsizei n, ALuint* effects ); + +/* Verify a handle is a valid Effect. */ +typedef ALboolean (__cdecl *LPALISEFFECT)( ALuint eid ); + +/* Set an integer parameter for an Effect object. */ +typedef void (__cdecl *LPALEFFECTI)( ALuint eid, ALenum param, ALint value); +typedef void (__cdecl *LPALEFFECTIV)( ALuint eid, ALenum param, ALint* values ); + +/* Set a floating point parameter for an Effect object. */ +typedef void (__cdecl *LPALEFFECTF)( ALuint eid, ALenum param, ALfloat value); +typedef void (__cdecl *LPALEFFECTFV)( ALuint eid, ALenum param, ALfloat* values ); + +/* Get an integer parameter for an Effect object. */ +typedef void (__cdecl *LPALGETEFFECTI)( ALuint eid, ALenum pname, ALint* value ); +typedef void (__cdecl *LPALGETEFFECTIV)( ALuint eid, ALenum pname, ALint* values ); + +/* Get a floating point parameter for an Effect object. */ +typedef void (__cdecl *LPALGETEFFECTF)( ALuint eid, ALenum pname, ALfloat* value ); +typedef void (__cdecl *LPALGETEFFECTFV)( ALuint eid, ALenum pname, ALfloat* values ); + + +/** + * Filter object functions + */ + +/* Create Filter objects. */ +typedef void (__cdecl *LPALGENFILTERS)( ALsizei n, ALuint* filters ); + +/* Delete Filter objects. */ +typedef void (__cdecl *LPALDELETEFILTERS)( ALsizei n, ALuint* filters ); + +/* Verify a handle is a valid Filter. */ +typedef ALboolean (__cdecl *LPALISFILTER)( ALuint fid ); + +/* Set an integer parameter for a Filter object. */ +typedef void (__cdecl *LPALFILTERI)( ALuint fid, ALenum param, ALint value ); +typedef void (__cdecl *LPALFILTERIV)( ALuint fid, ALenum param, ALint* values ); + +/* Set a floating point parameter for an Filter object. */ +typedef void (__cdecl *LPALFILTERF)( ALuint fid, ALenum param, ALfloat value); +typedef void (__cdecl *LPALFILTERFV)( ALuint fid, ALenum param, ALfloat* values ); + +/* Get an integer parameter for a Filter object. */ +typedef void (__cdecl *LPALGETFILTERI)( ALuint fid, ALenum pname, ALint* value ); +typedef void (__cdecl *LPALGETFILTERIV)( ALuint fid, ALenum pname, ALint* values ); + +/* Get a floating point parameter for a Filter object. */ +typedef void (__cdecl *LPALGETFILTERF)( ALuint fid, ALenum pname, ALfloat* value ); +typedef void (__cdecl *LPALGETFILTERFV)( ALuint fid, ALenum pname, ALfloat* values ); + + +/** + * Auxiliary Slot object functions + */ + +/* Create Auxiliary Slot objects. */ +typedef void (__cdecl *LPALGENAUXILIARYEFFECTSLOTS)( ALsizei n, ALuint* slots ); + +/* Delete Auxiliary Slot objects. */ +typedef void (__cdecl *LPALDELETEAUXILIARYEFFECTSLOTS)( ALsizei n, ALuint* slots ); + +/* Verify a handle is a valid Auxiliary Slot. */ +typedef ALboolean (__cdecl *LPALISAUXILIARYEFFECTSLOT)( ALuint slot ); + +/* Set an integer parameter for a Auxiliary Slot object. */ +typedef void (__cdecl *LPALAUXILIARYEFFECTSLOTI)( ALuint asid, ALenum param, ALint value ); +typedef void (__cdecl *LPALAUXILIARYEFFECTSLOTIV)( ALuint asid, ALenum param, ALint* values ); + +/* Set a floating point parameter for an Auxiliary Slot object. */ +typedef void (__cdecl *LPALAUXILIARYEFFECTSLOTF)( ALuint asid, ALenum param, ALfloat value ); +typedef void (__cdecl *LPALAUXILIARYEFFECTSLOTFV)( ALuint asid, ALenum param, ALfloat* values ); + +/* Get an integer parameter for a Auxiliary Slot object. */ +typedef void (__cdecl *LPALGETAUXILIARYEFFECTSLOTI)( ALuint asid, ALenum pname, ALint* value ); +typedef void (__cdecl *LPALGETAUXILIARYEFFECTSLOTIV)( ALuint asid, ALenum pname, ALint* values ); + +/* Get a floating point parameter for a Auxiliary Slot object. */ +typedef void (__cdecl *LPALGETAUXILIARYEFFECTSLOTF)( ALuint asid, ALenum pname, ALfloat* value ); +typedef void (__cdecl *LPALGETAUXILIARYEFFECTSLOTFV)( ALuint asid, ALenum pname, ALfloat* values ); + + + + +/********************************************************** + * Filter ranges and defaults. + */ + +/** + * Lowpass filter + */ + +#define LOWPASS_MIN_GAIN 0.0f +#define LOWPASS_MAX_GAIN 1.0f +#define LOWPASS_DEFAULT_GAIN 1.0f + +#define LOWPASS_MIN_GAINHF 0.0f +#define LOWPASS_MAX_GAINHF 1.0f +#define LOWPASS_DEFAULT_GAINHF 1.0f + +/** + * Highpass filter + */ + +#define HIGHPASS_MIN_GAIN 0.0f +#define HIGHPASS_MAX_GAIN 1.0f +#define HIGHPASS_DEFAULT_GAIN 1.0f + +#define HIGHPASS_MIN_GAINLF 0.0f +#define HIGHPASS_MAX_GAINLF 1.0f +#define HIGHPASS_DEFAULT_GAINLF 1.0f + +/** + * Bandpass filter + */ + +#define BANDPASS_MIN_GAIN 0.0f +#define BANDPASS_MAX_GAIN 1.0f +#define BANDPASS_DEFAULT_GAIN 1.0f + +#define BANDPASS_MIN_GAINHF 0.0f +#define BANDPASS_MAX_GAINHF 1.0f +#define BANDPASS_DEFAULT_GAINHF 1.0f + +#define BANDPASS_MIN_GAINLF 0.0f +#define BANDPASS_MAX_GAINLF 1.0f +#define BANDPASS_DEFAULT_GAINLF 1.0f + + + + + /********************************************************** + * Effect parameter structures, value definitions, ranges and defaults. + */ + +/** + * AL reverb effect parameter ranges and defaults + */ +#define AL_REVERB_MIN_DENSITY 0.0f +#define AL_REVERB_MAX_DENSITY 1.0f +#define AL_REVERB_DEFAULT_DENSITY 1.0f + +#define AL_REVERB_MIN_DIFFUSION 0.0f +#define AL_REVERB_MAX_DIFFUSION 1.0f +#define AL_REVERB_DEFAULT_DIFFUSION 1.0f + +#define AL_REVERB_MIN_GAIN 0.0f +#define AL_REVERB_MAX_GAIN 1.0f +#define AL_REVERB_DEFAULT_GAIN 0.32f + +#define AL_REVERB_MIN_GAINHF 0.0f +#define AL_REVERB_MAX_GAINHF 1.0f +#define AL_REVERB_DEFAULT_GAINHF 0.89f + +#define AL_REVERB_MIN_DECAY_TIME 0.1f +#define AL_REVERB_MAX_DECAY_TIME 20.0f +#define AL_REVERB_DEFAULT_DECAY_TIME 1.49f + +#define AL_REVERB_MIN_DECAY_HFRATIO 0.1f +#define AL_REVERB_MAX_DECAY_HFRATIO 2.0f +#define AL_REVERB_DEFAULT_DECAY_HFRATIO 0.83f + +#define AL_REVERB_MIN_REFLECTIONS_GAIN 0.0f +#define AL_REVERB_MAX_REFLECTIONS_GAIN 3.16f +#define AL_REVERB_DEFAULT_REFLECTIONS_GAIN 0.05f + +#define AL_REVERB_MIN_REFLECTIONS_DELAY 0.0f +#define AL_REVERB_MAX_REFLECTIONS_DELAY 0.3f +#define AL_REVERB_DEFAULT_REFLECTIONS_DELAY 0.007f + +#define AL_REVERB_MIN_LATE_REVERB_GAIN 0.0f +#define AL_REVERB_MAX_LATE_REVERB_GAIN 10.0f +#define AL_REVERB_DEFAULT_LATE_REVERB_GAIN 1.26f + +#define AL_REVERB_MIN_LATE_REVERB_DELAY 0.0f +#define AL_REVERB_MAX_LATE_REVERB_DELAY 0.1f +#define AL_REVERB_DEFAULT_LATE_REVERB_DELAY 0.011f + +#define AL_REVERB_MIN_AIR_ABSORPTION_GAINHF 0.892f +#define AL_REVERB_MAX_AIR_ABSORPTION_GAINHF 1.0f +#define AL_REVERB_DEFAULT_AIR_ABSORPTION_GAINHF 0.994f + +#define AL_REVERB_MIN_ROOM_ROLLOFF_FACTOR 0.0f +#define AL_REVERB_MAX_ROOM_ROLLOFF_FACTOR 10.0f +#define AL_REVERB_DEFAULT_ROOM_ROLLOFF_FACTOR 0.0f + +#define AL_REVERB_MIN_DECAY_HFLIMIT AL_FALSE +#define AL_REVERB_MAX_DECAY_HFLIMIT AL_TRUE +#define AL_REVERB_DEFAULT_DECAY_HFLIMIT AL_TRUE + +/** + * AL chorus effect parameter ranges and defaults + */ +#define AL_CHORUS_MIN_WAVEFORM 0 +#define AL_CHORUS_MAX_WAVEFORM 1 +#define AL_CHORUS_DEFAULT_WAVEFORM 1 + +#define AL_CHORUS_WAVEFORM_SINUSOID 0 +#define AL_CHORUS_WAVEFORM_TRIANGLE 1 + +#define AL_CHORUS_MIN_PHASE (-180) +#define AL_CHORUS_MAX_PHASE 180 +#define AL_CHORUS_DEFAULT_PHASE 90 + +#define AL_CHORUS_MIN_RATE 0.0f +#define AL_CHORUS_MAX_RATE 10.0f +#define AL_CHORUS_DEFAULT_RATE 1.1f + +#define AL_CHORUS_MIN_DEPTH 0.0f +#define AL_CHORUS_MAX_DEPTH 1.0f +#define AL_CHORUS_DEFAULT_DEPTH 0.1f + +#define AL_CHORUS_MIN_FEEDBACK (-1.0f) +#define AL_CHORUS_MAX_FEEDBACK 1.0f +#define AL_CHORUS_DEFAULT_FEEDBACK 0.25f + +#define AL_CHORUS_MIN_DELAY 0.0f +#define AL_CHORUS_MAX_DELAY 0.016f +#define AL_CHORUS_DEFAULT_DELAY 0.016f + +/** + * AL distortion effect parameter ranges and defaults + */ +#define AL_DISTORTION_MIN_EDGE 0.0f +#define AL_DISTORTION_MAX_EDGE 1.0f +#define AL_DISTORTION_DEFAULT_EDGE 0.2f + +#define AL_DISTORTION_MIN_GAIN 0.01f +#define AL_DISTORTION_MAX_GAIN 1.0f +#define AL_DISTORTION_DEFAULT_GAIN 0.05f + +#define AL_DISTORTION_MIN_LOWPASS_CUTOFF 80.0f +#define AL_DISTORTION_MAX_LOWPASS_CUTOFF 24000.0f +#define AL_DISTORTION_DEFAULT_LOWPASS_CUTOFF 8000.0f + +#define AL_DISTORTION_MIN_EQCENTER 80.0f +#define AL_DISTORTION_MAX_EQCENTER 24000.0f +#define AL_DISTORTION_DEFAULT_EQCENTER 3600.0f + +#define AL_DISTORTION_MIN_EQBANDWIDTH 80.0f +#define AL_DISTORTION_MAX_EQBANDWIDTH 24000.0f +#define AL_DISTORTION_DEFAULT_EQBANDWIDTH 3600.0f + +/** + * AL echo effect parameter ranges and defaults + */ +#define AL_ECHO_MIN_DELAY 0.0f +#define AL_ECHO_MAX_DELAY 0.207f +#define AL_ECHO_DEFAULT_DELAY 0.1f + +#define AL_ECHO_MIN_LRDELAY 0.0f +#define AL_ECHO_MAX_LRDELAY 0.404f +#define AL_ECHO_DEFAULT_LRDELAY 0.1f + +#define AL_ECHO_MIN_DAMPING 0.0f +#define AL_ECHO_MAX_DAMPING 0.99f +#define AL_ECHO_DEFAULT_DAMPING 0.5f + +#define AL_ECHO_MIN_FEEDBACK 0.0f +#define AL_ECHO_MAX_FEEDBACK 1.0f +#define AL_ECHO_DEFAULT_FEEDBACK 0.5f + +#define AL_ECHO_MIN_SPREAD (-1.0f) +#define AL_ECHO_MAX_SPREAD 1.0f +#define AL_ECHO_DEFAULT_SPREAD (-1.0f) + +/** + * AL flanger effect parameter ranges and defaults + */ +#define AL_FLANGER_MIN_WAVEFORM 0 +#define AL_FLANGER_MAX_WAVEFORM 1 +#define AL_FLANGER_DEFAULT_WAVEFORM 1 + +#define AL_FLANGER_WAVEFORM_SINUSOID 0 +#define AL_FLANGER_WAVEFORM_TRIANGLE 1 + +#define AL_FLANGER_MIN_PHASE (-180) +#define AL_FLANGER_MAX_PHASE 180 +#define AL_FLANGER_DEFAULT_PHASE 0 + +#define AL_FLANGER_MIN_RATE 0.0f +#define AL_FLANGER_MAX_RATE 10.0f +#define AL_FLANGER_DEFAULT_RATE 0.27f + +#define AL_FLANGER_MIN_DEPTH 0.0f +#define AL_FLANGER_MAX_DEPTH 1.0f +#define AL_FLANGER_DEFAULT_DEPTH 1.0f + +#define AL_FLANGER_MIN_FEEDBACK (-1.0f) +#define AL_FLANGER_MAX_FEEDBACK 1.0f +#define AL_FLANGER_DEFAULT_FEEDBACK (-0.5f) + +#define AL_FLANGER_MIN_DELAY 0.0f +#define AL_FLANGER_MAX_DELAY 0.004f +#define AL_FLANGER_DEFAULT_DELAY 0.002f + +/** + * AL frequency shifter effect parameter ranges and defaults + */ +#define AL_FREQUENCY_SHIFTER_MIN_FREQUENCY 0.0f +#define AL_FREQUENCY_SHIFTER_MAX_FREQUENCY 24000.0f +#define AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY 0.0f + +#define AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION 0 +#define AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION 2 +#define AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION 0 + +#define AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION 0 +#define AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION 2 +#define AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION 0 + +#define AL_FREQUENCY_SHIFTER_DIRECTION_DOWN 0 +#define AL_FREQUENCY_SHIFTER_DIRECTION_UP 1 +#define AL_FREQUENCY_SHIFTER_DIRECTION_OFF 2 + +/** + * AL vocal morpher effect parameter ranges and defaults + */ +#define AL_VOCAL_MORPHER_MIN_PHONEMEA 0 +#define AL_VOCAL_MORPHER_MAX_PHONEMEA 29 +#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEA 0 + +#define AL_VOCAL_MORPHER_MIN_PHONEMEA_COARSE_TUNING (-24) +#define AL_VOCAL_MORPHER_MAX_PHONEMEA_COARSE_TUNING 24 +#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEA_COARSE_TUNING 0 + +#define AL_VOCAL_MORPHER_MIN_PHONEMEB 0 +#define AL_VOCAL_MORPHER_MAX_PHONEMEB 29 +#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEB 10 + +#define AL_VOCAL_MORPHER_PHONEME_A 0 +#define AL_VOCAL_MORPHER_PHONEME_E 1 +#define AL_VOCAL_MORPHER_PHONEME_I 2 +#define AL_VOCAL_MORPHER_PHONEME_O 3 +#define AL_VOCAL_MORPHER_PHONEME_U 4 +#define AL_VOCAL_MORPHER_PHONEME_AA 5 +#define AL_VOCAL_MORPHER_PHONEME_AE 6 +#define AL_VOCAL_MORPHER_PHONEME_AH 7 +#define AL_VOCAL_MORPHER_PHONEME_AO 8 +#define AL_VOCAL_MORPHER_PHONEME_EH 9 +#define AL_VOCAL_MORPHER_PHONEME_ER 10 +#define AL_VOCAL_MORPHER_PHONEME_IH 11 +#define AL_VOCAL_MORPHER_PHONEME_IY 12 +#define AL_VOCAL_MORPHER_PHONEME_UH 13 +#define AL_VOCAL_MORPHER_PHONEME_UW 14 +#define AL_VOCAL_MORPHER_PHONEME_B 15 +#define AL_VOCAL_MORPHER_PHONEME_D 16 +#define AL_VOCAL_MORPHER_PHONEME_F 17 +#define AL_VOCAL_MORPHER_PHONEME_G 18 +#define AL_VOCAL_MORPHER_PHONEME_J 19 +#define AL_VOCAL_MORPHER_PHONEME_K 20 +#define AL_VOCAL_MORPHER_PHONEME_L 21 +#define AL_VOCAL_MORPHER_PHONEME_M 22 +#define AL_VOCAL_MORPHER_PHONEME_N 23 +#define AL_VOCAL_MORPHER_PHONEME_P 24 +#define AL_VOCAL_MORPHER_PHONEME_R 25 +#define AL_VOCAL_MORPHER_PHONEME_S 26 +#define AL_VOCAL_MORPHER_PHONEME_T 27 +#define AL_VOCAL_MORPHER_PHONEME_V 28 +#define AL_VOCAL_MORPHER_PHONEME_Z 29 + +#define AL_VOCAL_MORPHER_MIN_PHONEMEB_COARSE_TUNING (-24) +#define AL_VOCAL_MORPHER_MAX_PHONEMEB_COARSE_TUNING 24 +#define AL_VOCAL_MORPHER_DEFAULT_PHONEMEB_COARSE_TUNING 0 + +#define AL_VOCAL_MORPHER_MIN_WAVEFORM 0 +#define AL_VOCAL_MORPHER_MAX_WAVEFORM 2 +#define AL_VOCAL_MORPHER_DEFAULT_WAVEFORM 0 + +#define AL_VOCAL_MORPHER_WAVEFORM_SINUSOID 0 +#define AL_VOCAL_MORPHER_WAVEFORM_TRIANGLE 1 +#define AL_VOCAL_MORPHER_WAVEFORM_SAWTOOTH 2 + +#define AL_VOCAL_MORPHER_MIN_RATE 0.0f +#define AL_VOCAL_MORPHER_MAX_RATE 10.0f +#define AL_VOCAL_MORPHER_DEFAULT_RATE 1.41f + +/** + * AL pitch shifter effect parameter ranges and defaults + */ +#define AL_PITCH_SHIFTER_MIN_COARSE_TUNE (-12) +#define AL_PITCH_SHIFTER_MAX_COARSE_TUNE 12 +#define AL_PITCH_SHIFTER_DEFAULT_COARSE_TUNE 12 + +#define AL_PITCH_SHIFTER_MIN_FINE_TUNE (-50) +#define AL_PITCH_SHIFTER_MAX_FINE_TUNE 50 +#define AL_PITCH_SHIFTER_DEFAULT_FINE_TUNE 0 + +/** + * AL ring modulator effect parameter ranges and defaults + */ +#define AL_RING_MODULATOR_MIN_FREQUENCY 0.0f +#define AL_RING_MODULATOR_MAX_FREQUENCY 8000.0f +#define AL_RING_MODULATOR_DEFAULT_FREQUENCY 440.0f + +#define AL_RING_MODULATOR_MIN_HIGHPASS_CUTOFF 0.0f +#define AL_RING_MODULATOR_MAX_HIGHPASS_CUTOFF 24000.0f +#define AL_RING_MODULATOR_DEFAULT_HIGHPASS_CUTOFF 800.0f + +#define AL_RING_MODULATOR_MIN_WAVEFORM 0 +#define AL_RING_MODULATOR_MAX_WAVEFORM 2 +#define AL_RING_MODULATOR_DEFAULT_WAVEFORM 0 + +#define AL_RING_MODULATOR_SINUSOID 0 +#define AL_RING_MODULATOR_SAWTOOTH 1 +#define AL_RING_MODULATOR_SQUARE 2 + +/** + * AL autowah effect parameter ranges and defaults + */ +#define AL_AUTOWAH_MIN_ATTACK_TIME 0.0001f +#define AL_AUTOWAH_MAX_ATTACK_TIME 1.0f +#define AL_AUTOWAH_DEFAULT_ATTACK_TIME 0.06f + +#define AL_AUTOWAH_MIN_RELEASE_TIME 0.0001f +#define AL_AUTOWAH_MAX_RELEASE_TIME 1.0f +#define AL_AUTOWAH_DEFAULT_RELEASE_TIME 0.06f + +#define AL_AUTOWAH_MIN_RESONANCE 2.0f +#define AL_AUTOWAH_MAX_RESONANCE 1000.0f +#define AL_AUTOWAH_DEFAULT_RESONANCE 1000.0f + +#define AL_AUTOWAH_MIN_PEAK_GAIN 0.00003f +#define AL_AUTOWAH_MAX_PEAK_GAIN 31621.0f +#define AL_AUTOWAH_DEFAULT_PEAK_GAIN 11.22f + +/** + * AL compressor effect parameter ranges and defaults + */ +#define AL_COMPRESSOR_MIN_ONOFF 0 +#define AL_COMPRESSOR_MAX_ONOFF 1 +#define AL_COMPRESSOR_DEFAULT_ONOFF 1 + +/** + * AL equalizer effect parameter ranges and defaults + */ +#define AL_EQUALIZER_MIN_LOW_GAIN 0.126f +#define AL_EQUALIZER_MAX_LOW_GAIN 7.943f +#define AL_EQUALIZER_DEFAULT_LOW_GAIN 1.0f + +#define AL_EQUALIZER_MIN_LOW_CUTOFF 50.0f +#define AL_EQUALIZER_MAX_LOW_CUTOFF 800.0f +#define AL_EQUALIZER_DEFAULT_LOW_CUTOFF 200.0f + +#define AL_EQUALIZER_MIN_MID1_GAIN 0.126f +#define AL_EQUALIZER_MAX_MID1_GAIN 7.943f +#define AL_EQUALIZER_DEFAULT_MID1_GAIN 1.0f + +#define AL_EQUALIZER_MIN_MID1_CENTER 200.0f +#define AL_EQUALIZER_MAX_MID1_CENTER 3000.0f +#define AL_EQUALIZER_DEFAULT_MID1_CENTER 500.0f + +#define AL_EQUALIZER_MIN_MID1_WIDTH 0.01f +#define AL_EQUALIZER_MAX_MID1_WIDTH 1.0f +#define AL_EQUALIZER_DEFAULT_MID1_WIDTH 1.0f + +#define AL_EQUALIZER_MIN_MID2_GAIN 0.126f +#define AL_EQUALIZER_MAX_MID2_GAIN 7.943f +#define AL_EQUALIZER_DEFAULT_MID2_GAIN 1.0f + +#define AL_EQUALIZER_MIN_MID2_CENTER 1000.0f +#define AL_EQUALIZER_MAX_MID2_CENTER 8000.0f +#define AL_EQUALIZER_DEFAULT_MID2_CENTER 3000.0f + +#define AL_EQUALIZER_MIN_MID2_WIDTH 0.01f +#define AL_EQUALIZER_MAX_MID2_WIDTH 1.0f +#define AL_EQUALIZER_DEFAULT_MID2_WIDTH 1.0f + +#define AL_EQUALIZER_MIN_HIGH_GAIN 0.126f +#define AL_EQUALIZER_MAX_HIGH_GAIN 7.943f +#define AL_EQUALIZER_DEFAULT_HIGH_GAIN 1.0f + +#define AL_EQUALIZER_MIN_HIGH_CUTOFF 4000.0f +#define AL_EQUALIZER_MAX_HIGH_CUTOFF 16000.0f +#define AL_EQUALIZER_DEFAULT_HIGH_CUTOFF 6000.0f + + + + +/********************************************************** + * Source parameter value definitions, ranges and defaults. + */ +#define AL_MIN_AIR_ABSORPTION_FACTOR 0.0f +#define AL_MAX_AIR_ABSORPTION_FACTOR 10.0f +#define AL_DEFAULT_AIR_ABSORPTION_FACTOR 0.0f + +#define AL_MIN_ROOM_ROLLOFF_FACTOR 0.0f +#define AL_MAX_ROOM_ROLLOFF_FACTOR 10.0f +#define AL_DEFAULT_ROOM_ROLLOFF_FACTOR 0.0f + +#define AL_MIN_CONE_OUTER_GAINHF 0.0f +#define AL_MAX_CONE_OUTER_GAINHF 1.0f +#define AL_DEFAULT_CONE_OUTER_GAINHF 1.0f + +#define AL_MIN_DIRECT_FILTER_GAINHF_AUTO AL_FALSE +#define AL_MAX_DIRECT_FILTER_GAINHF_AUTO AL_TRUE +#define AL_DEFAULT_DIRECT_FILTER_GAINHF_AUTO AL_TRUE + +#define AL_MIN_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_FALSE +#define AL_MAX_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_TRUE +#define AL_DEFAULT_AUXILIARY_SEND_FILTER_GAIN_AUTO AL_TRUE + +#define AL_MIN_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_FALSE +#define AL_MAX_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_TRUE +#define AL_DEFAULT_AUXILIARY_SEND_FILTER_GAINHF_AUTO AL_TRUE + + + + +/********************************************************** + * Listener parameter value definitions, ranges and defaults. + */ +#define AL_MIN_METERS_PER_UNIT FLT_MIN +#define AL_MAX_METERS_PER_UNIT FLT_MAX +#define AL_DEFAULT_METERS_PER_UNIT 1.0f + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __efx_h_ */ diff --git a/Engine/lib/openal/win32/al/xram.h b/Engine/lib/openal/win32/al/xram.h new file mode 100644 index 000000000..5d8366242 --- /dev/null +++ b/Engine/lib/openal/win32/al/xram.h @@ -0,0 +1,94 @@ +#include + +// X-RAM Function pointer definitions +typedef ALboolean (__cdecl *EAXSetBufferMode)(ALsizei n, ALuint *buffers, ALint value); +typedef ALenum (__cdecl *EAXGetBufferMode)(ALuint buffer, ALint *value); + +////////////////////////////////////////////////////////////////////////////// +// Query for X-RAM extension +// +// if (alIsExtensionPresent("EAX-RAM") == AL_TRUE) +// X-RAM Extension found +// +////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////// +// X-RAM enum names +// +// "AL_EAX_RAM_SIZE" +// "AL_EAX_RAM_FREE" +// "AL_STORAGE_AUTOMATIC" +// "AL_STORAGE_HARDWARE" +// "AL_STORAGE_ACCESSIBLE" +// +// Query enum values using alGetEnumValue, for example +// +// long lRamSizeEnum = alGetEnumValue("AL_EAX_RAM_SIZE") +// +////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////// +// Query total amount of X-RAM +// +// long lTotalSize = alGetInteger(alGetEnumValue("AL_EAX_RAM_SIZE") +// +////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////// +// Query free X-RAM available +// +// long lFreeSize = alGetInteger(alGetEnumValue("AL_EAX_RAM_FREE") +// +////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////// +// Query X-RAM Function pointers +// +// Use typedefs defined above to get the X-RAM function pointers using +// alGetProcAddress +// +// EAXSetBufferMode eaxSetBufferMode; +// EAXGetBufferMode eaxGetBufferMode; +// +// eaxSetBufferMode = (EAXSetBufferMode)alGetProcAddress("EAXSetBufferMode"); +// eaxGetBufferMode = (EAXGetBufferMode)alGetProcAddress("EAXGetBufferMode"); +// +////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////// +// Force an Open AL Buffer into X-RAM (good for non-streaming buffers) +// +// ALuint uiBuffer; +// alGenBuffers(1, &uiBuffer); +// eaxSetBufferMode(1, &uiBuffer, alGetEnumValue("AL_STORAGE_HARDWARE")); +// alBufferData(...); +// +////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////// +// Force an Open AL Buffer into 'accessible' (currently host) RAM (good for streaming buffers) +// +// ALuint uiBuffer; +// alGenBuffers(1, &uiBuffer); +// eaxSetBufferMode(1, &uiBuffer, alGetEnumValue("AL_STORAGE_ACCESSIBLE")); +// alBufferData(...); +// +////////////////////////////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////////////////////////////// +// Put an Open AL Buffer into X-RAM if memory is available, otherwise use +// host RAM. This is the default mode. +// +// ALuint uiBuffer; +// alGenBuffers(1, &uiBuffer); +// eaxSetBufferMode(1, &uiBuffer, alGetEnumValue("AL_STORAGE_AUTOMATIC")); +// alBufferData(...); +// +////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/Engine/lib/pcre/changes.txt b/Engine/lib/pcre/changes.txt new file mode 100644 index 000000000..fad731894 --- /dev/null +++ b/Engine/lib/pcre/changes.txt @@ -0,0 +1,5 @@ +Don't use bits/type_traits.h on Linux - Andrew Galante, GG 8/2/2009: pcre_stringpiece.h + +Both Mac and Linux support strtoq - Andrew Galante, GG 8/2/2009: config.h + +Neither Mac nor Linux support _strtoi64 - Andrew Galante, GG 8/2/2009: config.h diff --git a/Engine/lib/pcre/config.h b/Engine/lib/pcre/config.h new file mode 100644 index 000000000..b9c0c4080 --- /dev/null +++ b/Engine/lib/pcre/config.h @@ -0,0 +1,251 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + + +/* On Unix-like systems config.h.in is converted by "configure" into config.h. +Some other environments also support the use of "configure". PCRE is written in +Standard C, but there are a few non-standard things it can cope with, allowing +it to run on SunOS4 and other "close to standard" systems. + +If you are going to build PCRE "by hand" on a system without "configure" you +should copy the distributed config.h.generic to config.h, and then set up the +macro definitions the way you need them. You must then add -DHAVE_CONFIG_H to +all of your compile commands, so that config.h is included at the start of +every source. + +Alternatively, you can avoid editing by using -D on the compiler command line +to set the macro values. In this case, you do not have to set -DHAVE_CONFIG_H. + +PCRE uses memmove() if HAVE_MEMMOVE is set to 1; otherwise it uses bcopy() if +HAVE_BCOPY is set to 1. If your system has neither bcopy() nor memmove(), set +them both to 0; an emulation function will be used. */ + +/* By default, the \R escape sequence matches any Unicode line ending + character or sequence of characters. If BSR_ANYCRLF is defined, this is + changed so that backslash-R matches only CR, LF, or CRLF. The build- time + default can be overridden by the user of PCRE at runtime. On systems that + support it, "configure" can be used to override the default. */ +/* #undef BSR_ANYCRLF */ + +/* If you are compiling for a system that uses EBCDIC instead of ASCII + character codes, define this macro as 1. On systems that can use + "configure", this can be done via --enable-ebcdic. */ +/* #undef EBCDIC */ + +/* Define to 1 if you have the `bcopy' function. */ +/* #undef HAVE_BCOPY */ + +/* Define to 1 if you have the header file. */ +#define HAVE_BITS_TYPE_TRAITS_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_BZLIB_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_DIRENT_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_DLFCN_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if the system has the type `long long'. */ +#define HAVE_LONG_LONG 1 + +/* Define to 1 if you have the `memmove' function. */ +#define HAVE_MEMMOVE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_READLINE_HISTORY_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_READLINE_READLINE_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strerror' function. */ +#define HAVE_STRERROR 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the `strtoll' function. */ +#if defined(SN_TARGET_PS3) +#define HAVE_STRTOLL 1 +#endif + +/* Define to 1 if you have the `strtoq' function. */ +/* Both Mac and Linux support strtoq - Andrew Galante, GG 8/2/2009 */ +#ifdef __GNUC__ +#define HAVE_STRTOQ 1 +#endif + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_TYPE_TRAITS_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if the system has the type `unsigned long long'. */ +#define HAVE_UNSIGNED_LONG_LONG 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_WINDOWS_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_ZLIB_H */ + +/* Define to 1 if you have the `_strtoi64' function. */ +/* Neither Mac nor Linux support _strtoi64 - Andrew Galante, GG 8/2/2009 */ +#ifndef __GNUC__ +#define HAVE__STRTOI64 1 +#endif + +/* The value of LINK_SIZE determines the number of bytes used to store links + as offsets within the compiled regex. The default is 2, which allows for + compiled patterns up to 64K long. This covers the vast majority of cases. + However, PCRE can also be compiled to use 3 or 4 bytes instead. This allows + for longer patterns in extreme cases. On systems that support it, + "configure" can be used to override this default. */ +#define LINK_SIZE 2 + +/* The value of MATCH_LIMIT determines the default number of times the + internal match() function can be called during a single execution of + pcre_exec(). There is a runtime interface for setting a different limit. + The limit exists in order to catch runaway regular expressions that take + for ever to determine that they do not match. The default is set very large + so that it does not accidentally catch legitimate cases. On systems that + support it, "configure" can be used to override this default default. */ +#define MATCH_LIMIT 10000000 + +/* The above limit applies to all calls of match(), whether or not they + increase the recursion depth. In some environments it is desirable to limit + the depth of recursive calls of match() more strictly, in order to restrict + the maximum amount of stack (or heap, if NO_RECURSE is defined) that is + used. The value of MATCH_LIMIT_RECURSION applies only to recursive calls of + match(). To have any useful effect, it must be less than the value of + MATCH_LIMIT. The default is to use the same value as MATCH_LIMIT. There is + a runtime method for setting a different limit. On systems that support it, + "configure" can be used to override the default. */ +#define MATCH_LIMIT_RECURSION MATCH_LIMIT + +/* This limit is parameterized just in case anybody ever wants to change it. + Care must be taken if it is increased, because it guards against integer + overflow caused by enormously large patterns. */ +#define MAX_NAME_COUNT 10000 + +/* This limit is parameterized just in case anybody ever wants to change it. + Care must be taken if it is increased, because it guards against integer + overflow caused by enormously large patterns. */ +#define MAX_NAME_SIZE 32 + +/* The value of NEWLINE determines the newline character sequence. On systems + that support it, "configure" can be used to override the default, which is + 10. The possible values are 10 (LF), 13 (CR), 3338 (CRLF), -1 (ANY), or -2 + (ANYCRLF). */ +#define NEWLINE 10 + +/* PCRE uses recursive function calls to handle backtracking while matching. + This can sometimes be a problem on systems that have stacks of limited + size. Define NO_RECURSE to get a version that doesn't use recursion in the + match() function; instead it creates its own stack by steam using + pcre_recurse_malloc() to obtain memory from the heap. For more detail, see + the comments and other stuff just above the match() function. On systems + that support it, "configure" can be used to set this in the Makefile (use + --disable-stack-for-recursion). */ +/* #undef NO_RECURSE */ + +/* Name of package */ +#define PACKAGE "pcre" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "PCRE" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "PCRE 7.6" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "pcre" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "7.6" + + +/* If you are compiling for a system other than a Unix-like system or + Win32, and it needs some magic to be inserted before the definition + of a function that is exported by the library, define this macro to + contain the relevant magic. If you do not define this macro, it + defaults to "extern" for a C compiler and "extern C" for a C++ + compiler on non-Win32 systems. This macro apears at the start of + every exported function that is part of the external API. It does + not appear on functions that are "external" in the C sense, but + which are internal to the library. */ +/* #undef PCRE_EXP_DEFN */ + +/* Define if linking statically (TODO: make nice with Libtool) */ +#define PCRE_STATIC 1 + +/* When calling PCRE via the POSIX interface, additional working storage is + required for holding the pointers to capturing substrings because PCRE + requires three integers per substring, whereas the POSIX interface provides + only two. If the number of expected substrings is small, the wrapper + function uses space on the stack, because this is faster than using + malloc() for each call. The threshold above which the stack is no longer + used is defined by POSIX_MALLOC_THRESHOLD. On systems that support it, + "configure" can be used to override this default. */ +#define POSIX_MALLOC_THRESHOLD 10 + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to allow pcregrep to be linked with libbz2, so that it is able to + handle .bz2 files. */ +/* #undef SUPPORT_LIBBZ2 */ + +/* Define to allow pcretest to be linked with libreadline. */ +/* #undef SUPPORT_LIBREADLINE */ + +/* Define to allow pcregrep to be linked with libz, so that it is able to + handle .gz files. */ +/* #undef SUPPORT_LIBZ */ + +/* Define to enable support for Unicode properties */ +/* #undef SUPPORT_UCP */ + +/* Define to enable support for the UTF-8 Unicode encoding. */ +/* #undef SUPPORT_UTF8 */ + +/* Version number of package */ +#define VERSION "7.6" + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ diff --git a/Engine/lib/pcre/lib/mac/libpcre.a b/Engine/lib/pcre/lib/mac/libpcre.a new file mode 100644 index 000000000..87348d79f Binary files /dev/null and b/Engine/lib/pcre/lib/mac/libpcre.a differ diff --git a/Engine/lib/pcre/lib/mac/libpcrecpp.a b/Engine/lib/pcre/lib/mac/libpcrecpp.a new file mode 100644 index 000000000..fa5d9564c Binary files /dev/null and b/Engine/lib/pcre/lib/mac/libpcrecpp.a differ diff --git a/Engine/lib/pcre/lib/mingw/libpcre.a b/Engine/lib/pcre/lib/mingw/libpcre.a new file mode 100644 index 000000000..9cb325b5b Binary files /dev/null and b/Engine/lib/pcre/lib/mingw/libpcre.a differ diff --git a/Engine/lib/pcre/lib/mingw/libpcrecpp.a b/Engine/lib/pcre/lib/mingw/libpcrecpp.a new file mode 100644 index 000000000..2b972398d Binary files /dev/null and b/Engine/lib/pcre/lib/mingw/libpcrecpp.a differ diff --git a/Engine/lib/pcre/lib/vc8/pcre-d.lib b/Engine/lib/pcre/lib/vc8/pcre-d.lib new file mode 100644 index 000000000..4c4577992 Binary files /dev/null and b/Engine/lib/pcre/lib/vc8/pcre-d.lib differ diff --git a/Engine/lib/pcre/lib/vc8/pcre.lib b/Engine/lib/pcre/lib/vc8/pcre.lib new file mode 100644 index 000000000..b49b7b56c Binary files /dev/null and b/Engine/lib/pcre/lib/vc8/pcre.lib differ diff --git a/Engine/lib/pcre/lib/vc8/pcrecpp-d.lib b/Engine/lib/pcre/lib/vc8/pcrecpp-d.lib new file mode 100644 index 000000000..f3eb084d8 Binary files /dev/null and b/Engine/lib/pcre/lib/vc8/pcrecpp-d.lib differ diff --git a/Engine/lib/pcre/lib/vc8/pcrecpp.lib b/Engine/lib/pcre/lib/vc8/pcrecpp.lib new file mode 100644 index 000000000..e5977a927 Binary files /dev/null and b/Engine/lib/pcre/lib/vc8/pcrecpp.lib differ diff --git a/Engine/lib/pcre/lib/vc9/pcre-d.lib b/Engine/lib/pcre/lib/vc9/pcre-d.lib new file mode 100644 index 000000000..25aea6be2 Binary files /dev/null and b/Engine/lib/pcre/lib/vc9/pcre-d.lib differ diff --git a/Engine/lib/pcre/lib/vc9/pcre.lib b/Engine/lib/pcre/lib/vc9/pcre.lib new file mode 100644 index 000000000..271096891 Binary files /dev/null and b/Engine/lib/pcre/lib/vc9/pcre.lib differ diff --git a/Engine/lib/pcre/lib/vc9/pcrecpp-d.lib b/Engine/lib/pcre/lib/vc9/pcrecpp-d.lib new file mode 100644 index 000000000..483321a7f Binary files /dev/null and b/Engine/lib/pcre/lib/vc9/pcrecpp-d.lib differ diff --git a/Engine/lib/pcre/lib/vc9/pcrecpp.lib b/Engine/lib/pcre/lib/vc9/pcrecpp.lib new file mode 100644 index 000000000..7bd419789 Binary files /dev/null and b/Engine/lib/pcre/lib/vc9/pcrecpp.lib differ diff --git a/Engine/lib/pcre/pcre.h b/Engine/lib/pcre/pcre.h new file mode 100644 index 000000000..c85c32ea3 --- /dev/null +++ b/Engine/lib/pcre/pcre.h @@ -0,0 +1,303 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* This is the public header file for the PCRE library, to be #included by +applications that call the PCRE functions. + + Copyright (c) 1997-2008 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + +#ifndef _PCRE_H +#define _PCRE_H + +/* The current PCRE version information. */ + +#define PCRE_MAJOR 7 +#define PCRE_MINOR 6 +#define PCRE_PRERELEASE +#define PCRE_DATE 2008-01-28 + +/* When an application links to a PCRE DLL in Windows, the symbols that are +imported have to be identified as such. When building PCRE, the appropriate +export setting is defined in pcre_internal.h, which includes this file. So we +don't change existing definitions of PCRE_EXP_DECL and PCRECPP_EXP_DECL. */ + +#if defined(_WIN32) && !defined(PCRE_STATIC) +# ifndef PCRE_EXP_DECL +# define PCRE_EXP_DECL extern __declspec(dllimport) +# endif +# ifdef __cplusplus +# ifndef PCRECPP_EXP_DECL +# define PCRECPP_EXP_DECL extern __declspec(dllimport) +# endif +# ifndef PCRECPP_EXP_DEFN +# define PCRECPP_EXP_DEFN __declspec(dllimport) +# endif +# endif +#endif + +/* By default, we use the standard "extern" declarations. */ + +#ifndef PCRE_EXP_DECL +# ifdef __cplusplus +# define PCRE_EXP_DECL extern "C" +# else +# define PCRE_EXP_DECL extern +# endif +#endif + +#ifdef __cplusplus +# ifndef PCRECPP_EXP_DECL +# define PCRECPP_EXP_DECL extern +# endif +# ifndef PCRECPP_EXP_DEFN +# define PCRECPP_EXP_DEFN +# endif +#endif + +/* Have to include stdlib.h in order to ensure that size_t is defined; +it is needed here for malloc. */ + +#include + +/* Allow for C++ users */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Options */ + +#define PCRE_CASELESS 0x00000001 +#define PCRE_MULTILINE 0x00000002 +#define PCRE_DOTALL 0x00000004 +#define PCRE_EXTENDED 0x00000008 +#define PCRE_ANCHORED 0x00000010 +#define PCRE_DOLLAR_ENDONLY 0x00000020 +#define PCRE_EXTRA 0x00000040 +#define PCRE_NOTBOL 0x00000080 +#define PCRE_NOTEOL 0x00000100 +#define PCRE_UNGREEDY 0x00000200 +#define PCRE_NOTEMPTY 0x00000400 +#define PCRE_UTF8 0x00000800 +#define PCRE_NO_AUTO_CAPTURE 0x00001000 +#define PCRE_NO_UTF8_CHECK 0x00002000 +#define PCRE_AUTO_CALLOUT 0x00004000 +#define PCRE_PARTIAL 0x00008000 +#define PCRE_DFA_SHORTEST 0x00010000 +#define PCRE_DFA_RESTART 0x00020000 +#define PCRE_FIRSTLINE 0x00040000 +#define PCRE_DUPNAMES 0x00080000 +#define PCRE_NEWLINE_CR 0x00100000 +#define PCRE_NEWLINE_LF 0x00200000 +#define PCRE_NEWLINE_CRLF 0x00300000 +#define PCRE_NEWLINE_ANY 0x00400000 +#define PCRE_NEWLINE_ANYCRLF 0x00500000 +#define PCRE_BSR_ANYCRLF 0x00800000 +#define PCRE_BSR_UNICODE 0x01000000 + +/* Exec-time and get/set-time error codes */ + +#define PCRE_ERROR_NOMATCH (-1) +#define PCRE_ERROR_NULL (-2) +#define PCRE_ERROR_BADOPTION (-3) +#define PCRE_ERROR_BADMAGIC (-4) +#define PCRE_ERROR_UNKNOWN_OPCODE (-5) +#define PCRE_ERROR_UNKNOWN_NODE (-5) /* For backward compatibility */ +#define PCRE_ERROR_NOMEMORY (-6) +#define PCRE_ERROR_NOSUBSTRING (-7) +#define PCRE_ERROR_MATCHLIMIT (-8) +#define PCRE_ERROR_CALLOUT (-9) /* Never used by PCRE itself */ +#define PCRE_ERROR_BADUTF8 (-10) +#define PCRE_ERROR_BADUTF8_OFFSET (-11) +#define PCRE_ERROR_PARTIAL (-12) +#define PCRE_ERROR_BADPARTIAL (-13) +#define PCRE_ERROR_INTERNAL (-14) +#define PCRE_ERROR_BADCOUNT (-15) +#define PCRE_ERROR_DFA_UITEM (-16) +#define PCRE_ERROR_DFA_UCOND (-17) +#define PCRE_ERROR_DFA_UMLIMIT (-18) +#define PCRE_ERROR_DFA_WSSIZE (-19) +#define PCRE_ERROR_DFA_RECURSE (-20) +#define PCRE_ERROR_RECURSIONLIMIT (-21) +#define PCRE_ERROR_NULLWSLIMIT (-22) /* No longer actually used */ +#define PCRE_ERROR_BADNEWLINE (-23) + +/* Request types for pcre_fullinfo() */ + +#define PCRE_INFO_OPTIONS 0 +#define PCRE_INFO_SIZE 1 +#define PCRE_INFO_CAPTURECOUNT 2 +#define PCRE_INFO_BACKREFMAX 3 +#define PCRE_INFO_FIRSTBYTE 4 +#define PCRE_INFO_FIRSTCHAR 4 /* For backwards compatibility */ +#define PCRE_INFO_FIRSTTABLE 5 +#define PCRE_INFO_LASTLITERAL 6 +#define PCRE_INFO_NAMEENTRYSIZE 7 +#define PCRE_INFO_NAMECOUNT 8 +#define PCRE_INFO_NAMETABLE 9 +#define PCRE_INFO_STUDYSIZE 10 +#define PCRE_INFO_DEFAULT_TABLES 11 +#define PCRE_INFO_OKPARTIAL 12 +#define PCRE_INFO_JCHANGED 13 +#define PCRE_INFO_HASCRORLF 14 + +/* Request types for pcre_config(). Do not re-arrange, in order to remain +compatible. */ + +#define PCRE_CONFIG_UTF8 0 +#define PCRE_CONFIG_NEWLINE 1 +#define PCRE_CONFIG_LINK_SIZE 2 +#define PCRE_CONFIG_POSIX_MALLOC_THRESHOLD 3 +#define PCRE_CONFIG_MATCH_LIMIT 4 +#define PCRE_CONFIG_STACKRECURSE 5 +#define PCRE_CONFIG_UNICODE_PROPERTIES 6 +#define PCRE_CONFIG_MATCH_LIMIT_RECURSION 7 +#define PCRE_CONFIG_BSR 8 + +/* Bit flags for the pcre_extra structure. Do not re-arrange or redefine +these bits, just add new ones on the end, in order to remain compatible. */ + +#define PCRE_EXTRA_STUDY_DATA 0x0001 +#define PCRE_EXTRA_MATCH_LIMIT 0x0002 +#define PCRE_EXTRA_CALLOUT_DATA 0x0004 +#define PCRE_EXTRA_TABLES 0x0008 +#define PCRE_EXTRA_MATCH_LIMIT_RECURSION 0x0010 + +/* Types */ + +struct real_pcre; /* declaration; the definition is private */ +typedef struct real_pcre pcre; + +/* When PCRE is compiled as a C++ library, the subject pointer type can be +replaced with a custom type. For conventional use, the public interface is a +const char *. */ + +#ifndef PCRE_SPTR +#define PCRE_SPTR const char * +#endif + +/* The structure for passing additional data to pcre_exec(). This is defined in +such as way as to be extensible. Always add new fields at the end, in order to +remain compatible. */ + +typedef struct pcre_extra { + unsigned long int flags; /* Bits for which fields are set */ + void *study_data; /* Opaque data from pcre_study() */ + unsigned long int match_limit; /* Maximum number of calls to match() */ + void *callout_data; /* Data passed back in callouts */ + const unsigned char *tables; /* Pointer to character tables */ + unsigned long int match_limit_recursion; /* Max recursive calls to match() */ +} pcre_extra; + +/* The structure for passing out data via the pcre_callout_function. We use a +structure so that new fields can be added on the end in future versions, +without changing the API of the function, thereby allowing old clients to work +without modification. */ + +typedef struct pcre_callout_block { + int version; /* Identifies version of block */ + /* ------------------------ Version 0 ------------------------------- */ + int callout_number; /* Number compiled into pattern */ + int *offset_vector; /* The offset vector */ + PCRE_SPTR subject; /* The subject being matched */ + int subject_length; /* The length of the subject */ + int start_match; /* Offset to start of this match attempt */ + int current_position; /* Where we currently are in the subject */ + int capture_top; /* Max current capture */ + int capture_last; /* Most recently closed capture */ + void *callout_data; /* Data passed in with the call */ + /* ------------------- Added for Version 1 -------------------------- */ + int pattern_position; /* Offset to next item in the pattern */ + int next_item_length; /* Length of next item in the pattern */ + /* ------------------------------------------------------------------ */ +} pcre_callout_block; + +/* Indirection for store get and free functions. These can be set to +alternative malloc/free functions if required. Special ones are used in the +non-recursive case for "frames". There is also an optional callout function +that is triggered by the (?) regex item. For Virtual Pascal, these definitions +have to take another form. */ + +#ifndef VPCOMPAT +PCRE_EXP_DECL void *(*pcre_malloc)(size_t); +PCRE_EXP_DECL void (*pcre_free)(void *); +PCRE_EXP_DECL void *(*pcre_stack_malloc)(size_t); +PCRE_EXP_DECL void (*pcre_stack_free)(void *); +PCRE_EXP_DECL int (*pcre_callout)(pcre_callout_block *); +#else /* VPCOMPAT */ +PCRE_EXP_DECL void *pcre_malloc(size_t); +PCRE_EXP_DECL void pcre_free(void *); +PCRE_EXP_DECL void *pcre_stack_malloc(size_t); +PCRE_EXP_DECL void pcre_stack_free(void *); +PCRE_EXP_DECL int pcre_callout(pcre_callout_block *); +#endif /* VPCOMPAT */ + +/* Exported PCRE functions */ + +PCRE_EXP_DECL pcre *pcre_compile(const char *, int, const char **, int *, + const unsigned char *); +PCRE_EXP_DECL pcre *pcre_compile2(const char *, int, int *, const char **, + int *, const unsigned char *); +PCRE_EXP_DECL int pcre_config(int, void *); +PCRE_EXP_DECL int pcre_copy_named_substring(const pcre *, const char *, + int *, int, const char *, char *, int); +PCRE_EXP_DECL int pcre_copy_substring(const char *, int *, int, int, char *, + int); +PCRE_EXP_DECL int pcre_dfa_exec(const pcre *, const pcre_extra *, + const char *, int, int, int, int *, int , int *, int); +PCRE_EXP_DECL int pcre_exec(const pcre *, const pcre_extra *, PCRE_SPTR, + int, int, int, int *, int); +PCRE_EXP_DECL void pcre_free_substring(const char *); +PCRE_EXP_DECL void pcre_free_substring_list(const char **); +PCRE_EXP_DECL int pcre_fullinfo(const pcre *, const pcre_extra *, int, + void *); +PCRE_EXP_DECL int pcre_get_named_substring(const pcre *, const char *, + int *, int, const char *, const char **); +PCRE_EXP_DECL int pcre_get_stringnumber(const pcre *, const char *); +PCRE_EXP_DECL int pcre_get_stringtable_entries(const pcre *, const char *, + char **, char **); +PCRE_EXP_DECL int pcre_get_substring(const char *, int *, int, int, + const char **); +PCRE_EXP_DECL int pcre_get_substring_list(const char *, int *, int, + const char ***); +PCRE_EXP_DECL int pcre_info(const pcre *, int *, int *); +PCRE_EXP_DECL const unsigned char *pcre_maketables(void); +PCRE_EXP_DECL int pcre_refcount(pcre *, int); +PCRE_EXP_DECL pcre_extra *pcre_study(const pcre *, int, const char **); +PCRE_EXP_DECL const char *pcre_version(void); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* End of pcre.h */ diff --git a/Engine/lib/pcre/pcre_chartables.c b/Engine/lib/pcre/pcre_chartables.c new file mode 100644 index 000000000..36a7959ef --- /dev/null +++ b/Engine/lib/pcre/pcre_chartables.c @@ -0,0 +1,193 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* This file was automatically written by the dftables auxiliary +program. It contains character tables that are used when no external +tables are passed to PCRE by the application that calls it. The tables +are used only for characters whose code values are less than 256. + +The following #includes are present because without them gcc 4.x may remove +the array definition from the final binary if PCRE is built into a static +library and dead code stripping is activated. This leads to link errors. +Pulling in the header ensures that the array gets flagged as "someone +outside this compilation unit might reference this" and so it will always +be supplied to the linker. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + +const unsigned char _pcre_default_tables[] = { + +/* This table is a lower casing table. */ + + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 97, 98, 99,100,101,102,103, + 104,105,106,107,108,109,110,111, + 112,113,114,115,116,117,118,119, + 120,121,122, 91, 92, 93, 94, 95, + 96, 97, 98, 99,100,101,102,103, + 104,105,106,107,108,109,110,111, + 112,113,114,115,116,117,118,119, + 120,121,122,123,124,125,126,127, + 128,129,130,131,132,133,134,135, + 136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151, + 152,153,154,155,156,157,158,159, + 160,161,162,163,164,165,166,167, + 168,169,170,171,172,173,174,175, + 176,177,178,179,180,181,182,183, + 184,185,186,187,188,189,190,191, + 192,193,194,195,196,197,198,199, + 200,201,202,203,204,205,206,207, + 208,209,210,211,212,213,214,215, + 216,217,218,219,220,221,222,223, + 224,225,226,227,228,229,230,231, + 232,233,234,235,236,237,238,239, + 240,241,242,243,244,245,246,247, + 248,249,250,251,252,253,254,255, + +/* This table is a case flipping table. */ + + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 97, 98, 99,100,101,102,103, + 104,105,106,107,108,109,110,111, + 112,113,114,115,116,117,118,119, + 120,121,122, 91, 92, 93, 94, 95, + 96, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90,123,124,125,126,127, + 128,129,130,131,132,133,134,135, + 136,137,138,139,140,141,142,143, + 144,145,146,147,148,149,150,151, + 152,153,154,155,156,157,158,159, + 160,161,162,163,164,165,166,167, + 168,169,170,171,172,173,174,175, + 176,177,178,179,180,181,182,183, + 184,185,186,187,188,189,190,191, + 192,193,194,195,196,197,198,199, + 200,201,202,203,204,205,206,207, + 208,209,210,211,212,213,214,215, + 216,217,218,219,220,221,222,223, + 224,225,226,227,228,229,230,231, + 232,233,234,235,236,237,238,239, + 240,241,242,243,244,245,246,247, + 248,249,250,251,252,253,254,255, + +/* This table contains bit maps for various character classes. +Each map is 32 bytes long and the bits run from the least +significant end of each byte. The classes that have their own +maps are: space, xdigit, digit, upper, lower, word, graph +print, punct, and cntrl. Other classes are built from combinations. */ + + 0x00,0x3e,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, + 0x7e,0x00,0x00,0x00,0x7e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xfe,0xff,0xff,0x07,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0x07, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03, + 0xfe,0xff,0xff,0x87,0xfe,0xff,0xff,0x07, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0x00,0x00,0x00,0x00,0xfe,0xff,0x00,0xfc, + 0x01,0x00,0x00,0xf8,0x01,0x00,0x00,0x78, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + + 0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + +/* This table identifies various classes of character by individual bits: + 0x01 white space character + 0x02 letter + 0x04 decimal digit + 0x08 hexadecimal digit + 0x10 alphanumeric or '_' + 0x80 regular expression metacharacter or binary zero +*/ + + 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 */ + 0x00,0x01,0x01,0x00,0x01,0x01,0x00,0x00, /* 8- 15 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 16- 23 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */ + 0x01,0x00,0x00,0x00,0x80,0x00,0x00,0x00, /* - ' */ + 0x80,0x80,0x80,0x80,0x00,0x00,0x80,0x00, /* ( - / */ + 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, /* 0 - 7 */ + 0x1c,0x1c,0x00,0x00,0x00,0x00,0x00,0x80, /* 8 - ? */ + 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* @ - G */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* H - O */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* P - W */ + 0x12,0x12,0x12,0x80,0x80,0x00,0x80,0x10, /* X - _ */ + 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* ` - g */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* h - o */ + 0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* p - w */ + 0x12,0x12,0x12,0x80,0x80,0x00,0x00,0x00, /* x -127 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 128-135 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 136-143 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144-151 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 152-159 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160-167 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 168-175 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 176-183 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 192-199 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 200-207 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 208-215 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 216-223 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 224-231 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 232-239 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 240-247 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};/* 248-255 */ + +/* End of pcre_chartables.c */ diff --git a/Engine/lib/pcre/pcre_compile.c b/Engine/lib/pcre/pcre_compile.c new file mode 100644 index 000000000..799f47cc9 --- /dev/null +++ b/Engine/lib/pcre/pcre_compile.c @@ -0,0 +1,6221 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2008 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains the external function pcre_compile(), along with +supporting internal functions that are not used by other modules. */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define NLBLOCK cd /* Block containing newline information */ +#define PSSTART start_pattern /* Field containing processed string start */ +#define PSEND end_pattern /* Field containing processed string end */ + +#include "pcre_internal.h" + + +/* When DEBUG is defined, we need the pcre_printint() function, which is also +used by pcretest. DEBUG is not defined when building a production library. */ + +#ifdef DEBUG +#include "pcre_printint.src" +#endif + + +/* Macro for setting individual bits in class bitmaps. */ + +#define SETBIT(a,b) a[b/8] |= (1 << (b%8)) + +/* Maximum length value to check against when making sure that the integer that +holds the compiled pattern length does not overflow. We make it a bit less than +INT_MAX to allow for adding in group terminating bytes, so that we don't have +to check them every time. */ + +#define OFLOW_MAX (INT_MAX - 20) + + +/************************************************* +* Code parameters and static tables * +*************************************************/ + +/* This value specifies the size of stack workspace that is used during the +first pre-compile phase that determines how much memory is required. The regex +is partly compiled into this space, but the compiled parts are discarded as +soon as they can be, so that hopefully there will never be an overrun. The code +does, however, check for an overrun. The largest amount I've seen used is 218, +so this number is very generous. + +The same workspace is used during the second, actual compile phase for +remembering forward references to groups so that they can be filled in at the +end. Each entry in this list occupies LINK_SIZE bytes, so even when LINK_SIZE +is 4 there is plenty of room. */ + +#define COMPILE_WORK_SIZE (4096) + + +/* Table for handling escaped characters in the range '0'-'z'. Positive returns +are simple data values; negative values are for special things like \d and so +on. Zero means further processing is needed (for things like \x), or the escape +is invalid. */ + +#ifndef EBCDIC /* This is the "normal" table for ASCII systems */ +static const short int escapes[] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - 7 */ + 0, 0, ':', ';', '<', '=', '>', '?', /* 8 - ? */ + '@', -ESC_A, -ESC_B, -ESC_C, -ESC_D, -ESC_E, 0, -ESC_G, /* @ - G */ +-ESC_H, 0, 0, -ESC_K, 0, 0, 0, 0, /* H - O */ +-ESC_P, -ESC_Q, -ESC_R, -ESC_S, 0, 0, -ESC_V, -ESC_W, /* P - W */ +-ESC_X, 0, -ESC_Z, '[', '\\', ']', '^', '_', /* X - _ */ + '`', 7, -ESC_b, 0, -ESC_d, ESC_e, ESC_f, 0, /* ` - g */ +-ESC_h, 0, 0, -ESC_k, 0, 0, ESC_n, 0, /* h - o */ +-ESC_p, 0, ESC_r, -ESC_s, ESC_tee, 0, -ESC_v, -ESC_w, /* p - w */ + 0, 0, -ESC_z /* x - z */ +}; + +#else /* This is the "abnormal" table for EBCDIC systems */ +static const short int escapes[] = { +/* 48 */ 0, 0, 0, '.', '<', '(', '+', '|', +/* 50 */ '&', 0, 0, 0, 0, 0, 0, 0, +/* 58 */ 0, 0, '!', '$', '*', ')', ';', '~', +/* 60 */ '-', '/', 0, 0, 0, 0, 0, 0, +/* 68 */ 0, 0, '|', ',', '%', '_', '>', '?', +/* 70 */ 0, 0, 0, 0, 0, 0, 0, 0, +/* 78 */ 0, '`', ':', '#', '@', '\'', '=', '"', +/* 80 */ 0, 7, -ESC_b, 0, -ESC_d, ESC_e, ESC_f, 0, +/* 88 */-ESC_h, 0, 0, '{', 0, 0, 0, 0, +/* 90 */ 0, 0, -ESC_k, 'l', 0, ESC_n, 0, -ESC_p, +/* 98 */ 0, ESC_r, 0, '}', 0, 0, 0, 0, +/* A0 */ 0, '~', -ESC_s, ESC_tee, 0,-ESC_v, -ESC_w, 0, +/* A8 */ 0,-ESC_z, 0, 0, 0, '[', 0, 0, +/* B0 */ 0, 0, 0, 0, 0, 0, 0, 0, +/* B8 */ 0, 0, 0, 0, 0, ']', '=', '-', +/* C0 */ '{',-ESC_A, -ESC_B, -ESC_C, -ESC_D,-ESC_E, 0, -ESC_G, +/* C8 */-ESC_H, 0, 0, 0, 0, 0, 0, 0, +/* D0 */ '}', 0, -ESC_K, 0, 0, 0, 0, -ESC_P, +/* D8 */-ESC_Q,-ESC_R, 0, 0, 0, 0, 0, 0, +/* E0 */ '\\', 0, -ESC_S, 0, 0,-ESC_V, -ESC_W, -ESC_X, +/* E8 */ 0,-ESC_Z, 0, 0, 0, 0, 0, 0, +/* F0 */ 0, 0, 0, 0, 0, 0, 0, 0, +/* F8 */ 0, 0, 0, 0, 0, 0, 0, 0 +}; +#endif + + +/* Table of special "verbs" like (*PRUNE). This is a short table, so it is +searched linearly. Put all the names into a single string, in order to reduce +the number of relocations when a shared library is dynamically linked. */ + +typedef struct verbitem { + int len; + int op; +} verbitem; + +static const char verbnames[] = + "ACCEPT\0" + "COMMIT\0" + "F\0" + "FAIL\0" + "PRUNE\0" + "SKIP\0" + "THEN"; + +static verbitem verbs[] = { + { 6, OP_ACCEPT }, + { 6, OP_COMMIT }, + { 1, OP_FAIL }, + { 4, OP_FAIL }, + { 5, OP_PRUNE }, + { 4, OP_SKIP }, + { 4, OP_THEN } +}; + +static int verbcount = sizeof(verbs)/sizeof(verbitem); + + +/* Tables of names of POSIX character classes and their lengths. The names are +now all in a single string, to reduce the number of relocations when a shared +library is dynamically loaded. The list of lengths is terminated by a zero +length entry. The first three must be alpha, lower, upper, as this is assumed +for handling case independence. */ + +static const char posix_names[] = + "alpha\0" "lower\0" "upper\0" "alnum\0" "ascii\0" "blank\0" + "cntrl\0" "digit\0" "graph\0" "print\0" "punct\0" "space\0" + "word\0" "xdigit"; + +static const uschar posix_name_lengths[] = { + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 6, 0 }; + +/* Table of class bit maps for each POSIX class. Each class is formed from a +base map, with an optional addition or removal of another map. Then, for some +classes, there is some additional tweaking: for [:blank:] the vertical space +characters are removed, and for [:alpha:] and [:alnum:] the underscore +character is removed. The triples in the table consist of the base map offset, +second map offset or -1 if no second map, and a non-negative value for map +addition or a negative value for map subtraction (if there are two maps). The +absolute value of the third field has these meanings: 0 => no tweaking, 1 => +remove vertical space characters, 2 => remove underscore. */ + +static const int posix_class_maps[] = { + cbit_word, cbit_digit, -2, /* alpha */ + cbit_lower, -1, 0, /* lower */ + cbit_upper, -1, 0, /* upper */ + cbit_word, -1, 2, /* alnum - word without underscore */ + cbit_print, cbit_cntrl, 0, /* ascii */ + cbit_space, -1, 1, /* blank - a GNU extension */ + cbit_cntrl, -1, 0, /* cntrl */ + cbit_digit, -1, 0, /* digit */ + cbit_graph, -1, 0, /* graph */ + cbit_print, -1, 0, /* print */ + cbit_punct, -1, 0, /* punct */ + cbit_space, -1, 0, /* space */ + cbit_word, -1, 0, /* word - a Perl extension */ + cbit_xdigit,-1, 0 /* xdigit */ +}; + + +#define STRING(a) # a +#define XSTRING(s) STRING(s) + +/* The texts of compile-time error messages. These are "char *" because they +are passed to the outside world. Do not ever re-use any error number, because +they are documented. Always add a new error instead. Messages marked DEAD below +are no longer used. This used to be a table of strings, but in order to reduce +the number of relocations needed when a shared library is loaded dynamically, +it is now one long string. We cannot use a table of offsets, because the +lengths of inserts such as XSTRING(MAX_NAME_SIZE) are not known. Instead, we +simply count through to the one we want - this isn't a performance issue +because these strings are used only when there is a compilation error. */ + +static const char error_texts[] = + "no error\0" + "\\ at end of pattern\0" + "\\c at end of pattern\0" + "unrecognized character follows \\\0" + "numbers out of order in {} quantifier\0" + /* 5 */ + "number too big in {} quantifier\0" + "missing terminating ] for character class\0" + "invalid escape sequence in character class\0" + "range out of order in character class\0" + "nothing to repeat\0" + /* 10 */ + "operand of unlimited repeat could match the empty string\0" /** DEAD **/ + "internal error: unexpected repeat\0" + "unrecognized character after (? or (?-\0" + "POSIX named classes are supported only within a class\0" + "missing )\0" + /* 15 */ + "reference to non-existent subpattern\0" + "erroffset passed as NULL\0" + "unknown option bit(s) set\0" + "missing ) after comment\0" + "parentheses nested too deeply\0" /** DEAD **/ + /* 20 */ + "regular expression is too large\0" + "failed to get memory\0" + "unmatched parentheses\0" + "internal error: code overflow\0" + "unrecognized character after (?<\0" + /* 25 */ + "lookbehind assertion is not fixed length\0" + "malformed number or name after (?(\0" + "conditional group contains more than two branches\0" + "assertion expected after (?(\0" + "(?R or (?[+-]digits must be followed by )\0" + /* 30 */ + "unknown POSIX class name\0" + "POSIX collating elements are not supported\0" + "this version of PCRE is not compiled with PCRE_UTF8 support\0" + "spare error\0" /** DEAD **/ + "character value in \\x{...} sequence is too large\0" + /* 35 */ + "invalid condition (?(0)\0" + "\\C not allowed in lookbehind assertion\0" + "PCRE does not support \\L, \\l, \\N, \\U, or \\u\0" + "number after (?C is > 255\0" + "closing ) for (?C expected\0" + /* 40 */ + "recursive call could loop indefinitely\0" + "unrecognized character after (?P\0" + "syntax error in subpattern name (missing terminator)\0" + "two named subpatterns have the same name\0" + "invalid UTF-8 string\0" + /* 45 */ + "support for \\P, \\p, and \\X has not been compiled\0" + "malformed \\P or \\p sequence\0" + "unknown property name after \\P or \\p\0" + "subpattern name is too long (maximum " XSTRING(MAX_NAME_SIZE) " characters)\0" + "too many named subpatterns (maximum " XSTRING(MAX_NAME_COUNT) ")\0" + /* 50 */ + "repeated subpattern is too long\0" /** DEAD **/ + "octal value is greater than \\377 (not in UTF-8 mode)\0" + "internal error: overran compiling workspace\0" + "internal error: previously-checked referenced subpattern not found\0" + "DEFINE group contains more than one branch\0" + /* 55 */ + "repeating a DEFINE group is not allowed\0" + "inconsistent NEWLINE options\0" + "\\g is not followed by a braced name or an optionally braced non-zero number\0" + "(?+ or (?- or (?(+ or (?(- must be followed by a non-zero number\0" + "(*VERB) with an argument is not supported\0" + /* 60 */ + "(*VERB) not recognized\0" + "number is too big\0" + "subpattern name expected\0" + "digit expected after (?+"; + + +/* Table to identify digits and hex digits. This is used when compiling +patterns. Note that the tables in chartables are dependent on the locale, and +may mark arbitrary characters as digits - but the PCRE compiling code expects +to handle only 0-9, a-z, and A-Z as digits when compiling. That is why we have +a private table here. It costs 256 bytes, but it is a lot faster than doing +character value tests (at least in some simple cases I timed), and in some +applications one wants PCRE to compile efficiently as well as match +efficiently. + +For convenience, we use the same bit definitions as in chartables: + + 0x04 decimal digit + 0x08 hexadecimal digit + +Then we can use ctype_digit and ctype_xdigit in the code. */ + +#ifndef EBCDIC /* This is the "normal" case, for ASCII systems */ +static const unsigned char digitab[] = + { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 8- 15 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 16- 23 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* - ' */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* ( - / */ + 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, /* 0 - 7 */ + 0x0c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00, /* 8 - ? */ + 0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00, /* @ - G */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* H - O */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* P - W */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* X - _ */ + 0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00, /* ` - g */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* h - o */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* p - w */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* x -127 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 128-135 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 136-143 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144-151 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 152-159 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160-167 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 168-175 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 176-183 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 192-199 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 200-207 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 208-215 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 216-223 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 224-231 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 232-239 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 240-247 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};/* 248-255 */ + +#else /* This is the "abnormal" case, for EBCDIC systems */ +static const unsigned char digitab[] = + { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 0 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 8- 15 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 16- 23 10 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 32- 39 20 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 40- 47 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 48- 55 30 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 56- 63 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* - 71 40 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 72- | */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* & - 87 50 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 88- 95 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* - -103 60 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 104- ? */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 112-119 70 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 120- " */ + 0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00, /* 128- g 80 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* h -143 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144- p 90 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* q -159 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160- x A0 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* y -175 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* ^ -183 B0 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191 */ + 0x00,0x08,0x08,0x08,0x08,0x08,0x08,0x00, /* { - G C0 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* H -207 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* } - P D0 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* Q -223 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* \ - X E0 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* Y -239 */ + 0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c,0x0c, /* 0 - 7 F0 */ + 0x0c,0x0c,0x00,0x00,0x00,0x00,0x00,0x00};/* 8 -255 */ + +static const unsigned char ebcdic_chartab[] = { /* chartable partial dup */ + 0x80,0x00,0x00,0x00,0x00,0x01,0x00,0x00, /* 0- 7 */ + 0x00,0x00,0x00,0x00,0x01,0x01,0x00,0x00, /* 8- 15 */ + 0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00, /* 16- 23 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */ + 0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00, /* 32- 39 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 40- 47 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 48- 55 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 56- 63 */ + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* - 71 */ + 0x00,0x00,0x00,0x80,0x00,0x80,0x80,0x80, /* 72- | */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* & - 87 */ + 0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00, /* 88- 95 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* - -103 */ + 0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x80, /* 104- ? */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 112-119 */ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 120- " */ + 0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* 128- g */ + 0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /* h -143 */ + 0x00,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* 144- p */ + 0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /* q -159 */ + 0x00,0x00,0x12,0x12,0x12,0x12,0x12,0x12, /* 160- x */ + 0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /* y -175 */ + 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* ^ -183 */ + 0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00, /* 184-191 */ + 0x80,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* { - G */ + 0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /* H -207 */ + 0x00,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* } - P */ + 0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /* Q -223 */ + 0x00,0x00,0x12,0x12,0x12,0x12,0x12,0x12, /* \ - X */ + 0x12,0x12,0x00,0x00,0x00,0x00,0x00,0x00, /* Y -239 */ + 0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, /* 0 - 7 */ + 0x1c,0x1c,0x00,0x00,0x00,0x00,0x00,0x00};/* 8 -255 */ +#endif + + +/* Definition to allow mutual recursion */ + +static BOOL + compile_regex(int, int, uschar **, const uschar **, int *, BOOL, BOOL, int, + int *, int *, branch_chain *, compile_data *, int *); + + + +/************************************************* +* Find an error text * +*************************************************/ + +/* The error texts are now all in one long string, to save on relocations. As +some of the text is of unknown length, we can't use a table of offsets. +Instead, just count through the strings. This is not a performance issue +because it happens only when there has been a compilation error. + +Argument: the error number +Returns: pointer to the error string +*/ + +static const char * +find_error_text(int n) +{ +const char *s = error_texts; +for (; n > 0; n--) while (*s++ != 0); +return s; +} + + +/************************************************* +* Handle escapes * +*************************************************/ + +/* This function is called when a \ has been encountered. It either returns a +positive value for a simple escape such as \n, or a negative value which +encodes one of the more complicated things such as \d. A backreference to group +n is returned as -(ESC_REF + n); ESC_REF is the highest ESC_xxx macro. When +UTF-8 is enabled, a positive value greater than 255 may be returned. On entry, +ptr is pointing at the \. On exit, it is on the final character of the escape +sequence. + +Arguments: + ptrptr points to the pattern position pointer + errorcodeptr points to the errorcode variable + bracount number of previous extracting brackets + options the options bits + isclass TRUE if inside a character class + +Returns: zero or positive => a data character + negative => a special escape sequence + on error, errorcodeptr is set +*/ + +static int +check_escape(const uschar **ptrptr, int *errorcodeptr, int bracount, + int options, BOOL isclass) +{ +BOOL utf8 = (options & PCRE_UTF8) != 0; +const uschar *ptr = *ptrptr + 1; +int c, i; + +GETCHARINCTEST(c, ptr); /* Get character value, increment pointer */ +ptr--; /* Set pointer back to the last byte */ + +/* If backslash is at the end of the pattern, it's an error. */ + +if (c == 0) *errorcodeptr = ERR1; + +/* Non-alphanumerics are literals. For digits or letters, do an initial lookup +in a table. A non-zero result is something that can be returned immediately. +Otherwise further processing may be required. */ + +#ifndef EBCDIC /* ASCII coding */ +else if (c < '0' || c > 'z') {} /* Not alphanumeric */ +else if ((i = escapes[c - '0']) != 0) c = i; + +#else /* EBCDIC coding */ +else if (c < 'a' || (ebcdic_chartab[c] & 0x0E) == 0) {} /* Not alphanumeric */ +else if ((i = escapes[c - 0x48]) != 0) c = i; +#endif + +/* Escapes that need further processing, or are illegal. */ + +else + { + const uschar *oldptr; + BOOL braced, negated; + + switch (c) + { + /* A number of Perl escapes are not handled by PCRE. We give an explicit + error. */ + + case 'l': + case 'L': + case 'N': + case 'u': + case 'U': + *errorcodeptr = ERR37; + break; + + /* \g must be followed by a number, either plain or braced. If positive, it + is an absolute backreference. If negative, it is a relative backreference. + This is a Perl 5.10 feature. Perl 5.10 also supports \g{name} as a + reference to a named group. This is part of Perl's movement towards a + unified syntax for back references. As this is synonymous with \k{name}, we + fudge it up by pretending it really was \k. */ + + case 'g': + if (ptr[1] == '{') + { + const uschar *p; + for (p = ptr+2; *p != 0 && *p != '}'; p++) + if (*p != '-' && (digitab[*p] & ctype_digit) == 0) break; + if (*p != 0 && *p != '}') + { + c = -ESC_k; + break; + } + braced = TRUE; + ptr++; + } + else braced = FALSE; + + if (ptr[1] == '-') + { + negated = TRUE; + ptr++; + } + else negated = FALSE; + + c = 0; + while ((digitab[ptr[1]] & ctype_digit) != 0) + c = c * 10 + *(++ptr) - '0'; + + if (c < 0) + { + *errorcodeptr = ERR61; + break; + } + + if (c == 0 || (braced && *(++ptr) != '}')) + { + *errorcodeptr = ERR57; + break; + } + + if (negated) + { + if (c > bracount) + { + *errorcodeptr = ERR15; + break; + } + c = bracount - (c - 1); + } + + c = -(ESC_REF + c); + break; + + /* The handling of escape sequences consisting of a string of digits + starting with one that is not zero is not straightforward. By experiment, + the way Perl works seems to be as follows: + + Outside a character class, the digits are read as a decimal number. If the + number is less than 10, or if there are that many previous extracting + left brackets, then it is a back reference. Otherwise, up to three octal + digits are read to form an escaped byte. Thus \123 is likely to be octal + 123 (cf \0123, which is octal 012 followed by the literal 3). If the octal + value is greater than 377, the least significant 8 bits are taken. Inside a + character class, \ followed by a digit is always an octal number. */ + + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': + + if (!isclass) + { + oldptr = ptr; + c -= '0'; + while ((digitab[ptr[1]] & ctype_digit) != 0) + c = c * 10 + *(++ptr) - '0'; + if (c < 0) + { + *errorcodeptr = ERR61; + break; + } + if (c < 10 || c <= bracount) + { + c = -(ESC_REF + c); + break; + } + ptr = oldptr; /* Put the pointer back and fall through */ + } + + /* Handle an octal number following \. If the first digit is 8 or 9, Perl + generates a binary zero byte and treats the digit as a following literal. + Thus we have to pull back the pointer by one. */ + + if ((c = *ptr) >= '8') + { + ptr--; + c = 0; + break; + } + + /* \0 always starts an octal number, but we may drop through to here with a + larger first octal digit. The original code used just to take the least + significant 8 bits of octal numbers (I think this is what early Perls used + to do). Nowadays we allow for larger numbers in UTF-8 mode, but no more + than 3 octal digits. */ + + case '0': + c -= '0'; + while(i++ < 2 && ptr[1] >= '0' && ptr[1] <= '7') + c = c * 8 + *(++ptr) - '0'; + if (!utf8 && c > 255) *errorcodeptr = ERR51; + break; + + /* \x is complicated. \x{ddd} is a character number which can be greater + than 0xff in utf8 mode, but only if the ddd are hex digits. If not, { is + treated as a data character. */ + + case 'x': + if (ptr[1] == '{') + { + const uschar *pt = ptr + 2; + int count = 0; + + c = 0; + while ((digitab[*pt] & ctype_xdigit) != 0) + { + register int cc = *pt++; + if (c == 0 && cc == '0') continue; /* Leading zeroes */ + count++; + +#ifndef EBCDIC /* ASCII coding */ + if (cc >= 'a') cc -= 32; /* Convert to upper case */ + c = (c << 4) + cc - ((cc < 'A')? '0' : ('A' - 10)); +#else /* EBCDIC coding */ + if (cc >= 'a' && cc <= 'z') cc += 64; /* Convert to upper case */ + c = (c << 4) + cc - ((cc >= '0')? '0' : ('A' - 10)); +#endif + } + + if (*pt == '}') + { + if (c < 0 || count > (utf8? 8 : 2)) *errorcodeptr = ERR34; + ptr = pt; + break; + } + + /* If the sequence of hex digits does not end with '}', then we don't + recognize this construct; fall through to the normal \x handling. */ + } + + /* Read just a single-byte hex-defined char */ + + c = 0; + while (i++ < 2 && (digitab[ptr[1]] & ctype_xdigit) != 0) + { + int cc; /* Some compilers don't like ++ */ + cc = *(++ptr); /* in initializers */ +#ifndef EBCDIC /* ASCII coding */ + if (cc >= 'a') cc -= 32; /* Convert to upper case */ + c = c * 16 + cc - ((cc < 'A')? '0' : ('A' - 10)); +#else /* EBCDIC coding */ + if (cc <= 'z') cc += 64; /* Convert to upper case */ + c = c * 16 + cc - ((cc >= '0')? '0' : ('A' - 10)); +#endif + } + break; + + /* For \c, a following letter is upper-cased; then the 0x40 bit is flipped. + This coding is ASCII-specific, but then the whole concept of \cx is + ASCII-specific. (However, an EBCDIC equivalent has now been added.) */ + + case 'c': + c = *(++ptr); + if (c == 0) + { + *errorcodeptr = ERR2; + break; + } + +#ifndef EBCDIC /* ASCII coding */ + if (c >= 'a' && c <= 'z') c -= 32; + c ^= 0x40; +#else /* EBCDIC coding */ + if (c >= 'a' && c <= 'z') c += 64; + c ^= 0xC0; +#endif + break; + + /* PCRE_EXTRA enables extensions to Perl in the matter of escapes. Any + other alphanumeric following \ is an error if PCRE_EXTRA was set; + otherwise, for Perl compatibility, it is a literal. This code looks a bit + odd, but there used to be some cases other than the default, and there may + be again in future, so I haven't "optimized" it. */ + + default: + if ((options & PCRE_EXTRA) != 0) switch(c) + { + default: + *errorcodeptr = ERR3; + break; + } + break; + } + } + +*ptrptr = ptr; +return c; +} + + + +#ifdef SUPPORT_UCP +/************************************************* +* Handle \P and \p * +*************************************************/ + +/* This function is called after \P or \p has been encountered, provided that +PCRE is compiled with support for Unicode properties. On entry, ptrptr is +pointing at the P or p. On exit, it is pointing at the final character of the +escape sequence. + +Argument: + ptrptr points to the pattern position pointer + negptr points to a boolean that is set TRUE for negation else FALSE + dptr points to an int that is set to the detailed property value + errorcodeptr points to the error code variable + +Returns: type value from ucp_type_table, or -1 for an invalid type +*/ + +static int +get_ucp(const uschar **ptrptr, BOOL *negptr, int *dptr, int *errorcodeptr) +{ +int c, i, bot, top; +const uschar *ptr = *ptrptr; +char name[32]; + +c = *(++ptr); +if (c == 0) goto ERROR_RETURN; + +*negptr = FALSE; + +/* \P or \p can be followed by a name in {}, optionally preceded by ^ for +negation. */ + +if (c == '{') + { + if (ptr[1] == '^') + { + *negptr = TRUE; + ptr++; + } + for (i = 0; i < (int)sizeof(name) - 1; i++) + { + c = *(++ptr); + if (c == 0) goto ERROR_RETURN; + if (c == '}') break; + name[i] = c; + } + if (c !='}') goto ERROR_RETURN; + name[i] = 0; + } + +/* Otherwise there is just one following character */ + +else + { + name[0] = c; + name[1] = 0; + } + +*ptrptr = ptr; + +/* Search for a recognized property name using binary chop */ + +bot = 0; +top = _pcre_utt_size; + +while (bot < top) + { + i = (bot + top) >> 1; + c = strcmp(name, _pcre_utt_names + _pcre_utt[i].name_offset); + if (c == 0) + { + *dptr = _pcre_utt[i].value; + return _pcre_utt[i].type; + } + if (c > 0) bot = i + 1; else top = i; + } + +*errorcodeptr = ERR47; +*ptrptr = ptr; +return -1; + +ERROR_RETURN: +*errorcodeptr = ERR46; +*ptrptr = ptr; +return -1; +} +#endif + + + + +/************************************************* +* Check for counted repeat * +*************************************************/ + +/* This function is called when a '{' is encountered in a place where it might +start a quantifier. It looks ahead to see if it really is a quantifier or not. +It is only a quantifier if it is one of the forms {ddd} {ddd,} or {ddd,ddd} +where the ddds are digits. + +Arguments: + p pointer to the first char after '{' + +Returns: TRUE or FALSE +*/ + +static BOOL +is_counted_repeat(const uschar *p) +{ +if ((digitab[*p++] & ctype_digit) == 0) return FALSE; +while ((digitab[*p] & ctype_digit) != 0) p++; +if (*p == '}') return TRUE; + +if (*p++ != ',') return FALSE; +if (*p == '}') return TRUE; + +if ((digitab[*p++] & ctype_digit) == 0) return FALSE; +while ((digitab[*p] & ctype_digit) != 0) p++; + +return (*p == '}'); +} + + + +/************************************************* +* Read repeat counts * +*************************************************/ + +/* Read an item of the form {n,m} and return the values. This is called only +after is_counted_repeat() has confirmed that a repeat-count quantifier exists, +so the syntax is guaranteed to be correct, but we need to check the values. + +Arguments: + p pointer to first char after '{' + minp pointer to int for min + maxp pointer to int for max + returned as -1 if no max + errorcodeptr points to error code variable + +Returns: pointer to '}' on success; + current ptr on error, with errorcodeptr set non-zero +*/ + +static const uschar * +read_repeat_counts(const uschar *p, int *minp, int *maxp, int *errorcodeptr) +{ +int min = 0; +int max = -1; + +/* Read the minimum value and do a paranoid check: a negative value indicates +an integer overflow. */ + +while ((digitab[*p] & ctype_digit) != 0) min = min * 10 + *p++ - '0'; +if (min < 0 || min > 65535) + { + *errorcodeptr = ERR5; + return p; + } + +/* Read the maximum value if there is one, and again do a paranoid on its size. +Also, max must not be less than min. */ + +if (*p == '}') max = min; else + { + if (*(++p) != '}') + { + max = 0; + while((digitab[*p] & ctype_digit) != 0) max = max * 10 + *p++ - '0'; + if (max < 0 || max > 65535) + { + *errorcodeptr = ERR5; + return p; + } + if (max < min) + { + *errorcodeptr = ERR4; + return p; + } + } + } + +/* Fill in the required variables, and pass back the pointer to the terminating +'}'. */ + +*minp = min; +*maxp = max; +return p; +} + + + +/************************************************* +* Find forward referenced subpattern * +*************************************************/ + +/* This function scans along a pattern's text looking for capturing +subpatterns, and counting them. If it finds a named pattern that matches the +name it is given, it returns its number. Alternatively, if the name is NULL, it +returns when it reaches a given numbered subpattern. This is used for forward +references to subpatterns. We know that if (?P< is encountered, the name will +be terminated by '>' because that is checked in the first pass. + +Arguments: + ptr current position in the pattern + count current count of capturing parens so far encountered + name name to seek, or NULL if seeking a numbered subpattern + lorn name length, or subpattern number if name is NULL + xmode TRUE if we are in /x mode + +Returns: the number of the named subpattern, or -1 if not found +*/ + +static int +find_parens(const uschar *ptr, int count, const uschar *name, int lorn, + BOOL xmode) +{ +const uschar *thisname; + +for (; *ptr != 0; ptr++) + { + int term; + + /* Skip over backslashed characters and also entire \Q...\E */ + + if (*ptr == '\\') + { + if (*(++ptr) == 0) return -1; + if (*ptr == 'Q') for (;;) + { + while (*(++ptr) != 0 && *ptr != '\\'); + if (*ptr == 0) return -1; + if (*(++ptr) == 'E') break; + } + continue; + } + + /* Skip over character classes */ + + if (*ptr == '[') + { + while (*(++ptr) != ']') + { + if (*ptr == 0) return -1; + if (*ptr == '\\') + { + if (*(++ptr) == 0) return -1; + if (*ptr == 'Q') for (;;) + { + while (*(++ptr) != 0 && *ptr != '\\'); + if (*ptr == 0) return -1; + if (*(++ptr) == 'E') break; + } + continue; + } + } + continue; + } + + /* Skip comments in /x mode */ + + if (xmode && *ptr == '#') + { + while (*(++ptr) != 0 && *ptr != '\n'); + if (*ptr == 0) return -1; + continue; + } + + /* An opening parens must now be a real metacharacter */ + + if (*ptr != '(') continue; + if (ptr[1] != '?' && ptr[1] != '*') + { + count++; + if (name == NULL && count == lorn) return count; + continue; + } + + ptr += 2; + if (*ptr == 'P') ptr++; /* Allow optional P */ + + /* We have to disambiguate (? */ + + if ((*ptr != '<' || ptr[1] == '!' || ptr[1] == '=') && + *ptr != '\'') + continue; + + count++; + + if (name == NULL && count == lorn) return count; + term = *ptr++; + if (term == '<') term = '>'; + thisname = ptr; + while (*ptr != term) ptr++; + if (name != NULL && lorn == ptr - thisname && + strncmp((const char *)name, (const char *)thisname, lorn) == 0) + return count; + } + +return -1; +} + + + +/************************************************* +* Find first significant op code * +*************************************************/ + +/* This is called by several functions that scan a compiled expression looking +for a fixed first character, or an anchoring op code etc. It skips over things +that do not influence this. For some calls, a change of option is important. +For some calls, it makes sense to skip negative forward and all backward +assertions, and also the \b assertion; for others it does not. + +Arguments: + code pointer to the start of the group + options pointer to external options + optbit the option bit whose changing is significant, or + zero if none are + skipassert TRUE if certain assertions are to be skipped + +Returns: pointer to the first significant opcode +*/ + +static const uschar* +first_significant_code(const uschar *code, int *options, int optbit, + BOOL skipassert) +{ +for (;;) + { + switch ((int)*code) + { + case OP_OPT: + if (optbit > 0 && ((int)code[1] & optbit) != (*options & optbit)) + *options = (int)code[1]; + code += 2; + break; + + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + if (!skipassert) return code; + do code += GET(code, 1); while (*code == OP_ALT); + code += _pcre_OP_lengths[*code]; + break; + + case OP_WORD_BOUNDARY: + case OP_NOT_WORD_BOUNDARY: + if (!skipassert) return code; + /* Fall through */ + + case OP_CALLOUT: + case OP_CREF: + case OP_RREF: + case OP_DEF: + code += _pcre_OP_lengths[*code]; + break; + + default: + return code; + } + } +/* Control never reaches here */ +} + + + + +/************************************************* +* Find the fixed length of a pattern * +*************************************************/ + +/* Scan a pattern and compute the fixed length of subject that will match it, +if the length is fixed. This is needed for dealing with backward assertions. +In UTF8 mode, the result is in characters rather than bytes. + +Arguments: + code points to the start of the pattern (the bracket) + options the compiling options + +Returns: the fixed length, or -1 if there is no fixed length, + or -2 if \C was encountered +*/ + +static int +find_fixedlength(uschar *code, int options) +{ +int length = -1; + +register int branchlength = 0; +register uschar *cc = code + 1 + LINK_SIZE; + +/* Scan along the opcodes for this branch. If we get to the end of the +branch, check the length against that of the other branches. */ + +for (;;) + { + int d; + register int op = *cc; + switch (op) + { + case OP_CBRA: + case OP_BRA: + case OP_ONCE: + case OP_COND: + d = find_fixedlength(cc + ((op == OP_CBRA)? 2:0), options); + if (d < 0) return d; + branchlength += d; + do cc += GET(cc, 1); while (*cc == OP_ALT); + cc += 1 + LINK_SIZE; + break; + + /* Reached end of a branch; if it's a ket it is the end of a nested + call. If it's ALT it is an alternation in a nested call. If it is + END it's the end of the outer call. All can be handled by the same code. */ + + case OP_ALT: + case OP_KET: + case OP_KETRMAX: + case OP_KETRMIN: + case OP_END: + if (length < 0) length = branchlength; + else if (length != branchlength) return -1; + if (*cc != OP_ALT) return length; + cc += 1 + LINK_SIZE; + branchlength = 0; + break; + + /* Skip over assertive subpatterns */ + + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + do cc += GET(cc, 1); while (*cc == OP_ALT); + /* Fall through */ + + /* Skip over things that don't match chars */ + + case OP_REVERSE: + case OP_CREF: + case OP_RREF: + case OP_DEF: + case OP_OPT: + case OP_CALLOUT: + case OP_SOD: + case OP_SOM: + case OP_EOD: + case OP_EODN: + case OP_CIRC: + case OP_DOLL: + case OP_NOT_WORD_BOUNDARY: + case OP_WORD_BOUNDARY: + cc += _pcre_OP_lengths[*cc]; + break; + + /* Handle literal characters */ + + case OP_CHAR: + case OP_CHARNC: + case OP_NOT: + branchlength++; + cc += 2; +#ifdef SUPPORT_UTF8 + if ((options & PCRE_UTF8) != 0) + { + while ((*cc & 0xc0) == 0x80) cc++; + } +#endif + break; + + /* Handle exact repetitions. The count is already in characters, but we + need to skip over a multibyte character in UTF8 mode. */ + + case OP_EXACT: + branchlength += GET2(cc,1); + cc += 4; +#ifdef SUPPORT_UTF8 + if ((options & PCRE_UTF8) != 0) + { + while((*cc & 0x80) == 0x80) cc++; + } +#endif + break; + + case OP_TYPEEXACT: + branchlength += GET2(cc,1); + if (cc[3] == OP_PROP || cc[3] == OP_NOTPROP) cc += 2; + cc += 4; + break; + + /* Handle single-char matchers */ + + case OP_PROP: + case OP_NOTPROP: + cc += 2; + /* Fall through */ + + case OP_NOT_DIGIT: + case OP_DIGIT: + case OP_NOT_WHITESPACE: + case OP_WHITESPACE: + case OP_NOT_WORDCHAR: + case OP_WORDCHAR: + case OP_ANY: + branchlength++; + cc++; + break; + + /* The single-byte matcher isn't allowed */ + + case OP_ANYBYTE: + return -2; + + /* Check a class for variable quantification */ + +#ifdef SUPPORT_UTF8 + case OP_XCLASS: + cc += GET(cc, 1) - 33; + /* Fall through */ +#endif + + case OP_CLASS: + case OP_NCLASS: + cc += 33; + + switch (*cc) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRQUERY: + case OP_CRMINQUERY: + return -1; + + case OP_CRRANGE: + case OP_CRMINRANGE: + if (GET2(cc,1) != GET2(cc,3)) return -1; + branchlength += GET2(cc,1); + cc += 5; + break; + + default: + branchlength++; + } + break; + + /* Anything else is variable length */ + + default: + return -1; + } + } +/* Control never gets here */ +} + + + + +/************************************************* +* Scan compiled regex for numbered bracket * +*************************************************/ + +/* This little function scans through a compiled pattern until it finds a +capturing bracket with the given number. + +Arguments: + code points to start of expression + utf8 TRUE in UTF-8 mode + number the required bracket number + +Returns: pointer to the opcode for the bracket, or NULL if not found +*/ + +static const uschar * +find_bracket(const uschar *code, BOOL utf8, int number) +{ +for (;;) + { + register int c = *code; + if (c == OP_END) return NULL; + + /* XCLASS is used for classes that cannot be represented just by a bit + map. This includes negated single high-valued characters. The length in + the table is zero; the actual length is stored in the compiled code. */ + + if (c == OP_XCLASS) code += GET(code, 1); + + /* Handle capturing bracket */ + + else if (c == OP_CBRA) + { + int n = GET2(code, 1+LINK_SIZE); + if (n == number) return (uschar *)code; + code += _pcre_OP_lengths[c]; + } + + /* Otherwise, we can get the item's length from the table, except that for + repeated character types, we have to test for \p and \P, which have an extra + two bytes of parameters. */ + + else + { + switch(c) + { + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + case OP_TYPEPOSSTAR: + case OP_TYPEPOSPLUS: + case OP_TYPEPOSQUERY: + if (code[1] == OP_PROP || code[1] == OP_NOTPROP) code += 2; + break; + + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + case OP_TYPEEXACT: + case OP_TYPEPOSUPTO: + if (code[3] == OP_PROP || code[3] == OP_NOTPROP) code += 2; + break; + } + + /* Add in the fixed length from the table */ + + code += _pcre_OP_lengths[c]; + + /* In UTF-8 mode, opcodes that are followed by a character may be followed by + a multi-byte character. The length in the table is a minimum, so we have to + arrange to skip the extra bytes. */ + +#ifdef SUPPORT_UTF8 + if (utf8) switch(c) + { + case OP_CHAR: + case OP_CHARNC: + case OP_EXACT: + case OP_UPTO: + case OP_MINUPTO: + case OP_POSUPTO: + case OP_STAR: + case OP_MINSTAR: + case OP_POSSTAR: + case OP_PLUS: + case OP_MINPLUS: + case OP_POSPLUS: + case OP_QUERY: + case OP_MINQUERY: + case OP_POSQUERY: + if (code[-1] >= 0xc0) code += _pcre_utf8_table4[code[-1] & 0x3f]; + break; + } +#endif + } + } +} + + + +/************************************************* +* Scan compiled regex for recursion reference * +*************************************************/ + +/* This little function scans through a compiled pattern until it finds an +instance of OP_RECURSE. + +Arguments: + code points to start of expression + utf8 TRUE in UTF-8 mode + +Returns: pointer to the opcode for OP_RECURSE, or NULL if not found +*/ + +static const uschar * +find_recurse(const uschar *code, BOOL utf8) +{ +for (;;) + { + register int c = *code; + if (c == OP_END) return NULL; + if (c == OP_RECURSE) return code; + + /* XCLASS is used for classes that cannot be represented just by a bit + map. This includes negated single high-valued characters. The length in + the table is zero; the actual length is stored in the compiled code. */ + + if (c == OP_XCLASS) code += GET(code, 1); + + /* Otherwise, we can get the item's length from the table, except that for + repeated character types, we have to test for \p and \P, which have an extra + two bytes of parameters. */ + + else + { + switch(c) + { + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + case OP_TYPEPOSSTAR: + case OP_TYPEPOSPLUS: + case OP_TYPEPOSQUERY: + if (code[1] == OP_PROP || code[1] == OP_NOTPROP) code += 2; + break; + + case OP_TYPEPOSUPTO: + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + case OP_TYPEEXACT: + if (code[3] == OP_PROP || code[3] == OP_NOTPROP) code += 2; + break; + } + + /* Add in the fixed length from the table */ + + code += _pcre_OP_lengths[c]; + + /* In UTF-8 mode, opcodes that are followed by a character may be followed + by a multi-byte character. The length in the table is a minimum, so we have + to arrange to skip the extra bytes. */ + +#ifdef SUPPORT_UTF8 + if (utf8) switch(c) + { + case OP_CHAR: + case OP_CHARNC: + case OP_EXACT: + case OP_UPTO: + case OP_MINUPTO: + case OP_POSUPTO: + case OP_STAR: + case OP_MINSTAR: + case OP_POSSTAR: + case OP_PLUS: + case OP_MINPLUS: + case OP_POSPLUS: + case OP_QUERY: + case OP_MINQUERY: + case OP_POSQUERY: + if (code[-1] >= 0xc0) code += _pcre_utf8_table4[code[-1] & 0x3f]; + break; + } +#endif + } + } +} + + + +/************************************************* +* Scan compiled branch for non-emptiness * +*************************************************/ + +/* This function scans through a branch of a compiled pattern to see whether it +can match the empty string or not. It is called from could_be_empty() +below and from compile_branch() when checking for an unlimited repeat of a +group that can match nothing. Note that first_significant_code() skips over +backward and negative forward assertions when its final argument is TRUE. If we +hit an unclosed bracket, we return "empty" - this means we've struck an inner +bracket whose current branch will already have been scanned. + +Arguments: + code points to start of search + endcode points to where to stop + utf8 TRUE if in UTF8 mode + +Returns: TRUE if what is matched could be empty +*/ + +static BOOL +could_be_empty_branch(const uschar *code, const uschar *endcode, BOOL utf8) +{ +register int c; +for (code = first_significant_code(code + _pcre_OP_lengths[*code], NULL, 0, TRUE); + code < endcode; + code = first_significant_code(code + _pcre_OP_lengths[c], NULL, 0, TRUE)) + { + const uschar *ccode; + + c = *code; + + /* Skip over forward assertions; the other assertions are skipped by + first_significant_code() with a TRUE final argument. */ + + if (c == OP_ASSERT) + { + do code += GET(code, 1); while (*code == OP_ALT); + c = *code; + continue; + } + + /* Groups with zero repeats can of course be empty; skip them. */ + + if (c == OP_BRAZERO || c == OP_BRAMINZERO) + { + code += _pcre_OP_lengths[c]; + do code += GET(code, 1); while (*code == OP_ALT); + c = *code; + continue; + } + + /* For other groups, scan the branches. */ + + if (c == OP_BRA || c == OP_CBRA || c == OP_ONCE || c == OP_COND) + { + BOOL empty_branch; + if (GET(code, 1) == 0) return TRUE; /* Hit unclosed bracket */ + + /* Scan a closed bracket */ + + empty_branch = FALSE; + do + { + if (!empty_branch && could_be_empty_branch(code, endcode, utf8)) + empty_branch = TRUE; + code += GET(code, 1); + } + while (*code == OP_ALT); + if (!empty_branch) return FALSE; /* All branches are non-empty */ + c = *code; + continue; + } + + /* Handle the other opcodes */ + + switch (c) + { + /* Check for quantifiers after a class. XCLASS is used for classes that + cannot be represented just by a bit map. This includes negated single + high-valued characters. The length in _pcre_OP_lengths[] is zero; the + actual length is stored in the compiled code, so we must update "code" + here. */ + +#ifdef SUPPORT_UTF8 + case OP_XCLASS: + ccode = code += GET(code, 1); + goto CHECK_CLASS_REPEAT; +#endif + + case OP_CLASS: + case OP_NCLASS: + ccode = code + 33; + +#ifdef SUPPORT_UTF8 + CHECK_CLASS_REPEAT: +#endif + + switch (*ccode) + { + case OP_CRSTAR: /* These could be empty; continue */ + case OP_CRMINSTAR: + case OP_CRQUERY: + case OP_CRMINQUERY: + break; + + default: /* Non-repeat => class must match */ + case OP_CRPLUS: /* These repeats aren't empty */ + case OP_CRMINPLUS: + return FALSE; + + case OP_CRRANGE: + case OP_CRMINRANGE: + if (GET2(ccode, 1) > 0) return FALSE; /* Minimum > 0 */ + break; + } + break; + + /* Opcodes that must match a character */ + + case OP_PROP: + case OP_NOTPROP: + case OP_EXTUNI: + case OP_NOT_DIGIT: + case OP_DIGIT: + case OP_NOT_WHITESPACE: + case OP_WHITESPACE: + case OP_NOT_WORDCHAR: + case OP_WORDCHAR: + case OP_ANY: + case OP_ANYBYTE: + case OP_CHAR: + case OP_CHARNC: + case OP_NOT: + case OP_PLUS: + case OP_MINPLUS: + case OP_POSPLUS: + case OP_EXACT: + case OP_NOTPLUS: + case OP_NOTMINPLUS: + case OP_NOTPOSPLUS: + case OP_NOTEXACT: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEPOSPLUS: + case OP_TYPEEXACT: + return FALSE; + + /* These are going to continue, as they may be empty, but we have to + fudge the length for the \p and \P cases. */ + + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPOSSTAR: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + case OP_TYPEPOSQUERY: + if (code[1] == OP_PROP || code[1] == OP_NOTPROP) code += 2; + break; + + /* Same for these */ + + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + case OP_TYPEPOSUPTO: + if (code[3] == OP_PROP || code[3] == OP_NOTPROP) code += 2; + break; + + /* End of branch */ + + case OP_KET: + case OP_KETRMAX: + case OP_KETRMIN: + case OP_ALT: + return TRUE; + + /* In UTF-8 mode, STAR, MINSTAR, POSSTAR, QUERY, MINQUERY, POSQUERY, UPTO, + MINUPTO, and POSUPTO may be followed by a multibyte character */ + +#ifdef SUPPORT_UTF8 + case OP_STAR: + case OP_MINSTAR: + case OP_POSSTAR: + case OP_QUERY: + case OP_MINQUERY: + case OP_POSQUERY: + case OP_UPTO: + case OP_MINUPTO: + case OP_POSUPTO: + if (utf8) while ((code[2] & 0xc0) == 0x80) code++; + break; +#endif + } + } + +return TRUE; +} + + + +/************************************************* +* Scan compiled regex for non-emptiness * +*************************************************/ + +/* This function is called to check for left recursive calls. We want to check +the current branch of the current pattern to see if it could match the empty +string. If it could, we must look outwards for branches at other levels, +stopping when we pass beyond the bracket which is the subject of the recursion. + +Arguments: + code points to start of the recursion + endcode points to where to stop (current RECURSE item) + bcptr points to the chain of current (unclosed) branch starts + utf8 TRUE if in UTF-8 mode + +Returns: TRUE if what is matched could be empty +*/ + +static BOOL +could_be_empty(const uschar *code, const uschar *endcode, branch_chain *bcptr, + BOOL utf8) +{ +while (bcptr != NULL && bcptr->current >= code) + { + if (!could_be_empty_branch(bcptr->current, endcode, utf8)) return FALSE; + bcptr = bcptr->outer; + } +return TRUE; +} + + + +/************************************************* +* Check for POSIX class syntax * +*************************************************/ + +/* This function is called when the sequence "[:" or "[." or "[=" is +encountered in a character class. It checks whether this is followed by a +sequence of characters terminated by a matching ":]" or ".]" or "=]". If we +reach an unescaped ']' without the special preceding character, return FALSE. + +Originally, this function only recognized a sequence of letters between the +terminators, but it seems that Perl recognizes any sequence of characters, +though of course unknown POSIX names are subsequently rejected. Perl gives an +"Unknown POSIX class" error for [:f\oo:] for example, where previously PCRE +didn't consider this to be a POSIX class. Likewise for [:1234:]. + +The problem in trying to be exactly like Perl is in the handling of escapes. We +have to be sure that [abc[:x\]pqr] is *not* treated as containing a POSIX +class, but [abc[:x\]pqr:]] is (so that an error can be generated). The code +below handles the special case of \], but does not try to do any other escape +processing. This makes it different from Perl for cases such as [:l\ower:] +where Perl recognizes it as the POSIX class "lower" but PCRE does not recognize +"l\ower". This is a lesser evil that not diagnosing bad classes when Perl does, +I think. + +Arguments: + ptr pointer to the initial [ + endptr where to return the end pointer + +Returns: TRUE or FALSE +*/ + +static BOOL +check_posix_syntax(const uschar *ptr, const uschar **endptr) +{ +int terminator; /* Don't combine these lines; the Solaris cc */ +terminator = *(++ptr); /* compiler warns about "non-constant" initializer. */ +for (++ptr; *ptr != 0; ptr++) + { + if (*ptr == '\\' && ptr[1] == ']') ptr++; else + { + if (*ptr == ']') return FALSE; + if (*ptr == terminator && ptr[1] == ']') + { + *endptr = ptr; + return TRUE; + } + } + } +return FALSE; +} + + + + +/************************************************* +* Check POSIX class name * +*************************************************/ + +/* This function is called to check the name given in a POSIX-style class entry +such as [:alnum:]. + +Arguments: + ptr points to the first letter + len the length of the name + +Returns: a value representing the name, or -1 if unknown +*/ + +static int +check_posix_name(const uschar *ptr, int len) +{ +const char *pn = posix_names; +register int yield = 0; +while (posix_name_lengths[yield] != 0) + { + if (len == posix_name_lengths[yield] && + strncmp((const char *)ptr, pn, len) == 0) return yield; + pn += posix_name_lengths[yield] + 1; + yield++; + } +return -1; +} + + +/************************************************* +* Adjust OP_RECURSE items in repeated group * +*************************************************/ + +/* OP_RECURSE items contain an offset from the start of the regex to the group +that is referenced. This means that groups can be replicated for fixed +repetition simply by copying (because the recursion is allowed to refer to +earlier groups that are outside the current group). However, when a group is +optional (i.e. the minimum quantifier is zero), OP_BRAZERO is inserted before +it, after it has been compiled. This means that any OP_RECURSE items within it +that refer to the group itself or any contained groups have to have their +offsets adjusted. That one of the jobs of this function. Before it is called, +the partially compiled regex must be temporarily terminated with OP_END. + +This function has been extended with the possibility of forward references for +recursions and subroutine calls. It must also check the list of such references +for the group we are dealing with. If it finds that one of the recursions in +the current group is on this list, it adjusts the offset in the list, not the +value in the reference (which is a group number). + +Arguments: + group points to the start of the group + adjust the amount by which the group is to be moved + utf8 TRUE in UTF-8 mode + cd contains pointers to tables etc. + save_hwm the hwm forward reference pointer at the start of the group + +Returns: nothing +*/ + +static void +adjust_recurse(uschar *group, int adjust, BOOL utf8, compile_data *cd, + uschar *save_hwm) +{ +uschar *ptr = group; + +while ((ptr = (uschar *)find_recurse(ptr, utf8)) != NULL) + { + int offset; + uschar *hc; + + /* See if this recursion is on the forward reference list. If so, adjust the + reference. */ + + for (hc = save_hwm; hc < cd->hwm; hc += LINK_SIZE) + { + offset = GET(hc, 0); + if (cd->start_code + offset == ptr + 1) + { + PUT(hc, 0, offset + adjust); + break; + } + } + + /* Otherwise, adjust the recursion offset if it's after the start of this + group. */ + + if (hc >= cd->hwm) + { + offset = GET(ptr, 1); + if (cd->start_code + offset >= group) PUT(ptr, 1, offset + adjust); + } + + ptr += 1 + LINK_SIZE; + } +} + + + +/************************************************* +* Insert an automatic callout point * +*************************************************/ + +/* This function is called when the PCRE_AUTO_CALLOUT option is set, to insert +callout points before each pattern item. + +Arguments: + code current code pointer + ptr current pattern pointer + cd pointers to tables etc + +Returns: new code pointer +*/ + +static uschar * +auto_callout(uschar *code, const uschar *ptr, compile_data *cd) +{ +*code++ = OP_CALLOUT; +*code++ = 255; +PUT(code, 0, ptr - cd->start_pattern); /* Pattern offset */ +PUT(code, LINK_SIZE, 0); /* Default length */ +return code + 2*LINK_SIZE; +} + + + +/************************************************* +* Complete a callout item * +*************************************************/ + +/* A callout item contains the length of the next item in the pattern, which +we can't fill in till after we have reached the relevant point. This is used +for both automatic and manual callouts. + +Arguments: + previous_callout points to previous callout item + ptr current pattern pointer + cd pointers to tables etc + +Returns: nothing +*/ + +static void +complete_callout(uschar *previous_callout, const uschar *ptr, compile_data *cd) +{ +int length = ptr - cd->start_pattern - GET(previous_callout, 2); +PUT(previous_callout, 2 + LINK_SIZE, length); +} + + + +#ifdef SUPPORT_UCP +/************************************************* +* Get othercase range * +*************************************************/ + +/* This function is passed the start and end of a class range, in UTF-8 mode +with UCP support. It searches up the characters, looking for internal ranges of +characters in the "other" case. Each call returns the next one, updating the +start address. + +Arguments: + cptr points to starting character value; updated + d end value + ocptr where to put start of othercase range + odptr where to put end of othercase range + +Yield: TRUE when range returned; FALSE when no more +*/ + +static BOOL +get_othercase_range(unsigned int *cptr, unsigned int d, unsigned int *ocptr, + unsigned int *odptr) +{ +unsigned int c, othercase, next; + +for (c = *cptr; c <= d; c++) + { if ((othercase = _pcre_ucp_othercase(c)) != NOTACHAR) break; } + +if (c > d) return FALSE; + +*ocptr = othercase; +next = othercase + 1; + +for (++c; c <= d; c++) + { + if (_pcre_ucp_othercase(c) != next) break; + next++; + } + +*odptr = next - 1; +*cptr = c; + +return TRUE; +} +#endif /* SUPPORT_UCP */ + + + +/************************************************* +* Check if auto-possessifying is possible * +*************************************************/ + +/* This function is called for unlimited repeats of certain items, to see +whether the next thing could possibly match the repeated item. If not, it makes +sense to automatically possessify the repeated item. + +Arguments: + op_code the repeated op code + this data for this item, depends on the opcode + utf8 TRUE in UTF-8 mode + utf8_char used for utf8 character bytes, NULL if not relevant + ptr next character in pattern + options options bits + cd contains pointers to tables etc. + +Returns: TRUE if possessifying is wanted +*/ + +static BOOL +check_auto_possessive(int op_code, int item, BOOL utf8, uschar *utf8_char, + const uschar *ptr, int options, compile_data *cd) +{ +int next; + +/* Skip whitespace and comments in extended mode */ + +if ((options & PCRE_EXTENDED) != 0) + { + for (;;) + { + while ((cd->ctypes[*ptr] & ctype_space) != 0) ptr++; + if (*ptr == '#') + { + while (*(++ptr) != 0) + if (IS_NEWLINE(ptr)) { ptr += cd->nllen; break; } + } + else break; + } + } + +/* If the next item is one that we can handle, get its value. A non-negative +value is a character, a negative value is an escape value. */ + +if (*ptr == '\\') + { + int temperrorcode = 0; + next = check_escape(&ptr, &temperrorcode, cd->bracount, options, FALSE); + if (temperrorcode != 0) return FALSE; + ptr++; /* Point after the escape sequence */ + } + +else if ((cd->ctypes[*ptr] & ctype_meta) == 0) + { +#ifdef SUPPORT_UTF8 + if (utf8) { GETCHARINC(next, ptr); } else +#endif + next = *ptr++; + } + +else return FALSE; + +/* Skip whitespace and comments in extended mode */ + +if ((options & PCRE_EXTENDED) != 0) + { + for (;;) + { + while ((cd->ctypes[*ptr] & ctype_space) != 0) ptr++; + if (*ptr == '#') + { + while (*(++ptr) != 0) + if (IS_NEWLINE(ptr)) { ptr += cd->nllen; break; } + } + else break; + } + } + +/* If the next thing is itself optional, we have to give up. */ + +if (*ptr == '*' || *ptr == '?' || strncmp((char *)ptr, "{0,", 3) == 0) + return FALSE; + +/* Now compare the next item with the previous opcode. If the previous is a +positive single character match, "item" either contains the character or, if +"item" is greater than 127 in utf8 mode, the character's bytes are in +utf8_char. */ + + +/* Handle cases when the next item is a character. */ + +if (next >= 0) switch(op_code) + { + case OP_CHAR: +#ifdef SUPPORT_UTF8 + if (utf8 && item > 127) { GETCHAR(item, utf8_char); } +#endif + return item != next; + + /* For CHARNC (caseless character) we must check the other case. If we have + Unicode property support, we can use it to test the other case of + high-valued characters. */ + + case OP_CHARNC: +#ifdef SUPPORT_UTF8 + if (utf8 && item > 127) { GETCHAR(item, utf8_char); } +#endif + if (item == next) return FALSE; +#ifdef SUPPORT_UTF8 + if (utf8) + { + unsigned int othercase; + if (next < 128) othercase = cd->fcc[next]; else +#ifdef SUPPORT_UCP + othercase = _pcre_ucp_othercase((unsigned int)next); +#else + othercase = NOTACHAR; +#endif + return (unsigned int)item != othercase; + } + else +#endif /* SUPPORT_UTF8 */ + return (item != cd->fcc[next]); /* Non-UTF-8 mode */ + + /* For OP_NOT, "item" must be a single-byte character. */ + + case OP_NOT: + if (next < 0) return FALSE; /* Not a character */ + if (item == next) return TRUE; + if ((options & PCRE_CASELESS) == 0) return FALSE; +#ifdef SUPPORT_UTF8 + if (utf8) + { + unsigned int othercase; + if (next < 128) othercase = cd->fcc[next]; else +#ifdef SUPPORT_UCP + othercase = _pcre_ucp_othercase(next); +#else + othercase = NOTACHAR; +#endif + return (unsigned int)item == othercase; + } + else +#endif /* SUPPORT_UTF8 */ + return (item == cd->fcc[next]); /* Non-UTF-8 mode */ + + case OP_DIGIT: + return next > 127 || (cd->ctypes[next] & ctype_digit) == 0; + + case OP_NOT_DIGIT: + return next <= 127 && (cd->ctypes[next] & ctype_digit) != 0; + + case OP_WHITESPACE: + return next > 127 || (cd->ctypes[next] & ctype_space) == 0; + + case OP_NOT_WHITESPACE: + return next <= 127 && (cd->ctypes[next] & ctype_space) != 0; + + case OP_WORDCHAR: + return next > 127 || (cd->ctypes[next] & ctype_word) == 0; + + case OP_NOT_WORDCHAR: + return next <= 127 && (cd->ctypes[next] & ctype_word) != 0; + + case OP_HSPACE: + case OP_NOT_HSPACE: + switch(next) + { + case 0x09: + case 0x20: + case 0xa0: + case 0x1680: + case 0x180e: + case 0x2000: + case 0x2001: + case 0x2002: + case 0x2003: + case 0x2004: + case 0x2005: + case 0x2006: + case 0x2007: + case 0x2008: + case 0x2009: + case 0x200A: + case 0x202f: + case 0x205f: + case 0x3000: + return op_code != OP_HSPACE; + default: + return op_code == OP_HSPACE; + } + + case OP_VSPACE: + case OP_NOT_VSPACE: + switch(next) + { + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x85: + case 0x2028: + case 0x2029: + return op_code != OP_VSPACE; + default: + return op_code == OP_VSPACE; + } + + default: + return FALSE; + } + + +/* Handle the case when the next item is \d, \s, etc. */ + +switch(op_code) + { + case OP_CHAR: + case OP_CHARNC: +#ifdef SUPPORT_UTF8 + if (utf8 && item > 127) { GETCHAR(item, utf8_char); } +#endif + switch(-next) + { + case ESC_d: + return item > 127 || (cd->ctypes[item] & ctype_digit) == 0; + + case ESC_D: + return item <= 127 && (cd->ctypes[item] & ctype_digit) != 0; + + case ESC_s: + return item > 127 || (cd->ctypes[item] & ctype_space) == 0; + + case ESC_S: + return item <= 127 && (cd->ctypes[item] & ctype_space) != 0; + + case ESC_w: + return item > 127 || (cd->ctypes[item] & ctype_word) == 0; + + case ESC_W: + return item <= 127 && (cd->ctypes[item] & ctype_word) != 0; + + case ESC_h: + case ESC_H: + switch(item) + { + case 0x09: + case 0x20: + case 0xa0: + case 0x1680: + case 0x180e: + case 0x2000: + case 0x2001: + case 0x2002: + case 0x2003: + case 0x2004: + case 0x2005: + case 0x2006: + case 0x2007: + case 0x2008: + case 0x2009: + case 0x200A: + case 0x202f: + case 0x205f: + case 0x3000: + return -next != ESC_h; + default: + return -next == ESC_h; + } + + case ESC_v: + case ESC_V: + switch(item) + { + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x85: + case 0x2028: + case 0x2029: + return -next != ESC_v; + default: + return -next == ESC_v; + } + + default: + return FALSE; + } + + case OP_DIGIT: + return next == -ESC_D || next == -ESC_s || next == -ESC_W || + next == -ESC_h || next == -ESC_v; + + case OP_NOT_DIGIT: + return next == -ESC_d; + + case OP_WHITESPACE: + return next == -ESC_S || next == -ESC_d || next == -ESC_w; + + case OP_NOT_WHITESPACE: + return next == -ESC_s || next == -ESC_h || next == -ESC_v; + + case OP_HSPACE: + return next == -ESC_S || next == -ESC_H || next == -ESC_d || next == -ESC_w; + + case OP_NOT_HSPACE: + return next == -ESC_h; + + /* Can't have \S in here because VT matches \S (Perl anomaly) */ + case OP_VSPACE: + return next == -ESC_V || next == -ESC_d || next == -ESC_w; + + case OP_NOT_VSPACE: + return next == -ESC_v; + + case OP_WORDCHAR: + return next == -ESC_W || next == -ESC_s || next == -ESC_h || next == -ESC_v; + + case OP_NOT_WORDCHAR: + return next == -ESC_w || next == -ESC_d; + + default: + return FALSE; + } + +/* Control does not reach here */ +} + + + +/************************************************* +* Compile one branch * +*************************************************/ + +/* Scan the pattern, compiling it into the a vector. If the options are +changed during the branch, the pointer is used to change the external options +bits. This function is used during the pre-compile phase when we are trying +to find out the amount of memory needed, as well as during the real compile +phase. The value of lengthptr distinguishes the two phases. + +Arguments: + optionsptr pointer to the option bits + codeptr points to the pointer to the current code point + ptrptr points to the current pattern pointer + errorcodeptr points to error code variable + firstbyteptr set to initial literal character, or < 0 (REQ_UNSET, REQ_NONE) + reqbyteptr set to the last literal character required, else < 0 + bcptr points to current branch chain + cd contains pointers to tables etc. + lengthptr NULL during the real compile phase + points to length accumulator during pre-compile phase + +Returns: TRUE on success + FALSE, with *errorcodeptr set non-zero on error +*/ + +static BOOL +compile_branch(int *optionsptr, uschar **codeptr, const uschar **ptrptr, + int *errorcodeptr, int *firstbyteptr, int *reqbyteptr, branch_chain *bcptr, + compile_data *cd, int *lengthptr) +{ +int repeat_type, op_type; +int repeat_min = 0, repeat_max = 0; /* To please picky compilers */ +int bravalue = 0; +int greedy_default, greedy_non_default; +int firstbyte, reqbyte; +int zeroreqbyte, zerofirstbyte; +int req_caseopt, reqvary, tempreqvary; +int options = *optionsptr; +int after_manual_callout = 0; +int length_prevgroup = 0; +register int c; +register uschar *code = *codeptr; +uschar *last_code = code; +uschar *orig_code = code; +uschar *tempcode; +BOOL inescq = FALSE; +BOOL groupsetfirstbyte = FALSE; +const uschar *ptr = *ptrptr; +const uschar *tempptr; +uschar *previous = NULL; +uschar *previous_callout = NULL; +uschar *save_hwm = NULL; +uschar classbits[32]; + +#ifdef SUPPORT_UTF8 +BOOL class_utf8; +BOOL utf8 = (options & PCRE_UTF8) != 0; +uschar *class_utf8data; +uschar *class_utf8data_base; +uschar utf8_char[6]; +#else +BOOL utf8 = FALSE; +uschar *utf8_char = NULL; +#endif + +#ifdef DEBUG +if (lengthptr != NULL) DPRINTF((">> start branch\n")); +#endif + +/* Set up the default and non-default settings for greediness */ + +greedy_default = ((options & PCRE_UNGREEDY) != 0); +greedy_non_default = greedy_default ^ 1; + +/* Initialize no first byte, no required byte. REQ_UNSET means "no char +matching encountered yet". It gets changed to REQ_NONE if we hit something that +matches a non-fixed char first char; reqbyte just remains unset if we never +find one. + +When we hit a repeat whose minimum is zero, we may have to adjust these values +to take the zero repeat into account. This is implemented by setting them to +zerofirstbyte and zeroreqbyte when such a repeat is encountered. The individual +item types that can be repeated set these backoff variables appropriately. */ + +firstbyte = reqbyte = zerofirstbyte = zeroreqbyte = REQ_UNSET; + +/* The variable req_caseopt contains either the REQ_CASELESS value or zero, +according to the current setting of the caseless flag. REQ_CASELESS is a bit +value > 255. It is added into the firstbyte or reqbyte variables to record the +case status of the value. This is used only for ASCII characters. */ + +req_caseopt = ((options & PCRE_CASELESS) != 0)? REQ_CASELESS : 0; + +/* Switch on next character until the end of the branch */ + +for (;; ptr++) + { + BOOL negate_class; + BOOL should_flip_negation; + BOOL possessive_quantifier; + BOOL is_quantifier; + BOOL is_recurse; + BOOL reset_bracount; + int class_charcount; + int class_lastchar; + int newoptions; + int recno; + int refsign; + int skipbytes; + int subreqbyte; + int subfirstbyte; + int terminator; + int mclength; + uschar mcbuffer[8]; + + /* Get next byte in the pattern */ + + c = *ptr; + + /* If we are in the pre-compile phase, accumulate the length used for the + previous cycle of this loop. */ + + if (lengthptr != NULL) + { +#ifdef DEBUG + if (code > cd->hwm) cd->hwm = code; /* High water info */ +#endif + if (code > cd->start_workspace + COMPILE_WORK_SIZE) /* Check for overrun */ + { + *errorcodeptr = ERR52; + goto FAILED; + } + + /* There is at least one situation where code goes backwards: this is the + case of a zero quantifier after a class (e.g. [ab]{0}). At compile time, + the class is simply eliminated. However, it is created first, so we have to + allow memory for it. Therefore, don't ever reduce the length at this point. + */ + + if (code < last_code) code = last_code; + + /* Paranoid check for integer overflow */ + + if (OFLOW_MAX - *lengthptr < code - last_code) + { + *errorcodeptr = ERR20; + goto FAILED; + } + + *lengthptr += code - last_code; + DPRINTF(("length=%d added %d c=%c\n", *lengthptr, code - last_code, c)); + + /* If "previous" is set and it is not at the start of the work space, move + it back to there, in order to avoid filling up the work space. Otherwise, + if "previous" is NULL, reset the current code pointer to the start. */ + + if (previous != NULL) + { + if (previous > orig_code) + { + memmove(orig_code, previous, code - previous); + code -= previous - orig_code; + previous = orig_code; + } + } + else code = orig_code; + + /* Remember where this code item starts so we can pick up the length + next time round. */ + + last_code = code; + } + + /* In the real compile phase, just check the workspace used by the forward + reference list. */ + + else if (cd->hwm > cd->start_workspace + COMPILE_WORK_SIZE) + { + *errorcodeptr = ERR52; + goto FAILED; + } + + /* If in \Q...\E, check for the end; if not, we have a literal */ + + if (inescq && c != 0) + { + if (c == '\\' && ptr[1] == 'E') + { + inescq = FALSE; + ptr++; + continue; + } + else + { + if (previous_callout != NULL) + { + if (lengthptr == NULL) /* Don't attempt in pre-compile phase */ + complete_callout(previous_callout, ptr, cd); + previous_callout = NULL; + } + if ((options & PCRE_AUTO_CALLOUT) != 0) + { + previous_callout = code; + code = auto_callout(code, ptr, cd); + } + goto NORMAL_CHAR; + } + } + + /* Fill in length of a previous callout, except when the next thing is + a quantifier. */ + + is_quantifier = c == '*' || c == '+' || c == '?' || + (c == '{' && is_counted_repeat(ptr+1)); + + if (!is_quantifier && previous_callout != NULL && + after_manual_callout-- <= 0) + { + if (lengthptr == NULL) /* Don't attempt in pre-compile phase */ + complete_callout(previous_callout, ptr, cd); + previous_callout = NULL; + } + + /* In extended mode, skip white space and comments */ + + if ((options & PCRE_EXTENDED) != 0) + { + if ((cd->ctypes[c] & ctype_space) != 0) continue; + if (c == '#') + { + while (*(++ptr) != 0) + { + if (IS_NEWLINE(ptr)) { ptr += cd->nllen - 1; break; } + } + if (*ptr != 0) continue; + + /* Else fall through to handle end of string */ + c = 0; + } + } + + /* No auto callout for quantifiers. */ + + if ((options & PCRE_AUTO_CALLOUT) != 0 && !is_quantifier) + { + previous_callout = code; + code = auto_callout(code, ptr, cd); + } + + switch(c) + { + /* ===================================================================*/ + case 0: /* The branch terminates at string end */ + case '|': /* or | or ) */ + case ')': + *firstbyteptr = firstbyte; + *reqbyteptr = reqbyte; + *codeptr = code; + *ptrptr = ptr; + if (lengthptr != NULL) + { + if (OFLOW_MAX - *lengthptr < code - last_code) + { + *errorcodeptr = ERR20; + goto FAILED; + } + *lengthptr += code - last_code; /* To include callout length */ + DPRINTF((">> end branch\n")); + } + return TRUE; + + + /* ===================================================================*/ + /* Handle single-character metacharacters. In multiline mode, ^ disables + the setting of any following char as a first character. */ + + case '^': + if ((options & PCRE_MULTILINE) != 0) + { + if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE; + } + previous = NULL; + *code++ = OP_CIRC; + break; + + case '$': + previous = NULL; + *code++ = OP_DOLL; + break; + + /* There can never be a first char if '.' is first, whatever happens about + repeats. The value of reqbyte doesn't change either. */ + + case '.': + if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE; + zerofirstbyte = firstbyte; + zeroreqbyte = reqbyte; + previous = code; + *code++ = OP_ANY; + break; + + + /* ===================================================================*/ + /* Character classes. If the included characters are all < 256, we build a + 32-byte bitmap of the permitted characters, except in the special case + where there is only one such character. For negated classes, we build the + map as usual, then invert it at the end. However, we use a different opcode + so that data characters > 255 can be handled correctly. + + If the class contains characters outside the 0-255 range, a different + opcode is compiled. It may optionally have a bit map for characters < 256, + but those above are are explicitly listed afterwards. A flag byte tells + whether the bitmap is present, and whether this is a negated class or not. + */ + + case '[': + previous = code; + + /* PCRE supports POSIX class stuff inside a class. Perl gives an error if + they are encountered at the top level, so we'll do that too. */ + + if ((ptr[1] == ':' || ptr[1] == '.' || ptr[1] == '=') && + check_posix_syntax(ptr, &tempptr)) + { + *errorcodeptr = (ptr[1] == ':')? ERR13 : ERR31; + goto FAILED; + } + + /* If the first character is '^', set the negation flag and skip it. Also, + if the first few characters (either before or after ^) are \Q\E or \E we + skip them too. This makes for compatibility with Perl. */ + + negate_class = FALSE; + for (;;) + { + c = *(++ptr); + if (c == '\\') + { + if (ptr[1] == 'E') ptr++; + else if (strncmp((const char *)ptr+1, "Q\\E", 3) == 0) ptr += 3; + else break; + } + else if (!negate_class && c == '^') + negate_class = TRUE; + else break; + } + + /* If a class contains a negative special such as \S, we need to flip the + negation flag at the end, so that support for characters > 255 works + correctly (they are all included in the class). */ + + should_flip_negation = FALSE; + + /* Keep a count of chars with values < 256 so that we can optimize the case + of just a single character (as long as it's < 256). However, For higher + valued UTF-8 characters, we don't yet do any optimization. */ + + class_charcount = 0; + class_lastchar = -1; + + /* Initialize the 32-char bit map to all zeros. We build the map in a + temporary bit of memory, in case the class contains only 1 character (less + than 256), because in that case the compiled code doesn't use the bit map. + */ + + memset(classbits, 0, 32 * sizeof(uschar)); + +#ifdef SUPPORT_UTF8 + class_utf8 = FALSE; /* No chars >= 256 */ + class_utf8data = code + LINK_SIZE + 2; /* For UTF-8 items */ + class_utf8data_base = class_utf8data; /* For resetting in pass 1 */ +#endif + + /* Process characters until ] is reached. By writing this as a "do" it + means that an initial ] is taken as a data character. At the start of the + loop, c contains the first byte of the character. */ + + if (c != 0) do + { + const uschar *oldptr; + +#ifdef SUPPORT_UTF8 + if (utf8 && c > 127) + { /* Braces are required because the */ + GETCHARLEN(c, ptr, ptr); /* macro generates multiple statements */ + } + + /* In the pre-compile phase, accumulate the length of any UTF-8 extra + data and reset the pointer. This is so that very large classes that + contain a zillion UTF-8 characters no longer overwrite the work space + (which is on the stack). */ + + if (lengthptr != NULL) + { + *lengthptr += class_utf8data - class_utf8data_base; + class_utf8data = class_utf8data_base; + } + +#endif + + /* Inside \Q...\E everything is literal except \E */ + + if (inescq) + { + if (c == '\\' && ptr[1] == 'E') /* If we are at \E */ + { + inescq = FALSE; /* Reset literal state */ + ptr++; /* Skip the 'E' */ + continue; /* Carry on with next */ + } + goto CHECK_RANGE; /* Could be range if \E follows */ + } + + /* Handle POSIX class names. Perl allows a negation extension of the + form [:^name:]. A square bracket that doesn't match the syntax is + treated as a literal. We also recognize the POSIX constructions + [.ch.] and [=ch=] ("collating elements") and fault them, as Perl + 5.6 and 5.8 do. */ + + if (c == '[' && + (ptr[1] == ':' || ptr[1] == '.' || ptr[1] == '=') && + check_posix_syntax(ptr, &tempptr)) + { + BOOL local_negate = FALSE; + int posix_class, taboffset, tabopt; + register const uschar *cbits = cd->cbits; + uschar pbits[32]; + + if (ptr[1] != ':') + { + *errorcodeptr = ERR31; + goto FAILED; + } + + ptr += 2; + if (*ptr == '^') + { + local_negate = TRUE; + should_flip_negation = TRUE; /* Note negative special */ + ptr++; + } + + posix_class = check_posix_name(ptr, tempptr - ptr); + if (posix_class < 0) + { + *errorcodeptr = ERR30; + goto FAILED; + } + + /* If matching is caseless, upper and lower are converted to + alpha. This relies on the fact that the class table starts with + alpha, lower, upper as the first 3 entries. */ + + if ((options & PCRE_CASELESS) != 0 && posix_class <= 2) + posix_class = 0; + + /* We build the bit map for the POSIX class in a chunk of local store + because we may be adding and subtracting from it, and we don't want to + subtract bits that may be in the main map already. At the end we or the + result into the bit map that is being built. */ + + posix_class *= 3; + + /* Copy in the first table (always present) */ + + memcpy(pbits, cbits + posix_class_maps[posix_class], + 32 * sizeof(uschar)); + + /* If there is a second table, add or remove it as required. */ + + taboffset = posix_class_maps[posix_class + 1]; + tabopt = posix_class_maps[posix_class + 2]; + + if (taboffset >= 0) + { + if (tabopt >= 0) + for (c = 0; c < 32; c++) pbits[c] |= cbits[c + taboffset]; + else + for (c = 0; c < 32; c++) pbits[c] &= ~cbits[c + taboffset]; + } + + /* Not see if we need to remove any special characters. An option + value of 1 removes vertical space and 2 removes underscore. */ + + if (tabopt < 0) tabopt = -tabopt; + if (tabopt == 1) pbits[1] &= ~0x3c; + else if (tabopt == 2) pbits[11] &= 0x7f; + + /* Add the POSIX table or its complement into the main table that is + being built and we are done. */ + + if (local_negate) + for (c = 0; c < 32; c++) classbits[c] |= ~pbits[c]; + else + for (c = 0; c < 32; c++) classbits[c] |= pbits[c]; + + ptr = tempptr + 1; + class_charcount = 10; /* Set > 1; assumes more than 1 per class */ + continue; /* End of POSIX syntax handling */ + } + + /* Backslash may introduce a single character, or it may introduce one + of the specials, which just set a flag. The sequence \b is a special + case. Inside a class (and only there) it is treated as backspace. + Elsewhere it marks a word boundary. Other escapes have preset maps ready + to 'or' into the one we are building. We assume they have more than one + character in them, so set class_charcount bigger than one. */ + + if (c == '\\') + { + c = check_escape(&ptr, errorcodeptr, cd->bracount, options, TRUE); + if (*errorcodeptr != 0) goto FAILED; + + if (-c == ESC_b) c = '\b'; /* \b is backspace in a class */ + else if (-c == ESC_X) c = 'X'; /* \X is literal X in a class */ + else if (-c == ESC_R) c = 'R'; /* \R is literal R in a class */ + else if (-c == ESC_Q) /* Handle start of quoted string */ + { + if (ptr[1] == '\\' && ptr[2] == 'E') + { + ptr += 2; /* avoid empty string */ + } + else inescq = TRUE; + continue; + } + else if (-c == ESC_E) continue; /* Ignore orphan \E */ + + if (c < 0) + { + register const uschar *cbits = cd->cbits; + class_charcount += 2; /* Greater than 1 is what matters */ + + /* Save time by not doing this in the pre-compile phase. */ + + if (lengthptr == NULL) switch (-c) + { + case ESC_d: + for (c = 0; c < 32; c++) classbits[c] |= cbits[c+cbit_digit]; + continue; + + case ESC_D: + should_flip_negation = TRUE; + for (c = 0; c < 32; c++) classbits[c] |= ~cbits[c+cbit_digit]; + continue; + + case ESC_w: + for (c = 0; c < 32; c++) classbits[c] |= cbits[c+cbit_word]; + continue; + + case ESC_W: + should_flip_negation = TRUE; + for (c = 0; c < 32; c++) classbits[c] |= ~cbits[c+cbit_word]; + continue; + + case ESC_s: + for (c = 0; c < 32; c++) classbits[c] |= cbits[c+cbit_space]; + classbits[1] &= ~0x08; /* Perl 5.004 onwards omits VT from \s */ + continue; + + case ESC_S: + should_flip_negation = TRUE; + for (c = 0; c < 32; c++) classbits[c] |= ~cbits[c+cbit_space]; + classbits[1] |= 0x08; /* Perl 5.004 onwards omits VT from \s */ + continue; + + default: /* Not recognized; fall through */ + break; /* Need "default" setting to stop compiler warning. */ + } + + /* In the pre-compile phase, just do the recognition. */ + + else if (c == -ESC_d || c == -ESC_D || c == -ESC_w || + c == -ESC_W || c == -ESC_s || c == -ESC_S) continue; + + /* We need to deal with \H, \h, \V, and \v in both phases because + they use extra memory. */ + + if (-c == ESC_h) + { + SETBIT(classbits, 0x09); /* VT */ + SETBIT(classbits, 0x20); /* SPACE */ + SETBIT(classbits, 0xa0); /* NSBP */ +#ifdef SUPPORT_UTF8 + if (utf8) + { + class_utf8 = TRUE; + *class_utf8data++ = XCL_SINGLE; + class_utf8data += _pcre_ord2utf8(0x1680, class_utf8data); + *class_utf8data++ = XCL_SINGLE; + class_utf8data += _pcre_ord2utf8(0x180e, class_utf8data); + *class_utf8data++ = XCL_RANGE; + class_utf8data += _pcre_ord2utf8(0x2000, class_utf8data); + class_utf8data += _pcre_ord2utf8(0x200A, class_utf8data); + *class_utf8data++ = XCL_SINGLE; + class_utf8data += _pcre_ord2utf8(0x202f, class_utf8data); + *class_utf8data++ = XCL_SINGLE; + class_utf8data += _pcre_ord2utf8(0x205f, class_utf8data); + *class_utf8data++ = XCL_SINGLE; + class_utf8data += _pcre_ord2utf8(0x3000, class_utf8data); + } +#endif + continue; + } + + if (-c == ESC_H) + { + for (c = 0; c < 32; c++) + { + int x = 0xff; + switch (c) + { + case 0x09/8: x ^= 1 << (0x09%8); break; + case 0x20/8: x ^= 1 << (0x20%8); break; + case 0xa0/8: x ^= 1 << (0xa0%8); break; + default: break; + } + classbits[c] |= x; + } + +#ifdef SUPPORT_UTF8 + if (utf8) + { + class_utf8 = TRUE; + *class_utf8data++ = XCL_RANGE; + class_utf8data += _pcre_ord2utf8(0x0100, class_utf8data); + class_utf8data += _pcre_ord2utf8(0x167f, class_utf8data); + *class_utf8data++ = XCL_RANGE; + class_utf8data += _pcre_ord2utf8(0x1681, class_utf8data); + class_utf8data += _pcre_ord2utf8(0x180d, class_utf8data); + *class_utf8data++ = XCL_RANGE; + class_utf8data += _pcre_ord2utf8(0x180f, class_utf8data); + class_utf8data += _pcre_ord2utf8(0x1fff, class_utf8data); + *class_utf8data++ = XCL_RANGE; + class_utf8data += _pcre_ord2utf8(0x200B, class_utf8data); + class_utf8data += _pcre_ord2utf8(0x202e, class_utf8data); + *class_utf8data++ = XCL_RANGE; + class_utf8data += _pcre_ord2utf8(0x2030, class_utf8data); + class_utf8data += _pcre_ord2utf8(0x205e, class_utf8data); + *class_utf8data++ = XCL_RANGE; + class_utf8data += _pcre_ord2utf8(0x2060, class_utf8data); + class_utf8data += _pcre_ord2utf8(0x2fff, class_utf8data); + *class_utf8data++ = XCL_RANGE; + class_utf8data += _pcre_ord2utf8(0x3001, class_utf8data); + class_utf8data += _pcre_ord2utf8(0x7fffffff, class_utf8data); + } +#endif + continue; + } + + if (-c == ESC_v) + { + SETBIT(classbits, 0x0a); /* LF */ + SETBIT(classbits, 0x0b); /* VT */ + SETBIT(classbits, 0x0c); /* FF */ + SETBIT(classbits, 0x0d); /* CR */ + SETBIT(classbits, 0x85); /* NEL */ +#ifdef SUPPORT_UTF8 + if (utf8) + { + class_utf8 = TRUE; + *class_utf8data++ = XCL_RANGE; + class_utf8data += _pcre_ord2utf8(0x2028, class_utf8data); + class_utf8data += _pcre_ord2utf8(0x2029, class_utf8data); + } +#endif + continue; + } + + if (-c == ESC_V) + { + for (c = 0; c < 32; c++) + { + int x = 0xff; + switch (c) + { + case 0x0a/8: x ^= 1 << (0x0a%8); + x ^= 1 << (0x0b%8); + x ^= 1 << (0x0c%8); + x ^= 1 << (0x0d%8); + break; + case 0x85/8: x ^= 1 << (0x85%8); break; + default: break; + } + classbits[c] |= x; + } + +#ifdef SUPPORT_UTF8 + if (utf8) + { + class_utf8 = TRUE; + *class_utf8data++ = XCL_RANGE; + class_utf8data += _pcre_ord2utf8(0x0100, class_utf8data); + class_utf8data += _pcre_ord2utf8(0x2027, class_utf8data); + *class_utf8data++ = XCL_RANGE; + class_utf8data += _pcre_ord2utf8(0x2029, class_utf8data); + class_utf8data += _pcre_ord2utf8(0x7fffffff, class_utf8data); + } +#endif + continue; + } + + /* We need to deal with \P and \p in both phases. */ + +#ifdef SUPPORT_UCP + if (-c == ESC_p || -c == ESC_P) + { + BOOL negated; + int pdata; + int ptype = get_ucp(&ptr, &negated, &pdata, errorcodeptr); + if (ptype < 0) goto FAILED; + class_utf8 = TRUE; + *class_utf8data++ = ((-c == ESC_p) != negated)? + XCL_PROP : XCL_NOTPROP; + *class_utf8data++ = ptype; + *class_utf8data++ = pdata; + class_charcount -= 2; /* Not a < 256 character */ + continue; + } +#endif + /* Unrecognized escapes are faulted if PCRE is running in its + strict mode. By default, for compatibility with Perl, they are + treated as literals. */ + + if ((options & PCRE_EXTRA) != 0) + { + *errorcodeptr = ERR7; + goto FAILED; + } + + class_charcount -= 2; /* Undo the default count from above */ + c = *ptr; /* Get the final character and fall through */ + } + + /* Fall through if we have a single character (c >= 0). This may be + greater than 256 in UTF-8 mode. */ + + } /* End of backslash handling */ + + /* A single character may be followed by '-' to form a range. However, + Perl does not permit ']' to be the end of the range. A '-' character + at the end is treated as a literal. Perl ignores orphaned \E sequences + entirely. The code for handling \Q and \E is messy. */ + + CHECK_RANGE: + while (ptr[1] == '\\' && ptr[2] == 'E') + { + inescq = FALSE; + ptr += 2; + } + + oldptr = ptr; + + /* Remember \r or \n */ + + if (c == '\r' || c == '\n') cd->external_flags |= PCRE_HASCRORLF; + + /* Check for range */ + + if (!inescq && ptr[1] == '-') + { + int d; + ptr += 2; + while (*ptr == '\\' && ptr[1] == 'E') ptr += 2; + + /* If we hit \Q (not followed by \E) at this point, go into escaped + mode. */ + + while (*ptr == '\\' && ptr[1] == 'Q') + { + ptr += 2; + if (*ptr == '\\' && ptr[1] == 'E') { ptr += 2; continue; } + inescq = TRUE; + break; + } + + if (*ptr == 0 || (!inescq && *ptr == ']')) + { + ptr = oldptr; + goto LONE_SINGLE_CHARACTER; + } + +#ifdef SUPPORT_UTF8 + if (utf8) + { /* Braces are required because the */ + GETCHARLEN(d, ptr, ptr); /* macro generates multiple statements */ + } + else +#endif + d = *ptr; /* Not UTF-8 mode */ + + /* The second part of a range can be a single-character escape, but + not any of the other escapes. Perl 5.6 treats a hyphen as a literal + in such circumstances. */ + + if (!inescq && d == '\\') + { + d = check_escape(&ptr, errorcodeptr, cd->bracount, options, TRUE); + if (*errorcodeptr != 0) goto FAILED; + + /* \b is backspace; \X is literal X; \R is literal R; any other + special means the '-' was literal */ + + if (d < 0) + { + if (d == -ESC_b) d = '\b'; + else if (d == -ESC_X) d = 'X'; + else if (d == -ESC_R) d = 'R'; else + { + ptr = oldptr; + goto LONE_SINGLE_CHARACTER; /* A few lines below */ + } + } + } + + /* Check that the two values are in the correct order. Optimize + one-character ranges */ + + if (d < c) + { + *errorcodeptr = ERR8; + goto FAILED; + } + + if (d == c) goto LONE_SINGLE_CHARACTER; /* A few lines below */ + + /* Remember \r or \n */ + + if (d == '\r' || d == '\n') cd->external_flags |= PCRE_HASCRORLF; + + /* In UTF-8 mode, if the upper limit is > 255, or > 127 for caseless + matching, we have to use an XCLASS with extra data items. Caseless + matching for characters > 127 is available only if UCP support is + available. */ + +#ifdef SUPPORT_UTF8 + if (utf8 && (d > 255 || ((options & PCRE_CASELESS) != 0 && d > 127))) + { + class_utf8 = TRUE; + + /* With UCP support, we can find the other case equivalents of + the relevant characters. There may be several ranges. Optimize how + they fit with the basic range. */ + +#ifdef SUPPORT_UCP + if ((options & PCRE_CASELESS) != 0) + { + unsigned int occ, ocd; + unsigned int cc = c; + unsigned int origd = d; + while (get_othercase_range(&cc, origd, &occ, &ocd)) + { + if (occ >= (unsigned int)c && + ocd <= (unsigned int)d) + continue; /* Skip embedded ranges */ + + if (occ < (unsigned int)c && + ocd >= (unsigned int)c - 1) /* Extend the basic range */ + { /* if there is overlap, */ + c = occ; /* noting that if occ < c */ + continue; /* we can't have ocd > d */ + } /* because a subrange is */ + if (ocd > (unsigned int)d && + occ <= (unsigned int)d + 1) /* always shorter than */ + { /* the basic range. */ + d = ocd; + continue; + } + + if (occ == ocd) + { + *class_utf8data++ = XCL_SINGLE; + } + else + { + *class_utf8data++ = XCL_RANGE; + class_utf8data += _pcre_ord2utf8(occ, class_utf8data); + } + class_utf8data += _pcre_ord2utf8(ocd, class_utf8data); + } + } +#endif /* SUPPORT_UCP */ + + /* Now record the original range, possibly modified for UCP caseless + overlapping ranges. */ + + *class_utf8data++ = XCL_RANGE; + class_utf8data += _pcre_ord2utf8(c, class_utf8data); + class_utf8data += _pcre_ord2utf8(d, class_utf8data); + + /* With UCP support, we are done. Without UCP support, there is no + caseless matching for UTF-8 characters > 127; we can use the bit map + for the smaller ones. */ + +#ifdef SUPPORT_UCP + continue; /* With next character in the class */ +#else + if ((options & PCRE_CASELESS) == 0 || c > 127) continue; + + /* Adjust upper limit and fall through to set up the map */ + + d = 127; + +#endif /* SUPPORT_UCP */ + } +#endif /* SUPPORT_UTF8 */ + + /* We use the bit map for all cases when not in UTF-8 mode; else + ranges that lie entirely within 0-127 when there is UCP support; else + for partial ranges without UCP support. */ + + class_charcount += d - c + 1; + class_lastchar = d; + + /* We can save a bit of time by skipping this in the pre-compile. */ + + if (lengthptr == NULL) for (; c <= d; c++) + { + classbits[c/8] |= (1 << (c&7)); + if ((options & PCRE_CASELESS) != 0) + { + int uc = cd->fcc[c]; /* flip case */ + classbits[uc/8] |= (1 << (uc&7)); + } + } + + continue; /* Go get the next char in the class */ + } + + /* Handle a lone single character - we can get here for a normal + non-escape char, or after \ that introduces a single character or for an + apparent range that isn't. */ + + LONE_SINGLE_CHARACTER: + + /* Handle a character that cannot go in the bit map */ + +#ifdef SUPPORT_UTF8 + if (utf8 && (c > 255 || ((options & PCRE_CASELESS) != 0 && c > 127))) + { + class_utf8 = TRUE; + *class_utf8data++ = XCL_SINGLE; + class_utf8data += _pcre_ord2utf8(c, class_utf8data); + +#ifdef SUPPORT_UCP + if ((options & PCRE_CASELESS) != 0) + { + unsigned int othercase; + if ((othercase = _pcre_ucp_othercase(c)) != NOTACHAR) + { + *class_utf8data++ = XCL_SINGLE; + class_utf8data += _pcre_ord2utf8(othercase, class_utf8data); + } + } +#endif /* SUPPORT_UCP */ + + } + else +#endif /* SUPPORT_UTF8 */ + + /* Handle a single-byte character */ + { + classbits[c/8] |= (1 << (c&7)); + if ((options & PCRE_CASELESS) != 0) + { + c = cd->fcc[c]; /* flip case */ + classbits[c/8] |= (1 << (c&7)); + } + class_charcount++; + class_lastchar = c; + } + } + + /* Loop until ']' reached. This "while" is the end of the "do" above. */ + + while ((c = *(++ptr)) != 0 && (c != ']' || inescq)); + + if (c == 0) /* Missing terminating ']' */ + { + *errorcodeptr = ERR6; + goto FAILED; + } + + +/* This code has been disabled because it would mean that \s counts as +an explicit \r or \n reference, and that's not really what is wanted. Now +we set the flag only if there is a literal "\r" or "\n" in the class. */ + +#if 0 + /* Remember whether \r or \n are in this class */ + + if (negate_class) + { + if ((classbits[1] & 0x24) != 0x24) cd->external_flags |= PCRE_HASCRORLF; + } + else + { + if ((classbits[1] & 0x24) != 0) cd->external_flags |= PCRE_HASCRORLF; + } +#endif + + + /* If class_charcount is 1, we saw precisely one character whose value is + less than 256. As long as there were no characters >= 128 and there was no + use of \p or \P, in other words, no use of any XCLASS features, we can + optimize. + + In UTF-8 mode, we can optimize the negative case only if there were no + characters >= 128 because OP_NOT and the related opcodes like OP_NOTSTAR + operate on single-bytes only. This is an historical hangover. Maybe one day + we can tidy these opcodes to handle multi-byte characters. + + The optimization throws away the bit map. We turn the item into a + 1-character OP_CHAR[NC] if it's positive, or OP_NOT if it's negative. Note + that OP_NOT does not support multibyte characters. In the positive case, it + can cause firstbyte to be set. Otherwise, there can be no first char if + this item is first, whatever repeat count may follow. In the case of + reqbyte, save the previous value for reinstating. */ + +#ifdef SUPPORT_UTF8 + if (class_charcount == 1 && !class_utf8 && + (!utf8 || !negate_class || class_lastchar < 128)) +#else + if (class_charcount == 1) +#endif + { + zeroreqbyte = reqbyte; + + /* The OP_NOT opcode works on one-byte characters only. */ + + if (negate_class) + { + if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE; + zerofirstbyte = firstbyte; + *code++ = OP_NOT; + *code++ = class_lastchar; + break; + } + + /* For a single, positive character, get the value into mcbuffer, and + then we can handle this with the normal one-character code. */ + +#ifdef SUPPORT_UTF8 + if (utf8 && class_lastchar > 127) + mclength = _pcre_ord2utf8(class_lastchar, mcbuffer); + else +#endif + { + mcbuffer[0] = class_lastchar; + mclength = 1; + } + goto ONE_CHAR; + } /* End of 1-char optimization */ + + /* The general case - not the one-char optimization. If this is the first + thing in the branch, there can be no first char setting, whatever the + repeat count. Any reqbyte setting must remain unchanged after any kind of + repeat. */ + + if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE; + zerofirstbyte = firstbyte; + zeroreqbyte = reqbyte; + + /* If there are characters with values > 255, we have to compile an + extended class, with its own opcode, unless there was a negated special + such as \S in the class, because in that case all characters > 255 are in + the class, so any that were explicitly given as well can be ignored. If + (when there are explicit characters > 255 that must be listed) there are no + characters < 256, we can omit the bitmap in the actual compiled code. */ + +#ifdef SUPPORT_UTF8 + if (class_utf8 && !should_flip_negation) + { + *class_utf8data++ = XCL_END; /* Marks the end of extra data */ + *code++ = OP_XCLASS; + code += LINK_SIZE; + *code = negate_class? XCL_NOT : 0; + + /* If the map is required, move up the extra data to make room for it; + otherwise just move the code pointer to the end of the extra data. */ + + if (class_charcount > 0) + { + *code++ |= XCL_MAP; + memmove(code + 32, code, class_utf8data - code); + memcpy(code, classbits, 32); + code = class_utf8data + 32; + } + else code = class_utf8data; + + /* Now fill in the complete length of the item */ + + PUT(previous, 1, code - previous); + break; /* End of class handling */ + } +#endif + + /* If there are no characters > 255, set the opcode to OP_CLASS or + OP_NCLASS, depending on whether the whole class was negated and whether + there were negative specials such as \S in the class. Then copy the 32-byte + map into the code vector, negating it if necessary. */ + + *code++ = (negate_class == should_flip_negation) ? OP_CLASS : OP_NCLASS; + if (negate_class) + { + if (lengthptr == NULL) /* Save time in the pre-compile phase */ + for (c = 0; c < 32; c++) code[c] = ~classbits[c]; + } + else + { + memcpy(code, classbits, 32); + } + code += 32; + break; + + + /* ===================================================================*/ + /* Various kinds of repeat; '{' is not necessarily a quantifier, but this + has been tested above. */ + + case '{': + if (!is_quantifier) goto NORMAL_CHAR; + ptr = read_repeat_counts(ptr+1, &repeat_min, &repeat_max, errorcodeptr); + if (*errorcodeptr != 0) goto FAILED; + goto REPEAT; + + case '*': + repeat_min = 0; + repeat_max = -1; + goto REPEAT; + + case '+': + repeat_min = 1; + repeat_max = -1; + goto REPEAT; + + case '?': + repeat_min = 0; + repeat_max = 1; + + REPEAT: + if (previous == NULL) + { + *errorcodeptr = ERR9; + goto FAILED; + } + + if (repeat_min == 0) + { + firstbyte = zerofirstbyte; /* Adjust for zero repeat */ + reqbyte = zeroreqbyte; /* Ditto */ + } + + /* Remember whether this is a variable length repeat */ + + reqvary = (repeat_min == repeat_max)? 0 : REQ_VARY; + + op_type = 0; /* Default single-char op codes */ + possessive_quantifier = FALSE; /* Default not possessive quantifier */ + + /* Save start of previous item, in case we have to move it up to make space + for an inserted OP_ONCE for the additional '+' extension. */ + + tempcode = previous; + + /* If the next character is '+', we have a possessive quantifier. This + implies greediness, whatever the setting of the PCRE_UNGREEDY option. + If the next character is '?' this is a minimizing repeat, by default, + but if PCRE_UNGREEDY is set, it works the other way round. We change the + repeat type to the non-default. */ + + if (ptr[1] == '+') + { + repeat_type = 0; /* Force greedy */ + possessive_quantifier = TRUE; + ptr++; + } + else if (ptr[1] == '?') + { + repeat_type = greedy_non_default; + ptr++; + } + else repeat_type = greedy_default; + + /* If previous was a character match, abolish the item and generate a + repeat item instead. If a char item has a minumum of more than one, ensure + that it is set in reqbyte - it might not be if a sequence such as x{3} is + the first thing in a branch because the x will have gone into firstbyte + instead. */ + + if (*previous == OP_CHAR || *previous == OP_CHARNC) + { + /* Deal with UTF-8 characters that take up more than one byte. It's + easier to write this out separately than try to macrify it. Use c to + hold the length of the character in bytes, plus 0x80 to flag that it's a + length rather than a small character. */ + +#ifdef SUPPORT_UTF8 + if (utf8 && (code[-1] & 0x80) != 0) + { + uschar *lastchar = code - 1; + while((*lastchar & 0xc0) == 0x80) lastchar--; + c = code - lastchar; /* Length of UTF-8 character */ + memcpy(utf8_char, lastchar, c); /* Save the char */ + c |= 0x80; /* Flag c as a length */ + } + else +#endif + + /* Handle the case of a single byte - either with no UTF8 support, or + with UTF-8 disabled, or for a UTF-8 character < 128. */ + + { + c = code[-1]; + if (repeat_min > 1) reqbyte = c | req_caseopt | cd->req_varyopt; + } + + /* If the repetition is unlimited, it pays to see if the next thing on + the line is something that cannot possibly match this character. If so, + automatically possessifying this item gains some performance in the case + where the match fails. */ + + if (!possessive_quantifier && + repeat_max < 0 && + check_auto_possessive(*previous, c, utf8, utf8_char, ptr + 1, + options, cd)) + { + repeat_type = 0; /* Force greedy */ + possessive_quantifier = TRUE; + } + + goto OUTPUT_SINGLE_REPEAT; /* Code shared with single character types */ + } + + /* If previous was a single negated character ([^a] or similar), we use + one of the special opcodes, replacing it. The code is shared with single- + character repeats by setting opt_type to add a suitable offset into + repeat_type. We can also test for auto-possessification. OP_NOT is + currently used only for single-byte chars. */ + + else if (*previous == OP_NOT) + { + op_type = OP_NOTSTAR - OP_STAR; /* Use "not" opcodes */ + c = previous[1]; + if (!possessive_quantifier && + repeat_max < 0 && + check_auto_possessive(OP_NOT, c, utf8, NULL, ptr + 1, options, cd)) + { + repeat_type = 0; /* Force greedy */ + possessive_quantifier = TRUE; + } + goto OUTPUT_SINGLE_REPEAT; + } + + /* If previous was a character type match (\d or similar), abolish it and + create a suitable repeat item. The code is shared with single-character + repeats by setting op_type to add a suitable offset into repeat_type. Note + the the Unicode property types will be present only when SUPPORT_UCP is + defined, but we don't wrap the little bits of code here because it just + makes it horribly messy. */ + + else if (*previous < OP_EODN) + { + uschar *oldcode; + int prop_type, prop_value; + op_type = OP_TYPESTAR - OP_STAR; /* Use type opcodes */ + c = *previous; + + if (!possessive_quantifier && + repeat_max < 0 && + check_auto_possessive(c, 0, utf8, NULL, ptr + 1, options, cd)) + { + repeat_type = 0; /* Force greedy */ + possessive_quantifier = TRUE; + } + + OUTPUT_SINGLE_REPEAT: + if (*previous == OP_PROP || *previous == OP_NOTPROP) + { + prop_type = previous[1]; + prop_value = previous[2]; + } + else prop_type = prop_value = -1; + + oldcode = code; + code = previous; /* Usually overwrite previous item */ + + /* If the maximum is zero then the minimum must also be zero; Perl allows + this case, so we do too - by simply omitting the item altogether. */ + + if (repeat_max == 0) goto END_REPEAT; + + /* All real repeats make it impossible to handle partial matching (maybe + one day we will be able to remove this restriction). */ + + if (repeat_max != 1) cd->external_flags |= PCRE_NOPARTIAL; + + /* Combine the op_type with the repeat_type */ + + repeat_type += op_type; + + /* A minimum of zero is handled either as the special case * or ?, or as + an UPTO, with the maximum given. */ + + if (repeat_min == 0) + { + if (repeat_max == -1) *code++ = OP_STAR + repeat_type; + else if (repeat_max == 1) *code++ = OP_QUERY + repeat_type; + else + { + *code++ = OP_UPTO + repeat_type; + PUT2INC(code, 0, repeat_max); + } + } + + /* A repeat minimum of 1 is optimized into some special cases. If the + maximum is unlimited, we use OP_PLUS. Otherwise, the original item is + left in place and, if the maximum is greater than 1, we use OP_UPTO with + one less than the maximum. */ + + else if (repeat_min == 1) + { + if (repeat_max == -1) + *code++ = OP_PLUS + repeat_type; + else + { + code = oldcode; /* leave previous item in place */ + if (repeat_max == 1) goto END_REPEAT; + *code++ = OP_UPTO + repeat_type; + PUT2INC(code, 0, repeat_max - 1); + } + } + + /* The case {n,n} is just an EXACT, while the general case {n,m} is + handled as an EXACT followed by an UPTO. */ + + else + { + *code++ = OP_EXACT + op_type; /* NB EXACT doesn't have repeat_type */ + PUT2INC(code, 0, repeat_min); + + /* If the maximum is unlimited, insert an OP_STAR. Before doing so, + we have to insert the character for the previous code. For a repeated + Unicode property match, there are two extra bytes that define the + required property. In UTF-8 mode, long characters have their length in + c, with the 0x80 bit as a flag. */ + + if (repeat_max < 0) + { +#ifdef SUPPORT_UTF8 + if (utf8 && c >= 128) + { + memcpy(code, utf8_char, c & 7); + code += c & 7; + } + else +#endif + { + *code++ = c; + if (prop_type >= 0) + { + *code++ = prop_type; + *code++ = prop_value; + } + } + *code++ = OP_STAR + repeat_type; + } + + /* Else insert an UPTO if the max is greater than the min, again + preceded by the character, for the previously inserted code. If the + UPTO is just for 1 instance, we can use QUERY instead. */ + + else if (repeat_max != repeat_min) + { +#ifdef SUPPORT_UTF8 + if (utf8 && c >= 128) + { + memcpy(code, utf8_char, c & 7); + code += c & 7; + } + else +#endif + *code++ = c; + if (prop_type >= 0) + { + *code++ = prop_type; + *code++ = prop_value; + } + repeat_max -= repeat_min; + + if (repeat_max == 1) + { + *code++ = OP_QUERY + repeat_type; + } + else + { + *code++ = OP_UPTO + repeat_type; + PUT2INC(code, 0, repeat_max); + } + } + } + + /* The character or character type itself comes last in all cases. */ + +#ifdef SUPPORT_UTF8 + if (utf8 && c >= 128) + { + memcpy(code, utf8_char, c & 7); + code += c & 7; + } + else +#endif + *code++ = c; + + /* For a repeated Unicode property match, there are two extra bytes that + define the required property. */ + +#ifdef SUPPORT_UCP + if (prop_type >= 0) + { + *code++ = prop_type; + *code++ = prop_value; + } +#endif + } + + /* If previous was a character class or a back reference, we put the repeat + stuff after it, but just skip the item if the repeat was {0,0}. */ + + else if (*previous == OP_CLASS || + *previous == OP_NCLASS || +#ifdef SUPPORT_UTF8 + *previous == OP_XCLASS || +#endif + *previous == OP_REF) + { + if (repeat_max == 0) + { + code = previous; + goto END_REPEAT; + } + + /* All real repeats make it impossible to handle partial matching (maybe + one day we will be able to remove this restriction). */ + + if (repeat_max != 1) cd->external_flags |= PCRE_NOPARTIAL; + + if (repeat_min == 0 && repeat_max == -1) + *code++ = OP_CRSTAR + repeat_type; + else if (repeat_min == 1 && repeat_max == -1) + *code++ = OP_CRPLUS + repeat_type; + else if (repeat_min == 0 && repeat_max == 1) + *code++ = OP_CRQUERY + repeat_type; + else + { + *code++ = OP_CRRANGE + repeat_type; + PUT2INC(code, 0, repeat_min); + if (repeat_max == -1) repeat_max = 0; /* 2-byte encoding for max */ + PUT2INC(code, 0, repeat_max); + } + } + + /* If previous was a bracket group, we may have to replicate it in certain + cases. */ + + else if (*previous == OP_BRA || *previous == OP_CBRA || + *previous == OP_ONCE || *previous == OP_COND) + { + register int i; + int ketoffset = 0; + int len = code - previous; + uschar *bralink = NULL; + + /* Repeating a DEFINE group is pointless */ + + if (*previous == OP_COND && previous[LINK_SIZE+1] == OP_DEF) + { + *errorcodeptr = ERR55; + goto FAILED; + } + + /* If the maximum repeat count is unlimited, find the end of the bracket + by scanning through from the start, and compute the offset back to it + from the current code pointer. There may be an OP_OPT setting following + the final KET, so we can't find the end just by going back from the code + pointer. */ + + if (repeat_max == -1) + { + register uschar *ket = previous; + do ket += GET(ket, 1); while (*ket != OP_KET); + ketoffset = code - ket; + } + + /* The case of a zero minimum is special because of the need to stick + OP_BRAZERO in front of it, and because the group appears once in the + data, whereas in other cases it appears the minimum number of times. For + this reason, it is simplest to treat this case separately, as otherwise + the code gets far too messy. There are several special subcases when the + minimum is zero. */ + + if (repeat_min == 0) + { + /* If the maximum is also zero, we just omit the group from the output + altogether. */ + + if (repeat_max == 0) + { + code = previous; + goto END_REPEAT; + } + + /* If the maximum is 1 or unlimited, we just have to stick in the + BRAZERO and do no more at this point. However, we do need to adjust + any OP_RECURSE calls inside the group that refer to the group itself or + any internal or forward referenced group, because the offset is from + the start of the whole regex. Temporarily terminate the pattern while + doing this. */ + + if (repeat_max <= 1) + { + *code = OP_END; + adjust_recurse(previous, 1, utf8, cd, save_hwm); + memmove(previous+1, previous, len); + code++; + *previous++ = OP_BRAZERO + repeat_type; + } + + /* If the maximum is greater than 1 and limited, we have to replicate + in a nested fashion, sticking OP_BRAZERO before each set of brackets. + The first one has to be handled carefully because it's the original + copy, which has to be moved up. The remainder can be handled by code + that is common with the non-zero minimum case below. We have to + adjust the value or repeat_max, since one less copy is required. Once + again, we may have to adjust any OP_RECURSE calls inside the group. */ + + else + { + int offset; + *code = OP_END; + adjust_recurse(previous, 2 + LINK_SIZE, utf8, cd, save_hwm); + memmove(previous + 2 + LINK_SIZE, previous, len); + code += 2 + LINK_SIZE; + *previous++ = OP_BRAZERO + repeat_type; + *previous++ = OP_BRA; + + /* We chain together the bracket offset fields that have to be + filled in later when the ends of the brackets are reached. */ + + offset = (bralink == NULL)? 0 : previous - bralink; + bralink = previous; + PUTINC(previous, 0, offset); + } + + repeat_max--; + } + + /* If the minimum is greater than zero, replicate the group as many + times as necessary, and adjust the maximum to the number of subsequent + copies that we need. If we set a first char from the group, and didn't + set a required char, copy the latter from the former. If there are any + forward reference subroutine calls in the group, there will be entries on + the workspace list; replicate these with an appropriate increment. */ + + else + { + if (repeat_min > 1) + { + /* In the pre-compile phase, we don't actually do the replication. We + just adjust the length as if we had. Do some paranoid checks for + potential integer overflow. */ + + if (lengthptr != NULL) + { + int delta = (repeat_min - 1)*length_prevgroup; + if ((double)(repeat_min - 1)*(double)length_prevgroup > + (double)INT_MAX || + OFLOW_MAX - *lengthptr < delta) + { + *errorcodeptr = ERR20; + goto FAILED; + } + *lengthptr += delta; + } + + /* This is compiling for real */ + + else + { + if (groupsetfirstbyte && reqbyte < 0) reqbyte = firstbyte; + for (i = 1; i < repeat_min; i++) + { + uschar *hc; + uschar *this_hwm = cd->hwm; + memcpy(code, previous, len); + for (hc = save_hwm; hc < this_hwm; hc += LINK_SIZE) + { + PUT(cd->hwm, 0, GET(hc, 0) + len); + cd->hwm += LINK_SIZE; + } + save_hwm = this_hwm; + code += len; + } + } + } + + if (repeat_max > 0) repeat_max -= repeat_min; + } + + /* This code is common to both the zero and non-zero minimum cases. If + the maximum is limited, it replicates the group in a nested fashion, + remembering the bracket starts on a stack. In the case of a zero minimum, + the first one was set up above. In all cases the repeat_max now specifies + the number of additional copies needed. Again, we must remember to + replicate entries on the forward reference list. */ + + if (repeat_max >= 0) + { + /* In the pre-compile phase, we don't actually do the replication. We + just adjust the length as if we had. For each repetition we must add 1 + to the length for BRAZERO and for all but the last repetition we must + add 2 + 2*LINKSIZE to allow for the nesting that occurs. Do some + paranoid checks to avoid integer overflow. */ + + if (lengthptr != NULL && repeat_max > 0) + { + int delta = repeat_max * (length_prevgroup + 1 + 2 + 2*LINK_SIZE) - + 2 - 2*LINK_SIZE; /* Last one doesn't nest */ + if ((double)repeat_max * + (double)(length_prevgroup + 1 + 2 + 2*LINK_SIZE) + > (double)INT_MAX || + OFLOW_MAX - *lengthptr < delta) + { + *errorcodeptr = ERR20; + goto FAILED; + } + *lengthptr += delta; + } + + /* This is compiling for real */ + + else for (i = repeat_max - 1; i >= 0; i--) + { + uschar *hc; + uschar *this_hwm = cd->hwm; + + *code++ = OP_BRAZERO + repeat_type; + + /* All but the final copy start a new nesting, maintaining the + chain of brackets outstanding. */ + + if (i != 0) + { + int offset; + *code++ = OP_BRA; + offset = (bralink == NULL)? 0 : code - bralink; + bralink = code; + PUTINC(code, 0, offset); + } + + memcpy(code, previous, len); + for (hc = save_hwm; hc < this_hwm; hc += LINK_SIZE) + { + PUT(cd->hwm, 0, GET(hc, 0) + len + ((i != 0)? 2+LINK_SIZE : 1)); + cd->hwm += LINK_SIZE; + } + save_hwm = this_hwm; + code += len; + } + + /* Now chain through the pending brackets, and fill in their length + fields (which are holding the chain links pro tem). */ + + while (bralink != NULL) + { + int oldlinkoffset; + int offset = code - bralink + 1; + uschar *bra = code - offset; + oldlinkoffset = GET(bra, 1); + bralink = (oldlinkoffset == 0)? NULL : bralink - oldlinkoffset; + *code++ = OP_KET; + PUTINC(code, 0, offset); + PUT(bra, 1, offset); + } + } + + /* If the maximum is unlimited, set a repeater in the final copy. We + can't just offset backwards from the current code point, because we + don't know if there's been an options resetting after the ket. The + correct offset was computed above. + + Then, when we are doing the actual compile phase, check to see whether + this group is a non-atomic one that could match an empty string. If so, + convert the initial operator to the S form (e.g. OP_BRA -> OP_SBRA) so + that runtime checking can be done. [This check is also applied to + atomic groups at runtime, but in a different way.] */ + + else + { + uschar *ketcode = code - ketoffset; + uschar *bracode = ketcode - GET(ketcode, 1); + *ketcode = OP_KETRMAX + repeat_type; + if (lengthptr == NULL && *bracode != OP_ONCE) + { + uschar *scode = bracode; + do + { + if (could_be_empty_branch(scode, ketcode, utf8)) + { + *bracode += OP_SBRA - OP_BRA; + break; + } + scode += GET(scode, 1); + } + while (*scode == OP_ALT); + } + } + } + + /* Else there's some kind of shambles */ + + else + { + *errorcodeptr = ERR11; + goto FAILED; + } + + /* If the character following a repeat is '+', or if certain optimization + tests above succeeded, possessive_quantifier is TRUE. For some of the + simpler opcodes, there is an special alternative opcode for this. For + anything else, we wrap the entire repeated item inside OP_ONCE brackets. + The '+' notation is just syntactic sugar, taken from Sun's Java package, + but the special opcodes can optimize it a bit. The repeated item starts at + tempcode, not at previous, which might be the first part of a string whose + (former) last char we repeated. + + Possessifying an 'exact' quantifier has no effect, so we can ignore it. But + an 'upto' may follow. We skip over an 'exact' item, and then test the + length of what remains before proceeding. */ + + if (possessive_quantifier) + { + int len; + if (*tempcode == OP_EXACT || *tempcode == OP_TYPEEXACT || + *tempcode == OP_NOTEXACT) + tempcode += _pcre_OP_lengths[*tempcode] + + ((*tempcode == OP_TYPEEXACT && + (tempcode[3] == OP_PROP || tempcode[3] == OP_NOTPROP))? 2:0); + len = code - tempcode; + if (len > 0) switch (*tempcode) + { + case OP_STAR: *tempcode = OP_POSSTAR; break; + case OP_PLUS: *tempcode = OP_POSPLUS; break; + case OP_QUERY: *tempcode = OP_POSQUERY; break; + case OP_UPTO: *tempcode = OP_POSUPTO; break; + + case OP_TYPESTAR: *tempcode = OP_TYPEPOSSTAR; break; + case OP_TYPEPLUS: *tempcode = OP_TYPEPOSPLUS; break; + case OP_TYPEQUERY: *tempcode = OP_TYPEPOSQUERY; break; + case OP_TYPEUPTO: *tempcode = OP_TYPEPOSUPTO; break; + + case OP_NOTSTAR: *tempcode = OP_NOTPOSSTAR; break; + case OP_NOTPLUS: *tempcode = OP_NOTPOSPLUS; break; + case OP_NOTQUERY: *tempcode = OP_NOTPOSQUERY; break; + case OP_NOTUPTO: *tempcode = OP_NOTPOSUPTO; break; + + default: + memmove(tempcode + 1+LINK_SIZE, tempcode, len); + code += 1 + LINK_SIZE; + len += 1 + LINK_SIZE; + tempcode[0] = OP_ONCE; + *code++ = OP_KET; + PUTINC(code, 0, len); + PUT(tempcode, 1, len); + break; + } + } + + /* In all case we no longer have a previous item. We also set the + "follows varying string" flag for subsequently encountered reqbytes if + it isn't already set and we have just passed a varying length item. */ + + END_REPEAT: + previous = NULL; + cd->req_varyopt |= reqvary; + break; + + + /* ===================================================================*/ + /* Start of nested parenthesized sub-expression, or comment or lookahead or + lookbehind or option setting or condition or all the other extended + parenthesis forms. */ + + case '(': + newoptions = options; + skipbytes = 0; + bravalue = OP_CBRA; + save_hwm = cd->hwm; + reset_bracount = FALSE; + + /* First deal with various "verbs" that can be introduced by '*'. */ + + if (*(++ptr) == '*' && (cd->ctypes[ptr[1]] & ctype_letter) != 0) + { + int i, namelen; + const char *vn = verbnames; + const uschar *name = ++ptr; + previous = NULL; + while ((cd->ctypes[*++ptr] & ctype_letter) != 0); + if (*ptr == ':') + { + *errorcodeptr = ERR59; /* Not supported */ + goto FAILED; + } + if (*ptr != ')') + { + *errorcodeptr = ERR60; + goto FAILED; + } + namelen = ptr - name; + for (i = 0; i < verbcount; i++) + { + if (namelen == verbs[i].len && + strncmp((char *)name, vn, namelen) == 0) + { + *code = verbs[i].op; + if (*code++ == OP_ACCEPT) cd->had_accept = TRUE; + break; + } + vn += verbs[i].len + 1; + } + if (i < verbcount) continue; + *errorcodeptr = ERR60; + goto FAILED; + } + + /* Deal with the extended parentheses; all are introduced by '?', and the + appearance of any of them means that this is not a capturing group. */ + + else if (*ptr == '?') + { + int i, set, unset, namelen; + int *optset; + const uschar *name; + uschar *slot; + + switch (*(++ptr)) + { + case '#': /* Comment; skip to ket */ + ptr++; + while (*ptr != 0 && *ptr != ')') ptr++; + if (*ptr == 0) + { + *errorcodeptr = ERR18; + goto FAILED; + } + continue; + + + /* ------------------------------------------------------------ */ + case '|': /* Reset capture count for each branch */ + reset_bracount = TRUE; + /* Fall through */ + + /* ------------------------------------------------------------ */ + case ':': /* Non-capturing bracket */ + bravalue = OP_BRA; + ptr++; + break; + + + /* ------------------------------------------------------------ */ + case '(': + bravalue = OP_COND; /* Conditional group */ + + /* A condition can be an assertion, a number (referring to a numbered + group), a name (referring to a named group), or 'R', referring to + recursion. R and R&name are also permitted for recursion tests. + + There are several syntaxes for testing a named group: (?(name)) is used + by Python; Perl 5.10 onwards uses (?() or (?('name')). + + There are two unfortunate ambiguities, caused by history. (a) 'R' can + be the recursive thing or the name 'R' (and similarly for 'R' followed + by digits), and (b) a number could be a name that consists of digits. + In both cases, we look for a name first; if not found, we try the other + cases. */ + + /* For conditions that are assertions, check the syntax, and then exit + the switch. This will take control down to where bracketed groups, + including assertions, are processed. */ + + if (ptr[1] == '?' && (ptr[2] == '=' || ptr[2] == '!' || ptr[2] == '<')) + break; + + /* Most other conditions use OP_CREF (a couple change to OP_RREF + below), and all need to skip 3 bytes at the start of the group. */ + + code[1+LINK_SIZE] = OP_CREF; + skipbytes = 3; + refsign = -1; + + /* Check for a test for recursion in a named group. */ + + if (ptr[1] == 'R' && ptr[2] == '&') + { + terminator = -1; + ptr += 2; + code[1+LINK_SIZE] = OP_RREF; /* Change the type of test */ + } + + /* Check for a test for a named group's having been set, using the Perl + syntax (?() or (?('name') */ + + else if (ptr[1] == '<') + { + terminator = '>'; + ptr++; + } + else if (ptr[1] == '\'') + { + terminator = '\''; + ptr++; + } + else + { + terminator = 0; + if (ptr[1] == '-' || ptr[1] == '+') refsign = *(++ptr); + } + + /* We now expect to read a name; any thing else is an error */ + + if ((cd->ctypes[ptr[1]] & ctype_word) == 0) + { + ptr += 1; /* To get the right offset */ + *errorcodeptr = ERR28; + goto FAILED; + } + + /* Read the name, but also get it as a number if it's all digits */ + + recno = 0; + name = ++ptr; + while ((cd->ctypes[*ptr] & ctype_word) != 0) + { + if (recno >= 0) + recno = ((digitab[*ptr] & ctype_digit) != 0)? + recno * 10 + *ptr - '0' : -1; + ptr++; + } + namelen = ptr - name; + + if ((terminator > 0 && *ptr++ != terminator) || *ptr++ != ')') + { + ptr--; /* Error offset */ + *errorcodeptr = ERR26; + goto FAILED; + } + + /* Do no further checking in the pre-compile phase. */ + + if (lengthptr != NULL) break; + + /* In the real compile we do the work of looking for the actual + reference. If the string started with "+" or "-" we require the rest to + be digits, in which case recno will be set. */ + + if (refsign > 0) + { + if (recno <= 0) + { + *errorcodeptr = ERR58; + goto FAILED; + } + recno = (refsign == '-')? + cd->bracount - recno + 1 : recno +cd->bracount; + if (recno <= 0 || recno > cd->final_bracount) + { + *errorcodeptr = ERR15; + goto FAILED; + } + PUT2(code, 2+LINK_SIZE, recno); + break; + } + + /* Otherwise (did not start with "+" or "-"), start by looking for the + name. */ + + slot = cd->name_table; + for (i = 0; i < cd->names_found; i++) + { + if (strncmp((char *)name, (char *)slot+2, namelen) == 0) break; + slot += cd->name_entry_size; + } + + /* Found a previous named subpattern */ + + if (i < cd->names_found) + { + recno = GET2(slot, 0); + PUT2(code, 2+LINK_SIZE, recno); + } + + /* Search the pattern for a forward reference */ + + else if ((i = find_parens(ptr, cd->bracount, name, namelen, + (options & PCRE_EXTENDED) != 0)) > 0) + { + PUT2(code, 2+LINK_SIZE, i); + } + + /* If terminator == 0 it means that the name followed directly after + the opening parenthesis [e.g. (?(abc)...] and in this case there are + some further alternatives to try. For the cases where terminator != 0 + [things like (?(... or (?('name')... or (?(R&name)... ] we have + now checked all the possibilities, so give an error. */ + + else if (terminator != 0) + { + *errorcodeptr = ERR15; + goto FAILED; + } + + /* Check for (?(R) for recursion. Allow digits after R to specify a + specific group number. */ + + else if (*name == 'R') + { + recno = 0; + for (i = 1; i < namelen; i++) + { + if ((digitab[name[i]] & ctype_digit) == 0) + { + *errorcodeptr = ERR15; + goto FAILED; + } + recno = recno * 10 + name[i] - '0'; + } + if (recno == 0) recno = RREF_ANY; + code[1+LINK_SIZE] = OP_RREF; /* Change test type */ + PUT2(code, 2+LINK_SIZE, recno); + } + + /* Similarly, check for the (?(DEFINE) "condition", which is always + false. */ + + else if (namelen == 6 && strncmp((char *)name, "DEFINE", 6) == 0) + { + code[1+LINK_SIZE] = OP_DEF; + skipbytes = 1; + } + + /* Check for the "name" actually being a subpattern number. We are + in the second pass here, so final_bracount is set. */ + + else if (recno > 0 && recno <= cd->final_bracount) + { + PUT2(code, 2+LINK_SIZE, recno); + } + + /* Either an unidentified subpattern, or a reference to (?(0) */ + + else + { + *errorcodeptr = (recno == 0)? ERR35: ERR15; + goto FAILED; + } + break; + + + /* ------------------------------------------------------------ */ + case '=': /* Positive lookahead */ + bravalue = OP_ASSERT; + ptr++; + break; + + + /* ------------------------------------------------------------ */ + case '!': /* Negative lookahead */ + ptr++; + if (*ptr == ')') /* Optimize (?!) */ + { + *code++ = OP_FAIL; + previous = NULL; + continue; + } + bravalue = OP_ASSERT_NOT; + break; + + + /* ------------------------------------------------------------ */ + case '<': /* Lookbehind or named define */ + switch (ptr[1]) + { + case '=': /* Positive lookbehind */ + bravalue = OP_ASSERTBACK; + ptr += 2; + break; + + case '!': /* Negative lookbehind */ + bravalue = OP_ASSERTBACK_NOT; + ptr += 2; + break; + + default: /* Could be name define, else bad */ + if ((cd->ctypes[ptr[1]] & ctype_word) != 0) goto DEFINE_NAME; + ptr++; /* Correct offset for error */ + *errorcodeptr = ERR24; + goto FAILED; + } + break; + + + /* ------------------------------------------------------------ */ + case '>': /* One-time brackets */ + bravalue = OP_ONCE; + ptr++; + break; + + + /* ------------------------------------------------------------ */ + case 'C': /* Callout - may be followed by digits; */ + previous_callout = code; /* Save for later completion */ + after_manual_callout = 1; /* Skip one item before completing */ + *code++ = OP_CALLOUT; + { + int n = 0; + while ((digitab[*(++ptr)] & ctype_digit) != 0) + n = n * 10 + *ptr - '0'; + if (*ptr != ')') + { + *errorcodeptr = ERR39; + goto FAILED; + } + if (n > 255) + { + *errorcodeptr = ERR38; + goto FAILED; + } + *code++ = n; + PUT(code, 0, ptr - cd->start_pattern + 1); /* Pattern offset */ + PUT(code, LINK_SIZE, 0); /* Default length */ + code += 2 * LINK_SIZE; + } + previous = NULL; + continue; + + + /* ------------------------------------------------------------ */ + case 'P': /* Python-style named subpattern handling */ + if (*(++ptr) == '=' || *ptr == '>') /* Reference or recursion */ + { + is_recurse = *ptr == '>'; + terminator = ')'; + goto NAMED_REF_OR_RECURSE; + } + else if (*ptr != '<') /* Test for Python-style definition */ + { + *errorcodeptr = ERR41; + goto FAILED; + } + /* Fall through to handle (?P< as (?< is handled */ + + + /* ------------------------------------------------------------ */ + DEFINE_NAME: /* Come here from (?< handling */ + case '\'': + { + terminator = (*ptr == '<')? '>' : '\''; + name = ++ptr; + + while ((cd->ctypes[*ptr] & ctype_word) != 0) ptr++; + namelen = ptr - name; + + /* In the pre-compile phase, just do a syntax check. */ + + if (lengthptr != NULL) + { + if (*ptr != terminator) + { + *errorcodeptr = ERR42; + goto FAILED; + } + if (cd->names_found >= MAX_NAME_COUNT) + { + *errorcodeptr = ERR49; + goto FAILED; + } + if (namelen + 3 > cd->name_entry_size) + { + cd->name_entry_size = namelen + 3; + if (namelen > MAX_NAME_SIZE) + { + *errorcodeptr = ERR48; + goto FAILED; + } + } + } + + /* In the real compile, create the entry in the table */ + + else + { + slot = cd->name_table; + for (i = 0; i < cd->names_found; i++) + { + int crc = memcmp(name, slot+2, namelen); + if (crc == 0) + { + if (slot[2+namelen] == 0) + { + if ((options & PCRE_DUPNAMES) == 0) + { + *errorcodeptr = ERR43; + goto FAILED; + } + } + else crc = -1; /* Current name is substring */ + } + if (crc < 0) + { + memmove(slot + cd->name_entry_size, slot, + (cd->names_found - i) * cd->name_entry_size); + break; + } + slot += cd->name_entry_size; + } + + PUT2(slot, 0, cd->bracount + 1); + memcpy(slot + 2, name, namelen); + slot[2+namelen] = 0; + } + } + + /* In both cases, count the number of names we've encountered. */ + + ptr++; /* Move past > or ' */ + cd->names_found++; + goto NUMBERED_GROUP; + + + /* ------------------------------------------------------------ */ + case '&': /* Perl recursion/subroutine syntax */ + terminator = ')'; + is_recurse = TRUE; + /* Fall through */ + + /* We come here from the Python syntax above that handles both + references (?P=name) and recursion (?P>name), as well as falling + through from the Perl recursion syntax (?&name). We also come here from + the Perl \k or \k'name' back reference syntax and the \k{name} + .NET syntax. */ + + NAMED_REF_OR_RECURSE: + name = ++ptr; + while ((cd->ctypes[*ptr] & ctype_word) != 0) ptr++; + namelen = ptr - name; + + /* In the pre-compile phase, do a syntax check and set a dummy + reference number. */ + + if (lengthptr != NULL) + { + if (namelen == 0) + { + *errorcodeptr = ERR62; + goto FAILED; + } + if (*ptr != terminator) + { + *errorcodeptr = ERR42; + goto FAILED; + } + if (namelen > MAX_NAME_SIZE) + { + *errorcodeptr = ERR48; + goto FAILED; + } + recno = 0; + } + + /* In the real compile, seek the name in the table. We check the name + first, and then check that we have reached the end of the name in the + table. That way, if the name that is longer than any in the table, + the comparison will fail without reading beyond the table entry. */ + + else + { + slot = cd->name_table; + for (i = 0; i < cd->names_found; i++) + { + if (strncmp((char *)name, (char *)slot+2, namelen) == 0 && + slot[2+namelen] == 0) + break; + slot += cd->name_entry_size; + } + + if (i < cd->names_found) /* Back reference */ + { + recno = GET2(slot, 0); + } + else if ((recno = /* Forward back reference */ + find_parens(ptr, cd->bracount, name, namelen, + (options & PCRE_EXTENDED) != 0)) <= 0) + { + *errorcodeptr = ERR15; + goto FAILED; + } + } + + /* In both phases, we can now go to the code than handles numerical + recursion or backreferences. */ + + if (is_recurse) goto HANDLE_RECURSION; + else goto HANDLE_REFERENCE; + + + /* ------------------------------------------------------------ */ + case 'R': /* Recursion */ + ptr++; /* Same as (?0) */ + /* Fall through */ + + + /* ------------------------------------------------------------ */ + case '-': case '+': + case '0': case '1': case '2': case '3': case '4': /* Recursion or */ + case '5': case '6': case '7': case '8': case '9': /* subroutine */ + { + const uschar *called; + + if ((refsign = *ptr) == '+') + { + ptr++; + if ((digitab[*ptr] & ctype_digit) == 0) + { + *errorcodeptr = ERR63; + goto FAILED; + } + } + else if (refsign == '-') + { + if ((digitab[ptr[1]] & ctype_digit) == 0) + goto OTHER_CHAR_AFTER_QUERY; + ptr++; + } + + recno = 0; + while((digitab[*ptr] & ctype_digit) != 0) + recno = recno * 10 + *ptr++ - '0'; + + if (*ptr != ')') + { + *errorcodeptr = ERR29; + goto FAILED; + } + + if (refsign == '-') + { + if (recno == 0) + { + *errorcodeptr = ERR58; + goto FAILED; + } + recno = cd->bracount - recno + 1; + if (recno <= 0) + { + *errorcodeptr = ERR15; + goto FAILED; + } + } + else if (refsign == '+') + { + if (recno == 0) + { + *errorcodeptr = ERR58; + goto FAILED; + } + recno += cd->bracount; + } + + /* Come here from code above that handles a named recursion */ + + HANDLE_RECURSION: + + previous = code; + called = cd->start_code; + + /* When we are actually compiling, find the bracket that is being + referenced. Temporarily end the regex in case it doesn't exist before + this point. If we end up with a forward reference, first check that + the bracket does occur later so we can give the error (and position) + now. Then remember this forward reference in the workspace so it can + be filled in at the end. */ + + if (lengthptr == NULL) + { + *code = OP_END; + if (recno != 0) called = find_bracket(cd->start_code, utf8, recno); + + /* Forward reference */ + + if (called == NULL) + { + if (find_parens(ptr, cd->bracount, NULL, recno, + (options & PCRE_EXTENDED) != 0) < 0) + { + *errorcodeptr = ERR15; + goto FAILED; + } + called = cd->start_code + recno; + PUTINC(cd->hwm, 0, code + 2 + LINK_SIZE - cd->start_code); + } + + /* If not a forward reference, and the subpattern is still open, + this is a recursive call. We check to see if this is a left + recursion that could loop for ever, and diagnose that case. */ + + else if (GET(called, 1) == 0 && + could_be_empty(called, code, bcptr, utf8)) + { + *errorcodeptr = ERR40; + goto FAILED; + } + } + + /* Insert the recursion/subroutine item, automatically wrapped inside + "once" brackets. Set up a "previous group" length so that a + subsequent quantifier will work. */ + + *code = OP_ONCE; + PUT(code, 1, 2 + 2*LINK_SIZE); + code += 1 + LINK_SIZE; + + *code = OP_RECURSE; + PUT(code, 1, called - cd->start_code); + code += 1 + LINK_SIZE; + + *code = OP_KET; + PUT(code, 1, 2 + 2*LINK_SIZE); + code += 1 + LINK_SIZE; + + length_prevgroup = 3 + 3*LINK_SIZE; + } + + /* Can't determine a first byte now */ + + if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE; + continue; + + + /* ------------------------------------------------------------ */ + default: /* Other characters: check option setting */ + OTHER_CHAR_AFTER_QUERY: + set = unset = 0; + optset = &set; + + while (*ptr != ')' && *ptr != ':') + { + switch (*ptr++) + { + case '-': optset = &unset; break; + + case 'J': /* Record that it changed in the external options */ + *optset |= PCRE_DUPNAMES; + cd->external_flags |= PCRE_JCHANGED; + break; + + case 'i': *optset |= PCRE_CASELESS; break; + case 'm': *optset |= PCRE_MULTILINE; break; + case 's': *optset |= PCRE_DOTALL; break; + case 'x': *optset |= PCRE_EXTENDED; break; + case 'U': *optset |= PCRE_UNGREEDY; break; + case 'X': *optset |= PCRE_EXTRA; break; + + default: *errorcodeptr = ERR12; + ptr--; /* Correct the offset */ + goto FAILED; + } + } + + /* Set up the changed option bits, but don't change anything yet. */ + + newoptions = (options | set) & (~unset); + + /* If the options ended with ')' this is not the start of a nested + group with option changes, so the options change at this level. If this + item is right at the start of the pattern, the options can be + abstracted and made external in the pre-compile phase, and ignored in + the compile phase. This can be helpful when matching -- for instance in + caseless checking of required bytes. + + If the code pointer is not (cd->start_code + 1 + LINK_SIZE), we are + definitely *not* at the start of the pattern because something has been + compiled. In the pre-compile phase, however, the code pointer can have + that value after the start, because it gets reset as code is discarded + during the pre-compile. However, this can happen only at top level - if + we are within parentheses, the starting BRA will still be present. At + any parenthesis level, the length value can be used to test if anything + has been compiled at that level. Thus, a test for both these conditions + is necessary to ensure we correctly detect the start of the pattern in + both phases. + + If we are not at the pattern start, compile code to change the ims + options if this setting actually changes any of them. We also pass the + new setting back so that it can be put at the start of any following + branches, and when this group ends (if we are in a group), a resetting + item can be compiled. */ + + if (*ptr == ')') + { + if (code == cd->start_code + 1 + LINK_SIZE && + (lengthptr == NULL || *lengthptr == 2 + 2*LINK_SIZE)) + { + cd->external_options = newoptions; + options = newoptions; + } + else + { + if ((options & PCRE_IMS) != (newoptions & PCRE_IMS)) + { + *code++ = OP_OPT; + *code++ = newoptions & PCRE_IMS; + } + + /* Change options at this level, and pass them back for use + in subsequent branches. Reset the greedy defaults and the case + value for firstbyte and reqbyte. */ + + *optionsptr = options = newoptions; + greedy_default = ((newoptions & PCRE_UNGREEDY) != 0); + greedy_non_default = greedy_default ^ 1; + req_caseopt = ((options & PCRE_CASELESS) != 0)? REQ_CASELESS : 0; + } + + previous = NULL; /* This item can't be repeated */ + continue; /* It is complete */ + } + + /* If the options ended with ':' we are heading into a nested group + with possible change of options. Such groups are non-capturing and are + not assertions of any kind. All we need to do is skip over the ':'; + the newoptions value is handled below. */ + + bravalue = OP_BRA; + ptr++; + } /* End of switch for character following (? */ + } /* End of (? handling */ + + /* Opening parenthesis not followed by '?'. If PCRE_NO_AUTO_CAPTURE is set, + all unadorned brackets become non-capturing and behave like (?:...) + brackets. */ + + else if ((options & PCRE_NO_AUTO_CAPTURE) != 0) + { + bravalue = OP_BRA; + } + + /* Else we have a capturing group. */ + + else + { + NUMBERED_GROUP: + cd->bracount += 1; + PUT2(code, 1+LINK_SIZE, cd->bracount); + skipbytes = 2; + } + + /* Process nested bracketed regex. Assertions may not be repeated, but + other kinds can be. All their opcodes are >= OP_ONCE. We copy code into a + non-register variable in order to be able to pass its address because some + compilers complain otherwise. Pass in a new setting for the ims options if + they have changed. */ + + previous = (bravalue >= OP_ONCE)? code : NULL; + *code = bravalue; + tempcode = code; + tempreqvary = cd->req_varyopt; /* Save value before bracket */ + length_prevgroup = 0; /* Initialize for pre-compile phase */ + + if (!compile_regex( + newoptions, /* The complete new option state */ + options & PCRE_IMS, /* The previous ims option state */ + &tempcode, /* Where to put code (updated) */ + &ptr, /* Input pointer (updated) */ + errorcodeptr, /* Where to put an error message */ + (bravalue == OP_ASSERTBACK || + bravalue == OP_ASSERTBACK_NOT), /* TRUE if back assert */ + reset_bracount, /* True if (?| group */ + skipbytes, /* Skip over bracket number */ + &subfirstbyte, /* For possible first char */ + &subreqbyte, /* For possible last char */ + bcptr, /* Current branch chain */ + cd, /* Tables block */ + (lengthptr == NULL)? NULL : /* Actual compile phase */ + &length_prevgroup /* Pre-compile phase */ + )) + goto FAILED; + + /* At the end of compiling, code is still pointing to the start of the + group, while tempcode has been updated to point past the end of the group + and any option resetting that may follow it. The pattern pointer (ptr) + is on the bracket. */ + + /* If this is a conditional bracket, check that there are no more than + two branches in the group, or just one if it's a DEFINE group. We do this + in the real compile phase, not in the pre-pass, where the whole group may + not be available. */ + + if (bravalue == OP_COND && lengthptr == NULL) + { + uschar *tc = code; + int condcount = 0; + + do { + condcount++; + tc += GET(tc,1); + } + while (*tc != OP_KET); + + /* A DEFINE group is never obeyed inline (the "condition" is always + false). It must have only one branch. */ + + if (code[LINK_SIZE+1] == OP_DEF) + { + if (condcount > 1) + { + *errorcodeptr = ERR54; + goto FAILED; + } + bravalue = OP_DEF; /* Just a flag to suppress char handling below */ + } + + /* A "normal" conditional group. If there is just one branch, we must not + make use of its firstbyte or reqbyte, because this is equivalent to an + empty second branch. */ + + else + { + if (condcount > 2) + { + *errorcodeptr = ERR27; + goto FAILED; + } + if (condcount == 1) subfirstbyte = subreqbyte = REQ_NONE; + } + } + + /* Error if hit end of pattern */ + + if (*ptr != ')') + { + *errorcodeptr = ERR14; + goto FAILED; + } + + /* In the pre-compile phase, update the length by the length of the group, + less the brackets at either end. Then reduce the compiled code to just a + set of non-capturing brackets so that it doesn't use much memory if it is + duplicated by a quantifier.*/ + + if (lengthptr != NULL) + { + if (OFLOW_MAX - *lengthptr < length_prevgroup - 2 - 2*LINK_SIZE) + { + *errorcodeptr = ERR20; + goto FAILED; + } + *lengthptr += length_prevgroup - 2 - 2*LINK_SIZE; + *code++ = OP_BRA; + PUTINC(code, 0, 1 + LINK_SIZE); + *code++ = OP_KET; + PUTINC(code, 0, 1 + LINK_SIZE); + break; /* No need to waste time with special character handling */ + } + + /* Otherwise update the main code pointer to the end of the group. */ + + code = tempcode; + + /* For a DEFINE group, required and first character settings are not + relevant. */ + + if (bravalue == OP_DEF) break; + + /* Handle updating of the required and first characters for other types of + group. Update for normal brackets of all kinds, and conditions with two + branches (see code above). If the bracket is followed by a quantifier with + zero repeat, we have to back off. Hence the definition of zeroreqbyte and + zerofirstbyte outside the main loop so that they can be accessed for the + back off. */ + + zeroreqbyte = reqbyte; + zerofirstbyte = firstbyte; + groupsetfirstbyte = FALSE; + + if (bravalue >= OP_ONCE) + { + /* If we have not yet set a firstbyte in this branch, take it from the + subpattern, remembering that it was set here so that a repeat of more + than one can replicate it as reqbyte if necessary. If the subpattern has + no firstbyte, set "none" for the whole branch. In both cases, a zero + repeat forces firstbyte to "none". */ + + if (firstbyte == REQ_UNSET) + { + if (subfirstbyte >= 0) + { + firstbyte = subfirstbyte; + groupsetfirstbyte = TRUE; + } + else firstbyte = REQ_NONE; + zerofirstbyte = REQ_NONE; + } + + /* If firstbyte was previously set, convert the subpattern's firstbyte + into reqbyte if there wasn't one, using the vary flag that was in + existence beforehand. */ + + else if (subfirstbyte >= 0 && subreqbyte < 0) + subreqbyte = subfirstbyte | tempreqvary; + + /* If the subpattern set a required byte (or set a first byte that isn't + really the first byte - see above), set it. */ + + if (subreqbyte >= 0) reqbyte = subreqbyte; + } + + /* For a forward assertion, we take the reqbyte, if set. This can be + helpful if the pattern that follows the assertion doesn't set a different + char. For example, it's useful for /(?=abcde).+/. We can't set firstbyte + for an assertion, however because it leads to incorrect effect for patterns + such as /(?=a)a.+/ when the "real" "a" would then become a reqbyte instead + of a firstbyte. This is overcome by a scan at the end if there's no + firstbyte, looking for an asserted first char. */ + + else if (bravalue == OP_ASSERT && subreqbyte >= 0) reqbyte = subreqbyte; + break; /* End of processing '(' */ + + + /* ===================================================================*/ + /* Handle metasequences introduced by \. For ones like \d, the ESC_ values + are arranged to be the negation of the corresponding OP_values. For the + back references, the values are ESC_REF plus the reference number. Only + back references and those types that consume a character may be repeated. + We can test for values between ESC_b and ESC_Z for the latter; this may + have to change if any new ones are ever created. */ + + case '\\': + tempptr = ptr; + c = check_escape(&ptr, errorcodeptr, cd->bracount, options, FALSE); + if (*errorcodeptr != 0) goto FAILED; + + if (c < 0) + { + if (-c == ESC_Q) /* Handle start of quoted string */ + { + if (ptr[1] == '\\' && ptr[2] == 'E') ptr += 2; /* avoid empty string */ + else inescq = TRUE; + continue; + } + + if (-c == ESC_E) continue; /* Perl ignores an orphan \E */ + + /* For metasequences that actually match a character, we disable the + setting of a first character if it hasn't already been set. */ + + if (firstbyte == REQ_UNSET && -c > ESC_b && -c < ESC_Z) + firstbyte = REQ_NONE; + + /* Set values to reset to if this is followed by a zero repeat. */ + + zerofirstbyte = firstbyte; + zeroreqbyte = reqbyte; + + /* \k or \k'name' is a back reference by name (Perl syntax). + We also support \k{name} (.NET syntax) */ + + if (-c == ESC_k && (ptr[1] == '<' || ptr[1] == '\'' || ptr[1] == '{')) + { + is_recurse = FALSE; + terminator = (*(++ptr) == '<')? '>' : (*ptr == '\'')? '\'' : '}'; + goto NAMED_REF_OR_RECURSE; + } + + /* Back references are handled specially; must disable firstbyte if + not set to cope with cases like (?=(\w+))\1: which would otherwise set + ':' later. */ + + if (-c >= ESC_REF) + { + recno = -c - ESC_REF; + + HANDLE_REFERENCE: /* Come here from named backref handling */ + if (firstbyte == REQ_UNSET) firstbyte = REQ_NONE; + previous = code; + *code++ = OP_REF; + PUT2INC(code, 0, recno); + cd->backref_map |= (recno < 32)? (1 << recno) : 1; + if (recno > cd->top_backref) cd->top_backref = recno; + } + + /* So are Unicode property matches, if supported. */ + +#ifdef SUPPORT_UCP + else if (-c == ESC_P || -c == ESC_p) + { + BOOL negated; + int pdata; + int ptype = get_ucp(&ptr, &negated, &pdata, errorcodeptr); + if (ptype < 0) goto FAILED; + previous = code; + *code++ = ((-c == ESC_p) != negated)? OP_PROP : OP_NOTPROP; + *code++ = ptype; + *code++ = pdata; + } +#else + + /* If Unicode properties are not supported, \X, \P, and \p are not + allowed. */ + + else if (-c == ESC_X || -c == ESC_P || -c == ESC_p) + { + *errorcodeptr = ERR45; + goto FAILED; + } +#endif + + /* For the rest (including \X when Unicode properties are supported), we + can obtain the OP value by negating the escape value. */ + + else + { + previous = (-c > ESC_b && -c < ESC_Z)? code : NULL; + *code++ = -c; + } + continue; + } + + /* We have a data character whose value is in c. In UTF-8 mode it may have + a value > 127. We set its representation in the length/buffer, and then + handle it as a data character. */ + +#ifdef SUPPORT_UTF8 + if (utf8 && c > 127) + mclength = _pcre_ord2utf8(c, mcbuffer); + else +#endif + + { + mcbuffer[0] = c; + mclength = 1; + } + goto ONE_CHAR; + + + /* ===================================================================*/ + /* Handle a literal character. It is guaranteed not to be whitespace or # + when the extended flag is set. If we are in UTF-8 mode, it may be a + multi-byte literal character. */ + + default: + NORMAL_CHAR: + mclength = 1; + mcbuffer[0] = c; + +#ifdef SUPPORT_UTF8 + if (utf8 && c >= 0xc0) + { + while ((ptr[1] & 0xc0) == 0x80) + mcbuffer[mclength++] = *(++ptr); + } +#endif + + /* At this point we have the character's bytes in mcbuffer, and the length + in mclength. When not in UTF-8 mode, the length is always 1. */ + + ONE_CHAR: + previous = code; + *code++ = ((options & PCRE_CASELESS) != 0)? OP_CHARNC : OP_CHAR; + for (c = 0; c < mclength; c++) *code++ = mcbuffer[c]; + + /* Remember if \r or \n were seen */ + + if (mcbuffer[0] == '\r' || mcbuffer[0] == '\n') + cd->external_flags |= PCRE_HASCRORLF; + + /* Set the first and required bytes appropriately. If no previous first + byte, set it from this character, but revert to none on a zero repeat. + Otherwise, leave the firstbyte value alone, and don't change it on a zero + repeat. */ + + if (firstbyte == REQ_UNSET) + { + zerofirstbyte = REQ_NONE; + zeroreqbyte = reqbyte; + + /* If the character is more than one byte long, we can set firstbyte + only if it is not to be matched caselessly. */ + + if (mclength == 1 || req_caseopt == 0) + { + firstbyte = mcbuffer[0] | req_caseopt; + if (mclength != 1) reqbyte = code[-1] | cd->req_varyopt; + } + else firstbyte = reqbyte = REQ_NONE; + } + + /* firstbyte was previously set; we can set reqbyte only the length is + 1 or the matching is caseful. */ + + else + { + zerofirstbyte = firstbyte; + zeroreqbyte = reqbyte; + if (mclength == 1 || req_caseopt == 0) + reqbyte = code[-1] | req_caseopt | cd->req_varyopt; + } + + break; /* End of literal character handling */ + } + } /* end of big loop */ + + +/* Control never reaches here by falling through, only by a goto for all the +error states. Pass back the position in the pattern so that it can be displayed +to the user for diagnosing the error. */ + +FAILED: +*ptrptr = ptr; +return FALSE; +} + + + + +/************************************************* +* Compile sequence of alternatives * +*************************************************/ + +/* On entry, ptr is pointing past the bracket character, but on return it +points to the closing bracket, or vertical bar, or end of string. The code +variable is pointing at the byte into which the BRA operator has been stored. +If the ims options are changed at the start (for a (?ims: group) or during any +branch, we need to insert an OP_OPT item at the start of every following branch +to ensure they get set correctly at run time, and also pass the new options +into every subsequent branch compile. + +This function is used during the pre-compile phase when we are trying to find +out the amount of memory needed, as well as during the real compile phase. The +value of lengthptr distinguishes the two phases. + +Arguments: + options option bits, including any changes for this subpattern + oldims previous settings of ims option bits + codeptr -> the address of the current code pointer + ptrptr -> the address of the current pattern pointer + errorcodeptr -> pointer to error code variable + lookbehind TRUE if this is a lookbehind assertion + reset_bracount TRUE to reset the count for each branch + skipbytes skip this many bytes at start (for brackets and OP_COND) + firstbyteptr place to put the first required character, or a negative number + reqbyteptr place to put the last required character, or a negative number + bcptr pointer to the chain of currently open branches + cd points to the data block with tables pointers etc. + lengthptr NULL during the real compile phase + points to length accumulator during pre-compile phase + +Returns: TRUE on success +*/ + +static BOOL +compile_regex(int options, int oldims, uschar **codeptr, const uschar **ptrptr, + int *errorcodeptr, BOOL lookbehind, BOOL reset_bracount, int skipbytes, + int *firstbyteptr, int *reqbyteptr, branch_chain *bcptr, compile_data *cd, + int *lengthptr) +{ +const uschar *ptr = *ptrptr; +uschar *code = *codeptr; +uschar *last_branch = code; +uschar *start_bracket = code; +uschar *reverse_count = NULL; +int firstbyte, reqbyte; +int branchfirstbyte, branchreqbyte; +int length; +int orig_bracount; +int max_bracount; +branch_chain bc; + +bc.outer = bcptr; +bc.current = code; + +firstbyte = reqbyte = REQ_UNSET; + +/* Accumulate the length for use in the pre-compile phase. Start with the +length of the BRA and KET and any extra bytes that are required at the +beginning. We accumulate in a local variable to save frequent testing of +lenthptr for NULL. We cannot do this by looking at the value of code at the +start and end of each alternative, because compiled items are discarded during +the pre-compile phase so that the work space is not exceeded. */ + +length = 2 + 2*LINK_SIZE + skipbytes; + +/* WARNING: If the above line is changed for any reason, you must also change +the code that abstracts option settings at the start of the pattern and makes +them global. It tests the value of length for (2 + 2*LINK_SIZE) in the +pre-compile phase to find out whether anything has yet been compiled or not. */ + +/* Offset is set zero to mark that this bracket is still open */ + +PUT(code, 1, 0); +code += 1 + LINK_SIZE + skipbytes; + +/* Loop for each alternative branch */ + +orig_bracount = max_bracount = cd->bracount; +for (;;) + { + /* For a (?| group, reset the capturing bracket count so that each branch + uses the same numbers. */ + + if (reset_bracount) cd->bracount = orig_bracount; + + /* Handle a change of ims options at the start of the branch */ + + if ((options & PCRE_IMS) != oldims) + { + *code++ = OP_OPT; + *code++ = options & PCRE_IMS; + length += 2; + } + + /* Set up dummy OP_REVERSE if lookbehind assertion */ + + if (lookbehind) + { + *code++ = OP_REVERSE; + reverse_count = code; + PUTINC(code, 0, 0); + length += 1 + LINK_SIZE; + } + + /* Now compile the branch; in the pre-compile phase its length gets added + into the length. */ + + if (!compile_branch(&options, &code, &ptr, errorcodeptr, &branchfirstbyte, + &branchreqbyte, &bc, cd, (lengthptr == NULL)? NULL : &length)) + { + *ptrptr = ptr; + return FALSE; + } + + /* Keep the highest bracket count in case (?| was used and some branch + has fewer than the rest. */ + + if (cd->bracount > max_bracount) max_bracount = cd->bracount; + + /* In the real compile phase, there is some post-processing to be done. */ + + if (lengthptr == NULL) + { + /* If this is the first branch, the firstbyte and reqbyte values for the + branch become the values for the regex. */ + + if (*last_branch != OP_ALT) + { + firstbyte = branchfirstbyte; + reqbyte = branchreqbyte; + } + + /* If this is not the first branch, the first char and reqbyte have to + match the values from all the previous branches, except that if the + previous value for reqbyte didn't have REQ_VARY set, it can still match, + and we set REQ_VARY for the regex. */ + + else + { + /* If we previously had a firstbyte, but it doesn't match the new branch, + we have to abandon the firstbyte for the regex, but if there was + previously no reqbyte, it takes on the value of the old firstbyte. */ + + if (firstbyte >= 0 && firstbyte != branchfirstbyte) + { + if (reqbyte < 0) reqbyte = firstbyte; + firstbyte = REQ_NONE; + } + + /* If we (now or from before) have no firstbyte, a firstbyte from the + branch becomes a reqbyte if there isn't a branch reqbyte. */ + + if (firstbyte < 0 && branchfirstbyte >= 0 && branchreqbyte < 0) + branchreqbyte = branchfirstbyte; + + /* Now ensure that the reqbytes match */ + + if ((reqbyte & ~REQ_VARY) != (branchreqbyte & ~REQ_VARY)) + reqbyte = REQ_NONE; + else reqbyte |= branchreqbyte; /* To "or" REQ_VARY */ + } + + /* If lookbehind, check that this branch matches a fixed-length string, and + put the length into the OP_REVERSE item. Temporarily mark the end of the + branch with OP_END. */ + + if (lookbehind) + { + int fixed_length; + *code = OP_END; + fixed_length = find_fixedlength(last_branch, options); + DPRINTF(("fixed length = %d\n", fixed_length)); + if (fixed_length < 0) + { + *errorcodeptr = (fixed_length == -2)? ERR36 : ERR25; + *ptrptr = ptr; + return FALSE; + } + PUT(reverse_count, 0, fixed_length); + } + } + + /* Reached end of expression, either ')' or end of pattern. In the real + compile phase, go back through the alternative branches and reverse the chain + of offsets, with the field in the BRA item now becoming an offset to the + first alternative. If there are no alternatives, it points to the end of the + group. The length in the terminating ket is always the length of the whole + bracketed item. If any of the ims options were changed inside the group, + compile a resetting op-code following, except at the very end of the pattern. + Return leaving the pointer at the terminating char. */ + + if (*ptr != '|') + { + if (lengthptr == NULL) + { + int branch_length = code - last_branch; + do + { + int prev_length = GET(last_branch, 1); + PUT(last_branch, 1, branch_length); + branch_length = prev_length; + last_branch -= branch_length; + } + while (branch_length > 0); + } + + /* Fill in the ket */ + + *code = OP_KET; + PUT(code, 1, code - start_bracket); + code += 1 + LINK_SIZE; + + /* Resetting option if needed */ + + if ((options & PCRE_IMS) != oldims && *ptr == ')') + { + *code++ = OP_OPT; + *code++ = oldims; + length += 2; + } + + /* Retain the highest bracket number, in case resetting was used. */ + + cd->bracount = max_bracount; + + /* Set values to pass back */ + + *codeptr = code; + *ptrptr = ptr; + *firstbyteptr = firstbyte; + *reqbyteptr = reqbyte; + if (lengthptr != NULL) + { + if (OFLOW_MAX - *lengthptr < length) + { + *errorcodeptr = ERR20; + return FALSE; + } + *lengthptr += length; + } + return TRUE; + } + + /* Another branch follows. In the pre-compile phase, we can move the code + pointer back to where it was for the start of the first branch. (That is, + pretend that each branch is the only one.) + + In the real compile phase, insert an ALT node. Its length field points back + to the previous branch while the bracket remains open. At the end the chain + is reversed. It's done like this so that the start of the bracket has a + zero offset until it is closed, making it possible to detect recursion. */ + + if (lengthptr != NULL) + { + code = *codeptr + 1 + LINK_SIZE + skipbytes; + length += 1 + LINK_SIZE; + } + else + { + *code = OP_ALT; + PUT(code, 1, code - last_branch); + bc.current = last_branch = code; + code += 1 + LINK_SIZE; + } + + ptr++; + } +/* Control never reaches here */ +} + + + + +/************************************************* +* Check for anchored expression * +*************************************************/ + +/* Try to find out if this is an anchored regular expression. Consider each +alternative branch. If they all start with OP_SOD or OP_CIRC, or with a bracket +all of whose alternatives start with OP_SOD or OP_CIRC (recurse ad lib), then +it's anchored. However, if this is a multiline pattern, then only OP_SOD +counts, since OP_CIRC can match in the middle. + +We can also consider a regex to be anchored if OP_SOM starts all its branches. +This is the code for \G, which means "match at start of match position, taking +into account the match offset". + +A branch is also implicitly anchored if it starts with .* and DOTALL is set, +because that will try the rest of the pattern at all possible matching points, +so there is no point trying again.... er .... + +.... except when the .* appears inside capturing parentheses, and there is a +subsequent back reference to those parentheses. We haven't enough information +to catch that case precisely. + +At first, the best we could do was to detect when .* was in capturing brackets +and the highest back reference was greater than or equal to that level. +However, by keeping a bitmap of the first 31 back references, we can catch some +of the more common cases more precisely. + +Arguments: + code points to start of expression (the bracket) + options points to the options setting + bracket_map a bitmap of which brackets we are inside while testing; this + handles up to substring 31; after that we just have to take + the less precise approach + backref_map the back reference bitmap + +Returns: TRUE or FALSE +*/ + +static BOOL +is_anchored(register const uschar *code, int *options, unsigned int bracket_map, + unsigned int backref_map) +{ +do { + const uschar *scode = first_significant_code(code + _pcre_OP_lengths[*code], + options, PCRE_MULTILINE, FALSE); + register int op = *scode; + + /* Non-capturing brackets */ + + if (op == OP_BRA) + { + if (!is_anchored(scode, options, bracket_map, backref_map)) return FALSE; + } + + /* Capturing brackets */ + + else if (op == OP_CBRA) + { + int n = GET2(scode, 1+LINK_SIZE); + int new_map = bracket_map | ((n < 32)? (1 << n) : 1); + if (!is_anchored(scode, options, new_map, backref_map)) return FALSE; + } + + /* Other brackets */ + + else if (op == OP_ASSERT || op == OP_ONCE || op == OP_COND) + { + if (!is_anchored(scode, options, bracket_map, backref_map)) return FALSE; + } + + /* .* is not anchored unless DOTALL is set and it isn't in brackets that + are or may be referenced. */ + + else if ((op == OP_TYPESTAR || op == OP_TYPEMINSTAR || + op == OP_TYPEPOSSTAR) && + (*options & PCRE_DOTALL) != 0) + { + if (scode[1] != OP_ANY || (bracket_map & backref_map) != 0) return FALSE; + } + + /* Check for explicit anchoring */ + + else if (op != OP_SOD && op != OP_SOM && + ((*options & PCRE_MULTILINE) != 0 || op != OP_CIRC)) + return FALSE; + code += GET(code, 1); + } +while (*code == OP_ALT); /* Loop for each alternative */ +return TRUE; +} + + + +/************************************************* +* Check for starting with ^ or .* * +*************************************************/ + +/* This is called to find out if every branch starts with ^ or .* so that +"first char" processing can be done to speed things up in multiline +matching and for non-DOTALL patterns that start with .* (which must start at +the beginning or after \n). As in the case of is_anchored() (see above), we +have to take account of back references to capturing brackets that contain .* +because in that case we can't make the assumption. + +Arguments: + code points to start of expression (the bracket) + bracket_map a bitmap of which brackets we are inside while testing; this + handles up to substring 31; after that we just have to take + the less precise approach + backref_map the back reference bitmap + +Returns: TRUE or FALSE +*/ + +static BOOL +is_startline(const uschar *code, unsigned int bracket_map, + unsigned int backref_map) +{ +do { + const uschar *scode = first_significant_code(code + _pcre_OP_lengths[*code], + NULL, 0, FALSE); + register int op = *scode; + + /* Non-capturing brackets */ + + if (op == OP_BRA) + { + if (!is_startline(scode, bracket_map, backref_map)) return FALSE; + } + + /* Capturing brackets */ + + else if (op == OP_CBRA) + { + int n = GET2(scode, 1+LINK_SIZE); + int new_map = bracket_map | ((n < 32)? (1 << n) : 1); + if (!is_startline(scode, new_map, backref_map)) return FALSE; + } + + /* Other brackets */ + + else if (op == OP_ASSERT || op == OP_ONCE || op == OP_COND) + { if (!is_startline(scode, bracket_map, backref_map)) return FALSE; } + + /* .* means "start at start or after \n" if it isn't in brackets that + may be referenced. */ + + else if (op == OP_TYPESTAR || op == OP_TYPEMINSTAR || op == OP_TYPEPOSSTAR) + { + if (scode[1] != OP_ANY || (bracket_map & backref_map) != 0) return FALSE; + } + + /* Check for explicit circumflex */ + + else if (op != OP_CIRC) return FALSE; + + /* Move on to the next alternative */ + + code += GET(code, 1); + } +while (*code == OP_ALT); /* Loop for each alternative */ +return TRUE; +} + + + +/************************************************* +* Check for asserted fixed first char * +*************************************************/ + +/* During compilation, the "first char" settings from forward assertions are +discarded, because they can cause conflicts with actual literals that follow. +However, if we end up without a first char setting for an unanchored pattern, +it is worth scanning the regex to see if there is an initial asserted first +char. If all branches start with the same asserted char, or with a bracket all +of whose alternatives start with the same asserted char (recurse ad lib), then +we return that char, otherwise -1. + +Arguments: + code points to start of expression (the bracket) + options pointer to the options (used to check casing changes) + inassert TRUE if in an assertion + +Returns: -1 or the fixed first char +*/ + +static int +find_firstassertedchar(const uschar *code, int *options, BOOL inassert) +{ +register int c = -1; +do { + int d; + const uschar *scode = + first_significant_code(code + 1+LINK_SIZE, options, PCRE_CASELESS, TRUE); + register int op = *scode; + + switch(op) + { + default: + return -1; + + case OP_BRA: + case OP_CBRA: + case OP_ASSERT: + case OP_ONCE: + case OP_COND: + if ((d = find_firstassertedchar(scode, options, op == OP_ASSERT)) < 0) + return -1; + if (c < 0) c = d; else if (c != d) return -1; + break; + + case OP_EXACT: /* Fall through */ + scode += 2; + + case OP_CHAR: + case OP_CHARNC: + case OP_PLUS: + case OP_MINPLUS: + case OP_POSPLUS: + if (!inassert) return -1; + if (c < 0) + { + c = scode[1]; + if ((*options & PCRE_CASELESS) != 0) c |= REQ_CASELESS; + } + else if (c != scode[1]) return -1; + break; + } + + code += GET(code, 1); + } +while (*code == OP_ALT); +return c; +} + + + +/************************************************* +* Compile a Regular Expression * +*************************************************/ + +/* This function takes a string and returns a pointer to a block of store +holding a compiled version of the expression. The original API for this +function had no error code return variable; it is retained for backwards +compatibility. The new function is given a new name. + +Arguments: + pattern the regular expression + options various option bits + errorcodeptr pointer to error code variable (pcre_compile2() only) + can be NULL if you don't want a code value + errorptr pointer to pointer to error text + erroroffset ptr offset in pattern where error was detected + tables pointer to character tables or NULL + +Returns: pointer to compiled data block, or NULL on error, + with errorptr and erroroffset set +*/ + +PCRE_EXP_DEFN pcre * +pcre_compile(const char *pattern, int options, const char **errorptr, + int *erroroffset, const unsigned char *tables) +{ +return pcre_compile2(pattern, options, NULL, errorptr, erroroffset, tables); +} + + +PCRE_EXP_DEFN pcre * +pcre_compile2(const char *pattern, int options, int *errorcodeptr, + const char **errorptr, int *erroroffset, const unsigned char *tables) +{ +real_pcre *re; +int length = 1; /* For final END opcode */ +int firstbyte, reqbyte, newline; +int errorcode = 0; +int skipatstart = 0; +#ifdef SUPPORT_UTF8 +BOOL utf8; +#endif +size_t size; +uschar *code; +const uschar *codestart; +const uschar *ptr; +compile_data compile_block; +compile_data *cd = &compile_block; + +/* This space is used for "compiling" into during the first phase, when we are +computing the amount of memory that is needed. Compiled items are thrown away +as soon as possible, so that a fairly large buffer should be sufficient for +this purpose. The same space is used in the second phase for remembering where +to fill in forward references to subpatterns. */ + +uschar cworkspace[COMPILE_WORK_SIZE]; + +/* Set this early so that early errors get offset 0. */ + +ptr = (const uschar *)pattern; + +/* We can't pass back an error message if errorptr is NULL; I guess the best we +can do is just return NULL, but we can set a code value if there is a code +pointer. */ + +if (errorptr == NULL) + { + if (errorcodeptr != NULL) *errorcodeptr = 99; + return NULL; + } + +*errorptr = NULL; +if (errorcodeptr != NULL) *errorcodeptr = ERR0; + +/* However, we can give a message for this error */ + +if (erroroffset == NULL) + { + errorcode = ERR16; + goto PCRE_EARLY_ERROR_RETURN2; + } + +*erroroffset = 0; + +/* Can't support UTF8 unless PCRE has been compiled to include the code. */ + +#ifdef SUPPORT_UTF8 +utf8 = (options & PCRE_UTF8) != 0; +if (utf8 && (options & PCRE_NO_UTF8_CHECK) == 0 && + (*erroroffset = _pcre_valid_utf8((uschar *)pattern, -1)) >= 0) + { + errorcode = ERR44; + goto PCRE_EARLY_ERROR_RETURN2; + } +#else +if ((options & PCRE_UTF8) != 0) + { + errorcode = ERR32; + goto PCRE_EARLY_ERROR_RETURN; + } +#endif + +if ((options & ~PUBLIC_OPTIONS) != 0) + { + errorcode = ERR17; + goto PCRE_EARLY_ERROR_RETURN; + } + +/* Set up pointers to the individual character tables */ + +if (tables == NULL) tables = _pcre_default_tables; +cd->lcc = tables + lcc_offset; +cd->fcc = tables + fcc_offset; +cd->cbits = tables + cbits_offset; +cd->ctypes = tables + ctypes_offset; + +/* Check for global one-time settings at the start of the pattern, and remember +the offset for later. */ + +while (ptr[skipatstart] == '(' && ptr[skipatstart+1] == '*') + { + int newnl = 0; + int newbsr = 0; + + if (strncmp((char *)(ptr+skipatstart+2), "CR)", 3) == 0) + { skipatstart += 5; newnl = PCRE_NEWLINE_CR; } + else if (strncmp((char *)(ptr+skipatstart+2), "LF)", 3) == 0) + { skipatstart += 5; newnl = PCRE_NEWLINE_LF; } + else if (strncmp((char *)(ptr+skipatstart+2), "CRLF)", 5) == 0) + { skipatstart += 7; newnl = PCRE_NEWLINE_CR + PCRE_NEWLINE_LF; } + else if (strncmp((char *)(ptr+skipatstart+2), "ANY)", 4) == 0) + { skipatstart += 6; newnl = PCRE_NEWLINE_ANY; } + else if (strncmp((char *)(ptr+skipatstart+2), "ANYCRLF)", 8) == 0) + { skipatstart += 10; newnl = PCRE_NEWLINE_ANYCRLF; } + + else if (strncmp((char *)(ptr+skipatstart+2), "BSR_ANYCRLF)", 12) == 0) + { skipatstart += 14; newbsr = PCRE_BSR_ANYCRLF; } + else if (strncmp((char *)(ptr+skipatstart+2), "BSR_UNICODE)", 12) == 0) + { skipatstart += 14; newbsr = PCRE_BSR_UNICODE; } + + if (newnl != 0) + options = (options & ~PCRE_NEWLINE_BITS) | newnl; + else if (newbsr != 0) + options = (options & ~(PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE)) | newbsr; + else break; + } + +/* Check validity of \R options. */ + +switch (options & (PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE)) + { + case 0: + case PCRE_BSR_ANYCRLF: + case PCRE_BSR_UNICODE: + break; + default: errorcode = ERR56; goto PCRE_EARLY_ERROR_RETURN; + } + +/* Handle different types of newline. The three bits give seven cases. The +current code allows for fixed one- or two-byte sequences, plus "any" and +"anycrlf". */ + +switch (options & PCRE_NEWLINE_BITS) + { + case 0: newline = NEWLINE; break; /* Build-time default */ + case PCRE_NEWLINE_CR: newline = '\r'; break; + case PCRE_NEWLINE_LF: newline = '\n'; break; + case PCRE_NEWLINE_CR+ + PCRE_NEWLINE_LF: newline = ('\r' << 8) | '\n'; break; + case PCRE_NEWLINE_ANY: newline = -1; break; + case PCRE_NEWLINE_ANYCRLF: newline = -2; break; + default: errorcode = ERR56; goto PCRE_EARLY_ERROR_RETURN; + } + +if (newline == -2) + { + cd->nltype = NLTYPE_ANYCRLF; + } +else if (newline < 0) + { + cd->nltype = NLTYPE_ANY; + } +else + { + cd->nltype = NLTYPE_FIXED; + if (newline > 255) + { + cd->nllen = 2; + cd->nl[0] = (newline >> 8) & 255; + cd->nl[1] = newline & 255; + } + else + { + cd->nllen = 1; + cd->nl[0] = newline; + } + } + +/* Maximum back reference and backref bitmap. The bitmap records up to 31 back +references to help in deciding whether (.*) can be treated as anchored or not. +*/ + +cd->top_backref = 0; +cd->backref_map = 0; + +/* Reflect pattern for debugging output */ + +DPRINTF(("------------------------------------------------------------------\n")); +DPRINTF(("%s\n", pattern)); + +/* Pretend to compile the pattern while actually just accumulating the length +of memory required. This behaviour is triggered by passing a non-NULL final +argument to compile_regex(). We pass a block of workspace (cworkspace) for it +to compile parts of the pattern into; the compiled code is discarded when it is +no longer needed, so hopefully this workspace will never overflow, though there +is a test for its doing so. */ + +cd->bracount = cd->final_bracount = 0; +cd->names_found = 0; +cd->name_entry_size = 0; +cd->name_table = NULL; +cd->start_workspace = cworkspace; +cd->start_code = cworkspace; +cd->hwm = cworkspace; +cd->start_pattern = (const uschar *)pattern; +cd->end_pattern = (const uschar *)(pattern + strlen(pattern)); +cd->req_varyopt = 0; +cd->external_options = options; +cd->external_flags = 0; + +/* Now do the pre-compile. On error, errorcode will be set non-zero, so we +don't need to look at the result of the function here. The initial options have +been put into the cd block so that they can be changed if an option setting is +found within the regex right at the beginning. Bringing initial option settings +outside can help speed up starting point checks. */ + +ptr += skipatstart; +code = cworkspace; +*code = OP_BRA; +(void)compile_regex(cd->external_options, cd->external_options & PCRE_IMS, + &code, &ptr, &errorcode, FALSE, FALSE, 0, &firstbyte, &reqbyte, NULL, cd, + &length); +if (errorcode != 0) goto PCRE_EARLY_ERROR_RETURN; + +DPRINTF(("end pre-compile: length=%d workspace=%d\n", length, + cd->hwm - cworkspace)); + +if (length > MAX_PATTERN_SIZE) + { + errorcode = ERR20; + goto PCRE_EARLY_ERROR_RETURN; + } + +/* Compute the size of data block needed and get it, either from malloc or +externally provided function. Integer overflow should no longer be possible +because nowadays we limit the maximum value of cd->names_found and +cd->name_entry_size. */ + +size = length + sizeof(real_pcre) + cd->names_found * (cd->name_entry_size + 3); +re = (real_pcre *)(pcre_malloc)(size); + +if (re == NULL) + { + errorcode = ERR21; + goto PCRE_EARLY_ERROR_RETURN; + } + +/* Put in the magic number, and save the sizes, initial options, internal +flags, and character table pointer. NULL is used for the default character +tables. The nullpad field is at the end; it's there to help in the case when a +regex compiled on a system with 4-byte pointers is run on another with 8-byte +pointers. */ + +re->magic_number = MAGIC_NUMBER; +re->size = size; +re->options = cd->external_options; +re->flags = cd->external_flags; +re->dummy1 = 0; +re->first_byte = 0; +re->req_byte = 0; +re->name_table_offset = sizeof(real_pcre); +re->name_entry_size = cd->name_entry_size; +re->name_count = cd->names_found; +re->ref_count = 0; +re->tables = (tables == _pcre_default_tables)? NULL : tables; +re->nullpad = NULL; + +/* The starting points of the name/number translation table and of the code are +passed around in the compile data block. The start/end pattern and initial +options are already set from the pre-compile phase, as is the name_entry_size +field. Reset the bracket count and the names_found field. Also reset the hwm +field; this time it's used for remembering forward references to subpatterns. +*/ + +cd->final_bracount = cd->bracount; /* Save for checking forward references */ +cd->bracount = 0; +cd->names_found = 0; +cd->name_table = (uschar *)re + re->name_table_offset; +codestart = cd->name_table + re->name_entry_size * re->name_count; +cd->start_code = codestart; +cd->hwm = cworkspace; +cd->req_varyopt = 0; +cd->had_accept = FALSE; + +/* Set up a starting, non-extracting bracket, then compile the expression. On +error, errorcode will be set non-zero, so we don't need to look at the result +of the function here. */ + +ptr = (const uschar *)pattern + skipatstart; +code = (uschar *)codestart; +*code = OP_BRA; +(void)compile_regex(re->options, re->options & PCRE_IMS, &code, &ptr, + &errorcode, FALSE, FALSE, 0, &firstbyte, &reqbyte, NULL, cd, NULL); +re->top_bracket = cd->bracount; +re->top_backref = cd->top_backref; +re->flags = cd->external_flags; + +if (cd->had_accept) reqbyte = -1; /* Must disable after (*ACCEPT) */ + +/* If not reached end of pattern on success, there's an excess bracket. */ + +if (errorcode == 0 && *ptr != 0) errorcode = ERR22; + +/* Fill in the terminating state and check for disastrous overflow, but +if debugging, leave the test till after things are printed out. */ + +*code++ = OP_END; + +#ifndef DEBUG +if (code - codestart > length) errorcode = ERR23; +#endif + +/* Fill in any forward references that are required. */ + +while (errorcode == 0 && cd->hwm > cworkspace) + { + int offset, recno; + const uschar *groupptr; + cd->hwm -= LINK_SIZE; + offset = GET(cd->hwm, 0); + recno = GET(codestart, offset); + groupptr = find_bracket(codestart, (re->options & PCRE_UTF8) != 0, recno); + if (groupptr == NULL) errorcode = ERR53; + else PUT(((uschar *)codestart), offset, groupptr - codestart); + } + +/* Give an error if there's back reference to a non-existent capturing +subpattern. */ + +if (errorcode == 0 && re->top_backref > re->top_bracket) errorcode = ERR15; + +/* Failed to compile, or error while post-processing */ + +if (errorcode != 0) + { + (pcre_free)(re); + PCRE_EARLY_ERROR_RETURN: + *erroroffset = ptr - (const uschar *)pattern; + PCRE_EARLY_ERROR_RETURN2: + *errorptr = find_error_text(errorcode); + if (errorcodeptr != NULL) *errorcodeptr = errorcode; + return NULL; + } + +/* If the anchored option was not passed, set the flag if we can determine that +the pattern is anchored by virtue of ^ characters or \A or anything else (such +as starting with .* when DOTALL is set). + +Otherwise, if we know what the first byte has to be, save it, because that +speeds up unanchored matches no end. If not, see if we can set the +PCRE_STARTLINE flag. This is helpful for multiline matches when all branches +start with ^. and also when all branches start with .* for non-DOTALL matches. +*/ + +if ((re->options & PCRE_ANCHORED) == 0) + { + int temp_options = re->options; /* May get changed during these scans */ + if (is_anchored(codestart, &temp_options, 0, cd->backref_map)) + re->options |= PCRE_ANCHORED; + else + { + if (firstbyte < 0) + firstbyte = find_firstassertedchar(codestart, &temp_options, FALSE); + if (firstbyte >= 0) /* Remove caseless flag for non-caseable chars */ + { + int ch = firstbyte & 255; + re->first_byte = ((firstbyte & REQ_CASELESS) != 0 && + cd->fcc[ch] == ch)? ch : firstbyte; + re->flags |= PCRE_FIRSTSET; + } + else if (is_startline(codestart, 0, cd->backref_map)) + re->flags |= PCRE_STARTLINE; + } + } + +/* For an anchored pattern, we use the "required byte" only if it follows a +variable length item in the regex. Remove the caseless flag for non-caseable +bytes. */ + +if (reqbyte >= 0 && + ((re->options & PCRE_ANCHORED) == 0 || (reqbyte & REQ_VARY) != 0)) + { + int ch = reqbyte & 255; + re->req_byte = ((reqbyte & REQ_CASELESS) != 0 && + cd->fcc[ch] == ch)? (reqbyte & ~REQ_CASELESS) : reqbyte; + re->flags |= PCRE_REQCHSET; + } + +/* Print out the compiled data if debugging is enabled. This is never the +case when building a production library. */ + +#ifdef DEBUG + +printf("Length = %d top_bracket = %d top_backref = %d\n", + length, re->top_bracket, re->top_backref); + +printf("Options=%08x\n", re->options); + +if ((re->flags & PCRE_FIRSTSET) != 0) + { + int ch = re->first_byte & 255; + const char *caseless = ((re->first_byte & REQ_CASELESS) == 0)? + "" : " (caseless)"; + if (isprint(ch)) printf("First char = %c%s\n", ch, caseless); + else printf("First char = \\x%02x%s\n", ch, caseless); + } + +if ((re->flags & PCRE_REQCHSET) != 0) + { + int ch = re->req_byte & 255; + const char *caseless = ((re->req_byte & REQ_CASELESS) == 0)? + "" : " (caseless)"; + if (isprint(ch)) printf("Req char = %c%s\n", ch, caseless); + else printf("Req char = \\x%02x%s\n", ch, caseless); + } + +pcre_printint(re, stdout, TRUE); + +/* This check is done here in the debugging case so that the code that +was compiled can be seen. */ + +if (code - codestart > length) + { + (pcre_free)(re); + *errorptr = find_error_text(ERR23); + *erroroffset = ptr - (uschar *)pattern; + if (errorcodeptr != NULL) *errorcodeptr = ERR23; + return NULL; + } +#endif /* DEBUG */ + +return (pcre *)re; +} + +/* End of pcre_compile.c */ diff --git a/Engine/lib/pcre/pcre_config.c b/Engine/lib/pcre/pcre_config.c new file mode 100644 index 000000000..454fed98d --- /dev/null +++ b/Engine/lib/pcre/pcre_config.c @@ -0,0 +1,128 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2008 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains the external function pcre_config(). */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + + +/************************************************* +* Return info about what features are configured * +*************************************************/ + +/* This function has an extensible interface so that additional items can be +added compatibly. + +Arguments: + what what information is required + where where to put the information + +Returns: 0 if data returned, negative on error +*/ + +PCRE_EXP_DEFN int +pcre_config(int what, void *where) +{ +switch (what) + { + case PCRE_CONFIG_UTF8: +#ifdef SUPPORT_UTF8 + *((int *)where) = 1; +#else + *((int *)where) = 0; +#endif + break; + + case PCRE_CONFIG_UNICODE_PROPERTIES: +#ifdef SUPPORT_UCP + *((int *)where) = 1; +#else + *((int *)where) = 0; +#endif + break; + + case PCRE_CONFIG_NEWLINE: + *((int *)where) = NEWLINE; + break; + + case PCRE_CONFIG_BSR: +#ifdef BSR_ANYCRLF + *((int *)where) = 1; +#else + *((int *)where) = 0; +#endif + break; + + case PCRE_CONFIG_LINK_SIZE: + *((int *)where) = LINK_SIZE; + break; + + case PCRE_CONFIG_POSIX_MALLOC_THRESHOLD: + *((int *)where) = POSIX_MALLOC_THRESHOLD; + break; + + case PCRE_CONFIG_MATCH_LIMIT: + *((unsigned int *)where) = MATCH_LIMIT; + break; + + case PCRE_CONFIG_MATCH_LIMIT_RECURSION: + *((unsigned int *)where) = MATCH_LIMIT_RECURSION; + break; + + case PCRE_CONFIG_STACKRECURSE: +#ifdef NO_RECURSE + *((int *)where) = 0; +#else + *((int *)where) = 1; +#endif + break; + + default: return PCRE_ERROR_BADOPTION; + } + +return 0; +} + +/* End of pcre_config.c */ diff --git a/Engine/lib/pcre/pcre_dfa_exec.c b/Engine/lib/pcre/pcre_dfa_exec.c new file mode 100644 index 000000000..6283ff781 --- /dev/null +++ b/Engine/lib/pcre/pcre_dfa_exec.c @@ -0,0 +1,2896 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2008 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains the external function pcre_dfa_exec(), which is an +alternative matching function that uses a sort of DFA algorithm (not a true +FSM). This is NOT Perl- compatible, but it has advantages in certain +applications. */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define NLBLOCK md /* Block containing newline information */ +#define PSSTART start_subject /* Field containing processed string start */ +#define PSEND end_subject /* Field containing processed string end */ + +#include "pcre_internal.h" + + +/* For use to indent debugging output */ + +#define SP " " + + + +/************************************************* +* Code parameters and static tables * +*************************************************/ + +/* These are offsets that are used to turn the OP_TYPESTAR and friends opcodes +into others, under special conditions. A gap of 20 between the blocks should be +enough. The resulting opcodes don't have to be less than 256 because they are +never stored, so we push them well clear of the normal opcodes. */ + +#define OP_PROP_EXTRA 300 +#define OP_EXTUNI_EXTRA 320 +#define OP_ANYNL_EXTRA 340 +#define OP_HSPACE_EXTRA 360 +#define OP_VSPACE_EXTRA 380 + + +/* This table identifies those opcodes that are followed immediately by a +character that is to be tested in some way. This makes is possible to +centralize the loading of these characters. In the case of Type * etc, the +"character" is the opcode for \D, \d, \S, \s, \W, or \w, which will always be a +small value. ***NOTE*** If the start of this table is modified, the two tables +that follow must also be modified. */ + +static uschar coptable[] = { + 0, /* End */ + 0, 0, 0, 0, 0, /* \A, \G, \K, \B, \b */ + 0, 0, 0, 0, 0, 0, /* \D, \d, \S, \s, \W, \w */ + 0, 0, /* Any, Anybyte */ + 0, 0, 0, /* NOTPROP, PROP, EXTUNI */ + 0, 0, 0, 0, 0, /* \R, \H, \h, \V, \v */ + 0, 0, 0, 0, 0, /* \Z, \z, Opt, ^, $ */ + 1, /* Char */ + 1, /* Charnc */ + 1, /* not */ + /* Positive single-char repeats */ + 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */ + 3, 3, 3, /* upto, minupto, exact */ + 1, 1, 1, 3, /* *+, ++, ?+, upto+ */ + /* Negative single-char repeats - only for chars < 256 */ + 1, 1, 1, 1, 1, 1, /* NOT *, *?, +, +?, ?, ?? */ + 3, 3, 3, /* NOT upto, minupto, exact */ + 1, 1, 1, 3, /* NOT *+, ++, ?+, updo+ */ + /* Positive type repeats */ + 1, 1, 1, 1, 1, 1, /* Type *, *?, +, +?, ?, ?? */ + 3, 3, 3, /* Type upto, minupto, exact */ + 1, 1, 1, 3, /* Type *+, ++, ?+, upto+ */ + /* Character class & ref repeats */ + 0, 0, 0, 0, 0, 0, /* *, *?, +, +?, ?, ?? */ + 0, 0, /* CRRANGE, CRMINRANGE */ + 0, /* CLASS */ + 0, /* NCLASS */ + 0, /* XCLASS - variable length */ + 0, /* REF */ + 0, /* RECURSE */ + 0, /* CALLOUT */ + 0, /* Alt */ + 0, /* Ket */ + 0, /* KetRmax */ + 0, /* KetRmin */ + 0, /* Assert */ + 0, /* Assert not */ + 0, /* Assert behind */ + 0, /* Assert behind not */ + 0, /* Reverse */ + 0, 0, 0, 0, /* ONCE, BRA, CBRA, COND */ + 0, 0, 0, /* SBRA, SCBRA, SCOND */ + 0, /* CREF */ + 0, /* RREF */ + 0, /* DEF */ + 0, 0, /* BRAZERO, BRAMINZERO */ + 0, 0, 0, 0, /* PRUNE, SKIP, THEN, COMMIT */ + 0, 0 /* FAIL, ACCEPT */ +}; + +/* These 2 tables allow for compact code for testing for \D, \d, \S, \s, \W, +and \w */ + +static uschar toptable1[] = { + 0, 0, 0, 0, 0, 0, + ctype_digit, ctype_digit, + ctype_space, ctype_space, + ctype_word, ctype_word, + 0 /* OP_ANY */ +}; + +static uschar toptable2[] = { + 0, 0, 0, 0, 0, 0, + ctype_digit, 0, + ctype_space, 0, + ctype_word, 0, + 1 /* OP_ANY */ +}; + + +/* Structure for holding data about a particular state, which is in effect the +current data for an active path through the match tree. It must consist +entirely of ints because the working vector we are passed, and which we put +these structures in, is a vector of ints. */ + +typedef struct stateblock { + int offset; /* Offset to opcode */ + int count; /* Count for repeats */ + int ims; /* ims flag bits */ + int data; /* Some use extra data */ +} stateblock; + +#define INTS_PER_STATEBLOCK (sizeof(stateblock)/sizeof(int)) + + +#ifdef DEBUG +/************************************************* +* Print character string * +*************************************************/ + +/* Character string printing function for debugging. + +Arguments: + p points to string + length number of bytes + f where to print + +Returns: nothing +*/ + +static void +pchars(unsigned char *p, int length, FILE *f) +{ +int c; +while (length-- > 0) + { + if (isprint(c = *(p++))) + fprintf(f, "%c", c); + else + fprintf(f, "\\x%02x", c); + } +} +#endif + + + +/************************************************* +* Execute a Regular Expression - DFA engine * +*************************************************/ + +/* This internal function applies a compiled pattern to a subject string, +starting at a given point, using a DFA engine. This function is called from the +external one, possibly multiple times if the pattern is not anchored. The +function calls itself recursively for some kinds of subpattern. + +Arguments: + md the match_data block with fixed information + this_start_code the opening bracket of this subexpression's code + current_subject where we currently are in the subject string + start_offset start offset in the subject string + offsets vector to contain the matching string offsets + offsetcount size of same + workspace vector of workspace + wscount size of same + ims the current ims flags + rlevel function call recursion level + recursing regex recursive call level + +Returns: > 0 => + = 0 => + -1 => failed to match + < -1 => some kind of unexpected problem + +The following macros are used for adding states to the two state vectors (one +for the current character, one for the following character). */ + +#define ADD_ACTIVE(x,y) \ + if (active_count++ < wscount) \ + { \ + next_active_state->offset = (x); \ + next_active_state->count = (y); \ + next_active_state->ims = ims; \ + next_active_state++; \ + DPRINTF(("%.*sADD_ACTIVE(%d,%d)\n", rlevel*2-2, SP, (x), (y))); \ + } \ + else return PCRE_ERROR_DFA_WSSIZE + +#define ADD_ACTIVE_DATA(x,y,z) \ + if (active_count++ < wscount) \ + { \ + next_active_state->offset = (x); \ + next_active_state->count = (y); \ + next_active_state->ims = ims; \ + next_active_state->data = (z); \ + next_active_state++; \ + DPRINTF(("%.*sADD_ACTIVE_DATA(%d,%d,%d)\n", rlevel*2-2, SP, (x), (y), (z))); \ + } \ + else return PCRE_ERROR_DFA_WSSIZE + +#define ADD_NEW(x,y) \ + if (new_count++ < wscount) \ + { \ + next_new_state->offset = (x); \ + next_new_state->count = (y); \ + next_new_state->ims = ims; \ + next_new_state++; \ + DPRINTF(("%.*sADD_NEW(%d,%d)\n", rlevel*2-2, SP, (x), (y))); \ + } \ + else return PCRE_ERROR_DFA_WSSIZE + +#define ADD_NEW_DATA(x,y,z) \ + if (new_count++ < wscount) \ + { \ + next_new_state->offset = (x); \ + next_new_state->count = (y); \ + next_new_state->ims = ims; \ + next_new_state->data = (z); \ + next_new_state++; \ + DPRINTF(("%.*sADD_NEW_DATA(%d,%d,%d)\n", rlevel*2-2, SP, (x), (y), (z))); \ + } \ + else return PCRE_ERROR_DFA_WSSIZE + +/* And now, here is the code */ + +static int +internal_dfa_exec( + dfa_match_data *md, + const uschar *this_start_code, + const uschar *current_subject, + int start_offset, + int *offsets, + int offsetcount, + int *workspace, + int wscount, + int ims, + int rlevel, + int recursing) +{ +stateblock *active_states, *new_states, *temp_states; +stateblock *next_active_state, *next_new_state; + +const uschar *ctypes, *lcc, *fcc; +const uschar *ptr; +const uschar *end_code, *first_op; + +int active_count, new_count, match_count; + +/* Some fields in the md block are frequently referenced, so we load them into +independent variables in the hope that this will perform better. */ + +const uschar *start_subject = md->start_subject; +const uschar *end_subject = md->end_subject; +const uschar *start_code = md->start_code; + +#ifdef SUPPORT_UTF8 +BOOL utf8 = (md->poptions & PCRE_UTF8) != 0; +#else +BOOL utf8 = FALSE; +#endif + +rlevel++; +offsetcount &= (-2); + +wscount -= 2; +wscount = (wscount - (wscount % (INTS_PER_STATEBLOCK * 2))) / + (2 * INTS_PER_STATEBLOCK); + +DPRINTF(("\n%.*s---------------------\n" + "%.*sCall to internal_dfa_exec f=%d r=%d\n", + rlevel*2-2, SP, rlevel*2-2, SP, rlevel, recursing)); + +ctypes = md->tables + ctypes_offset; +lcc = md->tables + lcc_offset; +fcc = md->tables + fcc_offset; + +match_count = PCRE_ERROR_NOMATCH; /* A negative number */ + +active_states = (stateblock *)(workspace + 2); +next_new_state = new_states = active_states + wscount; +new_count = 0; + +first_op = this_start_code + 1 + LINK_SIZE + + ((*this_start_code == OP_CBRA || *this_start_code == OP_SCBRA)? 2:0); + +/* The first thing in any (sub) pattern is a bracket of some sort. Push all +the alternative states onto the list, and find out where the end is. This +makes is possible to use this function recursively, when we want to stop at a +matching internal ket rather than at the end. + +If the first opcode in the first alternative is OP_REVERSE, we are dealing with +a backward assertion. In that case, we have to find out the maximum amount to +move back, and set up each alternative appropriately. */ + +if (*first_op == OP_REVERSE) + { + int max_back = 0; + int gone_back; + + end_code = this_start_code; + do + { + int back = GET(end_code, 2+LINK_SIZE); + if (back > max_back) max_back = back; + end_code += GET(end_code, 1); + } + while (*end_code == OP_ALT); + + /* If we can't go back the amount required for the longest lookbehind + pattern, go back as far as we can; some alternatives may still be viable. */ + +#ifdef SUPPORT_UTF8 + /* In character mode we have to step back character by character */ + + if (utf8) + { + for (gone_back = 0; gone_back < max_back; gone_back++) + { + if (current_subject <= start_subject) break; + current_subject--; + while (current_subject > start_subject && + (*current_subject & 0xc0) == 0x80) + current_subject--; + } + } + else +#endif + + /* In byte-mode we can do this quickly. */ + + { + gone_back = (current_subject - max_back < start_subject)? + current_subject - start_subject : max_back; + current_subject -= gone_back; + } + + /* Now we can process the individual branches. */ + + end_code = this_start_code; + do + { + int back = GET(end_code, 2+LINK_SIZE); + if (back <= gone_back) + { + int bstate = end_code - start_code + 2 + 2*LINK_SIZE; + ADD_NEW_DATA(-bstate, 0, gone_back - back); + } + end_code += GET(end_code, 1); + } + while (*end_code == OP_ALT); + } + +/* This is the code for a "normal" subpattern (not a backward assertion). The +start of a whole pattern is always one of these. If we are at the top level, +we may be asked to restart matching from the same point that we reached for a +previous partial match. We still have to scan through the top-level branches to +find the end state. */ + +else + { + end_code = this_start_code; + + /* Restarting */ + + if (rlevel == 1 && (md->moptions & PCRE_DFA_RESTART) != 0) + { + do { end_code += GET(end_code, 1); } while (*end_code == OP_ALT); + new_count = workspace[1]; + if (!workspace[0]) + memcpy(new_states, active_states, new_count * sizeof(stateblock)); + } + + /* Not restarting */ + + else + { + int length = 1 + LINK_SIZE + + ((*this_start_code == OP_CBRA || *this_start_code == OP_SCBRA)? 2:0); + do + { + ADD_NEW(end_code - start_code + length, 0); + end_code += GET(end_code, 1); + length = 1 + LINK_SIZE; + } + while (*end_code == OP_ALT); + } + } + +workspace[0] = 0; /* Bit indicating which vector is current */ + +DPRINTF(("%.*sEnd state = %d\n", rlevel*2-2, SP, end_code - start_code)); + +/* Loop for scanning the subject */ + +ptr = current_subject; +for (;;) + { + int i, j; + int clen, dlen; + unsigned int c, d; + + /* Make the new state list into the active state list and empty the + new state list. */ + + temp_states = active_states; + active_states = new_states; + new_states = temp_states; + active_count = new_count; + new_count = 0; + + workspace[0] ^= 1; /* Remember for the restarting feature */ + workspace[1] = active_count; + +#ifdef DEBUG + printf("%.*sNext character: rest of subject = \"", rlevel*2-2, SP); + pchars((uschar *)ptr, strlen((char *)ptr), stdout); + printf("\"\n"); + + printf("%.*sActive states: ", rlevel*2-2, SP); + for (i = 0; i < active_count; i++) + printf("%d/%d ", active_states[i].offset, active_states[i].count); + printf("\n"); +#endif + + /* Set the pointers for adding new states */ + + next_active_state = active_states + active_count; + next_new_state = new_states; + + /* Load the current character from the subject outside the loop, as many + different states may want to look at it, and we assume that at least one + will. */ + + if (ptr < end_subject) + { + clen = 1; /* Number of bytes in the character */ +#ifdef SUPPORT_UTF8 + if (utf8) { GETCHARLEN(c, ptr, clen); } else +#endif /* SUPPORT_UTF8 */ + c = *ptr; + } + else + { + clen = 0; /* This indicates the end of the subject */ + c = NOTACHAR; /* This value should never actually be used */ + } + + /* Scan up the active states and act on each one. The result of an action + may be to add more states to the currently active list (e.g. on hitting a + parenthesis) or it may be to put states on the new list, for considering + when we move the character pointer on. */ + + for (i = 0; i < active_count; i++) + { + stateblock *current_state = active_states + i; + const uschar *code; + int state_offset = current_state->offset; + int count, codevalue; +#ifdef SUPPORT_UCP + int chartype, script; +#endif + +#ifdef DEBUG + printf ("%.*sProcessing state %d c=", rlevel*2-2, SP, state_offset); + if (clen == 0) printf("EOL\n"); + else if (c > 32 && c < 127) printf("'%c'\n", c); + else printf("0x%02x\n", c); +#endif + + /* This variable is referred to implicity in the ADD_xxx macros. */ + + ims = current_state->ims; + + /* A negative offset is a special case meaning "hold off going to this + (negated) state until the number of characters in the data field have + been skipped". */ + + if (state_offset < 0) + { + if (current_state->data > 0) + { + DPRINTF(("%.*sSkipping this character\n", rlevel*2-2, SP)); + ADD_NEW_DATA(state_offset, current_state->count, + current_state->data - 1); + continue; + } + else + { + current_state->offset = state_offset = -state_offset; + } + } + + /* Check for a duplicate state with the same count, and skip if found. */ + + for (j = 0; j < i; j++) + { + if (active_states[j].offset == state_offset && + active_states[j].count == current_state->count) + { + DPRINTF(("%.*sDuplicate state: skipped\n", rlevel*2-2, SP)); + goto NEXT_ACTIVE_STATE; + } + } + + /* The state offset is the offset to the opcode */ + + code = start_code + state_offset; + codevalue = *code; + + /* If this opcode is followed by an inline character, load it. It is + tempting to test for the presence of a subject character here, but that + is wrong, because sometimes zero repetitions of the subject are + permitted. + + We also use this mechanism for opcodes such as OP_TYPEPLUS that take an + argument that is not a data character - but is always one byte long. We + have to take special action to deal with \P, \p, \H, \h, \V, \v and \X in + this case. To keep the other cases fast, convert these ones to new opcodes. + */ + + if (coptable[codevalue] > 0) + { + dlen = 1; +#ifdef SUPPORT_UTF8 + if (utf8) { GETCHARLEN(d, (code + coptable[codevalue]), dlen); } else +#endif /* SUPPORT_UTF8 */ + d = code[coptable[codevalue]]; + if (codevalue >= OP_TYPESTAR) + { + switch(d) + { + case OP_ANYBYTE: return PCRE_ERROR_DFA_UITEM; + case OP_NOTPROP: + case OP_PROP: codevalue += OP_PROP_EXTRA; break; + case OP_ANYNL: codevalue += OP_ANYNL_EXTRA; break; + case OP_EXTUNI: codevalue += OP_EXTUNI_EXTRA; break; + case OP_NOT_HSPACE: + case OP_HSPACE: codevalue += OP_HSPACE_EXTRA; break; + case OP_NOT_VSPACE: + case OP_VSPACE: codevalue += OP_VSPACE_EXTRA; break; + default: break; + } + } + } + else + { + dlen = 0; /* Not strictly necessary, but compilers moan */ + d = NOTACHAR; /* if these variables are not set. */ + } + + + /* Now process the individual opcodes */ + + switch (codevalue) + { + +/* ========================================================================== */ + /* Reached a closing bracket. If not at the end of the pattern, carry + on with the next opcode. Otherwise, unless we have an empty string and + PCRE_NOTEMPTY is set, save the match data, shifting up all previous + matches so we always have the longest first. */ + + case OP_KET: + case OP_KETRMIN: + case OP_KETRMAX: + if (code != end_code) + { + ADD_ACTIVE(state_offset + 1 + LINK_SIZE, 0); + if (codevalue != OP_KET) + { + ADD_ACTIVE(state_offset - GET(code, 1), 0); + } + } + else if (ptr > current_subject || (md->moptions & PCRE_NOTEMPTY) == 0) + { + if (match_count < 0) match_count = (offsetcount >= 2)? 1 : 0; + else if (match_count > 0 && ++match_count * 2 >= offsetcount) + match_count = 0; + count = ((match_count == 0)? offsetcount : match_count * 2) - 2; + if (count > 0) memmove(offsets + 2, offsets, count * sizeof(int)); + if (offsetcount >= 2) + { + offsets[0] = current_subject - start_subject; + offsets[1] = ptr - start_subject; + DPRINTF(("%.*sSet matched string = \"%.*s\"\n", rlevel*2-2, SP, + offsets[1] - offsets[0], current_subject)); + } + if ((md->moptions & PCRE_DFA_SHORTEST) != 0) + { + DPRINTF(("%.*sEnd of internal_dfa_exec %d: returning %d\n" + "%.*s---------------------\n\n", rlevel*2-2, SP, rlevel, + match_count, rlevel*2-2, SP)); + return match_count; + } + } + break; + +/* ========================================================================== */ + /* These opcodes add to the current list of states without looking + at the current character. */ + + /*-----------------------------------------------------------------*/ + case OP_ALT: + do { code += GET(code, 1); } while (*code == OP_ALT); + ADD_ACTIVE(code - start_code, 0); + break; + + /*-----------------------------------------------------------------*/ + case OP_BRA: + case OP_SBRA: + do + { + ADD_ACTIVE(code - start_code + 1 + LINK_SIZE, 0); + code += GET(code, 1); + } + while (*code == OP_ALT); + break; + + /*-----------------------------------------------------------------*/ + case OP_CBRA: + case OP_SCBRA: + ADD_ACTIVE(code - start_code + 3 + LINK_SIZE, 0); + code += GET(code, 1); + while (*code == OP_ALT) + { + ADD_ACTIVE(code - start_code + 1 + LINK_SIZE, 0); + code += GET(code, 1); + } + break; + + /*-----------------------------------------------------------------*/ + case OP_BRAZERO: + case OP_BRAMINZERO: + ADD_ACTIVE(state_offset + 1, 0); + code += 1 + GET(code, 2); + while (*code == OP_ALT) code += GET(code, 1); + ADD_ACTIVE(code - start_code + 1 + LINK_SIZE, 0); + break; + + /*-----------------------------------------------------------------*/ + case OP_CIRC: + if ((ptr == start_subject && (md->moptions & PCRE_NOTBOL) == 0) || + ((ims & PCRE_MULTILINE) != 0 && + ptr != end_subject && + WAS_NEWLINE(ptr))) + { ADD_ACTIVE(state_offset + 1, 0); } + break; + + /*-----------------------------------------------------------------*/ + case OP_EOD: + if (ptr >= end_subject) { ADD_ACTIVE(state_offset + 1, 0); } + break; + + /*-----------------------------------------------------------------*/ + case OP_OPT: + ims = code[1]; + ADD_ACTIVE(state_offset + 2, 0); + break; + + /*-----------------------------------------------------------------*/ + case OP_SOD: + if (ptr == start_subject) { ADD_ACTIVE(state_offset + 1, 0); } + break; + + /*-----------------------------------------------------------------*/ + case OP_SOM: + if (ptr == start_subject + start_offset) { ADD_ACTIVE(state_offset + 1, 0); } + break; + + +/* ========================================================================== */ + /* These opcodes inspect the next subject character, and sometimes + the previous one as well, but do not have an argument. The variable + clen contains the length of the current character and is zero if we are + at the end of the subject. */ + + /*-----------------------------------------------------------------*/ + case OP_ANY: + if (clen > 0 && ((ims & PCRE_DOTALL) != 0 || !IS_NEWLINE(ptr))) + { ADD_NEW(state_offset + 1, 0); } + break; + + /*-----------------------------------------------------------------*/ + case OP_EODN: + if (clen == 0 || (IS_NEWLINE(ptr) && ptr == end_subject - md->nllen)) + { ADD_ACTIVE(state_offset + 1, 0); } + break; + + /*-----------------------------------------------------------------*/ + case OP_DOLL: + if ((md->moptions & PCRE_NOTEOL) == 0) + { + if (clen == 0 || + (IS_NEWLINE(ptr) && + ((ims & PCRE_MULTILINE) != 0 || ptr == end_subject - md->nllen) + )) + { ADD_ACTIVE(state_offset + 1, 0); } + } + else if ((ims & PCRE_MULTILINE) != 0 && IS_NEWLINE(ptr)) + { ADD_ACTIVE(state_offset + 1, 0); } + break; + + /*-----------------------------------------------------------------*/ + + case OP_DIGIT: + case OP_WHITESPACE: + case OP_WORDCHAR: + if (clen > 0 && c < 256 && + ((ctypes[c] & toptable1[codevalue]) ^ toptable2[codevalue]) != 0) + { ADD_NEW(state_offset + 1, 0); } + break; + + /*-----------------------------------------------------------------*/ + case OP_NOT_DIGIT: + case OP_NOT_WHITESPACE: + case OP_NOT_WORDCHAR: + if (clen > 0 && (c >= 256 || + ((ctypes[c] & toptable1[codevalue]) ^ toptable2[codevalue]) != 0)) + { ADD_NEW(state_offset + 1, 0); } + break; + + /*-----------------------------------------------------------------*/ + case OP_WORD_BOUNDARY: + case OP_NOT_WORD_BOUNDARY: + { + int left_word, right_word; + + if (ptr > start_subject) + { + const uschar *temp = ptr - 1; +#ifdef SUPPORT_UTF8 + if (utf8) BACKCHAR(temp); +#endif + GETCHARTEST(d, temp); + left_word = d < 256 && (ctypes[d] & ctype_word) != 0; + } + else left_word = 0; + + if (clen > 0) right_word = c < 256 && (ctypes[c] & ctype_word) != 0; + else right_word = 0; + + if ((left_word == right_word) == (codevalue == OP_NOT_WORD_BOUNDARY)) + { ADD_ACTIVE(state_offset + 1, 0); } + } + break; + + + /*-----------------------------------------------------------------*/ + /* Check the next character by Unicode property. We will get here only + if the support is in the binary; otherwise a compile-time error occurs. + */ + +#ifdef SUPPORT_UCP + case OP_PROP: + case OP_NOTPROP: + if (clen > 0) + { + BOOL OK; + int category = _pcre_ucp_findprop(c, &chartype, &script); + switch(code[1]) + { + case PT_ANY: + OK = TRUE; + break; + + case PT_LAMP: + OK = chartype == ucp_Lu || chartype == ucp_Ll || chartype == ucp_Lt; + break; + + case PT_GC: + OK = category == code[2]; + break; + + case PT_PC: + OK = chartype == code[2]; + break; + + case PT_SC: + OK = script == code[2]; + break; + + /* Should never occur, but keep compilers from grumbling. */ + + default: + OK = codevalue != OP_PROP; + break; + } + + if (OK == (codevalue == OP_PROP)) { ADD_NEW(state_offset + 3, 0); } + } + break; +#endif + + + +/* ========================================================================== */ + /* These opcodes likewise inspect the subject character, but have an + argument that is not a data character. It is one of these opcodes: + OP_ANY, OP_DIGIT, OP_NOT_DIGIT, OP_WHITESPACE, OP_NOT_SPACE, OP_WORDCHAR, + OP_NOT_WORDCHAR. The value is loaded into d. */ + + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEPOSPLUS: + count = current_state->count; /* Already matched */ + if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); } + if (clen > 0) + { + if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) || + (c < 256 && + (d != OP_ANY || + (ims & PCRE_DOTALL) != 0 || + !IS_NEWLINE(ptr) + ) && + ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0)) + { + if (count > 0 && codevalue == OP_TYPEPOSPLUS) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + count++; + ADD_NEW(state_offset, count); + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + case OP_TYPEPOSQUERY: + ADD_ACTIVE(state_offset + 2, 0); + if (clen > 0) + { + if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) || + (c < 256 && + (d != OP_ANY || + (ims & PCRE_DOTALL) != 0 || + !IS_NEWLINE(ptr) + ) && + ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0)) + { + if (codevalue == OP_TYPEPOSQUERY) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + ADD_NEW(state_offset + 2, 0); + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPOSSTAR: + ADD_ACTIVE(state_offset + 2, 0); + if (clen > 0) + { + if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) || + (c < 256 && + (d != OP_ANY || + (ims & PCRE_DOTALL) != 0 || + !IS_NEWLINE(ptr) + ) && + ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0)) + { + if (codevalue == OP_TYPEPOSSTAR) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + ADD_NEW(state_offset, 0); + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_TYPEEXACT: + count = current_state->count; /* Number already matched */ + if (clen > 0) + { + if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) || + (c < 256 && + (d != OP_ANY || + (ims & PCRE_DOTALL) != 0 || + !IS_NEWLINE(ptr) + ) && + ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0)) + { + if (++count >= GET2(code, 1)) + { ADD_NEW(state_offset + 4, 0); } + else + { ADD_NEW(state_offset, count); } + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + case OP_TYPEPOSUPTO: + ADD_ACTIVE(state_offset + 4, 0); + count = current_state->count; /* Number already matched */ + if (clen > 0) + { + if ((c >= 256 && d != OP_DIGIT && d != OP_WHITESPACE && d != OP_WORDCHAR) || + (c < 256 && + (d != OP_ANY || + (ims & PCRE_DOTALL) != 0 || + !IS_NEWLINE(ptr) + ) && + ((ctypes[c] & toptable1[d]) ^ toptable2[d]) != 0)) + { + if (codevalue == OP_TYPEPOSUPTO) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + if (++count >= GET2(code, 1)) + { ADD_NEW(state_offset + 4, 0); } + else + { ADD_NEW(state_offset, count); } + } + } + break; + +/* ========================================================================== */ + /* These are virtual opcodes that are used when something like + OP_TYPEPLUS has OP_PROP, OP_NOTPROP, OP_ANYNL, or OP_EXTUNI as its + argument. It keeps the code above fast for the other cases. The argument + is in the d variable. */ + +#ifdef SUPPORT_UCP + case OP_PROP_EXTRA + OP_TYPEPLUS: + case OP_PROP_EXTRA + OP_TYPEMINPLUS: + case OP_PROP_EXTRA + OP_TYPEPOSPLUS: + count = current_state->count; /* Already matched */ + if (count > 0) { ADD_ACTIVE(state_offset + 4, 0); } + if (clen > 0) + { + BOOL OK; + int category = _pcre_ucp_findprop(c, &chartype, &script); + switch(code[2]) + { + case PT_ANY: + OK = TRUE; + break; + + case PT_LAMP: + OK = chartype == ucp_Lu || chartype == ucp_Ll || chartype == ucp_Lt; + break; + + case PT_GC: + OK = category == code[3]; + break; + + case PT_PC: + OK = chartype == code[3]; + break; + + case PT_SC: + OK = script == code[3]; + break; + + /* Should never occur, but keep compilers from grumbling. */ + + default: + OK = codevalue != OP_PROP; + break; + } + + if (OK == (d == OP_PROP)) + { + if (count > 0 && codevalue == OP_PROP_EXTRA + OP_TYPEPOSPLUS) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + count++; + ADD_NEW(state_offset, count); + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_EXTUNI_EXTRA + OP_TYPEPLUS: + case OP_EXTUNI_EXTRA + OP_TYPEMINPLUS: + case OP_EXTUNI_EXTRA + OP_TYPEPOSPLUS: + count = current_state->count; /* Already matched */ + if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); } + if (clen > 0 && _pcre_ucp_findprop(c, &chartype, &script) != ucp_M) + { + const uschar *nptr = ptr + clen; + int ncount = 0; + if (count > 0 && codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSPLUS) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + while (nptr < end_subject) + { + int nd; + int ndlen = 1; + GETCHARLEN(nd, nptr, ndlen); + if (_pcre_ucp_findprop(nd, &chartype, &script) != ucp_M) break; + ncount++; + nptr += ndlen; + } + count++; + ADD_NEW_DATA(-state_offset, count, ncount); + } + break; +#endif + + /*-----------------------------------------------------------------*/ + case OP_ANYNL_EXTRA + OP_TYPEPLUS: + case OP_ANYNL_EXTRA + OP_TYPEMINPLUS: + case OP_ANYNL_EXTRA + OP_TYPEPOSPLUS: + count = current_state->count; /* Already matched */ + if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); } + if (clen > 0) + { + int ncount = 0; + switch (c) + { + case 0x000b: + case 0x000c: + case 0x0085: + case 0x2028: + case 0x2029: + if ((md->moptions & PCRE_BSR_ANYCRLF) != 0) break; + goto ANYNL01; + + case 0x000d: + if (ptr + 1 < end_subject && ptr[1] == 0x0a) ncount = 1; + /* Fall through */ + + ANYNL01: + case 0x000a: + if (count > 0 && codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSPLUS) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + count++; + ADD_NEW_DATA(-state_offset, count, ncount); + break; + + default: + break; + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_VSPACE_EXTRA + OP_TYPEPLUS: + case OP_VSPACE_EXTRA + OP_TYPEMINPLUS: + case OP_VSPACE_EXTRA + OP_TYPEPOSPLUS: + count = current_state->count; /* Already matched */ + if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); } + if (clen > 0) + { + BOOL OK; + switch (c) + { + case 0x000a: + case 0x000b: + case 0x000c: + case 0x000d: + case 0x0085: + case 0x2028: + case 0x2029: + OK = TRUE; + break; + + default: + OK = FALSE; + break; + } + + if (OK == (d == OP_VSPACE)) + { + if (count > 0 && codevalue == OP_VSPACE_EXTRA + OP_TYPEPOSPLUS) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + count++; + ADD_NEW_DATA(-state_offset, count, 0); + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_HSPACE_EXTRA + OP_TYPEPLUS: + case OP_HSPACE_EXTRA + OP_TYPEMINPLUS: + case OP_HSPACE_EXTRA + OP_TYPEPOSPLUS: + count = current_state->count; /* Already matched */ + if (count > 0) { ADD_ACTIVE(state_offset + 2, 0); } + if (clen > 0) + { + BOOL OK; + switch (c) + { + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ + OK = TRUE; + break; + + default: + OK = FALSE; + break; + } + + if (OK == (d == OP_HSPACE)) + { + if (count > 0 && codevalue == OP_HSPACE_EXTRA + OP_TYPEPOSPLUS) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + count++; + ADD_NEW_DATA(-state_offset, count, 0); + } + } + break; + + /*-----------------------------------------------------------------*/ +#ifdef SUPPORT_UCP + case OP_PROP_EXTRA + OP_TYPEQUERY: + case OP_PROP_EXTRA + OP_TYPEMINQUERY: + case OP_PROP_EXTRA + OP_TYPEPOSQUERY: + count = 4; + goto QS1; + + case OP_PROP_EXTRA + OP_TYPESTAR: + case OP_PROP_EXTRA + OP_TYPEMINSTAR: + case OP_PROP_EXTRA + OP_TYPEPOSSTAR: + count = 0; + + QS1: + + ADD_ACTIVE(state_offset + 4, 0); + if (clen > 0) + { + BOOL OK; + int category = _pcre_ucp_findprop(c, &chartype, &script); + switch(code[2]) + { + case PT_ANY: + OK = TRUE; + break; + + case PT_LAMP: + OK = chartype == ucp_Lu || chartype == ucp_Ll || chartype == ucp_Lt; + break; + + case PT_GC: + OK = category == code[3]; + break; + + case PT_PC: + OK = chartype == code[3]; + break; + + case PT_SC: + OK = script == code[3]; + break; + + /* Should never occur, but keep compilers from grumbling. */ + + default: + OK = codevalue != OP_PROP; + break; + } + + if (OK == (d == OP_PROP)) + { + if (codevalue == OP_PROP_EXTRA + OP_TYPEPOSSTAR || + codevalue == OP_PROP_EXTRA + OP_TYPEPOSQUERY) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + ADD_NEW(state_offset + count, 0); + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_EXTUNI_EXTRA + OP_TYPEQUERY: + case OP_EXTUNI_EXTRA + OP_TYPEMINQUERY: + case OP_EXTUNI_EXTRA + OP_TYPEPOSQUERY: + count = 2; + goto QS2; + + case OP_EXTUNI_EXTRA + OP_TYPESTAR: + case OP_EXTUNI_EXTRA + OP_TYPEMINSTAR: + case OP_EXTUNI_EXTRA + OP_TYPEPOSSTAR: + count = 0; + + QS2: + + ADD_ACTIVE(state_offset + 2, 0); + if (clen > 0 && _pcre_ucp_findprop(c, &chartype, &script) != ucp_M) + { + const uschar *nptr = ptr + clen; + int ncount = 0; + if (codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSSTAR || + codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSQUERY) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + while (nptr < end_subject) + { + int nd; + int ndlen = 1; + GETCHARLEN(nd, nptr, ndlen); + if (_pcre_ucp_findprop(nd, &chartype, &script) != ucp_M) break; + ncount++; + nptr += ndlen; + } + ADD_NEW_DATA(-(state_offset + count), 0, ncount); + } + break; +#endif + + /*-----------------------------------------------------------------*/ + case OP_ANYNL_EXTRA + OP_TYPEQUERY: + case OP_ANYNL_EXTRA + OP_TYPEMINQUERY: + case OP_ANYNL_EXTRA + OP_TYPEPOSQUERY: + count = 2; + goto QS3; + + case OP_ANYNL_EXTRA + OP_TYPESTAR: + case OP_ANYNL_EXTRA + OP_TYPEMINSTAR: + case OP_ANYNL_EXTRA + OP_TYPEPOSSTAR: + count = 0; + + QS3: + ADD_ACTIVE(state_offset + 2, 0); + if (clen > 0) + { + int ncount = 0; + switch (c) + { + case 0x000b: + case 0x000c: + case 0x0085: + case 0x2028: + case 0x2029: + if ((md->moptions & PCRE_BSR_ANYCRLF) != 0) break; + goto ANYNL02; + + case 0x000d: + if (ptr + 1 < end_subject && ptr[1] == 0x0a) ncount = 1; + /* Fall through */ + + ANYNL02: + case 0x000a: + if (codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSSTAR || + codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSQUERY) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + ADD_NEW_DATA(-(state_offset + count), 0, ncount); + break; + + default: + break; + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_VSPACE_EXTRA + OP_TYPEQUERY: + case OP_VSPACE_EXTRA + OP_TYPEMINQUERY: + case OP_VSPACE_EXTRA + OP_TYPEPOSQUERY: + count = 2; + goto QS4; + + case OP_VSPACE_EXTRA + OP_TYPESTAR: + case OP_VSPACE_EXTRA + OP_TYPEMINSTAR: + case OP_VSPACE_EXTRA + OP_TYPEPOSSTAR: + count = 0; + + QS4: + ADD_ACTIVE(state_offset + 2, 0); + if (clen > 0) + { + BOOL OK; + switch (c) + { + case 0x000a: + case 0x000b: + case 0x000c: + case 0x000d: + case 0x0085: + case 0x2028: + case 0x2029: + OK = TRUE; + break; + + default: + OK = FALSE; + break; + } + if (OK == (d == OP_VSPACE)) + { + if (codevalue == OP_VSPACE_EXTRA + OP_TYPEPOSSTAR || + codevalue == OP_VSPACE_EXTRA + OP_TYPEPOSQUERY) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + ADD_NEW_DATA(-(state_offset + count), 0, 0); + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_HSPACE_EXTRA + OP_TYPEQUERY: + case OP_HSPACE_EXTRA + OP_TYPEMINQUERY: + case OP_HSPACE_EXTRA + OP_TYPEPOSQUERY: + count = 2; + goto QS5; + + case OP_HSPACE_EXTRA + OP_TYPESTAR: + case OP_HSPACE_EXTRA + OP_TYPEMINSTAR: + case OP_HSPACE_EXTRA + OP_TYPEPOSSTAR: + count = 0; + + QS5: + ADD_ACTIVE(state_offset + 2, 0); + if (clen > 0) + { + BOOL OK; + switch (c) + { + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ + OK = TRUE; + break; + + default: + OK = FALSE; + break; + } + + if (OK == (d == OP_HSPACE)) + { + if (codevalue == OP_HSPACE_EXTRA + OP_TYPEPOSSTAR || + codevalue == OP_HSPACE_EXTRA + OP_TYPEPOSQUERY) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + ADD_NEW_DATA(-(state_offset + count), 0, 0); + } + } + break; + + /*-----------------------------------------------------------------*/ +#ifdef SUPPORT_UCP + case OP_PROP_EXTRA + OP_TYPEEXACT: + case OP_PROP_EXTRA + OP_TYPEUPTO: + case OP_PROP_EXTRA + OP_TYPEMINUPTO: + case OP_PROP_EXTRA + OP_TYPEPOSUPTO: + if (codevalue != OP_PROP_EXTRA + OP_TYPEEXACT) + { ADD_ACTIVE(state_offset + 6, 0); } + count = current_state->count; /* Number already matched */ + if (clen > 0) + { + BOOL OK; + int category = _pcre_ucp_findprop(c, &chartype, &script); + switch(code[4]) + { + case PT_ANY: + OK = TRUE; + break; + + case PT_LAMP: + OK = chartype == ucp_Lu || chartype == ucp_Ll || chartype == ucp_Lt; + break; + + case PT_GC: + OK = category == code[5]; + break; + + case PT_PC: + OK = chartype == code[5]; + break; + + case PT_SC: + OK = script == code[5]; + break; + + /* Should never occur, but keep compilers from grumbling. */ + + default: + OK = codevalue != OP_PROP; + break; + } + + if (OK == (d == OP_PROP)) + { + if (codevalue == OP_PROP_EXTRA + OP_TYPEPOSUPTO) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + if (++count >= GET2(code, 1)) + { ADD_NEW(state_offset + 6, 0); } + else + { ADD_NEW(state_offset, count); } + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_EXTUNI_EXTRA + OP_TYPEEXACT: + case OP_EXTUNI_EXTRA + OP_TYPEUPTO: + case OP_EXTUNI_EXTRA + OP_TYPEMINUPTO: + case OP_EXTUNI_EXTRA + OP_TYPEPOSUPTO: + if (codevalue != OP_EXTUNI_EXTRA + OP_TYPEEXACT) + { ADD_ACTIVE(state_offset + 4, 0); } + count = current_state->count; /* Number already matched */ + if (clen > 0 && _pcre_ucp_findprop(c, &chartype, &script) != ucp_M) + { + const uschar *nptr = ptr + clen; + int ncount = 0; + if (codevalue == OP_EXTUNI_EXTRA + OP_TYPEPOSUPTO) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + while (nptr < end_subject) + { + int nd; + int ndlen = 1; + GETCHARLEN(nd, nptr, ndlen); + if (_pcre_ucp_findprop(nd, &chartype, &script) != ucp_M) break; + ncount++; + nptr += ndlen; + } + if (++count >= GET2(code, 1)) + { ADD_NEW_DATA(-(state_offset + 4), 0, ncount); } + else + { ADD_NEW_DATA(-state_offset, count, ncount); } + } + break; +#endif + + /*-----------------------------------------------------------------*/ + case OP_ANYNL_EXTRA + OP_TYPEEXACT: + case OP_ANYNL_EXTRA + OP_TYPEUPTO: + case OP_ANYNL_EXTRA + OP_TYPEMINUPTO: + case OP_ANYNL_EXTRA + OP_TYPEPOSUPTO: + if (codevalue != OP_ANYNL_EXTRA + OP_TYPEEXACT) + { ADD_ACTIVE(state_offset + 4, 0); } + count = current_state->count; /* Number already matched */ + if (clen > 0) + { + int ncount = 0; + switch (c) + { + case 0x000b: + case 0x000c: + case 0x0085: + case 0x2028: + case 0x2029: + if ((md->moptions & PCRE_BSR_ANYCRLF) != 0) break; + goto ANYNL03; + + case 0x000d: + if (ptr + 1 < end_subject && ptr[1] == 0x0a) ncount = 1; + /* Fall through */ + + ANYNL03: + case 0x000a: + if (codevalue == OP_ANYNL_EXTRA + OP_TYPEPOSUPTO) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + if (++count >= GET2(code, 1)) + { ADD_NEW_DATA(-(state_offset + 4), 0, ncount); } + else + { ADD_NEW_DATA(-state_offset, count, ncount); } + break; + + default: + break; + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_VSPACE_EXTRA + OP_TYPEEXACT: + case OP_VSPACE_EXTRA + OP_TYPEUPTO: + case OP_VSPACE_EXTRA + OP_TYPEMINUPTO: + case OP_VSPACE_EXTRA + OP_TYPEPOSUPTO: + if (codevalue != OP_VSPACE_EXTRA + OP_TYPEEXACT) + { ADD_ACTIVE(state_offset + 4, 0); } + count = current_state->count; /* Number already matched */ + if (clen > 0) + { + BOOL OK; + switch (c) + { + case 0x000a: + case 0x000b: + case 0x000c: + case 0x000d: + case 0x0085: + case 0x2028: + case 0x2029: + OK = TRUE; + break; + + default: + OK = FALSE; + } + + if (OK == (d == OP_VSPACE)) + { + if (codevalue == OP_VSPACE_EXTRA + OP_TYPEPOSUPTO) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + if (++count >= GET2(code, 1)) + { ADD_NEW_DATA(-(state_offset + 4), 0, 0); } + else + { ADD_NEW_DATA(-state_offset, count, 0); } + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_HSPACE_EXTRA + OP_TYPEEXACT: + case OP_HSPACE_EXTRA + OP_TYPEUPTO: + case OP_HSPACE_EXTRA + OP_TYPEMINUPTO: + case OP_HSPACE_EXTRA + OP_TYPEPOSUPTO: + if (codevalue != OP_HSPACE_EXTRA + OP_TYPEEXACT) + { ADD_ACTIVE(state_offset + 4, 0); } + count = current_state->count; /* Number already matched */ + if (clen > 0) + { + BOOL OK; + switch (c) + { + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ + OK = TRUE; + break; + + default: + OK = FALSE; + break; + } + + if (OK == (d == OP_HSPACE)) + { + if (codevalue == OP_HSPACE_EXTRA + OP_TYPEPOSUPTO) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + if (++count >= GET2(code, 1)) + { ADD_NEW_DATA(-(state_offset + 4), 0, 0); } + else + { ADD_NEW_DATA(-state_offset, count, 0); } + } + } + break; + +/* ========================================================================== */ + /* These opcodes are followed by a character that is usually compared + to the current subject character; it is loaded into d. We still get + here even if there is no subject character, because in some cases zero + repetitions are permitted. */ + + /*-----------------------------------------------------------------*/ + case OP_CHAR: + if (clen > 0 && c == d) { ADD_NEW(state_offset + dlen + 1, 0); } + break; + + /*-----------------------------------------------------------------*/ + case OP_CHARNC: + if (clen == 0) break; + +#ifdef SUPPORT_UTF8 + if (utf8) + { + if (c == d) { ADD_NEW(state_offset + dlen + 1, 0); } else + { + unsigned int othercase; + if (c < 128) othercase = fcc[c]; else + + /* If we have Unicode property support, we can use it to test the + other case of the character. */ + +#ifdef SUPPORT_UCP + othercase = _pcre_ucp_othercase(c); +#else + othercase = NOTACHAR; +#endif + + if (d == othercase) { ADD_NEW(state_offset + dlen + 1, 0); } + } + } + else +#endif /* SUPPORT_UTF8 */ + + /* Non-UTF-8 mode */ + { + if (lcc[c] == lcc[d]) { ADD_NEW(state_offset + 2, 0); } + } + break; + + +#ifdef SUPPORT_UCP + /*-----------------------------------------------------------------*/ + /* This is a tricky one because it can match more than one character. + Find out how many characters to skip, and then set up a negative state + to wait for them to pass before continuing. */ + + case OP_EXTUNI: + if (clen > 0 && _pcre_ucp_findprop(c, &chartype, &script) != ucp_M) + { + const uschar *nptr = ptr + clen; + int ncount = 0; + while (nptr < end_subject) + { + int nclen = 1; + GETCHARLEN(c, nptr, nclen); + if (_pcre_ucp_findprop(c, &chartype, &script) != ucp_M) break; + ncount++; + nptr += nclen; + } + ADD_NEW_DATA(-(state_offset + 1), 0, ncount); + } + break; +#endif + + /*-----------------------------------------------------------------*/ + /* This is a tricky like EXTUNI because it too can match more than one + character (when CR is followed by LF). In this case, set up a negative + state to wait for one character to pass before continuing. */ + + case OP_ANYNL: + if (clen > 0) switch(c) + { + case 0x000b: + case 0x000c: + case 0x0085: + case 0x2028: + case 0x2029: + if ((md->moptions & PCRE_BSR_ANYCRLF) != 0) break; + + case 0x000a: + ADD_NEW(state_offset + 1, 0); + break; + + case 0x000d: + if (ptr + 1 < end_subject && ptr[1] == 0x0a) + { + ADD_NEW_DATA(-(state_offset + 1), 0, 1); + } + else + { + ADD_NEW(state_offset + 1, 0); + } + break; + } + break; + + /*-----------------------------------------------------------------*/ + case OP_NOT_VSPACE: + if (clen > 0) switch(c) + { + case 0x000a: + case 0x000b: + case 0x000c: + case 0x000d: + case 0x0085: + case 0x2028: + case 0x2029: + break; + + default: + ADD_NEW(state_offset + 1, 0); + break; + } + break; + + /*-----------------------------------------------------------------*/ + case OP_VSPACE: + if (clen > 0) switch(c) + { + case 0x000a: + case 0x000b: + case 0x000c: + case 0x000d: + case 0x0085: + case 0x2028: + case 0x2029: + ADD_NEW(state_offset + 1, 0); + break; + + default: break; + } + break; + + /*-----------------------------------------------------------------*/ + case OP_NOT_HSPACE: + if (clen > 0) switch(c) + { + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ + break; + + default: + ADD_NEW(state_offset + 1, 0); + break; + } + break; + + /*-----------------------------------------------------------------*/ + case OP_HSPACE: + if (clen > 0) switch(c) + { + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ + ADD_NEW(state_offset + 1, 0); + break; + } + break; + + /*-----------------------------------------------------------------*/ + /* Match a negated single character. This is only used for one-byte + characters, that is, we know that d < 256. The character we are + checking (c) can be multibyte. */ + + case OP_NOT: + if (clen > 0) + { + unsigned int otherd = ((ims & PCRE_CASELESS) != 0)? fcc[d] : d; + if (c != d && c != otherd) { ADD_NEW(state_offset + dlen + 1, 0); } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_PLUS: + case OP_MINPLUS: + case OP_POSPLUS: + case OP_NOTPLUS: + case OP_NOTMINPLUS: + case OP_NOTPOSPLUS: + count = current_state->count; /* Already matched */ + if (count > 0) { ADD_ACTIVE(state_offset + dlen + 1, 0); } + if (clen > 0) + { + unsigned int otherd = NOTACHAR; + if ((ims & PCRE_CASELESS) != 0) + { +#ifdef SUPPORT_UTF8 + if (utf8 && d >= 128) + { +#ifdef SUPPORT_UCP + otherd = _pcre_ucp_othercase(d); +#endif /* SUPPORT_UCP */ + } + else +#endif /* SUPPORT_UTF8 */ + otherd = fcc[d]; + } + if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR)) + { + if (count > 0 && + (codevalue == OP_POSPLUS || codevalue == OP_NOTPOSPLUS)) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + count++; + ADD_NEW(state_offset, count); + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_QUERY: + case OP_MINQUERY: + case OP_POSQUERY: + case OP_NOTQUERY: + case OP_NOTMINQUERY: + case OP_NOTPOSQUERY: + ADD_ACTIVE(state_offset + dlen + 1, 0); + if (clen > 0) + { + unsigned int otherd = NOTACHAR; + if ((ims & PCRE_CASELESS) != 0) + { +#ifdef SUPPORT_UTF8 + if (utf8 && d >= 128) + { +#ifdef SUPPORT_UCP + otherd = _pcre_ucp_othercase(d); +#endif /* SUPPORT_UCP */ + } + else +#endif /* SUPPORT_UTF8 */ + otherd = fcc[d]; + } + if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR)) + { + if (codevalue == OP_POSQUERY || codevalue == OP_NOTPOSQUERY) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + ADD_NEW(state_offset + dlen + 1, 0); + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_STAR: + case OP_MINSTAR: + case OP_POSSTAR: + case OP_NOTSTAR: + case OP_NOTMINSTAR: + case OP_NOTPOSSTAR: + ADD_ACTIVE(state_offset + dlen + 1, 0); + if (clen > 0) + { + unsigned int otherd = NOTACHAR; + if ((ims & PCRE_CASELESS) != 0) + { +#ifdef SUPPORT_UTF8 + if (utf8 && d >= 128) + { +#ifdef SUPPORT_UCP + otherd = _pcre_ucp_othercase(d); +#endif /* SUPPORT_UCP */ + } + else +#endif /* SUPPORT_UTF8 */ + otherd = fcc[d]; + } + if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR)) + { + if (codevalue == OP_POSSTAR || codevalue == OP_NOTPOSSTAR) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + ADD_NEW(state_offset, 0); + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_EXACT: + case OP_NOTEXACT: + count = current_state->count; /* Number already matched */ + if (clen > 0) + { + unsigned int otherd = NOTACHAR; + if ((ims & PCRE_CASELESS) != 0) + { +#ifdef SUPPORT_UTF8 + if (utf8 && d >= 128) + { +#ifdef SUPPORT_UCP + otherd = _pcre_ucp_othercase(d); +#endif /* SUPPORT_UCP */ + } + else +#endif /* SUPPORT_UTF8 */ + otherd = fcc[d]; + } + if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR)) + { + if (++count >= GET2(code, 1)) + { ADD_NEW(state_offset + dlen + 3, 0); } + else + { ADD_NEW(state_offset, count); } + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_UPTO: + case OP_MINUPTO: + case OP_POSUPTO: + case OP_NOTUPTO: + case OP_NOTMINUPTO: + case OP_NOTPOSUPTO: + ADD_ACTIVE(state_offset + dlen + 3, 0); + count = current_state->count; /* Number already matched */ + if (clen > 0) + { + unsigned int otherd = NOTACHAR; + if ((ims & PCRE_CASELESS) != 0) + { +#ifdef SUPPORT_UTF8 + if (utf8 && d >= 128) + { +#ifdef SUPPORT_UCP + otherd = _pcre_ucp_othercase(d); +#endif /* SUPPORT_UCP */ + } + else +#endif /* SUPPORT_UTF8 */ + otherd = fcc[d]; + } + if ((c == d || c == otherd) == (codevalue < OP_NOTSTAR)) + { + if (codevalue == OP_POSUPTO || codevalue == OP_NOTPOSUPTO) + { + active_count--; /* Remove non-match possibility */ + next_active_state--; + } + if (++count >= GET2(code, 1)) + { ADD_NEW(state_offset + dlen + 3, 0); } + else + { ADD_NEW(state_offset, count); } + } + } + break; + + +/* ========================================================================== */ + /* These are the class-handling opcodes */ + + case OP_CLASS: + case OP_NCLASS: + case OP_XCLASS: + { + BOOL isinclass = FALSE; + int next_state_offset; + const uschar *ecode; + + /* For a simple class, there is always just a 32-byte table, and we + can set isinclass from it. */ + + if (codevalue != OP_XCLASS) + { + ecode = code + 33; + if (clen > 0) + { + isinclass = (c > 255)? (codevalue == OP_NCLASS) : + ((code[1 + c/8] & (1 << (c&7))) != 0); + } + } + + /* An extended class may have a table or a list of single characters, + ranges, or both, and it may be positive or negative. There's a + function that sorts all this out. */ + + else + { + ecode = code + GET(code, 1); + if (clen > 0) isinclass = _pcre_xclass(c, code + 1 + LINK_SIZE); + } + + /* At this point, isinclass is set for all kinds of class, and ecode + points to the byte after the end of the class. If there is a + quantifier, this is where it will be. */ + + next_state_offset = ecode - start_code; + + switch (*ecode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + ADD_ACTIVE(next_state_offset + 1, 0); + if (isinclass) { ADD_NEW(state_offset, 0); } + break; + + case OP_CRPLUS: + case OP_CRMINPLUS: + count = current_state->count; /* Already matched */ + if (count > 0) { ADD_ACTIVE(next_state_offset + 1, 0); } + if (isinclass) { count++; ADD_NEW(state_offset, count); } + break; + + case OP_CRQUERY: + case OP_CRMINQUERY: + ADD_ACTIVE(next_state_offset + 1, 0); + if (isinclass) { ADD_NEW(next_state_offset + 1, 0); } + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + count = current_state->count; /* Already matched */ + if (count >= GET2(ecode, 1)) + { ADD_ACTIVE(next_state_offset + 5, 0); } + if (isinclass) + { + int max = GET2(ecode, 3); + if (++count >= max && max != 0) /* Max 0 => no limit */ + { ADD_NEW(next_state_offset + 5, 0); } + else + { ADD_NEW(state_offset, count); } + } + break; + + default: + if (isinclass) { ADD_NEW(next_state_offset, 0); } + break; + } + } + break; + +/* ========================================================================== */ + /* These are the opcodes for fancy brackets of various kinds. We have + to use recursion in order to handle them. */ + + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + { + int rc; + int local_offsets[2]; + int local_workspace[1000]; + const uschar *endasscode = code + GET(code, 1); + + while (*endasscode == OP_ALT) endasscode += GET(endasscode, 1); + + rc = internal_dfa_exec( + md, /* static match data */ + code, /* this subexpression's code */ + ptr, /* where we currently are */ + ptr - start_subject, /* start offset */ + local_offsets, /* offset vector */ + sizeof(local_offsets)/sizeof(int), /* size of same */ + local_workspace, /* workspace vector */ + sizeof(local_workspace)/sizeof(int), /* size of same */ + ims, /* the current ims flags */ + rlevel, /* function recursion level */ + recursing); /* pass on regex recursion */ + + if ((rc >= 0) == (codevalue == OP_ASSERT || codevalue == OP_ASSERTBACK)) + { ADD_ACTIVE(endasscode + LINK_SIZE + 1 - start_code, 0); } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_COND: + case OP_SCOND: + { + int local_offsets[1000]; + int local_workspace[1000]; + int condcode = code[LINK_SIZE+1]; + + /* Back reference conditions are not supported */ + + if (condcode == OP_CREF) return PCRE_ERROR_DFA_UCOND; + + /* The DEFINE condition is always false */ + + if (condcode == OP_DEF) + { + ADD_ACTIVE(state_offset + GET(code, 1) + LINK_SIZE + 1, 0); + } + + /* The only supported version of OP_RREF is for the value RREF_ANY, + which means "test if in any recursion". We can't test for specifically + recursed groups. */ + + else if (condcode == OP_RREF) + { + int value = GET2(code, LINK_SIZE+2); + if (value != RREF_ANY) return PCRE_ERROR_DFA_UCOND; + if (recursing > 0) { ADD_ACTIVE(state_offset + LINK_SIZE + 4, 0); } + else { ADD_ACTIVE(state_offset + GET(code, 1) + LINK_SIZE + 1, 0); } + } + + /* Otherwise, the condition is an assertion */ + + else + { + int rc; + const uschar *asscode = code + LINK_SIZE + 1; + const uschar *endasscode = asscode + GET(asscode, 1); + + while (*endasscode == OP_ALT) endasscode += GET(endasscode, 1); + + rc = internal_dfa_exec( + md, /* fixed match data */ + asscode, /* this subexpression's code */ + ptr, /* where we currently are */ + ptr - start_subject, /* start offset */ + local_offsets, /* offset vector */ + sizeof(local_offsets)/sizeof(int), /* size of same */ + local_workspace, /* workspace vector */ + sizeof(local_workspace)/sizeof(int), /* size of same */ + ims, /* the current ims flags */ + rlevel, /* function recursion level */ + recursing); /* pass on regex recursion */ + + if ((rc >= 0) == + (condcode == OP_ASSERT || condcode == OP_ASSERTBACK)) + { ADD_ACTIVE(endasscode + LINK_SIZE + 1 - start_code, 0); } + else + { ADD_ACTIVE(state_offset + GET(code, 1) + LINK_SIZE + 1, 0); } + } + } + break; + + /*-----------------------------------------------------------------*/ + case OP_RECURSE: + { + int local_offsets[1000]; + int local_workspace[1000]; + int rc; + + DPRINTF(("%.*sStarting regex recursion %d\n", rlevel*2-2, SP, + recursing + 1)); + + rc = internal_dfa_exec( + md, /* fixed match data */ + start_code + GET(code, 1), /* this subexpression's code */ + ptr, /* where we currently are */ + ptr - start_subject, /* start offset */ + local_offsets, /* offset vector */ + sizeof(local_offsets)/sizeof(int), /* size of same */ + local_workspace, /* workspace vector */ + sizeof(local_workspace)/sizeof(int), /* size of same */ + ims, /* the current ims flags */ + rlevel, /* function recursion level */ + recursing + 1); /* regex recurse level */ + + DPRINTF(("%.*sReturn from regex recursion %d: rc=%d\n", rlevel*2-2, SP, + recursing + 1, rc)); + + /* Ran out of internal offsets */ + + if (rc == 0) return PCRE_ERROR_DFA_RECURSE; + + /* For each successful matched substring, set up the next state with a + count of characters to skip before trying it. Note that the count is in + characters, not bytes. */ + + if (rc > 0) + { + for (rc = rc*2 - 2; rc >= 0; rc -= 2) + { + const uschar *p = start_subject + local_offsets[rc]; + const uschar *pp = start_subject + local_offsets[rc+1]; + int charcount = local_offsets[rc+1] - local_offsets[rc]; + while (p < pp) if ((*p++ & 0xc0) == 0x80) charcount--; + if (charcount > 0) + { + ADD_NEW_DATA(-(state_offset + LINK_SIZE + 1), 0, (charcount - 1)); + } + else + { + ADD_ACTIVE(state_offset + LINK_SIZE + 1, 0); + } + } + } + else if (rc != PCRE_ERROR_NOMATCH) return rc; + } + break; + + /*-----------------------------------------------------------------*/ + case OP_ONCE: + { + int local_offsets[2]; + int local_workspace[1000]; + + int rc = internal_dfa_exec( + md, /* fixed match data */ + code, /* this subexpression's code */ + ptr, /* where we currently are */ + ptr - start_subject, /* start offset */ + local_offsets, /* offset vector */ + sizeof(local_offsets)/sizeof(int), /* size of same */ + local_workspace, /* workspace vector */ + sizeof(local_workspace)/sizeof(int), /* size of same */ + ims, /* the current ims flags */ + rlevel, /* function recursion level */ + recursing); /* pass on regex recursion */ + + if (rc >= 0) + { + const uschar *end_subpattern = code; + int charcount = local_offsets[1] - local_offsets[0]; + int next_state_offset, repeat_state_offset; + + do { end_subpattern += GET(end_subpattern, 1); } + while (*end_subpattern == OP_ALT); + next_state_offset = end_subpattern - start_code + LINK_SIZE + 1; + + /* If the end of this subpattern is KETRMAX or KETRMIN, we must + arrange for the repeat state also to be added to the relevant list. + Calculate the offset, or set -1 for no repeat. */ + + repeat_state_offset = (*end_subpattern == OP_KETRMAX || + *end_subpattern == OP_KETRMIN)? + end_subpattern - start_code - GET(end_subpattern, 1) : -1; + + /* If we have matched an empty string, add the next state at the + current character pointer. This is important so that the duplicate + checking kicks in, which is what breaks infinite loops that match an + empty string. */ + + if (charcount == 0) + { + ADD_ACTIVE(next_state_offset, 0); + } + + /* Optimization: if there are no more active states, and there + are no new states yet set up, then skip over the subject string + right here, to save looping. Otherwise, set up the new state to swing + into action when the end of the substring is reached. */ + + else if (i + 1 >= active_count && new_count == 0) + { + ptr += charcount; + clen = 0; + ADD_NEW(next_state_offset, 0); + + /* If we are adding a repeat state at the new character position, + we must fudge things so that it is the only current state. + Otherwise, it might be a duplicate of one we processed before, and + that would cause it to be skipped. */ + + if (repeat_state_offset >= 0) + { + next_active_state = active_states; + active_count = 0; + i = -1; + ADD_ACTIVE(repeat_state_offset, 0); + } + } + else + { + const uschar *p = start_subject + local_offsets[0]; + const uschar *pp = start_subject + local_offsets[1]; + while (p < pp) if ((*p++ & 0xc0) == 0x80) charcount--; + ADD_NEW_DATA(-next_state_offset, 0, (charcount - 1)); + if (repeat_state_offset >= 0) + { ADD_NEW_DATA(-repeat_state_offset, 0, (charcount - 1)); } + } + + } + else if (rc != PCRE_ERROR_NOMATCH) return rc; + } + break; + + +/* ========================================================================== */ + /* Handle callouts */ + + case OP_CALLOUT: + if (pcre_callout != NULL) + { + int rrc; + pcre_callout_block cb; + cb.version = 1; /* Version 1 of the callout block */ + cb.callout_number = code[1]; + cb.offset_vector = offsets; + cb.subject = (PCRE_SPTR)start_subject; + cb.subject_length = end_subject - start_subject; + cb.start_match = current_subject - start_subject; + cb.current_position = ptr - start_subject; + cb.pattern_position = GET(code, 2); + cb.next_item_length = GET(code, 2 + LINK_SIZE); + cb.capture_top = 1; + cb.capture_last = -1; + cb.callout_data = md->callout_data; + if ((rrc = (*pcre_callout)(&cb)) < 0) return rrc; /* Abandon */ + if (rrc == 0) { ADD_ACTIVE(state_offset + 2 + 2*LINK_SIZE, 0); } + } + break; + + +/* ========================================================================== */ + default: /* Unsupported opcode */ + return PCRE_ERROR_DFA_UITEM; + } + + NEXT_ACTIVE_STATE: continue; + + } /* End of loop scanning active states */ + + /* We have finished the processing at the current subject character. If no + new states have been set for the next character, we have found all the + matches that we are going to find. If we are at the top level and partial + matching has been requested, check for appropriate conditions. */ + + if (new_count <= 0) + { + if (match_count < 0 && /* No matches found */ + rlevel == 1 && /* Top level match function */ + (md->moptions & PCRE_PARTIAL) != 0 && /* Want partial matching */ + ptr >= end_subject && /* Reached end of subject */ + ptr > current_subject) /* Matched non-empty string */ + { + if (offsetcount >= 2) + { + offsets[0] = current_subject - start_subject; + offsets[1] = end_subject - start_subject; + } + match_count = PCRE_ERROR_PARTIAL; + } + + DPRINTF(("%.*sEnd of internal_dfa_exec %d: returning %d\n" + "%.*s---------------------\n\n", rlevel*2-2, SP, rlevel, match_count, + rlevel*2-2, SP)); + break; /* In effect, "return", but see the comment below */ + } + + /* One or more states are active for the next character. */ + + ptr += clen; /* Advance to next subject character */ + } /* Loop to move along the subject string */ + +/* Control gets here from "break" a few lines above. We do it this way because +if we use "return" above, we have compiler trouble. Some compilers warn if +there's nothing here because they think the function doesn't return a value. On +the other hand, if we put a dummy statement here, some more clever compilers +complain that it can't be reached. Sigh. */ + +return match_count; +} + + + + +/************************************************* +* Execute a Regular Expression - DFA engine * +*************************************************/ + +/* This external function applies a compiled re to a subject string using a DFA +engine. This function calls the internal function multiple times if the pattern +is not anchored. + +Arguments: + argument_re points to the compiled expression + extra_data points to extra data or is NULL + subject points to the subject string + length length of subject string (may contain binary zeros) + start_offset where to start in the subject string + options option bits + offsets vector of match offsets + offsetcount size of same + workspace workspace vector + wscount size of same + +Returns: > 0 => number of match offset pairs placed in offsets + = 0 => offsets overflowed; longest matches are present + -1 => failed to match + < -1 => some kind of unexpected problem +*/ + +PCRE_EXP_DEFN int +pcre_dfa_exec(const pcre *argument_re, const pcre_extra *extra_data, + const char *subject, int length, int start_offset, int options, int *offsets, + int offsetcount, int *workspace, int wscount) +{ +real_pcre *re = (real_pcre *)argument_re; +dfa_match_data match_block; +dfa_match_data *md = &match_block; +BOOL utf8, anchored, startline, firstline; +const uschar *current_subject, *end_subject, *lcc; + +pcre_study_data internal_study; +const pcre_study_data *study = NULL; +real_pcre internal_re; + +const uschar *req_byte_ptr; +const uschar *start_bits = NULL; +BOOL first_byte_caseless = FALSE; +BOOL req_byte_caseless = FALSE; +int first_byte = -1; +int req_byte = -1; +int req_byte2 = -1; +int newline; + +/* Plausibility checks */ + +if ((options & ~PUBLIC_DFA_EXEC_OPTIONS) != 0) return PCRE_ERROR_BADOPTION; +if (re == NULL || subject == NULL || workspace == NULL || + (offsets == NULL && offsetcount > 0)) return PCRE_ERROR_NULL; +if (offsetcount < 0) return PCRE_ERROR_BADCOUNT; +if (wscount < 20) return PCRE_ERROR_DFA_WSSIZE; + +/* We need to find the pointer to any study data before we test for byte +flipping, so we scan the extra_data block first. This may set two fields in the +match block, so we must initialize them beforehand. However, the other fields +in the match block must not be set until after the byte flipping. */ + +md->tables = re->tables; +md->callout_data = NULL; + +if (extra_data != NULL) + { + unsigned int flags = extra_data->flags; + if ((flags & PCRE_EXTRA_STUDY_DATA) != 0) + study = (const pcre_study_data *)extra_data->study_data; + if ((flags & PCRE_EXTRA_MATCH_LIMIT) != 0) return PCRE_ERROR_DFA_UMLIMIT; + if ((flags & PCRE_EXTRA_MATCH_LIMIT_RECURSION) != 0) + return PCRE_ERROR_DFA_UMLIMIT; + if ((flags & PCRE_EXTRA_CALLOUT_DATA) != 0) + md->callout_data = extra_data->callout_data; + if ((flags & PCRE_EXTRA_TABLES) != 0) + md->tables = extra_data->tables; + } + +/* Check that the first field in the block is the magic number. If it is not, +test for a regex that was compiled on a host of opposite endianness. If this is +the case, flipped values are put in internal_re and internal_study if there was +study data too. */ + +if (re->magic_number != MAGIC_NUMBER) + { + re = _pcre_try_flipped(re, &internal_re, study, &internal_study); + if (re == NULL) return PCRE_ERROR_BADMAGIC; + if (study != NULL) study = &internal_study; + } + +/* Set some local values */ + +current_subject = (const unsigned char *)subject + start_offset; +end_subject = (const unsigned char *)subject + length; +req_byte_ptr = current_subject - 1; + +#ifdef SUPPORT_UTF8 +utf8 = (re->options & PCRE_UTF8) != 0; +#else +utf8 = FALSE; +#endif + +anchored = (options & (PCRE_ANCHORED|PCRE_DFA_RESTART)) != 0 || + (re->options & PCRE_ANCHORED) != 0; + +/* The remaining fixed data for passing around. */ + +md->start_code = (const uschar *)argument_re + + re->name_table_offset + re->name_count * re->name_entry_size; +md->start_subject = (const unsigned char *)subject; +md->end_subject = end_subject; +md->moptions = options; +md->poptions = re->options; + +/* If the BSR option is not set at match time, copy what was set +at compile time. */ + +if ((md->moptions & (PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE)) == 0) + { + if ((re->options & (PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE)) != 0) + md->moptions |= re->options & (PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE); +#ifdef BSR_ANYCRLF + else md->moptions |= PCRE_BSR_ANYCRLF; +#endif + } + +/* Handle different types of newline. The three bits give eight cases. If +nothing is set at run time, whatever was used at compile time applies. */ + +switch ((((options & PCRE_NEWLINE_BITS) == 0)? re->options : (pcre_uint32)options) & + PCRE_NEWLINE_BITS) + { + case 0: newline = NEWLINE; break; /* Compile-time default */ + case PCRE_NEWLINE_CR: newline = '\r'; break; + case PCRE_NEWLINE_LF: newline = '\n'; break; + case PCRE_NEWLINE_CR+ + PCRE_NEWLINE_LF: newline = ('\r' << 8) | '\n'; break; + case PCRE_NEWLINE_ANY: newline = -1; break; + case PCRE_NEWLINE_ANYCRLF: newline = -2; break; + default: return PCRE_ERROR_BADNEWLINE; + } + +if (newline == -2) + { + md->nltype = NLTYPE_ANYCRLF; + } +else if (newline < 0) + { + md->nltype = NLTYPE_ANY; + } +else + { + md->nltype = NLTYPE_FIXED; + if (newline > 255) + { + md->nllen = 2; + md->nl[0] = (newline >> 8) & 255; + md->nl[1] = newline & 255; + } + else + { + md->nllen = 1; + md->nl[0] = newline; + } + } + +/* Check a UTF-8 string if required. Unfortunately there's no way of passing +back the character offset. */ + +#ifdef SUPPORT_UTF8 +if (utf8 && (options & PCRE_NO_UTF8_CHECK) == 0) + { + if (_pcre_valid_utf8((uschar *)subject, length) >= 0) + return PCRE_ERROR_BADUTF8; + if (start_offset > 0 && start_offset < length) + { + int tb = ((uschar *)subject)[start_offset]; + if (tb > 127) + { + tb &= 0xc0; + if (tb != 0 && tb != 0xc0) return PCRE_ERROR_BADUTF8_OFFSET; + } + } + } +#endif + +/* If the exec call supplied NULL for tables, use the inbuilt ones. This +is a feature that makes it possible to save compiled regex and re-use them +in other programs later. */ + +if (md->tables == NULL) md->tables = _pcre_default_tables; + +/* The lower casing table and the "must be at the start of a line" flag are +used in a loop when finding where to start. */ + +lcc = md->tables + lcc_offset; +startline = (re->flags & PCRE_STARTLINE) != 0; +firstline = (re->options & PCRE_FIRSTLINE) != 0; + +/* Set up the first character to match, if available. The first_byte value is +never set for an anchored regular expression, but the anchoring may be forced +at run time, so we have to test for anchoring. The first char may be unset for +an unanchored pattern, of course. If there's no first char and the pattern was +studied, there may be a bitmap of possible first characters. */ + +if (!anchored) + { + if ((re->flags & PCRE_FIRSTSET) != 0) + { + first_byte = re->first_byte & 255; + if ((first_byte_caseless = ((re->first_byte & REQ_CASELESS) != 0)) == TRUE) + first_byte = lcc[first_byte]; + } + else + { + if (startline && study != NULL && + (study->options & PCRE_STUDY_MAPPED) != 0) + start_bits = study->start_bits; + } + } + +/* For anchored or unanchored matches, there may be a "last known required +character" set. */ + +if ((re->flags & PCRE_REQCHSET) != 0) + { + req_byte = re->req_byte & 255; + req_byte_caseless = (re->req_byte & REQ_CASELESS) != 0; + req_byte2 = (md->tables + fcc_offset)[req_byte]; /* case flipped */ + } + +/* Call the main matching function, looping for a non-anchored regex after a +failed match. Unless restarting, optimize by moving to the first match +character if possible, when not anchored. Then unless wanting a partial match, +check for a required later character. */ + +for (;;) + { + int rc; + + if ((options & PCRE_DFA_RESTART) == 0) + { + const uschar *save_end_subject = end_subject; + + /* Advance to a unique first char if possible. If firstline is TRUE, the + start of the match is constrained to the first line of a multiline string. + Implement this by temporarily adjusting end_subject so that we stop + scanning at a newline. If the match fails at the newline, later code breaks + this loop. */ + + if (firstline) + { + const uschar *t = current_subject; + while (t < md->end_subject && !IS_NEWLINE(t)) t++; + end_subject = t; + } + + if (first_byte >= 0) + { + if (first_byte_caseless) + while (current_subject < end_subject && + lcc[*current_subject] != first_byte) + current_subject++; + else + while (current_subject < end_subject && *current_subject != first_byte) + current_subject++; + } + + /* Or to just after a linebreak for a multiline match if possible */ + + else if (startline) + { + if (current_subject > md->start_subject + start_offset) + { + while (current_subject <= end_subject && !WAS_NEWLINE(current_subject)) + current_subject++; + + /* If we have just passed a CR and the newline option is ANY or + ANYCRLF, and we are now at a LF, advance the match position by one more + character. */ + + if (current_subject[-1] == '\r' && + (md->nltype == NLTYPE_ANY || md->nltype == NLTYPE_ANYCRLF) && + current_subject < end_subject && + *current_subject == '\n') + current_subject++; + } + } + + /* Or to a non-unique first char after study */ + + else if (start_bits != NULL) + { + while (current_subject < end_subject) + { + register unsigned int c = *current_subject; + if ((start_bits[c/8] & (1 << (c&7))) == 0) current_subject++; + else break; + } + } + + /* Restore fudged end_subject */ + + end_subject = save_end_subject; + } + + /* If req_byte is set, we know that that character must appear in the subject + for the match to succeed. If the first character is set, req_byte must be + later in the subject; otherwise the test starts at the match point. This + optimization can save a huge amount of work in patterns with nested unlimited + repeats that aren't going to match. Writing separate code for cased/caseless + versions makes it go faster, as does using an autoincrement and backing off + on a match. + + HOWEVER: when the subject string is very, very long, searching to its end can + take a long time, and give bad performance on quite ordinary patterns. This + showed up when somebody was matching /^C/ on a 32-megabyte string... so we + don't do this when the string is sufficiently long. + + ALSO: this processing is disabled when partial matching is requested. + */ + + if (req_byte >= 0 && + end_subject - current_subject < REQ_BYTE_MAX && + (options & PCRE_PARTIAL) == 0) + { + register const uschar *p = current_subject + ((first_byte >= 0)? 1 : 0); + + /* We don't need to repeat the search if we haven't yet reached the + place we found it at last time. */ + + if (p > req_byte_ptr) + { + if (req_byte_caseless) + { + while (p < end_subject) + { + register int pp = *p++; + if (pp == req_byte || pp == req_byte2) { p--; break; } + } + } + else + { + while (p < end_subject) + { + if (*p++ == req_byte) { p--; break; } + } + } + + /* If we can't find the required character, break the matching loop, + which will cause a return or PCRE_ERROR_NOMATCH. */ + + if (p >= end_subject) break; + + /* If we have found the required character, save the point where we + found it, so that we don't search again next time round the loop if + the start hasn't passed this character yet. */ + + req_byte_ptr = p; + } + } + + /* OK, now we can do the business */ + + rc = internal_dfa_exec( + md, /* fixed match data */ + md->start_code, /* this subexpression's code */ + current_subject, /* where we currently are */ + start_offset, /* start offset in subject */ + offsets, /* offset vector */ + offsetcount, /* size of same */ + workspace, /* workspace vector */ + wscount, /* size of same */ + re->options & (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL), /* ims flags */ + 0, /* function recurse level */ + 0); /* regex recurse level */ + + /* Anything other than "no match" means we are done, always; otherwise, carry + on only if not anchored. */ + + if (rc != PCRE_ERROR_NOMATCH || anchored) return rc; + + /* Advance to the next subject character unless we are at the end of a line + and firstline is set. */ + + if (firstline && IS_NEWLINE(current_subject)) break; + current_subject++; + if (utf8) + { + while (current_subject < end_subject && (*current_subject & 0xc0) == 0x80) + current_subject++; + } + if (current_subject > end_subject) break; + + /* If we have just passed a CR and we are now at a LF, and the pattern does + not contain any explicit matches for \r or \n, and the newline option is CRLF + or ANY or ANYCRLF, advance the match position by one more character. */ + + if (current_subject[-1] == '\r' && + current_subject < end_subject && + *current_subject == '\n' && + (re->flags & PCRE_HASCRORLF) == 0 && + (md->nltype == NLTYPE_ANY || + md->nltype == NLTYPE_ANYCRLF || + md->nllen == 2)) + current_subject++; + + } /* "Bumpalong" loop */ + +return PCRE_ERROR_NOMATCH; +} + +/* End of pcre_dfa_exec.c */ diff --git a/Engine/lib/pcre/pcre_exec.c b/Engine/lib/pcre/pcre_exec.c new file mode 100644 index 000000000..56739915e --- /dev/null +++ b/Engine/lib/pcre/pcre_exec.c @@ -0,0 +1,4940 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2008 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains pcre_exec(), the externally visible function that does +pattern matching using an NFA algorithm, trying to mimic Perl as closely as +possible. There are also some static supporting functions. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define NLBLOCK md /* Block containing newline information */ +#define PSSTART start_subject /* Field containing processed string start */ +#define PSEND end_subject /* Field containing processed string end */ + +#include "pcre_internal.h" + +/* Undefine some potentially clashing cpp symbols */ + +#undef min +#undef max + +/* Flag bits for the match() function */ + +#define match_condassert 0x01 /* Called to check a condition assertion */ +#define match_cbegroup 0x02 /* Could-be-empty unlimited repeat group */ + +/* Non-error returns from the match() function. Error returns are externally +defined PCRE_ERROR_xxx codes, which are all negative. */ + +#define MATCH_MATCH 1 +#define MATCH_NOMATCH 0 + +/* Special internal returns from the match() function. Make them sufficiently +negative to avoid the external error codes. */ + +#define MATCH_COMMIT (-999) +#define MATCH_PRUNE (-998) +#define MATCH_SKIP (-997) +#define MATCH_THEN (-996) + +/* Maximum number of ints of offset to save on the stack for recursive calls. +If the offset vector is bigger, malloc is used. This should be a multiple of 3, +because the offset vector is always a multiple of 3 long. */ + +#define REC_STACK_SAVE_MAX 30 + +/* Min and max values for the common repeats; for the maxima, 0 => infinity */ + +static const char rep_min[] = { 0, 0, 1, 1, 0, 0 }; +static const char rep_max[] = { 0, 0, 0, 0, 1, 1 }; + + + +#ifdef DEBUG +/************************************************* +* Debugging function to print chars * +*************************************************/ + +/* Print a sequence of chars in printable format, stopping at the end of the +subject if the requested. + +Arguments: + p points to characters + length number to print + is_subject TRUE if printing from within md->start_subject + md pointer to matching data block, if is_subject is TRUE + +Returns: nothing +*/ + +static void +pchars(const uschar *p, int length, BOOL is_subject, match_data *md) +{ +unsigned int c; +if (is_subject && length > md->end_subject - p) length = md->end_subject - p; +while (length-- > 0) + if (isprint(c = *(p++))) printf("%c", c); else printf("\\x%02x", c); +} +#endif + + + +/************************************************* +* Match a back-reference * +*************************************************/ + +/* If a back reference hasn't been set, the length that is passed is greater +than the number of characters left in the string, so the match fails. + +Arguments: + offset index into the offset vector + eptr points into the subject + length length to be matched + md points to match data block + ims the ims flags + +Returns: TRUE if matched +*/ + +static BOOL +match_ref(int offset, register USPTR eptr, int length, match_data *md, + unsigned long int ims) +{ +USPTR p = md->start_subject + md->offset_vector[offset]; + +#ifdef DEBUG +if (eptr >= md->end_subject) + printf("matching subject "); +else + { + printf("matching subject "); + pchars(eptr, length, TRUE, md); + } +printf(" against backref "); +pchars(p, length, FALSE, md); +printf("\n"); +#endif + +/* Always fail if not enough characters left */ + +if (length > md->end_subject - eptr) return FALSE; + +/* Separate the caselesss case for speed */ + +if ((ims & PCRE_CASELESS) != 0) + { + while (length-- > 0) + if (md->lcc[*p++] != md->lcc[*eptr++]) return FALSE; + } +else + { while (length-- > 0) if (*p++ != *eptr++) return FALSE; } + +return TRUE; +} + + + +/*************************************************************************** +**************************************************************************** + RECURSION IN THE match() FUNCTION + +The match() function is highly recursive, though not every recursive call +increases the recursive depth. Nevertheless, some regular expressions can cause +it to recurse to a great depth. I was writing for Unix, so I just let it call +itself recursively. This uses the stack for saving everything that has to be +saved for a recursive call. On Unix, the stack can be large, and this works +fine. + +It turns out that on some non-Unix-like systems there are problems with +programs that use a lot of stack. (This despite the fact that every last chip +has oodles of memory these days, and techniques for extending the stack have +been known for decades.) So.... + +There is a fudge, triggered by defining NO_RECURSE, which avoids recursive +calls by keeping local variables that need to be preserved in blocks of memory +obtained from malloc() instead instead of on the stack. Macros are used to +achieve this so that the actual code doesn't look very different to what it +always used to. + +The original heap-recursive code used longjmp(). However, it seems that this +can be very slow on some operating systems. Following a suggestion from Stan +Switzer, the use of longjmp() has been abolished, at the cost of having to +provide a unique number for each call to RMATCH. There is no way of generating +a sequence of numbers at compile time in C. I have given them names, to make +them stand out more clearly. + +Crude tests on x86 Linux show a small speedup of around 5-8%. However, on +FreeBSD, avoiding longjmp() more than halves the time taken to run the standard +tests. Furthermore, not using longjmp() means that local dynamic variables +don't have indeterminate values; this has meant that the frame size can be +reduced because the result can be "passed back" by straight setting of the +variable instead of being passed in the frame. +**************************************************************************** +***************************************************************************/ + +/* Numbers for RMATCH calls. When this list is changed, the code at HEAP_RETURN +below must be updated in sync. */ + +enum { RM1=1, RM2, RM3, RM4, RM5, RM6, RM7, RM8, RM9, RM10, + RM11, RM12, RM13, RM14, RM15, RM16, RM17, RM18, RM19, RM20, + RM21, RM22, RM23, RM24, RM25, RM26, RM27, RM28, RM29, RM30, + RM31, RM32, RM33, RM34, RM35, RM36, RM37, RM38, RM39, RM40, + RM41, RM42, RM43, RM44, RM45, RM46, RM47, RM48, RM49, RM50, + RM51, RM52, RM53, RM54 }; + +/* These versions of the macros use the stack, as normal. There are debugging +versions and production versions. Note that the "rw" argument of RMATCH isn't +actuall used in this definition. */ + +#ifndef NO_RECURSE +#define REGISTER register + +#ifdef DEBUG +#define RMATCH(ra,rb,rc,rd,re,rf,rg,rw) \ + { \ + printf("match() called in line %d\n", __LINE__); \ + rrc = match(ra,rb,mstart,rc,rd,re,rf,rg,rdepth+1); \ + printf("to line %d\n", __LINE__); \ + } +#define RRETURN(ra) \ + { \ + printf("match() returned %d from line %d ", ra, __LINE__); \ + return ra; \ + } +#else +#define RMATCH(ra,rb,rc,rd,re,rf,rg,rw) \ + rrc = match(ra,rb,mstart,rc,rd,re,rf,rg,rdepth+1) +#define RRETURN(ra) return ra +#endif + +#else + + +/* These versions of the macros manage a private stack on the heap. Note that +the "rd" argument of RMATCH isn't actually used in this definition. It's the md +argument of match(), which never changes. */ + +#define REGISTER + +#define RMATCH(ra,rb,rc,rd,re,rf,rg,rw)\ + {\ + heapframe *newframe = (pcre_stack_malloc)(sizeof(heapframe));\ + frame->Xwhere = rw; \ + newframe->Xeptr = ra;\ + newframe->Xecode = rb;\ + newframe->Xmstart = mstart;\ + newframe->Xoffset_top = rc;\ + newframe->Xims = re;\ + newframe->Xeptrb = rf;\ + newframe->Xflags = rg;\ + newframe->Xrdepth = frame->Xrdepth + 1;\ + newframe->Xprevframe = frame;\ + frame = newframe;\ + DPRINTF(("restarting from line %d\n", __LINE__));\ + goto HEAP_RECURSE;\ + L_##rw:\ + DPRINTF(("jumped back to line %d\n", __LINE__));\ + } + +#define RRETURN(ra)\ + {\ + heapframe *newframe = frame;\ + frame = newframe->Xprevframe;\ + (pcre_stack_free)(newframe);\ + if (frame != NULL)\ + {\ + rrc = ra;\ + goto HEAP_RETURN;\ + }\ + return ra;\ + } + + +/* Structure for remembering the local variables in a private frame */ + +typedef struct heapframe { + struct heapframe *Xprevframe; + + /* Function arguments that may change */ + + const uschar *Xeptr; + const uschar *Xecode; + const uschar *Xmstart; + int Xoffset_top; + long int Xims; + eptrblock *Xeptrb; + int Xflags; + unsigned int Xrdepth; + + /* Function local variables */ + + const uschar *Xcallpat; + const uschar *Xcharptr; + const uschar *Xdata; + const uschar *Xnext; + const uschar *Xpp; + const uschar *Xprev; + const uschar *Xsaved_eptr; + + recursion_info Xnew_recursive; + + BOOL Xcur_is_word; + BOOL Xcondition; + BOOL Xprev_is_word; + + unsigned long int Xoriginal_ims; + +#ifdef SUPPORT_UCP + int Xprop_type; + int Xprop_value; + int Xprop_fail_result; + int Xprop_category; + int Xprop_chartype; + int Xprop_script; + int Xoclength; + uschar Xocchars[8]; +#endif + + int Xctype; + unsigned int Xfc; + int Xfi; + int Xlength; + int Xmax; + int Xmin; + int Xnumber; + int Xoffset; + int Xop; + int Xsave_capture_last; + int Xsave_offset1, Xsave_offset2, Xsave_offset3; + int Xstacksave[REC_STACK_SAVE_MAX]; + + eptrblock Xnewptrb; + + /* Where to jump back to */ + + int Xwhere; + +} heapframe; + +#endif + + +/*************************************************************************** +***************************************************************************/ + + + +/************************************************* +* Match from current position * +*************************************************/ + +/* This function is called recursively in many circumstances. Whenever it +returns a negative (error) response, the outer incarnation must also return the +same response. + +Performance note: It might be tempting to extract commonly used fields from the +md structure (e.g. utf8, end_subject) into individual variables to improve +performance. Tests using gcc on a SPARC disproved this; in the first case, it +made performance worse. + +Arguments: + eptr pointer to current character in subject + ecode pointer to current position in compiled code + mstart pointer to the current match start position (can be modified + by encountering \K) + offset_top current top pointer + md pointer to "static" info for the match + ims current /i, /m, and /s options + eptrb pointer to chain of blocks containing eptr at start of + brackets - for testing for empty matches + flags can contain + match_condassert - this is an assertion condition + match_cbegroup - this is the start of an unlimited repeat + group that can match an empty string + rdepth the recursion depth + +Returns: MATCH_MATCH if matched ) these values are >= 0 + MATCH_NOMATCH if failed to match ) + a negative PCRE_ERROR_xxx value if aborted by an error condition + (e.g. stopped by repeated call or recursion limit) +*/ + +static int +match(REGISTER USPTR eptr, REGISTER const uschar *ecode, const uschar *mstart, + int offset_top, match_data *md, unsigned long int ims, eptrblock *eptrb, + int flags, unsigned int rdepth) +{ +/* These variables do not need to be preserved over recursion in this function, +so they can be ordinary variables in all cases. Mark some of them with +"register" because they are used a lot in loops. */ + +register int rrc; /* Returns from recursive calls */ +register int i; /* Used for loops not involving calls to RMATCH() */ +register unsigned int c; /* Character values not kept over RMATCH() calls */ +register BOOL utf8; /* Local copy of UTF-8 flag for speed */ + +BOOL minimize, possessive; /* Quantifier options */ + +/* When recursion is not being used, all "local" variables that have to be +preserved over calls to RMATCH() are part of a "frame" which is obtained from +heap storage. Set up the top-level frame here; others are obtained from the +heap whenever RMATCH() does a "recursion". See the macro definitions above. */ + +#ifdef NO_RECURSE +heapframe *frame = (pcre_stack_malloc)(sizeof(heapframe)); +frame->Xprevframe = NULL; /* Marks the top level */ + +/* Copy in the original argument variables */ + +frame->Xeptr = eptr; +frame->Xecode = ecode; +frame->Xmstart = mstart; +frame->Xoffset_top = offset_top; +frame->Xims = ims; +frame->Xeptrb = eptrb; +frame->Xflags = flags; +frame->Xrdepth = rdepth; + +/* This is where control jumps back to to effect "recursion" */ + +HEAP_RECURSE: + +/* Macros make the argument variables come from the current frame */ + +#define eptr frame->Xeptr +#define ecode frame->Xecode +#define mstart frame->Xmstart +#define offset_top frame->Xoffset_top +#define ims frame->Xims +#define eptrb frame->Xeptrb +#define flags frame->Xflags +#define rdepth frame->Xrdepth + +/* Ditto for the local variables */ + +#ifdef SUPPORT_UTF8 +#define charptr frame->Xcharptr +#endif +#define callpat frame->Xcallpat +#define data frame->Xdata +#define next frame->Xnext +#define pp frame->Xpp +#define prev frame->Xprev +#define saved_eptr frame->Xsaved_eptr + +#define new_recursive frame->Xnew_recursive + +#define cur_is_word frame->Xcur_is_word +#define condition frame->Xcondition +#define prev_is_word frame->Xprev_is_word + +#define original_ims frame->Xoriginal_ims + +#ifdef SUPPORT_UCP +#define prop_type frame->Xprop_type +#define prop_value frame->Xprop_value +#define prop_fail_result frame->Xprop_fail_result +#define prop_category frame->Xprop_category +#define prop_chartype frame->Xprop_chartype +#define prop_script frame->Xprop_script +#define oclength frame->Xoclength +#define occhars frame->Xocchars +#endif + +#define ctype frame->Xctype +#define fc frame->Xfc +#define fi frame->Xfi +#define length frame->Xlength +#define max frame->Xmax +#define min frame->Xmin +#define number frame->Xnumber +#define offset frame->Xoffset +#define op frame->Xop +#define save_capture_last frame->Xsave_capture_last +#define save_offset1 frame->Xsave_offset1 +#define save_offset2 frame->Xsave_offset2 +#define save_offset3 frame->Xsave_offset3 +#define stacksave frame->Xstacksave + +#define newptrb frame->Xnewptrb + +/* When recursion is being used, local variables are allocated on the stack and +get preserved during recursion in the normal way. In this environment, fi and +i, and fc and c, can be the same variables. */ + +#else /* NO_RECURSE not defined */ +#define fi i +#define fc c + + +#ifdef SUPPORT_UTF8 /* Many of these variables are used only */ +const uschar *charptr; /* in small blocks of the code. My normal */ +#endif /* style of coding would have declared */ +const uschar *callpat; /* them within each of those blocks. */ +const uschar *data; /* However, in order to accommodate the */ +const uschar *next; /* version of this code that uses an */ +USPTR pp; /* external "stack" implemented on the */ +const uschar *prev; /* heap, it is easier to declare them all */ +USPTR saved_eptr; /* here, so the declarations can be cut */ + /* out in a block. The only declarations */ +recursion_info new_recursive; /* within blocks below are for variables */ + /* that do not have to be preserved over */ +BOOL cur_is_word; /* a recursive call to RMATCH(). */ +BOOL condition; +BOOL prev_is_word; + +unsigned long int original_ims; + +#ifdef SUPPORT_UCP +int prop_type; +int prop_value; +int prop_fail_result; +int prop_category; +int prop_chartype; +int prop_script; +int oclength; +uschar occhars[8]; +#endif + +int ctype; +int length; +int max; +int min; +int number; +int offset; +int op; +int save_capture_last; +int save_offset1, save_offset2, save_offset3; +int stacksave[REC_STACK_SAVE_MAX]; + +eptrblock newptrb; +#endif /* NO_RECURSE */ + +/* These statements are here to stop the compiler complaining about unitialized +variables. */ + +#ifdef SUPPORT_UCP +prop_value = 0; +prop_fail_result = 0; +#endif + + +/* This label is used for tail recursion, which is used in a few cases even +when NO_RECURSE is not defined, in order to reduce the amount of stack that is +used. Thanks to Ian Taylor for noticing this possibility and sending the +original patch. */ + +TAIL_RECURSE: + +/* OK, now we can get on with the real code of the function. Recursive calls +are specified by the macro RMATCH and RRETURN is used to return. When +NO_RECURSE is *not* defined, these just turn into a recursive call to match() +and a "return", respectively (possibly with some debugging if DEBUG is +defined). However, RMATCH isn't like a function call because it's quite a +complicated macro. It has to be used in one particular way. This shouldn't, +however, impact performance when true recursion is being used. */ + +#ifdef SUPPORT_UTF8 +utf8 = md->utf8; /* Local copy of the flag */ +#else +utf8 = FALSE; +#endif + +/* First check that we haven't called match() too many times, or that we +haven't exceeded the recursive call limit. */ + +if (md->match_call_count++ >= md->match_limit) RRETURN(PCRE_ERROR_MATCHLIMIT); +if (rdepth >= md->match_limit_recursion) RRETURN(PCRE_ERROR_RECURSIONLIMIT); + +original_ims = ims; /* Save for resetting on ')' */ + +/* At the start of a group with an unlimited repeat that may match an empty +string, the match_cbegroup flag is set. When this is the case, add the current +subject pointer to the chain of such remembered pointers, to be checked when we +hit the closing ket, in order to break infinite loops that match no characters. +When match() is called in other circumstances, don't add to the chain. The +match_cbegroup flag must NOT be used with tail recursion, because the memory +block that is used is on the stack, so a new one may be required for each +match(). */ + +if ((flags & match_cbegroup) != 0) + { + newptrb.epb_saved_eptr = eptr; + newptrb.epb_prev = eptrb; + eptrb = &newptrb; + } + +/* Now start processing the opcodes. */ + +for (;;) + { + minimize = possessive = FALSE; + op = *ecode; + + /* For partial matching, remember if we ever hit the end of the subject after + matching at least one subject character. */ + + if (md->partial && + eptr >= md->end_subject && + eptr > mstart) + md->hitend = TRUE; + + switch(op) + { + case OP_FAIL: + RRETURN(MATCH_NOMATCH); + + case OP_PRUNE: + RMATCH(eptr, ecode + _pcre_OP_lengths[*ecode], offset_top, md, + ims, eptrb, flags, RM51); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + RRETURN(MATCH_PRUNE); + + case OP_COMMIT: + RMATCH(eptr, ecode + _pcre_OP_lengths[*ecode], offset_top, md, + ims, eptrb, flags, RM52); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + RRETURN(MATCH_COMMIT); + + case OP_SKIP: + RMATCH(eptr, ecode + _pcre_OP_lengths[*ecode], offset_top, md, + ims, eptrb, flags, RM53); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + md->start_match_ptr = eptr; /* Pass back current position */ + RRETURN(MATCH_SKIP); + + case OP_THEN: + RMATCH(eptr, ecode + _pcre_OP_lengths[*ecode], offset_top, md, + ims, eptrb, flags, RM54); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + RRETURN(MATCH_THEN); + + /* Handle a capturing bracket. If there is space in the offset vector, save + the current subject position in the working slot at the top of the vector. + We mustn't change the current values of the data slot, because they may be + set from a previous iteration of this group, and be referred to by a + reference inside the group. + + If the bracket fails to match, we need to restore this value and also the + values of the final offsets, in case they were set by a previous iteration + of the same bracket. + + If there isn't enough space in the offset vector, treat this as if it were + a non-capturing bracket. Don't worry about setting the flag for the error + case here; that is handled in the code for KET. */ + + case OP_CBRA: + case OP_SCBRA: + number = GET2(ecode, 1+LINK_SIZE); + offset = number << 1; + +#ifdef DEBUG + printf("start bracket %d\n", number); + printf("subject="); + pchars(eptr, 16, TRUE, md); + printf("\n"); +#endif + + if (offset < md->offset_max) + { + save_offset1 = md->offset_vector[offset]; + save_offset2 = md->offset_vector[offset+1]; + save_offset3 = md->offset_vector[md->offset_end - number]; + save_capture_last = md->capture_last; + + DPRINTF(("saving %d %d %d\n", save_offset1, save_offset2, save_offset3)); + md->offset_vector[md->offset_end - number] = eptr - md->start_subject; + + flags = (op == OP_SCBRA)? match_cbegroup : 0; + do + { + RMATCH(eptr, ecode + _pcre_OP_lengths[*ecode], offset_top, md, + ims, eptrb, flags, RM1); + if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) RRETURN(rrc); + md->capture_last = save_capture_last; + ecode += GET(ecode, 1); + } + while (*ecode == OP_ALT); + + DPRINTF(("bracket %d failed\n", number)); + + md->offset_vector[offset] = save_offset1; + md->offset_vector[offset+1] = save_offset2; + md->offset_vector[md->offset_end - number] = save_offset3; + + RRETURN(MATCH_NOMATCH); + } + + /* FALL THROUGH ... Insufficient room for saving captured contents. Treat + as a non-capturing bracket. */ + + /* VVVVVVVVVVVVVVVVVVVVVVVVV */ + /* VVVVVVVVVVVVVVVVVVVVVVVVV */ + + DPRINTF(("insufficient capture room: treat as non-capturing\n")); + + /* VVVVVVVVVVVVVVVVVVVVVVVVV */ + /* VVVVVVVVVVVVVVVVVVVVVVVVV */ + + /* Non-capturing bracket. Loop for all the alternatives. When we get to the + final alternative within the brackets, we would return the result of a + recursive call to match() whatever happened. We can reduce stack usage by + turning this into a tail recursion, except in the case when match_cbegroup + is set.*/ + + case OP_BRA: + case OP_SBRA: + DPRINTF(("start non-capturing bracket\n")); + flags = (op >= OP_SBRA)? match_cbegroup : 0; + for (;;) + { + if (ecode[GET(ecode, 1)] != OP_ALT) /* Final alternative */ + { + if (flags == 0) /* Not a possibly empty group */ + { + ecode += _pcre_OP_lengths[*ecode]; + DPRINTF(("bracket 0 tail recursion\n")); + goto TAIL_RECURSE; + } + + /* Possibly empty group; can't use tail recursion. */ + + RMATCH(eptr, ecode + _pcre_OP_lengths[*ecode], offset_top, md, ims, + eptrb, flags, RM48); + RRETURN(rrc); + } + + /* For non-final alternatives, continue the loop for a NOMATCH result; + otherwise return. */ + + RMATCH(eptr, ecode + _pcre_OP_lengths[*ecode], offset_top, md, ims, + eptrb, flags, RM2); + if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) RRETURN(rrc); + ecode += GET(ecode, 1); + } + /* Control never reaches here. */ + + /* Conditional group: compilation checked that there are no more than + two branches. If the condition is false, skipping the first branch takes us + past the end if there is only one branch, but that's OK because that is + exactly what going to the ket would do. As there is only one branch to be + obeyed, we can use tail recursion to avoid using another stack frame. */ + + case OP_COND: + case OP_SCOND: + if (ecode[LINK_SIZE+1] == OP_RREF) /* Recursion test */ + { + offset = GET2(ecode, LINK_SIZE + 2); /* Recursion group number*/ + condition = md->recursive != NULL && + (offset == RREF_ANY || offset == md->recursive->group_num); + ecode += condition? 3 : GET(ecode, 1); + } + + else if (ecode[LINK_SIZE+1] == OP_CREF) /* Group used test */ + { + offset = GET2(ecode, LINK_SIZE+2) << 1; /* Doubled ref number */ + condition = offset < offset_top && md->offset_vector[offset] >= 0; + ecode += condition? 3 : GET(ecode, 1); + } + + else if (ecode[LINK_SIZE+1] == OP_DEF) /* DEFINE - always false */ + { + condition = FALSE; + ecode += GET(ecode, 1); + } + + /* The condition is an assertion. Call match() to evaluate it - setting + the final argument match_condassert causes it to stop at the end of an + assertion. */ + + else + { + RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, NULL, + match_condassert, RM3); + if (rrc == MATCH_MATCH) + { + condition = TRUE; + ecode += 1 + LINK_SIZE + GET(ecode, LINK_SIZE + 2); + while (*ecode == OP_ALT) ecode += GET(ecode, 1); + } + else if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) + { + RRETURN(rrc); /* Need braces because of following else */ + } + else + { + condition = FALSE; + ecode += GET(ecode, 1); + } + } + + /* We are now at the branch that is to be obeyed. As there is only one, + we can use tail recursion to avoid using another stack frame, except when + match_cbegroup is required for an unlimited repeat of a possibly empty + group. If the second alternative doesn't exist, we can just plough on. */ + + if (condition || *ecode == OP_ALT) + { + ecode += 1 + LINK_SIZE; + if (op == OP_SCOND) /* Possibly empty group */ + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, match_cbegroup, RM49); + RRETURN(rrc); + } + else /* Group must match something */ + { + flags = 0; + goto TAIL_RECURSE; + } + } + else /* Condition false & no 2nd alternative */ + { + ecode += 1 + LINK_SIZE; + } + break; + + + /* End of the pattern, either real or forced. If we are in a top-level + recursion, we should restore the offsets appropriately and continue from + after the call. */ + + case OP_ACCEPT: + case OP_END: + if (md->recursive != NULL && md->recursive->group_num == 0) + { + recursion_info *rec = md->recursive; + DPRINTF(("End of pattern in a (?0) recursion\n")); + md->recursive = rec->prevrec; + memmove(md->offset_vector, rec->offset_save, + rec->saved_max * sizeof(int)); + mstart = rec->save_start; + ims = original_ims; + ecode = rec->after_call; + break; + } + + /* Otherwise, if PCRE_NOTEMPTY is set, fail if we have matched an empty + string - backtracking will then try other alternatives, if any. */ + + if (md->notempty && eptr == mstart) RRETURN(MATCH_NOMATCH); + md->end_match_ptr = eptr; /* Record where we ended */ + md->end_offset_top = offset_top; /* and how many extracts were taken */ + md->start_match_ptr = mstart; /* and the start (\K can modify) */ + RRETURN(MATCH_MATCH); + + /* Change option settings */ + + case OP_OPT: + ims = ecode[1]; + ecode += 2; + DPRINTF(("ims set to %02lx\n", ims)); + break; + + /* Assertion brackets. Check the alternative branches in turn - the + matching won't pass the KET for an assertion. If any one branch matches, + the assertion is true. Lookbehind assertions have an OP_REVERSE item at the + start of each branch to move the current point backwards, so the code at + this level is identical to the lookahead case. */ + + case OP_ASSERT: + case OP_ASSERTBACK: + do + { + RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, NULL, 0, + RM4); + if (rrc == MATCH_MATCH) break; + if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) RRETURN(rrc); + ecode += GET(ecode, 1); + } + while (*ecode == OP_ALT); + if (*ecode == OP_KET) RRETURN(MATCH_NOMATCH); + + /* If checking an assertion for a condition, return MATCH_MATCH. */ + + if ((flags & match_condassert) != 0) RRETURN(MATCH_MATCH); + + /* Continue from after the assertion, updating the offsets high water + mark, since extracts may have been taken during the assertion. */ + + do ecode += GET(ecode,1); while (*ecode == OP_ALT); + ecode += 1 + LINK_SIZE; + offset_top = md->end_offset_top; + continue; + + /* Negative assertion: all branches must fail to match */ + + case OP_ASSERT_NOT: + case OP_ASSERTBACK_NOT: + do + { + RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, NULL, 0, + RM5); + if (rrc == MATCH_MATCH) RRETURN(MATCH_NOMATCH); + if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) RRETURN(rrc); + ecode += GET(ecode,1); + } + while (*ecode == OP_ALT); + + if ((flags & match_condassert) != 0) RRETURN(MATCH_MATCH); + + ecode += 1 + LINK_SIZE; + continue; + + /* Move the subject pointer back. This occurs only at the start of + each branch of a lookbehind assertion. If we are too close to the start to + move back, this match function fails. When working with UTF-8 we move + back a number of characters, not bytes. */ + + case OP_REVERSE: +#ifdef SUPPORT_UTF8 + if (utf8) + { + i = GET(ecode, 1); + while (i-- > 0) + { + eptr--; + if (eptr < md->start_subject) RRETURN(MATCH_NOMATCH); + BACKCHAR(eptr); + } + } + else +#endif + + /* No UTF-8 support, or not in UTF-8 mode: count is byte count */ + + { + eptr -= GET(ecode, 1); + if (eptr < md->start_subject) RRETURN(MATCH_NOMATCH); + } + + /* Skip to next op code */ + + ecode += 1 + LINK_SIZE; + break; + + /* The callout item calls an external function, if one is provided, passing + details of the match so far. This is mainly for debugging, though the + function is able to force a failure. */ + + case OP_CALLOUT: + if (pcre_callout != NULL) + { + pcre_callout_block cb; + cb.version = 1; /* Version 1 of the callout block */ + cb.callout_number = ecode[1]; + cb.offset_vector = md->offset_vector; + cb.subject = (PCRE_SPTR)md->start_subject; + cb.subject_length = md->end_subject - md->start_subject; + cb.start_match = mstart - md->start_subject; + cb.current_position = eptr - md->start_subject; + cb.pattern_position = GET(ecode, 2); + cb.next_item_length = GET(ecode, 2 + LINK_SIZE); + cb.capture_top = offset_top/2; + cb.capture_last = md->capture_last; + cb.callout_data = md->callout_data; + if ((rrc = (*pcre_callout)(&cb)) > 0) RRETURN(MATCH_NOMATCH); + if (rrc < 0) RRETURN(rrc); + } + ecode += 2 + 2*LINK_SIZE; + break; + + /* Recursion either matches the current regex, or some subexpression. The + offset data is the offset to the starting bracket from the start of the + whole pattern. (This is so that it works from duplicated subpatterns.) + + If there are any capturing brackets started but not finished, we have to + save their starting points and reinstate them after the recursion. However, + we don't know how many such there are (offset_top records the completed + total) so we just have to save all the potential data. There may be up to + 65535 such values, which is too large to put on the stack, but using malloc + for small numbers seems expensive. As a compromise, the stack is used when + there are no more than REC_STACK_SAVE_MAX values to store; otherwise malloc + is used. A problem is what to do if the malloc fails ... there is no way of + returning to the top level with an error. Save the top REC_STACK_SAVE_MAX + values on the stack, and accept that the rest may be wrong. + + There are also other values that have to be saved. We use a chained + sequence of blocks that actually live on the stack. Thanks to Robin Houston + for the original version of this logic. */ + + case OP_RECURSE: + { + callpat = md->start_code + GET(ecode, 1); + new_recursive.group_num = (callpat == md->start_code)? 0 : + GET2(callpat, 1 + LINK_SIZE); + + /* Add to "recursing stack" */ + + new_recursive.prevrec = md->recursive; + md->recursive = &new_recursive; + + /* Find where to continue from afterwards */ + + ecode += 1 + LINK_SIZE; + new_recursive.after_call = ecode; + + /* Now save the offset data. */ + + new_recursive.saved_max = md->offset_end; + if (new_recursive.saved_max <= REC_STACK_SAVE_MAX) + new_recursive.offset_save = stacksave; + else + { + new_recursive.offset_save = + (int *)(pcre_malloc)(new_recursive.saved_max * sizeof(int)); + if (new_recursive.offset_save == NULL) RRETURN(PCRE_ERROR_NOMEMORY); + } + + memcpy(new_recursive.offset_save, md->offset_vector, + new_recursive.saved_max * sizeof(int)); + new_recursive.save_start = mstart; + mstart = eptr; + + /* OK, now we can do the recursion. For each top-level alternative we + restore the offset and recursion data. */ + + DPRINTF(("Recursing into group %d\n", new_recursive.group_num)); + flags = (*callpat >= OP_SBRA)? match_cbegroup : 0; + do + { + RMATCH(eptr, callpat + _pcre_OP_lengths[*callpat], offset_top, + md, ims, eptrb, flags, RM6); + if (rrc == MATCH_MATCH) + { + DPRINTF(("Recursion matched\n")); + md->recursive = new_recursive.prevrec; + if (new_recursive.offset_save != stacksave) + (pcre_free)(new_recursive.offset_save); + RRETURN(MATCH_MATCH); + } + else if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) + { + DPRINTF(("Recursion gave error %d\n", rrc)); + RRETURN(rrc); + } + + md->recursive = &new_recursive; + memcpy(md->offset_vector, new_recursive.offset_save, + new_recursive.saved_max * sizeof(int)); + callpat += GET(callpat, 1); + } + while (*callpat == OP_ALT); + + DPRINTF(("Recursion didn't match\n")); + md->recursive = new_recursive.prevrec; + if (new_recursive.offset_save != stacksave) + (pcre_free)(new_recursive.offset_save); + RRETURN(MATCH_NOMATCH); + } + /* Control never reaches here */ + + /* "Once" brackets are like assertion brackets except that after a match, + the point in the subject string is not moved back. Thus there can never be + a move back into the brackets. Friedl calls these "atomic" subpatterns. + Check the alternative branches in turn - the matching won't pass the KET + for this kind of subpattern. If any one branch matches, we carry on as at + the end of a normal bracket, leaving the subject pointer. */ + + case OP_ONCE: + prev = ecode; + saved_eptr = eptr; + + do + { + RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb, 0, RM7); + if (rrc == MATCH_MATCH) break; + if (rrc != MATCH_NOMATCH && rrc != MATCH_THEN) RRETURN(rrc); + ecode += GET(ecode,1); + } + while (*ecode == OP_ALT); + + /* If hit the end of the group (which could be repeated), fail */ + + if (*ecode != OP_ONCE && *ecode != OP_ALT) RRETURN(MATCH_NOMATCH); + + /* Continue as from after the assertion, updating the offsets high water + mark, since extracts may have been taken. */ + + do ecode += GET(ecode, 1); while (*ecode == OP_ALT); + + offset_top = md->end_offset_top; + eptr = md->end_match_ptr; + + /* For a non-repeating ket, just continue at this level. This also + happens for a repeating ket if no characters were matched in the group. + This is the forcible breaking of infinite loops as implemented in Perl + 5.005. If there is an options reset, it will get obeyed in the normal + course of events. */ + + if (*ecode == OP_KET || eptr == saved_eptr) + { + ecode += 1+LINK_SIZE; + break; + } + + /* The repeating kets try the rest of the pattern or restart from the + preceding bracket, in the appropriate order. The second "call" of match() + uses tail recursion, to avoid using another stack frame. We need to reset + any options that changed within the bracket before re-running it, so + check the next opcode. */ + + if (ecode[1+LINK_SIZE] == OP_OPT) + { + ims = (ims & ~PCRE_IMS) | ecode[4]; + DPRINTF(("ims set to %02lx at group repeat\n", ims)); + } + + if (*ecode == OP_KETRMIN) + { + RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb, 0, RM8); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + ecode = prev; + flags = 0; + goto TAIL_RECURSE; + } + else /* OP_KETRMAX */ + { + RMATCH(eptr, prev, offset_top, md, ims, eptrb, match_cbegroup, RM9); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + ecode += 1 + LINK_SIZE; + flags = 0; + goto TAIL_RECURSE; + } + /* Control never gets here */ + + /* An alternation is the end of a branch; scan along to find the end of the + bracketed group and go to there. */ + + case OP_ALT: + do ecode += GET(ecode,1); while (*ecode == OP_ALT); + break; + + /* BRAZERO and BRAMINZERO occur just before a bracket group, indicating + that it may occur zero times. It may repeat infinitely, or not at all - + i.e. it could be ()* or ()? in the pattern. Brackets with fixed upper + repeat limits are compiled as a number of copies, with the optional ones + preceded by BRAZERO or BRAMINZERO. */ + + case OP_BRAZERO: + { + next = ecode+1; + RMATCH(eptr, next, offset_top, md, ims, eptrb, 0, RM10); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + do next += GET(next,1); while (*next == OP_ALT); + ecode = next + 1 + LINK_SIZE; + } + break; + + case OP_BRAMINZERO: + { + next = ecode+1; + do next += GET(next, 1); while (*next == OP_ALT); + RMATCH(eptr, next + 1+LINK_SIZE, offset_top, md, ims, eptrb, 0, RM11); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + ecode++; + } + break; + + /* End of a group, repeated or non-repeating. */ + + case OP_KET: + case OP_KETRMIN: + case OP_KETRMAX: + prev = ecode - GET(ecode, 1); + + /* If this was a group that remembered the subject start, in order to break + infinite repeats of empty string matches, retrieve the subject start from + the chain. Otherwise, set it NULL. */ + + if (*prev >= OP_SBRA) + { + saved_eptr = eptrb->epb_saved_eptr; /* Value at start of group */ + eptrb = eptrb->epb_prev; /* Backup to previous group */ + } + else saved_eptr = NULL; + + /* If we are at the end of an assertion group, stop matching and return + MATCH_MATCH, but record the current high water mark for use by positive + assertions. Do this also for the "once" (atomic) groups. */ + + if (*prev == OP_ASSERT || *prev == OP_ASSERT_NOT || + *prev == OP_ASSERTBACK || *prev == OP_ASSERTBACK_NOT || + *prev == OP_ONCE) + { + md->end_match_ptr = eptr; /* For ONCE */ + md->end_offset_top = offset_top; + RRETURN(MATCH_MATCH); + } + + /* For capturing groups we have to check the group number back at the start + and if necessary complete handling an extraction by setting the offsets and + bumping the high water mark. Note that whole-pattern recursion is coded as + a recurse into group 0, so it won't be picked up here. Instead, we catch it + when the OP_END is reached. Other recursion is handled here. */ + + if (*prev == OP_CBRA || *prev == OP_SCBRA) + { + number = GET2(prev, 1+LINK_SIZE); + offset = number << 1; + +#ifdef DEBUG + printf("end bracket %d", number); + printf("\n"); +#endif + + md->capture_last = number; + if (offset >= md->offset_max) md->offset_overflow = TRUE; else + { + md->offset_vector[offset] = + md->offset_vector[md->offset_end - number]; + md->offset_vector[offset+1] = eptr - md->start_subject; + if (offset_top <= offset) offset_top = offset + 2; + } + + /* Handle a recursively called group. Restore the offsets + appropriately and continue from after the call. */ + + if (md->recursive != NULL && md->recursive->group_num == number) + { + recursion_info *rec = md->recursive; + DPRINTF(("Recursion (%d) succeeded - continuing\n", number)); + md->recursive = rec->prevrec; + mstart = rec->save_start; + memcpy(md->offset_vector, rec->offset_save, + rec->saved_max * sizeof(int)); + ecode = rec->after_call; + ims = original_ims; + break; + } + } + + /* For both capturing and non-capturing groups, reset the value of the ims + flags, in case they got changed during the group. */ + + ims = original_ims; + DPRINTF(("ims reset to %02lx\n", ims)); + + /* For a non-repeating ket, just continue at this level. This also + happens for a repeating ket if no characters were matched in the group. + This is the forcible breaking of infinite loops as implemented in Perl + 5.005. If there is an options reset, it will get obeyed in the normal + course of events. */ + + if (*ecode == OP_KET || eptr == saved_eptr) + { + ecode += 1 + LINK_SIZE; + break; + } + + /* The repeating kets try the rest of the pattern or restart from the + preceding bracket, in the appropriate order. In the second case, we can use + tail recursion to avoid using another stack frame, unless we have an + unlimited repeat of a group that can match an empty string. */ + + flags = (*prev >= OP_SBRA)? match_cbegroup : 0; + + if (*ecode == OP_KETRMIN) + { + RMATCH(eptr, ecode + 1 + LINK_SIZE, offset_top, md, ims, eptrb, 0, RM12); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (flags != 0) /* Could match an empty string */ + { + RMATCH(eptr, prev, offset_top, md, ims, eptrb, flags, RM50); + RRETURN(rrc); + } + ecode = prev; + goto TAIL_RECURSE; + } + else /* OP_KETRMAX */ + { + RMATCH(eptr, prev, offset_top, md, ims, eptrb, flags, RM13); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + ecode += 1 + LINK_SIZE; + flags = 0; + goto TAIL_RECURSE; + } + /* Control never gets here */ + + /* Start of subject unless notbol, or after internal newline if multiline */ + + case OP_CIRC: + if (md->notbol && eptr == md->start_subject) RRETURN(MATCH_NOMATCH); + if ((ims & PCRE_MULTILINE) != 0) + { + if (eptr != md->start_subject && + (eptr == md->end_subject || !WAS_NEWLINE(eptr))) + RRETURN(MATCH_NOMATCH); + ecode++; + break; + } + /* ... else fall through */ + + /* Start of subject assertion */ + + case OP_SOD: + if (eptr != md->start_subject) RRETURN(MATCH_NOMATCH); + ecode++; + break; + + /* Start of match assertion */ + + case OP_SOM: + if (eptr != md->start_subject + md->start_offset) RRETURN(MATCH_NOMATCH); + ecode++; + break; + + /* Reset the start of match point */ + + case OP_SET_SOM: + mstart = eptr; + ecode++; + break; + + /* Assert before internal newline if multiline, or before a terminating + newline unless endonly is set, else end of subject unless noteol is set. */ + + case OP_DOLL: + if ((ims & PCRE_MULTILINE) != 0) + { + if (eptr < md->end_subject) + { if (!IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH); } + else + { if (md->noteol) RRETURN(MATCH_NOMATCH); } + ecode++; + break; + } + else + { + if (md->noteol) RRETURN(MATCH_NOMATCH); + if (!md->endonly) + { + if (eptr != md->end_subject && + (!IS_NEWLINE(eptr) || eptr != md->end_subject - md->nllen)) + RRETURN(MATCH_NOMATCH); + ecode++; + break; + } + } + /* ... else fall through for endonly */ + + /* End of subject assertion (\z) */ + + case OP_EOD: + if (eptr < md->end_subject) RRETURN(MATCH_NOMATCH); + ecode++; + break; + + /* End of subject or ending \n assertion (\Z) */ + + case OP_EODN: + if (eptr != md->end_subject && + (!IS_NEWLINE(eptr) || eptr != md->end_subject - md->nllen)) + RRETURN(MATCH_NOMATCH); + ecode++; + break; + + /* Word boundary assertions */ + + case OP_NOT_WORD_BOUNDARY: + case OP_WORD_BOUNDARY: + { + + /* Find out if the previous and current characters are "word" characters. + It takes a bit more work in UTF-8 mode. Characters > 255 are assumed to + be "non-word" characters. */ + +#ifdef SUPPORT_UTF8 + if (utf8) + { + if (eptr == md->start_subject) prev_is_word = FALSE; else + { + const uschar *lastptr = eptr - 1; + while((*lastptr & 0xc0) == 0x80) lastptr--; + GETCHAR(c, lastptr); + prev_is_word = c < 256 && (md->ctypes[c] & ctype_word) != 0; + } + if (eptr >= md->end_subject) cur_is_word = FALSE; else + { + GETCHAR(c, eptr); + cur_is_word = c < 256 && (md->ctypes[c] & ctype_word) != 0; + } + } + else +#endif + + /* More streamlined when not in UTF-8 mode */ + + { + prev_is_word = (eptr != md->start_subject) && + ((md->ctypes[eptr[-1]] & ctype_word) != 0); + cur_is_word = (eptr < md->end_subject) && + ((md->ctypes[*eptr] & ctype_word) != 0); + } + + /* Now see if the situation is what we want */ + + if ((*ecode++ == OP_WORD_BOUNDARY)? + cur_is_word == prev_is_word : cur_is_word != prev_is_word) + RRETURN(MATCH_NOMATCH); + } + break; + + /* Match a single character type; inline for speed */ + + case OP_ANY: + if ((ims & PCRE_DOTALL) == 0) + { + if (IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH); + } + if (eptr++ >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (utf8) + while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++; + ecode++; + break; + + /* Match a single byte, even in UTF-8 mode. This opcode really does match + any byte, even newline, independent of the setting of PCRE_DOTALL. */ + + case OP_ANYBYTE: + if (eptr++ >= md->end_subject) RRETURN(MATCH_NOMATCH); + ecode++; + break; + + case OP_NOT_DIGIT: + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINCTEST(c, eptr); + if ( +#ifdef SUPPORT_UTF8 + c < 256 && +#endif + (md->ctypes[c] & ctype_digit) != 0 + ) + RRETURN(MATCH_NOMATCH); + ecode++; + break; + + case OP_DIGIT: + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINCTEST(c, eptr); + if ( +#ifdef SUPPORT_UTF8 + c >= 256 || +#endif + (md->ctypes[c] & ctype_digit) == 0 + ) + RRETURN(MATCH_NOMATCH); + ecode++; + break; + + case OP_NOT_WHITESPACE: + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINCTEST(c, eptr); + if ( +#ifdef SUPPORT_UTF8 + c < 256 && +#endif + (md->ctypes[c] & ctype_space) != 0 + ) + RRETURN(MATCH_NOMATCH); + ecode++; + break; + + case OP_WHITESPACE: + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINCTEST(c, eptr); + if ( +#ifdef SUPPORT_UTF8 + c >= 256 || +#endif + (md->ctypes[c] & ctype_space) == 0 + ) + RRETURN(MATCH_NOMATCH); + ecode++; + break; + + case OP_NOT_WORDCHAR: + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINCTEST(c, eptr); + if ( +#ifdef SUPPORT_UTF8 + c < 256 && +#endif + (md->ctypes[c] & ctype_word) != 0 + ) + RRETURN(MATCH_NOMATCH); + ecode++; + break; + + case OP_WORDCHAR: + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINCTEST(c, eptr); + if ( +#ifdef SUPPORT_UTF8 + c >= 256 || +#endif + (md->ctypes[c] & ctype_word) == 0 + ) + RRETURN(MATCH_NOMATCH); + ecode++; + break; + + case OP_ANYNL: + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINCTEST(c, eptr); + switch(c) + { + default: RRETURN(MATCH_NOMATCH); + case 0x000d: + if (eptr < md->end_subject && *eptr == 0x0a) eptr++; + break; + + case 0x000a: + break; + + case 0x000b: + case 0x000c: + case 0x0085: + case 0x2028: + case 0x2029: + if (md->bsr_anycrlf) RRETURN(MATCH_NOMATCH); + break; + } + ecode++; + break; + + case OP_NOT_HSPACE: + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINCTEST(c, eptr); + switch(c) + { + default: break; + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ + RRETURN(MATCH_NOMATCH); + } + ecode++; + break; + + case OP_HSPACE: + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINCTEST(c, eptr); + switch(c) + { + default: RRETURN(MATCH_NOMATCH); + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ + break; + } + ecode++; + break; + + case OP_NOT_VSPACE: + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINCTEST(c, eptr); + switch(c) + { + default: break; + case 0x0a: /* LF */ + case 0x0b: /* VT */ + case 0x0c: /* FF */ + case 0x0d: /* CR */ + case 0x85: /* NEL */ + case 0x2028: /* LINE SEPARATOR */ + case 0x2029: /* PARAGRAPH SEPARATOR */ + RRETURN(MATCH_NOMATCH); + } + ecode++; + break; + + case OP_VSPACE: + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINCTEST(c, eptr); + switch(c) + { + default: RRETURN(MATCH_NOMATCH); + case 0x0a: /* LF */ + case 0x0b: /* VT */ + case 0x0c: /* FF */ + case 0x0d: /* CR */ + case 0x85: /* NEL */ + case 0x2028: /* LINE SEPARATOR */ + case 0x2029: /* PARAGRAPH SEPARATOR */ + break; + } + ecode++; + break; + +#ifdef SUPPORT_UCP + /* Check the next character by Unicode property. We will get here only + if the support is in the binary; otherwise a compile-time error occurs. */ + + case OP_PROP: + case OP_NOTPROP: + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINCTEST(c, eptr); + { + int chartype, script; + int category = _pcre_ucp_findprop(c, &chartype, &script); + + switch(ecode[1]) + { + case PT_ANY: + if (op == OP_NOTPROP) RRETURN(MATCH_NOMATCH); + break; + + case PT_LAMP: + if ((chartype == ucp_Lu || + chartype == ucp_Ll || + chartype == ucp_Lt) == (op == OP_NOTPROP)) + RRETURN(MATCH_NOMATCH); + break; + + case PT_GC: + if ((ecode[2] != category) == (op == OP_PROP)) + RRETURN(MATCH_NOMATCH); + break; + + case PT_PC: + if ((ecode[2] != chartype) == (op == OP_PROP)) + RRETURN(MATCH_NOMATCH); + break; + + case PT_SC: + if ((ecode[2] != script) == (op == OP_PROP)) + RRETURN(MATCH_NOMATCH); + break; + + default: + RRETURN(PCRE_ERROR_INTERNAL); + } + + ecode += 3; + } + break; + + /* Match an extended Unicode sequence. We will get here only if the support + is in the binary; otherwise a compile-time error occurs. */ + + case OP_EXTUNI: + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINCTEST(c, eptr); + { + int chartype, script; + int category = _pcre_ucp_findprop(c, &chartype, &script); + if (category == ucp_M) RRETURN(MATCH_NOMATCH); + while (eptr < md->end_subject) + { + int len = 1; + if (!utf8) c = *eptr; else + { + GETCHARLEN(c, eptr, len); + } + category = _pcre_ucp_findprop(c, &chartype, &script); + if (category != ucp_M) break; + eptr += len; + } + } + ecode++; + break; +#endif + + + /* Match a back reference, possibly repeatedly. Look past the end of the + item to see if there is repeat information following. The code is similar + to that for character classes, but repeated for efficiency. Then obey + similar code to character type repeats - written out again for speed. + However, if the referenced string is the empty string, always treat + it as matched, any number of times (otherwise there could be infinite + loops). */ + + case OP_REF: + { + offset = GET2(ecode, 1) << 1; /* Doubled ref number */ + ecode += 3; /* Advance past item */ + + /* If the reference is unset, set the length to be longer than the amount + of subject left; this ensures that every attempt at a match fails. We + can't just fail here, because of the possibility of quantifiers with zero + minima. */ + + length = (offset >= offset_top || md->offset_vector[offset] < 0)? + md->end_subject - eptr + 1 : + md->offset_vector[offset+1] - md->offset_vector[offset]; + + /* Set up for repetition, or handle the non-repeated case */ + + switch (*ecode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + c = *ecode++ - OP_CRSTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + minimize = (*ecode == OP_CRMINRANGE); + min = GET2(ecode, 1); + max = GET2(ecode, 3); + if (max == 0) max = INT_MAX; + ecode += 5; + break; + + default: /* No repeat follows */ + if (!match_ref(offset, eptr, length, md, ims)) RRETURN(MATCH_NOMATCH); + eptr += length; + continue; /* With the main loop */ + } + + /* If the length of the reference is zero, just continue with the + main loop. */ + + if (length == 0) continue; + + /* First, ensure the minimum number of matches are present. We get back + the length of the reference string explicitly rather than passing the + address of eptr, so that eptr can be a register variable. */ + + for (i = 1; i <= min; i++) + { + if (!match_ref(offset, eptr, length, md, ims)) RRETURN(MATCH_NOMATCH); + eptr += length; + } + + /* If min = max, continue at the same level without recursion. + They are not both allowed to be zero. */ + + if (min == max) continue; + + /* If minimizing, keep trying and advancing the pointer */ + + if (minimize) + { + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM14); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max || !match_ref(offset, eptr, length, md, ims)) + RRETURN(MATCH_NOMATCH); + eptr += length; + } + /* Control never gets here */ + } + + /* If maximizing, find the longest string and work backwards */ + + else + { + pp = eptr; + for (i = min; i < max; i++) + { + if (!match_ref(offset, eptr, length, md, ims)) break; + eptr += length; + } + while (eptr >= pp) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM15); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + eptr -= length; + } + RRETURN(MATCH_NOMATCH); + } + } + /* Control never gets here */ + + + + /* Match a bit-mapped character class, possibly repeatedly. This op code is + used when all the characters in the class have values in the range 0-255, + and either the matching is caseful, or the characters are in the range + 0-127 when UTF-8 processing is enabled. The only difference between + OP_CLASS and OP_NCLASS occurs when a data character outside the range is + encountered. + + First, look past the end of the item to see if there is repeat information + following. Then obey similar code to character type repeats - written out + again for speed. */ + + case OP_NCLASS: + case OP_CLASS: + { + data = ecode + 1; /* Save for matching */ + ecode += 33; /* Advance past the item */ + + switch (*ecode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + c = *ecode++ - OP_CRSTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + minimize = (*ecode == OP_CRMINRANGE); + min = GET2(ecode, 1); + max = GET2(ecode, 3); + if (max == 0) max = INT_MAX; + ecode += 5; + break; + + default: /* No repeat follows */ + min = max = 1; + break; + } + + /* First, ensure the minimum number of matches are present. */ + +#ifdef SUPPORT_UTF8 + /* UTF-8 mode */ + if (utf8) + { + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINC(c, eptr); + if (c > 255) + { + if (op == OP_CLASS) RRETURN(MATCH_NOMATCH); + } + else + { + if ((data[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH); + } + } + } + else +#endif + /* Not UTF-8 mode */ + { + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + c = *eptr++; + if ((data[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH); + } + } + + /* If max == min we can continue with the main loop without the + need to recurse. */ + + if (min == max) continue; + + /* If minimizing, keep testing the rest of the expression and advancing + the pointer while it matches the class. */ + + if (minimize) + { +#ifdef SUPPORT_UTF8 + /* UTF-8 mode */ + if (utf8) + { + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM16); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINC(c, eptr); + if (c > 255) + { + if (op == OP_CLASS) RRETURN(MATCH_NOMATCH); + } + else + { + if ((data[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH); + } + } + } + else +#endif + /* Not UTF-8 mode */ + { + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM17); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + c = *eptr++; + if ((data[c/8] & (1 << (c&7))) == 0) RRETURN(MATCH_NOMATCH); + } + } + /* Control never gets here */ + } + + /* If maximizing, find the longest possible run, then work backwards. */ + + else + { + pp = eptr; + +#ifdef SUPPORT_UTF8 + /* UTF-8 mode */ + if (utf8) + { + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(c, eptr, len); + if (c > 255) + { + if (op == OP_CLASS) break; + } + else + { + if ((data[c/8] & (1 << (c&7))) == 0) break; + } + eptr += len; + } + for (;;) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM18); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (eptr-- == pp) break; /* Stop if tried at original pos */ + BACKCHAR(eptr); + } + } + else +#endif + /* Not UTF-8 mode */ + { + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) break; + c = *eptr; + if ((data[c/8] & (1 << (c&7))) == 0) break; + eptr++; + } + while (eptr >= pp) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM19); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + eptr--; + } + } + + RRETURN(MATCH_NOMATCH); + } + } + /* Control never gets here */ + + + /* Match an extended character class. This opcode is encountered only + in UTF-8 mode, because that's the only time it is compiled. */ + +#ifdef SUPPORT_UTF8 + case OP_XCLASS: + { + data = ecode + 1 + LINK_SIZE; /* Save for matching */ + ecode += GET(ecode, 1); /* Advance past the item */ + + switch (*ecode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + c = *ecode++ - OP_CRSTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + minimize = (*ecode == OP_CRMINRANGE); + min = GET2(ecode, 1); + max = GET2(ecode, 3); + if (max == 0) max = INT_MAX; + ecode += 5; + break; + + default: /* No repeat follows */ + min = max = 1; + break; + } + + /* First, ensure the minimum number of matches are present. */ + + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINC(c, eptr); + if (!_pcre_xclass(c, data)) RRETURN(MATCH_NOMATCH); + } + + /* If max == min we can continue with the main loop without the + need to recurse. */ + + if (min == max) continue; + + /* If minimizing, keep testing the rest of the expression and advancing + the pointer while it matches the class. */ + + if (minimize) + { + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM20); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINC(c, eptr); + if (!_pcre_xclass(c, data)) RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + } + + /* If maximizing, find the longest possible run, then work backwards. */ + + else + { + pp = eptr; + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(c, eptr, len); + if (!_pcre_xclass(c, data)) break; + eptr += len; + } + for(;;) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM21); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (eptr-- == pp) break; /* Stop if tried at original pos */ + if (utf8) BACKCHAR(eptr); + } + RRETURN(MATCH_NOMATCH); + } + + /* Control never gets here */ + } +#endif /* End of XCLASS */ + + /* Match a single character, casefully */ + + case OP_CHAR: +#ifdef SUPPORT_UTF8 + if (utf8) + { + length = 1; + ecode++; + GETCHARLEN(fc, ecode, length); + if (length > md->end_subject - eptr) RRETURN(MATCH_NOMATCH); + while (length-- > 0) if (*ecode++ != *eptr++) RRETURN(MATCH_NOMATCH); + } + else +#endif + + /* Non-UTF-8 mode */ + { + if (md->end_subject - eptr < 1) RRETURN(MATCH_NOMATCH); + if (ecode[1] != *eptr++) RRETURN(MATCH_NOMATCH); + ecode += 2; + } + break; + + /* Match a single character, caselessly */ + + case OP_CHARNC: +#ifdef SUPPORT_UTF8 + if (utf8) + { + length = 1; + ecode++; + GETCHARLEN(fc, ecode, length); + + if (length > md->end_subject - eptr) RRETURN(MATCH_NOMATCH); + + /* If the pattern character's value is < 128, we have only one byte, and + can use the fast lookup table. */ + + if (fc < 128) + { + if (md->lcc[*ecode++] != md->lcc[*eptr++]) RRETURN(MATCH_NOMATCH); + } + + /* Otherwise we must pick up the subject character */ + + else + { + unsigned int dc; + GETCHARINC(dc, eptr); + ecode += length; + + /* If we have Unicode property support, we can use it to test the other + case of the character, if there is one. */ + + if (fc != dc) + { +#ifdef SUPPORT_UCP + if (dc != _pcre_ucp_othercase(fc)) +#endif + RRETURN(MATCH_NOMATCH); + } + } + } + else +#endif /* SUPPORT_UTF8 */ + + /* Non-UTF-8 mode */ + { + if (md->end_subject - eptr < 1) RRETURN(MATCH_NOMATCH); + if (md->lcc[ecode[1]] != md->lcc[*eptr++]) RRETURN(MATCH_NOMATCH); + ecode += 2; + } + break; + + /* Match a single character repeatedly. */ + + case OP_EXACT: + min = max = GET2(ecode, 1); + ecode += 3; + goto REPEATCHAR; + + case OP_POSUPTO: + possessive = TRUE; + /* Fall through */ + + case OP_UPTO: + case OP_MINUPTO: + min = 0; + max = GET2(ecode, 1); + minimize = *ecode == OP_MINUPTO; + ecode += 3; + goto REPEATCHAR; + + case OP_POSSTAR: + possessive = TRUE; + min = 0; + max = INT_MAX; + ecode++; + goto REPEATCHAR; + + case OP_POSPLUS: + possessive = TRUE; + min = 1; + max = INT_MAX; + ecode++; + goto REPEATCHAR; + + case OP_POSQUERY: + possessive = TRUE; + min = 0; + max = 1; + ecode++; + goto REPEATCHAR; + + case OP_STAR: + case OP_MINSTAR: + case OP_PLUS: + case OP_MINPLUS: + case OP_QUERY: + case OP_MINQUERY: + c = *ecode++ - OP_STAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + + /* Common code for all repeated single-character matches. We can give + up quickly if there are fewer than the minimum number of characters left in + the subject. */ + + REPEATCHAR: +#ifdef SUPPORT_UTF8 + if (utf8) + { + length = 1; + charptr = ecode; + GETCHARLEN(fc, ecode, length); + if (min * length > md->end_subject - eptr) RRETURN(MATCH_NOMATCH); + ecode += length; + + /* Handle multibyte character matching specially here. There is + support for caseless matching if UCP support is present. */ + + if (length > 1) + { +#ifdef SUPPORT_UCP + unsigned int othercase; + if ((ims & PCRE_CASELESS) != 0 && + (othercase = _pcre_ucp_othercase(fc)) != NOTACHAR) + oclength = _pcre_ord2utf8(othercase, occhars); + else oclength = 0; +#endif /* SUPPORT_UCP */ + + for (i = 1; i <= min; i++) + { + if (memcmp(eptr, charptr, length) == 0) eptr += length; +#ifdef SUPPORT_UCP + /* Need braces because of following else */ + else if (oclength == 0) { RRETURN(MATCH_NOMATCH); } + else + { + if (memcmp(eptr, occhars, oclength) != 0) RRETURN(MATCH_NOMATCH); + eptr += oclength; + } +#else /* without SUPPORT_UCP */ + else { RRETURN(MATCH_NOMATCH); } +#endif /* SUPPORT_UCP */ + } + + if (min == max) continue; + + if (minimize) + { + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM22); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + if (memcmp(eptr, charptr, length) == 0) eptr += length; +#ifdef SUPPORT_UCP + /* Need braces because of following else */ + else if (oclength == 0) { RRETURN(MATCH_NOMATCH); } + else + { + if (memcmp(eptr, occhars, oclength) != 0) RRETURN(MATCH_NOMATCH); + eptr += oclength; + } +#else /* without SUPPORT_UCP */ + else { RRETURN (MATCH_NOMATCH); } +#endif /* SUPPORT_UCP */ + } + /* Control never gets here */ + } + + else /* Maximize */ + { + pp = eptr; + for (i = min; i < max; i++) + { + if (eptr > md->end_subject - length) break; + if (memcmp(eptr, charptr, length) == 0) eptr += length; +#ifdef SUPPORT_UCP + else if (oclength == 0) break; + else + { + if (memcmp(eptr, occhars, oclength) != 0) break; + eptr += oclength; + } +#else /* without SUPPORT_UCP */ + else break; +#endif /* SUPPORT_UCP */ + } + + if (possessive) continue; + for(;;) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM23); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (eptr == pp) RRETURN(MATCH_NOMATCH); +#ifdef SUPPORT_UCP + eptr--; + BACKCHAR(eptr); +#else /* without SUPPORT_UCP */ + eptr -= length; +#endif /* SUPPORT_UCP */ + } + } + /* Control never gets here */ + } + + /* If the length of a UTF-8 character is 1, we fall through here, and + obey the code as for non-UTF-8 characters below, though in this case the + value of fc will always be < 128. */ + } + else +#endif /* SUPPORT_UTF8 */ + + /* When not in UTF-8 mode, load a single-byte character. */ + { + if (min > md->end_subject - eptr) RRETURN(MATCH_NOMATCH); + fc = *ecode++; + } + + /* The value of fc at this point is always less than 256, though we may or + may not be in UTF-8 mode. The code is duplicated for the caseless and + caseful cases, for speed, since matching characters is likely to be quite + common. First, ensure the minimum number of matches are present. If min = + max, continue at the same level without recursing. Otherwise, if + minimizing, keep trying the rest of the expression and advancing one + matching character if failing, up to the maximum. Alternatively, if + maximizing, find the maximum number of characters and work backwards. */ + + DPRINTF(("matching %c{%d,%d} against subject %.*s\n", fc, min, max, + max, eptr)); + + if ((ims & PCRE_CASELESS) != 0) + { + fc = md->lcc[fc]; + for (i = 1; i <= min; i++) + if (fc != md->lcc[*eptr++]) RRETURN(MATCH_NOMATCH); + if (min == max) continue; + if (minimize) + { + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM24); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max || eptr >= md->end_subject || + fc != md->lcc[*eptr++]) + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + } + else /* Maximize */ + { + pp = eptr; + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || fc != md->lcc[*eptr]) break; + eptr++; + } + if (possessive) continue; + while (eptr >= pp) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM25); + eptr--; + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + } + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + } + + /* Caseful comparisons (includes all multi-byte characters) */ + + else + { + for (i = 1; i <= min; i++) if (fc != *eptr++) RRETURN(MATCH_NOMATCH); + if (min == max) continue; + if (minimize) + { + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM26); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max || eptr >= md->end_subject || fc != *eptr++) + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + } + else /* Maximize */ + { + pp = eptr; + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || fc != *eptr) break; + eptr++; + } + if (possessive) continue; + while (eptr >= pp) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM27); + eptr--; + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + } + RRETURN(MATCH_NOMATCH); + } + } + /* Control never gets here */ + + /* Match a negated single one-byte character. The character we are + checking can be multibyte. */ + + case OP_NOT: + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + ecode++; + GETCHARINCTEST(c, eptr); + if ((ims & PCRE_CASELESS) != 0) + { +#ifdef SUPPORT_UTF8 + if (c < 256) +#endif + c = md->lcc[c]; + if (md->lcc[*ecode++] == c) RRETURN(MATCH_NOMATCH); + } + else + { + if (*ecode++ == c) RRETURN(MATCH_NOMATCH); + } + break; + + /* Match a negated single one-byte character repeatedly. This is almost a + repeat of the code for a repeated single character, but I haven't found a + nice way of commoning these up that doesn't require a test of the + positive/negative option for each character match. Maybe that wouldn't add + very much to the time taken, but character matching *is* what this is all + about... */ + + case OP_NOTEXACT: + min = max = GET2(ecode, 1); + ecode += 3; + goto REPEATNOTCHAR; + + case OP_NOTUPTO: + case OP_NOTMINUPTO: + min = 0; + max = GET2(ecode, 1); + minimize = *ecode == OP_NOTMINUPTO; + ecode += 3; + goto REPEATNOTCHAR; + + case OP_NOTPOSSTAR: + possessive = TRUE; + min = 0; + max = INT_MAX; + ecode++; + goto REPEATNOTCHAR; + + case OP_NOTPOSPLUS: + possessive = TRUE; + min = 1; + max = INT_MAX; + ecode++; + goto REPEATNOTCHAR; + + case OP_NOTPOSQUERY: + possessive = TRUE; + min = 0; + max = 1; + ecode++; + goto REPEATNOTCHAR; + + case OP_NOTPOSUPTO: + possessive = TRUE; + min = 0; + max = GET2(ecode, 1); + ecode += 3; + goto REPEATNOTCHAR; + + case OP_NOTSTAR: + case OP_NOTMINSTAR: + case OP_NOTPLUS: + case OP_NOTMINPLUS: + case OP_NOTQUERY: + case OP_NOTMINQUERY: + c = *ecode++ - OP_NOTSTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + + /* Common code for all repeated single-byte matches. We can give up quickly + if there are fewer than the minimum number of bytes left in the + subject. */ + + REPEATNOTCHAR: + if (min > md->end_subject - eptr) RRETURN(MATCH_NOMATCH); + fc = *ecode++; + + /* The code is duplicated for the caseless and caseful cases, for speed, + since matching characters is likely to be quite common. First, ensure the + minimum number of matches are present. If min = max, continue at the same + level without recursing. Otherwise, if minimizing, keep trying the rest of + the expression and advancing one matching character if failing, up to the + maximum. Alternatively, if maximizing, find the maximum number of + characters and work backwards. */ + + DPRINTF(("negative matching %c{%d,%d} against subject %.*s\n", fc, min, max, + max, eptr)); + + if ((ims & PCRE_CASELESS) != 0) + { + fc = md->lcc[fc]; + +#ifdef SUPPORT_UTF8 + /* UTF-8 mode */ + if (utf8) + { + register unsigned int d; + for (i = 1; i <= min; i++) + { + GETCHARINC(d, eptr); + if (d < 256) d = md->lcc[d]; + if (fc == d) RRETURN(MATCH_NOMATCH); + } + } + else +#endif + + /* Not UTF-8 mode */ + { + for (i = 1; i <= min; i++) + if (fc == md->lcc[*eptr++]) RRETURN(MATCH_NOMATCH); + } + + if (min == max) continue; + + if (minimize) + { +#ifdef SUPPORT_UTF8 + /* UTF-8 mode */ + if (utf8) + { + register unsigned int d; + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM28); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + GETCHARINC(d, eptr); + if (d < 256) d = md->lcc[d]; + if (fi >= max || eptr >= md->end_subject || fc == d) + RRETURN(MATCH_NOMATCH); + } + } + else +#endif + /* Not UTF-8 mode */ + { + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM29); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max || eptr >= md->end_subject || fc == md->lcc[*eptr++]) + RRETURN(MATCH_NOMATCH); + } + } + /* Control never gets here */ + } + + /* Maximize case */ + + else + { + pp = eptr; + +#ifdef SUPPORT_UTF8 + /* UTF-8 mode */ + if (utf8) + { + register unsigned int d; + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(d, eptr, len); + if (d < 256) d = md->lcc[d]; + if (fc == d) break; + eptr += len; + } + if (possessive) continue; + for(;;) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM30); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (eptr-- == pp) break; /* Stop if tried at original pos */ + BACKCHAR(eptr); + } + } + else +#endif + /* Not UTF-8 mode */ + { + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || fc == md->lcc[*eptr]) break; + eptr++; + } + if (possessive) continue; + while (eptr >= pp) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM31); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + eptr--; + } + } + + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + } + + /* Caseful comparisons */ + + else + { +#ifdef SUPPORT_UTF8 + /* UTF-8 mode */ + if (utf8) + { + register unsigned int d; + for (i = 1; i <= min; i++) + { + GETCHARINC(d, eptr); + if (fc == d) RRETURN(MATCH_NOMATCH); + } + } + else +#endif + /* Not UTF-8 mode */ + { + for (i = 1; i <= min; i++) + if (fc == *eptr++) RRETURN(MATCH_NOMATCH); + } + + if (min == max) continue; + + if (minimize) + { +#ifdef SUPPORT_UTF8 + /* UTF-8 mode */ + if (utf8) + { + register unsigned int d; + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM32); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + GETCHARINC(d, eptr); + if (fi >= max || eptr >= md->end_subject || fc == d) + RRETURN(MATCH_NOMATCH); + } + } + else +#endif + /* Not UTF-8 mode */ + { + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM33); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max || eptr >= md->end_subject || fc == *eptr++) + RRETURN(MATCH_NOMATCH); + } + } + /* Control never gets here */ + } + + /* Maximize case */ + + else + { + pp = eptr; + +#ifdef SUPPORT_UTF8 + /* UTF-8 mode */ + if (utf8) + { + register unsigned int d; + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(d, eptr, len); + if (fc == d) break; + eptr += len; + } + if (possessive) continue; + for(;;) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM34); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (eptr-- == pp) break; /* Stop if tried at original pos */ + BACKCHAR(eptr); + } + } + else +#endif + /* Not UTF-8 mode */ + { + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || fc == *eptr) break; + eptr++; + } + if (possessive) continue; + while (eptr >= pp) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM35); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + eptr--; + } + } + + RRETURN(MATCH_NOMATCH); + } + } + /* Control never gets here */ + + /* Match a single character type repeatedly; several different opcodes + share code. This is very similar to the code for single characters, but we + repeat it in the interests of efficiency. */ + + case OP_TYPEEXACT: + min = max = GET2(ecode, 1); + minimize = TRUE; + ecode += 3; + goto REPEATTYPE; + + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + min = 0; + max = GET2(ecode, 1); + minimize = *ecode == OP_TYPEMINUPTO; + ecode += 3; + goto REPEATTYPE; + + case OP_TYPEPOSSTAR: + possessive = TRUE; + min = 0; + max = INT_MAX; + ecode++; + goto REPEATTYPE; + + case OP_TYPEPOSPLUS: + possessive = TRUE; + min = 1; + max = INT_MAX; + ecode++; + goto REPEATTYPE; + + case OP_TYPEPOSQUERY: + possessive = TRUE; + min = 0; + max = 1; + ecode++; + goto REPEATTYPE; + + case OP_TYPEPOSUPTO: + possessive = TRUE; + min = 0; + max = GET2(ecode, 1); + ecode += 3; + goto REPEATTYPE; + + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + c = *ecode++ - OP_TYPESTAR; + minimize = (c & 1) != 0; + min = rep_min[c]; /* Pick up values from tables; */ + max = rep_max[c]; /* zero for max => infinity */ + if (max == 0) max = INT_MAX; + + /* Common code for all repeated single character type matches. Note that + in UTF-8 mode, '.' matches a character of any length, but for the other + character types, the valid characters are all one-byte long. */ + + REPEATTYPE: + ctype = *ecode++; /* Code for the character type */ + +#ifdef SUPPORT_UCP + if (ctype == OP_PROP || ctype == OP_NOTPROP) + { + prop_fail_result = ctype == OP_NOTPROP; + prop_type = *ecode++; + prop_value = *ecode++; + } + else prop_type = -1; +#endif + + /* First, ensure the minimum number of matches are present. Use inline + code for maximizing the speed, and do the type test once at the start + (i.e. keep it out of the loop). Also we can test that there are at least + the minimum number of bytes before we start. This isn't as effective in + UTF-8 mode, but it does no harm. Separate the UTF-8 code completely as that + is tidier. Also separate the UCP code, which can be the same for both UTF-8 + and single-bytes. */ + + if (min > md->end_subject - eptr) RRETURN(MATCH_NOMATCH); + if (min > 0) + { +#ifdef SUPPORT_UCP + if (prop_type >= 0) + { + switch(prop_type) + { + case PT_ANY: + if (prop_fail_result) RRETURN(MATCH_NOMATCH); + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINCTEST(c, eptr); + } + break; + + case PT_LAMP: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINCTEST(c, eptr); + prop_category = _pcre_ucp_findprop(c, &prop_chartype, &prop_script); + if ((prop_chartype == ucp_Lu || + prop_chartype == ucp_Ll || + prop_chartype == ucp_Lt) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + break; + + case PT_GC: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINCTEST(c, eptr); + prop_category = _pcre_ucp_findprop(c, &prop_chartype, &prop_script); + if ((prop_category == prop_value) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + break; + + case PT_PC: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINCTEST(c, eptr); + prop_category = _pcre_ucp_findprop(c, &prop_chartype, &prop_script); + if ((prop_chartype == prop_value) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + break; + + case PT_SC: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINCTEST(c, eptr); + prop_category = _pcre_ucp_findprop(c, &prop_chartype, &prop_script); + if ((prop_script == prop_value) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + break; + + default: + RRETURN(PCRE_ERROR_INTERNAL); + } + } + + /* Match extended Unicode sequences. We will get here only if the + support is in the binary; otherwise a compile-time error occurs. */ + + else if (ctype == OP_EXTUNI) + { + for (i = 1; i <= min; i++) + { + GETCHARINCTEST(c, eptr); + prop_category = _pcre_ucp_findprop(c, &prop_chartype, &prop_script); + if (prop_category == ucp_M) RRETURN(MATCH_NOMATCH); + while (eptr < md->end_subject) + { + int len = 1; + if (!utf8) c = *eptr; else + { + GETCHARLEN(c, eptr, len); + } + prop_category = _pcre_ucp_findprop(c, &prop_chartype, &prop_script); + if (prop_category != ucp_M) break; + eptr += len; + } + } + } + + else +#endif /* SUPPORT_UCP */ + +/* Handle all other cases when the coding is UTF-8 */ + +#ifdef SUPPORT_UTF8 + if (utf8) switch(ctype) + { + case OP_ANY: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject || + ((ims & PCRE_DOTALL) == 0 && IS_NEWLINE(eptr))) + RRETURN(MATCH_NOMATCH); + eptr++; + while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++; + } + break; + + case OP_ANYBYTE: + eptr += min; + break; + + case OP_ANYNL: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINC(c, eptr); + switch(c) + { + default: RRETURN(MATCH_NOMATCH); + case 0x000d: + if (eptr < md->end_subject && *eptr == 0x0a) eptr++; + break; + + case 0x000a: + break; + + case 0x000b: + case 0x000c: + case 0x0085: + case 0x2028: + case 0x2029: + if (md->bsr_anycrlf) RRETURN(MATCH_NOMATCH); + break; + } + } + break; + + case OP_NOT_HSPACE: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINC(c, eptr); + switch(c) + { + default: break; + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ + RRETURN(MATCH_NOMATCH); + } + } + break; + + case OP_HSPACE: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINC(c, eptr); + switch(c) + { + default: RRETURN(MATCH_NOMATCH); + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ + break; + } + } + break; + + case OP_NOT_VSPACE: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINC(c, eptr); + switch(c) + { + default: break; + case 0x0a: /* LF */ + case 0x0b: /* VT */ + case 0x0c: /* FF */ + case 0x0d: /* CR */ + case 0x85: /* NEL */ + case 0x2028: /* LINE SEPARATOR */ + case 0x2029: /* PARAGRAPH SEPARATOR */ + RRETURN(MATCH_NOMATCH); + } + } + break; + + case OP_VSPACE: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINC(c, eptr); + switch(c) + { + default: RRETURN(MATCH_NOMATCH); + case 0x0a: /* LF */ + case 0x0b: /* VT */ + case 0x0c: /* FF */ + case 0x0d: /* CR */ + case 0x85: /* NEL */ + case 0x2028: /* LINE SEPARATOR */ + case 0x2029: /* PARAGRAPH SEPARATOR */ + break; + } + } + break; + + case OP_NOT_DIGIT: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINC(c, eptr); + if (c < 128 && (md->ctypes[c] & ctype_digit) != 0) + RRETURN(MATCH_NOMATCH); + } + break; + + case OP_DIGIT: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject || + *eptr >= 128 || (md->ctypes[*eptr++] & ctype_digit) == 0) + RRETURN(MATCH_NOMATCH); + /* No need to skip more bytes - we know it's a 1-byte character */ + } + break; + + case OP_NOT_WHITESPACE: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject || + (*eptr < 128 && (md->ctypes[*eptr] & ctype_space) != 0)) + RRETURN(MATCH_NOMATCH); + while (++eptr < md->end_subject && (*eptr & 0xc0) == 0x80); + } + break; + + case OP_WHITESPACE: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject || + *eptr >= 128 || (md->ctypes[*eptr++] & ctype_space) == 0) + RRETURN(MATCH_NOMATCH); + /* No need to skip more bytes - we know it's a 1-byte character */ + } + break; + + case OP_NOT_WORDCHAR: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject || + (*eptr < 128 && (md->ctypes[*eptr] & ctype_word) != 0)) + RRETURN(MATCH_NOMATCH); + while (++eptr < md->end_subject && (*eptr & 0xc0) == 0x80); + } + break; + + case OP_WORDCHAR: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject || + *eptr >= 128 || (md->ctypes[*eptr++] & ctype_word) == 0) + RRETURN(MATCH_NOMATCH); + /* No need to skip more bytes - we know it's a 1-byte character */ + } + break; + + default: + RRETURN(PCRE_ERROR_INTERNAL); + } /* End switch(ctype) */ + + else +#endif /* SUPPORT_UTF8 */ + + /* Code for the non-UTF-8 case for minimum matching of operators other + than OP_PROP and OP_NOTPROP. We can assume that there are the minimum + number of bytes present, as this was tested above. */ + + switch(ctype) + { + case OP_ANY: + if ((ims & PCRE_DOTALL) == 0) + { + for (i = 1; i <= min; i++) + { + if (IS_NEWLINE(eptr)) RRETURN(MATCH_NOMATCH); + eptr++; + } + } + else eptr += min; + break; + + case OP_ANYBYTE: + eptr += min; + break; + + /* Because of the CRLF case, we can't assume the minimum number of + bytes are present in this case. */ + + case OP_ANYNL: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + switch(*eptr++) + { + default: RRETURN(MATCH_NOMATCH); + case 0x000d: + if (eptr < md->end_subject && *eptr == 0x0a) eptr++; + break; + case 0x000a: + break; + + case 0x000b: + case 0x000c: + case 0x0085: + if (md->bsr_anycrlf) RRETURN(MATCH_NOMATCH); + break; + } + } + break; + + case OP_NOT_HSPACE: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + switch(*eptr++) + { + default: break; + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + RRETURN(MATCH_NOMATCH); + } + } + break; + + case OP_HSPACE: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + switch(*eptr++) + { + default: RRETURN(MATCH_NOMATCH); + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + break; + } + } + break; + + case OP_NOT_VSPACE: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + switch(*eptr++) + { + default: break; + case 0x0a: /* LF */ + case 0x0b: /* VT */ + case 0x0c: /* FF */ + case 0x0d: /* CR */ + case 0x85: /* NEL */ + RRETURN(MATCH_NOMATCH); + } + } + break; + + case OP_VSPACE: + for (i = 1; i <= min; i++) + { + if (eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + switch(*eptr++) + { + default: RRETURN(MATCH_NOMATCH); + case 0x0a: /* LF */ + case 0x0b: /* VT */ + case 0x0c: /* FF */ + case 0x0d: /* CR */ + case 0x85: /* NEL */ + break; + } + } + break; + + case OP_NOT_DIGIT: + for (i = 1; i <= min; i++) + if ((md->ctypes[*eptr++] & ctype_digit) != 0) RRETURN(MATCH_NOMATCH); + break; + + case OP_DIGIT: + for (i = 1; i <= min; i++) + if ((md->ctypes[*eptr++] & ctype_digit) == 0) RRETURN(MATCH_NOMATCH); + break; + + case OP_NOT_WHITESPACE: + for (i = 1; i <= min; i++) + if ((md->ctypes[*eptr++] & ctype_space) != 0) RRETURN(MATCH_NOMATCH); + break; + + case OP_WHITESPACE: + for (i = 1; i <= min; i++) + if ((md->ctypes[*eptr++] & ctype_space) == 0) RRETURN(MATCH_NOMATCH); + break; + + case OP_NOT_WORDCHAR: + for (i = 1; i <= min; i++) + if ((md->ctypes[*eptr++] & ctype_word) != 0) + RRETURN(MATCH_NOMATCH); + break; + + case OP_WORDCHAR: + for (i = 1; i <= min; i++) + if ((md->ctypes[*eptr++] & ctype_word) == 0) + RRETURN(MATCH_NOMATCH); + break; + + default: + RRETURN(PCRE_ERROR_INTERNAL); + } + } + + /* If min = max, continue at the same level without recursing */ + + if (min == max) continue; + + /* If minimizing, we have to test the rest of the pattern before each + subsequent match. Again, separate the UTF-8 case for speed, and also + separate the UCP cases. */ + + if (minimize) + { +#ifdef SUPPORT_UCP + if (prop_type >= 0) + { + switch(prop_type) + { + case PT_ANY: + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM36); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINC(c, eptr); + if (prop_fail_result) RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + case PT_LAMP: + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM37); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINC(c, eptr); + prop_category = _pcre_ucp_findprop(c, &prop_chartype, &prop_script); + if ((prop_chartype == ucp_Lu || + prop_chartype == ucp_Ll || + prop_chartype == ucp_Lt) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + case PT_GC: + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM38); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINC(c, eptr); + prop_category = _pcre_ucp_findprop(c, &prop_chartype, &prop_script); + if ((prop_category == prop_value) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + case PT_PC: + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM39); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINC(c, eptr); + prop_category = _pcre_ucp_findprop(c, &prop_chartype, &prop_script); + if ((prop_chartype == prop_value) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + case PT_SC: + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM40); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINC(c, eptr); + prop_category = _pcre_ucp_findprop(c, &prop_chartype, &prop_script); + if ((prop_script == prop_value) == prop_fail_result) + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + default: + RRETURN(PCRE_ERROR_INTERNAL); + } + } + + /* Match extended Unicode sequences. We will get here only if the + support is in the binary; otherwise a compile-time error occurs. */ + + else if (ctype == OP_EXTUNI) + { + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM41); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max || eptr >= md->end_subject) RRETURN(MATCH_NOMATCH); + GETCHARINCTEST(c, eptr); + prop_category = _pcre_ucp_findprop(c, &prop_chartype, &prop_script); + if (prop_category == ucp_M) RRETURN(MATCH_NOMATCH); + while (eptr < md->end_subject) + { + int len = 1; + if (!utf8) c = *eptr; else + { + GETCHARLEN(c, eptr, len); + } + prop_category = _pcre_ucp_findprop(c, &prop_chartype, &prop_script); + if (prop_category != ucp_M) break; + eptr += len; + } + } + } + + else +#endif /* SUPPORT_UCP */ + +#ifdef SUPPORT_UTF8 + /* UTF-8 mode */ + if (utf8) + { + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM42); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max || eptr >= md->end_subject || + (ctype == OP_ANY && (ims & PCRE_DOTALL) == 0 && + IS_NEWLINE(eptr))) + RRETURN(MATCH_NOMATCH); + + GETCHARINC(c, eptr); + switch(ctype) + { + case OP_ANY: /* This is the DOTALL case */ + break; + + case OP_ANYBYTE: + break; + + case OP_ANYNL: + switch(c) + { + default: RRETURN(MATCH_NOMATCH); + case 0x000d: + if (eptr < md->end_subject && *eptr == 0x0a) eptr++; + break; + case 0x000a: + break; + + case 0x000b: + case 0x000c: + case 0x0085: + case 0x2028: + case 0x2029: + if (md->bsr_anycrlf) RRETURN(MATCH_NOMATCH); + break; + } + break; + + case OP_NOT_HSPACE: + switch(c) + { + default: break; + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ + RRETURN(MATCH_NOMATCH); + } + break; + + case OP_HSPACE: + switch(c) + { + default: RRETURN(MATCH_NOMATCH); + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ + break; + } + break; + + case OP_NOT_VSPACE: + switch(c) + { + default: break; + case 0x0a: /* LF */ + case 0x0b: /* VT */ + case 0x0c: /* FF */ + case 0x0d: /* CR */ + case 0x85: /* NEL */ + case 0x2028: /* LINE SEPARATOR */ + case 0x2029: /* PARAGRAPH SEPARATOR */ + RRETURN(MATCH_NOMATCH); + } + break; + + case OP_VSPACE: + switch(c) + { + default: RRETURN(MATCH_NOMATCH); + case 0x0a: /* LF */ + case 0x0b: /* VT */ + case 0x0c: /* FF */ + case 0x0d: /* CR */ + case 0x85: /* NEL */ + case 0x2028: /* LINE SEPARATOR */ + case 0x2029: /* PARAGRAPH SEPARATOR */ + break; + } + break; + + case OP_NOT_DIGIT: + if (c < 256 && (md->ctypes[c] & ctype_digit) != 0) + RRETURN(MATCH_NOMATCH); + break; + + case OP_DIGIT: + if (c >= 256 || (md->ctypes[c] & ctype_digit) == 0) + RRETURN(MATCH_NOMATCH); + break; + + case OP_NOT_WHITESPACE: + if (c < 256 && (md->ctypes[c] & ctype_space) != 0) + RRETURN(MATCH_NOMATCH); + break; + + case OP_WHITESPACE: + if (c >= 256 || (md->ctypes[c] & ctype_space) == 0) + RRETURN(MATCH_NOMATCH); + break; + + case OP_NOT_WORDCHAR: + if (c < 256 && (md->ctypes[c] & ctype_word) != 0) + RRETURN(MATCH_NOMATCH); + break; + + case OP_WORDCHAR: + if (c >= 256 || (md->ctypes[c] & ctype_word) == 0) + RRETURN(MATCH_NOMATCH); + break; + + default: + RRETURN(PCRE_ERROR_INTERNAL); + } + } + } + else +#endif + /* Not UTF-8 mode */ + { + for (fi = min;; fi++) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM43); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (fi >= max || eptr >= md->end_subject || + ((ims & PCRE_DOTALL) == 0 && IS_NEWLINE(eptr))) + RRETURN(MATCH_NOMATCH); + + c = *eptr++; + switch(ctype) + { + case OP_ANY: /* This is the DOTALL case */ + break; + + case OP_ANYBYTE: + break; + + case OP_ANYNL: + switch(c) + { + default: RRETURN(MATCH_NOMATCH); + case 0x000d: + if (eptr < md->end_subject && *eptr == 0x0a) eptr++; + break; + + case 0x000a: + break; + + case 0x000b: + case 0x000c: + case 0x0085: + if (md->bsr_anycrlf) RRETURN(MATCH_NOMATCH); + break; + } + break; + + case OP_NOT_HSPACE: + switch(c) + { + default: break; + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + RRETURN(MATCH_NOMATCH); + } + break; + + case OP_HSPACE: + switch(c) + { + default: RRETURN(MATCH_NOMATCH); + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + break; + } + break; + + case OP_NOT_VSPACE: + switch(c) + { + default: break; + case 0x0a: /* LF */ + case 0x0b: /* VT */ + case 0x0c: /* FF */ + case 0x0d: /* CR */ + case 0x85: /* NEL */ + RRETURN(MATCH_NOMATCH); + } + break; + + case OP_VSPACE: + switch(c) + { + default: RRETURN(MATCH_NOMATCH); + case 0x0a: /* LF */ + case 0x0b: /* VT */ + case 0x0c: /* FF */ + case 0x0d: /* CR */ + case 0x85: /* NEL */ + break; + } + break; + + case OP_NOT_DIGIT: + if ((md->ctypes[c] & ctype_digit) != 0) RRETURN(MATCH_NOMATCH); + break; + + case OP_DIGIT: + if ((md->ctypes[c] & ctype_digit) == 0) RRETURN(MATCH_NOMATCH); + break; + + case OP_NOT_WHITESPACE: + if ((md->ctypes[c] & ctype_space) != 0) RRETURN(MATCH_NOMATCH); + break; + + case OP_WHITESPACE: + if ((md->ctypes[c] & ctype_space) == 0) RRETURN(MATCH_NOMATCH); + break; + + case OP_NOT_WORDCHAR: + if ((md->ctypes[c] & ctype_word) != 0) RRETURN(MATCH_NOMATCH); + break; + + case OP_WORDCHAR: + if ((md->ctypes[c] & ctype_word) == 0) RRETURN(MATCH_NOMATCH); + break; + + default: + RRETURN(PCRE_ERROR_INTERNAL); + } + } + } + /* Control never gets here */ + } + + /* If maximizing, it is worth using inline code for speed, doing the type + test once at the start (i.e. keep it out of the loop). Again, keep the + UTF-8 and UCP stuff separate. */ + + else + { + pp = eptr; /* Remember where we started */ + +#ifdef SUPPORT_UCP + if (prop_type >= 0) + { + switch(prop_type) + { + case PT_ANY: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(c, eptr, len); + if (prop_fail_result) break; + eptr+= len; + } + break; + + case PT_LAMP: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(c, eptr, len); + prop_category = _pcre_ucp_findprop(c, &prop_chartype, &prop_script); + if ((prop_chartype == ucp_Lu || + prop_chartype == ucp_Ll || + prop_chartype == ucp_Lt) == prop_fail_result) + break; + eptr+= len; + } + break; + + case PT_GC: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(c, eptr, len); + prop_category = _pcre_ucp_findprop(c, &prop_chartype, &prop_script); + if ((prop_category == prop_value) == prop_fail_result) + break; + eptr+= len; + } + break; + + case PT_PC: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(c, eptr, len); + prop_category = _pcre_ucp_findprop(c, &prop_chartype, &prop_script); + if ((prop_chartype == prop_value) == prop_fail_result) + break; + eptr+= len; + } + break; + + case PT_SC: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(c, eptr, len); + prop_category = _pcre_ucp_findprop(c, &prop_chartype, &prop_script); + if ((prop_script == prop_value) == prop_fail_result) + break; + eptr+= len; + } + break; + } + + /* eptr is now past the end of the maximum run */ + + if (possessive) continue; + for(;;) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM44); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (eptr-- == pp) break; /* Stop if tried at original pos */ + if (utf8) BACKCHAR(eptr); + } + } + + /* Match extended Unicode sequences. We will get here only if the + support is in the binary; otherwise a compile-time error occurs. */ + + else if (ctype == OP_EXTUNI) + { + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) break; + GETCHARINCTEST(c, eptr); + prop_category = _pcre_ucp_findprop(c, &prop_chartype, &prop_script); + if (prop_category == ucp_M) break; + while (eptr < md->end_subject) + { + int len = 1; + if (!utf8) c = *eptr; else + { + GETCHARLEN(c, eptr, len); + } + prop_category = _pcre_ucp_findprop(c, &prop_chartype, &prop_script); + if (prop_category != ucp_M) break; + eptr += len; + } + } + + /* eptr is now past the end of the maximum run */ + + if (possessive) continue; + for(;;) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM45); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (eptr-- == pp) break; /* Stop if tried at original pos */ + for (;;) /* Move back over one extended */ + { + int len = 1; + if (!utf8) c = *eptr; else + { + BACKCHAR(eptr); + GETCHARLEN(c, eptr, len); + } + prop_category = _pcre_ucp_findprop(c, &prop_chartype, &prop_script); + if (prop_category != ucp_M) break; + eptr--; + } + } + } + + else +#endif /* SUPPORT_UCP */ + +#ifdef SUPPORT_UTF8 + /* UTF-8 mode */ + + if (utf8) + { + switch(ctype) + { + case OP_ANY: + if (max < INT_MAX) + { + if ((ims & PCRE_DOTALL) == 0) + { + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || IS_NEWLINE(eptr)) break; + eptr++; + while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++; + } + } + else + { + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) break; + eptr++; + while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++; + } + } + } + + /* Handle unlimited UTF-8 repeat */ + + else + { + if ((ims & PCRE_DOTALL) == 0) + { + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || IS_NEWLINE(eptr)) break; + eptr++; + while (eptr < md->end_subject && (*eptr & 0xc0) == 0x80) eptr++; + } + } + else + { + eptr = md->end_subject; + } + } + break; + + /* The byte case is the same as non-UTF8 */ + + case OP_ANYBYTE: + c = max - min; + if (c > (unsigned int)(md->end_subject - eptr)) + c = md->end_subject - eptr; + eptr += c; + break; + + case OP_ANYNL: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(c, eptr, len); + if (c == 0x000d) + { + if (++eptr >= md->end_subject) break; + if (*eptr == 0x000a) eptr++; + } + else + { + if (c != 0x000a && + (md->bsr_anycrlf || + (c != 0x000b && c != 0x000c && + c != 0x0085 && c != 0x2028 && c != 0x2029))) + break; + eptr += len; + } + } + break; + + case OP_NOT_HSPACE: + case OP_HSPACE: + for (i = min; i < max; i++) + { + BOOL gotspace; + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(c, eptr, len); + switch(c) + { + default: gotspace = FALSE; break; + case 0x09: /* HT */ + case 0x20: /* SPACE */ + case 0xa0: /* NBSP */ + case 0x1680: /* OGHAM SPACE MARK */ + case 0x180e: /* MONGOLIAN VOWEL SEPARATOR */ + case 0x2000: /* EN QUAD */ + case 0x2001: /* EM QUAD */ + case 0x2002: /* EN SPACE */ + case 0x2003: /* EM SPACE */ + case 0x2004: /* THREE-PER-EM SPACE */ + case 0x2005: /* FOUR-PER-EM SPACE */ + case 0x2006: /* SIX-PER-EM SPACE */ + case 0x2007: /* FIGURE SPACE */ + case 0x2008: /* PUNCTUATION SPACE */ + case 0x2009: /* THIN SPACE */ + case 0x200A: /* HAIR SPACE */ + case 0x202f: /* NARROW NO-BREAK SPACE */ + case 0x205f: /* MEDIUM MATHEMATICAL SPACE */ + case 0x3000: /* IDEOGRAPHIC SPACE */ + gotspace = TRUE; + break; + } + if (gotspace == (ctype == OP_NOT_HSPACE)) break; + eptr += len; + } + break; + + case OP_NOT_VSPACE: + case OP_VSPACE: + for (i = min; i < max; i++) + { + BOOL gotspace; + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(c, eptr, len); + switch(c) + { + default: gotspace = FALSE; break; + case 0x0a: /* LF */ + case 0x0b: /* VT */ + case 0x0c: /* FF */ + case 0x0d: /* CR */ + case 0x85: /* NEL */ + case 0x2028: /* LINE SEPARATOR */ + case 0x2029: /* PARAGRAPH SEPARATOR */ + gotspace = TRUE; + break; + } + if (gotspace == (ctype == OP_NOT_VSPACE)) break; + eptr += len; + } + break; + + case OP_NOT_DIGIT: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(c, eptr, len); + if (c < 256 && (md->ctypes[c] & ctype_digit) != 0) break; + eptr+= len; + } + break; + + case OP_DIGIT: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(c, eptr, len); + if (c >= 256 ||(md->ctypes[c] & ctype_digit) == 0) break; + eptr+= len; + } + break; + + case OP_NOT_WHITESPACE: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(c, eptr, len); + if (c < 256 && (md->ctypes[c] & ctype_space) != 0) break; + eptr+= len; + } + break; + + case OP_WHITESPACE: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(c, eptr, len); + if (c >= 256 ||(md->ctypes[c] & ctype_space) == 0) break; + eptr+= len; + } + break; + + case OP_NOT_WORDCHAR: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(c, eptr, len); + if (c < 256 && (md->ctypes[c] & ctype_word) != 0) break; + eptr+= len; + } + break; + + case OP_WORDCHAR: + for (i = min; i < max; i++) + { + int len = 1; + if (eptr >= md->end_subject) break; + GETCHARLEN(c, eptr, len); + if (c >= 256 || (md->ctypes[c] & ctype_word) == 0) break; + eptr+= len; + } + break; + + default: + RRETURN(PCRE_ERROR_INTERNAL); + } + + /* eptr is now past the end of the maximum run */ + + if (possessive) continue; + for(;;) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM46); + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + if (eptr-- == pp) break; /* Stop if tried at original pos */ + BACKCHAR(eptr); + } + } + else +#endif /* SUPPORT_UTF8 */ + + /* Not UTF-8 mode */ + { + switch(ctype) + { + case OP_ANY: + if ((ims & PCRE_DOTALL) == 0) + { + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || IS_NEWLINE(eptr)) break; + eptr++; + } + break; + } + /* For DOTALL case, fall through and treat as \C */ + + case OP_ANYBYTE: + c = max - min; + if (c > (unsigned int)(md->end_subject - eptr)) + c = md->end_subject - eptr; + eptr += c; + break; + + case OP_ANYNL: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) break; + c = *eptr; + if (c == 0x000d) + { + if (++eptr >= md->end_subject) break; + if (*eptr == 0x000a) eptr++; + } + else + { + if (c != 0x000a && + (md->bsr_anycrlf || + (c != 0x000b && c != 0x000c && c != 0x0085))) + break; + eptr++; + } + } + break; + + case OP_NOT_HSPACE: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) break; + c = *eptr; + if (c == 0x09 || c == 0x20 || c == 0xa0) break; + eptr++; + } + break; + + case OP_HSPACE: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) break; + c = *eptr; + if (c != 0x09 && c != 0x20 && c != 0xa0) break; + eptr++; + } + break; + + case OP_NOT_VSPACE: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) break; + c = *eptr; + if (c == 0x0a || c == 0x0b || c == 0x0c || c == 0x0d || c == 0x85) + break; + eptr++; + } + break; + + case OP_VSPACE: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject) break; + c = *eptr; + if (c != 0x0a && c != 0x0b && c != 0x0c && c != 0x0d && c != 0x85) + break; + eptr++; + } + break; + + case OP_NOT_DIGIT: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_digit) != 0) + break; + eptr++; + } + break; + + case OP_DIGIT: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_digit) == 0) + break; + eptr++; + } + break; + + case OP_NOT_WHITESPACE: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_space) != 0) + break; + eptr++; + } + break; + + case OP_WHITESPACE: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_space) == 0) + break; + eptr++; + } + break; + + case OP_NOT_WORDCHAR: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_word) != 0) + break; + eptr++; + } + break; + + case OP_WORDCHAR: + for (i = min; i < max; i++) + { + if (eptr >= md->end_subject || (md->ctypes[*eptr] & ctype_word) == 0) + break; + eptr++; + } + break; + + default: + RRETURN(PCRE_ERROR_INTERNAL); + } + + /* eptr is now past the end of the maximum run */ + + if (possessive) continue; + while (eptr >= pp) + { + RMATCH(eptr, ecode, offset_top, md, ims, eptrb, 0, RM47); + eptr--; + if (rrc != MATCH_NOMATCH) RRETURN(rrc); + } + } + + /* Get here if we can't make it match with any permitted repetitions */ + + RRETURN(MATCH_NOMATCH); + } + /* Control never gets here */ + + /* There's been some horrible disaster. Arrival here can only mean there is + something seriously wrong in the code above or the OP_xxx definitions. */ + + default: + DPRINTF(("Unknown opcode %d\n", *ecode)); + RRETURN(PCRE_ERROR_UNKNOWN_OPCODE); + } + + /* Do not stick any code in here without much thought; it is assumed + that "continue" in the code above comes out to here to repeat the main + loop. */ + + } /* End of main loop */ +/* Control never reaches here */ + + +/* When compiling to use the heap rather than the stack for recursive calls to +match(), the RRETURN() macro jumps here. The number that is saved in +frame->Xwhere indicates which label we actually want to return to. */ + +#ifdef NO_RECURSE +#define LBL(val) case val: goto L_RM##val; +HEAP_RETURN: +switch (frame->Xwhere) + { + LBL( 1) LBL( 2) LBL( 3) LBL( 4) LBL( 5) LBL( 6) LBL( 7) LBL( 8) + LBL( 9) LBL(10) LBL(11) LBL(12) LBL(13) LBL(14) LBL(15) LBL(17) + LBL(19) LBL(24) LBL(25) LBL(26) LBL(27) LBL(29) LBL(31) LBL(33) + LBL(35) LBL(43) LBL(47) LBL(48) LBL(49) LBL(50) LBL(51) LBL(52) + LBL(53) LBL(54) +#ifdef SUPPORT_UTF8 + LBL(16) LBL(18) LBL(20) LBL(21) LBL(22) LBL(23) LBL(28) LBL(30) + LBL(32) LBL(34) LBL(42) LBL(46) +#ifdef SUPPORT_UCP + LBL(36) LBL(37) LBL(38) LBL(39) LBL(40) LBL(41) LBL(44) LBL(45) +#endif /* SUPPORT_UCP */ +#endif /* SUPPORT_UTF8 */ + default: + DPRINTF(("jump error in pcre match: label %d non-existent\n", frame->Xwhere)); + return PCRE_ERROR_INTERNAL; + } +#undef LBL +#endif /* NO_RECURSE */ +} + + +/*************************************************************************** +**************************************************************************** + RECURSION IN THE match() FUNCTION + +Undefine all the macros that were defined above to handle this. */ + +#ifdef NO_RECURSE +#undef eptr +#undef ecode +#undef mstart +#undef offset_top +#undef ims +#undef eptrb +#undef flags + +#undef callpat +#undef charptr +#undef data +#undef next +#undef pp +#undef prev +#undef saved_eptr + +#undef new_recursive + +#undef cur_is_word +#undef condition +#undef prev_is_word + +#undef original_ims + +#undef ctype +#undef length +#undef max +#undef min +#undef number +#undef offset +#undef op +#undef save_capture_last +#undef save_offset1 +#undef save_offset2 +#undef save_offset3 +#undef stacksave + +#undef newptrb + +#endif + +/* These two are defined as macros in both cases */ + +#undef fc +#undef fi + +/*************************************************************************** +***************************************************************************/ + + + +/************************************************* +* Execute a Regular Expression * +*************************************************/ + +/* This function applies a compiled re to a subject string and picks out +portions of the string if it matches. Two elements in the vector are set for +each substring: the offsets to the start and end of the substring. + +Arguments: + argument_re points to the compiled expression + extra_data points to extra data or is NULL + subject points to the subject string + length length of subject string (may contain binary zeros) + start_offset where to start in the subject string + options option bits + offsets points to a vector of ints to be filled in with offsets + offsetcount the number of elements in the vector + +Returns: > 0 => success; value is the number of elements filled in + = 0 => success, but offsets is not big enough + -1 => failed to match + < -1 => some kind of unexpected problem +*/ + +PCRE_EXP_DEFN int +pcre_exec(const pcre *argument_re, const pcre_extra *extra_data, + PCRE_SPTR subject, int length, int start_offset, int options, int *offsets, + int offsetcount) +{ +int rc, resetcount, ocount; +int first_byte = -1; +int req_byte = -1; +int req_byte2 = -1; +int newline; +unsigned long int ims; +BOOL using_temporary_offsets = FALSE; +BOOL anchored; +BOOL startline; +BOOL firstline; +BOOL first_byte_caseless = FALSE; +BOOL req_byte_caseless = FALSE; +BOOL utf8; +match_data match_block; +match_data *md = &match_block; +const uschar *tables; +const uschar *start_bits = NULL; +USPTR start_match = (USPTR)subject + start_offset; +USPTR end_subject; +USPTR req_byte_ptr = start_match - 1; + +pcre_study_data internal_study; +const pcre_study_data *study; + +real_pcre internal_re; +const real_pcre *external_re = (const real_pcre *)argument_re; +const real_pcre *re = external_re; + +/* Plausibility checks */ + +if ((options & ~PUBLIC_EXEC_OPTIONS) != 0) return PCRE_ERROR_BADOPTION; +if (re == NULL || subject == NULL || + (offsets == NULL && offsetcount > 0)) return PCRE_ERROR_NULL; +if (offsetcount < 0) return PCRE_ERROR_BADCOUNT; + +/* Fish out the optional data from the extra_data structure, first setting +the default values. */ + +study = NULL; +md->match_limit = MATCH_LIMIT; +md->match_limit_recursion = MATCH_LIMIT_RECURSION; +md->callout_data = NULL; + +/* The table pointer is always in native byte order. */ + +tables = external_re->tables; + +if (extra_data != NULL) + { + register unsigned int flags = extra_data->flags; + if ((flags & PCRE_EXTRA_STUDY_DATA) != 0) + study = (const pcre_study_data *)extra_data->study_data; + if ((flags & PCRE_EXTRA_MATCH_LIMIT) != 0) + md->match_limit = extra_data->match_limit; + if ((flags & PCRE_EXTRA_MATCH_LIMIT_RECURSION) != 0) + md->match_limit_recursion = extra_data->match_limit_recursion; + if ((flags & PCRE_EXTRA_CALLOUT_DATA) != 0) + md->callout_data = extra_data->callout_data; + if ((flags & PCRE_EXTRA_TABLES) != 0) tables = extra_data->tables; + } + +/* If the exec call supplied NULL for tables, use the inbuilt ones. This +is a feature that makes it possible to save compiled regex and re-use them +in other programs later. */ + +if (tables == NULL) tables = _pcre_default_tables; + +/* Check that the first field in the block is the magic number. If it is not, +test for a regex that was compiled on a host of opposite endianness. If this is +the case, flipped values are put in internal_re and internal_study if there was +study data too. */ + +if (re->magic_number != MAGIC_NUMBER) + { + re = _pcre_try_flipped(re, &internal_re, study, &internal_study); + if (re == NULL) return PCRE_ERROR_BADMAGIC; + if (study != NULL) study = &internal_study; + } + +/* Set up other data */ + +anchored = ((re->options | options) & PCRE_ANCHORED) != 0; +startline = (re->flags & PCRE_STARTLINE) != 0; +firstline = (re->options & PCRE_FIRSTLINE) != 0; + +/* The code starts after the real_pcre block and the capture name table. */ + +md->start_code = (const uschar *)external_re + re->name_table_offset + + re->name_count * re->name_entry_size; + +md->start_subject = (USPTR)subject; +md->start_offset = start_offset; +md->end_subject = md->start_subject + length; +end_subject = md->end_subject; + +md->endonly = (re->options & PCRE_DOLLAR_ENDONLY) != 0; +utf8 = md->utf8 = (re->options & PCRE_UTF8) != 0; + +md->notbol = (options & PCRE_NOTBOL) != 0; +md->noteol = (options & PCRE_NOTEOL) != 0; +md->notempty = (options & PCRE_NOTEMPTY) != 0; +md->partial = (options & PCRE_PARTIAL) != 0; +md->hitend = FALSE; + +md->recursive = NULL; /* No recursion at top level */ + +md->lcc = tables + lcc_offset; +md->ctypes = tables + ctypes_offset; + +/* Handle different \R options. */ + +switch (options & (PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE)) + { + case 0: + if ((re->options & (PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE)) != 0) + md->bsr_anycrlf = (re->options & PCRE_BSR_ANYCRLF) != 0; + else +#ifdef BSR_ANYCRLF + md->bsr_anycrlf = TRUE; +#else + md->bsr_anycrlf = FALSE; +#endif + break; + + case PCRE_BSR_ANYCRLF: + md->bsr_anycrlf = TRUE; + break; + + case PCRE_BSR_UNICODE: + md->bsr_anycrlf = FALSE; + break; + + default: return PCRE_ERROR_BADNEWLINE; + } + +/* Handle different types of newline. The three bits give eight cases. If +nothing is set at run time, whatever was used at compile time applies. */ + +switch ((((options & PCRE_NEWLINE_BITS) == 0)? re->options : + (pcre_uint32)options) & PCRE_NEWLINE_BITS) + { + case 0: newline = NEWLINE; break; /* Compile-time default */ + case PCRE_NEWLINE_CR: newline = '\r'; break; + case PCRE_NEWLINE_LF: newline = '\n'; break; + case PCRE_NEWLINE_CR+ + PCRE_NEWLINE_LF: newline = ('\r' << 8) | '\n'; break; + case PCRE_NEWLINE_ANY: newline = -1; break; + case PCRE_NEWLINE_ANYCRLF: newline = -2; break; + default: return PCRE_ERROR_BADNEWLINE; + } + +if (newline == -2) + { + md->nltype = NLTYPE_ANYCRLF; + } +else if (newline < 0) + { + md->nltype = NLTYPE_ANY; + } +else + { + md->nltype = NLTYPE_FIXED; + if (newline > 255) + { + md->nllen = 2; + md->nl[0] = (newline >> 8) & 255; + md->nl[1] = newline & 255; + } + else + { + md->nllen = 1; + md->nl[0] = newline; + } + } + +/* Partial matching is supported only for a restricted set of regexes at the +moment. */ + +if (md->partial && (re->flags & PCRE_NOPARTIAL) != 0) + return PCRE_ERROR_BADPARTIAL; + +/* Check a UTF-8 string if required. Unfortunately there's no way of passing +back the character offset. */ + +#ifdef SUPPORT_UTF8 +if (utf8 && (options & PCRE_NO_UTF8_CHECK) == 0) + { + if (_pcre_valid_utf8((uschar *)subject, length) >= 0) + return PCRE_ERROR_BADUTF8; + if (start_offset > 0 && start_offset < length) + { + int tb = ((uschar *)subject)[start_offset]; + if (tb > 127) + { + tb &= 0xc0; + if (tb != 0 && tb != 0xc0) return PCRE_ERROR_BADUTF8_OFFSET; + } + } + } +#endif + +/* The ims options can vary during the matching as a result of the presence +of (?ims) items in the pattern. They are kept in a local variable so that +restoring at the exit of a group is easy. */ + +ims = re->options & (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL); + +/* If the expression has got more back references than the offsets supplied can +hold, we get a temporary chunk of working store to use during the matching. +Otherwise, we can use the vector supplied, rounding down its size to a multiple +of 3. */ + +ocount = offsetcount - (offsetcount % 3); + +if (re->top_backref > 0 && re->top_backref >= ocount/3) + { + ocount = re->top_backref * 3 + 3; + md->offset_vector = (int *)(pcre_malloc)(ocount * sizeof(int)); + if (md->offset_vector == NULL) return PCRE_ERROR_NOMEMORY; + using_temporary_offsets = TRUE; + DPRINTF(("Got memory to hold back references\n")); + } +else md->offset_vector = offsets; + +md->offset_end = ocount; +md->offset_max = (2*ocount)/3; +md->offset_overflow = FALSE; +md->capture_last = -1; + +/* Compute the minimum number of offsets that we need to reset each time. Doing +this makes a huge difference to execution time when there aren't many brackets +in the pattern. */ + +resetcount = 2 + re->top_bracket * 2; +if (resetcount > offsetcount) resetcount = ocount; + +/* Reset the working variable associated with each extraction. These should +never be used unless previously set, but they get saved and restored, and so we +initialize them to avoid reading uninitialized locations. */ + +if (md->offset_vector != NULL) + { + register int *iptr = md->offset_vector + ocount; + register int *iend = iptr - resetcount/2 + 1; + while (--iptr >= iend) *iptr = -1; + } + +/* Set up the first character to match, if available. The first_byte value is +never set for an anchored regular expression, but the anchoring may be forced +at run time, so we have to test for anchoring. The first char may be unset for +an unanchored pattern, of course. If there's no first char and the pattern was +studied, there may be a bitmap of possible first characters. */ + +if (!anchored) + { + if ((re->flags & PCRE_FIRSTSET) != 0) + { + first_byte = re->first_byte & 255; + if ((first_byte_caseless = ((re->first_byte & REQ_CASELESS) != 0)) == TRUE) + first_byte = md->lcc[first_byte]; + } + else + if (!startline && study != NULL && + (study->options & PCRE_STUDY_MAPPED) != 0) + start_bits = study->start_bits; + } + +/* For anchored or unanchored matches, there may be a "last known required +character" set. */ + +if ((re->flags & PCRE_REQCHSET) != 0) + { + req_byte = re->req_byte & 255; + req_byte_caseless = (re->req_byte & REQ_CASELESS) != 0; + req_byte2 = (tables + fcc_offset)[req_byte]; /* case flipped */ + } + + +/* ==========================================================================*/ + +/* Loop for handling unanchored repeated matching attempts; for anchored regexs +the loop runs just once. */ + +for(;;) + { + USPTR save_end_subject = end_subject; + USPTR new_start_match; + + /* Reset the maximum number of extractions we might see. */ + + if (md->offset_vector != NULL) + { + register int *iptr = md->offset_vector; + register int *iend = iptr + resetcount; + while (iptr < iend) *iptr++ = -1; + } + + /* Advance to a unique first char if possible. If firstline is TRUE, the + start of the match is constrained to the first line of a multiline string. + That is, the match must be before or at the first newline. Implement this by + temporarily adjusting end_subject so that we stop scanning at a newline. If + the match fails at the newline, later code breaks this loop. */ + + if (firstline) + { + USPTR t = start_match; + while (t < md->end_subject && !IS_NEWLINE(t)) t++; + end_subject = t; + } + + /* Now test for a unique first byte */ + + if (first_byte >= 0) + { + if (first_byte_caseless) + while (start_match < end_subject && + md->lcc[*start_match] != first_byte) + { NEXTCHAR(start_match); } + else + while (start_match < end_subject && *start_match != first_byte) + { NEXTCHAR(start_match); } + } + + /* Or to just after a linebreak for a multiline match if possible */ + + else if (startline) + { + if (start_match > md->start_subject + start_offset) + { + while (start_match <= end_subject && !WAS_NEWLINE(start_match)) + { NEXTCHAR(start_match); } + + /* If we have just passed a CR and the newline option is ANY or ANYCRLF, + and we are now at a LF, advance the match position by one more character. + */ + + if (start_match[-1] == '\r' && + (md->nltype == NLTYPE_ANY || md->nltype == NLTYPE_ANYCRLF) && + start_match < end_subject && + *start_match == '\n') + start_match++; + } + } + + /* Or to a non-unique first char after study */ + + else if (start_bits != NULL) + { + while (start_match < end_subject) + { + register unsigned int c = *start_match; + if ((start_bits[c/8] & (1 << (c&7))) == 0) + { NEXTCHAR(start_match); } + else break; + } + } + + /* Restore fudged end_subject */ + + end_subject = save_end_subject; + +#ifdef DEBUG /* Sigh. Some compilers never learn. */ + printf(">>>> Match against: "); + pchars(start_match, end_subject - start_match, TRUE, md); + printf("\n"); +#endif + + /* If req_byte is set, we know that that character must appear in the subject + for the match to succeed. If the first character is set, req_byte must be + later in the subject; otherwise the test starts at the match point. This + optimization can save a huge amount of backtracking in patterns with nested + unlimited repeats that aren't going to match. Writing separate code for + cased/caseless versions makes it go faster, as does using an autoincrement + and backing off on a match. + + HOWEVER: when the subject string is very, very long, searching to its end can + take a long time, and give bad performance on quite ordinary patterns. This + showed up when somebody was matching something like /^\d+C/ on a 32-megabyte + string... so we don't do this when the string is sufficiently long. + + ALSO: this processing is disabled when partial matching is requested. + */ + + if (req_byte >= 0 && + end_subject - start_match < REQ_BYTE_MAX && + !md->partial) + { + register USPTR p = start_match + ((first_byte >= 0)? 1 : 0); + + /* We don't need to repeat the search if we haven't yet reached the + place we found it at last time. */ + + if (p > req_byte_ptr) + { + if (req_byte_caseless) + { + while (p < end_subject) + { + register int pp = *p++; + if (pp == req_byte || pp == req_byte2) { p--; break; } + } + } + else + { + while (p < end_subject) + { + if (*p++ == req_byte) { p--; break; } + } + } + + /* If we can't find the required character, break the matching loop, + forcing a match failure. */ + + if (p >= end_subject) + { + rc = MATCH_NOMATCH; + break; + } + + /* If we have found the required character, save the point where we + found it, so that we don't search again next time round the loop if + the start hasn't passed this character yet. */ + + req_byte_ptr = p; + } + } + + /* OK, we can now run the match. */ + + md->start_match_ptr = start_match; + md->match_call_count = 0; + rc = match(start_match, md->start_code, start_match, 2, md, ims, NULL, 0, 0); + + switch(rc) + { + /* NOMATCH and PRUNE advance by one character. THEN at this level acts + exactly like PRUNE. */ + + case MATCH_NOMATCH: + case MATCH_PRUNE: + case MATCH_THEN: + new_start_match = start_match + 1; +#ifdef SUPPORT_UTF8 + if (utf8) + while(new_start_match < end_subject && (*new_start_match & 0xc0) == 0x80) + new_start_match++; +#endif + break; + + /* SKIP passes back the next starting point explicitly. */ + + case MATCH_SKIP: + new_start_match = md->start_match_ptr; + break; + + /* COMMIT disables the bumpalong, but otherwise behaves as NOMATCH. */ + + case MATCH_COMMIT: + rc = MATCH_NOMATCH; + goto ENDLOOP; + + /* Any other return is some kind of error. */ + + default: + goto ENDLOOP; + } + + /* Control reaches here for the various types of "no match at this point" + result. Reset the code to MATCH_NOMATCH for subsequent checking. */ + + rc = MATCH_NOMATCH; + + /* If PCRE_FIRSTLINE is set, the match must happen before or at the first + newline in the subject (though it may continue over the newline). Therefore, + if we have just failed to match, starting at a newline, do not continue. */ + + if (firstline && IS_NEWLINE(start_match)) break; + + /* Advance to new matching position */ + + start_match = new_start_match; + + /* Break the loop if the pattern is anchored or if we have passed the end of + the subject. */ + + if (anchored || start_match > end_subject) break; + + /* If we have just passed a CR and we are now at a LF, and the pattern does + not contain any explicit matches for \r or \n, and the newline option is CRLF + or ANY or ANYCRLF, advance the match position by one more character. */ + + if (start_match[-1] == '\r' && + start_match < end_subject && + *start_match == '\n' && + (re->flags & PCRE_HASCRORLF) == 0 && + (md->nltype == NLTYPE_ANY || + md->nltype == NLTYPE_ANYCRLF || + md->nllen == 2)) + start_match++; + + } /* End of for(;;) "bumpalong" loop */ + +/* ==========================================================================*/ + +/* We reach here when rc is not MATCH_NOMATCH, or if one of the stopping +conditions is true: + +(1) The pattern is anchored or the match was failed by (*COMMIT); + +(2) We are past the end of the subject; + +(3) PCRE_FIRSTLINE is set and we have failed to match at a newline, because + this option requests that a match occur at or before the first newline in + the subject. + +When we have a match and the offset vector is big enough to deal with any +backreferences, captured substring offsets will already be set up. In the case +where we had to get some local store to hold offsets for backreference +processing, copy those that we can. In this case there need not be overflow if +certain parts of the pattern were not used, even though there are more +capturing parentheses than vector slots. */ + +ENDLOOP: + +if (rc == MATCH_MATCH) + { + if (using_temporary_offsets) + { + if (offsetcount >= 4) + { + memcpy(offsets + 2, md->offset_vector + 2, + (offsetcount - 2) * sizeof(int)); + DPRINTF(("Copied offsets from temporary memory\n")); + } + if (md->end_offset_top > offsetcount) md->offset_overflow = TRUE; + DPRINTF(("Freeing temporary memory\n")); + (pcre_free)(md->offset_vector); + } + + /* Set the return code to the number of captured strings, or 0 if there are + too many to fit into the vector. */ + + rc = md->offset_overflow? 0 : md->end_offset_top/2; + + /* If there is space, set up the whole thing as substring 0. The value of + md->start_match_ptr might be modified if \K was encountered on the success + matching path. */ + + if (offsetcount < 2) rc = 0; else + { + offsets[0] = md->start_match_ptr - md->start_subject; + offsets[1] = md->end_match_ptr - md->start_subject; + } + + DPRINTF((">>>> returning %d\n", rc)); + return rc; + } + +/* Control gets here if there has been an error, or if the overall match +attempt has failed at all permitted starting positions. */ + +if (using_temporary_offsets) + { + DPRINTF(("Freeing temporary memory\n")); + (pcre_free)(md->offset_vector); + } + +if (rc != MATCH_NOMATCH) + { + DPRINTF((">>>> error: returning %d\n", rc)); + return rc; + } +else if (md->partial && md->hitend) + { + DPRINTF((">>>> returning PCRE_ERROR_PARTIAL\n")); + return PCRE_ERROR_PARTIAL; + } +else + { + DPRINTF((">>>> returning PCRE_ERROR_NOMATCH\n")); + return PCRE_ERROR_NOMATCH; + } +} + +/* End of pcre_exec.c */ diff --git a/Engine/lib/pcre/pcre_fullinfo.c b/Engine/lib/pcre/pcre_fullinfo.c new file mode 100644 index 000000000..7b001c60b --- /dev/null +++ b/Engine/lib/pcre/pcre_fullinfo.c @@ -0,0 +1,165 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2008 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains the external function pcre_fullinfo(), which returns +information about a compiled pattern. */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + + +/************************************************* +* Return info about compiled pattern * +*************************************************/ + +/* This is a newer "info" function which has an extensible interface so +that additional items can be added compatibly. + +Arguments: + argument_re points to compiled code + extra_data points extra data, or NULL + what what information is required + where where to put the information + +Returns: 0 if data returned, negative on error +*/ + +PCRE_EXP_DEFN int +pcre_fullinfo(const pcre *argument_re, const pcre_extra *extra_data, int what, + void *where) +{ +real_pcre internal_re; +pcre_study_data internal_study; +const real_pcre *re = (const real_pcre *)argument_re; +const pcre_study_data *study = NULL; + +if (re == NULL || where == NULL) return PCRE_ERROR_NULL; + +if (extra_data != NULL && (extra_data->flags & PCRE_EXTRA_STUDY_DATA) != 0) + study = (const pcre_study_data *)extra_data->study_data; + +if (re->magic_number != MAGIC_NUMBER) + { + re = _pcre_try_flipped(re, &internal_re, study, &internal_study); + if (re == NULL) return PCRE_ERROR_BADMAGIC; + if (study != NULL) study = &internal_study; + } + +switch (what) + { + case PCRE_INFO_OPTIONS: + *((unsigned long int *)where) = re->options & PUBLIC_OPTIONS; + break; + + case PCRE_INFO_SIZE: + *((size_t *)where) = re->size; + break; + + case PCRE_INFO_STUDYSIZE: + *((size_t *)where) = (study == NULL)? 0 : study->size; + break; + + case PCRE_INFO_CAPTURECOUNT: + *((int *)where) = re->top_bracket; + break; + + case PCRE_INFO_BACKREFMAX: + *((int *)where) = re->top_backref; + break; + + case PCRE_INFO_FIRSTBYTE: + *((int *)where) = + ((re->flags & PCRE_FIRSTSET) != 0)? re->first_byte : + ((re->flags & PCRE_STARTLINE) != 0)? -1 : -2; + break; + + /* Make sure we pass back the pointer to the bit vector in the external + block, not the internal copy (with flipped integer fields). */ + + case PCRE_INFO_FIRSTTABLE: + *((const uschar **)where) = + (study != NULL && (study->options & PCRE_STUDY_MAPPED) != 0)? + ((const pcre_study_data *)extra_data->study_data)->start_bits : NULL; + break; + + case PCRE_INFO_LASTLITERAL: + *((int *)where) = + ((re->flags & PCRE_REQCHSET) != 0)? re->req_byte : -1; + break; + + case PCRE_INFO_NAMEENTRYSIZE: + *((int *)where) = re->name_entry_size; + break; + + case PCRE_INFO_NAMECOUNT: + *((int *)where) = re->name_count; + break; + + case PCRE_INFO_NAMETABLE: + *((const uschar **)where) = (const uschar *)re + re->name_table_offset; + break; + + case PCRE_INFO_DEFAULT_TABLES: + *((const uschar **)where) = (const uschar *)(_pcre_default_tables); + break; + + case PCRE_INFO_OKPARTIAL: + *((int *)where) = (re->flags & PCRE_NOPARTIAL) == 0; + break; + + case PCRE_INFO_JCHANGED: + *((int *)where) = (re->flags & PCRE_JCHANGED) != 0; + break; + + case PCRE_INFO_HASCRORLF: + *((int *)where) = (re->flags & PCRE_HASCRORLF) != 0; + break; + + default: return PCRE_ERROR_BADOPTION; + } + +return 0; +} + +/* End of pcre_fullinfo.c */ diff --git a/Engine/lib/pcre/pcre_get.c b/Engine/lib/pcre/pcre_get.c new file mode 100644 index 000000000..68b8de47b --- /dev/null +++ b/Engine/lib/pcre/pcre_get.c @@ -0,0 +1,465 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2008 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains some convenience functions for extracting substrings +from the subject string after a regex match has succeeded. The original idea +for these functions came from Scott Wimer. */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + + +/************************************************* +* Find number for named string * +*************************************************/ + +/* This function is used by the get_first_set() function below, as well +as being generally available. It assumes that names are unique. + +Arguments: + code the compiled regex + stringname the name whose number is required + +Returns: the number of the named parentheses, or a negative number + (PCRE_ERROR_NOSUBSTRING) if not found +*/ + +int +pcre_get_stringnumber(const pcre *code, const char *stringname) +{ +int rc; +int entrysize; +int top, bot; +uschar *nametable; + +if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0) + return rc; +if (top <= 0) return PCRE_ERROR_NOSUBSTRING; + +if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize)) != 0) + return rc; +if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, &nametable)) != 0) + return rc; + +bot = 0; +while (top > bot) + { + int mid = (top + bot) / 2; + uschar *entry = nametable + entrysize*mid; + int c = strcmp(stringname, (char *)(entry + 2)); + if (c == 0) return (entry[0] << 8) + entry[1]; + if (c > 0) bot = mid + 1; else top = mid; + } + +return PCRE_ERROR_NOSUBSTRING; +} + + + +/************************************************* +* Find (multiple) entries for named string * +*************************************************/ + +/* This is used by the get_first_set() function below, as well as being +generally available. It is used when duplicated names are permitted. + +Arguments: + code the compiled regex + stringname the name whose entries required + firstptr where to put the pointer to the first entry + lastptr where to put the pointer to the last entry + +Returns: the length of each entry, or a negative number + (PCRE_ERROR_NOSUBSTRING) if not found +*/ + +int +pcre_get_stringtable_entries(const pcre *code, const char *stringname, + char **firstptr, char **lastptr) +{ +int rc; +int entrysize; +int top, bot; +uschar *nametable, *lastentry; + +if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMECOUNT, &top)) != 0) + return rc; +if (top <= 0) return PCRE_ERROR_NOSUBSTRING; + +if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMEENTRYSIZE, &entrysize)) != 0) + return rc; +if ((rc = pcre_fullinfo(code, NULL, PCRE_INFO_NAMETABLE, &nametable)) != 0) + return rc; + +lastentry = nametable + entrysize * (top - 1); +bot = 0; +while (top > bot) + { + int mid = (top + bot) / 2; + uschar *entry = nametable + entrysize*mid; + int c = strcmp(stringname, (char *)(entry + 2)); + if (c == 0) + { + uschar *first = entry; + uschar *last = entry; + while (first > nametable) + { + if (strcmp(stringname, (char *)(first - entrysize + 2)) != 0) break; + first -= entrysize; + } + while (last < lastentry) + { + if (strcmp(stringname, (char *)(last + entrysize + 2)) != 0) break; + last += entrysize; + } + *firstptr = (char *)first; + *lastptr = (char *)last; + return entrysize; + } + if (c > 0) bot = mid + 1; else top = mid; + } + +return PCRE_ERROR_NOSUBSTRING; +} + + + +/************************************************* +* Find first set of multiple named strings * +*************************************************/ + +/* This function allows for duplicate names in the table of named substrings. +It returns the number of the first one that was set in a pattern match. + +Arguments: + code the compiled regex + stringname the name of the capturing substring + ovector the vector of matched substrings + +Returns: the number of the first that is set, + or the number of the last one if none are set, + or a negative number on error +*/ + +static int +get_first_set(const pcre *code, const char *stringname, int *ovector) +{ +const real_pcre *re = (const real_pcre *)code; +int entrysize; +char *first, *last; +uschar *entry; +if ((re->options & PCRE_DUPNAMES) == 0 && (re->flags & PCRE_JCHANGED) == 0) + return pcre_get_stringnumber(code, stringname); +entrysize = pcre_get_stringtable_entries(code, stringname, &first, &last); +if (entrysize <= 0) return entrysize; +for (entry = (uschar *)first; entry <= (uschar *)last; entry += entrysize) + { + int n = (entry[0] << 8) + entry[1]; + if (ovector[n*2] >= 0) return n; + } +return (first[0] << 8) + first[1]; +} + + + + +/************************************************* +* Copy captured string to given buffer * +*************************************************/ + +/* This function copies a single captured substring into a given buffer. +Note that we use memcpy() rather than strncpy() in case there are binary zeros +in the string. + +Arguments: + subject the subject string that was matched + ovector pointer to the offsets table + stringcount the number of substrings that were captured + (i.e. the yield of the pcre_exec call, unless + that was zero, in which case it should be 1/3 + of the offset table size) + stringnumber the number of the required substring + buffer where to put the substring + size the size of the buffer + +Returns: if successful: + the length of the copied string, not including the zero + that is put on the end; can be zero + if not successful: + PCRE_ERROR_NOMEMORY (-6) buffer too small + PCRE_ERROR_NOSUBSTRING (-7) no such captured substring +*/ + +int +pcre_copy_substring(const char *subject, int *ovector, int stringcount, + int stringnumber, char *buffer, int size) +{ +int yield; +if (stringnumber < 0 || stringnumber >= stringcount) + return PCRE_ERROR_NOSUBSTRING; +stringnumber *= 2; +yield = ovector[stringnumber+1] - ovector[stringnumber]; +if (size < yield + 1) return PCRE_ERROR_NOMEMORY; +memcpy(buffer, subject + ovector[stringnumber], yield); +buffer[yield] = 0; +return yield; +} + + + +/************************************************* +* Copy named captured string to given buffer * +*************************************************/ + +/* This function copies a single captured substring into a given buffer, +identifying it by name. If the regex permits duplicate names, the first +substring that is set is chosen. + +Arguments: + code the compiled regex + subject the subject string that was matched + ovector pointer to the offsets table + stringcount the number of substrings that were captured + (i.e. the yield of the pcre_exec call, unless + that was zero, in which case it should be 1/3 + of the offset table size) + stringname the name of the required substring + buffer where to put the substring + size the size of the buffer + +Returns: if successful: + the length of the copied string, not including the zero + that is put on the end; can be zero + if not successful: + PCRE_ERROR_NOMEMORY (-6) buffer too small + PCRE_ERROR_NOSUBSTRING (-7) no such captured substring +*/ + +int +pcre_copy_named_substring(const pcre *code, const char *subject, int *ovector, + int stringcount, const char *stringname, char *buffer, int size) +{ +int n = get_first_set(code, stringname, ovector); +if (n <= 0) return n; +return pcre_copy_substring(subject, ovector, stringcount, n, buffer, size); +} + + + +/************************************************* +* Copy all captured strings to new store * +*************************************************/ + +/* This function gets one chunk of store and builds a list of pointers and all +of the captured substrings in it. A NULL pointer is put on the end of the list. + +Arguments: + subject the subject string that was matched + ovector pointer to the offsets table + stringcount the number of substrings that were captured + (i.e. the yield of the pcre_exec call, unless + that was zero, in which case it should be 1/3 + of the offset table size) + listptr set to point to the list of pointers + +Returns: if successful: 0 + if not successful: + PCRE_ERROR_NOMEMORY (-6) failed to get store +*/ + +int +pcre_get_substring_list(const char *subject, int *ovector, int stringcount, + const char ***listptr) +{ +int i; +int size = sizeof(char *); +int double_count = stringcount * 2; +char **stringlist; +char *p; + +for (i = 0; i < double_count; i += 2) + size += sizeof(char *) + ovector[i+1] - ovector[i] + 1; + +stringlist = (char **)(pcre_malloc)(size); +if (stringlist == NULL) return PCRE_ERROR_NOMEMORY; + +*listptr = (const char **)stringlist; +p = (char *)(stringlist + stringcount + 1); + +for (i = 0; i < double_count; i += 2) + { + int len = ovector[i+1] - ovector[i]; + memcpy(p, subject + ovector[i], len); + *stringlist++ = p; + p += len; + *p++ = 0; + } + +*stringlist = NULL; +return 0; +} + + + +/************************************************* +* Free store obtained by get_substring_list * +*************************************************/ + +/* This function exists for the benefit of people calling PCRE from non-C +programs that can call its functions, but not free() or (pcre_free)() directly. + +Argument: the result of a previous pcre_get_substring_list() +Returns: nothing +*/ + +void +pcre_free_substring_list(const char **pointer) +{ +(pcre_free)((void *)pointer); +} + + + +/************************************************* +* Copy captured string to new store * +*************************************************/ + +/* This function copies a single captured substring into a piece of new +store + +Arguments: + subject the subject string that was matched + ovector pointer to the offsets table + stringcount the number of substrings that were captured + (i.e. the yield of the pcre_exec call, unless + that was zero, in which case it should be 1/3 + of the offset table size) + stringnumber the number of the required substring + stringptr where to put a pointer to the substring + +Returns: if successful: + the length of the string, not including the zero that + is put on the end; can be zero + if not successful: + PCRE_ERROR_NOMEMORY (-6) failed to get store + PCRE_ERROR_NOSUBSTRING (-7) substring not present +*/ + +int +pcre_get_substring(const char *subject, int *ovector, int stringcount, + int stringnumber, const char **stringptr) +{ +int yield; +char *substring; +if (stringnumber < 0 || stringnumber >= stringcount) + return PCRE_ERROR_NOSUBSTRING; +stringnumber *= 2; +yield = ovector[stringnumber+1] - ovector[stringnumber]; +substring = (char *)(pcre_malloc)(yield + 1); +if (substring == NULL) return PCRE_ERROR_NOMEMORY; +memcpy(substring, subject + ovector[stringnumber], yield); +substring[yield] = 0; +*stringptr = substring; +return yield; +} + + + +/************************************************* +* Copy named captured string to new store * +*************************************************/ + +/* This function copies a single captured substring, identified by name, into +new store. If the regex permits duplicate names, the first substring that is +set is chosen. + +Arguments: + code the compiled regex + subject the subject string that was matched + ovector pointer to the offsets table + stringcount the number of substrings that were captured + (i.e. the yield of the pcre_exec call, unless + that was zero, in which case it should be 1/3 + of the offset table size) + stringname the name of the required substring + stringptr where to put the pointer + +Returns: if successful: + the length of the copied string, not including the zero + that is put on the end; can be zero + if not successful: + PCRE_ERROR_NOMEMORY (-6) couldn't get memory + PCRE_ERROR_NOSUBSTRING (-7) no such captured substring +*/ + +int +pcre_get_named_substring(const pcre *code, const char *subject, int *ovector, + int stringcount, const char *stringname, const char **stringptr) +{ +int n = get_first_set(code, stringname, ovector); +if (n <= 0) return n; +return pcre_get_substring(subject, ovector, stringcount, n, stringptr); +} + + + + +/************************************************* +* Free store obtained by get_substring * +*************************************************/ + +/* This function exists for the benefit of people calling PCRE from non-C +programs that can call its functions, but not free() or (pcre_free)() directly. + +Argument: the result of a previous pcre_get_substring() +Returns: nothing +*/ + +void +pcre_free_substring(const char *pointer) +{ +(pcre_free)((void *)pointer); +} + +/* End of pcre_get.c */ diff --git a/Engine/lib/pcre/pcre_globals.c b/Engine/lib/pcre/pcre_globals.c new file mode 100644 index 000000000..24ed03d39 --- /dev/null +++ b/Engine/lib/pcre/pcre_globals.c @@ -0,0 +1,63 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2008 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains global variables that are exported by the PCRE library. +PCRE is thread-clean and doesn't use any global variables in the normal sense. +However, it calls memory allocation and freeing functions via the four +indirections below, and it can optionally do callouts, using the fifth +indirection. These values can be changed by the caller, but are shared between +all threads. However, when compiling for Virtual Pascal, things are done +differently, and global variables are not used (see pcre.in). */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + +#ifndef VPCOMPAT +PCRE_EXP_DATA_DEFN void *(*pcre_malloc)(size_t) = malloc; +PCRE_EXP_DATA_DEFN void (*pcre_free)(void *) = free; +PCRE_EXP_DATA_DEFN void *(*pcre_stack_malloc)(size_t) = malloc; +PCRE_EXP_DATA_DEFN void (*pcre_stack_free)(void *) = free; +PCRE_EXP_DATA_DEFN int (*pcre_callout)(pcre_callout_block *) = NULL; +#endif + +/* End of pcre_globals.c */ diff --git a/Engine/lib/pcre/pcre_info.c b/Engine/lib/pcre/pcre_info.c new file mode 100644 index 000000000..638a47531 --- /dev/null +++ b/Engine/lib/pcre/pcre_info.c @@ -0,0 +1,93 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2008 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains the external function pcre_info(), which gives some +information about a compiled pattern. However, use of this function is now +deprecated, as it has been superseded by pcre_fullinfo(). */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + + +/************************************************* +* (Obsolete) Return info about compiled pattern * +*************************************************/ + +/* This is the original "info" function. It picks potentially useful data out +of the private structure, but its interface was too rigid. It remains for +backwards compatibility. The public options are passed back in an int - though +the re->options field has been expanded to a long int, all the public options +at the low end of it, and so even on 16-bit systems this will still be OK. +Therefore, I haven't changed the API for pcre_info(). + +Arguments: + argument_re points to compiled code + optptr where to pass back the options + first_byte where to pass back the first character, + or -1 if multiline and all branches start ^, + or -2 otherwise + +Returns: number of capturing subpatterns + or negative values on error +*/ + +PCRE_EXP_DEFN int +pcre_info(const pcre *argument_re, int *optptr, int *first_byte) +{ +real_pcre internal_re; +const real_pcre *re = (const real_pcre *)argument_re; +if (re == NULL) return PCRE_ERROR_NULL; +if (re->magic_number != MAGIC_NUMBER) + { + re = _pcre_try_flipped(re, &internal_re, NULL, NULL); + if (re == NULL) return PCRE_ERROR_BADMAGIC; + } +if (optptr != NULL) *optptr = (int)(re->options & PUBLIC_OPTIONS); +if (first_byte != NULL) + *first_byte = ((re->flags & PCRE_FIRSTSET) != 0)? re->first_byte : + ((re->flags & PCRE_STARTLINE) != 0)? -1 : -2; +return re->top_bracket; +} + +/* End of pcre_info.c */ diff --git a/Engine/lib/pcre/pcre_internal.h b/Engine/lib/pcre/pcre_internal.h new file mode 100644 index 000000000..caf7b831d --- /dev/null +++ b/Engine/lib/pcre/pcre_internal.h @@ -0,0 +1,1126 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2008 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + +/* This header contains definitions that are shared between the different +modules, but which are not relevant to the exported API. This includes some +functions whose names all begin with "_pcre_". */ + +#ifndef PCRE_INTERNAL_H +#define PCRE_INTERNAL_H + +/* Define DEBUG to get debugging output on stdout. */ + +#if 0 +#define DEBUG +#endif + +/* Use a macro for debugging printing, 'cause that eliminates the use of #ifdef +inline, and there are *still* stupid compilers about that don't like indented +pre-processor statements, or at least there were when I first wrote this. After +all, it had only been about 10 years then... + +It turns out that the Mac Debugging.h header also defines the macro DPRINTF, so +be absolutely sure we get our version. */ + +#undef DPRINTF +#ifdef DEBUG +#define DPRINTF(p) printf p +#else +#define DPRINTF(p) /* Nothing */ +#endif + + +/* Standard C headers plus the external interface definition. The only time +setjmp and stdarg are used is when NO_RECURSE is set. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* When compiling a DLL for Windows, the exported symbols have to be declared +using some MS magic. I found some useful information on this web page: +http://msdn2.microsoft.com/en-us/library/y4h7bcy6(VS.80).aspx. According to the +information there, using __declspec(dllexport) without "extern" we have a +definition; with "extern" we have a declaration. The settings here override the +setting in pcre.h (which is included below); it defines only PCRE_EXP_DECL, +which is all that is needed for applications (they just import the symbols). We +use: + + PCRE_EXP_DECL for declarations + PCRE_EXP_DEFN for definitions of exported functions + PCRE_EXP_DATA_DEFN for definitions of exported variables + +The reason for the two DEFN macros is that in non-Windows environments, one +does not want to have "extern" before variable definitions because it leads to +compiler warnings. So we distinguish between functions and variables. In +Windows, the two should always be the same. + +The reason for wrapping this in #ifndef PCRE_EXP_DECL is so that pcretest, +which is an application, but needs to import this file in order to "peek" at +internals, can #include pcre.h first to get an application's-eye view. + +In principle, people compiling for non-Windows, non-Unix-like (i.e. uncommon, +special-purpose environments) might want to stick other stuff in front of +exported symbols. That's why, in the non-Windows case, we set PCRE_EXP_DEFN and +PCRE_EXP_DATA_DEFN only if they are not already set. */ + +#ifndef PCRE_EXP_DECL +# ifdef _WIN32 +# ifndef PCRE_STATIC +# define PCRE_EXP_DECL extern __declspec(dllexport) +# define PCRE_EXP_DEFN __declspec(dllexport) +# define PCRE_EXP_DATA_DEFN __declspec(dllexport) +# else +# define PCRE_EXP_DECL extern +# define PCRE_EXP_DEFN +# define PCRE_EXP_DATA_DEFN +# endif +# else +# ifdef __cplusplus +# define PCRE_EXP_DECL extern "C" +# else +# define PCRE_EXP_DECL extern +# endif +# ifndef PCRE_EXP_DEFN +# define PCRE_EXP_DEFN PCRE_EXP_DECL +# endif +# ifndef PCRE_EXP_DATA_DEFN +# define PCRE_EXP_DATA_DEFN +# endif +# endif +#endif + +/* We need to have types that specify unsigned 16-bit and 32-bit integers. We +cannot determine these outside the compilation (e.g. by running a program as +part of "configure") because PCRE is often cross-compiled for use on other +systems. Instead we make use of the maximum sizes that are available at +preprocessor time in standard C environments. */ + +#if USHRT_MAX == 65535 + typedef unsigned short pcre_uint16; +#elif UINT_MAX == 65535 + typedef unsigned int pcre_uint16; +#else + #error Cannot determine a type for 16-bit unsigned integers +#endif + +#if UINT_MAX == 4294967295 + typedef unsigned int pcre_uint32; +#elif ULONG_MAX == 4294967295 + typedef unsigned long int pcre_uint32; +#else + #error Cannot determine a type for 32-bit unsigned integers +#endif + +/* All character handling must be done as unsigned characters. Otherwise there +are problems with top-bit-set characters and functions such as isspace(). +However, we leave the interface to the outside world as char *, because that +should make things easier for callers. We define a short type for unsigned char +to save lots of typing. I tried "uchar", but it causes problems on Digital +Unix, where it is defined in sys/types, so use "uschar" instead. */ + +typedef unsigned char uschar; + +/* This is an unsigned int value that no character can ever have. UTF-8 +characters only go up to 0x7fffffff (though Unicode doesn't go beyond +0x0010ffff). */ + +#define NOTACHAR 0xffffffff + +/* PCRE is able to support several different kinds of newline (CR, LF, CRLF, +"any" and "anycrlf" at present). The following macros are used to package up +testing for newlines. NLBLOCK, PSSTART, and PSEND are defined in the various +modules to indicate in which datablock the parameters exist, and what the +start/end of string field names are. */ + +#define NLTYPE_FIXED 0 /* Newline is a fixed length string */ +#define NLTYPE_ANY 1 /* Newline is any Unicode line ending */ +#define NLTYPE_ANYCRLF 2 /* Newline is CR, LF, or CRLF */ + +/* This macro checks for a newline at the given position */ + +#define IS_NEWLINE(p) \ + ((NLBLOCK->nltype != NLTYPE_FIXED)? \ + ((p) < NLBLOCK->PSEND && \ + _pcre_is_newline((p), NLBLOCK->nltype, NLBLOCK->PSEND, &(NLBLOCK->nllen),\ + utf8)) \ + : \ + ((p) <= NLBLOCK->PSEND - NLBLOCK->nllen && \ + (p)[0] == NLBLOCK->nl[0] && \ + (NLBLOCK->nllen == 1 || (p)[1] == NLBLOCK->nl[1]) \ + ) \ + ) + +/* This macro checks for a newline immediately preceding the given position */ + +#define WAS_NEWLINE(p) \ + ((NLBLOCK->nltype != NLTYPE_FIXED)? \ + ((p) > NLBLOCK->PSSTART && \ + _pcre_was_newline((p), NLBLOCK->nltype, NLBLOCK->PSSTART, \ + &(NLBLOCK->nllen), utf8)) \ + : \ + ((p) >= NLBLOCK->PSSTART + NLBLOCK->nllen && \ + (p)[-NLBLOCK->nllen] == NLBLOCK->nl[0] && \ + (NLBLOCK->nllen == 1 || (p)[-NLBLOCK->nllen+1] == NLBLOCK->nl[1]) \ + ) \ + ) + +/* When PCRE is compiled as a C++ library, the subject pointer can be replaced +with a custom type. This makes it possible, for example, to allow pcre_exec() +to process subject strings that are discontinuous by using a smart pointer +class. It must always be possible to inspect all of the subject string in +pcre_exec() because of the way it backtracks. Two macros are required in the +normal case, for sign-unspecified and unsigned char pointers. The former is +used for the external interface and appears in pcre.h, which is why its name +must begin with PCRE_. */ + +#ifdef CUSTOM_SUBJECT_PTR +#define PCRE_SPTR CUSTOM_SUBJECT_PTR +#define USPTR CUSTOM_SUBJECT_PTR +#else +#define PCRE_SPTR const char * +#define USPTR const unsigned char * +#endif + + + +/* Include the public PCRE header and the definitions of UCP character property +values. */ + +#include "pcre.h" +#include "ucp.h" + +/* When compiling for use with the Virtual Pascal compiler, these functions +need to have their names changed. PCRE must be compiled with the -DVPCOMPAT +option on the command line. */ + +#ifdef VPCOMPAT +#define strlen(s) _strlen(s) +#define strncmp(s1,s2,m) _strncmp(s1,s2,m) +#define memcmp(s,c,n) _memcmp(s,c,n) +#define memcpy(d,s,n) _memcpy(d,s,n) +#define memmove(d,s,n) _memmove(d,s,n) +#define memset(s,c,n) _memset(s,c,n) +#else /* VPCOMPAT */ + +/* To cope with SunOS4 and other systems that lack memmove() but have bcopy(), +define a macro for memmove() if HAVE_MEMMOVE is false, provided that HAVE_BCOPY +is set. Otherwise, include an emulating function for those systems that have +neither (there some non-Unix environments where this is the case). */ + +#ifndef HAVE_MEMMOVE +#undef memmove /* some systems may have a macro */ +#ifdef HAVE_BCOPY +#define memmove(a, b, c) bcopy(b, a, c) +#else /* HAVE_BCOPY */ +static void * +pcre_memmove(void *d, const void *s, size_t n) +{ +size_t i; +unsigned char *dest = (unsigned char *)d; +const unsigned char *src = (const unsigned char *)s; +if (dest > src) + { + dest += n; + src += n; + for (i = 0; i < n; ++i) *(--dest) = *(--src); + return (void *)dest; + } +else + { + for (i = 0; i < n; ++i) *dest++ = *src++; + return (void *)(dest - n); + } +} +#define memmove(a, b, c) pcre_memmove(a, b, c) +#endif /* not HAVE_BCOPY */ +#endif /* not HAVE_MEMMOVE */ +#endif /* not VPCOMPAT */ + + +/* PCRE keeps offsets in its compiled code as 2-byte quantities (always stored +in big-endian order) by default. These are used, for example, to link from the +start of a subpattern to its alternatives and its end. The use of 2 bytes per +offset limits the size of the compiled regex to around 64K, which is big enough +for almost everybody. However, I received a request for an even bigger limit. +For this reason, and also to make the code easier to maintain, the storing and +loading of offsets from the byte string is now handled by the macros that are +defined here. + +The macros are controlled by the value of LINK_SIZE. This defaults to 2 in +the config.h file, but can be overridden by using -D on the command line. This +is automated on Unix systems via the "configure" command. */ + +#if LINK_SIZE == 2 + +#define PUT(a,n,d) \ + (a[n] = (d) >> 8), \ + (a[(n)+1] = (d) & 255) + +#define GET(a,n) \ + (((a)[n] << 8) | (a)[(n)+1]) + +#define MAX_PATTERN_SIZE (1 << 16) + + +#elif LINK_SIZE == 3 + +#define PUT(a,n,d) \ + (a[n] = (d) >> 16), \ + (a[(n)+1] = (d) >> 8), \ + (a[(n)+2] = (d) & 255) + +#define GET(a,n) \ + (((a)[n] << 16) | ((a)[(n)+1] << 8) | (a)[(n)+2]) + +#define MAX_PATTERN_SIZE (1 << 24) + + +#elif LINK_SIZE == 4 + +#define PUT(a,n,d) \ + (a[n] = (d) >> 24), \ + (a[(n)+1] = (d) >> 16), \ + (a[(n)+2] = (d) >> 8), \ + (a[(n)+3] = (d) & 255) + +#define GET(a,n) \ + (((a)[n] << 24) | ((a)[(n)+1] << 16) | ((a)[(n)+2] << 8) | (a)[(n)+3]) + +#define MAX_PATTERN_SIZE (1 << 30) /* Keep it positive */ + + +#else +#error LINK_SIZE must be either 2, 3, or 4 +#endif + + +/* Convenience macro defined in terms of the others */ + +#define PUTINC(a,n,d) PUT(a,n,d), a += LINK_SIZE + + +/* PCRE uses some other 2-byte quantities that do not change when the size of +offsets changes. There are used for repeat counts and for other things such as +capturing parenthesis numbers in back references. */ + +#define PUT2(a,n,d) \ + a[n] = (d) >> 8; \ + a[(n)+1] = (d) & 255 + +#define GET2(a,n) \ + (((a)[n] << 8) | (a)[(n)+1]) + +#define PUT2INC(a,n,d) PUT2(a,n,d), a += 2 + + +/* When UTF-8 encoding is being used, a character is no longer just a single +byte. The macros for character handling generate simple sequences when used in +byte-mode, and more complicated ones for UTF-8 characters. BACKCHAR should +never be called in byte mode. To make sure it can never even appear when UTF-8 +support is omitted, we don't even define it. */ + +#ifndef SUPPORT_UTF8 +#define NEXTCHAR(p) p++; +#define GETCHAR(c, eptr) c = *eptr; +#define GETCHARTEST(c, eptr) c = *eptr; +#define GETCHARINC(c, eptr) c = *eptr++; +#define GETCHARINCTEST(c, eptr) c = *eptr++; +#define GETCHARLEN(c, eptr, len) c = *eptr; +/* #define BACKCHAR(eptr) */ + +#else /* SUPPORT_UTF8 */ + +/* Advance a character pointer one byte in non-UTF-8 mode and by one character +in UTF-8 mode. */ + +#define NEXTCHAR(p) \ + p++; \ + if (utf8) { while((*p & 0xc0) == 0x80) p++; } + +/* Get the next UTF-8 character, not advancing the pointer. This is called when +we know we are in UTF-8 mode. */ + +#define GETCHAR(c, eptr) \ + c = *eptr; \ + if (c >= 0xc0) \ + { \ + int gcii; \ + int gcaa = _pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes */ \ + int gcss = 6*gcaa; \ + c = (c & _pcre_utf8_table3[gcaa]) << gcss; \ + for (gcii = 1; gcii <= gcaa; gcii++) \ + { \ + gcss -= 6; \ + c |= (eptr[gcii] & 0x3f) << gcss; \ + } \ + } + +/* Get the next UTF-8 character, testing for UTF-8 mode, and not advancing the +pointer. */ + +#define GETCHARTEST(c, eptr) \ + c = *eptr; \ + if (utf8 && c >= 0xc0) \ + { \ + int gcii; \ + int gcaa = _pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes */ \ + int gcss = 6*gcaa; \ + c = (c & _pcre_utf8_table3[gcaa]) << gcss; \ + for (gcii = 1; gcii <= gcaa; gcii++) \ + { \ + gcss -= 6; \ + c |= (eptr[gcii] & 0x3f) << gcss; \ + } \ + } + +/* Get the next UTF-8 character, advancing the pointer. This is called when we +know we are in UTF-8 mode. */ + +#define GETCHARINC(c, eptr) \ + c = *eptr++; \ + if (c >= 0xc0) \ + { \ + int gcaa = _pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes */ \ + int gcss = 6*gcaa; \ + c = (c & _pcre_utf8_table3[gcaa]) << gcss; \ + while (gcaa-- > 0) \ + { \ + gcss -= 6; \ + c |= (*eptr++ & 0x3f) << gcss; \ + } \ + } + +/* Get the next character, testing for UTF-8 mode, and advancing the pointer */ + +#define GETCHARINCTEST(c, eptr) \ + c = *eptr++; \ + if (utf8 && c >= 0xc0) \ + { \ + int gcaa = _pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes */ \ + int gcss = 6*gcaa; \ + c = (c & _pcre_utf8_table3[gcaa]) << gcss; \ + while (gcaa-- > 0) \ + { \ + gcss -= 6; \ + c |= (*eptr++ & 0x3f) << gcss; \ + } \ + } + +/* Get the next UTF-8 character, not advancing the pointer, incrementing length +if there are extra bytes. This is called when we know we are in UTF-8 mode. */ + +#define GETCHARLEN(c, eptr, len) \ + c = *eptr; \ + if (c >= 0xc0) \ + { \ + int gcii; \ + int gcaa = _pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes */ \ + int gcss = 6*gcaa; \ + c = (c & _pcre_utf8_table3[gcaa]) << gcss; \ + for (gcii = 1; gcii <= gcaa; gcii++) \ + { \ + gcss -= 6; \ + c |= (eptr[gcii] & 0x3f) << gcss; \ + } \ + len += gcaa; \ + } + +/* If the pointer is not at the start of a character, move it back until +it is. This is called only in UTF-8 mode - we don't put a test within the macro +because almost all calls are already within a block of UTF-8 only code. */ + +#define BACKCHAR(eptr) while((*eptr & 0xc0) == 0x80) eptr-- + +#endif + + +/* In case there is no definition of offsetof() provided - though any proper +Standard C system should have one. */ + +#ifndef offsetof +#define offsetof(p_type,field) ((size_t)&(((p_type *)0)->field)) +#endif + + +/* These are the public options that can change during matching. */ + +#define PCRE_IMS (PCRE_CASELESS|PCRE_MULTILINE|PCRE_DOTALL) + +/* Private flags containing information about the compiled regex. They used to +live at the top end of the options word, but that got almost full, so now they +are in a 16-bit flags word. */ + +#define PCRE_NOPARTIAL 0x0001 /* can't use partial with this regex */ +#define PCRE_FIRSTSET 0x0002 /* first_byte is set */ +#define PCRE_REQCHSET 0x0004 /* req_byte is set */ +#define PCRE_STARTLINE 0x0008 /* start after \n for multiline */ +#define PCRE_JCHANGED 0x0010 /* j option used in regex */ +#define PCRE_HASCRORLF 0x0020 /* explicit \r or \n in pattern */ + +/* Options for the "extra" block produced by pcre_study(). */ + +#define PCRE_STUDY_MAPPED 0x01 /* a map of starting chars exists */ + +/* Masks for identifying the public options that are permitted at compile +time, run time, or study time, respectively. */ + +#define PCRE_NEWLINE_BITS (PCRE_NEWLINE_CR|PCRE_NEWLINE_LF|PCRE_NEWLINE_ANY| \ + PCRE_NEWLINE_ANYCRLF) + +#define PUBLIC_OPTIONS \ + (PCRE_CASELESS|PCRE_EXTENDED|PCRE_ANCHORED|PCRE_MULTILINE| \ + PCRE_DOTALL|PCRE_DOLLAR_ENDONLY|PCRE_EXTRA|PCRE_UNGREEDY|PCRE_UTF8| \ + PCRE_NO_AUTO_CAPTURE|PCRE_NO_UTF8_CHECK|PCRE_AUTO_CALLOUT|PCRE_FIRSTLINE| \ + PCRE_DUPNAMES|PCRE_NEWLINE_BITS|PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE) + +#define PUBLIC_EXEC_OPTIONS \ + (PCRE_ANCHORED|PCRE_NOTBOL|PCRE_NOTEOL|PCRE_NOTEMPTY|PCRE_NO_UTF8_CHECK| \ + PCRE_PARTIAL|PCRE_NEWLINE_BITS|PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE) + +#define PUBLIC_DFA_EXEC_OPTIONS \ + (PCRE_ANCHORED|PCRE_NOTBOL|PCRE_NOTEOL|PCRE_NOTEMPTY|PCRE_NO_UTF8_CHECK| \ + PCRE_PARTIAL|PCRE_DFA_SHORTEST|PCRE_DFA_RESTART|PCRE_NEWLINE_BITS| \ + PCRE_BSR_ANYCRLF|PCRE_BSR_UNICODE) + +#define PUBLIC_STUDY_OPTIONS 0 /* None defined */ + +/* Magic number to provide a small check against being handed junk. Also used +to detect whether a pattern was compiled on a host of different endianness. */ + +#define MAGIC_NUMBER 0x50435245UL /* 'PCRE' */ + +/* Negative values for the firstchar and reqchar variables */ + +#define REQ_UNSET (-2) +#define REQ_NONE (-1) + +/* The maximum remaining length of subject we are prepared to search for a +req_byte match. */ + +#define REQ_BYTE_MAX 1000 + +/* Flags added to firstbyte or reqbyte; a "non-literal" item is either a +variable-length repeat, or a anything other than literal characters. */ + +#define REQ_CASELESS 0x0100 /* indicates caselessness */ +#define REQ_VARY 0x0200 /* reqbyte followed non-literal item */ + +/* Miscellaneous definitions */ + +typedef int BOOL; + +#define FALSE 0 +#define TRUE 1 + +/* Escape items that are just an encoding of a particular data value. */ + +#ifndef ESC_e +#define ESC_e 27 +#endif + +#ifndef ESC_f +#define ESC_f '\f' +#endif + +#ifndef ESC_n +#define ESC_n '\n' +#endif + +#ifndef ESC_r +#define ESC_r '\r' +#endif + +/* We can't officially use ESC_t because it is a POSIX reserved identifier +(presumably because of all the others like size_t). */ + +#ifndef ESC_tee +#define ESC_tee '\t' +#endif + +/* Codes for different types of Unicode property */ + +#define PT_ANY 0 /* Any property - matches all chars */ +#define PT_LAMP 1 /* L& - the union of Lu, Ll, Lt */ +#define PT_GC 2 /* General characteristic (e.g. L) */ +#define PT_PC 3 /* Particular characteristic (e.g. Lu) */ +#define PT_SC 4 /* Script (e.g. Han) */ + +/* Flag bits and data types for the extended class (OP_XCLASS) for classes that +contain UTF-8 characters with values greater than 255. */ + +#define XCL_NOT 0x01 /* Flag: this is a negative class */ +#define XCL_MAP 0x02 /* Flag: a 32-byte map is present */ + +#define XCL_END 0 /* Marks end of individual items */ +#define XCL_SINGLE 1 /* Single item (one multibyte char) follows */ +#define XCL_RANGE 2 /* A range (two multibyte chars) follows */ +#define XCL_PROP 3 /* Unicode property (2-byte property code follows) */ +#define XCL_NOTPROP 4 /* Unicode inverted property (ditto) */ + +/* These are escaped items that aren't just an encoding of a particular data +value such as \n. They must have non-zero values, as check_escape() returns +their negation. Also, they must appear in the same order as in the opcode +definitions below, up to ESC_z. There's a dummy for OP_ANY because it +corresponds to "." rather than an escape sequence. The final one must be +ESC_REF as subsequent values are used for backreferences (\1, \2, \3, etc). +There are two tests in the code for an escape greater than ESC_b and less than +ESC_Z to detect the types that may be repeated. These are the types that +consume characters. If any new escapes are put in between that don't consume a +character, that code will have to change. */ + +enum { ESC_A = 1, ESC_G, ESC_K, ESC_B, ESC_b, ESC_D, ESC_d, ESC_S, ESC_s, + ESC_W, ESC_w, ESC_dum1, ESC_C, ESC_P, ESC_p, ESC_R, ESC_H, ESC_h, + ESC_V, ESC_v, ESC_X, ESC_Z, ESC_z, ESC_E, ESC_Q, ESC_k, ESC_REF }; + + +/* Opcode table: Starting from 1 (i.e. after OP_END), the values up to +OP_EOD must correspond in order to the list of escapes immediately above. + +*** NOTE NOTE NOTE *** Whenever this list is updated, the two macro definitions +that follow must also be updated to match. There is also a table called +"coptable" in pcre_dfa_exec.c that must be updated. */ + +enum { + OP_END, /* 0 End of pattern */ + + /* Values corresponding to backslashed metacharacters */ + + OP_SOD, /* 1 Start of data: \A */ + OP_SOM, /* 2 Start of match (subject + offset): \G */ + OP_SET_SOM, /* 3 Set start of match (\K) */ + OP_NOT_WORD_BOUNDARY, /* 4 \B */ + OP_WORD_BOUNDARY, /* 5 \b */ + OP_NOT_DIGIT, /* 6 \D */ + OP_DIGIT, /* 7 \d */ + OP_NOT_WHITESPACE, /* 8 \S */ + OP_WHITESPACE, /* 9 \s */ + OP_NOT_WORDCHAR, /* 10 \W */ + OP_WORDCHAR, /* 11 \w */ + OP_ANY, /* 12 Match any character */ + OP_ANYBYTE, /* 13 Match any byte (\C); different to OP_ANY for UTF-8 */ + OP_NOTPROP, /* 14 \P (not Unicode property) */ + OP_PROP, /* 15 \p (Unicode property) */ + OP_ANYNL, /* 16 \R (any newline sequence) */ + OP_NOT_HSPACE, /* 17 \H (not horizontal whitespace) */ + OP_HSPACE, /* 18 \h (horizontal whitespace) */ + OP_NOT_VSPACE, /* 19 \V (not vertical whitespace) */ + OP_VSPACE, /* 20 \v (vertical whitespace) */ + OP_EXTUNI, /* 21 \X (extended Unicode sequence */ + OP_EODN, /* 22 End of data or \n at end of data: \Z. */ + OP_EOD, /* 23 End of data: \z */ + + OP_OPT, /* 24 Set runtime options */ + OP_CIRC, /* 25 Start of line - varies with multiline switch */ + OP_DOLL, /* 26 End of line - varies with multiline switch */ + OP_CHAR, /* 27 Match one character, casefully */ + OP_CHARNC, /* 28 Match one character, caselessly */ + OP_NOT, /* 29 Match one character, not the following one */ + + OP_STAR, /* 30 The maximizing and minimizing versions of */ + OP_MINSTAR, /* 31 these six opcodes must come in pairs, with */ + OP_PLUS, /* 32 the minimizing one second. */ + OP_MINPLUS, /* 33 This first set applies to single characters.*/ + OP_QUERY, /* 34 */ + OP_MINQUERY, /* 35 */ + + OP_UPTO, /* 36 From 0 to n matches */ + OP_MINUPTO, /* 37 */ + OP_EXACT, /* 38 Exactly n matches */ + + OP_POSSTAR, /* 39 Possessified star */ + OP_POSPLUS, /* 40 Possessified plus */ + OP_POSQUERY, /* 41 Posesssified query */ + OP_POSUPTO, /* 42 Possessified upto */ + + OP_NOTSTAR, /* 43 The maximizing and minimizing versions of */ + OP_NOTMINSTAR, /* 44 these six opcodes must come in pairs, with */ + OP_NOTPLUS, /* 45 the minimizing one second. They must be in */ + OP_NOTMINPLUS, /* 46 exactly the same order as those above. */ + OP_NOTQUERY, /* 47 This set applies to "not" single characters. */ + OP_NOTMINQUERY, /* 48 */ + + OP_NOTUPTO, /* 49 From 0 to n matches */ + OP_NOTMINUPTO, /* 50 */ + OP_NOTEXACT, /* 51 Exactly n matches */ + + OP_NOTPOSSTAR, /* 52 Possessified versions */ + OP_NOTPOSPLUS, /* 53 */ + OP_NOTPOSQUERY, /* 54 */ + OP_NOTPOSUPTO, /* 55 */ + + OP_TYPESTAR, /* 56 The maximizing and minimizing versions of */ + OP_TYPEMINSTAR, /* 57 these six opcodes must come in pairs, with */ + OP_TYPEPLUS, /* 58 the minimizing one second. These codes must */ + OP_TYPEMINPLUS, /* 59 be in exactly the same order as those above. */ + OP_TYPEQUERY, /* 60 This set applies to character types such as \d */ + OP_TYPEMINQUERY, /* 61 */ + + OP_TYPEUPTO, /* 62 From 0 to n matches */ + OP_TYPEMINUPTO, /* 63 */ + OP_TYPEEXACT, /* 64 Exactly n matches */ + + OP_TYPEPOSSTAR, /* 65 Possessified versions */ + OP_TYPEPOSPLUS, /* 66 */ + OP_TYPEPOSQUERY, /* 67 */ + OP_TYPEPOSUPTO, /* 68 */ + + OP_CRSTAR, /* 69 The maximizing and minimizing versions of */ + OP_CRMINSTAR, /* 70 all these opcodes must come in pairs, with */ + OP_CRPLUS, /* 71 the minimizing one second. These codes must */ + OP_CRMINPLUS, /* 72 be in exactly the same order as those above. */ + OP_CRQUERY, /* 73 These are for character classes and back refs */ + OP_CRMINQUERY, /* 74 */ + OP_CRRANGE, /* 75 These are different to the three sets above. */ + OP_CRMINRANGE, /* 76 */ + + OP_CLASS, /* 77 Match a character class, chars < 256 only */ + OP_NCLASS, /* 78 Same, but the bitmap was created from a negative + class - the difference is relevant only when a UTF-8 + character > 255 is encountered. */ + + OP_XCLASS, /* 79 Extended class for handling UTF-8 chars within the + class. This does both positive and negative. */ + + OP_REF, /* 80 Match a back reference */ + OP_RECURSE, /* 81 Match a numbered subpattern (possibly recursive) */ + OP_CALLOUT, /* 82 Call out to external function if provided */ + + OP_ALT, /* 83 Start of alternation */ + OP_KET, /* 84 End of group that doesn't have an unbounded repeat */ + OP_KETRMAX, /* 85 These two must remain together and in this */ + OP_KETRMIN, /* 86 order. They are for groups the repeat for ever. */ + + /* The assertions must come before BRA, CBRA, ONCE, and COND.*/ + + OP_ASSERT, /* 87 Positive lookahead */ + OP_ASSERT_NOT, /* 88 Negative lookahead */ + OP_ASSERTBACK, /* 89 Positive lookbehind */ + OP_ASSERTBACK_NOT, /* 90 Negative lookbehind */ + OP_REVERSE, /* 91 Move pointer back - used in lookbehind assertions */ + + /* ONCE, BRA, CBRA, and COND must come after the assertions, with ONCE first, + as there's a test for >= ONCE for a subpattern that isn't an assertion. */ + + OP_ONCE, /* 92 Atomic group */ + OP_BRA, /* 93 Start of non-capturing bracket */ + OP_CBRA, /* 94 Start of capturing bracket */ + OP_COND, /* 95 Conditional group */ + + /* These three must follow the previous three, in the same order. There's a + check for >= SBRA to distinguish the two sets. */ + + OP_SBRA, /* 96 Start of non-capturing bracket, check empty */ + OP_SCBRA, /* 97 Start of capturing bracket, check empty */ + OP_SCOND, /* 98 Conditional group, check empty */ + + OP_CREF, /* 99 Used to hold a capture number as condition */ + OP_RREF, /* 100 Used to hold a recursion number as condition */ + OP_DEF, /* 101 The DEFINE condition */ + + OP_BRAZERO, /* 102 These two must remain together and in this */ + OP_BRAMINZERO, /* 103 order. */ + + /* These are backtracking control verbs */ + + OP_PRUNE, /* 104 */ + OP_SKIP, /* 105 */ + OP_THEN, /* 106 */ + OP_COMMIT, /* 107 */ + + /* These are forced failure and success verbs */ + + OP_FAIL, /* 108 */ + OP_ACCEPT /* 109 */ +}; + + +/* This macro defines textual names for all the opcodes. These are used only +for debugging. The macro is referenced only in pcre_printint.c. */ + +#define OP_NAME_LIST \ + "End", "\\A", "\\G", "\\K", "\\B", "\\b", "\\D", "\\d", \ + "\\S", "\\s", "\\W", "\\w", "Any", "Anybyte", \ + "notprop", "prop", "\\R", "\\H", "\\h", "\\V", "\\v", \ + "extuni", "\\Z", "\\z", \ + "Opt", "^", "$", "char", "charnc", "not", \ + "*", "*?", "+", "+?", "?", "??", "{", "{", "{", \ + "*+","++", "?+", "{", \ + "*", "*?", "+", "+?", "?", "??", "{", "{", "{", \ + "*+","++", "?+", "{", \ + "*", "*?", "+", "+?", "?", "??", "{", "{", "{", \ + "*+","++", "?+", "{", \ + "*", "*?", "+", "+?", "?", "??", "{", "{", \ + "class", "nclass", "xclass", "Ref", "Recurse", "Callout", \ + "Alt", "Ket", "KetRmax", "KetRmin", "Assert", "Assert not", \ + "AssertB", "AssertB not", "Reverse", \ + "Once", "Bra", "CBra", "Cond", "SBra", "SCBra", "SCond", \ + "Cond ref", "Cond rec", "Cond def", "Brazero", "Braminzero", \ + "*PRUNE", "*SKIP", "*THEN", "*COMMIT", "*FAIL", "*ACCEPT" + + +/* This macro defines the length of fixed length operations in the compiled +regex. The lengths are used when searching for specific things, and also in the +debugging printing of a compiled regex. We use a macro so that it can be +defined close to the definitions of the opcodes themselves. + +As things have been extended, some of these are no longer fixed lenths, but are +minima instead. For example, the length of a single-character repeat may vary +in UTF-8 mode. The code that uses this table must know about such things. */ + +#define OP_LENGTHS \ + 1, /* End */ \ + 1, 1, 1, 1, 1, /* \A, \G, \K, \B, \b */ \ + 1, 1, 1, 1, 1, 1, /* \D, \d, \S, \s, \W, \w */ \ + 1, 1, /* Any, Anybyte */ \ + 3, 3, 1, /* NOTPROP, PROP, EXTUNI */ \ + 1, 1, 1, 1, 1, /* \R, \H, \h, \V, \v */ \ + 1, 1, 2, 1, 1, /* \Z, \z, Opt, ^, $ */ \ + 2, /* Char - the minimum length */ \ + 2, /* Charnc - the minimum length */ \ + 2, /* not */ \ + /* Positive single-char repeats ** These are */ \ + 2, 2, 2, 2, 2, 2, /* *, *?, +, +?, ?, ?? ** minima in */ \ + 4, 4, 4, /* upto, minupto, exact ** UTF-8 mode */ \ + 2, 2, 2, 4, /* *+, ++, ?+, upto+ */ \ + /* Negative single-char repeats - only for chars < 256 */ \ + 2, 2, 2, 2, 2, 2, /* NOT *, *?, +, +?, ?, ?? */ \ + 4, 4, 4, /* NOT upto, minupto, exact */ \ + 2, 2, 2, 4, /* Possessive *, +, ?, upto */ \ + /* Positive type repeats */ \ + 2, 2, 2, 2, 2, 2, /* Type *, *?, +, +?, ?, ?? */ \ + 4, 4, 4, /* Type upto, minupto, exact */ \ + 2, 2, 2, 4, /* Possessive *+, ++, ?+, upto+ */ \ + /* Character class & ref repeats */ \ + 1, 1, 1, 1, 1, 1, /* *, *?, +, +?, ?, ?? */ \ + 5, 5, /* CRRANGE, CRMINRANGE */ \ + 33, /* CLASS */ \ + 33, /* NCLASS */ \ + 0, /* XCLASS - variable length */ \ + 3, /* REF */ \ + 1+LINK_SIZE, /* RECURSE */ \ + 2+2*LINK_SIZE, /* CALLOUT */ \ + 1+LINK_SIZE, /* Alt */ \ + 1+LINK_SIZE, /* Ket */ \ + 1+LINK_SIZE, /* KetRmax */ \ + 1+LINK_SIZE, /* KetRmin */ \ + 1+LINK_SIZE, /* Assert */ \ + 1+LINK_SIZE, /* Assert not */ \ + 1+LINK_SIZE, /* Assert behind */ \ + 1+LINK_SIZE, /* Assert behind not */ \ + 1+LINK_SIZE, /* Reverse */ \ + 1+LINK_SIZE, /* ONCE */ \ + 1+LINK_SIZE, /* BRA */ \ + 3+LINK_SIZE, /* CBRA */ \ + 1+LINK_SIZE, /* COND */ \ + 1+LINK_SIZE, /* SBRA */ \ + 3+LINK_SIZE, /* SCBRA */ \ + 1+LINK_SIZE, /* SCOND */ \ + 3, /* CREF */ \ + 3, /* RREF */ \ + 1, /* DEF */ \ + 1, 1, /* BRAZERO, BRAMINZERO */ \ + 1, 1, 1, 1, /* PRUNE, SKIP, THEN, COMMIT, */ \ + 1, 1 /* FAIL, ACCEPT */ + + +/* A magic value for OP_RREF to indicate the "any recursion" condition. */ + +#define RREF_ANY 0xffff + +/* Error code numbers. They are given names so that they can more easily be +tracked. */ + +enum { ERR0, ERR1, ERR2, ERR3, ERR4, ERR5, ERR6, ERR7, ERR8, ERR9, + ERR10, ERR11, ERR12, ERR13, ERR14, ERR15, ERR16, ERR17, ERR18, ERR19, + ERR20, ERR21, ERR22, ERR23, ERR24, ERR25, ERR26, ERR27, ERR28, ERR29, + ERR30, ERR31, ERR32, ERR33, ERR34, ERR35, ERR36, ERR37, ERR38, ERR39, + ERR40, ERR41, ERR42, ERR43, ERR44, ERR45, ERR46, ERR47, ERR48, ERR49, + ERR50, ERR51, ERR52, ERR53, ERR54, ERR55, ERR56, ERR57, ERR58, ERR59, + ERR60, ERR61, ERR62, ERR63 }; + +/* The real format of the start of the pcre block; the index of names and the +code vector run on as long as necessary after the end. We store an explicit +offset to the name table so that if a regex is compiled on one host, saved, and +then run on another where the size of pointers is different, all might still +be well. For the case of compiled-on-4 and run-on-8, we include an extra +pointer that is always NULL. For future-proofing, a few dummy fields were +originally included - even though you can never get this planning right - but +there is only one left now. + +NOTE NOTE NOTE: +Because people can now save and re-use compiled patterns, any additions to this +structure should be made at the end, and something earlier (e.g. a new +flag in the options or one of the dummy fields) should indicate that the new +fields are present. Currently PCRE always sets the dummy fields to zero. +NOTE NOTE NOTE: +*/ + +typedef struct real_pcre { + pcre_uint32 magic_number; + pcre_uint32 size; /* Total that was malloced */ + pcre_uint32 options; /* Public options */ + pcre_uint16 flags; /* Private flags */ + pcre_uint16 dummy1; /* For future use */ + pcre_uint16 top_bracket; + pcre_uint16 top_backref; + pcre_uint16 first_byte; + pcre_uint16 req_byte; + pcre_uint16 name_table_offset; /* Offset to name table that follows */ + pcre_uint16 name_entry_size; /* Size of any name items */ + pcre_uint16 name_count; /* Number of name items */ + pcre_uint16 ref_count; /* Reference count */ + + const unsigned char *tables; /* Pointer to tables or NULL for std */ + const unsigned char *nullpad; /* NULL padding */ +} real_pcre; + +/* The format of the block used to store data from pcre_study(). The same +remark (see NOTE above) about extending this structure applies. */ + +typedef struct pcre_study_data { + pcre_uint32 size; /* Total that was malloced */ + pcre_uint32 options; + uschar start_bits[32]; +} pcre_study_data; + +/* Structure for passing "static" information around between the functions +doing the compiling, so that they are thread-safe. */ + +typedef struct compile_data { + const uschar *lcc; /* Points to lower casing table */ + const uschar *fcc; /* Points to case-flipping table */ + const uschar *cbits; /* Points to character type table */ + const uschar *ctypes; /* Points to table of type maps */ + const uschar *start_workspace;/* The start of working space */ + const uschar *start_code; /* The start of the compiled code */ + const uschar *start_pattern; /* The start of the pattern */ + const uschar *end_pattern; /* The end of the pattern */ + uschar *hwm; /* High watermark of workspace */ + uschar *name_table; /* The name/number table */ + int names_found; /* Number of entries so far */ + int name_entry_size; /* Size of each entry */ + int bracount; /* Count of capturing parens as we compile */ + int final_bracount; /* Saved value after first pass */ + int top_backref; /* Maximum back reference */ + unsigned int backref_map; /* Bitmap of low back refs */ + int external_options; /* External (initial) options */ + int external_flags; /* External flag bits to be set */ + int req_varyopt; /* "After variable item" flag for reqbyte */ + BOOL had_accept; /* (*ACCEPT) encountered */ + int nltype; /* Newline type */ + int nllen; /* Newline string length */ + uschar nl[4]; /* Newline string when fixed length */ +} compile_data; + +/* Structure for maintaining a chain of pointers to the currently incomplete +branches, for testing for left recursion. */ + +typedef struct branch_chain { + struct branch_chain *outer; + uschar *current; +} branch_chain; + +/* Structure for items in a linked list that represents an explicit recursive +call within the pattern. */ + +typedef struct recursion_info { + struct recursion_info *prevrec; /* Previous recursion record (or NULL) */ + int group_num; /* Number of group that was called */ + const uschar *after_call; /* "Return value": points after the call in the expr */ + USPTR save_start; /* Old value of mstart */ + int *offset_save; /* Pointer to start of saved offsets */ + int saved_max; /* Number of saved offsets */ +} recursion_info; + +/* Structure for building a chain of data for holding the values of the subject +pointer at the start of each subpattern, so as to detect when an empty string +has been matched by a subpattern - to break infinite loops. */ + +typedef struct eptrblock { + struct eptrblock *epb_prev; + USPTR epb_saved_eptr; +} eptrblock; + + +/* Structure for passing "static" information around between the functions +doing traditional NFA matching, so that they are thread-safe. */ + +typedef struct match_data { + unsigned long int match_call_count; /* As it says */ + unsigned long int match_limit; /* As it says */ + unsigned long int match_limit_recursion; /* As it says */ + int *offset_vector; /* Offset vector */ + int offset_end; /* One past the end */ + int offset_max; /* The maximum usable for return data */ + int nltype; /* Newline type */ + int nllen; /* Newline string length */ + uschar nl[4]; /* Newline string when fixed */ + const uschar *lcc; /* Points to lower casing table */ + const uschar *ctypes; /* Points to table of type maps */ + BOOL offset_overflow; /* Set if too many extractions */ + BOOL notbol; /* NOTBOL flag */ + BOOL noteol; /* NOTEOL flag */ + BOOL utf8; /* UTF8 flag */ + BOOL endonly; /* Dollar not before final \n */ + BOOL notempty; /* Empty string match not wanted */ + BOOL partial; /* PARTIAL flag */ + BOOL hitend; /* Hit the end of the subject at some point */ + BOOL bsr_anycrlf; /* \R is just any CRLF, not full Unicode */ + const uschar *start_code; /* For use when recursing */ + USPTR start_subject; /* Start of the subject string */ + USPTR end_subject; /* End of the subject string */ + USPTR start_match_ptr; /* Start of matched string */ + USPTR end_match_ptr; /* Subject position at end match */ + int end_offset_top; /* Highwater mark at end of match */ + int capture_last; /* Most recent capture number */ + int start_offset; /* The start offset value */ + eptrblock *eptrchain; /* Chain of eptrblocks for tail recursions */ + int eptrn; /* Next free eptrblock */ + recursion_info *recursive; /* Linked list of recursion data */ + void *callout_data; /* To pass back to callouts */ +} match_data; + +/* A similar structure is used for the same purpose by the DFA matching +functions. */ + +typedef struct dfa_match_data { + const uschar *start_code; /* Start of the compiled pattern */ + const uschar *start_subject; /* Start of the subject string */ + const uschar *end_subject; /* End of subject string */ + const uschar *tables; /* Character tables */ + int moptions; /* Match options */ + int poptions; /* Pattern options */ + int nltype; /* Newline type */ + int nllen; /* Newline string length */ + uschar nl[4]; /* Newline string when fixed */ + void *callout_data; /* To pass back to callouts */ +} dfa_match_data; + +/* Bit definitions for entries in the pcre_ctypes table. */ + +#define ctype_space 0x01 +#define ctype_letter 0x02 +#define ctype_digit 0x04 +#define ctype_xdigit 0x08 +#define ctype_word 0x10 /* alphanumeric or '_' */ +#define ctype_meta 0x80 /* regexp meta char or zero (end pattern) */ + +/* Offsets for the bitmap tables in pcre_cbits. Each table contains a set +of bits for a class map. Some classes are built by combining these tables. */ + +#define cbit_space 0 /* [:space:] or \s */ +#define cbit_xdigit 32 /* [:xdigit:] */ +#define cbit_digit 64 /* [:digit:] or \d */ +#define cbit_upper 96 /* [:upper:] */ +#define cbit_lower 128 /* [:lower:] */ +#define cbit_word 160 /* [:word:] or \w */ +#define cbit_graph 192 /* [:graph:] */ +#define cbit_print 224 /* [:print:] */ +#define cbit_punct 256 /* [:punct:] */ +#define cbit_cntrl 288 /* [:cntrl:] */ +#define cbit_length 320 /* Length of the cbits table */ + +/* Offsets of the various tables from the base tables pointer, and +total length. */ + +#define lcc_offset 0 +#define fcc_offset 256 +#define cbits_offset 512 +#define ctypes_offset (cbits_offset + cbit_length) +#define tables_length (ctypes_offset + 256) + +/* Layout of the UCP type table that translates property names into types and +codes. Each entry used to point directly to a name, but to reduce the number of +relocations in shared libraries, it now has an offset into a single string +instead. */ + +typedef struct { + pcre_uint16 name_offset; + pcre_uint16 type; + pcre_uint16 value; +} ucp_type_table; + + +/* Internal shared data tables. These are tables that are used by more than one +of the exported public functions. They have to be "external" in the C sense, +but are not part of the PCRE public API. The data for these tables is in the +pcre_tables.c module. */ + +extern const int _pcre_utf8_table1[]; +extern const int _pcre_utf8_table2[]; +extern const int _pcre_utf8_table3[]; +extern const uschar _pcre_utf8_table4[]; + +extern const int _pcre_utf8_table1_size; + +extern const char _pcre_utt_names[]; +extern const ucp_type_table _pcre_utt[]; +extern const int _pcre_utt_size; + +extern const uschar _pcre_default_tables[]; + +extern const uschar _pcre_OP_lengths[]; + + +/* Internal shared functions. These are functions that are used by more than +one of the exported public functions. They have to be "external" in the C +sense, but are not part of the PCRE public API. */ + +extern BOOL _pcre_is_newline(const uschar *, int, const uschar *, + int *, BOOL); +extern int _pcre_ord2utf8(int, uschar *); +extern real_pcre *_pcre_try_flipped(const real_pcre *, real_pcre *, + const pcre_study_data *, pcre_study_data *); +extern int _pcre_ucp_findprop(const unsigned int, int *, int *); +extern unsigned int _pcre_ucp_othercase(const unsigned int); +extern int _pcre_valid_utf8(const uschar *, int); +extern BOOL _pcre_was_newline(const uschar *, int, const uschar *, + int *, BOOL); +extern BOOL _pcre_xclass(int, const uschar *); + +#endif + +/* End of pcre_internal.h */ diff --git a/Engine/lib/pcre/pcre_maketables.c b/Engine/lib/pcre/pcre_maketables.c new file mode 100644 index 000000000..219973e37 --- /dev/null +++ b/Engine/lib/pcre/pcre_maketables.c @@ -0,0 +1,143 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2008 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains the external function pcre_maketables(), which builds +character tables for PCRE in the current locale. The file is compiled on its +own as part of the PCRE library. However, it is also included in the +compilation of dftables.c, in which case the macro DFTABLES is defined. */ + + +#ifndef DFTABLES +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif +# include "pcre_internal.h" +#endif + + +/************************************************* +* Create PCRE character tables * +*************************************************/ + +/* This function builds a set of character tables for use by PCRE and returns +a pointer to them. They are build using the ctype functions, and consequently +their contents will depend upon the current locale setting. When compiled as +part of the library, the store is obtained via pcre_malloc(), but when compiled +inside dftables, use malloc(). + +Arguments: none +Returns: pointer to the contiguous block of data +*/ + +const unsigned char * +pcre_maketables(void) +{ +unsigned char *yield, *p; +int i; + +#ifndef DFTABLES +yield = (unsigned char*)(pcre_malloc)(tables_length); +#else +yield = (unsigned char*)malloc(tables_length); +#endif + +if (yield == NULL) return NULL; +p = yield; + +/* First comes the lower casing table */ + +for (i = 0; i < 256; i++) *p++ = tolower(i); + +/* Next the case-flipping table */ + +for (i = 0; i < 256; i++) *p++ = islower(i)? toupper(i) : tolower(i); + +/* Then the character class tables. Don't try to be clever and save effort on +exclusive ones - in some locales things may be different. Note that the table +for "space" includes everything "isspace" gives, including VT in the default +locale. This makes it work for the POSIX class [:space:]. Note also that it is +possible for a character to be alnum or alpha without being lower or upper, +such as "male and female ordinals" (\xAA and \xBA) in the fr_FR locale (at +least under Debian Linux's locales as of 12/2005). So we must test for alnum +specially. */ + +memset(p, 0, cbit_length); +for (i = 0; i < 256; i++) + { + if (isdigit(i)) p[cbit_digit + i/8] |= 1 << (i&7); + if (isupper(i)) p[cbit_upper + i/8] |= 1 << (i&7); + if (islower(i)) p[cbit_lower + i/8] |= 1 << (i&7); + if (isalnum(i)) p[cbit_word + i/8] |= 1 << (i&7); + if (i == '_') p[cbit_word + i/8] |= 1 << (i&7); + if (isspace(i)) p[cbit_space + i/8] |= 1 << (i&7); + if (isxdigit(i))p[cbit_xdigit + i/8] |= 1 << (i&7); + if (isgraph(i)) p[cbit_graph + i/8] |= 1 << (i&7); + if (isprint(i)) p[cbit_print + i/8] |= 1 << (i&7); + if (ispunct(i)) p[cbit_punct + i/8] |= 1 << (i&7); + if (iscntrl(i)) p[cbit_cntrl + i/8] |= 1 << (i&7); + } +p += cbit_length; + +/* Finally, the character type table. In this, we exclude VT from the white +space chars, because Perl doesn't recognize it as such for \s and for comments +within regexes. */ + +for (i = 0; i < 256; i++) + { + int x = 0; + if (i != 0x0b && isspace(i)) x += ctype_space; + if (isalpha(i)) x += ctype_letter; + if (isdigit(i)) x += ctype_digit; + if (isxdigit(i)) x += ctype_xdigit; + if (isalnum(i) || i == '_') x += ctype_word; + + /* Note: strchr includes the terminating zero in the characters it considers. + In this instance, that is ok because we want binary zero to be flagged as a + meta-character, which in this sense is any character that terminates a run + of data characters. */ + + if (strchr("\\*+?{^.$|()[", i) != 0) x += ctype_meta; + *p++ = x; + } + +return yield; +} + +/* End of pcre_maketables.c */ diff --git a/Engine/lib/pcre/pcre_newline.c b/Engine/lib/pcre/pcre_newline.c new file mode 100644 index 000000000..58885760e --- /dev/null +++ b/Engine/lib/pcre/pcre_newline.c @@ -0,0 +1,164 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2008 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains internal functions for testing newlines when more than +one kind of newline is to be recognized. When a newline is found, its length is +returned. In principle, we could implement several newline "types", each +referring to a different set of newline characters. At present, PCRE supports +only NLTYPE_FIXED, which gets handled without these functions, NLTYPE_ANYCRLF, +and NLTYPE_ANY. The full list of Unicode newline characters is taken from +http://unicode.org/unicode/reports/tr18/. */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + + + +/************************************************* +* Check for newline at given position * +*************************************************/ + +/* It is guaranteed that the initial value of ptr is less than the end of the +string that is being processed. + +Arguments: + ptr pointer to possible newline + type the newline type + endptr pointer to the end of the string + lenptr where to return the length + utf8 TRUE if in utf8 mode + +Returns: TRUE or FALSE +*/ + +BOOL +_pcre_is_newline(const uschar *ptr, int type, const uschar *endptr, + int *lenptr, BOOL utf8) +{ +int c; +if (utf8) { GETCHAR(c, ptr); } else c = *ptr; + +if (type == NLTYPE_ANYCRLF) switch(c) + { + case 0x000a: *lenptr = 1; return TRUE; /* LF */ + case 0x000d: *lenptr = (ptr < endptr - 1 && ptr[1] == 0x0a)? 2 : 1; + return TRUE; /* CR */ + default: return FALSE; + } + +/* NLTYPE_ANY */ + +else switch(c) + { + case 0x000a: /* LF */ + case 0x000b: /* VT */ + case 0x000c: *lenptr = 1; return TRUE; /* FF */ + case 0x000d: *lenptr = (ptr < endptr - 1 && ptr[1] == 0x0a)? 2 : 1; + return TRUE; /* CR */ + case 0x0085: *lenptr = utf8? 2 : 1; return TRUE; /* NEL */ + case 0x2028: /* LS */ + case 0x2029: *lenptr = 3; return TRUE; /* PS */ + default: return FALSE; + } +} + + + +/************************************************* +* Check for newline at previous position * +*************************************************/ + +/* It is guaranteed that the initial value of ptr is greater than the start of +the string that is being processed. + +Arguments: + ptr pointer to possible newline + type the newline type + startptr pointer to the start of the string + lenptr where to return the length + utf8 TRUE if in utf8 mode + +Returns: TRUE or FALSE +*/ + +BOOL +_pcre_was_newline(const uschar *ptr, int type, const uschar *startptr, + int *lenptr, BOOL utf8) +{ +int c; +ptr--; +#ifdef SUPPORT_UTF8 +if (utf8) + { + BACKCHAR(ptr); + GETCHAR(c, ptr); + } +else c = *ptr; +#else /* no UTF-8 support */ +c = *ptr; +#endif /* SUPPORT_UTF8 */ + +if (type == NLTYPE_ANYCRLF) switch(c) + { + case 0x000a: *lenptr = (ptr > startptr && ptr[-1] == 0x0d)? 2 : 1; + return TRUE; /* LF */ + case 0x000d: *lenptr = 1; return TRUE; /* CR */ + default: return FALSE; + } + +else switch(c) + { + case 0x000a: *lenptr = (ptr > startptr && ptr[-1] == 0x0d)? 2 : 1; + return TRUE; /* LF */ + case 0x000b: /* VT */ + case 0x000c: /* FF */ + case 0x000d: *lenptr = 1; return TRUE; /* CR */ + case 0x0085: *lenptr = utf8? 2 : 1; return TRUE; /* NEL */ + case 0x2028: /* LS */ + case 0x2029: *lenptr = 3; return TRUE; /* PS */ + default: return FALSE; + } +} + +/* End of pcre_newline.c */ diff --git a/Engine/lib/pcre/pcre_ord2utf8.c b/Engine/lib/pcre/pcre_ord2utf8.c new file mode 100644 index 000000000..0fdc512a0 --- /dev/null +++ b/Engine/lib/pcre/pcre_ord2utf8.c @@ -0,0 +1,85 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2008 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This file contains a private PCRE function that converts an ordinal +character value into a UTF8 string. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + + +/************************************************* +* Convert character value to UTF-8 * +*************************************************/ + +/* This function takes an integer value in the range 0 - 0x7fffffff +and encodes it as a UTF-8 character in 0 to 6 bytes. + +Arguments: + cvalue the character value + buffer pointer to buffer for result - at least 6 bytes long + +Returns: number of characters placed in the buffer +*/ + +int +_pcre_ord2utf8(int cvalue, uschar *buffer) +{ +#ifdef SUPPORT_UTF8 +register int i, j; +for (i = 0; i < _pcre_utf8_table1_size; i++) + if (cvalue <= _pcre_utf8_table1[i]) break; +buffer += i; +for (j = i; j > 0; j--) + { + *buffer-- = 0x80 | (cvalue & 0x3f); + cvalue >>= 6; + } +*buffer = _pcre_utf8_table2[i] | cvalue; +return i + 1; +#else +return 0; /* Keep compiler happy; this function won't ever be */ +#endif /* called when SUPPORT_UTF8 is not defined. */ +} + +/* End of pcre_ord2utf8.c */ diff --git a/Engine/lib/pcre/pcre_printint.src b/Engine/lib/pcre/pcre_printint.src new file mode 100644 index 000000000..98b42aa80 --- /dev/null +++ b/Engine/lib/pcre/pcre_printint.src @@ -0,0 +1,512 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2008 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains a PCRE private debugging function for printing out the +internal form of a compiled regular expression, along with some supporting +local functions. This source file is used in two places: + +(1) It is #included by pcre_compile.c when it is compiled in debugging mode +(DEBUG defined in pcre_internal.h). It is not included in production compiles. + +(2) It is always #included by pcretest.c, which can be asked to print out a +compiled regex for debugging purposes. */ + + +/* Macro that decides whether a character should be output as a literal or in +hexadecimal. We don't use isprint() because that can vary from system to system +(even without the use of locales) and we want the output always to be the same, +for testing purposes. This macro is used in pcretest as well as in this file. */ + +#define PRINTABLE(c) ((c) >= 32 && (c) < 127) + +/* The table of operator names. */ + +static const char *OP_names[] = { OP_NAME_LIST }; + + + +/************************************************* +* Print single- or multi-byte character * +*************************************************/ + +static int +print_char(FILE *f, uschar *ptr, BOOL utf8) +{ +int c = *ptr; + +#ifndef SUPPORT_UTF8 +utf8 = utf8; /* Avoid compiler warning */ +if (PRINTABLE(c)) fprintf(f, "%c", c); else fprintf(f, "\\x%02x", c); +return 0; + +#else +if (!utf8 || (c & 0xc0) != 0xc0) + { + if (PRINTABLE(c)) fprintf(f, "%c", c); else fprintf(f, "\\x%02x", c); + return 0; + } +else + { + int i; + int a = _pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes */ + int s = 6*a; + c = (c & _pcre_utf8_table3[a]) << s; + for (i = 1; i <= a; i++) + { + /* This is a check for malformed UTF-8; it should only occur if the sanity + check has been turned off. Rather than swallow random bytes, just stop if + we hit a bad one. Print it with \X instead of \x as an indication. */ + + if ((ptr[i] & 0xc0) != 0x80) + { + fprintf(f, "\\X{%x}", c); + return i - 1; + } + + /* The byte is OK */ + + s -= 6; + c |= (ptr[i] & 0x3f) << s; + } + if (c < 128) fprintf(f, "\\x%02x", c); else fprintf(f, "\\x{%x}", c); + return a; + } +#endif +} + + + +/************************************************* +* Find Unicode property name * +*************************************************/ + +static const char * +get_ucpname(int ptype, int pvalue) +{ +#ifdef SUPPORT_UCP +int i; +for (i = _pcre_utt_size - 1; i >= 0; i--) + { + if (ptype == _pcre_utt[i].type && pvalue == _pcre_utt[i].value) break; + } +return (i >= 0)? _pcre_utt_names + _pcre_utt[i].name_offset : "??"; +#else +/* It gets harder and harder to shut off unwanted compiler warnings. */ +ptype = ptype * pvalue; +return (ptype == pvalue)? "??" : "??"; +#endif +} + + + +/************************************************* +* Print compiled regex * +*************************************************/ + +/* Make this function work for a regex with integers either byte order. +However, we assume that what we are passed is a compiled regex. The +print_lengths flag controls whether offsets and lengths of items are printed. +They can be turned off from pcretest so that automatic tests on bytecode can be +written that do not depend on the value of LINK_SIZE. */ + +static void +pcre_printint(pcre *external_re, FILE *f, BOOL print_lengths) +{ +real_pcre *re = (real_pcre *)external_re; +uschar *codestart, *code; +BOOL utf8; + +unsigned int options = re->options; +int offset = re->name_table_offset; +int count = re->name_count; +int size = re->name_entry_size; + +if (re->magic_number != MAGIC_NUMBER) + { + offset = ((offset << 8) & 0xff00) | ((offset >> 8) & 0xff); + count = ((count << 8) & 0xff00) | ((count >> 8) & 0xff); + size = ((size << 8) & 0xff00) | ((size >> 8) & 0xff); + options = ((options << 24) & 0xff000000) | + ((options << 8) & 0x00ff0000) | + ((options >> 8) & 0x0000ff00) | + ((options >> 24) & 0x000000ff); + } + +code = codestart = (uschar *)re + offset + count * size; +utf8 = (options & PCRE_UTF8) != 0; + +for(;;) + { + uschar *ccode; + int c; + int extra = 0; + + if (print_lengths) + fprintf(f, "%3d ", (int)(code - codestart)); + else + fprintf(f, " "); + + switch(*code) + { + case OP_END: + fprintf(f, " %s\n", OP_names[*code]); + fprintf(f, "------------------------------------------------------------------\n"); + return; + + case OP_OPT: + fprintf(f, " %.2x %s", code[1], OP_names[*code]); + break; + + case OP_CHAR: + fprintf(f, " "); + do + { + code++; + code += 1 + print_char(f, code, utf8); + } + while (*code == OP_CHAR); + fprintf(f, "\n"); + continue; + + case OP_CHARNC: + fprintf(f, " NC "); + do + { + code++; + code += 1 + print_char(f, code, utf8); + } + while (*code == OP_CHARNC); + fprintf(f, "\n"); + continue; + + case OP_CBRA: + case OP_SCBRA: + if (print_lengths) fprintf(f, "%3d ", GET(code, 1)); + else fprintf(f, " "); + fprintf(f, "%s %d", OP_names[*code], GET2(code, 1+LINK_SIZE)); + break; + + case OP_BRA: + case OP_SBRA: + case OP_KETRMAX: + case OP_KETRMIN: + case OP_ALT: + case OP_KET: + case OP_ASSERT: + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + case OP_ONCE: + case OP_COND: + case OP_SCOND: + case OP_REVERSE: + if (print_lengths) fprintf(f, "%3d ", GET(code, 1)); + else fprintf(f, " "); + fprintf(f, "%s", OP_names[*code]); + break; + + case OP_CREF: + fprintf(f, "%3d %s", GET2(code,1), OP_names[*code]); + break; + + case OP_RREF: + c = GET2(code, 1); + if (c == RREF_ANY) + fprintf(f, " Cond recurse any"); + else + fprintf(f, " Cond recurse %d", c); + break; + + case OP_DEF: + fprintf(f, " Cond def"); + break; + + case OP_STAR: + case OP_MINSTAR: + case OP_POSSTAR: + case OP_PLUS: + case OP_MINPLUS: + case OP_POSPLUS: + case OP_QUERY: + case OP_MINQUERY: + case OP_POSQUERY: + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPOSSTAR: + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + case OP_TYPEPOSPLUS: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + case OP_TYPEPOSQUERY: + fprintf(f, " "); + if (*code >= OP_TYPESTAR) + { + fprintf(f, "%s", OP_names[code[1]]); + if (code[1] == OP_PROP || code[1] == OP_NOTPROP) + { + fprintf(f, " %s ", get_ucpname(code[2], code[3])); + extra = 2; + } + } + else extra = print_char(f, code+1, utf8); + fprintf(f, "%s", OP_names[*code]); + break; + + case OP_EXACT: + case OP_UPTO: + case OP_MINUPTO: + case OP_POSUPTO: + fprintf(f, " "); + extra = print_char(f, code+3, utf8); + fprintf(f, "{"); + if (*code != OP_EXACT) fprintf(f, "0,"); + fprintf(f, "%d}", GET2(code,1)); + if (*code == OP_MINUPTO) fprintf(f, "?"); + else if (*code == OP_POSUPTO) fprintf(f, "+"); + break; + + case OP_TYPEEXACT: + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + case OP_TYPEPOSUPTO: + fprintf(f, " %s", OP_names[code[3]]); + if (code[3] == OP_PROP || code[3] == OP_NOTPROP) + { + fprintf(f, " %s ", get_ucpname(code[4], code[5])); + extra = 2; + } + fprintf(f, "{"); + if (*code != OP_TYPEEXACT) fprintf(f, "0,"); + fprintf(f, "%d}", GET2(code,1)); + if (*code == OP_TYPEMINUPTO) fprintf(f, "?"); + else if (*code == OP_TYPEPOSUPTO) fprintf(f, "+"); + break; + + case OP_NOT: + c = code[1]; + if (PRINTABLE(c)) fprintf(f, " [^%c]", c); + else fprintf(f, " [^\\x%02x]", c); + break; + + case OP_NOTSTAR: + case OP_NOTMINSTAR: + case OP_NOTPOSSTAR: + case OP_NOTPLUS: + case OP_NOTMINPLUS: + case OP_NOTPOSPLUS: + case OP_NOTQUERY: + case OP_NOTMINQUERY: + case OP_NOTPOSQUERY: + c = code[1]; + if (PRINTABLE(c)) fprintf(f, " [^%c]", c); + else fprintf(f, " [^\\x%02x]", c); + fprintf(f, "%s", OP_names[*code]); + break; + + case OP_NOTEXACT: + case OP_NOTUPTO: + case OP_NOTMINUPTO: + case OP_NOTPOSUPTO: + c = code[3]; + if (PRINTABLE(c)) fprintf(f, " [^%c]{", c); + else fprintf(f, " [^\\x%02x]{", c); + if (*code != OP_NOTEXACT) fprintf(f, "0,"); + fprintf(f, "%d}", GET2(code,1)); + if (*code == OP_NOTMINUPTO) fprintf(f, "?"); + else if (*code == OP_NOTPOSUPTO) fprintf(f, "+"); + break; + + case OP_RECURSE: + if (print_lengths) fprintf(f, "%3d ", GET(code, 1)); + else fprintf(f, " "); + fprintf(f, "%s", OP_names[*code]); + break; + + case OP_REF: + fprintf(f, " \\%d", GET2(code,1)); + ccode = code + _pcre_OP_lengths[*code]; + goto CLASS_REF_REPEAT; + + case OP_CALLOUT: + fprintf(f, " %s %d %d %d", OP_names[*code], code[1], GET(code,2), + GET(code, 2 + LINK_SIZE)); + break; + + case OP_PROP: + case OP_NOTPROP: + fprintf(f, " %s %s", OP_names[*code], get_ucpname(code[1], code[2])); + break; + + /* OP_XCLASS can only occur in UTF-8 mode. However, there's no harm in + having this code always here, and it makes it less messy without all those + #ifdefs. */ + + case OP_CLASS: + case OP_NCLASS: + case OP_XCLASS: + { + int i, min, max; + BOOL printmap; + + fprintf(f, " ["); + + if (*code == OP_XCLASS) + { + extra = GET(code, 1); + ccode = code + LINK_SIZE + 1; + printmap = (*ccode & XCL_MAP) != 0; + if ((*ccode++ & XCL_NOT) != 0) fprintf(f, "^"); + } + else + { + printmap = TRUE; + ccode = code + 1; + } + + /* Print a bit map */ + + if (printmap) + { + for (i = 0; i < 256; i++) + { + if ((ccode[i/8] & (1 << (i&7))) != 0) + { + int j; + for (j = i+1; j < 256; j++) + if ((ccode[j/8] & (1 << (j&7))) == 0) break; + if (i == '-' || i == ']') fprintf(f, "\\"); + if (PRINTABLE(i)) fprintf(f, "%c", i); + else fprintf(f, "\\x%02x", i); + if (--j > i) + { + if (j != i + 1) fprintf(f, "-"); + if (j == '-' || j == ']') fprintf(f, "\\"); + if (PRINTABLE(j)) fprintf(f, "%c", j); + else fprintf(f, "\\x%02x", j); + } + i = j; + } + } + ccode += 32; + } + + /* For an XCLASS there is always some additional data */ + + if (*code == OP_XCLASS) + { + int ch; + while ((ch = *ccode++) != XCL_END) + { + if (ch == XCL_PROP) + { + int ptype = *ccode++; + int pvalue = *ccode++; + fprintf(f, "\\p{%s}", get_ucpname(ptype, pvalue)); + } + else if (ch == XCL_NOTPROP) + { + int ptype = *ccode++; + int pvalue = *ccode++; + fprintf(f, "\\P{%s}", get_ucpname(ptype, pvalue)); + } + else + { + ccode += 1 + print_char(f, ccode, TRUE); + if (ch == XCL_RANGE) + { + fprintf(f, "-"); + ccode += 1 + print_char(f, ccode, TRUE); + } + } + } + } + + /* Indicate a non-UTF8 class which was created by negation */ + + fprintf(f, "]%s", (*code == OP_NCLASS)? " (neg)" : ""); + + /* Handle repeats after a class or a back reference */ + + CLASS_REF_REPEAT: + switch(*ccode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRPLUS: + case OP_CRMINPLUS: + case OP_CRQUERY: + case OP_CRMINQUERY: + fprintf(f, "%s", OP_names[*ccode]); + extra += _pcre_OP_lengths[*ccode]; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + min = GET2(ccode,1); + max = GET2(ccode,3); + if (max == 0) fprintf(f, "{%d,}", min); + else fprintf(f, "{%d,%d}", min, max); + if (*ccode == OP_CRMINRANGE) fprintf(f, "?"); + extra += _pcre_OP_lengths[*ccode]; + break; + + /* Do nothing if it's not a repeat; this code stops picky compilers + warning about the lack of a default code path. */ + + default: + break; + } + } + break; + + /* Anything else is just an item with no data*/ + + default: + fprintf(f, " %s", OP_names[*code]); + break; + } + + code += _pcre_OP_lengths[*code] + extra; + fprintf(f, "\n"); + } +} + +/* End of pcre_printint.src */ diff --git a/Engine/lib/pcre/pcre_refcount.c b/Engine/lib/pcre/pcre_refcount.c new file mode 100644 index 000000000..eeb2897c8 --- /dev/null +++ b/Engine/lib/pcre/pcre_refcount.c @@ -0,0 +1,82 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2008 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains the external function pcre_refcount(), which is an +auxiliary function that can be used to maintain a reference count in a compiled +pattern data block. This might be helpful in applications where the block is +shared by different users. */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + + +/************************************************* +* Maintain reference count * +*************************************************/ + +/* The reference count is a 16-bit field, initialized to zero. It is not +possible to transfer a non-zero count from one host to a different host that +has a different byte order - though I can't see why anyone in their right mind +would ever want to do that! + +Arguments: + argument_re points to compiled code + adjust value to add to the count + +Returns: the (possibly updated) count value (a non-negative number), or + a negative error number +*/ + +PCRE_EXP_DEFN int +pcre_refcount(pcre *argument_re, int adjust) +{ +real_pcre *re = (real_pcre *)argument_re; +if (re == NULL) return PCRE_ERROR_NULL; +re->ref_count = (-adjust > re->ref_count)? 0 : + (adjust + re->ref_count > 65535)? 65535 : + re->ref_count + adjust; +return re->ref_count; +} + +/* End of pcre_refcount.c */ diff --git a/Engine/lib/pcre/pcre_scanner.cc b/Engine/lib/pcre/pcre_scanner.cc new file mode 100644 index 000000000..a817a684e --- /dev/null +++ b/Engine/lib/pcre/pcre_scanner.cc @@ -0,0 +1,199 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Sanjay Ghemawat + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "pcrecpp_internal.h" +#include "pcre_scanner.h" + +using std::vector; + +namespace pcrecpp { + +Scanner::Scanner() + : data_(), + input_(data_), + skip_(NULL), + should_skip_(false), + skip_repeat_(false), + save_comments_(false), + comments_(NULL), + comments_offset_(0) { +} + +Scanner::Scanner(const string& in) + : data_(in), + input_(data_), + skip_(NULL), + should_skip_(false), + skip_repeat_(false), + save_comments_(false), + comments_(NULL), + comments_offset_(0) { +} + +Scanner::~Scanner() { + delete skip_; + delete comments_; +} + +void Scanner::SetSkipExpression(const char* re) { + delete skip_; + if (re != NULL) { + skip_ = new RE(re); + should_skip_ = true; + skip_repeat_ = true; + ConsumeSkip(); + } else { + skip_ = NULL; + should_skip_ = false; + skip_repeat_ = false; + } +} + +void Scanner::Skip(const char* re) { + delete skip_; + if (re != NULL) { + skip_ = new RE(re); + should_skip_ = true; + skip_repeat_ = false; + ConsumeSkip(); + } else { + skip_ = NULL; + should_skip_ = false; + skip_repeat_ = false; + } +} + +void Scanner::DisableSkip() { + assert(skip_ != NULL); + should_skip_ = false; +} + +void Scanner::EnableSkip() { + assert(skip_ != NULL); + should_skip_ = true; + ConsumeSkip(); +} + +int Scanner::LineNumber() const { + // TODO: Make it more efficient by keeping track of the last point + // where we computed line numbers and counting newlines since then. + // We could use std:count, but not all systems have it. :-( + int count = 1; + for (const char* p = data_.data(); p < input_.data(); ++p) + if (*p == '\n') + ++count; + return count; +} + +int Scanner::Offset() const { + return input_.data() - data_.c_str(); +} + +bool Scanner::LookingAt(const RE& re) const { + int consumed; + return re.DoMatch(input_, RE::ANCHOR_START, &consumed, 0, 0); +} + + +bool Scanner::Consume(const RE& re, + const Arg& arg0, + const Arg& arg1, + const Arg& arg2) { + const bool result = re.Consume(&input_, arg0, arg1, arg2); + if (result && should_skip_) ConsumeSkip(); + return result; +} + +// helper function to consume *skip_ and honour save_comments_ +void Scanner::ConsumeSkip() { + const char* start_data = input_.data(); + while (skip_->Consume(&input_)) { + if (!skip_repeat_) { + // Only one skip allowed. + break; + } + } + if (save_comments_) { + if (comments_ == NULL) { + comments_ = new vector; + } + // already pointing one past end, so no need to +1 + int length = input_.data() - start_data; + if (length > 0) { + comments_->push_back(StringPiece(start_data, length)); + } + } +} + + +void Scanner::GetComments(int start, int end, vector *ranges) { + // short circuit out if we've not yet initialized comments_ + // (e.g., when save_comments is false) + if (!comments_) { + return; + } + // TODO: if we guarantee that comments_ will contain StringPieces + // that are ordered by their start, then we can do a binary search + // for the first StringPiece at or past start and then scan for the + // ones contained in the range, quit early (use equal_range or + // lower_bound) + for (vector::const_iterator it = comments_->begin(); + it != comments_->end(); ++it) { + if ((it->data() >= data_.c_str() + start && + it->data() + it->size() <= data_.c_str() + end)) { + ranges->push_back(*it); + } + } +} + + +void Scanner::GetNextComments(vector *ranges) { + // short circuit out if we've not yet initialized comments_ + // (e.g., when save_comments is false) + if (!comments_) { + return; + } + for (vector::const_iterator it = + comments_->begin() + comments_offset_; + it != comments_->end(); ++it) { + ranges->push_back(*it); + ++comments_offset_; + } +} + +} // namespace pcrecpp diff --git a/Engine/lib/pcre/pcre_scanner.h b/Engine/lib/pcre/pcre_scanner.h new file mode 100644 index 000000000..5617e4515 --- /dev/null +++ b/Engine/lib/pcre/pcre_scanner.h @@ -0,0 +1,172 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Sanjay Ghemawat +// +// Regular-expression based scanner for parsing an input stream. +// +// Example 1: parse a sequence of "var = number" entries from input: +// +// Scanner scanner(input); +// string var; +// int number; +// scanner.SetSkipExpression("\\s+"); // Skip any white space we encounter +// while (scanner.Consume("(\\w+) = (\\d+)", &var, &number)) { +// ...; +// } + +#ifndef _PCRE_SCANNER_H +#define _PCRE_SCANNER_H + +#include +#include +#include + +#include +#include + +namespace pcrecpp { + +class PCRECPP_EXP_DEFN Scanner { + public: + Scanner(); + explicit Scanner(const std::string& input); + ~Scanner(); + + // Return current line number. The returned line-number is + // one-based. I.e. it returns 1 + the number of consumed newlines. + // + // Note: this method may be slow. It may take time proportional to + // the size of the input. + int LineNumber() const; + + // Return the byte-offset that the scanner is looking in the + // input data; + int Offset() const; + + // Return true iff the start of the remaining input matches "re" + bool LookingAt(const RE& re) const; + + // Return true iff all of the following are true + // a. the start of the remaining input matches "re", + // b. if any arguments are supplied, matched sub-patterns can be + // parsed and stored into the arguments. + // If it returns true, it skips over the matched input and any + // following input that matches the "skip" regular expression. + bool Consume(const RE& re, + const Arg& arg0 = RE::no_arg, + const Arg& arg1 = RE::no_arg, + const Arg& arg2 = RE::no_arg + // TODO: Allow more arguments? + ); + + // Set the "skip" regular expression. If after consuming some data, + // a prefix of the input matches this RE, it is automatically + // skipped. For example, a programming language scanner would use + // a skip RE that matches white space and comments. + // + // scanner.SetSkipExpression("\\s+|//.*|/[*](.|\n)*?[*]/"); + // + // Skipping repeats as long as it succeeds. We used to let people do + // this by writing "(...)*" in the regular expression, but that added + // up to lots of recursive calls within the pcre library, so now we + // control repetition explicitly via the function call API. + // + // You can pass NULL for "re" if you do not want any data to be skipped. + void Skip(const char* re); // DEPRECATED; does *not* repeat + void SetSkipExpression(const char* re); + + // Temporarily pause "skip"ing. This + // Skip("Foo"); code ; DisableSkip(); code; EnableSkip() + // is similar to + // Skip("Foo"); code ; Skip(NULL); code ; Skip("Foo"); + // but avoids creating/deleting new RE objects. + void DisableSkip(); + + // Reenable previously paused skipping. Any prefix of the input + // that matches the skip pattern is immediately dropped. + void EnableSkip(); + + /***** Special wrappers around SetSkip() for some common idioms *****/ + + // Arranges to skip whitespace, C comments, C++ comments. + // The overall RE is a disjunction of the following REs: + // \\s whitespace + // //.*\n C++ comment + // /[*](.|\n)*?[*]/ C comment (x*? means minimal repetitions of x) + // We get repetition via the semantics of SetSkipExpression, not by using * + void SkipCXXComments() { + SetSkipExpression("\\s|//.*\n|/[*](?:\n|.)*?[*]/"); + } + + void set_save_comments(bool comments) { + save_comments_ = comments; + } + + bool save_comments() { + return save_comments_; + } + + // Append to vector ranges the comments found in the + // byte range [start,end] (inclusive) of the input data. + // Only comments that were extracted entirely within that + // range are returned: no range splitting of atomically-extracted + // comments is performed. + void GetComments(int start, int end, std::vector *ranges); + + // Append to vector ranges the comments added + // since the last time this was called. This + // functionality is provided for efficiency when + // interleaving scanning with parsing. + void GetNextComments(std::vector *ranges); + + private: + std::string data_; // All the input data + StringPiece input_; // Unprocessed input + RE* skip_; // If non-NULL, RE for skipping input + bool should_skip_; // If true, use skip_ + bool skip_repeat_; // If true, repeat skip_ as long as it works + bool save_comments_; // If true, aggregate the skip expression + + // the skipped comments + // TODO: later consider requiring that the StringPieces be added + // in order by their start position + std::vector *comments_; + + // the offset into comments_ that has been returned by GetNextComments + int comments_offset_; + + // helper function to consume *skip_ and honour + // save_comments_ + void ConsumeSkip(); +}; + +} // namespace pcrecpp + +#endif /* _PCRE_SCANNER_H */ diff --git a/Engine/lib/pcre/pcre_stringpiece.cc b/Engine/lib/pcre/pcre_stringpiece.cc new file mode 100644 index 000000000..67c0f1fc0 --- /dev/null +++ b/Engine/lib/pcre/pcre_stringpiece.cc @@ -0,0 +1,43 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wilsonh@google.com (Wilson Hsieh) +// + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "pcrecpp_internal.h" +#include "pcre_stringpiece.h" + +std::ostream& operator<<(std::ostream& o, const pcrecpp::StringPiece& piece) { + return (o << piece.as_string()); +} diff --git a/Engine/lib/pcre/pcre_stringpiece.h b/Engine/lib/pcre/pcre_stringpiece.h new file mode 100644 index 000000000..a8af8d6db --- /dev/null +++ b/Engine/lib/pcre/pcre_stringpiece.h @@ -0,0 +1,180 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Sanjay Ghemawat +// +// A string like object that points into another piece of memory. +// Useful for providing an interface that allows clients to easily +// pass in either a "const char*" or a "string". +// +// Arghh! I wish C++ literals were automatically of type "string". + +#ifndef _PCRE_STRINGPIECE_H +#define _PCRE_STRINGPIECE_H + +#include +#include +#include // for ostream forward-declaration + +// Don't use bits/type_traits.h on Linux - Andrew Galante, GG 8/2/2009 +#if !defined(_MSC_VER) && !defined(__CELLOS_LV2__) && !defined(__APPLE__) && !defined(__linux__) +#ifdef __MINGW32__ +#define HAVE_TYPE_TRAITS +#include +#else +#define HAVE_TYPE_TRAITS +#include +#endif +#endif + +#include + +using std::string; + +namespace pcrecpp { + +class PCRECPP_EXP_DEFN StringPiece { + private: + const char* ptr_; + int length_; + + public: + // We provide non-explicit singleton constructors so users can pass + // in a "const char*" or a "string" wherever a "StringPiece" is + // expected. + StringPiece() + : ptr_(NULL), length_(0) { } + StringPiece(const char* str) + : ptr_(str), length_(static_cast(strlen(ptr_))) { } + StringPiece(const unsigned char* str) + : ptr_(reinterpret_cast(str)), + length_(static_cast(strlen(ptr_))) { } + StringPiece(const string& str) + : ptr_(str.data()), length_(static_cast(str.size())) { } + StringPiece(const char* offset, int len) + : ptr_(offset), length_(len) { } + + // data() may return a pointer to a buffer with embedded NULs, and the + // returned buffer may or may not be null terminated. Therefore it is + // typically a mistake to pass data() to a routine that expects a NUL + // terminated string. Use "as_string().c_str()" if you really need to do + // this. Or better yet, change your routine so it does not rely on NUL + // termination. + const char* data() const { return ptr_; } + int size() const { return length_; } + bool empty() const { return length_ == 0; } + + void clear() { ptr_ = NULL; length_ = 0; } + void set(const char* buffer, int len) { ptr_ = buffer; length_ = len; } + void set(const char* str) { + ptr_ = str; + length_ = static_cast(strlen(str)); + } + void set(const void* buffer, int len) { + ptr_ = reinterpret_cast(buffer); + length_ = len; + } + + char operator[](int i) const { return ptr_[i]; } + + void remove_prefix(int n) { + ptr_ += n; + length_ -= n; + } + + void remove_suffix(int n) { + length_ -= n; + } + + bool operator==(const StringPiece& x) const { + return ((length_ == x.length_) && + (memcmp(ptr_, x.ptr_, length_) == 0)); + } + bool operator!=(const StringPiece& x) const { + return !(*this == x); + } + +#define STRINGPIECE_BINARY_PREDICATE(cmp,auxcmp) \ + bool operator cmp (const StringPiece& x) const { \ + int r = memcmp(ptr_, x.ptr_, length_ < x.length_ ? length_ : x.length_); \ + return ((r auxcmp 0) || ((r == 0) && (length_ cmp x.length_))); \ + } + STRINGPIECE_BINARY_PREDICATE(<, <); + STRINGPIECE_BINARY_PREDICATE(<=, <); + STRINGPIECE_BINARY_PREDICATE(>=, >); + STRINGPIECE_BINARY_PREDICATE(>, >); +#undef STRINGPIECE_BINARY_PREDICATE + + int compare(const StringPiece& x) const { + int r = memcmp(ptr_, x.ptr_, length_ < x.length_ ? length_ : x.length_); + if (r == 0) { + if (length_ < x.length_) r = -1; + else if (length_ > x.length_) r = +1; + } + return r; + } + + string as_string() const { + return string(data(), size()); + } + + void CopyToString(string* target) const { + target->assign(ptr_, length_); + } + + // Does "this" start with "x" + bool starts_with(const StringPiece& x) const { + return ((length_ >= x.length_) && (memcmp(ptr_, x.ptr_, x.length_) == 0)); + } +}; + +} // namespace pcrecpp + +// ------------------------------------------------------------------ +// Functions used to create STL containers that use StringPiece +// Remember that a StringPiece's lifetime had better be less than +// that of the underlying string or char*. If it is not, then you +// cannot safely store a StringPiece into an STL container +// ------------------------------------------------------------------ + +#ifdef HAVE_TYPE_TRAITS +// This makes vector really fast for some STL implementations +template<> struct __type_traits { + typedef __true_type has_trivial_default_constructor; + typedef __true_type has_trivial_copy_constructor; + typedef __true_type has_trivial_assignment_operator; + typedef __true_type has_trivial_destructor; + typedef __true_type is_POD_type; +}; +#endif + +// allow StringPiece to be logged +std::ostream& operator<<(std::ostream& o, const pcrecpp::StringPiece& piece); + +#endif /* _PCRE_STRINGPIECE_H */ diff --git a/Engine/lib/pcre/pcre_study.c b/Engine/lib/pcre/pcre_study.c new file mode 100644 index 000000000..ff1f260ec --- /dev/null +++ b/Engine/lib/pcre/pcre_study.c @@ -0,0 +1,579 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2008 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains the external function pcre_study(), along with local +supporting functions. */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + + +/* Returns from set_start_bits() */ + +enum { SSB_FAIL, SSB_DONE, SSB_CONTINUE }; + + +/************************************************* +* Set a bit and maybe its alternate case * +*************************************************/ + +/* Given a character, set its bit in the table, and also the bit for the other +version of a letter if we are caseless. + +Arguments: + start_bits points to the bit map + c is the character + caseless the caseless flag + cd the block with char table pointers + +Returns: nothing +*/ + +static void +set_bit(uschar *start_bits, unsigned int c, BOOL caseless, compile_data *cd) +{ +start_bits[c/8] |= (1 << (c&7)); +if (caseless && (cd->ctypes[c] & ctype_letter) != 0) + start_bits[cd->fcc[c]/8] |= (1 << (cd->fcc[c]&7)); +} + + + +/************************************************* +* Create bitmap of starting bytes * +*************************************************/ + +/* This function scans a compiled unanchored expression recursively and +attempts to build a bitmap of the set of possible starting bytes. As time goes +by, we may be able to get more clever at doing this. The SSB_CONTINUE return is +useful for parenthesized groups in patterns such as (a*)b where the group +provides some optional starting bytes but scanning must continue at the outer +level to find at least one mandatory byte. At the outermost level, this +function fails unless the result is SSB_DONE. + +Arguments: + code points to an expression + start_bits points to a 32-byte table, initialized to 0 + caseless the current state of the caseless flag + utf8 TRUE if in UTF-8 mode + cd the block with char table pointers + +Returns: SSB_FAIL => Failed to find any starting bytes + SSB_DONE => Found mandatory starting bytes + SSB_CONTINUE => Found optional starting bytes +*/ + +static int +set_start_bits(const uschar *code, uschar *start_bits, BOOL caseless, + BOOL utf8, compile_data *cd) +{ +register int c; +int yield = SSB_DONE; + +#if 0 +/* ========================================================================= */ +/* The following comment and code was inserted in January 1999. In May 2006, +when it was observed to cause compiler warnings about unused values, I took it +out again. If anybody is still using OS/2, they will have to put it back +manually. */ + +/* This next statement and the later reference to dummy are here in order to +trick the optimizer of the IBM C compiler for OS/2 into generating correct +code. Apparently IBM isn't going to fix the problem, and we would rather not +disable optimization (in this module it actually makes a big difference, and +the pcre module can use all the optimization it can get). */ + +volatile int dummy; +/* ========================================================================= */ +#endif + +do + { + const uschar *tcode = code + (((int)*code == OP_CBRA)? 3:1) + LINK_SIZE; + BOOL try_next = TRUE; + + while (try_next) /* Loop for items in this branch */ + { + int rc; + switch(*tcode) + { + /* Fail if we reach something we don't understand */ + + default: + return SSB_FAIL; + + /* If we hit a bracket or a positive lookahead assertion, recurse to set + bits from within the subpattern. If it can't find anything, we have to + give up. If it finds some mandatory character(s), we are done for this + branch. Otherwise, carry on scanning after the subpattern. */ + + case OP_BRA: + case OP_SBRA: + case OP_CBRA: + case OP_SCBRA: + case OP_ONCE: + case OP_ASSERT: + rc = set_start_bits(tcode, start_bits, caseless, utf8, cd); + if (rc == SSB_FAIL) return SSB_FAIL; + if (rc == SSB_DONE) try_next = FALSE; else + { + do tcode += GET(tcode, 1); while (*tcode == OP_ALT); + tcode += 1 + LINK_SIZE; + } + break; + + /* If we hit ALT or KET, it means we haven't found anything mandatory in + this branch, though we might have found something optional. For ALT, we + continue with the next alternative, but we have to arrange that the final + result from subpattern is SSB_CONTINUE rather than SSB_DONE. For KET, + return SSB_CONTINUE: if this is the top level, that indicates failure, + but after a nested subpattern, it causes scanning to continue. */ + + case OP_ALT: + yield = SSB_CONTINUE; + try_next = FALSE; + break; + + case OP_KET: + case OP_KETRMAX: + case OP_KETRMIN: + return SSB_CONTINUE; + + /* Skip over callout */ + + case OP_CALLOUT: + tcode += 2 + 2*LINK_SIZE; + break; + + /* Skip over lookbehind and negative lookahead assertions */ + + case OP_ASSERT_NOT: + case OP_ASSERTBACK: + case OP_ASSERTBACK_NOT: + do tcode += GET(tcode, 1); while (*tcode == OP_ALT); + tcode += 1 + LINK_SIZE; + break; + + /* Skip over an option setting, changing the caseless flag */ + + case OP_OPT: + caseless = (tcode[1] & PCRE_CASELESS) != 0; + tcode += 2; + break; + + /* BRAZERO does the bracket, but carries on. */ + + case OP_BRAZERO: + case OP_BRAMINZERO: + if (set_start_bits(++tcode, start_bits, caseless, utf8, cd) == SSB_FAIL) + return SSB_FAIL; +/* ========================================================================= + See the comment at the head of this function concerning the next line, + which was an old fudge for the benefit of OS/2. + dummy = 1; + ========================================================================= */ + do tcode += GET(tcode,1); while (*tcode == OP_ALT); + tcode += 1 + LINK_SIZE; + break; + + /* Single-char * or ? sets the bit and tries the next item */ + + case OP_STAR: + case OP_MINSTAR: + case OP_POSSTAR: + case OP_QUERY: + case OP_MINQUERY: + case OP_POSQUERY: + set_bit(start_bits, tcode[1], caseless, cd); + tcode += 2; +#ifdef SUPPORT_UTF8 + if (utf8 && tcode[-1] >= 0xc0) + tcode += _pcre_utf8_table4[tcode[-1] & 0x3f]; +#endif + break; + + /* Single-char upto sets the bit and tries the next */ + + case OP_UPTO: + case OP_MINUPTO: + case OP_POSUPTO: + set_bit(start_bits, tcode[3], caseless, cd); + tcode += 4; +#ifdef SUPPORT_UTF8 + if (utf8 && tcode[-1] >= 0xc0) + tcode += _pcre_utf8_table4[tcode[-1] & 0x3f]; +#endif + break; + + /* At least one single char sets the bit and stops */ + + case OP_EXACT: /* Fall through */ + tcode += 2; + + case OP_CHAR: + case OP_CHARNC: + case OP_PLUS: + case OP_MINPLUS: + case OP_POSPLUS: + set_bit(start_bits, tcode[1], caseless, cd); + try_next = FALSE; + break; + + /* Single character type sets the bits and stops */ + + case OP_NOT_DIGIT: + for (c = 0; c < 32; c++) + start_bits[c] |= ~cd->cbits[c+cbit_digit]; + try_next = FALSE; + break; + + case OP_DIGIT: + for (c = 0; c < 32; c++) + start_bits[c] |= cd->cbits[c+cbit_digit]; + try_next = FALSE; + break; + + /* The cbit_space table has vertical tab as whitespace; we have to + discard it. */ + + case OP_NOT_WHITESPACE: + for (c = 0; c < 32; c++) + { + int d = cd->cbits[c+cbit_space]; + if (c == 1) d &= ~0x08; + start_bits[c] |= ~d; + } + try_next = FALSE; + break; + + /* The cbit_space table has vertical tab as whitespace; we have to + discard it. */ + + case OP_WHITESPACE: + for (c = 0; c < 32; c++) + { + int d = cd->cbits[c+cbit_space]; + if (c == 1) d &= ~0x08; + start_bits[c] |= d; + } + try_next = FALSE; + break; + + case OP_NOT_WORDCHAR: + for (c = 0; c < 32; c++) + start_bits[c] |= ~cd->cbits[c+cbit_word]; + try_next = FALSE; + break; + + case OP_WORDCHAR: + for (c = 0; c < 32; c++) + start_bits[c] |= cd->cbits[c+cbit_word]; + try_next = FALSE; + break; + + /* One or more character type fudges the pointer and restarts, knowing + it will hit a single character type and stop there. */ + + case OP_TYPEPLUS: + case OP_TYPEMINPLUS: + tcode++; + break; + + case OP_TYPEEXACT: + tcode += 3; + break; + + /* Zero or more repeats of character types set the bits and then + try again. */ + + case OP_TYPEUPTO: + case OP_TYPEMINUPTO: + case OP_TYPEPOSUPTO: + tcode += 2; /* Fall through */ + + case OP_TYPESTAR: + case OP_TYPEMINSTAR: + case OP_TYPEPOSSTAR: + case OP_TYPEQUERY: + case OP_TYPEMINQUERY: + case OP_TYPEPOSQUERY: + switch(tcode[1]) + { + case OP_ANY: + return SSB_FAIL; + + case OP_NOT_DIGIT: + for (c = 0; c < 32; c++) + start_bits[c] |= ~cd->cbits[c+cbit_digit]; + break; + + case OP_DIGIT: + for (c = 0; c < 32; c++) + start_bits[c] |= cd->cbits[c+cbit_digit]; + break; + + /* The cbit_space table has vertical tab as whitespace; we have to + discard it. */ + + case OP_NOT_WHITESPACE: + for (c = 0; c < 32; c++) + { + int d = cd->cbits[c+cbit_space]; + if (c == 1) d &= ~0x08; + start_bits[c] |= ~d; + } + break; + + /* The cbit_space table has vertical tab as whitespace; we have to + discard it. */ + + case OP_WHITESPACE: + for (c = 0; c < 32; c++) + { + int d = cd->cbits[c+cbit_space]; + if (c == 1) d &= ~0x08; + start_bits[c] |= d; + } + break; + + case OP_NOT_WORDCHAR: + for (c = 0; c < 32; c++) + start_bits[c] |= ~cd->cbits[c+cbit_word]; + break; + + case OP_WORDCHAR: + for (c = 0; c < 32; c++) + start_bits[c] |= cd->cbits[c+cbit_word]; + break; + } + + tcode += 2; + break; + + /* Character class where all the information is in a bit map: set the + bits and either carry on or not, according to the repeat count. If it was + a negative class, and we are operating with UTF-8 characters, any byte + with a value >= 0xc4 is a potentially valid starter because it starts a + character with a value > 255. */ + + case OP_NCLASS: +#ifdef SUPPORT_UTF8 + if (utf8) + { + start_bits[24] |= 0xf0; /* Bits for 0xc4 - 0xc8 */ + memset(start_bits+25, 0xff, 7); /* Bits for 0xc9 - 0xff */ + } +#endif + /* Fall through */ + + case OP_CLASS: + { + tcode++; + + /* In UTF-8 mode, the bits in a bit map correspond to character + values, not to byte values. However, the bit map we are constructing is + for byte values. So we have to do a conversion for characters whose + value is > 127. In fact, there are only two possible starting bytes for + characters in the range 128 - 255. */ + +#ifdef SUPPORT_UTF8 + if (utf8) + { + for (c = 0; c < 16; c++) start_bits[c] |= tcode[c]; + for (c = 128; c < 256; c++) + { + if ((tcode[c/8] && (1 << (c&7))) != 0) + { + int d = (c >> 6) | 0xc0; /* Set bit for this starter */ + start_bits[d/8] |= (1 << (d&7)); /* and then skip on to the */ + c = (c & 0xc0) + 0x40 - 1; /* next relevant character. */ + } + } + } + + /* In non-UTF-8 mode, the two bit maps are completely compatible. */ + + else +#endif + { + for (c = 0; c < 32; c++) start_bits[c] |= tcode[c]; + } + + /* Advance past the bit map, and act on what follows */ + + tcode += 32; + switch (*tcode) + { + case OP_CRSTAR: + case OP_CRMINSTAR: + case OP_CRQUERY: + case OP_CRMINQUERY: + tcode++; + break; + + case OP_CRRANGE: + case OP_CRMINRANGE: + if (((tcode[1] << 8) + tcode[2]) == 0) tcode += 5; + else try_next = FALSE; + break; + + default: + try_next = FALSE; + break; + } + } + break; /* End of bitmap class handling */ + + } /* End of switch */ + } /* End of try_next loop */ + + code += GET(code, 1); /* Advance to next branch */ + } +while (*code == OP_ALT); +return yield; +} + + + +/************************************************* +* Study a compiled expression * +*************************************************/ + +/* This function is handed a compiled expression that it must study to produce +information that will speed up the matching. It returns a pcre_extra block +which then gets handed back to pcre_exec(). + +Arguments: + re points to the compiled expression + options contains option bits + errorptr points to where to place error messages; + set NULL unless error + +Returns: pointer to a pcre_extra block, with study_data filled in and the + appropriate flag set; + NULL on error or if no optimization possible +*/ + +PCRE_EXP_DEFN pcre_extra * +pcre_study(const pcre *external_re, int options, const char **errorptr) +{ +uschar start_bits[32]; +pcre_extra *extra; +pcre_study_data *study; +const uschar *tables; +uschar *code; +compile_data compile_block; +const real_pcre *re = (const real_pcre *)external_re; + +*errorptr = NULL; + +if (re == NULL || re->magic_number != MAGIC_NUMBER) + { + *errorptr = "argument is not a compiled regular expression"; + return NULL; + } + +if ((options & ~PUBLIC_STUDY_OPTIONS) != 0) + { + *errorptr = "unknown or incorrect option bit(s) set"; + return NULL; + } + +code = (uschar *)re + re->name_table_offset + + (re->name_count * re->name_entry_size); + +/* For an anchored pattern, or an unanchored pattern that has a first char, or +a multiline pattern that matches only at "line starts", no further processing +at present. */ + +if ((re->options & PCRE_ANCHORED) != 0 || + (re->flags & (PCRE_FIRSTSET|PCRE_STARTLINE)) != 0) + return NULL; + +/* Set the character tables in the block that is passed around */ + +tables = re->tables; +if (tables == NULL) + (void)pcre_fullinfo(external_re, NULL, PCRE_INFO_DEFAULT_TABLES, + (void *)(&tables)); + +compile_block.lcc = tables + lcc_offset; +compile_block.fcc = tables + fcc_offset; +compile_block.cbits = tables + cbits_offset; +compile_block.ctypes = tables + ctypes_offset; + +/* See if we can find a fixed set of initial characters for the pattern. */ + +memset(start_bits, 0, 32 * sizeof(uschar)); +if (set_start_bits(code, start_bits, (re->options & PCRE_CASELESS) != 0, + (re->options & PCRE_UTF8) != 0, &compile_block) != SSB_DONE) return NULL; + +/* Get a pcre_extra block and a pcre_study_data block. The study data is put in +the latter, which is pointed to by the former, which may also get additional +data set later by the calling program. At the moment, the size of +pcre_study_data is fixed. We nevertheless save it in a field for returning via +the pcre_fullinfo() function so that if it becomes variable in the future, we +don't have to change that code. */ + +extra = (pcre_extra *)(pcre_malloc) + (sizeof(pcre_extra) + sizeof(pcre_study_data)); + +if (extra == NULL) + { + *errorptr = "failed to get memory"; + return NULL; + } + +study = (pcre_study_data *)((char *)extra + sizeof(pcre_extra)); +extra->flags = PCRE_EXTRA_STUDY_DATA; +extra->study_data = study; + +study->size = sizeof(pcre_study_data); +study->options = PCRE_STUDY_MAPPED; +memcpy(study->start_bits, start_bits, sizeof(start_bits)); + +return extra; +} + +/* End of pcre_study.c */ diff --git a/Engine/lib/pcre/pcre_tables.c b/Engine/lib/pcre/pcre_tables.c new file mode 100644 index 000000000..e37dcb629 --- /dev/null +++ b/Engine/lib/pcre/pcre_tables.c @@ -0,0 +1,318 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2008 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains some fixed tables that are used by more than one of the +PCRE code modules. The tables are also #included by the pcretest program, which +uses macros to change their names from _pcre_xxx to xxxx, thereby avoiding name +clashes with the library. */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + + +/* Table of sizes for the fixed-length opcodes. It's defined in a macro so that +the definition is next to the definition of the opcodes in pcre_internal.h. */ + +const uschar _pcre_OP_lengths[] = { OP_LENGTHS }; + + + +/************************************************* +* Tables for UTF-8 support * +*************************************************/ + +/* These are the breakpoints for different numbers of bytes in a UTF-8 +character. */ + +#ifdef SUPPORT_UTF8 + +const int _pcre_utf8_table1[] = + { 0x7f, 0x7ff, 0xffff, 0x1fffff, 0x3ffffff, 0x7fffffff}; + +const int _pcre_utf8_table1_size = sizeof(_pcre_utf8_table1)/sizeof(int); + +/* These are the indicator bits and the mask for the data bits to set in the +first byte of a character, indexed by the number of additional bytes. */ + +const int _pcre_utf8_table2[] = { 0, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc}; +const int _pcre_utf8_table3[] = { 0xff, 0x1f, 0x0f, 0x07, 0x03, 0x01}; + +/* Table of the number of extra bytes, indexed by the first byte masked with +0x3f. The highest number for a valid UTF-8 first byte is in fact 0x3d. */ + +const uschar _pcre_utf8_table4[] = { + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 }; + +/* The pcre_utt[] table below translates Unicode property names into type and +code values. It is searched by binary chop, so must be in collating sequence of +name. Originally, the table contained pointers to the name strings in the first +field of each entry. However, that leads to a large number of relocations when +a shared library is dynamically loaded. A significant reduction is made by +putting all the names into a single, large string and then using offsets in the +table itself. Maintenance is more error-prone, but frequent changes to this +data is unlikely. */ + +const char _pcre_utt_names[] = + "Any\0" + "Arabic\0" + "Armenian\0" + "Balinese\0" + "Bengali\0" + "Bopomofo\0" + "Braille\0" + "Buginese\0" + "Buhid\0" + "C\0" + "Canadian_Aboriginal\0" + "Cc\0" + "Cf\0" + "Cherokee\0" + "Cn\0" + "Co\0" + "Common\0" + "Coptic\0" + "Cs\0" + "Cuneiform\0" + "Cypriot\0" + "Cyrillic\0" + "Deseret\0" + "Devanagari\0" + "Ethiopic\0" + "Georgian\0" + "Glagolitic\0" + "Gothic\0" + "Greek\0" + "Gujarati\0" + "Gurmukhi\0" + "Han\0" + "Hangul\0" + "Hanunoo\0" + "Hebrew\0" + "Hiragana\0" + "Inherited\0" + "Kannada\0" + "Katakana\0" + "Kharoshthi\0" + "Khmer\0" + "L\0" + "L&\0" + "Lao\0" + "Latin\0" + "Limbu\0" + "Linear_B\0" + "Ll\0" + "Lm\0" + "Lo\0" + "Lt\0" + "Lu\0" + "M\0" + "Malayalam\0" + "Mc\0" + "Me\0" + "Mn\0" + "Mongolian\0" + "Myanmar\0" + "N\0" + "Nd\0" + "New_Tai_Lue\0" + "Nko\0" + "Nl\0" + "No\0" + "Ogham\0" + "Old_Italic\0" + "Old_Persian\0" + "Oriya\0" + "Osmanya\0" + "P\0" + "Pc\0" + "Pd\0" + "Pe\0" + "Pf\0" + "Phags_Pa\0" + "Phoenician\0" + "Pi\0" + "Po\0" + "Ps\0" + "Runic\0" + "S\0" + "Sc\0" + "Shavian\0" + "Sinhala\0" + "Sk\0" + "Sm\0" + "So\0" + "Syloti_Nagri\0" + "Syriac\0" + "Tagalog\0" + "Tagbanwa\0" + "Tai_Le\0" + "Tamil\0" + "Telugu\0" + "Thaana\0" + "Thai\0" + "Tibetan\0" + "Tifinagh\0" + "Ugaritic\0" + "Yi\0" + "Z\0" + "Zl\0" + "Zp\0" + "Zs\0"; + +const ucp_type_table _pcre_utt[] = { + { 0, PT_ANY, 0 }, + { 4, PT_SC, ucp_Arabic }, + { 11, PT_SC, ucp_Armenian }, + { 20, PT_SC, ucp_Balinese }, + { 29, PT_SC, ucp_Bengali }, + { 37, PT_SC, ucp_Bopomofo }, + { 46, PT_SC, ucp_Braille }, + { 54, PT_SC, ucp_Buginese }, + { 63, PT_SC, ucp_Buhid }, + { 69, PT_GC, ucp_C }, + { 71, PT_SC, ucp_Canadian_Aboriginal }, + { 91, PT_PC, ucp_Cc }, + { 94, PT_PC, ucp_Cf }, + { 97, PT_SC, ucp_Cherokee }, + { 106, PT_PC, ucp_Cn }, + { 109, PT_PC, ucp_Co }, + { 112, PT_SC, ucp_Common }, + { 119, PT_SC, ucp_Coptic }, + { 126, PT_PC, ucp_Cs }, + { 129, PT_SC, ucp_Cuneiform }, + { 139, PT_SC, ucp_Cypriot }, + { 147, PT_SC, ucp_Cyrillic }, + { 156, PT_SC, ucp_Deseret }, + { 164, PT_SC, ucp_Devanagari }, + { 175, PT_SC, ucp_Ethiopic }, + { 184, PT_SC, ucp_Georgian }, + { 193, PT_SC, ucp_Glagolitic }, + { 204, PT_SC, ucp_Gothic }, + { 211, PT_SC, ucp_Greek }, + { 217, PT_SC, ucp_Gujarati }, + { 226, PT_SC, ucp_Gurmukhi }, + { 235, PT_SC, ucp_Han }, + { 239, PT_SC, ucp_Hangul }, + { 246, PT_SC, ucp_Hanunoo }, + { 254, PT_SC, ucp_Hebrew }, + { 261, PT_SC, ucp_Hiragana }, + { 270, PT_SC, ucp_Inherited }, + { 280, PT_SC, ucp_Kannada }, + { 288, PT_SC, ucp_Katakana }, + { 297, PT_SC, ucp_Kharoshthi }, + { 308, PT_SC, ucp_Khmer }, + { 314, PT_GC, ucp_L }, + { 316, PT_LAMP, 0 }, + { 319, PT_SC, ucp_Lao }, + { 323, PT_SC, ucp_Latin }, + { 329, PT_SC, ucp_Limbu }, + { 335, PT_SC, ucp_Linear_B }, + { 344, PT_PC, ucp_Ll }, + { 347, PT_PC, ucp_Lm }, + { 350, PT_PC, ucp_Lo }, + { 353, PT_PC, ucp_Lt }, + { 356, PT_PC, ucp_Lu }, + { 359, PT_GC, ucp_M }, + { 361, PT_SC, ucp_Malayalam }, + { 371, PT_PC, ucp_Mc }, + { 374, PT_PC, ucp_Me }, + { 377, PT_PC, ucp_Mn }, + { 380, PT_SC, ucp_Mongolian }, + { 390, PT_SC, ucp_Myanmar }, + { 398, PT_GC, ucp_N }, + { 400, PT_PC, ucp_Nd }, + { 403, PT_SC, ucp_New_Tai_Lue }, + { 415, PT_SC, ucp_Nko }, + { 419, PT_PC, ucp_Nl }, + { 422, PT_PC, ucp_No }, + { 425, PT_SC, ucp_Ogham }, + { 431, PT_SC, ucp_Old_Italic }, + { 442, PT_SC, ucp_Old_Persian }, + { 454, PT_SC, ucp_Oriya }, + { 460, PT_SC, ucp_Osmanya }, + { 468, PT_GC, ucp_P }, + { 470, PT_PC, ucp_Pc }, + { 473, PT_PC, ucp_Pd }, + { 476, PT_PC, ucp_Pe }, + { 479, PT_PC, ucp_Pf }, + { 482, PT_SC, ucp_Phags_Pa }, + { 491, PT_SC, ucp_Phoenician }, + { 502, PT_PC, ucp_Pi }, + { 505, PT_PC, ucp_Po }, + { 508, PT_PC, ucp_Ps }, + { 511, PT_SC, ucp_Runic }, + { 517, PT_GC, ucp_S }, + { 519, PT_PC, ucp_Sc }, + { 522, PT_SC, ucp_Shavian }, + { 530, PT_SC, ucp_Sinhala }, + { 538, PT_PC, ucp_Sk }, + { 541, PT_PC, ucp_Sm }, + { 544, PT_PC, ucp_So }, + { 547, PT_SC, ucp_Syloti_Nagri }, + { 560, PT_SC, ucp_Syriac }, + { 567, PT_SC, ucp_Tagalog }, + { 575, PT_SC, ucp_Tagbanwa }, + { 584, PT_SC, ucp_Tai_Le }, + { 591, PT_SC, ucp_Tamil }, + { 597, PT_SC, ucp_Telugu }, + { 604, PT_SC, ucp_Thaana }, + { 611, PT_SC, ucp_Thai }, + { 616, PT_SC, ucp_Tibetan }, + { 624, PT_SC, ucp_Tifinagh }, + { 633, PT_SC, ucp_Ugaritic }, + { 642, PT_SC, ucp_Yi }, + { 645, PT_GC, ucp_Z }, + { 647, PT_PC, ucp_Zl }, + { 650, PT_PC, ucp_Zp }, + { 653, PT_PC, ucp_Zs } +}; + +const int _pcre_utt_size = sizeof(_pcre_utt)/sizeof(ucp_type_table); + +#endif /* SUPPORT_UTF8 */ + +/* End of pcre_tables.c */ diff --git a/Engine/lib/pcre/pcre_try_flipped.c b/Engine/lib/pcre/pcre_try_flipped.c new file mode 100644 index 000000000..0d2f3a2d3 --- /dev/null +++ b/Engine/lib/pcre/pcre_try_flipped.c @@ -0,0 +1,137 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2008 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains an internal function that tests a compiled pattern to +see if it was compiled with the opposite endianness. If so, it uses an +auxiliary local function to flip the appropriate bytes. */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + + +/************************************************* +* Flip bytes in an integer * +*************************************************/ + +/* This function is called when the magic number in a regex doesn't match, in +order to flip its bytes to see if we are dealing with a pattern that was +compiled on a host of different endianness. If so, this function is used to +flip other byte values. + +Arguments: + value the number to flip + n the number of bytes to flip (assumed to be 2 or 4) + +Returns: the flipped value +*/ + +static unsigned long int +byteflip(unsigned long int value, int n) +{ +if (n == 2) return ((value & 0x00ff) << 8) | ((value & 0xff00) >> 8); +return ((value & 0x000000ff) << 24) | + ((value & 0x0000ff00) << 8) | + ((value & 0x00ff0000) >> 8) | + ((value & 0xff000000) >> 24); +} + + + +/************************************************* +* Test for a byte-flipped compiled regex * +*************************************************/ + +/* This function is called from pcre_exec(), pcre_dfa_exec(), and also from +pcre_fullinfo(). Its job is to test whether the regex is byte-flipped - that +is, it was compiled on a system of opposite endianness. The function is called +only when the native MAGIC_NUMBER test fails. If the regex is indeed flipped, +we flip all the relevant values into a different data block, and return it. + +Arguments: + re points to the regex + study points to study data, or NULL + internal_re points to a new regex block + internal_study points to a new study block + +Returns: the new block if is is indeed a byte-flipped regex + NULL if it is not +*/ + +real_pcre * +_pcre_try_flipped(const real_pcre *re, real_pcre *internal_re, + const pcre_study_data *study, pcre_study_data *internal_study) +{ +if (byteflip(re->magic_number, sizeof(re->magic_number)) != MAGIC_NUMBER) + return NULL; + +*internal_re = *re; /* To copy other fields */ +internal_re->size = byteflip(re->size, sizeof(re->size)); +internal_re->options = byteflip(re->options, sizeof(re->options)); +internal_re->flags = (pcre_uint16)byteflip(re->flags, sizeof(re->flags)); +internal_re->top_bracket = + (pcre_uint16)byteflip(re->top_bracket, sizeof(re->top_bracket)); +internal_re->top_backref = + (pcre_uint16)byteflip(re->top_backref, sizeof(re->top_backref)); +internal_re->first_byte = + (pcre_uint16)byteflip(re->first_byte, sizeof(re->first_byte)); +internal_re->req_byte = + (pcre_uint16)byteflip(re->req_byte, sizeof(re->req_byte)); +internal_re->name_table_offset = + (pcre_uint16)byteflip(re->name_table_offset, sizeof(re->name_table_offset)); +internal_re->name_entry_size = + (pcre_uint16)byteflip(re->name_entry_size, sizeof(re->name_entry_size)); +internal_re->name_count = + (pcre_uint16)byteflip(re->name_count, sizeof(re->name_count)); + +if (study != NULL) + { + *internal_study = *study; /* To copy other fields */ + internal_study->size = byteflip(study->size, sizeof(study->size)); + internal_study->options = byteflip(study->options, sizeof(study->options)); + } + +return internal_re; +} + +/* End of pcre_tryflipped.c */ diff --git a/Engine/lib/pcre/pcre_ucp_searchfuncs.c b/Engine/lib/pcre/pcre_ucp_searchfuncs.c new file mode 100644 index 000000000..461ca4377 --- /dev/null +++ b/Engine/lib/pcre/pcre_ucp_searchfuncs.c @@ -0,0 +1,179 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2008 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains code for searching the table of Unicode character +properties. */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + +#include "ucp.h" /* Category definitions */ +#include "ucpinternal.h" /* Internal table details */ +#include "ucptable.h" /* The table itself */ + + +/* Table to translate from particular type value to the general value. */ + +static const int ucp_gentype[] = { + ucp_C, ucp_C, ucp_C, ucp_C, ucp_C, /* Cc, Cf, Cn, Co, Cs */ + ucp_L, ucp_L, ucp_L, ucp_L, ucp_L, /* Ll, Lu, Lm, Lo, Lt */ + ucp_M, ucp_M, ucp_M, /* Mc, Me, Mn */ + ucp_N, ucp_N, ucp_N, /* Nd, Nl, No */ + ucp_P, ucp_P, ucp_P, ucp_P, ucp_P, /* Pc, Pd, Pe, Pf, Pi */ + ucp_P, ucp_P, /* Ps, Po */ + ucp_S, ucp_S, ucp_S, ucp_S, /* Sc, Sk, Sm, So */ + ucp_Z, ucp_Z, ucp_Z /* Zl, Zp, Zs */ +}; + + + +/************************************************* +* Search table and return type * +*************************************************/ + +/* Three values are returned: the category is ucp_C, ucp_L, etc. The detailed +character type is ucp_Lu, ucp_Nd, etc. The script is ucp_Latin, etc. + +Arguments: + c the character value + type_ptr the detailed character type is returned here + script_ptr the script is returned here + +Returns: the character type category +*/ + +int +_pcre_ucp_findprop(const unsigned int c, int *type_ptr, int *script_ptr) +{ +int bot = 0; +int top = sizeof(ucp_table)/sizeof(cnode); +int mid; + +/* The table is searched using a binary chop. You might think that using +intermediate variables to hold some of the common expressions would speed +things up, but tests with gcc 3.4.4 on Linux showed that, on the contrary, it +makes things a lot slower. */ + +for (;;) + { + if (top <= bot) + { + *type_ptr = ucp_Cn; + *script_ptr = ucp_Common; + return ucp_C; + } + mid = (bot + top) >> 1; + if (c == (ucp_table[mid].f0 & f0_charmask)) break; + if (c < (ucp_table[mid].f0 & f0_charmask)) top = mid; + else + { + if ((ucp_table[mid].f0 & f0_rangeflag) != 0 && + c <= (ucp_table[mid].f0 & f0_charmask) + + (ucp_table[mid].f1 & f1_rangemask)) break; + bot = mid + 1; + } + } + +/* Found an entry in the table. Set the script and detailed type values, and +return the general type. */ + +*script_ptr = (ucp_table[mid].f0 & f0_scriptmask) >> f0_scriptshift; +*type_ptr = (ucp_table[mid].f1 & f1_typemask) >> f1_typeshift; + +return ucp_gentype[*type_ptr]; +} + + + +/************************************************* +* Search table and return other case * +*************************************************/ + +/* If the given character is a letter, and there is another case for the +letter, return the other case. Otherwise, return -1. + +Arguments: + c the character value + +Returns: the other case or NOTACHAR if none +*/ + +unsigned int +_pcre_ucp_othercase(const unsigned int c) +{ +int bot = 0; +int top = sizeof(ucp_table)/sizeof(cnode); +int mid, offset; + +/* The table is searched using a binary chop. You might think that using +intermediate variables to hold some of the common expressions would speed +things up, but tests with gcc 3.4.4 on Linux showed that, on the contrary, it +makes things a lot slower. */ + +for (;;) + { + if (top <= bot) return -1; + mid = (bot + top) >> 1; + if (c == (ucp_table[mid].f0 & f0_charmask)) break; + if (c < (ucp_table[mid].f0 & f0_charmask)) top = mid; + else + { + if ((ucp_table[mid].f0 & f0_rangeflag) != 0 && + c <= (ucp_table[mid].f0 & f0_charmask) + + (ucp_table[mid].f1 & f1_rangemask)) break; + bot = mid + 1; + } + } + +/* Found an entry in the table. Return NOTACHAR for a range entry. Otherwise +return the other case if there is one, else NOTACHAR. */ + +if ((ucp_table[mid].f0 & f0_rangeflag) != 0) return NOTACHAR; + +offset = ucp_table[mid].f1 & f1_casemask; +if ((offset & f1_caseneg) != 0) offset |= f1_caseneg; +return (offset == 0)? NOTACHAR : c + offset; +} + + +/* End of pcre_ucp_searchfuncs.c */ diff --git a/Engine/lib/pcre/pcre_valid_utf8.c b/Engine/lib/pcre/pcre_valid_utf8.c new file mode 100644 index 000000000..bc3bc8f3d --- /dev/null +++ b/Engine/lib/pcre/pcre_valid_utf8.c @@ -0,0 +1,162 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2008 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains an internal function for validating UTF-8 character +strings. */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + + +/************************************************* +* Validate a UTF-8 string * +*************************************************/ + +/* This function is called (optionally) at the start of compile or match, to +validate that a supposed UTF-8 string is actually valid. The early check means +that subsequent code can assume it is dealing with a valid string. The check +can be turned off for maximum performance, but the consequences of supplying +an invalid string are then undefined. + +Originally, this function checked according to RFC 2279, allowing for values in +the range 0 to 0x7fffffff, up to 6 bytes long, but ensuring that they were in +the canonical format. Once somebody had pointed out RFC 3629 to me (it +obsoletes 2279), additional restrictions were applied. The values are now +limited to be between 0 and 0x0010ffff, no more than 4 bytes long, and the +subrange 0xd000 to 0xdfff is excluded. + +Arguments: + string points to the string + length length of string, or -1 if the string is zero-terminated + +Returns: < 0 if the string is a valid UTF-8 string + >= 0 otherwise; the value is the offset of the bad byte +*/ + +int +_pcre_valid_utf8(const uschar *string, int length) +{ +#ifdef SUPPORT_UTF8 +register const uschar *p; + +if (length < 0) + { + for (p = string; *p != 0; p++); + length = p - string; + } + +for (p = string; length-- > 0; p++) + { + register int ab; + register int c = *p; + if (c < 128) continue; + if (c < 0xc0) return p - string; + ab = _pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes */ + if (length < ab || ab > 3) return p - string; + length -= ab; + + /* Check top bits in the second byte */ + if ((*(++p) & 0xc0) != 0x80) return p - string; + + /* Check for overlong sequences for each different length, and for the + excluded range 0xd000 to 0xdfff. */ + + switch (ab) + { + /* Check for xx00 000x (overlong sequence) */ + + case 1: + if ((c & 0x3e) == 0) return p - string; + continue; /* We know there aren't any more bytes to check */ + + /* Check for 1110 0000, xx0x xxxx (overlong sequence) or + 1110 1101, 1010 xxxx (0xd000 - 0xdfff) */ + + case 2: + if ((c == 0xe0 && (*p & 0x20) == 0) || + (c == 0xed && *p >= 0xa0)) + return p - string; + break; + + /* Check for 1111 0000, xx00 xxxx (overlong sequence) or + greater than 0x0010ffff (f4 8f bf bf) */ + + case 3: + if ((c == 0xf0 && (*p & 0x30) == 0) || + (c > 0xf4 ) || + (c == 0xf4 && *p > 0x8f)) + return p - string; + break; + +#if 0 + /* These cases can no longer occur, as we restrict to a maximum of four + bytes nowadays. Leave the code here in case we ever want to add an option + for longer sequences. */ + + /* Check for 1111 1000, xx00 0xxx */ + case 4: + if (c == 0xf8 && (*p & 0x38) == 0) return p - string; + break; + + /* Check for leading 0xfe or 0xff, and then for 1111 1100, xx00 00xx */ + case 5: + if (c == 0xfe || c == 0xff || + (c == 0xfc && (*p & 0x3c) == 0)) return p - string; + break; +#endif + + } + + /* Check for valid bytes after the 2nd, if any; all must start 10 */ + while (--ab > 0) + { + if ((*(++p) & 0xc0) != 0x80) return p - string; + } + } +#endif + +return -1; +} + +/* End of pcre_valid_utf8.c */ diff --git a/Engine/lib/pcre/pcre_version.c b/Engine/lib/pcre/pcre_version.c new file mode 100644 index 000000000..697b44b39 --- /dev/null +++ b/Engine/lib/pcre/pcre_version.c @@ -0,0 +1,90 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2008 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains the external function pcre_version(), which returns a +string that identifies the PCRE version that is in use. */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + + +/************************************************* +* Return version string * +*************************************************/ + +/* These macros are the standard way of turning unquoted text into C strings. +They allow macros like PCRE_MAJOR to be defined without quotes, which is +convenient for user programs that want to test its value. */ + +#define STRING(a) # a +#define XSTRING(s) STRING(s) + +/* A problem turned up with PCRE_PRERELEASE, which is defined empty for +production releases. Originally, it was used naively in this code: + + return XSTRING(PCRE_MAJOR) + "." XSTRING(PCRE_MINOR) + XSTRING(PCRE_PRERELEASE) + " " XSTRING(PCRE_DATE); + +However, when PCRE_PRERELEASE is empty, this leads to an attempted expansion of +STRING(). The C standard states: "If (before argument substitution) any +argument consists of no preprocessing tokens, the behavior is undefined." It +turns out the gcc treats this case as a single empty string - which is what we +really want - but Visual C grumbles about the lack of an argument for the +macro. Unfortunately, both are within their rights. To cope with both ways of +handling this, I had resort to some messy hackery that does a test at run time. +I could find no way of detecting that a macro is defined as an empty string at +pre-processor time. This hack uses a standard trick for avoiding calling +the STRING macro with an empty argument when doing the test. */ + +PCRE_EXP_DEFN const char * +pcre_version(void) +{ +return (XSTRING(Z PCRE_PRERELEASE)[1] == 0)? + XSTRING(PCRE_MAJOR.PCRE_MINOR PCRE_DATE) : + XSTRING(PCRE_MAJOR.PCRE_MINOR) XSTRING(PCRE_PRERELEASE PCRE_DATE); +} + +/* End of pcre_version.c */ diff --git a/Engine/lib/pcre/pcre_xclass.c b/Engine/lib/pcre/pcre_xclass.c new file mode 100644 index 000000000..cfb5a77b5 --- /dev/null +++ b/Engine/lib/pcre/pcre_xclass.c @@ -0,0 +1,148 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* PCRE is a library of functions to support regular expressions whose syntax +and semantics are as close as possible to those of the Perl 5 language. + + Written by Philip Hazel + Copyright (c) 1997-2008 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +/* This module contains an internal function that is used to match an extended +class (one that contains characters whose values are > 255). It is used by both +pcre_exec() and pcre_def_exec(). */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "pcre_internal.h" + + +/************************************************* +* Match character against an XCLASS * +*************************************************/ + +/* This function is called to match a character against an extended class that +might contain values > 255. + +Arguments: + c the character + data points to the flag byte of the XCLASS data + +Returns: TRUE if character matches, else FALSE +*/ + +BOOL +_pcre_xclass(int c, const uschar *data) +{ +int t; +BOOL negated = (*data & XCL_NOT) != 0; + +/* Character values < 256 are matched against a bitmap, if one is present. If +not, we still carry on, because there may be ranges that start below 256 in the +additional data. */ + +if (c < 256) + { + if ((*data & XCL_MAP) != 0 && (data[1 + c/8] & (1 << (c&7))) != 0) + return !negated; /* char found */ + } + +/* First skip the bit map if present. Then match against the list of Unicode +properties or large chars or ranges that end with a large char. We won't ever +encounter XCL_PROP or XCL_NOTPROP when UCP support is not compiled. */ + +if ((*data++ & XCL_MAP) != 0) data += 32; + +while ((t = *data++) != XCL_END) + { + int x, y; + if (t == XCL_SINGLE) + { + GETCHARINC(x, data); + if (c == x) return !negated; + } + else if (t == XCL_RANGE) + { + GETCHARINC(x, data); + GETCHARINC(y, data); + if (c >= x && c <= y) return !negated; + } + +#ifdef SUPPORT_UCP + else /* XCL_PROP & XCL_NOTPROP */ + { + int chartype, script; + int category = _pcre_ucp_findprop(c, &chartype, &script); + + switch(*data) + { + case PT_ANY: + if (t == XCL_PROP) return !negated; + break; + + case PT_LAMP: + if ((chartype == ucp_Lu || chartype == ucp_Ll || chartype == ucp_Lt) == + (t == XCL_PROP)) return !negated; + break; + + case PT_GC: + if ((data[1] == category) == (t == XCL_PROP)) return !negated; + break; + + case PT_PC: + if ((data[1] == chartype) == (t == XCL_PROP)) return !negated; + break; + + case PT_SC: + if ((data[1] == script) == (t == XCL_PROP)) return !negated; + break; + + /* This should never occur, but compilers may mutter if there is no + default. */ + + default: + return FALSE; + } + + data += 2; + } +#endif /* SUPPORT_UCP */ + } + +return negated; /* char did not match */ +} + +/* End of pcre_xclass.c */ diff --git a/Engine/lib/pcre/pcrecpp.cc b/Engine/lib/pcre/pcrecpp.cc new file mode 100644 index 000000000..0e2171851 --- /dev/null +++ b/Engine/lib/pcre/pcrecpp.cc @@ -0,0 +1,871 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Sanjay Ghemawat + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include /* for SHRT_MIN, USHRT_MAX, etc */ +#include +#include +#include +#include + +#include "pcrecpp_internal.h" +#include "pcre.h" +#include "pcrecpp.h" +#include "pcre_stringpiece.h" + + +namespace pcrecpp { + +// Maximum number of args we can set +static const int kMaxArgs = 16; +static const int kVecSize = (1 + kMaxArgs) * 3; // results + PCRE workspace + +// Special object that stands-in for no argument +Arg RE::no_arg((void*)NULL); + +// If a regular expression has no error, its error_ field points here +static const string empty_string; + +// If the user doesn't ask for any options, we just use this one +static RE_Options default_options; + +void RE::Init(const string& pat, const RE_Options* options) { + pattern_ = pat; + if (options == NULL) { + options_ = default_options; + } else { + options_ = *options; + } + error_ = &empty_string; + re_full_ = NULL; + re_partial_ = NULL; + + re_partial_ = Compile(UNANCHORED); + if (re_partial_ != NULL) { + re_full_ = Compile(ANCHOR_BOTH); + } +} + +void RE::Cleanup() { + if (re_full_ != NULL) (*pcre_free)(re_full_); + if (re_partial_ != NULL) (*pcre_free)(re_partial_); + if (error_ != &empty_string) delete error_; +} + + +RE::~RE() { + Cleanup(); +} + + +pcre* RE::Compile(Anchor anchor) { + // First, convert RE_Options into pcre options + int pcre_options = 0; + pcre_options = options_.all_options(); + + // Special treatment for anchoring. This is needed because at + // runtime pcre only provides an option for anchoring at the + // beginning of a string (unless you use offset). + // + // There are three types of anchoring we want: + // UNANCHORED Compile the original pattern, and use + // a pcre unanchored match. + // ANCHOR_START Compile the original pattern, and use + // a pcre anchored match. + // ANCHOR_BOTH Tack a "\z" to the end of the original pattern + // and use a pcre anchored match. + + const char* compile_error; + int eoffset; + pcre* re; + if (anchor != ANCHOR_BOTH) { + re = pcre_compile(pattern_.c_str(), pcre_options, + &compile_error, &eoffset, NULL); + } else { + // Tack a '\z' at the end of RE. Parenthesize it first so that + // the '\z' applies to all top-level alternatives in the regexp. + string wrapped = "(?:"; // A non-counting grouping operator + wrapped += pattern_; + wrapped += ")\\z"; + re = pcre_compile(wrapped.c_str(), pcre_options, + &compile_error, &eoffset, NULL); + } + if (re == NULL) { + if (error_ == &empty_string) error_ = new string(compile_error); + } + return re; +} + +/***** Matching interfaces *****/ + +bool RE::FullMatch(const StringPiece& text, + const Arg& ptr1, + const Arg& ptr2, + const Arg& ptr3, + const Arg& ptr4, + const Arg& ptr5, + const Arg& ptr6, + const Arg& ptr7, + const Arg& ptr8, + const Arg& ptr9, + const Arg& ptr10, + const Arg& ptr11, + const Arg& ptr12, + const Arg& ptr13, + const Arg& ptr14, + const Arg& ptr15, + const Arg& ptr16) const { + const Arg* args[kMaxArgs]; + int n = 0; + if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1; + if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2; + if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3; + if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4; + if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5; + if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6; + if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7; + if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8; + if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9; + if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10; + if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11; + if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12; + if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13; + if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14; + if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15; + if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16; + done: + + int consumed; + int vec[kVecSize]; + return DoMatchImpl(text, ANCHOR_BOTH, &consumed, args, n, vec, kVecSize); +} + +bool RE::PartialMatch(const StringPiece& text, + const Arg& ptr1, + const Arg& ptr2, + const Arg& ptr3, + const Arg& ptr4, + const Arg& ptr5, + const Arg& ptr6, + const Arg& ptr7, + const Arg& ptr8, + const Arg& ptr9, + const Arg& ptr10, + const Arg& ptr11, + const Arg& ptr12, + const Arg& ptr13, + const Arg& ptr14, + const Arg& ptr15, + const Arg& ptr16) const { + const Arg* args[kMaxArgs]; + int n = 0; + if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1; + if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2; + if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3; + if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4; + if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5; + if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6; + if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7; + if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8; + if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9; + if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10; + if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11; + if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12; + if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13; + if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14; + if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15; + if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16; + done: + + int consumed; + int vec[kVecSize]; + return DoMatchImpl(text, UNANCHORED, &consumed, args, n, vec, kVecSize); +} + +bool RE::Consume(StringPiece* input, + const Arg& ptr1, + const Arg& ptr2, + const Arg& ptr3, + const Arg& ptr4, + const Arg& ptr5, + const Arg& ptr6, + const Arg& ptr7, + const Arg& ptr8, + const Arg& ptr9, + const Arg& ptr10, + const Arg& ptr11, + const Arg& ptr12, + const Arg& ptr13, + const Arg& ptr14, + const Arg& ptr15, + const Arg& ptr16) const { + const Arg* args[kMaxArgs]; + int n = 0; + if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1; + if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2; + if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3; + if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4; + if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5; + if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6; + if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7; + if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8; + if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9; + if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10; + if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11; + if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12; + if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13; + if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14; + if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15; + if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16; + done: + + int consumed; + int vec[kVecSize]; + if (DoMatchImpl(*input, ANCHOR_START, &consumed, + args, n, vec, kVecSize)) { + input->remove_prefix(consumed); + return true; + } else { + return false; + } +} + +bool RE::FindAndConsume(StringPiece* input, + const Arg& ptr1, + const Arg& ptr2, + const Arg& ptr3, + const Arg& ptr4, + const Arg& ptr5, + const Arg& ptr6, + const Arg& ptr7, + const Arg& ptr8, + const Arg& ptr9, + const Arg& ptr10, + const Arg& ptr11, + const Arg& ptr12, + const Arg& ptr13, + const Arg& ptr14, + const Arg& ptr15, + const Arg& ptr16) const { + const Arg* args[kMaxArgs]; + int n = 0; + if (&ptr1 == &no_arg) goto done; args[n++] = &ptr1; + if (&ptr2 == &no_arg) goto done; args[n++] = &ptr2; + if (&ptr3 == &no_arg) goto done; args[n++] = &ptr3; + if (&ptr4 == &no_arg) goto done; args[n++] = &ptr4; + if (&ptr5 == &no_arg) goto done; args[n++] = &ptr5; + if (&ptr6 == &no_arg) goto done; args[n++] = &ptr6; + if (&ptr7 == &no_arg) goto done; args[n++] = &ptr7; + if (&ptr8 == &no_arg) goto done; args[n++] = &ptr8; + if (&ptr9 == &no_arg) goto done; args[n++] = &ptr9; + if (&ptr10 == &no_arg) goto done; args[n++] = &ptr10; + if (&ptr11 == &no_arg) goto done; args[n++] = &ptr11; + if (&ptr12 == &no_arg) goto done; args[n++] = &ptr12; + if (&ptr13 == &no_arg) goto done; args[n++] = &ptr13; + if (&ptr14 == &no_arg) goto done; args[n++] = &ptr14; + if (&ptr15 == &no_arg) goto done; args[n++] = &ptr15; + if (&ptr16 == &no_arg) goto done; args[n++] = &ptr16; + done: + + int consumed; + int vec[kVecSize]; + if (DoMatchImpl(*input, UNANCHORED, &consumed, + args, n, vec, kVecSize)) { + input->remove_prefix(consumed); + return true; + } else { + return false; + } +} + +bool RE::Replace(const StringPiece& rewrite, + string *str) const { + int vec[kVecSize]; + int matches = TryMatch(*str, 0, UNANCHORED, vec, kVecSize); + if (matches == 0) + return false; + + string s; + if (!Rewrite(&s, rewrite, *str, vec, matches)) + return false; + + assert(vec[0] >= 0); + assert(vec[1] >= 0); + str->replace(vec[0], vec[1] - vec[0], s); + return true; +} + +// Returns PCRE_NEWLINE_CRLF, PCRE_NEWLINE_CR, or PCRE_NEWLINE_LF. +// Note that PCRE_NEWLINE_CRLF is defined to be P_N_CR | P_N_LF. +// Modified by PH to add PCRE_NEWLINE_ANY and PCRE_NEWLINE_ANYCRLF. + +static int NewlineMode(int pcre_options) { + // TODO: if we can make it threadsafe, cache this var + int newline_mode = 0; + /* if (newline_mode) return newline_mode; */ // do this once it's cached + if (pcre_options & (PCRE_NEWLINE_CRLF|PCRE_NEWLINE_CR|PCRE_NEWLINE_LF| + PCRE_NEWLINE_ANY|PCRE_NEWLINE_ANYCRLF)) { + newline_mode = (pcre_options & + (PCRE_NEWLINE_CRLF|PCRE_NEWLINE_CR|PCRE_NEWLINE_LF| + PCRE_NEWLINE_ANY|PCRE_NEWLINE_ANYCRLF)); + } else { + int newline; + pcre_config(PCRE_CONFIG_NEWLINE, &newline); + if (newline == 10) + newline_mode = PCRE_NEWLINE_LF; + else if (newline == 13) + newline_mode = PCRE_NEWLINE_CR; + else if (newline == 3338) + newline_mode = PCRE_NEWLINE_CRLF; + else if (newline == -1) + newline_mode = PCRE_NEWLINE_ANY; + else if (newline == -2) + newline_mode = PCRE_NEWLINE_ANYCRLF; + else + assert("" == "Unexpected return value from pcre_config(NEWLINE)"); + } + return newline_mode; +} + +int RE::GlobalReplace(const StringPiece& rewrite, + string *str) const { + int count = 0; + int vec[kVecSize]; + string out; + int start = 0; + int lastend = -1; + + while (start <= static_cast(str->length())) { + int matches = TryMatch(*str, start, UNANCHORED, vec, kVecSize); + if (matches <= 0) + break; + int matchstart = vec[0], matchend = vec[1]; + assert(matchstart >= start); + assert(matchend >= matchstart); + if (matchstart == matchend && matchstart == lastend) { + // advance one character if we matched an empty string at the same + // place as the last match occurred + matchend = start + 1; + // If the current char is CR and we're in CRLF mode, skip LF too. + // Note it's better to call pcre_fullinfo() than to examine + // all_options(), since options_ could have changed bewteen + // compile-time and now, but this is simpler and safe enough. + // Modified by PH to add ANY and ANYCRLF. + if (start+1 < static_cast(str->length()) && + (*str)[start] == '\r' && (*str)[start+1] == '\n' && + (NewlineMode(options_.all_options()) == PCRE_NEWLINE_CRLF || + NewlineMode(options_.all_options()) == PCRE_NEWLINE_ANY || + NewlineMode(options_.all_options()) == PCRE_NEWLINE_ANYCRLF) + ) { + matchend++; + } + // We also need to advance more than one char if we're in utf8 mode. +#ifdef SUPPORT_UTF8 + if (options_.utf8()) { + while (matchend < static_cast(str->length()) && + ((*str)[matchend] & 0xc0) == 0x80) + matchend++; + } +#endif + if (matchend <= static_cast(str->length())) + out.append(*str, start, matchend - start); + start = matchend; + } else { + out.append(*str, start, matchstart - start); + Rewrite(&out, rewrite, *str, vec, matches); + start = matchend; + lastend = matchend; + count++; + } + } + + if (count == 0) + return 0; + + if (start < static_cast(str->length())) + out.append(*str, start, str->length() - start); + swap(out, *str); + return count; +} + +bool RE::Extract(const StringPiece& rewrite, + const StringPiece& text, + string *out) const { + int vec[kVecSize]; + int matches = TryMatch(text, 0, UNANCHORED, vec, kVecSize); + if (matches == 0) + return false; + out->erase(); + return Rewrite(out, rewrite, text, vec, matches); +} + +/*static*/ string RE::QuoteMeta(const StringPiece& unquoted) { + string result; + + // Escape any ascii character not in [A-Za-z_0-9]. + // + // Note that it's legal to escape a character even if it has no + // special meaning in a regular expression -- so this function does + // that. (This also makes it identical to the perl function of the + // same name; see `perldoc -f quotemeta`.) + for (int ii = 0; ii < unquoted.size(); ++ii) { + // Note that using 'isalnum' here raises the benchmark time from + // 32ns to 58ns: + if ((unquoted[ii] < 'a' || unquoted[ii] > 'z') && + (unquoted[ii] < 'A' || unquoted[ii] > 'Z') && + (unquoted[ii] < '0' || unquoted[ii] > '9') && + unquoted[ii] != '_' && + // If this is the part of a UTF8 or Latin1 character, we need + // to copy this byte without escaping. Experimentally this is + // what works correctly with the regexp library. + !(unquoted[ii] & 128)) { + result += '\\'; + } + result += unquoted[ii]; + } + + return result; +} + +/***** Actual matching and rewriting code *****/ + +int RE::TryMatch(const StringPiece& text, + int startpos, + Anchor anchor, + int *vec, + int vecsize) const { + pcre* re = (anchor == ANCHOR_BOTH) ? re_full_ : re_partial_; + if (re == NULL) { + //fprintf(stderr, "Matching against invalid re: %s\n", error_->c_str()); + return 0; + } + + pcre_extra extra = { 0, 0, 0, 0, 0, 0 }; + if (options_.match_limit() > 0) { + extra.flags |= PCRE_EXTRA_MATCH_LIMIT; + extra.match_limit = options_.match_limit(); + } + if (options_.match_limit_recursion() > 0) { + extra.flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION; + extra.match_limit_recursion = options_.match_limit_recursion(); + } + int rc = pcre_exec(re, // The regular expression object + &extra, + (text.data() == NULL) ? "" : text.data(), + text.size(), + startpos, + (anchor == UNANCHORED) ? 0 : PCRE_ANCHORED, + vec, + vecsize); + + // Handle errors + if (rc == PCRE_ERROR_NOMATCH) { + return 0; + } else if (rc < 0) { + //fprintf(stderr, "Unexpected return code: %d when matching '%s'\n", + // re, pattern_.c_str()); + return 0; + } else if (rc == 0) { + // pcre_exec() returns 0 as a special case when the number of + // capturing subpatterns exceeds the size of the vector. + // When this happens, there is a match and the output vector + // is filled, but we miss out on the positions of the extra subpatterns. + rc = vecsize / 2; + } + + return rc; +} + +bool RE::DoMatchImpl(const StringPiece& text, + Anchor anchor, + int* consumed, + const Arg* const* args, + int n, + int* vec, + int vecsize) const { + assert((1 + n) * 3 <= vecsize); // results + PCRE workspace + int matches = TryMatch(text, 0, anchor, vec, vecsize); + assert(matches >= 0); // TryMatch never returns negatives + if (matches == 0) + return false; + + *consumed = vec[1]; + + if (n == 0 || args == NULL) { + // We are not interested in results + return true; + } + + if (NumberOfCapturingGroups() < n) { + // RE has fewer capturing groups than number of arg pointers passed in + return false; + } + + // If we got here, we must have matched the whole pattern. + // We do not need (can not do) any more checks on the value of 'matches' here + // -- see the comment for TryMatch. + for (int i = 0; i < n; i++) { + const int start = vec[2*(i+1)]; + const int limit = vec[2*(i+1)+1]; + if (!args[i]->Parse(text.data() + start, limit-start)) { + // TODO: Should we indicate what the error was? + return false; + } + } + + return true; +} + +bool RE::DoMatch(const StringPiece& text, + Anchor anchor, + int* consumed, + const Arg* const args[], + int n) const { + assert(n >= 0); + size_t const vecsize = (1 + n) * 3; // results + PCRE workspace + // (as for kVecSize) + int space[21]; // use stack allocation for small vecsize (common case) + int* vec = vecsize <= 21 ? space : new int[vecsize]; + bool retval = DoMatchImpl(text, anchor, consumed, args, n, vec, vecsize); + if (vec != space) delete [] vec; + return retval; +} + +bool RE::Rewrite(string *out, const StringPiece &rewrite, + const StringPiece &text, int *vec, int veclen) const { + for (const char *s = rewrite.data(), *end = s + rewrite.size(); + s < end; s++) { + int c = *s; + if (c == '\\') { + c = *++s; + if (isdigit(c)) { + int n = (c - '0'); + if (n >= veclen) { + //fprintf(stderr, requested group %d in regexp %.*s\n", + // n, rewrite.size(), rewrite.data()); + return false; + } + int start = vec[2 * n]; + if (start >= 0) + out->append(text.data() + start, vec[2 * n + 1] - start); + } else if (c == '\\') { + out->push_back('\\'); + } else { + //fprintf(stderr, "invalid rewrite pattern: %.*s\n", + // rewrite.size(), rewrite.data()); + return false; + } + } else { + out->push_back(c); + } + } + return true; +} + +// Return the number of capturing subpatterns, or -1 if the +// regexp wasn't valid on construction. +int RE::NumberOfCapturingGroups() const { + if (re_partial_ == NULL) return -1; + + int result; + int pcre_retval = pcre_fullinfo(re_partial_, // The regular expression object + NULL, // We did not study the pattern + PCRE_INFO_CAPTURECOUNT, + &result); + assert(pcre_retval == 0); + return result; +} + +/***** Parsers for various types *****/ + +bool Arg::parse_null(const char* str, int n, void* dest) { + // We fail if somebody asked us to store into a non-NULL void* pointer + return (dest == NULL); +} + +bool Arg::parse_string(const char* str, int n, void* dest) { + if (dest == NULL) return true; + reinterpret_cast(dest)->assign(str, n); + return true; +} + +bool Arg::parse_stringpiece(const char* str, int n, void* dest) { + if (dest == NULL) return true; + reinterpret_cast(dest)->set(str, n); + return true; +} + +bool Arg::parse_char(const char* str, int n, void* dest) { + if (n != 1) return false; + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = str[0]; + return true; +} + +bool Arg::parse_uchar(const char* str, int n, void* dest) { + if (n != 1) return false; + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = str[0]; + return true; +} + +// Largest number spec that we are willing to parse +static const int kMaxNumberLength = 32; + +// REQUIRES "buf" must have length at least kMaxNumberLength+1 +// REQUIRES "n > 0" +// Copies "str" into "buf" and null-terminates if necessary. +// Returns one of: +// a. "str" if no termination is needed +// b. "buf" if the string was copied and null-terminated +// c. "" if the input was invalid and has no hope of being parsed +static const char* TerminateNumber(char* buf, const char* str, int n) { + if ((n > 0) && isspace(*str)) { + // We are less forgiving than the strtoxxx() routines and do not + // allow leading spaces. + return ""; + } + + // See if the character right after the input text may potentially + // look like a digit. + if (isdigit(str[n]) || + ((str[n] >= 'a') && (str[n] <= 'f')) || + ((str[n] >= 'A') && (str[n] <= 'F'))) { + if (n > kMaxNumberLength) return ""; // Input too big to be a valid number + memcpy(buf, str, n); + buf[n] = '\0'; + return buf; + } else { + // We can parse right out of the supplied string, so return it. + return str; + } +} + +bool Arg::parse_long_radix(const char* str, + int n, + void* dest, + int radix) { + if (n == 0) return false; + char buf[kMaxNumberLength+1]; + str = TerminateNumber(buf, str, n); + char* end; + errno = 0; + long r = strtol(str, &end, radix); + if (end != str + n) return false; // Leftover junk + if (errno) return false; + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = r; + return true; +} + +bool Arg::parse_ulong_radix(const char* str, + int n, + void* dest, + int radix) { + if (n == 0) return false; + char buf[kMaxNumberLength+1]; + str = TerminateNumber(buf, str, n); + if (str[0] == '-') return false; // strtoul() on a negative number?! + char* end; + errno = 0; + unsigned long r = strtoul(str, &end, radix); + if (end != str + n) return false; // Leftover junk + if (errno) return false; + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = r; + return true; +} + +bool Arg::parse_short_radix(const char* str, + int n, + void* dest, + int radix) { + long r; + if (!parse_long_radix(str, n, &r, radix)) return false; // Could not parse + if (r < SHRT_MIN || r > SHRT_MAX) return false; // Out of range + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = static_cast(r); + return true; +} + +bool Arg::parse_ushort_radix(const char* str, + int n, + void* dest, + int radix) { + unsigned long r; + if (!parse_ulong_radix(str, n, &r, radix)) return false; // Could not parse + if (r > USHRT_MAX) return false; // Out of range + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = static_cast(r); + return true; +} + +bool Arg::parse_int_radix(const char* str, + int n, + void* dest, + int radix) { + long r; + if (!parse_long_radix(str, n, &r, radix)) return false; // Could not parse + if (r < INT_MIN || r > INT_MAX) return false; // Out of range + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = r; + return true; +} + +bool Arg::parse_uint_radix(const char* str, + int n, + void* dest, + int radix) { + unsigned long r; + if (!parse_ulong_radix(str, n, &r, radix)) return false; // Could not parse + if (r > UINT_MAX) return false; // Out of range + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = r; + return true; +} + +bool Arg::parse_longlong_radix(const char* str, + int n, + void* dest, + int radix) { +#ifndef HAVE_LONG_LONG + return false; +#else + if (n == 0) return false; + char buf[kMaxNumberLength+1]; + str = TerminateNumber(buf, str, n); + char* end; + errno = 0; +#if defined HAVE_STRTOQ + long long r = strtoq(str, &end, radix); +#elif defined HAVE_STRTOLL + long long r = strtoll(str, &end, radix); +#elif defined HAVE__STRTOI64 + long long r = _strtoi64(str, &end, radix); +#else +#error parse_longlong_radix: cannot convert input to a long-long +#endif + if (end != str + n) return false; // Leftover junk + if (errno) return false; + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = r; + return true; +#endif /* HAVE_LONG_LONG */ +} + +bool Arg::parse_ulonglong_radix(const char* str, + int n, + void* dest, + int radix) { +#ifndef HAVE_UNSIGNED_LONG_LONG + return false; +#else + if (n == 0) return false; + char buf[kMaxNumberLength+1]; + str = TerminateNumber(buf, str, n); + if (str[0] == '-') return false; // strtoull() on a negative number?! + char* end; + errno = 0; +#if defined HAVE_STRTOQ + unsigned long long r = strtouq(str, &end, radix); +#elif defined HAVE_STRTOLL + unsigned long long r = strtoull(str, &end, radix); +#elif defined HAVE__STRTOI64 + unsigned long long r = _strtoui64(str, &end, radix); +#else +#error parse_ulonglong_radix: cannot convert input to a long-long +#endif + if (end != str + n) return false; // Leftover junk + if (errno) return false; + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = r; + return true; +#endif /* HAVE_UNSIGNED_LONG_LONG */ +} + +bool Arg::parse_double(const char* str, int n, void* dest) { + if (n == 0) return false; + static const int kMaxLength = 200; + char buf[kMaxLength]; + if (n >= kMaxLength) return false; + memcpy(buf, str, n); + buf[n] = '\0'; + errno = 0; + char* end; + double r = strtod(buf, &end); + if (end != buf + n) return false; // Leftover junk + if (errno) return false; + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = r; + return true; +} + +bool Arg::parse_float(const char* str, int n, void* dest) { + double r; + if (!parse_double(str, n, &r)) return false; + if (dest == NULL) return true; + *(reinterpret_cast(dest)) = static_cast(r); + return true; +} + + +#define DEFINE_INTEGER_PARSERS(name) \ + bool Arg::parse_##name(const char* str, int n, void* dest) { \ + return parse_##name##_radix(str, n, dest, 10); \ + } \ + bool Arg::parse_##name##_hex(const char* str, int n, void* dest) { \ + return parse_##name##_radix(str, n, dest, 16); \ + } \ + bool Arg::parse_##name##_octal(const char* str, int n, void* dest) { \ + return parse_##name##_radix(str, n, dest, 8); \ + } \ + bool Arg::parse_##name##_cradix(const char* str, int n, void* dest) { \ + return parse_##name##_radix(str, n, dest, 0); \ + } + +DEFINE_INTEGER_PARSERS(short) /* */ +DEFINE_INTEGER_PARSERS(ushort) /* */ +DEFINE_INTEGER_PARSERS(int) /* Don't use semicolons after these */ +DEFINE_INTEGER_PARSERS(uint) /* statements because they can cause */ +DEFINE_INTEGER_PARSERS(long) /* compiler warnings if the checking */ +DEFINE_INTEGER_PARSERS(ulong) /* level is turned up high enough. */ +DEFINE_INTEGER_PARSERS(longlong) /* */ +DEFINE_INTEGER_PARSERS(ulonglong) /* */ + +#undef DEFINE_INTEGER_PARSERS + +} // namespace pcrecpp diff --git a/Engine/lib/pcre/pcrecpp.h b/Engine/lib/pcre/pcrecpp.h new file mode 100644 index 000000000..a4638e1e7 --- /dev/null +++ b/Engine/lib/pcre/pcrecpp.h @@ -0,0 +1,700 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Sanjay Ghemawat +// Support for PCRE_XXX modifiers added by Giuseppe Maxia, July 2005 + +#ifndef _PCRECPP_H +#define _PCRECPP_H + +// C++ interface to the pcre regular-expression library. RE supports +// Perl-style regular expressions (with extensions like \d, \w, \s, +// ...). +// +// ----------------------------------------------------------------------- +// REGEXP SYNTAX: +// +// This module is part of the pcre library and hence supports its syntax +// for regular expressions. +// +// The syntax is pretty similar to Perl's. For those not familiar +// with Perl's regular expressions, here are some examples of the most +// commonly used extensions: +// +// "hello (\\w+) world" -- \w matches a "word" character +// "version (\\d+)" -- \d matches a digit +// "hello\\s+world" -- \s matches any whitespace character +// "\\b(\\w+)\\b" -- \b matches empty string at a word boundary +// "(?i)hello" -- (?i) turns on case-insensitive matching +// "/\\*(.*?)\\*/" -- .*? matches . minimum no. of times possible +// +// ----------------------------------------------------------------------- +// MATCHING INTERFACE: +// +// The "FullMatch" operation checks that supplied text matches a +// supplied pattern exactly. +// +// Example: successful match +// pcrecpp::RE re("h.*o"); +// re.FullMatch("hello"); +// +// Example: unsuccessful match (requires full match): +// pcrecpp::RE re("e"); +// !re.FullMatch("hello"); +// +// Example: creating a temporary RE object: +// pcrecpp::RE("h.*o").FullMatch("hello"); +// +// You can pass in a "const char*" or a "string" for "text". The +// examples below tend to use a const char*. +// +// You can, as in the different examples above, store the RE object +// explicitly in a variable or use a temporary RE object. The +// examples below use one mode or the other arbitrarily. Either +// could correctly be used for any of these examples. +// +// ----------------------------------------------------------------------- +// MATCHING WITH SUB-STRING EXTRACTION: +// +// You can supply extra pointer arguments to extract matched subpieces. +// +// Example: extracts "ruby" into "s" and 1234 into "i" +// int i; +// string s; +// pcrecpp::RE re("(\\w+):(\\d+)"); +// re.FullMatch("ruby:1234", &s, &i); +// +// Example: does not try to extract any extra sub-patterns +// re.FullMatch("ruby:1234", &s); +// +// Example: does not try to extract into NULL +// re.FullMatch("ruby:1234", NULL, &i); +// +// Example: integer overflow causes failure +// !re.FullMatch("ruby:1234567891234", NULL, &i); +// +// Example: fails because there aren't enough sub-patterns: +// !pcrecpp::RE("\\w+:\\d+").FullMatch("ruby:1234", &s); +// +// Example: fails because string cannot be stored in integer +// !pcrecpp::RE("(.*)").FullMatch("ruby", &i); +// +// The provided pointer arguments can be pointers to any scalar numeric +// type, or one of +// string (matched piece is copied to string) +// StringPiece (StringPiece is mutated to point to matched piece) +// T (where "bool T::ParseFrom(const char*, int)" exists) +// NULL (the corresponding matched sub-pattern is not copied) +// +// CAVEAT: An optional sub-pattern that does not exist in the matched +// string is assigned the empty string. Therefore, the following will +// return false (because the empty string is not a valid number): +// int number; +// pcrecpp::RE::FullMatch("abc", "[a-z]+(\\d+)?", &number); +// +// ----------------------------------------------------------------------- +// DO_MATCH +// +// The matching interface supports at most 16 arguments per call. +// If you need more, consider using the more general interface +// pcrecpp::RE::DoMatch(). See pcrecpp.h for the signature for DoMatch. +// +// ----------------------------------------------------------------------- +// PARTIAL MATCHES +// +// You can use the "PartialMatch" operation when you want the pattern +// to match any substring of the text. +// +// Example: simple search for a string: +// pcrecpp::RE("ell").PartialMatch("hello"); +// +// Example: find first number in a string: +// int number; +// pcrecpp::RE re("(\\d+)"); +// re.PartialMatch("x*100 + 20", &number); +// assert(number == 100); +// +// ----------------------------------------------------------------------- +// UTF-8 AND THE MATCHING INTERFACE: +// +// By default, pattern and text are plain text, one byte per character. +// The UTF8 flag, passed to the constructor, causes both pattern +// and string to be treated as UTF-8 text, still a byte stream but +// potentially multiple bytes per character. In practice, the text +// is likelier to be UTF-8 than the pattern, but the match returned +// may depend on the UTF8 flag, so always use it when matching +// UTF8 text. E.g., "." will match one byte normally but with UTF8 +// set may match up to three bytes of a multi-byte character. +// +// Example: +// pcrecpp::RE_Options options; +// options.set_utf8(); +// pcrecpp::RE re(utf8_pattern, options); +// re.FullMatch(utf8_string); +// +// Example: using the convenience function UTF8(): +// pcrecpp::RE re(utf8_pattern, pcrecpp::UTF8()); +// re.FullMatch(utf8_string); +// +// NOTE: The UTF8 option is ignored if pcre was not configured with the +// --enable-utf8 flag. +// +// ----------------------------------------------------------------------- +// PASSING MODIFIERS TO THE REGULAR EXPRESSION ENGINE +// +// PCRE defines some modifiers to change the behavior of the regular +// expression engine. +// The C++ wrapper defines an auxiliary class, RE_Options, as a vehicle +// to pass such modifiers to a RE class. +// +// Currently, the following modifiers are supported +// +// modifier description Perl corresponding +// +// PCRE_CASELESS case insensitive match /i +// PCRE_MULTILINE multiple lines match /m +// PCRE_DOTALL dot matches newlines /s +// PCRE_DOLLAR_ENDONLY $ matches only at end N/A +// PCRE_EXTRA strict escape parsing N/A +// PCRE_EXTENDED ignore whitespaces /x +// PCRE_UTF8 handles UTF8 chars built-in +// PCRE_UNGREEDY reverses * and *? N/A +// PCRE_NO_AUTO_CAPTURE disables matching parens N/A (*) +// +// (For a full account on how each modifier works, please check the +// PCRE API reference manual). +// +// (*) Both Perl and PCRE allow non matching parentheses by means of the +// "?:" modifier within the pattern itself. e.g. (?:ab|cd) does not +// capture, while (ab|cd) does. +// +// For each modifier, there are two member functions whose name is made +// out of the modifier in lowercase, without the "PCRE_" prefix. For +// instance, PCRE_CASELESS is handled by +// bool caseless(), +// which returns true if the modifier is set, and +// RE_Options & set_caseless(bool), +// which sets or unsets the modifier. +// +// Moreover, PCRE_EXTRA_MATCH_LIMIT can be accessed through the +// set_match_limit() and match_limit() member functions. +// Setting match_limit to a non-zero value will limit the executation of +// pcre to keep it from doing bad things like blowing the stack or taking +// an eternity to return a result. A value of 5000 is good enough to stop +// stack blowup in a 2MB thread stack. Setting match_limit to zero will +// disable match limiting. Alternately, you can set match_limit_recursion() +// which uses PCRE_EXTRA_MATCH_LIMIT_RECURSION to limit how much pcre +// recurses. match_limit() caps the number of matches pcre does; +// match_limit_recrusion() caps the depth of recursion. +// +// Normally, to pass one or more modifiers to a RE class, you declare +// a RE_Options object, set the appropriate options, and pass this +// object to a RE constructor. Example: +// +// RE_options opt; +// opt.set_caseless(true); +// +// if (RE("HELLO", opt).PartialMatch("hello world")) ... +// +// RE_options has two constructors. The default constructor takes no +// arguments and creates a set of flags that are off by default. +// +// The optional parameter 'option_flags' is to facilitate transfer +// of legacy code from C programs. This lets you do +// RE(pattern, RE_Options(PCRE_CASELESS|PCRE_MULTILINE)).PartialMatch(str); +// +// But new code is better off doing +// RE(pattern, +// RE_Options().set_caseless(true).set_multiline(true)).PartialMatch(str); +// (See below) +// +// If you are going to pass one of the most used modifiers, there are some +// convenience functions that return a RE_Options class with the +// appropriate modifier already set: +// CASELESS(), UTF8(), MULTILINE(), DOTALL(), EXTENDED() +// +// If you need to set several options at once, and you don't want to go +// through the pains of declaring a RE_Options object and setting several +// options, there is a parallel method that give you such ability on the +// fly. You can concatenate several set_xxxxx member functions, since each +// of them returns a reference to its class object. e.g.: to pass +// PCRE_CASELESS, PCRE_EXTENDED, and PCRE_MULTILINE to a RE with one +// statement, you may write +// +// RE(" ^ xyz \\s+ .* blah$", RE_Options() +// .set_caseless(true) +// .set_extended(true) +// .set_multiline(true)).PartialMatch(sometext); +// +// ----------------------------------------------------------------------- +// SCANNING TEXT INCREMENTALLY +// +// The "Consume" operation may be useful if you want to repeatedly +// match regular expressions at the front of a string and skip over +// them as they match. This requires use of the "StringPiece" type, +// which represents a sub-range of a real string. Like RE, StringPiece +// is defined in the pcrecpp namespace. +// +// Example: read lines of the form "var = value" from a string. +// string contents = ...; // Fill string somehow +// pcrecpp::StringPiece input(contents); // Wrap in a StringPiece +// +// string var; +// int value; +// pcrecpp::RE re("(\\w+) = (\\d+)\n"); +// while (re.Consume(&input, &var, &value)) { +// ...; +// } +// +// Each successful call to "Consume" will set "var/value", and also +// advance "input" so it points past the matched text. +// +// The "FindAndConsume" operation is similar to "Consume" but does not +// anchor your match at the beginning of the string. For example, you +// could extract all words from a string by repeatedly calling +// pcrecpp::RE("(\\w+)").FindAndConsume(&input, &word) +// +// ----------------------------------------------------------------------- +// PARSING HEX/OCTAL/C-RADIX NUMBERS +// +// By default, if you pass a pointer to a numeric value, the +// corresponding text is interpreted as a base-10 number. You can +// instead wrap the pointer with a call to one of the operators Hex(), +// Octal(), or CRadix() to interpret the text in another base. The +// CRadix operator interprets C-style "0" (base-8) and "0x" (base-16) +// prefixes, but defaults to base-10. +// +// Example: +// int a, b, c, d; +// pcrecpp::RE re("(.*) (.*) (.*) (.*)"); +// re.FullMatch("100 40 0100 0x40", +// pcrecpp::Octal(&a), pcrecpp::Hex(&b), +// pcrecpp::CRadix(&c), pcrecpp::CRadix(&d)); +// will leave 64 in a, b, c, and d. +// +// ----------------------------------------------------------------------- +// REPLACING PARTS OF STRINGS +// +// You can replace the first match of "pattern" in "str" with +// "rewrite". Within "rewrite", backslash-escaped digits (\1 to \9) +// can be used to insert text matching corresponding parenthesized +// group from the pattern. \0 in "rewrite" refers to the entire +// matching text. E.g., +// +// string s = "yabba dabba doo"; +// pcrecpp::RE("b+").Replace("d", &s); +// +// will leave "s" containing "yada dabba doo". The result is true if +// the pattern matches and a replacement occurs, or false otherwise. +// +// GlobalReplace() is like Replace(), except that it replaces all +// occurrences of the pattern in the string with the rewrite. +// Replacements are not subject to re-matching. E.g., +// +// string s = "yabba dabba doo"; +// pcrecpp::RE("b+").GlobalReplace("d", &s); +// +// will leave "s" containing "yada dada doo". It returns the number +// of replacements made. +// +// Extract() is like Replace(), except that if the pattern matches, +// "rewrite" is copied into "out" (an additional argument) with +// substitutions. The non-matching portions of "text" are ignored. +// Returns true iff a match occurred and the extraction happened +// successfully. If no match occurs, the string is left unaffected. + + +#include +#include +#include // defines the Arg class +// This isn't technically needed here, but we include it +// anyway so folks who include pcrecpp.h don't have to. +#include + +namespace pcrecpp { + +#define PCRE_SET_OR_CLEAR(b, o) \ + if (b) all_options_ |= (o); else all_options_ &= ~(o); \ + return *this + +#define PCRE_IS_SET(o) \ + (all_options_ & o) == o + +/***** Compiling regular expressions: the RE class *****/ + +// RE_Options allow you to set options to be passed along to pcre, +// along with other options we put on top of pcre. +// Only 9 modifiers, plus match_limit and match_limit_recursion, +// are supported now. +class PCRECPP_EXP_DEFN RE_Options { + public: + // constructor + RE_Options() : match_limit_(0), match_limit_recursion_(0), all_options_(0) {} + + // alternative constructor. + // To facilitate transfer of legacy code from C programs + // + // This lets you do + // RE(pattern, RE_Options(PCRE_CASELESS|PCRE_MULTILINE)).PartialMatch(str); + // But new code is better off doing + // RE(pattern, + // RE_Options().set_caseless(true).set_multiline(true)).PartialMatch(str); + RE_Options(int option_flags) : match_limit_(0), match_limit_recursion_(0), + all_options_(option_flags) {} + // we're fine with the default destructor, copy constructor, etc. + + // accessors and mutators + int match_limit() const { return match_limit_; }; + RE_Options &set_match_limit(int limit) { + match_limit_ = limit; + return *this; + } + + int match_limit_recursion() const { return match_limit_recursion_; }; + RE_Options &set_match_limit_recursion(int limit) { + match_limit_recursion_ = limit; + return *this; + } + + bool caseless() const { + return PCRE_IS_SET(PCRE_CASELESS); + } + RE_Options &set_caseless(bool x) { + PCRE_SET_OR_CLEAR(x, PCRE_CASELESS); + } + + bool multiline() const { + return PCRE_IS_SET(PCRE_MULTILINE); + } + RE_Options &set_multiline(bool x) { + PCRE_SET_OR_CLEAR(x, PCRE_MULTILINE); + } + + bool dotall() const { + return PCRE_IS_SET(PCRE_DOTALL); + } + RE_Options &set_dotall(bool x) { + PCRE_SET_OR_CLEAR(x, PCRE_DOTALL); + } + + bool extended() const { + return PCRE_IS_SET(PCRE_EXTENDED); + } + RE_Options &set_extended(bool x) { + PCRE_SET_OR_CLEAR(x, PCRE_EXTENDED); + } + + bool dollar_endonly() const { + return PCRE_IS_SET(PCRE_DOLLAR_ENDONLY); + } + RE_Options &set_dollar_endonly(bool x) { + PCRE_SET_OR_CLEAR(x, PCRE_DOLLAR_ENDONLY); + } + + bool extra() const { + return PCRE_IS_SET(PCRE_EXTRA); + } + RE_Options &set_extra(bool x) { + PCRE_SET_OR_CLEAR(x, PCRE_EXTRA); + } + + bool ungreedy() const { + return PCRE_IS_SET(PCRE_UNGREEDY); + } + RE_Options &set_ungreedy(bool x) { + PCRE_SET_OR_CLEAR(x, PCRE_UNGREEDY); + } + + bool utf8() const { + return PCRE_IS_SET(PCRE_UTF8); + } + RE_Options &set_utf8(bool x) { + PCRE_SET_OR_CLEAR(x, PCRE_UTF8); + } + + bool no_auto_capture() const { + return PCRE_IS_SET(PCRE_NO_AUTO_CAPTURE); + } + RE_Options &set_no_auto_capture(bool x) { + PCRE_SET_OR_CLEAR(x, PCRE_NO_AUTO_CAPTURE); + } + + RE_Options &set_all_options(int opt) { + all_options_ = opt; + return *this; + } + int all_options() const { + return all_options_ ; + } + + // TODO: add other pcre flags + + private: + int match_limit_; + int match_limit_recursion_; + int all_options_; +}; + +// These functions return some common RE_Options +static inline RE_Options UTF8() { + return RE_Options().set_utf8(true); +} + +static inline RE_Options CASELESS() { + return RE_Options().set_caseless(true); +} +static inline RE_Options MULTILINE() { + return RE_Options().set_multiline(true); +} + +static inline RE_Options DOTALL() { + return RE_Options().set_dotall(true); +} + +static inline RE_Options EXTENDED() { + return RE_Options().set_extended(true); +} + +// Interface for regular expression matching. Also corresponds to a +// pre-compiled regular expression. An "RE" object is safe for +// concurrent use by multiple threads. +class PCRECPP_EXP_DEFN RE { + public: + // We provide implicit conversions from strings so that users can + // pass in a string or a "const char*" wherever an "RE" is expected. + RE(const string& pat) { Init(pat, NULL); } + RE(const string& pat, const RE_Options& option) { Init(pat, &option); } + RE(const char* pat) { Init(pat, NULL); } + RE(const char* pat, const RE_Options& option) { Init(pat, &option); } + RE(const unsigned char* pat) { + Init(reinterpret_cast(pat), NULL); + } + RE(const unsigned char* pat, const RE_Options& option) { + Init(reinterpret_cast(pat), &option); + } + + // Copy constructor & assignment - note that these are expensive + // because they recompile the expression. + RE(const RE& re) { Init(re.pattern_, &re.options_); } + const RE& operator=(const RE& re) { + if (this != &re) { + Cleanup(); + + // This is the code that originally came from Google + // Init(re.pattern_.c_str(), &re.options_); + + // This is the replacement from Ari Pollak + Init(re.pattern_, &re.options_); + } + return *this; + } + + + ~RE(); + + // The string specification for this RE. E.g. + // RE re("ab*c?d+"); + // re.pattern(); // "ab*c?d+" + const string& pattern() const { return pattern_; } + + // If RE could not be created properly, returns an error string. + // Else returns the empty string. + const string& error() const { return *error_; } + + /***** The useful part: the matching interface *****/ + + // This is provided so one can do pattern.ReplaceAll() just as + // easily as ReplaceAll(pattern-text, ....) + + bool FullMatch(const StringPiece& text, + const Arg& ptr1 = no_arg, + const Arg& ptr2 = no_arg, + const Arg& ptr3 = no_arg, + const Arg& ptr4 = no_arg, + const Arg& ptr5 = no_arg, + const Arg& ptr6 = no_arg, + const Arg& ptr7 = no_arg, + const Arg& ptr8 = no_arg, + const Arg& ptr9 = no_arg, + const Arg& ptr10 = no_arg, + const Arg& ptr11 = no_arg, + const Arg& ptr12 = no_arg, + const Arg& ptr13 = no_arg, + const Arg& ptr14 = no_arg, + const Arg& ptr15 = no_arg, + const Arg& ptr16 = no_arg) const; + + bool PartialMatch(const StringPiece& text, + const Arg& ptr1 = no_arg, + const Arg& ptr2 = no_arg, + const Arg& ptr3 = no_arg, + const Arg& ptr4 = no_arg, + const Arg& ptr5 = no_arg, + const Arg& ptr6 = no_arg, + const Arg& ptr7 = no_arg, + const Arg& ptr8 = no_arg, + const Arg& ptr9 = no_arg, + const Arg& ptr10 = no_arg, + const Arg& ptr11 = no_arg, + const Arg& ptr12 = no_arg, + const Arg& ptr13 = no_arg, + const Arg& ptr14 = no_arg, + const Arg& ptr15 = no_arg, + const Arg& ptr16 = no_arg) const; + + bool Consume(StringPiece* input, + const Arg& ptr1 = no_arg, + const Arg& ptr2 = no_arg, + const Arg& ptr3 = no_arg, + const Arg& ptr4 = no_arg, + const Arg& ptr5 = no_arg, + const Arg& ptr6 = no_arg, + const Arg& ptr7 = no_arg, + const Arg& ptr8 = no_arg, + const Arg& ptr9 = no_arg, + const Arg& ptr10 = no_arg, + const Arg& ptr11 = no_arg, + const Arg& ptr12 = no_arg, + const Arg& ptr13 = no_arg, + const Arg& ptr14 = no_arg, + const Arg& ptr15 = no_arg, + const Arg& ptr16 = no_arg) const; + + bool FindAndConsume(StringPiece* input, + const Arg& ptr1 = no_arg, + const Arg& ptr2 = no_arg, + const Arg& ptr3 = no_arg, + const Arg& ptr4 = no_arg, + const Arg& ptr5 = no_arg, + const Arg& ptr6 = no_arg, + const Arg& ptr7 = no_arg, + const Arg& ptr8 = no_arg, + const Arg& ptr9 = no_arg, + const Arg& ptr10 = no_arg, + const Arg& ptr11 = no_arg, + const Arg& ptr12 = no_arg, + const Arg& ptr13 = no_arg, + const Arg& ptr14 = no_arg, + const Arg& ptr15 = no_arg, + const Arg& ptr16 = no_arg) const; + + bool Replace(const StringPiece& rewrite, + string *str) const; + + int GlobalReplace(const StringPiece& rewrite, + string *str) const; + + bool Extract(const StringPiece &rewrite, + const StringPiece &text, + string *out) const; + + // Escapes all potentially meaningful regexp characters in + // 'unquoted'. The returned string, used as a regular expression, + // will exactly match the original string. For example, + // 1.5-2.0? + // may become: + // 1\.5\-2\.0\? + static string QuoteMeta(const StringPiece& unquoted); + + + /***** Generic matching interface *****/ + + // Type of match (TODO: Should be restructured as part of RE_Options) + enum Anchor { + UNANCHORED, // No anchoring + ANCHOR_START, // Anchor at start only + ANCHOR_BOTH // Anchor at start and end + }; + + // General matching routine. Stores the length of the match in + // "*consumed" if successful. + bool DoMatch(const StringPiece& text, + Anchor anchor, + int* consumed, + const Arg* const* args, int n) const; + + // Return the number of capturing subpatterns, or -1 if the + // regexp wasn't valid on construction. + int NumberOfCapturingGroups() const; + + // The default value for an argument, to indicate no arg was passed in + static Arg no_arg; + + private: + + void Init(const string& pattern, const RE_Options* options); + void Cleanup(); + + // Match against "text", filling in "vec" (up to "vecsize" * 2/3) with + // pairs of integers for the beginning and end positions of matched + // text. The first pair corresponds to the entire matched text; + // subsequent pairs correspond, in order, to parentheses-captured + // matches. Returns the number of pairs (one more than the number of + // the last subpattern with a match) if matching was successful + // and zero if the match failed. + // I.e. for RE("(foo)|(bar)|(baz)") it will return 2, 3, and 4 when matching + // against "foo", "bar", and "baz" respectively. + // When matching RE("(foo)|hello") against "hello", it will return 1. + // But the values for all subpattern are filled in into "vec". + int TryMatch(const StringPiece& text, + int startpos, + Anchor anchor, + int *vec, + int vecsize) const; + + // Append the "rewrite" string, with backslash subsitutions from "text" + // and "vec", to string "out". + bool Rewrite(string *out, + const StringPiece& rewrite, + const StringPiece& text, + int *vec, + int veclen) const; + + // internal implementation for DoMatch + bool DoMatchImpl(const StringPiece& text, + Anchor anchor, + int* consumed, + const Arg* const args[], + int n, + int* vec, + int vecsize) const; + + // Compile the regexp for the specified anchoring mode + pcre* Compile(Anchor anchor); + + string pattern_; + RE_Options options_; + pcre* re_full_; // For full matches + pcre* re_partial_; // For partial matches + const string* error_; // Error indicator (or points to empty string) +}; + +} // namespace pcrecpp + +#endif /* _PCRECPP_H */ diff --git a/Engine/lib/pcre/pcrecpp_internal.h b/Engine/lib/pcre/pcrecpp_internal.h new file mode 100644 index 000000000..88fdf9c24 --- /dev/null +++ b/Engine/lib/pcre/pcrecpp_internal.h @@ -0,0 +1,68 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +/* +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + + +#ifndef PCRECPP_INTERNAL_H +#define PCRECPP_INTERNAL_H + +/* When compiling a DLL for Windows, the exported symbols have to be declared +using some MS magic. I found some useful information on this web page: +http://msdn2.microsoft.com/en-us/library/y4h7bcy6(VS.80).aspx. According to the +information there, using __declspec(dllexport) without "extern" we have a +definition; with "extern" we have a declaration. The settings here override the +setting in pcre.h. We use: + + PCRECPP_EXP_DECL for declarations + PCRECPP_EXP_DEFN for definitions of exported functions + +*/ + +#ifndef PCRECPP_EXP_DECL +# ifdef _WIN32 +# ifndef PCRE_STATIC +# define PCRECPP_EXP_DECL extern __declspec(dllexport) +# define PCRECPP_EXP_DEFN __declspec(dllexport) +# else +# define PCRECPP_EXP_DECL extern +# define PCRECPP_EXP_DEFN +# endif +# else +# define PCRECPP_EXP_DECL extern +# define PCRECPP_EXP_DEFN +# endif +#endif + +#endif /* PCRECPP_INTERNAL_H */ + +/* End of pcrecpp_internal.h */ diff --git a/Engine/lib/pcre/pcrecpparg.h b/Engine/lib/pcre/pcrecpparg.h new file mode 100644 index 000000000..b4f9c3f49 --- /dev/null +++ b/Engine/lib/pcre/pcrecpparg.h @@ -0,0 +1,174 @@ +// Copyright (c) 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Sanjay Ghemawat + +#ifndef _PCRECPPARG_H +#define _PCRECPPARG_H + +#include // for NULL +#include + +#include + +namespace pcrecpp { + +class StringPiece; + +// Hex/Octal/Binary? + +// Special class for parsing into objects that define a ParseFrom() method +template +class _RE_MatchObject { + public: + static inline bool Parse(const char* str, int n, void* dest) { + if (dest == NULL) return true; + T* object = reinterpret_cast(dest); + return object->ParseFrom(str, n); + } +}; + +class PCRECPP_EXP_DEFN Arg { + public: + // Empty constructor so we can declare arrays of Arg + Arg(); + + // Constructor specially designed for NULL arguments + Arg(void*); + + typedef bool (*Parser)(const char* str, int n, void* dest); + +// Type-specific parsers +#define PCRE_MAKE_PARSER(type,name) \ + Arg(type* p) : arg_(p), parser_(name) { } \ + Arg(type* p, Parser parser) : arg_(p), parser_(parser) { } + + + PCRE_MAKE_PARSER(char, parse_char); + PCRE_MAKE_PARSER(unsigned char, parse_uchar); + PCRE_MAKE_PARSER(short, parse_short); + PCRE_MAKE_PARSER(unsigned short, parse_ushort); + PCRE_MAKE_PARSER(int, parse_int); + PCRE_MAKE_PARSER(unsigned int, parse_uint); + PCRE_MAKE_PARSER(long, parse_long); + PCRE_MAKE_PARSER(unsigned long, parse_ulong); +#if 1 + PCRE_MAKE_PARSER(long long, parse_longlong); +#endif +#if 1 + PCRE_MAKE_PARSER(unsigned long long, parse_ulonglong); +#endif + PCRE_MAKE_PARSER(float, parse_float); + PCRE_MAKE_PARSER(double, parse_double); + PCRE_MAKE_PARSER(std::string, parse_string); + PCRE_MAKE_PARSER(StringPiece, parse_stringpiece); + +#undef PCRE_MAKE_PARSER + + // Generic constructor + template Arg(T*, Parser parser); + // Generic constructor template + template Arg(T* p) + : arg_(p), parser_(_RE_MatchObject::Parse) { + } + + // Parse the data + bool Parse(const char* str, int n) const; + + private: + void* arg_; + Parser parser_; + + static bool parse_null (const char* str, int n, void* dest); + static bool parse_char (const char* str, int n, void* dest); + static bool parse_uchar (const char* str, int n, void* dest); + static bool parse_float (const char* str, int n, void* dest); + static bool parse_double (const char* str, int n, void* dest); + static bool parse_string (const char* str, int n, void* dest); + static bool parse_stringpiece (const char* str, int n, void* dest); + +#define PCRE_DECLARE_INTEGER_PARSER(name) \ + private: \ + static bool parse_ ## name(const char* str, int n, void* dest); \ + static bool parse_ ## name ## _radix( \ + const char* str, int n, void* dest, int radix); \ + public: \ + static bool parse_ ## name ## _hex(const char* str, int n, void* dest); \ + static bool parse_ ## name ## _octal(const char* str, int n, void* dest); \ + static bool parse_ ## name ## _cradix(const char* str, int n, void* dest) + + PCRE_DECLARE_INTEGER_PARSER(short); + PCRE_DECLARE_INTEGER_PARSER(ushort); + PCRE_DECLARE_INTEGER_PARSER(int); + PCRE_DECLARE_INTEGER_PARSER(uint); + PCRE_DECLARE_INTEGER_PARSER(long); + PCRE_DECLARE_INTEGER_PARSER(ulong); + PCRE_DECLARE_INTEGER_PARSER(longlong); + PCRE_DECLARE_INTEGER_PARSER(ulonglong); + +#undef PCRE_DECLARE_INTEGER_PARSER +}; + +inline Arg::Arg() : arg_(NULL), parser_(parse_null) { } +inline Arg::Arg(void* p) : arg_(p), parser_(parse_null) { } + +inline bool Arg::Parse(const char* str, int n) const { + return (*parser_)(str, n, arg_); +} + +// This part of the parser, appropriate only for ints, deals with bases +#define MAKE_INTEGER_PARSER(type, name) \ + inline Arg Hex(type* ptr) { \ + return Arg(ptr, Arg::parse_ ## name ## _hex); } \ + inline Arg Octal(type* ptr) { \ + return Arg(ptr, Arg::parse_ ## name ## _octal); } \ + inline Arg CRadix(type* ptr) { \ + return Arg(ptr, Arg::parse_ ## name ## _cradix); } + +MAKE_INTEGER_PARSER(short, short) /* */ +MAKE_INTEGER_PARSER(unsigned short, ushort) /* */ +MAKE_INTEGER_PARSER(int, int) /* Don't use semicolons */ +MAKE_INTEGER_PARSER(unsigned int, uint) /* after these statement */ +MAKE_INTEGER_PARSER(long, long) /* because they can cause */ +MAKE_INTEGER_PARSER(unsigned long, ulong) /* compiler warnings if */ +#if 1 /* the checking level is */ +MAKE_INTEGER_PARSER(long long, longlong) /* turned up high enough. */ +#endif /* */ +#if 1 /* */ +MAKE_INTEGER_PARSER(unsigned long long, ulonglong) /* */ +#endif + +#undef PCRE_IS_SET +#undef PCRE_SET_OR_CLEAR +#undef MAKE_INTEGER_PARSER + +} // namespace pcrecpp + + +#endif /* _PCRECPPARG_H */ diff --git a/Engine/lib/pcre/pcreposix.h b/Engine/lib/pcre/pcreposix.h new file mode 100644 index 000000000..109376d90 --- /dev/null +++ b/Engine/lib/pcre/pcreposix.h @@ -0,0 +1,142 @@ +/************************************************* +* Perl-Compatible Regular Expressions * +*************************************************/ + +#ifndef _PCREPOSIX_H +#define _PCREPOSIX_H + +/* This is the header for the POSIX wrapper interface to the PCRE Perl- +Compatible Regular Expression library. It defines the things POSIX says should +be there. I hope. + + Copyright (c) 1997-2008 University of Cambridge + +----------------------------------------------------------------------------- +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the University of Cambridge nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- +*/ + +/* Have to include stdlib.h in order to ensure that size_t is defined. */ + +#include + +/* Allow for C++ users */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Options, mostly defined by POSIX, but with a couple of extras. */ + +#define REG_ICASE 0x0001 +#define REG_NEWLINE 0x0002 +#define REG_NOTBOL 0x0004 +#define REG_NOTEOL 0x0008 +#define REG_DOTALL 0x0010 /* NOT defined by POSIX. */ +#define REG_NOSUB 0x0020 +#define REG_UTF8 0x0040 /* NOT defined by POSIX. */ + +/* This is not used by PCRE, but by defining it we make it easier +to slot PCRE into existing programs that make POSIX calls. */ + +#define REG_EXTENDED 0 + +/* Error values. Not all these are relevant or used by the wrapper. */ + +enum { + REG_ASSERT = 1, /* internal error ? */ + REG_BADBR, /* invalid repeat counts in {} */ + REG_BADPAT, /* pattern error */ + REG_BADRPT, /* ? * + invalid */ + REG_EBRACE, /* unbalanced {} */ + REG_EBRACK, /* unbalanced [] */ + REG_ECOLLATE, /* collation error - not relevant */ + REG_ECTYPE, /* bad class */ + REG_EESCAPE, /* bad escape sequence */ + REG_EMPTY, /* empty expression */ + REG_EPAREN, /* unbalanced () */ + REG_ERANGE, /* bad range inside [] */ + REG_ESIZE, /* expression too big */ + REG_ESPACE, /* failed to get memory */ + REG_ESUBREG, /* bad back reference */ + REG_INVARG, /* bad argument */ + REG_NOMATCH /* match failed */ +}; + + +/* The structure representing a compiled regular expression. */ + +typedef struct { + void *re_pcre; + size_t re_nsub; + size_t re_erroffset; +} regex_t; + +/* The structure in which a captured offset is returned. */ + +typedef int regoff_t; + +typedef struct { + regoff_t rm_so; + regoff_t rm_eo; +} regmatch_t; + +/* When an application links to a PCRE DLL in Windows, the symbols that are +imported have to be identified as such. When building PCRE, the appropriate +export settings are needed, and are set in pcreposix.c before including this +file. */ + +#if defined(_WIN32) && !defined(PCRE_STATIC) && !defined(PCREPOSIX_EXP_DECL) +# define PCREPOSIX_EXP_DECL extern __declspec(dllimport) +# define PCREPOSIX_EXP_DEFN __declspec(dllimport) +#endif + +/* By default, we use the standard "extern" declarations. */ + +#ifndef PCREPOSIX_EXP_DECL +# ifdef __cplusplus +# define PCREPOSIX_EXP_DECL extern "C" +# define PCREPOSIX_EXP_DEFN extern "C" +# else +# define PCREPOSIX_EXP_DECL extern +# define PCREPOSIX_EXP_DEFN extern +# endif +#endif + +/* The functions */ + +PCREPOSIX_EXP_DECL int regcomp(regex_t *, const char *, int); +PCREPOSIX_EXP_DECL int regexec(const regex_t *, const char *, size_t, + regmatch_t *, int); +PCREPOSIX_EXP_DECL size_t regerror(int, const regex_t *, char *, size_t); +PCREPOSIX_EXP_DECL void regfree(regex_t *); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* End of pcreposix.h */ diff --git a/Engine/lib/pcre/ucp.h b/Engine/lib/pcre/ucp.h new file mode 100644 index 000000000..3a4179b76 --- /dev/null +++ b/Engine/lib/pcre/ucp.h @@ -0,0 +1,133 @@ +/************************************************* +* Unicode Property Table handler * +*************************************************/ + +#ifndef _UCP_H +#define _UCP_H + +/* This file contains definitions of the property values that are returned by +the function _pcre_ucp_findprop(). New values that are added for new releases +of Unicode should always be at the end of each enum, for backwards +compatibility. */ + +/* These are the general character categories. */ + +enum { + ucp_C, /* Other */ + ucp_L, /* Letter */ + ucp_M, /* Mark */ + ucp_N, /* Number */ + ucp_P, /* Punctuation */ + ucp_S, /* Symbol */ + ucp_Z /* Separator */ +}; + +/* These are the particular character types. */ + +enum { + ucp_Cc, /* Control */ + ucp_Cf, /* Format */ + ucp_Cn, /* Unassigned */ + ucp_Co, /* Private use */ + ucp_Cs, /* Surrogate */ + ucp_Ll, /* Lower case letter */ + ucp_Lm, /* Modifier letter */ + ucp_Lo, /* Other letter */ + ucp_Lt, /* Title case letter */ + ucp_Lu, /* Upper case letter */ + ucp_Mc, /* Spacing mark */ + ucp_Me, /* Enclosing mark */ + ucp_Mn, /* Non-spacing mark */ + ucp_Nd, /* Decimal number */ + ucp_Nl, /* Letter number */ + ucp_No, /* Other number */ + ucp_Pc, /* Connector punctuation */ + ucp_Pd, /* Dash punctuation */ + ucp_Pe, /* Close punctuation */ + ucp_Pf, /* Final punctuation */ + ucp_Pi, /* Initial punctuation */ + ucp_Po, /* Other punctuation */ + ucp_Ps, /* Open punctuation */ + ucp_Sc, /* Currency symbol */ + ucp_Sk, /* Modifier symbol */ + ucp_Sm, /* Mathematical symbol */ + ucp_So, /* Other symbol */ + ucp_Zl, /* Line separator */ + ucp_Zp, /* Paragraph separator */ + ucp_Zs /* Space separator */ +}; + +/* These are the script identifications. */ + +enum { + ucp_Arabic, + ucp_Armenian, + ucp_Bengali, + ucp_Bopomofo, + ucp_Braille, + ucp_Buginese, + ucp_Buhid, + ucp_Canadian_Aboriginal, + ucp_Cherokee, + ucp_Common, + ucp_Coptic, + ucp_Cypriot, + ucp_Cyrillic, + ucp_Deseret, + ucp_Devanagari, + ucp_Ethiopic, + ucp_Georgian, + ucp_Glagolitic, + ucp_Gothic, + ucp_Greek, + ucp_Gujarati, + ucp_Gurmukhi, + ucp_Han, + ucp_Hangul, + ucp_Hanunoo, + ucp_Hebrew, + ucp_Hiragana, + ucp_Inherited, + ucp_Kannada, + ucp_Katakana, + ucp_Kharoshthi, + ucp_Khmer, + ucp_Lao, + ucp_Latin, + ucp_Limbu, + ucp_Linear_B, + ucp_Malayalam, + ucp_Mongolian, + ucp_Myanmar, + ucp_New_Tai_Lue, + ucp_Ogham, + ucp_Old_Italic, + ucp_Old_Persian, + ucp_Oriya, + ucp_Osmanya, + ucp_Runic, + ucp_Shavian, + ucp_Sinhala, + ucp_Syloti_Nagri, + ucp_Syriac, + ucp_Tagalog, + ucp_Tagbanwa, + ucp_Tai_Le, + ucp_Tamil, + ucp_Telugu, + ucp_Thaana, + ucp_Thai, + ucp_Tibetan, + ucp_Tifinagh, + ucp_Ugaritic, + ucp_Yi, + ucp_Balinese, /* New for Unicode 5.0.0 */ + ucp_Cuneiform, /* New for Unicode 5.0.0 */ + ucp_Nko, /* New for Unicode 5.0.0 */ + ucp_Phags_Pa, /* New for Unicode 5.0.0 */ + ucp_Phoenician /* New for Unicode 5.0.0 */ +}; + +#endif + +/* End of ucp.h */ diff --git a/Engine/lib/pcre/ucpinternal.h b/Engine/lib/pcre/ucpinternal.h new file mode 100644 index 000000000..811a373c8 --- /dev/null +++ b/Engine/lib/pcre/ucpinternal.h @@ -0,0 +1,92 @@ +/************************************************* +* Unicode Property Table handler * +*************************************************/ + +#ifndef _UCPINTERNAL_H +#define _UCPINTERNAL_H + +/* Internal header file defining the layout of the bits in each pair of 32-bit +words that form a data item in the table. */ + +typedef struct cnode { + pcre_uint32 f0; + pcre_uint32 f1; +} cnode; + +/* Things for the f0 field */ + +#define f0_scriptmask 0xff000000 /* Mask for script field */ +#define f0_scriptshift 24 /* Shift for script value */ +#define f0_rangeflag 0x00f00000 /* Flag for a range item */ +#define f0_charmask 0x001fffff /* Mask for code point value */ + +/* Things for the f1 field */ + +#define f1_typemask 0xfc000000 /* Mask for char type field */ +#define f1_typeshift 26 /* Shift for the type field */ +#define f1_rangemask 0x0000ffff /* Mask for a range offset */ +#define f1_casemask 0x0000ffff /* Mask for a case offset */ +#define f1_caseneg 0xffff8000 /* Bits for negation */ + +/* The data consists of a vector of structures of type cnode. The two unsigned +32-bit integers are used as follows: + +(f0) (1) The most significant byte holds the script number. The numbers are + defined by the enum in ucp.h. + + (2) The 0x00800000 bit is set if this entry defines a range of characters. + It is not set if this entry defines a single character + + (3) The 0x00600000 bits are spare. + + (4) The 0x001fffff bits contain the code point. No Unicode code point will + ever be greater than 0x0010ffff, so this should be OK for ever. + +(f1) (1) The 0xfc000000 bits contain the character type number. The numbers are + defined by an enum in ucp.h. + + (2) The 0x03ff0000 bits are spare. + + (3) The 0x0000ffff bits contain EITHER the unsigned offset to the top of + range if this entry defines a range, OR the *signed* offset to the + character's "other case" partner if this entry defines a single + character. There is no partner if the value is zero. + +------------------------------------------------------------------------------- +| script (8) |.|.|.| codepoint (21) || type (6) |.|.| spare (8) | offset (16) | +------------------------------------------------------------------------------- + | | | | | + | | |-> spare | |-> spare + | | | + | |-> spare |-> spare + | + |-> range flag + +The upper/lower casing information is set only for characters that come in +pairs. The non-one-to-one mappings in the Unicode data are ignored. + +When searching the data, proceed as follows: + +(1) Set up for a binary chop search. + +(2) If the top is not greater than the bottom, the character is not in the + table. Its type must therefore be "Cn" ("Undefined"). + +(3) Find the middle vector element. + +(4) Extract the code point and compare. If equal, we are done. + +(5) If the test character is smaller, set the top to the current point, and + goto (2). + +(6) If the current entry defines a range, compute the last character by adding + the offset, and see if the test character is within the range. If it is, + we are done. + +(7) Otherwise, set the bottom to one element past the current point and goto + (2). +*/ + +#endif /* _UCPINTERNAL_H */ + +/* End of ucpinternal.h */ diff --git a/Engine/lib/pcre/ucptable.h b/Engine/lib/pcre/ucptable.h new file mode 100644 index 000000000..a274d443e --- /dev/null +++ b/Engine/lib/pcre/ucptable.h @@ -0,0 +1,3088 @@ +/* This source module is automatically generated from the Unicode +property table. See ucpinternal.h for a description of the layout. +This version was made from the Unicode 5.0.0 tables. */ + +static const cnode ucp_table[] = { + { 0x09800000, 0x0000001f }, + { 0x09000020, 0x74000000 }, + { 0x09800021, 0x54000002 }, + { 0x09000024, 0x5c000000 }, + { 0x09800025, 0x54000002 }, + { 0x09000028, 0x58000000 }, + { 0x09000029, 0x48000000 }, + { 0x0900002a, 0x54000000 }, + { 0x0900002b, 0x64000000 }, + { 0x0900002c, 0x54000000 }, + { 0x0900002d, 0x44000000 }, + { 0x0980002e, 0x54000001 }, + { 0x09800030, 0x34000009 }, + { 0x0980003a, 0x54000001 }, + { 0x0980003c, 0x64000002 }, + { 0x0980003f, 0x54000001 }, + { 0x21000041, 0x24000020 }, + { 0x21000042, 0x24000020 }, + { 0x21000043, 0x24000020 }, + { 0x21000044, 0x24000020 }, + { 0x21000045, 0x24000020 }, + { 0x21000046, 0x24000020 }, + { 0x21000047, 0x24000020 }, + { 0x21000048, 0x24000020 }, + { 0x21000049, 0x24000020 }, + { 0x2100004a, 0x24000020 }, + { 0x2100004b, 0x24000020 }, + { 0x2100004c, 0x24000020 }, + { 0x2100004d, 0x24000020 }, + { 0x2100004e, 0x24000020 }, + { 0x2100004f, 0x24000020 }, + { 0x21000050, 0x24000020 }, + { 0x21000051, 0x24000020 }, + { 0x21000052, 0x24000020 }, + { 0x21000053, 0x24000020 }, + { 0x21000054, 0x24000020 }, + { 0x21000055, 0x24000020 }, + { 0x21000056, 0x24000020 }, + { 0x21000057, 0x24000020 }, + { 0x21000058, 0x24000020 }, + { 0x21000059, 0x24000020 }, + { 0x2100005a, 0x24000020 }, + { 0x0900005b, 0x58000000 }, + { 0x0900005c, 0x54000000 }, + { 0x0900005d, 0x48000000 }, + { 0x0900005e, 0x60000000 }, + { 0x0900005f, 0x40000000 }, + { 0x09000060, 0x60000000 }, + { 0x21000061, 0x1400ffe0 }, + { 0x21000062, 0x1400ffe0 }, + { 0x21000063, 0x1400ffe0 }, + { 0x21000064, 0x1400ffe0 }, + { 0x21000065, 0x1400ffe0 }, + { 0x21000066, 0x1400ffe0 }, + { 0x21000067, 0x1400ffe0 }, + { 0x21000068, 0x1400ffe0 }, + { 0x21000069, 0x1400ffe0 }, + { 0x2100006a, 0x1400ffe0 }, + { 0x2100006b, 0x1400ffe0 }, + { 0x2100006c, 0x1400ffe0 }, + { 0x2100006d, 0x1400ffe0 }, + { 0x2100006e, 0x1400ffe0 }, + { 0x2100006f, 0x1400ffe0 }, + { 0x21000070, 0x1400ffe0 }, + { 0x21000071, 0x1400ffe0 }, + { 0x21000072, 0x1400ffe0 }, + { 0x21000073, 0x1400ffe0 }, + { 0x21000074, 0x1400ffe0 }, + { 0x21000075, 0x1400ffe0 }, + { 0x21000076, 0x1400ffe0 }, + { 0x21000077, 0x1400ffe0 }, + { 0x21000078, 0x1400ffe0 }, + { 0x21000079, 0x1400ffe0 }, + { 0x2100007a, 0x1400ffe0 }, + { 0x0900007b, 0x58000000 }, + { 0x0900007c, 0x64000000 }, + { 0x0900007d, 0x48000000 }, + { 0x0900007e, 0x64000000 }, + { 0x0980007f, 0x00000020 }, + { 0x090000a0, 0x74000000 }, + { 0x090000a1, 0x54000000 }, + { 0x098000a2, 0x5c000003 }, + { 0x098000a6, 0x68000001 }, + { 0x090000a8, 0x60000000 }, + { 0x090000a9, 0x68000000 }, + { 0x210000aa, 0x14000000 }, + { 0x090000ab, 0x50000000 }, + { 0x090000ac, 0x64000000 }, + { 0x090000ad, 0x04000000 }, + { 0x090000ae, 0x68000000 }, + { 0x090000af, 0x60000000 }, + { 0x090000b0, 0x68000000 }, + { 0x090000b1, 0x64000000 }, + { 0x098000b2, 0x3c000001 }, + { 0x090000b4, 0x60000000 }, + { 0x090000b5, 0x140002e7 }, + { 0x090000b6, 0x68000000 }, + { 0x090000b7, 0x54000000 }, + { 0x090000b8, 0x60000000 }, + { 0x090000b9, 0x3c000000 }, + { 0x210000ba, 0x14000000 }, + { 0x090000bb, 0x4c000000 }, + { 0x098000bc, 0x3c000002 }, + { 0x090000bf, 0x54000000 }, + { 0x210000c0, 0x24000020 }, + { 0x210000c1, 0x24000020 }, + { 0x210000c2, 0x24000020 }, + { 0x210000c3, 0x24000020 }, + { 0x210000c4, 0x24000020 }, + { 0x210000c5, 0x24000020 }, + { 0x210000c6, 0x24000020 }, + { 0x210000c7, 0x24000020 }, + { 0x210000c8, 0x24000020 }, + { 0x210000c9, 0x24000020 }, + { 0x210000ca, 0x24000020 }, + { 0x210000cb, 0x24000020 }, + { 0x210000cc, 0x24000020 }, + { 0x210000cd, 0x24000020 }, + { 0x210000ce, 0x24000020 }, + { 0x210000cf, 0x24000020 }, + { 0x210000d0, 0x24000020 }, + { 0x210000d1, 0x24000020 }, + { 0x210000d2, 0x24000020 }, + { 0x210000d3, 0x24000020 }, + { 0x210000d4, 0x24000020 }, + { 0x210000d5, 0x24000020 }, + { 0x210000d6, 0x24000020 }, + { 0x090000d7, 0x64000000 }, + { 0x210000d8, 0x24000020 }, + { 0x210000d9, 0x24000020 }, + { 0x210000da, 0x24000020 }, + { 0x210000db, 0x24000020 }, + { 0x210000dc, 0x24000020 }, + { 0x210000dd, 0x24000020 }, + { 0x210000de, 0x24000020 }, + { 0x210000df, 0x14000000 }, + { 0x210000e0, 0x1400ffe0 }, + { 0x210000e1, 0x1400ffe0 }, + { 0x210000e2, 0x1400ffe0 }, + { 0x210000e3, 0x1400ffe0 }, + { 0x210000e4, 0x1400ffe0 }, + { 0x210000e5, 0x1400ffe0 }, + { 0x210000e6, 0x1400ffe0 }, + { 0x210000e7, 0x1400ffe0 }, + { 0x210000e8, 0x1400ffe0 }, + { 0x210000e9, 0x1400ffe0 }, + { 0x210000ea, 0x1400ffe0 }, + { 0x210000eb, 0x1400ffe0 }, + { 0x210000ec, 0x1400ffe0 }, + { 0x210000ed, 0x1400ffe0 }, + { 0x210000ee, 0x1400ffe0 }, + { 0x210000ef, 0x1400ffe0 }, + { 0x210000f0, 0x1400ffe0 }, + { 0x210000f1, 0x1400ffe0 }, + { 0x210000f2, 0x1400ffe0 }, + { 0x210000f3, 0x1400ffe0 }, + { 0x210000f4, 0x1400ffe0 }, + { 0x210000f5, 0x1400ffe0 }, + { 0x210000f6, 0x1400ffe0 }, + { 0x090000f7, 0x64000000 }, + { 0x210000f8, 0x1400ffe0 }, + { 0x210000f9, 0x1400ffe0 }, + { 0x210000fa, 0x1400ffe0 }, + { 0x210000fb, 0x1400ffe0 }, + { 0x210000fc, 0x1400ffe0 }, + { 0x210000fd, 0x1400ffe0 }, + { 0x210000fe, 0x1400ffe0 }, + { 0x210000ff, 0x14000079 }, + { 0x21000100, 0x24000001 }, + { 0x21000101, 0x1400ffff }, + { 0x21000102, 0x24000001 }, + { 0x21000103, 0x1400ffff }, + { 0x21000104, 0x24000001 }, + { 0x21000105, 0x1400ffff }, + { 0x21000106, 0x24000001 }, + { 0x21000107, 0x1400ffff }, + { 0x21000108, 0x24000001 }, + { 0x21000109, 0x1400ffff }, + { 0x2100010a, 0x24000001 }, + { 0x2100010b, 0x1400ffff }, + { 0x2100010c, 0x24000001 }, + { 0x2100010d, 0x1400ffff }, + { 0x2100010e, 0x24000001 }, + { 0x2100010f, 0x1400ffff }, + { 0x21000110, 0x24000001 }, + { 0x21000111, 0x1400ffff }, + { 0x21000112, 0x24000001 }, + { 0x21000113, 0x1400ffff }, + { 0x21000114, 0x24000001 }, + { 0x21000115, 0x1400ffff }, + { 0x21000116, 0x24000001 }, + { 0x21000117, 0x1400ffff }, + { 0x21000118, 0x24000001 }, + { 0x21000119, 0x1400ffff }, + { 0x2100011a, 0x24000001 }, + { 0x2100011b, 0x1400ffff }, + { 0x2100011c, 0x24000001 }, + { 0x2100011d, 0x1400ffff }, + { 0x2100011e, 0x24000001 }, + { 0x2100011f, 0x1400ffff }, + { 0x21000120, 0x24000001 }, + { 0x21000121, 0x1400ffff }, + { 0x21000122, 0x24000001 }, + { 0x21000123, 0x1400ffff }, + { 0x21000124, 0x24000001 }, + { 0x21000125, 0x1400ffff }, + { 0x21000126, 0x24000001 }, + { 0x21000127, 0x1400ffff }, + { 0x21000128, 0x24000001 }, + { 0x21000129, 0x1400ffff }, + { 0x2100012a, 0x24000001 }, + { 0x2100012b, 0x1400ffff }, + { 0x2100012c, 0x24000001 }, + { 0x2100012d, 0x1400ffff }, + { 0x2100012e, 0x24000001 }, + { 0x2100012f, 0x1400ffff }, + { 0x21000130, 0x2400ff39 }, + { 0x21000131, 0x1400ff18 }, + { 0x21000132, 0x24000001 }, + { 0x21000133, 0x1400ffff }, + { 0x21000134, 0x24000001 }, + { 0x21000135, 0x1400ffff }, + { 0x21000136, 0x24000001 }, + { 0x21000137, 0x1400ffff }, + { 0x21000138, 0x14000000 }, + { 0x21000139, 0x24000001 }, + { 0x2100013a, 0x1400ffff }, + { 0x2100013b, 0x24000001 }, + { 0x2100013c, 0x1400ffff }, + { 0x2100013d, 0x24000001 }, + { 0x2100013e, 0x1400ffff }, + { 0x2100013f, 0x24000001 }, + { 0x21000140, 0x1400ffff }, + { 0x21000141, 0x24000001 }, + { 0x21000142, 0x1400ffff }, + { 0x21000143, 0x24000001 }, + { 0x21000144, 0x1400ffff }, + { 0x21000145, 0x24000001 }, + { 0x21000146, 0x1400ffff }, + { 0x21000147, 0x24000001 }, + { 0x21000148, 0x1400ffff }, + { 0x21000149, 0x14000000 }, + { 0x2100014a, 0x24000001 }, + { 0x2100014b, 0x1400ffff }, + { 0x2100014c, 0x24000001 }, + { 0x2100014d, 0x1400ffff }, + { 0x2100014e, 0x24000001 }, + { 0x2100014f, 0x1400ffff }, + { 0x21000150, 0x24000001 }, + { 0x21000151, 0x1400ffff }, + { 0x21000152, 0x24000001 }, + { 0x21000153, 0x1400ffff }, + { 0x21000154, 0x24000001 }, + { 0x21000155, 0x1400ffff }, + { 0x21000156, 0x24000001 }, + { 0x21000157, 0x1400ffff }, + { 0x21000158, 0x24000001 }, + { 0x21000159, 0x1400ffff }, + { 0x2100015a, 0x24000001 }, + { 0x2100015b, 0x1400ffff }, + { 0x2100015c, 0x24000001 }, + { 0x2100015d, 0x1400ffff }, + { 0x2100015e, 0x24000001 }, + { 0x2100015f, 0x1400ffff }, + { 0x21000160, 0x24000001 }, + { 0x21000161, 0x1400ffff }, + { 0x21000162, 0x24000001 }, + { 0x21000163, 0x1400ffff }, + { 0x21000164, 0x24000001 }, + { 0x21000165, 0x1400ffff }, + { 0x21000166, 0x24000001 }, + { 0x21000167, 0x1400ffff }, + { 0x21000168, 0x24000001 }, + { 0x21000169, 0x1400ffff }, + { 0x2100016a, 0x24000001 }, + { 0x2100016b, 0x1400ffff }, + { 0x2100016c, 0x24000001 }, + { 0x2100016d, 0x1400ffff }, + { 0x2100016e, 0x24000001 }, + { 0x2100016f, 0x1400ffff }, + { 0x21000170, 0x24000001 }, + { 0x21000171, 0x1400ffff }, + { 0x21000172, 0x24000001 }, + { 0x21000173, 0x1400ffff }, + { 0x21000174, 0x24000001 }, + { 0x21000175, 0x1400ffff }, + { 0x21000176, 0x24000001 }, + { 0x21000177, 0x1400ffff }, + { 0x21000178, 0x2400ff87 }, + { 0x21000179, 0x24000001 }, + { 0x2100017a, 0x1400ffff }, + { 0x2100017b, 0x24000001 }, + { 0x2100017c, 0x1400ffff }, + { 0x2100017d, 0x24000001 }, + { 0x2100017e, 0x1400ffff }, + { 0x2100017f, 0x1400fed4 }, + { 0x21000180, 0x140000c3 }, + { 0x21000181, 0x240000d2 }, + { 0x21000182, 0x24000001 }, + { 0x21000183, 0x1400ffff }, + { 0x21000184, 0x24000001 }, + { 0x21000185, 0x1400ffff }, + { 0x21000186, 0x240000ce }, + { 0x21000187, 0x24000001 }, + { 0x21000188, 0x1400ffff }, + { 0x21000189, 0x240000cd }, + { 0x2100018a, 0x240000cd }, + { 0x2100018b, 0x24000001 }, + { 0x2100018c, 0x1400ffff }, + { 0x2100018d, 0x14000000 }, + { 0x2100018e, 0x2400004f }, + { 0x2100018f, 0x240000ca }, + { 0x21000190, 0x240000cb }, + { 0x21000191, 0x24000001 }, + { 0x21000192, 0x1400ffff }, + { 0x21000193, 0x240000cd }, + { 0x21000194, 0x240000cf }, + { 0x21000195, 0x14000061 }, + { 0x21000196, 0x240000d3 }, + { 0x21000197, 0x240000d1 }, + { 0x21000198, 0x24000001 }, + { 0x21000199, 0x1400ffff }, + { 0x2100019a, 0x140000a3 }, + { 0x2100019b, 0x14000000 }, + { 0x2100019c, 0x240000d3 }, + { 0x2100019d, 0x240000d5 }, + { 0x2100019e, 0x14000082 }, + { 0x2100019f, 0x240000d6 }, + { 0x210001a0, 0x24000001 }, + { 0x210001a1, 0x1400ffff }, + { 0x210001a2, 0x24000001 }, + { 0x210001a3, 0x1400ffff }, + { 0x210001a4, 0x24000001 }, + { 0x210001a5, 0x1400ffff }, + { 0x210001a6, 0x240000da }, + { 0x210001a7, 0x24000001 }, + { 0x210001a8, 0x1400ffff }, + { 0x210001a9, 0x240000da }, + { 0x218001aa, 0x14000001 }, + { 0x210001ac, 0x24000001 }, + { 0x210001ad, 0x1400ffff }, + { 0x210001ae, 0x240000da }, + { 0x210001af, 0x24000001 }, + { 0x210001b0, 0x1400ffff }, + { 0x210001b1, 0x240000d9 }, + { 0x210001b2, 0x240000d9 }, + { 0x210001b3, 0x24000001 }, + { 0x210001b4, 0x1400ffff }, + { 0x210001b5, 0x24000001 }, + { 0x210001b6, 0x1400ffff }, + { 0x210001b7, 0x240000db }, + { 0x210001b8, 0x24000001 }, + { 0x210001b9, 0x1400ffff }, + { 0x210001ba, 0x14000000 }, + { 0x210001bb, 0x1c000000 }, + { 0x210001bc, 0x24000001 }, + { 0x210001bd, 0x1400ffff }, + { 0x210001be, 0x14000000 }, + { 0x210001bf, 0x14000038 }, + { 0x218001c0, 0x1c000003 }, + { 0x210001c4, 0x24000002 }, + { 0x210001c5, 0x2000ffff }, + { 0x210001c6, 0x1400fffe }, + { 0x210001c7, 0x24000002 }, + { 0x210001c8, 0x2000ffff }, + { 0x210001c9, 0x1400fffe }, + { 0x210001ca, 0x24000002 }, + { 0x210001cb, 0x2000ffff }, + { 0x210001cc, 0x1400fffe }, + { 0x210001cd, 0x24000001 }, + { 0x210001ce, 0x1400ffff }, + { 0x210001cf, 0x24000001 }, + { 0x210001d0, 0x1400ffff }, + { 0x210001d1, 0x24000001 }, + { 0x210001d2, 0x1400ffff }, + { 0x210001d3, 0x24000001 }, + { 0x210001d4, 0x1400ffff }, + { 0x210001d5, 0x24000001 }, + { 0x210001d6, 0x1400ffff }, + { 0x210001d7, 0x24000001 }, + { 0x210001d8, 0x1400ffff }, + { 0x210001d9, 0x24000001 }, + { 0x210001da, 0x1400ffff }, + { 0x210001db, 0x24000001 }, + { 0x210001dc, 0x1400ffff }, + { 0x210001dd, 0x1400ffb1 }, + { 0x210001de, 0x24000001 }, + { 0x210001df, 0x1400ffff }, + { 0x210001e0, 0x24000001 }, + { 0x210001e1, 0x1400ffff }, + { 0x210001e2, 0x24000001 }, + { 0x210001e3, 0x1400ffff }, + { 0x210001e4, 0x24000001 }, + { 0x210001e5, 0x1400ffff }, + { 0x210001e6, 0x24000001 }, + { 0x210001e7, 0x1400ffff }, + { 0x210001e8, 0x24000001 }, + { 0x210001e9, 0x1400ffff }, + { 0x210001ea, 0x24000001 }, + { 0x210001eb, 0x1400ffff }, + { 0x210001ec, 0x24000001 }, + { 0x210001ed, 0x1400ffff }, + { 0x210001ee, 0x24000001 }, + { 0x210001ef, 0x1400ffff }, + { 0x210001f0, 0x14000000 }, + { 0x210001f1, 0x24000002 }, + { 0x210001f2, 0x2000ffff }, + { 0x210001f3, 0x1400fffe }, + { 0x210001f4, 0x24000001 }, + { 0x210001f5, 0x1400ffff }, + { 0x210001f6, 0x2400ff9f }, + { 0x210001f7, 0x2400ffc8 }, + { 0x210001f8, 0x24000001 }, + { 0x210001f9, 0x1400ffff }, + { 0x210001fa, 0x24000001 }, + { 0x210001fb, 0x1400ffff }, + { 0x210001fc, 0x24000001 }, + { 0x210001fd, 0x1400ffff }, + { 0x210001fe, 0x24000001 }, + { 0x210001ff, 0x1400ffff }, + { 0x21000200, 0x24000001 }, + { 0x21000201, 0x1400ffff }, + { 0x21000202, 0x24000001 }, + { 0x21000203, 0x1400ffff }, + { 0x21000204, 0x24000001 }, + { 0x21000205, 0x1400ffff }, + { 0x21000206, 0x24000001 }, + { 0x21000207, 0x1400ffff }, + { 0x21000208, 0x24000001 }, + { 0x21000209, 0x1400ffff }, + { 0x2100020a, 0x24000001 }, + { 0x2100020b, 0x1400ffff }, + { 0x2100020c, 0x24000001 }, + { 0x2100020d, 0x1400ffff }, + { 0x2100020e, 0x24000001 }, + { 0x2100020f, 0x1400ffff }, + { 0x21000210, 0x24000001 }, + { 0x21000211, 0x1400ffff }, + { 0x21000212, 0x24000001 }, + { 0x21000213, 0x1400ffff }, + { 0x21000214, 0x24000001 }, + { 0x21000215, 0x1400ffff }, + { 0x21000216, 0x24000001 }, + { 0x21000217, 0x1400ffff }, + { 0x21000218, 0x24000001 }, + { 0x21000219, 0x1400ffff }, + { 0x2100021a, 0x24000001 }, + { 0x2100021b, 0x1400ffff }, + { 0x2100021c, 0x24000001 }, + { 0x2100021d, 0x1400ffff }, + { 0x2100021e, 0x24000001 }, + { 0x2100021f, 0x1400ffff }, + { 0x21000220, 0x2400ff7e }, + { 0x21000221, 0x14000000 }, + { 0x21000222, 0x24000001 }, + { 0x21000223, 0x1400ffff }, + { 0x21000224, 0x24000001 }, + { 0x21000225, 0x1400ffff }, + { 0x21000226, 0x24000001 }, + { 0x21000227, 0x1400ffff }, + { 0x21000228, 0x24000001 }, + { 0x21000229, 0x1400ffff }, + { 0x2100022a, 0x24000001 }, + { 0x2100022b, 0x1400ffff }, + { 0x2100022c, 0x24000001 }, + { 0x2100022d, 0x1400ffff }, + { 0x2100022e, 0x24000001 }, + { 0x2100022f, 0x1400ffff }, + { 0x21000230, 0x24000001 }, + { 0x21000231, 0x1400ffff }, + { 0x21000232, 0x24000001 }, + { 0x21000233, 0x1400ffff }, + { 0x21800234, 0x14000005 }, + { 0x2100023a, 0x24002a2b }, + { 0x2100023b, 0x24000001 }, + { 0x2100023c, 0x1400ffff }, + { 0x2100023d, 0x2400ff5d }, + { 0x2100023e, 0x24002a28 }, + { 0x2180023f, 0x14000001 }, + { 0x21000241, 0x24000001 }, + { 0x21000242, 0x1400ffff }, + { 0x21000243, 0x2400ff3d }, + { 0x21000244, 0x24000045 }, + { 0x21000245, 0x24000047 }, + { 0x21000246, 0x24000001 }, + { 0x21000247, 0x1400ffff }, + { 0x21000248, 0x24000001 }, + { 0x21000249, 0x1400ffff }, + { 0x2100024a, 0x24000001 }, + { 0x2100024b, 0x1400ffff }, + { 0x2100024c, 0x24000001 }, + { 0x2100024d, 0x1400ffff }, + { 0x2100024e, 0x24000001 }, + { 0x2100024f, 0x1400ffff }, + { 0x21800250, 0x14000002 }, + { 0x21000253, 0x1400ff2e }, + { 0x21000254, 0x1400ff32 }, + { 0x21000255, 0x14000000 }, + { 0x21000256, 0x1400ff33 }, + { 0x21000257, 0x1400ff33 }, + { 0x21000258, 0x14000000 }, + { 0x21000259, 0x1400ff36 }, + { 0x2100025a, 0x14000000 }, + { 0x2100025b, 0x1400ff35 }, + { 0x2180025c, 0x14000003 }, + { 0x21000260, 0x1400ff33 }, + { 0x21800261, 0x14000001 }, + { 0x21000263, 0x1400ff31 }, + { 0x21800264, 0x14000003 }, + { 0x21000268, 0x1400ff2f }, + { 0x21000269, 0x1400ff2d }, + { 0x2100026a, 0x14000000 }, + { 0x2100026b, 0x140029f7 }, + { 0x2180026c, 0x14000002 }, + { 0x2100026f, 0x1400ff2d }, + { 0x21800270, 0x14000001 }, + { 0x21000272, 0x1400ff2b }, + { 0x21800273, 0x14000001 }, + { 0x21000275, 0x1400ff2a }, + { 0x21800276, 0x14000006 }, + { 0x2100027d, 0x140029e7 }, + { 0x2180027e, 0x14000001 }, + { 0x21000280, 0x1400ff26 }, + { 0x21800281, 0x14000001 }, + { 0x21000283, 0x1400ff26 }, + { 0x21800284, 0x14000003 }, + { 0x21000288, 0x1400ff26 }, + { 0x21000289, 0x1400ffbb }, + { 0x2100028a, 0x1400ff27 }, + { 0x2100028b, 0x1400ff27 }, + { 0x2100028c, 0x1400ffb9 }, + { 0x2180028d, 0x14000004 }, + { 0x21000292, 0x1400ff25 }, + { 0x21000293, 0x14000000 }, + { 0x21000294, 0x1c000000 }, + { 0x21800295, 0x1400001a }, + { 0x218002b0, 0x18000008 }, + { 0x098002b9, 0x18000008 }, + { 0x098002c2, 0x60000003 }, + { 0x098002c6, 0x1800000b }, + { 0x098002d2, 0x6000000d }, + { 0x218002e0, 0x18000004 }, + { 0x098002e5, 0x60000008 }, + { 0x090002ee, 0x18000000 }, + { 0x098002ef, 0x60000010 }, + { 0x1b800300, 0x30000044 }, + { 0x1b000345, 0x30000054 }, + { 0x1b800346, 0x30000029 }, + { 0x13800374, 0x60000001 }, + { 0x1300037a, 0x18000000 }, + { 0x1300037b, 0x14000082 }, + { 0x1300037c, 0x14000082 }, + { 0x1300037d, 0x14000082 }, + { 0x0900037e, 0x54000000 }, + { 0x13800384, 0x60000001 }, + { 0x13000386, 0x24000026 }, + { 0x09000387, 0x54000000 }, + { 0x13000388, 0x24000025 }, + { 0x13000389, 0x24000025 }, + { 0x1300038a, 0x24000025 }, + { 0x1300038c, 0x24000040 }, + { 0x1300038e, 0x2400003f }, + { 0x1300038f, 0x2400003f }, + { 0x13000390, 0x14000000 }, + { 0x13000391, 0x24000020 }, + { 0x13000392, 0x24000020 }, + { 0x13000393, 0x24000020 }, + { 0x13000394, 0x24000020 }, + { 0x13000395, 0x24000020 }, + { 0x13000396, 0x24000020 }, + { 0x13000397, 0x24000020 }, + { 0x13000398, 0x24000020 }, + { 0x13000399, 0x24000020 }, + { 0x1300039a, 0x24000020 }, + { 0x1300039b, 0x24000020 }, + { 0x1300039c, 0x24000020 }, + { 0x1300039d, 0x24000020 }, + { 0x1300039e, 0x24000020 }, + { 0x1300039f, 0x24000020 }, + { 0x130003a0, 0x24000020 }, + { 0x130003a1, 0x24000020 }, + { 0x130003a3, 0x24000020 }, + { 0x130003a4, 0x24000020 }, + { 0x130003a5, 0x24000020 }, + { 0x130003a6, 0x24000020 }, + { 0x130003a7, 0x24000020 }, + { 0x130003a8, 0x24000020 }, + { 0x130003a9, 0x24000020 }, + { 0x130003aa, 0x24000020 }, + { 0x130003ab, 0x24000020 }, + { 0x130003ac, 0x1400ffda }, + { 0x130003ad, 0x1400ffdb }, + { 0x130003ae, 0x1400ffdb }, + { 0x130003af, 0x1400ffdb }, + { 0x130003b0, 0x14000000 }, + { 0x130003b1, 0x1400ffe0 }, + { 0x130003b2, 0x1400ffe0 }, + { 0x130003b3, 0x1400ffe0 }, + { 0x130003b4, 0x1400ffe0 }, + { 0x130003b5, 0x1400ffe0 }, + { 0x130003b6, 0x1400ffe0 }, + { 0x130003b7, 0x1400ffe0 }, + { 0x130003b8, 0x1400ffe0 }, + { 0x130003b9, 0x1400ffe0 }, + { 0x130003ba, 0x1400ffe0 }, + { 0x130003bb, 0x1400ffe0 }, + { 0x130003bc, 0x1400ffe0 }, + { 0x130003bd, 0x1400ffe0 }, + { 0x130003be, 0x1400ffe0 }, + { 0x130003bf, 0x1400ffe0 }, + { 0x130003c0, 0x1400ffe0 }, + { 0x130003c1, 0x1400ffe0 }, + { 0x130003c2, 0x1400ffe1 }, + { 0x130003c3, 0x1400ffe0 }, + { 0x130003c4, 0x1400ffe0 }, + { 0x130003c5, 0x1400ffe0 }, + { 0x130003c6, 0x1400ffe0 }, + { 0x130003c7, 0x1400ffe0 }, + { 0x130003c8, 0x1400ffe0 }, + { 0x130003c9, 0x1400ffe0 }, + { 0x130003ca, 0x1400ffe0 }, + { 0x130003cb, 0x1400ffe0 }, + { 0x130003cc, 0x1400ffc0 }, + { 0x130003cd, 0x1400ffc1 }, + { 0x130003ce, 0x1400ffc1 }, + { 0x130003d0, 0x1400ffc2 }, + { 0x130003d1, 0x1400ffc7 }, + { 0x138003d2, 0x24000002 }, + { 0x130003d5, 0x1400ffd1 }, + { 0x130003d6, 0x1400ffca }, + { 0x130003d7, 0x14000000 }, + { 0x130003d8, 0x24000001 }, + { 0x130003d9, 0x1400ffff }, + { 0x130003da, 0x24000001 }, + { 0x130003db, 0x1400ffff }, + { 0x130003dc, 0x24000001 }, + { 0x130003dd, 0x1400ffff }, + { 0x130003de, 0x24000001 }, + { 0x130003df, 0x1400ffff }, + { 0x130003e0, 0x24000001 }, + { 0x130003e1, 0x1400ffff }, + { 0x0a0003e2, 0x24000001 }, + { 0x0a0003e3, 0x1400ffff }, + { 0x0a0003e4, 0x24000001 }, + { 0x0a0003e5, 0x1400ffff }, + { 0x0a0003e6, 0x24000001 }, + { 0x0a0003e7, 0x1400ffff }, + { 0x0a0003e8, 0x24000001 }, + { 0x0a0003e9, 0x1400ffff }, + { 0x0a0003ea, 0x24000001 }, + { 0x0a0003eb, 0x1400ffff }, + { 0x0a0003ec, 0x24000001 }, + { 0x0a0003ed, 0x1400ffff }, + { 0x0a0003ee, 0x24000001 }, + { 0x0a0003ef, 0x1400ffff }, + { 0x130003f0, 0x1400ffaa }, + { 0x130003f1, 0x1400ffb0 }, + { 0x130003f2, 0x14000007 }, + { 0x130003f3, 0x14000000 }, + { 0x130003f4, 0x2400ffc4 }, + { 0x130003f5, 0x1400ffa0 }, + { 0x130003f6, 0x64000000 }, + { 0x130003f7, 0x24000001 }, + { 0x130003f8, 0x1400ffff }, + { 0x130003f9, 0x2400fff9 }, + { 0x130003fa, 0x24000001 }, + { 0x130003fb, 0x1400ffff }, + { 0x130003fc, 0x14000000 }, + { 0x130003fd, 0x2400ff7e }, + { 0x130003fe, 0x2400ff7e }, + { 0x130003ff, 0x2400ff7e }, + { 0x0c000400, 0x24000050 }, + { 0x0c000401, 0x24000050 }, + { 0x0c000402, 0x24000050 }, + { 0x0c000403, 0x24000050 }, + { 0x0c000404, 0x24000050 }, + { 0x0c000405, 0x24000050 }, + { 0x0c000406, 0x24000050 }, + { 0x0c000407, 0x24000050 }, + { 0x0c000408, 0x24000050 }, + { 0x0c000409, 0x24000050 }, + { 0x0c00040a, 0x24000050 }, + { 0x0c00040b, 0x24000050 }, + { 0x0c00040c, 0x24000050 }, + { 0x0c00040d, 0x24000050 }, + { 0x0c00040e, 0x24000050 }, + { 0x0c00040f, 0x24000050 }, + { 0x0c000410, 0x24000020 }, + { 0x0c000411, 0x24000020 }, + { 0x0c000412, 0x24000020 }, + { 0x0c000413, 0x24000020 }, + { 0x0c000414, 0x24000020 }, + { 0x0c000415, 0x24000020 }, + { 0x0c000416, 0x24000020 }, + { 0x0c000417, 0x24000020 }, + { 0x0c000418, 0x24000020 }, + { 0x0c000419, 0x24000020 }, + { 0x0c00041a, 0x24000020 }, + { 0x0c00041b, 0x24000020 }, + { 0x0c00041c, 0x24000020 }, + { 0x0c00041d, 0x24000020 }, + { 0x0c00041e, 0x24000020 }, + { 0x0c00041f, 0x24000020 }, + { 0x0c000420, 0x24000020 }, + { 0x0c000421, 0x24000020 }, + { 0x0c000422, 0x24000020 }, + { 0x0c000423, 0x24000020 }, + { 0x0c000424, 0x24000020 }, + { 0x0c000425, 0x24000020 }, + { 0x0c000426, 0x24000020 }, + { 0x0c000427, 0x24000020 }, + { 0x0c000428, 0x24000020 }, + { 0x0c000429, 0x24000020 }, + { 0x0c00042a, 0x24000020 }, + { 0x0c00042b, 0x24000020 }, + { 0x0c00042c, 0x24000020 }, + { 0x0c00042d, 0x24000020 }, + { 0x0c00042e, 0x24000020 }, + { 0x0c00042f, 0x24000020 }, + { 0x0c000430, 0x1400ffe0 }, + { 0x0c000431, 0x1400ffe0 }, + { 0x0c000432, 0x1400ffe0 }, + { 0x0c000433, 0x1400ffe0 }, + { 0x0c000434, 0x1400ffe0 }, + { 0x0c000435, 0x1400ffe0 }, + { 0x0c000436, 0x1400ffe0 }, + { 0x0c000437, 0x1400ffe0 }, + { 0x0c000438, 0x1400ffe0 }, + { 0x0c000439, 0x1400ffe0 }, + { 0x0c00043a, 0x1400ffe0 }, + { 0x0c00043b, 0x1400ffe0 }, + { 0x0c00043c, 0x1400ffe0 }, + { 0x0c00043d, 0x1400ffe0 }, + { 0x0c00043e, 0x1400ffe0 }, + { 0x0c00043f, 0x1400ffe0 }, + { 0x0c000440, 0x1400ffe0 }, + { 0x0c000441, 0x1400ffe0 }, + { 0x0c000442, 0x1400ffe0 }, + { 0x0c000443, 0x1400ffe0 }, + { 0x0c000444, 0x1400ffe0 }, + { 0x0c000445, 0x1400ffe0 }, + { 0x0c000446, 0x1400ffe0 }, + { 0x0c000447, 0x1400ffe0 }, + { 0x0c000448, 0x1400ffe0 }, + { 0x0c000449, 0x1400ffe0 }, + { 0x0c00044a, 0x1400ffe0 }, + { 0x0c00044b, 0x1400ffe0 }, + { 0x0c00044c, 0x1400ffe0 }, + { 0x0c00044d, 0x1400ffe0 }, + { 0x0c00044e, 0x1400ffe0 }, + { 0x0c00044f, 0x1400ffe0 }, + { 0x0c000450, 0x1400ffb0 }, + { 0x0c000451, 0x1400ffb0 }, + { 0x0c000452, 0x1400ffb0 }, + { 0x0c000453, 0x1400ffb0 }, + { 0x0c000454, 0x1400ffb0 }, + { 0x0c000455, 0x1400ffb0 }, + { 0x0c000456, 0x1400ffb0 }, + { 0x0c000457, 0x1400ffb0 }, + { 0x0c000458, 0x1400ffb0 }, + { 0x0c000459, 0x1400ffb0 }, + { 0x0c00045a, 0x1400ffb0 }, + { 0x0c00045b, 0x1400ffb0 }, + { 0x0c00045c, 0x1400ffb0 }, + { 0x0c00045d, 0x1400ffb0 }, + { 0x0c00045e, 0x1400ffb0 }, + { 0x0c00045f, 0x1400ffb0 }, + { 0x0c000460, 0x24000001 }, + { 0x0c000461, 0x1400ffff }, + { 0x0c000462, 0x24000001 }, + { 0x0c000463, 0x1400ffff }, + { 0x0c000464, 0x24000001 }, + { 0x0c000465, 0x1400ffff }, + { 0x0c000466, 0x24000001 }, + { 0x0c000467, 0x1400ffff }, + { 0x0c000468, 0x24000001 }, + { 0x0c000469, 0x1400ffff }, + { 0x0c00046a, 0x24000001 }, + { 0x0c00046b, 0x1400ffff }, + { 0x0c00046c, 0x24000001 }, + { 0x0c00046d, 0x1400ffff }, + { 0x0c00046e, 0x24000001 }, + { 0x0c00046f, 0x1400ffff }, + { 0x0c000470, 0x24000001 }, + { 0x0c000471, 0x1400ffff }, + { 0x0c000472, 0x24000001 }, + { 0x0c000473, 0x1400ffff }, + { 0x0c000474, 0x24000001 }, + { 0x0c000475, 0x1400ffff }, + { 0x0c000476, 0x24000001 }, + { 0x0c000477, 0x1400ffff }, + { 0x0c000478, 0x24000001 }, + { 0x0c000479, 0x1400ffff }, + { 0x0c00047a, 0x24000001 }, + { 0x0c00047b, 0x1400ffff }, + { 0x0c00047c, 0x24000001 }, + { 0x0c00047d, 0x1400ffff }, + { 0x0c00047e, 0x24000001 }, + { 0x0c00047f, 0x1400ffff }, + { 0x0c000480, 0x24000001 }, + { 0x0c000481, 0x1400ffff }, + { 0x0c000482, 0x68000000 }, + { 0x0c800483, 0x30000003 }, + { 0x0c800488, 0x2c000001 }, + { 0x0c00048a, 0x24000001 }, + { 0x0c00048b, 0x1400ffff }, + { 0x0c00048c, 0x24000001 }, + { 0x0c00048d, 0x1400ffff }, + { 0x0c00048e, 0x24000001 }, + { 0x0c00048f, 0x1400ffff }, + { 0x0c000490, 0x24000001 }, + { 0x0c000491, 0x1400ffff }, + { 0x0c000492, 0x24000001 }, + { 0x0c000493, 0x1400ffff }, + { 0x0c000494, 0x24000001 }, + { 0x0c000495, 0x1400ffff }, + { 0x0c000496, 0x24000001 }, + { 0x0c000497, 0x1400ffff }, + { 0x0c000498, 0x24000001 }, + { 0x0c000499, 0x1400ffff }, + { 0x0c00049a, 0x24000001 }, + { 0x0c00049b, 0x1400ffff }, + { 0x0c00049c, 0x24000001 }, + { 0x0c00049d, 0x1400ffff }, + { 0x0c00049e, 0x24000001 }, + { 0x0c00049f, 0x1400ffff }, + { 0x0c0004a0, 0x24000001 }, + { 0x0c0004a1, 0x1400ffff }, + { 0x0c0004a2, 0x24000001 }, + { 0x0c0004a3, 0x1400ffff }, + { 0x0c0004a4, 0x24000001 }, + { 0x0c0004a5, 0x1400ffff }, + { 0x0c0004a6, 0x24000001 }, + { 0x0c0004a7, 0x1400ffff }, + { 0x0c0004a8, 0x24000001 }, + { 0x0c0004a9, 0x1400ffff }, + { 0x0c0004aa, 0x24000001 }, + { 0x0c0004ab, 0x1400ffff }, + { 0x0c0004ac, 0x24000001 }, + { 0x0c0004ad, 0x1400ffff }, + { 0x0c0004ae, 0x24000001 }, + { 0x0c0004af, 0x1400ffff }, + { 0x0c0004b0, 0x24000001 }, + { 0x0c0004b1, 0x1400ffff }, + { 0x0c0004b2, 0x24000001 }, + { 0x0c0004b3, 0x1400ffff }, + { 0x0c0004b4, 0x24000001 }, + { 0x0c0004b5, 0x1400ffff }, + { 0x0c0004b6, 0x24000001 }, + { 0x0c0004b7, 0x1400ffff }, + { 0x0c0004b8, 0x24000001 }, + { 0x0c0004b9, 0x1400ffff }, + { 0x0c0004ba, 0x24000001 }, + { 0x0c0004bb, 0x1400ffff }, + { 0x0c0004bc, 0x24000001 }, + { 0x0c0004bd, 0x1400ffff }, + { 0x0c0004be, 0x24000001 }, + { 0x0c0004bf, 0x1400ffff }, + { 0x0c0004c0, 0x2400000f }, + { 0x0c0004c1, 0x24000001 }, + { 0x0c0004c2, 0x1400ffff }, + { 0x0c0004c3, 0x24000001 }, + { 0x0c0004c4, 0x1400ffff }, + { 0x0c0004c5, 0x24000001 }, + { 0x0c0004c6, 0x1400ffff }, + { 0x0c0004c7, 0x24000001 }, + { 0x0c0004c8, 0x1400ffff }, + { 0x0c0004c9, 0x24000001 }, + { 0x0c0004ca, 0x1400ffff }, + { 0x0c0004cb, 0x24000001 }, + { 0x0c0004cc, 0x1400ffff }, + { 0x0c0004cd, 0x24000001 }, + { 0x0c0004ce, 0x1400ffff }, + { 0x0c0004cf, 0x1400fff1 }, + { 0x0c0004d0, 0x24000001 }, + { 0x0c0004d1, 0x1400ffff }, + { 0x0c0004d2, 0x24000001 }, + { 0x0c0004d3, 0x1400ffff }, + { 0x0c0004d4, 0x24000001 }, + { 0x0c0004d5, 0x1400ffff }, + { 0x0c0004d6, 0x24000001 }, + { 0x0c0004d7, 0x1400ffff }, + { 0x0c0004d8, 0x24000001 }, + { 0x0c0004d9, 0x1400ffff }, + { 0x0c0004da, 0x24000001 }, + { 0x0c0004db, 0x1400ffff }, + { 0x0c0004dc, 0x24000001 }, + { 0x0c0004dd, 0x1400ffff }, + { 0x0c0004de, 0x24000001 }, + { 0x0c0004df, 0x1400ffff }, + { 0x0c0004e0, 0x24000001 }, + { 0x0c0004e1, 0x1400ffff }, + { 0x0c0004e2, 0x24000001 }, + { 0x0c0004e3, 0x1400ffff }, + { 0x0c0004e4, 0x24000001 }, + { 0x0c0004e5, 0x1400ffff }, + { 0x0c0004e6, 0x24000001 }, + { 0x0c0004e7, 0x1400ffff }, + { 0x0c0004e8, 0x24000001 }, + { 0x0c0004e9, 0x1400ffff }, + { 0x0c0004ea, 0x24000001 }, + { 0x0c0004eb, 0x1400ffff }, + { 0x0c0004ec, 0x24000001 }, + { 0x0c0004ed, 0x1400ffff }, + { 0x0c0004ee, 0x24000001 }, + { 0x0c0004ef, 0x1400ffff }, + { 0x0c0004f0, 0x24000001 }, + { 0x0c0004f1, 0x1400ffff }, + { 0x0c0004f2, 0x24000001 }, + { 0x0c0004f3, 0x1400ffff }, + { 0x0c0004f4, 0x24000001 }, + { 0x0c0004f5, 0x1400ffff }, + { 0x0c0004f6, 0x24000001 }, + { 0x0c0004f7, 0x1400ffff }, + { 0x0c0004f8, 0x24000001 }, + { 0x0c0004f9, 0x1400ffff }, + { 0x0c0004fa, 0x24000001 }, + { 0x0c0004fb, 0x1400ffff }, + { 0x0c0004fc, 0x24000001 }, + { 0x0c0004fd, 0x1400ffff }, + { 0x0c0004fe, 0x24000001 }, + { 0x0c0004ff, 0x1400ffff }, + { 0x0c000500, 0x24000001 }, + { 0x0c000501, 0x1400ffff }, + { 0x0c000502, 0x24000001 }, + { 0x0c000503, 0x1400ffff }, + { 0x0c000504, 0x24000001 }, + { 0x0c000505, 0x1400ffff }, + { 0x0c000506, 0x24000001 }, + { 0x0c000507, 0x1400ffff }, + { 0x0c000508, 0x24000001 }, + { 0x0c000509, 0x1400ffff }, + { 0x0c00050a, 0x24000001 }, + { 0x0c00050b, 0x1400ffff }, + { 0x0c00050c, 0x24000001 }, + { 0x0c00050d, 0x1400ffff }, + { 0x0c00050e, 0x24000001 }, + { 0x0c00050f, 0x1400ffff }, + { 0x0c000510, 0x24000001 }, + { 0x0c000511, 0x1400ffff }, + { 0x0c000512, 0x24000001 }, + { 0x0c000513, 0x1400ffff }, + { 0x01000531, 0x24000030 }, + { 0x01000532, 0x24000030 }, + { 0x01000533, 0x24000030 }, + { 0x01000534, 0x24000030 }, + { 0x01000535, 0x24000030 }, + { 0x01000536, 0x24000030 }, + { 0x01000537, 0x24000030 }, + { 0x01000538, 0x24000030 }, + { 0x01000539, 0x24000030 }, + { 0x0100053a, 0x24000030 }, + { 0x0100053b, 0x24000030 }, + { 0x0100053c, 0x24000030 }, + { 0x0100053d, 0x24000030 }, + { 0x0100053e, 0x24000030 }, + { 0x0100053f, 0x24000030 }, + { 0x01000540, 0x24000030 }, + { 0x01000541, 0x24000030 }, + { 0x01000542, 0x24000030 }, + { 0x01000543, 0x24000030 }, + { 0x01000544, 0x24000030 }, + { 0x01000545, 0x24000030 }, + { 0x01000546, 0x24000030 }, + { 0x01000547, 0x24000030 }, + { 0x01000548, 0x24000030 }, + { 0x01000549, 0x24000030 }, + { 0x0100054a, 0x24000030 }, + { 0x0100054b, 0x24000030 }, + { 0x0100054c, 0x24000030 }, + { 0x0100054d, 0x24000030 }, + { 0x0100054e, 0x24000030 }, + { 0x0100054f, 0x24000030 }, + { 0x01000550, 0x24000030 }, + { 0x01000551, 0x24000030 }, + { 0x01000552, 0x24000030 }, + { 0x01000553, 0x24000030 }, + { 0x01000554, 0x24000030 }, + { 0x01000555, 0x24000030 }, + { 0x01000556, 0x24000030 }, + { 0x01000559, 0x18000000 }, + { 0x0180055a, 0x54000005 }, + { 0x01000561, 0x1400ffd0 }, + { 0x01000562, 0x1400ffd0 }, + { 0x01000563, 0x1400ffd0 }, + { 0x01000564, 0x1400ffd0 }, + { 0x01000565, 0x1400ffd0 }, + { 0x01000566, 0x1400ffd0 }, + { 0x01000567, 0x1400ffd0 }, + { 0x01000568, 0x1400ffd0 }, + { 0x01000569, 0x1400ffd0 }, + { 0x0100056a, 0x1400ffd0 }, + { 0x0100056b, 0x1400ffd0 }, + { 0x0100056c, 0x1400ffd0 }, + { 0x0100056d, 0x1400ffd0 }, + { 0x0100056e, 0x1400ffd0 }, + { 0x0100056f, 0x1400ffd0 }, + { 0x01000570, 0x1400ffd0 }, + { 0x01000571, 0x1400ffd0 }, + { 0x01000572, 0x1400ffd0 }, + { 0x01000573, 0x1400ffd0 }, + { 0x01000574, 0x1400ffd0 }, + { 0x01000575, 0x1400ffd0 }, + { 0x01000576, 0x1400ffd0 }, + { 0x01000577, 0x1400ffd0 }, + { 0x01000578, 0x1400ffd0 }, + { 0x01000579, 0x1400ffd0 }, + { 0x0100057a, 0x1400ffd0 }, + { 0x0100057b, 0x1400ffd0 }, + { 0x0100057c, 0x1400ffd0 }, + { 0x0100057d, 0x1400ffd0 }, + { 0x0100057e, 0x1400ffd0 }, + { 0x0100057f, 0x1400ffd0 }, + { 0x01000580, 0x1400ffd0 }, + { 0x01000581, 0x1400ffd0 }, + { 0x01000582, 0x1400ffd0 }, + { 0x01000583, 0x1400ffd0 }, + { 0x01000584, 0x1400ffd0 }, + { 0x01000585, 0x1400ffd0 }, + { 0x01000586, 0x1400ffd0 }, + { 0x01000587, 0x14000000 }, + { 0x09000589, 0x54000000 }, + { 0x0100058a, 0x44000000 }, + { 0x19800591, 0x3000002c }, + { 0x190005be, 0x54000000 }, + { 0x190005bf, 0x30000000 }, + { 0x190005c0, 0x54000000 }, + { 0x198005c1, 0x30000001 }, + { 0x190005c3, 0x54000000 }, + { 0x198005c4, 0x30000001 }, + { 0x190005c6, 0x54000000 }, + { 0x190005c7, 0x30000000 }, + { 0x198005d0, 0x1c00001a }, + { 0x198005f0, 0x1c000002 }, + { 0x198005f3, 0x54000001 }, + { 0x09800600, 0x04000003 }, + { 0x0000060b, 0x5c000000 }, + { 0x0900060c, 0x54000000 }, + { 0x0000060d, 0x54000000 }, + { 0x0080060e, 0x68000001 }, + { 0x00800610, 0x30000005 }, + { 0x0900061b, 0x54000000 }, + { 0x0000061e, 0x54000000 }, + { 0x0900061f, 0x54000000 }, + { 0x00800621, 0x1c000019 }, + { 0x09000640, 0x18000000 }, + { 0x00800641, 0x1c000009 }, + { 0x1b80064b, 0x3000000a }, + { 0x00800656, 0x30000008 }, + { 0x09800660, 0x34000009 }, + { 0x0080066a, 0x54000003 }, + { 0x0080066e, 0x1c000001 }, + { 0x1b000670, 0x30000000 }, + { 0x00800671, 0x1c000062 }, + { 0x000006d4, 0x54000000 }, + { 0x000006d5, 0x1c000000 }, + { 0x008006d6, 0x30000006 }, + { 0x090006dd, 0x04000000 }, + { 0x000006de, 0x2c000000 }, + { 0x008006df, 0x30000005 }, + { 0x008006e5, 0x18000001 }, + { 0x008006e7, 0x30000001 }, + { 0x000006e9, 0x68000000 }, + { 0x008006ea, 0x30000003 }, + { 0x008006ee, 0x1c000001 }, + { 0x008006f0, 0x34000009 }, + { 0x008006fa, 0x1c000002 }, + { 0x008006fd, 0x68000001 }, + { 0x000006ff, 0x1c000000 }, + { 0x31800700, 0x5400000d }, + { 0x3100070f, 0x04000000 }, + { 0x31000710, 0x1c000000 }, + { 0x31000711, 0x30000000 }, + { 0x31800712, 0x1c00001d }, + { 0x31800730, 0x3000001a }, + { 0x3180074d, 0x1c000002 }, + { 0x00800750, 0x1c00001d }, + { 0x37800780, 0x1c000025 }, + { 0x378007a6, 0x3000000a }, + { 0x370007b1, 0x1c000000 }, + { 0x3f8007c0, 0x34000009 }, + { 0x3f8007ca, 0x1c000020 }, + { 0x3f8007eb, 0x30000008 }, + { 0x3f8007f4, 0x18000001 }, + { 0x3f0007f6, 0x68000000 }, + { 0x3f8007f7, 0x54000002 }, + { 0x3f0007fa, 0x18000000 }, + { 0x0e800901, 0x30000001 }, + { 0x0e000903, 0x28000000 }, + { 0x0e800904, 0x1c000035 }, + { 0x0e00093c, 0x30000000 }, + { 0x0e00093d, 0x1c000000 }, + { 0x0e80093e, 0x28000002 }, + { 0x0e800941, 0x30000007 }, + { 0x0e800949, 0x28000003 }, + { 0x0e00094d, 0x30000000 }, + { 0x0e000950, 0x1c000000 }, + { 0x0e800951, 0x30000003 }, + { 0x0e800958, 0x1c000009 }, + { 0x0e800962, 0x30000001 }, + { 0x09800964, 0x54000001 }, + { 0x0e800966, 0x34000009 }, + { 0x09000970, 0x54000000 }, + { 0x0e80097b, 0x1c000004 }, + { 0x02000981, 0x30000000 }, + { 0x02800982, 0x28000001 }, + { 0x02800985, 0x1c000007 }, + { 0x0280098f, 0x1c000001 }, + { 0x02800993, 0x1c000015 }, + { 0x028009aa, 0x1c000006 }, + { 0x020009b2, 0x1c000000 }, + { 0x028009b6, 0x1c000003 }, + { 0x020009bc, 0x30000000 }, + { 0x020009bd, 0x1c000000 }, + { 0x028009be, 0x28000002 }, + { 0x028009c1, 0x30000003 }, + { 0x028009c7, 0x28000001 }, + { 0x028009cb, 0x28000001 }, + { 0x020009cd, 0x30000000 }, + { 0x020009ce, 0x1c000000 }, + { 0x020009d7, 0x28000000 }, + { 0x028009dc, 0x1c000001 }, + { 0x028009df, 0x1c000002 }, + { 0x028009e2, 0x30000001 }, + { 0x028009e6, 0x34000009 }, + { 0x028009f0, 0x1c000001 }, + { 0x028009f2, 0x5c000001 }, + { 0x028009f4, 0x3c000005 }, + { 0x020009fa, 0x68000000 }, + { 0x15800a01, 0x30000001 }, + { 0x15000a03, 0x28000000 }, + { 0x15800a05, 0x1c000005 }, + { 0x15800a0f, 0x1c000001 }, + { 0x15800a13, 0x1c000015 }, + { 0x15800a2a, 0x1c000006 }, + { 0x15800a32, 0x1c000001 }, + { 0x15800a35, 0x1c000001 }, + { 0x15800a38, 0x1c000001 }, + { 0x15000a3c, 0x30000000 }, + { 0x15800a3e, 0x28000002 }, + { 0x15800a41, 0x30000001 }, + { 0x15800a47, 0x30000001 }, + { 0x15800a4b, 0x30000002 }, + { 0x15800a59, 0x1c000003 }, + { 0x15000a5e, 0x1c000000 }, + { 0x15800a66, 0x34000009 }, + { 0x15800a70, 0x30000001 }, + { 0x15800a72, 0x1c000002 }, + { 0x14800a81, 0x30000001 }, + { 0x14000a83, 0x28000000 }, + { 0x14800a85, 0x1c000008 }, + { 0x14800a8f, 0x1c000002 }, + { 0x14800a93, 0x1c000015 }, + { 0x14800aaa, 0x1c000006 }, + { 0x14800ab2, 0x1c000001 }, + { 0x14800ab5, 0x1c000004 }, + { 0x14000abc, 0x30000000 }, + { 0x14000abd, 0x1c000000 }, + { 0x14800abe, 0x28000002 }, + { 0x14800ac1, 0x30000004 }, + { 0x14800ac7, 0x30000001 }, + { 0x14000ac9, 0x28000000 }, + { 0x14800acb, 0x28000001 }, + { 0x14000acd, 0x30000000 }, + { 0x14000ad0, 0x1c000000 }, + { 0x14800ae0, 0x1c000001 }, + { 0x14800ae2, 0x30000001 }, + { 0x14800ae6, 0x34000009 }, + { 0x14000af1, 0x5c000000 }, + { 0x2b000b01, 0x30000000 }, + { 0x2b800b02, 0x28000001 }, + { 0x2b800b05, 0x1c000007 }, + { 0x2b800b0f, 0x1c000001 }, + { 0x2b800b13, 0x1c000015 }, + { 0x2b800b2a, 0x1c000006 }, + { 0x2b800b32, 0x1c000001 }, + { 0x2b800b35, 0x1c000004 }, + { 0x2b000b3c, 0x30000000 }, + { 0x2b000b3d, 0x1c000000 }, + { 0x2b000b3e, 0x28000000 }, + { 0x2b000b3f, 0x30000000 }, + { 0x2b000b40, 0x28000000 }, + { 0x2b800b41, 0x30000002 }, + { 0x2b800b47, 0x28000001 }, + { 0x2b800b4b, 0x28000001 }, + { 0x2b000b4d, 0x30000000 }, + { 0x2b000b56, 0x30000000 }, + { 0x2b000b57, 0x28000000 }, + { 0x2b800b5c, 0x1c000001 }, + { 0x2b800b5f, 0x1c000002 }, + { 0x2b800b66, 0x34000009 }, + { 0x2b000b70, 0x68000000 }, + { 0x2b000b71, 0x1c000000 }, + { 0x35000b82, 0x30000000 }, + { 0x35000b83, 0x1c000000 }, + { 0x35800b85, 0x1c000005 }, + { 0x35800b8e, 0x1c000002 }, + { 0x35800b92, 0x1c000003 }, + { 0x35800b99, 0x1c000001 }, + { 0x35000b9c, 0x1c000000 }, + { 0x35800b9e, 0x1c000001 }, + { 0x35800ba3, 0x1c000001 }, + { 0x35800ba8, 0x1c000002 }, + { 0x35800bae, 0x1c00000b }, + { 0x35800bbe, 0x28000001 }, + { 0x35000bc0, 0x30000000 }, + { 0x35800bc1, 0x28000001 }, + { 0x35800bc6, 0x28000002 }, + { 0x35800bca, 0x28000002 }, + { 0x35000bcd, 0x30000000 }, + { 0x35000bd7, 0x28000000 }, + { 0x35800be6, 0x34000009 }, + { 0x35800bf0, 0x3c000002 }, + { 0x35800bf3, 0x68000005 }, + { 0x35000bf9, 0x5c000000 }, + { 0x35000bfa, 0x68000000 }, + { 0x36800c01, 0x28000002 }, + { 0x36800c05, 0x1c000007 }, + { 0x36800c0e, 0x1c000002 }, + { 0x36800c12, 0x1c000016 }, + { 0x36800c2a, 0x1c000009 }, + { 0x36800c35, 0x1c000004 }, + { 0x36800c3e, 0x30000002 }, + { 0x36800c41, 0x28000003 }, + { 0x36800c46, 0x30000002 }, + { 0x36800c4a, 0x30000003 }, + { 0x36800c55, 0x30000001 }, + { 0x36800c60, 0x1c000001 }, + { 0x36800c66, 0x34000009 }, + { 0x1c800c82, 0x28000001 }, + { 0x1c800c85, 0x1c000007 }, + { 0x1c800c8e, 0x1c000002 }, + { 0x1c800c92, 0x1c000016 }, + { 0x1c800caa, 0x1c000009 }, + { 0x1c800cb5, 0x1c000004 }, + { 0x1c000cbc, 0x30000000 }, + { 0x1c000cbd, 0x1c000000 }, + { 0x1c000cbe, 0x28000000 }, + { 0x1c000cbf, 0x30000000 }, + { 0x1c800cc0, 0x28000004 }, + { 0x1c000cc6, 0x30000000 }, + { 0x1c800cc7, 0x28000001 }, + { 0x1c800cca, 0x28000001 }, + { 0x1c800ccc, 0x30000001 }, + { 0x1c800cd5, 0x28000001 }, + { 0x1c000cde, 0x1c000000 }, + { 0x1c800ce0, 0x1c000001 }, + { 0x1c800ce2, 0x30000001 }, + { 0x1c800ce6, 0x34000009 }, + { 0x1c800cf1, 0x68000001 }, + { 0x24800d02, 0x28000001 }, + { 0x24800d05, 0x1c000007 }, + { 0x24800d0e, 0x1c000002 }, + { 0x24800d12, 0x1c000016 }, + { 0x24800d2a, 0x1c00000f }, + { 0x24800d3e, 0x28000002 }, + { 0x24800d41, 0x30000002 }, + { 0x24800d46, 0x28000002 }, + { 0x24800d4a, 0x28000002 }, + { 0x24000d4d, 0x30000000 }, + { 0x24000d57, 0x28000000 }, + { 0x24800d60, 0x1c000001 }, + { 0x24800d66, 0x34000009 }, + { 0x2f800d82, 0x28000001 }, + { 0x2f800d85, 0x1c000011 }, + { 0x2f800d9a, 0x1c000017 }, + { 0x2f800db3, 0x1c000008 }, + { 0x2f000dbd, 0x1c000000 }, + { 0x2f800dc0, 0x1c000006 }, + { 0x2f000dca, 0x30000000 }, + { 0x2f800dcf, 0x28000002 }, + { 0x2f800dd2, 0x30000002 }, + { 0x2f000dd6, 0x30000000 }, + { 0x2f800dd8, 0x28000007 }, + { 0x2f800df2, 0x28000001 }, + { 0x2f000df4, 0x54000000 }, + { 0x38800e01, 0x1c00002f }, + { 0x38000e31, 0x30000000 }, + { 0x38800e32, 0x1c000001 }, + { 0x38800e34, 0x30000006 }, + { 0x09000e3f, 0x5c000000 }, + { 0x38800e40, 0x1c000005 }, + { 0x38000e46, 0x18000000 }, + { 0x38800e47, 0x30000007 }, + { 0x38000e4f, 0x54000000 }, + { 0x38800e50, 0x34000009 }, + { 0x38800e5a, 0x54000001 }, + { 0x20800e81, 0x1c000001 }, + { 0x20000e84, 0x1c000000 }, + { 0x20800e87, 0x1c000001 }, + { 0x20000e8a, 0x1c000000 }, + { 0x20000e8d, 0x1c000000 }, + { 0x20800e94, 0x1c000003 }, + { 0x20800e99, 0x1c000006 }, + { 0x20800ea1, 0x1c000002 }, + { 0x20000ea5, 0x1c000000 }, + { 0x20000ea7, 0x1c000000 }, + { 0x20800eaa, 0x1c000001 }, + { 0x20800ead, 0x1c000003 }, + { 0x20000eb1, 0x30000000 }, + { 0x20800eb2, 0x1c000001 }, + { 0x20800eb4, 0x30000005 }, + { 0x20800ebb, 0x30000001 }, + { 0x20000ebd, 0x1c000000 }, + { 0x20800ec0, 0x1c000004 }, + { 0x20000ec6, 0x18000000 }, + { 0x20800ec8, 0x30000005 }, + { 0x20800ed0, 0x34000009 }, + { 0x20800edc, 0x1c000001 }, + { 0x39000f00, 0x1c000000 }, + { 0x39800f01, 0x68000002 }, + { 0x39800f04, 0x5400000e }, + { 0x39800f13, 0x68000004 }, + { 0x39800f18, 0x30000001 }, + { 0x39800f1a, 0x68000005 }, + { 0x39800f20, 0x34000009 }, + { 0x39800f2a, 0x3c000009 }, + { 0x39000f34, 0x68000000 }, + { 0x39000f35, 0x30000000 }, + { 0x39000f36, 0x68000000 }, + { 0x39000f37, 0x30000000 }, + { 0x39000f38, 0x68000000 }, + { 0x39000f39, 0x30000000 }, + { 0x39000f3a, 0x58000000 }, + { 0x39000f3b, 0x48000000 }, + { 0x39000f3c, 0x58000000 }, + { 0x39000f3d, 0x48000000 }, + { 0x39800f3e, 0x28000001 }, + { 0x39800f40, 0x1c000007 }, + { 0x39800f49, 0x1c000021 }, + { 0x39800f71, 0x3000000d }, + { 0x39000f7f, 0x28000000 }, + { 0x39800f80, 0x30000004 }, + { 0x39000f85, 0x54000000 }, + { 0x39800f86, 0x30000001 }, + { 0x39800f88, 0x1c000003 }, + { 0x39800f90, 0x30000007 }, + { 0x39800f99, 0x30000023 }, + { 0x39800fbe, 0x68000007 }, + { 0x39000fc6, 0x30000000 }, + { 0x39800fc7, 0x68000005 }, + { 0x39000fcf, 0x68000000 }, + { 0x39800fd0, 0x54000001 }, + { 0x26801000, 0x1c000021 }, + { 0x26801023, 0x1c000004 }, + { 0x26801029, 0x1c000001 }, + { 0x2600102c, 0x28000000 }, + { 0x2680102d, 0x30000003 }, + { 0x26001031, 0x28000000 }, + { 0x26001032, 0x30000000 }, + { 0x26801036, 0x30000001 }, + { 0x26001038, 0x28000000 }, + { 0x26001039, 0x30000000 }, + { 0x26801040, 0x34000009 }, + { 0x2680104a, 0x54000005 }, + { 0x26801050, 0x1c000005 }, + { 0x26801056, 0x28000001 }, + { 0x26801058, 0x30000001 }, + { 0x100010a0, 0x24001c60 }, + { 0x100010a1, 0x24001c60 }, + { 0x100010a2, 0x24001c60 }, + { 0x100010a3, 0x24001c60 }, + { 0x100010a4, 0x24001c60 }, + { 0x100010a5, 0x24001c60 }, + { 0x100010a6, 0x24001c60 }, + { 0x100010a7, 0x24001c60 }, + { 0x100010a8, 0x24001c60 }, + { 0x100010a9, 0x24001c60 }, + { 0x100010aa, 0x24001c60 }, + { 0x100010ab, 0x24001c60 }, + { 0x100010ac, 0x24001c60 }, + { 0x100010ad, 0x24001c60 }, + { 0x100010ae, 0x24001c60 }, + { 0x100010af, 0x24001c60 }, + { 0x100010b0, 0x24001c60 }, + { 0x100010b1, 0x24001c60 }, + { 0x100010b2, 0x24001c60 }, + { 0x100010b3, 0x24001c60 }, + { 0x100010b4, 0x24001c60 }, + { 0x100010b5, 0x24001c60 }, + { 0x100010b6, 0x24001c60 }, + { 0x100010b7, 0x24001c60 }, + { 0x100010b8, 0x24001c60 }, + { 0x100010b9, 0x24001c60 }, + { 0x100010ba, 0x24001c60 }, + { 0x100010bb, 0x24001c60 }, + { 0x100010bc, 0x24001c60 }, + { 0x100010bd, 0x24001c60 }, + { 0x100010be, 0x24001c60 }, + { 0x100010bf, 0x24001c60 }, + { 0x100010c0, 0x24001c60 }, + { 0x100010c1, 0x24001c60 }, + { 0x100010c2, 0x24001c60 }, + { 0x100010c3, 0x24001c60 }, + { 0x100010c4, 0x24001c60 }, + { 0x100010c5, 0x24001c60 }, + { 0x108010d0, 0x1c00002a }, + { 0x090010fb, 0x54000000 }, + { 0x100010fc, 0x18000000 }, + { 0x17801100, 0x1c000059 }, + { 0x1780115f, 0x1c000043 }, + { 0x178011a8, 0x1c000051 }, + { 0x0f801200, 0x1c000048 }, + { 0x0f80124a, 0x1c000003 }, + { 0x0f801250, 0x1c000006 }, + { 0x0f001258, 0x1c000000 }, + { 0x0f80125a, 0x1c000003 }, + { 0x0f801260, 0x1c000028 }, + { 0x0f80128a, 0x1c000003 }, + { 0x0f801290, 0x1c000020 }, + { 0x0f8012b2, 0x1c000003 }, + { 0x0f8012b8, 0x1c000006 }, + { 0x0f0012c0, 0x1c000000 }, + { 0x0f8012c2, 0x1c000003 }, + { 0x0f8012c8, 0x1c00000e }, + { 0x0f8012d8, 0x1c000038 }, + { 0x0f801312, 0x1c000003 }, + { 0x0f801318, 0x1c000042 }, + { 0x0f00135f, 0x30000000 }, + { 0x0f001360, 0x68000000 }, + { 0x0f801361, 0x54000007 }, + { 0x0f801369, 0x3c000013 }, + { 0x0f801380, 0x1c00000f }, + { 0x0f801390, 0x68000009 }, + { 0x088013a0, 0x1c000054 }, + { 0x07801401, 0x1c00026b }, + { 0x0780166d, 0x54000001 }, + { 0x0780166f, 0x1c000007 }, + { 0x28001680, 0x74000000 }, + { 0x28801681, 0x1c000019 }, + { 0x2800169b, 0x58000000 }, + { 0x2800169c, 0x48000000 }, + { 0x2d8016a0, 0x1c00004a }, + { 0x098016eb, 0x54000002 }, + { 0x2d8016ee, 0x38000002 }, + { 0x32801700, 0x1c00000c }, + { 0x3280170e, 0x1c000003 }, + { 0x32801712, 0x30000002 }, + { 0x18801720, 0x1c000011 }, + { 0x18801732, 0x30000002 }, + { 0x09801735, 0x54000001 }, + { 0x06801740, 0x1c000011 }, + { 0x06801752, 0x30000001 }, + { 0x33801760, 0x1c00000c }, + { 0x3380176e, 0x1c000002 }, + { 0x33801772, 0x30000001 }, + { 0x1f801780, 0x1c000033 }, + { 0x1f8017b4, 0x04000001 }, + { 0x1f0017b6, 0x28000000 }, + { 0x1f8017b7, 0x30000006 }, + { 0x1f8017be, 0x28000007 }, + { 0x1f0017c6, 0x30000000 }, + { 0x1f8017c7, 0x28000001 }, + { 0x1f8017c9, 0x3000000a }, + { 0x1f8017d4, 0x54000002 }, + { 0x1f0017d7, 0x18000000 }, + { 0x1f8017d8, 0x54000002 }, + { 0x1f0017db, 0x5c000000 }, + { 0x1f0017dc, 0x1c000000 }, + { 0x1f0017dd, 0x30000000 }, + { 0x1f8017e0, 0x34000009 }, + { 0x1f8017f0, 0x3c000009 }, + { 0x25801800, 0x54000001 }, + { 0x09801802, 0x54000001 }, + { 0x25001804, 0x54000000 }, + { 0x09001805, 0x54000000 }, + { 0x25001806, 0x44000000 }, + { 0x25801807, 0x54000003 }, + { 0x2580180b, 0x30000002 }, + { 0x2500180e, 0x74000000 }, + { 0x25801810, 0x34000009 }, + { 0x25801820, 0x1c000022 }, + { 0x25001843, 0x18000000 }, + { 0x25801844, 0x1c000033 }, + { 0x25801880, 0x1c000028 }, + { 0x250018a9, 0x30000000 }, + { 0x22801900, 0x1c00001c }, + { 0x22801920, 0x30000002 }, + { 0x22801923, 0x28000003 }, + { 0x22801927, 0x30000001 }, + { 0x22801929, 0x28000002 }, + { 0x22801930, 0x28000001 }, + { 0x22001932, 0x30000000 }, + { 0x22801933, 0x28000005 }, + { 0x22801939, 0x30000002 }, + { 0x22001940, 0x68000000 }, + { 0x22801944, 0x54000001 }, + { 0x22801946, 0x34000009 }, + { 0x34801950, 0x1c00001d }, + { 0x34801970, 0x1c000004 }, + { 0x27801980, 0x1c000029 }, + { 0x278019b0, 0x28000010 }, + { 0x278019c1, 0x1c000006 }, + { 0x278019c8, 0x28000001 }, + { 0x278019d0, 0x34000009 }, + { 0x278019de, 0x54000001 }, + { 0x1f8019e0, 0x6800001f }, + { 0x05801a00, 0x1c000016 }, + { 0x05801a17, 0x30000001 }, + { 0x05801a19, 0x28000002 }, + { 0x05801a1e, 0x54000001 }, + { 0x3d801b00, 0x30000003 }, + { 0x3d001b04, 0x28000000 }, + { 0x3d801b05, 0x1c00002e }, + { 0x3d001b34, 0x30000000 }, + { 0x3d001b35, 0x28000000 }, + { 0x3d801b36, 0x30000004 }, + { 0x3d001b3b, 0x28000000 }, + { 0x3d001b3c, 0x30000000 }, + { 0x3d801b3d, 0x28000004 }, + { 0x3d001b42, 0x30000000 }, + { 0x3d801b43, 0x28000001 }, + { 0x3d801b45, 0x1c000006 }, + { 0x3d801b50, 0x34000009 }, + { 0x3d801b5a, 0x54000006 }, + { 0x3d801b61, 0x68000009 }, + { 0x3d801b6b, 0x30000008 }, + { 0x3d801b74, 0x68000008 }, + { 0x21801d00, 0x14000025 }, + { 0x13801d26, 0x14000004 }, + { 0x0c001d2b, 0x14000000 }, + { 0x21801d2c, 0x18000030 }, + { 0x13801d5d, 0x18000004 }, + { 0x21801d62, 0x14000003 }, + { 0x13801d66, 0x14000004 }, + { 0x21801d6b, 0x1400000c }, + { 0x0c001d78, 0x18000000 }, + { 0x21801d79, 0x14000003 }, + { 0x21001d7d, 0x14000ee6 }, + { 0x21801d7e, 0x1400001c }, + { 0x21801d9b, 0x18000023 }, + { 0x13001dbf, 0x18000000 }, + { 0x1b801dc0, 0x3000000a }, + { 0x1b801dfe, 0x30000001 }, + { 0x21001e00, 0x24000001 }, + { 0x21001e01, 0x1400ffff }, + { 0x21001e02, 0x24000001 }, + { 0x21001e03, 0x1400ffff }, + { 0x21001e04, 0x24000001 }, + { 0x21001e05, 0x1400ffff }, + { 0x21001e06, 0x24000001 }, + { 0x21001e07, 0x1400ffff }, + { 0x21001e08, 0x24000001 }, + { 0x21001e09, 0x1400ffff }, + { 0x21001e0a, 0x24000001 }, + { 0x21001e0b, 0x1400ffff }, + { 0x21001e0c, 0x24000001 }, + { 0x21001e0d, 0x1400ffff }, + { 0x21001e0e, 0x24000001 }, + { 0x21001e0f, 0x1400ffff }, + { 0x21001e10, 0x24000001 }, + { 0x21001e11, 0x1400ffff }, + { 0x21001e12, 0x24000001 }, + { 0x21001e13, 0x1400ffff }, + { 0x21001e14, 0x24000001 }, + { 0x21001e15, 0x1400ffff }, + { 0x21001e16, 0x24000001 }, + { 0x21001e17, 0x1400ffff }, + { 0x21001e18, 0x24000001 }, + { 0x21001e19, 0x1400ffff }, + { 0x21001e1a, 0x24000001 }, + { 0x21001e1b, 0x1400ffff }, + { 0x21001e1c, 0x24000001 }, + { 0x21001e1d, 0x1400ffff }, + { 0x21001e1e, 0x24000001 }, + { 0x21001e1f, 0x1400ffff }, + { 0x21001e20, 0x24000001 }, + { 0x21001e21, 0x1400ffff }, + { 0x21001e22, 0x24000001 }, + { 0x21001e23, 0x1400ffff }, + { 0x21001e24, 0x24000001 }, + { 0x21001e25, 0x1400ffff }, + { 0x21001e26, 0x24000001 }, + { 0x21001e27, 0x1400ffff }, + { 0x21001e28, 0x24000001 }, + { 0x21001e29, 0x1400ffff }, + { 0x21001e2a, 0x24000001 }, + { 0x21001e2b, 0x1400ffff }, + { 0x21001e2c, 0x24000001 }, + { 0x21001e2d, 0x1400ffff }, + { 0x21001e2e, 0x24000001 }, + { 0x21001e2f, 0x1400ffff }, + { 0x21001e30, 0x24000001 }, + { 0x21001e31, 0x1400ffff }, + { 0x21001e32, 0x24000001 }, + { 0x21001e33, 0x1400ffff }, + { 0x21001e34, 0x24000001 }, + { 0x21001e35, 0x1400ffff }, + { 0x21001e36, 0x24000001 }, + { 0x21001e37, 0x1400ffff }, + { 0x21001e38, 0x24000001 }, + { 0x21001e39, 0x1400ffff }, + { 0x21001e3a, 0x24000001 }, + { 0x21001e3b, 0x1400ffff }, + { 0x21001e3c, 0x24000001 }, + { 0x21001e3d, 0x1400ffff }, + { 0x21001e3e, 0x24000001 }, + { 0x21001e3f, 0x1400ffff }, + { 0x21001e40, 0x24000001 }, + { 0x21001e41, 0x1400ffff }, + { 0x21001e42, 0x24000001 }, + { 0x21001e43, 0x1400ffff }, + { 0x21001e44, 0x24000001 }, + { 0x21001e45, 0x1400ffff }, + { 0x21001e46, 0x24000001 }, + { 0x21001e47, 0x1400ffff }, + { 0x21001e48, 0x24000001 }, + { 0x21001e49, 0x1400ffff }, + { 0x21001e4a, 0x24000001 }, + { 0x21001e4b, 0x1400ffff }, + { 0x21001e4c, 0x24000001 }, + { 0x21001e4d, 0x1400ffff }, + { 0x21001e4e, 0x24000001 }, + { 0x21001e4f, 0x1400ffff }, + { 0x21001e50, 0x24000001 }, + { 0x21001e51, 0x1400ffff }, + { 0x21001e52, 0x24000001 }, + { 0x21001e53, 0x1400ffff }, + { 0x21001e54, 0x24000001 }, + { 0x21001e55, 0x1400ffff }, + { 0x21001e56, 0x24000001 }, + { 0x21001e57, 0x1400ffff }, + { 0x21001e58, 0x24000001 }, + { 0x21001e59, 0x1400ffff }, + { 0x21001e5a, 0x24000001 }, + { 0x21001e5b, 0x1400ffff }, + { 0x21001e5c, 0x24000001 }, + { 0x21001e5d, 0x1400ffff }, + { 0x21001e5e, 0x24000001 }, + { 0x21001e5f, 0x1400ffff }, + { 0x21001e60, 0x24000001 }, + { 0x21001e61, 0x1400ffff }, + { 0x21001e62, 0x24000001 }, + { 0x21001e63, 0x1400ffff }, + { 0x21001e64, 0x24000001 }, + { 0x21001e65, 0x1400ffff }, + { 0x21001e66, 0x24000001 }, + { 0x21001e67, 0x1400ffff }, + { 0x21001e68, 0x24000001 }, + { 0x21001e69, 0x1400ffff }, + { 0x21001e6a, 0x24000001 }, + { 0x21001e6b, 0x1400ffff }, + { 0x21001e6c, 0x24000001 }, + { 0x21001e6d, 0x1400ffff }, + { 0x21001e6e, 0x24000001 }, + { 0x21001e6f, 0x1400ffff }, + { 0x21001e70, 0x24000001 }, + { 0x21001e71, 0x1400ffff }, + { 0x21001e72, 0x24000001 }, + { 0x21001e73, 0x1400ffff }, + { 0x21001e74, 0x24000001 }, + { 0x21001e75, 0x1400ffff }, + { 0x21001e76, 0x24000001 }, + { 0x21001e77, 0x1400ffff }, + { 0x21001e78, 0x24000001 }, + { 0x21001e79, 0x1400ffff }, + { 0x21001e7a, 0x24000001 }, + { 0x21001e7b, 0x1400ffff }, + { 0x21001e7c, 0x24000001 }, + { 0x21001e7d, 0x1400ffff }, + { 0x21001e7e, 0x24000001 }, + { 0x21001e7f, 0x1400ffff }, + { 0x21001e80, 0x24000001 }, + { 0x21001e81, 0x1400ffff }, + { 0x21001e82, 0x24000001 }, + { 0x21001e83, 0x1400ffff }, + { 0x21001e84, 0x24000001 }, + { 0x21001e85, 0x1400ffff }, + { 0x21001e86, 0x24000001 }, + { 0x21001e87, 0x1400ffff }, + { 0x21001e88, 0x24000001 }, + { 0x21001e89, 0x1400ffff }, + { 0x21001e8a, 0x24000001 }, + { 0x21001e8b, 0x1400ffff }, + { 0x21001e8c, 0x24000001 }, + { 0x21001e8d, 0x1400ffff }, + { 0x21001e8e, 0x24000001 }, + { 0x21001e8f, 0x1400ffff }, + { 0x21001e90, 0x24000001 }, + { 0x21001e91, 0x1400ffff }, + { 0x21001e92, 0x24000001 }, + { 0x21001e93, 0x1400ffff }, + { 0x21001e94, 0x24000001 }, + { 0x21001e95, 0x1400ffff }, + { 0x21801e96, 0x14000004 }, + { 0x21001e9b, 0x1400ffc5 }, + { 0x21001ea0, 0x24000001 }, + { 0x21001ea1, 0x1400ffff }, + { 0x21001ea2, 0x24000001 }, + { 0x21001ea3, 0x1400ffff }, + { 0x21001ea4, 0x24000001 }, + { 0x21001ea5, 0x1400ffff }, + { 0x21001ea6, 0x24000001 }, + { 0x21001ea7, 0x1400ffff }, + { 0x21001ea8, 0x24000001 }, + { 0x21001ea9, 0x1400ffff }, + { 0x21001eaa, 0x24000001 }, + { 0x21001eab, 0x1400ffff }, + { 0x21001eac, 0x24000001 }, + { 0x21001ead, 0x1400ffff }, + { 0x21001eae, 0x24000001 }, + { 0x21001eaf, 0x1400ffff }, + { 0x21001eb0, 0x24000001 }, + { 0x21001eb1, 0x1400ffff }, + { 0x21001eb2, 0x24000001 }, + { 0x21001eb3, 0x1400ffff }, + { 0x21001eb4, 0x24000001 }, + { 0x21001eb5, 0x1400ffff }, + { 0x21001eb6, 0x24000001 }, + { 0x21001eb7, 0x1400ffff }, + { 0x21001eb8, 0x24000001 }, + { 0x21001eb9, 0x1400ffff }, + { 0x21001eba, 0x24000001 }, + { 0x21001ebb, 0x1400ffff }, + { 0x21001ebc, 0x24000001 }, + { 0x21001ebd, 0x1400ffff }, + { 0x21001ebe, 0x24000001 }, + { 0x21001ebf, 0x1400ffff }, + { 0x21001ec0, 0x24000001 }, + { 0x21001ec1, 0x1400ffff }, + { 0x21001ec2, 0x24000001 }, + { 0x21001ec3, 0x1400ffff }, + { 0x21001ec4, 0x24000001 }, + { 0x21001ec5, 0x1400ffff }, + { 0x21001ec6, 0x24000001 }, + { 0x21001ec7, 0x1400ffff }, + { 0x21001ec8, 0x24000001 }, + { 0x21001ec9, 0x1400ffff }, + { 0x21001eca, 0x24000001 }, + { 0x21001ecb, 0x1400ffff }, + { 0x21001ecc, 0x24000001 }, + { 0x21001ecd, 0x1400ffff }, + { 0x21001ece, 0x24000001 }, + { 0x21001ecf, 0x1400ffff }, + { 0x21001ed0, 0x24000001 }, + { 0x21001ed1, 0x1400ffff }, + { 0x21001ed2, 0x24000001 }, + { 0x21001ed3, 0x1400ffff }, + { 0x21001ed4, 0x24000001 }, + { 0x21001ed5, 0x1400ffff }, + { 0x21001ed6, 0x24000001 }, + { 0x21001ed7, 0x1400ffff }, + { 0x21001ed8, 0x24000001 }, + { 0x21001ed9, 0x1400ffff }, + { 0x21001eda, 0x24000001 }, + { 0x21001edb, 0x1400ffff }, + { 0x21001edc, 0x24000001 }, + { 0x21001edd, 0x1400ffff }, + { 0x21001ede, 0x24000001 }, + { 0x21001edf, 0x1400ffff }, + { 0x21001ee0, 0x24000001 }, + { 0x21001ee1, 0x1400ffff }, + { 0x21001ee2, 0x24000001 }, + { 0x21001ee3, 0x1400ffff }, + { 0x21001ee4, 0x24000001 }, + { 0x21001ee5, 0x1400ffff }, + { 0x21001ee6, 0x24000001 }, + { 0x21001ee7, 0x1400ffff }, + { 0x21001ee8, 0x24000001 }, + { 0x21001ee9, 0x1400ffff }, + { 0x21001eea, 0x24000001 }, + { 0x21001eeb, 0x1400ffff }, + { 0x21001eec, 0x24000001 }, + { 0x21001eed, 0x1400ffff }, + { 0x21001eee, 0x24000001 }, + { 0x21001eef, 0x1400ffff }, + { 0x21001ef0, 0x24000001 }, + { 0x21001ef1, 0x1400ffff }, + { 0x21001ef2, 0x24000001 }, + { 0x21001ef3, 0x1400ffff }, + { 0x21001ef4, 0x24000001 }, + { 0x21001ef5, 0x1400ffff }, + { 0x21001ef6, 0x24000001 }, + { 0x21001ef7, 0x1400ffff }, + { 0x21001ef8, 0x24000001 }, + { 0x21001ef9, 0x1400ffff }, + { 0x13001f00, 0x14000008 }, + { 0x13001f01, 0x14000008 }, + { 0x13001f02, 0x14000008 }, + { 0x13001f03, 0x14000008 }, + { 0x13001f04, 0x14000008 }, + { 0x13001f05, 0x14000008 }, + { 0x13001f06, 0x14000008 }, + { 0x13001f07, 0x14000008 }, + { 0x13001f08, 0x2400fff8 }, + { 0x13001f09, 0x2400fff8 }, + { 0x13001f0a, 0x2400fff8 }, + { 0x13001f0b, 0x2400fff8 }, + { 0x13001f0c, 0x2400fff8 }, + { 0x13001f0d, 0x2400fff8 }, + { 0x13001f0e, 0x2400fff8 }, + { 0x13001f0f, 0x2400fff8 }, + { 0x13001f10, 0x14000008 }, + { 0x13001f11, 0x14000008 }, + { 0x13001f12, 0x14000008 }, + { 0x13001f13, 0x14000008 }, + { 0x13001f14, 0x14000008 }, + { 0x13001f15, 0x14000008 }, + { 0x13001f18, 0x2400fff8 }, + { 0x13001f19, 0x2400fff8 }, + { 0x13001f1a, 0x2400fff8 }, + { 0x13001f1b, 0x2400fff8 }, + { 0x13001f1c, 0x2400fff8 }, + { 0x13001f1d, 0x2400fff8 }, + { 0x13001f20, 0x14000008 }, + { 0x13001f21, 0x14000008 }, + { 0x13001f22, 0x14000008 }, + { 0x13001f23, 0x14000008 }, + { 0x13001f24, 0x14000008 }, + { 0x13001f25, 0x14000008 }, + { 0x13001f26, 0x14000008 }, + { 0x13001f27, 0x14000008 }, + { 0x13001f28, 0x2400fff8 }, + { 0x13001f29, 0x2400fff8 }, + { 0x13001f2a, 0x2400fff8 }, + { 0x13001f2b, 0x2400fff8 }, + { 0x13001f2c, 0x2400fff8 }, + { 0x13001f2d, 0x2400fff8 }, + { 0x13001f2e, 0x2400fff8 }, + { 0x13001f2f, 0x2400fff8 }, + { 0x13001f30, 0x14000008 }, + { 0x13001f31, 0x14000008 }, + { 0x13001f32, 0x14000008 }, + { 0x13001f33, 0x14000008 }, + { 0x13001f34, 0x14000008 }, + { 0x13001f35, 0x14000008 }, + { 0x13001f36, 0x14000008 }, + { 0x13001f37, 0x14000008 }, + { 0x13001f38, 0x2400fff8 }, + { 0x13001f39, 0x2400fff8 }, + { 0x13001f3a, 0x2400fff8 }, + { 0x13001f3b, 0x2400fff8 }, + { 0x13001f3c, 0x2400fff8 }, + { 0x13001f3d, 0x2400fff8 }, + { 0x13001f3e, 0x2400fff8 }, + { 0x13001f3f, 0x2400fff8 }, + { 0x13001f40, 0x14000008 }, + { 0x13001f41, 0x14000008 }, + { 0x13001f42, 0x14000008 }, + { 0x13001f43, 0x14000008 }, + { 0x13001f44, 0x14000008 }, + { 0x13001f45, 0x14000008 }, + { 0x13001f48, 0x2400fff8 }, + { 0x13001f49, 0x2400fff8 }, + { 0x13001f4a, 0x2400fff8 }, + { 0x13001f4b, 0x2400fff8 }, + { 0x13001f4c, 0x2400fff8 }, + { 0x13001f4d, 0x2400fff8 }, + { 0x13001f50, 0x14000000 }, + { 0x13001f51, 0x14000008 }, + { 0x13001f52, 0x14000000 }, + { 0x13001f53, 0x14000008 }, + { 0x13001f54, 0x14000000 }, + { 0x13001f55, 0x14000008 }, + { 0x13001f56, 0x14000000 }, + { 0x13001f57, 0x14000008 }, + { 0x13001f59, 0x2400fff8 }, + { 0x13001f5b, 0x2400fff8 }, + { 0x13001f5d, 0x2400fff8 }, + { 0x13001f5f, 0x2400fff8 }, + { 0x13001f60, 0x14000008 }, + { 0x13001f61, 0x14000008 }, + { 0x13001f62, 0x14000008 }, + { 0x13001f63, 0x14000008 }, + { 0x13001f64, 0x14000008 }, + { 0x13001f65, 0x14000008 }, + { 0x13001f66, 0x14000008 }, + { 0x13001f67, 0x14000008 }, + { 0x13001f68, 0x2400fff8 }, + { 0x13001f69, 0x2400fff8 }, + { 0x13001f6a, 0x2400fff8 }, + { 0x13001f6b, 0x2400fff8 }, + { 0x13001f6c, 0x2400fff8 }, + { 0x13001f6d, 0x2400fff8 }, + { 0x13001f6e, 0x2400fff8 }, + { 0x13001f6f, 0x2400fff8 }, + { 0x13001f70, 0x1400004a }, + { 0x13001f71, 0x1400004a }, + { 0x13001f72, 0x14000056 }, + { 0x13001f73, 0x14000056 }, + { 0x13001f74, 0x14000056 }, + { 0x13001f75, 0x14000056 }, + { 0x13001f76, 0x14000064 }, + { 0x13001f77, 0x14000064 }, + { 0x13001f78, 0x14000080 }, + { 0x13001f79, 0x14000080 }, + { 0x13001f7a, 0x14000070 }, + { 0x13001f7b, 0x14000070 }, + { 0x13001f7c, 0x1400007e }, + { 0x13001f7d, 0x1400007e }, + { 0x13001f80, 0x14000008 }, + { 0x13001f81, 0x14000008 }, + { 0x13001f82, 0x14000008 }, + { 0x13001f83, 0x14000008 }, + { 0x13001f84, 0x14000008 }, + { 0x13001f85, 0x14000008 }, + { 0x13001f86, 0x14000008 }, + { 0x13001f87, 0x14000008 }, + { 0x13001f88, 0x2000fff8 }, + { 0x13001f89, 0x2000fff8 }, + { 0x13001f8a, 0x2000fff8 }, + { 0x13001f8b, 0x2000fff8 }, + { 0x13001f8c, 0x2000fff8 }, + { 0x13001f8d, 0x2000fff8 }, + { 0x13001f8e, 0x2000fff8 }, + { 0x13001f8f, 0x2000fff8 }, + { 0x13001f90, 0x14000008 }, + { 0x13001f91, 0x14000008 }, + { 0x13001f92, 0x14000008 }, + { 0x13001f93, 0x14000008 }, + { 0x13001f94, 0x14000008 }, + { 0x13001f95, 0x14000008 }, + { 0x13001f96, 0x14000008 }, + { 0x13001f97, 0x14000008 }, + { 0x13001f98, 0x2000fff8 }, + { 0x13001f99, 0x2000fff8 }, + { 0x13001f9a, 0x2000fff8 }, + { 0x13001f9b, 0x2000fff8 }, + { 0x13001f9c, 0x2000fff8 }, + { 0x13001f9d, 0x2000fff8 }, + { 0x13001f9e, 0x2000fff8 }, + { 0x13001f9f, 0x2000fff8 }, + { 0x13001fa0, 0x14000008 }, + { 0x13001fa1, 0x14000008 }, + { 0x13001fa2, 0x14000008 }, + { 0x13001fa3, 0x14000008 }, + { 0x13001fa4, 0x14000008 }, + { 0x13001fa5, 0x14000008 }, + { 0x13001fa6, 0x14000008 }, + { 0x13001fa7, 0x14000008 }, + { 0x13001fa8, 0x2000fff8 }, + { 0x13001fa9, 0x2000fff8 }, + { 0x13001faa, 0x2000fff8 }, + { 0x13001fab, 0x2000fff8 }, + { 0x13001fac, 0x2000fff8 }, + { 0x13001fad, 0x2000fff8 }, + { 0x13001fae, 0x2000fff8 }, + { 0x13001faf, 0x2000fff8 }, + { 0x13001fb0, 0x14000008 }, + { 0x13001fb1, 0x14000008 }, + { 0x13001fb2, 0x14000000 }, + { 0x13001fb3, 0x14000009 }, + { 0x13001fb4, 0x14000000 }, + { 0x13801fb6, 0x14000001 }, + { 0x13001fb8, 0x2400fff8 }, + { 0x13001fb9, 0x2400fff8 }, + { 0x13001fba, 0x2400ffb6 }, + { 0x13001fbb, 0x2400ffb6 }, + { 0x13001fbc, 0x2000fff7 }, + { 0x13001fbd, 0x60000000 }, + { 0x13001fbe, 0x1400e3db }, + { 0x13801fbf, 0x60000002 }, + { 0x13001fc2, 0x14000000 }, + { 0x13001fc3, 0x14000009 }, + { 0x13001fc4, 0x14000000 }, + { 0x13801fc6, 0x14000001 }, + { 0x13001fc8, 0x2400ffaa }, + { 0x13001fc9, 0x2400ffaa }, + { 0x13001fca, 0x2400ffaa }, + { 0x13001fcb, 0x2400ffaa }, + { 0x13001fcc, 0x2000fff7 }, + { 0x13801fcd, 0x60000002 }, + { 0x13001fd0, 0x14000008 }, + { 0x13001fd1, 0x14000008 }, + { 0x13801fd2, 0x14000001 }, + { 0x13801fd6, 0x14000001 }, + { 0x13001fd8, 0x2400fff8 }, + { 0x13001fd9, 0x2400fff8 }, + { 0x13001fda, 0x2400ff9c }, + { 0x13001fdb, 0x2400ff9c }, + { 0x13801fdd, 0x60000002 }, + { 0x13001fe0, 0x14000008 }, + { 0x13001fe1, 0x14000008 }, + { 0x13801fe2, 0x14000002 }, + { 0x13001fe5, 0x14000007 }, + { 0x13801fe6, 0x14000001 }, + { 0x13001fe8, 0x2400fff8 }, + { 0x13001fe9, 0x2400fff8 }, + { 0x13001fea, 0x2400ff90 }, + { 0x13001feb, 0x2400ff90 }, + { 0x13001fec, 0x2400fff9 }, + { 0x13801fed, 0x60000002 }, + { 0x13001ff2, 0x14000000 }, + { 0x13001ff3, 0x14000009 }, + { 0x13001ff4, 0x14000000 }, + { 0x13801ff6, 0x14000001 }, + { 0x13001ff8, 0x2400ff80 }, + { 0x13001ff9, 0x2400ff80 }, + { 0x13001ffa, 0x2400ff82 }, + { 0x13001ffb, 0x2400ff82 }, + { 0x13001ffc, 0x2000fff7 }, + { 0x13801ffd, 0x60000001 }, + { 0x09802000, 0x7400000a }, + { 0x0900200b, 0x04000000 }, + { 0x1b80200c, 0x04000001 }, + { 0x0980200e, 0x04000001 }, + { 0x09802010, 0x44000005 }, + { 0x09802016, 0x54000001 }, + { 0x09002018, 0x50000000 }, + { 0x09002019, 0x4c000000 }, + { 0x0900201a, 0x58000000 }, + { 0x0980201b, 0x50000001 }, + { 0x0900201d, 0x4c000000 }, + { 0x0900201e, 0x58000000 }, + { 0x0900201f, 0x50000000 }, + { 0x09802020, 0x54000007 }, + { 0x09002028, 0x6c000000 }, + { 0x09002029, 0x70000000 }, + { 0x0980202a, 0x04000004 }, + { 0x0900202f, 0x74000000 }, + { 0x09802030, 0x54000008 }, + { 0x09002039, 0x50000000 }, + { 0x0900203a, 0x4c000000 }, + { 0x0980203b, 0x54000003 }, + { 0x0980203f, 0x40000001 }, + { 0x09802041, 0x54000002 }, + { 0x09002044, 0x64000000 }, + { 0x09002045, 0x58000000 }, + { 0x09002046, 0x48000000 }, + { 0x09802047, 0x5400000a }, + { 0x09002052, 0x64000000 }, + { 0x09002053, 0x54000000 }, + { 0x09002054, 0x40000000 }, + { 0x09802055, 0x54000009 }, + { 0x0900205f, 0x74000000 }, + { 0x09802060, 0x04000003 }, + { 0x0980206a, 0x04000005 }, + { 0x09002070, 0x3c000000 }, + { 0x21002071, 0x14000000 }, + { 0x09802074, 0x3c000005 }, + { 0x0980207a, 0x64000002 }, + { 0x0900207d, 0x58000000 }, + { 0x0900207e, 0x48000000 }, + { 0x2100207f, 0x14000000 }, + { 0x09802080, 0x3c000009 }, + { 0x0980208a, 0x64000002 }, + { 0x0900208d, 0x58000000 }, + { 0x0900208e, 0x48000000 }, + { 0x21802090, 0x18000004 }, + { 0x098020a0, 0x5c000015 }, + { 0x1b8020d0, 0x3000000c }, + { 0x1b8020dd, 0x2c000003 }, + { 0x1b0020e1, 0x30000000 }, + { 0x1b8020e2, 0x2c000002 }, + { 0x1b8020e5, 0x3000000a }, + { 0x09802100, 0x68000001 }, + { 0x09002102, 0x24000000 }, + { 0x09802103, 0x68000003 }, + { 0x09002107, 0x24000000 }, + { 0x09802108, 0x68000001 }, + { 0x0900210a, 0x14000000 }, + { 0x0980210b, 0x24000002 }, + { 0x0980210e, 0x14000001 }, + { 0x09802110, 0x24000002 }, + { 0x09002113, 0x14000000 }, + { 0x09002114, 0x68000000 }, + { 0x09002115, 0x24000000 }, + { 0x09802116, 0x68000002 }, + { 0x09802119, 0x24000004 }, + { 0x0980211e, 0x68000005 }, + { 0x09002124, 0x24000000 }, + { 0x09002125, 0x68000000 }, + { 0x13002126, 0x2400e2a3 }, + { 0x09002127, 0x68000000 }, + { 0x09002128, 0x24000000 }, + { 0x09002129, 0x68000000 }, + { 0x2100212a, 0x2400df41 }, + { 0x2100212b, 0x2400dfba }, + { 0x0980212c, 0x24000001 }, + { 0x0900212e, 0x68000000 }, + { 0x0900212f, 0x14000000 }, + { 0x09802130, 0x24000001 }, + { 0x21002132, 0x2400001c }, + { 0x09002133, 0x24000000 }, + { 0x09002134, 0x14000000 }, + { 0x09802135, 0x1c000003 }, + { 0x09002139, 0x14000000 }, + { 0x0980213a, 0x68000001 }, + { 0x0980213c, 0x14000001 }, + { 0x0980213e, 0x24000001 }, + { 0x09802140, 0x64000004 }, + { 0x09002145, 0x24000000 }, + { 0x09802146, 0x14000003 }, + { 0x0900214a, 0x68000000 }, + { 0x0900214b, 0x64000000 }, + { 0x0980214c, 0x68000001 }, + { 0x2100214e, 0x1400ffe4 }, + { 0x09802153, 0x3c00000c }, + { 0x09002160, 0x38000010 }, + { 0x09002161, 0x38000010 }, + { 0x09002162, 0x38000010 }, + { 0x09002163, 0x38000010 }, + { 0x09002164, 0x38000010 }, + { 0x09002165, 0x38000010 }, + { 0x09002166, 0x38000010 }, + { 0x09002167, 0x38000010 }, + { 0x09002168, 0x38000010 }, + { 0x09002169, 0x38000010 }, + { 0x0900216a, 0x38000010 }, + { 0x0900216b, 0x38000010 }, + { 0x0900216c, 0x38000010 }, + { 0x0900216d, 0x38000010 }, + { 0x0900216e, 0x38000010 }, + { 0x0900216f, 0x38000010 }, + { 0x09002170, 0x3800fff0 }, + { 0x09002171, 0x3800fff0 }, + { 0x09002172, 0x3800fff0 }, + { 0x09002173, 0x3800fff0 }, + { 0x09002174, 0x3800fff0 }, + { 0x09002175, 0x3800fff0 }, + { 0x09002176, 0x3800fff0 }, + { 0x09002177, 0x3800fff0 }, + { 0x09002178, 0x3800fff0 }, + { 0x09002179, 0x3800fff0 }, + { 0x0900217a, 0x3800fff0 }, + { 0x0900217b, 0x3800fff0 }, + { 0x0900217c, 0x3800fff0 }, + { 0x0900217d, 0x3800fff0 }, + { 0x0900217e, 0x3800fff0 }, + { 0x0900217f, 0x3800fff0 }, + { 0x09802180, 0x38000002 }, + { 0x09002183, 0x24000001 }, + { 0x21002184, 0x1400ffff }, + { 0x09802190, 0x64000004 }, + { 0x09802195, 0x68000004 }, + { 0x0980219a, 0x64000001 }, + { 0x0980219c, 0x68000003 }, + { 0x090021a0, 0x64000000 }, + { 0x098021a1, 0x68000001 }, + { 0x090021a3, 0x64000000 }, + { 0x098021a4, 0x68000001 }, + { 0x090021a6, 0x64000000 }, + { 0x098021a7, 0x68000006 }, + { 0x090021ae, 0x64000000 }, + { 0x098021af, 0x6800001e }, + { 0x098021ce, 0x64000001 }, + { 0x098021d0, 0x68000001 }, + { 0x090021d2, 0x64000000 }, + { 0x090021d3, 0x68000000 }, + { 0x090021d4, 0x64000000 }, + { 0x098021d5, 0x6800001e }, + { 0x098021f4, 0x6400010b }, + { 0x09802300, 0x68000007 }, + { 0x09802308, 0x64000003 }, + { 0x0980230c, 0x68000013 }, + { 0x09802320, 0x64000001 }, + { 0x09802322, 0x68000006 }, + { 0x09002329, 0x58000000 }, + { 0x0900232a, 0x48000000 }, + { 0x0980232b, 0x68000050 }, + { 0x0900237c, 0x64000000 }, + { 0x0980237d, 0x6800001d }, + { 0x0980239b, 0x64000018 }, + { 0x098023b4, 0x68000027 }, + { 0x098023dc, 0x64000005 }, + { 0x098023e2, 0x68000005 }, + { 0x09802400, 0x68000026 }, + { 0x09802440, 0x6800000a }, + { 0x09802460, 0x3c00003b }, + { 0x0980249c, 0x68000019 }, + { 0x090024b6, 0x6800001a }, + { 0x090024b7, 0x6800001a }, + { 0x090024b8, 0x6800001a }, + { 0x090024b9, 0x6800001a }, + { 0x090024ba, 0x6800001a }, + { 0x090024bb, 0x6800001a }, + { 0x090024bc, 0x6800001a }, + { 0x090024bd, 0x6800001a }, + { 0x090024be, 0x6800001a }, + { 0x090024bf, 0x6800001a }, + { 0x090024c0, 0x6800001a }, + { 0x090024c1, 0x6800001a }, + { 0x090024c2, 0x6800001a }, + { 0x090024c3, 0x6800001a }, + { 0x090024c4, 0x6800001a }, + { 0x090024c5, 0x6800001a }, + { 0x090024c6, 0x6800001a }, + { 0x090024c7, 0x6800001a }, + { 0x090024c8, 0x6800001a }, + { 0x090024c9, 0x6800001a }, + { 0x090024ca, 0x6800001a }, + { 0x090024cb, 0x6800001a }, + { 0x090024cc, 0x6800001a }, + { 0x090024cd, 0x6800001a }, + { 0x090024ce, 0x6800001a }, + { 0x090024cf, 0x6800001a }, + { 0x090024d0, 0x6800ffe6 }, + { 0x090024d1, 0x6800ffe6 }, + { 0x090024d2, 0x6800ffe6 }, + { 0x090024d3, 0x6800ffe6 }, + { 0x090024d4, 0x6800ffe6 }, + { 0x090024d5, 0x6800ffe6 }, + { 0x090024d6, 0x6800ffe6 }, + { 0x090024d7, 0x6800ffe6 }, + { 0x090024d8, 0x6800ffe6 }, + { 0x090024d9, 0x6800ffe6 }, + { 0x090024da, 0x6800ffe6 }, + { 0x090024db, 0x6800ffe6 }, + { 0x090024dc, 0x6800ffe6 }, + { 0x090024dd, 0x6800ffe6 }, + { 0x090024de, 0x6800ffe6 }, + { 0x090024df, 0x6800ffe6 }, + { 0x090024e0, 0x6800ffe6 }, + { 0x090024e1, 0x6800ffe6 }, + { 0x090024e2, 0x6800ffe6 }, + { 0x090024e3, 0x6800ffe6 }, + { 0x090024e4, 0x6800ffe6 }, + { 0x090024e5, 0x6800ffe6 }, + { 0x090024e6, 0x6800ffe6 }, + { 0x090024e7, 0x6800ffe6 }, + { 0x090024e8, 0x6800ffe6 }, + { 0x090024e9, 0x6800ffe6 }, + { 0x098024ea, 0x3c000015 }, + { 0x09802500, 0x680000b6 }, + { 0x090025b7, 0x64000000 }, + { 0x098025b8, 0x68000008 }, + { 0x090025c1, 0x64000000 }, + { 0x098025c2, 0x68000035 }, + { 0x098025f8, 0x64000007 }, + { 0x09802600, 0x6800006e }, + { 0x0900266f, 0x64000000 }, + { 0x09802670, 0x6800002c }, + { 0x098026a0, 0x68000012 }, + { 0x09802701, 0x68000003 }, + { 0x09802706, 0x68000003 }, + { 0x0980270c, 0x6800001b }, + { 0x09802729, 0x68000022 }, + { 0x0900274d, 0x68000000 }, + { 0x0980274f, 0x68000003 }, + { 0x09002756, 0x68000000 }, + { 0x09802758, 0x68000006 }, + { 0x09802761, 0x68000006 }, + { 0x09002768, 0x58000000 }, + { 0x09002769, 0x48000000 }, + { 0x0900276a, 0x58000000 }, + { 0x0900276b, 0x48000000 }, + { 0x0900276c, 0x58000000 }, + { 0x0900276d, 0x48000000 }, + { 0x0900276e, 0x58000000 }, + { 0x0900276f, 0x48000000 }, + { 0x09002770, 0x58000000 }, + { 0x09002771, 0x48000000 }, + { 0x09002772, 0x58000000 }, + { 0x09002773, 0x48000000 }, + { 0x09002774, 0x58000000 }, + { 0x09002775, 0x48000000 }, + { 0x09802776, 0x3c00001d }, + { 0x09002794, 0x68000000 }, + { 0x09802798, 0x68000017 }, + { 0x098027b1, 0x6800000d }, + { 0x098027c0, 0x64000004 }, + { 0x090027c5, 0x58000000 }, + { 0x090027c6, 0x48000000 }, + { 0x098027c7, 0x64000003 }, + { 0x098027d0, 0x64000015 }, + { 0x090027e6, 0x58000000 }, + { 0x090027e7, 0x48000000 }, + { 0x090027e8, 0x58000000 }, + { 0x090027e9, 0x48000000 }, + { 0x090027ea, 0x58000000 }, + { 0x090027eb, 0x48000000 }, + { 0x098027f0, 0x6400000f }, + { 0x04802800, 0x680000ff }, + { 0x09802900, 0x64000082 }, + { 0x09002983, 0x58000000 }, + { 0x09002984, 0x48000000 }, + { 0x09002985, 0x58000000 }, + { 0x09002986, 0x48000000 }, + { 0x09002987, 0x58000000 }, + { 0x09002988, 0x48000000 }, + { 0x09002989, 0x58000000 }, + { 0x0900298a, 0x48000000 }, + { 0x0900298b, 0x58000000 }, + { 0x0900298c, 0x48000000 }, + { 0x0900298d, 0x58000000 }, + { 0x0900298e, 0x48000000 }, + { 0x0900298f, 0x58000000 }, + { 0x09002990, 0x48000000 }, + { 0x09002991, 0x58000000 }, + { 0x09002992, 0x48000000 }, + { 0x09002993, 0x58000000 }, + { 0x09002994, 0x48000000 }, + { 0x09002995, 0x58000000 }, + { 0x09002996, 0x48000000 }, + { 0x09002997, 0x58000000 }, + { 0x09002998, 0x48000000 }, + { 0x09802999, 0x6400003e }, + { 0x090029d8, 0x58000000 }, + { 0x090029d9, 0x48000000 }, + { 0x090029da, 0x58000000 }, + { 0x090029db, 0x48000000 }, + { 0x098029dc, 0x6400001f }, + { 0x090029fc, 0x58000000 }, + { 0x090029fd, 0x48000000 }, + { 0x098029fe, 0x64000101 }, + { 0x09802b00, 0x6800001a }, + { 0x09802b20, 0x68000003 }, + { 0x11002c00, 0x24000030 }, + { 0x11002c01, 0x24000030 }, + { 0x11002c02, 0x24000030 }, + { 0x11002c03, 0x24000030 }, + { 0x11002c04, 0x24000030 }, + { 0x11002c05, 0x24000030 }, + { 0x11002c06, 0x24000030 }, + { 0x11002c07, 0x24000030 }, + { 0x11002c08, 0x24000030 }, + { 0x11002c09, 0x24000030 }, + { 0x11002c0a, 0x24000030 }, + { 0x11002c0b, 0x24000030 }, + { 0x11002c0c, 0x24000030 }, + { 0x11002c0d, 0x24000030 }, + { 0x11002c0e, 0x24000030 }, + { 0x11002c0f, 0x24000030 }, + { 0x11002c10, 0x24000030 }, + { 0x11002c11, 0x24000030 }, + { 0x11002c12, 0x24000030 }, + { 0x11002c13, 0x24000030 }, + { 0x11002c14, 0x24000030 }, + { 0x11002c15, 0x24000030 }, + { 0x11002c16, 0x24000030 }, + { 0x11002c17, 0x24000030 }, + { 0x11002c18, 0x24000030 }, + { 0x11002c19, 0x24000030 }, + { 0x11002c1a, 0x24000030 }, + { 0x11002c1b, 0x24000030 }, + { 0x11002c1c, 0x24000030 }, + { 0x11002c1d, 0x24000030 }, + { 0x11002c1e, 0x24000030 }, + { 0x11002c1f, 0x24000030 }, + { 0x11002c20, 0x24000030 }, + { 0x11002c21, 0x24000030 }, + { 0x11002c22, 0x24000030 }, + { 0x11002c23, 0x24000030 }, + { 0x11002c24, 0x24000030 }, + { 0x11002c25, 0x24000030 }, + { 0x11002c26, 0x24000030 }, + { 0x11002c27, 0x24000030 }, + { 0x11002c28, 0x24000030 }, + { 0x11002c29, 0x24000030 }, + { 0x11002c2a, 0x24000030 }, + { 0x11002c2b, 0x24000030 }, + { 0x11002c2c, 0x24000030 }, + { 0x11002c2d, 0x24000030 }, + { 0x11002c2e, 0x24000030 }, + { 0x11002c30, 0x1400ffd0 }, + { 0x11002c31, 0x1400ffd0 }, + { 0x11002c32, 0x1400ffd0 }, + { 0x11002c33, 0x1400ffd0 }, + { 0x11002c34, 0x1400ffd0 }, + { 0x11002c35, 0x1400ffd0 }, + { 0x11002c36, 0x1400ffd0 }, + { 0x11002c37, 0x1400ffd0 }, + { 0x11002c38, 0x1400ffd0 }, + { 0x11002c39, 0x1400ffd0 }, + { 0x11002c3a, 0x1400ffd0 }, + { 0x11002c3b, 0x1400ffd0 }, + { 0x11002c3c, 0x1400ffd0 }, + { 0x11002c3d, 0x1400ffd0 }, + { 0x11002c3e, 0x1400ffd0 }, + { 0x11002c3f, 0x1400ffd0 }, + { 0x11002c40, 0x1400ffd0 }, + { 0x11002c41, 0x1400ffd0 }, + { 0x11002c42, 0x1400ffd0 }, + { 0x11002c43, 0x1400ffd0 }, + { 0x11002c44, 0x1400ffd0 }, + { 0x11002c45, 0x1400ffd0 }, + { 0x11002c46, 0x1400ffd0 }, + { 0x11002c47, 0x1400ffd0 }, + { 0x11002c48, 0x1400ffd0 }, + { 0x11002c49, 0x1400ffd0 }, + { 0x11002c4a, 0x1400ffd0 }, + { 0x11002c4b, 0x1400ffd0 }, + { 0x11002c4c, 0x1400ffd0 }, + { 0x11002c4d, 0x1400ffd0 }, + { 0x11002c4e, 0x1400ffd0 }, + { 0x11002c4f, 0x1400ffd0 }, + { 0x11002c50, 0x1400ffd0 }, + { 0x11002c51, 0x1400ffd0 }, + { 0x11002c52, 0x1400ffd0 }, + { 0x11002c53, 0x1400ffd0 }, + { 0x11002c54, 0x1400ffd0 }, + { 0x11002c55, 0x1400ffd0 }, + { 0x11002c56, 0x1400ffd0 }, + { 0x11002c57, 0x1400ffd0 }, + { 0x11002c58, 0x1400ffd0 }, + { 0x11002c59, 0x1400ffd0 }, + { 0x11002c5a, 0x1400ffd0 }, + { 0x11002c5b, 0x1400ffd0 }, + { 0x11002c5c, 0x1400ffd0 }, + { 0x11002c5d, 0x1400ffd0 }, + { 0x11002c5e, 0x1400ffd0 }, + { 0x21002c60, 0x24000001 }, + { 0x21002c61, 0x1400ffff }, + { 0x21002c62, 0x2400d609 }, + { 0x21002c63, 0x2400f11a }, + { 0x21002c64, 0x2400d619 }, + { 0x21002c65, 0x1400d5d5 }, + { 0x21002c66, 0x1400d5d8 }, + { 0x21002c67, 0x24000001 }, + { 0x21002c68, 0x1400ffff }, + { 0x21002c69, 0x24000001 }, + { 0x21002c6a, 0x1400ffff }, + { 0x21002c6b, 0x24000001 }, + { 0x21002c6c, 0x1400ffff }, + { 0x21002c74, 0x14000000 }, + { 0x21002c75, 0x24000001 }, + { 0x21002c76, 0x1400ffff }, + { 0x21002c77, 0x14000000 }, + { 0x0a002c80, 0x24000001 }, + { 0x0a002c81, 0x1400ffff }, + { 0x0a002c82, 0x24000001 }, + { 0x0a002c83, 0x1400ffff }, + { 0x0a002c84, 0x24000001 }, + { 0x0a002c85, 0x1400ffff }, + { 0x0a002c86, 0x24000001 }, + { 0x0a002c87, 0x1400ffff }, + { 0x0a002c88, 0x24000001 }, + { 0x0a002c89, 0x1400ffff }, + { 0x0a002c8a, 0x24000001 }, + { 0x0a002c8b, 0x1400ffff }, + { 0x0a002c8c, 0x24000001 }, + { 0x0a002c8d, 0x1400ffff }, + { 0x0a002c8e, 0x24000001 }, + { 0x0a002c8f, 0x1400ffff }, + { 0x0a002c90, 0x24000001 }, + { 0x0a002c91, 0x1400ffff }, + { 0x0a002c92, 0x24000001 }, + { 0x0a002c93, 0x1400ffff }, + { 0x0a002c94, 0x24000001 }, + { 0x0a002c95, 0x1400ffff }, + { 0x0a002c96, 0x24000001 }, + { 0x0a002c97, 0x1400ffff }, + { 0x0a002c98, 0x24000001 }, + { 0x0a002c99, 0x1400ffff }, + { 0x0a002c9a, 0x24000001 }, + { 0x0a002c9b, 0x1400ffff }, + { 0x0a002c9c, 0x24000001 }, + { 0x0a002c9d, 0x1400ffff }, + { 0x0a002c9e, 0x24000001 }, + { 0x0a002c9f, 0x1400ffff }, + { 0x0a002ca0, 0x24000001 }, + { 0x0a002ca1, 0x1400ffff }, + { 0x0a002ca2, 0x24000001 }, + { 0x0a002ca3, 0x1400ffff }, + { 0x0a002ca4, 0x24000001 }, + { 0x0a002ca5, 0x1400ffff }, + { 0x0a002ca6, 0x24000001 }, + { 0x0a002ca7, 0x1400ffff }, + { 0x0a002ca8, 0x24000001 }, + { 0x0a002ca9, 0x1400ffff }, + { 0x0a002caa, 0x24000001 }, + { 0x0a002cab, 0x1400ffff }, + { 0x0a002cac, 0x24000001 }, + { 0x0a002cad, 0x1400ffff }, + { 0x0a002cae, 0x24000001 }, + { 0x0a002caf, 0x1400ffff }, + { 0x0a002cb0, 0x24000001 }, + { 0x0a002cb1, 0x1400ffff }, + { 0x0a002cb2, 0x24000001 }, + { 0x0a002cb3, 0x1400ffff }, + { 0x0a002cb4, 0x24000001 }, + { 0x0a002cb5, 0x1400ffff }, + { 0x0a002cb6, 0x24000001 }, + { 0x0a002cb7, 0x1400ffff }, + { 0x0a002cb8, 0x24000001 }, + { 0x0a002cb9, 0x1400ffff }, + { 0x0a002cba, 0x24000001 }, + { 0x0a002cbb, 0x1400ffff }, + { 0x0a002cbc, 0x24000001 }, + { 0x0a002cbd, 0x1400ffff }, + { 0x0a002cbe, 0x24000001 }, + { 0x0a002cbf, 0x1400ffff }, + { 0x0a002cc0, 0x24000001 }, + { 0x0a002cc1, 0x1400ffff }, + { 0x0a002cc2, 0x24000001 }, + { 0x0a002cc3, 0x1400ffff }, + { 0x0a002cc4, 0x24000001 }, + { 0x0a002cc5, 0x1400ffff }, + { 0x0a002cc6, 0x24000001 }, + { 0x0a002cc7, 0x1400ffff }, + { 0x0a002cc8, 0x24000001 }, + { 0x0a002cc9, 0x1400ffff }, + { 0x0a002cca, 0x24000001 }, + { 0x0a002ccb, 0x1400ffff }, + { 0x0a002ccc, 0x24000001 }, + { 0x0a002ccd, 0x1400ffff }, + { 0x0a002cce, 0x24000001 }, + { 0x0a002ccf, 0x1400ffff }, + { 0x0a002cd0, 0x24000001 }, + { 0x0a002cd1, 0x1400ffff }, + { 0x0a002cd2, 0x24000001 }, + { 0x0a002cd3, 0x1400ffff }, + { 0x0a002cd4, 0x24000001 }, + { 0x0a002cd5, 0x1400ffff }, + { 0x0a002cd6, 0x24000001 }, + { 0x0a002cd7, 0x1400ffff }, + { 0x0a002cd8, 0x24000001 }, + { 0x0a002cd9, 0x1400ffff }, + { 0x0a002cda, 0x24000001 }, + { 0x0a002cdb, 0x1400ffff }, + { 0x0a002cdc, 0x24000001 }, + { 0x0a002cdd, 0x1400ffff }, + { 0x0a002cde, 0x24000001 }, + { 0x0a002cdf, 0x1400ffff }, + { 0x0a002ce0, 0x24000001 }, + { 0x0a002ce1, 0x1400ffff }, + { 0x0a002ce2, 0x24000001 }, + { 0x0a002ce3, 0x1400ffff }, + { 0x0a002ce4, 0x14000000 }, + { 0x0a802ce5, 0x68000005 }, + { 0x0a802cf9, 0x54000003 }, + { 0x0a002cfd, 0x3c000000 }, + { 0x0a802cfe, 0x54000001 }, + { 0x10002d00, 0x1400e3a0 }, + { 0x10002d01, 0x1400e3a0 }, + { 0x10002d02, 0x1400e3a0 }, + { 0x10002d03, 0x1400e3a0 }, + { 0x10002d04, 0x1400e3a0 }, + { 0x10002d05, 0x1400e3a0 }, + { 0x10002d06, 0x1400e3a0 }, + { 0x10002d07, 0x1400e3a0 }, + { 0x10002d08, 0x1400e3a0 }, + { 0x10002d09, 0x1400e3a0 }, + { 0x10002d0a, 0x1400e3a0 }, + { 0x10002d0b, 0x1400e3a0 }, + { 0x10002d0c, 0x1400e3a0 }, + { 0x10002d0d, 0x1400e3a0 }, + { 0x10002d0e, 0x1400e3a0 }, + { 0x10002d0f, 0x1400e3a0 }, + { 0x10002d10, 0x1400e3a0 }, + { 0x10002d11, 0x1400e3a0 }, + { 0x10002d12, 0x1400e3a0 }, + { 0x10002d13, 0x1400e3a0 }, + { 0x10002d14, 0x1400e3a0 }, + { 0x10002d15, 0x1400e3a0 }, + { 0x10002d16, 0x1400e3a0 }, + { 0x10002d17, 0x1400e3a0 }, + { 0x10002d18, 0x1400e3a0 }, + { 0x10002d19, 0x1400e3a0 }, + { 0x10002d1a, 0x1400e3a0 }, + { 0x10002d1b, 0x1400e3a0 }, + { 0x10002d1c, 0x1400e3a0 }, + { 0x10002d1d, 0x1400e3a0 }, + { 0x10002d1e, 0x1400e3a0 }, + { 0x10002d1f, 0x1400e3a0 }, + { 0x10002d20, 0x1400e3a0 }, + { 0x10002d21, 0x1400e3a0 }, + { 0x10002d22, 0x1400e3a0 }, + { 0x10002d23, 0x1400e3a0 }, + { 0x10002d24, 0x1400e3a0 }, + { 0x10002d25, 0x1400e3a0 }, + { 0x3a802d30, 0x1c000035 }, + { 0x3a002d6f, 0x18000000 }, + { 0x0f802d80, 0x1c000016 }, + { 0x0f802da0, 0x1c000006 }, + { 0x0f802da8, 0x1c000006 }, + { 0x0f802db0, 0x1c000006 }, + { 0x0f802db8, 0x1c000006 }, + { 0x0f802dc0, 0x1c000006 }, + { 0x0f802dc8, 0x1c000006 }, + { 0x0f802dd0, 0x1c000006 }, + { 0x0f802dd8, 0x1c000006 }, + { 0x09802e00, 0x54000001 }, + { 0x09002e02, 0x50000000 }, + { 0x09002e03, 0x4c000000 }, + { 0x09002e04, 0x50000000 }, + { 0x09002e05, 0x4c000000 }, + { 0x09802e06, 0x54000002 }, + { 0x09002e09, 0x50000000 }, + { 0x09002e0a, 0x4c000000 }, + { 0x09002e0b, 0x54000000 }, + { 0x09002e0c, 0x50000000 }, + { 0x09002e0d, 0x4c000000 }, + { 0x09802e0e, 0x54000008 }, + { 0x09002e17, 0x44000000 }, + { 0x09002e1c, 0x50000000 }, + { 0x09002e1d, 0x4c000000 }, + { 0x16802e80, 0x68000019 }, + { 0x16802e9b, 0x68000058 }, + { 0x16802f00, 0x680000d5 }, + { 0x09802ff0, 0x6800000b }, + { 0x09003000, 0x74000000 }, + { 0x09803001, 0x54000002 }, + { 0x09003004, 0x68000000 }, + { 0x16003005, 0x18000000 }, + { 0x09003006, 0x1c000000 }, + { 0x16003007, 0x38000000 }, + { 0x09003008, 0x58000000 }, + { 0x09003009, 0x48000000 }, + { 0x0900300a, 0x58000000 }, + { 0x0900300b, 0x48000000 }, + { 0x0900300c, 0x58000000 }, + { 0x0900300d, 0x48000000 }, + { 0x0900300e, 0x58000000 }, + { 0x0900300f, 0x48000000 }, + { 0x09003010, 0x58000000 }, + { 0x09003011, 0x48000000 }, + { 0x09803012, 0x68000001 }, + { 0x09003014, 0x58000000 }, + { 0x09003015, 0x48000000 }, + { 0x09003016, 0x58000000 }, + { 0x09003017, 0x48000000 }, + { 0x09003018, 0x58000000 }, + { 0x09003019, 0x48000000 }, + { 0x0900301a, 0x58000000 }, + { 0x0900301b, 0x48000000 }, + { 0x0900301c, 0x44000000 }, + { 0x0900301d, 0x58000000 }, + { 0x0980301e, 0x48000001 }, + { 0x09003020, 0x68000000 }, + { 0x16803021, 0x38000008 }, + { 0x1b80302a, 0x30000005 }, + { 0x09003030, 0x44000000 }, + { 0x09803031, 0x18000004 }, + { 0x09803036, 0x68000001 }, + { 0x16803038, 0x38000002 }, + { 0x1600303b, 0x18000000 }, + { 0x0900303c, 0x1c000000 }, + { 0x0900303d, 0x54000000 }, + { 0x0980303e, 0x68000001 }, + { 0x1a803041, 0x1c000055 }, + { 0x1b803099, 0x30000001 }, + { 0x0980309b, 0x60000001 }, + { 0x1a80309d, 0x18000001 }, + { 0x1a00309f, 0x1c000000 }, + { 0x090030a0, 0x44000000 }, + { 0x1d8030a1, 0x1c000059 }, + { 0x090030fb, 0x54000000 }, + { 0x090030fc, 0x18000000 }, + { 0x1d8030fd, 0x18000001 }, + { 0x1d0030ff, 0x1c000000 }, + { 0x03803105, 0x1c000027 }, + { 0x17803131, 0x1c00005d }, + { 0x09803190, 0x68000001 }, + { 0x09803192, 0x3c000003 }, + { 0x09803196, 0x68000009 }, + { 0x038031a0, 0x1c000017 }, + { 0x098031c0, 0x6800000f }, + { 0x1d8031f0, 0x1c00000f }, + { 0x17803200, 0x6800001e }, + { 0x09803220, 0x3c000009 }, + { 0x0980322a, 0x68000019 }, + { 0x09003250, 0x68000000 }, + { 0x09803251, 0x3c00000e }, + { 0x17803260, 0x6800001d }, + { 0x0980327e, 0x68000001 }, + { 0x09803280, 0x3c000009 }, + { 0x0980328a, 0x68000026 }, + { 0x098032b1, 0x3c00000e }, + { 0x098032c0, 0x6800003e }, + { 0x09803300, 0x680000ff }, + { 0x16803400, 0x1c0019b5 }, + { 0x09804dc0, 0x6800003f }, + { 0x16804e00, 0x1c0051bb }, + { 0x3c80a000, 0x1c000014 }, + { 0x3c00a015, 0x18000000 }, + { 0x3c80a016, 0x1c000476 }, + { 0x3c80a490, 0x68000036 }, + { 0x0980a700, 0x60000016 }, + { 0x0980a717, 0x18000003 }, + { 0x0980a720, 0x60000001 }, + { 0x3080a800, 0x1c000001 }, + { 0x3000a802, 0x28000000 }, + { 0x3080a803, 0x1c000002 }, + { 0x3000a806, 0x30000000 }, + { 0x3080a807, 0x1c000003 }, + { 0x3000a80b, 0x30000000 }, + { 0x3080a80c, 0x1c000016 }, + { 0x3080a823, 0x28000001 }, + { 0x3080a825, 0x30000001 }, + { 0x3000a827, 0x28000000 }, + { 0x3080a828, 0x68000003 }, + { 0x4080a840, 0x1c000033 }, + { 0x4080a874, 0x54000003 }, + { 0x1780ac00, 0x1c002ba3 }, + { 0x0980d800, 0x1000037f }, + { 0x0980db80, 0x1000007f }, + { 0x0980dc00, 0x100003ff }, + { 0x0980e000, 0x0c0018ff }, + { 0x1680f900, 0x1c00012d }, + { 0x1680fa30, 0x1c00003a }, + { 0x1680fa70, 0x1c000069 }, + { 0x2180fb00, 0x14000006 }, + { 0x0180fb13, 0x14000004 }, + { 0x1900fb1d, 0x1c000000 }, + { 0x1900fb1e, 0x30000000 }, + { 0x1980fb1f, 0x1c000009 }, + { 0x1900fb29, 0x64000000 }, + { 0x1980fb2a, 0x1c00000c }, + { 0x1980fb38, 0x1c000004 }, + { 0x1900fb3e, 0x1c000000 }, + { 0x1980fb40, 0x1c000001 }, + { 0x1980fb43, 0x1c000001 }, + { 0x1980fb46, 0x1c000009 }, + { 0x0080fb50, 0x1c000061 }, + { 0x0080fbd3, 0x1c00016a }, + { 0x0900fd3e, 0x58000000 }, + { 0x0900fd3f, 0x48000000 }, + { 0x0080fd50, 0x1c00003f }, + { 0x0080fd92, 0x1c000035 }, + { 0x0080fdf0, 0x1c00000b }, + { 0x0000fdfc, 0x5c000000 }, + { 0x0900fdfd, 0x68000000 }, + { 0x1b80fe00, 0x3000000f }, + { 0x0980fe10, 0x54000006 }, + { 0x0900fe17, 0x58000000 }, + { 0x0900fe18, 0x48000000 }, + { 0x0900fe19, 0x54000000 }, + { 0x1b80fe20, 0x30000003 }, + { 0x0900fe30, 0x54000000 }, + { 0x0980fe31, 0x44000001 }, + { 0x0980fe33, 0x40000001 }, + { 0x0900fe35, 0x58000000 }, + { 0x0900fe36, 0x48000000 }, + { 0x0900fe37, 0x58000000 }, + { 0x0900fe38, 0x48000000 }, + { 0x0900fe39, 0x58000000 }, + { 0x0900fe3a, 0x48000000 }, + { 0x0900fe3b, 0x58000000 }, + { 0x0900fe3c, 0x48000000 }, + { 0x0900fe3d, 0x58000000 }, + { 0x0900fe3e, 0x48000000 }, + { 0x0900fe3f, 0x58000000 }, + { 0x0900fe40, 0x48000000 }, + { 0x0900fe41, 0x58000000 }, + { 0x0900fe42, 0x48000000 }, + { 0x0900fe43, 0x58000000 }, + { 0x0900fe44, 0x48000000 }, + { 0x0980fe45, 0x54000001 }, + { 0x0900fe47, 0x58000000 }, + { 0x0900fe48, 0x48000000 }, + { 0x0980fe49, 0x54000003 }, + { 0x0980fe4d, 0x40000002 }, + { 0x0980fe50, 0x54000002 }, + { 0x0980fe54, 0x54000003 }, + { 0x0900fe58, 0x44000000 }, + { 0x0900fe59, 0x58000000 }, + { 0x0900fe5a, 0x48000000 }, + { 0x0900fe5b, 0x58000000 }, + { 0x0900fe5c, 0x48000000 }, + { 0x0900fe5d, 0x58000000 }, + { 0x0900fe5e, 0x48000000 }, + { 0x0980fe5f, 0x54000002 }, + { 0x0900fe62, 0x64000000 }, + { 0x0900fe63, 0x44000000 }, + { 0x0980fe64, 0x64000002 }, + { 0x0900fe68, 0x54000000 }, + { 0x0900fe69, 0x5c000000 }, + { 0x0980fe6a, 0x54000001 }, + { 0x0080fe70, 0x1c000004 }, + { 0x0080fe76, 0x1c000086 }, + { 0x0900feff, 0x04000000 }, + { 0x0980ff01, 0x54000002 }, + { 0x0900ff04, 0x5c000000 }, + { 0x0980ff05, 0x54000002 }, + { 0x0900ff08, 0x58000000 }, + { 0x0900ff09, 0x48000000 }, + { 0x0900ff0a, 0x54000000 }, + { 0x0900ff0b, 0x64000000 }, + { 0x0900ff0c, 0x54000000 }, + { 0x0900ff0d, 0x44000000 }, + { 0x0980ff0e, 0x54000001 }, + { 0x0980ff10, 0x34000009 }, + { 0x0980ff1a, 0x54000001 }, + { 0x0980ff1c, 0x64000002 }, + { 0x0980ff1f, 0x54000001 }, + { 0x2100ff21, 0x24000020 }, + { 0x2100ff22, 0x24000020 }, + { 0x2100ff23, 0x24000020 }, + { 0x2100ff24, 0x24000020 }, + { 0x2100ff25, 0x24000020 }, + { 0x2100ff26, 0x24000020 }, + { 0x2100ff27, 0x24000020 }, + { 0x2100ff28, 0x24000020 }, + { 0x2100ff29, 0x24000020 }, + { 0x2100ff2a, 0x24000020 }, + { 0x2100ff2b, 0x24000020 }, + { 0x2100ff2c, 0x24000020 }, + { 0x2100ff2d, 0x24000020 }, + { 0x2100ff2e, 0x24000020 }, + { 0x2100ff2f, 0x24000020 }, + { 0x2100ff30, 0x24000020 }, + { 0x2100ff31, 0x24000020 }, + { 0x2100ff32, 0x24000020 }, + { 0x2100ff33, 0x24000020 }, + { 0x2100ff34, 0x24000020 }, + { 0x2100ff35, 0x24000020 }, + { 0x2100ff36, 0x24000020 }, + { 0x2100ff37, 0x24000020 }, + { 0x2100ff38, 0x24000020 }, + { 0x2100ff39, 0x24000020 }, + { 0x2100ff3a, 0x24000020 }, + { 0x0900ff3b, 0x58000000 }, + { 0x0900ff3c, 0x54000000 }, + { 0x0900ff3d, 0x48000000 }, + { 0x0900ff3e, 0x60000000 }, + { 0x0900ff3f, 0x40000000 }, + { 0x0900ff40, 0x60000000 }, + { 0x2100ff41, 0x1400ffe0 }, + { 0x2100ff42, 0x1400ffe0 }, + { 0x2100ff43, 0x1400ffe0 }, + { 0x2100ff44, 0x1400ffe0 }, + { 0x2100ff45, 0x1400ffe0 }, + { 0x2100ff46, 0x1400ffe0 }, + { 0x2100ff47, 0x1400ffe0 }, + { 0x2100ff48, 0x1400ffe0 }, + { 0x2100ff49, 0x1400ffe0 }, + { 0x2100ff4a, 0x1400ffe0 }, + { 0x2100ff4b, 0x1400ffe0 }, + { 0x2100ff4c, 0x1400ffe0 }, + { 0x2100ff4d, 0x1400ffe0 }, + { 0x2100ff4e, 0x1400ffe0 }, + { 0x2100ff4f, 0x1400ffe0 }, + { 0x2100ff50, 0x1400ffe0 }, + { 0x2100ff51, 0x1400ffe0 }, + { 0x2100ff52, 0x1400ffe0 }, + { 0x2100ff53, 0x1400ffe0 }, + { 0x2100ff54, 0x1400ffe0 }, + { 0x2100ff55, 0x1400ffe0 }, + { 0x2100ff56, 0x1400ffe0 }, + { 0x2100ff57, 0x1400ffe0 }, + { 0x2100ff58, 0x1400ffe0 }, + { 0x2100ff59, 0x1400ffe0 }, + { 0x2100ff5a, 0x1400ffe0 }, + { 0x0900ff5b, 0x58000000 }, + { 0x0900ff5c, 0x64000000 }, + { 0x0900ff5d, 0x48000000 }, + { 0x0900ff5e, 0x64000000 }, + { 0x0900ff5f, 0x58000000 }, + { 0x0900ff60, 0x48000000 }, + { 0x0900ff61, 0x54000000 }, + { 0x0900ff62, 0x58000000 }, + { 0x0900ff63, 0x48000000 }, + { 0x0980ff64, 0x54000001 }, + { 0x1d80ff66, 0x1c000009 }, + { 0x0900ff70, 0x18000000 }, + { 0x1d80ff71, 0x1c00002c }, + { 0x0980ff9e, 0x18000001 }, + { 0x1780ffa0, 0x1c00001e }, + { 0x1780ffc2, 0x1c000005 }, + { 0x1780ffca, 0x1c000005 }, + { 0x1780ffd2, 0x1c000005 }, + { 0x1780ffda, 0x1c000002 }, + { 0x0980ffe0, 0x5c000001 }, + { 0x0900ffe2, 0x64000000 }, + { 0x0900ffe3, 0x60000000 }, + { 0x0900ffe4, 0x68000000 }, + { 0x0980ffe5, 0x5c000001 }, + { 0x0900ffe8, 0x68000000 }, + { 0x0980ffe9, 0x64000003 }, + { 0x0980ffed, 0x68000001 }, + { 0x0980fff9, 0x04000002 }, + { 0x0980fffc, 0x68000001 }, + { 0x23810000, 0x1c00000b }, + { 0x2381000d, 0x1c000019 }, + { 0x23810028, 0x1c000012 }, + { 0x2381003c, 0x1c000001 }, + { 0x2381003f, 0x1c00000e }, + { 0x23810050, 0x1c00000d }, + { 0x23810080, 0x1c00007a }, + { 0x09810100, 0x54000001 }, + { 0x09010102, 0x68000000 }, + { 0x09810107, 0x3c00002c }, + { 0x09810137, 0x68000008 }, + { 0x13810140, 0x38000034 }, + { 0x13810175, 0x3c000003 }, + { 0x13810179, 0x68000010 }, + { 0x1301018a, 0x3c000000 }, + { 0x29810300, 0x1c00001e }, + { 0x29810320, 0x3c000003 }, + { 0x12810330, 0x1c000010 }, + { 0x12010341, 0x38000000 }, + { 0x12810342, 0x1c000007 }, + { 0x1201034a, 0x38000000 }, + { 0x3b810380, 0x1c00001d }, + { 0x3b01039f, 0x54000000 }, + { 0x2a8103a0, 0x1c000023 }, + { 0x2a8103c8, 0x1c000007 }, + { 0x2a0103d0, 0x54000000 }, + { 0x2a8103d1, 0x38000004 }, + { 0x0d010400, 0x24000028 }, + { 0x0d010401, 0x24000028 }, + { 0x0d010402, 0x24000028 }, + { 0x0d010403, 0x24000028 }, + { 0x0d010404, 0x24000028 }, + { 0x0d010405, 0x24000028 }, + { 0x0d010406, 0x24000028 }, + { 0x0d010407, 0x24000028 }, + { 0x0d010408, 0x24000028 }, + { 0x0d010409, 0x24000028 }, + { 0x0d01040a, 0x24000028 }, + { 0x0d01040b, 0x24000028 }, + { 0x0d01040c, 0x24000028 }, + { 0x0d01040d, 0x24000028 }, + { 0x0d01040e, 0x24000028 }, + { 0x0d01040f, 0x24000028 }, + { 0x0d010410, 0x24000028 }, + { 0x0d010411, 0x24000028 }, + { 0x0d010412, 0x24000028 }, + { 0x0d010413, 0x24000028 }, + { 0x0d010414, 0x24000028 }, + { 0x0d010415, 0x24000028 }, + { 0x0d010416, 0x24000028 }, + { 0x0d010417, 0x24000028 }, + { 0x0d010418, 0x24000028 }, + { 0x0d010419, 0x24000028 }, + { 0x0d01041a, 0x24000028 }, + { 0x0d01041b, 0x24000028 }, + { 0x0d01041c, 0x24000028 }, + { 0x0d01041d, 0x24000028 }, + { 0x0d01041e, 0x24000028 }, + { 0x0d01041f, 0x24000028 }, + { 0x0d010420, 0x24000028 }, + { 0x0d010421, 0x24000028 }, + { 0x0d010422, 0x24000028 }, + { 0x0d010423, 0x24000028 }, + { 0x0d010424, 0x24000028 }, + { 0x0d010425, 0x24000028 }, + { 0x0d010426, 0x24000028 }, + { 0x0d010427, 0x24000028 }, + { 0x0d010428, 0x1400ffd8 }, + { 0x0d010429, 0x1400ffd8 }, + { 0x0d01042a, 0x1400ffd8 }, + { 0x0d01042b, 0x1400ffd8 }, + { 0x0d01042c, 0x1400ffd8 }, + { 0x0d01042d, 0x1400ffd8 }, + { 0x0d01042e, 0x1400ffd8 }, + { 0x0d01042f, 0x1400ffd8 }, + { 0x0d010430, 0x1400ffd8 }, + { 0x0d010431, 0x1400ffd8 }, + { 0x0d010432, 0x1400ffd8 }, + { 0x0d010433, 0x1400ffd8 }, + { 0x0d010434, 0x1400ffd8 }, + { 0x0d010435, 0x1400ffd8 }, + { 0x0d010436, 0x1400ffd8 }, + { 0x0d010437, 0x1400ffd8 }, + { 0x0d010438, 0x1400ffd8 }, + { 0x0d010439, 0x1400ffd8 }, + { 0x0d01043a, 0x1400ffd8 }, + { 0x0d01043b, 0x1400ffd8 }, + { 0x0d01043c, 0x1400ffd8 }, + { 0x0d01043d, 0x1400ffd8 }, + { 0x0d01043e, 0x1400ffd8 }, + { 0x0d01043f, 0x1400ffd8 }, + { 0x0d010440, 0x1400ffd8 }, + { 0x0d010441, 0x1400ffd8 }, + { 0x0d010442, 0x1400ffd8 }, + { 0x0d010443, 0x1400ffd8 }, + { 0x0d010444, 0x1400ffd8 }, + { 0x0d010445, 0x1400ffd8 }, + { 0x0d010446, 0x1400ffd8 }, + { 0x0d010447, 0x1400ffd8 }, + { 0x0d010448, 0x1400ffd8 }, + { 0x0d010449, 0x1400ffd8 }, + { 0x0d01044a, 0x1400ffd8 }, + { 0x0d01044b, 0x1400ffd8 }, + { 0x0d01044c, 0x1400ffd8 }, + { 0x0d01044d, 0x1400ffd8 }, + { 0x0d01044e, 0x1400ffd8 }, + { 0x0d01044f, 0x1400ffd8 }, + { 0x2e810450, 0x1c00002f }, + { 0x2c810480, 0x1c00001d }, + { 0x2c8104a0, 0x34000009 }, + { 0x0b810800, 0x1c000005 }, + { 0x0b010808, 0x1c000000 }, + { 0x0b81080a, 0x1c00002b }, + { 0x0b810837, 0x1c000001 }, + { 0x0b01083c, 0x1c000000 }, + { 0x0b01083f, 0x1c000000 }, + { 0x41810900, 0x1c000015 }, + { 0x41810916, 0x3c000003 }, + { 0x4101091f, 0x54000000 }, + { 0x1e010a00, 0x1c000000 }, + { 0x1e810a01, 0x30000002 }, + { 0x1e810a05, 0x30000001 }, + { 0x1e810a0c, 0x30000003 }, + { 0x1e810a10, 0x1c000003 }, + { 0x1e810a15, 0x1c000002 }, + { 0x1e810a19, 0x1c00001a }, + { 0x1e810a38, 0x30000002 }, + { 0x1e010a3f, 0x30000000 }, + { 0x1e810a40, 0x3c000007 }, + { 0x1e810a50, 0x54000008 }, + { 0x3e812000, 0x1c00036e }, + { 0x3e812400, 0x38000062 }, + { 0x3e812470, 0x54000003 }, + { 0x0981d000, 0x680000f5 }, + { 0x0981d100, 0x68000026 }, + { 0x0981d12a, 0x6800003a }, + { 0x0981d165, 0x28000001 }, + { 0x1b81d167, 0x30000002 }, + { 0x0981d16a, 0x68000002 }, + { 0x0981d16d, 0x28000005 }, + { 0x0981d173, 0x04000007 }, + { 0x1b81d17b, 0x30000007 }, + { 0x0981d183, 0x68000001 }, + { 0x1b81d185, 0x30000006 }, + { 0x0981d18c, 0x6800001d }, + { 0x1b81d1aa, 0x30000003 }, + { 0x0981d1ae, 0x6800002f }, + { 0x1381d200, 0x68000041 }, + { 0x1381d242, 0x30000002 }, + { 0x1301d245, 0x68000000 }, + { 0x0981d300, 0x68000056 }, + { 0x0981d360, 0x3c000011 }, + { 0x0981d400, 0x24000019 }, + { 0x0981d41a, 0x14000019 }, + { 0x0981d434, 0x24000019 }, + { 0x0981d44e, 0x14000006 }, + { 0x0981d456, 0x14000011 }, + { 0x0981d468, 0x24000019 }, + { 0x0981d482, 0x14000019 }, + { 0x0901d49c, 0x24000000 }, + { 0x0981d49e, 0x24000001 }, + { 0x0901d4a2, 0x24000000 }, + { 0x0981d4a5, 0x24000001 }, + { 0x0981d4a9, 0x24000003 }, + { 0x0981d4ae, 0x24000007 }, + { 0x0981d4b6, 0x14000003 }, + { 0x0901d4bb, 0x14000000 }, + { 0x0981d4bd, 0x14000006 }, + { 0x0981d4c5, 0x1400000a }, + { 0x0981d4d0, 0x24000019 }, + { 0x0981d4ea, 0x14000019 }, + { 0x0981d504, 0x24000001 }, + { 0x0981d507, 0x24000003 }, + { 0x0981d50d, 0x24000007 }, + { 0x0981d516, 0x24000006 }, + { 0x0981d51e, 0x14000019 }, + { 0x0981d538, 0x24000001 }, + { 0x0981d53b, 0x24000003 }, + { 0x0981d540, 0x24000004 }, + { 0x0901d546, 0x24000000 }, + { 0x0981d54a, 0x24000006 }, + { 0x0981d552, 0x14000019 }, + { 0x0981d56c, 0x24000019 }, + { 0x0981d586, 0x14000019 }, + { 0x0981d5a0, 0x24000019 }, + { 0x0981d5ba, 0x14000019 }, + { 0x0981d5d4, 0x24000019 }, + { 0x0981d5ee, 0x14000019 }, + { 0x0981d608, 0x24000019 }, + { 0x0981d622, 0x14000019 }, + { 0x0981d63c, 0x24000019 }, + { 0x0981d656, 0x14000019 }, + { 0x0981d670, 0x24000019 }, + { 0x0981d68a, 0x1400001b }, + { 0x0981d6a8, 0x24000018 }, + { 0x0901d6c1, 0x64000000 }, + { 0x0981d6c2, 0x14000018 }, + { 0x0901d6db, 0x64000000 }, + { 0x0981d6dc, 0x14000005 }, + { 0x0981d6e2, 0x24000018 }, + { 0x0901d6fb, 0x64000000 }, + { 0x0981d6fc, 0x14000018 }, + { 0x0901d715, 0x64000000 }, + { 0x0981d716, 0x14000005 }, + { 0x0981d71c, 0x24000018 }, + { 0x0901d735, 0x64000000 }, + { 0x0981d736, 0x14000018 }, + { 0x0901d74f, 0x64000000 }, + { 0x0981d750, 0x14000005 }, + { 0x0981d756, 0x24000018 }, + { 0x0901d76f, 0x64000000 }, + { 0x0981d770, 0x14000018 }, + { 0x0901d789, 0x64000000 }, + { 0x0981d78a, 0x14000005 }, + { 0x0981d790, 0x24000018 }, + { 0x0901d7a9, 0x64000000 }, + { 0x0981d7aa, 0x14000018 }, + { 0x0901d7c3, 0x64000000 }, + { 0x0981d7c4, 0x14000005 }, + { 0x0901d7ca, 0x24000000 }, + { 0x0901d7cb, 0x14000000 }, + { 0x0981d7ce, 0x34000031 }, + { 0x16820000, 0x1c00a6d6 }, + { 0x1682f800, 0x1c00021d }, + { 0x090e0001, 0x04000000 }, + { 0x098e0020, 0x0400005f }, + { 0x1b8e0100, 0x300000ef }, + { 0x098f0000, 0x0c00fffd }, + { 0x09900000, 0x0c00fffd }, +}; diff --git a/Engine/lib/sdl/linux/libSDL-1.2.so.0 b/Engine/lib/sdl/linux/libSDL-1.2.so.0 new file mode 100644 index 000000000..69b58fc25 Binary files /dev/null and b/Engine/lib/sdl/linux/libSDL-1.2.so.0 differ diff --git a/Engine/lib/sdl/linux/readme.txt b/Engine/lib/sdl/linux/readme.txt new file mode 100644 index 000000000..185e62554 --- /dev/null +++ b/Engine/lib/sdl/linux/readme.txt @@ -0,0 +1,10 @@ +This is an i586 optimized SDL 1.2.5 library that is suitable for shipping +games. It is glibc 2.2.5+ compatible. Note that currently this library is +not used when building the engine source code (it is only used by the +install_build scripts). + +Built on RH 7.3 with gcc296. + +Configure command: + +./configure --target=i586-pc-linux-gnu --enable-debug=no --enable-audio=yes --enable-arts-shared=yes --enable-esd-shared=yes --enable-dlopen=yes diff --git a/Engine/lib/squish/README b/Engine/lib/squish/README new file mode 100644 index 000000000..d26b72ed5 --- /dev/null +++ b/Engine/lib/squish/README @@ -0,0 +1,35 @@ +LICENSE +------- + +The squish library is distributed under the terms and conditions of the MIT +license. This license is specified at the top of each source file and must be +preserved in its entirety. + +BUILDING AND INSTALLING THE LIBRARY +----------------------------------- + +If you are using Visual Studio 2003 or above under Windows then load the Visual +Studio 2003 project in the vs7 folder. By default, the library is built using +SSE2 optimisations. To change this either change or remove the SQUISH_USE_SSE=2 +from the preprocessor symbols. + +If you are using a Mac then load the Xcode 2.2 project in the distribution. By +default, the library is built using Altivec optimisations. To change this +either change or remove SQUISH_USE_ALTIVEC=1 from the preprocessor symbols. I +guess I'll have to think about changing this for the new Intel Macs that are +rolling out... + +If you are using unix then first edit the config file in the base directory of +the distribution, enabling Altivec or SSE with the USE_ALTIVEC or USE_SSE +variables, and editing the optimisation flags passed to the C++ compiler if +necessary. Then make can be used to build the library, and make install (from +the superuser account) can be used to install (into /usr/local by default). + +REPORTING BUGS OR FEATURE REQUESTS +---------------------------------- + +Feedback can be sent to Simon Brown (the developer) at si@sjbrown.co.uk + +New releases are announced on the squish library homepage at +http://sjbrown.co.uk/?code=squish + diff --git a/Engine/lib/squish/alpha.cpp b/Engine/lib/squish/alpha.cpp new file mode 100644 index 000000000..f4a72b474 --- /dev/null +++ b/Engine/lib/squish/alpha.cpp @@ -0,0 +1,348 @@ +/* ----------------------------------------------------------------------------- + + Copyright (c) 2006 Simon Brown si@sjbrown.co.uk + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + -------------------------------------------------------------------------- */ + +#include "alpha.h" +#include "squishMath.h" + +namespace squish { + +static int FloatToInt( float a, int limit ) +{ + // use ANSI round-to-zero behaviour to get round-to-nearest + int i = ( int )( a + 0.5f ); + + // clamp to the limit + if( i < 0 ) + i = 0; + else if( i > limit ) + i = limit; + + // done + return i; +} + +void CompressAlphaDxt3( u8 const* rgba, int mask, void* block ) +{ + u8* bytes = reinterpret_cast< u8* >( block ); + + // quantise and pack the alpha values pairwise + for( int i = 0; i < 8; ++i ) + { + // quantise down to 4 bits + float alpha1 = ( float )rgba[8*i + 3] * ( 15.0f/255.0f ); + float alpha2 = ( float )rgba[8*i + 7] * ( 15.0f/255.0f ); + int quant1 = FloatToInt( alpha1, 15 ); + int quant2 = FloatToInt( alpha2, 15 ); + + // set alpha to zero where masked + int bit1 = 1 << ( 2*i ); + int bit2 = 1 << ( 2*i + 1 ); + if( ( mask & bit1 ) == 0 ) + quant1 = 0; + if( ( mask & bit2 ) == 0 ) + quant2 = 0; + + // pack into the byte + bytes[i] = ( u8 )( quant1 | ( quant2 << 4 ) ); + } +} + +void DecompressAlphaDxt3( u8* rgba, void const* block ) +{ + u8 const* bytes = reinterpret_cast< u8 const* >( block ); + + // unpack the alpha values pairwise + for( int i = 0; i < 8; ++i ) + { + // quantise down to 4 bits + u8 quant = bytes[i]; + + // unpack the values + u8 lo = quant & 0x0f; + u8 hi = quant & 0xf0; + + // convert back up to bytes + rgba[8*i + 3] = lo | ( lo << 4 ); + rgba[8*i + 7] = hi | ( hi >> 4 ); + } +} + +static void FixRange( int& min, int& max, int steps ) +{ + if( max - min < steps ) + max = SquishMath::min( min + steps, 255 ); + if( max - min < steps ) + min = SquishMath::max( 0, max - steps ); +} + +static int FitCodes( u8 const* rgba, int mask, u8 const* codes, u8* indices ) +{ + // fit each alpha value to the codebook + int err = 0; + for( int i = 0; i < 16; ++i ) + { + // check this pixel is valid + int bit = 1 << i; + if( ( mask & bit ) == 0 ) + { + // use the first code + indices[i] = 0; + continue; + } + + // find the least error and corresponding index + int value = rgba[4*i + 3]; + int least = INT_MAX; + int index = 0; + for( int j = 0; j < 8; ++j ) + { + // get the squared error from this code + int dist = ( int )value - ( int )codes[j]; + dist *= dist; + + // compare with the best so far + if( dist < least ) + { + least = dist; + index = j; + } + } + + // save this index and accumulate the error + indices[i] = ( u8 )index; + err += least; + } + + // return the total error + return err; +} + +static void WriteAlphaBlock( int alpha0, int alpha1, u8 const* indices, void* block ) +{ + u8* bytes = reinterpret_cast< u8* >( block ); + + // write the first two bytes + bytes[0] = ( u8 )alpha0; + bytes[1] = ( u8 )alpha1; + + // pack the indices with 3 bits each + u8* dest = bytes + 2; + u8 const* src = indices; + for( int i = 0; i < 2; ++i ) + { + // pack 8 3-bit values + int value = 0; + for( int j = 0; j < 8; ++j ) + { + int index = *src++; + value |= ( index << 3*j ); + } + + // store in 3 bytes + for( int j = 0; j < 3; ++j ) + { + int byte = ( value >> 8*j ) & 0xff; + *dest++ = ( u8 )byte; + } + } +} + +static void WriteAlphaBlock5( int alpha0, int alpha1, u8 const* indices, void* block ) +{ + // check the relative values of the endpoints + if( alpha0 > alpha1 ) + { + // swap the indices + u8 swapped[16]; + for( int i = 0; i < 16; ++i ) + { + u8 index = indices[i]; + if( index == 0 ) + swapped[i] = 1; + else if( index == 1 ) + swapped[i] = 0; + else if( index <= 5 ) + swapped[i] = 7 - index; + else + swapped[i] = index; + } + + // write the block + WriteAlphaBlock( alpha1, alpha0, swapped, block ); + } + else + { + // write the block + WriteAlphaBlock( alpha0, alpha1, indices, block ); + } +} + +static void WriteAlphaBlock7( int alpha0, int alpha1, u8 const* indices, void* block ) +{ + // check the relative values of the endpoints + if( alpha0 < alpha1 ) + { + // swap the indices + u8 swapped[16]; + for( int i = 0; i < 16; ++i ) + { + u8 index = indices[i]; + if( index == 0 ) + swapped[i] = 1; + else if( index == 1 ) + swapped[i] = 0; + else + swapped[i] = 9 - index; + } + + // write the block + WriteAlphaBlock( alpha1, alpha0, swapped, block ); + } + else + { + // write the block + WriteAlphaBlock( alpha0, alpha1, indices, block ); + } +} + +void CompressAlphaDxt5( u8 const* rgba, int mask, void* block ) +{ + // get the range for 5-alpha and 7-alpha interpolation + int min5 = 255; + int max5 = 0; + int min7 = 255; + int max7 = 0; + for( int i = 0; i < 16; ++i ) + { + // check this pixel is valid + int bit = 1 << i; + if( ( mask & bit ) == 0 ) + continue; + + // incorporate into the min/max + int value = rgba[4*i + 3]; + if( value < min7 ) + min7 = value; + if( value > max7 ) + max7 = value; + if( value != 0 && value < min5 ) + min5 = value; + if( value != 255 && value > max5 ) + max5 = value; + } + + // handle the case that no valid range was found + if( min5 > max5 ) + min5 = max5; + if( min7 > max7 ) + min7 = max7; + + // fix the range to be the minimum in each case + FixRange( min5, max5, 5 ); + FixRange( min7, max7, 7 ); + + // set up the 5-alpha code book + u8 codes5[8]; + codes5[0] = ( u8 )min5; + codes5[1] = ( u8 )max5; + for( int i = 1; i < 5; ++i ) + codes5[1 + i] = ( u8 )( ( ( 5 - i )*min5 + i*max5 )/5 ); + codes5[6] = 0; + codes5[7] = 255; + + // set up the 7-alpha code book + u8 codes7[8]; + codes7[0] = ( u8 )min7; + codes7[1] = ( u8 )max7; + for( int i = 1; i < 7; ++i ) + codes7[1 + i] = ( u8 )( ( ( 7 - i )*min7 + i*max7 )/7 ); + + // fit the data to both code books + u8 indices5[16]; + u8 indices7[16]; + int err5 = FitCodes( rgba, mask, codes5, indices5 ); + int err7 = FitCodes( rgba, mask, codes7, indices7 ); + + // save the block with least error + if( err5 <= err7 ) + WriteAlphaBlock5( min5, max5, indices5, block ); + else + WriteAlphaBlock7( min7, max7, indices7, block ); +} + +void DecompressAlphaDxt5( u8* rgba, void const* block ) +{ + // get the two alpha values + u8 const* bytes = reinterpret_cast< u8 const* >( block ); + int alpha0 = bytes[0]; + int alpha1 = bytes[1]; + + // compare the values to build the codebook + u8 codes[8]; + codes[0] = ( u8 )alpha0; + codes[1] = ( u8 )alpha1; + if( alpha0 <= alpha1 ) + { + // use 5-alpha codebook + for( int i = 1; i < 5; ++i ) + codes[1 + i] = ( u8 )( ( ( 5 - i )*alpha0 + i*alpha1 )/5 ); + codes[6] = 0; + codes[7] = 255; + } + else + { + // use 7-alpha codebook + for( int i = 1; i < 7; ++i ) + codes[1 + i] = ( u8 )( ( ( 7 - i )*alpha0 + i*alpha1 )/7 ); + } + + // decode the indices + u8 indices[16]; + u8 const* src = bytes + 2; + u8* dest = indices; + for( int i = 0; i < 2; ++i ) + { + // grab 3 bytes + int value = 0; + for( int j = 0; j < 3; ++j ) + { + int byte = *src++; + value |= ( byte << 8*j ); + } + + // unpack 8 3-bit values from it + for( int j = 0; j < 8; ++j ) + { + int index = ( value >> 3*j ) & 0x7; + *dest++ = ( u8 )index; + } + } + + // write out the indexed codebook values + for( int i = 0; i < 16; ++i ) + rgba[4*i + 3] = codes[indices[i]]; +} + +} // namespace squish diff --git a/Engine/lib/squish/alpha.h b/Engine/lib/squish/alpha.h new file mode 100644 index 000000000..573605255 --- /dev/null +++ b/Engine/lib/squish/alpha.h @@ -0,0 +1,41 @@ +/* ----------------------------------------------------------------------------- + + Copyright (c) 2006 Simon Brown si@sjbrown.co.uk + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + -------------------------------------------------------------------------- */ + +#ifndef SQUISH_ALPHA_H +#define SQUISH_ALPHA_H + +#include + +namespace squish { + +void CompressAlphaDxt3( u8 const* rgba, int mask, void* block ); +void CompressAlphaDxt5( u8 const* rgba, int mask, void* block ); + +void DecompressAlphaDxt3( u8* rgba, void const* block ); +void DecompressAlphaDxt5( u8* rgba, void const* block ); + +} // namespace squish + +#endif // ndef SQUISH_ALPHA_H diff --git a/Engine/lib/squish/clusterfit.cpp b/Engine/lib/squish/clusterfit.cpp new file mode 100644 index 000000000..4ac89984d --- /dev/null +++ b/Engine/lib/squish/clusterfit.cpp @@ -0,0 +1,394 @@ +/* ----------------------------------------------------------------------------- + + Copyright (c) 2006 Simon Brown si@sjbrown.co.uk + Copyright (c) 2007 Ignacio Castano icastano@nvidia.com + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + -------------------------------------------------------------------------- */ + +#include "clusterfit.h" +#include "colourset.h" +#include "colourblock.h" +#include "squishMath.h" +#include + +namespace squish { + +ClusterFit::ClusterFit( ColourSet const* colours, int flags ) + : ColourFit( colours, flags ) +{ + // set the iteration count + m_iterationCount = ( m_flags & kColourIterativeClusterFit ) ? kMaxIterations : 1; + + // initialise the best error + m_besterror = VEC4_CONST( FLT_MAX ); + + // initialise the metric + bool perceptual = ( ( m_flags & kColourMetricPerceptual ) != 0 ); + if( perceptual ) + m_metric = Vec4( 0.2126f, 0.7152f, 0.0722f, 0.0f ); + else + m_metric = VEC4_CONST( 1.0f ); + + // cache some values + int const count = m_colours->GetCount(); + Vec3 const* values = m_colours->GetPoints(); + + // get the covariance matrix + Sym3x3 covariance = ComputeWeightedCovariance( count, values, m_colours->GetWeights() ); + + // compute the principle component + m_principle = ComputePrincipleComponent( covariance ); +} + +bool ClusterFit::ConstructOrdering( Vec3 const& axis, int iteration ) +{ + // cache some values + int const count = m_colours->GetCount(); + Vec3 const* values = m_colours->GetPoints(); + + // build the list of dot products + float dps[16]; + u8* order = ( u8* )m_order + 16*iteration; + for( int i = 0; i < count; ++i ) + { + dps[i] = Dot( values[i], axis ); + order[i] = ( u8 )i; + } + + // stable sort using them + for( int i = 0; i < count; ++i ) + { + for( int j = i; j > 0 && dps[j] < dps[j - 1]; --j ) + { + std::swap( dps[j], dps[j - 1] ); + std::swap( order[j], order[j - 1] ); + } + } + + // check this ordering is unique + for( int it = 0; it < iteration; ++it ) + { + u8 const* prev = ( u8* )m_order + 16*it; + bool same = true; + for( int i = 0; i < count; ++i ) + { + if( order[i] != prev[i] ) + { + same = false; + break; + } + } + if( same ) + return false; + } + + // copy the ordering and weight all the points + Vec3 const* unweighted = m_colours->GetPoints(); + float const* weights = m_colours->GetWeights(); + m_xsum_wsum = VEC4_CONST( 0.0f ); + for( int i = 0; i < count; ++i ) + { + int j = order[i]; + Vec4 p( unweighted[j].X(), unweighted[j].Y(), unweighted[j].Z(), 1.0f ); + Vec4 w( weights[j] ); + Vec4 x = p*w; + m_points_weights[i] = x; + m_xsum_wsum += x; + } + return true; +} + +void ClusterFit::Compress3( void* block ) +{ + // declare variables + int const count = m_colours->GetCount(); + Vec4 const two = VEC4_CONST( 2.0 ); + Vec4 const one = VEC4_CONST( 1.0f ); + Vec4 const half_half2( 0.5f, 0.5f, 0.5f, 0.25f ); + Vec4 const zero = VEC4_CONST( 0.0f ); + Vec4 const half = VEC4_CONST( 0.5f ); + Vec4 const grid( 31.0f, 63.0f, 31.0f, 0.0f ); + Vec4 const gridrcp( 1.0f/31.0f, 1.0f/63.0f, 1.0f/31.0f, 0.0f ); + + // prepare an ordering using the principle axis + ConstructOrdering( m_principle, 0 ); + + // check all possible clusters and iterate on the total order + Vec4 beststart = VEC4_CONST( 0.0f ); + Vec4 bestend = VEC4_CONST( 0.0f ); + Vec4 besterror = m_besterror; + u8 bestindices[16]; + int bestiteration = 0; + int besti = 0, bestj = 0; + + // loop over iterations (we avoid the case that all points in first or last cluster) + for( int iterationIndex = 0;; ) + { + // first cluster [0,i) is at the start + Vec4 part0 = VEC4_CONST( 0.0f ); + for( int i = 0; i < count; ++i ) + { + // second cluster [i,j) is half along + Vec4 part1 = ( i == 0 ) ? m_points_weights[0] : VEC4_CONST( 0.0f ); + int jmin = ( i == 0 ) ? 1 : i; + for( int j = jmin;; ) + { + // last cluster [j,count) is at the end + Vec4 part2 = m_xsum_wsum - part1 - part0; + + // compute least squares terms directly + Vec4 alphax_sum = MultiplyAdd( part1, half_half2, part0 ); + Vec4 alpha2_sum = alphax_sum.SplatW(); + + Vec4 betax_sum = MultiplyAdd( part1, half_half2, part2 ); + Vec4 beta2_sum = betax_sum.SplatW(); + + Vec4 alphabeta_sum = ( part1*half_half2 ).SplatW(); + + // compute the least-squares optimal points + Vec4 factor = Reciprocal( NegativeMultiplySubtract( alphabeta_sum, alphabeta_sum, alpha2_sum*beta2_sum ) ); + Vec4 a = NegativeMultiplySubtract( betax_sum, alphabeta_sum, alphax_sum*beta2_sum )*factor; + Vec4 b = NegativeMultiplySubtract( alphax_sum, alphabeta_sum, betax_sum*alpha2_sum )*factor; + + // clamp to the grid + a = Min( one, Max( zero, a ) ); + b = Min( one, Max( zero, b ) ); + a = Truncate( MultiplyAdd( grid, a, half ) )*gridrcp; + b = Truncate( MultiplyAdd( grid, b, half ) )*gridrcp; + + // compute the error (we skip the constant xxsum) + Vec4 e1 = MultiplyAdd( a*a, alpha2_sum, b*b*beta2_sum ); + Vec4 e2 = NegativeMultiplySubtract( a, alphax_sum, a*b*alphabeta_sum ); + Vec4 e3 = NegativeMultiplySubtract( b, betax_sum, e2 ); + Vec4 e4 = MultiplyAdd( two, e3, e1 ); + + // apply the metric to the error term + Vec4 e5 = e4*m_metric; + Vec4 error = e5.SplatX() + e5.SplatY() + e5.SplatZ(); + + // keep the solution if it wins + if( CompareAnyLessThan( error, besterror ) ) + { + beststart = a; + bestend = b; + besti = i; + bestj = j; + besterror = error; + bestiteration = iterationIndex; + } + + // advance + if( j == count ) + break; + part1 += m_points_weights[j]; + ++j; + } + + // advance + part0 += m_points_weights[i]; + } + + // stop if we didn't improve in this iteration + if( bestiteration != iterationIndex ) + break; + + // advance if possible + ++iterationIndex; + if( iterationIndex == m_iterationCount ) + break; + + // stop if a new iteration is an ordering that has already been tried + Vec3 axis = ( bestend - beststart ).GetVec3(); + if( !ConstructOrdering( axis, iterationIndex ) ) + break; + } + + // save the block if necessary + if( CompareAnyLessThan( besterror, m_besterror ) ) + { + // remap the indices + u8 const* order = ( u8* )m_order + 16*bestiteration; + + u8 unordered[16]; + for( int m = 0; m < besti; ++m ) + unordered[order[m]] = 0; + for( int m = besti; m < bestj; ++m ) + unordered[order[m]] = 2; + for( int m = bestj; m < count; ++m ) + unordered[order[m]] = 1; + + m_colours->RemapIndices( unordered, bestindices ); + + // save the block + WriteColourBlock3( beststart.GetVec3(), bestend.GetVec3(), bestindices, block ); + + // save the error + m_besterror = besterror; + } +} + +void ClusterFit::Compress4( void* block ) +{ + // declare variables + int const count = m_colours->GetCount(); + Vec4 const two = VEC4_CONST( 2.0f ); + Vec4 const one = VEC4_CONST( 1.0f ); + Vec4 const onethird_onethird2( 1.0f/3.0f, 1.0f/3.0f, 1.0f/3.0f, 1.0f/9.0f ); + Vec4 const twothirds_twothirds2( 2.0f/3.0f, 2.0f/3.0f, 2.0f/3.0f, 4.0f/9.0f ); + Vec4 const twonineths = VEC4_CONST( 2.0f/9.0f ); + Vec4 const zero = VEC4_CONST( 0.0f ); + Vec4 const half = VEC4_CONST( 0.5f ); + Vec4 const grid( 31.0f, 63.0f, 31.0f, 0.0f ); + Vec4 const gridrcp( 1.0f/31.0f, 1.0f/63.0f, 1.0f/31.0f, 0.0f ); + + // prepare an ordering using the principle axis + ConstructOrdering( m_principle, 0 ); + + // check all possible clusters and iterate on the total order + Vec4 beststart = VEC4_CONST( 0.0f ); + Vec4 bestend = VEC4_CONST( 0.0f ); + Vec4 besterror = m_besterror; + u8 bestindices[16]; + int bestiteration = 0; + int besti = 0, bestj = 0, bestk = 0; + + // loop over iterations (we avoid the case that all points in first or last cluster) + for( int iterationIndex = 0;; ) + { + // first cluster [0,i) is at the start + Vec4 part0 = VEC4_CONST( 0.0f ); + for( int i = 0; i < count; ++i ) + { + // second cluster [i,j) is one third along + Vec4 part1 = VEC4_CONST( 0.0f ); + for( int j = i;; ) + { + // third cluster [j,k) is two thirds along + Vec4 part2 = ( j == 0 ) ? m_points_weights[0] : VEC4_CONST( 0.0f ); + int kmin = ( j == 0 ) ? 1 : j; + for( int k = kmin;; ) + { + // last cluster [k,count) is at the end + Vec4 part3 = m_xsum_wsum - part2 - part1 - part0; + + // compute least squares terms directly + Vec4 const alphax_sum = MultiplyAdd( part2, onethird_onethird2, MultiplyAdd( part1, twothirds_twothirds2, part0 ) ); + Vec4 const alpha2_sum = alphax_sum.SplatW(); + + Vec4 const betax_sum = MultiplyAdd( part1, onethird_onethird2, MultiplyAdd( part2, twothirds_twothirds2, part3 ) ); + Vec4 const beta2_sum = betax_sum.SplatW(); + + Vec4 const alphabeta_sum = twonineths*( part1 + part2 ).SplatW(); + + // compute the least-squares optimal points + Vec4 factor = Reciprocal( NegativeMultiplySubtract( alphabeta_sum, alphabeta_sum, alpha2_sum*beta2_sum ) ); + Vec4 a = NegativeMultiplySubtract( betax_sum, alphabeta_sum, alphax_sum*beta2_sum )*factor; + Vec4 b = NegativeMultiplySubtract( alphax_sum, alphabeta_sum, betax_sum*alpha2_sum )*factor; + + // clamp to the grid + a = Min( one, Max( zero, a ) ); + b = Min( one, Max( zero, b ) ); + a = Truncate( MultiplyAdd( grid, a, half ) )*gridrcp; + b = Truncate( MultiplyAdd( grid, b, half ) )*gridrcp; + + // compute the error (we skip the constant xxsum) + Vec4 e1 = MultiplyAdd( a*a, alpha2_sum, b*b*beta2_sum ); + Vec4 e2 = NegativeMultiplySubtract( a, alphax_sum, a*b*alphabeta_sum ); + Vec4 e3 = NegativeMultiplySubtract( b, betax_sum, e2 ); + Vec4 e4 = MultiplyAdd( two, e3, e1 ); + + // apply the metric to the error term + Vec4 e5 = e4*m_metric; + Vec4 error = e5.SplatX() + e5.SplatY() + e5.SplatZ(); + + // keep the solution if it wins + if( CompareAnyLessThan( error, besterror ) ) + { + beststart = a; + bestend = b; + besterror = error; + besti = i; + bestj = j; + bestk = k; + bestiteration = iterationIndex; + } + + // advance + if( k == count ) + break; + part2 += m_points_weights[k]; + ++k; + } + + // advance + if( j == count ) + break; + part1 += m_points_weights[j]; + ++j; + } + + // advance + part0 += m_points_weights[i]; + } + + // stop if we didn't improve in this iteration + if( bestiteration != iterationIndex ) + break; + + // advance if possible + ++iterationIndex; + if( iterationIndex == m_iterationCount ) + break; + + // stop if a new iteration is an ordering that has already been tried + Vec3 axis = ( bestend - beststart ).GetVec3(); + if( !ConstructOrdering( axis, iterationIndex ) ) + break; + } + + // save the block if necessary + if( CompareAnyLessThan( besterror, m_besterror ) ) + { + // remap the indices + u8 const* order = ( u8* )m_order + 16*bestiteration; + + u8 unordered[16]; + for( int m = 0; m < besti; ++m ) + unordered[order[m]] = 0; + for( int m = besti; m < bestj; ++m ) + unordered[order[m]] = 2; + for( int m = bestj; m < bestk; ++m ) + unordered[order[m]] = 3; + for( int m = bestk; m < count; ++m ) + unordered[order[m]] = 1; + + m_colours->RemapIndices( unordered, bestindices ); + + // save the block + WriteColourBlock4( beststart.GetVec3(), bestend.GetVec3(), bestindices, block ); + + // save the error + m_besterror = besterror; + } +} + +} // namespace squish diff --git a/Engine/lib/squish/clusterfit.h b/Engine/lib/squish/clusterfit.h new file mode 100644 index 000000000..17db5d387 --- /dev/null +++ b/Engine/lib/squish/clusterfit.h @@ -0,0 +1,61 @@ +/* ----------------------------------------------------------------------------- + + Copyright (c) 2006 Simon Brown si@sjbrown.co.uk + Copyright (c) 2007 Ignacio Castano icastano@nvidia.com + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + -------------------------------------------------------------------------- */ + +#ifndef SQUISH_CLUSTERFIT_H +#define SQUISH_CLUSTERFIT_H + +#include +#include "maths.h" +#include "simd.h" +#include "colourfit.h" + +namespace squish { + +class ClusterFit : public ColourFit +{ +public: + ClusterFit( ColourSet const* colours, int flags ); + +private: + bool ConstructOrdering( Vec3 const& axis, int iteration ); + + virtual void Compress3( void* block ); + virtual void Compress4( void* block ); + + enum { kMaxIterations = 8 }; + + int m_iterationCount; + Vec3 m_principle; + u8 m_order[16*kMaxIterations]; + Vec4 m_points_weights[16]; + Vec4 m_xsum_wsum; + Vec4 m_metric; + Vec4 m_besterror; +}; + +} // namespace squish + +#endif // ndef SQUISH_CLUSTERFIT_H diff --git a/Engine/lib/squish/colourblock.cpp b/Engine/lib/squish/colourblock.cpp new file mode 100644 index 000000000..eff96b002 --- /dev/null +++ b/Engine/lib/squish/colourblock.cpp @@ -0,0 +1,215 @@ +/* ----------------------------------------------------------------------------- + + Copyright (c) 2006 Simon Brown si@sjbrown.co.uk + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + -------------------------------------------------------------------------- */ + +#include "colourblock.h" +#include + +namespace squish { + +static int FloatToInt( float a, int limit ) +{ + // use ANSI round-to-zero behaviour to get round-to-nearest + int i = ( int )( a + 0.5f ); + + // clamp to the limit + if( i < 0 ) + i = 0; + else if( i > limit ) + i = limit; + + // done + return i; +} + +static int FloatTo565( Vec3::Arg colour ) +{ + // get the components in the correct range + int r = FloatToInt( 31.0f*colour.X(), 31 ); + int g = FloatToInt( 63.0f*colour.Y(), 63 ); + int b = FloatToInt( 31.0f*colour.Z(), 31 ); + + // pack into a single value + return ( r << 11 ) | ( g << 5 ) | b; +} + +static void WriteColourBlock( int a, int b, u8* indices, void* block ) +{ + // get the block as bytes + u8* bytes = ( u8* )block; + + // write the endpoints + bytes[0] = ( u8 )( a & 0xff ); + bytes[1] = ( u8 )( a >> 8 ); + bytes[2] = ( u8 )( b & 0xff ); + bytes[3] = ( u8 )( b >> 8 ); + + // write the indices + for( int i = 0; i < 4; ++i ) + { + u8 const* ind = indices + 4*i; + bytes[4 + i] = ind[0] | ( ind[1] << 2 ) | ( ind[2] << 4 ) | ( ind[3] << 6 ); + } +} + +void WriteColourBlock3( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block ) +{ + // get the packed values + int a = FloatTo565( start ); + int b = FloatTo565( end ); + + // remap the indices + u8 remapped[16]; + if( a <= b ) + { + // use the indices directly + for( int i = 0; i < 16; ++i ) + remapped[i] = indices[i]; + } + else + { + // swap a and b + std::swap( a, b ); + for( int i = 0; i < 16; ++i ) + { + if( indices[i] == 0 ) + remapped[i] = 1; + else if( indices[i] == 1 ) + remapped[i] = 0; + else + remapped[i] = indices[i]; + } + } + + // write the block + WriteColourBlock( a, b, remapped, block ); +} + +void WriteColourBlock4( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block ) +{ + // get the packed values + int a = FloatTo565( start ); + int b = FloatTo565( end ); + + // remap the indices + u8 remapped[16]; + if( a < b ) + { + // swap a and b + std::swap( a, b ); + for( int i = 0; i < 16; ++i ) + remapped[i] = ( indices[i] ^ 0x1 ) & 0x3; + } + else if( a == b ) + { + // use index 0 + for( int i = 0; i < 16; ++i ) + remapped[i] = 0; + } + else + { + // use the indices directly + for( int i = 0; i < 16; ++i ) + remapped[i] = indices[i]; + } + + // write the block + WriteColourBlock( a, b, remapped, block ); +} + +static int Unpack565( u8 const* packed, u8* colour ) +{ + // build the packed value + int value = ( int )packed[0] | ( ( int )packed[1] << 8 ); + + // get the components in the stored range + u8 red = ( u8 )( ( value >> 11 ) & 0x1f ); + u8 green = ( u8 )( ( value >> 5 ) & 0x3f ); + u8 blue = ( u8 )( value & 0x1f ); + + // scale up to 8 bits + colour[0] = ( red << 3 ) | ( red >> 2 ); + colour[1] = ( green << 2 ) | ( green >> 4 ); + colour[2] = ( blue << 3 ) | ( blue >> 2 ); + colour[3] = 255; + + // return the value + return value; +} + +void DecompressColour( u8* rgba, void const* block, bool isDxt1 ) +{ + // get the block bytes + u8 const* bytes = reinterpret_cast< u8 const* >( block ); + + // unpack the endpoints + u8 codes[16]; + int a = Unpack565( bytes, codes ); + int b = Unpack565( bytes + 2, codes + 4 ); + + // generate the midpoints + for( int i = 0; i < 3; ++i ) + { + int c = codes[i]; + int d = codes[4 + i]; + + if( isDxt1 && a <= b ) + { + codes[8 + i] = ( u8 )( ( c + d )/2 ); + codes[12 + i] = 0; + } + else + { + codes[8 + i] = ( u8 )( ( 2*c + d )/3 ); + codes[12 + i] = ( u8 )( ( c + 2*d )/3 ); + } + } + + // fill in alpha for the intermediate values + codes[8 + 3] = 255; + codes[12 + 3] = ( isDxt1 && a <= b ) ? 0 : 255; + + // unpack the indices + u8 indices[16]; + for( int i = 0; i < 4; ++i ) + { + u8* ind = indices + 4*i; + u8 packed = bytes[4 + i]; + + ind[0] = packed & 0x3; + ind[1] = ( packed >> 2 ) & 0x3; + ind[2] = ( packed >> 4 ) & 0x3; + ind[3] = ( packed >> 6 ) & 0x3; + } + + // store out the colours + for( int i = 0; i < 16; ++i ) + { + u8 offset = 4*indices[i]; + for( int j = 0; j < 4; ++j ) + rgba[4*i + j] = codes[offset + j]; + } +} + +} // namespace squish diff --git a/Engine/lib/squish/colourblock.h b/Engine/lib/squish/colourblock.h new file mode 100644 index 000000000..df0a47217 --- /dev/null +++ b/Engine/lib/squish/colourblock.h @@ -0,0 +1,41 @@ +/* ----------------------------------------------------------------------------- + + Copyright (c) 2006 Simon Brown si@sjbrown.co.uk + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + -------------------------------------------------------------------------- */ + +#ifndef SQUISH_COLOURBLOCK_H +#define SQUISH_COLOURBLOCK_H + +#include +#include "maths.h" + +namespace squish { + +void WriteColourBlock3( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block ); +void WriteColourBlock4( Vec3::Arg start, Vec3::Arg end, u8 const* indices, void* block ); + +void DecompressColour( u8* rgba, void const* block, bool isDxt1 ); + +} // namespace squish + +#endif // ndef SQUISH_COLOURBLOCK_H diff --git a/Engine/lib/squish/colourfit.cpp b/Engine/lib/squish/colourfit.cpp new file mode 100644 index 000000000..dba2b87e8 --- /dev/null +++ b/Engine/lib/squish/colourfit.cpp @@ -0,0 +1,50 @@ +/* ----------------------------------------------------------------------------- + + Copyright (c) 2006 Simon Brown si@sjbrown.co.uk + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + -------------------------------------------------------------------------- */ + +#include "colourfit.h" +#include "colourset.h" + +namespace squish { + +ColourFit::ColourFit( ColourSet const* colours, int flags ) + : m_colours( colours ), + m_flags( flags ) +{ +} + +void ColourFit::Compress( void* block ) +{ + bool isDxt1 = ( ( m_flags & kDxt1 ) != 0 ); + if( isDxt1 ) + { + Compress3( block ); + if( !m_colours->IsTransparent() ) + Compress4( block ); + } + else + Compress4( block ); +} + +} // namespace squish diff --git a/Engine/lib/squish/colourfit.h b/Engine/lib/squish/colourfit.h new file mode 100644 index 000000000..a2d0559a3 --- /dev/null +++ b/Engine/lib/squish/colourfit.h @@ -0,0 +1,53 @@ +/* ----------------------------------------------------------------------------- + + Copyright (c) 2006 Simon Brown si@sjbrown.co.uk + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + -------------------------------------------------------------------------- */ + +#ifndef SQUISH_COLOURFIT_H +#define SQUISH_COLOURFIT_H + +#include +#include "maths.h" + +namespace squish { + +class ColourSet; + +class ColourFit +{ +public: + ColourFit( ColourSet const* colours, int flags ); + + void Compress( void* block ); + +protected: + virtual void Compress3( void* block ) = 0; + virtual void Compress4( void* block ) = 0; + + ColourSet const* m_colours; + int m_flags; +}; + +} // namespace squish + +#endif // ndef SQUISH_COLOURFIT_H diff --git a/Engine/lib/squish/colourset.cpp b/Engine/lib/squish/colourset.cpp new file mode 100644 index 000000000..15cc690c6 --- /dev/null +++ b/Engine/lib/squish/colourset.cpp @@ -0,0 +1,122 @@ +/* ----------------------------------------------------------------------------- + + Copyright (c) 2006 Simon Brown si@sjbrown.co.uk + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + -------------------------------------------------------------------------- */ + +#include "colourset.h" +#include "squishMath.h" + +namespace squish { + +ColourSet::ColourSet( u8 const* rgba, int mask, int flags ) + : m_count( 0 ), + m_transparent( false ) +{ + // check the compression mode for dxt1 + bool isDxt1 = ( ( flags & kDxt1 ) != 0 ); + bool weightByAlpha = ( ( flags & kWeightColourByAlpha ) != 0 ); + + // create the minimal set + for( int i = 0; i < 16; ++i ) + { + // check this pixel is enabled + int bit = 1 << i; + if( ( mask & bit ) == 0 ) + { + m_remap[i] = -1; + continue; + } + + // check for transparent pixels when using dxt1 + if( isDxt1 && rgba[4*i + 3] < 128 ) + { + m_remap[i] = -1; + m_transparent = true; + continue; + } + + // loop over previous points for a match + for( int j = 0;; ++j ) + { + // allocate a new point + if( j == i ) + { + // normalise coordinates to [0,1] + float x = ( float )rgba[4*i] / 255.0f; + float y = ( float )rgba[4*i + 1] / 255.0f; + float z = ( float )rgba[4*i + 2] / 255.0f; + + // ensure there is always non-zero weight even for zero alpha + float w = ( float )( rgba[4*i + 3] + 1 ) / 256.0f; + + // add the point + m_points[m_count] = Vec3( x, y, z ); + m_weights[m_count] = ( weightByAlpha ? w : 1.0f ); + m_remap[i] = m_count; + + // advance + ++m_count; + break; + } + + // check for a match + int oldbit = 1 << j; + bool match = ( ( mask & oldbit ) != 0 ) + && ( rgba[4*i] == rgba[4*j] ) + && ( rgba[4*i + 1] == rgba[4*j + 1] ) + && ( rgba[4*i + 2] == rgba[4*j + 2] ) + && ( rgba[4*j + 3] >= 128 || !isDxt1 ); + if( match ) + { + // get the index of the match + int index = m_remap[j]; + + // ensure there is always non-zero weight even for zero alpha + float w = ( float )( rgba[4*i + 3] + 1 ) / 256.0f; + + // map to this point and increase the weight + m_weights[index] += ( weightByAlpha ? w : 1.0f ); + m_remap[i] = index; + break; + } + } + } + + // square root the weights + for( int i = 0; i < m_count; ++i ) + m_weights[i] = SquishMath::sqrt( m_weights[i] ); +} + +void ColourSet::RemapIndices( u8 const* source, u8* target ) const +{ + for( int i = 0; i < 16; ++i ) + { + int j = m_remap[i]; + if( j == -1 ) + target[i] = 3; + else + target[i] = source[j]; + } +} + +} // namespace squish diff --git a/Engine/lib/squish/colourset.h b/Engine/lib/squish/colourset.h new file mode 100644 index 000000000..dcf56ae28 --- /dev/null +++ b/Engine/lib/squish/colourset.h @@ -0,0 +1,58 @@ +/* ----------------------------------------------------------------------------- + + Copyright (c) 2006 Simon Brown si@sjbrown.co.uk + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + -------------------------------------------------------------------------- */ + +#ifndef SQUISH_COLOURSET_H +#define SQUISH_COLOURSET_H + +#include +#include "maths.h" + +namespace squish { + +/*! @brief Represents a set of block colours +*/ +class ColourSet +{ +public: + ColourSet( u8 const* rgba, int mask, int flags ); + + int GetCount() const { return m_count; } + Vec3 const* GetPoints() const { return m_points; } + float const* GetWeights() const { return m_weights; } + bool IsTransparent() const { return m_transparent; } + + void RemapIndices( u8 const* source, u8* target ) const; + +private: + int m_count; + Vec3 m_points[16]; + float m_weights[16]; + int m_remap[16]; + bool m_transparent; +}; + +} // namespace sqish + +#endif // ndef SQUISH_COLOURSET_H diff --git a/Engine/lib/squish/config.h b/Engine/lib/squish/config.h new file mode 100644 index 000000000..8427407d4 --- /dev/null +++ b/Engine/lib/squish/config.h @@ -0,0 +1,55 @@ +/* ----------------------------------------------------------------------------- + + Copyright (c) 2006 Simon Brown si@sjbrown.co.uk + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + -------------------------------------------------------------------------- */ + +#ifndef SQUISH_CONFIG_H +#define SQUISH_CONFIG_H + +// Set to 1 when building squish to use Altivec instructions. +#ifndef SQUISH_USE_ALTIVEC +#define SQUISH_USE_ALTIVEC 0 +#endif + +// Set to 1 or 2 when building squish to use SSE or SSE2 instructions. +#ifndef SQUISH_USE_SSE +#define SQUISH_USE_SSE 0 +#endif + +// Internally et SQUISH_USE_SIMD when either Altivec or SSE is available. +#if SQUISH_USE_ALTIVEC && SQUISH_USE_SSE +#error "Cannot enable both Altivec and SSE!" +#endif +#if SQUISH_USE_ALTIVEC || SQUISH_USE_SSE +#define SQUISH_USE_SIMD 1 +#else +#define SQUISH_USE_SIMD 0 +#endif + +// TORQUE MODIFICATIONS +#ifdef TORQUE_DEBUG +# undef SQUISH_USE_SSE +# define SQUISH_USE_SSE 0 +#endif + +#endif // ndef SQUISH_CONFIG_H diff --git a/Engine/lib/squish/maths.cpp b/Engine/lib/squish/maths.cpp new file mode 100644 index 000000000..37bf51683 --- /dev/null +++ b/Engine/lib/squish/maths.cpp @@ -0,0 +1,226 @@ +/* ----------------------------------------------------------------------------- + + Copyright (c) 2006 Simon Brown si@sjbrown.co.uk + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + -------------------------------------------------------------------------- */ + +/*! @file + + The symmetric eigensystem solver algorithm is from + http://www.geometrictools.com/Documentation/EigenSymmetric3x3.pdf +*/ + +#include "maths.h" + +namespace squish { + +Sym3x3 ComputeWeightedCovariance( int n, Vec3 const* points, float const* weights ) +{ + // compute the centroid + float total = 0.0f; + Vec3 centroid( 0.0f ); + for( int i = 0; i < n; ++i ) + { + total += weights[i]; + centroid += weights[i]*points[i]; + } + centroid /= total; + + // accumulate the covariance matrix + Sym3x3 covariance( 0.0f ); + for( int i = 0; i < n; ++i ) + { + Vec3 a = points[i] - centroid; + Vec3 b = weights[i]*a; + + covariance[0] += a.X()*b.X(); + covariance[1] += a.X()*b.Y(); + covariance[2] += a.X()*b.Z(); + covariance[3] += a.Y()*b.Y(); + covariance[4] += a.Y()*b.Z(); + covariance[5] += a.Z()*b.Z(); + } + + // return it + return covariance; +} + +static Vec3 GetMultiplicity1Evector( Sym3x3 const& matrix, float evalue ) +{ + // compute M + Sym3x3 m; + m[0] = matrix[0] - evalue; + m[1] = matrix[1]; + m[2] = matrix[2]; + m[3] = matrix[3] - evalue; + m[4] = matrix[4]; + m[5] = matrix[5] - evalue; + + // compute U + Sym3x3 u; + u[0] = m[3]*m[5] - m[4]*m[4]; + u[1] = m[2]*m[4] - m[1]*m[5]; + u[2] = m[1]*m[4] - m[2]*m[3]; + u[3] = m[0]*m[5] - m[2]*m[2]; + u[4] = m[1]*m[2] - m[4]*m[0]; + u[5] = m[0]*m[3] - m[1]*m[1]; + + // find the largest component + float mc = SquishMath::fabs( u[0] ); + int mi = 0; + for( int i = 1; i < 6; ++i ) + { + float c = SquishMath::fabs( u[i] ); + if( c > mc ) + { + mc = c; + mi = i; + } + } + + // pick the column with this component + switch( mi ) + { + case 0: + return Vec3( u[0], u[1], u[2] ); + + case 1: + case 3: + return Vec3( u[1], u[3], u[4] ); + + default: + return Vec3( u[2], u[4], u[5] ); + } +} + +static Vec3 GetMultiplicity2Evector( Sym3x3 const& matrix, float evalue ) +{ + // compute M + Sym3x3 m; + m[0] = matrix[0] - evalue; + m[1] = matrix[1]; + m[2] = matrix[2]; + m[3] = matrix[3] - evalue; + m[4] = matrix[4]; + m[5] = matrix[5] - evalue; + + // find the largest component + float mc = SquishMath::fabs( m[0] ); + int mi = 0; + for( int i = 1; i < 6; ++i ) + { + float c = SquishMath::fabs( m[i] ); + if( c > mc ) + { + mc = c; + mi = i; + } + } + + // pick the first eigenvector based on this index + switch( mi ) + { + case 0: + case 1: + return Vec3( -m[1], m[0], 0.0f ); + + case 2: + return Vec3( m[2], 0.0f, -m[0] ); + + case 3: + case 4: + return Vec3( 0.0f, -m[4], m[3] ); + + default: + return Vec3( 0.0f, -m[5], m[4] ); + } +} + +Vec3 ComputePrincipleComponent( Sym3x3 const& matrix ) +{ + // compute the cubic coefficients + float c0 = matrix[0]*matrix[3]*matrix[5] + + 2.0f*matrix[1]*matrix[2]*matrix[4] + - matrix[0]*matrix[4]*matrix[4] + - matrix[3]*matrix[2]*matrix[2] + - matrix[5]*matrix[1]*matrix[1]; + float c1 = matrix[0]*matrix[3] + matrix[0]*matrix[5] + matrix[3]*matrix[5] + - matrix[1]*matrix[1] - matrix[2]*matrix[2] - matrix[4]*matrix[4]; + float c2 = matrix[0] + matrix[3] + matrix[5]; + + // compute the quadratic coefficients + float a = c1 - ( 1.0f/3.0f )*c2*c2; + float b = ( -2.0f/27.0f )*c2*c2*c2 + ( 1.0f/3.0f )*c1*c2 - c0; + + // compute the root count check + float Q = 0.25f*b*b + ( 1.0f/27.0f )*a*a*a; + + // test the multiplicity + if( FLT_EPSILON < Q ) + { + // only one root, which implies we have a multiple of the identity + return Vec3( 1.0f ); + } + else if( Q < -FLT_EPSILON ) + { + // three distinct roots + float theta = SquishMath::atan2( SquishMath::sqrt( -Q ), -0.5f*b ); + float rho = SquishMath::sqrt( 0.25f*b*b - Q ); + + float rt = SquishMath::pow( rho, 1.0f/3.0f ); + float ct = SquishMath::cos( theta/3.0f ); + float st = SquishMath::sin( theta/3.0f ); + + float l1 = ( 1.0f/3.0f )*c2 + 2.0f*rt*ct; + float l2 = ( 1.0f/3.0f )*c2 - rt*( ct + ( float )SquishMath::sqrt( 3.0f )*st ); + float l3 = ( 1.0f/3.0f )*c2 - rt*( ct - ( float )SquishMath::sqrt( 3.0f )*st ); + + // pick the larger + if( SquishMath::fabs( l2 ) > SquishMath::fabs( l1 ) ) + l1 = l2; + if( SquishMath::fabs( l3 ) > SquishMath::fabs( l1 ) ) + l1 = l3; + + // get the eigenvector + return GetMultiplicity1Evector( matrix, l1 ); + } + else // if( -FLT_EPSILON <= Q && Q <= FLT_EPSILON ) + { + // two roots + float rt; + if( b < 0.0f ) + rt = -SquishMath::pow( -0.5f*b, 1.0f/3.0f ); + else + rt = SquishMath::pow( 0.5f*b, 1.0f/3.0f ); + + float l1 = ( 1.0f/3.0f )*c2 + rt; // repeated + float l2 = ( 1.0f/3.0f )*c2 - 2.0f*rt; + + // get the eigenvector + if( SquishMath::fabs( l1 ) > SquishMath::fabs( l2 ) ) + return GetMultiplicity2Evector( matrix, l1 ); + else + return GetMultiplicity1Evector( matrix, l2 ); + } +} + +} // namespace squish diff --git a/Engine/lib/squish/maths.h b/Engine/lib/squish/maths.h new file mode 100644 index 000000000..47f455bda --- /dev/null +++ b/Engine/lib/squish/maths.h @@ -0,0 +1,232 @@ +/* ----------------------------------------------------------------------------- + + Copyright (c) 2006 Simon Brown si@sjbrown.co.uk + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + -------------------------------------------------------------------------- */ + +#ifndef SQUISH_MATHS_H +#define SQUISH_MATHS_H + +#include "squishMath.h" +#include "config.h" + +namespace squish { + +class Vec3 +{ +public: + typedef Vec3 const& Arg; + + Vec3() + { + } + + explicit Vec3( float s ) + { + m_x = s; + m_y = s; + m_z = s; + } + + Vec3( float x, float y, float z ) + { + m_x = x; + m_y = y; + m_z = z; + } + + float X() const { return m_x; } + float Y() const { return m_y; } + float Z() const { return m_z; } + + Vec3 operator-() const + { + return Vec3( -m_x, -m_y, -m_z ); + } + + Vec3& operator+=( Arg v ) + { + m_x += v.m_x; + m_y += v.m_y; + m_z += v.m_z; + return *this; + } + + Vec3& operator-=( Arg v ) + { + m_x -= v.m_x; + m_y -= v.m_y; + m_z -= v.m_z; + return *this; + } + + Vec3& operator*=( Arg v ) + { + m_x *= v.m_x; + m_y *= v.m_y; + m_z *= v.m_z; + return *this; + } + + Vec3& operator*=( float s ) + { + m_x *= s; + m_y *= s; + m_z *= s; + return *this; + } + + Vec3& operator/=( Arg v ) + { + m_x /= v.m_x; + m_y /= v.m_y; + m_z /= v.m_z; + return *this; + } + + Vec3& operator/=( float s ) + { + float t = 1.0f/s; + m_x *= t; + m_y *= t; + m_z *= t; + return *this; + } + + friend Vec3 operator+( Arg left, Arg right ) + { + Vec3 copy( left ); + return copy += right; + } + + friend Vec3 operator-( Arg left, Arg right ) + { + Vec3 copy( left ); + return copy -= right; + } + + friend Vec3 operator*( Arg left, Arg right ) + { + Vec3 copy( left ); + return copy *= right; + } + + friend Vec3 operator*( Arg left, float right ) + { + Vec3 copy( left ); + return copy *= right; + } + + friend Vec3 operator*( float left, Arg right ) + { + Vec3 copy( right ); + return copy *= left; + } + + friend Vec3 operator/( Arg left, Arg right ) + { + Vec3 copy( left ); + return copy /= right; + } + + friend Vec3 operator/( Arg left, float right ) + { + Vec3 copy( left ); + return copy /= right; + } + + friend float Dot( Arg left, Arg right ) + { + return left.m_x*right.m_x + left.m_y*right.m_y + left.m_z*right.m_z; + } + + friend Vec3 Min( Arg left, Arg right ) + { + return Vec3( + SquishMath::min( left.m_x, right.m_x ), + SquishMath::min( left.m_y, right.m_y ), + SquishMath::min( left.m_z, right.m_z ) + ); + } + + friend Vec3 Max( Arg left, Arg right ) + { + return Vec3( + SquishMath::max( left.m_x, right.m_x ), + SquishMath::max( left.m_y, right.m_y ), + SquishMath::max( left.m_z, right.m_z ) + ); + } + + friend Vec3 Truncate( Arg v ) + { + return Vec3( + v.m_x > 0.0f ? SquishMath::floor( v.m_x ) : SquishMath::ceil( v.m_x ), + v.m_y > 0.0f ? SquishMath::floor( v.m_y ) : SquishMath::ceil( v.m_y ), + v.m_z > 0.0f ? SquishMath::floor( v.m_z ) : SquishMath::ceil( v.m_z ) + ); + } + +private: + float m_x; + float m_y; + float m_z; +}; + +inline float LengthSquared( Vec3::Arg v ) +{ + return Dot( v, v ); +} + +class Sym3x3 +{ +public: + Sym3x3() + { + } + + Sym3x3( float s ) + { + for( int i = 0; i < 6; ++i ) + m_x[i] = s; + } + + float operator[]( int index ) const + { + return m_x[index]; + } + + float& operator[]( int index ) + { + return m_x[index]; + } + +private: + float m_x[6]; +}; + +Sym3x3 ComputeWeightedCovariance( int n, Vec3 const* points, float const* weights ); +Vec3 ComputePrincipleComponent( Sym3x3 const& matrix ); + +} // namespace squish + +#endif // ndef SQUISH_MATHS_H diff --git a/Engine/lib/squish/rangefit.cpp b/Engine/lib/squish/rangefit.cpp new file mode 100644 index 000000000..5a6643605 --- /dev/null +++ b/Engine/lib/squish/rangefit.cpp @@ -0,0 +1,202 @@ +/* ----------------------------------------------------------------------------- + + Copyright (c) 2006 Simon Brown si@sjbrown.co.uk + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + -------------------------------------------------------------------------- */ + +#include "rangefit.h" +#include "colourset.h" +#include "colourblock.h" +#include + +namespace squish { + +RangeFit::RangeFit( ColourSet const* colours, int flags ) + : ColourFit( colours, flags ) +{ + // initialise the metric + bool perceptual = ( ( m_flags & kColourMetricPerceptual ) != 0 ); + if( perceptual ) + m_metric = Vec3( 0.2126f, 0.7152f, 0.0722f ); + else + m_metric = Vec3( 1.0f ); + + // initialise the best error + m_besterror = FLT_MAX; + + // cache some values + int const count = m_colours->GetCount(); + Vec3 const* values = m_colours->GetPoints(); + float const* weights = m_colours->GetWeights(); + + // get the covariance matrix + Sym3x3 covariance = ComputeWeightedCovariance( count, values, weights ); + + // compute the principle component + Vec3 principle = ComputePrincipleComponent( covariance ); + + // get the min and max range as the codebook endpoints + Vec3 start( 0.0f ); + Vec3 end( 0.0f ); + if( count > 0 ) + { + float min, max; + + // compute the range + start = end = values[0]; + min = max = Dot( values[0], principle ); + for( int i = 1; i < count; ++i ) + { + float val = Dot( values[i], principle ); + if( val < min ) + { + start = values[i]; + min = val; + } + else if( val > max ) + { + end = values[i]; + max = val; + } + } + } + + // clamp the output to [0, 1] + Vec3 const one( 1.0f ); + Vec3 const zero( 0.0f ); + start = Min( one, Max( zero, start ) ); + end = Min( one, Max( zero, end ) ); + + // clamp to the grid and save + Vec3 const grid( 31.0f, 63.0f, 31.0f ); + Vec3 const gridrcp( 1.0f/31.0f, 1.0f/63.0f, 1.0f/31.0f ); + Vec3 const half( 0.5f ); + m_start = Truncate( grid*start + half )*gridrcp; + m_end = Truncate( grid*end + half )*gridrcp; +} + +void RangeFit::Compress3( void* block ) +{ + // cache some values + int const count = m_colours->GetCount(); + Vec3 const* values = m_colours->GetPoints(); + + // create a codebook + Vec3 codes[3]; + codes[0] = m_start; + codes[1] = m_end; + codes[2] = 0.5f*m_start + 0.5f*m_end; + + // match each point to the closest code + u8 closest[16]; + float error = 0.0f; + for( int i = 0; i < count; ++i ) + { + // find the closest code + float dist = FLT_MAX; + int idx = 0; + for( int j = 0; j < 3; ++j ) + { + float d = LengthSquared( m_metric*( values[i] - codes[j] ) ); + if( d < dist ) + { + dist = d; + idx = j; + } + } + + // save the index + closest[i] = ( u8 )idx; + + // accumulate the error + error += dist; + } + + // save this scheme if it wins + if( error < m_besterror ) + { + // remap the indices + u8 indices[16]; + m_colours->RemapIndices( closest, indices ); + + // save the block + WriteColourBlock3( m_start, m_end, indices, block ); + + // save the error + m_besterror = error; + } +} + +void RangeFit::Compress4( void* block ) +{ + // cache some values + int const count = m_colours->GetCount(); + Vec3 const* values = m_colours->GetPoints(); + + // create a codebook + Vec3 codes[4]; + codes[0] = m_start; + codes[1] = m_end; + codes[2] = ( 2.0f/3.0f )*m_start + ( 1.0f/3.0f )*m_end; + codes[3] = ( 1.0f/3.0f )*m_start + ( 2.0f/3.0f )*m_end; + + // match each point to the closest code + u8 closest[16]; + float error = 0.0f; + for( int i = 0; i < count; ++i ) + { + // find the closest code + float dist = FLT_MAX; + int idx = 0; + for( int j = 0; j < 4; ++j ) + { + float d = LengthSquared( m_metric*( values[i] - codes[j] ) ); + if( d < dist ) + { + dist = d; + idx = j; + } + } + + // save the index + closest[i] = ( u8 )idx; + + // accumulate the error + error += dist; + } + + // save this scheme if it wins + if( error < m_besterror ) + { + // remap the indices + u8 indices[16]; + m_colours->RemapIndices( closest, indices ); + + // save the block + WriteColourBlock4( m_start, m_end, indices, block ); + + // save the error + m_besterror = error; + } +} + +} // namespace squish diff --git a/Engine/lib/squish/rangefit.h b/Engine/lib/squish/rangefit.h new file mode 100644 index 000000000..795201993 --- /dev/null +++ b/Engine/lib/squish/rangefit.h @@ -0,0 +1,54 @@ +/* ----------------------------------------------------------------------------- + + Copyright (c) 2006 Simon Brown si@sjbrown.co.uk + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + -------------------------------------------------------------------------- */ + +#ifndef SQUISH_RANGEFIT_H +#define SQUISH_RANGEFIT_H + +#include +#include "colourfit.h" +#include "maths.h" + +namespace squish { + +class ColourSet; + +class RangeFit : public ColourFit +{ +public: + RangeFit( ColourSet const* colours, int flags ); + +private: + virtual void Compress3( void* block ); + virtual void Compress4( void* block ); + + Vec3 m_metric; + Vec3 m_start; + Vec3 m_end; + float m_besterror; +}; + +} // squish + +#endif // ndef SQUISH_RANGEFIT_H diff --git a/Engine/lib/squish/simd.h b/Engine/lib/squish/simd.h new file mode 100644 index 000000000..22bd10a46 --- /dev/null +++ b/Engine/lib/squish/simd.h @@ -0,0 +1,40 @@ +/* ----------------------------------------------------------------------------- + + Copyright (c) 2006 Simon Brown si@sjbrown.co.uk + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + -------------------------------------------------------------------------- */ + +#ifndef SQUISH_SIMD_H +#define SQUISH_SIMD_H + +#include "maths.h" + +#if SQUISH_USE_ALTIVEC +#include "simd_ve.h" +#elif SQUISH_USE_SSE +#include "simd_sse.h" +#else +#include "simd_float.h" +#endif + + +#endif // ndef SQUISH_SIMD_H diff --git a/Engine/lib/squish/simd_float.h b/Engine/lib/squish/simd_float.h new file mode 100644 index 000000000..6052736f3 --- /dev/null +++ b/Engine/lib/squish/simd_float.h @@ -0,0 +1,183 @@ +/* ----------------------------------------------------------------------------- + + Copyright (c) 2006 Simon Brown si@sjbrown.co.uk + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + -------------------------------------------------------------------------- */ + +#ifndef SQUISH_SIMD_FLOAT_H +#define SQUISH_SIMD_FLOAT_H + +#include + +namespace squish { + +#define VEC4_CONST( X ) Vec4( X ) + +class Vec4 +{ +public: + typedef Vec4 const& Arg; + + Vec4() {} + + explicit Vec4( float s ) + : m_x( s ), + m_y( s ), + m_z( s ), + m_w( s ) + { + } + + Vec4( float x, float y, float z, float w ) + : m_x( x ), + m_y( y ), + m_z( z ), + m_w( w ) + { + } + + Vec3 GetVec3() const + { + return Vec3( m_x, m_y, m_z ); + } + + Vec4 SplatX() const { return Vec4( m_x ); } + Vec4 SplatY() const { return Vec4( m_y ); } + Vec4 SplatZ() const { return Vec4( m_z ); } + Vec4 SplatW() const { return Vec4( m_w ); } + + Vec4& operator+=( Arg v ) + { + m_x += v.m_x; + m_y += v.m_y; + m_z += v.m_z; + m_w += v.m_w; + return *this; + } + + Vec4& operator-=( Arg v ) + { + m_x -= v.m_x; + m_y -= v.m_y; + m_z -= v.m_z; + m_w -= v.m_w; + return *this; + } + + Vec4& operator*=( Arg v ) + { + m_x *= v.m_x; + m_y *= v.m_y; + m_z *= v.m_z; + m_w *= v.m_w; + return *this; + } + + friend Vec4 operator+( Vec4::Arg left, Vec4::Arg right ) + { + Vec4 copy( left ); + return copy += right; + } + + friend Vec4 operator-( Vec4::Arg left, Vec4::Arg right ) + { + Vec4 copy( left ); + return copy -= right; + } + + friend Vec4 operator*( Vec4::Arg left, Vec4::Arg right ) + { + Vec4 copy( left ); + return copy *= right; + } + + //! Returns a*b + c + friend Vec4 MultiplyAdd( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c ) + { + return a*b + c; + } + + //! Returns -( a*b - c ) + friend Vec4 NegativeMultiplySubtract( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c ) + { + return c - a*b; + } + + friend Vec4 Reciprocal( Vec4::Arg v ) + { + return Vec4( + 1.0f/v.m_x, + 1.0f/v.m_y, + 1.0f/v.m_z, + 1.0f/v.m_w + ); + } + + friend Vec4 Min( Vec4::Arg left, Vec4::Arg right ) + { + return Vec4( + SquishMath::min( left.m_x, right.m_x ), + SquishMath::min( left.m_y, right.m_y ), + SquishMath::min( left.m_z, right.m_z ), + SquishMath::min( left.m_w, right.m_w ) + ); + } + + friend Vec4 Max( Vec4::Arg left, Vec4::Arg right ) + { + return Vec4( + SquishMath::max( left.m_x, right.m_x ), + SquishMath::max( left.m_y, right.m_y ), + SquishMath::max( left.m_z, right.m_z ), + SquishMath::max( left.m_w, right.m_w ) + ); + } + + friend Vec4 Truncate( Vec4::Arg v ) + { + return Vec4( + v.m_x > 0.0f ? SquishMath::floor( v.m_x ) : SquishMath::ceil( v.m_x ), + v.m_y > 0.0f ? SquishMath::floor( v.m_y ) : SquishMath::ceil( v.m_y ), + v.m_z > 0.0f ? SquishMath::floor( v.m_z ) : SquishMath::ceil( v.m_z ), + v.m_w > 0.0f ? SquishMath::floor( v.m_w ) : SquishMath::ceil( v.m_w ) + ); + } + + friend bool CompareAnyLessThan( Vec4::Arg left, Vec4::Arg right ) + { + return left.m_x < right.m_x + || left.m_y < right.m_y + || left.m_z < right.m_z + || left.m_w < right.m_w; + } + +private: + float m_x; + float m_y; + float m_z; + float m_w; +}; + +} // namespace squish + +#endif // ndef SQUISH_SIMD_FLOAT_H + diff --git a/Engine/lib/squish/simd_sse.h b/Engine/lib/squish/simd_sse.h new file mode 100644 index 000000000..e584f2a0e --- /dev/null +++ b/Engine/lib/squish/simd_sse.h @@ -0,0 +1,180 @@ +/* ----------------------------------------------------------------------------- + + Copyright (c) 2006 Simon Brown si@sjbrown.co.uk + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + -------------------------------------------------------------------------- */ + +#ifndef SQUISH_SIMD_SSE_H +#define SQUISH_SIMD_SSE_H + +#include +#if ( SQUISH_USE_SSE > 1 ) +#include +#endif + +#define SQUISH_SSE_SPLAT( a ) \ + ( ( a ) | ( ( a ) << 2 ) | ( ( a ) << 4 ) | ( ( a ) << 6 ) ) + +#define SQUISH_SSE_SHUF( x, y, z, w ) \ + ( ( x ) | ( ( y ) << 2 ) | ( ( z ) << 4 ) | ( ( w ) << 6 ) ) + +namespace squish { + +#define VEC4_CONST( X ) Vec4( X ) + +class Vec4 +{ +public: + typedef Vec4 const& Arg; + + Vec4() {} + + explicit Vec4( __m128 v ) : m_v( v ) {} + + Vec4( Vec4 const& arg ) : m_v( arg.m_v ) {} + + Vec4& operator=( Vec4 const& arg ) + { + m_v = arg.m_v; + return *this; + } + + explicit Vec4( float s ) : m_v( _mm_set1_ps( s ) ) {} + + Vec4( float x, float y, float z, float w ) : m_v( _mm_setr_ps( x, y, z, w ) ) {} + + Vec3 GetVec3() const + { +#ifdef __GNUC__ + __attribute__ ((__aligned__ (16))) float c[4]; +#else + __declspec(align(16)) float c[4]; +#endif + _mm_store_ps( c, m_v ); + return Vec3( c[0], c[1], c[2] ); + } + + Vec4 SplatX() const { return Vec4( _mm_shuffle_ps( m_v, m_v, SQUISH_SSE_SPLAT( 0 ) ) ); } + Vec4 SplatY() const { return Vec4( _mm_shuffle_ps( m_v, m_v, SQUISH_SSE_SPLAT( 1 ) ) ); } + Vec4 SplatZ() const { return Vec4( _mm_shuffle_ps( m_v, m_v, SQUISH_SSE_SPLAT( 2 ) ) ); } + Vec4 SplatW() const { return Vec4( _mm_shuffle_ps( m_v, m_v, SQUISH_SSE_SPLAT( 3 ) ) ); } + + Vec4& operator+=( Arg v ) + { + m_v = _mm_add_ps( m_v, v.m_v ); + return *this; + } + + Vec4& operator-=( Arg v ) + { + m_v = _mm_sub_ps( m_v, v.m_v ); + return *this; + } + + Vec4& operator*=( Arg v ) + { + m_v = _mm_mul_ps( m_v, v.m_v ); + return *this; + } + + friend Vec4 operator+( Vec4::Arg left, Vec4::Arg right ) + { + return Vec4( _mm_add_ps( left.m_v, right.m_v ) ); + } + + friend Vec4 operator-( Vec4::Arg left, Vec4::Arg right ) + { + return Vec4( _mm_sub_ps( left.m_v, right.m_v ) ); + } + + friend Vec4 operator*( Vec4::Arg left, Vec4::Arg right ) + { + return Vec4( _mm_mul_ps( left.m_v, right.m_v ) ); + } + + //! Returns a*b + c + friend Vec4 MultiplyAdd( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c ) + { + return Vec4( _mm_add_ps( _mm_mul_ps( a.m_v, b.m_v ), c.m_v ) ); + } + + //! Returns -( a*b - c ) + friend Vec4 NegativeMultiplySubtract( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c ) + { + return Vec4( _mm_sub_ps( c.m_v, _mm_mul_ps( a.m_v, b.m_v ) ) ); + } + + friend Vec4 Reciprocal( Vec4::Arg v ) + { + // get the reciprocal estimate + __m128 estimate = _mm_rcp_ps( v.m_v ); + + // one round of Newton-Rhaphson refinement + __m128 diff = _mm_sub_ps( _mm_set1_ps( 1.0f ), _mm_mul_ps( estimate, v.m_v ) ); + return Vec4( _mm_add_ps( _mm_mul_ps( diff, estimate ), estimate ) ); + } + + friend Vec4 Min( Vec4::Arg left, Vec4::Arg right ) + { + return Vec4( _mm_min_ps( left.m_v, right.m_v ) ); + } + + friend Vec4 Max( Vec4::Arg left, Vec4::Arg right ) + { + return Vec4( _mm_max_ps( left.m_v, right.m_v ) ); + } + + friend Vec4 Truncate( Vec4::Arg v ) + { +#if ( SQUISH_USE_SSE == 1 ) + // convert to ints + __m128 input = v.m_v; + __m64 lo = _mm_cvttps_pi32( input ); + __m64 hi = _mm_cvttps_pi32( _mm_movehl_ps( input, input ) ); + + // convert to floats + __m128 part = _mm_movelh_ps( input, _mm_cvtpi32_ps( input, hi ) ); + __m128 truncated = _mm_cvtpi32_ps( part, lo ); + + // clear out the MMX multimedia state to allow FP calls later + _mm_empty(); + return Vec4( truncated ); +#else + // use SSE2 instructions + return Vec4( _mm_cvtepi32_ps( _mm_cvttps_epi32( v.m_v ) ) ); +#endif + } + + friend bool CompareAnyLessThan( Vec4::Arg left, Vec4::Arg right ) + { + __m128 bits = _mm_cmplt_ps( left.m_v, right.m_v ); + int value = _mm_movemask_ps( bits ); + return value != 0; + } + +private: + __m128 m_v; +}; + +} // namespace squish + +#endif // ndef SQUISH_SIMD_SSE_H diff --git a/Engine/lib/squish/simd_ve.h b/Engine/lib/squish/simd_ve.h new file mode 100644 index 000000000..9a33955ff --- /dev/null +++ b/Engine/lib/squish/simd_ve.h @@ -0,0 +1,166 @@ +/* ----------------------------------------------------------------------------- + + Copyright (c) 2006 Simon Brown si@sjbrown.co.uk + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + -------------------------------------------------------------------------- */ + +#ifndef SQUISH_SIMD_VE_H +#define SQUISH_SIMD_VE_H + +#include +#undef bool + +namespace squish { + +#define VEC4_CONST( X ) Vec4( ( vector float )( X ) ) + +class Vec4 +{ +public: + typedef Vec4 Arg; + + Vec4() {} + + explicit Vec4( vector float v ) : m_v( v ) {} + + Vec4( Vec4 const& arg ) : m_v( arg.m_v ) {} + + Vec4& operator=( Vec4 const& arg ) + { + m_v = arg.m_v; + return *this; + } + + explicit Vec4( float s ) + { + union { vector float v; float c[4]; } u; + u.c[0] = s; + u.c[1] = s; + u.c[2] = s; + u.c[3] = s; + m_v = u.v; + } + + Vec4( float x, float y, float z, float w ) + { + union { vector float v; float c[4]; } u; + u.c[0] = x; + u.c[1] = y; + u.c[2] = z; + u.c[3] = w; + m_v = u.v; + } + + Vec3 GetVec3() const + { + union { vector float v; float c[4]; } u; + u.v = m_v; + return Vec3( u.c[0], u.c[1], u.c[2] ); + } + + Vec4 SplatX() const { return Vec4( vec_splat( m_v, 0 ) ); } + Vec4 SplatY() const { return Vec4( vec_splat( m_v, 1 ) ); } + Vec4 SplatZ() const { return Vec4( vec_splat( m_v, 2 ) ); } + Vec4 SplatW() const { return Vec4( vec_splat( m_v, 3 ) ); } + + Vec4& operator+=( Arg v ) + { + m_v = vec_add( m_v, v.m_v ); + return *this; + } + + Vec4& operator-=( Arg v ) + { + m_v = vec_sub( m_v, v.m_v ); + return *this; + } + + Vec4& operator*=( Arg v ) + { + m_v = vec_madd( m_v, v.m_v, ( vector float )( -0.0f ) ); + return *this; + } + + friend Vec4 operator+( Vec4::Arg left, Vec4::Arg right ) + { + return Vec4( vec_add( left.m_v, right.m_v ) ); + } + + friend Vec4 operator-( Vec4::Arg left, Vec4::Arg right ) + { + return Vec4( vec_sub( left.m_v, right.m_v ) ); + } + + friend Vec4 operator*( Vec4::Arg left, Vec4::Arg right ) + { + return Vec4( vec_madd( left.m_v, right.m_v, ( vector float )( -0.0f ) ) ); + } + + //! Returns a*b + c + friend Vec4 MultiplyAdd( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c ) + { + return Vec4( vec_madd( a.m_v, b.m_v, c.m_v ) ); + } + + //! Returns -( a*b - c ) + friend Vec4 NegativeMultiplySubtract( Vec4::Arg a, Vec4::Arg b, Vec4::Arg c ) + { + return Vec4( vec_nmsub( a.m_v, b.m_v, c.m_v ) ); + } + + friend Vec4 Reciprocal( Vec4::Arg v ) + { + // get the reciprocal estimate + vector float estimate = vec_re( v.m_v ); + + // one round of Newton-Rhaphson refinement + vector float diff = vec_nmsub( estimate, v.m_v, ( vector float )( 1.0f ) ); + return Vec4( vec_madd( diff, estimate, estimate ) ); + } + + friend Vec4 Min( Vec4::Arg left, Vec4::Arg right ) + { + return Vec4( vec_min( left.m_v, right.m_v ) ); + } + + friend Vec4 Max( Vec4::Arg left, Vec4::Arg right ) + { + return Vec4( vec_max( left.m_v, right.m_v ) ); + } + + friend Vec4 Truncate( Vec4::Arg v ) + { + return Vec4( vec_trunc( v.m_v ) ); + } + + friend bool CompareAnyLessThan( Vec4::Arg left, Vec4::Arg right ) + { + return vec_any_lt( left.m_v, right.m_v ) != 0; + } + +private: + vector float m_v; +}; + +} // namespace squish + +#endif // ndef SQUISH_SIMD_VE_H diff --git a/Engine/lib/squish/singlecolourfit.cpp b/Engine/lib/squish/singlecolourfit.cpp new file mode 100644 index 000000000..e8a011769 --- /dev/null +++ b/Engine/lib/squish/singlecolourfit.cpp @@ -0,0 +1,172 @@ +/* ----------------------------------------------------------------------------- + + Copyright (c) 2006 Simon Brown si@sjbrown.co.uk + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + -------------------------------------------------------------------------- */ + +#include "singlecolourfit.h" +#include "colourset.h" +#include "colourblock.h" + +namespace squish { + +struct SourceBlock +{ + u8 start; + u8 end; + u8 error; +}; + +struct SingleColourLookup +{ + SourceBlock sources[2]; +}; + +#include "singlecolourlookup.inl" + +static int FloatToInt( float a, int limit ) +{ + // use ANSI round-to-zero behaviour to get round-to-nearest + int i = ( int )( a + 0.5f ); + + // clamp to the limit + if( i < 0 ) + i = 0; + else if( i > limit ) + i = limit; + + // done + return i; +} + +SingleColourFit::SingleColourFit( ColourSet const* colours, int flags ) + : ColourFit( colours, flags ) +{ + // grab the single colour + Vec3 const* values = m_colours->GetPoints(); + m_colour[0] = ( u8 )FloatToInt( 255.0f*values->X(), 255 ); + m_colour[1] = ( u8 )FloatToInt( 255.0f*values->Y(), 255 ); + m_colour[2] = ( u8 )FloatToInt( 255.0f*values->Z(), 255 ); + + // initialise the best error + m_besterror = INT_MAX; +} + +void SingleColourFit::Compress3( void* block ) +{ + // build the table of lookups + SingleColourLookup const* const lookups[] = + { + lookup_5_3, + lookup_6_3, + lookup_5_3 + }; + + // find the best end-points and index + ComputeEndPoints( lookups ); + + // build the block if we win + if( m_error < m_besterror ) + { + // remap the indices + u8 indices[16]; + m_colours->RemapIndices( &m_index, indices ); + + // save the block + WriteColourBlock3( m_start, m_end, indices, block ); + + // save the error + m_besterror = m_error; + } +} + +void SingleColourFit::Compress4( void* block ) +{ + // build the table of lookups + SingleColourLookup const* const lookups[] = + { + lookup_5_4, + lookup_6_4, + lookup_5_4 + }; + + // find the best end-points and index + ComputeEndPoints( lookups ); + + // build the block if we win + if( m_error < m_besterror ) + { + // remap the indices + u8 indices[16]; + m_colours->RemapIndices( &m_index, indices ); + + // save the block + WriteColourBlock4( m_start, m_end, indices, block ); + + // save the error + m_besterror = m_error; + } +} + +void SingleColourFit::ComputeEndPoints( SingleColourLookup const* const* lookups ) +{ + // check each index combination (endpoint or intermediate) + m_error = INT_MAX; + for( int index = 0; index < 2; ++index ) + { + // check the error for this codebook index + SourceBlock const* sources[3]; + int error = 0; + for( int channel = 0; channel < 3; ++channel ) + { + // grab the lookup table and index for this channel + SingleColourLookup const* lookup = lookups[channel]; + int target = m_colour[channel]; + + // store a pointer to the source for this channel + sources[channel] = lookup[target].sources + index; + + // accumulate the error + int diff = sources[channel]->error; + error += diff*diff; + } + + // keep it if the error is lower + if( error < m_error ) + { + m_start = Vec3( + ( float )sources[0]->start/31.0f, + ( float )sources[1]->start/63.0f, + ( float )sources[2]->start/31.0f + ); + m_end = Vec3( + ( float )sources[0]->end/31.0f, + ( float )sources[1]->end/63.0f, + ( float )sources[2]->end/31.0f + ); + m_index = ( u8 )( 2*index ); + m_error = error; + } + } +} + +} // namespace squish diff --git a/Engine/lib/squish/singlecolourfit.h b/Engine/lib/squish/singlecolourfit.h new file mode 100644 index 000000000..0388fda02 --- /dev/null +++ b/Engine/lib/squish/singlecolourfit.h @@ -0,0 +1,58 @@ +/* ----------------------------------------------------------------------------- + + Copyright (c) 2006 Simon Brown si@sjbrown.co.uk + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + -------------------------------------------------------------------------- */ + +#ifndef SQUISH_SINGLECOLOURFIT_H +#define SQUISH_SINGLECOLOURFIT_H + +#include +#include "colourfit.h" + +namespace squish { + +class ColourSet; +struct SingleColourLookup; + +class SingleColourFit : public ColourFit +{ +public: + SingleColourFit( ColourSet const* colours, int flags ); + +private: + virtual void Compress3( void* block ); + virtual void Compress4( void* block ); + + void ComputeEndPoints( SingleColourLookup const* const* lookups ); + + u8 m_colour[3]; + Vec3 m_start; + Vec3 m_end; + u8 m_index; + int m_error; + int m_besterror; +}; + +} // namespace squish + +#endif // ndef SQUISH_SINGLECOLOURFIT_H diff --git a/Engine/lib/squish/singlecolourlookup.inl b/Engine/lib/squish/singlecolourlookup.inl new file mode 100644 index 000000000..f1c95a102 --- /dev/null +++ b/Engine/lib/squish/singlecolourlookup.inl @@ -0,0 +1,1040 @@ + +static SingleColourLookup const lookup_5_3[] = +{ + { { { 0, 0, 0 }, { 0, 0, 0 } } }, + { { { 0, 0, 1 }, { 0, 0, 1 } } }, + { { { 0, 0, 2 }, { 0, 0, 2 } } }, + { { { 0, 0, 3 }, { 0, 1, 1 } } }, + { { { 0, 0, 4 }, { 0, 1, 0 } } }, + { { { 1, 0, 3 }, { 0, 1, 1 } } }, + { { { 1, 0, 2 }, { 0, 1, 2 } } }, + { { { 1, 0, 1 }, { 0, 2, 1 } } }, + { { { 1, 0, 0 }, { 0, 2, 0 } } }, + { { { 1, 0, 1 }, { 0, 2, 1 } } }, + { { { 1, 0, 2 }, { 0, 2, 2 } } }, + { { { 1, 0, 3 }, { 0, 3, 1 } } }, + { { { 1, 0, 4 }, { 0, 3, 0 } } }, + { { { 2, 0, 3 }, { 0, 3, 1 } } }, + { { { 2, 0, 2 }, { 0, 3, 2 } } }, + { { { 2, 0, 1 }, { 0, 4, 1 } } }, + { { { 2, 0, 0 }, { 0, 4, 0 } } }, + { { { 2, 0, 1 }, { 0, 4, 1 } } }, + { { { 2, 0, 2 }, { 0, 4, 2 } } }, + { { { 2, 0, 3 }, { 0, 5, 1 } } }, + { { { 2, 0, 4 }, { 0, 5, 0 } } }, + { { { 3, 0, 3 }, { 0, 5, 1 } } }, + { { { 3, 0, 2 }, { 0, 5, 2 } } }, + { { { 3, 0, 1 }, { 0, 6, 1 } } }, + { { { 3, 0, 0 }, { 0, 6, 0 } } }, + { { { 3, 0, 1 }, { 0, 6, 1 } } }, + { { { 3, 0, 2 }, { 0, 6, 2 } } }, + { { { 3, 0, 3 }, { 0, 7, 1 } } }, + { { { 3, 0, 4 }, { 0, 7, 0 } } }, + { { { 4, 0, 4 }, { 0, 7, 1 } } }, + { { { 4, 0, 3 }, { 0, 7, 2 } } }, + { { { 4, 0, 2 }, { 1, 7, 1 } } }, + { { { 4, 0, 1 }, { 1, 7, 0 } } }, + { { { 4, 0, 0 }, { 0, 8, 0 } } }, + { { { 4, 0, 1 }, { 0, 8, 1 } } }, + { { { 4, 0, 2 }, { 2, 7, 1 } } }, + { { { 4, 0, 3 }, { 2, 7, 0 } } }, + { { { 4, 0, 4 }, { 0, 9, 0 } } }, + { { { 5, 0, 3 }, { 0, 9, 1 } } }, + { { { 5, 0, 2 }, { 3, 7, 1 } } }, + { { { 5, 0, 1 }, { 3, 7, 0 } } }, + { { { 5, 0, 0 }, { 0, 10, 0 } } }, + { { { 5, 0, 1 }, { 0, 10, 1 } } }, + { { { 5, 0, 2 }, { 0, 10, 2 } } }, + { { { 5, 0, 3 }, { 0, 11, 1 } } }, + { { { 5, 0, 4 }, { 0, 11, 0 } } }, + { { { 6, 0, 3 }, { 0, 11, 1 } } }, + { { { 6, 0, 2 }, { 0, 11, 2 } } }, + { { { 6, 0, 1 }, { 0, 12, 1 } } }, + { { { 6, 0, 0 }, { 0, 12, 0 } } }, + { { { 6, 0, 1 }, { 0, 12, 1 } } }, + { { { 6, 0, 2 }, { 0, 12, 2 } } }, + { { { 6, 0, 3 }, { 0, 13, 1 } } }, + { { { 6, 0, 4 }, { 0, 13, 0 } } }, + { { { 7, 0, 3 }, { 0, 13, 1 } } }, + { { { 7, 0, 2 }, { 0, 13, 2 } } }, + { { { 7, 0, 1 }, { 0, 14, 1 } } }, + { { { 7, 0, 0 }, { 0, 14, 0 } } }, + { { { 7, 0, 1 }, { 0, 14, 1 } } }, + { { { 7, 0, 2 }, { 0, 14, 2 } } }, + { { { 7, 0, 3 }, { 0, 15, 1 } } }, + { { { 7, 0, 4 }, { 0, 15, 0 } } }, + { { { 8, 0, 4 }, { 0, 15, 1 } } }, + { { { 8, 0, 3 }, { 0, 15, 2 } } }, + { { { 8, 0, 2 }, { 1, 15, 1 } } }, + { { { 8, 0, 1 }, { 1, 15, 0 } } }, + { { { 8, 0, 0 }, { 0, 16, 0 } } }, + { { { 8, 0, 1 }, { 0, 16, 1 } } }, + { { { 8, 0, 2 }, { 2, 15, 1 } } }, + { { { 8, 0, 3 }, { 2, 15, 0 } } }, + { { { 8, 0, 4 }, { 0, 17, 0 } } }, + { { { 9, 0, 3 }, { 0, 17, 1 } } }, + { { { 9, 0, 2 }, { 3, 15, 1 } } }, + { { { 9, 0, 1 }, { 3, 15, 0 } } }, + { { { 9, 0, 0 }, { 0, 18, 0 } } }, + { { { 9, 0, 1 }, { 0, 18, 1 } } }, + { { { 9, 0, 2 }, { 0, 18, 2 } } }, + { { { 9, 0, 3 }, { 0, 19, 1 } } }, + { { { 9, 0, 4 }, { 0, 19, 0 } } }, + { { { 10, 0, 3 }, { 0, 19, 1 } } }, + { { { 10, 0, 2 }, { 0, 19, 2 } } }, + { { { 10, 0, 1 }, { 0, 20, 1 } } }, + { { { 10, 0, 0 }, { 0, 20, 0 } } }, + { { { 10, 0, 1 }, { 0, 20, 1 } } }, + { { { 10, 0, 2 }, { 0, 20, 2 } } }, + { { { 10, 0, 3 }, { 0, 21, 1 } } }, + { { { 10, 0, 4 }, { 0, 21, 0 } } }, + { { { 11, 0, 3 }, { 0, 21, 1 } } }, + { { { 11, 0, 2 }, { 0, 21, 2 } } }, + { { { 11, 0, 1 }, { 0, 22, 1 } } }, + { { { 11, 0, 0 }, { 0, 22, 0 } } }, + { { { 11, 0, 1 }, { 0, 22, 1 } } }, + { { { 11, 0, 2 }, { 0, 22, 2 } } }, + { { { 11, 0, 3 }, { 0, 23, 1 } } }, + { { { 11, 0, 4 }, { 0, 23, 0 } } }, + { { { 12, 0, 4 }, { 0, 23, 1 } } }, + { { { 12, 0, 3 }, { 0, 23, 2 } } }, + { { { 12, 0, 2 }, { 1, 23, 1 } } }, + { { { 12, 0, 1 }, { 1, 23, 0 } } }, + { { { 12, 0, 0 }, { 0, 24, 0 } } }, + { { { 12, 0, 1 }, { 0, 24, 1 } } }, + { { { 12, 0, 2 }, { 2, 23, 1 } } }, + { { { 12, 0, 3 }, { 2, 23, 0 } } }, + { { { 12, 0, 4 }, { 0, 25, 0 } } }, + { { { 13, 0, 3 }, { 0, 25, 1 } } }, + { { { 13, 0, 2 }, { 3, 23, 1 } } }, + { { { 13, 0, 1 }, { 3, 23, 0 } } }, + { { { 13, 0, 0 }, { 0, 26, 0 } } }, + { { { 13, 0, 1 }, { 0, 26, 1 } } }, + { { { 13, 0, 2 }, { 0, 26, 2 } } }, + { { { 13, 0, 3 }, { 0, 27, 1 } } }, + { { { 13, 0, 4 }, { 0, 27, 0 } } }, + { { { 14, 0, 3 }, { 0, 27, 1 } } }, + { { { 14, 0, 2 }, { 0, 27, 2 } } }, + { { { 14, 0, 1 }, { 0, 28, 1 } } }, + { { { 14, 0, 0 }, { 0, 28, 0 } } }, + { { { 14, 0, 1 }, { 0, 28, 1 } } }, + { { { 14, 0, 2 }, { 0, 28, 2 } } }, + { { { 14, 0, 3 }, { 0, 29, 1 } } }, + { { { 14, 0, 4 }, { 0, 29, 0 } } }, + { { { 15, 0, 3 }, { 0, 29, 1 } } }, + { { { 15, 0, 2 }, { 0, 29, 2 } } }, + { { { 15, 0, 1 }, { 0, 30, 1 } } }, + { { { 15, 0, 0 }, { 0, 30, 0 } } }, + { { { 15, 0, 1 }, { 0, 30, 1 } } }, + { { { 15, 0, 2 }, { 0, 30, 2 } } }, + { { { 15, 0, 3 }, { 0, 31, 1 } } }, + { { { 15, 0, 4 }, { 0, 31, 0 } } }, + { { { 16, 0, 4 }, { 0, 31, 1 } } }, + { { { 16, 0, 3 }, { 0, 31, 2 } } }, + { { { 16, 0, 2 }, { 1, 31, 1 } } }, + { { { 16, 0, 1 }, { 1, 31, 0 } } }, + { { { 16, 0, 0 }, { 4, 28, 0 } } }, + { { { 16, 0, 1 }, { 4, 28, 1 } } }, + { { { 16, 0, 2 }, { 2, 31, 1 } } }, + { { { 16, 0, 3 }, { 2, 31, 0 } } }, + { { { 16, 0, 4 }, { 4, 29, 0 } } }, + { { { 17, 0, 3 }, { 4, 29, 1 } } }, + { { { 17, 0, 2 }, { 3, 31, 1 } } }, + { { { 17, 0, 1 }, { 3, 31, 0 } } }, + { { { 17, 0, 0 }, { 4, 30, 0 } } }, + { { { 17, 0, 1 }, { 4, 30, 1 } } }, + { { { 17, 0, 2 }, { 4, 30, 2 } } }, + { { { 17, 0, 3 }, { 4, 31, 1 } } }, + { { { 17, 0, 4 }, { 4, 31, 0 } } }, + { { { 18, 0, 3 }, { 4, 31, 1 } } }, + { { { 18, 0, 2 }, { 4, 31, 2 } } }, + { { { 18, 0, 1 }, { 5, 31, 1 } } }, + { { { 18, 0, 0 }, { 5, 31, 0 } } }, + { { { 18, 0, 1 }, { 5, 31, 1 } } }, + { { { 18, 0, 2 }, { 5, 31, 2 } } }, + { { { 18, 0, 3 }, { 6, 31, 1 } } }, + { { { 18, 0, 4 }, { 6, 31, 0 } } }, + { { { 19, 0, 3 }, { 6, 31, 1 } } }, + { { { 19, 0, 2 }, { 6, 31, 2 } } }, + { { { 19, 0, 1 }, { 7, 31, 1 } } }, + { { { 19, 0, 0 }, { 7, 31, 0 } } }, + { { { 19, 0, 1 }, { 7, 31, 1 } } }, + { { { 19, 0, 2 }, { 7, 31, 2 } } }, + { { { 19, 0, 3 }, { 8, 31, 1 } } }, + { { { 19, 0, 4 }, { 8, 31, 0 } } }, + { { { 20, 0, 4 }, { 8, 31, 1 } } }, + { { { 20, 0, 3 }, { 8, 31, 2 } } }, + { { { 20, 0, 2 }, { 9, 31, 1 } } }, + { { { 20, 0, 1 }, { 9, 31, 0 } } }, + { { { 20, 0, 0 }, { 12, 28, 0 } } }, + { { { 20, 0, 1 }, { 12, 28, 1 } } }, + { { { 20, 0, 2 }, { 10, 31, 1 } } }, + { { { 20, 0, 3 }, { 10, 31, 0 } } }, + { { { 20, 0, 4 }, { 12, 29, 0 } } }, + { { { 21, 0, 3 }, { 12, 29, 1 } } }, + { { { 21, 0, 2 }, { 11, 31, 1 } } }, + { { { 21, 0, 1 }, { 11, 31, 0 } } }, + { { { 21, 0, 0 }, { 12, 30, 0 } } }, + { { { 21, 0, 1 }, { 12, 30, 1 } } }, + { { { 21, 0, 2 }, { 12, 30, 2 } } }, + { { { 21, 0, 3 }, { 12, 31, 1 } } }, + { { { 21, 0, 4 }, { 12, 31, 0 } } }, + { { { 22, 0, 3 }, { 12, 31, 1 } } }, + { { { 22, 0, 2 }, { 12, 31, 2 } } }, + { { { 22, 0, 1 }, { 13, 31, 1 } } }, + { { { 22, 0, 0 }, { 13, 31, 0 } } }, + { { { 22, 0, 1 }, { 13, 31, 1 } } }, + { { { 22, 0, 2 }, { 13, 31, 2 } } }, + { { { 22, 0, 3 }, { 14, 31, 1 } } }, + { { { 22, 0, 4 }, { 14, 31, 0 } } }, + { { { 23, 0, 3 }, { 14, 31, 1 } } }, + { { { 23, 0, 2 }, { 14, 31, 2 } } }, + { { { 23, 0, 1 }, { 15, 31, 1 } } }, + { { { 23, 0, 0 }, { 15, 31, 0 } } }, + { { { 23, 0, 1 }, { 15, 31, 1 } } }, + { { { 23, 0, 2 }, { 15, 31, 2 } } }, + { { { 23, 0, 3 }, { 16, 31, 1 } } }, + { { { 23, 0, 4 }, { 16, 31, 0 } } }, + { { { 24, 0, 4 }, { 16, 31, 1 } } }, + { { { 24, 0, 3 }, { 16, 31, 2 } } }, + { { { 24, 0, 2 }, { 17, 31, 1 } } }, + { { { 24, 0, 1 }, { 17, 31, 0 } } }, + { { { 24, 0, 0 }, { 20, 28, 0 } } }, + { { { 24, 0, 1 }, { 20, 28, 1 } } }, + { { { 24, 0, 2 }, { 18, 31, 1 } } }, + { { { 24, 0, 3 }, { 18, 31, 0 } } }, + { { { 24, 0, 4 }, { 20, 29, 0 } } }, + { { { 25, 0, 3 }, { 20, 29, 1 } } }, + { { { 25, 0, 2 }, { 19, 31, 1 } } }, + { { { 25, 0, 1 }, { 19, 31, 0 } } }, + { { { 25, 0, 0 }, { 20, 30, 0 } } }, + { { { 25, 0, 1 }, { 20, 30, 1 } } }, + { { { 25, 0, 2 }, { 20, 30, 2 } } }, + { { { 25, 0, 3 }, { 20, 31, 1 } } }, + { { { 25, 0, 4 }, { 20, 31, 0 } } }, + { { { 26, 0, 3 }, { 20, 31, 1 } } }, + { { { 26, 0, 2 }, { 20, 31, 2 } } }, + { { { 26, 0, 1 }, { 21, 31, 1 } } }, + { { { 26, 0, 0 }, { 21, 31, 0 } } }, + { { { 26, 0, 1 }, { 21, 31, 1 } } }, + { { { 26, 0, 2 }, { 21, 31, 2 } } }, + { { { 26, 0, 3 }, { 22, 31, 1 } } }, + { { { 26, 0, 4 }, { 22, 31, 0 } } }, + { { { 27, 0, 3 }, { 22, 31, 1 } } }, + { { { 27, 0, 2 }, { 22, 31, 2 } } }, + { { { 27, 0, 1 }, { 23, 31, 1 } } }, + { { { 27, 0, 0 }, { 23, 31, 0 } } }, + { { { 27, 0, 1 }, { 23, 31, 1 } } }, + { { { 27, 0, 2 }, { 23, 31, 2 } } }, + { { { 27, 0, 3 }, { 24, 31, 1 } } }, + { { { 27, 0, 4 }, { 24, 31, 0 } } }, + { { { 28, 0, 4 }, { 24, 31, 1 } } }, + { { { 28, 0, 3 }, { 24, 31, 2 } } }, + { { { 28, 0, 2 }, { 25, 31, 1 } } }, + { { { 28, 0, 1 }, { 25, 31, 0 } } }, + { { { 28, 0, 0 }, { 28, 28, 0 } } }, + { { { 28, 0, 1 }, { 28, 28, 1 } } }, + { { { 28, 0, 2 }, { 26, 31, 1 } } }, + { { { 28, 0, 3 }, { 26, 31, 0 } } }, + { { { 28, 0, 4 }, { 28, 29, 0 } } }, + { { { 29, 0, 3 }, { 28, 29, 1 } } }, + { { { 29, 0, 2 }, { 27, 31, 1 } } }, + { { { 29, 0, 1 }, { 27, 31, 0 } } }, + { { { 29, 0, 0 }, { 28, 30, 0 } } }, + { { { 29, 0, 1 }, { 28, 30, 1 } } }, + { { { 29, 0, 2 }, { 28, 30, 2 } } }, + { { { 29, 0, 3 }, { 28, 31, 1 } } }, + { { { 29, 0, 4 }, { 28, 31, 0 } } }, + { { { 30, 0, 3 }, { 28, 31, 1 } } }, + { { { 30, 0, 2 }, { 28, 31, 2 } } }, + { { { 30, 0, 1 }, { 29, 31, 1 } } }, + { { { 30, 0, 0 }, { 29, 31, 0 } } }, + { { { 30, 0, 1 }, { 29, 31, 1 } } }, + { { { 30, 0, 2 }, { 29, 31, 2 } } }, + { { { 30, 0, 3 }, { 30, 31, 1 } } }, + { { { 30, 0, 4 }, { 30, 31, 0 } } }, + { { { 31, 0, 3 }, { 30, 31, 1 } } }, + { { { 31, 0, 2 }, { 30, 31, 2 } } }, + { { { 31, 0, 1 }, { 31, 31, 1 } } }, + { { { 31, 0, 0 }, { 31, 31, 0 } } } +}; + +static SingleColourLookup const lookup_6_3[] = +{ + { { { 0, 0, 0 }, { 0, 0, 0 } } }, + { { { 0, 0, 1 }, { 0, 1, 1 } } }, + { { { 0, 0, 2 }, { 0, 1, 0 } } }, + { { { 1, 0, 1 }, { 0, 2, 1 } } }, + { { { 1, 0, 0 }, { 0, 2, 0 } } }, + { { { 1, 0, 1 }, { 0, 3, 1 } } }, + { { { 1, 0, 2 }, { 0, 3, 0 } } }, + { { { 2, 0, 1 }, { 0, 4, 1 } } }, + { { { 2, 0, 0 }, { 0, 4, 0 } } }, + { { { 2, 0, 1 }, { 0, 5, 1 } } }, + { { { 2, 0, 2 }, { 0, 5, 0 } } }, + { { { 3, 0, 1 }, { 0, 6, 1 } } }, + { { { 3, 0, 0 }, { 0, 6, 0 } } }, + { { { 3, 0, 1 }, { 0, 7, 1 } } }, + { { { 3, 0, 2 }, { 0, 7, 0 } } }, + { { { 4, 0, 1 }, { 0, 8, 1 } } }, + { { { 4, 0, 0 }, { 0, 8, 0 } } }, + { { { 4, 0, 1 }, { 0, 9, 1 } } }, + { { { 4, 0, 2 }, { 0, 9, 0 } } }, + { { { 5, 0, 1 }, { 0, 10, 1 } } }, + { { { 5, 0, 0 }, { 0, 10, 0 } } }, + { { { 5, 0, 1 }, { 0, 11, 1 } } }, + { { { 5, 0, 2 }, { 0, 11, 0 } } }, + { { { 6, 0, 1 }, { 0, 12, 1 } } }, + { { { 6, 0, 0 }, { 0, 12, 0 } } }, + { { { 6, 0, 1 }, { 0, 13, 1 } } }, + { { { 6, 0, 2 }, { 0, 13, 0 } } }, + { { { 7, 0, 1 }, { 0, 14, 1 } } }, + { { { 7, 0, 0 }, { 0, 14, 0 } } }, + { { { 7, 0, 1 }, { 0, 15, 1 } } }, + { { { 7, 0, 2 }, { 0, 15, 0 } } }, + { { { 8, 0, 1 }, { 0, 16, 1 } } }, + { { { 8, 0, 0 }, { 0, 16, 0 } } }, + { { { 8, 0, 1 }, { 0, 17, 1 } } }, + { { { 8, 0, 2 }, { 0, 17, 0 } } }, + { { { 9, 0, 1 }, { 0, 18, 1 } } }, + { { { 9, 0, 0 }, { 0, 18, 0 } } }, + { { { 9, 0, 1 }, { 0, 19, 1 } } }, + { { { 9, 0, 2 }, { 0, 19, 0 } } }, + { { { 10, 0, 1 }, { 0, 20, 1 } } }, + { { { 10, 0, 0 }, { 0, 20, 0 } } }, + { { { 10, 0, 1 }, { 0, 21, 1 } } }, + { { { 10, 0, 2 }, { 0, 21, 0 } } }, + { { { 11, 0, 1 }, { 0, 22, 1 } } }, + { { { 11, 0, 0 }, { 0, 22, 0 } } }, + { { { 11, 0, 1 }, { 0, 23, 1 } } }, + { { { 11, 0, 2 }, { 0, 23, 0 } } }, + { { { 12, 0, 1 }, { 0, 24, 1 } } }, + { { { 12, 0, 0 }, { 0, 24, 0 } } }, + { { { 12, 0, 1 }, { 0, 25, 1 } } }, + { { { 12, 0, 2 }, { 0, 25, 0 } } }, + { { { 13, 0, 1 }, { 0, 26, 1 } } }, + { { { 13, 0, 0 }, { 0, 26, 0 } } }, + { { { 13, 0, 1 }, { 0, 27, 1 } } }, + { { { 13, 0, 2 }, { 0, 27, 0 } } }, + { { { 14, 0, 1 }, { 0, 28, 1 } } }, + { { { 14, 0, 0 }, { 0, 28, 0 } } }, + { { { 14, 0, 1 }, { 0, 29, 1 } } }, + { { { 14, 0, 2 }, { 0, 29, 0 } } }, + { { { 15, 0, 1 }, { 0, 30, 1 } } }, + { { { 15, 0, 0 }, { 0, 30, 0 } } }, + { { { 15, 0, 1 }, { 0, 31, 1 } } }, + { { { 15, 0, 2 }, { 0, 31, 0 } } }, + { { { 16, 0, 2 }, { 1, 31, 1 } } }, + { { { 16, 0, 1 }, { 1, 31, 0 } } }, + { { { 16, 0, 0 }, { 0, 32, 0 } } }, + { { { 16, 0, 1 }, { 2, 31, 0 } } }, + { { { 16, 0, 2 }, { 0, 33, 0 } } }, + { { { 17, 0, 1 }, { 3, 31, 0 } } }, + { { { 17, 0, 0 }, { 0, 34, 0 } } }, + { { { 17, 0, 1 }, { 4, 31, 0 } } }, + { { { 17, 0, 2 }, { 0, 35, 0 } } }, + { { { 18, 0, 1 }, { 5, 31, 0 } } }, + { { { 18, 0, 0 }, { 0, 36, 0 } } }, + { { { 18, 0, 1 }, { 6, 31, 0 } } }, + { { { 18, 0, 2 }, { 0, 37, 0 } } }, + { { { 19, 0, 1 }, { 7, 31, 0 } } }, + { { { 19, 0, 0 }, { 0, 38, 0 } } }, + { { { 19, 0, 1 }, { 8, 31, 0 } } }, + { { { 19, 0, 2 }, { 0, 39, 0 } } }, + { { { 20, 0, 1 }, { 9, 31, 0 } } }, + { { { 20, 0, 0 }, { 0, 40, 0 } } }, + { { { 20, 0, 1 }, { 10, 31, 0 } } }, + { { { 20, 0, 2 }, { 0, 41, 0 } } }, + { { { 21, 0, 1 }, { 11, 31, 0 } } }, + { { { 21, 0, 0 }, { 0, 42, 0 } } }, + { { { 21, 0, 1 }, { 12, 31, 0 } } }, + { { { 21, 0, 2 }, { 0, 43, 0 } } }, + { { { 22, 0, 1 }, { 13, 31, 0 } } }, + { { { 22, 0, 0 }, { 0, 44, 0 } } }, + { { { 22, 0, 1 }, { 14, 31, 0 } } }, + { { { 22, 0, 2 }, { 0, 45, 0 } } }, + { { { 23, 0, 1 }, { 15, 31, 0 } } }, + { { { 23, 0, 0 }, { 0, 46, 0 } } }, + { { { 23, 0, 1 }, { 0, 47, 1 } } }, + { { { 23, 0, 2 }, { 0, 47, 0 } } }, + { { { 24, 0, 1 }, { 0, 48, 1 } } }, + { { { 24, 0, 0 }, { 0, 48, 0 } } }, + { { { 24, 0, 1 }, { 0, 49, 1 } } }, + { { { 24, 0, 2 }, { 0, 49, 0 } } }, + { { { 25, 0, 1 }, { 0, 50, 1 } } }, + { { { 25, 0, 0 }, { 0, 50, 0 } } }, + { { { 25, 0, 1 }, { 0, 51, 1 } } }, + { { { 25, 0, 2 }, { 0, 51, 0 } } }, + { { { 26, 0, 1 }, { 0, 52, 1 } } }, + { { { 26, 0, 0 }, { 0, 52, 0 } } }, + { { { 26, 0, 1 }, { 0, 53, 1 } } }, + { { { 26, 0, 2 }, { 0, 53, 0 } } }, + { { { 27, 0, 1 }, { 0, 54, 1 } } }, + { { { 27, 0, 0 }, { 0, 54, 0 } } }, + { { { 27, 0, 1 }, { 0, 55, 1 } } }, + { { { 27, 0, 2 }, { 0, 55, 0 } } }, + { { { 28, 0, 1 }, { 0, 56, 1 } } }, + { { { 28, 0, 0 }, { 0, 56, 0 } } }, + { { { 28, 0, 1 }, { 0, 57, 1 } } }, + { { { 28, 0, 2 }, { 0, 57, 0 } } }, + { { { 29, 0, 1 }, { 0, 58, 1 } } }, + { { { 29, 0, 0 }, { 0, 58, 0 } } }, + { { { 29, 0, 1 }, { 0, 59, 1 } } }, + { { { 29, 0, 2 }, { 0, 59, 0 } } }, + { { { 30, 0, 1 }, { 0, 60, 1 } } }, + { { { 30, 0, 0 }, { 0, 60, 0 } } }, + { { { 30, 0, 1 }, { 0, 61, 1 } } }, + { { { 30, 0, 2 }, { 0, 61, 0 } } }, + { { { 31, 0, 1 }, { 0, 62, 1 } } }, + { { { 31, 0, 0 }, { 0, 62, 0 } } }, + { { { 31, 0, 1 }, { 0, 63, 1 } } }, + { { { 31, 0, 2 }, { 0, 63, 0 } } }, + { { { 32, 0, 2 }, { 1, 63, 1 } } }, + { { { 32, 0, 1 }, { 1, 63, 0 } } }, + { { { 32, 0, 0 }, { 16, 48, 0 } } }, + { { { 32, 0, 1 }, { 2, 63, 0 } } }, + { { { 32, 0, 2 }, { 16, 49, 0 } } }, + { { { 33, 0, 1 }, { 3, 63, 0 } } }, + { { { 33, 0, 0 }, { 16, 50, 0 } } }, + { { { 33, 0, 1 }, { 4, 63, 0 } } }, + { { { 33, 0, 2 }, { 16, 51, 0 } } }, + { { { 34, 0, 1 }, { 5, 63, 0 } } }, + { { { 34, 0, 0 }, { 16, 52, 0 } } }, + { { { 34, 0, 1 }, { 6, 63, 0 } } }, + { { { 34, 0, 2 }, { 16, 53, 0 } } }, + { { { 35, 0, 1 }, { 7, 63, 0 } } }, + { { { 35, 0, 0 }, { 16, 54, 0 } } }, + { { { 35, 0, 1 }, { 8, 63, 0 } } }, + { { { 35, 0, 2 }, { 16, 55, 0 } } }, + { { { 36, 0, 1 }, { 9, 63, 0 } } }, + { { { 36, 0, 0 }, { 16, 56, 0 } } }, + { { { 36, 0, 1 }, { 10, 63, 0 } } }, + { { { 36, 0, 2 }, { 16, 57, 0 } } }, + { { { 37, 0, 1 }, { 11, 63, 0 } } }, + { { { 37, 0, 0 }, { 16, 58, 0 } } }, + { { { 37, 0, 1 }, { 12, 63, 0 } } }, + { { { 37, 0, 2 }, { 16, 59, 0 } } }, + { { { 38, 0, 1 }, { 13, 63, 0 } } }, + { { { 38, 0, 0 }, { 16, 60, 0 } } }, + { { { 38, 0, 1 }, { 14, 63, 0 } } }, + { { { 38, 0, 2 }, { 16, 61, 0 } } }, + { { { 39, 0, 1 }, { 15, 63, 0 } } }, + { { { 39, 0, 0 }, { 16, 62, 0 } } }, + { { { 39, 0, 1 }, { 16, 63, 1 } } }, + { { { 39, 0, 2 }, { 16, 63, 0 } } }, + { { { 40, 0, 1 }, { 17, 63, 1 } } }, + { { { 40, 0, 0 }, { 17, 63, 0 } } }, + { { { 40, 0, 1 }, { 18, 63, 1 } } }, + { { { 40, 0, 2 }, { 18, 63, 0 } } }, + { { { 41, 0, 1 }, { 19, 63, 1 } } }, + { { { 41, 0, 0 }, { 19, 63, 0 } } }, + { { { 41, 0, 1 }, { 20, 63, 1 } } }, + { { { 41, 0, 2 }, { 20, 63, 0 } } }, + { { { 42, 0, 1 }, { 21, 63, 1 } } }, + { { { 42, 0, 0 }, { 21, 63, 0 } } }, + { { { 42, 0, 1 }, { 22, 63, 1 } } }, + { { { 42, 0, 2 }, { 22, 63, 0 } } }, + { { { 43, 0, 1 }, { 23, 63, 1 } } }, + { { { 43, 0, 0 }, { 23, 63, 0 } } }, + { { { 43, 0, 1 }, { 24, 63, 1 } } }, + { { { 43, 0, 2 }, { 24, 63, 0 } } }, + { { { 44, 0, 1 }, { 25, 63, 1 } } }, + { { { 44, 0, 0 }, { 25, 63, 0 } } }, + { { { 44, 0, 1 }, { 26, 63, 1 } } }, + { { { 44, 0, 2 }, { 26, 63, 0 } } }, + { { { 45, 0, 1 }, { 27, 63, 1 } } }, + { { { 45, 0, 0 }, { 27, 63, 0 } } }, + { { { 45, 0, 1 }, { 28, 63, 1 } } }, + { { { 45, 0, 2 }, { 28, 63, 0 } } }, + { { { 46, 0, 1 }, { 29, 63, 1 } } }, + { { { 46, 0, 0 }, { 29, 63, 0 } } }, + { { { 46, 0, 1 }, { 30, 63, 1 } } }, + { { { 46, 0, 2 }, { 30, 63, 0 } } }, + { { { 47, 0, 1 }, { 31, 63, 1 } } }, + { { { 47, 0, 0 }, { 31, 63, 0 } } }, + { { { 47, 0, 1 }, { 32, 63, 1 } } }, + { { { 47, 0, 2 }, { 32, 63, 0 } } }, + { { { 48, 0, 2 }, { 33, 63, 1 } } }, + { { { 48, 0, 1 }, { 33, 63, 0 } } }, + { { { 48, 0, 0 }, { 48, 48, 0 } } }, + { { { 48, 0, 1 }, { 34, 63, 0 } } }, + { { { 48, 0, 2 }, { 48, 49, 0 } } }, + { { { 49, 0, 1 }, { 35, 63, 0 } } }, + { { { 49, 0, 0 }, { 48, 50, 0 } } }, + { { { 49, 0, 1 }, { 36, 63, 0 } } }, + { { { 49, 0, 2 }, { 48, 51, 0 } } }, + { { { 50, 0, 1 }, { 37, 63, 0 } } }, + { { { 50, 0, 0 }, { 48, 52, 0 } } }, + { { { 50, 0, 1 }, { 38, 63, 0 } } }, + { { { 50, 0, 2 }, { 48, 53, 0 } } }, + { { { 51, 0, 1 }, { 39, 63, 0 } } }, + { { { 51, 0, 0 }, { 48, 54, 0 } } }, + { { { 51, 0, 1 }, { 40, 63, 0 } } }, + { { { 51, 0, 2 }, { 48, 55, 0 } } }, + { { { 52, 0, 1 }, { 41, 63, 0 } } }, + { { { 52, 0, 0 }, { 48, 56, 0 } } }, + { { { 52, 0, 1 }, { 42, 63, 0 } } }, + { { { 52, 0, 2 }, { 48, 57, 0 } } }, + { { { 53, 0, 1 }, { 43, 63, 0 } } }, + { { { 53, 0, 0 }, { 48, 58, 0 } } }, + { { { 53, 0, 1 }, { 44, 63, 0 } } }, + { { { 53, 0, 2 }, { 48, 59, 0 } } }, + { { { 54, 0, 1 }, { 45, 63, 0 } } }, + { { { 54, 0, 0 }, { 48, 60, 0 } } }, + { { { 54, 0, 1 }, { 46, 63, 0 } } }, + { { { 54, 0, 2 }, { 48, 61, 0 } } }, + { { { 55, 0, 1 }, { 47, 63, 0 } } }, + { { { 55, 0, 0 }, { 48, 62, 0 } } }, + { { { 55, 0, 1 }, { 48, 63, 1 } } }, + { { { 55, 0, 2 }, { 48, 63, 0 } } }, + { { { 56, 0, 1 }, { 49, 63, 1 } } }, + { { { 56, 0, 0 }, { 49, 63, 0 } } }, + { { { 56, 0, 1 }, { 50, 63, 1 } } }, + { { { 56, 0, 2 }, { 50, 63, 0 } } }, + { { { 57, 0, 1 }, { 51, 63, 1 } } }, + { { { 57, 0, 0 }, { 51, 63, 0 } } }, + { { { 57, 0, 1 }, { 52, 63, 1 } } }, + { { { 57, 0, 2 }, { 52, 63, 0 } } }, + { { { 58, 0, 1 }, { 53, 63, 1 } } }, + { { { 58, 0, 0 }, { 53, 63, 0 } } }, + { { { 58, 0, 1 }, { 54, 63, 1 } } }, + { { { 58, 0, 2 }, { 54, 63, 0 } } }, + { { { 59, 0, 1 }, { 55, 63, 1 } } }, + { { { 59, 0, 0 }, { 55, 63, 0 } } }, + { { { 59, 0, 1 }, { 56, 63, 1 } } }, + { { { 59, 0, 2 }, { 56, 63, 0 } } }, + { { { 60, 0, 1 }, { 57, 63, 1 } } }, + { { { 60, 0, 0 }, { 57, 63, 0 } } }, + { { { 60, 0, 1 }, { 58, 63, 1 } } }, + { { { 60, 0, 2 }, { 58, 63, 0 } } }, + { { { 61, 0, 1 }, { 59, 63, 1 } } }, + { { { 61, 0, 0 }, { 59, 63, 0 } } }, + { { { 61, 0, 1 }, { 60, 63, 1 } } }, + { { { 61, 0, 2 }, { 60, 63, 0 } } }, + { { { 62, 0, 1 }, { 61, 63, 1 } } }, + { { { 62, 0, 0 }, { 61, 63, 0 } } }, + { { { 62, 0, 1 }, { 62, 63, 1 } } }, + { { { 62, 0, 2 }, { 62, 63, 0 } } }, + { { { 63, 0, 1 }, { 63, 63, 1 } } }, + { { { 63, 0, 0 }, { 63, 63, 0 } } } +}; + +static SingleColourLookup const lookup_5_4[] = +{ + { { { 0, 0, 0 }, { 0, 0, 0 } } }, + { { { 0, 0, 1 }, { 0, 1, 1 } } }, + { { { 0, 0, 2 }, { 0, 1, 0 } } }, + { { { 0, 0, 3 }, { 0, 1, 1 } } }, + { { { 0, 0, 4 }, { 0, 2, 1 } } }, + { { { 1, 0, 3 }, { 0, 2, 0 } } }, + { { { 1, 0, 2 }, { 0, 2, 1 } } }, + { { { 1, 0, 1 }, { 0, 3, 1 } } }, + { { { 1, 0, 0 }, { 0, 3, 0 } } }, + { { { 1, 0, 1 }, { 1, 2, 1 } } }, + { { { 1, 0, 2 }, { 1, 2, 0 } } }, + { { { 1, 0, 3 }, { 0, 4, 0 } } }, + { { { 1, 0, 4 }, { 0, 5, 1 } } }, + { { { 2, 0, 3 }, { 0, 5, 0 } } }, + { { { 2, 0, 2 }, { 0, 5, 1 } } }, + { { { 2, 0, 1 }, { 0, 6, 1 } } }, + { { { 2, 0, 0 }, { 0, 6, 0 } } }, + { { { 2, 0, 1 }, { 2, 3, 1 } } }, + { { { 2, 0, 2 }, { 2, 3, 0 } } }, + { { { 2, 0, 3 }, { 0, 7, 0 } } }, + { { { 2, 0, 4 }, { 1, 6, 1 } } }, + { { { 3, 0, 3 }, { 1, 6, 0 } } }, + { { { 3, 0, 2 }, { 0, 8, 0 } } }, + { { { 3, 0, 1 }, { 0, 9, 1 } } }, + { { { 3, 0, 0 }, { 0, 9, 0 } } }, + { { { 3, 0, 1 }, { 0, 9, 1 } } }, + { { { 3, 0, 2 }, { 0, 10, 1 } } }, + { { { 3, 0, 3 }, { 0, 10, 0 } } }, + { { { 3, 0, 4 }, { 2, 7, 1 } } }, + { { { 4, 0, 4 }, { 2, 7, 0 } } }, + { { { 4, 0, 3 }, { 0, 11, 0 } } }, + { { { 4, 0, 2 }, { 1, 10, 1 } } }, + { { { 4, 0, 1 }, { 1, 10, 0 } } }, + { { { 4, 0, 0 }, { 0, 12, 0 } } }, + { { { 4, 0, 1 }, { 0, 13, 1 } } }, + { { { 4, 0, 2 }, { 0, 13, 0 } } }, + { { { 4, 0, 3 }, { 0, 13, 1 } } }, + { { { 4, 0, 4 }, { 0, 14, 1 } } }, + { { { 5, 0, 3 }, { 0, 14, 0 } } }, + { { { 5, 0, 2 }, { 2, 11, 1 } } }, + { { { 5, 0, 1 }, { 2, 11, 0 } } }, + { { { 5, 0, 0 }, { 0, 15, 0 } } }, + { { { 5, 0, 1 }, { 1, 14, 1 } } }, + { { { 5, 0, 2 }, { 1, 14, 0 } } }, + { { { 5, 0, 3 }, { 0, 16, 0 } } }, + { { { 5, 0, 4 }, { 0, 17, 1 } } }, + { { { 6, 0, 3 }, { 0, 17, 0 } } }, + { { { 6, 0, 2 }, { 0, 17, 1 } } }, + { { { 6, 0, 1 }, { 0, 18, 1 } } }, + { { { 6, 0, 0 }, { 0, 18, 0 } } }, + { { { 6, 0, 1 }, { 2, 15, 1 } } }, + { { { 6, 0, 2 }, { 2, 15, 0 } } }, + { { { 6, 0, 3 }, { 0, 19, 0 } } }, + { { { 6, 0, 4 }, { 1, 18, 1 } } }, + { { { 7, 0, 3 }, { 1, 18, 0 } } }, + { { { 7, 0, 2 }, { 0, 20, 0 } } }, + { { { 7, 0, 1 }, { 0, 21, 1 } } }, + { { { 7, 0, 0 }, { 0, 21, 0 } } }, + { { { 7, 0, 1 }, { 0, 21, 1 } } }, + { { { 7, 0, 2 }, { 0, 22, 1 } } }, + { { { 7, 0, 3 }, { 0, 22, 0 } } }, + { { { 7, 0, 4 }, { 2, 19, 1 } } }, + { { { 8, 0, 4 }, { 2, 19, 0 } } }, + { { { 8, 0, 3 }, { 0, 23, 0 } } }, + { { { 8, 0, 2 }, { 1, 22, 1 } } }, + { { { 8, 0, 1 }, { 1, 22, 0 } } }, + { { { 8, 0, 0 }, { 0, 24, 0 } } }, + { { { 8, 0, 1 }, { 0, 25, 1 } } }, + { { { 8, 0, 2 }, { 0, 25, 0 } } }, + { { { 8, 0, 3 }, { 0, 25, 1 } } }, + { { { 8, 0, 4 }, { 0, 26, 1 } } }, + { { { 9, 0, 3 }, { 0, 26, 0 } } }, + { { { 9, 0, 2 }, { 2, 23, 1 } } }, + { { { 9, 0, 1 }, { 2, 23, 0 } } }, + { { { 9, 0, 0 }, { 0, 27, 0 } } }, + { { { 9, 0, 1 }, { 1, 26, 1 } } }, + { { { 9, 0, 2 }, { 1, 26, 0 } } }, + { { { 9, 0, 3 }, { 0, 28, 0 } } }, + { { { 9, 0, 4 }, { 0, 29, 1 } } }, + { { { 10, 0, 3 }, { 0, 29, 0 } } }, + { { { 10, 0, 2 }, { 0, 29, 1 } } }, + { { { 10, 0, 1 }, { 0, 30, 1 } } }, + { { { 10, 0, 0 }, { 0, 30, 0 } } }, + { { { 10, 0, 1 }, { 2, 27, 1 } } }, + { { { 10, 0, 2 }, { 2, 27, 0 } } }, + { { { 10, 0, 3 }, { 0, 31, 0 } } }, + { { { 10, 0, 4 }, { 1, 30, 1 } } }, + { { { 11, 0, 3 }, { 1, 30, 0 } } }, + { { { 11, 0, 2 }, { 4, 24, 0 } } }, + { { { 11, 0, 1 }, { 1, 31, 1 } } }, + { { { 11, 0, 0 }, { 1, 31, 0 } } }, + { { { 11, 0, 1 }, { 1, 31, 1 } } }, + { { { 11, 0, 2 }, { 2, 30, 1 } } }, + { { { 11, 0, 3 }, { 2, 30, 0 } } }, + { { { 11, 0, 4 }, { 2, 31, 1 } } }, + { { { 12, 0, 4 }, { 2, 31, 0 } } }, + { { { 12, 0, 3 }, { 4, 27, 0 } } }, + { { { 12, 0, 2 }, { 3, 30, 1 } } }, + { { { 12, 0, 1 }, { 3, 30, 0 } } }, + { { { 12, 0, 0 }, { 4, 28, 0 } } }, + { { { 12, 0, 1 }, { 3, 31, 1 } } }, + { { { 12, 0, 2 }, { 3, 31, 0 } } }, + { { { 12, 0, 3 }, { 3, 31, 1 } } }, + { { { 12, 0, 4 }, { 4, 30, 1 } } }, + { { { 13, 0, 3 }, { 4, 30, 0 } } }, + { { { 13, 0, 2 }, { 6, 27, 1 } } }, + { { { 13, 0, 1 }, { 6, 27, 0 } } }, + { { { 13, 0, 0 }, { 4, 31, 0 } } }, + { { { 13, 0, 1 }, { 5, 30, 1 } } }, + { { { 13, 0, 2 }, { 5, 30, 0 } } }, + { { { 13, 0, 3 }, { 8, 24, 0 } } }, + { { { 13, 0, 4 }, { 5, 31, 1 } } }, + { { { 14, 0, 3 }, { 5, 31, 0 } } }, + { { { 14, 0, 2 }, { 5, 31, 1 } } }, + { { { 14, 0, 1 }, { 6, 30, 1 } } }, + { { { 14, 0, 0 }, { 6, 30, 0 } } }, + { { { 14, 0, 1 }, { 6, 31, 1 } } }, + { { { 14, 0, 2 }, { 6, 31, 0 } } }, + { { { 14, 0, 3 }, { 8, 27, 0 } } }, + { { { 14, 0, 4 }, { 7, 30, 1 } } }, + { { { 15, 0, 3 }, { 7, 30, 0 } } }, + { { { 15, 0, 2 }, { 8, 28, 0 } } }, + { { { 15, 0, 1 }, { 7, 31, 1 } } }, + { { { 15, 0, 0 }, { 7, 31, 0 } } }, + { { { 15, 0, 1 }, { 7, 31, 1 } } }, + { { { 15, 0, 2 }, { 8, 30, 1 } } }, + { { { 15, 0, 3 }, { 8, 30, 0 } } }, + { { { 15, 0, 4 }, { 10, 27, 1 } } }, + { { { 16, 0, 4 }, { 10, 27, 0 } } }, + { { { 16, 0, 3 }, { 8, 31, 0 } } }, + { { { 16, 0, 2 }, { 9, 30, 1 } } }, + { { { 16, 0, 1 }, { 9, 30, 0 } } }, + { { { 16, 0, 0 }, { 12, 24, 0 } } }, + { { { 16, 0, 1 }, { 9, 31, 1 } } }, + { { { 16, 0, 2 }, { 9, 31, 0 } } }, + { { { 16, 0, 3 }, { 9, 31, 1 } } }, + { { { 16, 0, 4 }, { 10, 30, 1 } } }, + { { { 17, 0, 3 }, { 10, 30, 0 } } }, + { { { 17, 0, 2 }, { 10, 31, 1 } } }, + { { { 17, 0, 1 }, { 10, 31, 0 } } }, + { { { 17, 0, 0 }, { 12, 27, 0 } } }, + { { { 17, 0, 1 }, { 11, 30, 1 } } }, + { { { 17, 0, 2 }, { 11, 30, 0 } } }, + { { { 17, 0, 3 }, { 12, 28, 0 } } }, + { { { 17, 0, 4 }, { 11, 31, 1 } } }, + { { { 18, 0, 3 }, { 11, 31, 0 } } }, + { { { 18, 0, 2 }, { 11, 31, 1 } } }, + { { { 18, 0, 1 }, { 12, 30, 1 } } }, + { { { 18, 0, 0 }, { 12, 30, 0 } } }, + { { { 18, 0, 1 }, { 14, 27, 1 } } }, + { { { 18, 0, 2 }, { 14, 27, 0 } } }, + { { { 18, 0, 3 }, { 12, 31, 0 } } }, + { { { 18, 0, 4 }, { 13, 30, 1 } } }, + { { { 19, 0, 3 }, { 13, 30, 0 } } }, + { { { 19, 0, 2 }, { 16, 24, 0 } } }, + { { { 19, 0, 1 }, { 13, 31, 1 } } }, + { { { 19, 0, 0 }, { 13, 31, 0 } } }, + { { { 19, 0, 1 }, { 13, 31, 1 } } }, + { { { 19, 0, 2 }, { 14, 30, 1 } } }, + { { { 19, 0, 3 }, { 14, 30, 0 } } }, + { { { 19, 0, 4 }, { 14, 31, 1 } } }, + { { { 20, 0, 4 }, { 14, 31, 0 } } }, + { { { 20, 0, 3 }, { 16, 27, 0 } } }, + { { { 20, 0, 2 }, { 15, 30, 1 } } }, + { { { 20, 0, 1 }, { 15, 30, 0 } } }, + { { { 20, 0, 0 }, { 16, 28, 0 } } }, + { { { 20, 0, 1 }, { 15, 31, 1 } } }, + { { { 20, 0, 2 }, { 15, 31, 0 } } }, + { { { 20, 0, 3 }, { 15, 31, 1 } } }, + { { { 20, 0, 4 }, { 16, 30, 1 } } }, + { { { 21, 0, 3 }, { 16, 30, 0 } } }, + { { { 21, 0, 2 }, { 18, 27, 1 } } }, + { { { 21, 0, 1 }, { 18, 27, 0 } } }, + { { { 21, 0, 0 }, { 16, 31, 0 } } }, + { { { 21, 0, 1 }, { 17, 30, 1 } } }, + { { { 21, 0, 2 }, { 17, 30, 0 } } }, + { { { 21, 0, 3 }, { 20, 24, 0 } } }, + { { { 21, 0, 4 }, { 17, 31, 1 } } }, + { { { 22, 0, 3 }, { 17, 31, 0 } } }, + { { { 22, 0, 2 }, { 17, 31, 1 } } }, + { { { 22, 0, 1 }, { 18, 30, 1 } } }, + { { { 22, 0, 0 }, { 18, 30, 0 } } }, + { { { 22, 0, 1 }, { 18, 31, 1 } } }, + { { { 22, 0, 2 }, { 18, 31, 0 } } }, + { { { 22, 0, 3 }, { 20, 27, 0 } } }, + { { { 22, 0, 4 }, { 19, 30, 1 } } }, + { { { 23, 0, 3 }, { 19, 30, 0 } } }, + { { { 23, 0, 2 }, { 20, 28, 0 } } }, + { { { 23, 0, 1 }, { 19, 31, 1 } } }, + { { { 23, 0, 0 }, { 19, 31, 0 } } }, + { { { 23, 0, 1 }, { 19, 31, 1 } } }, + { { { 23, 0, 2 }, { 20, 30, 1 } } }, + { { { 23, 0, 3 }, { 20, 30, 0 } } }, + { { { 23, 0, 4 }, { 22, 27, 1 } } }, + { { { 24, 0, 4 }, { 22, 27, 0 } } }, + { { { 24, 0, 3 }, { 20, 31, 0 } } }, + { { { 24, 0, 2 }, { 21, 30, 1 } } }, + { { { 24, 0, 1 }, { 21, 30, 0 } } }, + { { { 24, 0, 0 }, { 24, 24, 0 } } }, + { { { 24, 0, 1 }, { 21, 31, 1 } } }, + { { { 24, 0, 2 }, { 21, 31, 0 } } }, + { { { 24, 0, 3 }, { 21, 31, 1 } } }, + { { { 24, 0, 4 }, { 22, 30, 1 } } }, + { { { 25, 0, 3 }, { 22, 30, 0 } } }, + { { { 25, 0, 2 }, { 22, 31, 1 } } }, + { { { 25, 0, 1 }, { 22, 31, 0 } } }, + { { { 25, 0, 0 }, { 24, 27, 0 } } }, + { { { 25, 0, 1 }, { 23, 30, 1 } } }, + { { { 25, 0, 2 }, { 23, 30, 0 } } }, + { { { 25, 0, 3 }, { 24, 28, 0 } } }, + { { { 25, 0, 4 }, { 23, 31, 1 } } }, + { { { 26, 0, 3 }, { 23, 31, 0 } } }, + { { { 26, 0, 2 }, { 23, 31, 1 } } }, + { { { 26, 0, 1 }, { 24, 30, 1 } } }, + { { { 26, 0, 0 }, { 24, 30, 0 } } }, + { { { 26, 0, 1 }, { 26, 27, 1 } } }, + { { { 26, 0, 2 }, { 26, 27, 0 } } }, + { { { 26, 0, 3 }, { 24, 31, 0 } } }, + { { { 26, 0, 4 }, { 25, 30, 1 } } }, + { { { 27, 0, 3 }, { 25, 30, 0 } } }, + { { { 27, 0, 2 }, { 28, 24, 0 } } }, + { { { 27, 0, 1 }, { 25, 31, 1 } } }, + { { { 27, 0, 0 }, { 25, 31, 0 } } }, + { { { 27, 0, 1 }, { 25, 31, 1 } } }, + { { { 27, 0, 2 }, { 26, 30, 1 } } }, + { { { 27, 0, 3 }, { 26, 30, 0 } } }, + { { { 27, 0, 4 }, { 26, 31, 1 } } }, + { { { 28, 0, 4 }, { 26, 31, 0 } } }, + { { { 28, 0, 3 }, { 28, 27, 0 } } }, + { { { 28, 0, 2 }, { 27, 30, 1 } } }, + { { { 28, 0, 1 }, { 27, 30, 0 } } }, + { { { 28, 0, 0 }, { 28, 28, 0 } } }, + { { { 28, 0, 1 }, { 27, 31, 1 } } }, + { { { 28, 0, 2 }, { 27, 31, 0 } } }, + { { { 28, 0, 3 }, { 27, 31, 1 } } }, + { { { 28, 0, 4 }, { 28, 30, 1 } } }, + { { { 29, 0, 3 }, { 28, 30, 0 } } }, + { { { 29, 0, 2 }, { 30, 27, 1 } } }, + { { { 29, 0, 1 }, { 30, 27, 0 } } }, + { { { 29, 0, 0 }, { 28, 31, 0 } } }, + { { { 29, 0, 1 }, { 29, 30, 1 } } }, + { { { 29, 0, 2 }, { 29, 30, 0 } } }, + { { { 29, 0, 3 }, { 29, 30, 1 } } }, + { { { 29, 0, 4 }, { 29, 31, 1 } } }, + { { { 30, 0, 3 }, { 29, 31, 0 } } }, + { { { 30, 0, 2 }, { 29, 31, 1 } } }, + { { { 30, 0, 1 }, { 30, 30, 1 } } }, + { { { 30, 0, 0 }, { 30, 30, 0 } } }, + { { { 30, 0, 1 }, { 30, 31, 1 } } }, + { { { 30, 0, 2 }, { 30, 31, 0 } } }, + { { { 30, 0, 3 }, { 30, 31, 1 } } }, + { { { 30, 0, 4 }, { 31, 30, 1 } } }, + { { { 31, 0, 3 }, { 31, 30, 0 } } }, + { { { 31, 0, 2 }, { 31, 30, 1 } } }, + { { { 31, 0, 1 }, { 31, 31, 1 } } }, + { { { 31, 0, 0 }, { 31, 31, 0 } } } +}; + +static SingleColourLookup const lookup_6_4[] = +{ + { { { 0, 0, 0 }, { 0, 0, 0 } } }, + { { { 0, 0, 1 }, { 0, 1, 0 } } }, + { { { 0, 0, 2 }, { 0, 2, 0 } } }, + { { { 1, 0, 1 }, { 0, 3, 1 } } }, + { { { 1, 0, 0 }, { 0, 3, 0 } } }, + { { { 1, 0, 1 }, { 0, 4, 0 } } }, + { { { 1, 0, 2 }, { 0, 5, 0 } } }, + { { { 2, 0, 1 }, { 0, 6, 1 } } }, + { { { 2, 0, 0 }, { 0, 6, 0 } } }, + { { { 2, 0, 1 }, { 0, 7, 0 } } }, + { { { 2, 0, 2 }, { 0, 8, 0 } } }, + { { { 3, 0, 1 }, { 0, 9, 1 } } }, + { { { 3, 0, 0 }, { 0, 9, 0 } } }, + { { { 3, 0, 1 }, { 0, 10, 0 } } }, + { { { 3, 0, 2 }, { 0, 11, 0 } } }, + { { { 4, 0, 1 }, { 0, 12, 1 } } }, + { { { 4, 0, 0 }, { 0, 12, 0 } } }, + { { { 4, 0, 1 }, { 0, 13, 0 } } }, + { { { 4, 0, 2 }, { 0, 14, 0 } } }, + { { { 5, 0, 1 }, { 0, 15, 1 } } }, + { { { 5, 0, 0 }, { 0, 15, 0 } } }, + { { { 5, 0, 1 }, { 0, 16, 0 } } }, + { { { 5, 0, 2 }, { 1, 15, 0 } } }, + { { { 6, 0, 1 }, { 0, 17, 0 } } }, + { { { 6, 0, 0 }, { 0, 18, 0 } } }, + { { { 6, 0, 1 }, { 0, 19, 0 } } }, + { { { 6, 0, 2 }, { 3, 14, 0 } } }, + { { { 7, 0, 1 }, { 0, 20, 0 } } }, + { { { 7, 0, 0 }, { 0, 21, 0 } } }, + { { { 7, 0, 1 }, { 0, 22, 0 } } }, + { { { 7, 0, 2 }, { 4, 15, 0 } } }, + { { { 8, 0, 1 }, { 0, 23, 0 } } }, + { { { 8, 0, 0 }, { 0, 24, 0 } } }, + { { { 8, 0, 1 }, { 0, 25, 0 } } }, + { { { 8, 0, 2 }, { 6, 14, 0 } } }, + { { { 9, 0, 1 }, { 0, 26, 0 } } }, + { { { 9, 0, 0 }, { 0, 27, 0 } } }, + { { { 9, 0, 1 }, { 0, 28, 0 } } }, + { { { 9, 0, 2 }, { 7, 15, 0 } } }, + { { { 10, 0, 1 }, { 0, 29, 0 } } }, + { { { 10, 0, 0 }, { 0, 30, 0 } } }, + { { { 10, 0, 1 }, { 0, 31, 0 } } }, + { { { 10, 0, 2 }, { 9, 14, 0 } } }, + { { { 11, 0, 1 }, { 0, 32, 0 } } }, + { { { 11, 0, 0 }, { 0, 33, 0 } } }, + { { { 11, 0, 1 }, { 2, 30, 0 } } }, + { { { 11, 0, 2 }, { 0, 34, 0 } } }, + { { { 12, 0, 1 }, { 0, 35, 0 } } }, + { { { 12, 0, 0 }, { 0, 36, 0 } } }, + { { { 12, 0, 1 }, { 3, 31, 0 } } }, + { { { 12, 0, 2 }, { 0, 37, 0 } } }, + { { { 13, 0, 1 }, { 0, 38, 0 } } }, + { { { 13, 0, 0 }, { 0, 39, 0 } } }, + { { { 13, 0, 1 }, { 5, 30, 0 } } }, + { { { 13, 0, 2 }, { 0, 40, 0 } } }, + { { { 14, 0, 1 }, { 0, 41, 0 } } }, + { { { 14, 0, 0 }, { 0, 42, 0 } } }, + { { { 14, 0, 1 }, { 6, 31, 0 } } }, + { { { 14, 0, 2 }, { 0, 43, 0 } } }, + { { { 15, 0, 1 }, { 0, 44, 0 } } }, + { { { 15, 0, 0 }, { 0, 45, 0 } } }, + { { { 15, 0, 1 }, { 8, 30, 0 } } }, + { { { 15, 0, 2 }, { 0, 46, 0 } } }, + { { { 16, 0, 2 }, { 0, 47, 0 } } }, + { { { 16, 0, 1 }, { 1, 46, 0 } } }, + { { { 16, 0, 0 }, { 0, 48, 0 } } }, + { { { 16, 0, 1 }, { 0, 49, 0 } } }, + { { { 16, 0, 2 }, { 0, 50, 0 } } }, + { { { 17, 0, 1 }, { 2, 47, 0 } } }, + { { { 17, 0, 0 }, { 0, 51, 0 } } }, + { { { 17, 0, 1 }, { 0, 52, 0 } } }, + { { { 17, 0, 2 }, { 0, 53, 0 } } }, + { { { 18, 0, 1 }, { 4, 46, 0 } } }, + { { { 18, 0, 0 }, { 0, 54, 0 } } }, + { { { 18, 0, 1 }, { 0, 55, 0 } } }, + { { { 18, 0, 2 }, { 0, 56, 0 } } }, + { { { 19, 0, 1 }, { 5, 47, 0 } } }, + { { { 19, 0, 0 }, { 0, 57, 0 } } }, + { { { 19, 0, 1 }, { 0, 58, 0 } } }, + { { { 19, 0, 2 }, { 0, 59, 0 } } }, + { { { 20, 0, 1 }, { 7, 46, 0 } } }, + { { { 20, 0, 0 }, { 0, 60, 0 } } }, + { { { 20, 0, 1 }, { 0, 61, 0 } } }, + { { { 20, 0, 2 }, { 0, 62, 0 } } }, + { { { 21, 0, 1 }, { 8, 47, 0 } } }, + { { { 21, 0, 0 }, { 0, 63, 0 } } }, + { { { 21, 0, 1 }, { 1, 62, 0 } } }, + { { { 21, 0, 2 }, { 1, 63, 0 } } }, + { { { 22, 0, 1 }, { 10, 46, 0 } } }, + { { { 22, 0, 0 }, { 2, 62, 0 } } }, + { { { 22, 0, 1 }, { 2, 63, 0 } } }, + { { { 22, 0, 2 }, { 3, 62, 0 } } }, + { { { 23, 0, 1 }, { 11, 47, 0 } } }, + { { { 23, 0, 0 }, { 3, 63, 0 } } }, + { { { 23, 0, 1 }, { 4, 62, 0 } } }, + { { { 23, 0, 2 }, { 4, 63, 0 } } }, + { { { 24, 0, 1 }, { 13, 46, 0 } } }, + { { { 24, 0, 0 }, { 5, 62, 0 } } }, + { { { 24, 0, 1 }, { 5, 63, 0 } } }, + { { { 24, 0, 2 }, { 6, 62, 0 } } }, + { { { 25, 0, 1 }, { 14, 47, 0 } } }, + { { { 25, 0, 0 }, { 6, 63, 0 } } }, + { { { 25, 0, 1 }, { 7, 62, 0 } } }, + { { { 25, 0, 2 }, { 7, 63, 0 } } }, + { { { 26, 0, 1 }, { 16, 45, 0 } } }, + { { { 26, 0, 0 }, { 8, 62, 0 } } }, + { { { 26, 0, 1 }, { 8, 63, 0 } } }, + { { { 26, 0, 2 }, { 9, 62, 0 } } }, + { { { 27, 0, 1 }, { 16, 48, 0 } } }, + { { { 27, 0, 0 }, { 9, 63, 0 } } }, + { { { 27, 0, 1 }, { 10, 62, 0 } } }, + { { { 27, 0, 2 }, { 10, 63, 0 } } }, + { { { 28, 0, 1 }, { 16, 51, 0 } } }, + { { { 28, 0, 0 }, { 11, 62, 0 } } }, + { { { 28, 0, 1 }, { 11, 63, 0 } } }, + { { { 28, 0, 2 }, { 12, 62, 0 } } }, + { { { 29, 0, 1 }, { 16, 54, 0 } } }, + { { { 29, 0, 0 }, { 12, 63, 0 } } }, + { { { 29, 0, 1 }, { 13, 62, 0 } } }, + { { { 29, 0, 2 }, { 13, 63, 0 } } }, + { { { 30, 0, 1 }, { 16, 57, 0 } } }, + { { { 30, 0, 0 }, { 14, 62, 0 } } }, + { { { 30, 0, 1 }, { 14, 63, 0 } } }, + { { { 30, 0, 2 }, { 15, 62, 0 } } }, + { { { 31, 0, 1 }, { 16, 60, 0 } } }, + { { { 31, 0, 0 }, { 15, 63, 0 } } }, + { { { 31, 0, 1 }, { 24, 46, 0 } } }, + { { { 31, 0, 2 }, { 16, 62, 0 } } }, + { { { 32, 0, 2 }, { 16, 63, 0 } } }, + { { { 32, 0, 1 }, { 17, 62, 0 } } }, + { { { 32, 0, 0 }, { 25, 47, 0 } } }, + { { { 32, 0, 1 }, { 17, 63, 0 } } }, + { { { 32, 0, 2 }, { 18, 62, 0 } } }, + { { { 33, 0, 1 }, { 18, 63, 0 } } }, + { { { 33, 0, 0 }, { 27, 46, 0 } } }, + { { { 33, 0, 1 }, { 19, 62, 0 } } }, + { { { 33, 0, 2 }, { 19, 63, 0 } } }, + { { { 34, 0, 1 }, { 20, 62, 0 } } }, + { { { 34, 0, 0 }, { 28, 47, 0 } } }, + { { { 34, 0, 1 }, { 20, 63, 0 } } }, + { { { 34, 0, 2 }, { 21, 62, 0 } } }, + { { { 35, 0, 1 }, { 21, 63, 0 } } }, + { { { 35, 0, 0 }, { 30, 46, 0 } } }, + { { { 35, 0, 1 }, { 22, 62, 0 } } }, + { { { 35, 0, 2 }, { 22, 63, 0 } } }, + { { { 36, 0, 1 }, { 23, 62, 0 } } }, + { { { 36, 0, 0 }, { 31, 47, 0 } } }, + { { { 36, 0, 1 }, { 23, 63, 0 } } }, + { { { 36, 0, 2 }, { 24, 62, 0 } } }, + { { { 37, 0, 1 }, { 24, 63, 0 } } }, + { { { 37, 0, 0 }, { 32, 47, 0 } } }, + { { { 37, 0, 1 }, { 25, 62, 0 } } }, + { { { 37, 0, 2 }, { 25, 63, 0 } } }, + { { { 38, 0, 1 }, { 26, 62, 0 } } }, + { { { 38, 0, 0 }, { 32, 50, 0 } } }, + { { { 38, 0, 1 }, { 26, 63, 0 } } }, + { { { 38, 0, 2 }, { 27, 62, 0 } } }, + { { { 39, 0, 1 }, { 27, 63, 0 } } }, + { { { 39, 0, 0 }, { 32, 53, 0 } } }, + { { { 39, 0, 1 }, { 28, 62, 0 } } }, + { { { 39, 0, 2 }, { 28, 63, 0 } } }, + { { { 40, 0, 1 }, { 29, 62, 0 } } }, + { { { 40, 0, 0 }, { 32, 56, 0 } } }, + { { { 40, 0, 1 }, { 29, 63, 0 } } }, + { { { 40, 0, 2 }, { 30, 62, 0 } } }, + { { { 41, 0, 1 }, { 30, 63, 0 } } }, + { { { 41, 0, 0 }, { 32, 59, 0 } } }, + { { { 41, 0, 1 }, { 31, 62, 0 } } }, + { { { 41, 0, 2 }, { 31, 63, 0 } } }, + { { { 42, 0, 1 }, { 32, 61, 0 } } }, + { { { 42, 0, 0 }, { 32, 62, 0 } } }, + { { { 42, 0, 1 }, { 32, 63, 0 } } }, + { { { 42, 0, 2 }, { 41, 46, 0 } } }, + { { { 43, 0, 1 }, { 33, 62, 0 } } }, + { { { 43, 0, 0 }, { 33, 63, 0 } } }, + { { { 43, 0, 1 }, { 34, 62, 0 } } }, + { { { 43, 0, 2 }, { 42, 47, 0 } } }, + { { { 44, 0, 1 }, { 34, 63, 0 } } }, + { { { 44, 0, 0 }, { 35, 62, 0 } } }, + { { { 44, 0, 1 }, { 35, 63, 0 } } }, + { { { 44, 0, 2 }, { 44, 46, 0 } } }, + { { { 45, 0, 1 }, { 36, 62, 0 } } }, + { { { 45, 0, 0 }, { 36, 63, 0 } } }, + { { { 45, 0, 1 }, { 37, 62, 0 } } }, + { { { 45, 0, 2 }, { 45, 47, 0 } } }, + { { { 46, 0, 1 }, { 37, 63, 0 } } }, + { { { 46, 0, 0 }, { 38, 62, 0 } } }, + { { { 46, 0, 1 }, { 38, 63, 0 } } }, + { { { 46, 0, 2 }, { 47, 46, 0 } } }, + { { { 47, 0, 1 }, { 39, 62, 0 } } }, + { { { 47, 0, 0 }, { 39, 63, 0 } } }, + { { { 47, 0, 1 }, { 40, 62, 0 } } }, + { { { 47, 0, 2 }, { 48, 46, 0 } } }, + { { { 48, 0, 2 }, { 40, 63, 0 } } }, + { { { 48, 0, 1 }, { 41, 62, 0 } } }, + { { { 48, 0, 0 }, { 41, 63, 0 } } }, + { { { 48, 0, 1 }, { 48, 49, 0 } } }, + { { { 48, 0, 2 }, { 42, 62, 0 } } }, + { { { 49, 0, 1 }, { 42, 63, 0 } } }, + { { { 49, 0, 0 }, { 43, 62, 0 } } }, + { { { 49, 0, 1 }, { 48, 52, 0 } } }, + { { { 49, 0, 2 }, { 43, 63, 0 } } }, + { { { 50, 0, 1 }, { 44, 62, 0 } } }, + { { { 50, 0, 0 }, { 44, 63, 0 } } }, + { { { 50, 0, 1 }, { 48, 55, 0 } } }, + { { { 50, 0, 2 }, { 45, 62, 0 } } }, + { { { 51, 0, 1 }, { 45, 63, 0 } } }, + { { { 51, 0, 0 }, { 46, 62, 0 } } }, + { { { 51, 0, 1 }, { 48, 58, 0 } } }, + { { { 51, 0, 2 }, { 46, 63, 0 } } }, + { { { 52, 0, 1 }, { 47, 62, 0 } } }, + { { { 52, 0, 0 }, { 47, 63, 0 } } }, + { { { 52, 0, 1 }, { 48, 61, 0 } } }, + { { { 52, 0, 2 }, { 48, 62, 0 } } }, + { { { 53, 0, 1 }, { 56, 47, 0 } } }, + { { { 53, 0, 0 }, { 48, 63, 0 } } }, + { { { 53, 0, 1 }, { 49, 62, 0 } } }, + { { { 53, 0, 2 }, { 49, 63, 0 } } }, + { { { 54, 0, 1 }, { 58, 46, 0 } } }, + { { { 54, 0, 0 }, { 50, 62, 0 } } }, + { { { 54, 0, 1 }, { 50, 63, 0 } } }, + { { { 54, 0, 2 }, { 51, 62, 0 } } }, + { { { 55, 0, 1 }, { 59, 47, 0 } } }, + { { { 55, 0, 0 }, { 51, 63, 0 } } }, + { { { 55, 0, 1 }, { 52, 62, 0 } } }, + { { { 55, 0, 2 }, { 52, 63, 0 } } }, + { { { 56, 0, 1 }, { 61, 46, 0 } } }, + { { { 56, 0, 0 }, { 53, 62, 0 } } }, + { { { 56, 0, 1 }, { 53, 63, 0 } } }, + { { { 56, 0, 2 }, { 54, 62, 0 } } }, + { { { 57, 0, 1 }, { 62, 47, 0 } } }, + { { { 57, 0, 0 }, { 54, 63, 0 } } }, + { { { 57, 0, 1 }, { 55, 62, 0 } } }, + { { { 57, 0, 2 }, { 55, 63, 0 } } }, + { { { 58, 0, 1 }, { 56, 62, 1 } } }, + { { { 58, 0, 0 }, { 56, 62, 0 } } }, + { { { 58, 0, 1 }, { 56, 63, 0 } } }, + { { { 58, 0, 2 }, { 57, 62, 0 } } }, + { { { 59, 0, 1 }, { 57, 63, 1 } } }, + { { { 59, 0, 0 }, { 57, 63, 0 } } }, + { { { 59, 0, 1 }, { 58, 62, 0 } } }, + { { { 59, 0, 2 }, { 58, 63, 0 } } }, + { { { 60, 0, 1 }, { 59, 62, 1 } } }, + { { { 60, 0, 0 }, { 59, 62, 0 } } }, + { { { 60, 0, 1 }, { 59, 63, 0 } } }, + { { { 60, 0, 2 }, { 60, 62, 0 } } }, + { { { 61, 0, 1 }, { 60, 63, 1 } } }, + { { { 61, 0, 0 }, { 60, 63, 0 } } }, + { { { 61, 0, 1 }, { 61, 62, 0 } } }, + { { { 61, 0, 2 }, { 61, 63, 0 } } }, + { { { 62, 0, 1 }, { 62, 62, 1 } } }, + { { { 62, 0, 0 }, { 62, 62, 0 } } }, + { { { 62, 0, 1 }, { 62, 63, 0 } } }, + { { { 62, 0, 2 }, { 63, 62, 0 } } }, + { { { 63, 0, 1 }, { 63, 63, 1 } } }, + { { { 63, 0, 0 }, { 63, 63, 0 } } } +}; diff --git a/Engine/lib/squish/squish-Info.plist b/Engine/lib/squish/squish-Info.plist new file mode 100644 index 000000000..5cb05e056 --- /dev/null +++ b/Engine/lib/squish/squish-Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.sjbrown.squish + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + FMWK + CFBundleSignature + ???? + CFBundleVersion + 1.0 + + diff --git a/Engine/lib/squish/squish.cpp b/Engine/lib/squish/squish.cpp new file mode 100644 index 000000000..5180738fc --- /dev/null +++ b/Engine/lib/squish/squish.cpp @@ -0,0 +1,239 @@ +/* ----------------------------------------------------------------------------- + + Copyright (c) 2006 Simon Brown si@sjbrown.co.uk + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + -------------------------------------------------------------------------- */ + +#include +#include "colourset.h" +#include "maths.h" +#include "rangefit.h" +#include "clusterfit.h" +#include "colourblock.h" +#include "alpha.h" +#include "singlecolourfit.h" + +namespace squish { + +int FixFlags( int flags ) +{ + // grab the flag bits + int method = flags & ( kDxt1 | kDxt3 | kDxt5 ); + int fit = flags & ( kColourIterativeClusterFit | kColourClusterFit | kColourRangeFit ); + int metric = flags & ( kColourMetricPerceptual | kColourMetricUniform ); + int extra = flags & kWeightColourByAlpha; + + // set defaults + if( method != kDxt3 && method != kDxt5 ) + method = kDxt1; + if( fit != kColourRangeFit ) + fit = kColourClusterFit; + if( metric != kColourMetricUniform ) + metric = kColourMetricPerceptual; + + // done + return method | fit | metric | extra; +} + +void Compress( u8 const* rgba, void* block, int flags ) +{ + // compress with full mask + CompressMasked( rgba, 0xffff, block, flags ); +} + +void CompressMasked( u8 const* rgba, int mask, void* block, int flags ) +{ + // fix any bad flags + flags = FixFlags( flags ); + + // get the block locations + void* colourBlock = block; + void* alphaBock = block; + if( ( flags & ( kDxt3 | kDxt5 ) ) != 0 ) + colourBlock = reinterpret_cast< u8* >( block ) + 8; + + // create the minimal point set + ColourSet colours( rgba, mask, flags ); + + // check the compression type and compress colour + if( colours.GetCount() == 1 ) + { + // always do a single colour fit + SingleColourFit fit( &colours, flags ); + fit.Compress( colourBlock ); + } + else if( ( flags & kColourRangeFit ) != 0 || colours.GetCount() == 0 ) + { + // do a range fit + RangeFit fit( &colours, flags ); + fit.Compress( colourBlock ); + } + else + { + // default to a cluster fit (could be iterative or not) + ClusterFit fit( &colours, flags ); + fit.Compress( colourBlock ); + } + + // compress alpha separately if necessary + if( ( flags & kDxt3 ) != 0 ) + CompressAlphaDxt3( rgba, mask, alphaBock ); + else if( ( flags & kDxt5 ) != 0 ) + CompressAlphaDxt5( rgba, mask, alphaBock ); +} + +void Decompress( u8* rgba, void const* block, int flags ) +{ + // fix any bad flags + flags = FixFlags( flags ); + + // get the block locations + void const* colourBlock = block; + void const* alphaBock = block; + if( ( flags & ( kDxt3 | kDxt5 ) ) != 0 ) + colourBlock = reinterpret_cast< u8 const* >( block ) + 8; + + // decompress colour + DecompressColour( rgba, colourBlock, ( flags & kDxt1 ) != 0 ); + + // decompress alpha separately if necessary + if( ( flags & kDxt3 ) != 0 ) + DecompressAlphaDxt3( rgba, alphaBock ); + else if( ( flags & kDxt5 ) != 0 ) + DecompressAlphaDxt5( rgba, alphaBock ); +} + +int GetStorageRequirements( int width, int height, int flags ) +{ + // fix any bad flags + flags = FixFlags( flags ); + + // compute the storage requirements + int blockcount = ( ( width + 3 )/4 ) * ( ( height + 3 )/4 ); + int blocksize = ( ( flags & kDxt1 ) != 0 ) ? 8 : 16; + return blockcount*blocksize; +} + +void CompressImage( u8 const* rgba, int width, int height, void* blocks, int flags ) +{ + // fix any bad flags + flags = FixFlags( flags ); + + // initialise the block output + u8* targetBlock = reinterpret_cast< u8* >( blocks ); + int bytesPerBlock = ( ( flags & kDxt1 ) != 0 ) ? 8 : 16; + + // loop over blocks + for( int y = 0; y < height; y += 4 ) + { + for( int x = 0; x < width; x += 4 ) + { + // build the 4x4 block of pixels + u8 sourceRgba[16*4]; + u8* targetPixel = sourceRgba; + int mask = 0; + for( int py = 0; py < 4; ++py ) + { + for( int px = 0; px < 4; ++px ) + { + // get the source pixel in the image + int sx = x + px; + int sy = y + py; + + // enable if we're in the image + if( sx < width && sy < height ) + { + // copy the rgba value + u8 const* sourcePixel = rgba + 4*( width*sy + sx ); + for( int i = 0; i < 4; ++i ) + *targetPixel++ = *sourcePixel++; + + // enable this pixel + mask |= ( 1 << ( 4*py + px ) ); + } + else + { + // skip this pixel as its outside the image + targetPixel += 4; + } + } + } + + // compress it into the output + CompressMasked( sourceRgba, mask, targetBlock, flags ); + + // advance + targetBlock += bytesPerBlock; + } + } +} + +void DecompressImage( u8* rgba, int width, int height, void const* blocks, int flags ) +{ + // fix any bad flags + flags = FixFlags( flags ); + + // initialise the block input + u8 const* sourceBlock = reinterpret_cast< u8 const* >( blocks ); + int bytesPerBlock = ( ( flags & kDxt1 ) != 0 ) ? 8 : 16; + + // loop over blocks + for( int y = 0; y < height; y += 4 ) + { + for( int x = 0; x < width; x += 4 ) + { + // decompress the block + u8 targetRgba[4*16]; + Decompress( targetRgba, sourceBlock, flags ); + + // write the decompressed pixels to the correct image locations + u8 const* sourcePixel = targetRgba; + for( int py = 0; py < 4; ++py ) + { + for( int px = 0; px < 4; ++px ) + { + // get the target location + int sx = x + px; + int sy = y + py; + if( sx < width && sy < height ) + { + u8* targetPixel = rgba + 4*( width*sy + sx ); + + // copy the rgba value + for( int i = 0; i < 4; ++i ) + *targetPixel++ = *sourcePixel++; + } + else + { + // skip this pixel as its outside the image + sourcePixel += 4; + } + } + } + + // advance + sourceBlock += bytesPerBlock; + } + } +} + +} // namespace squish diff --git a/Engine/lib/squish/squish.h b/Engine/lib/squish/squish.h new file mode 100644 index 000000000..b69cba65c --- /dev/null +++ b/Engine/lib/squish/squish.h @@ -0,0 +1,251 @@ +/* ----------------------------------------------------------------------------- + + Copyright (c) 2006 Simon Brown si@sjbrown.co.uk + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + -------------------------------------------------------------------------- */ + +#ifndef SQUISH_H +#define SQUISH_H + +//! All squish API functions live in this namespace. +namespace squish { + +// ----------------------------------------------------------------------------- + +//! Typedef a quantity that is a single unsigned byte. +typedef unsigned char u8; + +// ----------------------------------------------------------------------------- + +enum +{ + //! Use DXT1 compression. + kDxt1 = ( 1 << 0 ), + + //! Use DXT3 compression. + kDxt3 = ( 1 << 1 ), + + //! Use DXT5 compression. + kDxt5 = ( 1 << 2 ), + + //! Use a very slow but very high quality colour compressor. + kColourIterativeClusterFit = ( 1 << 8 ), + + //! Use a slow but high quality colour compressor (the default). + kColourClusterFit = ( 1 << 3 ), + + //! Use a fast but low quality colour compressor. + kColourRangeFit = ( 1 << 4 ), + + //! Use a perceptual metric for colour error (the default). + kColourMetricPerceptual = ( 1 << 5 ), + + //! Use a uniform metric for colour error. + kColourMetricUniform = ( 1 << 6 ), + + //! Weight the colour by alpha during cluster fit (disabled by default). + kWeightColourByAlpha = ( 1 << 7 ) +}; + +// ----------------------------------------------------------------------------- + +/*! @brief Compresses a 4x4 block of pixels. + + @param rgba The rgba values of the 16 source pixels. + @param block Storage for the compressed DXT block. + @param flags Compression flags. + + The source pixels should be presented as a contiguous array of 16 rgba + values, with each component as 1 byte each. In memory this should be: + + { r1, g1, b1, a1, .... , r16, g16, b16, a16 } + + The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, + however, DXT1 will be used by default if none is specified. When using DXT1 + compression, 8 bytes of storage are required for the compressed DXT block. + DXT3 and DXT5 compression require 16 bytes of storage per block. + + The flags parameter can also specify a preferred colour compressor and + colour error metric to use when fitting the RGB components of the data. + Possible colour compressors are: kColourClusterFit (the default), + kColourRangeFit or kColourIterativeClusterFit. Possible colour error metrics + are: kColourMetricPerceptual (the default) or kColourMetricUniform. If no + flags are specified in any particular category then the default will be + used. Unknown flags are ignored. + + When using kColourClusterFit, an additional flag can be specified to + weight the colour of each pixel by its alpha value. For images that are + rendered using alpha blending, this can significantly increase the + perceived quality. +*/ +void Compress( u8 const* rgba, void* block, int flags ); + +// ----------------------------------------------------------------------------- + +/*! @brief Compresses a 4x4 block of pixels. + + @param rgba The rgba values of the 16 source pixels. + @param mask The valid pixel mask. + @param block Storage for the compressed DXT block. + @param flags Compression flags. + + The source pixels should be presented as a contiguous array of 16 rgba + values, with each component as 1 byte each. In memory this should be: + + { r1, g1, b1, a1, .... , r16, g16, b16, a16 } + + The mask parameter enables only certain pixels within the block. The lowest + bit enables the first pixel and so on up to the 16th bit. Bits beyond the + 16th bit are ignored. Pixels that are not enabled are allowed to take + arbitrary colours in the output block. An example of how this can be used + is in the CompressImage function to disable pixels outside the bounds of + the image when the width or height is not divisible by 4. + + The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, + however, DXT1 will be used by default if none is specified. When using DXT1 + compression, 8 bytes of storage are required for the compressed DXT block. + DXT3 and DXT5 compression require 16 bytes of storage per block. + + The flags parameter can also specify a preferred colour compressor and + colour error metric to use when fitting the RGB components of the data. + Possible colour compressors are: kColourClusterFit (the default), + kColourRangeFit or kColourIterativeClusterFit. Possible colour error metrics + are: kColourMetricPerceptual (the default) or kColourMetricUniform. If no + flags are specified in any particular category then the default will be + used. Unknown flags are ignored. + + When using kColourClusterFit, an additional flag can be specified to + weight the colour of each pixel by its alpha value. For images that are + rendered using alpha blending, this can significantly increase the + perceived quality. +*/ +void CompressMasked( u8 const* rgba, int mask, void* block, int flags ); + +// ----------------------------------------------------------------------------- + +/*! @brief Decompresses a 4x4 block of pixels. + + @param rgba Storage for the 16 decompressed pixels. + @param block The compressed DXT block. + @param flags Compression flags. + + The decompressed pixels will be written as a contiguous array of 16 rgba + values, with each component as 1 byte each. In memory this is: + + { r1, g1, b1, a1, .... , r16, g16, b16, a16 } + + The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, + however, DXT1 will be used by default if none is specified. All other flags + are ignored. +*/ +void Decompress( u8* rgba, void const* block, int flags ); + +// ----------------------------------------------------------------------------- + +/*! @brief Computes the amount of compressed storage required. + + @param width The width of the image. + @param height The height of the image. + @param flags Compression flags. + + The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, + however, DXT1 will be used by default if none is specified. All other flags + are ignored. + + Most DXT images will be a multiple of 4 in each dimension, but this + function supports arbitrary size images by allowing the outer blocks to + be only partially used. +*/ +int GetStorageRequirements( int width, int height, int flags ); + +// ----------------------------------------------------------------------------- + +/*! @brief Compresses an image in memory. + + @param rgba The pixels of the source. + @param width The width of the source image. + @param height The height of the source image. + @param blocks Storage for the compressed output. + @param flags Compression flags. + + The source pixels should be presented as a contiguous array of width*height + rgba values, with each component as 1 byte each. In memory this should be: + + { r1, g1, b1, a1, .... , rn, gn, bn, an } for n = width*height + + The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, + however, DXT1 will be used by default if none is specified. When using DXT1 + compression, 8 bytes of storage are required for each compressed DXT block. + DXT3 and DXT5 compression require 16 bytes of storage per block. + + The flags parameter can also specify a preferred colour compressor and + colour error metric to use when fitting the RGB components of the data. + Possible colour compressors are: kColourClusterFit (the default), + kColourRangeFit or kColourIterativeClusterFit. Possible colour error metrics + are: kColourMetricPerceptual (the default) or kColourMetricUniform. If no + flags are specified in any particular category then the default will be + used. Unknown flags are ignored. + + When using kColourClusterFit, an additional flag can be specified to + weight the colour of each pixel by its alpha value. For images that are + rendered using alpha blending, this can significantly increase the + perceived quality. + + Internally this function calls squish::Compress for each block. To see how + much memory is required in the compressed image, use + squish::GetStorageRequirements. +*/ +void CompressImage( u8 const* rgba, int width, int height, void* blocks, int flags ); +void CompressImageOMP( u8 const* rgba, int width, int height, void* blocks, int flags ); + +// ----------------------------------------------------------------------------- + +/*! @brief Decompresses an image in memory. + + @param rgba Storage for the decompressed pixels. + @param width The width of the source image. + @param height The height of the source image. + @param blocks The compressed DXT blocks. + @param flags Compression flags. + + The decompressed pixels will be written as a contiguous array of width*height + 16 rgba values, with each component as 1 byte each. In memory this is: + + { r1, g1, b1, a1, .... , rn, gn, bn, an } for n = width*height + + The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression, + however, DXT1 will be used by default if none is specified. All other flags + are ignored. + + Internally this function calls squish::Decompress for each block. +*/ +void DecompressImage( u8* rgba, int width, int height, void const* blocks, int flags ); + +// ----------------------------------------------------------------------------- + +// Helper method +int FixFlags( int flags ); + +} // namespace squish + +#endif // ndef SQUISH_H + diff --git a/Engine/lib/squish/squishMath.cpp b/Engine/lib/squish/squishMath.cpp new file mode 100644 index 000000000..9842d4f71 --- /dev/null +++ b/Engine/lib/squish/squishMath.cpp @@ -0,0 +1,62 @@ +#include "squishMath.h" +#include + +float SquishMath::fabs( const float f ) +{ + return ::fabs( f ); +} + +float SquishMath::pow( const float x, const float y ) +{ + return ::pow( x, y ); +} + +float SquishMath::cos( const float theta ) +{ + return ::cos( theta ); +} + +float SquishMath::sin( const float theta ) +{ + return ::sin( theta ); +} + +float SquishMath::sqrt( const float a ) +{ + return ::sqrtf( a ); +} + +float SquishMath::atan2( const float a, const float b ) +{ + return ::atan2f( a, b ); +} + +float SquishMath::min( const float a, const float b ) +{ + return a < b ? a : b; +} + +float SquishMath::max( const float a, const float b ) +{ + return a < b ? b : a; +} + +float SquishMath::floor( const float a ) +{ + return ::floorf( a ); +} + +float SquishMath::ceil( const float a ) +{ + return ::ceilf( a ); +} + +int SquishMath::min( const int a, const int b ) +{ + return a < b ? a : b; +} + +int SquishMath::max( const int a, const int b ) +{ + return a < b ? b : a; +} \ No newline at end of file diff --git a/Engine/lib/squish/squishMath.h b/Engine/lib/squish/squishMath.h new file mode 100644 index 000000000..6b72e197e --- /dev/null +++ b/Engine/lib/squish/squishMath.h @@ -0,0 +1,28 @@ +#ifndef _SQUISH_MATH_H_ +#define _SQUISH_MATH_H_ + +#define FLT_MAX 3.402823466e+38F +#define FLT_EPSILON 1.192092896e-07F +#define INT_MAX 2147483647 /* maximum (signed) int value */ + +// Abstract the math in squish so it doesn't use std:: directly +namespace SquishMath +{ + float fabs( const float f ); + float pow( const float x, const float y ); + float cos( const float theta ); + float sin( const float theta ); + float sqrt( const float a ); + float atan2( const float a, const float b ); + + float min( const float a, const float b ); + float max( const float a, const float b ); + + float floor( const float a ); + float ceil( const float a ); + + int min( const int a, const int b ); + int max( const int a, const int b ); +}; + +#endif \ No newline at end of file diff --git a/Engine/lib/squish/squishOmp.cpp b/Engine/lib/squish/squishOmp.cpp new file mode 100644 index 000000000..ba4be57d5 --- /dev/null +++ b/Engine/lib/squish/squishOmp.cpp @@ -0,0 +1,102 @@ +/* ----------------------------------------------------------------------------- + +Copyright (c) 2006 Simon Brown si@sjbrown.co.uk + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +-------------------------------------------------------------------------- */ +#include + +//#define ENABLE_OPEN_MP + +#ifndef ENABLE_OPEN_MP +void squish::CompressImageOMP(u8 const* rgba, int width, int height, void* blocks, int flags) +{ + squish::CompressImage( rgba, width, height, blocks, flags ); +} + +#else + +// OMP implementation +#include + +// OpenMP implementation of squish::CompressImage +// +// If you have any fixes, improvements, suggestions of: "L2OMP n00b" +// please send them to: +// Pat Wilson +// patw@garagegames.com +namespace squish { + +struct _blk_row +{ + unsigned int pxl[4]; +}; + +void CompressImageOMP( u8 const* rgba, int width, int height, void* blocks, int flags ) +{ + // fix any bad flags + flags = FixFlags( flags ); + + // Should really assert here or something + if( width % 4 || height % 4 ) + return; + + // initialize the block output + u8 *const targetBlock = reinterpret_cast( blocks ); + const int bytesPerBlock = ( ( flags & kDxt1 ) != 0 ) ? 8 : 16; + const int blockHeight = height >> 2; + const int blockWidth = width >> 2; + +#pragma omp parallel +{ // begin omp block + + // loop over blocks +#pragma omp for + for( int by = 0; by < blockHeight; by++ ) + { + const int y = by * 4; + + for( int bx = 0; bx < blockWidth; bx++ ) + { + const int x = bx * 4; + + // build the 4x4 block of pixels + u8 sourceRgba[16 * 4]; + +#define _load_row(r) (reinterpret_cast<_blk_row *>(sourceRgba))[r] = (*reinterpret_cast(rgba + 4 * ( width * (y + r) + x ))) + _load_row(0); + _load_row(1); + _load_row(2); + _load_row(3); +#undef _load_row + + // compress it into the output + const int blockIdx = by * blockWidth + bx; + Compress( sourceRgba, &targetBlock[blockIdx * bytesPerBlock], flags ); + } + } + +} // end omp block + +} + +} // namespace +#endif diff --git a/Engine/lib/squish/texture_compression_s3tc.txt b/Engine/lib/squish/texture_compression_s3tc.txt new file mode 100644 index 000000000..f229cf367 --- /dev/null +++ b/Engine/lib/squish/texture_compression_s3tc.txt @@ -0,0 +1,508 @@ +Name + + EXT_texture_compression_s3tc + +Name Strings + + GL_EXT_texture_compression_s3tc + +Contact + + Pat Brown, NVIDIA Corporation (pbrown 'at' nvidia.com) + +Status + + FINAL + +Version + + 1.1, 16 November 2001 (containing only clarifications relative to + version 1.0, dated 7 July 2000) + +Number + + 198 + +Dependencies + + OpenGL 1.1 is required. + + GL_ARB_texture_compression is required. + + This extension is written against the OpenGL 1.2.1 Specification. + +Overview + + This extension provides additional texture compression functionality + specific to S3's S3TC format (called DXTC in Microsoft's DirectX API), + subject to all the requirements and limitations described by the extension + GL_ARB_texture_compression. + + This extension supports DXT1, DXT3, and DXT5 texture compression formats. + For the DXT1 image format, this specification supports an RGB-only mode + and a special RGBA mode with single-bit "transparent" alpha. + +IP Status + + Contact S3 Incorporated (http://www.s3.com) regarding any intellectual + property issues associated with implementing this extension. + + WARNING: Vendors able to support S3TC texture compression in Direct3D + drivers do not necessarily have the right to use the same functionality in + OpenGL. + +Issues + + (1) Should DXT2 and DXT4 (premultiplied alpha) formats be supported? + + RESOLVED: No -- insufficient interest. Supporting DXT2 and DXT4 + would require some rework to the TexEnv definition (maybe add a new + base internal format RGBA_PREMULTIPLIED_ALPHA) for these formats. + Note that the EXT_texture_env_combine extension (which extends normal + TexEnv modes) can be used to support textures with premultipled alpha. + + (2) Should generic "RGB_S3TC_EXT" and "RGBA_S3TC_EXT" enums be supported + or should we use only the DXT enums? + + RESOLVED: No. A generic RGBA_S3TC_EXT is problematic because DXT3 + and DXT5 are both nominally RGBA (and DXT1 with the 1-bit alpha is + also) yet one format must be chosen up front. + + (3) Should TexSubImage support all block-aligned edits or just the minimal + functionality required by the ARB_texture_compression extension? + + RESOLVED: Allow all valid block-aligned edits. + + (4) A pre-compressed image with a DXT1 format can be used as either an + RGB_S3TC_DXT1 or an RGBA_S3TC_DXT1 image. If the image has + transparent texels, how are they treated in each format? + + RESOLVED: The renderer has to make sure that an RGB_S3TC_DXT1 format + is decoded as RGB (where alpha is effectively one for all texels), + while RGBA_S3TC_DXT1 is decoded as RGBA (where alpha is zero for all + texels with "transparent" encodings). Otherwise, the formats are + identical. + + (5) Is the encoding of the RGB components for DXT1 formats correct in this + spec? MSDN documentation does not specify an RGB color for the + "transparent" encoding. Is it really black? + + RESOLVED: Yes. The specification for the DXT1 format initially + required black, but later changed that requirement to a + recommendation. All vendors involved in the definition of this + specification support black. In addition, specifying black has a + useful behavior. + + When blending multiple texels (GL_LINEAR filtering), mixing opaque and + transparent samples is problematic. Defining a black color on + transparent texels achieves a sensible result that works like a + texture with premultiplied alpha. For example, if three opaque white + and one transparent sample is being averaged, the result would be a + 75% intensity gray (with an alpha of 75%). This is the same result on + the color channels as would be obtained using a white color, 75% + alpha, and a SRC_ALPHA blend factor. + + (6) Is the encoding of the RGB components for DXT3 and DXT5 formats + correct in this spec? MSDN documentation suggests that the RGB blocks + for DXT3 and DXT5 are decoded as described by the DXT1 format. + + RESOLVED: Yes -- this appears to be a bug in the MSDN documentation. + The specification for the DXT2-DXT5 formats require decoding using the + opaque block encoding, regardless of the relative values of "color0" + and "color1". + +New Procedures and Functions + + None. + +New Tokens + + Accepted by the parameter of TexImage2D, CopyTexImage2D, + and CompressedTexImage2DARB and the parameter of + CompressedTexSubImage2DARB: + + COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 + COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 + COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 + COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 + +Additions to Chapter 2 of the OpenGL 1.2.1 Specification (OpenGL Operation) + + None. + +Additions to Chapter 3 of the OpenGL 1.2.1 Specification (Rasterization) + + Add to Table 3.16.1: Specific Compressed Internal Formats + + Compressed Internal Format Base Internal Format + ========================== ==================== + COMPRESSED_RGB_S3TC_DXT1_EXT RGB + COMPRESSED_RGBA_S3TC_DXT1_EXT RGBA + COMPRESSED_RGBA_S3TC_DXT3_EXT RGBA + COMPRESSED_RGBA_S3TC_DXT5_EXT RGBA + + + Modify Section 3.8.2, Alternate Image Specification + + (add to end of TexSubImage discussion, p.123 -- after edit from the + ARB_texture_compression spec) + + If the internal format of the texture image being modified is + COMPRESSED_RGB_S3TC_DXT1_EXT, COMPRESSED_RGBA_S3TC_DXT1_EXT, + COMPRESSED_RGBA_S3TC_DXT3_EXT, or COMPRESSED_RGBA_S3TC_DXT5_EXT, the + texture is stored using one of the several S3TC compressed texture image + formats. Such images are easily edited along 4x4 texel boundaries, so the + limitations on TexSubImage2D or CopyTexSubImage2D parameters are relaxed. + TexSubImage2D and CopyTexSubImage2D will result in an INVALID_OPERATION + error only if one of the following conditions occurs: + + * is not a multiple of four or equal to TEXTURE_WIDTH, + unless and are both zero. + * is not a multiple of four or equal to TEXTURE_HEIGHT, + unless and are both zero. + * or is not a multiple of four. + + The contents of any 4x4 block of texels of an S3TC compressed texture + image that does not intersect the area being modified are preserved during + valid TexSubImage2D and CopyTexSubImage2D calls. + + + Add to Section 3.8.2, Alternate Image Specification (adding to the end of + the CompressedTexImage section introduced by the ARB_texture_compression + spec) + + If is COMPRESSED_RGB_S3TC_DXT1_EXT, + COMPRESSED_RGBA_S3TC_DXT1_EXT, COMPRESSED_RGBA_S3TC_DXT3_EXT, or + COMPRESSED_RGBA_S3TC_DXT5_EXT, the compressed texture is stored using one + of several S3TC compressed texture image formats. The S3TC texture + compression algorithm supports only 2D images without borders. + CompressedTexImage1DARB and CompressedTexImage3DARB produce an + INVALID_ENUM error if is an S3TC format. + CompressedTexImage2DARB will produce an INVALID_OPERATION error if + is non-zero. + + + Add to Section 3.8.2, Alternate Image Specification (adding to the end of + the CompressedTexSubImage section introduced by the + ARB_texture_compression spec) + + If the internal format of the texture image being modified is + COMPRESSED_RGB_S3TC_DXT1_EXT, COMPRESSED_RGBA_S3TC_DXT1_EXT, + COMPRESSED_RGBA_S3TC_DXT3_EXT, or COMPRESSED_RGBA_S3TC_DXT5_EXT, the + texture is stored using one of the several S3TC compressed texture image + formats. Since the S3TC texture compression algorithm supports only 2D + images, CompressedTexSubImage1DARB and CompressedTexSubImage3DARB produce + an INVALID_ENUM error if is an S3TC format. Since S3TC images + are easily edited along 4x4 texel boundaries, the limitations on + CompressedTexSubImage2D are relaxed. CompressedTexSubImage2D will result + in an INVALID_OPERATION error only if one of the following conditions + occurs: + + * is not a multiple of four or equal to TEXTURE_WIDTH. + * is not a multiple of four or equal to TEXTURE_HEIGHT. + * or is not a multiple of four. + + The contents of any 4x4 block of texels of an S3TC compressed texture + image that does not intersect the area being modified are preserved during + valid TexSubImage2D and CopyTexSubImage2D calls. + +Additions to Chapter 4 of the OpenGL 1.2.1 Specification (Per-Fragment +Operations and the Frame Buffer) + + None. + +Additions to Chapter 5 of the OpenGL 1.2.1 Specification (Special Functions) + + None. + +Additions to Chapter 6 of the OpenGL 1.2.1 Specification (State and +State Requests) + + None. + +Additions to Appendix A of the OpenGL 1.2.1 Specification (Invariance) + + None. + +Additions to the AGL/GLX/WGL Specifications + + None. + +GLX Protocol + + None. + +Errors + + INVALID_ENUM is generated by CompressedTexImage1DARB or + CompressedTexImage3DARB if is + COMPRESSED_RGB_S3TC_DXT1_EXT, COMPRESSED_RGBA_S3TC_DXT1_EXT, + COMPRESSED_RGBA_S3TC_DXT3_EXT, or COMPRESSED_RGBA_S3TC_DXT5_EXT. + + INVALID_OPERATION is generated by CompressedTexImage2DARB if + is COMPRESSED_RGB_S3TC_DXT1_EXT, + COMPRESSED_RGBA_S3TC_DXT1_EXT, COMPRESSED_RGBA_S3TC_DXT3_EXT, or + COMPRESSED_RGBA_S3TC_DXT5_EXT and is not equal to zero. + + INVALID_ENUM is generated by CompressedTexSubImage1DARB or + CompressedTexSubImage3DARB if is COMPRESSED_RGB_S3TC_DXT1_EXT, + COMPRESSED_RGBA_S3TC_DXT1_EXT, COMPRESSED_RGBA_S3TC_DXT3_EXT, or + COMPRESSED_RGBA_S3TC_DXT5_EXT. + + INVALID_OPERATION is generated by TexSubImage2D CopyTexSubImage2D, or + CompressedTexSubImage2D if TEXTURE_INTERNAL_FORMAT is + COMPRESSED_RGB_S3TC_DXT1_EXT, COMPRESSED_RGBA_S3TC_DXT1_EXT, + COMPRESSED_RGBA_S3TC_DXT3_EXT, or COMPRESSED_RGBA_S3TC_DXT5_EXT and any of + the following apply: is not a multiple of four or equal to + TEXTURE_WIDTH; is not a multiple of four or equal to + TEXTURE_HEIGHT; or is not a multiple of four. + + + The following restrictions from the ARB_texture_compression specification + do not apply to S3TC texture formats, since subimage modification is + straightforward as long as the subimage is properly aligned. + + DELETE: INVALID_OPERATION is generated by TexSubImage1D, TexSubImage2D, + DELETE: TexSubImage3D, CopyTexSubImage1D, CopyTexSubImage2D, or + DELETE: CopyTexSubImage3D if the internal format of the texture image is + DELETE: compressed and , , or does not equal + DELETE: -b, where b is value of TEXTURE_BORDER. + + DELETE: INVALID_VALUE is generated by CompressedTexSubImage1DARB, + DELETE: CompressedTexSubImage2DARB, or CompressedTexSubImage3DARB if the + DELETE: entire texture image is not being edited: if , + DELETE: , or is greater than -b, + is + DELETE: less than w+b, + is less than h+b, or + DELETE: + is less than d+b, where b is the value of + DELETE: TEXTURE_BORDER, w is the value of TEXTURE_WIDTH, h is the value of + DELETE: TEXTURE_HEIGHT, and d is the value of TEXTURE_DEPTH. + + See also errors in the GL_ARB_texture_compression specification. + +New State + + In the "Textures" state table, increment the TEXTURE_INTERNAL_FORMAT + subscript for Z by 4 in the "Type" row. + +New Implementation Dependent State + + None + +Appendix + + S3TC Compressed Texture Image Formats + + Compressed texture images stored using the S3TC compressed image formats + are represented as a collection of 4x4 texel blocks, where each block + contains 64 or 128 bits of texel data. The image is encoded as a normal + 2D raster image in which each 4x4 block is treated as a single pixel. If + an S3TC image has a width or height less than four, the data corresponding + to texels outside the image are irrelevant and undefined. + + When an S3TC image with a width of , height of , and block size of + (8 or 16 bytes) is decoded, the corresponding image size (in + bytes) is: + + ceil(/4) * ceil(/4) * blocksize. + + When decoding an S3TC image, the block containing the texel at offset + (, ) begins at an offset (in bytes) relative to the base of the + image of: + + blocksize * (ceil(/4) * floor(/4) + floor(/4)). + + The data corresponding to a specific texel (, ) are extracted from a + 4x4 texel block using a relative (x,y) value of + + ( modulo 4, modulo 4). + + There are four distinct S3TC image formats: + + COMPRESSED_RGB_S3TC_DXT1_EXT: Each 4x4 block of texels consists of 64 + bits of RGB image data. + + Each RGB image data block is encoded as a sequence of 8 bytes, called (in + order of increasing address): + + c0_lo, c0_hi, c1_lo, c1_hi, bits_0, bits_1, bits_2, bits_3 + + The 8 bytes of the block are decoded into three quantities: + + color0 = c0_lo + c0_hi * 256 + color1 = c1_lo + c1_hi * 256 + bits = bits_0 + 256 * (bits_1 + 256 * (bits_2 + 256 * bits_3)) + + color0 and color1 are 16-bit unsigned integers that are unpacked to + RGB colors RGB0 and RGB1 as though they were 16-bit packed pixels with + a of RGB and a type of UNSIGNED_SHORT_5_6_5. + + bits is a 32-bit unsigned integer, from which a two-bit control code + is extracted for a texel at location (x,y) in the block using: + + code(x,y) = bits[2*(4*y+x)+1..2*(4*y+x)+0] + + where bit 31 is the most significant and bit 0 is the least + significant bit. + + The RGB color for a texel at location (x,y) in the block is given by: + + RGB0, if color0 > color1 and code(x,y) == 0 + RGB1, if color0 > color1 and code(x,y) == 1 + (2*RGB0+RGB1)/3, if color0 > color1 and code(x,y) == 2 + (RGB0+2*RGB1)/3, if color0 > color1 and code(x,y) == 3 + + RGB0, if color0 <= color1 and code(x,y) == 0 + RGB1, if color0 <= color1 and code(x,y) == 1 + (RGB0+RGB1)/2, if color0 <= color1 and code(x,y) == 2 + BLACK, if color0 <= color1 and code(x,y) == 3 + + Arithmetic operations are done per component, and BLACK refers to an + RGB color where red, green, and blue are all zero. + + Since this image has an RGB format, there is no alpha component and the + image is considered fully opaque. + + + COMPRESSED_RGBA_S3TC_DXT1_EXT: Each 4x4 block of texels consists of 64 + bits of RGB image data and minimal alpha information. The RGB components + of a texel are extracted in the same way as COMPRESSED_RGB_S3TC_DXT1_EXT. + + The alpha component for a texel at location (x,y) in the block is + given by: + + 0.0, if color0 <= color1 and code(x,y) == 3 + 1.0, otherwise + + IMPORTANT: When encoding an RGBA image into a format using 1-bit + alpha, any texels with an alpha component less than 0.5 end up with an + alpha of 0.0 and any texels with an alpha component greater than or + equal to 0.5 end up with an alpha of 1.0. When encoding an RGBA image + into the COMPRESSED_RGBA_S3TC_DXT1_EXT format, the resulting red, + green, and blue components of any texels with a final alpha of 0.0 + will automatically be zero (black). If this behavior is not desired + by an application, it should not use COMPRESSED_RGBA_S3TC_DXT1_EXT. + This format will never be used when a generic compressed internal + format (Table 3.16.2) is specified, although the nearly identical + format COMPRESSED_RGB_S3TC_DXT1_EXT (above) may be. + + + COMPRESSED_RGBA_S3TC_DXT3_EXT: Each 4x4 block of texels consists of 64 + bits of uncompressed alpha image data followed by 64 bits of RGB image + data. + + Each RGB image data block is encoded according to the + COMPRESSED_RGB_S3TC_DXT1_EXT format, with the exception that the two code + bits always use the non-transparent encodings. In other words, they are + treated as though color0 > color1, regardless of the actual values of + color0 and color1. + + Each alpha image data block is encoded as a sequence of 8 bytes, called + (in order of increasing address): + + a0, a1, a2, a3, a4, a5, a6, a7 + + The 8 bytes of the block are decoded into one 64-bit integer: + + alpha = a0 + 256 * (a1 + 256 * (a2 + 256 * (a3 + 256 * (a4 + + 256 * (a5 + 256 * (a6 + 256 * a7)))))) + + alpha is a 64-bit unsigned integer, from which a four-bit alpha value + is extracted for a texel at location (x,y) in the block using: + + alpha(x,y) = bits[4*(4*y+x)+3..4*(4*y+x)+0] + + where bit 63 is the most significant and bit 0 is the least + significant bit. + + The alpha component for a texel at location (x,y) in the block is + given by alpha(x,y) / 15. + + + COMPRESSED_RGBA_S3TC_DXT5_EXT: Each 4x4 block of texels consists of 64 + bits of compressed alpha image data followed by 64 bits of RGB image data. + + Each RGB image data block is encoded according to the + COMPRESSED_RGB_S3TC_DXT1_EXT format, with the exception that the two code + bits always use the non-transparent encodings. In other words, they are + treated as though color0 > color1, regardless of the actual values of + color0 and color1. + + Each alpha image data block is encoded as a sequence of 8 bytes, called + (in order of increasing address): + + alpha0, alpha1, bits_0, bits_1, bits_2, bits_3, bits_4, bits_5 + + The alpha0 and alpha1 are 8-bit unsigned bytes converted to alpha + components by multiplying by 1/255. + + The 6 "bits" bytes of the block are decoded into one 48-bit integer: + + bits = bits_0 + 256 * (bits_1 + 256 * (bits_2 + 256 * (bits_3 + + 256 * (bits_4 + 256 * bits_5)))) + + bits is a 48-bit unsigned integer, from which a three-bit control code + is extracted for a texel at location (x,y) in the block using: + + code(x,y) = bits[3*(4*y+x)+1..3*(4*y+x)+0] + + where bit 47 is the most significant and bit 0 is the least + significant bit. + + The alpha component for a texel at location (x,y) in the block is + given by: + + alpha0, code(x,y) == 0 + alpha1, code(x,y) == 1 + + (6*alpha0 + 1*alpha1)/7, alpha0 > alpha1 and code(x,y) == 2 + (5*alpha0 + 2*alpha1)/7, alpha0 > alpha1 and code(x,y) == 3 + (4*alpha0 + 3*alpha1)/7, alpha0 > alpha1 and code(x,y) == 4 + (3*alpha0 + 4*alpha1)/7, alpha0 > alpha1 and code(x,y) == 5 + (2*alpha0 + 5*alpha1)/7, alpha0 > alpha1 and code(x,y) == 6 + (1*alpha0 + 6*alpha1)/7, alpha0 > alpha1 and code(x,y) == 7 + + (4*alpha0 + 1*alpha1)/5, alpha0 <= alpha1 and code(x,y) == 2 + (3*alpha0 + 2*alpha1)/5, alpha0 <= alpha1 and code(x,y) == 3 + (2*alpha0 + 3*alpha1)/5, alpha0 <= alpha1 and code(x,y) == 4 + (1*alpha0 + 4*alpha1)/5, alpha0 <= alpha1 and code(x,y) == 5 + 0.0, alpha0 <= alpha1 and code(x,y) == 6 + 1.0, alpha0 <= alpha1 and code(x,y) == 7 + + +Revision History + + 1.1, 11/16/01 pbrown: Updated contact info, clarified where texels + fall within a single block. + + 1.0, 07/07/00 prbrown1: Published final version agreed to by working + group members. + + 0.9, 06/24/00 prbrown1: Documented that block-aligned TexSubImage calls + do not modify existing texels outside the + modified blocks. Added caveat to allow for a + (0,0)-anchored TexSubImage operation of + arbitrary size. + + 0.7, 04/11/00 prbrown1: Added issues on DXT1, DXT3, and DXT5 encodings + where the MSDN documentation doesn't match what + is really done. Added enum values from the + extension registry. + + 0.4, 03/28/00 prbrown1: Updated to reflect final version of the + ARB_texture_compression extension. Allowed + block-aligned TexSubImage calls. + + 0.3, 03/07/00 prbrown1: Resolved issues pertaining to the format of RGB + blocks in the DXT3 and DXT5 formats (they don't + ever use the "transparent" encoding). Fixed + decoding of DXT1 blocks. Pointed out issue of + "transparent" texels in DXT1 encodings having + different behaviors for RGB and RGBA internal + formats. + + 0.2, 02/23/00 prbrown1: Minor revisions; added several issues. + + 0.11, 02/17/00 prbrown1: Slight modification to error semantics + (INVALID_ENUM instead of INVALID_OPERATION). + + 0.1, 02/15/00 prbrown1: Initial revision. diff --git a/Engine/lib/tinyxml/changes.txt b/Engine/lib/tinyxml/changes.txt new file mode 100644 index 000000000..4075fd620 --- /dev/null +++ b/Engine/lib/tinyxml/changes.txt @@ -0,0 +1,269 @@ +Changes in version 1.0.1: +- Fixed comment tags which were outputing as ' include. Thanks + to Steve Lhomme for that. + +Changes in version 2.0.0 BETA +- Made the ToXXX() casts safe if 'this' is null. + When "LoadFile" is called with a filename, the value will correctly get set. + Thanks to Brian Yoder. +- Fixed bug where isalpha() and isalnum() would get called with a negative value for + high ascii numbers. Thanks to Alesky Aksenov. +- Fixed some errors codes that were not getting set. +- Made methods "const" that were not. +- Added a switch to enable or disable the ignoring of white space. ( TiXmlDocument::SetIgnoreWhiteSpace() ) +- Greater standardization and code re-use in the parser. +- Added a stream out operator. +- Added a stream in operator. +- Entity support, of predefined entites. &#x entities are untouched by input or output. +- Improved text out formatting. +- Fixed ReplaceChild bug, thanks to Tao Chen. + +Changes in version 2.0.1 +- Fixed hanging on loading a 0 length file. Thanks to Jeff Scozzafava. +- Fixed crashing on InsertBeforeChild and InsertAfterChild. Also possibility of bad links being + created by same function. Thanks to Frank De prins. +- Added missing licence text. Thanks to Lars Willemsens. +- Added include, at the suggestion of Steve Walters. + +Changes in version 2.1.0 +- Yves Berquin brings us the STL switch. The forum on SourceForge, and various emails to + me, have long debated all out STL vs. no STL at all. And now you can have it both ways. + TinyXml will compile either way. + +Changes in version 2.1.1 +- Compilation warnings. + +Changes in version 2.1.2 +- Uneeded code is not compiled in the STL case. +- Changed headers so that STL can be turned on or off in tinyxml.h + +Changes in version 2.1.3 +- Fixed non-const reference in API; now uses a pointer. +- Copy constructor of TiXmlString not checking for assignment to self. +- Nimrod Cohen found a truly evil bug in the STL implementation that occurs + when a string is converted to a c_str and then assigned to self. Search for + STL_STRING_BUG for a full description. I'm asserting this is a Microsoft STL + bug, since &string and string.c_str() should never be the same. Nevertheless, + the code works around it. +- Urivan Saaib pointed out a compiler conflict, where the C headers define + the isblank macro, which was wiping out the TiXmlString::isblank() method. + The method was unused and has been removed. + +Changes in version 2.1.4 +- Reworked the entity code. Entities were not correctly surving round trip input and output. + Will now automatically create entities for high ascii in output. + +Changes in version 2.1.5 +- Bug fix by kylotan : infinite loop on some input (tinyxmlparser.cpp rev 1.27) +- Contributed by Ivica Aracic (bytelord) : 1 new VC++ project to compile versions as static libraries (tinyxml_lib.dsp), + and an example usage in xmltest.dsp + (Patch request ID 678605) +- A suggestion by Ronald Fenner Jr (dormlock) to add #include and for Apple's Project Builder + (Patch request ID 697642) +- A patch from ohommes that allows to parse correctly dots in element names and attribute names + (Patch request 602600 and kylotan 701728) +- A patch from hermitgeek ( James ) and wasteland for improper error reporting +- Reviewed by Lee, with the following changes: + - Got sick of fighting the STL/non-STL thing in the windows build. Broke + them out as seperate projects. + - I have too long not included the dsw. Added. + - TinyXmlText had a protected Print. Odd. + - Made LinkEndChild public, with docs and appropriate warnings. + - Updated the docs. + +2.2.0 +- Fixed an uninitialized pointer in the TiXmlAttributes +- Fixed STL compilation problem in MinGW (and gcc 3?) - thanks Brian Yoder for finding this one +- Fixed a syntax error in TiXmlDeclaration - thanks Brian Yoder +- Fletcher Dunn proposed and submitted new error handling that tracked the row and column. Lee + modified it to not have performance impact. +- General cleanup suggestions from Fletcher Dunn. +- In error handling, general errors will no longer clear the error state of specific ones. +- Fix error in documentation : comments starting with ">) has now + been fixed. + +2.5.2 +- Lieven, and others, pointed out a missing const-cast that upset the Open Watcom compiler. + Should now be fixed. +- ErrorRow and ErrorCol should have been const, and weren't. Fixed thanks to Dmitry Polutov. + +2.5.3 +- zloe_zlo identified a missing string specialization for QueryValueAttribute() [ 1695429 ]. Worked + on this bug, but not sure how to fix it in a safe, cross-compiler way. +- increased warning level to 4 and turned on detect 64 bit portability issues for VC2005. + May address [ 1677737 ] VS2005: /Wp64 warnings +- grosheck identified several problems with the Document copy. Many thanks for [ 1660367 ] +- Nice catch, and suggested fix, be Gilad Novik on the Printer dropping entities. + "[ 1600650 ] Bug when printing xml text" is now fixed. +- A subtle fix from Nicos Gollan in the tinystring initializer: + [ 1581449 ] Fix initialiser of TiXmlString::nullrep_ +- Great catch, although there isn't a submitter for the bug. [ 1475201 ] TinyXML parses entities in comments. + Comments should not, in fact, parse entities. Fixed the code path and added tests. +- We were not catching all the returns from ftell. Thanks to Bernard for catching that. + diff --git a/Engine/lib/tinyxml/docs/annotated.html b/Engine/lib/tinyxml/docs/annotated.html new file mode 100644 index 000000000..795347834 --- /dev/null +++ b/Engine/lib/tinyxml/docs/annotated.html @@ -0,0 +1,39 @@ + + +TinyXml: Class List + + + + + + +

      TinyXml Class List

      Here are the classes, structs, unions and interfaces with brief descriptions: + + + + + + + + + + + + +
      TiXmlAttributeAn attribute is a name-value pair
      TiXmlBaseTiXmlBase is a base class for every class in TinyXml
      TiXmlCommentAn XML comment
      TiXmlDeclarationIn correct XML the declaration is the first entry in the file
      TiXmlDocumentAlways the top level node
      TiXmlElementThe element is a container class
      TiXmlHandleA TiXmlHandle is a class that wraps a node pointer with null checks; this is an incredibly useful thing
      TiXmlNodeThe parent class for everything in the Document Object Model
      TiXmlPrinterPrint to memory functionality
      TiXmlTextXML text
      TiXmlUnknownAny tag that tinyXml doesn't recognize is saved as an unknown
      TiXmlVisitorIf you call the Accept() method, it requires being passed a TiXmlVisitor class to handle callbacks
      +
      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/classTiXmlAttribute-members.html b/Engine/lib/tinyxml/docs/classTiXmlAttribute-members.html new file mode 100644 index 000000000..b3c0fc67b --- /dev/null +++ b/Engine/lib/tinyxml/docs/classTiXmlAttribute-members.html @@ -0,0 +1,54 @@ + + +TinyXml: Member List + + + + + + +

      TiXmlAttribute Member List

      This is the complete list of members for TiXmlAttribute, including all inherited members.

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      Column() const TiXmlBase [inline]
      DoubleValue() const TiXmlAttribute
      EncodeString(const TIXML_STRING &str, TIXML_STRING *out)TiXmlBase [static]
      GetUserData()TiXmlBase [inline]
      GetUserData() const TiXmlBase [inline]
      IntValue() const TiXmlAttribute
      IsWhiteSpaceCondensed()TiXmlBase [inline, static]
      Name() const TiXmlAttribute [inline]
      Next() const TiXmlAttribute
      Previous() const TiXmlAttribute
      Print(FILE *cfile, int depth) const TiXmlAttribute [inline, virtual]
      QueryDoubleValue(double *_value) const TiXmlAttribute
      QueryIntValue(int *_value) const TiXmlAttribute
      Row() const TiXmlBase [inline]
      SetCondenseWhiteSpace(bool condense)TiXmlBase [inline, static]
      SetDoubleValue(double _value)TiXmlAttribute
      SetIntValue(int _value)TiXmlAttribute
      SetName(const char *_name)TiXmlAttribute [inline]
      SetName(const std::string &_name)TiXmlAttribute [inline]
      SetUserData(void *user)TiXmlBase [inline]
      SetValue(const char *_value)TiXmlAttribute [inline]
      SetValue(const std::string &_value)TiXmlAttribute [inline]
      TiXmlAttribute()TiXmlAttribute [inline]
      TiXmlAttribute(const std::string &_name, const std::string &_value)TiXmlAttribute [inline]
      TiXmlAttribute(const char *_name, const char *_value)TiXmlAttribute [inline]
      userDataTiXmlBase [protected]
      Value() const TiXmlAttribute [inline]
      ValueStr() const TiXmlAttribute [inline]


      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/classTiXmlAttribute.html b/Engine/lib/tinyxml/docs/classTiXmlAttribute.html new file mode 100644 index 000000000..9cdc9a9ba --- /dev/null +++ b/Engine/lib/tinyxml/docs/classTiXmlAttribute.html @@ -0,0 +1,181 @@ + + +TinyXml: TiXmlAttribute Class Reference + + + + + + +

      TiXmlAttribute Class Reference

      An attribute is a name-value pair. +More... +

      +#include <tinyxml.h> +

      +

      Inheritance diagram for TiXmlAttribute: +

      + +TiXmlBase + +List of all members. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      Public Member Functions

      TiXmlAttribute ()
       Construct an empty attribute.
      TiXmlAttribute (const std::string &_name, const std::string &_value)
       std::string constructor.
      TiXmlAttribute (const char *_name, const char *_value)
       Construct an attribute with a name and value.
      +const char * Name () const
       Return the name of this attribute.
      +const char * Value () const
       Return the value of this attribute.
      +const std::string & ValueStr () const
       Return the value of this attribute.
      +int IntValue () const
       Return the value of this attribute, converted to an integer.
      +double DoubleValue () const
       Return the value of this attribute, converted to a double.
      int QueryIntValue (int *_value) const
       QueryIntValue examines the value string.
      +int QueryDoubleValue (double *_value) const
       QueryDoubleValue examines the value string. See QueryIntValue().
      +void SetName (const char *_name)
       Set the name of this attribute.
      +void SetValue (const char *_value)
       Set the value.
      +void SetIntValue (int _value)
       Set the value from an integer.
      +void SetDoubleValue (double _value)
       Set the value from a double.
      +void SetName (const std::string &_name)
       STL std::string form.
      +void SetValue (const std::string &_value)
       STL std::string form.
      +const TiXmlAttributeNext () const
       Get the next sibling attribute in the DOM. Returns null at end.
      +const TiXmlAttributePrevious () const
       Get the previous sibling attribute in the DOM. Returns null at beginning.
      virtual void Print (FILE *cfile, int depth) const
       All TinyXml classes can print themselves to a filestream or the string class (TiXmlString in non-STL mode, std::string in STL mode.
      +

      Detailed Description

      +An attribute is a name-value pair. +

      +Elements have an arbitrary number of attributes, each with a unique name.

      +

      Note:
      The attributes are not TiXmlNodes, since they are not part of the tinyXML document object model. There are other suggested ways to look at this problem.
      + +

      +


      Member Function Documentation

      + +
      +
      + + + + + + + + + + + + + + + + + + +
      virtual void TiXmlAttribute::Print (FILE *  cfile,
      int  depth 
      ) const [inline, virtual]
      +
      +
      + +

      +All TinyXml classes can print themselves to a filestream or the string class (TiXmlString in non-STL mode, std::string in STL mode. +

      +) Either or both cfile and str can be null.

      +This is a formatted print, and will insert tabs and newlines.

      +(For an unformatted stream, use the << operator.) +

      +Implements TiXmlBase. +

      +

      + +

      +
      + + + + + + + + + +
      int TiXmlAttribute::QueryIntValue (int *  _value  )  const
      +
      +
      + +

      +QueryIntValue examines the value string. +

      +It is an alternative to the IntValue() method with richer error checking. If the value is an integer, it is stored in 'value' and the call returns TIXML_SUCCESS. If it is not an integer, it returns TIXML_WRONG_TYPE.

      +A specialized but useful call. Note that for success it returns 0, which is the opposite of almost all other TinyXml calls. +

      +

      +


      The documentation for this class was generated from the following file: +
      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/classTiXmlAttribute.png b/Engine/lib/tinyxml/docs/classTiXmlAttribute.png new file mode 100644 index 000000000..ebac5ca9f Binary files /dev/null and b/Engine/lib/tinyxml/docs/classTiXmlAttribute.png differ diff --git a/Engine/lib/tinyxml/docs/classTiXmlBase-members.html b/Engine/lib/tinyxml/docs/classTiXmlBase-members.html new file mode 100644 index 000000000..352900e8c --- /dev/null +++ b/Engine/lib/tinyxml/docs/classTiXmlBase-members.html @@ -0,0 +1,36 @@ + + +TinyXml: Member List + + + + + + +

      TiXmlBase Member List

      This is the complete list of members for TiXmlBase, including all inherited members.

      + + + + + + + + + + +
      Column() const TiXmlBase [inline]
      EncodeString(const TIXML_STRING &str, TIXML_STRING *out)TiXmlBase [static]
      GetUserData()TiXmlBase [inline]
      GetUserData() const TiXmlBase [inline]
      IsWhiteSpaceCondensed()TiXmlBase [inline, static]
      Print(FILE *cfile, int depth) const =0TiXmlBase [pure virtual]
      Row() const TiXmlBase [inline]
      SetCondenseWhiteSpace(bool condense)TiXmlBase [inline, static]
      SetUserData(void *user)TiXmlBase [inline]
      userDataTiXmlBase [protected]


      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/classTiXmlBase.html b/Engine/lib/tinyxml/docs/classTiXmlBase.html new file mode 100644 index 000000000..bdb638931 --- /dev/null +++ b/Engine/lib/tinyxml/docs/classTiXmlBase.html @@ -0,0 +1,230 @@ + + +TinyXml: TiXmlBase Class Reference + + + + + + +

      TiXmlBase Class Reference

      TiXmlBase is a base class for every class in TinyXml. +More... +

      +#include <tinyxml.h> +

      +

      Inheritance diagram for TiXmlBase: +

      + +TiXmlAttribute +TiXmlNode +TiXmlComment +TiXmlDeclaration +TiXmlDocument +TiXmlElement +TiXmlText +TiXmlUnknown + +List of all members. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      Public Member Functions

      virtual void Print (FILE *cfile, int depth) const =0
       All TinyXml classes can print themselves to a filestream or the string class (TiXmlString in non-STL mode, std::string in STL mode.
      int Row () const
       Return the position, in the original source file, of this node or attribute.
      +int Column () const
       See Row().
      +void SetUserData (void *user)
       Set a pointer to arbitrary user data.
      +void * GetUserData ()
       Get a pointer to arbitrary user data.
      +const void * GetUserData () const
       Get a pointer to arbitrary user data.

      Static Public Member Functions

      static void SetCondenseWhiteSpace (bool condense)
       The world does not agree on whether white space should be kept or not.
      +static bool IsWhiteSpaceCondensed ()
       Return the current white space setting.
      static void EncodeString (const TIXML_STRING &str, TIXML_STRING *out)
       Expands entities in a string.

      Protected Attributes

      +void * userData
       Field containing a generic user pointer.

      Friends

      +class TiXmlNode
      +class TiXmlElement
      +class TiXmlDocument
      +

      Detailed Description

      +TiXmlBase is a base class for every class in TinyXml. +

      +It does little except to establish that TinyXml classes can be printed and provide some utility functions.

      +In XML, the document and elements can contain other elements and other types of nodes.

      +

      	A Document can contain:	Element	(container or leaf)
      +							Comment (leaf)
      +							Unknown (leaf)
      +							Declaration( leaf )
      +
      +	An Element can contain:	Element (container or leaf)
      +							Text	(leaf)
      +							Attributes (not on tree)
      +							Comment (leaf)
      +							Unknown (leaf)
      +
      +	A Decleration contains: Attributes (not on tree)
      +	
      +

      +


      Member Function Documentation

      + +
      +
      + + + + + + + + + + + + + + + + + + +
      static void TiXmlBase::EncodeString (const TIXML_STRING &  str,
      TIXML_STRING *  out 
      ) [static]
      +
      +
      + +

      +Expands entities in a string. +

      +Note this should not contian the tag's '<', '>', etc, or they will be transformed into entities! +

      +

      + +

      +
      + + + + + + + + + + + + + + + + + + +
      virtual void TiXmlBase::Print (FILE *  cfile,
      int  depth 
      ) const [pure virtual]
      +
      +
      + +

      +All TinyXml classes can print themselves to a filestream or the string class (TiXmlString in non-STL mode, std::string in STL mode. +

      +) Either or both cfile and str can be null.

      +This is a formatted print, and will insert tabs and newlines.

      +(For an unformatted stream, use the << operator.) +

      +Implemented in TiXmlAttribute, TiXmlElement, TiXmlComment, TiXmlText, TiXmlDeclaration, TiXmlUnknown, and TiXmlDocument. +

      +

      + +

      +
      + + + + + + + + +
      int TiXmlBase::Row (  )  const [inline]
      +
      +
      + +

      +Return the position, in the original source file, of this node or attribute. +

      +The row and column are 1-based. (That is the first row and first column is 1,1). If the returns values are 0 or less, then the parser does not have a row and column value.

      +Generally, the row and column value will be set when the TiXmlDocument::Load(), TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set when the DOM was created from operator>>.

      +The values reflect the initial load. Once the DOM is modified programmatically (by adding or changing nodes and attributes) the new values will NOT update to reflect changes in the document.

      +There is a minor performance cost to computing the row and column. Computation can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value.

      +

      See also:
      TiXmlDocument::SetTabSize()
      + +
      +

      + +

      +
      + + + + + + + + + +
      static void TiXmlBase::SetCondenseWhiteSpace (bool  condense  )  [inline, static]
      +
      +
      + +

      +The world does not agree on whether white space should be kept or not. +

      +In order to make everyone happy, these global, static functions are provided to set whether or not TinyXml will condense all white space into a single space or not. The default is to condense. Note changing this value is not thread safe. +

      +

      +


      The documentation for this class was generated from the following file: +
      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/classTiXmlBase.png b/Engine/lib/tinyxml/docs/classTiXmlBase.png new file mode 100644 index 000000000..085db6e50 Binary files /dev/null and b/Engine/lib/tinyxml/docs/classTiXmlBase.png differ diff --git a/Engine/lib/tinyxml/docs/classTiXmlComment-members.html b/Engine/lib/tinyxml/docs/classTiXmlComment-members.html new file mode 100644 index 000000000..9b2c4ec06 --- /dev/null +++ b/Engine/lib/tinyxml/docs/classTiXmlComment-members.html @@ -0,0 +1,100 @@ + + +TinyXml: Member List + + + + + + +

      TiXmlComment Member List

      This is the complete list of members for TiXmlComment, including all inherited members.

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      Accept(TiXmlVisitor *visitor) const TiXmlComment [virtual]
      Clear()TiXmlNode
      Clone() const TiXmlComment [virtual]
      Column() const TiXmlBase [inline]
      EncodeString(const TIXML_STRING &str, TIXML_STRING *out)TiXmlBase [static]
      FirstChild() const TiXmlNode [inline]
      FirstChild(const char *value) const TiXmlNode
      FirstChild(const char *_value)TiXmlNode [inline]
      FirstChild(const std::string &_value) const TiXmlNode [inline]
      FirstChild(const std::string &_value)TiXmlNode [inline]
      FirstChildElement() const TiXmlNode
      FirstChildElement(const char *_value) const TiXmlNode
      FirstChildElement(const std::string &_value) const TiXmlNode [inline]
      FirstChildElement(const std::string &_value)TiXmlNode [inline]
      GetDocument() const TiXmlNode
      GetUserData()TiXmlBase [inline]
      GetUserData() const TiXmlBase [inline]
      InsertAfterChild(TiXmlNode *afterThis, const TiXmlNode &addThis)TiXmlNode
      InsertBeforeChild(TiXmlNode *beforeThis, const TiXmlNode &addThis)TiXmlNode
      InsertEndChild(const TiXmlNode &addThis)TiXmlNode
      IsWhiteSpaceCondensed()TiXmlBase [inline, static]
      IterateChildren(const TiXmlNode *previous) const TiXmlNode
      IterateChildren(const char *value, const TiXmlNode *previous) const TiXmlNode
      IterateChildren(const std::string &_value, const TiXmlNode *previous) const TiXmlNode [inline]
      IterateChildren(const std::string &_value, const TiXmlNode *previous)TiXmlNode [inline]
      LastChild()TiXmlNode [inline]
      LastChild(const char *_value)TiXmlNode [inline]
      LastChild(const std::string &_value) const TiXmlNode [inline]
      LastChild(const std::string &_value)TiXmlNode [inline]
      LinkEndChild(TiXmlNode *addThis)TiXmlNode
      NextSibling(const std::string &_value) const TiXmlNode [inline]
      NextSibling(const std::string &_value)TiXmlNode [inline]
      NextSibling() const TiXmlNode [inline]
      NextSibling(const char *) const TiXmlNode
      NextSiblingElement() const TiXmlNode
      NextSiblingElement(const char *) const TiXmlNode
      NextSiblingElement(const std::string &_value) const TiXmlNode [inline]
      NextSiblingElement(const std::string &_value)TiXmlNode [inline]
      NoChildren() const TiXmlNode [inline]
      NodeType enum nameTiXmlNode
      operator<<(std::ostream &out, const TiXmlNode &base)TiXmlNode [friend]
      operator<<(std::string &out, const TiXmlNode &base)TiXmlNode [friend]
      operator>>(std::istream &in, TiXmlNode &base)TiXmlNode [friend]
      Parent()TiXmlNode [inline]
      PreviousSibling() const TiXmlNode [inline]
      PreviousSibling(const char *) const TiXmlNode
      PreviousSibling(const std::string &_value) const TiXmlNode [inline]
      PreviousSibling(const std::string &_value)TiXmlNode [inline]
      Print(FILE *cfile, int depth) const TiXmlComment [virtual]
      RemoveChild(TiXmlNode *removeThis)TiXmlNode
      ReplaceChild(TiXmlNode *replaceThis, const TiXmlNode &withThis)TiXmlNode
      Row() const TiXmlBase [inline]
      SetCondenseWhiteSpace(bool condense)TiXmlBase [inline, static]
      SetUserData(void *user)TiXmlBase [inline]
      SetValue(const char *_value)TiXmlNode [inline]
      SetValue(const std::string &_value)TiXmlNode [inline]
      TiXmlComment()TiXmlComment [inline]
      TiXmlComment(const char *_value)TiXmlComment [inline]
      ToComment() const TiXmlComment [inline, virtual]
      ToComment()TiXmlComment [inline, virtual]
      ToDeclaration() const TiXmlNode [inline, virtual]
      ToDeclaration()TiXmlNode [inline, virtual]
      ToDocument() const TiXmlNode [inline, virtual]
      ToDocument()TiXmlNode [inline, virtual]
      ToElement() const TiXmlNode [inline, virtual]
      ToElement()TiXmlNode [inline, virtual]
      ToText() const TiXmlNode [inline, virtual]
      ToText()TiXmlNode [inline, virtual]
      ToUnknown() const TiXmlNode [inline, virtual]
      ToUnknown()TiXmlNode [inline, virtual]
      Type() const TiXmlNode [inline]
      userDataTiXmlBase [protected]
      Value() const TiXmlNode [inline]
      ValueStr() const TiXmlNode [inline]


      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/classTiXmlComment.html b/Engine/lib/tinyxml/docs/classTiXmlComment.html new file mode 100644 index 000000000..65ea27097 --- /dev/null +++ b/Engine/lib/tinyxml/docs/classTiXmlComment.html @@ -0,0 +1,108 @@ + + +TinyXml: TiXmlComment Class Reference + + + + + + +

      TiXmlComment Class Reference

      An XML comment. +More... +

      +#include <tinyxml.h> +

      +

      Inheritance diagram for TiXmlComment: +

      + +TiXmlNode +TiXmlBase + +List of all members. + + + + + + + + + + + + + + + + + + + + + + + +

      Public Member Functions

      TiXmlComment ()
       Constructs an empty comment.
      TiXmlComment (const char *_value)
       Construct a comment from text.
      +virtual TiXmlNodeClone () const
       Returns a copy of this Comment.
      virtual void Print (FILE *cfile, int depth) const
       All TinyXml classes can print themselves to a filestream or the string class (TiXmlString in non-STL mode, std::string in STL mode.
      +virtual const TiXmlCommentToComment () const
       Cast to a more defined type. Will return null not of the requested type.
      +virtual TiXmlCommentToComment ()
       Cast to a more defined type. Will return null not of the requested type.
      +virtual bool Accept (TiXmlVisitor *visitor) const
       Walk the XML tree visiting this node and all of its children.
      +

      Detailed Description

      +An XML comment. +

      +


      Member Function Documentation

      + +
      +
      + + + + + + + + + + + + + + + + + + +
      virtual void TiXmlComment::Print (FILE *  cfile,
      int  depth 
      ) const [virtual]
      +
      +
      + +

      +All TinyXml classes can print themselves to a filestream or the string class (TiXmlString in non-STL mode, std::string in STL mode. +

      +) Either or both cfile and str can be null.

      +This is a formatted print, and will insert tabs and newlines.

      +(For an unformatted stream, use the << operator.) +

      +Implements TiXmlBase. +

      +

      +


      The documentation for this class was generated from the following file: +
      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/classTiXmlComment.png b/Engine/lib/tinyxml/docs/classTiXmlComment.png new file mode 100644 index 000000000..e33d74258 Binary files /dev/null and b/Engine/lib/tinyxml/docs/classTiXmlComment.png differ diff --git a/Engine/lib/tinyxml/docs/classTiXmlDeclaration-members.html b/Engine/lib/tinyxml/docs/classTiXmlDeclaration-members.html new file mode 100644 index 000000000..147bb9661 --- /dev/null +++ b/Engine/lib/tinyxml/docs/classTiXmlDeclaration-members.html @@ -0,0 +1,104 @@ + + +TinyXml: Member List + + + + + + +

      TiXmlDeclaration Member List

      This is the complete list of members for TiXmlDeclaration, including all inherited members.

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      Accept(TiXmlVisitor *visitor) const TiXmlDeclaration [virtual]
      Clear()TiXmlNode
      Clone() const TiXmlDeclaration [virtual]
      Column() const TiXmlBase [inline]
      EncodeString(const TIXML_STRING &str, TIXML_STRING *out)TiXmlBase [static]
      Encoding() const TiXmlDeclaration [inline]
      FirstChild() const TiXmlNode [inline]
      FirstChild(const char *value) const TiXmlNode
      FirstChild(const char *_value)TiXmlNode [inline]
      FirstChild(const std::string &_value) const TiXmlNode [inline]
      FirstChild(const std::string &_value)TiXmlNode [inline]
      FirstChildElement() const TiXmlNode
      FirstChildElement(const char *_value) const TiXmlNode
      FirstChildElement(const std::string &_value) const TiXmlNode [inline]
      FirstChildElement(const std::string &_value)TiXmlNode [inline]
      GetDocument() const TiXmlNode
      GetUserData()TiXmlBase [inline]
      GetUserData() const TiXmlBase [inline]
      InsertAfterChild(TiXmlNode *afterThis, const TiXmlNode &addThis)TiXmlNode
      InsertBeforeChild(TiXmlNode *beforeThis, const TiXmlNode &addThis)TiXmlNode
      InsertEndChild(const TiXmlNode &addThis)TiXmlNode
      IsWhiteSpaceCondensed()TiXmlBase [inline, static]
      IterateChildren(const TiXmlNode *previous) const TiXmlNode
      IterateChildren(const char *value, const TiXmlNode *previous) const TiXmlNode
      IterateChildren(const std::string &_value, const TiXmlNode *previous) const TiXmlNode [inline]
      IterateChildren(const std::string &_value, const TiXmlNode *previous)TiXmlNode [inline]
      LastChild()TiXmlNode [inline]
      LastChild(const char *_value)TiXmlNode [inline]
      LastChild(const std::string &_value) const TiXmlNode [inline]
      LastChild(const std::string &_value)TiXmlNode [inline]
      LinkEndChild(TiXmlNode *addThis)TiXmlNode
      NextSibling(const std::string &_value) const TiXmlNode [inline]
      NextSibling(const std::string &_value)TiXmlNode [inline]
      NextSibling() const TiXmlNode [inline]
      NextSibling(const char *) const TiXmlNode
      NextSiblingElement() const TiXmlNode
      NextSiblingElement(const char *) const TiXmlNode
      NextSiblingElement(const std::string &_value) const TiXmlNode [inline]
      NextSiblingElement(const std::string &_value)TiXmlNode [inline]
      NoChildren() const TiXmlNode [inline]
      NodeType enum nameTiXmlNode
      operator<<(std::ostream &out, const TiXmlNode &base)TiXmlNode [friend]
      operator<<(std::string &out, const TiXmlNode &base)TiXmlNode [friend]
      operator>>(std::istream &in, TiXmlNode &base)TiXmlNode [friend]
      Parent()TiXmlNode [inline]
      PreviousSibling() const TiXmlNode [inline]
      PreviousSibling(const char *) const TiXmlNode
      PreviousSibling(const std::string &_value) const TiXmlNode [inline]
      PreviousSibling(const std::string &_value)TiXmlNode [inline]
      Print(FILE *cfile, int depth) const TiXmlDeclaration [inline, virtual]
      RemoveChild(TiXmlNode *removeThis)TiXmlNode
      ReplaceChild(TiXmlNode *replaceThis, const TiXmlNode &withThis)TiXmlNode
      Row() const TiXmlBase [inline]
      SetCondenseWhiteSpace(bool condense)TiXmlBase [inline, static]
      SetUserData(void *user)TiXmlBase [inline]
      SetValue(const char *_value)TiXmlNode [inline]
      SetValue(const std::string &_value)TiXmlNode [inline]
      Standalone() const TiXmlDeclaration [inline]
      TiXmlDeclaration()TiXmlDeclaration [inline]
      TiXmlDeclaration(const std::string &_version, const std::string &_encoding, const std::string &_standalone)TiXmlDeclaration
      TiXmlDeclaration(const char *_version, const char *_encoding, const char *_standalone)TiXmlDeclaration
      ToComment() const TiXmlNode [inline, virtual]
      ToComment()TiXmlNode [inline, virtual]
      ToDeclaration() const TiXmlDeclaration [inline, virtual]
      ToDeclaration()TiXmlDeclaration [inline, virtual]
      ToDocument() const TiXmlNode [inline, virtual]
      ToDocument()TiXmlNode [inline, virtual]
      ToElement() const TiXmlNode [inline, virtual]
      ToElement()TiXmlNode [inline, virtual]
      ToText() const TiXmlNode [inline, virtual]
      ToText()TiXmlNode [inline, virtual]
      ToUnknown() const TiXmlNode [inline, virtual]
      ToUnknown()TiXmlNode [inline, virtual]
      Type() const TiXmlNode [inline]
      userDataTiXmlBase [protected]
      Value() const TiXmlNode [inline]
      ValueStr() const TiXmlNode [inline]
      Version() const TiXmlDeclaration [inline]


      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/classTiXmlDeclaration.html b/Engine/lib/tinyxml/docs/classTiXmlDeclaration.html new file mode 100644 index 000000000..5ae9a0f3e --- /dev/null +++ b/Engine/lib/tinyxml/docs/classTiXmlDeclaration.html @@ -0,0 +1,129 @@ + + +TinyXml: TiXmlDeclaration Class Reference + + + + + + +

      TiXmlDeclaration Class Reference

      In correct XML the declaration is the first entry in the file. +More... +

      +#include <tinyxml.h> +

      +

      Inheritance diagram for TiXmlDeclaration: +

      + +TiXmlNode +TiXmlBase + +List of all members. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      Public Member Functions

      TiXmlDeclaration ()
       Construct an empty declaration.
      TiXmlDeclaration (const std::string &_version, const std::string &_encoding, const std::string &_standalone)
       Constructor.
      TiXmlDeclaration (const char *_version, const char *_encoding, const char *_standalone)
       Construct.
      +const char * Version () const
       Version. Will return an empty string if none was found.
      +const char * Encoding () const
       Encoding. Will return an empty string if none was found.
      +const char * Standalone () const
       Is this a standalone document?
      +virtual TiXmlNodeClone () const
       Creates a copy of this Declaration and returns it.
      virtual void Print (FILE *cfile, int depth) const
       All TinyXml classes can print themselves to a filestream or the string class (TiXmlString in non-STL mode, std::string in STL mode.
      +virtual const TiXmlDeclarationToDeclaration () const
       Cast to a more defined type. Will return null not of the requested type.
      +virtual TiXmlDeclarationToDeclaration ()
       Cast to a more defined type. Will return null not of the requested type.
      +virtual bool Accept (TiXmlVisitor *visitor) const
       Walk the XML tree visiting this node and all of its children.
      +

      Detailed Description

      +In correct XML the declaration is the first entry in the file. +

      +

      		<?xml version="1.0" standalone="yes"?>
      +	

      +TinyXml will happily read or write files without a declaration, however. There are 3 possible attributes to the declaration: version, encoding, and standalone.

      +Note: In this version of the code, the attributes are handled as special cases, not generic attributes, simply because there can only be at most 3 and they are always the same. +

      +


      Member Function Documentation

      + +
      +
      + + + + + + + + + + + + + + + + + + +
      virtual void TiXmlDeclaration::Print (FILE *  cfile,
      int  depth 
      ) const [inline, virtual]
      +
      +
      + +

      +All TinyXml classes can print themselves to a filestream or the string class (TiXmlString in non-STL mode, std::string in STL mode. +

      +) Either or both cfile and str can be null.

      +This is a formatted print, and will insert tabs and newlines.

      +(For an unformatted stream, use the << operator.) +

      +Implements TiXmlBase. +

      +

      +


      The documentation for this class was generated from the following file: +
      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/classTiXmlDeclaration.png b/Engine/lib/tinyxml/docs/classTiXmlDeclaration.png new file mode 100644 index 000000000..c10912b70 Binary files /dev/null and b/Engine/lib/tinyxml/docs/classTiXmlDeclaration.png differ diff --git a/Engine/lib/tinyxml/docs/classTiXmlDocument-members.html b/Engine/lib/tinyxml/docs/classTiXmlDocument-members.html new file mode 100644 index 000000000..df6132a03 --- /dev/null +++ b/Engine/lib/tinyxml/docs/classTiXmlDocument-members.html @@ -0,0 +1,119 @@ + + +TinyXml: Member List + + + + + + +

      TiXmlDocument Member List

      This is the complete list of members for TiXmlDocument, including all inherited members.

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      Accept(TiXmlVisitor *content) const TiXmlDocument [virtual]
      Clear()TiXmlNode
      ClearError()TiXmlDocument [inline]
      Clone() const TiXmlDocument [protected, virtual]
      Column() const TiXmlBase [inline]
      EncodeString(const TIXML_STRING &str, TIXML_STRING *out)TiXmlBase [static]
      Error() const TiXmlDocument [inline]
      ErrorCol() const TiXmlDocument [inline]
      ErrorDesc() const TiXmlDocument [inline]
      ErrorId() const TiXmlDocument [inline]
      ErrorRow() const TiXmlDocument [inline]
      FirstChild() const TiXmlNode [inline]
      FirstChild(const char *value) const TiXmlNode
      FirstChild(const char *_value)TiXmlNode [inline]
      FirstChild(const std::string &_value) const TiXmlNode [inline]
      FirstChild(const std::string &_value)TiXmlNode [inline]
      FirstChildElement() const TiXmlNode
      FirstChildElement(const char *_value) const TiXmlNode
      FirstChildElement(const std::string &_value) const TiXmlNode [inline]
      FirstChildElement(const std::string &_value)TiXmlNode [inline]
      GetDocument() const TiXmlNode
      GetUserData()TiXmlBase [inline]
      GetUserData() const TiXmlBase [inline]
      InsertAfterChild(TiXmlNode *afterThis, const TiXmlNode &addThis)TiXmlNode
      InsertBeforeChild(TiXmlNode *beforeThis, const TiXmlNode &addThis)TiXmlNode
      InsertEndChild(const TiXmlNode &addThis)TiXmlNode
      IsWhiteSpaceCondensed()TiXmlBase [inline, static]
      IterateChildren(const TiXmlNode *previous) const TiXmlNode
      IterateChildren(const char *value, const TiXmlNode *previous) const TiXmlNode
      IterateChildren(const std::string &_value, const TiXmlNode *previous) const TiXmlNode [inline]
      IterateChildren(const std::string &_value, const TiXmlNode *previous)TiXmlNode [inline]
      LastChild()TiXmlNode [inline]
      LastChild(const char *_value)TiXmlNode [inline]
      LastChild(const std::string &_value) const TiXmlNode [inline]
      LastChild(const std::string &_value)TiXmlNode [inline]
      LinkEndChild(TiXmlNode *addThis)TiXmlNode
      LoadFile(TiXmlEncoding encoding=TIXML_DEFAULT_ENCODING)TiXmlDocument
      LoadFile(const char *filename, TiXmlEncoding encoding=TIXML_DEFAULT_ENCODING)TiXmlDocument
      LoadFile(FILE *, TiXmlEncoding encoding=TIXML_DEFAULT_ENCODING)TiXmlDocument
      LoadFile(const std::string &filename, TiXmlEncoding encoding=TIXML_DEFAULT_ENCODING)TiXmlDocument [inline]
      NextSibling(const std::string &_value) const TiXmlNode [inline]
      NextSibling(const std::string &_value)TiXmlNode [inline]
      NextSibling() const TiXmlNode [inline]
      NextSibling(const char *) const TiXmlNode
      NextSiblingElement() const TiXmlNode
      NextSiblingElement(const char *) const TiXmlNode
      NextSiblingElement(const std::string &_value) const TiXmlNode [inline]
      NextSiblingElement(const std::string &_value)TiXmlNode [inline]
      NoChildren() const TiXmlNode [inline]
      NodeType enum nameTiXmlNode
      operator<<(std::ostream &out, const TiXmlNode &base)TiXmlNode [friend]
      operator<<(std::string &out, const TiXmlNode &base)TiXmlNode [friend]
      operator>>(std::istream &in, TiXmlNode &base)TiXmlNode [friend]
      Parent()TiXmlNode [inline]
      Parse(const char *p, TiXmlParsingData *data=0, TiXmlEncoding encoding=TIXML_DEFAULT_ENCODING)TiXmlDocument [virtual]
      PreviousSibling() const TiXmlNode [inline]
      PreviousSibling(const char *) const TiXmlNode
      PreviousSibling(const std::string &_value) const TiXmlNode [inline]
      PreviousSibling(const std::string &_value)TiXmlNode [inline]
      Print() const TiXmlDocument [inline]
      Print(FILE *cfile, int depth=0) const TiXmlDocument [virtual]
      RemoveChild(TiXmlNode *removeThis)TiXmlNode
      ReplaceChild(TiXmlNode *replaceThis, const TiXmlNode &withThis)TiXmlNode
      RootElement() const TiXmlDocument [inline]
      Row() const TiXmlBase [inline]
      SaveFile() const TiXmlDocument
      SaveFile(const char *filename) const TiXmlDocument
      SaveFile(FILE *) const TiXmlDocument
      SaveFile(const std::string &filename) const TiXmlDocument [inline]
      SetCondenseWhiteSpace(bool condense)TiXmlBase [inline, static]
      SetTabSize(int _tabsize)TiXmlDocument [inline]
      SetUserData(void *user)TiXmlBase [inline]
      SetValue(const char *_value)TiXmlNode [inline]
      SetValue(const std::string &_value)TiXmlNode [inline]
      TiXmlDocument()TiXmlDocument
      TiXmlDocument(const char *documentName)TiXmlDocument
      TiXmlDocument(const std::string &documentName)TiXmlDocument
      ToComment() const TiXmlNode [inline, virtual]
      ToComment()TiXmlNode [inline, virtual]
      ToDeclaration() const TiXmlNode [inline, virtual]
      ToDeclaration()TiXmlNode [inline, virtual]
      ToDocument() const TiXmlDocument [inline, virtual]
      ToDocument()TiXmlDocument [inline, virtual]
      ToElement() const TiXmlNode [inline, virtual]
      ToElement()TiXmlNode [inline, virtual]
      ToText() const TiXmlNode [inline, virtual]
      ToText()TiXmlNode [inline, virtual]
      ToUnknown() const TiXmlNode [inline, virtual]
      ToUnknown()TiXmlNode [inline, virtual]
      Type() const TiXmlNode [inline]
      userDataTiXmlBase [protected]
      Value() const TiXmlNode [inline]
      ValueStr() const TiXmlNode [inline]


      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/classTiXmlDocument.html b/Engine/lib/tinyxml/docs/classTiXmlDocument.html new file mode 100644 index 000000000..9779d3cc6 --- /dev/null +++ b/Engine/lib/tinyxml/docs/classTiXmlDocument.html @@ -0,0 +1,430 @@ + + +TinyXml: TiXmlDocument Class Reference + + + + + + +

      TiXmlDocument Class Reference

      Always the top level node. +More... +

      +#include <tinyxml.h> +

      +

      Inheritance diagram for TiXmlDocument: +

      + +TiXmlNode +TiXmlBase + +List of all members. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      Public Member Functions

      TiXmlDocument ()
       Create an empty document, that has no name.
      TiXmlDocument (const char *documentName)
       Create a document with a name. The name of the document is also the filename of the xml.
      TiXmlDocument (const std::string &documentName)
       Constructor.
      bool LoadFile (TiXmlEncoding encoding=TIXML_DEFAULT_ENCODING)
       Load a file using the current document value.
      +bool SaveFile () const
       Save a file using the current document value. Returns true if successful.
      +bool LoadFile (const char *filename, TiXmlEncoding encoding=TIXML_DEFAULT_ENCODING)
       Load a file using the given filename. Returns true if successful.
      +bool SaveFile (const char *filename) const
       Save a file using the given filename. Returns true if successful.
      bool LoadFile (FILE *, TiXmlEncoding encoding=TIXML_DEFAULT_ENCODING)
       Load a file using the given FILE*.
      +bool SaveFile (FILE *) const
       Save a file using the given FILE*. Returns true if successful.
      bool LoadFile (const std::string &filename, TiXmlEncoding encoding=TIXML_DEFAULT_ENCODING)
      +bool SaveFile (const std::string &filename) const
       < STL std::string version.
      virtual const char * Parse (const char *p, TiXmlParsingData *data=0, TiXmlEncoding encoding=TIXML_DEFAULT_ENCODING)
       Parse the given null terminated block of xml data.
      const TiXmlElementRootElement () const
       Get the root element -- the only top level element -- of the document.
      bool Error () const
       If an error occurs, Error will be set to true.
      +const char * ErrorDesc () const
       Contains a textual (english) description of the error if one occurs.
      int ErrorId () const
       Generally, you probably want the error string ( ErrorDesc() ).
      int ErrorRow () const
       Returns the location (if known) of the error.
      +int ErrorCol () const
       The column where the error occured. See ErrorRow().
      void SetTabSize (int _tabsize)
       SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol()) to report the correct values for row and column.
      void ClearError ()
       If you have handled the error, it can be reset with this call.
      +void Print () const
       Write the document to standard out using formatted printing ("pretty print").
      +virtual void Print (FILE *cfile, int depth=0) const
       Print this Document to a FILE stream.
      +virtual const TiXmlDocumentToDocument () const
       Cast to a more defined type. Will return null not of the requested type.
      +virtual TiXmlDocumentToDocument ()
       Cast to a more defined type. Will return null not of the requested type.
      +virtual bool Accept (TiXmlVisitor *content) const
       Walk the XML tree visiting this node and all of its children.

      Protected Member Functions

      virtual TiXmlNodeClone () const
       Create an exact duplicate of this node and return it.
      +

      Detailed Description

      +Always the top level node. +

      +A document binds together all the XML pieces. It can be saved, loaded, and printed to the screen. The 'value' of a document node is the xml file name. +

      +


      Member Function Documentation

      + +
      +
      + + + + + + + + +
      void TiXmlDocument::ClearError (  )  [inline]
      +
      +
      + +

      +If you have handled the error, it can be reset with this call. +

      +The error state is automatically cleared if you Parse a new XML block. +

      +

      + +

      +
      + + + + + + + + +
      virtual TiXmlNode* TiXmlDocument::Clone (  )  const [protected, virtual]
      +
      +
      + +

      +Create an exact duplicate of this node and return it. +

      +The memory must be deleted by the caller. +

      +Implements TiXmlNode. +

      +

      + +

      +
      + + + + + + + + +
      bool TiXmlDocument::Error (  )  const [inline]
      +
      +
      + +

      +If an error occurs, Error will be set to true. +

      +Also,

        +
      • The ErrorId() will contain the integer identifier of the error (not generally useful)
      • The ErrorDesc() method will return the name of the error. (very useful)
      • The ErrorRow() and ErrorCol() will return the location of the error (if known)
      + +
      +

      + +

      +
      + + + + + + + + +
      int TiXmlDocument::ErrorId (  )  const [inline]
      +
      +
      + +

      +Generally, you probably want the error string ( ErrorDesc() ). +

      +But if you prefer the ErrorId, this function will fetch it. +

      +

      + +

      +
      + + + + + + + + +
      int TiXmlDocument::ErrorRow (  )  const [inline]
      +
      +
      + +

      +Returns the location (if known) of the error. +

      +The first column is column 1, and the first row is row 1. A value of 0 means the row and column wasn't applicable (memory errors, for example, have no row/column) or the parser lost the error. (An error in the error reporting, in that case.)

      +

      See also:
      SetTabSize, Row, Column
      + +
      +

      + +

      +
      + + + + + + + + + + + + + + + + + + +
      bool TiXmlDocument::LoadFile (const std::string &  filename,
      TiXmlEncoding  encoding = TIXML_DEFAULT_ENCODING 
      ) [inline]
      +
      +
      + +

      +

      Parameters:
      + + +
      encoding  +STL std::string version.
      +
      +
      +

      + +

      +
      + + + + + + + + + + + + + + + + + + +
      bool TiXmlDocument::LoadFile (FILE * ,
      TiXmlEncoding  encoding = TIXML_DEFAULT_ENCODING 
      )
      +
      +
      + +

      +Load a file using the given FILE*. +

      +Returns true if successful. Note that this method doesn't stream - the entire object pointed at by the FILE* will be interpreted as an XML file. TinyXML doesn't stream in XML from the current file location. Streaming may be added in the future. +

      +

      + +

      +
      + + + + + + + + + +
      bool TiXmlDocument::LoadFile (TiXmlEncoding  encoding = TIXML_DEFAULT_ENCODING  ) 
      +
      +
      + +

      +Load a file using the current document value. +

      +Returns true if successful. Will delete any existing document data before loading. +

      +

      + +

      +
      + + + + + + + + + + + + + + + + + + + + + + + + +
      virtual const char* TiXmlDocument::Parse (const char *  p,
      TiXmlParsingData *  data = 0,
      TiXmlEncoding  encoding = TIXML_DEFAULT_ENCODING 
      ) [virtual]
      +
      +
      + +

      +Parse the given null terminated block of xml data. +

      +Passing in an encoding to this method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml to use that encoding, regardless of what TinyXml might otherwise try to detect. +

      +Implements TiXmlBase. +

      +

      + +

      +
      + + + + + + + + +
      const TiXmlElement* TiXmlDocument::RootElement (  )  const [inline]
      +
      +
      + +

      +Get the root element -- the only top level element -- of the document. +

      +In well formed XML, there should only be one. TinyXml is tolerant of multiple elements at the document level. +

      +

      + +

      +
      + + + + + + + + + +
      void TiXmlDocument::SetTabSize (int  _tabsize  )  [inline]
      +
      +
      + +

      +SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol()) to report the correct values for row and column. +

      +It does not change the output or input in any way.

      +By calling this method, with a tab size greater than 0, the row and column of each node and attribute is stored when the file is loaded. Very useful for tracking the DOM back in to the source file.

      +The tab size is required for calculating the location of nodes. If not set, the default of 4 is used. The tabsize is set per document. Setting the tabsize to 0 disables row/column tracking.

      +Note that row and column tracking is not supported when using operator>>.

      +The tab size needs to be enabled before the parse or load. Correct usage:

      		TiXmlDocument doc;
      +		doc.SetTabSize( 8 );
      +		doc.Load( "myfile.xml" );
      +		

      +

      See also:
      Row, Column
      + +
      +

      +


      The documentation for this class was generated from the following file: +
      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/classTiXmlDocument.png b/Engine/lib/tinyxml/docs/classTiXmlDocument.png new file mode 100644 index 000000000..32fd267e4 Binary files /dev/null and b/Engine/lib/tinyxml/docs/classTiXmlDocument.png differ diff --git a/Engine/lib/tinyxml/docs/classTiXmlElement-members.html b/Engine/lib/tinyxml/docs/classTiXmlElement-members.html new file mode 100644 index 000000000..ece5255d5 --- /dev/null +++ b/Engine/lib/tinyxml/docs/classTiXmlElement-members.html @@ -0,0 +1,116 @@ + + +TinyXml: Member List + + + + + + +

      TiXmlElement Member List

      This is the complete list of members for TiXmlElement, including all inherited members.

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      Accept(TiXmlVisitor *visitor) const TiXmlElement [virtual]
      Attribute(const char *name) const TiXmlElement
      Attribute(const char *name, int *i) const TiXmlElement
      Attribute(const char *name, double *d) const TiXmlElement
      Clear()TiXmlNode
      Clone() const TiXmlElement [virtual]
      Column() const TiXmlBase [inline]
      EncodeString(const TIXML_STRING &str, TIXML_STRING *out)TiXmlBase [static]
      FirstAttribute() const TiXmlElement [inline]
      FirstChild() const TiXmlNode [inline]
      FirstChild(const char *value) const TiXmlNode
      FirstChild(const char *_value)TiXmlNode [inline]
      FirstChild(const std::string &_value) const TiXmlNode [inline]
      FirstChild(const std::string &_value)TiXmlNode [inline]
      FirstChildElement() const TiXmlNode
      FirstChildElement(const char *_value) const TiXmlNode
      FirstChildElement(const std::string &_value) const TiXmlNode [inline]
      FirstChildElement(const std::string &_value)TiXmlNode [inline]
      GetDocument() const TiXmlNode
      GetText() const TiXmlElement
      GetUserData()TiXmlBase [inline]
      GetUserData() const TiXmlBase [inline]
      InsertAfterChild(TiXmlNode *afterThis, const TiXmlNode &addThis)TiXmlNode
      InsertBeforeChild(TiXmlNode *beforeThis, const TiXmlNode &addThis)TiXmlNode
      InsertEndChild(const TiXmlNode &addThis)TiXmlNode
      IsWhiteSpaceCondensed()TiXmlBase [inline, static]
      IterateChildren(const TiXmlNode *previous) const TiXmlNode
      IterateChildren(const char *value, const TiXmlNode *previous) const TiXmlNode
      IterateChildren(const std::string &_value, const TiXmlNode *previous) const TiXmlNode [inline]
      IterateChildren(const std::string &_value, const TiXmlNode *previous)TiXmlNode [inline]
      LastAttribute() const TiXmlElement [inline]
      LastChild()TiXmlNode [inline]
      LastChild(const char *_value)TiXmlNode [inline]
      LastChild(const std::string &_value) const TiXmlNode [inline]
      LastChild(const std::string &_value)TiXmlNode [inline]
      LinkEndChild(TiXmlNode *addThis)TiXmlNode
      NextSibling(const std::string &_value) const TiXmlNode [inline]
      NextSibling(const std::string &_value)TiXmlNode [inline]
      NextSibling() const TiXmlNode [inline]
      NextSibling(const char *) const TiXmlNode
      NextSiblingElement() const TiXmlNode
      NextSiblingElement(const char *) const TiXmlNode
      NextSiblingElement(const std::string &_value) const TiXmlNode [inline]
      NextSiblingElement(const std::string &_value)TiXmlNode [inline]
      NoChildren() const TiXmlNode [inline]
      NodeType enum nameTiXmlNode
      operator<<(std::ostream &out, const TiXmlNode &base)TiXmlNode [friend]
      operator<<(std::string &out, const TiXmlNode &base)TiXmlNode [friend]
      operator>>(std::istream &in, TiXmlNode &base)TiXmlNode [friend]
      Parent()TiXmlNode [inline]
      PreviousSibling() const TiXmlNode [inline]
      PreviousSibling(const char *) const TiXmlNode
      PreviousSibling(const std::string &_value) const TiXmlNode [inline]
      PreviousSibling(const std::string &_value)TiXmlNode [inline]
      Print(FILE *cfile, int depth) const TiXmlElement [virtual]
      QueryDoubleAttribute(const char *name, double *_value) const TiXmlElement
      QueryFloatAttribute(const char *name, float *_value) const TiXmlElement [inline]
      QueryIntAttribute(const char *name, int *_value) const TiXmlElement
      QueryValueAttribute(const std::string &name, T *outValue) const TiXmlElement [inline]
      RemoveAttribute(const char *name)TiXmlElement
      RemoveAttribute(const std::string &name)TiXmlElement [inline]
      RemoveChild(TiXmlNode *removeThis)TiXmlNode
      ReplaceChild(TiXmlNode *replaceThis, const TiXmlNode &withThis)TiXmlNode
      Row() const TiXmlBase [inline]
      SetAttribute(const char *name, const char *_value)TiXmlElement
      SetAttribute(const std::string &name, const std::string &_value)TiXmlElement
      SetAttribute(const char *name, int value)TiXmlElement
      SetCondenseWhiteSpace(bool condense)TiXmlBase [inline, static]
      SetDoubleAttribute(const char *name, double value)TiXmlElement
      SetUserData(void *user)TiXmlBase [inline]
      SetValue(const char *_value)TiXmlNode [inline]
      SetValue(const std::string &_value)TiXmlNode [inline]
      TiXmlElement(const char *in_value)TiXmlElement
      TiXmlElement(const std::string &_value)TiXmlElement
      ToComment() const TiXmlNode [inline, virtual]
      ToComment()TiXmlNode [inline, virtual]
      ToDeclaration() const TiXmlNode [inline, virtual]
      ToDeclaration()TiXmlNode [inline, virtual]
      ToDocument() const TiXmlNode [inline, virtual]
      ToDocument()TiXmlNode [inline, virtual]
      ToElement() const TiXmlElement [inline, virtual]
      ToElement()TiXmlElement [inline, virtual]
      ToText() const TiXmlNode [inline, virtual]
      ToText()TiXmlNode [inline, virtual]
      ToUnknown() const TiXmlNode [inline, virtual]
      ToUnknown()TiXmlNode [inline, virtual]
      Type() const TiXmlNode [inline]
      userDataTiXmlBase [protected]
      Value() const TiXmlNode [inline]
      ValueStr() const TiXmlNode [inline]


      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/classTiXmlElement.html b/Engine/lib/tinyxml/docs/classTiXmlElement.html new file mode 100644 index 000000000..dcbd25157 --- /dev/null +++ b/Engine/lib/tinyxml/docs/classTiXmlElement.html @@ -0,0 +1,420 @@ + + +TinyXml: TiXmlElement Class Reference + + + + + + +

      TiXmlElement Class Reference

      The element is a container class. +More... +

      +#include <tinyxml.h> +

      +

      Inheritance diagram for TiXmlElement: +

      + +TiXmlNode +TiXmlBase + +List of all members. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      Public Member Functions

      TiXmlElement (const char *in_value)
       Construct an element.
      TiXmlElement (const std::string &_value)
       std::string constructor.
      +const char * Attribute (const char *name) const
       Given an attribute name, Attribute() returns the value for the attribute of that name, or null if none exists.
      const char * Attribute (const char *name, int *i) const
       Given an attribute name, Attribute() returns the value for the attribute of that name, or null if none exists.
      const char * Attribute (const char *name, double *d) const
       Given an attribute name, Attribute() returns the value for the attribute of that name, or null if none exists.
      int QueryIntAttribute (const char *name, int *_value) const
       QueryIntAttribute examines the attribute - it is an alternative to the Attribute() method with richer error checking.
      +int QueryDoubleAttribute (const char *name, double *_value) const
       QueryDoubleAttribute examines the attribute - see QueryIntAttribute().
      +int QueryFloatAttribute (const char *name, float *_value) const
       QueryFloatAttribute examines the attribute - see QueryIntAttribute().
      template<typename T>
      int QueryValueAttribute (const std::string &name, T *outValue) const
       Template form of the attribute query which will try to read the attribute into the specified type.
      void SetAttribute (const char *name, const char *_value)
       Sets an attribute of name to a given value.
      +void SetAttribute (const std::string &name, const std::string &_value)
       STL std::string form. STL std::string form.
      void SetAttribute (const char *name, int value)
       Sets an attribute of name to a given value.
      void SetDoubleAttribute (const char *name, double value)
       Sets an attribute of name to a given value.
      +void RemoveAttribute (const char *name)
       Deletes an attribute with the given name.
      +void RemoveAttribute (const std::string &name)
       STL std::string form.
      +const TiXmlAttributeFirstAttribute () const
       Access the first attribute in this element.
      +const TiXmlAttributeLastAttribute () const
       Access the last attribute in this element.
      const char * GetText () const
       Convenience function for easy access to the text inside an element.
      +virtual TiXmlNodeClone () const
       Creates a new Element and returns it - the returned element is a copy.
      virtual void Print (FILE *cfile, int depth) const
       All TinyXml classes can print themselves to a filestream or the string class (TiXmlString in non-STL mode, std::string in STL mode.
      +virtual const TiXmlElementToElement () const
       Cast to a more defined type. Will return null not of the requested type.
      +virtual TiXmlElementToElement ()
       Cast to a more defined type. Will return null not of the requested type.
      +virtual bool Accept (TiXmlVisitor *visitor) const
       Walk the XML tree visiting this node and all of its children.
      +

      Detailed Description

      +The element is a container class. +

      +It has a value, the element name, and can contain other elements, text, comments, and unknowns. Elements also contain an arbitrary number of attributes. +

      +


      Member Function Documentation

      + +
      +
      + + + + + + + + + + + + + + + + + + +
      const char* TiXmlElement::Attribute (const char *  name,
      double *  d 
      ) const
      +
      +
      + +

      +Given an attribute name, Attribute() returns the value for the attribute of that name, or null if none exists. +

      +If the attribute exists and can be converted to an double, the double value will be put in the return 'd', if 'd' is non-null. +

      +

      + +

      +
      + + + + + + + + + + + + + + + + + + +
      const char* TiXmlElement::Attribute (const char *  name,
      int *  i 
      ) const
      +
      +
      + +

      +Given an attribute name, Attribute() returns the value for the attribute of that name, or null if none exists. +

      +If the attribute exists and can be converted to an integer, the integer value will be put in the return 'i', if 'i' is non-null. +

      +

      + +

      +
      + + + + + + + + +
      const char* TiXmlElement::GetText (  )  const
      +
      +
      + +

      +Convenience function for easy access to the text inside an element. +

      +Although easy and concise, GetText() is limited compared to getting the TiXmlText child and accessing it directly.

      +If the first child of 'this' is a TiXmlText, the GetText() returns the character string of the Text node, else null is returned.

      +This is a convenient method for getting the text of simple contained text:

      		<foo>This is text</foo>
      +		const char* str = fooElement->GetText();
      +		

      +'str' will be a pointer to "This is text".

      +Note that this function can be misleading. If the element foo was created from this XML:

      		<foo><b>This is text</b></foo> 
      +		

      +then the value of str would be null. The first child node isn't a text node, it is another element. From this XML:

      		<foo>This is <b>text</b></foo> 
      +		
      GetText() will return "This is ".

      +WARNING: GetText() accesses a child node - don't become confused with the similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are safe type casts on the referenced node. +

      +

      + +

      +
      + + + + + + + + + + + + + + + + + + +
      virtual void TiXmlElement::Print (FILE *  cfile,
      int  depth 
      ) const [virtual]
      +
      +
      + +

      +All TinyXml classes can print themselves to a filestream or the string class (TiXmlString in non-STL mode, std::string in STL mode. +

      +) Either or both cfile and str can be null.

      +This is a formatted print, and will insert tabs and newlines.

      +(For an unformatted stream, use the << operator.) +

      +Implements TiXmlBase. +

      +

      + +

      +
      + + + + + + + + + + + + + + + + + + +
      int TiXmlElement::QueryIntAttribute (const char *  name,
      int *  _value 
      ) const
      +
      +
      + +

      +QueryIntAttribute examines the attribute - it is an alternative to the Attribute() method with richer error checking. +

      +If the attribute is an integer, it is stored in 'value' and the call returns TIXML_SUCCESS. If it is not an integer, it returns TIXML_WRONG_TYPE. If the attribute does not exist, then TIXML_NO_ATTRIBUTE is returned. +

      +

      + +

      +
      +
      +template<typename T>
      + + + + + + + + + + + + + + + + + + +
      int TiXmlElement::QueryValueAttribute (const std::string &  name,
      T *  outValue 
      ) const [inline]
      +
      +
      + +

      +Template form of the attribute query which will try to read the attribute into the specified type. +

      +Very easy, very powerful, but be careful to make sure to call this with the correct type.

      +NOTE: This method doesn't work correctly for 'string' types.

      +

      Returns:
      TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE
      + +
      +

      + +

      +
      + + + + + + + + + + + + + + + + + + +
      void TiXmlElement::SetAttribute (const char *  name,
      int  value 
      )
      +
      +
      + +

      +Sets an attribute of name to a given value. +

      +The attribute will be created if it does not exist, or changed if it does. +

      +

      + +

      +
      + + + + + + + + + + + + + + + + + + +
      void TiXmlElement::SetAttribute (const char *  name,
      const char *  _value 
      )
      +
      +
      + +

      +Sets an attribute of name to a given value. +

      +The attribute will be created if it does not exist, or changed if it does. +

      +

      + +

      +
      + + + + + + + + + + + + + + + + + + +
      void TiXmlElement::SetDoubleAttribute (const char *  name,
      double  value 
      )
      +
      +
      + +

      +Sets an attribute of name to a given value. +

      +The attribute will be created if it does not exist, or changed if it does. +

      +

      +


      The documentation for this class was generated from the following file: +
      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/classTiXmlElement.png b/Engine/lib/tinyxml/docs/classTiXmlElement.png new file mode 100644 index 000000000..5acc21bd0 Binary files /dev/null and b/Engine/lib/tinyxml/docs/classTiXmlElement.png differ diff --git a/Engine/lib/tinyxml/docs/classTiXmlHandle-members.html b/Engine/lib/tinyxml/docs/classTiXmlHandle-members.html new file mode 100644 index 000000000..77ed5baba --- /dev/null +++ b/Engine/lib/tinyxml/docs/classTiXmlHandle-members.html @@ -0,0 +1,44 @@ + + +TinyXml: Member List + + + + + + +

      TiXmlHandle Member List

      This is the complete list of members for TiXmlHandle, including all inherited members.

      + + + + + + + + + + + + + + + + + + +
      Child(const char *value, int index) const TiXmlHandle
      Child(int index) const TiXmlHandle
      ChildElement(const char *value, int index) const TiXmlHandle
      ChildElement(int index) const TiXmlHandle
      Element() const TiXmlHandle [inline]
      FirstChild() const TiXmlHandle
      FirstChild(const char *value) const TiXmlHandle
      FirstChildElement() const TiXmlHandle
      FirstChildElement(const char *value) const TiXmlHandle
      Node() const TiXmlHandle [inline]
      Text() const TiXmlHandle [inline]
      TiXmlHandle(TiXmlNode *_node)TiXmlHandle [inline]
      TiXmlHandle(const TiXmlHandle &ref)TiXmlHandle [inline]
      ToElement() const TiXmlHandle [inline]
      ToNode() const TiXmlHandle [inline]
      ToText() const TiXmlHandle [inline]
      ToUnknown() const TiXmlHandle [inline]
      Unknown() const TiXmlHandle [inline]


      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/classTiXmlHandle.html b/Engine/lib/tinyxml/docs/classTiXmlHandle.html new file mode 100644 index 000000000..3808e66f6 --- /dev/null +++ b/Engine/lib/tinyxml/docs/classTiXmlHandle.html @@ -0,0 +1,419 @@ + + +TinyXml: TiXmlHandle Class Reference + + + + + + +

      TiXmlHandle Class Reference

      A TiXmlHandle is a class that wraps a node pointer with null checks; this is an incredibly useful thing. +More... +

      +#include <tinyxml.h> +

      +List of all members. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      Public Member Functions

      TiXmlHandle (TiXmlNode *_node)
       Create a handle from any node (at any depth of the tree.) This can be a null pointer.
      TiXmlHandle (const TiXmlHandle &ref)
       Copy constructor.
      +TiXmlHandle FirstChild () const
       Return a handle to the first child node.
      +TiXmlHandle FirstChild (const char *value) const
       Return a handle to the first child node with the given name.
      +TiXmlHandle FirstChildElement () const
       Return a handle to the first child element.
      +TiXmlHandle FirstChildElement (const char *value) const
       Return a handle to the first child element with the given name.
      TiXmlHandle Child (const char *value, int index) const
       Return a handle to the "index" child with the given name.
      TiXmlHandle Child (int index) const
       Return a handle to the "index" child.
      TiXmlHandle ChildElement (const char *value, int index) const
       Return a handle to the "index" child element with the given name.
      TiXmlHandle ChildElement (int index) const
       Return a handle to the "index" child element.
      TiXmlNodeToNode () const
       Return the handle as a TiXmlNode.
      TiXmlElementToElement () const
       Return the handle as a TiXmlElement.
      TiXmlTextToText () const
       Return the handle as a TiXmlText.
      TiXmlUnknownToUnknown () const
       Return the handle as a TiXmlUnknown.
      TiXmlNodeNode () const
      TiXmlElementElement () const
      TiXmlTextText () const
      TiXmlUnknownUnknown () const
      +


      Detailed Description

      +A TiXmlHandle is a class that wraps a node pointer with null checks; this is an incredibly useful thing. +

      +Note that TiXmlHandle is not part of the TinyXml DOM structure. It is a separate utility class.

      +Take an example:

      	<Document>
      +		<Element attributeA = "valueA">
      +			<Child attributeB = "value1" />
      +			<Child attributeB = "value2" />
      +		</Element>
      +	<Document>
      +	

      +Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very easy to write a *lot* of code that looks like:

      +

      	TiXmlElement* root = document.FirstChildElement( "Document" );
      +	if ( root )
      +	{
      +		TiXmlElement* element = root->FirstChildElement( "Element" );
      +		if ( element )
      +		{
      +			TiXmlElement* child = element->FirstChildElement( "Child" );
      +			if ( child )
      +			{
      +				TiXmlElement* child2 = child->NextSiblingElement( "Child" );
      +				if ( child2 )
      +				{
      +					// Finally do something useful.
      +	

      +And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity of such code. A TiXmlHandle checks for null pointers so it is perfectly safe and correct to use:

      +

      	TiXmlHandle docHandle( &document );
      +	TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement();
      +	if ( child2 )
      +	{
      +		// do something useful
      +	

      +Which is MUCH more concise and useful.

      +It is also safe to copy handles - internally they are nothing more than node pointers.

      	TiXmlHandle handleCopy = handle;
      +	

      +What they should not be used for is iteration:

      +

      	int i=0; 
      +	while ( true )
      +	{
      +		TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).ToElement();
      +		if ( !child )
      +			break;
      +		// do something
      +		++i;
      +	}
      +	

      +It seems reasonable, but it is in fact two embedded while loops. The Child method is a linear walk to find the element, so this code would iterate much more than it needs to. Instead, prefer:

      +

      	TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).ToElement();
      +
      +	for( child; child; child=child->NextSiblingElement() )
      +	{
      +		// do something
      +	}
      +	
      +

      +


      Member Function Documentation

      + +
      +
      + + + + + + + + + +
      TiXmlHandle TiXmlHandle::Child (int  index  )  const
      +
      +
      + +

      +Return a handle to the "index" child. +

      +The first child is 0, the second 1, etc. +

      +

      + +

      +
      + + + + + + + + + + + + + + + + + + +
      TiXmlHandle TiXmlHandle::Child (const char *  value,
      int  index 
      ) const
      +
      +
      + +

      +Return a handle to the "index" child with the given name. +

      +The first child is 0, the second 1, etc. +

      +

      + +

      +
      + + + + + + + + + +
      TiXmlHandle TiXmlHandle::ChildElement (int  index  )  const
      +
      +
      + +

      +Return a handle to the "index" child element. +

      +The first child element is 0, the second 1, etc. Note that only TiXmlElements are indexed: other types are not counted. +

      +

      + +

      +
      + + + + + + + + + + + + + + + + + + +
      TiXmlHandle TiXmlHandle::ChildElement (const char *  value,
      int  index 
      ) const
      +
      +
      + +

      +Return a handle to the "index" child element with the given name. +

      +The first child element is 0, the second 1, etc. Note that only TiXmlElements are indexed: other types are not counted. +

      +

      + +

      +
      + + + + + + + + +
      TiXmlElement* TiXmlHandle::Element (  )  const [inline]
      +
      +
      + +

      +

      Deprecated:
      use ToElement. Return the handle as a TiXmlElement. This may return null.
      + +
      +

      + +

      +
      + + + + + + + + +
      TiXmlNode* TiXmlHandle::Node (  )  const [inline]
      +
      +
      + +

      +

      Deprecated:
      use ToNode. Return the handle as a TiXmlNode. This may return null.
      + +
      +

      + +

      +
      + + + + + + + + +
      TiXmlText* TiXmlHandle::Text (  )  const [inline]
      +
      +
      + +

      +

      Deprecated:
      use ToText() Return the handle as a TiXmlText. This may return null.
      + +
      +

      + +

      +
      + + + + + + + + +
      TiXmlElement* TiXmlHandle::ToElement (  )  const [inline]
      +
      +
      + +

      +Return the handle as a TiXmlElement. +

      +This may return null. +

      +

      + +

      +
      + + + + + + + + +
      TiXmlNode* TiXmlHandle::ToNode (  )  const [inline]
      +
      +
      + +

      +Return the handle as a TiXmlNode. +

      +This may return null. +

      +

      + +

      +
      + + + + + + + + +
      TiXmlText* TiXmlHandle::ToText (  )  const [inline]
      +
      +
      + +

      +Return the handle as a TiXmlText. +

      +This may return null. +

      +

      + +

      +
      + + + + + + + + +
      TiXmlUnknown* TiXmlHandle::ToUnknown (  )  const [inline]
      +
      +
      + +

      +Return the handle as a TiXmlUnknown. +

      +This may return null. +

      +

      + +

      +
      + + + + + + + + +
      TiXmlUnknown* TiXmlHandle::Unknown (  )  const [inline]
      +
      +
      + +

      +

      Deprecated:
      use ToUnknown() Return the handle as a TiXmlUnknown. This may return null.
      + +
      +

      +


      The documentation for this class was generated from the following file: +
      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/classTiXmlNode-members.html b/Engine/lib/tinyxml/docs/classTiXmlNode-members.html new file mode 100644 index 000000000..b7c9ecf57 --- /dev/null +++ b/Engine/lib/tinyxml/docs/classTiXmlNode-members.html @@ -0,0 +1,98 @@ + + +TinyXml: Member List + + + + + + +

      TiXmlNode Member List

      This is the complete list of members for TiXmlNode, including all inherited members.

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      Accept(TiXmlVisitor *visitor) const =0TiXmlNode [pure virtual]
      Clear()TiXmlNode
      Clone() const =0TiXmlNode [pure virtual]
      Column() const TiXmlBase [inline]
      EncodeString(const TIXML_STRING &str, TIXML_STRING *out)TiXmlBase [static]
      FirstChild() const TiXmlNode [inline]
      FirstChild(const char *value) const TiXmlNode
      FirstChild(const char *_value)TiXmlNode [inline]
      FirstChild(const std::string &_value) const TiXmlNode [inline]
      FirstChild(const std::string &_value)TiXmlNode [inline]
      FirstChildElement() const TiXmlNode
      FirstChildElement(const char *_value) const TiXmlNode
      FirstChildElement(const std::string &_value) const TiXmlNode [inline]
      FirstChildElement(const std::string &_value)TiXmlNode [inline]
      GetDocument() const TiXmlNode
      GetUserData()TiXmlBase [inline]
      GetUserData() const TiXmlBase [inline]
      InsertAfterChild(TiXmlNode *afterThis, const TiXmlNode &addThis)TiXmlNode
      InsertBeforeChild(TiXmlNode *beforeThis, const TiXmlNode &addThis)TiXmlNode
      InsertEndChild(const TiXmlNode &addThis)TiXmlNode
      IsWhiteSpaceCondensed()TiXmlBase [inline, static]
      IterateChildren(const TiXmlNode *previous) const TiXmlNode
      IterateChildren(const char *value, const TiXmlNode *previous) const TiXmlNode
      IterateChildren(const std::string &_value, const TiXmlNode *previous) const TiXmlNode [inline]
      IterateChildren(const std::string &_value, const TiXmlNode *previous)TiXmlNode [inline]
      LastChild()TiXmlNode [inline]
      LastChild(const char *_value)TiXmlNode [inline]
      LastChild(const std::string &_value) const TiXmlNode [inline]
      LastChild(const std::string &_value)TiXmlNode [inline]
      LinkEndChild(TiXmlNode *addThis)TiXmlNode
      NextSibling(const std::string &_value) const TiXmlNode [inline]
      NextSibling(const std::string &_value)TiXmlNode [inline]
      NextSibling() const TiXmlNode [inline]
      NextSibling(const char *) const TiXmlNode
      NextSiblingElement() const TiXmlNode
      NextSiblingElement(const char *) const TiXmlNode
      NextSiblingElement(const std::string &_value) const TiXmlNode [inline]
      NextSiblingElement(const std::string &_value)TiXmlNode [inline]
      NoChildren() const TiXmlNode [inline]
      NodeType enum nameTiXmlNode
      operator<<(std::ostream &out, const TiXmlNode &base)TiXmlNode [friend]
      operator<<(std::string &out, const TiXmlNode &base)TiXmlNode [friend]
      operator>>(std::istream &in, TiXmlNode &base)TiXmlNode [friend]
      Parent()TiXmlNode [inline]
      PreviousSibling() const TiXmlNode [inline]
      PreviousSibling(const char *) const TiXmlNode
      PreviousSibling(const std::string &_value) const TiXmlNode [inline]
      PreviousSibling(const std::string &_value)TiXmlNode [inline]
      Print(FILE *cfile, int depth) const =0TiXmlBase [pure virtual]
      RemoveChild(TiXmlNode *removeThis)TiXmlNode
      ReplaceChild(TiXmlNode *replaceThis, const TiXmlNode &withThis)TiXmlNode
      Row() const TiXmlBase [inline]
      SetCondenseWhiteSpace(bool condense)TiXmlBase [inline, static]
      SetUserData(void *user)TiXmlBase [inline]
      SetValue(const char *_value)TiXmlNode [inline]
      SetValue(const std::string &_value)TiXmlNode [inline]
      ToComment() const TiXmlNode [inline, virtual]
      ToComment()TiXmlNode [inline, virtual]
      ToDeclaration() const TiXmlNode [inline, virtual]
      ToDeclaration()TiXmlNode [inline, virtual]
      ToDocument() const TiXmlNode [inline, virtual]
      ToDocument()TiXmlNode [inline, virtual]
      ToElement() const TiXmlNode [inline, virtual]
      ToElement()TiXmlNode [inline, virtual]
      ToText() const TiXmlNode [inline, virtual]
      ToText()TiXmlNode [inline, virtual]
      ToUnknown() const TiXmlNode [inline, virtual]
      ToUnknown()TiXmlNode [inline, virtual]
      Type() const TiXmlNode [inline]
      userDataTiXmlBase [protected]
      Value() const TiXmlNode [inline]
      ValueStr() const TiXmlNode [inline]


      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/classTiXmlNode.html b/Engine/lib/tinyxml/docs/classTiXmlNode.html new file mode 100644 index 000000000..b64a0d60f --- /dev/null +++ b/Engine/lib/tinyxml/docs/classTiXmlNode.html @@ -0,0 +1,780 @@ + + +TinyXml: TiXmlNode Class Reference + + + + + + +

      TiXmlNode Class Reference

      The parent class for everything in the Document Object Model. +More... +

      +#include <tinyxml.h> +

      +

      Inheritance diagram for TiXmlNode: +

      + +TiXmlBase +TiXmlComment +TiXmlDeclaration +TiXmlDocument +TiXmlElement +TiXmlText +TiXmlUnknown + +List of all members. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      Public Types

      enum  NodeType
       The types of XML nodes supported by TinyXml. More...

      Public Member Functions

      const char * Value () const
       The meaning of 'value' changes for the specific type of TiXmlNode.
      const std::string & ValueStr () const
       Return Value() as a std::string.
      void SetValue (const char *_value)
       Changes the value of the node.
      +void SetValue (const std::string &_value)
       STL std::string form.
      +void Clear ()
       Delete all the children of this node. Does not affect 'this'.
      +TiXmlNodeParent ()
       One step up the DOM.
      +const TiXmlNodeFirstChild () const
       The first child of this node. Will be null if there are no children.
      const TiXmlNodeFirstChild (const char *value) const
       The first child of this node with the matching 'value'.
      +TiXmlNodeFirstChild (const char *_value)
       The first child of this node with the matching 'value'. Will be null if none found.
      +TiXmlNodeLastChild ()
       The last child of this node. Will be null if there are no children.
      +TiXmlNodeLastChild (const char *_value)
       The last child of this node matching 'value'. Will be null if there are no children.
      +const TiXmlNodeFirstChild (const std::string &_value) const
       STL std::string form.
      +TiXmlNodeFirstChild (const std::string &_value)
       STL std::string form.
      +const TiXmlNodeLastChild (const std::string &_value) const
       STL std::string form.
      +TiXmlNodeLastChild (const std::string &_value)
       STL std::string form.
      const TiXmlNodeIterateChildren (const TiXmlNode *previous) const
       An alternate way to walk the children of a node.
      +const TiXmlNodeIterateChildren (const char *value, const TiXmlNode *previous) const
       This flavor of IterateChildren searches for children with a particular 'value'.
      +const TiXmlNodeIterateChildren (const std::string &_value, const TiXmlNode *previous) const
       STL std::string form.
      +TiXmlNodeIterateChildren (const std::string &_value, const TiXmlNode *previous)
       STL std::string form.
      TiXmlNodeInsertEndChild (const TiXmlNode &addThis)
       Add a new node related to this.
      TiXmlNodeLinkEndChild (TiXmlNode *addThis)
       Add a new node related to this.
      TiXmlNodeInsertBeforeChild (TiXmlNode *beforeThis, const TiXmlNode &addThis)
       Add a new node related to this.
      TiXmlNodeInsertAfterChild (TiXmlNode *afterThis, const TiXmlNode &addThis)
       Add a new node related to this.
      TiXmlNodeReplaceChild (TiXmlNode *replaceThis, const TiXmlNode &withThis)
       Replace a child of this node.
      +bool RemoveChild (TiXmlNode *removeThis)
       Delete a child of this node.
      +const TiXmlNodePreviousSibling () const
       Navigate to a sibling node.
      +const TiXmlNodePreviousSibling (const char *) const
       Navigate to a sibling node.
      +const TiXmlNodePreviousSibling (const std::string &_value) const
       STL std::string form.
      +TiXmlNodePreviousSibling (const std::string &_value)
       STL std::string form.
      +const TiXmlNodeNextSibling (const std::string &_value) const
       STL std::string form.
      +TiXmlNodeNextSibling (const std::string &_value)
       STL std::string form.
      +const TiXmlNodeNextSibling () const
       Navigate to a sibling node.
      +const TiXmlNodeNextSibling (const char *) const
       Navigate to a sibling node with the given 'value'.
      const TiXmlElementNextSiblingElement () const
       Convenience function to get through elements.
      const TiXmlElementNextSiblingElement (const char *) const
       Convenience function to get through elements.
      +const TiXmlElementNextSiblingElement (const std::string &_value) const
       STL std::string form.
      +TiXmlElementNextSiblingElement (const std::string &_value)
       STL std::string form.
      +const TiXmlElementFirstChildElement () const
       Convenience function to get through elements.
      +const TiXmlElementFirstChildElement (const char *_value) const
       Convenience function to get through elements.
      +const TiXmlElementFirstChildElement (const std::string &_value) const
       STL std::string form.
      +TiXmlElementFirstChildElement (const std::string &_value)
       STL std::string form.
      int Type () const
       Query the type (as an enumerated value, above) of this node.
      const TiXmlDocumentGetDocument () const
       Return a pointer to the Document this node lives in.
      +bool NoChildren () const
       Returns true if this node has no children.
      +virtual const TiXmlDocumentToDocument () const
       Cast to a more defined type. Will return null if not of the requested type.
      +virtual const TiXmlElementToElement () const
       Cast to a more defined type. Will return null if not of the requested type.
      +virtual const TiXmlCommentToComment () const
       Cast to a more defined type. Will return null if not of the requested type.
      +virtual const TiXmlUnknownToUnknown () const
       Cast to a more defined type. Will return null if not of the requested type.
      +virtual const TiXmlTextToText () const
       Cast to a more defined type. Will return null if not of the requested type.
      +virtual const TiXmlDeclarationToDeclaration () const
       Cast to a more defined type. Will return null if not of the requested type.
      +virtual TiXmlDocumentToDocument ()
       Cast to a more defined type. Will return null if not of the requested type.
      +virtual TiXmlElementToElement ()
       Cast to a more defined type. Will return null if not of the requested type.
      +virtual TiXmlCommentToComment ()
       Cast to a more defined type. Will return null if not of the requested type.
      +virtual TiXmlUnknownToUnknown ()
       Cast to a more defined type. Will return null if not of the requested type.
      +virtual TiXmlTextToText ()
       Cast to a more defined type. Will return null if not of the requested type.
      +virtual TiXmlDeclarationToDeclaration ()
       Cast to a more defined type. Will return null if not of the requested type.
      virtual TiXmlNodeClone () const =0
       Create an exact duplicate of this node and return it.
      virtual bool Accept (TiXmlVisitor *visitor) const =0
       Accept a hierchical visit the nodes in the TinyXML DOM.

      Friends

      +class TiXmlDocument
      +class TiXmlElement
      std::istream & operator>> (std::istream &in, TiXmlNode &base)
       An input stream operator, for every class.
      std::ostream & operator<< (std::ostream &out, const TiXmlNode &base)
       An output stream operator, for every class.
      +std::string & operator<< (std::string &out, const TiXmlNode &base)
       Appends the XML node or attribute to a std::string.
      +

      Detailed Description

      +The parent class for everything in the Document Object Model. +

      +(Except for attributes). Nodes have siblings, a parent, and children. A node can be in a document, or stand on its own. The type of a TiXmlNode can be queried, and it can be cast to its more defined type. +

      +


      Member Enumeration Documentation

      + +
      +
      + + + + +
      enum TiXmlNode::NodeType
      +
      +
      + +

      +The types of XML nodes supported by TinyXml. +

      +(All the unsupported types are picked up by UNKNOWN.) +

      +

      +


      Member Function Documentation

      + +
      +
      + + + + + + + + + +
      virtual bool TiXmlNode::Accept (TiXmlVisitor visitor  )  const [pure virtual]
      +
      +
      + +

      +Accept a hierchical visit the nodes in the TinyXML DOM. +

      +Every node in the XML tree will be conditionally visited and the host will be called back via the TiXmlVisitor interface.

      +This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse the XML for the callbacks, so the performance of TinyXML is unchanged by using this interface versus any other.)

      +The interface has been based on ideas from:

      +

      +

      +Which are both good references for "visiting".

      +An example of using Accept():

      		TiXmlPrinter printer;
      +		tinyxmlDoc.Accept( &printer );
      +		const char* xmlcstr = printer.CStr();
      +		
      +

      +Implemented in TiXmlElement, TiXmlComment, TiXmlText, TiXmlDeclaration, TiXmlUnknown, and TiXmlDocument. +

      +

      + +

      +
      + + + + + + + + +
      virtual TiXmlNode* TiXmlNode::Clone (  )  const [pure virtual]
      +
      +
      + +

      +Create an exact duplicate of this node and return it. +

      +The memory must be deleted by the caller. +

      +Implemented in TiXmlElement, TiXmlComment, TiXmlText, TiXmlDeclaration, TiXmlUnknown, and TiXmlDocument. +

      +

      + +

      +
      + + + + + + + + + +
      const TiXmlNode* TiXmlNode::FirstChild (const char *  value  )  const
      +
      +
      + +

      +The first child of this node with the matching 'value'. +

      +Will be null if none found. +

      +

      + +

      +
      + + + + + + + + +
      const TiXmlDocument* TiXmlNode::GetDocument (  )  const
      +
      +
      + +

      +Return a pointer to the Document this node lives in. +

      +Returns null if not in a document. +

      +

      + +

      +
      + + + + + + + + + + + + + + + + + + +
      TiXmlNode* TiXmlNode::InsertAfterChild (TiXmlNode afterThis,
      const TiXmlNode addThis 
      )
      +
      +
      + +

      +Add a new node related to this. +

      +Adds a child after the specified child. Returns a pointer to the new object or NULL if an error occured. +

      +

      + +

      +
      + + + + + + + + + + + + + + + + + + +
      TiXmlNode* TiXmlNode::InsertBeforeChild (TiXmlNode beforeThis,
      const TiXmlNode addThis 
      )
      +
      +
      + +

      +Add a new node related to this. +

      +Adds a child before the specified child. Returns a pointer to the new object or NULL if an error occured. +

      +

      + +

      +
      + + + + + + + + + +
      TiXmlNode* TiXmlNode::InsertEndChild (const TiXmlNode addThis  ) 
      +
      +
      + +

      +Add a new node related to this. +

      +Adds a child past the LastChild. Returns a pointer to the new object or NULL if an error occured. +

      +

      + +

      +
      + + + + + + + + + +
      const TiXmlNode* TiXmlNode::IterateChildren (const TiXmlNode previous  )  const
      +
      +
      + +

      +An alternate way to walk the children of a node. +

      +One way to iterate over nodes is:

      			for( child = parent->FirstChild(); child; child = child->NextSibling() )
      +		

      +IterateChildren does the same thing with the syntax:

      			child = 0;
      +			while( child = parent->IterateChildren( child ) )
      +		

      +IterateChildren takes the previous child as input and finds the next one. If the previous child is null, it returns the first. IterateChildren will return null when done. +

      +

      + +

      +
      + + + + + + + + + +
      TiXmlNode* TiXmlNode::LinkEndChild (TiXmlNode addThis  ) 
      +
      +
      + +

      +Add a new node related to this. +

      +Adds a child past the LastChild.

      +NOTE: the node to be added is passed by pointer, and will be henceforth owned (and deleted) by tinyXml. This method is efficient and avoids an extra copy, but should be used with care as it uses a different memory model than the other insert functions.

      +

      See also:
      InsertEndChild
      + +
      +

      + +

      +
      + + + + + + + + + +
      const TiXmlElement* TiXmlNode::NextSiblingElement (const char *   )  const
      +
      +
      + +

      +Convenience function to get through elements. +

      +Calls NextSibling and ToElement. Will skip all non-Element nodes. Returns 0 if there is not another element. +

      +

      + +

      +
      + + + + + + + + +
      const TiXmlElement* TiXmlNode::NextSiblingElement (  )  const
      +
      +
      + +

      +Convenience function to get through elements. +

      +Calls NextSibling and ToElement. Will skip all non-Element nodes. Returns 0 if there is not another element. +

      +

      + +

      +
      + + + + + + + + + + + + + + + + + + +
      TiXmlNode* TiXmlNode::ReplaceChild (TiXmlNode replaceThis,
      const TiXmlNode withThis 
      )
      +
      +
      + +

      +Replace a child of this node. +

      +Returns a pointer to the new object or NULL if an error occured. +

      +

      + +

      +
      + + + + + + + + + +
      void TiXmlNode::SetValue (const char *  _value  )  [inline]
      +
      +
      + +

      +Changes the value of the node. +

      +Defined as:

      		Document:	filename of the xml file
      +		Element:	name of the element
      +		Comment:	the comment text
      +		Unknown:	the tag contents
      +		Text:		the text string
      +		
      +
      +

      + +

      +
      + + + + + + + + +
      int TiXmlNode::Type (  )  const [inline]
      +
      +
      + +

      +Query the type (as an enumerated value, above) of this node. +

      +The possible types are: DOCUMENT, ELEMENT, COMMENT, UNKNOWN, TEXT, and DECLARATION. +

      +

      + +

      +
      + + + + + + + + +
      const char* TiXmlNode::Value (  )  const [inline]
      +
      +
      + +

      +The meaning of 'value' changes for the specific type of TiXmlNode. +

      +

      		Document:	filename of the xml file
      +		Element:	name of the element
      +		Comment:	the comment text
      +		Unknown:	the tag contents
      +		Text:		the text string
      +		

      +The subclasses will wrap this function. +

      +

      + +

      +
      + + + + + + + + +
      const std::string& TiXmlNode::ValueStr (  )  const [inline]
      +
      +
      + +

      +Return Value() as a std::string. +

      +If you only use STL, this is more efficient than calling Value(). Only available in STL mode. +

      +

      +


      Friends And Related Function Documentation

      + +
      +
      + + + + + + + + + + + + + + + + + + +
      std::ostream& operator<< (std::ostream &  out,
      const TiXmlNode base 
      ) [friend]
      +
      +
      + +

      +An output stream operator, for every class. +

      +Note that this outputs without any newlines or formatting, as opposed to Print(), which includes tabs and new lines.

      +The operator<< and operator>> are not completely symmetric. Writing a node to a stream is very well defined. You'll get a nice stream of output, without any extra whitespace or newlines.

      +But reading is not as well defined. (As it always is.) If you create a TiXmlElement (for example) and read that from an input stream, the text needs to define an element or junk will result. This is true of all input streams, but it's worth keeping in mind.

      +A TiXmlDocument will read nodes until it reads a root element, and all the children of that root element. +

      +

      + +

      +
      + + + + + + + + + + + + + + + + + + +
      std::istream& operator>> (std::istream &  in,
      TiXmlNode base 
      ) [friend]
      +
      +
      + +

      +An input stream operator, for every class. +

      +Tolerant of newlines and formatting, but doesn't expect them. +

      +

      +


      The documentation for this class was generated from the following file: +
      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/classTiXmlNode.png b/Engine/lib/tinyxml/docs/classTiXmlNode.png new file mode 100644 index 000000000..6a663cf67 Binary files /dev/null and b/Engine/lib/tinyxml/docs/classTiXmlNode.png differ diff --git a/Engine/lib/tinyxml/docs/classTiXmlPrinter-members.html b/Engine/lib/tinyxml/docs/classTiXmlPrinter-members.html new file mode 100644 index 000000000..bd011fe3c --- /dev/null +++ b/Engine/lib/tinyxml/docs/classTiXmlPrinter-members.html @@ -0,0 +1,42 @@ + + +TinyXml: Member List + + + + + + +

      TiXmlPrinter Member List

      This is the complete list of members for TiXmlPrinter, including all inherited members.

      + + + + + + + + + + + + + + + + +
      CStr()TiXmlPrinter [inline]
      Indent()TiXmlPrinter [inline]
      LineBreak()TiXmlPrinter [inline]
      SetIndent(const char *_indent)TiXmlPrinter [inline]
      SetLineBreak(const char *_lineBreak)TiXmlPrinter [inline]
      SetStreamPrinting()TiXmlPrinter [inline]
      Size()TiXmlPrinter [inline]
      Str()TiXmlPrinter [inline]
      Visit(const TiXmlDeclaration &declaration)TiXmlPrinter [virtual]
      Visit(const TiXmlText &text)TiXmlPrinter [virtual]
      Visit(const TiXmlComment &comment)TiXmlPrinter [virtual]
      Visit(const TiXmlUnknown &unknown)TiXmlPrinter [virtual]
      VisitEnter(const TiXmlDocument &doc)TiXmlPrinter [virtual]
      VisitEnter(const TiXmlElement &element, const TiXmlAttribute *firstAttribute)TiXmlPrinter [virtual]
      VisitExit(const TiXmlDocument &doc)TiXmlPrinter [virtual]
      VisitExit(const TiXmlElement &element)TiXmlPrinter [virtual]


      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/classTiXmlPrinter.html b/Engine/lib/tinyxml/docs/classTiXmlPrinter.html new file mode 100644 index 000000000..c33fdfb3e --- /dev/null +++ b/Engine/lib/tinyxml/docs/classTiXmlPrinter.html @@ -0,0 +1,184 @@ + + +TinyXml: TiXmlPrinter Class Reference + + + + + + +

      TiXmlPrinter Class Reference

      Print to memory functionality. +More... +

      +#include <tinyxml.h> +

      +

      Inheritance diagram for TiXmlPrinter: +

      + +TiXmlVisitor + +List of all members. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      Public Member Functions

      +virtual bool VisitEnter (const TiXmlDocument &doc)
       Visit a document.
      +virtual bool VisitExit (const TiXmlDocument &doc)
       Visit a document.
      +virtual bool VisitEnter (const TiXmlElement &element, const TiXmlAttribute *firstAttribute)
       Visit an element.
      +virtual bool VisitExit (const TiXmlElement &element)
       Visit an element.
      +virtual bool Visit (const TiXmlDeclaration &declaration)
       Visit a declaration.
      +virtual bool Visit (const TiXmlText &text)
       Visit a text node.
      +virtual bool Visit (const TiXmlComment &comment)
       Visit a comment node.
      +virtual bool Visit (const TiXmlUnknown &unknown)
       Visit an unknow node.
      void SetIndent (const char *_indent)
       Set the indent characters for printing.
      +const char * Indent ()
       Query the indention string.
      void SetLineBreak (const char *_lineBreak)
       Set the line breaking string.
      +const char * LineBreak ()
       Query the current line breaking string.
      void SetStreamPrinting ()
       Switch over to "stream printing" which is the most dense formatting without linebreaks.
      +const char * CStr ()
       Return the result.
      +size_t Size ()
       Return the length of the result string.
      +const std::string & Str ()
       Return the result.
      +

      Detailed Description

      +Print to memory functionality. +

      +The TiXmlPrinter is useful when you need to:

      +

        +
      1. Print to memory (especially in non-STL mode)
      2. Control formatting (line endings, etc.)
      +

      +When constructed, the TiXmlPrinter is in its default "pretty printing" mode. Before calling Accept() you can call methods to control the printing of the XML document. After TiXmlNode::Accept() is called, the printed document can be accessed via the CStr(), Str(), and Size() methods.

      +TiXmlPrinter uses the Visitor API.

      	TiXmlPrinter printer;
      +	printer.SetIndent( "\t" );
      +
      +	doc.Accept( &printer );
      +	fprintf( stdout, "%s", printer.CStr() );
      +	
      +

      +


      Member Function Documentation

      + +
      +
      + + + + + + + + + +
      void TiXmlPrinter::SetIndent (const char *  _indent  )  [inline]
      +
      +
      + +

      +Set the indent characters for printing. +

      +By default 4 spaces but tab () is also useful, or null/empty string for no indentation. +

      +

      + +

      +
      + + + + + + + + + +
      void TiXmlPrinter::SetLineBreak (const char *  _lineBreak  )  [inline]
      +
      +
      + +

      +Set the line breaking string. +

      +By default set to newline (
      +). Some operating systems prefer other characters, or can be set to the null/empty string for no indenation. +

      +

      + +

      +
      + + + + + + + + +
      void TiXmlPrinter::SetStreamPrinting (  )  [inline]
      +
      +
      + +

      +Switch over to "stream printing" which is the most dense formatting without linebreaks. +

      +Common when the XML is needed for network transmission. +

      +

      +


      The documentation for this class was generated from the following file: +
      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/classTiXmlPrinter.png b/Engine/lib/tinyxml/docs/classTiXmlPrinter.png new file mode 100644 index 000000000..208878269 Binary files /dev/null and b/Engine/lib/tinyxml/docs/classTiXmlPrinter.png differ diff --git a/Engine/lib/tinyxml/docs/classTiXmlText-members.html b/Engine/lib/tinyxml/docs/classTiXmlText-members.html new file mode 100644 index 000000000..93a3b98f9 --- /dev/null +++ b/Engine/lib/tinyxml/docs/classTiXmlText-members.html @@ -0,0 +1,102 @@ + + +TinyXml: Member List + + + + + + +

      TiXmlText Member List

      This is the complete list of members for TiXmlText, including all inherited members.

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      Accept(TiXmlVisitor *content) const TiXmlText [virtual]
      CDATA() const TiXmlText [inline]
      Clear()TiXmlNode
      Clone() const TiXmlText [protected, virtual]
      Column() const TiXmlBase [inline]
      EncodeString(const TIXML_STRING &str, TIXML_STRING *out)TiXmlBase [static]
      FirstChild() const TiXmlNode [inline]
      FirstChild(const char *value) const TiXmlNode
      FirstChild(const char *_value)TiXmlNode [inline]
      FirstChild(const std::string &_value) const TiXmlNode [inline]
      FirstChild(const std::string &_value)TiXmlNode [inline]
      FirstChildElement() const TiXmlNode
      FirstChildElement(const char *_value) const TiXmlNode
      FirstChildElement(const std::string &_value) const TiXmlNode [inline]
      FirstChildElement(const std::string &_value)TiXmlNode [inline]
      GetDocument() const TiXmlNode
      GetUserData()TiXmlBase [inline]
      GetUserData() const TiXmlBase [inline]
      InsertAfterChild(TiXmlNode *afterThis, const TiXmlNode &addThis)TiXmlNode
      InsertBeforeChild(TiXmlNode *beforeThis, const TiXmlNode &addThis)TiXmlNode
      InsertEndChild(const TiXmlNode &addThis)TiXmlNode
      IsWhiteSpaceCondensed()TiXmlBase [inline, static]
      IterateChildren(const TiXmlNode *previous) const TiXmlNode
      IterateChildren(const char *value, const TiXmlNode *previous) const TiXmlNode
      IterateChildren(const std::string &_value, const TiXmlNode *previous) const TiXmlNode [inline]
      IterateChildren(const std::string &_value, const TiXmlNode *previous)TiXmlNode [inline]
      LastChild()TiXmlNode [inline]
      LastChild(const char *_value)TiXmlNode [inline]
      LastChild(const std::string &_value) const TiXmlNode [inline]
      LastChild(const std::string &_value)TiXmlNode [inline]
      LinkEndChild(TiXmlNode *addThis)TiXmlNode
      NextSibling(const std::string &_value) const TiXmlNode [inline]
      NextSibling(const std::string &_value)TiXmlNode [inline]
      NextSibling() const TiXmlNode [inline]
      NextSibling(const char *) const TiXmlNode
      NextSiblingElement() const TiXmlNode
      NextSiblingElement(const char *) const TiXmlNode
      NextSiblingElement(const std::string &_value) const TiXmlNode [inline]
      NextSiblingElement(const std::string &_value)TiXmlNode [inline]
      NoChildren() const TiXmlNode [inline]
      NodeType enum nameTiXmlNode
      operator<<(std::ostream &out, const TiXmlNode &base)TiXmlNode [friend]
      operator<<(std::string &out, const TiXmlNode &base)TiXmlNode [friend]
      operator>>(std::istream &in, TiXmlNode &base)TiXmlNode [friend]
      Parent()TiXmlNode [inline]
      PreviousSibling() const TiXmlNode [inline]
      PreviousSibling(const char *) const TiXmlNode
      PreviousSibling(const std::string &_value) const TiXmlNode [inline]
      PreviousSibling(const std::string &_value)TiXmlNode [inline]
      Print(FILE *cfile, int depth) const TiXmlText [virtual]
      RemoveChild(TiXmlNode *removeThis)TiXmlNode
      ReplaceChild(TiXmlNode *replaceThis, const TiXmlNode &withThis)TiXmlNode
      Row() const TiXmlBase [inline]
      SetCDATA(bool _cdata)TiXmlText [inline]
      SetCondenseWhiteSpace(bool condense)TiXmlBase [inline, static]
      SetUserData(void *user)TiXmlBase [inline]
      SetValue(const char *_value)TiXmlNode [inline]
      SetValue(const std::string &_value)TiXmlNode [inline]
      TiXmlText(const char *initValue)TiXmlText [inline]
      TiXmlText(const std::string &initValue)TiXmlText [inline]
      ToComment() const TiXmlNode [inline, virtual]
      ToComment()TiXmlNode [inline, virtual]
      ToDeclaration() const TiXmlNode [inline, virtual]
      ToDeclaration()TiXmlNode [inline, virtual]
      ToDocument() const TiXmlNode [inline, virtual]
      ToDocument()TiXmlNode [inline, virtual]
      ToElement() const TiXmlNode [inline, virtual]
      ToElement()TiXmlNode [inline, virtual]
      ToText() const TiXmlText [inline, virtual]
      ToText()TiXmlText [inline, virtual]
      ToUnknown() const TiXmlNode [inline, virtual]
      ToUnknown()TiXmlNode [inline, virtual]
      Type() const TiXmlNode [inline]
      userDataTiXmlBase [protected]
      Value() const TiXmlNode [inline]
      ValueStr() const TiXmlNode [inline]


      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/classTiXmlText.html b/Engine/lib/tinyxml/docs/classTiXmlText.html new file mode 100644 index 000000000..0dadfa95c --- /dev/null +++ b/Engine/lib/tinyxml/docs/classTiXmlText.html @@ -0,0 +1,145 @@ + + +TinyXml: TiXmlText Class Reference + + + + + + +

      TiXmlText Class Reference

      XML text. +More... +

      +#include <tinyxml.h> +

      +

      Inheritance diagram for TiXmlText: +

      + +TiXmlNode +TiXmlBase + +List of all members. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

      Public Member Functions

       TiXmlText (const char *initValue)
       Constructor for text element.
      TiXmlText (const std::string &initValue)
       Constructor.
      virtual void Print (FILE *cfile, int depth) const
       All TinyXml classes can print themselves to a filestream or the string class (TiXmlString in non-STL mode, std::string in STL mode.
      +bool CDATA () const
       Queries whether this represents text using a CDATA section.
      +void SetCDATA (bool _cdata)
       Turns on or off a CDATA representation of text.
      +virtual const TiXmlTextToText () const
       Cast to a more defined type. Will return null not of the requested type.
      +virtual TiXmlTextToText ()
       Cast to a more defined type. Will return null not of the requested type.
      +virtual bool Accept (TiXmlVisitor *content) const
       Walk the XML tree visiting this node and all of its children.

      Protected Member Functions

      +virtual TiXmlNodeClone () const
       [internal use] Creates a new Element and returns it.

      Friends

      +class TiXmlElement
      +

      Detailed Description

      +XML text. +

      +A text node can have 2 ways to output the next. "normal" output and CDATA. It will default to the mode it was parsed from the XML file and you generally want to leave it alone, but you can change the output mode with SetCDATA() and query it with CDATA(). +

      +


      Constructor & Destructor Documentation

      + +
      +
      + + + + + + + + + +
      TiXmlText::TiXmlText (const char *  initValue  )  [inline]
      +
      +
      + +

      +Constructor for text element. +

      +By default, it is treated as normal, encoded text. If you want it be output as a CDATA text element, set the parameter _cdata to 'true' +

      +

      +


      Member Function Documentation

      + +
      +
      + + + + + + + + + + + + + + + + + + +
      virtual void TiXmlText::Print (FILE *  cfile,
      int  depth 
      ) const [virtual]
      +
      +
      + +

      +All TinyXml classes can print themselves to a filestream or the string class (TiXmlString in non-STL mode, std::string in STL mode. +

      +) Either or both cfile and str can be null.

      +This is a formatted print, and will insert tabs and newlines.

      +(For an unformatted stream, use the << operator.) +

      +Implements TiXmlBase. +

      +

      +


      The documentation for this class was generated from the following file: +
      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/classTiXmlText.png b/Engine/lib/tinyxml/docs/classTiXmlText.png new file mode 100644 index 000000000..c9e71d430 Binary files /dev/null and b/Engine/lib/tinyxml/docs/classTiXmlText.png differ diff --git a/Engine/lib/tinyxml/docs/classTiXmlUnknown-members.html b/Engine/lib/tinyxml/docs/classTiXmlUnknown-members.html new file mode 100644 index 000000000..31e24973c --- /dev/null +++ b/Engine/lib/tinyxml/docs/classTiXmlUnknown-members.html @@ -0,0 +1,98 @@ + + +TinyXml: Member List + + + + + + +

      TiXmlUnknown Member List

      This is the complete list of members for TiXmlUnknown, including all inherited members.

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      Accept(TiXmlVisitor *content) const TiXmlUnknown [virtual]
      Clear()TiXmlNode
      Clone() const TiXmlUnknown [virtual]
      Column() const TiXmlBase [inline]
      EncodeString(const TIXML_STRING &str, TIXML_STRING *out)TiXmlBase [static]
      FirstChild() const TiXmlNode [inline]
      FirstChild(const char *value) const TiXmlNode
      FirstChild(const char *_value)TiXmlNode [inline]
      FirstChild(const std::string &_value) const TiXmlNode [inline]
      FirstChild(const std::string &_value)TiXmlNode [inline]
      FirstChildElement() const TiXmlNode
      FirstChildElement(const char *_value) const TiXmlNode
      FirstChildElement(const std::string &_value) const TiXmlNode [inline]
      FirstChildElement(const std::string &_value)TiXmlNode [inline]
      GetDocument() const TiXmlNode
      GetUserData()TiXmlBase [inline]
      GetUserData() const TiXmlBase [inline]
      InsertAfterChild(TiXmlNode *afterThis, const TiXmlNode &addThis)TiXmlNode
      InsertBeforeChild(TiXmlNode *beforeThis, const TiXmlNode &addThis)TiXmlNode
      InsertEndChild(const TiXmlNode &addThis)TiXmlNode
      IsWhiteSpaceCondensed()TiXmlBase [inline, static]
      IterateChildren(const TiXmlNode *previous) const TiXmlNode
      IterateChildren(const char *value, const TiXmlNode *previous) const TiXmlNode
      IterateChildren(const std::string &_value, const TiXmlNode *previous) const TiXmlNode [inline]
      IterateChildren(const std::string &_value, const TiXmlNode *previous)TiXmlNode [inline]
      LastChild()TiXmlNode [inline]
      LastChild(const char *_value)TiXmlNode [inline]
      LastChild(const std::string &_value) const TiXmlNode [inline]
      LastChild(const std::string &_value)TiXmlNode [inline]
      LinkEndChild(TiXmlNode *addThis)TiXmlNode
      NextSibling(const std::string &_value) const TiXmlNode [inline]
      NextSibling(const std::string &_value)TiXmlNode [inline]
      NextSibling() const TiXmlNode [inline]
      NextSibling(const char *) const TiXmlNode
      NextSiblingElement() const TiXmlNode
      NextSiblingElement(const char *) const TiXmlNode
      NextSiblingElement(const std::string &_value) const TiXmlNode [inline]
      NextSiblingElement(const std::string &_value)TiXmlNode [inline]
      NoChildren() const TiXmlNode [inline]
      NodeType enum nameTiXmlNode
      operator<<(std::ostream &out, const TiXmlNode &base)TiXmlNode [friend]
      operator<<(std::string &out, const TiXmlNode &base)TiXmlNode [friend]
      operator>>(std::istream &in, TiXmlNode &base)TiXmlNode [friend]
      Parent()TiXmlNode [inline]
      PreviousSibling() const TiXmlNode [inline]
      PreviousSibling(const char *) const TiXmlNode
      PreviousSibling(const std::string &_value) const TiXmlNode [inline]
      PreviousSibling(const std::string &_value)TiXmlNode [inline]
      Print(FILE *cfile, int depth) const TiXmlUnknown [virtual]
      RemoveChild(TiXmlNode *removeThis)TiXmlNode
      ReplaceChild(TiXmlNode *replaceThis, const TiXmlNode &withThis)TiXmlNode
      Row() const TiXmlBase [inline]
      SetCondenseWhiteSpace(bool condense)TiXmlBase [inline, static]
      SetUserData(void *user)TiXmlBase [inline]
      SetValue(const char *_value)TiXmlNode [inline]
      SetValue(const std::string &_value)TiXmlNode [inline]
      ToComment() const TiXmlNode [inline, virtual]
      ToComment()TiXmlNode [inline, virtual]
      ToDeclaration() const TiXmlNode [inline, virtual]
      ToDeclaration()TiXmlNode [inline, virtual]
      ToDocument() const TiXmlNode [inline, virtual]
      ToDocument()TiXmlNode [inline, virtual]
      ToElement() const TiXmlNode [inline, virtual]
      ToElement()TiXmlNode [inline, virtual]
      ToText() const TiXmlNode [inline, virtual]
      ToText()TiXmlNode [inline, virtual]
      ToUnknown() const TiXmlUnknown [inline, virtual]
      ToUnknown()TiXmlUnknown [inline, virtual]
      Type() const TiXmlNode [inline]
      userDataTiXmlBase [protected]
      Value() const TiXmlNode [inline]
      ValueStr() const TiXmlNode [inline]


      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/classTiXmlUnknown.html b/Engine/lib/tinyxml/docs/classTiXmlUnknown.html new file mode 100644 index 000000000..b1fa21879 --- /dev/null +++ b/Engine/lib/tinyxml/docs/classTiXmlUnknown.html @@ -0,0 +1,103 @@ + + +TinyXml: TiXmlUnknown Class Reference + + + + + + +

      TiXmlUnknown Class Reference

      Any tag that tinyXml doesn't recognize is saved as an unknown. +More... +

      +#include <tinyxml.h> +

      +

      Inheritance diagram for TiXmlUnknown: +

      + +TiXmlNode +TiXmlBase + +List of all members. + + + + + + + + + + + + + + + + + +

      Public Member Functions

      +virtual TiXmlNodeClone () const
       Creates a copy of this Unknown and returns it.
      virtual void Print (FILE *cfile, int depth) const
       All TinyXml classes can print themselves to a filestream or the string class (TiXmlString in non-STL mode, std::string in STL mode.
      +virtual const TiXmlUnknownToUnknown () const
       Cast to a more defined type. Will return null not of the requested type.
      +virtual TiXmlUnknownToUnknown ()
       Cast to a more defined type. Will return null not of the requested type.
      +virtual bool Accept (TiXmlVisitor *content) const
       Walk the XML tree visiting this node and all of its children.
      +

      Detailed Description

      +Any tag that tinyXml doesn't recognize is saved as an unknown. +

      +It is a tag of text, but should not be modified. It will be written back to the XML, unchanged, when the file is saved.

      +DTD tags get thrown into TiXmlUnknowns. +

      +


      Member Function Documentation

      + +
      +
      + + + + + + + + + + + + + + + + + + +
      virtual void TiXmlUnknown::Print (FILE *  cfile,
      int  depth 
      ) const [virtual]
      +
      +
      + +

      +All TinyXml classes can print themselves to a filestream or the string class (TiXmlString in non-STL mode, std::string in STL mode. +

      +) Either or both cfile and str can be null.

      +This is a formatted print, and will insert tabs and newlines.

      +(For an unformatted stream, use the << operator.) +

      +Implements TiXmlBase. +

      +

      +


      The documentation for this class was generated from the following file: +
      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/classTiXmlUnknown.png b/Engine/lib/tinyxml/docs/classTiXmlUnknown.png new file mode 100644 index 000000000..338bfab71 Binary files /dev/null and b/Engine/lib/tinyxml/docs/classTiXmlUnknown.png differ diff --git a/Engine/lib/tinyxml/docs/classTiXmlVisitor-members.html b/Engine/lib/tinyxml/docs/classTiXmlVisitor-members.html new file mode 100644 index 000000000..f6cf17491 --- /dev/null +++ b/Engine/lib/tinyxml/docs/classTiXmlVisitor-members.html @@ -0,0 +1,34 @@ + + +TinyXml: Member List + + + + + + +

      TiXmlVisitor Member List

      This is the complete list of members for TiXmlVisitor, including all inherited members.

      + + + + + + + + +
      Visit(const TiXmlDeclaration &)TiXmlVisitor [inline, virtual]
      Visit(const TiXmlText &)TiXmlVisitor [inline, virtual]
      Visit(const TiXmlComment &)TiXmlVisitor [inline, virtual]
      Visit(const TiXmlUnknown &)TiXmlVisitor [inline, virtual]
      VisitEnter(const TiXmlDocument &)TiXmlVisitor [inline, virtual]
      VisitEnter(const TiXmlElement &, const TiXmlAttribute *)TiXmlVisitor [inline, virtual]
      VisitExit(const TiXmlDocument &)TiXmlVisitor [inline, virtual]
      VisitExit(const TiXmlElement &)TiXmlVisitor [inline, virtual]


      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/classTiXmlVisitor.html b/Engine/lib/tinyxml/docs/classTiXmlVisitor.html new file mode 100644 index 000000000..4698b85ad --- /dev/null +++ b/Engine/lib/tinyxml/docs/classTiXmlVisitor.html @@ -0,0 +1,84 @@ + + +TinyXml: TiXmlVisitor Class Reference + + + + + + +

      TiXmlVisitor Class Reference

      If you call the Accept() method, it requires being passed a TiXmlVisitor class to handle callbacks. +More... +

      +#include <tinyxml.h> +

      +

      Inheritance diagram for TiXmlVisitor: +

      + +TiXmlPrinter + +List of all members. + + + + + + + + + + + + + + + + + + + + + + + + + + +

      Public Member Functions

      +virtual bool VisitEnter (const TiXmlDocument &)
       Visit a document.
      +virtual bool VisitExit (const TiXmlDocument &)
       Visit a document.
      +virtual bool VisitEnter (const TiXmlElement &, const TiXmlAttribute *)
       Visit an element.
      +virtual bool VisitExit (const TiXmlElement &)
       Visit an element.
      +virtual bool Visit (const TiXmlDeclaration &)
       Visit a declaration.
      +virtual bool Visit (const TiXmlText &)
       Visit a text node.
      +virtual bool Visit (const TiXmlComment &)
       Visit a comment node.
      +virtual bool Visit (const TiXmlUnknown &)
       Visit an unknow node.
      +

      Detailed Description

      +If you call the Accept() method, it requires being passed a TiXmlVisitor class to handle callbacks. +

      +For nodes that contain other nodes (Document, Element) you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves are simple called with Visit().

      +If you return 'true' from a Visit method, recursive parsing will continue. If you return false, no children of this node or its sibilings will be Visited.

      +All flavors of Visit methods have a default implementation that returns 'true' (continue visiting). You need to only override methods that are interesting to you.

      +Generally Accept() is called on the TiXmlDocument, although all nodes suppert Visiting.

      +You should never change the document from a callback.

      +

      See also:
      TiXmlNode::Accept()
      + +

      +


      The documentation for this class was generated from the following file: +
      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/classTiXmlVisitor.png b/Engine/lib/tinyxml/docs/classTiXmlVisitor.png new file mode 100644 index 000000000..3e7daece4 Binary files /dev/null and b/Engine/lib/tinyxml/docs/classTiXmlVisitor.png differ diff --git a/Engine/lib/tinyxml/docs/deprecated.html b/Engine/lib/tinyxml/docs/deprecated.html new file mode 100644 index 000000000..ccfb3f699 --- /dev/null +++ b/Engine/lib/tinyxml/docs/deprecated.html @@ -0,0 +1,38 @@ + + +TinyXml: Deprecated List + + + + + +

      Deprecated List

      +
      Member TiXmlHandle::Element () const
      +
      use ToElement. Return the handle as a TiXmlElement. This may return null.
      +
      +

      +

      +
      Member TiXmlHandle::Node () const
      +
      use ToNode. Return the handle as a TiXmlNode. This may return null.
      +
      +

      +

      +
      Member TiXmlHandle::Text () const
      +
      use ToText() Return the handle as a TiXmlText. This may return null.
      +
      +

      +

      +
      Member TiXmlHandle::Unknown () const
      +
      use ToUnknown() Return the handle as a TiXmlUnknown. This may return null.
      +
      +
      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/doxygen.css b/Engine/lib/tinyxml/docs/doxygen.css new file mode 100644 index 000000000..5d583694e --- /dev/null +++ b/Engine/lib/tinyxml/docs/doxygen.css @@ -0,0 +1,358 @@ +BODY,H1,H2,H3,H4,H5,H6,P,CENTER,TD,TH,UL,DL,DIV { + font-family: Geneva, Arial, Helvetica, sans-serif; +} +BODY,TD { + font-size: 90%; +} +H1 { + text-align: center; + font-size: 160%; +} +H2 { + font-size: 120%; +} +H3 { + font-size: 100%; +} +CAPTION { font-weight: bold } +DIV.qindex { + width: 100%; + background-color: #e8eef2; + border: 1px solid #84b0c7; + text-align: center; + margin: 2px; + padding: 2px; + line-height: 140%; +} +DIV.nav { + width: 100%; + background-color: #e8eef2; + border: 1px solid #84b0c7; + text-align: center; + margin: 2px; + padding: 2px; + line-height: 140%; +} +DIV.navtab { + background-color: #e8eef2; + border: 1px solid #84b0c7; + text-align: center; + margin: 2px; + margin-right: 15px; + padding: 2px; +} +TD.navtab { + font-size: 70%; +} +A.qindex { + text-decoration: none; + font-weight: bold; + color: #1A419D; +} +A.qindex:visited { + text-decoration: none; + font-weight: bold; + color: #1A419D +} +A.qindex:hover { + text-decoration: none; + background-color: #ddddff; +} +A.qindexHL { + text-decoration: none; + font-weight: bold; + background-color: #6666cc; + color: #ffffff; + border: 1px double #9295C2; +} +A.qindexHL:hover { + text-decoration: none; + background-color: #6666cc; + color: #ffffff; +} +A.qindexHL:visited { text-decoration: none; background-color: #6666cc; color: #ffffff } +A.el { text-decoration: none; font-weight: bold } +A.elRef { font-weight: bold } +A.code:link { text-decoration: none; font-weight: normal; color: #0000FF} +A.code:visited { text-decoration: none; font-weight: normal; color: #0000FF} +A.codeRef:link { font-weight: normal; color: #0000FF} +A.codeRef:visited { font-weight: normal; color: #0000FF} +A:hover { text-decoration: none; background-color: #f2f2ff } +DL.el { margin-left: -1cm } +.fragment { + font-family: monospace, fixed; + font-size: 95%; +} +PRE.fragment { + border: 1px solid #CCCCCC; + background-color: #f5f5f5; + margin-top: 4px; + margin-bottom: 4px; + margin-left: 2px; + margin-right: 8px; + padding-left: 6px; + padding-right: 6px; + padding-top: 4px; + padding-bottom: 4px; +} +DIV.ah { background-color: black; font-weight: bold; color: #ffffff; margin-bottom: 3px; margin-top: 3px } + +DIV.groupHeader { + margin-left: 16px; + margin-top: 12px; + margin-bottom: 6px; + font-weight: bold; +} +DIV.groupText { margin-left: 16px; font-style: italic; font-size: 90% } +BODY { + background: white; + color: black; + margin-right: 20px; + margin-left: 20px; +} +TD.indexkey { + background-color: #e8eef2; + font-weight: bold; + padding-right : 10px; + padding-top : 2px; + padding-left : 10px; + padding-bottom : 2px; + margin-left : 0px; + margin-right : 0px; + margin-top : 2px; + margin-bottom : 2px; + border: 1px solid #CCCCCC; +} +TD.indexvalue { + background-color: #e8eef2; + font-style: italic; + padding-right : 10px; + padding-top : 2px; + padding-left : 10px; + padding-bottom : 2px; + margin-left : 0px; + margin-right : 0px; + margin-top : 2px; + margin-bottom : 2px; + border: 1px solid #CCCCCC; +} +TR.memlist { + background-color: #f0f0f0; +} +P.formulaDsp { text-align: center; } +IMG.formulaDsp { } +IMG.formulaInl { vertical-align: middle; } +SPAN.keyword { color: #008000 } +SPAN.keywordtype { color: #604020 } +SPAN.keywordflow { color: #e08000 } +SPAN.comment { color: #800000 } +SPAN.preprocessor { color: #806020 } +SPAN.stringliteral { color: #002080 } +SPAN.charliteral { color: #008080 } +.mdescLeft { + padding: 0px 8px 4px 8px; + font-size: 80%; + font-style: italic; + background-color: #FAFAFA; + border-top: 1px none #E0E0E0; + border-right: 1px none #E0E0E0; + border-bottom: 1px none #E0E0E0; + border-left: 1px none #E0E0E0; + margin: 0px; +} +.mdescRight { + padding: 0px 8px 4px 8px; + font-size: 80%; + font-style: italic; + background-color: #FAFAFA; + border-top: 1px none #E0E0E0; + border-right: 1px none #E0E0E0; + border-bottom: 1px none #E0E0E0; + border-left: 1px none #E0E0E0; + margin: 0px; +} +.memItemLeft { + padding: 1px 0px 0px 8px; + margin: 4px; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-color: #E0E0E0; + border-right-color: #E0E0E0; + border-bottom-color: #E0E0E0; + border-left-color: #E0E0E0; + border-top-style: solid; + border-right-style: none; + border-bottom-style: none; + border-left-style: none; + background-color: #FAFAFA; + font-size: 80%; +} +.memItemRight { + padding: 1px 8px 0px 8px; + margin: 4px; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-color: #E0E0E0; + border-right-color: #E0E0E0; + border-bottom-color: #E0E0E0; + border-left-color: #E0E0E0; + border-top-style: solid; + border-right-style: none; + border-bottom-style: none; + border-left-style: none; + background-color: #FAFAFA; + font-size: 80%; +} +.memTemplItemLeft { + padding: 1px 0px 0px 8px; + margin: 4px; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-color: #E0E0E0; + border-right-color: #E0E0E0; + border-bottom-color: #E0E0E0; + border-left-color: #E0E0E0; + border-top-style: none; + border-right-style: none; + border-bottom-style: none; + border-left-style: none; + background-color: #FAFAFA; + font-size: 80%; +} +.memTemplItemRight { + padding: 1px 8px 0px 8px; + margin: 4px; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-color: #E0E0E0; + border-right-color: #E0E0E0; + border-bottom-color: #E0E0E0; + border-left-color: #E0E0E0; + border-top-style: none; + border-right-style: none; + border-bottom-style: none; + border-left-style: none; + background-color: #FAFAFA; + font-size: 80%; +} +.memTemplParams { + padding: 1px 0px 0px 8px; + margin: 4px; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-color: #E0E0E0; + border-right-color: #E0E0E0; + border-bottom-color: #E0E0E0; + border-left-color: #E0E0E0; + border-top-style: solid; + border-right-style: none; + border-bottom-style: none; + border-left-style: none; + color: #606060; + background-color: #FAFAFA; + font-size: 80%; +} +.search { color: #003399; + font-weight: bold; +} +FORM.search { + margin-bottom: 0px; + margin-top: 0px; +} +INPUT.search { font-size: 75%; + color: #000080; + font-weight: normal; + background-color: #e8eef2; +} +TD.tiny { font-size: 75%; +} +a { + color: #1A41A8; +} +a:visited { + color: #2A3798; +} +.dirtab { padding: 4px; + border-collapse: collapse; + border: 1px solid #84b0c7; +} +TH.dirtab { background: #e8eef2; + font-weight: bold; +} +HR { height: 1px; + border: none; + border-top: 1px solid black; +} + +/* Style for detailed member documentation */ +.memtemplate { + font-size: 80%; + color: #606060; + font-weight: normal; +} +.memnav { + background-color: #e8eef2; + border: 1px solid #84b0c7; + text-align: center; + margin: 2px; + margin-right: 15px; + padding: 2px; +} +.memitem { + padding: 4px; + background-color: #eef3f5; + border-width: 1px; + border-style: solid; + border-color: #dedeee; + -moz-border-radius: 8px 8px 8px 8px; +} +.memname { + white-space: nowrap; + font-weight: bold; +} +.memdoc{ + padding-left: 10px; +} +.memproto { + background-color: #d5e1e8; + width: 100%; + border-width: 1px; + border-style: solid; + border-color: #84b0c7; + font-weight: bold; + -moz-border-radius: 8px 8px 8px 8px; +} +.paramkey { + text-align: right; +} +.paramtype { + white-space: nowrap; +} +.paramname { + color: #602020; + font-style: italic; +} +/* End Styling for detailed member documentation */ + +/* for the tree view */ +.ftvtree { + font-family: sans-serif; + margin:0.5em; +} +.directory { font-size: 9pt; font-weight: bold; } +.directory h3 { margin: 0px; margin-top: 1em; font-size: 11pt; } +.directory > h3 { margin-top: 0; } +.directory p { margin: 0px; white-space: nowrap; } +.directory div { display: none; margin: 0px; } +.directory img { vertical-align: -30%; } + diff --git a/Engine/lib/tinyxml/docs/doxygen.png b/Engine/lib/tinyxml/docs/doxygen.png new file mode 100644 index 000000000..f0a274bba Binary files /dev/null and b/Engine/lib/tinyxml/docs/doxygen.png differ diff --git a/Engine/lib/tinyxml/docs/files.html b/Engine/lib/tinyxml/docs/files.html new file mode 100644 index 000000000..23a447d20 --- /dev/null +++ b/Engine/lib/tinyxml/docs/files.html @@ -0,0 +1,23 @@ + + +TinyXml: File Index + + + + + +

      TinyXml File List

      Here is a list of all documented files with brief descriptions: + + +
      tinystr.h [code]
      tinyxml.h [code]
      +
      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/functions.html b/Engine/lib/tinyxml/docs/functions.html new file mode 100644 index 000000000..e12432261 --- /dev/null +++ b/Engine/lib/tinyxml/docs/functions.html @@ -0,0 +1,196 @@ + + +TinyXml: Class Members + + + + + + + +
      + +
      + +

      +Here is a list of all documented class members with links to the class documentation for each member: +

      +

      - a -

      +

      - c -

      +

      - d -

      +

      - e -

      +

      - f -

      +

      - g -

      +

      - i -

      +

      - l -

      +

      - n -

      +

      - o -

      +

      - p -

      +

      - q -

      +

      - r -

      +

      - s -

      +

      - t -

      +

      - u -

      +

      - v -

      +
      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/functions_enum.html b/Engine/lib/tinyxml/docs/functions_enum.html new file mode 100644 index 000000000..563709ba7 --- /dev/null +++ b/Engine/lib/tinyxml/docs/functions_enum.html @@ -0,0 +1,39 @@ + + +TinyXml: Class Members - Enumerations + + + + + + + +  +

      +

      +
      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/functions_func.html b/Engine/lib/tinyxml/docs/functions_func.html new file mode 100644 index 000000000..99bdfbd3c --- /dev/null +++ b/Engine/lib/tinyxml/docs/functions_func.html @@ -0,0 +1,189 @@ + + +TinyXml: Class Members - Functions + + + + + + + +
      + +
      + +

      +  +

      +

      - a -

      +

      - c -

      +

      - d -

      +

      - e -

      +

      - f -

      +

      - g -

      +

      - i -

      +

      - l -

      +

      - n -

      +

      - p -

      +

      - q -

      +

      - r -

      +

      - s -

      +

      - t -

      +

      - u -

      +

      - v -

      +
      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/functions_rela.html b/Engine/lib/tinyxml/docs/functions_rela.html new file mode 100644 index 000000000..fddd43387 --- /dev/null +++ b/Engine/lib/tinyxml/docs/functions_rela.html @@ -0,0 +1,40 @@ + + +TinyXml: Class Members - Related Functions + + + + + + + +  +

      +

      +
      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/functions_vars.html b/Engine/lib/tinyxml/docs/functions_vars.html new file mode 100644 index 000000000..52e814cb3 --- /dev/null +++ b/Engine/lib/tinyxml/docs/functions_vars.html @@ -0,0 +1,39 @@ + + +TinyXml: Class Members - Variables + + + + + + + +  +

      +

      +
      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/hierarchy.html b/Engine/lib/tinyxml/docs/hierarchy.html new file mode 100644 index 000000000..0eb50b46f --- /dev/null +++ b/Engine/lib/tinyxml/docs/hierarchy.html @@ -0,0 +1,45 @@ + + +TinyXml: Hierarchical Index + + + + + + +

      TinyXml Class Hierarchy

      This inheritance list is sorted roughly, but not completely, alphabetically: +
      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/index.html b/Engine/lib/tinyxml/docs/index.html new file mode 100644 index 000000000..01d51e784 --- /dev/null +++ b/Engine/lib/tinyxml/docs/index.html @@ -0,0 +1,275 @@ + + +TinyXml: Main Page + + + + + +

      TinyXml Documentation

      +

      +

      2.5.3

      TinyXML

      +

      +TinyXML is a simple, small, C++ XML parser that can be easily integrated into other programs.

      +

      What it does.

      +

      +In brief, TinyXML parses an XML document, and builds from that a Document Object Model (DOM) that can be read, modified, and saved.

      +XML stands for "eXtensible Markup Language." It allows you to create your own document markups. Where HTML does a very good job of marking documents for browsers, XML allows you to define any kind of document markup, for example a document that describes a "to do" list for an organizer application. XML is a very structured and convenient format. All those random file formats created to store application data can all be replaced with XML. One parser for everything.

      +The best place for the complete, correct, and quite frankly hard to read spec is at http://www.w3.org/TR/2004/REC-xml-20040204/. An intro to XML (that I really like) can be found at http://skew.org/xml/tutorial.

      +There are different ways to access and interact with XML data. TinyXML uses a Document Object Model (DOM), meaning the XML data is parsed into a C++ objects that can be browsed and manipulated, and then written to disk or another output stream. You can also construct an XML document from scratch with C++ objects and write this to disk or another output stream.

      +TinyXML is designed to be easy and fast to learn. It is two headers and four cpp files. Simply add these to your project and off you go. There is an example file - xmltest.cpp - to get you started.

      +TinyXML is released under the ZLib license, so you can use it in open source or commercial code. The details of the license are at the top of every source file.

      +TinyXML attempts to be a flexible parser, but with truly correct and compliant XML output. TinyXML should compile on any reasonably C++ compliant system. It does not rely on exceptions or RTTI. It can be compiled with or without STL support. TinyXML fully supports the UTF-8 encoding, and the first 64k character entities.

      +

      What it doesn't do.

      +

      +TinyXML doesn't parse or use DTDs (Document Type Definitions) or XSLs (eXtensible Stylesheet Language.) There are other parsers out there (check out www.sourceforge.org, search for XML) that are much more fully featured. But they are also much bigger, take longer to set up in your project, have a higher learning curve, and often have a more restrictive license. If you are working with browsers or have more complete XML needs, TinyXML is not the parser for you.

      +The following DTD syntax will not parse at this time in TinyXML:

      +

      	<!DOCTYPE Archiv [
      +	 <!ELEMENT Comment (#PCDATA)>
      +	]>
      +

      +because TinyXML sees this as a !DOCTYPE node with an illegally embedded !ELEMENT node. This may be addressed in the future.

      +

      Tutorials.

      +

      +For the impatient, here is a tutorial to get you going. A great way to get started, but it is worth your time to read this (very short) manual completely.

      +

      +

      +

      Code Status.

      +

      +TinyXML is mature, tested code. It is very stable. If you find bugs, please file a bug report on the sourceforge web site (www.sourceforge.net/projects/tinyxml). We'll get them straightened out as soon as possible.

      +There are some areas of improvement; please check sourceforge if you are interested in working on TinyXML.

      +

      Related Projects

      +

      +TinyXML projects you may find useful! (Descriptions provided by the projects.)

      +

      +

      +

      Features

      +

      +

      Using STL

      +

      +TinyXML can be compiled to use or not use STL. When using STL, TinyXML uses the std::string class, and fully supports std::istream, std::ostream, operator<<, and operator>>. Many API methods have both 'const char*' and 'const std::string&' forms.

      +When STL support is compiled out, no STL files are included whatsoever. All the string classes are implemented by TinyXML itself. API methods all use the 'const char*' form for input.

      +Use the compile time define:

      +TIXML_USE_STL

      +to compile one version or the other. This can be passed by the compiler, or set as the first line of "tinyxml.h".

      +Note: If compiling the test code in Linux, setting the environment variable TINYXML_USE_STL=YES/NO will control STL compilation. In the Windows project file, STL and non STL targets are provided. In your project, It's probably easiest to add the line "#define TIXML_USE_STL" as the first line of tinyxml.h.

      +

      UTF-8

      +

      +TinyXML supports UTF-8 allowing to manipulate XML files in any language. TinyXML also supports "legacy mode" - the encoding used before UTF-8 support and probably best described as "extended ascii".

      +Normally, TinyXML will try to detect the correct encoding and use it. However, by setting the value of TIXML_DEFAULT_ENCODING in the header file, TinyXML can be forced to always use one encoding.

      +TinyXML will assume Legacy Mode until one of the following occurs:

        +
      1. +If the non-standard but common "UTF-8 lead bytes" (0xef 0xbb 0xbf) begin the file or data stream, TinyXML will read it as UTF-8.
      2. +
      3. +If the declaration tag is read, and it has an encoding="UTF-8", then TinyXML will read it as UTF-8.
      4. +
      5. +If the declaration tag is read, and it has no encoding specified, then TinyXML will read it as UTF-8.
      6. +
      7. +If the declaration tag is read, and it has an encoding="something else", then TinyXML will read it as Legacy Mode. In legacy mode, TinyXML will work as it did before. It's not clear what that mode does exactly, but old content should keep working.
      8. +
      9. +Until one of the above criteria is met, TinyXML runs in Legacy Mode.
      10. +
      +

      +What happens if the encoding is incorrectly set or detected? TinyXML will try to read and pass through text seen as improperly encoded. You may get some strange results or mangled characters. You may want to force TinyXML to the correct mode.

      +You may force TinyXML to Legacy Mode by using LoadFile( TIXML_ENCODING_LEGACY ) or LoadFile( filename, TIXML_ENCODING_LEGACY ). You may force it to use legacy mode all the time by setting TIXML_DEFAULT_ENCODING = TIXML_ENCODING_LEGACY. Likewise, you may force it to TIXML_ENCODING_UTF8 with the same technique.

      +For English users, using English XML, UTF-8 is the same as low-ASCII. You don't need to be aware of UTF-8 or change your code in any way. You can think of UTF-8 as a "superset" of ASCII.

      +UTF-8 is not a double byte format - but it is a standard encoding of Unicode! TinyXML does not use or directly support wchar, TCHAR, or Microsoft's _UNICODE at this time. It is common to see the term "Unicode" improperly refer to UTF-16, a wide byte encoding of unicode. This is a source of confusion.

      +For "high-ascii" languages - everything not English, pretty much - TinyXML can handle all languages, at the same time, as long as the XML is encoded in UTF-8. That can be a little tricky, older programs and operating systems tend to use the "default" or "traditional" code page. Many apps (and almost all modern ones) can output UTF-8, but older or stubborn (or just broken) ones still output text in the default code page.

      +For example, Japanese systems traditionally use SHIFT-JIS encoding. Text encoded as SHIFT-JIS can not be read by TinyXML. A good text editor can import SHIFT-JIS and then save as UTF-8.

      +The Skew.org link does a great job covering the encoding issue.

      +The test file "utf8test.xml" is an XML containing English, Spanish, Russian, and Simplified Chinese. (Hopefully they are translated correctly). The file "utf8test.gif" is a screen capture of the XML file, rendered in IE. Note that if you don't have the correct fonts (Simplified Chinese or Russian) on your system, you won't see output that matches the GIF file even if you can parse it correctly. Also note that (at least on my Windows machine) console output is in a Western code page, so that Print() or printf() cannot correctly display the file. This is not a bug in TinyXML - just an OS issue. No data is lost or destroyed by TinyXML. The console just doesn't render UTF-8.

      +

      Entities

      +

      +TinyXML recognizes the pre-defined "character entities", meaning special characters. Namely:

      +

      	&amp;	&
      +	&lt;	<
      +	&gt;	>
      +	&quot;	"
      +	&apos;	'
      +

      +These are recognized when the XML document is read, and translated to there UTF-8 equivalents. For instance, text with the XML of:

      +

      	Far &amp; Away
      +

      +will have the Value() of "Far & Away" when queried from the TiXmlText object, and will be written back to the XML stream/file as an ampersand. Older versions of TinyXML "preserved" character entities, but the newer versions will translate them into characters.

      +Additionally, any character can be specified by its Unicode code point: The syntax "&#xA0;" or "&#160;" are both to the non-breaking space characher.

      +

      Printing

      +

      +TinyXML can print output in several different ways that all have strengths and limitations.

      +

        +
      • Print( FILE* ). Output to a std-C stream, which includes all C files as well as stdout.
          +
        • "Pretty prints", but you don't have control over printing options.
        • The output is streamed directly to the FILE object, so there is no memory overhead in the TinyXML code.
        • used by Print() and SaveFile()
        +
      +

      +

        +
      • operator<<. Output to a c++ stream.
          +
        • Integrates with standart C++ iostreams.
        • Outputs in "network printing" mode without line breaks. Good for network transmission and moving XML between C++ objects, but hard for a human to read.
        +
      +

      +

        +
      • TiXmlPrinter. Output to a std::string or memory buffer.
          +
        • API is less concise
        • Future printing options will be put here.
        • Printing may change slightly in future versions as it is refined and expanded.
        +
      +

      +

      Streams

      +

      +With TIXML_USE_STL on TinyXML supports C++ streams (operator <<,>>) streams as well as C (FILE*) streams. There are some differences that you may need to be aware of.

      +C style output:

        +
      • based on FILE*
      • the Print() and SaveFile() methods
      +

      +Generates formatted output, with plenty of white space, intended to be as human-readable as possible. They are very fast, and tolerant of ill formed XML documents. For example, an XML document that contains 2 root elements and 2 declarations, will still print.

      +C style input:

        +
      • based on FILE*
      • the Parse() and LoadFile() methods
      +

      +A fast, tolerant read. Use whenever you don't need the C++ streams.

      +C++ style output:

        +
      • based on std::ostream
      • operator<<
      +

      +Generates condensed output, intended for network transmission rather than readability. Depending on your system's implementation of the ostream class, these may be somewhat slower. (Or may not.) Not tolerant of ill formed XML: a document should contain the correct one root element. Additional root level elements will not be streamed out.

      +C++ style input:

        +
      • based on std::istream
      • operator>>
      +

      +Reads XML from a stream, making it useful for network transmission. The tricky part is knowing when the XML document is complete, since there will almost certainly be other data in the stream. TinyXML will assume the XML data is complete after it reads the root element. Put another way, documents that are ill-constructed with more than one root element will not read correctly. Also note that operator>> is somewhat slower than Parse, due to both implementation of the STL and limitations of TinyXML.

      +

      White space

      +

      +The world simply does not agree on whether white space should be kept, or condensed. For example, pretend the '_' is a space, and look at "Hello____world". HTML, and at least some XML parsers, will interpret this as "Hello_world". They condense white space. Some XML parsers do not, and will leave it as "Hello____world". (Remember to keep pretending the _ is a space.) Others suggest that __Hello___world__ should become Hello___world.

      +It's an issue that hasn't been resolved to my satisfaction. TinyXML supports the first 2 approaches. Call TiXmlBase::SetCondenseWhiteSpace( bool ) to set the desired behavior. The default is to condense white space.

      +If you change the default, you should call TiXmlBase::SetCondenseWhiteSpace( bool ) before making any calls to Parse XML data, and I don't recommend changing it after it has been set.

      +

      Handles

      +

      +Where browsing an XML document in a robust way, it is important to check for null returns from method calls. An error safe implementation can generate a lot of code like:

      +

      TiXmlElement* root = document.FirstChildElement( "Document" );
      +if ( root )
      +{
      +	TiXmlElement* element = root->FirstChildElement( "Element" );
      +	if ( element )
      +	{
      +		TiXmlElement* child = element->FirstChildElement( "Child" );
      +		if ( child )
      +		{
      +			TiXmlElement* child2 = child->NextSiblingElement( "Child" );
      +			if ( child2 )
      +			{
      +				// Finally do something useful.
      +

      +Handles have been introduced to clean this up. Using the TiXmlHandle class, the previous code reduces to:

      +

      TiXmlHandle docHandle( &document );
      +TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement();
      +if ( child2 )
      +{
      +	// do something useful
      +

      +Which is much easier to deal with. See TiXmlHandle for more information.

      +

      Row and Column tracking

      +

      +Being able to track nodes and attributes back to their origin location in source files can be very important for some applications. Additionally, knowing where parsing errors occured in the original source can be very time saving.

      +TinyXML can tracks the row and column origin of all nodes and attributes in a text file. The TiXmlBase::Row() and TiXmlBase::Column() methods return the origin of the node in the source text. The correct tabs can be configured in TiXmlDocument::SetTabSize().

      +

      Using and Installing

      +

      +To Compile and Run xmltest:

      +A Linux Makefile and a Windows Visual C++ .dsw file is provided. Simply compile and run. It will write the file demotest.xml to your disk and generate output on the screen. It also tests walking the DOM by printing out the number of nodes found using different techniques.

      +The Linux makefile is very generic and runs on many systems - it is currently tested on mingw and MacOSX. You do not need to run 'make depend'. The dependecies have been hard coded.

      +

      Windows project file for VC6

      +

      +

        +
      • +tinyxml: tinyxml library, non-STL
      • +
      • +tinyxmlSTL: tinyxml library, STL
      • +
      • +tinyXmlTest: test app, non-STL
      • +
      • +tinyXmlTestSTL: test app, STL
      • +
      +

      +

      Makefile

      +

      +At the top of the makefile you can set:

      +PROFILE, DEBUG, and TINYXML_USE_STL. Details (such that they are) are in the makefile.

      +In the tinyxml directory, type "make clean" then "make". The executable file 'xmltest' will be created.

      +

      To Use in an Application:

      +

      +Add tinyxml.cpp, tinyxml.h, tinyxmlerror.cpp, tinyxmlparser.cpp, tinystr.cpp, and tinystr.h to your project or make file. That's it! It should compile on any reasonably compliant C++ system. You do not need to enable exceptions or RTTI for TinyXML.

      +

      How TinyXML works.

      +

      +An example is probably the best way to go. Take:

      	<?xml version="1.0" standalone=no>
      +	<!-- Our to do list data -->
      +	<ToDo>
      +		<Item priority="1"> Go to the <bold>Toy store!</bold></Item>
      +		<Item priority="2"> Do bills</Item>
      +	</ToDo>
      +

      +Its not much of a To Do list, but it will do. To read this file (say "demo.xml") you would create a document, and parse it in:

      	TiXmlDocument doc( "demo.xml" );
      +	doc.LoadFile();
      +

      +And its ready to go. Now lets look at some lines and how they relate to the DOM.

      +

      <?xml version="1.0" standalone=no>
      +

      +The first line is a declaration, and gets turned into the TiXmlDeclaration class. It will be the first child of the document node.

      +This is the only directive/special tag parsed by by TinyXML. Generally directive tags are stored in TiXmlUnknown so the commands wont be lost when it is saved back to disk.

      +

      <!-- Our to do list data -->
      +

      +A comment. Will become a TiXmlComment object.

      +

      <ToDo>
      +

      +The "ToDo" tag defines a TiXmlElement object. This one does not have any attributes, but does contain 2 other elements.

      +

      <Item priority="1"> 
      +

      +Creates another TiXmlElement which is a child of the "ToDo" element. This element has 1 attribute, with the name "priority" and the value "1".

      +

      Go to the
      +

      +A TiXmlText. This is a leaf node and cannot contain other nodes. It is a child of the "Item" TiXmlElement.

      +

      <bold>
      +

      +Another TiXmlElement, this one a child of the "Item" element.

      +Etc.

      +Looking at the entire object tree, you end up with:

      TiXmlDocument					"demo.xml"
      +	TiXmlDeclaration			"version='1.0'" "standalone=no"
      +	TiXmlComment				" Our to do list data"
      +	TiXmlElement				"ToDo"
      +		TiXmlElement			"Item" Attribtutes: priority = 1
      +			TiXmlText			"Go to the "
      +			TiXmlElement		"bold"
      +				TiXmlText		"Toy store!"
      +		TiXmlElement			"Item" Attributes: priority=2
      +			TiXmlText			"Do bills"
      +

      +

      Documentation

      +

      +The documentation is build with Doxygen, using the 'dox' configuration file.

      +

      License

      +

      +TinyXML is released under the zlib license:

      +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.

      +

      References

      +

      +The World Wide Web Consortium is the definitive standard body for XML, and there web pages contain huge amounts of information.

      +The definitive spec: http://www.w3.org/TR/2004/REC-xml-20040204/

      +I also recommend "XML Pocket Reference" by Robert Eckstein and published by OReilly...the book that got the whole thing started.

      +

      Contributors, Contacts, and a Brief History

      +

      +Thanks very much to everyone who sends suggestions, bugs, ideas, and encouragement. It all helps, and makes this project fun. A special thanks to the contributors on the web pages that keep it lively.

      +So many people have sent in bugs and ideas, that rather than list here we try to give credit due in the "changes.txt" file.

      +TinyXML was originally written by Lee Thomason. (Often the "I" still in the documentation.) Lee reviews changes and releases new versions, with the help of Yves Berquin, Andrew Ellerton, and the tinyXml community.

      +We appreciate your suggestions, and would love to know if you use TinyXML. Hopefully you will enjoy it and find it useful. Please post questions, comments, file bugs, or contact us at:

      +www.sourceforge.net/projects/tinyxml

      +Lee Thomason, Yves Berquin, Andrew Ellerton


      Generated on Sun May 6 15:41:22 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/pages.html b/Engine/lib/tinyxml/docs/pages.html new file mode 100644 index 000000000..b23a328b5 --- /dev/null +++ b/Engine/lib/tinyxml/docs/pages.html @@ -0,0 +1,23 @@ + + +TinyXml: Page Index + + + + + +

      TinyXml Related Pages

      Here is a list of all related documentation pages: +
      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/tab_b.gif b/Engine/lib/tinyxml/docs/tab_b.gif new file mode 100644 index 000000000..0d623483f Binary files /dev/null and b/Engine/lib/tinyxml/docs/tab_b.gif differ diff --git a/Engine/lib/tinyxml/docs/tab_l.gif b/Engine/lib/tinyxml/docs/tab_l.gif new file mode 100644 index 000000000..9b1e6337c Binary files /dev/null and b/Engine/lib/tinyxml/docs/tab_l.gif differ diff --git a/Engine/lib/tinyxml/docs/tab_r.gif b/Engine/lib/tinyxml/docs/tab_r.gif new file mode 100644 index 000000000..ce9dd9f53 Binary files /dev/null and b/Engine/lib/tinyxml/docs/tab_r.gif differ diff --git a/Engine/lib/tinyxml/docs/tabs.css b/Engine/lib/tinyxml/docs/tabs.css new file mode 100644 index 000000000..a61552a67 --- /dev/null +++ b/Engine/lib/tinyxml/docs/tabs.css @@ -0,0 +1,102 @@ +/* tabs styles, based on http://www.alistapart.com/articles/slidingdoors */ + +DIV.tabs +{ + float : left; + width : 100%; + background : url("tab_b.gif") repeat-x bottom; + margin-bottom : 4px; +} + +DIV.tabs UL +{ + margin : 0px; + padding-left : 10px; + list-style : none; +} + +DIV.tabs LI, DIV.tabs FORM +{ + display : inline; + margin : 0px; + padding : 0px; +} + +DIV.tabs FORM +{ + float : right; +} + +DIV.tabs A +{ + float : left; + background : url("tab_r.gif") no-repeat right top; + border-bottom : 1px solid #84B0C7; + font-size : x-small; + font-weight : bold; + text-decoration : none; +} + +DIV.tabs A:hover +{ + background-position: 100% -150px; +} + +DIV.tabs A:link, DIV.tabs A:visited, +DIV.tabs A:active, DIV.tabs A:hover +{ + color: #1A419D; +} + +DIV.tabs SPAN +{ + float : left; + display : block; + background : url("tab_l.gif") no-repeat left top; + padding : 5px 9px; + white-space : nowrap; +} + +DIV.tabs INPUT +{ + float : right; + display : inline; + font-size : 1em; +} + +DIV.tabs TD +{ + font-size : x-small; + font-weight : bold; + text-decoration : none; +} + + + +/* Commented Backslash Hack hides rule from IE5-Mac \*/ +DIV.tabs SPAN {float : none;} +/* End IE5-Mac hack */ + +DIV.tabs A:hover SPAN +{ + background-position: 0% -150px; +} + +DIV.tabs LI#current A +{ + background-position: 100% -150px; + border-width : 0px; +} + +DIV.tabs LI#current SPAN +{ + background-position: 0% -150px; + padding-bottom : 6px; +} + +DIV.nav +{ + background : none; + border : none; + border-bottom : 1px solid #84B0C7; +} diff --git a/Engine/lib/tinyxml/docs/tinystr_8h-source.html b/Engine/lib/tinyxml/docs/tinystr_8h-source.html new file mode 100644 index 000000000..71408eed9 --- /dev/null +++ b/Engine/lib/tinyxml/docs/tinystr_8h-source.html @@ -0,0 +1,338 @@ + + +TinyXml: tinystr.h Source File + + + + + +

      tinystr.h

      00001 /*
      +00002 www.sourceforge.net/projects/tinyxml
      +00003 Original file by Yves Berquin.
      +00004 
      +00005 This software is provided 'as-is', without any express or implied
      +00006 warranty. In no event will the authors be held liable for any
      +00007 damages arising from the use of this software.
      +00008 
      +00009 Permission is granted to anyone to use this software for any
      +00010 purpose, including commercial applications, and to alter it and
      +00011 redistribute it freely, subject to the following restrictions:
      +00012 
      +00013 1. The origin of this software must not be misrepresented; you must
      +00014 not claim that you wrote the original software. If you use this
      +00015 software in a product, an acknowledgment in the product documentation
      +00016 would be appreciated but is not required.
      +00017 
      +00018 2. Altered source versions must be plainly marked as such, and
      +00019 must not be misrepresented as being the original software.
      +00020 
      +00021 3. This notice may not be removed or altered from any source
      +00022 distribution.
      +00023 */
      +00024 
      +00025 /*
      +00026  * THIS FILE WAS ALTERED BY Tyge Lovset, 7. April 2005.
      +00027  *
      +00028  * - completely rewritten. compact, clean, and fast implementation.
      +00029  * - sizeof(TiXmlString) = pointer size (4 bytes on 32-bit systems)
      +00030  * - fixed reserve() to work as per specification.
      +00031  * - fixed buggy compares operator==(), operator<(), and operator>()
      +00032  * - fixed operator+=() to take a const ref argument, following spec.
      +00033  * - added "copy" constructor with length, and most compare operators.
      +00034  * - added swap(), clear(), size(), capacity(), operator+().
      +00035  */
      +00036 
      +00037 #ifndef TIXML_USE_STL
      +00038 
      +00039 #ifndef TIXML_STRING_INCLUDED
      +00040 #define TIXML_STRING_INCLUDED
      +00041 
      +00042 #include <assert.h>
      +00043 #include <string.h>
      +00044 
      +00045 /*  The support for explicit isn't that universal, and it isn't really
      +00046     required - it is used to check that the TiXmlString class isn't incorrectly
      +00047     used. Be nice to old compilers and macro it here:
      +00048 */
      +00049 #if defined(_MSC_VER) && (_MSC_VER >= 1200 )
      +00050     // Microsoft visual studio, version 6 and higher.
      +00051     #define TIXML_EXPLICIT explicit
      +00052 #elif defined(__GNUC__) && (__GNUC__ >= 3 )
      +00053     // GCC version 3 and higher.s
      +00054     #define TIXML_EXPLICIT explicit
      +00055 #else
      +00056     #define TIXML_EXPLICIT
      +00057 #endif
      +00058 
      +00059 
      +00060 /*
      +00061    TiXmlString is an emulation of a subset of the std::string template.
      +00062    Its purpose is to allow compiling TinyXML on compilers with no or poor STL support.
      +00063    Only the member functions relevant to the TinyXML project have been implemented.
      +00064    The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase
      +00065    a string and there's no more room, we allocate a buffer twice as big as we need.
      +00066 */
      +00067 class TiXmlString
      +00068 {
      +00069   public :
      +00070     // The size type used
      +00071     typedef size_t size_type;
      +00072 
      +00073     // Error value for find primitive
      +00074     static const size_type npos; // = -1;
      +00075 
      +00076 
      +00077     // TiXmlString empty constructor
      +00078     TiXmlString () : rep_(&nullrep_)
      +00079     {
      +00080     }
      +00081 
      +00082     // TiXmlString copy constructor
      +00083     TiXmlString ( const TiXmlString & copy) : rep_(0)
      +00084     {
      +00085         init(copy.length());
      +00086         memcpy(start(), copy.data(), length());
      +00087     }
      +00088 
      +00089     // TiXmlString constructor, based on a string
      +00090     TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0)
      +00091     {
      +00092         init( static_cast<size_type>( strlen(copy) ));
      +00093         memcpy(start(), copy, length());
      +00094     }
      +00095 
      +00096     // TiXmlString constructor, based on a string
      +00097     TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0)
      +00098     {
      +00099         init(len);
      +00100         memcpy(start(), str, len);
      +00101     }
      +00102 
      +00103     // TiXmlString destructor
      +00104     ~TiXmlString ()
      +00105     {
      +00106         quit();
      +00107     }
      +00108 
      +00109     // = operator
      +00110     TiXmlString& operator = (const char * copy)
      +00111     {
      +00112         return assign( copy, (size_type)strlen(copy));
      +00113     }
      +00114 
      +00115     // = operator
      +00116     TiXmlString& operator = (const TiXmlString & copy)
      +00117     {
      +00118         return assign(copy.start(), copy.length());
      +00119     }
      +00120 
      +00121 
      +00122     // += operator. Maps to append
      +00123     TiXmlString& operator += (const char * suffix)
      +00124     {
      +00125         return append(suffix, static_cast<size_type>( strlen(suffix) ));
      +00126     }
      +00127 
      +00128     // += operator. Maps to append
      +00129     TiXmlString& operator += (char single)
      +00130     {
      +00131         return append(&single, 1);
      +00132     }
      +00133 
      +00134     // += operator. Maps to append
      +00135     TiXmlString& operator += (const TiXmlString & suffix)
      +00136     {
      +00137         return append(suffix.data(), suffix.length());
      +00138     }
      +00139 
      +00140 
      +00141     // Convert a TiXmlString into a null-terminated char *
      +00142     const char * c_str () const { return rep_->str; }
      +00143 
      +00144     // Convert a TiXmlString into a char * (need not be null terminated).
      +00145     const char * data () const { return rep_->str; }
      +00146 
      +00147     // Return the length of a TiXmlString
      +00148     size_type length () const { return rep_->size; }
      +00149 
      +00150     // Alias for length()
      +00151     size_type size () const { return rep_->size; }
      +00152 
      +00153     // Checks if a TiXmlString is empty
      +00154     bool empty () const { return rep_->size == 0; }
      +00155 
      +00156     // Return capacity of string
      +00157     size_type capacity () const { return rep_->capacity; }
      +00158 
      +00159 
      +00160     // single char extraction
      +00161     const char& at (size_type index) const
      +00162     {
      +00163         assert( index < length() );
      +00164         return rep_->str[ index ];
      +00165     }
      +00166 
      +00167     // [] operator
      +00168     char& operator [] (size_type index) const
      +00169     {
      +00170         assert( index < length() );
      +00171         return rep_->str[ index ];
      +00172     }
      +00173 
      +00174     // find a char in a string. Return TiXmlString::npos if not found
      +00175     size_type find (char lookup) const
      +00176     {
      +00177         return find(lookup, 0);
      +00178     }
      +00179 
      +00180     // find a char in a string from an offset. Return TiXmlString::npos if not found
      +00181     size_type find (char tofind, size_type offset) const
      +00182     {
      +00183         if (offset >= length()) return npos;
      +00184 
      +00185         for (const char* p = c_str() + offset; *p != '\0'; ++p)
      +00186         {
      +00187            if (*p == tofind) return static_cast< size_type >( p - c_str() );
      +00188         }
      +00189         return npos;
      +00190     }
      +00191 
      +00192     void clear ()
      +00193     {
      +00194         //Lee:
      +00195         //The original was just too strange, though correct:
      +00196         //  TiXmlString().swap(*this);
      +00197         //Instead use the quit & re-init:
      +00198         quit();
      +00199         init(0,0);
      +00200     }
      +00201 
      +00202     /*  Function to reserve a big amount of data when we know we'll need it. Be aware that this
      +00203         function DOES NOT clear the content of the TiXmlString if any exists.
      +00204     */
      +00205     void reserve (size_type cap);
      +00206 
      +00207     TiXmlString& assign (const char* str, size_type len);
      +00208 
      +00209     TiXmlString& append (const char* str, size_type len);
      +00210 
      +00211     void swap (TiXmlString& other)
      +00212     {
      +00213         Rep* r = rep_;
      +00214         rep_ = other.rep_;
      +00215         other.rep_ = r;
      +00216     }
      +00217 
      +00218   private:
      +00219 
      +00220     void init(size_type sz) { init(sz, sz); }
      +00221     void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; }
      +00222     char* start() const { return rep_->str; }
      +00223     char* finish() const { return rep_->str + rep_->size; }
      +00224 
      +00225     struct Rep
      +00226     {
      +00227         size_type size, capacity;
      +00228         char str[1];
      +00229     };
      +00230 
      +00231     void init(size_type sz, size_type cap)
      +00232     {
      +00233         if (cap)
      +00234         {
      +00235             // Lee: the original form:
      +00236             //  rep_ = static_cast<Rep*>(operator new(sizeof(Rep) + cap));
      +00237             // doesn't work in some cases of new being overloaded. Switching
      +00238             // to the normal allocation, although use an 'int' for systems
      +00239             // that are overly picky about structure alignment.
      +00240             const size_type bytesNeeded = sizeof(Rep) + cap;
      +00241             const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int ); 
      +00242             rep_ = reinterpret_cast<Rep*>( new int[ intsNeeded ] );
      +00243 
      +00244             rep_->str[ rep_->size = sz ] = '\0';
      +00245             rep_->capacity = cap;
      +00246         }
      +00247         else
      +00248         {
      +00249             rep_ = &nullrep_;
      +00250         }
      +00251     }
      +00252 
      +00253     void quit()
      +00254     {
      +00255         if (rep_ != &nullrep_)
      +00256         {
      +00257             // The rep_ is really an array of ints. (see the allocator, above).
      +00258             // Cast it back before delete, so the compiler won't incorrectly call destructors.
      +00259             delete [] ( reinterpret_cast<int*>( rep_ ) );
      +00260         }
      +00261     }
      +00262 
      +00263     Rep * rep_;
      +00264     static Rep nullrep_;
      +00265 
      +00266 } ;
      +00267 
      +00268 
      +00269 inline bool operator == (const TiXmlString & a, const TiXmlString & b)
      +00270 {
      +00271     return    ( a.length() == b.length() )              // optimization on some platforms
      +00272            && ( strcmp(a.c_str(), b.c_str()) == 0 );    // actual compare
      +00273 }
      +00274 inline bool operator < (const TiXmlString & a, const TiXmlString & b)
      +00275 {
      +00276     return strcmp(a.c_str(), b.c_str()) < 0;
      +00277 }
      +00278 
      +00279 inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); }
      +00280 inline bool operator >  (const TiXmlString & a, const TiXmlString & b) { return b < a; }
      +00281 inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); }
      +00282 inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); }
      +00283 
      +00284 inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; }
      +00285 inline bool operator == (const char* a, const TiXmlString & b) { return b == a; }
      +00286 inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); }
      +00287 inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); }
      +00288 
      +00289 TiXmlString operator + (const TiXmlString & a, const TiXmlString & b);
      +00290 TiXmlString operator + (const TiXmlString & a, const char* b);
      +00291 TiXmlString operator + (const char* a, const TiXmlString & b);
      +00292 
      +00293 
      +00294 /*
      +00295    TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString.
      +00296    Only the operators that we need for TinyXML have been developped.
      +00297 */
      +00298 class TiXmlOutStream : public TiXmlString
      +00299 {
      +00300 public :
      +00301 
      +00302     // TiXmlOutStream << operator.
      +00303     TiXmlOutStream & operator << (const TiXmlString & in)
      +00304     {
      +00305         *this += in;
      +00306         return *this;
      +00307     }
      +00308 
      +00309     // TiXmlOutStream << operator.
      +00310     TiXmlOutStream & operator << (const char * in)
      +00311     {
      +00312         *this += in;
      +00313         return *this;
      +00314     }
      +00315 
      +00316 } ;
      +00317 
      +00318 #endif  // TIXML_STRING_INCLUDED
      +00319 #endif  // TIXML_USE_STL
      +

      Generated on Sun May 6 15:41:22 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/tinyxml_8h-source.html b/Engine/lib/tinyxml/docs/tinyxml_8h-source.html new file mode 100644 index 000000000..135da9ce6 --- /dev/null +++ b/Engine/lib/tinyxml/docs/tinyxml_8h-source.html @@ -0,0 +1,1201 @@ + + +TinyXml: tinyxml.h Source File + + + + + +

      tinyxml.h

      00001 /*
      +00002 www.sourceforge.net/projects/tinyxml
      +00003 Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com)
      +00004 
      +00005 This software is provided 'as-is', without any express or implied
      +00006 warranty. In no event will the authors be held liable for any
      +00007 damages arising from the use of this software.
      +00008 
      +00009 Permission is granted to anyone to use this software for any
      +00010 purpose, including commercial applications, and to alter it and
      +00011 redistribute it freely, subject to the following restrictions:
      +00012 
      +00013 1. The origin of this software must not be misrepresented; you must
      +00014 not claim that you wrote the original software. If you use this
      +00015 software in a product, an acknowledgment in the product documentation
      +00016 would be appreciated but is not required.
      +00017 
      +00018 2. Altered source versions must be plainly marked as such, and
      +00019 must not be misrepresented as being the original software.
      +00020 
      +00021 3. This notice may not be removed or altered from any source
      +00022 distribution.
      +00023 */
      +00024 
      +00025 
      +00026 #ifndef TINYXML_INCLUDED
      +00027 #define TINYXML_INCLUDED
      +00028 
      +00029 #ifdef _MSC_VER
      +00030 #pragma warning( push )
      +00031 #pragma warning( disable : 4530 )
      +00032 #pragma warning( disable : 4786 )
      +00033 #endif
      +00034 
      +00035 #include <ctype.h>
      +00036 #include <stdio.h>
      +00037 #include <stdlib.h>
      +00038 #include <string.h>
      +00039 #include <assert.h>
      +00040 
      +00041 // Help out windows:
      +00042 #if defined( _DEBUG ) && !defined( DEBUG )
      +00043 #define DEBUG
      +00044 #endif
      +00045 
      +00046 #ifdef TIXML_USE_STL
      +00047     #include <string>
      +00048     #include <iostream>
      +00049     #include <sstream>
      +00050     #define TIXML_STRING        std::string
      +00051 #else
      +00052     #include "tinystr.h"
      +00053     #define TIXML_STRING        TiXmlString
      +00054 #endif
      +00055 
      +00056 // Deprecated library function hell. Compilers want to use the
      +00057 // new safe versions. This probably doesn't fully address the problem,
      +00058 // but it gets closer. There are too many compilers for me to fully
      +00059 // test. If you get compilation troubles, undefine TIXML_SAFE
      +00060 #define TIXML_SAFE
      +00061 
      +00062 #ifdef TIXML_SAFE
      +00063     #if defined(_MSC_VER) && (_MSC_VER >= 1400 )
      +00064         // Microsoft visual studio, version 2005 and higher.
      +00065         #define TIXML_SNPRINTF _snprintf_s
      +00066         #define TIXML_SNSCANF  _snscanf_s
      +00067         #define TIXML_SSCANF   sscanf_s
      +00068     #elif defined(_MSC_VER) && (_MSC_VER >= 1200 )
      +00069         // Microsoft visual studio, version 6 and higher.
      +00070         //#pragma message( "Using _sn* functions." )
      +00071         #define TIXML_SNPRINTF _snprintf
      +00072         #define TIXML_SNSCANF  _snscanf
      +00073         #define TIXML_SSCANF   sscanf
      +00074     #elif defined(__GNUC__) && (__GNUC__ >= 3 )
      +00075         // GCC version 3 and higher.s
      +00076         //#warning( "Using sn* functions." )
      +00077         #define TIXML_SNPRINTF snprintf
      +00078         #define TIXML_SNSCANF  snscanf
      +00079         #define TIXML_SSCANF   sscanf
      +00080     #else
      +00081         #define TIXML_SSCANF   sscanf
      +00082     #endif
      +00083 #endif  
      +00084 
      +00085 class TiXmlDocument;
      +00086 class TiXmlElement;
      +00087 class TiXmlComment;
      +00088 class TiXmlUnknown;
      +00089 class TiXmlAttribute;
      +00090 class TiXmlText;
      +00091 class TiXmlDeclaration;
      +00092 class TiXmlParsingData;
      +00093 
      +00094 const int TIXML_MAJOR_VERSION = 2;
      +00095 const int TIXML_MINOR_VERSION = 5;
      +00096 const int TIXML_PATCH_VERSION = 3;
      +00097 
      +00098 /*  Internal structure for tracking location of items 
      +00099     in the XML file.
      +00100 */
      +00101 struct TiXmlCursor
      +00102 {
      +00103     TiXmlCursor()       { Clear(); }
      +00104     void Clear()        { row = col = -1; }
      +00105 
      +00106     int row;    // 0 based.
      +00107     int col;    // 0 based.
      +00108 };
      +00109 
      +00110 
      +00129 class TiXmlVisitor
      +00130 {
      +00131 public:
      +00132     virtual ~TiXmlVisitor() {}
      +00133 
      +00135     virtual bool VisitEnter( const TiXmlDocument& /*doc*/ )         { return true; }
      +00137     virtual bool VisitExit( const TiXmlDocument& /*doc*/ )          { return true; }
      +00138 
      +00140     virtual bool VisitEnter( const TiXmlElement& /*element*/, const TiXmlAttribute* /*firstAttribute*/ )    { return true; }
      +00142     virtual bool VisitExit( const TiXmlElement& /*element*/ )       { return true; }
      +00143 
      +00145     virtual bool Visit( const TiXmlDeclaration& /*declaration*/ )   { return true; }
      +00147     virtual bool Visit( const TiXmlText& /*text*/ )                 { return true; }
      +00149     virtual bool Visit( const TiXmlComment& /*comment*/ )           { return true; }
      +00151     virtual bool Visit( const TiXmlUnknown& /*unknown*/ )           { return true; }
      +00152 };
      +00153 
      +00154 // Only used by Attribute::Query functions
      +00155 enum 
      +00156 { 
      +00157     TIXML_SUCCESS,
      +00158     TIXML_NO_ATTRIBUTE,
      +00159     TIXML_WRONG_TYPE
      +00160 };
      +00161 
      +00162 
      +00163 // Used by the parsing routines.
      +00164 enum TiXmlEncoding
      +00165 {
      +00166     TIXML_ENCODING_UNKNOWN,
      +00167     TIXML_ENCODING_UTF8,
      +00168     TIXML_ENCODING_LEGACY
      +00169 };
      +00170 
      +00171 const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN;
      +00172 
      +00195 class TiXmlBase
      +00196 {
      +00197     friend class TiXmlNode;
      +00198     friend class TiXmlElement;
      +00199     friend class TiXmlDocument;
      +00200 
      +00201 public:
      +00202     TiXmlBase() :   userData(0)     {}
      +00203     virtual ~TiXmlBase()            {}
      +00204 
      +00214     virtual void Print( FILE* cfile, int depth ) const = 0;
      +00215 
      +00222     static void SetCondenseWhiteSpace( bool condense )      { condenseWhiteSpace = condense; }
      +00223 
      +00225     static bool IsWhiteSpaceCondensed()                     { return condenseWhiteSpace; }
      +00226 
      +00245     int Row() const         { return location.row + 1; }
      +00246     int Column() const      { return location.col + 1; }    
      +00247 
      +00248     void  SetUserData( void* user )         { userData = user; }    
      +00249     void* GetUserData()                     { return userData; }    
      +00250     const void* GetUserData() const         { return userData; }    
      +00251 
      +00252     // Table that returs, for a given lead byte, the total number of bytes
      +00253     // in the UTF-8 sequence.
      +00254     static const int utf8ByteTable[256];
      +00255 
      +00256     virtual const char* Parse(  const char* p, 
      +00257                                 TiXmlParsingData* data, 
      +00258                                 TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0;
      +00259 
      +00263     static void EncodeString( const TIXML_STRING& str, TIXML_STRING* out );
      +00264 
      +00265     enum
      +00266     {
      +00267         TIXML_NO_ERROR = 0,
      +00268         TIXML_ERROR,
      +00269         TIXML_ERROR_OPENING_FILE,
      +00270         TIXML_ERROR_OUT_OF_MEMORY,
      +00271         TIXML_ERROR_PARSING_ELEMENT,
      +00272         TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME,
      +00273         TIXML_ERROR_READING_ELEMENT_VALUE,
      +00274         TIXML_ERROR_READING_ATTRIBUTES,
      +00275         TIXML_ERROR_PARSING_EMPTY,
      +00276         TIXML_ERROR_READING_END_TAG,
      +00277         TIXML_ERROR_PARSING_UNKNOWN,
      +00278         TIXML_ERROR_PARSING_COMMENT,
      +00279         TIXML_ERROR_PARSING_DECLARATION,
      +00280         TIXML_ERROR_DOCUMENT_EMPTY,
      +00281         TIXML_ERROR_EMBEDDED_NULL,
      +00282         TIXML_ERROR_PARSING_CDATA,
      +00283         TIXML_ERROR_DOCUMENT_TOP_ONLY,
      +00284 
      +00285         TIXML_ERROR_STRING_COUNT
      +00286     };
      +00287 
      +00288 protected:
      +00289 
      +00290     static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding );
      +00291     inline static bool IsWhiteSpace( char c )       
      +00292     { 
      +00293         return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); 
      +00294     }
      +00295     inline static bool IsWhiteSpace( int c )
      +00296     {
      +00297         if ( c < 256 )
      +00298             return IsWhiteSpace( (char) c );
      +00299         return false;   // Again, only truly correct for English/Latin...but usually works.
      +00300     }
      +00301 
      +00302     #ifdef TIXML_USE_STL
      +00303     static bool StreamWhiteSpace( std::istream * in, TIXML_STRING * tag );
      +00304     static bool StreamTo( std::istream * in, int character, TIXML_STRING * tag );
      +00305     #endif
      +00306 
      +00307     /*  Reads an XML name into the string provided. Returns
      +00308         a pointer just past the last character of the name,
      +00309         or 0 if the function has an error.
      +00310     */
      +00311     static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding );
      +00312 
      +00313     /*  Reads text. Returns a pointer past the given end tag.
      +00314         Wickedly complex options, but it keeps the (sensitive) code in one place.
      +00315     */
      +00316     static const char* ReadText(    const char* in,             // where to start
      +00317                                     TIXML_STRING* text,         // the string read
      +00318                                     bool ignoreWhiteSpace,      // whether to keep the white space
      +00319                                     const char* endTag,         // what ends this text
      +00320                                     bool ignoreCase,            // whether to ignore case in the end tag
      +00321                                     TiXmlEncoding encoding );   // the current encoding
      +00322 
      +00323     // If an entity has been found, transform it into a character.
      +00324     static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding );
      +00325 
      +00326     // Get a character, while interpreting entities.
      +00327     // The length can be from 0 to 4 bytes.
      +00328     inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding )
      +00329     {
      +00330         assert( p );
      +00331         if ( encoding == TIXML_ENCODING_UTF8 )
      +00332         {
      +00333             *length = utf8ByteTable[ *((const unsigned char*)p) ];
      +00334             assert( *length >= 0 && *length < 5 );
      +00335         }
      +00336         else
      +00337         {
      +00338             *length = 1;
      +00339         }
      +00340 
      +00341         if ( *length == 1 )
      +00342         {
      +00343             if ( *p == '&' )
      +00344                 return GetEntity( p, _value, length, encoding );
      +00345             *_value = *p;
      +00346             return p+1;
      +00347         }
      +00348         else if ( *length )
      +00349         {
      +00350             //strncpy( _value, p, *length );    // lots of compilers don't like this function (unsafe),
      +00351                                                 // and the null terminator isn't needed
      +00352             for( int i=0; p[i] && i<*length; ++i ) {
      +00353                 _value[i] = p[i];
      +00354             }
      +00355             return p + (*length);
      +00356         }
      +00357         else
      +00358         {
      +00359             // Not valid text.
      +00360             return 0;
      +00361         }
      +00362     }
      +00363 
      +00364     // Return true if the next characters in the stream are any of the endTag sequences.
      +00365     // Ignore case only works for english, and should only be relied on when comparing
      +00366     // to English words: StringEqual( p, "version", true ) is fine.
      +00367     static bool StringEqual(    const char* p,
      +00368                                 const char* endTag,
      +00369                                 bool ignoreCase,
      +00370                                 TiXmlEncoding encoding );
      +00371 
      +00372     static const char* errorString[ TIXML_ERROR_STRING_COUNT ];
      +00373 
      +00374     TiXmlCursor location;
      +00375 
      +00377     void*           userData;
      +00378     
      +00379     // None of these methods are reliable for any language except English.
      +00380     // Good for approximation, not great for accuracy.
      +00381     static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding );
      +00382     static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding );
      +00383     inline static int ToLower( int v, TiXmlEncoding encoding )
      +00384     {
      +00385         if ( encoding == TIXML_ENCODING_UTF8 )
      +00386         {
      +00387             if ( v < 128 ) return tolower( v );
      +00388             return v;
      +00389         }
      +00390         else
      +00391         {
      +00392             return tolower( v );
      +00393         }
      +00394     }
      +00395     static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length );
      +00396 
      +00397 private:
      +00398     TiXmlBase( const TiXmlBase& );              // not implemented.
      +00399     void operator=( const TiXmlBase& base );    // not allowed.
      +00400 
      +00401     struct Entity
      +00402     {
      +00403         const char*     str;
      +00404         unsigned int    strLength;
      +00405         char            chr;
      +00406     };
      +00407     enum
      +00408     {
      +00409         NUM_ENTITY = 5,
      +00410         MAX_ENTITY_LENGTH = 6
      +00411 
      +00412     };
      +00413     static Entity entity[ NUM_ENTITY ];
      +00414     static bool condenseWhiteSpace;
      +00415 };
      +00416 
      +00417 
      +00424 class TiXmlNode : public TiXmlBase
      +00425 {
      +00426     friend class TiXmlDocument;
      +00427     friend class TiXmlElement;
      +00428 
      +00429 public:
      +00430     #ifdef TIXML_USE_STL    
      +00431 
      +00435         friend std::istream& operator >> (std::istream& in, TiXmlNode& base);
      +00436 
      +00453         friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base);
      +00454 
      +00456         friend std::string& operator<< (std::string& out, const TiXmlNode& base );
      +00457 
      +00458     #endif
      +00459 
      +00463     enum NodeType
      +00464     {
      +00465         DOCUMENT,
      +00466         ELEMENT,
      +00467         COMMENT,
      +00468         UNKNOWN,
      +00469         TEXT,
      +00470         DECLARATION,
      +00471         TYPECOUNT
      +00472     };
      +00473 
      +00474     virtual ~TiXmlNode();
      +00475 
      +00488     const char *Value() const { return value.c_str (); }
      +00489 
      +00490     #ifdef TIXML_USE_STL
      +00491 
      +00495     const std::string& ValueStr() const { return value; }
      +00496     #endif
      +00497 
      +00498     const TIXML_STRING& ValueTStr() const { return value; }
      +00499 
      +00509     void SetValue(const char * _value) { value = _value;}
      +00510 
      +00511     #ifdef TIXML_USE_STL
      +00513     void SetValue( const std::string& _value )  { value = _value; }
      +00514     #endif
      +00515 
      +00517     void Clear();
      +00518 
      +00520     TiXmlNode* Parent()                         { return parent; }
      +00521     const TiXmlNode* Parent() const             { return parent; }
      +00522 
      +00523     const TiXmlNode* FirstChild()   const       { return firstChild; }  
      +00524     TiXmlNode* FirstChild()                     { return firstChild; }
      +00525     const TiXmlNode* FirstChild( const char * value ) const;            
      +00526 
      +00527     TiXmlNode* FirstChild( const char * _value ) {
      +00528         // Call through to the const version - safe since nothing is changed. Exiting syntax: cast this to a const (always safe)
      +00529         // call the method, cast the return back to non-const.
      +00530         return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->FirstChild( _value ));
      +00531     }
      +00532     const TiXmlNode* LastChild() const  { return lastChild; }       
      +00533     TiXmlNode* LastChild()  { return lastChild; }
      +00534     
      +00535     const TiXmlNode* LastChild( const char * value ) const;         
      +00536     TiXmlNode* LastChild( const char * _value ) {
      +00537         return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->LastChild( _value ));
      +00538     }
      +00539 
      +00540     #ifdef TIXML_USE_STL
      +00541     const TiXmlNode* FirstChild( const std::string& _value ) const  {   return FirstChild (_value.c_str ());    }   
      +00542     TiXmlNode* FirstChild( const std::string& _value )              {   return FirstChild (_value.c_str ());    }   
      +00543     const TiXmlNode* LastChild( const std::string& _value ) const   {   return LastChild (_value.c_str ()); }   
      +00544     TiXmlNode* LastChild( const std::string& _value )               {   return LastChild (_value.c_str ()); }   
      +00545     #endif
      +00546 
      +00563     const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const;
      +00564     TiXmlNode* IterateChildren( const TiXmlNode* previous ) {
      +00565         return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( previous ) );
      +00566     }
      +00567 
      +00569     const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const;
      +00570     TiXmlNode* IterateChildren( const char * _value, const TiXmlNode* previous ) {
      +00571         return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( _value, previous ) );
      +00572     }
      +00573 
      +00574     #ifdef TIXML_USE_STL
      +00575     const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const  {   return IterateChildren (_value.c_str (), previous); }   
      +00576     TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) {    return IterateChildren (_value.c_str (), previous); }   
      +00577     #endif
      +00578 
      +00582     TiXmlNode* InsertEndChild( const TiXmlNode& addThis );
      +00583 
      +00584 
      +00594     TiXmlNode* LinkEndChild( TiXmlNode* addThis );
      +00595 
      +00599     TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis );
      +00600 
      +00604     TiXmlNode* InsertAfterChild(  TiXmlNode* afterThis, const TiXmlNode& addThis );
      +00605 
      +00609     TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis );
      +00610 
      +00612     bool RemoveChild( TiXmlNode* removeThis );
      +00613 
      +00615     const TiXmlNode* PreviousSibling() const            { return prev; }
      +00616     TiXmlNode* PreviousSibling()                        { return prev; }
      +00617 
      +00619     const TiXmlNode* PreviousSibling( const char * ) const;
      +00620     TiXmlNode* PreviousSibling( const char *_prev ) {
      +00621         return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->PreviousSibling( _prev ) );
      +00622     }
      +00623 
      +00624     #ifdef TIXML_USE_STL
      +00625     const TiXmlNode* PreviousSibling( const std::string& _value ) const {   return PreviousSibling (_value.c_str ());   }   
      +00626     TiXmlNode* PreviousSibling( const std::string& _value )             {   return PreviousSibling (_value.c_str ());   }   
      +00627     const TiXmlNode* NextSibling( const std::string& _value) const      {   return NextSibling (_value.c_str ());   }   
      +00628     TiXmlNode* NextSibling( const std::string& _value)                  {   return NextSibling (_value.c_str ());   }   
      +00629     #endif
      +00630 
      +00632     const TiXmlNode* NextSibling() const                { return next; }
      +00633     TiXmlNode* NextSibling()                            { return next; }
      +00634 
      +00636     const TiXmlNode* NextSibling( const char * ) const;
      +00637     TiXmlNode* NextSibling( const char* _next ) {
      +00638         return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->NextSibling( _next ) );
      +00639     }
      +00640 
      +00645     const TiXmlElement* NextSiblingElement() const;
      +00646     TiXmlElement* NextSiblingElement() {
      +00647         return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement() );
      +00648     }
      +00649 
      +00654     const TiXmlElement* NextSiblingElement( const char * ) const;
      +00655     TiXmlElement* NextSiblingElement( const char *_next ) {
      +00656         return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement( _next ) );
      +00657     }
      +00658 
      +00659     #ifdef TIXML_USE_STL
      +00660     const TiXmlElement* NextSiblingElement( const std::string& _value) const    {   return NextSiblingElement (_value.c_str ());    }   
      +00661     TiXmlElement* NextSiblingElement( const std::string& _value)                {   return NextSiblingElement (_value.c_str ());    }   
      +00662     #endif
      +00663 
      +00665     const TiXmlElement* FirstChildElement() const;
      +00666     TiXmlElement* FirstChildElement() {
      +00667         return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement() );
      +00668     }
      +00669 
      +00671     const TiXmlElement* FirstChildElement( const char * _value ) const;
      +00672     TiXmlElement* FirstChildElement( const char * _value ) {
      +00673         return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement( _value ) );
      +00674     }
      +00675 
      +00676     #ifdef TIXML_USE_STL
      +00677     const TiXmlElement* FirstChildElement( const std::string& _value ) const    {   return FirstChildElement (_value.c_str ()); }   
      +00678     TiXmlElement* FirstChildElement( const std::string& _value )                {   return FirstChildElement (_value.c_str ()); }   
      +00679     #endif
      +00680 
      +00685     int Type() const    { return type; }
      +00686 
      +00690     const TiXmlDocument* GetDocument() const;
      +00691     TiXmlDocument* GetDocument() {
      +00692         return const_cast< TiXmlDocument* >( (const_cast< const TiXmlNode* >(this))->GetDocument() );
      +00693     }
      +00694 
      +00696     bool NoChildren() const                     { return !firstChild; }
      +00697 
      +00698     virtual const TiXmlDocument*    ToDocument()    const { return 0; } 
      +00699     virtual const TiXmlElement*     ToElement()     const { return 0; } 
      +00700     virtual const TiXmlComment*     ToComment()     const { return 0; } 
      +00701     virtual const TiXmlUnknown*     ToUnknown()     const { return 0; } 
      +00702     virtual const TiXmlText*        ToText()        const { return 0; } 
      +00703     virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } 
      +00704 
      +00705     virtual TiXmlDocument*          ToDocument()    { return 0; } 
      +00706     virtual TiXmlElement*           ToElement()     { return 0; } 
      +00707     virtual TiXmlComment*           ToComment()     { return 0; } 
      +00708     virtual TiXmlUnknown*           ToUnknown()     { return 0; } 
      +00709     virtual TiXmlText*              ToText()        { return 0; } 
      +00710     virtual TiXmlDeclaration*       ToDeclaration() { return 0; } 
      +00711 
      +00715     virtual TiXmlNode* Clone() const = 0;
      +00716 
      +00739     virtual bool Accept( TiXmlVisitor* visitor ) const = 0;
      +00740 
      +00741 protected:
      +00742     TiXmlNode( NodeType _type );
      +00743 
      +00744     // Copy to the allocated object. Shared functionality between Clone, Copy constructor,
      +00745     // and the assignment operator.
      +00746     void CopyTo( TiXmlNode* target ) const;
      +00747 
      +00748     #ifdef TIXML_USE_STL
      +00749         // The real work of the input operator.
      +00750     virtual void StreamIn( std::istream* in, TIXML_STRING* tag ) = 0;
      +00751     #endif
      +00752 
      +00753     // Figure out what is at *p, and parse it. Returns null if it is not an xml node.
      +00754     TiXmlNode* Identify( const char* start, TiXmlEncoding encoding );
      +00755 
      +00756     TiXmlNode*      parent;
      +00757     NodeType        type;
      +00758 
      +00759     TiXmlNode*      firstChild;
      +00760     TiXmlNode*      lastChild;
      +00761 
      +00762     TIXML_STRING    value;
      +00763 
      +00764     TiXmlNode*      prev;
      +00765     TiXmlNode*      next;
      +00766 
      +00767 private:
      +00768     TiXmlNode( const TiXmlNode& );              // not implemented.
      +00769     void operator=( const TiXmlNode& base );    // not allowed.
      +00770 };
      +00771 
      +00772 
      +00780 class TiXmlAttribute : public TiXmlBase
      +00781 {
      +00782     friend class TiXmlAttributeSet;
      +00783 
      +00784 public:
      +00786     TiXmlAttribute() : TiXmlBase()
      +00787     {
      +00788         document = 0;
      +00789         prev = next = 0;
      +00790     }
      +00791 
      +00792     #ifdef TIXML_USE_STL
      +00794     TiXmlAttribute( const std::string& _name, const std::string& _value )
      +00795     {
      +00796         name = _name;
      +00797         value = _value;
      +00798         document = 0;
      +00799         prev = next = 0;
      +00800     }
      +00801     #endif
      +00802 
      +00804     TiXmlAttribute( const char * _name, const char * _value )
      +00805     {
      +00806         name = _name;
      +00807         value = _value;
      +00808         document = 0;
      +00809         prev = next = 0;
      +00810     }
      +00811 
      +00812     const char*     Name()  const       { return name.c_str(); }        
      +00813     const char*     Value() const       { return value.c_str(); }       
      +00814     #ifdef TIXML_USE_STL
      +00815     const std::string& ValueStr() const { return value; }               
      +00816     #endif
      +00817     int             IntValue() const;                                   
      +00818     double          DoubleValue() const;                                
      +00819 
      +00820     // Get the tinyxml string representation
      +00821     const TIXML_STRING& NameTStr() const { return name; }
      +00822 
      +00832     int QueryIntValue( int* _value ) const;
      +00834     int QueryDoubleValue( double* _value ) const;
      +00835 
      +00836     void SetName( const char* _name )   { name = _name; }               
      +00837     void SetValue( const char* _value ) { value = _value; }             
      +00838 
      +00839     void SetIntValue( int _value );                                     
      +00840     void SetDoubleValue( double _value );                               
      +00841 
      +00842     #ifdef TIXML_USE_STL
      +00844     void SetName( const std::string& _name )    { name = _name; }   
      +00846     void SetValue( const std::string& _value )  { value = _value; }
      +00847     #endif
      +00848 
      +00850     const TiXmlAttribute* Next() const;
      +00851     TiXmlAttribute* Next() {
      +00852         return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Next() ); 
      +00853     }
      +00854 
      +00856     const TiXmlAttribute* Previous() const;
      +00857     TiXmlAttribute* Previous() {
      +00858         return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Previous() ); 
      +00859     }
      +00860 
      +00861     bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; }
      +00862     bool operator<( const TiXmlAttribute& rhs )  const { return name < rhs.name; }
      +00863     bool operator>( const TiXmlAttribute& rhs )  const { return name > rhs.name; }
      +00864 
      +00865     /*  Attribute parsing starts: first letter of the name
      +00866                          returns: the next char after the value end quote
      +00867     */
      +00868     virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
      +00869 
      +00870     // Prints this Attribute to a FILE stream.
      +00871     virtual void Print( FILE* cfile, int depth ) const {
      +00872         Print( cfile, depth, 0 );
      +00873     }
      +00874     void Print( FILE* cfile, int depth, TIXML_STRING* str ) const;
      +00875 
      +00876     // [internal use]
      +00877     // Set the document pointer so the attribute can report errors.
      +00878     void SetDocument( TiXmlDocument* doc )  { document = doc; }
      +00879 
      +00880 private:
      +00881     TiXmlAttribute( const TiXmlAttribute& );                // not implemented.
      +00882     void operator=( const TiXmlAttribute& base );   // not allowed.
      +00883 
      +00884     TiXmlDocument*  document;   // A pointer back to a document, for error reporting.
      +00885     TIXML_STRING name;
      +00886     TIXML_STRING value;
      +00887     TiXmlAttribute* prev;
      +00888     TiXmlAttribute* next;
      +00889 };
      +00890 
      +00891 
      +00892 /*  A class used to manage a group of attributes.
      +00893     It is only used internally, both by the ELEMENT and the DECLARATION.
      +00894     
      +00895     The set can be changed transparent to the Element and Declaration
      +00896     classes that use it, but NOT transparent to the Attribute
      +00897     which has to implement a next() and previous() method. Which makes
      +00898     it a bit problematic and prevents the use of STL.
      +00899 
      +00900     This version is implemented with circular lists because:
      +00901         - I like circular lists
      +00902         - it demonstrates some independence from the (typical) doubly linked list.
      +00903 */
      +00904 class TiXmlAttributeSet
      +00905 {
      +00906 public:
      +00907     TiXmlAttributeSet();
      +00908     ~TiXmlAttributeSet();
      +00909 
      +00910     void Add( TiXmlAttribute* attribute );
      +00911     void Remove( TiXmlAttribute* attribute );
      +00912 
      +00913     const TiXmlAttribute* First()   const   { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; }
      +00914     TiXmlAttribute* First()                 { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; }
      +00915     const TiXmlAttribute* Last() const      { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; }
      +00916     TiXmlAttribute* Last()                  { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; }
      +00917 
      +00918     const TiXmlAttribute*   Find( const char* _name ) const;
      +00919     TiXmlAttribute* Find( const char* _name ) {
      +00920         return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) );
      +00921     }
      +00922     #ifdef TIXML_USE_STL
      +00923     const TiXmlAttribute*   Find( const std::string& _name ) const;
      +00924     TiXmlAttribute* Find( const std::string& _name ) {
      +00925         return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) );
      +00926     }
      +00927 
      +00928     #endif
      +00929 
      +00930 private:
      +00931     //*ME:  Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element),
      +00932     //*ME:  this class must be also use a hidden/disabled copy-constructor !!!
      +00933     TiXmlAttributeSet( const TiXmlAttributeSet& );  // not allowed
      +00934     void operator=( const TiXmlAttributeSet& ); // not allowed (as TiXmlAttribute)
      +00935 
      +00936     TiXmlAttribute sentinel;
      +00937 };
      +00938 
      +00939 
      +00944 class TiXmlElement : public TiXmlNode
      +00945 {
      +00946 public:
      +00948     TiXmlElement (const char * in_value);
      +00949 
      +00950     #ifdef TIXML_USE_STL
      +00952     TiXmlElement( const std::string& _value );
      +00953     #endif
      +00954 
      +00955     TiXmlElement( const TiXmlElement& );
      +00956 
      +00957     void operator=( const TiXmlElement& base );
      +00958 
      +00959     virtual ~TiXmlElement();
      +00960 
      +00964     const char* Attribute( const char* name ) const;
      +00965 
      +00972     const char* Attribute( const char* name, int* i ) const;
      +00973 
      +00980     const char* Attribute( const char* name, double* d ) const;
      +00981 
      +00989     int QueryIntAttribute( const char* name, int* _value ) const;
      +00991     int QueryDoubleAttribute( const char* name, double* _value ) const;
      +00993     int QueryFloatAttribute( const char* name, float* _value ) const {
      +00994         double d;
      +00995         int result = QueryDoubleAttribute( name, &d );
      +00996         if ( result == TIXML_SUCCESS ) {
      +00997             *_value = (float)d;
      +00998         }
      +00999         return result;
      +01000     }
      +01001 
      +01002     #ifdef TIXML_USE_STL
      +01003 
      +01011     template< typename T > int QueryValueAttribute( const std::string& name, T* outValue ) const
      +01012     {
      +01013         const TiXmlAttribute* node = attributeSet.Find( name );
      +01014         if ( !node )
      +01015             return TIXML_NO_ATTRIBUTE;
      +01016 
      +01017         std::stringstream sstream( node->ValueStr() );
      +01018         sstream >> *outValue;
      +01019         if ( !sstream.fail() )
      +01020             return TIXML_SUCCESS;
      +01021         return TIXML_WRONG_TYPE;
      +01022     }
      +01023     /*
      +01024      This is - in theory - a bug fix for "QueryValueAtribute returns truncated std::string"
      +01025      but template specialization is hard to get working cross-compiler. Leaving the bug for now.
      +01026      
      +01027     // The above will fail for std::string because the space character is used as a seperator.
      +01028     // Specialize for strings. Bug [ 1695429 ] QueryValueAtribute returns truncated std::string
      +01029     template<> int QueryValueAttribute( const std::string& name, std::string* outValue ) const
      +01030     {
      +01031         const TiXmlAttribute* node = attributeSet.Find( name );
      +01032         if ( !node )
      +01033             return TIXML_NO_ATTRIBUTE;
      +01034         *outValue = node->ValueStr();
      +01035         return TIXML_SUCCESS;
      +01036     }
      +01037     */
      +01038     #endif
      +01039 
      +01043     void SetAttribute( const char* name, const char * _value );
      +01044 
      +01045     #ifdef TIXML_USE_STL
      +01046     const std::string* Attribute( const std::string& name ) const;
      +01047     const std::string* Attribute( const std::string& name, int* i ) const;
      +01048     const std::string* Attribute( const std::string& name, double* d ) const;
      +01049     int QueryIntAttribute( const std::string& name, int* _value ) const;
      +01050     int QueryDoubleAttribute( const std::string& name, double* _value ) const;
      +01051 
      +01053     void SetAttribute( const std::string& name, const std::string& _value );
      +01055     void SetAttribute( const std::string& name, int _value );
      +01056     #endif
      +01057 
      +01061     void SetAttribute( const char * name, int value );
      +01062 
      +01066     void SetDoubleAttribute( const char * name, double value );
      +01067 
      +01070     void RemoveAttribute( const char * name );
      +01071     #ifdef TIXML_USE_STL
      +01072     void RemoveAttribute( const std::string& name ) {   RemoveAttribute (name.c_str ());    }   
      +01073     #endif
      +01074 
      +01075     const TiXmlAttribute* FirstAttribute() const    { return attributeSet.First(); }        
      +01076     TiXmlAttribute* FirstAttribute()                { return attributeSet.First(); }
      +01077     const TiXmlAttribute* LastAttribute()   const   { return attributeSet.Last(); }     
      +01078     TiXmlAttribute* LastAttribute()                 { return attributeSet.Last(); }
      +01079 
      +01112     const char* GetText() const;
      +01113 
      +01115     virtual TiXmlNode* Clone() const;
      +01116     // Print the Element to a FILE stream.
      +01117     virtual void Print( FILE* cfile, int depth ) const;
      +01118 
      +01119     /*  Attribtue parsing starts: next char past '<'
      +01120                          returns: next char past '>'
      +01121     */
      +01122     virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
      +01123 
      +01124     virtual const TiXmlElement*     ToElement()     const { return this; } 
      +01125     virtual TiXmlElement*           ToElement()           { return this; } 
      +01126 
      +01129     virtual bool Accept( TiXmlVisitor* visitor ) const;
      +01130 
      +01131 protected:
      +01132 
      +01133     void CopyTo( TiXmlElement* target ) const;
      +01134     void ClearThis();   // like clear, but initializes 'this' object as well
      +01135 
      +01136     // Used to be public [internal use]
      +01137     #ifdef TIXML_USE_STL
      +01138     virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
      +01139     #endif
      +01140     /*  [internal use]
      +01141         Reads the "value" of the element -- another element, or text.
      +01142         This should terminate with the current end tag.
      +01143     */
      +01144     const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding );
      +01145 
      +01146 private:
      +01147 
      +01148     TiXmlAttributeSet attributeSet;
      +01149 };
      +01150 
      +01151 
      +01154 class TiXmlComment : public TiXmlNode
      +01155 {
      +01156 public:
      +01158     TiXmlComment() : TiXmlNode( TiXmlNode::COMMENT ) {}
      +01160     TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::COMMENT ) {
      +01161         SetValue( _value );
      +01162     }
      +01163     TiXmlComment( const TiXmlComment& );
      +01164     void operator=( const TiXmlComment& base );
      +01165 
      +01166     virtual ~TiXmlComment() {}
      +01167 
      +01169     virtual TiXmlNode* Clone() const;
      +01170     // Write this Comment to a FILE stream.
      +01171     virtual void Print( FILE* cfile, int depth ) const;
      +01172 
      +01173     /*  Attribtue parsing starts: at the ! of the !--
      +01174                          returns: next char past '>'
      +01175     */
      +01176     virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
      +01177 
      +01178     virtual const TiXmlComment*  ToComment() const { return this; } 
      +01179     virtual TiXmlComment*  ToComment() { return this; } 
      +01180 
      +01183     virtual bool Accept( TiXmlVisitor* visitor ) const;
      +01184 
      +01185 protected:
      +01186     void CopyTo( TiXmlComment* target ) const;
      +01187 
      +01188     // used to be public
      +01189     #ifdef TIXML_USE_STL
      +01190     virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
      +01191     #endif
      +01192 //  virtual void StreamOut( TIXML_OSTREAM * out ) const;
      +01193 
      +01194 private:
      +01195 
      +01196 };
      +01197 
      +01198 
      +01204 class TiXmlText : public TiXmlNode
      +01205 {
      +01206     friend class TiXmlElement;
      +01207 public:
      +01212     TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TEXT)
      +01213     {
      +01214         SetValue( initValue );
      +01215         cdata = false;
      +01216     }
      +01217     virtual ~TiXmlText() {}
      +01218 
      +01219     #ifdef TIXML_USE_STL
      +01221     TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TEXT)
      +01222     {
      +01223         SetValue( initValue );
      +01224         cdata = false;
      +01225     }
      +01226     #endif
      +01227 
      +01228     TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TEXT )   { copy.CopyTo( this ); }
      +01229     void operator=( const TiXmlText& base )                             { base.CopyTo( this ); }
      +01230 
      +01231     // Write this text object to a FILE stream.
      +01232     virtual void Print( FILE* cfile, int depth ) const;
      +01233 
      +01235     bool CDATA() const              { return cdata; }
      +01237     void SetCDATA( bool _cdata )    { cdata = _cdata; }
      +01238 
      +01239     virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
      +01240 
      +01241     virtual const TiXmlText* ToText() const { return this; } 
      +01242     virtual TiXmlText*       ToText()       { return this; } 
      +01243 
      +01246     virtual bool Accept( TiXmlVisitor* content ) const;
      +01247 
      +01248 protected :
      +01250     virtual TiXmlNode* Clone() const;
      +01251     void CopyTo( TiXmlText* target ) const;
      +01252 
      +01253     bool Blank() const; // returns true if all white space and new lines
      +01254     // [internal use]
      +01255     #ifdef TIXML_USE_STL
      +01256     virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
      +01257     #endif
      +01258 
      +01259 private:
      +01260     bool cdata;         // true if this should be input and output as a CDATA style text element
      +01261 };
      +01262 
      +01263 
      +01277 class TiXmlDeclaration : public TiXmlNode
      +01278 {
      +01279 public:
      +01281     TiXmlDeclaration()   : TiXmlNode( TiXmlNode::DECLARATION ) {}
      +01282 
      +01283 #ifdef TIXML_USE_STL
      +01285     TiXmlDeclaration(   const std::string& _version,
      +01286                         const std::string& _encoding,
      +01287                         const std::string& _standalone );
      +01288 #endif
      +01289 
      +01291     TiXmlDeclaration(   const char* _version,
      +01292                         const char* _encoding,
      +01293                         const char* _standalone );
      +01294 
      +01295     TiXmlDeclaration( const TiXmlDeclaration& copy );
      +01296     void operator=( const TiXmlDeclaration& copy );
      +01297 
      +01298     virtual ~TiXmlDeclaration() {}
      +01299 
      +01301     const char *Version() const         { return version.c_str (); }
      +01303     const char *Encoding() const        { return encoding.c_str (); }
      +01305     const char *Standalone() const      { return standalone.c_str (); }
      +01306 
      +01308     virtual TiXmlNode* Clone() const;
      +01309     // Print this declaration to a FILE stream.
      +01310     virtual void Print( FILE* cfile, int depth, TIXML_STRING* str ) const;
      +01311     virtual void Print( FILE* cfile, int depth ) const {
      +01312         Print( cfile, depth, 0 );
      +01313     }
      +01314 
      +01315     virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
      +01316 
      +01317     virtual const TiXmlDeclaration* ToDeclaration() const { return this; } 
      +01318     virtual TiXmlDeclaration*       ToDeclaration()       { return this; } 
      +01319 
      +01322     virtual bool Accept( TiXmlVisitor* visitor ) const;
      +01323 
      +01324 protected:
      +01325     void CopyTo( TiXmlDeclaration* target ) const;
      +01326     // used to be public
      +01327     #ifdef TIXML_USE_STL
      +01328     virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
      +01329     #endif
      +01330 
      +01331 private:
      +01332 
      +01333     TIXML_STRING version;
      +01334     TIXML_STRING encoding;
      +01335     TIXML_STRING standalone;
      +01336 };
      +01337 
      +01338 
      +01346 class TiXmlUnknown : public TiXmlNode
      +01347 {
      +01348 public:
      +01349     TiXmlUnknown() : TiXmlNode( TiXmlNode::UNKNOWN )    {}
      +01350     virtual ~TiXmlUnknown() {}
      +01351 
      +01352     TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::UNKNOWN )      { copy.CopyTo( this ); }
      +01353     void operator=( const TiXmlUnknown& copy )                                      { copy.CopyTo( this ); }
      +01354 
      +01356     virtual TiXmlNode* Clone() const;
      +01357     // Print this Unknown to a FILE stream.
      +01358     virtual void Print( FILE* cfile, int depth ) const;
      +01359 
      +01360     virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
      +01361 
      +01362     virtual const TiXmlUnknown*     ToUnknown()     const { return this; } 
      +01363     virtual TiXmlUnknown*           ToUnknown()     { return this; } 
      +01364 
      +01367     virtual bool Accept( TiXmlVisitor* content ) const;
      +01368 
      +01369 protected:
      +01370     void CopyTo( TiXmlUnknown* target ) const;
      +01371 
      +01372     #ifdef TIXML_USE_STL
      +01373     virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
      +01374     #endif
      +01375 
      +01376 private:
      +01377 
      +01378 };
      +01379 
      +01380 
      +01385 class TiXmlDocument : public TiXmlNode
      +01386 {
      +01387 public:
      +01389     TiXmlDocument();
      +01391     TiXmlDocument( const char * documentName );
      +01392 
      +01393     #ifdef TIXML_USE_STL
      +01395     TiXmlDocument( const std::string& documentName );
      +01396     #endif
      +01397 
      +01398     TiXmlDocument( const TiXmlDocument& copy );
      +01399     void operator=( const TiXmlDocument& copy );
      +01400 
      +01401     virtual ~TiXmlDocument() {}
      +01402 
      +01407     bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
      +01409     bool SaveFile() const;
      +01411     bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
      +01413     bool SaveFile( const char * filename ) const;
      +01419     bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
      +01421     bool SaveFile( FILE* ) const;
      +01422 
      +01423     #ifdef TIXML_USE_STL
      +01424     bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING )           
      +01425     {
      +01426 //      StringToBuffer f( filename );
      +01427 //      return ( f.buffer && LoadFile( f.buffer, encoding ));
      +01428         return LoadFile( filename.c_str(), encoding );
      +01429     }
      +01430     bool SaveFile( const std::string& filename ) const      
      +01431     {
      +01432 //      StringToBuffer f( filename );
      +01433 //      return ( f.buffer && SaveFile( f.buffer ));
      +01434         return SaveFile( filename.c_str() );
      +01435     }
      +01436     #endif
      +01437 
      +01442     virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
      +01443 
      +01448     const TiXmlElement* RootElement() const     { return FirstChildElement(); }
      +01449     TiXmlElement* RootElement()                 { return FirstChildElement(); }
      +01450 
      +01456     bool Error() const                      { return error; }
      +01457 
      +01459     const char * ErrorDesc() const  { return errorDesc.c_str (); }
      +01460 
      +01464     int ErrorId()   const               { return errorId; }
      +01465 
      +01473     int ErrorRow() const    { return errorLocation.row+1; }
      +01474     int ErrorCol() const    { return errorLocation.col+1; } 
      +01475 
      +01500     void SetTabSize( int _tabsize )     { tabsize = _tabsize; }
      +01501 
      +01502     int TabSize() const { return tabsize; }
      +01503 
      +01507     void ClearError()                       {   error = false; 
      +01508                                                 errorId = 0; 
      +01509                                                 errorDesc = ""; 
      +01510                                                 errorLocation.row = errorLocation.col = 0; 
      +01511                                                 //errorLocation.last = 0; 
      +01512                                             }
      +01513 
      +01515     void Print() const                      { Print( stdout, 0 ); }
      +01516 
      +01517     /* Write the document to a string using formatted printing ("pretty print"). This
      +01518         will allocate a character array (new char[]) and return it as a pointer. The
      +01519         calling code pust call delete[] on the return char* to avoid a memory leak.
      +01520     */
      +01521     //char* PrintToMemory() const; 
      +01522 
      +01524     virtual void Print( FILE* cfile, int depth = 0 ) const;
      +01525     // [internal use]
      +01526     void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding );
      +01527 
      +01528     virtual const TiXmlDocument*    ToDocument()    const { return this; } 
      +01529     virtual TiXmlDocument*          ToDocument()          { return this; } 
      +01530 
      +01533     virtual bool Accept( TiXmlVisitor* content ) const;
      +01534 
      +01535 protected :
      +01536     // [internal use]
      +01537     virtual TiXmlNode* Clone() const;
      +01538     #ifdef TIXML_USE_STL
      +01539     virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
      +01540     #endif
      +01541 
      +01542 private:
      +01543     void CopyTo( TiXmlDocument* target ) const;
      +01544 
      +01545     bool error;
      +01546     int  errorId;
      +01547     TIXML_STRING errorDesc;
      +01548     int tabsize;
      +01549     TiXmlCursor errorLocation;
      +01550     bool useMicrosoftBOM;       // the UTF-8 BOM were found when read. Note this, and try to write.
      +01551 };
      +01552 
      +01553 
      +01634 class TiXmlHandle
      +01635 {
      +01636 public:
      +01638     TiXmlHandle( TiXmlNode* _node )                 { this->node = _node; }
      +01640     TiXmlHandle( const TiXmlHandle& ref )           { this->node = ref.node; }
      +01641     TiXmlHandle operator=( const TiXmlHandle& ref ) { this->node = ref.node; return *this; }
      +01642 
      +01644     TiXmlHandle FirstChild() const;
      +01646     TiXmlHandle FirstChild( const char * value ) const;
      +01648     TiXmlHandle FirstChildElement() const;
      +01650     TiXmlHandle FirstChildElement( const char * value ) const;
      +01651 
      +01655     TiXmlHandle Child( const char* value, int index ) const;
      +01659     TiXmlHandle Child( int index ) const;
      +01664     TiXmlHandle ChildElement( const char* value, int index ) const;
      +01669     TiXmlHandle ChildElement( int index ) const;
      +01670 
      +01671     #ifdef TIXML_USE_STL
      +01672     TiXmlHandle FirstChild( const std::string& _value ) const               { return FirstChild( _value.c_str() ); }
      +01673     TiXmlHandle FirstChildElement( const std::string& _value ) const        { return FirstChildElement( _value.c_str() ); }
      +01674 
      +01675     TiXmlHandle Child( const std::string& _value, int index ) const         { return Child( _value.c_str(), index ); }
      +01676     TiXmlHandle ChildElement( const std::string& _value, int index ) const  { return ChildElement( _value.c_str(), index ); }
      +01677     #endif
      +01678 
      +01681     TiXmlNode* ToNode() const           { return node; } 
      +01684     TiXmlElement* ToElement() const     { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); }
      +01687     TiXmlText* ToText() const           { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); }
      +01690     TiXmlUnknown* ToUnknown() const     { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); }
      +01691 
      +01695     TiXmlNode* Node() const         { return ToNode(); } 
      +01699     TiXmlElement* Element() const   { return ToElement(); }
      +01703     TiXmlText* Text() const         { return ToText(); }
      +01707     TiXmlUnknown* Unknown() const   { return ToUnknown(); }
      +01708 
      +01709 private:
      +01710     TiXmlNode* node;
      +01711 };
      +01712 
      +01713 
      +01733 class TiXmlPrinter : public TiXmlVisitor
      +01734 {
      +01735 public:
      +01736     TiXmlPrinter() : depth( 0 ), simpleTextPrint( false ),
      +01737                      buffer(), indent( "    " ), lineBreak( "\n" ) {}
      +01738 
      +01739     virtual bool VisitEnter( const TiXmlDocument& doc );
      +01740     virtual bool VisitExit( const TiXmlDocument& doc );
      +01741 
      +01742     virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute );
      +01743     virtual bool VisitExit( const TiXmlElement& element );
      +01744 
      +01745     virtual bool Visit( const TiXmlDeclaration& declaration );
      +01746     virtual bool Visit( const TiXmlText& text );
      +01747     virtual bool Visit( const TiXmlComment& comment );
      +01748     virtual bool Visit( const TiXmlUnknown& unknown );
      +01749 
      +01753     void SetIndent( const char* _indent )           { indent = _indent ? _indent : "" ; }
      +01755     const char* Indent()                            { return indent.c_str(); }
      +01760     void SetLineBreak( const char* _lineBreak )     { lineBreak = _lineBreak ? _lineBreak : ""; }
      +01762     const char* LineBreak()                         { return lineBreak.c_str(); }
      +01763 
      +01767     void SetStreamPrinting()                        { indent = "";
      +01768                                                       lineBreak = "";
      +01769                                                     }   
      +01771     const char* CStr()                              { return buffer.c_str(); }
      +01773     size_t Size()                                   { return buffer.size(); }
      +01774 
      +01775     #ifdef TIXML_USE_STL
      +01777     const std::string& Str()                        { return buffer; }
      +01778     #endif
      +01779 
      +01780 private:
      +01781     void DoIndent() {
      +01782         for( int i=0; i<depth; ++i )
      +01783             buffer += indent;
      +01784     }
      +01785     void DoLineBreak() {
      +01786         buffer += lineBreak;
      +01787     }
      +01788 
      +01789     int depth;
      +01790     bool simpleTextPrint;
      +01791     TIXML_STRING buffer;
      +01792     TIXML_STRING indent;
      +01793     TIXML_STRING lineBreak;
      +01794 };
      +01795 
      +01796 
      +01797 #ifdef _MSC_VER
      +01798 #pragma warning( pop )
      +01799 #endif
      +01800 
      +01801 #endif
      +01802 
      +

      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/docs/tutorial0.html b/Engine/lib/tinyxml/docs/tutorial0.html new file mode 100644 index 000000000..d5d19cab1 --- /dev/null +++ b/Engine/lib/tinyxml/docs/tutorial0.html @@ -0,0 +1,721 @@ + + +TinyXml: TinyXML Tutorial + + + + + + +

      TinyXML Tutorial

      What is this?

      +

      +This tutorial has a few tips and suggestions on how to use TinyXML effectively.

      +I've also tried to include some C++ tips like how to convert strings to integers and vice versa. This isn't anything to do with TinyXML itself, but it may helpful for your project so I've put it in anyway.

      +If you don't know basic C++ concepts this tutorial won't be useful. Likewise if you don't know what a DOM is, look elsewhere first.

      +

      Before we start

      +

      +Some example XML datasets/files will be used.

      +example1.xml:

      +

      <?xml version="1.0" ?>
      +<Hello>World</Hello>
      +

      +example2.xml:

      +

      <?xml version="1.0" ?>
      +<poetry>
      +	<verse>
      +		Alas
      +		  Great World
      +			Alas (again)
      +	</verse>
      +</poetry>
      +

      +example3.xml:

      +

      <?xml version="1.0" ?>
      +<shapes>
      +	<circle name="int-based" x="20" y="30" r="50" />
      +	<point name="float-based" x="3.5" y="52.1" />
      +</shapes>
      +

      +example4.xml

      +

      <?xml version="1.0" ?>
      +<MyApp>
      +    <!-- Settings for MyApp -->
      +    <Messages>
      +        <Welcome>Welcome to MyApp</Welcome>
      +        <Farewell>Thank you for using MyApp</Farewell>
      +    </Messages>
      +    <Windows>
      +        <Window name="MainFrame" x="5" y="15" w="400" h="250" />
      +    </Windows>
      +    <Connection ip="192.168.0.1" timeout="123.456000" />
      +</MyApp>
      +

      +

      Getting Started

      +

      +

      Load XML from a file

      +

      +The simplest way to load a file into a TinyXML DOM is:

      +

      TiXmlDocument doc( "demo.xml" );
      +doc.LoadFile();
      +

      +A more real-world usage is shown below. This will load the file and display the contents to STDOUT:

      +

      // load the named file and dump its structure to STDOUT
      +void dump_to_stdout(const char* pFilename)
      +{
      +	TiXmlDocument doc(pFilename);
      +	bool loadOkay = doc.LoadFile();
      +	if (loadOkay)
      +	{
      +		printf("\n%s:\n", pFilename);
      +		dump_to_stdout( &doc ); // defined later in the tutorial
      +	}
      +	else
      +	{
      +		printf("Failed to load file \"%s\"\n", pFilename);
      +	}
      +}
      +

      +A simple demonstration of this function is to use a main like this:

      +

      int main(void)
      +{
      +	dump_to_stdout("example1.xml");
      +	return 0;
      +}
      +

      +Recall that Example 1 XML is:

      +

      <?xml version="1.0" ?>
      +<Hello>World</Hello>
      +

      +Running the program with this XML will display this in the console/DOS window:

      +

      DOCUMENT
      ++ DECLARATION
      ++ ELEMENT Hello
      +  + TEXT[World]
      +

      +The ``dump_to_stdout`` function is defined later in this tutorial and is useful if you want to understand recursive traversal of a DOM.

      +

      Building Documents Programatically

      +

      +This is how to build Example 1 pragmatically:

      +

      void build_simple_doc( )
      +{
      +	// Make xml: <?xml ..><Hello>World</Hello>
      +	TiXmlDocument doc;
      +	TiXmlDeclaration * decl = new TiXmlDeclaration( "1.0", "", "" );
      +	TiXmlElement * element = new TiXmlElement( "Hello" );
      +	TiXmlText * text = new TiXmlText( "World" );
      +	element->LinkEndChild( text );
      +	doc.LinkEndChild( decl );
      +	doc.LinkEndChild( element );
      +	doc.SaveFile( "madeByHand.xml" );
      +}
      +

      +This can be loaded and displayed on the console with:

      +

      dump_to_stdout("madeByHand.xml"); // this func defined later in the tutorial
      +

      +and you'll see it is identical to Example 1:

      +

      madeByHand.xml:
      +Document
      ++ Declaration
      ++ Element [Hello]
      +  + Text: [World]
      +

      +This code produces exactly the same XML DOM but it shows a different ordering to node creation and linking:

      +

      void write_simple_doc2( )
      +{
      +	// same as write_simple_doc1 but add each node
      +	// as early as possible into the tree.
      +
      +	TiXmlDocument doc;
      +	TiXmlDeclaration * decl = new TiXmlDeclaration( "1.0", "", "" );
      +	doc.LinkEndChild( decl );
      +	
      +	TiXmlElement * element = new TiXmlElement( "Hello" );
      +	doc.LinkEndChild( element );
      +	
      +	TiXmlText * text = new TiXmlText( "World" );
      +	element->LinkEndChild( text );
      +	
      +	doc.SaveFile( "madeByHand2.xml" );
      +}
      +

      +Both of these produce the same XML, namely:

      +

      <?xml version="1.0" ?>
      +<Hello>World</Hello>
      +

      +Or in structure form:

      +

      DOCUMENT
      ++ DECLARATION
      ++ ELEMENT Hello
      +  + TEXT[World]
      +

      +

      Attributes

      +

      +Given an existing node, settings attributes is easy:

      +

      window = new TiXmlElement( "Demo" );  
      +window->SetAttribute("name", "Circle");
      +window->SetAttribute("x", 5);
      +window->SetAttribute("y", 15);
      +window->SetDoubleAttribute("radius", 3.14159);
      +

      +You can it also work with the TiXmlAttribute objects if you want.

      +The following code shows one way (not the only way) to get all attributes of an element, print the name and string value, and if the value can be converted to an integer or double, print that value too:

      +

      // print all attributes of pElement.
      +// returns the number of attributes printed
      +int dump_attribs_to_stdout(TiXmlElement* pElement, unsigned int indent)
      +{
      +	if ( !pElement ) return 0;
      +
      +	TiXmlAttribute* pAttrib=pElement->FirstAttribute();
      +	int i=0;
      +	int ival;
      +	double dval;
      +	const char* pIndent=getIndent(indent);
      +	printf("\n");
      +	while (pAttrib)
      +	{
      +		printf( "%s%s: value=[%s]", pIndent, pAttrib->Name(), pAttrib->Value());
      +
      +		if (pAttrib->QueryIntValue(&ival)==TIXML_SUCCESS)    printf( " int=%d", ival);
      +		if (pAttrib->QueryDoubleValue(&dval)==TIXML_SUCCESS) printf( " d=%1.1f", dval);
      +		printf( "\n" );
      +		i++;
      +		pAttrib=pAttrib->Next();
      +	}
      +	return i;
      +}
      +

      +

      Writing a document to a file

      +

      +Writing a pre-built DOM to a file is trivial:

      +

      doc.SaveFile( saveFilename );  
      +

      +Recall, for example, example 4:

      +

      <?xml version="1.0" ?>
      +<MyApp>
      +    <!-- Settings for MyApp -->
      +    <Messages>
      +        <Welcome>Welcome to MyApp</Welcome>
      +        <Farewell>Thank you for using MyApp</Farewell>
      +    </Messages>
      +    <Windows>
      +        <Window name="MainFrame" x="5" y="15" w="400" h="250" />
      +    </Windows>
      +    <Connection ip="192.168.0.1" timeout="123.456000" />
      +</MyApp>
      +

      +The following function builds this DOM and writes the file "appsettings.xml":

      +

      void write_app_settings_doc( )  
      +{  
      +	TiXmlDocument doc;  
      +	TiXmlElement* msg;
      + 	TiXmlDeclaration* decl = new TiXmlDeclaration( "1.0", "", "" );  
      +	doc.LinkEndChild( decl );  
      + 
      +	TiXmlElement * root = new TiXmlElement( "MyApp" );  
      +	doc.LinkEndChild( root );  
      +
      +	TiXmlComment * comment = new TiXmlComment();
      +	comment->SetValue(" Settings for MyApp " );  
      +	root->LinkEndChild( comment );  
      + 
      +	TiXmlElement * msgs = new TiXmlElement( "Messages" );  
      +	root->LinkEndChild( msgs );  
      + 
      +	msg = new TiXmlElement( "Welcome" );  
      +	msg->LinkEndChild( new TiXmlText( "Welcome to MyApp" ));  
      +	msgs->LinkEndChild( msg );  
      + 
      +	msg = new TiXmlElement( "Farewell" );  
      +	msg->LinkEndChild( new TiXmlText( "Thank you for using MyApp" ));  
      +	msgs->LinkEndChild( msg );  
      + 
      +	TiXmlElement * windows = new TiXmlElement( "Windows" );  
      +	root->LinkEndChild( windows );  
      +
      +	TiXmlElement * window;
      +	window = new TiXmlElement( "Window" );  
      +	windows->LinkEndChild( window );  
      +	window->SetAttribute("name", "MainFrame");
      +	window->SetAttribute("x", 5);
      +	window->SetAttribute("y", 15);
      +	window->SetAttribute("w", 400);
      +	window->SetAttribute("h", 250);
      +
      +	TiXmlElement * cxn = new TiXmlElement( "Connection" );  
      +	root->LinkEndChild( cxn );  
      +	cxn->SetAttribute("ip", "192.168.0.1");
      +	cxn->SetDoubleAttribute("timeout", 123.456); // floating point attrib
      +	
      +	dump_to_stdout( &doc );
      +	doc.SaveFile( "appsettings.xml" );  
      +} 
      +

      +The dump_to_stdout function will show this structure:

      +

      Document
      ++ Declaration
      ++ Element [MyApp]
      + (No attributes)
      +  + Comment: [ Settings for MyApp ]
      +  + Element [Messages]
      + (No attributes)
      +    + Element [Welcome]
      + (No attributes)
      +      + Text: [Welcome to MyApp]
      +    + Element [Farewell]
      + (No attributes)
      +      + Text: [Thank you for using MyApp]
      +  + Element [Windows]
      + (No attributes)
      +    + Element [Window]
      +      + name: value=[MainFrame]
      +      + x: value=[5] int=5 d=5.0
      +      + y: value=[15] int=15 d=15.0
      +      + w: value=[400] int=400 d=400.0
      +      + h: value=[250] int=250 d=250.0
      +      5 attributes
      +  + Element [Connection]
      +    + ip: value=[192.168.0.1] int=192 d=192.2
      +    + timeout: value=[123.456000] int=123 d=123.5
      +    2 attributes
      +

      +I was surprised that TinyXml, by default, writes the XML in what other APIs call a "pretty" format - it modifies the whitespace of text of elements that contain other nodes so that writing the tree includes an indication of nesting level.

      +I haven't looked yet to see if there is a way to turn off indenting when writing a file - its bound to be easy.

      +[Lee: It's easy in STL mode, just use cout << myDoc. Non-STL mode is always in "pretty" format. Adding a switch would be a nice feature and has been requested.]

      +

      XML to/from C++ objects

      +

      +

      Intro

      +

      +This example assumes you're loading and saving your app settings in an XML file, e.g. something like example4.xml.

      +There are a number of ways to do this. For example, look into the TinyBind project at http://sourceforge.net/projects/tinybind

      +This section shows a plain-old approach to loading and saving a basic object structure using XML.

      +

      Set up your object classes

      +

      +Start off with some basic classes like these:

      +

      #include <string>
      +#include <map>
      +using namespace std;
      +
      +typedef std::map<std::string,std::string> MessageMap;
      +
      +// a basic window abstraction - demo purposes only
      +class WindowSettings
      +{
      +public:
      +	int x,y,w,h;
      +	string name;
      +
      +	WindowSettings()
      +		: x(0), y(0), w(100), h(100), name("Untitled")
      +	{
      +	}
      +
      +	WindowSettings(int x, int y, int w, int h, const string& name)
      +	{
      +		this->x=x;
      +		this->y=y;
      +		this->w=w;
      +		this->h=h;
      +		this->name=name;
      +	}
      +};
      +
      +class ConnectionSettings
      +{
      +public:
      +	string ip;
      +	double timeout;
      +};
      +
      +class AppSettings
      +{
      +public:
      +	string m_name;
      +	MessageMap m_messages;
      +	list<WindowSettings> m_windows;
      +	ConnectionSettings m_connection;
      +
      +	AppSettings() {}
      +
      +	void save(const char* pFilename);
      +	void load(const char* pFilename);
      +	
      +	// just to show how to do it
      +	void setDemoValues()
      +	{
      +		m_name="MyApp";
      +		m_messages.clear();
      +		m_messages["Welcome"]="Welcome to "+m_name;
      +		m_messages["Farewell"]="Thank you for using "+m_name;
      +		m_windows.clear();
      +		m_windows.push_back(WindowSettings(15,15,400,250,"Main"));
      +		m_connection.ip="Unknown";
      +		m_connection.timeout=123.456;
      +	}
      +};
      +

      +This is a basic main() that shows how to create a default settings object tree, save it and load it again:

      +

      int main(void)
      +{
      +	AppSettings settings;
      +	
      +	settings.save("appsettings2.xml");
      +	settings.load("appsettings2.xml");
      +	return 0;
      +}
      +

      +The following main() shows creation, modification, saving and then loading of a settings structure:

      +

      int main(void)
      +{
      +	// block: customise and save settings
      +	{
      +		AppSettings settings;
      +		settings.m_name="HitchHikerApp";
      +		settings.m_messages["Welcome"]="Don't Panic";
      +		settings.m_messages["Farewell"]="Thanks for all the fish";
      +		settings.m_windows.push_back(WindowSettings(15,25,300,250,"BookFrame"));
      +		settings.m_connection.ip="192.168.0.77";
      +		settings.m_connection.timeout=42.0;
      +
      +		settings.save("appsettings2.xml");
      +	}
      +	
      +	// block: load settings
      +	{
      +		AppSettings settings;
      +		settings.load("appsettings2.xml");
      +		printf("%s: %s\n", settings.m_name.c_str(), 
      +			settings.m_messages["Welcome"].c_str());
      +		WindowSettings & w=settings.m_windows.front();
      +		printf("%s: Show window '%s' at %d,%d (%d x %d)\n", 
      +			settings.m_name.c_str(), w.name.c_str(), w.x, w.y, w.w, w.h);
      +		printf("%s: %s\n", settings.m_name.c_str(), settings.m_messages["Farewell"].c_str());
      +	}
      +	return 0;
      +}
      +

      +When the save() and load() are completed (see below), running this main() displays on the console:

      +

      HitchHikerApp: Don't Panic
      +HitchHikerApp: Show window 'BookFrame' at 15,25 (300 x 100)
      +HitchHikerApp: Thanks for all the fish
      +

      +

      Encode C++ state as XML

      +

      +There are lots of different ways to approach saving this to a file. Here's one:

      +

      void AppSettings::save(const char* pFilename)
      +{
      +	TiXmlDocument doc;  
      +	TiXmlElement* msg;
      +	TiXmlComment * comment;
      +	string s;
      + 	TiXmlDeclaration* decl = new TiXmlDeclaration( "1.0", "", "" );  
      +	doc.LinkEndChild( decl ); 
      + 
      +	TiXmlElement * root = new TiXmlElement(m_name.c_str());  
      +	doc.LinkEndChild( root );  
      +
      +	comment = new TiXmlComment();
      +	s=" Settings for "+m_name+" ";
      +	comment->SetValue(s.c_str());  
      +	root->LinkEndChild( comment );  
      +
      +	// block: messages
      +	{
      +		MessageMap::iterator iter;
      +
      +		TiXmlElement * msgs = new TiXmlElement( "Messages" );  
      +		root->LinkEndChild( msgs );  
      + 
      +		for (iter=m_messages.begin(); iter != m_messages.end(); iter++)
      +		{
      +			const string & key=(*iter).first;
      +			const string & value=(*iter).second;
      +			msg = new TiXmlElement(key.c_str());  
      +			msg->LinkEndChild( new TiXmlText(value.c_str()));  
      +			msgs->LinkEndChild( msg );  
      +		}
      +	}
      +
      +	// block: windows
      +	{
      +		TiXmlElement * windowsNode = new TiXmlElement( "Windows" );  
      +		root->LinkEndChild( windowsNode );  
      +
      +		list<WindowSettings>::iterator iter;
      +
      +		for (iter=m_windows.begin(); iter != m_windows.end(); iter++)
      +		{
      +			const WindowSettings& w=*iter;
      +
      +			TiXmlElement * window;
      +			window = new TiXmlElement( "Window" );  
      +			windowsNode->LinkEndChild( window );  
      +			window->SetAttribute("name", w.name.c_str());
      +			window->SetAttribute("x", w.x);
      +			window->SetAttribute("y", w.y);
      +			window->SetAttribute("w", w.w);
      +			window->SetAttribute("h", w.h);
      +		}
      +	}
      +
      +	// block: connection
      +	{
      +		TiXmlElement * cxn = new TiXmlElement( "Connection" );  
      +		root->LinkEndChild( cxn );  
      +		cxn->SetAttribute("ip", m_connection.ip.c_str());
      +		cxn->SetDoubleAttribute("timeout", m_connection.timeout); 
      +	}
      +
      +	doc.SaveFile(pFilename);  
      +}
      +

      +Running this with the modified main produces this file:

      +

      <?xml version="1.0" ?>
      +<HitchHikerApp>
      +    <!-- Settings for HitchHikerApp -->
      +    <Messages>
      +        <Farewell>Thanks for all the fish</Farewell>
      +        <Welcome>Don&apos;t Panic</Welcome>
      +    </Messages>
      +    <Windows>
      +        <Window name="BookFrame" x="15" y="25" w="300" h="250" />
      +    </Windows>
      +    <Connection ip="192.168.0.77" timeout="42.000000" />
      +</HitchHikerApp>
      +

      +

      Decoding state from XML

      +

      +As with encoding objects, there are a number of approaches to decoding XML into your own C++ object structure. The following approach uses TiXmlHandles.

      +

      void AppSettings::load(const char* pFilename)
      +{
      +	TiXmlDocument doc(pFilename);
      +	if (!doc.LoadFile()) return;
      +
      +	TiXmlHandle hDoc(&doc);
      +	TiXmlElement* pElem;
      +	TiXmlHandle hRoot(0);
      +
      +	// block: name
      +	{
      +		pElem=hDoc.FirstChildElement().Element();
      +		// should always have a valid root but handle gracefully if it does
      +		if (!pElem) return;
      +		m_name=pElem->Value();
      +
      +		// save this for later
      +		hRoot=TiXmlHandle(pElem);
      +	}
      +
      +	// block: string table
      +	{
      +		m_messages.clear(); // trash existing table
      +
      +		pElem=hRoot.FirstChild( "Messages" ).FirstChild().Element();
      +		for( pElem; pElem; pElem=pElem->NextSiblingElement())
      +		{
      +			const char *pKey=pElem->Value();
      +			const char *pText=pElem->GetText();
      +			if (pKey && pText) 
      +			{
      +				m_messages[pKey]=pText;
      +			}
      +		}
      +	}
      +
      +	// block: windows
      +	{
      +		m_windows.clear(); // trash existing list
      +
      +		TiXmlElement* pWindowNode=hRoot.FirstChild( "Windows" ).FirstChild().Element();
      +		for( pWindowNode; pWindowNode; pWindowNode=pWindowNode->NextSiblingElement())
      +		{
      +			WindowSettings w;
      +			const char *pName=pWindowNode->Attribute("name");
      +			if (pName) w.name=pName;
      +			
      +			pWindowNode->QueryIntAttribute("x", &w.x); // If this fails, original value is left as-is
      +			pWindowNode->QueryIntAttribute("y", &w.y);
      +			pWindowNode->QueryIntAttribute("w", &w.w);
      +			pWindowNode->QueryIntAttribute("hh", &w.h);
      +
      +			m_windows.push_back(w);
      +		}
      +	}
      +
      +	// block: connection
      +	{
      +		pElem=hRoot.FirstChild("Connection").Element();
      +		if (pElem)
      +		{
      +			m_connection.ip=pElem->Attribute("ip");
      +			pElem->QueryDoubleAttribute("timeout",&m_connection.timeout);
      +		}
      +	}
      +}
      +

      +

      Full listing for dump_to_stdout

      +

      +Below is a copy-and-paste demo program for loading arbitrary XML files and dumping the structure to STDOUT using the recursive traversal listed above.

      +

      // tutorial demo program
      +#include "stdafx.h"
      +#include "tinyxml.h"
      +
      +// ----------------------------------------------------------------------
      +// STDOUT dump and indenting utility functions
      +// ----------------------------------------------------------------------
      +const unsigned int NUM_INDENTS_PER_SPACE=2;
      +
      +const char * getIndent( unsigned int numIndents )
      +{
      +	static const char * pINDENT="                                      + ";
      +	static const unsigned int LENGTH=strlen( pINDENT );
      +	unsigned int n=numIndents*NUM_INDENTS_PER_SPACE;
      +	if ( n > LENGTH ) n = LENGTH;
      +
      +	return &pINDENT[ LENGTH-n ];
      +}
      +
      +// same as getIndent but no "+" at the end
      +const char * getIndentAlt( unsigned int numIndents )
      +{
      +	static const char * pINDENT="                                        ";
      +	static const unsigned int LENGTH=strlen( pINDENT );
      +	unsigned int n=numIndents*NUM_INDENTS_PER_SPACE;
      +	if ( n > LENGTH ) n = LENGTH;
      +
      +	return &pINDENT[ LENGTH-n ];
      +}
      +
      +int dump_attribs_to_stdout(TiXmlElement* pElement, unsigned int indent)
      +{
      +	if ( !pElement ) return 0;
      +
      +	TiXmlAttribute* pAttrib=pElement->FirstAttribute();
      +	int i=0;
      +	int ival;
      +	double dval;
      +	const char* pIndent=getIndent(indent);
      +	printf("\n");
      +	while (pAttrib)
      +	{
      +		printf( "%s%s: value=[%s]", pIndent, pAttrib->Name(), pAttrib->Value());
      +
      +		if (pAttrib->QueryIntValue(&ival)==TIXML_SUCCESS)    printf( " int=%d", ival);
      +		if (pAttrib->QueryDoubleValue(&dval)==TIXML_SUCCESS) printf( " d=%1.1f", dval);
      +		printf( "\n" );
      +		i++;
      +		pAttrib=pAttrib->Next();
      +	}
      +	return i;	
      +}
      +
      +void dump_to_stdout( TiXmlNode* pParent, unsigned int indent = 0 )
      +{
      +	if ( !pParent ) return;
      +
      +	TiXmlNode* pChild;
      +	TiXmlText* pText;
      +	int t = pParent->Type();
      +	printf( "%s", getIndent(indent));
      +	int num;
      +
      +	switch ( t )
      +	{
      +	case TiXmlNode::DOCUMENT:
      +		printf( "Document" );
      +		break;
      +
      +	case TiXmlNode::ELEMENT:
      +		printf( "Element [%s]", pParent->Value() );
      +		num=dump_attribs_to_stdout(pParent->ToElement(), indent+1);
      +		switch(num)
      +		{
      +			case 0:  printf( " (No attributes)"); break;
      +			case 1:  printf( "%s1 attribute", getIndentAlt(indent)); break;
      +			default: printf( "%s%d attributes", getIndentAlt(indent), num); break;
      +		}
      +		break;
      +
      +	case TiXmlNode::COMMENT:
      +		printf( "Comment: [%s]", pParent->Value());
      +		break;
      +
      +	case TiXmlNode::UNKNOWN:
      +		printf( "Unknown" );
      +		break;
      +
      +	case TiXmlNode::TEXT:
      +		pText = pParent->ToText();
      +		printf( "Text: [%s]", pText->Value() );
      +		break;
      +
      +	case TiXmlNode::DECLARATION:
      +		printf( "Declaration" );
      +		break;
      +	default:
      +		break;
      +	}
      +	printf( "\n" );
      +	for ( pChild = pParent->FirstChild(); pChild != 0; pChild = pChild->NextSibling()) 
      +	{
      +		dump_to_stdout( pChild, indent+1 );
      +	}
      +}
      +
      +// load the named file and dump its structure to STDOUT
      +void dump_to_stdout(const char* pFilename)
      +{
      +	TiXmlDocument doc(pFilename);
      +	bool loadOkay = doc.LoadFile();
      +	if (loadOkay)
      +	{
      +		printf("\n%s:\n", pFilename);
      +		dump_to_stdout( &doc ); // defined later in the tutorial
      +	}
      +	else
      +	{
      +		printf("Failed to load file \"%s\"\n", pFilename);
      +	}
      +}
      +
      +// ----------------------------------------------------------------------
      +// main() for printing files named on the command line
      +// ----------------------------------------------------------------------
      +int main(int argc, char* argv[])
      +{
      +	for (int i=1; i<argc; i++)
      +	{
      +		dump_to_stdout(argv[i]);
      +	}
      +	return 0;
      +}
      +

      +Run this from the command line or a DOS window, e.g.:

      +

      C:\dev\tinyxml> Debug\tinyxml_1.exe example1.xml
      +
      +example1.xml:
      +Document
      ++ Declaration
      ++ Element [Hello]
      + (No attributes)
      +  + Text: [World]
      +

      + Authors and Changes

        +
      • +Written by Ellers, April, May, June 2005
      • +
      • +Minor edits and integration into doc system, Lee Thomason September 2005
      • +
      • +Updated by Ellers, October 2005
      • +
      +
      Generated on Sun May 6 15:41:23 2007 for TinyXml by  + +doxygen 1.4.7
      + + diff --git a/Engine/lib/tinyxml/readme.txt b/Engine/lib/tinyxml/readme.txt new file mode 100644 index 000000000..14ec3d2e0 --- /dev/null +++ b/Engine/lib/tinyxml/readme.txt @@ -0,0 +1,530 @@ +/** @mainpage + +

      TinyXML

      + +TinyXML is a simple, small, C++ XML parser that can be easily +integrated into other programs. + +

      What it does.

      + +In brief, TinyXML parses an XML document, and builds from that a +Document Object Model (DOM) that can be read, modified, and saved. + +XML stands for "eXtensible Markup Language." It allows you to create +your own document markups. Where HTML does a very good job of marking +documents for browsers, XML allows you to define any kind of document +markup, for example a document that describes a "to do" list for an +organizer application. XML is a very structured and convenient format. +All those random file formats created to store application data can +all be replaced with XML. One parser for everything. + +The best place for the complete, correct, and quite frankly hard to +read spec is at +http://www.w3.org/TR/2004/REC-xml-20040204/. An intro to XML +(that I really like) can be found at +http://skew.org/xml/tutorial. + +There are different ways to access and interact with XML data. +TinyXML uses a Document Object Model (DOM), meaning the XML data is parsed +into a C++ objects that can be browsed and manipulated, and then +written to disk or another output stream. You can also construct an XML document +from scratch with C++ objects and write this to disk or another output +stream. + +TinyXML is designed to be easy and fast to learn. It is two headers +and four cpp files. Simply add these to your project and off you go. +There is an example file - xmltest.cpp - to get you started. + +TinyXML is released under the ZLib license, +so you can use it in open source or commercial code. The details +of the license are at the top of every source file. + +TinyXML attempts to be a flexible parser, but with truly correct and +compliant XML output. TinyXML should compile on any reasonably C++ +compliant system. It does not rely on exceptions or RTTI. It can be +compiled with or without STL support. TinyXML fully supports +the UTF-8 encoding, and the first 64k character entities. + + +

      What it doesn't do.

      + +TinyXML doesn't parse or use DTDs (Document Type Definitions) or XSLs +(eXtensible Stylesheet Language.) There are other parsers out there +(check out www.sourceforge.org, search for XML) that are much more fully +featured. But they are also much bigger, take longer to set up in +your project, have a higher learning curve, and often have a more +restrictive license. If you are working with browsers or have more +complete XML needs, TinyXML is not the parser for you. + +The following DTD syntax will not parse at this time in TinyXML: + +@verbatim + + ]> +@endverbatim + +because TinyXML sees this as a !DOCTYPE node with an illegally +embedded !ELEMENT node. This may be addressed in the future. + +

      Tutorials.

      + +For the impatient, here is a tutorial to get you going. A great way to get started, +but it is worth your time to read this (very short) manual completely. + +- @subpage tutorial0 + +

      Code Status.

      + +TinyXML is mature, tested code. It is very stable. If you find +bugs, please file a bug report on the sourceforge web site +(www.sourceforge.net/projects/tinyxml). We'll get them straightened +out as soon as possible. + +There are some areas of improvement; please check sourceforge if you are +interested in working on TinyXML. + +

      Related Projects

      + +TinyXML projects you may find useful! (Descriptions provided by the projects.) + +
        +
      • TinyXPath (http://tinyxpath.sourceforge.net). TinyXPath is a small footprint + XPath syntax decoder, written in C++.
      • +
      • TinyXML++ (http://code.google.com/p/ticpp/). TinyXML++ is a completely new + interface to TinyXML that uses MANY of the C++ strengths. Templates, + exceptions, and much better error handling.
      • +
      + +

      Features

      + +

      Using STL

      + +TinyXML can be compiled to use or not use STL. When using STL, TinyXML +uses the std::string class, and fully supports std::istream, std::ostream, +operator<<, and operator>>. Many API methods have both 'const char*' and +'const std::string&' forms. + +When STL support is compiled out, no STL files are included whatsoever. All +the string classes are implemented by TinyXML itself. API methods +all use the 'const char*' form for input. + +Use the compile time #define: + + TIXML_USE_STL + +to compile one version or the other. This can be passed by the compiler, +or set as the first line of "tinyxml.h". + +Note: If compiling the test code in Linux, setting the environment +variable TINYXML_USE_STL=YES/NO will control STL compilation. In the +Windows project file, STL and non STL targets are provided. In your project, +It's probably easiest to add the line "#define TIXML_USE_STL" as the first +line of tinyxml.h. + +

      UTF-8

      + +TinyXML supports UTF-8 allowing to manipulate XML files in any language. TinyXML +also supports "legacy mode" - the encoding used before UTF-8 support and +probably best described as "extended ascii". + +Normally, TinyXML will try to detect the correct encoding and use it. However, +by setting the value of TIXML_DEFAULT_ENCODING in the header file, TinyXML +can be forced to always use one encoding. + +TinyXML will assume Legacy Mode until one of the following occurs: +
        +
      1. If the non-standard but common "UTF-8 lead bytes" (0xef 0xbb 0xbf) + begin the file or data stream, TinyXML will read it as UTF-8.
      2. +
      3. If the declaration tag is read, and it has an encoding="UTF-8", then + TinyXML will read it as UTF-8.
      4. +
      5. If the declaration tag is read, and it has no encoding specified, then TinyXML will + read it as UTF-8.
      6. +
      7. If the declaration tag is read, and it has an encoding="something else", then TinyXML + will read it as Legacy Mode. In legacy mode, TinyXML will work as it did before. It's + not clear what that mode does exactly, but old content should keep working.
      8. +
      9. Until one of the above criteria is met, TinyXML runs in Legacy Mode.
      10. +
      + +What happens if the encoding is incorrectly set or detected? TinyXML will try +to read and pass through text seen as improperly encoded. You may get some strange results or +mangled characters. You may want to force TinyXML to the correct mode. + +You may force TinyXML to Legacy Mode by using LoadFile( TIXML_ENCODING_LEGACY ) or +LoadFile( filename, TIXML_ENCODING_LEGACY ). You may force it to use legacy mode all +the time by setting TIXML_DEFAULT_ENCODING = TIXML_ENCODING_LEGACY. Likewise, you may +force it to TIXML_ENCODING_UTF8 with the same technique. + +For English users, using English XML, UTF-8 is the same as low-ASCII. You +don't need to be aware of UTF-8 or change your code in any way. You can think +of UTF-8 as a "superset" of ASCII. + +UTF-8 is not a double byte format - but it is a standard encoding of Unicode! +TinyXML does not use or directly support wchar, TCHAR, or Microsoft's _UNICODE at this time. +It is common to see the term "Unicode" improperly refer to UTF-16, a wide byte encoding +of unicode. This is a source of confusion. + +For "high-ascii" languages - everything not English, pretty much - TinyXML can +handle all languages, at the same time, as long as the XML is encoded +in UTF-8. That can be a little tricky, older programs and operating systems +tend to use the "default" or "traditional" code page. Many apps (and almost all +modern ones) can output UTF-8, but older or stubborn (or just broken) ones +still output text in the default code page. + +For example, Japanese systems traditionally use SHIFT-JIS encoding. +Text encoded as SHIFT-JIS can not be read by TinyXML. +A good text editor can import SHIFT-JIS and then save as UTF-8. + +The Skew.org link does a great +job covering the encoding issue. + +The test file "utf8test.xml" is an XML containing English, Spanish, Russian, +and Simplified Chinese. (Hopefully they are translated correctly). The file +"utf8test.gif" is a screen capture of the XML file, rendered in IE. Note that +if you don't have the correct fonts (Simplified Chinese or Russian) on your +system, you won't see output that matches the GIF file even if you can parse +it correctly. Also note that (at least on my Windows machine) console output +is in a Western code page, so that Print() or printf() cannot correctly display +the file. This is not a bug in TinyXML - just an OS issue. No data is lost or +destroyed by TinyXML. The console just doesn't render UTF-8. + + +

      Entities

      +TinyXML recognizes the pre-defined "character entities", meaning special +characters. Namely: + +@verbatim + & & + < < + > > + " " + ' ' +@endverbatim + +These are recognized when the XML document is read, and translated to there +UTF-8 equivalents. For instance, text with the XML of: + +@verbatim + Far & Away +@endverbatim + +will have the Value() of "Far & Away" when queried from the TiXmlText object, +and will be written back to the XML stream/file as an ampersand. Older versions +of TinyXML "preserved" character entities, but the newer versions will translate +them into characters. + +Additionally, any character can be specified by its Unicode code point: +The syntax " " or " " are both to the non-breaking space characher. + +

      Printing

      +TinyXML can print output in several different ways that all have strengths and limitations. + +- Print( FILE* ). Output to a std-C stream, which includes all C files as well as stdout. + - "Pretty prints", but you don't have control over printing options. + - The output is streamed directly to the FILE object, so there is no memory overhead + in the TinyXML code. + - used by Print() and SaveFile() + +- operator<<. Output to a c++ stream. + - Integrates with standart C++ iostreams. + - Outputs in "network printing" mode without line breaks. Good for network transmission + and moving XML between C++ objects, but hard for a human to read. + +- TiXmlPrinter. Output to a std::string or memory buffer. + - API is less concise + - Future printing options will be put here. + - Printing may change slightly in future versions as it is refined and expanded. + +

      Streams

      +With TIXML_USE_STL on TinyXML supports C++ streams (operator <<,>>) streams as well +as C (FILE*) streams. There are some differences that you may need to be aware of. + +C style output: + - based on FILE* + - the Print() and SaveFile() methods + + Generates formatted output, with plenty of white space, intended to be as + human-readable as possible. They are very fast, and tolerant of ill formed + XML documents. For example, an XML document that contains 2 root elements + and 2 declarations, will still print. + +C style input: + - based on FILE* + - the Parse() and LoadFile() methods + + A fast, tolerant read. Use whenever you don't need the C++ streams. + +C++ style output: + - based on std::ostream + - operator<< + + Generates condensed output, intended for network transmission rather than + readability. Depending on your system's implementation of the ostream class, + these may be somewhat slower. (Or may not.) Not tolerant of ill formed XML: + a document should contain the correct one root element. Additional root level + elements will not be streamed out. + +C++ style input: + - based on std::istream + - operator>> + + Reads XML from a stream, making it useful for network transmission. The tricky + part is knowing when the XML document is complete, since there will almost + certainly be other data in the stream. TinyXML will assume the XML data is + complete after it reads the root element. Put another way, documents that + are ill-constructed with more than one root element will not read correctly. + Also note that operator>> is somewhat slower than Parse, due to both + implementation of the STL and limitations of TinyXML. + +

      White space

      +The world simply does not agree on whether white space should be kept, or condensed. +For example, pretend the '_' is a space, and look at "Hello____world". HTML, and +at least some XML parsers, will interpret this as "Hello_world". They condense white +space. Some XML parsers do not, and will leave it as "Hello____world". (Remember +to keep pretending the _ is a space.) Others suggest that __Hello___world__ should become +Hello___world. + +It's an issue that hasn't been resolved to my satisfaction. TinyXML supports the +first 2 approaches. Call TiXmlBase::SetCondenseWhiteSpace( bool ) to set the desired behavior. +The default is to condense white space. + +If you change the default, you should call TiXmlBase::SetCondenseWhiteSpace( bool ) +before making any calls to Parse XML data, and I don't recommend changing it after +it has been set. + + +

      Handles

      + +Where browsing an XML document in a robust way, it is important to check +for null returns from method calls. An error safe implementation can +generate a lot of code like: + +@verbatim +TiXmlElement* root = document.FirstChildElement( "Document" ); +if ( root ) +{ + TiXmlElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + TiXmlElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + TiXmlElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. +@endverbatim + +Handles have been introduced to clean this up. Using the TiXmlHandle class, +the previous code reduces to: + +@verbatim +TiXmlHandle docHandle( &document ); +TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement(); +if ( child2 ) +{ + // do something useful +@endverbatim + +Which is much easier to deal with. See TiXmlHandle for more information. + + +

      Row and Column tracking

      +Being able to track nodes and attributes back to their origin location +in source files can be very important for some applications. Additionally, +knowing where parsing errors occured in the original source can be very +time saving. + +TinyXML can tracks the row and column origin of all nodes and attributes +in a text file. The TiXmlBase::Row() and TiXmlBase::Column() methods return +the origin of the node in the source text. The correct tabs can be +configured in TiXmlDocument::SetTabSize(). + + +

      Using and Installing

      + +To Compile and Run xmltest: + +A Linux Makefile and a Windows Visual C++ .dsw file is provided. +Simply compile and run. It will write the file demotest.xml to your +disk and generate output on the screen. It also tests walking the +DOM by printing out the number of nodes found using different +techniques. + +The Linux makefile is very generic and runs on many systems - it +is currently tested on mingw and +MacOSX. You do not need to run 'make depend'. The dependecies have been +hard coded. + +

      Windows project file for VC6

      +
        +
      • tinyxml: tinyxml library, non-STL
      • +
      • tinyxmlSTL: tinyxml library, STL
      • +
      • tinyXmlTest: test app, non-STL
      • +
      • tinyXmlTestSTL: test app, STL
      • +
      + +

      Makefile

      +At the top of the makefile you can set: + +PROFILE, DEBUG, and TINYXML_USE_STL. Details (such that they are) are in +the makefile. + +In the tinyxml directory, type "make clean" then "make". The executable +file 'xmltest' will be created. + + + +

      To Use in an Application:

      + +Add tinyxml.cpp, tinyxml.h, tinyxmlerror.cpp, tinyxmlparser.cpp, tinystr.cpp, and tinystr.h to your +project or make file. That's it! It should compile on any reasonably +compliant C++ system. You do not need to enable exceptions or +RTTI for TinyXML. + + +

      How TinyXML works.

      + +An example is probably the best way to go. Take: +@verbatim + + + + Go to the Toy store! + Do bills + +@endverbatim + +Its not much of a To Do list, but it will do. To read this file +(say "demo.xml") you would create a document, and parse it in: +@verbatim + TiXmlDocument doc( "demo.xml" ); + doc.LoadFile(); +@endverbatim + +And its ready to go. Now lets look at some lines and how they +relate to the DOM. + +@verbatim + +@endverbatim + + The first line is a declaration, and gets turned into the + TiXmlDeclaration class. It will be the first child of the + document node. + + This is the only directive/special tag parsed by by TinyXML. + Generally directive tags are stored in TiXmlUnknown so the + commands wont be lost when it is saved back to disk. + +@verbatim + +@endverbatim + + A comment. Will become a TiXmlComment object. + +@verbatim + +@endverbatim + + The "ToDo" tag defines a TiXmlElement object. This one does not have + any attributes, but does contain 2 other elements. + +@verbatim + +@endverbatim + + Creates another TiXmlElement which is a child of the "ToDo" element. + This element has 1 attribute, with the name "priority" and the value + "1". + +@verbatim +Go to the +@endverbatim + + A TiXmlText. This is a leaf node and cannot contain other nodes. + It is a child of the "Item" TiXmlElement. + +@verbatim + +@endverbatim + + + Another TiXmlElement, this one a child of the "Item" element. + +Etc. + +Looking at the entire object tree, you end up with: +@verbatim +TiXmlDocument "demo.xml" + TiXmlDeclaration "version='1.0'" "standalone=no" + TiXmlComment " Our to do list data" + TiXmlElement "ToDo" + TiXmlElement "Item" Attribtutes: priority = 1 + TiXmlText "Go to the " + TiXmlElement "bold" + TiXmlText "Toy store!" + TiXmlElement "Item" Attributes: priority=2 + TiXmlText "Do bills" +@endverbatim + +

      Documentation

      + +The documentation is build with Doxygen, using the 'dox' +configuration file. + +

      License

      + +TinyXML is released under the zlib license: + +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. + +

      References

      + +The World Wide Web Consortium is the definitive standard body for +XML, and there web pages contain huge amounts of information. + +The definitive spec: +http://www.w3.org/TR/2004/REC-xml-20040204/ + +I also recommend "XML Pocket Reference" by Robert Eckstein and published by +OReilly...the book that got the whole thing started. + +

      Contributors, Contacts, and a Brief History

      + +Thanks very much to everyone who sends suggestions, bugs, ideas, and +encouragement. It all helps, and makes this project fun. A special thanks +to the contributors on the web pages that keep it lively. + +So many people have sent in bugs and ideas, that rather than list here +we try to give credit due in the "changes.txt" file. + +TinyXML was originally written by Lee Thomason. (Often the "I" still +in the documentation.) Lee reviews changes and releases new versions, +with the help of Yves Berquin, Andrew Ellerton, and the tinyXml community. + +We appreciate your suggestions, and would love to know if you +use TinyXML. Hopefully you will enjoy it and find it useful. +Please post questions, comments, file bugs, or contact us at: + +www.sourceforge.net/projects/tinyxml + +Lee Thomason, Yves Berquin, Andrew Ellerton +*/ diff --git a/Engine/lib/tinyxml/tinystr.cpp b/Engine/lib/tinyxml/tinystr.cpp new file mode 100644 index 000000000..681250714 --- /dev/null +++ b/Engine/lib/tinyxml/tinystr.cpp @@ -0,0 +1,116 @@ +/* +www.sourceforge.net/projects/tinyxml +Original file by Yves Berquin. + +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. +*/ + +/* + * THIS FILE WAS ALTERED BY Tyge Lřvset, 7. April 2005. + */ + + +#ifndef TIXML_USE_STL + +#include "tinystr.h" + +// Error value for find primitive +const TiXmlString::size_type TiXmlString::npos = static_cast< TiXmlString::size_type >(-1); + + +// Null rep. +TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } }; + + +void TiXmlString::reserve (size_type cap) +{ + if (cap > capacity()) + { + TiXmlString tmp; + tmp.init(length(), cap); + memcpy(tmp.start(), data(), length()); + swap(tmp); + } +} + + +TiXmlString& TiXmlString::assign(const char* str, size_type len) +{ + size_type cap = capacity(); + if (len > cap || cap > 3*(len + 8)) + { + TiXmlString tmp; + tmp.init(len); + memcpy(tmp.start(), str, len); + swap(tmp); + } + else + { + memmove(start(), str, len); + set_size(len); + } + return *this; +} + + +TiXmlString& TiXmlString::append(const char* str, size_type len) +{ + size_type newsize = length() + len; + if (newsize > capacity()) + { + reserve (newsize + capacity()); + } + memmove(finish(), str, len); + set_size(newsize); + return *this; +} + + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b) +{ + TiXmlString tmp; + tmp.reserve(a.length() + b.length()); + tmp += a; + tmp += b; + return tmp; +} + +TiXmlString operator + (const TiXmlString & a, const char* b) +{ + TiXmlString tmp; + TiXmlString::size_type b_len = static_cast( strlen(b) ); + tmp.reserve(a.length() + b_len); + tmp += a; + tmp.append(b, b_len); + return tmp; +} + +TiXmlString operator + (const char* a, const TiXmlString & b) +{ + TiXmlString tmp; + TiXmlString::size_type a_len = static_cast( strlen(a) ); + tmp.reserve(a_len + b.length()); + tmp.append(a, a_len); + tmp += b; + return tmp; +} + + +#endif // TIXML_USE_STL diff --git a/Engine/lib/tinyxml/tinystr.h b/Engine/lib/tinyxml/tinystr.h new file mode 100644 index 000000000..3c2aa9d54 --- /dev/null +++ b/Engine/lib/tinyxml/tinystr.h @@ -0,0 +1,319 @@ +/* +www.sourceforge.net/projects/tinyxml +Original file by Yves Berquin. + +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. +*/ + +/* + * THIS FILE WAS ALTERED BY Tyge Lovset, 7. April 2005. + * + * - completely rewritten. compact, clean, and fast implementation. + * - sizeof(TiXmlString) = pointer size (4 bytes on 32-bit systems) + * - fixed reserve() to work as per specification. + * - fixed buggy compares operator==(), operator<(), and operator>() + * - fixed operator+=() to take a const ref argument, following spec. + * - added "copy" constructor with length, and most compare operators. + * - added swap(), clear(), size(), capacity(), operator+(). + */ + +#ifndef TIXML_USE_STL + +#ifndef TIXML_STRING_INCLUDED +#define TIXML_STRING_INCLUDED + +#include +#include + +/* The support for explicit isn't that universal, and it isn't really + required - it is used to check that the TiXmlString class isn't incorrectly + used. Be nice to old compilers and macro it here: +*/ +#if defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + #define TIXML_EXPLICIT explicit +#elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + #define TIXML_EXPLICIT explicit +#else + #define TIXML_EXPLICIT +#endif + + +/* + TiXmlString is an emulation of a subset of the std::string template. + Its purpose is to allow compiling TinyXML on compilers with no or poor STL support. + Only the member functions relevant to the TinyXML project have been implemented. + The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase + a string and there's no more room, we allocate a buffer twice as big as we need. +*/ +class TiXmlString +{ + public : + // The size type used + typedef size_t size_type; + + // Error value for find primitive + static const size_type npos; // = -1; + + + // TiXmlString empty constructor + TiXmlString () : rep_(&nullrep_) + { + } + + // TiXmlString copy constructor + TiXmlString ( const TiXmlString & copy) : rep_(0) + { + init(copy.length()); + memcpy(start(), copy.data(), length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0) + { + init( static_cast( strlen(copy) )); + memcpy(start(), copy, length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0) + { + init(len); + memcpy(start(), str, len); + } + + // TiXmlString destructor + ~TiXmlString () + { + quit(); + } + + // = operator + TiXmlString& operator = (const char * copy) + { + return assign( copy, (size_type)strlen(copy)); + } + + // = operator + TiXmlString& operator = (const TiXmlString & copy) + { + return assign(copy.start(), copy.length()); + } + + + // += operator. Maps to append + TiXmlString& operator += (const char * suffix) + { + return append(suffix, static_cast( strlen(suffix) )); + } + + // += operator. Maps to append + TiXmlString& operator += (char single) + { + return append(&single, 1); + } + + // += operator. Maps to append + TiXmlString& operator += (const TiXmlString & suffix) + { + return append(suffix.data(), suffix.length()); + } + + + // Convert a TiXmlString into a null-terminated char * + const char * c_str () const { return rep_->str; } + + // Convert a TiXmlString into a char * (need not be null terminated). + const char * data () const { return rep_->str; } + + // Return the length of a TiXmlString + size_type length () const { return rep_->size; } + + // Alias for length() + size_type size () const { return rep_->size; } + + // Checks if a TiXmlString is empty + bool empty () const { return rep_->size == 0; } + + // Return capacity of string + size_type capacity () const { return rep_->capacity; } + + + // single char extraction + const char& at (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // [] operator + char& operator [] (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // find a char in a string. Return TiXmlString::npos if not found + size_type find (char lookup) const + { + return find(lookup, 0); + } + + // find a char in a string from an offset. Return TiXmlString::npos if not found + size_type find (char tofind, size_type offset) const + { + if (offset >= length()) return npos; + + for (const char* p = c_str() + offset; *p != '\0'; ++p) + { + if (*p == tofind) return static_cast< size_type >( p - c_str() ); + } + return npos; + } + + void clear () + { + //Lee: + //The original was just too strange, though correct: + // TiXmlString().swap(*this); + //Instead use the quit & re-init: + quit(); + init(0,0); + } + + /* Function to reserve a big amount of data when we know we'll need it. Be aware that this + function DOES NOT clear the content of the TiXmlString if any exists. + */ + void reserve (size_type cap); + + TiXmlString& assign (const char* str, size_type len); + + TiXmlString& append (const char* str, size_type len); + + void swap (TiXmlString& other) + { + Rep* r = rep_; + rep_ = other.rep_; + other.rep_ = r; + } + + private: + + void init(size_type sz) { init(sz, sz); } + void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; } + char* start() const { return rep_->str; } + char* finish() const { return rep_->str + rep_->size; } + + struct Rep + { + size_type size, capacity; + char str[1]; + }; + + void init(size_type sz, size_type cap) + { + if (cap) + { + // Lee: the original form: + // rep_ = static_cast(operator new(sizeof(Rep) + cap)); + // doesn't work in some cases of new being overloaded. Switching + // to the normal allocation, although use an 'int' for systems + // that are overly picky about structure alignment. + const size_type bytesNeeded = sizeof(Rep) + cap; + const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int ); + rep_ = reinterpret_cast( new int[ intsNeeded ] ); + + rep_->str[ rep_->size = sz ] = '\0'; + rep_->capacity = cap; + } + else + { + rep_ = &nullrep_; + } + } + + void quit() + { + if (rep_ != &nullrep_) + { + // The rep_ is really an array of ints. (see the allocator, above). + // Cast it back before delete, so the compiler won't incorrectly call destructors. + delete [] ( reinterpret_cast( rep_ ) ); + } + } + + Rep * rep_; + static Rep nullrep_; + +} ; + + +inline bool operator == (const TiXmlString & a, const TiXmlString & b) +{ + return ( a.length() == b.length() ) // optimization on some platforms + && ( strcmp(a.c_str(), b.c_str()) == 0 ); // actual compare +} +inline bool operator < (const TiXmlString & a, const TiXmlString & b) +{ + return strcmp(a.c_str(), b.c_str()) < 0; +} + +inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); } +inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; } +inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); } +inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); } + +inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; } +inline bool operator == (const char* a, const TiXmlString & b) { return b == a; } +inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); } +inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); } + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b); +TiXmlString operator + (const TiXmlString & a, const char* b); +TiXmlString operator + (const char* a, const TiXmlString & b); + + +/* + TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString. + Only the operators that we need for TinyXML have been developped. +*/ +class TiXmlOutStream : public TiXmlString +{ +public : + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const TiXmlString & in) + { + *this += in; + return *this; + } + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const char * in) + { + *this += in; + return *this; + } + +} ; + +#endif // TIXML_STRING_INCLUDED +#endif // TIXML_USE_STL diff --git a/Engine/lib/tinyxml/tinyxml.cpp b/Engine/lib/tinyxml/tinyxml.cpp new file mode 100644 index 000000000..9fea74ccf --- /dev/null +++ b/Engine/lib/tinyxml/tinyxml.cpp @@ -0,0 +1,1893 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) + +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 + +#ifdef TIXML_USE_STL +#include +#include +#endif + +#include "tinyxml.h" + + +bool TiXmlBase::condenseWhiteSpace = true; + +// Microsoft compiler security +FILE* TiXmlFOpen( const char* filename, const char* mode ) +{ + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + FILE* fp = 0; + errno_t err = fopen_s( &fp, filename, mode ); + if ( !err && fp ) + return fp; + return 0; + #else + return fopen( filename, mode ); + #endif +} + +void TiXmlBase::EncodeString( const TIXML_STRING& str, TIXML_STRING* outString ) +{ + int i=0; + + while( i<(int)str.length() ) + { + unsigned char c = (unsigned char) str[i]; + + if ( c == '&' + && i < ( (int)str.length() - 2 ) + && str[i+1] == '#' + && str[i+2] == 'x' ) + { + // Hexadecimal character reference. + // Pass through unchanged. + // © -- copyright symbol, for example. + // + // The -1 is a bug fix from Rob Laveaux. It keeps + // an overflow from happening if there is no ';'. + // There are actually 2 ways to exit this loop - + // while fails (error case) and break (semicolon found). + // However, there is no mechanism (currently) for + // this function to return an error. + while ( i<(int)str.length()-1 ) + { + outString->append( str.c_str() + i, 1 ); + ++i; + if ( str[i] == ';' ) + break; + } + } + else if ( c == '&' ) + { + outString->append( entity[0].str, entity[0].strLength ); + ++i; + } + else if ( c == '<' ) + { + outString->append( entity[1].str, entity[1].strLength ); + ++i; + } + else if ( c == '>' ) + { + outString->append( entity[2].str, entity[2].strLength ); + ++i; + } + else if ( c == '\"' ) + { + outString->append( entity[3].str, entity[3].strLength ); + ++i; + } + else if ( c == '\'' ) + { + outString->append( entity[4].str, entity[4].strLength ); + ++i; + } + else if ( c < 32 ) + { + // Easy pass at non-alpha/numeric/symbol + // Below 32 is symbolic. + char buf[ 32 ]; + + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) ); + #else + sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) ); + #endif + + //*ME: warning C4267: convert 'size_t' to 'int' + //*ME: Int-Cast to make compiler happy ... + outString->append( buf, (int)strlen( buf ) ); + ++i; + } + else + { + //char realc = (char) c; + //outString->append( &realc, 1 ); + *outString += (char) c; // somewhat more efficient function call. + ++i; + } + } +} + + +TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase() +{ + parent = 0; + type = _type; + firstChild = 0; + lastChild = 0; + prev = 0; + next = 0; +} + + +TiXmlNode::~TiXmlNode() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } +} + + +void TiXmlNode::CopyTo( TiXmlNode* target ) const +{ + target->SetValue (value.c_str() ); + target->userData = userData; +} + + +void TiXmlNode::Clear() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } + + firstChild = 0; + lastChild = 0; +} + + +TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node ) +{ + assert( node->parent == 0 || node->parent == this ); + assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() ); + + if ( node->Type() == TiXmlNode::DOCUMENT ) + { + delete node; + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + node->parent = this; + + node->prev = lastChild; + node->next = 0; + + if ( lastChild ) + lastChild->next = node; + else + firstChild = node; // it was an empty list. + + lastChild = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis ) +{ + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + + return LinkEndChild( node ); +} + + +TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ) +{ + if ( !beforeThis || beforeThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->next = beforeThis; + node->prev = beforeThis->prev; + if ( beforeThis->prev ) + { + beforeThis->prev->next = node; + } + else + { + assert( firstChild == beforeThis ); + firstChild = node; + } + beforeThis->prev = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ) +{ + if ( !afterThis || afterThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::DOCUMENT ) + { + if ( GetDocument() ) GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->prev = afterThis; + node->next = afterThis->next; + if ( afterThis->next ) + { + afterThis->next->prev = node; + } + else + { + assert( lastChild == afterThis ); + lastChild = node; + } + afterThis->next = node; + return node; +} + + +TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ) +{ + if ( replaceThis->parent != this ) + return 0; + + TiXmlNode* node = withThis.Clone(); + if ( !node ) + return 0; + + node->next = replaceThis->next; + node->prev = replaceThis->prev; + + if ( replaceThis->next ) + replaceThis->next->prev = node; + else + lastChild = node; + + if ( replaceThis->prev ) + replaceThis->prev->next = node; + else + firstChild = node; + + delete replaceThis; + node->parent = this; + return node; +} + + +bool TiXmlNode::RemoveChild( TiXmlNode* removeThis ) +{ + if ( removeThis->parent != this ) + { + assert( 0 ); + return false; + } + + if ( removeThis->next ) + removeThis->next->prev = removeThis->prev; + else + lastChild = removeThis->prev; + + if ( removeThis->prev ) + removeThis->prev->next = removeThis->next; + else + firstChild = removeThis->next; + + delete removeThis; + return true; +} + +const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = firstChild; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = lastChild; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild(); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling(); + } +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild( val ); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling( val ); + } +} + + +const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = next; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = prev; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +void TiXmlElement::RemoveAttribute( const char * name ) +{ + #ifdef TIXML_USE_STL + TIXML_STRING str( name ); + TiXmlAttribute* node = attributeSet.Find( str ); + #else + TiXmlAttribute* node = attributeSet.Find( name ); + #endif + if ( node ) + { + attributeSet.Remove( node ); + delete node; + } +} + +const TiXmlElement* TiXmlNode::FirstChildElement() const +{ + const TiXmlNode* node; + + for ( node = FirstChild(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = FirstChild( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement() const +{ + const TiXmlNode* node; + + for ( node = NextSibling(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = NextSibling( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlDocument* TiXmlNode::GetDocument() const +{ + const TiXmlNode* node; + + for( node = this; node; node = node->parent ) + { + if ( node->ToDocument() ) + return node->ToDocument(); + } + return 0; +} + + +TiXmlElement::TiXmlElement (const char * _value) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} + + +#ifdef TIXML_USE_STL +TiXmlElement::TiXmlElement( const std::string& _value ) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} +#endif + + +TiXmlElement::TiXmlElement( const TiXmlElement& copy) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + copy.CopyTo( this ); +} + + +void TiXmlElement::operator=( const TiXmlElement& base ) +{ + ClearThis(); + base.CopyTo( this ); +} + + +TiXmlElement::~TiXmlElement() +{ + ClearThis(); +} + + +void TiXmlElement::ClearThis() +{ + Clear(); + while( attributeSet.First() ) + { + TiXmlAttribute* node = attributeSet.First(); + attributeSet.Remove( node ); + delete node; + } +} + + +const char* TiXmlElement::Attribute( const char* name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + return node->Value(); + return 0; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + return &node->ValueStr(); + return 0; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, int* i ) const +{ + const char* s = Attribute( name ); + if ( i ) + { + if ( s ) { + *i = atoi( s ); + } + else { + *i = 0; + } + } + return s; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const +{ + const std::string* s = Attribute( name ); + if ( i ) + { + if ( s ) { + *i = atoi( s->c_str() ); + } + else { + *i = 0; + } + } + return s; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, double* d ) const +{ + const char* s = Attribute( name ); + if ( d ) + { + if ( s ) { + *d = atof( s ); + } + else { + *d = 0; + } + } + return s; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const +{ + const std::string* s = Attribute( name ); + if ( d ) + { + if ( s ) { + *d = atof( s->c_str() ); + } + else { + *d = 0; + } + } + return s; +} +#endif + + +int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryIntValue( ival ); +} + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryIntValue( ival ); +} +#endif + + +int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryDoubleValue( dval ); +} + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + return node->QueryDoubleValue( dval ); +} +#endif + + +void TiXmlElement::SetAttribute( const char * name, int val ) +{ + char buf[64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%d", val ); + #else + sprintf( buf, "%d", val ); + #endif + SetAttribute( name, buf ); +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& name, int val ) +{ + std::ostringstream oss; + oss << val; + SetAttribute( name, oss.str() ); +} +#endif + + +void TiXmlElement::SetDoubleAttribute( const char * name, double val ) +{ + char buf[256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%f", val ); + #else + sprintf( buf, "%f", val ); + #endif + SetAttribute( name, buf ); +} + + +void TiXmlElement::SetAttribute( const char * cname, const char * cvalue ) +{ + #ifdef TIXML_USE_STL + TIXML_STRING _name( cname ); + TIXML_STRING _value( cvalue ); + #else + const char* _name = cname; + const char* _value = cvalue; + #endif + + TiXmlAttribute* node = attributeSet.Find( _name ); + if ( node ) + { + node->SetValue( _value ); + return; + } + + TiXmlAttribute* attrib = new TiXmlAttribute( cname, cvalue ); + if ( attrib ) + { + attributeSet.Add( attrib ); + } + else + { + TiXmlDocument* document = GetDocument(); + if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& name, const std::string& _value ) +{ + TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + { + node->SetValue( _value ); + return; + } + + TiXmlAttribute* attrib = new TiXmlAttribute( name, _value ); + if ( attrib ) + { + attributeSet.Add( attrib ); + } + else + { + TiXmlDocument* document = GetDocument(); + if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); + } +} +#endif + + +void TiXmlElement::Print( FILE* cfile, int depth ) const +{ + int i; + assert( cfile ); + for ( i=0; iNext() ) + { + fprintf( cfile, " " ); + attrib->Print( cfile, depth ); + } + + // There are 3 different formatting approaches: + // 1) An element without children is printed as a node + // 2) An element with only a text child is printed as text + // 3) An element with children is printed on multiple lines. + TiXmlNode* node; + if ( !firstChild ) + { + fprintf( cfile, " />" ); + } + else if ( firstChild == lastChild && firstChild->ToText() ) + { + fprintf( cfile, ">" ); + firstChild->Print( cfile, depth + 1 ); + fprintf( cfile, "", value.c_str() ); + } + else + { + fprintf( cfile, ">" ); + + for ( node = firstChild; node; node=node->NextSibling() ) + { + fprintf( cfile, "\n" ); + + if ( node->ToText() ) + { + for( i=0; iPrint( cfile, depth+1 ); + } + fprintf( cfile, "\n" ); + for( i=0; i", value.c_str() ); + } +} + + +void TiXmlElement::CopyTo( TiXmlElement* target ) const +{ + // superclass: + TiXmlNode::CopyTo( target ); + + // Element class: + // Clone the attributes, then clone the children. + const TiXmlAttribute* attribute = 0; + for( attribute = attributeSet.First(); + attribute; + attribute = attribute->Next() ) + { + target->SetAttribute( attribute->Name(), attribute->Value() ); + } + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + +bool TiXmlElement::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this, attributeSet.First() ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +TiXmlNode* TiXmlElement::Clone() const +{ + TiXmlElement* clone = new TiXmlElement( Value() ); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +const char* TiXmlElement::GetText() const +{ + const TiXmlNode* child = this->FirstChild(); + if ( child ) { + const TiXmlText* childText = child->ToText(); + if ( childText ) { + return childText->Value(); + } + } + return 0; +} + + +TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + ClearError(); +} + +TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} + + +#ifdef TIXML_USE_STL +TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} +#endif + + +TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + copy.CopyTo( this ); +} + + +void TiXmlDocument::operator=( const TiXmlDocument& copy ) +{ + Clear(); + copy.CopyTo( this ); +} + + +bool TiXmlDocument::LoadFile( TiXmlEncoding encoding ) +{ + // See STL_STRING_BUG below. + //StringToBuffer buf( value ); + + return LoadFile( Value(), encoding ); +} + + +bool TiXmlDocument::SaveFile() const +{ + // See STL_STRING_BUG below. +// StringToBuffer buf( value ); +// +// if ( buf.buffer && SaveFile( buf.buffer ) ) +// return true; +// +// return false; + return SaveFile( Value() ); +} + +bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding ) +{ + // There was a really terrifying little bug here. The code: + // value = filename + // in the STL case, cause the assignment method of the std::string to + // be called. What is strange, is that the std::string had the same + // address as it's c_str() method, and so bad things happen. Looks + // like a bug in the Microsoft STL implementation. + // Add an extra string to avoid the crash. + TIXML_STRING filename( _filename ); + value = filename; + + // reading in binary mode so that tinyxml can normalize the EOL + FILE* file = TiXmlFOpen( value.c_str (), "rb" ); + + if ( file ) + { + bool result = LoadFile( file, encoding ); + fclose( file ); + return result; + } + else + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } +} + +bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding ) +{ + if ( !file ) + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Delete the existing data: + Clear(); + location.Clear(); + + // Get the file size, so we can pre-allocate the string. HUGE speed impact. + long length = 0; + fseek( file, 0, SEEK_END ); + length = ftell( file ); + fseek( file, 0, SEEK_SET ); + + // Strange case, but good to handle up front. + if ( length <= 0 ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // If we have a file, assume it is all one big XML file, and read it in. + // The document parser may decide the document ends sooner than the entire file, however. + TIXML_STRING data; + data.reserve( length ); + + // Subtle bug here. TinyXml did use fgets. But from the XML spec: + // 2.11 End-of-Line Handling + // + // + // ...the XML processor MUST behave as if it normalized all line breaks in external + // parsed entities (including the document entity) on input, before parsing, by translating + // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to + // a single #xA character. + // + // + // It is not clear fgets does that, and certainly isn't clear it works cross platform. + // Generally, you expect fgets to translate from the convention of the OS to the c/unix + // convention, and not work generally. + + /* + while( fgets( buf, sizeof(buf), file ) ) + { + data += buf; + } + */ + + char* buf = new char[ length+1 ]; + buf[0] = 0; + + if ( fread( buf, length, 1, file ) != 1 ) { + delete [] buf; + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + const char* lastPos = buf; + const char* p = buf; + + buf[length] = 0; + while( *p ) { + assert( p < (buf+length) ); + if ( *p == 0xa ) { + // Newline character. No special rules for this. Append all the characters + // since the last string, and include the newline. + data.append( lastPos, (p-lastPos+1) ); // append, include the newline + ++p; // move past the newline + lastPos = p; // and point to the new buffer (may be 0) + assert( p <= (buf+length) ); + } + else if ( *p == 0xd ) { + // Carriage return. Append what we have so far, then + // handle moving forward in the buffer. + if ( (p-lastPos) > 0 ) { + data.append( lastPos, p-lastPos ); // do not add the CR + } + data += (char)0xa; // a proper newline + + if ( *(p+1) == 0xa ) { + // Carriage return - new line sequence + p += 2; + lastPos = p; + assert( p <= (buf+length) ); + } + else { + // it was followed by something else...that is presumably characters again. + ++p; + lastPos = p; + assert( p <= (buf+length) ); + } + } + else { + ++p; + } + } + // Handle any left over characters. + if ( p-lastPos ) { + data.append( lastPos, p-lastPos ); + } + delete [] buf; + buf = 0; + + Parse( data.c_str(), 0, encoding ); + + if ( Error() ) + return false; + else + return true; +} + + +bool TiXmlDocument::SaveFile( const char * filename ) const +{ + // The old c stuff lives on... + FILE* fp = TiXmlFOpen( filename, "w" ); + if ( fp ) + { + bool result = SaveFile( fp ); + fclose( fp ); + return result; + } + return false; +} + + +bool TiXmlDocument::SaveFile( FILE* fp ) const +{ + if ( useMicrosoftBOM ) + { + const unsigned char TIXML_UTF_LEAD_0 = 0xefU; + const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; + const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + + fputc( TIXML_UTF_LEAD_0, fp ); + fputc( TIXML_UTF_LEAD_1, fp ); + fputc( TIXML_UTF_LEAD_2, fp ); + } + Print( fp, 0 ); + return (ferror(fp) == 0); +} + + +void TiXmlDocument::CopyTo( TiXmlDocument* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->error = error; + target->errorId = errorId; + target->errorDesc = errorDesc; + target->tabsize = tabsize; + target->errorLocation = errorLocation; + target->useMicrosoftBOM = useMicrosoftBOM; + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + + +TiXmlNode* TiXmlDocument::Clone() const +{ + TiXmlDocument* clone = new TiXmlDocument(); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlDocument::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + node->Print( cfile, depth ); + fprintf( cfile, "\n" ); + } +} + + +bool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +const TiXmlAttribute* TiXmlAttribute::Next() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} + +/* +TiXmlAttribute* TiXmlAttribute::Next() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} +*/ + +const TiXmlAttribute* TiXmlAttribute::Previous() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} + +/* +TiXmlAttribute* TiXmlAttribute::Previous() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} +*/ + +void TiXmlAttribute::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + TIXML_STRING n, v; + + EncodeString( name, &n ); + EncodeString( value, &v ); + + if (value.find ('\"') == TIXML_STRING::npos) { + if ( cfile ) { + fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\""; + } + } + else { + if ( cfile ) { + fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'"; + } + } +} + + +int TiXmlAttribute::QueryIntValue( int* ival ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%d", ival ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +int TiXmlAttribute::QueryDoubleValue( double* dval ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%lf", dval ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +void TiXmlAttribute::SetIntValue( int _value ) +{ + char buf [64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value); + #else + sprintf (buf, "%d", _value); + #endif + SetValue (buf); +} + +void TiXmlAttribute::SetDoubleValue( double _value ) +{ + char buf [256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%lf", _value); + #else + sprintf (buf, "%lf", _value); + #endif + SetValue (buf); +} + +int TiXmlAttribute::IntValue() const +{ + return atoi (value.c_str ()); +} + +double TiXmlAttribute::DoubleValue() const +{ + return atof (value.c_str ()); +} + + +TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::COMMENT ) +{ + copy.CopyTo( this ); +} + + +void TiXmlComment::operator=( const TiXmlComment& base ) +{ + Clear(); + base.CopyTo( this ); +} + + +void TiXmlComment::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( int i=0; i", value.c_str() ); +} + + +void TiXmlComment::CopyTo( TiXmlComment* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlComment::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlComment::Clone() const +{ + TiXmlComment* clone = new TiXmlComment(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlText::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + if ( cdata ) + { + int i; + fprintf( cfile, "\n" ); + for ( i=0; i\n", value.c_str() ); // unformatted output + } + else + { + TIXML_STRING buffer; + EncodeString( value, &buffer ); + fprintf( cfile, "%s", buffer.c_str() ); + } +} + + +void TiXmlText::CopyTo( TiXmlText* target ) const +{ + TiXmlNode::CopyTo( target ); + target->cdata = cdata; +} + + +bool TiXmlText::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlText::Clone() const +{ + TiXmlText* clone = 0; + clone = new TiXmlText( "" ); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlDeclaration::TiXmlDeclaration( const char * _version, + const char * _encoding, + const char * _standalone ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} + + +#ifdef TIXML_USE_STL +TiXmlDeclaration::TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} +#endif + + +TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + copy.CopyTo( this ); +} + + +void TiXmlDeclaration::operator=( const TiXmlDeclaration& copy ) +{ + Clear(); + copy.CopyTo( this ); +} + + +void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + if ( cfile ) fprintf( cfile, "" ); + if ( str ) (*str) += "?>"; +} + + +void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->version = version; + target->encoding = encoding; + target->standalone = standalone; +} + + +bool TiXmlDeclaration::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlDeclaration::Clone() const +{ + TiXmlDeclaration* clone = new TiXmlDeclaration(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlUnknown::Print( FILE* cfile, int depth ) const +{ + for ( int i=0; i", value.c_str() ); +} + + +void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlUnknown::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlUnknown::Clone() const +{ + TiXmlUnknown* clone = new TiXmlUnknown(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlAttributeSet::TiXmlAttributeSet() +{ + sentinel.next = &sentinel; + sentinel.prev = &sentinel; +} + + +TiXmlAttributeSet::~TiXmlAttributeSet() +{ + assert( sentinel.next == &sentinel ); + assert( sentinel.prev == &sentinel ); +} + + +void TiXmlAttributeSet::Add( TiXmlAttribute* addMe ) +{ + #ifdef TIXML_USE_STL + assert( !Find( TIXML_STRING( addMe->Name() ) ) ); // Shouldn't be multiply adding to the set. + #else + assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set. + #endif + + addMe->next = &sentinel; + addMe->prev = sentinel.prev; + + sentinel.prev->next = addMe; + sentinel.prev = addMe; +} + +void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe ) +{ + TiXmlAttribute* node; + + for( node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node == removeMe ) + { + node->prev->next = node->next; + node->next->prev = node->prev; + node->next = 0; + node->prev = 0; + return; + } + } + assert( 0 ); // we tried to remove a non-linked attribute. +} + + +#ifdef TIXML_USE_STL +const TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const +{ + for( const TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} + +/* +TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} +*/ +#endif + + +const TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const +{ + for( const TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( strcmp( node->name.c_str(), name ) == 0 ) + return node; + } + return 0; +} + +/* +TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( strcmp( node->name.c_str(), name ) == 0 ) + return node; + } + return 0; +} +*/ + +#ifdef TIXML_USE_STL +std::istream& operator>> (std::istream & in, TiXmlNode & base) +{ + TIXML_STRING tag; + tag.reserve( 8 * 1000 ); + base.StreamIn( &in, &tag ); + + base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING ); + return in; +} +#endif + + +#ifdef TIXML_USE_STL +std::ostream& operator<< (std::ostream & out, const TiXmlNode & base) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out << printer.Str(); + + return out; +} + + +std::string& operator<< (std::string& out, const TiXmlNode& base ) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out.append( printer.Str() ); + + return out; +} +#endif + + +TiXmlHandle TiXmlHandle::FirstChild() const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement() const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild(); + for ( i=0; + child && iNextSibling(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild( value ); + for ( i=0; + child && iNextSibling( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement(); + for ( i=0; + child && iNextSiblingElement(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement( value ); + for ( i=0; + child && iNextSiblingElement( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +bool TiXmlPrinter::VisitEnter( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitExit( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) +{ + DoIndent(); + buffer += "<"; + buffer += element.Value(); + + for( const TiXmlAttribute* attrib = firstAttribute; attrib; attrib = attrib->Next() ) + { + buffer += " "; + attrib->Print( 0, 0, &buffer ); + } + + if ( !element.FirstChild() ) + { + buffer += " />"; + DoLineBreak(); + } + else + { + buffer += ">"; + if ( element.FirstChild()->ToText() + && element.LastChild() == element.FirstChild() + && element.FirstChild()->ToText()->CDATA() == false ) + { + simpleTextPrint = true; + // no DoLineBreak()! + } + else + { + DoLineBreak(); + } + } + ++depth; + return true; +} + + +bool TiXmlPrinter::VisitExit( const TiXmlElement& element ) +{ + --depth; + if ( !element.FirstChild() ) + { + // nothing. + } + else + { + if ( simpleTextPrint ) + { + simpleTextPrint = false; + } + else + { + DoIndent(); + } + buffer += ""; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlText& text ) +{ + if ( text.CDATA() ) + { + DoIndent(); + buffer += ""; + DoLineBreak(); + } + else if ( simpleTextPrint ) + { + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + } + else + { + DoIndent(); + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlDeclaration& declaration ) +{ + DoIndent(); + declaration.Print( 0, 0, &buffer ); + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlComment& comment ) +{ + DoIndent(); + buffer += ""; + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlUnknown& unknown ) +{ + DoIndent(); + buffer += "<"; + buffer += unknown.Value(); + buffer += ">"; + DoLineBreak(); + return true; +} + diff --git a/Engine/lib/tinyxml/tinyxml.h b/Engine/lib/tinyxml/tinyxml.h new file mode 100644 index 000000000..c6f40cc27 --- /dev/null +++ b/Engine/lib/tinyxml/tinyxml.h @@ -0,0 +1,1802 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) + +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 TINYXML_INCLUDED +#define TINYXML_INCLUDED + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4530 ) +#pragma warning( disable : 4786 ) +#endif + +#include +#include +#include +#include +#include + +// Help out windows: +#if defined( _DEBUG ) && !defined( DEBUG ) +#define DEBUG +#endif + +#ifdef TIXML_USE_STL + #include + #include + #include + #define TIXML_STRING std::string +#else + #include "tinystr.h" + #define TIXML_STRING TiXmlString +#endif + +// Deprecated library function hell. Compilers want to use the +// new safe versions. This probably doesn't fully address the problem, +// but it gets closer. There are too many compilers for me to fully +// test. If you get compilation troubles, undefine TIXML_SAFE +#define TIXML_SAFE + +#ifdef TIXML_SAFE + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + // Microsoft visual studio, version 2005 and higher. + #define TIXML_SNPRINTF _snprintf_s + #define TIXML_SNSCANF _snscanf_s + #define TIXML_SSCANF sscanf_s + #elif defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + //#pragma message( "Using _sn* functions." ) + #define TIXML_SNPRINTF _snprintf + #define TIXML_SNSCANF _snscanf + #define TIXML_SSCANF sscanf + #elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + //#warning( "Using sn* functions." ) + #define TIXML_SNPRINTF snprintf + #define TIXML_SNSCANF snscanf + #define TIXML_SSCANF sscanf + #else + #define TIXML_SSCANF sscanf + #endif +#endif + +class TiXmlDocument; +class TiXmlElement; +class TiXmlComment; +class TiXmlUnknown; +class TiXmlAttribute; +class TiXmlText; +class TiXmlDeclaration; +class TiXmlParsingData; + +const int TIXML_MAJOR_VERSION = 2; +const int TIXML_MINOR_VERSION = 5; +const int TIXML_PATCH_VERSION = 3; + +/* Internal structure for tracking location of items + in the XML file. +*/ +struct TiXmlCursor +{ + TiXmlCursor() { Clear(); } + void Clear() { row = col = -1; } + + int row; // 0 based. + int col; // 0 based. +}; + + +/** + If you call the Accept() method, it requires being passed a TiXmlVisitor + class to handle callbacks. For nodes that contain other nodes (Document, Element) + you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves + are simple called with Visit(). + + If you return 'true' from a Visit method, recursive parsing will continue. If you return + false, no children of this node or its sibilings will be Visited. + + All flavors of Visit methods have a default implementation that returns 'true' (continue + visiting). You need to only override methods that are interesting to you. + + Generally Accept() is called on the TiXmlDocument, although all nodes suppert Visiting. + + You should never change the document from a callback. + + @sa TiXmlNode::Accept() +*/ +class TiXmlVisitor +{ +public: + virtual ~TiXmlVisitor() {} + + /// Visit a document. + virtual bool VisitEnter( const TiXmlDocument& /*doc*/ ) { return true; } + /// Visit a document. + virtual bool VisitExit( const TiXmlDocument& /*doc*/ ) { return true; } + + /// Visit an element. + virtual bool VisitEnter( const TiXmlElement& /*element*/, const TiXmlAttribute* /*firstAttribute*/ ) { return true; } + /// Visit an element. + virtual bool VisitExit( const TiXmlElement& /*element*/ ) { return true; } + + /// Visit a declaration + virtual bool Visit( const TiXmlDeclaration& /*declaration*/ ) { return true; } + /// Visit a text node + virtual bool Visit( const TiXmlText& /*text*/ ) { return true; } + /// Visit a comment node + virtual bool Visit( const TiXmlComment& /*comment*/ ) { return true; } + /// Visit an unknow node + virtual bool Visit( const TiXmlUnknown& /*unknown*/ ) { return true; } +}; + +// Only used by Attribute::Query functions +enum +{ + TIXML_SUCCESS, + TIXML_NO_ATTRIBUTE, + TIXML_WRONG_TYPE +}; + + +// Used by the parsing routines. +enum TiXmlEncoding +{ + TIXML_ENCODING_UNKNOWN, + TIXML_ENCODING_UTF8, + TIXML_ENCODING_LEGACY +}; + +const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN; + +/** TiXmlBase is a base class for every class in TinyXml. + It does little except to establish that TinyXml classes + can be printed and provide some utility functions. + + In XML, the document and elements can contain + other elements and other types of nodes. + + @verbatim + A Document can contain: Element (container or leaf) + Comment (leaf) + Unknown (leaf) + Declaration( leaf ) + + An Element can contain: Element (container or leaf) + Text (leaf) + Attributes (not on tree) + Comment (leaf) + Unknown (leaf) + + A Decleration contains: Attributes (not on tree) + @endverbatim +*/ +class TiXmlBase +{ + friend class TiXmlNode; + friend class TiXmlElement; + friend class TiXmlDocument; + +public: + TiXmlBase() : userData(0) {} + virtual ~TiXmlBase() {} + + /** All TinyXml classes can print themselves to a filestream + or the string class (TiXmlString in non-STL mode, std::string + in STL mode.) Either or both cfile and str can be null. + + This is a formatted print, and will insert + tabs and newlines. + + (For an unformatted stream, use the << operator.) + */ + virtual void Print( FILE* cfile, int depth ) const = 0; + + /** The world does not agree on whether white space should be kept or + not. In order to make everyone happy, these global, static functions + are provided to set whether or not TinyXml will condense all white space + into a single space or not. The default is to condense. Note changing this + value is not thread safe. + */ + static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; } + + /// Return the current white space setting. + static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; } + + /** Return the position, in the original source file, of this node or attribute. + The row and column are 1-based. (That is the first row and first column is + 1,1). If the returns values are 0 or less, then the parser does not have + a row and column value. + + Generally, the row and column value will be set when the TiXmlDocument::Load(), + TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set + when the DOM was created from operator>>. + + The values reflect the initial load. Once the DOM is modified programmatically + (by adding or changing nodes and attributes) the new values will NOT update to + reflect changes in the document. + + There is a minor performance cost to computing the row and column. Computation + can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value. + + @sa TiXmlDocument::SetTabSize() + */ + int Row() const { return location.row + 1; } + int Column() const { return location.col + 1; } ///< See Row() + + void SetUserData( void* user ) { userData = user; } ///< Set a pointer to arbitrary user data. + void* GetUserData() { return userData; } ///< Get a pointer to arbitrary user data. + const void* GetUserData() const { return userData; } ///< Get a pointer to arbitrary user data. + + // Table that returs, for a given lead byte, the total number of bytes + // in the UTF-8 sequence. + static const int utf8ByteTable[256]; + + virtual const char* Parse( const char* p, + TiXmlParsingData* data, + TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0; + + /** Expands entities in a string. Note this should not contian the tag's '<', '>', etc, + or they will be transformed into entities! + */ + static void EncodeString( const TIXML_STRING& str, TIXML_STRING* out ); + + enum + { + TIXML_NO_ERROR = 0, + TIXML_ERROR, + TIXML_ERROR_OPENING_FILE, + TIXML_ERROR_OUT_OF_MEMORY, + TIXML_ERROR_PARSING_ELEMENT, + TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, + TIXML_ERROR_READING_ELEMENT_VALUE, + TIXML_ERROR_READING_ATTRIBUTES, + TIXML_ERROR_PARSING_EMPTY, + TIXML_ERROR_READING_END_TAG, + TIXML_ERROR_PARSING_UNKNOWN, + TIXML_ERROR_PARSING_COMMENT, + TIXML_ERROR_PARSING_DECLARATION, + TIXML_ERROR_DOCUMENT_EMPTY, + TIXML_ERROR_EMBEDDED_NULL, + TIXML_ERROR_PARSING_CDATA, + TIXML_ERROR_DOCUMENT_TOP_ONLY, + + TIXML_ERROR_STRING_COUNT + }; + +protected: + + static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding ); + inline static bool IsWhiteSpace( char c ) + { + return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); + } + inline static bool IsWhiteSpace( int c ) + { + if ( c < 256 ) + return IsWhiteSpace( (char) c ); + return false; // Again, only truly correct for English/Latin...but usually works. + } + + #ifdef TIXML_USE_STL + static bool StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ); + static bool StreamTo( std::istream * in, int character, TIXML_STRING * tag ); + #endif + + /* Reads an XML name into the string provided. Returns + a pointer just past the last character of the name, + or 0 if the function has an error. + */ + static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding ); + + /* Reads text. Returns a pointer past the given end tag. + Wickedly complex options, but it keeps the (sensitive) code in one place. + */ + static const char* ReadText( const char* in, // where to start + TIXML_STRING* text, // the string read + bool ignoreWhiteSpace, // whether to keep the white space + const char* endTag, // what ends this text + bool ignoreCase, // whether to ignore case in the end tag + TiXmlEncoding encoding ); // the current encoding + + // If an entity has been found, transform it into a character. + static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding ); + + // Get a character, while interpreting entities. + // The length can be from 0 to 4 bytes. + inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding ) + { + assert( p ); + if ( encoding == TIXML_ENCODING_UTF8 ) + { + *length = utf8ByteTable[ *((const unsigned char*)p) ]; + assert( *length >= 0 && *length < 5 ); + } + else + { + *length = 1; + } + + if ( *length == 1 ) + { + if ( *p == '&' ) + return GetEntity( p, _value, length, encoding ); + *_value = *p; + return p+1; + } + else if ( *length ) + { + //strncpy( _value, p, *length ); // lots of compilers don't like this function (unsafe), + // and the null terminator isn't needed + for( int i=0; p[i] && i<*length; ++i ) { + _value[i] = p[i]; + } + return p + (*length); + } + else + { + // Not valid text. + return 0; + } + } + + // Return true if the next characters in the stream are any of the endTag sequences. + // Ignore case only works for english, and should only be relied on when comparing + // to English words: StringEqual( p, "version", true ) is fine. + static bool StringEqual( const char* p, + const char* endTag, + bool ignoreCase, + TiXmlEncoding encoding ); + + static const char* errorString[ TIXML_ERROR_STRING_COUNT ]; + + TiXmlCursor location; + + /// Field containing a generic user pointer + void* userData; + + // None of these methods are reliable for any language except English. + // Good for approximation, not great for accuracy. + static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding ); + static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding ); + inline static int ToLower( int v, TiXmlEncoding encoding ) + { + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( v < 128 ) return tolower( v ); + return v; + } + else + { + return tolower( v ); + } + } + static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); + +private: + TiXmlBase( const TiXmlBase& ); // not implemented. + void operator=( const TiXmlBase& base ); // not allowed. + + struct Entity + { + const char* str; + unsigned int strLength; + char chr; + }; + enum + { + NUM_ENTITY = 5, + MAX_ENTITY_LENGTH = 6 + + }; + static Entity entity[ NUM_ENTITY ]; + static bool condenseWhiteSpace; +}; + + +/** The parent class for everything in the Document Object Model. + (Except for attributes). + Nodes have siblings, a parent, and children. A node can be + in a document, or stand on its own. The type of a TiXmlNode + can be queried, and it can be cast to its more defined type. +*/ +class TiXmlNode : public TiXmlBase +{ + friend class TiXmlDocument; + friend class TiXmlElement; + +public: + #ifdef TIXML_USE_STL + + /** An input stream operator, for every class. Tolerant of newlines and + formatting, but doesn't expect them. + */ + friend std::istream& operator >> (std::istream& in, TiXmlNode& base); + + /** An output stream operator, for every class. Note that this outputs + without any newlines or formatting, as opposed to Print(), which + includes tabs and new lines. + + The operator<< and operator>> are not completely symmetric. Writing + a node to a stream is very well defined. You'll get a nice stream + of output, without any extra whitespace or newlines. + + But reading is not as well defined. (As it always is.) If you create + a TiXmlElement (for example) and read that from an input stream, + the text needs to define an element or junk will result. This is + true of all input streams, but it's worth keeping in mind. + + A TiXmlDocument will read nodes until it reads a root element, and + all the children of that root element. + */ + friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base); + + /// Appends the XML node or attribute to a std::string. + friend std::string& operator<< (std::string& out, const TiXmlNode& base ); + + #endif + + /** The types of XML nodes supported by TinyXml. (All the + unsupported types are picked up by UNKNOWN.) + */ + enum NodeType + { + DOCUMENT, + ELEMENT, + COMMENT, + UNKNOWN, + TEXT, + DECLARATION, + TYPECOUNT + }; + + virtual ~TiXmlNode(); + + /** The meaning of 'value' changes for the specific type of + TiXmlNode. + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + + The subclasses will wrap this function. + */ + const char *Value() const { return value.c_str (); } + + #ifdef TIXML_USE_STL + /** Return Value() as a std::string. If you only use STL, + this is more efficient than calling Value(). + Only available in STL mode. + */ + const std::string& ValueStr() const { return value; } + #endif + + const TIXML_STRING& ValueTStr() const { return value; } + + /** Changes the value of the node. Defined as: + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + */ + void SetValue(const char * _value) { value = _value;} + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Delete all the children of this node. Does not affect 'this'. + void Clear(); + + /// One step up the DOM. + TiXmlNode* Parent() { return parent; } + const TiXmlNode* Parent() const { return parent; } + + const TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children. + TiXmlNode* FirstChild() { return firstChild; } + const TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found. + /// The first child of this node with the matching 'value'. Will be null if none found. + TiXmlNode* FirstChild( const char * _value ) { + // Call through to the const version - safe since nothing is changed. Exiting syntax: cast this to a const (always safe) + // call the method, cast the return back to non-const. + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->FirstChild( _value )); + } + const TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children. + TiXmlNode* LastChild() { return lastChild; } + + const TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children. + TiXmlNode* LastChild( const char * _value ) { + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->LastChild( _value )); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* FirstChild( const std::string& _value ) { return FirstChild (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* LastChild( const std::string& _value ) { return LastChild (_value.c_str ()); } ///< STL std::string form. + #endif + + /** An alternate way to walk the children of a node. + One way to iterate over nodes is: + @verbatim + for( child = parent->FirstChild(); child; child = child->NextSibling() ) + @endverbatim + + IterateChildren does the same thing with the syntax: + @verbatim + child = 0; + while( child = parent->IterateChildren( child ) ) + @endverbatim + + IterateChildren takes the previous child as input and finds + the next one. If the previous child is null, it returns the + first. IterateChildren will return null when done. + */ + const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( previous ) ); + } + + /// This flavor of IterateChildren searches for children with a particular 'value' + const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const char * _value, const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( _value, previous ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + #endif + + /** Add a new node related to this. Adds a child past the LastChild. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertEndChild( const TiXmlNode& addThis ); + + + /** Add a new node related to this. Adds a child past the LastChild. + + NOTE: the node to be added is passed by pointer, and will be + henceforth owned (and deleted) by tinyXml. This method is efficient + and avoids an extra copy, but should be used with care as it + uses a different memory model than the other insert functions. + + @sa InsertEndChild + */ + TiXmlNode* LinkEndChild( TiXmlNode* addThis ); + + /** Add a new node related to this. Adds a child before the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ); + + /** Add a new node related to this. Adds a child after the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ); + + /** Replace a child of this node. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ); + + /// Delete a child of this node. + bool RemoveChild( TiXmlNode* removeThis ); + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling() const { return prev; } + TiXmlNode* PreviousSibling() { return prev; } + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling( const char * ) const; + TiXmlNode* PreviousSibling( const char *_prev ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->PreviousSibling( _prev ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* PreviousSibling( const std::string& _value ) { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* NextSibling( const std::string& _value) { return NextSibling (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Navigate to a sibling node. + const TiXmlNode* NextSibling() const { return next; } + TiXmlNode* NextSibling() { return next; } + + /// Navigate to a sibling node with the given 'value'. + const TiXmlNode* NextSibling( const char * ) const; + TiXmlNode* NextSibling( const char* _next ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->NextSibling( _next ) ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement() const; + TiXmlElement* NextSiblingElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement() ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement( const char * ) const; + TiXmlElement* NextSiblingElement( const char *_next ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement( _next ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* NextSiblingElement( const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement() const; + TiXmlElement* FirstChildElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement() ); + } + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement( const char * _value ) const; + TiXmlElement* FirstChildElement( const char * _value ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement( _value ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* FirstChildElement( const std::string& _value ) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /** Query the type (as an enumerated value, above) of this node. + The possible types are: DOCUMENT, ELEMENT, COMMENT, + UNKNOWN, TEXT, and DECLARATION. + */ + int Type() const { return type; } + + /** Return a pointer to the Document this node lives in. + Returns null if not in a document. + */ + const TiXmlDocument* GetDocument() const; + TiXmlDocument* GetDocument() { + return const_cast< TiXmlDocument* >( (const_cast< const TiXmlNode* >(this))->GetDocument() ); + } + + /// Returns true if this node has no children. + bool NoChildren() const { return !firstChild; } + + virtual const TiXmlDocument* ToDocument() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlElement* ToElement() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlComment* ToComment() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlUnknown* ToUnknown() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlText* ToText() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + virtual TiXmlDocument* ToDocument() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlElement* ToElement() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlComment* ToComment() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlText* ToText() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + /** Create an exact duplicate of this node and return it. The memory must be deleted + by the caller. + */ + virtual TiXmlNode* Clone() const = 0; + + /** Accept a hierchical visit the nodes in the TinyXML DOM. Every node in the + XML tree will be conditionally visited and the host will be called back + via the TiXmlVisitor interface. + + This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse + the XML for the callbacks, so the performance of TinyXML is unchanged by using this + interface versus any other.) + + The interface has been based on ideas from: + + - http://www.saxproject.org/ + - http://c2.com/cgi/wiki?HierarchicalVisitorPattern + + Which are both good references for "visiting". + + An example of using Accept(): + @verbatim + TiXmlPrinter printer; + tinyxmlDoc.Accept( &printer ); + const char* xmlcstr = printer.CStr(); + @endverbatim + */ + virtual bool Accept( TiXmlVisitor* visitor ) const = 0; + +protected: + TiXmlNode( NodeType _type ); + + // Copy to the allocated object. Shared functionality between Clone, Copy constructor, + // and the assignment operator. + void CopyTo( TiXmlNode* target ) const; + + #ifdef TIXML_USE_STL + // The real work of the input operator. + virtual void StreamIn( std::istream* in, TIXML_STRING* tag ) = 0; + #endif + + // Figure out what is at *p, and parse it. Returns null if it is not an xml node. + TiXmlNode* Identify( const char* start, TiXmlEncoding encoding ); + + TiXmlNode* parent; + NodeType type; + + TiXmlNode* firstChild; + TiXmlNode* lastChild; + + TIXML_STRING value; + + TiXmlNode* prev; + TiXmlNode* next; + +private: + TiXmlNode( const TiXmlNode& ); // not implemented. + void operator=( const TiXmlNode& base ); // not allowed. +}; + + +/** An attribute is a name-value pair. Elements have an arbitrary + number of attributes, each with a unique name. + + @note The attributes are not TiXmlNodes, since they are not + part of the tinyXML document object model. There are other + suggested ways to look at this problem. +*/ +class TiXmlAttribute : public TiXmlBase +{ + friend class TiXmlAttributeSet; + +public: + /// Construct an empty attribute. + TiXmlAttribute() : TiXmlBase() + { + document = 0; + prev = next = 0; + } + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlAttribute( const std::string& _name, const std::string& _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + #endif + + /// Construct an attribute with a name and value. + TiXmlAttribute( const char * _name, const char * _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + + const char* Name() const { return name.c_str(); } ///< Return the name of this attribute. + const char* Value() const { return value.c_str(); } ///< Return the value of this attribute. + #ifdef TIXML_USE_STL + const std::string& ValueStr() const { return value; } ///< Return the value of this attribute. + #endif + int IntValue() const; ///< Return the value of this attribute, converted to an integer. + double DoubleValue() const; ///< Return the value of this attribute, converted to a double. + + // Get the tinyxml string representation + const TIXML_STRING& NameTStr() const { return name; } + + /** QueryIntValue examines the value string. It is an alternative to the + IntValue() method with richer error checking. + If the value is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. + + A specialized but useful call. Note that for success it returns 0, + which is the opposite of almost all other TinyXml calls. + */ + int QueryIntValue( int* _value ) const; + /// QueryDoubleValue examines the value string. See QueryIntValue(). + int QueryDoubleValue( double* _value ) const; + + void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute. + void SetValue( const char* _value ) { value = _value; } ///< Set the value. + + void SetIntValue( int _value ); ///< Set the value from an integer. + void SetDoubleValue( double _value ); ///< Set the value from a double. + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetName( const std::string& _name ) { name = _name; } + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Get the next sibling attribute in the DOM. Returns null at end. + const TiXmlAttribute* Next() const; + TiXmlAttribute* Next() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Next() ); + } + + /// Get the previous sibling attribute in the DOM. Returns null at beginning. + const TiXmlAttribute* Previous() const; + TiXmlAttribute* Previous() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Previous() ); + } + + bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; } + bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; } + bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; } + + /* Attribute parsing starts: first letter of the name + returns: the next char after the value end quote + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + // Prints this Attribute to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + + // [internal use] + // Set the document pointer so the attribute can report errors. + void SetDocument( TiXmlDocument* doc ) { document = doc; } + +private: + TiXmlAttribute( const TiXmlAttribute& ); // not implemented. + void operator=( const TiXmlAttribute& base ); // not allowed. + + TiXmlDocument* document; // A pointer back to a document, for error reporting. + TIXML_STRING name; + TIXML_STRING value; + TiXmlAttribute* prev; + TiXmlAttribute* next; +}; + + +/* A class used to manage a group of attributes. + It is only used internally, both by the ELEMENT and the DECLARATION. + + The set can be changed transparent to the Element and Declaration + classes that use it, but NOT transparent to the Attribute + which has to implement a next() and previous() method. Which makes + it a bit problematic and prevents the use of STL. + + This version is implemented with circular lists because: + - I like circular lists + - it demonstrates some independence from the (typical) doubly linked list. +*/ +class TiXmlAttributeSet +{ +public: + TiXmlAttributeSet(); + ~TiXmlAttributeSet(); + + void Add( TiXmlAttribute* attribute ); + void Remove( TiXmlAttribute* attribute ); + + const TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + TiXmlAttribute* First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + + const TiXmlAttribute* Find( const char* _name ) const; + TiXmlAttribute* Find( const char* _name ) { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) ); + } + #ifdef TIXML_USE_STL + const TiXmlAttribute* Find( const std::string& _name ) const; + TiXmlAttribute* Find( const std::string& _name ) { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttributeSet* >(this))->Find( _name ) ); + } + + #endif + +private: + //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element), + //*ME: this class must be also use a hidden/disabled copy-constructor !!! + TiXmlAttributeSet( const TiXmlAttributeSet& ); // not allowed + void operator=( const TiXmlAttributeSet& ); // not allowed (as TiXmlAttribute) + + TiXmlAttribute sentinel; +}; + + +/** The element is a container class. It has a value, the element name, + and can contain other elements, text, comments, and unknowns. + Elements also contain an arbitrary number of attributes. +*/ +class TiXmlElement : public TiXmlNode +{ +public: + /// Construct an element. + TiXmlElement (const char * in_value); + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlElement( const std::string& _value ); + #endif + + TiXmlElement( const TiXmlElement& ); + + void operator=( const TiXmlElement& base ); + + virtual ~TiXmlElement(); + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + */ + const char* Attribute( const char* name ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an integer, + the integer value will be put in the return 'i', if 'i' + is non-null. + */ + const char* Attribute( const char* name, int* i ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an double, + the double value will be put in the return 'd', if 'd' + is non-null. + */ + const char* Attribute( const char* name, double* d ) const; + + /** QueryIntAttribute examines the attribute - it is an alternative to the + Attribute() method with richer error checking. + If the attribute is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. If the attribute + does not exist, then TIXML_NO_ATTRIBUTE is returned. + */ + int QueryIntAttribute( const char* name, int* _value ) const; + /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute(). + int QueryDoubleAttribute( const char* name, double* _value ) const; + /// QueryFloatAttribute examines the attribute - see QueryIntAttribute(). + int QueryFloatAttribute( const char* name, float* _value ) const { + double d; + int result = QueryDoubleAttribute( name, &d ); + if ( result == TIXML_SUCCESS ) { + *_value = (float)d; + } + return result; + } + + #ifdef TIXML_USE_STL + /** Template form of the attribute query which will try to read the + attribute into the specified type. Very easy, very powerful, but + be careful to make sure to call this with the correct type. + + NOTE: This method doesn't work correctly for 'string' types. + + @return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE + */ + template< typename T > int QueryValueAttribute( const std::string& name, T* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + std::stringstream sstream( node->ValueStr() ); + sstream >> *outValue; + if ( !sstream.fail() ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; + } + /* + This is - in theory - a bug fix for "QueryValueAtribute returns truncated std::string" + but template specialization is hard to get working cross-compiler. Leaving the bug for now. + + // The above will fail for std::string because the space character is used as a seperator. + // Specialize for strings. Bug [ 1695429 ] QueryValueAtribute returns truncated std::string + template<> int QueryValueAttribute( const std::string& name, std::string* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + *outValue = node->ValueStr(); + return TIXML_SUCCESS; + } + */ + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char* name, const char * _value ); + + #ifdef TIXML_USE_STL + const std::string* Attribute( const std::string& name ) const; + const std::string* Attribute( const std::string& name, int* i ) const; + const std::string* Attribute( const std::string& name, double* d ) const; + int QueryIntAttribute( const std::string& name, int* _value ) const; + int QueryDoubleAttribute( const std::string& name, double* _value ) const; + + /// STL std::string form. + void SetAttribute( const std::string& name, const std::string& _value ); + ///< STL std::string form. + void SetAttribute( const std::string& name, int _value ); + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char * name, int value ); + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetDoubleAttribute( const char * name, double value ); + + /** Deletes an attribute with the given name. + */ + void RemoveAttribute( const char * name ); + #ifdef TIXML_USE_STL + void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form. + #endif + + const TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element. + TiXmlAttribute* FirstAttribute() { return attributeSet.First(); } + const TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element. + TiXmlAttribute* LastAttribute() { return attributeSet.Last(); } + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, GetText() is limited compared to getting the TiXmlText child + and accessing it directly. + + If the first child of 'this' is a TiXmlText, the GetText() + returns the character string of the Text node, else null is returned. + + This is a convenient method for getting the text of simple contained text: + @verbatim + This is text + const char* str = fooElement->GetText(); + @endverbatim + + 'str' will be a pointer to "This is text". + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + This is text + @endverbatim + + then the value of str would be null. The first child node isn't a text node, it is + another element. From this XML: + @verbatim + This is text + @endverbatim + GetText() will return "This is ". + + WARNING: GetText() accesses a child node - don't become confused with the + similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are + safe type casts on the referenced node. + */ + const char* GetText() const; + + /// Creates a new Element and returns it - the returned element is a copy. + virtual TiXmlNode* Clone() const; + // Print the Element to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: next char past '<' + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlElement* ToElement() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlElement* ToElement() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + + void CopyTo( TiXmlElement* target ) const; + void ClearThis(); // like clear, but initializes 'this' object as well + + // Used to be public [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + /* [internal use] + Reads the "value" of the element -- another element, or text. + This should terminate with the current end tag. + */ + const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + +private: + + TiXmlAttributeSet attributeSet; +}; + + +/** An XML comment. +*/ +class TiXmlComment : public TiXmlNode +{ +public: + /// Constructs an empty comment. + TiXmlComment() : TiXmlNode( TiXmlNode::COMMENT ) {} + /// Construct a comment from text. + TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::COMMENT ) { + SetValue( _value ); + } + TiXmlComment( const TiXmlComment& ); + void operator=( const TiXmlComment& base ); + + virtual ~TiXmlComment() {} + + /// Returns a copy of this Comment. + virtual TiXmlNode* Clone() const; + // Write this Comment to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: at the ! of the !-- + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlComment* ToComment() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlComment* ToComment() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlComment* target ) const; + + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif +// virtual void StreamOut( TIXML_OSTREAM * out ) const; + +private: + +}; + + +/** XML text. A text node can have 2 ways to output the next. "normal" output + and CDATA. It will default to the mode it was parsed from the XML file and + you generally want to leave it alone, but you can change the output mode with + SetCDATA() and query it with CDATA(). +*/ +class TiXmlText : public TiXmlNode +{ + friend class TiXmlElement; +public: + /** Constructor for text element. By default, it is treated as + normal, encoded text. If you want it be output as a CDATA text + element, set the parameter _cdata to 'true' + */ + TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TEXT) + { + SetValue( initValue ); + cdata = false; + } + virtual ~TiXmlText() {} + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TEXT) + { + SetValue( initValue ); + cdata = false; + } + #endif + + TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TEXT ) { copy.CopyTo( this ); } + void operator=( const TiXmlText& base ) { base.CopyTo( this ); } + + // Write this text object to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /// Queries whether this represents text using a CDATA section. + bool CDATA() const { return cdata; } + /// Turns on or off a CDATA representation of text. + void SetCDATA( bool _cdata ) { cdata = _cdata; } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlText* ToText() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlText* ToText() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + /// [internal use] Creates a new Element and returns it. + virtual TiXmlNode* Clone() const; + void CopyTo( TiXmlText* target ) const; + + bool Blank() const; // returns true if all white space and new lines + // [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + bool cdata; // true if this should be input and output as a CDATA style text element +}; + + +/** In correct XML the declaration is the first entry in the file. + @verbatim + + @endverbatim + + TinyXml will happily read or write files without a declaration, + however. There are 3 possible attributes to the declaration: + version, encoding, and standalone. + + Note: In this version of the code, the attributes are + handled as special cases, not generic attributes, simply + because there can only be at most 3 and they are always the same. +*/ +class TiXmlDeclaration : public TiXmlNode +{ +public: + /// Construct an empty declaration. + TiXmlDeclaration() : TiXmlNode( TiXmlNode::DECLARATION ) {} + +#ifdef TIXML_USE_STL + /// Constructor. + TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ); +#endif + + /// Construct. + TiXmlDeclaration( const char* _version, + const char* _encoding, + const char* _standalone ); + + TiXmlDeclaration( const TiXmlDeclaration& copy ); + void operator=( const TiXmlDeclaration& copy ); + + virtual ~TiXmlDeclaration() {} + + /// Version. Will return an empty string if none was found. + const char *Version() const { return version.c_str (); } + /// Encoding. Will return an empty string if none was found. + const char *Encoding() const { return encoding.c_str (); } + /// Is this a standalone document? + const char *Standalone() const { return standalone.c_str (); } + + /// Creates a copy of this Declaration and returns it. + virtual TiXmlNode* Clone() const; + // Print this declaration to a FILE stream. + virtual void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlDeclaration* ToDeclaration() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlDeclaration* target ) const; + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + + TIXML_STRING version; + TIXML_STRING encoding; + TIXML_STRING standalone; +}; + + +/** Any tag that tinyXml doesn't recognize is saved as an + unknown. It is a tag of text, but should not be modified. + It will be written back to the XML, unchanged, when the file + is saved. + + DTD tags get thrown into TiXmlUnknowns. +*/ +class TiXmlUnknown : public TiXmlNode +{ +public: + TiXmlUnknown() : TiXmlNode( TiXmlNode::UNKNOWN ) {} + virtual ~TiXmlUnknown() {} + + TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::UNKNOWN ) { copy.CopyTo( this ); } + void operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); } + + /// Creates a copy of this Unknown and returns it. + virtual TiXmlNode* Clone() const; + // Print this Unknown to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlUnknown* ToUnknown() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected: + void CopyTo( TiXmlUnknown* target ) const; + + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + +}; + + +/** Always the top level node. A document binds together all the + XML pieces. It can be saved, loaded, and printed to the screen. + The 'value' of a document node is the xml file name. +*/ +class TiXmlDocument : public TiXmlNode +{ +public: + /// Create an empty document, that has no name. + TiXmlDocument(); + /// Create a document with a name. The name of the document is also the filename of the xml. + TiXmlDocument( const char * documentName ); + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlDocument( const std::string& documentName ); + #endif + + TiXmlDocument( const TiXmlDocument& copy ); + void operator=( const TiXmlDocument& copy ); + + virtual ~TiXmlDocument() {} + + /** Load a file using the current document value. + Returns true if successful. Will delete any existing + document data before loading. + */ + bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the current document value. Returns true if successful. + bool SaveFile() const; + /// Load a file using the given filename. Returns true if successful. + bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given filename. Returns true if successful. + bool SaveFile( const char * filename ) const; + /** Load a file using the given FILE*. Returns true if successful. Note that this method + doesn't stream - the entire object pointed at by the FILE* + will be interpreted as an XML file. TinyXML doesn't stream in XML from the current + file location. Streaming may be added in the future. + */ + bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given FILE*. Returns true if successful. + bool SaveFile( FILE* ) const; + + #ifdef TIXML_USE_STL + bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version. + { +// StringToBuffer f( filename ); +// return ( f.buffer && LoadFile( f.buffer, encoding )); + return LoadFile( filename.c_str(), encoding ); + } + bool SaveFile( const std::string& filename ) const ///< STL std::string version. + { +// StringToBuffer f( filename ); +// return ( f.buffer && SaveFile( f.buffer )); + return SaveFile( filename.c_str() ); + } + #endif + + /** Parse the given null terminated block of xml data. Passing in an encoding to this + method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml + to use that encoding, regardless of what TinyXml might otherwise try to detect. + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + + /** Get the root element -- the only top level element -- of the document. + In well formed XML, there should only be one. TinyXml is tolerant of + multiple elements at the document level. + */ + const TiXmlElement* RootElement() const { return FirstChildElement(); } + TiXmlElement* RootElement() { return FirstChildElement(); } + + /** If an error occurs, Error will be set to true. Also, + - The ErrorId() will contain the integer identifier of the error (not generally useful) + - The ErrorDesc() method will return the name of the error. (very useful) + - The ErrorRow() and ErrorCol() will return the location of the error (if known) + */ + bool Error() const { return error; } + + /// Contains a textual (english) description of the error if one occurs. + const char * ErrorDesc() const { return errorDesc.c_str (); } + + /** Generally, you probably want the error string ( ErrorDesc() ). But if you + prefer the ErrorId, this function will fetch it. + */ + int ErrorId() const { return errorId; } + + /** Returns the location (if known) of the error. The first column is column 1, + and the first row is row 1. A value of 0 means the row and column wasn't applicable + (memory errors, for example, have no row/column) or the parser lost the error. (An + error in the error reporting, in that case.) + + @sa SetTabSize, Row, Column + */ + int ErrorRow() const { return errorLocation.row+1; } + int ErrorCol() const { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow() + + /** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol()) + to report the correct values for row and column. It does not change the output + or input in any way. + + By calling this method, with a tab size + greater than 0, the row and column of each node and attribute is stored + when the file is loaded. Very useful for tracking the DOM back in to + the source file. + + The tab size is required for calculating the location of nodes. If not + set, the default of 4 is used. The tabsize is set per document. Setting + the tabsize to 0 disables row/column tracking. + + Note that row and column tracking is not supported when using operator>>. + + The tab size needs to be enabled before the parse or load. Correct usage: + @verbatim + TiXmlDocument doc; + doc.SetTabSize( 8 ); + doc.Load( "myfile.xml" ); + @endverbatim + + @sa Row, Column + */ + void SetTabSize( int _tabsize ) { tabsize = _tabsize; } + + int TabSize() const { return tabsize; } + + /** If you have handled the error, it can be reset with this call. The error + state is automatically cleared if you Parse a new XML block. + */ + void ClearError() { error = false; + errorId = 0; + errorDesc = ""; + errorLocation.row = errorLocation.col = 0; + //errorLocation.last = 0; + } + + /** Write the document to standard out using formatted printing ("pretty print"). */ + void Print() const { Print( stdout, 0 ); } + + /* Write the document to a string using formatted printing ("pretty print"). This + will allocate a character array (new char[]) and return it as a pointer. The + calling code pust call delete[] on the return char* to avoid a memory leak. + */ + //char* PrintToMemory() const; + + /// Print this Document to a FILE stream. + virtual void Print( FILE* cfile, int depth = 0 ) const; + // [internal use] + void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + + virtual const TiXmlDocument* ToDocument() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDocument* ToDocument() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + // [internal use] + virtual TiXmlNode* Clone() const; + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + void CopyTo( TiXmlDocument* target ) const; + + bool error; + int errorId; + TIXML_STRING errorDesc; + int tabsize; + TiXmlCursor errorLocation; + bool useMicrosoftBOM; // the UTF-8 BOM were found when read. Note this, and try to write. +}; + + +/** + A TiXmlHandle is a class that wraps a node pointer with null checks; this is + an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml + DOM structure. It is a separate utility class. + + Take an example: + @verbatim + + + + + + + @endverbatim + + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very + easy to write a *lot* of code that looks like: + + @verbatim + TiXmlElement* root = document.FirstChildElement( "Document" ); + if ( root ) + { + TiXmlElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + TiXmlElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + TiXmlElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. + @endverbatim + + And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity + of such code. A TiXmlHandle checks for null pointers so it is perfectly safe + and correct to use: + + @verbatim + TiXmlHandle docHandle( &document ); + TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement(); + if ( child2 ) + { + // do something useful + @endverbatim + + Which is MUCH more concise and useful. + + It is also safe to copy handles - internally they are nothing more than node pointers. + @verbatim + TiXmlHandle handleCopy = handle; + @endverbatim + + What they should not be used for is iteration: + + @verbatim + int i=0; + while ( true ) + { + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).ToElement(); + if ( !child ) + break; + // do something + ++i; + } + @endverbatim + + It seems reasonable, but it is in fact two embedded while loops. The Child method is + a linear walk to find the element, so this code would iterate much more than it needs + to. Instead, prefer: + + @verbatim + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).ToElement(); + + for( child; child; child=child->NextSiblingElement() ) + { + // do something + } + @endverbatim +*/ +class TiXmlHandle +{ +public: + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. + TiXmlHandle( TiXmlNode* _node ) { this->node = _node; } + /// Copy constructor + TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; } + TiXmlHandle operator=( const TiXmlHandle& ref ) { this->node = ref.node; return *this; } + + /// Return a handle to the first child node. + TiXmlHandle FirstChild() const; + /// Return a handle to the first child node with the given name. + TiXmlHandle FirstChild( const char * value ) const; + /// Return a handle to the first child element. + TiXmlHandle FirstChildElement() const; + /// Return a handle to the first child element with the given name. + TiXmlHandle FirstChildElement( const char * value ) const; + + /** Return a handle to the "index" child with the given name. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( const char* value, int index ) const; + /** Return a handle to the "index" child. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( int index ) const; + /** Return a handle to the "index" child element with the given name. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( const char* value, int index ) const; + /** Return a handle to the "index" child element. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( int index ) const; + + #ifdef TIXML_USE_STL + TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); } + TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); } + + TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); } + TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); } + #endif + + /** Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* ToNode() const { return node; } + /** Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* ToElement() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); } + /** Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* ToText() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); } + /** Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* ToUnknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); } + + /** @deprecated use ToNode. + Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* Node() const { return ToNode(); } + /** @deprecated use ToElement. + Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* Element() const { return ToElement(); } + /** @deprecated use ToText() + Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* Text() const { return ToText(); } + /** @deprecated use ToUnknown() + Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* Unknown() const { return ToUnknown(); } + +private: + TiXmlNode* node; +}; + + +/** Print to memory functionality. The TiXmlPrinter is useful when you need to: + + -# Print to memory (especially in non-STL mode) + -# Control formatting (line endings, etc.) + + When constructed, the TiXmlPrinter is in its default "pretty printing" mode. + Before calling Accept() you can call methods to control the printing + of the XML document. After TiXmlNode::Accept() is called, the printed document can + be accessed via the CStr(), Str(), and Size() methods. + + TiXmlPrinter uses the Visitor API. + @verbatim + TiXmlPrinter printer; + printer.SetIndent( "\t" ); + + doc.Accept( &printer ); + fprintf( stdout, "%s", printer.CStr() ); + @endverbatim +*/ +class TiXmlPrinter : public TiXmlVisitor +{ +public: + TiXmlPrinter() : depth( 0 ), simpleTextPrint( false ), + buffer(), indent( " " ), lineBreak( "\n" ) {} + + virtual bool VisitEnter( const TiXmlDocument& doc ); + virtual bool VisitExit( const TiXmlDocument& doc ); + + virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ); + virtual bool VisitExit( const TiXmlElement& element ); + + virtual bool Visit( const TiXmlDeclaration& declaration ); + virtual bool Visit( const TiXmlText& text ); + virtual bool Visit( const TiXmlComment& comment ); + virtual bool Visit( const TiXmlUnknown& unknown ); + + /** Set the indent characters for printing. By default 4 spaces + but tab (\t) is also useful, or null/empty string for no indentation. + */ + void SetIndent( const char* _indent ) { indent = _indent ? _indent : "" ; } + /// Query the indention string. + const char* Indent() { return indent.c_str(); } + /** Set the line breaking string. By default set to newline (\n). + Some operating systems prefer other characters, or can be + set to the null/empty string for no indenation. + */ + void SetLineBreak( const char* _lineBreak ) { lineBreak = _lineBreak ? _lineBreak : ""; } + /// Query the current line breaking string. + const char* LineBreak() { return lineBreak.c_str(); } + + /** Switch over to "stream printing" which is the most dense formatting without + linebreaks. Common when the XML is needed for network transmission. + */ + void SetStreamPrinting() { indent = ""; + lineBreak = ""; + } + /// Return the result. + const char* CStr() { return buffer.c_str(); } + /// Return the length of the result string. + size_t Size() { return buffer.size(); } + + #ifdef TIXML_USE_STL + /// Return the result. + const std::string& Str() { return buffer; } + #endif + +private: + void DoIndent() { + for( int i=0; i +#include + +#include "tinyxml.h" + +//#define DEBUG_PARSER +#if defined( DEBUG_PARSER ) +# if defined( DEBUG ) && defined( _MSC_VER ) +# include +# define TIXML_LOG OutputDebugString +# else +# define TIXML_LOG printf +# endif +#endif + +// Note tha "PutString" hardcodes the same list. This +// is less flexible than it appears. Changing the entries +// or order will break putstring. +TiXmlBase::Entity TiXmlBase::entity[ NUM_ENTITY ] = +{ + { "&", 5, '&' }, + { "<", 4, '<' }, + { ">", 4, '>' }, + { """, 6, '\"' }, + { "'", 6, '\'' } +}; + +// Bunch of unicode info at: +// http://www.unicode.org/faq/utf_bom.html +// Including the basic of this table, which determines the #bytes in the +// sequence from the lead byte. 1 placed for invalid sequences -- +// although the result will be junk, pass it through as much as possible. +// Beware of the non-characters in UTF-8: +// ef bb bf (Microsoft "lead bytes") +// ef bf be +// ef bf bf + +const unsigned char TIXML_UTF_LEAD_0 = 0xefU; +const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; +const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + +const int TiXmlBase::utf8ByteTable[256] = +{ + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 + 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte + 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid +}; + + +void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) +{ + const unsigned long BYTE_MASK = 0xBF; + const unsigned long BYTE_MARK = 0x80; + const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + if (input < 0x80) + *length = 1; + else if ( input < 0x800 ) + *length = 2; + else if ( input < 0x10000 ) + *length = 3; + else if ( input < 0x200000 ) + *length = 4; + else + { *length = 0; return; } // This code won't covert this correctly anyway. + + output += *length; + + // Scary scary fall throughs. + switch (*length) + { + case 4: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 3: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 2: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 1: + --output; + *output = (char)(input | FIRST_BYTE_MARK[*length]); + } +} + + +/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalpha( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalpha( anyByte ); +// } +} + + +/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalnum( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalnum( anyByte ); +// } +} + + +class TiXmlParsingData +{ + friend class TiXmlDocument; + public: + void Stamp( const char* now, TiXmlEncoding encoding ); + + const TiXmlCursor& Cursor() { return cursor; } + + private: + // Only used by the document! + TiXmlParsingData( const char* start, int _tabsize, int row, int col ) + { + assert( start ); + stamp = start; + tabsize = _tabsize; + cursor.row = row; + cursor.col = col; + } + + TiXmlCursor cursor; + const char* stamp; + int tabsize; +}; + + +void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding ) +{ + assert( now ); + + // Do nothing if the tabsize is 0. + if ( tabsize < 1 ) + { + return; + } + + // Get the current row, column. + int row = cursor.row; + int col = cursor.col; + const char* p = stamp; + assert( p ); + + while ( p < now ) + { + // Treat p as unsigned, so we have a happy compiler. + const unsigned char* pU = (const unsigned char*)p; + + // Code contributed by Fletcher Dunn: (modified by lee) + switch (*pU) { + case 0: + // We *should* never get here, but in case we do, don't + // advance past the terminating null character, ever + return; + + case '\r': + // bump down to the next line + ++row; + col = 0; + // Eat the character + ++p; + + // Check for \r\n sequence, and treat this as a single character + if (*p == '\n') { + ++p; + } + break; + + case '\n': + // bump down to the next line + ++row; + col = 0; + + // Eat the character + ++p; + + // Check for \n\r sequence, and treat this as a single + // character. (Yes, this bizarre thing does occur still + // on some arcane platforms...) + if (*p == '\r') { + ++p; + } + break; + + case '\t': + // Eat the character + ++p; + + // Skip to next tab stop + col = (col / tabsize + 1) * tabsize; + break; + + case TIXML_UTF_LEAD_0: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( *(p+1) && *(p+2) ) + { + // In these cases, don't advance the column. These are + // 0-width spaces. + if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU ) + p += 3; + else + { p +=3; ++col; } // A normal character. + } + } + else + { + ++p; + ++col; + } + break; + + default: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // Eat the 1 to 4 byte utf8 character. + int step = TiXmlBase::utf8ByteTable[*((const unsigned char*)p)]; + if ( step == 0 ) + step = 1; // Error case from bad encoding, but handle gracefully. + p += step; + + // Just advance one column, of course. + ++col; + } + else + { + ++p; + ++col; + } + break; + } + } + cursor.row = row; + cursor.col = col; + assert( cursor.row >= -1 ); + assert( cursor.col >= -1 ); + stamp = p; + assert( stamp ); +} + + +const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding ) +{ + if ( !p || !*p ) + { + return 0; + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + while ( *p ) + { + const unsigned char* pU = (const unsigned char*)p; + + // Skip the stupid Microsoft UTF-8 Byte order marks + if ( *(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==TIXML_UTF_LEAD_1 + && *(pU+2)==TIXML_UTF_LEAD_2 ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbeU ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbfU ) + { + p += 3; + continue; + } + + if ( IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) // Still using old rules for white space. + ++p; + else + break; + } + } + else + { + while ( *p && IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) + ++p; + } + + return p; +} + +#ifdef TIXML_USE_STL +/*static*/ bool TiXmlBase::StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ) +{ + for( ;; ) + { + if ( !in->good() ) return false; + + int c = in->peek(); + // At this scope, we can't get to a document. So fail silently. + if ( !IsWhiteSpace( c ) || c <= 0 ) + return true; + + *tag += (char) in->get(); + } +} + +/*static*/ bool TiXmlBase::StreamTo( std::istream * in, int character, TIXML_STRING * tag ) +{ + //assert( character > 0 && character < 128 ); // else it won't work in utf-8 + while ( in->good() ) + { + int c = in->peek(); + if ( c == character ) + return true; + if ( c <= 0 ) // Silent failure: can't get document at this scope + return false; + + in->get(); + *tag += (char) c; + } + return false; +} +#endif + +// One of TinyXML's more performance demanding functions. Try to keep the memory overhead down. The +// "assign" optimization removes over 10% of the execution time. +// +const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding ) +{ + // Oddly, not supported on some comilers, + //name->clear(); + // So use this: + *name = ""; + assert( p ); + + // Names start with letters or underscores. + // Of course, in unicode, tinyxml has no idea what a letter *is*. The + // algorithm is generous. + // + // After that, they can be letters, underscores, numbers, + // hyphens, or colons. (Colons are valid ony for namespaces, + // but tinyxml can't tell namespaces from names.) + if ( p && *p + && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) ) + { + const char* start = p; + while( p && *p + && ( IsAlphaNum( (unsigned char ) *p, encoding ) + || *p == '_' + || *p == '-' + || *p == '.' + || *p == ':' ) ) + { + //(*name) += *p; // expensive + ++p; + } + if ( p-start > 0 ) { + name->assign( start, p-start ); + } + return p; + } + return 0; +} + +const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding ) +{ + // Presume an entity, and pull it out. + TIXML_STRING ent; + int i; + *length = 0; + + if ( *(p+1) && *(p+1) == '#' && *(p+2) ) + { + unsigned long ucs = 0; + ptrdiff_t delta = 0; + unsigned mult = 1; + + if ( *(p+2) == 'x' ) + { + // Hexadecimal. + if ( !*(p+3) ) return 0; + + const char* q = p+3; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != 'x' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else if ( *q >= 'a' && *q <= 'f' ) + ucs += mult * (*q - 'a' + 10); + else if ( *q >= 'A' && *q <= 'F' ) + ucs += mult * (*q - 'A' + 10 ); + else + return 0; + mult *= 16; + --q; + } + } + else + { + // Decimal. + if ( !*(p+2) ) return 0; + + const char* q = p+2; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != '#' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else + return 0; + mult *= 10; + --q; + } + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // convert the UCS to UTF-8 + ConvertUTF32ToUTF8( ucs, value, length ); + } + else + { + *value = (char)ucs; + *length = 1; + } + return p + delta + 1; + } + + // Now try to match it. + for( i=0; iappend( cArr, len ); + } + } + else + { + bool whitespace = false; + + // Remove leading white space: + p = SkipWhiteSpace( p, encoding ); + while ( p && *p + && !StringEqual( p, endTag, caseInsensitive, encoding ) ) + { + if ( *p == '\r' || *p == '\n' ) + { + whitespace = true; + ++p; + } + else if ( IsWhiteSpace( *p ) ) + { + whitespace = true; + ++p; + } + else + { + // If we've found whitespace, add it before the + // new character. Any whitespace just becomes a space. + if ( whitespace ) + { + (*text) += ' '; + whitespace = false; + } + int len; + char cArr[4] = { 0, 0, 0, 0 }; + p = GetChar( p, cArr, &len, encoding ); + if ( len == 1 ) + (*text) += cArr[0]; // more efficient + else + text->append( cArr, len ); + } + } + } + if ( p ) + p += strlen( endTag ); + return p; +} + +#ifdef TIXML_USE_STL + +void TiXmlDocument::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + // The basic issue with a document is that we don't know what we're + // streaming. Read something presumed to be a tag (and hope), then + // identify it, and call the appropriate stream method on the tag. + // + // This "pre-streaming" will never read the closing ">" so the + // sub-tag can orient itself. + + if ( !StreamTo( in, '<', tag ) ) + { + SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + while ( in->good() ) + { + int tagIndex = (int) tag->length(); + while ( in->good() && in->peek() != '>' ) + { + int c = in->get(); + if ( c <= 0 ) + { + SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + break; + } + (*tag) += (char) c; + } + + if ( in->good() ) + { + // We now have something we presume to be a node of + // some sort. Identify it, and call the node to + // continue streaming. + TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING ); + + if ( node ) + { + node->StreamIn( in, tag ); + bool isElement = node->ToElement() != 0; + delete node; + node = 0; + + // If this is the root element, we're done. Parsing will be + // done by the >> operator. + if ( isElement ) + { + return; + } + } + else + { + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + } + } + // We should have returned sooner. + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); +} + +#endif + +const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding ) +{ + ClearError(); + + // Parse away, at the document level. Since a document + // contains nothing but other tags, most of what happens + // here is skipping white space. + if ( !p || !*p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + // Note that, for a document, this needs to come + // before the while space skip, so that parsing + // starts from the pointer we are given. + location.Clear(); + if ( prevData ) + { + location.row = prevData->cursor.row; + location.col = prevData->cursor.col; + } + else + { + location.row = 0; + location.col = 0; + } + TiXmlParsingData data( p, TabSize(), location.row, location.col ); + location = data.Cursor(); + + if ( encoding == TIXML_ENCODING_UNKNOWN ) + { + // Check for the Microsoft UTF-8 lead bytes. + const unsigned char* pU = (const unsigned char*)p; + if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0 + && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1 + && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 ) + { + encoding = TIXML_ENCODING_UTF8; + useMicrosoftBOM = true; + } + } + + p = SkipWhiteSpace( p, encoding ); + if ( !p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + while ( p && *p ) + { + TiXmlNode* node = Identify( p, encoding ); + if ( node ) + { + p = node->Parse( p, &data, encoding ); + LinkEndChild( node ); + } + else + { + break; + } + + // Did we get encoding info? + if ( encoding == TIXML_ENCODING_UNKNOWN + && node->ToDeclaration() ) + { + TiXmlDeclaration* dec = node->ToDeclaration(); + const char* enc = dec->Encoding(); + assert( enc ); + + if ( *enc == 0 ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice + else + encoding = TIXML_ENCODING_LEGACY; + } + + p = SkipWhiteSpace( p, encoding ); + } + + // Was this empty? + if ( !firstChild ) { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding ); + return 0; + } + + // All is well. + return p; +} + +void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + // The first error in a chain is more accurate - don't set again! + if ( error ) + return; + + assert( err > 0 && err < TIXML_ERROR_STRING_COUNT ); + error = true; + errorId = err; + errorDesc = errorString[ errorId ]; + + errorLocation.Clear(); + if ( pError && data ) + { + data->Stamp( pError, encoding ); + errorLocation = data->Cursor(); + } +} + + +TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding ) +{ + TiXmlNode* returnNode = 0; + + p = SkipWhiteSpace( p, encoding ); + if( !p || !*p || *p != '<' ) + { + return 0; + } + + TiXmlDocument* doc = GetDocument(); + p = SkipWhiteSpace( p, encoding ); + + if ( !p || !*p ) + { + return 0; + } + + // What is this thing? + // - Elements start with a letter or underscore, but xml is reserved. + // - Comments: "; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // [ 1475201 ] TinyXML parses entities in comments + // Oops - ReadText doesn't work, because we don't want to parse the entities. + // p = ReadText( p, &value, false, endTag, false, encoding ); + // + // from the XML spec: + /* + [Definition: Comments may appear anywhere in a document outside other markup; in addition, + they may appear within the document type declaration at places allowed by the grammar. + They are not part of the document's character data; an XML processor MAY, but need not, + make it possible for an application to retrieve the text of comments. For compatibility, + the string "--" (double-hyphen) MUST NOT occur within comments.] Parameter entity + references MUST NOT be recognized within comments. + + An example of a comment: + + + */ + + value = ""; + // Keep all the white space. + while ( p && *p && !StringEqual( p, endTag, false, encoding ) ) + { + value.append( p, 1 ); + ++p; + } + if ( p ) + p += strlen( endTag ); + + return p; +} + + +const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) return 0; + +// int tabsize = 4; +// if ( document ) +// tabsize = document->TabSize(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + // Read the name, the '=' and the value. + const char* pErr = p; + p = ReadName( p, &name, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); + return 0; + } + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p || *p != '=' ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + ++p; // skip '=' + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + const char* end; + const char SINGLE_QUOTE = '\''; + const char DOUBLE_QUOTE = '\"'; + + if ( *p == SINGLE_QUOTE ) + { + ++p; + end = "\'"; // single quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else if ( *p == DOUBLE_QUOTE ) + { + ++p; + end = "\""; // double quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else + { + // All attribute values should be in single or double quotes. + // But this is such a common error that the parser will try + // its best, even without them. + value = ""; + while ( p && *p // existence + && !IsWhiteSpace( *p ) && *p != '\n' && *p != '\r' // whitespace + && *p != '/' && *p != '>' ) // tag end + { + if ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) { + // [ 1451649 ] Attribute values with trailing quotes not handled correctly + // We did not have an opening quote but seem to have a + // closing one. Give up and throw an error. + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + value += *p; + ++p; + } + } + return p; +} + +#ifdef TIXML_USE_STL +void TiXmlText::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->peek(); + if ( !cdata && (c == '<' ) ) + { + return; + } + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + (*tag) += (char) c; + in->get(); // "commits" the peek made above + + if ( cdata && c == '>' && tag->size() >= 3 ) { + size_t len = tag->size(); + if ( (*tag)[len-2] == ']' && (*tag)[len-3] == ']' ) { + // terminator of cdata. + return; + } + } + } +} +#endif + +const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + value = ""; + TiXmlDocument* document = GetDocument(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + + const char* const startTag = ""; + + if ( cdata || StringEqual( p, startTag, false, encoding ) ) + { + cdata = true; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + document->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // Keep all the white space, ignore the encoding, etc. + while ( p && *p + && !StringEqual( p, endTag, false, encoding ) + ) + { + value += *p; + ++p; + } + + TIXML_STRING dummy; + p = ReadText( p, &dummy, false, endTag, false, encoding ); + return p; + } + else + { + bool ignoreWhite = true; + + const char* end = "<"; + p = ReadText( p, &value, ignoreWhite, end, false, encoding ); + if ( p ) + return p-1; // don't truncate the '<' + return 0; + } +} + +#ifdef TIXML_USE_STL +void TiXmlDeclaration::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c; + + if ( c == '>' ) + { + // All is well. + return; + } + } +} +#endif + +const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding ) +{ + p = SkipWhiteSpace( p, _encoding ); + // Find the beginning, find the end, and look for + // the stuff in-between. + TiXmlDocument* document = GetDocument(); + if ( !p || !*p || !StringEqual( p, "SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding ); + return 0; + } + if ( data ) + { + data->Stamp( p, _encoding ); + location = data->Cursor(); + } + p += 5; + + version = ""; + encoding = ""; + standalone = ""; + + while ( p && *p ) + { + if ( *p == '>' ) + { + ++p; + return p; + } + + p = SkipWhiteSpace( p, _encoding ); + if ( StringEqual( p, "version", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + version = attrib.Value(); + } + else if ( StringEqual( p, "encoding", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + encoding = attrib.Value(); + } + else if ( StringEqual( p, "standalone", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + standalone = attrib.Value(); + } + else + { + // Read over whatever it is. + while( p && *p && *p != '>' && !IsWhiteSpace( *p ) ) + ++p; + } + } + return 0; +} + +bool TiXmlText::Blank() const +{ + for ( unsigned i=0; i= (BASE << 16)) a -= (BASE << 16); \ + if (a >= (BASE << 15)) a -= (BASE << 15); \ + if (a >= (BASE << 14)) a -= (BASE << 14); \ + if (a >= (BASE << 13)) a -= (BASE << 13); \ + if (a >= (BASE << 12)) a -= (BASE << 12); \ + if (a >= (BASE << 11)) a -= (BASE << 11); \ + if (a >= (BASE << 10)) a -= (BASE << 10); \ + if (a >= (BASE << 9)) a -= (BASE << 9); \ + if (a >= (BASE << 8)) a -= (BASE << 8); \ + if (a >= (BASE << 7)) a -= (BASE << 7); \ + if (a >= (BASE << 6)) a -= (BASE << 6); \ + if (a >= (BASE << 5)) a -= (BASE << 5); \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +# define MOD4(a) \ + do { \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +#else +# define MOD(a) a %= BASE +# define MOD4(a) a %= BASE +#endif + +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + unsigned long sum2; + unsigned n; + + /* split Adler-32 into component sums */ + sum2 = (adler >> 16) & 0xffff; + adler &= 0xffff; + + /* in case user likes doing a byte at a time, keep it fast */ + if (len == 1) { + adler += buf[0]; + if (adler >= BASE) + adler -= BASE; + sum2 += adler; + if (sum2 >= BASE) + sum2 -= BASE; + return adler | (sum2 << 16); + } + + /* initial Adler-32 value (deferred check for len == 1 speed) */ + if (buf == Z_NULL) + return 1L; + + /* in case short lengths are provided, keep it somewhat fast */ + if (len < 16) { + while (len--) { + adler += *buf++; + sum2 += adler; + } + if (adler >= BASE) + adler -= BASE; + MOD4(sum2); /* only added so many BASE's */ + return adler | (sum2 << 16); + } + + /* do length NMAX blocks -- requires just one modulo operation */ + while (len >= NMAX) { + len -= NMAX; + n = NMAX / 16; /* NMAX is divisible by 16 */ + do { + DO16(buf); /* 16 sums unrolled */ + buf += 16; + } while (--n); + MOD(adler); + MOD(sum2); + } + + /* do remaining bytes (less than NMAX, still just one modulo) */ + if (len) { /* avoid modulos if none remaining */ + while (len >= 16) { + len -= 16; + DO16(buf); + buf += 16; + } + while (len--) { + adler += *buf++; + sum2 += adler; + } + MOD(adler); + MOD(sum2); + } + + /* return recombined sums */ + return adler | (sum2 << 16); +} + +/* ========================================================================= */ +uLong ZEXPORT adler32_combine(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off_t len2; +{ + unsigned long sum1; + unsigned long sum2; + unsigned rem; + + /* the derivation of this formula is left as an exercise for the reader */ + rem = (unsigned)(len2 % BASE); + sum1 = adler1 & 0xffff; + sum2 = rem * sum1; + MOD(sum2); + sum1 += (adler2 & 0xffff) + BASE - 1; + sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; + if (sum1 > BASE) sum1 -= BASE; + if (sum1 > BASE) sum1 -= BASE; + if (sum2 > (BASE << 1)) sum2 -= (BASE << 1); + if (sum2 > BASE) sum2 -= BASE; + return sum1 | (sum2 << 16); +} diff --git a/Engine/lib/zlib/compress.c b/Engine/lib/zlib/compress.c new file mode 100644 index 000000000..df04f0148 --- /dev/null +++ b/Engine/lib/zlib/compress.c @@ -0,0 +1,79 @@ +/* compress.c -- compress a memory buffer + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ +int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; + int level; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; +#ifdef MAXSEG_64K + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; +#endif + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit(&stream, level); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} + +/* =========================================================================== + */ +int ZEXPORT compress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); +} + +/* =========================================================================== + If the default memLevel or windowBits for deflateInit() is changed, then + this function needs to be updated. + */ +uLong ZEXPORT compressBound (sourceLen) + uLong sourceLen; +{ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + 11; +} diff --git a/Engine/lib/zlib/crc32.c b/Engine/lib/zlib/crc32.c new file mode 100644 index 000000000..f658a9ef5 --- /dev/null +++ b/Engine/lib/zlib/crc32.c @@ -0,0 +1,423 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Thanks to Rodney Brown for his contribution of faster + * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing + * tables for updating the shift register in one step with three exclusive-ors + * instead of four steps with four exclusive-ors. This results in about a + * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. + */ + +/* @(#) $Id$ */ + +/* + Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore + protection on the static variables used to control the first-use generation + of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should + first call get_crc_table() to initialize the tables before allowing more than + one thread to use crc32(). + */ + +#ifdef MAKECRCH +# include +# ifndef DYNAMIC_CRC_TABLE +# define DYNAMIC_CRC_TABLE +# endif /* !DYNAMIC_CRC_TABLE */ +#endif /* MAKECRCH */ + +#include "zutil.h" /* for STDC and FAR definitions */ + +#define local static + +/* Find a four-byte integer type for crc32_little() and crc32_big(). */ +#ifndef NOBYFOUR +# ifdef STDC /* need ANSI C limits.h to determine sizes */ +# include +# define BYFOUR +# if (UINT_MAX == 0xffffffffUL) + typedef unsigned int u4; +# else +# if (ULONG_MAX == 0xffffffffUL) + typedef unsigned long u4; +# else +# if (USHRT_MAX == 0xffffffffUL) + typedef unsigned short u4; +# else +# undef BYFOUR /* can't find a four-byte integer type! */ +# endif +# endif +# endif +# endif /* STDC */ +#endif /* !NOBYFOUR */ + +/* Definitions for doing the crc four data bytes at a time. */ +#ifdef BYFOUR +# define REV(w) (((w)>>24)+(((w)>>8)&0xff00)+ \ + (((w)&0xff00)<<8)+(((w)&0xff)<<24)) + local unsigned long crc32_little OF((unsigned long, + const unsigned char FAR *, unsigned)); + local unsigned long crc32_big OF((unsigned long, + const unsigned char FAR *, unsigned)); +# define TBLS 8 +#else +# define TBLS 1 +#endif /* BYFOUR */ + +/* Local functions for crc concatenation */ +local unsigned long gf2_matrix_times OF((unsigned long *mat, + unsigned long vec)); +local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); + +#ifdef DYNAMIC_CRC_TABLE + +local volatile int crc_table_empty = 1; +local unsigned long FAR crc_table[TBLS][256]; +local void make_crc_table OF((void)); +#ifdef MAKECRCH + local void write_table OF((FILE *, const unsigned long FAR *)); +#endif /* MAKECRCH */ +/* + Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The first table is simply the CRC of all possible eight bit values. This is + all the information needed to generate CRCs on data a byte at a time for all + combinations of CRC register values and incoming bytes. The remaining tables + allow for word-at-a-time CRC calculation for both big-endian and little- + endian machines, where a word is four bytes. +*/ +local void make_crc_table() +{ + unsigned long c; + int n, k; + unsigned long poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static volatile int first = 1; /* flag to limit concurrent making */ + static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* See if another task is already doing this (not thread-safe, but better + than nothing -- significantly reduces duration of vulnerability in + case the advice about DYNAMIC_CRC_TABLE is ignored) */ + if (first) { + first = 0; + + /* make exclusive-or pattern from polynomial (0xedb88320UL) */ + poly = 0UL; + for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++) + poly |= 1UL << (31 - p[n]); + + /* generate a crc for every 8-bit value */ + for (n = 0; n < 256; n++) { + c = (unsigned long)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[0][n] = c; + } + +#ifdef BYFOUR + /* generate crc for each value followed by one, two, and three zeros, + and then the byte reversal of those as well as the first table */ + for (n = 0; n < 256; n++) { + c = crc_table[0][n]; + crc_table[4][n] = REV(c); + for (k = 1; k < 4; k++) { + c = crc_table[0][c & 0xff] ^ (c >> 8); + crc_table[k][n] = c; + crc_table[k + 4][n] = REV(c); + } + } +#endif /* BYFOUR */ + + crc_table_empty = 0; + } + else { /* not first */ + /* wait for the other guy to finish (not efficient, but rare) */ + while (crc_table_empty) + ; + } + +#ifdef MAKECRCH + /* write out CRC tables to crc32.h */ + { + FILE *out; + + out = fopen("crc32.h", "w"); + if (out == NULL) return; + fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); + fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); + fprintf(out, "local const unsigned long FAR "); + fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); + write_table(out, crc_table[0]); +# ifdef BYFOUR + fprintf(out, "#ifdef BYFOUR\n"); + for (k = 1; k < 8; k++) { + fprintf(out, " },\n {\n"); + write_table(out, crc_table[k]); + } + fprintf(out, "#endif\n"); +# endif /* BYFOUR */ + fprintf(out, " }\n};\n"); + fclose(out); + } +#endif /* MAKECRCH */ +} + +#ifdef MAKECRCH +local void write_table(out, table) + FILE *out; + const unsigned long FAR *table; +{ + int n; + + for (n = 0; n < 256; n++) + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n], + n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); +} +#endif /* MAKECRCH */ + +#else /* !DYNAMIC_CRC_TABLE */ +/* ======================================================================== + * Tables of CRC-32s of all single-byte values, made by make_crc_table(). + */ +#include "crc32.h" +#endif /* DYNAMIC_CRC_TABLE */ + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const unsigned long FAR * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + return (const unsigned long FAR *)crc_table; +} + +/* ========================================================================= */ +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 + +/* ========================================================================= */ +unsigned long ZEXPORT crc32(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + if (buf == Z_NULL) return 0UL; + +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + +#ifdef BYFOUR + if (sizeof(void *) == sizeof(ptrdiff_t)) { + u4 endian; + + endian = 1; + if (*((unsigned char *)(&endian))) + return crc32_little(crc, buf, len); + else + return crc32_big(crc, buf, len); + } +#endif /* BYFOUR */ + crc = crc ^ 0xffffffffUL; + while (len >= 8) { + DO8; + len -= 8; + } + if (len) do { + DO1; + } while (--len); + return crc ^ 0xffffffffUL; +} + +#ifdef BYFOUR + +/* ========================================================================= */ +#define DOLIT4 c ^= *buf4++; \ + c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ + crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 + +/* ========================================================================= */ +local unsigned long crc32_little(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = (u4)crc; + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + while (len >= 32) { + DOLIT32; + len -= 32; + } + while (len >= 4) { + DOLIT4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + } while (--len); + c = ~c; + return (unsigned long)c; +} + +/* ========================================================================= */ +#define DOBIG4 c ^= *++buf4; \ + c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ + crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 + +/* ========================================================================= */ +local unsigned long crc32_big(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = REV((u4)crc); + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + buf4--; + while (len >= 32) { + DOBIG32; + len -= 32; + } + while (len >= 4) { + DOBIG4; + len -= 4; + } + buf4++; + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + } while (--len); + c = ~c; + return (unsigned long)(REV(c)); +} + +#endif /* BYFOUR */ + +#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ + +/* ========================================================================= */ +local unsigned long gf2_matrix_times(mat, vec) + unsigned long *mat; + unsigned long vec; +{ + unsigned long sum; + + sum = 0; + while (vec) { + if (vec & 1) + sum ^= *mat; + vec >>= 1; + mat++; + } + return sum; +} + +/* ========================================================================= */ +local void gf2_matrix_square(square, mat) + unsigned long *square; + unsigned long *mat; +{ + int n; + + for (n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); +} + +/* ========================================================================= */ +uLong ZEXPORT crc32_combine(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off_t len2; +{ + int n; + unsigned long row; + unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ + unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ + + /* degenerate case */ + if (len2 == 0) + return crc1; + + /* put operator for one zero bit in odd */ + odd[0] = 0xedb88320L; /* CRC-32 polynomial */ + row = 1; + for (n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + /* put operator for two zero bits in even */ + gf2_matrix_square(even, odd); + + /* put operator for four zero bits in odd */ + gf2_matrix_square(odd, even); + + /* apply len2 zeros to crc1 (first square will put the operator for one + zero byte, eight zero bits, in even) */ + do { + /* apply zeros operator for this bit of len2 */ + gf2_matrix_square(even, odd); + if (len2 & 1) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + if (len2 == 0) + break; + + /* another iteration of the loop with odd and even swapped */ + gf2_matrix_square(odd, even); + if (len2 & 1) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; +} diff --git a/Engine/lib/zlib/crc32.h b/Engine/lib/zlib/crc32.h new file mode 100644 index 000000000..8053b6117 --- /dev/null +++ b/Engine/lib/zlib/crc32.h @@ -0,0 +1,441 @@ +/* crc32.h -- tables for rapid CRC calculation + * Generated automatically by crc32.c + */ + +local const unsigned long FAR crc_table[TBLS][256] = +{ + { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +#ifdef BYFOUR + }, + { + 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, + 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, + 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, + 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, + 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, + 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, + 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, + 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, + 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, + 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, + 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, + 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, + 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, + 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, + 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, + 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, + 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, + 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, + 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, + 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, + 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, + 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, + 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, + 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, + 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, + 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, + 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, + 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, + 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, + 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, + 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, + 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, + 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, + 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, + 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, + 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, + 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, + 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, + 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, + 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, + 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, + 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, + 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, + 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, + 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, + 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, + 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, + 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, + 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, + 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, + 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, + 0x9324fd72UL + }, + { + 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, + 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, + 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, + 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, + 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, + 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, + 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, + 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, + 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, + 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, + 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, + 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, + 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, + 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, + 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, + 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, + 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, + 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, + 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, + 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, + 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, + 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, + 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, + 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, + 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, + 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, + 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, + 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, + 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, + 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, + 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, + 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, + 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, + 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, + 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, + 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, + 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, + 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, + 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, + 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, + 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, + 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, + 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, + 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, + 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, + 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, + 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, + 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, + 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, + 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, + 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, + 0xbe9834edUL + }, + { + 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, + 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, + 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, + 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, + 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, + 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, + 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, + 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, + 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, + 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, + 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, + 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, + 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, + 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, + 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, + 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, + 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, + 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, + 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, + 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, + 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, + 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, + 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, + 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, + 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, + 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, + 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, + 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, + 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, + 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, + 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, + 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, + 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, + 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, + 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, + 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, + 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, + 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, + 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, + 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, + 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, + 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, + 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, + 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, + 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, + 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, + 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, + 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, + 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, + 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, + 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, + 0xde0506f1UL + }, + { + 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, + 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, + 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, + 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, + 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, + 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, + 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, + 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, + 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, + 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, + 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, + 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, + 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, + 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, + 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, + 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, + 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, + 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, + 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, + 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, + 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, + 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, + 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, + 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, + 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, + 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, + 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, + 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, + 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, + 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, + 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, + 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, + 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, + 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, + 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, + 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, + 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, + 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, + 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, + 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, + 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, + 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, + 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, + 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, + 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, + 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, + 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, + 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, + 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, + 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, + 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, + 0x8def022dUL + }, + { + 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, + 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, + 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, + 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, + 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, + 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, + 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, + 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, + 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, + 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, + 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, + 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, + 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, + 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, + 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, + 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, + 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, + 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, + 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, + 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, + 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, + 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, + 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, + 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, + 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, + 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, + 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, + 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, + 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, + 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, + 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, + 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, + 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, + 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, + 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, + 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, + 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, + 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, + 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, + 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, + 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, + 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, + 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, + 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, + 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, + 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, + 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, + 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, + 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, + 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, + 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, + 0x72fd2493UL + }, + { + 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, + 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, + 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, + 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, + 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, + 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, + 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, + 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, + 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, + 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, + 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, + 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, + 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, + 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, + 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, + 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, + 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, + 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, + 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, + 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, + 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, + 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, + 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, + 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, + 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, + 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, + 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, + 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, + 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, + 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, + 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, + 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, + 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, + 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, + 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, + 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, + 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, + 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, + 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, + 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, + 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, + 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, + 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, + 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, + 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, + 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, + 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, + 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, + 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, + 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, + 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, + 0xed3498beUL + }, + { + 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, + 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, + 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, + 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, + 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, + 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, + 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, + 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, + 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, + 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, + 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, + 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, + 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, + 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, + 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, + 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, + 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, + 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, + 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, + 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, + 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, + 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, + 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, + 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, + 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, + 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, + 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, + 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, + 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, + 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, + 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, + 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, + 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, + 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, + 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, + 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, + 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, + 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, + 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, + 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, + 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, + 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, + 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, + 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, + 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, + 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, + 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, + 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, + 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, + 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, + 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, + 0xf10605deUL +#endif + } +}; diff --git a/Engine/lib/zlib/deflate.c b/Engine/lib/zlib/deflate.c new file mode 100644 index 000000000..29ce1f64a --- /dev/null +++ b/Engine/lib/zlib/deflate.c @@ -0,0 +1,1736 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in http://www.ietf.org/rfc/rfc1951.txt + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* @(#) $Id$ */ + +#include "deflate.h" + +const char deflate_copyright[] = + " deflate 1.2.3 Copyright 1995-2005 Jean-loup Gailly "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +#ifndef FASTEST +local block_state deflate_slow OF((deflate_state *s, int flush)); +#endif +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifndef FASTEST +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif +#endif +local uInt longest_match_fast OF((deflate_state *s, IPos cur_match)); + +#ifdef DEBUG +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +#ifdef FASTEST +local const config configuration_table[2] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ +#else +local const config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ +#endif + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +#ifndef NO_DUMMY_DECL +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ +#endif + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int ZEXPORT deflateInit_(strm, level, version, stream_size) + z_streamp strm; + int level; + const char *version; + int stream_size; +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + version, stream_size) + z_streamp strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; + const char *version; + int stream_size; +{ + deflate_state *s; + int wrap = 1; + static const char my_version[] = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } +#ifdef GZIP + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } +#endif + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + + s->wrap = wrap; + s->gzhead = Z_NULL; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + s->status = FINISH_STATE; + strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) + z_streamp strm; + const Bytef *dictionary; + uInt dictLength; +{ + deflate_state *s; + uInt length = dictLength; + uInt n; + IPos hash_head = 0; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL || + strm->state->wrap == 2 || + (strm->state->wrap == 1 && strm->state->status != INIT_STATE)) + return Z_STREAM_ERROR; + + s = strm->state; + if (s->wrap) + strm->adler = adler32(strm->adler, dictionary, dictLength); + + if (length < MIN_MATCH) return Z_OK; + if (length > MAX_DIST(s)) { + length = MAX_DIST(s); + dictionary += dictLength - length; /* use the tail of the dictionary */ + } + zmemcpy(s->window, dictionary, length); + s->strstart = length; + s->block_start = (long)length; + + /* Insert all strings in the hash table (except for the last two bytes). + * s->lookahead stays null, so s->ins_h will be recomputed at the next + * call of fill_window. + */ + s->ins_h = s->window[0]; + UPDATE_HASH(s, s->ins_h, s->window[1]); + for (n = 0; n <= length - MIN_MATCH; n++) { + INSERT_STRING(s, n, hash_head); + } + if (hash_head) hash_head = 0; /* to make compiler happy */ + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (strm) + z_streamp strm; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { + return Z_STREAM_ERROR; + } + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->wrap < 0) { + s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ + } + s->status = s->wrap ? INIT_STATE : BUSY_STATE; + strm->adler = +#ifdef GZIP + s->wrap == 2 ? crc32(0L, Z_NULL, 0) : +#endif + adler32(0L, Z_NULL, 0); + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + lm_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateSetHeader (strm, head) + z_streamp strm; + gz_headerp head; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (strm->state->wrap != 2) return Z_STREAM_ERROR; + strm->state->gzhead = head; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePrime (strm, bits, value) + z_streamp strm; + int bits; + int value; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + strm->state->bi_valid = bits; + strm->state->bi_buf = (ush)(value & ((1 << bits) - 1)); + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams(strm, level, strategy) + z_streamp strm; + int level; + int strategy; +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if (func != configuration_table[level].func && strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_PARTIAL_FLUSH); + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= */ +int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) + z_streamp strm; + int good_length; + int max_lazy; + int nice_length; + int max_chain; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + s->good_match = good_length; + s->max_lazy_match = max_lazy; + s->nice_match = nice_length; + s->max_chain_length = max_chain; + return Z_OK; +} + +/* ========================================================================= + * For the default windowBits of 15 and memLevel of 8, this function returns + * a close to exact, as well as small, upper bound on the compressed size. + * They are coded as constants here for a reason--if the #define's are + * changed, then this function needs to be changed as well. The return + * value for 15 and 8 only works for those exact settings. + * + * For any setting other than those defaults for windowBits and memLevel, + * the value returned is a conservative worst case for the maximum expansion + * resulting from using fixed blocks instead of stored blocks, which deflate + * can emit on compressed data for some combinations of the parameters. + * + * This function could be more sophisticated to provide closer upper bounds + * for every combination of windowBits and memLevel, as well as wrap. + * But even the conservative upper bound of about 14% expansion does not + * seem onerous for output buffer allocation. + */ +uLong ZEXPORT deflateBound(strm, sourceLen) + z_streamp strm; + uLong sourceLen; +{ + deflate_state *s; + uLong destLen; + + /* conservative upper bound */ + destLen = sourceLen + + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 11; + + /* if can't get parameters, return conservative bound */ + if (strm == Z_NULL || strm->state == Z_NULL) + return destLen; + + /* if not default parameters, return conservative bound */ + s = strm->state; + if (s->w_bits != 15 || s->hash_bits != 8 + 7) + return destLen; + + /* default settings: return tight bound for that case */ + return compressBound(sourceLen); +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(strm) + z_streamp strm; +{ + unsigned len = strm->state->pending; + + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, strm->state->pending_out, len); + strm->next_out += len; + strm->state->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + strm->state->pending -= len; + if (strm->state->pending == 0) { + strm->state->pending_out = strm->state->pending_buf; + } +} + +/* ========================================================================= */ +int ZEXPORT deflate (strm, flush) + z_streamp strm; + int flush; +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_FINISH || flush < 0) { + return Z_STREAM_ERROR; + } + s = strm->state; + + if (strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the header */ + if (s->status == INIT_STATE) { +#ifdef GZIP + if (s->wrap == 2) { + strm->adler = crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (s->gzhead == NULL) { + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); + s->status = BUSY_STATE; + } + else { + put_byte(s, (s->gzhead->text ? 1 : 0) + + (s->gzhead->hcrc ? 2 : 0) + + (s->gzhead->extra == Z_NULL ? 0 : 4) + + (s->gzhead->name == Z_NULL ? 0 : 8) + + (s->gzhead->comment == Z_NULL ? 0 : 16) + ); + put_byte(s, (Byte)(s->gzhead->time & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, s->gzhead->os & 0xff); + if (s->gzhead->extra != NULL) { + put_byte(s, s->gzhead->extra_len & 0xff); + put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); + } + if (s->gzhead->hcrc) + strm->adler = crc32(strm->adler, s->pending_buf, + s->pending); + s->gzindex = 0; + s->status = EXTRA_STATE; + } + } + else +#endif + { + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; + else + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = adler32(0L, Z_NULL, 0); + } + } +#ifdef GZIP + if (s->status == EXTRA_STATE) { + if (s->gzhead->extra != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + + while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) + break; + } + put_byte(s, s->gzhead->extra[s->gzindex]); + s->gzindex++; + } + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (s->gzindex == s->gzhead->extra_len) { + s->gzindex = 0; + s->status = NAME_STATE; + } + } + else + s->status = NAME_STATE; + } + if (s->status == NAME_STATE) { + if (s->gzhead->name != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->name[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) { + s->gzindex = 0; + s->status = COMMENT_STATE; + } + } + else + s->status = COMMENT_STATE; + } + if (s->status == COMMENT_STATE) { + if (s->gzhead->comment != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->comment[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) + s->status = HCRC_STATE; + } + else + s->status = HCRC_STATE; + } + if (s->status == HCRC_STATE) { + if (s->gzhead->hcrc) { + if (s->pending + 2 > s->pending_buf_size) + flush_pending(strm); + if (s->pending + 2 <= s->pending_buf_size) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + strm->adler = crc32(0L, Z_NULL, 0); + s->status = BUSY_STATE; + } + } + else + s->status = BUSY_STATE; + } +#endif + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm->avail_in == 0 && flush <= old_flush && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = (*(configuration_table[s->level].func))(s, flush); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->wrap <= 0) return Z_STREAM_END; + + /* Write the trailer */ +#ifdef GZIP + if (s->wrap == 2) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); + put_byte(s, (Byte)(strm->total_in & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); + } + else +#endif + { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (strm) + z_streamp strm; +{ + int status; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + status = strm->state->status; + if (status != INIT_STATE && + status != EXTRA_STATE && + status != NAME_STATE && + status != COMMENT_STATE && + status != HCRC_STATE && + status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, strm->state->pending_buf); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (dest, source) + z_streamp dest; + z_streamp source; +{ +#ifdef MAXSEG_64K + return Z_STREAM_ERROR; +#else + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + return Z_STREAM_ERROR; + } + + ss = source->state; + + zmemcpy(dest, source, sizeof(z_stream)); + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + zmemcpy(ds, ss, sizeof(deflate_state)); + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* following zmemcpy do not work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +#endif /* MAXSEG_64K */ +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(strm, buf, size) + z_streamp strm; + Bytef *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, strm->next_in, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, strm->next_in, len); + } +#endif + zmemcpy(buf, strm->next_in, len); + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) + deflate_state *s; +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifndef FASTEST +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +#endif +} + +#ifndef FASTEST +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return (uInt)best_len; + return s->lookahead; +} +#endif /* ASMV */ +#endif /* FASTEST */ + +/* --------------------------------------------------------------------------- + * Optimized version for level == 1 or strategy == Z_RLE only + */ +local uInt longest_match_fast(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + Assert(cur_match < s->strstart, "no future"); + + match = s->window + cur_match; + + /* Return failure if the match length is less than 2: + */ + if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match += 2; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + + if (len < MIN_MATCH) return MIN_MATCH - 1; + + s->match_start = cur_match; + return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; +} + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) + deflate_state *s; + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (zmemcmp(s->window + match, + s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif /* DEBUG */ + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(s) + deflate_state *s; +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + /* %%% avoid this when Z_RLE */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif + more += wsize; + } + if (s->strm->avail_in == 0) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead >= MIN_MATCH) { + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, eof) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (eof)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, eof) { \ + FLUSH_BLOCK_ONLY(s, eof); \ + if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(s, flush) + deflate_state *s; + int flush; +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (long)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ +#ifdef FASTEST + if ((s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) || + (s->strategy == Z_RLE && s->strstart - hash_head == 1)) { + s->match_length = longest_match_fast (s, hash_head); + } +#else + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } +#endif + /* longest_match() or longest_match_fast() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + _tr_tally_dist(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ +#ifndef FASTEST + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else +#endif + { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +#ifndef FASTEST +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } + /* longest_match() or longest_match_fast() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED +#if TOO_FAR <= 32767 + || (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR) +#endif + )) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + if (bflush) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + s->match_available = 0; + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif /* FASTEST */ + +#if 0 +/* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one. Do not maintain a hash table. (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ +local block_state deflate_rle(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + uInt run; /* length of run */ + uInt max; /* maximum length of run */ + uInt prev; /* byte at distance one to match */ + Bytef *scan; /* scan for end of run */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the longest encodable run. + */ + if (s->lookahead < MAX_MATCH) { + fill_window(s); + if (s->lookahead < MAX_MATCH && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + run = 0; + if (s->strstart > 0) { /* if there is a previous byte, that is */ + max = s->lookahead < MAX_MATCH ? s->lookahead : MAX_MATCH; + scan = s->window + s->strstart - 1; + prev = *scan++; + do { + if (*scan++ != prev) + break; + } while (++run < max); + } + + /* Emit match if have run of MIN_MATCH or longer, else emit literal */ + if (run >= MIN_MATCH) { + check_match(s, s->strstart, s->strstart - 1, run); + _tr_tally_dist(s, 1, run - MIN_MATCH, bflush); + s->lookahead -= run; + s->strstart += run; + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif diff --git a/Engine/lib/zlib/deflate.h b/Engine/lib/zlib/deflate.h new file mode 100644 index 000000000..05a5ab3a2 --- /dev/null +++ b/Engine/lib/zlib/deflate.h @@ -0,0 +1,331 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2004 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef DEFLATE_H +#define DEFLATE_H + +#include "zutil.h" + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer creation by deflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip encoding + should be left enabled. */ +#ifndef NO_GZIP +# define GZIP +#endif + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define INIT_STATE 42 +#define EXTRA_STATE 69 +#define NAME_STATE 73 +#define COMMENT_STATE 91 +#define HCRC_STATE 103 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + uInt pending; /* nb of bytes in the pending buffer */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + gz_headerp gzhead; /* gzip header information to write */ + uInt gzindex; /* where in extra, name, or comment */ + Byte method; /* STORED (for zip only) or DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to supress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + int last_eob_len; /* bit length of EOB code for last block */ + +#ifdef DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + + /* in trees.c */ +void _tr_init OF((deflate_state *s)); +int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); +void _tr_align OF((deflate_state *s)); +void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch _length_code[]; + extern uch _dist_code[]; +#else + extern const uch _length_code[]; + extern const uch _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (length); \ + ush dist = (distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ diff --git a/Engine/lib/zlib/gzio.c b/Engine/lib/zlib/gzio.c new file mode 100644 index 000000000..7e90f4928 --- /dev/null +++ b/Engine/lib/zlib/gzio.c @@ -0,0 +1,1026 @@ +/* gzio.c -- IO on .gz files + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Compile this file with -DNO_GZCOMPRESS to avoid the compression code. + */ + +/* @(#) $Id$ */ + +#include + +#include "zutil.h" + +#ifdef NO_DEFLATE /* for compatibility with old definition */ +# define NO_GZCOMPRESS +#endif + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +#ifndef Z_BUFSIZE +# ifdef MAXSEG_64K +# define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */ +# else +# define Z_BUFSIZE 16384 +# endif +#endif +#ifndef Z_PRINTF_BUFSIZE +# define Z_PRINTF_BUFSIZE 4096 +#endif + +#ifdef __MVS__ +# pragma map (fdopen , "\174\174FDOPEN") + FILE *fdopen(int, const char *); +#endif + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern void free OF((voidpf ptr)); +#endif + +#define ALLOC(size) malloc(size) +#define TRYFREE(p) {if (p) free(p);} + +static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ + +typedef struct gz_stream { + z_stream stream; + int z_err; /* error code for last stream operation */ + int z_eof; /* set if end of input file */ + FILE *file; /* .gz file */ + Byte *inbuf; /* input buffer */ + Byte *outbuf; /* output buffer */ + uLong crc; /* crc32 of uncompressed data */ + char *msg; /* error message */ + char *path; /* path name for debugging only */ + int transparent; /* 1 if input file is not a .gz file */ + char mode; /* 'w' or 'r' */ + z_off_t start; /* start of compressed data in file (header skipped) */ + z_off_t in; /* bytes into deflate or inflate */ + z_off_t out; /* bytes out of deflate or inflate */ + int back; /* one character push-back */ + int last; /* true if push-back is last character */ +} gz_stream; + + +local gzFile gz_open OF((const char *path, const char *mode, int fd)); +local int do_flush OF((gzFile file, int flush)); +local int get_byte OF((gz_stream *s)); +local void check_header OF((gz_stream *s)); +local int destroy OF((gz_stream *s)); +local void putLong OF((FILE *file, uLong x)); +local uLong getLong OF((gz_stream *s)); + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb"). The file is given either by file descriptor + or path name (if fd == -1). + gz_open returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). +*/ +local gzFile gz_open (path, mode, fd) + const char *path; + const char *mode; + int fd; +{ + int err; + int level = Z_DEFAULT_COMPRESSION; /* compression level */ + int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */ + char *p = (char*)mode; + gz_stream *s; + char fmode[80]; /* copy of mode, without the compression level */ + char *m = fmode; + + if (!path || !mode) return Z_NULL; + + s = (gz_stream *)ALLOC(sizeof(gz_stream)); + if (!s) return Z_NULL; + + s->stream.zalloc = (alloc_func)0; + s->stream.zfree = (free_func)0; + s->stream.opaque = (voidpf)0; + s->stream.next_in = s->inbuf = Z_NULL; + s->stream.next_out = s->outbuf = Z_NULL; + s->stream.avail_in = s->stream.avail_out = 0; + s->file = NULL; + s->z_err = Z_OK; + s->z_eof = 0; + s->in = 0; + s->out = 0; + s->back = EOF; + s->crc = crc32(0L, Z_NULL, 0); + s->msg = NULL; + s->transparent = 0; + + s->path = (char*)ALLOC(strlen(path)+1); + if (s->path == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + strcpy(s->path, path); /* do this early for debugging */ + + s->mode = '\0'; + do { + if (*p == 'r') s->mode = 'r'; + if (*p == 'w' || *p == 'a') s->mode = 'w'; + if (*p >= '0' && *p <= '9') { + level = *p - '0'; + } else if (*p == 'f') { + strategy = Z_FILTERED; + } else if (*p == 'h') { + strategy = Z_HUFFMAN_ONLY; + } else if (*p == 'R') { + strategy = Z_RLE; + } else { + *m++ = *p; /* copy the mode */ + } + } while (*p++ && m != fmode + sizeof(fmode)); + if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateInit2(&(s->stream), level, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy); + /* windowBits is passed < 0 to suppress zlib header */ + + s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); +#endif + if (err != Z_OK || s->outbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } else { + s->stream.next_in = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); + + err = inflateInit2(&(s->stream), -MAX_WBITS); + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are + * present after the compressed stream. + */ + if (err != Z_OK || s->inbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } + s->stream.avail_out = Z_BUFSIZE; + + errno = 0; + s->file = fd < 0 ? F_OPEN(path, fmode) : (FILE*)fdopen(fd, fmode); + + if (s->file == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + if (s->mode == 'w') { + /* Write a very simple .gz header: + */ + fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1], + Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE); + s->start = 10L; + /* We use 10L instead of ftell(s->file) to because ftell causes an + * fflush on some systems. This version of the library doesn't use + * start anyway in write mode, so this initialization is not + * necessary. + */ + } else { + check_header(s); /* skip the .gz header */ + s->start = ftell(s->file) - s->stream.avail_in; + } + + return (gzFile)s; +} + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. +*/ +gzFile ZEXPORT gzopen (path, mode) + const char *path; + const char *mode; +{ + return gz_open (path, mode, -1); +} + +/* =========================================================================== + Associate a gzFile with the file descriptor fd. fd is not dup'ed here + to mimic the behavio(u)r of fdopen. +*/ +gzFile ZEXPORT gzdopen (fd, mode) + int fd; + const char *mode; +{ + char name[46]; /* allow for up to 128-bit integers */ + + if (fd < 0) return (gzFile)Z_NULL; + sprintf(name, "", fd); /* for debugging */ + + return gz_open (name, mode, fd); +} + +/* =========================================================================== + * Update the compression level and strategy + */ +int ZEXPORT gzsetparams (file, level, strategy) + gzFile file; + int level; + int strategy; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + /* Make room to allow flushing */ + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + } + s->stream.avail_out = Z_BUFSIZE; + } + + return deflateParams (&(s->stream), level, strategy); +} + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ +local int get_byte(s) + gz_stream *s; +{ + if (s->z_eof) return EOF; + if (s->stream.avail_in == 0) { + errno = 0; + s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) s->z_err = Z_ERRNO; + return EOF; + } + s->stream.next_in = s->inbuf; + } + s->stream.avail_in--; + return *(s->stream.next_in)++; +} + +/* =========================================================================== + Check the gzip header of a gz_stream opened for reading. Set the stream + mode to transparent if the gzip magic header is not present; set s->err + to Z_DATA_ERROR if the magic header is present but the rest of the header + is incorrect. + IN assertion: the stream s has already been created sucessfully; + s->stream.avail_in is zero for the first time, but may be non-zero + for concatenated .gz files. +*/ +local void check_header(s) + gz_stream *s; +{ + int method; /* method byte */ + int flags; /* flags byte */ + uInt len; + int c; + + /* Assure two bytes in the buffer so we can peek ahead -- handle case + where first byte of header is at the end of the buffer after the last + gzip segment */ + len = s->stream.avail_in; + if (len < 2) { + if (len) s->inbuf[0] = s->stream.next_in[0]; + errno = 0; + len = (uInt)fread(s->inbuf + len, 1, Z_BUFSIZE >> len, s->file); + if (len == 0 && ferror(s->file)) s->z_err = Z_ERRNO; + s->stream.avail_in += len; + s->stream.next_in = s->inbuf; + if (s->stream.avail_in < 2) { + s->transparent = s->stream.avail_in; + return; + } + } + + /* Peek ahead to check the gzip magic header */ + if (s->stream.next_in[0] != gz_magic[0] || + s->stream.next_in[1] != gz_magic[1]) { + s->transparent = 1; + return; + } + s->stream.avail_in -= 2; + s->stream.next_in += 2; + + /* Check the rest of the gzip header */ + method = get_byte(s); + flags = get_byte(s); + if (method != Z_DEFLATED || (flags & RESERVED) != 0) { + s->z_err = Z_DATA_ERROR; + return; + } + + /* Discard time, xflags and OS code: */ + for (len = 0; len < 6; len++) (void)get_byte(s); + + if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */ + len = (uInt)get_byte(s); + len += ((uInt)get_byte(s))<<8; + /* len is garbage if EOF but the loop below will quit anyway */ + while (len-- != 0 && get_byte(s) != EOF) ; + } + if ((flags & ORIG_NAME) != 0) { /* skip the original file name */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & COMMENT) != 0) { /* skip the .gz file comment */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & HEAD_CRC) != 0) { /* skip the header crc */ + for (len = 0; len < 2; len++) (void)get_byte(s); + } + s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK; +} + + /* =========================================================================== + * Cleanup then free the given gz_stream. Return a zlib error code. + Try freeing in the reverse order of allocations. + */ +local int destroy (s) + gz_stream *s; +{ + int err = Z_OK; + + if (!s) return Z_STREAM_ERROR; + + TRYFREE(s->msg); + + if (s->stream.state != NULL) { + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateEnd(&(s->stream)); +#endif + } else if (s->mode == 'r') { + err = inflateEnd(&(s->stream)); + } + } + if (s->file != NULL && fclose(s->file)) { +#ifdef ESPIPE + if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */ +#endif + err = Z_ERRNO; + } + if (s->z_err < 0) err = s->z_err; + + TRYFREE(s->inbuf); + TRYFREE(s->outbuf); + TRYFREE(s->path); + TRYFREE(s); + return err; +} + +/* =========================================================================== + Reads the given number of uncompressed bytes from the compressed file. + gzread returns the number of bytes actually read (0 for end of file). +*/ +int ZEXPORT gzread (file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + Bytef *start = (Bytef*)buf; /* starting point for crc computation */ + Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */ + + if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR; + + if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1; + if (s->z_err == Z_STREAM_END) return 0; /* EOF */ + + next_out = (Byte*)buf; + s->stream.next_out = (Bytef*)buf; + s->stream.avail_out = len; + + if (s->stream.avail_out && s->back != EOF) { + *next_out++ = s->back; + s->stream.next_out++; + s->stream.avail_out--; + s->back = EOF; + s->out++; + start++; + if (s->last) { + s->z_err = Z_STREAM_END; + return 1; + } + } + + while (s->stream.avail_out != 0) { + + if (s->transparent) { + /* Copy first the lookahead bytes: */ + uInt n = s->stream.avail_in; + if (n > s->stream.avail_out) n = s->stream.avail_out; + if (n > 0) { + zmemcpy(s->stream.next_out, s->stream.next_in, n); + next_out += n; + s->stream.next_out = next_out; + s->stream.next_in += n; + s->stream.avail_out -= n; + s->stream.avail_in -= n; + } + if (s->stream.avail_out > 0) { + s->stream.avail_out -= + (uInt)fread(next_out, 1, s->stream.avail_out, s->file); + } + len -= s->stream.avail_out; + s->in += len; + s->out += len; + if (len == 0) s->z_eof = 1; + return (int)len; + } + if (s->stream.avail_in == 0 && !s->z_eof) { + + errno = 0; + s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) { + s->z_err = Z_ERRNO; + break; + } + } + s->stream.next_in = s->inbuf; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = inflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + + if (s->z_err == Z_STREAM_END) { + /* Check CRC and original size */ + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + start = s->stream.next_out; + + if (getLong(s) != s->crc) { + s->z_err = Z_DATA_ERROR; + } else { + (void)getLong(s); + /* The uncompressed length returned by above getlong() may be + * different from s->out in case of concatenated .gz files. + * Check for such files: + */ + check_header(s); + if (s->z_err == Z_OK) { + inflateReset(&(s->stream)); + s->crc = crc32(0L, Z_NULL, 0); + } + } + } + if (s->z_err != Z_OK || s->z_eof) break; + } + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + + if (len == s->stream.avail_out && + (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO)) + return -1; + return (int)(len - s->stream.avail_out); +} + + +/* =========================================================================== + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ +int ZEXPORT gzgetc(file) + gzFile file; +{ + unsigned char c; + + return gzread(file, &c, 1) == 1 ? c : -1; +} + + +/* =========================================================================== + Push one byte back onto the stream. +*/ +int ZEXPORT gzungetc(c, file) + int c; + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r' || c == EOF || s->back != EOF) return EOF; + s->back = c; + s->out--; + s->last = (s->z_err == Z_STREAM_END); + if (s->last) s->z_err = Z_OK; + s->z_eof = 0; + return c; +} + + +/* =========================================================================== + Reads bytes from the compressed file until len-1 characters are + read, or a newline character is read and transferred to buf, or an + end-of-file condition is encountered. The string is then terminated + with a null character. + gzgets returns buf, or Z_NULL in case of error. + + The current implementation is not optimized at all. +*/ +char * ZEXPORT gzgets(file, buf, len) + gzFile file; + char *buf; + int len; +{ + char *b = buf; + if (buf == Z_NULL || len <= 0) return Z_NULL; + + while (--len > 0 && gzread(file, buf, 1) == 1 && *buf++ != '\n') ; + *buf = '\0'; + return b == buf && len > 0 ? Z_NULL : b; +} + + +#ifndef NO_GZCOMPRESS +/* =========================================================================== + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of bytes actually written (0 in case of error). +*/ +int ZEXPORT gzwrite (file, buf, len) + gzFile file; + voidpc buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.next_in = (Bytef*)buf; + s->stream.avail_in = len; + + while (s->stream.avail_in != 0) { + + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + break; + } + s->stream.avail_out = Z_BUFSIZE; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + if (s->z_err != Z_OK) break; + } + s->crc = crc32(s->crc, (const Bytef *)buf, len); + + return (int)(len - s->stream.avail_in); +} + + +/* =========================================================================== + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). +*/ +#ifdef STDC +#include + +int ZEXPORTVA gzprintf (gzFile file, const char *format, /* args */ ...) +{ + char buf[Z_PRINTF_BUFSIZE]; + va_list va; + int len; + + buf[sizeof(buf) - 1] = 0; + va_start(va, format); +#ifdef NO_vsnprintf +# ifdef HAS_vsprintf_void + (void)vsprintf(buf, format, va); + va_end(va); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = vsprintf(buf, format, va); + va_end(va); +# endif +#else +# ifdef HAS_vsnprintf_void + (void)vsnprintf(buf, sizeof(buf), format, va); + va_end(va); + len = strlen(buf); +# else + len = vsnprintf(buf, sizeof(buf), format, va); + va_end(va); +# endif +#endif + if (len <= 0 || len >= (int)sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, (unsigned)len); +} +#else /* not ANSI C */ + +int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + gzFile file; + const char *format; + int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; +{ + char buf[Z_PRINTF_BUFSIZE]; + int len; + + buf[sizeof(buf) - 1] = 0; +#ifdef NO_snprintf +# ifdef HAS_sprintf_void + sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#else +# ifdef HAS_snprintf_void + snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = strlen(buf); +# else + len = snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#endif + if (len <= 0 || len >= sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, len); +} +#endif + +/* =========================================================================== + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ +int ZEXPORT gzputc(file, c) + gzFile file; + int c; +{ + unsigned char cc = (unsigned char) c; /* required for big endian systems */ + + return gzwrite(file, &cc, 1) == 1 ? (int)cc : -1; +} + + +/* =========================================================================== + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ +int ZEXPORT gzputs(file, s) + gzFile file; + const char *s; +{ + return gzwrite(file, (char*)s, (unsigned)strlen(s)); +} + + +/* =========================================================================== + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. +*/ +local int do_flush (file, flush) + gzFile file; + int flush; +{ + uInt len; + int done = 0; + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.avail_in = 0; /* should be zero already anyway */ + + for (;;) { + len = Z_BUFSIZE - s->stream.avail_out; + + if (len != 0) { + if ((uInt)fwrite(s->outbuf, 1, len, s->file) != len) { + s->z_err = Z_ERRNO; + return Z_ERRNO; + } + s->stream.next_out = s->outbuf; + s->stream.avail_out = Z_BUFSIZE; + } + if (done) break; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), flush); + s->out -= s->stream.avail_out; + + /* Ignore the second of two consecutive flushes: */ + if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK; + + /* deflate has finished flushing only when it hasn't used up + * all the available space in the output buffer: + */ + done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END); + + if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break; + } + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} + +int ZEXPORT gzflush (file, flush) + gzFile file; + int flush; +{ + gz_stream *s = (gz_stream*)file; + int err = do_flush (file, flush); + + if (err) return err; + fflush(s->file); + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} +#endif /* NO_GZCOMPRESS */ + +/* =========================================================================== + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error. + SEEK_END is not implemented, returns error. + In this version of the library, gzseek can be extremely slow. +*/ +z_off_t ZEXPORT gzseek (file, offset, whence) + gzFile file; + z_off_t offset; + int whence; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || whence == SEEK_END || + s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) { + return -1L; + } + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return -1L; +#else + if (whence == SEEK_SET) { + offset -= s->in; + } + if (offset < 0) return -1L; + + /* At this point, offset is the number of zero bytes to write. */ + if (s->inbuf == Z_NULL) { + s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); /* for seeking */ + if (s->inbuf == Z_NULL) return -1L; + zmemzero(s->inbuf, Z_BUFSIZE); + } + while (offset > 0) { + uInt size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (uInt)offset; + + size = gzwrite(file, s->inbuf, size); + if (size == 0) return -1L; + + offset -= size; + } + return s->in; +#endif + } + /* Rest of function is for reading only */ + + /* compute absolute position */ + if (whence == SEEK_CUR) { + offset += s->out; + } + if (offset < 0) return -1L; + + if (s->transparent) { + /* map to fseek */ + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + if (fseek(s->file, offset, SEEK_SET) < 0) return -1L; + + s->in = s->out = offset; + return offset; + } + + /* For a negative seek, rewind and use positive seek */ + if (offset >= s->out) { + offset -= s->out; + } else if (gzrewind(file) < 0) { + return -1L; + } + /* offset is now the number of bytes to skip. */ + + if (offset != 0 && s->outbuf == Z_NULL) { + s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); + if (s->outbuf == Z_NULL) return -1L; + } + if (offset && s->back != EOF) { + s->back = EOF; + s->out++; + offset--; + if (s->last) s->z_err = Z_STREAM_END; + } + while (offset > 0) { + int size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (int)offset; + + size = gzread(file, s->outbuf, (uInt)size); + if (size <= 0) return -1L; + offset -= size; + } + return s->out; +} + +/* =========================================================================== + Rewinds input file. +*/ +int ZEXPORT gzrewind (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return -1; + + s->z_err = Z_OK; + s->z_eof = 0; + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + s->crc = crc32(0L, Z_NULL, 0); + if (!s->transparent) (void)inflateReset(&s->stream); + s->in = 0; + s->out = 0; + return fseek(s->file, s->start, SEEK_SET); +} + +/* =========================================================================== + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. +*/ +z_off_t ZEXPORT gztell (file) + gzFile file; +{ + return gzseek(file, 0L, SEEK_CUR); +} + +/* =========================================================================== + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ +int ZEXPORT gzeof (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + /* With concatenated compressed files that can have embedded + * crc trailers, z_eof is no longer the only/best indicator of EOF + * on a gz_stream. Handle end-of-stream error explicitly here. + */ + if (s == NULL || s->mode != 'r') return 0; + if (s->z_eof) return 1; + return s->z_err == Z_STREAM_END; +} + +/* =========================================================================== + Returns 1 if reading and doing so transparently, otherwise zero. +*/ +int ZEXPORT gzdirect (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return 0; + return s->transparent; +} + +/* =========================================================================== + Outputs a long in LSB order to the given file +*/ +local void putLong (file, x) + FILE *file; + uLong x; +{ + int n; + for (n = 0; n < 4; n++) { + fputc((int)(x & 0xff), file); + x >>= 8; + } +} + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets z_err in case + of error. +*/ +local uLong getLong (s) + gz_stream *s; +{ + uLong x = (uLong)get_byte(s); + int c; + + x += ((uLong)get_byte(s))<<8; + x += ((uLong)get_byte(s))<<16; + c = get_byte(s); + if (c == EOF) s->z_err = Z_DATA_ERROR; + x += ((uLong)c)<<24; + return x; +} + +/* =========================================================================== + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. +*/ +int ZEXPORT gzclose (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return Z_STREAM_ERROR; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return Z_STREAM_ERROR; +#else + if (do_flush (file, Z_FINISH) != Z_OK) + return destroy((gz_stream*)file); + + putLong (s->file, s->crc); + putLong (s->file, (uLong)(s->in & 0xffffffff)); +#endif + } + return destroy((gz_stream*)file); +} + +#ifdef STDC +# define zstrerror(errnum) strerror(errnum) +#else +# define zstrerror(errnum) "" +#endif + +/* =========================================================================== + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ +const char * ZEXPORT gzerror (file, errnum) + gzFile file; + int *errnum; +{ + char *m; + gz_stream *s = (gz_stream*)file; + + if (s == NULL) { + *errnum = Z_STREAM_ERROR; + return (const char*)ERR_MSG(Z_STREAM_ERROR); + } + *errnum = s->z_err; + if (*errnum == Z_OK) return (const char*)""; + + m = (char*)(*errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg); + + if (m == NULL || *m == '\0') m = (char*)ERR_MSG(s->z_err); + + TRYFREE(s->msg); + s->msg = (char*)ALLOC(strlen(s->path) + strlen(m) + 3); + if (s->msg == Z_NULL) return (const char*)ERR_MSG(Z_MEM_ERROR); + strcpy(s->msg, s->path); + strcat(s->msg, ": "); + strcat(s->msg, m); + return (const char*)s->msg; +} + +/* =========================================================================== + Clear the error and end-of-file flags, and do the same for the real file. +*/ +void ZEXPORT gzclearerr (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return; + if (s->z_err != Z_STREAM_END) s->z_err = Z_OK; + s->z_eof = 0; + clearerr(s->file); +} diff --git a/Engine/lib/zlib/infback.c b/Engine/lib/zlib/infback.c new file mode 100644 index 000000000..455dbc9ee --- /dev/null +++ b/Engine/lib/zlib/infback.c @@ -0,0 +1,623 @@ +/* infback.c -- inflate using a call-back interface + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + This code is largely copied from inflate.c. Normally either infback.o or + inflate.o would be linked into an application--not both. The interface + with inffast.c is retained so that optimized assembler-coded versions of + inflate_fast() can be used with either inflate.c or infback.c. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); + +/* + strm provides memory allocation functions in zalloc and zfree, or + Z_NULL to use the library memory allocation functions. + + windowBits is in the range 8..15, and window is a user-supplied + window and output buffer that is 2**windowBits bytes. + */ +int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size) +z_streamp strm; +int windowBits; +unsigned char FAR *window; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL || window == Z_NULL || + windowBits < 8 || windowBits > 15) + return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *)ZALLOC(strm, 1, + sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->dmax = 32768U; + state->wbits = windowBits; + state->wsize = 1U << windowBits; + state->window = window; + state->write = 0; + state->whave = 0; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +/* Macros for inflateBack(): */ + +/* Load returned state from inflate_fast() */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Set state from registers for inflate_fast() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Assure that some input is available. If input is requested, but denied, + then return a Z_BUF_ERROR from inflateBack(). */ +#define PULL() \ + do { \ + if (have == 0) { \ + have = in(in_desc, &next); \ + if (have == 0) { \ + next = Z_NULL; \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflateBack() + with an error if there is no input available. */ +#define PULLBYTE() \ + do { \ + PULL(); \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflateBack() with + an error. */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Assure that some output space is available, by writing out the window + if it's full. If the write fails, return from inflateBack() with a + Z_BUF_ERROR. */ +#define ROOM() \ + do { \ + if (left == 0) { \ + put = state->window; \ + left = state->wsize; \ + state->whave = left; \ + if (out(out_desc, put, left)) { \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* + strm provides the memory allocation functions and window buffer on input, + and provides information on the unused input on return. For Z_DATA_ERROR + returns, strm will also provide an error message. + + in() and out() are the call-back input and output functions. When + inflateBack() needs more input, it calls in(). When inflateBack() has + filled the window with output, or when it completes with data in the + window, it calls out() to write out the data. The application must not + change the provided input until in() is called again or inflateBack() + returns. The application must not change the window/output buffer until + inflateBack() returns. + + in() and out() are called with a descriptor parameter provided in the + inflateBack() call. This parameter can be a structure that provides the + information required to do the read or write, as well as accumulated + information on the input and output such as totals and check values. + + in() should return zero on failure. out() should return non-zero on + failure. If either in() or out() fails, than inflateBack() returns a + Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it + was in() or out() that caused in the error. Otherwise, inflateBack() + returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format + error, or Z_MEM_ERROR if it could not allocate memory for the state. + inflateBack() can also return Z_STREAM_ERROR if the input parameters + are not correct, i.e. strm is Z_NULL or the state was not initialized. + */ +int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc) +z_streamp strm; +in_func in; +void FAR *in_desc; +out_func out; +void FAR *out_desc; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + /* Check that the strm exists and that the state was initialized */ + if (strm == Z_NULL || strm->state == Z_NULL) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* Reset the state */ + strm->msg = Z_NULL; + state->mode = TYPE; + state->last = 0; + state->whave = 0; + next = strm->next_in; + have = next != Z_NULL ? strm->avail_in : 0; + hold = 0; + bits = 0; + put = state->window; + left = state->wsize; + + /* Inflate until end of block marked as last */ + for (;;) + switch (state->mode) { + case TYPE: + /* determine and dispatch block type */ + if (state->last) { + BYTEBITS(); + state->mode = DONE; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + + case STORED: + /* get and verify stored block length */ + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + + /* copy stored block from input to output */ + while (state->length != 0) { + copy = state->length; + PULL(); + ROOM(); + if (copy > have) copy = have; + if (copy > left) copy = left; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + + case TABLE: + /* get dynamic table entries descriptor */ + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + + /* get code length code lengths (not a typo) */ + state->have = 0; + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + + /* get length and distance code code lengths */ + state->have = 0; + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = (unsigned)(state->lens[state->have - 1]); + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + + case LEN: + /* use inflate_fast() if we have enough input and output */ + if (have >= 6 && left >= 258) { + RESTORE(); + if (state->whave < state->wsize) + state->whave = state->wsize - left; + inflate_fast(strm, state->wsize); + LOAD(); + break; + } + + /* get a literal, length, or end-of-block code */ + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + + /* process literal */ + if (this.op == 0) { + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + ROOM(); + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + } + + /* process end of block */ + if (this.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + + /* invalid code */ + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + + /* length code -- get extra bits, if any */ + state->extra = (unsigned)(this.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + + /* get distance code */ + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + + /* get distance extra bits, if any */ + state->extra = (unsigned)(this.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } + if (state->offset > state->wsize - (state->whave < state->wsize ? + left : 0)) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + + /* copy match from window to output */ + do { + ROOM(); + copy = state->wsize - state->offset; + if (copy < left) { + from = put + copy; + copy = left - copy; + } + else { + from = put - state->offset; + copy = left; + } + if (copy > state->length) copy = state->length; + state->length -= copy; + left -= copy; + do { + *put++ = *from++; + } while (--copy); + } while (state->length != 0); + break; + + case DONE: + /* inflate stream terminated properly -- write leftover output */ + ret = Z_STREAM_END; + if (left < state->wsize) { + if (out(out_desc, state->window, state->wsize - left)) + ret = Z_BUF_ERROR; + } + goto inf_leave; + + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + + default: /* can't happen, but makes compilers happy */ + ret = Z_STREAM_ERROR; + goto inf_leave; + } + + /* Return unused input */ + inf_leave: + strm->next_in = next; + strm->avail_in = have; + return ret; +} + +int ZEXPORT inflateBackEnd(strm) +z_streamp strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} diff --git a/Engine/lib/zlib/inffast.c b/Engine/lib/zlib/inffast.c new file mode 100644 index 000000000..bbee92ed1 --- /dev/null +++ b/Engine/lib/zlib/inffast.c @@ -0,0 +1,318 @@ +/* inffast.c -- fast decoding + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifndef ASMINF + +/* Allow machine dependent optimization for post-increment or pre-increment. + Based on testing to date, + Pre-increment preferred for: + - PowerPC G3 (Adler) + - MIPS R5000 (Randers-Pehrson) + Post-increment preferred for: + - none + No measurable difference: + - Pentium III (Anderson) + - M68060 (Nikl) + */ +#ifdef POSTINC +# define OFF 0 +# define PUP(a) *(a)++ +#else +# define OFF 1 +# define PUP(a) *++(a) +#endif + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + */ +void inflate_fast(strm, start) +z_streamp strm; +unsigned start; /* inflate()'s starting value for strm->avail_out */ +{ + struct inflate_state FAR *state; + unsigned char FAR *in; /* local strm->next_in */ + unsigned char FAR *last; /* while in < last, enough input available */ + unsigned char FAR *out; /* local strm->next_out */ + unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ + unsigned char FAR *end; /* while out < end, enough space available */ +#ifdef INFLATE_STRICT + unsigned dmax; /* maximum distance from zlib header */ +#endif + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const FAR *lcode; /* local strm->lencode */ + code const FAR *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + code this; /* retrieved table entry */ + unsigned op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned char FAR *from; /* where to copy match from */ + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + in = strm->next_in - OFF; + last = in + (strm->avail_in - 5); + out = strm->next_out - OFF; + beg = out - (start - strm->avail_out); + end = out + (strm->avail_out - 257); +#ifdef INFLATE_STRICT + dmax = state->dmax; +#endif + wsize = state->wsize; + whave = state->whave; + write = state->write; + window = state->window; + hold = state->hold; + bits = state->bits; + lcode = state->lencode; + dcode = state->distcode; + lmask = (1U << state->lenbits) - 1; + dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + do { + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = lcode[hold & lmask]; + dolen: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op == 0) { /* literal */ + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + PUP(out) = (unsigned char)(this.val); + } + else if (op & 16) { /* length base */ + len = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + len += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + } + Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = dcode[hold & dmask]; + dodist: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op & 16) { /* distance base */ + dist = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + } + dist += (unsigned)hold & ((1U << op) - 1); +#ifdef INFLATE_STRICT + if (dist > dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + hold >>= op; + bits -= op; + Tracevv((stderr, "inflate: distance %u\n", dist)); + op = (unsigned)(out - beg); /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + from = window - OFF; + if (write == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + else if (write < op) { /* wrap around window */ + from += wsize + write - op; + op -= write; + if (op < len) { /* some from end of window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = window - OFF; + if (write < len) { /* some from start of window */ + op = write; + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + } + else { /* contiguous in window */ + from += write - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + while (len > 2) { + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + else { + from = out - dist; /* copy direct from output */ + do { /* minimum length is three */ + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } while (len > 2); + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + } + else if ((op & 64) == 0) { /* 2nd level distance code */ + this = dcode[this.val + (hold & ((1U << op) - 1))]; + goto dodist; + } + else { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + } + else if ((op & 64) == 0) { /* 2nd level length code */ + this = lcode[this.val + (hold & ((1U << op) - 1))]; + goto dolen; + } + else if (op & 32) { /* end-of-block */ + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + else { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + } while (in < last && out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + in -= len; + bits -= len << 3; + hold &= (1U << bits) - 1; + + /* update state and return */ + strm->next_in = in + OFF; + strm->next_out = out + OFF; + strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); + strm->avail_out = (unsigned)(out < end ? + 257 + (end - out) : 257 - (out - end)); + state->hold = hold; + state->bits = bits; + return; +} + +/* + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and write == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ + +#endif /* !ASMINF */ diff --git a/Engine/lib/zlib/inffast.h b/Engine/lib/zlib/inffast.h new file mode 100644 index 000000000..1e88d2d97 --- /dev/null +++ b/Engine/lib/zlib/inffast.h @@ -0,0 +1,11 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/Engine/lib/zlib/inffixed.h b/Engine/lib/zlib/inffixed.h new file mode 100644 index 000000000..75ed4b597 --- /dev/null +++ b/Engine/lib/zlib/inffixed.h @@ -0,0 +1,94 @@ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. It + is part of the implementation of the compression library and + is subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; diff --git a/Engine/lib/zlib/inflate.c b/Engine/lib/zlib/inflate.c new file mode 100644 index 000000000..792fdee8e --- /dev/null +++ b/Engine/lib/zlib/inflate.c @@ -0,0 +1,1368 @@ +/* inflate.c -- zlib decompression + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * Change history: + * + * 1.2.beta0 24 Nov 2002 + * - First version -- complete rewrite of inflate to simplify code, avoid + * creation of window when not needed, minimize use of window when it is + * needed, make inffast.c even faster, implement gzip decoding, and to + * improve code readability and style over the previous zlib inflate code + * + * 1.2.beta1 25 Nov 2002 + * - Use pointers for available input and output checking in inffast.c + * - Remove input and output counters in inffast.c + * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 + * - Remove unnecessary second byte pull from length extra in inffast.c + * - Unroll direct copy to three copies per loop in inffast.c + * + * 1.2.beta2 4 Dec 2002 + * - Change external routine names to reduce potential conflicts + * - Correct filename to inffixed.h for fixed tables in inflate.c + * - Make hbuf[] unsigned char to match parameter type in inflate.c + * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) + * to avoid negation problem on Alphas (64 bit) in inflate.c + * + * 1.2.beta3 22 Dec 2002 + * - Add comments on state->bits assertion in inffast.c + * - Add comments on op field in inftrees.h + * - Fix bug in reuse of allocated window after inflateReset() + * - Remove bit fields--back to byte structure for speed + * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths + * - Change post-increments to pre-increments in inflate_fast(), PPC biased? + * - Add compile time option, POSTINC, to use post-increments instead (Intel?) + * - Make MATCH copy in inflate() much faster for when inflate_fast() not used + * - Use local copies of stream next and avail values, as well as local bit + * buffer and bit count in inflate()--for speed when inflate_fast() not used + * + * 1.2.beta4 1 Jan 2003 + * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings + * - Move a comment on output buffer sizes from inffast.c to inflate.c + * - Add comments in inffast.c to introduce the inflate_fast() routine + * - Rearrange window copies in inflate_fast() for speed and simplification + * - Unroll last copy for window match in inflate_fast() + * - Use local copies of window variables in inflate_fast() for speed + * - Pull out common write == 0 case for speed in inflate_fast() + * - Make op and len in inflate_fast() unsigned for consistency + * - Add FAR to lcode and dcode declarations in inflate_fast() + * - Simplified bad distance check in inflate_fast() + * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new + * source file infback.c to provide a call-back interface to inflate for + * programs like gzip and unzip -- uses window as output buffer to avoid + * window copying + * + * 1.2.beta5 1 Jan 2003 + * - Improved inflateBack() interface to allow the caller to provide initial + * input in strm. + * - Fixed stored blocks bug in inflateBack() + * + * 1.2.beta6 4 Jan 2003 + * - Added comments in inffast.c on effectiveness of POSTINC + * - Typecasting all around to reduce compiler warnings + * - Changed loops from while (1) or do {} while (1) to for (;;), again to + * make compilers happy + * - Changed type of window in inflateBackInit() to unsigned char * + * + * 1.2.beta7 27 Jan 2003 + * - Changed many types to unsigned or unsigned short to avoid warnings + * - Added inflateCopy() function + * + * 1.2.0 9 Mar 2003 + * - Changed inflateBack() interface to provide separate opaque descriptors + * for the in() and out() functions + * - Changed inflateBack() argument and in_func typedef to swap the length + * and buffer address return values for the input function + * - Check next_in and next_out for Z_NULL on entry to inflate() + * + * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef MAKEFIXED +# ifndef BUILDFIXED +# define BUILDFIXED +# endif +#endif + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); +local int updatewindow OF((z_streamp strm, unsigned out)); +#ifdef BUILDFIXED + void makefixed OF((void)); +#endif +local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf, + unsigned len)); + +int ZEXPORT inflateReset(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + strm->total_in = strm->total_out = state->total = 0; + strm->msg = Z_NULL; + strm->adler = 1; /* to support ill-conceived Java test suite */ + state->mode = HEAD; + state->last = 0; + state->havedict = 0; + state->dmax = 32768U; + state->head = Z_NULL; + state->wsize = 0; + state->whave = 0; + state->write = 0; + state->hold = 0; + state->bits = 0; + state->lencode = state->distcode = state->next = state->codes; + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int ZEXPORT inflatePrime(strm, bits, value) +z_streamp strm; +int bits; +int value; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += value << state->bits; + state->bits += bits; + return Z_OK; +} + +int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) +z_streamp strm; +int windowBits; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL) return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *) + ZALLOC(strm, 1, sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + if (windowBits < 0) { + state->wrap = 0; + windowBits = -windowBits; + } + else { + state->wrap = (windowBits >> 4) + 1; +#ifdef GUNZIP + if (windowBits < 48) windowBits &= 15; +#endif + } + if (windowBits < 8 || windowBits > 15) { + ZFREE(strm, state); + strm->state = Z_NULL; + return Z_STREAM_ERROR; + } + state->wbits = (unsigned)windowBits; + state->window = Z_NULL; + return inflateReset(strm); +} + +int ZEXPORT inflateInit_(strm, version, stream_size) +z_streamp strm; +const char *version; +int stream_size; +{ + return inflateInit2_(strm, DEF_WBITS, version, stream_size); +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +#ifdef MAKEFIXED +#include + +/* + Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also + defines BUILDFIXED, so the tables are built on the fly. makefixed() writes + those tables to stdout, which would be piped to inffixed.h. A small program + can simply call makefixed to do this: + + void makefixed(void); + + int main(void) + { + makefixed(); + return 0; + } + + Then that can be linked with zlib built with MAKEFIXED defined and run: + + a.out > inffixed.h + */ +void makefixed() +{ + unsigned low, size; + struct inflate_state state; + + fixedtables(&state); + puts(" /* inffixed.h -- table for decoding fixed codes"); + puts(" * Generated automatically by makefixed()."); + puts(" */"); + puts(""); + puts(" /* WARNING: this file should *not* be used by applications."); + puts(" It is part of the implementation of this library and is"); + puts(" subject to change. Applications should only use zlib.h."); + puts(" */"); + puts(""); + size = 1U << 9; + printf(" static const code lenfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 7) == 0) printf("\n "); + printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits, + state.lencode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); + size = 1U << 5; + printf("\n static const code distfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 6) == 0) printf("\n "); + printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, + state.distcode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); +} +#endif /* MAKEFIXED */ + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +local int updatewindow(strm, out) +z_streamp strm; +unsigned out; +{ + struct inflate_state FAR *state; + unsigned copy, dist; + + state = (struct inflate_state FAR *)strm->state; + + /* if it hasn't been done already, allocate space for the window */ + if (state->window == Z_NULL) { + state->window = (unsigned char FAR *) + ZALLOC(strm, 1U << state->wbits, + sizeof(unsigned char)); + if (state->window == Z_NULL) return 1; + } + + /* if window not in use yet, initialize */ + if (state->wsize == 0) { + state->wsize = 1U << state->wbits; + state->write = 0; + state->whave = 0; + } + + /* copy state->wsize or less output bytes into the circular window */ + copy = out - strm->avail_out; + if (copy >= state->wsize) { + zmemcpy(state->window, strm->next_out - state->wsize, state->wsize); + state->write = 0; + state->whave = state->wsize; + } + else { + dist = state->wsize - state->write; + if (dist > copy) dist = copy; + zmemcpy(state->window + state->write, strm->next_out - copy, dist); + copy -= dist; + if (copy) { + zmemcpy(state->window, strm->next_out - copy, copy); + state->write = copy; + state->whave = state->wsize; + } + else { + state->write += dist; + if (state->write == state->wsize) state->write = 0; + if (state->whave < state->wsize) state->whave += dist; + } + } + return 0; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#ifdef GUNZIP +# define UPDATE(check, buf, len) \ + (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) +#else +# define UPDATE(check, buf, len) adler32(check, buf, len) +#endif + +/* check macros for header crc */ +#ifdef GUNZIP +# define CRC2(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + check = crc32(check, hbuf, 2); \ + } while (0) + +# define CRC4(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + hbuf[2] = (unsigned char)((word) >> 16); \ + hbuf[3] = (unsigned char)((word) >> 24); \ + check = crc32(check, hbuf, 4); \ + } while (0) +#endif + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() + if there is no input available. */ +#define PULLBYTE() \ + do { \ + if (have == 0) goto inf_leave; \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Reverse the bytes in a 32-bit value */ +#define REVERSE(q) \ + ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +/* + inflate() uses a state machine to process as much input data and generate as + much output data as possible before returning. The state machine is + structured roughly as follows: + + for (;;) switch (state) { + ... + case STATEn: + if (not enough input data or output space to make progress) + return; + ... make progress ... + state = STATEm; + break; + ... + } + + so when inflate() is called again, the same case is attempted again, and + if the appropriate resources are provided, the machine proceeds to the + next state. The NEEDBITS() macro is usually the way the state evaluates + whether it can proceed or should return. NEEDBITS() does the return if + the requested bits are not available. The typical use of the BITS macros + is: + + NEEDBITS(n); + ... do something with BITS(n) ... + DROPBITS(n); + + where NEEDBITS(n) either returns from inflate() if there isn't enough + input left to load n bits into the accumulator, or it continues. BITS(n) + gives the low n bits in the accumulator. When done, DROPBITS(n) drops + the low n bits off the accumulator. INITBITS() clears the accumulator + and sets the number of available bits to zero. BYTEBITS() discards just + enough bits to put the accumulator on a byte boundary. After BYTEBITS() + and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + + NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return + if there is no input available. The decoding of variable length codes uses + PULLBYTE() directly in order to pull just enough bytes to decode the next + code, and no more. + + Some states loop until they get enough input, making sure that enough + state information is maintained to continue the loop where it left off + if NEEDBITS() returns in the loop. For example, want, need, and keep + would all have to actually be part of the saved state in case NEEDBITS() + returns: + + case STATEw: + while (want < need) { + NEEDBITS(n); + keep[want++] = BITS(n); + DROPBITS(n); + } + state = STATEx; + case STATEx: + + As shown above, if the next state is also the next case, then the break + is omitted. + + A state may also return if there is not enough output space available to + complete that state. Those states are copying stored data, writing a + literal byte, and copying a matching string. + + When returning, a "goto inf_leave" is used to update the total counters, + update the check value, and determine whether any progress has been made + during that inflate() call in order to return the proper return code. + Progress is defined as a change in either strm->avail_in or strm->avail_out. + When there is a window, goto inf_leave will update the window with the last + output written. If a goto inf_leave occurs in the middle of decompression + and there is no window currently, goto inf_leave will create one and copy + output to the window for the next call of inflate(). + + In this implementation, the flush parameter of inflate() only affects the + return code (per zlib.h). inflate() always writes as much as possible to + strm->next_out, given the space available and the provided input--the effect + documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers + the allocation of and copying into a sliding window until necessary, which + provides the effect documented in zlib.h for Z_FINISH when the entire input + stream available. So the only thing the flush parameter actually does is: + when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it + will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int ZEXPORT inflate(strm, flush) +z_streamp strm; +int flush; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned in, out; /* save starting available input and output */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ +#ifdef GUNZIP + unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ +#endif + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0)) + return Z_STREAM_ERROR; + + state = (struct inflate_state FAR *)strm->state; + if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ + LOAD(); + in = have; + out = left; + ret = Z_OK; + for (;;) + switch (state->mode) { + case HEAD: + if (state->wrap == 0) { + state->mode = TYPEDO; + break; + } + NEEDBITS(16); +#ifdef GUNZIP + if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ + state->check = crc32(0L, Z_NULL, 0); + CRC2(state->check, hold); + INITBITS(); + state->mode = FLAGS; + break; + } + state->flags = 0; /* expect zlib header */ + if (state->head != Z_NULL) + state->head->done = -1; + if (!(state->wrap & 1) || /* check if zlib header allowed */ +#else + if ( +#endif + ((BITS(8) << 8) + (hold >> 8)) % 31) { + strm->msg = (char *)"incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + len = BITS(4) + 8; + if (len > state->wbits) { + strm->msg = (char *)"invalid window size"; + state->mode = BAD; + break; + } + state->dmax = 1U << len; + Tracev((stderr, "inflate: zlib header ok\n")); + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = hold & 0x200 ? DICTID : TYPE; + INITBITS(); + break; +#ifdef GUNZIP + case FLAGS: + NEEDBITS(16); + state->flags = (int)(hold); + if ((state->flags & 0xff) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + if (state->flags & 0xe000) { + strm->msg = (char *)"unknown header flags set"; + state->mode = BAD; + break; + } + if (state->head != Z_NULL) + state->head->text = (int)((hold >> 8) & 1); + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = TIME; + case TIME: + NEEDBITS(32); + if (state->head != Z_NULL) + state->head->time = hold; + if (state->flags & 0x0200) CRC4(state->check, hold); + INITBITS(); + state->mode = OS; + case OS: + NEEDBITS(16); + if (state->head != Z_NULL) { + state->head->xflags = (int)(hold & 0xff); + state->head->os = (int)(hold >> 8); + } + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = EXLEN; + case EXLEN: + if (state->flags & 0x0400) { + NEEDBITS(16); + state->length = (unsigned)(hold); + if (state->head != Z_NULL) + state->head->extra_len = (unsigned)hold; + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + } + else if (state->head != Z_NULL) + state->head->extra = Z_NULL; + state->mode = EXTRA; + case EXTRA: + if (state->flags & 0x0400) { + copy = state->length; + if (copy > have) copy = have; + if (copy) { + if (state->head != Z_NULL && + state->head->extra != Z_NULL) { + len = state->head->extra_len - state->length; + zmemcpy(state->head->extra + len, next, + len + copy > state->head->extra_max ? + state->head->extra_max - len : copy); + } + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + state->length -= copy; + } + if (state->length) goto inf_leave; + } + state->length = 0; + state->mode = NAME; + case NAME: + if (state->flags & 0x0800) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->name != Z_NULL && + state->length < state->head->name_max) + state->head->name[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->name = Z_NULL; + state->length = 0; + state->mode = COMMENT; + case COMMENT: + if (state->flags & 0x1000) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->comment != Z_NULL && + state->length < state->head->comm_max) + state->head->comment[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->comment = Z_NULL; + state->mode = HCRC; + case HCRC: + if (state->flags & 0x0200) { + NEEDBITS(16); + if (hold != (state->check & 0xffff)) { + strm->msg = (char *)"header crc mismatch"; + state->mode = BAD; + break; + } + INITBITS(); + } + if (state->head != Z_NULL) { + state->head->hcrc = (int)((state->flags >> 9) & 1); + state->head->done = 1; + } + strm->adler = state->check = crc32(0L, Z_NULL, 0); + state->mode = TYPE; + break; +#endif + case DICTID: + NEEDBITS(32); + strm->adler = state->check = REVERSE(hold); + INITBITS(); + state->mode = DICT; + case DICT: + if (state->havedict == 0) { + RESTORE(); + return Z_NEED_DICT; + } + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = TYPE; + case TYPE: + if (flush == Z_BLOCK) goto inf_leave; + case TYPEDO: + if (state->last) { + BYTEBITS(); + state->mode = CHECK; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + case STORED: + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + state->mode = COPY; + case COPY: + copy = state->length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy == 0) goto inf_leave; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + break; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + case TABLE: + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + state->have = 0; + state->mode = LENLENS; + case LENLENS: + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + state->have = 0; + state->mode = CODELENS; + case CODELENS: + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = state->lens[state->have - 1]; + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + RESTORE(); + inflate_fast(strm, out); + LOAD(); + break; + } + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + if ((int)(this.op) == 0) { + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + state->mode = LIT; + break; + } + if (this.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + state->extra = (unsigned)(this.op) & 15; + state->mode = LENEXT; + case LENEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + state->mode = DIST; + case DIST: + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + state->extra = (unsigned)(this.op) & 15; + state->mode = DISTEXT; + case DISTEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } +#ifdef INFLATE_STRICT + if (state->offset > state->dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + if (state->offset > state->whave + out - left) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + state->mode = MATCH; + case MATCH: + if (left == 0) goto inf_leave; + copy = out - left; + if (state->offset > copy) { /* copy from window */ + copy = state->offset - copy; + if (copy > state->write) { + copy -= state->write; + from = state->window + (state->wsize - copy); + } + else + from = state->window + (state->write - copy); + if (copy > state->length) copy = state->length; + } + else { /* copy from output */ + from = put - state->offset; + copy = state->length; + } + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = *from++; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; + case LIT: + if (left == 0) goto inf_leave; + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + case CHECK: + if (state->wrap) { + NEEDBITS(32); + out -= left; + strm->total_out += out; + state->total += out; + if (out) + strm->adler = state->check = + UPDATE(state->check, put - out, out); + out = left; + if (( +#ifdef GUNZIP + state->flags ? hold : +#endif + REVERSE(hold)) != state->check) { + strm->msg = (char *)"incorrect data check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: check matches trailer\n")); + } +#ifdef GUNZIP + state->mode = LENGTH; + case LENGTH: + if (state->wrap && state->flags) { + NEEDBITS(32); + if (hold != (state->total & 0xffffffffUL)) { + strm->msg = (char *)"incorrect length check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: length matches trailer\n")); + } +#endif + state->mode = DONE; + case DONE: + ret = Z_STREAM_END; + goto inf_leave; + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + default: + return Z_STREAM_ERROR; + } + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + inf_leave: + RESTORE(); + if (state->wsize || (state->mode < CHECK && out != strm->avail_out)) + if (updatewindow(strm, out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + in -= strm->avail_in; + out -= strm->avail_out; + strm->total_in += in; + strm->total_out += out; + state->total += out; + if (state->wrap && out) + strm->adler = state->check = + UPDATE(state->check, strm->next_out - out, out); + strm->data_type = state->bits + (state->last ? 64 : 0) + + (state->mode == TYPE ? 128 : 0); + if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) + ret = Z_BUF_ERROR; + return ret; +} + +int ZEXPORT inflateEnd(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->window != Z_NULL) ZFREE(strm, state->window); + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + +int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) +z_streamp strm; +const Bytef *dictionary; +uInt dictLength; +{ + struct inflate_state FAR *state; + unsigned long id; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->wrap != 0 && state->mode != DICT) + return Z_STREAM_ERROR; + + /* check for correct dictionary id */ + if (state->mode == DICT) { + id = adler32(0L, Z_NULL, 0); + id = adler32(id, dictionary, dictLength); + if (id != state->check) + return Z_DATA_ERROR; + } + + /* copy dictionary to window */ + if (updatewindow(strm, strm->avail_out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + if (dictLength > state->wsize) { + zmemcpy(state->window, dictionary + dictLength - state->wsize, + state->wsize); + state->whave = state->wsize; + } + else { + zmemcpy(state->window + state->wsize - dictLength, dictionary, + dictLength); + state->whave = dictLength; + } + state->havedict = 1; + Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; +} + +int ZEXPORT inflateGetHeader(strm, head) +z_streamp strm; +gz_headerp head; +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; + + /* save header structure */ + state->head = head; + head->done = 0; + return Z_OK; +} + +/* + Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found + or when out of input. When called, *have is the number of pattern bytes + found in order so far, in 0..3. On return *have is updated to the new + state. If on return *have equals four, then the pattern was found and the + return value is how many bytes were read including the last byte of the + pattern. If *have is less than four, then the pattern has not been found + yet and the return value is len. In the latter case, syncsearch() can be + called again with more data and the *have state. *have is initialized to + zero for the first call. + */ +local unsigned syncsearch(have, buf, len) +unsigned FAR *have; +unsigned char FAR *buf; +unsigned len; +{ + unsigned got; + unsigned next; + + got = *have; + next = 0; + while (next < len && got < 4) { + if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) + got++; + else if (buf[next]) + got = 0; + else + got = 4 - got; + next++; + } + *have = got; + return next; +} + +int ZEXPORT inflateSync(strm) +z_streamp strm; +{ + unsigned len; /* number of bytes to look at or looked at */ + unsigned long in, out; /* temporary to save total_in and total_out */ + unsigned char buf[4]; /* to restore bit buffer to byte string */ + struct inflate_state FAR *state; + + /* check parameters */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + + /* if first time, start search in bit buffer */ + if (state->mode != SYNC) { + state->mode = SYNC; + state->hold <<= state->bits & 7; + state->bits -= state->bits & 7; + len = 0; + while (state->bits >= 8) { + buf[len++] = (unsigned char)(state->hold); + state->hold >>= 8; + state->bits -= 8; + } + state->have = 0; + syncsearch(&(state->have), buf, len); + } + + /* search available input */ + len = syncsearch(&(state->have), strm->next_in, strm->avail_in); + strm->avail_in -= len; + strm->next_in += len; + strm->total_in += len; + + /* return no joy or set up to restart inflate() on a new block */ + if (state->have != 4) return Z_DATA_ERROR; + in = strm->total_in; out = strm->total_out; + inflateReset(strm); + strm->total_in = in; strm->total_out = out; + state->mode = TYPE; + return Z_OK; +} + +/* + Returns true if inflate is currently at the end of a block generated by + Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses + Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored + block. When decompressing, PPP checks that at the end of input packet, + inflate is waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + return state->mode == STORED && state->bits == 0; +} + +int ZEXPORT inflateCopy(dest, source) +z_streamp dest; +z_streamp source; +{ + struct inflate_state FAR *state; + struct inflate_state FAR *copy; + unsigned char FAR *window; + unsigned wsize; + + /* check input */ + if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || + source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)source->state; + + /* allocate space */ + copy = (struct inflate_state FAR *) + ZALLOC(source, 1, sizeof(struct inflate_state)); + if (copy == Z_NULL) return Z_MEM_ERROR; + window = Z_NULL; + if (state->window != Z_NULL) { + window = (unsigned char FAR *) + ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); + if (window == Z_NULL) { + ZFREE(source, copy); + return Z_MEM_ERROR; + } + } + + /* copy state */ + zmemcpy(dest, source, sizeof(z_stream)); + zmemcpy(copy, state, sizeof(struct inflate_state)); + if (state->lencode >= state->codes && + state->lencode <= state->codes + ENOUGH - 1) { + copy->lencode = copy->codes + (state->lencode - state->codes); + copy->distcode = copy->codes + (state->distcode - state->codes); + } + copy->next = copy->codes + (state->next - state->codes); + if (window != Z_NULL) { + wsize = 1U << state->wbits; + zmemcpy(window, state->window, wsize); + } + copy->window = window; + dest->state = (struct internal_state FAR *)copy; + return Z_OK; +} diff --git a/Engine/lib/zlib/inflate.h b/Engine/lib/zlib/inflate.h new file mode 100644 index 000000000..07bd3e78a --- /dev/null +++ b/Engine/lib/zlib/inflate.h @@ -0,0 +1,115 @@ +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer decoding by inflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip decoding + should be left enabled. */ +#ifndef NO_GZIP +# define GUNZIP +#endif + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD, /* i: waiting for magic header */ + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN, /* i: waiting for length/lit code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ + LENGTH, /* i: waiting for 32-bit length (gzip) */ + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to the BAD or MEM mode -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME + NAME -> COMMENT -> HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + Read deflate blocks: + TYPE -> STORED or TABLE or LEN or CHECK + STORED -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN + Read deflate codes: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* state maintained between inflate() calls. Approximately 7K bytes. */ +struct inflate_state { + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ + unsigned long check; /* protected copy of check value */ + unsigned long total; /* protected copy of output count */ + gz_headerp head; /* where to save gzip header information */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned long hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const FAR *lencode; /* starting table for length/literal codes */ + code const FAR *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code FAR *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ +}; diff --git a/Engine/lib/zlib/inftrees.c b/Engine/lib/zlib/inftrees.c new file mode 100644 index 000000000..8a9c13ff0 --- /dev/null +++ b/Engine/lib/zlib/inftrees.c @@ -0,0 +1,329 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#define MAXBITS 15 + +const char inflate_copyright[] = + " inflate 1.2.3 Copyright 1995-2005 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. + */ +int inflate_table(type, lens, codes, table, bits, work) +codetype type; +unsigned short FAR *lens; +unsigned codes; +code FAR * FAR *table; +unsigned FAR *bits; +unsigned short FAR *work; +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code this; /* table entry for duplication */ + code FAR *next; /* next available space in table */ + const unsigned short FAR *base; /* base value table to use */ + const unsigned short FAR *extra; /* extra bits table to use */ + int end; /* use base and extra for symbol > end */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const unsigned short lext[31] = { /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 201, 196}; + static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0}; + static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) { /* no symbols to code at all */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)1; + this.val = (unsigned short)0; + *(*table)++ = this; /* make a table to force an error */ + *(*table)++ = this; + *bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min <= MAXBITS; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || max != 1)) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked when a LENS table is being made + against the space in *table, ENOUGH, minus the maximum space needed by + the worst case distance code, MAXD. This should never happen, but the + sufficiency of ENOUGH has not been proven exhaustively, hence the check. + This assumes that when type == LENS, bits == 9. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + end = 19; + break; + case LENS: + base = lbase; + base -= 257; + extra = lext; + extra -= 257; + end = 256; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + this.bits = (unsigned char)(len - drop); + if ((int)(work[sym]) < end) { + this.op = (unsigned char)0; + this.val = work[sym]; + } + else if ((int)(work[sym]) > end) { + this.op = (unsigned char)(extra[work[sym]]); + this.val = base[work[sym]]; + } + else { + this.op = (unsigned char)(32 + 64); /* end of block */ + this.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + next[(huff >> drop) + fill] = this; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } + } + + /* + Fill in rest of table for incomplete codes. This loop is similar to the + loop above in incrementing huff for table indices. It is assumed that + len is equal to curr + drop, so there is no loop needed to increment + through high index bits. When the current sub-table is filled, the loop + drops back to the root table to fill in any remaining entries there. + */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)(len - drop); + this.val = (unsigned short)0; + while (huff != 0) { + /* when done with sub-table, drop back to root table */ + if (drop != 0 && (huff & mask) != low) { + drop = 0; + len = root; + next = *table; + this.bits = (unsigned char)len; + } + + /* put invalid code marker in table */ + next[huff >> drop] = this; + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; +} diff --git a/Engine/lib/zlib/inftrees.h b/Engine/lib/zlib/inftrees.h new file mode 100644 index 000000000..b1104c87e --- /dev/null +++ b/Engine/lib/zlib/inftrees.h @@ -0,0 +1,55 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of dynamic tree. The maximum found in a long but non- + exhaustive search was 1444 code structures (852 for length/literals + and 592 for distances, the latter actually the result of an + exhaustive search). The true maximum is not known, but the value + below is more than safe. */ +#define ENOUGH 2048 +#define MAXD 592 + +/* Type of code to build for inftable() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +extern int inflate_table OF((codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work)); diff --git a/Engine/lib/zlib/trees.c b/Engine/lib/zlib/trees.c new file mode 100644 index 000000000..395e4e168 --- /dev/null +++ b/Engine/lib/zlib/trees.c @@ -0,0 +1,1219 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-2005 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* @(#) $Id$ */ + +/* #define GEN_TREES_H */ + +#include "deflate.h" + +#ifdef DEBUG +# include +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else +# include "trees.h" +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { + const ct_data *static_tree; /* static tree or NULL */ + const intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, ct_data *ltree, + ct_data *dtree)); +local void set_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef DEBUG +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(s, c, tree) \ + { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (value << s->bi_valid); + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (val << s->bi_valid);\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG */ + + +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + _dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; + +# ifdef GEN_TREES_H + gen_trees_header(); +# endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +# ifndef DEBUG +# include +# endif + +# define SEPARATOR(i, last, width) \ + ((i) == (last)? "\n};\n\n" : \ + ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ + FILE *header = fopen("trees.h", "w"); + int i; + + Assert (header != NULL, "Can't open trees.h"); + fprintf(header, + "/* header created automatically with -DGEN_TREES_H */\n\n"); + + fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); + for (i = 0; i < L_CODES+2; i++) { + fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + } + + fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + } + + fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n"); + for (i = 0; i < DIST_CODE_LEN; i++) { + fprintf(header, "%2u%s", _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + } + + fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { + fprintf(header, "%2u%s", _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + } + + fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); + for (i = 0; i < LENGTH_CODES; i++) { + fprintf(header, "%1u%s", base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + } + + fprintf(header, "local const int base_dist[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "%5u%s", base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + } + + fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void _tr_init(s) + deflate_state *s; +{ + tr_static_init(); + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; + s->last_eob_len = 8; /* enough lookahead for inflate */ +#ifdef DEBUG + s->compressed_len = 0L; + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + const ct_data *stree = desc->stat_desc->static_tree; + const intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if ((unsigned) tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ushf *bl_count; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<dyn_tree; + const ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? + s->depth[n] : s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void _tr_stored_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */ +#ifdef DEBUG + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; +#endif + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + * The current inflate code requires 9 bits of lookahead. If the + * last two codes for the previous block (real code plus EOB) were coded + * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + * the last real code. In this case we send two empty static blocks instead + * of one. (There are no problems if the previous block is stored or fixed.) + * To simplify the code, we assume the worst case of last real code encoded + * on one bit only. + */ +void _tr_align(s) + deflate_state *s; +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif + bi_flush(s); + /* Of the 10 bits for the empty block, we have already sent + * (10 - bi_valid) bits. The lookahead for the last real code (before + * the EOB of the previous block) was thus at least one plus the length + * of the EOB plus what we have just sent of the empty static block. + */ + if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; +#endif + bi_flush(s); + } + s->last_eob_len = 7; +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +void _tr_flush_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is binary or text */ + if (stored_len > 0 && s->strm->data_type == Z_UNKNOWN) + set_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, eof); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+eof, 3); + compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->static_len; +#endif + } else { + send_bits(s, (DYN_TREES<<1)+eof, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->opt_len; +#endif + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (eof) { + bi_windup(s); +#ifdef DEBUG + s->compressed_len += 7; /* align on byte boundary */ +#endif + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*eof)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int _tr_tally (s, dist, lc) + deflate_state *s; + unsigned dist; /* distance of matched string */ + unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + +#ifdef TRUNCATE_BLOCK + /* Try to guess if it is profitable to stop the current block here */ + if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } +#endif + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) + deflate_state *s; + ct_data *ltree; /* literal tree */ + ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); + s->last_eob_len = ltree[END_BLOCK].Len; +} + +/* =========================================================================== + * Set the data type to BINARY or TEXT, using a crude approximation: + * set it to Z_TEXT if all symbols are either printable characters (33 to 255) + * or white spaces (9 to 13, or 32); or set it to Z_BINARY otherwise. + * IN assertion: the fields Freq of dyn_ltree are set. + */ +local void set_data_type(s) + deflate_state *s; +{ + int n; + + for (n = 0; n < 9; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + if (n == 9) + for (n = 14; n < 32; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + s->strm->data_type = (n == 32) ? Z_TEXT : Z_BINARY; +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) + deflate_state *s; +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(s, buf, len, header) + deflate_state *s; + charf *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + s->last_eob_len = 8; /* enough lookahead for inflate */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG + s->bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(s, *buf++); + } +} diff --git a/Engine/lib/zlib/trees.h b/Engine/lib/zlib/trees.h new file mode 100644 index 000000000..72facf900 --- /dev/null +++ b/Engine/lib/zlib/trees.h @@ -0,0 +1,128 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch _dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; + diff --git a/Engine/lib/zlib/uncompr.c b/Engine/lib/zlib/uncompr.c new file mode 100644 index 000000000..b59e3d0de --- /dev/null +++ b/Engine/lib/zlib/uncompr.c @@ -0,0 +1,61 @@ +/* uncompr.c -- decompress a memory buffer + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted. +*/ +int ZEXPORT uncompress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; + + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + + err = inflateInit(&stream); + if (err != Z_OK) return err; + + err = inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + inflateEnd(&stream); + if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) + return Z_DATA_ERROR; + return err; + } + *destLen = stream.total_out; + + err = inflateEnd(&stream); + return err; +} diff --git a/Engine/lib/zlib/zconf.h b/Engine/lib/zlib/zconf.h new file mode 100644 index 000000000..03a9431c8 --- /dev/null +++ b/Engine/lib/zlib/zconf.h @@ -0,0 +1,332 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflateParams z_deflateParams +# define deflateBound z_deflateBound +# define deflatePrime z_deflatePrime +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateCopy z_inflateCopy +# define inflateReset z_inflateReset +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table +# define zError z_zError + +# define alloc_func z_alloc_func +# define free_func z_free_func +# define in_func z_in_func +# define out_func z_out_func +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ +# include /* for off_t */ +# include /* for SEEK_* and off_t */ +# ifdef VMS +# include /* for off_t */ +# endif +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +# ifdef FAR +# undef FAR +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(deflateBound,"DEBND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(compressBound,"CMBND") +# pragma map(inflate_table,"INTABL") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/Engine/lib/zlib/zlib.h b/Engine/lib/zlib/zlib.h new file mode 100644 index 000000000..022817927 --- /dev/null +++ b/Engine/lib/zlib/zlib.h @@ -0,0 +1,1357 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.3, July 18th, 2005 + + Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler + + 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. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.3" +#define ZLIB_VERNUM 0x1230 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms will be added later and will have the same + stream interface. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never + crash even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce some + output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumualte before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In particular + avail_in is zero after the call if enough output space has been provided + before the call.) Flushing may degrade compression for some compression + algorithms and so it should be used only when necessary. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + the value returned by deflateBound (see below). If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the exact + value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller. msg is set to null if there is no error + message. inflateInit does not perform any decompression apart from reading + the zlib header if present: this will be done by inflate(). (So next_in and + avail_in may be modified, but next_out and avail_out are unchanged.) +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, + Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() stop + if and when it gets to the next deflate block boundary. When decoding the + zlib or gzip format, this will cause inflate() to return immediately after + the header and before the first block. When doing a raw inflate, inflate() + will go ahead and process the first block, and will return when it gets to + the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 + if inflate() is currently decoding the last block in the deflate stream, + plus 128 if inflate() returned immediately after decoding an end-of-block + code or decoding the complete header up to just before the first byte of the + deflate stream. The end-of-block will not be indicated until all of the + uncompressed data from that block has been written to strm->next_out. The + number of unused bits may in general be greater than seven, except when + bit 7 of data_type is set, in which case the number of unused bits will be + less than eight. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster approach + may be used for the single inflate() call. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the only effect of the flush parameter in this implementation + is on the return value of inflate(), as noted below, or when it returns early + because Z_BLOCK is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the adler32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the adler32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() will decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically. Any information + contained in the gzip header is not retained, so applications that need that + information should instead use raw inflate, see inflateInit2() below, or + inflateBack() and perform their own processing of the gzip header and + trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may then + call inflateSync() to look for a good compression block if a partial recovery + of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), + no header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as + Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy + parameter only affects the compression ratio but not the correctness of the + compressed output even if it is not set appropriately. Z_FIXED prevents the + use of dynamic Huffman codes, allowing for a simpler decoder for special + applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid + method). msg is set to null if there is no error message. deflateInit2 does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any + call of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size in + deflate or deflate2. Thus the strings most likely to be useful should be + put at the end of the dictionary, not at the front. In addition, the + current implementation of deflate will use at most the window size minus + 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different + strategy. If the compression level is changed, the input available so far + is compressed with the old level (and may be flushed); the new level will + take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() + or deflateInit2(). This would be used to allocate an output buffer + for deflation in a single pass, and so would be called before deflate(). +*/ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the + bits leftover from a previous deflate stream when appending to it. As such, + this function can only be used for raw deflate, and must be used before the + first deflate() call after a deflateInit2() or deflateReset(). bits must be + less than or equal to 16, and that many of the least significant bits of + value will be inserted in the output. + + deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is + a crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg + is set to null if there is no error message. inflateInit2 does not perform + any decompression apart from reading the zlib header if present: this will + be done by inflate(). (So next_in and avail_in may be modified, but next_out + and avail_out are unchanged.) +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called + immediately after inflateInit2() or inflateReset() and before any call of + inflate() to set the dictionary. The application must insure that the + dictionary that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK can be used to + force inflate() to return immediately after header processing is complete + and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When + any of extra, name, or comment are not Z_NULL and the respective field is + not present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the paramaters are invalid, Z_MEM_ERROR if the internal state could not + be allocated, or Z_VERSION_ERROR if the version of the library does not + match the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is more efficient than inflate() for + file i/o applications in that it avoids copying between the output and the + sliding window by simply making the window itself the output buffer. This + function trusts the application to not change the output buffer passed by + the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free + the allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects + only the raw deflate stream to decompress. This is different from the + normal behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format + error in the deflate stream (in which case strm->msg is set to indicate the + nature of the error), or Z_STREAM_ERROR if the stream was not properly + initialized. In the case of Z_BUF_ERROR, an input or output error can be + distinguished using strm->next_in which will be Z_NULL only if in() returned + an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to + out() returning non-zero. (in() will always be called before out(), so + strm->next_in is assured to be defined if out() returns non-zero.) Note + that inflateBack() cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the + basic stream-oriented functions. To simplify the interface, some + default options are assumed (compression level and memory usage, + standard memory allocation functions). The source code of these + utility functions can easily be modified if you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least the value returned + by compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + This function can be used to compress a whole file at once if the + input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before + a compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. +*/ + + +typedef voidp gzFile; + +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for + Huffman only compression as in "wb1h", or 'R' for run-length encoding + as in "wb1R". (See the description of deflateInit2 for more information + about the strategy parameter.) + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). */ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). The number of + uncompressed bytes written is limited to 4095. The caller should assure that + this limit is not exceeded. If it is exceeded, then gzprintf() will return + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or + a newline character is read and transferred to buf, or an end-of-file + condition is encountered. The string is then terminated with a null + character. + gzgets returns buf, or Z_NULL in case of error. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read again later. + Only one character of push-back is allowed. gzungetc() returns the + character pushed, or -1 on failure. gzungetc() will fail if a + character has been pushed but not read yet, or if c is -1. The pushed + character will be discarded if the stream is repositioned with gzseek() + or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); +/* + Sets the starting position for the next gzread or gzwrite on the + given compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); +/* + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns 1 if file is being read directly without decompression, otherwise + zero. +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the + compression library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); +/* + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is NULL, this function returns the required initial + value for the for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + +/* + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, sizeof(z_stream)) + + +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/Engine/lib/zlib/zutil.c b/Engine/lib/zlib/zutil.c new file mode 100644 index 000000000..d55f5948a --- /dev/null +++ b/Engine/lib/zlib/zutil.c @@ -0,0 +1,318 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +const char * const z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ + uLong flags; + + flags = 0; + switch (sizeof(uInt)) { + case 2: break; + case 4: flags += 1; break; + case 8: flags += 2; break; + default: flags += 3; + } + switch (sizeof(uLong)) { + case 2: break; + case 4: flags += 1 << 2; break; + case 8: flags += 2 << 2; break; + default: flags += 3 << 2; + } + switch (sizeof(voidpf)) { + case 2: break; + case 4: flags += 1 << 4; break; + case 8: flags += 2 << 4; break; + default: flags += 3 << 4; + } + switch (sizeof(z_off_t)) { + case 2: break; + case 4: flags += 1 << 6; break; + case 8: flags += 2 << 6; break; + default: flags += 3 << 6; + } +#ifdef DEBUG + flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) + flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI + flags += 1 << 10; +#endif +#ifdef BUILDFIXED + flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE + flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS + flags += 1L << 16; +#endif +#ifdef NO_GZIP + flags += 1L << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND + flags += 1L << 20; +#endif +#ifdef FASTEST + flags += 1L << 21; +#endif +#ifdef STDC +# ifdef NO_vsnprintf + flags += 1L << 25; +# ifdef HAS_vsprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_vsnprintf_void + flags += 1L << 26; +# endif +# endif +#else + flags += 1L << 24; +# ifdef NO_snprintf + flags += 1L << 25; +# ifdef HAS_sprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_snprintf_void + flags += 1L << 26; +# endif +# endif +#endif + return flags; +} + +#ifdef DEBUG + +# ifndef verbose +# define verbose 0 +# endif +int z_verbose = verbose; + +void z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) + int err; +{ + return ERR_MSG(err); +} + +#if defined(_WIN32_WCE) + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. + */ + int errno = 0; +#endif + +#ifndef HAVE_MEMCPY + +void zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + if (opaque) items += size - size; /* make compiler happy */ + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); +} + +void zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ diff --git a/Engine/lib/zlib/zutil.h b/Engine/lib/zlib/zutil.h new file mode 100644 index 000000000..b7d5eff81 --- /dev/null +++ b/Engine/lib/zlib/zutil.h @@ -0,0 +1,269 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#define ZLIB_INTERNAL +#include "zlib.h" + +#ifdef STDC +# ifndef _WIN32_WCE +# include +# endif +# include +# include +#endif +#ifdef NO_ERRNO_H +# ifdef _WIN32_WCE + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. We rename it to + * avoid conflict with other libraries that use the same workaround. + */ +# define errno z_errno +# endif + extern int errno; +#else +# ifndef _WIN32_WCE +# include +# endif +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +# ifdef M_I86 + #include +# endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifdef WIN32 +# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ +# define OS_CODE 0x0b +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS + /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 + /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# define vsnprintf _vsnprintf +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +#endif +#ifdef VMS +# define NO_vsnprintf +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + extern void zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include + extern int z_verbose; + extern void z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); +void zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* ZUTIL_H */ diff --git a/Engine/source/T3D/aiClient.cpp b/Engine/source/T3D/aiClient.cpp new file mode 100644 index 000000000..ee16964ae --- /dev/null +++ b/Engine/source/T3D/aiClient.cpp @@ -0,0 +1,582 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/aiClient.h" + +#include "math/mMatrix.h" +#include "T3D/shapeBase.h" +#include "T3D/player.h" +#include "T3D/gameBase/moveManager.h" +#include "console/consoleInternal.h" + + +IMPLEMENT_CONOBJECT( AIClient ); + +ConsoleDocClass( AIClient, + "@brief Simulated client driven by AI commands.\n\n" + + "This object is derived from the AIConnection class. It introduces its own Player object " + "to solidify the purpose of this class: Simulated client connecting as a player\n\n" + "To get more specific, if you want a strong alternative to AIPlayer (and wish to make use " + "of the AIConnection structure), consider AIClient. AIClient inherits from AIConnection, " + "contains quite a bit of functionality you will find in AIPlayer, and has its own Player " + "object.\n\n" + + "@note This is a legacy class, which you are discouraged from using as it will " + "most likely be deprecated in a future version. For now it has been left in for " + "backwards compatibility with TGE and the old RTS Kit. Use AIPlayer instead.\n\n" + + "@see AIPlayer, AIConnection\n\n" + + "@ingroup AI\n" + "@ingroup Networking\n" +); + +/** + * Constructor + */ +AIClient::AIClient() { + mMoveMode = ModeStop; + mMoveDestination.set( 0.0f, 0.0f, 0.0f ); + mAimLocation.set( 0.0f, 0.0f, 0.0f ); + mMoveSpeed = 1.0f; + mMoveTolerance = 0.25f; + + // Clear the triggers + for( int i = 0; i < MaxTriggerKeys; i++ ) + mTriggers[i] = false; + + mAimToDestination = true; + mTargetInLOS = false; + + mLocation.set( 0.0f, 0.0f, 0.0f ); + mPlayer = NULL; +} + +/** + * Destructor + */ +AIClient::~AIClient() { + // Blah +} + +/** + * Sets the object the bot is targeting + * + * @param targetObject The object to target + */ +void AIClient::setTargetObject( ShapeBase *targetObject ) { + if ( !targetObject || !bool( mTargetObject ) || targetObject->getId() != mTargetObject->getId() ) + mTargetInLOS = false; + + mTargetObject = targetObject; +} + +/** + * Returns the target object + * + * @return Object bot is targeting + */ +S32 AIClient::getTargetObject() const { + if( bool( mTargetObject ) ) + return mTargetObject->getId(); + else + return -1; +} + +/** + * Sets the speed at which this AI moves + * + * @param speed Speed to move, default player was 10 + */ +void AIClient::setMoveSpeed( F32 speed ) { + if( speed <= 0.0f ) + mMoveSpeed = 0.0f; + else + mMoveSpeed = getMin( 1.0f, speed ); +} + +/** + * Sets the movement mode for this AI + * + * @param mode Movement mode, see enum + */ +void AIClient::setMoveMode( S32 mode ) { + if( mode < 0 || mode >= ModeCount ) + mode = 0; + + if( mode != mMoveMode ) { + switch( mode ) { + case ModeStuck: + throwCallback( "onStuck" ); + break; + case ModeMove: + if( mMoveMode == ModeStuck ) + throwCallback( "onUnStuck" ); + else + throwCallback( "onMove" ); + break; + case ModeStop: + throwCallback( "onStop" ); + break; + } + } + + mMoveMode = mode; +} + +/** + * Sets how far away from the move location is considered + * "on target" + * + * @param tolerance Movement tolerance for error + */ +void AIClient::setMoveTolerance( const F32 tolerance ) { + mMoveTolerance = getMax( 0.1f, tolerance ); +} + +/** + * Sets the location for the bot to run to + * + * @param location Point to run to + */ +void AIClient::setMoveDestination( const Point3F &location ) { + // Ok, here's the story...we're going to aim where we are going UNLESS told otherwise + if( mAimToDestination ) { + mAimLocation = location; + mAimLocation.z = 0.0f; + } + + mMoveDestination = location; +} + +/** + * Sets the location for the bot to aim at + * + * @param location Point to aim at + */ +void AIClient::setAimLocation( const Point3F &location ) { + mAimLocation = location; + mAimToDestination = false; +} + +/** + * Clears the aim location and sets it to the bot's + * current destination so he looks where he's going + */ +void AIClient::clearAim() { + mAimLocation = Point3F( 0.0f, 0.0f, 0.0f ); + mAimToDestination = true; +} + +/** + * This method gets the move list for an object, in the case + * of the AI, it actually calculates the moves, and then + * sends them down the pipe. + * + * @param movePtr Pointer to move the move list into + * @param numMoves Number of moves in the move list + */ +U32 AIClient::getMoveList( Move **movePtr,U32 *numMoves ) { + //initialize the move structure and return pointers + mMove = NullMove; + *movePtr = &mMove; + *numMoves = 1; + + // Check if we got a player + mPlayer = NULL; + mPlayer = dynamic_cast( getControlObject() ); + + // We got a something controling us? + if( !mPlayer ) + return 1; + + + // What is The Matrix? + MatrixF moveMatrix; + moveMatrix.set( EulerF( 0, 0, 0 ) ); + moveMatrix.setColumn( 3, Point3F( 0, 0, 0 ) ); + moveMatrix.transpose(); + + // Position / rotation variables + F32 curYaw, curPitch; + F32 newYaw, newPitch; + F32 xDiff, yDiff; + + + F32 moveSpeed = mMoveSpeed; + + switch( mMoveMode ) { + + case ModeStop: + return 1; // Stop means no action + break; + + case ModeStuck: + // Fall through, so we still try to move + case ModeMove: + + // Get my location + MatrixF const& myTransform = mPlayer->getTransform(); + myTransform.getColumn( 3, &mLocation ); + + // Set rotation variables + Point3F rotation = mPlayer->getRotation(); + Point3F headRotation = mPlayer->getHeadRotation(); + curYaw = rotation.z; + curPitch = headRotation.x; + xDiff = mAimLocation.x - mLocation.x; + yDiff = mAimLocation.y - mLocation.y; + + // first do Yaw + if( !mIsZero( xDiff ) || !mIsZero( yDiff ) ) { + // use the cur yaw between -Pi and Pi + while( curYaw > M_2PI_F ) + curYaw -= M_2PI_F; + while( curYaw < -M_2PI_F ) + curYaw += M_2PI_F; + + // find the new yaw + newYaw = mAtan2( xDiff, yDiff ); + + // find the yaw diff + F32 yawDiff = newYaw - curYaw; + + // make it between 0 and 2PI + if( yawDiff < 0.0f ) + yawDiff += M_2PI_F; + else if( yawDiff >= M_2PI_F ) + yawDiff -= M_2PI_F; + + // now make sure we take the short way around the circle + if( yawDiff > M_2PI_F ) + yawDiff -= M_2PI_F; + else if( yawDiff < -M_2PI_F ) + yawDiff += M_2PI_F; + + mMove.yaw = yawDiff; + + // set up the movement matrix + moveMatrix.set( EulerF( 0.0f, 0.0f, newYaw ) ); + } + else + moveMatrix.set( EulerF( 0.0f, 0.0f, curYaw ) ); + + // next do pitch + F32 horzDist = Point2F( mAimLocation.x, mAimLocation.y ).len(); + + if( !mIsZero( horzDist ) ) { + //we shoot from the gun, not the eye... + F32 vertDist = mAimLocation.z; + + newPitch = mAtan2( horzDist, vertDist ) - ( M_2PI_F / 2.0f ); + + F32 pitchDiff = newPitch - curPitch; + mMove.pitch = pitchDiff; + } + + // finally, mMove towards mMoveDestination + xDiff = mMoveDestination.x - mLocation.x; + yDiff = mMoveDestination.y - mLocation.y; + + + // Check if we should mMove, or if we are 'close enough' + if( ( ( mFabs( xDiff ) > mMoveTolerance ) || + ( mFabs( yDiff ) > mMoveTolerance ) ) && ( !mIsZero( mMoveSpeed ) ) ) + { + if( mIsZero( xDiff ) ) + mMove.y = ( mLocation.y > mMoveDestination.y ? -moveSpeed : moveSpeed ); + else if( mIsZero( yDiff ) ) + mMove.x = ( mLocation.x > mMoveDestination.x ? -moveSpeed : moveSpeed ); + else if( mFabs( xDiff ) > mFabs( yDiff ) ) { + F32 value = mFabs( yDiff / xDiff ) * mMoveSpeed; + mMove.y = ( mLocation.y > mMoveDestination.y ? -value : value ); + mMove.x = ( mLocation.x > mMoveDestination.x ? -moveSpeed : moveSpeed ); + } + else { + F32 value = mFabs( xDiff / yDiff ) * mMoveSpeed; + mMove.x = ( mLocation.x > mMoveDestination.x ? -value : value ); + mMove.y = ( mLocation.y > mMoveDestination.y ? -moveSpeed : moveSpeed ); + } + + //now multiply the mMove vector by the transpose of the object rotation matrix + moveMatrix.transpose(); + Point3F newMove; + moveMatrix.mulP( Point3F( mMove.x, mMove.y, 0.0f ), &newMove ); + + //and sub the result back in the mMove structure + mMove.x = newMove.x; + mMove.y = newMove.y; + + // We should check to see if we are stuck... + if( mLocation.x == mLastLocation.x && + mLocation.y == mLastLocation.y && + mLocation.z == mLastLocation.z ) { + + // We're stuck...probably + setMoveMode( ModeStuck ); + } + else + setMoveMode( ModeMove ); + } + else { + // Ok, we are close enough, lets stop + + // setMoveMode( ModeStop ); // DON'T use this, it'll throw the wrong callback + mMoveMode = ModeStop; + throwCallback( "onReachDestination" ); // Callback + + } + break; + } + + // Test for target location in sight + RayInfo dummy; + Point3F targetLoc = mMoveDestination; // Change this + + if( mPlayer ) { + if( !mPlayer->getContainer()->castRay( mLocation, targetLoc, InteriorObjectType | + StaticShapeObjectType | StaticObjectType | + TerrainObjectType, &dummy ) ) { + if( !mTargetInLOS ) + throwCallback( "onTargetEnterLOS" ); + } + else { + if( mTargetInLOS ) + throwCallback( "onTargetExitLOS" ); + + } + } + + // Copy over the trigger status + for( int i = 0; i < MaxTriggerKeys; i++ ) { + mMove.trigger[i] = mTriggers[i]; + mTriggers[i] = false; + } + + return 1; +} + +/** + * This method is just called to stop the bots from running amuck + * while the mission cycles + */ +void AIClient::missionCycleCleanup() { + setMoveMode( ModeStop ); +} + + +/** + * Utility function to throw callbacks + */ +void AIClient::throwCallback( const char *name ) { + Con::executef( this, name ); +} + +/** + * What gets called when this gets created, different from constructor + */ +void AIClient::onAdd( const char *nameSpace ) { + + // This doesn't work... + // + if( dStrcmp( nameSpace, mNameSpace->mName ) ) { + Con::linkNamespaces( mNameSpace->mName, nameSpace ); + mNameSpace = Con::lookupNamespace( nameSpace ); + } + + throwCallback( "onAdd" ); +} + +// -------------------------------------------------------------------------------------------- +// Console Functions +// -------------------------------------------------------------------------------------------- + +/** + * Sets the move speed for an AI object + */ +ConsoleMethod( AIClient, setMoveSpeed, void, 3, 3, "ai.setMoveSpeed( float );" ) { + AIClient *ai = static_cast( object ); + ai->setMoveSpeed( dAtof( argv[2] ) ); +} + +/** + * Stops all AI movement, halt! + */ +ConsoleMethod( AIClient, stop, void, 2, 2, "ai.stop();" ) { + AIClient *ai = static_cast( object ); + ai->setMoveMode( AIClient::ModeStop ); +} + +/** + * Tells the AI to aim at the location provided + */ +ConsoleMethod( AIClient, setAimLocation, void, 3, 3, "ai.setAimLocation( x y z );" ) { + AIClient *ai = static_cast( object ); + Point3F v( 0.0f,0.0f,0.0f ); + dSscanf( argv[2], "%f %f %f", &v.x, &v.y, &v.z ); + + ai->setAimLocation( v ); +} + +/** + * Tells the AI to move to the location provided + */ +ConsoleMethod( AIClient, setMoveDestination, void, 3, 3, "ai.setMoveDestination( x y z );" ) { + AIClient *ai = static_cast( object ); + Point3F v( 0.0f, 0.0f, 0.0f ); + dSscanf( argv[2], "%f %f", &v.x, &v.y ); + + ai->setMoveDestination( v ); +} + +/** + * Returns the point the AI is aiming at + */ +ConsoleMethod( AIClient, getAimLocation, const char *, 2, 2, "ai.getAimLocation();" ) { + AIClient *ai = static_cast( object ); + Point3F aimPoint = ai->getAimLocation(); + + char *returnBuffer = Con::getReturnBuffer( 256 ); + dSprintf( returnBuffer, 256, "%f %f %f", aimPoint.x, aimPoint.y, aimPoint.z ); + + return returnBuffer; +} + +/** + * Returns the point the AI is set to move to + */ +ConsoleMethod( AIClient, getMoveDestination, const char *, 2, 2, "ai.getMoveDestination();" ) { + AIClient *ai = static_cast( object ); + Point3F movePoint = ai->getMoveDestination(); + + char *returnBuffer = Con::getReturnBuffer( 256 ); + dSprintf( returnBuffer, 256, "%f %f %f", movePoint.x, movePoint.y, movePoint.z ); + + return returnBuffer; +} + +/** + * Sets the bots target object + */ +ConsoleMethod( AIClient, setTargetObject, void, 3, 3, "ai.setTargetObject( obj );" ) { + AIClient *ai = static_cast( object ); + + // Find the target + ShapeBase *targetObject; + if( Sim::findObject( argv[2], targetObject ) ) + ai->setTargetObject( targetObject ); + else + ai->setTargetObject( NULL ); +} + +/** + * Gets the object the AI is targeting + */ +ConsoleMethod( AIClient, getTargetObject, S32, 2, 2, "ai.getTargetObject();" ) { + AIClient *ai = static_cast( object ); + + return ai->getTargetObject(); +} + +/** + * Tells the bot the mission is cycling + */ +ConsoleMethod( AIClient, missionCycleCleanup, void, 2, 2, "ai.missionCycleCleanup();" ) { + AIClient *ai = static_cast( object ); + ai->missionCycleCleanup(); +} + +/** + * Sets the AI to run mode + */ +ConsoleMethod( AIClient, move, void, 2, 2, "ai.move();" ) { + AIClient *ai = static_cast( object ); + ai->setMoveMode( AIClient::ModeMove ); +} + +/** + * Gets the AI's location in the world + */ +ConsoleMethod( AIClient, getLocation, const char *, 2, 2, "ai.getLocation();" ) { + AIClient *ai = static_cast( object ); + Point3F locPoint = ai->getLocation(); + + char *returnBuffer = Con::getReturnBuffer( 256 ); + dSprintf( returnBuffer, 256, "%f %f %f", locPoint.x, locPoint.y, locPoint.z ); + + return returnBuffer; +} + +/** + * Adds an AI Player to the game + */ +ConsoleFunction( aiAddPlayer, S32 , 2, 3, "aiAddPlayer( 'playerName'[, 'AIClassType'] );" ) { + // Create the player + AIClient *aiPlayer = new AIClient(); + aiPlayer->registerObject(); + aiPlayer->setGhostFrom(false); + aiPlayer->setGhostTo(false); + aiPlayer->setSendingEvents(false); + aiPlayer->setTranslatesStrings(true); + aiPlayer->setEstablished(); + + // Add the connection to the client group + SimGroup *g = Sim::getClientGroup(); + g->addObject( aiPlayer ); + + char *name = new char[ dStrlen( argv[1] ) + 1]; + char *ns = new char[ dStrlen( argv[2] ) + 1]; + + dStrcpy( name, argv[1] ); + dStrcpy( ns, argv[2] ); + + // Execute the connect console function, this is the same + // onConnect function invoked for normal client connections + Con::executef( aiPlayer, "onConnect", name ); + + // Now execute the onAdd command and feed it the namespace + if( argc > 2 ) + aiPlayer->onAdd( ns ); + else + aiPlayer->onAdd( "AIClient" ); + + return aiPlayer->getId(); +} + + +/** + * Tells the AI to move forward 100 units...TEST FXN + */ +ConsoleMethod( AIClient, moveForward, void, 2, 2, "ai.moveForward();" ) { + + AIClient *ai = static_cast( object ); + ShapeBase *player = dynamic_cast(ai->getControlObject()); + Point3F location; + MatrixF const &myTransform = player->getTransform(); + myTransform.getColumn( 3, &location ); + + location.y += 100.0f; + + ai->setMoveDestination( location ); +} // *** /TEST FXN diff --git a/Engine/source/T3D/aiClient.h b/Engine/source/T3D/aiClient.h new file mode 100644 index 000000000..37d490a74 --- /dev/null +++ b/Engine/source/T3D/aiClient.h @@ -0,0 +1,114 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _AICLIENT_H_ +#define _AICLIENT_H_ + +#ifndef _AICONNECTION_H_ +#include "T3D/aiConnection.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +class ShapeBase; +class Player; + +class AIClient : public AIConnection { + + typedef AIConnection Parent; + + private: + enum { + FireTrigger = 0, + JumpTrigger = 2, + JetTrigger = 3, + GrenadeTrigger = 4, + MineTrigger = 5 + }; + + F32 mMoveSpeed; + S32 mMoveMode; + F32 mMoveTolerance; // How close to the destination before we stop + + bool mTriggers[MaxTriggerKeys]; + + Player *mPlayer; + + Point3F mMoveDestination; + Point3F mLocation; + Point3F mLastLocation; // For stuck check + + bool mAimToDestination; + Point3F mAimLocation; + bool mTargetInLOS; + + SimObjectPtr mTargetObject; + + // Utility Methods + void throwCallback( const char *name ); + public: + + DECLARE_CONOBJECT( AIClient ); + + enum { + ModeStop = 0, + ModeMove, + ModeStuck, + ModeCount // This is in there as a max index value + }; + + AIClient(); + ~AIClient(); + + U32 getMoveList( Move **movePtr,U32 *numMoves ); + + // ---Targeting and aiming sets/gets + void setTargetObject( ShapeBase *targetObject ); + S32 getTargetObject() const; + + // ---Movement sets/gets + void setMoveSpeed( const F32 speed ); + F32 getMoveSpeed() const { return mMoveSpeed; } + + void setMoveMode( S32 mode ); + S32 getMoveMode() const { return mMoveMode; } + + void setMoveTolerance( const F32 tolerance ); + F32 getMoveTolerance() const { return mMoveTolerance; } + + void setMoveDestination( const Point3F &location ); + Point3F getMoveDestination() const { return mMoveDestination; } + + Point3F getLocation() const { return mLocation; } + + // ---Facing(Aiming) sets/gets + void setAimLocation( const Point3F &location ); + Point3F getAimLocation() const { return mAimLocation; } + void clearAim(); + + // ---Other + void missionCycleCleanup(); + void onAdd( const char *nameSpace ); +}; + +#endif diff --git a/Engine/source/T3D/aiConnection.cpp b/Engine/source/T3D/aiConnection.cpp new file mode 100644 index 000000000..c025b371d --- /dev/null +++ b/Engine/source/T3D/aiConnection.cpp @@ -0,0 +1,258 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "T3D/aiConnection.h" + +IMPLEMENT_CONOBJECT( AIConnection ); + +ConsoleDocClass( AIConnection, + "@brief Special client connection driven by an AI, rather than a human.\n\n" + + "Unlike other net connections, AIConnection is intended to run unmanned. Rather than " + "gathering input from a human using a device, move events, triggers, and look events " + "are driven through functions like AIConnection::setMove.\n\n" + + "In addition to having its own set of functions for managing client move events, " + "a member variable inherited by GameConnection is toggle: mAIControlled. This is useful " + "for a server to determine if a connection is AI driven via the function GameConnection::isAIControlled\n\n" + + "AIConnection is an alternative to manually creating an AI driven game object. When you " + "want the server to manage AI, you will create a specific one from script using a " + "class like AIPlayer. If you do not want the server managing the AI and wish to simulate " + "a complete client connection, you will use AIConnection\n\n." + + "To get more specific, if you want a strong alternative to AIPlayer (and wish to make use " + "of the AIConnection structure), consider AIClient. AIClient inherits from AIConnection, " + "contains quite a bit of functionality you will find in AIPlayer, and has its own Player " + "object.\n\n" + + "@tsexample\n" + "// Create a new AI client connection\n" + "%botConnection = aiConnect(\"MasterBlaster\" @ %i, -1, 0.5, false, \"SDF\", 1.0);\n\n" + "// In another area of the code, you can locate this and any other AIConnections\n" + "// using the isAIControlled function\n" + "for(%i = 0; %i < ClientGroup.getCount(); %i++)\n" + "{\n" + " %client = ClientGroup.getObject(%i);\n" + " if(%client.isAIControlled())\n" + " {\n" + " // React to this AI controlled client\n" + " }\n" + "}\n" + "@endtsexample\n\n" + + "@note This is a legacy class, which you are discouraged from using as it will " + "most likely be deprecated in a future version. For now it has been left in for " + "backwards compatibility with TGE and the old RTS Kit. Use GameConnection " + "and AIPlayer instead.\n\n" + + "@see GameConnection, NetConnection, AIClient\n\n" + + "@ingroup AI\n" + "@ingroup Networking\n" +); +//----------------------------------------------------------------------------- + +AIConnection::AIConnection() { + mAIControlled = true; + mMove = NullMove; +} + + +//----------------------------------------------------------------------------- + +void AIConnection::clearMoves( U32 ) +{ + // Clear the pending move list. This connection generates moves + // on the fly, so there are never any pending moves. +} + +void AIConnection::setMove(Move* m) +{ + mMove = *m; +} + +const Move& AIConnection::getMove() +{ + return mMove; +} + +/// Retrive the pending moves +/** + * The GameConnection base class queues moves for delivery to the + * controll object. This function is normally used to retrieve the + * queued moves recieved from the client. The AI connection does not + * have a connected client and simply generates moves on-the-fly + * base on it's current state. + */ +U32 AIConnection::getMoveList( Move **lngMove, U32 *numMoves ) +{ + *numMoves = 1; + *lngMove = &mMove; + return *numMoves; +} + + +//----------------------------------------------------------------------------- +// Console functions & methods +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- + +static inline F32 moveClamp(F32 v) +{ + // Support function to convert/clamp the input into a move rotation + // which only allows 0 -> M_2PI. + F32 a = mClampF(v, -M_2PI_F, M_2PI_F); + return (a < 0) ? a + M_2PI_F : a; +} + + +//----------------------------------------------------------------------------- +/// Construct and connect an AI connection object +ConsoleFunction(aiConnect, S32 , 2, 20, "(...)" + "@brief Creates a new AIConnection, and passes arguments to its onConnect script callback.\n\n" + "@returns The newly created AIConnection\n" + "@see GameConnection for parameter information\n" + "@ingroup AI") +{ + // Create the connection + AIConnection *aiConnection = new AIConnection(); + aiConnection->registerObject(); + + // Add the connection to the client group + SimGroup *g = Sim::getClientGroup(); + g->addObject( aiConnection ); + + // Prep the arguments for the console exec... + // Make sure and leav args[1] empty. + const char* args[21]; + args[0] = "onConnect"; + for (S32 i = 1; i < argc; i++) + args[i + 1] = argv[i]; + + // Execute the connect console function, this is the same + // onConnect function invoked for normal client connections + Con::execute(aiConnection, argc + 1, args); + return aiConnection->getId(); +} + + +//----------------------------------------------------------------------------- +ConsoleMethod(AIConnection,setMove,void,4, 4,"(string field, float value)" + "Set a field on the current move.\n\n" + "@param field One of {'x','y','z','yaw','pitch','roll'}\n" + "@param value Value to set field to.") +{ + Move move = object->getMove(); + + // Ok, a little slow for now, but this is just an example.. + if (!dStricmp(argv[2],"x")) + move.x = mClampF(dAtof(argv[3]),-1,1); + else + if (!dStricmp(argv[2],"y")) + move.y = mClampF(dAtof(argv[3]),-1,1); + else + if (!dStricmp(argv[2],"z")) + move.z = mClampF(dAtof(argv[3]),-1,1); + else + if (!dStricmp(argv[2],"yaw")) + move.yaw = moveClamp(dAtof(argv[3])); + else + if (!dStricmp(argv[2],"pitch")) + move.pitch = moveClamp(dAtof(argv[3])); + else + if (!dStricmp(argv[2],"roll")) + move.roll = moveClamp(dAtof(argv[3])); + + // + object->setMove(&move); +} + +ConsoleMethod(AIConnection,getMove,F32,3, 3,"(string field)" + "Get the given field of a move.\n\n" + "@param field One of {'x','y','z','yaw','pitch','roll'}\n" + "@returns The requested field on the current move.") +{ + const Move& move = object->getMove(); + if (!dStricmp(argv[2],"x")) + return move.x; + if (!dStricmp(argv[2],"y")) + return move.y; + if (!dStricmp(argv[2],"z")) + return move.z; + if (!dStricmp(argv[2],"yaw")) + return move.yaw; + if (!dStricmp(argv[2],"pitch")) + return move.pitch; + if (!dStricmp(argv[2],"roll")) + return move.roll; + return 0; +} + + +ConsoleMethod(AIConnection,setFreeLook,void,3, 3,"(bool isFreeLook)" + "Enable/disable freelook on the current move.") +{ + Move move = object->getMove(); + move.freeLook = dAtob(argv[2]); + object->setMove(&move); +} + +ConsoleMethod(AIConnection,getFreeLook,bool,2, 2,"getFreeLook()" + "Is freelook on for the current move?") +{ + return object->getMove().freeLook; +} + + +//----------------------------------------------------------------------------- + +ConsoleMethod(AIConnection,setTrigger,void,4, 4,"(int trigger, bool set)" + "Set a trigger.") +{ + S32 idx = dAtoi(argv[2]); + if (idx >= 0 && idx < MaxTriggerKeys) { + Move move = object->getMove(); + move.trigger[idx] = dAtob(argv[3]); + object->setMove(&move); + } +} + +ConsoleMethod(AIConnection,getTrigger,bool,4, 4,"(int trigger)" + "Is the given trigger set?") +{ + S32 idx = dAtoi(argv[2]); + if (idx >= 0 && idx < MaxTriggerKeys) + return object->getMove().trigger[idx]; + return false; +} + + +//----------------------------------------------------------------------------- + +ConsoleMethod(AIConnection,getAddress,const char*,2, 2,"") +{ + // Override the netConnection method to return to indicate + // this is an ai connection. + return "ai:local"; +} diff --git a/Engine/source/T3D/aiConnection.h b/Engine/source/T3D/aiConnection.h new file mode 100644 index 000000000..63707853c --- /dev/null +++ b/Engine/source/T3D/aiConnection.h @@ -0,0 +1,56 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _AICONNECTION_H_ +#define _AICONNECTION_H_ + +#ifndef _GAMECONNECTION_H_ +#include "T3D/gameBase/gameConnection.h" +#endif +#ifndef _MOVEMANAGER_H_ +#include "T3D/gameBase/moveManager.h" +#endif + +//----------------------------------------------------------------------------- + +class AIConnection : public GameConnection +{ + typedef GameConnection Parent; + +protected: + Move mMove; + +public: + AIConnection(); + DECLARE_CONOBJECT( AIConnection ); + + // Interface + const Move& getMove(); + void setMove(Move *m); + + // GameConnection overrides + void clearMoves(U32 n); + virtual U32 getMoveList(Move **,U32 *numMoves); +}; + + +#endif diff --git a/Engine/source/T3D/aiPlayer.cpp b/Engine/source/T3D/aiPlayer.cpp new file mode 100644 index 000000000..2a0aeab85 --- /dev/null +++ b/Engine/source/T3D/aiPlayer.cpp @@ -0,0 +1,602 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/aiPlayer.h" + +#include "console/consoleInternal.h" +#include "math/mMatrix.h" +#include "T3D/gameBase/moveManager.h" +#include "console/engineAPI.h" + +IMPLEMENT_CO_NETOBJECT_V1(AIPlayer); + +ConsoleDocClass( AIPlayer, + "@brief A Player object not controlled by conventional input, but by an AI engine.\n\n" + + "The AIPlayer provides a Player object that may be controlled from script. You control " + "where the player moves and how fast. You may also set where the AIPlayer is aiming at " + "-- either a location or another game object.\n\n" + + "The AIPlayer class does not have a datablock of its own. It makes use of the PlayerData " + "datablock to define how it looks, etc. As the AIPlayer is an extension of the Player class " + "it can mount objects and fire weapons, or mount vehicles and drive them.\n\n" + + "While the PlayerData datablock is used, there are a number of additional callbacks that are " + "implemented by AIPlayer on the datablock. These are listed here:\n\n" + + "void onReachDestination(AIPlayer obj) \n" + "Called when the player has reached its set destination using the setMoveDestination() method. " + "The actual point at which this callback is called is when the AIPlayer is within the mMoveTolerance " + "of the defined destination.\n\n" + + "void onMoveStuck(AIPlayer obj) \n" + "While in motion, if an AIPlayer has moved less than moveStuckTolerance within a single tick, this " + "callback is called. From here you could choose an alternate destination to get the AIPlayer moving " + "again.\n\n" + + "void onTargetEnterLOS(AIPlayer obj) \n" + "When an object is being aimed at (following a call to setAimObject()) and the targeted object enters " + "the AIPlayer's line of sight, this callback is called. The LOS test is a ray from the AIPlayer's eye " + "position to the center of the target's bounding box. The LOS ray test only checks against interiors, " + "statis shapes, and terrain.\n\n" + + "void onTargetExitLOS(AIPlayer obj) \n" + "When an object is being aimed at (following a call to setAimObject()) and the targeted object leaves " + "the AIPlayer's line of sight, this callback is called. The LOS test is a ray from the AIPlayer's eye " + "position to the center of the target's bounding box. The LOS ray test only checks against interiors, " + "statis shapes, and terrain.\n\n" + + "@tsexample\n" + "// Create the demo player object\n" + "%player = new AiPlayer()\n" + "{\n" + " dataBlock = DemoPlayer;\n" + " path = \"\";\n" + "};\n" + "@endtsexample\n\n" + + "@see Player for a list of all inherited functions, variables, and base description\n" + + "@ingroup AI\n" + "@ingroup gameObjects\n"); +/** + * Constructor + */ +AIPlayer::AIPlayer() +{ + mMoveDestination.set( 0.0f, 0.0f, 0.0f ); + mMoveSpeed = 1.0f; + mMoveTolerance = 0.25f; + mMoveStuckTolerance = 0.01f; + mMoveStuckTestDelay = 30; + mMoveStuckTestCountdown = 0; + mMoveSlowdown = true; + mMoveState = ModeStop; + + mAimObject = 0; + mAimLocationSet = false; + mTargetInLOS = false; + mAimOffset = Point3F(0.0f, 0.0f, 0.0f); + + mIsAiControlled = true; +} + +/** + * Destructor + */ +AIPlayer::~AIPlayer() +{ +} + +void AIPlayer::initPersistFields() +{ + addGroup( "AI" ); + + addField( "mMoveTolerance", TypeF32, Offset( mMoveTolerance, AIPlayer ), + "@brief Distance from destination before stopping.\n\n" + "When the AIPlayer is moving to a given destination it will move to within " + "this distance of the destination and then stop. By providing this tolerance " + "it helps the AIPlayer from never reaching its destination due to minor obstacles, " + "rounding errors on its position calculation, etc. By default it is set to 0.25.\n"); + + addField( "moveStuckTolerance", TypeF32, Offset( mMoveStuckTolerance, AIPlayer ), + "@brief Distance tolerance on stuck check.\n\n" + "When the AIPlayer is moving to a given destination, if it ever moves less than " + "this tolerance during a single tick, the AIPlayer is considered stuck. At this point " + "the onMoveStuck() callback is called on the datablock.\n"); + + addField( "moveStuckTestDelay", TypeS32, Offset( mMoveStuckTestDelay, AIPlayer ), + "@brief The number of ticks to wait before testing if the AIPlayer is stuck.\n\n" + "When the AIPlayer is asked to move, this property is the number of ticks to wait " + "before the AIPlayer starts to check if it is stuck. This delay allows the AIPlayer " + "to accelerate to full speed without its initial slow start being considered as stuck.\n" + "@note Set to zero to have the stuck test start immediately.\n"); + + endGroup( "AI" ); + + Parent::initPersistFields(); +} + +bool AIPlayer::onAdd() +{ + if (!Parent::onAdd()) + return false; + + // Use the eye as the current position (see getAIMove) + MatrixF eye; + getEyeTransform(&eye); + mLastLocation = eye.getPosition(); + + return true; +} + +/** + * Sets the speed at which this AI moves + * + * @param speed Speed to move, default player was 10 + */ +void AIPlayer::setMoveSpeed( F32 speed ) +{ + mMoveSpeed = getMax(0.0f, getMin( 1.0f, speed )); +} + +/** + * Stops movement for this AI + */ +void AIPlayer::stopMove() +{ + mMoveState = ModeStop; +} + +/** + * Sets how far away from the move location is considered + * "on target" + * + * @param tolerance Movement tolerance for error + */ +void AIPlayer::setMoveTolerance( const F32 tolerance ) +{ + mMoveTolerance = getMax( 0.1f, tolerance ); +} + +/** + * Sets the location for the bot to run to + * + * @param location Point to run to + */ +void AIPlayer::setMoveDestination( const Point3F &location, bool slowdown ) +{ + mMoveDestination = location; + mMoveState = ModeMove; + mMoveSlowdown = slowdown; + mMoveStuckTestCountdown = mMoveStuckTestDelay; +} + +/** + * Sets the object the bot is targeting + * + * @param targetObject The object to target + */ +void AIPlayer::setAimObject( GameBase *targetObject ) +{ + mAimObject = targetObject; + mTargetInLOS = false; + mAimOffset = Point3F(0.0f, 0.0f, 0.0f); +} + +/** + * Sets the object the bot is targeting and an offset to add to target location + * + * @param targetObject The object to target + * @param offset The offest from the target location to aim at + */ +void AIPlayer::setAimObject( GameBase *targetObject, Point3F offset ) +{ + mAimObject = targetObject; + mTargetInLOS = false; + mAimOffset = offset; +} + +/** + * Sets the location for the bot to aim at + * + * @param location Point to aim at + */ +void AIPlayer::setAimLocation( const Point3F &location ) +{ + mAimObject = 0; + mAimLocationSet = true; + mAimLocation = location; + mAimOffset = Point3F(0.0f, 0.0f, 0.0f); +} + +/** + * Clears the aim location and sets it to the bot's + * current destination so he looks where he's going + */ +void AIPlayer::clearAim() +{ + mAimObject = 0; + mAimLocationSet = false; + mAimOffset = Point3F(0.0f, 0.0f, 0.0f); +} + +/** + * This method calculates the moves for the AI player + * + * @param movePtr Pointer to move the move list into + */ +bool AIPlayer::getAIMove(Move *movePtr) +{ + *movePtr = NullMove; + + // Use the eye as the current position. + MatrixF eye; + getEyeTransform(&eye); + Point3F location = eye.getPosition(); + Point3F rotation = getRotation(); + + // Orient towards the aim point, aim object, or towards + // our destination. + if (mAimObject || mAimLocationSet || mMoveState != ModeStop) + { + // Update the aim position if we're aiming for an object + if (mAimObject) + mAimLocation = mAimObject->getPosition() + mAimOffset; + else + if (!mAimLocationSet) + mAimLocation = mMoveDestination; + + F32 xDiff = mAimLocation.x - location.x; + F32 yDiff = mAimLocation.y - location.y; + + if (!mIsZero(xDiff) || !mIsZero(yDiff)) + { + // First do Yaw + // use the cur yaw between -Pi and Pi + F32 curYaw = rotation.z; + while (curYaw > M_2PI_F) + curYaw -= M_2PI_F; + while (curYaw < -M_2PI_F) + curYaw += M_2PI_F; + + // find the yaw offset + F32 newYaw = mAtan2( xDiff, yDiff ); + F32 yawDiff = newYaw - curYaw; + + // make it between 0 and 2PI + if( yawDiff < 0.0f ) + yawDiff += M_2PI_F; + else if( yawDiff >= M_2PI_F ) + yawDiff -= M_2PI_F; + + // now make sure we take the short way around the circle + if( yawDiff > M_PI_F ) + yawDiff -= M_2PI_F; + else if( yawDiff < -M_PI_F ) + yawDiff += M_2PI_F; + + movePtr->yaw = yawDiff; + + // Next do pitch. + if (!mAimObject && !mAimLocationSet) + { + // Level out if were just looking at our next way point. + Point3F headRotation = getHeadRotation(); + movePtr->pitch = -headRotation.x; + } + else + { + // This should be adjusted to run from the + // eye point to the object's center position. Though this + // works well enough for now. + F32 vertDist = mAimLocation.z - location.z; + F32 horzDist = mSqrt(xDiff * xDiff + yDiff * yDiff); + F32 newPitch = mAtan2( horzDist, vertDist ) - ( M_PI_F / 2.0f ); + if (mFabs(newPitch) > 0.01f) + { + Point3F headRotation = getHeadRotation(); + movePtr->pitch = newPitch - headRotation.x; + } + } + } + } + else + { + // Level out if we're not doing anything else + Point3F headRotation = getHeadRotation(); + movePtr->pitch = -headRotation.x; + } + + // Move towards the destination + if (mMoveState != ModeStop) + { + F32 xDiff = mMoveDestination.x - location.x; + F32 yDiff = mMoveDestination.y - location.y; + + // Check if we should mMove, or if we are 'close enough' + if (mFabs(xDiff) < mMoveTolerance && mFabs(yDiff) < mMoveTolerance) + { + mMoveState = ModeStop; + throwCallback("onReachDestination"); + } + else + { + // Build move direction in world space + if (mIsZero(xDiff)) + movePtr->y = (location.y > mMoveDestination.y) ? -1.0f : 1.0f; + else + if (mIsZero(yDiff)) + movePtr->x = (location.x > mMoveDestination.x) ? -1.0f : 1.0f; + else + if (mFabs(xDiff) > mFabs(yDiff)) + { + F32 value = mFabs(yDiff / xDiff); + movePtr->y = (location.y > mMoveDestination.y) ? -value : value; + movePtr->x = (location.x > mMoveDestination.x) ? -1.0f : 1.0f; + } + else + { + F32 value = mFabs(xDiff / yDiff); + movePtr->x = (location.x > mMoveDestination.x) ? -value : value; + movePtr->y = (location.y > mMoveDestination.y) ? -1.0f : 1.0f; + } + + // Rotate the move into object space (this really only needs + // a 2D matrix) + Point3F newMove; + MatrixF moveMatrix; + moveMatrix.set(EulerF(0.0f, 0.0f, -(rotation.z + movePtr->yaw))); + moveMatrix.mulV( Point3F( movePtr->x, movePtr->y, 0.0f ), &newMove ); + movePtr->x = newMove.x; + movePtr->y = newMove.y; + + // Set movement speed. We'll slow down once we get close + // to try and stop on the spot... + if (mMoveSlowdown) + { + F32 speed = mMoveSpeed; + F32 dist = mSqrt(xDiff*xDiff + yDiff*yDiff); + F32 maxDist = 5.0f; + if (dist < maxDist) + speed *= dist / maxDist; + movePtr->x *= speed; + movePtr->y *= speed; + + mMoveState = ModeSlowing; + } + else + { + movePtr->x *= mMoveSpeed; + movePtr->y *= mMoveSpeed; + + mMoveState = ModeMove; + } + + if (mMoveStuckTestCountdown > 0) + --mMoveStuckTestCountdown; + else + { + // We should check to see if we are stuck... + F32 locationDelta = (location - mLastLocation).len(); + if (locationDelta < mMoveStuckTolerance && mDamageState == Enabled) + { + // If we are slowing down, then it's likely that our location delta will be less than + // our move stuck tolerance. Because we can be both slowing and stuck + // we should TRY to check if we've moved. This could use better detection. + if ( mMoveState != ModeSlowing || locationDelta == 0 ) + { + mMoveState = ModeStuck; + throwCallback("onMoveStuck"); + } + } + } + } + } + + // Test for target location in sight if it's an object. The LOS is + // run from the eye position to the center of the object's bounding, + // which is not very accurate. + if (mAimObject) { + MatrixF eyeMat; + getEyeTransform(&eyeMat); + eyeMat.getColumn(3,&location); + Point3F targetLoc = mAimObject->getBoxCenter(); + + // This ray ignores non-static shapes. Cast Ray returns true + // if it hit something. + RayInfo dummy; + if (getContainer()->castRay( location, targetLoc, + InteriorObjectType | StaticShapeObjectType | StaticObjectType | + TerrainObjectType, &dummy)) { + if (mTargetInLOS) { + throwCallback( "onTargetExitLOS" ); + mTargetInLOS = false; + } + } + else + if (!mTargetInLOS) { + throwCallback( "onTargetEnterLOS" ); + mTargetInLOS = true; + } + } + + // Replicate the trigger state into the move so that + // triggers can be controlled from scripts. + for( int i = 0; i < MaxTriggerKeys; i++ ) + movePtr->trigger[i] = getImageTriggerState(i); + + mLastLocation = location; + + return true; +} + +/** + * Utility function to throw callbacks. Callbacks always occure + * on the datablock class. + * + * @param name Name of script function to call + */ +void AIPlayer::throwCallback( const char *name ) +{ + Con::executef(getDataBlock(), name, getIdString()); +} + + +// -------------------------------------------------------------------------------------------- +// Console Functions +// -------------------------------------------------------------------------------------------- + +DefineEngineMethod( AIPlayer, stop, void, ( ),, + "@brief Tells the AIPlayer to stop moving.\n\n") +{ + object->stopMove(); +} + +DefineEngineMethod( AIPlayer, clearAim, void, ( ),, + "@brief Use this to stop aiming at an object or a point.\n\n" + + "@see setAimLocation()\n" + "@see setAimObject()\n") +{ + object->clearAim(); +} + +DefineEngineMethod( AIPlayer, setMoveSpeed, void, ( F32 speed ),, + "@brief Sets the move speed for an AI object.\n\n" + + "@param speed A speed multiplier between 0.0 and 1.0. " + "This is multiplied by the AIPlayer's base movement rates (as defined in " + "its PlayerData datablock)\n\n" + + "@see getMoveDestination()\n") +{ + object->setMoveSpeed(speed); +} + +DefineEngineMethod( AIPlayer, setMoveDestination, void, ( Point3F goal, bool slowDown ), ( true ), + "@brief Tells the AI to move to the location provided\n\n" + + "@param goal Coordinates in world space representing location to move to.\n" + "@param slowDown A boolean value. If set to true, the bot will slow down " + "when it gets within 5-meters of its move destination. If false, the bot " + "will stop abruptly when it reaches the move destination. By default, this is true.\n\n" + + "@note Upon reaching a move destination, the bot will clear its move destination and " + "calls to getMoveDestination will return \"0 0 0\"." + + "@see getMoveDestination()\n") +{ + object->setMoveDestination( goal, slowDown); +} + +DefineEngineMethod( AIPlayer, getMoveDestination, Point3F, (),, + "@brief Get the AIPlayer's current destination.\n\n" + + "@return Returns a point containing the \"x y z\" position " + "of the AIPlayer's current move destination. If no move destination " + "has yet been set, this returns \"0 0 0\"." + + "@see setMoveDestination()\n") +{ + return object->getMoveDestination(); +} + +DefineEngineMethod( AIPlayer, setAimLocation, void, ( Point3F target ),, + "@brief Tells the AIPlayer to aim at the location provided.\n\n" + + "@param target An \"x y z\" position in the game world to target.\n\n" + + "@see getAimLocation()\n") +{ + object->setAimLocation(target); +} + +DefineEngineMethod( AIPlayer, getAimLocation, Point3F, (),, + "@brief Returns the point the AIPlayer is aiming at.\n\n" + + "This will reflect the position set by setAimLocation(), " + "or the position of the object that the bot is now aiming at. " + "If the bot is not aiming at anything, this value will " + "change to whatever point the bot's current line-of-sight intercepts." + + "@return World space coordinates of the object AI is aiming at. Formatted as \"X Y Z\".\n\n" + + "@see setAimLocation()\n" + "@see setAimObject()\n") +{ + return object->getAimLocation(); +} + +ConsoleDocFragment _setAimObject( + "@brief Sets the AIPlayer's target object. May optionally set an offset from target location\n\n" + + "@param targetObject The object to target\n" + "@param offset Optional three-element offset vector which will be added to the position of the aim object.\n\n" + + "@tsexample\n" + "// Without an offset\n" + "%ai.setAimObject(%target);\n\n" + "// With an offset\n" + "// Cause our AI object to aim at the target\n" + "// offset (0, 0, 1) so you don't aim at the target's feet\n" + "%ai.setAimObject(%target, \"0 0 1\");\n" + "@endtsexample\n\n" + + "@see getAimLocation()\n" + "@see getAimObject()\n" + "@see clearAim()\n", + + "AIPlayer", + "void setAimObject(GameBase targetObject, Point3F offset);" +); +ConsoleMethod( AIPlayer, setAimObject, void, 3, 4, "( GameBase obj, [Point3F offset] )" + "Sets the bot's target object. Optionally set an offset from target location." + "@hide") +{ + Point3F off( 0.0f, 0.0f, 0.0f ); + + // Find the target + GameBase *targetObject; + if( Sim::findObject( argv[2], targetObject ) ) + { + if (argc == 4) + dSscanf( argv[3], "%g %g %g", &off.x, &off.y, &off.z ); + + object->setAimObject( targetObject, off ); + } + else + object->setAimObject( 0, off ); +} + +DefineEngineMethod( AIPlayer, getAimObject, S32, (),, + "@brief Gets the object the AIPlayer is targeting.\n\n" + + "@return Returns -1 if no object is being aimed at, " + "or the SimObjectID of the object the AIPlayer is aiming at.\n\n" + + "@see setAimObject()\n") +{ + GameBase* obj = object->getAimObject(); + return obj? obj->getId(): -1; +} diff --git a/Engine/source/T3D/aiPlayer.h b/Engine/source/T3D/aiPlayer.h new file mode 100644 index 000000000..4e0bb5a3a --- /dev/null +++ b/Engine/source/T3D/aiPlayer.h @@ -0,0 +1,94 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _AIPLAYER_H_ +#define _AIPLAYER_H_ + +#ifndef _PLAYER_H_ +#include "T3D/player.h" +#endif + + +class AIPlayer : public Player { + + typedef Player Parent; + +public: + enum MoveState { + ModeStop, // AI has stopped moving. + ModeMove, // AI is currently moving. + ModeStuck, // AI is stuck, but wants to move. + ModeSlowing, // AI is slowing down as it reaches it's destination. + }; + +private: + MoveState mMoveState; + F32 mMoveSpeed; + F32 mMoveTolerance; // Distance from destination before we stop + Point3F mMoveDestination; // Destination for movement + Point3F mLastLocation; // For stuck check + F32 mMoveStuckTolerance; // Distance tolerance on stuck check + S32 mMoveStuckTestDelay; // The number of ticks to wait before checking if the AI is stuck + S32 mMoveStuckTestCountdown; // The current countdown until at AI starts to check if it is stuck + bool mMoveSlowdown; // Slowdown as we near the destination + + SimObjectPtr mAimObject; // Object to point at, overrides location + bool mAimLocationSet; // Has an aim location been set? + Point3F mAimLocation; // Point to look at + bool mTargetInLOS; // Is target object visible? + + Point3F mAimOffset; + + // Utility Methods + void throwCallback( const char *name ); + +public: + DECLARE_CONOBJECT( AIPlayer ); + + AIPlayer(); + ~AIPlayer(); + + static void initPersistFields(); + + bool onAdd(); + + virtual bool getAIMove( Move *move ); + + // Targeting and aiming sets/gets + void setAimObject( GameBase *targetObject ); + void setAimObject( GameBase *targetObject, Point3F offset ); + GameBase* getAimObject() const { return mAimObject; } + void setAimLocation( const Point3F &location ); + Point3F getAimLocation() const { return mAimLocation; } + void clearAim(); + + // Movement sets/gets + void setMoveSpeed( const F32 speed ); + F32 getMoveSpeed() const { return mMoveSpeed; } + void setMoveTolerance( const F32 tolerance ); + F32 getMoveTolerance() const { return mMoveTolerance; } + void setMoveDestination( const Point3F &location, bool slowdown ); + Point3F getMoveDestination() const { return mMoveDestination; } + void stopMove(); +}; + +#endif diff --git a/Engine/source/T3D/camera.cpp b/Engine/source/T3D/camera.cpp new file mode 100644 index 000000000..0d742b620 --- /dev/null +++ b/Engine/source/T3D/camera.cpp @@ -0,0 +1,1978 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/camera.h" + +#include "math/mMath.h" +#include "core/stream/bitStream.h" +#include "T3D/fx/cameraFXMgr.h" +#include "T3D/gameBase/gameConnection.h" +#include "math/mathIO.h" +#include "gui/worldEditor/editor.h" +#include "console/engineAPI.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "math/mathUtils.h" +#include "math/mTransform.h" + +#define MaxPitch 1.5706f +#define CameraRadius 0.05f; + + +ImplementEnumType( CameraMotionMode, + "Movement behavior type for Camera.\n\n" + "@ingroup BaseCamera" ) + { Camera::StationaryMode, "Stationary", "Camera does not rotate or move." }, + { Camera::FreeRotateMode, "FreeRotate", "Camera may rotate but does not move." }, + { Camera::FlyMode, "Fly", "Camera may rotate and move freely." }, + { Camera::OrbitObjectMode, "OrbitObject", "Camera orbits about a given object. Damage flash and white out is determined by the object being orbited. See Camera::setOrbitMode() to set the orbit object and other parameters." }, + { Camera::OrbitPointMode, "OrbitPoint", "Camera orbits about a given point. See Camera::setOrbitMode() to set the orbit point and other parameters." }, + { Camera::TrackObjectMode, "TrackObject", "Camera always faces a given object. See Camera::setTrackObject() to set the object to track and a distance to remain from the object." }, + { Camera::OverheadMode, "Overhead", "Camera moves in the XY plane." }, + { Camera::EditOrbitMode, "EditOrbit", "Used by the World Editor to orbit about a point. When first activated, the camera is rotated to face the orbit point rather than move to it." } +EndImplementEnumType; + + +//============================================================================= +// CameraData. +//============================================================================= +// MARK: ---- CameraData ---- + +IMPLEMENT_CO_DATABLOCK_V1( CameraData ); +ConsoleDocClass( CameraData, + "@brief A datablock that describes a camera.\n\n" + + "@tsexample\n" + "datablock CameraData(Observer)\n" + "{\n" + " mode = \"Observer\";\n" + "};\n" + "@endtsexample\n" + + "@see Camera\n\n" + "@ref Datablock_Networking\n" + "@ingroup BaseCamera\n" + "@ingroup Datablocks\n" +); + +//----------------------------------------------------------------------------- + +void CameraData::initPersistFields() +{ + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +void CameraData::packData(BitStream* stream) +{ + Parent::packData(stream); +} + +//----------------------------------------------------------------------------- + +void CameraData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); +} + +//============================================================================= +// Camera. +//============================================================================= +// MARK: ---- Camera ---- + +IMPLEMENT_CO_NETOBJECT_V1( Camera ); +ConsoleDocClass( Camera, + "@brief Represents a position, direction and field of view to render a scene from.\n\n" + + "A camera is typically manipulated by a GameConnection. When set as the connection's " + "control object, the camera handles all movement actions ($mvForwardAction, $mvPitch, etc.) " + "just like a Player.\n" + + "@tsexample\n" + "// Set an already created camera as the GameConnection's control object\n" + "%connection.setControlObject(%camera);\n" + "@endtsexample\n\n" + + "

      Methods of Operation

      \n\n" + + "The camera has two general methods of operation. The first is the standard mode where " + "the camera starts and stops its motion and rotation instantly. This is the default operation " + "of the camera and is used by most games. It may be specifically set with Camera::setFlyMode() " + "for 6 DoF motion. It is also typically the method used with Camera::setOrbitMode() or one of " + "its helper methods to orbit about a specific object (such as the Player's dead body) or a " + "specific point.\n\n" + + "The second method goes under the name of Newton as it follows Newton's 2nd law of " + "motion: F=ma. This provides the camera with an ease-in and ease-out feel for both movement " + "and rotation. To activate this method for movement, either use Camera::setNewtonFlyMode() or set " + "the Camera::newtonMode field to true. To activate this method for rotation, set the Camera::newtonRotation " + "to true. This method of operation is not typically used in games, and was developed to allow " + "for a smooth fly through of a game level while recording a demo video. But with the right force " + "and drag settings, it may give a more organic feel to the camera to games that use an overhead view, " + "such as a RTS.\n\n" + + "There is a third, minor method of operation but it is not generally used for games. This is when the " + "camera is used with Torque's World Editor in Edit Orbit Mode. When set, this allows the camera " + "to rotate about a specific point in the world, and move towards and away from this point. See " + "Camera::setEditOrbitMode() and Camera::setEditOrbitPoint(). While in this mode, Camera::autoFitRadius() " + "may also be used.\n\n" + + "@tsexample\n" + "// Create a camera in the level and set its position to a given spawn point.\n" + "// Note: The camera starts in the standard fly mode.\n" + "%cam = new Camera() {\n" + " datablock = \"Observer\";\n" + "};\n" + "MissionCleanup.add( %cam );\n" + "%cam.setTransform( %spawnPoint.getTransform() );\n" + "@endtsexample\n\n" + + "@tsexample\n" + "// Create a camera at the given spawn point for the specified\n" + "// GameConnection i.e. the client. Uses the standard\n" + "// Sim::spawnObject() function to create the camera using the\n" + "// defined default settings.\n" + "// Note: The camera starts in the standard fly mode.\n" + "function GameConnection::spawnCamera(%this, %spawnPoint)\n" + "{\n" + " // Set the control object to the default camera\n" + " if (!isObject(%this.camera))\n" + " {\n" + " if (isDefined(\"$Game::DefaultCameraClass\"))\n" + " %this.camera = spawnObject($Game::DefaultCameraClass, $Game::DefaultCameraDataBlock);\n" + " }\n" + "\n" + " // If we have a camera then set up some properties\n" + " if (isObject(%this.camera))\n" + " {\n" + " // Make sure we're cleaned up when the mission ends\n" + " MissionCleanup.add( %this.camera );\n" + "\n" + " // Make sure the camera is always in scope for the connection\n" + " %this.camera.scopeToClient(%this);\n" + "\n" + " // Send all user input from the connection to the camera\n" + " %this.setControlObject(%this.camera);\n" + "\n" + " if (isDefined(\"%spawnPoint\"))\n" + " {\n" + " // Attempt to treat %spawnPoint as an object, such as a\n" + " // SpawnSphere class.\n" + " if (getWordCount(%spawnPoint) == 1 && isObject(%spawnPoint))\n" + " {\n" + " %this.camera.setTransform(%spawnPoint.getTransform());\n" + " }\n" + " else\n" + " {\n" + " // Treat %spawnPoint as an AngleAxis transform\n" + " %this.camera.setTransform(%spawnPoint);\n" + " }\n" + " }\n" + " }\n" + "}\n" + "@endtsexample\n\n" + + "

      Motion Modes

      \n\n" + + "Beyond the different operation methods, the Camera may be set to one of a number " + "of motion modes. These motion modes determine how the camera will respond to input " + "and may be used to constrain how the Camera moves. The CameraMotionMode enumeration " + "defines the possible set of modes and the Camera's current may be obtained by using " + "getMode().\n\n" + + "Some of the motion modes may be set using specific script methods. These often provide " + "additional parameters to set up the mode in one go. Otherwise, it is always possible to " + "set a Camera's motion mode using the controlMode property. Just pass in the name of the " + "mode enum. The following table lists the motion modes, how to set them up, and what they offer:\n\n" + + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
      ModeSet From ScriptInput MoveInput RotateCan Use Newton Mode?
      StationarycontrolMode propertyNoNoNo
      FreeRotatecontrolMode propertyNoYesRotate Only
      FlysetFlyMode()YesYesYes
      OrbitObjectsetOrbitMode()Orbits objectPoints to objectMove only
      OrbitPointsetOrbitPoint()Orbits pointPoints to locationMove only
      TrackObjectsetTrackObject()NoPoints to objectYes
      OverheadcontrolMode propertyYesNoYes
      EditOrbit (object selected)setEditOrbitMode()Orbits objectPoints to objectMove only
      EditOrbit (no object)setEditOrbitMode()YesYesYes
      \n\n" + + "

      %Trigger Input

      \n\n" + + "Passing a move trigger ($mvTriggerCount0, $mvTriggerCount1, etc.) on to a Camera performs " + "different actions depending on which mode the camera is in. While in Fly, Overhead or " + "EditOrbit mode, either trigger0 or trigger1 will cause a camera to move twice its normal " + "movement speed. You can see this in action within the World Editor, where holding down the " + "left mouse button while in mouse look mode (right mouse button is also down) causes the Camera " + "to move faster.\n\n" + + "Passing along trigger2 will put the camera into strafe mode. While in this mode a Fly, " + "FreeRotate or Overhead Camera will not rotate from the move input. Instead the yaw motion " + "will be applied to the Camera's x motion, and the pitch motion will be applied to the Camera's " + "z motion. You can see this in action within the World Editor where holding down the middle mouse " + "button allows the user to move the camera up, down and side-to-side.\n\n" + + "While the camera is operating in Newton Mode, trigger0 and trigger1 behave slightly differently. " + "Here trigger0 activates a multiplier to the applied acceleration force as defined by speedMultiplier. " + "This has the affect of making the camera move up to speed faster. trigger1 has the opposite affect " + "by acting as a brake. When trigger1 is active a multiplier is added to the Camera's drag as " + "defined by brakeMultiplier.\n\n" + + "@see CameraData\n" + "@see CameraMotionMode\n" + "@see Camera::movementSpeed\n\n" + "@ingroup BaseCamera\n" +); + +F32 Camera::smMovementSpeed = 40.0f; + +//---------------------------------------------------------------------------- + +Camera::Camera() +{ + mNetFlags.clear(Ghostable); + mTypeMask |= CameraObjectType; + mDelta.pos = Point3F(0.0f, 0.0f, 100.0f); + mDelta.rot = Point3F(0.0f, 0.0f, 0.0f); + mDelta.posVec = mDelta.rotVec = VectorF(0.0f, 0.0f, 0.0f); + mObjToWorld.setColumn(3, mDelta.pos); + mRot = mDelta.rot; + + mOffset.set(0.0f, 0.0f, 0.0f); + + mMinOrbitDist = 0.0f; + mMaxOrbitDist = 0.0f; + mCurOrbitDist = 0.0f; + mOrbitObject = NULL; + mPosition.set(0.0f, 0.0f, 0.0f); + mObservingClientObject = false; + mMode = FlyMode; + + // For NewtonFlyMode + mNewtonRotation = false; + mAngularVelocity.set(0.0f, 0.0f, 0.0f); + mAngularForce = 100.0f; + mAngularDrag = 2.0f; + mVelocity.set(0.0f, 0.0f, 0.0f); + mNewtonMode = false; + mMass = 10.0f; + mDrag = 2.0; + mFlyForce = 500.0f; + mSpeedMultiplier = 2.0f; + mBrakeMultiplier = 2.0f; + + // For EditOrbitMode + mValidEditOrbitPoint = false; + mEditOrbitPoint.set(0.0f, 0.0f, 0.0f); + mCurrentEditOrbitDist = 2.0; + + mLocked = false; +} + +//---------------------------------------------------------------------------- + +Camera::~Camera() +{ +} + +//---------------------------------------------------------------------------- + +bool Camera::onAdd() +{ + if(!Parent::onAdd()) + return false; + + mObjBox.maxExtents = mObjScale; + mObjBox.minExtents = mObjScale; + mObjBox.minExtents.neg(); + resetWorldBox(); + + addToScene(); + return true; +} + +//---------------------------------------------------------------------------- + +void Camera::onEditorEnable() +{ + mNetFlags.set(Ghostable); +} + +//---------------------------------------------------------------------------- + +void Camera::onEditorDisable() +{ + mNetFlags.clear(Ghostable); +} + +//---------------------------------------------------------------------------- + +void Camera::onRemove() +{ + removeFromScene(); + Parent::onRemove(); +} + +//---------------------------------------------------------------------------- + +// check if the object needs to be observed through its own camera... +void Camera::getCameraTransform(F32* pos, MatrixF* mat) +{ + // The camera doesn't support a third person mode, + // so we want to override the default ShapeBase behavior. + ShapeBase * obj = dynamic_cast(static_cast(mOrbitObject)); + if(obj && static_cast(obj->getDataBlock())->observeThroughObject) + obj->getCameraTransform(pos, mat); + else + getRenderEyeTransform(mat); + + // Apply Camera FX. + mat->mul( gCamFXMgr.getTrans() ); +} + +//---------------------------------------------------------------------------- + +F32 Camera::getCameraFov() +{ + ShapeBase * obj = dynamic_cast(static_cast(mOrbitObject)); + if(obj && static_cast(obj->getDataBlock())->observeThroughObject) + return(obj->getCameraFov()); + else + return(Parent::getCameraFov()); +} + +//---------------------------------------------------------------------------- + +F32 Camera::getDefaultCameraFov() +{ + ShapeBase * obj = dynamic_cast(static_cast(mOrbitObject)); + if(obj && static_cast(obj->getDataBlock())->observeThroughObject) + return(obj->getDefaultCameraFov()); + else + return(Parent::getDefaultCameraFov()); +} + +//---------------------------------------------------------------------------- + +bool Camera::isValidCameraFov(F32 fov) +{ + ShapeBase * obj = dynamic_cast(static_cast(mOrbitObject)); + if(obj && static_cast(obj->getDataBlock())->observeThroughObject) + return(obj->isValidCameraFov(fov)); + else + return(Parent::isValidCameraFov(fov)); +} + +//---------------------------------------------------------------------------- + +void Camera::setCameraFov(F32 fov) +{ + ShapeBase * obj = dynamic_cast(static_cast(mOrbitObject)); + if(obj && static_cast(obj->getDataBlock())->observeThroughObject) + obj->setCameraFov(fov); + else + Parent::setCameraFov(fov); +} + +//---------------------------------------------------------------------------- + +void clampPitchAngle(F32 &pitch) +{ + // Clamp pitch to +/-MaxPitch, but allow pitch=PI as it is used by some editor + // views (bottom, front, right) + if ((pitch > MaxPitch) && !mIsEqual(pitch, M_PI_F, 0.001f)) + pitch = MaxPitch; + else if (pitch < -MaxPitch) + pitch = -MaxPitch; +} + +//---------------------------------------------------------------------------- + +void Camera::processTick(const Move* move) +{ + Parent::processTick(move); + + if ( isMounted() ) + { + // Fetch Mount Transform. + MatrixF mat; + mMount.object->getMountTransform( mMount.node, mMount.xfm, &mat ); + + // Apply. + setTransform( mat ); + + // Update SceneContainer. + updateContainer(); + return; + } + + Point3F vec,pos; + if (move) + { + bool strafeMode = move->trigger[2]; + + // If using editor then force camera into fly mode, unless using EditOrbitMode + if(gEditingMission && mMode != FlyMode && mMode != EditOrbitMode) + setFlyMode(); + + // Massage the mode if we're in EditOrbitMode + CameraMotionMode virtualMode = mMode; + if(mMode == EditOrbitMode) + { + if(!mValidEditOrbitPoint) + { + virtualMode = FlyMode; + } + else + { + // Reset any Newton camera velocities for when we switch + // out of EditOrbitMode. + mNewtonRotation = false; + mVelocity.set(0.0f, 0.0f, 0.0f); + mAngularVelocity.set(0.0f, 0.0f, 0.0f); + } + } + + // Update orientation + mDelta.rotVec = mRot; + + VectorF rotVec(0, 0, 0); + + // process input/determine rotation vector + if(virtualMode != StationaryMode && + virtualMode != TrackObjectMode && + (!mLocked || virtualMode != OrbitObjectMode && virtualMode != OrbitPointMode)) + { + if(!strafeMode) + { + rotVec.x = move->pitch; + rotVec.z = move->yaw; + } + } + else if(virtualMode == TrackObjectMode && bool(mOrbitObject)) + { + // orient the camera to face the object + Point3F objPos; + // If this is a shapebase, use its render eye transform + // to avoid jittering. + ShapeBase *shape = dynamic_cast((GameBase*)mOrbitObject); + if( shape != NULL ) + { + MatrixF ret; + shape->getRenderEyeTransform( &ret ); + objPos = ret.getPosition(); + } + else + { + mOrbitObject->getWorldBox().getCenter(&objPos); + } + mObjToWorld.getColumn(3,&pos); + vec = objPos - pos; + vec.normalizeSafe(); + F32 pitch, yaw; + MathUtils::getAnglesFromVector(vec, yaw, pitch); + rotVec.x = -pitch - mRot.x; + rotVec.z = yaw - mRot.z; + if(rotVec.z > M_PI_F) + rotVec.z -= M_2PI_F; + else if(rotVec.z < -M_PI_F) + rotVec.z += M_2PI_F; + } + + // apply rotation vector according to physics rules + if(mNewtonRotation) + { + const F32 force = mAngularForce; + const F32 drag = mAngularDrag; + + VectorF acc(0.0f, 0.0f, 0.0f); + + rotVec.x *= 2.0f; // Assume that our -2PI to 2PI range was clamped to -PI to PI in script + rotVec.z *= 2.0f; // Assume that our -2PI to 2PI range was clamped to -PI to PI in script + + F32 rotVecL = rotVec.len(); + if(rotVecL > 0) + { + acc = (rotVec * force / mMass) * TickSec; + } + + // Accelerate + mAngularVelocity += acc; + + // Drag + mAngularVelocity -= mAngularVelocity * drag * TickSec; + + // Rotate + mRot += mAngularVelocity * TickSec; + clampPitchAngle(mRot.x); + } + else + { + mRot.x += rotVec.x; + mRot.z += rotVec.z; + clampPitchAngle(mRot.x); + } + + // Update position + VectorF posVec(0, 0, 0); + bool mustValidateEyePoint = false; + bool serverInterpolate = false; + + // process input/determine translation vector + if(virtualMode == OrbitObjectMode || virtualMode == OrbitPointMode) + { + pos = mDelta.pos; + if(virtualMode == OrbitObjectMode && bool(mOrbitObject)) + { + // If this is a shapebase, use its render eye transform + // to avoid jittering. + GameBase *castObj = mOrbitObject; + ShapeBase* shape = dynamic_cast(castObj); + if( shape != NULL ) + { + MatrixF ret; + shape->getRenderEyeTransform( &ret ); + mPosition = ret.getPosition(); + } + else + { + // Hopefully this is a static object that doesn't move, + // because the worldbox doesn't get updated between ticks. + mOrbitObject->getWorldBox().getCenter(&mPosition); + } + } + + posVec = (mPosition + mOffset) - pos; + mustValidateEyePoint = true; + serverInterpolate = mNewtonMode; + } + else if(virtualMode == EditOrbitMode && mValidEditOrbitPoint) + { + bool faster = move->trigger[0] || move->trigger[1]; + F32 scale = smMovementSpeed * (faster + 1); + mCurrentEditOrbitDist -= move->y * TickSec * scale; + mCurrentEditOrbitDist -= move->roll * TickSec * scale; // roll will be -Pi to Pi and we'll attempt to scale it here to be in line with the move->y calculation above + if(mCurrentEditOrbitDist < 0.0f) + mCurrentEditOrbitDist = 0.0f; + + mPosition = mEditOrbitPoint; + _setPosition(mPosition, mRot); + _calcEditOrbitPoint(&mObjToWorld, mRot); + pos = mPosition; + } + else if(virtualMode == FlyMode) + { + bool faster = move->trigger[0] || move->trigger[1]; + F32 scale = smMovementSpeed * (faster + 1); + + mObjToWorld.getColumn(3,&pos); + mObjToWorld.getColumn(0,&vec); + posVec = vec * move->x * TickSec * scale + vec * (strafeMode ? move->yaw * 2.0f * TickSec * scale : 0.0f); + mObjToWorld.getColumn(1,&vec); + posVec += vec * move->y * TickSec * scale + vec * move->roll * TickSec * scale; + mObjToWorld.getColumn(2,&vec); + posVec += vec * move->z * TickSec * scale - vec * (strafeMode ? move->pitch * 2.0f * TickSec * scale : 0.0f); + } + else if(virtualMode == OverheadMode) + { + bool faster = move->trigger[0] || move->trigger[1]; + F32 scale = smMovementSpeed * (faster + 1); + + mObjToWorld.getColumn(3,&pos); + + mObjToWorld.getColumn(0,&vec); + vec.z = 0; + vec.normalizeSafe(); + vec = vec * move->x * TickSec * scale + (strafeMode ? vec * move->yaw * 2.0f * TickSec * scale : Point3F(0, 0, 0)); + posVec = vec; + + mObjToWorld.getColumn(1,&vec); + vec.z = 0; + if (vec.isZero()) + { + mObjToWorld.getColumn(2,&vec); + vec.z = 0; + } + vec.normalizeSafe(); + vec = vec * move->y * TickSec * scale - (strafeMode ? vec * move->pitch * 2.0f * TickSec * scale : Point3F(0, 0, 0)); + posVec += vec; + posVec.z += move->z * TickSec * scale + move->roll * TickSec * scale; + } + else // ignore input + { + mObjToWorld.getColumn(3,&pos); + } + + // apply translation vector according to physics rules + mDelta.posVec = pos; + if(mNewtonMode) + { + bool faster = move->trigger[0]; + bool brake = move->trigger[1]; + + const F32 movementSpeedMultiplier = smMovementSpeed / 40.0f; // Using the starting value as the base + const F32 force = faster ? mFlyForce * movementSpeedMultiplier * mSpeedMultiplier : mFlyForce * movementSpeedMultiplier; + const F32 drag = brake ? mDrag * mBrakeMultiplier : mDrag; + + VectorF acc(0.0f, 0.0f, 0.0f); + + F32 posVecL = posVec.len(); + if(posVecL > 0) + { + acc = (posVec * force / mMass) * TickSec; + } + + // Accelerate + mVelocity += acc; + + // Drag + mVelocity -= mVelocity * drag * TickSec; + + // Move + pos += mVelocity * TickSec; + } + else + { + pos += posVec; + } + + _setPosition(pos,mRot); + + // If on the client, calc delta for backstepping + if (serverInterpolate || isClientObject()) + { + mDelta.pos = pos; + mDelta.rot = mRot; + mDelta.posVec = mDelta.posVec - mDelta.pos; + mDelta.rotVec = mDelta.rotVec - mDelta.rot; + } + + if(mustValidateEyePoint) + _validateEyePoint(1.0f, &mObjToWorld); + + setMaskBits(MoveMask); + } + + // Need to calculate the orbit position for the editor so the camera position + // doesn't change when switching between orbit and other camera modes + if (isServerObject() && (mMode == EditOrbitMode && mValidEditOrbitPoint)) + _calcEditOrbitPoint(&mObjToWorld, mRot); + + if(getControllingClient() && mContainer) + updateContainer(); +} + +//---------------------------------------------------------------------------- + +void Camera::onDeleteNotify( SimObject* obj ) +{ + if( obj == mOrbitObject ) + { + mOrbitObject = NULL; + + if( mMode == OrbitObjectMode ) + mMode = OrbitPointMode; + } + + Parent::onDeleteNotify( obj ); +} + +//---------------------------------------------------------------------------- + +void Camera::interpolateTick(F32 dt) +{ + Parent::interpolateTick(dt); + + if ( isMounted() ) + { + // Fetch Mount Transform. + MatrixF mat; + mMount.object->getRenderMountTransform( dt, mMount.node, mMount.xfm, &mat ); + + // Apply. + setRenderTransform( mat ); + + return; + } + + Point3F rot = mDelta.rot + mDelta.rotVec * dt; + + if((mMode == OrbitObjectMode || mMode == OrbitPointMode) && !mNewtonMode) + { + if(mMode == OrbitObjectMode && bool(mOrbitObject)) + { + // If this is a shapebase, use its render eye transform + // to avoid jittering. + GameBase *castObj = mOrbitObject; + ShapeBase* shape = dynamic_cast(castObj); + if( shape != NULL ) + { + MatrixF ret; + shape->getRenderEyeTransform( &ret ); + mPosition = ret.getPosition(); + } + else + { + // Hopefully this is a static object that doesn't move, + // because the worldbox doesn't get updated between ticks. + mOrbitObject->getWorldBox().getCenter(&mPosition); + } + } + _setRenderPosition( mPosition + mOffset, rot ); + _validateEyePoint( 1.0f, &mRenderObjToWorld ); + } + else if(mMode == EditOrbitMode && mValidEditOrbitPoint) + { + mPosition = mEditOrbitPoint; + _setRenderPosition(mPosition, rot); + _calcEditOrbitPoint(&mRenderObjToWorld, rot); + } + else if(mMode == TrackObjectMode && bool(mOrbitObject) && !mNewtonRotation) + { + // orient the camera to face the object + Point3F objPos; + // If this is a shapebase, use its render eye transform + // to avoid jittering. + ShapeBase *shape = dynamic_cast((GameBase*)mOrbitObject); + if( shape != NULL ) + { + MatrixF ret; + shape->getRenderEyeTransform( &ret ); + objPos = ret.getPosition(); + } + else + { + mOrbitObject->getWorldBox().getCenter(&objPos); + } + Point3F pos = mDelta.pos + mDelta.posVec * dt; + Point3F vec = objPos - pos; + vec.normalizeSafe(); + F32 pitch, yaw; + MathUtils::getAnglesFromVector(vec, yaw, pitch); + rot.x = -pitch; + rot.z = yaw; + _setRenderPosition(pos, rot); + } + else + { + Point3F pos = mDelta.pos + mDelta.posVec * dt; + _setRenderPosition(pos,rot); + if(mMode == OrbitObjectMode || mMode == OrbitPointMode) + _validateEyePoint(1.0f, &mRenderObjToWorld); + } +} + +//---------------------------------------------------------------------------- + +void Camera::_setPosition(const Point3F& pos, const Point3F& rot) +{ + MatrixF xRot, zRot; + xRot.set(EulerF(rot.x, 0.0f, 0.0f)); + zRot.set(EulerF(0.0f, 0.0f, rot.z)); + + MatrixF temp; + temp.mul(zRot, xRot); + temp.setColumn(3, pos); + Parent::setTransform(temp); + mRot = rot; +} + +//---------------------------------------------------------------------------- + +void Camera::setRotation(const Point3F& rot) +{ + MatrixF xRot, zRot; + xRot.set(EulerF(rot.x, 0.0f, 0.0f)); + zRot.set(EulerF(0.0f, 0.0f, rot.z)); + + MatrixF temp; + temp.mul(zRot, xRot); + temp.setColumn(3, getPosition()); + Parent::setTransform(temp); + mRot = rot; +} + +//---------------------------------------------------------------------------- + +void Camera::_setRenderPosition(const Point3F& pos,const Point3F& rot) +{ + MatrixF xRot, zRot; + xRot.set(EulerF(rot.x, 0, 0)); + zRot.set(EulerF(0, 0, rot.z)); + MatrixF temp; + temp.mul(zRot, xRot); + temp.setColumn(3, pos); + Parent::setRenderTransform(temp); +} + +//---------------------------------------------------------------------------- + +void Camera::writePacketData(GameConnection *connection, BitStream *bstream) +{ + // Update client regardless of status flags. + Parent::writePacketData(connection, bstream); + + Point3F pos; + mObjToWorld.getColumn(3,&pos); + bstream->setCompressionPoint(pos); + mathWrite(*bstream, pos); + bstream->write(mRot.x); + bstream->write(mRot.z); + + U32 writeMode = mMode; + Point3F writePos = mPosition; + S32 gIndex = -1; + if(mMode == OrbitObjectMode) + { + gIndex = bool(mOrbitObject) ? connection->getGhostIndex(mOrbitObject): -1; + if(gIndex == -1) + { + writeMode = OrbitPointMode; + if(bool(mOrbitObject)) + mOrbitObject->getWorldBox().getCenter(&writePos); + } + } + else if(mMode == TrackObjectMode) + { + gIndex = bool(mOrbitObject) ? connection->getGhostIndex(mOrbitObject): -1; + if(gIndex == -1) + writeMode = StationaryMode; + } + bstream->writeRangedU32(writeMode, CameraFirstMode, CameraLastMode); + + if (writeMode == OrbitObjectMode || writeMode == OrbitPointMode) + { + bstream->write(mMinOrbitDist); + bstream->write(mMaxOrbitDist); + bstream->write(mCurOrbitDist); + if(writeMode == OrbitObjectMode) + { + bstream->writeFlag(mObservingClientObject); + bstream->writeInt(gIndex, NetConnection::GhostIdBitSize); + } + if (writeMode == OrbitPointMode) + bstream->writeCompressedPoint(writePos); + } + else if(writeMode == TrackObjectMode) + { + bstream->writeInt(gIndex, NetConnection::GhostIdBitSize); + } + + if(bstream->writeFlag(mNewtonMode)) + { + bstream->write(mVelocity.x); + bstream->write(mVelocity.y); + bstream->write(mVelocity.z); + } + if(bstream->writeFlag(mNewtonRotation)) + { + bstream->write(mAngularVelocity.x); + bstream->write(mAngularVelocity.y); + bstream->write(mAngularVelocity.z); + } + + bstream->writeFlag(mValidEditOrbitPoint); + if(writeMode == EditOrbitMode) + { + bstream->write(mEditOrbitPoint.x); + bstream->write(mEditOrbitPoint.y); + bstream->write(mEditOrbitPoint.z); + bstream->write(mCurrentEditOrbitDist); + } +} + +//---------------------------------------------------------------------------- + +void Camera::readPacketData(GameConnection *connection, BitStream *bstream) +{ + Parent::readPacketData(connection, bstream); + Point3F pos,rot; + mathRead(*bstream, &pos); + bstream->setCompressionPoint(pos); + bstream->read(&rot.x); + bstream->read(&rot.z); + + GameBase* obj = 0; + mMode = (CameraMotionMode)bstream->readRangedU32(CameraFirstMode, CameraLastMode); + mObservingClientObject = false; + if (mMode == OrbitObjectMode || mMode == OrbitPointMode) + { + bstream->read(&mMinOrbitDist); + bstream->read(&mMaxOrbitDist); + bstream->read(&mCurOrbitDist); + + if(mMode == OrbitObjectMode) + { + mObservingClientObject = bstream->readFlag(); + S32 gIndex = bstream->readInt(NetConnection::GhostIdBitSize); + obj = static_cast(connection->resolveGhost(gIndex)); + } + if (mMode == OrbitPointMode) + bstream->readCompressedPoint(&mPosition); + } + else if (mMode == TrackObjectMode) + { + S32 gIndex = bstream->readInt(NetConnection::GhostIdBitSize); + obj = static_cast(connection->resolveGhost(gIndex)); + } + + if (obj != (GameBase*)mOrbitObject) + { + if (mOrbitObject) + { + clearProcessAfter(); + clearNotify(mOrbitObject); + } + + mOrbitObject = obj; + if (mOrbitObject) + { + processAfter(mOrbitObject); + deleteNotify(mOrbitObject); + } + } + + mNewtonMode = bstream->readFlag(); + if(mNewtonMode) + { + bstream->read(&mVelocity.x); + bstream->read(&mVelocity.y); + bstream->read(&mVelocity.z); + } + + mNewtonRotation = bstream->readFlag(); + if(mNewtonRotation) + { + bstream->read(&mAngularVelocity.x); + bstream->read(&mAngularVelocity.y); + bstream->read(&mAngularVelocity.z); + } + + mValidEditOrbitPoint = bstream->readFlag(); + if(mMode == EditOrbitMode) + { + bstream->read(&mEditOrbitPoint.x); + bstream->read(&mEditOrbitPoint.y); + bstream->read(&mEditOrbitPoint.z); + bstream->read(&mCurrentEditOrbitDist); + } + + _setPosition(pos,rot); + // Movement in OrbitObjectMode is not input-based - don't reset interpolation + if(mMode != OrbitObjectMode) + { + mDelta.pos = pos; + mDelta.posVec.set(0.0f, 0.0f, 0.0f); + mDelta.rot = rot; + mDelta.rotVec.set(0.0f, 0.0f, 0.0f); + } +} + +//---------------------------------------------------------------------------- + +U32 Camera::packUpdate(NetConnection *con, U32 mask, BitStream *bstream) +{ + Parent::packUpdate(con, mask, bstream); + + if (bstream->writeFlag(mask & UpdateMask)) + { + bstream->writeFlag(mLocked); + mathWrite(*bstream, mOffset); + } + + if(bstream->writeFlag(mask & NewtonCameraMask)) + { + bstream->write(mAngularForce); + bstream->write(mAngularDrag); + bstream->write(mMass); + bstream->write(mDrag); + bstream->write(mFlyForce); + bstream->write(mSpeedMultiplier); + bstream->write(mBrakeMultiplier); + } + + if(bstream->writeFlag(mask & EditOrbitMask)) + { + bstream->write(mEditOrbitPoint.x); + bstream->write(mEditOrbitPoint.y); + bstream->write(mEditOrbitPoint.z); + bstream->write(mCurrentEditOrbitDist); + } + + // The rest of the data is part of the control object packet update. + // If we're controlled by this client, we don't need to send it. + if(bstream->writeFlag(getControllingClient() == con && !(mask & InitialUpdateMask))) + return 0; + + if (bstream->writeFlag(mask & MoveMask)) + { + Point3F pos; + mObjToWorld.getColumn(3,&pos); + bstream->write(pos.x); + bstream->write(pos.y); + bstream->write(pos.z); + bstream->write(mRot.x); + bstream->write(mRot.z); + + // Only required if in NewtonFlyMode + F32 len = mVelocity.len(); + if(bstream->writeFlag(mNewtonMode && len > 0.02f)) + { + Point3F outVel = mVelocity; + outVel *= 1.0f/len; + bstream->writeNormalVector(outVel, 10); + len *= 32.0f; // 5 bits of fraction + if(len > 8191) + len = 8191; + bstream->writeInt((S32)len, 13); + } + + // Rotation + len = mAngularVelocity.len(); + if(bstream->writeFlag(mNewtonRotation && len > 0.02f)) + { + Point3F outVel = mAngularVelocity; + outVel *= 1.0f/len; + bstream->writeNormalVector(outVel, 10); + len *= 32.0f; // 5 bits of fraction + if(len > 8191) + len = 8191; + bstream->writeInt((S32)len, 13); + } + } + + return 0; +} + +//---------------------------------------------------------------------------- + +void Camera::unpackUpdate(NetConnection *con, BitStream *bstream) +{ + Parent::unpackUpdate(con,bstream); + + if (bstream->readFlag()) + { + mLocked = bstream->readFlag(); + mathRead(*bstream, &mOffset); + } + + // NewtonCameraMask + if(bstream->readFlag()) + { + bstream->read(&mAngularForce); + bstream->read(&mAngularDrag); + bstream->read(&mMass); + bstream->read(&mDrag); + bstream->read(&mFlyForce); + bstream->read(&mSpeedMultiplier); + bstream->read(&mBrakeMultiplier); + } + + // EditOrbitMask + if(bstream->readFlag()) + { + bstream->read(&mEditOrbitPoint.x); + bstream->read(&mEditOrbitPoint.y); + bstream->read(&mEditOrbitPoint.z); + bstream->read(&mCurrentEditOrbitDist); + } + + // controlled by the client? + if(bstream->readFlag()) + return; + + // MoveMask + if (bstream->readFlag()) + { + Point3F pos,rot; + bstream->read(&pos.x); + bstream->read(&pos.y); + bstream->read(&pos.z); + bstream->read(&rot.x); + bstream->read(&rot.z); + _setPosition(pos,rot); + + // NewtonMode + if(bstream->readFlag()) + { + bstream->readNormalVector(&mVelocity, 10); + mVelocity *= bstream->readInt(13) / 32.0f; + } + + // NewtonRotation + mNewtonRotation = bstream->readFlag(); + if(mNewtonRotation) + { + bstream->readNormalVector(&mAngularVelocity, 10); + mAngularVelocity *= bstream->readInt(13) / 32.0f; + } + + if(mMode != OrbitObjectMode) + { + // New delta for client side interpolation + mDelta.pos = pos; + mDelta.rot = rot; + mDelta.posVec = mDelta.rotVec = VectorF(0.0f, 0.0f, 0.0f); + } + } +} + +//---------------------------------------------------------------------------- + +void Camera::initPersistFields() +{ + addGroup( "Camera" ); + addProtectedField( "controlMode", TYPEID< CameraMotionMode >(), Offset( mMode, Camera ), + &_setModeField, &defaultProtectedGetFn, + "The current camera control mode." ); + endGroup( "Camera" ); + + addGroup( "Camera: Newton Mode" ); + addField( "newtonMode", TypeBool, Offset(mNewtonMode, Camera), + "Apply smoothing (acceleration and damping) to camera movements." ); + addField( "newtonRotation", TypeBool, Offset(mNewtonRotation, Camera), + "Apply smoothing (acceleration and damping) to camera rotations." ); + addProtectedField( "mass", TypeF32, Offset(mMass, Camera), &_setNewtonField, &defaultProtectedGetFn, + "The camera's mass (Newton mode only). Default value is 10." ); + addProtectedField( "drag", TypeF32, Offset(mDrag, Camera), &_setNewtonField, &defaultProtectedGetFn, + "Drag on camera when moving (Newton mode only). Default value is 2." ); + addProtectedField( "force", TypeF32, Offset(mFlyForce, Camera), &_setNewtonField, &defaultProtectedGetFn, + "Force applied on camera when asked to move (Newton mode only). Default value is 500." ); + addProtectedField( "angularDrag", TypeF32, Offset(mAngularDrag, Camera), &_setNewtonField, &defaultProtectedGetFn, + "Drag on camera when rotating (Newton mode only). Default value is 2." ); + addProtectedField( "angularForce", TypeF32, Offset(mAngularForce, Camera), &_setNewtonField, &defaultProtectedGetFn, + "Force applied on camera when asked to rotate (Newton mode only). Default value is 100." ); + addProtectedField( "speedMultiplier", TypeF32, Offset(mSpeedMultiplier, Camera), &_setNewtonField, &defaultProtectedGetFn, + "Speed multiplier when triggering the accelerator (Newton mode only). Default value is 2." ); + addProtectedField( "brakeMultiplier", TypeF32, Offset(mBrakeMultiplier, Camera), &_setNewtonField, &defaultProtectedGetFn, + "Speed multiplier when triggering the brake (Newton mode only). Default value is 2." ); + endGroup( "Camera: Newton Mode" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +void Camera::consoleInit() +{ + Con::addVariable("$Camera::movementSpeed", TypeF32, &smMovementSpeed, + "@brief Global camera movement speed in units/s (typically m/s), with a base value of 40.\n\n" + "Used in the following camera modes:\n" + "- Edit Orbit Mode\n" + "- Fly Mode\n" + "- Overhead Mode\n" + "@ingroup BaseCamera\n"); +} + +//----------------------------------------------------------------------------- + +bool Camera::_setNewtonField( void *object, const char *index, const char *data ) +{ + static_cast(object)->setMaskBits(NewtonCameraMask); + return true; // ok to set value +} + +//----------------------------------------------------------------------------- + +bool Camera::_setModeField( void *object, const char *index, const char *data ) +{ + Camera *cam = static_cast( object ); + + if( dStricmp(data, "Fly") == 0 ) + { + cam->setFlyMode(); + return false; // already changed mode + } + + else if( dStricmp(data, "EditOrbit" ) == 0 ) + { + cam->setEditOrbitMode(); + return false; // already changed mode + } + + else if( (dStricmp(data, "OrbitObject") == 0 && cam->mMode != OrbitObjectMode) || + (dStricmp(data, "TrackObject") == 0 && cam->mMode != TrackObjectMode) || + (dStricmp(data, "OrbitPoint") == 0 && cam->mMode != OrbitPointMode) ) + { + Con::warnf("Couldn't change Camera mode to %s: required information missing. Use camera.set%s().", data, data); + return false; // don't change the mode - not valid + } + + else if( dStricmp(data, "OrbitObject") != 0 && + dStricmp(data, "TrackObject") != 0 && + bool(cam->mOrbitObject) ) + { + cam->clearProcessAfter(); + cam->clearNotify(cam->mOrbitObject); + cam->mOrbitObject = NULL; + } + + // make sure the requested mode is supported, and set it + // NOTE: The _CameraMode namespace is generated by ImplementEnumType above + + const EngineEnumTable& enums = *( TYPE< CameraMotionMode >()->getEnumTable() ); + const U32 numValues = enums.getNumValues(); + + for( S32 i = 0; i < numValues; ++i) + { + if( dStricmp(data, enums[i].mName) == 0 ) + { + cam->mMode = (CameraMotionMode) enums[i].mInt; + return false; + } + } + + Con::warnf("Unsupported camera mode: %s", data); + return false; +} + +//----------------------------------------------------------------------------- + +Point3F Camera::getPosition() +{ + static Point3F position; + mObjToWorld.getColumn(3, &position); + return position; +} + +//----------------------------------------------------------------------------- + +void Camera::setFlyMode() +{ + mMode = FlyMode; + + if (bool(mOrbitObject)) + { + clearProcessAfter(); + clearNotify(mOrbitObject); + } + mOrbitObject = NULL; +} + +//----------------------------------------------------------------------------- + +void Camera::setNewtonFlyMode() +{ + mNewtonMode = true; + setFlyMode(); +} + +//----------------------------------------------------------------------------- + +void Camera::setOrbitMode(GameBase *obj, const Point3F &pos, const Point3F &rot, const Point3F& offset, F32 minDist, F32 maxDist, F32 curDist, bool ownClientObject, bool locked) +{ + mObservingClientObject = ownClientObject; + + if (bool(mOrbitObject)) + { + clearProcessAfter(); + clearNotify(mOrbitObject); + } + + mOrbitObject = obj; + if(bool(mOrbitObject)) + { + processAfter(mOrbitObject); + deleteNotify(mOrbitObject); + mOrbitObject->getWorldBox().getCenter(&mPosition); + mMode = OrbitObjectMode; + } + else + { + mMode = OrbitPointMode; + mPosition = pos; + } + + _setPosition(mPosition, rot); + + mMinOrbitDist = minDist; + mMaxOrbitDist = maxDist; + mCurOrbitDist = curDist; + + if (locked != mLocked || mOffset != offset) + { + mLocked = locked; + mOffset = offset; + setMaskBits(UpdateMask); + } +} + +//----------------------------------------------------------------------------- + +void Camera::setTrackObject(GameBase *obj, const Point3F &offset) +{ + if(bool(mOrbitObject)) + { + clearProcessAfter(); + clearNotify(mOrbitObject); + } + mOrbitObject = obj; + if(bool(mOrbitObject)) + { + processAfter(mOrbitObject); + deleteNotify(mOrbitObject); + } + + mOffset = offset; + mMode = TrackObjectMode; + + setMaskBits( UpdateMask ); +} + +//----------------------------------------------------------------------------- + +void Camera::_validateEyePoint(F32 pos, MatrixF *mat) +{ + if (pos != 0) + { + // Use the eye transform to orient the camera + Point3F dir; + mat->getColumn(1, &dir); + if (mMaxOrbitDist - mMinOrbitDist > 0.0f) + pos *= mMaxOrbitDist - mMinOrbitDist; + + // Use the camera node's pos. + Point3F startPos = getRenderPosition(); + Point3F endPos; + + // Make sure we don't extend the camera into anything solid + if(mOrbitObject) + mOrbitObject->disableCollision(); + disableCollision(); + RayInfo collision; + U32 mask = TerrainObjectType | + InteriorObjectType | + WaterObjectType | + StaticShapeObjectType | + PlayerObjectType | + ItemObjectType | + VehicleObjectType; + + SceneContainer* pContainer = isServerObject() ? &gServerContainer : &gClientContainer; + if (!pContainer->castRay(startPos, startPos - dir * 2.5 * pos, mask, &collision)) + endPos = startPos - dir * pos; + else + { + float dot = mDot(dir, collision.normal); + if (dot > 0.01f) + { + float colDist = mDot(startPos - collision.point, dir) - (1 / dot) * CameraRadius; + if (colDist > pos) + colDist = pos; + if (colDist < 0.0f) + colDist = 0.0f; + endPos = startPos - dir * colDist; + } + else + endPos = startPos - dir * pos; + } + mat->setColumn(3, endPos); + enableCollision(); + if(mOrbitObject) + mOrbitObject->enableCollision(); + } +} + +//----------------------------------------------------------------------------- + +void Camera::setTransform(const MatrixF& mat) +{ + // This method should never be called on the client. + + // This currently converts all rotation in the mat into + // rotations around the z and x axis. + Point3F pos,vec; + mat.getColumn(1, &vec); + mat.getColumn(3, &pos); + Point3F rot(-mAtan2(vec.z, mSqrt(vec.x * vec.x + vec.y * vec.y)), 0.0f, -mAtan2(-vec.x, vec.y)); + _setPosition(pos,rot); +} + +//----------------------------------------------------------------------------- + +void Camera::setRenderTransform(const MatrixF& mat) +{ + // This method should never be called on the client. + + // This currently converts all rotation in the mat into + // rotations around the z and x axis. + Point3F pos,vec; + mat.getColumn(1,&vec); + mat.getColumn(3,&pos); + Point3F rot(-mAtan2(vec.z, mSqrt(vec.x*vec.x + vec.y*vec.y)),0,-mAtan2(-vec.x,vec.y)); + _setRenderPosition(pos,rot); +} + +//----------------------------------------------------------------------------- + +F32 Camera::getDamageFlash() const +{ + if (mMode == OrbitObjectMode && isServerObject() && bool(mOrbitObject)) + { + const GameBase *castObj = mOrbitObject; + const ShapeBase* psb = dynamic_cast(castObj); + if (psb) + return psb->getDamageFlash(); + } + + return mDamageFlash; +} + +//----------------------------------------------------------------------------- + +F32 Camera::getWhiteOut() const +{ + if (mMode == OrbitObjectMode && isServerObject() && bool(mOrbitObject)) + { + const GameBase *castObj = mOrbitObject; + const ShapeBase* psb = dynamic_cast(castObj); + if (psb) + return psb->getWhiteOut(); + } + + return mWhiteOut; +} + +//----------------------------------------------------------------------------- + +void Camera::setVelocity(const VectorF& vel) +{ + mVelocity = vel; + setMaskBits(MoveMask); +} + +//----------------------------------------------------------------------------- + +void Camera::setAngularVelocity(const VectorF& vel) +{ + mAngularVelocity = vel; + setMaskBits(MoveMask); +} + +//----------------------------------------------------------------------------- + +void Camera::setEditOrbitMode() +{ + mMode = EditOrbitMode; + + if (bool(mOrbitObject)) + { + clearProcessAfter(); + clearNotify(mOrbitObject); + } + mOrbitObject = NULL; + + // If there is a valid orbit point, then point to it + // rather than move the camera. + if(mValidEditOrbitPoint) + { + Point3F currentPos; + mObjToWorld.getColumn(3,¤tPos); + + Point3F dir = mEditOrbitPoint - currentPos; + mCurrentEditOrbitDist = dir.len(); + dir.normalize(); + + F32 yaw, pitch; + MathUtils::getAnglesFromVector(dir, yaw, pitch); + mRot.x = -pitch; + mRot.z = yaw; + } +} + +//----------------------------------------------------------------------------- + +void Camera::_calcOrbitPoint( MatrixF* mat, const Point3F& rot ) +{ + Point3F pos; + + pos.x = mCurOrbitDist * mSin( rot.x + mDegToRad( 90.0f ) ) * mCos( -1.0f * ( rot.z + mDegToRad( 90.0f ) ) ) + mPosition.x + mOffset.x; + pos.y = mCurOrbitDist * mSin( rot.x + mDegToRad( 90.0f ) ) * mSin( -1.0f * ( rot.z + mDegToRad( 90.0f ) ) ) + mPosition.y + mOffset.y; + pos.z = mCurOrbitDist * mSin( rot.x ) + mPosition.z + mOffset.z; + + mat->setColumn( 3, pos ); +} + +//----------------------------------------------------------------------------- + +void Camera::_calcEditOrbitPoint( MatrixF *mat, const Point3F& rot ) +{ + //Point3F dir; + //mat->getColumn(1, &dir); + + //Point3F startPos = getRenderPosition(); + //Point3F endPos = startPos - dir * mCurrentEditOrbitDist; + + Point3F pos; + pos.x = mCurrentEditOrbitDist * mSin(rot.x + mDegToRad(90.0f)) * mCos(-1.0f * (rot.z + mDegToRad(90.0f))) + mEditOrbitPoint.x; + pos.y = mCurrentEditOrbitDist * mSin(rot.x + mDegToRad(90.0f)) * mSin(-1.0f * (rot.z + mDegToRad(90.0f))) + mEditOrbitPoint.y; + pos.z = mCurrentEditOrbitDist * mSin(rot.x) + mEditOrbitPoint.z; + + mat->setColumn(3, pos); +} + +//----------------------------------------------------------------------------- + +void Camera::setValidEditOrbitPoint( bool state ) +{ + mValidEditOrbitPoint = state; + setMaskBits(EditOrbitMask); +} + +//----------------------------------------------------------------------------- + +Point3F Camera::getEditOrbitPoint() const +{ + return mEditOrbitPoint; +} + +//----------------------------------------------------------------------------- + +void Camera::setEditOrbitPoint( const Point3F& pnt ) +{ + // Change the point that we orbit in EditOrbitMode. + // We'll also change the facing and distance of the + // camera so that it doesn't jump around. + Point3F currentPos; + mObjToWorld.getColumn(3,¤tPos); + + Point3F dir = pnt - currentPos; + mCurrentEditOrbitDist = dir.len(); + + if(mMode == EditOrbitMode) + { + dir.normalize(); + + F32 yaw, pitch; + MathUtils::getAnglesFromVector(dir, yaw, pitch); + mRot.x = -pitch; + mRot.z = yaw; + } + + mEditOrbitPoint = pnt; + + setMaskBits(EditOrbitMask); +} + +//---------------------------------------------------------------------------- + +void Camera::lookAt( const Point3F& pos ) +{ + Point3F vec; + mObjToWorld.getColumn(3, &mPosition); + vec = pos - mPosition; + vec.normalizeSafe(); + F32 pitch, yaw; + MathUtils::getAnglesFromVector(vec, yaw, pitch); + mRot.x = -pitch; + mRot.z = yaw; + _setPosition(mPosition, mRot); +} + +//---------------------------------------------------------------------------- + +void Camera::autoFitRadius( F32 radius ) +{ + F32 fov = mDegToRad( getCameraFov() ); + F32 viewradius = (radius * 2.0f) / mTan(fov * 0.5f); + + // Be careful of infinite sized objects. Clip to 16km + if(viewradius > 16000.0f) + viewradius = 16000.0f; + + if(mMode == EditOrbitMode && mValidEditOrbitPoint) + { + mCurrentEditOrbitDist = viewradius; + } + else if(mValidEditOrbitPoint) + { + mCurrentEditOrbitDist = viewradius; + + Point3F currentPos; + mObjToWorld.getColumn(3,¤tPos); + + Point3F dir = mEditOrbitPoint - currentPos; + dir.normalize(); + + F32 yaw, pitch; + MathUtils::getAnglesFromVector(dir, yaw, pitch); + mRot.x = -pitch; + mRot.z = yaw; + + mPosition = mEditOrbitPoint; + _setPosition(mPosition, mRot); + _calcEditOrbitPoint(&mObjToWorld, mRot); + } +} + +//============================================================================= +// Console API. +//============================================================================= +// MARK: ---- Console API ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Camera, getMode, Camera::CameraMotionMode, (), , + "Returns the current camera control mode.\n\n" + "@see CameraMotionMode") +{ + return object->getMode(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Camera, getPosition, Point3F, (), , + "Get the camera's position in the world.\n\n" + "@returns The position in the form of \"x y z\".") +{ + return object->getPosition(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Camera, getRotation, Point3F, (), , + "Get the camera's Euler rotation in radians.\n\n" + "@returns The rotation in radians in the form of \"x y z\".") +{ + return object->getRotation(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Camera, setRotation, void, ( Point3F rot ), , + "Set the camera's Euler rotation in radians.\n\n" + "@param rot The rotation in radians in the form of \"x y z\"." + "@note Rotation around the Y axis is ignored" ) +{ + return object->setRotation( rot ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Camera, getOffset, Point3F, (), , + "Get the camera's offset from its orbit or tracking point.\n\n" + "The offset is added to the camera's position when set to CameraMode::OrbitObject.\n" + "@returns The offset in the form of \"x y z\".") +{ + return object->getOffset(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Camera, setOffset, void, (Point3F offset), , + "Set the camera's offset.\n\n" + "The offset is added to the camera's position when set to CameraMode::OrbitObject.\n" + "@param offset The distance to offset the camera by in the form of \"x y z\".") +{ + object->setOffset(offset); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Camera, setOrbitMode, void, (GameBase* orbitObject, TransformF orbitPoint, F32 minDistance, F32 maxDistance, + F32 initDistance, bool ownClientObj, Point3F offset, bool locked), (false, Point3F(0.0f, 0.0f, 0.0f), false), + "Set the camera to orbit around the given object, or if none is given, around the given point.\n\n" + "@param orbitObject The object to orbit around. If no object is given (0 or blank string is passed in) use the orbitPoint instead\n" + "@param orbitPoint The point to orbit around when no object is given. In the form of \"x y z ax ay az aa\" such as returned by SceneObject::getTransform().\n" + "@param minDistance The minimum distance allowed to the orbit object or point.\n" + "@param maxDistance The maximum distance allowed from the orbit object or point.\n" + "@param initDistance The initial distance from the orbit object or point.\n" + "@param ownClientObj [optional] Are we orbiting an object that is owned by us? Default is false.\n" + "@param offset [optional] An offset added to the camera's position. Default is no offset.\n" + "@param locked [optional] Indicates the camera does not receive input from the player. Default is false.\n" + "@see Camera::setOrbitObject()\n" + "@see Camera::setOrbitPoint()\n") +{ + MatrixF mat; + orbitPoint.mOrientation.setMatrix(&mat); + object->setOrbitMode(orbitObject, orbitPoint.mPosition, mat.toEuler(), offset, + minDistance, maxDistance, initDistance, ownClientObj, locked); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Camera, setOrbitObject, bool, (GameBase* orbitObject, VectorF rotation, F32 minDistance, + F32 maxDistance, F32 initDistance, bool ownClientObject, Point3F offset, bool locked), + (false, Point3F(0.0f, 0.0f, 0.0f), false), + "Set the camera to orbit around a given object.\n\n" + "@param orbitObject The object to orbit around.\n" + "@param rotation The initial camera rotation about the object in radians in the form of \"x y z\".\n" + "@param minDistance The minimum distance allowed to the orbit object or point.\n" + "@param maxDistance The maximum distance allowed from the orbit object or point.\n" + "@param initDistance The initial distance from the orbit object or point.\n" + "@param ownClientObject [optional] Are we orbiting an object that is owned by us? Default is false.\n" + "@param offset [optional] An offset added to the camera's position. Default is no offset.\n" + "@param locked [optional] Indicates the camera does not receive input from the player. Default is false.\n" + "@returns false if the given object could not be found.\n" + "@see Camera::setOrbitMode()\n") +{ + if( !orbitObject ) + { + Con::errorf( "Camera::setOrbitObject - Invalid object"); + return false; + } + + object->setOrbitMode(orbitObject, Point3F(0.0f, 0.0f, 0.0f), rotation, offset, + minDistance, maxDistance, initDistance, ownClientObject, locked); + return true; +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Camera, setOrbitPoint, void, (TransformF orbitPoint, F32 minDistance, F32 maxDistance, F32 initDistance, + Point3F offset, bool locked), + (Point3F(0.0f, 0.0f, 0.0f), false), + "Set the camera to orbit around a given point.\n\n" + "@param orbitPoint The point to orbit around. In the form of \"x y z ax ay az aa\" such as returned by SceneObject::getTransform().\n" + "@param minDistance The minimum distance allowed to the orbit object or point.\n" + "@param maxDistance The maximum distance allowed from the orbit object or point.\n" + "@param initDistance The initial distance from the orbit object or point.\n" + "@param offset [optional] An offset added to the camera's position. Default is no offset.\n" + "@param locked [optional] Indicates the camera does not receive input from the player. Default is false.\n" + "@see Camera::setOrbitMode()\n") +{ + MatrixF mat; + orbitPoint.mOrientation.setMatrix(&mat); + object->setOrbitMode(NULL, orbitPoint.mPosition, mat.toEuler(), offset, + minDistance, maxDistance, initDistance, false, locked); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Camera, setTrackObject, bool, (GameBase* trackObject, Point3F offset), (Point3F(0.0f, 0.0f, 0.0f)), + "Set the camera to track a given object.\n\n" + "@param trackObject The object to track.\n" + "@param offset [optional] An offset added to the camera's position. Default is no offset.\n" + "@returns false if the given object could not be found.\n") +{ + if(!trackObject) + { + Con::warnf("Cannot track non-existing object."); + return false; + } + + object->setTrackObject(trackObject, offset); + return true; +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Camera, setEditOrbitMode, void, (), , + "Set the editor camera to orbit around a point set with Camera::setEditOrbitPoint().\n\n" + "@note This method is generally used only within the World Editor and other tools. To " + "orbit about an object or point within a game, see Camera::setOrbitMode() and its helper methods.\n") +{ + object->setEditOrbitMode(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Camera, setFlyMode, void, (), , + "Set the camera to fly freely.\n\n" + "Allows the camera to have 6 degrees of freedom. Provides for instantaneous motion " + "and rotation unless one of the Newton fields has been set to true. See Camera::newtonMode " + "and Camera::newtonRotation.\n") +{ + object->setFlyMode(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Camera, setNewtonFlyMode, void, (), , + "Set the camera to fly freely, but with ease-in and ease-out.\n\n" + "This method allows for the same 6 degrees of freedom as Camera::setFlyMode() but " + "activates the ease-in and ease-out on the camera's movement. To also activate " + "Newton mode for the camera's rotation, set Camera::newtonRotation to true.") +{ + object->setNewtonFlyMode(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Camera, isRotationDamped, bool, (), , + "Is this a Newton Fly mode camera with damped rotation?\n\n" + "@returns true if the camera uses a damped rotation. i.e. Camera::newtonRotation is set to true.\n") +{ + return object->isRotationDamped(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Camera, getAngularVelocity, VectorF, (), , + "Get the angular velocity for a Newton mode camera.\n\n" + "@returns The angular velocity in the form of \"x y z\".\n" + "@note Only returns useful results when Camera::newtonRotation is set to true.") +{ + return object->getAngularVelocity(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Camera, setAngularVelocity, void, (VectorF velocity), , + "Set the angular velocity for a Newton mode camera.\n\n" + "@param velocity The angular velocity infor form of \"x y z\".\n" + "@note Only takes affect when Camera::newtonRotation is set to true.") +{ + object->setAngularVelocity(velocity); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Camera, setAngularForce, void, (F32 force), , + "Set the angular force for a Newton mode camera.\n\n" + "@param force The angular force applied when attempting to rotate the camera." + "@note Only takes affect when Camera::newtonRotation is set to true.") +{ + object->setAngularForce(force); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Camera, setAngularDrag, void, (F32 drag), , + "Set the angular drag for a Newton mode camera.\n\n" + "@param drag The angular drag applied while the camera is rotating." + "@note Only takes affect when Camera::newtonRotation is set to true.") +{ + object->setAngularDrag(drag); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Camera, setMass, void, (F32 mass), , + "Set the mass for a Newton mode camera.\n\n" + "@param mass The mass used during ease-in and ease-out calculations." + "@note Only used when Camera is in Newton mode.") +{ + object->setMass(mass); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Camera, getVelocity, VectorF, (), , + "Get the velocity for the camera.\n\n" + "@returns The camera's velocity in the form of \"x y z\"." + "@note Only useful when the Camera is in Newton mode.") +{ + return object->getVelocity(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Camera, setVelocity, void, (VectorF velocity), , + "Set the velocity for the camera.\n\n" + "@param velocity The camera's velocity in the form of \"x y z\"." + "@note Only affects the Camera when in Newton mode.") +{ + object->setVelocity(velocity); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Camera, setDrag, void, (F32 drag), , + "Set the drag for a Newton mode camera.\n\n" + "@param drag The drag applied to the camera while moving." + "@note Only used when Camera is in Newton mode.") +{ + object->setDrag(drag); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Camera, setFlyForce, void, (F32 force), , + "Set the force applied to a Newton mode camera while moving.\n\n" + "@param force The force applied to the camera while attempting to move." + "@note Only used when Camera is in Newton mode.") +{ + object->setFlyForce(force); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Camera, setSpeedMultiplier, void, (F32 multiplier), , + "Set the Newton mode camera speed multiplier when trigger[0] is active.\n\n" + "@param multiplier The speed multiplier to apply." + "@note Only used when Camera is in Newton mode.") +{ + object->setSpeedMultiplier(multiplier); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Camera, setBrakeMultiplier, void, (F32 multiplier), , + "Set the Newton mode camera brake multiplier when trigger[1] is active.\n\n" + "@param multiplier The brake multiplier to apply." + "@note Only used when Camera is in Newton mode.") +{ + object->setBrakeMultiplier(multiplier); +} + +//---------------------------------------------------------------------------- + +DefineEngineMethod( Camera, isEditOrbitMode, bool, (), , + "Is the camera in edit orbit mode?\n\n" + "@returns true if the camera is in edit orbit mode.") +{ + return object->isEditOrbitMode(); +} + +//---------------------------------------------------------------------------- + +DefineEngineMethod( Camera, setValidEditOrbitPoint, void, (bool validPoint), , + "Set if there is a valid editor camera orbit point.\n" + "When validPoint is set to false the Camera operates as if it is " + "in Fly mode rather than an Orbit mode.\n\n" + "@param validPoint Indicates the validity of the orbit point." + "@note Only used when Camera is in Edit Orbit Mode.") +{ + object->setValidEditOrbitPoint(validPoint); +} + +//---------------------------------------------------------------------------- + +DefineEngineMethod( Camera, setEditOrbitPoint, void, (Point3F point), , + "Set the editor camera's orbit point.\n\n" + "@param point The point the camera will orbit in the form of \"x y z\".") +{ + object->setEditOrbitPoint(point); +} + +//---------------------------------------------------------------------------- + +DefineEngineMethod( Camera, autoFitRadius, void, (F32 radius), , + "Move the camera to fully view the given radius.\n\n" + "@note For this operation to take affect a valid edit orbit point must first be specified. See Camera::setEditOrbitPoint().\n" + "@param radius The radius to view.") +{ + object->autoFitRadius(radius); +} + +//---------------------------------------------------------------------------- + +DefineEngineMethod( Camera, lookAt, void, (Point3F point), , + "Point the camera at the specified position. Does not work in Orbit or Track modes.\n\n" + "@param point The position to point the camera at.") +{ + object->lookAt(point); +} diff --git a/Engine/source/T3D/camera.h b/Engine/source/T3D/camera.h new file mode 100644 index 000000000..1d39e2875 --- /dev/null +++ b/Engine/source/T3D/camera.h @@ -0,0 +1,243 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _CAMERA_H_ +#define _CAMERA_H_ + +#ifndef _SHAPEBASE_H_ +#include "T3D/shapeBase.h" +#endif + +#ifndef _DYNAMIC_CONSOLETYPES_H_ +#include "console/dynamicTypes.h" +#endif + + +class CameraData: public ShapeBaseData +{ + public: + + typedef ShapeBaseData Parent; + + // ShapeBaseData. + DECLARE_CONOBJECT( CameraData ); + DECLARE_CATEGORY( "Game" ); + DECLARE_DESCRIPTION( "A datablock that describes a camera." ); + + static void initPersistFields(); + virtual void packData(BitStream* stream); + virtual void unpackData(BitStream* stream); +}; + + +/// Implements a basic camera object. +class Camera: public ShapeBase +{ + public: + + typedef ShapeBase Parent; + + /// Movement behavior type for camera. + enum CameraMotionMode + { + StationaryMode = 0, + + FreeRotateMode, + FlyMode, + OrbitObjectMode, + OrbitPointMode, + TrackObjectMode, + OverheadMode, + EditOrbitMode, ///< Used by the World Editor + + CameraFirstMode = 0, + CameraLastMode = EditOrbitMode + }; + + protected: + + enum MaskBits + { + MoveMask = Parent::NextFreeMask, + UpdateMask = Parent::NextFreeMask << 1, + NewtonCameraMask = Parent::NextFreeMask << 2, + EditOrbitMask = Parent::NextFreeMask << 3, + NextFreeMask = Parent::NextFreeMask << 4 + }; + + struct StateDelta + { + Point3F pos; + Point3F rot; + VectorF posVec; + VectorF rotVec; + }; + + Point3F mRot; + StateDelta mDelta; + + Point3F mOffset; + + static F32 smMovementSpeed; + + SimObjectPtr mOrbitObject; + F32 mMinOrbitDist; + F32 mMaxOrbitDist; + F32 mCurOrbitDist; + Point3F mPosition; + bool mObservingClientObject; + + /// @name NewtonFlyMode + /// @{ + + VectorF mAngularVelocity; + F32 mAngularForce; + F32 mAngularDrag; + VectorF mVelocity; + bool mNewtonMode; + bool mNewtonRotation; + F32 mMass; + F32 mDrag; + F32 mFlyForce; + F32 mSpeedMultiplier; + F32 mBrakeMultiplier; + + /// @} + + /// @name EditOrbitMode + /// @{ + + bool mValidEditOrbitPoint; + Point3F mEditOrbitPoint; + F32 mCurrentEditOrbitDist; + + /// @} + + bool mLocked; + + CameraMotionMode mMode; + + void _setPosition(const Point3F& pos,const Point3F& viewRot); + void _setRenderPosition(const Point3F& pos,const Point3F& viewRot); + void _validateEyePoint( F32 pos, MatrixF* mat ); + + void _calcOrbitPoint( MatrixF* mat, const Point3F& rot ); + void _calcEditOrbitPoint( MatrixF *mat, const Point3F& rot ); + + static bool _setModeField( void *object, const char *index, const char *data ); + static bool _setNewtonField( void *object, const char *index, const char *data ); + + // ShapeBase. + virtual F32 getCameraFov(); + virtual void setCameraFov( F32 fov ); + virtual F32 getDefaultCameraFov(); + virtual bool isValidCameraFov( F32 fov ); + virtual F32 getDamageFlash() const; + virtual F32 getWhiteOut() const; + virtual void setTransform( const MatrixF& mat ); + virtual void setRenderTransform( const MatrixF& mat ); + + public: + + Camera(); + ~Camera(); + + CameraMotionMode getMode() const { return mMode; } + + Point3F getPosition(); + Point3F getRotation() { return mRot; }; + void setRotation( const Point3F& viewRot ); + + Point3F getOffset() { return mOffset; }; + void lookAt( const Point3F& pos); + void setOffset( const Point3F& offset) { if( mOffset != offset ) mOffset = offset; setMaskBits( UpdateMask ); } + void setFlyMode(); + void setOrbitMode( GameBase *obj, const Point3F& pos, const Point3F& rot, const Point3F& offset, + F32 minDist, F32 maxDist, F32 curDist, bool ownClientObject, bool locked = false ); + void setTrackObject( GameBase *obj, const Point3F& offset); + void onDeleteNotify( SimObject* obj ); + + GameBase* getOrbitObject() { return(mOrbitObject); } + bool isObservingClientObject() { return(mObservingClientObject); } + + /// @name NewtonFlyMode + /// @{ + + void setNewtonFlyMode(); + VectorF getVelocity() const { return mVelocity; } + void setVelocity( const VectorF& vel ); + VectorF getAngularVelocity() const { return mAngularVelocity; } + void setAngularVelocity( const VectorF& vel ); + bool isRotationDamped() {return mNewtonRotation;} + void setAngularForce( F32 force ) {mAngularForce = force; setMaskBits(NewtonCameraMask);} + void setAngularDrag( F32 drag ) {mAngularDrag = drag; setMaskBits(NewtonCameraMask);} + void setMass( F32 mass ) {mMass = mass; setMaskBits(NewtonCameraMask);} + void setDrag( F32 drag ) {mDrag = drag; setMaskBits(NewtonCameraMask);} + void setFlyForce( F32 force ) {mFlyForce = force; setMaskBits(NewtonCameraMask);} + void setSpeedMultiplier( F32 mul ) {mSpeedMultiplier = mul; setMaskBits(NewtonCameraMask);} + void setBrakeMultiplier( F32 mul ) {mBrakeMultiplier = mul; setMaskBits(NewtonCameraMask);} + + /// @} + + /// @name EditOrbitMode + /// @{ + + void setEditOrbitMode(); + bool isEditOrbitMode() {return mMode == EditOrbitMode;} + bool getValidEditOrbitPoint() { return mValidEditOrbitPoint; } + void setValidEditOrbitPoint( bool state ); + Point3F getEditOrbitPoint() const; + void setEditOrbitPoint( const Point3F& pnt ); + + /// Orient the camera to view the given radius. Requires that an + /// edit orbit point has been set. + void autoFitRadius( F32 radius ); + + /// @} + + // ShapeBase. + static void initPersistFields(); + static void consoleInit(); + + virtual void onEditorEnable(); + virtual void onEditorDisable(); + + virtual bool onAdd(); + virtual void onRemove(); + virtual void processTick( const Move* move ); + virtual void interpolateTick( F32 delta); + virtual void getCameraTransform( F32* pos,MatrixF* mat ); + + virtual void writePacketData( GameConnection* conn, BitStream* stream ); + virtual void readPacketData( GameConnection* conn, BitStream* stream ); + virtual U32 packUpdate( NetConnection* conn, U32 mask, BitStream* stream ); + virtual void unpackUpdate( NetConnection* conn, BitStream* stream ); + + DECLARE_CONOBJECT( Camera ); + DECLARE_CATEGORY( "Game" ); + DECLARE_DESCRIPTION( "Represents a position, direction and field of view to render a scene from." ); +}; + +typedef Camera::CameraMotionMode CameraMotionMode; +DefineEnumType( CameraMotionMode ); + +#endif diff --git a/Engine/source/T3D/cameraSpline.cpp b/Engine/source/T3D/cameraSpline.cpp new file mode 100644 index 000000000..ba4bec72e --- /dev/null +++ b/Engine/source/T3D/cameraSpline.cpp @@ -0,0 +1,387 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + + +#include "T3D/cameraSpline.h" + +#include "console/console.h" +#include "gfx/gfxDevice.h" + + +//----------------------------------------------------------------------------- + +CameraSpline::Knot::Knot(const Knot &k) +{ + mPosition = k.mPosition; + mRotation = k.mRotation; + mSpeed = k.mSpeed; + mType = k.mType; + mPath = k.mPath; + prev = NULL; next = NULL; +} + +CameraSpline::Knot::Knot(const Point3F &p, const QuatF &r, F32 s, Knot::Type type, Knot::Path path) +{ + mPosition = p; + mRotation = r; + mSpeed = s; + mType = type; + mPath = path; + prev = NULL; next = NULL; +} + + +//----------------------------------------------------------------------------- + +CameraSpline::CameraSpline() +{ + mFront = NULL; + mSize = 0; + mIsMapDirty = true; + VECTOR_SET_ASSOCIATION(mTimeMap); +} + + +CameraSpline::~CameraSpline() +{ + removeAll(); +} + + +void CameraSpline::push_back(Knot *w) +{ + if (!mFront) + { + mFront = w; + w->next = w; + w->prev = w; + } + else + { + Knot *before = back(); + Knot *after = before->next; + + w->next = before->next; + w->prev = before; + after->prev = w; + before->next = w; + } + ++mSize; + mIsMapDirty = true; +} + +CameraSpline::Knot* CameraSpline::getKnot(S32 i) +{ + Knot *k = mFront; + while(i--) + k = k->next; + return k; +} + +CameraSpline::Knot* CameraSpline::remove(Knot *w) +{ + if (w->next == mFront && w->prev == mFront) + mFront = NULL; + else + { + w->prev->next = w->next; + w->next->prev = w->prev; + if (mFront == w) + mFront = w->next; + } + --mSize; + mIsMapDirty = true; + return w; +} + + +void CameraSpline::removeAll() +{ + while(front()) + delete remove(front()); + mSize = 0; +} + + +//----------------------------------------------------------------------------- + +static bool gBuilding = false; + +void CameraSpline::buildTimeMap() +{ + if (!mIsMapDirty) + return; + + gBuilding = true; + + mTimeMap.clear(); + mTimeMap.reserve(size()*3); // preallocate + + // Initial node and knot value.. + TimeMap map; + map.mTime = 0; + map.mDistance = 0; + mTimeMap.push_back(map); + + Knot ka,kj,ki; + value(0, &kj, true); + F32 length = 0.0f; + ka = kj; + + // Loop through the knots and add nodes. Nodes are added for every knot and + // whenever the spline length and segment length deviate by epsilon. + F32 epsilon = Con::getFloatVariable("CameraSpline::epsilon", 0.90f); + const F32 Step = 0.05f; + F32 lt = 0,time = 0; + do + { + if ((time += Step) > F32(mSize - 1)) + time = (F32)mSize - 1.0f; + + value(time, &ki, true); + length += (ki.mPosition - kj.mPosition).len(); + F32 segment = (ki.mPosition - ka.mPosition).len(); + + if ((segment / length) < epsilon || time == (mSize - 1) || mFloor(lt) != mFloor(time)) + { + map.mTime = time; + map.mDistance = length; + mTimeMap.push_back(map); + ka = ki; + } + kj = ki; + lt = time; + } + while (time < mSize - 1); + + mIsMapDirty = false; + + gBuilding = false; +} + + +//----------------------------------------------------------------------------- + +void CameraSpline::renderTimeMap() +{ + buildTimeMap(); + + gBuilding = true; + + // Build vertex buffer + GFXVertexBufferHandle vb; + vb.set(GFX, mTimeMap.size(), GFXBufferTypeVolatile); + vb.lock(); + + MRandomLCG random(1376312589 * (U32)this); + int index = 0; + for(Vector::iterator itr=mTimeMap.begin(); itr != mTimeMap.end(); itr++) + { + Knot a; + value(itr->mTime, &a, true); + + S32 cr = random.randI(0,255); + S32 cg = random.randI(0,255); + S32 cb = random.randI(0,255); + vb[index].color.set(cr, cg, cb); + vb[index].point.set(a.mPosition.x, a.mPosition.y, a.mPosition.z); + index++; + } + + gBuilding = false; + + vb.unlock(); + + // Render the buffer + GFX->pushWorldMatrix(); + GFX->disableShaders(); + GFX->setVertexBuffer(vb); + GFX->drawPrimitive(GFXLineStrip,0,index); + GFX->popWorldMatrix(); +} + + +//----------------------------------------------------------------------------- + +F32 CameraSpline::advanceTime(F32 t, S32 delta_ms) +{ + buildTimeMap(); + Knot k; + value(t, &k, false); + F32 dist = getDistance(t) + k.mSpeed * (F32(delta_ms) / 1000.0f); + return getTime(dist); +} + + +F32 CameraSpline::advanceDist(F32 t, F32 meters) +{ + buildTimeMap(); + F32 dist = getDistance(t) + meters; + return getTime(dist); +} + + +F32 CameraSpline::getDistance(F32 t) +{ + if (mSize <= 1) + return 0; + + // Find the nodes spanning the time + Vector::iterator end = mTimeMap.begin() + 1, start; + for (; end < (mTimeMap.end() - 1) && end->mTime < t; end++) { } + start = end - 1; + + // Interpolate between the two nodes + F32 i = (t - start->mTime) / (end->mTime - start->mTime); + return start->mDistance + (end->mDistance - start->mDistance) * i; +} + + +F32 CameraSpline::getTime(F32 d) +{ + if (mSize <= 1) + return 0; + + // Find nodes spanning the distance + Vector::iterator end = mTimeMap.begin() + 1, start; + for (; end < (mTimeMap.end() - 1) && end->mDistance < d; end++) { } + start = end - 1; + + // Check for duplicate points.. + F32 seg = end->mDistance - start->mDistance; + if (!seg) + return end->mTime; + + // Interpolate between the two nodes + F32 i = (d - start->mDistance) / (end->mDistance - start->mDistance); + return start->mTime + (end->mTime - start->mTime) * i; +} + + +//----------------------------------------------------------------------------- +void CameraSpline::value(F32 t, CameraSpline::Knot *result, bool skip_rotation) +{ + // Do some easing in and out for t. + if(!gBuilding) + { + F32 oldT = t; + if(oldT < 0.5f) + { + t = 0.5f - (mSin( (0.5 - oldT) * M_PI ) / 2.f); + } + + if((F32(size()) - 1.5f) > 0.f && oldT - (F32(size()) - 1.5f) > 0.f) + { + oldT -= (F32(size()) - 1.5f); + t = (F32(size()) - 1.5f) + (mCos( (0.5f - oldT) * F32(M_PI) ) / 2.f); + } + } + + // Verify that t is in range [0 >= t > size] +// AssertFatal(t >= 0.0f && t < (F32)size(), "t out of range"); + Knot *p1 = getKnot((S32)mFloor(t)); + Knot *p2 = next(p1); + + F32 i = t - mFloor(t); // adjust t to 0 to 1 on p1-p2 interval + + if (p1->mPath == Knot::SPLINE) + { + Knot *p0 = (p1->mType == Knot::KINK) ? p1 : prev(p1); + Knot *p3 = (p2->mType == Knot::KINK) ? p2 : next(p2); + result->mPosition.x = mCatmullrom(i, p0->mPosition.x, p1->mPosition.x, p2->mPosition.x, p3->mPosition.x); + result->mPosition.y = mCatmullrom(i, p0->mPosition.y, p1->mPosition.y, p2->mPosition.y, p3->mPosition.y); + result->mPosition.z = mCatmullrom(i, p0->mPosition.z, p1->mPosition.z, p2->mPosition.z, p3->mPosition.z); + } + else + { // Linear + result->mPosition.interpolate(p1->mPosition, p2->mPosition, i); + } + + if (skip_rotation) + return; + + buildTimeMap(); + + // find the two knots to interpolate rotation and velocity through since some + // knots are only positional + S32 start = (S32)mFloor(t); + S32 end = (p2 == p1) ? start : (start + 1); + while (p1->mType == Knot::POSITION_ONLY && p1 != front()) + { + p1 = prev(p1); + start--; + } + + while (p2->mType == Knot::POSITION_ONLY && p2 != back()) + { + p2 = next(p2); + end++; + } + + if (start == end) + { + result->mRotation = p1->mRotation; + result->mSpeed = p1->mSpeed; + } + else + { + + F32 c = getDistance(t); + F32 d1 = getDistance((F32)start); + F32 d2 = getDistance((F32)end); + + if (d1 == d2) + { + result->mRotation = p2->mRotation; + result->mSpeed = p2->mSpeed; + } + else + { + i = (c-d1)/(d2-d1); + + if(p1->mPath == Knot::SPLINE) + { + Knot *p0 = (p1->mType == Knot::KINK) ? p1 : prev(p1); + Knot *p3 = (p2->mType == Knot::KINK) ? p2 : next(p2); + + F32 q,w,e; + q = mCatmullrom(i, 0, 1, 1, 1); + w = mCatmullrom(i, 0, 0, 0, 1); + e = mCatmullrom(i, 0, 0, 1, 1); + + QuatF a; a.interpolate(p0->mRotation, p1->mRotation, q); + QuatF b; b.interpolate(p2->mRotation, p3->mRotation, w); + + result->mRotation.interpolate(a, b, e); + result->mSpeed = mCatmullrom(i, p0->mSpeed, p1->mSpeed, p2->mSpeed, p3->mSpeed); + } + else + { + result->mRotation.interpolate(p1->mRotation, p2->mRotation, i); + result->mSpeed = (p1->mSpeed * (1.0f-i)) + (p2->mSpeed * i); + } + } + } +} + + + diff --git a/Engine/source/T3D/cameraSpline.h b/Engine/source/T3D/cameraSpline.h new file mode 100644 index 000000000..31f79f131 --- /dev/null +++ b/Engine/source/T3D/cameraSpline.h @@ -0,0 +1,108 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + + +#ifndef _CAMERASPLINE_H_ +#define _CAMERASPLINE_H_ + +#include "math/mMath.h" +#include "core/util/tVector.h" + +//---------------------------------------------------------------------------- +class CameraSpline +{ +public: + struct Knot + { + public: + Point3F mPosition; + QuatF mRotation; + F32 mSpeed; /// in meters per second + enum Type { + NORMAL, + POSITION_ONLY, + KINK, + NUM_TYPE_BITS = 2 + }mType; + + enum Path { + LINEAR, + SPLINE, + NUM_PATH_BITS = 1 + }mPath; + + F32 mDistance; + Knot *prev; + Knot *next; + + Knot() {}; + Knot(const Knot &k); + Knot(const Point3F &p, const QuatF &r, F32 s, Knot::Type type = NORMAL, Knot::Path path = SPLINE); + }; + + + CameraSpline(); + ~CameraSpline(); + + bool isEmpty() { return (mFront == NULL); } + S32 size() { return mSize; } + Knot* remove(Knot *w); + void removeAll(); + + Knot* front() { return mFront; } + Knot* back() { return (mFront == NULL) ? NULL : mFront->prev; } + + void push_back(Knot *w); + void push_front(Knot *w) { push_back(w); mFront = w; mIsMapDirty = true; } + + Knot* getKnot(S32 i); + Knot* next(Knot *k) { return (k->next == mFront) ? k : k->next; } + Knot* prev(Knot *k) { return (k == mFront) ? k : k->prev; } + + F32 advanceTime(F32 t, S32 delta_ms); + F32 advanceDist(F32 t, F32 meters); + void value(F32 t, Knot *result, bool skip_rotation=false); + + F32 getDistance(F32 t); + F32 getTime(F32 d); + + void renderTimeMap(); + + +private: + Knot *mFront; + S32 mSize; + bool mIsMapDirty; + + struct TimeMap { + F32 mTime; + F32 mDistance; + }; + + Vector mTimeMap; + void buildTimeMap(); +}; + + + + +#endif \ No newline at end of file diff --git a/Engine/source/T3D/containerQuery.cpp b/Engine/source/T3D/containerQuery.cpp new file mode 100644 index 000000000..1c3f34609 --- /dev/null +++ b/Engine/source/T3D/containerQuery.cpp @@ -0,0 +1,97 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/containerQuery.h" + +#include "scene/sceneObject.h" +#include "environment/waterObject.h" +#include "T3D/physicalZone.h" + + +void findRouter( SceneObject *obj, void *key ) +{ + if (obj->getTypeMask() & WaterObjectType) + waterFind(obj, key); + else if (obj->getTypeMask() & PhysicalZoneObjectType) + physicalZoneFind(obj, key); + else { + AssertFatal(false, "Error, must be either water or physical zone here!"); + } +} + +void waterFind( SceneObject *obj, void *key ) +{ + PROFILE_SCOPE( waterFind ); + + // This is called for each WaterObject the ShapeBase object is overlapping. + + ContainerQueryInfo *info = static_cast(key); + WaterObject *water = dynamic_cast(obj); + AssertFatal( water != NULL, "containerQuery - waterFind(), passed object was not of class WaterObject!"); + + // Get point at the bottom/center of the box. + Point3F testPnt = info->box.getCenter(); + testPnt.z = info->box.minExtents.z; + + F32 coverage = water->getWaterCoverage(info->box); + + // Since a WaterObject can have global bounds we may get this call + // even though we have zero coverage. If so we want to early out and + // not save the water properties. + if ( coverage == 0.0f ) + return; + + // Add in flow force. Would be appropriate to try scaling it by coverage + // thought. Or perhaps have getFlow do that internally and take + // the box parameter. + info->appliedForce += water->getFlow( testPnt ); + + // Only save the following properties for the WaterObject with the + // greatest water coverage for this ShapeBase object. + if ( coverage < info->waterCoverage ) + return; + + info->waterCoverage = coverage; + info->liquidType = water->getLiquidType(); + info->waterViscosity = water->getViscosity(); + info->waterDensity = water->getDensity(); + info->waterHeight = water->getSurfaceHeight( Point2F(testPnt.x,testPnt.y) ); + info->waterObject = water; +} + +void physicalZoneFind(SceneObject* obj, void *key) +{ + PROFILE_SCOPE( physicalZoneFind ); + + ContainerQueryInfo *info = static_cast(key); + PhysicalZone* pz = dynamic_cast(obj); + AssertFatal(pz != NULL, "Error, not a physical zone!"); + if (pz == NULL || pz->testBox(info->box) == false) + return; + + if (pz->isActive()) { + info->gravityScale *= pz->getGravityMod(); + info->appliedForce += pz->getForce(); + } +} + diff --git a/Engine/source/T3D/containerQuery.h b/Engine/source/T3D/containerQuery.h new file mode 100644 index 000000000..761c8d898 --- /dev/null +++ b/Engine/source/T3D/containerQuery.h @@ -0,0 +1,71 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _CONTAINERQUERY_H_ +#define _CONTAINERQUERY_H_ + +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _STRINGTABLE_H_ +#include "core/stringTable.h" +#endif +#ifndef _MBOX_H_ +#include "math/mBox.h" +#endif + +class SceneObject; +class WaterObject; + +struct ContainerQueryInfo +{ + ContainerQueryInfo() + : waterCoverage(0.0f), + waterHeight(0.0f), + waterDensity(0.0f), + waterViscosity(0.0f), + gravityScale(1.0f), + appliedForce(0,0,0), + box(-1,-1,-1,1,1,1), + mass(1.0f), + waterObject(NULL) + { + } + + //SceneObject *sceneObject; + Box3F box; + F32 mass; + F32 waterCoverage; + F32 waterHeight; + F32 waterDensity; + F32 waterViscosity; + String liquidType; + F32 gravityScale; + Point3F appliedForce; + WaterObject *waterObject; +}; + +extern void findRouter( SceneObject *obj, void *key ); +extern void waterFind( SceneObject *obj, void *key ); +extern void physicalZoneFind( SceneObject *obj, void *key ); + +#endif // _CONTAINERQUERY_H_ \ No newline at end of file diff --git a/Engine/source/T3D/convexShape.cpp b/Engine/source/T3D/convexShape.cpp new file mode 100644 index 000000000..713f52ccd --- /dev/null +++ b/Engine/source/T3D/convexShape.cpp @@ -0,0 +1,1770 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/convexShape.h" + +#include "math/mathIO.h" +#include "math/mathUtils.h" +#include "scene/sceneRenderState.h" +#include "console/consoleTypes.h" +#include "core/stream/bitStream.h" +#include "materials/materialManager.h" +#include "lighting/lightQuery.h" +#include "renderInstance/renderPassManager.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxDrawUtil.h" +#include "core/bitVector.h" +#include "materials/materialFeatureTypes.h" +#include "materials/baseMatInstance.h" +#include "collision/optimizedPolyList.h" +#include "T3D/physics/physicsPlugin.h" +#include "T3D/physics/physicsBody.h" +#include "T3D/physics/physicsCollision.h" +#include "console/engineAPI.h" + +IMPLEMENT_CO_NETOBJECT_V1( ConvexShape ); + +ConsoleDocClass( ConvexShape, + "@brief A renderable, collidable convex shape defined by a collection of surface planes.\n\n" + + "%ConvexShape is intended to be used as a temporary asset for quickly " + "blocking out a scene or filling in approximate shapes to be later replaced " + "with final assets. This is most easily done by using the WorldEditor's " + "Sketch Tool.\n\n" + + "@ingroup enviroMisc" +); + + +Point3F ConvexShapeCollisionConvex::support( const VectorF &vec ) const +{ + const Vector< Point3F > &pointList = pShape->mGeometry.points; + + if ( pointList.empty() ) + return pShape->getObjBox().getCenter(); + + // This doesn't deal with the case that the farthest plane along vec is also + // perpendicular to it, but in that case maybe it doesn't matter which point we return + // anyway. + + F32 bestDot = mDot( pointList[0], vec ); + + const Point3F *bestP = &pointList[0]; + + for ( S32 i = 1; i < pointList.size(); i++ ) + { + F32 newD = mDot( pointList[i], vec ); + + if ( newD > bestDot ) + { + bestDot = newD; + bestP = &pointList[i]; + } + } + + return *bestP; +} + +void ConvexShapeCollisionConvex::getFeatures( const MatrixF &mat, const VectorF &n, ConvexFeature *cf ) +{ + if ( pShape->mGeometry.points.empty() ) + { + cf->material = 0; + cf->object = NULL; + return; + } + + cf->material = 0; + cf->object = mObject; + + // Simple implementation... Add all Points, Edges and Faces. + + + // Points... + + S32 firstVert = cf->mVertexList.size(); + + const Vector< Point3F > &pointList = pShape->mGeometry.points; + const U32 pointListCount = pointList.size(); + + cf->mVertexList.increment( pointListCount ); + + for ( S32 i = 0; i < pointListCount; i++ ) + mat.mulP( pointList[i], &(cf->mVertexList[ firstVert + i ]) ); + + + // Edges and Triangles for each face... + + const Vector< ConvexShape::Face > &faceList = pShape->mGeometry.faces; + + for ( S32 i = 0; i < faceList.size(); i++ ) + { + // Add this Face's Edges. + + const Vector< ConvexShape::Edge > &edgeList = faceList[i].edges; + const U32 edgeCount = edgeList.size(); + const S32 firstEdge = cf->mEdgeList.size(); + + cf->mEdgeList.increment( edgeCount ); + + for ( S32 j = 0; j < edgeCount; j++ ) + { + cf->mEdgeList[ firstEdge + j ].vertex[0] = faceList[i].points[ edgeList[j].p0 ]; + cf->mEdgeList[ firstEdge + j ].vertex[1] = faceList[i].points[ edgeList[j].p1 ]; + } + + // Add this face's Triangles. + + // Note that ConvexFeature calls triangles 'faces' but a ConvexShape 'Face' is not + // necessarily a single triangle. + + const Vector< ConvexShape::Triangle > &triangleList = faceList[i].triangles; + const U32 triangleCount = triangleList.size(); + S32 firstTriangle = cf->mFaceList.size(); + + cf->mFaceList.increment( triangleCount ); + + for ( S32 j = 0; j < triangleCount; j++ ) + { + ConvexFeature::Face &cft = cf->mFaceList[ firstTriangle + j ]; + + cft.normal = faceList[i].normal; + cft.vertex[0] = triangleList[j].p0; + cft.vertex[1] = triangleList[j].p1; + cft.vertex[2] = triangleList[j].p2; + } + } +} + + +void ConvexShapeCollisionConvex::getPolyList( AbstractPolyList* list ) +{ + SphereF sphere( Point3F::Zero, 0.0f ); + + pShape->buildPolyList( PLC_Collision, list, Box3F::Invalid, sphere ); +} + + +GFXImplementVertexFormat( ConvexVert ) +{ + addElement( "POSITION", GFXDeclType_Float3 ); + addElement( "COLOR", GFXDeclType_Color ); + addElement( "NORMAL", GFXDeclType_Float3 ); + addElement( "TANGENT", GFXDeclType_Float3 ); + addElement( "TEXCOORD", GFXDeclType_Float2, 0 ); +}; + +static const U32 sgConvexFaceColorCount = 16; +static const ColorI sgConvexFaceColors[ sgConvexFaceColorCount ] = +{ + ColorI( 239, 131, 201 ), + ColorI( 124, 255, 69 ), + ColorI( 255, 65, 77 ), + ColorI( 33, 118, 235 ), + ColorI( 114, 227, 110 ), + ColorI( 197, 50, 237 ), + ColorI( 236, 255, 255 ), + ColorI( 139, 225, 192 ), + ColorI( 215, 9, 65 ), + ColorI( 249, 114, 93 ), + ColorI( 255, 255, 90 ), + ColorI( 93, 104, 97 ), + ColorI( 255, 214, 192 ), + ColorI( 122, 44, 198 ), + ColorI( 137, 141, 194 ), + ColorI( 164, 114, 43 ) +}; + +bool ConvexShape::smRenderEdges = false; + +bool ConvexShape::protectedSetSurface( void *object, const char *index, const char *data ) +{ + ConvexShape *shape = static_cast< ConvexShape* >( object ); + + QuatF quat; + Point3F pos; + //MatrixF mat; + + /* + dSscanf( data, "%g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g", + &mat[0], &mat[1], &mat[2], &mat[3], + &mat[4], &mat[5], &mat[6], &mat[7], + &mat[8], &mat[9], &mat[10], &mat[11], + &mat[12], &mat[13], &mat[14], &mat[15] ); + */ + + dSscanf( data, "%g %g %g %g %g %g %g", &quat.x, &quat.y, &quat.z, &quat.w, &pos.x, &pos.y, &pos.z ); + + MatrixF surface; + quat.setMatrix( &surface ); + surface.setPosition( pos ); + + shape->mSurfaces.push_back( surface ); + + return false; +} + + +ConvexShape::ConvexShape() + : mMaterialInst( NULL ), + mNormalLength( 0.3f ), + mVertCount( 0 ), + mPrimCount( 0 ), + mMaterialName( "Grid512_OrangeLines_Mat" ), + mPhysicsRep( NULL ) +{ + mNetFlags.set( Ghostable | ScopeAlways ); + + mTypeMask |= StaticObjectType | + StaticShapeObjectType; + + mConvexList = new Convex; +} + +ConvexShape::~ConvexShape() +{ + if ( mMaterialInst ) + SAFE_DELETE( mMaterialInst ); + + delete mConvexList; + mConvexList = NULL; +} + +void ConvexShape::initPersistFields() +{ + addGroup( "Rendering" ); + + addField( "material", TypeMaterialName, Offset( mMaterialName, ConvexShape ), "Material used to render the ConvexShape surface." ); + + endGroup( "Rendering" ); + + addGroup( "Internal" ); + + addProtectedField( "surface", TypeRealString, NULL, &protectedSetSurface, &defaultProtectedGetFn, + "Do not modify, for internal use.", AbstractClassRep::FIELD_HideInInspectors ); + + endGroup( "Internal" ); + + Parent::initPersistFields(); +} + +void ConvexShape::inspectPostApply() +{ + Parent::inspectPostApply(); + + _updateGeometry( true ); + + setMaskBits( UpdateMask ); +} + +bool ConvexShape::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + //mObjBox.set( -0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f ); + //resetWorldBox(); + + // Face Order: + // Top, Bottom, Front, Back, Left, Right + + // X Axis + static const Point3F cubeTangents[6] = + { + Point3F( 1, 0, 0 ), + Point3F(-1, 0, 0 ), + Point3F( 1, 0, 0 ), + Point3F(-1, 0, 0 ), + Point3F( 0, 1, 0 ), + Point3F( 0, -1, 0 ) + }; + + // Y Axis + static const Point3F cubeBinormals[6] = + { + Point3F( 0, 1, 0 ), + Point3F( 0, 1, 0 ), + Point3F( 0, 0, -1 ), + Point3F( 0, 0, -1 ), + Point3F( 0, 0, -1 ), + Point3F( 0, 0, -1 ) + }; + + // Z Axis + static const Point3F cubeNormals[6] = + { + Point3F( 0, 0, 1), + Point3F( 0, 0, -1), + Point3F( 0, 1, 0), + Point3F( 0, -1, 0), + Point3F(-1, 0, 0), + Point3F( 1, 0, 0), + }; + + if ( mSurfaces.empty() ) + { + for ( S32 i = 0; i < 6; i++ ) + { + mSurfaces.increment(); + MatrixF &surf = mSurfaces.last(); + + surf.identity(); + + surf.setColumn( 0, cubeTangents[i] ); + surf.setColumn( 1, cubeBinormals[i] ); + surf.setColumn( 2, cubeNormals[i] ); + surf.setPosition( cubeNormals[i] * 0.5f ); + } + } + + if ( isClientObject() ) + _updateMaterial(); + + _updateGeometry( true ); + + addToScene(); + + return true; +} + +void ConvexShape::onRemove() +{ + removeFromScene(); + + mConvexList->nukeList(); + + SAFE_DELETE( mPhysicsRep ); + + Parent::onRemove(); +} + +void ConvexShape::writeFields( Stream &stream, U32 tabStop ) +{ + Parent::writeFields( stream, tabStop ); + + // Now write all planes. + + stream.write(2, "\r\n"); + + for ( U32 i = 0; i < mSurfaces.size(); i++ ) + { + const MatrixF &mat = mSurfaces[i]; + + QuatF quat( mat ); + Point3F pos( mat.getPosition() ); + + stream.writeTabs(tabStop); + + char buffer[1024]; + dMemset( buffer, 0, 1024 ); + + dSprintf( buffer, 1024, "surface = \"%g %g %g %g %g %g %g\";", + quat.x, quat.y, quat.z, quat.w, pos.x, pos.y, pos.z ); + + stream.writeLine( (const U8*)buffer ); + } +} + +bool ConvexShape::writeField( StringTableEntry fieldname, const char *value ) +{ + if ( fieldname == StringTable->insert("surface") ) + return false; + + return Parent::writeField( fieldname, value ); +} + +void ConvexShape::onScaleChanged() +{ + if ( isProperlyAdded() ) + _updateCollision(); +} + +void ConvexShape::setTransform( const MatrixF &mat ) +{ + Parent::setTransform( mat ); + + if ( mPhysicsRep ) + mPhysicsRep->setTransform( mat ); + + setMaskBits( TransformMask ); +} + +U32 ConvexShape::packUpdate( NetConnection *conn, U32 mask, BitStream *stream ) +{ + U32 retMask = Parent::packUpdate( conn, mask, stream ); + + if ( stream->writeFlag( mask & TransformMask ) ) + { + mathWrite(*stream, getTransform()); + mathWrite(*stream, getScale()); + } + + if ( stream->writeFlag( mask & UpdateMask ) ) + { + stream->write( mMaterialName ); + + const U32 surfCount = mSurfaces.size(); + stream->writeInt( surfCount, 32 ); + + for ( S32 i = 0; i < surfCount; i++ ) + mathWrite( *stream, mSurfaces[i] ); + } + + return retMask; +} + +void ConvexShape::unpackUpdate( NetConnection *conn, BitStream *stream ) +{ + Parent::unpackUpdate( conn, stream ); + + if ( stream->readFlag() ) // TransformMask + { + mathRead(*stream, &mObjToWorld); + mathRead(*stream, &mObjScale); + + setTransform( mObjToWorld ); + setScale( mObjScale ); + } + + if ( stream->readFlag() ) // UpdateMask + { + stream->read( &mMaterialName ); + + if ( isProperlyAdded() ) + _updateMaterial(); + + mSurfaces.clear(); + + const U32 surfCount = stream->readInt( 32 ); + for ( S32 i = 0; i < surfCount; i++ ) + { + mSurfaces.increment(); + MatrixF &mat = mSurfaces.last(); + + mathRead( *stream, &mat ); + } + + if ( isProperlyAdded() ) + _updateGeometry( true ); + } +} + +void ConvexShape::prepRenderImage( SceneRenderState *state ) +{ + /* + if ( state->isDiffusePass() ) + { + ObjectRenderInst *ri2 = state->getRenderPass()->allocInst(); + ri2->renderDelegate.bind( this, &ConvexShape::_renderDebug ); + ri2->type = RenderPassManager::RIT_Editor; + state->getRenderPass()->addInst( ri2 ); + } + */ + + if ( mVertexBuffer.isNull() ) + return; + + // If we don't have a material instance after the override then + // we can skip rendering all together. + BaseMatInstance *matInst = state->getOverrideMaterial( mMaterialInst ? mMaterialInst : MATMGR->getWarningMatInstance() ); + if ( !matInst ) + return; + + // Get a handy pointer to our RenderPassmanager + RenderPassManager *renderPass = state->getRenderPass(); + + // Allocate an MeshRenderInst so that we can submit it to the RenderPassManager + MeshRenderInst *ri = renderPass->allocInst(); + + // Set our RenderInst as a standard mesh render + ri->type = RenderPassManager::RIT_Mesh; + + // Calculate our sorting point + if ( state ) + { + // Calculate our sort point manually. + const Box3F& rBox = getRenderWorldBox(); + ri->sortDistSq = rBox.getSqDistanceToPoint( state->getCameraPosition() ); + } + else + ri->sortDistSq = 0.0f; + + // Set up our transforms + MatrixF objectToWorld = getRenderTransform(); + objectToWorld.scale( getScale() ); + + ri->objectToWorld = renderPass->allocUniqueXform( objectToWorld ); + ri->worldToCamera = renderPass->allocSharedXform(RenderPassManager::View); + ri->projection = renderPass->allocSharedXform(RenderPassManager::Projection); + + // If we need lights then set them up. + if ( matInst->isForwardLit() ) + { + LightQuery query; + query.init( getWorldSphere() ); + query.getLights( ri->lights, 8 ); + } + + // Make sure we have an up-to-date backbuffer in case + // our Material would like to make use of it + // NOTICE: SFXBB is removed and refraction is disabled! + //ri->backBuffTex = GFX->getSfxBackBuffer(); + + // Set our Material + ri->matInst = matInst; + if ( matInst->getMaterial()->isTranslucent() ) + { + ri->translucentSort = true; + ri->type = RenderPassManager::RIT_Translucent; + } + + // Set up our vertex buffer and primitive buffer + ri->vertBuff = &mVertexBuffer; + ri->primBuff = &mPrimitiveBuffer; + + ri->prim = renderPass->allocPrim(); + ri->prim->type = GFXTriangleList; + ri->prim->minIndex = 0; + ri->prim->startIndex = 0; + ri->prim->numPrimitives = mPrimCount; + ri->prim->startVertex = 0; + ri->prim->numVertices = mVertCount; + + // We sort by the material then vertex buffer. + ri->defaultKey = matInst->getStateHint(); + ri->defaultKey2 = (U32)ri->vertBuff; // Not 64bit safe! + + // Submit our RenderInst to the RenderPassManager + state->getRenderPass()->addInst( ri ); +} + +void ConvexShape::buildConvex( const Box3F &box, Convex *convex ) +{ + if ( mGeometry.faces.empty() ) + return; + + mConvexList->collectGarbage(); + + Box3F realBox = box; + mWorldToObj.mul( realBox ); + realBox.minExtents.convolveInverse( mObjScale ); + realBox.maxExtents.convolveInverse( mObjScale ); + + if ( realBox.isOverlapped( getObjBox() ) == false ) + return; + + // See if this convex exists in the working set already... + Convex *cc = 0; + CollisionWorkingList &wl = convex->getWorkingList(); + for ( CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext ) + { + if ( itr->mConvex->getType() == ConvexShapeCollisionConvexType ) + { + ConvexShapeCollisionConvex *pConvex = static_cast(itr->mConvex); + + if ( pConvex->pShape == this ) + { + cc = itr->mConvex; + return; + } + } + } + + // Set up the convex... + + ConvexShapeCollisionConvex *cp = new ConvexShapeCollisionConvex(); + + mConvexList->registerObject( cp ); + convex->addToWorkingList( cp ); + + cp->mObject = this; + cp->pShape = this; +} + +bool ConvexShape::buildPolyList( PolyListContext context, AbstractPolyList *plist, const Box3F &box, const SphereF &sphere ) +{ + if ( mGeometry.points.empty() ) + return false; + + // If we're exporting deal with that first. + if ( context == PLC_Export ) + { + AssertFatal( dynamic_cast( plist ), "ConvexShape::buildPolyList - Bad polylist for export!" ); + _export( (OptimizedPolyList*)plist, box, sphere ); + return true; + } + + plist->setTransform( &mObjToWorld, mObjScale ); + plist->setObject( this ); + + + // Add points... + + const Vector< Point3F > pointList = mGeometry.points; + + S32 base = plist->addPoint( pointList[0] ); + + for ( S32 i = 1; i < pointList.size(); i++ ) + plist->addPoint( pointList[i] ); + + + // Add Surfaces... + + const Vector< ConvexShape::Face > faceList = mGeometry.faces; + + for ( S32 i = 0; i < faceList.size(); i++ ) + { + const ConvexShape::Face &face = faceList[i]; + + plist->begin( 0, i ); + + plist->plane( PlaneF( face.centroid, face.normal ) ); + + for ( S32 j = 0; j < face.triangles.size(); j++ ) + { + plist->vertex( base + face.points[ face.triangles[j].p0 ] ); + plist->vertex( base + face.points[ face.triangles[j].p1 ] ); + plist->vertex( base + face.points[ face.triangles[j].p2 ] ); + } + + plist->end(); + } + + return true; +} + +void ConvexShape::_export( OptimizedPolyList *plist, const Box3F &box, const SphereF &sphere ) +{ + BaseMatInstance *matInst = mMaterialInst; + if ( isServerObject() && getClientObject() ) + matInst = dynamic_cast(getClientObject())->mMaterialInst; + + MatrixF saveMat; + Point3F saveScale; + plist->getTransform( &saveMat, &saveScale ); + + plist->setTransform( &mObjToWorld, mObjScale ); + plist->setObject( this ); + + const Vector< ConvexShape::Face > faceList = mGeometry.faces; + const Vector< Point3F > &pointList = mGeometry.points; + + for ( S32 i = 0; i < faceList.size(); i++ ) + { + const ConvexShape::Face &face = faceList[i]; + + plist->begin( matInst, i, OptimizedPolyList::TriangleList ); + + plist->plane( PlaneF( face.centroid, face.normal ) ); + + for ( S32 j = 0; j < face.triangles.size(); j++ ) + { + for ( S32 k = 0; k < 3; k++ ) + { + U32 vertId = face.triangles[j][k]; + plist->vertex( pointList[ face.points[ vertId ] ], face.normal, face.texcoords[ vertId ] ); + } + } + + plist->end(); + } + + plist->setTransform( &saveMat, saveScale ); +} + +bool ConvexShape::castRay( const Point3F &start, const Point3F &end, RayInfo *info ) +{ + if ( mPlanes.empty() ) + return false; + + const Vector< PlaneF > &planeList = mPlanes; + const U32 planeCount = planeList.size(); + + F32 t; + F32 tmin = F32_MAX; + S32 hitFace = -1; + Point3F hitPnt, pnt; + VectorF rayDir( end - start ); + rayDir.normalizeSafe(); + + if ( false ) + { + PlaneF plane( Point3F(0,0,0), Point3F(0,0,1) ); + Point3F sp( 0,0,-1 ); + Point3F ep( 0,0,1 ); + + F32 t = plane.intersect( sp, ep ); + Point3F hitPnt; + hitPnt.interpolate( sp, ep, t ); + } + + for ( S32 i = 0; i < planeCount; i++ ) + { + // Don't hit the back-side of planes. + if ( mDot( rayDir, planeList[i] ) >= 0.0f ) + continue; + + t = planeList[i].intersect( start, end ); + + if ( t >= 0.0f && t <= 1.0f && t < tmin ) + { + pnt.interpolate( start, end, t ); + + S32 j = 0; + for ( ; j < planeCount; j++ ) + { + if ( i == j ) + continue; + + F32 dist = planeList[j].distToPlane( pnt ); + if ( dist > 1.0e-004f ) + break; + } + + if ( j == planeCount ) + { + tmin = t; + hitFace = i; + } + } + } + + if ( hitFace == -1 ) + return false; + + info->face = hitFace; + info->material = mMaterialInst; + info->normal = planeList[ hitFace ]; + info->object = this; + info->t = tmin; + + //mObjToWorld.mulV( info->normal ); + + return true; +} + +bool ConvexShape::collideBox( const Point3F &start, const Point3F &end, RayInfo *info ) +{ + return Parent::collideBox( start, end, info ); +} + +void ConvexShape::updateBounds( bool recenter ) +{ + if ( mGeometry.points.size() == 0 ) + return; + + Vector &pointListOS = mGeometry.points; + U32 pointCount = pointListOS.size(); + + Point3F volumnCenter( 0,0,0 ); + F32 areaSum = 0.0f; + + F32 faceCount = mGeometry.faces.size(); + + for ( S32 i = 0; i < faceCount; i++ ) + { + volumnCenter += mGeometry.faces[i].centroid * mGeometry.faces[i].area; + areaSum += mGeometry.faces[i].area; + } + + if ( areaSum == 0.0f ) + return; + + volumnCenter /= areaSum; + + mObjBox.minExtents = mObjBox.maxExtents = Point3F::Zero; + mObjBox.setCenter( volumnCenter ); + + for ( S32 i = 0; i < pointCount; i++ ) + mObjBox.extend( pointListOS[i] ); + + resetWorldBox(); +} + +void ConvexShape::recenter() +{ + if ( mGeometry.points.size() == 0 ) + return; + + Point3F volCenterOS( 0,0,0 ); + F32 areaSum = 0.0f; + + F32 faceCount = mGeometry.faces.size(); + + for ( S32 i = 0; i < faceCount; i++ ) + { + volCenterOS += mGeometry.faces[i].centroid * mGeometry.faces[i].area; + areaSum += mGeometry.faces[i].area; + } + + volCenterOS /= areaSum; + + for ( S32 i = 0; i < mSurfaces.size(); i++ ) + mSurfaces[i].setPosition( mSurfaces[i].getPosition() - volCenterOS ); + + Point3F volCenterWS; + MatrixF objToWorld( mObjToWorld ); + objToWorld.scale( mObjScale ); + objToWorld.mulP( volCenterOS, &volCenterWS ); + + setPosition( volCenterWS ); + + _updateGeometry(true); +} + +MatrixF ConvexShape::getSurfaceWorldMat( S32 surfId, bool scaled ) const +{ + if ( surfId < 0 || surfId >= mSurfaces.size() ) + return MatrixF::Identity; + + MatrixF objToWorld( mObjToWorld ); + + if ( scaled ) + objToWorld.scale( mObjScale ); + + MatrixF surfMat; + surfMat.mul( objToWorld, mSurfaces[surfId] ); + + return surfMat; +} + +// Used in cullEmptyPlanes. +S32 QSORT_CALLBACK sortDescendingU32( const void *a, const void *b ) +{ + U32 *aa = (U32*)(a); + U32 *bb = (U32*)(b); + + return (S32)(*bb) - (S32)(*aa); +} + +void ConvexShape::cullEmptyPlanes( Vector< U32 > *removedPlanes ) +{ + //if ( mPlanes.size() == mGeometry.faces.size() ) + // return; + + removedPlanes->clear(); + const U32 startPlaneCount = mPlanes.size(); + + const Vector< ConvexShape::Face > &faceList = mGeometry.faces; + const U32 faceCount = faceList.size(); + + S32 *used = new S32[ startPlaneCount ]; + + for ( S32 i = 0; i < startPlaneCount; i++ ) + used[i] = i; + + for ( S32 i = 0; i < faceCount; i++ ) + { + if ( faceList[i].area > 0.001f ) + used[ faceList[i].id ] = -1; + } + + for ( S32 i = 0; i < startPlaneCount; i++ ) + { + if ( used[i] != -1 ) + removedPlanes->push_back( used[i] ); + } + + dQsort( removedPlanes->address(), removedPlanes->size(), sizeof( U32 ), sortDescendingU32 ); + + for ( S32 i = 0; i < removedPlanes->size(); i++ ) + { + mPlanes.erase( (*removedPlanes)[i] ); + mSurfaces.erase( (*removedPlanes)[i] ); + } + + delete [] used; +} + +void ConvexShape::exportToCollada() +{ + if ( mSurfaces.size() == 0 ) + { + Con::errorf( "ConvexShape::exportToCollada() - has no surfaces to export!" ); + return; + } +/* + // Get an optimized version of our mesh + OptimizedPolyList polyList; + + if (bakeTransform) + { + MatrixF mat = getTransform(); + Point3F scale = getScale(); + + pInterior->buildExportPolyList(interiorMesh, &mat, &scale); + } + else + pInterior->buildExportPolyList(interiorMesh); + + // Get our export path + Torque::Path colladaFile = mInteriorRes.getPath(); + + // Make sure to set our Collada extension + colladaFile.setExtension("dae"); + + // Use the InteriorInstance name if possible + String meshName = getName(); + + // Otherwise use the DIF's file name + if (meshName.isEmpty()) + meshName = colladaFile.getFileName(); + + // If we are baking the transform then append + // a CRC version of the transform to the mesh/file name + if (bakeTransform) + { + F32 trans[19]; + + const MatrixF& mat = getTransform(); + const Point3F& scale = getScale(); + + // Copy in the transform + for (U32 i = 0; i < 4; i++) + { + for (U32 j = 0; j < 4; j++) + { + trans[i * 4 + j] = mat(i, j); + } + } + + // Copy in the scale + trans[16] = scale.x; + trans[17] = scale.y; + trans[18] = scale.z; + + U32 crc = CRC::calculateCRC(trans, sizeof(F32) * 19); + + meshName += String::ToString("_%x", crc); + } + + // Set the file name as the meshName + colladaFile.setFileName(meshName); + + // Use a ColladaUtils function to do the actual export to a Collada file + ColladaUtils::exportToCollada(colladaFile, interiorMesh, meshName); + } + */ +} + +void ConvexShape::resizePlanes( const Point3F &size ) +{ + //Point3F nSize; + //mWorldToObj.mulV( nSize ); + + for ( S32 i = 0; i < mSurfaces.size(); i++ ) + { + MatrixF objToPlane( mSurfaces[i] ); + objToPlane.inverse(); + + Point3F lim; + objToPlane.mulV( size, &lim ); + + F32 sign = ( mPlanes[i].d > 0.0f ) ? 1.0f : -1.0f; + mPlanes[i].d = mFabs(lim.z) * 0.5f * sign; + + //mPlanes[i].d = -lim.z * 0.5f; + + mSurfaces[i].setPosition( mPlanes[i].getPosition() ); + } +} + +void ConvexShape::getSurfaceLineList( S32 surfId, Vector< Point3F > &lineList ) +{ + if ( surfId < 0 || surfId > mSurfaces.size() - 1 ) + return; + + S32 faceId = -1; + + for ( S32 i = 0; i < mGeometry.faces.size(); i++ ) + { + if ( mGeometry.faces[i].id == surfId ) + { + faceId = i; + break; + } + } + + if ( faceId == -1 ) + return; + + ConvexShape::Face &face = mGeometry.faces[faceId]; + const Vector< Point3F > &pointList = mGeometry.points; + + if ( pointList.size() == 0 ) + return; + + for ( S32 i = 0; i < face.winding.size(); i++ ) + lineList.push_back( pointList[ face.points[ face.winding[i] ] ] ); + + lineList.push_back( pointList[ face.points[ face.winding.first() ] ] ); +} + +void ConvexShape::_updateMaterial() +{ + // If the material name matches then don't bother updating it. + if ( mMaterialInst && mMaterialName.equal( mMaterialInst->getMaterial()->getName(), String::NoCase ) ) + return; + + SAFE_DELETE( mMaterialInst ); + + Material *material; + + if ( !Sim::findObject( mMaterialName, material ) ) + Sim::findObject( "WarningMaterial", material ); + + mMaterialInst = material->createMatInstance(); + + //GFXStateBlockDesc desc; + //desc.setCullMode( GFXCullNone ); + //desc.setBlend( false ); + + //mMaterialInst->addStateBlockDesc( desc ); + + FeatureSet features = MATMGR->getDefaultFeatures(); + //features.addFeature( MFT_DiffuseVertColor ); + + mMaterialInst->init( features, getGFXVertexFormat() ); + + if ( !mMaterialInst->isValid() ) + { + SAFE_DELETE( mMaterialInst ); + } +} + +void ConvexShape::_updateGeometry( bool updateCollision ) +{ + mPlanes.clear(); + + for ( S32 i = 0; i < mSurfaces.size(); i++ ) + mPlanes.push_back( PlaneF( mSurfaces[i].getPosition(), mSurfaces[i].getUpVector() ) ); + + Vector< Point3F > tangents; + for ( S32 i = 0; i < mSurfaces.size(); i++ ) + tangents.push_back( mSurfaces[i].getRightVector() ); + + mGeometry.generate( mPlanes, tangents ); + + AssertFatal( mGeometry.faces.size() <= mSurfaces.size(), "Got more faces than planes?" ); + + const Vector< ConvexShape::Face > &faceList = mGeometry.faces; + const Vector< Point3F > &pointList = mGeometry.points; + + // Reset our surface center points. + + for ( S32 i = 0; i < faceList.size(); i++ ) + mSurfaces[ faceList[i].id ].setPosition( faceList[i].centroid ); + + mPlanes.clear(); + + for ( S32 i = 0; i < mSurfaces.size(); i++ ) + mPlanes.push_back( PlaneF( mSurfaces[i].getPosition(), mSurfaces[i].getUpVector() ) ); + + // Update bounding box. + updateBounds( false ); + + mVertexBuffer = NULL; + mPrimitiveBuffer = NULL; + mVertCount = 0; + mPrimCount = 0; + + if ( updateCollision ) + _updateCollision(); + + // Server does not need to generate vertex/prim buffers. + if ( isServerObject() ) + return; + + if ( faceList.empty() ) + return; + + + // Get total vert and prim count. + + for ( S32 i = 0; i < faceList.size(); i++ ) + { + U32 count = faceList[i].triangles.size(); + mPrimCount += count; + mVertCount += count * 3; + } + + // Allocate VB and copy in data. + + mVertexBuffer.set( GFX, mVertCount, GFXBufferTypeStatic ); + VertexType *pVert = mVertexBuffer.lock(); + + for ( S32 i = 0; i < faceList.size(); i++ ) + { + const ConvexShape::Face &face = faceList[i]; + const Vector< U32 > &facePntMap = face.points; + const Vector< ConvexShape::Triangle > &triangles = face.triangles; + const ColorI &faceColor = sgConvexFaceColors[ i % sgConvexFaceColorCount ]; + + const Point3F binormal = mCross( face.normal, face.tangent ); + + for ( S32 j = 0; j < triangles.size(); j++ ) + { + for ( S32 k = 0; k < 3; k++ ) + { + pVert->normal = face.normal; + pVert->tangent = face.tangent; + pVert->color = faceColor; + pVert->point = pointList[ facePntMap[ triangles[j][k] ] ]; + pVert->texCoord = face.texcoords[ triangles[j][k] ]; + + pVert++; + } + } + } + + mVertexBuffer.unlock(); + + // Allocate PB + + mPrimitiveBuffer.set( GFX, mPrimCount * 3, mPrimCount, GFXBufferTypeStatic ); + + U16 *pIndex; + mPrimitiveBuffer.lock( &pIndex ); + + for ( U16 i = 0; i < mPrimCount * 3; i++ ) + { + *pIndex = i; + pIndex++; + } + + mPrimitiveBuffer.unlock(); +} + +void ConvexShape::_updateCollision() +{ + SAFE_DELETE( mPhysicsRep ); + + if ( !PHYSICSMGR ) + return; + + PhysicsCollision *colShape = PHYSICSMGR->createCollision(); + + // We need the points untransformed! + Vector rawPoints; + MatrixF xfm( getWorldTransform() ); + xfm.setPosition( Point3F::Zero ); + for ( U32 i=0; i < mGeometry.points.size(); i++ ) + { + Point3F p = mGeometry.points[i]; + xfm.mulP( p ); + rawPoints.push_back( p ); + } + + // The convex generation from a point cloud + // can fail at times... give up in that case. + if ( !colShape->addConvex( mGeometry.points.address(), + mGeometry.points.size(), + MatrixF::Identity ) ) + { + delete colShape; + return; + } + + PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" ); + + mPhysicsRep = PHYSICSMGR->createBody(); + mPhysicsRep->init( colShape, 0, 0, this, world ); + + mPhysicsRep->setTransform( getTransform() ); +} + +void ConvexShape::_renderDebug( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *mat ) +{ + GFXDrawUtil *drawer = GFX->getDrawUtil(); + + GFX->setTexture( 0, NULL ); + + // Render world box. + if ( false ) + { + Box3F wbox( mWorldBox ); + //if ( getServerObject() ) + // Box3F wbox = static_cast( getServerObject() )->mWorldBox; + GFXStateBlockDesc desc; + desc.setCullMode( GFXCullNone ); + desc.setFillModeWireframe(); + drawer->drawCube( desc, wbox, ColorI::RED ); + } + + + const Vector< Point3F > &pointList = mGeometry.points; + const Vector< ConvexShape::Face > &faceList = mGeometry.faces; + + // Render Edges. + if ( false ) + { + GFXTransformSaver saver; + //GFXFrustumSaver fsaver; + + MatrixF xfm( getRenderTransform() ); + xfm.scale( getScale() ); + GFX->multWorld( xfm ); + + GFXStateBlockDesc desc; + desc.setZReadWrite( true, false ); + desc.setBlend( true ); + GFX->setStateBlockByDesc( desc ); + + //MathUtils::getZBiasProjectionMatrix( 0.01f, state->getFrustum(), ) + + const Point3F &camFvec = state->getCameraTransform().getForwardVector(); + + + + for ( S32 i = 0; i < faceList.size(); i++ ) + { + const ConvexShape::Face &face = faceList[i]; + + const Vector< ConvexShape::Edge > &edgeList = face.edges; + + const Vector< U32 > &facePntList = face.points; + + PrimBuild::begin( GFXLineList, edgeList.size() * 2 ); + + PrimBuild::color( ColorI::WHITE * 0.8f ); + + for ( S32 j = 0; j < edgeList.size(); j++ ) + { + PrimBuild::vertex3fv( pointList[ facePntList[ edgeList[j].p0 ] ] - camFvec * 0.001f ); + PrimBuild::vertex3fv( pointList[ facePntList[ edgeList[j].p1 ] ] - camFvec * 0.001f ); + } + + PrimBuild::end(); + } + } + + ColorI faceColorsx[4] = + { + ColorI( 255, 0, 0 ), + ColorI( 0, 255, 0 ), + ColorI( 0, 0, 255 ), + ColorI( 255, 0, 255 ) + }; + + MatrixF objToWorld( mObjToWorld ); + objToWorld.scale( mObjScale ); + + // Render faces centers/colors. + if ( false ) + { + GFXStateBlockDesc desc; + desc.setCullMode( GFXCullNone ); + + Point3F size( 0.1f ); + + for ( S32 i = 0; i < faceList.size(); i++ ) + { + ColorI color = faceColorsx[ i % 4 ]; + S32 div = ( i / 4 ) * 4; + if ( div > 0 ) + color /= div; + color.alpha = 255; + + Point3F pnt; + objToWorld.mulP( faceList[i].centroid, &pnt ); + drawer->drawCube( desc, size, pnt, color, NULL ); + } + } + + // Render winding order. + if ( false ) + { + GFXStateBlockDesc desc; + desc.setCullMode( GFXCullNone ); + desc.setZReadWrite( true, false ); + GFX->setStateBlockByDesc( desc ); + + U32 pointCount = 0; + for ( S32 i = 0; i < faceList.size(); i++ ) + pointCount += faceList[i].winding.size(); + + PrimBuild::begin( GFXLineList, pointCount * 2 ); + + for ( S32 i = 0; i < faceList.size(); i++ ) + { + for ( S32 j = 0; j < faceList[i].winding.size(); j++ ) + { + Point3F p0 = pointList[ faceList[i].points[ faceList[i].winding[j] ] ]; + Point3F p1 = p0 + mSurfaces[ faceList[i].id ].getUpVector() * 0.75f * ( Point3F::One / mObjScale ); + + objToWorld.mulP( p0 ); + objToWorld.mulP( p1 ); + + ColorI color = faceColorsx[ j % 4 ]; + S32 div = ( j / 4 ) * 4; + if ( div > 0 ) + color /= div; + color.alpha = 255; + + PrimBuild::color( color ); + PrimBuild::vertex3fv( p0 ); + PrimBuild::color( color ); + PrimBuild::vertex3fv( p1 ); + } + } + + PrimBuild::end(); + } + + // Render Points. + if ( false ) + { + /* + GFXTransformSaver saver; + + MatrixF xfm( getRenderTransform() ); + xfm.scale( getScale() ); + GFX->multWorld( xfm ); + + GFXStateBlockDesc desc; + Point3F size( 0.05f ); + */ + } + + // Render surface transforms. + if ( false ) + { + GFXStateBlockDesc desc; + desc.setBlend( false ); + desc.setZReadWrite( true, true ); + + Point3F scale(mNormalLength); + + for ( S32 i = 0; i < mSurfaces.size(); i++ ) + { + MatrixF objToWorld( mObjToWorld ); + objToWorld.scale( mObjScale ); + + MatrixF renderMat; + renderMat.mul( objToWorld, mSurfaces[i] ); + + renderMat.setPosition( renderMat.getPosition() + renderMat.getUpVector() * 0.001f ); + + drawer->drawTransform( desc, renderMat, &scale, NULL ); + } + } +} + +void ConvexShape::renderFaceEdges( S32 faceid, const ColorI &color /*= ColorI::WHITE*/, F32 lineWidth /*= 1.0f */ ) +{ + const Vector< ConvexShape::Face > &faceList = mGeometry.faces; + + if ( faceid >= faceList.size() ) + return; + + GFXTransformSaver saver; + MatrixF xfm( mObjToWorld ); + xfm.scale( mObjScale ); + GFX->multWorld( xfm ); + + GFXStateBlockDesc desc; + desc.setBlend( true ); + GFX->setStateBlockByDesc( desc ); + + MatrixF projBias(true); + const Frustum& frustum = GFX->getFrustum(); + MathUtils::getZBiasProjectionMatrix( 0.001f, frustum, &projBias ); + GFX->setProjectionMatrix( projBias ); + + S32 s = faceid; + S32 e = faceid + 1; + + if ( faceid == -1 ) + { + s = 0; + e = faceList.size(); + } + + for ( S32 i = s; i < e; i++ ) + { + const ConvexShape::Face &face = faceList[i]; + const Vector< ConvexShape::Edge > &edgeList = face.edges; + const Vector< U32 > &facePntList = face.points; + const Vector< Point3F > &pointList = mGeometry.points; + + PrimBuild::begin( GFXLineList, edgeList.size() * 2 ); + + PrimBuild::color( color ); + + for ( S32 j = 0; j < edgeList.size(); j++ ) + { + PrimBuild::vertex3fv( pointList[ facePntList[ edgeList[j].p0 ] ] ); + PrimBuild::vertex3fv( pointList[ facePntList[ edgeList[j].p1 ] ] ); + } + + PrimBuild::end(); + } +} + +void ConvexShape::getSurfaceTriangles( S32 surfId, Vector< Point3F > *outPoints, Vector< Point2F > *outCoords, bool worldSpace ) +{ + S32 faceId = -1; + for ( S32 i = 0; i < mGeometry.faces.size(); i++ ) + { + if ( mGeometry.faces[i].id == surfId ) + { + faceId = i; + break; + } + } + + if ( faceId == -1 ) + return; + + const ConvexShape::Face &face = mGeometry.faces[ faceId ]; + const Vector< Point3F > &pointList = mGeometry.points; + + const MatrixF &surfToObj = mSurfaces[ faceId ]; + MatrixF objToSurf( surfToObj ); + objToSurf.inverse(); + + Point3F surfScale( 1.5f, 1.5f, 1.0f ); + + for ( S32 i = 0; i < face.triangles.size(); i++ ) + { + for ( S32 j = 0; j < 3; j++ ) + { + Point3F pnt( pointList[ face.points[ face.triangles[i][j] ] ] ); + + objToSurf.mulP( pnt ); + pnt *= surfScale; + surfToObj.mulP( pnt ); + + outPoints->push_back( pnt ); + + if ( outCoords ) + outCoords->push_back( face.texcoords[ face.triangles[i][j] ] ); + } + } + + if ( worldSpace ) + { + MatrixF objToWorld( mObjToWorld ); + objToWorld.scale( mObjScale ); + + for ( S32 i = 0; i < outPoints->size(); i++ ) + objToWorld.mulP( (*outPoints)[i] ); + } +} +void ConvexShape::Geometry::generate( const Vector< PlaneF > &planes, const Vector< Point3F > &tangents ) +{ + PROFILE_SCOPE( Geometry_generate ); + + points.clear(); + faces.clear(); + + AssertFatal( planes.size() == tangents.size(), "ConvexShape - incorrect plane/tangent count." ); + +#ifdef TORQUE_ENABLE_ASSERTS + for ( S32 i = 0; i < planes.size(); i++ ) + { + F32 dt = mDot( planes[i], tangents[i] ); + AssertFatal( mIsZero( dt, 0.0001f ), "ConvexShape - non perpendicular input vectors." ); + AssertFatal( planes[i].isUnitLength() && tangents[i].isUnitLength(), "ConvexShape - non unit length input vector." ); + } +#endif + + const U32 planeCount = planes.size(); + + Point3F linePt, lineDir; + + for ( S32 i = 0; i < planeCount; i++ ) + { + Vector< MathUtils::Line > collideLines; + + // Find the lines defined by the intersection of this plane with all others. + + for ( S32 j = 0; j < planeCount; j++ ) + { + if ( i == j ) + continue; + + if ( planes[i].intersect( planes[j], linePt, lineDir ) ) + { + collideLines.increment(); + MathUtils::Line &line = collideLines.last(); + line.origin = linePt; + line.direction = lineDir; + } + } + + if ( collideLines.empty() ) + continue; + + // Find edges and points defined by the intersection of these lines. + // As we find them we fill them into our working ConvexShape::Face + // structure. + + Face newFace; + + for ( S32 j = 0; j < collideLines.size(); j++ ) + { + Vector< Point3F > collidePoints; + + for ( S32 k = 0; k < collideLines.size(); k++ ) + { + if ( j == k ) + continue; + + MathUtils::LineSegment segment; + MathUtils::mShortestSegmentBetweenLines( collideLines[j], collideLines[k], &segment ); + + F32 dist = ( segment.p0 - segment.p1 ).len(); + + if ( dist < 0.0005f ) + { + S32 l = 0; + for ( ; l < planeCount; l++ ) + { + if ( planes[l].whichSide( segment.p0 ) == PlaneF::Front ) + break; + } + + if ( l == planeCount ) + collidePoints.push_back( segment.p0 ); + } + } + + //AssertFatal( collidePoints.size() <= 2, "A line can't collide with more than 2 other lines in a convex shape..." ); + + if ( collidePoints.size() != 2 ) + continue; + + // Push back collision points into our points vector + // if they are not duplicates and determine the id + // index for those points to be used by Edge(s). + + const Point3F &pnt0 = collidePoints[0]; + const Point3F &pnt1 = collidePoints[1]; + S32 idx0 = -1; + S32 idx1 = -1; + + for ( S32 k = 0; k < points.size(); k++ ) + { + if ( pnt0.equal( points[k] ) ) + { + idx0 = k; + break; + } + } + + for ( S32 k = 0; k < points.size(); k++ ) + { + if ( pnt1.equal( points[k] ) ) + { + idx1 = k; + break; + } + } + + if ( idx0 == -1 ) + { + points.push_back( pnt0 ); + idx0 = points.size() - 1; + } + + if ( idx1 == -1 ) + { + points.push_back( pnt1 ); + idx1 = points.size() - 1; + } + + // Construct the Face::Edge defined by this collision. + + S32 localIdx0 = newFace.points.push_back_unique( idx0 ); + S32 localIdx1 = newFace.points.push_back_unique( idx1 ); + + newFace.edges.increment(); + ConvexShape::Edge &newEdge = newFace.edges.last(); + newEdge.p0 = localIdx0; + newEdge.p1 = localIdx1; + } + + if ( newFace.points.size() < 3 ) + continue; + + //AssertFatal( newFace.points.size() == newFace.edges.size(), "ConvexShape - face point count does not equal edge count." ); + + + // Fill in some basic Face information. + + newFace.id = i; + newFace.normal = planes[i]; + newFace.tangent = tangents[i]; + + + // Make a working array of Point3Fs on this face. + + U32 pntCount = newFace.points.size(); + Point3F *workPoints = new Point3F[ pntCount ]; + + for ( S32 j = 0; j < pntCount; j++ ) + workPoints[j] = points[ newFace.points[j] ]; + + + // Calculate the average point for calculating winding order. + + Point3F averagePnt = Point3F::Zero; + + for ( S32 j = 0; j < pntCount; j++ ) + averagePnt += workPoints[j]; + + averagePnt /= pntCount; + + + // Sort points in correct winding order. + + U32 *vertMap = new U32[pntCount]; + + MatrixF quadMat( true ); + quadMat.setPosition( averagePnt ); + quadMat.setColumn( 0, newFace.tangent ); + quadMat.setColumn( 1, mCross( newFace.normal, newFace.tangent ) ); + quadMat.setColumn( 2, newFace.normal ); + quadMat.inverse(); + + // Transform working points into quad space + // so we can work with them as 2D points. + + for ( S32 j = 0; j < pntCount; j++ ) + quadMat.mulP( workPoints[j] ); + + MathUtils::sortQuadWindingOrder( true, workPoints, vertMap, pntCount ); + + // Save points in winding order. + + for ( S32 j = 0; j < pntCount; j++ ) + newFace.winding.push_back( vertMap[j] ); + + // Calculate the area and centroid of the face. + + newFace.area = 0.0f; + for ( S32 j = 0; j < pntCount; j++ ) + { + S32 k = ( j + 1 ) % pntCount; + const Point3F &p0 = workPoints[ vertMap[j] ]; + const Point3F &p1 = workPoints[ vertMap[k] ]; + + // Note that this calculation returns positive area for clockwise winding + // and negative area for counterclockwise winding. + newFace.area += p0.y * p1.x; + newFace.area -= p0.x * p1.y; + } + + //AssertFatal( newFace.area > 0.0f, "ConvexShape - face area was not positive." ); + if ( newFace.area > 0.0f ) + newFace.area /= 2.0f; + + F32 factor; + F32 cx = 0.0f, cy = 0.0f; + + for ( S32 j = 0; j < pntCount; j++ ) + { + S32 k = ( j + 1 ) % pntCount; + const Point3F &p0 = workPoints[ vertMap[j] ]; + const Point3F &p1 = workPoints[ vertMap[k] ]; + + factor = p0.x * p1.y - p1.x * p0.y; + cx += ( p0.x + p1.x ) * factor; + cy += ( p0.y + p1.y ) * factor; + } + + factor = 1.0f / ( newFace.area * 6.0f ); + newFace.centroid.set( cx * factor, cy * factor, 0.0f ); + quadMat.inverse(); + quadMat.mulP( newFace.centroid ); + + delete [] workPoints; + workPoints = NULL; + + // Make polygons / triangles for this face. + + const U32 polyCount = pntCount - 2; + + newFace.triangles.setSize( polyCount ); + + for ( S32 j = 0; j < polyCount; j++ ) + { + ConvexShape::Triangle &poly = newFace.triangles[j]; + + poly.p0 = vertMap[0]; + + if ( j == 0 ) + { + poly.p1 = vertMap[ 1 ]; + poly.p2 = vertMap[ 2 ]; + } + else + { + poly.p1 = vertMap[ 1 + j ]; + poly.p2 = vertMap[ 2 + j ]; + } + } + + delete [] vertMap; + + + // Calculate texture coordinates for each point in this face. + + const Point3F binormal = mCross( newFace.normal, newFace.tangent ); + PlaneF planey( newFace.centroid - 0.5f * binormal, binormal ); + PlaneF planex( newFace.centroid - 0.5f * newFace.tangent, newFace.tangent ); + + newFace.texcoords.setSize( newFace.points.size() ); + + for ( S32 j = 0; j < newFace.points.size(); j++ ) + { + F32 x = planex.distToPlane( points[ newFace.points[ j ] ] ); + F32 y = planey.distToPlane( points[ newFace.points[ j ] ] ); + + newFace.texcoords[j].set( -x, -y ); + } + + // Data verification tests. +#ifdef TORQUE_ENABLE_ASSERTS + //S32 triCount = newFace.triangles.size(); + //S32 edgeCount = newFace.edges.size(); + //AssertFatal( triCount == edgeCount - 2, "ConvexShape - triangle/edge count do not match." ); + + /* + for ( S32 j = 0; j < triCount; j++ ) + { + F32 area = MathUtils::mTriangleArea( points[ newFace.points[ newFace.triangles[j][0] ] ], + points[ newFace.points[ newFace.triangles[j][1] ] ], + points[ newFace.points[ newFace.triangles[j][2] ] ] ); + AssertFatal( area > 0.0f, "ConvexShape - triangle winding bad." ); + }*/ +#endif + + + // Done with this Face. + + faces.push_back( newFace ); + } +} diff --git a/Engine/source/T3D/convexShape.h b/Engine/source/T3D/convexShape.h new file mode 100644 index 000000000..ed9f23d2e --- /dev/null +++ b/Engine/source/T3D/convexShape.h @@ -0,0 +1,248 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _CONVEXSHAPE_H_ +#define _CONVEXSHAPE_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _GFXVERTEXBUFFER_H_ +#include "gfx/gfxVertexBuffer.h" +#endif +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif +#ifndef _CONVEX_H_ +#include "collision/convex.h" +#endif + +class ConvexShape; + +// Crap name, but whatcha gonna do. +class ConvexShapeCollisionConvex : public Convex +{ + typedef Convex Parent; + friend class ConvexShape; + +protected: + + ConvexShape *pShape; + +public: + + ConvexShapeCollisionConvex() { mType = ConvexShapeCollisionConvexType; } + + ConvexShapeCollisionConvex( const ConvexShapeCollisionConvex& cv ) { + mType = ConvexShapeCollisionConvexType; + mObject = cv.mObject; + pShape = cv.pShape; + } + + Point3F support(const VectorF& vec) const; + void getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf); + void getPolyList(AbstractPolyList* list); +}; + + +GFXDeclareVertexFormat( ConvexVert ) +{ + Point3F point; + GFXVertexColor color; + Point3F normal; + Point3F tangent; + Point2F texCoord; +}; + +class PhysicsBody; + +// Define our vertex format here so we don't have to +// change it in multiple spots later +typedef ConvexVert VertexType; + +class ConvexShape : public SceneObject +{ + typedef SceneObject Parent; + friend class GuiConvexEditorCtrl; + friend class GuiConvexEditorUndoAction; + friend class ConvexShapeCollisionConvex; + +public: + + enum NetBits { + TransformMask = Parent::NextFreeMask, + UpdateMask = Parent::NextFreeMask << 1, + NextFreeMask = Parent::NextFreeMask << 2 + }; + + // Declaring these structs directly within ConvexShape to prevent + // the otherwise excessively deep scoping we had. + // eg. ConvexShape::Face::Triangle ... + + struct Edge + { + U32 p0; + U32 p1; + }; + + struct Triangle + { + U32 p0; + U32 p1; + U32 p2; + + U32 operator []( U32 index ) const + { + AssertFatal( index >= 0 && index <= 2, "index out of range" ); + return *( (&p0) + index ); + } + }; + + struct Face + { + Vector< Edge > edges; + Vector< U32 > points; + Vector< U32 > winding; + Vector< Point2F > texcoords; + Vector< Triangle > triangles; + Point3F tangent; + Point3F normal; + Point3F centroid; + F32 area; + S32 id; + }; + + struct Geometry + { + void generate( const Vector< PlaneF > &planes, const Vector< Point3F > &tangents ); + + Vector< Point3F > points; + Vector< Face > faces; + }; + + static bool smRenderEdges; + +public: + + ConvexShape(); + virtual ~ConvexShape(); + + DECLARE_CONOBJECT( ConvexShape ); + + // ConsoleObject + static void initPersistFields(); + + // SimObject + virtual void inspectPostApply(); + virtual bool onAdd(); + virtual void onRemove(); + virtual void writeFields(Stream &stream, U32 tabStop); + virtual bool writeField( StringTableEntry fieldname, const char *value ); + + // NetObject + virtual U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream ); + virtual void unpackUpdate( NetConnection *conn, BitStream *stream ); + + // SceneObject + virtual void onScaleChanged(); + virtual void setTransform( const MatrixF &mat ); + virtual void prepRenderImage( SceneRenderState *state ); + virtual void buildConvex( const Box3F &box, Convex *convex ); + virtual bool buildPolyList( PolyListContext context, AbstractPolyList *polyList, const Box3F &box, const SphereF &sphere ); + virtual bool castRay( const Point3F &start, const Point3F &end, RayInfo *info ); + virtual bool collideBox( const Point3F &start, const Point3F &end, RayInfo *info ); + + + void updateBounds( bool recenter ); + void recenter(); + + /// Geometry access. + /// @{ + + MatrixF getSurfaceWorldMat( S32 faceid, bool scaled = false ) const; + void cullEmptyPlanes( Vector< U32 > *removedPlanes ); + void exportToCollada(); + void resizePlanes( const Point3F &size ); + void getSurfaceLineList( S32 surfId, Vector< Point3F > &lineList ); + Geometry& getGeometry() { return mGeometry; } + Vector& getSurfaces() { return mSurfaces; } + void getSurfaceTriangles( S32 surfId, Vector< Point3F > *outPoints, Vector< Point2F > *outCoords, bool worldSpace ); + + /// @} + + /// Geometry Visualization. + /// @{ + + void renderFaceEdges( S32 faceid, const ColorI &color = ColorI::WHITE, F32 lineWidth = 1.0f ); + + /// @} + +protected: + + void _updateMaterial(); + void _updateGeometry( bool updateCollision = false ); + void _updateCollision(); + void _export( OptimizedPolyList *plist, const Box3F &box, const SphereF &sphere ); + + void _renderDebug( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *mat ); + + static S32 QSORT_CALLBACK _comparePlaneDist( const void *a, const void *b ); + + static bool protectedSetSurface( void *object, const char *index, const char *data ); + +protected: + + // The name of the Material we will use for rendering + String mMaterialName; + + // The actual Material instance + BaseMatInstance* mMaterialInst; + + // The GFX vertex and primitive buffers + GFXVertexBufferHandle< VertexType > mVertexBuffer; + GFXPrimitiveBufferHandle mPrimitiveBuffer; + + U32 mVertCount; + U32 mPrimCount; + + Geometry mGeometry; + + Vector< PlaneF > mPlanes; + + Vector< MatrixF > mSurfaces; + + Vector< Point3F > mFaceCenters; + + Convex *mConvexList; + + PhysicsBody *mPhysicsRep; + + /// Geometry visualization + /// @{ + + F32 mNormalLength; + + /// @} + +}; + +#endif // _CONVEXSHAPE_H_ \ No newline at end of file diff --git a/Engine/source/T3D/debris.cpp b/Engine/source/T3D/debris.cpp new file mode 100644 index 000000000..637fe933b --- /dev/null +++ b/Engine/source/T3D/debris.cpp @@ -0,0 +1,942 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/debris.h" + +#include "core/stream/bitStream.h" +#include "math/mathUtils.h" +#include "console/consoleTypes.h" +#include "console/consoleObject.h" +#include "sim/netConnection.h" +#include "scene/sceneRenderState.h" +#include "scene/sceneManager.h" +#include "ts/tsShapeInstance.h" +#include "ts/tsPartInstance.h" +#include "T3D/fx/particleEmitter.h" +#include "T3D/fx/explosion.h" +#include "T3D/gameBase/gameProcess.h" +#include "core/resourceManager.h" +#include "gfx/gfxTransformSaver.h" +#include "console/engineAPI.h" +#include "lighting/lightQuery.h" + + +const U32 csmStaticCollisionMask = TerrainObjectType | + InteriorObjectType; + +const U32 csmDynamicCollisionMask = StaticShapeObjectType; + + +IMPLEMENT_CO_DATABLOCK_V1(DebrisData); + +ConsoleDocClass( DebrisData, + "@brief Stores properties for an individual debris type.\n\n" + + "DebrisData defines the base properties for a Debris object. Typically you'll want a Debris object to consist of " + "a shape and possibly up to two particle emitters. The DebrisData datablock provides the definition for these items, " + "along with physical properties and how a Debris object will react to other game objects, such as water and terrain.\n" + + "@tsexample\n" + "datablock DebrisData(GrenadeDebris)\n" + "{\n" + " shapeFile = \"art/shapes/weapons/ramrifle/debris.dts\";\n" + " emitters[0] = GrenadeDebrisFireEmitter;\n" + " elasticity = 0.4;\n" + " friction = 0.25;\n" + " numBounces = 3;\n" + " bounceVariance = 1;\n" + " explodeOnMaxBounce = false;\n" + " staticOnMaxBounce = false;\n" + " snapOnMaxBounce = false;\n" + " minSpinSpeed = 200;\n" + " maxSpinSpeed = 600;\n" + " lifetime = 4;\n" + " lifetimeVariance = 1.5;\n" + " velocity = 15;\n" + " velocityVariance = 5;\n" + " fade = true;\n" + " useRadiusMass = true;\n" + " baseRadius = 0.3;\n" + " gravModifier = 1.0;\n" + " terminalVelocity = 20;\n" + " ignoreWater = false;\n" + "};\n" + "@endtsexample\n\n" + "@see Debris\n\n" + "@ingroup FX\n" +); + +DebrisData::DebrisData() +{ + dMemset( emitterList, 0, sizeof( emitterList ) ); + dMemset( emitterIDList, 0, sizeof( emitterIDList ) ); + + explosion = NULL; + explosionId = 0; + + velocity = 0.0f; + velocityVariance = 0.0; + elasticity = 0.3f; + friction = 0.2f; + numBounces = 0; + bounceVariance = 0; + minSpinSpeed = maxSpinSpeed = 0.0; + staticOnMaxBounce = false; + explodeOnMaxBounce = false; + snapOnMaxBounce = false; + lifetime = 3.0f; + lifetimeVariance = 0.0f; + minSpinSpeed = 0.0f; + maxSpinSpeed = 0.0f; + textureName = NULL; + shapeName = NULL; + fade = true; + useRadiusMass = false; + baseRadius = 1.0f; + gravModifier = 1.0f; + terminalVelocity = 0.0f; + ignoreWater = true; +} + +bool DebrisData::onAdd() +{ + if(!Parent::onAdd()) + return false; + + for( int i=0; i velocity ) + { + Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: velocityVariance invalid", getName()); + velocityVariance = velocity; + } + if( friction < -10.0f || friction > 10.0f ) + { + Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: friction invalid", getName()); + friction = 0.2f; + } + if( elasticity < -10.0f || elasticity > 10.0f ) + { + Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: elasticity invalid", getName()); + elasticity = 0.2f; + } + if( lifetime < 0.0f || lifetime > 1000.0f ) + { + Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: lifetime invalid", getName()); + lifetime = 3.0f; + } + if( lifetimeVariance < 0.0f || lifetimeVariance > lifetime ) + { + Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: lifetimeVariance invalid", getName()); + lifetimeVariance = 0.0f; + } + if( numBounces < 0 || numBounces > 10000 ) + { + Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: numBounces invalid", getName()); + numBounces = 3; + } + if( bounceVariance < 0 || bounceVariance > numBounces ) + { + Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: bounceVariance invalid", getName()); + bounceVariance = 0; + } + if( minSpinSpeed < -10000.0f || minSpinSpeed > 10000.0f || minSpinSpeed > maxSpinSpeed ) + { + Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: minSpinSpeed invalid", getName()); + minSpinSpeed = maxSpinSpeed - 1.0f; + } + if( maxSpinSpeed < -10000.0f || maxSpinSpeed > 10000.0f ) + { + Con::warnf(ConsoleLogEntry::General, "DebrisData(%s)::onAdd: maxSpinSpeed invalid", getName()); + maxSpinSpeed = 0.0f; + } + + return true; +} + +bool DebrisData::preload(bool server, String &errorStr) +{ + if (Parent::preload(server, errorStr) == false) + return false; + + if( server ) return true; + + if( shapeName && shapeName[0] != '\0' && !bool(shape) ) + { + shape = ResourceManager::get().load(shapeName); + if( bool(shape) == false ) + { + errorStr = String::ToString("DebrisData::load: Couldn't load shape \"%s\"", shapeName); + return false; + } + else + { + TSShapeInstance* pDummy = new TSShapeInstance(shape, !server); + delete pDummy; + } + + } + + return true; +} + +void DebrisData::initPersistFields() +{ + addGroup("Display"); + addField("texture", TypeString, Offset(textureName, DebrisData), + "@brief Texture imagemap to use for this debris object.\n\nNot used any more.\n"); + addField("shapeFile", TypeShapeFilename, Offset(shapeName, DebrisData), + "@brief Object model to use for this debris object.\n\nThis shape is optional. You could have Debris made up of only particles.\n"); + endGroup("Display"); + + addGroup("Datablocks"); + addField("emitters", TYPEID< ParticleEmitterData >(), Offset(emitterList, DebrisData), DDC_NUM_EMITTERS, + "@brief List of particle emitters to spawn along with this debris object.\n\nThese are optional. You could have Debris made up of only a shape.\n"); + addField("explosion", TYPEID< ExplosionData >(), Offset(explosion, DebrisData), + "@brief ExplosionData to spawn along with this debris object.\n\nThis is optional as not all Debris explode.\n"); + endGroup("Datablocks"); + + addGroup("Physical Properties"); + addField("elasticity", TypeF32, Offset(elasticity, DebrisData), + "@brief A floating-point value specifying how 'bouncy' this object is.\n\nMust be in the range of -10 to 10.\n"); + addField("friction", TypeF32, Offset(friction, DebrisData), + "@brief A floating-point value specifying how much velocity is lost to impact and sliding friction.\n\nMust be in the range of -10 to 10.\n"); + addField("numBounces", TypeS32, Offset(numBounces, DebrisData), + "@brief How many times to allow this debris object to bounce until it either explodes, becomes static or snaps (defined in explodeOnMaxBounce, staticOnMaxBounce, snapOnMaxBounce).\n\n" + "Must be within the range of 0 to 10000.\n" + "@see bounceVariance\n"); + addField("bounceVariance", TypeS32, Offset(bounceVariance, DebrisData), + "@brief Allowed variance in the value of numBounces.\n\nMust be less than numBounces.\n@see numBounces\n"); + addField("minSpinSpeed", TypeF32, Offset(minSpinSpeed, DebrisData), + "@brief Minimum speed that this debris object will rotate.\n\nMust be in the range of -10000 to 1000, and must be less than maxSpinSpeed.\n@see maxSpinSpeed\n"); + addField("maxSpinSpeed", TypeF32, Offset(maxSpinSpeed, DebrisData), + "@brief Maximum speed that this debris object will rotate.\n\nMust be in the range of -10000 to 10000.\n@see minSpinSpeed\n"); + addField("gravModifier", TypeF32, Offset(gravModifier, DebrisData), "How much gravity affects debris."); + addField("terminalVelocity", TypeF32, Offset(terminalVelocity, DebrisData), "Max velocity magnitude."); + addField("velocity", TypeF32, Offset(velocity, DebrisData), + "@brief Speed at which this debris object will move.\n\n@see velocityVariance\n"); + addField("velocityVariance", TypeF32, Offset(velocityVariance, DebrisData), + "@brief Allowed variance in the value of velocity\n\nMust be less than velocity.\n@see velocity\n"); + addField("lifetime", TypeF32, Offset(lifetime, DebrisData), + "@brief Amount of time until this debris object is destroyed.\n\nMust be in the range of 0 to 1000.\n@see lifetimeVariance"); + addField("lifetimeVariance", TypeF32, Offset(lifetimeVariance, DebrisData), + "@brief Allowed variance in the value of lifetime.\n\nMust be less than lifetime.\n@see lifetime\n"); + addField("useRadiusMass", TypeBool, Offset(useRadiusMass, DebrisData), + "@brief Use mass calculations based on radius.\n\nAllows for the adjustment of elasticity and friction based on the Debris size.\n@see baseRadius\n"); + addField("baseRadius", TypeF32, Offset(baseRadius, DebrisData), + "@brief Radius at which the standard elasticity and friction apply.\n\nOnly used when useRaduisMass is true.\n@see useRadiusMass.\n"); + endGroup("Physical Properties"); + + addGroup("Behavior"); + addField("explodeOnMaxBounce", TypeBool, Offset(explodeOnMaxBounce, DebrisData), + "@brief If true, this debris object will explode after it has bounced max times.\n\nBe sure to provide an ExplosionData datablock for this to take effect.\n@see explosion\n"); + addField("staticOnMaxBounce", TypeBool, Offset(staticOnMaxBounce, DebrisData), "If true, this debris object becomes static after it has bounced max times."); + addField("snapOnMaxBounce", TypeBool, Offset(snapOnMaxBounce, DebrisData), "If true, this debris object will snap into a resting position on the last bounce."); + addField("fade", TypeBool, Offset(fade, DebrisData), + "@brief If true, this debris object will fade out when destroyed.\n\nThis fade occurs over the last second of the Debris' lifetime.\n"); + addField("ignoreWater", TypeBool, Offset(ignoreWater, DebrisData), "If true, this debris object will not collide with water, acting as if the water is not there."); + endGroup("Behavior"); + + Parent::initPersistFields(); +} + +void DebrisData::packData(BitStream* stream) +{ + Parent::packData(stream); + + stream->write(elasticity); + stream->write(friction); + stream->write(numBounces); + stream->write(bounceVariance); + stream->write(minSpinSpeed); + stream->write(maxSpinSpeed); + stream->write(explodeOnMaxBounce); + stream->write(staticOnMaxBounce); + stream->write(snapOnMaxBounce); + stream->write(lifetime); + stream->write(lifetimeVariance); + stream->write(velocity); + stream->write(velocityVariance); + stream->write(fade); + stream->write(useRadiusMass); + stream->write(baseRadius); + stream->write(gravModifier); + stream->write(terminalVelocity); + stream->write(ignoreWater); + + stream->writeString( textureName ); + stream->writeString( shapeName ); + + for( int i=0; iwriteFlag( emitterList[i] != NULL ) ) + { + stream->writeRangedU32( emitterList[i]->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + } + + if( stream->writeFlag( explosion ) ) + { + stream->writeRangedU32(packed? SimObjectId(explosion): + explosion->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast); + } + +} + +void DebrisData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + + stream->read(&elasticity); + stream->read(&friction); + stream->read(&numBounces); + stream->read(&bounceVariance); + stream->read(&minSpinSpeed); + stream->read(&maxSpinSpeed); + stream->read(&explodeOnMaxBounce); + stream->read(&staticOnMaxBounce); + stream->read(&snapOnMaxBounce); + stream->read(&lifetime); + stream->read(&lifetimeVariance); + stream->read(&velocity); + stream->read(&velocityVariance); + stream->read(&fade); + stream->read(&useRadiusMass); + stream->read(&baseRadius); + stream->read(&gravModifier); + stream->read(&terminalVelocity); + stream->read(&ignoreWater); + + textureName = stream->readSTString(); + shapeName = stream->readSTString(); + + for( int i=0; ireadFlag() ) + { + emitterIDList[i] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + } + + if(stream->readFlag()) + { + explosionId = (S32)stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); + } + else + { + explosionId = 0; + } + +} + + +IMPLEMENT_CO_NETOBJECT_V1(Debris); + +ConsoleDocClass( Debris, + "@brief Base debris class. Uses the DebrisData datablock for properties of individual debris objects.\n\n" + + "Debris is typically made up of a shape and up to two particle emitters. In most cases Debris objects are " + "not created directly. They are usually produced automatically by other means, such as through the Explosion " + "class. When an explosion goes off, its ExplosionData datablock determines what Debris to emit.\n" + + "@tsexample\n" + "datablock ExplosionData(GrenadeLauncherExplosion)\n" + "{\n" + " // Assiging debris data\n" + " debris = GrenadeDebris;\n\n" + " // Adjust how debris is ejected\n" + " debrisThetaMin = 10;\n" + " debrisThetaMax = 60;\n" + " debrisNum = 4;\n" + " debrisNumVariance = 2;\n" + " debrisVelocity = 25;\n" + " debrisVelocityVariance = 5;\n\n" + " // Note: other ExplosionData properties are not listed for this example\n" + "};\n" + "@endtsexample\n\n" + + "@note Debris are client side only objects.\n" + + "@see DebrisData\n" + "@see ExplosionData\n" + "@see Explosion\n" + + "@ingroup FX\n" +); + +DefineEngineMethod(Debris, init, bool, (const char* inputPosition, const char* inputVelocity), + ("1.0 1.0 1.0", "1.0 0.0 0.0"), + "@brief Manually set this piece of debris at the given position with the given velocity.\n\n" + + "Usually you do not manually create Debris objects as they are generated through other means, " + "such as an Explosion. This method exists when you do manually create a Debris object and " + "want to have it start moving.\n" + + "@param inputPosition Position to place the debris.\n" + "@param inputVelocity Velocity to move the debris after it has been placed.\n" + "@return Always returns true.\n" + + "@tsexample\n" + "// Define the position\n" + "%position = \"1.0 1.0 1.0\";\n\n" + "// Define the velocity\n" + "%velocity = \"1.0 0.0 0.0\";\n\n" + "// Inform the debris object of its new position and velocity\n" + "%debris.init(%position,%velocity);\n" + "@endtsexample\n") +{ + Point3F pos; + dSscanf( inputPosition, "%f %f %f", &pos.x, &pos.y, &pos.z ); + + Point3F vel; + dSscanf( inputVelocity, "%f %f %f", &vel.x, &vel.y, &vel.z ); + + object->init( pos, vel ); + + return true; +} + +Debris::Debris() +{ + mTypeMask |= DebrisObjectType | DynamicShapeObjectType; + + mVelocity = Point3F( 0.0f, 0.0f, 4.0f ); + mLifetime = gRandGen.randF( 1.0f, 10.0f ); + mLastPos = getPosition(); + mNumBounces = gRandGen.randI( 0, 1 ); + mSize = 2.0f; + mElapsedTime = 0.0f; + mShape = NULL; + mPart = NULL; + mDataBlock = NULL; + mXRotSpeed = 0.0f; + mZRotSpeed = 0.0f; + mInitialTrans.identity(); + mRadius = 0.2f; + mStatic = false; + + dMemset( mEmitterList, 0, sizeof( mEmitterList ) ); + + // Only allocated client side. + mNetFlags.set( IsGhost ); +} + +Debris::~Debris() +{ + if( mShape ) + { + delete mShape; + mShape = NULL; + } + + if( mPart ) + { + delete mPart; + mPart = NULL; + } +} + +void Debris::initPersistFields() +{ + addGroup( "Debris" ); + + addField( "lifetime", TypeF32, Offset(mLifetime, Debris), + "@brief Length of time for this debris object to exist. When expired, the object will be deleted.\n\n" + "The initial lifetime value comes from the DebrisData datablock.\n" + "@see DebrisData::lifetime\n" + "@see DebrisData::lifetimeVariance\n"); + + endGroup( "Debris" ); + + Parent::initPersistFields(); +} + +void Debris::init( const Point3F &position, const Point3F &velocity ) +{ + setPosition( position ); + setVelocity( velocity ); +} + +bool Debris::onNewDataBlock( GameBaseData *dptr, bool reload ) +{ + mDataBlock = dynamic_cast< DebrisData* >( dptr ); + if( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) ) + return false; + + scriptOnNewDataBlock(); + return true; + +} + +bool Debris::onAdd() +{ + if( !Parent::onAdd() ) + { + return false; + } + + // create emitters + for( int i=0; iemitterList[i] != NULL ) + { + ParticleEmitter * pEmitter = new ParticleEmitter; + pEmitter->onNewDataBlock( mDataBlock->emitterList[i], false ); + if( !pEmitter->registerObject() ) + { + Con::warnf( ConsoleLogEntry::General, "Could not register emitter for particle of class: %s", mDataBlock->getName() ); + delete pEmitter; + pEmitter = NULL; + } + mEmitterList[i] = pEmitter; + } + } + + // set particle sizes based on debris size + F32 sizeList[ParticleData::PDC_NUM_KEYS]; + + if( mEmitterList[0] ) + { + sizeList[0] = mSize * 0.5; + sizeList[1] = mSize; + sizeList[2] = mSize * 1.5; + + mEmitterList[0]->setSizes( sizeList ); + } + + if( mEmitterList[1] ) + { + sizeList[0] = 0.0; + sizeList[1] = mSize * 0.5; + sizeList[2] = mSize; + + mEmitterList[1]->setSizes( sizeList ); + } + + S32 bounceVar = (S32)mDataBlock->bounceVariance; + bounceVar = gRandGen.randI( -bounceVar, bounceVar ); + mNumBounces = mDataBlock->numBounces + bounceVar; + + F32 lifeVar = (mDataBlock->lifetimeVariance * 2.0f * gRandGen.randF(-1.0,1.0)) - mDataBlock->lifetimeVariance; + mLifetime = mDataBlock->lifetime + lifeVar; + + F32 xRotSpeed = gRandGen.randF( mDataBlock->minSpinSpeed, mDataBlock->maxSpinSpeed ); + F32 zRotSpeed = gRandGen.randF( mDataBlock->minSpinSpeed, mDataBlock->maxSpinSpeed ); + zRotSpeed *= gRandGen.randF( 0.1f, 0.5f ); + + mRotAngles.set( xRotSpeed, 0.0f, zRotSpeed ); + + mElasticity = mDataBlock->elasticity; + mFriction = mDataBlock->friction; + + // Setup our bounding box + if( mDataBlock->shape ) + { + mObjBox = mDataBlock->shape->bounds; + } + else + { + mObjBox = Box3F(Point3F(-1, -1, -1), Point3F(1, 1, 1)); + } + + if( mDataBlock->shape ) + { + mShape = new TSShapeInstance( mDataBlock->shape, true); + } + + if( mPart ) + { + // use half radius becuase we want debris to stick in ground + mRadius = mPart->getRadius() * 0.5; + mObjBox = mPart->getBounds(); + } + + resetWorldBox(); + + mInitialTrans = getTransform(); + + if( mDataBlock->velocity != 0.0 ) + { + F32 velocity = mDataBlock->velocity + gRandGen.randF( -mDataBlock->velocityVariance, mDataBlock->velocityVariance ); + + mVelocity.normalizeSafe(); + mVelocity *= velocity; + } + + // mass calculations + if( mDataBlock->useRadiusMass ) + { + if( mRadius < mDataBlock->baseRadius ) + { + mRadius = mDataBlock->baseRadius; + } + + // linear falloff + F32 multFactor = mDataBlock->baseRadius / mRadius; + + mElasticity *= multFactor; + mFriction *= multFactor; + mRotAngles *= multFactor; + } + + + // tell engine the debris exists + gClientSceneGraph->addObjectToScene(this); + + removeFromProcessList(); + ClientProcessList::get()->addObject(this); + + NetConnection* pNC = NetConnection::getConnectionToServer(); + AssertFatal(pNC != NULL, "Error, must have a connection to the server!"); + pNC->addObject(this); + + return true; +} + +void Debris::onRemove() +{ + for( int i=0; ideleteWhenEmpty(); + mEmitterList[i] = NULL; + } + } + + if( mPart ) + { + TSShapeInstance *ss = mPart->getSourceShapeInstance(); + if( ss ) + { + ss->decDebrisRefCount(); + if( ss->getDebrisRefCount() == 0 ) + { + delete ss; + } + } + } + + getSceneManager()->removeObjectFromScene(this); + getContainer()->removeObject(this); + + Parent::onRemove(); +} + +void Debris::processTick(const Move*) +{ + if (mLifetime <= 0.0) + deleteObject(); +} + +void Debris::advanceTime( F32 dt ) +{ + mElapsedTime += dt; + + mLifetime -= dt; + if( mLifetime <= 0.0 ) + { + mLifetime = 0.0; + return; + } + + mLastPos = getPosition(); + + if( !mStatic ) + { + rotate( dt ); + + Point3F nextPos = getPosition(); + computeNewState( nextPos, mVelocity, dt ); + + if( bounce( nextPos, dt ) ) + { + --mNumBounces; + if( mNumBounces <= 0 ) + { + if( mDataBlock->explodeOnMaxBounce ) + { + explode(); + mLifetime = 0.0; + } + if( mDataBlock->snapOnMaxBounce ) + { + // orient debris so it's flat + MatrixF stat = getTransform(); + + Point3F dir; + stat.getColumn( 1, &dir ); + dir.z = 0.0; + + MatrixF newTrans = MathUtils::createOrientFromDir( dir ); + + // hack for shell casings to get them above ground. Need something better - bramage + newTrans.setPosition( getPosition() + Point3F( 0.0f, 0.0f, 0.10f ) ); + + setTransform( newTrans ); + } + if( mDataBlock->staticOnMaxBounce ) + { + mStatic = true; + } + } + } + else + { + setPosition( nextPos ); + } + } + + Point3F pos( getPosition( ) ); + updateEmitters( pos, mVelocity, (U32)(dt * 1000.0)); + +} + +void Debris::rotate( F32 dt ) +{ + MatrixF curTrans = getTransform(); + curTrans.setPosition( Point3F(0.0f, 0.0f, 0.0f) ); + + Point3F curAngles = mRotAngles * dt * M_PI_F/180.0f; + MatrixF rotMatrix( EulerF( curAngles.x, curAngles.y, curAngles.z ) ); + + curTrans.mul( rotMatrix ); + curTrans.setPosition( getPosition() ); + setTransform( curTrans ); +} + +bool Debris::bounce( const Point3F &nextPos, F32 dt ) +{ + Point3F curPos = getPosition(); + + Point3F dir = nextPos - curPos; + if( dir.magnitudeSafe() == 0.0f ) return false; + dir.normalizeSafe(); + Point3F extent = nextPos + dir * mRadius; + F32 totalDist = Point3F( extent - curPos ).magnitudeSafe(); + F32 moveDist = Point3F( nextPos - curPos ).magnitudeSafe(); + F32 movePercent = (moveDist / totalDist); + + RayInfo rayInfo; + U32 collisionMask = csmStaticCollisionMask; + if( !mDataBlock->ignoreWater ) + { + collisionMask |= WaterObjectType; + } + + if( getContainer()->castRay( curPos, extent, collisionMask, &rayInfo ) ) + { + + Point3F reflection = mVelocity - rayInfo.normal * (mDot( mVelocity, rayInfo.normal ) * 2.0f); + mVelocity = reflection; + + Point3F tangent = reflection - rayInfo.normal * mDot( reflection, rayInfo.normal ); + mVelocity -= tangent * mFriction; + + Point3F velDir = mVelocity; + velDir.normalizeSafe(); + + mVelocity *= mElasticity; + + Point3F bouncePos = curPos + dir * rayInfo.t * movePercent; + bouncePos += mVelocity * dt; + + setPosition( bouncePos ); + + mRotAngles *= mElasticity; + + return true; + + } + + return false; + +} + +void Debris::explode() +{ + + if( !mDataBlock->explosion ) return; + + Point3F explosionPos = getPosition(); + + Explosion* pExplosion = new Explosion; + pExplosion->onNewDataBlock(mDataBlock->explosion, false); + + MatrixF trans( true ); + trans.setPosition( getPosition() ); + + pExplosion->setTransform( trans ); + pExplosion->setInitialState( explosionPos, VectorF(0,0,1), 1); + if (!pExplosion->registerObject()) + delete pExplosion; +} + +void Debris::computeNewState( Point3F &newPos, Point3F &newVel, F32 dt ) +{ + // apply gravity + Point3F force = Point3F(0, 0, -9.81 * mDataBlock->gravModifier ); + + if( mDataBlock->terminalVelocity > 0.0001 ) + { + if( newVel.magnitudeSafe() > mDataBlock->terminalVelocity ) + { + newVel.normalizeSafe(); + newVel *= mDataBlock->terminalVelocity; + } + else + { + newVel += force * dt; + } + } + else + { + newVel += force * dt; + } + + newPos += newVel * dt; + +} + +void Debris::updateEmitters( Point3F &pos, Point3F &vel, U32 ms ) +{ + + Point3F axis = -vel; + + if( axis.magnitudeSafe() == 0.0 ) + { + axis = Point3F( 0.0, 0.0, 1.0 ); + } + axis.normalizeSafe(); + + + Point3F lastPos = mLastPos; + + for( int i=0; iemitParticles( lastPos, pos, axis, vel, ms ); + } + } + +} + +void Debris::prepRenderImage( SceneRenderState *state ) +{ + if( !mPart && !mShape ) + return; + + Point3F cameraOffset; + mObjToWorld.getColumn(3,&cameraOffset); + cameraOffset -= state->getDiffuseCameraPosition(); + F32 dist = cameraOffset.len(); + F32 invScale = (1.0f/getMax(getMax(mObjScale.x,mObjScale.y),mObjScale.z)); + + if( mShape ) + { + mShape->setDetailFromDistance( state, dist * invScale ); + if( mShape->getCurrentDetail() < 0 ) + return; + } + + if( mPart ) + { + // get the shapeInstance that we are using for the debris parts + TSShapeInstance *si = mPart->getSourceShapeInstance(); + if ( si ) + si->setDetailFromDistance( state, dist * invScale ); + } + + prepBatchRender( state ); +} + +void Debris::prepBatchRender( SceneRenderState *state ) +{ + if ( !mShape && !mPart ) + return; + + GFXTransformSaver saver; + + F32 alpha = 1.0; + if( mDataBlock->fade ) + { + if( mLifetime < 1.0 ) alpha = mLifetime; + } + + Point3F cameraOffset; + mObjToWorld.getColumn(3,&cameraOffset); + cameraOffset -= state->getCameraPosition(); + + // Set up our TS render state. + TSRenderState rdata; + rdata.setSceneState( state ); + + // We might have some forward lit materials + // so pass down a query to gather lights. + LightQuery query; + query.init( getWorldSphere() ); + rdata.setLightQuery( &query ); + + if( mShape ) + { + MatrixF mat = getRenderTransform(); + GFX->setWorldMatrix( mat ); + + rdata.setFadeOverride( alpha ); + mShape->render( rdata ); + } + else + { + if (mPart->getCurrentObjectDetail() != -1) + { + MatrixF mat = getRenderTransform(); + GFX->setWorldMatrix( mat ); + + rdata.setFadeOverride( alpha ); + mPart->render( rdata ); + } + } +} + +void Debris::setSize( F32 size ) +{ + mSize = size; +} diff --git a/Engine/source/T3D/debris.h b/Engine/source/T3D/debris.h new file mode 100644 index 000000000..925da1053 --- /dev/null +++ b/Engine/source/T3D/debris.h @@ -0,0 +1,173 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _DEBRIS_H_ +#define _DEBRIS_H_ + +#ifndef __RESOURCE_H__ +#include "core/resource.h" +#endif +#ifndef _GAMEBASE_H_ +#include "T3D/gameBase/gameBase.h" +#endif + +class ParticleEmitterData; +class ParticleEmitter; +class ExplosionData; +class TSPartInstance; +class TSShapeInstance; +class TSShape; + +//************************************************************************** +// Debris Data +//************************************************************************** +struct DebrisData : public GameBaseData +{ + typedef GameBaseData Parent; + + //----------------------------------------------------------------------- + // Data Decs + //----------------------------------------------------------------------- + enum DebrisDataConst + { + DDC_NUM_EMITTERS = 2, + }; + + + //----------------------------------------------------------------------- + // Debris datablock + //----------------------------------------------------------------------- + F32 velocity; + F32 velocityVariance; + F32 friction; + F32 elasticity; + F32 lifetime; + F32 lifetimeVariance; + U32 numBounces; + U32 bounceVariance; + F32 minSpinSpeed; + F32 maxSpinSpeed; + bool explodeOnMaxBounce; // explodes after it has bounced max times + bool staticOnMaxBounce; // becomes static after bounced max times + bool snapOnMaxBounce; // snap into a "resting" position on last bounce + bool fade; + bool useRadiusMass; // use mass calculations based on radius + F32 baseRadius; // radius at which the standard elasticity and friction apply + F32 gravModifier; // how much gravity affects debris + F32 terminalVelocity; // max velocity magnitude + bool ignoreWater; + + const char* shapeName; + Resource shape; + + StringTableEntry textureName; + + + S32 explosionId; + ExplosionData * explosion; + ParticleEmitterData* emitterList[DDC_NUM_EMITTERS]; + S32 emitterIDList[DDC_NUM_EMITTERS]; + + DebrisData(); + + bool onAdd(); + bool preload( bool server, String &errorStr ); + static void initPersistFields(); + void packData(BitStream* stream); + void unpackData(BitStream* stream); + + DECLARE_CONOBJECT(DebrisData); + +}; + +//************************************************************************** +// Debris +//************************************************************************** +class Debris : public GameBase +{ + typedef GameBase Parent; + +private: + S32 mNumBounces; + F32 mSize; + Point3F mLastPos; + Point3F mVelocity; + F32 mLifetime; + DebrisData * mDataBlock; + F32 mElapsedTime; + TSShapeInstance * mShape; + TSPartInstance * mPart; + MatrixF mInitialTrans; + F32 mXRotSpeed; + F32 mZRotSpeed; + Point3F mRotAngles; + F32 mRadius; + bool mStatic; + F32 mElasticity; + F32 mFriction; + + SimObjectPtr mEmitterList[ DebrisData::DDC_NUM_EMITTERS ]; + + /// Bounce the debris - returns true if debris bounces. + bool bounce( const Point3F &nextPos, F32 dt ); + + /// Compute state of debris as if it hasn't collided with anything. + void computeNewState( Point3F &newPos, Point3F &newVel, F32 dt ); + + void explode(); + void rotate( F32 dt ); + +protected: + virtual void processTick(const Move* move); + virtual void advanceTime( F32 dt ); + void prepRenderImage(SceneRenderState *state); + void prepBatchRender(SceneRenderState *state); + + + bool onAdd(); + void onRemove(); + void updateEmitters( Point3F &pos, Point3F &vel, U32 ms ); + +public: + + Debris(); + ~Debris(); + + static void initPersistFields(); + + bool onNewDataBlock( GameBaseData *dptr, bool reload ); + + void init( const Point3F &position, const Point3F &velocity ); + void setLifetime( F32 lifetime ){ mLifetime = lifetime; } + void setPartInstance( TSPartInstance *part ){ mPart = part; } + void setSize( F32 size ); + void setVelocity( const Point3F &vel ){ mVelocity = vel; } + void setRotAngles( const Point3F &angles ){ mRotAngles = angles; } + + DECLARE_CONOBJECT(Debris); + +}; + + + + +#endif diff --git a/Engine/source/T3D/decal/decalData.cpp b/Engine/source/T3D/decal/decalData.cpp new file mode 100644 index 000000000..67436e991 --- /dev/null +++ b/Engine/source/T3D/decal/decalData.cpp @@ -0,0 +1,481 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/decal/decalData.h" + +#include "console/consoleTypes.h" +#include "core/stream/bitStream.h" +#include "math/mathIO.h" +#include "materials/materialManager.h" +#include "materials/baseMatInstance.h" +#include "T3D/objectTypes.h" +#include "console/engineAPI.h" + +GFXImplementVertexFormat( DecalVertex ) +{ + addElement( "POSITION", GFXDeclType_Float3 ); + addElement( "NORMAL", GFXDeclType_Float3 ); + addElement( "TANGENT", GFXDeclType_Float3 ); + addElement( "COLOR", GFXDeclType_Color ); + addElement( "TEXCOORD", GFXDeclType_Float2, 0 ); +} + +IMPLEMENT_CO_DATABLOCK_V1( DecalData ); + +ConsoleDocClass( DecalData, + "@brief A datablock describing an individual decal.\n\n" + + "The textures defined by the decal Material can be divided into multiple " + "rectangular sub-textures as shown below, with a different sub-texture " + "selected by all decals using the same DecalData (via #frame) or each decal " + "instance (via #randomize).\n" + + "@image html images/decal_example.png \"Example of a Decal imagemap\"\n" + + "@tsexample\n" + "datablock DecalData(BulletHoleDecal)\n" + "{\n" + " material = \"DECAL_BulletHole\";\n" + " size = \"5.0\";\n" + " lifeSpan = \"50000\";\n" + " randomize = \"1\";\n" + " texRows = \"2\";\n" + " texCols = \"2\";\n" + " clippingAngle = \"60\";\n" + "};\n" + "@endtsexample\n\n" + "@see Decals\n" + "@ingroup Decals\n" + "@ingroup FX\n" +); + +//------------------------------------------------------------------------- +// DecalData +//------------------------------------------------------------------------- + +DecalData::DecalData() +{ + size = 5; + materialName = ""; + + lifeSpan = 5000; + fadeTime = 1000; + + frame = 0; + randomize = false; + texRows = 1; + texCols = 1; + + fadeStartPixelSize = -1.0f; + fadeEndPixelSize = 200.0f; + + material = NULL; + matInst = NULL; + + renderPriority = 10; + clippingMasks = STATIC_COLLISION_TYPEMASK; + clippingAngle = 89.0f; + + texCoordCount = 1; + + // TODO: We could in theory calculate if we can skip + // normals on the decal by checking the material features. + skipVertexNormals = false; + + for ( S32 i = 0; i < 16; i++ ) + { + texRect[i].point.set( 0.0f, 0.0f ); + texRect[i].extent.set( 1.0f, 1.0f ); + } +} + +DecalData::~DecalData() +{ + SAFE_DELETE( matInst ); +} + +bool DecalData::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + if (size < 0.0) { + Con::warnf("DecalData::onAdd: size < 0"); + size = 0; + } + + getSet()->addObject( this ); + + if( texRows > 1 || texCols > 1 ) + reloadRects(); + + return true; +} + +void DecalData::onRemove() +{ + Parent::onRemove(); +} + +void DecalData::initPersistFields() +{ + addGroup( "Decal" ); + + addField( "size", TypeF32, Offset( size, DecalData ), + "Width and height of the decal in meters before scale is applied." ); + + addField( "material", TypeMaterialName, Offset( materialName, DecalData ), + "Material to use for this decal." ); + + addField( "lifeSpan", TypeS32, Offset( lifeSpan, DecalData ), + "Time (in milliseconds) before this decal will be automatically deleted." ); + + addField( "fadeTime", TypeS32, Offset( fadeTime, DecalData ), + "@brief Time (in milliseconds) over which to fade out the decal before " + "deleting it at the end of its lifetime.\n\n" + "@see lifeSpan" ); + + endGroup( "Decal" ); + + addGroup( "Rendering" ); + + addField( "fadeStartPixelSize", TypeF32, Offset( fadeStartPixelSize, DecalData ), + "@brief LOD value - size in pixels at which decals of this type begin " + "to fade out.\n\n" + "This should be a larger value than #fadeEndPixelSize. However, you may " + "also set this to a negative value to disable lod-based fading." ); + + addField( "fadeEndPixelSize", TypeF32, Offset( fadeEndPixelSize, DecalData ), + "@brief LOD value - size in pixels at which decals of this type are " + "fully faded out.\n\n" + "This should be a smaller value than #fadeStartPixelSize." ); + + addField( "renderPriority", TypeS8, Offset( renderPriority, DecalData ), + "Default renderPriority for decals of this type (determines draw " + "order when decals overlap)." ); + + addField( "clippingAngle", TypeF32, Offset( clippingAngle, DecalData ), + "The angle in degrees used to clip geometry that faces away from the " + "decal projection direction." ); + + endGroup( "Rendering" ); + + addGroup( "Texturing" ); + + addField( "frame", TypeS32, Offset( frame, DecalData ), + "Index of the texture rectangle within the imagemap to use for this decal." ); + + addField( "randomize", TypeBool, Offset( randomize, DecalData ), + "If true, a random frame from the imagemap is selected for each " + "instance of the decal." ); + + addField( "textureCoordCount", TypeS32, Offset( texCoordCount, DecalData ), + "Number of individual frames in the imagemap (maximum 16)." ); + + addField( "texRows", TypeS32, Offset( texRows, DecalData ), + "@brief Number of rows in the supplied imagemap.\n\n" + "Use #texRows and #texCols if the imagemap frames are arranged in a " + "grid; use #textureCoords to manually specify UV coordinates for " + "irregular sized frames." ); + + addField( "texCols", TypeS32, Offset( texCols, DecalData ), + "@brief Number of columns in the supplied imagemap.\n\n" + "Use #texRows and #texCols if the imagemap frames are arranged in a " + "grid; use #textureCoords to manually specify UV coordinates for " + "irregular sized frames." ); + + addField( "textureCoords", TypeRectF, Offset( texRect, DecalData ), MAX_TEXCOORD_COUNT, + "@brief An array of RectFs (topleft.x topleft.y extent.x extent.y) " + "representing the UV coordinates for each frame in the imagemap.\n\n" + "@note This field should only be set if the imagemap frames are " + "irregular in size. Otherwise use the #texRows and #texCols fields " + "and the UV coordinates will be calculated automatically." ); + + endGroup( "Texturing" ); + + Parent::initPersistFields(); +} + +void DecalData::onStaticModified( const char *slotName, const char *newValue ) +{ + Parent::onStaticModified( slotName, newValue ); + + if ( !isProperlyAdded() ) + return; + + // To allow changing materials live. + if ( dStricmp( slotName, "material" ) == 0 ) + { + materialName = newValue; + _updateMaterial(); + } + // To allow changing name live. + else if ( dStricmp( slotName, "name" ) == 0 ) + { + lookupName = getName(); + } + else if ( dStricmp( slotName, "renderPriority" ) == 0 ) + { + renderPriority = getMax( renderPriority, (U8)1 ); + } +} + +bool DecalData::preload( bool server, String &errorStr ) +{ + if (Parent::preload(server, errorStr) == false) + return false; + + // Server assigns name to lookupName, + // client assigns lookupName in unpack. + if ( server ) + lookupName = getName(); + + return true; +} + +void DecalData::packData( BitStream *stream ) +{ + Parent::packData( stream ); + + stream->write( lookupName ); + stream->write( size ); + stream->write( materialName ); + stream->write( lifeSpan ); + stream->write( fadeTime ); + stream->write( texCoordCount ); + + for (S32 i = 0; i < texCoordCount; i++) + mathWrite( *stream, texRect[i] ); + + stream->write( fadeStartPixelSize ); + stream->write( fadeEndPixelSize ); + stream->write( renderPriority ); + stream->write( clippingMasks ); + stream->write( clippingAngle ); + + stream->write( texRows ); + stream->write( texCols ); + stream->write( frame ); + stream->write( randomize ); +} + +void DecalData::unpackData( BitStream *stream ) +{ + Parent::unpackData( stream ); + + stream->read( &lookupName ); + stream->read( &size ); + stream->read( &materialName ); + _updateMaterial(); + stream->read( &lifeSpan ); + stream->read( &fadeTime ); + stream->read( &texCoordCount ); + + for (S32 i = 0; i < texCoordCount; i++) + mathRead(*stream, &texRect[i]); + + stream->read( &fadeStartPixelSize ); + stream->read( &fadeEndPixelSize ); + stream->read( &renderPriority ); + stream->read( &clippingMasks ); + stream->read( &clippingAngle ); + + stream->read( &texRows ); + stream->read( &texCols ); + stream->read( &frame ); + stream->read( &randomize ); +} + +void DecalData::_initMaterial() +{ + SAFE_DELETE( matInst ); + + if ( material ) + matInst = material->createMatInstance(); + else + matInst = MATMGR->createMatInstance( "WarningMaterial" ); + + GFXStateBlockDesc desc; + desc.setZReadWrite( true, false ); + //desc.zFunc = GFXCmpLess; + matInst->addStateBlockDesc( desc ); + + matInst->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat() ); + if( !matInst->isValid() ) + { + Con::errorf( "DecalData::_initMaterial - failed to create material instance for '%s'", materialName.c_str() ); + SAFE_DELETE( matInst ); + matInst = MATMGR->createMatInstance( "WarningMaterial" ); + matInst->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat< DecalVertex >() ); + } +} + +void DecalData::_updateMaterial() +{ + if ( materialName.isEmpty() ) + return; + + Material *pMat = NULL; + if ( !Sim::findObject( materialName, pMat ) ) + { + Con::printf( "DecalData::unpackUpdate, failed to find Material of name %s!", materialName.c_str() ); + return; + } + + material = pMat; + + // Only update material instance if we have one allocated. + if ( matInst ) + _initMaterial(); +} + +Material* DecalData::getMaterial() +{ + if ( !material ) + { + _updateMaterial(); + if ( !material ) + material = static_cast( Sim::findObject("WarningMaterial") ); + } + + return material; +} + +BaseMatInstance* DecalData::getMaterialInstance() +{ + if ( !material || !matInst || matInst->getMaterial() != material ) + _initMaterial(); + + return matInst; +} + +DecalData* DecalData::findDatablock( String searchName ) +{ + StringTableEntry className = DecalData::getStaticClassRep()->getClassName(); + DecalData *pData; + SimSet *set = getSet(); + SimSetIterator iter( set ); + + for ( ; *iter; ++iter ) + { + if ( (*iter)->getClassName() != className ) + { + Con::errorf( "DecalData::findDatablock - found a class %s object in DecalDataSet!", (*iter)->getClassName() ); + continue; + } + + pData = static_cast( *iter ); + if ( pData->lookupName.equal( searchName, String::NoCase ) ) + return pData; + } + + return NULL; +} + +void DecalData::inspectPostApply() +{ + reloadRects(); +} + +void DecalData::reloadRects() +{ + F32 rowsBase = 0; + F32 colsBase = 0; + bool canRenderRowsByFrame = false; + bool canRenderColsByFrame = false; + S32 id = 0; + + texRect[id].point.x = 0.f; + texRect[id].extent.x = 1.f; + texRect[id].point.y = 0.f; + texRect[id].extent.y = 1.f; + + texCoordCount = (texRows * texCols) - 1; + + if( texCoordCount > 16 ) + { + Con::warnf("Coordinate max must be lower than 16 to be a valid decal !"); + texRows = 1; + texCols = 1; + texCoordCount = 1; + } + + // use current datablock information in order to build a template to extract + // coordinates from. + if( texRows > 1 ) + { + rowsBase = ( 1.f / texRows ); + canRenderRowsByFrame = true; + } + if( texCols > 1 ) + { + colsBase = ( 1.f / texCols ); + canRenderColsByFrame = true; + } + + // if were able, lets enter the loop + if( frame >= 0 && (canRenderRowsByFrame || canRenderColsByFrame) ) + { + // columns first then rows + for ( S32 colId = 1; colId <= texCols; colId++ ) + { + for ( S32 rowId = 1; rowId <= texRows; rowId++, id++ ) + { + // if were over the coord count, lets go + if(id > texCoordCount) + return; + + // keep our dimensions correct + if(rowId > texRows) + rowId = 1; + + if(colId > texCols) + colId = 1; + + // start setting our rect values per frame + if( canRenderRowsByFrame ) + { + texRect[id].point.x = rowsBase * ( rowId - 1 ); + texRect[id].extent.x = rowsBase; + } + + if( canRenderColsByFrame ) + { + texRect[id].point.y = colsBase * ( colId - 1 ); + texRect[id].extent.y = colsBase; + } + } + } + } +} + +DefineEngineMethod(DecalData, postApply, void, (),, + "Recompute the imagemap sub-texture rectangles for this DecalData.\n" + "@tsexample\n" + "// Inform the decal object to reload its imagemap and frame data.\n" + "%decalData.texRows = 4;\n" + "%decalData.postApply();\n" + "@endtsexample\n") +{ + object->inspectPostApply(); +} diff --git a/Engine/source/T3D/decal/decalData.h b/Engine/source/T3D/decal/decalData.h new file mode 100644 index 000000000..4eb5001a2 --- /dev/null +++ b/Engine/source/T3D/decal/decalData.h @@ -0,0 +1,143 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _DECALDATA_H_ +#define _DECALDATA_H_ + +#ifndef _SIMDATABLOCK_H_ +#include "console/simDatablock.h" +#endif +#ifndef _MATERIALDEFINITION_H_ +#include "materials/materialDefinition.h" +#endif +#ifndef _MRECT_H_ +#include "math/mRect.h" +#endif +#ifndef _DYNAMIC_CONSOLETYPES_H_ +#include "console/dynamicTypes.h" +#endif + +GFXDeclareVertexFormat( DecalVertex ) +{ + // .xyz = coords + Point3F point; + Point3F normal; + Point3F tangent; + GFXVertexColor color; + Point2F texCoord; +}; + +/// DataBlock implementation for decals. +class DecalData : public SimDataBlock +{ + typedef SimDataBlock Parent; + + public: + + enum { MAX_TEXCOORD_COUNT = 16 }; + + F32 size; + + /// Milliseconds for decal to expire. + U32 lifeSpan; + + /// Milliseconds for decal to fade after expiration. + U32 fadeTime; + + S32 texCoordCount; + RectF texRect[MAX_TEXCOORD_COUNT]; + + /// + S32 frame; + bool randomize; + S32 texRows; + S32 texCols; + + F32 fadeStartPixelSize; + F32 fadeEndPixelSize; + + /// Name of material to use. + String materialName; + + /// Render material for decal. + SimObjectPtr material; + + /// Material instance for decal. + BaseMatInstance *matInst; + + String lookupName; + + U8 renderPriority; + + S32 clippingMasks; + + /// The angle in degress used to clip geometry + /// that faces away from the decal projection. + F32 clippingAngle; + + /// Skip generating and collecting vertex normals for decals. + bool skipVertexNormals; + + public: + + DecalData(); + ~DecalData(); + + DECLARE_CONOBJECT(DecalData); + static void initPersistFields(); + virtual void onStaticModified( const char *slotName, const char *newValue = NULL ); + + virtual bool onAdd(); + virtual void onRemove(); + + virtual bool preload( bool server, String &errorStr ); + virtual void packData( BitStream* ); + virtual void unpackData( BitStream* ); + + Material* getMaterial(); + BaseMatInstance* getMaterialInstance(); + + static SimSet* getSet(); + static DecalData* findDatablock( String lookupName ); + + virtual void inspectPostApply(); + void reloadRects(); + + protected: + + void _initMaterial(); + void _updateMaterial(); +}; + +inline SimSet* DecalData::getSet() +{ + SimSet *set = NULL; + if ( !Sim::findObject( "DecalDataSet", set ) ) + { + set = new SimSet; + set->registerObject( "DecalDataSet" ); + Sim::getRootGroup()->addObject( set ); + } + return set; +} + +#endif // _DECALDATA_H_ \ No newline at end of file diff --git a/Engine/source/T3D/decal/decalDataFile.cpp b/Engine/source/T3D/decal/decalDataFile.cpp new file mode 100644 index 000000000..002184c16 --- /dev/null +++ b/Engine/source/T3D/decal/decalDataFile.cpp @@ -0,0 +1,407 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "decalDataFile.h" + +#include "math/mathIO.h" +#include "core/tAlgorithm.h" +#include "core/stream/fileStream.h" + +#include "T3D/decal/decalManager.h" +#include "T3D/decal/decalData.h" + + +template<> +void* Resource::create( const Torque::Path &path ) +{ + FileStream stream; + stream.open( path.getFullPath(), Torque::FS::File::Read ); + if ( stream.getStatus() != Stream::Ok ) + return NULL; + + DecalDataFile *file = new DecalDataFile(); + if ( !file->read( stream ) ) + { + delete file; + return NULL; + } + + return file; +} + +template<> +ResourceBase::Signature Resource::signature() +{ + return MakeFourCC('d','e','c','f'); +} + + +//----------------------------------------------------------------------------- + +DecalDataFile::DecalDataFile() + : mIsDirty( false ), + mSphereWithLastInsertion( NULL ) +{ + VECTOR_SET_ASSOCIATION( mSphereList ); +} + +//----------------------------------------------------------------------------- + +DecalDataFile::~DecalDataFile() +{ + clear(); +} + +//----------------------------------------------------------------------------- + +void DecalDataFile::clear() +{ + for ( U32 i=0; i < mSphereList.size(); i++ ) + delete mSphereList[i]; + + mSphereList.clear(); + mSphereWithLastInsertion = NULL; + mChunker.freeBlocks(); + + mIsDirty = true; +} + +//----------------------------------------------------------------------------- + +bool DecalDataFile::write( Stream& stream ) +{ + // Write our identifier... so we have a better + // idea if we're reading pure garbage. + // This identifier stands for "Torque Decal Data File". + stream.write( 4, "TDDF" ); + + // Now the version number. + stream.write( (U8)FILE_VERSION ); + + Vector allDecals; + + // Gather all DecalInstances that should be saved. + for ( U32 i = 0; i < mSphereList.size(); i++ ) + { + Vector::const_iterator item = mSphereList[i]->mItems.begin(); + for ( ; item != mSphereList[i]->mItems.end(); item++ ) + { + if ( (*item)->mFlags & SaveDecal ) + allDecals.push_back( (*item) ); + } + } + + // Gather all the DecalData datablocks used. + Vector allDatablocks; + for ( U32 i = 0; i < allDecals.size(); i++ ) + allDatablocks.push_back_unique( allDecals[i]->mDataBlock ); + + // Write out datablock lookupNames. + U32 count = allDatablocks.size(); + stream.write( count ); + for ( U32 i = 0; i < count; i++ ) + stream.write( allDatablocks[i]->lookupName ); + + Vector::iterator dataIter; + + // Write out the DecalInstance list. + count = allDecals.size(); + stream.write( count ); + for ( U32 i = 0; i < count; i++ ) + { + DecalInstance *inst = allDecals[i]; + + dataIter = find( allDatablocks.begin(), allDatablocks.end(), inst->mDataBlock ); + U8 dataIndex = dataIter - allDatablocks.begin(); + + stream.write( dataIndex ); + mathWrite( stream, inst->mPosition ); + mathWrite( stream, inst->mNormal ); + mathWrite( stream, inst->mTangent ); + stream.write( inst->mTextureRectIdx ); + stream.write( inst->mSize ); + stream.write( inst->mRenderPriority ); + } + + // Clear the dirty flag. + mIsDirty = false; + + return true; +} + +//----------------------------------------------------------------------------- + +bool DecalDataFile::read( Stream &stream ) +{ + // NOTE: we are shortcutting by just saving out the DecalInst and + // using regular addDecal methods to add them, which will end up + // generating the DecalSphere(s) in the process. + // It would be more efficient however to just save out all the data + // and read it all in with no calculation required. + + // Read our identifier... so we know we're + // not reading in pure garbage. + char id[4] = { 0 }; + stream.read( 4, id ); + if ( dMemcmp( id, "TDDF", 4 ) != 0 ) + { + Con::errorf( "DecalDataFile::read() - This is not a Decal file!" ); + return false; + } + + // Empty ourselves before we really begin reading. + clear(); + + // Now the version number. + U8 version; + stream.read( &version ); + if ( version != (U8)FILE_VERSION ) + { + Con::errorf( "DecalDataFile::read() - file versions do not match!" ); + Con::errorf( "You must manually delete the old .decals file before continuing!" ); + return false; + } + + // Read in the lookupNames of the DecalData datablocks and recover the datablock. + Vector allDatablocks; + U32 count; + stream.read( &count ); + allDatablocks.setSize( count ); + for ( U32 i = 0; i < count; i++ ) + { + String lookupName; + stream.read( &lookupName ); + + DecalData *data = DecalData::findDatablock( lookupName ); + + if ( !data ) + { + char name[512]; + dSprintf(name, 512, "%s_missing", lookupName.c_str()); + + DecalData *stubCheck = DecalData::findDatablock( name ); + if( !stubCheck ) + { + data = new DecalData; + data->lookupName = name; + data->registerObject(name); + Sim::getRootGroup()->addObject( data ); + data->materialName = "WarningMaterial"; + data->material = dynamic_cast(Sim::findObject("WarningMaterial")); + + Con::errorf( "DecalDataFile::read() - DecalData %s does not exist! Temporarily created %s_missing.", lookupName.c_str() ); + } + } + + allDatablocks[ i ] = data; + } + + U8 dataIndex; + DecalData *data; + + // Now read all the DecalInstance(s). + stream.read( &count ); + for ( U32 i = 0; i < count; i++ ) + { + DecalInstance *inst = _allocateInstance(); + + stream.read( &dataIndex ); + mathRead( stream, &inst->mPosition ); + mathRead( stream, &inst->mNormal ); + mathRead( stream, &inst->mTangent ); + stream.read( &inst->mTextureRectIdx ); + stream.read( &inst->mSize ); + stream.read( &inst->mRenderPriority ); + + inst->mVisibility = 1.0f; + inst->mFlags = PermanentDecal | SaveDecal | ClipDecal; + inst->mCreateTime = Sim::getCurrentTime(); + inst->mVerts = NULL; + inst->mIndices = NULL; + inst->mVertCount = 0; + inst->mIndxCount = 0; + + data = allDatablocks[ dataIndex ]; + + if ( data ) + { + inst->mDataBlock = data; + + _addDecalToSpheres( inst ); + + // onload set instances should get added to the appropriate vec + inst->mId = gDecalManager->mDecalInstanceVec.size(); + gDecalManager->mDecalInstanceVec.push_back(inst); + } + else + { + _freeInstance( inst ); + Con::errorf( "DecalDataFile::read - cannot find DecalData for DecalInstance read from disk." ); + } + } + + // Clear the dirty flag. + mIsDirty = false; + + return true; +} + +//----------------------------------------------------------------------------- + +DecalInstance* DecalDataFile::addDecal( const Point3F& pos, const Point3F& normal, const Point3F& tangent, + DecalData* decalData, F32 decalScale, S32 decalTexIndex, U8 flags ) +{ + DecalInstance* newDecal = _allocateInstance(); + + newDecal->mRenderPriority = 0; + newDecal->mCustomTex = NULL; + newDecal->mId = -1; + + newDecal->mPosition = pos; + newDecal->mNormal = normal; + newDecal->mTangent = tangent; + + newDecal->mSize = decalData->size * decalScale; + + newDecal->mDataBlock = decalData; + + S32 frame = newDecal->mDataBlock->frame; + // randomize the frame if the flag is set. this number is used directly below us + // when calculating render coords + if ( decalData->randomize ) + frame = gRandGen.randI(); + + frame %= getMax( decalData->texCoordCount, 0 ) + 1; + + newDecal->mTextureRectIdx = frame; + + newDecal->mVisibility = 1.0f; + + newDecal->mLastAlpha = -1.0f; + + newDecal->mCreateTime = Sim::getCurrentTime(); + + newDecal->mVerts = NULL; + newDecal->mIndices = NULL; + newDecal->mVertCount = 0; + newDecal->mIndxCount = 0; + + newDecal->mFlags = flags; + newDecal->mFlags |= ClipDecal; + + _addDecalToSpheres( newDecal ); + + return newDecal; +} + +//----------------------------------------------------------------------------- + +void DecalDataFile::_addDecalToSpheres( DecalInstance* inst ) +{ + // First try the sphere we have last inserted an item into, if there is one. + // Given a good spatial locality of insertions, this is a reasonable first + // guess as a good candidate. + + if( mSphereWithLastInsertion && mSphereWithLastInsertion->tryAddItem( inst ) ) + return; + + // Otherwise, go through the list and try to find an existing sphere that meets + // our tolerances. + + for( U32 i = 0; i < mSphereList.size(); i++ ) + { + DecalSphere* sphere = mSphereList[i]; + + if( sphere == mSphereWithLastInsertion ) + continue; + + if( sphere->tryAddItem( inst ) ) + { + mSphereWithLastInsertion = sphere; + return; + } + } + + // Didn't find a suitable existing sphere, so create a new one. + + DecalSphere* sphere = new DecalSphere( inst->mPosition, inst->mSize / 2.f ); + + mSphereList.push_back( sphere ); + mSphereWithLastInsertion = sphere; + + sphere->mItems.push_back( inst ); +} + +//----------------------------------------------------------------------------- + +void DecalDataFile::removeDecal( DecalInstance *inst ) +{ + if( !_removeDecalFromSpheres( inst ) ) + return; + + _freeInstance( inst ); +} + +//----------------------------------------------------------------------------- + +bool DecalDataFile::_removeDecalFromSpheres( DecalInstance *inst ) +{ + for( U32 i = 0; i < mSphereList.size(); i++ ) + { + DecalSphere* sphere = mSphereList[ i ]; + Vector< DecalInstance* > &items = sphere->mItems; + + // Try to remove the instance from the list of this sphere. + // If that fails, the instance doesn't belong to this sphere + // so continue. + + if( !items.remove( inst ) ) + continue; + + // If the sphere is now empty, remove it. Otherwise, update + // it's bounds. + + if( items.empty() ) + { + if( mSphereWithLastInsertion == sphere ) + mSphereWithLastInsertion = NULL; + + delete sphere; + mSphereList.erase( i ); + } + else + sphere->updateWorldSphere(); + + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- + +void DecalDataFile::notifyDecalModified( DecalInstance *inst ) +{ + _removeDecalFromSpheres( inst ); + _addDecalToSpheres( inst ); +} diff --git a/Engine/source/T3D/decal/decalDataFile.h b/Engine/source/T3D/decal/decalDataFile.h new file mode 100644 index 000000000..abe46ecfd --- /dev/null +++ b/Engine/source/T3D/decal/decalDataFile.h @@ -0,0 +1,133 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _DECALDATAFILE_H_ +#define _DECALDATAFILE_H_ + +#ifndef _DATACHUNKER_H_ +#include "core/dataChunker.h" +#endif + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +#ifndef _DECALSPHERE_H_ +#include "T3D/decal/decalSphere.h" +#endif + + +class Stream; +class DecalData; + + + +/// This is the data file for decals. +/// Not intended to be used directly, do your work with decals +/// via the DecalManager. +class DecalDataFile +{ + protected: + + enum { FILE_VERSION = 5 }; + + /// Set to true if the file is dirty and + /// needs to be saved before being destroyed. + bool mIsDirty; + + /// @name Memory Management + /// @{ + + /// Allocator for DecalInstances. + FreeListChunker< DecalInstance > mChunker; + + /// Allocate a new, uninitialized DecalInstance. + DecalInstance* _allocateInstance() { return mChunker.alloc(); } + + /// Free the memory of the given DecalInstance. + void _freeInstance( DecalInstance *decal ) { mChunker.free( decal ); } + + /// @} + + /// @name Instance Management + /// @{ + + /// The decal sphere that we have last insert an item into. This sphere + /// is most likely to be a good candidate for the next insertion so + /// test this sphere first. + DecalSphere* mSphereWithLastInsertion; + + /// List of bounding sphere shapes that contain and organize + /// DecalInstances for optimized culling and lookup. + Vector< DecalSphere* > mSphereList; + + /// Add the given decal to the sphere list. + void _addDecalToSpheres( DecalInstance *inst ); + + /// Remove the decal from the sphere list. + bool _removeDecalFromSpheres( DecalInstance *inst ); + + /// @} + + public: + + DecalDataFile(); + virtual ~DecalDataFile(); + + Vector< DecalSphere* >& getSphereList() { return mSphereList; } + const Vector< DecalSphere* >& getSphereList() const { return mSphereList; } + + /// Return true if the decal data has been modified since the last save or load. + bool isDirty() const { return mIsDirty; } + + /// Deletes all the data and resets the + /// file to an empty state. + void clear(); + + /// @name I/O + /// @{ + + /// Write the decal data to the given stream. + bool write( Stream& stream ); + + /// Read the decal data from the given stream. + bool read( Stream& stream ); + + /// @} + + /// @name Decal Management + /// @{ + + /// Create a new decal in this file using the given data. + DecalInstance* addDecal( const Point3F& pos, const Point3F& normal, const Point3F& tangent, + DecalData* decalData, F32 decalScale, S32 decalTexIndex, U8 flags ); + + /// Remove a decal from the file. + void removeDecal( DecalInstance *inst ); + + /// Let the file know that the data of the given decal has changed. + void notifyDecalModified( DecalInstance *inst ); + + /// @} +}; + +#endif // _DECALDATAFILE_H_ diff --git a/Engine/source/T3D/decal/decalInstance.cpp b/Engine/source/T3D/decal/decalInstance.cpp new file mode 100644 index 000000000..935361b6d --- /dev/null +++ b/Engine/source/T3D/decal/decalInstance.cpp @@ -0,0 +1,73 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/decal/decalInstance.h" +#include "scene/sceneRenderState.h" + +void DecalInstance::getWorldMatrix( MatrixF *outMat, bool flip ) +{ + outMat->setPosition( mPosition ); + + Point3F fvec; + mCross( mNormal, mTangent, &fvec ); + + outMat->setColumn( 0, mTangent ); + outMat->setColumn( 1, fvec ); + outMat->setColumn( 2, mNormal ); +} + +F32 DecalInstance::calcPixelSize( U32 viewportHeight, const Point3F &cameraPos, F32 worldToScreenScaleY ) const +{ + // If fadeStartPixelSize is set less than zero this is interpreted to mean + // pixelSize based fading is disabled and the decal always renders. + // Returning a value of F32_MAX should be treated by the caller as + // meaning "big enough to render at normal quality, dont LOD me.". + if ( mDataBlock->fadeStartPixelSize < 0.0f ) + return F32_MAX; + + // This is an approximation since mSize is actually the xyz size of the + // cube we use to clip geometry for the decal. In this case we are assuming + // it is appropriate to use the radius of the sphere 'inscribed' in that + // cube as the equivalent of mShape->radius for purposes of calculating + // the pixelScale ( see TSShapeInstance::setDetailFromDistance ). + const F32 radius = mSize * 0.5f; + + // Approximate distance to the decal. + const F32 distance = ( cameraPos - mPosition ).len() - radius; + + // We are inside the decal's volume. There is no useful pixelSize for us + // to return, it is essentially the entire screen. + if ( distance <= 0.0f ) + return F32_MAX; + + // Lod is relative to a viewport with a heigh of 300. Could prescale this into decalSize... + const F32 pixelScale = viewportHeight / 300.0f; + + // Inline of SceneState::projectRadius. + const F32 pixelSize = mSize / distance * worldToScreenScaleY * pixelScale; + + // Optionally scale pixelSize by a detail adjust value here... + // eg. pixelSize *= TSDetailAdjust... + + return pixelSize; +} diff --git a/Engine/source/T3D/decal/decalInstance.h b/Engine/source/T3D/decal/decalInstance.h new file mode 100644 index 000000000..52402e89a --- /dev/null +++ b/Engine/source/T3D/decal/decalInstance.h @@ -0,0 +1,93 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _DECALINSTANCE_H_ +#define _DECALINSTANCE_H_ + +#ifndef _GFXVERTEXBUFFER_H_ +#include "gfx/gfxVertexBuffer.h" +#endif + +#ifndef _DECALDATA_H_ +#include "T3D/decal/decalData.h" +#endif + +struct DecalVertex; +class SceneRenderState; + +/// DecalInstance represents a rendering decal in the scene. +/// You should not allocate this yourself, add new decals to the scene +/// via the DecalManager. +/// All data is public, change it if you wish, but be sure to inform +/// the DecalManager. +class DecalInstance +{ + public: + + DecalData *mDataBlock; + + Point3F mPosition; + Point3F mNormal; + Point3F mTangent; + F32 mRotAroundNormal; + F32 mSize; + + U32 mCreateTime; + F32 mVisibility; + + F32 mLastAlpha; + + U32 mTextureRectIdx; + + DecalVertex *mVerts; + U16 *mIndices; + + U32 mVertCount; + U32 mIndxCount; + + U8 mFlags; + + U8 mRenderPriority; + + S32 mId; + + GFXTexHandle *mCustomTex; + + void getWorldMatrix( MatrixF *outMat, bool flip = false ); + + Box3F getWorldBox() const + { + return Box3F( mPosition - Point3F( mSize / 2.f ), mPosition + Point3F( mSize / 2.f ) ); + } + + U8 getRenderPriority() const + { + return mRenderPriority == 0 ? mDataBlock->renderPriority : mRenderPriority; + } + + /// Calculates the size of this decal onscreen in pixels, used for LOD. + F32 calcPixelSize( U32 viewportHeight, const Point3F &cameraPos, F32 worldToScreenScaleY ) const; + + DecalInstance() : mId(-1) {} +}; + +#endif // _DECALINSTANCE_H_ diff --git a/Engine/source/T3D/decal/decalManager.cpp b/Engine/source/T3D/decal/decalManager.cpp new file mode 100644 index 000000000..9d4d3f826 --- /dev/null +++ b/Engine/source/T3D/decal/decalManager.cpp @@ -0,0 +1,1707 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/decal/decalManager.h" + +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "ts/tsShapeInstance.h" +#include "console/console.h" +#include "console/dynamicTypes.h" +#include "gfx/primBuilder.h" +#include "console/consoleTypes.h" +#include "platform/profiler.h" +#include "gfx/gfxTransformSaver.h" +#include "lighting/lightManager.h" +#include "lighting/lightInfo.h" +#include "gfx/gfxDrawUtil.h" +#include "gfx/sim/gfxStateBlockData.h" +#include "materials/shaderData.h" +#include "materials/matInstance.h" +#include "renderInstance/renderPassManager.h" +#include "core/resourceManager.h" +#include "core/stream/fileStream.h" +#include "gfx/gfxDebugEvent.h" +#include "math/util/quadTransforms.h" +#include "math/mathUtils.h" +#include "core/volume.h" +#include "core/module.h" +#include "T3D/decal/decalData.h" +#include "console/engineAPI.h" + + +extern bool gEditingMission; + + +MODULE_BEGIN( DecalManager ) + + MODULE_INIT_AFTER( Scene ) + MODULE_SHUTDOWN_BEFORE( Scene ) + + MODULE_INIT + { + gDecalManager = new DecalManager; + gClientSceneGraph->addObjectToScene( gDecalManager ); + } + + MODULE_SHUTDOWN + { + gClientSceneGraph->removeObjectFromScene( gDecalManager ); + SAFE_DELETE( gDecalManager ); + } + +MODULE_END; + + +/// A bias applied to the nearPlane for Decal and DecalRoad rendering. +/// Is set by by LevelInfo. +F32 gDecalBias = 0.0015f; + +bool DecalManager::smDecalsOn = true; +bool DecalManager::smDebugRender = false; +F32 DecalManager::smDecalLifeTimeScale = 1.0f; +bool DecalManager::smPoolBuffers = true; +const U32 DecalManager::smMaxVerts = 6000; +const U32 DecalManager::smMaxIndices = 10000; + +DecalManager *gDecalManager = NULL; + +IMPLEMENT_CONOBJECT(DecalManager); + +ConsoleDoc( + "@defgroup Decals\n" + "@brief Decals are non-SimObject derived objects that are stored and loaded " + "separately from the normal mission file.\n\n" + + "The DecalManager handles all aspects of decal management including loading, " + "creation, saving, and automatically deleting decals that have exceeded their " + "lifeSpan.\n\n" + + "The static decals associated with a mission are normally loaded immediately " + "after the mission itself has loaded as shown below.\n" + + "@tsexample\n" + "// Load the static mission decals.\n" + "decalManagerLoad( %missionName @ \".decals\" );\n" + "@endtsexample\n" + + "@ingroup FX\n" +); + +ConsoleDocClass( DecalManager, + "@brief The object that manages all of the decals in the active mission.\n\n" + "@see Decals\n" + "@ingroup Decals\n" + "@ingroup FX\n" +); + +namespace { + +int QSORT_CALLBACK cmpDecalInstance(const void* p1, const void* p2) +{ + const DecalInstance** pd1 = (const DecalInstance**)p1; + const DecalInstance** pd2 = (const DecalInstance**)p2; + + return int(((char *)(*pd1)->mDataBlock) - ((char *)(*pd2)->mDataBlock)); +} + +int QSORT_CALLBACK cmpPointsXY( const void *p1, const void *p2 ) +{ + const Point3F *pnt1 = (const Point3F*)p1; + const Point3F *pnt2 = (const Point3F*)p2; + + if ( pnt1->x < pnt2->x ) + return -1; + else if ( pnt1->x > pnt2->x ) + return 1; + else if ( pnt1->y < pnt2->y ) + return -1; + else if ( pnt1->y > pnt2->y ) + return 1; + else + return 0; +} + +int QSORT_CALLBACK cmpQuadPointTheta( const void *p1, const void *p2 ) +{ + const Point4F *pnt1 = (const Point4F*)p1; + const Point4F *pnt2 = (const Point4F*)p2; + + if ( mFabs( pnt1->w ) > mFabs( pnt2->w ) ) + return 1; + else if ( mFabs( pnt1->w ) < mFabs( pnt2->w ) ) + return -1; + else + return 0; +} + +static Point3F gSortPoint; + +int QSORT_CALLBACK cmpDecalDistance( const void *p1, const void *p2 ) +{ + const DecalInstance** pd1 = (const DecalInstance**)p1; + const DecalInstance** pd2 = (const DecalInstance**)p2; + + F32 dist1 = ( (*pd1)->mPosition - gSortPoint ).lenSquared(); + F32 dist2 = ( (*pd2)->mPosition - gSortPoint ).lenSquared(); + + return mSign( dist1 - dist2 ); +} + +int QSORT_CALLBACK cmpDecalRenderOrder( const void *p1, const void *p2 ) +{ + const DecalInstance** pd1 = (const DecalInstance**)p1; + const DecalInstance** pd2 = (const DecalInstance**)p2; + + if ( ( (*pd2)->mFlags & SaveDecal ) && !( (*pd1)->mFlags & SaveDecal ) ) + return -1; + else if ( !( (*pd2)->mFlags & SaveDecal ) && ( (*pd1)->mFlags & SaveDecal ) ) + return 1; + else + { + int priority = (*pd1)->getRenderPriority() - (*pd2)->getRenderPriority(); + + if ( priority != 0 ) + return priority; + + if ( (*pd2)->mFlags & SaveDecal ) + { + int id = ( (*pd1)->mDataBlock->getMaterial()->getId() - (*pd2)->mDataBlock->getMaterial()->getId() ); + if ( id != 0 ) + return id; + + return (*pd1)->mCreateTime - (*pd2)->mCreateTime; + } + else + return (*pd1)->mCreateTime - (*pd2)->mCreateTime; + } +} + +} // namespace {} + +// These numbers should be tweaked to get as many dynamically placed decals +// as possible to allocate buffer arrays with the FreeListChunker. +enum +{ + SIZE_CLASS_0 = 256, + SIZE_CLASS_1 = 512, + SIZE_CLASS_2 = 1024, + + NUM_SIZE_CLASSES = 3 +}; + +//------------------------------------------------------------------------- +// DecalManager +//------------------------------------------------------------------------- +DecalManager::DecalManager() +{ + #ifdef DECALMANAGER_DEBUG + VECTOR_SET_ASSOCIATION( mDebugPlanes ); + #endif + + setGlobalBounds(); + + mDataFileName = NULL; + + mTypeMask |= EnvironmentObjectType; + + mDirty = false; + + mChunkers[0] = new FreeListChunkerUntyped( SIZE_CLASS_0 * sizeof( U8 ) ); + mChunkers[1] = new FreeListChunkerUntyped( SIZE_CLASS_1 * sizeof( U8 ) ); + mChunkers[2] = new FreeListChunkerUntyped( SIZE_CLASS_2 * sizeof( U8 ) ); + + GFXDevice::getDeviceEventSignal().notify(this, &DecalManager::_handleGFXEvent); +} + +DecalManager::~DecalManager() +{ + GFXDevice::getDeviceEventSignal().remove(this, &DecalManager::_handleGFXEvent); + + clearData(); + + for( U32 i = 0; i < NUM_SIZE_CLASSES; ++ i ) + delete mChunkers[ i ]; +} + +void DecalManager::consoleInit() +{ + Con::addVariable( "$pref::Decals::enabled", TypeBool, &smDecalsOn, + "Controls whether decals are rendered.\n" + "@ingroup Decals" ); + + Con::addVariable( "$pref::Decals::lifeTimeScale", TypeF32, &smDecalLifeTimeScale, + "@brief Lifetime that decals will last after being created in the world.\n" + "Deprecated. Use DecalData::lifeSpan instead.\n" + "@ingroup Decals" ); + + Con::addVariable( "$Decals::poolBuffers", TypeBool, &smPoolBuffers, + "If true, will merge all PrimitiveBuffers and VertexBuffers into a pair " + "of pools before clearing them at the end of a frame.\n" + "If false, will just clear them at the end of a frame.\n" + "@ingroup Decals" ); + + Con::addVariable( "$Decals::debugRender", TypeBool, &smDebugRender, + "If true, the decal spheres will be visualized when in the editor.\n\n" + "@ingroup Decals" ); + + Con::addVariable( "$Decals::sphereDistanceTolerance", TypeF32, &DecalSphere::smDistanceTolerance, + "The distance at which the decal system will start breaking up decal " + "spheres when adding new decals.\n\n" + "@ingroup Decals" ); + + Con::addVariable( "$Decals::sphereRadiusTolerance", TypeF32, &DecalSphere::smRadiusTolerance, + "The radius beyond which the decal system will start breaking up decal " + "spheres when adding new decals.\n\n" + "@ingroup Decals" ); +} + +bool DecalManager::_handleGFXEvent(GFXDevice::GFXDeviceEventType event) +{ + switch(event) + { + case GFXDevice::deEndOfFrame: + + // Return PrimitiveBuffers and VertexBuffers used this frame to the pool. + + if ( smPoolBuffers ) + { + mPBPool.merge( mPBs ); + mPBs.clear(); + + mVBPool.merge( mVBs ); + mVBs.clear(); + } + else + { + _freePools(); + } + + break; + + default: ; + } + + return true; +} + +bool DecalManager::clipDecal( DecalInstance *decal, Vector *edgeVerts, const Point2F *clipDepth ) +{ + PROFILE_SCOPE( DecalManager_clipDecal ); + + // Free old verts and indices. + _freeBuffers( decal ); + + F32 halfSize = decal->mSize * 0.5f; + + // Ugly hack for ProjectedShadow! + F32 halfSizeZ = clipDepth ? clipDepth->x : halfSize; + F32 negHalfSize = clipDepth ? clipDepth->y : halfSize; + Point3F decalHalfSize( halfSize, halfSize, halfSize ); + Point3F decalHalfSizeZ( halfSizeZ, halfSizeZ, halfSizeZ ); + + MatrixF projMat( true ); + decal->getWorldMatrix( &projMat ); + + const VectorF &crossVec = decal->mNormal; + const Point3F &decalPos = decal->mPosition; + + VectorF newFwd, newRight; + projMat.getColumn( 0, &newRight ); + projMat.getColumn( 1, &newFwd ); + + VectorF objRight( 1.0f, 0, 0 ); + VectorF objFwd( 0, 1.0f, 0 ); + VectorF objUp( 0, 0, 1.0f ); + + // See above re: decalHalfSizeZ hack. + mClipper.clear(); + mClipper.mPlaneList.setSize(6); + mClipper.mPlaneList[0].set( ( decalPos + ( -newRight * halfSize ) ), -newRight ); + mClipper.mPlaneList[1].set( ( decalPos + ( -newFwd * halfSize ) ), -newFwd ); + mClipper.mPlaneList[2].set( ( decalPos + ( -crossVec * decalHalfSizeZ ) ), -crossVec ); + mClipper.mPlaneList[3].set( ( decalPos + ( newRight * halfSize ) ), newRight ); + mClipper.mPlaneList[4].set( ( decalPos + ( newFwd * halfSize ) ), newFwd ); + mClipper.mPlaneList[5].set( ( decalPos + ( crossVec * negHalfSize ) ), crossVec ); + + mClipper.mNormal = decal->mNormal; + + const DecalData *decalData = decal->mDataBlock; + + mClipper.mNormalTolCosineRadians = mCos( mDegToRad( decalData->clippingAngle ) ); + + Box3F box( -decalHalfSizeZ, decalHalfSizeZ ); + + projMat.mul( box ); + + PROFILE_START( DecalManager_clipDecal_buildPolyList ); + getContainer()->buildPolyList( PLC_Decal, box, decalData->clippingMasks, &mClipper ); + PROFILE_END(); + + mClipper.cullUnusedVerts(); + mClipper.triangulate(); + + if ( !decalData->skipVertexNormals ) + mClipper.generateNormals(); + + if ( mClipper.mVertexList.empty() ) + return false; + +#ifdef DECALMANAGER_DEBUG + mDebugPlanes.clear(); + mDebugPlanes.merge( mClipper.mPlaneList ); +#endif + + decal->mVertCount = mClipper.mVertexList.size(); + decal->mIndxCount = mClipper.mIndexList.size(); + + Vector tmpPoints; + + tmpPoints.push_back(( objFwd * decalHalfSize ) + ( objRight * decalHalfSize )); + tmpPoints.push_back(( objFwd * decalHalfSize ) + ( -objRight * decalHalfSize )); + tmpPoints.push_back(( -objFwd * decalHalfSize ) + ( -objRight * decalHalfSize )); + + Point3F lowerLeft(( -objFwd * decalHalfSize ) + ( objRight * decalHalfSize )); + + projMat.inverse(); + + _generateWindingOrder( lowerLeft, &tmpPoints ); + + BiQuadToSqr quadToSquare( Point2F( lowerLeft.x, lowerLeft.y ), + Point2F( tmpPoints[0].x, tmpPoints[0].y ), + Point2F( tmpPoints[1].x, tmpPoints[1].y ), + Point2F( tmpPoints[2].x, tmpPoints[2].y ) ); + + Point2F uv( 0, 0 ); + Point3F vecX(0.0f, 0.0f, 0.0f); + + // Allocate memory for vert and index arrays + _allocBuffers( decal ); + + // Mark this so that the color will be assigned on these verts the next + // time it renders, since we just threw away the previous verts. + decal->mLastAlpha = -1; + + Point3F vertPoint( 0, 0, 0 ); + + for ( U32 i = 0; i < mClipper.mVertexList.size(); i++ ) + { + const ClippedPolyList::Vertex &vert = mClipper.mVertexList[i]; + vertPoint = vert.point; + + // Transform this point to + // object space to look up the + // UV coordinate for this vertex. + projMat.mulP( vertPoint ); + + // Clamp the point to be within the quad. + vertPoint.x = mClampF( vertPoint.x, -decalHalfSize.x, decalHalfSize.x ); + vertPoint.y = mClampF( vertPoint.y, -decalHalfSize.y, decalHalfSize.y ); + + // Get our UV. + uv = quadToSquare.transform( Point2F( vertPoint.x, vertPoint.y ) ); + + const RectF &rect = decal->mDataBlock->texRect[decal->mTextureRectIdx]; + + uv *= rect.extent; + uv += rect.point; + + // Set the world space vertex position. + decal->mVerts[i].point = vert.point; + + decal->mVerts[i].texCoord.set( uv.x, uv.y ); + + if ( mClipper.mNormalList.empty() ) + continue; + + decal->mVerts[i].normal = mClipper.mNormalList[i]; + decal->mVerts[i].normal.normalize(); + + if( mFabs( decal->mVerts[i].normal.z ) > 0.8f ) + mCross( decal->mVerts[i].normal, Point3F( 1.0f, 0.0f, 0.0f ), &vecX ); + else if ( mFabs( decal->mVerts[i].normal.x ) > 0.8f ) + mCross( decal->mVerts[i].normal, Point3F( 0.0f, 1.0f, 0.0f ), &vecX ); + else if ( mFabs( decal->mVerts[i].normal.y ) > 0.8f ) + mCross( decal->mVerts[i].normal, Point3F( 0.0f, 0.0f, 1.0f ), &vecX ); + + decal->mVerts[i].tangent = mCross( decal->mVerts[i].normal, vecX ); + } + + U32 curIdx = 0; + for ( U32 j = 0; j < mClipper.mPolyList.size(); j++ ) + { + // Write indices for each Poly + ClippedPolyList::Poly *poly = &mClipper.mPolyList[j]; + + AssertFatal( poly->vertexCount == 3, "Got non-triangle poly!" ); + + decal->mIndices[curIdx] = mClipper.mIndexList[poly->vertexStart]; + curIdx++; + decal->mIndices[curIdx] = mClipper.mIndexList[poly->vertexStart + 1]; + curIdx++; + decal->mIndices[curIdx] = mClipper.mIndexList[poly->vertexStart + 2]; + curIdx++; + } + + if ( !edgeVerts ) + return true; + + Point3F tmpHullPt( 0, 0, 0 ); + Vector tmpHullPts; + + for ( U32 i = 0; i < mClipper.mVertexList.size(); i++ ) + { + const ClippedPolyList::Vertex &vert = mClipper.mVertexList[i]; + tmpHullPt = vert.point; + projMat.mulP( tmpHullPt ); + tmpHullPts.push_back( tmpHullPt ); + } + + edgeVerts->clear(); + U32 verts = _generateConvexHull( tmpHullPts, edgeVerts ); + edgeVerts->setSize( verts ); + + projMat.inverse(); + for ( U32 i = 0; i < edgeVerts->size(); i++ ) + projMat.mulP( (*edgeVerts)[i] ); + + return true; +} + +DecalInstance* DecalManager::addDecal( const Point3F &pos, + const Point3F &normal, + F32 rotAroundNormal, + DecalData *decalData, + F32 decalScale, + S32 decalTexIndex, + U8 flags ) +{ + MatrixF mat( true ); + MathUtils::getMatrixFromUpVector( normal, &mat ); + + AngAxisF rot( normal, rotAroundNormal ); + MatrixF rotmat; + rot.setMatrix( &rotmat ); + mat.mul( rotmat ); + + Point3F tangent; + mat.getColumn( 1, &tangent ); + + return addDecal( pos, normal, tangent, decalData, decalScale, decalTexIndex, flags ); +} + +DecalInstance* DecalManager::addDecal( const Point3F& pos, + const Point3F& normal, + const Point3F& tangent, + DecalData* decalData, + F32 decalScale, + S32 decalTexIndex, + U8 flags ) +{ + if ( !mData && !_createDataFile() ) + return NULL; + + // only dirty the manager if this decal should be saved + if ( flags & SaveDecal ) + mDirty = true; + + return mData->addDecal( pos, normal, tangent, decalData, decalScale, decalTexIndex, flags ); +} + +void DecalManager::removeDecal( DecalInstance *inst ) +{ + // If this is a decal we save then we need + // to set the dirty flag. + if ( inst->mFlags & SaveDecal ) + mDirty = true; + + // Remove the decal from the instance vector. + + if( inst->mId != -1 && inst->mId < mDecalInstanceVec.size() ) + mDecalInstanceVec[ inst->mId ] = NULL; + + // Release its geometry (if it has any). + + _freeBuffers( inst ); + + // Remove it from the decal file. + + if ( mData ) + mData->removeDecal( inst ); +} + +DecalInstance* DecalManager::getDecal( S32 id ) +{ + if( id < 0 || id >= mDecalInstanceVec.size() ) + return NULL; + + return mDecalInstanceVec[id]; +} + +void DecalManager::notifyDecalModified( DecalInstance *inst ) +{ + // If this is a decal we save then we need + // to set the dirty flag. + if ( inst->mFlags & SaveDecal ) + mDirty = true; + + if ( mData ) + mData->notifyDecalModified( inst ); +} + +DecalInstance* DecalManager::getClosestDecal( const Point3F &pos ) +{ + if ( !mData ) + return NULL; + + const Vector &grid = mData->getSphereList(); + + DecalInstance *inst = NULL; + SphereF worldPickSphere( pos, 0.5f ); + SphereF worldInstSphere( Point3F( 0, 0, 0 ), 1.0f ); + + Vector collectedInsts; + + for ( U32 i = 0; i < grid.size(); i++ ) + { + DecalSphere *decalSphere = grid[i]; + const SphereF &worldSphere = decalSphere->mWorldSphere; + if ( !worldSphere.isIntersecting( worldPickSphere ) && + !worldSphere.isContained( pos ) ) + continue; + + const Vector &items = decalSphere->mItems; + for ( U32 n = 0; n < items.size(); n++ ) + { + inst = items[n]; + if ( !inst ) + continue; + + worldInstSphere.center = inst->mPosition; + worldInstSphere.radius = inst->mSize; + + if ( !worldInstSphere.isContained( inst->mPosition ) ) + continue; + + collectedInsts.push_back( inst ); + } + } + + F32 closestDistance = F32_MAX; + F32 currentDist = 0; + U32 closestIndex = 0; + for ( U32 i = 0; i < collectedInsts.size(); i++ ) + { + inst = collectedInsts[i]; + currentDist = (inst->mPosition - pos).len(); + if ( currentDist < closestDistance ) + { + closestIndex = i; + closestDistance = currentDist; + worldInstSphere.center = inst->mPosition; + worldInstSphere.radius = inst->mSize; + } + } + + if ( !collectedInsts.empty() && + collectedInsts[closestIndex] && + closestDistance < 1.0f || + worldInstSphere.isContained( pos ) ) + return collectedInsts[closestIndex]; + else + return NULL; +} + +DecalInstance* DecalManager::raycast( const Point3F &start, const Point3F &end, bool savedDecalsOnly ) +{ + if ( !mData ) + return NULL; + + const Vector &grid = mData->getSphereList(); + + DecalInstance *inst = NULL; + SphereF worldSphere( Point3F( 0, 0, 0 ), 1.0f ); + + Vector hitDecals; + + for ( U32 i = 0; i < grid.size(); i++ ) + { + DecalSphere *decalSphere = grid[i]; + if ( !decalSphere->mWorldSphere.intersectsRay( start, end ) ) + continue; + + const Vector &items = decalSphere->mItems; + for ( U32 n = 0; n < items.size(); n++ ) + { + inst = items[n]; + if ( !inst ) + continue; + + if ( savedDecalsOnly && !(inst->mFlags & SaveDecal) ) + continue; + + worldSphere.center = inst->mPosition; + worldSphere.radius = inst->mSize; + + if ( !worldSphere.intersectsRay( start, end ) ) + continue; + + RayInfo ri; + bool containsPoint = false; + if ( gServerContainer.castRayRendered( start, end, STATIC_COLLISION_TYPEMASK, &ri ) ) + { + Point2F poly[4]; + poly[0].set( inst->mPosition.x - (inst->mSize / 2), inst->mPosition.y + (inst->mSize / 2)); + poly[1].set( inst->mPosition.x - (inst->mSize / 2), inst->mPosition.y - (inst->mSize / 2)); + poly[2].set( inst->mPosition.x + (inst->mSize / 2), inst->mPosition.y - (inst->mSize / 2)); + poly[3].set( inst->mPosition.x + (inst->mSize / 2), inst->mPosition.y + (inst->mSize / 2)); + + if ( MathUtils::pointInPolygon( poly, 4, Point2F(ri.point.x, ri.point.y) ) ) + containsPoint = true; + } + + if( !containsPoint ) + continue; + + hitDecals.push_back( inst ); + } + } + + if ( hitDecals.empty() ) + return NULL; + + gSortPoint = start; + dQsort( hitDecals.address(), hitDecals.size(), sizeof(DecalInstance*), cmpDecalDistance ); + return hitDecals[0]; +} + +U32 DecalManager::_generateConvexHull( const Vector &points, Vector *outPoints ) +{ + PROFILE_SCOPE( DecalManager_generateConvexHull ); + + // chainHull_2D(): Andrew's monotone chain 2D convex hull algorithm + // Input: P[] = an array of 2D points + // presorted by increasing x- and y-coordinates + // n = the number of points in P[] + // Output: H[] = an array of the convex hull vertices (max is n) + // Return: the number of points in H[] + //int + + if ( points.size() < 3 ) + { + outPoints->merge( points ); + return outPoints->size(); + } + + // Sort our input points. + dQsort( points.address(), points.size(), sizeof( Point3F ), cmpPointsXY ); + + U32 n = points.size(); + + Vector tmpPoints; + tmpPoints.setSize( n ); + + // the output array H[] will be used as the stack + S32 bot=0, top=(-1); // indices for bottom and top of the stack + S32 i; // array scan index + S32 toptmp = 0; + + // Get the indices of points with min x-coord and min|max y-coord + S32 minmin = 0, minmax; + F32 xmin = points[0].x; + for ( i = 1; i < n; i++ ) + if (points[i].x != xmin) + break; + + minmax = i - 1; + if ( minmax == n - 1 ) + { + // degenerate case: all x-coords == xmin + toptmp = top + 1; + if ( toptmp < n ) + tmpPoints[++top] = points[minmin]; + + if ( points[minmax].y != points[minmin].y ) // a nontrivial segment + { + toptmp = top + 1; + if ( toptmp < n ) + tmpPoints[++top] = points[minmax]; + } + + toptmp = top + 1; + if ( toptmp < n ) + tmpPoints[++top] = points[minmin]; // add polygon endpoint + + return top+1; + } + + // Get the indices of points with max x-coord and min|max y-coord + S32 maxmin, maxmax = n-1; + F32 xmax = points[n-1].x; + + for ( i = n - 2; i >= 0; i-- ) + if ( points[i].x != xmax ) + break; + + maxmin = i + 1; + + // Compute the lower hull on the stack H + toptmp = top + 1; + if ( toptmp < n ) + tmpPoints[++top] = points[minmin]; // push minmin point onto stack + + i = minmax; + while ( ++i <= maxmin ) + { + // the lower line joins P[minmin] with P[maxmin] + if ( isLeft( points[minmin], points[maxmin], points[i]) >= 0 && i < maxmin ) + continue; // ignore P[i] above or on the lower line + + while (top > 0) // there are at least 2 points on the stack + { + // test if P[i] is left of the line at the stack top + if ( isLeft( tmpPoints[top-1], tmpPoints[top], points[i]) > 0) + break; // P[i] is a new hull vertex + else + top--; // pop top point off stack + } + + toptmp = top + 1; + if ( toptmp < n ) + tmpPoints[++top] = points[i]; // push P[i] onto stack + } + + // Next, compute the upper hull on the stack H above the bottom hull + if (maxmax != maxmin) // if distinct xmax points + { + toptmp = top + 1; + if ( toptmp < n ) + tmpPoints[++top] = points[maxmax]; // push maxmax point onto stack + } + + bot = top; // the bottom point of the upper hull stack + i = maxmin; + while (--i >= minmax) + { + // the upper line joins P[maxmax] with P[minmax] + if ( isLeft( points[maxmax], points[minmax], points[i] ) >= 0 && i > minmax ) + continue; // ignore P[i] below or on the upper line + + while ( top > bot ) // at least 2 points on the upper stack + { + // test if P[i] is left of the line at the stack top + if ( isLeft( tmpPoints[top-1], tmpPoints[top], points[i] ) > 0 ) + break; // P[i] is a new hull vertex + else + top--; // pop top point off stack + } + + toptmp = top + 1; + if ( toptmp < n ) + tmpPoints[++top] = points[i]; // push P[i] onto stack + } + + if (minmax != minmin) + { + toptmp = top + 1; + if ( toptmp < n ) + tmpPoints[++top] = points[minmin]; // push joining endpoint onto stack + } + + outPoints->merge( tmpPoints ); + + return top + 1; +} + +void DecalManager::_generateWindingOrder( const Point3F &cornerPoint, Vector *sortPoints ) +{ + // This block of code is used to find + // the winding order for the points in our quad. + + // First, choose an arbitrary corner point. + // We'll use the "lowerRight" point. + Point3F relPoint( 0, 0, 0 ); + + // See comment below about radius. + //F32 radius = 0; + + F32 theta = 0; + + Vector tmpPoints; + + for ( U32 i = 0; i < (*sortPoints).size(); i++ ) + { + const Point3F &pnt = (*sortPoints)[i]; + relPoint = cornerPoint - pnt; + + // Get the radius (r^2 = x^2 + y^2). + + // This is commented because for a quad + // you typically can't have the same values + // for theta, which is the caveat which would + // require sorting by the radius. + //radius = mSqrt( (relPoint.x * relPoint.x) + (relPoint.y * relPoint.y) ); + + // Get the theta value for the + // interval -PI, PI. + + // This algorithm for determining the + // theta value is defined by + // | arctan( y / x ) if x > 0 + // | arctan( y / x ) if x < 0 and y >= 0 + // theta = | arctan( y / x ) if x < 0 and y < 0 + // | PI / 2 if x = 0 and y > 0 + // | -( PI / 2 ) if x = 0 and y < 0 + if ( relPoint.x > 0.0f ) + theta = mAtan2( relPoint.y, relPoint.x ); + else if ( relPoint.x < 0.0f ) + { + if ( relPoint.y >= 0.0f ) + theta = mAtan2( relPoint.y, relPoint.x ) + M_PI_F; + else if ( relPoint.y < 0.0f ) + theta = mAtan2( relPoint.y, relPoint.x ) - M_PI_F; + } + else if ( relPoint.x == 0.0f ) + { + if ( relPoint.y > 0.0f ) + theta = M_PI_F / 2.0f; + else if ( relPoint.y < 0.0f ) + theta = -(M_PI_F / 2.0f); + } + + tmpPoints.push_back( Point4F( pnt.x, pnt.y, pnt.z, theta ) ); + } + + dQsort( tmpPoints.address(), tmpPoints.size(), sizeof( Point4F ), cmpQuadPointTheta ); + + for ( U32 i = 0; i < tmpPoints.size(); i++ ) + { + const Point4F &tmpPoint = tmpPoints[i]; + (*sortPoints)[i].set( tmpPoint.x, tmpPoint.y, tmpPoint.z ); + } +} + +void DecalManager::_allocBuffers( DecalInstance *inst ) +{ + const S32 sizeClass = _getSizeClass( inst ); + + void* data; + if ( sizeClass == -1 ) + data = dMalloc( sizeof( DecalVertex ) * inst->mVertCount + sizeof( U16 ) * inst->mIndxCount ); + else + data = mChunkers[sizeClass]->alloc(); + + inst->mVerts = reinterpret_cast< DecalVertex* >( data ); + data = (U8*)data + sizeof( DecalVertex ) * inst->mVertCount; + inst->mIndices = reinterpret_cast< U16* >( data ); +} + +void DecalManager::_freeBuffers( DecalInstance *inst ) +{ + if ( inst->mVerts != NULL ) + { + const S32 sizeClass = _getSizeClass( inst ); + + if ( sizeClass == -1 ) + dFree( inst->mVerts ); + else + { + // Use FreeListChunker + mChunkers[sizeClass]->free( inst->mVerts ); + } + + inst->mVerts = NULL; + inst->mVertCount = 0; + inst->mIndices = NULL; + inst->mIndxCount = 0; + } +} + +void DecalManager::_freePools() +{ + while ( !mVBPool.empty() ) + { + delete mVBPool.last(); + mVBPool.pop_back(); + } + + while ( !mVBs.empty() ) + { + delete mVBs.last(); + mVBs.pop_back(); + } + + while ( !mPBPool.empty() ) + { + delete mPBPool.last(); + mPBPool.pop_back(); + } + + while ( !mPBs.empty() ) + { + delete mPBs.last(); + mPBs.pop_back(); + } +} + +S32 DecalManager::_getSizeClass( DecalInstance *inst ) const +{ + U32 bytes = inst->mVertCount * sizeof( DecalVertex ) + inst->mIndxCount * sizeof ( U16 ); + + if ( bytes <= SIZE_CLASS_0 ) + return 0; + if ( bytes <= SIZE_CLASS_1 ) + return 1; + if ( bytes <= SIZE_CLASS_2 ) + return 2; + + // Size is outside of the largest chunker. + return -1; +} + +void DecalManager::prepRenderImage( SceneRenderState* state ) +{ + PROFILE_SCOPE( DecalManager_RenderDecals ); + + if ( !smDecalsOn || !mData ) + return; + + // Decals only render in the diffuse pass! + // We technically could render them into reflections but prefer to save + // the performance. This would also break the DecalInstance::mLastAlpha + // optimization. + if ( !state->isDiffusePass() ) + return; + + PROFILE_START( DecalManager_RenderDecals_SphereTreeCull ); + + const Frustum& rootFrustum = state->getFrustum(); + + // Populate vector of decal instances to be rendered with all + // decals from visible decal spheres. + + SceneManager* sceneManager = state->getSceneManager(); + SceneZoneSpaceManager* zoneManager = sceneManager->getZoneManager(); + AssertFatal( zoneManager, "DecalManager::prepRenderImage - No zone manager!" ); + const Vector &grid = mData->getSphereList(); + const bool haveOnlyOutdoorZone = ( zoneManager->getNumActiveZones() == 1 ); + + mDecalQueue.clear(); + for ( U32 i = 0; i < grid.size(); i++ ) + { + DecalSphere* decalSphere = grid[i]; + const SphereF& worldSphere = decalSphere->mWorldSphere; + + // See if this decal sphere can be culled. + + const SceneCullingState& cullingState = state->getCullingState(); + if( haveOnlyOutdoorZone ) + { + U32 outdoorZone = SceneZoneSpaceManager::RootZoneId; + if( cullingState.isCulled( worldSphere, &outdoorZone, 1 ) ) + continue; + } + else + { + // Update the zoning state of the sphere, if we need to. + + if( decalSphere->mZones.size() == 0 ) + decalSphere->updateZoning( zoneManager ); + + // Skip the sphere if it is not visible in any of its zones. + + if( cullingState.isCulled( worldSphere, decalSphere->mZones.address(), decalSphere->mZones.size() ) ) + continue; + } + + // TODO: If each sphere stored its largest decal instance we + // could do an LOD step on it here and skip adding any of the + // decals in the sphere. + + mDecalQueue.merge( decalSphere->mItems ); + } + + PROFILE_END(); + + PROFILE_START( DecalManager_RenderDecals_Update ); + + const U32 &curSimTime = Sim::getCurrentTime(); + F32 pixelSize; + U32 delta, diff; + DecalInstance *dinst; + DecalData *ddata; + + // Loop through DecalQueue once for preRendering work. + // 1. Update DecalInstance fade (over time) + // 2. Clip geometry if flagged to do so. + // 3. Calculate lod - if decal is far enough away it will not render. + for ( U32 i = 0; i < mDecalQueue.size(); i++ ) + { + dinst = mDecalQueue[i]; + ddata = dinst->mDataBlock; + + // LOD calculation... + + pixelSize = dinst->calcPixelSize( state->getViewport().extent.y, state->getCameraPosition(), state->getWorldToScreenScale().y ); + + if ( pixelSize != F32_MAX && pixelSize < ddata->fadeEndPixelSize ) + { + mDecalQueue.erase_fast( i ); + i--; + continue; + } + + // We will try to render this decal... so do any + // final adjustments to it before rendering. + + // Update fade and delete expired. + if ( !( dinst->mFlags & PermanentDecal || dinst->mFlags & CustomDecal ) ) + { + delta = ( curSimTime - dinst->mCreateTime ); + if ( delta > dinst->mDataBlock->lifeSpan ) + { + diff = delta - dinst->mDataBlock->lifeSpan; + dinst->mVisibility = 1.0f - (F32)diff / (F32)dinst->mDataBlock->fadeTime; + + if ( dinst->mVisibility <= 0.0f ) + { + mDecalQueue.erase_fast( i ); + removeDecal( dinst ); + i--; + continue; + } + } + } + + // Build clipped geometry for this decal if needed. + if ( dinst->mFlags & ClipDecal && !( dinst->mFlags & CustomDecal ) ) + { + // Turn off the flag so we don't continually try to clip + // if it fails. + dinst->mFlags = dinst->mFlags & ~ClipDecal; + + if ( !(dinst->mFlags & CustomDecal) && !clipDecal( dinst ) ) + { + // Clipping failed to get any geometry... + + // Remove it from the render queue. + mDecalQueue.erase_fast( i ); + i--; + + // If the decal is one placed at run-time (not the editor) + // then we should also permanently delete the decal instance. + if ( !(dinst->mFlags & SaveDecal) ) + { + removeDecal( dinst ); + } + + // If this is a decal placed by the editor it will be + // flagged to attempt clipping again the next time it is + // modified. For now we just skip rendering it. + continue; + } + } + + // If we get here and the decal still does not have any geometry + // skip rendering it. It must be an editor placed decal that failed + // to clip any geometry but has not yet been flagged to try again. + if ( !dinst->mVerts || dinst->mVertCount == 0 || dinst->mIndxCount == 0 ) + { + mDecalQueue.erase_fast( i ); + i--; + continue; + } + + // Calculate the alpha value for this decal and apply it to the verts. + { + PROFILE_START( DecalManager_RenderDecals_Update_SetAlpha ); + + F32 alpha = 1.0f; + + // Only necessary for decals which fade over time or distance. + if ( !( dinst->mFlags & PermanentDecal ) || dinst->mDataBlock->fadeStartPixelSize >= 0.0f ) + { + if ( pixelSize < ddata->fadeStartPixelSize ) + { + const F32 range = ddata->fadeStartPixelSize - ddata->fadeEndPixelSize; + alpha = 1.0f - mClampF( ( ddata->fadeStartPixelSize - pixelSize ) / range, 0.0f, 1.0f ); + } + + alpha *= dinst->mVisibility; + } + + // If the alpha value has not changed since last render avoid + // looping through all the verts! + if ( alpha != dinst->mLastAlpha ) + { + // calculate the swizzles color once, outside the loop. + GFXVertexColor color; + color.set( 255, 255, 255, (U8)(alpha * 255.0f) ); + + for ( U32 v = 0; v < dinst->mVertCount; v++ ) + dinst->mVerts[v].color = color; + + dinst->mLastAlpha = alpha; + } + + PROFILE_END(); + } + } + + PROFILE_END(); + + if ( mDecalQueue.empty() ) + return; + + // Sort queued decals... + // 1. Editor decals - in render priority order first, creation time second, and material third. + // 2. Dynamic decals - in render priority order first and creation time second. + // + // With the constraint that decals with different render priority cannot + // be rendered together in the same draw call. + + PROFILE_START( DecalManager_RenderDecals_Sort ); + dQsort( mDecalQueue.address(), mDecalQueue.size(), sizeof(DecalInstance*), cmpDecalRenderOrder ); + PROFILE_END(); + + PROFILE_SCOPE( DecalManager_RenderDecals_RenderBatch ); + + RenderPassManager *renderPass = state->getRenderPass(); + + // Base render instance we use for convenience. + // Data shared by all instances we allocate below can be copied + // from the base instance at the same time. + MeshRenderInst baseRenderInst; + baseRenderInst.clear(); + + MatrixF *tempMat = renderPass->allocUniqueXform( MatrixF( true ) ); + MathUtils::getZBiasProjectionMatrix( gDecalBias, rootFrustum, tempMat ); + baseRenderInst.projection = tempMat; + + baseRenderInst.objectToWorld = &MatrixF::Identity; + baseRenderInst.worldToCamera = renderPass->allocSharedXform(RenderPassManager::View); + + baseRenderInst.type = RenderPassManager::RIT_Decal; + + // Make it the sort distance the max distance so that + // it renders after all the other opaque geometry in + // the prepass bin. + baseRenderInst.sortDistSq = F32_MAX; + + Vector batches; + DecalBatch *currentBatch = NULL; + + // Loop through DecalQueue collecting them into render batches. + for ( U32 i = 0; i < mDecalQueue.size(); i++ ) + { + DecalInstance *decal = mDecalQueue[i]; + DecalData *data = decal->mDataBlock; + Material *mat = data->getMaterial(); + + if ( currentBatch == NULL ) + { + // Start a new batch, beginning with this decal. + + batches.increment(); + currentBatch = &batches.last(); + currentBatch->startDecal = i; + currentBatch->decalCount = 1; + currentBatch->iCount = decal->mIndxCount; + currentBatch->vCount = decal->mVertCount; + currentBatch->mat = mat; + currentBatch->matInst = decal->mDataBlock->getMaterialInstance(); + currentBatch->priority = decal->getRenderPriority(); + currentBatch->dynamic = !(decal->mFlags & SaveDecal); + + continue; + } + + if ( currentBatch->iCount + decal->mIndxCount >= smMaxIndices || + currentBatch->vCount + decal->mVertCount >= smMaxVerts || + currentBatch->mat != mat || + currentBatch->priority != decal->getRenderPriority() || + decal->mCustomTex ) + { + // End batch. + + currentBatch = NULL; + i--; + continue; + } + + // Add on to current batch. + currentBatch->decalCount++; + currentBatch->iCount += decal->mIndxCount; + currentBatch->vCount += decal->mVertCount; + } + + // Make sure our primitive and vertex buffer handle storage is + // big enough to take all batches. Doing this now avoids reallocation + // later on which would invalidate all the pointers we already had + // passed into render instances. + + //mPBs.reserve( batches.size() ); + //mVBs.reserve( batches.size() ); + + // System memory array of verts and indices so we can fill them incrementally + // and then memcpy to the graphics device buffers in one call. + static DecalVertex vertData[smMaxVerts]; + static U16 indexData[smMaxIndices]; + + // Loop through batches allocating buffers and submitting render instances. + for ( U32 i = 0; i < batches.size(); i++ ) + { + DecalBatch ¤tBatch = batches[i]; + + // Copy data into the system memory arrays, from all decals in this batch... + + DecalVertex *vpPtr = vertData; + U16 *pbPtr = indexData; + + U32 lastDecal = currentBatch.startDecal + currentBatch.decalCount; + + U32 voffset = 0; + U32 ioffset = 0; + + // This is an ugly hack for ProjectedShadow! + GFXTextureObject *customTex = NULL; + + for ( U32 j = currentBatch.startDecal; j < lastDecal; j++ ) + { + DecalInstance *dinst = mDecalQueue[j]; + + for ( U32 k = 0; k < dinst->mIndxCount; k++ ) + { + *( pbPtr + ioffset + k ) = dinst->mIndices[k] + voffset; + } + + ioffset += dinst->mIndxCount; + + dMemcpy( vpPtr + voffset, dinst->mVerts, sizeof( DecalVertex ) * dinst->mVertCount ); + voffset += dinst->mVertCount; + + // Ugly hack for ProjectedShadow! + if ( (dinst->mFlags & CustomDecal) && dinst->mCustomTex != NULL ) + customTex = *dinst->mCustomTex; + } + + AssertFatal( ioffset == currentBatch.iCount, "bad" ); + AssertFatal( voffset == currentBatch.vCount, "bad" ); + + // Get handles to video memory buffers we will be filling... + + GFXVertexBufferHandle *vb = NULL; + + if ( mVBPool.empty() ) + { + // If the Pool is empty allocate a new one. + vb = new GFXVertexBufferHandle; + vb->set( GFX, smMaxVerts, GFXBufferTypeDynamic ); + } + else + { + // Otherwise grab from the pool. + vb = mVBPool.last(); + mVBPool.pop_back(); + } + + // Push into our vector of 'in use' buffers. + mVBs.push_back( vb ); + + // Ready to start filling. + vpPtr = vb->lock(); + + // Same deal as above... + GFXPrimitiveBufferHandle *pb = NULL; + if ( mPBPool.empty() ) + { + pb = new GFXPrimitiveBufferHandle; + pb->set( GFX, smMaxIndices, 0, GFXBufferTypeDynamic ); + } + else + { + pb = mPBPool.last(); + mPBPool.pop_back(); + } + mPBs.push_back( pb ); + + pb->lock( &pbPtr ); + + // Memcpy from system to video memory. + dMemcpy( vpPtr, vertData, sizeof( DecalVertex ) * currentBatch.vCount ); + dMemcpy( pbPtr, indexData, sizeof( U16 ) * currentBatch.iCount ); + + pb->unlock(); + vb->unlock(); + + // DecalManager must hold handles to these buffers so they remain valid, + // we don't actually use them elsewhere. + //mPBs.push_back( pb ); + //mVBs.push_back( vb ); + + // Get the best lights for the current camera position + // if the materail is forward lit and we haven't got them yet. + if ( currentBatch.matInst->isForwardLit() && !baseRenderInst.lights[0] ) + { + LightQuery query; + query.init( rootFrustum.getPosition(), + rootFrustum.getTransform().getForwardVector(), + rootFrustum.getFarDist() ); + query.getLights( baseRenderInst.lights, 8 ); + } + + // Submit render inst... + MeshRenderInst *ri = renderPass->allocInst(); + *ri = baseRenderInst; + + ri->primBuff = pb; + ri->vertBuff = vb; + + ri->matInst = currentBatch.matInst; + + ri->prim = renderPass->allocPrim(); + ri->prim->type = GFXTriangleList; + ri->prim->minIndex = 0; + ri->prim->startIndex = 0; + ri->prim->numPrimitives = currentBatch.iCount / 3; + ri->prim->startVertex = 0; + ri->prim->numVertices = currentBatch.vCount; + + // Ugly hack for ProjectedShadow! + if ( customTex ) + ri->miscTex = customTex; + + // The decal bin will contain render instances for both decals and decalRoad's. + // Dynamic decals render last, then editor decals and roads in priority order. + // DefaultKey is sorted in descending order. + ri->defaultKey = currentBatch.dynamic ? 0xFFFFFFFF : (U32)currentBatch.priority; + ri->defaultKey2 = 1;//(U32)lastDecal->mDataBlock; + + renderPass->addInst( ri ); + } + +#ifdef TORQUE_GATHER_METRICS + Con::setIntVariable( "$Decal::Batches", batches.size() ); + Con::setIntVariable( "$Decal::Buffers", mPBs.size() + mPBPool.size() ); + Con::setIntVariable( "$Decal::DecalsRendered", mDecalQueue.size() ); +#endif + + if( smDebugRender && gEditingMission ) + { + ObjectRenderInst* ri = state->getRenderPass()->allocInst< ObjectRenderInst >(); + + ri->renderDelegate.bind( this, &DecalManager::_renderDecalSpheres ); + ri->type = RenderPassManager::RIT_Editor; + ri->defaultKey = 0; + ri->defaultKey2 = 0; + + state->getRenderPass()->addInst( ri ); + } +} + +void DecalManager::_renderDecalSpheres( ObjectRenderInst* ri, SceneRenderState* state, BaseMatInstance* overrideMat ) +{ + if( !mData ) + return; + + const Vector &grid = mData->getSphereList(); + + GFXDrawUtil *drawUtil = GFX->getDrawUtil(); + ColorI sphereColor( 0, 255, 0, 45 ); + + GFXStateBlockDesc desc; + desc.setBlend( true ); + desc.setZReadWrite( true, false ); + desc.setCullMode( GFXCullNone ); + + for ( U32 i = 0; i < grid.size(); i++ ) + { + DecalSphere *decalSphere = grid[i]; + const SphereF &worldSphere = decalSphere->mWorldSphere; + + if( state->getFrustum().isCulled( worldSphere ) ) + continue; + + drawUtil->drawSphere( desc, worldSphere.radius, worldSphere.center, sphereColor ); + } +} + +bool DecalManager::_createDataFile() +{ + AssertFatal( !mData, "DecalManager::tried to create duplicate data file?" ); + + // We need to construct a default file name + char fileName[1024]; + fileName[0] = 0; + + // See if we know our current mission name + char missionName[1024]; + dStrcpy( missionName, Con::getVariable( "$Client::MissionFile" ) ); + char *dot = dStrstr((const char*)missionName, ".mis"); + if(dot) + *dot = '\0'; + + dSprintf( fileName, sizeof(fileName), "%s.mis.decals", missionName ); + + mDataFileName = StringTable->insert( fileName ); + + // If the file doesn't exist, create an empty file. + + if( !Torque::FS::IsFile( fileName ) ) + { + FileStream stream; + if( stream.open( mDataFileName, Torque::FS::File::Write ) ) + { + DecalDataFile dataFile; + dataFile.write( stream ); + } + } + + mData = ResourceManager::get().load( mDataFileName ); + return (bool)mData; +} + +void DecalManager::saveDecals( const UTF8* fileName ) +{ + if( !mData ) + return; + + // Create the file. + + FileStream stream; + if ( !stream.open( fileName, Torque::FS::File::Write ) ) + { + Con::errorf( "DecalManager::saveDecals - Could not open '%s' for writing!", fileName ); + return; + } + + // Write the data. + + if( !mData->write( stream ) ) + { + Con::errorf( "DecalManager::saveDecals - Failed to write '%s'", fileName ); + return; + } + + mDirty = false; +} + +bool DecalManager::loadDecals( const UTF8 *fileName ) +{ + if( mData ) + clearData(); + + mData = ResourceManager::get().load( fileName ); + + mDirty = false; + + return mData != NULL; +} + +void DecalManager::clearData() +{ + mClearDataSignal.trigger(); + + // Free all geometry buffers. + + if( mData ) + { + const Vector< DecalSphere* > grid = mData->getSphereList(); + for( U32 i = 0; i < grid.size(); ++ i ) + { + DecalSphere* sphere = grid[ i ]; + for( U32 n = 0; n < sphere->mItems.size(); ++ n ) + _freeBuffers( sphere->mItems[ n ] ); + } + } + + mData = NULL; + mDecalInstanceVec.clear(); + + _freePools(); +} + +bool DecalManager::onSceneAdd() +{ + if( !Parent::onSceneAdd() ) + return false; + + SceneZoneSpaceManager::getZoningChangedSignal().notify( this, &DecalManager::_handleZoningChangedEvent ); + + return true; +} + +void DecalManager::onSceneRemove() +{ + SceneZoneSpaceManager::getZoningChangedSignal().remove( this, &DecalManager::_handleZoningChangedEvent ); + Parent::onSceneRemove(); +} + +void DecalManager::_handleZoningChangedEvent( SceneZoneSpaceManager* zoneManager ) +{ + if( zoneManager != getSceneManager()->getZoneManager() || !getDecalDataFile() ) + return; + + // Clear the zoning state of all DecalSpheres in the data file. + + const Vector< DecalSphere* > grid = getDecalDataFile()->getSphereList(); + const U32 numSpheres = grid.size(); + + for( U32 i = 0; i < numSpheres; ++ i ) + grid[ i ]->mZones.clear(); +} + +DefineEngineFunction( decalManagerSave, void, ( String decalSaveFile ), ( "" ), + "Saves the decals for the active mission in the entered filename.\n" + "@param decalSaveFile Filename to save the decals to.\n" + "@tsexample\n" + "// Set the filename to save the decals in. If no filename is set, then the\n" + "// decals will default to .mis.decals\n" + "%fileName = \"./missionDecals.mis.decals\";\n" + "// Inform the decal manager to save the decals for the active mission.\n" + "decalManagerSave( %fileName );\n" + "@endtsexample\n" + "@ingroup Decals" ) +{ + // If not given a file name, synthesize one. + + if( decalSaveFile.isEmpty() ) + { + String fileName = String::ToString( "%s.decals", Con::getVariable( "$Client::MissionFile" ) ); + + char fullName[ 4096 ]; + Platform::makeFullPathName( fileName, fullName, sizeof( fullName ) ); + + decalSaveFile = String( fullName ); + } + + // Write the data. + + gDecalManager->saveDecals( decalSaveFile ); +} + +DefineEngineFunction( decalManagerLoad, bool, ( const char* fileName ),, + "Clears existing decals and replaces them with decals loaded from the specified file.\n" + "@param fileName Filename to load the decals from.\n" + "@return True if the decal manager was able to load the requested file, " + "false if it could not.\n" + "@tsexample\n" + "// Set the filename to load the decals from.\n" + "%fileName = \"./missionDecals.mis.decals\";\n" + "// Inform the decal manager to load the decals from the entered filename.\n" + "decalManagerLoad( %fileName );\n" + "@endtsexample\n" + "@ingroup Decals" ) +{ + return gDecalManager->loadDecals( fileName ); +} + +DefineEngineFunction( decalManagerDirty, bool, (),, + "Returns whether the decal manager has unsaved modifications.\n" + "@return True if the decal manager has unsaved modifications, false if " + "everything has been saved.\n" + "@tsexample\n" + "// Ask the decal manager if it has unsaved modifications.\n" + "%hasUnsavedModifications = decalManagerDirty();\n" + "@endtsexample\n" + "@ingroup Decals" ) +{ + return gDecalManager->isDirty(); +} + +DefineEngineFunction( decalManagerClear, void, (),, + "Removes all decals currently loaded in the decal manager.\n" + "@tsexample\n" + "// Tell the decal manager to remove all existing decals.\n" + "decalManagerClear();\n" + "@endtsexample\n" + "@ingroup Decals" ) +{ + gDecalManager->clearData(); +} + +DefineEngineFunction( decalManagerAddDecal, S32, + ( Point3F position, Point3F normal, F32 rot, F32 scale, DecalData* decalData, bool isImmortal), ( false ), + "Adds a new decal to the decal manager.\n" + "@param position World position for the decal.\n" + "@param normal Decal normal vector (if the decal was a tire lying flat on a " + "surface, this is the vector pointing in the direction of the axle).\n" + "@param rot Angle (in radians) to rotate this decal around its normal vector.\n" + "@param scale Scale factor applied to the decal.\n" + "@param decalData DecalData datablock to use for the new decal.\n" + "@param isImmortal Whether or not this decal is immortal. If immortal, it " + "does not expire automatically and must be removed explicitly.\n" + "@return Returns the ID of the new Decal object or -1 on failure.\n" + "@tsexample\n" + "// Specify the decal position\n" + "%position = \"1.0 1.0 1.0\";\n\n" + "// Specify the up vector\n" + "%normal = \"0.0 0.0 1.0\";\n\n" + "// Add the new decal.\n" + "%decalObj = decalManagerAddDecal( %position, %normal, 0.5, 0.35, ScorchBigDecal, false );\n" + "@endtsexample\n" + "@ingroup Decals" ) +{ + if( !decalData ) + { + Con::errorf( "decalManagerAddDecal - Invalid Decal DataBlock" ); + return -1; + } + + U8 flags = 0; + if( isImmortal ) + flags |= PermanentDecal; + + DecalInstance* inst = gDecalManager->addDecal( position, normal, rot, decalData, scale, -1, flags ); + if( !inst ) + { + Con::errorf( "decalManagerAddDecal - Unable to create decal instance." ); + return -1; + } + + // Add the decal to the instance vector. + inst->mId = gDecalManager->mDecalInstanceVec.size(); + gDecalManager->mDecalInstanceVec.push_back( inst ); + + return inst->mId; +} + +DefineEngineFunction( decalManagerRemoveDecal, bool, ( S32 decalID ),, + "Remove specified decal from the scene.\n" + "@param decalID ID of the decal to remove.\n" + "@return Returns true if successful, false if decal ID not found.\n" + "@tsexample\n" + "// Specify a decal ID to be removed\n" + "%decalID = 1;\n\n" + "// Tell the decal manager to remove the specified decal ID.\n" + "decalManagerRemoveDecal( %decalId )\n" + "@endtsexample\n" + "@ingroup Decals" ) +{ + DecalInstance *inst = gDecalManager->getDecal( decalID ); + if( !inst ) + return false; + + gDecalManager->removeDecal(inst); + return true; +} diff --git a/Engine/source/T3D/decal/decalManager.h b/Engine/source/T3D/decal/decalManager.h new file mode 100644 index 000000000..9e03c59c7 --- /dev/null +++ b/Engine/source/T3D/decal/decalManager.h @@ -0,0 +1,278 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _DECALMANAGER_H_ +#define _DECALMANAGER_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif + +#ifndef _MATHUTIL_FRUSTUM_H_ +#include "math/util/frustum.h" +#endif + +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif + +#ifndef _GFXDEVICE_H_ +#include "gfx/gfxDevice.h" +#endif + +#ifndef _CLIPPEDPOLYLIST_H_ +#include "collision/clippedPolyList.h" +#endif + +#ifndef _DECALDATAFILE_H_ +#include "decalDataFile.h" +#endif + +#ifndef __RESOURCE_H__ +#include "core/resource.h" +#endif + +#ifndef _DECALINSTANCE_H_ +#include "decalInstance.h" +#endif + +#ifndef _TSIGNAL_H_ +#include "core/util/tSignal.h" +#endif + +#ifndef _DATACHUNKER_H_ +#include "core/dataChunker.h" +#endif + + +//#define DECALMANAGER_DEBUG + + +struct ObjectRenderInst; +class Material; + + +enum DecalFlags +{ + PermanentDecal = 1 << 0, + SaveDecal = 1 << 1, + ClipDecal = 1 << 2, + CustomDecal = 1 << 3 // DecalManager will not attempt to clip or remove this decal + // it is managed by someone else. +}; + + +/// Manage decals in the scene. +class DecalManager : public SceneObject +{ + public: + + typedef SceneObject Parent; + + // [rene, 11-Mar-11] This vector is very poorly managed; the logic is spread all over the place + Vector mDecalInstanceVec; + + protected: + + /// The clipper we keep around between decal updates + /// to avoid excessive memory allocations. + ClippedPolyList mClipper; + + Vector mDecalQueue; + + StringTableEntry mDataFileName; + Resource mData; + + Signal< void() > mClearDataSignal; + + Vector< GFXVertexBufferHandle* > mVBs; + Vector< GFXPrimitiveBufferHandle* > mPBs; + + Vector< GFXVertexBufferHandle* > mVBPool; + Vector< GFXPrimitiveBufferHandle* > mPBPool; + + FreeListChunkerUntyped *mChunkers[3]; + + #ifdef DECALMANAGER_DEBUG + Vector mDebugPlanes; + #endif + + bool mDirty; + + struct DecalBatch + { + U32 startDecal; + U32 decalCount; + U32 iCount; + U32 vCount; + U8 priority; + Material *mat; + BaseMatInstance *matInst; + bool dynamic; + }; + + /// Whether to render visualizations for debugging in the editor. + static bool smDebugRender; + + static bool smDecalsOn; + static F32 smDecalLifeTimeScale; + static bool smPoolBuffers; + static const U32 smMaxVerts; + static const U32 smMaxIndices; + + // Assume that a class is already given for the object: + // Point with coordinates {float x, y;} + //=================================================================== + + // isLeft(): tests if a point is Left|On|Right of an infinite line. + // Input: three points P0, P1, and P2 + // Return: >0 for P2 left of the line through P0 and P1 + // =0 for P2 on the line + // <0 for P2 right of the line + // See: the January 2001 Algorithm on Area of Triangles + inline F32 isLeft( const Point3F &P0, const Point3F &P1, const Point3F &P2 ) + { + return (P1.x - P0.x)*(P2.y - P0.y) - (P2.x - P0.x)*(P1.y - P0.y); + } + + U32 _generateConvexHull( const Vector &points, Vector *outPoints ); + + // Rendering + void prepRenderImage( SceneRenderState *state ); + + void _generateWindingOrder( const Point3F &cornerPoint, Vector *sortPoints ); + + // Helpers for creating and deleting the vert and index arrays + // held by DecalInstance. + void _allocBuffers( DecalInstance *inst ); + void _freeBuffers( DecalInstance *inst ); + void _freePools(); + + /// Returns index used to index into the correct sized FreeListChunker for + /// allocating vertex and index arrays. + S32 _getSizeClass( DecalInstance *inst ) const; + + // Hide this from Doxygen + /// @cond + bool _handleGFXEvent(GFXDevice::GFXDeviceEventType event); + /// @endcond + + void _renderDecalSpheres( ObjectRenderInst* inst, SceneRenderState* state, BaseMatInstance* overrideMat ); + + /// + void _handleZoningChangedEvent( SceneZoneSpaceManager* zoneManager ); + + bool _createDataFile(); + + // SceneObject. + virtual bool onSceneAdd(); + virtual void onSceneRemove(); public: + + public: + + DecalManager(); + ~DecalManager(); + + /// @name Decal Addition + /// @{ + + /// Adds a decal using a normal and a rotation. + /// + /// @param pos The 3d position for the decal center. + /// @param normal The decal up vector. + /// @param rotAroundNormal The decal rotation around the normal. + /// @param decalData The datablock which defines this decal. + /// @param decalScale A scalar for adjusting the default size of the decal. + /// @param decalTexIndex Selects the texture coord index within the decal + /// data to use. If it is less than zero then a random + /// coord is selected. + /// @param flags The decal flags. @see DecalFlags + /// + DecalInstance* addDecal( const Point3F &pos, + const Point3F &normal, + F32 rotAroundNormal, + DecalData *decalData, + F32 decalScale = 1.0f, + S32 decalTexIndex = 0, + U8 flags = 0x000 ); + + /// Adds a decal using a normal and a tangent. + /// + /// @param pos The 3d position for the decal center. + /// @param normal The decal up vector. + /// @param tanget The decal right vector. + /// @param decalData The datablock which defines this decal. + /// @param decalScale A scalar for adjusting the default size of the decal. + /// @param decalTexIndex Selects the texture coord index within the decal + /// data to use. If it is less than zero then a random + /// coord is selected. + /// @param flags The decal flags. @see DecalFlags + /// + DecalInstance* addDecal( const Point3F &pos, + const Point3F &normal, + const Point3F &tangent, + DecalData *decalData, + F32 decalScale = 1.0f, + S32 decalTexIndex = 0, + U8 flags = 0 ); + + /// @} + + /// @name Decal Removal + /// @{ + + void removeDecal( DecalInstance *inst ); + + /// @} + + DecalInstance* getDecal( S32 id ); + + DecalInstance* getClosestDecal( const Point3F &pos ); + + /// Return the closest DecalInstance hit by a ray. + DecalInstance* raycast( const Point3F &start, const Point3F &end, bool savedDecalsOnly = true ); + + //void dataDeleted( DecalData *data ); + + void saveDecals( const UTF8 *fileName ); + bool loadDecals( const UTF8 *fileName ); + void clearData(); + + /// Returns true if changes have been made since the last load/save + bool isDirty() const { return mDirty; } + + bool clipDecal( DecalInstance *decal, Vector *edgeVerts = NULL, const Point2F *clipDepth = NULL ); + + void notifyDecalModified( DecalInstance *inst ); + + Signal< void() >& getClearDataSignal() { return mClearDataSignal; } + + Resource getDecalDataFile() { return mData; } + + // SimObject. + DECLARE_CONOBJECT( DecalManager ); + static void consoleInit(); +}; + +extern DecalManager* gDecalManager; + +#endif // _DECALMANAGER_H_ diff --git a/Engine/source/T3D/decal/decalSphere.cpp b/Engine/source/T3D/decal/decalSphere.cpp new file mode 100644 index 000000000..bcd46db20 --- /dev/null +++ b/Engine/source/T3D/decal/decalSphere.cpp @@ -0,0 +1,109 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/decal/decalSphere.h" + +#include "scene/zones/sceneZoneSpaceManager.h" +#include "T3D/decal/decalInstance.h" + + +F32 DecalSphere::smDistanceTolerance = 30.0f; +F32 DecalSphere::smRadiusTolerance = 40.0f; + + +//----------------------------------------------------------------------------- + +bool DecalSphere::tryAddItem( DecalInstance* inst ) +{ + // If we are further away from the center than our tolerance + // allows, don't use this sphere. + // + // Note that we take the distance from the nearest point on the + // bounding sphere rather than the distance to the center of the + // decal. This takes decal sizes into account and generally works + // better. + + const F32 distCenterToCenter = ( mWorldSphere.center - inst->mPosition ).len(); + const F32 distBoundsToCenter = distCenterToCenter - inst->mSize / 2.f; + + if( distBoundsToCenter > smDistanceTolerance ) + return false; + + // Also, if adding the current decal to the sphere would make + // it larger than our radius tolerance, don't use it. + + const F32 newRadius = distCenterToCenter + inst->mSize / 2.f + 0.5f; + if( newRadius > mWorldSphere.radius && newRadius > smRadiusTolerance ) + return false; + + // Otherwise, go with this sphere and add the item to it. + + mItems.push_back( inst ); + + // Update the sphere bounds, if necessary. + + if( newRadius > mWorldSphere.radius ) + updateWorldSphere(); + + return true; +} + +//----------------------------------------------------------------------------- + +void DecalSphere::updateWorldSphere() +{ + AssertFatal( mItems.size() >= 1, "DecalSphere::updateWorldSphere - Sphere is empty!" ); + + Box3F aabb( mItems[ 0 ]->getWorldBox() ); + + const U32 numItems = mItems.size(); + for( U32 i = 1; i < numItems; ++ i ) + aabb.intersect( mItems[ i ]->getWorldBox() ); + + mWorldSphere = aabb.getBoundingSphere(); + + // Clear the zoning data so that it gets recomputed. + mZones.clear(); +} + +//----------------------------------------------------------------------------- + +void DecalSphere::updateZoning( SceneZoneSpaceManager* zoneManager ) +{ + mZones.clear(); + + // If there's only a single zone in the world, it must be the + // outdoor zone, so there's no point maintaining zoning information + // on all the decalspheres. + + if( zoneManager->getNumZones() == 1 ) + return; + + // Otherwise query the scene graph for all zones that intersect with the + // AABB around our world sphere. + + Box3F aabb( mWorldSphere.radius ); + aabb.setCenter( mWorldSphere.center ); + + zoneManager->findZones( aabb, mZones ); +} diff --git a/Engine/source/T3D/decal/decalSphere.h b/Engine/source/T3D/decal/decalSphere.h new file mode 100644 index 000000000..f57209b62 --- /dev/null +++ b/Engine/source/T3D/decal/decalSphere.h @@ -0,0 +1,83 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _DECALSPHERE_H_ +#define _DECALSPHERE_H_ + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +#ifndef _MSPHERE_H_ +#include "math/mSphere.h" +#endif + + +class DecalInstance; +class SceneZoneSpaceManager; + + +/// A bounding sphere in world space and a list of DecalInstance(s) +/// contained by it. DecalInstance(s) are organized/binned in this fashion +/// as a lookup and culling optimization. +class DecalSphere +{ + public: + + static F32 smDistanceTolerance; + static F32 smRadiusTolerance; + + DecalSphere() + { + VECTOR_SET_ASSOCIATION( mItems ); + VECTOR_SET_ASSOCIATION( mZones ); + } + DecalSphere( const Point3F &position, F32 radius ) + { + VECTOR_SET_ASSOCIATION( mItems ); + VECTOR_SET_ASSOCIATION( mZones ); + + mWorldSphere.center = position; + mWorldSphere.radius = radius; + } + + /// Recompute #mWorldSphere from the current instance list. + void updateWorldSphere(); + + /// Recompute the zoning information from the current bounds. + void updateZoning( SceneZoneSpaceManager* zoneManager ); + + /// Decal instances in this sphere. + Vector< DecalInstance* > mItems; + + /// Zones that intersect with this sphere. If this is empty, the zoning + /// state of the sphere is uninitialized. + Vector< U32 > mZones; + + /// World-space sphere corresponding to this DecalSphere. + SphereF mWorldSphere; + + /// + bool tryAddItem( DecalInstance* inst ); +}; + +#endif // !_DECALSPHERE_H_ \ No newline at end of file diff --git a/Engine/source/T3D/examples/renderMeshExample.cpp b/Engine/source/T3D/examples/renderMeshExample.cpp new file mode 100644 index 000000000..ec6831382 --- /dev/null +++ b/Engine/source/T3D/examples/renderMeshExample.cpp @@ -0,0 +1,346 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/examples/renderMeshExample.h" + +#include "math/mathIO.h" +#include "scene/sceneRenderState.h" +#include "console/consoleTypes.h" +#include "core/stream/bitStream.h" +#include "materials/materialManager.h" +#include "materials/baseMatInstance.h" +#include "renderInstance/renderPassManager.h" +#include "lighting/lightQuery.h" +#include "console/engineAPI.h" + + +IMPLEMENT_CO_NETOBJECT_V1(RenderMeshExample); + +ConsoleDocClass( RenderMeshExample, + "@brief An example scene object which renders a mesh.\n\n" + "This class implements a basic SceneObject that can exist in the world at a " + "3D position and render itself. There are several valid ways to render an " + "object in Torque. This class implements the preferred rendering method which " + "is to submit a MeshRenderInst along with a Material, vertex buffer, " + "primitive buffer, and transform and allow the RenderMeshMgr handle the " + "actual setup and rendering for you.\n\n" + "See the C++ code for implementation details.\n\n" + "@ingroup Examples\n" ); + + +//----------------------------------------------------------------------------- +// Object setup and teardown +//----------------------------------------------------------------------------- +RenderMeshExample::RenderMeshExample() +{ + // Flag this object so that it will always + // be sent across the network to clients + mNetFlags.set( Ghostable | ScopeAlways ); + + // Set it as a "static" object that casts shadows + mTypeMask |= StaticObjectType | StaticShapeObjectType; + + // Make sure we the Material instance to NULL + // so we don't try to access it incorrectly + mMaterialInst = NULL; +} + +RenderMeshExample::~RenderMeshExample() +{ + if ( mMaterialInst ) + SAFE_DELETE( mMaterialInst ); +} + +//----------------------------------------------------------------------------- +// Object Editing +//----------------------------------------------------------------------------- +void RenderMeshExample::initPersistFields() +{ + addGroup( "Rendering" ); + addField( "material", TypeMaterialName, Offset( mMaterialName, RenderMeshExample ), + "The name of the material used to render the mesh." ); + endGroup( "Rendering" ); + + // SceneObject already handles exposing the transform + Parent::initPersistFields(); +} + +void RenderMeshExample::inspectPostApply() +{ + Parent::inspectPostApply(); + + // Flag the network mask to send the updates + // to the client object + setMaskBits( UpdateMask ); +} + +bool RenderMeshExample::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + // Set up a 1x1x1 bounding box + mObjBox.set( Point3F( -0.5f, -0.5f, -0.5f ), + Point3F( 0.5f, 0.5f, 0.5f ) ); + + resetWorldBox(); + + // Add this object to the scene + addToScene(); + + return true; +} + +void RenderMeshExample::onRemove() +{ + // Remove this object from the scene + removeFromScene(); + + Parent::onRemove(); +} + +void RenderMeshExample::setTransform(const MatrixF & mat) +{ + // Let SceneObject handle all of the matrix manipulation + Parent::setTransform( mat ); + + // Dirty our network mask so that the new transform gets + // transmitted to the client object + setMaskBits( TransformMask ); +} + +U32 RenderMeshExample::packUpdate( NetConnection *conn, U32 mask, BitStream *stream ) +{ + // Allow the Parent to get a crack at writing its info + U32 retMask = Parent::packUpdate( conn, mask, stream ); + + // Write our transform information + if ( stream->writeFlag( mask & TransformMask ) ) + { + mathWrite(*stream, getTransform()); + mathWrite(*stream, getScale()); + } + + // Write out any of the updated editable properties + if ( stream->writeFlag( mask & UpdateMask ) ) + stream->write( mMaterialName ); + + return retMask; +} + +void RenderMeshExample::unpackUpdate(NetConnection *conn, BitStream *stream) +{ + // Let the Parent read any info it sent + Parent::unpackUpdate(conn, stream); + + if ( stream->readFlag() ) // TransformMask + { + mathRead(*stream, &mObjToWorld); + mathRead(*stream, &mObjScale); + + setTransform( mObjToWorld ); + } + + if ( stream->readFlag() ) // UpdateMask + { + stream->read( &mMaterialName ); + + if ( isProperlyAdded() ) + updateMaterial(); + } +} + +//----------------------------------------------------------------------------- +// Object Rendering +//----------------------------------------------------------------------------- +void RenderMeshExample::createGeometry() +{ + static const Point3F cubePoints[8] = + { + Point3F( 1, -1, -1), Point3F( 1, -1, 1), Point3F( 1, 1, -1), Point3F( 1, 1, 1), + Point3F(-1, -1, -1), Point3F(-1, 1, -1), Point3F(-1, -1, 1), Point3F(-1, 1, 1) + }; + + static const Point3F cubeNormals[6] = + { + Point3F( 1, 0, 0), Point3F(-1, 0, 0), Point3F( 0, 1, 0), + Point3F( 0, -1, 0), Point3F( 0, 0, 1), Point3F( 0, 0, -1) + }; + + static const Point2F cubeTexCoords[4] = + { + Point2F( 0, 0), Point2F( 0, -1), + Point2F( 1, 0), Point2F( 1, -1) + }; + + static const U32 cubeFaces[36][3] = + { + { 3, 0, 3 }, { 0, 0, 0 }, { 1, 0, 1 }, + { 2, 0, 2 }, { 0, 0, 0 }, { 3, 0, 3 }, + { 7, 1, 1 }, { 4, 1, 2 }, { 5, 1, 0 }, + { 6, 1, 3 }, { 4, 1, 2 }, { 7, 1, 1 }, + { 3, 2, 1 }, { 5, 2, 2 }, { 2, 2, 0 }, + { 7, 2, 3 }, { 5, 2, 2 }, { 3, 2, 1 }, + { 1, 3, 3 }, { 4, 3, 0 }, { 6, 3, 1 }, + { 0, 3, 2 }, { 4, 3, 0 }, { 1, 3, 3 }, + { 3, 4, 3 }, { 6, 4, 0 }, { 7, 4, 1 }, + { 1, 4, 2 }, { 6, 4, 0 }, { 3, 4, 3 }, + { 2, 5, 1 }, { 4, 5, 2 }, { 0, 5, 0 }, + { 5, 5, 3 }, { 4, 5, 2 }, { 2, 5, 1 } + }; + + // Fill the vertex buffer + VertexType *pVert = NULL; + + mVertexBuffer.set( GFX, 36, GFXBufferTypeStatic ); + pVert = mVertexBuffer.lock(); + + Point3F halfSize = getObjBox().getExtents() * 0.5f; + + for (U32 i = 0; i < 36; i++) + { + const U32& vdx = cubeFaces[i][0]; + const U32& ndx = cubeFaces[i][1]; + const U32& tdx = cubeFaces[i][2]; + + pVert[i].point = cubePoints[vdx] * halfSize; + pVert[i].normal = cubeNormals[ndx]; + pVert[i].texCoord = cubeTexCoords[tdx]; + } + + mVertexBuffer.unlock(); + + // Fill the primitive buffer + U16 *pIdx = NULL; + + mPrimitiveBuffer.set( GFX, 36, 12, GFXBufferTypeStatic ); + + mPrimitiveBuffer.lock(&pIdx); + + for (U16 i = 0; i < 36; i++) + pIdx[i] = i; + + mPrimitiveBuffer.unlock(); +} + +void RenderMeshExample::updateMaterial() +{ + if ( mMaterialName.isEmpty() ) + return; + + // If the material name matches then don't bother updating it. + if ( mMaterialInst && mMaterialName.equal( mMaterialInst->getMaterial()->getName(), String::NoCase ) ) + return; + + SAFE_DELETE( mMaterialInst ); + + mMaterialInst = MATMGR->createMatInstance( mMaterialName, getGFXVertexFormat< VertexType >() ); + if ( !mMaterialInst ) + Con::errorf( "RenderMeshExample::updateMaterial - no Material called '%s'", mMaterialName.c_str() ); +} + +void RenderMeshExample::prepRenderImage( SceneRenderState *state ) +{ + // Do a little prep work if needed + if ( mVertexBuffer.isNull() ) + createGeometry(); + + // If we have no material then skip out. + if ( !mMaterialInst ) + return; + + // If we don't have a material instance after the override then + // we can skip rendering all together. + BaseMatInstance *matInst = state->getOverrideMaterial( mMaterialInst ); + if ( !matInst ) + return; + + // Get a handy pointer to our RenderPassmanager + RenderPassManager *renderPass = state->getRenderPass(); + + // Allocate an MeshRenderInst so that we can submit it to the RenderPassManager + MeshRenderInst *ri = renderPass->allocInst(); + + // Set our RenderInst as a standard mesh render + ri->type = RenderPassManager::RIT_Mesh; + + // Calculate our sorting point + if ( state ) + { + // Calculate our sort point manually. + const Box3F& rBox = getRenderWorldBox(); + ri->sortDistSq = rBox.getSqDistanceToPoint( state->getCameraPosition() ); + } + else + ri->sortDistSq = 0.0f; + + // Set up our transforms + MatrixF objectToWorld = getRenderTransform(); + objectToWorld.scale( getScale() ); + + ri->objectToWorld = renderPass->allocUniqueXform( objectToWorld ); + ri->worldToCamera = renderPass->allocSharedXform(RenderPassManager::View); + ri->projection = renderPass->allocSharedXform(RenderPassManager::Projection); + + // If our material needs lights then fill the RIs + // light vector with the best lights. + if ( matInst->isForwardLit() ) + { + LightQuery query; + query.init( getWorldSphere() ); + query.getLights( ri->lights, 8 ); + } + + // Make sure we have an up-to-date backbuffer in case + // our Material would like to make use of it + // NOTICE: SFXBB is removed and refraction is disabled! + //ri->backBuffTex = GFX->getSfxBackBuffer(); + + // Set our Material + ri->matInst = matInst; + + // Set up our vertex buffer and primitive buffer + ri->vertBuff = &mVertexBuffer; + ri->primBuff = &mPrimitiveBuffer; + + ri->prim = renderPass->allocPrim(); + ri->prim->type = GFXTriangleList; + ri->prim->minIndex = 0; + ri->prim->startIndex = 0; + ri->prim->numPrimitives = 12; + ri->prim->startVertex = 0; + ri->prim->numVertices = 36; + + // We sort by the material then vertex buffer + ri->defaultKey = matInst->getStateHint(); + ri->defaultKey2 = (U32)ri->vertBuff; // Not 64bit safe! + + // Submit our RenderInst to the RenderPassManager + state->getRenderPass()->addInst( ri ); +} + +DefineEngineMethod( RenderMeshExample, postApply, void, (),, + "A utility method for forcing a network update.\n") +{ + object->inspectPostApply(); +} \ No newline at end of file diff --git a/Engine/source/T3D/examples/renderMeshExample.h b/Engine/source/T3D/examples/renderMeshExample.h new file mode 100644 index 000000000..0b2347c3b --- /dev/null +++ b/Engine/source/T3D/examples/renderMeshExample.h @@ -0,0 +1,134 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _RENDERMESHEXAMPLE_H_ +#define _RENDERMESHEXAMPLE_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _GFXVERTEXBUFFER_H_ +#include "gfx/gfxVertexBuffer.h" +#endif +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif + +class BaseMatInstance; + + +//----------------------------------------------------------------------------- +// This class implements a basic SceneObject that can exist in the world at a +// 3D position and render itself. There are several valid ways to render an +// object in Torque. This class implements the preferred rendering method which +// is to submit a MeshRenderInst along with a Material, vertex buffer, +// primitive buffer, and transform and allow the RenderMeshMgr handle the +// actual setup and rendering for you. +//----------------------------------------------------------------------------- + +class RenderMeshExample : public SceneObject +{ + typedef SceneObject Parent; + + // Networking masks + // We need to implement a mask specifically to handle + // updating our transform from the server object to its + // client-side "ghost". We also need to implement a + // maks for handling editor updates to our properties + // (like material). + enum MaskBits + { + TransformMask = Parent::NextFreeMask << 0, + UpdateMask = Parent::NextFreeMask << 1, + NextFreeMask = Parent::NextFreeMask << 2 + }; + + //-------------------------------------------------------------------------- + // Rendering variables + //-------------------------------------------------------------------------- + // The name of the Material we will use for rendering + String mMaterialName; + // The actual Material instance + BaseMatInstance* mMaterialInst; + + // Define our vertex format here so we don't have to + // change it in multiple spots later + typedef GFXVertexPNT VertexType; + + // The GFX vertex and primitive buffers + GFXVertexBufferHandle< VertexType > mVertexBuffer; + GFXPrimitiveBufferHandle mPrimitiveBuffer; + +public: + RenderMeshExample(); + virtual ~RenderMeshExample(); + + // Declare this object as a ConsoleObject so that we can + // instantiate it into the world and network it + DECLARE_CONOBJECT(RenderMeshExample); + + //-------------------------------------------------------------------------- + // Object Editing + // Since there is always a server and a client object in Torque and we + // actually edit the server object we need to implement some basic + // networking functions + //-------------------------------------------------------------------------- + // Set up any fields that we want to be editable (like position) + static void initPersistFields(); + + // Allows the object to update its editable settings + // from the server object to the client + virtual void inspectPostApply(); + + // Handle when we are added to the scene and removed from the scene + bool onAdd(); + void onRemove(); + + // Override this so that we can dirty the network flag when it is called + void setTransform( const MatrixF &mat ); + + // This function handles sending the relevant data from the server + // object to the client object + U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream ); + // This function handles receiving relevant data from the server + // object and applying it to the client object + void unpackUpdate( NetConnection *conn, BitStream *stream ); + + //-------------------------------------------------------------------------- + // Object Rendering + // Torque utilizes a "batch" rendering system. This means that it builds a + // list of objects that need to render (via RenderInst's) and then renders + // them all in one batch. This allows it to optimized on things like + // minimizing texture, state, and shader switching by grouping objects that + // use the same Materials. + //-------------------------------------------------------------------------- + // Create the geometry for rendering + void createGeometry(); + + // Get the Material instance + void updateMaterial(); + + // This is the function that allows this object to submit itself for rendering + void prepRenderImage( SceneRenderState *state ); +}; + +#endif // _RENDERMESHEXAMPLE_H_ \ No newline at end of file diff --git a/Engine/source/T3D/examples/renderObjectExample.cpp b/Engine/source/T3D/examples/renderObjectExample.cpp new file mode 100644 index 000000000..e491eb447 --- /dev/null +++ b/Engine/source/T3D/examples/renderObjectExample.cpp @@ -0,0 +1,280 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "T3D/examples/renderObjectExample.h" + +#include "math/mathIO.h" +#include "scene/sceneRenderState.h" +#include "core/stream/bitStream.h" +#include "materials/sceneData.h" +#include "gfx/gfxDebugEvent.h" +#include "gfx/gfxTransformSaver.h" +#include "renderInstance/renderPassManager.h" + + +IMPLEMENT_CO_NETOBJECT_V1(RenderObjectExample); + +ConsoleDocClass( RenderObjectExample, + "@brief An example scene object which renders using a callback.\n\n" + "This class implements a basic SceneObject that can exist in the world at a " + "3D position and render itself. Note that RenderObjectExample handles its own " + "rendering by submitting itself as an ObjectRenderInst (see " + "renderInstance\renderPassmanager.h) along with a delegate for its render() " + "function. However, the preffered rendering method in the engine is to submit " + "a MeshRenderInst along with a Material, vertex buffer, primitive buffer, and " + "transform and allow the RenderMeshMgr handle the actual rendering. You can " + "see this implemented in RenderMeshExample.\n\n" + "See the C++ code for implementation details.\n\n" + "@ingroup Examples\n" ); + +//----------------------------------------------------------------------------- +// Object setup and teardown +//----------------------------------------------------------------------------- +RenderObjectExample::RenderObjectExample() +{ + // Flag this object so that it will always + // be sent across the network to clients + mNetFlags.set( Ghostable | ScopeAlways ); + + // Set it as a "static" object + mTypeMask |= StaticObjectType | StaticShapeObjectType; +} + +RenderObjectExample::~RenderObjectExample() +{ +} + +//----------------------------------------------------------------------------- +// Object Editing +//----------------------------------------------------------------------------- +void RenderObjectExample::initPersistFields() +{ + // SceneObject already handles exposing the transform + Parent::initPersistFields(); +} + +bool RenderObjectExample::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + // Set up a 1x1x1 bounding box + mObjBox.set( Point3F( -0.5f, -0.5f, -0.5f ), + Point3F( 0.5f, 0.5f, 0.5f ) ); + + resetWorldBox(); + + // Add this object to the scene + addToScene(); + + return true; +} + +void RenderObjectExample::onRemove() +{ + // Remove this object from the scene + removeFromScene(); + + Parent::onRemove(); +} + +void RenderObjectExample::setTransform(const MatrixF & mat) +{ + // Let SceneObject handle all of the matrix manipulation + Parent::setTransform( mat ); + + // Dirty our network mask so that the new transform gets + // transmitted to the client object + setMaskBits( TransformMask ); +} + +U32 RenderObjectExample::packUpdate( NetConnection *conn, U32 mask, BitStream *stream ) +{ + // Allow the Parent to get a crack at writing its info + U32 retMask = Parent::packUpdate( conn, mask, stream ); + + // Write our transform information + if ( stream->writeFlag( mask & TransformMask ) ) + { + mathWrite(*stream, getTransform()); + mathWrite(*stream, getScale()); + } + + return retMask; +} + +void RenderObjectExample::unpackUpdate(NetConnection *conn, BitStream *stream) +{ + // Let the Parent read any info it sent + Parent::unpackUpdate(conn, stream); + + if ( stream->readFlag() ) // TransformMask + { + mathRead(*stream, &mObjToWorld); + mathRead(*stream, &mObjScale); + + setTransform( mObjToWorld ); + } +} + +//----------------------------------------------------------------------------- +// Object Rendering +//----------------------------------------------------------------------------- +void RenderObjectExample::createGeometry() +{ + static const Point3F cubePoints[8] = + { + Point3F( 1.0f, -1.0f, -1.0f), Point3F( 1.0f, -1.0f, 1.0f), + Point3F( 1.0f, 1.0f, -1.0f), Point3F( 1.0f, 1.0f, 1.0f), + Point3F(-1.0f, -1.0f, -1.0f), Point3F(-1.0f, 1.0f, -1.0f), + Point3F(-1.0f, -1.0f, 1.0f), Point3F(-1.0f, 1.0f, 1.0f) + }; + + static const Point3F cubeNormals[6] = + { + Point3F( 1.0f, 0.0f, 0.0f), Point3F(-1.0f, 0.0f, 0.0f), + Point3F( 0.0f, 1.0f, 0.0f), Point3F( 0.0f, -1.0f, 0.0f), + Point3F( 0.0f, 0.0f, 1.0f), Point3F( 0.0f, 0.0f, -1.0f) + }; + + static const ColorI cubeColors[3] = + { + ColorI( 255, 0, 0, 255 ), + ColorI( 0, 255, 0, 255 ), + ColorI( 0, 0, 255, 255 ) + }; + + static const U32 cubeFaces[36][3] = + { + { 3, 0, 0 }, { 0, 0, 0 }, { 1, 0, 0 }, + { 2, 0, 0 }, { 0, 0, 0 }, { 3, 0, 0 }, + { 7, 1, 0 }, { 4, 1, 0 }, { 5, 1, 0 }, + { 6, 1, 0 }, { 4, 1, 0 }, { 7, 1, 0 }, + { 3, 2, 1 }, { 5, 2, 1 }, { 2, 2, 1 }, + { 7, 2, 1 }, { 5, 2, 1 }, { 3, 2, 1 }, + { 1, 3, 1 }, { 4, 3, 1 }, { 6, 3, 1 }, + { 0, 3, 1 }, { 4, 3, 1 }, { 1, 3, 1 }, + { 3, 4, 2 }, { 6, 4, 2 }, { 7, 4, 2 }, + { 1, 4, 2 }, { 6, 4, 2 }, { 3, 4, 2 }, + { 2, 5, 2 }, { 4, 5, 2 }, { 0, 5, 2 }, + { 5, 5, 2 }, { 4, 5, 2 }, { 2, 5, 2 } + }; + + // Fill the vertex buffer + VertexType *pVert = NULL; + + mVertexBuffer.set( GFX, 36, GFXBufferTypeStatic ); + pVert = mVertexBuffer.lock(); + + Point3F halfSize = getObjBox().getExtents() * 0.5f; + + for (U32 i = 0; i < 36; i++) + { + const U32& vdx = cubeFaces[i][0]; + const U32& ndx = cubeFaces[i][1]; + const U32& cdx = cubeFaces[i][2]; + + pVert[i].point = cubePoints[vdx] * halfSize; + pVert[i].normal = cubeNormals[ndx]; + pVert[i].color = cubeColors[cdx]; + } + + mVertexBuffer.unlock(); + + // Set up our normal and reflection StateBlocks + GFXStateBlockDesc desc; + + // The normal StateBlock only needs a default StateBlock + mNormalSB = GFX->createStateBlock( desc ); + + // The reflection needs its culling reversed + desc.cullDefined = true; + desc.cullMode = GFXCullCW; + mReflectSB = GFX->createStateBlock( desc ); +} + +void RenderObjectExample::prepRenderImage( SceneRenderState *state ) +{ + // Do a little prep work if needed + if ( mVertexBuffer.isNull() ) + createGeometry(); + + // Allocate an ObjectRenderInst so that we can submit it to the RenderPassManager + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + + // Now bind our rendering function so that it will get called + ri->renderDelegate.bind( this, &RenderObjectExample::render ); + + // Set our RenderInst as a standard object render + ri->type = RenderPassManager::RIT_Object; + + // Set our sorting keys to a default value + ri->defaultKey = 0; + ri->defaultKey2 = 0; + + // Submit our RenderInst to the RenderPassManager + state->getRenderPass()->addInst( ri ); +} + +void RenderObjectExample::render( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ) +{ + if ( overrideMat ) + return; + + if ( mVertexBuffer.isNull() ) + return; + + PROFILE_SCOPE(RenderObjectExample_Render); + + // Set up a GFX debug event (this helps with debugging rendering events in external tools) + GFXDEBUGEVENT_SCOPE( RenderObjectExample_Render, ColorI::RED ); + + // GFXTransformSaver is a handy helper class that restores + // the current GFX matrices to their original values when + // it goes out of scope at the end of the function + GFXTransformSaver saver; + + // Calculate our object to world transform matrix + MatrixF objectToWorld = getRenderTransform(); + objectToWorld.scale( getScale() ); + + // Apply our object transform + GFX->multWorld( objectToWorld ); + + // Deal with reflect pass otherwise + // set the normal StateBlock + if ( state->isReflectPass() ) + GFX->setStateBlock( mReflectSB ); + else + GFX->setStateBlock( mNormalSB ); + + // Set up the "generic" shaders + // These handle rendering on GFX layers that don't support + // fixed function. Otherwise they disable shaders. + GFX->setupGenericShaders( GFXDevice::GSModColorTexture ); + + // Set the vertex buffer + GFX->setVertexBuffer( mVertexBuffer ); + + // Draw our triangles + GFX->drawPrimitive( GFXTriangleList, 0, 12 ); +} \ No newline at end of file diff --git a/Engine/source/T3D/examples/renderObjectExample.h b/Engine/source/T3D/examples/renderObjectExample.h new file mode 100644 index 000000000..faa009602 --- /dev/null +++ b/Engine/source/T3D/examples/renderObjectExample.h @@ -0,0 +1,136 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _RENDEROBJECTEXAMPLE_H_ +#define _RENDEROBJECTEXAMPLE_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _GFXSTATEBLOCK_H_ +#include "gfx/gfxStateBlock.h" +#endif +#ifndef _GFXVERTEXBUFFER_H_ +#include "gfx/gfxVertexBuffer.h" +#endif +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif + +class BaseMatInstance; + + +//----------------------------------------------------------------------------- +// This class implements a basic SceneObject that can exist in the world at a +// 3D position and render itself. Note that RenderObjectExample handles its own +// rendering by submitting itself as an ObjectRenderInst (see +// renderInstance\renderPassmanager.h) along with a delegate for its render() +// function. However, the preffered rendering method in the engine is to submit +// a MeshRenderInst along with a Material, vertex buffer, primitive buffer, and +// transform and allow the RenderMeshMgr handle the actual rendering. You can +// see this implemented in RenderMeshExample. +//----------------------------------------------------------------------------- + +class RenderObjectExample : public SceneObject +{ + typedef SceneObject Parent; + + // Networking masks + // We need to implement at least one of these to allow + // the client version of the object to receive updates + // from the server version (like if it has been moved + // or edited) + enum MaskBits + { + TransformMask = Parent::NextFreeMask << 0, + NextFreeMask = Parent::NextFreeMask << 1 + }; + + //-------------------------------------------------------------------------- + // Rendering variables + //-------------------------------------------------------------------------- + // Define our vertex format here so we don't have to + // change it in multiple spots later + typedef GFXVertexPCN VertexType; + + // The handles for our StateBlocks + GFXStateBlockRef mNormalSB; + GFXStateBlockRef mReflectSB; + + // The GFX vertex and primitive buffers + GFXVertexBufferHandle< VertexType > mVertexBuffer; + +public: + RenderObjectExample(); + virtual ~RenderObjectExample(); + + // Declare this object as a ConsoleObject so that we can + // instantiate it into the world and network it + DECLARE_CONOBJECT(RenderObjectExample); + + //-------------------------------------------------------------------------- + // Object Editing + // Since there is always a server and a client object in Torque and we + // actually edit the server object we need to implement some basic + // networking functions + //-------------------------------------------------------------------------- + // Set up any fields that we want to be editable (like position) + static void initPersistFields(); + + // Handle when we are added to the scene and removed from the scene + bool onAdd(); + void onRemove(); + + // Override this so that we can dirty the network flag when it is called + void setTransform( const MatrixF &mat ); + + // This function handles sending the relevant data from the server + // object to the client object + U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream ); + // This function handles receiving relevant data from the server + // object and applying it to the client object + void unpackUpdate( NetConnection *conn, BitStream *stream ); + + //-------------------------------------------------------------------------- + // Object Rendering + // Torque utilizes a "batch" rendering system. This means that it builds a + // list of objects that need to render (via RenderInst's) and then renders + // them all in one batch. This allows it to optimized on things like + // minimizing texture, state, and shader switching by grouping objects that + // use the same Materials. For this example, however, we are just going to + // get this object added to the list of objects that handle their own + // rendering. + //-------------------------------------------------------------------------- + // Create the geometry for rendering + void createGeometry(); + + // This is the function that allows this object to submit itself for rendering + void prepRenderImage( SceneRenderState *state ); + + // This is the function that actually gets called to do the rendering + // Note that there is no longer a predefined name for this function. + // Instead, when we submit our ObjectRenderInst in prepRenderImage(), + // we bind this function as our rendering delegate function + void render( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); +}; + +#endif // _RENDEROBJECTEXAMPLE_H_ \ No newline at end of file diff --git a/Engine/source/T3D/examples/renderShapeExample.cpp b/Engine/source/T3D/examples/renderShapeExample.cpp new file mode 100644 index 000000000..0e60c3801 --- /dev/null +++ b/Engine/source/T3D/examples/renderShapeExample.cpp @@ -0,0 +1,273 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "T3D/examples/renderShapeExample.h" + +#include "math/mathIO.h" +#include "sim/netConnection.h" +#include "scene/sceneRenderState.h" +#include "console/consoleTypes.h" +#include "core/resourceManager.h" +#include "core/stream/bitStream.h" +#include "gfx/gfxTransformSaver.h" +#include "renderInstance/renderPassManager.h" +#include "lighting/lightQuery.h" + + +IMPLEMENT_CO_NETOBJECT_V1(RenderShapeExample); + +ConsoleDocClass( RenderShapeExample, + "@brief An example scene object which renders a DTS.\n\n" + "This class implements a basic SceneObject that can exist in the world at a " + "3D position and render itself. There are several valid ways to render an " + "object in Torque. This class makes use of the 'TS' (three space) shape " + "system. TS manages loading the various mesh formats supported by Torque as " + "well was rendering those meshes (including LOD and animation...though this " + "example doesn't include any animation over time).\n\n" + "See the C++ code for implementation details.\n\n" + "@ingroup Examples\n" ); + +//----------------------------------------------------------------------------- +// Object setup and teardown +//----------------------------------------------------------------------------- +RenderShapeExample::RenderShapeExample() +{ + // Flag this object so that it will always + // be sent across the network to clients + mNetFlags.set( Ghostable | ScopeAlways ); + + // Set it as a "static" object. + mTypeMask |= StaticObjectType | StaticShapeObjectType; + + // Make sure to initialize our TSShapeInstance to NULL + mShapeInstance = NULL; +} + +RenderShapeExample::~RenderShapeExample() +{ +} + +//----------------------------------------------------------------------------- +// Object Editing +//----------------------------------------------------------------------------- +void RenderShapeExample::initPersistFields() +{ + addGroup( "Rendering" ); + addField( "shapeFile", TypeStringFilename, Offset( mShapeFile, RenderShapeExample ), + "The path to the DTS shape file." ); + endGroup( "Rendering" ); + + // SceneObject already handles exposing the transform + Parent::initPersistFields(); +} + +void RenderShapeExample::inspectPostApply() +{ + Parent::inspectPostApply(); + + // Flag the network mask to send the updates + // to the client object + setMaskBits( UpdateMask ); +} + +bool RenderShapeExample::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + // Set up a 1x1x1 bounding box + mObjBox.set( Point3F( -0.5f, -0.5f, -0.5f ), + Point3F( 0.5f, 0.5f, 0.5f ) ); + + resetWorldBox(); + + // Add this object to the scene + addToScene(); + + // Setup the shape. + createShape(); + + return true; +} + +void RenderShapeExample::onRemove() +{ + // Remove this object from the scene + removeFromScene(); + + // Remove our TSShapeInstance + if ( mShapeInstance ) + SAFE_DELETE( mShapeInstance ); + + Parent::onRemove(); +} + +void RenderShapeExample::setTransform(const MatrixF & mat) +{ + // Let SceneObject handle all of the matrix manipulation + Parent::setTransform( mat ); + + // Dirty our network mask so that the new transform gets + // transmitted to the client object + setMaskBits( TransformMask ); +} + +U32 RenderShapeExample::packUpdate( NetConnection *conn, U32 mask, BitStream *stream ) +{ + // Allow the Parent to get a crack at writing its info + U32 retMask = Parent::packUpdate( conn, mask, stream ); + + // Write our transform information + if ( stream->writeFlag( mask & TransformMask ) ) + { + mathWrite(*stream, getTransform()); + mathWrite(*stream, getScale()); + } + + // Write out any of the updated editable properties + if ( stream->writeFlag( mask & UpdateMask ) ) + { + stream->write( mShapeFile ); + + // Allow the server object a chance to handle a new shape + createShape(); + } + + return retMask; +} + +void RenderShapeExample::unpackUpdate(NetConnection *conn, BitStream *stream) +{ + // Let the Parent read any info it sent + Parent::unpackUpdate(conn, stream); + + if ( stream->readFlag() ) // TransformMask + { + mathRead(*stream, &mObjToWorld); + mathRead(*stream, &mObjScale); + + setTransform( mObjToWorld ); + } + + if ( stream->readFlag() ) // UpdateMask + { + stream->read( &mShapeFile ); + + if ( isProperlyAdded() ) + createShape(); + } +} + +//----------------------------------------------------------------------------- +// Object Rendering +//----------------------------------------------------------------------------- +void RenderShapeExample::createShape() +{ + if ( mShapeFile.isEmpty() ) + return; + + // If this is the same shape then no reason to update it + if ( mShapeInstance && mShapeFile.equal( mShape.getPath().getFullPath(), String::NoCase ) ) + return; + + // Clean up our previous shape + if ( mShapeInstance ) + SAFE_DELETE( mShapeInstance ); + mShape = NULL; + + // Attempt to get the resource from the ResourceManager + mShape = ResourceManager::get().load( mShapeFile ); + + if ( !mShape ) + { + Con::errorf( "RenderShapeExample::createShape() - Unable to load shape: %s", mShapeFile.c_str() ); + return; + } + + // Attempt to preload the Materials for this shape + if ( isClientObject() && + !mShape->preloadMaterialList( mShape.getPath() ) && + NetConnection::filesWereDownloaded() ) + { + mShape = NULL; + return; + } + + // Update the bounding box + mObjBox = mShape->bounds; + resetWorldBox(); + setRenderTransform(mObjToWorld); + + // Create the TSShapeInstance + mShapeInstance = new TSShapeInstance( mShape, isClientObject() ); +} + +void RenderShapeExample::prepRenderImage( SceneRenderState *state ) +{ + // Make sure we have a TSShapeInstance + if ( !mShapeInstance ) + return; + + // Calculate the distance of this object from the camera + Point3F cameraOffset; + getRenderTransform().getColumn( 3, &cameraOffset ); + cameraOffset -= state->getDiffuseCameraPosition(); + F32 dist = cameraOffset.len(); + if ( dist < 0.01f ) + dist = 0.01f; + + // Set up the LOD for the shape + F32 invScale = ( 1.0f / getMax( getMax( mObjScale.x, mObjScale.y ), mObjScale.z ) ); + + mShapeInstance->setDetailFromDistance( state, dist * invScale ); + + // Make sure we have a valid level of detail + if ( mShapeInstance->getCurrentDetail() < 0 ) + return; + + // GFXTransformSaver is a handy helper class that restores + // the current GFX matrices to their original values when + // it goes out of scope at the end of the function + GFXTransformSaver saver; + + // Set up our TS render state + TSRenderState rdata; + rdata.setSceneState( state ); + rdata.setFadeOverride( 1.0f ); + + // We might have some forward lit materials + // so pass down a query to gather lights. + LightQuery query; + query.init( getWorldSphere() ); + rdata.setLightQuery( &query ); + + // Set the world matrix to the objects render transform + MatrixF mat = getRenderTransform(); + mat.scale( mObjScale ); + GFX->setWorldMatrix( mat ); + + // Animate the the shape + mShapeInstance->animate(); + + // Allow the shape to submit the RenderInst(s) for itself + mShapeInstance->render( rdata ); +} \ No newline at end of file diff --git a/Engine/source/T3D/examples/renderShapeExample.h b/Engine/source/T3D/examples/renderShapeExample.h new file mode 100644 index 000000000..255d39045 --- /dev/null +++ b/Engine/source/T3D/examples/renderShapeExample.h @@ -0,0 +1,119 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _RENDERSHAPEEXAMPLE_H_ +#define _RENDERSHAPEEXAMPLE_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _TSSHAPEINSTANCE_H_ +#include "ts/tsShapeInstance.h" +#endif + +//----------------------------------------------------------------------------- +// This class implements a basic SceneObject that can exist in the world at a +// 3D position and render itself. There are several valid ways to render an +// object in Torque. This class makes use of the "TS" (three space) shape +// system. TS manages loading the various mesh formats supported by Torque as +// well was rendering those meshes (including LOD and animation...though this +// example doesn't include any animation over time). +//----------------------------------------------------------------------------- + +class RenderShapeExample : public SceneObject +{ + typedef SceneObject Parent; + + // Networking masks + // We need to implement a mask specifically to handle + // updating our transform from the server object to its + // client-side "ghost". We also need to implement a + // maks for handling editor updates to our properties + // (like material). + enum MaskBits + { + TransformMask = Parent::NextFreeMask << 0, + UpdateMask = Parent::NextFreeMask << 1, + NextFreeMask = Parent::NextFreeMask << 2 + }; + + //-------------------------------------------------------------------------- + // Rendering variables + //-------------------------------------------------------------------------- + // The name of the shape file we will use for rendering + String mShapeFile; + // The actual shape instance + TSShapeInstance* mShapeInstance; + // Store the resource so we can access the filename later + Resource mShape; + +public: + RenderShapeExample(); + virtual ~RenderShapeExample(); + + // Declare this object as a ConsoleObject so that we can + // instantiate it into the world and network it + DECLARE_CONOBJECT(RenderShapeExample); + + //-------------------------------------------------------------------------- + // Object Editing + // Since there is always a server and a client object in Torque and we + // actually edit the server object we need to implement some basic + // networking functions + //-------------------------------------------------------------------------- + // Set up any fields that we want to be editable (like position) + static void initPersistFields(); + + // Allows the object to update its editable settings + // from the server object to the client + virtual void inspectPostApply(); + + // Handle when we are added to the scene and removed from the scene + bool onAdd(); + void onRemove(); + + // Override this so that we can dirty the network flag when it is called + void setTransform( const MatrixF &mat ); + + // This function handles sending the relevant data from the server + // object to the client object + U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream ); + // This function handles receiving relevant data from the server + // object and applying it to the client object + void unpackUpdate( NetConnection *conn, BitStream *stream ); + + //-------------------------------------------------------------------------- + // Object Rendering + // Torque utilizes a "batch" rendering system. This means that it builds a + // list of objects that need to render (via RenderInst's) and then renders + // them all in one batch. This allows it to optimized on things like + // minimizing texture, state, and shader switching by grouping objects that + // use the same Materials. + //-------------------------------------------------------------------------- + // Create the geometry for rendering + void createShape(); + + // This is the function that allows this object to submit itself for rendering + void prepRenderImage( SceneRenderState *state ); +}; + +#endif // _RENDERSHAPEEXAMPLE_H_ \ No newline at end of file diff --git a/Engine/source/T3D/fps/guiClockHud.cpp b/Engine/source/T3D/fps/guiClockHud.cpp new file mode 100644 index 000000000..334b272ea --- /dev/null +++ b/Engine/source/T3D/fps/guiClockHud.cpp @@ -0,0 +1,197 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "gui/core/guiControl.h" +#include "console/consoleTypes.h" +#include "T3D/shapeBase.h" +#include "gfx/gfxDrawUtil.h" +#include "console/engineAPI.h" + +//----------------------------------------------------------------------------- + +/// Vary basic HUD clock. +/// Displays the current simulation time offset from some base. The base time +/// is usually synchronized with the server as mission start time. This hud +/// currently only displays minutes:seconds. +class GuiClockHud : public GuiControl +{ + typedef GuiControl Parent; + + bool mShowFrame; + bool mShowFill; + bool mTimeReversed; + + ColorF mFillColor; + ColorF mFrameColor; + ColorF mTextColor; + + S32 mTimeOffset; + +public: + GuiClockHud(); + + void setTime(F32 newTime); + void setReverseTime(F32 reverseTime); + F32 getTime(); + + void onRender( Point2I, const RectI &); + static void initPersistFields(); + DECLARE_CONOBJECT( GuiClockHud ); + DECLARE_CATEGORY( "Gui Game" ); + DECLARE_DESCRIPTION( "Basic HUD clock. Displays the current simulation time offset from some base." ); +}; + + +//----------------------------------------------------------------------------- + +IMPLEMENT_CONOBJECT( GuiClockHud ); + +ConsoleDocClass( GuiClockHud, + "@brief Basic HUD clock. Displays the current simulation time offset from some base.\n" + + "@tsexample\n" + "\n new GuiClockHud()" + "{\n" + " fillColor = \"0.0 1.0 0.0 1.0\"; // Fills with a solid green color\n" + " frameColor = \"1.0 1.0 1.0 1.0\"; // Solid white frame color\n" + " textColor = \"1.0 1.0 1.0 1.0\"; // Solid white text Color\n" + " showFill = \"true\";\n" + " showFrame = \"true\";\n" + "};\n" + "@endtsexample\n\n" + + "@ingroup GuiGame\n" +); + +GuiClockHud::GuiClockHud() +{ + mShowFrame = mShowFill = true; + mFillColor.set(0, 0, 0, 0.5); + mFrameColor.set(0, 1, 0, 1); + mTextColor.set( 0, 1, 0, 1 ); + + mTimeOffset = 0; +} + +void GuiClockHud::initPersistFields() +{ + addGroup("Misc"); + addField( "showFill", TypeBool, Offset( mShowFill, GuiClockHud ), "If true, draws a background color behind the control."); + addField( "showFrame", TypeBool, Offset( mShowFrame, GuiClockHud ), "If true, draws a frame around the control." ); + addField( "fillColor", TypeColorF, Offset( mFillColor, GuiClockHud ), "Standard color for the background of the control." ); + addField( "frameColor", TypeColorF, Offset( mFrameColor, GuiClockHud ), "Color for the control's frame." ); + addField( "textColor", TypeColorF, Offset( mTextColor, GuiClockHud ), "Color for the text on this control." ); + endGroup("Misc"); + + Parent::initPersistFields(); +} + + +//----------------------------------------------------------------------------- + +void GuiClockHud::onRender(Point2I offset, const RectI &updateRect) +{ + // Background first + if (mShowFill) + GFX->getDrawUtil()->drawRectFill(updateRect, mFillColor); + + // Convert ms time into hours, minutes and seconds. + S32 time = S32(getTime()); + S32 secs = time % 60; + S32 mins = (time % 3600) / 60; + + // Currently only displays min/sec + char buf[256]; + dSprintf(buf,sizeof(buf), "%02d:%02d",mins,secs); + + // Center the text + offset.x += (getWidth() - mProfile->mFont->getStrWidth((const UTF8 *)buf)) / 2; + offset.y += (getHeight() - mProfile->mFont->getHeight()) / 2; + GFX->getDrawUtil()->setBitmapModulation(mTextColor); + GFX->getDrawUtil()->drawText(mProfile->mFont, offset, buf); + GFX->getDrawUtil()->clearBitmapModulation(); + + // Border last + if (mShowFrame) + GFX->getDrawUtil()->drawRect(updateRect, mFrameColor); +} + + +//----------------------------------------------------------------------------- + +void GuiClockHud::setReverseTime(F32 time) +{ + // Set the current time in seconds. + mTimeReversed = true; + mTimeOffset = S32(time * 1000) + Platform::getVirtualMilliseconds(); +} + +void GuiClockHud::setTime(F32 time) +{ + // Set the current time in seconds. + mTimeReversed = false; + mTimeOffset = S32(time * 1000) - Platform::getVirtualMilliseconds(); +} + +F32 GuiClockHud::getTime() +{ + // Return elapsed time in seconds. + if(mTimeReversed) + return F32(mTimeOffset - Platform::getVirtualMilliseconds()) / 1000; + else + return F32(mTimeOffset + Platform::getVirtualMilliseconds()) / 1000; +} + +DefineEngineMethod(GuiClockHud, setTime, void, (F32 timeInSeconds),(60), "Sets the current base time for the clock.\n" + "@param timeInSeconds Time to set the clock, in seconds (IE: 00:02 would be 120)\n" + "@tsexample\n" + "// Define the time, in seconds\n" + "%timeInSeconds = 120;\n\n" + "// Change the time on the GuiClockHud control\n" + "%guiClockHud.setTime(%timeInSeconds);\n" + "@endtsexample\n" + ) +{ + object->setTime(timeInSeconds); +} + +DefineEngineMethod(GuiClockHud, setReverseTime, void, (F32 timeInSeconds),(60), "@brief Sets a time for a countdown clock.\n\n" + "Setting the time like this will cause the clock to count backwards from the specified time.\n\n" + "@param timeInSeconds Time to set the clock, in seconds (IE: 00:02 would be 120)\n\n" + "@see setTime\n" + ) +{ + object->setReverseTime(timeInSeconds); +} + +DefineEngineMethod(GuiClockHud, getTime, F32, (),, "Returns the current time, in seconds.\n" + "@return timeInseconds Current time, in seconds\n" + "@tsexample\n" + "// Get the current time from the GuiClockHud control\n" + "%timeInSeconds = %guiClockHud.getTime();\n" + "@endtsexample\n" + ) +{ + return object->getTime(); +} diff --git a/Engine/source/T3D/fps/guiCrossHairHud.cpp b/Engine/source/T3D/fps/guiCrossHairHud.cpp new file mode 100644 index 000000000..90fc62bc1 --- /dev/null +++ b/Engine/source/T3D/fps/guiCrossHairHud.cpp @@ -0,0 +1,191 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "gui/core/guiControl.h" +#include "gui/controls/guiBitmapCtrl.h" +#include "console/consoleTypes.h" +#include "scene/sceneManager.h" +#include "T3D/gameBase/gameConnection.h" +#include "T3D/shapeBase.h" +#include "gfx/gfxDrawUtil.h" +#include "console/engineAPI.h" + + +//----------------------------------------------------------------------------- +/// Vary basic cross hair hud. +/// Uses the base bitmap control to render a bitmap, and decides whether +/// to draw or not depending on the current control object and it's state. +/// If there is ShapeBase object under the cross hair and it's named, +/// then a small health bar is displayed. +class GuiCrossHairHud : public GuiBitmapCtrl +{ + typedef GuiBitmapCtrl Parent; + + ColorF mDamageFillColor; + ColorF mDamageFrameColor; + Point2I mDamageRectSize; + Point2I mDamageOffset; + +protected: + void drawDamage(Point2I offset, F32 damage, F32 opacity); + +public: + GuiCrossHairHud(); + + void onRender( Point2I, const RectI &); + static void initPersistFields(); + DECLARE_CONOBJECT( GuiCrossHairHud ); + DECLARE_CATEGORY( "Gui Game" ); + DECLARE_DESCRIPTION( "Basic cross hair hud. Reacts to state of control object.\n" + "Also displays health bar for named objects under the cross hair." ); +}; + +/// Valid object types for which the cross hair will render, this +/// should really all be script controlled. +static const U32 ObjectMask = PlayerObjectType | VehicleObjectType; + + +//----------------------------------------------------------------------------- + +IMPLEMENT_CONOBJECT( GuiCrossHairHud ); + +ConsoleDocClass( GuiCrossHairHud, + "@brief Basic cross hair hud. Reacts to state of control object. Also displays health bar for named objects under the cross hair.\n\n" + "Uses the base bitmap control to render a bitmap, and decides whether to draw or not depending " + "on the current control object and it's state. If there is ShapeBase object under the cross hair " + "and it's named, then a small health bar is displayed.\n\n" + + "@tsexample\n" + "\n new GuiCrossHairHud()" + "{\n" + " damageFillColor = \"1.0 0.0 0.0 1.0\"; // Fills with a solid red color\n" + " damageFrameColor = \"1.0 1.0 1.0 1.0\"; // Solid white frame color\n" + " damageRect = \"15 5\";\n" + " damageOffset = \"0 -10\";\n" + "};\n" + "@endtsexample\n" + + "@ingroup GuiGame\n" +); + +GuiCrossHairHud::GuiCrossHairHud() +{ + mDamageFillColor.set( 0.0f, 1.0f, 0.0f, 1.0f ); + mDamageFrameColor.set( 1.0f, 0.6f, 0.0f, 1.0f ); + mDamageRectSize.set(50, 4); + mDamageOffset.set(0,32); +} + +void GuiCrossHairHud::initPersistFields() +{ + addGroup("Damage"); + addField( "damageFillColor", TypeColorF, Offset( mDamageFillColor, GuiCrossHairHud ), "As the health bar depletes, this color will represent the health loss amount." ); + addField( "damageFrameColor", TypeColorF, Offset( mDamageFrameColor, GuiCrossHairHud ), "Color for the health bar's frame." ); + addField( "damageRect", TypePoint2I, Offset( mDamageRectSize, GuiCrossHairHud ), "Size for the health bar portion of the control." ); + addField( "damageOffset", TypePoint2I, Offset( mDamageOffset, GuiCrossHairHud ), "Offset for drawing the damage portion of the health control." ); + endGroup("Damage"); + Parent::initPersistFields(); +} + + +//----------------------------------------------------------------------------- + +void GuiCrossHairHud::onRender(Point2I offset, const RectI &updateRect) +{ + // Must have a connection and player control object + GameConnection* conn = GameConnection::getConnectionToServer(); + if (!conn) + return; + ShapeBase* control = dynamic_cast(conn->getControlObject()); + if (!control || !(control->getTypeMask() & ObjectMask) || !conn->isFirstPerson()) + return; + + // Parent render. + Parent::onRender(offset,updateRect); + + // Get control camera info + MatrixF cam; + Point3F camPos; + conn->getControlCameraTransform(0,&cam); + cam.getColumn(3, &camPos); + + // Extend the camera vector to create an endpoint for our ray + Point3F endPos; + cam.getColumn(1, &endPos); + endPos *= gClientSceneGraph->getVisibleDistance(); + endPos += camPos; + + // Collision info. We're going to be running LOS tests and we + // don't want to collide with the control object. + static U32 losMask = TerrainObjectType | InteriorObjectType | ShapeBaseObjectType; + control->disableCollision(); + + RayInfo info; + if (gClientContainer.castRay(camPos, endPos, losMask, &info)) { + // Hit something... but we'll only display health for named + // ShapeBase objects. Could mask against the object type here + // and do a static cast if it's a ShapeBaseObjectType, but this + // isn't a performance situation, so I'll just use dynamic_cast. + if (ShapeBase* obj = dynamic_cast(info.object)) + if (obj->getShapeName()) { + offset.x = updateRect.point.x + updateRect.extent.x / 2; + offset.y = updateRect.point.y + updateRect.extent.y / 2; + drawDamage(offset + mDamageOffset, obj->getDamageValue(), 1); + } + } + + // Restore control object collision + control->enableCollision(); +} + + +//----------------------------------------------------------------------------- +/** + Display a damage bar ubove the shape. + This is a support funtion, called by onRender. +*/ +void GuiCrossHairHud::drawDamage(Point2I offset, F32 damage, F32 opacity) +{ + mDamageFillColor.alpha = mDamageFrameColor.alpha = opacity; + + // Damage should be 0->1 (0 being no damage,or healthy), but + // we'll just make sure here as we flip it. + damage = mClampF(1 - damage, 0, 1); + + // Center the bar + RectI rect(offset, mDamageRectSize); + rect.point.x -= mDamageRectSize.x / 2; + + // Draw the border + GFX->getDrawUtil()->drawRect(rect, mDamageFrameColor); + + // Draw the damage % fill + rect.point += Point2I(1, 1); + rect.extent -= Point2I(1, 1); + rect.extent.x = (S32)(rect.extent.x * damage); + if (rect.extent.x == 1) + rect.extent.x = 2; + if (rect.extent.x > 0) + GFX->getDrawUtil()->drawRectFill(rect, mDamageFillColor); +} diff --git a/Engine/source/T3D/fps/guiHealthBarHud.cpp b/Engine/source/T3D/fps/guiHealthBarHud.cpp new file mode 100644 index 000000000..c50712aa0 --- /dev/null +++ b/Engine/source/T3D/fps/guiHealthBarHud.cpp @@ -0,0 +1,190 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "gui/core/guiControl.h" +#include "console/consoleTypes.h" +#include "T3D/gameBase/gameConnection.h" +#include "T3D/shapeBase.h" +#include "gfx/gfxDrawUtil.h" + + +//----------------------------------------------------------------------------- +/// A basic health bar control. +/// This gui displays the damage value of the current PlayerObjectType +/// control object. The gui can be set to pulse if the health value +/// drops below a set value. This control only works if a server +/// connection exists and it's control object is a PlayerObjectType. If +/// either of these requirements is false, the control is not rendered. +class GuiHealthBarHud : public GuiControl +{ + typedef GuiControl Parent; + + bool mShowFrame; + bool mShowFill; + bool mDisplayEnergy; + + ColorF mFillColor; + ColorF mFrameColor; + ColorF mDamageFillColor; + + S32 mPulseRate; + F32 mPulseThreshold; + + F32 mValue; + +public: + GuiHealthBarHud(); + + void onRender( Point2I, const RectI &); + static void initPersistFields(); + DECLARE_CONOBJECT( GuiHealthBarHud ); + DECLARE_CATEGORY( "Gui Game" ); + DECLARE_DESCRIPTION( "A basic health bar. Shows the damage value of the current\n" + "PlayerObjectType control object." ); +}; + + +//----------------------------------------------------------------------------- + +IMPLEMENT_CONOBJECT( GuiHealthBarHud ); + +ConsoleDocClass( GuiHealthBarHud, + "@brief A basic health bar. Shows the damage value of the current PlayerObjectType control object.\n\n" + "This gui displays the damage value of the current PlayerObjectType control object. " + "The gui can be set to pulse if the health value drops below a set value. " + "This control only works if a server connection exists and it's control object " + "is a PlayerObjectType. If either of these requirements is false, the control is not rendered.\n\n" + + "@tsexample\n" + "\n new GuiHealthBarHud()" + "{\n" + " fillColor = \"0.0 1.0 0.0 1.0\"; // Fills with a solid green color\n" + " frameColor = \"1.0 1.0 1.0 1.0\"; // Solid white frame color\n" + " damageFillColor = \"1.0 0.0 0.0 1.0\"; // Fills with a solid red color\n" + " pulseRate = \"500\";\n" + " pulseThreshold = \"0.25\";\n" + " showFill = \"true\";\n" + " showFrame = \"true\";\n" + " displayEnergy = \"false\";\n" + "};\n" + "@endtsexample\n\n" + + "@ingroup GuiGame\n" +); + + +GuiHealthBarHud::GuiHealthBarHud() +{ + mShowFrame = mShowFill = true; + mDisplayEnergy = false; + mFillColor.set(0, 0, 0, 0.5); + mFrameColor.set(0, 1, 0, 1); + mDamageFillColor.set(0, 1, 0, 1); + + mPulseRate = 0; + mPulseThreshold = 0.3f; + mValue = 0.2f; +} + +void GuiHealthBarHud::initPersistFields() +{ + addGroup("Colors"); + addField( "fillColor", TypeColorF, Offset( mFillColor, GuiHealthBarHud ), "Standard color for the background of the control." ); + addField( "frameColor", TypeColorF, Offset( mFrameColor, GuiHealthBarHud ), "Color for the control's frame." ); + addField( "damageFillColor", TypeColorF, Offset( mDamageFillColor, GuiHealthBarHud ), "As the health bar depletes, this color will represent the health loss amount." ); + endGroup("Colors"); + + addGroup("Pulse"); + addField( "pulseRate", TypeS32, Offset( mPulseRate, GuiHealthBarHud ), "Speed at which the control will pulse." ); + addField( "pulseThreshold", TypeF32, Offset( mPulseThreshold, GuiHealthBarHud ), "Health level the control must be under before the control will pulse." ); + endGroup("Pulse"); + + addGroup("Misc"); + addField( "showFill", TypeBool, Offset( mShowFill, GuiHealthBarHud ), "If true, we draw the background color of the control." ); + addField( "showFrame", TypeBool, Offset( mShowFrame, GuiHealthBarHud ), "If true, we draw the frame of the control." ); + addField( "displayEnergy", TypeBool, Offset( mDisplayEnergy, GuiHealthBarHud ), "If true, display the energy value rather than the damage value." ); + endGroup("Misc"); + + Parent::initPersistFields(); +} + + +//----------------------------------------------------------------------------- +/** + Gui onRender method. + Renders a health bar with filled background and border. +*/ +void GuiHealthBarHud::onRender(Point2I offset, const RectI &updateRect) +{ + // Must have a connection and player control object + GameConnection* conn = GameConnection::getConnectionToServer(); + if (!conn) + return; + ShapeBase* control = dynamic_cast(conn->getControlObject()); + if (!control || !(control->getTypeMask() & PlayerObjectType)) + return; + + if(mDisplayEnergy) + { + mValue = control->getEnergyValue(); + } + else + { + // We'll just grab the damage right off the control object. + // Damage value 0 = no damage. + mValue = 1 - control->getDamageValue(); + } + + + // Background first + if (mShowFill) + GFX->getDrawUtil()->drawRectFill(updateRect, mFillColor); + + // Pulse the damage fill if it's below the threshold + if (mPulseRate != 0) + if (mValue < mPulseThreshold) + { + U32 time = Platform::getVirtualMilliseconds(); + F32 alpha = 2.0f * F32(time % mPulseRate) / F32(mPulseRate); + mDamageFillColor.alpha = (alpha > 1.0f)? 2.0f - alpha: alpha; + } + else + mDamageFillColor.alpha = 1; + + // Render damage fill % + RectI rect(updateRect); + if(getWidth() > getHeight()) + rect.extent.x = (S32)(rect.extent.x * mValue); + else + { + S32 bottomY = rect.point.y + rect.extent.y; + rect.extent.y = (S32)(rect.extent.y * mValue); + rect.point.y = bottomY - rect.extent.y; + } + GFX->getDrawUtil()->drawRectFill(rect, mDamageFillColor); + + // Border last + if (mShowFrame) + GFX->getDrawUtil()->drawRect(updateRect, mFrameColor); +} diff --git a/Engine/source/T3D/fps/guiShapeNameHud.cpp b/Engine/source/T3D/fps/guiShapeNameHud.cpp new file mode 100644 index 000000000..ddfcdcdf2 --- /dev/null +++ b/Engine/source/T3D/fps/guiShapeNameHud.cpp @@ -0,0 +1,287 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "gui/core/guiControl.h" +#include "gui/3d/guiTSControl.h" +#include "console/consoleTypes.h" +#include "scene/sceneManager.h" +#include "T3D/gameBase/gameConnection.h" +#include "T3D/shapeBase.h" +#include "gfx/gfxDrawUtil.h" +#include "console/engineAPI.h" + + +//---------------------------------------------------------------------------- +/// Displays name & damage above shape objects. +/// +/// This control displays the name and damage value of all named +/// ShapeBase objects on the client. The name and damage of objects +/// within the control's display area are overlayed above the object. +/// +/// This GUI control must be a child of a TSControl, and a server connection +/// and control object must be present. +/// +/// This is a stand-alone control and relies only on the standard base GuiControl. +class GuiShapeNameHud : public GuiControl { + typedef GuiControl Parent; + + // field data + ColorF mFillColor; + ColorF mFrameColor; + ColorF mTextColor; + + F32 mVerticalOffset; + F32 mDistanceFade; + bool mShowFrame; + bool mShowFill; + +protected: + void drawName( Point2I offset, const char *buf, F32 opacity); + +public: + GuiShapeNameHud(); + + // GuiControl + virtual void onRender(Point2I offset, const RectI &updateRect); + + static void initPersistFields(); + DECLARE_CONOBJECT( GuiShapeNameHud ); + DECLARE_CATEGORY( "Gui Game" ); + DECLARE_DESCRIPTION( "Displays name and damage of ShapeBase objects in its bounds.\n" + "Must be a child of a GuiTSCtrl and a server connection must be present." ); +}; + + +//----------------------------------------------------------------------------- + +IMPLEMENT_CONOBJECT(GuiShapeNameHud); + +ConsoleDocClass( GuiShapeNameHud, + "@brief Displays name and damage of ShapeBase objects in its bounds. Must be a child of a GuiTSCtrl and a server connection must be present.\n\n" + "This control displays the name and damage value of all named ShapeBase objects on the client. " + "The name and damage of objects within the control's display area are overlayed above the object.\n\n" + "This GUI control must be a child of a TSControl, and a server connection and control object must be present. " + "This is a stand-alone control and relies only on the standard base GuiControl.\n\n" + + "@tsexample\n" + "\n new GuiShapeNameHud()" + "{\n" + " fillColor = \"0.0 1.0 0.0 1.0\"; // Fills with a solid green color\n" + " frameColor = \"1.0 1.0 1.0 1.0\"; // Solid white frame color\n" + " textColor = \"1.0 1.0 1.0 1.0\"; // Solid white text Color\n" + " showFill = \"true\";\n" + " showFrame = \"true\";\n" + " verticalOffset = \"0.15\";\n" + " distanceFade = \"15.0\";\n" + "};\n" + "@endtsexample\n\n" + + "@ingroup GuiGame\n" +); + +/// Default distance for object's information to be displayed. +static const F32 cDefaultVisibleDistance = 500.0f; + +GuiShapeNameHud::GuiShapeNameHud() +{ + mFillColor.set( 0.25f, 0.25f, 0.25f, 0.25f ); + mFrameColor.set( 0, 1, 0, 1 ); + mTextColor.set( 0, 1, 0, 1 ); + mShowFrame = mShowFill = true; + mVerticalOffset = 0.5f; + mDistanceFade = 0.1f; +} + +void GuiShapeNameHud::initPersistFields() +{ + addGroup("Colors"); + addField( "fillColor", TypeColorF, Offset( mFillColor, GuiShapeNameHud ), "Standard color for the background of the control." ); + addField( "frameColor", TypeColorF, Offset( mFrameColor, GuiShapeNameHud ), "Color for the control's frame." ); + addField( "textColor", TypeColorF, Offset( mTextColor, GuiShapeNameHud ), "Color for the text on this control." ); + endGroup("Colors"); + + addGroup("Misc"); + addField( "showFill", TypeBool, Offset( mShowFill, GuiShapeNameHud ), "If true, we draw the background color of the control." ); + addField( "showFrame", TypeBool, Offset( mShowFrame, GuiShapeNameHud ), "If true, we draw the frame of the control." ); + addField( "verticalOffset", TypeF32, Offset( mVerticalOffset, GuiShapeNameHud ), "Amount to vertically offset the control in relation to the ShapeBase object in focus." ); + addField( "distanceFade", TypeF32, Offset( mDistanceFade, GuiShapeNameHud ), "Visibility distance (how far the player must be from the ShapeBase object in focus) for this control to render." ); + endGroup("Misc"); + Parent::initPersistFields(); +} + + +//---------------------------------------------------------------------------- +/// Core rendering method for this control. +/// +/// This method scans through all the current client ShapeBase objects. +/// If one is named, it displays the name and damage information for it. +/// +/// Information is offset from the center of the object's bounding box, +/// unless the object is a PlayerObjectType, in which case the eye point +/// is used. +/// +/// @param updateRect Extents of control. +void GuiShapeNameHud::onRender( Point2I, const RectI &updateRect) +{ + // Background fill first + if (mShowFill) + GFX->getDrawUtil()->drawRectFill(updateRect, mFillColor); + + // Must be in a TS Control + GuiTSCtrl *parent = dynamic_cast(getParent()); + if (!parent) return; + + // Must have a connection and control object + GameConnection* conn = GameConnection::getConnectionToServer(); + if (!conn) return; + GameBase * control = dynamic_cast(conn->getControlObject()); + if (!control) return; + + // Get control camera info + MatrixF cam; + Point3F camPos; + VectorF camDir; + conn->getControlCameraTransform(0,&cam); + cam.getColumn(3, &camPos); + cam.getColumn(1, &camDir); + + F32 camFov; + conn->getControlCameraFov(&camFov); + camFov = mDegToRad(camFov) / 2; + + // Visible distance info & name fading + F32 visDistance = gClientSceneGraph->getVisibleDistance(); + F32 visDistanceSqr = visDistance * visDistance; + F32 fadeDistance = visDistance * mDistanceFade; + + // Collision info. We're going to be running LOS tests and we + // don't want to collide with the control object. + static U32 losMask = TerrainObjectType | InteriorObjectType | ShapeBaseObjectType; + control->disableCollision(); + + // All ghosted objects are added to the server connection group, + // so we can find all the shape base objects by iterating through + // our current connection. + for (SimSetIterator itr(conn); *itr; ++itr) { + ShapeBase* shape = dynamic_cast< ShapeBase* >(*itr); + if ( shape ) { + if (shape != control && shape->getShapeName()) + { + + // Target pos to test, if it's a player run the LOS to his eye + // point, otherwise we'll grab the generic box center. + Point3F shapePos; + if (shape->getTypeMask() & PlayerObjectType) + { + MatrixF eye; + + // Use the render eye transform, otherwise we'll see jittering + shape->getRenderEyeTransform(&eye); + eye.getColumn(3, &shapePos); + } + else + { + // Use the render transform instead of the box center + // otherwise it'll jitter. + MatrixF srtMat = shape->getRenderTransform(); + srtMat.getColumn(3, &shapePos); + } + VectorF shapeDir = shapePos - camPos; + + // Test to see if it's in range + F32 shapeDist = shapeDir.lenSquared(); + if (shapeDist == 0 || shapeDist > visDistanceSqr) + continue; + shapeDist = mSqrt(shapeDist); + + // Test to see if it's within our viewcone, this test doesn't + // actually match the viewport very well, should consider + // projection and box test. + shapeDir.normalize(); + F32 dot = mDot(shapeDir, camDir); + if (dot < camFov) + continue; + + // Test to see if it's behind something, and we want to + // ignore anything it's mounted on when we run the LOS. + RayInfo info; + shape->disableCollision(); + SceneObject *mount = shape->getObjectMount(); + if (mount) + mount->disableCollision(); + bool los = !gClientContainer.castRay(camPos, shapePos,losMask, &info); + shape->enableCollision(); + if (mount) + mount->enableCollision(); + if (!los) + continue; + + // Project the shape pos into screen space and calculate + // the distance opacity used to fade the labels into the + // distance. + Point3F projPnt; + shapePos.z += mVerticalOffset; + if (!parent->project(shapePos, &projPnt)) + continue; + F32 opacity = (shapeDist < fadeDistance)? 1.0: + 1.0 - (shapeDist - fadeDistance) / (visDistance - fadeDistance); + + // Render the shape's name + drawName(Point2I((S32)projPnt.x, (S32)projPnt.y),shape->getShapeName(),opacity); + } + } + } + + // Restore control object collision + control->enableCollision(); + + // Border last + if (mShowFrame) + GFX->getDrawUtil()->drawRect(updateRect, mFrameColor); +} + + +//---------------------------------------------------------------------------- +/// Render object names. +/// +/// Helper function for GuiShapeNameHud::onRender +/// +/// @param offset Screen coordinates to render name label. (Text is centered +/// horizontally about this location, with bottom of text at +/// specified y position.) +/// @param name String name to display. +/// @param opacity Opacity of name (a fraction). +void GuiShapeNameHud::drawName(Point2I offset, const char *name, F32 opacity) +{ + // Center the name + offset.x -= mProfile->mFont->getStrWidth((const UTF8 *)name) / 2; + offset.y -= mProfile->mFont->getHeight(); + + // Deal with opacity and draw. + mTextColor.alpha = opacity; + GFX->getDrawUtil()->setBitmapModulation(mTextColor); + GFX->getDrawUtil()->drawText(mProfile->mFont, offset, name); + GFX->getDrawUtil()->clearBitmapModulation(); +} + diff --git a/Engine/source/T3D/fx/cameraFXMgr.cpp b/Engine/source/T3D/fx/cameraFXMgr.cpp new file mode 100644 index 000000000..7623cc218 --- /dev/null +++ b/Engine/source/T3D/fx/cameraFXMgr.cpp @@ -0,0 +1,208 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/fx/cameraFXMgr.h" + +#include "platform/profiler.h" +#include "math/mRandom.h" +#include "math/mMatrix.h" + +// global cam fx +CameraFXManager gCamFXMgr; + + +//************************************************************************** +// Camera effect +//************************************************************************** +CameraFX::CameraFX() +{ + mElapsedTime = 0.0; + mDuration = 1.0; +} + +//-------------------------------------------------------------------------- +// Update +//-------------------------------------------------------------------------- +void CameraFX::update( F32 dt ) +{ + mElapsedTime += dt; +} + + + + + +//************************************************************************** +// Camera shake effect +//************************************************************************** +CameraShake::CameraShake() +{ + mFreq.zero(); + mAmp.zero(); + mStartAmp.zero(); + mTimeOffset.zero(); + mCamFXTrans.identity(); + mFalloff = 10.0; + remoteControlled = false; + isAdded = false; +} + +bool CameraShake::isExpired() +{ + if ( remoteControlled ) + return false; + + return Parent::isExpired(); +} + +//-------------------------------------------------------------------------- +// Update +//-------------------------------------------------------------------------- +void CameraShake::update( F32 dt ) +{ + Parent::update( dt ); + + if ( !remoteControlled ) + fadeAmplitude(); + else + mAmp = mStartAmp; + + VectorF camOffset; + camOffset.x = mAmp.x * sin( M_2PI * (mTimeOffset.x + mElapsedTime) * mFreq.x ); + camOffset.y = mAmp.y * sin( M_2PI * (mTimeOffset.y + mElapsedTime) * mFreq.y ); + camOffset.z = mAmp.z * sin( M_2PI * (mTimeOffset.z + mElapsedTime) * mFreq.z ); + + VectorF rotAngles; + rotAngles.x = camOffset.x * 10.0 * M_PI/180.0; + rotAngles.y = camOffset.y * 10.0 * M_PI/180.0; + rotAngles.z = camOffset.z * 10.0 * M_PI/180.0; + MatrixF rotMatrix( EulerF( rotAngles.x, rotAngles.y, rotAngles.z ) ); + + mCamFXTrans = rotMatrix; + mCamFXTrans.setPosition( camOffset ); +} + +//-------------------------------------------------------------------------- +// Fade out the amplitude over time +//-------------------------------------------------------------------------- +void CameraShake::fadeAmplitude() +{ + F32 percentDone = (mElapsedTime / mDuration); + if( percentDone > 1.0 ) percentDone = 1.0; + + F32 time = 1 + percentDone * mFalloff; + time = 1 / (time * time); + + mAmp = mStartAmp * time; +} + +//-------------------------------------------------------------------------- +// Initialize +//-------------------------------------------------------------------------- +void CameraShake::init() +{ + mTimeOffset.x = 0.0; + mTimeOffset.y = gRandGen.randF(); + mTimeOffset.z = gRandGen.randF(); +} + +//************************************************************************** +// CameraFXManager +//************************************************************************** +CameraFXManager::CameraFXManager() +{ + mCamFXTrans.identity(); +} + +//-------------------------------------------------------------------------- +// Destructor +//-------------------------------------------------------------------------- +CameraFXManager::~CameraFXManager() +{ + clear(); +} + +//-------------------------------------------------------------------------- +// Add new effect to currently running list +//-------------------------------------------------------------------------- +void CameraFXManager::addFX( CameraFX *newFX ) +{ + mFXList.pushFront( newFX ); +} + +void CameraFXManager::removeFX( CameraFX *fx ) +{ + CamFXList::Iterator itr = mFXList.begin(); + for ( ; itr != mFXList.end(); itr++ ) + { + if ( *itr == fx ) + { + mFXList.erase( itr ); + return; + } + } + + return; +} + +//-------------------------------------------------------------------------- +// Clear all currently running camera effects +//-------------------------------------------------------------------------- +void CameraFXManager::clear() +{ + for(CamFXList::Iterator i = mFXList.begin(); i != mFXList.end(); ++i) + { + delete *i; + } + + mFXList.clear(); +} + +//-------------------------------------------------------------------------- +// Update camera effects +//-------------------------------------------------------------------------- +void CameraFXManager::update( F32 dt ) +{ + PROFILE_SCOPE( CameraFXManager_update ); + + mCamFXTrans.identity(); + + CamFXList::Iterator cur; + for(CamFXList::Iterator i = mFXList.begin(); i != mFXList.end(); /*Trickiness*/) + { + // Store previous iterator and increment while iterator is still valid. + cur = i; + ++i; + CameraFX * curFX = *cur; + curFX->update( dt ); + MatrixF fxTrans = curFX->getTrans(); + + mCamFXTrans.mul( fxTrans ); + + if( curFX->isExpired() ) + { + delete curFX; + mFXList.erase( cur ); + } + } +} diff --git a/Engine/source/T3D/fx/cameraFXMgr.h b/Engine/source/T3D/fx/cameraFXMgr.h new file mode 100644 index 000000000..5a560bfc6 --- /dev/null +++ b/Engine/source/T3D/fx/cameraFXMgr.h @@ -0,0 +1,113 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _CAMERAFXMGR_H_ +#define _CAMERAFXMGR_H_ + +#ifndef _TORQUE_LIST_ +#include "core/util/tList.h" +#endif +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif + +//************************************************************************** +// Abstract camera effect template +//************************************************************************** +class CameraFX +{ +protected: + MatrixF mCamFXTrans; + F32 mElapsedTime; + F32 mDuration; + +public: + CameraFX(); + + MatrixF & getTrans(){ return mCamFXTrans; } + virtual bool isExpired(){ return mElapsedTime >= mDuration; } + void setDuration( F32 duration ){ mDuration = duration; } + + virtual void update( F32 dt ); +}; + +//-------------------------------------------------------------------------- +// Camera shake effect +//-------------------------------------------------------------------------- +class CameraShake : public CameraFX +{ + typedef CameraFX Parent; + + VectorF mFreq; // these are vectors to represent these values in 3D + VectorF mStartAmp; + VectorF mAmp; + VectorF mTimeOffset; + F32 mFalloff; + +public: + + /// Is controlled by someone else, ignore duration and do not delete. + bool remoteControlled; + bool isAdded; + + CameraShake(); + + void init(); + void fadeAmplitude(); + void setFalloff( F32 falloff ){ mFalloff = falloff; } + void setFrequency( VectorF &freq ){ mFreq = freq; } + void setAmplitude( VectorF & ){ mStartAmp = amp; } + bool isExpired(); + + virtual void update( F32 dt ); +}; + + +//************************************************************************** +// CameraFXManager +//************************************************************************** +class CameraFXManager +{ + typedef CameraFX * CameraFXPtr; + + MatrixF mCamFXTrans; + typedef Torque::List CamFXList; + CamFXList mFXList; + +public: + void addFX( CameraFX *newFX ); + void removeFX( CameraFX *fx ); + void clear(); + MatrixF & getTrans(){ return mCamFXTrans; } + void update( F32 dt ); + + CameraFXManager(); + ~CameraFXManager(); +}; + +extern CameraFXManager gCamFXMgr; + + +#endif diff --git a/Engine/source/T3D/fx/explosion.cpp b/Engine/source/T3D/fx/explosion.cpp new file mode 100644 index 000000000..58b57bf15 --- /dev/null +++ b/Engine/source/T3D/fx/explosion.cpp @@ -0,0 +1,1269 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/fx/explosion.h" + +#include "core/resourceManager.h" +#include "console/consoleTypes.h" +#include "console/typeValidators.h" +#include "sfx/sfxSystem.h" +#include "sfx/sfxTrack.h" +#include "sfx/sfxTypes.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "lighting/lightInfo.h" +#include "lighting/lightManager.h" +#include "core/stream/bitStream.h" +#include "sim/netConnection.h" +#include "ts/tsShape.h" +#include "ts/tsShapeInstance.h" +#include "math/mRandom.h" +#include "math/mathIO.h" +#include "math/mathUtils.h" +#include "T3D/debris.h" +#include "T3D/gameBase/gameConnection.h" +#include "T3D/fx/particleEmitter.h" +#include "T3D/fx/cameraFXMgr.h" +#include "T3D/debris.h" +#include "T3D/shapeBase.h" +#include "T3D/gameBase/gameProcess.h" +#include "renderInstance/renderPassManager.h" +#include "console/engineAPI.h" + +IMPLEMENT_CONOBJECT(Explosion); + +ConsoleDocClass( Explosion, + "@brief The emitter for an explosion effect, with properties defined by a " + "ExplosionData object.\n\n" + "@ingroup FX\n" + "The object will initiate the explosion effects automatically after being " + "added to the simulation.\n" + "@tsexample\n" + "datablock ExplosionData( GrenadeSubExplosion )\n" + "{\n" + " offset = 0.25;\n" + " emitter[0] = GrenadeExpSparkEmitter;\n\n" + " lightStartRadius = 4.0;\n" + " lightEndRadius = 0.0;\n" + " lightStartColor = \"0.9 0.7 0.7\";\n" + " lightEndColor = \"0.9 0.7 0.7\";\n" + " lightStartBrightness = 2.0;\n" + " lightEndBrightness = 0.0;\n" + "};\n\n" + "datablock ExplosionData( GrenadeLauncherExplosion )\n" + "{\n" + " soundProfile = GrenadeLauncherExplosionSound;\n" + " lifeTimeMS = 400; // Quick flash, short burn, and moderate dispersal\n\n" + " // Volume particles\n" + " particleEmitter = GrenadeExpFireEmitter;\n" + " particleDensity = 75;\n" + " particleRadius = 2.25;\n\n" + " // Point emission\n" + " emitter[0] = GrenadeExpDustEmitter;\n" + " emitter[1] = GrenadeExpSparksEmitter;\n" + " emitter[2] = GrenadeExpSmokeEmitter;\n\n" + " // Sub explosion objects\n" + " subExplosion[0] = GrenadeSubExplosion;\n\n" + " // Camera Shaking\n" + " shakeCamera = true;\n" + " camShakeFreq = \"10.0 11.0 9.0\";\n" + " camShakeAmp = \"15.0 15.0 15.0\";\n" + " camShakeDuration = 1.5;\n" + " camShakeRadius = 20;\n\n" + " // Exploding debris\n" + " debris = GrenadeDebris;\n" + " debrisThetaMin = 10;\n" + " debrisThetaMax = 60;\n" + " debrisNum = 4;\n" + " debrisNumVariance = 2;\n" + " debrisVelocity = 25;\n" + " debrisVelocityVariance = 5;\n\n" + " lightStartRadius = 4.0;\n" + " lightEndRadius = 0.0;\n" + " lightStartColor = \"1.0 1.0 1.0\";\n" + " lightEndColor = \"1.0 1.0 1.0\";\n" + " lightStartBrightness = 4.0;\n" + " lightEndBrightness = 0.0;\n" + " lightNormalOffset = 2.0;\n" + "};\n\n" + "function createExplosion()\n" + "{\n" + " // Create a new explosion - it will explode automatically\n" + " %pos = \"0 0 100\";\n" + " %obj = new Explosion()\n" + " {\n" + " position = %pos;\n" + " dataBlock = GrenadeLauncherExplosion;\n" + " };\n" + "}\n\n" + "// schedule an explosion\n" + "schedule(1000, 0, createExplosion);\n" + "@endtsexample" +); + +#define MaxLightRadius 20 + +MRandomLCG sgRandom(0xdeadbeef); + + +DefineEngineFunction(calcExplosionCoverage, F32, (Point3F pos, S32 id, U32 covMask),(Point3F(0.0f,0.0f,0.0f), NULL, NULL), + "@brief Calculates how much an explosion effects a specific object.\n\n" + "Use this to determine how much damage to apply to objects based on their " + "distance from the explosion's center point, and whether the explosion is " + "blocked by other objects.\n\n" + "@param pos Center position of the explosion.\n" + "@param id Id of the object of which to check coverage.\n" + "@param covMask Mask of object types that may block the explosion.\n" + "@return Coverage value from 0 (not affected by the explosion) to 1 (fully affected)\n\n" + "@tsexample\n" + "// Get the position of the explosion.\n" + "%position = %explosion.getPosition();\n\n" + "// Set a list of TypeMasks (defined in gameFunctioncs.cpp), seperated by the | character.\n" + "%TypeMasks = $TypeMasks::StaticObjectType | $TypeMasks::ItemObjectType\n\n" + "// Acquire the damage value from 0.0f - 1.0f.\n" + "%coverage = calcExplosionCoverage( %position, %sceneObject, %TypeMasks );\n\n" + "// Apply damage to object\n" + "%sceneObject.applyDamage( %coverage * 20 );\n" + "@endtsexample\n" + "@ingroup FX") +{ + Point3F center; + + SceneObject* sceneObject = NULL; + if (Sim::findObject(id, sceneObject) == false) { + Con::warnf(ConsoleLogEntry::General, "calcExplosionCoverage: couldn't find object: %s", id); + return 1.0f; + } + if (sceneObject->isClientObject() || sceneObject->getContainer() == NULL) { + Con::warnf(ConsoleLogEntry::General, "calcExplosionCoverage: object is on the client, or not in the container system"); + return 1.0f; + } + + sceneObject->getObjBox().getCenter(¢er); + center.convolve(sceneObject->getScale()); + sceneObject->getTransform().mulP(center); + + RayInfo rayInfo; + sceneObject->disableCollision(); + if (sceneObject->getContainer()->castRay(pos, center, covMask, &rayInfo) == true) { + // Try casting up and then out + if (sceneObject->getContainer()->castRay(pos, pos + Point3F(0.0f, 0.0f, 1.0f), covMask, &rayInfo) == false) + { + if (sceneObject->getContainer()->castRay(pos + Point3F(0.0f, 0.0f, 1.0f), center, covMask, &rayInfo) == false) + { + sceneObject->enableCollision(); + return 1.0f; + } + } + + sceneObject->enableCollision(); + return 0.0f; + } else { + sceneObject->enableCollision(); + return 1.0f; + } +} + +//---------------------------------------------------------------------------- +// +IMPLEMENT_CO_DATABLOCK_V1(ExplosionData); + +ConsoleDocClass( ExplosionData, + "@brief Defines the attributes of an Explosion: particleEmitters, debris, " + "lighting and camera shake effects.\n" + "@ingroup FX\n" +); + +ExplosionData::ExplosionData() +{ + dtsFileName = NULL; + particleDensity = 10; + particleRadius = 1.0f; + + faceViewer = false; + + soundProfile = NULL; + particleEmitter = NULL; + particleEmitterId = 0; + + explosionScale.set(1.0f, 1.0f, 1.0f); + playSpeed = 1.0f; + + dMemset( emitterList, 0, sizeof( emitterList ) ); + dMemset( emitterIDList, 0, sizeof( emitterIDList ) ); + dMemset( debrisList, 0, sizeof( debrisList ) ); + dMemset( debrisIDList, 0, sizeof( debrisIDList ) ); + + debrisThetaMin = 0.0f; + debrisThetaMax = 90.0f; + debrisPhiMin = 0.0f; + debrisPhiMax = 360.0f; + debrisNum = 1; + debrisNumVariance = 0; + debrisVelocity = 2.0f; + debrisVelocityVariance = 0.0f; + + dMemset( explosionList, 0, sizeof( explosionList ) ); + dMemset( explosionIDList, 0, sizeof( explosionIDList ) ); + + delayMS = 0; + delayVariance = 0; + lifetimeMS = 1000; + lifetimeVariance = 0; + offset = 0.0f; + + shockwave = NULL; + shockwaveID = 0; + shockwaveOnTerrain = false; + + shakeCamera = false; + camShakeFreq.set( 10.0f, 10.0f, 10.0f ); + camShakeAmp.set( 1.0f, 1.0f, 1.0f ); + camShakeDuration = 1.5f; + camShakeRadius = 10.0f; + camShakeFalloff = 10.0f; + + for( U32 i=0; iambient
      animation of this model will be played automatically at " + "the start of the explosion." ); + addField( "explosionScale", TypePoint3F, Offset(explosionScale, ExplosionData), + "\"X Y Z\" scale factor applied to the explosionShape model at the start " + "of the explosion." ); + addField( "playSpeed", TypeF32, Offset(playSpeed, ExplosionData), + "Time scale at which to play the explosionShape ambient sequence." ); + addField( "soundProfile", TYPEID< SFXTrack >(), Offset(soundProfile, ExplosionData), + "Non-looping sound effect that will be played at the start of the explosion." ); + addField( "faceViewer", TypeBool, Offset(faceViewer, ExplosionData), + "Controls whether the visual effects of the explosion always face the camera." ); + + addField( "particleEmitter", TYPEID< ParticleEmitterData >(), Offset(particleEmitter, ExplosionData), + "@brief Emitter used to generate a cloud of particles at the start of the explosion.\n\n" + "Explosions can generate two different particle effects. The first is a " + "single burst of particles at the start of the explosion emitted in a " + "spherical cloud using particleEmitter.\n\n" + "The second effect spawns the list of ParticleEmitters given by the emitter[] " + "field. These emitters generate particles in the normal way throughout the " + "lifetime of the explosion." ); + addField( "particleDensity", TypeS32, Offset(particleDensity, ExplosionData), + "@brief Density of the particle cloud created at the start of the explosion.\n\n" + "@see particleEmitter" ); + addField( "particleRadius", TypeF32, Offset(particleRadius, ExplosionData), + "@brief Radial distance from the explosion center at which cloud particles " + "are emitted.\n\n" + "@see particleEmitter" ); + addField( "emitter", TYPEID< ParticleEmitterData >(), Offset(emitterList, ExplosionData), EC_NUM_EMITTERS, + "@brief List of additional ParticleEmitterData objects to spawn with this " + "explosion.\n\n" + "@see particleEmitter" ); + +// addField( "shockwave", TypeShockwaveDataPtr, Offset(shockwave, ExplosionData) ); +// addField( "shockwaveOnTerrain", TypeBool, Offset(shockwaveOnTerrain, ExplosionData) ); + + addField( "debris", TYPEID< DebrisData >(), Offset(debrisList, ExplosionData), EC_NUM_DEBRIS_TYPES, + "List of DebrisData objects to spawn with this explosion." ); + addField( "debrisThetaMin", TypeF32, Offset(debrisThetaMin, ExplosionData), + "Minimum angle, from the horizontal plane, to eject debris from." ); + addField( "debrisThetaMax", TypeF32, Offset(debrisThetaMax, ExplosionData), + "Maximum angle, from the horizontal plane, to eject debris from." ); + addField( "debrisPhiMin", TypeF32, Offset(debrisPhiMin, ExplosionData), + "Minimum reference angle, from the vertical plane, to eject debris from." ); + addField( "debrisPhiMax", TypeF32, Offset(debrisPhiMax, ExplosionData), + "Maximum reference angle, from the vertical plane, to eject debris from." ); + addField( "debrisNum", TypeS32, Offset(debrisNum, ExplosionData), + "Number of debris objects to create." ); + addField( "debrisNumVariance", TypeS32, Offset(debrisNumVariance, ExplosionData), + "Variance in the number of debris objects to create (must be from 0 - debrisNum)." ); + addField( "debrisVelocity", TypeF32, Offset(debrisVelocity, ExplosionData), + "Velocity to toss debris at." ); + addField( "debrisVelocityVariance", TypeF32, Offset(debrisVelocityVariance, ExplosionData), + "Variance in the debris initial velocity (must be >= 0)." ); + + addField( "subExplosion", TYPEID< ExplosionData >(), Offset(explosionList, ExplosionData), EC_MAX_SUB_EXPLOSIONS, + "List of additional ExplosionData objects to create at the start of the " + "explosion." ); + + addField( "delayMS", TypeS32, Offset(delayMS, ExplosionData), + "Amount of time, in milliseconds, to delay the start of the explosion effect " + "from the creation of the Explosion object." ); + addField( "delayVariance", TypeS32, Offset(delayVariance, ExplosionData), + "Variance, in milliseconds, of delayMS." ); + addField( "lifetimeMS", TypeS32, Offset(lifetimeMS, ExplosionData), + "@brief Lifetime, in milliseconds, of the Explosion object.\n\n" + "@note If explosionShape is defined and contains an ambient animation, " + "this field is ignored, and the playSpeed scaled duration of the animation " + "is used instead." ); + addField( "lifetimeVariance", TypeS32, Offset(lifetimeVariance, ExplosionData), + "Variance, in milliseconds, of the lifetimeMS of the Explosion object.\n" ); + addField( "offset", TypeF32, Offset(offset, ExplosionData), + "@brief Offset distance (in a random direction) of the center of the explosion " + "from the Explosion object position.\n\n" + "Most often used to create some variance in position for subExplosion effects." ); + + addField( "times", TypeF32, Offset(times, ExplosionData), EC_NUM_TIME_KEYS, + "@brief Time keyframes used to scale the explosionShape model.\n\n" + "Values should be in increasing order from 0.0 - 1.0, and correspond to " + "the life of the Explosion where 0 is the beginning and 1 is the end of " + "the explosion lifetime.\n" + "@see lifetimeMS" ); + addField( "sizes", TypePoint3F, Offset(sizes, ExplosionData), EC_NUM_TIME_KEYS, + "@brief \"X Y Z\" size keyframes used to scale the explosionShape model.\n\n" + "The explosionShape (if defined) will be scaled using the times/sizes " + "keyframes over the lifetime of the explosion.\n" + "@see lifetimeMS" ); + + addField( "shakeCamera", TypeBool, Offset(shakeCamera, ExplosionData), + "Controls whether the camera shakes during this explosion." ); + addField( "camShakeFreq", TypePoint3F, Offset(camShakeFreq, ExplosionData), + "Frequency of camera shaking, defined in the \"X Y Z\" axes." ); + addField( "camShakeAmp", TypePoint3F, Offset(camShakeAmp, ExplosionData), + "@brief Amplitude of camera shaking, defined in the \"X Y Z\" axes.\n\n" + "Set any value to 0 to disable shaking in that axis." ); + addField( "camShakeDuration", TypeF32, Offset(camShakeDuration, ExplosionData), + "Duration (in seconds) to shake the camera." ); + addField( "camShakeRadius", TypeF32, Offset(camShakeRadius, ExplosionData), + "Radial distance that a camera's position must be within relative to the " + "center of the explosion to be shaken." ); + addField( "camShakeFalloff", TypeF32, Offset(camShakeFalloff, ExplosionData), + "Falloff value for the camera shake." ); + + addField( "lightStartRadius", TypeF32, Offset(lightStartRadius, ExplosionData), + "@brief Initial radius of the PointLight created by this explosion.\n\n" + "Radius is linearly interpolated from lightStartRadius to lightEndRadius " + "over the lifetime of the explosion.\n" + "@see lifetimeMS" ); + addField( "lightEndRadius", TypeF32, Offset(lightEndRadius, ExplosionData), + "@brief Final radius of the PointLight created by this explosion.\n\n" + "@see lightStartRadius" ); + addField( "lightStartColor", TypeColorF, Offset(lightStartColor, ExplosionData), + "@brief Initial color of the PointLight created by this explosion.\n\n" + "Color is linearly interpolated from lightStartColor to lightEndColor " + "over the lifetime of the explosion.\n" + "@see lifetimeMS" ); + addField( "lightEndColor", TypeColorF, Offset(lightEndColor, ExplosionData), + "@brief Final color of the PointLight created by this explosion.\n\n" + "@see lightStartColor" ); + addField( "lightStartBrightness", TypeF32, Offset(lightStartBrightness, ExplosionData), + "@brief Initial brightness of the PointLight created by this explosion.\n\n" + "Brightness is linearly interpolated from lightStartBrightness to " + "lightEndBrightness over the lifetime of the explosion.\n" + "@see lifetimeMS" ); + addField("lightEndBrightness", TypeF32, Offset(lightEndBrightness, ExplosionData), + "@brief Final brightness of the PointLight created by this explosion.\n\n" + "@see lightStartBrightness" ); + addField( "lightNormalOffset", TypeF32, Offset(lightNormalOffset, ExplosionData), + "Distance (in the explosion normal direction) of the PointLight position " + "from the explosion center." ); + + Parent::initPersistFields(); +} + +bool ExplosionData::onAdd() +{ + if (Parent::onAdd() == false) + return false; + + if (explosionScale.x < 0.01f || explosionScale.y < 0.01f || explosionScale.z < 0.01f) + { + Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s)::onAdd: ExplosionScale components must be >= 0.01", getName()); + explosionScale.x = explosionScale.x < 0.01f ? 0.01f : explosionScale.x; + explosionScale.y = explosionScale.y < 0.01f ? 0.01f : explosionScale.y; + explosionScale.z = explosionScale.z < 0.01f ? 0.01f : explosionScale.z; + } + + if (debrisThetaMin < 0.0f) + { + Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisThetaMin < 0.0", getName()); + debrisThetaMin = 0.0f; + } + if (debrisThetaMax > 180.0f) + { + Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisThetaMax > 180.0", getName()); + debrisThetaMax = 180.0f; + } + if (debrisThetaMin > debrisThetaMax) { + Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisThetaMin > debrisThetaMax", getName()); + debrisThetaMin = debrisThetaMax; + } + if (debrisPhiMin < 0.0f) + { + Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisPhiMin < 0.0", getName()); + debrisPhiMin = 0.0f; + } + if (debrisPhiMax > 360.0f) + { + Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisPhiMax > 360.0", getName()); + debrisPhiMax = 360.0f; + } + if (debrisPhiMin > debrisPhiMax) { + Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisPhiMin > debrisPhiMax", getName()); + debrisPhiMin = debrisPhiMax; + } + if (debrisNum > 1000) { + Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisNum > 1000", getName()); + debrisNum = 1000; + } + if (debrisNumVariance > 1000) { + Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisNumVariance > 1000", getName()); + debrisNumVariance = 1000; + } + if (debrisVelocity < 0.1f) + { + Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisVelocity < 0.1", getName()); + debrisVelocity = 0.1f; + } + if (debrisVelocityVariance > 1000) { + Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) debrisVelocityVariance > 1000", getName()); + debrisVelocityVariance = 1000; + } + if (playSpeed < 0.05f) + { + Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) playSpeed < 0.05", getName()); + playSpeed = 0.05f; + } + if (lifetimeMS < 1) { + Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) lifetimeMS < 1", getName()); + lifetimeMS = 1; + } + if (lifetimeVariance > lifetimeMS) { + Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) lifetimeVariance > lifetimeMS", getName()); + lifetimeVariance = lifetimeMS; + } + if (delayMS < 0) { + Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) delayMS < 0", getName()); + delayMS = 0; + } + if (delayVariance > delayMS) { + Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) delayVariance > delayMS", getName()); + delayVariance = delayMS; + } + if (offset < 0.0f) + { + Con::warnf(ConsoleLogEntry::General, "ExplosionData(%s) offset < 0.0", getName()); + offset = 0.0f; + } + + S32 i; + for( i=0; iwriteString(dtsFileName); + + sfxWrite( stream, soundProfile ); + if (stream->writeFlag(particleEmitter)) + stream->writeRangedU32(particleEmitter->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast); + + stream->writeInt(particleDensity, 14); + stream->write(particleRadius); + stream->writeFlag(faceViewer); + if(stream->writeFlag(explosionScale.x != 1 || explosionScale.y != 1 || explosionScale.z != 1)) + { + stream->writeInt((S32)(explosionScale.x * 100), 16); + stream->writeInt((S32)(explosionScale.y * 100), 16); + stream->writeInt((S32)(explosionScale.z * 100), 16); + } + stream->writeInt((S32)(playSpeed * 20), 14); + stream->writeRangedU32((U32)debrisThetaMin, 0, 180); + stream->writeRangedU32((U32)debrisThetaMax, 0, 180); + stream->writeRangedU32((U32)debrisPhiMin, 0, 360); + stream->writeRangedU32((U32)debrisPhiMax, 0, 360); + stream->writeRangedU32((U32)debrisNum, 0, 1000); + stream->writeRangedU32(debrisNumVariance, 0, 1000); + stream->writeInt((S32)(debrisVelocity * 10), 14); + stream->writeRangedU32((U32)(debrisVelocityVariance * 10), 0, 10000); + stream->writeInt(delayMS >> 5, 16); + stream->writeInt(delayVariance >> 5, 16); + stream->writeInt(lifetimeMS >> 5, 16); + stream->writeInt(lifetimeVariance >> 5, 16); + stream->write(offset); + + stream->writeFlag( shakeCamera ); + stream->write(camShakeFreq.x); + stream->write(camShakeFreq.y); + stream->write(camShakeFreq.z); + stream->write(camShakeAmp.x); + stream->write(camShakeAmp.y); + stream->write(camShakeAmp.z); + stream->write(camShakeDuration); + stream->write(camShakeRadius); + stream->write(camShakeFalloff); + + for( S32 j=0; jwriteFlag( debrisList[j] ) ) + { + stream->writeRangedU32( debrisList[j]->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + } + + S32 i; + for( i=0; iwriteFlag( emitterList[i] != NULL ) ) + { + stream->writeRangedU32( emitterList[i]->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + } + + for( i=0; iwriteFlag( explosionList[i] != NULL ) ) + { + stream->writeRangedU32( explosionList[i]->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + } + U32 count; + for(count = 0; count < EC_NUM_TIME_KEYS; count++) + if(times[i] >= 1) + break; + count++; + if(count > EC_NUM_TIME_KEYS) + count = EC_NUM_TIME_KEYS; + + stream->writeRangedU32(count, 0, EC_NUM_TIME_KEYS); + + for( i=0; iwriteFloat( times[i], 8 ); + + for( i=0; iwriteRangedU32((U32)(sizes[i].x * 100), 0, 16000); + stream->writeRangedU32((U32)(sizes[i].y * 100), 0, 16000); + stream->writeRangedU32((U32)(sizes[i].z * 100), 0, 16000); + } + + // Dynamic light info + stream->writeFloat(lightStartRadius/MaxLightRadius, 8); + stream->writeFloat(lightEndRadius/MaxLightRadius, 8); + stream->writeFloat(lightStartColor.red,7); + stream->writeFloat(lightStartColor.green,7); + stream->writeFloat(lightStartColor.blue,7); + stream->writeFloat(lightEndColor.red,7); + stream->writeFloat(lightEndColor.green,7); + stream->writeFloat(lightEndColor.blue,7); + stream->writeFloat(lightStartBrightness/MaxLightRadius, 8); + stream->writeFloat(lightEndBrightness/MaxLightRadius, 8); + stream->write(lightNormalOffset); +} + +void ExplosionData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + + dtsFileName = stream->readSTString(); + + sfxRead( stream, &soundProfile ); + + if (stream->readFlag()) + particleEmitterId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); + else + particleEmitterId = 0; + + particleDensity = stream->readInt(14); + stream->read(&particleRadius); + faceViewer = stream->readFlag(); + if(stream->readFlag()) + { + explosionScale.x = stream->readInt(16) / 100.0f; + explosionScale.y = stream->readInt(16) / 100.0f; + explosionScale.z = stream->readInt(16) / 100.0f; + } + else + explosionScale.set(1,1,1); + playSpeed = stream->readInt(14) / 20.0f; + debrisThetaMin = stream->readRangedU32(0, 180); + debrisThetaMax = stream->readRangedU32(0, 180); + debrisPhiMin = stream->readRangedU32(0, 360); + debrisPhiMax = stream->readRangedU32(0, 360); + debrisNum = stream->readRangedU32(0, 1000); + debrisNumVariance = stream->readRangedU32(0, 1000); + + debrisVelocity = stream->readInt(14) / 10.0f; + debrisVelocityVariance = stream->readRangedU32(0, 10000) / 10.0f; + delayMS = stream->readInt(16) << 5; + delayVariance = stream->readInt(16) << 5; + lifetimeMS = stream->readInt(16) << 5; + lifetimeVariance = stream->readInt(16) << 5; + + stream->read(&offset); + + shakeCamera = stream->readFlag(); + stream->read(&camShakeFreq.x); + stream->read(&camShakeFreq.y); + stream->read(&camShakeFreq.z); + stream->read(&camShakeAmp.x); + stream->read(&camShakeAmp.y); + stream->read(&camShakeAmp.z); + stream->read(&camShakeDuration); + stream->read(&camShakeRadius); + stream->read(&camShakeFalloff); + + + for( S32 j=0; jreadFlag() ) + { + debrisIDList[j] = (S32) stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + } + + U32 i; + for( i=0; ireadFlag() ) + { + emitterIDList[i] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + } + + for( S32 k=0; kreadFlag() ) + { + explosionIDList[k] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + } + + U32 count = stream->readRangedU32(0, EC_NUM_TIME_KEYS); + + for( i=0; ireadFloat(8); + + for( i=0; ireadRangedU32(0, 16000) / 100.0f; + sizes[i].y = stream->readRangedU32(0, 16000) / 100.0f; + sizes[i].z = stream->readRangedU32(0, 16000) / 100.0f; + } + + // + lightStartRadius = stream->readFloat(8) * MaxLightRadius; + lightEndRadius = stream->readFloat(8) * MaxLightRadius; + lightStartColor.red = stream->readFloat(7); + lightStartColor.green = stream->readFloat(7); + lightStartColor.blue = stream->readFloat(7); + lightEndColor.red = stream->readFloat(7); + lightEndColor.green = stream->readFloat(7); + lightEndColor.blue = stream->readFloat(7); + lightStartBrightness = stream->readFloat(8) * MaxLightRadius; + lightEndBrightness = stream->readFloat(8) * MaxLightRadius; + stream->read( &lightNormalOffset ); +} + +bool ExplosionData::preload(bool server, String &errorStr) +{ + if (Parent::preload(server, errorStr) == false) + return false; + + if( !server ) + { + String errorStr; + if( !sfxResolve( &soundProfile, errorStr ) ) + Con::errorf(ConsoleLogEntry::General, "Error, unable to load sound profile for explosion datablock: %s", errorStr.c_str()); + if (!particleEmitter && particleEmitterId != 0) + if (Sim::findObject(particleEmitterId, particleEmitter) == false) + Con::errorf(ConsoleLogEntry::General, "Error, unable to load particle emitter for explosion datablock"); + } + + if (dtsFileName && dtsFileName[0]) { + explosionShape = ResourceManager::get().load(dtsFileName); + if (!bool(explosionShape)) { + errorStr = String::ToString("ExplosionData: Couldn't load shape \"%s\"", dtsFileName); + return false; + } + + // Resolve animations + explosionAnimation = explosionShape->findSequence("ambient"); + + // Preload textures with a dummy instance... + TSShapeInstance* pDummy = new TSShapeInstance(explosionShape, !server); + delete pDummy; + + } else { + explosionShape = NULL; + explosionAnimation = -1; + } + + return true; +} + + +//-------------------------------------------------------------------------- +//-------------------------------------- +// +Explosion::Explosion() +{ + mTypeMask |= ExplosionObjectType | LightObjectType; + + mExplosionInstance = NULL; + mExplosionThread = NULL; + + dMemset( mEmitterList, 0, sizeof( mEmitterList ) ); + mMainEmitter = NULL; + + mFade = 1; + mDelayMS = 0; + mCurrMS = 0; + mEndingMS = 1000; + mActive = false; + mCollideType = 0; + + mInitialNormal.set( 0.0f, 0.0f, 1.0f ); + mRandAngle = sgRandom.randF( 0.0f, 1.0f ) * M_PI_F * 2.0f; + mLight = LIGHTMGR->createLightInfo(); + + mNetFlags.set( IsGhost ); +} + +Explosion::~Explosion() +{ + if( mExplosionInstance ) + { + delete mExplosionInstance; + mExplosionInstance = NULL; + mExplosionThread = NULL; + } + + SAFE_DELETE(mLight); +} + + +void Explosion::setInitialState(const Point3F& point, const Point3F& normal, const F32 fade) +{ + setPosition(point); + mInitialNormal = normal; + mFade = fade; +} + +//-------------------------------------------------------------------------- +void Explosion::initPersistFields() +{ + Parent::initPersistFields(); + + // +} + +//-------------------------------------------------------------------------- +bool Explosion::onAdd() +{ + // first check if we have a server connection, if we dont then this is on the server + // and we should exit, then check if the parent fails to add the object + GameConnection *conn = GameConnection::getConnectionToServer(); + if ( !conn || !Parent::onAdd() ) + return false; + + mDelayMS = mDataBlock->delayMS + sgRandom.randI( -mDataBlock->delayVariance, mDataBlock->delayVariance ); + mEndingMS = mDataBlock->lifetimeMS + sgRandom.randI( -mDataBlock->lifetimeVariance, mDataBlock->lifetimeVariance ); + + if( mFabs( mDataBlock->offset ) > 0.001f ) + { + MatrixF axisOrient = MathUtils::createOrientFromDir( mInitialNormal ); + + MatrixF trans = getTransform(); + Point3F randVec; + randVec.x = sgRandom.randF( -1.0f, 1.0f ); + randVec.y = sgRandom.randF( 0.0f, 1.0f ); + randVec.z = sgRandom.randF( -1.0f, 1.0f ); + randVec.normalize(); + randVec *= mDataBlock->offset; + axisOrient.mulV( randVec ); + trans.setPosition( trans.getPosition() + randVec ); + setTransform( trans ); + } + + // shake camera + if( mDataBlock->shakeCamera ) + { + // first check if explosion is near player + GameConnection* connection = GameConnection::getConnectionToServer(); + ShapeBase *obj = dynamic_cast(connection->getControlObject()); + + bool applyShake = true; + + if( obj ) + { + ShapeBase* cObj = obj; + while((cObj = cObj->getControlObject()) != 0) + { + if(cObj->useObjsEyePoint()) + { + applyShake = false; + break; + } + } + } + + + if( applyShake && obj ) + { + VectorF diff = obj->getPosition() - getPosition(); + F32 dist = diff.len(); + if( dist < mDataBlock->camShakeRadius ) + { + CameraShake *camShake = new CameraShake; + camShake->setDuration( mDataBlock->camShakeDuration ); + camShake->setFrequency( mDataBlock->camShakeFreq ); + + F32 falloff = dist / mDataBlock->camShakeRadius; + falloff = 1.0f + falloff * 10.0f; + falloff = 1.0f / (falloff * falloff); + + VectorF shakeAmp = mDataBlock->camShakeAmp * falloff; + camShake->setAmplitude( shakeAmp ); + camShake->setFalloff( mDataBlock->camShakeFalloff ); + camShake->init(); + gCamFXMgr.addFX( camShake ); + } + } + } + + + if( mDelayMS == 0 ) + { + if( !explode() ) + { + return false; + } + } + + gClientSceneGraph->addObjectToScene(this); + + removeFromProcessList(); + ClientProcessList::get()->addObject(this); + + mRandomVal = sgRandom.randF(); + + NetConnection* pNC = NetConnection::getConnectionToServer(); + AssertFatal(pNC != NULL, "Error, must have a connection to the server!"); + pNC->addObject(this); + + // Initialize the light structure and register as a dynamic light + if (mDataBlock->lightStartRadius != 0.0f || mDataBlock->lightEndRadius) + { + mLight->setType( LightInfo::Point ); + mLight->setRange( mDataBlock->lightStartRadius ); + mLight->setColor( mDataBlock->lightStartColor ); + } + + return true; +} + +void Explosion::onRemove() +{ + for( int i=0; ideleteWhenEmpty(); + mEmitterList[i] = NULL; + } + } + + if( mMainEmitter ) + { + mMainEmitter->deleteWhenEmpty(); + mMainEmitter = NULL; + } + + if (getSceneManager() != NULL) + getSceneManager()->removeObjectFromScene(this); + if (getContainer() != NULL) + getContainer()->removeObject(this); + + Parent::onRemove(); +} + + +bool Explosion::onNewDataBlock( GameBaseData *dptr, bool reload ) +{ + mDataBlock = dynamic_cast( dptr ); + if (!mDataBlock || !Parent::onNewDataBlock( dptr, reload )) + return false; + + scriptOnNewDataBlock(); + return true; +} + + +//-------------------------------------------------------------------------- +void Explosion::prepRenderImage( SceneRenderState* state ) +{ + prepBatchRender( state ); +} + +void Explosion::setCurrentScale() +{ + F32 t = F32(mCurrMS) / F32(mEndingMS); + + for( U32 i = 1; i < ExplosionData::EC_NUM_TIME_KEYS; i++ ) + { + if( mDataBlock->times[i] >= t ) + { + F32 firstPart = t - mDataBlock->times[i-1]; + F32 total = mDataBlock->times[i] - + mDataBlock->times[i-1]; + + firstPart /= total; + + mObjScale = (mDataBlock->sizes[i-1] * (1.0f - firstPart)) + + (mDataBlock->sizes[i] * firstPart); + + return; + } + } + +} + +//-------------------------------------------------------------------------- +// Make the explosion face the viewer (if desired) +//-------------------------------------------------------------------------- +void Explosion::prepModelView(SceneRenderState* state) +{ + MatrixF rotMatrix( true ); + Point3F targetVector; + + if( mDataBlock->faceViewer ) + { + targetVector = getPosition() - state->getCameraPosition(); + targetVector.normalize(); + + // rotate explosion each time so it's a little different + rotMatrix.set( EulerF( 0.0f, mRandAngle, 0.0f ) ); + } + else + { + targetVector = mInitialNormal; + } + + MatrixF explOrient = MathUtils::createOrientFromDir( targetVector ); + explOrient.mul( rotMatrix ); + explOrient.setPosition( getPosition() ); + + setCurrentScale(); + explOrient.scale( mObjScale ); + GFX->setWorldMatrix( explOrient ); +} + +//-------------------------------------------------------------------------- +// Render object +//-------------------------------------------------------------------------- +void Explosion::prepBatchRender(SceneRenderState* state) +{ + if ( !mExplosionInstance ) + return; + + MatrixF proj = GFX->getProjectionMatrix(); + RectI viewport = GFX->getViewport(); + + // Set up our TS render state here. + TSRenderState rdata; + rdata.setSceneState( state ); + + // We might have some forward lit materials + // so pass down a query to gather lights. + LightQuery query; + query.init( getWorldSphere() ); + rdata.setLightQuery( &query ); + + // render mesh + GFX->pushWorldMatrix(); + + prepModelView( state ); + + mExplosionInstance->animate(); + mExplosionInstance->render( rdata ); + + GFX->popWorldMatrix(); + GFX->setProjectionMatrix( proj ); + GFX->setViewport( viewport ); +} + +void Explosion::submitLights( LightManager *lm, bool staticLighting ) +{ + if ( staticLighting ) + return; + + // Update the light's info and add it to the scene, the light will + // only be visible for this current frame. + mLight->setPosition( getRenderTransform().getPosition() + mInitialNormal * mDataBlock->lightNormalOffset ); + F32 t = F32(mCurrMS) / F32(mEndingMS); + mLight->setRange( mDataBlock->lightStartRadius + + (mDataBlock->lightEndRadius - mDataBlock->lightStartRadius) * t ); + mLight->setColor( mDataBlock->lightStartColor + + (mDataBlock->lightEndColor - mDataBlock->lightStartColor) * t ); + mLight->setBrightness( mDataBlock->lightStartBrightness + + (mDataBlock->lightEndBrightness - mDataBlock->lightStartBrightness) * t ); + + lm->registerGlobalLight( mLight, this ); +} + + +//-------------------------------------------------------------------------- +void Explosion::processTick(const Move*) +{ + mCurrMS += TickMs; + + if( mCurrMS >= mEndingMS ) + { + deleteObject(); + return; + } + + if( (mCurrMS > mDelayMS) && !mActive ) + explode(); +} + +void Explosion::advanceTime(F32 dt) +{ + if (dt == 0.0f) + return; + + GameConnection* conn = GameConnection::getConnectionToServer(); + if(!conn) + return; + + updateEmitters( dt ); + + if( mExplosionInstance ) + mExplosionInstance->advanceTime(dt, mExplosionThread); +} + +//---------------------------------------------------------------------------- +// Update emitters +//---------------------------------------------------------------------------- +void Explosion::updateEmitters( F32 dt ) +{ + Point3F pos = getPosition(); + + for( int i=0; iemitParticles( pos, pos, mInitialNormal, Point3F( 0.0f, 0.0f, 0.0f ), (U32)(dt * 1000)); + } + } + +} + +//---------------------------------------------------------------------------- +// Launch Debris +//---------------------------------------------------------------------------- +void Explosion::launchDebris( Point3F &axis ) +{ + GameConnection* conn = GameConnection::getConnectionToServer(); + if(!conn) + return; + + bool hasDebris = false; + for( int j=0; jdebrisList[j] ) + { + hasDebris = true; + break; + } + } + if( !hasDebris ) + { + return; + } + + Point3F axisx; + if (mFabs(axis.z) < 0.999f) + mCross(axis, Point3F(0.0f, 0.0f, 1.0f), &axisx); + else + mCross(axis, Point3F(0.0f, 1.0f, 0.0f), &axisx); + axisx.normalize(); + + Point3F pos( 0.0f, 0.0f, 0.5f ); + pos += getPosition(); + + + U32 numDebris = mDataBlock->debrisNum + sgRandom.randI( -mDataBlock->debrisNumVariance, mDataBlock->debrisNumVariance ); + + for( int i=0; idebrisThetaMin, mDataBlock->debrisThetaMax, + mDataBlock->debrisPhiMin, mDataBlock->debrisPhiMax ); + + F32 debrisVel = mDataBlock->debrisVelocity + mDataBlock->debrisVelocityVariance * sgRandom.randF( -1.0f, 1.0f ); + + launchDir *= debrisVel; + + Debris *debris = new Debris; + debris->setDataBlock( mDataBlock->debrisList[0] ); + debris->setTransform( getTransform() ); + debris->init( pos, launchDir ); + + if( !debris->registerObject() ) + { + Con::warnf( ConsoleLogEntry::General, "Could not register debris for class: %s", mDataBlock->getName() ); + delete debris; + debris = NULL; + } + } +} + +//---------------------------------------------------------------------------- +// Spawn sub explosions +//---------------------------------------------------------------------------- +void Explosion::spawnSubExplosions() +{ + GameConnection* conn = GameConnection::getConnectionToServer(); + if(!conn) + return; + + for( S32 i=0; iexplosionList[i] ) + { + MatrixF trans = getTransform(); + Explosion* pExplosion = new Explosion; + pExplosion->setDataBlock( mDataBlock->explosionList[i] ); + pExplosion->setTransform( trans ); + pExplosion->setInitialState( trans.getPosition(), mInitialNormal, 1); + if (!pExplosion->registerObject()) + delete pExplosion; + } + } +} + +//---------------------------------------------------------------------------- +// Explode +//---------------------------------------------------------------------------- +bool Explosion::explode() +{ + mActive = true; + + GameConnection* conn = GameConnection::getConnectionToServer(); + if(!conn) + return false; + + launchDebris( mInitialNormal ); + spawnSubExplosions(); + + if (bool(mDataBlock->explosionShape) && mDataBlock->explosionAnimation != -1) { + mExplosionInstance = new TSShapeInstance(mDataBlock->explosionShape, true); + + mExplosionThread = mExplosionInstance->addThread(); + mExplosionInstance->setSequence(mExplosionThread, mDataBlock->explosionAnimation, 0); + mExplosionInstance->setTimeScale(mExplosionThread, mDataBlock->playSpeed); + + mCurrMS = 0; + mEndingMS = U32(mExplosionInstance->getScaledDuration(mExplosionThread) * 1000.0f); + + mObjScale.convolve(mDataBlock->explosionScale); + mObjBox = mDataBlock->explosionShape->bounds; + resetWorldBox(); + } + + if (mDataBlock->soundProfile) + SFX->playOnce( mDataBlock->soundProfile, &getTransform() ); + + if (mDataBlock->particleEmitter) { + mMainEmitter = new ParticleEmitter; + mMainEmitter->setDataBlock(mDataBlock->particleEmitter); + mMainEmitter->registerObject(); + + mMainEmitter->emitParticles(getPosition(), mInitialNormal, mDataBlock->particleRadius, + Point3F::Zero, U32(mDataBlock->particleDensity * mFade)); + } + + for( int i=0; iemitterList[i] != NULL ) + { + ParticleEmitter * pEmitter = new ParticleEmitter; + pEmitter->setDataBlock( mDataBlock->emitterList[i] ); + if( !pEmitter->registerObject() ) + { + Con::warnf( ConsoleLogEntry::General, "Could not register emitter for particle of class: %s", mDataBlock->getName() ); + SAFE_DELETE(pEmitter); + } + mEmitterList[i] = pEmitter; + } + } + + return true; +} + diff --git a/Engine/source/T3D/fx/explosion.h b/Engine/source/T3D/fx/explosion.h new file mode 100644 index 000000000..7fe0406d4 --- /dev/null +++ b/Engine/source/T3D/fx/explosion.h @@ -0,0 +1,199 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _EXPLOSION_H_ +#define _EXPLOSION_H_ + +#ifndef _GAMEBASE_H_ +#include "T3D/gameBase/gameBase.h" +#endif +#ifndef _TSSHAPE_H_ +#include "ts/tsShape.h" +#endif +#ifndef __RESOURCE_H__ +#include "core/resource.h" +#endif +#ifndef _LIGHTINFO_H_ +#include "lighting/lightInfo.h" +#endif + +class ParticleEmitter; +class ParticleEmitterData; +class TSThread; +class SFXTrack; +struct DebrisData; +class ShockwaveData; + +//-------------------------------------------------------------------------- +class ExplosionData : public GameBaseData { + public: + typedef GameBaseData Parent; + + enum ExplosionConsts + { + EC_NUM_DEBRIS_TYPES = 1, + EC_NUM_EMITTERS = 4, + EC_MAX_SUB_EXPLOSIONS = 5, + EC_NUM_TIME_KEYS = 4, + }; + + public: + StringTableEntry dtsFileName; + + bool faceViewer; + + S32 particleDensity; + F32 particleRadius; + + SFXTrack* soundProfile; + ParticleEmitterData* particleEmitter; + S32 particleEmitterId; + + Point3F explosionScale; + F32 playSpeed; + + Resource explosionShape; + S32 explosionAnimation; + + ParticleEmitterData* emitterList[EC_NUM_EMITTERS]; + S32 emitterIDList[EC_NUM_EMITTERS]; + + ShockwaveData * shockwave; + S32 shockwaveID; + bool shockwaveOnTerrain; + + DebrisData * debrisList[EC_NUM_DEBRIS_TYPES]; + S32 debrisIDList[EC_NUM_DEBRIS_TYPES]; + + F32 debrisThetaMin; + F32 debrisThetaMax; + F32 debrisPhiMin; + F32 debrisPhiMax; + S32 debrisNum; + S32 debrisNumVariance; + F32 debrisVelocity; + F32 debrisVelocityVariance; + + // sub - explosions + ExplosionData* explosionList[EC_MAX_SUB_EXPLOSIONS]; + S32 explosionIDList[EC_MAX_SUB_EXPLOSIONS]; + + S32 delayMS; + S32 delayVariance; + S32 lifetimeMS; + S32 lifetimeVariance; + + F32 offset; + Point3F sizes[ EC_NUM_TIME_KEYS ]; + F32 times[ EC_NUM_TIME_KEYS ]; + + // camera shake data + bool shakeCamera; + VectorF camShakeFreq; + VectorF camShakeAmp; + F32 camShakeDuration; + F32 camShakeRadius; + F32 camShakeFalloff; + + // Dynamic Lighting. The light is smoothly + // interpolated from start to end time. + F32 lightStartRadius; + F32 lightEndRadius; + ColorF lightStartColor; + ColorF lightEndColor; + F32 lightStartBrightness; + F32 lightEndBrightness; + F32 lightNormalOffset; + + ExplosionData(); + DECLARE_CONOBJECT(ExplosionData); + bool onAdd(); + bool preload(bool server, String &errorStr); + static void initPersistFields(); + virtual void packData(BitStream* stream); + virtual void unpackData(BitStream* stream); +}; + + +//-------------------------------------------------------------------------- +class Explosion : public GameBase, public ISceneLight +{ + typedef GameBase Parent; + + private: + ExplosionData* mDataBlock; + + TSShapeInstance* mExplosionInstance; + TSThread* mExplosionThread; + + SimObjectPtr mEmitterList[ ExplosionData::EC_NUM_EMITTERS ]; + SimObjectPtr mMainEmitter; + + U32 mCurrMS; + U32 mEndingMS; + F32 mRandAngle; + LightInfo* mLight; + + protected: + Point3F mInitialNormal; + F32 mFade; + bool mActive; + S32 mDelayMS; + F32 mRandomVal; + U32 mCollideType; + + protected: + bool onAdd(); + void onRemove(); + bool explode(); + + void processTick(const Move *move); + void advanceTime(F32 dt); + void updateEmitters( F32 dt ); + void launchDebris( Point3F &axis ); + void spawnSubExplosions(); + void setCurrentScale(); + + // Rendering + protected: + void prepRenderImage( SceneRenderState *state ); + void prepBatchRender(SceneRenderState *state); + void prepModelView(SceneRenderState*); + + public: + Explosion(); + ~Explosion(); + void setInitialState(const Point3F& point, const Point3F& normal, const F32 fade = 1.0); + + // ISceneLight + virtual void submitLights( LightManager *lm, bool staticLighting ); + virtual LightInfo* getLight() { return mLight; } + + bool onNewDataBlock( GameBaseData *dptr, bool reload ); + void setCollideType( U32 cType ){ mCollideType = cType; } + + DECLARE_CONOBJECT(Explosion); + static void initPersistFields(); +}; + +#endif // _H_EXPLOSION + diff --git a/Engine/source/T3D/fx/fxFoliageReplicator.cpp b/Engine/source/T3D/fx/fxFoliageReplicator.cpp new file mode 100644 index 000000000..f44947eec --- /dev/null +++ b/Engine/source/T3D/fx/fxFoliageReplicator.cpp @@ -0,0 +1,1818 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// Written by Melvyn May, Started on 4th August 2002. +// +// "My code is written for the Torque community, so do your worst with it, +// just don't rip-it-off and call it your own without even thanking me". +// +// - Melv. +// +// +// Conversion to TSE By Brian "bzztbomb" Richardson 9/2005 +// This was a neat piece of code! Thanks Melv! +// I've switched this to use one large indexed primitive buffer. All animation +// is then done in the vertex shader. This means we have a static vertex/primitive +// buffer that never changes! How spiff! Because of this, the culling code was +// changed to render out full quadtree nodes, we don't try to cull each individual +// node ourselves anymore. This means to get good performance, you probably need to do the +// following: +// 1. If it's a small area to cover, turn off culling completely. +// 2. You want to tune the parameters to make sure there are a lot of billboards within +// each quadrant. +// +// POTENTIAL TODO LIST: +// TODO: Clamp item alpha to fog alpha + +#include "platform/platform.h" +#include "T3D/fx/fxFoliageReplicator.h" + +#include "gfx/gfxDevice.h" +#include "gfx/primBuilder.h" // Used for debug / mission edit rendering +#include "console/consoleTypes.h" +#include "core/stream/bitStream.h" +#include "math/mRandom.h" +#include "math/mathIO.h" +#include "console/simBase.h" +#include "scene/sceneManager.h" +#include "renderInstance/renderPassManager.h" +#include "scene/sceneRenderState.h" +#include "sim/netConnection.h" +#include "materials/shaderData.h" +#include "console/engineAPI.h" + +const U32 AlphaTexLen = 1024; + +GFXImplementVertexFormat( GFXVertexFoliage ) +{ + addElement( "POSITION", GFXDeclType_Float3 ); + addElement( "NORMAL", GFXDeclType_Float3 ); + addElement( "TEXCOORD", GFXDeclType_Float2, 0 ); + addElement( "TEXCOORD", GFXDeclType_Float2, 1 ); +} + +//------------------------------------------------------------------------------ +// +// Put the function in /example/common/editor/ObjectBuilderGui.gui [around line 458] ... +// +// function ObjectBuilderGui::buildfxFoliageReplicator(%this) +// { +// %this.className = "fxFoliageReplicator"; +// %this.process(); +// } +// +//------------------------------------------------------------------------------ +// +// Put this in /example/common/editor/EditorGui.cs in [function Creator::init( %this )] +// +// %Environment_Item[8] = "fxFoliageReplicator"; <-- ADD THIS. +// +//------------------------------------------------------------------------------ +// +// Put this in /example/common/client/missionDownload.cs in [function clientCmdMissionStartPhase3(%seq,%missionName)] (line 65) +// after codeline 'onPhase2Complete();'. +// +// StartFoliageReplication(); +// +//------------------------------------------------------------------------------ +// +// Put this in /engine/console/simBase.h (around line 509) in +// +// namespace Sim +// { +// DeclareNamedSet(fxFoliageSet) <-- ADD THIS (Note no semi-colon). +// +//------------------------------------------------------------------------------ +// +// Put this in /engine/console/simBase.cc (around line 19) in +// +// ImplementNamedSet(fxFoliageSet) <-- ADD THIS (Note no semi-colon). +// +//------------------------------------------------------------------------------ +// +// Put this in /engine/console/simManager.cc [function void init()] (around line 269). +// +// namespace Sim +// { +// InstantiateNamedSet(fxFoliageSet); <-- ADD THIS (Including Semi-colon). +// +//------------------------------------------------------------------------------ +extern bool gEditingMission; + +//------------------------------------------------------------------------------ + +IMPLEMENT_CO_NETOBJECT_V1(fxFoliageReplicator); + +ConsoleDocClass( fxFoliageReplicator, + "@brief An emitter to replicate fxFoliageItem objects across an area.\n" + "@ingroup Foliage\n" +); + +//------------------------------------------------------------------------------ +// +// Trig Table Lookups. +// +//------------------------------------------------------------------------------ +const F32 PeriodLen = (F32) 2.0f * (F32) M_PI; +const F32 PeriodLenMinus = (F32) (2.0f * M_PI) - 0.01f; + +//------------------------------------------------------------------------------ +// +// Class: fxFoliageRenderList +// +//------------------------------------------------------------------------------ + +void fxFoliageRenderList::SetupClipPlanes( SceneRenderState* state, const F32 farClipPlane ) +{ + const F32 nearPlane = state->getNearPlane(); + const F32 farPlane = farClipPlane; + + const Frustum& frustum = state->getFrustum(); + + // [rene, 23-Feb-11] Why isn't this preserving the ortho state of the original frustum? + + mFrustum.set( false,//zoneState.frustum.isOrtho(), + frustum.getNearLeft(), + frustum.getNearRight(), + frustum.getNearTop(), + frustum.getNearBottom(), + nearPlane, + farPlane, + frustum.getTransform() + ); + + + mBox = mFrustum.getBounds(); +} + +//------------------------------------------------------------------------------ + + +inline void fxFoliageRenderList::DrawQuadBox(const Box3F& QuadBox, const ColorF Colour) +{ + // Define our debug box. + static Point3F BoxPnts[] = { + Point3F(0,0,0), + Point3F(0,0,1), + Point3F(0,1,0), + Point3F(0,1,1), + Point3F(1,0,0), + Point3F(1,0,1), + Point3F(1,1,0), + Point3F(1,1,1) + }; + + static U32 BoxVerts[][4] = { + {0,2,3,1}, // -x + {7,6,4,5}, // +x + {0,1,5,4}, // -y + {3,2,6,7}, // +y + {0,4,6,2}, // -z + {3,7,5,1} // +z + }; + + // Project our Box Points. + Point3F ProjectionPoints[8]; + + for( U32 i=0; i<8; i++ ) + { + ProjectionPoints[i].set(BoxPnts[i].x ? QuadBox.maxExtents.x : QuadBox.minExtents.x, + BoxPnts[i].y ? QuadBox.maxExtents.y : QuadBox.minExtents.y, + BoxPnts[i].z ? (mHeightLerp * QuadBox.maxExtents.z) + (1-mHeightLerp) * QuadBox.minExtents.z : QuadBox.minExtents.z); + + } + + PrimBuild::color(Colour); + + // Draw the Box. + for(U32 x = 0; x < 6; x++) + { + // Draw a line-loop. + PrimBuild::begin(GFXLineStrip, 5); + + for(U32 y = 0; y < 4; y++) + { + PrimBuild::vertex3f(ProjectionPoints[BoxVerts[x][y]].x, + ProjectionPoints[BoxVerts[x][y]].y, + ProjectionPoints[BoxVerts[x][y]].z); + } + PrimBuild::vertex3f(ProjectionPoints[BoxVerts[x][0]].x, + ProjectionPoints[BoxVerts[x][0]].y, + ProjectionPoints[BoxVerts[x][0]].z); + PrimBuild::end(); + } +} + +//------------------------------------------------------------------------------ +bool fxFoliageRenderList::IsQuadrantVisible(const Box3F VisBox, const MatrixF& RenderTransform) +{ + // Can we trivially accept the visible box? + if ( !mFrustum.isCulled( VisBox ) ) + return true; + + // Not visible. + return false; +} + + + +//------------------------------------------------------------------------------ +// +// Class: fxFoliageCulledList +// +//------------------------------------------------------------------------------ +fxFoliageCulledList::fxFoliageCulledList(Box3F SearchBox, fxFoliageCulledList* InVec) +{ + // Find the Candidates. + FindCandidates(SearchBox, InVec); +} + +//------------------------------------------------------------------------------ + +void fxFoliageCulledList::FindCandidates(Box3F SearchBox, fxFoliageCulledList* InVec) +{ + // Search the Culled List. + for (U32 i = 0; i < InVec->GetListCount(); i++) + { + // Is this Box overlapping our search box? + if (SearchBox.isOverlapped(InVec->GetElement(i)->FoliageBox)) + { + // Yes, so add it to our culled list. + mCulledObjectSet.push_back(InVec->GetElement(i)); + } + } +} + + + +//------------------------------------------------------------------------------ +// +// Class: fxFoliageReplicator +// +//------------------------------------------------------------------------------ + +fxFoliageReplicator::fxFoliageReplicator() +{ + // Setup NetObject. + mTypeMask |= StaticObjectType; + mNetFlags.set(Ghostable | ScopeAlways); + + // Reset Client Replication Started. + mClientReplicationStarted = false; + + // Reset Foliage Count. + mCurrentFoliageCount = 0; + + // Reset Creation Area Angle Animation. + mCreationAreaAngle = 0; + + // Reset Last Render Time. + mLastRenderTime = 0; + + // Reset Foliage Nodes. + mPotentialFoliageNodes = 0; + // Reset Billboards Acquired. + mBillboardsAcquired = 0; + + // Reset Frame Serial ID. + mFrameSerialID = 0; + + mAlphaLookup = NULL; + + mDirty = true; + + mFoliageShaderProjectionSC = NULL; + mFoliageShaderWorldSC = NULL; + mFoliageShaderGlobalSwayPhaseSC = NULL; + mFoliageShaderSwayMagnitudeSideSC = NULL; + mFoliageShaderSwayMagnitudeFrontSC = NULL; + mFoliageShaderGlobalLightPhaseSC = NULL; + mFoliageShaderLuminanceMagnitudeSC = NULL; + mFoliageShaderLuminanceMidpointSC = NULL; + mFoliageShaderDistanceRangeSC = NULL; + mFoliageShaderCameraPosSC = NULL; + mFoliageShaderTrueBillboardSC = NULL; + mFoliageShaderGroundAlphaSC = NULL; + mFoliageShaderAmbientColorSC = NULL; + + mShaderData = NULL; +} + +//------------------------------------------------------------------------------ + +fxFoliageReplicator::~fxFoliageReplicator() +{ + if (mAlphaLookup) + delete mAlphaLookup; + + mPlacementSB = NULL; +} + +//------------------------------------------------------------------------------ + +void fxFoliageReplicator::initPersistFields() +{ + // Add out own persistent fields. + addGroup( "Debugging" ); // MM: Added Group Header. + addField( "UseDebugInfo", TypeBool, Offset( mFieldData.mUseDebugInfo, fxFoliageReplicator ), "Culling bins are drawn when set to true." ); + addField( "DebugBoxHeight", TypeF32, Offset( mFieldData.mDebugBoxHeight, fxFoliageReplicator ), "Height multiplier for drawn culling bins."); + addField( "HideFoliage", TypeBool, Offset( mFieldData.mHideFoliage, fxFoliageReplicator ), "Foliage is hidden when set to true." ); + addField( "ShowPlacementArea", TypeBool, Offset( mFieldData.mShowPlacementArea, fxFoliageReplicator ), "Draw placement rings when set to true." ); + addField( "PlacementAreaHeight", TypeS32, Offset( mFieldData.mPlacementBandHeight, fxFoliageReplicator ), "Height of the placement ring in world units." ); + addField( "PlacementColour", TypeColorF, Offset( mFieldData.mPlaceAreaColour, fxFoliageReplicator ), "Color of the placement ring." ); + endGroup( "Debugging" ); // MM: Added Group Footer. + + addGroup( "Media" ); // MM: Added Group Header. + addField( "Seed", TypeS32, Offset( mFieldData.mSeed, fxFoliageReplicator ), "Random seed for foliage placement." ); + addField( "FoliageFile", TypeFilename, Offset( mFieldData.mFoliageFile, fxFoliageReplicator ), "Image file for the foliage texture." ); + addField( "FoliageCount", TypeS32, Offset( mFieldData.mFoliageCount, fxFoliageReplicator ), "Maximum foliage instance count." ); + addField( "FoliageRetries", TypeS32, Offset( mFieldData.mFoliageRetries, fxFoliageReplicator ), "Number of times to try placing a foliage instance before giving up." ); + endGroup( "Media" ); // MM: Added Group Footer. + + addGroup( "Area" ); // MM: Added Group Header. + addField( "InnerRadiusX", TypeS32, Offset( mFieldData.mInnerRadiusX, fxFoliageReplicator ), "Placement area inner radius on the X axis" ); + addField( "InnerRadiusY", TypeS32, Offset( mFieldData.mInnerRadiusY, fxFoliageReplicator ), "Placement area inner radius on the Y axis" ); + addField( "OuterRadiusX", TypeS32, Offset( mFieldData.mOuterRadiusX, fxFoliageReplicator ), "Placement area outer radius on the X axis" ); + addField( "OuterRadiusY", TypeS32, Offset( mFieldData.mOuterRadiusY, fxFoliageReplicator ), "Placement area outer radius on the Y axis" ); + endGroup( "Area" ); // MM: Added Group Footer. + + addGroup( "Dimensions" ); // MM: Added Group Header. + addField( "MinWidth", TypeF32, Offset( mFieldData.mMinWidth, fxFoliageReplicator ), "Minimum width of foliage billboards" ); + addField( "MaxWidth", TypeF32, Offset( mFieldData.mMaxWidth, fxFoliageReplicator ), "Maximum width of foliage billboards" ); + addField( "MinHeight", TypeF32, Offset( mFieldData.mMinHeight, fxFoliageReplicator ), "Minimum height of foliage billboards" ); + addField( "MaxHeight", TypeF32, Offset( mFieldData.mMaxHeight, fxFoliageReplicator ), "Maximum height of foliage billboards" ); + addField( "FixAspectRatio", TypeBool, Offset( mFieldData.mFixAspectRatio, fxFoliageReplicator ), "Maintain aspect ratio of image if true. This option ignores MaxWidth." ); + addField( "FixSizeToMax", TypeBool, Offset( mFieldData.mFixSizeToMax, fxFoliageReplicator ), "Use only MaxWidth and MaxHeight for billboard size. Ignores MinWidth and MinHeight." ); + addField( "OffsetZ", TypeF32, Offset( mFieldData.mOffsetZ, fxFoliageReplicator ), "Offset billboards by this amount vertically." ); + addField( "RandomFlip", TypeBool, Offset( mFieldData.mRandomFlip, fxFoliageReplicator ), "Randomly flip billboards left-to-right." ); + addField( "UseTrueBillboards", TypeBool, Offset( mFieldData.mUseTrueBillboards, fxFoliageReplicator ), "Use camera facing billboards ( including the z axis )." ); + endGroup( "Dimensions" ); // MM: Added Group Footer. + + addGroup( "Culling" ); // MM: Added Group Header. + addField( "UseCulling", TypeBool, Offset( mFieldData.mUseCulling, fxFoliageReplicator ), "Use culling bins when enabled." ); + addField( "CullResolution", TypeS32, Offset( mFieldData.mCullResolution, fxFoliageReplicator ), "Minimum size of culling bins. Must be >= 8 and <= OuterRadius." ); + addField( "ViewDistance", TypeF32, Offset( mFieldData.mViewDistance, fxFoliageReplicator ), "Maximum distance from camera where foliage appears." ); + addField( "ViewClosest", TypeF32, Offset( mFieldData.mViewClosest, fxFoliageReplicator ), "Minimum distance from camera where foliage appears." ); + addField( "FadeInRegion", TypeF32, Offset( mFieldData.mFadeInRegion, fxFoliageReplicator ), "Region beyond ViewDistance where foliage fades in/out." ); + addField( "FadeOutRegion", TypeF32, Offset( mFieldData.mFadeOutRegion, fxFoliageReplicator ), "Region before ViewClosest where foliage fades in/out." ); + addField( "AlphaCutoff", TypeF32, Offset( mFieldData.mAlphaCutoff, fxFoliageReplicator ), "Minimum alpha value allowed on foliage instances." ); + addField( "GroundAlpha", TypeF32, Offset( mFieldData.mGroundAlpha, fxFoliageReplicator ), "Alpha of the foliage at ground level. 0 = transparent, 1 = opaque." ); + endGroup( "Culling" ); // MM: Added Group Footer. + + addGroup( "Animation" ); // MM: Added Group Header. + addField( "SwayOn", TypeBool, Offset( mFieldData.mSwayOn, fxFoliageReplicator ), "Foliage should sway randomly when true." ); + addField( "SwaySync", TypeBool, Offset( mFieldData.mSwaySync, fxFoliageReplicator ), "Foliage instances should sway together when true and SwayOn is enabled." ); + addField( "SwayMagSide", TypeF32, Offset( mFieldData.mSwayMagnitudeSide, fxFoliageReplicator ), "Left-to-right sway magnitude." ); + addField( "SwayMagFront", TypeF32, Offset( mFieldData.mSwayMagnitudeFront, fxFoliageReplicator ), "Front-to-back sway magnitude." ); + addField( "MinSwayTime", TypeF32, Offset( mFieldData.mMinSwayTime, fxFoliageReplicator ), "Minumum sway cycle time in seconds." ); + addField( "MaxSwayTime", TypeF32, Offset( mFieldData.mMaxSwayTime, fxFoliageReplicator ), "Maximum sway cycle time in seconds." ); + endGroup( "Animation" ); // MM: Added Group Footer. + + addGroup( "Lighting" ); // MM: Added Group Header. + addField( "LightOn", TypeBool, Offset( mFieldData.mLightOn, fxFoliageReplicator ), "Foliage should be illuminated with changing lights when true." ); + addField( "LightSync", TypeBool, Offset( mFieldData.mLightSync, fxFoliageReplicator ), "Foliage instances have the same lighting when set and LightOn is set." ); + addField( "MinLuminance", TypeF32, Offset( mFieldData.mMinLuminance, fxFoliageReplicator ), "Minimum luminance for foliage instances." ); + addField( "MaxLuminance", TypeF32, Offset( mFieldData.mMaxLuminance, fxFoliageReplicator ), "Maximum luminance for foliage instances." ); + addField( "LightTime", TypeF32, Offset( mFieldData.mLightTime, fxFoliageReplicator ), "Time before foliage illumination cycle repeats." ); + endGroup( "Lighting" ); // MM: Added Group Footer. + + addGroup( "Restrictions" ); // MM: Added Group Header. + addField( "AllowOnTerrain", TypeBool, Offset( mFieldData.mAllowOnTerrain, fxFoliageReplicator ), "Foliage will be placed on terrain when set." ); + addField( "AllowOnInteriors", TypeBool, Offset( mFieldData.mAllowOnInteriors, fxFoliageReplicator ), "Foliage will be placed on InteriorInstances when set." ); + addField( "AllowOnStatics", TypeBool, Offset( mFieldData.mAllowStatics, fxFoliageReplicator ), "Foliage will be placed on Static shapes when set." ); + addField( "AllowOnWater", TypeBool, Offset( mFieldData.mAllowOnWater, fxFoliageReplicator ), "Foliage will be placed on/under water when set." ); + addField( "AllowWaterSurface", TypeBool, Offset( mFieldData.mAllowWaterSurface, fxFoliageReplicator ), "Foliage will be placed on water when set. Requires AllowOnWater." ); + addField( "AllowedTerrainSlope", TypeS32, Offset( mFieldData.mAllowedTerrainSlope, fxFoliageReplicator ), "Maximum surface angle allowed for foliage instances." ); + endGroup( "Restrictions" ); // MM: Added Group Footer. + + // Initialise parents' persistent fields. + Parent::initPersistFields(); +} + +//------------------------------------------------------------------------------ + +void fxFoliageReplicator::CreateFoliage(void) +{ + F32 HypX, HypY; + F32 Angle; + U32 RelocationRetry; + Point3F FoliagePosition; + Point3F FoliageStart; + Point3F FoliageEnd; + Point3F FoliageScale; + bool CollisionResult; + RayInfo RayEvent; + + // Let's get a minimum bounding volume. + Point3F MinPoint( -0.5, -0.5, -0.5 ); + Point3F MaxPoint( 0.5, 0.5, 0.5 ); + + // Check Host. + AssertFatal(isClientObject(), "Trying to create Foliage on Server, this is bad!") + + // Cannot continue without Foliage Texture! + if (dStrlen(mFieldData.mFoliageFile) == 0) + return; + + // Check that we can position somewhere! + if (!( mFieldData.mAllowOnTerrain || + mFieldData.mAllowOnInteriors || + mFieldData.mAllowStatics || + mFieldData.mAllowOnWater)) + { + // Problem ... + Con::warnf(ConsoleLogEntry::General, "fxFoliageReplicator - Could not place Foliage, All alloweds are off!"); + + // Return here. + return; + } + + // Destroy Foliage if we've already got some. + if (mCurrentFoliageCount != 0) DestroyFoliage(); + + // Inform the user if culling has been disabled! + if (!mFieldData.mUseCulling) + { + // Console Output. + Con::printf("fxFoliageReplicator - Culling has been disabled!"); + } + + // ---------------------------------------------------------------------------------------------------------------------- + // > Calculate the Potential Foliage Nodes Required to achieve the selected culling resolution. + // > Populate Quad-tree structure to depth determined by culling resolution. + // + // A little explanation is called for here ... + // + // The approach to this problem has been choosen to make it *much* easier for + // the user to control the quad-tree culling resolution. The user enters a single + // world-space value 'mCullResolution' which controls the highest resolution at + // which the replicator will check visibility culling. + // + // example: If 'mCullResolution' is 32 and the size of the replicated area is 128 radius + // (256 diameter) then this results in the replicator creating a quad-tree where + // there are 256/32 = 8x8 blocks. Each of these can be checked to see if they + // reside within the viewing frustum and if not then they get culled therefore + // removing the need to parse all the billboards that occcupy that region. + // Most of the time you will get better than this as the culling algorithm will + // check the culling pyramid from the top to bottom e.g. the follow 'blocks' + // will be checked:- + // + // 1 x 256 x 256 (All of replicated area) + // 4 x 128 x 128 (4 corners of above) + // 16 x 64 x 64 (16 x 4 corners of above) + // etc. + // + // + // 1. First-up, the replicator needs to create a fixed-list of quad-tree nodes to work with. + // + // To calculate this we take the largest outer-radius value set in the replicator and + // calculate how many quad-tree levels are required to achieve the selected 'mCullResolution'. + // One of the initial problems is that the replicator has seperate radii values for X & Y. + // This can lead to a culling resolution smaller in one axis than the other if there is a + // difference between the Outer-Radii. Unfortunately, we just live with this as there is + // not much we can do here if we still want to allow the user to have this kind of + // elliptical placement control. + // + // To calculate the number of nodes needed we using the following equation:- + // + // Note:- We are changing the Logarithmic bases from 10 -> 2 ... grrrr! + // + // Cr = mCullResolution + // Rs = Maximum Radii Diameter + // + // + // ( Log10( Rs / Cr ) ) + // int ( ---------------- + 0.5 ) + // ( Log10( 2 ) ) + // + // ---------| + // | + // | n + // / 4 + // / + // ---------| + // n = 0 + // + // + // So basically we calculate the number of blocks in 1D at the highest resolution, then + // calculate the inverse exponential (base 2 - 1D) to achieve that quantity of blocks. + // We round that upto the next highest integer = e. We then sum 4 to the power 0->e + // which gives us the correct number of nodes required. e is also stored as the starting + // level value for populating the quad-tree (see 3. below). + // + // 2. We then proceed to calculate the billboard positions as normal and calculate and assign + // each billboard a basic volume (rather than treat each as a point). We need to take into + // account possible front/back swaying as well as the basic plane dimensions here. + // When all the billboards have been choosen we then proceed to populate the quad-tree. + // + // 3. To populate the quad-tree we start with a box which completely encapsulates the volume + // occupied by all the billboards and enter into a recursive procedure to process that node. + // Processing this node involves splitting it into quadrants in X/Y untouched (for now). + // We then find candidate billboards with each of these quadrants searching using the + // current subset of shapes from the parent (this reduces the searching to a minimum and + // is very efficient). + // + // If a quadrant does not enclose any billboards then the node is dropped otherwise it + // is processed again using the same procedure. + // + // This happens until we have recursed through the maximum number of levels as calculated + // using the summation max (see equation above). When level 0 is reached, the current list + // of enclosed objects is stored within the node (for the rendering algorithm). + // + // 4. When this is complete we have finished here. The next stage is when rendering takes place. + // An algorithm steps through the quad-tree from the top and does visibility culling on + // each box (with respect to the viewing frustum) and culls as appropriate. If the box is + // visible then the next level is checked until we reach level 0 where the node contains + // a complete subset of billboards enclosed by the visible box. + // + // + // Using the above algorithm we can now generate *massive* quantities of billboards and (using the + // appropriate 'mCullResolution') only visible blocks of billboards will be processed. + // + // - Melv. + // + // ---------------------------------------------------------------------------------------------------------------------- + + + + // ---------------------------------------------------------------------------------------------------------------------- + // Step 1. + // ---------------------------------------------------------------------------------------------------------------------- + + // Calculate the maximum dimension. + F32 MaxDimension = 2.0f * ( (mFieldData.mOuterRadiusX > mFieldData.mOuterRadiusY) ? mFieldData.mOuterRadiusX : mFieldData.mOuterRadiusY ); + + // Let's check that our cull resolution is not greater than half our maximum dimension (and less than 1). + if (mFieldData.mCullResolution > (MaxDimension/2) || mFieldData.mCullResolution < 8) + { + // Problem ... + Con::warnf(ConsoleLogEntry::General, "fxFoliageReplicator - Could create Foliage, invalid Culling Resolution!"); + Con::warnf(ConsoleLogEntry::General, "fxFoliageReplicator - Culling Resolution *must* be >=8 or <= %0.2f!", (MaxDimension/2)); + + // Return here. + return; + } + + // Take first Timestamp. + F32 mStartCreationTime = (F32) Platform::getRealMilliseconds(); + + // Calculate the quad-tree levels needed for selected 'mCullResolution'. + mQuadTreeLevels = (U32)(mCeil(mLog( MaxDimension / mFieldData.mCullResolution ) / mLog( 2.0f ))); + + // Calculate the number of potential nodes required. + mPotentialFoliageNodes = 0; + for (U32 n = 0; n <= mQuadTreeLevels; n++) + mPotentialFoliageNodes += (U32)(mCeil(mPow(4.0f, (F32) n))); // Ceil to be safe! + + // ---------------------------------------------------------------------------------------------------------------------- + // Step 2. + // ---------------------------------------------------------------------------------------------------------------------- + + // Set Seed. + RandomGen.setSeed(mFieldData.mSeed); + + // Add Foliage. + for (U32 idx = 0; idx < mFieldData.mFoliageCount; idx++) + { + fxFoliageItem* pFoliageItem; + Point3F FoliageOffsetPos; + + // Reset Relocation Retry. + RelocationRetry = mFieldData.mFoliageRetries; + + // Find it a home ... + do + { + // Get the fxFoliageReplicator Position. + FoliagePosition = getPosition(); + + // Calculate a random offset + HypX = RandomGen.randF((F32) mFieldData.mInnerRadiusX < mFieldData.mOuterRadiusX ? mFieldData.mInnerRadiusX : mFieldData.mOuterRadiusX, (F32) mFieldData.mOuterRadiusX); + HypY = RandomGen.randF((F32) mFieldData.mInnerRadiusY < mFieldData.mOuterRadiusY ? mFieldData.mInnerRadiusY : mFieldData.mOuterRadiusY, (F32) mFieldData.mOuterRadiusY); + Angle = RandomGen.randF(0, (F32) M_2PI); + + // Calcualte the new position. + FoliagePosition.x += HypX * mCos(Angle); + FoliagePosition.y += HypY * mSin(Angle); + + // Initialise RayCast Search Start/End Positions. + FoliageStart = FoliageEnd = FoliagePosition; + FoliageStart.z = 2000.f; + FoliageEnd.z= -2000.f; + + // Perform Ray Cast Collision on Client. + CollisionResult = gClientContainer.castRay( FoliageStart, FoliageEnd, FXFOLIAGEREPLICATOR_COLLISION_MASK, &RayEvent); + + // Did we hit anything? + if (CollisionResult) + { + // For now, let's pretend we didn't get a collision. + CollisionResult = false; + + // Yes, so get it's type. + U32 CollisionType = RayEvent.object->getTypeMask(); + + // Check Illegal Placements, fail if we hit a disallowed type. + if (((CollisionType & TerrainObjectType) && !mFieldData.mAllowOnTerrain) || + ((CollisionType & InteriorObjectType) && !mFieldData.mAllowOnInteriors) || + ((CollisionType & StaticShapeObjectType ) && !mFieldData.mAllowStatics) || + ((CollisionType & WaterObjectType) && !mFieldData.mAllowOnWater) ) continue; + + // If we collided with water and are not allowing on the water surface then let's find the + // terrain underneath and pass this on as the original collision else fail. + if ((CollisionType & WaterObjectType) && !mFieldData.mAllowWaterSurface && + !gClientContainer.castRay( FoliageStart, FoliageEnd, FXFOLIAGEREPLICATOR_NOWATER_COLLISION_MASK, &RayEvent)) continue; + + // We passed with flying colour so carry on. + CollisionResult = true; + } + + // Invalidate if we are below Allowed Terrain Angle. + if (RayEvent.normal.z < mSin(mDegToRad(90.0f-mFieldData.mAllowedTerrainSlope))) CollisionResult = false; + + // Wait until we get a collision. + } while(!CollisionResult && --RelocationRetry); + + // Check for Relocation Problem. + if (RelocationRetry > 0) + { + // Adjust Impact point. + RayEvent.point.z += mFieldData.mOffsetZ; + + // Set New Position. + FoliagePosition = RayEvent.point; + } + else + { + // Warning. + Con::warnf(ConsoleLogEntry::General, "fxFoliageReplicator - Could not find satisfactory position for Foliage!"); + + // Skip to next. + continue; + } + + // Monitor the total volume. + FoliageOffsetPos = FoliagePosition - getPosition(); + MinPoint.setMin(FoliageOffsetPos); + MaxPoint.setMax(FoliageOffsetPos); + + // Create our Foliage Item. + pFoliageItem = new fxFoliageItem; + + // Reset Frame Serial. + pFoliageItem->LastFrameSerialID = 0; + + // Reset Transform. + pFoliageItem->Transform.identity(); + + // Set Position. + pFoliageItem->Transform.setColumn(3, FoliagePosition); + + // Are we fixing size @ max? + if (mFieldData.mFixSizeToMax) + { + // Yes, so set height maximum height. + pFoliageItem->Height = mFieldData.mMaxHeight; + // Is the Aspect Ratio Fixed? + if (mFieldData.mFixAspectRatio) + // Yes, so lock to height. + pFoliageItem->Width = pFoliageItem->Height; + else + // No, so set width to maximum width. + pFoliageItem->Width = mFieldData.mMaxWidth; + } + else + { + // No, so choose a new Scale. + pFoliageItem->Height = RandomGen.randF(mFieldData.mMinHeight, mFieldData.mMaxHeight); + // Is the Aspect Ratio Fixed? + if (mFieldData.mFixAspectRatio) + // Yes, so lock to height. + pFoliageItem->Width = pFoliageItem->Height; + else + // No, so choose a random width. + pFoliageItem->Width = RandomGen.randF(mFieldData.mMinWidth, mFieldData.mMaxWidth); + } + + // Are we randomly flipping horizontally? + if (mFieldData.mRandomFlip) + // Yes, so choose a random flip for this object. + pFoliageItem->Flipped = (RandomGen.randF(0, 1000) < 500.0f) ? false : true; + else + // No, so turn-off flipping. + pFoliageItem->Flipped = false; + + // Calculate Foliage Item World Box. + // NOTE:- We generate a psuedo-volume here. It's basically the volume to which the + // plane can move and this includes swaying! + // + // Is Sway On? + if (mFieldData.mSwayOn) + { + // Yes, so take swaying into account... + pFoliageItem->FoliageBox.minExtents = FoliagePosition + + Point3F(-pFoliageItem->Width / 2.0f - mFieldData.mSwayMagnitudeSide, + -0.5f - mFieldData.mSwayMagnitudeFront, + pFoliageItem->Height ); + + pFoliageItem->FoliageBox.maxExtents = FoliagePosition + + Point3F(+pFoliageItem->Width / 2.0f + mFieldData.mSwayMagnitudeSide, + +0.5f + mFieldData.mSwayMagnitudeFront, + pFoliageItem->Height ); + } + else + { + // No, so give it a minimum volume... + pFoliageItem->FoliageBox.minExtents = FoliagePosition + + Point3F(-pFoliageItem->Width / 2.0f, + -0.5f, + pFoliageItem->Height ); + + pFoliageItem->FoliageBox.maxExtents = FoliagePosition + + Point3F(+pFoliageItem->Width / 2.0f, + +0.5f, + pFoliageItem->Height ); + } + + // Store Shape in Replicated Shapes Vector. + mReplicatedFoliage.push_back(pFoliageItem); + + // Increase Foliage Count. + mCurrentFoliageCount++; + } + + // Is Lighting On? + if (mFieldData.mLightOn) + { + // Yes, so reset Global Light phase. + mGlobalLightPhase = 0.0f; + // Set Global Light Time Ratio. + mGlobalLightTimeRatio = PeriodLenMinus / mFieldData.mLightTime; + + // Yes, so step through Foliage. + for (U32 idx = 0; idx < mCurrentFoliageCount; idx++) + { + fxFoliageItem* pFoliageItem; + + // Fetch the Foliage Item. + pFoliageItem = mReplicatedFoliage[idx]; + + // Do we have an item? + if (pFoliageItem) + { + // Yes, so are lights syncronised? + if (mFieldData.mLightSync) + { + pFoliageItem->LightTimeRatio = 1.0f; + pFoliageItem->LightPhase = 0.0f; + } + else + { + // No, so choose a random Light phase. + pFoliageItem->LightPhase = RandomGen.randF(0, PeriodLenMinus); + // Set Light Time Ratio. + pFoliageItem->LightTimeRatio = PeriodLenMinus / mFieldData.mLightTime; + } + } + } + + } + + // Is Swaying Enabled? + if (mFieldData.mSwayOn) + { + // Yes, so reset Global Sway phase. + mGlobalSwayPhase = 0.0f; + // Always set Global Sway Time Ratio. + mGlobalSwayTimeRatio = PeriodLenMinus / RandomGen.randF(mFieldData.mMinSwayTime, mFieldData.mMaxSwayTime); + + // Yes, so step through Foliage. + for (U32 idx = 0; idx < mCurrentFoliageCount; idx++) + { + fxFoliageItem* pFoliageItem; + + // Fetch the Foliage Item. + pFoliageItem = mReplicatedFoliage[idx]; + // Do we have an item? + if (pFoliageItem) + { + // Are we using Sway Sync? + if (mFieldData.mSwaySync) + { + pFoliageItem->SwayPhase = 0; + pFoliageItem->SwayTimeRatio = mGlobalSwayTimeRatio; + } + else + { + // No, so choose a random Sway phase. + pFoliageItem->SwayPhase = RandomGen.randF(0, PeriodLenMinus); + // Set to random Sway Time. + pFoliageItem->SwayTimeRatio = PeriodLenMinus / RandomGen.randF(mFieldData.mMinSwayTime, mFieldData.mMaxSwayTime); + } + } + } + } + + // Update our Object Volume. + mObjBox.minExtents.set(MinPoint); + mObjBox.maxExtents.set(MaxPoint); + setTransform(mObjToWorld); + + // ---------------------------------------------------------------------------------------------------------------------- + // Step 3. + // ---------------------------------------------------------------------------------------------------------------------- + + // Reset Next Allocated Node to Stack base. + mNextAllocatedNodeIdx = 0; + + // Allocate a new Node. + fxFoliageQuadrantNode* pNewNode = new fxFoliageQuadrantNode; + + // Store it in the Quad-tree. + mFoliageQuadTree.push_back(pNewNode); + + // Populate Initial Node. + // + // Set Start Level. + pNewNode->Level = mQuadTreeLevels; + // Calculate Total Foliage Area. + pNewNode->QuadrantBox = getWorldBox(); + // Reset Quadrant child nodes. + pNewNode->QuadrantChildNode[0] = + pNewNode->QuadrantChildNode[1] = + pNewNode->QuadrantChildNode[2] = + pNewNode->QuadrantChildNode[3] = NULL; + + // Create our initial cull list with *all* billboards into. + fxFoliageCulledList CullList; + CullList.mCulledObjectSet = mReplicatedFoliage; + + // Move to next node Index. + mNextAllocatedNodeIdx++; + + // Let's start this thing going by recursing it's children. + ProcessNodeChildren(pNewNode, &CullList); + + // Calculate Elapsed Time and take new Timestamp. + F32 ElapsedTime = (Platform::getRealMilliseconds() - mStartCreationTime) * 0.001f; + + // Console Output. + Con::printf("fxFoliageReplicator - Lev: %d PotNodes: %d Used: %d Objs: %d Time: %0.4fs.", + mQuadTreeLevels, + mPotentialFoliageNodes, + mNextAllocatedNodeIdx-1, + mBillboardsAcquired, + ElapsedTime); + + // Dump (*very*) approximate allocated memory. + F32 MemoryAllocated = (F32) ((mNextAllocatedNodeIdx-1) * sizeof(fxFoliageQuadrantNode)); + MemoryAllocated += mCurrentFoliageCount * sizeof(fxFoliageItem); + MemoryAllocated += mCurrentFoliageCount * sizeof(fxFoliageItem*); + Con::printf("fxFoliageReplicator - Approx. %0.2fMb allocated.", MemoryAllocated / 1048576.0f); + + // ---------------------------------------------------------------------------------------------------------------------- + + SetupBuffers(); + + // Take first Timestamp. + mLastRenderTime = Platform::getVirtualMilliseconds(); +} + +void fxFoliageReplicator::SetupShader() +{ + if ( !mShaderData ) + { + if ( !Sim::findObject( "fxFoliageReplicatorShader", mShaderData ) ) + { + Con::errorf( "fxFoliageReplicator::SetupShader - could not find ShaderData named fxFoliageReplicatorShader" ); + return; + } + } + + Vector macros; + if ( mFieldData.mUseTrueBillboards ) + macros.push_back( GFXShaderMacro( "TRUE_BILLBOARD" ) ); + + mShader = mShaderData->getShader( macros ); + + if ( !mShader ) + return; + + + mFoliageShaderConsts = mShader->allocConstBuffer(); + + mFoliageShaderProjectionSC = mShader->getShaderConstHandle( "$projection" ); + mFoliageShaderWorldSC = mShader->getShaderConstHandle( "$world" ); + mFoliageShaderGlobalSwayPhaseSC = mShader->getShaderConstHandle( "$GlobalSwayPhase" ); + mFoliageShaderSwayMagnitudeSideSC = mShader->getShaderConstHandle( "$SwayMagnitudeSide" ); + mFoliageShaderSwayMagnitudeFrontSC = mShader->getShaderConstHandle( "$SwayMagnitudeFront" ); + mFoliageShaderGlobalLightPhaseSC = mShader->getShaderConstHandle( "$GlobalLightPhase" ); + mFoliageShaderLuminanceMagnitudeSC = mShader->getShaderConstHandle( "$LuminanceMagnitude" ); + mFoliageShaderLuminanceMidpointSC = mShader->getShaderConstHandle( "$LuminanceMidpoint" ); + mFoliageShaderDistanceRangeSC = mShader->getShaderConstHandle( "$DistanceRange" ); + mFoliageShaderCameraPosSC = mShader->getShaderConstHandle( "$CameraPos" ); + mFoliageShaderTrueBillboardSC = mShader->getShaderConstHandle( "$TrueBillboard" ); + mFoliageShaderGroundAlphaSC = mShader->getShaderConstHandle( "$groundAlpha" ); + mFoliageShaderAmbientColorSC = mShader->getShaderConstHandle( "$ambient" ); + mDiffuseTextureSC = mShader->getShaderConstHandle( "$diffuseMap" ); + mAlphaMapTextureSC = mShader->getShaderConstHandle( "$alphaMap" ); +} + +// Ok, what we do is let the older code setup the FoliageItem list and the QuadTree. +// Then we build the Vertex and Primitive buffers here. It would probably be +// slightly more memory efficient to build the buffers directly, but we +// want to sort the items within the buffer by the quadtreenodes +void fxFoliageReplicator::SetupBuffers() +{ + // Following two arrays are used to build the vertex and primitive buffers. + Point3F basePoints[8]; + basePoints[0] = Point3F(-0.5f, 0.0f, 1.0f); + basePoints[1] = Point3F(-0.5f, 0.0f, 0.0f); + basePoints[2] = Point3F(0.5f, 0.0f, 0.0f); + basePoints[3] = Point3F(0.5f, 0.0f, 1.0f); + + Point2F texCoords[4]; + texCoords[0] = Point2F(0.0, 0.0); + texCoords[1] = Point2F(0.0, 1.0); + texCoords[2] = Point2F(1.0, 1.0); + texCoords[3] = Point2F(1.0, 0.0); + + // Init our Primitive Buffer + U32 indexSize = mFieldData.mFoliageCount * 6; + U16* indices = new U16[indexSize]; + // Two triangles per particle + for (U16 i = 0; i < mFieldData.mFoliageCount; i++) { + U16* idx = &indices[i*6]; // hey, no offset math below, neat + U16 vertOffset = i*4; + idx[0] = vertOffset + 0; + idx[1] = vertOffset + 1; + idx[2] = vertOffset + 2; + idx[3] = vertOffset + 2; + idx[4] = vertOffset + 3; + idx[5] = vertOffset + 0; + } + // Init the prim buffer and copy our indexes over + U16 *ibIndices; + mPrimBuffer.set(GFX, indexSize, 0, GFXBufferTypeStatic); + mPrimBuffer.lock(&ibIndices); + dMemcpy(ibIndices, indices, indexSize * sizeof(U16)); + mPrimBuffer.unlock(); + delete[] indices; + + // Now, let's init the vertex buffer + U32 currPrimitiveStartIndex = 0; + mVertexBuffer.set(GFX, mFieldData.mFoliageCount * 4, GFXBufferTypeStatic); + mVertexBuffer.lock(); + U32 idx = 0; + for (S32 qtIdx = 0; qtIdx < mFoliageQuadTree.size(); qtIdx++) { + fxFoliageQuadrantNode* quadNode = mFoliageQuadTree[qtIdx]; + if (quadNode->Level == 0) { + quadNode->startIndex = currPrimitiveStartIndex; + quadNode->primitiveCount = 0; + // Ok, there should be data in here! + for (S32 i = 0; i < quadNode->RenderList.size(); i++) { + fxFoliageItem* pFoliageItem = quadNode->RenderList[i]; + if (pFoliageItem->LastFrameSerialID == 0) { + pFoliageItem->LastFrameSerialID++; + // Dump it into the vertex buffer + for (U32 vertIndex = 0; vertIndex < 4; vertIndex++) { + GFXVertexFoliage *vert = &mVertexBuffer[(idx*4) + vertIndex]; + // This is the position of the billboard. + vert->point = pFoliageItem->Transform.getPosition(); + // Normal contains the point of the billboard (except for the y component, see below) + vert->normal = basePoints[vertIndex]; + + vert->normal.x *= pFoliageItem->Width; + vert->normal.z *= pFoliageItem->Height; + // Handle texture coordinates + vert->texCoord = texCoords[vertIndex]; + if (pFoliageItem->Flipped) + vert->texCoord.x = 1.0f - vert->texCoord.x; + // Handle sway. Sway is stored in a texture coord. The x coordinate is the sway phase multiplier, + // the y coordinate determines if this vertex actually sways or not. + if ((vertIndex == 0) || (vertIndex == 3)) { + vert->texCoord2.set(pFoliageItem->SwayTimeRatio / mGlobalSwayTimeRatio, 1.0f); + } else { + vert->texCoord2.set(0.0f, 0.0f); + } + // Handle lighting, lighting happens at the same time as global so this is just an offset. + vert->normal.y = pFoliageItem->LightPhase; + } + idx++; + quadNode->primitiveCount += 2; + currPrimitiveStartIndex += 6; + } + } + } + } + mVertexBuffer.unlock(); + + DestroyFoliageItems(); +} + +//------------------------------------------------------------------------------ + +Box3F fxFoliageReplicator::FetchQuadrant(Box3F Box, U32 Quadrant) +{ + Box3F QuadrantBox; + + // Select Quadrant. + switch(Quadrant) + { + // UL. + case 0: + QuadrantBox.minExtents = Box.minExtents + Point3F(0, Box.len_y()/2, 0); + QuadrantBox.maxExtents = QuadrantBox.minExtents + Point3F(Box.len_x()/2, Box.len_y()/2, Box.len_z()); + break; + + // UR. + case 1: + QuadrantBox.minExtents = Box.minExtents + Point3F(Box.len_x()/2, Box.len_y()/2, 0); + QuadrantBox.maxExtents = QuadrantBox.minExtents + Point3F(Box.len_x()/2, Box.len_y()/2, Box.len_z()); + break; + + // LL. + case 2: + QuadrantBox.minExtents = Box.minExtents; + QuadrantBox.maxExtents = QuadrantBox.minExtents + Point3F(Box.len_x()/2, Box.len_y()/2, Box.len_z()); + break; + + // LR. + case 3: + QuadrantBox.minExtents = Box.minExtents + Point3F(Box.len_x()/2, 0, 0); + QuadrantBox.maxExtents = QuadrantBox.minExtents + Point3F(Box.len_x()/2, Box.len_y()/2, Box.len_z()); + break; + + default: + return Box; + } + + return QuadrantBox; +} + +//------------------------------------------------------------------------------ + +void fxFoliageReplicator::ProcessNodeChildren(fxFoliageQuadrantNode* pParentNode, fxFoliageCulledList* pCullList) +{ + // --------------------------------------------------------------- + // Split Node into Quadrants and Process each. + // --------------------------------------------------------------- + + // Process All Quadrants (UL/UR/LL/LR). + for (U32 q = 0; q < 4; q++) + ProcessQuadrant(pParentNode, pCullList, q); +} + +//------------------------------------------------------------------------------ + +void fxFoliageReplicator::ProcessQuadrant(fxFoliageQuadrantNode* pParentNode, fxFoliageCulledList* pCullList, U32 Quadrant) +{ + // Fetch Quadrant Box. + const Box3F QuadrantBox = FetchQuadrant(pParentNode->QuadrantBox, Quadrant); + + // Create our new Cull List. + fxFoliageCulledList CullList(QuadrantBox, pCullList); + + // Did we get any objects? + if (CullList.GetListCount() > 0) + { + // Yes, so allocate a new Node. + fxFoliageQuadrantNode* pNewNode = new fxFoliageQuadrantNode; + + // Store it in the Quad-tree. + mFoliageQuadTree.push_back(pNewNode); + + // Move to next node Index. + mNextAllocatedNodeIdx++; + + // Populate Quadrant Node. + // + // Next Sub-level. + pNewNode->Level = pParentNode->Level - 1; + // Calculate Quadrant Box. + pNewNode->QuadrantBox = QuadrantBox; + // Reset Child Nodes. + pNewNode->QuadrantChildNode[0] = + pNewNode->QuadrantChildNode[1] = + pNewNode->QuadrantChildNode[2] = + pNewNode->QuadrantChildNode[3] = NULL; + + // Put a reference in parent. + pParentNode->QuadrantChildNode[Quadrant] = pNewNode; + + // If we're not at sub-level 0 then process this nodes children. + if (pNewNode->Level != 0) ProcessNodeChildren(pNewNode, &CullList); + // If we've reached sub-level 0 then store Cull List (for rendering). + if (pNewNode->Level == 0) + { + // Store the render list from our culled object set. + pNewNode->RenderList = CullList.mCulledObjectSet; + // Keep track of the total billboard acquired. + mBillboardsAcquired += CullList.GetListCount(); + } + } +} + +//------------------------------------------------------------------------------ + +void fxFoliageReplicator::SyncFoliageReplicators(void) +{ + // Check Host. + AssertFatal(isServerObject(), "We *MUST* be on server when Synchronising Foliage!") + + // Find the Replicator Set. + SimSet *fxFoliageSet = dynamic_cast(Sim::findObject("fxFoliageSet")); + + // Return if Error. + if (!fxFoliageSet) + { + // Console Warning. + Con::warnf("fxFoliageReplicator - Cannot locate the 'fxFoliageSet', this is bad!"); + // Return here. + return; + } + + // Parse Replication Object(s). + for (SimSetIterator itr(fxFoliageSet); *itr; ++itr) + { + // Fetch the Replicator Object. + fxFoliageReplicator* Replicator = static_cast(*itr); + // Set Foliage Replication Mask. + if (Replicator->isServerObject()) + { + Con::printf("fxFoliageReplicator - Restarting fxFoliageReplicator Object..."); + Replicator->setMaskBits(FoliageReplicationMask); + } + } + + // Info ... + Con::printf("fxFoliageReplicator - Client Foliage Sync has completed."); +} + + +//------------------------------------------------------------------------------ +// Lets chill our memory requirements out a little +void fxFoliageReplicator::DestroyFoliageItems() +{ + // Remove shapes. + for (S32 idx = 0; idx < mReplicatedFoliage.size(); idx++) + { + fxFoliageItem* pFoliageItem; + + // Fetch the Foliage Item. + pFoliageItem = mReplicatedFoliage[idx]; + + // Delete Shape. + if (pFoliageItem) delete pFoliageItem; + } + // Clear the Replicated Foliage Vector. + mReplicatedFoliage.clear(); + + // Clear out old references also + for (S32 qtIdx = 0; qtIdx < mFoliageQuadTree.size(); qtIdx++) { + fxFoliageQuadrantNode* quadNode = mFoliageQuadTree[qtIdx]; + if (quadNode->Level == 0) { + quadNode->RenderList.clear(); + } + } +} + +void fxFoliageReplicator::DestroyFoliage(void) +{ + // Check Host. + AssertFatal(isClientObject(), "Trying to destroy Foliage on Server, this is bad!") + + // Destroy Quad-tree. + mPotentialFoliageNodes = 0; + // Reset Billboards Acquired. + mBillboardsAcquired = 0; + + // Finish if we didn't create any shapes. + if (mCurrentFoliageCount == 0) return; + + DestroyFoliageItems(); + + // Let's remove the Quad-Tree allocations. + for ( Vector::iterator QuadNodeItr = mFoliageQuadTree.begin(); + QuadNodeItr != mFoliageQuadTree.end(); + QuadNodeItr++ ) + { + // Remove the node. + delete *QuadNodeItr; + } + + // Clear the Foliage Quad-Tree Vector. + mFoliageQuadTree.clear(); + + // Clear the Frustum Render Set Vector. + mFrustumRenderSet.mVisObjectSet.clear(); + + // Reset Foliage Count. + mCurrentFoliageCount = 0; +} + +//------------------------------------------------------------------------------ + +void fxFoliageReplicator::StartUp(void) +{ + // Flag, Client Replication Started. + mClientReplicationStarted = true; + + // Create foliage on Client. + if (isClientObject()) CreateFoliage(); +} + +//------------------------------------------------------------------------------ + +bool fxFoliageReplicator::onAdd() +{ + if(!Parent::onAdd()) return(false); + + // Add the Replicator to the Replicator Set. + dynamic_cast(Sim::findObject("fxFoliageSet"))->addObject(this); + + // Set Default Object Box. + mObjBox.minExtents.set( -0.5, -0.5, -0.5 ); + mObjBox.maxExtents.set( 0.5, 0.5, 0.5 ); + resetWorldBox(); + setRenderTransform(mObjToWorld); + + // Add to Scene. + addToScene(); + + // Are we on the client? + if ( isClientObject() ) + { + // Yes, so load foliage texture. + if( mFieldData.mFoliageFile != NULL && dStrlen(mFieldData.mFoliageFile) > 0 ) + mFieldData.mFoliageTexture = GFXTexHandle( mFieldData.mFoliageFile, &GFXDefaultStaticDiffuseProfile, avar("%s() - mFieldData.mFoliageTexture (line %d)", __FUNCTION__, __LINE__) ); + + if ((GFXTextureObject*) mFieldData.mFoliageTexture == NULL) + Con::printf("fxFoliageReplicator: %s is an invalid or missing foliage texture file.", mFieldData.mFoliageFile); + + mAlphaLookup = new GBitmap(AlphaTexLen, 1); + computeAlphaTex(); + + // Register for notification when GhostAlways objects are done loading + NetConnection::smGhostAlwaysDone.notify( this, &fxFoliageReplicator::onGhostAlwaysDone ); + + SetupShader(); + } + + // Return OK. + return(true); +} + +//------------------------------------------------------------------------------ + +void fxFoliageReplicator::onRemove() +{ + // Remove the Replicator from the Replicator Set. + dynamic_cast(Sim::findObject("fxFoliageSet"))->removeObject(this); + + NetConnection::smGhostAlwaysDone.remove( this, &fxFoliageReplicator::onGhostAlwaysDone ); + + // Remove from Scene. + removeFromScene(); + + // Are we on the Client? + if (isClientObject()) + { + // Yes, so destroy Foliage. + DestroyFoliage(); + + // Remove Texture. + mFieldData.mFoliageTexture = NULL; + + mShader = NULL; + } + + // Do Parent. + Parent::onRemove(); +} + +//------------------------------------------------------------------------------ + +void fxFoliageReplicator::onGhostAlwaysDone() +{ + if ( isClientObject() ) + CreateFoliage(); +} + +//------------------------------------------------------------------------------ + +void fxFoliageReplicator::inspectPostApply() +{ + // Set Parent. + Parent::inspectPostApply(); + + // Set Foliage Replication Mask (this object only). + setMaskBits(FoliageReplicationMask); + + mDirty = true; +} + +//------------------------------------------------------------------------------ + +DefineEngineFunction(StartFoliageReplication, void,(),, "Activates the foliage replicator.\n" + "@tsexample\n" + "// Call the function\n" + "StartFoliageReplication();\n" + "@endtsexample\n" + "@ingroup Foliage") +{ + // Find the Replicator Set. + SimSet *fxFoliageSet = dynamic_cast(Sim::findObject("fxFoliageSet")); + + // Return if Error. + if (!fxFoliageSet) + { + // Console Warning. + Con::warnf("fxFoliageReplicator - Cannot locate the 'fxFoliageSet', this is bad!"); + // Return here. + return; + } + + // Parse Replication Object(s). + U32 startupCount = 0; + for (SimSetIterator itr(fxFoliageSet); *itr; ++itr) + { + // Fetch the Replicator Object. + fxFoliageReplicator* Replicator = static_cast(*itr); + + // Start Client Objects Only. + if (Replicator->isClientObject()) + { + Replicator->StartUp(); + startupCount++; + } + } + + // Info ... + Con::printf("fxFoliageReplicator - replicated client foliage for %d objects", startupCount); +} + +//------------------------------------------------------------------------------ + +void fxFoliageReplicator::prepRenderImage( SceneRenderState* state ) +{ + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind(this, &fxFoliageReplicator::renderObject); + ri->type = RenderPassManager::RIT_Foliage; + state->getRenderPass()->addInst( ri ); +} + +// +// RENDERING +// +void fxFoliageReplicator::computeAlphaTex() +{ + // Distances used in alpha + const F32 ClippedViewDistance = mFieldData.mViewDistance; + const F32 MaximumViewDistance = ClippedViewDistance + mFieldData.mFadeInRegion; + + // This is used for the alpha computation in the shader. + for (U32 i = 0; i < AlphaTexLen; i++) { + F32 Distance = ((float) i / (float) AlphaTexLen) * MaximumViewDistance; + F32 ItemAlpha = 1.0f; + // Are we fading out? + if (Distance < mFieldData.mViewClosest) + { + // Yes, so set fade-out. + ItemAlpha = 1.0f - ((mFieldData.mViewClosest - Distance) * mFadeOutGradient); + } + // No, so are we fading in? + else if (Distance > ClippedViewDistance) + { + // Yes, so set fade-in + ItemAlpha = 1.0f - ((Distance - ClippedViewDistance) * mFadeInGradient); + } + + // Set texture info + ColorI c((U8) (255.0f * ItemAlpha), 0, 0); + mAlphaLookup->setColor(i, 0, c); + } + mAlphaTexture.set(mAlphaLookup, &GFXDefaultStaticDiffuseProfile, false, String("fxFoliage Replicator Alpha Texture") ); +} + +// Renders a triangle stripped oval +void fxFoliageReplicator::renderArc(const F32 fRadiusX, const F32 fRadiusY) +{ + PrimBuild::begin(GFXTriangleStrip, 720); + for (U32 Angle = mCreationAreaAngle; Angle < (mCreationAreaAngle+360); Angle++) + { + F32 XPos, YPos; + + // Calculate Position. + XPos = fRadiusX * mCos(mDegToRad(-(F32)Angle)); + YPos = fRadiusY * mSin(mDegToRad(-(F32)Angle)); + + // Set Colour. + PrimBuild::color4f(mFieldData.mPlaceAreaColour.red, + mFieldData.mPlaceAreaColour.green, + mFieldData.mPlaceAreaColour.blue, + AREA_ANIMATION_ARC * (Angle-mCreationAreaAngle)); + + PrimBuild::vertex3f(XPos, YPos, -(F32)mFieldData.mPlacementBandHeight/2.0f); + PrimBuild::vertex3f(XPos, YPos, +(F32)mFieldData.mPlacementBandHeight/2.0f); + } + PrimBuild::end(); +} + +// This currently uses the primbuilder, could convert out, but why allocate the buffer if we +// never edit the misison? +void fxFoliageReplicator::renderPlacementArea(const F32 ElapsedTime) +{ + if (gEditingMission && mFieldData.mShowPlacementArea) + { + GFX->pushWorldMatrix(); + GFX->multWorld(getTransform()); + + if (!mPlacementSB) + { + GFXStateBlockDesc transparent; + transparent.setCullMode(GFXCullNone); + transparent.alphaTestEnable = true; + transparent.setZReadWrite(true); + transparent.zWriteEnable = false; + transparent.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha); + mPlacementSB = GFX->createStateBlock( transparent ); + } + + GFX->setStateBlock(mPlacementSB); + + // Do we need to draw the Outer Radius? + if (mFieldData.mOuterRadiusX || mFieldData.mOuterRadiusY) + renderArc((F32) mFieldData.mOuterRadiusX, (F32) mFieldData.mOuterRadiusY); + // Inner radius? + if (mFieldData.mInnerRadiusX || mFieldData.mInnerRadiusY) + renderArc((F32) mFieldData.mInnerRadiusX, (F32) mFieldData.mInnerRadiusY); + + GFX->popWorldMatrix(); + mCreationAreaAngle = (U32)(mCreationAreaAngle + (1000 * ElapsedTime)); + mCreationAreaAngle = mCreationAreaAngle % 360; + } +} + +void fxFoliageReplicator::renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* overrideMat) +{ + if (overrideMat) + return; + + if ( !mShader ) + return; + + // If we're rendering and we haven't placed any foliage yet - do it. + if(!mClientReplicationStarted) + { + Con::warnf("fxFoliageReplicator::renderObject - tried to render a non replicated fxFoliageReplicator; replicating it now..."); + + StartUp(); + } + + // Calculate Elapsed Time and take new Timestamp. + S32 Time = Platform::getVirtualMilliseconds(); + F32 ElapsedTime = (Time - mLastRenderTime) * 0.001f; + mLastRenderTime = Time; + + renderPlacementArea(ElapsedTime); + + if (mCurrentFoliageCount > 0) { + + if ( mRenderSB.isNull() || mDirty) + { + mDirty = false; + + GFXStateBlockDesc desc; + + // Debug SB + desc.samplersDefined = true; + desc.samplers[0].textureColorOp = GFXTOPDisable; + desc.samplers[1].textureColorOp = GFXTOPDisable; + + mDebugSB = GFX->createStateBlock(desc); + + // Render SB + desc.samplers[0].textureColorOp = GFXTOPModulate; + desc.samplers[1].textureColorOp = GFXTOPModulate; + desc.samplers[1].addressModeU = GFXAddressClamp; + desc.samplers[1].addressModeV = GFXAddressClamp; + + desc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha); + desc.setAlphaTest(true, GFXCmpGreater, (U8) (255.0f * mFieldData.mAlphaCutoff)); + desc.setCullMode(GFXCullNone); + + mRenderSB = GFX->createStateBlock(desc); + } + + if (!mFieldData.mHideFoliage) { + // Animate Global Sway Phase (Modulus). + mGlobalSwayPhase = mGlobalSwayPhase + (mGlobalSwayTimeRatio * ElapsedTime); + + // Animate Global Light Phase (Modulus). + mGlobalLightPhase = mGlobalLightPhase + (mGlobalLightTimeRatio * ElapsedTime); + + // Compute other light parameters + const F32 LuminanceMidPoint = (mFieldData.mMinLuminance + mFieldData.mMaxLuminance) / 2.0f; + const F32 LuminanceMagnitude = mFieldData.mMaxLuminance - LuminanceMidPoint; + + // Distances used in alpha + const F32 ClippedViewDistance = mFieldData.mViewDistance; + const F32 MaximumViewDistance = ClippedViewDistance + mFieldData.mFadeInRegion; + + if (mFoliageShaderConsts.isValid()) + { + mFoliageShaderConsts->setSafe(mFoliageShaderGlobalSwayPhaseSC, mGlobalSwayPhase); + mFoliageShaderConsts->setSafe(mFoliageShaderSwayMagnitudeSideSC, mFieldData.mSwayMagnitudeSide); + mFoliageShaderConsts->setSafe(mFoliageShaderSwayMagnitudeFrontSC, mFieldData.mSwayMagnitudeFront); + mFoliageShaderConsts->setSafe(mFoliageShaderGlobalLightPhaseSC, mGlobalLightPhase); + mFoliageShaderConsts->setSafe(mFoliageShaderLuminanceMagnitudeSC, LuminanceMagnitude); + mFoliageShaderConsts->setSafe(mFoliageShaderLuminanceMidpointSC, LuminanceMidPoint); + + // Set up our shader constants + // Projection matrix + MatrixF proj = GFX->getProjectionMatrix(); + //proj.transpose(); + mFoliageShaderConsts->setSafe(mFoliageShaderProjectionSC, proj); + + // World transform matrix + MatrixF world = GFX->getWorldMatrix(); + //world.transpose(); + + mFoliageShaderConsts->setSafe(mFoliageShaderWorldSC, world); + + Point3F camPos = state->getCameraPosition(); + + mFoliageShaderConsts->setSafe(mFoliageShaderDistanceRangeSC, MaximumViewDistance); + mFoliageShaderConsts->setSafe(mFoliageShaderCameraPosSC, camPos); + mFoliageShaderConsts->setSafe(mFoliageShaderTrueBillboardSC, mFieldData.mUseTrueBillboards ? 1.0f : 0.0f ); + mFoliageShaderConsts->setSafe(mFoliageShaderGroundAlphaSC, Point4F(mFieldData.mGroundAlpha, mFieldData.mGroundAlpha, mFieldData.mGroundAlpha, mFieldData.mGroundAlpha)); + + if (mFoliageShaderAmbientColorSC->isValid()) + mFoliageShaderConsts->set(mFoliageShaderAmbientColorSC, state->getAmbientLightColor()); + + GFX->setShaderConstBuffer(mFoliageShaderConsts); + + } + + // Blend ops + // Set up our texture and color ops. + + GFX->setStateBlock(mRenderSB); + GFX->setShader( mShader ); + + GFX->setTexture(mDiffuseTextureSC->getSamplerRegister(), mFieldData.mFoliageTexture); + // computeAlphaTex(); // Uncomment if we figure out how to clamp to fogAndHaze + GFX->setTexture(mAlphaMapTextureSC->getSamplerRegister(), mAlphaTexture); + + // Setup our buffers + GFX->setVertexBuffer(mVertexBuffer); + GFX->setPrimitiveBuffer(mPrimBuffer); + + // If we use culling, we're going to send chunks of our buffers to the card + if (mFieldData.mUseCulling) + { + // Setup the Clip-Planes. + F32 FarClipPlane = getMin((F32)state->getFarPlane(), + mFieldData.mViewDistance + mFieldData.mFadeInRegion); + mFrustumRenderSet.SetupClipPlanes(state, FarClipPlane); + + renderQuad(mFoliageQuadTree[0], getRenderTransform(), false); + + // Multipass, don't want to interrupt the vb state + if (mFieldData.mUseDebugInfo) + { + // hey man, we're done, so it doesn't matter if we kill it to render the next part + GFX->setStateBlock(mDebugSB); + renderQuad(mFoliageQuadTree[0], getRenderTransform(), true); + } + } + else + { + // Draw the whole shebang! + GFX->drawIndexedPrimitive(GFXTriangleList, 0, 0, mVertexBuffer->mNumVerts, + 0, mPrimBuffer->mIndexCount / 3); + } + } + } +} + +void fxFoliageReplicator::renderQuad(fxFoliageQuadrantNode* quadNode, const MatrixF& RenderTransform, const bool UseDebug) +{ + if (quadNode != NULL) { + if (mFrustumRenderSet.IsQuadrantVisible(quadNode->QuadrantBox, RenderTransform)) + { + // Draw the Quad Box (Debug Only). + if (UseDebug) + mFrustumRenderSet.DrawQuadBox(quadNode->QuadrantBox, ColorF(0.0f, 1.0f, 0.1f, 1.0f)); + if (quadNode->Level != 0) { + for (U32 i = 0; i < 4; i++) + renderQuad(quadNode->QuadrantChildNode[i], RenderTransform, UseDebug); + } else { + if (!UseDebug) + if(quadNode->primitiveCount) + GFX->drawIndexedPrimitive(GFXTriangleList, 0, 0, mVertexBuffer->mNumVerts, + quadNode->startIndex, quadNode->primitiveCount); + } + } else { + // Use a different color to say "I think I'm not visible!" + if (UseDebug) + mFrustumRenderSet.DrawQuadBox(quadNode->QuadrantBox, ColorF(1.0f, 0.8f, 0.1f, 1.0f)); + } + } +} + +//------------------------------------------------------------------------------ +// NETWORK +//------------------------------------------------------------------------------ + +U32 fxFoliageReplicator::packUpdate(NetConnection * con, U32 mask, BitStream * stream) +{ + // Pack Parent. + U32 retMask = Parent::packUpdate(con, mask, stream); + + // Write Foliage Replication Flag. + if (stream->writeFlag(mask & FoliageReplicationMask)) + { + stream->writeAffineTransform(mObjToWorld); // Foliage Master-Object Position. + + stream->writeFlag(mFieldData.mUseDebugInfo); // Foliage Debug Information Flag. + stream->write(mFieldData.mDebugBoxHeight); // Foliage Debug Height. + stream->write(mFieldData.mSeed); // Foliage Seed. + stream->write(mFieldData.mFoliageCount); // Foliage Count. + stream->write(mFieldData.mFoliageRetries); // Foliage Retries. + stream->writeString(mFieldData.mFoliageFile); // Foliage File. + + stream->write(mFieldData.mInnerRadiusX); // Foliage Inner Radius X. + stream->write(mFieldData.mInnerRadiusY); // Foliage Inner Radius Y. + stream->write(mFieldData.mOuterRadiusX); // Foliage Outer Radius X. + stream->write(mFieldData.mOuterRadiusY); // Foliage Outer Radius Y. + + stream->write(mFieldData.mMinWidth); // Foliage Minimum Width. + stream->write(mFieldData.mMaxWidth); // Foliage Maximum Width. + stream->write(mFieldData.mMinHeight); // Foliage Minimum Height. + stream->write(mFieldData.mMaxHeight); // Foliage Maximum Height. + stream->write(mFieldData.mFixAspectRatio); // Foliage Fix Aspect Ratio. + stream->write(mFieldData.mFixSizeToMax); // Foliage Fix Size to Max. + stream->write(mFieldData.mOffsetZ); // Foliage Offset Z. + stream->writeFlag(mFieldData.mRandomFlip); // Foliage Random Flip. + stream->writeFlag(mFieldData.mUseTrueBillboards); // Foliage faces the camera (including z axis) + + stream->write(mFieldData.mUseCulling); // Foliage Use Culling. + stream->write(mFieldData.mCullResolution); // Foliage Cull Resolution. + stream->write(mFieldData.mViewDistance); // Foliage View Distance. + stream->write(mFieldData.mViewClosest); // Foliage View Closest. + stream->write(mFieldData.mFadeInRegion); // Foliage Fade-In Region. + stream->write(mFieldData.mFadeOutRegion); // Foliage Fade-Out Region. + stream->write(mFieldData.mAlphaCutoff); // Foliage Alpha Cutoff. + stream->write(mFieldData.mGroundAlpha); // Foliage Ground Alpha. + + stream->writeFlag(mFieldData.mSwayOn); // Foliage Sway On Flag. + stream->writeFlag(mFieldData.mSwaySync); // Foliage Sway Sync Flag. + stream->write(mFieldData.mSwayMagnitudeSide); // Foliage Sway Magnitude Side2Side. + stream->write(mFieldData.mSwayMagnitudeFront); // Foliage Sway Magnitude Front2Back. + stream->write(mFieldData.mMinSwayTime); // Foliage Minimum Sway Time. + stream->write(mFieldData.mMaxSwayTime); // Foliage Maximum way Time. + + stream->writeFlag(mFieldData.mLightOn); // Foliage Light On Flag. + stream->writeFlag(mFieldData.mLightSync); // Foliage Light Sync + stream->write(mFieldData.mMinLuminance); // Foliage Minimum Luminance. + stream->write(mFieldData.mMaxLuminance); // Foliage Maximum Luminance. + stream->write(mFieldData.mLightTime); // Foliage Light Time. + + stream->writeFlag(mFieldData.mAllowOnTerrain); // Allow on Terrain. + stream->writeFlag(mFieldData.mAllowOnInteriors); // Allow on Interiors. + stream->writeFlag(mFieldData.mAllowStatics); // Allow on Statics. + stream->writeFlag(mFieldData.mAllowOnWater); // Allow on Water. + stream->writeFlag(mFieldData.mAllowWaterSurface); // Allow on Water Surface. + stream->write(mFieldData.mAllowedTerrainSlope); // Foliage Offset Z. + + stream->writeFlag(mFieldData.mHideFoliage); // Hide Foliage. + stream->writeFlag(mFieldData.mShowPlacementArea); // Show Placement Area Flag. + stream->write(mFieldData.mPlacementBandHeight); // Placement Area Height. + stream->write(mFieldData.mPlaceAreaColour); // Placement Area Colour. + } + + // Were done ... + return(retMask); +} + +//------------------------------------------------------------------------------ + +void fxFoliageReplicator::unpackUpdate(NetConnection * con, BitStream * stream) +{ + // Unpack Parent. + Parent::unpackUpdate(con, stream); + + // Read Replication Details. + if(stream->readFlag()) + { + MatrixF ReplicatorObjectMatrix; + + stream->readAffineTransform(&ReplicatorObjectMatrix); // Foliage Master Object Position. + + mFieldData.mUseDebugInfo = stream->readFlag(); // Foliage Debug Information Flag. + stream->read(&mFieldData.mDebugBoxHeight); // Foliage Debug Height. + stream->read(&mFieldData.mSeed); // Foliage Seed. + stream->read(&mFieldData.mFoliageCount); // Foliage Count. + stream->read(&mFieldData.mFoliageRetries); // Foliage Retries. + mFieldData.mFoliageFile = stream->readSTString(); // Foliage File. + + stream->read(&mFieldData.mInnerRadiusX); // Foliage Inner Radius X. + stream->read(&mFieldData.mInnerRadiusY); // Foliage Inner Radius Y. + stream->read(&mFieldData.mOuterRadiusX); // Foliage Outer Radius X. + stream->read(&mFieldData.mOuterRadiusY); // Foliage Outer Radius Y. + + stream->read(&mFieldData.mMinWidth); // Foliage Minimum Width. + stream->read(&mFieldData.mMaxWidth); // Foliage Maximum Width. + stream->read(&mFieldData.mMinHeight); // Foliage Minimum Height. + stream->read(&mFieldData.mMaxHeight); // Foliage Maximum Height. + stream->read(&mFieldData.mFixAspectRatio); // Foliage Fix Aspect Ratio. + stream->read(&mFieldData.mFixSizeToMax); // Foliage Fix Size to Max. + stream->read(&mFieldData.mOffsetZ); // Foliage Offset Z. + mFieldData.mRandomFlip = stream->readFlag(); // Foliage Random Flip. + + bool wasTrueBB = mFieldData.mUseTrueBillboards; + mFieldData.mUseTrueBillboards = stream->readFlag(); // Foliage is camera facing (including z axis). + + stream->read(&mFieldData.mUseCulling); // Foliage Use Culling. + stream->read(&mFieldData.mCullResolution); // Foliage Cull Resolution. + stream->read(&mFieldData.mViewDistance); // Foliage View Distance. + stream->read(&mFieldData.mViewClosest); // Foliage View Closest. + stream->read(&mFieldData.mFadeInRegion); // Foliage Fade-In Region. + stream->read(&mFieldData.mFadeOutRegion); // Foliage Fade-Out Region. + stream->read(&mFieldData.mAlphaCutoff); // Foliage Alpha Cutoff. + stream->read(&mFieldData.mGroundAlpha); // Foliage Ground Alpha. + + mFieldData.mSwayOn = stream->readFlag(); // Foliage Sway On Flag. + mFieldData.mSwaySync = stream->readFlag(); // Foliage Sway Sync Flag. + stream->read(&mFieldData.mSwayMagnitudeSide); // Foliage Sway Magnitude Side2Side. + stream->read(&mFieldData.mSwayMagnitudeFront); // Foliage Sway Magnitude Front2Back. + stream->read(&mFieldData.mMinSwayTime); // Foliage Minimum Sway Time. + stream->read(&mFieldData.mMaxSwayTime); // Foliage Maximum way Time. + + mFieldData.mLightOn = stream->readFlag(); // Foliage Light On Flag. + mFieldData.mLightSync = stream->readFlag(); // Foliage Light Sync + stream->read(&mFieldData.mMinLuminance); // Foliage Minimum Luminance. + stream->read(&mFieldData.mMaxLuminance); // Foliage Maximum Luminance. + stream->read(&mFieldData.mLightTime); // Foliage Light Time. + + mFieldData.mAllowOnTerrain = stream->readFlag(); // Allow on Terrain. + mFieldData.mAllowOnInteriors = stream->readFlag(); // Allow on Interiors. + mFieldData.mAllowStatics = stream->readFlag(); // Allow on Statics. + mFieldData.mAllowOnWater = stream->readFlag(); // Allow on Water. + mFieldData.mAllowWaterSurface = stream->readFlag(); // Allow on Water Surface. + stream->read(&mFieldData.mAllowedTerrainSlope); // Allowed Terrain Slope. + + mFieldData.mHideFoliage = stream->readFlag(); // Hide Foliage. + mFieldData.mShowPlacementArea = stream->readFlag(); // Show Placement Area Flag. + stream->read(&mFieldData.mPlacementBandHeight); // Placement Area Height. + stream->read(&mFieldData.mPlaceAreaColour); + + // Calculate Fade-In/Out Gradients. + mFadeInGradient = 1.0f / mFieldData.mFadeInRegion; + mFadeOutGradient = 1.0f / mFieldData.mFadeOutRegion; + + // Set Transform. + setTransform(ReplicatorObjectMatrix); + + // Load Foliage Texture on the client. + if( mFieldData.mFoliageFile != NULL && dStrlen(mFieldData.mFoliageFile) > 0 ) + mFieldData.mFoliageTexture = GFXTexHandle( mFieldData.mFoliageFile, &GFXDefaultStaticDiffuseProfile, avar("%s() - mFieldData.mFoliageTexture (line %d)", __FUNCTION__, __LINE__) ); + + if ((GFXTextureObject*) mFieldData.mFoliageTexture == NULL) + Con::printf("fxFoliageReplicator: %s is an invalid or missing foliage texture file.", mFieldData.mFoliageFile); + + // Set Quad-Tree Box Height Lerp. + mFrustumRenderSet.mHeightLerp = mFieldData.mDebugBoxHeight; + + // Create Foliage (if Replication has begun). + if (mClientReplicationStarted) + { + CreateFoliage(); + mDirty = true; + } + + if ( isProperlyAdded() && mFieldData.mUseTrueBillboards != wasTrueBB ) + SetupShader(); + } +} diff --git a/Engine/source/T3D/fx/fxFoliageReplicator.h b/Engine/source/T3D/fx/fxFoliageReplicator.h new file mode 100644 index 000000000..8d4e09d35 --- /dev/null +++ b/Engine/source/T3D/fx/fxFoliageReplicator.h @@ -0,0 +1,392 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FOLIAGEREPLICATOR_H_ +#define _FOLIAGEREPLICATOR_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _GFXTEXTUREHANDLE_H_ +#include "gfx/gfxTextureHandle.h" +#endif +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif +#ifndef _GBITMAP_H_ +#include "gfx/bitmap/gBitmap.h" +#endif +#ifndef _RENDERPASSMANAGER_H_ +#include "renderInstance/renderPassManager.h" +#endif +#ifndef _MATHUTIL_FRUSTUM_H_ +#include "math/util/frustum.h" +#endif + +#pragma warning( push, 4 ) + +#define AREA_ANIMATION_ARC (1.0f / 360.0f) + +#define FXFOLIAGEREPLICATOR_COLLISION_MASK ( TerrainObjectType | \ + InteriorObjectType | \ + StaticShapeObjectType | \ + WaterObjectType ) + +#define FXFOLIAGEREPLICATOR_NOWATER_COLLISION_MASK ( TerrainObjectType | \ + InteriorObjectType | \ + StaticShapeObjectType ) + + +#define FXFOLIAGE_ALPHA_EPSILON 1e-4 + + + +//------------------------------------------------------------------------------ +// Class: fxFoliageItem +//------------------------------------------------------------------------------ +class fxFoliageItem +{ +public: + MatrixF Transform; + F32 Width; + F32 Height; + Box3F FoliageBox; + bool Flipped; + F32 SwayPhase; + F32 SwayTimeRatio; + F32 LightPhase; + F32 LightTimeRatio; + U32 LastFrameSerialID; +}; + +//------------------------------------------------------------------------------ +// Class: fxFoliageCulledList +//------------------------------------------------------------------------------ +class fxFoliageCulledList +{ +public: + fxFoliageCulledList() {}; + fxFoliageCulledList(Box3F SearchBox, fxFoliageCulledList* InVec); + ~fxFoliageCulledList() {}; + + void FindCandidates(Box3F SearchBox, fxFoliageCulledList* InVec); + + U32 GetListCount(void) { return mCulledObjectSet.size(); }; + fxFoliageItem* GetElement(U32 index) { return mCulledObjectSet[index]; }; + + Vector mCulledObjectSet; // Culled Object Set. +}; + + +//------------------------------------------------------------------------------ +// Class: fxFoliageQuadNode +//------------------------------------------------------------------------------ +class fxFoliageQuadrantNode { +public: + U32 Level; + Box3F QuadrantBox; + fxFoliageQuadrantNode* QuadrantChildNode[4]; + Vector RenderList; + // Used in DrawIndexPrimitive call. + U32 startIndex; + U32 primitiveCount; +}; + + +//------------------------------------------------------------------------------ +// Class: fxFoliageRenderList +//------------------------------------------------------------------------------ +class fxFoliageRenderList +{ +public: + + Box3F mBox; // Clipping Box. + Frustum mFrustum; // View frustum. + + Vector mVisObjectSet; // Visible Object Set. + F32 mHeightLerp; // Height Lerp. + +public: + bool IsQuadrantVisible(const Box3F VisBox, const MatrixF& RenderTransform); + void SetupClipPlanes(SceneRenderState* state, const F32 FarClipPlane); + void DrawQuadBox(const Box3F& QuadBox, const ColorF Colour); +}; + + +// Define a vertex +GFXDeclareVertexFormat( GFXVertexFoliage ) +{ + Point3F point; + Point3F normal; + Point2F texCoord; + Point2F texCoord2; +}; + + +//------------------------------------------------------------------------------ +// Class: fxFoliageReplicator +//------------------------------------------------------------------------------ +class fxFoliageReplicator : public SceneObject +{ +private: + typedef SceneObject Parent; + +protected: + + void CreateFoliage(void); + void DestroyFoliage(void); + void DestroyFoliageItems(); + + + void SyncFoliageReplicators(void); + + Box3F FetchQuadrant(Box3F Box, U32 Quadrant); + void ProcessQuadrant(fxFoliageQuadrantNode* pParentNode, fxFoliageCulledList* pCullList, U32 Quadrant); + void ProcessNodeChildren(fxFoliageQuadrantNode* pParentNode, fxFoliageCulledList* pCullList); + + enum { FoliageReplicationMask = (1 << 0) }; + + + U32 mCreationAreaAngle; + bool mClientReplicationStarted; + U32 mCurrentFoliageCount; + + Vector mFoliageQuadTree; + Vector mReplicatedFoliage; + fxFoliageRenderList mFrustumRenderSet; + + GFXVertexBufferHandle mVertexBuffer; + GFXPrimitiveBufferHandle mPrimBuffer; + GFXShaderRef mShader; + ShaderData* mShaderData; + GBitmap* mAlphaLookup; + + MRandomLCG RandomGen; + F32 mFadeInGradient; + F32 mFadeOutGradient; + S32 mLastRenderTime; + F32 mGlobalSwayPhase; + F32 mGlobalSwayTimeRatio; + F32 mGlobalLightPhase; + F32 mGlobalLightTimeRatio; + U32 mFrameSerialID; + + U32 mQuadTreeLevels; // Quad-Tree Levels. + U32 mPotentialFoliageNodes; // Potential Foliage Nodes. + U32 mNextAllocatedNodeIdx; // Next Allocated Node Index. + U32 mBillboardsAcquired; // Billboards Acquired. + + // Used for alpha lookup in the pixel shader + GFXTexHandle mAlphaTexture; + + GFXStateBlockRef mPlacementSB; + GFXStateBlockRef mRenderSB; + GFXStateBlockRef mDebugSB; + + GFXShaderConstBufferRef mFoliageShaderConsts; + + GFXShaderConstHandle* mFoliageShaderProjectionSC; + GFXShaderConstHandle* mFoliageShaderWorldSC; + GFXShaderConstHandle* mFoliageShaderGlobalSwayPhaseSC; + GFXShaderConstHandle* mFoliageShaderSwayMagnitudeSideSC; + GFXShaderConstHandle* mFoliageShaderSwayMagnitudeFrontSC; + GFXShaderConstHandle* mFoliageShaderGlobalLightPhaseSC; + GFXShaderConstHandle* mFoliageShaderLuminanceMagnitudeSC; + GFXShaderConstHandle* mFoliageShaderLuminanceMidpointSC; + GFXShaderConstHandle* mFoliageShaderDistanceRangeSC; + GFXShaderConstHandle* mFoliageShaderCameraPosSC; + GFXShaderConstHandle* mFoliageShaderTrueBillboardSC; + + //pixel shader + GFXShaderConstHandle* mFoliageShaderGroundAlphaSC; + GFXShaderConstHandle* mFoliageShaderAmbientColorSC; + GFXShaderConstHandle* mDiffuseTextureSC; + GFXShaderConstHandle* mAlphaMapTextureSC; + + + + bool mDirty; + + void SetupShader(); + void SetupBuffers(); + void renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance*); + void renderBuffers(SceneRenderState* state); + void renderArc(const F32 fRadiusX, const F32 fRadiusY); + void renderPlacementArea(const F32 ElapsedTime); + void renderQuad(fxFoliageQuadrantNode* quadNode, const MatrixF& RenderTransform, const bool UseDebug); + void computeAlphaTex(); +public: + fxFoliageReplicator(); + ~fxFoliageReplicator(); + + void StartUp(void); + void ShowReplication(void); + void HideReplication(void); + + // SceneObject + virtual void prepRenderImage( SceneRenderState *state ); + + // SimObject + bool onAdd(); + void onRemove(); + void inspectPostApply(); + + // NetObject + U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream); + void unpackUpdate(NetConnection *conn, BitStream *stream); + + // Editor + void onGhostAlwaysDone(); + + // ConObject. + static void initPersistFields(); + + // Field Data. + class tagFieldData + { + public: + + bool mUseDebugInfo; + F32 mDebugBoxHeight; + U32 mSeed; + StringTableEntry mFoliageFile; + GFXTexHandle mFoliageTexture; + U32 mFoliageCount; + U32 mFoliageRetries; + + U32 mInnerRadiusX; + U32 mInnerRadiusY; + U32 mOuterRadiusX; + U32 mOuterRadiusY; + + F32 mMinWidth; + F32 mMaxWidth; + F32 mMinHeight; + F32 mMaxHeight; + bool mFixAspectRatio; + bool mFixSizeToMax; + F32 mOffsetZ; + bool mRandomFlip; + bool mUseTrueBillboards; + + bool mUseCulling; + U32 mCullResolution; + F32 mViewDistance; + F32 mViewClosest; + F32 mFadeInRegion; + F32 mFadeOutRegion; + F32 mAlphaCutoff; + F32 mGroundAlpha; + + bool mSwayOn; + bool mSwaySync; + F32 mSwayMagnitudeSide; + F32 mSwayMagnitudeFront; + F32 mMinSwayTime; + F32 mMaxSwayTime; + + bool mLightOn; + bool mLightSync; + F32 mMinLuminance; + F32 mMaxLuminance; + F32 mLightTime; + + bool mAllowOnTerrain; + bool mAllowOnInteriors; + bool mAllowStatics; + bool mAllowOnWater; + bool mAllowWaterSurface; + S32 mAllowedTerrainSlope; + + bool mHideFoliage; + bool mShowPlacementArea; + U32 mPlacementBandHeight; + ColorF mPlaceAreaColour; + + tagFieldData() + { + // Set Defaults. + mUseDebugInfo = false; + mDebugBoxHeight = 1.0f; + mSeed = 1376312589; + mFoliageFile = StringTable->insert(""); + mFoliageTexture = GFXTexHandle(); + mFoliageCount = 10; + mFoliageRetries = 100; + + mInnerRadiusX = 0; + mInnerRadiusY = 0; + mOuterRadiusX = 128; + mOuterRadiusY = 128; + + mMinWidth = 1; + mMaxWidth = 3; + mMinHeight = 1; + mMaxHeight = 5; + mFixAspectRatio = true; + mFixSizeToMax = false; + mOffsetZ = 0; + mRandomFlip = true; + mUseTrueBillboards = false; + + mUseCulling = true; + mCullResolution = 64; + mViewDistance = 50.0f; + mViewClosest = 1.0f; + mFadeInRegion = 10.0f; + mFadeOutRegion = 1.0f; + mAlphaCutoff = 0.2f; + mGroundAlpha = 1.0f; + + mSwayOn = false; + mSwaySync = false; + mSwayMagnitudeSide = 0.1f; + mSwayMagnitudeFront = 0.2f; + mMinSwayTime = 3.0f; + mMaxSwayTime = 10.0f; + + mLightOn = false; + mLightSync = false; + mMinLuminance = 0.7f; + mMaxLuminance = 1.0f; + mLightTime = 5.0f; + + mAllowOnTerrain = true; + mAllowOnInteriors = true; + mAllowStatics = true; + mAllowOnWater = false; + mAllowWaterSurface = false; + mAllowedTerrainSlope = 90; + + mHideFoliage = false; + mShowPlacementArea = true; + mPlacementBandHeight = 25; + mPlaceAreaColour .set(0.4f, 0, 0.8f); + } + + } mFieldData; + + // Declare Console Object. + DECLARE_CONOBJECT(fxFoliageReplicator); +}; +#pragma warning( pop ) +#endif // _FOLIAGEREPLICATOR_H_ diff --git a/Engine/source/T3D/fx/fxShapeReplicator.cpp b/Engine/source/T3D/fx/fxShapeReplicator.cpp new file mode 100644 index 000000000..c9a3e7f52 --- /dev/null +++ b/Engine/source/T3D/fx/fxShapeReplicator.cpp @@ -0,0 +1,764 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/fx/fxShapeReplicator.h" + +#include "gfx/gfxDevice.h" +#include "gfx/primBuilder.h" +#include "console/consoleTypes.h" +#include "core/stream/bitStream.h" +#include "math/mRandom.h" +#include "math/mathIO.h" +#include "T3D/gameBase/gameConnection.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "renderInstance/renderPassManager.h" +#include "console/engineAPI.h" + +//------------------------------------------------------------------------------ +// +// Put this in /example/common/editor/editor.cs in function [Editor::create()] (around line 66). +// +// // Ignore Replicated fxStatic Instances. +// EWorldEditor.ignoreObjClass("fxShapeReplicatedStatic"); +// +//------------------------------------------------------------------------------ +// +// Put this in /example/common/editor/EditorGui.cs in [function Creator::init( %this )] +// +// %Environment_Item[8] = "fxShapeReplicator"; <-- ADD THIS. +// +//------------------------------------------------------------------------------ +// +// Put the function in /example/common/editor/ObjectBuilderGui.gui [around line 458] ... +// +// function ObjectBuilderGui::buildfxShapeReplicator(%this) +// { +// %this.className = "fxShapeReplicator"; +// %this.process(); +// } +// +//------------------------------------------------------------------------------ +// +// Put this in /example/common/client/missionDownload.cs in [function clientCmdMissionStartPhase3(%seq,%missionName)] (line 65) +// after codeline 'onPhase2Complete();'. +// +// StartClientReplication(); +// +//------------------------------------------------------------------------------ +// +// Put this in /engine/console/simBase.h (around line 509) in +// +// namespace Sim +// { +// DeclareNamedSet(fxReplicatorSet) <-- ADD THIS (Note no semi-colon). +// +//------------------------------------------------------------------------------ +// +// Put this in /engine/console/simBase.cc (around line 19) in +// +// ImplementNamedSet(fxReplicatorSet) <-- ADD THIS +// +//------------------------------------------------------------------------------ +// +// Put this in /engine/console/simManager.cc [function void init()] (around line 269). +// +// namespace Sim +// { +// InstantiateNamedSet(fxReplicatorSet); <-- ADD THIS +// +//------------------------------------------------------------------------------ + +extern bool gEditingMission; + +//------------------------------------------------------------------------------ + +IMPLEMENT_CO_NETOBJECT_V1(fxShapeReplicator); +IMPLEMENT_CO_NETOBJECT_V1(fxShapeReplicatedStatic); + +ConsoleDocClass( fxShapeReplicator, + "@brief An emitter for objects to replicate across an area.\n" + "@ingroup Foliage\n" +); + +ConsoleDocClass( fxShapeReplicatedStatic, + "@brief The object definition for shapes that will be replicated across an area using an fxShapeReplicator.\n" + "@ingroup Foliage\n" +); + +//------------------------------------------------------------------------------ +// Class: fxShapeReplicator +//------------------------------------------------------------------------------ + +fxShapeReplicator::fxShapeReplicator() +{ + // Setup NetObject. + mTypeMask |= StaticObjectType; + mNetFlags.set(Ghostable | ScopeAlways); + + // Reset Shape Count. + mCurrentShapeCount = 0; + + // Reset Creation Area Angle Animation. + mCreationAreaAngle = 0; + + // Reset Last Render Time. + mLastRenderTime = 0; + + mPlacementSB = NULL; +} + +//------------------------------------------------------------------------------ + +fxShapeReplicator::~fxShapeReplicator() +{ + mPlacementSB = NULL; +} + +//------------------------------------------------------------------------------ + +void fxShapeReplicator::initPersistFields() +{ + // Add out own persistent fields. + addGroup( "Debugging" ); // MM: Added Group Header. + addField( "HideReplications", TypeBool, Offset( mFieldData.mHideReplications, fxShapeReplicator ), "Replicated shapes are hidden when set to true." ); + addField( "ShowPlacementArea", TypeBool, Offset( mFieldData.mShowPlacementArea, fxShapeReplicator ), "Draw placement rings when set to true." ); + addField( "PlacementAreaHeight", TypeS32, Offset( mFieldData.mPlacementBandHeight, fxShapeReplicator ), "Height of the placement ring in world units." ); + addField( "PlacementColour", TypeColorF, Offset( mFieldData.mPlaceAreaColour, fxShapeReplicator ), "Color of the placement ring." ); + endGroup( "Debugging" ); // MM: Added Group Footer. + + addGroup( "Media" ); // MM: Added Group Header. + addField( "ShapeFile", TypeShapeFilename, Offset( mFieldData.mShapeFile, fxShapeReplicator ), "Filename of shape to replicate." ); + endGroup( "Media" ); // MM: Added Group Footer. + + addGroup( "Replications" ); // MM: Added Group Header. + addField( "Seed", TypeS32, Offset( mFieldData.mSeed, fxShapeReplicator ), "Random seed for shape placement." ); + addField( "ShapeCount", TypeS32, Offset( mFieldData.mShapeCount, fxShapeReplicator ), "Maximum shape instance count." ); + addField( "ShapeRetries", TypeS32, Offset( mFieldData.mShapeRetries, fxShapeReplicator ), "Number of times to try placing a shape instance before giving up." ); + endGroup( "Replications" ); // MM: Added Group Footer. + + addGroup( "Placement Radius" ); // MM: Added Group Header. + addField( "InnerRadiusX", TypeS32, Offset( mFieldData.mInnerRadiusX, fxShapeReplicator ), "Placement area inner radius on the X axis" ); + addField( "InnerRadiusY", TypeS32, Offset( mFieldData.mInnerRadiusY, fxShapeReplicator ), "Placement area inner radius on the Y axis" ); + addField( "OuterRadiusX", TypeS32, Offset( mFieldData.mOuterRadiusX, fxShapeReplicator ), "Placement area outer radius on the X axis" ); + addField( "OuterRadiusY", TypeS32, Offset( mFieldData.mOuterRadiusY, fxShapeReplicator ), "Placement area outer radius on the Y axis" ); + endGroup( "Placement Radius" ); // MM: Added Group Footer. + + addGroup( "Restraints" ); // MM: Added Group Header. + addField( "AllowOnTerrain", TypeBool, Offset( mFieldData.mAllowOnTerrain, fxShapeReplicator ), "Shapes will be placed on terrain when set." ); + addField( "AllowOnInteriors", TypeBool, Offset( mFieldData.mAllowOnInteriors, fxShapeReplicator ), "Shapes will be placed on InteriorInstances when set." ); + addField( "AllowOnStatics", TypeBool, Offset( mFieldData.mAllowStatics, fxShapeReplicator ), "Shapes will be placed on Static shapes when set." ); + addField( "AllowOnWater", TypeBool, Offset( mFieldData.mAllowOnWater, fxShapeReplicator ), "Shapes will be placed on/under water when set." ); + addField( "AllowWaterSurface", TypeBool, Offset( mFieldData.mAllowWaterSurface, fxShapeReplicator ), "Shapes will be placed on water when set. Requires AllowOnWater." ); + addField( "AlignToTerrain", TypeBool, Offset( mFieldData.mAlignToTerrain, fxShapeReplicator ), "Align shapes to surface normal when set." ); + addField( "Interactions", TypeBool, Offset( mFieldData.mInteractions, fxShapeReplicator ), "Allow physics interactions with shapes." ); + addField( "AllowedTerrainSlope", TypeS32, Offset( mFieldData.mAllowedTerrainSlope, fxShapeReplicator ), "Maximum surface angle allowed for shape instances." ); + addField( "TerrainAlignment", TypePoint3F, Offset( mFieldData.mTerrainAlignment, fxShapeReplicator ), "Surface normals will be multiplied by these values when AlignToTerrain is enabled." ); + endGroup( "Restraints" ); // MM: Added Group Footer. + + addGroup( "Object Transforms" ); // MM: Added Group Header. + addField( "ShapeScaleMin", TypePoint3F, Offset( mFieldData.mShapeScaleMin, fxShapeReplicator ), "Minimum shape scale." ); + addField( "ShapeScaleMax", TypePoint3F, Offset( mFieldData.mShapeScaleMax, fxShapeReplicator ), "Maximum shape scale." ); + addField( "ShapeRotateMin", TypePoint3F, Offset( mFieldData.mShapeRotateMin, fxShapeReplicator ), "Minimum shape rotation angles."); + addField( "ShapeRotateMax", TypePoint3F, Offset( mFieldData.mShapeRotateMax, fxShapeReplicator ), "Maximum shape rotation angles." ); + addField( "OffsetZ", TypeS32, Offset( mFieldData.mOffsetZ, fxShapeReplicator ), "Offset shapes by this amount vertically." ); + endGroup( "Object Transforms" ); // MM: Added Group Footer. + + // Initialise parents' persistent fields. + Parent::initPersistFields(); +} + +//------------------------------------------------------------------------------ + +void fxShapeReplicator::CreateShapes(void) +{ + F32 HypX, HypY; + F32 Angle; + U32 RelocationRetry; + Point3F ShapePosition; + Point3F ShapeStart; + Point3F ShapeEnd; + Point3F ShapeScale; + EulerF ShapeRotation; + QuatF QRotation; + bool CollisionResult; + RayInfo RayEvent; + TSShape* pShape; + + + // Don't create shapes if we are hiding replications. + if (mFieldData.mHideReplications) return; + + // Cannot continue without shapes! + if (dStrcmp(mFieldData.mShapeFile, "") == 0) return; + + // Check that we can position somewhere! + if (!( mFieldData.mAllowOnTerrain || + mFieldData.mAllowOnInteriors || + mFieldData.mAllowStatics || + mFieldData.mAllowOnWater)) + { + // Problem ... + Con::warnf(ConsoleLogEntry::General, "[%s] - Could not place object, All alloweds are off!", getName()); + + // Return here. + return; + } + + // Check Shapes. + AssertFatal(mCurrentShapeCount==0,"Shapes already present, this should not be possible!") + + // Check that we have a shape... + if (!mFieldData.mShapeFile) return; + + // Set Seed. + RandomGen.setSeed(mFieldData.mSeed); + + // Set shape vector. + mReplicatedShapes.clear(); + + // Add shapes. + for (U32 idx = 0; idx < mFieldData.mShapeCount; idx++) + { + fxShapeReplicatedStatic* fxStatic; + + // Create our static shape. + fxStatic = new fxShapeReplicatedStatic(); + + // Set the 'shapeName' field. + fxStatic->setField("shapeName", mFieldData.mShapeFile); + + // Is this Replicator on the Server? + if (isServerObject()) + // Yes, so stop it from Ghosting. (Hack, Hack, Hack!) + fxStatic->touchNetFlags(Ghostable, false); + else + // No, so flag as ghost object. (Another damn Hack!) + fxStatic->touchNetFlags(IsGhost, true); + + // Register the Object. + if (!fxStatic->registerObject()) + { + // Problem ... + Con::warnf(ConsoleLogEntry::General, "[%s] - Could not load shape file '%s'!", getName(), mFieldData.mShapeFile); + + // Destroy Shape. + delete fxStatic; + + // Destroy existing hapes. + DestroyShapes(); + + // Quit. + return; + } + + // Get Allocated Shape. + pShape = fxStatic->getShape(); + + // Reset Relocation Retry. + RelocationRetry = mFieldData.mShapeRetries; + + // Find it a home ... + do + { + // Get the Replicator Position. + ShapePosition = getPosition(); + + // Calculate a random offset + HypX = RandomGen.randF(mFieldData.mInnerRadiusX, mFieldData.mOuterRadiusX); + HypY = RandomGen.randF(mFieldData.mInnerRadiusY, mFieldData.mOuterRadiusY); + Angle = RandomGen.randF(0, (F32)M_2PI); + + // Calcualte the new position. + ShapePosition.x += HypX * mCos(Angle); + ShapePosition.y += HypY * mSin(Angle); + + + // Initialise RayCast Search Start/End Positions. + ShapeStart = ShapeEnd = ShapePosition; + ShapeStart.z = 2000.f; + ShapeEnd.z= -2000.f; + + // Is this the Server? + if (isServerObject()) + // Perform Ray Cast Collision on Server Terrain. + CollisionResult = gServerContainer.castRay(ShapeStart, ShapeEnd, FXREPLICATOR_COLLISION_MASK, &RayEvent); + else + // Perform Ray Cast Collision on Client Terrain. + CollisionResult = gClientContainer.castRay( ShapeStart, ShapeEnd, FXREPLICATOR_COLLISION_MASK, &RayEvent); + + // Did we hit anything? + if (CollisionResult) + { + // For now, let's pretend we didn't get a collision. + CollisionResult = false; + + // Yes, so get it's type. + U32 CollisionType = RayEvent.object->getTypeMask(); + + // Check Illegal Placements. + if (((CollisionType & TerrainObjectType) && !mFieldData.mAllowOnTerrain) || + ((CollisionType & InteriorObjectType) && !mFieldData.mAllowOnInteriors) || + ((CollisionType & StaticShapeObjectType) && !mFieldData.mAllowStatics) || + ((CollisionType & WaterObjectType) && !mFieldData.mAllowOnWater) ) continue; + + // If we collided with water and are not allowing on the water surface then let's find the + // terrain underneath and pass this on as the original collision else fail. + // + // NOTE:- We need to do this on the server/client as appropriate. + if ((CollisionType & WaterObjectType) && !mFieldData.mAllowWaterSurface) + { + // Is this the Server? + if (isServerObject()) + { + // Yes, so do it on the server container. + if (!gServerContainer.castRay( ShapeStart, ShapeEnd, FXREPLICATOR_NOWATER_COLLISION_MASK, &RayEvent)) continue; + } + else + { + // No, so do it on the client container. + if (!gClientContainer.castRay( ShapeStart, ShapeEnd, FXREPLICATOR_NOWATER_COLLISION_MASK, &RayEvent)) continue; + } + } + + // We passed with flying colours so carry on. + CollisionResult = true; + } + + // Invalidate if we are below Allowed Terrain Angle. + if (RayEvent.normal.z < mSin(mDegToRad(90.0f-mFieldData.mAllowedTerrainSlope))) CollisionResult = false; + + // Wait until we get a collision. + } while(!CollisionResult && --RelocationRetry); + + // Check for Relocation Problem. + if (RelocationRetry > 0) + { + // Adjust Impact point. + RayEvent.point.z += mFieldData.mOffsetZ; + + // Set New Position. + ShapePosition = RayEvent.point; + } + else + { + // Warning. + Con::warnf(ConsoleLogEntry::General, "[%s] - Could not find satisfactory position for shape '%s' on %s!", getName(), mFieldData.mShapeFile,isServerObject()?"Server":"Client"); + + // Unregister Object. + fxStatic->unregisterObject(); + + // Destroy Shape. + delete fxStatic; + + // Skip to next. + continue; + } + + // Get Shape Transform. + MatrixF XForm = fxStatic->getTransform(); + + // Are we aligning to Terrain? + if (mFieldData.mAlignToTerrain) + { + // Yes, so set rotation to Terrain Impact Normal. + ShapeRotation = RayEvent.normal * mFieldData.mTerrainAlignment; + } + else + { + // No, so choose a new Rotation (in Radians). + ShapeRotation.set( mDegToRad(RandomGen.randF(mFieldData.mShapeRotateMin.x, mFieldData.mShapeRotateMax.x)), + mDegToRad(RandomGen.randF(mFieldData.mShapeRotateMin.y, mFieldData.mShapeRotateMax.y)), + mDegToRad(RandomGen.randF(mFieldData.mShapeRotateMin.z, mFieldData.mShapeRotateMax.z))); + } + + // Set Quaternion Roation. + QRotation.set(ShapeRotation); + + // Set Transform Rotation. + QRotation.setMatrix(&XForm); + + // Set Position. + XForm.setColumn(3, ShapePosition); + + // Set Shape Position / Rotation. + fxStatic->setTransform(XForm); + + // Choose a new Scale. + ShapeScale.set( RandomGen.randF(mFieldData.mShapeScaleMin.x, mFieldData.mShapeScaleMax.x), + RandomGen.randF(mFieldData.mShapeScaleMin.y, mFieldData.mShapeScaleMax.y), + RandomGen.randF(mFieldData.mShapeScaleMin.z, mFieldData.mShapeScaleMax.z)); + + // Set Shape Scale. + fxStatic->setScale(ShapeScale); + + // Lock it. + fxStatic->setLocked(true); + + // Store Shape in Replicated Shapes Vector. + //mReplicatedShapes[mCurrentShapeCount++] = fxStatic; + mReplicatedShapes.push_back(fxStatic); + + } + + mCurrentShapeCount = mReplicatedShapes.size(); + + // Take first Timestamp. + mLastRenderTime = Platform::getVirtualMilliseconds(); +} + +//------------------------------------------------------------------------------ + +void fxShapeReplicator::DestroyShapes(void) +{ + // Finish if we didn't create any shapes. + if (mCurrentShapeCount == 0) return; + + // Remove shapes. + for (U32 idx = 0; idx < mCurrentShapeCount; idx++) + { + fxShapeReplicatedStatic* fxStatic; + + // Fetch the Shape Object. + fxStatic = mReplicatedShapes[idx]; + + // Got a Shape? + if (fxStatic) + { + // Unlock it. + fxStatic->setLocked(false); + + // Unregister the object. + fxStatic->unregisterObject(); + + // Delete it. + delete fxStatic; + } + } + + // Empty the Replicated Shapes Vector. + mReplicatedShapes.clear(); + + // Reset Shape Count. + mCurrentShapeCount = 0; +} + +//------------------------------------------------------------------------------ + +void fxShapeReplicator::RenewShapes(void) +{ + // Destroy any shapes. + DestroyShapes(); + + // Don't create shapes on the Server if we don't need interactions. + if (isServerObject() && !mFieldData.mInteractions) return; + + // Create Shapes. + CreateShapes(); +} + +//------------------------------------------------------------------------------ + +void fxShapeReplicator::StartUp(void) +{ + RenewShapes(); +} + +//------------------------------------------------------------------------------ + +bool fxShapeReplicator::onAdd() +{ + if(!Parent::onAdd()) + return(false); + + // Add the Replicator to the Replicator Set. + dynamic_cast(Sim::findObject("fxReplicatorSet"))->addObject(this); + + // Set Default Object Box. + mObjBox.minExtents.set( -0.5, -0.5, -0.5 ); + mObjBox.maxExtents.set( 0.5, 0.5, 0.5 ); + resetWorldBox(); + + // Add to Scene. + setRenderTransform(mObjToWorld); + addToScene(); + + // Register for notification when GhostAlways objects are done loading + NetConnection::smGhostAlwaysDone.notify( this, &fxShapeReplicator::onGhostAlwaysDone ); + + return true; +} + +//------------------------------------------------------------------------------ + +void fxShapeReplicator::onRemove() +{ + // Remove the Replicator from the Replicator Set. + dynamic_cast(Sim::findObject("fxReplicatorSet"))->removeObject(this); + + NetConnection::smGhostAlwaysDone.remove( this, &fxShapeReplicator::onGhostAlwaysDone ); + + removeFromScene(); + + // Destroy Shapes. + DestroyShapes(); + + // Do Parent. + Parent::onRemove(); +} + +//------------------------------------------------------------------------------ + +void fxShapeReplicator::onGhostAlwaysDone() +{ + RenewShapes(); +} + +//------------------------------------------------------------------------------ + +void fxShapeReplicator::inspectPostApply() +{ + // Set Parent. + Parent::inspectPostApply(); + + // Renew Shapes. + RenewShapes(); + + // Set Replication Mask. + setMaskBits(ReplicationMask); +} + +//------------------------------------------------------------------------------ + +DefineEngineFunction(StartClientReplication, void, (),, "Activates the shape replicator.\n" + "@tsexample\n" + "// Call the function\n" + "StartClientReplication()\n" + "@endtsexample\n" + "@ingroup Foliage" + ) +{ + // Find the Replicator Set. + SimSet *fxReplicatorSet = dynamic_cast(Sim::findObject("fxReplicatorSet")); + + // Return if Error. + if (!fxReplicatorSet) return; + + // StartUp Replication Object. + for (SimSetIterator itr(fxReplicatorSet); *itr; ++itr) + { + // Fetch the Replicator Object. + fxShapeReplicator* Replicator = static_cast(*itr); + // Start Client Objects Only. + if (Replicator->isClientObject()) Replicator->StartUp(); + } + // Info ... + Con::printf("Client Replication Startup has Happened!"); +} + + +//------------------------------------------------------------------------------ + +void fxShapeReplicator::prepRenderImage( SceneRenderState* state ) +{ + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind(this, &fxShapeReplicator::renderObject); + // The fxShapeReplicator isn't technically foliage but our debug + // effect seems to render best as a Foliage type (translucent, + // renders itself, no sorting) + ri->type = RenderPassManager::RIT_Foliage; + state->getRenderPass()->addInst( ri ); +} + +//------------------------------------------------------------------------------ + +// Renders a triangle stripped oval +void fxShapeReplicator::renderArc(const F32 fRadiusX, const F32 fRadiusY) +{ + PrimBuild::begin(GFXTriangleStrip, 720); + for (U32 Angle = mCreationAreaAngle; Angle < (mCreationAreaAngle+360); Angle++) + { + F32 XPos, YPos; + + // Calculate Position. + XPos = fRadiusX * mCos(mDegToRad(-(F32)Angle)); + YPos = fRadiusY * mSin(mDegToRad(-(F32)Angle)); + + // Set Colour. + PrimBuild::color4f(mFieldData.mPlaceAreaColour.red, + mFieldData.mPlaceAreaColour.green, + mFieldData.mPlaceAreaColour.blue, + AREA_ANIMATION_ARC * (Angle-mCreationAreaAngle)); + + PrimBuild::vertex3f(XPos, YPos, -(F32)mFieldData.mPlacementBandHeight/2.0f); + PrimBuild::vertex3f(XPos, YPos, +(F32)mFieldData.mPlacementBandHeight/2.0f); + } + PrimBuild::end(); +} + +// This currently uses the primbuilder, could convert out, but why allocate the buffer if we +// never edit the misison? +void fxShapeReplicator::renderPlacementArea(const F32 ElapsedTime) +{ + if (gEditingMission && mFieldData.mShowPlacementArea) + { + GFX->pushWorldMatrix(); + GFX->multWorld(getTransform()); + + if (!mPlacementSB) + { + GFXStateBlockDesc transparent; + transparent.setCullMode(GFXCullNone); + transparent.alphaTestEnable = true; + transparent.setZReadWrite(true); + transparent.zWriteEnable = false; + transparent.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha); + mPlacementSB = GFX->createStateBlock( transparent ); + } + + GFX->setStateBlock(mPlacementSB); + + // Do we need to draw the Outer Radius? + if (mFieldData.mOuterRadiusX || mFieldData.mOuterRadiusY) + renderArc((F32) mFieldData.mOuterRadiusX, (F32) mFieldData.mOuterRadiusY); + // Inner radius? + if (mFieldData.mInnerRadiusX || mFieldData.mInnerRadiusY) + renderArc((F32) mFieldData.mInnerRadiusX, (F32) mFieldData.mInnerRadiusY); + + GFX->popWorldMatrix(); + mCreationAreaAngle = (U32)(mCreationAreaAngle + (1000 * ElapsedTime)); + mCreationAreaAngle = mCreationAreaAngle % 360; + } +} + +//------------------------------------------------------------------------------ + +void fxShapeReplicator::renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* overrideMat) +{ + if (overrideMat) + return; + + // Return if placement area not needed. + if (!mFieldData.mShowPlacementArea) + return; + + // Calculate Elapsed Time and take new Timestamp. + S32 Time = Platform::getVirtualMilliseconds(); + F32 ElapsedTime = (Time - mLastRenderTime) * 0.001f; + mLastRenderTime = Time; + + renderPlacementArea(ElapsedTime); +} + +//------------------------------------------------------------------------------ + +U32 fxShapeReplicator::packUpdate(NetConnection * con, U32 mask, BitStream * stream) +{ + // Pack Parent. + U32 retMask = Parent::packUpdate(con, mask, stream); + + // Write Replication Flag. + if (stream->writeFlag(mask & ReplicationMask)) + { + stream->writeAffineTransform(mObjToWorld); // Replicator Position. + + stream->writeInt(mFieldData.mSeed, 32); // Replicator Seed. + stream->writeInt(mFieldData.mShapeCount, 32); // Shapes Count. + stream->writeInt(mFieldData.mShapeRetries, 32); // Shapes Retries. + stream->writeString(mFieldData.mShapeFile); + stream->writeInt(mFieldData.mInnerRadiusX, 32); // Shapes Inner Radius X. + stream->writeInt(mFieldData.mInnerRadiusY, 32); // Shapes Inner Radius Y. + stream->writeInt(mFieldData.mOuterRadiusX, 32); // Shapes Outer Radius X. + stream->writeInt(mFieldData.mOuterRadiusY, 32); // Shapes Outer Radius Y. + mathWrite(*stream, mFieldData.mShapeScaleMin); // Shapes Scale Min. + mathWrite(*stream, mFieldData.mShapeScaleMax); // Shapes Scale Max. + mathWrite(*stream, mFieldData.mShapeRotateMin); // Shapes Rotate Min. + mathWrite(*stream, mFieldData.mShapeRotateMax); // Shapes Rotate Max. + stream->writeSignedInt(mFieldData.mOffsetZ, 32); // Shapes Offset Z. + stream->writeFlag(mFieldData.mAllowOnTerrain); // Allow on Terrain. + stream->writeFlag(mFieldData.mAllowOnInteriors); // Allow on Interiors. + stream->writeFlag(mFieldData.mAllowStatics); // Allow on Statics. + stream->writeFlag(mFieldData.mAllowOnWater); // Allow on Water. + stream->writeFlag(mFieldData.mAllowWaterSurface); // Allow on Water Surface. + stream->writeSignedInt(mFieldData.mAllowedTerrainSlope, 32); // Shapes Offset Z. + stream->writeFlag(mFieldData.mAlignToTerrain); // Shapes AlignToTerrain. + mathWrite(*stream, mFieldData.mTerrainAlignment); // Write Terrain Alignment. + stream->writeFlag(mFieldData.mHideReplications); // Hide Replications. + stream->writeFlag(mFieldData.mInteractions); // Shape Interactions. + stream->writeFlag(mFieldData.mShowPlacementArea); // Show Placement Area Flag. + stream->writeInt(mFieldData.mPlacementBandHeight, 32); // Placement Area Height. + stream->write(mFieldData.mPlaceAreaColour); + } + + // Were done ... + return(retMask); +} + +//------------------------------------------------------------------------------ + +void fxShapeReplicator::unpackUpdate(NetConnection * con, BitStream * stream) +{ + // Unpack Parent. + Parent::unpackUpdate(con, stream); + + // Read Replication Details. + if(stream->readFlag()) + { + MatrixF ReplicatorObjectMatrix; + + stream->readAffineTransform(&ReplicatorObjectMatrix); // Replication Position. + + mFieldData.mSeed = stream->readInt(32); // Replicator Seed. + mFieldData.mShapeCount = stream->readInt(32); // Shapes Count. + mFieldData.mShapeRetries = stream->readInt(32); // Shapes Retries. + mFieldData.mShapeFile = stream->readSTString(); // Shape File. + mFieldData.mInnerRadiusX = stream->readInt(32); // Shapes Inner Radius X. + mFieldData.mInnerRadiusY = stream->readInt(32); // Shapes Inner Radius Y. + mFieldData.mOuterRadiusX = stream->readInt(32); // Shapes Outer Radius X. + mFieldData.mOuterRadiusY = stream->readInt(32); // Shapes Outer Radius Y. + mathRead(*stream, &mFieldData.mShapeScaleMin); // Shapes Scale Min. + mathRead(*stream, &mFieldData.mShapeScaleMax); // Shapes Scale Max. + mathRead(*stream, &mFieldData.mShapeRotateMin); // Shapes Rotate Min. + mathRead(*stream, &mFieldData.mShapeRotateMax); // Shapes Rotate Max. + mFieldData.mOffsetZ = stream->readSignedInt(32); // Shapes Offset Z. + mFieldData.mAllowOnTerrain = stream->readFlag(); // Allow on Terrain. + mFieldData.mAllowOnInteriors = stream->readFlag(); // Allow on Interiors. + mFieldData.mAllowStatics = stream->readFlag(); // Allow on Statics. + mFieldData.mAllowOnWater = stream->readFlag(); // Allow on Water. + mFieldData.mAllowWaterSurface = stream->readFlag(); // Allow on Water Surface. + mFieldData.mAllowedTerrainSlope = stream->readSignedInt(32); // Allowed Terrain Slope. + mFieldData.mAlignToTerrain = stream->readFlag(); // Read AlignToTerrain. + mathRead(*stream, &mFieldData.mTerrainAlignment); // Read Terrain Alignment. + mFieldData.mHideReplications = stream->readFlag(); // Hide Replications. + mFieldData.mInteractions = stream->readFlag(); // Read Interactions. + mFieldData.mShowPlacementArea = stream->readFlag(); // Show Placement Area Flag. + mFieldData.mPlacementBandHeight = stream->readInt(32); // Placement Area Height. + stream->read(&mFieldData.mPlaceAreaColour); + + // Set Transform. + setTransform(ReplicatorObjectMatrix); + + RenewShapes(); + } +} + diff --git a/Engine/source/T3D/fx/fxShapeReplicator.h b/Engine/source/T3D/fx/fxShapeReplicator.h new file mode 100644 index 000000000..66ec29baa --- /dev/null +++ b/Engine/source/T3D/fx/fxShapeReplicator.h @@ -0,0 +1,196 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SHAPEREPLICATOR_H_ +#define _SHAPEREPLICATOR_H_ + +#ifndef _TSSTATIC_H_ +#include "T3D/tsStatic.h" +#endif +#ifndef _TSSHAPEINSTANCE_H_ +#include "ts/tsShapeInstance.h" +#endif +#ifndef _RENDERPASSMANAGER_H_ +#include "renderInstance/renderPassManager.h" +#endif + +#define AREA_ANIMATION_ARC (1.0f / 360.0f) + +#define FXREPLICATOR_COLLISION_MASK ( TerrainObjectType | \ + InteriorObjectType | \ + StaticShapeObjectType | \ + WaterObjectType ) + +#define FXREPLICATOR_NOWATER_COLLISION_MASK ( TerrainObjectType | \ + InteriorObjectType | \ + StaticShapeObjectType ) + + +//------------------------------------------------------------------------------ +// Class: fxShapeReplicatedStatic +//------------------------------------------------------------------------------ +class fxShapeReplicatedStatic : public TSStatic +{ +private: + typedef SceneObject Parent; + +public: + fxShapeReplicatedStatic() {}; + ~fxShapeReplicatedStatic() {}; + void touchNetFlags(const U32 m, bool setflag = true) { if (setflag) mNetFlags.set(m); else mNetFlags.clear(m); }; + TSShape* getShape(void) { return mShapeInstance->getShape(); }; + void setTransform(const MatrixF & mat) { Parent::setTransform(mat); setRenderTransform(mat); }; + + DECLARE_CONOBJECT(fxShapeReplicatedStatic); +}; + + +//------------------------------------------------------------------------------ +// Class: fxShapeReplicator +//------------------------------------------------------------------------------ +class fxShapeReplicator : public SceneObject +{ +private: + typedef SceneObject Parent; + +protected: + + void CreateShapes(void); + void DestroyShapes(void); + void RenewShapes(void); + + enum { ReplicationMask = (1 << 0) }; + + U32 mCreationAreaAngle; + U32 mCurrentShapeCount; + Vector mReplicatedShapes; + MRandomLCG RandomGen; + S32 mLastRenderTime; + + +public: + fxShapeReplicator(); + ~fxShapeReplicator(); + + + void StartUp(void); + void ShowReplication(void); + void HideReplication(void); + + GFXStateBlockRef mPlacementSB; + + void renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance*); + void renderArc(const F32 fRadiusX, const F32 fRadiusY); + void renderPlacementArea(const F32 ElapsedTime); + + // SceneObject + virtual void prepRenderImage( SceneRenderState *state ); + + // SimObject + bool onAdd(); + void onRemove(); + void inspectPostApply(); + + // NetObject + U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream); + void unpackUpdate(NetConnection *conn, BitStream *stream); + + // Editor + void onGhostAlwaysDone(); + + // ConObject. + static void initPersistFields(); + + // Field Data. + class tagFieldData + { + public: + + U32 mSeed; + StringTableEntry mShapeFile; + U32 mShapeCount; + U32 mShapeRetries; + Point3F mShapeScaleMin; + Point3F mShapeScaleMax; + Point3F mShapeRotateMin; + Point3F mShapeRotateMax; + U32 mInnerRadiusX; + U32 mInnerRadiusY; + U32 mOuterRadiusX; + U32 mOuterRadiusY; + S32 mOffsetZ; + bool mAllowOnTerrain; + bool mAllowOnInteriors; + bool mAllowStatics; + bool mAllowOnWater; + S32 mAllowedTerrainSlope; + bool mAlignToTerrain; + bool mAllowWaterSurface; + Point3F mTerrainAlignment; + bool mInteractions; + bool mHideReplications; + bool mShowPlacementArea; + U32 mPlacementBandHeight; + ColorF mPlaceAreaColour; + + tagFieldData() + { + // Set Defaults. + mSeed = 1376312589; + mShapeFile = StringTable->insert(""); + mShapeCount = 10; + mShapeRetries = 100; + mInnerRadiusX = 0; + mInnerRadiusY = 0; + mOuterRadiusX = 100; + mOuterRadiusY = 100; + mOffsetZ = 0; + + mAllowOnTerrain = true; + mAllowOnInteriors = true; + mAllowStatics = true; + mAllowOnWater = false; + mAllowWaterSurface = false; + mAllowedTerrainSlope= 90; + mAlignToTerrain = false; + mInteractions = true; + + mHideReplications = false; + + mShowPlacementArea = true; + mPlacementBandHeight = 25; + mPlaceAreaColour .set(0.4f, 0, 0.8f); + + mShapeScaleMin .set(1, 1, 1); + mShapeScaleMax .set(1, 1, 1); + mShapeRotateMin .set(0, 0, 0); + mShapeRotateMax .set(0, 0, 0); + mTerrainAlignment .set(1, 1, 1); + } + + } mFieldData; + + // Declare Console Object. + DECLARE_CONOBJECT(fxShapeReplicator); +}; + +#endif // _SHAPEREPLICATOR_H_ diff --git a/Engine/source/T3D/fx/groundCover.cpp b/Engine/source/T3D/fx/groundCover.cpp new file mode 100644 index 000000000..1911f0a78 --- /dev/null +++ b/Engine/source/T3D/fx/groundCover.cpp @@ -0,0 +1,1694 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/fx/groundCover.h" + +#include "core/resourceManager.h" +#include "core/stream/bitStream.h" +#include "console/consoleTypes.h" +#include "scene/sceneRenderState.h" +#include "terrain/terrData.h" +#include "renderInstance/renderPassManager.h" +#include "gfx/gfxDrawUtil.h" +#include "gfx/primBuilder.h" +#include "T3D/gameBase/gameConnection.h" +#include "gfx/gfxVertexBuffer.h" +#include "gfx/gfxStructs.h" +#include "ts/tsShapeInstance.h" +#include "lighting/lightManager.h" +#include "lighting/lightInfo.h" +#include "materials/shaderData.h" +#include "gfx/gfxTransformSaver.h" +#include "shaderGen/shaderGenVars.h" +#include "materials/matTextureTarget.h" +#include "gfx/util/screenspace.h" +#include "materials/materialDefinition.h" +#include "materials/materialManager.h" +#include "materials/sceneData.h" +#include "materials/materialFeatureTypes.h" +#include "materials/matInstance.h" +#include "renderInstance/renderPrePassMgr.h" +#include "console/engineAPI.h" + +/// This is used for rendering ground cover billboards. +GFXImplementVertexFormat( GCVertex ) +{ + addElement( "POSITION", GFXDeclType_Float3 ); + addElement( "NORMAL", GFXDeclType_Float3 ); + addElement( "COLOR", GFXDeclType_Color ); + addElement( "TEXCOORD", GFXDeclType_Float4, 0 ); +}; + +GroundCoverShaderConstHandles::GroundCoverShaderConstHandles() + : mTypeRectsSC( NULL ), + mFadeSC( NULL ), + mWindDirSC( NULL ), + mGustInfoSC( NULL ), + mTurbInfoSC( NULL ), + mCamRightSC( NULL ), + mCamUpSC( NULL ), + mGroundCover( NULL ) +{ +} + +void GroundCoverShaderConstHandles::init( GFXShader *shader ) +{ + mTypeRectsSC = shader->getShaderConstHandle( "$gc_typeRects" ); + mFadeSC = shader->getShaderConstHandle( "$gc_fadeParams" ); + mWindDirSC = shader->getShaderConstHandle( "$gc_windDir" ); + mGustInfoSC = shader->getShaderConstHandle( "$gc_gustInfo" ); + mTurbInfoSC = shader->getShaderConstHandle( "$gc_turbInfo" ); + mCamRightSC = shader->getShaderConstHandle( "$gc_camRight" ); + mCamUpSC = shader->getShaderConstHandle( "$gc_camUp" ); +} + +void GroundCoverShaderConstHandles::setConsts( SceneRenderState *state, const SceneData &sgData, GFXShaderConstBuffer *buffer ) +{ + AlignedArray rectData( MAX_COVERTYPES, sizeof( Point4F ), (U8*)(mGroundCover->mBillboardRects), false ); + buffer->setSafe( mTypeRectsSC, rectData ); + + const GroundCoverShaderConstData &data = mGroundCover->getShaderConstData(); + + buffer->setSafe( mFadeSC, data.fadeInfo ); + buffer->setSafe( mWindDirSC, mGroundCover->mWindDirection ); + buffer->setSafe( mGustInfoSC, data.gustInfo ); + buffer->setSafe( mTurbInfoSC, data.turbInfo ); + buffer->setSafe( mCamRightSC, data.camRight ); + buffer->setSafe( mCamUpSC, data.camUp ); +} + +/// This defines one grid cell. +class GroundCoverCell +{ +protected: + + friend class GroundCover; + + struct Placement + { + Point3F point; + Point3F normal; + Point3F size; + F32 rotation; + U32 type; + F32 windAmplitude; + Box3F worldBox; + ColorF lmColor; + }; + + /// This is the x,y index for this cell. + Point2I mIndex; + + /// The worldspace bounding box this cell. + Box3F mBounds; + + /// The worldspace bounding box of the renderable + /// content within this cell. + Box3F mRenderBounds; + + /// The instances of billboard cover elements in this cell. + Vector mBillboards; + + /// The instances of shape cover elements in this cell. + Vector mShapes; + + typedef GFXVertexBufferHandle VBHandle; + typedef Vector< VBHandle > VBHandleVector; + + /// The vertex buffers that hold all the + /// prepared billboards for this cell. + VBHandleVector mVBs; + + /// Used to mark the cell dirty and in need + /// of a rebuild. + bool mDirty; + + /// Repacks the billboards into the vertex buffer. + void _rebuildVB(); + +public: + + GroundCoverCell() {} + + ~GroundCoverCell() + { + mVBs.clear(); + } + + const Point2I& shiftIndex( const Point2I& shift ) { return mIndex += shift; } + + /// The worldspace bounding box this cell. + const Box3F& getBounds() const { return mBounds; } + + /// The worldspace bounding box of the renderable + /// content within this cell. + const Box3F& getRenderBounds() const { return mRenderBounds; } + + Point3F getCenter() const { return mBounds.getCenter(); } + + VectorF getSize() const { return VectorF( mBounds.len_x() / 2.0f, + mBounds.len_y() / 2.0f, + mBounds.len_z() / 2.0f ); } + + void renderBillboards( SceneRenderState *state, BaseMatInstance *mat, GFXPrimitiveBufferHandle *pb ); + + U32 renderShapes( const TSRenderState &rdata, + Frustum *culler, + TSShapeInstance** shapes ); +}; + +void GroundCoverCell::_rebuildVB() +{ + if ( mBillboards.empty() ) + return; + + PROFILE_SCOPE(GroundCover_RebuildVB); + + // The maximum verts we can put in one vertex buffer batch. + const U32 MAX_BILLBOARDS = 0xFFFF / 4; + + // How many batches will we need in total? + const U32 batches = mCeil( (F32)mBillboards.size() / (F32)MAX_BILLBOARDS ); + + // So... how many billboards do we need in + // each batch? We're trying to evenly divide + // the amount across all the VBs. + const U32 batchBB = mBillboards.size() / batches; + + // Init the vertex buffer list to the right size. Any + // VBs already in there will remain unless we're truncating + // the list... those are freed. + mVBs.setSize( batches ); + + // Get the iter to the first billboard. + Vector::const_iterator iter = mBillboards.begin(); + + // Prepare each batch. + U32 bb, remaining = mBillboards.size(); + for ( U32 b = 0; b < batches; b++ ) + { + // Grab a reference to the vb. + VBHandle &vb = mVBs[b]; + + // How many billboards in this batch? + bb = getMin( batchBB, remaining ); + remaining -= bb; + + // Ok... now how many verts is that? + const U32 verts = bb * 4; + + // Create the VB hasn't been created or if its + // too small then resize it. + if ( vb.isNull() || vb->mNumVerts < verts ) + { + PROFILE_START(GroundCover_CreateVB); + vb.set( GFX, verts, GFXBufferTypeStatic ); + PROFILE_END(); + } + + // Fill this puppy! + GCVertex* vertPtr = vb.lock( 0, verts ); + + GFXVertexColor color; + + Vector::const_iterator last = iter + bb; + for ( ; iter != last; iter++ ) + { + const Point3F &position = (*iter).point; + const Point3F &normal = (*iter).normal; + const S32 &type = (*iter).type; + const Point3F &size = (*iter).size; + const F32 &windAmplitude = (*iter).windAmplitude; + GFXVertexColor color = (ColorI)(*iter).lmColor; + U8 *col = (U8 *)const_cast( (const U32 *)color ); + + vertPtr->point = position; + vertPtr->normal = normal; + vertPtr->params.x = size.x; + vertPtr->params.y = size.y; + vertPtr->params.z = type; + vertPtr->params.w = 0; + col[3] = 0; + vertPtr->ambient = color; + ++vertPtr; + + vertPtr->point = position; + vertPtr->normal = normal; + vertPtr->params.x = size.x; + vertPtr->params.y = size.y; + vertPtr->params.z = type; + vertPtr->params.w = 0; + col[3] = 1; + vertPtr->ambient = color; + ++vertPtr; + + vertPtr->point = position; + vertPtr->normal = normal; + vertPtr->params.x = size.x; + vertPtr->params.y = size.y; + vertPtr->params.z = type; + vertPtr->params.w = windAmplitude; + col[3] = 2; + vertPtr->ambient = color; + ++vertPtr; + + vertPtr->point = position; + vertPtr->normal = normal; + vertPtr->params.x = size.x; + vertPtr->params.y = size.y; + vertPtr->params.z = type; + vertPtr->params.w = windAmplitude; + col[3] = 3; + vertPtr->ambient = color; + ++vertPtr; + } + + vb.unlock(); + } +} + +U32 GroundCoverCell::renderShapes( const TSRenderState &rdata, + Frustum *culler, + TSShapeInstance** shapes ) +{ + MatrixF worldMat; + TSShapeInstance* shape; + Point3F camVector; + F32 dist; + F32 invScale; + + const SceneRenderState *state = rdata.getSceneState(); + + U32 totalRendered = 0; + + Vector::const_iterator iter = mShapes.begin(); + for ( ; iter != mShapes.end(); iter++ ) + { + // Grab a reference here once. + const Placement& inst = (*iter); + + // If we were pass a culler then us it to test the shape world box. + if ( culler && culler->isCulled( inst.worldBox ) ) + continue; + + shape = shapes[ inst.type ]; + + camVector = inst.point - state->getDiffuseCameraPosition(); + dist = getMax( camVector.len(), 0.01f ); + + worldMat.set( EulerF(0, 0, inst.rotation), inst.point ); + + // TSShapeInstance::render() uses the + // world matrix for the RenderInst. + worldMat.scale( inst.size ); + GFX->setWorldMatrix( worldMat ); + + // Obey the normal screen space lod metrics. The shapes should + // be tuned to lod out quickly for ground cover. + // + // Note: The profile doesn't indicate that lod selection is + // very expensive... in fact its less than 1/10th of the cost + // of the render() call below. + PROFILE_START(GroundCover_RenderShapes_SelectDetail); + + invScale = (1.0f/getMax(getMax(inst.size.x,inst.size.y),inst.size.z)); + shape->setDetailFromDistance( state, dist * invScale ); + + PROFILE_END(); // GroundCover_RenderShapes_SelectDetail + + // Note: This is the most expensive call of this loop. We + // need to rework the render call completely to optimize it. + PROFILE_START(GroundCover_RenderShapes_Render); + + shape->render( rdata ); + + PROFILE_END(); // GroundCover_RenderShapes_Render + + totalRendered++; + } + + return totalRendered; +} + +void GroundCoverCell::renderBillboards( SceneRenderState *state, BaseMatInstance *mat, GFXPrimitiveBufferHandle *pb ) +{ + if ( mDirty ) + { + _rebuildVB(); + mDirty = false; + } + + // Do we have anything to render? + if ( mBillboards.size() == 0 || mVBs.empty() || !mat ) + return; + + // TODO: Maybe add support for non-facing billboards + // with random rotations and optional crosses. We could + // stick them into the buffer after the normal billboards, + // then change shader consts. + + RenderPassManager *pass = state->getRenderPass(); + + // Draw each batch. + U32 remaining = mBillboards.size(); + const U32 batches = mVBs.size(); + const U32 batchBB = remaining / batches; + + for ( U32 b = 0; b < batches; b++ ) + { + // Grab a reference to the vb. + VBHandle &vb = mVBs[b]; + + // How many billboards in this batch? + U32 bb = getMin( batchBB, remaining ); + remaining -= bb; + + MeshRenderInst *ri = pass->allocInst(); + ri->type = RenderPassManager::RIT_Mesh; + ri->matInst = mat; + ri->vertBuff = &vb; + ri->primBuff = pb; + ri->objectToWorld = &MatrixF::Identity; + ri->worldToCamera = pass->allocSharedXform(RenderPassManager::View); + ri->projection = pass->allocSharedXform(RenderPassManager::Projection); + ri->defaultKey = mat->getStateHint(); + ri->prim = pass->allocPrim(); + ri->prim->numPrimitives = bb * 2; + ri->prim->numVertices = bb * 4; + ri->prim->startIndex = 0; + ri->prim->startVertex = 0; + ri->prim->minIndex = 0; + ri->prim->type = GFXTriangleList; + + // If we need lights then set them up. + if ( mat->isForwardLit() ) + { + LightQuery query; + query.init( mBounds ); + query.getLights( ri->lights, 8 ); + } + + pass->addInst( ri ); + + GroundCover::smStatRenderedBatches++; + GroundCover::smStatRenderedBillboards += bb; + } + + GroundCover::smStatRenderedCells++; +} + + +U32 GroundCover::smStatRenderedCells = 0; +U32 GroundCover::smStatRenderedBillboards = 0; +U32 GroundCover::smStatRenderedBatches = 0; +U32 GroundCover::smStatRenderedShapes = 0; +F32 GroundCover::smDensityScale = 1.0f; + +ConsoleDocClass( GroundCover, + "@brief Covers the ground in a field of objects (IE: Grass, Flowers, etc)." + "@ingroup Foliage\n" +); + +GroundCover::GroundCover() +{ + mTypeMask |= StaticObjectType | StaticShapeObjectType; + mNetFlags.set( Ghostable | ScopeAlways ); + + mRadius = 200.0f; + mZOffset = 0.0f; + mFadeRadius = 50.0f; + mShapeCullRadius = 75.0f; + mShapesCastShadows = true; + mReflectRadiusScale = 0.25f; + + mGridSize = 7; + + // By initializing this to a big value we + // ensure we warp on first render. + mGridIndex.set( S32_MAX, S32_MAX ); + + mMaxPlacement = 1000; + mLastPlacementCount = 0; + + mDebugRenderCells = false; + mDebugNoBillboards = false; + mDebugNoShapes = false; + mDebugLockFrustum = false; + + mRandomSeed = 1; + + mMaterial = NULL; + mMatInst = NULL; + mMatParams = NULL; + mTypeRectsParam = NULL; + mFadeParams = NULL; + mWindDirParam = NULL; + mGustInfoParam = NULL; + mTurbInfoParam = NULL; + mCamRightParam = NULL; + mCamUpParam = NULL; + + mMaxBillboardTiltAngle = 90.0f; + + // TODO: This really doesn't belong here... we need a + // real wind system for Torque scenes. This data + // would be part of a global scene wind or area wind + // emitter. + // + // Tom Spilman - 10/16/2007 + + mWindGustLength = 20.0f; + mWindGustFrequency = 0.5f; + mWindGustStrength = 0.5f; + mWindDirection.set( 1.0f, 0.0f ); + mWindTurbulenceFrequency = 1.2f; + mWindTurbulenceStrength = 0.125f; + + for ( S32 i=0; i < MAX_COVERTYPES; i++ ) + { + mProbability[i] = 0.0f; + + mSizeMin[i] = 1.0f; + mSizeMax[i] = 1.0f; + mSizeExponent[i] = 1.0f; + + mWindScale[i] = 1.0f; + + mMaxSlope[i] = 0.0f; + + mMinElevation[i] = -99999.0f; + mMaxElevation[i] = 99999.0f; + + mLayer[i] = StringTable->EmptyString(); + mInvertLayer[i] = false; + + mMinClumpCount[i] = 1; + mMaxClumpCount[i] = 1; + mClumpCountExponent[i] = 1.0f; + mClumpRadius[i] = 1.0f; + + mBillboardRects[i].point.set( 0.0f, 0.0f ); + mBillboardRects[i].extent.set( 1.0f, 1.0f ); + + mShapeFilenames[i] = NULL; + mShapeInstances[i] = NULL; + + mBillboardAspectScales[i] = 1.0f; + + mNormalizedProbability[i] = 0.0f; + } +} + +GroundCover::~GroundCover() +{ + SAFE_DELETE( mMatInst ); +} + +IMPLEMENT_CO_NETOBJECT_V1(GroundCover); + +void GroundCover::initPersistFields() +{ + addGroup( "GroundCover General" ); + + addField( "material", TypeMaterialName, Offset( mMaterialName, GroundCover ), "Material used by all GroundCover segments." ); + + addField( "radius", TypeF32, Offset( mRadius, GroundCover ), "Outer generation radius from the current camera position." ); + addField( "dissolveRadius",TypeF32, Offset( mFadeRadius, GroundCover ), "This is less than or equal to radius and defines when fading of cover elements begins." ); + addField( "reflectScale", TypeF32, Offset( mReflectRadiusScale, GroundCover ), "Scales the various culling radii when rendering a reflection. Typically for water." ); + + addField( "gridSize", TypeS32, Offset( mGridSize, GroundCover ), "The number of cells per axis in the grid." ); + addField( "zOffset", TypeF32, Offset( mZOffset, GroundCover ), "Offset along the Z axis to render the ground cover." ); + + addField( "seed", TypeS32, Offset( mRandomSeed, GroundCover ), "This RNG seed is saved and sent to clients for generating the same cover." ); + addField( "maxElements", TypeS32, Offset( mMaxPlacement, GroundCover ), "The maximum amount of cover elements to include in the grid at any one time." ); + + addField( "maxBillboardTiltAngle", TypeF32, Offset( mMaxBillboardTiltAngle, GroundCover ),"The maximum amout of degrees the billboard will tilt down to match the camera." ); + addField( "shapeCullRadius", TypeF32, Offset( mShapeCullRadius, GroundCover ), "This is the distance at which DTS elements are completely culled out." ); + addField( "shapesCastShadows", TypeBool, Offset( mShapesCastShadows, GroundCover ), "Whether DTS elements should cast shadows or not." ); + + addArray( "Types", MAX_COVERTYPES ); + + addField( "billboardUVs", TypeRectUV, Offset( mBillboardRects, GroundCover ), MAX_COVERTYPES, "Subset material UV coordinates for this cover billboard." ); + + addField( "shapeFilename", TypeFilename, Offset( mShapeFilenames, GroundCover ), MAX_COVERTYPES, "The cover shape filename. [Optional]" ); + + addField( "layer", TypeTerrainMaterialName, Offset( mLayer, GroundCover ), MAX_COVERTYPES, "Terrain material name to limit coverage to, or blank to not limit." ); + + addField( "invertLayer", TypeBool, Offset( mInvertLayer, GroundCover ), MAX_COVERTYPES, "Indicates that the terrain material index given in 'layer' is an exclusion mask." ); + + addField( "probability", TypeF32, Offset( mProbability, GroundCover ), MAX_COVERTYPES, "The probability of one cover type verses another (relative to all cover types)." ); + + addField( "sizeMin", TypeF32, Offset( mSizeMin, GroundCover ), MAX_COVERTYPES, "The minimum random size for each cover type." ); + + addField( "sizeMax", TypeF32, Offset( mSizeMax, GroundCover ), MAX_COVERTYPES, "The maximum random size of this cover type." ); + + addField( "sizeExponent", TypeF32, Offset( mSizeExponent, GroundCover ), MAX_COVERTYPES, "An exponent used to bias between the minimum and maximum random sizes." ); + + addField( "windScale", TypeF32, Offset( mWindScale, GroundCover ), MAX_COVERTYPES, "The wind effect scale." ); + + addField( "maxSlope", TypeF32, Offset( mMaxSlope, GroundCover ), MAX_COVERTYPES, "The maximum slope angle in degrees for placement." ); + + addField( "minElevation", TypeF32, Offset( mMinElevation, GroundCover ), MAX_COVERTYPES, "The minimum world space elevation for placement." ); + + addField( "maxElevation", TypeF32, Offset( mMaxElevation, GroundCover ), MAX_COVERTYPES, "The maximum world space elevation for placement." ); + + addField( "minClumpCount", TypeS32, Offset( mMinClumpCount, GroundCover ), MAX_COVERTYPES, "The minimum amount of elements in a clump." ); + + addField( "maxClumpCount", TypeS32, Offset( mMaxClumpCount, GroundCover ), MAX_COVERTYPES, "The maximum amount of elements in a clump." ); + + addField( "clumpExponent", TypeF32, Offset( mClumpCountExponent, GroundCover ), MAX_COVERTYPES, "An exponent used to bias between the minimum and maximum clump counts for a particular clump." ); + + addField( "clumpRadius", TypeF32, Offset( mClumpRadius, GroundCover ), MAX_COVERTYPES, "The maximum clump radius." ); + + endArray( "Types" ); + + endGroup( "GroundCover General" ); + + addGroup( "GroundCover Wind" ); + + addField( "windDirection", TypePoint2F, Offset( mWindDirection, GroundCover ), "The direction of the wind." ); + + addField( "windGustLength", TypeF32, Offset( mWindGustLength, GroundCover ), "The length in meters between peaks in the wind gust." ); + addField( "windGustFrequency",TypeF32, Offset( mWindGustFrequency, GroundCover ), "Controls how often the wind gust peaks per second." ); + addField( "windGustStrength", TypeF32, Offset( mWindGustStrength, GroundCover ), "The maximum distance in meters that the peak wind gust will displace an element." ); + + addField( "windTurbulenceFrequency", TypeF32, Offset( mWindTurbulenceFrequency, GroundCover ),"Controls the overall rapidity of the wind turbulence." ); + addField( "windTurbulenceStrength", TypeF32, Offset( mWindTurbulenceStrength, GroundCover ), "The maximum distance in meters that the turbulence can displace a ground cover element." ); + + endGroup( "GroundCover Wind" ); + + addGroup( "GroundCover Debug" ); + + addField( "lockFrustum", TypeBool, Offset( mDebugLockFrustum, GroundCover ), "Debug parameter for locking the culling frustum which will freeze the cover generation." ); + addField( "renderCells", TypeBool, Offset( mDebugRenderCells, GroundCover ), "Debug parameter for displaying the grid cells." ); + addField( "noBillboards", TypeBool, Offset( mDebugNoBillboards, GroundCover ), "Debug parameter for turning off billboard rendering." ); + addField( "noShapes", TypeBool, Offset( mDebugNoShapes, GroundCover ), "Debug parameter for turning off shape rendering." ); + + endGroup( "GroundCover Debug" ); + + Parent::initPersistFields(); +} + +void GroundCover::consoleInit() +{ + Con::addVariable( "$pref::GroundCover::densityScale", TypeF32, &smDensityScale, "A global LOD scalar which can reduce the overall density of placed GroundCover.\n" + "@ingroup Foliage\n"); + + Con::addVariable( "$GroundCover::renderedCells", TypeS32, &smStatRenderedCells, "Stat for number of rendered cells.\n" + "@ingroup Foliage\n"); + Con::addVariable( "$GroundCover::renderedBillboards", TypeS32, &smStatRenderedBillboards, "Stat for number of rendered billboards.\n" + "@ingroup Foliage\n"); + Con::addVariable( "$GroundCover::renderedBatches", TypeS32, &smStatRenderedBatches, "Stat for number of rendered billboard batches.\n" + "@ingroup Foliage\n"); + Con::addVariable( "$GroundCover::renderedShapes", TypeS32, &smStatRenderedShapes, "Stat for number of rendered shapes.\n" + "@ingroup Foliage\n"); + + Parent::consoleInit(); +} + +bool GroundCover::onAdd() +{ + if (!Parent::onAdd()) + return false; + + // We don't use any bounds. + setGlobalBounds(); + + resetWorldBox(); + + // Prepare some client side things. + if ( isClientObject() ) + { + _initMaterial(); + + _initShapes(); + + // Hook ourselves up to get terrain change notifications. + TerrainBlock::smUpdateSignal.notify( this, &GroundCover::onTerrainUpdated ); + } + + addToScene(); + + return true; +} + +void GroundCover::onRemove() +{ + Parent::onRemove(); + + _deleteCells(); + _deleteShapes(); + + if ( isClientObject() ) + { + TerrainBlock::smUpdateSignal.remove( this, &GroundCover::onTerrainUpdated ); + } + + removeFromScene(); +} + +void GroundCover::inspectPostApply() +{ + Parent::inspectPostApply(); + + // We flag all the parameters as changed because + // we're feeling lazy and there is not a good way + // to track what parameters changed. + // + // TODO: Add a mask bit option to addField() and/or + // addGroup() which is passed to inspectPostApply + // for detection of changed elements. + // + setMaskBits(U32(-1) ); +} + +U32 GroundCover::packUpdate( NetConnection *connection, U32 mask, BitStream *stream ) +{ + U32 retMask = Parent::packUpdate( connection, mask, stream ); + + if (stream->writeFlag(mask & InitialUpdateMask)) + { + // TODO: We could probably optimize a few of these + // based on reasonable units at some point. + + stream->write( mMaterialName ); + + stream->write( mRadius ); + stream->write( mZOffset ); + stream->write( mFadeRadius ); + stream->write( mShapeCullRadius ); + stream->writeFlag( mShapesCastShadows ); + stream->write( mReflectRadiusScale ); + stream->write( mGridSize ); + stream->write( mRandomSeed ); + stream->write( mMaxPlacement ); + stream->write( mMaxBillboardTiltAngle ); + + stream->write( mWindDirection.x ); + stream->write( mWindDirection.y ); + stream->write( mWindGustLength ); + stream->write( mWindGustFrequency ); + stream->write( mWindGustStrength ); + stream->write( mWindTurbulenceFrequency ); + stream->write( mWindTurbulenceStrength ); + + for ( S32 i=0; i < MAX_COVERTYPES; i++ ) + { + stream->write( mProbability[i] ); + stream->write( mSizeMin[i] ); + stream->write( mSizeMax[i] ); + stream->write( mSizeExponent[i] ); + stream->write( mWindScale[i] ); + + stream->write( mMaxSlope[i] ); + + stream->write( mMinElevation[i] ); + stream->write( mMaxElevation[i] ); + + stream->writeString( mLayer[i] ); + stream->writeFlag( mInvertLayer[i] ); + + stream->write( mMinClumpCount[i] ); + stream->write( mMaxClumpCount[i] ); + stream->write( mClumpCountExponent[i] ); + stream->write( mClumpRadius[i] ); + + stream->write( mBillboardRects[i].point.x ); + stream->write( mBillboardRects[i].point.y ); + stream->write( mBillboardRects[i].extent.x ); + stream->write( mBillboardRects[i].extent.y ); + + stream->writeString( mShapeFilenames[i] ); + } + + stream->writeFlag( mDebugRenderCells ); + stream->writeFlag( mDebugNoBillboards ); + stream->writeFlag( mDebugNoShapes ); + stream->writeFlag( mDebugLockFrustum ); + } + + return retMask; +} + +void GroundCover::unpackUpdate( NetConnection *connection, BitStream *stream ) +{ + Parent::unpackUpdate( connection, stream ); + + if (stream->readFlag()) + { + stream->read( &mMaterialName ); + + stream->read( &mRadius ); + stream->read( &mZOffset ); + stream->read( &mFadeRadius ); + stream->read( &mShapeCullRadius ); + mShapesCastShadows = stream->readFlag(); + stream->read( &mReflectRadiusScale ); + stream->read( &mGridSize ); + stream->read( &mRandomSeed ); + stream->read( &mMaxPlacement ); + stream->read( &mMaxBillboardTiltAngle ); + + stream->read( &mWindDirection.x ); + stream->read( &mWindDirection.y ); + stream->read( &mWindGustLength ); + stream->read( &mWindGustFrequency ); + stream->read( &mWindGustStrength ); + stream->read( &mWindTurbulenceFrequency ); + stream->read( &mWindTurbulenceStrength ); + + for ( S32 i=0; i < MAX_COVERTYPES; i++ ) + { + stream->read( &mProbability[i] ); + stream->read( &mSizeMin[i] ); + stream->read( &mSizeMax[i] ); + stream->read( &mSizeExponent[i] ); + stream->read( &mWindScale[i] ); + + stream->read( &mMaxSlope[i] ); + + stream->read( &mMinElevation[i] ); + stream->read( &mMaxElevation[i] ); + + mLayer[i] = stream->readSTString(); + mInvertLayer[i] = stream->readFlag(); + + stream->read( &mMinClumpCount[i] ); + stream->read( &mMaxClumpCount[i] ); + stream->read( &mClumpCountExponent[i] ); + stream->read( &mClumpRadius[i] ); + + stream->read( &mBillboardRects[i].point.x ); + stream->read( &mBillboardRects[i].point.y ); + stream->read( &mBillboardRects[i].extent.x ); + stream->read( &mBillboardRects[i].extent.y ); + + mShapeFilenames[i] = stream->readSTString(); + } + + mDebugRenderCells = stream->readFlag(); + mDebugNoBillboards = stream->readFlag(); + mDebugNoShapes = stream->readFlag(); + mDebugLockFrustum = stream->readFlag(); + + // We have no way to easily know what changed, so by clearing + // the cells we force a reinit and regeneration of the cells. + // It's sloppy, but it works for now. + _freeCells(); + + if ( isProperlyAdded() ) + _initMaterial(); + } +} + +void GroundCover::_initMaterial() +{ + SAFE_DELETE( mMatInst ); + + if ( mMaterialName.isNotEmpty() ) + if ( !Sim::findObject( mMaterialName, mMaterial ) ) + Con::errorf( "GroundCover::_initMaterial - Material %s was not found.", mMaterialName.c_str() ); + + if ( mMaterial ) + mMatInst = mMaterial->createMatInstance(); + else + mMatInst = MATMGR->createMatInstance( "WarningMaterial" ); + + // Add our special feature that makes it all work... + FeatureSet features = MATMGR->getDefaultFeatures(); + features.addFeature( MFT_Foliage ); + + // Our feature requires a pointer back to this object + // to properly setup its shader consts. + mMatInst->setUserObject( this ); + + // DO IT! + mMatInst->init( features, getGFXVertexFormat() ); +} + +void GroundCover::_initShapes() +{ + _deleteShapes(); + + for ( S32 i=0; i < MAX_COVERTYPES; i++ ) + { + if ( !mShapeFilenames[i] || !mShapeFilenames[i][0] ) + continue; + + // Load the shape. + Resource shape = ResourceManager::get().load(mShapeFilenames[i]); + if ( !(bool)shape ) + { + Con::warnf( "GroundCover::_initShapes() unable to load shape: %s", mShapeFilenames[i] ); + continue; + } + + if ( isClientObject() && !shape->preloadMaterialList(shape.getPath()) && NetConnection::filesWereDownloaded() ) + { + Con::warnf( "GroundCover::_initShapes() material preload failed for shape: %s", mShapeFilenames[i] ); + continue; + } + + // Create the shape instance. + mShapeInstances[i] = new TSShapeInstance( shape, isClientObject() ); + } +} + +void GroundCover::_deleteShapes() +{ + for ( S32 i=0; i < MAX_COVERTYPES; i++ ) + { + delete mShapeInstances[i]; + mShapeInstances[i] = NULL; + } +} + +void GroundCover::_deleteCells() +{ + // Delete the allocation list. + for ( S32 i=0; i < mAllocCellList.size(); i++ ) + delete mAllocCellList[i]; + mAllocCellList.clear(); + + // Zero out the rest of the stuff. + _freeCells(); +} + +void GroundCover::_freeCells() +{ + // Zero the grid and scratch space. + mCellGrid.clear(); + mScratchGrid.clear(); + + // Compact things... remove excess allocated cells. + const U32 maxCells = mGridSize * mGridSize; + if ( mAllocCellList.size() > maxCells ) + { + for ( S32 i=maxCells; i < mAllocCellList.size(); i++ ) + delete mAllocCellList[i]; + mAllocCellList.setSize( maxCells ); + } + + // Move all the alloced cells into the free list. + mFreeCellList.clear(); + mFreeCellList.merge( mAllocCellList ); + + // Release the primitive buffer. + mPrimBuffer = NULL; +} + +void GroundCover::_recycleCell( GroundCoverCell* cell ) +{ + mFreeCellList.push_back( cell ); +} + +void GroundCover::_initialize( U32 cellCount, U32 cellPlacementCount ) +{ + // Cleanup everything... we're starting over. + _freeCells(); + _deleteShapes(); + + // Nothing to do without a count! + if ( cellPlacementCount == 0 ) + return; + + // Reset the grid sizes. + mCellGrid.setSize( cellCount ); + dMemset( mCellGrid.address(), 0, mCellGrid.memSize() ); + mScratchGrid.setSize( cellCount ); + + // Rebuild the texture aspect scales for each type. + F32 textureAspect = 1.0f; + if( mMatInst && mMatInst->isValid()) + { + Material* mat = dynamic_cast(mMatInst->getMaterial()); + if(mat) + { + GFXTexHandle tex(mat->mDiffuseMapFilename[0], &GFXDefaultStaticDiffuseProfile, "GroundCover texture aspect ratio check" ); + if(tex.isValid()) + { + U32 w = tex.getWidth(); + U32 h = tex.getHeight(); + if(h > 0) + textureAspect = F32(w) / F32(h); + } + } + } + for ( S32 i=0; i < MAX_COVERTYPES; i++ ) + { + if ( mBillboardRects[i].extent.y > 0.0f ) + { + mBillboardAspectScales[i] = textureAspect * mBillboardRects[i].extent.x / mBillboardRects[i].extent.y; + } + else + mBillboardAspectScales[i] = 0.0f; + } + + // Load the shapes again. + _initShapes(); + + // Set the primitive buffer up for the maximum placement in a cell. + mPrimBuffer.set( GFX, cellPlacementCount * 6, 0, GFXBufferTypeStatic ); + U16 *idxBuff; + mPrimBuffer.lock(&idxBuff); + for ( U32 i=0; i < cellPlacementCount; i++ ) + { + // + // The vertex pattern in the VB for each + // billboard is as follows... + // + // 0----1 + // |\ | + // | \ | + // | \ | + // | \| + // 3----2 + // + // We setup the index order below to ensure + // sequential, cache friendly, access. + // + U32 offset = i * 4; + idxBuff[i*6+0] = 0 + offset; + idxBuff[i*6+1] = 1 + offset; + idxBuff[i*6+2] = 2 + offset; + idxBuff[i*6+3] = 2 + offset; + idxBuff[i*6+4] = 3 + offset; + idxBuff[i*6+5] = 0 + offset; + } + mPrimBuffer.unlock(); + + // Generate the normalized probability. + F32 total = 0.0f; + for ( S32 i=0; i < MAX_COVERTYPES; i++ ) + { + // If the element isn't gonna render... then + // set the probability to zero. + if ( mShapeInstances[i] == NULL && mBillboardAspectScales[i] <= 0.0001f ) + { + mNormalizedProbability[i] = 0.0f; + } + else + { + mNormalizedProbability[i] = mProbability[i]; + + total += mProbability[i]; + } + } + if ( total > 0.0f ) + { + for ( S32 i=0; i < MAX_COVERTYPES; i++ ) + mNormalizedProbability[i] /= total; + } +} + +GroundCoverCell* GroundCover::_generateCell( const Point2I& index, + const Box3F& bounds, + U32 placementCount, + S32 randSeed ) +{ + PROFILE_SCOPE(GroundCover_GenerateCell); + + const Vector terrainBlocks = getContainer()->getTerrains(); + if ( terrainBlocks.empty() ) + return NULL; + + // Grab a free cell or allocate a new one. + GroundCoverCell* cell; + if ( mFreeCellList.empty() ) + { + cell = new GroundCoverCell(); + mAllocCellList.push_back( cell ); + } + else + { + cell = mFreeCellList.last(); + mFreeCellList.pop_back(); + } + + cell->mDirty = true; + cell->mIndex = index; + cell->mBounds = bounds; + + Point3F pos( 0, 0, 0 ); + + Box3F renderBounds = bounds; + Point3F point; + Point3F normal; + F32 h; + Point2F cp, uv; + bool hit; + GroundCoverCell::Placement p; + F32 rotation; + F32 size; + F32 sizeExponent; + Point2I lpos; + //F32 value; + VectorF right; + StringTableEntry matName = StringTable->EmptyString(); + bool firstElem = true; + + TerrainBlock *terrainBlock = NULL; + + cell->mBillboards.clear(); + cell->mBillboards.reserve( placementCount ); + cell->mShapes.clear(); + cell->mShapes.reserve( placementCount ); + + F32 terrainSquareSize, + oneOverTerrainLength, + oneOverTerrainSquareSize; + const GBitmap* terrainLM = NULL; + + // The RNG that we'll use in generation. + MRandom rand( 0 ); + + // We process one type at a time. + for ( U32 type=0; type < MAX_COVERTYPES; type++ ) + { + // How many cover elements do we need to generate for this type? + const S32 typeCount = mNormalizedProbability[type] * (F32)placementCount; + if ( typeCount <= 0 ) + continue; + + // Grab the terrain layer for this type. + /* + const TerrainDataLayer* dataLayer = NULL; + const bool typeInvertLayer = mInvertLayer[type]; + if ( mLayer[type] > -1 ) + { + dataLayer = mTerrainBlock->getDataLayer( mLayer[type] ); + if ( dataLayer ) + { + // Do an initial check to see if we can place any place anything + // at all... if the layer area for this element is empty then + // there is nothing more to do. + + RectI area( (S32)mFloor( ( bounds.minExtents.x - pos.x ) * oneOverTerrainSquareSize ), + (S32)mFloor( ( bounds.minExtents.y - pos.y ) * oneOverTerrainSquareSize ), + (S32)mCeil( ( bounds.maxExtents.x - pos.x ) * oneOverTerrainSquareSize ), + (S32)mCeil( ( bounds.maxExtents.y - pos.y ) * oneOverTerrainSquareSize ) ); + area.extent -= area.point; + + if ( dataLayer->testFill( area, typeInvertLayer ? 255 : 0 ) ) + continue; + } + } + + // If the layer is not inverted and we have no data + // then we have nothing to draw. + if ( !typeInvertLayer && !dataLayer ) + continue; + */ + + // We set the seed we were passed which is based on this grids position + // in the world and add the type value. This keeps changes to one type + // from effecting the outcome of the others. + rand.setSeed( randSeed + type ); + + // Setup for doing clumps. + S32 clumps = 0; + Point2F clumpCenter(0.0f, 0.0f); + const S32 clumpMin = getMax( 1, (S32)mMinClumpCount[type] ); + F32 clumpExponent; + + // We mult this by -1 each billboard we make then use + // it to scale the billboard x axis to flip them. This + // essentially gives us twice the variation for free. + F32 flipBB = -1.0f; + + // Precompute a few other type specific values. + const F32 typeSizeRange = mSizeMax[type] - mSizeMin[type]; + const F32 typeMaxSlope = mMaxSlope[type]; + const F32 typeMaxElevation = mMaxElevation[type]; + const F32 typeMinElevation = mMinElevation[type]; + const bool typeIsShape = mShapeInstances[ type ] != NULL; + const Box3F typeShapeBounds = typeIsShape ? mShapeInstances[ type ]->getShape()->bounds : Box3F(); + const F32 typeWindScale = mWindScale[type]; + StringTableEntry typeLayer = mLayer[type]; + const bool typeInvertLayer = mInvertLayer[type]; + + // We can set this once here... all the placements for this are the same. + p.type = type; + p.windAmplitude = typeWindScale; + p.lmColor.set(1.0f,1.0f,1.0f); + + // Generate all the cover elements for this type. + for ( S32 i=0; i < typeCount; i++ ) + { + // Do all the other random things here first as to not + // disturb the random sequence if the terrain geometry + // or cover layers change. + + // Get the random position. + cp.set( rand.randF(), rand.randF() ); + + // Prepare the clump info. + clumpExponent = mClampF( mPow( rand.randF(), mClumpCountExponent[type] ), 0.0f, 1.0f ); + if ( clumps <= 0 ) + { + // We're starting a new clump. + clumps = ( clumpMin + mFloor( ( mMaxClumpCount[type] - clumpMin ) * clumpExponent ) ) - 1; + cp.set( bounds.minExtents.x + cp.x * bounds.len_x(), + bounds.minExtents.y + cp.y * bounds.len_y() ); + clumpCenter = cp; + } + else + { + clumps--; + cp.set( clumpCenter.x - ( ( cp.x - 0.5f ) * mClumpRadius[type] ), + clumpCenter.y - ( ( cp.y - 0.5f ) * mClumpRadius[type] ) ); + } + + // Which terrain do I place on? + if ( terrainBlocks.size() == 1 ) + terrainBlock = dynamic_cast< TerrainBlock* >( terrainBlocks.first() ); + else + { + for ( U32 i = 0; i < terrainBlocks.size(); i++ ) + { + TerrainBlock *terrain = dynamic_cast< TerrainBlock* >( terrainBlocks[ i ] ); + if( !terrain ) + continue; + + const Box3F &terrBounds = terrain->getWorldBox(); + + if ( cp.x < terrBounds.minExtents.x || cp.x > terrBounds.maxExtents.x || + cp.y < terrBounds.minExtents.y || cp.y > terrBounds.maxExtents.y ) + continue; + + terrainBlock = terrain; + break; + } + } + + // This should only happen if the generation went off + // the edge of the terrain blocks. + if ( !terrainBlock ) + continue; + + terrainLM = terrainBlock->getLightMap(); + pos = terrainBlock->getPosition(); + + terrainSquareSize = (F32)terrainBlock->getSquareSize(); + oneOverTerrainLength = 1.0f / terrainBlock->getWorldBlockSize(); + oneOverTerrainSquareSize = 1.0f / terrainSquareSize; + + // The size is calculated using an exponent to control + // the frequency between min and max sizes. + sizeExponent = mClampF( mPow( rand.randF(), mSizeExponent[type] ), 0.0f, 1.0f ); + size = mSizeMin[type] + ( typeSizeRange * sizeExponent ); + + // Generate a random z rotation. + rotation = rand.randF() * M_2PI_F; + + // Flip the billboard now for the next generation. + flipBB *= -1.0f; + + PROFILE_START( GroundCover_TerrainRayCast ); + hit = terrainBlock->getNormalHeightMaterial( Point2F( cp.x - pos.x, cp.y - pos.y ), + &normal, &h, matName ); + + // TODO: When did we loose the world space elevation when + // getting the terrain height? + h += pos.z + mZOffset; + + PROFILE_END(); // GroundCover_TerrainRayCast + if ( !hit || h > typeMaxElevation || h < typeMinElevation || + ( typeLayer[0] && !typeInvertLayer && matName != typeLayer ) || + ( typeLayer[0] && typeInvertLayer && matName == typeLayer ) ) + continue; + + // Do we need to check slope? + if ( !mIsZero( typeMaxSlope ) ) + { + if (mAcos(normal.z) > mDegToRad(typeMaxSlope)) + continue; + } + + point.set( cp.x, cp.y, h ); + p.point = point; + p.rotation = rotation; + p.normal = normal; + + // Grab the terrain lightmap color at this position. + // + // TODO: Can't we remove this test? The terrain + // lightmap should never be null... NEVER! + // + if ( terrainLM ) + { + // TODO: We could probably call terrainLM->getBits() + // once outside the loop then pre-calculate the scalar + // for converting a world position into a lexel... + // avoiding the extra protections inside of sampleTexel(). + + uv.x = (point.x + pos.x) * oneOverTerrainLength; + uv.y = (point.y + pos.y) * oneOverTerrainLength; + uv.x -= mFloor(uv.x); + uv.y -= mFloor(uv.y); + p.lmColor = terrainLM->sampleTexel(uv.x,uv.y); + } + + // Put it into the right list by type. + // + // TODO: Could we break up the generation into + // two separate loops for shapes and billboards + // and gain performance? + // + if ( typeIsShape ) + { + // TODO: Convert the size into a real size... not scale! + + // TODO: We could probably cache the shape bounds + // into a primitive array and avoid the double pointer + // dereference per placement. + + p.size.set( size, size, size ); + p.worldBox = typeShapeBounds; + p.worldBox.minExtents *= size; + p.worldBox.maxExtents *= size; + p.worldBox.minExtents += point; + p.worldBox.maxExtents += point; + + cell->mShapes.push_back( p ); + } + else + { + p.size.y = size; + p.size.x = size * flipBB * mBillboardAspectScales[type]; + p.worldBox.maxExtents = p.worldBox.minExtents = point; + + cell->mBillboards.push_back( p ); + } + + // Update the render bounds. + if ( firstElem ) + { + renderBounds = p.worldBox; + firstElem = false; + } + else + { + renderBounds.extend( p.worldBox.minExtents ); + renderBounds.extend( p.worldBox.maxExtents ); + } + + } // for ( S32 i=0; i < typeCount; i++ ) + + } // for ( U32 type=0; type < NumCoverTypes; type++ ) + + + cell->mRenderBounds = renderBounds; + cell->mBounds.minExtents.z = renderBounds.minExtents.z; + cell->mBounds.maxExtents.z = renderBounds.maxExtents.z; + + return cell; +} + +void GroundCover::onTerrainUpdated( U32 flags, TerrainBlock *tblock, const Point2I& min, const Point2I& max ) +{ + if ( isServerObject() ) + return; + + // Free all the cells if we've gotten a lightmap update. + if ( flags & TerrainBlock::LightmapUpdate ) + { + _freeCells(); + return; + } + + // TODO: EmptyUpdate doesn't work yet... fix editor/terrain. + + // If this is a height or opacity update only clear + // the cells that have changed. + if ( flags & TerrainBlock::HeightmapUpdate || + flags & TerrainBlock::LayersUpdate || + flags & TerrainBlock::EmptyUpdate ) + { + // Convert the min and max into world space. + const F32 size = tblock->getSquareSize(); + const Point3F pos = tblock->getPosition(); + + // TODO: I don't think this works right with tiling! + Box3F dirty( F32( min.x * size ) + pos.x, F32( min.y * size ) + pos.y, 0.0f, + F32( max.x * size ) + pos.x, F32( max.y * size ) + pos.y, 0.0f ); + + // Now free any cells that overlap it! + for ( S32 i = 0; i < mCellGrid.size(); i++ ) + { + GroundCoverCell* cell = mCellGrid[ i ]; + if ( !cell ) + continue; + + const Box3F& bounds = cell->getBounds(); + dirty.minExtents.z = bounds.minExtents.z; + dirty.maxExtents.z = bounds.maxExtents.z; + if ( bounds.isOverlapped( dirty ) ) + { + mCellGrid[ i ] = NULL; + _recycleCell( cell ); + } + } + } +} + +void GroundCover::_updateCoverGrid( const Frustum &culler ) +{ + PROFILE_SCOPE( GroundCover_UpdateCoverGrid ); + + mGridSize = getMax( mGridSize, (U32)2 ); + + // How many cells in the grid? + const U32 cells = mGridSize * mGridSize; + + // Whats the max placement count for each cell considering + // the grid size and quality scale LOD value. + const S32 placementCount = getMax( ( (F32)mMaxPlacement * smDensityScale ) / F32( mGridSize * mGridSize ), 0.0f ); + + // If the cell grid isn't sized or the placement count + // changed (most likely because of quality lod) then we + // need to initialize the system again. + if ( mCellGrid.empty() || placementCount != mLastPlacementCount ) + { + _initialize( cells, placementCount ); + mLastPlacementCount = placementCount; + } + + // Without a count... we don't function at all. + if ( placementCount == 0 ) + return; + + // Clear the scratch grid. + dMemset( mScratchGrid.address(), 0, mScratchGrid.memSize() ); + + // Calculate the normal cell size here. + const F32 cellSize = ( mRadius * 2.0f ) / (F32)(mGridSize - 1); + + // Figure out the root index of the new grid based on the camera position. + Point2I index( (S32)mFloor( ( culler.getPosition().x - mRadius ) / cellSize ), + (S32)mFloor( ( culler.getPosition().y - mRadius ) / cellSize ) ); + + // Figure out the cell shift between the old and new grid positions. + Point2I shift = mGridIndex - index; + + // If we've shifted more than one in either axis then we've warped. + bool didWarp = shift.x > 1 || shift.x < -1 || + shift.y > 1 || shift.y < -1 ? true : false; + + // Go thru the grid shifting each cell we find and + // placing them in the scratch grid. + for ( S32 i = 0; i < mCellGrid.size(); i++ ) + { + GroundCoverCell* cell = mCellGrid[ i ]; + if ( !cell ) + continue; + + // Whats our new index? + Point2I newIndex = cell->shiftIndex( shift ); + + // Is this cell outside of the new grid? + if ( newIndex.x < 0 || newIndex.x >= mGridSize || + newIndex.y < 0 || newIndex.y >= mGridSize ) + { + _recycleCell( cell ); + continue; + } + + // Place the cell in the scratch grid. + mScratchGrid[ ( newIndex.y * mGridSize ) + newIndex.x ] = cell; + } + + // Get the terrain elevation range for setting the default cell bounds. + F32 terrainMinHeight = -5000.0f, + terrainMaxHeight = 5000.0f; + + // Go thru the scratch grid copying each cell back to the + // cell grid and creating new cells as needed. + // + // By limiting ourselves to only one new cell generation per + // update we're lowering the performance hiccup during movement + // without getting into the complexity of threading. The delay + // in generation is rarely noticeable in normal play. + // + // The only caveat is that we need to generate the entire visible + // grid when we warp. + U32 cellsGenerated = 0; + for ( S32 i = 0; i < mScratchGrid.size(); i++ ) + { + GroundCoverCell* cell = mScratchGrid[ i ]; + if ( !cell && ( cellsGenerated == 0 || didWarp ) ) + { + // Get the index point of this new cell. + S32 y = i / mGridSize; + S32 x = i - ( y * mGridSize ); + Point2I newIndex = index + Point2I( x, y ); + + // What will be the world placement bounds for this cell. + Box3F bounds; + bounds.minExtents.set( newIndex.x * cellSize, newIndex.y * cellSize, terrainMinHeight ); + bounds.maxExtents.set( bounds.minExtents.x + cellSize, bounds.minExtents.y + cellSize, terrainMaxHeight ); + + if ( mCuller.isCulled( bounds ) ) + { + mCellGrid[ i ] = NULL; + continue; + } + + // We need to allocate a new cell. + // + // TODO: This is the expensive call and where we should optimize. In + // particular the next best optimization would be to take advantage of + // multiple cores so that we can generate all the cells in one update. + // + // Instead of generating the cell here we would allocate a cell and stick + // it into a thread safe queue (maybe lockless) as well as the mCellGrid. + // Once all were allocated we would do something like this... + // + // TorqueParallelProcess( cellsToGenerateQueue, _generateCell ); + // + // Internally this function would pass the queue to some global pre-allocated + // worker threads which are locked to a particular core. While the main + // thread waits for the worker threads to finish it will process cells itself. + // + + cell = _generateCell( newIndex - index, + bounds, + placementCount, + mRandomSeed + mAbs( newIndex.x ) + mAbs( newIndex.y ) ); + + // Increment our generation count. + if ( cell ) + ++cellsGenerated; + } + + mCellGrid[ i ] = cell; + } + + // Store the new grid index. + mGridIndex = index; +} + +void GroundCover::prepRenderImage( SceneRenderState *state ) +{ + // Reset stats each time we hit the diffuse pass. + + if( state->isDiffusePass() ) + { + smStatRenderedCells = 0; + smStatRenderedBillboards = 0; + smStatRenderedBatches = 0; + smStatRenderedShapes = 0; + } + + // TODO: Make sure that the ground cover stops rendering + // if you're inside a zoned interior. + + if ( state->isReflectPass() && mReflectRadiusScale <= 0.0f ) + return; + + // Nothing to do if this is a shadow pass and shapes in this GoundCover + // should not be casting shadows. + + if( state->isShadowPass() && !mShapesCastShadows ) + return; + + GFXTransformSaver saver; + + // Setup the frustum culler. + if ( ( mCuller.getPosition().isZero() || !mDebugLockFrustum ) && !state->isShadowPass() ) + mCuller = state->getFrustum(); + + // Update the cells, but only during the diffuse pass. + // We don't want cell generation to thrash when the reflection camera + // position doesn't match the diffuse camera! + if ( state->isDiffusePass() ) + _updateCoverGrid( mCuller ); + + // Render billboards but not into shadow passes. + + if ( !state->isShadowPass() && mMatInst->isValid() && !mDebugNoBillboards ) + { + PROFILE_SCOPE( GroundCover_RenderBillboards ); + + // Set the far distance for billboards. + mCuller.setFarDist( mRadius ); + + F32 cullScale = 1.0f; + if ( state->isReflectPass() ) + cullScale = mReflectRadiusScale; + + // Setup our shader const data. + // Must be done prior to submitting our render instance. + + mShaderConstData.fadeInfo.set( mFadeRadius * cullScale, mRadius * cullScale ); + + const F32 simTime = Sim::getCurrentTime() * 0.001f; + + mShaderConstData.gustInfo.set( mWindGustLength, mWindGustFrequency * simTime, mWindGustStrength ); + mShaderConstData.turbInfo.set( mWindTurbulenceFrequency * simTime, mWindTurbulenceStrength ); + + const MatrixF &camMat = state->getDiffuseCameraTransform(); + Point3F camDir, camUp, camRight; + camMat.getColumn( 1, &camDir ); + camMat.getColumn( 2, &camUp ); + camMat.getColumn( 0, &camRight ); + + // Limit the camera up vector to keep the billboards + // from leaning too far down into the terrain. + VectorF lookDir( camDir.x, camDir.y, 0.0f ); + F32 angle; + if ( !lookDir.isZero() ) + { + lookDir.normalize(); + angle = mAcos( mDot( camUp, lookDir ) ); + } + else + { + angle = camDir.z < 0.0f ? 0.0f : ( M_PI_F / 2.0f ); + } + + const F32 maxBillboardTiltRads = mDegToRad( mMaxBillboardTiltAngle ); + if ( angle < (M_PI_F / 2.0f) - maxBillboardTiltRads ) + { + QuatF quat( AngAxisF( camRight, maxBillboardTiltRads ) ); + quat.mulP( VectorF( 0.0f, 0.0f, 1.0f ), &camUp ); + } + + mShaderConstData.camRight = camRight; + mShaderConstData.camUp = camUp; + + + // Cull and submit render instances for cells. + + for ( S32 i = 0; i < mCellGrid.size(); i++ ) + { + GroundCoverCell* cell = mCellGrid[ i ]; + if ( !cell ) + continue; + + if ( mCuller.isCulled( cell->getRenderBounds() ) ) + continue; + + cell->renderBillboards( state, mMatInst, &mPrimBuffer ); + } + } + + // Render TS shapes. + + if ( !mDebugNoShapes ) + { + // Prepare to render the grid shapes. + PROFILE_SCOPE(GroundCover_RenderShapes); + + // Set up our TS render state. + TSRenderState rdata; + rdata.setSceneState( state ); + + // We might have some forward lit materials + // so pass down a query to gather lights. + LightQuery query; + rdata.setLightQuery( &query ); + + // TODO: Add a special fade out for DTS? + mCuller.setFarDist( mShapeCullRadius ); + + for ( S32 i = 0; i < mCellGrid.size(); i++ ) + { + GroundCoverCell* cell = mCellGrid[ i ]; + if ( !cell || mDebugNoShapes ) + continue; + + const Box3F &renderBounds = cell->getRenderBounds(); + U32 clipMask = mCuller.testPlanes( renderBounds, Frustum::PlaneMaskAll ); + if ( clipMask == -1 ) + continue; + + smStatRenderedCells++; + + // Use the cell bounds as the light query volume. + // + // This means all forward lit items in this cell will + // get the same lights, but it performs much better. + query.init( renderBounds ); + + // Render the shapes in this cell... only pass the culler if the + // cell wasn't fully within the frustum. + smStatRenderedShapes += cell->renderShapes( + rdata, + clipMask != 0 ? &mCuller : NULL, + mShapeInstances ); + } + } + + if ( mDebugRenderCells ) + { + RenderPassManager *pass = state->getRenderPass(); + + ObjectRenderInst *ri = pass->allocInst(); + ri->type = RenderPassManager::RIT_Editor; + ri->renderDelegate.bind( this, &GroundCover::_debugRender ); + pass->addInst( ri ); + } +} + +void GroundCover::_debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ) +{ + GFXDrawUtil* drawer = GFX->getDrawUtil(); + + GFXStateBlockDesc desc; + desc.setZReadWrite( true, false ); + desc.setBlend( true ); + desc.fillMode = GFXFillWireframe; + + for ( S32 i = 0; i < mCellGrid.size(); i++ ) + { + GroundCoverCell* cell = mCellGrid[ i ]; + if ( !cell || ( cell->mBillboards.size() + cell->mShapes.size() ) == 0 ) + continue; + + if ( mCuller.isCulled( cell->getRenderBounds() ) ) + continue; + + drawer->drawCube( desc, cell->getRenderBounds().getExtents(), cell->getRenderBounds().getCenter(), ColorI( 0, 255, 0 ) ); + } +} diff --git a/Engine/source/T3D/fx/groundCover.h b/Engine/source/T3D/fx/groundCover.h new file mode 100644 index 000000000..256f6770e --- /dev/null +++ b/Engine/source/T3D/fx/groundCover.h @@ -0,0 +1,391 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GROUNDCOVER_H_ +#define _GROUNDCOVER_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _MATHUTIL_FRUSTUM_H_ +#include "math/util/frustum.h" +#endif +#ifndef _GFXTEXTUREHANDLE_H_ +#include "gfx/gfxTextureHandle.h" +#endif +#ifndef _GFX_GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif +#ifndef _RENDERPASSMANAGER_H_ +#include "renderInstance/renderPassManager.h" +#endif +#ifndef _MATTEXTURETARGET_H_ +#include "materials/matTextureTarget.h" +#endif +#ifndef _SHADERFEATURE_H_ +#include "shaderGen/shaderFeature.h" +#endif + +class TerrainBlock; +class GroundCoverCell; +class TSShapeInstance; +class Material; +class MaterialParameters; +class MaterialParameterHandle; + + +/// +#define MAX_COVERTYPES 8 + + +GFXDeclareVertexFormat( GCVertex ) +{ + Point3F point; + + Point3F normal; + + // .rgb = ambient + // .a = corner index + GFXVertexColor ambient; + + // .x = size x + // .y = size y + // .z = type + // .w = wind amplitude + Point4F params; +}; + +struct GroundCoverShaderConstData +{ + Point2F fadeInfo; + Point3F gustInfo; + Point2F turbInfo; + Point3F camRight; + Point3F camUp; +}; + +class GroundCover; + +class GroundCoverShaderConstHandles : public ShaderFeatureConstHandles +{ +public: + + GroundCoverShaderConstHandles(); + + virtual void init( GFXShader *shader ); + + virtual void setConsts( SceneRenderState *state, + const SceneData &sgData, + GFXShaderConstBuffer *buffer ); + + GroundCover *mGroundCover; + + GFXShaderConstHandle *mTypeRectsSC; + GFXShaderConstHandle *mFadeSC; + GFXShaderConstHandle *mWindDirSC; + GFXShaderConstHandle *mGustInfoSC; + GFXShaderConstHandle *mTurbInfoSC; + GFXShaderConstHandle *mCamRightSC; + GFXShaderConstHandle *mCamUpSC; +}; + + +class GroundCover : public SceneObject +{ + friend class GroundCoverShaderConstHandles; + friend class GroundCoverCell; + typedef SceneObject Parent; + +public: + + GroundCover(); + ~GroundCover(); + + DECLARE_CONOBJECT(GroundCover); + + static void consoleInit(); + static void initPersistFields(); + + bool onAdd(); + void onRemove(); + void inspectPostApply(); + + // Network + U32 packUpdate( NetConnection *, U32 mask, BitStream *stream ); + void unpackUpdate( NetConnection *, BitStream *stream ); + + // Rendering + void prepRenderImage( SceneRenderState *state ); + + // Editor + void onTerrainUpdated( U32 flags, TerrainBlock *tblock, const Point2I& min, const Point2I& max ); + + // Misc + const GroundCoverShaderConstData& getShaderConstData() const { return mShaderConstData; } + + /// Sets the global ground cover LOD scalar which controls + /// the percentage of the maximum designed cover to put down. + /// It scales both rendering cost and placement CPU performance. + /// Returns the actual value set. + static F32 setQualityScale( F32 scale ) { return smDensityScale = mClampF( scale, 0.0f, 1.0f ); } + + /// Returns the current quality scale... see above. + static F32 getQualityScale() { return smDensityScale; } + +protected: + + enum MaskBits + { + TerrainBlockMask = Parent::NextFreeMask << 0, + NextFreeMask = Parent::NextFreeMask << 1 + }; + + MaterialParameters *mMatParams; + MaterialParameterHandle *mTypeRectsParam; + MaterialParameterHandle *mFadeParams; + MaterialParameterHandle *mWindDirParam; + MaterialParameterHandle *mGustInfoParam; + MaterialParameterHandle *mTurbInfoParam; + MaterialParameterHandle *mCamRightParam; + MaterialParameterHandle *mCamUpParam; + + /// This RNG seed is saved and sent to clients + /// for generating the same cover. + S32 mRandomSeed; + + /// This is the outer generation radius from + /// the current camera position. + F32 mRadius; + + // Offset along the Z axis to render the ground cover. + F32 mZOffset; + + /// This is less than or equal to mRadius and + /// defines when fading of cover elements begins. + F32 mFadeRadius; + + /// This is the distance at which DTS elements are + /// completely culled out. + F32 mShapeCullRadius; + + /// Whether shapes rendered by the GroundCover should cast shadows. + bool mShapesCastShadows; + + /// This is used to scale the various culling radii + /// when rendering a reflection... typically for water. + F32 mReflectRadiusScale; + + /// This is the number of cells per axis in the grid. + U32 mGridSize; + + typedef Vector CellVector; + + /// This is the allocator for GridCell chunks. + CellVector mAllocCellList; + CellVector mFreeCellList; + + /// This is the grid of active cells. + CellVector mCellGrid; + + /// This is a scratch grid used while updating + /// the cell grid. + CellVector mScratchGrid; + + /// This is the index to the first grid cell. + Point2I mGridIndex; + + /// The maximum amount of cover elements to include in + /// the grid at any one time. The actual amount may be + /// less than this based on randomization. + S32 mMaxPlacement; + + /// Used to detect changes in cell placement count from + /// the global quality scale so we can regen the cells. + S32 mLastPlacementCount; + + /// Used for culling cells to update and render. + Frustum mCuller; + + /// Debug parameter for displaying the grid cells. + bool mDebugRenderCells; + + /// Debug parameter for turning off billboard rendering. + bool mDebugNoBillboards; + + /// Debug parameter for turning off shape rendering. + bool mDebugNoShapes; + + /// Debug parameter for locking the culling frustum which + /// will freeze the cover generation. + bool mDebugLockFrustum; + + /// Stat for number of rendered cells. + static U32 smStatRenderedCells; + + /// Stat for number of rendered billboards. + static U32 smStatRenderedBillboards; + + /// Stat for number of rendered billboard batches. + static U32 smStatRenderedBatches; + + /// Stat for number of rendered shapes. + static U32 smStatRenderedShapes; + + /// The global ground cover LOD scalar which controls + /// the percentage of the maximum amount of cover to put + /// down. It scales both rendering cost and placement + /// CPU performance. + static F32 smDensityScale; + + String mMaterialName; + Material *mMaterial; + BaseMatInstance *mMatInst; + + GroundCoverShaderConstData mShaderConstData; + + /// This is the maximum amout of degrees the billboard will + /// tilt down to match the camera. + F32 mMaxBillboardTiltAngle; + + /// The probability of one cover type verses another. + F32 mProbability[MAX_COVERTYPES]; + + /// The minimum random size for each cover type. + F32 mSizeMin[MAX_COVERTYPES]; + + /// The maximum random size of this cover type. + F32 mSizeMax[MAX_COVERTYPES]; + + /// An exponent used to bias between the minimum + /// and maximum random sizes. + F32 mSizeExponent[MAX_COVERTYPES]; + + /// The wind effect scale. + F32 mWindScale[MAX_COVERTYPES]; + + /// The maximum slope angle in degrees for placement. + F32 mMaxSlope[MAX_COVERTYPES]; + + /// The minimum world space elevation for placement. + F32 mMinElevation[MAX_COVERTYPES]; + + /// The maximum world space elevation for placement. + F32 mMaxElevation[MAX_COVERTYPES]; + + /// Terrain material name to limit coverage to, or + /// left empty to cover entire terrain. + StringTableEntry mLayer[MAX_COVERTYPES]; + + /// Inverts the data layer test making the + /// layer an exclusion mask. + bool mInvertLayer[MAX_COVERTYPES]; + + /// The minimum amount of elements in a clump. + S32 mMinClumpCount[MAX_COVERTYPES]; + + /// The maximum amount of elements in a clump. + S32 mMaxClumpCount[MAX_COVERTYPES]; + + /// An exponent used to bias between the minimum + /// and maximum clump counts for a particular clump. + F32 mClumpCountExponent[MAX_COVERTYPES]; + + /// The maximum clump radius. + F32 mClumpRadius[MAX_COVERTYPES]; + + /// This is a cached array of billboard aspect scales + /// used to avoid some calculations when generating cells. + F32 mBillboardAspectScales[MAX_COVERTYPES]; + + RectF mBillboardRects[MAX_COVERTYPES]; + + /// The cover shape filenames. + StringTableEntry mShapeFilenames[MAX_COVERTYPES]; + + /// The cover shape instances. + TSShapeInstance* mShapeInstances[MAX_COVERTYPES]; + + /// This is the same as mProbability, but normalized for use + /// during the cover placement process. + F32 mNormalizedProbability[MAX_COVERTYPES]; + + /// A shared primitive buffer setup for drawing the maximum amount + /// of billboards you could possibly have in a single cell. + GFXPrimitiveBufferHandle mPrimBuffer; + + /// The length in meters between peaks in the wind gust. + F32 mWindGustLength; + + /// Controls how often the wind gust peaks per second. + F32 mWindGustFrequency; + + /// The maximum distance in meters that the peak wind + /// gust will displace an element. + F32 mWindGustStrength; + + /// The direction of the wind. + Point2F mWindDirection; + + /// Controls the overall rapidity of the wind turbulence. + F32 mWindTurbulenceFrequency; + + /// The maximum distance in meters that the turbulence can + /// displace a ground cover element. + F32 mWindTurbulenceStrength; + + void _initMaterial(); + + bool _initShader(); + + void _initShapes(); + + void _deleteShapes(); + + /// Called when GroundCover parameters are changed and + /// things need to be reinitialized to continue. + void _initialize( U32 cellCount, U32 cellPlacementCount ); + + /// Updates the cover grid by removing cells that + /// have fallen outside of mRadius and adding new + /// ones that have come into view. + void _updateCoverGrid( const Frustum &culler ); + + /// Clears the cell grid, moves all the allocated cells to + /// the free list, and deletes excess free cells. + void _freeCells(); + + /// Clears the cell grid and deletes all the free cells. + void _deleteCells(); + + /// Returns a cell to the free list. + void _recycleCell( GroundCoverCell* cell ); + + /// Generates a new cell using the recycle list when possible. + GroundCoverCell* _generateCell( const Point2I& index, + const Box3F& bounds, + U32 placementCount, + S32 randSeed ); + + void _debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); +}; + +#endif // _GROUNDCOVER_H_ diff --git a/Engine/source/T3D/fx/lightning.cpp b/Engine/source/T3D/fx/lightning.cpp new file mode 100644 index 000000000..338bd0a95 --- /dev/null +++ b/Engine/source/T3D/fx/lightning.cpp @@ -0,0 +1,1248 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/fx/lightning.h" + +#include "scene/sceneRenderState.h" +#include "console/consoleTypes.h" +#include "math/mathIO.h" +#include "core/stream/bitStream.h" +#include "T3D/gameBase/gameConnection.h" +#include "T3D/shapeBase.h" +#include "math/mRandom.h" +#include "math/mathUtils.h" +#include "terrain/terrData.h" +#include "scene/sceneManager.h" +#include "T3D/player.h" +#include "T3D/camera.h" +#include "sfx/sfxSystem.h" +#include "sfx/sfxTrack.h" +#include "sfx/sfxTypes.h" +#include "gfx/primBuilder.h" +#include "console/engineAPI.h" + + +IMPLEMENT_CO_DATABLOCK_V1(LightningData); +IMPLEMENT_CO_NETOBJECT_V1(Lightning); + +ConsoleDocClass( LightningData, + "@brief Common data for a Lightning emitter object.\n" + "@see Lightning\n" + "@ingroup FX\n" + "@ingroup Atmosphere\n" + "@ingroup Datablocks\n" +); + +ConsoleDocClass( Lightning, + "@brief An emitter for lightning bolts.\n\n" + + "Lightning strike events are created on the server and transmitted to all " + "clients to render the bolt. The strike may be followed by a random thunder " + "sound. Player or Vehicle objects within the Lightning strike range can be " + "hit and damaged by bolts.\n" + + "@see LightningData\n" + "@ingroup FX\n" + "@ingroup Atmosphere\n" +); + +IMPLEMENT_CALLBACK( Lightning, applyDamage, void, ( const Point3F& hitPosition, const Point3F& hitNormal, SceneObject* hitObject ), + ( hitPosition, hitNormal, hitObject ), + "Informs an object that it was hit by a lightning bolt and needs to take damage.\n" + "@param hitPosition World position hit by the lightning bolt.\n" + "@param hitNormal Surface normal at @a hitPosition.\n" + "@param hitObject Player or Vehicle object that was hit.\n" + "@tsexample\n" + "function Lightning::applyDamage( %this, %hitPosition, %hitNormal, %hitObject )\n" + "{\n" + " // apply damage to the player\n" + " %hitObject.applyDamage( 25 );\n" + "}\n" + "@endtsexample\n" +); + + +MRandomLCG sgLightningRand; + + +S32 QSORT_CALLBACK cmpSounds(const void* p1, const void* p2) +{ + U32 i1 = *((const S32*)p1); + U32 i2 = *((const S32*)p2); + + if (i1 < i2) { + return 1; + } else if (i1 > i2) { + return -1; + } else { + return 0; + } +} + +//-------------------------------------------------------------------------- +//-------------------------------------- +// +class LightningStrikeEvent : public NetEvent +{ + public: + typedef NetEvent Parent; + enum EventType { + WarningFlash = 0, + Strike = 1, + TargetedStrike = 2, + + TypeMin = WarningFlash, + TypeMax = TargetedStrike + }; + enum Constants { + PositionalBits = 10 + }; + + Point2F mStart; + SimObjectPtr mTarget; + + Lightning* mLightning; + + // Set by unpack... + public: + S32 mClientId; + + public: + LightningStrikeEvent(); + ~LightningStrikeEvent(); + + void pack(NetConnection*, BitStream*); + void write(NetConnection*, BitStream*){} + void unpack(NetConnection*, BitStream*); + void process(NetConnection*); + + DECLARE_CONOBJECT(LightningStrikeEvent); +}; + +IMPLEMENT_CO_CLIENTEVENT_V1(LightningStrikeEvent); + +ConsoleDocClass( LightningStrikeEvent, + "@brief Network event that triggers a lightning strike on the client when it " + "is received.\n\n" + "This event is sent to all clients when the warningFlashes(), " + "strikeRandomPoint() or strikeObject() methods are invoked on the Lightning " + "object on the server.\n" + "@see Lightning, LightningData\n" + "@ingroup FX\n" + "@ingroup Atmosphere\n" +); + +LightningStrikeEvent::LightningStrikeEvent() +{ + mLightning = NULL; + mTarget = NULL; +} + +LightningStrikeEvent::~LightningStrikeEvent() +{ +} + +void LightningStrikeEvent::pack(NetConnection* con, BitStream* stream) +{ + if(!mLightning) + { + stream->writeFlag(false); + return; + } + S32 id = con->getGhostIndex(mLightning); + if(id == -1) + { + stream->writeFlag(false); + return; + } + stream->writeFlag(true); + stream->writeRangedU32(U32(id), 0, NetConnection::MaxGhostCount); + stream->writeFloat(mStart.x, PositionalBits); + stream->writeFloat(mStart.y, PositionalBits); + + if( mTarget ) + { + S32 ghostIndex = con->getGhostIndex(mTarget); + if (ghostIndex == -1) + stream->writeFlag(false); + else + { + stream->writeFlag(true); + stream->writeRangedU32(U32(ghostIndex), 0, NetConnection::MaxGhostCount); + } + } + else + stream->writeFlag( false ); +} + +void LightningStrikeEvent::unpack(NetConnection* con, BitStream* stream) +{ + if(!stream->readFlag()) + return; + S32 mClientId = stream->readRangedU32(0, NetConnection::MaxGhostCount); + mLightning = NULL; + NetObject* pObject = con->resolveGhost(mClientId); + if (pObject) + mLightning = dynamic_cast(pObject); + + mStart.x = stream->readFloat(PositionalBits); + mStart.y = stream->readFloat(PositionalBits); + + if( stream->readFlag() ) + { + // target id + S32 mTargetID = stream->readRangedU32(0, NetConnection::MaxGhostCount); + + NetObject* pObject = con->resolveGhost(mTargetID); + if( pObject != NULL ) + { + mTarget = dynamic_cast(pObject); + } + if( bool(mTarget) == false ) + { + Con::errorf(ConsoleLogEntry::General, "LightningStrikeEvent::unpack: could not resolve target ghost properly"); + } + } +} + +void LightningStrikeEvent::process(NetConnection*) +{ + if (mLightning) + mLightning->processEvent(this); +} + + +//-------------------------------------------------------------------------- +//-------------------------------------- +// +LightningData::LightningData() +{ + strikeSound = NULL; + + dMemset( strikeTextureNames, 0, sizeof( strikeTextureNames ) ); + dMemset( strikeTextures, 0, sizeof( strikeTextures ) ); + dMemset( thunderSounds, 0, sizeof( thunderSounds ) ); +} + +LightningData::~LightningData() +{ + +} + +//-------------------------------------------------------------------------- +void LightningData::initPersistFields() +{ + addField( "strikeSound", TYPEID< SFXTrack >(), Offset(strikeSound, LightningData), + "Sound profile to play when a lightning strike occurs." ); + addField( "thunderSounds", TYPEID< SFXTrack >(), Offset(thunderSounds, LightningData), MaxThunders, + "@brief List of thunder sound effects to play.\n\n" + "A random one of these sounds will be played shortly after each strike " + "occurs." ); + addField( "strikeTextures", TypeString, Offset(strikeTextureNames, LightningData), MaxTextures, + "List of textures to use to render lightning strikes." ); + + Parent::initPersistFields(); +} + + +//-------------------------------------------------------------------------- +bool LightningData::onAdd() +{ + if(!Parent::onAdd()) + return false; + + return true; +} + + +bool LightningData::preload(bool server, String &errorStr) +{ + if (Parent::preload(server, errorStr) == false) + return false; + + dQsort(thunderSounds, MaxThunders, sizeof(SFXTrack*), cmpSounds); + for (numThunders = 0; numThunders < MaxThunders && thunderSounds[numThunders] != NULL; numThunders++) { + // + } + + if (server == false) + { + String errorStr; + for (U32 i = 0; i < MaxThunders; i++) { + if( !sfxResolve( &thunderSounds[ i ], errorStr ) ) + Con::errorf(ConsoleLogEntry::General, "LightningData::preload: Invalid packet: %s", errorStr.c_str()); + } + + if( !sfxResolve( &strikeSound, errorStr ) ) + Con::errorf(ConsoleLogEntry::General, "LightningData::preload: Invalid packet: %s", errorStr.c_str()); + + for (U32 i = 0; i < MaxTextures; i++) + { + if (strikeTextureNames[i][0]) + strikeTextures[i] = GFXTexHandle(strikeTextureNames[i], &GFXDefaultStaticDiffuseProfile, avar("%s() - strikeTextures[%d] (line %d)", __FUNCTION__, i, __LINE__)); + } + } + + + return true; +} + + +//-------------------------------------------------------------------------- +void LightningData::packData(BitStream* stream) +{ + Parent::packData(stream); + + U32 i; + for (i = 0; i < MaxThunders; i++) + sfxWrite( stream, thunderSounds[ i ] ); + for (i = 0; i < MaxTextures; i++) { + stream->writeString(strikeTextureNames[i]); + } + + sfxWrite( stream, strikeSound ); +} + +void LightningData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + + U32 i; + for (i = 0; i < MaxThunders; i++) + sfxRead( stream, &thunderSounds[ i ] ); + for (i = 0; i < MaxTextures; i++) { + strikeTextureNames[i] = stream->readSTString(); + } + + sfxRead( stream, &strikeSound ); +} + + +//-------------------------------------------------------------------------- +//-------------------------------------- +// +Lightning::Lightning() +{ + mNetFlags.set(Ghostable|ScopeAlways); + mTypeMask |= StaticObjectType|EnvironmentObjectType; + + mLastThink = 0; + + mStrikeListHead = NULL; + mThunderListHead = NULL; + + strikesPerMinute = 12; + strikeWidth = 2.5; + chanceToHitTarget = 0.5f; + strikeRadius = 20.0f; + boltStartRadius = 20.0f; + color.set( 1.0f, 1.0f, 1.0f, 1.0f ); + fadeColor.set( 0.1f, 0.1f, 1.0f, 1.0f ); + useFog = true; + + setScale( VectorF( 512.0f, 512.0f, 300.0f ) ); +} + +Lightning::~Lightning() +{ + while( mThunderListHead ) + { + Thunder* next = mThunderListHead->next; + delete mThunderListHead; + mThunderListHead = next; + } + + while( mStrikeListHead ) + { + Strike* next = mStrikeListHead->next; + delete mStrikeListHead; + mStrikeListHead = next; + } +} + +//-------------------------------------------------------------------------- +void Lightning::initPersistFields() +{ + addGroup( "Strikes" ); + addField( "strikesPerMinute", TypeS32, Offset(strikesPerMinute, Lightning), + "@brief Number of lightning strikes to perform per minute.\n\n" + "Automatically invokes strikeRandomPoint() at regular intervals." ); + addField( "strikeWidth", TypeF32, Offset(strikeWidth, Lightning), + "Width of a lightning bolt." ); + addField( "strikeRadius", TypeF32, Offset(strikeRadius, Lightning), + "@brief Horizontal size (XY plane) of the search box used to find and " + "damage Player or Vehicle objects within range of the strike.\n\n" + "Only the object at highest altitude with a clear line of sight to the " + "bolt will be hit." ); + endGroup( "Strikes" ); + + addGroup( "Colors" ); + addField( "color", TypeColorF, Offset(color, Lightning), + "Color to blend the strike texture with." ); + addField( "fadeColor", TypeColorF, Offset(fadeColor, Lightning), + "@brief Color to blend the strike texture with when the bolt is fading away.\n\n" + "Bolts fade away automatically shortly after the strike occurs." ); + endGroup( "Colors" ); + + addGroup( "Bolts" ); + addField( "chanceToHitTarget", TypeF32, Offset(chanceToHitTarget, Lightning), + "Percentage chance (0-1) that a given lightning bolt will hit something." ); + addField( "boltStartRadius", TypeF32, Offset(boltStartRadius, Lightning), + "@brief Radial distance from the center of the Lightning object for the " + "start point of the bolt.\n\n" + "The actual start point will be a random point within this radius." ); + addField( "useFog", TypeBool, Offset(useFog, Lightning), + "Controls whether lightning bolts are affected by fog when they are rendered." ); + endGroup( "Bolts" ); + + Parent::initPersistFields(); +} + +//-------------------------------------------------------------------------- +bool Lightning::onAdd() +{ + if(!Parent::onAdd()) + return false; + + mObjBox.minExtents.set( -0.5f, -0.5f, -0.5f ); + mObjBox.maxExtents.set( 0.5f, 0.5f, 0.5f ); + + resetWorldBox(); + addToScene(); + + return true; +} + + +void Lightning::onRemove() +{ + removeFromScene(); + + Parent::onRemove(); +} + + +bool Lightning::onNewDataBlock( GameBaseData *dptr, bool reload ) +{ + mDataBlock = dynamic_cast( dptr ); + if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) ) + return false; + + scriptOnNewDataBlock(); + return true; +} + + +//-------------------------------------------------------------------------- +void Lightning::prepRenderImage( SceneRenderState* state ) +{ + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind(this, &Lightning::renderObject); + // The Lightning isn't technically foliage but our debug + // effect seems to render best as a Foliage type (translucent, + // renders itself, no sorting) + ri->type = RenderPassManager::RIT_Foliage; + state->getRenderPass()->addInst( ri ); +} + + +void Lightning::renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* overrideMat) +{ + if (overrideMat) + return; + + if (mLightningSB.isNull()) + { + GFXStateBlockDesc desc; + desc.setBlend( true, GFXBlendSrcAlpha, GFXBlendOne); + desc.setCullMode(GFXCullNone); + desc.zWriteEnable = false; + desc.samplersDefined = true; + desc.samplers[0].magFilter = GFXTextureFilterLinear; + desc.samplers[0].minFilter = GFXTextureFilterLinear; + desc.samplers[0].addressModeU = GFXAddressWrap; + desc.samplers[0].addressModeV = GFXAddressWrap; + + mLightningSB = GFX->createStateBlock(desc); + + } + + GFX->setStateBlock(mLightningSB); + + + Strike* walk = mStrikeListHead; + while (walk != NULL) + { + GFX->setTexture(0, mDataBlock->strikeTextures[0]); + + for( U32 i=0; i<3; i++ ) + { + if( walk->bolt[i].isFading ) + { + F32 alpha = 1.0f - walk->bolt[i].percentFade; + if( alpha < 0.0f ) alpha = 0.0f; + PrimBuild::color4f( fadeColor.red, fadeColor.green, fadeColor.blue, alpha ); + } + else + { + PrimBuild::color4f( color.red, color.green, color.blue, color.alpha ); + } + walk->bolt[i].render( state->getCameraPosition() ); + } + + walk = walk->next; + } + + //GFX->setZWriteEnable(true); + //GFX->setAlphaTestEnable(false); + //GFX->setAlphaBlendEnable(false); +} + +void Lightning::scheduleThunder(Strike* newStrike) +{ + AssertFatal(isClientObject(), "Lightning::scheduleThunder: server objects should not enter this version of the function"); + + // If no thunder sounds, don't schedule anything! + if (mDataBlock->numThunders == 0) + return; + + GameConnection* connection = GameConnection::getConnectionToServer(); + if (connection) { + MatrixF cameraMatrix; + + if (connection->getControlCameraTransform(0, &cameraMatrix)) { + Point3F worldPos; + cameraMatrix.getColumn(3, &worldPos); + + worldPos.x -= newStrike->xVal; + worldPos.y -= newStrike->yVal; + worldPos.z = 0.0f; + + F32 dist = worldPos.len(); + F32 t = dist / 330.0f; + + // Ok, we need to schedule a random strike sound t secs in the future... + // + if (t <= 0.03f) { + // If it's really close, just play it... + U32 thunder = sgLightningRand.randI(0, mDataBlock->numThunders - 1); + SFX->playOnce(mDataBlock->thunderSounds[thunder]); + } else { + Thunder* pThunder = new Thunder; + pThunder->tRemaining = t; + pThunder->next = mThunderListHead; + mThunderListHead = pThunder; + } + } + } +} + + +//-------------------------------------------------------------------------- +void Lightning::processTick(const Move* move) +{ + Parent::processTick(move); + + if (isServerObject() && !isHidden()) { + S32 msBetweenStrikes = (S32)(60.0 / strikesPerMinute * 1000.0); + + mLastThink += TickMs; + if( mLastThink > msBetweenStrikes ) + { + strikeRandomPoint(); + mLastThink -= msBetweenStrikes; + } + } +} + +void Lightning::interpolateTick(F32 dt) +{ + Parent::interpolateTick(dt); +} + +void Lightning::advanceTime(F32 dt) +{ + Parent::advanceTime(dt); + + Strike** pWalker = &mStrikeListHead; + while (*pWalker != NULL) { + Strike* pStrike = *pWalker; + + for( U32 i=0; i<3; i++ ) + { + pStrike->bolt[i].update( dt ); + } + + pStrike->currentAge += dt; + if (pStrike->currentAge > pStrike->deathAge) { + *pWalker = pStrike->next; + delete pStrike; + } else { + pWalker = &((*pWalker)->next); + } + } + + Thunder** pThunderWalker = &mThunderListHead; + while (*pThunderWalker != NULL) { + Thunder* pThunder = *pThunderWalker; + + pThunder->tRemaining -= dt; + if (pThunder->tRemaining <= 0.0f) { + *pThunderWalker = pThunder->next; + delete pThunder; + + // Play the sound... + U32 thunder = sgLightningRand.randI(0, mDataBlock->numThunders - 1); + SFX->playOnce(mDataBlock->thunderSounds[thunder]); + } else { + pThunderWalker = &((*pThunderWalker)->next); + } + } +} + + +//-------------------------------------------------------------------------- +void Lightning::processEvent(LightningStrikeEvent* pEvent) +{ + AssertFatal(pEvent->mStart.x >= 0.0f && pEvent->mStart.x <= 1.0f, "Out of bounds coord!"); + + Strike* pStrike = new Strike; + + Point3F strikePoint; + strikePoint.zero(); + + if( pEvent->mTarget ) + { + Point3F objectCenter; + pEvent->mTarget->getObjBox().getCenter( &objectCenter ); + objectCenter.convolve( pEvent->mTarget->getScale() ); + pEvent->mTarget->getTransform().mulP( objectCenter ); + + strikePoint = objectCenter; + } + else + { + strikePoint.x = pEvent->mStart.x; + strikePoint.y = pEvent->mStart.y; + strikePoint *= mObjScale; + strikePoint += getPosition(); + strikePoint += Point3F( -mObjScale.x * 0.5f, -mObjScale.y * 0.5f, 0.0f ); + + RayInfo rayInfo; + Point3F start = strikePoint; + start.z = mObjScale.z * 0.5f + getPosition().z; + strikePoint.z += -mObjScale.z * 0.5f; + bool rayHit = gClientContainer.castRay( start, strikePoint, + (STATIC_COLLISION_TYPEMASK | WaterObjectType), + &rayInfo); + if( rayHit ) + { + strikePoint.z = rayInfo.point.z; + } + else + { + strikePoint.z = pStrike->bolt[0].findHeight( strikePoint, getSceneManager() ); + } + } + + pStrike->xVal = strikePoint.x; + pStrike->yVal = strikePoint.y; + + pStrike->deathAge = 1.6f; + pStrike->currentAge = 0.0f; + pStrike->next = mStrikeListHead; + + for( U32 i=0; i<3; i++ ) + { + F32 randStart = boltStartRadius; + F32 height = mObjScale.z * 0.5f + getPosition().z; + pStrike->bolt[i].startPoint.set( pStrike->xVal + gRandGen.randF( -randStart, randStart ), pStrike->yVal + gRandGen.randF( -randStart, randStart ), height ); + pStrike->bolt[i].endPoint = strikePoint; + pStrike->bolt[i].width = strikeWidth; + pStrike->bolt[i].numMajorNodes = 10; + pStrike->bolt[i].maxMajorAngle = 30.0f; + pStrike->bolt[i].numMinorNodes = 4; + pStrike->bolt[i].maxMinorAngle = 15.0f; + pStrike->bolt[i].generate(); + pStrike->bolt[i].startSplits(); + pStrike->bolt[i].lifetime = 1.0f; + pStrike->bolt[i].fadeTime = 0.2f; + pStrike->bolt[i].renderTime = gRandGen.randF(0.0f, 0.25f); + } + + mStrikeListHead = pStrike; + + scheduleThunder(pStrike); + + MatrixF trans(true); + trans.setPosition( strikePoint ); + + if (mDataBlock->strikeSound) + { + SFX->playOnce(mDataBlock->strikeSound, &trans ); + } + +} + +void Lightning::warningFlashes() +{ + AssertFatal(isServerObject(), "Error, client objects may not initiate lightning!"); + + + SimGroup* pClientGroup = Sim::getClientGroup(); + for (SimGroup::iterator itr = pClientGroup->begin(); itr != pClientGroup->end(); itr++) { + NetConnection* nc = static_cast(*itr); + if (nc != NULL) + { + LightningStrikeEvent* pEvent = new LightningStrikeEvent; + pEvent->mLightning = this; + + nc->postNetEvent(pEvent); + } + } +} + +void Lightning::strikeRandomPoint() +{ + AssertFatal(isServerObject(), "Error, client objects may not initiate lightning!"); + + + Point3F strikePoint( gRandGen.randF( 0.0f, 1.0f ), gRandGen.randF( 0.0f, 1.0f ), 0.0f ); + + // check if an object is within target range + + strikePoint *= mObjScale; + strikePoint += getPosition(); + strikePoint += Point3F( -mObjScale.x * 0.5f, -mObjScale.y * 0.5f, 0.0f ); + + Box3F queryBox; + F32 boxWidth = strikeRadius * 2.0f; + + queryBox.minExtents.set( -boxWidth * 0.5f, -boxWidth * 0.5f, -mObjScale.z * 0.5f ); + queryBox.maxExtents.set( boxWidth * 0.5f, boxWidth * 0.5f, mObjScale.z * 0.5f ); + queryBox.minExtents += strikePoint; + queryBox.maxExtents += strikePoint; + + SimpleQueryList sql; + getContainer()->findObjects(queryBox, DAMAGEABLE_TYPEMASK, + SimpleQueryList::insertionCallback, &sql); + + SceneObject *highestObj = NULL; + F32 highestPnt = 0.0f; + + for( U32 i = 0; i < sql.mList.size(); i++ ) + { + Point3F objectCenter; + sql.mList[i]->getObjBox().getCenter(&objectCenter); + objectCenter.convolve(sql.mList[i]->getScale()); + sql.mList[i]->getTransform().mulP(objectCenter); + + // check if object can be struck + + RayInfo rayInfo; + Point3F start = objectCenter; + start.z = mObjScale.z * 0.5f + getPosition().z; + Point3F end = objectCenter; + end.z = -mObjScale.z * 0.5f + getPosition().z; + bool rayHit = gServerContainer.castRay( start, end, + (0xFFFFFFFF), + &rayInfo); + + if( rayHit && rayInfo.object == sql.mList[i] ) + { + if( !highestObj ) + { + highestObj = sql.mList[i]; + highestPnt = objectCenter.z; + continue; + } + + if( objectCenter.z > highestPnt ) + { + highestObj = sql.mList[i]; + highestPnt = objectCenter.z; + } + } + + + } + + // hah haaaaa, we have a target! + SceneObject *targetObj = NULL; + if( highestObj ) + { + F32 chance = gRandGen.randF(); + if( chance <= chanceToHitTarget ) + { + Point3F objectCenter; + highestObj->getObjBox().getCenter(&objectCenter); + objectCenter.convolve(highestObj->getScale()); + highestObj->getTransform().mulP(objectCenter); + + bool playerInWarmup = false; + Player *playerObj = dynamic_cast< Player * >(highestObj); + if( playerObj ) + { + if( !playerObj->getControllingClient() ) + { + playerInWarmup = true; + } + } + + if( !playerInWarmup ) + { + applyDamage_callback( objectCenter, VectorF( 0.0f, 0.0f, 1.0f ), highestObj ); + targetObj = highestObj; + } + } + } + + SimGroup* pClientGroup = Sim::getClientGroup(); + for (SimGroup::iterator itr = pClientGroup->begin(); itr != pClientGroup->end(); itr++) + { + NetConnection* nc = static_cast(*itr); + + LightningStrikeEvent* pEvent = new LightningStrikeEvent; + pEvent->mLightning = this; + + pEvent->mStart.x = strikePoint.x; + pEvent->mStart.y = strikePoint.y; + pEvent->mTarget = targetObj; + + nc->postNetEvent(pEvent); + } + + +} + +//-------------------------------------------------------------------------- +void Lightning::strikeObject(ShapeBase*) +{ + AssertFatal(isServerObject(), "Error, client objects may not initiate lightning!"); + + AssertFatal(false, "Lightning::strikeObject is not implemented."); +} + + +//-------------------------------------------------------------------------- +U32 Lightning::packUpdate(NetConnection* con, U32 mask, BitStream* stream) +{ + U32 retMask = Parent::packUpdate(con, mask, stream); + + // Only write data if this is the initial packet or we've been inspected. + if (stream->writeFlag(mask & (InitialUpdateMask | ExtendedInfoMask))) + { + // Initial update + mathWrite(*stream, getPosition()); + mathWrite(*stream, mObjScale); + + stream->write(strikeWidth); + stream->write(chanceToHitTarget); + stream->write(strikeRadius); + stream->write(boltStartRadius); + stream->write(color.red); + stream->write(color.green); + stream->write(color.blue); + stream->write(fadeColor.red); + stream->write(fadeColor.green); + stream->write(fadeColor.blue); + stream->write(useFog); + stream->write(strikesPerMinute); + } + + return retMask; +} + +//-------------------------------------------------------------------------- +void Lightning::unpackUpdate(NetConnection* con, BitStream* stream) +{ + Parent::unpackUpdate(con, stream); + + if (stream->readFlag()) + { + // Initial update + Point3F pos; + mathRead(*stream, &pos); + setPosition( pos ); + + mathRead(*stream, &mObjScale); + + stream->read(&strikeWidth); + stream->read(&chanceToHitTarget); + stream->read(&strikeRadius); + stream->read(&boltStartRadius); + stream->read(&color.red); + stream->read(&color.green); + stream->read(&color.blue); + stream->read(&fadeColor.red); + stream->read(&fadeColor.green); + stream->read(&fadeColor.blue); + stream->read(&useFog); + stream->read(&strikesPerMinute); + } +} + +//-------------------------------------------------------------------------- + +DefineEngineMethod(Lightning, warningFlashes, void, (),, + "@brief Creates a LightningStrikeEvent that triggers harmless lightning " + "bolts on all clients.\n" + "No objects will be damaged by these bolts.\n" + "@tsexample\n" + "// Generate a harmless lightning strike effect on all clients\n" + "%lightning.warningFlashes();\n" + "@endtsexample" ) +{ + if (object->isServerObject()) + object->warningFlashes(); +} + +DefineEngineMethod(Lightning, strikeRandomPoint, void, (),, + "Creates a LightningStrikeEvent which attempts to strike and damage a random " + "object in range of the Lightning object.\n" + "@tsexample\n" + "// Generate a damaging lightning strike effect on all clients\n" + "%lightning.strikeRandomPoint();\n" + "@endtsexample" ) +{ + if (object->isServerObject()) + object->strikeRandomPoint(); +} + +DefineEngineMethod(Lightning, strikeObject, void, (S32 id), (NULL), + "Creates a LightningStrikeEvent which strikes a specific object.\n" + "@note This method is currently unimplemented.\n" ) +{ + ShapeBase* pSB; + + if (object->isServerObject() && Sim::findObject(id, pSB)) + object->strikeObject(pSB); +} + +//************************************************************************** +// Lightning Bolt +//************************************************************************** +LightningBolt::LightningBolt() +{ + width = 0.1f; + startPoint.zero(); + endPoint.zero(); + chanceOfSplit = 0.0f; + isFading = false; + elapsedTime = 0.0f; + lifetime = 1.0f; + startRender = false; +} + +//-------------------------------------------------------------------------- +// Destructor +//-------------------------------------------------------------------------- +LightningBolt::~LightningBolt() +{ + splitList.clear(); +} + +//-------------------------------------------------------------------------- +// Generate nodes +//-------------------------------------------------------------------------- +void LightningBolt::NodeManager::generateNodes() +{ + F32 overallDist = VectorF( endPoint - startPoint ).magnitudeSafe(); + F32 minDistBetweenNodes = overallDist / (numNodes-1); + F32 maxDistBetweenNodes = minDistBetweenNodes / mCos( maxAngle * M_PI_F / 180.0f ); + + VectorF mainLineDir = endPoint - startPoint; + mainLineDir.normalizeSafe(); + + for( U32 i=0; iisFading = true; + } + i->render( camPos ); + } + +} + +//-------------------------------------------------------------------------- +// Render segment +//-------------------------------------------------------------------------- +void LightningBolt::renderSegment( NodeManager &segment, const Point3F &camPos, bool renderLastPoint ) +{ + + for (U32 i = 0; i < segment.numNodes; i++) + { + Point3F curPoint = segment.nodeList[i].point; + + Point3F nextPoint; + Point3F segDir; + + if( i == (segment.numNodes-1) ) + { + if( renderLastPoint ) + { + segDir = curPoint - segment.nodeList[i-1].point; + } + else + { + continue; + } + } + else + { + nextPoint = segment.nodeList[i+1].point; + segDir = nextPoint - curPoint; + } + segDir.normalizeSafe(); + + + Point3F dirFromCam = curPoint - camPos; + Point3F crossVec; + mCross(dirFromCam, segDir, &crossVec); + crossVec.normalize(); + crossVec *= width * 0.5f; + + F32 u = i % 2; + + PrimBuild::texCoord2f( u, 1.0 ); + PrimBuild::vertex3fv( curPoint - crossVec ); + + PrimBuild::texCoord2f( u, 0.0 ); + PrimBuild::vertex3fv( curPoint + crossVec ); + } + +} + +//---------------------------------------------------------------------------- +// Find height +//---------------------------------------------------------------------------- +F32 LightningBolt::findHeight( Point3F &point, SceneManager *sceneManager ) +{ + const Vector< SceneObject* > terrains = sceneManager->getContainer()->getTerrains(); + for( Vector< SceneObject* >::const_iterator iter = terrains.begin(); iter != terrains.end(); ++ iter ) + { + TerrainBlock* terrain = dynamic_cast< TerrainBlock* >( *iter ); + if( !terrain ) + continue; + + Point3F terrPt = point; + terrain->getWorldTransform().mulP(terrPt); + + F32 h; + if( terrain->getHeight( Point2F( terrPt.x, terrPt.y ), &h ) ) + return h; + } + + return 0.f; +} + + +//---------------------------------------------------------------------------- +// Generate lightning bolt +//---------------------------------------------------------------------------- +void LightningBolt::generate() +{ + mMajorNodes.startPoint = startPoint; + mMajorNodes.endPoint = endPoint; + mMajorNodes.numNodes = numMajorNodes; + mMajorNodes.maxAngle = maxMajorAngle; + + mMajorNodes.generateNodes(); + + generateMinorNodes(); + +} + +//---------------------------------------------------------------------------- +// Generate Minor Nodes +//---------------------------------------------------------------------------- +void LightningBolt::generateMinorNodes() +{ + mMinorNodes.clear(); + + for( int i=0; i 0.70f ) + return; + + if( width < 0.75f ) + width = 0.75f; + + VectorF diff = endPoint - startPoint; + F32 length = diff.len(); + diff.normalizeSafe(); + + LightningBolt newBolt; + newBolt.startPoint = startPoint; + newBolt.endPoint = endPoint; + newBolt.width = width; + newBolt.numMajorNodes = 3; + newBolt.maxMajorAngle = 30.0f; + newBolt.numMinorNodes = 3; + newBolt.maxMinorAngle = 10.0f; + newBolt.startRender = true; + newBolt.generate(); + + splitList.pushBack( newBolt ); + + VectorF newDir1 = MathUtils::randomDir( diff, 10.0f, 45.0f ); + Point3F newEndPoint1 = endPoint + newDir1 * gRandGen.randF( 0.5f, 1.5f ) * length; + + VectorF newDir2 = MathUtils::randomDir( diff, 10.0f, 45.0f ); + Point3F newEndPoint2 = endPoint + newDir2 * gRandGen.randF( 0.5f, 1.5f ) * length; + + createSplit( endPoint, newEndPoint1, depth - 1, width * 0.30f ); + createSplit( endPoint, newEndPoint2, depth - 1, width * 0.30f ); + +} + +//---------------------------------------------------------------------------- +// Start split - kick off the recursive 'createSplit' procedure +//---------------------------------------------------------------------------- +void LightningBolt::startSplits() +{ + + for( U32 i=0; i 0.3f ) + continue; + + Node node = mMajorNodes.nodeList[i]; + Node node2 = mMajorNodes.nodeList[i+1]; + + VectorF segDir = node2.point - node.point; + F32 length = segDir.len(); + segDir.normalizeSafe(); + + VectorF newDir = MathUtils::randomDir( segDir, 20.0f, 40.0f ); + Point3F newEndPoint = node.point + newDir * gRandGen.randF( 0.5f, 1.5f ) * length; + + + createSplit( node.point, newEndPoint, 4, width * 0.30f ); + } + + +} + +//---------------------------------------------------------------------------- +// Update +//---------------------------------------------------------------------------- +void LightningBolt::update( F32 dt ) +{ + elapsedTime += dt; + + F32 percentDone = elapsedTime / lifetime; + + if( elapsedTime > fadeTime ) + { + isFading = true; + percentFade = percentDone + (fadeTime/lifetime); + } + + if( elapsedTime > renderTime && !startRender ) + { + startRender = true; + isFading = false; + elapsedTime = 0.0f; + } +} \ No newline at end of file diff --git a/Engine/source/T3D/fx/lightning.h b/Engine/source/T3D/fx/lightning.h new file mode 100644 index 000000000..20620fca5 --- /dev/null +++ b/Engine/source/T3D/fx/lightning.h @@ -0,0 +1,241 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _LIGHTNING_H_ +#define _LIGHTNING_H_ + +#ifndef _GAMEBASE_H_ +#include "T3D/gameBase/gameBase.h" +#endif +#ifndef _TORQUE_LIST_ +#include "core/util/tList.h" +#endif +#ifndef _COLOR_H_ +#include "core/color.h" +#endif +#ifndef _RENDERPASSMANAGER_H_ +#include "renderInstance/renderPassManager.h" +#endif +#ifndef _ENGINEAPI_H_ +#include "console/engineAPI.h" +#endif + +#include "gfx/gfxTextureHandle.h" + + + +class ShapeBase; +class LightningStrikeEvent; +class SFXTrack; + + +// ------------------------------------------------------------------------- +class LightningData : public GameBaseData +{ + typedef GameBaseData Parent; + + public: + enum Constants { + MaxThunders = 8, + MaxTextures = 8 + }; + + //-------------------------------------- Console set variables + public: + SFXTrack* thunderSounds[MaxThunders]; + SFXTrack* strikeSound; + StringTableEntry strikeTextureNames[MaxTextures]; + + //-------------------------------------- load set variables + public: + + GFXTexHandle strikeTextures[MaxTextures]; + U32 numThunders; + + protected: + bool onAdd(); + + public: + LightningData(); + ~LightningData(); + + void packData(BitStream*); + void unpackData(BitStream*); + bool preload(bool server, String &errorStr); + + DECLARE_CONOBJECT(LightningData); + static void initPersistFields(); +}; + + +// ------------------------------------------------------------------------- +struct LightningBolt +{ + + struct Node + { + Point3F point; + VectorF dirToMainLine; + }; + + struct NodeManager + { + Node nodeList[10]; + + Point3F startPoint; + Point3F endPoint; + U32 numNodes; + F32 maxAngle; + + void generateNodes(); + }; + + NodeManager mMajorNodes; + Vector< NodeManager > mMinorNodes; + + typedef Torque::List LightingBoltList; + LightingBoltList splitList; + + F32 lifetime; + F32 elapsedTime; + F32 fadeTime; + bool isFading; + F32 percentFade; + bool startRender; + F32 renderTime; + + F32 width; + F32 chanceOfSplit; + Point3F startPoint; + Point3F endPoint; + + U32 numMajorNodes; + F32 maxMajorAngle; + U32 numMinorNodes; + F32 maxMinorAngle; + + LightningBolt(); + ~LightningBolt(); + + void createSplit( const Point3F &startPoint, const Point3F &endPoint, U32 depth, F32 width ); + F32 findHeight( Point3F &point, SceneManager* sceneManager ); + void render( const Point3F &camPos ); + void renderSegment( NodeManager &segment, const Point3F &camPos, bool renderLastPoint ); + void generate(); + void generateMinorNodes(); + void startSplits(); + void update( F32 dt ); + +}; + + +// ------------------------------------------------------------------------- +class Lightning : public GameBase +{ + typedef GameBase Parent; + + protected: + bool onAdd(); + void onRemove(); + bool onNewDataBlock( GameBaseData *dptr, bool reload ); + + DECLARE_CALLBACK( void, applyDamage, ( const Point3F& hitPosition, const Point3F& hitNormal, SceneObject* hitObject )); + + struct Strike { + F32 xVal; // Position in cloud layer of strike + F32 yVal; // top + + bool targetedStrike; // Is this a targeted strike? + U32 targetGID; + + F32 deathAge; // Age at which this strike expires + F32 currentAge; // Current age of this strike (updated by advanceTime) + + LightningBolt bolt[3]; + + Strike* next; + }; + struct Thunder { + F32 tRemaining; + Thunder* next; + }; + + public: + + //-------------------------------------- Console set variables + public: + + U32 strikesPerMinute; + F32 strikeWidth; + F32 chanceToHitTarget; + F32 strikeRadius; + F32 boltStartRadius; + ColorF color; + ColorF fadeColor; + bool useFog; + + GFXStateBlockRef mLightningSB; + + protected: + + // Rendering + void prepRenderImage(SceneRenderState *state); + void renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* ); + + // Time management + void processTick(const Move *move); + void interpolateTick(F32 delta); + void advanceTime(F32 dt); + + // Strike management + void scheduleThunder(Strike*); + + // Data members + private: + LightningData* mDataBlock; + + protected: + U32 mLastThink; // Valid only on server + + Strike* mStrikeListHead; // Valid on on the client + Thunder* mThunderListHead; + + static const U32 csmTargetMask; + + public: + Lightning(); + ~Lightning(); + + void warningFlashes(); + void strikeRandomPoint(); + void strikeObject(ShapeBase*); + void processEvent(LightningStrikeEvent*); + + DECLARE_CONOBJECT(Lightning); + static void initPersistFields(); + + U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream); + void unpackUpdate(NetConnection *conn, BitStream *stream); +}; + +#endif // _H_LIGHTNING + diff --git a/Engine/source/T3D/fx/particle.cpp b/Engine/source/T3D/fx/particle.cpp new file mode 100644 index 000000000..891f9353b --- /dev/null +++ b/Engine/source/T3D/fx/particle.cpp @@ -0,0 +1,633 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#include "particle.h" +#include "console/consoleTypes.h" +#include "core/stream/bitStream.h" +#include "math/mRandom.h" +#include "math/mathIO.h" +#include "console/engineAPI.h" + +IMPLEMENT_CO_DATABLOCK_V1( ParticleData ); + +ConsoleDocClass( ParticleData, + "@brief Contains information for how specific particles should look and react " + "including particle colors, particle imagemap, acceleration value for individual " + "particles and spin information.\n" + + "@tsexample\n" + "datablock ParticleData( GLWaterExpSmoke )\n" + "{\n" + " textureName = \"art/shapes/particles/smoke\";\n" + " dragCoefficient = 0.4;\n" + " gravityCoefficient = -0.25;\n" + " inheritedVelFactor = 0.025;\n" + " constantAcceleration = -1.1;\n" + " lifetimeMS = 1250;\n" + " lifetimeVarianceMS = 0;\n" + " useInvAlpha = false;\n" + " spinSpeed = 1;\n" + " spinRandomMin = -200.0;\n" + " spinRandomMax = 200.0;\n\n" + " colors[0] = \"0.1 0.1 1.0 1.0\";\n" + " colors[1] = \"0.4 0.4 1.0 1.0\";\n" + " colors[2] = \"0.4 0.4 1.0 0.0\";\n\n" + " sizes[0] = 2.0;\n" + " sizes[1] = 6.0;\n" + " sizes[2] = 2.0;\n\n" + " times[0] = 0.0;\n" + " times[1] = 0.5;\n" + " times[2] = 1.0;\n" + "};\n" + "@endtsexample\n" + + "@ingroup FX\n" + "@see ParticleEmitter\n" + "@see ParticleEmitterData\n" + "@see ParticleEmitterNode\n" +); + +static const float sgDefaultWindCoefficient = 0.0f; +static const float sgDefaultConstantAcceleration = 0.f; +static const float sgDefaultSpinSpeed = 1.f; +static const float sgDefaultSpinRandomMin = 0.f; +static const float sgDefaultSpinRandomMax = 0.f; + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +ParticleData::ParticleData() +{ + dragCoefficient = 0.0f; + windCoefficient = sgDefaultWindCoefficient; + gravityCoefficient = 0.0f; + inheritedVelFactor = 0.0f; + constantAcceleration = sgDefaultConstantAcceleration; + lifetimeMS = 1000; + lifetimeVarianceMS = 0; + spinSpeed = sgDefaultSpinSpeed; + spinRandomMin = sgDefaultSpinRandomMin; + spinRandomMax = sgDefaultSpinRandomMax; + useInvAlpha = false; + animateTexture = false; + + numFrames = 1; + framesPerSec = numFrames; + + S32 i; + for( i=0; i(), Offset(dragCoefficient, ParticleData), + "Particle physics drag amount." ); + addField( "windCoefficient", TYPEID< F32 >(), Offset(windCoefficient, ParticleData), + "Strength of wind on the particles." ); + addField( "gravityCoefficient", TYPEID< F32 >(), Offset(gravityCoefficient, ParticleData), + "Strength of gravity on the particles." ); + addField( "inheritedVelFactor", TYPEID< F32 >(), Offset(inheritedVelFactor, ParticleData), + "Amount of emitter velocity to add to particle initial velocity." ); + addField( "constantAcceleration", TYPEID< F32 >(), Offset(constantAcceleration, ParticleData), + "Constant acceleration to apply to this particle." ); + addField( "lifetimeMS", TYPEID< S32 >(), Offset(lifetimeMS, ParticleData), + "Time in milliseconds before this particle is destroyed." ); + addField( "lifetimeVarianceMS", TYPEID< S32 >(), Offset(lifetimeVarianceMS, ParticleData), + "Variance in lifetime of particle, from 0 - lifetimeMS." ); + addField( "spinSpeed", TYPEID< F32 >(), Offset(spinSpeed, ParticleData), + "Speed at which to spin the particle." ); + addField( "spinRandomMin", TYPEID< F32 >(), Offset(spinRandomMin, ParticleData), + "Minimum allowed spin speed of this particle, between -10000 and spinRandomMax." ); + addField( "spinRandomMax", TYPEID< F32 >(), Offset(spinRandomMax, ParticleData), + "Maximum allowed spin speed of this particle, between spinRandomMin and 10000." ); + addField( "useInvAlpha", TYPEID< bool >(), Offset(useInvAlpha, ParticleData), + "@brief Controls how particles blend with the scene.\n\n" + "If true, particles blend like ParticleBlendStyle NORMAL, if false, " + "blend like ParticleBlendStyle ADDITIVE.\n" + "@note If ParticleEmitterData::blendStyle is set, it will override this value." ); + addField( "animateTexture", TYPEID< bool >(), Offset(animateTexture, ParticleData), + "If true, allow the particle texture to be an animated sprite." ); + addField( "framesPerSec", TYPEID< S32 >(), Offset(framesPerSec, ParticleData), + "If animateTexture is true, this defines the frames per second of the " + "sprite animation." ); + + addField( "textureCoords", TYPEID< Point2F >(), Offset(texCoords, ParticleData), 4, + "@brief 4 element array defining the UV coords into textureName to use " + "for this particle.\n\n" + "Coords should be set for the first tile only when using animTexTiling; " + "coordinates for other tiles will be calculated automatically. \"0 0\" is " + "top left and \"1 1\" is bottom right." ); + addField( "animTexTiling", TYPEID< Point2I >(), Offset(animTexTiling, ParticleData), + "@brief The number of frames, in rows and columns stored in textureName " + "(when animateTexture is true).\n\n" + "A maximum of 256 frames can be stored in a single texture when using " + "animTexTiling. Value should be \"NumColumns NumRows\", for example \"4 4\"." ); + addField( "animTexFrames", TYPEID< StringTableEntry >(), Offset(animTexFramesString,ParticleData), + "@brief A list of frames and/or frame ranges to use for particle " + "animation if animateTexture is true.\n\n" + "Each frame token must be separated by whitespace. A frame token must be " + "a positive integer frame number or a range of frame numbers separated " + "with a '-'. The range separator, '-', cannot have any whitspace around " + "it.\n\n" + "Ranges can be specified to move through the frames in reverse as well " + "as forward (eg. 19-14). Frame numbers exceeding the number of tiles will " + "wrap.\n" + "@tsexample\n" + "animTexFrames = \"0-16 20 19 18 17 31-21\";\n" + "@endtsexample\n" ); + + addField( "textureName", TYPEID< StringTableEntry >(), Offset(textureName, ParticleData), + "Texture file to use for this particle." ); + addField( "animTexName", TYPEID< StringTableEntry >(), Offset(textureName, ParticleData), + "@brief Texture file to use for this particle if animateTexture is true.\n\n" + "Deprecated. Use textureName instead." ); + + // Interpolation variables + addField( "colors", TYPEID< ColorF >(), Offset(colors, ParticleData), PDC_NUM_KEYS, + "@brief Particle RGBA color keyframe values.\n\n" + "The particle color will linearly interpolate between the color/time keys " + "over the lifetime of the particle." ); + addField( "sizes", TYPEID< F32 >(), Offset(sizes, ParticleData), PDC_NUM_KEYS, + "@brief Particle size keyframe values.\n\n" + "The particle size will linearly interpolate between the size/time keys " + "over the lifetime of the particle." ); + addProtectedField( "times", TYPEID< F32 >(), Offset(times, ParticleData), &protectedSetTimes, + &defaultProtectedGetFn, PDC_NUM_KEYS, + "@brief Time keys used with the colors and sizes keyframes.\n\n" + "Values are from 0.0 (particle creation) to 1.0 (end of lifespace)." ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- +// Pack data +//----------------------------------------------------------------------------- +void ParticleData::packData(BitStream* stream) +{ + Parent::packData(stream); + + stream->writeFloat(dragCoefficient / 5, 10); + if( stream->writeFlag(windCoefficient != sgDefaultWindCoefficient ) ) + stream->write(windCoefficient); + if (stream->writeFlag(gravityCoefficient != 0.0f)) + stream->writeSignedFloat(gravityCoefficient / 10, 12); + stream->writeFloat(inheritedVelFactor, 9); + if( stream->writeFlag( constantAcceleration != sgDefaultConstantAcceleration ) ) + stream->write(constantAcceleration); + + stream->write( lifetimeMS ); + stream->write( lifetimeVarianceMS ); + + if( stream->writeFlag( spinSpeed != sgDefaultSpinSpeed ) ) + stream->write(spinSpeed); + if(stream->writeFlag(spinRandomMin != sgDefaultSpinRandomMin || spinRandomMax != sgDefaultSpinRandomMax)) + { + stream->writeInt((S32)(spinRandomMin + 1000), 11); + stream->writeInt((S32)(spinRandomMax + 1000), 11); + } + stream->writeFlag(useInvAlpha); + + S32 i, count; + + // see how many frames there are: + for(count = 0; count < 3; count++) + if(times[count] >= 1) + break; + + count++; + + stream->writeInt(count-1, 2); + + for( i=0; iwriteFloat( colors[i].red, 7); + stream->writeFloat( colors[i].green, 7); + stream->writeFloat( colors[i].blue, 7); + stream->writeFloat( colors[i].alpha, 7); + stream->writeFloat( sizes[i]/MaxParticleSize, 14); + stream->writeFloat( times[i], 8); + } + + if (stream->writeFlag(textureName && textureName[0])) + stream->writeString(textureName); + for (i = 0; i < 4; i++) + mathWrite(*stream, texCoords[i]); + if (stream->writeFlag(animateTexture)) + { + if (stream->writeFlag(animTexFramesString && animTexFramesString[0])) + { + stream->writeString(animTexFramesString); + } + mathWrite(*stream, animTexTiling); + stream->writeInt(framesPerSec, 8); + } +} + +//----------------------------------------------------------------------------- +// Unpack data +//----------------------------------------------------------------------------- +void ParticleData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + + dragCoefficient = stream->readFloat(10) * 5; + if(stream->readFlag()) + stream->read(&windCoefficient); + else + windCoefficient = sgDefaultWindCoefficient; + if (stream->readFlag()) + gravityCoefficient = stream->readSignedFloat(12)*10; + else + gravityCoefficient = 0.0f; + inheritedVelFactor = stream->readFloat(9); + if(stream->readFlag()) + stream->read(&constantAcceleration); + else + constantAcceleration = sgDefaultConstantAcceleration; + + stream->read( &lifetimeMS ); + stream->read( &lifetimeVarianceMS ); + + if(stream->readFlag()) + stream->read(&spinSpeed); + else + spinSpeed = sgDefaultSpinSpeed; + + if(stream->readFlag()) + { + spinRandomMin = (F32)(stream->readInt(11) - 1000); + spinRandomMax = (F32)(stream->readInt(11) - 1000); + } + else + { + spinRandomMin = sgDefaultSpinRandomMin; + spinRandomMax = sgDefaultSpinRandomMax; + } + + useInvAlpha = stream->readFlag(); + + S32 i; + S32 count = stream->readInt(2) + 1; + for(i = 0;i < count; i++) + { + colors[i].red = stream->readFloat(7); + colors[i].green = stream->readFloat(7); + colors[i].blue = stream->readFloat(7); + colors[i].alpha = stream->readFloat(7); + sizes[i] = stream->readFloat(14) * MaxParticleSize; + times[i] = stream->readFloat(8); + } + textureName = (stream->readFlag()) ? stream->readSTString() : 0; + for (i = 0; i < 4; i++) + mathRead(*stream, &texCoords[i]); + + animateTexture = stream->readFlag(); + if (animateTexture) + { + animTexFramesString = (stream->readFlag()) ? stream->readSTString() : 0; + mathRead(*stream, &animTexTiling); + framesPerSec = stream->readInt(8); + } +} + +bool ParticleData::protectedSetTimes( void *object, const char *index, const char *data) +{ + ParticleData *pData = static_cast( object ); + F32 val = dAtof(data); + U32 i; + + if (!index) + i = 0; + else + i = dAtoui(index); + + pData->times[i] = mClampF( val, 0.f, 1.f ); + + return true; +} + +//----------------------------------------------------------------------------- +// onAdd +//----------------------------------------------------------------------------- +bool ParticleData::onAdd() +{ + if (Parent::onAdd() == false) + return false; + + if (dragCoefficient < 0.0) { + Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) drag coeff less than 0", getName()); + dragCoefficient = 0.0f; + } + if (lifetimeMS < 1) { + Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) lifetime < 1 ms", getName()); + lifetimeMS = 1; + } + if (lifetimeVarianceMS >= lifetimeMS) { + Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) lifetimeVariance >= lifetime", getName()); + lifetimeVarianceMS = lifetimeMS - 1; + } + if (spinSpeed > 10000.0 || spinSpeed < -10000.0) { + Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) spinSpeed invalid", getName()); + return false; + } + if (spinRandomMin > 10000.0 || spinRandomMin < -10000.0) { + Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) spinRandomMin invalid", getName()); + spinRandomMin = -360.0; + return false; + } + if (spinRandomMin > spinRandomMax) { + Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) spinRandomMin greater than spinRandomMax", getName()); + spinRandomMin = spinRandomMax - (spinRandomMin - spinRandomMax ); + return false; + } + if (spinRandomMax > 10000.0 || spinRandomMax < -10000.0) { + Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) spinRandomMax invalid", getName()); + spinRandomMax = 360.0; + return false; + } + if (framesPerSec > 255) + { + Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) framesPerSec > 255, too high", getName()); + framesPerSec = 255; + return false; + } + + times[0] = 0.0f; + for (U32 i = 1; i < 4; i++) { + if (times[i] < times[i-1]) { + Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) times[%d] < times[%d]", getName(), i, i-1); + times[i] = times[i-1]; + } + } + + // Here we validate parameters + if (animateTexture) + { + // Tiling dimensions must be positive and non-zero + if (animTexTiling.x <= 0 || animTexTiling.y <= 0) + { + Con::warnf(ConsoleLogEntry::General, + "ParticleData(%s) bad value(s) for animTexTiling [%d or %d <= 0], invalid datablock", + animTexTiling.x, animTexTiling.y, getName()); + return false; + } + + // Indices must fit into a byte so these are also bad + if (animTexTiling.x * animTexTiling.y > 256) + { + Con::warnf(ConsoleLogEntry::General, + "ParticleData(%s) bad values for animTexTiling [%d*%d > %d], invalid datablock", + animTexTiling.x, animTexTiling.y, 256, getName()); + return false; + } + + // A list of frames is required + if (!animTexFramesString || !animTexFramesString[0]) + { + Con::warnf(ConsoleLogEntry::General, "ParticleData(%s) no animTexFrames, invalid datablock", getName()); + return false; + } + + // The frame list cannot be too long. + if (animTexFramesString && dStrlen(animTexFramesString) > 255) + { + Con::errorf(ConsoleLogEntry::General, "ParticleData(%s) animTexFrames string too long [> 255 chars]", getName()); + return false; + } + } + + return true; +} + +//----------------------------------------------------------------------------- +// preload +//----------------------------------------------------------------------------- +bool ParticleData::preload(bool server, String &errorStr) +{ + if (Parent::preload(server, errorStr) == false) + return false; + + bool error = false; + if(!server) + { + // Here we attempt to load the particle's texture if specified. An undefined + // texture is *not* an error since the emitter may provide one. + if (textureName && textureName[0]) + { + textureHandle = GFXTexHandle(textureName, &GFXDefaultStaticDiffuseProfile, avar("%s() - textureHandle (line %d)", __FUNCTION__, __LINE__)); + if (!textureHandle) + { + errorStr = String::ToString("Missing particle texture: %s", textureName); + error = true; + } + } + + if (animateTexture) + { + // Here we parse animTexFramesString into byte-size frame numbers in animTexFrames. + // Each frame token must be separated by whitespace. + // A frame token must be a positive integer frame number or a range of frame numbers + // separated with a '-'. + // The range separator, '-', cannot have any whitspace around it. + // Ranges can be specified to move through the frames in reverse as well as forward. + // Frame numbers exceeding the number of tiles will wrap. + // example: + // "0-16 20 19 18 17 31-21" + + S32 n_tiles = animTexTiling.x * animTexTiling.y; + AssertFatal(n_tiles > 0 && n_tiles <= 256, "Error, bad animTexTiling setting." ); + + animTexFrames.clear(); + + char* tokCopy = new char[dStrlen(animTexFramesString) + 1]; + dStrcpy(tokCopy, animTexFramesString); + + char* currTok = dStrtok(tokCopy, " \t"); + while (currTok != NULL) + { + char* minus = dStrchr(currTok, '-'); + if (minus) + { + // add a range of frames + *minus = '\0'; + S32 range_a = dAtoi(currTok); + S32 range_b = dAtoi(minus+1); + if (range_b < range_a) + { + // reverse frame range + for (S32 i = range_a; i >= range_b; i--) + animTexFrames.push_back((U8)(i % n_tiles)); + } + else + { + // forward frame range + for (S32 i = range_a; i <= range_b; i++) + animTexFrames.push_back((U8)(i % n_tiles)); + } + } + else + { + // add one frame + animTexFrames.push_back((U8)(dAtoi(currTok) % n_tiles)); + } + currTok = dStrtok(NULL, " \t"); + } + + // Here we pre-calculate the UVs for each frame tile, which are + // tiled inside the UV region specified by texCoords. Since the + // UVs are calculated using bilinear interpolation, the texCoords + // region does *not* have to be an axis-aligned rectangle. + + if (animTexUVs) + delete [] animTexUVs; + + animTexUVs = new Point2F[(animTexTiling.x+1)*(animTexTiling.y+1)]; + + // interpolate points on the left and right edge of the uv quadrangle + Point2F lf_pt = texCoords[0]; + Point2F rt_pt = texCoords[3]; + + // per-row delta for left and right interpolated points + Point2F lf_d = (texCoords[1] - texCoords[0])/(F32)animTexTiling.y; + Point2F rt_d = (texCoords[2] - texCoords[3])/(F32)animTexTiling.y; + + S32 idx = 0; + for (S32 yy = 0; yy <= animTexTiling.y; yy++) + { + Point2F p = lf_pt; + Point2F dp = (rt_pt - lf_pt)/(F32)animTexTiling.x; + for (S32 xx = 0; xx <= animTexTiling.x; xx++) + { + animTexUVs[idx++] = p; + p += dp; + } + lf_pt += lf_d; + rt_pt += rt_d; + } + + // cleanup + delete [] tokCopy; + numFrames = animTexFrames.size(); + } + } + + return !error; +} + +//----------------------------------------------------------------------------- +// Initialize particle +//----------------------------------------------------------------------------- +void ParticleData::initializeParticle(Particle* init, const Point3F& inheritVelocity) +{ + init->dataBlock = this; + + // Calculate the constant accleration... + init->vel += inheritVelocity * inheritedVelFactor; + init->acc = init->vel * constantAcceleration; + + // Calculate this instance's lifetime... + init->totalLifetime = lifetimeMS; + if (lifetimeVarianceMS != 0) + init->totalLifetime += S32(gRandGen.randI() % (2 * lifetimeVarianceMS + 1)) - S32(lifetimeVarianceMS); + + // assign spin amount + init->spinSpeed = spinSpeed * gRandGen.randF( spinRandomMin, spinRandomMax ); +} + +bool ParticleData::reload(char errorBuffer[256]) +{ + bool error = false; + if (textureName && textureName[0]) + { + textureHandle = GFXTexHandle(textureName, &GFXDefaultStaticDiffuseProfile, avar("%s() - textureHandle (line %d)", __FUNCTION__, __LINE__)); + if (!textureHandle) + { + dSprintf(errorBuffer, 256, "Missing particle texture: %s", textureName); + error = true; + } + } + /* + numFrames = 0; + for( int i=0; ireload(errorBuffer); +} diff --git a/Engine/source/T3D/fx/particle.h b/Engine/source/T3D/fx/particle.h new file mode 100644 index 000000000..3f1b7ff26 --- /dev/null +++ b/Engine/source/T3D/fx/particle.h @@ -0,0 +1,128 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PARTICLE_H_ +#define _PARTICLE_H_ + +#ifndef _GAMEBASE_H_ +#include "T3D/gameBase/gameBase.h" +#endif +#ifndef _GFXTEXTUREHANDLE_H_ +#include "gfx/gfxTextureHandle.h" +#endif + +#define MaxParticleSize 50.0 + +struct Particle; + +//***************************************************************************** +// Particle Data +//***************************************************************************** +class ParticleData : public SimDataBlock +{ + typedef SimDataBlock Parent; + + public: + enum PDConst + { + PDC_NUM_KEYS = 4, + }; + + F32 dragCoefficient; + F32 windCoefficient; + F32 gravityCoefficient; + + F32 inheritedVelFactor; + F32 constantAcceleration; + + S32 lifetimeMS; + S32 lifetimeVarianceMS; + + F32 spinSpeed; // degrees per second + F32 spinRandomMin; + F32 spinRandomMax; + + bool useInvAlpha; + + bool animateTexture; + U32 numFrames; + U32 framesPerSec; + + ColorF colors[ PDC_NUM_KEYS ]; + F32 sizes[ PDC_NUM_KEYS ]; + F32 times[ PDC_NUM_KEYS ]; + + Point2F* animTexUVs; + Point2F texCoords[4]; // default: {{0.0,0.0}, {0.0,1.0}, {1.0,1.0}, {1.0,0.0}} + Point2I animTexTiling; + StringTableEntry animTexFramesString; + Vector animTexFrames; + StringTableEntry textureName; + GFXTexHandle textureHandle; + + static bool protectedSetTimes( void *object, const char *index, const char *data ); + + public: + ParticleData(); + ~ParticleData(); + + // move this procedure to Particle + void initializeParticle(Particle*, const Point3F&); + + void packData(BitStream* stream); + void unpackData(BitStream* stream); + bool onAdd(); + bool preload(bool server, String &errorStr); + DECLARE_CONOBJECT(ParticleData); + static void initPersistFields(); + + bool reload(char errorBuffer[256]); +}; + +//***************************************************************************** +// Particle +// +// This structure should be as small as possible. +//***************************************************************************** +struct Particle +{ + Point3F pos; // current instantaneous position + Point3F vel; // " " velocity + Point3F acc; // Constant acceleration + Point3F orientDir; // direction particle should go if using oriented particles + + U32 totalLifetime; // Total ms that this instance should be "live" + ParticleData* dataBlock; // datablock that contains global parameters for + // this instance + U32 currentAge; + + + // are these necessary to store here? - they are interpolated in real time + ColorF color; + F32 size; + + F32 spinSpeed; + Particle * next; +}; + + +#endif // _PARTICLE_H_ \ No newline at end of file diff --git a/Engine/source/T3D/fx/particleEmitter.cpp b/Engine/source/T3D/fx/particleEmitter.cpp new file mode 100644 index 000000000..eb0d120ed --- /dev/null +++ b/Engine/source/T3D/fx/particleEmitter.cpp @@ -0,0 +1,2006 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/fx/particleEmitter.h" + +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "console/consoleTypes.h" +#include "core/stream/bitStream.h" +#include "core/strings/stringUnit.h" +#include "math/mRandom.h" +#include "gfx/gfxDevice.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxStringEnumTranslate.h" +#include "renderInstance/renderPassManager.h" +#include "T3D/gameBase/gameProcess.h" +#include "lighting/lightInfo.h" +#include "console/engineAPI.h" + +#if defined(TORQUE_OS_XENON) +# include "gfx/D3D9/360/gfx360MemVertexBuffer.h" +#endif + +Point3F ParticleEmitter::mWindVelocity( 0.0, 0.0, 0.0 ); +const F32 ParticleEmitter::AgedSpinToRadians = (1.0f/1000.0f) * (1.0f/360.0f) * M_PI_F * 2.0f; + +IMPLEMENT_CO_DATABLOCK_V1(ParticleEmitterData); +IMPLEMENT_CONOBJECT(ParticleEmitter); + +ConsoleDocClass( ParticleEmitter, + "@brief This object is responsible for spawning particles.\n\n" + + "@note This class is not normally instantiated directly - to place a simple " + "particle emitting object in the scene, use a ParticleEmitterNode instead.\n\n" + + "This class is the main interface for creating particles - though it is " + "usually only accessed from within another object like ParticleEmitterNode " + "or WheeledVehicle. If using this object class (via C++) directly, be aware " + "that it does not track changes in source axis or velocity over the " + "course of a single update, so emitParticles should be called at a fairly " + "fine grain. The emitter will potentially track the last particle to be " + "created into the next call to this function in order to create a uniformly " + "random time distribution of the particles.\n\n" + + "If the object to which the emitter is attached is in motion, it should try " + "to ensure that for call (n+1) to this function, start is equal to the end " + "from call (n). This will ensure a uniform spatial distribution.\n\n" + + "@ingroup FX\n" + "@see ParticleEmitterData\n" + "@see ParticleEmitterNode\n" +); + +ConsoleDocClass( ParticleEmitterData, + "@brief Defines particle emission properties such as ejection angle, period " + "and velocity for a ParticleEmitter.\n\n" + + "@tsexample\n" + "datablock ParticleEmitterData( GrenadeExpDustEmitter )\n" + "{\n" + " ejectionPeriodMS = 1;\n" + " periodVarianceMS = 0;\n" + " ejectionVelocity = 15;\n" + " velocityVariance = 0.0;\n" + " ejectionOffset = 0.0;\n" + " thetaMin = 85;\n" + " thetaMax = 85;\n" + " phiReferenceVel = 0;\n" + " phiVariance = 360;\n" + " overrideAdvance = false;\n" + " lifetimeMS = 200;\n" + " particles = \"GrenadeExpDust\";\n" + "};\n" + "@endtsexample\n\n" + + "@ingroup FX\n" + "@see ParticleEmitter\n" + "@see ParticleData\n" + "@see ParticleEmitterNode\n" +); + +static const float sgDefaultEjectionOffset = 0.f; +static const float sgDefaultPhiReferenceVel = 0.f; +static const float sgDefaultPhiVariance = 360.f; + +//----------------------------------------------------------------------------- +// ParticleEmitterData +//----------------------------------------------------------------------------- +ParticleEmitterData::ParticleEmitterData() +{ + VECTOR_SET_ASSOCIATION(particleDataBlocks); + VECTOR_SET_ASSOCIATION(dataBlockIds); + + ejectionPeriodMS = 100; // 10 Particles Per second + periodVarianceMS = 0; // exactly + + ejectionVelocity = 2.0f; // From 1.0 - 3.0 meters per sec + velocityVariance = 1.0f; + ejectionOffset = sgDefaultEjectionOffset; // ejection from the emitter point + + thetaMin = 0.0f; // All heights + thetaMax = 90.0f; + + phiReferenceVel = sgDefaultPhiReferenceVel; // All directions + phiVariance = sgDefaultPhiVariance; + + softnessDistance = 1.0f; + ambientFactor = 0.0f; + + lifetimeMS = 0; + lifetimeVarianceMS = 0; + + overrideAdvance = true; + orientParticles = false; + orientOnVelocity = true; + useEmitterSizes = false; + useEmitterColors = false; + particleString = NULL; + partListInitSize = 0; + + // These members added for support of user defined blend factors + // and optional particle sorting. + blendStyle = ParticleRenderInst::BlendUndefined; + sortParticles = false; + renderReflection = true; + reverseOrder = false; + textureName = 0; + textureHandle = 0; + highResOnly = true; + + alignParticles = false; + alignDirection = Point3F(0.0f, 1.0f, 0.0f); +} + + + +// Enum tables used for fields blendStyle, srcBlendFactor, dstBlendFactor. +// Note that the enums for srcBlendFactor and dstBlendFactor are consistent +// with the blending enums used in Torque Game Builder. + +typedef ParticleRenderInst::BlendStyle ParticleBlendStyle; +DefineEnumType( ParticleBlendStyle ); + +ImplementEnumType( ParticleBlendStyle, + "The type of visual blending style to apply to the particles.\n" + "@ingroup FX\n\n") + { ParticleRenderInst::BlendNormal, "NORMAL", "No blending style.\n" }, + { ParticleRenderInst::BlendAdditive, "ADDITIVE", "Adds the color of the pixel to the frame buffer with full alpha for each pixel.\n" }, + { ParticleRenderInst::BlendSubtractive, "SUBTRACTIVE", "Subtractive Blending. Reverses the color model, causing dark colors to have a stronger visual effect.\n" }, + { ParticleRenderInst::BlendPremultAlpha, "PREMULTALPHA", "Color blends with the colors of the imagemap rather than the alpha.\n" }, +EndImplementEnumType; + +//----------------------------------------------------------------------------- +// initPersistFields +//----------------------------------------------------------------------------- +void ParticleEmitterData::initPersistFields() +{ + addGroup( "ParticleEmitterData" ); + + addField("ejectionPeriodMS", TYPEID< S32 >(), Offset(ejectionPeriodMS, ParticleEmitterData), + "Time (in milliseconds) between each particle ejection." ); + + addField("periodVarianceMS", TYPEID< S32 >(), Offset(periodVarianceMS, ParticleEmitterData), + "Variance in ejection period, from 1 - ejectionPeriodMS." ); + + addField( "ejectionVelocity", TYPEID< F32 >(), Offset(ejectionVelocity, ParticleEmitterData), + "Particle ejection velocity." ); + + addField( "velocityVariance", TYPEID< F32 >(), Offset(velocityVariance, ParticleEmitterData), + "Variance for ejection velocity, from 0 - ejectionVelocity." ); + + addField( "ejectionOffset", TYPEID< F32 >(), Offset(ejectionOffset, ParticleEmitterData), + "Distance along ejection Z axis from which to eject particles." ); + + addField( "thetaMin", TYPEID< F32 >(), Offset(thetaMin, ParticleEmitterData), + "Minimum angle, from the horizontal plane, to eject from." ); + + addField( "thetaMax", TYPEID< F32 >(), Offset(thetaMax, ParticleEmitterData), + "Maximum angle, from the horizontal plane, to eject particles from." ); + + addField( "phiReferenceVel", TYPEID< F32 >(), Offset(phiReferenceVel, ParticleEmitterData), + "Reference angle, from the vertical plane, to eject particles from." ); + + addField( "phiVariance", TYPEID< F32 >(), Offset(phiVariance, ParticleEmitterData), + "Variance from the reference angle, from 0 - 360." ); + + addField( "softnessDistance", TYPEID< F32 >(), Offset(softnessDistance, ParticleEmitterData), + "For soft particles, the distance (in meters) where particles will be " + "faded based on the difference in depth between the particle and the " + "scene geometry." ); + + addField( "ambientFactor", TYPEID< F32 >(), Offset(ambientFactor, ParticleEmitterData), + "Used to generate the final particle color by controlling interpolation " + "between the particle color and the particle color multiplied by the " + "ambient light color." ); + + addField( "overrideAdvance", TYPEID< bool >(), Offset(overrideAdvance, ParticleEmitterData), + "If false, particles emitted in the same frame have their positions " + "adjusted. If true, adjustment is skipped and particles will clump " + "together." ); + + addField( "orientParticles", TYPEID< bool >(), Offset(orientParticles, ParticleEmitterData), + "If true, Particles will always face the camera." ); + + addField( "orientOnVelocity", TYPEID< bool >(), Offset(orientOnVelocity, ParticleEmitterData), + "If true, particles will be oriented to face in the direction they are moving." ); + + addField( "particles", TYPEID< StringTableEntry >(), Offset(particleString, ParticleEmitterData), + "@brief List of space or TAB delimited ParticleData datablock names.\n\n" + "A random one of these datablocks is selected each time a particle is " + "emitted." ); + + addField( "lifetimeMS", TYPEID< S32 >(), Offset(lifetimeMS, ParticleEmitterData), + "Lifetime of emitted particles (in milliseconds)." ); + + addField("lifetimeVarianceMS", TYPEID< S32 >(), Offset(lifetimeVarianceMS, ParticleEmitterData), + "Variance in particle lifetime from 0 - lifetimeMS." ); + + addField( "useEmitterSizes", TYPEID< bool >(), Offset(useEmitterSizes, ParticleEmitterData), + "@brief If true, use emitter specified sizes instead of datablock sizes.\n" + "Useful for Debris particle emitters that control the particle size." ); + + addField( "useEmitterColors", TYPEID< bool >(), Offset(useEmitterColors, ParticleEmitterData), + "@brief If true, use emitter specified colors instead of datablock colors.\n\n" + "Useful for ShapeBase dust and WheeledVehicle wheel particle emitters that use " + "the current material to control particle color." ); + + /// These fields added for support of user defined blend factors and optional particle sorting. + //@{ + addField( "blendStyle", TYPEID< ParticleRenderInst::BlendStyle >(), Offset(blendStyle, ParticleEmitterData), + "String value that controls how emitted particles blend with the scene." ); + + addField( "sortParticles", TYPEID< bool >(), Offset(sortParticles, ParticleEmitterData), + "If true, particles are sorted furthest to nearest."); + + addField( "reverseOrder", TYPEID< bool >(), Offset(reverseOrder, ParticleEmitterData), + "@brief If true, reverses the normal draw order of particles.\n\n" + "Particles are normally drawn from newest to oldest, or in Z order " + "(furthest first) if sortParticles is true. Setting this field to " + "true will reverse that order: oldest first, or nearest first if " + "sortParticles is true." ); + + addField( "textureName", TYPEID< StringTableEntry >(), Offset(textureName, ParticleEmitterData), + "Optional texture to override ParticleData::textureName." ); + + addField( "alignParticles", TYPEID< bool >(), Offset(alignParticles, ParticleEmitterData), + "If true, particles always face along the axis defined by alignDirection." ); + + addProtectedField( "alignDirection", TYPEID< Point3F>(), Offset(alignDirection, ParticleEmitterData), &ParticleEmitterData::_setAlignDirection, &defaultProtectedGetFn, + "The direction aligned particles should face, only valid if alignParticles is true." ); + + addField( "highResOnly", TYPEID< bool >(), Offset(highResOnly, ParticleEmitterData), + "This particle system should not use the mixed-resolution renderer. " + "If your particle system has large amounts of overdraw, consider " + "disabling this option." ); + + addField( "renderReflection", TYPEID< bool >(), Offset(renderReflection, ParticleEmitterData), + "Controls whether particles are rendered onto reflective surfaces like water." ); + + //@} + + endGroup( "ParticleEmitterData" ); + + Parent::initPersistFields(); +} + +bool ParticleEmitterData::_setAlignDirection( void *object, const char *index, const char *data ) +{ + ParticleEmitterData *p = static_cast( object ); + + Con::setData( TypePoint3F, &p->alignDirection, 0, 1, &data ); + p->alignDirection.normalizeSafe(); + + // we already set the field + return false; +} + +//----------------------------------------------------------------------------- +// packData +//----------------------------------------------------------------------------- +void ParticleEmitterData::packData(BitStream* stream) +{ + Parent::packData(stream); + + stream->writeInt(ejectionPeriodMS, 10); + stream->writeInt(periodVarianceMS, 10); + stream->writeInt((S32)(ejectionVelocity * 100), 16); + stream->writeInt((S32)(velocityVariance * 100), 14); + if( stream->writeFlag( ejectionOffset != sgDefaultEjectionOffset ) ) + stream->writeInt((S32)(ejectionOffset * 100), 16); + stream->writeRangedU32((U32)thetaMin, 0, 180); + stream->writeRangedU32((U32)thetaMax, 0, 180); + if( stream->writeFlag( phiReferenceVel != sgDefaultPhiReferenceVel ) ) + stream->writeRangedU32((U32)phiReferenceVel, 0, 360); + if( stream->writeFlag( phiVariance != sgDefaultPhiVariance ) ) + stream->writeRangedU32((U32)phiVariance, 0, 360); + + stream->write( softnessDistance ); + stream->write( ambientFactor ); + + stream->writeFlag(overrideAdvance); + stream->writeFlag(orientParticles); + stream->writeFlag(orientOnVelocity); + stream->write( lifetimeMS ); + stream->write( lifetimeVarianceMS ); + stream->writeFlag(useEmitterSizes); + stream->writeFlag(useEmitterColors); + + stream->write(dataBlockIds.size()); + for (U32 i = 0; i < dataBlockIds.size(); i++) + stream->write(dataBlockIds[i]); + stream->writeFlag(sortParticles); + stream->writeFlag(reverseOrder); + if (stream->writeFlag(textureName != 0)) + stream->writeString(textureName); + + if (stream->writeFlag(alignParticles)) + { + stream->write(alignDirection.x); + stream->write(alignDirection.y); + stream->write(alignDirection.z); + } + stream->writeFlag(highResOnly); + stream->writeFlag(renderReflection); + stream->writeInt( blendStyle, 4 ); +} + +//----------------------------------------------------------------------------- +// unpackData +//----------------------------------------------------------------------------- +void ParticleEmitterData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + + ejectionPeriodMS = stream->readInt(10); + periodVarianceMS = stream->readInt(10); + ejectionVelocity = stream->readInt(16) / 100.0f; + velocityVariance = stream->readInt(14) / 100.0f; + if( stream->readFlag() ) + ejectionOffset = stream->readInt(16) / 100.0f; + else + ejectionOffset = sgDefaultEjectionOffset; + + thetaMin = (F32)stream->readRangedU32(0, 180); + thetaMax = (F32)stream->readRangedU32(0, 180); + if( stream->readFlag() ) + phiReferenceVel = (F32)stream->readRangedU32(0, 360); + else + phiReferenceVel = sgDefaultPhiReferenceVel; + + if( stream->readFlag() ) + phiVariance = (F32)stream->readRangedU32(0, 360); + else + phiVariance = sgDefaultPhiVariance; + + stream->read( &softnessDistance ); + stream->read( &ambientFactor ); + + overrideAdvance = stream->readFlag(); + orientParticles = stream->readFlag(); + orientOnVelocity = stream->readFlag(); + stream->read( &lifetimeMS ); + stream->read( &lifetimeVarianceMS ); + useEmitterSizes = stream->readFlag(); + useEmitterColors = stream->readFlag(); + + U32 size; stream->read(&size); + dataBlockIds.setSize(size); + for (U32 i = 0; i < dataBlockIds.size(); i++) + stream->read(&dataBlockIds[i]); + sortParticles = stream->readFlag(); + reverseOrder = stream->readFlag(); + textureName = (stream->readFlag()) ? stream->readSTString() : 0; + + alignParticles = stream->readFlag(); + if (alignParticles) + { + stream->read(&alignDirection.x); + stream->read(&alignDirection.y); + stream->read(&alignDirection.z); + } + highResOnly = stream->readFlag(); + renderReflection = stream->readFlag(); + blendStyle = stream->readInt( 4 ); +} + +//----------------------------------------------------------------------------- +// onAdd +//----------------------------------------------------------------------------- +bool ParticleEmitterData::onAdd() +{ + if( Parent::onAdd() == false ) + return false; + +// if (overrideAdvance == true) { +// Con::errorf(ConsoleLogEntry::General, "ParticleEmitterData: Not going to work. Fix it!"); +// return false; +// } + + // Validate the parameters... + // + if( ejectionPeriodMS < 1 ) + { + Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) period < 1 ms", getName()); + ejectionPeriodMS = 1; + } + if( periodVarianceMS >= ejectionPeriodMS ) + { + Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) periodVariance >= period", getName()); + periodVarianceMS = ejectionPeriodMS - 1; + } + if( ejectionVelocity < 0.0f ) + { + Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) ejectionVelocity < 0.0f", getName()); + ejectionVelocity = 0.0f; + } + if( velocityVariance < 0.0f ) + { + Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) velocityVariance < 0.0f", getName()); + velocityVariance = 0.0f; + } + if( velocityVariance > ejectionVelocity ) + { + Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) velocityVariance > ejectionVelocity", getName()); + velocityVariance = ejectionVelocity; + } + if( ejectionOffset < 0.0f ) + { + Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) ejectionOffset < 0", getName()); + ejectionOffset = 0.0f; + } + if( thetaMin < 0.0f ) + { + Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) thetaMin < 0.0", getName()); + thetaMin = 0.0f; + } + if( thetaMax > 180.0f ) + { + Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) thetaMax > 180.0", getName()); + thetaMax = 180.0f; + } + if( thetaMin > thetaMax ) + { + Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) thetaMin > thetaMax", getName()); + thetaMin = thetaMax; + } + if( phiVariance < 0.0f || phiVariance > 360.0f ) + { + Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) invalid phiVariance", getName()); + phiVariance = phiVariance < 0.0f ? 0.0f : 360.0f; + } + + if ( softnessDistance < 0.0f ) + { + Con::warnf( ConsoleLogEntry::General, "ParticleEmitterData(%s) invalid softnessDistance", getName() ); + softnessDistance = 0.0f; + } + + if (particleString == NULL && dataBlockIds.size() == 0) + { + Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) no particleString, invalid datablock", getName()); + return false; + } + if (particleString && particleString[0] == '\0') + { + Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) no particleString, invalid datablock", getName()); + return false; + } + if (particleString && dStrlen(particleString) > 255) + { + Con::errorf(ConsoleLogEntry::General, "ParticleEmitterData(%s) particle string too long [> 255 chars]", getName()); + return false; + } + + if( lifetimeMS < 0 ) + { + Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) lifetimeMS < 0.0f", getName()); + lifetimeMS = 0; + } + if( lifetimeVarianceMS > lifetimeMS ) + { + Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) lifetimeVarianceMS >= lifetimeMS", getName()); + lifetimeVarianceMS = lifetimeMS; + } + + + // load the particle datablocks... + // + if( particleString != NULL ) + { + // particleString is once again a list of particle datablocks so it + // must be parsed to extract the particle references. + + // First we parse particleString into a list of particle name tokens + Vector dataBlocks(__FILE__, __LINE__); + char* tokCopy = new char[dStrlen(particleString) + 1]; + dStrcpy(tokCopy, particleString); + + char* currTok = dStrtok(tokCopy, " \t"); + while (currTok != NULL) + { + dataBlocks.push_back(currTok); + currTok = dStrtok(NULL, " \t"); + } + if (dataBlocks.size() == 0) + { + Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) invalid particles string. No datablocks found", getName()); + delete [] tokCopy; + return false; + } + + // Now we convert the particle name tokens into particle datablocks and IDs + particleDataBlocks.clear(); + dataBlockIds.clear(); + + for (U32 i = 0; i < dataBlocks.size(); i++) + { + ParticleData* pData = NULL; + if (Sim::findObject(dataBlocks[i], pData) == false) + { + Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) unable to find particle datablock: %s", getName(), dataBlocks[i]); + } + else + { + particleDataBlocks.push_back(pData); + dataBlockIds.push_back(pData->getId()); + } + } + + // cleanup + delete [] tokCopy; + + // check that we actually found some particle datablocks + if (particleDataBlocks.size() == 0) + { + Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) unable to find any particle datablocks", getName()); + return false; + } + } + + return true; +} + +//----------------------------------------------------------------------------- +// preload +//----------------------------------------------------------------------------- +bool ParticleEmitterData::preload(bool server, String &errorStr) +{ + if( Parent::preload(server, errorStr) == false ) + return false; + + particleDataBlocks.clear(); + for (U32 i = 0; i < dataBlockIds.size(); i++) + { + ParticleData* pData = NULL; + if (Sim::findObject(dataBlockIds[i], pData) == false) + Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) unable to find particle datablock: %d", getName(), dataBlockIds[i]); + else + particleDataBlocks.push_back(pData); + } + + if (!server) + { + // load emitter texture if specified + if (textureName && textureName[0]) + { + textureHandle = GFXTexHandle(textureName, &GFXDefaultStaticDiffuseProfile, avar("%s() - textureHandle (line %d)", __FUNCTION__, __LINE__)); + if (!textureHandle) + { + errorStr = String::ToString("Missing particle emitter texture: %s", textureName); + return false; + } + } + // otherwise, check that all particles refer to the same texture + else if (particleDataBlocks.size() > 1) + { + StringTableEntry txr_name = particleDataBlocks[0]->textureName; + for (S32 i = 1; i < particleDataBlocks.size(); i++) + { + // warn if particle textures are inconsistent + if (particleDataBlocks[i]->textureName != txr_name) + { + Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) particles reference different textures.", getName()); + break; + } + } + } + } + + // if blend-style is undefined check legacy useInvAlpha settings + if (blendStyle == ParticleRenderInst::BlendUndefined && particleDataBlocks.size() > 0) + { + bool useInvAlpha = particleDataBlocks[0]->useInvAlpha; + for (S32 i = 1; i < particleDataBlocks.size(); i++) + { + // warn if blend-style legacy useInvAlpha settings are inconsistent + if (particleDataBlocks[i]->useInvAlpha != useInvAlpha) + { + Con::warnf(ConsoleLogEntry::General, "ParticleEmitterData(%s) particles have inconsistent useInvAlpha settings.", getName()); + break; + } + } + blendStyle = (useInvAlpha) ? ParticleRenderInst::BlendNormal : ParticleRenderInst::BlendAdditive; + } + + if( !server ) + { + allocPrimBuffer(); + } + + return true; +} + +//----------------------------------------------------------------------------- +// alloc PrimitiveBuffer +// The datablock allocates this static index buffer because it's the same +// for all of the emitters - each particle quad uses the same index ordering +//----------------------------------------------------------------------------- +void ParticleEmitterData::allocPrimBuffer( S32 overrideSize ) +{ + // calculate particle list size + AssertFatal(particleDataBlocks.size() > 0, "Error, no particles found." ); + U32 maxPartLife = particleDataBlocks[0]->lifetimeMS + particleDataBlocks[0]->lifetimeVarianceMS; + for (S32 i = 1; i < particleDataBlocks.size(); i++) + { + U32 mpl = particleDataBlocks[i]->lifetimeMS + particleDataBlocks[i]->lifetimeVarianceMS; + if (mpl > maxPartLife) + maxPartLife = mpl; + } + + partListInitSize = maxPartLife / (ejectionPeriodMS - periodVarianceMS); + partListInitSize += 8; // add 8 as "fudge factor" to make sure it doesn't realloc if it goes over by 1 + + // if override size is specified, then the emitter overran its buffer and needs a larger allocation + if( overrideSize != -1 ) + { + partListInitSize = overrideSize; + } + + // create index buffer based on that size + U32 indexListSize = partListInitSize * 6; // 6 indices per particle + U16 *indices = new U16[ indexListSize ]; + + for( U32 i=0; i( Sim::findObject( "ClientMissionCleanup") ); + if( cleanup != NULL ) + { + cleanup->addObject( this ); + } + else + { + AssertFatal( false, "Error, could not find ClientMissionCleanup group" ); + return false; + } + + removeFromProcessList(); + + F32 radius = 5.0; + mObjBox.minExtents = Point3F(-radius, -radius, -radius); + mObjBox.maxExtents = Point3F(radius, radius, radius); + resetWorldBox(); + + return true; +} + + +//----------------------------------------------------------------------------- +// onRemove +//----------------------------------------------------------------------------- +void ParticleEmitter::onRemove() +{ + removeFromScene(); + Parent::onRemove(); +} + + +//----------------------------------------------------------------------------- +// onNewDataBlock +//----------------------------------------------------------------------------- +bool ParticleEmitter::onNewDataBlock( GameBaseData *dptr, bool reload ) +{ + mDataBlock = dynamic_cast( dptr ); + if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) ) + return false; + + mLifetimeMS = mDataBlock->lifetimeMS; + if( mDataBlock->lifetimeVarianceMS ) + { + mLifetimeMS += S32( gRandGen.randI() % (2 * mDataBlock->lifetimeVarianceMS + 1)) - S32(mDataBlock->lifetimeVarianceMS ); + } + + // Allocate particle structures and init the freelist. Member part_store + // is a Vector so that we can allocate more particles if partListInitSize + // turns out to be too small. + // + if (mDataBlock->partListInitSize > 0) + { + for( S32 i = 0; i < part_store.size(); i++ ) + { + delete [] part_store[i]; + } + part_store.clear(); + n_part_capacity = mDataBlock->partListInitSize; + Particle* store_block = new Particle[n_part_capacity]; + part_store.push_back(store_block); + part_freelist = store_block; + Particle* last_part = part_freelist; + Particle* part = last_part+1; + for( S32 i = 1; i < n_part_capacity; i++, part++, last_part++ ) + { + last_part->next = part; + } + store_block[n_part_capacity-1].next = NULL; + part_list_head.next = NULL; + n_parts = 0; + } + + scriptOnNewDataBlock(); + return true; +} + +//----------------------------------------------------------------------------- +// getCollectiveColor +//----------------------------------------------------------------------------- +ColorF ParticleEmitter::getCollectiveColor() +{ + U32 count = 0; + ColorF color = ColorF(0.0f, 0.0f, 0.0f); + + count = n_parts; + for( Particle* part = part_list_head.next; part != NULL; part = part->next ) + { + color += part->color; + } + + if(count > 0) + { + color /= F32(count); + } + + //if(color.red == 0.0f && color.green == 0.0f && color.blue == 0.0f) + // color = color; + + return color; +} + + +//----------------------------------------------------------------------------- +// prepRenderImage +//----------------------------------------------------------------------------- +void ParticleEmitter::prepRenderImage(SceneRenderState* state) +{ + if( state->isReflectPass() && !getDataBlock()->renderReflection ) + return; + + // Never render into shadows. + if (state->isShadowPass()) + return; + + PROFILE_SCOPE(ParticleEmitter_prepRenderImage); + + if ( mDead || + n_parts == 0 || + part_list_head.next == NULL ) + return; + + RenderPassManager *renderManager = state->getRenderPass(); + const Point3F &camPos = state->getCameraPosition(); + copyToVB( camPos, state->getAmbientLightColor() ); + + if (!mVertBuff.isValid()) + return; + + ParticleRenderInst *ri = renderManager->allocInst(); + + ri->vertBuff = &mVertBuff; + ri->primBuff = &getDataBlock()->primBuff; + ri->translucentSort = true; + ri->type = RenderPassManager::RIT_Particle; + ri->sortDistSq = getRenderWorldBox().getSqDistanceToPoint( camPos ); + + // Draw the system offscreen unless the highResOnly flag is set on the datablock + ri->systemState = ( getDataBlock()->highResOnly ? PSS_AwaitingHighResDraw : PSS_AwaitingOffscreenDraw ); + + ri->modelViewProj = renderManager->allocUniqueXform( GFX->getProjectionMatrix() * + GFX->getViewMatrix() * + GFX->getWorldMatrix() ); + + // Update position on the matrix before multiplying it + mBBObjToWorld.setPosition(mLastPosition); + + ri->bbModelViewProj = renderManager->allocUniqueXform( *ri->modelViewProj * mBBObjToWorld ); + + ri->count = n_parts; + + ri->blendStyle = mDataBlock->blendStyle; + + // use first particle's texture unless there is an emitter texture to override it + if (mDataBlock->textureHandle) + ri->diffuseTex = &*(mDataBlock->textureHandle); + else + ri->diffuseTex = &*(part_list_head.next->dataBlock->textureHandle); + + ri->softnessDistance = mDataBlock->softnessDistance; + + // Sort by texture too. + ri->defaultKey = ri->diffuseTex ? (U32)ri->diffuseTex : (U32)ri->vertBuff; + + renderManager->addInst( ri ); + +} + +//----------------------------------------------------------------------------- +// setSizes +//----------------------------------------------------------------------------- +void ParticleEmitter::setSizes( F32 *sizeList ) +{ + for( int i=0; i 0 && mElapsedTimeMS > mLifetimeMS ) + { + return; + } + + Point3F realStart; + if( useLastPosition && mHasLastPosition ) + realStart = mLastPosition; + else + realStart = point; + + emitParticles(realStart, point, + axis, + velocity, + numMilliseconds); +} + +//----------------------------------------------------------------------------- +// emitParticles +//----------------------------------------------------------------------------- +void ParticleEmitter::emitParticles(const Point3F& start, + const Point3F& end, + const Point3F& axis, + const Point3F& velocity, + const U32 numMilliseconds) +{ + if( mDead ) return; + + if( mDataBlock->particleDataBlocks.empty() ) + return; + + // lifetime over - no more particles + if( mLifetimeMS > 0 && mElapsedTimeMS > mLifetimeMS ) + { + return; + } + + U32 currTime = 0; + bool particlesAdded = false; + + Point3F axisx; + if( mFabs(axis.z) < 0.9f ) + mCross(axis, Point3F(0, 0, 1), &axisx); + else + mCross(axis, Point3F(0, 1, 0), &axisx); + axisx.normalize(); + + if( mNextParticleTime != 0 ) + { + // Need to handle next particle + // + if( mNextParticleTime > numMilliseconds ) + { + // Defer to next update + // (Note that this introduces a potential spatial irregularity if the owning + // object is accelerating, and updating at a low frequency) + // + mNextParticleTime -= numMilliseconds; + mInternalClock += numMilliseconds; + mLastPosition = end; + mHasLastPosition = true; + return; + } + else + { + currTime += mNextParticleTime; + mInternalClock += mNextParticleTime; + // Emit particle at curr time + + // Create particle at the correct position + Point3F pos; + pos.interpolate(start, end, F32(currTime) / F32(numMilliseconds)); + addParticle(pos, axis, velocity, axisx); + particlesAdded = true; + mNextParticleTime = 0; + } + } + + while( currTime < numMilliseconds ) + { + S32 nextTime = mDataBlock->ejectionPeriodMS; + if( mDataBlock->periodVarianceMS != 0 ) + { + nextTime += S32(gRandGen.randI() % (2 * mDataBlock->periodVarianceMS + 1)) - + S32(mDataBlock->periodVarianceMS); + } + AssertFatal(nextTime > 0, "Error, next particle ejection time must always be greater than 0"); + + if( currTime + nextTime > numMilliseconds ) + { + mNextParticleTime = (currTime + nextTime) - numMilliseconds; + mInternalClock += numMilliseconds - currTime; + AssertFatal(mNextParticleTime > 0, "Error, should not have deferred this particle!"); + break; + } + + currTime += nextTime; + mInternalClock += nextTime; + + // Create particle at the correct position + Point3F pos; + pos.interpolate(start, end, F32(currTime) / F32(numMilliseconds)); + addParticle(pos, axis, velocity, axisx); + particlesAdded = true; + + // This override-advance code is restored in order to correctly adjust + // animated parameters of particles allocated within the same frame + // update. Note that ordering is important and this code correctly + // adds particles in the same newest-to-oldest ordering of the link-list. + // + // NOTE: We are assuming that the just added particle is at the head of our + // list. If that changes, so must this... + U32 advanceMS = numMilliseconds - currTime; + if (mDataBlock->overrideAdvance == false && advanceMS != 0) + { + Particle* last_part = part_list_head.next; + if (advanceMS > last_part->totalLifetime) + { + part_list_head.next = last_part->next; + n_parts--; + last_part->next = part_freelist; + part_freelist = last_part; + } + else + { + if (advanceMS != 0) + { + F32 t = F32(advanceMS) / 1000.0; + + Point3F a = last_part->acc; + a -= last_part->vel * last_part->dataBlock->dragCoefficient; + a -= mWindVelocity * last_part->dataBlock->windCoefficient; + a += Point3F(0.0f, 0.0f, -9.81f) * last_part->dataBlock->gravityCoefficient; + + last_part->vel += a * t; + last_part->pos += last_part->vel * t; + + updateKeyData( last_part ); + } + } + } + } + + // DMMFIX: Lame and slow... + if( particlesAdded == true ) + updateBBox(); + + + if( n_parts > 0 && getSceneManager() == NULL ) + { + gClientSceneGraph->addObjectToScene(this); + ClientProcessList::get()->addObject(this); + } + + mLastPosition = end; + mHasLastPosition = true; +} + +//----------------------------------------------------------------------------- +// emitParticles +//----------------------------------------------------------------------------- +void ParticleEmitter::emitParticles(const Point3F& rCenter, + const Point3F& rNormal, + const F32 radius, + const Point3F& velocity, + S32 count) +{ + if( mDead ) return; + + // lifetime over - no more particles + if( mLifetimeMS > 0 && mElapsedTimeMS > mLifetimeMS ) + { + return; + } + + + Point3F axisx, axisy; + Point3F axisz = rNormal; + + if( axisz.isZero() ) + { + axisz.set( 0.0, 0.0, 1.0 ); + } + + if( mFabs(axisz.z) < 0.98 ) + { + mCross(axisz, Point3F(0, 0, 1), &axisy); + axisy.normalize(); + } + else + { + mCross(axisz, Point3F(0, 1, 0), &axisy); + axisy.normalize(); + } + mCross(axisz, axisy, &axisx); + axisx.normalize(); + + // Should think of a better way to distribute the + // particles within the hemisphere. + for( S32 i = 0; i < count; i++ ) + { + Point3F pos = axisx * (radius * (1 - (2 * gRandGen.randF()))); + pos += axisy * (radius * (1 - (2 * gRandGen.randF()))); + pos += axisz * (radius * gRandGen.randF()); + + Point3F axis = pos; + axis.normalize(); + pos += rCenter; + + addParticle(pos, axis, velocity, axisz); + } + + // Set world bounding box + mObjBox.minExtents = rCenter - Point3F(radius, radius, radius); + mObjBox.maxExtents = rCenter + Point3F(radius, radius, radius); + resetWorldBox(); + + // Make sure we're part of the world + if( n_parts > 0 && getSceneManager() == NULL ) + { + gClientSceneGraph->addObjectToScene(this); + ClientProcessList::get()->addObject(this); + } + + mHasLastPosition = false; +} + +//----------------------------------------------------------------------------- +// updateBBox - SLOW, bad news +//----------------------------------------------------------------------------- +void ParticleEmitter::updateBBox() +{ + Point3F minPt(1e10, 1e10, 1e10); + Point3F maxPt(-1e10, -1e10, -1e10); + + for (Particle* part = part_list_head.next; part != NULL; part = part->next) + { + Point3F particleSize(part->size * 0.5f, 0.0f, part->size * 0.5f); + minPt.setMin( part->pos - particleSize ); + maxPt.setMax( part->pos + particleSize ); + } + + mObjBox = Box3F(minPt, maxPt); + MatrixF temp = getTransform(); + setTransform(temp); + + mBBObjToWorld.identity(); + Point3F boxScale = mObjBox.getExtents(); + boxScale.x = getMax(boxScale.x, 1.0f); + boxScale.y = getMax(boxScale.y, 1.0f); + boxScale.z = getMax(boxScale.z, 1.0f); + mBBObjToWorld.scale(boxScale); +} + +//----------------------------------------------------------------------------- +// addParticle +//----------------------------------------------------------------------------- +void ParticleEmitter::addParticle(const Point3F& pos, + const Point3F& axis, + const Point3F& vel, + const Point3F& axisx) +{ + n_parts++; + if (n_parts > n_part_capacity || n_parts > mDataBlock->partListInitSize) + { + // In an emergency we allocate additional particles in blocks of 16. + // This should happen rarely. + Particle* store_block = new Particle[16]; + part_store.push_back(store_block); + n_part_capacity += 16; + for (S32 i = 0; i < 16; i++) + { + store_block[i].next = part_freelist; + part_freelist = &store_block[i]; + } + mDataBlock->allocPrimBuffer(n_part_capacity); // allocate larger primitive buffer or will crash + } + Particle* pNew = part_freelist; + part_freelist = pNew->next; + pNew->next = part_list_head.next; + part_list_head.next = pNew; + + Point3F ejectionAxis = axis; + F32 theta = (mDataBlock->thetaMax - mDataBlock->thetaMin) * gRandGen.randF() + + mDataBlock->thetaMin; + + F32 ref = (F32(mInternalClock) / 1000.0) * mDataBlock->phiReferenceVel; + F32 phi = ref + gRandGen.randF() * mDataBlock->phiVariance; + + // Both phi and theta are in degs. Create axis angles out of them, and create the + // appropriate rotation matrix... + AngAxisF thetaRot(axisx, theta * (M_PI / 180.0)); + AngAxisF phiRot(axis, phi * (M_PI / 180.0)); + + MatrixF temp(true); + thetaRot.setMatrix(&temp); + temp.mulP(ejectionAxis); + phiRot.setMatrix(&temp); + temp.mulP(ejectionAxis); + + F32 initialVel = mDataBlock->ejectionVelocity; + initialVel += (mDataBlock->velocityVariance * 2.0f * gRandGen.randF()) - mDataBlock->velocityVariance; + + pNew->pos = pos + (ejectionAxis * mDataBlock->ejectionOffset); + pNew->vel = ejectionAxis * initialVel; + pNew->orientDir = ejectionAxis; + pNew->acc.set(0, 0, 0); + pNew->currentAge = 0; + + // Choose a new particle datablack randomly from the list + U32 dBlockIndex = gRandGen.randI() % mDataBlock->particleDataBlocks.size(); + mDataBlock->particleDataBlocks[dBlockIndex]->initializeParticle(pNew, vel); + updateKeyData( pNew ); + +} + + +//----------------------------------------------------------------------------- +// processTick +//----------------------------------------------------------------------------- +void ParticleEmitter::processTick(const Move*) +{ + if( mDeleteOnTick == true ) + { + mDead = true; + deleteObject(); + } +} + + +//----------------------------------------------------------------------------- +// advanceTime +//----------------------------------------------------------------------------- +void ParticleEmitter::advanceTime(F32 dt) +{ + if( dt < 0.00001 ) return; + + Parent::advanceTime(dt); + + if( dt > 0.5 ) dt = 0.5; + + if( mDead ) return; + + mElapsedTimeMS += (S32)(dt * 1000.0f); + + U32 numMSToUpdate = (U32)(dt * 1000.0f); + if( numMSToUpdate == 0 ) return; + + // TODO: Prefetch + + // remove dead particles + Particle* last_part = &part_list_head; + for (Particle* part = part_list_head.next; part != NULL; part = part->next) + { + part->currentAge += numMSToUpdate; + if (part->currentAge > part->totalLifetime) + { + n_parts--; + last_part->next = part->next; + part->next = part_freelist; + part_freelist = part; + part = last_part; + } + else + { + last_part = part; + } + } + + AssertFatal( n_parts >= 0, "ParticleEmitter: negative part count!" ); + + if (n_parts < 1 && mDeleteWhenEmpty) + { + mDeleteOnTick = true; + return; + } + + if( numMSToUpdate != 0 && n_parts > 0 ) + { + update( numMSToUpdate ); + } +} + +//----------------------------------------------------------------------------- +// Update key related particle data +//----------------------------------------------------------------------------- +void ParticleEmitter::updateKeyData( Particle *part ) +{ + //Ensure that our lifetime is never below 0 + if( part->totalLifetime < 1 ) + part->totalLifetime = 1; + + F32 t = F32(part->currentAge) / F32(part->totalLifetime); + AssertFatal(t <= 1.0f, "Out out bounds filter function for particle."); + + for( U32 i = 1; i < ParticleData::PDC_NUM_KEYS; i++ ) + { + if( part->dataBlock->times[i] >= t ) + { + F32 firstPart = t - part->dataBlock->times[i-1]; + F32 total = part->dataBlock->times[i] - + part->dataBlock->times[i-1]; + + firstPart /= total; + + if( mDataBlock->useEmitterColors ) + { + part->color.interpolate(colors[i-1], colors[i], firstPart); + } + else + { + part->color.interpolate(part->dataBlock->colors[i-1], + part->dataBlock->colors[i], + firstPart); + } + + if( mDataBlock->useEmitterSizes ) + { + part->size = (sizes[i-1] * (1.0 - firstPart)) + + (sizes[i] * firstPart); + } + else + { + part->size = (part->dataBlock->sizes[i-1] * (1.0 - firstPart)) + + (part->dataBlock->sizes[i] * firstPart); + } + break; + + } + } +} + +//----------------------------------------------------------------------------- +// Update particles +//----------------------------------------------------------------------------- +void ParticleEmitter::update( U32 ms ) +{ + // TODO: Prefetch + + for (Particle* part = part_list_head.next; part != NULL; part = part->next) + { + F32 t = F32(ms) / 1000.0; + + Point3F a = part->acc; + a -= part->vel * part->dataBlock->dragCoefficient; + a -= mWindVelocity * part->dataBlock->windCoefficient; + a += Point3F(0.0f, 0.0f, -9.81f) * part->dataBlock->gravityCoefficient; + + part->vel += a * t; + part->pos += part->vel * t; + + updateKeyData( part ); + } +} + +//----------------------------------------------------------------------------- +// Copy particles to vertex buffer +//----------------------------------------------------------------------------- + +// structure used for particle sorting. +struct SortParticle +{ + Particle* p; + F32 k; +}; + +// qsort callback function for particle sorting +int QSORT_CALLBACK cmpSortParticles(const void* p1, const void* p2) +{ + const SortParticle* sp1 = (const SortParticle*)p1; + const SortParticle* sp2 = (const SortParticle*)p2; + + if (sp2->k > sp1->k) + return 1; + else if (sp2->k == sp1->k) + return 0; + else + return -1; +} + +void ParticleEmitter::copyToVB( const Point3F &camPos, const ColorF &ambientColor ) +{ + static Vector orderedVector(__FILE__, __LINE__); + + PROFILE_START(ParticleEmitter_copyToVB); + + PROFILE_START(ParticleEmitter_copyToVB_Sort); + // build sorted list of particles (far to near) + if (mDataBlock->sortParticles) + { + orderedVector.clear(); + + MatrixF modelview = GFX->getWorldMatrix(); + Point3F viewvec; modelview.getRow(1, &viewvec); + + // add each particle and a distance based sort key to orderedVector + for (Particle* pp = part_list_head.next; pp != NULL; pp = pp->next) + { + orderedVector.increment(); + orderedVector.last().p = pp; + orderedVector.last().k = mDot(pp->pos, viewvec); + } + + // qsort the list into far to near ordering + dQsort(orderedVector.address(), orderedVector.size(), sizeof(SortParticle), cmpSortParticles); + } + PROFILE_END(); + +#if defined(TORQUE_OS_XENON) + // Allocate writecombined since we don't read back from this buffer (yay!) + if(mVertBuff.isNull()) + mVertBuff = new GFX360MemVertexBuffer(GFX, 1, getGFXVertexFormat(), sizeof(ParticleVertexType), GFXBufferTypeDynamic, PAGE_WRITECOMBINE); + if( n_parts > mCurBuffSize ) + { + mCurBuffSize = n_parts; + mVertBuff.resize(n_parts * 4); + } + + ParticleVertexType *buffPtr = mVertBuff.lock(); +#else + static Vector tempBuff(2048); + tempBuff.reserve( n_parts*4 + 64); // make sure tempBuff is big enough + ParticleVertexType *buffPtr = tempBuff.address(); // use direct pointer (faster) +#endif + + if (mDataBlock->orientParticles) + { + PROFILE_START(ParticleEmitter_copyToVB_Orient); + + if (mDataBlock->reverseOrder) + { + buffPtr += 4*(n_parts-1); + // do sorted-oriented particles + if (mDataBlock->sortParticles) + { + SortParticle* partPtr = orderedVector.address(); + for (U32 i = 0; i < n_parts; i++, partPtr++, buffPtr-=4 ) + setupOriented(partPtr->p, camPos, ambientColor, buffPtr); + } + // do unsorted-oriented particles + else + { + for (Particle* partPtr = part_list_head.next; partPtr != NULL; partPtr = partPtr->next, buffPtr-=4) + setupOriented(partPtr, camPos, ambientColor, buffPtr); + } + } + else + { + // do sorted-oriented particles + if (mDataBlock->sortParticles) + { + SortParticle* partPtr = orderedVector.address(); + for (U32 i = 0; i < n_parts; i++, partPtr++, buffPtr+=4 ) + setupOriented(partPtr->p, camPos, ambientColor, buffPtr); + } + // do unsorted-oriented particles + else + { + for (Particle* partPtr = part_list_head.next; partPtr != NULL; partPtr = partPtr->next, buffPtr+=4) + setupOriented(partPtr, camPos, ambientColor, buffPtr); + } + } + PROFILE_END(); + } + else if (mDataBlock->alignParticles) + { + PROFILE_START(ParticleEmitter_copyToVB_Aligned); + + if (mDataBlock->reverseOrder) + { + buffPtr += 4*(n_parts-1); + + // do sorted-oriented particles + if (mDataBlock->sortParticles) + { + SortParticle* partPtr = orderedVector.address(); + for (U32 i = 0; i < n_parts; i++, partPtr++, buffPtr-=4 ) + setupAligned(partPtr->p, ambientColor, buffPtr); + } + // do unsorted-oriented particles + else + { + Particle *partPtr = part_list_head.next; + for (; partPtr != NULL; partPtr = partPtr->next, buffPtr-=4) + setupAligned(partPtr, ambientColor, buffPtr); + } + } + else + { + // do sorted-oriented particles + if (mDataBlock->sortParticles) + { + SortParticle* partPtr = orderedVector.address(); + for (U32 i = 0; i < n_parts; i++, partPtr++, buffPtr+=4 ) + setupAligned(partPtr->p, ambientColor, buffPtr); + } + // do unsorted-oriented particles + else + { + Particle *partPtr = part_list_head.next; + for (; partPtr != NULL; partPtr = partPtr->next, buffPtr+=4) + setupAligned(partPtr, ambientColor, buffPtr); + } + } + PROFILE_END(); + } + else + { + PROFILE_START(ParticleEmitter_copyToVB_NonOriented); + // somewhat odd ordering so that texture coordinates match the oriented + // particles + Point3F basePoints[4]; + basePoints[0] = Point3F(-1.0, 0.0, 1.0); + basePoints[1] = Point3F(-1.0, 0.0, -1.0); + basePoints[2] = Point3F( 1.0, 0.0, -1.0); + basePoints[3] = Point3F( 1.0, 0.0, 1.0); + + MatrixF camView = GFX->getWorldMatrix(); + camView.transpose(); // inverse - this gets the particles facing camera + + if (mDataBlock->reverseOrder) + { + buffPtr += 4*(n_parts-1); + // do sorted-billboard particles + if (mDataBlock->sortParticles) + { + SortParticle *partPtr = orderedVector.address(); + for( U32 i=0; ip, basePoints, camView, ambientColor, buffPtr ); + } + // do unsorted-billboard particles + else + { + for (Particle* partPtr = part_list_head.next; partPtr != NULL; partPtr = partPtr->next, buffPtr-=4) + setupBillboard( partPtr, basePoints, camView, ambientColor, buffPtr ); + } + } + else + { + // do sorted-billboard particles + if (mDataBlock->sortParticles) + { + SortParticle *partPtr = orderedVector.address(); + for( U32 i=0; ip, basePoints, camView, ambientColor, buffPtr ); + } + // do unsorted-billboard particles + else + { + for (Particle* partPtr = part_list_head.next; partPtr != NULL; partPtr = partPtr->next, buffPtr+=4) + setupBillboard( partPtr, basePoints, camView, ambientColor, buffPtr ); + } + } + + PROFILE_END(); + } + +#if defined(TORQUE_OS_XENON) + mVertBuff.unlock(); +#else + PROFILE_START(ParticleEmitter_copyToVB_LockCopy); + // create new VB if emitter size grows + if( !mVertBuff || n_parts > mCurBuffSize ) + { + mCurBuffSize = n_parts; + mVertBuff.set( GFX, n_parts * 4, GFXBufferTypeDynamic ); + } + // lock and copy tempBuff to video RAM + ParticleVertexType *verts = mVertBuff.lock(); + dMemcpy( verts, tempBuff.address(), n_parts * 4 * sizeof(ParticleVertexType) ); + mVertBuff.unlock(); + PROFILE_END(); +#endif + + PROFILE_END(); +} + +//----------------------------------------------------------------------------- +// Set up particle for billboard style render +//----------------------------------------------------------------------------- +void ParticleEmitter::setupBillboard( Particle *part, + Point3F *basePts, + const MatrixF &camView, + const ColorF &ambientColor, + ParticleVertexType *lVerts ) +{ + F32 width = part->size * 0.5f; + F32 spinAngle = part->spinSpeed * part->currentAge * AgedSpinToRadians; + + F32 sy, cy; + mSinCos(spinAngle, sy, cy); + + const F32 ambientLerp = mClampF( mDataBlock->ambientFactor, 0.0f, 1.0f ); + ColorF partCol = mLerp( part->color, ( part->color * ambientColor ), ambientLerp ); + + // fill four verts, use macro and unroll loop + #define fillVert(){ \ + lVerts->point.x = cy * basePts->x - sy * basePts->z; \ + lVerts->point.y = 0.0f; \ + lVerts->point.z = sy * basePts->x + cy * basePts->z; \ + camView.mulV( lVerts->point ); \ + lVerts->point *= width; \ + lVerts->point += part->pos; \ + lVerts->color = partCol; } \ + + // Here we deal with UVs for animated particle (billboard) + if (part->dataBlock->animateTexture) + { + S32 fm = (S32)(part->currentAge*(1.0/1000.0)*part->dataBlock->framesPerSec); + U8 fm_tile = part->dataBlock->animTexFrames[fm % part->dataBlock->numFrames]; + S32 uv[4]; + uv[0] = fm_tile + fm_tile/part->dataBlock->animTexTiling.x; + uv[1] = uv[0] + (part->dataBlock->animTexTiling.x + 1); + uv[2] = uv[1] + 1; + uv[3] = uv[0] + 1; + + fillVert(); + // Here and below, we copy UVs from particle datablock's current frame's UVs (billboard) + lVerts->texCoord = part->dataBlock->animTexUVs[uv[0]]; + ++lVerts; + ++basePts; + + fillVert(); + lVerts->texCoord = part->dataBlock->animTexUVs[uv[1]]; + ++lVerts; + ++basePts; + + fillVert(); + lVerts->texCoord = part->dataBlock->animTexUVs[uv[2]]; + ++lVerts; + ++basePts; + + fillVert(); + lVerts->texCoord = part->dataBlock->animTexUVs[uv[3]]; + ++lVerts; + ++basePts; + + return; + } + + fillVert(); + // Here and below, we copy UVs from particle datablock's texCoords (billboard) + lVerts->texCoord = part->dataBlock->texCoords[0]; + ++lVerts; + ++basePts; + + fillVert(); + lVerts->texCoord = part->dataBlock->texCoords[1]; + ++lVerts; + ++basePts; + + fillVert(); + lVerts->texCoord = part->dataBlock->texCoords[2]; + ++lVerts; + ++basePts; + + fillVert(); + lVerts->texCoord = part->dataBlock->texCoords[3]; + ++lVerts; + ++basePts; +} + +//----------------------------------------------------------------------------- +// Set up oriented particle +//----------------------------------------------------------------------------- +void ParticleEmitter::setupOriented( Particle *part, + const Point3F &camPos, + const ColorF &ambientColor, + ParticleVertexType *lVerts ) +{ + Point3F dir; + + if( mDataBlock->orientOnVelocity ) + { + // don't render oriented particle if it has no velocity + if( part->vel.magnitudeSafe() == 0.0 ) return; + dir = part->vel; + } + else + { + dir = part->orientDir; + } + + Point3F dirFromCam = part->pos - camPos; + Point3F crossDir; + mCross( dirFromCam, dir, &crossDir ); + crossDir.normalize(); + dir.normalize(); + + F32 width = part->size * 0.5f; + dir *= width; + crossDir *= width; + Point3F start = part->pos - dir; + Point3F end = part->pos + dir; + + const F32 ambientLerp = mClampF( mDataBlock->ambientFactor, 0.0f, 1.0f ); + ColorF partCol = mLerp( part->color, ( part->color * ambientColor ), ambientLerp ); + + // Here we deal with UVs for animated particle (oriented) + if (part->dataBlock->animateTexture) + { + // Let particle compute the UV indices for current frame + S32 fm = (S32)(part->currentAge*(1.0f/1000.0f)*part->dataBlock->framesPerSec); + U8 fm_tile = part->dataBlock->animTexFrames[fm % part->dataBlock->numFrames]; + S32 uv[4]; + uv[0] = fm_tile + fm_tile/part->dataBlock->animTexTiling.x; + uv[1] = uv[0] + (part->dataBlock->animTexTiling.x + 1); + uv[2] = uv[1] + 1; + uv[3] = uv[0] + 1; + + lVerts->point = start + crossDir; + lVerts->color = partCol; + // Here and below, we copy UVs from particle datablock's current frame's UVs (oriented) + lVerts->texCoord = part->dataBlock->animTexUVs[uv[0]]; + ++lVerts; + + lVerts->point = start - crossDir; + lVerts->color = partCol; + lVerts->texCoord = part->dataBlock->animTexUVs[uv[1]]; + ++lVerts; + + lVerts->point = end - crossDir; + lVerts->color = partCol; + lVerts->texCoord = part->dataBlock->animTexUVs[uv[2]]; + ++lVerts; + + lVerts->point = end + crossDir; + lVerts->color = partCol; + lVerts->texCoord = part->dataBlock->animTexUVs[uv[3]]; + ++lVerts; + + return; + } + + lVerts->point = start + crossDir; + lVerts->color = partCol; + // Here and below, we copy UVs from particle datablock's texCoords (oriented) + lVerts->texCoord = part->dataBlock->texCoords[0]; + ++lVerts; + + lVerts->point = start - crossDir; + lVerts->color = partCol; + lVerts->texCoord = part->dataBlock->texCoords[1]; + ++lVerts; + + lVerts->point = end - crossDir; + lVerts->color = partCol; + lVerts->texCoord = part->dataBlock->texCoords[2]; + ++lVerts; + + lVerts->point = end + crossDir; + lVerts->color = partCol; + lVerts->texCoord = part->dataBlock->texCoords[3]; + ++lVerts; +} + +void ParticleEmitter::setupAligned( const Particle *part, + const ColorF &ambientColor, + ParticleVertexType *lVerts ) +{ + // The aligned direction will always be normalized. + Point3F dir = mDataBlock->alignDirection; + + // Find a right vector for this particle. + Point3F right; + if (mFabs(dir.y) > mFabs(dir.z)) + mCross(Point3F::UnitZ, dir, &right); + else + mCross(Point3F::UnitY, dir, &right); + right.normalize(); + + // If we have a spin velocity. + if ( !mIsZero( part->spinSpeed ) ) + { + F32 spinAngle = part->spinSpeed * part->currentAge * AgedSpinToRadians; + + // This is an inline quaternion vector rotation which + // is faster that QuatF.mulP(), but generates different + // results and hence cannot replace it right now. + + F32 sin, qw; + mSinCos( spinAngle * 0.5f, sin, qw ); + F32 qx = dir.x * sin; + F32 qy = dir.y * sin; + F32 qz = dir.z * sin; + + F32 vx = ( right.x * qw ) + ( right.z * qy ) - ( right.y * qz ); + F32 vy = ( right.y * qw ) + ( right.x * qz ) - ( right.z * qx ); + F32 vz = ( right.z * qw ) + ( right.y * qx ) - ( right.x * qy ); + F32 vw = ( right.x * qx ) + ( right.y * qy ) + ( right.z * qz ); + + right.x = ( qw * vx ) + ( qx * vw ) + ( qy * vz ) - ( qz * vy ); + right.y = ( qw * vy ) + ( qy * vw ) + ( qz * vx ) - ( qx * vz ); + right.z = ( qw * vz ) + ( qz * vw ) + ( qx * vy ) - ( qy * vx ); + } + + // Get the cross vector. + Point3F cross; + mCross(right, dir, &cross); + + F32 width = part->size * 0.5f; + right *= width; + cross *= width; + Point3F start = part->pos - right; + Point3F end = part->pos + right; + + const F32 ambientLerp = mClampF( mDataBlock->ambientFactor, 0.0f, 1.0f ); + ColorF partCol = mLerp( part->color, ( part->color * ambientColor ), ambientLerp ); + + // Here we deal with UVs for animated particle + if (part->dataBlock->animateTexture) + { + // Let particle compute the UV indices for current frame + S32 fm = (S32)(part->currentAge*(1.0f/1000.0f)*part->dataBlock->framesPerSec); + U8 fm_tile = part->dataBlock->animTexFrames[fm % part->dataBlock->numFrames]; + S32 uv[4]; + uv[0] = fm_tile + fm_tile/part->dataBlock->animTexTiling.x; + uv[1] = uv[0] + (part->dataBlock->animTexTiling.x + 1); + uv[2] = uv[1] + 1; + uv[3] = uv[0] + 1; + + lVerts->point = start + cross; + lVerts->color = partCol; + lVerts->texCoord = part->dataBlock->animTexUVs[uv[0]]; + ++lVerts; + + lVerts->point = start - cross; + lVerts->color = partCol; + lVerts->texCoord = part->dataBlock->animTexUVs[uv[1]]; + ++lVerts; + + lVerts->point = end - cross; + lVerts->color = partCol; + lVerts->texCoord = part->dataBlock->animTexUVs[uv[2]]; + ++lVerts; + + lVerts->point = end + cross; + lVerts->color = partCol; + lVerts->texCoord = part->dataBlock->animTexUVs[uv[3]]; + ++lVerts; + } + else + { + // Here and below, we copy UVs from particle datablock's texCoords + lVerts->point = start + cross; + lVerts->color = partCol; + lVerts->texCoord = part->dataBlock->texCoords[0]; + ++lVerts; + + lVerts->point = start - cross; + lVerts->color = partCol; + lVerts->texCoord = part->dataBlock->texCoords[1]; + ++lVerts; + + lVerts->point = end - cross; + lVerts->color = partCol; + lVerts->texCoord = part->dataBlock->texCoords[2]; + ++lVerts; + + lVerts->point = end + cross; + lVerts->color = partCol; + lVerts->texCoord = part->dataBlock->texCoords[3]; + ++lVerts; + } +} + +bool ParticleEmitterData::reload() +{ + // Clear out current particle data. + + dataBlockIds.clear(); + particleDataBlocks.clear(); + + // Parse out particle string. + + U32 numUnits = 0; + if( particleString ) + numUnits = StringUnit::getUnitCount( particleString, " \t" ); + if( !particleString || !particleString[ 0 ] || !numUnits ) + { + Con::errorf( "ParticleEmitterData(%s) has an empty particles string.", getName() ); + mReloadSignal.trigger(); + return false; + } + + for( U32 i = 0; i < numUnits; ++ i ) + { + const char* dbName = StringUnit::getUnit( particleString, i, " \t" ); + + ParticleData* data = NULL; + if( !Sim::findObject( dbName, data ) ) + { + Con::errorf( ConsoleLogEntry::General, "ParticleEmitterData(%s) unable to find particle datablock: %s", getName(), dbName ); + continue; + } + + particleDataBlocks.push_back( data ); + dataBlockIds.push_back( data->getId() ); + } + + // Check that we actually found some particle datablocks. + + if( particleDataBlocks.empty() ) + { + Con::errorf( ConsoleLogEntry::General, "ParticleEmitterData(%s) unable to find any particle datablocks", getName() ); + mReloadSignal.trigger(); + return false; + } + + // Trigger reload. + + mReloadSignal.trigger(); + + return true; +} + +DefineEngineMethod(ParticleEmitterData, reload, void,(),, + "Reloads the ParticleData datablocks and other fields used by this emitter.\n" + "@tsexample\n" + "// Get the editor's current particle emitter\n" + "%emitter = PE_EmitterEditor.currEmitter\n\n" + "// Change a field value\n" + "%emitter.setFieldValue( %propertyField, %value );\n\n" + "// Reload this emitter\n" + "%emitter.reload();\n" + "@endtsexample\n") +{ + object->reload(); +} diff --git a/Engine/source/T3D/fx/particleEmitter.h b/Engine/source/T3D/fx/particleEmitter.h new file mode 100644 index 000000000..02aab525a --- /dev/null +++ b/Engine/source/T3D/fx/particleEmitter.h @@ -0,0 +1,287 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _H_PARTICLE_EMITTER +#define _H_PARTICLE_EMITTER + +#ifndef _GAMEBASE_H_ +#include "T3D/gameBase/gameBase.h" +#endif +#ifndef _COLOR_H_ +#include "core/color.h" +#endif +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif +#ifndef _GFXVERTEXBUFFER_H_ +#include "gfx/gfxVertexBuffer.h" +#endif +#ifndef _PARTICLE_H_ +#include "T3D/fx/particle.h" +#endif + +#if defined(TORQUE_OS_XENON) +#include "gfx/D3D9/360/gfx360MemVertexBuffer.h" +#endif + +class RenderPassManager; +class ParticleData; + +//***************************************************************************** +// Particle Emitter Data +//***************************************************************************** +class ParticleEmitterData : public GameBaseData +{ + typedef GameBaseData Parent; + + static bool _setAlignDirection( void *object, const char *index, const char *data ); + + public: + + ParticleEmitterData(); + DECLARE_CONOBJECT(ParticleEmitterData); + static void initPersistFields(); + void packData(BitStream* stream); + void unpackData(BitStream* stream); + bool preload(bool server, String &errorStr); + bool onAdd(); + void allocPrimBuffer( S32 overrideSize = -1 ); + + public: + S32 ejectionPeriodMS; ///< Time, in Milliseconds, between particle ejection + S32 periodVarianceMS; ///< Varience in ejection peroid between 0 and n + + F32 ejectionVelocity; ///< Ejection velocity + F32 velocityVariance; ///< Variance for velocity between 0 and n + F32 ejectionOffset; ///< Z offset from emitter point to eject from + + F32 thetaMin; ///< Minimum angle, from the horizontal plane, to eject from + F32 thetaMax; ///< Maximum angle, from the horizontal plane, to eject from + + F32 phiReferenceVel; ///< Reference angle, from the verticle plane, to eject from + F32 phiVariance; ///< Varience from the reference angle, from 0 to n + + F32 softnessDistance; ///< For soft particles, the distance (in meters) where particles will be faded + ///< based on the difference in depth between the particle and the scene geometry. + + /// A scalar value used to influence the effect + /// of the ambient color on the particle. + F32 ambientFactor; + + U32 lifetimeMS; ///< Lifetime of particles + U32 lifetimeVarianceMS; ///< Varience in lifetime from 0 to n + + bool overrideAdvance; ///< + bool orientParticles; ///< Particles always face the screen + bool orientOnVelocity; ///< Particles face the screen at the start + bool useEmitterSizes; ///< Use emitter specified sizes instead of datablock sizes + bool useEmitterColors; ///< Use emitter specified colors instead of datablock colors + bool alignParticles; ///< Particles always face along a particular axis + Point3F alignDirection; ///< The direction aligned particles should face + + StringTableEntry particleString; ///< Used to load particle data directly from a string + + Vector particleDataBlocks; ///< Particle Datablocks + Vector dataBlockIds; ///< Datablock IDs (parellel array to particleDataBlocks) + + U32 partListInitSize; /// initial size of particle list calc'd from datablock info + + GFXPrimitiveBufferHandle primBuff; + + S32 blendStyle; ///< Pre-define blend factor setting + bool sortParticles; ///< Particles are sorted back-to-front + bool reverseOrder; ///< reverses draw order + StringTableEntry textureName; ///< Emitter texture file to override particle textures + GFXTexHandle textureHandle; ///< Emitter texture handle from txrName + bool highResOnly; ///< This particle system should not use the mixed-resolution particle rendering + bool renderReflection; ///< Enables this emitter to render into reflection passes. + + bool reload(); +}; + +//***************************************************************************** +// Particle Emitter +//***************************************************************************** +class ParticleEmitter : public GameBase +{ + typedef GameBase Parent; + + public: + +#if defined(TORQUE_OS_XENON) + typedef GFXVertexPCTT ParticleVertexType; +#else + typedef GFXVertexPCT ParticleVertexType; +#endif + + ParticleEmitter(); + ~ParticleEmitter(); + + DECLARE_CONOBJECT(ParticleEmitter); + + static Point3F mWindVelocity; + static void setWindVelocity( const Point3F &vel ){ mWindVelocity = vel; } + + ColorF getCollectiveColor(); + + /// Sets sizes of particles based on sizelist provided + /// @param sizeList List of sizes + void setSizes( F32 *sizeList ); + + /// Sets colors for particles based on color list provided + /// @param colorList List of colors + void setColors( ColorF *colorList ); + + ParticleEmitterData *getDataBlock(){ return mDataBlock; } + bool onNewDataBlock( GameBaseData *dptr, bool reload ); + + /// By default, a particle renderer will wait for it's owner to delete it. When this + /// is turned on, it will delete itself as soon as it's particle count drops to zero. + void deleteWhenEmpty(); + + /// @name Particle Emission + /// Main interface for creating particles. The emitter does _not_ track changes + /// in axis or velocity over the course of a single update, so this should be called + /// at a fairly fine grain. The emitter will potentially track the last particle + /// to be created into the next call to this function in order to create a uniformly + /// random time distribution of the particles. If the object to which the emitter is + /// attached is in motion, it should try to ensure that for call (n+1) to this + /// function, start is equal to the end from call (n). This will ensure a uniform + /// spatial distribution. + /// @{ + + void emitParticles(const Point3F& start, + const Point3F& end, + const Point3F& axis, + const Point3F& velocity, + const U32 numMilliseconds); + void emitParticles(const Point3F& point, + const bool useLastPosition, + const Point3F& axis, + const Point3F& velocity, + const U32 numMilliseconds); + void emitParticles(const Point3F& rCenter, + const Point3F& rNormal, + const F32 radius, + const Point3F& velocity, + S32 count); + /// @} + + bool mDead; + + protected: + /// @name Internal interface + /// @{ + + /// Adds a particle + /// @param pos Initial position of particle + /// @param axis + /// @param vel Initial velocity + /// @param axisx + void addParticle(const Point3F &pos, const Point3F &axis, const Point3F &vel, const Point3F &axisx); + + + inline void setupBillboard( Particle *part, + Point3F *basePts, + const MatrixF &camView, + const ColorF &ambientColor, + ParticleVertexType *lVerts ); + + inline void setupOriented( Particle *part, + const Point3F &camPos, + const ColorF &ambientColor, + ParticleVertexType *lVerts ); + + inline void setupAligned( const Particle *part, + const ColorF &ambientColor, + ParticleVertexType *lVerts ); + + /// Updates the bounding box for the particle system + void updateBBox(); + + /// @} + protected: + bool onAdd(); + void onRemove(); + + void processTick(const Move *move); + void advanceTime(F32 dt); + + // Rendering + protected: + void prepRenderImage( SceneRenderState *state ); + void copyToVB( const Point3F &camPos, const ColorF &ambientColor ); + + // PEngine interface + private: + + void update( U32 ms ); + inline void updateKeyData( Particle *part ); + + + private: + + /// Constant used to calculate particle + /// rotation from spin and age. + static const F32 AgedSpinToRadians; + + ParticleEmitterData* mDataBlock; + + U32 mInternalClock; + + U32 mNextParticleTime; + + Point3F mLastPosition; + bool mHasLastPosition; + MatrixF mBBObjToWorld; + + bool mDeleteWhenEmpty; + bool mDeleteOnTick; + + S32 mLifetimeMS; + S32 mElapsedTimeMS; + + F32 sizes[ ParticleData::PDC_NUM_KEYS ]; + ColorF colors[ ParticleData::PDC_NUM_KEYS ]; + +#if defined(TORQUE_OS_XENON) + GFX360MemVertexBufferHandle mVertBuff; +#else + GFXVertexBufferHandle mVertBuff; +#endif + + // These members are for implementing a link-list of the active emitter + // particles. Member part_store contains blocks of particles that can be + // chained in a link-list. Usually the first part_store block is large + // enough to contain all the particles but it can be expanded in emergency + // circumstances. + Vector part_store; + Particle* part_freelist; + Particle part_list_head; + S32 n_part_capacity; + S32 n_parts; + S32 mCurBuffSize; + +}; + +#endif // _H_PARTICLE_EMITTER + diff --git a/Engine/source/T3D/fx/particleEmitterNode.cpp b/Engine/source/T3D/fx/particleEmitterNode.cpp new file mode 100644 index 000000000..41e6b2192 --- /dev/null +++ b/Engine/source/T3D/fx/particleEmitterNode.cpp @@ -0,0 +1,419 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "particleEmitterNode.h" +#include "console/consoleTypes.h" +#include "core/stream/bitStream.h" +#include "T3D/fx/particleEmitter.h" +#include "math/mathIO.h" +#include "sim/netConnection.h" +#include "console/engineAPI.h" + +IMPLEMENT_CO_DATABLOCK_V1(ParticleEmitterNodeData); +IMPLEMENT_CO_NETOBJECT_V1(ParticleEmitterNode); + +ConsoleDocClass( ParticleEmitterNodeData, + "@brief Contains additional data to be associated with a ParticleEmitterNode." + "@ingroup FX\n" +); + +ConsoleDocClass( ParticleEmitterNode, + "@brief A particle emitter object that can be positioned in the world and " + "dynamically enabled or disabled.\n\n" + + "@tsexample\n" + "datablock ParticleEmitterNodeData( SimpleEmitterNodeData )\n" + "{\n" + " timeMultiple = 1.0;\n" + "};\n\n" + + "%emitter = new ParticleEmitterNode()\n" + "{\n" + " datablock = SimpleEmitterNodeData;\n" + " active = true;\n" + " emitter = FireEmitterData;\n" + " velocity = 3.5;\n" + "};\n\n" + + "// Dynamically change emitter datablock\n" + "%emitter.setEmitterDataBlock( DustEmitterData );\n" + "@endtsexample\n" + + "@note To change the emitter field dynamically (after the ParticleEmitterNode " + "object has been created) you must use the setEmitterDataBlock() method or the " + "change will not be replicated to other clients in the game.\n" + "Similarly, use the setActive() method instead of changing the active field " + "directly. When changing velocity, you need to toggle setActive() on and off " + "to force the state change to be transmitted to other clients.\n\n" + + "@ingroup FX\n" + "@see ParticleEmitterNodeData\n" + "@see ParticleEmitterData\n" +); + + +//----------------------------------------------------------------------------- +// ParticleEmitterNodeData +//----------------------------------------------------------------------------- +ParticleEmitterNodeData::ParticleEmitterNodeData() +{ + timeMultiple = 1.0; +} + +ParticleEmitterNodeData::~ParticleEmitterNodeData() +{ + +} + +//----------------------------------------------------------------------------- +// initPersistFields +//----------------------------------------------------------------------------- +void ParticleEmitterNodeData::initPersistFields() +{ + addField( "timeMultiple", TYPEID< F32 >(), Offset(timeMultiple, ParticleEmitterNodeData), + "@brief Time multiplier for particle emitter nodes.\n\n" + "Increasing timeMultiple is like running the emitter at a faster rate - single-shot " + "emitters will complete in a shorter time, and continuous emitters will generate " + "particles more quickly.\n\n" + "Valid range is 0.01 - 100." ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- +// onAdd +//----------------------------------------------------------------------------- +bool ParticleEmitterNodeData::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + if( timeMultiple < 0.01 || timeMultiple > 100 ) + { + Con::warnf("ParticleEmitterNodeData::onAdd(%s): timeMultiple must be between 0.01 and 100", getName()); + timeMultiple = timeMultiple < 0.01 ? 0.01 : 100; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// preload +//----------------------------------------------------------------------------- +bool ParticleEmitterNodeData::preload(bool server, String &errorStr) +{ + if( Parent::preload(server, errorStr) == false ) + return false; + + return true; +} + + +//----------------------------------------------------------------------------- +// packData +//----------------------------------------------------------------------------- +void ParticleEmitterNodeData::packData(BitStream* stream) +{ + Parent::packData(stream); + + stream->write(timeMultiple); +} + +//----------------------------------------------------------------------------- +// unpackData +//----------------------------------------------------------------------------- +void ParticleEmitterNodeData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + + stream->read(&timeMultiple); +} + + +//----------------------------------------------------------------------------- +// ParticleEmitterNode +//----------------------------------------------------------------------------- +ParticleEmitterNode::ParticleEmitterNode() +{ + // Todo: ScopeAlways? + mNetFlags.set(Ghostable); + mTypeMask |= EnvironmentObjectType; + + mActive = true; + + mDataBlock = NULL; + mEmitterDatablock = NULL; + mEmitterDatablockId = 0; + mEmitter = NULL; + mVelocity = 1.0; +} + +//----------------------------------------------------------------------------- +// Destructor +//----------------------------------------------------------------------------- +ParticleEmitterNode::~ParticleEmitterNode() +{ + // +} + +//----------------------------------------------------------------------------- +// initPersistFields +//----------------------------------------------------------------------------- +void ParticleEmitterNode::initPersistFields() +{ + addField( "active", TYPEID< bool >(), Offset(mActive,ParticleEmitterNode), + "Controls whether particles are emitted from this node." ); + addField( "emitter", TYPEID< ParticleEmitterData >(), Offset(mEmitterDatablock, ParticleEmitterNode), + "Datablock to use when emitting particles." ); + addField( "velocity", TYPEID< F32 >(), Offset(mVelocity, ParticleEmitterNode), + "Velocity to use when emitting particles (in the direction of the " + "ParticleEmitterNode object's up (Z) axis)." ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- +// onAdd +//----------------------------------------------------------------------------- +bool ParticleEmitterNode::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + if( !mEmitterDatablock && mEmitterDatablockId != 0 ) + { + if( Sim::findObject(mEmitterDatablockId, mEmitterDatablock) == false ) + Con::errorf(ConsoleLogEntry::General, "ParticleEmitterNode::onAdd: Invalid packet, bad datablockId(mEmitterDatablock): %d", mEmitterDatablockId); + } + + if( isClientObject() ) + { + setEmitterDataBlock( mEmitterDatablock ); + } + else + { + setMaskBits( StateMask | EmitterDBMask ); + } + + mObjBox.minExtents.set(-0.5, -0.5, -0.5); + mObjBox.maxExtents.set( 0.5, 0.5, 0.5); + resetWorldBox(); + addToScene(); + + return true; +} + +//----------------------------------------------------------------------------- +// onRemove +//----------------------------------------------------------------------------- +void ParticleEmitterNode::onRemove() +{ + removeFromScene(); + if( isClientObject() ) + { + if( mEmitter ) + { + mEmitter->deleteWhenEmpty(); + mEmitter = NULL; + } + } + + Parent::onRemove(); +} + +//----------------------------------------------------------------------------- +// onNewDataBlock +//----------------------------------------------------------------------------- +bool ParticleEmitterNode::onNewDataBlock( GameBaseData *dptr, bool reload ) +{ + mDataBlock = dynamic_cast( dptr ); + if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) ) + return false; + + // Todo: Uncomment if this is a "leaf" class + scriptOnNewDataBlock(); + return true; +} + +//----------------------------------------------------------------------------- +void ParticleEmitterNode::inspectPostApply() +{ + Parent::inspectPostApply(); + setMaskBits(StateMask | EmitterDBMask); +} + +//----------------------------------------------------------------------------- +// advanceTime +//----------------------------------------------------------------------------- +void ParticleEmitterNode::processTick(const Move* move) +{ + Parent::processTick(move); + + if ( isMounted() ) + { + MatrixF mat; + mMount.object->getMountTransform( mMount.node, mMount.xfm, &mat ); + setTransform( mat ); + } +} + +void ParticleEmitterNode::advanceTime(F32 dt) +{ + Parent::advanceTime(dt); + + if(!mActive || mEmitter.isNull() || !mDataBlock) + return; + + Point3F emitPoint, emitVelocity; + Point3F emitAxis(0, 0, 1); + getTransform().mulV(emitAxis); + getTransform().getColumn(3, &emitPoint); + emitVelocity = emitAxis * mVelocity; + + mEmitter->emitParticles(emitPoint, emitPoint, + emitAxis, + emitVelocity, (U32)(dt * mDataBlock->timeMultiple * 1000.0f)); +} + +//----------------------------------------------------------------------------- +// packUpdate +//----------------------------------------------------------------------------- +U32 ParticleEmitterNode::packUpdate(NetConnection* con, U32 mask, BitStream* stream) +{ + U32 retMask = Parent::packUpdate(con, mask, stream); + + if ( stream->writeFlag( mask & InitialUpdateMask ) ) + { + mathWrite(*stream, getTransform()); + mathWrite(*stream, getScale()); + } + + if ( stream->writeFlag( mask & EmitterDBMask ) ) + { + if( stream->writeFlag(mEmitterDatablock != NULL) ) + { + stream->writeRangedU32(mEmitterDatablock->getId(), DataBlockObjectIdFirst, + DataBlockObjectIdLast); + } + } + + if ( stream->writeFlag( mask & StateMask ) ) + { + stream->writeFlag( mActive ); + stream->write( mVelocity ); + } + + return retMask; +} + +//----------------------------------------------------------------------------- +// unpackUpdate +//----------------------------------------------------------------------------- +void ParticleEmitterNode::unpackUpdate(NetConnection* con, BitStream* stream) +{ + Parent::unpackUpdate(con, stream); + + if ( stream->readFlag() ) + { + MatrixF temp; + Point3F tempScale; + mathRead(*stream, &temp); + mathRead(*stream, &tempScale); + + setScale(tempScale); + setTransform(temp); + } + + if ( stream->readFlag() ) + { + mEmitterDatablockId = stream->readFlag() ? + stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast) : 0; + + ParticleEmitterData *emitterDB = NULL; + Sim::findObject( mEmitterDatablockId, emitterDB ); + if ( isProperlyAdded() ) + setEmitterDataBlock( emitterDB ); + } + + if ( stream->readFlag() ) + { + mActive = stream->readFlag(); + stream->read( &mVelocity ); + } +} + +void ParticleEmitterNode::setEmitterDataBlock(ParticleEmitterData* data) +{ + if ( isServerObject() ) + { + setMaskBits( EmitterDBMask ); + } + else + { + ParticleEmitter* pEmitter = NULL; + if ( data ) + { + // Create emitter with new datablock + pEmitter = new ParticleEmitter; + pEmitter->onNewDataBlock( data, false ); + if( pEmitter->registerObject() == false ) + { + Con::warnf(ConsoleLogEntry::General, "Could not register base emitter for particle of class: %s", data->getName() ? data->getName() : data->getIdString() ); + delete pEmitter; + return; + } + } + + // Replace emitter + if ( mEmitter ) + mEmitter->deleteWhenEmpty(); + + mEmitter = pEmitter; + } + + mEmitterDatablock = data; +} + +DefineEngineMethod(ParticleEmitterNode, setEmitterDataBlock, void, (ParticleEmitterData* emitterDatablock), (0), + "Assigns the datablock for this emitter node.\n" + "@param emitterDatablock ParticleEmitterData datablock to assign\n" + "@tsexample\n" + "// Assign a new emitter datablock\n" + "%emitter.setEmitterDatablock( %emitterDatablock );\n" + "@endtsexample\n" ) +{ + if ( !emitterDatablock ) + { + Con::errorf("ParticleEmitterData datablock could not be found when calling setEmitterDataBlock in particleEmitterNode."); + return; + } + + object->setEmitterDataBlock(emitterDatablock); +} + +DefineEngineMethod(ParticleEmitterNode, setActive, void, (bool active),, + "Turns the emitter on or off.\n" + "@param active New emitter state\n" ) +{ + object->setActive( active ); +} diff --git a/Engine/source/T3D/fx/particleEmitterNode.h b/Engine/source/T3D/fx/particleEmitterNode.h new file mode 100644 index 000000000..48b7c4306 --- /dev/null +++ b/Engine/source/T3D/fx/particleEmitterNode.h @@ -0,0 +1,118 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PARTICLEEMITTERDUMMY_H_ +#define _PARTICLEEMITTERDUMMY_H_ + +#ifndef _GAMEBASE_H_ +#include "T3D/gameBase/gameBase.h" +#endif + +class ParticleEmitterData; +class ParticleEmitter; + +//***************************************************************************** +// ParticleEmitterNodeData +//***************************************************************************** +class ParticleEmitterNodeData : public GameBaseData +{ + typedef GameBaseData Parent; + + protected: + bool onAdd(); + + //-------------------------------------- Console set variables + public: + F32 timeMultiple; + + //-------------------------------------- load set variables + public: + + public: + ParticleEmitterNodeData(); + ~ParticleEmitterNodeData(); + + void packData(BitStream*); + void unpackData(BitStream*); + bool preload(bool server, String &errorStr); + + DECLARE_CONOBJECT(ParticleEmitterNodeData); + static void initPersistFields(); +}; + + +//***************************************************************************** +// ParticleEmitterNode +//***************************************************************************** +class ParticleEmitterNode : public GameBase +{ + typedef GameBase Parent; + + enum MaskBits + { + StateMask = Parent::NextFreeMask << 0, + EmitterDBMask = Parent::NextFreeMask << 1, + NextFreeMask = Parent::NextFreeMask << 2, + }; + + private: + ParticleEmitterNodeData* mDataBlock; + + protected: + bool onAdd(); + void onRemove(); + bool onNewDataBlock( GameBaseData *dptr, bool reload ); + void inspectPostApply(); + + ParticleEmitterData* mEmitterDatablock; + S32 mEmitterDatablockId; + + bool mActive; + + SimObjectPtr mEmitter; + F32 mVelocity; + + public: + ParticleEmitterNode(); + ~ParticleEmitterNode(); + + ParticleEmitter *getParticleEmitter() {return mEmitter;} + + // Time/Move Management + public: + void processTick(const Move* move); + void advanceTime(F32 dt); + + DECLARE_CONOBJECT(ParticleEmitterNode); + static void initPersistFields(); + + U32 packUpdate (NetConnection *conn, U32 mask, BitStream* stream); + void unpackUpdate(NetConnection *conn, BitStream* stream); + + inline bool getActive( void ) { return mActive; }; + inline void setActive( bool active ) { mActive = active; setMaskBits( StateMask ); }; + + void setEmitterDataBlock(ParticleEmitterData* data); +}; + +#endif // _H_PARTICLEEMISSIONDUMMY + diff --git a/Engine/source/T3D/fx/precipitation.cpp b/Engine/source/T3D/fx/precipitation.cpp new file mode 100644 index 000000000..e267dcd69 --- /dev/null +++ b/Engine/source/T3D/fx/precipitation.cpp @@ -0,0 +1,1867 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/fx/precipitation.h" + +#include "math/mathIO.h" +#include "console/consoleTypes.h" +#include "console/typeValidators.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "lighting/lightInfo.h" +#include "lighting/lightManager.h" +#include "materials/shaderData.h" +#include "T3D/gameBase/gameConnection.h" +#include "T3D/player.h" +#include "core/stream/bitStream.h" +#include "platform/profiler.h" +#include "renderInstance/renderPassManager.h" +#include "sfx/sfxSystem.h" +#include "sfx/sfxTrack.h" +#include "sfx/sfxSource.h" +#include "sfx/sfxTypes.h" +#include "console/engineAPI.h" +#include "particleEmitter.h" + +static const U32 dropHitMask = + TerrainObjectType | + InteriorObjectType | + WaterObjectType | + StaticShapeObjectType; + +IMPLEMENT_CO_NETOBJECT_V1(Precipitation); +IMPLEMENT_CO_DATABLOCK_V1(PrecipitationData); + +ConsoleDocClass( Precipitation, + "@brief Defines a precipitation based storm (rain, snow, etc).\n\n" + + "The Precipitation effect works by creating many 'drops' within a fixed size " + "box. This box can be configured to move around with the camera (to simulate " + "level-wide precipitation), or to remain in a fixed position (to simulate " + "localized precipitation). When #followCam is true, the box containing the " + "droplets can be thought of as centered on the camera then pushed slightly " + "forward in the direction the camera is facing so most of the box is in " + "front of the camera (allowing more drops to be visible on screen at once).\n\n" + + "The effect can also be configured to create a small 'splash' whenever a drop " + "hits another world object.\n\n" + + "@tsexample\n" + "// The following is added to a level file (.mis) by the World Editor\n" + "new Precipitation( TheRain )\n" + "{\n" + " dropSize = \"0.5\";\n" + " splashSize = \"0.5\";\n" + " splashMS = \"250\";\n" + " animateSplashes = \"1\";\n" + " dropAnimateMS = \"0\";\n" + " fadeDist = \"0\";\n" + " fadeDistEnd = \"0\";\n" + " useTrueBillboards = \"0\";\n" + " useLighting = \"0\";\n" + " glowIntensity = \"0 0 0 0\";\n" + " reflect = \"0\";\n" + " rotateWithCamVel = \"1\";\n" + " doCollision = \"1\";\n" + " hitPlayers = \"0\";\n" + " hitVehicles = \"0\";\n" + " followCam = \"1\";\n" + " useWind = \"0\";\n" + " minSpeed = \"1.5\";\n" + " maxSpeed = \"2\";\n" + " minMass = \"0.75\";\n" + " maxMass = \"0.85\";\n" + " useTurbulence = \"0\";\n" + " maxTurbulence = \"0.1\";\n" + " turbulenceSpeed = \"0.2\";\n" + " numDrops = \"1024\";\n" + " boxWidth = \"200\";\n" + " boxHeight = \"100\";\n" + " dataBlock = \"HeavyRain\";\n" + "};\n" + "@endtsexample\n" + "@ingroup FX\n" + "@ingroup Atmosphere\n" + "@see PrecipitationData\n" +); + +ConsoleDocClass( PrecipitationData, + "@brief Defines the droplets used in a storm (raindrops, snowflakes, etc).\n\n" + "@tsexample\n" + "datablock PrecipitationData( HeavyRain )\n" + "{\n" + " soundProfile = \"HeavyRainSound\";\n" + " dropTexture = \"art/environment/precipitation/rain\";\n" + " splashTexture = \"art/environment/precipitation/water_splash\";\n" + " dropsPerSide = 4;\n" + " splashesPerSide = 2;\n" + "};\n" + "@endtsexample\n" + "@ingroup FX\n" + "@ingroup Atmosphere\n" + "@see Precipitation\n" +); + + +//---------------------------------------------------------- +// PrecipitationData +//---------------------------------------------------------- +PrecipitationData::PrecipitationData() +{ + soundProfile = NULL; + + mDropName = StringTable->insert(""); + mDropShaderName = StringTable->insert(""); + mSplashName = StringTable->insert(""); + mSplashShaderName = StringTable->insert(""); + + mDropsPerSide = 4; + mSplashesPerSide = 2; +} + +void PrecipitationData::initPersistFields() +{ + addField( "soundProfile", TYPEID< SFXTrack >(), Offset(soundProfile, PrecipitationData), + "Looping SFXProfile effect to play while Precipitation is active." ); + addField( "dropTexture", TypeFilename, Offset(mDropName, PrecipitationData), + "@brief Texture filename for drop particles.\n\n" + "The drop texture can contain several different drop sub-textures " + "arranged in a grid. There must be the same number of rows as columns. A " + "random frame will be chosen for each drop." ); + addField( "dropShader", TypeString, Offset(mDropShaderName, PrecipitationData), + "The name of the shader used for raindrops." ); + addField( "splashTexture", TypeFilename, Offset(mSplashName, PrecipitationData), + "@brief Texture filename for splash particles.\n\n" + "The splash texture can contain several different splash sub-textures " + "arranged in a grid. There must be the same number of rows as columns. A " + "random frame will be chosen for each splash." ); + addField( "splashShader", TypeString, Offset(mSplashShaderName, PrecipitationData), + "The name of the shader used for splashes." ); + addField( "dropsPerSide", TypeS32, Offset(mDropsPerSide, PrecipitationData), + "@brief How many rows and columns are in the raindrop texture.\n\n" + "For example, if the texture has 16 raindrops arranged in a grid, this " + "field should be set to 4." ); + addField( "splashesPerSide", TypeS32, Offset(mSplashesPerSide, PrecipitationData), + "@brief How many rows and columns are in the splash texture.\n\n" + "For example, if the texture has 9 splashes arranged in a grid, this " + "field should be set to 3." ); + + Parent::initPersistFields(); +} + +bool PrecipitationData::preload( bool server, String &errorStr ) +{ + if( Parent::preload( server, errorStr) == false) + return false; + + if( !server && !sfxResolve( &soundProfile, errorStr ) ) + return false; + + return true; +} + +void PrecipitationData::packData(BitStream* stream) +{ + Parent::packData(stream); + + sfxWrite( stream, soundProfile ); + + stream->writeString(mDropName); + stream->writeString(mDropShaderName); + stream->writeString(mSplashName); + stream->writeString(mSplashShaderName); + stream->write(mDropsPerSide); + stream->write(mSplashesPerSide); +} + +void PrecipitationData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + + sfxRead( stream, &soundProfile ); + + mDropName = stream->readSTString(); + mDropShaderName = stream->readSTString(); + mSplashName = stream->readSTString(); + mSplashShaderName = stream->readSTString(); + stream->read(&mDropsPerSide); + stream->read(&mSplashesPerSide); +} + +//---------------------------------------------------------- +// Precipitation! +//---------------------------------------------------------- +Precipitation::Precipitation() +{ + mTypeMask |= ProjectileObjectType; + + mDataBlock = NULL; + + mTexCoords = NULL; + mSplashCoords = NULL; + + mDropShader = NULL; + mDropHandle = NULL; + + mSplashShader = NULL; + mSplashHandle = NULL; + + mDropHead = NULL; + mSplashHead = NULL; + mNumDrops = 1024; + mPercentage = 1.0; + + mMinSpeed = 1.5; + mMaxSpeed = 2.0; + + mFollowCam = true; + + mLastRenderFrame = 0; + + mDropHitMask = 0; + + mDropSize = 0.5; + mSplashSize = 0.5; + mUseTrueBillboards = false; + mSplashMS = 250; + + mAnimateSplashes = true; + mDropAnimateMS = 0; + + mUseLighting = false; + mGlowIntensity = ColorF( 0,0,0,0 ); + + mReflect = false; + + mUseWind = false; + + mBoxWidth = 200; + mBoxHeight = 100; + mFadeDistance = 0; + mFadeDistanceEnd = 0; + + mMinMass = 0.75f; + mMaxMass = 0.85f; + + mMaxTurbulence = 0.1f; + mTurbulenceSpeed = 0.2f; + mUseTurbulence = false; + + mRotateWithCamVel = true; + + mDoCollision = true; + mDropHitPlayers = false; + mDropHitVehicles = false; + + mStormData.valid = false; + mStormData.startPct = 0; + mStormData.endPct = 0; + mStormData.startTime = 0; + mStormData.totalTime = 0; + + mTurbulenceData.valid = false; + mTurbulenceData.startTime = 0; + mTurbulenceData.totalTime = 0; + mTurbulenceData.startMax = 0; + mTurbulenceData.startSpeed = 0; + mTurbulenceData.endMax = 0; + mTurbulenceData.endSpeed = 0; + + mAmbientSound = NULL; + + mDropShaderModelViewSC = NULL; + mDropShaderFadeStartEndSC = NULL; + mDropShaderCameraPosSC = NULL; + mDropShaderAmbientSC = NULL; + + mSplashShaderModelViewSC = NULL; + mSplashShaderFadeStartEndSC = NULL; + mSplashShaderCameraPosSC = NULL; + mSplashShaderAmbientSC = NULL; + +} + +Precipitation::~Precipitation() +{ + SAFE_DELETE_ARRAY(mTexCoords); + SAFE_DELETE_ARRAY(mSplashCoords); +} + +void Precipitation::inspectPostApply() +{ + if (mFollowCam) + { + setGlobalBounds(); + } + else + { + mObjBox.minExtents = -Point3F(mBoxWidth/2, mBoxWidth/2, mBoxHeight/2); + mObjBox.maxExtents = Point3F(mBoxWidth/2, mBoxWidth/2, mBoxHeight/2); + } + + resetWorldBox(); + setMaskBits(DataMask); +} + +void Precipitation::setTransform(const MatrixF & mat) +{ + Parent::setTransform(mat); + + setMaskBits(TransformMask); +} + +//-------------------------------------------------------------------------- +// Console stuff... +//-------------------------------------------------------------------------- + +IRangeValidator ValidNumDropsRange(1, 100000); + +void Precipitation::initPersistFields() +{ + addGroup("Precipitation"); + + addFieldV( "numDrops", TypeS32, Offset(mNumDrops, Precipitation), &ValidNumDropsRange, + "@brief Maximum number of drops allowed to exist in the precipitation " + "box at any one time.\n\n" + "The actual number of drops in the effect depends on the current " + "percentage, which can change over time using modifyStorm()." ); + + addField( "boxWidth", TypeF32, Offset(mBoxWidth, Precipitation), + "Width and depth (horizontal dimensions) of the precipitation box." ); + + addField( "boxHeight", TypeF32, Offset(mBoxHeight, Precipitation), + "Height (vertical dimension) of the precipitation box." ); + + endGroup("Precipitation"); + + addGroup("Rendering"); + + addField( "dropSize", TypeF32, Offset(mDropSize, Precipitation), + "Size of each drop of precipitation. This will scale the texture." ); + + addField( "splashSize", TypeF32, Offset(mSplashSize, Precipitation), + "Size of each splash animation when a drop collides with another surface." ); + + addField( "splashMS", TypeS32, Offset(mSplashMS, Precipitation), + "Lifetime of splashes in milliseconds." ); + + addField( "animateSplashes", TypeBool, Offset(mAnimateSplashes, Precipitation), + "Set to true to enable splash animations when drops collide with other surfaces." ); + + addField( "dropAnimateMS", TypeS32, Offset(mDropAnimateMS, Precipitation), + "@brief Length (in milliseconds) to display each drop frame.\n\n" + "If #dropAnimateMS <= 0, drops select a single random frame at creation " + "that does not change throughout the drop's lifetime. If #dropAnimateMS " + "> 0, each drop cycles through the the available frames in the drop " + "texture at the given rate." ); + + addField( "fadeDist", TypeF32, Offset(mFadeDistance, Precipitation), + "The distance at which drops begin to fade out." ); + + addField( "fadeDistEnd", TypeF32, Offset(mFadeDistanceEnd, Precipitation), + "The distance at which drops are completely faded out." ); + + addField( "useTrueBillboards", TypeBool, Offset(mUseTrueBillboards, Precipitation), + "Set to true to make drops true (non axis-aligned) billboards." ); + + addField( "useLighting", TypeBool, Offset(mUseLighting, Precipitation), + "Set to true to enable shading of the drops and splashes by the sun color." ); + + addField( "glowIntensity", TypeColorF, Offset(mGlowIntensity, Precipitation), + "Set to 0 to disable the glow or or use it to control the intensity of each channel." ); + + addField( "reflect", TypeBool, Offset(mReflect, Precipitation), + "@brief This enables precipitation rendering during reflection passes.\n\n" + "@note This is expensive." ); + + addField( "rotateWithCamVel", TypeBool, Offset(mRotateWithCamVel, Precipitation), + "Set to true to include the camera velocity when calculating drop " + "rotation speed." ); + + endGroup("Rendering"); + + addGroup("Collision"); + + addField( "doCollision", TypeBool, Offset(mDoCollision, Precipitation), + "@brief Allow drops to collide with world objects.\n\n" + "If #animateSplashes is true, drops that collide with another object " + "will produce a simple splash animation.\n" + "@note This can be expensive as each drop will perform a raycast when " + "it is created to determine where it will hit." ); + + addField( "hitPlayers", TypeBool, Offset(mDropHitPlayers, Precipitation), + "Allow drops to collide with Player objects; only valid if #doCollision is true." ); + + addField( "hitVehicles", TypeBool, Offset(mDropHitVehicles, Precipitation), + "Allow drops to collide with Vehicle objects; only valid if #doCollision is true." ); + + endGroup("Collision"); + + addGroup("Movement"); + + addField( "followCam", TypeBool, Offset(mFollowCam, Precipitation), + "@brief Controls whether the Precipitation system follows the camera " + "or remains where it is first placed in the scene.\n\n" + "Set to true to make it seem like it is raining everywhere in the " + "level (ie. the Player will always be in the rain). Set to false " + "to have a single area affected by rain (ie. the Player can move in " + "and out of the rainy area)." ); + + addField( "useWind", TypeBool, Offset(mUseWind, Precipitation), + "Controls whether drops are affected by wind.\n" + "@see ForestWindEmitter" ); + + addField( "minSpeed", TypeF32, Offset(mMinSpeed, Precipitation), + "@brief Minimum speed at which a drop will fall.\n\n" + "On creation, the drop will be assigned a random speed between #minSpeed " + "and #maxSpeed." ); + + addField( "maxSpeed", TypeF32, Offset(mMaxSpeed, Precipitation), + "@brief Maximum speed at which a drop will fall.\n\n" + "On creation, the drop will be assigned a random speed between #minSpeed " + "and #maxSpeed." ); + + addField( "minMass", TypeF32, Offset(mMinMass, Precipitation), + "@brief Minimum mass of a drop.\n\n" + "Drop mass determines how strongly the drop is affected by wind and " + "turbulence. On creation, the drop will be assigned a random speed " + "between #minMass and #minMass." ); + + addField( "maxMass", TypeF32, Offset(mMaxMass, Precipitation), + "@brief Maximum mass of a drop.\n\n" + "Drop mass determines how strongly the drop is affected by wind and " + "turbulence. On creation, the drop will be assigned a random speed " + "between #minMass and #minMass." ); + + endGroup("Movement"); + + addGroup("Turbulence"); + + addField( "useTurbulence", TypeBool, Offset(mUseTurbulence, Precipitation), + "Check to enable turbulence. This causes precipitation drops to spiral " + "while falling." ); + + addField( "maxTurbulence", TypeF32, Offset(mMaxTurbulence, Precipitation), + "Radius at which precipitation drops spiral when turbulence is enabled." ); + + addField( "turbulenceSpeed", TypeF32, Offset(mTurbulenceSpeed, Precipitation), + "Speed at which precipitation drops spiral when turbulence is enabled." ); + + endGroup("Turbulence"); + + Parent::initPersistFields(); +} + +//----------------------------------- +// Console methods... +DefineEngineMethod(Precipitation, setPercentage, void, (F32 percentage), (1.0f), + "Sets the maximum number of drops in the effect, as a percentage of #numDrops.\n" + "The change occurs instantly (use modifyStorm() to change the number of drops " + "over a period of time.\n" + "@param percentage New maximum number of drops value (as a percentage of " + "#numDrops). Valid range is 0-1.\n" + "@tsexample\n" + "%percentage = 0.5; // The percentage, from 0 to 1, of the maximum drops to display\n" + "%precipitation.setPercentage( %percentage );\n" + "@endtsexample\n" + "@see modifyStorm\n" ) +{ + object->setPercentage(percentage); +} + +DefineEngineMethod(Precipitation, modifyStorm, void, (F32 percentage, F32 seconds), (1.0f, 5.0f), + "Smoothly change the maximum number of drops in the effect (from current " + "value to #numDrops * @a percentage).\n" + "This method can be used to simulate a storm building or fading in intensity " + "as the number of drops in the Precipitation box changes.\n" + "@param percentage New maximum number of drops value (as a percentage of " + "#numDrops). Valid range is 0-1.\n" + "@param seconds Length of time (in seconds) over which to increase the drops " + "percentage value. Set to 0 to change instantly.\n" + "@tsexample\n" + "%percentage = 0.5; // The percentage, from 0 to 1, of the maximum drops to display\n" + "%seconds = 5.0; // The length of time over which to make the change.\n" + "%precipitation.modifyStorm( %percentage, %seconds );\n" + "@endtsexample\n" ) +{ + object->modifyStorm(percentage, S32(seconds * 1000.0f)); +} + +DefineEngineMethod(Precipitation, setTurbulence, void, (F32 max, F32 speed, F32 seconds), (1.0f, 5.0f, 5.0), + "Smoothly change the turbulence parameters over a period of time.\n" + "@param max New #maxTurbulence value. Set to 0 to disable turbulence.\n" + "@param speed New #turbulenceSpeed value.\n" + "@param seconds Length of time (in seconds) over which to interpolate the " + "turbulence settings. Set to 0 to change instantly.\n" + "@tsexample\n" + "%turbulence = 0.5; // Set the new turbulence value. Set to 0 to disable turbulence.\n" + "%speed = 5.0; // The new speed of the turbulance effect.\n" + "%seconds = 5.0; // The length of time over which to make the change.\n" + "%precipitation.setTurbulence( %turbulence, %speed, %seconds );\n" + "@endtsexample\n" ) +{ + object->setTurbulence( max, speed, S32(seconds * 1000.0f)); +} + +//-------------------------------------------------------------------------- +// Backend +//-------------------------------------------------------------------------- +bool Precipitation::onAdd() +{ + if(!Parent::onAdd()) + return false; + + if (mFollowCam) + { + setGlobalBounds(); + } + else + { + mObjBox.minExtents = -Point3F(mBoxWidth/2, mBoxWidth/2, mBoxHeight/2); + mObjBox.maxExtents = Point3F(mBoxWidth/2, mBoxWidth/2, mBoxHeight/2); + } + resetWorldBox(); + + if (isClientObject()) + { + fillDropList(); + initRenderObjects(); + initMaterials(); + } + + addToScene(); + + return true; +} + +void Precipitation::onRemove() +{ + removeFromScene(); + Parent::onRemove(); + + SFX_DELETE( mAmbientSound ); + + if (isClientObject()) + killDropList(); +} + +bool Precipitation::onNewDataBlock( GameBaseData *dptr, bool reload ) +{ + mDataBlock = dynamic_cast( dptr ); + if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) ) + return false; + + if (isClientObject()) + { + SFX_DELETE( mAmbientSound ); + + if ( mDataBlock->soundProfile ) + { + mAmbientSound = SFX->createSource( mDataBlock->soundProfile, &getTransform() ); + if ( mAmbientSound ) + mAmbientSound->play(); + } + + initRenderObjects(); + initMaterials(); + } + + scriptOnNewDataBlock(); + return true; +} + +void Precipitation::initMaterials() +{ + AssertFatal(isClientObject(), "Precipitation is setting materials on the server - BAD!"); + + if(!mDataBlock) + return; + + PrecipitationData *pd = (PrecipitationData*)mDataBlock; + + mDropHandle = NULL; + mSplashHandle = NULL; + mDropShader = NULL; + mSplashShader = NULL; + + if( dStrlen(pd->mDropName) > 0 && !mDropHandle.set(pd->mDropName, &GFXDefaultStaticDiffuseProfile, avar("%s() - mDropHandle (line %d)", __FUNCTION__, __LINE__)) ) + Con::warnf("Precipitation::initMaterials - failed to locate texture '%s'!", pd->mDropName); + + if ( dStrlen(pd->mDropShaderName) > 0 ) + { + ShaderData *shaderData; + if ( Sim::findObject( pd->mDropShaderName, shaderData ) ) + mDropShader = shaderData->getShader(); + + if( !mDropShader ) + Con::warnf( "Precipitation::initMaterials - could not find shader '%s'!", pd->mDropShaderName ); + else + { + mDropShaderConsts = mDropShader->allocConstBuffer(); + mDropShaderModelViewSC = mDropShader->getShaderConstHandle("$modelView"); + mDropShaderFadeStartEndSC = mDropShader->getShaderConstHandle("$fadeStartEnd"); + mDropShaderCameraPosSC = mDropShader->getShaderConstHandle("$cameraPos"); + mDropShaderAmbientSC = mDropShader->getShaderConstHandle("$ambient"); + } + } + + if( dStrlen(pd->mSplashName) > 0 && !mSplashHandle.set(pd->mSplashName, &GFXDefaultStaticDiffuseProfile, avar("%s() - mSplashHandle (line %d)", __FUNCTION__, __LINE__)) ) + Con::warnf("Precipitation::initMaterials - failed to locate texture '%s'!", pd->mSplashName); + + if ( dStrlen(pd->mSplashShaderName) > 0 ) + { + ShaderData *shaderData; + if ( Sim::findObject( pd->mSplashShaderName, shaderData ) ) + mSplashShader = shaderData->getShader(); + + if( !mSplashShader ) + Con::warnf( "Precipitation::initMaterials - could not find shader '%s'!", pd->mSplashShaderName ); + else + { + mSplashShaderConsts = mSplashShader->allocConstBuffer(); + mSplashShaderModelViewSC = mSplashShader->getShaderConstHandle("$modelView"); + mSplashShaderFadeStartEndSC = mSplashShader->getShaderConstHandle("$fadeStartEnd"); + mSplashShaderCameraPosSC = mSplashShader->getShaderConstHandle("$cameraPos"); + mSplashShaderAmbientSC = mSplashShader->getShaderConstHandle("$ambient"); + } + } +} + +U32 Precipitation::packUpdate(NetConnection* con, U32 mask, BitStream* stream) +{ + Parent::packUpdate(con, mask, stream); + + if (stream->writeFlag( !mFollowCam && mask & TransformMask)) + stream->writeAffineTransform(mObjToWorld); + + if (stream->writeFlag(mask & DataMask)) + { + stream->write(mDropSize); + stream->write(mSplashSize); + stream->write(mSplashMS); + stream->write(mDropAnimateMS); + stream->write(mNumDrops); + stream->write(mMinSpeed); + stream->write(mMaxSpeed); + stream->write(mBoxWidth); + stream->write(mBoxHeight); + stream->write(mMinMass); + stream->write(mMaxMass); + stream->write(mMaxTurbulence); + stream->write(mTurbulenceSpeed); + stream->write(mFadeDistance); + stream->write(mFadeDistanceEnd); + stream->write(mGlowIntensity.red); + stream->write(mGlowIntensity.green); + stream->write(mGlowIntensity.blue); + stream->write(mGlowIntensity.alpha); + stream->writeFlag(mReflect); + stream->writeFlag(mRotateWithCamVel); + stream->writeFlag(mDoCollision); + stream->writeFlag(mDropHitPlayers); + stream->writeFlag(mDropHitVehicles); + stream->writeFlag(mUseTrueBillboards); + stream->writeFlag(mUseTurbulence); + stream->writeFlag(mUseLighting); + stream->writeFlag(mUseWind); + stream->writeFlag(mFollowCam); + stream->writeFlag(mAnimateSplashes); + } + + if (stream->writeFlag(!(mask & DataMask) && (mask & TurbulenceMask))) + { + stream->write(mTurbulenceData.endMax); + stream->write(mTurbulenceData.endSpeed); + stream->write(mTurbulenceData.totalTime); + } + + if (stream->writeFlag(mask & PercentageMask)) + { + stream->write(mPercentage); + } + + if (stream->writeFlag(!(mask & ~(DataMask | PercentageMask | StormMask)) && (mask & StormMask))) + { + stream->write(mStormData.endPct); + stream->write(mStormData.totalTime); + } + + return 0; +} + +void Precipitation::unpackUpdate(NetConnection* con, BitStream* stream) +{ + Parent::unpackUpdate(con, stream); + + if (stream->readFlag()) + { + MatrixF mat; + stream->readAffineTransform(&mat); + Parent::setTransform(mat); + } + + U32 oldDrops = U32(mNumDrops * mPercentage); + if (stream->readFlag()) + { + stream->read(&mDropSize); + stream->read(&mSplashSize); + stream->read(&mSplashMS); + stream->read(&mDropAnimateMS); + stream->read(&mNumDrops); + stream->read(&mMinSpeed); + stream->read(&mMaxSpeed); + stream->read(&mBoxWidth); + stream->read(&mBoxHeight); + stream->read(&mMinMass); + stream->read(&mMaxMass); + stream->read(&mMaxTurbulence); + stream->read(&mTurbulenceSpeed); + stream->read(&mFadeDistance); + stream->read(&mFadeDistanceEnd); + stream->read(&mGlowIntensity.red); + stream->read(&mGlowIntensity.green); + stream->read(&mGlowIntensity.blue); + stream->read(&mGlowIntensity.alpha); + mReflect = stream->readFlag(); + mRotateWithCamVel = stream->readFlag(); + mDoCollision = stream->readFlag(); + mDropHitPlayers = stream->readFlag(); + mDropHitVehicles = stream->readFlag(); + mUseTrueBillboards = stream->readFlag(); + mUseTurbulence = stream->readFlag(); + mUseLighting = stream->readFlag(); + mUseWind = stream->readFlag(); + mFollowCam = stream->readFlag(); + mAnimateSplashes = stream->readFlag(); + + mDropHitMask = dropHitMask | + ( mDropHitPlayers ? PlayerObjectType : 0 ) | + ( mDropHitVehicles ? VehicleObjectType : 0 ); + + mTurbulenceData.valid = false; + } + + if (stream->readFlag()) + { + F32 max, speed; + U32 ms; + stream->read(&max); + stream->read(&speed); + stream->read(&ms); + setTurbulence( max, speed, ms ); + } + + if (stream->readFlag()) + { + F32 pct; + stream->read(&pct); + setPercentage(pct); + } + + if (stream->readFlag()) + { + F32 pct; + U32 time; + stream->read(&pct); + stream->read(&time); + modifyStorm(pct, time); + } + + AssertFatal(isClientObject(), "Precipitation::unpackUpdate() should only be called on the client!"); + + U32 newDrops = U32(mNumDrops * mPercentage); + if (oldDrops != newDrops) + { + fillDropList(); + initRenderObjects(); + } + + if (mFollowCam) + { + setGlobalBounds(); + } + else + { + mObjBox.minExtents = -Point3F(mBoxWidth/2, mBoxWidth/2, mBoxHeight/2); + mObjBox.maxExtents = Point3F(mBoxWidth/2, mBoxWidth/2, mBoxHeight/2); + } + + resetWorldBox(); +} + +//-------------------------------------------------------------------------- +// Support functions +//-------------------------------------------------------------------------- +VectorF Precipitation::getWindVelocity() +{ + // The WindManager happens to set global-wind velocity here, it is not just for particles. + return mUseWind ? ParticleEmitter::mWindVelocity : Point3F::Zero; +} + +void Precipitation::fillDropList() +{ + AssertFatal(isClientObject(), "Precipitation is doing stuff on the server - BAD!"); + + F32 density = Con::getFloatVariable("$pref::precipitationDensity", 1.0f); + U32 newDropCount = (U32)(mNumDrops * mPercentage * density); + U32 dropCount = 0; + + if (newDropCount == 0) + killDropList(); + + if (mDropHead) + { + Raindrop* curr = mDropHead; + while (curr) + { + dropCount++; + curr = curr->next; + if (dropCount == newDropCount && curr) + { + //delete the remaining drops + Raindrop* next = curr->next; + curr->next = NULL; + while (next) + { + Raindrop* last = next; + next = next->next; + last->next = NULL; + destroySplash(last); + delete last; + } + break; + } + } + } + + if (dropCount < newDropCount) + { + //move to the end + Raindrop* curr = mDropHead; + if (curr) + { + while (curr->next) + curr = curr->next; + } + else + { + mDropHead = curr = new Raindrop; + spawnNewDrop(curr); + dropCount++; + } + + //and add onto it + while (dropCount < newDropCount) + { + curr->next = new Raindrop; + curr = curr->next; + spawnNewDrop(curr); + dropCount++; + } + } +} + +void Precipitation::initRenderObjects() +{ + AssertFatal(isClientObject(), "Precipitation is doing stuff on the server - BAD!"); + + SAFE_DELETE_ARRAY(mTexCoords); + SAFE_DELETE_ARRAY(mSplashCoords); + + if (!mDataBlock) + return; + + mTexCoords = new Point2F[4*mDataBlock->mDropsPerSide*mDataBlock->mDropsPerSide]; + + // Setup the texcoords for the drop texture. + // The order of the coords when animating is... + // + // +---+---+---+ + // | 1 | 2 | 3 | + // |---|---|---+ + // | 4 | 5 | 6 | + // +---+---+---+ + // | 7 | etc... + // +---+ + // + U32 count = 0; + for (U32 v = 0; v < mDataBlock->mDropsPerSide; v++) + { + F32 y1 = (F32) v / mDataBlock->mDropsPerSide; + F32 y2 = (F32)(v+1) / mDataBlock->mDropsPerSide; + for (U32 u = 0; u < mDataBlock->mDropsPerSide; u++) + { + F32 x1 = (F32) u / mDataBlock->mDropsPerSide; + F32 x2 = (F32)(u+1) / mDataBlock->mDropsPerSide; + + mTexCoords[4*count+0].x = x1; + mTexCoords[4*count+0].y = y1; + + mTexCoords[4*count+1].x = x2; + mTexCoords[4*count+1].y = y1; + + mTexCoords[4*count+2].x = x2; + mTexCoords[4*count+2].y = y2; + + mTexCoords[4*count+3].x = x1; + mTexCoords[4*count+3].y = y2; + count++; + } + } + + count = 0; + mSplashCoords = new Point2F[4*mDataBlock->mSplashesPerSide*mDataBlock->mSplashesPerSide]; + for (U32 v = 0; v < mDataBlock->mSplashesPerSide; v++) + { + F32 y1 = (F32) v / mDataBlock->mSplashesPerSide; + F32 y2 = (F32)(v+1) / mDataBlock->mSplashesPerSide; + for (U32 u = 0; u < mDataBlock->mSplashesPerSide; u++) + { + F32 x1 = (F32) u / mDataBlock->mSplashesPerSide; + F32 x2 = (F32)(u+1) / mDataBlock->mSplashesPerSide; + + mSplashCoords[4*count+0].x = x1; + mSplashCoords[4*count+0].y = y1; + + mSplashCoords[4*count+1].x = x2; + mSplashCoords[4*count+1].y = y1; + + mSplashCoords[4*count+2].x = x2; + mSplashCoords[4*count+2].y = y2; + + mSplashCoords[4*count+3].x = x1; + mSplashCoords[4*count+3].y = y2; + count++; + } + } + + // Cap the number of precipitation drops so that we don't blow out the max verts + mMaxVBDrops = getMin( (U32)mNumDrops, ( GFX->getMaxDynamicVerts() / 4 ) - 1 ); + + // If we have no drops then skip allocating anything! + if ( mMaxVBDrops == 0 ) + return; + + // Create a volitile vertex buffer which + // we'll lock and fill every frame. + mRainVB.set(GFX, mMaxVBDrops * 4, GFXBufferTypeVolatile); + + // Init the index buffer for rendering the + // entire or a partially filled vb. + mRainIB.set(GFX, mMaxVBDrops * 6, 0, GFXBufferTypeStatic); + U16 *idxBuff; + mRainIB.lock(&idxBuff, NULL, NULL, NULL); + for( U32 i=0; i < mMaxVBDrops; i++ ) + { + // + // The vertex pattern in the VB for each + // particle is as follows... + // + // 0----1 + // |\ | + // | \ | + // | \ | + // | \| + // 3----2 + // + // We setup the index order below to ensure + // sequential, cache friendly, access. + // + U32 offset = i * 4; + idxBuff[i*6+0] = 0 + offset; + idxBuff[i*6+1] = 1 + offset; + idxBuff[i*6+2] = 2 + offset; + idxBuff[i*6+3] = 2 + offset; + idxBuff[i*6+4] = 3 + offset; + idxBuff[i*6+5] = 0 + offset; + } + mRainIB.unlock(); +} + +void Precipitation::killDropList() +{ + AssertFatal(isClientObject(), "Precipitation is doing stuff on the server - BAD!"); + + Raindrop* curr = mDropHead; + while (curr) + { + Raindrop* next = curr->next; + delete curr; + curr = next; + } + mDropHead = NULL; + mSplashHead = NULL; +} + +void Precipitation::spawnDrop(Raindrop *drop) +{ + PROFILE_START(PrecipSpawnDrop); + AssertFatal(isClientObject(), "Precipitation is doing stuff on the server - BAD!"); + + drop->velocity = Platform::getRandom() * (mMaxSpeed - mMinSpeed) + mMinSpeed; + + drop->position.x = Platform::getRandom() * mBoxWidth; + drop->position.y = Platform::getRandom() * mBoxWidth; + + // The start time should be randomized so that + // all the drops are not animating at the same time. + drop->animStartTime = (SimTime)(Platform::getVirtualMilliseconds() * Platform::getRandom()); + + if (mDropAnimateMS <= 0 && mDataBlock) + drop->texCoordIndex = (U32)(Platform::getRandom() * ((F32)mDataBlock->mDropsPerSide*mDataBlock->mDropsPerSide - 0.5)); + + drop->valid = true; + drop->time = Platform::getRandom() * M_2PI; + drop->mass = Platform::getRandom() * (mMaxMass - mMinMass) + mMinMass; + PROFILE_END(); +} + +void Precipitation::spawnNewDrop(Raindrop *drop) +{ + AssertFatal(isClientObject(), "Precipitation is doing stuff on the server - BAD!"); + + spawnDrop(drop); + drop->position.z = Platform::getRandom() * mBoxHeight - (mBoxHeight / 2); +} + +void Precipitation::wrapDrop(Raindrop *drop, const Box3F &box, const U32 currTime, const VectorF &windVel) +{ + //could probably be slightly optimized to get rid of the while loops + if (drop->position.z < box.minExtents.z) + { + spawnDrop(drop); + drop->position.x += box.minExtents.x; + drop->position.y += box.minExtents.y; + while (drop->position.z < box.minExtents.z) + drop->position.z += mBoxHeight; + findDropCutoff(drop, box, windVel); + } + else if (drop->position.z > box.maxExtents.z) + { + while (drop->position.z > box.maxExtents.z) + drop->position.z -= mBoxHeight; + findDropCutoff(drop, box, windVel); + } + else if (drop->position.x < box.minExtents.x) + { + while (drop->position.x < box.minExtents.x) + drop->position.x += mBoxWidth; + findDropCutoff(drop, box, windVel); + } + else if (drop->position.x > box.maxExtents.x) + { + while (drop->position.x > box.maxExtents.x) + drop->position.x -= mBoxWidth; + findDropCutoff(drop, box, windVel); + } + else if (drop->position.y < box.minExtents.y) + { + while (drop->position.y < box.minExtents.y) + drop->position.y += mBoxWidth; + findDropCutoff(drop, box, windVel); + } + else if (drop->position.y > box.maxExtents.y) + { + while (drop->position.y > box.maxExtents.y) + drop->position.y -= mBoxWidth; + findDropCutoff(drop, box, windVel); + } +} + +void Precipitation::findDropCutoff(Raindrop *drop, const Box3F &box, const VectorF &windVel) +{ + PROFILE_START(PrecipFindDropCutoff); + AssertFatal(isClientObject(), "Precipitation is doing stuff on the server - BAD!"); + + if (mDoCollision) + { + VectorF velocity = windVel / drop->mass - VectorF(0, 0, drop->velocity); + velocity.normalize(); + + Point3F end = drop->position + 100 * velocity; + Point3F start = drop->position - (mFollowCam ? 500.0f : 0.0f) * velocity; + + if (!mFollowCam) + { + mObjToWorld.mulP(start); + mObjToWorld.mulP(end); + } + + // Look for a collision... make sure we don't + // collide with backfaces. + RayInfo rInfo; + if (getContainer()->castRay(start, end, mDropHitMask, &rInfo)) + { + // TODO: Add check to filter out hits on backfaces. + + if (!mFollowCam) + mWorldToObj.mulP(rInfo.point); + + drop->hitPos = rInfo.point; + drop->hitType = rInfo.object->getTypeMask(); + } + else + drop->hitPos = Point3F(0,0,-1000); + + drop->valid = drop->position.z > drop->hitPos.z; + } + else + { + drop->hitPos = Point3F(0,0,-1000); + drop->valid = true; + } + PROFILE_END(); +} + +void Precipitation::createSplash(Raindrop *drop) +{ + if (!mDataBlock) + return; + + PROFILE_START(PrecipCreateSplash); + if (drop != mSplashHead && !(drop->nextSplashDrop || drop->prevSplashDrop)) + { + if (!mSplashHead) + { + mSplashHead = drop; + drop->prevSplashDrop = NULL; + drop->nextSplashDrop = NULL; + } + else + { + mSplashHead->prevSplashDrop = drop; + drop->nextSplashDrop = mSplashHead; + drop->prevSplashDrop = NULL; + mSplashHead = drop; + } + } + + drop->animStartTime = Platform::getVirtualMilliseconds(); + + if (!mAnimateSplashes) + drop->texCoordIndex = (U32)(Platform::getRandom() * ((F32)mDataBlock->mSplashesPerSide*mDataBlock->mSplashesPerSide - 0.5)); + + PROFILE_END(); +} + +void Precipitation::destroySplash(Raindrop *drop) +{ + PROFILE_START(PrecipDestroySplash); + if (drop == mSplashHead) + { + mSplashHead = NULL; + PROFILE_END(); + return; + } + + if (drop->nextSplashDrop) + drop->nextSplashDrop->prevSplashDrop = drop->prevSplashDrop; + if (drop->prevSplashDrop) + drop->prevSplashDrop->nextSplashDrop = drop->nextSplashDrop; + + drop->nextSplashDrop = NULL; + drop->prevSplashDrop = NULL; + + PROFILE_END(); +} + +//-------------------------------------------------------------------------- +// Processing +//-------------------------------------------------------------------------- +void Precipitation::setPercentage(F32 pct) +{ + mPercentage = mClampF(pct, 0, 1); + mStormData.valid = false; + + if (isServerObject()) + { + setMaskBits(PercentageMask); + } +} + +void Precipitation::modifyStorm(F32 pct, U32 ms) +{ + if ( ms == 0 ) + { + setPercentage( pct ); + return; + } + + pct = mClampF(pct, 0, 1); + mStormData.endPct = pct; + mStormData.totalTime = ms; + + if (isServerObject()) + { + setMaskBits(StormMask); + return; + } + + mStormData.startTime = Platform::getVirtualMilliseconds(); + mStormData.startPct = mPercentage; + mStormData.valid = true; +} + +void Precipitation::setTurbulence(F32 max, F32 speed, U32 ms) +{ + if ( ms == 0 && !isServerObject() ) + { + mUseTurbulence = max > 0; + mMaxTurbulence = max; + mTurbulenceSpeed = speed; + return; + } + + mTurbulenceData.endMax = max; + mTurbulenceData.endSpeed = speed; + mTurbulenceData.totalTime = ms; + + if (isServerObject()) + { + setMaskBits(TurbulenceMask); + return; + } + + mTurbulenceData.startTime = Platform::getVirtualMilliseconds(); + mTurbulenceData.startMax = mMaxTurbulence; + mTurbulenceData.startSpeed = mTurbulenceSpeed; + mTurbulenceData.valid = true; +} + +void Precipitation::interpolateTick(F32 delta) +{ + AssertFatal(isClientObject(), "Precipitation is doing stuff on the server - BAD!"); + + // If we're not being seen then the simulation + // is paused and we don't need any interpolation. + if (mLastRenderFrame != ShapeBase::sLastRenderFrame) + return; + + PROFILE_START(PrecipInterpolate); + + const F32 dt = 1-delta; + const VectorF windVel = dt * getWindVelocity(); + const F32 turbSpeed = dt * mTurbulenceSpeed; + + Raindrop* curr = mDropHead; + VectorF turbulence; + F32 renderTime; + + while (curr) + { + if (!curr->valid || !curr->toRender) + { + curr = curr->next; + continue; + } + + if (mUseTurbulence) + { + renderTime = curr->time + turbSpeed; + turbulence.x = windVel.x + ( mSin(renderTime) * mMaxTurbulence ); + turbulence.y = windVel.y + ( mCos(renderTime) * mMaxTurbulence ); + turbulence.z = windVel.z; + curr->renderPosition = curr->position + turbulence / curr->mass; + } + else + curr->renderPosition = curr->position + windVel / curr->mass; + + curr->renderPosition.z -= dt * curr->velocity; + + curr = curr->next; + } + PROFILE_END(); +} + +void Precipitation::processTick(const Move *) +{ + //nothing to do on the server + if (isServerObject() || mDataBlock == NULL) + return; + + const U32 currTime = Platform::getVirtualMilliseconds(); + + // Update the storm if necessary + if (mStormData.valid) + { + F32 t = (currTime - mStormData.startTime) / (F32)mStormData.totalTime; + if (t >= 1) + { + mPercentage = mStormData.endPct; + mStormData.valid = false; + } + else + mPercentage = mStormData.startPct * (1-t) + mStormData.endPct * t; + + fillDropList(); + } + + // Do we need to update the turbulence? + if ( mTurbulenceData.valid ) + { + F32 t = (currTime - mTurbulenceData.startTime) / (F32)mTurbulenceData.totalTime; + if (t >= 1) + { + mMaxTurbulence = mTurbulenceData.endMax; + mTurbulenceSpeed = mTurbulenceData.endSpeed; + mTurbulenceData.valid = false; + } + else + { + mMaxTurbulence = mTurbulenceData.startMax * (1-t) + mTurbulenceData.endMax * t; + mTurbulenceSpeed = mTurbulenceData.startSpeed * (1-t) + mTurbulenceData.endSpeed * t; + } + + mUseTurbulence = mMaxTurbulence > 0; + } + + // If we're not being seen then pause the + // simulation. Precip is generally noisy + // enough that no one should notice. + if (mLastRenderFrame != ShapeBase::sLastRenderFrame) + return; + + //we need to update positions and do some collision here + GameConnection* conn = GameConnection::getConnectionToServer(); + if (!conn) + return; //need connection to server + + ShapeBase* camObj = dynamic_cast(conn->getCameraObject()); + if (!camObj) + return; + + PROFILE_START(PrecipProcess); + + MatrixF camMat; + camObj->getEyeTransform(&camMat); + + const F32 camFov = camObj->getCameraFov(); + + Point3F camPos, camDir; + Box3F box; + + if (mFollowCam) + { + camMat.getColumn(3, &camPos); + + box = Box3F(camPos.x - mBoxWidth / 2, camPos.y - mBoxWidth / 2, camPos.z - mBoxHeight / 2, + camPos.x + mBoxWidth / 2, camPos.y + mBoxWidth / 2, camPos.z + mBoxHeight / 2); + + camMat.getColumn(1, &camDir); + camDir.normalize(); + } + else + { + box = mObjBox; + + camMat.getColumn(3, &camPos); + mWorldToObj.mulP(camPos); + + camMat.getColumn(1, &camDir); + camDir.normalize(); + mWorldToObj.mulV(camDir); + } + + const VectorF windVel = getWindVelocity(); + const F32 fovDot = camFov / 180; + + Raindrop* curr = mDropHead; + + //offset the renderbox in the direction of the camera direction + //in order to have more of the drops actually rendered + if (mFollowCam) + { + box.minExtents.x += camDir.x * mBoxWidth / 4; + box.maxExtents.x += camDir.x * mBoxWidth / 4; + box.minExtents.y += camDir.y * mBoxWidth / 4; + box.maxExtents.y += camDir.y * mBoxWidth / 4; + box.minExtents.z += camDir.z * mBoxHeight / 4; + box.maxExtents.z += camDir.z * mBoxHeight / 4; + } + + VectorF lookVec; + F32 pct; + const S32 dropCount = mDataBlock->mDropsPerSide*mDataBlock->mDropsPerSide; + while (curr) + { + // Update the position. This happens even if this + // is a splash so that the drop respawns when it wraps + // around to the top again. + if (mUseTurbulence) + curr->time += mTurbulenceSpeed; + curr->position += windVel / curr->mass; + curr->position.z -= curr->velocity; + + // Wrap the drop if it reaches an edge of the box. + wrapDrop(curr, box, currTime, windVel); + + // Did the drop pass below the hit position? + if (curr->valid && curr->position.z < curr->hitPos.z) + { + // If this drop was to hit a player or vehicle double + // check to see if the object has moved out of the way. + // This keeps us from leaving phantom trails of splashes + // behind a moving player/vehicle. + if (curr->hitType & (PlayerObjectType | VehicleObjectType)) + { + findDropCutoff(curr, box, windVel); + + if (curr->position.z > curr->hitPos.z) + goto NO_SPLASH; // Ugly, yet simple. + } + + // The drop is dead. + curr->valid = false; + + // Convert the drop into a splash or let it + // wrap around and respawn in wrapDrop(). + if (mSplashMS > 0) + createSplash(curr); + + // So ugly... yet simple. +NO_SPLASH:; + } + + // We do not do cull individual drops when we're not + // following as it is usually a tight box and all of + // the particles are in view. + if (!mFollowCam) + curr->toRender = true; + else + { + lookVec = curr->position - camPos; + curr->toRender = mDot(lookVec, camDir) > fovDot; + } + + // Do we need to animate the drop? + if (curr->valid && mDropAnimateMS > 0 && curr->toRender) + { + pct = (F32)(currTime - curr->animStartTime) / mDropAnimateMS; + pct = mFmod(pct, 1); + curr->texCoordIndex = (U32)(dropCount * pct); + } + + curr = curr->next; + } + + //update splashes + curr = mSplashHead; + Raindrop *next; + const S32 splashCount = mDataBlock->mSplashesPerSide * mDataBlock->mSplashesPerSide; + while (curr) + { + pct = (F32)(currTime - curr->animStartTime) / mSplashMS; + if (pct >= 1.0f) + { + next = curr->nextSplashDrop; + destroySplash(curr); + curr = next; + continue; + } + + if (mAnimateSplashes) + curr->texCoordIndex = (U32)(splashCount * pct); + + curr = curr->nextSplashDrop; + } + + PROFILE_END_NAMED(PrecipProcess); +} + +//-------------------------------------------------------------------------- +// Rendering +//-------------------------------------------------------------------------- +void Precipitation::prepRenderImage(SceneRenderState* state) +{ + PROFILE_SCOPE(Precipitation_prepRenderImage); + + // We we have no drops then skip rendering + // and don't bother with the sound. + if (mMaxVBDrops == 0) + return; + + // We do nothing if we're not supposed to be reflected. + if ( state->isReflectPass() && !mReflect ) + return; + + // This should be sufficient for most objects that don't manage zones, and + // don't need to return a specialized RenderImage... + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind(this, &Precipitation::renderObject); + ri->type = RenderPassManager::RIT_Foliage; + state->getRenderPass()->addInst( ri ); +} + +void Precipitation::renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* overrideMat) +{ + if (overrideMat) + return; + +#ifdef TORQUE_OS_XENON + return; +#endif + + GameConnection* conn = GameConnection::getConnectionToServer(); + if (!conn) + return; //need connection to server + + ShapeBase* camObj = dynamic_cast(conn->getCameraObject()); + if (!camObj) + return; // need camera object + + PROFILE_START(PrecipRender); + + GFX->pushWorldMatrix(); + + MatrixF world = GFX->getWorldMatrix(); + MatrixF proj = GFX->getProjectionMatrix(); + if (!mFollowCam) + { + world.mul( getRenderTransform() ); + world.scale( getScale() ); + GFX->setWorldMatrix( world ); + } + proj.mul(world); + + //GFX2 doesn't require transpose? + //proj.transpose(); + + Point3F camPos = state->getCameraPosition(); + VectorF camVel = camObj->getVelocity(); + if (!mFollowCam) + { + getRenderWorldTransform().mulP(camPos); + getRenderWorldTransform().mulV(camVel); + } + const VectorF windVel = getWindVelocity(); + const bool useBillboards = mUseTrueBillboards; + const F32 dropSize = mDropSize; + + Point3F pos; + VectorF orthoDir, velocity, right, up, rightUp(0.0f, 0.0f, 0.0f), leftUp(0.0f, 0.0f, 0.0f); + F32 distance = 0; + GFXVertexPT* vertPtr = NULL; + const Point2F *tc; + + // Do this here and we won't have to in the loop! + if (useBillboards) + { + MatrixF camMat = state->getCameraTransform(); + camMat.inverse(); + camMat.getRow(0,&right); + camMat.getRow(2,&up); + if (!mFollowCam) + { + mWorldToObj.mulV(right); + mWorldToObj.mulV(up); + } + right.normalize(); + up.normalize(); + right *= mDropSize; + up *= mDropSize; + rightUp = right + up; + leftUp = -right + up; + } + + // We pass the sunlight as a constant to the + // shader. Once the lighting and shadow systems + // are added into TSE we can expand this to include + // the N nearest lights to the camera + the ambient. + ColorF ambient( 1, 1, 1 ); + if ( mUseLighting ) + { + const LightInfo *sunlight = LIGHTMGR->getSpecialLight(LightManager::slSunLightType); + ambient = sunlight->getColor(); + } + + if ( mGlowIntensity.red > 0 || + mGlowIntensity.green > 0 || + mGlowIntensity.blue > 0 ) + { + ambient *= mGlowIntensity; + } + + // Setup render state + + if (mDefaultSB.isNull()) + { + GFXStateBlockDesc desc; + + desc.zWriteEnable = false; + desc.setAlphaTest(true, GFXCmpGreaterEqual, 1); + desc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha); + + mDefaultSB = GFX->createStateBlock(desc); + + desc.samplersDefined = true; + desc.samplers[0].textureColorOp = GFXTOPModulate; + desc.samplers[0].colorArg1 = GFXTATexture; + desc.samplers[0].colorArg2 = GFXTADiffuse; + desc.samplers[0].alphaOp = GFXTOPSelectARG1; + desc.samplers[0].alphaArg1 = GFXTATexture; + + desc.samplers[1].textureColorOp = GFXTOPDisable; + desc.samplers[1].alphaOp = GFXTOPDisable; + + mDistantSB = GFX->createStateBlock(desc); + } + + GFX->setStateBlock(mDefaultSB); + + // Everything is rendered from these buffers. + GFX->setPrimitiveBuffer(mRainIB); + GFX->setVertexBuffer(mRainVB); + + // Set the constants used by the shaders. + if (mDropShader) + { + Point2F fadeStartEnd( mFadeDistance, mFadeDistanceEnd ); + + mDropShaderConsts->setSafe(mDropShaderModelViewSC, proj); + mDropShaderConsts->setSafe(mDropShaderFadeStartEndSC, fadeStartEnd); + mDropShaderConsts->setSafe(mDropShaderCameraPosSC, camPos); + mDropShaderConsts->setSafe(mDropShaderAmbientSC, Point3F(ambient.red, ambient.green, ambient.blue)); + } + + + if (mSplashShader) + { + Point2F fadeStartEnd( mFadeDistance, mFadeDistanceEnd ); + + mSplashShaderConsts->setSafe(mSplashShaderModelViewSC, proj); + mSplashShaderConsts->setSafe(mSplashShaderFadeStartEndSC, fadeStartEnd); + mSplashShaderConsts->setSafe(mSplashShaderCameraPosSC, camPos); + mSplashShaderConsts->setSafe(mSplashShaderAmbientSC, Point3F(ambient.red, ambient.green, ambient.blue)); + } + + // Time to render the drops... + const Raindrop *curr = mDropHead; + U32 vertCount = 0; + + GFX->setTexture(0, mDropHandle); + + // Use the shader or setup the pipeline + // for fixed function rendering. + if (mDropShader) + { + GFX->setShader( mDropShader ); + GFX->setShaderConstBuffer( mDropShaderConsts ); + } + else + { + GFX->disableShaders(); + + // We don't support distance fade or lighting without shaders. + GFX->setStateBlock(mDistantSB); + } + + while (curr) + { + // Skip ones that are not drops (hit something and + // may have been converted into a splash) or they + // are behind the camera. + if (!curr->valid || !curr->toRender) + { + curr = curr->next; + continue; + } + + pos = curr->renderPosition; + + // two forms of billboards - true billboards (which we set + // above outside this loop) or axis-aligned with velocity + // (this codeblock) the axis-aligned billboards are aligned + // with the velocity of the raindrop, and tilted slightly + // towards the camera + if (!useBillboards) + { + orthoDir = camPos - pos; + distance = orthoDir.len(); + + // Inline the normalize so we don't + // calculate the ortho len twice. + if (distance > 0.0) + orthoDir *= 1.0f / distance; + else + orthoDir.set( 0, 0, 1 ); + + velocity = windVel / curr->mass; + + // We do not optimize this for the "still" case + // because its not a typical scenario. + if (mRotateWithCamVel) + velocity -= camVel / (distance > 2.0f ? distance : 2.0f) * 0.3f; + + velocity.z -= curr->velocity; + velocity.normalize(); + + right = mCross(-velocity, orthoDir); + right.normalize(); + up = mCross(orthoDir, right) * 0.5 - velocity * 0.5; + up.normalize(); + right *= dropSize; + up *= dropSize; + rightUp = right + up; + leftUp = -right + up; + } + + // Do we need to relock the buffer? + if ( !vertPtr ) + vertPtr = mRainVB.lock(); + + // Set the proper texture coords... (it's fun!) + tc = &mTexCoords[4*curr->texCoordIndex]; + vertPtr->point = pos + leftUp; + vertPtr->texCoord = *tc; + tc++; + vertPtr++; + + vertPtr->point = pos + rightUp; + vertPtr->texCoord = *tc; + tc++; + vertPtr++; + + vertPtr->point = pos - leftUp; + vertPtr->texCoord = *tc; + tc++; + vertPtr++; + + vertPtr->point = pos - rightUp; + vertPtr->texCoord = *tc; + tc++; + vertPtr++; + + // Do we need to render to clear the buffer? + vertCount += 4; + if ( (vertCount + 4) >= mRainVB->mNumVerts ) { + + mRainVB.unlock(); + GFX->drawIndexedPrimitive(GFXTriangleList, 0, 0, vertCount, 0, vertCount / 2); + vertPtr = NULL; + vertCount = 0; + } + + curr = curr->next; + } + + // Do we have stuff left to render? + if ( vertCount > 0 ) { + + mRainVB.unlock(); + GFX->drawIndexedPrimitive(GFXTriangleList, 0, 0, vertCount, 0, vertCount / 2); + vertCount = 0; + vertPtr = NULL; + } + + // Setup the billboard for the splashes. + MatrixF camMat = state->getCameraTransform(); + camMat.inverse(); + camMat.getRow(0, &right); + camMat.getRow(2, &up); + if (!mFollowCam) + { + mWorldToObj.mulV(right); + mWorldToObj.mulV(up); + } + right.normalize(); + up.normalize(); + right *= mSplashSize; + up *= mSplashSize; + rightUp = right + up; + leftUp = -right + up; + + // Render the visible splashes. + curr = mSplashHead; + + GFX->setTexture(0, mSplashHandle); + + if (mSplashShader) + { + GFX->setShader( mSplashShader ); + GFX->setShaderConstBuffer(mSplashShaderConsts); + } + else + GFX->disableShaders(); + + while (curr) + { + if (!curr->toRender) + { + curr = curr->nextSplashDrop; + continue; + } + + pos = curr->hitPos; + + tc = &mSplashCoords[4*curr->texCoordIndex]; + + // Do we need to relock the buffer? + if ( !vertPtr ) + vertPtr = mRainVB.lock(); + + vertPtr->point = pos + leftUp; + vertPtr->texCoord = *tc; + tc++; + vertPtr++; + + vertPtr->point = pos + rightUp; + vertPtr->texCoord = *tc; + tc++; + vertPtr++; + + vertPtr->point = pos - leftUp; + vertPtr->texCoord = *tc; + tc++; + vertPtr++; + + vertPtr->point = pos - rightUp; + vertPtr->texCoord = *tc; + tc++; + vertPtr++; + + // Do we need to flush the buffer by rendering? + vertCount += 4; + if ( (vertCount + 4) >= mRainVB->mNumVerts ) { + + mRainVB.unlock(); + GFX->drawIndexedPrimitive(GFXTriangleList, 0, 0, vertCount, 0, vertCount / 2); + vertPtr = NULL; + vertCount = 0; + } + + curr = curr->nextSplashDrop; + } + + // Do we have stuff left to render? + if ( vertCount > 0 ) { + + mRainVB.unlock(); + GFX->drawIndexedPrimitive(GFXTriangleList, 0, 0, vertCount, 0, vertCount / 2); + } + + mLastRenderFrame = ShapeBase::sLastRenderFrame; + + GFX->popWorldMatrix(); + + PROFILE_END(); +} diff --git a/Engine/source/T3D/fx/precipitation.h b/Engine/source/T3D/fx/precipitation.h new file mode 100644 index 000000000..908d7005f --- /dev/null +++ b/Engine/source/T3D/fx/precipitation.h @@ -0,0 +1,287 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PRECIPITATION_H_ +#define _PRECIPITATION_H_ + +#include "gfx/gfxDevice.h" +#include "T3D/gameBase/gameBase.h" + +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif +#ifndef _RENDERPASSMANAGER_H_ +#include "renderInstance/renderPassManager.h" +#endif + +class SFXTrack; +class SFXSource; + +//-------------------------------------------------------------------------- +/// Precipitation datablock. +class PrecipitationData : public GameBaseData +{ + typedef GameBaseData Parent; + + public: + SFXTrack* soundProfile; + + StringTableEntry mDropName; ///< Texture filename for drop particles + StringTableEntry mDropShaderName; ///< The name of the shader used for raindrops + StringTableEntry mSplashName; ///< Texture filename for splash particles + StringTableEntry mSplashShaderName; ///< The name of the shader used for raindrops + + S32 mDropsPerSide; ///< How many drops are on a side of the raindrop texture. + S32 mSplashesPerSide; ///< How many splash are on a side of the splash texture. + + PrecipitationData(); + DECLARE_CONOBJECT(PrecipitationData); + bool preload( bool server, String& errorStr ); + static void initPersistFields(); + virtual void packData(BitStream* stream); + virtual void unpackData(BitStream* stream); +}; + +struct Raindrop +{ + F32 velocity; ///< How fast the drop is falling downwards + Point3F position; ///< Position of the drop + Point3F renderPosition; ///< Interpolated render-position of the drop + F32 time; ///< Time into the turbulence function + F32 mass; ///< Mass of drop used for how much turbulence/wind effects the drop + + U32 texCoordIndex; ///< Which piece of the material will be used + + bool toRender; ///< Don't want to render all drops, just the ones that pass a few tests + bool valid; ///< Drop becomes invalid after hitting something. Just keep updating + ///< the position of it, but don't render until it hits the bottom + ///< of the renderbox and respawns + + Point3F hitPos; ///< Point at which the drop will collide with something + U32 hitType; ///< What kind of object the drop will hit + + Raindrop *nextSplashDrop; ///< Linked list cruft for easily adding/removing stuff from the splash list + Raindrop *prevSplashDrop; ///< Same as next but previous! + + SimTime animStartTime; ///< Animation time tracker + + Raindrop* next; ///< linked list cruft + + Raindrop() + { + velocity = 0; + time = 0; + mass = 1; + texCoordIndex = 0; + next = NULL; + toRender = false; + valid = true; + nextSplashDrop = NULL; + prevSplashDrop = NULL; + animStartTime = 0; + hitType = 0; + hitPos = Point3F(0,0,0); + } +}; + +//-------------------------------------------------------------------------- +class Precipitation : public GameBase +{ + protected: + + typedef GameBase Parent; + PrecipitationData* mDataBlock; + + Raindrop *mDropHead; ///< Drop linked list head + Raindrop *mSplashHead; ///< Splash linked list head + + Point2F* mTexCoords; ///< texture coords for rain texture + Point2F* mSplashCoords; ///< texture coordinates for splash texture + + SFXSource* mAmbientSound; ///< Ambient sound + + GFXShaderRef mDropShader; ///< The shader used for raindrops + GFXTexHandle mDropHandle; ///< Texture handle for raindrop + GFXShaderRef mSplashShader; ///< The shader used for splashes + GFXTexHandle mSplashHandle; ///< Texture handle for splash + + U32 mLastRenderFrame; ///< Used to skip processTick when we haven't been visible. + + U32 mDropHitMask; ///< Stores the current drop hit mask. + + //console exposed variables + bool mFollowCam; ///< Does the system follow the camera or stay where it's placed. + + F32 mDropSize; ///< Droplet billboard size + F32 mSplashSize; ///< Splash billboard size + bool mUseTrueBillboards; ///< True to use true billboards, false for axis-aligned billboards + S32 mSplashMS; ///< How long in milliseconds a splash will last + bool mAnimateSplashes; ///< Animate the splashes using the frames in the texture. + + S32 mDropAnimateMS; ///< If greater than zero, will animate the drops from + ///< the frames in the texture + + S32 mNumDrops; ///< Number of drops in the scene + F32 mPercentage; ///< Server-side set var (NOT exposed to console) + ///< which controls how many drops are present [0,1] + + F32 mMinSpeed; ///< Minimum downward speed of drops + F32 mMaxSpeed; ///< Maximum downward speed of drops + + F32 mMinMass; ///< Minimum mass of drops + F32 mMaxMass; ///< Maximum mass of drops + + F32 mBoxWidth; ///< How far away in the x and y directions drops will render + F32 mBoxHeight; ///< How high drops will render + + F32 mMaxTurbulence; ///< Coefficient to sin/cos for adding turbulence + F32 mTurbulenceSpeed; ///< How fast the turbulence wraps in a circle + bool mUseTurbulence; ///< Whether to use turbulence or not (MAY EFFECT PERFORMANCE) + + bool mUseLighting; ///< This enables shading of the drops and splashes + ///< by the sun color. + + ColorF mGlowIntensity; ///< Set it to 0 to disable the glow or use it to control + ///< the intensity of each channel. + + bool mReflect; ///< This enables the precipitation to be rendered + ///< during reflection passes. This is expensive. + + bool mUseWind; ///< This enables the wind from the sky SceneObject + ///< to effect the velocitiy of the drops. + + bool mRotateWithCamVel; ///< Rotate the drops relative to the camera velocity + ///< This is useful for "streak" type drops + + bool mDoCollision; ///< Whether or not to do collision + bool mDropHitPlayers; ///< Should drops collide with players + bool mDropHitVehicles; ///< Should drops collide with vehicles + + F32 mFadeDistance; ///< The distance at which fading of the particles begins. + F32 mFadeDistanceEnd; ///< The distance at which fading of the particles ends. + + U32 mMaxVBDrops; ///< The maximum drops allowed in one render batch. + + GFXStateBlockRef mDefaultSB; + GFXStateBlockRef mDistantSB; + + GFXShaderConstBufferRef mDropShaderConsts; + + GFXShaderConstHandle* mDropShaderModelViewSC; + GFXShaderConstHandle* mDropShaderFadeStartEndSC; + GFXShaderConstHandle* mDropShaderCameraPosSC; + GFXShaderConstHandle* mDropShaderAmbientSC; + + GFXShaderConstBufferRef mSplashShaderConsts; + + GFXShaderConstHandle* mSplashShaderModelViewSC; + GFXShaderConstHandle* mSplashShaderFadeStartEndSC; + GFXShaderConstHandle* mSplashShaderCameraPosSC; + GFXShaderConstHandle* mSplashShaderAmbientSC; + + struct + { + bool valid; + U32 startTime; + U32 totalTime; + F32 startPct; + F32 endPct; + + } mStormData; + + struct + { + bool valid; + U32 startTime; + U32 totalTime; + F32 startMax; + F32 startSpeed; + F32 endMax; + F32 endSpeed; + + } mTurbulenceData; + + //other functions... + void processTick(const Move*); + void interpolateTick(F32 delta); + + VectorF getWindVelocity(); + void fillDropList(); ///< Adds/removes drops from the list to have the right # of drops + void killDropList(); ///< Deletes the entire drop list + void initRenderObjects(); ///< Re-inits the texture coord lookup tables + void initMaterials(); ///< Re-inits the textures and shaders + void spawnDrop(Raindrop *drop); ///< Fills drop info with random velocity, x/y positions, and mass + void spawnNewDrop(Raindrop *drop); ///< Same as spawnDrop except also does z position + + void findDropCutoff(Raindrop *drop, const Box3F &box, const VectorF &windVel); ///< Casts a ray to see if/when a drop will collide + void wrapDrop(Raindrop *drop, const Box3F &box, const U32 currTime, const VectorF &windVel); ///< Wraps a drop within the specified box + + void createSplash(Raindrop *drop); ///< Adds a drop to the splash list + void destroySplash(Raindrop *drop); ///< Removes a drop from the splash list + + GFXPrimitiveBufferHandle mRainIB; + GFXVertexBufferHandle mRainVB; + + bool onAdd(); + void onRemove(); + + // Rendering + void prepRenderImage( SceneRenderState* state ); + void renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* ); + + void setTransform(const MatrixF &mat); + + public: + + Precipitation(); + ~Precipitation(); + void inspectPostApply(); + + enum + { + DataMask = Parent::NextFreeMask << 0, + PercentageMask = Parent::NextFreeMask << 1, + StormMask = Parent::NextFreeMask << 2, + TransformMask = Parent::NextFreeMask << 3, + TurbulenceMask = Parent::NextFreeMask << 4, + NextFreeMask = Parent::NextFreeMask << 5 + }; + + bool onNewDataBlock( GameBaseData *dptr, bool reload ); + DECLARE_CONOBJECT(Precipitation); + static void initPersistFields(); + + U32 packUpdate(NetConnection*, U32 mask, BitStream* stream); + void unpackUpdate(NetConnection*, BitStream* stream); + + void setPercentage(F32 pct); + void modifyStorm(F32 pct, U32 ms); + + /// This is used to smoothly change the turbulence + /// over a desired time period. Setting ms to zero + /// will cause the change to be instantaneous. Setting + /// max zero will disable turbulence. + void setTurbulence(F32 max, F32 speed, U32 ms); +}; + +#endif // PRECIPITATION_H_ + diff --git a/Engine/source/T3D/fx/splash.cpp b/Engine/source/T3D/fx/splash.cpp new file mode 100644 index 000000000..7a3eda0f9 --- /dev/null +++ b/Engine/source/T3D/fx/splash.cpp @@ -0,0 +1,698 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "T3D/fx/splash.h" + +#include "console/consoleTypes.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxDrawUtil.h" +#include "sfx/sfxSystem.h" +#include "sfx/sfxProfile.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "core/stream/bitStream.h" +#include "math/mathIO.h" +#include "T3D/fx/explosion.h" +#include "T3D/fx/particle.h" +#include "T3D/fx/particleEmitter.h" +#include "T3D/fx/particleEmitterNode.h" +#include "T3D/gameBase/gameProcess.h" +#include "sim/netConnection.h" +#include "renderInstance/renderPassManager.h" +#include "console/engineAPI.h" + +namespace +{ + +MRandomLCG sgRandom(0xdeadbeef); + +} // namespace {} + +//---------------------------------------------------------------------------- + +IMPLEMENT_CO_DATABLOCK_V1(SplashData); +IMPLEMENT_CO_NETOBJECT_V1(Splash); + +ConsoleDocClass( SplashData, + "@brief Acts as the physical point in space in white a Splash is created from.\n" + "@ingroup FX\n" +); + +ConsoleDocClass( Splash, + "@brief Manages the ring used for a Splash effect.\n" + "@ingroup FX\n" +); + +//-------------------------------------------------------------------------- +// Splash Data +//-------------------------------------------------------------------------- +SplashData::SplashData() +{ + soundProfile = NULL; + soundProfileId = 0; + + scale.set(1, 1, 1); + + dMemset( emitterList, 0, sizeof( emitterList ) ); + dMemset( emitterIDList, 0, sizeof( emitterIDList ) ); + + delayMS = 0; + delayVariance = 0; + lifetimeMS = 1000; + lifetimeVariance = 0; + width = 4.0; + numSegments = 10; + velocity = 5.0; + height = 0.0; + acceleration = 0.0; + texWrap = 1.0; + texFactor = 3.0; + ejectionFreq = 5; + ejectionAngle = 45.0; + ringLifetime = 1.0; + startRadius = 0.5; + explosion = NULL; + explosionId = 0; + + dMemset( textureName, 0, sizeof( textureName ) ); + + U32 i; + for( i=0; i(), Offset(soundProfile, SplashData), "SFXProfile effect to play.\n"); + addField("scale", TypePoint3F, Offset(scale, SplashData), "The scale of this splashing effect, defined as the F32 points X, Y, Z.\n"); + addField("emitter", TYPEID< ParticleEmitterData >(), Offset(emitterList, SplashData), NUM_EMITTERS, "List of particle emitters to create at the point of this Splash effect.\n"); + addField("delayMS", TypeS32, Offset(delayMS, SplashData), "Time to delay, in milliseconds, before actually starting this effect.\n"); + addField("delayVariance", TypeS32, Offset(delayVariance, SplashData), "Time variance for delayMS.\n"); + addField("lifetimeMS", TypeS32, Offset(lifetimeMS, SplashData), "Lifetime for this effect, in milliseconds.\n"); + addField("lifetimeVariance", TypeS32, Offset(lifetimeVariance, SplashData), "Time variance for lifetimeMS.\n"); + addField("width", TypeF32, Offset(width, SplashData), "Width for the X and Y coordinates to create this effect within."); + addField("numSegments", TypeS32, Offset(numSegments, SplashData), "Number of ejection points in the splash ring.\n"); + addField("velocity", TypeF32, Offset(velocity, SplashData), "Velocity for the splash effect to travel.\n"); + addField("height", TypeF32, Offset(height, SplashData), "Height for the splash to reach.\n"); + addField("acceleration", TypeF32, Offset(acceleration, SplashData), "Constant acceleration value to place upon the splash effect.\n"); + addField("times", TypeF32, Offset(times, SplashData), NUM_TIME_KEYS, "Times to transition through the splash effect. Up to 4 allowed. Values are 0.0 - 1.0, and corrispond to the life of the particle where 0 is first created and 1 is end of lifespace.\n" ); + addField("colors", TypeColorF, Offset(colors, SplashData), NUM_TIME_KEYS, "Color values to set the splash effect, rgba. Up to 4 allowed. Will transition through colors based on values set in the times value. Example: colors[0] = \"0.6 1.0 1.0 0.5\".\n" ); + addField("texture", TypeFilename, Offset(textureName, SplashData), NUM_TEX, "Imagemap file to use as the texture for the splash effect.\n"); + addField("texWrap", TypeF32, Offset(texWrap, SplashData), "Amount to wrap the texture around the splash ring, 0.0f - 1.0f.\n"); + addField("texFactor", TypeF32, Offset(texFactor, SplashData), "Factor in which to apply the texture to the splash ring, 0.0f - 1.0f.\n"); + addField("ejectionFreq", TypeF32, Offset(ejectionFreq, SplashData), "Frequency in which to emit splash rings.\n"); + addField("ejectionAngle", TypeF32, Offset(ejectionAngle, SplashData), "Rotational angle to create a splash ring.\n"); + addField("ringLifetime", TypeF32, Offset(ringLifetime, SplashData), "Lifetime, in milliseconds, for a splash ring.\n"); + addField("startRadius", TypeF32, Offset(startRadius, SplashData), "Starting radius size of a splash ring.\n"); + addField("explosion", TYPEID< ExplosionData >(), Offset(explosion, SplashData), "ExplosionData object to create at the creation position of this splash effect.\n"); + + Parent::initPersistFields(); +} + +//-------------------------------------------------------------------------- +// On add - verify data settings +//-------------------------------------------------------------------------- +bool SplashData::onAdd() +{ + if (Parent::onAdd() == false) + return false; + + return true; +} + +//-------------------------------------------------------------------------- +// Pack data +//-------------------------------------------------------------------------- +void SplashData::packData(BitStream* stream) +{ + Parent::packData(stream); + + mathWrite(*stream, scale); + stream->write(delayMS); + stream->write(delayVariance); + stream->write(lifetimeMS); + stream->write(lifetimeVariance); + stream->write(width); + stream->write(numSegments); + stream->write(velocity); + stream->write(height); + stream->write(acceleration); + stream->write(texWrap); + stream->write(texFactor); + stream->write(ejectionFreq); + stream->write(ejectionAngle); + stream->write(ringLifetime); + stream->write(startRadius); + + if( stream->writeFlag( explosion ) ) + { + stream->writeRangedU32(explosion->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast); + } + + S32 i; + for( i=0; iwriteFlag( emitterList[i] != NULL ) ) + { + stream->writeRangedU32( emitterList[i]->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + } + + for( i=0; iwrite( colors[i] ); + } + + for( i=0; iwrite( times[i] ); + } + + for( i=0; iwriteString(textureName[i]); + } +} + +//-------------------------------------------------------------------------- +// Unpack data +//-------------------------------------------------------------------------- +void SplashData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + + mathRead(*stream, &scale); + stream->read(&delayMS); + stream->read(&delayVariance); + stream->read(&lifetimeMS); + stream->read(&lifetimeVariance); + stream->read(&width); + stream->read(&numSegments); + stream->read(&velocity); + stream->read(&height); + stream->read(&acceleration); + stream->read(&texWrap); + stream->read(&texFactor); + stream->read(&ejectionFreq); + stream->read(&ejectionAngle); + stream->read(&ringLifetime); + stream->read(&startRadius); + + if( stream->readFlag() ) + { + explosionId = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + + U32 i; + for( i=0; ireadFlag() ) + { + emitterIDList[i] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + } + + for( i=0; iread( &colors[i] ); + } + + for( i=0; iread( ×[i] ); + } + + for( i=0; ireadSTString(); + } +} + +//-------------------------------------------------------------------------- +// Preload data - load resources +//-------------------------------------------------------------------------- +bool SplashData::preload(bool server, String &errorStr) +{ + if (Parent::preload(server, errorStr) == false) + return false; + + if (!server) + { + S32 i; + for( i=0; idelayMS + sgRandom.randI( -mDataBlock->delayVariance, mDataBlock->delayVariance ); + mEndingMS = mDataBlock->lifetimeMS + sgRandom.randI( -mDataBlock->lifetimeVariance, mDataBlock->lifetimeVariance ); + + mVelocity = mDataBlock->velocity; + mHeight = mDataBlock->height; + mTimeSinceLastRing = 1.0 / mDataBlock->ejectionFreq; + + for( U32 i=0; iemitterList[i] != NULL ) + { + ParticleEmitter * pEmitter = new ParticleEmitter; + pEmitter->onNewDataBlock( mDataBlock->emitterList[i], false ); + if( !pEmitter->registerObject() ) + { + Con::warnf( ConsoleLogEntry::General, "Could not register emitter for particle of class: %s", mDataBlock->getName() ); + delete pEmitter; + pEmitter = NULL; + } + mEmitterList[i] = pEmitter; + } + } + + spawnExplosion(); + + mObjBox.minExtents = Point3F( -1, -1, -1 ); + mObjBox.maxExtents = Point3F( 1, 1, 1 ); + resetWorldBox(); + + gClientSceneGraph->addObjectToScene(this); + + removeFromProcessList(); + ClientProcessList::get()->addObject(this); + + conn->addObject(this); + + return true; +} + +//-------------------------------------------------------------------------- +// OnRemove +//-------------------------------------------------------------------------- +void Splash::onRemove() +{ + for( U32 i=0; ideleteWhenEmpty(); + mEmitterList[i] = NULL; + } + } + + ringList.clear(); + + getSceneManager()->removeObjectFromScene(this); + getContainer()->removeObject(this); + + Parent::onRemove(); +} + + +//-------------------------------------------------------------------------- +// On New Data Block +//-------------------------------------------------------------------------- +bool Splash::onNewDataBlock( GameBaseData *dptr, bool reload ) +{ + mDataBlock = dynamic_cast(dptr); + if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload)) + return false; + + scriptOnNewDataBlock(); + return true; +} + + +//-------------------------------------------------------------------------- +// Process tick +//-------------------------------------------------------------------------- +void Splash::processTick(const Move*) +{ + mCurrMS += TickMs; + + if( isServerObject() ) + { + if( mCurrMS >= mEndingMS ) + { + mDead = true; + if( mCurrMS >= (mEndingMS + mDataBlock->ringLifetime * 1000) ) + { + deleteObject(); + } + } + } + else + { + if( mCurrMS >= mEndingMS ) + { + mDead = true; + } + } +} + +//-------------------------------------------------------------------------- +// Advance time +//-------------------------------------------------------------------------- +void Splash::advanceTime(F32 dt) +{ + if (dt == 0.0) + return; + + mElapsedTime += dt; + + updateColor(); + updateWave( dt ); + updateEmitters( dt ); + updateRings( dt ); + + if( !mDead ) + { + emitRings( dt ); + } +} + +//---------------------------------------------------------------------------- +// Update emitters +//---------------------------------------------------------------------------- +void Splash::updateEmitters( F32 dt ) +{ + Point3F pos = getPosition(); + + for( U32 i=0; iemitParticles( pos, pos, mInitialNormal, Point3F( 0.0, 0.0, 0.0 ), (S32) (dt * 1000) ); + } + } + +} + +//---------------------------------------------------------------------------- +// Update wave +//---------------------------------------------------------------------------- +void Splash::updateWave( F32 dt ) +{ + mVelocity += mDataBlock->acceleration * dt; + mRadius += mVelocity * dt; + +} + +//---------------------------------------------------------------------------- +// Update color +//---------------------------------------------------------------------------- +void Splash::updateColor() +{ + for(SplashRingList::Iterator ring = ringList.begin(); ring != ringList.end(); ++ring) + { + F32 t = F32(ring->elapsedTime) / F32(ring->lifetime); + + for( U32 i = 1; i < SplashData::NUM_TIME_KEYS; i++ ) + { + if( mDataBlock->times[i] >= t ) + { + F32 firstPart = t - mDataBlock->times[i-1]; + F32 total = (mDataBlock->times[i] - + mDataBlock->times[i-1]); + + firstPart /= total; + + ring->color.interpolate( mDataBlock->colors[i-1], + mDataBlock->colors[i], + firstPart); + break; + } + } + } +} + +//---------------------------------------------------------------------------- +// Create ring +//---------------------------------------------------------------------------- +SplashRing Splash::createRing() +{ + SplashRing ring; + U32 numPoints = mDataBlock->numSegments + 1; + + Point3F ejectionAxis( 0.0, 0.0, 1.0 ); + + Point3F axisx; + if (mFabs(ejectionAxis.z) < 0.999f) + mCross(ejectionAxis, Point3F(0, 0, 1), &axisx); + else + mCross(ejectionAxis, Point3F(0, 1, 0), &axisx); + axisx.normalize(); + + for( U32 i=0; iejectionAngle * (M_PI / 180.0)); + AngAxisF phiRot( ejectionAxis, t * (M_PI * 2.0)); + + Point3F pointAxis = ejectionAxis; + + MatrixF temp; + thetaRot.setMatrix(&temp); + temp.mulP(pointAxis); + phiRot.setMatrix(&temp); + temp.mulP(pointAxis); + + Point3F startOffset = axisx; + temp.mulV( startOffset ); + startOffset *= mDataBlock->startRadius; + + SplashRingPoint point; + point.position = getPosition() + startOffset; + point.velocity = pointAxis * mDataBlock->velocity; + + ring.points.push_back( point ); + } + + ring.color = mDataBlock->colors[0]; + ring.lifetime = mDataBlock->ringLifetime; + ring.elapsedTime = 0.0; + ring.v = mDataBlock->texFactor * mFmod( mElapsedTime, 1.0 ); + + return ring; +} + +//---------------------------------------------------------------------------- +// Emit rings +//---------------------------------------------------------------------------- +void Splash::emitRings( F32 dt ) +{ + mTimeSinceLastRing += dt; + + S32 numNewRings = (S32) (mTimeSinceLastRing * F32(mDataBlock->ejectionFreq)); + + mTimeSinceLastRing -= numNewRings / mDataBlock->ejectionFreq; + + for( S32 i=numNewRings-1; i>=0; i-- ) + { + F32 t = F32(i) / F32(numNewRings); + t *= dt; + t += mTimeSinceLastRing; + + SplashRing ring = createRing(); + updateRing( ring, t ); + + ringList.pushBack( ring ); + } +} + +//---------------------------------------------------------------------------- +// Update rings +//---------------------------------------------------------------------------- +void Splash::updateRings( F32 dt ) +{ + SplashRingList::Iterator ring; + for(SplashRingList::Iterator i = ringList.begin(); i != ringList.end(); /*Trickiness*/) + { + ring = i++; + ring->elapsedTime += dt; + + if( !ring->isActive() ) + { + ringList.erase( ring ); + } + else + { + updateRing( *ring, dt ); + } + } +} + +//---------------------------------------------------------------------------- +// Update ring +//---------------------------------------------------------------------------- +void Splash::updateRing( SplashRing& ring, F32 dt ) +{ + for( U32 i=0; iacceleration; + ring.points[i].velocity += vel * dt; + } + + ring.points[i].velocity += Point3F( 0.0f, 0.0f, -9.8f ) * dt; + ring.points[i].position += ring.points[i].velocity * dt; + } +} + +//---------------------------------------------------------------------------- +// Explode +//---------------------------------------------------------------------------- +void Splash::spawnExplosion() +{ + if( !mDataBlock->explosion ) return; + + Explosion* pExplosion = new Explosion; + pExplosion->onNewDataBlock(mDataBlock->explosion, false); + + MatrixF trans = getTransform(); + trans.setPosition( getPosition() ); + + pExplosion->setTransform( trans ); + pExplosion->setInitialState( trans.getPosition(), VectorF(0,0,1), 1); + if (!pExplosion->registerObject()) + delete pExplosion; +} + +//-------------------------------------------------------------------------- +// packUpdate +//-------------------------------------------------------------------------- +U32 Splash::packUpdate(NetConnection* con, U32 mask, BitStream* stream) +{ + U32 retMask = Parent::packUpdate(con, mask, stream); + + if( stream->writeFlag(mask & GameBase::InitialUpdateMask) ) + { + mathWrite(*stream, mInitialPosition); + } + + return retMask; +} + +//-------------------------------------------------------------------------- +// unpackUpdate +//-------------------------------------------------------------------------- +void Splash::unpackUpdate(NetConnection* con, BitStream* stream) +{ + Parent::unpackUpdate(con, stream); + + if( stream->readFlag() ) + { + mathRead(*stream, &mInitialPosition); + setPosition( mInitialPosition ); + } +} diff --git a/Engine/source/T3D/fx/splash.h b/Engine/source/T3D/fx/splash.h new file mode 100644 index 000000000..53362e6c0 --- /dev/null +++ b/Engine/source/T3D/fx/splash.h @@ -0,0 +1,195 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SPLASH_H_ +#define _SPLASH_H_ + +#ifndef _GAMEBASE_H_ +#include "T3D/gameBase/gameBase.h" +#endif + +#ifndef _TORQUE_LIST_ +#include "core/util/tList.h" +#endif + +#include "gfx/gfxTextureHandle.h" + +class ParticleEmitter; +class ParticleEmitterData; +class AudioProfile; +class ExplosionData; + + +//-------------------------------------------------------------------------- +// Ring Point +//-------------------------------------------------------------------------- +struct SplashRingPoint +{ + Point3F position; + Point3F velocity; +}; + +//-------------------------------------------------------------------------- +// Splash Ring +//-------------------------------------------------------------------------- +struct SplashRing +{ + Vector points; + ColorF color; + F32 lifetime; + F32 elapsedTime; + F32 v; + + SplashRing() + { + color.set( 0.0, 0.0, 0.0, 1.0 ); + lifetime = 0.0; + elapsedTime = 0.0; + v = 0.0; + } + + bool isActive() + { + return elapsedTime < lifetime; + } +}; + +//-------------------------------------------------------------------------- +// Splash Data +//-------------------------------------------------------------------------- +class SplashData : public GameBaseData +{ + public: + typedef GameBaseData Parent; + + enum Constants + { + NUM_EMITTERS = 3, + NUM_TIME_KEYS = 4, + NUM_TEX = 2, + }; + +public: + AudioProfile* soundProfile; + S32 soundProfileId; + + ParticleEmitterData* emitterList[NUM_EMITTERS]; + S32 emitterIDList[NUM_EMITTERS]; + + S32 delayMS; + S32 delayVariance; + S32 lifetimeMS; + S32 lifetimeVariance; + Point3F scale; + F32 width; + F32 height; + U32 numSegments; + F32 velocity; + F32 acceleration; + F32 texWrap; + F32 texFactor; + + F32 ejectionFreq; + F32 ejectionAngle; + F32 ringLifetime; + F32 startRadius; + + F32 times[ NUM_TIME_KEYS ]; + ColorF colors[ NUM_TIME_KEYS ]; + + StringTableEntry textureName[NUM_TEX]; + GFXTexHandle textureHandle[NUM_TEX]; + + ExplosionData* explosion; + S32 explosionId; + + SplashData(); + DECLARE_CONOBJECT(SplashData); + bool onAdd(); + bool preload(bool server, String &errorStr); + static void initPersistFields(); + virtual void packData(BitStream* stream); + virtual void unpackData(BitStream* stream); +}; + +//-------------------------------------------------------------------------- +// Splash +//-------------------------------------------------------------------------- +class Splash : public GameBase +{ + typedef GameBase Parent; + +private: + SplashData* mDataBlock; + + SimObjectPtr mEmitterList[ SplashData::NUM_EMITTERS ]; + + typedef Torque::List SplashRingList; + SplashRingList ringList; + + U32 mCurrMS; + U32 mEndingMS; + F32 mRandAngle; + F32 mRadius; + F32 mVelocity; + F32 mHeight; + ColorF mColor; + F32 mTimeSinceLastRing; + bool mDead; + F32 mElapsedTime; + +protected: + Point3F mInitialPosition; + Point3F mInitialNormal; + F32 mFade; + F32 mFog; + bool mActive; + S32 mDelayMS; + +protected: + bool onAdd(); + void onRemove(); + void processTick(const Move *move); + void advanceTime(F32 dt); + void updateEmitters( F32 dt ); + void updateWave( F32 dt ); + void updateColor(); + SplashRing createRing(); + void updateRings( F32 dt ); + void updateRing( SplashRing& ring, F32 dt ); + void emitRings( F32 dt ); + void spawnExplosion(); + +public: + Splash(); + ~Splash(); + void setInitialState(const Point3F& point, const Point3F& normal, const F32 fade = 1.0); + + U32 packUpdate (NetConnection *conn, U32 mask, BitStream* stream); + void unpackUpdate(NetConnection *conn, BitStream* stream); + + bool onNewDataBlock( GameBaseData *dptr, bool reload ); + DECLARE_CONOBJECT(Splash); +}; + + +#endif // _H_SPLASH diff --git a/Engine/source/T3D/fx/windEmitter.cpp b/Engine/source/T3D/fx/windEmitter.cpp new file mode 100644 index 000000000..719ecb905 --- /dev/null +++ b/Engine/source/T3D/fx/windEmitter.cpp @@ -0,0 +1,136 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/fx/windEmitter.h" + +#include "math/mBox.h" +#include "core/tAlgorithm.h" +#include "platform/profiler.h" + + +Vector WindEmitter::smAllEmitters; + + +WindEmitter::WindEmitter() +{ + smAllEmitters.push_back( this ); + + mEnabled = true; + mScore = 0.0f; + mSphere.center.zero(); + mSphere.radius = 0.0f; + mStrength = 0.0f; + mTurbulenceFrequency = 0.0f; + mTurbulenceStrength = 0.0f; + mVelocity.zero(); +} + +WindEmitter::~WindEmitter() +{ + WindEmitterList::iterator iter = find( smAllEmitters.begin(), smAllEmitters.end(), this ); + smAllEmitters.erase( iter ); +} + +void WindEmitter::setPosition( const Point3F& pos ) +{ + mSphere.center = pos; +} + +void WindEmitter::update( const Point3F& pos, const VectorF& velocity ) +{ + mSphere.center = pos; + mVelocity = velocity; +} + +void WindEmitter::setRadius( F32 radius ) +{ + mSphere.radius = radius; +} + +void WindEmitter::setStrength( F32 strength ) +{ + mStrength = strength; +} + +void WindEmitter::setTurbulency( F32 frequency, F32 strength ) +{ + mTurbulenceFrequency = frequency; + mTurbulenceStrength = strength; +} + +S32 QSORT_CALLBACK WindEmitter::_sortByScore(const void* a, const void* b) +{ + return mSign((*(WindEmitter**)b)->mScore - (*(WindEmitter**)a)->mScore); +} + +bool WindEmitter::findBest( const Point3F& cameraPos, + const VectorF& cameraDir, + F32 viewDistance, + U32 maxResults, + WindEmitterList* results ) +{ + PROFILE_START(WindEmitter_findBest); + + // Build a sphere from the camera point. + SphereF cameraSphere; + cameraSphere.center = cameraPos; + cameraSphere.radius = viewDistance; + + // Collect the active spheres within the camera space and score them. + WindEmitterList best; + WindEmitterList::iterator iter = smAllEmitters.begin(); + for ( ; iter != smAllEmitters.end(); iter++ ) + { + const SphereF& sphere = *(*iter); + + // Skip any spheres outside of our camera range or that are disabled. + if ( !(*iter)->mEnabled || !cameraSphere.isIntersecting( sphere ) ) + continue; + + // Simple score calculation... + // + // score = ( radius / distance to camera ) * dot( cameraDir, vector from camera to sphere ) + // + Point3F vect = sphere.center - cameraSphere.center; + F32 dist = vect.len(); + (*iter)->mScore = dist * sphere.radius; + vect /= getMax( dist, 0.001f ); + (*iter)->mScore *= mDot( vect, cameraDir ); + + best.push_back( *iter ); + } + + // Sort the results by score! + dQsort( best.address(), best.size(), sizeof(WindEmitter*), &WindEmitter::_sortByScore ); + + // Clip the results to the max requested. + if ( best.size() > maxResults ) + best.setSize( maxResults ); + + // Merge the results and return. + results->merge( best ); + + PROFILE_END(); // WindEmitter_findBest + + return best.size() > 0; +} diff --git a/Engine/source/T3D/fx/windEmitter.h b/Engine/source/T3D/fx/windEmitter.h new file mode 100644 index 000000000..9cfa6b086 --- /dev/null +++ b/Engine/source/T3D/fx/windEmitter.h @@ -0,0 +1,96 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _WINDEMITTER_H_ +#define _WINDEMITTER_H_ + +#ifndef _MPOINT3_H_ + #include "math/mPoint3.h" +#endif +#ifndef _MSPHERE_H_ + #include "math/mSphere.h" +#endif +#ifndef _TVECTOR_H_ + #include "core/util/tVector.h" +#endif + +class WindEmitter; + +/// A vector of WindEmitter pointers. +typedef Vector WindEmitterList; + + +class WindEmitter +{ +public: + WindEmitter(); + ~WindEmitter(); + + operator const SphereF&() const { return mSphere; } + + void update( const Point3F& pos, const VectorF& velocity ); + + void setPosition( const Point3F& pos ); + + void setRadius( F32 radius ); + + void setStrength( F32 strength ); + + void setTurbulency( F32 frequency, F32 strength ); + + const Point3F& getCenter() const { return mSphere.center; } + + F32 getRadius() const { return mSphere.radius; } + + F32 getStrength() const { return mStrength; } + + F32 getTurbulenceFrequency() const { return mTurbulenceFrequency; } + + F32 getTurbulenceStrength() const { return mTurbulenceStrength; } + + const VectorF& getVelocity() const { return mVelocity; } + + + static bool findBest( const Point3F& cameraPos, + const VectorF& cameraDir, + F32 viewDistance, + U32 maxResults, + WindEmitterList* results ); + +protected: + SphereF mSphere; + + VectorF mVelocity; + + F32 mStrength; + F32 mTurbulenceFrequency; + F32 mTurbulenceStrength; + F32 mScore; + + bool mEnabled; + + static WindEmitterList smAllEmitters; + + static S32 QSORT_CALLBACK _sortByScore( const void* a, const void* b ); +}; + +#endif // _WINDEMITTER_H_ diff --git a/Engine/source/T3D/gameBase/gameBase.cpp b/Engine/source/T3D/gameBase/gameBase.cpp new file mode 100644 index 000000000..bc796383e --- /dev/null +++ b/Engine/source/T3D/gameBase/gameBase.cpp @@ -0,0 +1,703 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/gameBase/gameBase.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "console/consoleInternal.h" +#include "core/stream/bitStream.h" +#include "sim/netConnection.h" +#include "T3D/gameBase/gameConnection.h" +#include "math/mathIO.h" +#include "T3D/gameBase/moveManager.h" +#include "T3D/gameBase/gameProcess.h" + +#ifdef TORQUE_DEBUG_NET_MOVES +#include "T3D/aiConnection.h" +#endif + +//---------------------------------------------------------------------------- +// Ghost update relative priority values + +static F32 sUpFov = 1.0; +static F32 sUpDistance = 0.4f; +static F32 sUpVelocity = 0.4f; +static F32 sUpSkips = 0.2f; +static F32 sUpInterest = 0.2f; + +//---------------------------------------------------------------------------- + +IMPLEMENT_CO_DATABLOCK_V1(GameBaseData); + +ConsoleDocClass( GameBaseData, + "@brief Scriptable, demo-able datablock. Used by GameBase objects.\n\n" + "@see GameBase\n" + "@ingroup gameObjects\n" +); + +IMPLEMENT_CALLBACK( GameBaseData, onAdd, void, ( GameBase* obj ), ( obj ), + "@brief Called when the object is added to the scene.\n\n" + + "@param obj the GameBase object\n\n" + + "@tsexample\n" + "datablock GameBaseData(MyObjectData)\n" + "{\n" + " category = \"Misc\";\n" + "};\n\n" + "function MyObjectData::onAdd( %this, %obj )\n" + "{\n" + " echo( \"Added \" @ %obj.getName() @ \" to the scene.\" );\n" + "}\n\n" + "function MyObjectData::onNewDataBlock( %this, %obj )\n" + "{\n" + " echo( \"Assign \" @ %this.getName() @ \" datablock to \" %obj.getName() );\n" + "}\n\n" + "function MyObjectData::onRemove( %this, %obj )\n" + "{\n" + " echo( \"Removed \" @ %obj.getName() @ \" to the scene.\" );\n" + "}\n\n" + "function MyObjectData::onMount( %this, %obj, %mountObj, %node )\n" + "{\n" + " echo( %obj.getName() @ \" mounted to \" @ %mountObj.getName() );\n" + "}\n\n" + "function MyObjectData::onUnmount( %this, %obj, %mountObj, %node )\n" + "{\n" + " echo( %obj.getName() @ \" unmounted from \" @ %mountObj.getName() );\n" + "}\n\n" + "@endtsexample\n" ); + +IMPLEMENT_CALLBACK( GameBaseData, onNewDataBlock, void, ( GameBase* obj ), ( obj ), + "@brief Called when the object has a new datablock assigned.\n\n" + "@param obj the GameBase object\n\n" + "@see onAdd for an example\n" ); + +IMPLEMENT_CALLBACK( GameBaseData, onRemove, void, ( GameBase* obj ), ( obj ), + "@brief Called when the object is removed from the scene.\n\n" + "@param obj the GameBase object\n\n" + "@see onAdd for an example\n" ); + +IMPLEMENT_CALLBACK( GameBaseData, onMount, void, ( GameBase* obj, SceneObject* mountObj, S32 node ), ( obj, mountObj, node ), + "@brief Called when the object is mounted to another object in the scene.\n\n" + "@param obj the GameBase object being mounted\n" + "@param mountObj the object we are mounted to\n" + "@param node the mountObj node we are mounted to\n\n" + "@see onAdd for an example\n" ); + +IMPLEMENT_CALLBACK( GameBaseData, onUnmount, void, ( GameBase* obj, SceneObject* mountObj, S32 node ), ( obj, mountObj, node ), + "@brief Called when the object is unmounted from another object in the scene.\n\n" + "@param obj the GameBase object being unmounted\n" + "@param mountObj the object we are unmounted from\n" + "@param node the mountObj node we are unmounted from\n\n" + "@see onAdd for an example\n" ); + +IMPLEMENT_CALLBACK( GameBase, setControl, void, ( bool controlled ), ( controlled ), + "@brief Called when the client controlling the object changes.\n\n" + "@param controlled true if a client now controls this object, false if no " + "client controls this object.\n" ); + + +GameBaseData::GameBaseData() +{ + category = ""; + packed = false; +} + +void GameBaseData::inspectPostApply() +{ + Parent::inspectPostApply(); + + // Tell interested parties ( like objects referencing this datablock ) + // that we have been modified and they might want to rebuild... + mReloadSignal.trigger(); +} + +bool GameBaseData::onAdd() +{ + if (!Parent::onAdd()) + return false; + + return true; +} + +void GameBaseData::initPersistFields() +{ + addGroup("Scripting"); + + addField( "category", TypeCaseString, Offset( category, GameBaseData ), + "The group that this datablock will show up in under the \"Scripted\" " + "tab in the World Editor Library." ); + + endGroup("Scripting"); + + Parent::initPersistFields(); +} + +bool GameBaseData::preload(bool server, String &errorStr) +{ + if (!Parent::preload(server, errorStr)) + return false; + packed = false; + return true; +} + +void GameBaseData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + packed = true; +} + +//---------------------------------------------------------------------------- +bool UNPACK_DB_ID(BitStream * stream, U32 & id) +{ + if (stream->readFlag()) + { + id = stream->readRangedU32(DataBlockObjectIdFirst,DataBlockObjectIdLast); + return true; + } + return false; +} + +bool PACK_DB_ID(BitStream * stream, U32 id) +{ + if (stream->writeFlag(id)) + { + stream->writeRangedU32(id,DataBlockObjectIdFirst,DataBlockObjectIdLast); + return true; + } + return false; +} + +bool PRELOAD_DB(U32 & id, SimDataBlock ** data, bool server, const char * clientMissing, const char * serverMissing) +{ + if (server) + { + if (*data) + id = (*data)->getId(); + else if (server && serverMissing) + { + Con::errorf(ConsoleLogEntry::General,serverMissing); + return false; + } + } + else + { + if (id && !Sim::findObject(id,*data) && clientMissing) + { + Con::errorf(ConsoleLogEntry::General,clientMissing); + return false; + } + } + return true; +} +//---------------------------------------------------------------------------- + +bool GameBase::gShowBoundingBox = false; + +//---------------------------------------------------------------------------- +IMPLEMENT_CO_NETOBJECT_V1(GameBase); + +ConsoleDocClass( GameBase, + "@brief Base class for game objects which use datablocks, networking, are " + "editable, and need to process ticks.\n\n" + "@ingroup gameObjects\n" +); + +GameBase::GameBase() +: mDataBlock( NULL ), + mControllingClient( NULL ), + mCurrentWaterObject( NULL ) +{ + mNetFlags.set(Ghostable); + mTypeMask |= GameBaseObjectType; + mProcessTag = 0; + + // From ProcessObject + mIsGameBase = true; + +#ifdef TORQUE_DEBUG_NET_MOVES + mLastMoveId = 0; + mTicksSinceLastMove = 0; + mIsAiControlled = false; +#endif +} + +GameBase::~GameBase() +{ +} + + +//---------------------------------------------------------------------------- + +bool GameBase::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + // Datablock must be initialized on the server. + // Client datablock are initialized by the initial update. + if ( isServerObject() && mDataBlock && !onNewDataBlock( mDataBlock, false ) ) + return false; + + setProcessTick( true ); + + return true; +} + +void GameBase::onRemove() +{ + // EDITOR FEATURE: Remove us from the reload signal of our datablock. + if ( mDataBlock ) + mDataBlock->mReloadSignal.remove( this, &GameBase::_onDatablockModified ); + + Parent::onRemove(); +} + +bool GameBase::onNewDataBlock( GameBaseData *dptr, bool reload ) +{ + // EDITOR FEATURE: Remove us from old datablock's reload signal and + // add us to the new one. + if ( !reload ) + { + if ( mDataBlock ) + mDataBlock->mReloadSignal.remove( this, &GameBase::_onDatablockModified ); + if ( dptr ) + dptr->mReloadSignal.notify( this, &GameBase::_onDatablockModified ); + } + + + mDataBlock = dptr; + + if ( !mDataBlock ) + return false; + + setMaskBits(DataBlockMask); + return true; +} + +void GameBase::_onDatablockModified() +{ + AssertFatal( mDataBlock, "GameBase::onDatablockModified - mDataBlock is NULL." ); + onNewDataBlock( mDataBlock, true ); +} + +void GameBase::inspectPostApply() +{ + Parent::inspectPostApply(); + setMaskBits(ExtendedInfoMask); +} + +//---------------------------------------------------------------------------- + +void GameBase::processTick(const Move * move) +{ +#ifdef TORQUE_DEBUG_NET_MOVES + if (!move) + mTicksSinceLastMove++; + + const char * srv = isClientObject() ? "client" : "server"; + const char * who = ""; + if (isClientObject()) + { + if (this == (GameBase*)GameConnection::getConnectionToServer()->getControlObject()) + who = " player"; + else + who = " ghost"; + if (mIsAiControlled) + who = " ai"; + } + if (isServerObject()) + { + if (dynamic_cast(getControllingClient())) + { + who = " ai"; + mIsAiControlled = true; + } + else if (getControllingClient()) + { + who = " player"; + mIsAiControlled = false; + } + else + { + who = ""; + mIsAiControlled = false; + } + } + U32 moveid = mLastMoveId+mTicksSinceLastMove; + if (move) + moveid = move->id; + + if (getTypeMask() & GameBaseHiFiObjectType) + { + if (move) + Con::printf("Processing (%s%s id %i) move %i",srv,who,getId(), move->id); + else + Con::printf("Processing (%s%s id %i) move %i (%i)",srv,who,getId(),mLastMoveId+mTicksSinceLastMove,mTicksSinceLastMove); + } + + if (move) + { + mLastMoveId = move->id; + mTicksSinceLastMove=0; + } +#endif +} + +//---------------------------------------------------------------------------- + +F32 GameBase::getUpdatePriority(CameraScopeQuery *camInfo, U32 updateMask, S32 updateSkips) +{ + TORQUE_UNUSED(updateMask); + + // Calculate a priority used to decide if this object + // will be updated on the client. All the weights + // are calculated 0 -> 1 Then weighted together at the + // end to produce a priority. + Point3F pos; + getWorldBox().getCenter(&pos); + pos -= camInfo->pos; + F32 dist = pos.len(); + if (dist == 0.0f) dist = 0.001f; + pos *= 1.0f / dist; + + // Weight based on linear distance, the basic stuff. + F32 wDistance = (dist < camInfo->visibleDistance)? + 1.0f - (dist / camInfo->visibleDistance): 0.0f; + + // Weight by field of view, objects directly in front + // will be weighted 1, objects behind will be 0 + F32 dot = mDot(pos,camInfo->orientation); + bool inFov = dot > camInfo->cosFov; + F32 wFov = inFov? 1.0f: 0; + + // Weight by linear velocity parallel to the viewing plane + // (if it's the field of view, 0 if it's not). + F32 wVelocity = 0.0f; + if (inFov) + { + Point3F vec; + mCross(camInfo->orientation,getVelocity(),&vec); + wVelocity = (vec.len() * camInfo->fov) / + (camInfo->fov * camInfo->visibleDistance); + if (wVelocity > 1.0f) + wVelocity = 1.0f; + } + + // Weight by interest. + F32 wInterest; + if (getTypeMask() & PlayerObjectType) + wInterest = 0.75f; + else if (getTypeMask() & ProjectileObjectType) + { + // Projectiles are more interesting if they + // are heading for us. + wInterest = 0.30f; + F32 dot = -mDot(pos,getVelocity()); + if (dot > 0.0f) + wInterest += 0.20 * dot; + } + else + { + if (getTypeMask() & ItemObjectType) + wInterest = 0.25f; + else + // Everything else is less interesting. + wInterest = 0.0f; + } + + // Weight by updateSkips + F32 wSkips = updateSkips * 0.5; + + // Calculate final priority, should total to about 1.0f + // + return + wFov * sUpFov + + wDistance * sUpDistance + + wVelocity * sUpVelocity + + wSkips * sUpSkips + + wInterest * sUpInterest; +} + +//---------------------------------------------------------------------------- +bool GameBase::setDataBlock(GameBaseData* dptr) +{ + if (isGhost() || isProperlyAdded()) { + if (mDataBlock != dptr) + return onNewDataBlock(dptr,false); + } + else + mDataBlock = dptr; + return true; +} + + +//-------------------------------------------------------------------------- +void GameBase::scriptOnAdd() +{ + // Script onAdd() must be called by the leaf class after + // everything is ready. + if (mDataBlock && !isGhost()) + mDataBlock->onAdd_callback( this ); +} + +void GameBase::scriptOnNewDataBlock() +{ + // Script onNewDataBlock() must be called by the leaf class + // after everything is loaded. + if (mDataBlock && !isGhost()) + mDataBlock->onNewDataBlock_callback( this ); +} + +void GameBase::scriptOnRemove() +{ + // Script onRemove() must be called by leaf class while + // the object state is still valid. + if (!isGhost() && mDataBlock) + mDataBlock->onRemove_callback( this ); +} + +//---------------------------------------------------------------------------- + +void GameBase::setControllingClient(GameConnection* client) +{ + if (isClientObject()) + { + if (mControllingClient) + setControl_callback( 0 ); + if (client) + setControl_callback( 1 ); + } + + mControllingClient = client; +} + +U32 GameBase::getPacketDataChecksum(GameConnection * connection) +{ + // just write the packet data into a buffer + // then we can CRC the buffer. This should always let us + // know when there is a checksum problem. + + static U8 buffer[1500] = { 0, }; + BitStream stream(buffer, sizeof(buffer)); + + writePacketData(connection, &stream); + U32 byteCount = stream.getPosition(); + U32 ret = CRC::calculateCRC(buffer, byteCount, 0xFFFFFFFF); + dMemset(buffer, 0, byteCount); + return ret; +} + +void GameBase::writePacketData(GameConnection*, BitStream*) +{ +} + +void GameBase::readPacketData(GameConnection*, BitStream*) +{ +} + +U32 GameBase::packUpdate( NetConnection *connection, U32 mask, BitStream *stream ) +{ + U32 retMask = Parent::packUpdate( connection, mask, stream ); + + if ( stream->writeFlag( mask & ScaleMask ) ) + { + // Only write one bit if the scale is one. + if ( stream->writeFlag( mObjScale != Point3F::One ) ) + mathWrite( *stream, mObjScale ); + } + + if ( stream->writeFlag( ( mask & DataBlockMask ) && mDataBlock != NULL ) ) + { + stream->writeRangedU32( mDataBlock->getId(), + DataBlockObjectIdFirst, + DataBlockObjectIdLast ); + if ( stream->writeFlag( mNetFlags.test( NetOrdered ) ) ) + stream->writeInt( mOrderGUID, 16 ); + } + +#ifdef TORQUE_DEBUG_NET_MOVES + stream->write(mLastMoveId); + stream->writeFlag(mIsAiControlled); +#endif + + return retMask; +} + +void GameBase::unpackUpdate(NetConnection *con, BitStream *stream) +{ + Parent::unpackUpdate( con, stream ); + + // ScaleMask + if ( stream->readFlag() ) + { + if ( stream->readFlag() ) + { + VectorF scale; + mathRead( *stream, &scale ); + setScale( scale ); + } + else + setScale( Point3F::One ); + } + + // DataBlockMask + if ( stream->readFlag() ) + { + GameBaseData *dptr = 0; + SimObjectId id = stream->readRangedU32( DataBlockObjectIdFirst, + DataBlockObjectIdLast ); + if ( stream->readFlag() ) + mOrderGUID = stream->readInt( 16 ); + + if ( !Sim::findObject( id, dptr ) || !setDataBlock( dptr ) ) + con->setLastError( "Invalid packet GameBase::unpackUpdate()" ); + } + +#ifdef TORQUE_DEBUG_NET_MOVES + stream->read(&mLastMoveId); + mTicksSinceLastMove = 0; + mIsAiControlled = stream->readFlag(); +#endif +} + +void GameBase::onMount( SceneObject *obj, S32 node ) +{ + deleteNotify( obj ); + + // Are we mounting to a GameBase object? + GameBase *gbaseObj = dynamic_cast( obj ); + + if ( gbaseObj && gbaseObj->getControlObject() != this ) + processAfter( gbaseObj ); + + if (!isGhost()) { + setMaskBits(MountedMask); + mDataBlock->onMount_callback( this, obj, node ); + } +} + +void GameBase::onUnmount( SceneObject *obj, S32 node ) +{ + clearNotify(obj); + + GameBase *gbaseObj = dynamic_cast( obj ); + + if ( gbaseObj && gbaseObj->getControlObject() != this ) + clearProcessAfter(); + + if (!isGhost()) { + setMaskBits(MountedMask); + mDataBlock->onUnmount_callback( this, obj, node ); + } +} + +bool GameBase::setDataBlockProperty( void *obj, const char *index, const char *db) +{ + if( db == NULL || !db || !db[ 0 ] ) + { + Con::errorf( "GameBase::setDataBlockProperty - Can't unset datablock on GameBase objects" ); + return false; + } + + GameBase* object = static_cast< GameBase* >( obj ); + GameBaseData* data; + if( Sim::findObject( db, data ) ) + return object->setDataBlock( data ); + + Con::errorf( "GameBase::setDatablockProperty - Could not find data block \"%s\"", db ); + return false; +} + +MoveList* GameBase::getMoveList() +{ + return mControllingClient ? mControllingClient->mMoveList : NULL; +} + +//---------------------------------------------------------------------------- +DefineEngineMethod( GameBase, getDataBlock, S32, (),, + "@brief Get the datablock used by this object.\n\n" + "@return the datablock this GameBase is using." + "@see setDataBlock()\n") +{ + return object->getDataBlock()? object->getDataBlock()->getId(): 0; +} + +//---------------------------------------------------------------------------- +DefineEngineMethod( GameBase, setDataBlock, bool, ( GameBaseData* data ),, + "@brief Assign this GameBase to use the specified datablock.\n\n" + "@param data new datablock to use\n" + "@return true if successful, false if failed." + "@see getDataBlock()\n") +{ + return ( data && object->setDataBlock(data) ); +} + +//---------------------------------------------------------------------------- + +void GameBase::initPersistFields() +{ + addGroup( "Game" ); + + addProtectedField( "dataBlock", TYPEID< GameBaseData >(), Offset(mDataBlock, GameBase), + &setDataBlockProperty, &defaultProtectedGetFn, + "Script datablock used for game objects." ); + + endGroup( "Game" ); + + Parent::initPersistFields(); +} + +void GameBase::consoleInit() +{ +#ifdef TORQUE_DEBUG + Con::addVariable( "GameBase::boundingBox", TypeBool, &gShowBoundingBox, + "@brief Toggles on the rendering of the bounding boxes for certain types of objects in scene.\n\n" + "@ingroup GameBase" ); +#endif +} + +DefineEngineMethod( GameBase, applyImpulse, bool, ( Point3F pos, VectorF vel ),, + "@brief Apply an impulse to this object as defined by a world position and velocity vector.\n\n" + + "@param pos impulse world position\n" + "@param vel impulse velocity (impulse force F = m * v)\n" + "@return Always true\n" + + "@note Not all objects that derrive from GameBase have this defined.\n") +{ + object->applyImpulse(pos,vel); + return true; +} + +DefineEngineMethod( GameBase, applyRadialImpulse, void, ( Point3F origin, F32 radius, F32 magnitude ),, + "@brief Applies a radial impulse to the object using the given origin and force.\n\n" + + "@param origin World point of origin of the radial impulse.\n" + "@param radius The radius of the impulse area.\n" + "@param magnitude The strength of the impulse.\n" + + "@note Not all objects that derrive from GameBase have this defined.\n") +{ + object->applyRadialImpulse( origin, radius, magnitude ); +} diff --git a/Engine/source/T3D/gameBase/gameBase.h b/Engine/source/T3D/gameBase/gameBase.h new file mode 100644 index 000000000..68354cdf0 --- /dev/null +++ b/Engine/source/T3D/gameBase/gameBase.h @@ -0,0 +1,451 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GAMEBASE_H_ +#define _GAMEBASE_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _PROCESSLIST_H_ +#include "T3D/gameBase/processList.h" +#endif +#ifndef _TICKCACHE_H_ +#include "T3D/gameBase/tickCache.h" +#endif +#ifndef _DYNAMIC_CONSOLETYPES_H_ +#include "console/dynamicTypes.h" +#endif + +class NetConnection; +class ProcessList; +class GameBase; +struct Move; + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +/// Scriptable, demo-able datablock. +/// +/// This variant of SimDataBlock performs these additional tasks: +/// - Linking datablock's namepsaces to the namespace of their C++ class, so +/// that datablocks can expose script functionality. +/// - Linking datablocks to a user defined scripting namespace, by setting the +/// 'class' field at datablock definition time. +/// - Adds a category field; this is used by the world creator in the editor to +/// classify creatable shapes. Creatable shapes are placed under the Shapes +/// node in the treeview for this; additional levels are created, named after +/// the category fields. +/// - Adds support for demo stream recording. This support takes the form +/// of the member variable packed. When a demo is being recorded by a client, +/// data is unpacked, then packed again to the data stream, then, in the case +/// of datablocks, preload() is called to process the data. It is occasionally +/// the case that certain references in the datablock stream cannot be resolved +/// until preload is called, in which case a raw ID field is stored in the variable +/// which will eventually be used to store a pointer to the object. However, if +/// packData() is called before we resolve this ID, trying to call getID() on the +/// objecct ID would be a fatal error. Therefore, in these cases, we test packed; +/// if it is true, then we know we have to write the raw data, instead of trying +/// to resolve an ID. +/// +/// @see SimDataBlock for further details about datablocks. +/// @see http://hosted.tribalwar.com/t2faq/datablocks.shtml for an excellent +/// explanation of the basics of datablocks from a scripting perspective. +/// @nosubgrouping +struct GameBaseData : public SimDataBlock +{ +private: + + typedef SimDataBlock Parent; + +public: + + bool packed; + StringTableEntry category; + + // Signal triggered when this datablock is modified. + // GameBase objects referencing this datablock notify with this signal. + Signal mReloadSignal; + + // Triggers the reload signal. + void inspectPostApply(); + + bool onAdd(); + + // The derived class should provide the following: + DECLARE_CONOBJECT(GameBaseData); + GameBaseData(); + static void initPersistFields(); + bool preload(bool server, String &errorStr); + void unpackData(BitStream* stream); + + /// @name Callbacks + /// @{ + DECLARE_CALLBACK( void, onAdd, ( GameBase* obj ) ); + DECLARE_CALLBACK( void, onRemove, ( GameBase* obj ) ); + DECLARE_CALLBACK( void, onNewDataBlock, ( GameBase* obj ) ); + DECLARE_CALLBACK( void, onMount, ( GameBase* obj, SceneObject* mountObj, S32 node ) ); + DECLARE_CALLBACK( void, onUnmount, ( GameBase* obj, SceneObject* mountObj, S32 node ) ); + /// @} +}; + +//---------------------------------------------------------------------------- +// A few utility methods for sending datablocks over the net +//---------------------------------------------------------------------------- + +bool UNPACK_DB_ID(BitStream *, U32 & id); +bool PACK_DB_ID(BitStream *, U32 id); +bool PRELOAD_DB(U32 & id, SimDataBlock **, bool server, const char * clientMissing = NULL, const char * serverMissing = NULL); + +//---------------------------------------------------------------------------- +class GameConnection; +class WaterObject; +class MoveList; + +// For truly it is written: "The wise man extends GameBase for his purposes, +// while the fool has the ability to eject shell casings from the belly of his +// dragon." -- KillerBunny + +/// Base class for game objects which use datablocks, networking, are editable, +/// and need to process ticks. +/// +/// @section GameBase_process GameBase and ProcessList +/// +/// GameBase adds two kinds of time-based updates. Torque works off of a concept +/// of ticks. Ticks are slices of time 32 milliseconds in length. There are three +/// methods which are used to update GameBase objects that are registered with +/// the ProcessLists: +/// - processTick(Move*) is called on each object once for every tick, regardless +/// of the "real" framerate. +/// - interpolateTick(float) is called on client objects when they need to interpolate +/// to match the next tick. +/// - advanceTime(float) is called on client objects so they can do time-based behaviour, +/// like updating animations. +/// +/// Torque maintains a server and a client processing list; in a local game, both +/// are populated, while in multiplayer situations, either one or the other is +/// populated. +/// +/// You can control whether an object is considered for ticking by means of the +/// setProcessTick() method. +/// +/// @section GameBase_datablock GameBase and Datablocks +/// +/// GameBase adds support for datablocks. Datablocks are secondary classes which store +/// static data for types of game elements. For instance, this means that all "light human +/// male armor" type Players share the same datablock. Datablocks typically store not only +/// raw data, but perform precalculations, like finding nodes in the game model, or +/// validating movement parameters. +/// +/// There are three parts to the datablock interface implemented in GameBase: +/// - getDataBlock(), which gets a pointer to the current datablock. This is +/// mostly for external use; for in-class use, it's better to directly access the +/// mDataBlock member. +/// - setDataBlock(), which sets mDataBlock to point to a new datablock; it +/// uses the next part of the interface to inform subclasses of this. +/// - onNewDataBlock() is called whenever a new datablock is assigned to a GameBase. +/// +/// Datablocks are also usable through the scripting language. +/// +/// @see SimDataBlock for more details. +/// +/// @section GameBase_networking GameBase and Networking +/// +/// writePacketData() and readPacketData() are called to transfer information needed for client +/// side prediction. They are usually used when updating a client of its control object state. +/// +/// Subclasses of GameBase usually transmit positional and basic status data in the packUpdate() +/// functions, while giving velocity, momentum, and similar state information in the writePacketData(). +/// +/// writePacketData()/readPacketData() are called in addition to packUpdate/unpackUpdate(). +/// +/// @nosubgrouping +class GameBase : public SceneObject +{ + typedef SceneObject Parent; + + /// @name Datablock + /// @{ + + GameBaseData* mDataBlock; + + /// @} + + TickCache mTickCache; + + // Control interface + GameConnection* mControllingClient; + +public: + + static bool gShowBoundingBox; ///< Should we render bounding boxes? + +protected: + + F32 mCameraFov; + + /// The WaterObject we are currently within. + WaterObject *mCurrentWaterObject; + + static bool setDataBlockProperty( void *object, const char *index, const char *data ); + +#ifdef TORQUE_DEBUG_NET_MOVES + U32 mLastMoveId; + U32 mTicksSinceLastMove; + bool mIsAiControlled; +#endif + +public: + + GameBase(); + ~GameBase(); + + enum GameBaseMasks { + DataBlockMask = Parent::NextFreeMask << 0, + ExtendedInfoMask = Parent::NextFreeMask << 1, + NextFreeMask = Parent::NextFreeMask << 2 + }; + + // net flags added by game base + enum + { + NetOrdered = BIT(Parent::MaxNetFlagBit+1), /// Process in same order on client and server. + NetNearbyAdded = BIT(Parent::MaxNetFlagBit+2), /// Is set during client catchup when neighbors have been checked. + GhostUpdated = BIT(Parent::MaxNetFlagBit+3), /// Is set whenever ghost updated (and reset) on the client, for hifi objects. + TickLast = BIT(Parent::MaxNetFlagBit+4), /// Tick this object after all others. + NewGhost = BIT(Parent::MaxNetFlagBit+5), /// This ghost was just added during the last update. + HiFiPassive = BIT(Parent::MaxNetFlagBit+6), /// Do not interact with other hifi passive objects. + MaxNetFlagBit = Parent::MaxNetFlagBit+6 + }; + + /// @name Inherited Functionality. + /// @{ + + bool onAdd(); + void onRemove(); + void inspectPostApply(); + static void initPersistFields(); + static void consoleInit(); + + /// @} + + ///@name Datablock + ///@{ + + /// Assigns this object a datablock and loads attributes with onNewDataBlock. + /// + /// @see onNewDataBlock + /// @param dptr Datablock + bool setDataBlock( GameBaseData *dptr ); + + /// Returns the datablock for this object. + GameBaseData* getDataBlock() { return mDataBlock; } + + /// Called when a new datablock is set. This allows subclasses to + /// appropriately handle new datablocks. + /// + /// @see setDataBlock() + /// @param dptr New datablock + /// @param reload Is this a new datablock or are we reloading one + /// we already had. + virtual bool onNewDataBlock( GameBaseData *dptr, bool reload ); + ///@} + + /// @name Script + /// The scriptOnXX methods are invoked by the leaf classes + /// @{ + + /// Executes the 'onAdd' script function for this object. + /// @note This must be called after everything is ready + void scriptOnAdd(); + + /// Executes the 'onNewDataBlock' script function for this object. + /// + /// @note This must be called after everything is loaded. + void scriptOnNewDataBlock(); + + /// Executes the 'onRemove' script function for this object. + /// @note This must be called while the object is still valid + void scriptOnRemove(); + + /// @} + + // ProcessObject override + void processTick( const Move *move ); + + /// @name GameBase NetFlags & Hifi-Net Interface + /// @{ + + /// Set or clear the GhostUpdated bit in our NetFlags. + /// @see GhostUpdated + void setGhostUpdated( bool b ) { if (b) mNetFlags.set(GhostUpdated); else mNetFlags.clear(GhostUpdated); } + + /// Returns true if the GhostUpdated bit in our NetFlags is set. + /// @see GhostUpdated + bool isGhostUpdated() const { return mNetFlags.test(GhostUpdated); } + + /// Set or clear the NewGhost bit in our NetFlags. + /// @see NewGhost + void setNewGhost( bool n ) { if (n) mNetFlags.set(NewGhost); else mNetFlags.clear(NewGhost); } + + /// Returns true if the NewGhost bit in out NetFlags is set. + /// @see NewGhost + bool isNewGhost() const { return mNetFlags.test(NewGhost); } + + /// Set or clear the NetNearbyAdded bit in our NetFlags. + /// @see NetNearbyAdded + void setNetNearbyAdded( bool b ) { if (b) mNetFlags.set(NetNearbyAdded); else mNetFlags.clear(NetNearbyAdded); } + + /// Returns true if the NetNearby bit in our NetFlags is set. + /// @see NetNearbyAdded + bool isNetNearbyAdded() const { return mNetFlags.test(NetNearbyAdded); } + + /// Returns true if the HiFiPassive bit in our NetFlags is set. + /// @see HiFiPassive + bool isHifiPassive() const { return mNetFlags.test(HiFiPassive); } + + /// Returns true if the TickLast bit in our NetFlags is set. + /// @see TickLast + bool isTickLast() const { return mNetFlags.test(TickLast); } + + /// Returns true if the NetOrdered bit in our NetFlags is set. + /// @see NetOrdered + bool isNetOrdered() const { return mNetFlags.test(NetOrdered); } + + /// Called during client catchup under the hifi-net model. + virtual void computeNetSmooth( F32 backDelta ) {} + + /// Returns TickCache used under the hifi-net model. + TickCache& getTickCache() { return mTickCache; } + /// @} + + /// @name Network + /// @see NetObject, NetConnection + /// @{ + + F32 getUpdatePriority( CameraScopeQuery *focusObject, U32 updateMask, S32 updateSkips ); + U32 packUpdate ( NetConnection *conn, U32 mask, BitStream *stream ); + void unpackUpdate( NetConnection *conn, BitStream *stream ); + + /// Write state information necessary to perform client side prediction of an object. + /// + /// This information is sent only to the controlling object. For example, if you are a client + /// controlling a Player, the server uses writePacketData() instead of packUpdate() to + /// generate the data you receive. + /// + /// @param conn Connection for which we're generating this data. + /// @param stream Bitstream for output. + virtual void writePacketData( GameConnection *conn, BitStream *stream ); + + /// Read data written with writePacketData() and update the object state. + /// + /// @param conn Connection for which we're generating this data. + /// @param stream Bitstream to read. + virtual void readPacketData( GameConnection *conn, BitStream *stream ); + + /// Gets the checksum for packet data. + /// + /// Basically writes a packet, does a CRC check on it, and returns + /// that CRC. + /// + /// @see writePacketData + /// @param conn Game connection + virtual U32 getPacketDataChecksum( GameConnection *conn ); + ///@} + + + /// @name Mounted objects ( overrides ) + /// @{ + +public: + + virtual void onMount( SceneObject *obj, S32 node ); + virtual void onUnmount( SceneObject *obj,S32 node ); + + /// @} + + /// @name User control + /// @{ + + /// Returns the client controlling this object + GameConnection *getControllingClient() { return mControllingClient; } + const GameConnection *getControllingClient() const { return mControllingClient; } + + /// Returns the MoveList of the client controlling this object. + /// If there is no client it returns NULL; + MoveList* getMoveList(); + + /// Sets the client controlling this object + /// @param client Client that is now controlling this object + virtual void setControllingClient( GameConnection *client ); + + virtual GameBase * getControllingObject() { return NULL; } + virtual GameBase * getControlObject() { return NULL; } + virtual void setControlObject( GameBase * ) { } + /// @} + + virtual F32 getDefaultCameraFov() { return 90.f; } + virtual F32 getCameraFov() { return 90.f; } + virtual void setCameraFov( F32 fov ) { } + virtual bool isValidCameraFov( F32 fov ) { return true; } + virtual bool useObjsEyePoint() const { return false; } + virtual bool onlyFirstPerson() const { return false; } + virtual F32 getDamageFlash() const { return 1.0f; } + virtual F32 getWhiteOut() const { return 1.0f; } + + // Not implemented here, but should return the Camera to world transformation matrix + virtual void getCameraTransform (F32 *pos, MatrixF *mat ) { *mat = MatrixF::Identity; } + + /// Returns the water object we are colliding with, it is up to derived + /// classes to actually set this object. + virtual WaterObject* getCurrentWaterObject() { return mCurrentWaterObject; } + + #ifdef TORQUE_DEBUG_NET_MOVES + bool isAIControlled() const { return mIsAiControlled; } + #endif + + DECLARE_CONOBJECT (GameBase ); + + /// @name Callbacks + /// @{ + DECLARE_CALLBACK( void, setControl, ( bool controlled ) ); + /// @} + +private: + + /// This is called by the reload signal in our datablock when it is + /// modified in the editor. + /// + /// This method is private and is not virtual. To handle a datablock-modified + /// even in a child-class specific way you should override onNewDatablock + /// and handle the reload( true ) case. + /// + /// Warning: For local-client, editor situations only. + /// + /// Warning: Do not attempt to call .remove or .notify on mDataBlock->mReloadSignal + /// within this callback. + /// + void _onDatablockModified(); +}; + + +#endif // _GAMEBASE_H_ diff --git a/Engine/source/T3D/gameBase/gameConnection.cpp b/Engine/source/T3D/gameBase/gameConnection.cpp new file mode 100644 index 000000000..afd1f9e25 --- /dev/null +++ b/Engine/source/T3D/gameBase/gameConnection.cpp @@ -0,0 +1,2105 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/gameBase/gameConnection.h" + +#include "platform/profiler.h" +#include "core/dnet.h" +#include "core/util/safeDelete.h" +#include "core/stream/bitStream.h" +#include "console/consoleTypes.h" +#include "console/simBase.h" +#include "sfx/sfxProfile.h" +#include "sfx/sfxDescription.h" +#include "app/game.h" +#include "app/auth.h" +#include "T3D/camera.h" +#include "T3D/gameBase/gameProcess.h" +#include "T3D/gameBase/gameConnectionEvents.h" +#include "console/engineAPI.h" +#include "math/mTransform.h" + +#ifdef TORQUE_HIFI_NET + #include "T3D/gameBase/hifi/hifiMoveList.h" +#else + #include "T3D/gameBase/std/stdMoveList.h" +#endif + +//---------------------------------------------------------------------------- +#define MAX_MOVE_PACKET_SENDS 4 + +#define ControlRequestTime 5000 + +const U32 GameConnection::CurrentProtocolVersion = 12; +const U32 GameConnection::MinRequiredProtocolVersion = 12; + +//---------------------------------------------------------------------------- + +IMPLEMENT_CONOBJECT(GameConnection); +S32 GameConnection::mLagThresholdMS = 0; +Signal GameConnection::smFovUpdate; +Signal GameConnection::smPlayingDemo; + +ConsoleDocClass( GameConnection, + "@brief The game-specific subclass of NetConnection.\n\n" + + "The GameConnection introduces the concept of the control object. The control object " + "is simply the object that the client is associated with that network connection controls. By " + "default the control object is an instance of the Player class, but can also be an instance " + "of Camera (when editing the mission, for example), or any other ShapeBase derived class as " + "appropriate for the game.\n\n" + + "Torque uses a model in which the server is the authoritative master of the simulation. To " + "prevent clients from cheating, the server simulates all player moves and then tells the " + "client where his player is in the world. This model, while secure, can have problems. If " + "the network latency is high, this round-trip time can give the player a very noticeable sense " + "of movement lag. To correct this problem, the game uses a form of prediction - it simulates " + "the movement of the control object on the client and on the server both. This way the client " + "doesn't need to wait for round-trip verification of his moves. Only in the case of a force " + "acting on the control object on the server that doesn't exist on the client does the client's " + "position need to be forcefully changed.\n\n" + + "To support this, all control objects (derivative of ShapeBase) must supply a writePacketData() " + "and readPacketData() function that send enough data to accurately simulate the object on the " + "client. These functions are only called for the current control object, and only when the " + "server can determine that the client's simulation is somehow out of sync with the server. This " + "occurs usually if the client is affected by a force not present on the server (like an " + "interpolating object) or if the server object is affected by a server only force (such as the " + "impulse from an explosion).\n\n" + + "The Move structure is a 32 millisecond snapshot of player input, containing x, y, and z " + "positional and rotational changes as well as trigger state changes. When time passes in the " + "simulation moves are collected (depending on how much time passes), and applied to the current " + "control object on the client. The same moves are then packed over to the server in " + "GameConnection::writePacket(), for processing on the server's version of the control object.\n\n" + + "@see @ref Networking, NetConnection, ShapeBase\n\n" + + "@ingroup Networking\n"); + +//---------------------------------------------------------------------------- +IMPLEMENT_CALLBACK( GameConnection, onConnectionTimedOut, void, (), (), + "@brief Called on the client when the connection to the server times out.\n\n"); + +IMPLEMENT_CALLBACK( GameConnection, onConnectionAccepted, void, (), (), + "@brief Called on the client when the connection to the server has been established.\n\n"); + +IMPLEMENT_CALLBACK( GameConnection, onConnectRequestTimedOut, void, (), (), + "@brief Called when connection attempts have timed out.\n\n"); + +IMPLEMENT_CALLBACK( GameConnection, onConnectionDropped, void, (const char* reason), (reason), + "@brief Called on the client when the connection to the server has been dropped.\n\n" + "@param reason The reason why the connection was dropped.\n\n"); + +IMPLEMENT_CALLBACK( GameConnection, onConnectRequestRejected, void, (const char* reason), (reason), + "@brief Called on the client when the connection to the server has been rejected.\n\n" + "@param reason The reason why the connection request was rejected.\n\n"); + +IMPLEMENT_CALLBACK( GameConnection, onConnectionError, void, (const char* errorString), (errorString), + "@brief Called on the client when there is an error with the connection to the server.\n\n" + "@param errorString The connection error text.\n\n"); + +IMPLEMENT_CALLBACK( GameConnection, onDrop, void, (const char* disconnectReason), (disconnectReason), + "@brief Called on the server when the client's connection has been dropped.\n\n" + "@param disconnectReason The reason why the connection was dropped.\n\n"); + +IMPLEMENT_CALLBACK( GameConnection, initialControlSet, void, (), (), + "@brief Called on the client when the first control object has been set by the " + "server and we are now ready to go.\n\n" + "A common action to perform when this callback is called is to switch the GUI " + "canvas from the loading screen and over to the 3D game GUI."); + +IMPLEMENT_CALLBACK( GameConnection, onControlObjectChange, void, (), (), + "@brief Called on the client when the control object has been changed by the " + "server.\n\n"); + +IMPLEMENT_CALLBACK( GameConnection, setLagIcon, void, (bool state), (state), + "@brief Called on the client to display the lag icon.\n\n" + "When the connection with the server is lagging, this callback is called to " + "allow the game GUI to display some indicator to the player.\n\n" + "@param state Set to true if the lag icon should be displayed.\n\n"); + +IMPLEMENT_CALLBACK( GameConnection, onDataBlocksDone, void, (U32 sequence), (sequence), + "@brief Called on the server when all datablocks has been sent to the client.\n\n" + "During phase 1 of the mission download, all datablocks are sent from the server " + "to the client. Once all datablocks have been sent, this callback is called and " + "the mission download procedure may move on to the next phase.\n\n" + "@param sequence The sequence is common between the server and client and ensures " + "that the client is acting on the most recent mission start process. If an errant " + "network packet (one that was lost but has now been found) is received by the client " + "with an incorrect sequence, it is just ignored. This sequence number is updated on " + "the server every time a mission is loaded.\n\n" + "@see GameConnection::transmitDataBlocks()\n\n"); + +IMPLEMENT_GLOBAL_CALLBACK( onDataBlockObjectReceived, void, (U32 index, U32 total), (index, total), + "@brief Called on the client each time a datablock has been received.\n\n" + "This callback is typically used to notify the player of how far along " + "in the datablock download process they are.\n\n" + "@param index The index of the datablock just received.\n" + "@param total The total number of datablocks to be received.\n\n" + "@see GameConnection, GameConnection::transmitDataBlocks(), GameConnection::onDataBlocksDone()\n\n" + "@ingroup Networking\n"); + +IMPLEMENT_CALLBACK( GameConnection, onFlash, void, (bool state), (state), + "@brief Called on the client when the damage flash or white out states change.\n\n" + "When the server changes the damage flash or white out values, this callback is called " + "either is on or both are off. Typically this is used to enable the flash postFx.\n\n" + "@param state Set to true if either the damage flash or white out conditions are active.\n\n"); + +//---------------------------------------------------------------------------- +GameConnection::GameConnection() +{ + mLagging = false; + mControlObject = NULL; + mCameraObject = NULL; + +#ifdef TORQUE_HIFI_NET + mMoveList = new HifiMoveList(); +#else + mMoveList = new StdMoveList(); +#endif + + mMoveList->setConnection( this ); + + mDataBlockModifiedKey = 0; + mMaxDataBlockModifiedKey = 0; + mAuthInfo = NULL; + mControlForceMismatch = false; + mConnectArgc = 0; + + for(U32 i = 0; i < MaxConnectArgs; i++) + mConnectArgv[i] = 0; + + mJoinPassword = NULL; + + mMissionCRC = 0xffffffff; + + mDamageFlash = mWhiteOut = 0; + + mCameraPos = 0; + mCameraSpeed = 10; + + mCameraFov = 90.f; + mUpdateCameraFov = false; + + mAIControlled = false; + + mDisconnectReason[0] = 0; + + //blackout vars + mBlackOut = 0.0f; + mBlackOutTimeMS = 0; + mBlackOutStartTimeMS = 0; + mFadeToBlack = false; + + // first person + mFirstPerson = true; + mUpdateFirstPerson = false; +} + +GameConnection::~GameConnection() +{ + delete mAuthInfo; + for(U32 i = 0; i < mConnectArgc; i++) + dFree(mConnectArgv[i]); + dFree(mJoinPassword); + delete mMoveList; +} + +//---------------------------------------------------------------------------- + +bool GameConnection::canRemoteCreate() +{ + return true; +} + +void GameConnection::setConnectArgs(U32 argc, const char **argv) +{ + if(argc > MaxConnectArgs) + argc = MaxConnectArgs; + mConnectArgc = argc; + for(U32 i = 0; i < mConnectArgc; i++) + mConnectArgv[i] = dStrdup(argv[i]); +} + +void GameConnection::setJoinPassword(const char *password) +{ + mJoinPassword = dStrdup(password); +} + +DefineEngineMethod( GameConnection, setJoinPassword, void, (const char* password),, + "@brief On the client, set the password that will be passed to the server.\n\n" + + "On the server, this password is compared with what is stored in $pref::Server::Password. " + "If $pref::Server::Password is empty then the client's sent password is ignored. Otherwise, " + "if the passed in client password and the server password do not match, the CHR_PASSWORD " + "error string is sent back to the client and the connection is immediately terminated.\n\n" + + "This password checking is performed quite early on in the connection request process so as " + "to minimize the impact of multiple failed attempts -- also known as hacking.") +{ + object->setJoinPassword(password); +} + +ConsoleMethod(GameConnection, setConnectArgs, void, 3, 17, + "(const char* args) @brief On the client, pass along a variable set of parameters to the server.\n\n" + + "Once the connection is established with the server, the server calls its onConnect() method " + "with the client's passed in parameters as aruments.\n\n" + + "@see GameConnection::onConnect()\n\n") +{ + object->setConnectArgs(argc - 2, argv + 2); +} + +void GameConnection::onTimedOut() +{ + if(isConnectionToServer()) + { + Con::printf("Connection to server timed out"); + onConnectionTimedOut_callback(); + } + else + { + Con::printf("Client %d timed out.", getId()); + setDisconnectReason("TimedOut"); + } + +} + +void GameConnection::onConnectionEstablished(bool isInitiator) +{ + if(isInitiator) + { + setGhostFrom(false); + setGhostTo(true); + setSendingEvents(true); + setTranslatesStrings(true); + setIsConnectionToServer(); + mServerConnection = this; + Con::printf("Connection established %d", getId()); + onConnectionAccepted_callback(); + } + else + { + setGhostFrom(true); + setGhostTo(false); + setSendingEvents(true); + setTranslatesStrings(true); + Sim::getClientGroup()->addObject(this); + mMoveList->init(); + + const char *argv[MaxConnectArgs + 2]; + argv[0] = "onConnect"; + for(U32 i = 0; i < mConnectArgc; i++) + argv[i + 2] = mConnectArgv[i]; + // NOTE: Need to fallback to Con::execute() as IMPLEMENT_CALLBACK does not + // support variadic functions. + Con::execute(this, mConnectArgc + 2, argv); + } +} + +void GameConnection::onConnectTimedOut() +{ + onConnectRequestTimedOut_callback(); +} + +void GameConnection::onDisconnect(const char *reason) +{ + if(isConnectionToServer()) + { + Con::printf("Connection with server lost."); + onConnectionDropped_callback(reason); + mMoveList->init(); + } + else + { + Con::printf("Client %d disconnected.", getId()); + setDisconnectReason(reason); + } +} + +void GameConnection::onConnectionRejected(const char *reason) +{ + onConnectRequestRejected_callback(reason); +} + +void GameConnection::handleStartupError(const char *errorString) +{ + onConnectRequestRejected_callback(errorString); +} + +void GameConnection::writeConnectAccept(BitStream *stream) +{ + Parent::writeConnectAccept(stream); + stream->write(getProtocolVersion()); +} + +bool GameConnection::readConnectAccept(BitStream *stream, const char **errorString) +{ + if(!Parent::readConnectAccept(stream, errorString)) + return false; + + U32 protocolVersion; + stream->read(&protocolVersion); + if(protocolVersion < MinRequiredProtocolVersion || protocolVersion > CurrentProtocolVersion) + { + *errorString = "CHR_PROTOCOL"; // this should never happen unless someone is faking us out. + return false; + } + return true; +} + +void GameConnection::writeConnectRequest(BitStream *stream) +{ + Parent::writeConnectRequest(stream); + stream->writeString(GameString); + stream->write(CurrentProtocolVersion); + stream->write(MinRequiredProtocolVersion); + stream->writeString(mJoinPassword); + + stream->write(mConnectArgc); + for(U32 i = 0; i < mConnectArgc; i++) + stream->writeString(mConnectArgv[i]); +} + +bool GameConnection::readConnectRequest(BitStream *stream, const char **errorString) +{ + if(!Parent::readConnectRequest(stream, errorString)) + return false; + U32 currentProtocol, minProtocol; + char gameString[256]; + stream->readString(gameString); + if(dStrcmp(gameString, GameString)) + { + *errorString = "CHR_GAME"; + return false; + } + + stream->read(¤tProtocol); + stream->read(&minProtocol); + + char joinPassword[256]; + stream->readString(joinPassword); + + if(currentProtocol < MinRequiredProtocolVersion) + { + *errorString = "CHR_PROTOCOL_LESS"; + return false; + } + if(minProtocol > CurrentProtocolVersion) + { + *errorString = "CHR_PROTOCOL_GREATER"; + return false; + } + setProtocolVersion(currentProtocol < CurrentProtocolVersion ? currentProtocol : CurrentProtocolVersion); + + const char *serverPassword = Con::getVariable("pref::Server::Password"); + if(serverPassword[0]) + { + if(dStrcmp(joinPassword, serverPassword)) + { + *errorString = "CHR_PASSWORD"; + return false; + } + } + + stream->read(&mConnectArgc); + if(mConnectArgc > MaxConnectArgs) + { + *errorString = "CR_INVALID_ARGS"; + return false; + } + const char *connectArgv[MaxConnectArgs + 3]; + for(U32 i = 0; i < mConnectArgc; i++) + { + char argString[256]; + stream->readString(argString); + mConnectArgv[i] = dStrdup(argString); + connectArgv[i + 3] = mConnectArgv[i]; + } + connectArgv[0] = "onConnectRequest"; + char buffer[256]; + Net::addressToString(getNetAddress(), buffer); + connectArgv[2] = buffer; + + // NOTE: Cannot convert over to IMPLEMENT_CALLBACK as it has variable args. + const char *ret = Con::execute(this, mConnectArgc + 3, connectArgv); + if(ret[0]) + { + *errorString = ret; + return false; + } + return true; +} +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- +void GameConnection::connectionError(const char *errorString) +{ + if(isConnectionToServer()) + { + Con::printf("Connection error: %s.", errorString); + onConnectionError_callback(errorString); + } + else + { + Con::printf("Client %d packet error: %s.", getId(), errorString); + setDisconnectReason("Packet Error."); + } + deleteObject(); +} + + +void GameConnection::setAuthInfo(const AuthInfo *info) +{ + mAuthInfo = new AuthInfo; + *mAuthInfo = *info; +} + +const AuthInfo *GameConnection::getAuthInfo() +{ + return mAuthInfo; +} + + +//---------------------------------------------------------------------------- + +void GameConnection::setControlObject(GameBase *obj) +{ + if(mControlObject == obj) + return; + + if(mControlObject && mControlObject != mCameraObject) + mControlObject->setControllingClient(0); + + if(obj) + { + // Nothing else is permitted to control this object. + if (GameBase* coo = obj->getControllingObject()) + coo->setControlObject(0); + if (GameConnection *con = obj->getControllingClient()) + { + if(this != con) + { + // was it controlled via camera or control + if(con->getControlObject() == obj) + con->setControlObject(0); + else + con->setCameraObject(0); + } + } + + // We are now the controlling client of this object. + obj->setControllingClient(this); + + // Update the camera's FOV to match the new control object + setControlCameraFov( obj->getCameraFov() ); + } + + // Okay, set our control object. + mControlObject = obj; + mControlForceMismatch = true; + + if(mCameraObject.isNull()) + setScopeObject(mControlObject); +} + +void GameConnection::setCameraObject(GameBase *obj) +{ + if(mCameraObject == obj) + return; + + if(mCameraObject && mCameraObject != mControlObject) + mCameraObject->setControllingClient(0); + + if(obj) + { + // nothing else is permitted to control this object + if(GameBase *coo = obj->getControllingObject()) + coo->setControlObject(0); + + if(GameConnection *con = obj->getControllingClient()) + { + if(this != con) + { + // was it controlled via camera or control + if(con->getControlObject() == obj) + con->setControlObject(0); + else + con->setCameraObject(0); + } + } + + // we are now the controlling client of this object + obj->setControllingClient(this); + } + + // Okay, set our camera object. + mCameraObject = obj; + + if(mCameraObject.isNull()) + setScopeObject(mControlObject); + else + { + setScopeObject(mCameraObject); + + // if this is a client then set the fov and active image + if(isConnectionToServer()) + { + F32 fov = mCameraObject->getDefaultCameraFov(); + //GameSetCameraFov(fov); + smFovUpdate.trigger(fov); + } + } +} + +GameBase* GameConnection::getCameraObject() +{ + // If there is no camera object, or if we're first person, return + // the control object. + if( !mControlObject.isNull() && (mCameraObject.isNull() || mFirstPerson)) + return mControlObject; + return mCameraObject; +} + +static S32 sChaseQueueSize = 0; +static MatrixF* sChaseQueue = 0; +static S32 sChaseQueueHead = 0; +static S32 sChaseQueueTail = 0; + +bool GameConnection::getControlCameraTransform(F32 dt, MatrixF* mat) +{ + GameBase* obj = getCameraObject(); + if(!obj) + return false; + + GameBase* cObj = obj; + while((cObj = cObj->getControlObject()) != 0) + { + if(cObj->useObjsEyePoint()) + obj = cObj; + } + + if (dt) + { + if (mFirstPerson || obj->onlyFirstPerson()) + { + if (mCameraPos > 0) + if ((mCameraPos -= mCameraSpeed * dt) <= 0) + mCameraPos = 0; + } + else + { + if (mCameraPos < 1) + if ((mCameraPos += mCameraSpeed * dt) > 1) + mCameraPos = 1; + } + } + + if (!sChaseQueueSize || mFirstPerson || obj->onlyFirstPerson()) + obj->getCameraTransform(&mCameraPos,mat); + else + { + MatrixF& hm = sChaseQueue[sChaseQueueHead]; + MatrixF& tm = sChaseQueue[sChaseQueueTail]; + obj->getCameraTransform(&mCameraPos,&hm); + *mat = tm; + if (dt) + { + if ((sChaseQueueHead += 1) >= sChaseQueueSize) + sChaseQueueHead = 0; + if (sChaseQueueHead == sChaseQueueTail) + if ((sChaseQueueTail += 1) >= sChaseQueueSize) + sChaseQueueTail = 0; + } + } + return true; +} + +bool GameConnection::getControlCameraDefaultFov(F32 * fov) +{ + //find the last control object in the chain (client->player->turret->whatever...) + GameBase *obj = getCameraObject(); + GameBase *cObj = NULL; + while (obj) + { + cObj = obj; + obj = obj->getControlObject(); + } + if (cObj) + { + *fov = cObj->getDefaultCameraFov(); + return(true); + } + + return(false); +} + +bool GameConnection::getControlCameraFov(F32 * fov) +{ + //find the last control object in the chain (client->player->turret->whatever...) + GameBase *obj = getCameraObject(); + GameBase *cObj = NULL; + while (obj) + { + cObj = obj; + obj = obj->getControlObject(); + } + if (cObj) + { + *fov = cObj->getCameraFov(); + return(true); + } + + return(false); +} + +bool GameConnection::isValidControlCameraFov(F32 fov) +{ + //find the last control object in the chain (client->player->turret->whatever...) + GameBase *obj = getCameraObject(); + GameBase *cObj = NULL; + while (obj) + { + cObj = obj; + obj = obj->getControlObject(); + } + + return cObj ? cObj->isValidCameraFov(fov) : NULL; +} + +bool GameConnection::setControlCameraFov(F32 fov) +{ + //find the last control object in the chain (client->player->turret->whatever...) + GameBase *obj = getCameraObject(); + GameBase *cObj = NULL; + while (obj) + { + cObj = obj; + obj = obj->getControlObject(); + } + if (cObj) + { + // allow shapebase to clamp fov to its datablock values + cObj->setCameraFov(mClampF(fov, MinCameraFov, MaxCameraFov)); + F32 newFov = cObj->getCameraFov(); + + // server fov of client has 1degree resolution + if( S32(newFov) != S32(mCameraFov) || newFov != fov ) + mUpdateCameraFov = true; + + mCameraFov = newFov; + return(true); + } + return(false); +} + +bool GameConnection::getControlCameraVelocity(Point3F *vel) +{ + if (GameBase* obj = getCameraObject()) { + *vel = obj->getVelocity(); + return true; + } + return false; +} + +bool GameConnection::isControlObjectRotDampedCamera() +{ + if (Camera* cam = dynamic_cast(getCameraObject())) { + if(cam->isRotationDamped()) + return true; + } + return false; +} + +void GameConnection::setFirstPerson(bool firstPerson) +{ + mFirstPerson = firstPerson; + mUpdateFirstPerson = true; +} + + + +//---------------------------------------------------------------------------- + +bool GameConnection::onAdd() +{ + if (!Parent::onAdd()) + return false; + + return true; +} + +void GameConnection::onRemove() +{ + if(isNetworkConnection()) + { + sendDisconnectPacket(mDisconnectReason); + } + else if (isLocalConnection() && isConnectionToServer()) + { + // We're a client-side but local connection + // delete the server side of the connection on our local server so that it updates + // clientgroup and what not (this is so that we can disconnect from a local server + // without needing to destroy and recreate the server before we can connect to it + // again). + // Safe-delete as we don't know whether the server connection is currently being + // worked on. + getRemoteConnection()->safeDeleteObject(); + setRemoteConnectionObject(NULL); + } + if(!isConnectionToServer()) + { + onDrop_callback(mDisconnectReason); + } + + if (mControlObject) + mControlObject->setControllingClient(0); + Parent::onRemove(); +} + +void GameConnection::setDisconnectReason(const char *str) +{ + dStrncpy(mDisconnectReason, str, sizeof(mDisconnectReason) - 1); + mDisconnectReason[sizeof(mDisconnectReason) - 1] = 0; +} + +//---------------------------------------------------------------------------- + +void GameConnection::handleRecordedBlock(U32 type, U32 size, void *data) +{ + switch(type) + { + case BlockTypeMove: + mMoveList->pushMove(*((Move *) data)); + if(isRecording()) // put it back into the stream + recordBlock(type, size, data); + break; + default: + Parent::handleRecordedBlock(type, size, data); + break; + } +} + +void GameConnection::writeDemoStartBlock(ResizeBitStream *stream) +{ + // write all the data blocks to the stream: + + for(SimObjectId i = DataBlockObjectIdFirst; i <= DataBlockObjectIdLast; i++) + { + SimDataBlock *data; + if(Sim::findObject(i, data)) + { + stream->writeFlag(true); + SimDataBlockEvent evt(data); + evt.pack(this, stream); + stream->validate(); + } + } + stream->writeFlag(false); + stream->write(mFirstPerson); + stream->write(mCameraPos); + stream->write(mCameraSpeed); + + stream->writeString(Con::getVariable("$Client::MissionFile")); + + mMoveList->writeDemoStartBlock(stream); + + // dump all the "demo" vars associated with this connection: + SimFieldDictionaryIterator itr(getFieldDictionary()); + + SimFieldDictionary::Entry *entry; + while((entry = *itr) != NULL) + { + if(!dStrnicmp(entry->slotName, "demo", 4)) + { + stream->writeFlag(true); + stream->writeString(entry->slotName + 4); + stream->writeString(entry->value); + stream->validate(); + } + ++itr; + } + stream->writeFlag(false); + Parent::writeDemoStartBlock(stream); + + stream->validate(); + + // dump out the control object ghost id + S32 idx = mControlObject ? getGhostIndex(mControlObject) : -1; + stream->write(idx); + if(mControlObject) + { +#ifdef TORQUE_NET_STATS + U32 beginPos = stream->getBitPosition(); +#endif + mControlObject->writePacketData(this, stream); +#ifdef TORQUE_NET_STATS +// TYPEOF( mControlObject )->getNetInfo().updateNetStatWriteData( stream->getBitPosition() - beginPos ); + + mControlObject->getClassRep()->updateNetStatWriteData( stream->getBitPosition() - beginPos); +#endif + } + idx = mCameraObject ? getGhostIndex(mCameraObject) : -1; + stream->write(idx); + if(mCameraObject && mCameraObject != mControlObject) + { +#ifdef TORQUE_NET_STATS + U32 beginPos = stream->getBitPosition(); +#endif + + mCameraObject->writePacketData(this, stream); + +#ifdef TORQUE_NET_STATS + mCameraObject->getClassRep()->updateNetStatWriteData( stream->getBitPosition() - beginPos); +#endif + } + mLastControlRequestTime = Platform::getVirtualMilliseconds(); +} + +bool GameConnection::readDemoStartBlock(BitStream *stream) +{ + while(stream->readFlag()) + { + SimDataBlockEvent evt; + evt.unpack(this, stream); + evt.process(this); + } + + while(mDataBlockLoadList.size()) + { + preloadNextDataBlock(false); + if(mErrorBuffer.isNotEmpty()) + return false; + } + + stream->read(&mFirstPerson); + stream->read(&mCameraPos); + stream->read(&mCameraSpeed); + + char buf[256]; + stream->readString(buf); + Con::setVariable("$Client::MissionFile",buf); + + mMoveList->readDemoStartBlock(stream); + + // read in all the demo vars associated with this recording + // they are all tagged on to the object and start with the + // string "demo" + + while(stream->readFlag()) + { + StringTableEntry slotName = StringTable->insert("demo"); + char array[256]; + char value[256]; + stream->readString(array); + stream->readString(value); + setDataField(slotName, array, value); + } + bool ret = Parent::readDemoStartBlock(stream); + // grab the control object + S32 idx; + stream->read(&idx); + + GameBase * obj = 0; + if(idx != -1) + { + obj = dynamic_cast(resolveGhost(idx)); + setControlObject(obj); + obj->readPacketData(this, stream); + } + + // Get the camera object, and read it in if it's different + S32 idx2; + stream->read(&idx2); + obj = 0; + if(idx2 != -1 && idx2 != idx) + { + obj = dynamic_cast(resolveGhost(idx2)); + setCameraObject(obj); + obj->readPacketData(this, stream); + } + return ret; +} + +void GameConnection::demoPlaybackComplete() +{ + static const char *demoPlaybackArgv[1] = { "demoPlaybackComplete" }; + Sim::postCurrentEvent(Sim::getRootGroup(), new SimConsoleEvent(1, demoPlaybackArgv, false)); + Parent::demoPlaybackComplete(); +} + +void GameConnection::ghostPreRead(NetObject * nobj, bool newGhost) +{ + Parent::ghostPreRead( nobj, newGhost ); + + mMoveList->ghostPreRead(nobj,newGhost); +} + +void GameConnection::ghostReadExtra(NetObject * nobj, BitStream * bstream, bool newGhost) +{ + Parent::ghostReadExtra( nobj, bstream, newGhost ); + + mMoveList->ghostReadExtra(nobj, bstream, newGhost); + } + +void GameConnection::ghostWriteExtra(NetObject * nobj, BitStream * bstream) +{ + Parent::ghostWriteExtra( nobj, bstream); + + mMoveList->ghostWriteExtra(nobj, bstream); +} + +//---------------------------------------------------------------------------- + +void GameConnection::readPacket(BitStream *bstream) +{ + bstream->clearStringBuffer(); + bstream->clearCompressionPoint(); + + if (isConnectionToServer()) + { + mMoveList->clientReadMovePacket(bstream); + + bool hadFlash = mDamageFlash > 0 || mWhiteOut > 0; + mDamageFlash = 0; + mWhiteOut = 0; + if(bstream->readFlag()) + { + if(bstream->readFlag()) + mDamageFlash = bstream->readFloat(7); + if(bstream->readFlag()) + mWhiteOut = bstream->readFloat(7) * 1.5; + + if(!hadFlash) + { + // Started a damage flash or white out + onFlash_callback(true); + } + else + { + if(!(mDamageFlash > 0 || mWhiteOut > 0)) + { + // Received a zero damage flash and white out. + onFlash_callback(false); + } + } + } + else if(hadFlash) + { + // Catch those cases where both the damage flash and white out are at zero + // on the server but we did not receive an exact zero packet due to + // precision over the network issues. No problem as the flag we just + // read (which is false if we're here) has also told us about the change. + onFlash_callback(false); + } + + if ( bstream->readFlag() ) // gIndex != -1 + { + if ( bstream->readFlag() ) // mMoveList->isMismatch() || mControlForceMismatch + { + // the control object is dirty...so we get an update: + bool callScript = false; + bool controlObjChange = false; + if(mControlObject.isNull()) + callScript = true; + + S32 gIndex = bstream->readInt(NetConnection::GhostIdBitSize); + GameBase* obj = dynamic_cast(resolveGhost(gIndex)); + if (mControlObject != obj) + { + setControlObject(obj); + controlObjChange = true; + } +#ifdef TORQUE_NET_STATS + U32 beginSize = bstream->getBitPosition(); +#endif + obj->readPacketData(this, bstream); +#ifdef TORQUE_NET_STATS + obj->getClassRep()->updateNetStatReadData(bstream->getBitPosition() - beginSize); +#endif + + // let move list know that control object is dirty + mMoveList->markControlDirty(); + + if(callScript) + { + initialControlSet_callback(); + } + + if(controlObjChange) + { + onControlObjectChange_callback(); + } + } + else + { + // read out the compression point + Point3F pos; + bstream->read(&pos.x); + bstream->read(&pos.y); + bstream->read(&pos.z); + bstream->setCompressionPoint(pos); + } + } + + if (bstream->readFlag()) + { + S32 gIndex = bstream->readInt(NetConnection::GhostIdBitSize); + GameBase* obj = dynamic_cast(resolveGhost(gIndex)); + setCameraObject(obj); + obj->readPacketData(this, bstream); + } + else + setCameraObject(0); + + // server changed first person + if(bstream->readFlag()) + { + setFirstPerson(bstream->readFlag()); + mUpdateFirstPerson = false; + } + + // server forcing a fov change? + if(bstream->readFlag()) + { + S32 fov = bstream->readInt(8); + setControlCameraFov((F32)fov); + + // don't bother telling the server if we were able to set the fov + F32 setFov; + if(getControlCameraFov(&setFov) && (S32(setFov) == fov)) + mUpdateCameraFov = false; + + // update the games fov info + smFovUpdate.trigger((F32)fov); + } + } + else + { + mMoveList->serverReadMovePacket(bstream); + + mCameraPos = bstream->readFlag() ? 1.0f : 0.0f; + if (bstream->readFlag()) + mControlForceMismatch = true; + + // client changed first person + if(bstream->readFlag()) + { + setFirstPerson(bstream->readFlag()); + mUpdateFirstPerson = false; + } + + // check fov change.. 1degree granularity on server + if(bstream->readFlag()) + { + S32 fov = mClamp(bstream->readInt(8), S32(MinCameraFov), S32(MaxCameraFov)); + setControlCameraFov((F32)fov); + + // may need to force client back to a valid fov + F32 setFov; + if(getControlCameraFov(&setFov) && (S32(setFov) == fov)) + mUpdateCameraFov = false; + } + } + + Parent::readPacket(bstream); + bstream->clearCompressionPoint(); + bstream->clearStringBuffer(); + + if (isConnectionToServer()) + { + PROFILE_START(ClientCatchup); + ClientProcessList::get()->clientCatchup(this); + PROFILE_END(); + } +} + +void GameConnection::writePacket(BitStream *bstream, PacketNotify *note) +{ + bstream->clearCompressionPoint(); + bstream->clearStringBuffer(); + + GamePacketNotify *gnote = (GamePacketNotify *) note; + + U32 startPos = bstream->getBitPosition(); + if (isConnectionToServer()) + { + mMoveList->clientWriteMovePacket(bstream); + + bstream->writeFlag(mCameraPos == 1); + + // if we're recording, we want to make sure that we get periodic updates of the + // control object "just in case" - ie if the math copro is different between the + // recording machine (SIMD vs FPU), we get periodic corrections + + bool forceUpdate = false; + if(isRecording()) + { + U32 currentTime = Platform::getVirtualMilliseconds(); + if(currentTime - mLastControlRequestTime > ControlRequestTime) + { + mLastControlRequestTime = currentTime; + forceUpdate=true;; + } + } + bstream->writeFlag(forceUpdate); + + // first person changed? + if(bstream->writeFlag(mUpdateFirstPerson)) + { + bstream->writeFlag(mFirstPerson); + mUpdateFirstPerson = false; + } + + // camera fov changed? (server fov resolution is 1 degree) + if(bstream->writeFlag(mUpdateCameraFov)) + { + bstream->writeInt(mClamp(S32(mCameraFov), S32(MinCameraFov), S32(MaxCameraFov)), 8); + mUpdateCameraFov = false; + } + DEBUG_LOG(("PKLOG %d CLIENTMOVES: %d", getId(), bstream->getCurPos() - startPos)); + } + else + { + mMoveList->serverWriteMovePacket(bstream); + + // get the ghost index of the control object, and write out + // all the damage flash & white out + + S32 gIndex = -1; + if (!mControlObject.isNull()) + { + gIndex = getGhostIndex(mControlObject); + + F32 flash = mControlObject->getDamageFlash(); + F32 whiteOut = mControlObject->getWhiteOut(); + if(bstream->writeFlag(flash != 0 || whiteOut != 0)) + { + if(bstream->writeFlag(flash != 0)) + bstream->writeFloat(flash, 7); + if(bstream->writeFlag(whiteOut != 0)) + bstream->writeFloat(whiteOut/1.5, 7); + } + } + else + bstream->writeFlag(false); + + if (bstream->writeFlag(gIndex != -1)) + { + // assume that the control object will write in a compression point + if(bstream->writeFlag(mMoveList->isMismatch() || mControlForceMismatch)) + { +#ifdef TORQUE_DEBUG_NET + if (mMoveList->isMismatch()) + Con::printf("packetDataChecksum disagree!"); + else + Con::printf("packetDataChecksum disagree! (force)"); +#endif + + bstream->writeInt(gIndex, NetConnection::GhostIdBitSize); +#ifdef TORQUE_NET_STATS + U32 beginSize = bstream->getBitPosition(); +#endif + mControlObject->writePacketData(this, bstream); +#ifdef TORQUE_NET_STATS + mControlObject->getClassRep()->updateNetStatWriteData(bstream->getBitPosition() - beginSize); +#endif + mControlForceMismatch = false; + } + else + { + // we'll have to use the control object's position as the compression point + // should make this lower res for better space usage: + Point3F coPos = mControlObject->getPosition(); + bstream->write(coPos.x); + bstream->write(coPos.y); + bstream->write(coPos.z); + bstream->setCompressionPoint(coPos); + } + } + DEBUG_LOG(("PKLOG %d CONTROLOBJECTSTATE: %d", getId(), bstream->getCurPos() - startPos)); + startPos = bstream->getBitPosition(); + + if (!mCameraObject.isNull() && mCameraObject != mControlObject) + { + gIndex = getGhostIndex(mCameraObject); + if (bstream->writeFlag(gIndex != -1)) + { + bstream->writeInt(gIndex, NetConnection::GhostIdBitSize); + mCameraObject->writePacketData(this, bstream); + } + } + else + bstream->writeFlag( false ); + + // first person changed? + if(bstream->writeFlag(mUpdateFirstPerson)) + { + bstream->writeFlag(mFirstPerson); + mUpdateFirstPerson = false; + } + + // server forcing client fov? + gnote->cameraFov = -1; + if(bstream->writeFlag(mUpdateCameraFov)) + { + gnote->cameraFov = mClamp(S32(mCameraFov), S32(MinCameraFov), S32(MaxCameraFov)); + bstream->writeInt(gnote->cameraFov, 8); + mUpdateCameraFov = false; + } + DEBUG_LOG(("PKLOG %d PINGCAMSTATE: %d", getId(), bstream->getCurPos() - startPos)); + } + + Parent::writePacket(bstream, note); + bstream->clearCompressionPoint(); + bstream->clearStringBuffer(); +} + + +void GameConnection::detectLag() +{ + //see if we're lagging... + S32 curTime = Sim::getCurrentTime(); + if (curTime - mLastPacketTime > mLagThresholdMS) + { + if (!mLagging) + { + mLagging = true; + setLagIcon_callback(true); + } + } + else if (mLagging) + { + mLagging = false; + setLagIcon_callback(false); + } +} + +GameConnection::GamePacketNotify::GamePacketNotify() +{ + // need to fill in empty notifes for demo start block + cameraFov = 0; +} + +NetConnection::PacketNotify *GameConnection::allocNotify() +{ + return new GamePacketNotify; +} + +void GameConnection::packetReceived(PacketNotify *note) +{ + //record the time so we can tell if we're lagging... + mLastPacketTime = Sim::getCurrentTime(); + + // If we wanted to do something special, we grab our note like this: + //GamePacketNotify *gnote = (GamePacketNotify *) note; + + Parent::packetReceived(note); +} + +void GameConnection::packetDropped(PacketNotify *note) +{ + Parent::packetDropped(note); + GamePacketNotify *gnote = (GamePacketNotify *) note; + if(gnote->cameraFov != -1) + mUpdateCameraFov = true; +} + +//---------------------------------------------------------------------------- + +void GameConnection::play2D(SFXProfile* profile) +{ + postNetEvent(new Sim2DAudioEvent(profile)); +} + +void GameConnection::play3D(SFXProfile* profile, const MatrixF *transform) +{ + if ( !transform ) + play2D(profile); + + else if ( !mControlObject ) + postNetEvent(new Sim3DAudioEvent(profile,transform)); + + else + { + // TODO: Maybe improve this to account for the duration + // of the sound effect and if the control object can get + // into hearing range within time? + + // Only post the event if it's within audible range + // of the control object. + Point3F ear,pos; + transform->getColumn(3,&pos); + mControlObject->getTransform().getColumn(3,&ear); + if ((ear - pos).len() < profile->getDescription()->mMaxDistance) + postNetEvent(new Sim3DAudioEvent(profile,transform)); + } +} + +void GameConnection::doneScopingScene() +{ + // Could add special post-scene scoping here, such as scoping + // objects not visible to the camera, but visible to sensors. +} + +void GameConnection::preloadDataBlock(SimDataBlock *db) +{ + mDataBlockLoadList.push_back(db); + if(mDataBlockLoadList.size() == 1) + preloadNextDataBlock(false); +} + +void GameConnection::fileDownloadSegmentComplete() +{ + // this is called when a the file list has finished processing... + // at this point we can try again to add the object + // subclasses can override this to do, for example, datablock redos. + if(mDataBlockLoadList.size()) + preloadNextDataBlock(mNumDownloadedFiles != 0); + Parent::fileDownloadSegmentComplete(); +} + +void GameConnection::preloadNextDataBlock(bool hadNewFiles) +{ + if(!mDataBlockLoadList.size()) + return; + while(mDataBlockLoadList.size()) + { + // only check for new files if this is the first load, or if new + // files were downloaded from the server. +// if(hadNewFiles) +// gResourceManager->setMissingFileLogging(true); +// gResourceManager->clearMissingFileList(); + SimDataBlock *object = mDataBlockLoadList[0]; + if(!object) + { + // a null object is used to signify that the last ghost in the list is down + mDataBlockLoadList.pop_front(); + AssertFatal(mDataBlockLoadList.size() == 0, "Error! Datablock save list should be empty!"); + sendConnectionMessage(DataBlocksDownloadDone, mDataBlockSequence); + +// gResourceManager->setMissingFileLogging(false); + return; + } + mFilesWereDownloaded = hadNewFiles; + if(!object->preload(false, mErrorBuffer)) + { + mFilesWereDownloaded = false; + // make sure there's an error message if necessary + if(mErrorBuffer.isEmpty()) + setLastError("Invalid packet. (object preload)"); + + // if there were new files, make sure the error message + // is the one from the last time we tried to add this object + if(hadNewFiles) + { + mErrorBuffer = mLastFileErrorBuffer; +// gResourceManager->setMissingFileLogging(false); + return; + } + + // object failed to load, let's see if it had any missing files + if(isLocalConnection() /*|| !gResourceManager->getMissingFileList(mMissingFileList)*/) + { + // no missing files, must be an error + // connection will automagically delete the ghost always list + // when this error is reported. +// gResourceManager->setMissingFileLogging(false); + return; + } + + // ok, copy the error buffer out to a scratch pad for now + mLastFileErrorBuffer = mErrorBuffer; + //mErrorBuffer = String(); + + // request the missing files... + mNumDownloadedFiles = 0; + sendNextFileDownloadRequest(); + break; + } + mFilesWereDownloaded = false; +// gResourceManager->setMissingFileLogging(false); + mDataBlockLoadList.pop_front(); + hadNewFiles = true; + } +} + +void GameConnection::onEndGhosting() +{ + Parent::onEndGhosting(); + + // Reset move list. All the moves are obsolete now and furthermore, + // if we don't clear out the list now we might run in danger of + // getting backlogged later on what is a list full of obsolete moves. + + mMoveList->reset(); +} + +//---------------------------------------------------------------------------- +//localconnection only blackout functions +void GameConnection::setBlackOut(bool fadeToBlack, S32 timeMS) +{ + mFadeToBlack = fadeToBlack; + mBlackOutStartTimeMS = Sim::getCurrentTime(); + mBlackOutTimeMS = timeMS; + + //if timeMS <= 0 set the value instantly + if (mBlackOutTimeMS <= 0) + mBlackOut = (mFadeToBlack ? 1.0f : 0.0f); +} + +F32 GameConnection::getBlackOut() +{ + S32 curTime = Sim::getCurrentTime(); + + //see if we're in the middle of a black out + if (curTime < mBlackOutStartTimeMS + mBlackOutTimeMS) + { + S32 elapsedTime = curTime - mBlackOutStartTimeMS; + F32 timePercent = F32(elapsedTime) / F32(mBlackOutTimeMS); + mBlackOut = (mFadeToBlack ? timePercent : 1.0f - timePercent); + } + else + mBlackOut = (mFadeToBlack ? 1.0f : 0.0f); + + //return the blackout time + return mBlackOut; +} + +void GameConnection::handleConnectionMessage(U32 message, U32 sequence, U32 ghostCount) +{ + if(isConnectionToServer()) + { + if(message == DataBlocksDone) + { + mDataBlockLoadList.push_back(NULL); + mDataBlockSequence = sequence; + if(mDataBlockLoadList.size() == 1) + preloadNextDataBlock(true); + } + } + else + { + if(message == DataBlocksDownloadDone) + { + if(getDataBlockSequence() == sequence) + { + onDataBlocksDone_callback( getDataBlockSequence() ); + } + } + } + Parent::handleConnectionMessage(message, sequence, ghostCount); +} + +//---------------------------------------------------------------------------- + +DefineEngineMethod( GameConnection, transmitDataBlocks, void, (S32 sequence),, + "@brief Sent by the server during phase 1 of the mission download to send the datablocks to the client.\n\n" + + "SimDataBlocks, also known as just datablocks, need to be transmitted to the client " + "prior to the client entering the game world. These represent the static data that " + "most objects in the world reference. This is typically done during the standard " + "mission start phase 1 when following Torque's example mission startup sequence.\n\n" + + "When the datablocks have all been transmitted, onDataBlocksDone() is called to move " + "the mission start process to the next phase." + + "@param sequence The sequence is common between the server and client and ensures " + "that the client is acting on the most recent mission start process. If an errant " + "network packet (one that was lost but has now been found) is received by the client " + "with an incorrect sequence, it is just ignored. This sequence number is updated on " + "the server every time a mission is loaded.\n\n" + + "@tsexample\n" + "function serverCmdMissionStartPhase1Ack(%client, %seq)\n" + "{\n" + " // Make sure to ignore calls from a previous mission load\n" + " if (%seq != $missionSequence || !$MissionRunning)\n" + " return;\n" + " if (%client.currentPhase != 0)\n" + " return;\n" + " %client.currentPhase = 1;\n" + "\n" + " // Start with the CRC\n" + " %client.setMissionCRC( $missionCRC );\n" + "\n" + " // Send over the datablocks...\n" + " // OnDataBlocksDone will get called when have confirmation\n" + " // that they've all been received.\n" + " %client.transmitDataBlocks($missionSequence);\n" + "}\n" + "@endtsexample\n\n" + + "@see GameConnection::onDataBlocksDone()\n\n") +{ + // Set the datablock sequence. + object->setDataBlockSequence(sequence); + + // Store a pointer to the datablock group. + SimDataBlockGroup* pGroup = Sim::getDataBlockGroup(); + + // Determine the size of the datablock group. + const U32 iCount = pGroup->size(); + + // If this is the local client... + if (GameConnection::getLocalClientConnection() == object) + { + // Set up a pointer to the datablock. + SimDataBlock* pDataBlock = 0; + + // Set up a buffer for the datablock send. + U8 iBuffer[16384]; + BitStream mStream(iBuffer, 16384); + + // Iterate through all the datablocks... + for (U32 i = 0; i < iCount; i++) + { + // Get a pointer to the datablock in question... + pDataBlock = (SimDataBlock*)(*pGroup)[i]; + + // Set the client's new modified key. + object->setMaxDataBlockModifiedKey(pDataBlock->getModifiedKey()); + + // Pack the datablock stream. + mStream.setPosition(0); + mStream.clearCompressionPoint(); + pDataBlock->packData(&mStream); + + // Unpack the datablock stream. + mStream.setPosition(0); + mStream.clearCompressionPoint(); + pDataBlock->unpackData(&mStream); + + // Call the console function to set the number of blocks to be sent. + onDataBlockObjectReceived_callback(i, iCount); + + // Preload the datablock on the dummy client. + pDataBlock->preload(false, NetConnection::getErrorBuffer()); + } + + // Get the last datablock (if any)... + if (pDataBlock) + { + // Ensure the datablock modified key is set. + object->setDataBlockModifiedKey(object->getMaxDataBlockModifiedKey()); + + // Ensure that the client knows that the datablock send is done... + object->sendConnectionMessage(GameConnection::DataBlocksDone, object->getDataBlockSequence()); + } + } + else + { + // Otherwise, store the current datablock modified key. + const S32 iKey = object->getDataBlockModifiedKey(); + + // Iterate through the datablock group... + U32 i = 0; + for (; i < iCount; i++) + { + // If the datablock's modified key has already been set, break out of the loop... + if (((SimDataBlock*)(*pGroup)[i])->getModifiedKey() > iKey) + { + break; + } + } + + // If this is the last datablock in the group... + if (i == iCount) + { + // Ensure that the client knows that the datablock send is done... + object->sendConnectionMessage(GameConnection::DataBlocksDone, object->getDataBlockSequence()); + + // Then exit out since nothing else needs to be done. + return; + } + + // Set the maximum datablock modified key value. + object->setMaxDataBlockModifiedKey(iKey); + + // Get the minimum number of datablocks... + const U32 iMax = getMin(i + DataBlockQueueCount, iCount); + + // Iterate through the remaining datablocks... + for (;i < iMax; i++) + { + // Get a pointer to the datablock in question... + SimDataBlock* pDataBlock = (SimDataBlock*)(*pGroup)[i]; + + // Post the datablock event to the client. + object->postNetEvent(new SimDataBlockEvent(pDataBlock, i, iCount, object->getDataBlockSequence())); + } + } +} + +DefineEngineMethod( GameConnection, activateGhosting, void, (),, + "@brief Called by the server during phase 2 of the mission download to start sending ghosts to the client.\n\n" + + "Ghosts represent objects on the server that are in scope for the client. These need " + "to be synchronized with the client in order for the client to see and interact with them. " + "This is typically done during the standard mission start phase 2 when following Torque's " + "example mission startup sequence.\n\n" + + "@tsexample\n" + "function serverCmdMissionStartPhase2Ack(%client, %seq, %playerDB)\n" + "{\n" + " // Make sure to ignore calls from a previous mission load\n" + " if (%seq != $missionSequence || !$MissionRunning)\n" + " return;\n" + " if (%client.currentPhase != 1.5)\n" + " return;\n" + " %client.currentPhase = 2;\n" + "\n" + " // Set the player datablock choice\n" + " %client.playerDB = %playerDB;\n" + "\n" + " // Update mod paths, this needs to get there before the objects.\n" + " %client.transmitPaths();\n" + "\n" + " // Start ghosting objects to the client\n" + " %client.activateGhosting();\n" + "}\n" + "@endtsexample\n\n" + + "@see @ref ghosting_scoping for a description of the ghosting system.\n\n") +{ + object->activateGhosting(); +} + +DefineEngineMethod( GameConnection, resetGhosting, void, (),, + "@brief On the server, resets the connection to indicate that ghosting has been disabled.\n\n" + + "Typically when a mission has ended on the server, all connected clients are informed of this change " + "and their connections are reset back to a starting state. This method resets a connection on the " + "server to indicate that ghosts are no longer being transmitted. On the client end, all ghost " + "information will be deleted.\n\n" + + "@tsexample\n" + " // Inform the clients\n" + " for (%clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++)\n" + " {\n" + " // clear ghosts and paths from all clients\n" + " %cl = ClientGroup.getObject(%clientIndex);\n" + " %cl.endMission();\n" + " %cl.resetGhosting();\n" + " %cl.clearPaths();\n" + " }\n" + "@endtsexample\n\n" + + "@see @ref ghosting_scoping for a description of the ghosting system.\n\n") +{ + object->resetGhosting(); +} + +DefineEngineMethod( GameConnection, setControlObject, bool, (GameBase* ctrlObj),, + "@brief On the server, sets the object that the client will control.\n\n" + "By default the control object is an instance of the Player class, but can also be an instance " + "of Camera (when editing the mission, for example), or any other ShapeBase derived class as " + "appropriate for the game.\n\n" + "@param ctrlObj The GameBase object on the server to control.") +{ + if(!ctrlObj) + return false; + + object->setControlObject(ctrlObj); + return true; +} + +DefineEngineMethod( GameConnection, getControlObject, GameBase*, (),, + "@brief On the server, returns the object that the client is controlling." + "By default the control object is an instance of the Player class, but can also be an instance " + "of Camera (when editing the mission, for example), or any other ShapeBase derived class as " + "appropriate for the game.\n\n" + "@see GameConnection::setControlObject()\n\n") +{ + return object->getControlObject(); +} + +DefineEngineMethod( GameConnection, isAIControlled, bool, (),, + "@brief Returns true if this connection is AI controlled.\n\n" + "@see AIConnection") +{ + return object->isAIControlled(); +} + +DefineEngineMethod( GameConnection, isControlObjectRotDampedCamera, bool, (),, + "@brief Returns true if the object being controlled by the client is making use " + "of a rotation damped camera.\n\n" + "@see Camera") +{ + return object->isControlObjectRotDampedCamera(); +} + +DefineEngineMethod( GameConnection, play2D, bool, (SFXProfile* profile),, + "@brief Used on the server to play a 2D sound that is not attached to any object.\n\n" + + "@param profile The SFXProfile that defines the sound to play.\n\n" + + "@tsexample\n" + "function ServerPlay2D(%profile)\n" + "{\n" + " // Play the given sound profile on every client.\n" + " // The sounds will be transmitted as an event, not attached to any object.\n" + " for(%idx = 0; %idx < ClientGroup.getCount(); %idx++)\n" + " ClientGroup.getObject(%idx).play2D(%profile);\n" + "}\n" + "@endtsexample\n\n") +{ + if(!profile) + return false; + + object->play2D(profile); + return true; +} + +DefineEngineMethod( GameConnection, play3D, bool, (SFXProfile* profile, TransformF location),, + "@brief Used on the server to play a 3D sound that is not attached to any object.\n\n" + + "@param profile The SFXProfile that defines the sound to play.\n" + "@param location The position and orientation of the 3D sound given in the form of \"x y z ax ay az aa\".\n\n" + + "@tsexample\n" + "function ServerPlay3D(%profile,%transform)\n" + "{\n" + " // Play the given sound profile at the given position on every client\n" + " // The sound will be transmitted as an event, not attached to any object.\n" + " for(%idx = 0; %idx < ClientGroup.getCount(); %idx++)\n" + " ClientGroup.getObject(%idx).play3D(%profile,%transform);\n" + "}\n" + "@endtsexample\n\n") +{ + if(!profile) + return false; + + MatrixF mat = location.getMatrix(); + object->play3D(profile,&mat); + return true; +} + +DefineEngineMethod( GameConnection, chaseCam, bool, (S32 size),, + "@brief Sets the size of the chase camera's matrix queue.\n\n" + "@note This sets the queue size across all GameConnections.\n\n" + "@note This is not currently hooked up.\n\n") +{ + if (size != sChaseQueueSize) + { + SAFE_DELETE_ARRAY(sChaseQueue); + + sChaseQueueSize = size; + sChaseQueueHead = sChaseQueueTail = 0; + + if (size) + { + sChaseQueue = new MatrixF[size]; + return true; + } + } + return false; +} + +DefineEngineMethod( GameConnection, getControlCameraDefaultFov, F32, (),, + "@brief Returns the default field of view as used by the control object's camera.\n\n") +{ + F32 fov = 0.0f; + if(!object->getControlCameraDefaultFov(&fov)) + return(0.0f); + return(fov); +} + +DefineEngineMethod( GameConnection, setControlCameraFov, void, (F32 newFOV),, + "@brief On the server, sets the control object's camera's field of view.\n\n" + "@param newFOV New field of view (in degrees) to force the control object's camera to use. This value " + "is clamped to be within the range of 1 to 179 degrees.\n\n" + "@note When transmitted over the network to the client, the resolution is limited to " + "one degree. Any fraction is dropped.") +{ + object->setControlCameraFov(newFOV); +} + +DefineEngineMethod( GameConnection, getControlCameraFov, F32, (),, + "@brief Returns the field of view as used by the control object's camera.\n\n") +{ + F32 fov = 0.0f; + if(!object->getControlCameraFov(&fov)) + return(0.0f); + return(fov); +} + +DefineEngineMethod( GameConnection, getDamageFlash, F32, (),, + "@brief On the client, get the control object's damage flash level.\n\n" + "@return flash level\n") +{ + return object->getDamageFlash(); +} + +DefineEngineMethod( GameConnection, getWhiteOut, F32, (),, + "@brief On the client, get the control object's white-out level.\n\n" + "@return white-out level\n") +{ + return object->getWhiteOut(); +} + +DefineEngineMethod( GameConnection, setBlackOut, void, (bool doFade, S32 timeMS),, + "@brief On the server, sets the client's 3D display to fade to black.\n\n" + "@param doFade Set to true to fade to black, and false to fade from black.\n" + "@param timeMS Time it takes to perform the fade as measured in ms.\n\n" + "@note Not currently hooked up, and is not synchronized over the network.") +{ + object->setBlackOut(doFade, timeMS); +} + +DefineEngineMethod( GameConnection, setMissionCRC, void, (S32 CRC),, + "@brief On the server, transmits the mission file's CRC value to the client.\n\n" + + "Typically, during the standard mission start phase 1, the mission file's CRC value " + "on the server is send to the client. This allows the client to determine if the mission " + "has changed since the last time it downloaded this mission and act appropriately, such as " + "rebuilt cached lightmaps.\n\n" + + "@param CRC The mission file's CRC value on the server.\n\n" + + "@tsexample\n" + "function serverCmdMissionStartPhase1Ack(%client, %seq)\n" + "{\n" + " // Make sure to ignore calls from a previous mission load\n" + " if (%seq != $missionSequence || !$MissionRunning)\n" + " return;\n" + " if (%client.currentPhase != 0)\n" + " return;\n" + " %client.currentPhase = 1;\n" + "\n" + " // Start with the CRC\n" + " %client.setMissionCRC( $missionCRC );\n" + "\n" + " // Send over the datablocks...\n" + " // OnDataBlocksDone will get called when have confirmation\n" + " // that they've all been received.\n" + " %client.transmitDataBlocks($missionSequence);\n" + "}\n" + "@endtsexample\n\n") +{ + if(object->isConnectionToServer()) + return; + + object->postNetEvent(new SetMissionCRCEvent(CRC)); +} + +DefineEngineMethod( GameConnection, delete, void, (const char* reason), (""), + "@brief On the server, disconnect a client and pass along an optional reason why.\n\n" + + "This method performs two operations: it disconnects a client connection from the server, " + "and it deletes the connection object. The optional reason is sent in the disconnect packet " + "and is often displayed to the user so they know why they've been disconnected.\n\n" + + "@param reason [optional] The reason why the user has been disconnected from the server.\n\n" + + "@tsexample\n" + "function kick(%client)\n" + "{\n" + " messageAll( 'MsgAdminForce', '\\c2The Admin has kicked %1.', %client.playerName);\n" + "\n" + " if (!%client.isAIControlled())\n" + " BanList::add(%client.guid, %client.getAddress(), $Pref::Server::KickBanTime);\n" + " %client.delete(\"You have been kicked from this server\");\n" + "}\n" + "@endtsexample\n\n") +{ + object->setDisconnectReason(reason); + object->deleteObject(); +} + + +//-------------------------------------------------------------------------- +void GameConnection::consoleInit() +{ + Con::addVariable("$pref::Net::LagThreshold", TypeS32, &mLagThresholdMS, + "@brief How long between received packets before the client is considered as lagging (in ms).\n\n" + + "This is used by GameConnection to determine if the client is lagging. " + "If the client is indeed lagging, setLagIcon() is called to inform the user in some way. i.e. " + "display an icon on screen.\n\n" + + "@see GameConnection, GameConnection::setLagIcon()\n\n" + + "@ingroup Networking\n"); + + // Con::addVariable("specialFog", TypeBool, &SceneGraph::useSpecial); +} + +DefineEngineMethod( GameConnection, startRecording, void, (const char* fileName),, + "@brief On the client, starts recording the network connection's traffic to a demo file.\n\n" + + "It is often useful to play back a game session. This could be for producing a " + "demo of the game that will be shown at a later time, or for debugging a game. " + "By recording the entire network stream it is possible to later play game the game " + "exactly as it unfolded during the actual play session. This is because all user " + "control and server results pass through the connection.\n\n" + + "@param fileName The file name to use for the demo recording.\n\n" + + "@see GameConnection::stopRecording(), GameConnection::playDemo()") +{ + char expFileName[1024]; + Con::expandScriptFilename(expFileName, sizeof(expFileName), fileName); + object->startDemoRecord(expFileName); +} + +DefineEngineMethod( GameConnection, stopRecording, void, (),, + "@brief On the client, stops the recording of a connection's network traffic to a file.\n\n" + + "@see GameConnection::startRecording(), GameConnection::playDemo()") +{ + object->stopRecording(); +} + +DefineEngineMethod( GameConnection, playDemo, bool, (const char* demoFileName),, + "@brief On the client, play back a previously recorded game session.\n\n" + + "It is often useful to play back a game session. This could be for producing a " + "demo of the game that will be shown at a later time, or for debugging a game. " + "By recording the entire network stream it is possible to later play game the game " + "exactly as it unfolded during the actual play session. This is because all user " + "control and server results pass through the connection.\n\n" + + "@returns True if the playback was successful. False if there was an issue, such as " + "not being able to open the demo file for playback.\n\n" + + "@see GameConnection::startRecording(), GameConnection::stopRecording()") +{ + char filename[1024]; + Con::expandScriptFilename(filename, sizeof(filename), demoFileName); + + // Note that calling onConnectionEstablished will change the values in argv! + object->onConnectionEstablished(true); + object->setEstablished(); + + if(!object->replayDemoRecord(filename)) + { + Con::printf("Unable to open demo file %s.", filename); + object->deleteObject(); + return false; + } + + // After demo has loaded, execute the scene re-light the scene + //SceneLighting::lightScene(0, 0); + GameConnection::smPlayingDemo.trigger(); + + return true; +} + +DefineEngineMethod( GameConnection, isDemoPlaying, bool, (),, + "@brief Returns true if a previously recorded demo file is now playing.\n\n" + + "@see GameConnection::playDemo()") +{ + return object->isPlayingBack(); +} + +DefineEngineMethod( GameConnection, isDemoRecording, bool, (),, + "@brief Returns true if a demo file is now being recorded.\n\n" + + "@see GameConnection::startRecording(), GameConnection::stopRecording()") +{ + return object->isRecording(); +} + +DefineEngineMethod( GameConnection, listClassIDs, void, (),, + "@brief List all of the classes that this connection knows about, and what their IDs are. Useful for debugging network problems.\n\n" + "@note The list is sent to the console.\n\n") +{ + Con::printf("--------------- Class ID Listing ----------------"); + Con::printf(" id | name"); + + for(AbstractClassRep *rep = AbstractClassRep::getClassList(); + rep; + rep = rep->getNextClass()) + { + ConsoleObject *obj = rep->create(); + if(obj && rep->getClassId(object->getNetClassGroup()) >= 0) + Con::printf("%7.7d| %s", rep->getClassId(object->getNetClassGroup()), rep->getClassName()); + delete obj; + } +} + +DefineEngineStaticMethod( GameConnection, getServerConnection, S32, (),, + "@brief On the client, this static mehtod will return the connection to the server, if any.\n\n" + + "@returns The SimObject ID of the server connection, or -1 if none is found.\n\n") +{ + if(GameConnection::getConnectionToServer()) + return GameConnection::getConnectionToServer()->getId(); + else + { + Con::errorf("GameConnection::getServerConnection - no connection available."); + return -1; + } +} + +DefineEngineMethod( GameConnection, setCameraObject, bool, (GameBase* camera),, + "@brief On the server, set the connection's camera object used when not viewing " + "through the control object.\n\n" + + "@see GameConnection::getCameraObject() and GameConnection::clearCameraObject()\n\n") +{ + if(!camera) + return false; + + object->setCameraObject(camera); + return true; +} + +DefineEngineMethod( GameConnection, getCameraObject, SimObject*, (),, + "@brief Returns the connection's camera object used when not viewing through the control object.\n\n" + + "@see GameConnection::setCameraObject() and GameConnection::clearCameraObject()\n\n") +{ + SimObject *obj = dynamic_cast(object->getCameraObject()); + return obj; +} + +DefineEngineMethod( GameConnection, clearCameraObject, void, (),, + "@brief Clear the connection's camera object reference.\n\n" + + "@see GameConnection::setCameraObject() and GameConnection::getCameraObject()\n\n") +{ + object->setCameraObject(NULL); +} + +DefineEngineMethod( GameConnection, isFirstPerson, bool, (),, + "@brief Returns true if this connection is in first person mode.\n\n" + + "@note Transition to first person occurs over time via mCameraPos, so this " + "won't immediately return true after a set.\n\n") +{ + // Note: Transition to first person occurs over time via mCameraPos, so this + // won't immediately return true after a set. + return object->isFirstPerson(); +} + +DefineEngineMethod( GameConnection, setFirstPerson, void, (bool firstPerson),, + "@brief On the server, sets this connection into or out of first person mode.\n\n" + + "@param firstPerson Set to true to put the connection into first person mode.\n\n") +{ + object->setFirstPerson(firstPerson); +} diff --git a/Engine/source/T3D/gameBase/gameConnection.h b/Engine/source/T3D/gameBase/gameConnection.h new file mode 100644 index 000000000..fc25ba25a --- /dev/null +++ b/Engine/source/T3D/gameBase/gameConnection.h @@ -0,0 +1,345 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GAMECONNECTION_H_ +#define _GAMECONNECTION_H_ + +#ifndef _SIMBASE_H_ +#include "console/simBase.h" +#endif +#ifndef _GAMEBASE_H_ +#include "T3D/gameBase/gameBase.h" +#endif +#ifndef _NETCONNECTION_H_ +#include "sim/netConnection.h" +#endif +#ifndef _MOVEMANAGER_H_ +#include "T3D/gameBase/moveManager.h" +#endif +#ifndef _BITVECTOR_H_ +#include "core/bitVector.h" +#endif + +enum GameConnectionConstants +{ + MaxClients = 126, + DataBlockQueueCount = 16 +}; + +class SFXProfile; +class MatrixF; +class MatrixF; +class Point3F; +class MoveManager; +class MoveList; +struct Move; +struct AuthInfo; + +#define GameString TORQUE_APP_NAME + +const F32 MinCameraFov = 1.f; ///< min camera FOV +const F32 MaxCameraFov = 179.f; ///< max camera FOV + +class GameConnection : public NetConnection +{ +private: + typedef NetConnection Parent; + + SimObjectPtr mControlObject; + SimObjectPtr mCameraObject; + U32 mDataBlockSequence; + char mDisconnectReason[256]; + + U32 mMissionCRC; // crc of the current mission file from the server + +private: + U32 mLastControlRequestTime; + S32 mDataBlockModifiedKey; + S32 mMaxDataBlockModifiedKey; + + /// @name Client side first/third person + /// @{ + + /// + bool mFirstPerson; ///< Are we currently first person or not. + bool mUpdateFirstPerson; ///< Set to notify client or server of first person change. + bool mUpdateCameraFov; ///< Set to notify server of camera FOV change. + F32 mCameraFov; ///< Current camera fov (in degrees). + F32 mCameraPos; ///< Current camera pos (0-1). + F32 mCameraSpeed; ///< Camera in/out speed. + /// @} + +public: + + /// @name Protocol Versions + /// + /// Protocol versions are used to indicated changes in network traffic. + /// These could be changes in how any object transmits or processes + /// network information. You can specify backwards compatibility by + /// specifying a MinRequireProtocolVersion. If the client + /// protocol is >= this min value, the connection is accepted. + /// + /// Torque (V12) SDK 1.0 uses protocol = 1 + /// + /// Torque SDK 1.1 uses protocol = 2 + /// Torque SDK 1.4 uses protocol = 12 + /// @{ + static const U32 CurrentProtocolVersion; + static const U32 MinRequiredProtocolVersion; + /// @} + + /// Configuration + enum Constants { + BlockTypeMove = NetConnectionBlockTypeCount, + GameConnectionBlockTypeCount, + MaxConnectArgs = 16, + DataBlocksDone = NumConnectionMessages, + DataBlocksDownloadDone, + }; + + /// Set connection arguments; these are passed to the server when we connect. + void setConnectArgs(U32 argc, const char **argv); + + /// Set the server password to use when we join. + void setJoinPassword(const char *password); + + /// @name Event Handling + /// @{ + + virtual void onTimedOut(); + virtual void onConnectTimedOut(); + virtual void onDisconnect(const char *reason); + virtual void onConnectionRejected(const char *reason); + virtual void onConnectionEstablished(bool isInitiator); + virtual void handleStartupError(const char *errorString); + /// @} + + /// @name Packet I/O + /// @{ + + virtual void writeConnectRequest(BitStream *stream); + virtual bool readConnectRequest(BitStream *stream, const char **errorString); + virtual void writeConnectAccept(BitStream *stream); + virtual bool readConnectAccept(BitStream *stream, const char **errorString); + /// @} + + bool canRemoteCreate(); + +private: + /// @name Connection State + /// This data is set with setConnectArgs() and setJoinPassword(), and + /// sent across the wire when we connect. + /// @{ + + U32 mConnectArgc; + char *mConnectArgv[MaxConnectArgs]; + char *mJoinPassword; + /// @} + +protected: + struct GamePacketNotify : public NetConnection::PacketNotify + { + S32 cameraFov; + GamePacketNotify(); + }; + PacketNotify *allocNotify(); + + bool mControlForceMismatch; + + Vector mDataBlockLoadList; + +public: + + MoveList *mMoveList; + +protected: + bool mAIControlled; + AuthInfo * mAuthInfo; + + static S32 mLagThresholdMS; + S32 mLastPacketTime; + bool mLagging; + + /// @name Flashing + //// + /// Note, these variables are not networked, they are for the local connection only. + /// @{ + F32 mDamageFlash; + F32 mWhiteOut; + + F32 mBlackOut; + S32 mBlackOutTimeMS; + S32 mBlackOutStartTimeMS; + bool mFadeToBlack; + + /// @} + + /// @name Packet I/O + /// @{ + + void readPacket (BitStream *bstream); + void writePacket (BitStream *bstream, PacketNotify *note); + void packetReceived (PacketNotify *note); + void packetDropped (PacketNotify *note); + void connectionError (const char *errorString); + + void writeDemoStartBlock (ResizeBitStream *stream); + bool readDemoStartBlock (BitStream *stream); + void handleRecordedBlock (U32 type, U32 size, void *data); + /// @} + void ghostWriteExtra(NetObject *,BitStream *); + void ghostReadExtra(NetObject *,BitStream *, bool newGhost); + void ghostPreRead(NetObject *, bool newGhost); + + virtual void onEndGhosting(); + +public: + + DECLARE_CONOBJECT(GameConnection); + void handleConnectionMessage(U32 message, U32 sequence, U32 ghostCount); + void preloadDataBlock(SimDataBlock *block); + void fileDownloadSegmentComplete(); + void preloadNextDataBlock(bool hadNew); + + static void consoleInit(); + + void setDisconnectReason(const char *reason); + GameConnection(); + ~GameConnection(); + + bool onAdd(); + void onRemove(); + + static GameConnection *getConnectionToServer() + { + return dynamic_cast((NetConnection *) mServerConnection); + } + + static GameConnection *getLocalClientConnection() + { + return dynamic_cast((NetConnection *) mLocalClientConnection); + } + + /// @name Control object + /// @{ + + /// + void setControlObject(GameBase *); + GameBase* getControlObject() { return mControlObject; } + const GameBase* getControlObject() const { return mControlObject; } + + void setCameraObject(GameBase *); + GameBase* getCameraObject(); + + bool getControlCameraTransform(F32 dt,MatrixF* mat); + bool getControlCameraVelocity(Point3F *vel); + + bool getControlCameraDefaultFov(F32 *fov); + bool getControlCameraFov(F32 *fov); + bool setControlCameraFov(F32 fov); + bool isValidControlCameraFov(F32 fov); + + // Used by editor + bool isControlObjectRotDampedCamera(); + + void setFirstPerson(bool firstPerson); + + /// @} + + void detectLag(); + + /// @name Datablock management + /// @{ + + S32 getDataBlockModifiedKey () { return mDataBlockModifiedKey; } + void setDataBlockModifiedKey (S32 key) { mDataBlockModifiedKey = key; } + S32 getMaxDataBlockModifiedKey () { return mMaxDataBlockModifiedKey; } + void setMaxDataBlockModifiedKey (S32 key) { mMaxDataBlockModifiedKey = key; } + + /// Return the datablock sequence number that this game connection is on. + /// The datablock sequence number is synchronized to the mission sequence number + /// on each datablock transmission. + U32 getDataBlockSequence() { return mDataBlockSequence; } + + /// Set the datablock sequence number. + void setDataBlockSequence(U32 seq) { mDataBlockSequence = seq; } + + /// @} + + /// @name Fade control + /// @{ + + F32 getDamageFlash() const { return mDamageFlash; } + F32 getWhiteOut() const { return mWhiteOut; } + + void setBlackOut(bool fadeToBlack, S32 timeMS); + F32 getBlackOut(); + /// @} + + /// @name Authentication + /// + /// This is remnant code from Tribes 2. + /// @{ + + void setAuthInfo(const AuthInfo *info); + const AuthInfo *getAuthInfo(); + /// @} + + /// @name Sound + /// @{ + + void play2D(SFXProfile *profile); + void play3D(SFXProfile *profile, const MatrixF *transform); + /// @} + + /// @name Misc. + /// @{ + + bool isFirstPerson() const { return mCameraPos == 0; } + bool isAIControlled() { return mAIControlled; } + + void doneScopingScene(); + void demoPlaybackComplete(); + + void setMissionCRC(U32 crc) { mMissionCRC = crc; } + U32 getMissionCRC() { return(mMissionCRC); } + /// @} + + static Signal smFovUpdate; + static Signal smPlayingDemo; + +protected: + DECLARE_CALLBACK( void, onConnectionTimedOut, () ); + DECLARE_CALLBACK( void, onConnectionAccepted, () ); + DECLARE_CALLBACK( void, onConnectRequestTimedOut, () ); + DECLARE_CALLBACK( void, onConnectionDropped, (const char* reason) ); + DECLARE_CALLBACK( void, onConnectRequestRejected, (const char* reason) ); + DECLARE_CALLBACK( void, onConnectionError, (const char* errorString) ); + DECLARE_CALLBACK( void, onDrop, (const char* disconnectReason) ); + DECLARE_CALLBACK( void, initialControlSet, () ); + DECLARE_CALLBACK( void, onControlObjectChange, () ); + DECLARE_CALLBACK( void, setLagIcon, (bool state) ); + DECLARE_CALLBACK( void, onDataBlocksDone, (U32 sequence) ); + DECLARE_CALLBACK( void, onFlash, (bool state) ); +}; + +#endif diff --git a/Engine/source/T3D/gameBase/gameConnectionEvents.cpp b/Engine/source/T3D/gameBase/gameConnectionEvents.cpp new file mode 100644 index 000000000..37a8167ac --- /dev/null +++ b/Engine/source/T3D/gameBase/gameConnectionEvents.cpp @@ -0,0 +1,382 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/dnet.h" +#include "core/stream/bitStream.h" +#include "console/consoleTypes.h" +#include "console/simBase.h" +#include "scene/pathManager.h" +#include "scene/sceneManager.h" +#include "sfx/sfxSystem.h" +#include "sfx/sfxDescription.h" +#include "app/game.h" +#include "T3D/gameBase/gameConnection.h" +#include "T3D/gameBase/gameConnectionEvents.h" + +#define DebugChecksum 0xF00DBAAD + + +//#define DEBUG_SPEW + + +//-------------------------------------------------------------------------- +IMPLEMENT_CO_CLIENTEVENT_V1(SimDataBlockEvent); +IMPLEMENT_CO_CLIENTEVENT_V1(Sim2DAudioEvent); +IMPLEMENT_CO_CLIENTEVENT_V1(Sim3DAudioEvent); +IMPLEMENT_CO_CLIENTEVENT_V1(SetMissionCRCEvent); + +ConsoleDocClass( SimDataBlockEvent, + "@brief Use by GameConnection to process incoming datablocks.\n\n" + "Not intended for game development, internal use only, but does expose onDataBlockObjectReceived.\n\n " + "@internal"); + +ConsoleDocClass( Sim2DAudioEvent, + "@brief Use by GameConnection to send a 2D sound event over the network.\n\n" + "Not intended for game development, internal use only, but does expose GameConnection::play2D.\n\n " + "@internal"); + +ConsoleDocClass( Sim3DAudioEvent, + "@brief Use by GameConnection to send a 3D sound event over the network.\n\n" + "Not intended for game development, internal use only, but does expose GameConnection::play3D.\n\n " + "@internal"); + +ConsoleDocClass( SetMissionCRCEvent, + "@brief Use by GameConnection to send a 3D sound event over the network.\n\n" + "Not intended for game development, internal use only, but does expose GameConnection::setMissionCRC.\n\n " + "@internal"); + +//---------------------------------------------------------------------------- + +SimDataBlockEvent::SimDataBlockEvent(SimDataBlock* obj, U32 index, U32 total, U32 missionSequence) +{ + mObj = NULL; + mIndex = index; + mTotal = total; + mMissionSequence = missionSequence; + mProcess = false; + + if(obj) + { + id = obj->getId(); + AssertFatal(id >= DataBlockObjectIdFirst && id <= DataBlockObjectIdLast, + "Out of range event data block id... check simBase.h"); + + #ifdef DEBUG_SPEW + Con::printf("queuing data block: %d", mIndex); + #endif + } +} + +SimDataBlockEvent::~SimDataBlockEvent() +{ + if( mObj ) + delete mObj; +} + +#ifdef TORQUE_DEBUG_NET +const char *SimDataBlockEvent::getDebugName() +{ + SimObject *obj = Sim::findObject(id); + static char buffer[256]; + dSprintf(buffer, sizeof(buffer), "%s [%s - %s]", + getClassName(), + obj ? obj->getName() : "", + obj ? obj->getClassName() : "NONE"); + return buffer; +} +#endif // TORQUE_DEBUG_NET + +void SimDataBlockEvent::notifyDelivered(NetConnection *conn, bool ) +{ + // if the modified key for this event is not the current one, + // we've already resorted and resent some blocks, so fall out. + if(conn->isRemoved()) + return; + + GameConnection *gc = (GameConnection *) conn; + if(gc->getDataBlockSequence() != mMissionSequence) + return; + + U32 nextIndex = mIndex + DataBlockQueueCount; + SimDataBlockGroup *g = Sim::getDataBlockGroup(); + + if(mIndex == g->size() - 1) + { + gc->setDataBlockModifiedKey(gc->getMaxDataBlockModifiedKey()); + gc->sendConnectionMessage(GameConnection::DataBlocksDone, mMissionSequence); + } + + if(g->size() <= nextIndex) + return; + + SimDataBlock *blk = (SimDataBlock *) (*g)[nextIndex]; + gc->postNetEvent(new SimDataBlockEvent(blk, nextIndex, g->size(), mMissionSequence)); +} + +void SimDataBlockEvent::pack(NetConnection *conn, BitStream *bstream) +{ + SimDataBlock* obj; + Sim::findObject(id,obj); + GameConnection *gc = (GameConnection *) conn; + if(bstream->writeFlag(gc->getDataBlockModifiedKey() < obj->getModifiedKey())) + { + if(obj->getModifiedKey() > gc->getMaxDataBlockModifiedKey()) + gc->setMaxDataBlockModifiedKey(obj->getModifiedKey()); + + AssertFatal(obj, + "SimDataBlockEvent:: Data blocks cannot be deleted"); + bstream->writeInt(id - DataBlockObjectIdFirst,DataBlockObjectIdBitSize); + + S32 classId = obj->getClassId(conn->getNetClassGroup()); + bstream->writeClassId(classId, NetClassTypeDataBlock, conn->getNetClassGroup()); + bstream->writeInt(mIndex, DataBlockObjectIdBitSize); + bstream->writeInt(mTotal, DataBlockObjectIdBitSize + 1); + obj->packData(bstream); +#ifdef TORQUE_DEBUG_NET + bstream->writeInt(classId ^ DebugChecksum, 32); +#endif + } +} + +void SimDataBlockEvent::unpack(NetConnection *cptr, BitStream *bstream) +{ + if(bstream->readFlag()) + { + mProcess = true; + id = bstream->readInt(DataBlockObjectIdBitSize) + DataBlockObjectIdFirst; + S32 classId = bstream->readClassId(NetClassTypeDataBlock, cptr->getNetClassGroup()); + mIndex = bstream->readInt(DataBlockObjectIdBitSize); + mTotal = bstream->readInt(DataBlockObjectIdBitSize + 1); + + SimObject* ptr; + if( Sim::findObject( id, ptr ) ) + { + // An object with the given ID already exists. Make sure it has the right class. + + AbstractClassRep* classRep = AbstractClassRep::findClassRep( cptr->getNetClassGroup(), NetClassTypeDataBlock, classId ); + if( classRep && dStrcmp( classRep->getClassName(), ptr->getClassName() ) != 0 ) + { + Con::warnf( "A '%s' datablock with id: %d already existed. " + "Clobbering it with new '%s' datablock from server.", + ptr->getClassName(), id, classRep->getClassName() ); + ptr->deleteObject(); + ptr = NULL; + } + } + + if( !ptr ) + ptr = ( SimObject* ) ConsoleObject::create( cptr->getNetClassGroup(), NetClassTypeDataBlock, classId ); + + mObj = dynamic_cast< SimDataBlock* >( ptr ); + if( mObj != NULL ) + { + #ifdef DEBUG_SPEW + Con::printf(" - SimDataBlockEvent: unpacking event of type: %s", mObj->getClassName()); + #endif + + mObj->unpackData( bstream ); + } + else + { + #ifdef DEBUG_SPEW + Con::printf(" - SimDataBlockEvent: INVALID PACKET! Could not create class with classID: %d", classId); + #endif + + delete ptr; + cptr->setLastError("Invalid packet in SimDataBlockEvent::unpack()"); + } + +#ifdef TORQUE_DEBUG_NET + U32 checksum = bstream->readInt(32); + AssertISV( (checksum ^ DebugChecksum) == (U32)classId, + avar("unpack did not match pack for event of class %s.", + mObj->getClassName()) ); +#endif + + } +} + +void SimDataBlockEvent::write(NetConnection *cptr, BitStream *bstream) +{ + if(bstream->writeFlag(mProcess)) + { + bstream->writeInt(id - DataBlockObjectIdFirst,DataBlockObjectIdBitSize); + S32 classId = mObj->getClassId(cptr->getNetClassGroup()); + bstream->writeClassId(classId, NetClassTypeDataBlock, cptr->getNetClassGroup()); + bstream->writeInt(mIndex, DataBlockObjectIdBitSize); + bstream->writeInt(mTotal, DataBlockObjectIdBitSize + 1); + mObj->packData(bstream); + } +} + +void SimDataBlockEvent::process(NetConnection *cptr) +{ + if(mProcess) + { + //call the console function to set the number of blocks to be sent + Con::executef("onDataBlockObjectReceived", Con::getIntArg(mIndex), Con::getIntArg(mTotal)); + + String &errorBuffer = NetConnection::getErrorBuffer(); + + // Register the datablock object if this is a new DB + // and not for a modified datablock event. + + if( !mObj->isProperlyAdded() ) + { + // This is a fresh datablock object. + // Perform preload on datablock and register + // the object. + + GameConnection* conn = dynamic_cast< GameConnection* >( cptr ); + if( conn ) + conn->preloadDataBlock( mObj ); + + if( mObj->registerObject(id) ) + { + cptr->addObject( mObj ); + mObj = NULL; + } + } + else + { + // This is an update to an existing datablock. Preload + // to finish this. + + mObj->preload( false, errorBuffer ); + mObj = NULL; + } + } +} + + +//---------------------------------------------------------------------------- + + +Sim2DAudioEvent::Sim2DAudioEvent(SFXProfile *profile) +{ + mProfile = profile; +} + +void Sim2DAudioEvent::pack(NetConnection *, BitStream *bstream) +{ + bstream->writeInt( mProfile->getId() - DataBlockObjectIdFirst, DataBlockObjectIdBitSize); +} + +void Sim2DAudioEvent::write(NetConnection *, BitStream *bstream) +{ + bstream->writeInt( mProfile->getId() - DataBlockObjectIdFirst, DataBlockObjectIdBitSize); +} + +void Sim2DAudioEvent::unpack(NetConnection *, BitStream *bstream) +{ + SimObjectId id = bstream->readInt(DataBlockObjectIdBitSize) + DataBlockObjectIdFirst; + Sim::findObject(id, mProfile); +} + +void Sim2DAudioEvent::process(NetConnection *) +{ + if (mProfile) + SFX->playOnce( mProfile ); +} + +//---------------------------------------------------------------------------- + +static F32 SoundPosAccuracy = 0.5; +static S32 SoundRotBits = 8; + +Sim3DAudioEvent::Sim3DAudioEvent(SFXProfile *profile,const MatrixF* mat) +{ + mProfile = profile; + if (mat) + mTransform = *mat; +} + +void Sim3DAudioEvent::pack(NetConnection *con, BitStream *bstream) +{ + bstream->writeInt(mProfile->getId() - DataBlockObjectIdFirst, DataBlockObjectIdBitSize); + + // If the sound has cone parameters, the orientation is + // transmitted as well. + SFXDescription* ad = mProfile->getDescription(); + if ( bstream->writeFlag( ad->mConeInsideAngle || ad->mConeOutsideAngle ) ) + { + QuatF q(mTransform); + q.normalize(); + + // LH - we can get a valid quat that's very slightly over 1 in and so + // this fails (barely) check against zero. So use some error- + AssertFatal((1.0 - ((q.x * q.x) + (q.y * q.y) + (q.z * q.z))) >= (0.0 - 0.001), + "QuatF::normalize() is broken in Sim3DAudioEvent"); + + bstream->writeFloat(q.x,SoundRotBits); + bstream->writeFloat(q.y,SoundRotBits); + bstream->writeFloat(q.z,SoundRotBits); + bstream->writeFlag(q.w < 0.0); + } + + Point3F pos; + mTransform.getColumn(3,&pos); + bstream->writeCompressedPoint(pos,SoundPosAccuracy); +} + +void Sim3DAudioEvent::write(NetConnection *con, BitStream *bstream) +{ + // Just do the normal pack... + pack(con,bstream); +} + +void Sim3DAudioEvent::unpack(NetConnection *con, BitStream *bstream) +{ + SimObjectId id = bstream->readInt(DataBlockObjectIdBitSize) + DataBlockObjectIdFirst; + Sim::findObject(id, mProfile); + + if (bstream->readFlag()) { + QuatF q; + q.x = bstream->readFloat(SoundRotBits); + q.y = bstream->readFloat(SoundRotBits); + q.z = bstream->readFloat(SoundRotBits); + F32 value = ((q.x * q.x) + (q.y * q.y) + (q.z * q.z)); +// #ifdef __linux + // Hmm, this should never happen, but it does... + if ( value > 1.f ) + value = 1.f; +// #endif + q.w = mSqrt(1.f - value); + if (bstream->readFlag()) + q.w = -q.w; + q.setMatrix(&mTransform); + } + else + mTransform.identity(); + + Point3F pos; + bstream->readCompressedPoint(&pos,SoundPosAccuracy); + mTransform.setColumn(3, pos); +} + +void Sim3DAudioEvent::process(NetConnection *) +{ + if (mProfile) + SFX->playOnce( mProfile, &mTransform ); +} + diff --git a/Engine/source/T3D/gameBase/gameConnectionEvents.h b/Engine/source/T3D/gameBase/gameConnectionEvents.h new file mode 100644 index 000000000..b9652a940 --- /dev/null +++ b/Engine/source/T3D/gameBase/gameConnectionEvents.h @@ -0,0 +1,161 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GAMECONNECTIONEVENTS_H_ +#define _GAMECONNECTIONEVENTS_H_ + +#ifndef _SIMBASE_H_ +#include "console/simBase.h" +#endif + +#ifndef _GAMECONNECTION_H_ +#include "T3D/gameBase/gameConnection.h" +#endif + +#ifndef _SFXPROFILE_H_ +#include "sfx/sfxProfile.h" +#endif + +#ifndef _BITSTREAM_H_ +#include "core/stream/bitStream.h" +#endif + + +class QuitEvent : public SimEvent +{ + void process(SimObject *object) + { + Platform::postQuitMessage(0); + } +}; + +/// Event for sending a datablock over the net from the server to the client. +/// +/// Datablock events are GuaranteedOrdered client events. +/// +class SimDataBlockEvent : public NetEvent +{ + public: + + typedef NetEvent Parent; + + protected: + + /// Id of the datablock object to be sent. This must be a datablock ID + /// (as opposed to a normal object ID). + SimObjectId id; + + /// + U32 mIndex; + + /// Total number of datablocks that are part of this datablock transmission. + /// Each datablock is transmitted in an independent datablock event. + U32 mTotal; + + /// The mission sequence number to which this datablock transmission + /// belongs. + /// + /// @see GameConnection::getDataBlockSequence + U32 mMissionSequence; + + /// Datablock object constructed on the client side. + SimDataBlock *mObj; + + /// + bool mProcess; + + public: + + SimDataBlockEvent(SimDataBlock* obj = NULL, U32 index = 0, U32 total = 0, U32 missionSequence = 0); + ~SimDataBlockEvent(); + + void pack(NetConnection *, BitStream *bstream); + void write(NetConnection *, BitStream *bstream); + void unpack(NetConnection *cptr, BitStream *bstream); + void process(NetConnection*); + void notifyDelivered(NetConnection *, bool); + + #ifdef TORQUE_DEBUG_NET + const char *getDebugName(); + #endif + + DECLARE_CONOBJECT( SimDataBlockEvent ); + DECLARE_CATEGORY( "Game Networking" ); +}; + +class Sim2DAudioEvent: public NetEvent +{ + private: + SFXProfile *mProfile; + + public: + typedef NetEvent Parent; + Sim2DAudioEvent(SFXProfile *profile=NULL); + void pack(NetConnection *, BitStream *bstream); + void write(NetConnection *, BitStream *bstream); + void unpack(NetConnection *, BitStream *bstream); + void process(NetConnection *); + DECLARE_CONOBJECT(Sim2DAudioEvent); +}; + +class Sim3DAudioEvent: public NetEvent +{ + private: + SFXProfile *mProfile; + MatrixF mTransform; + + public: + typedef NetEvent Parent; + Sim3DAudioEvent(SFXProfile *profile=NULL,const MatrixF* mat=NULL); + void pack(NetConnection *, BitStream *bstream); + void write(NetConnection *, BitStream *bstream); + void unpack(NetConnection *, BitStream *bstream); + void process(NetConnection *); + DECLARE_CONOBJECT(Sim3DAudioEvent); +}; + + +//---------------------------------------------------------------------------- +// used to set the crc for the current mission (mission lighting) +//---------------------------------------------------------------------------- +class SetMissionCRCEvent : public NetEvent +{ + private: + U32 mCrc; + + public: + typedef NetEvent Parent; + SetMissionCRCEvent(U32 crc = 0xffffffff) + { mCrc = crc; } + void pack(NetConnection *, BitStream * bstream) + { bstream->write(mCrc); } + void write(NetConnection * con, BitStream * bstream) + { pack(con, bstream); } + void unpack(NetConnection *, BitStream * bstream) + { bstream->read(&mCrc); } + void process(NetConnection * con) + { static_cast(con)->setMissionCRC(mCrc); } + + DECLARE_CONOBJECT(SetMissionCRCEvent); +}; + +#endif diff --git a/Engine/source/T3D/gameBase/gameProcess.cpp b/Engine/source/T3D/gameBase/gameProcess.cpp new file mode 100644 index 000000000..d79c8f687 --- /dev/null +++ b/Engine/source/T3D/gameBase/gameProcess.cpp @@ -0,0 +1,184 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/gameBase/gameProcess.h" + +#include "T3D/gameBase/gameBase.h" +#include "T3D/gameBase/gameConnection.h" +#include "T3D/gameBase/moveList.h" + +//---------------------------------------------------------------------------- + +ClientProcessList* ClientProcessList::smClientProcessList = NULL; +ServerProcessList* ServerProcessList::smServerProcessList = NULL; +static U32 gNetOrderNextId = 0; + +ConsoleFunction( dumpProcessList, void, 1, 1, + "Dumps all ProcessObjects in ServerProcessList and ClientProcessList to the console." ) +{ + Con::printf( "client process list:" ); + ClientProcessList::get()->dumpToConsole(); + Con::printf( "server process list:" ); + ServerProcessList::get()->dumpToConsole(); +} + +//-------------------------------------------------------------------------- +// ClientProcessList +//-------------------------------------------------------------------------- + +ClientProcessList::ClientProcessList() +{ +} + +void ClientProcessList::addObject( ProcessObject *pobj ) +{ + AssertFatal( static_cast( pobj )->isClientObject(), "Tried to add server object to ClientProcessList." ); + + GameBase *obj = getGameBase( pobj ); + + if ( obj && obj->isNetOrdered() ) + { + if ( ( gNetOrderNextId & 0xFFFF ) == 0 ) + // don't let it be zero + gNetOrderNextId++; + + pobj->mOrderGUID = ( gNetOrderNextId++ ) & 0xFFFF; // 16 bits should be enough + pobj->plLinkBefore( &mHead ); + mDirty = true; + } + else if ( obj && obj->isTickLast() ) + { + pobj->mOrderGUID = 0xFFFFFFFF; + pobj->plLinkBefore( &mHead ); + // not dirty + } + else + { + pobj->plLinkAfter( &mHead ); + // not dirty + } +} + +bool ClientProcessList::doBacklogged( SimTime timeDelta ) +{ + #ifdef TORQUE_DEBUG + static bool backlogged = false; + static U32 backloggedTime = 0; + #endif + + // See if the control object has pending moves. + GameConnection *connection = GameConnection::getConnectionToServer(); + + if ( connection ) + { + // If the connection to the server is backlogged + // the simulation is frozen. + if ( connection->mMoveList->isBacklogged() ) + { + #ifdef TORQUE_DEBUG + if ( !backlogged ) + { + Con::printf( "client is backlogged, time is frozen" ); + backlogged = true; + } + + backloggedTime += timeDelta; + #endif + + return true; + } + } + + #ifdef TORQUE_DEBUG + if ( backlogged ) + { + Con::printf( "client is no longer backlogged, time is unfrozen (%i ms elapsed)", backloggedTime ); + backlogged = false; + backloggedTime = 0; + } + #endif + + return false; +} + +void ClientProcessList::onPreTickObject( ProcessObject *pobj ) +{ + // reset to tick boundary + pobj->interpolateTick( 0.0f ); +} + + +//-------------------------------------------------------------------------- +// ServerProcessList +//-------------------------------------------------------------------------- + +ServerProcessList::ServerProcessList() +{ +} + +void ServerProcessList::addObject( ProcessObject *pobj ) +{ + AssertFatal( static_cast( pobj )->isServerObject(), "Tried to add client object to ServerProcessList." ); + + GameBase *obj = getGameBase( pobj ); + + if ( obj && obj->isNetOrdered() ) + { + if ( ( gNetOrderNextId & 0xFFFF ) == 0) + // don't let it be zero + gNetOrderNextId++; + + pobj->mOrderGUID = ( gNetOrderNextId++ ) & 0xFFFF; // 16 bits should be enough + pobj->plLinkBefore( &mHead ); + mDirty = true; + } + else if ( obj && obj->isTickLast() ) + { + pobj->mOrderGUID = 0xFFFFFFFF; + pobj->plLinkBefore( &mHead ); + // not dirty + } + else + { + pobj->plLinkAfter( &mHead ); + // not dirty + } +} + +void ServerProcessList::advanceObjects() +{ + #ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("Advance server time..."); + #endif + + Parent::advanceObjects(); + + #ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("---------"); + #endif +} + +void ServerProcessList::onPreTickObject( ProcessObject *pobj ) +{ +} + diff --git a/Engine/source/T3D/gameBase/gameProcess.h b/Engine/source/T3D/gameBase/gameProcess.h new file mode 100644 index 000000000..d4ca2e023 --- /dev/null +++ b/Engine/source/T3D/gameBase/gameProcess.h @@ -0,0 +1,96 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GAMEPROCESS_H_ +#define _GAMEPROCESS_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#ifndef _PROCESSLIST_H_ +#include "T3D/gameBase/processList.h" +#endif + + +class GameBase; +class GameConnection; +struct Move; + + +class ClientProcessList : public ProcessList +{ + typedef ProcessList Parent; + +public: + + ClientProcessList(); + + // ProcessList + void addObject( ProcessObject *pobj ); + + /// Called after a correction packet is received from the server. + /// If the control object was corrected it will now play back any moves + /// which were rolled back. + virtual void clientCatchup( GameConnection *conn ) {} + + static ClientProcessList* get() { return smClientProcessList; } + +protected: + + // ProcessList + void onPreTickObject( ProcessObject *pobj ); + + /// Returns true if backlogged. + bool doBacklogged( SimTime timeDelta ); + +protected: + + static ClientProcessList* smClientProcessList; +}; + + +class ServerProcessList : public ProcessList +{ + typedef ProcessList Parent; + +public: + + ServerProcessList(); + + // ProcessList + void addObject( ProcessObject *pobj ); + + static ServerProcessList* get() { return smServerProcessList; } + +protected: + + // ProcessList + void onPreTickObject( ProcessObject *pobj ); + void advanceObjects(); + +protected: + + static ServerProcessList* smServerProcessList; +}; + + +#endif // _GAMEPROCESS_H_ \ No newline at end of file diff --git a/Engine/source/T3D/gameBase/hifi/hifiGameProcess.cpp b/Engine/source/T3D/gameBase/hifi/hifiGameProcess.cpp new file mode 100644 index 000000000..734b5135b --- /dev/null +++ b/Engine/source/T3D/gameBase/hifi/hifiGameProcess.cpp @@ -0,0 +1,607 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/gameBase/hifi/hifiGameProcess.h" + +#include "platform/profiler.h" +#include "core/frameAllocator.h" +#include "core/stream/bitStream.h" +#include "math/mathUtils.h" +#include "T3D/gameBase/hifi/hifiMoveList.h" +#include "T3D/gameBase/gameConnection.h" +#include "T3D/gameFunctions.h" + + +MODULE_BEGIN( ProcessList ) + + MODULE_INIT + { + HifiServerProcessList::init(); + HifiClientProcessList::init(); + } + + MODULE_SHUTDOWN + { + HifiServerProcessList::shutdown(); + HifiClientProcessList::shutdown(); + } + +MODULE_END; + +void HifiServerProcessList::init() +{ + smServerProcessList = new HifiServerProcessList(); +} + +void HifiServerProcessList::shutdown() +{ + delete smServerProcessList; +} + +void HifiClientProcessList::init() +{ + smClientProcessList = new HifiClientProcessList(); +} + +void HifiClientProcessList::shutdown() +{ + delete smClientProcessList; +} + +//---------------------------------------------------------------------------- + +F32 gMaxHiFiVelSq = 100 * 100; + +namespace +{ + inline GameBase * GetGameBase(ProcessObject * obj) + { + return static_cast(obj); + } + + // local work class + struct GameBaseListNode + { + GameBaseListNode() + { + mPrev=this; + mNext=this; + mObject=NULL; + } + + GameBaseListNode * mPrev; + GameBaseListNode * mNext; + GameBase * mObject; + + void linkBefore(GameBaseListNode * obj) + { + // Link this before obj + mNext = obj; + mPrev = obj->mPrev; + obj->mPrev = this; + mPrev->mNext = this; + } + }; + + // Structure used for synchronizing move lists on client/server + struct MoveSync + { + enum { ActionCount = 4 }; + + S32 moveDiff; + S32 moveDiffSteadyCount; + S32 moveDiffSameSignCount; + + bool doAction() { return moveDiffSteadyCount>=ActionCount || moveDiffSameSignCount>=4*ActionCount; } + void reset() { moveDiff=0; moveDiffSteadyCount=0; moveDiffSameSignCount=0; } + void update(S32 diff); + } moveSync; + + void MoveSync::update(S32 diff) + { + if (diff && diff==moveDiff) + { + moveDiffSteadyCount++; + moveDiffSameSignCount++; + } + else if (diff*moveDiff>0) + { + moveDiffSteadyCount = 0; + moveDiffSameSignCount++; + } + else + reset(); + moveDiff = diff; + } + +} // namespace + +//-------------------------------------------------------------------------- +// HifiClientProcessList +//-------------------------------------------------------------------------- + +HifiClientProcessList::HifiClientProcessList() +{ + mSkipAdvanceObjectsMs = 0; + mForceHifiReset = false; + mCatchup = 0; +} + +bool HifiClientProcessList::advanceTime( SimTime timeDelta ) +{ + PROFILE_SCOPE( AdvanceClientTime ); + + if ( mSkipAdvanceObjectsMs && timeDelta > mSkipAdvanceObjectsMs ) + { + timeDelta -= mSkipAdvanceObjectsMs; + advanceTime( mSkipAdvanceObjectsMs ); + AssertFatal( !mSkipAdvanceObjectsMs, "mSkipAdvanceObjectsMs must always be positive." ); + } + + if ( doBacklogged( timeDelta ) ) + return false; + + // remember interpolation value because we might need to set it back + F32 oldLastDelta = mLastDelta; + + bool ret = Parent::advanceTime( timeDelta ); + + if ( !mSkipAdvanceObjectsMs ) + { + AssertFatal( mLastDelta >= 0.0f && mLastDelta <= 1.0f, "mLastDelta must always be zero to one." ); + for ( ProcessObject *pobj = mHead.mProcessLink.next; pobj != &mHead; pobj = pobj->mProcessLink.next ) + { + if ( pobj->isTicking() ) + pobj->interpolateTick( mLastDelta ); + } + + // Inform objects of total elapsed delta so they can advance + // client side animations. + F32 dt = F32( timeDelta ) / 1000; + for ( ProcessObject *pobj = mHead.mProcessLink.next; pobj != &mHead; pobj = pobj->mProcessLink.next) + { + pobj->advanceTime( dt ); + } + } + else + { + mSkipAdvanceObjectsMs -= timeDelta; + mLastDelta = oldLastDelta; + } + + return ret; +} + +void HifiClientProcessList::onAdvanceObjects() +{ + GameConnection* connection = GameConnection::getConnectionToServer(); + if(connection) + { + // process any demo blocks that are NOT moves, and exactly one move + // we advance time in the demo stream by a move inserted on + // each tick. So before doing the tick processing we advance + // the demo stream until a move is ready + if(connection->isPlayingBack()) + { + U32 blockType; + do + { + blockType = connection->getNextBlockType(); + bool res = connection->processNextBlock(); + // if there are no more blocks, exit out of this function, + // as no more client time needs to process right now - we'll + // get it all on the next advanceClientTime() + if(!res) + return; + } + while(blockType != GameConnection::BlockTypeMove); + } + if (!mSkipAdvanceObjectsMs) + { + connection->mMoveList->collectMove(); + advanceObjects(); + } + connection->mMoveList->onAdvanceObjects(); + } +} + +void HifiClientProcessList::onTickObject(ProcessObject * pobj) +{ + // Each object is advanced a single tick + // If it's controlled by a client, tick using a move. + + Move *movePtr; + U32 numMoves; + GameConnection *con = pobj->getControllingClient(); + SimObjectPtr obj = getGameBase( pobj ); + + if ( obj && con && con->getControlObject() == obj && con->mMoveList->getMoves( &movePtr, &numMoves) ) + { +#ifdef TORQUE_DEBUG_NET_MOVES + U32 sum = Move::ChecksumMask & obj->getPacketDataChecksum( obj->getControllingClient() ); +#endif + + obj->processTick( movePtr ); + + if ( bool(obj) && obj->getControllingClient() ) + { + U32 newsum = Move::ChecksumMask & obj->getPacketDataChecksum( obj->getControllingClient() ); + + // set checksum if not set or check against stored value if set + movePtr->checksum = newsum; + +#ifdef TORQUE_DEBUG_NET_MOVES + Con::printf( "move checksum: %i, (start %i), (move %f %f %f)", + movePtr->checksum,sum,movePtr->yaw,movePtr->y,movePtr->z ); +#endif + } + con->mMoveList->clearMoves( 1 ); + } + else if ( pobj->isTicking() ) + pobj->processTick( 0 ); + + if ( obj && ( obj->getTypeMask() & GameBaseHiFiObjectType ) ) + { + GameConnection * serverConnection = GameConnection::getConnectionToServer(); + TickCacheEntry * tce = obj->getTickCache().addCacheEntry(); + BitStream bs( tce->packetData, TickCacheEntry::MaxPacketSize ); + obj->writePacketData( serverConnection, &bs ); + + Point3F vel = obj->getVelocity(); + F32 velSq = mDot( vel, vel ); + gMaxHiFiVelSq = getMax( gMaxHiFiVelSq, velSq ); + } +} + +void HifiClientProcessList::advanceObjects() +{ +#ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("Advance client time..."); +#endif + + // client re-computes this each time objects are advanced + gMaxHiFiVelSq = 0; + Parent::advanceObjects(); + + // We need to consume a move on the connections whether + // there is a control object to consume the move or not, + // otherwise client and server can get out of sync move-wise + // during startup. If there is a control object, we cleared + // a move above. Handle case where no control object here. + // Note that we might consume an extra move here and there when + // we had a control object in above loop but lost it during tick. + // That is no big deal so we don't bother trying to carefully + // track it. + GameConnection * client = GameConnection::getConnectionToServer(); + if (client && client->getControlObject() == NULL) + client->mMoveList->clearMoves(1); + +#ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("---------"); +#endif +} + +void HifiClientProcessList::ageTickCache(S32 numToAge, S32 len) +{ + for (ProcessObject * pobj = mHead.mProcessLink.next; pobj != &mHead; pobj = pobj->mProcessLink.next) + { + GameBase *obj = getGameBase(pobj); + if ( obj && obj->getTypeMask() & GameBaseHiFiObjectType ) + obj->getTickCache().ageCache(numToAge,len); + } +} + +void HifiClientProcessList::updateMoveSync(S32 moveDiff) +{ + moveSync.update(moveDiff); + if (moveSync.doAction() && moveDiff<0) + { + skipAdvanceObjects(TickMs * -moveDiff); + moveSync.reset(); + } +} + +void HifiClientProcessList::clientCatchup(GameConnection * connection) +{ +#ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("client catching up... (%i)%s", mCatchup, mForceHifiReset ? " reset" : ""); +#endif + + if (connection->getControlObject() && connection->getControlObject()->isGhostUpdated()) + // if control object is reset, make sure moves are reset too + connection->mMoveList->resetCatchup(); + + const F32 maxVel = mSqrt(gMaxHiFiVelSq) * 1.25f; + F32 dt = F32(mCatchup+1) * TickSec; + Point3F bigDelta(maxVel*dt,maxVel*dt,maxVel*dt); + + // walk through all process objects looking for ones which were updated + // -- during first pass merely collect neighbors which need to be reset and updated in unison + ProcessObject * pobj; + if (mCatchup && !mForceHifiReset) + { + for (pobj = mHead.mProcessLink.next; pobj != &mHead; pobj = pobj->mProcessLink.next) + { + GameBase *obj = getGameBase( pobj ); + static SimpleQueryList nearby; + nearby.mList.clear(); + // check for nearby objects which need to be reset and then caught up + // note the funky loop logic -- first time through obj is us, then + // we start iterating through nearby list (to look for objects nearby + // the nearby objects), which is why index starts at -1 + // [objects nearby the nearby objects also get added to the nearby list] + for (S32 i=-1; obj; obj = ++iisGhostUpdated() && (obj->getTypeMask() & GameBaseHiFiObjectType) && !obj->isNetNearbyAdded()) + { + Point3F start = obj->getWorldSphere().center; + Point3F end = start + 1.1f * dt * obj->getVelocity(); + F32 rad = 1.5f * obj->getWorldSphere().radius; + + // find nearby items not updated but are hi fi, mark them as updated (and restore old loc) + // check to see if added items have neighbors that need updating + Box3F box; + Point3F rads(rad,rad,rad); + box.minExtents = box.maxExtents = start; + box.minExtents -= bigDelta + rads; + box.maxExtents += bigDelta + rads; + + // CodeReview - this is left in for MBU, but also so we can deal with the issue later. + // add marble blast hack so hifi networking can see hidden objects + // (since hidden is under control of hifi networking) + // gForceNotHidden = true; + + S32 j = nearby.mList.size(); + gClientContainer.findObjects(box, GameBaseHiFiObjectType, SimpleQueryList::insertionCallback, &nearby); + + // CodeReview - this is left in for MBU, but also so we can deal with the issue later. + // disable above hack + // gForceNotHidden = false; + + // drop anyone not heading toward us or already checked + for (; jisHifiPassive() && obj2->isHifiPassive(); + if (!obj2->isGhostUpdated() && !passive) + { + // compare swept spheres of obj and obj2 + // if collide, reset obj2, setGhostUpdated(true), and continue + Point3F end2 = obj2->getWorldSphere().center; + Point3F start2 = end2 - 1.1f * dt * obj2->getVelocity(); + F32 rad2 = 1.5f * obj->getWorldSphere().radius; + if (MathUtils::capsuleCapsuleOverlap(start,end,rad,start2,end2,rad2)) + { + // better add obj2 + obj2->getTickCache().beginCacheList(); + TickCacheEntry * tce = obj2->getTickCache().incCacheList(); + BitStream bs(tce->packetData,TickCacheEntry::MaxPacketSize); + obj2->readPacketData(connection,&bs); + obj2->setGhostUpdated(true); + + // continue so we later add the neighbors too + continue; + } + + } + + // didn't pass above test...so don't add it or nearby objects + nearby.mList[j] = nearby.mList.last(); + nearby.mList.decrement(); + j--; + } + obj->setNetNearbyAdded(true); + } + } + } + } + + // save water mark -- for game base list + FrameAllocatorMarker mark; + + // build ordered list of client objects which need to be caught up + GameBaseListNode list; + for (pobj = mHead.mProcessLink.next; pobj != &mHead; pobj = pobj->mProcessLink.next) + { + GameBase *obj = getGameBase( pobj ); + //GameBase *obj = dynamic_cast( pobj ); + //GameBase *obj = (GameBase*)pobj; + + // Not a GameBase object so nothing to do. + if ( !obj ) + continue; + + if (obj->isGhostUpdated() && (obj->getTypeMask() & GameBaseHiFiObjectType)) + { + // construct process object and add it to the list + // hold pointer to our object in mAfterObject + GameBaseListNode * po = (GameBaseListNode*)FrameAllocator::alloc(sizeof(GameBaseListNode)); + po->mObject = obj; + po->linkBefore(&list); + + // begin iterating through tick list (skip first tick since that is the state we've been reset to) + obj->getTickCache().beginCacheList(); + obj->getTickCache().incCacheList(); + } + else if (mForceHifiReset && (obj->getTypeMask() & GameBaseHiFiObjectType)) + { + // add all hifi objects + obj->getTickCache().beginCacheList(); + TickCacheEntry * tce = obj->getTickCache().incCacheList(); + BitStream bs(tce->packetData,TickCacheEntry::MaxPacketSize); + obj->readPacketData(connection,&bs); + obj->setGhostUpdated(true); + + // construct process object and add it to the list + // hold pointer to our object in mAfterObject + GameBaseListNode * po = (GameBaseListNode*)FrameAllocator::alloc(sizeof(GameBaseListNode)); + po->mObject = obj; + po->linkBefore(&list); + } + else if (obj == connection->getControlObject() && obj->isGhostUpdated()) + { + // construct process object and add it to the list + // hold pointer to our object in mAfterObject + // .. but this is not a hi fi object, so don't mess with tick cache + GameBaseListNode * po = (GameBaseListNode*)FrameAllocator::alloc(sizeof(GameBaseListNode)); + po->mObject = obj; + po->linkBefore(&list); + } + else if (obj->isGhostUpdated()) + { + // not hifi but we were updated, so perform net smooth now + obj->computeNetSmooth(mLastDelta); + } + + // clear out work flags + obj->setNetNearbyAdded(false); + obj->setGhostUpdated(false); + } + + // run through all the moves in the move list so we can play them with our control object + Move* movePtr; + U32 numMoves; + connection->mMoveList->resetClientMoves(); + connection->mMoveList->getMoves(&movePtr, &numMoves); + AssertFatal(mCatchup<=numMoves,"doh"); + + // tick catchup time + for (U32 m=0; mmNext) + { + // note that we get object from after object not getGameBase function + // this is because we are an on the fly linked list which uses mAfterObject + // rather than the linked list embedded in GameBase (clean this up?) + GameBase * obj = walk->mObject; + + // it's possible for a non-hifi object to get in here, but + // only if it is a control object...make sure we don't do any + // of the tick cache stuff if we are not hifi. + bool hifi = obj->getTypeMask() & GameBaseHiFiObjectType; + TickCacheEntry * tce = hifi ? obj->getTickCache().incCacheList() : NULL; + + // tick object + if (obj==connection->getControlObject()) + { + obj->processTick(movePtr); + movePtr->checksum = obj->getPacketDataChecksum(connection); + movePtr++; + } + else + { + AssertFatal(tce && hifi,"Should not get in here unless a hi fi object!!!"); + obj->processTick(tce->move); + } + + if (hifi) + { + BitStream bs(tce->packetData,TickCacheEntry::MaxPacketSize); + obj->writePacketData(connection,&bs); + } + } + if (connection->getControlObject() == NULL) + movePtr++; + } + connection->mMoveList->clearMoves(mCatchup); + + // Handle network error smoothing here...but only for control object + GameBase * control = connection->getControlObject(); + if (control && !control->isNewGhost()) + { + control->computeNetSmooth(mLastDelta); + control->setNewGhost(false); + } + + if (moveSync.doAction() && moveSync.moveDiff>0) + { + S32 moveDiff = moveSync.moveDiff; +#ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("client timewarping to catchup %i moves",moveDiff); +#endif + while (moveDiff--) + advanceObjects(); + moveSync.reset(); + } + +#ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("---------"); +#endif + + // all caught up + mCatchup = 0; +} + +//-------------------------------------------------------------------------- +// HifiServerProcessList +//-------------------------------------------------------------------------- + +void HifiServerProcessList::onTickObject(ProcessObject * pobj) +{ + // Each object is advanced a single tick + // If it's controlled by a client, tick using a move. + + Move *movePtr; + U32 numMoves; + GameConnection *con = pobj->getControllingClient(); + SimObjectPtr obj = getGameBase( pobj ); + + if ( obj && con && con->getControlObject() == obj && con->mMoveList->getMoves( &movePtr, &numMoves ) ) + { +#ifdef TORQUE_DEBUG_NET_MOVES + U32 sum = Move::ChecksumMask & obj->getPacketDataChecksum( obj->getControllingClient() ); +#endif + + obj->processTick(movePtr); + + if ( bool(obj) && obj->getControllingClient() ) + { + U32 newsum = Move::ChecksumMask & obj->getPacketDataChecksum( obj->getControllingClient() ); + + // check move checksum + if ( movePtr->checksum != newsum ) + { +#ifdef TORQUE_DEBUG_NET_MOVES + if ( !obj->mIsAiControlled ) + Con::printf( "move %i checksum disagree: %i != %i, (start %i), (move %f %f %f)", + movePtr->id, movePtr->checksum, newsum, sum, movePtr->yaw, movePtr->y, movePtr->z ); +#endif + movePtr->checksum = Move::ChecksumMismatch; + } + else + { +#ifdef TORQUE_DEBUG_NET_MOVES + Con::printf( "move %i checksum agree: %i == %i, (start %i), (move %f %f %f)", + movePtr->id, movePtr->checksum, newsum, sum, movePtr->yaw, movePtr->y, movePtr->z ); +#endif + } + + // Adding this seems to fix constant corrections, but is it + // really a sound fix? + con->mMoveList->clearMoves( 1 ); + } + } + else if ( pobj->isTicking() ) + pobj->processTick( 0 ); +} diff --git a/Engine/source/T3D/gameBase/hifi/hifiGameProcess.h b/Engine/source/T3D/gameBase/hifi/hifiGameProcess.h new file mode 100644 index 000000000..520834990 --- /dev/null +++ b/Engine/source/T3D/gameBase/hifi/hifiGameProcess.h @@ -0,0 +1,91 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GAMEPROCESS_HIFI_H_ +#define _GAMEPROCESS_HIFI_H_ + +#ifndef _GAMEPROCESS_H_ +#include "T3D/gameBase/gameProcess.h" +#endif + + +/// List to keep track of GameBases to process. +class HifiClientProcessList : public ClientProcessList +{ + typedef ClientProcessList Parent; + friend class HifiMoveList; + +public: + + HifiClientProcessList(); + + // ProcessList + bool advanceTime(SimTime timeDelta); + + // ClientProcessList + void clientCatchup(GameConnection*); + + static void init(); + static void shutdown(); + +protected: + + // tick cache functions -- client only + void ageTickCache(S32 numToAge, S32 len); + void forceHifiReset(bool reset) { mForceHifiReset=reset; } + U32 getTotalTicks() { return mTotalTicks; } + void updateMoveSync(S32 moveDiff); + void skipAdvanceObjects(U32 ms) { mSkipAdvanceObjectsMs += ms; } + + // ProcessList + void onTickObject(ProcessObject *); + void advanceObjects(); + void onAdvanceObjects(); + + void setCatchup(U32 catchup) { mCatchup = catchup; } + +protected: + + U32 mSkipAdvanceObjectsMs; + bool mForceHifiReset; + U32 mCatchup; +}; + + +class HifiServerProcessList : public ServerProcessList +{ + typedef ServerProcessList Parent; + +public: + + HifiServerProcessList() {} + + static void init(); + static void shutdown(); + +protected: + + // ProcessList + void onTickObject(ProcessObject *); +}; + +#endif // _GAMEPROCESS_HIFI_H_ \ No newline at end of file diff --git a/Engine/source/T3D/gameBase/hifi/hifiMoveList.cpp b/Engine/source/T3D/gameBase/hifi/hifiMoveList.cpp new file mode 100644 index 000000000..74ca24ff2 --- /dev/null +++ b/Engine/source/T3D/gameBase/hifi/hifiMoveList.cpp @@ -0,0 +1,443 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "T3D/gameBase/hifi/hifiMoveList.h" +#include "T3D/gameBase/hifi/hifiGameProcess.h" +#include "T3D/gameBase/gameConnection.h" +#include "core/stream/bitStream.h" + +#define MAX_MOVE_PACKET_SENDS 4 + +const U32 DefaultTargetMoveListSize = 3; +const U32 DefaultMaxMoveSizeList = 5; +const F32 DefaultSmoothMoveAvg = 0.15f; +const F32 DefaultMoveListSizeSlack = 1.0f; + +HifiMoveList::HifiMoveList() +{ + mLastSentMove = 0; + mAvgMoveQueueSize = DefaultTargetMoveListSize ; + mTargetMoveListSize = DefaultTargetMoveListSize; + mMaxMoveListSize = DefaultMaxMoveSizeList; + mSmoothMoveAvg = DefaultSmoothMoveAvg; + mMoveListSizeSlack = DefaultMoveListSizeSlack; + mTotalServerTicks = ServerTicksUninitialized; + mSuppressMove = false; +} + +void HifiMoveList::updateClientServerTickDiff(S32 & tickDiff) +{ + if (mLastMoveAck==0) + tickDiff=0; + + // Make adjustments to move list to account for tick mis-matches between client and server. + if (tickDiff>0) + { + // Server ticked more than client. Adjust for this by reseting all hifi objects + // to a later position in the tick cache (see ageTickCache below) and at the same + // time pulling back some moves we thought we had made (so that time on client + // doesn't change). + S32 dropTicks = tickDiff; + while (dropTicks) + { +#ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("dropping move%s",mLastClientMove>mFirstMoveIndex ? "" : " but none there"); +#endif + if (mLastClientMove>mFirstMoveIndex) + mLastClientMove--; + else + tickDiff--; + dropTicks--; + } + AssertFatal(mLastClientMove >= mFirstMoveIndex, "Bad move request"); + AssertFatal(mLastClientMove - mFirstMoveIndex <= mMoveVec.size(), "Desynched first and last move."); + } + else + { + // Client ticked more than server. Adjust for this by taking extra moves + // (either adding back moves that were dropped above, or taking new ones). + for (S32 i=0; i<-tickDiff; i++) + { + if (mMoveVec.size() > mLastClientMove - mFirstMoveIndex) + { +#ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("add back move"); +#endif + mLastClientMove++; + } + else + { +#ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("add back move -- create one"); +#endif + collectMove(); + mLastClientMove++; + } + } + } + + // drop moves that are not made yet (because we rolled them back) and not yet sent + U32 len = getMax(mLastClientMove-mFirstMoveIndex,mLastSentMove-mFirstMoveIndex); + mMoveVec.setSize(len); + +#ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("move list size: %i, last move: %i, last sent: %i",mMoveVec.size(),mLastClientMove-mFirstMoveIndex,mLastSentMove-mFirstMoveIndex); +#endif +} + +S32 HifiMoveList::getServerTicks(U32 serverTickNum) +{ + S32 serverTicks=0; + if (serverTicksInitialized()) + { + // handle tick wrapping... + const S32 MaxTickCount = (1<>1; + U32 prevTickNum = mTotalServerTicks & TotalTicksMask; + serverTicks = serverTickNum-prevTickNum; + if (serverTicks>HalfMaxTickCount) + serverTicks -= MaxTickCount; + else if (-serverTicks>HalfMaxTickCount) + serverTicks += MaxTickCount; + AssertFatal(serverTicks>=0,"Server can't tick backwards!!!"); + if (serverTicks<0) + serverTicks=0; + } + mTotalServerTicks = serverTickNum; + return serverTicks; +} + +void HifiMoveList::markControlDirty() +{ + mLastClientMove = mLastMoveAck; + + // save state for future update + GameBase *obj = mConnection->getControlObject(); + AssertFatal(obj,"ClientProcessList::markControlDirty: no control object"); + obj->setGhostUpdated(true); + obj->getTickCache().beginCacheList(); + TickCacheEntry * tce = obj->getTickCache().incCacheList(); + BitStream bs(tce->packetData,TickCacheEntry::MaxPacketSize); + obj->writePacketData( mConnection, &bs ); +} + +void HifiMoveList::resetMoveList() +{ + mMoveVec.clear(); + mLastMoveAck = 0; + mLastClientMove = 0; + mFirstMoveIndex = 0; + mLastSentMove = 0; +} + +U32 HifiMoveList::getMoves(Move** movePtr,U32* numMoves) +{ + if (mConnection->isConnectionToServer()) + // give back moves starting at the last client move... + return Parent::getMoves(movePtr,numMoves); + + if (mSuppressMove || mMoveVec.size()==0) + { + *numMoves=0; + *movePtr=NULL; + } + else + { + *numMoves=1; + *movePtr=mMoveVec.begin(); + } + + return *numMoves; +} + +void HifiMoveList::advanceMove() +{ + S32 numMoves = mMoveVec.size(); + mAvgMoveQueueSize *= (1.0f-mSmoothMoveAvg); + mAvgMoveQueueSize += mSmoothMoveAvg * F32(numMoves); + +#ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("moves remaining: %i, running avg: %f",numMoves,mAvgMoveQueueSize); +#endif + + if (mAvgMoveQueueSizemMaxMoveListSize || (mAvgMoveQueueSize>mTargetMoveListSize+mMoveListSizeSlack && mMoveVec.size()>mTargetMoveListSize) ) + { + U32 drop = mMoveVec.size()-mTargetMoveListSize; + clearMoves(drop); + mAvgMoveQueueSize = (F32)mTargetMoveListSize; + +#ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("too many moves on server, dropping moves (%i)",drop); +#endif + } + + mSuppressMove = numMoves == 0; + + // now clear move + if (areMovesPending()) + clearMoves(1); +} + +void HifiMoveList::clientWriteMovePacket(BitStream *bstream) +{ + if (!serverTicksInitialized()) + resetMoveList(); + + AssertFatal(mLastMoveAck == mFirstMoveIndex, "Invalid move index."); + + // enforce limit on number of moves sent + if (mLastSentMove MaxMoveCount) + count = MaxMoveCount; + bstream->writeInt(start,32); + bstream->writeInt(count,MoveCountBits); + Move * prevMove = NULL; + for (int i = 0; i < count; i++) + { + move[offset + i].sendCount++; + move[offset + i].pack(bstream,prevMove); + bstream->writeInt(move[offset + i].checksum,Move::ChecksumBits); + prevMove = &move[offset+i]; + } +} + +void HifiMoveList::serverReadMovePacket(BitStream *bstream) +{ + // Server side packet read. + U32 start = bstream->readInt(32); + U32 count = bstream->readInt(MoveCountBits); + + Move * prevMove = NULL; + Move prevMoveHolder; + + // Skip forward (must be starting up), or over the moves + // we already have. + int skip = mLastMoveAck - start; + if (skip < 0) + { + mLastMoveAck = start; + } + else + { + if (skip > count) + skip = count; + for (int i = 0; i < skip; i++) + { + prevMoveHolder.unpack(bstream,prevMove); + prevMoveHolder.checksum = bstream->readInt(Move::ChecksumBits); + prevMove = &prevMoveHolder; + S32 idx = mMoveVec.size()-skip+i; + if (idx>=0) + { +#ifdef TORQUE_DEBUG_NET_MOVES + if (mMoveVec[idx].checksum != prevMoveHolder.checksum) + Con::printf("updated checksum on move %i from %i to %i",mMoveVec[idx].id,mMoveVec[idx].checksum,prevMoveHolder.checksum); +#endif + mMoveVec[idx].checksum = prevMoveHolder.checksum; + } + } + start += skip; + count = count - skip; + } + + // Put the rest on the move list. + int index = mMoveVec.size(); + mMoveVec.increment(count); + while (index < mMoveVec.size()) + { + mMoveVec[index].unpack(bstream,prevMove); + mMoveVec[index].checksum = bstream->readInt(Move::ChecksumBits); + prevMove = &mMoveVec[index]; + mMoveVec[index].id = start++; + index ++; + } + + mLastMoveAck += count; + + if (mMoveVec.size()>mMaxMoveListSize) + { + U32 drop = mMoveVec.size()-mTargetMoveListSize; + clearMoves(drop); + mAvgMoveQueueSize = (F32)mTargetMoveListSize; + +#ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("too many moves on server, dropping moves (%i)",drop); +#endif + } +} + +void HifiMoveList::serverWriteMovePacket(BitStream * bstream) +{ +#ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("ack %i minus %i",mLastMoveAck,mMoveVec.size()); +#endif + + // acknowledge only those moves that have been ticked + bstream->writeInt(mLastMoveAck - mMoveVec.size(),32); + + // send over the current tick count on the server... + bstream->writeInt(ServerProcessList::get()->getTotalTicks() & TotalTicksMask, TotalTicksBits); +} + +void HifiMoveList::clientReadMovePacket(BitStream * bstream) +{ + if (!serverTicksInitialized()) + resetMoveList(); + +#ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("pre move ack: %i", mLastMoveAck); +#endif + + mLastMoveAck = bstream->readInt(32); + +#ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("post move ack %i, first move %i, last move %i", mLastMoveAck, mFirstMoveIndex, mLastClientMove); +#endif + + // This is how many times we've ticked since last ack -- before adjustments below + S32 ourTicks = mLastMoveAck - mFirstMoveIndex; + + if (mLastMoveAck < mFirstMoveIndex) + mLastMoveAck = mFirstMoveIndex; + + if(mLastMoveAck > mLastClientMove) + { + ourTicks -= mLastMoveAck-mLastClientMove; + mLastClientMove = mLastMoveAck; + } + while(mFirstMoveIndex < mLastMoveAck) + { + if (mMoveVec.size()) + { + mMoveVec.pop_front(); + mFirstMoveIndex++; + } + else + { + AssertWarn(1, "Popping off too many moves!"); + mFirstMoveIndex = mLastMoveAck; + } + } + + // get server ticks using total number of ticks on server to date... + U32 serverTickNum = bstream->readInt(TotalTicksBits); + S32 serverTicks = getServerTicks(serverTickNum); + S32 tickDiff = serverTicks - ourTicks; + +#ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("server ticks: %i, client ticks: %i, diff: %i%s", serverTicks, ourTicks, tickDiff, !tickDiff ? "" : " (ticks mis-match)"); +#endif + + + // Apply the first (of two) client-side synchronization mechanisms. Key is that + // we need to both synchronize client/server move streams (so first move in list is made + // at same "time" on both client and server) and maintain the "time" at which the most + // recent move was made on the server. In both cases, "time" is the number of ticks + // it took to get to that move. + updateClientServerTickDiff(tickDiff); + + // Apply the second (and final) client-side synchronization mechanism. The tickDiff adjustments above + // make sure time is preserved on client. But that assumes that a future (or previous) update will adjust + // time in the other direction, so that we don't get too far behind or ahead of the server. The updateMoveSync + // mechanism tracks us over time to make sure we eventually return to be in sync, and makes adjustments + // if we don't after a certain time period (number of updates). Unlike the tickDiff mechanism, when + // the updateMoveSync acts time is not preserved on the client. + HifiClientProcessList * processList = dynamic_cast(ClientProcessList::get()); + if (processList) + { + processList->updateMoveSync(mLastSentMove-mLastClientMove); + + // set catchup parameters... + U32 totalCatchup = mLastClientMove - mFirstMoveIndex; + + processList->ageTickCache(ourTicks + (tickDiff>0 ? tickDiff : 0), totalCatchup+1); + processList->forceHifiReset(tickDiff!=0); + processList->setCatchup(totalCatchup); + } +} + +void HifiMoveList::ghostPreRead(NetObject * nobj, bool newGhost) +{ + GameBase* obj = dynamic_cast(nobj); + if ( obj && ( obj->getTypeMask() & GameBaseHiFiObjectType ) && !newGhost ) + { + // set next cache entry to start + obj->getTickCache().beginCacheList(); + + // reset to old state because we are about to unpack (and then tick forward) + TickCacheEntry * tce = obj->getTickCache().incCacheList(false); + if (tce) + { + BitStream bs(tce->packetData,TickCacheEntry::MaxPacketSize); + obj->readPacketData(mConnection, &bs); + } + } +} + +void HifiMoveList::ghostReadExtra(NetObject * nobj, BitStream * bstream, bool newGhost) +{ + // Receive additional per ghost information. + // Get pending moves for ghosts that have them and add the moves to + // the tick cache. + GameBase* obj = dynamic_cast(nobj); + if ( obj && ( obj->getTypeMask() & GameBaseHiFiObjectType ) ) + { + // mark ghost so that it updates correctly + obj->setGhostUpdated(true); + obj->setNewGhost(newGhost); + + // set next cache entry to start + obj->getTickCache().beginCacheList(); + + // save state for future update + TickCacheEntry * tce = obj->getTickCache().incCacheList(); + BitStream bs(tce->packetData,TickCacheEntry::MaxPacketSize); + obj->writePacketData(mConnection, &bs); + } +} diff --git a/Engine/source/T3D/gameBase/hifi/hifiMoveList.h b/Engine/source/T3D/gameBase/hifi/hifiMoveList.h new file mode 100644 index 000000000..1ad467c8a --- /dev/null +++ b/Engine/source/T3D/gameBase/hifi/hifiMoveList.h @@ -0,0 +1,76 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MOVELIST_HIFI_H_ +#define _MOVELIST_HIFI_H_ + +#ifndef _MOVELIST_H_ +#include "T3D/gameBase/moveList.h" +#endif + +class HifiMoveList : public MoveList +{ + typedef MoveList Parent; + +public: + + HifiMoveList(); + + void init() { mTotalServerTicks = ServerTicksUninitialized; } + + void ghostReadExtra(NetObject *,BitStream *, bool newGhost); + void ghostPreRead(NetObject *, bool newGhost); + + void clientWriteMovePacket(BitStream *bstream); + void clientReadMovePacket(BitStream *); + void serverWriteMovePacket(BitStream *); + void serverReadMovePacket(BitStream *bstream); + + void markControlDirty(); + U32 getMoves(Move**,U32* numMoves); + void onAdvanceObjects() { if (mMoveVec.size() > mLastSentMove-mFirstMoveIndex) mLastSentMove++; } + + void advanceMove(); + +protected: + void resetMoveList(); + S32 getServerTicks(U32 serverTickNum); + void updateClientServerTickDiff(S32 & tickDiff); + bool serverTicksInitialized() { return mTotalServerTicks!=ServerTicksUninitialized; } + +protected: + U32 mLastSentMove; + F32 mAvgMoveQueueSize; + + // server side move list management + U32 mTargetMoveListSize; // Target size of move buffer on server + U32 mMaxMoveListSize; // Max size move buffer allowed to grow to + F32 mSmoothMoveAvg; // Smoothing parameter for move list size running average + F32 mMoveListSizeSlack; // Amount above/below target size move list running average allowed to diverge + bool mSuppressMove; // If true, don't return move on server + + // client side tracking of server ticks + enum { TotalTicksBits=10, TotalTicksMask = (1< MaxMoveQueueSize ) + return false; + + F32 pitchAdd = MoveManager::mPitchUpSpeed - MoveManager::mPitchDownSpeed; + F32 yawAdd = MoveManager::mYawLeftSpeed - MoveManager::mYawRightSpeed; + F32 rollAdd = MoveManager::mRollRightSpeed - MoveManager::mRollLeftSpeed; + + curMove.pitch = MoveManager::mPitch + pitchAdd; + curMove.yaw = MoveManager::mYaw + yawAdd; + curMove.roll = MoveManager::mRoll + rollAdd; + + MoveManager::mPitch = 0; + MoveManager::mYaw = 0; + MoveManager::mRoll = 0; + + curMove.x = MoveManager::mRightAction - MoveManager::mLeftAction + MoveManager::mXAxis_L; + curMove.y = MoveManager::mForwardAction - MoveManager::mBackwardAction + MoveManager::mYAxis_L; + curMove.z = MoveManager::mUpAction - MoveManager::mDownAction; + + curMove.freeLook = MoveManager::mFreeLook; + curMove.deviceIsKeyboardMouse = MoveManager::mDeviceIsKeyboardMouse; + + for(U32 i = 0; i < MaxTriggerKeys; i++) + { + curMove.trigger[i] = false; + if(MoveManager::mTriggerCount[i] & 1) + curMove.trigger[i] = true; + else if(!(MoveManager::mPrevTriggerCount[i] & 1) && MoveManager::mPrevTriggerCount[i] != MoveManager::mTriggerCount[i]) + curMove.trigger[i] = true; + MoveManager::mPrevTriggerCount[i] = MoveManager::mTriggerCount[i]; + } + + if (mConnection->getControlObject()) + mConnection->getControlObject()->preprocessMove(&curMove); + + curMove.clamp(); // clamp for net traffic + return true; +} + +void MoveList::pushMove(const Move &mv) +{ + U32 id = mFirstMoveIndex + mMoveVec.size(); + U32 sz = mMoveVec.size(); + mMoveVec.push_back(mv); + mMoveVec[sz].id = id; + mMoveVec[sz].sendCount = 0; +} + +U32 MoveList::getMoves(Move** movePtr,U32* numMoves) +{ + if (mConnection->isConnectionToServer()) + { + // give back moves starting at the last client move... + + AssertFatal(mLastClientMove >= mFirstMoveIndex, "Bad move request"); + AssertFatal(mLastClientMove - mFirstMoveIndex <= mMoveVec.size(), "Desynched first and last move."); + *numMoves = mMoveVec.size() - mLastClientMove + mFirstMoveIndex; + *movePtr = mMoveVec.address() + mLastClientMove - mFirstMoveIndex; + } + else + { + // return the full list + *numMoves = mMoveVec.size(); + *movePtr = mMoveVec.begin(); + } + + return *numMoves; +} + +void MoveList::collectMove() +{ + Move mv; + if (mConnection) + { + if(!mConnection->isPlayingBack() && getNextMove(mv)) + { + mv.checksum=Move::ChecksumMismatch; + pushMove(mv); + mConnection->recordBlock(GameConnection::BlockTypeMove, sizeof(Move), &mv); + } + } + else + { + if(getNextMove(mv)) + { + mv.checksum=Move::ChecksumMismatch; + pushMove(mv); + } + } +} + +void MoveList::clearMoves(U32 count) +{ + if (mConnection->isConnectionToServer()) + { + mLastClientMove += count; + AssertFatal(mLastClientMove >= mFirstMoveIndex, "Bad move request"); + AssertFatal(mLastClientMove - mFirstMoveIndex <= mMoveVec.size(), "Desynched first and last move."); + if (!mConnection) + // drop right away if no connection + ackMoves(count); + } + else + { + AssertFatal(count <= mMoveVec.size(),"GameConnection: Clearing too many moves"); + for (S32 i=0; iisConnectionToServer() ? + mMoveVec.size() - mLastClientMove + mFirstMoveIndex : + mMoveVec.size(); +} + +bool MoveList::isBacklogged() +{ + if ( !mConnection->isConnectionToServer() ) + return false; + + return mLastClientMove - mFirstMoveIndex == mMoveVec.size() && + mMoveVec.size() >= MaxMoveCount; +} + +void MoveList::ackMoves(U32 count) +{ + mLastMoveAck += count; + if(mLastMoveAck > mLastClientMove) + mLastClientMove = mLastMoveAck; + while(mFirstMoveIndex < mLastMoveAck) + { + if (mMoveVec.size()) + { + mMoveVec.pop_front(); + mFirstMoveIndex++; + } + else + { + AssertWarn(1, "Popping off too many moves!"); + mFirstMoveIndex = mLastMoveAck; + } + } +} + +void MoveList::writeDemoStartBlock(ResizeBitStream *stream) +{ + stream->write(mLastMoveAck); + stream->write(mLastClientMove); + stream->write(mFirstMoveIndex); + + stream->write(U32(mMoveVec.size())); + for(U32 j = 0; j < mMoveVec.size(); j++) + mMoveVec[j].pack(stream); +} + +void MoveList::readDemoStartBlock(BitStream *stream) +{ + stream->read(&mLastMoveAck); + stream->read(&mLastClientMove); + stream->read(&mFirstMoveIndex); + + U32 size; + Move mv; + stream->read(&size); + mMoveVec.clear(); + while(size--) + { + mv.unpack(stream); + pushMove(mv); + } +} diff --git a/Engine/source/T3D/gameBase/moveList.h b/Engine/source/T3D/gameBase/moveList.h new file mode 100644 index 000000000..5b96f7953 --- /dev/null +++ b/Engine/source/T3D/gameBase/moveList.h @@ -0,0 +1,122 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MOVELIST_H_ +#define _MOVELIST_H_ + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _MOVEMANAGER_H_ +#include "T3D/gameBase/moveManager.h" +#endif + +class BitStream; +class ResizeBitStream; +class NetObject; +class GameConnection; +class PlayerRep; +class ProcessList; + +class MoveList +{ +public: + + MoveList(); + virtual ~MoveList() {} + + virtual void init() {} + + void setConnection( GameConnection *connection) { mConnection = connection; } + + /// @name Move Packets + /// Write/read move data to the packet. + /// @{ + + virtual void ghostReadExtra( NetObject *, BitStream *, bool newGhost) {}; + virtual void ghostWriteExtra( NetObject *,BitStream * ) {}; + virtual void ghostPreRead( NetObject *, bool newGhost ) {}; + + virtual void clientWriteMovePacket( BitStream *bstream ) = 0; + virtual void clientReadMovePacket( BitStream * ) = 0; + + virtual void serverWriteMovePacket( BitStream * ) = 0; + virtual void serverReadMovePacket( BitStream *bstream ) = 0; + + virtual void writeDemoStartBlock( ResizeBitStream *stream ); + virtual void readDemoStartBlock( BitStream *stream ); + /// @} + + virtual void advanceMove() = 0; + virtual void onAdvanceObjects() = 0; + virtual U32 getMoves( Move**, U32 *numMoves ); + + /// Reset to beginning of client move list. + void resetClientMoves() { mLastClientMove = mFirstMoveIndex; } + + /// Reset move list back to last acknowledged move. + void resetCatchup() { mLastClientMove = mLastMoveAck; } + + void collectMove(); + void pushMove( const Move &mv ); + virtual void clearMoves( U32 count ); + + virtual void markControlDirty() { mLastClientMove = mLastMoveAck; } + bool isMismatch() { return mControlMismatch; } + void clearMismatch() { mControlMismatch = false; } + + /// Clear out all moves in the list and reset to initial state. + void reset(); + + /// If there are no pending moves and the input queue is full, + /// then the connection to the server must be clogged. + bool isBacklogged(); + + bool areMovesPending(); + + void ackMoves( U32 count ); + +protected: + + bool getNextMove( Move &curMove ); + +protected: + + enum + { + MoveCountBits = 5, + /// MaxMoveCount should not exceed the MoveManager's + /// own maximum (MaxMoveQueueSize) + MaxMoveCount = 30, + }; + + U32 mLastMoveAck; + U32 mLastClientMove; + U32 mFirstMoveIndex; + bool mControlMismatch; + + GameConnection *mConnection; + + Vector mMoveVec; +}; + +#endif // _MOVELIST_H_ diff --git a/Engine/source/T3D/gameBase/moveManager.cpp b/Engine/source/T3D/gameBase/moveManager.cpp new file mode 100644 index 000000000..e9212149b --- /dev/null +++ b/Engine/source/T3D/gameBase/moveManager.cpp @@ -0,0 +1,302 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "T3D/gameBase/moveManager.h" +#include "core/stream/bitStream.h" +#include "core/module.h" +#include "console/consoleTypes.h" +#include "core/strings/stringFunctions.h" +#include "math/mConstants.h" + + +MODULE_BEGIN( MoveManager ) + + MODULE_INIT + { + MoveManager::init(); + } + +MODULE_END; + + +bool MoveManager::mDeviceIsKeyboardMouse = false; +F32 MoveManager::mForwardAction = 0; +F32 MoveManager::mBackwardAction = 0; +F32 MoveManager::mUpAction = 0; +F32 MoveManager::mDownAction = 0; +F32 MoveManager::mLeftAction = 0; +F32 MoveManager::mRightAction = 0; + +bool MoveManager::mFreeLook = false; +F32 MoveManager::mPitch = 0; +F32 MoveManager::mYaw = 0; +F32 MoveManager::mRoll = 0; + +F32 MoveManager::mPitchUpSpeed = 0; +F32 MoveManager::mPitchDownSpeed = 0; +F32 MoveManager::mYawLeftSpeed = 0; +F32 MoveManager::mYawRightSpeed = 0; +F32 MoveManager::mRollLeftSpeed = 0; +F32 MoveManager::mRollRightSpeed = 0; + +F32 MoveManager::mXAxis_L = 0; +F32 MoveManager::mYAxis_L = 0; +F32 MoveManager::mXAxis_R = 0; +F32 MoveManager::mYAxis_R = 0; + +U32 MoveManager::mTriggerCount[MaxTriggerKeys] = { 0, }; +U32 MoveManager::mPrevTriggerCount[MaxTriggerKeys] = { 0, }; + +const Move NullMove = +{ + /*px=*/16, /*py=*/16, /*pz=*/16, + /*pyaw=*/0, /*ppitch=*/0, /*proll=*/0, + /*x=*/0, /*y=*/0,/*z=*/0, + /*yaw=*/0, /*pitch=*/0, /*roll=*/0, + /*id=*/0, + /*sendCount=*/0, + + /*checksum=*/false, + /*deviceIsKeyboardMouse=*/false, + /*freeLook=*/false, + /*triggers=*/{false,false,false,false,false,false} +}; + +void MoveManager::init() +{ + Con::addVariable("mvForwardAction", TypeF32, &mForwardAction, + "Forwards movement speed for the active player.\n" + "@ingroup Game"); + Con::addVariable("mvBackwardAction", TypeF32, &mBackwardAction, + "Backwards movement speed for the active player.\n" + "@ingroup Game"); + Con::addVariable("mvUpAction", TypeF32, &mUpAction, + "Upwards movement speed for the active player.\n" + "@ingroup Game"); + Con::addVariable("mvDownAction", TypeF32, &mDownAction, + "Downwards movement speed for the active player.\n" + "@ingroup Game"); + Con::addVariable("mvLeftAction", TypeF32, &mLeftAction, + "Left movement speed for the active player.\n" + "@ingroup Game"); + Con::addVariable("mvRightAction", TypeF32, &mRightAction, + "Right movement speed for the active player.\n" + "@ingroup Game"); + + Con::addVariable("mvFreeLook", TypeBool, &mFreeLook, + "Boolean state for if freelook is active or not.\n" + "@ingroup Game"); + Con::addVariable("mvDeviceIsKeyboardMouse", TypeBool, &mDeviceIsKeyboardMouse, + "Boolean state for it the system is using a keyboard and mouse or not.\n" + "@ingroup Game"); + Con::addVariable("mvPitch", TypeF32, &mPitch, + "Current pitch value, typically applied through input devices, such as a mouse.\n" + "@ingroup Game"); + Con::addVariable("mvYaw", TypeF32, &mYaw, + "Current yaw value, typically applied through input devices, such as a mouse.\n" + "@ingroup Game"); + Con::addVariable("mvRoll", TypeF32, &mRoll, + "Current roll value, typically applied through input devices, such as a mouse.\n" + "@ingroup Game"); + Con::addVariable("mvPitchUpSpeed", TypeF32, &mPitchUpSpeed, + "Upwards pitch speed.\n" + "@ingroup Game"); + Con::addVariable("mvPitchDownSpeed", TypeF32, &mPitchDownSpeed, + "Downwards pitch speed.\n" + "@ingroup Game"); + Con::addVariable("mvYawLeftSpeed", TypeF32, &mYawLeftSpeed, + "Left Yaw speed.\n" + "@ingroup Game"); + Con::addVariable("mvYawRightSpeed", TypeF32, &mYawRightSpeed, + "Right Yaw speed.\n" + "@ingroup Game"); + Con::addVariable("mvRollLeftSpeed", TypeF32, &mRollLeftSpeed, + "Left roll speed.\n" + "@ingroup Game"); + Con::addVariable("mvRollRightSpeed", TypeF32, &mRollRightSpeed, + "Right roll speed.\n" + "@ingroup Game"); + + // Dual-analog + Con::addVariable( "mvXAxis_L", TypeF32, &mXAxis_L, + "Left thumbstick X axis position on a dual-analog gamepad.\n" + "@ingroup Game" ); + Con::addVariable( "mvYAxis_L", TypeF32, &mYAxis_L, + "Left thumbstick Y axis position on a dual-analog gamepad.\n" + "@ingroup Game" ); + + Con::addVariable( "mvXAxis_R", TypeF32, &mXAxis_R, + "Right thumbstick X axis position on a dual-analog gamepad.\n" + "@ingroup Game" ); + Con::addVariable( "mvYAxis_R", TypeF32, &mYAxis_R, + "Right thumbstick Y axis position on a dual-analog gamepad.\n" + "@ingroup Game"); + + for(U32 i = 0; i < MaxTriggerKeys; i++) + { + char varName[256]; + dSprintf(varName, sizeof(varName), "mvTriggerCount%d", i); + Con::addVariable(varName, TypeS32, &mTriggerCount[i], + "Used to determine the trigger counts of buttons. Namely used for input actions such as jumping and weapons firing.\n" + "@ingroup Game"); + } +} + +static inline F32 clampFloatWrap(F32 val) +{ + return val - F32(S32(val)); +} + +static inline S32 clampRangeClamp(F32 val) +{ + if(val < -1) + return 0; + if(val > 1) + return 32; + + // 0.5 / 16 = 0.03125 ... this forces a round up to + // make the precision near zero equal in the negative + // and positive directions. See... + // + // http://www.garagegames.com/community/forums/viewthread/49714 + + return (S32)((val + 1.03125) * 16); +} + + +#define FANG2IANG(x) ((U32)((S16)((F32(0x10000) / M_2PI) * x)) & 0xFFFF) +#define IANG2FANG(x) (F32)((M_2PI / F32(0x10000)) * (F32)((S16)x)) + +void Move::unclamp() +{ + yaw = IANG2FANG(pyaw); + pitch = IANG2FANG(ppitch); + roll = IANG2FANG(proll); + + x = (px - 16) / F32(16); + y = (py - 16) / F32(16); + z = (pz - 16) / F32(16); +} + +static inline F32 clampAngleClamp( F32 angle ) +{ + const F32 limit = ( M_PI_F / 180.0f ) * 179.999f; + if ( angle < -limit ) + return -limit; + if ( angle > limit ) + return limit; + + return angle; +} + +void Move::clamp() +{ + // If yaw/pitch/roll goes equal or greater than -PI/+PI it + // flips the direction of the rotation... we protect against + // that by clamping before the conversion. + + yaw = clampAngleClamp( yaw ); + pitch = clampAngleClamp( pitch ); + roll = clampAngleClamp( roll ); + + // angles are all 16 bit. + pyaw = FANG2IANG(yaw); + ppitch = FANG2IANG(pitch); + proll = FANG2IANG(roll); + + px = clampRangeClamp(x); + py = clampRangeClamp(y); + pz = clampRangeClamp(z); + unclamp(); +} + +void Move::pack(BitStream *stream, const Move * basemove) +{ + bool alwaysWriteAll = basemove!=NULL; + if (!basemove) + basemove = &NullMove; + + S32 i; + bool triggerDifferent = false; + for (i=0; i < MaxTriggerKeys; i++) + if (trigger[i] != basemove->trigger[i]) + triggerDifferent = true; + bool somethingDifferent = (pyaw!=basemove->pyaw) || + (ppitch!=basemove->ppitch) || + (proll!=basemove->proll) || + (px!=basemove->px) || + (py!=basemove->py) || + (pz!=basemove->pz) || + (deviceIsKeyboardMouse!=basemove->deviceIsKeyboardMouse) || + (freeLook!=basemove->freeLook) || + triggerDifferent; + + if (alwaysWriteAll || stream->writeFlag(somethingDifferent)) + { + if(stream->writeFlag(pyaw != basemove->pyaw)) + stream->writeInt(pyaw, 16); + if(stream->writeFlag(ppitch != basemove->ppitch)) + stream->writeInt(ppitch, 16); + if(stream->writeFlag(proll != basemove->proll)) + stream->writeInt(proll, 16); + + if (stream->writeFlag(px != basemove->px)) + stream->writeInt(px, 6); + if (stream->writeFlag(py != basemove->py)) + stream->writeInt(py, 6); + if (stream->writeFlag(pz != basemove->pz)) + stream->writeInt(pz, 6); + stream->writeFlag(freeLook); + stream->writeFlag(deviceIsKeyboardMouse); + + if (stream->writeFlag(triggerDifferent)) + for(i = 0; i < MaxTriggerKeys; i++) + stream->writeFlag(trigger[i]); + } +} + +void Move::unpack(BitStream *stream, const Move * basemove) +{ + bool alwaysReadAll = basemove!=NULL; + if (!basemove) + basemove=&NullMove; + + if (alwaysReadAll || stream->readFlag()) + { + pyaw = stream->readFlag() ? stream->readInt(16) : basemove->pyaw; + ppitch = stream->readFlag() ? stream->readInt(16) : basemove->ppitch; + proll = stream->readFlag() ? stream->readInt(16) : basemove->proll; + + px = stream->readFlag() ? stream->readInt(6) : basemove->px; + py = stream->readFlag() ? stream->readInt(6) : basemove->py; + pz = stream->readFlag() ? stream->readInt(6) : basemove->pz; + freeLook = stream->readFlag(); + deviceIsKeyboardMouse = stream->readFlag(); + + bool triggersDiffer = stream->readFlag(); + for (S32 i = 0; i< MaxTriggerKeys; i++) + trigger[i] = triggersDiffer ? stream->readFlag() : basemove->trigger[i]; + unclamp(); + } + else + *this = *basemove; +} diff --git a/Engine/source/T3D/gameBase/moveManager.h b/Engine/source/T3D/gameBase/moveManager.h new file mode 100644 index 000000000..9ee6d111c --- /dev/null +++ b/Engine/source/T3D/gameBase/moveManager.h @@ -0,0 +1,95 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MOVEMANAGER_H_ +#define _MOVEMANAGER_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif + +enum MoveConstants { + MaxTriggerKeys = 6, + MaxMoveQueueSize = 45, +}; + +class BitStream; + +struct Move +{ + enum { ChecksumBits = 16, ChecksumMask = ((1<mProcessLink.prev = mProcessLink.prev; + mProcessLink.prev->mProcessLink.next = mProcessLink.next; + mProcessLink.next = mProcessLink.prev = this; +} + +void ProcessObject::plLinkAfter(ProcessObject * obj) +{ + AssertFatal(mProcessLink.next == this && mProcessLink.prev == this,"ProcessObject::plLinkAfter: must be unlinked before calling this"); +#ifdef TORQUE_DEBUG + ProcessObject * test1 = obj; + ProcessObject * test2 = obj->mProcessLink.next; + ProcessObject * test3 = obj->mProcessLink.prev; + ProcessObject * test4 = this; +#endif + + // Link this after obj + mProcessLink.next = obj->mProcessLink.next; + mProcessLink.prev = obj; + obj->mProcessLink.next = this; + mProcessLink.next->mProcessLink.prev = this; + +#ifdef TORQUE_DEBUG + AssertFatal(test1->mProcessLink.next->mProcessLink.prev==test1 && test1->mProcessLink.prev->mProcessLink.next==test1,"Doh!"); + AssertFatal(test2->mProcessLink.next->mProcessLink.prev==test2 && test2->mProcessLink.prev->mProcessLink.next==test2,"Doh!"); + AssertFatal(test3->mProcessLink.next->mProcessLink.prev==test3 && test3->mProcessLink.prev->mProcessLink.next==test3,"Doh!"); + AssertFatal(test4->mProcessLink.next->mProcessLink.prev==test4 && test4->mProcessLink.prev->mProcessLink.next==test4,"Doh!"); +#endif +} + +void ProcessObject::plLinkBefore(ProcessObject * obj) +{ + AssertFatal(mProcessLink.next == this && mProcessLink.prev == this,"ProcessObject::plLinkBefore: must be unlinked before calling this"); +#ifdef TORQUE_DEBUG + ProcessObject * test1 = obj; + ProcessObject * test2 = obj->mProcessLink.next; + ProcessObject * test3 = obj->mProcessLink.prev; + ProcessObject * test4 = this; +#endif + + // Link this before obj + mProcessLink.next = obj; + mProcessLink.prev = obj->mProcessLink.prev; + obj->mProcessLink.prev = this; + mProcessLink.prev->mProcessLink.next = this; + +#ifdef TORQUE_DEBUG + AssertFatal(test1->mProcessLink.next->mProcessLink.prev==test1 && test1->mProcessLink.prev->mProcessLink.next==test1,"Doh!"); + AssertFatal(test2->mProcessLink.next->mProcessLink.prev==test2 && test2->mProcessLink.prev->mProcessLink.next==test2,"Doh!"); + AssertFatal(test3->mProcessLink.next->mProcessLink.prev==test3 && test3->mProcessLink.prev->mProcessLink.next==test3,"Doh!"); + AssertFatal(test4->mProcessLink.next->mProcessLink.prev==test4 && test4->mProcessLink.prev->mProcessLink.next==test4,"Doh!"); +#endif +} + +void ProcessObject::plJoin(ProcessObject * head) +{ + ProcessObject * tail1 = head->mProcessLink.prev; + ProcessObject * tail2 = mProcessLink.prev; + tail1->mProcessLink.next = this; + mProcessLink.prev = tail1; + tail2->mProcessLink.next = head; + head->mProcessLink.prev = tail2; +} + +//-------------------------------------------------------------------------- + +ProcessList::ProcessList() +{ + mCurrentTag = 0; + mDirty = false; + + mTotalTicks = 0; + mLastTick = 0; + mLastTime = 0; + mLastDelta = 0.0f; +} + +void ProcessList::addObject( ProcessObject *obj ) +{ + obj->plLinkAfter(&mHead); +} + +//---------------------------------------------------------------------------- + +void ProcessList::orderList() +{ + // ProcessObject tags are initialized to 0, so current tag should never be 0. + if (++mCurrentTag == 0) + mCurrentTag++; + + // Install a temporary head node + ProcessObject list; + list.plLinkBefore(mHead.mProcessLink.next); + mHead.plUnlink(); + + // start out by (bubble) sorting list by GUID + for (ProcessObject * cur = list.mProcessLink.next; cur != &list; cur = cur->mProcessLink.next) + { + if (cur->mOrderGUID == 0) + // special case -- can be no lower, so accept as lowest (this is also + // a common value since it is what non ordered objects have) + continue; + + for (ProcessObject * walk = cur->mProcessLink.next; walk != &list; walk = walk->mProcessLink.next) + { + if (walk->mOrderGUID < cur->mOrderGUID) + { + // swap walk and cur -- need to be careful because walk might be just after cur + // so insert after item before cur and before item after walk + ProcessObject * before = cur->mProcessLink.prev; + ProcessObject * after = walk->mProcessLink.next; + cur->plUnlink(); + walk->plUnlink(); + cur->plLinkBefore(after); + walk->plLinkAfter(before); + ProcessObject * swap = walk; + walk = cur; + cur = swap; + } + } + } + + // Reverse topological sort into the original head node + while (list.mProcessLink.next != &list) + { + ProcessObject * ptr = list.mProcessLink.next; + ProcessObject * afterObject = ptr->getAfterObject(); + ptr->mProcessTag = mCurrentTag; + ptr->plUnlink(); + if (afterObject) + { + // Build chain "stack" of dependent objects and patch + // it to the end of the current list. + while (afterObject && afterObject->mProcessTag != mCurrentTag) + { + afterObject->mProcessTag = mCurrentTag; + afterObject->plUnlink(); + afterObject->plLinkBefore(ptr); + ptr = afterObject; + afterObject = ptr->getAfterObject(); + } + ptr->plJoin(&mHead); + } + else + ptr->plLinkBefore(&mHead); + } + mDirty = false; +} + +GameBase* ProcessList::getGameBase( ProcessObject *obj ) +{ + if ( !obj->mIsGameBase ) + return NULL; + + return static_cast< GameBase* >( obj ); +} + + +void ProcessList::dumpToConsole() +{ + for (ProcessObject * pobj = mHead.mProcessLink.next; pobj != &mHead; pobj = pobj->mProcessLink.next) + { + SimObject * obj = dynamic_cast(pobj); + if (obj) + Con::printf("id %i, order guid %i, type %s", obj->getId(), pobj->mOrderGUID, obj->getClassName()); + else + Con::printf("---unknown object type, order guid %i", pobj->mOrderGUID); + } +} + +//---------------------------------------------------------------------------- + +bool ProcessList::advanceTime(SimTime timeDelta) +{ + PROFILE_START(ProcessList_AdvanceTime); + + // some drivers change the FPU control state, which will break our control object simulation + // (leading to packet mismatch errors due to small FP differences). So set it to the known + // state before advancing. + U32 mathState = Platform::getMathControlState(); + Platform::setMathControlStateKnown(); + + if (mDirty) + orderList(); + + SimTime targetTime = mLastTime + timeDelta; + SimTime targetTick = targetTime - (targetTime % TickMs); + SimTime tickDelta = targetTick - mLastTick; + bool tickPass = mLastTick != targetTick; + + if ( tickPass ) + mPreTick.trigger(); + + // Advance all the objects. + for (; mLastTick != targetTick; mLastTick += TickMs) + onAdvanceObjects(); + + mLastTime = targetTime; + mLastDelta = ((TickMs - ((targetTime+1) % TickMs)) % TickMs) / F32(TickMs); + + if ( tickPass ) + mPostTick.trigger( tickDelta ); + + // restore math control state in case others are relying on it being a certain value + Platform::setMathControlState(mathState); + + PROFILE_END(); + return tickPass; +} + +//---------------------------------------------------------------------------- + +void ProcessList::advanceObjects() +{ + PROFILE_START(ProcessList_AdvanceObjects); + + // A little link list shuffling is done here to avoid problems + // with objects being deleted from within the process method. + ProcessObject list; + list.plLinkBefore(mHead.mProcessLink.next); + mHead.plUnlink(); + for (ProcessObject * pobj = list.mProcessLink.next; pobj != &list; pobj = list.mProcessLink.next) + { + pobj->plUnlink(); + pobj->plLinkBefore(&mHead); + + onTickObject(pobj); + } + + mTotalTicks++; + + PROFILE_END(); +} + + + diff --git a/Engine/source/T3D/gameBase/processList.h b/Engine/source/T3D/gameBase/processList.h new file mode 100644 index 000000000..67c769e70 --- /dev/null +++ b/Engine/source/T3D/gameBase/processList.h @@ -0,0 +1,193 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PROCESSLIST_H_ +#define _PROCESSLIST_H_ + +#ifndef _SIM_H_ +#include "console/sim.h" +#endif +#ifndef _TSIGNAL_H_ +#include "core/util/tSignal.h" +#endif + +//---------------------------------------------------------------------------- + +#define TickMs 32 +#define TickSec (F32(TickMs) / 1000.0f) + +//---------------------------------------------------------------------------- + +class GameConnection; +struct Move; + + +class ProcessObject +{ + +public: + + ProcessObject(); + virtual ~ProcessObject() { removeFromProcessList(); } + + /// Removes this object from the tick-processing list + void removeFromProcessList() { plUnlink(); } + + /// Set the status of tick processing. + /// + /// Set true to receive processTick, advanceTime, and interpolateTick calls. + /// + /// @see processTick + /// @param t If true, tick processing is enabled. + virtual void setProcessTick( bool t ) { mProcessTick = t; } + + /// Returns true if this object processes ticks. + bool isTicking() const { return mProcessTick; } + + /// This is really implemented in GameBase and is only here to avoid + /// casts within ProcessList. + virtual GameConnection* getControllingClient() { return NULL; } + + /// This is really implemented in GameBase and is only here to avoid + /// casts within ProcessList. + virtual U32 getPacketDataChecksum( GameConnection *conn ) { return -1; } + + /// Force this object to process after some other object. + /// + /// For example, a player mounted to a vehicle would want to process after + /// the vehicle to prevent a visible 'lagging' from occurring when the + /// vehicle moves. So the player would be set to processAfter(theVehicle). + /// + /// @param obj Object to process after + virtual void processAfter( ProcessObject *obj ) {} + + /// Clears the effects of a call to processAfter() + virtual void clearProcessAfter() {} + + /// Returns the object that this processes after. + /// + /// @see processAfter + virtual ProcessObject* getAfterObject() const { return NULL; } + + /// Processes a move event and updates object state once every 32 milliseconds. + /// + /// This takes place both on the client and server, every 32 milliseconds (1 tick). + /// + /// @see ProcessList + /// @param move Move event corresponding to this tick, or NULL. + virtual void processTick( const Move *move ) {} + + /// Interpolates between tick events. This takes place on the CLIENT ONLY. + /// + /// @param delta Time since last call to interpolate + virtual void interpolateTick( F32 delta ) {} + + /// Advances simulation time for animations. This is called every frame. + /// + /// @param dt Time since last advance call + virtual void advanceTime( F32 dt ) {} + + /// Allow object to modify the Move before it is ticked or sent to the server. + /// This is only called for the control object on the client-side. + virtual void preprocessMove( Move *move ) {} + +//protected: + + struct Link + { + ProcessObject *next; + ProcessObject *prev; + }; + + // Processing interface + void plUnlink(); + void plLinkAfter(ProcessObject*); + void plLinkBefore(ProcessObject*); + void plJoin(ProcessObject*); + + U32 mProcessTag; // Tag used during sort + U32 mOrderGUID; // UID for keeping order synced (e.g., across network or runs of sim) + Link mProcessLink; // Ordered process queue + + bool mProcessTick; + + bool mIsGameBase; +}; + +//---------------------------------------------------------------------------- + +typedef Signal PreTickSignal; +typedef Signal PostTickSignal; +class GameBase; + +/// List of ProcessObjects. +class ProcessList +{ +public: + + ProcessList(); + virtual ~ProcessList() {} + + void markDirty() { mDirty = true; } + bool isDirty() { return mDirty; } + + SimTime getLastTime() { return mLastTime; } + F32 getLastDelta() { return mLastDelta; } + F32 getLastInterpDelta() { return mLastDelta / F32(TickMs); } + U32 getTotalTicks() { return mTotalTicks; } + void dumpToConsole(); + + PreTickSignal& preTickSignal() { return mPreTick; } + PostTickSignal& postTickSignal() { return mPostTick; } + + virtual void addObject( ProcessObject *obj ); + + /// Returns true if a tick was processed. + virtual bool advanceTime( SimTime timeDelta ); + +protected: + + void orderList(); + GameBase* getGameBase( ProcessObject *obj ); + + virtual void advanceObjects(); + virtual void onAdvanceObjects() { advanceObjects(); } + virtual void onPreTickObject( ProcessObject* ) {} + virtual void onTickObject( ProcessObject* ) {} + +protected: + + ProcessObject mHead; + + U32 mCurrentTag; + bool mDirty; + + U32 mTotalTicks; + SimTime mLastTick; + SimTime mLastTime; + F32 mLastDelta; + + PreTickSignal mPreTick; + PostTickSignal mPostTick; +}; + +#endif // _PROCESSLIST_H_ \ No newline at end of file diff --git a/Engine/source/T3D/gameBase/std/stdGameProcess.cpp b/Engine/source/T3D/gameBase/std/stdGameProcess.cpp new file mode 100644 index 000000000..63122b223 --- /dev/null +++ b/Engine/source/T3D/gameBase/std/stdGameProcess.cpp @@ -0,0 +1,400 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/gameBase/std/stdGameProcess.h" + +#include "platform/profiler.h" +#include "console/consoleTypes.h" +#include "core/dnet.h" +#include "core/stream/bitStream.h" +#include "core/frameAllocator.h" +#include "core/util/refBase.h" +#include "math/mPoint3.h" +#include "math/mMatrix.h" +#include "math/mathUtils.h" +#include "T3D/gameBase/gameBase.h" +#include "T3D/gameBase/gameConnection.h" +#include "T3D/gameBase/std/stdMoveList.h" +#include "T3D/fx/cameraFXMgr.h" + +MODULE_BEGIN( ProcessList ) + + MODULE_INIT + { + StdServerProcessList::init(); + StdClientProcessList::init(); + } + + MODULE_SHUTDOWN + { + StdServerProcessList::shutdown(); + StdClientProcessList::shutdown(); + } + +MODULE_END; + +void StdServerProcessList::init() +{ + smServerProcessList = new StdServerProcessList(); +} + +void StdServerProcessList::shutdown() +{ + delete smServerProcessList; +} + +void StdClientProcessList::init() +{ + smClientProcessList = new StdClientProcessList(); +} + +void StdClientProcessList::shutdown() +{ + delete smClientProcessList; +} + +//---------------------------------------------------------------------------- + + +namespace +{ + // local work class + struct GameBaseListNode + { + GameBaseListNode() + { + mPrev=this; + mNext=this; + mObject=NULL; + } + + GameBaseListNode * mPrev; + GameBaseListNode * mNext; + GameBase * mObject; + + void linkBefore(GameBaseListNode * obj) + { + // Link this before obj + mNext = obj; + mPrev = obj->mPrev; + obj->mPrev = this; + mPrev->mNext = this; + } + }; +} // namespace + +//-------------------------------------------------------------------------- +// ClientProcessList +//-------------------------------------------------------------------------- + +StdClientProcessList::StdClientProcessList() +{ +} + +bool StdClientProcessList::advanceTime( SimTime timeDelta ) +{ + PROFILE_SCOPE( StdClientProcessList_AdvanceTime ); + + if ( doBacklogged( timeDelta ) ) + return false; + + bool ret = Parent::advanceTime( timeDelta ); + ProcessObject *obj = NULL; + + AssertFatal( mLastDelta >= 0.0f && mLastDelta <= 1.0f, "mLastDelta is not zero to one."); + + obj = mHead.mProcessLink.next; + while ( obj != &mHead ) + { + if ( obj->isTicking() ) + obj->interpolateTick( mLastDelta ); + + obj = obj->mProcessLink.next; + } + + // Inform objects of total elapsed delta so they can advance + // client side animations. + F32 dt = F32(timeDelta) / 1000; + + // Update camera FX. + gCamFXMgr.update( dt ); + + obj = mHead.mProcessLink.next; + while ( obj != &mHead ) + { + obj->advanceTime( dt ); + obj = obj->mProcessLink.next; + } + + return ret; +} + +//---------------------------------------------------------------------------- +void StdClientProcessList::onAdvanceObjects() +{ + PROFILE_SCOPE( StdClientProcessList_OnAdvanceObjects ); + + GameConnection* connection = GameConnection::getConnectionToServer(); + if ( connection ) + { + // process any demo blocks that are NOT moves, and exactly one move + // we advance time in the demo stream by a move inserted on + // each tick. So before doing the tick processing we advance + // the demo stream until a move is ready + if ( connection->isPlayingBack() ) + { + U32 blockType; + do + { + blockType = connection->getNextBlockType(); + bool res = connection->processNextBlock(); + // if there are no more blocks, exit out of this function, + // as no more client time needs to process right now - we'll + // get it all on the next advanceClientTime() + if(!res) + return; + } + while ( blockType != GameConnection::BlockTypeMove ); + } + + connection->mMoveList->collectMove(); + advanceObjects(); + } + else + advanceObjects(); +} + +void StdClientProcessList::onTickObject( ProcessObject *obj ) +{ + PROFILE_SCOPE( StdClientProcessList_OnTickObject ); + + // In case the object deletes itself during its processTick. + SimObjectPtr safePtr = static_cast( obj ); + + // Each object is either advanced a single tick, or if it's + // being controlled by a client, ticked once for each pending move. + Move* movePtr; + U32 numMoves; + GameConnection* con = obj->getControllingClient(); + if ( con && con->getControlObject() == obj ) + { + con->mMoveList->getMoves( &movePtr, &numMoves ); + if ( numMoves ) + { + // Note: should only have a single move at this point + AssertFatal(numMoves==1,"ClientProccessList::onTickObject: more than one move in queue"); + + #ifdef TORQUE_DEBUG_NET_MOVES + U32 sum = Move::ChecksumMask & obj->getPacketDataChecksum(obj->getControllingClient()); + #endif + + if ( obj->isTicking() ) + obj->processTick( movePtr ); + + if ( bool(safePtr) && obj->getControllingClient() ) + { + U32 newsum = Move::ChecksumMask & obj->getPacketDataChecksum( obj->getControllingClient() ); + + // set checksum if not set or check against stored value if set + movePtr->checksum = newsum; + + #ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("move checksum: %i, (start %i), (move %f %f %f)", + movePtr->checksum,sum,movePtr->yaw,movePtr->y,movePtr->z); + #endif + } + con->mMoveList->clearMoves( 1 ); + } + } + else if ( obj->isTicking() ) + obj->processTick( 0 ); +} + +void StdClientProcessList::advanceObjects() +{ + PROFILE_SCOPE( StdClientProcessList_AdvanceObjects ); + + #ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("Advance client time..."); + #endif + + Parent::advanceObjects(); + + #ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("---------"); + #endif +} + +void StdClientProcessList::clientCatchup( GameConnection * connection ) +{ + SimObjectPtr control = connection->getControlObject(); + if ( control ) + { + Move * movePtr; + U32 numMoves; + U32 m = 0; + connection->mMoveList->getMoves( &movePtr, &numMoves ); + + #ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("client catching up... (%i)", numMoves); + #endif + + preTickSignal().trigger(); + + if ( control->isTicking() ) + for ( m = 0; m < numMoves; m++ ) + control->processTick( movePtr++ ); + + connection->mMoveList->clearMoves( m ); + } + + #ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("---------"); + #endif +} + +//-------------------------------------------------------------------------- +// ServerProcessList +//-------------------------------------------------------------------------- + +StdServerProcessList::StdServerProcessList() +{ +} + +void StdServerProcessList::onPreTickObject( ProcessObject *pobj ) +{ + if ( pobj->mIsGameBase ) + { + SimObjectPtr obj = getGameBase( pobj ); + + // Each object is either advanced a single tick, or if it's + // being controlled by a client, ticked once for each pending move. + GameConnection *con = obj->getControllingClient(); + + if ( con && con->getControlObject() == obj ) + { + Move* movePtr; + U32 numMoves; + con->mMoveList->getMoves( &movePtr, &numMoves ); + + if ( numMoves == 0 ) + { + #ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("no moves on object %i, skip tick",obj->getId()); + #endif + return; + } + } + } + + Parent::onPreTickObject (pobj ); +} + +void StdServerProcessList::onTickObject( ProcessObject *pobj ) +{ + PROFILE_SCOPE( StdServerProcessList_OnTickObject ); + + // Each object is either advanced a single tick, or if it's + // being controlled by a client, ticked once for each pending move. + + GameConnection *con = pobj->getControllingClient(); + + if ( pobj->mIsGameBase && con && con->getControlObject() == pobj ) + { + // In case the object is deleted during its own tick. + SimObjectPtr obj = getGameBase( pobj ); + + Move* movePtr; + U32 m, numMoves; + con->mMoveList->getMoves( &movePtr, &numMoves ); + + // For debugging it can be useful to know when this happens. + //if ( numMoves > 1 ) + // Con::printf( "numMoves: %i", numMoves ); + + // Do we really need to test the control object each iteration? Does it change? + for ( m = 0; m < numMoves && con && con->getControlObject() == obj; m++, movePtr++ ) + { + #ifdef TORQUE_DEBUG_NET_MOVES + U32 sum = Move::ChecksumMask & obj->getPacketDataChecksum(obj->getControllingClient()); + #endif + + if ( obj->isTicking() ) + obj->processTick( movePtr ); + + if ( con && con->getControlObject() == obj ) + { + U32 newsum = Move::ChecksumMask & obj->getPacketDataChecksum( obj->getControllingClient() ); + + // check move checksum + if ( movePtr->checksum != newsum ) + { + #ifdef TORQUE_DEBUG_NET_MOVES + if( !obj->isAIControlled() ) + Con::printf("move %i checksum disagree: %i != %i, (start %i), (move %f %f %f)", + movePtr->id, movePtr->checksum,newsum,sum,movePtr->yaw,movePtr->y,movePtr->z); + #endif + + movePtr->checksum = Move::ChecksumMismatch; + } + else + { + #ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("move %i checksum agree: %i == %i, (start %i), (move %f %f %f)", + movePtr->id, movePtr->checksum,newsum,sum,movePtr->yaw,movePtr->y,movePtr->z); + #endif + } + } + } + + con->mMoveList->clearMoves( m ); + } + else if ( pobj->isTicking() ) + pobj->processTick( 0 ); +} + +void StdServerProcessList::advanceObjects() +{ + #ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("Advance server time..."); + #endif + + Parent::advanceObjects(); + + // Credit all connections with the elapsed tick + SimGroup *clientGroup = Sim::getClientGroup(); + for(SimGroup::iterator i = clientGroup->begin(); i != clientGroup->end(); i++) + { + if (GameConnection *con = dynamic_cast(*i)) + { + con->mMoveList->advanceMove(); + } + } + + #ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("---------"); + #endif +} + + + diff --git a/Engine/source/T3D/gameBase/std/stdGameProcess.h b/Engine/source/T3D/gameBase/std/stdGameProcess.h new file mode 100644 index 000000000..96a548db9 --- /dev/null +++ b/Engine/source/T3D/gameBase/std/stdGameProcess.h @@ -0,0 +1,82 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GAMEPROCESS_STD_H_ +#define _GAMEPROCESS_STD_H_ + +//#include "T3D/gameBase/processList.h" +#ifndef _GAMEPROCESS_H_ +#include "T3D/gameBase/gameProcess.h" +#endif + +class GameBase; +class GameConnection; +struct Move; + +//---------------------------------------------------------------------------- + +/// List to keep track of GameBases to process. +class StdClientProcessList : public ClientProcessList +{ + typedef ClientProcessList Parent; + +protected: + + // ProcessList + void onTickObject(ProcessObject *); + void advanceObjects(); + void onAdvanceObjects(); + +public: + + StdClientProcessList(); + + // ProcessList + bool advanceTime( SimTime timeDelta ); + + // ClientProcessList + void clientCatchup( GameConnection *conn ); + + static void init(); + static void shutdown(); +}; + +class StdServerProcessList : public ServerProcessList +{ + typedef ServerProcessList Parent; + +protected: + + // ProcessList + void onPreTickObject( ProcessObject *pobj ); + void onTickObject( ProcessObject *pobj ); + void advanceObjects(); + +public: + + StdServerProcessList(); + + static void init(); + static void shutdown(); +}; + +#endif // _GAMEPROCESS_STD_H_ \ No newline at end of file diff --git a/Engine/source/T3D/gameBase/std/stdMoveList.cpp b/Engine/source/T3D/gameBase/std/stdMoveList.cpp new file mode 100644 index 000000000..6b4e11a72 --- /dev/null +++ b/Engine/source/T3D/gameBase/std/stdMoveList.cpp @@ -0,0 +1,198 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/gameBase/std/stdMoveList.h" + +#include "T3D/gameBase/gameConnection.h" +#include "core/stream/bitStream.h" + +#define MAX_MOVE_PACKET_SENDS 4 + + +StdMoveList::StdMoveList() +{ + mMoveCredit = MaxMoveCount; +} + +U32 StdMoveList::getMoves(Move** movePtr,U32* numMoves) +{ + if (!mConnection->isConnectionToServer()) + { + if (mMoveVec.size() > mMoveCredit) + mMoveVec.setSize(mMoveCredit); + } + return Parent::getMoves(movePtr,numMoves); +} + +void StdMoveList::clearMoves(U32 count) +{ + if (!mConnection->isConnectionToServer()) + { + count = mClamp(count,0,mMoveCredit); + mMoveCredit -= count; + } + + Parent::clearMoves(count); +} + +void StdMoveList::advanceMove() +{ + AssertFatal(!mConnection->isConnectionToServer(), "Cannot inc move credit on the client."); + + // Game tick increment + mMoveCredit++; + if (mMoveCredit > MaxMoveCount) + mMoveCredit = MaxMoveCount; + + // Clear pending moves for the elapsed time if there + // is no control object. + if ( mConnection->getControlObject() == NULL ) + mMoveVec.clear(); +} + +void StdMoveList::clientWriteMovePacket(BitStream *bstream) +{ + AssertFatal(mLastMoveAck == mFirstMoveIndex, "Invalid move index."); + U32 count = mMoveVec.size(); + + Move * move = mMoveVec.address(); + U32 start = mLastMoveAck; + U32 offset; + for(offset = 0; offset < count; offset++) + if(move[offset].sendCount < MAX_MOVE_PACKET_SENDS) + break; + if(offset == count && count != 0) + offset--; + + start += offset; + count -= offset; + + if (count > MaxMoveCount) + count = MaxMoveCount; + bstream->writeInt(start,32); + bstream->writeInt(count,MoveCountBits); + Move * prevMove = NULL; + for (int i = 0; i < count; i++) + { + move[offset + i].sendCount++; + move[offset + i].pack(bstream,prevMove); + bstream->writeInt(move[offset + i].checksum,Move::ChecksumBits); + prevMove = &move[offset+i]; + } +} + +void StdMoveList::serverReadMovePacket(BitStream *bstream) +{ + // Server side packet read. + U32 start = bstream->readInt(32); + U32 count = bstream->readInt(MoveCountBits); + + Move * prevMove = NULL; + Move prevMoveHolder; + + // Skip forward (must be starting up), or over the moves + // we already have. + int skip = mLastMoveAck - start; + if (skip < 0) + { + mLastMoveAck = start; + } + else + { + if (skip > count) + skip = count; + for (int i = 0; i < skip; i++) + { + prevMoveHolder.unpack(bstream,prevMove); + prevMoveHolder.checksum = bstream->readInt(Move::ChecksumBits); + prevMove = &prevMoveHolder; + S32 idx = mMoveVec.size()-skip+i; + if (idx>=0) + { +#ifdef TORQUE_DEBUG_NET_MOVES + if (mMoveVec[idx].checksum != prevMoveHolder.checksum) + Con::printf("updated checksum on move %i from %i to %i",mMoveVec[idx].id,mMoveVec[idx].checksum,prevMoveHolder.checksum); +#endif + mMoveVec[idx].checksum = prevMoveHolder.checksum; + } + } + start += skip; + count = count - skip; + } + + // Put the rest on the move list. + int index = mMoveVec.size(); + mMoveVec.increment(count); + while (index < mMoveVec.size()) + { + mMoveVec[index].unpack(bstream,prevMove); + mMoveVec[index].checksum = bstream->readInt(Move::ChecksumBits); + prevMove = &mMoveVec[index]; + mMoveVec[index].id = start++; + index ++; + } + + mLastMoveAck += count; +} + +void StdMoveList::serverWriteMovePacket(BitStream * bstream) +{ +#ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("ack %i minus %i",mLastMoveAck,mMoveVec.size()); +#endif + + // acknowledge only those moves that have been ticked + bstream->writeInt(mLastMoveAck - mMoveVec.size(),32); +} + +void StdMoveList::clientReadMovePacket(BitStream * bstream) +{ +#ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("pre move ack: %i", mLastMoveAck); +#endif + + mLastMoveAck = bstream->readInt(32); + +#ifdef TORQUE_DEBUG_NET_MOVES + Con::printf("post move ack %i, first move %i, last move %i", mLastMoveAck, mFirstMoveIndex, mLastClientMove); +#endif + + if (mLastMoveAck < mFirstMoveIndex) + mLastMoveAck = mFirstMoveIndex; + + if(mLastMoveAck > mLastClientMove) + mLastClientMove = mLastMoveAck; + while(mFirstMoveIndex < mLastMoveAck) + { + if (mMoveVec.size()) + { + mMoveVec.pop_front(); + mFirstMoveIndex++; + } + else + { + AssertWarn(1, "Popping off too many moves!"); + mFirstMoveIndex = mLastMoveAck; + } + } +} diff --git a/Engine/source/T3D/gameBase/std/stdMoveList.h b/Engine/source/T3D/gameBase/std/stdMoveList.h new file mode 100644 index 000000000..b07c5664d --- /dev/null +++ b/Engine/source/T3D/gameBase/std/stdMoveList.h @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MOVELIST_STD_H_ +#define _MOVELIST_STD_H_ + +#ifndef _MOVELIST_H_ +#include "T3D/gameBase/moveList.h" +#endif + +class StdMoveList : public MoveList +{ + typedef MoveList Parent; + +public: + + StdMoveList(); + + void clientWriteMovePacket(BitStream *); + void clientReadMovePacket(BitStream *); + + void serverWriteMovePacket(BitStream *); + void serverReadMovePacket(BitStream *); + + U32 getMoves(Move**,U32* numMoves); + void clearMoves(U32 count); + + void advanceMove(); + void onAdvanceObjects() {} + +protected: + + U32 mMoveCredit; +}; + +#endif // _MOVELIST_STD_H_ diff --git a/Engine/source/T3D/gameBase/tickCache.cpp b/Engine/source/T3D/gameBase/tickCache.cpp new file mode 100644 index 000000000..bc05488b0 --- /dev/null +++ b/Engine/source/T3D/gameBase/tickCache.cpp @@ -0,0 +1,174 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/gameBase/gameBase.h" +#include "console/consoleTypes.h" +#include "console/consoleInternal.h" +#include "core/stream/bitStream.h" +#include "sim/netConnection.h" +#include "T3D/gameBase/gameConnection.h" +#include "math/mathIO.h" +#include "T3D/gameBase/moveManager.h" +#include "T3D/gameBase/gameProcess.h" + +struct TickCacheHead +{ + TickCacheEntry * oldest; + TickCacheEntry * newest; + TickCacheEntry * next; + U32 numEntry; +}; + +namespace +{ + FreeListChunker sgTickCacheHeadStore; + FreeListChunker sgTickCacheEntryStore; + FreeListChunker sgMoveStore; + + static TickCacheHead * allocHead() { return sgTickCacheHeadStore.alloc(); } + static void freeHead(TickCacheHead * head) { sgTickCacheHeadStore.free(head); } + + static TickCacheEntry * allocEntry() { return sgTickCacheEntryStore.alloc(); } + static void freeEntry(TickCacheEntry * entry) { sgTickCacheEntryStore.free(entry); } + + static Move * allocMove() { return sgMoveStore.alloc(); } + static void freeMove(Move * move) { sgMoveStore.free(move); } +} + +//---------------------------------------------------------------------------- + +TickCache::~TickCache() +{ + if (mTickCacheHead) + { + setCacheSize(0); + freeHead(mTickCacheHead); + mTickCacheHead = NULL; + } +} + +Move * TickCacheEntry::allocateMove() +{ + return allocMove(); +} + +TickCacheEntry * TickCache::addCacheEntry() +{ + // Add a new entry, creating head if needed + if (!mTickCacheHead) + { + mTickCacheHead = allocHead(); + mTickCacheHead->newest = mTickCacheHead->oldest = mTickCacheHead->next = NULL; + mTickCacheHead->numEntry = 0; + } + if (!mTickCacheHead->newest) + { + mTickCacheHead->newest = mTickCacheHead->oldest = allocEntry(); + } + else + { + mTickCacheHead->newest->next = allocEntry(); + mTickCacheHead->newest = mTickCacheHead->newest->next; + } + mTickCacheHead->newest->next = NULL; + mTickCacheHead->newest->move = NULL; + mTickCacheHead->numEntry++; + return mTickCacheHead->newest; +} + +void TickCache::setCacheSize(S32 len) +{ + // grow cache to len size, adding to newest side of the list + while (!mTickCacheHead || mTickCacheHead->numEntry < len) + addCacheEntry(); + // shrink tick cache down to given size, popping off oldest entries first + while (mTickCacheHead && mTickCacheHead->numEntry > len) + dropOldest(); +} + +void TickCache::dropOldest() +{ + AssertFatal(mTickCacheHead->oldest,"Popping off too many tick cache entries"); + TickCacheEntry * oldest = mTickCacheHead->oldest; + mTickCacheHead->oldest = oldest->next; + if (oldest->move) + freeMove(oldest->move); + freeEntry(oldest); + mTickCacheHead->numEntry--; + if (mTickCacheHead->numEntry < 2) + mTickCacheHead->newest = mTickCacheHead->oldest; +} + +void TickCache::dropNextOldest() +{ + AssertFatal(mTickCacheHead->oldest && mTickCacheHead->numEntry>1,"Popping off too many tick cache entries"); + TickCacheEntry * oldest = mTickCacheHead->oldest; + TickCacheEntry * nextoldest = mTickCacheHead->oldest->next; + oldest->next = nextoldest->next; + if (nextoldest->move) + freeMove(nextoldest->move); + freeEntry(nextoldest); + mTickCacheHead->numEntry--; + if (mTickCacheHead->numEntry==1) + mTickCacheHead->newest = mTickCacheHead->oldest; +} + +void TickCache::ageCache(S32 numToAge, S32 len) +{ + AssertFatal(mTickCacheHead,"No tick cache head"); + AssertFatal(mTickCacheHead->numEntry>=numToAge,"Too few entries!"); + AssertFatal(mTickCacheHead->numEntry>numToAge,"Too few entries!"); + + while (numToAge--) + dropOldest(); + while (mTickCacheHead->numEntry>len) + dropNextOldest(); + while (mTickCacheHead->numEntrynext = mTickCacheHead->oldest; + // if no head, that's ok, we'll just add entries as we go +} + +TickCacheEntry * TickCache::incCacheList(bool addIfNeeded) +{ + // continue iterating through cache, returning current entry + // we'll add new entries if need be + TickCacheEntry * ret = NULL; + if (mTickCacheHead && mTickCacheHead->next) + { + ret = mTickCacheHead->next; + mTickCacheHead->next = mTickCacheHead->next->next; + } + else if (addIfNeeded) + { + addCacheEntry(); + ret = mTickCacheHead->newest; + } + return ret; +} \ No newline at end of file diff --git a/Engine/source/T3D/gameBase/tickCache.h b/Engine/source/T3D/gameBase/tickCache.h new file mode 100644 index 000000000..8f945f227 --- /dev/null +++ b/Engine/source/T3D/gameBase/tickCache.h @@ -0,0 +1,69 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TICKCACHE_H_ +#define _TICKCACHE_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif + +struct Move; + +struct TickCacheEntry +{ + enum { MaxPacketSize=140 }; + + U8 packetData[MaxPacketSize]; + TickCacheEntry * next; + Move * move; + + // If you want to assign moves to tick cache for later playback, allocate them here + Move * allocateMove(); +}; + +struct TickCacheHead; + +class TickCache +{ +public: + TickCache(); + ~TickCache(); + + TickCacheEntry * addCacheEntry(); + void dropOldest(); + void dropNextOldest(); + void ageCache(S32 numToAge, S32 len); + void setCacheSize(S32 len); + void beginCacheList(); + TickCacheEntry * incCacheList(bool addIfNeeded=true); + +private: + TickCacheHead * mTickCacheHead; +}; + +inline TickCache::TickCache() +{ + mTickCacheHead = NULL; +} + +#endif // _TICKCACHE_H_ \ No newline at end of file diff --git a/Engine/source/T3D/gameFunctions.cpp b/Engine/source/T3D/gameFunctions.cpp new file mode 100644 index 000000000..c3b3967a3 --- /dev/null +++ b/Engine/source/T3D/gameFunctions.cpp @@ -0,0 +1,470 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/gameFunctions.h" +#include "T3D/gameBase/gameConnection.h" +#include "T3D/camera.h" +#include "T3D/sfx/sfx3DWorld.h" +#include "console/consoleTypes.h" +#include "gui/3d/guiTSControl.h" +#include "core/util/journal/process.h" +#include "materials/materialManager.h" +#include "math/mEase.h" +#include "core/module.h" +#include "console/engineAPI.h" + + +static void RegisterGameFunctions(); +static void Process3D(); + +MODULE_BEGIN( 3D ) + + MODULE_INIT_AFTER( Process ) + MODULE_INIT_AFTER( Scene ) + + MODULE_SHUTDOWN_BEFORE( Process ) + MODULE_SHUTDOWN_BEFORE( Sim ) + MODULE_SHUTDOWN_AFTER( Scene ) + + MODULE_INIT + { + Process::notify(Process3D, PROCESS_TIME_ORDER); + + GameConnection::smFovUpdate.notify(GameSetCameraFov); + + RegisterGameFunctions(); + } + + MODULE_SHUTDOWN + { + GameConnection::smFovUpdate.remove(GameSetCameraFov); + + Process::remove(Process3D); + } + +MODULE_END; + + +static S32 gEaseInOut = Ease::InOut; +static S32 gEaseIn = Ease::In; +static S32 gEaseOut = Ease::Out; + +static S32 gEaseLinear = Ease::Linear; +static S32 gEaseQuadratic= Ease::Quadratic; +static S32 gEaseCubic= Ease::Cubic; +static S32 gEaseQuartic = Ease::Quartic; +static S32 gEaseQuintic = Ease::Quintic; +static S32 gEaseSinusoidal= Ease::Sinusoidal; +static S32 gEaseExponential = Ease::Exponential; +static S32 gEaseCircular = Ease::Circular; +static S32 gEaseElastic = Ease::Elastic; +static S32 gEaseBack = Ease::Back; +static S32 gEaseBounce = Ease::Bounce; + + +extern void ShowInit(); + +//------------------------------------------------------------------------------ +/// Camera and FOV info +namespace { + + const U32 MaxZoomSpeed = 2000; ///< max number of ms to reach target FOV + + static F32 sConsoleCameraFov = 90.f; ///< updated to camera FOV each frame + static F32 sDefaultFov = 90.f; ///< normal FOV + static F32 sCameraFov = 90.f; ///< current camera FOV + static F32 sTargetFov = 90.f; ///< the desired FOV + static F32 sLastCameraUpdateTime = 0; ///< last time camera was updated + static S32 sZoomSpeed = 500; ///< ms per 90deg fov change + + /// A scale to apply to the normal visible distance + /// typically used for tuning performance. + static F32 sVisDistanceScale = 1.0f; + +} // namespace {} + +// query +static SimpleQueryList sgServerQueryList; +static U32 sgServerQueryIndex = 0; + +//SERVER FUNCTIONS ONLY +ConsoleFunctionGroupBegin( Containers, "Spatial query functions. Server side only!"); + +ConsoleFunction(containerFindFirst, const char*, 6, 6, "(int mask, Point3F point, float x, float y, float z)" + "@brief Find objects matching the bitmask type within a box centered at point, with extents x, y, z.\n\n" + "@returns The first object found, or an empty string if nothing was found. Thereafter, you can get more " + "results using containerFindNext()." + "@see containerFindNext\n" + "@ingroup Game") +{ + //find out what we're looking for + U32 typeMask = U32(dAtoi(argv[1])); + + //find the center of the container volume + Point3F origin(0.0f, 0.0f, 0.0f); + dSscanf(argv[2], "%g %g %g", &origin.x, &origin.y, &origin.z); + + //find the box dimensions + Point3F size(0.0f, 0.0f, 0.0f); + size.x = mFabs(dAtof(argv[3])); + size.y = mFabs(dAtof(argv[4])); + size.z = mFabs(dAtof(argv[5])); + + //build the container volume + Box3F queryBox; + queryBox.minExtents = origin; + queryBox.maxExtents = origin; + queryBox.minExtents -= size; + queryBox.maxExtents += size; + + //initialize the list, and do the query + sgServerQueryList.mList.clear(); + gServerContainer.findObjects(queryBox, typeMask, SimpleQueryList::insertionCallback, &sgServerQueryList); + + //return the first element + sgServerQueryIndex = 0; + char *buff = Con::getReturnBuffer(100); + if (sgServerQueryList.mList.size()) + dSprintf(buff, 100, "%d", sgServerQueryList.mList[sgServerQueryIndex++]->getId()); + else + buff[0] = '\0'; + + return buff; +} + +ConsoleFunction( containerFindNext, const char*, 1, 1, "()" + "@brief Get more results from a previous call to containerFindFirst().\n\n" + "@note You must call containerFindFirst() to begin the search.\n" + "@returns The next object found, or an empty string if nothing else was found.\n" + "@see containerFindFirst()\n" + "@ingroup Game") +{ + //return the next element + char *buff = Con::getReturnBuffer(100); + if (sgServerQueryIndex < sgServerQueryList.mList.size()) + dSprintf(buff, 100, "%d", sgServerQueryList.mList[sgServerQueryIndex++]->getId()); + else + buff[0] = '\0'; + + return buff; +} + +ConsoleFunctionGroupEnd( Containers ); + +//------------------------------------------------------------------------------ + +bool GameGetCameraTransform(MatrixF *mat, Point3F *velocity) +{ + // Return the position and velocity of the control object + GameConnection* connection = GameConnection::getConnectionToServer(); + return connection && connection->getControlCameraTransform(0, mat) && + connection->getControlCameraVelocity(velocity); +} + +//------------------------------------------------------------------------------ +DefineEngineFunction( setDefaultFov, void, ( F32 defaultFOV ),, + "@brief Set the default FOV for a camera.\n" + "@param defaultFOV The default field of view in degrees\n" + "@ingroup CameraSystem") +{ + sDefaultFov = mClampF(defaultFOV, MinCameraFov, MaxCameraFov); + if(sCameraFov == sTargetFov) + sTargetFov = sDefaultFov; +} + +DefineEngineFunction( setZoomSpeed, void, ( S32 speed ),, + "@brief Set the zoom speed of the camera.\n" + "This affects how quickly the camera changes from one field of view " + "to another.\n" + "@param speed The camera's zoom speed in ms per 90deg FOV change\n" + "@ingroup CameraSystem") +{ + sZoomSpeed = mClamp(speed, 0, MaxZoomSpeed); +} + +DefineEngineFunction( setFov, void, ( F32 FOV ),, + "@brief Set the FOV of the camera.\n" + "@param FOV The camera's new FOV in degrees\n" + "@ingroup CameraSystem") +{ + sTargetFov = mClampF(FOV, MinCameraFov, MaxCameraFov); +} + +F32 GameGetCameraFov() +{ + return(sCameraFov); +} + +void GameSetCameraFov(F32 fov) +{ + sTargetFov = sCameraFov = fov; +} + +void GameSetCameraTargetFov(F32 fov) +{ + sTargetFov = fov; +} + +void GameUpdateCameraFov() +{ + F32 time = F32(Platform::getVirtualMilliseconds()); + + // need to update fov? + if(sTargetFov != sCameraFov) + { + F32 delta = time - sLastCameraUpdateTime; + + // snap zoom? + if((sZoomSpeed == 0) || (delta <= 0.f)) + sCameraFov = sTargetFov; + else + { + // gZoomSpeed is time in ms to zoom 90deg + F32 step = 90.f * (delta / F32(sZoomSpeed)); + + if(sCameraFov > sTargetFov) + { + sCameraFov -= step; + if(sCameraFov < sTargetFov) + sCameraFov = sTargetFov; + } + else + { + sCameraFov += step; + if(sCameraFov > sTargetFov) + sCameraFov = sTargetFov; + } + } + } + + // the game connection controls the vertical and the horizontal + GameConnection * connection = GameConnection::getConnectionToServer(); + if(connection) + { + // check if fov is valid on control object + if(connection->isValidControlCameraFov(sCameraFov)) + connection->setControlCameraFov(sCameraFov); + else + { + // will set to the closest fov (fails only on invalid control object) + if(connection->setControlCameraFov(sCameraFov)) + { + F32 setFov = sCameraFov; + connection->getControlCameraFov(&setFov); + sTargetFov = sCameraFov = setFov; + } + } + } + + // update the console variable + sConsoleCameraFov = sCameraFov; + sLastCameraUpdateTime = time; +} +//-------------------------------------------------------------------------- + +#ifdef TORQUE_DEBUG +// ConsoleFunction(dumpTSShapes, void, 1, 1, "dumpTSShapes();") +// { +// argc, argv; + +// FindMatch match("*.dts", 4096); +// gResourceManager->findMatches(&match); + +// for (U32 i = 0; i < match.numMatches(); i++) +// { +// U32 j; +// Resource shape = ResourceManager::get().load(match.matchList[i]); +// if (bool(shape) == false) +// Con::errorf(" aaa Couldn't load: %s", match.matchList[i]); + +// U32 numMeshes = 0, numSkins = 0; +// for (j = 0; j < shape->meshes.size(); j++) +// if (shape->meshes[j]) +// numMeshes++; +// for (j = 0; j < shape->skins.size(); j++) +// if (shape->skins[j]) +// numSkins++; + +// Con::printf(" aaa Shape: %s (%d meshes, %d skins)", match.matchList[i], numMeshes, numSkins); +// Con::printf(" aaa Meshes"); +// for (j = 0; j < shape->meshes.size(); j++) +// { +// if (shape->meshes[j]) +// Con::printf(" aaa %d -> nf: %d, nmf: %d, nvpf: %d (%d, %d, %d, %d, %d)", +// shape->meshes[j]->meshType & TSMesh::TypeMask, +// shape->meshes[j]->numFrames, +// shape->meshes[j]->numMatFrames, +// shape->meshes[j]->vertsPerFrame, +// shape->meshes[j]->verts.size(), +// shape->meshes[j]->norms.size(), +// shape->meshes[j]->tverts.size(), +// shape->meshes[j]->primitives.size(), +// shape->meshes[j]->indices.size()); +// } +// Con::printf(" aaa Skins"); +// for (j = 0; j < shape->skins.size(); j++) +// { +// if (shape->skins[j]) +// Con::printf(" aaa %d -> nf: %d, nmf: %d, nvpf: %d (%d, %d, %d, %d, %d)", +// shape->skins[j]->meshType & TSMesh::TypeMask, +// shape->skins[j]->numFrames, +// shape->skins[j]->numMatFrames, +// shape->skins[j]->vertsPerFrame, +// shape->skins[j]->verts.size(), +// shape->skins[j]->norms.size(), +// shape->skins[j]->tverts.size(), +// shape->skins[j]->primitives.size(), +// shape->skins[j]->indices.size()); +// } +// } +// } +#endif + +bool GameProcessCameraQuery(CameraQuery *query) +{ + GameConnection* connection = GameConnection::getConnectionToServer(); + + if (connection && connection->getControlCameraTransform(0.032f, &query->cameraMatrix)) + { + query->object = dynamic_cast(connection->getControlObject()); + query->nearPlane = gClientSceneGraph->getNearClip(); + + // Scale the normal visible distance by the performance + // tuning scale which we never let over 1. + sVisDistanceScale = mClampF( sVisDistanceScale, 0.01f, 1.0f ); + query->farPlane = gClientSceneGraph->getVisibleDistance() * sVisDistanceScale; + + F32 cameraFov; + if(!connection->getControlCameraFov(&cameraFov)) + return false; + + query->fov = mDegToRad(cameraFov); + return true; + } + return false; +} + +void GameRenderWorld() +{ + PROFILE_START(GameRenderWorld); + FrameAllocator::setWaterMark(0); + + gClientSceneGraph->renderScene( SPT_Diffuse ); + + // renderScene leaves some states dirty, which causes problems if GameTSCtrl is the last Gui object rendered + GFX->updateStates(); + + AssertFatal(FrameAllocator::getWaterMark() == 0, + "Error, someone didn't reset the water mark on the frame allocator!"); + FrameAllocator::setWaterMark(0); + PROFILE_END(); +} + + +static void Process3D() +{ + MATMGR->updateTime(); + + // Update the SFX world, if there is one. + + if( gSFX3DWorld ) + gSFX3DWorld->update(); +} + +static void RegisterGameFunctions() +{ + Con::addVariable( "$pref::Camera::distanceScale", TypeF32, &sVisDistanceScale, + "A scale to apply to the normal visible distance, typically used for tuning performance.\n" + "@ingroup Game"); + Con::addVariable( "$cameraFov", TypeF32, &sConsoleCameraFov, + "The camera's Field of View.\n\n" + "@ingroup Game" ); + + // Stuff game types into the console + Con::setIntVariable("$TypeMasks::StaticObjectType", StaticObjectType); + Con::setIntVariable("$TypeMasks::EnvironmentObjectType", EnvironmentObjectType); + Con::setIntVariable("$TypeMasks::TerrainObjectType", TerrainObjectType); + Con::setIntVariable("$TypeMasks::InteriorObjectType", InteriorObjectType); + Con::setIntVariable("$TypeMasks::WaterObjectType", WaterObjectType); + Con::setIntVariable("$TypeMasks::TriggerObjectType", TriggerObjectType); + Con::setIntVariable("$TypeMasks::MarkerObjectType", MarkerObjectType); + Con::setIntVariable("$TypeMasks::GameBaseObjectType", GameBaseObjectType); + Con::setIntVariable("$TypeMasks::ShapeBaseObjectType", ShapeBaseObjectType); + Con::setIntVariable("$TypeMasks::CameraObjectType", CameraObjectType); + Con::setIntVariable("$TypeMasks::StaticShapeObjectType", StaticShapeObjectType); + Con::setIntVariable("$TypeMasks::DynamicShapeObjectType", DynamicShapeObjectType); + Con::setIntVariable("$TypeMasks::PlayerObjectType", PlayerObjectType); + Con::setIntVariable("$TypeMasks::ItemObjectType", ItemObjectType); + Con::setIntVariable("$TypeMasks::VehicleObjectType", VehicleObjectType); + Con::setIntVariable("$TypeMasks::VehicleBlockerObjectType", VehicleBlockerObjectType); + Con::setIntVariable("$TypeMasks::ProjectileObjectType", ProjectileObjectType); + Con::setIntVariable("$TypeMasks::ExplosionObjectType", ExplosionObjectType); + Con::setIntVariable("$TypeMasks::CorpseObjectType", CorpseObjectType); + Con::setIntVariable("$TypeMasks::DebrisObjectType", DebrisObjectType); + Con::setIntVariable("$TypeMasks::PhysicalZoneObjectType", PhysicalZoneObjectType); + Con::setIntVariable("$TypeMasks::LightObjectType", LightObjectType); + + Con::addVariable("Ease::InOut", TypeS32, &gEaseInOut, + "InOut ease for curve movement.\n" + "@ingroup Game"); + Con::addVariable("Ease::In", TypeS32, &gEaseIn, + "In ease for curve movement.\n" + "@ingroup Game"); + Con::addVariable("Ease::Out", TypeS32, &gEaseOut, + "Out ease for curve movement.\n" + "@ingroup Game"); + + Con::addVariable("Ease::Linear", TypeS32, &gEaseLinear, + "Linear ease for curve movement.\n" + "@ingroup Game"); + Con::addVariable("Ease::Quadratic", TypeS32, &gEaseQuadratic, + "Quadratic ease for curve movement.\n" + "@ingroup Game"); + Con::addVariable("Ease::Cubic", TypeS32, &gEaseCubic, + "Cubic ease for curve movement.\n" + "@ingroup Game"); + Con::addVariable("Ease::Quartic", TypeS32, &gEaseQuartic, + "Quartic ease for curve movement.\n" + "@ingroup Game"); + Con::addVariable("Ease::Quintic", TypeS32, &gEaseQuintic, + "Quintic ease for curve movement.\n" + "@ingroup Game"); + Con::addVariable("Ease::Sinusoidal", TypeS32, &gEaseSinusoidal, + "Sinusoidal ease for curve movement.\n" + "@ingroup Game"); + Con::addVariable("Ease::Exponential", TypeS32, &gEaseExponential, + "Exponential ease for curve movement.\n" + "@ingroup Game"); + Con::addVariable("Ease::Circular", TypeS32, &gEaseCircular, + "Circular ease for curve movement.\n" + "@ingroup Game"); + Con::addVariable("Ease::Elastic", TypeS32, &gEaseElastic, + "Elastic ease for curve movement.\n" + "@ingroup Game"); + Con::addVariable("Ease::Back", TypeS32, &gEaseBack, + "Backwards ease for curve movement.\n" + "@ingroup Game"); + Con::addVariable("Ease::Bounce", TypeS32, &gEaseBounce, + "Bounce ease for curve movement.\n" + "@ingroup Game"); +} diff --git a/Engine/source/T3D/gameFunctions.h b/Engine/source/T3D/gameFunctions.h new file mode 100644 index 000000000..6eea00b55 --- /dev/null +++ b/Engine/source/T3D/gameFunctions.h @@ -0,0 +1,64 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GAMEFUNCTIONS_H_ +#define _GAMEFUNCTIONS_H_ + +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif + +struct CameraQuery; + + +/// Actually renders the world. This is the function that will render the +/// scene ONLY - new guis, no damage flashes. +void GameRenderWorld(); + +/// Renders overlays such as damage flashes, white outs, and water masks. +/// These are usually a color applied over the entire screen. +void GameRenderFilters(const CameraQuery& camq); + +/// Does the same thing as GameGetCameraTransform, but fills in other data +/// including information about the far and near clipping planes. +bool GameProcessCameraQuery(CameraQuery *query); + +/// Gets the position, rotation, and velocity of the camera. +bool GameGetCameraTransform(MatrixF *mat, Point3F *velocity); + +/// Gets the camera field of view angle. +F32 GameGetCameraFov(); + +/// Sets the field of view angle of the camera. +void GameSetCameraFov(F32 fov); + +/// Sets where the camera fov will be change to. This is for +/// non-instantaneous zooms/retractions. +void GameSetCameraTargetFov(F32 fov); + +/// Update the camera fov to be closer to the target fov. +void GameUpdateCameraFov(); + +#endif // _GAMEFUNCTIONS_H_ diff --git a/Engine/source/T3D/gameTSCtrl.cpp b/Engine/source/T3D/gameTSCtrl.cpp new file mode 100644 index 000000000..dc0590c59 --- /dev/null +++ b/Engine/source/T3D/gameTSCtrl.cpp @@ -0,0 +1,209 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "T3D/gameTSCtrl.h" +#include "console/consoleTypes.h" +#include "T3D/gameBase/gameBase.h" +#include "T3D/gameBase/gameConnection.h" +#include "T3D/shapeBase.h" +#include "T3D/gameFunctions.h" +#include "console/engineAPI.h" + +//--------------------------------------------------------------------------- +// Debug stuff: +Point3F lineTestStart = Point3F(0, 0, 0); +Point3F lineTestEnd = Point3F(0, 1000, 0); +Point3F lineTestIntersect = Point3F(0, 0, 0); +bool gSnapLine = false; + +//---------------------------------------------------------------------------- +// Class: GameTSCtrl +//---------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(GameTSCtrl); + +// See Torque manual (.CHM) for more information +ConsoleDocClass( GameTSCtrl, + "@brief The main 3D viewport for a Torque 3D game.\n\n" + "@ingroup Gui3D\n"); + +GameTSCtrl::GameTSCtrl() +{ +} + +//--------------------------------------------------------------------------- +bool GameTSCtrl::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + +#ifdef TORQUE_DEMO_WATERMARK + mWatermark.init(); +#endif + + return true; +} + +//--------------------------------------------------------------------------- +bool GameTSCtrl::processCameraQuery(CameraQuery *camq) +{ + GameUpdateCameraFov(); + return GameProcessCameraQuery(camq); +} + +//--------------------------------------------------------------------------- +void GameTSCtrl::renderWorld(const RectI &updateRect) +{ + GameRenderWorld(); +} + +//--------------------------------------------------------------------------- +void GameTSCtrl::makeScriptCall(const char *func, const GuiEvent &evt) const +{ + // write screen position + char *sp = Con::getArgBuffer(32); + dSprintf(sp, 32, "%d %d", evt.mousePoint.x, evt.mousePoint.y); + + // write world position + char *wp = Con::getArgBuffer(32); + Point3F camPos; + mLastCameraQuery.cameraMatrix.getColumn(3, &camPos); + dSprintf(wp, 32, "%g %g %g", camPos.x, camPos.y, camPos.z); + + // write click vector + char *vec = Con::getArgBuffer(32); + Point3F fp(evt.mousePoint.x, evt.mousePoint.y, 1.0); + Point3F ray; + unproject(fp, &ray); + ray -= camPos; + ray.normalizeSafe(); + dSprintf(vec, 32, "%g %g %g", ray.x, ray.y, ray.z); + + Con::executef( (SimObject*)this, func, sp, wp, vec ); +} + +void GameTSCtrl::onMouseDown(const GuiEvent &evt) +{ + Parent::onMouseDown(evt); + if( isMethod( "onMouseDown" ) ) + makeScriptCall( "onMouseDown", evt ); +} + +void GameTSCtrl::onRightMouseDown(const GuiEvent &evt) +{ + Parent::onRightMouseDown(evt); + if( isMethod( "onRightMouseDown" ) ) + makeScriptCall( "onRightMouseDown", evt ); +} + +void GameTSCtrl::onMiddleMouseDown(const GuiEvent &evt) +{ + Parent::onMiddleMouseDown(evt); + if( isMethod( "onMiddleMouseDown" ) ) + makeScriptCall( "onMiddleMouseDown", evt ); +} + +void GameTSCtrl::onMouseUp(const GuiEvent &evt) +{ + Parent::onMouseUp(evt); + if( isMethod( "onMouseUp" ) ) + makeScriptCall( "onMouseUp", evt ); +} + +void GameTSCtrl::onRightMouseUp(const GuiEvent &evt) +{ + Parent::onRightMouseUp(evt); + if( isMethod( "onRightMouseUp" ) ) + makeScriptCall( "onRightMouseUp", evt ); +} + +void GameTSCtrl::onMiddleMouseUp(const GuiEvent &evt) +{ + Parent::onMiddleMouseUp(evt); + if( isMethod( "onMiddleMouseUp" ) ) + makeScriptCall( "onMiddleMouseUp", evt ); +} + +void GameTSCtrl::onMouseMove(const GuiEvent &evt) +{ + if(gSnapLine) + return; + + MatrixF mat; + Point3F vel; + if ( GameGetCameraTransform(&mat, &vel) ) + { + Point3F pos; + mat.getColumn(3,&pos); + Point3F screenPoint((F32)evt.mousePoint.x, (F32)evt.mousePoint.y, -1.0f); + Point3F worldPoint; + if (unproject(screenPoint, &worldPoint)) { + Point3F vec = worldPoint - pos; + lineTestStart = pos; + vec.normalizeSafe(); + lineTestEnd = pos + vec * 1000; + } + } +} + +void GameTSCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + // check if should bother with a render + GameConnection * con = GameConnection::getConnectionToServer(); + bool skipRender = !con || (con->getWhiteOut() >= 1.f) || (con->getDamageFlash() >= 1.f) || (con->getBlackOut() >= 1.f); + + if(!skipRender || true) + Parent::onRender(offset, updateRect); + +#ifdef TORQUE_DEMO_WATERMARK + mWatermark.render(getExtent()); +#endif +} + +//-------------------------------------------------------------------------- + +DefineEngineFunction(snapToggle, void, (),, + "@brief Prevents mouse movement from being processed\n\n" + + "In the source, whenever a mouse move event occurs " + "GameTSCtrl::onMouseMove() is called. Whenever snapToggle() " + "is called, it will flag a variable that can prevent this " + "from happening: gSnapLine. This variable is not exposed to " + "script, so you need to call this function to trigger it.\n\n" + + "@tsexample\n" + "// Snapping is off by default, so we will toggle\n" + "// it on first:\n" + "PlayGui.snapToggle();\n\n" + "// Mouse movement should be disabled\n" + "// Let's turn it back on\n" + "PlayGui.snapToggle();\n" + "@endtsexample\n\n" + + "@ingroup GuiGame") +{ + gSnapLine = !gSnapLine; +} +// +//ConsoleFunction( snapToggle, void, 1, 1, "()" ) +//{ +// gSnapLine = !gSnapLine; +//} diff --git a/Engine/source/T3D/gameTSCtrl.h b/Engine/source/T3D/gameTSCtrl.h new file mode 100644 index 000000000..21b8d9ca2 --- /dev/null +++ b/Engine/source/T3D/gameTSCtrl.h @@ -0,0 +1,78 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GAMETSCTRL_H_ +#define _GAMETSCTRL_H_ + +#ifndef _GAME_H_ +#include "app/game.h" +#endif +#ifndef _GUITSCONTROL_H_ +#include "gui/3d/guiTSControl.h" +#endif + +#ifdef TORQUE_DEMO_WATERMARK +#ifndef _WATERMARK_H_ +#include "demo/watermark/watermark.h" +#endif +#endif + +class ProjectileData; +class GameBase; + +//---------------------------------------------------------------------------- +class GameTSCtrl : public GuiTSCtrl +{ +private: + typedef GuiTSCtrl Parent; + +#ifdef TORQUE_DEMO_WATERMARK + Watermark mWatermark; +#endif + + void makeScriptCall(const char *func, const GuiEvent &evt) const; + +public: + GameTSCtrl(); + + DECLARE_CONOBJECT(GameTSCtrl); + DECLARE_DESCRIPTION( "A control that renders a 3D view from the current control object." ); + + bool processCameraQuery(CameraQuery *query); + void renderWorld(const RectI &updateRect); + + // GuiControl + virtual void onMouseDown(const GuiEvent &evt); + virtual void onRightMouseDown(const GuiEvent &evt); + virtual void onMiddleMouseDown(const GuiEvent &evt); + + virtual void onMouseUp(const GuiEvent &evt); + virtual void onRightMouseUp(const GuiEvent &evt); + virtual void onMiddleMouseUp(const GuiEvent &evt); + + void onMouseMove(const GuiEvent &evt); + void onRender(Point2I offset, const RectI &updateRect); + + virtual bool onAdd(); +}; + +#endif diff --git a/Engine/source/T3D/groundPlane.cpp b/Engine/source/T3D/groundPlane.cpp new file mode 100644 index 000000000..a923502cf --- /dev/null +++ b/Engine/source/T3D/groundPlane.cpp @@ -0,0 +1,556 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/groundPlane.h" + +#include "renderInstance/renderPassManager.h" +#include "scene/sceneRenderState.h" +#include "materials/sceneData.h" +#include "materials/materialDefinition.h" +#include "materials/materialManager.h" +#include "materials/baseMatInstance.h" +#include "math/util/frustum.h" +#include "math/mPlane.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "core/stream/bitStream.h" +#include "collision/boxConvex.h" +#include "collision/abstractPolyList.h" +#include "T3D/physics/physicsPlugin.h" +#include "T3D/physics/physicsBody.h" +#include "T3D/physics/physicsCollision.h" + + +/// Minimum square size allowed. This is a cheap way to limit the amount +/// of geometry possibly generated by the GroundPlane (vertex buffers have a +/// limit, too). Dynamically clipping extents into range is a problem since the +/// location of the horizon depends on the camera orientation. Just shifting +/// squareSize as needed also doesn't work as that causes different geometry to +/// be generated depending on the viewpoint and orientation which affects the +/// texturing. +static const F32 sMIN_SQUARE_SIZE = 16; + + +IMPLEMENT_CO_NETOBJECT_V1( GroundPlane ); + +ConsoleDocClass( GroundPlane, + "@brief An infinite plane extending in all direction.\n\n" + + "%GroundPlane is useful for setting up simple testing scenes, or it can be " + "placed under an existing scene to keep objects from falling into 'nothing'.\n\n" + + "%GroundPlane may not be moved or rotated, it is always at the world origin.\n\n" + + "@ingroup Terrain" +); + +GroundPlane::GroundPlane() + : mSquareSize( 128.0f ), + mScaleU( 1.0f ), + mScaleV( 1.0f ), + mMaterial( NULL ), + mMin( 0.0f, 0.0f ), + mMax( 0.0f, 0.0f ), + mPhysicsRep( NULL ) +{ + mTypeMask |= StaticObjectType | StaticShapeObjectType; + mNetFlags.set( Ghostable | ScopeAlways ); + + mConvexList = new Convex; +} + +GroundPlane::~GroundPlane() +{ + if( mMaterial ) + SAFE_DELETE( mMaterial ); + + mConvexList->nukeList(); + SAFE_DELETE( mConvexList ); +} + +void GroundPlane::initPersistFields() +{ + addGroup( "Plane" ); + + addField( "squareSize", TypeF32, Offset( mSquareSize, GroundPlane ), "Square size in meters to which %GroundPlane subdivides its geometry." ); + addField( "scaleU", TypeF32, Offset( mScaleU, GroundPlane ), "Scale of texture repeat in the U direction." ); + addField( "scaleV", TypeF32, Offset( mScaleV, GroundPlane ), "Scale of texture repeat in the V direction." ); + addField( "material", TypeMaterialName, Offset( mMaterialName, GroundPlane ), "Name of Material used to render %GroundPlane's surface." ); + + endGroup( "Plane" ); + + Parent::initPersistFields(); + + removeField( "scale" ); + removeField( "position" ); + removeField( "rotation" ); +} + +bool GroundPlane::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + if( isClientObject() ) + _updateMaterial(); + + if( mSquareSize < sMIN_SQUARE_SIZE ) + { + Con::errorf( "GroundPlane - squareSize below threshold; re-setting to %.02f", sMIN_SQUARE_SIZE ); + mSquareSize = sMIN_SQUARE_SIZE; + } + + Parent::setScale( VectorF( 1.0f, 1.0f, 1.0f ) ); + Parent::setTransform( MatrixF::Identity ); + setGlobalBounds(); + resetWorldBox(); + + addToScene(); + + if ( PHYSICSMGR ) + { + PhysicsCollision *colShape = PHYSICSMGR->createCollision(); + colShape->addPlane( PlaneF( Point3F::Zero, Point3F( 0, 0, 1 ) ) ); + + PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" ); + mPhysicsRep = PHYSICSMGR->createBody(); + mPhysicsRep->init( colShape, 0, 0, this, world ); + } + + return true; +} + +void GroundPlane::onRemove() +{ + SAFE_DELETE( mPhysicsRep ); + + removeFromScene(); + Parent::onRemove(); +} + +void GroundPlane::inspectPostApply() +{ + Parent::inspectPostApply(); + setMaskBits( U32( -1 ) ); + + if( mSquareSize < sMIN_SQUARE_SIZE ) + { + Con::errorf( "GroundPlane - squareSize below threshold; re-setting to %.02f", sMIN_SQUARE_SIZE ); + mSquareSize = sMIN_SQUARE_SIZE; + } + + setScale( VectorF( 1.0f, 1.0f, 1.0f ) ); +} + +void GroundPlane::setTransform( const MatrixF &mat ) +{ + // Ignore. +} + +void GroundPlane::setScale( const Point3F& scale ) +{ + // Ignore. +} + +U32 GroundPlane::packUpdate( NetConnection* connection, U32 mask, BitStream* stream ) +{ + U32 retMask = Parent::packUpdate( connection, mask, stream ); + + stream->write( mSquareSize ); + stream->write( mScaleU ); + stream->write( mScaleV ); + stream->write( mMaterialName ); + + return retMask; +} + +void GroundPlane::unpackUpdate( NetConnection* connection, BitStream* stream ) +{ + Parent::unpackUpdate( connection, stream ); + + stream->read( &mSquareSize ); + stream->read( &mScaleU ); + stream->read( &mScaleV ); + stream->read( &mMaterialName ); + + // If we're added then something possibly changed in + // the editor... do an update of the material and the + // geometry. + if ( isProperlyAdded() ) + { + _updateMaterial(); + mVertexBuffer = NULL; + } +} + +void GroundPlane::_updateMaterial() +{ + if( mMaterialName.isEmpty() ) + { + Con::warnf( "GroundPlane::_updateMaterial - no material set; defaulting to 'WarningMaterial'" ); + mMaterialName = "WarningMaterial"; + } + + // If the material name matches then don't + // bother updating it. + if ( mMaterial && + mMaterialName.compare( mMaterial->getMaterial()->getName() ) == 0 ) + return; + + SAFE_DELETE( mMaterial ); + + mMaterial = MATMGR->createMatInstance( mMaterialName, getGFXVertexFormat< VertexType >() ); + if ( !mMaterial ) + Con::errorf( "GroundPlane::_updateMaterial - no material called '%s'", mMaterialName.c_str() ); +} + +bool GroundPlane::castRay( const Point3F& start, const Point3F& end, RayInfo* info ) +{ + PlaneF plane( Point3F( 0.0f, 0.0f, 0.0f ), Point3F( 0.0f, 0.0f, 1.0f ) ); + + F32 t = plane.intersect( start, end ); + if( t >= 0.0 && t <= 1.0 ) + { + info->t = t; + info->setContactPoint( start, end ); + info->normal.set( 0, 0, 1 ); + info->material = mMaterial; + info->object = this; + info->distance = 0; + info->faceDot = 0; + info->texCoord.set( 0, 0 ); + return true; + } + + return false; +} + +void GroundPlane::buildConvex( const Box3F& box, Convex* convex ) +{ + mConvexList->collectGarbage(); + + Box3F planeBox = getPlaneBox(); + if ( !box.isOverlapped( planeBox ) ) + return; + + // See if we already have a convex in the working set. + BoxConvex *boxConvex = NULL; + CollisionWorkingList &wl = convex->getWorkingList(); + CollisionWorkingList *itr = wl.wLink.mNext; + for ( ; itr != &wl; itr = itr->wLink.mNext ) + { + if ( itr->mConvex->getType() == BoxConvexType && + itr->mConvex->getObject() == this ) + { + boxConvex = (BoxConvex*)itr->mConvex; + break; + } + } + + if ( !boxConvex ) + { + boxConvex = new BoxConvex; + mConvexList->registerObject( boxConvex ); + boxConvex->init( this ); + + convex->addToWorkingList( boxConvex ); + } + + // Update our convex to best match the queried box + if ( boxConvex ) + { + Point3F queryCenter = box.getCenter(); + + boxConvex->mCenter = Point3F( queryCenter.x, queryCenter.y, -GROUND_PLANE_BOX_HEIGHT_HALF ); + boxConvex->mSize = Point3F( box.getExtents().x, + box.getExtents().y, + GROUND_PLANE_BOX_HEIGHT_HALF ); + } +} + +bool GroundPlane::buildPolyList( PolyListContext context, AbstractPolyList* polyList, const Box3F&, const SphereF& ) +{ + polyList->setObject( this ); + polyList->setTransform( &MatrixF::Identity, Point3F( 1.0f, 1.0f, 1.0f ) ); + + Box3F planeBox = getPlaneBox(); + polyList->addBox( planeBox, mMaterial ); + + return true; +} + +void GroundPlane::prepRenderImage( SceneRenderState* state ) +{ + PROFILE_SCOPE( GroundPlane_prepRenderImage ); + + // TODO: Should we skip rendering the ground plane into + // the shadows? Its not like you can ever get under it. + + if ( !mMaterial ) + return; + + // If we don't have a material instance after the override then + // we can skip rendering all together. + BaseMatInstance *matInst = state->getOverrideMaterial( mMaterial ); + if ( !matInst ) + return; + + PROFILE_SCOPE( GroundPlane_prepRender ); + + // Update the geometry. + createGeometry( state->getFrustum() ); + if( mVertexBuffer.isNull() ) + return; + + // Add a render instance. + + RenderPassManager* pass = state->getRenderPass(); + MeshRenderInst* ri = pass->allocInst< MeshRenderInst >(); + + ri->type = RenderPassManager::RIT_Mesh; + ri->vertBuff = &mVertexBuffer; + ri->primBuff = &mPrimitiveBuffer; + ri->prim = &mPrimitive; + ri->matInst = matInst; + ri->objectToWorld = pass->allocUniqueXform( MatrixF::Identity ); + ri->worldToCamera = pass->allocSharedXform( RenderPassManager::View ); + ri->projection = pass->allocSharedXform( RenderPassManager::Projection ); + ri->visibility = 1.0f; + ri->translucentSort = matInst->getMaterial()->isTranslucent(); + ri->defaultKey = matInst->getStateHint(); + + if( ri->translucentSort ) + ri->type = RenderPassManager::RIT_Translucent; + + // If we need lights then set them up. + if ( matInst->isForwardLit() ) + { + LightQuery query; + query.init( getWorldSphere() ); + query.getLights( ri->lights, 8 ); + } + + pass->addInst( ri ); +} + +/// Generate a subset of the ground plane matching the given frustum. + +void GroundPlane::createGeometry( const Frustum& frustum ) +{ + PROFILE_SCOPE( GroundPlane_createGeometry ); + + enum { MAX_WIDTH = 256, MAX_HEIGHT = 256 }; + + // Project the frustum onto the XY grid. + + Point2F min; + Point2F max; + + projectFrustum( frustum, mSquareSize, min, max ); + + // Early out if the grid projection hasn't changed. + + if( mVertexBuffer.isValid() && + min == mMin && + max == mMax ) + return; + + mMin = min; + mMax = max; + + // Determine the grid extents and allocate the buffers. + // Adjust square size permanently if with the given frustum, + // we end up producing more than a certain limit of geometry. + // This is to prevent this code from causing trouble with + // long viewing distances. + // This only affects the client object, of course, and thus + // has no permanent effect. + + U32 width = mCeil( ( max.x - min.x ) / mSquareSize ); + if( width > MAX_WIDTH ) + { + mSquareSize = mCeil( ( max.x - min.x ) / MAX_WIDTH ); + width = MAX_WIDTH; + } + else if( !width ) + width = 1; + + U32 height = mCeil( ( max.y - min.y ) / mSquareSize ); + if( height > MAX_HEIGHT ) + { + mSquareSize = mCeil( ( max.y - min.y ) / MAX_HEIGHT ); + height = MAX_HEIGHT; + } + else if( !height ) + height = 1; + + const U32 numVertices = ( width + 1 ) * ( height + 1 ); + const U32 numTriangles = width * height * 2; + + // Only reallocate if the buffers are too small. + if ( mVertexBuffer.isNull() || numVertices > mVertexBuffer->mNumVerts ) + { +#if defined(TORQUE_OS_XENON) + mVertexBuffer.set( GFX, numVertices, GFXBufferTypeVolatile ); +#else + mVertexBuffer.set( GFX, numVertices, GFXBufferTypeDynamic ); +#endif + } + if ( mPrimitiveBuffer.isNull() || numTriangles > mPrimitiveBuffer->mPrimitiveCount ) + { +#if defined(TORQUE_OS_XENON) + mPrimitiveBuffer.set( GFX, numTriangles*3, numTriangles, GFXBufferTypeVolatile ); +#else + mPrimitiveBuffer.set( GFX, numTriangles*3, numTriangles, GFXBufferTypeDynamic ); +#endif + } + + // Generate the grid. + + generateGrid( width, height, mSquareSize, min, max, mVertexBuffer, mPrimitiveBuffer ); + + // Set up GFX primitive. + + mPrimitive.type = GFXTriangleList; + mPrimitive.numPrimitives = numTriangles; + mPrimitive.numVertices = numVertices; +} + +/// Project the given frustum onto the ground plane and return the XY bounds in world space. + +void GroundPlane::projectFrustum( const Frustum& frustum, F32 squareSize, Point2F& outMin, Point2F& outMax ) +{ + // Get the frustum's min and max XY coordinates. + + const Box3F bounds = frustum.getBounds(); + + Point2F minPt( bounds.minExtents.x, bounds.minExtents.y ); + Point2F maxPt( bounds.maxExtents.x, bounds.maxExtents.y ); + + // Round the min and max coordinates so they align on the grid. + + minPt.x -= mFmod( minPt.x, squareSize ); + minPt.y -= mFmod( minPt.y, squareSize ); + + F32 maxDeltaX = mFmod( maxPt.x, squareSize ); + F32 maxDeltaY = mFmod( maxPt.y, squareSize ); + + if( maxDeltaX != 0.0f ) + maxPt.x += ( squareSize - maxDeltaX ); + if( maxDeltaY != 0.0f ) + maxPt.y += ( squareSize - maxDeltaY ); + + // Add a safezone, so we don't touch the clipping planes. + + minPt.x -= squareSize; minPt.y -= squareSize; + maxPt.x += squareSize; maxPt.y += squareSize; + + outMin = minPt; + outMax = maxPt; +} + +/// Generate a triangulated grid spanning the given bounds into the given buffers. + +void GroundPlane::generateGrid( U32 width, U32 height, F32 squareSize, + const Point2F& min, const Point2F& max, + GFXVertexBufferHandle< VertexType >& outVertices, + GFXPrimitiveBufferHandle& outPrimitives ) +{ + // Generate the vertices. + + VertexType* vertices = outVertices.lock(); + for( F32 y = min.y; y <= max.y; y += squareSize ) + for( F32 x = min.x; x <= max.x; x += squareSize ) + { + vertices->point.x = x; + vertices->point.y = y; + vertices->point.z = 0.0; + + vertices->texCoord.x = ( x / squareSize ) * mScaleU; + vertices->texCoord.y = ( y / squareSize ) * -mScaleV; + + vertices->normal.x = 0.0f; + vertices->normal.y = 0.0f; + vertices->normal.z = 1.0f; + + vertices->tangent.x = 1.0f; + vertices->tangent.y = 0.0f; + vertices->tangent.z = 0.0f; + + vertices->binormal.x = 0.0f; + vertices->binormal.y = 1.0f; + vertices->binormal.z = 0.0f; + + vertices++; + } + outVertices.unlock(); + + // Generate the indices. + + U16* indices; + outPrimitives.lock( &indices ); + + U16 corner1 = 0; + U16 corner2 = 1; + U16 corner3 = width + 1; + U16 corner4 = width + 2; + + for( U32 y = 0; y < height; ++ y ) + { + for( U32 x = 0; x < width; ++ x ) + { + indices[ 0 ] = corner3; + indices[ 1 ] = corner2; + indices[ 2 ] = corner1; + + indices += 3; + + indices[ 0 ] = corner3; + indices[ 1 ] = corner4; + indices[ 2 ] = corner2; + + indices += 3; + + corner1 ++; + corner2 ++; + corner3 ++; + corner4 ++; + } + + corner1 ++; + corner2 ++; + corner3 ++; + corner4 ++; + } + + outPrimitives.unlock(); +} + +DefineEngineMethod( GroundPlane, postApply, void, (),, + "Intended as a helper to developers and editor scripts.\n" + "Force trigger an inspectPostApply. This will transmit " + "material and other fields to client objects." + ) +{ + object->inspectPostApply(); +} \ No newline at end of file diff --git a/Engine/source/T3D/groundPlane.h b/Engine/source/T3D/groundPlane.h new file mode 100644 index 000000000..a634b4a84 --- /dev/null +++ b/Engine/source/T3D/groundPlane.h @@ -0,0 +1,138 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TORQUE_T3D_GROUNDPLANE_H_ +#define _TORQUE_T3D_GROUNDPLANE_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _GFXVERTEXBUFFER_H_ +#include "gfx/gfxVertexBuffer.h" +#endif +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif + +class PhysicsBody; +class BaseMatInstance; + + +/// A virtually infinite XY ground plane primitive. +/// +/// For rendering, a subset of the plane spanning the view frustum is generated +/// and rendered. Tesselation is determined by the given squareSize property. +/// +/// For collision detection, a finite bounding box is used to deal with finite +/// precision of floating-point operations (we can't use floating-point infinity +/// as infinity*0 is undefined.) +/// +/// The ground plane can be textured like regular geometry by assigning a material +/// name to its 'material' property. UVs mirror grid coordinates so that when +/// using UV wrapping, textures will tile nicely. + + +class GroundPlane : public SceneObject +{ +public: + + typedef SceneObject Parent; + + DECLARE_CONOBJECT( GroundPlane ); + + GroundPlane(); + virtual ~GroundPlane(); + + virtual bool onAdd(); + virtual void onRemove(); + virtual U32 packUpdate( NetConnection* connection, U32 mask, BitStream* stream ); + virtual void unpackUpdate( NetConnection* connection, BitStream* stream ); + virtual void prepRenderImage( SceneRenderState* state ); + virtual bool castRay( const Point3F& start, const Point3F& end, RayInfo* info ); + virtual void buildConvex( const Box3F& box, Convex* convex ); + virtual bool buildPolyList( PolyListContext context, AbstractPolyList* polyList, const Box3F& box, const SphereF& sphere ); + virtual void inspectPostApply(); + virtual void setTransform( const MatrixF &mat ); + virtual void setScale( const Point3F& scale ); + + static void initPersistFields(); + +protected: + + typedef GFXVertexPNTBT VertexType; + + void _updateMaterial(); + + void createGeometry( const Frustum& frustum ); + void projectFrustum( const Frustum& frustum, F32 squareSize, + Point2F& outMin, Point2F& outMax ); + void generateGrid( U32 width, U32 height, F32 squareSize, + const Point2F& min, const Point2F& max, + GFXVertexBufferHandle< VertexType >& outVertices, + GFXPrimitiveBufferHandle& outPrimitives ); + + Box3F getPlaneBox(); + +private: + + typedef GFXVertexBufferHandle< VertexType > VertexBuffer; + typedef GFXPrimitiveBufferHandle PrimitiveBuffer; + + F32 mSquareSize; ///< World units per grid cell edge. + F32 mScaleU; ///< Scale factor for U texture coordinates. + F32 mScaleV; ///< Scale factor for V texture coordinates. + String mMaterialName; ///< Object name of material to use. + BaseMatInstance* mMaterial; ///< Instantiated material based on given material name. + + PhysicsBody *mPhysicsRep; + + /// @name Rendering State + /// @{ + + Point2F mMin; + Point2F mMax; + VertexBuffer mVertexBuffer; + PrimitiveBuffer mPrimitiveBuffer; + GFXPrimitive mPrimitive; + + /// @} + + Convex* mConvexList; ///< List of collision convexes we have created; for cleanup. +}; + +static const F32 GROUND_PLANE_BOX_HEIGHT_HALF = 1.0f; +static const F32 GROUND_PLANE_BOX_EXTENT_HALF = 16000.0f; + +inline Box3F GroundPlane::getPlaneBox() +{ + Box3F planeBox; + + planeBox.minExtents = Point3F( - GROUND_PLANE_BOX_EXTENT_HALF, + - GROUND_PLANE_BOX_EXTENT_HALF, + - 0.05f ); + planeBox.maxExtents = Point3F( GROUND_PLANE_BOX_EXTENT_HALF, + GROUND_PLANE_BOX_EXTENT_HALF, + 0.05f ); + return planeBox; +} + +#endif // _TORQUE_T3D_GROUNDPLANE_H_ diff --git a/Engine/source/T3D/guiMaterialPreview.cpp b/Engine/source/T3D/guiMaterialPreview.cpp new file mode 100644 index 000000000..1229956e6 --- /dev/null +++ b/Engine/source/T3D/guiMaterialPreview.cpp @@ -0,0 +1,496 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// Original header: +// GuiMaterialPreview Control for Material Editor Written by Travis Vroman of Gaslight Studios +// Updated 2-14-09 +// Portions based off Constructor viewport code. + +#include "console/engineAPI.h" +#include "T3D/guiMaterialPreview.h" +#include "renderInstance/renderPassManager.h" +#include "lighting/lightManager.h" +#include "lighting/lightInfo.h" +#include "core/resourceManager.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" + +// GuiMaterialPreview +GuiMaterialPreview::GuiMaterialPreview() +: mMaxOrbitDist(5.0f), + mMinOrbitDist(0.0f), + mOrbitDist(5.0f), + mMouseState(None), + mModel(NULL), + mLastMousePoint(0, 0), + lastRenderTime(0), + runThread(0), + mFakeSun(NULL) +{ + mActive = true; + mCameraMatrix.identity(); + mCameraRot.set( mDegToRad(30.0f), 0, mDegToRad(-30.0f) ); + mCameraPos.set(0.0f, 1.75f, 1.25f); + mCameraMatrix.setColumn(3, mCameraPos); + mOrbitPos.set(0.0f, 0.0f, 0.0f); + mTransStep = 0.01f; + mTranMult = 4.0; + mLightTransStep = 0.01f; + mLightTranMult = 4.0; + mOrbitRelPos = Point3F(0,0,0); + + // By default don't do dynamic reflection + // updates for this viewport. + mReflectPriority = 0.0f; +} + +GuiMaterialPreview::~GuiMaterialPreview() +{ + SAFE_DELETE(mModel); + SAFE_DELETE(mFakeSun); +} + +bool GuiMaterialPreview::onWake() +{ + if( !Parent::onWake() ) + return false; + + if (!mFakeSun) + mFakeSun = LightManager::createLightInfo(); + + mFakeSun->setColor( ColorF( 1.0f, 1.0f, 1.0f ) ); + mFakeSun->setAmbient( ColorF( 0.5f, 0.5f, 0.5f ) ); + mFakeSun->setDirection( VectorF( 0.0f, 0.707f, -0.707f ) ); + mFakeSun->setPosition( mFakeSun->getDirection() * -10000.0f ); + mFakeSun->setRange( 2000000.0f ); + + return true; +} + +// This function allows the viewport's ambient color to be changed. This is exposed to script below. +void GuiMaterialPreview::setAmbientLightColor( F32 r, F32 g, F32 b ) +{ + ColorF temp(r, g, b); + temp.clamp(); + GuiMaterialPreview::mFakeSun->setAmbient( temp ); +} + +// This function allows the light's color to be changed. This is exposed to script below. +void GuiMaterialPreview::setLightColor( F32 r, F32 g, F32 b ) +{ + ColorF temp(r, g, b); + temp.clamp(); + GuiMaterialPreview::mFakeSun->setColor( temp ); +} + +// This function is for moving the light in the scene. This needs to be adjusted to keep the light +// from getting all out of whack. For now, we'll just rely on the reset function if we need it +// fixed. +void GuiMaterialPreview::setLightTranslate(S32 modifier, F32 xstep, F32 ystep) +{ + F32 _lighttransstep = (modifier & SI_SHIFT ? mLightTransStep : (mLightTransStep*mLightTranMult)); + + Point3F relativeLightDirection = GuiMaterialPreview::mFakeSun->getDirection(); + // May be able to get rid of this. For now, it helps to fix the position of the light if i gets messed up. + if (modifier & SI_PRIMARY_CTRL) + { + relativeLightDirection.x += ( xstep * _lighttransstep * -1 );//need to invert this for some reason. Otherwise, it's backwards. + relativeLightDirection.y += ( ystep * _lighttransstep ); + GuiMaterialPreview::mFakeSun->setDirection(relativeLightDirection); + } + // Default action taken by mouse wheel clicking. + else + { + relativeLightDirection.x += ( xstep * _lighttransstep * -1 ); //need to invert this for some reason. Otherwise, it's backwards. + relativeLightDirection.z += ( ystep * _lighttransstep ); + GuiMaterialPreview::mFakeSun->setDirection(relativeLightDirection); + } +} + +// This is for panning the viewport camera. +void GuiMaterialPreview::setTranslate(S32 modifier, F32 xstep, F32 ystep) +{ + F32 transstep = (modifier & SI_SHIFT ? mTransStep : (mTransStep*mTranMult)); + + F32 nominalDistance = 20.0; + Point3F vec = mCameraPos; + vec -= mOrbitPos; + transstep *= vec.len() / nominalDistance; + + if (modifier & SI_PRIMARY_CTRL) + { + mOrbitRelPos.x += ( xstep * transstep ); + mOrbitRelPos.y += ( ystep * transstep ); + } + else + { + mOrbitRelPos.x += ( xstep * transstep ); + mOrbitRelPos.z += ( ystep * transstep ); + } +} + +// Left Click +void GuiMaterialPreview::onMouseDown(const GuiEvent &event) +{ + mMouseState = MovingLight; + mLastMousePoint = event.mousePoint; + mouseLock(); +} + +// Left Click Release +void GuiMaterialPreview::onMouseUp(const GuiEvent &event) +{ + mouseUnlock(); + mMouseState = None; +} + +// Left Click Drag +void GuiMaterialPreview::onMouseDragged(const GuiEvent &event) +{ + if(mMouseState != MovingLight) + { + return; + } + // If we are MovingLight... + else + { + Point2I delta = event.mousePoint - mLastMousePoint; + mLastMousePoint = event.mousePoint; + setLightTranslate(event.modifier, delta.x, delta.y); + } +} + +// Right Click +void GuiMaterialPreview::onRightMouseDown(const GuiEvent &event) +{ + mMouseState = Rotating; + mLastMousePoint = event.mousePoint; + mouseLock(); +} + +// Right Click Release +void GuiMaterialPreview::onRightMouseUp(const GuiEvent &event) +{ + mouseUnlock(); + mMouseState = None; +} + +// Right Click Drag +void GuiMaterialPreview::onRightMouseDragged(const GuiEvent &event) +{ + if (mMouseState != Rotating) + { + return; + } + Point2I delta = event.mousePoint - mLastMousePoint; + mLastMousePoint = event.mousePoint; + mCameraRot.x += (delta.y * 0.01f); + mCameraRot.z += (delta.x * 0.01f); +} + +// Mouse Wheel Scroll Up +bool GuiMaterialPreview::onMouseWheelUp(const GuiEvent &event) +{ + mOrbitDist = (mOrbitDist - 0.10f); + return true; +} + +// Mouse Wheel Scroll Down +bool GuiMaterialPreview::onMouseWheelDown(const GuiEvent &event) +{ + mOrbitDist = (mOrbitDist + 0.10f); + return true; +} + +// Mouse Wheel Click +void GuiMaterialPreview::onMiddleMouseDown(const GuiEvent &event) +{ + if (!mActive || !mVisible || !mAwake) + { + return; + } + mMouseState = Panning; + mLastMousePoint = event.mousePoint; + mouseLock(); +} + +// Mouse Wheel Click Release +void GuiMaterialPreview::onMiddleMouseUp(const GuiEvent &event) +{ + mouseUnlock(); + mMouseState = None; +} + +// Mouse Wheel Click Drag +void GuiMaterialPreview::onMiddleMouseDragged(const GuiEvent &event) +{ + if (mMouseState != Panning) + { + return; + } + Point2I delta = event.mousePoint - mLastMousePoint; + mLastMousePoint = event.mousePoint; + setTranslate(event.modifier, delta.x, delta.y); +} + +// This is used to set the model we want to view in the control object. +void GuiMaterialPreview::setObjectModel(const char* modelName) +{ + deleteModel(); + + Resource model = ResourceManager::get().load(modelName); + if (! bool(model)) + { + Con::warnf(avar("GuiMaterialPreview: Failed to load model %s. Please check your model name and load a valid model.", modelName)); + return; + } + + mModel = new TSShapeInstance(model, true); + AssertFatal(mModel, avar("GuiMaterialPreview: Failed to load model %s. Please check your model name and load a valid model.", modelName)); + + // Initialize camera values: + mOrbitPos = mModel->getShape()->center; + mMinOrbitDist = mModel->getShape()->radius; + + lastRenderTime = Platform::getVirtualMilliseconds(); +} + +void GuiMaterialPreview::deleteModel() +{ + SAFE_DELETE(mModel); + runThread = 0; +} + +// This is called whenever there is a change in the camera. +bool GuiMaterialPreview::processCameraQuery(CameraQuery* query) +{ + MatrixF xRot, zRot; + Point3F vecf, vecu, vecr;; + xRot.set(EulerF(mCameraRot.x, 0.0f, 0.0f)); + zRot.set(EulerF(0.0f, 0.0f, mCameraRot.z)); + + if(mMouseState != Panning) + { + // Adjust the camera so that we are still facing the model: + Point3F vec; + + mCameraMatrix.mul(zRot, xRot); + mCameraMatrix.getColumn(1, &vec); + vec *= mOrbitDist; + mCameraPos = mOrbitPos - vec; + + query->farPlane = 2100.0f; + query->nearPlane = query->farPlane / 5000.0f; + query->fov = 45.0f; + mCameraMatrix.setColumn(3, mCameraPos); + query->cameraMatrix = mCameraMatrix; + } + else + { + mCameraMatrix.mul( zRot, xRot ); + mCameraMatrix.getColumn( 1, &vecf ); // Forward vector + mCameraMatrix.getColumn( 2, &vecu ); // Up vector + mCameraMatrix.getColumn( 0, &vecr ); // Right vector + + Point3F flatVecf(vecf.x, vecf.y, 0.0f); + + Point3F modvecf = flatVecf * mOrbitRelPos.y; + Point3F modvecu = vecu * mOrbitRelPos.z; + Point3F modvecr = vecr * mOrbitRelPos.x; + + // Change the orbit position + mOrbitPos += modvecu - modvecr + modvecf; + + F32 vecfmul = mOrbitDist; + Point3F virtualVecF = vecf * mOrbitDist; + vecf *= vecfmul; + + mCameraPos = mOrbitPos - virtualVecF; + + // Update the camera's position + mCameraMatrix.setColumn( 3, (mOrbitPos - vecf) ); + + query->farPlane = 2100.0f; + query->nearPlane = query->farPlane / 5000.0f; + query->fov = 45.0f; + query->cameraMatrix = mCameraMatrix; + + // Reset the relative position + mOrbitRelPos = Point3F(0,0,0); + } + return true; +} + +void GuiMaterialPreview::onMouseEnter(const GuiEvent & event) +{ + Con::executef(this, "onMouseEnter"); +} + +void GuiMaterialPreview::onMouseLeave(const GuiEvent & event) +{ + Con::executef(this, "onMouseLeave"); +} + +void GuiMaterialPreview::renderWorld(const RectI &updateRect) +{ + // nothing to render, punt + if ( !mModel && !mMountedModel ) + return; + + S32 time = Platform::getVirtualMilliseconds(); + //S32 dt = time - lastRenderTime; + lastRenderTime = time; + + + + F32 left, right, top, bottom, nearPlane, farPlane; + bool isOrtho; + GFX->getFrustum( &left, &right, &bottom, &top, &nearPlane, &farPlane, &isOrtho); + Frustum frust( isOrtho, left, right, bottom, top, nearPlane, farPlane, MatrixF::Identity ); + + FogData savedFogData = gClientSceneGraph->getFogData(); + gClientSceneGraph->setFogData( FogData() ); // no fog in preview window + + RenderPassManager* renderPass = gClientSceneGraph->getDefaultRenderPass(); + SceneRenderState state + ( + gClientSceneGraph, + SPT_Diffuse, + SceneCameraState( GFX->getViewport(), frust, GFX->getWorldMatrix(), GFX->getProjectionMatrix() ), + renderPass, + true + ); + + // Set up our TS render state here. + TSRenderState rdata; + rdata.setSceneState( &state ); + + // We might have some forward lit materials + // so pass down a query to gather lights. + LightQuery query; + query.init( SphereF( Point3F::Zero, 1.0f ) ); + rdata.setLightQuery( &query ); + + // Set up pass transforms + renderPass->assignSharedXform(RenderPassManager::View, MatrixF::Identity); + renderPass->assignSharedXform(RenderPassManager::Projection, GFX->getProjectionMatrix()); + + LIGHTMGR->unregisterAllLights(); + LIGHTMGR->setSpecialLight( LightManager::slSunLightType, mFakeSun ); + + if ( mModel ) + mModel->render( rdata ); + + if ( mMountedModel ) + { + // render a weapon + /* + MatrixF mat; + + GFX->pushWorldMatrix(); + GFX->multWorld( mat ); + + GFX->popWorldMatrix(); + */ + } + + renderPass->renderPass( &state ); + + gClientSceneGraph->setFogData( savedFogData ); // restore fog setting + + // Make sure to remove our fake sun + LIGHTMGR->unregisterAllLights(); +} + +// Make sure the orbit distance is within the acceptable range. +void GuiMaterialPreview::setOrbitDistance(F32 distance) +{ + mOrbitDist = mClampF(distance, mMinOrbitDist, mMaxOrbitDist); +} + +// This function is meant to be used with a button to put everything back to default settings. +void GuiMaterialPreview::resetViewport() +{ + // Reset the camera's orientation. + mCameraRot.set( mDegToRad(30.0f), 0, mDegToRad(-30.0f) ); + mCameraPos.set(0.0f, 1.75f, 1.25f); + mOrbitDist = 5.0f; + mOrbitPos = mModel->getShape()->center; + + // Reset the viewport's lighting. + GuiMaterialPreview::mFakeSun->setColor( ColorF( 1.0f, 1.0f, 1.0f ) ); + GuiMaterialPreview::mFakeSun->setAmbient( ColorF( 0.5f, 0.5f, 0.5f ) ); + GuiMaterialPreview::mFakeSun->setDirection( VectorF( 0.0f, 0.707f, -0.707f ) ); +} + +// Expose the class and functions to the console. +IMPLEMENT_CONOBJECT(GuiMaterialPreview); + +ConsoleDocClass( GuiMaterialPreview, + "@brief Visual preview of a specified Material\n\n" + "Editor use only.\n\n" + "@internal" +); + +// Set the model. +DefineEngineMethod(GuiMaterialPreview, setModel, void, ( const char* shapeName ),, + "Sets the model to be displayed in this control\n\n" + "@param shapeName Name of the model to display.\n") +{ + object->setObjectModel(shapeName); +} + +DefineEngineMethod(GuiMaterialPreview, deleteModel, void, (),, + "Deletes the preview model.\n") +{ + object->deleteModel(); +} + +// Set orbit distance around the model. +DefineEngineMethod(GuiMaterialPreview, setOrbitDistance, void, ( F32 distance ),, + "Sets the distance at which the camera orbits the object. Clamped to the " + "acceptable range defined in the class by min and max orbit distances.\n\n" + "@param distance The distance to set the orbit to (will be clamped).") +{ + object->setOrbitDistance(distance); +} + +// Reset control to default values. Meant to be used with a button. +DefineEngineMethod(GuiMaterialPreview, reset, void, (),, + "Resets the viewport to default zoom, pan, rotate and lighting.") +{ + object->resetViewport(); +} + +// This function allows the user to change the light's color. +DefineEngineMethod(GuiMaterialPreview, setLightColor, void, ( ColorF color ),, + "Sets the color of the light in the scene.\n") +{ + object->setLightColor( color.red, color.green, color.blue ); +} + +// This function allows the user to change the viewports's ambient color. +DefineEngineMethod(GuiMaterialPreview, setAmbientLightColor, void, ( ColorF color ),, + "Sets the color of the ambient light in the scene.\n") +{ + object->setAmbientLightColor( color.red, color.green, color.blue ); +} diff --git a/Engine/source/T3D/guiMaterialPreview.h b/Engine/source/T3D/guiMaterialPreview.h new file mode 100644 index 000000000..c51005a8a --- /dev/null +++ b/Engine/source/T3D/guiMaterialPreview.h @@ -0,0 +1,129 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// Original header: +// GuiMaterialPreview Control for Material Editor Written by Travis Vroman of Gaslight Studios +// Updated 2-14-09 +// Portions based off Constructor viewport code. + +#ifndef _GUIMATERIALPREVIEW_H_ +#define _GUIMATERIALPREVIEW_H_ + +#include "gui/3d/guiTSControl.h" +#include "ts/tsShapeInstance.h" + +class LightInfo; + +class GuiMaterialPreview : public GuiTSCtrl +{ +private: + typedef GuiTSCtrl Parent; + +protected: + enum MouseState + { + None, + Rotating, + Zooming, + Panning, + MovingLight + }; + + MouseState mMouseState; + + TSShapeInstance* mModel; + TSShapeInstance* mMountedModel; + U32 mSkinTag; + + // For Camera Panning. + F32 mTransStep; //*** Amount of translation with each mouse move + F32 mTranMult; //*** With a modifier, how much faster to translate + + // For light translation. + F32 mLightTransStep; + F32 mLightTranMult; + + Point3F mCameraPos; + MatrixF mCameraMatrix; + EulerF mCameraRot; + Point3F mOrbitPos; + Point3F mOrbitRelPos; + Point3F mCameraTransform; + + TSThread * runThread; + S32 lastRenderTime; + + Point2I mLastMousePoint; + + LightInfo* mFakeSun; + +public: + bool onWake(); + + void onMouseEnter(const GuiEvent &event); + void onMouseLeave(const GuiEvent &event); + void onMouseDown(const GuiEvent &event); + void onMouseUp(const GuiEvent &event); + void onMouseDragged(const GuiEvent &event); + void onRightMouseDown(const GuiEvent &event); + void onRightMouseUp(const GuiEvent &event); + void onRightMouseDragged(const GuiEvent &event); + bool onMouseWheelUp(const GuiEvent &event); + bool onMouseWheelDown(const GuiEvent &event); + void onMiddleMouseUp(const GuiEvent &event); + void onMiddleMouseDown(const GuiEvent &event); + void onMiddleMouseDragged(const GuiEvent &event); + + // For Camera Panning. + void setTranslate(S32 modifier, F32 xstep, F32 ystep); + + // For Light Translation. + void setLightTranslate(S32 modifier, F32 xstep, F32 ystep); + + // For changing the light color. + void setLightColor( F32 r, F32 g, F32 b ); + + // For changing the ambient light color. + void setAmbientLightColor( F32 r, F32 g, F32 b ); + + void setObjectModel(const char * modelName); + void deleteModel(); + void resetViewport(); + void setOrbitDistance(F32 distance); + + bool processCameraQuery(CameraQuery *query); + void renderWorld(const RectI &updateRect); + + DECLARE_CONOBJECT(GuiMaterialPreview); + DECLARE_CATEGORY( "Gui Editor" ); + + GuiMaterialPreview(); + ~GuiMaterialPreview(); + +private: + F32 mMaxOrbitDist; + F32 mMinOrbitDist; + F32 mOrbitDist; + +}; + +#endif diff --git a/Engine/source/T3D/guiNoMouseCtrl.cpp b/Engine/source/T3D/guiNoMouseCtrl.cpp new file mode 100644 index 000000000..31987ceb3 --- /dev/null +++ b/Engine/source/T3D/guiNoMouseCtrl.cpp @@ -0,0 +1,41 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/core/guiControl.h" + +//------------------------------------------------------------------------------ +class GuiNoMouseCtrl : public GuiControl +{ + typedef GuiControl Parent; + public: + + // GuiControl + bool pointInControl(const Point2I &) { return(false); } + DECLARE_CONOBJECT(GuiNoMouseCtrl); + DECLARE_CATEGORY( "Gui Other" ); +}; +IMPLEMENT_CONOBJECT(GuiNoMouseCtrl); + +ConsoleDocClass( GuiNoMouseCtrl, + "@brief No known usage, possibly Legacy.\n\n" + "Not used at all, internal until deprecated\n\n" + "@internal"); \ No newline at end of file diff --git a/Engine/source/T3D/guiObjectView.cpp b/Engine/source/T3D/guiObjectView.cpp new file mode 100644 index 000000000..35d14c1c9 --- /dev/null +++ b/Engine/source/T3D/guiObjectView.cpp @@ -0,0 +1,988 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "T3D/guiObjectView.h" +#include "renderInstance/renderPassManager.h" +#include "lighting/lightManager.h" +#include "lighting/lightInfo.h" +#include "core/resourceManager.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "console/consoleTypes.h" +#include "math/mathTypes.h" +#include "gfx/gfxTransformSaver.h" +#include "console/engineAPI.h" + +IMPLEMENT_CONOBJECT( GuiObjectView ); + +ConsoleDocClass( GuiObjectView, + "@brief GUI control which displays a 3D model.\n\n" + + "Model displayed in the control can have other objects mounted onto it, and the light settings can be adjusted.\n\n" + + "@tsexample\n" + " new GuiObjectView(ObjectPreview)\n" + " {\n" + " shapeFile = \"art/shapes/items/kit/healthkit.dts\";\n" + " mountedNode = \"mount0\";\n" + " lightColor = \"1 1 1 1\";\n" + " lightAmbient = \"0.5 0.5 0.5 1\";\n" + " lightDirection = \"0 0.707 -0.707\";\n" + " orbitDiststance = \"2\";\n" + " minOrbitDiststance = \"0.917688\";\n" + " maxOrbitDiststance = \"5\";\n" + " cameraSpeed = \"0.01\";\n" + " cameraZRot = \"0\";\n" + " forceFOV = \"0\";\n" + " reflectPriority = \"0\";\n" + " };\n" + "@endtsexample\n\n" + + "@see GuiControl\n\n" + + "@ingroup Gui3D\n" +); + +IMPLEMENT_CALLBACK( GuiObjectView, onMouseEnter, void, (),(), + "@brief Called whenever the mouse enters the control.\n\n" + "@tsexample\n" + "// The mouse has entered the control, causing the callback to occur\n" + "GuiObjectView::onMouseEnter(%this)\n" + " {\n" + " // Code to run when the mouse enters this control\n" + " }\n" + "@endtsexample\n\n" + "@see GuiControl\n\n" +); + +IMPLEMENT_CALLBACK( GuiObjectView, onMouseLeave, void, (),(), + "@brief Called whenever the mouse leaves the control.\n\n" + "@tsexample\n" + "// The mouse has left the control, causing the callback to occur\n" + "GuiObjectView::onMouseLeave(%this)\n" + " {\n" + " // Code to run when the mouse leaves this control\n" + " }\n" + "@endtsexample\n\n" + "@see GuiControl\n\n" +); + +//------------------------------------------------------------------------------ + +GuiObjectView::GuiObjectView() + : mMaxOrbitDist( 5.0f ), + mMinOrbitDist( 0.0f ), + mOrbitDist( 5.0f ), + mMouseState( None ), + mModel( NULL ), + mMountedModel( NULL ), + mLastMousePoint( 0, 0 ), + mLastRenderTime( 0 ), + mRunThread( NULL ), + mLight( NULL ), + mAnimationSeq( -1 ), + mMountNodeName( "mount0" ), + mMountNode( -1 ), + mCameraSpeed( 0.01f ), + mLightColor( 1.0f, 1.0f, 1.0f ), + mLightAmbient( 0.5f, 0.5f, 0.5f ), + mLightDirection( 0.f, 0.707f, -0.707f ) +{ + mCameraMatrix.identity(); + mCameraRot.set( 0.0f, 0.0f, 3.9f ); + mCameraPos.set( 0.0f, 1.75f, 1.25f ); + mCameraMatrix.setColumn( 3, mCameraPos ); + mOrbitPos.set( 0.0f, 0.0f, 0.0f ); + + // By default don't do dynamic reflection + // updates for this viewport. + mReflectPriority = 0.0f; +} + +//------------------------------------------------------------------------------ + +GuiObjectView::~GuiObjectView() +{ + if( mModel ) + SAFE_DELETE( mModel ); + if( mMountedModel ) + SAFE_DELETE( mMountedModel ); + if( mLight ) + SAFE_DELETE( mLight ); +} + +//------------------------------------------------------------------------------ + +void GuiObjectView::initPersistFields() +{ + addGroup( "Model" ); + + addField( "shapeFile", TypeStringFilename, Offset( mModelName, GuiObjectView ), + "The object model shape file to show in the view." ); + addField( "skin", TypeRealString, Offset( mSkinName, GuiObjectView ), + "The skin to use on the object model." ); + + endGroup( "Model" ); + + addGroup( "Animation" ); + + addField( "animSequence", TypeRealString, Offset( mAnimationSeqName, GuiObjectView ), + "The animation sequence to play on the model." ); + + endGroup( "Animation" ); + + addGroup( "Mounting" ); + + addField( "mountedShapeFile", TypeStringFilename, Offset( mMountedModelName, GuiObjectView ), + "Optional shape file to mount on the primary model (e.g. weapon)." ); + addField( "mountedSkin", TypeRealString, Offset( mMountSkinName, GuiObjectView ), + "Skin name used on mounted shape file." ); + addField( "mountedNode", TypeRealString, Offset( mMountNodeName, GuiObjectView ), + "Name of node on primary model to which to mount the secondary shape." ); + + endGroup( "Mounting" ); + + addGroup( "Lighting" ); + + addField( "lightColor", TypeColorF, Offset( mLightColor, GuiObjectView ), + "Diffuse color of the sunlight used to render the model." ); + addField( "lightAmbient", TypeColorF, Offset( mLightAmbient, GuiObjectView ), + "Ambient color of the sunlight used to render the model." ); + addField( "lightDirection", TypePoint3F, Offset( mLightDirection, GuiObjectView ), + "Direction from which the model is illuminated." ); + + endGroup( "Lighting" ); + + addGroup( "Camera" ); + + addField( "orbitDiststance", TypeF32, Offset( mOrbitDist, GuiObjectView ), + "Distance from which to render the model." ); + addField( "minOrbitDiststance", TypeF32, Offset( mMinOrbitDist, GuiObjectView ), + "Maxiumum distance to which the camera can be zoomed out." ); + addField( "maxOrbitDiststance", TypeF32, Offset( mMaxOrbitDist, GuiObjectView ), + "Minimum distance below which the camera will not zoom in further." ); + addField( "cameraSpeed", TypeF32, Offset( mCameraSpeed, GuiObjectView ), + "Multiplier for mouse camera operations." ); + + endGroup( "Camera" ); + + Parent::initPersistFields(); +} + +//------------------------------------------------------------------------------ + +void GuiObjectView::onStaticModified( StringTableEntry slotName, const char* newValue ) +{ + Parent::onStaticModified( slotName, newValue ); + + static StringTableEntry sShapeFile = StringTable->insert( "shapeFile" ); + static StringTableEntry sSkin = StringTable->insert( "skin" ); + static StringTableEntry sMountedShapeFile = StringTable->insert( "mountedShapeFile" ); + static StringTableEntry sMountedSkin = StringTable->insert( "mountedSkin" ); + static StringTableEntry sMountedNode = StringTable->insert( "mountedNode" ); + static StringTableEntry sLightColor = StringTable->insert( "lightColor" ); + static StringTableEntry sLightAmbient = StringTable->insert( "lightAmbient" ); + static StringTableEntry sLightDirection = StringTable->insert( "lightDirection" ); + static StringTableEntry sOrbitDistance = StringTable->insert( "orbitDistance" ); + static StringTableEntry sMinOrbitDistance = StringTable->insert( "minOrbitDistance" ); + static StringTableEntry sMaxOrbitDistance = StringTable->insert( "maxOrbitDistance" ); + static StringTableEntry sAnimSequence = StringTable->insert( "animSequence" ); + + if( slotName == sShapeFile ) + setObjectModel( String( mModelName ) ); + else if( slotName == sSkin ) + setSkin( String( mSkinName ) ); + else if( slotName == sMountedShapeFile ) + setMountedObject( String( mMountedModelName ) ); + else if( slotName == sMountedSkin ) + setMountSkin( String( mMountSkinName ) ); + else if( slotName == sMountedNode ) + setMountNode( String( mMountNodeName ) ); + else if( slotName == sLightColor ) + setLightColor( mLightColor ); + else if( slotName == sLightAmbient ) + setLightAmbient( mLightAmbient ); + else if( slotName == sLightDirection ) + setLightDirection( mLightDirection ); + else if( slotName == sOrbitDistance || slotName == sMinOrbitDistance || slotName == sMaxOrbitDistance ) + setOrbitDistance( mOrbitDist ); + else if( slotName == sAnimSequence ) + setObjectAnimation( String( mAnimationSeqName ) ); +} + +//------------------------------------------------------------------------------ + +bool GuiObjectView::onWake() +{ + if( !Parent::onWake() ) + return false; + + if( !mLight ) + { + mLight = LIGHTMGR->createLightInfo(); + + mLight->setColor( mLightColor ); + mLight->setAmbient( mLightAmbient ); + mLight->setDirection( mLightDirection ); + } + + return true; +} + +//------------------------------------------------------------------------------ + +void GuiObjectView::onMouseDown( const GuiEvent &event ) +{ + if( !mActive || !mVisible || !mAwake ) + return; + + mMouseState = Rotating; + mLastMousePoint = event.mousePoint; + mouseLock(); +} + +//------------------------------------------------------------------------------ + +void GuiObjectView::onMouseUp( const GuiEvent &event ) +{ + mouseUnlock(); + mMouseState = None; +} + +//------------------------------------------------------------------------------ + +void GuiObjectView::onMouseDragged( const GuiEvent &event ) +{ + if( mMouseState != Rotating ) + return; + + Point2I delta = event.mousePoint - mLastMousePoint; + mLastMousePoint = event.mousePoint; + + mCameraRot.x += ( delta.y * mCameraSpeed ); + mCameraRot.z += ( delta.x * mCameraSpeed ); +} + +//------------------------------------------------------------------------------ + +void GuiObjectView::onRightMouseDown( const GuiEvent &event ) +{ + mMouseState = Zooming; + mLastMousePoint = event.mousePoint; + mouseLock(); +} + +//------------------------------------------------------------------------------ + +void GuiObjectView::onRightMouseUp( const GuiEvent &event ) +{ + mouseUnlock(); + mMouseState = None; +} + +//------------------------------------------------------------------------------ + +void GuiObjectView::onRightMouseDragged( const GuiEvent &event ) +{ + if( mMouseState != Zooming ) + return; + + S32 delta = event.mousePoint.y - mLastMousePoint.y; + mLastMousePoint = event.mousePoint; + + mOrbitDist += ( delta * mCameraSpeed ); +} + +//------------------------------------------------------------------------------ + +void GuiObjectView::setObjectAnimation( S32 index ) +{ + mAnimationSeq = index; + mAnimationSeqName = String(); + + if( mModel ) + _initAnimation(); +} + +//------------------------------------------------------------------------------ + +void GuiObjectView::setObjectAnimation( const String& sequenceName ) +{ + mAnimationSeq = -1; + mAnimationSeqName = sequenceName; + + if( mModel ) + _initAnimation(); +} + +//------------------------------------------------------------------------------ + +void GuiObjectView::setObjectModel( const String& modelName ) +{ + SAFE_DELETE( mModel ); + mRunThread = 0; + mModelName = String::EmptyString; + + // Load the shape. + + Resource< TSShape > model = ResourceManager::get().load( modelName ); + if( !model ) + { + Con::warnf( "GuiObjectView::setObjectModel - Failed to load model '%s'", modelName.c_str() ); + return; + } + + // Instantiate it. + + mModel = new TSShapeInstance( model, true ); + mModelName = modelName; + + if( !mSkinName.isEmpty() ) + mModel->reSkin( mSkinName ); + + // Initialize camera values. + + mOrbitPos = mModel->getShape()->center; + mMinOrbitDist = mModel->getShape()->radius; + + // Initialize animation. + + _initAnimation(); + _initMount(); +} + +//------------------------------------------------------------------------------ + +void GuiObjectView::setSkin( const String& name ) +{ + if( mModel ) + mModel->reSkin( name, mSkinName ); + + mSkinName = name; +} + +//------------------------------------------------------------------------------ + +void GuiObjectView::setMountSkin( const String& name ) +{ + if( mMountedModel ) + mMountedModel->reSkin( name, mMountSkinName ); + + mMountSkinName = name; +} + +//------------------------------------------------------------------------------ + +void GuiObjectView::setMountNode( S32 index ) +{ + setMountNode( String::ToString( "mount%i", index ) ); +} + +//------------------------------------------------------------------------------ + +void GuiObjectView::setMountNode( const String& name ) +{ + mMountNodeName = name; + + if( mModel ) + _initMount(); +} + +//------------------------------------------------------------------------------ + +void GuiObjectView::setMountedObject( const String& modelName ) +{ + SAFE_DELETE( mMountedModel ); + mMountedModelName = String::EmptyString; + + // Load the model. + + Resource< TSShape > model = ResourceManager::get().load( modelName ); + if( !model ) + { + Con::warnf( "GuiObjectView::setMountedObject - Failed to load object model '%s'", + modelName.c_str() ); + return; + } + + mMountedModel = new TSShapeInstance( model, true ); + mMountedModelName = modelName; + + if( !mMountSkinName.isEmpty() ) + mMountedModel->reSkin( mMountSkinName ); + + if( mModel ) + _initMount(); +} + +//------------------------------------------------------------------------------ + +bool GuiObjectView::processCameraQuery( CameraQuery* query ) +{ + // Adjust the camera so that we are still facing the model. + + Point3F vec; + MatrixF xRot, zRot; + xRot.set( EulerF( mCameraRot.x, 0.0f, 0.0f ) ); + zRot.set( EulerF( 0.0f, 0.0f, mCameraRot.z ) ); + + mCameraMatrix.mul( zRot, xRot ); + mCameraMatrix.getColumn( 1, &vec ); + vec *= mOrbitDist; + mCameraPos = mOrbitPos - vec; + + query->farPlane = 2100.0f; + query->nearPlane = query->farPlane / 5000.0f; + query->fov = 45.0f; + mCameraMatrix.setColumn( 3, mCameraPos ); + query->cameraMatrix = mCameraMatrix; + + return true; +} + +//------------------------------------------------------------------------------ + +void GuiObjectView::onMouseEnter( const GuiEvent & event ) +{ + onMouseEnter_callback(); +} + +//------------------------------------------------------------------------------ + +void GuiObjectView::onMouseLeave( const GuiEvent & event ) +{ + onMouseLeave_callback(); +} + +//------------------------------------------------------------------------------ + +void GuiObjectView::renderWorld( const RectI& updateRect ) +{ + if( !mModel ) + return; + + GFXTransformSaver _saveTransforms; + + // Determine the camera position, and store off render state. + + MatrixF modelview; + MatrixF mv; + Point3F cp; + + modelview = GFX->getWorldMatrix(); + + mv = modelview; + mv.inverse(); + mv.getColumn( 3, &cp ); + + RenderPassManager* renderPass = gClientSceneGraph->getDefaultRenderPass(); + + S32 time = Platform::getVirtualMilliseconds(); + S32 dt = time - mLastRenderTime; + mLastRenderTime = time; + + LIGHTMGR->unregisterAllLights(); + LIGHTMGR->setSpecialLight( LightManager::slSunLightType, mLight ); + + GFX->setStateBlock( mDefaultGuiSB ); + + F32 left, right, top, bottom, nearPlane, farPlane; + bool isOrtho; + GFX->getFrustum( &left, &right, &bottom, &top, &nearPlane, &farPlane, &isOrtho ); + + Frustum frust( false, left, right, top, bottom, nearPlane, farPlane, MatrixF::Identity ); + + SceneRenderState state + ( + gClientSceneGraph, + SPT_Diffuse, + SceneCameraState( GFX->getViewport(), frust, GFX->getWorldMatrix(), GFX->getProjectionMatrix() ), + renderPass, + false + ); + + // Set up our TS render state here. + TSRenderState rdata; + rdata.setSceneState( &state ); + + // We might have some forward lit materials + // so pass down a query to gather lights. + LightQuery query; + query.init( SphereF( Point3F::Zero, 1.0f ) ); + rdata.setLightQuery( &query ); + + // Render primary model. + + if( mModel ) + { + if( mRunThread ) + { + mModel->advanceTime( dt / 1000.f, mRunThread ); + mModel->animate(); + } + + mModel->render( rdata ); + } + + // Render mounted model. + + if( mMountedModel && mMountNode != -1 ) + { + GFX->pushWorldMatrix(); + GFX->multWorld( mModel->mNodeTransforms[ mMountNode ] ); + GFX->multWorld( mMountTransform ); + + mMountedModel->render( rdata ); + + GFX->popWorldMatrix(); + } + + renderPass->renderPass( &state ); + + // Make sure to remove our fake sun. + LIGHTMGR->unregisterAllLights(); +} + +//------------------------------------------------------------------------------ + +void GuiObjectView::setOrbitDistance( F32 distance ) +{ + // Make sure the orbit distance is within the acceptable range + mOrbitDist = mClampF( distance, mMinOrbitDist, mMaxOrbitDist ); +} + +//------------------------------------------------------------------------------ + +void GuiObjectView::setCameraSpeed( F32 factor ) +{ + mCameraSpeed = factor; +} + +//------------------------------------------------------------------------------ + +void GuiObjectView::setLightColor( const ColorF& color ) +{ + mLightColor = color; + if( mLight ) + mLight->setColor( color ); +} + +//------------------------------------------------------------------------------ + +void GuiObjectView::setLightAmbient( const ColorF& color ) +{ + mLightAmbient = color; + if( mLight ) + mLight->setAmbient( color ); +} + +//------------------------------------------------------------------------------ + +void GuiObjectView::setLightDirection( const Point3F& direction ) +{ + mLightDirection = direction; + if( mLight ) + mLight->setDirection( direction ); +} + +//------------------------------------------------------------------------------ + +void GuiObjectView::_initAnimation() +{ + AssertFatal( mModel, "GuiObjectView::_initAnimation - No model loaded!" ); + + if( mAnimationSeqName.isEmpty() && mAnimationSeq == -1 ) + return; + + // Look up sequence by name. + + if( !mAnimationSeqName.isEmpty() ) + { + mAnimationSeq = mModel->getShape()->findSequence( mAnimationSeqName ); + + if( mAnimationSeq == -1 ) + { + Con::errorf( "GuiObjectView::_initAnimation - Cannot find animation sequence '%s' on '%s'", + mAnimationSeqName.c_str(), + mModelName.c_str() + ); + + return; + } + } + + // Start sequence. + + if( mAnimationSeq != -1 ) + { + if( mAnimationSeq >= mModel->getShape()->sequences.size() ) + { + Con::errorf( "GuiObjectView::_initAnimation - Sequence '%i' out of range for model '%s'", + mAnimationSeq, + mModelName.c_str() + ); + + mAnimationSeq = -1; + return; + } + + if( !mRunThread ) + mRunThread = mModel->addThread(); + + mModel->setSequence( mRunThread, mAnimationSeq, 0.f ); + } + + mLastRenderTime = Platform::getVirtualMilliseconds(); +} + +//------------------------------------------------------------------------------ + +void GuiObjectView::_initMount() +{ + AssertFatal( mModel, "GuiObjectView::_initMount - No model loaded!" ); + + if( !mMountedModel ) + return; + + mMountTransform.identity(); + + // Look up the node to which to mount to. + + if( !mMountNodeName.isEmpty() ) + { + mMountNode = mModel->getShape()->findNode( mMountNodeName ); + if( mMountNode == -1 ) + { + Con::errorf( "GuiObjectView::_initMount - No node '%s' on '%s'", + mMountNodeName.c_str(), + mModelName.c_str() + ); + + return; + } + } + + // Make sure mount node is valid. + + if( mMountNode != -1 && mMountNode >= mModel->getShape()->nodes.size() ) + { + Con::errorf( "GuiObjectView::_initMount - Mount node index '%i' out of range for '%s'", + mMountNode, + mModelName.c_str() + ); + + mMountNode = -1; + return; + } + + // Look up node on the mounted model from + // which to mount to the primary model's node. + + S32 mountPoint = mMountedModel->getShape()->findNode( "mountPoint" ); + if( mountPoint != -1 ) + { + mMountedModel->getShape()->getNodeWorldTransform( mountPoint, &mMountTransform ), + mMountTransform.inverse(); + } +} + +//============================================================================= +// Console Methods. +//============================================================================= +// MARK: ---- Console Methods ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiObjectView, getModel, const char*, (),, + "@brief Return the model displayed in this view.\n\n" + "@tsexample\n" + "// Request the displayed model name from the GuiObjectView object.\n" + "%modelName = %thisGuiObjectView.getModel();\n" + "@endtsexample\n\n" + "@return Name of the displayed model.\n\n" + "@see GuiControl") +{ + return Con::getReturnBuffer( object->getModelName() ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiObjectView, setModel, void, (const char* shapeName),, + "@brief Sets the model to be displayed in this control.\n\n" + "@param shapeName Name of the model to display.\n" + "@tsexample\n" + "// Define the model we want to display\n" + "%shapeName = \"gideon.dts\";\n\n" + "// Tell the GuiObjectView object to display the defined model\n" + "%thisGuiObjectView.setModel(%shapeName);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->setObjectModel( shapeName ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiObjectView, getMountedModel, const char*, (),, + "@brief Return the name of the mounted model.\n\n" + "@tsexample\n" + "// Request the name of the mounted model from the GuiObjectView object\n" + "%mountedModelName = %thisGuiObjectView.getMountedModel();\n" + "@endtsexample\n\n" + "@return Name of the mounted model.\n\n" + "@see GuiControl") +{ + return Con::getReturnBuffer( object->getMountedModelName() ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiObjectView, setMountedModel, void, (const char* shapeName),, + "@brief Sets the model to be mounted on the primary model.\n\n" + "@param shapeName Name of the model to mount.\n" + "@tsexample\n" + "// Define the model name to mount\n" + "%modelToMount = \"GideonGlasses.dts\";\n\n" + "// Inform the GuiObjectView object to mount the defined model to the existing model in the control\n" + "%thisGuiObjectView.setMountedModel(%modelToMount);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->setObjectModel(shapeName); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiObjectView, getSkin, const char*, (),, + "@brief Return the name of skin used on the primary model.\n\n" + "@tsexample\n" + "// Request the name of the skin used on the primary model in the control\n" + "%skinName = %thisGuiObjectView.getSkin();\n" + "@endtsexample\n\n" + "@return Name of the skin used on the primary model.\n\n" + "@see GuiControl") +{ + return Con::getReturnBuffer( object->getSkin() ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiObjectView, setSkin, void, (const char* skinName),, + "@brief Sets the skin to use on the model being displayed.\n\n" + "@param skinName Name of the skin to use.\n" + "@tsexample\n" + "// Define the skin we want to apply to the main model in the control\n" + "%skinName = \"disco_gideon\";\n\n" + "// Inform the GuiObjectView control to update the skin the to defined skin\n" + "%thisGuiObjectView.setSkin(%skinName);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->setSkin( skinName ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiObjectView, getMountSkin, const char*, ( S32 param1, S32 param2),, + "@brief Return the name of skin used on the mounted model.\n\n" + "@tsexample\n" + "// Request the skin name from the model mounted on to the main model in the control\n" + "%mountModelSkin = %thisGuiObjectView.getMountSkin();\n" + "@endtsexample\n\n" + "@return Name of the skin used on the mounted model.\n\n" + "@see GuiControl") +{ + return Con::getReturnBuffer( object->getMountSkin() ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiObjectView, setMountSkin, void, (const char* skinName),, + "@brief Sets the skin to use on the mounted model.\n\n" + "@param skinName Name of the skin to set on the model mounted to the main model in the control\n" + "@tsexample\n" + "// Define the name of the skin\n" + "%skinName = \"BronzeGlasses\";\n\n" + "// Inform the GuiObjectView Control of the skin to use on the mounted model\n" + "%thisGuiObjectViewCtrl.setMountSkin(%skinName);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->setMountSkin(skinName); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiObjectView, setSeq, void, (const char* indexOrName),, + "@brief Sets the animation to play for the viewed object.\n\n" + "@param indexOrName The index or name of the animation to play.\n" + "@tsexample\n" + "// Set the animation index value, or animation sequence name.\n" + "%indexVal = \"3\";\n" + "//OR:\n" + "%indexVal = \"idle\";\n\n" + "// Inform the GuiObjectView object to set the animation sequence of the object in the control.\n" + "%thisGuiObjectVew.setSeq(%indexVal);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + if( dIsdigit( indexOrName[0] ) ) + object->setObjectAnimation( dAtoi( indexOrName ) ); + else + object->setObjectAnimation( indexOrName ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiObjectView, setMount, void, ( const char* shapeName, const char* mountNodeIndexOrName),, + "@brief Mounts the given model to the specified mount point of the primary model displayed in this control.\n\n" + "Detailed description\n\n" + "@param shapeName Name of the model to mount.\n" + "@param mountNodeIndexOrName Index or name of the mount point to be mounted to. If index, corresponds to \"mountN\" in your shape where N is the number passed here.\n" + "@tsexample\n" + "// Set the shapeName to mount\n" + "%shapeName = \"GideonGlasses.dts\"\n\n" + "// Set the mount node of the primary model in the control to mount the new shape at\n" + "%mountNodeIndexOrName = \"3\";\n" + "//OR:\n" + "%mountNodeIndexOrName = \"Face\";\n\n" + "// Inform the GuiObjectView object to mount the shape at the specified node.\n" + "%thisGuiObjectView.setMount(%shapeName,%mountNodeIndexOrName);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + if( dIsdigit( mountNodeIndexOrName[0] ) ) + object->setMountNode( dAtoi( mountNodeIndexOrName ) ); + else + object->setMountNode( mountNodeIndexOrName ); + + object->setMountedObject( shapeName ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiObjectView, getOrbitDistance, F32, (),, + "@brief Return the current distance at which the camera orbits the object.\n\n" + "@tsexample\n" + "// Request the current orbit distance\n" + "%orbitDistance = %thisGuiObjectView.getOrbitDistance();\n" + "@endtsexample\n\n" + "@return The distance at which the camera orbits the object.\n\n" + "@see GuiControl") +{ + return object->getOrbitDistance(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiObjectView, setOrbitDistance, void, (F32 distance),, + "@brief Sets the distance at which the camera orbits the object. Clamped to the acceptable range defined in the class by min and max orbit distances.\n\n" + "Detailed description\n\n" + "@param distance The distance to set the orbit to (will be clamped).\n" + "@tsexample\n" + "// Define the orbit distance value\n" + "%orbitDistance = \"1.5\";\n\n" + "// Inform the GuiObjectView object to set the orbit distance to the defined value\n" + "%thisGuiObjectView.setOrbitDistance(%orbitDistance);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->setOrbitDistance( distance ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiObjectView, getCameraSpeed, F32, (),, + "@brief Return the current multiplier for camera zooming and rotation.\n\n" + "@tsexample\n" + "// Request the current camera zooming and rotation multiplier value\n" + "%multiplier = %thisGuiObjectView.getCameraSpeed();\n" + "@endtsexample\n\n" + "@return Camera zooming / rotation multiplier value.\n\n" + "@see GuiControl") +{ + return object->getCameraSpeed(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiObjectView, setCameraSpeed, void, (F32 factor),, + "@brief Sets the multiplier for the camera rotation and zoom speed.\n\n" + "@param factor Multiplier for camera rotation and zoom speed.\n" + "@tsexample\n" + "// Set the factor value\n" + "%factor = \"0.75\";\n\n" + "// Inform the GuiObjectView object to set the camera speed.\n" + "%thisGuiObjectView.setCameraSpeed(%factor);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->setCameraSpeed( factor ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiObjectView, setLightColor, void, ( ColorF color),, + "@brief Set the light color on the sun object used to render the model.\n\n" + "@param color Color of sunlight.\n" + "@tsexample\n" + "// Set the color value for the sun\n" + "%color = \"1.0 0.4 0.5\";\n\n" + "// Inform the GuiObjectView object to change the sun color to the defined value\n" + "%thisGuiObjectView.setLightColor(%color);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->setLightColor( color ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiObjectView, setLightAmbient, void, (ColorF color),, + "@brief Set the light ambient color on the sun object used to render the model.\n\n" + "@param color Ambient color of sunlight.\n" + "@tsexample\n" + "// Define the sun ambient color value\n" + "%color = \"1.0 0.4 0.6\";\n\n" + "// Inform the GuiObjectView object to set the sun ambient color to the requested value\n" + "%thisGuiObjectView.setLightAmbient(%color);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->setLightAmbient( color ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiObjectView, setLightDirection, void, (Point3F direction),, + "@brief Set the light direction from which to light the model.\n\n" + "@param direction XYZ direction from which the light will shine on the model\n" + "@tsexample\n" + "// Set the light direction\n" + "%direction = \"1.0 0.2 0.4\"\n\n" + "// Inform the GuiObjectView object to change the light direction to the defined value\n" + "%thisGuiObjectView.setLightDirection(%direction);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->setLightDirection( direction ); +} diff --git a/Engine/source/T3D/guiObjectView.h b/Engine/source/T3D/guiObjectView.h new file mode 100644 index 000000000..6007483a7 --- /dev/null +++ b/Engine/source/T3D/guiObjectView.h @@ -0,0 +1,285 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIOBJECTVIEW_H_ +#define _GUIOBJECTVIEW_H_ + +#ifndef _GUITSCONTROL_H_ + #include "gui/3d/guiTSControl.h" +#endif +#ifndef _TSSHAPEINSTANCE_H_ + #include "ts/tsShapeInstance.h" +#endif + + +class LightInfo; + + +/// A control that displays a TSShape in its view. +class GuiObjectView : public GuiTSCtrl +{ + public: + + typedef GuiTSCtrl Parent; + + DECLARE_CALLBACK( void, onMouseEnter, ()); + DECLARE_CALLBACK( void, onMouseLeave, ()); + + protected: + + /// @name Mouse Control + /// @{ + + enum MouseState + { + None, + Rotating, + Zooming + }; + + /// Current mouse operation. + MouseState mMouseState; + + /// Last mouse position during tracked mouse operations. + Point2I mLastMousePoint; + + /// @} + + /// @name Model + /// @{ + + /// Name of the model loaded for display. + String mModelName; + + /// Model being displayed in the view. + TSShapeInstance* mModel; + + /// Name of skin to use on model. + String mSkinName; + + /// @} + + /// @name Camera State + /// @{ + + Point3F mCameraPos; + MatrixF mCameraMatrix; + EulerF mCameraRot; + Point3F mOrbitPos; + + F32 mMaxOrbitDist; + F32 mMinOrbitDist; + + /// + F32 mOrbitDist; + + /// Multiplier for camera mouse operations (rotation and zooming). + F32 mCameraSpeed; + + /// @} + + /// @name Mounting + /// @{ + + /// Name of model to mount to the primary model. + String mMountedModelName; + + /// + String mMountSkinName; + + /// Index of the node to mount the secondary model to. -1 (disabled) by default. + S32 mMountNode; + + /// Name of node to mount the secondary model to. Unset by default. + String mMountNodeName; + + /// Model mounted as an image to the primary model. + TSShapeInstance* mMountedModel; + + /// + MatrixF mMountTransform; + + /// @} + + /// @name Animation + /// @{ + + /// Index of the animation sequence to play on the model. -1 (disabled) by default. + S32 mAnimationSeq; + + /// Name of the animation sequence to play on the model. Unset by default. + String mAnimationSeqName; + + /// Animation thread on the model. + TSThread* mRunThread; + + /// Last time we rendered the model. Used for animation. + S32 mLastRenderTime; + + /// @} + + /// @name Lighting + /// @{ + + /// Light object used as sun during rendering. + LightInfo* mLight; + + /// + ColorF mLightColor; + + /// + ColorF mLightAmbient; + + /// + Point3F mLightDirection; + + /// @} + + /// + void _initAnimation(); + + /// + void _initMount(); + + /// + void onStaticModified( StringTableEntry slotName, const char* newValue ); + + public: + + GuiObjectView(); + ~GuiObjectView(); + + /// @name Model + /// @{ + + /// + const String& getModelName() const { return mModelName; } + + /// Return the instance of the model being rendered in the view. + TSShapeInstance* getModel() const { return mModel; } + + /// Return the name of the skin used on the primary model. + const String& getSkin() const { return mSkinName; } + + /// Set the skin to use on the primary model. + void setSkin( const String& name ); + + /// Set the model to show in this view. + void setObjectModel( const String& modelName ); + + /// @} + + /// @name Animation + /// @{ + + /// Set the animation sequence to play on the model. + /// @param seqIndex Index of sequence to play. + void setObjectAnimation( S32 seqIndex ); + + /// Set the animation sequence to play on the model. + /// @param seqIndex Name of sequence to play. + void setObjectAnimation( const String& sequenceName ); + + /// @} + + /// @name Mounting + /// @{ + + /// Return the model mounted to the current primary model; NULL if none. + TSShapeInstance* getMountedModel() const { return mMountedModel; } + + /// + const String& getMountedModelName() const { return mMountedModelName; } + + /// Return the name of the skin used on the mounted model. + const String& getMountSkin() const { return mMountSkinName; } + + /// Set the skin to use on the mounted model. + void setMountSkin( const String& name ); + + /// + void setMountNode( S32 index ); + + /// + void setMountNode( const String& nodeName ); + + /// + void setMountedObject( const String& modelName ); + + /// @} + + /// @name Camera + /// @{ + + /// Return the current camera speed multiplier. + F32 getCameraSpeed() const { return mCameraSpeed; } + + /// Set the multiplier to apply to camera rotation and zooming. + void setCameraSpeed( F32 factor ); + + /// + F32 getOrbitDistance() const { return mOrbitDist; } + + /// Sets the distance at which the camera orbits the object. Clamped to the + /// acceptable range defined in the class by min and max orbit distances. + /// + /// @param distance The distance to set the orbit to (will be clamped). + void setOrbitDistance( F32 distance ); + + /// @} + + /// @name Lighting + /// @{ + + /// + void setLightColor( const ColorF& color ); + + /// + void setLightAmbient( const ColorF& color ); + + /// + void setLightDirection( const Point3F& direction ); + + /// @} + + // GuiTsCtrl. + bool onWake(); + + void onMouseEnter( const GuiEvent& event ); + void onMouseLeave( const GuiEvent& event ); + void onMouseDown( const GuiEvent& event ); + void onMouseUp( const GuiEvent& event ); + void onMouseDragged( const GuiEvent& event ); + void onRightMouseDown( const GuiEvent& event ); + void onRightMouseUp( const GuiEvent& event ); + void onRightMouseDragged( const GuiEvent& event ); + + bool processCameraQuery( CameraQuery* query ); + void renderWorld( const RectI& updateRect ); + + static void initPersistFields(); + + DECLARE_CONOBJECT( GuiObjectView ); + DECLARE_DESCRIPTION( "A control that shows a TSShape model." ); +}; + +#endif // !_GUIOBJECTVIEW_H_ diff --git a/Engine/source/T3D/item.cpp b/Engine/source/T3D/item.cpp new file mode 100644 index 000000000..80f10a1ea --- /dev/null +++ b/Engine/source/T3D/item.cpp @@ -0,0 +1,1370 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/item.h" + +#include "core/stream/bitStream.h" +#include "math/mMath.h" +#include "console/console.h" +#include "console/consoleTypes.h" +#include "sim/netConnection.h" +#include "collision/boxConvex.h" +#include "collision/earlyOutPolyList.h" +#include "collision/extrudedPolyList.h" +#include "math/mPolyhedron.h" +#include "math/mathIO.h" +#include "lighting/lightInfo.h" +#include "lighting/lightManager.h" +#include "T3D/physics/physicsPlugin.h" +#include "T3D/physics/physicsBody.h" +#include "T3D/physics/physicsCollision.h" +#include "ts/tsShapeInstance.h" +#include "console/engineAPI.h" + + +const F32 sRotationSpeed = 6.0f; // Secs/Rotation +const F32 sAtRestVelocity = 0.15f; // Min speed after collision +const S32 sCollisionTimeout = 15; // Timout value in ticks + +// Client prediction +static F32 sMinWarpTicks = 0.5 ; // Fraction of tick at which instant warp occures +static S32 sMaxWarpTicks = 3; // Max warp duration in ticks + +F32 Item::mGravity = -20.0f; + +const U32 sClientCollisionMask = (TerrainObjectType | + InteriorObjectType | StaticShapeObjectType | + VehicleObjectType | PlayerObjectType); + +const U32 sServerCollisionMask = (sClientCollisionMask); + +const S32 Item::csmAtRestTimer = 64; + +//---------------------------------------------------------------------------- + +IMPLEMENT_CO_DATABLOCK_V1(ItemData); + +ConsoleDocClass( ItemData, + "@brief Stores properties for an individual Item type.\n\n" + + "Items represent an object in the world, usually one that the player will interact with. " + "One example is a health kit on the group that is automatically picked up when the player " + "comes into contact with it.\n\n" + + "ItemData provides the common properties for a set of Items. These properties include a " + "DTS or DAE model used to render the Item in the world, its physical properties for when the " + "Item interacts with the world (such as being tossed by the player), and any lights that emit " + "from the Item.\n\n" + + "@tsexample\n" + "datablock ItemData(HealthKitSmall)\n" + "{\n" + " category =\"Health\";\n" + " className = \"HealthPatch\";\n" + " shapeFile = \"art/shapes/items/kit/healthkit.dts\";\n" + " gravityMod = \"1.0\";\n" + " mass = 2;\n" + " friction = 1;\n" + " elasticity = 0.3;\n" + " density = 2;\n" + " drag = 0.5;\n" + " maxVelocity = \"10.0\";\n" + " emap = true;\n" + " sticky = false;\n" + " dynamicType = \"0\"\n;" + " lightOnlyStatic = false;\n" + " lightType = \"NoLight\";\n" + " lightColor = \"1.0 1.0 1.0 1.0\";\n" + " lightTime = 1000;\n" + " lightRadius = 10.0;\n" + " simpleServerCollision = true;" + " // Dynamic properties used by the scripts\n\n" + " pickupName = \"a small health kit\";\n" + " repairAmount = 50;\n" + "};\n" + "@endtsexample\n" + + "@ingroup gameObjects\n" +); + + +ItemData::ItemData() +{ + shadowEnable = true; + + + friction = 0; + elasticity = 0; + + sticky = false; + gravityMod = 1.0; + maxVelocity = 25.0f; + + density = 2; + drag = 0.5; + + lightOnlyStatic = false; + lightType = Item::NoLight; + lightColor.set(1.f,1.f,1.f,1.f); + lightTime = 1000; + lightRadius = 10.f; + + simpleServerCollision = true; +} + +ImplementEnumType( ItemLightType, + "@brief The type of light the Item has\n\n" + "@ingroup gameObjects\n\n") + { Item::NoLight, "NoLight", "The item has no light attached.\n" }, + { Item::ConstantLight, "ConstantLight", "The item has a constantly emitting light attached.\n" }, + { Item::PulsingLight, "PulsingLight", "The item has a pulsing light attached.\n" } +EndImplementEnumType; + +void ItemData::initPersistFields() +{ + addField("friction", TypeF32, Offset(friction, ItemData), "A floating-point value specifying how much velocity is lost to impact and sliding friction."); + addField("elasticity", TypeF32, Offset(elasticity, ItemData), "A floating-point value specifying how 'bouncy' this ItemData is."); + addField("sticky", TypeBool, Offset(sticky, ItemData), + "@brief If true, ItemData will 'stick' to any surface it collides with.\n\n" + "When an item does stick to a surface, the Item::onStickyCollision() callback is called. The Item has methods to retrieve " + "the world position and normal the Item is stuck to.\n" + "@note Valid objects to stick to must be of StaticShapeObjectType.\n"); + addField("gravityMod", TypeF32, Offset(gravityMod, ItemData), "Floating point value to multiply the existing gravity with, just for this ItemData."); + addField("maxVelocity", TypeF32, Offset(maxVelocity, ItemData), "Maximum velocity that this ItemData is able to move."); + + addField("lightType", TYPEID< Item::LightType >(), Offset(lightType, ItemData), "Type of light to apply to this ItemData. Options are NoLight, ConstantLight, PulsingLight. Default is NoLight." ); + addField("lightColor", TypeColorF, Offset(lightColor, ItemData), + "@brief Color value to make this light. Example: \"1.0,1.0,1.0\"\n\n" + "@see lightType\n"); + addField("lightTime", TypeS32, Offset(lightTime, ItemData), + "@brief Time value for the light of this ItemData, used to control the pulse speed of the PulsingLight LightType.\n\n" + "@see lightType\n"); + addField("lightRadius", TypeF32, Offset(lightRadius, ItemData), + "@brief Distance from the center point of this ItemData for the light to affect\n\n" + "@see lightType\n"); + addField("lightOnlyStatic", TypeBool, Offset(lightOnlyStatic, ItemData), + "@brief If true, this ItemData will only cast a light if the Item for this ItemData has a static value of true.\n\n" + "@see lightType\n"); + + addField("simpleServerCollision", TypeBool, Offset(simpleServerCollision, ItemData), + "@brief Determines if only simple server-side collision will be used (for pick ups).\n\n" + "If set to true then only simple, server-side collision detection will be used. This is often the case " + "if the item is used for a pick up object, such as ammo. If set to false then a full collision volume " + "will be used as defined by the shape. The default is true.\n" + "@note Only applies when using a physics library.\n" + "@see TurretShape and ProximityMine for examples that should set this to false to allow them to be " + "shot by projectiles.\n"); + + Parent::initPersistFields(); +} + +void ItemData::packData(BitStream* stream) +{ + Parent::packData(stream); + stream->writeFloat(friction, 10); + stream->writeFloat(elasticity, 10); + stream->writeFlag(sticky); + if(stream->writeFlag(gravityMod != 1.0)) + stream->writeFloat(gravityMod, 10); + if(stream->writeFlag(maxVelocity != -1)) + stream->write(maxVelocity); + + if(stream->writeFlag(lightType != Item::NoLight)) + { + AssertFatal(Item::NumLightTypes < (1 << 2), "ItemData: light type needs more bits"); + stream->writeInt(lightType, 2); + stream->writeFloat(lightColor.red, 7); + stream->writeFloat(lightColor.green, 7); + stream->writeFloat(lightColor.blue, 7); + stream->writeFloat(lightColor.alpha, 7); + stream->write(lightTime); + stream->write(lightRadius); + stream->writeFlag(lightOnlyStatic); + } + + stream->writeFlag(simpleServerCollision); +} + +void ItemData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + friction = stream->readFloat(10); + elasticity = stream->readFloat(10); + sticky = stream->readFlag(); + if(stream->readFlag()) + gravityMod = stream->readFloat(10); + else + gravityMod = 1.0; + + if(stream->readFlag()) + stream->read(&maxVelocity); + else + maxVelocity = -1; + + if(stream->readFlag()) + { + lightType = stream->readInt(2); + lightColor.red = stream->readFloat(7); + lightColor.green = stream->readFloat(7); + lightColor.blue = stream->readFloat(7); + lightColor.alpha = stream->readFloat(7); + stream->read(&lightTime); + stream->read(&lightRadius); + lightOnlyStatic = stream->readFlag(); + } + else + lightType = Item::NoLight; + + simpleServerCollision = stream->readFlag(); +} + + +//---------------------------------------------------------------------------- + +IMPLEMENT_CO_NETOBJECT_V1(Item); + +ConsoleDocClass( Item, + "@brief Base Item class. Uses the ItemData datablock for common properties.\n\n" + + "Items represent an object in the world, usually one that the player will interact with. " + "One example is a health kit on the group that is automatically picked up when the player " + "comes into contact with it.\n\n" + + "@tsexample\n" + "// This is the \"health patch\" dropped by a dying player.\n" + "datablock ItemData(HealthKitPatch)\n" + "{\n" + " // Mission editor category, this datablock will show up in the\n" + " // specified category under the \"shapes\" root category.\n" + " category = \"Health\";\n\n" + " className = \"HealthPatch\";\n\n" + " // Basic Item properties\n" + " shapeFile = \"art/shapes/items/patch/healthpatch.dts\";\n" + " mass = 2;\n" + " friction = 1;\n" + " elasticity = 0.3;\n" + " emap = true;\n\n" + " // Dynamic properties used by the scripts\n" + " pickupName = \"a health patch\";\n" + " repairAmount = 50;\n" + "};\n\n" + + "%obj = new Item()\n" + "{\n" + " dataBlock = HealthKitSmall;\n" + " parentGroup = EWCreatorWindow.objectGroup;\n" + " static = true;\n" + " rotate = true;\n" + "};\n" + "@endtsexample\n\n" + + "@see ItemData\n" + + "@ingroup gameObjects\n" +); + +IMPLEMENT_CALLBACK( Item, onStickyCollision, void, ( const char* objID ),( objID ), + "@brief Informs the Item object that it is now sticking to another object.\n\n" + "This callback is only called if the ItemData::sticky property for this Item is true.\n" + "@param objID Object ID this Item object.\n" + "@note Server side only.\n" + "@see Item, ItemData\n" +); + +IMPLEMENT_CALLBACK( Item, onEnterLiquid, void, ( const char* objID, const char* waterCoverage, const char* liquidType ),( objID, waterCoverage, liquidType ), + "Informs an Item object that it has entered liquid, along with information about the liquid type.\n" + "@param objID Object ID for this Item object.\n" + "@param waterCoverage How much coverage of water this Item object has.\n" + "@param liquidType The type of liquid that this Item object has entered.\n" + "@note Server side only.\n" + "@see Item, ItemData, WaterObject\n" +); + +IMPLEMENT_CALLBACK( Item, onLeaveLiquid, void, ( const char* objID, const char* liquidType ),( objID, liquidType ), + "Informs an Item object that it has left a liquid, along with information about the liquid type.\n" + "@param objID Object ID for this Item object.\n" + "@param liquidType The type of liquid that this Item object has left.\n" + "@note Server side only.\n" + "@see Item, ItemData, WaterObject\n" +); + + +Item::Item() +{ + mTypeMask |= ItemObjectType | DynamicShapeObjectType; + mDataBlock = 0; + mStatic = false; + mRotate = false; + mVelocity = VectorF(0,0,0); + mAtRest = true; + mAtRestCounter = 0; + mInLiquid = false; + delta.warpTicks = 0; + delta.dt = 1; + mCollisionObject = 0; + mCollisionTimeout = 0; + mPhysicsRep = NULL; + + mConvex.init(this); + mWorkingQueryBox.minExtents.set(-1e9, -1e9, -1e9); + mWorkingQueryBox.maxExtents.set(-1e9, -1e9, -1e9); + + mLight = NULL; + + mSubclassItemHandlesScene = false; +} + +Item::~Item() +{ + SAFE_DELETE(mLight); +} + + +//---------------------------------------------------------------------------- + +bool Item::onAdd() +{ + if (!Parent::onAdd() || !mDataBlock) + return false; + + if (mStatic) + mAtRest = true; + mObjToWorld.getColumn(3,&delta.pos); + + // Setup the box for our convex object... + mObjBox.getCenter(&mConvex.mCenter); + mConvex.mSize.x = mObjBox.len_x() / 2.0; + mConvex.mSize.y = mObjBox.len_y() / 2.0; + mConvex.mSize.z = mObjBox.len_z() / 2.0; + mWorkingQueryBox.minExtents.set(-1e9, -1e9, -1e9); + mWorkingQueryBox.maxExtents.set(-1e9, -1e9, -1e9); + + if( !isHidden() && !mSubclassItemHandlesScene ) + addToScene(); + + if (isServerObject()) + { + if (!mSubclassItemHandlesScene) + scriptOnAdd(); + } + else if (mDataBlock->lightType != NoLight) + { + mDropTime = Sim::getCurrentTime(); + } + + _updatePhysics(); + + return true; +} + +void Item::_updatePhysics() +{ + SAFE_DELETE( mPhysicsRep ); + + if ( !PHYSICSMGR ) + return; + + if (mDataBlock->simpleServerCollision) + { + // We only need the trigger on the server. + if ( isServerObject() ) + { + PhysicsCollision *colShape = PHYSICSMGR->createCollision(); + colShape->addBox( mObjBox.getExtents() * 0.5f, MatrixF::Identity ); + + PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" ); + mPhysicsRep = PHYSICSMGR->createBody(); + mPhysicsRep->init( colShape, 0, PhysicsBody::BF_TRIGGER | PhysicsBody::BF_KINEMATIC, this, world ); + mPhysicsRep->setTransform( getTransform() ); + } + } + else + { + if ( !mShapeInstance ) + return; + + PhysicsCollision* colShape = mShapeInstance->getShape()->buildColShape( false, getScale() ); + + if ( colShape ) + { + PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" ); + mPhysicsRep = PHYSICSMGR->createBody(); + mPhysicsRep->init( colShape, 0, PhysicsBody::BF_KINEMATIC, this, world ); + mPhysicsRep->setTransform( getTransform() ); + } + } +} + +bool Item::onNewDataBlock( GameBaseData *dptr, bool reload ) +{ + mDataBlock = dynamic_cast(dptr); + if (!mDataBlock || !Parent::onNewDataBlock(dptr,reload)) + return false; + + if (!mSubclassItemHandlesScene) + scriptOnNewDataBlock(); + + if ( isProperlyAdded() ) + _updatePhysics(); + + return true; +} + +void Item::onRemove() +{ + mWorkingQueryBox.minExtents.set(-1e9, -1e9, -1e9); + mWorkingQueryBox.maxExtents.set(-1e9, -1e9, -1e9); + + SAFE_DELETE( mPhysicsRep ); + + if (!mSubclassItemHandlesScene) + { + scriptOnRemove(); + removeFromScene(); + } + + Parent::onRemove(); +} + +void Item::onDeleteNotify( SimObject *obj ) +{ + if ( obj == mCollisionObject ) + { + mCollisionObject = NULL; + mCollisionTimeout = 0; + } + + Parent::onDeleteNotify( obj ); +} + +// Lighting: ----------------------------------------------------------------- + +void Item::registerLights(LightManager * lightManager, bool lightingScene) +{ + if(lightingScene) + return; + + if(mDataBlock->lightOnlyStatic && !mStatic) + return; + + F32 intensity; + switch(mDataBlock->lightType) + { + case ConstantLight: + intensity = mFadeVal; + break; + + case PulsingLight: + { + S32 delta = Sim::getCurrentTime() - mDropTime; + intensity = 0.5f + 0.5f * mSin(M_PI_F * F32(delta) / F32(mDataBlock->lightTime)); + intensity = 0.15f + intensity * 0.85f; + intensity *= mFadeVal; // fade out light on flags + break; + } + + default: + return; + } + + // Create a light if needed + if (!mLight) + { + mLight = lightManager->createLightInfo(); + } + mLight->setColor( mDataBlock->lightColor * intensity ); + mLight->setType( LightInfo::Point ); + mLight->setRange( mDataBlock->lightRadius ); + mLight->setPosition( getBoxCenter() ); + + lightManager->registerGlobalLight( mLight, this ); +} + + +//---------------------------------------------------------------------------- + +Point3F Item::getVelocity() const +{ + return mVelocity; +} + +void Item::setVelocity(const VectorF& vel) +{ + mVelocity = vel; + + // Clamp against the maximum velocity. + if ( mDataBlock->maxVelocity > 0 ) + { + F32 len = mVelocity.magnitudeSafe(); + if ( len > mDataBlock->maxVelocity ) + { + Point3F excess = mVelocity * ( 1.0f - (mDataBlock->maxVelocity / len ) ); + mVelocity -= excess; + } + } + + setMaskBits(PositionMask); + mAtRest = false; + mAtRestCounter = 0; +} + +void Item::applyImpulse(const Point3F&,const VectorF& vec) +{ + // Items ignore angular velocity + VectorF vel; + vel.x = vec.x / mDataBlock->mass; + vel.y = vec.y / mDataBlock->mass; + vel.z = vec.z / mDataBlock->mass; + setVelocity(vel); +} + +void Item::setCollisionTimeout(ShapeBase* obj) +{ + if (mCollisionObject) + clearNotify(mCollisionObject); + deleteNotify(obj); + mCollisionObject = obj; + mCollisionTimeout = sCollisionTimeout; + setMaskBits(ThrowSrcMask); +} + + +//---------------------------------------------------------------------------- + +void Item::processTick(const Move* move) +{ + Parent::processTick(move); + + // + if (mCollisionObject && !--mCollisionTimeout) + mCollisionObject = 0; + + // Warp to catch up to server + if (delta.warpTicks > 0) + { + delta.warpTicks--; + + // Set new pos. + MatrixF mat = mObjToWorld; + mat.getColumn(3,&delta.pos); + delta.pos += delta.warpOffset; + mat.setColumn(3,delta.pos); + Parent::setTransform(mat); + + // Backstepping + delta.posVec.x = -delta.warpOffset.x; + delta.posVec.y = -delta.warpOffset.y; + delta.posVec.z = -delta.warpOffset.z; + } + else + { + if (isServerObject() && mAtRest && (mStatic == false && mDataBlock->sticky == false)) + { + if (++mAtRestCounter > csmAtRestTimer) + { + mAtRest = false; + mAtRestCounter = 0; + setMaskBits(PositionMask); + } + } + + if (!mStatic && !mAtRest && isHidden() == false) + { + updateVelocity(TickSec); + updateWorkingCollisionSet(isGhost() ? sClientCollisionMask : sServerCollisionMask, TickSec); + updatePos(isGhost() ? sClientCollisionMask : sServerCollisionMask, TickSec); + } + else + { + // Need to clear out last updatePos or warp interpolation + delta.posVec.set(0,0,0); + } + } +} + +void Item::interpolateTick(F32 dt) +{ + Parent::interpolateTick(dt); + + // Client side interpolation + Point3F pos = delta.pos + delta.posVec * dt; + MatrixF mat = mRenderObjToWorld; + mat.setColumn(3,pos); + setRenderTransform(mat); + delta.dt = dt; +} + + +//---------------------------------------------------------------------------- + +void Item::setTransform(const MatrixF& mat) +{ + Point3F pos; + mat.getColumn(3,&pos); + MatrixF tmat; + if (!mRotate) { + // Forces all rotation to be around the z axis + VectorF vec; + mat.getColumn(1,&vec); + tmat.set(EulerF(0,0,-mAtan2(-vec.x,vec.y))); + } + else + tmat.identity(); + tmat.setColumn(3,pos); + Parent::setTransform(tmat); + if (!mStatic) + { + mAtRest = false; + mAtRestCounter = 0; + } + + if ( mPhysicsRep ) + mPhysicsRep->setTransform( getTransform() ); + + setMaskBits(RotationMask | PositionMask | NoWarpMask); +} + + +//---------------------------------------------------------------------------- +void Item::updateWorkingCollisionSet(const U32 mask, const F32 dt) +{ + // It is assumed that we will never accelerate more than 10 m/s for gravity... + // + Point3F scaledVelocity = mVelocity * dt; + F32 len = scaledVelocity.len(); + F32 newLen = len + (10 * dt); + + // Check to see if it is actually necessary to construct the new working list, + // or if we can use the cached version from the last query. We use the x + // component of the min member of the mWorkingQueryBox, which is lame, but + // it works ok. + bool updateSet = false; + + Box3F convexBox = mConvex.getBoundingBox(getTransform(), getScale()); + F32 l = (newLen * 1.1) + 0.1; // from Convex::updateWorkingList + convexBox.minExtents -= Point3F(l, l, l); + convexBox.maxExtents += Point3F(l, l, l); + + // Check containment + { + if (mWorkingQueryBox.minExtents.x != -1e9) + { + if (mWorkingQueryBox.isContained(convexBox) == false) + { + // Needed region is outside the cached region. Update it. + updateSet = true; + } + else + { + // We can leave it alone, we're still inside the cached region + } + } + else + { + // Must update + updateSet = true; + } + } + + // Actually perform the query, if necessary + if (updateSet == true) + { + mWorkingQueryBox = convexBox; + mWorkingQueryBox.minExtents -= Point3F(2 * l, 2 * l, 2 * l); + mWorkingQueryBox.maxExtents += Point3F(2 * l, 2 * l, 2 * l); + + disableCollision(); + if (mCollisionObject) + mCollisionObject->disableCollision(); + + mConvex.updateWorkingList(mWorkingQueryBox, mask); + + if (mCollisionObject) + mCollisionObject->enableCollision(); + enableCollision(); + } +} + +void Item::updateVelocity(const F32 dt) +{ + // Acceleration due to gravity + mVelocity.z += (mGravity * mDataBlock->gravityMod) * dt; + F32 len; + if (mDataBlock->maxVelocity > 0 && (len = mVelocity.len()) > (mDataBlock->maxVelocity * 1.05)) { + Point3F excess = mVelocity * (1.0 - (mDataBlock->maxVelocity / len )); + excess *= 0.1f; + mVelocity -= excess; + } + + // Container buoyancy & drag + mVelocity.z -= mBuoyancy * (mGravity * mDataBlock->gravityMod * mGravityMod) * dt; + mVelocity -= mVelocity * mDrag * dt; +} + + +void Item::updatePos(const U32 /*mask*/, const F32 dt) +{ + // Try and move + Point3F pos; + mObjToWorld.getColumn(3,&pos); + delta.posVec = pos; + + bool contact = false; + bool nonStatic = false; + bool stickyNotify = false; + CollisionList collisionList; + F32 time = dt; + + static Polyhedron sBoxPolyhedron; + static ExtrudedPolyList sExtrudedPolyList; + static EarlyOutPolyList sEarlyOutPolyList; + MatrixF collisionMatrix(true); + Point3F end = pos + mVelocity * time; + U32 mask = isServerObject() ? sServerCollisionMask : sClientCollisionMask; + + // Part of our speed problem here is that we don't track contact surfaces, like we do + // with the player. In order to handle the most common and performance impacting + // instance of this problem, we'll use a ray cast to detect any contact surfaces below + // us. This won't be perfect, but it only needs to catch a few of these to make a + // big difference. We'll cast from the top center of the bounding box at the tick's + // beginning to the bottom center of the box at the end. + Point3F startCast((mObjBox.minExtents.x + mObjBox.maxExtents.x) * 0.5, + (mObjBox.minExtents.y + mObjBox.maxExtents.y) * 0.5, + mObjBox.maxExtents.z); + Point3F endCast((mObjBox.minExtents.x + mObjBox.maxExtents.x) * 0.5, + (mObjBox.minExtents.y + mObjBox.maxExtents.y) * 0.5, + mObjBox.minExtents.z); + collisionMatrix.setColumn(3, pos); + collisionMatrix.mulP(startCast); + collisionMatrix.setColumn(3, end); + collisionMatrix.mulP(endCast); + RayInfo rinfo; + bool doToughCollision = true; + disableCollision(); + if (mCollisionObject) + mCollisionObject->disableCollision(); + if (getContainer()->castRay(startCast, endCast, mask, &rinfo)) + { + F32 bd = -mDot(mVelocity, rinfo.normal); + + if (bd >= 0.0) + { + // Contact! + if (mDataBlock->sticky && rinfo.object->getTypeMask() & (STATIC_COLLISION_TYPEMASK)) { + mVelocity.set(0, 0, 0); + mAtRest = true; + mAtRestCounter = 0; + stickyNotify = true; + mStickyCollisionPos = rinfo.point; + mStickyCollisionNormal = rinfo.normal; + doToughCollision = false;; + } else { + // Subtract out velocity into surface and friction + VectorF fv = mVelocity + rinfo.normal * bd; + F32 fvl = fv.len(); + if (fvl) { + F32 ff = bd * mDataBlock->friction; + if (ff < fvl) { + fv *= ff / fvl; + fvl = ff; + } + } + bd *= 1 + mDataBlock->elasticity; + VectorF dv = rinfo.normal * (bd + 0.002); + mVelocity += dv; + mVelocity -= fv; + + // Keep track of what we hit + contact = true; + U32 typeMask = rinfo.object->getTypeMask(); + if (!(typeMask & StaticObjectType)) + nonStatic = true; + if (isServerObject() && (typeMask & ShapeBaseObjectType)) { + ShapeBase* col = static_cast(rinfo.object); + queueCollision(col,mVelocity - col->getVelocity()); + } + } + } + } + enableCollision(); + if (mCollisionObject) + mCollisionObject->enableCollision(); + + if (doToughCollision) + { + U32 count; + for (count = 0; count < 3; count++) + { + // Build list from convex states here... + end = pos + mVelocity * time; + + + collisionMatrix.setColumn(3, end); + Box3F wBox = getObjBox(); + collisionMatrix.mul(wBox); + Box3F testBox = wBox; + Point3F oldMin = testBox.minExtents; + Point3F oldMax = testBox.maxExtents; + testBox.minExtents.setMin(oldMin + (mVelocity * time)); + testBox.maxExtents.setMin(oldMax + (mVelocity * time)); + + sEarlyOutPolyList.clear(); + sEarlyOutPolyList.mNormal.set(0,0,0); + sEarlyOutPolyList.mPlaneList.setSize(6); + sEarlyOutPolyList.mPlaneList[0].set(wBox.minExtents,VectorF(-1,0,0)); + sEarlyOutPolyList.mPlaneList[1].set(wBox.maxExtents,VectorF(0,1,0)); + sEarlyOutPolyList.mPlaneList[2].set(wBox.maxExtents,VectorF(1,0,0)); + sEarlyOutPolyList.mPlaneList[3].set(wBox.minExtents,VectorF(0,-1,0)); + sEarlyOutPolyList.mPlaneList[4].set(wBox.minExtents,VectorF(0,0,-1)); + sEarlyOutPolyList.mPlaneList[5].set(wBox.maxExtents,VectorF(0,0,1)); + + CollisionWorkingList& eorList = mConvex.getWorkingList(); + CollisionWorkingList* eopList = eorList.wLink.mNext; + while (eopList != &eorList) { + if ((eopList->mConvex->getObject()->getTypeMask() & mask) != 0) + { + Box3F convexBox = eopList->mConvex->getBoundingBox(); + if (testBox.isOverlapped(convexBox)) + { + eopList->mConvex->getPolyList(&sEarlyOutPolyList); + if (sEarlyOutPolyList.isEmpty() == false) + break; + } + } + eopList = eopList->wLink.mNext; + } + if (sEarlyOutPolyList.isEmpty()) + { + pos = end; + break; + } + + collisionMatrix.setColumn(3, pos); + sBoxPolyhedron.buildBox(collisionMatrix, mObjBox, true); + + // Build extruded polyList... + VectorF vector = end - pos; + sExtrudedPolyList.extrude(sBoxPolyhedron, vector); + sExtrudedPolyList.setVelocity(mVelocity); + sExtrudedPolyList.setCollisionList(&collisionList); + + CollisionWorkingList& rList = mConvex.getWorkingList(); + CollisionWorkingList* pList = rList.wLink.mNext; + while (pList != &rList) { + if ((pList->mConvex->getObject()->getTypeMask() & mask) != 0) + { + Box3F convexBox = pList->mConvex->getBoundingBox(); + if (testBox.isOverlapped(convexBox)) + { + pList->mConvex->getPolyList(&sExtrudedPolyList); + } + } + pList = pList->wLink.mNext; + } + + if (collisionList.getTime() < 1.0) + { + // Set to collision point + F32 dt = time * collisionList.getTime(); + pos += mVelocity * dt; + time -= dt; + + // Pick the most resistant surface + F32 bd = 0; + const Collision* collision = 0; + for (int c = 0; c < collisionList.getCount(); c++) { + const Collision &cp = collisionList[c]; + F32 dot = -mDot(mVelocity,cp.normal); + if (dot > bd) { + bd = dot; + collision = &cp; + } + } + + if (collision && mDataBlock->sticky && collision->object->getTypeMask() & (STATIC_COLLISION_TYPEMASK)) { + mVelocity.set(0, 0, 0); + mAtRest = true; + mAtRestCounter = 0; + stickyNotify = true; + mStickyCollisionPos = collision->point; + mStickyCollisionNormal = collision->normal; + break; + } else { + // Subtract out velocity into surface and friction + if (collision) { + VectorF fv = mVelocity + collision->normal * bd; + F32 fvl = fv.len(); + if (fvl) { + F32 ff = bd * mDataBlock->friction; + if (ff < fvl) { + fv *= ff / fvl; + fvl = ff; + } + } + bd *= 1 + mDataBlock->elasticity; + VectorF dv = collision->normal * (bd + 0.002); + mVelocity += dv; + mVelocity -= fv; + + // Keep track of what we hit + contact = true; + U32 typeMask = collision->object->getTypeMask(); + if (!(typeMask & StaticObjectType)) + nonStatic = true; + if (isServerObject() && (typeMask & ShapeBaseObjectType)) { + ShapeBase* col = static_cast(collision->object); + queueCollision(col,mVelocity - col->getVelocity()); + } + } + } + } + else + { + pos = end; + break; + } + } + if (count == 3) + { + // Couldn't move... + mVelocity.set(0, 0, 0); + } + } + + // If on the client, calculate delta for backstepping + if (isGhost()) { + delta.pos = pos; + delta.posVec -= pos; + delta.dt = 1; + } + + // Update transform + MatrixF mat = mObjToWorld; + mat.setColumn(3,pos); + Parent::setTransform(mat); + enableCollision(); + if (mCollisionObject) + mCollisionObject->enableCollision(); + updateContainer(); + + if ( mPhysicsRep ) + mPhysicsRep->setTransform( mat ); + + // + if (contact) { + // Check for rest condition + if (!nonStatic && mVelocity.len() < sAtRestVelocity) { + mVelocity.x = mVelocity.y = mVelocity.z = 0; + mAtRest = true; + mAtRestCounter = 0; + } + + // Only update the client if we hit a non-static shape or + // if this is our final rest pos. + if (nonStatic || mAtRest) + setMaskBits(PositionMask); + } + + // Collision callbacks. These need to be processed whether we hit + // anything or not. + if (!isGhost()) + { + SimObjectPtr safePtr(this); + if (stickyNotify) + { + notifyCollision(); + if(bool(safePtr)) + onStickyCollision_callback( getIdString() ); + } + else + notifyCollision(); + + // water + if(bool(safePtr)) + { + if(!mInLiquid && mWaterCoverage != 0.0f) + { + onEnterLiquid_callback( getIdString(), Con::getFloatArg(mWaterCoverage), mLiquidType.c_str() ); + mInLiquid = true; + } + else if(mInLiquid && mWaterCoverage == 0.0f) + { + onLeaveLiquid_callback(getIdString(), mLiquidType.c_str()); + mInLiquid = false; + } + } + } +} + + +//---------------------------------------------------------------------------- + +static MatrixF IMat(1); + +bool Item::buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F&, const SphereF&) +{ + if ( context == PLC_Decal ) + return false; + + // Collision with the item is always against the item's object + // space bounding box axis aligned in world space. + Point3F pos; + mObjToWorld.getColumn(3,&pos); + IMat.setColumn(3,pos); + polyList->setTransform(&IMat, mObjScale); + polyList->setObject(this); + polyList->addBox(mObjBox); + return true; +} + + +//---------------------------------------------------------------------------- + +U32 Item::packUpdate(NetConnection *connection, U32 mask, BitStream *stream) +{ + U32 retMask = Parent::packUpdate(connection,mask,stream); + + if (stream->writeFlag(mask & InitialUpdateMask)) { + stream->writeFlag(mRotate); + stream->writeFlag(mStatic); + if (stream->writeFlag(getScale() != Point3F(1, 1, 1))) + mathWrite(*stream, getScale()); + } + + if (mask & ThrowSrcMask && mCollisionObject) { + S32 gIndex = connection->getGhostIndex(mCollisionObject); + if (stream->writeFlag(gIndex != -1)) + stream->writeInt(gIndex,NetConnection::GhostIdBitSize); + } + else + stream->writeFlag(false); + + if (stream->writeFlag(mask & RotationMask && !mRotate)) { + // Assumes rotation is about the Z axis + AngAxisF aa(mObjToWorld); + stream->writeFlag(aa.axis.z < 0); + stream->write(aa.angle); + } + + if (stream->writeFlag(mask & PositionMask)) { + Point3F pos; + mObjToWorld.getColumn(3,&pos); + mathWrite(*stream, pos); + if (!stream->writeFlag(mAtRest)) { + mathWrite(*stream, mVelocity); + } + stream->writeFlag(!(mask & NoWarpMask)); + } + return retMask; +} + +void Item::unpackUpdate(NetConnection *connection, BitStream *stream) +{ + Parent::unpackUpdate(connection,stream); + + // InitialUpdateMask + if (stream->readFlag()) { + mRotate = stream->readFlag(); + mStatic = stream->readFlag(); + if (stream->readFlag()) + mathRead(*stream, &mObjScale); + else + mObjScale.set(1, 1, 1); + } + + // ThrowSrcMask && mCollisionObject + if (stream->readFlag()) { + S32 gIndex = stream->readInt(NetConnection::GhostIdBitSize); + setCollisionTimeout(static_cast(connection->resolveGhost(gIndex))); + } + + MatrixF mat = mObjToWorld; + + // RotationMask && !mRotate + if (stream->readFlag()) { + // Assumes rotation is about the Z axis + AngAxisF aa; + aa.axis.set(0.0f, 0.0f, stream->readFlag() ? -1.0f : 1.0f); + stream->read(&aa.angle); + aa.setMatrix(&mat); + Point3F pos; + mObjToWorld.getColumn(3,&pos); + mat.setColumn(3,pos); + } + + // PositionMask + if (stream->readFlag()) { + Point3F pos; + mathRead(*stream, &pos); + F32 speed = mVelocity.len(); + if ((mAtRest = stream->readFlag()) == true) + mVelocity.set(0.0f, 0.0f, 0.0f); + else + mathRead(*stream, &mVelocity); + + if (stream->readFlag() && isProperlyAdded()) { + // Determin number of ticks to warp based on the average + // of the client and server velocities. + delta.warpOffset = pos - delta.pos; + F32 as = (speed + mVelocity.len()) * 0.5f * TickSec; + F32 dt = (as > 0.00001f) ? delta.warpOffset.len() / as: sMaxWarpTicks; + delta.warpTicks = (S32)((dt > sMinWarpTicks)? getMax(mFloor(dt + 0.5f), 1.0f): 0.0f); + + if (delta.warpTicks) + { + // Setup the warp to start on the next tick, only the + // object's position is warped. + if (delta.warpTicks > sMaxWarpTicks) + delta.warpTicks = sMaxWarpTicks; + delta.warpOffset /= (F32)delta.warpTicks; + } + else { + // Going to skip the warp, server and client are real close. + // Adjust the frame interpolation to move smoothly to the + // new position within the current tick. + Point3F cp = delta.pos + delta.posVec * delta.dt; + VectorF vec = delta.pos - cp; + F32 vl = vec.len(); + if (vl) { + F32 s = delta.posVec.len() / vl; + delta.posVec = (cp - pos) * s; + } + delta.pos = pos; + mat.setColumn(3,pos); + } + } + else { + // Set the item to the server position + delta.warpTicks = 0; + delta.posVec.set(0,0,0); + delta.pos = pos; + delta.dt = 0; + mat.setColumn(3,pos); + } + } + Parent::setTransform(mat); +} + +DefineEngineMethod( Item, isStatic, bool, (),, + "@brief Is the object static (ie, non-movable)?\n\n" + "@return True if the object is static, false if it is not.\n" + "@tsexample\n" + "// Query the item on if it is or is not static.\n" + "%isStatic = %itemData.isStatic();\n\n" + "@endtsexample\n\n" + "@see static\n" + ) +{ + return object->isStatic(); +} + +DefineEngineMethod( Item, isAtRest, bool, (),, + "@brief Is the object at rest (ie, no longer moving)?\n\n" + "@return True if the object is at rest, false if it is not.\n" + "@tsexample\n" + "// Query the item on if it is or is not at rest.\n" + "%isAtRest = %item.isAtRest();\n\n" + "@endtsexample\n\n" + ) +{ + return object->isAtRest(); +} + +DefineEngineMethod( Item, isRotating, bool, (),, + "@brief Is the object still rotating?\n\n" + "@return True if the object is still rotating, false if it is not.\n" + "@tsexample\n" + "// Query the item on if it is or is not rotating.\n" + "%isRotating = %itemData.isRotating();\n\n" + "@endtsexample\n\n" + "@see rotate\n" + ) +{ + return object->isRotating(); +} + +DefineEngineMethod( Item, setCollisionTimeout, bool, (int ignoreColObj),(NULL), + "@brief Temporarily disable collisions against a specific ShapeBase object.\n\n" + + "This is useful to prevent a player from immediately picking up an Item they have " + "just thrown. Only one object may be on the timeout list at a time. The timeout is " + "defined as 15 ticks.\n\n" + + "@param objectID ShapeBase object ID to disable collisions against.\n" + "@return Returns true if the ShapeBase object requested could be found, false if it could not.\n" + + "@tsexample\n" + "// Set the ShapeBase Object ID to disable collisions against\n" + "%ignoreColObj = %player.getID();\n\n" + "// Inform this Item object to ignore collisions temproarily against the %ignoreColObj.\n" + "%item.setCollisionTimeout(%ignoreColObj);\n\n" + "@endtsexample\n\n" + ) +{ + ShapeBase* source = NULL; + if (Sim::findObject(ignoreColObj,source)) { + object->setCollisionTimeout(source); + return true; + } + return false; +} + + +DefineEngineMethod( Item, getLastStickyPos, const char*, (),, + "@brief Get the position on the surface on which this Item is stuck.\n\n" + "@return Returns The XYZ position of where this Item is stuck.\n" + "@tsexample\n" + "// Acquire the position where this Item is currently stuck\n" + "%stuckPosition = %item.getLastStickPos();\n\n" + "@endtsexample\n\n" + "@note Server side only.\n" + ) +{ + char* ret = Con::getReturnBuffer(256); + if (object->isServerObject()) + dSprintf(ret, 255, "%g %g %g", + object->mStickyCollisionPos.x, + object->mStickyCollisionPos.y, + object->mStickyCollisionPos.z); + else + dStrcpy(ret, "0 0 0"); + + return ret; +} + +DefineEngineMethod( Item, getLastStickyNormal, const char *, (),, + "@brief Get the normal of the surface on which the object is stuck.\n\n" + "@return Returns The XYZ normal from where this Item is stuck.\n" + "@tsexample\n" + "// Acquire the position where this Item is currently stuck\n" + "%stuckPosition = %item.getLastStickPos();\n\n" + "@endtsexample\n\n" + "@note Server side only.\n" + ) +{ + char* ret = Con::getReturnBuffer(256); + if (object->isServerObject()) + dSprintf(ret, 255, "%g %g %g", + object->mStickyCollisionNormal.x, + object->mStickyCollisionNormal.y, + object->mStickyCollisionNormal.z); + else + dStrcpy(ret, "0 0 0"); + + return ret; +} + +//---------------------------------------------------------------------------- + +void Item::initPersistFields() +{ + addGroup("Misc"); + addField("static", TypeBool, Offset(mStatic, Item), "If true, the object is not moving in the world (and will not be updated through the network).\n"); + addField("rotate", TypeBool, Offset(mRotate, Item), "If true, the object will automatically rotate around its Z axis.\n"); + endGroup("Misc"); + + Parent::initPersistFields(); +} + +void Item::consoleInit() +{ + Con::addVariable("Item::minWarpTicks",TypeF32,&sMinWarpTicks, + "@brief Fraction of tick at which instant warp occures on the client.\n\n" + "@ingroup GameObjects"); + Con::addVariable("Item::maxWarpTicks",TypeS32,&sMaxWarpTicks, + "@brief When a warp needs to occur due to the client being too far off from the server, this is the " + "maximum number of ticks we'll allow the client to warp to catch up.\n\n" + "@ingroup GameObjects"); +} + +//---------------------------------------------------------------------------- + +void Item::prepRenderImage( SceneRenderState* state ) +{ + // Items do NOT render if destroyed + if (getDamageState() == Destroyed) + return; + + Parent::prepRenderImage( state ); +} + +void Item::buildConvex(const Box3F& box, Convex* convex) +{ + if (mShapeInstance == NULL) + return; + + // These should really come out of a pool + mConvexList->collectGarbage(); + + if (box.isOverlapped(getWorldBox()) == false) + return; + + // Just return a box convex for the entire shape... + Convex* cc = 0; + CollisionWorkingList& wl = convex->getWorkingList(); + for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) { + if (itr->mConvex->getType() == BoxConvexType && + itr->mConvex->getObject() == this) { + cc = itr->mConvex; + break; + } + } + if (cc) + return; + + // Create a new convex. + BoxConvex* cp = new BoxConvex; + mConvexList->registerObject(cp); + convex->addToWorkingList(cp); + cp->init(this); + + mObjBox.getCenter(&cp->mCenter); + cp->mSize.x = mObjBox.len_x() / 2.0f; + cp->mSize.y = mObjBox.len_y() / 2.0f; + cp->mSize.z = mObjBox.len_z() / 2.0f; +} + +void Item::advanceTime(F32 dt) +{ + Parent::advanceTime(dt); + + if( mRotate ) + { + F32 r = (dt / sRotationSpeed) * M_2PI; + Point3F pos = mRenderObjToWorld.getPosition(); + MatrixF rotMatrix; + if( mRotate ) + { + rotMatrix.set( EulerF( 0.0, 0.0, r ) ); + } + else + { + rotMatrix.set( EulerF( r * 0.5, 0.0, r ) ); + } + MatrixF mat = mRenderObjToWorld; + mat.setPosition( pos ); + mat.mul( rotMatrix ); + setRenderTransform(mat); + } + +} diff --git a/Engine/source/T3D/item.h b/Engine/source/T3D/item.h new file mode 100644 index 000000000..fd6822b7c --- /dev/null +++ b/Engine/source/T3D/item.h @@ -0,0 +1,191 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _ITEM_H_ +#define _ITEM_H_ + +#ifndef _SHAPEBASE_H_ + #include "T3D/shapeBase.h" +#endif +#ifndef _BOXCONVEX_H_ + #include "collision/boxConvex.h" +#endif +#ifndef _DYNAMIC_CONSOLETYPES_H_ + #include "console/dynamicTypes.h" +#endif + + +class PhysicsBody; + + +//---------------------------------------------------------------------------- + +struct ItemData: public ShapeBaseData { + typedef ShapeBaseData Parent; + + F32 friction; + F32 elasticity; + + bool sticky; + F32 gravityMod; + F32 maxVelocity; + + bool lightOnlyStatic; + S32 lightType; + ColorF lightColor; + S32 lightTime; + F32 lightRadius; + + bool simpleServerCollision; + + ItemData(); + DECLARE_CONOBJECT(ItemData); + static void initPersistFields(); + virtual void packData(BitStream* stream); + virtual void unpackData(BitStream* stream); +}; + + +//---------------------------------------------------------------------------- + +class Item: public ShapeBase +{ + protected: + typedef ShapeBase Parent; + + enum MaskBits { + HiddenMask = Parent::NextFreeMask, + ThrowSrcMask = Parent::NextFreeMask << 1, + PositionMask = Parent::NextFreeMask << 2, + RotationMask = Parent::NextFreeMask << 3, + NextFreeMask = Parent::NextFreeMask << 4 + }; + + // Client interpolation data + struct StateDelta { + Point3F pos; + VectorF posVec; + S32 warpTicks; + Point3F warpOffset; + F32 dt; + }; + StateDelta delta; + + // Static attributes + ItemData* mDataBlock; + static F32 mGravity; + bool mStatic; + bool mRotate; + + // + VectorF mVelocity; + bool mAtRest; + + S32 mAtRestCounter; + static const S32 csmAtRestTimer; + + bool mInLiquid; + + ShapeBase* mCollisionObject; + U32 mCollisionTimeout; + + PhysicsBody *mPhysicsRep; + + bool mSubclassItemHandlesScene; ///< A subclass of Item will handle all of the adding to the scene + + protected: + DECLARE_CALLBACK( void, onStickyCollision, ( const char* objID )); + DECLARE_CALLBACK( void, onEnterLiquid, ( const char* objID, const char* waterCoverage, const char* liquidType )); + DECLARE_CALLBACK( void, onLeaveLiquid, ( const char* objID, const char* liquidType )); + + public: + + void registerLights(LightManager * lightManager, bool lightingScene); + enum LightType + { + NoLight = 0, + ConstantLight, + PulsingLight, + + NumLightTypes, + }; + + private: + S32 mDropTime; + LightInfo* mLight; + + public: + + Point3F mStickyCollisionPos; + Point3F mStickyCollisionNormal; + + // + private: + OrthoBoxConvex mConvex; + Box3F mWorkingQueryBox; + + void updateVelocity(const F32 dt); + void updatePos(const U32 mask, const F32 dt); + void updateWorkingCollisionSet(const U32 mask, const F32 dt); + bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere); + void buildConvex(const Box3F& box, Convex* convex); + void onDeleteNotify(SimObject*); + + protected: + void _updatePhysics(); + void prepRenderImage(SceneRenderState *state); + void advanceTime(F32 dt); + + public: + DECLARE_CONOBJECT(Item); + + + Item(); + ~Item(); + static void initPersistFields(); + static void consoleInit(); + + bool onAdd(); + void onRemove(); + bool onNewDataBlock( GameBaseData *dptr, bool reload ); + + bool isStatic() { return mStatic; } + bool isAtRest() { return mAtRest; } + bool isRotating() { return mRotate; } + Point3F getVelocity() const; + void setVelocity(const VectorF& vel); + void applyImpulse(const Point3F& pos,const VectorF& vec); + void setCollisionTimeout(ShapeBase* obj); + ShapeBase* getCollisionObject() { return mCollisionObject; }; + + void processTick(const Move *move); + void interpolateTick(F32 delta); + virtual void setTransform(const MatrixF &mat); + + U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream); + void unpackUpdate(NetConnection *conn, BitStream *stream); +}; + +typedef Item::LightType ItemLightType; +DefineEnumType( ItemLightType ); + +#endif diff --git a/Engine/source/T3D/levelInfo.cpp b/Engine/source/T3D/levelInfo.cpp new file mode 100644 index 000000000..16cdf8be1 --- /dev/null +++ b/Engine/source/T3D/levelInfo.cpp @@ -0,0 +1,339 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/levelInfo.h" + +#include "console/consoleTypes.h" +#include "core/stream/bitStream.h" +#include "scene/sceneManager.h" +#include "lighting/advanced/advancedLightManager.h" +#include "lighting/advanced/advancedLightBinManager.h" +#include "sfx/sfxAmbience.h" +#include "sfx/sfxSoundscape.h" +#include "sfx/sfxSystem.h" +#include "sfx/sfxTypes.h" +#include "console/engineAPI.h" +#include "math/mathIO.h" + + +IMPLEMENT_CO_NETOBJECT_V1(LevelInfo); + +ConsoleDocClass( LevelInfo, + "@brief Stores and controls the rendering and status information for a game level.\n\n" + + "@tsexample\n" + "new LevelInfo(theLevelInfo)\n" + "{\n" + " visibleDistance = \"1000\";\n" + " fogColor = \"0.6 0.6 0.7 1\";\n" + " fogDensity = \"0\";\n" + " fogDensityOffset = \"700\";\n" + " fogAtmosphereHeight = \"0\";\n" + " canvasClearColor = \"0 0 0 255\";\n" + " canSaveDynamicFields = \"1\";\n" + " levelName = \"Blank Room\";\n" + " desc0 = \"A blank room ready to be populated with Torque objects.\";\n" + " Enabled = \"1\";\n" + "};\n" + "@endtsexample\n" + "@ingroup enviroMisc\n" +); + + +/// The color used to clear the canvas. +/// @see GuiCanvas +extern ColorI gCanvasClearColor; + +/// @see DecalManager +extern F32 gDecalBias; + + +/// Default SFXAmbience used to reset the global soundscape. +static SFXAmbience sDefaultAmbience; + + +//----------------------------------------------------------------------------- + +LevelInfo::LevelInfo() + : mNearClip( 0.1f ), + mVisibleDistance( 1000.0f ), + mDecalBias( 0.0015f ), + mCanvasClearColor( 255, 0, 255, 255 ), + mSoundAmbience( NULL ), + mSoundscape( NULL ), + mSoundDistanceModel( SFXDistanceModelLinear ), + mWorldSize( 10000.0f ), + mAmbientLightBlendPhase( 1.f ) +{ + mFogData.density = 0.0f; + mFogData.densityOffset = 0.0f; + mFogData.atmosphereHeight = 0.0f; + mFogData.color.set( 0.5f, 0.5f, 0.5f, 1.0f ), + + mNetFlags.set( ScopeAlways | Ghostable ); + + mAdvancedLightmapSupport = false; + + // Register with the light manager activation signal, and we need to do it first + // so the advanced light bin manager can be instructed about MRT lightmaps + LightManager::smActivateSignal.notify(this, &LevelInfo::_onLMActivate, 0.01f); +} + +//----------------------------------------------------------------------------- + +LevelInfo::~LevelInfo() +{ + LightManager::smActivateSignal.remove(this, &LevelInfo::_onLMActivate); +} + +//----------------------------------------------------------------------------- + +void LevelInfo::initPersistFields() +{ + addGroup( "Visibility" ); + + addField( "nearClip", TypeF32, Offset( mNearClip, LevelInfo ), "Closest distance from the camera's position to render the world." ); + addField( "visibleDistance", TypeF32, Offset( mVisibleDistance, LevelInfo ), "Furthest distance fromt he camera's position to render the world." ); + addField( "decalBias", TypeF32, Offset( mDecalBias, LevelInfo ), + "NearPlane bias used when rendering Decal and DecalRoad. This should be tuned to the visibleDistance in your level." ); + + endGroup( "Visibility" ); + + addGroup( "Fog" ); + + addField( "fogColor", TypeColorF, Offset( mFogData.color, LevelInfo ), + "The default color for the scene fog." ); + + addField( "fogDensity", TypeF32, Offset( mFogData.density, LevelInfo ), + "The 0 to 1 density value for the exponential fog falloff." ); + + addField( "fogDensityOffset", TypeF32, Offset( mFogData.densityOffset, LevelInfo ), + "An offset from the camera in meters for moving the start of the fog effect." ); + + addField( "fogAtmosphereHeight", TypeF32, Offset( mFogData.atmosphereHeight, LevelInfo ), + "A height in meters for altitude fog falloff." ); + + endGroup( "Fog" ); + + addGroup( "LevelInfo" ); + + addField( "canvasClearColor", TypeColorI, Offset( mCanvasClearColor, LevelInfo ), + "The color used to clear the background before the scene or any GUIs are rendered." ); + + endGroup( "LevelInfo" ); + + addGroup( "Lighting" ); + + addField( "ambientLightBlendPhase", TypeF32, Offset( mAmbientLightBlendPhase, LevelInfo ), + "Number of seconds it takes to blend from one ambient light color to a different one." ); + + addField( "ambientLightBlendCurve", TypeEaseF, Offset( mAmbientLightBlendCurve, LevelInfo ), + "Interpolation curve to use for blending from one ambient light color to a different one." ); + + addField( "advancedLightmapSupport", TypeBool, Offset( mAdvancedLightmapSupport, LevelInfo ), + "Enable expanded support for mixing static and dynamic lighting (more costly)" ); + + endGroup( "Lighting" ); + + addGroup( "Sound" ); + + addField( "soundAmbience", TypeSFXAmbienceName, Offset( mSoundAmbience, LevelInfo ), "The global ambient sound environment." ); + addField( "soundDistanceModel", TypeSFXDistanceModel, Offset( mSoundDistanceModel, LevelInfo ), "The distance attenuation model to use." ); + + endGroup( "Sound" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +void LevelInfo::inspectPostApply() +{ + _updateSceneGraph(); + setMaskBits( 0xFFFFFFFF ); + + Parent::inspectPostApply(); +} + +//----------------------------------------------------------------------------- + +U32 LevelInfo::packUpdate(NetConnection *conn, U32 mask, BitStream *stream) +{ + U32 retMask = Parent::packUpdate(conn, mask, stream); + + stream->write( mNearClip ); + stream->write( mVisibleDistance ); + stream->write( mDecalBias ); + + stream->write( mFogData.density ); + stream->write( mFogData.densityOffset ); + stream->write( mFogData.atmosphereHeight ); + stream->write( mFogData.color ); + + stream->write( mCanvasClearColor ); + stream->write( mWorldSize ); + + stream->writeFlag( mAdvancedLightmapSupport ); + stream->write( mAmbientLightBlendPhase ); + mathWrite( *stream, mAmbientLightBlendCurve ); + + sfxWrite( stream, mSoundAmbience ); + stream->writeInt( mSoundDistanceModel, 1 ); + + return retMask; +} + +//----------------------------------------------------------------------------- + +void LevelInfo::unpackUpdate(NetConnection *conn, BitStream *stream) +{ + Parent::unpackUpdate(conn, stream); + + stream->read( &mNearClip ); + stream->read( &mVisibleDistance ); + stream->read( &mDecalBias ); + + stream->read( &mFogData.density ); + stream->read( &mFogData.densityOffset ); + stream->read( &mFogData.atmosphereHeight ); + stream->read( &mFogData.color ); + + stream->read( &mCanvasClearColor ); + stream->read( &mWorldSize ); + + mAdvancedLightmapSupport = stream->readFlag(); + stream->read( &mAmbientLightBlendPhase ); + mathRead( *stream, &mAmbientLightBlendCurve ); + + String errorStr; + if( !sfxReadAndResolve( stream, &mSoundAmbience, errorStr ) ) + Con::errorf( "%s", errorStr.c_str() ); + mSoundDistanceModel = ( SFXDistanceModel ) stream->readInt( 1 ); + + if( isProperlyAdded() ) + { + _updateSceneGraph(); + + if( mSoundscape ) + { + if( mSoundAmbience ) + mSoundscape->setAmbience( mSoundAmbience ); + else + mSoundscape->setAmbience( &sDefaultAmbience ); + } + + SFX->setDistanceModel( mSoundDistanceModel ); + } +} + +//----------------------------------------------------------------------------- + +bool LevelInfo::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + // If no sound ambience has been set, default to + // 'AudioAmbienceDefault'. + + if( !mSoundAmbience ) + Sim::findObject( "AudioAmbienceDefault", mSoundAmbience ); + + // Set up sound on client. + + if( isClientObject() ) + { + SFX->setDistanceModel( mSoundDistanceModel ); + + // Set up the global ambient soundscape. + + mSoundscape = SFX->getSoundscapeManager()->getGlobalSoundscape(); + if( mSoundAmbience ) + mSoundscape->setAmbience( mSoundAmbience ); + } + + _updateSceneGraph(); + + return true; +} + +//----------------------------------------------------------------------------- + +void LevelInfo::onRemove() +{ + if( mSoundscape ) + mSoundscape->setAmbience( &sDefaultAmbience ); + + Parent::onRemove(); +} + +//----------------------------------------------------------------------------- + +void LevelInfo::_updateSceneGraph() +{ + // Clamp above zero before setting on the sceneGraph. + // If we don't we get serious crashes. + if ( mNearClip <= 0.0f ) + mNearClip = 0.001f; + + SceneManager* scene = isClientObject() ? gClientSceneGraph : gServerSceneGraph; + + scene->setNearClip( mNearClip ); + scene->setVisibleDistance( mVisibleDistance ); + + gDecalBias = mDecalBias; + + // Set ambient lighting properties. + + scene->setAmbientLightTransitionTime( mAmbientLightBlendPhase * 1000.f ); + scene->setAmbientLightTransitionCurve( mAmbientLightBlendCurve ); + + // Copy our AirFogData into the sceneGraph. + scene->setFogData( mFogData ); + + // If the level info specifies that MRT pre-pass should be used in this scene + // enable it via the appropriate light manager + // (Basic lighting doesn't do anything different right now) +#ifndef TORQUE_DEDICATED + if(isClientObject()) + _onLMActivate(LIGHTMGR->getId(), true); +#endif + + // TODO: This probably needs to be moved. + gCanvasClearColor = mCanvasClearColor; +} + +//----------------------------------------------------------------------------- + +void LevelInfo::_onLMActivate(const char *lm, bool enable) +{ +#ifndef TORQUE_DEDICATED + // Advanced light manager + if(enable && String(lm) == String("ADVLM")) + { + AssertFatal(dynamic_cast(LIGHTMGR), "Bad light manager type!"); + AdvancedLightManager *lightMgr = static_cast(LIGHTMGR); + lightMgr->getLightBinManager()->MRTLightmapsDuringPrePass(mAdvancedLightmapSupport); + } +#endif +} \ No newline at end of file diff --git a/Engine/source/T3D/levelInfo.h b/Engine/source/T3D/levelInfo.h new file mode 100644 index 000000000..425ba6f03 --- /dev/null +++ b/Engine/source/T3D/levelInfo.h @@ -0,0 +1,138 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _LEVELINFO_H_ +#define _LEVELINFO_H_ + +#ifndef _NETOBJECT_H_ + #include "sim/netObject.h" +#endif +#ifndef _COLOR_H_ + #include "core/color.h" +#endif +#ifndef _FOGSTRUCTS_H_ + #include "scene/fogStructs.h" +#endif +#ifndef _SFXCOMMON_H_ + #include "sfx/sfxCommon.h" +#endif + + +class SFXAmbience; +class SFXSoundscape; + + +class LevelInfo : public NetObject +{ + typedef NetObject Parent; + + private: + + F32 mWorldSize; + + FogData mFogData; + + F32 mNearClip; + + F32 mVisibleDistance; + + F32 mDecalBias; + + ColorI mCanvasClearColor; + + /// @name Lighting Properties + /// @{ + + bool mAdvancedLightmapSupport; + + /// Seconds it takes to go from one global ambient color + /// to a different one. + F32 mAmbientLightBlendPhase; + + /// Interpolation for going from one global ambient color + /// to a different one. + EaseF mAmbientLightBlendCurve; + + /// @} + + /// @name Sound Properties + /// @{ + + /// Global ambient sound space properties. + SFXAmbience* mSoundAmbience; + + /// Distance attenuation model to use. + SFXDistanceModel mSoundDistanceModel; + + /// + SFXSoundscape* mSoundscape; + + /// @} + + /// Responsible for passing on + /// the LevelInfo settings to the + /// client SceneGraph, from which + /// other systems can get at them. + void _updateSceneGraph(); + + void _onLMActivate(const char *lm, bool enable); + + public: + + LevelInfo(); + virtual ~LevelInfo(); + + DECLARE_CONOBJECT(LevelInfo); + + /// @name SceneObject Inheritance + /// @{ + + virtual SFXAmbience* getSoundAmbience() const { return mSoundAmbience; } + + /// @} + + /// @name SimObject Inheritance + /// @{ + + virtual bool onAdd(); + virtual void onRemove(); + virtual void inspectPostApply(); + + static void initPersistFields(); + + /// @} + + /// @name NetObject Inheritance + /// @{ + + enum NetMaskBits + { + UpdateMask = BIT(0) + }; + + virtual U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream ); + virtual void unpackUpdate( NetConnection *conn, BitStream *stream ); + + /// @} +}; + +#endif // _LEVELINFO_H_ \ No newline at end of file diff --git a/Engine/source/T3D/lightAnimData.cpp b/Engine/source/T3D/lightAnimData.cpp new file mode 100644 index 000000000..5f6270023 --- /dev/null +++ b/Engine/source/T3D/lightAnimData.cpp @@ -0,0 +1,309 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lightAnimData.h" + +#include "console/consoleTypes.h" +#include "T3D/lightBase.h" +#include "math/mRandom.h" +#include "math/mathIO.h" +#include "T3D/gameBase/processList.h" +#include "core/stream/bitStream.h" + + +LightAnimData::LightAnimData() +{ +} + +LightAnimData::~LightAnimData() +{ +} + +IMPLEMENT_CO_DATABLOCK_V1( LightAnimData ); + +ConsoleDocClass( LightAnimData, + "@brief A datablock which defines and performs light animation, such as rotation, brightness fade, and colorization.\n\n" + + "@tsexample\n" + "datablock LightAnimData( SubtlePulseLightAnim )\n" + "{\n" + " brightnessA = 0.5;\n" + " brightnessZ = 1;\n" + " brightnessPeriod = 1;\n" + " brightnessKeys = \"aza\";\n" + " brightnessSmooth = true;\n" + "};\n" + "@endtsexample\n\n" + "@see LightBase\n\n" + "@see LightDescription\n\n" + "@ingroup FX\n" + "@ingroup Lighting\n" +); + +void LightAnimData::initPersistFields() +{ + addGroup( "Offset", + "The XYZ translation animation state relative to the light position." ); + + addField( "offsetA", TypeF32, Offset( mOffset.value1, LightAnimData ), 3, + "The value of the A key in the keyframe sequence." ); + + addField( "offsetZ", TypeF32, Offset( mOffset.value2, LightAnimData ), 3, + "The value of the Z key in the keyframe sequence." ); + + addField( "offsetPeriod", TypeF32, Offset( mOffset.period, LightAnimData ), 3, + "The animation time for keyframe sequence." ); + + addField( "offsetKeys", TypeString, Offset( mOffset.keys, LightAnimData ), 3, + "The keyframe sequence encoded into a string where characters from A to Z define " + "a position between the two animation values." ); + + addField( "offsetSmooth", TypeBool, Offset( mOffset.smooth, LightAnimData ), 3, + "If true the transition between keyframes will be smooth." ); + + endGroup( "Offset" ); + + addGroup( "Rotation", + "The XYZ rotation animation state relative to the light orientation." ); + + addField( "rotA", TypeF32, Offset( mRot.value1, LightAnimData ), 3, + "The value of the A key in the keyframe sequence." ); + + addField( "rotZ", TypeF32, Offset( mRot.value2, LightAnimData ), 3, + "The value of the Z key in the keyframe sequence." ); + + addField( "rotPeriod", TypeF32, Offset( mRot.period, LightAnimData ), 3, + "The animation time for keyframe sequence." ); + + addField( "rotKeys", TypeString, Offset( mRot.keys, LightAnimData ), 3, + "The keyframe sequence encoded into a string where characters from A to Z define " + "a position between the two animation values." ); + + addField( "rotSmooth", TypeBool, Offset( mRot.smooth, LightAnimData ), 3, + "If true the transition between keyframes will be smooth." ); + + endGroup( "Rotation" ); + + addGroup( "Color", + "The RGB color animation state." ); + + addField( "colorA", TypeF32, Offset( mColor.value1, LightAnimData ), 3, + "The value of the A key in the keyframe sequence." ); + + addField( "colorZ", TypeF32, Offset( mColor.value2, LightAnimData ), 3, + "The value of the Z key in the keyframe sequence." ); + + addField( "colorPeriod", TypeF32, Offset( mColor.period, LightAnimData ), 3, + "The animation time for keyframe sequence." ); + + addField( "colorKeys", TypeString, Offset( mColor.keys, LightAnimData ), 3, + "The keyframe sequence encoded into a string where characters from A to Z define " + "a position between the two animation values." ); + + addField( "colorSmooth", TypeBool, Offset( mColor.smooth, LightAnimData ), 3, + "If true the transition between keyframes will be smooth." ); + + endGroup( "Color" ); + + addGroup( "Brightness", + "The brightness animation state." ); + + addField( "brightnessA", TypeF32, Offset( mBrightness.value1, LightAnimData ), + "The value of the A key in the keyframe sequence." ); + + addField( "brightnessZ", TypeF32, Offset( mBrightness.value2, LightAnimData ), + "The value of the Z key in the keyframe sequence." ); + + addField( "brightnessPeriod", TypeF32, Offset( mBrightness.period, LightAnimData ), + "The animation time for keyframe sequence." ); + + addField( "brightnessKeys", TypeString, Offset( mBrightness.keys, LightAnimData ), + "The keyframe sequence encoded into a string where characters from A to Z define " + "a position between the two animation values." ); + + addField( "brightnessSmooth", TypeBool, Offset( mBrightness.smooth, LightAnimData ), + "If true the transition between keyframes will be smooth." ); + + endGroup( "Brightness" ); + + Parent::initPersistFields(); +} + +bool LightAnimData::preload( bool server, String &errorStr ) +{ + if ( !Parent::preload( server, errorStr ) ) + return false; + + _updateKeys(); + + return true; +} + +void LightAnimData::inspectPostApply() +{ + Parent::inspectPostApply(); + _updateKeys(); +} + +void LightAnimData::_updateKeys() +{ + mOffset.updateKey(); + mRot.updateKey(); + mColor.updateKey(); + mBrightness.updateKey(); +} + +template +void LightAnimData::AnimValue::updateKey() +{ + for ( U32 i=0; i < COUNT; i++ ) + { + timeScale[i] = 0.0f; + keyLen[i] = 0.0f; + + if ( keys[i] && keys[i][0] && period[i] > 0.0f ) + { + keyLen[i] = dStrlen( keys[i] ); + timeScale[i] = (F32)( keyLen[i] - 1 ) / period[i]; + } + } +} + +template +bool LightAnimData::AnimValue::animate( F32 time, F32 *output ) +{ + F32 scaledTime, lerpFactor, valueRange, keyFrameLerp; + U32 posFrom, posTo; + S32 keyFrameFrom, keyFrameTo; + + bool wasAnimated = false; + + for ( U32 i=0; i < COUNT; i++ ) + { + if ( mIsZero( timeScale[i] ) ) + continue; + + wasAnimated = true; + + scaledTime = mFmod( time, period[i] ) * timeScale[i]; + + posFrom = mFloor( scaledTime ); + posTo = mCeil( scaledTime ); + + keyFrameFrom = dToupper( keys[i][posFrom] ) - 65; + keyFrameTo = dToupper( keys[i][posTo] ) - 65; + valueRange = ( value2[i] - value1[i] ) / 25.0f; + + if ( !smooth[i] ) + output[i] = value1[i] + ( keyFrameFrom * valueRange ); + else + { + lerpFactor = scaledTime - posFrom; + keyFrameLerp = ( keyFrameTo - keyFrameFrom ) * lerpFactor; + + output[i] = value1[i] + ( ( keyFrameFrom + keyFrameLerp ) * valueRange ); + } + } + + return wasAnimated; +} + +template +void LightAnimData::AnimValue::write( BitStream *stream ) const +{ + for ( U32 i=0; i < COUNT; i++ ) + { + stream->write( value1[i] ); + stream->write( value2[i] ); + stream->write( period[i] ); + stream->writeString( keys[i] ); + } +} + +template +void LightAnimData::AnimValue::read( BitStream *stream ) +{ + for ( U32 i=0; i < COUNT; i++ ) + { + stream->read( &value1[i] ); + stream->read( &value2[i] ); + stream->read( &period[i] ); + keys[i] = stream->readSTString(); + } +} + +void LightAnimData::packData( BitStream *stream ) +{ + Parent::packData( stream ); + + mOffset.write( stream ); + mRot.write( stream ); + mColor.write( stream ); + mBrightness.write( stream ); +} + +void LightAnimData::unpackData( BitStream *stream ) +{ + Parent::unpackData( stream ); + + mOffset.read( stream ); + mRot.read( stream ); + mColor.read( stream ); + mBrightness.read( stream ); +} + +void LightAnimData::animate( LightInfo *lightInfo, LightAnimState *state ) +{ + PROFILE_SCOPE( LightAnimData_animate ); + + // Calculate the input time for animation. + F32 time = state->animationPhase + + ( (F32)Sim::getCurrentTime() * 0.001f ) / + state->animationPeriod; + + MatrixF transform( state->transform ); + + EulerF euler( Point3F::Zero ); + if ( mRot.animate( time, euler ) ) + { + euler.x = mDegToRad( euler.x ); + euler.y = mDegToRad( euler.y ); + euler.z = mDegToRad( euler.z ); + MatrixF rot( euler ); + transform.mul( rot ); + } + + Point3F offset( Point3F::Zero ); + if ( mOffset.animate( time, offset ) ) + transform.displace( offset ); + + lightInfo->setTransform( transform ); + + ColorF color = state->color; + mColor.animate( time, color ); + lightInfo->setColor( color ); + + F32 brightness = state->brightness; + mBrightness.animate( time, &brightness ); + lightInfo->setBrightness( brightness ); +} diff --git a/Engine/source/T3D/lightAnimData.h b/Engine/source/T3D/lightAnimData.h new file mode 100644 index 000000000..69b2ff65d --- /dev/null +++ b/Engine/source/T3D/lightAnimData.h @@ -0,0 +1,180 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _LIGHTANIMDATA_H_ +#define _LIGHTANIMDATA_H_ + +#ifndef _SIMDATABLOCK_H_ +#include "console/simDatablock.h" +#endif +#ifndef _CONSOLETYPES_H_ +#include "console/consoleTypes.h" +#endif +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _COLOR_H_ +#include "core/color.h" +#endif + +class LightInfo; + + +/// The light animation state required by LightAnimData. +struct LightAnimState +{ + /// Constructor. + LightAnimState() + : active( true ), + animationPhase( 1.0f ), + animationPeriod( 1.0f ), + brightness( 1.0f ), + transform( true ), + color( 0, 0, 0, 0 ) + { + } + + /// If true light animation should be performed. + bool active; + + /// The phase used to offset the animation start + /// time to vary the animation of nearby lights. + F32 animationPhase; + + /// The length of time in seconds for a single playback + /// of the light animation. + F32 animationPeriod; + + /// The set light brightness before animation occurs. + F32 brightness; + + /// The set light transform before animation occurs. + MatrixF transform; + + /// The set light color before animation occurs. + ColorF color; +}; + + +/// A datablock which defines and performs light animation. +/// @see LightBase, LightDescription +class LightAnimData : public SimDataBlock +{ + typedef SimDataBlock Parent; + +protected: + + /// Called internally to update the key data. + void _updateKeys(); + +public: + + LightAnimData(); + virtual ~LightAnimData(); + + DECLARE_CONOBJECT( LightAnimData ); + + // SimObject + static void initPersistFields(); + virtual void inspectPostApply(); + + // SimDataBlock + virtual bool preload( bool server, String &errorStr ); + virtual void packData( BitStream *stream ); + virtual void unpackData( BitStream *stream ); + + /// Animates parameters on the passed Light's LightInfo object. + virtual void animate( LightInfo *light, LightAnimState *state ); + + /// Helper class used to keyframe light parameters. It is templatized + /// on the number of parameters to store. + template + struct AnimValue + { + /// Constructor. + AnimValue() + { + dMemset( value1, 0, sizeof( value1 ) ); + dMemset( value2, 0, sizeof( value2 ) ); + dMemset( period, 0, sizeof( period ) ); + dMemset( keys, 0, sizeof( keys ) ); + dMemset( smooth, 0, sizeof( smooth ) ); + dMemset( keyLen, 0, sizeof( keyLen ) ); + dMemset( timeScale, 0, sizeof( timeScale ) ); + } + + /// The first value associated with the A keyframe. + F32 value1[COUNT]; + + /// The second value associated with the Z keyframe. + F32 value2[COUNT]; + + /// The period of the full keyframe sequence. + F32 period[COUNT]; + + /// The keyframe keys as a string of letters A to Z. + StringTableEntry keys[COUNT]; + + /// If true the transition between keyframes will be smooth. + bool smooth[COUNT]; + + /// The calculated length of the keyframe string. + /// @see updateKey + U32 keyLen[COUNT]; + + /// The scale used to convert time into a keyframe position. + /// @see updateKey + F32 timeScale[COUNT]; + + /// Performs the animation returning the results in the output if + /// the time scale is greater than zero. + /// @return Returns true if the animation was performed. + bool animate( F32 time, F32 *output ); + + /// Called when the key string is changed to update the + /// key length and time scale. + void updateKey(); + + /// Write the animation data to the bitstream. + void write( BitStream *stream ) const; + + /// Read the animation data from the bitstream. + void read( BitStream *stream ); + }; + + /// The positional animation parameters for x, y, and z. + AnimValue<3> mOffset; + + /// The rotational animation parameters for x, y, and z. + AnimValue<3> mRot; + + /// The color animation parameters for r, g, and b. + AnimValue<3> mColor; + + /// The brightness animation parameter. + AnimValue<1> mBrightness; +}; + +#endif // _LIGHTANIMDATA_H_ \ No newline at end of file diff --git a/Engine/source/T3D/lightBase.cpp b/Engine/source/T3D/lightBase.cpp new file mode 100644 index 000000000..a3a5372cf --- /dev/null +++ b/Engine/source/T3D/lightBase.cpp @@ -0,0 +1,484 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/lightBase.h" + +#include "console/consoleTypes.h" +#include "console/typeValidators.h" +#include "core/stream/bitStream.h" +#include "sim/netConnection.h" +#include "lighting/lightManager.h" +#include "lighting/shadowMap/lightShadowMap.h" +#include "scene/sceneRenderState.h" +#include "renderInstance/renderPassManager.h" +#include "console/engineAPI.h" +#include "gfx/gfxDrawUtil.h" + +extern bool gEditingMission; + +bool LightBase::smRenderViz = false; + +IMPLEMENT_CONOBJECT( LightBase ); + +ConsoleDocClass( LightBase, + "@brief This is the base class for light objects.\n\n" + + "It is *NOT* intended to be used directly in script, but exists to provide the base member variables " + "and generic functionality. You should be using the derived classes PointLight and SpotLight, which " + "can be declared in TorqueScript or added from the World Editor.\n\n" + + "For this class, we only add basic lighting options that all lighting systems would use. " + "The specific lighting system options are injected at runtime by the lighting system itself.\n\n" + + "@see PointLight\n\n" + "@see SpotLight\n\n" + "@ingroup Lighting\n" +); + +LightBase::LightBase() + : mIsEnabled( true ), + mColor( ColorF::WHITE ), + mBrightness( 1.0f ), + mCastShadows( false ), + mPriority( 1.0f ), + mAnimationData( NULL ), + mFlareData( NULL ), + mFlareScale( 1.0f ) +{ + mNetFlags.set( Ghostable | ScopeAlways ); + mTypeMask = EnvironmentObjectType | LightObjectType; + + mLight = LightManager::createLightInfo(); + + mFlareState.clear(); +} + +LightBase::~LightBase() +{ + SAFE_DELETE( mLight ); +} + +void LightBase::initPersistFields() +{ + // We only add the basic lighting options that all lighting + // systems would use... the specific lighting system options + // are injected at runtime by the lighting system itself. + + addGroup( "Light" ); + + addField( "isEnabled", TypeBool, Offset( mIsEnabled, LightBase ), "Enables/Disables the object rendering and functionality in the scene." ); + addField( "color", TypeColorF, Offset( mColor, LightBase ), "Changes the base color hue of the light." ); + addField( "brightness", TypeF32, Offset( mBrightness, LightBase ), "Adjusts the lights power, 0 being off completely." ); + addField( "castShadows", TypeBool, Offset( mCastShadows, LightBase ), "Enables/disabled shadow casts by this light." ); + addField( "priority", TypeF32, Offset( mPriority, LightBase ), "Used for sorting of lights by the light manager. " + "Priority determines if a light has a stronger effect than, those with a lower value" ); + + endGroup( "Light" ); + + addGroup( "Light Animation" ); + + addField( "animate", TypeBool, Offset( mAnimState.active, LightBase ), "Toggles animation for the light on and off" ); + addField( "animationType", TYPEID< LightAnimData >(), Offset( mAnimationData, LightBase ), "Datablock containing light animation information (LightAnimData)" ); + addFieldV( "animationPeriod", TypeF32, Offset( mAnimState.animationPeriod, LightBase ), &CommonValidators::PositiveNonZeroFloat, "The length of time in seconds for a single playback of the light animation (must be > 0)" ); + addField( "animationPhase", TypeF32, Offset( mAnimState.animationPhase, LightBase ), "The phase used to offset the animation start time to vary the animation of nearby lights." ); + + endGroup( "Light Animation" ); + + addGroup( "Misc" ); + + addField( "flareType", TYPEID< LightFlareData >(), Offset( mFlareData, LightBase ), "Datablock containing light flare information (LightFlareData)" ); + addField( "flareScale", TypeF32, Offset( mFlareScale, LightBase ), "Globally scales all features of the light flare" ); + + endGroup( "Misc" ); + + // Now inject any light manager specific fields. + LightManager::initLightFields(); + + // We do the parent fields at the end so that + // they show up that way in the inspector. + Parent::initPersistFields(); + + Con::addVariable( "$Light::renderViz", TypeBool, &smRenderViz, + "Toggles visualization of light object's radius or cone.\n" + "@ingroup Lighting"); + + Con::addVariable( "$Light::renderLightFrustums", TypeBool, &LightShadowMap::smDebugRenderFrustums, + "Toggles rendering of light frustums when the light is selected in the editor.\n\n" + "@note Only works for shadow mapped lights.\n\n" + "@ingroup Lighting" ); +} + +bool LightBase::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + // Update the light parameters. + _conformLights(); + addToScene(); + + return true; +} + +void LightBase::onRemove() +{ + removeFromScene(); + Parent::onRemove(); +} + +void LightBase::submitLights( LightManager *lm, bool staticLighting ) +{ + if ( !mIsEnabled || staticLighting ) + return; + + if ( mAnimState.active && + mAnimState.animationPeriod > 0.0f && + mAnimationData ) + { + mAnimState.brightness = mBrightness; + mAnimState.transform = getRenderTransform(); + mAnimState.color = mColor; + + mAnimationData->animate( mLight, &mAnimState ); + } + + lm->registerGlobalLight( mLight, this ); +} + +void LightBase::inspectPostApply() +{ + // We do not call the parent here as it + // will call setScale() and screw up the + // real sizing fields on the light. + + // Ok fine... then we must set MountedMask ourself. + + _conformLights(); + setMaskBits( EnabledMask | UpdateMask | TransformMask | DatablockMask | MountedMask ); +} + +void LightBase::setTransform( const MatrixF &mat ) +{ + setMaskBits( TransformMask ); + Parent::setTransform( mat ); +} + +void LightBase::prepRenderImage( SceneRenderState *state ) +{ + if ( mIsEnabled && mFlareData ) + { + mFlareState.fullBrightness = mBrightness; + mFlareState.scale = mFlareScale; + mFlareState.lightInfo = mLight; + mFlareState.lightMat = getRenderTransform(); + + mFlareData->prepRender( state, &mFlareState ); + } + + if ( !state->isDiffusePass() ) + return; + + const bool isSelectedInEditor = ( gEditingMission && isSelected() ); + + // If the light is selected or light visualization + // is enabled then register the callback. + if ( smRenderViz || isSelectedInEditor ) + { + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &LightBase::_onRenderViz ); + ri->type = RenderPassManager::RIT_Editor; + state->getRenderPass()->addInst( ri ); + } +} + +void LightBase::_onRenderViz( ObjectRenderInst *ri, + SceneRenderState *state, + BaseMatInstance *overrideMat ) +{ + if ( overrideMat ) + return; + + _renderViz( state ); +} + +void LightBase::_onSelected() +{ + #ifdef TORQUE_DEBUG + // Enable debug rendering on the light. + if( isClientObject() ) + mLight->enableDebugRendering( true ); + #endif + + Parent::_onSelected(); +} + +void LightBase::_onUnselected() +{ + #ifdef TORQUE_DEBUG + // Disable debug rendering on the light. + if( isClientObject() ) + mLight->enableDebugRendering( false ); + #endif + + Parent::_onUnselected(); +} + +void LightBase::interpolateTick( F32 delta ) +{ +} + +void LightBase::processTick() +{ +} + +void LightBase::advanceTime( F32 timeDelta ) +{ + if ( isMounted() ) + { + MatrixF mat( true ); + mMount.object->getRenderMountTransform( 0.f, mMount.node, mMount.xfm, &mat ); + mLight->setTransform( mat ); + Parent::setTransform( mat ); + } +} + +U32 LightBase::packUpdate( NetConnection *conn, U32 mask, BitStream *stream ) +{ + U32 retMask = Parent::packUpdate( conn, mask, stream ); + + stream->writeFlag( mIsEnabled ); + + if ( stream->writeFlag( mask & TransformMask ) ) + stream->writeAffineTransform( mObjToWorld ); + + if ( stream->writeFlag( mask & UpdateMask ) ) + { + stream->write( mColor ); + stream->write( mBrightness ); + + stream->writeFlag( mCastShadows ); + + stream->write( mPriority ); + + mLight->packExtended( stream ); + + stream->writeFlag( mAnimState.active ); + stream->write( mAnimState.animationPeriod ); + stream->write( mAnimState.animationPhase ); + stream->write( mFlareScale ); + } + + if ( stream->writeFlag( mask & DatablockMask ) ) + { + if ( stream->writeFlag( mAnimationData ) ) + { + stream->writeRangedU32( mAnimationData->getId(), + DataBlockObjectIdFirst, + DataBlockObjectIdLast ); + } + + if ( stream->writeFlag( mFlareData ) ) + { + stream->writeRangedU32( mFlareData->getId(), + DataBlockObjectIdFirst, + DataBlockObjectIdLast ); + } + } + + return retMask; +} + +void LightBase::unpackUpdate( NetConnection *conn, BitStream *stream ) +{ + Parent::unpackUpdate( conn, stream ); + + mIsEnabled = stream->readFlag(); + + if ( stream->readFlag() ) // TransformMask + stream->readAffineTransform( &mObjToWorld ); + + if ( stream->readFlag() ) // UpdateMask + { + stream->read( &mColor ); + stream->read( &mBrightness ); + mCastShadows = stream->readFlag(); + + stream->read( &mPriority ); + + mLight->unpackExtended( stream ); + + mAnimState.active = stream->readFlag(); + stream->read( &mAnimState.animationPeriod ); + stream->read( &mAnimState.animationPhase ); + stream->read( &mFlareScale ); + } + + if ( stream->readFlag() ) // DatablockMask + { + if ( stream->readFlag() ) + { + SimObjectId id = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); + LightAnimData *datablock = NULL; + + if ( Sim::findObject( id, datablock ) ) + mAnimationData = datablock; + else + { + conn->setLastError( "Light::unpackUpdate() - invalid LightAnimData!" ); + mAnimationData = NULL; + } + } + else + mAnimationData = NULL; + + if ( stream->readFlag() ) + { + SimObjectId id = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); + LightFlareData *datablock = NULL; + + if ( Sim::findObject( id, datablock ) ) + mFlareData = datablock; + else + { + conn->setLastError( "Light::unpackUpdate() - invalid LightCoronaData!" ); + mFlareData = NULL; + } + } + else + mFlareData = NULL; + } + + if ( isProperlyAdded() ) + _conformLights(); +} + +void LightBase::setLightEnabled( bool enabled ) +{ + if ( mIsEnabled != enabled ) + { + mIsEnabled = enabled; + setMaskBits( EnabledMask ); + } +} + +DefineEngineMethod( LightBase, setLightEnabled, void, ( bool state ),, + "@brief Toggles the light on and off\n\n" + + "@param state Turns the light on (true) or off (false)\n" + + "@tsexample\n" + "// Disable the light\n" + "CrystalLight.setLightEnabled(false);\n\n" + "// Renable the light\n" + "CrystalLight.setLightEnabled(true);\n" + + "@endtsexample\n\n" +) +{ + object->setLightEnabled( state ); +} + +//ConsoleMethod( LightBase, setLightEnabled, void, 3, 3, "( bool enabled )\t" +// "Toggles the light on and off." ) +//{ +// object->setLightEnabled( dAtob( argv[2] ) ); +//} + +static ConsoleDocFragment _lbplayAnimation1( + "@brief Plays the light animation assigned to this light with the existing LightAnimData datablock.\n\n" + + "@tsexample\n" + "// Play the animation assigned to this light\n" + "CrystalLight.playAnimation();\n" + "@endtsexample\n\n", + "LightBase", + "void playAnimation();" +); +static ConsoleDocFragment _lbplayAnimation2( + "@brief Plays the light animation on this light using a new LightAnimData. If no LightAnimData " + "is passed the existing one is played.\n\n" + "@param anim Name of the LightAnimData datablock to be played\n\n" + "@tsexample\n" + "// Play the animation using a new LightAnimData datablock\n" + "CrystalLight.playAnimation(SubtlePulseLightAnim);\n" + "@endtsexample\n\n", + "LightBase", + "void playAnimation(LightAnimData anim);" +); +ConsoleMethod( LightBase, playAnimation, void, 2, 3, "( [LightAnimData anim] )\t" + "Plays a light animation on the light. If no LightAnimData is passed the " + "existing one is played." + "@hide") +{ + if ( argc == 2 ) + { + object->playAnimation(); + return; + } + + LightAnimData *animData; + if ( !Sim::findObject( argv[2], animData ) ) + { + Con::errorf( "LightBase::playAnimation() - Invalid LightAnimData '%s'.", argv[2] ); + return; + } + + // Play Animation. + object->playAnimation( animData ); +} + +void LightBase::playAnimation( void ) +{ + if ( !mAnimState.active ) + { + mAnimState.active = true; + setMaskBits( UpdateMask ); + } +} + +void LightBase::playAnimation( LightAnimData *animData ) +{ + // Play Animation. + playAnimation(); + + // Update Datablock? + if ( mAnimationData != animData ) + { + mAnimationData = animData; + setMaskBits( DatablockMask ); + } +} + +ConsoleMethod( LightBase, pauseAnimation, void, 2, 2, "Stops the light animation." ) +{ + object->pauseAnimation(); +} + +void LightBase::pauseAnimation( void ) +{ + if ( mAnimState.active ) + { + mAnimState.active = false; + setMaskBits( UpdateMask ); + } +} \ No newline at end of file diff --git a/Engine/source/T3D/lightBase.h b/Engine/source/T3D/lightBase.h new file mode 100644 index 000000000..a01926edb --- /dev/null +++ b/Engine/source/T3D/lightBase.h @@ -0,0 +1,136 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _LIGHTBASE_H_ +#define _LIGHTBASE_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _LIGHTINFO_H_ +#include "lighting/lightInfo.h" +#endif +#ifndef _ITICKABLE_H_ +#include "core/iTickable.h" +#endif +#ifndef _LIGHTFLAREDATA_H_ +#include "T3D/lightFlareData.h" +#endif +#ifndef _LIGHTANIMDATA_H_ +#include "T3D/lightAnimData.h" +#endif + +class LightAnimData; + +class LightBase : public SceneObject, public ISceneLight, public virtual ITickable +{ + typedef SceneObject Parent; + friend class LightAnimData; + friend class LightFlareData; + +protected: + + bool mIsEnabled; + + ColorF mColor; + + F32 mBrightness; + + bool mCastShadows; + + F32 mPriority; + + LightInfo *mLight; + + LightAnimData *mAnimationData; + LightAnimState mAnimState; + + LightFlareData *mFlareData; + LightFlareState mFlareState; + F32 mFlareScale; + + static bool smRenderViz; + + virtual void _conformLights() {} + + void _onRenderViz( ObjectRenderInst *ri, + SceneRenderState *state, + BaseMatInstance *overrideMat ); + + virtual void _renderViz( SceneRenderState *state ) {} + + enum LightMasks + { + InitialUpdateMask = Parent::NextFreeMask, + EnabledMask = Parent::NextFreeMask << 1, + TransformMask = Parent::NextFreeMask << 2, + UpdateMask = Parent::NextFreeMask << 3, + DatablockMask = Parent::NextFreeMask << 4, + NextFreeMask = Parent::NextFreeMask << 5 + }; + + // SimObject. + virtual void _onSelected(); + virtual void _onUnselected(); + +public: + + LightBase(); + virtual ~LightBase(); + + // SimObject + virtual bool onAdd(); + virtual void onRemove(); + + // ConsoleObject + void inspectPostApply(); + static void initPersistFields(); + DECLARE_CONOBJECT(LightBase); + + // NetObject + U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream ); + void unpackUpdate( NetConnection *conn, BitStream *stream ); + + // ISceneLight + virtual void submitLights( LightManager *lm, bool staticLighting ); + virtual LightInfo* getLight() { return mLight; } + + // SceneObject + virtual void setTransform( const MatrixF &mat ); + virtual void prepRenderImage( SceneRenderState *state ); + + // ITickable + virtual void interpolateTick( F32 delta ); + virtual void processTick(); + virtual void advanceTime( F32 timeDelta ); + + /// Toggles the light on and off. + void setLightEnabled( bool enabled ); + bool getLightEnabled() { return mIsEnabled; }; + + /// Animate the light. + virtual void pauseAnimation( void ); + virtual void playAnimation( void ); + virtual void playAnimation( LightAnimData *animData ); +}; + +#endif // _LIGHTBASE_H_ diff --git a/Engine/source/T3D/lightDescription.cpp b/Engine/source/T3D/lightDescription.cpp new file mode 100644 index 000000000..919f4418d --- /dev/null +++ b/Engine/source/T3D/lightDescription.cpp @@ -0,0 +1,271 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/lightDescription.h" + +#include "lighting/lightManager.h" +#include "T3D/lightFlareData.h" +#include "T3D/lightAnimData.h" +#include "core/stream/bitStream.h" +#include "lighting/lightInfo.h" +#include "console/engineAPI.h" + + +LightDescription::LightDescription() + : color( ColorF::WHITE ), + brightness( 1.0f ), + range( 5.0f ), + castShadows( false ), + animationData( NULL ), + animationDataId( 0 ), + animationPeriod( 1.0f ), + animationPhase( 1.0f ), + flareData( NULL ), + flareDataId( 0 ), + flareScale( 1.0f ) +{ +} + +LightDescription::~LightDescription() +{ + +} + +IMPLEMENT_CO_DATABLOCK_V1( LightDescription ); + +ConsoleDocClass( LightDescription, + "@brief A helper datablock used by classes (such as shapebase) " + "that submit lights to the scene but do not use actual \"LightBase\" objects.\n\n" + + "This datablock stores the properties of that light as fields that can be initialized from script." + + "@tsexample\n" + "// Declare a light description to be used on a rocket launcher projectile\n" + "datablock LightDescription(RocketLauncherLightDesc)\n" + "{\n" + " range = 4.0;\n" + " color = \"1 1 0\";\n" + " brightness = 5.0;\n" + " animationType = PulseLightAnim;\n" + " animationPeriod = 0.25;\n" + "};\n\n" + "// Declare a ProjectileDatablock which uses the light description\n" + "datablock ProjectileData(RocketLauncherProjectile)\n" + "{\n" + " lightDesc = RocketLauncherLightDesc;\n\n" + " projectileShapeName = \"art/shapes/weapons/SwarmGun/rocket.dts\";\n\n" + " directDamage = 30;\n" + " radiusDamage = 30;\n" + " damageRadius = 5;\n" + " areaImpulse = 2500;\n\n" + " // ... remaining ProjectileData fields not listed for this example\n" + "};\n" + "@endtsexample\n\n" + + "@see LightBase\n\n" + "@ingroup Lighting\n" +); + +void LightDescription::initPersistFields() +{ + addGroup( "Light" ); + + addField( "color", TypeColorF, Offset( color, LightDescription ), "Changes the base color hue of the light." ); + addField( "brightness", TypeF32, Offset( brightness, LightDescription ), "Adjusts the lights power, 0 being off completely." ); + addField( "range", TypeF32, Offset( range, LightDescription ), "Controls the size (radius) of the light" ); + addField( "castShadows", TypeBool, Offset( castShadows, LightDescription ), "Enables/disabled shadow casts by this light." ); + + endGroup( "Light" ); + + addGroup( "Light Animation" ); + + addField( "animationType", TYPEID< LightAnimData >(), Offset( animationData, LightDescription ), "Datablock containing light animation information (LightAnimData)" ); + addField( "animationPeriod", TypeF32, Offset( animationPeriod, LightDescription ), "The length of time in seconds for a single playback of the light animation" ); + addField( "animationPhase", TypeF32, Offset( animationPhase, LightDescription ), "The phase used to offset the animation start time to vary the animation of nearby lights." ); + + endGroup( "Light Animation" ); + + addGroup( "Misc" ); + + addField( "flareType", TYPEID< LightFlareData >(), Offset( flareData, LightDescription ), "Datablock containing light flare information (LightFlareData)" ); + addField( "flareScale", TypeF32, Offset( flareScale, LightDescription ), "Globally scales all features of the light flare" ); + + endGroup( "Misc" ); + + LightManager::initLightFields(); + + Parent::initPersistFields(); +} + +void LightDescription::inspectPostApply() +{ + Parent::inspectPostApply(); + + // Hack to allow changing properties in game. + // Do the same work as preload. + animationData = NULL; + flareData = NULL; + + String str; + _preload( false, str ); +} + +bool LightDescription::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + return true; +} +bool LightDescription::preload( bool server, String &errorStr ) +{ + if ( !Parent::preload( server, errorStr ) ) + return false; + + return _preload( server, errorStr ); +} + +void LightDescription::packData( BitStream *stream ) +{ + Parent::packData( stream ); + + stream->write( color ); + stream->write( brightness ); + stream->write( range ); + stream->writeFlag( castShadows ); + + stream->write( animationPeriod ); + stream->write( animationPhase ); + stream->write( flareScale ); + + if ( stream->writeFlag( animationData ) ) + { + stream->writeRangedU32( animationData->getId(), + DataBlockObjectIdFirst, + DataBlockObjectIdLast ); + } + + if ( stream->writeFlag( flareData ) ) + { + stream->writeRangedU32( flareData->getId(), + DataBlockObjectIdFirst, + DataBlockObjectIdLast ); + } +} + +void LightDescription::unpackData( BitStream *stream ) +{ + Parent::unpackData( stream ); + + stream->read( &color ); + stream->read( &brightness ); + stream->read( &range ); + castShadows = stream->readFlag(); + + stream->read( &animationPeriod ); + stream->read( &animationPhase ); + stream->read( &flareScale ); + + if ( stream->readFlag() ) + animationDataId = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); + + if ( stream->readFlag() ) + flareDataId = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); +} + +void LightDescription::submitLight( LightState *state, const MatrixF &xfm, LightManager *lm, SimObject *object ) +{ + LightInfo *li = state->lightInfo; + + li->setRange( range ); + li->setColor( color ); + li->setCastShadows( castShadows ); + li->setTransform( xfm ); + + if ( animationData ) + { + LightAnimState *animState = &state->animState; + + animState->brightness = brightness; + animState->transform = xfm; + animState->color = color; + animState->animationPeriod = animationPeriod; + animState->animationPhase = animationPhase; + + animationData->animate( li, animState ); + } + + lm->registerGlobalLight( li, object ); +} + +void LightDescription::prepRender( SceneRenderState *sceneState, LightState *lightState, const MatrixF &xfm ) +{ + if ( flareData ) + { + LightFlareState *flareState = &lightState->flareState; + flareState->fullBrightness = brightness; + flareState->scale = flareScale; + flareState->lightMat = xfm; + flareState->lightInfo = lightState->lightInfo; + + flareData->prepRender( sceneState, flareState ); + } +} + +bool LightDescription::_preload( bool server, String &errorStr ) +{ + if (!animationData && animationDataId != 0) + if (Sim::findObject(animationDataId, animationData) == false) + Con::errorf(ConsoleLogEntry::General, "LightDescription::onAdd: Invalid packet, bad datablockId(animationData): %d", animationDataId); + + if (!flareData && flareDataId != 0) + if (Sim::findObject(flareDataId, flareData) == false) + Con::errorf(ConsoleLogEntry::General, "LightDescription::onAdd: Invalid packet, bad datablockId(flareData): %d", flareDataId); + + return true; +} + +DefineEngineMethod( LightDescription, apply, void, (),, + "@brief Force an inspectPostApply call for the benefit of tweaking via the console\n\n" + + "Normally this functionality is only exposed to objects via the World Editor, once changes have been made. " + "Exposing apply to script allows you to make changes to it on the fly without the World Editor.\n\n" + + "@note This is intended for debugging and tweaking, not for game play\n\n" + + "@tsexample\n" + "// Change a property of the light description\n" + "RocketLauncherLightDesc.brightness = 10;\n\n" + "// Make it so\n" + "RocketLauncherLightDesc.apply();\n" + + "@endtsexample\n\n" +) +{ + object->inspectPostApply(); +} + +//ConsoleMethod( LightDescription, apply, void, 2, 2, "force an inspectPostApply for the benefit of tweaking via the console" ) +//{ +// object->inspectPostApply(); +//} \ No newline at end of file diff --git a/Engine/source/T3D/lightDescription.h b/Engine/source/T3D/lightDescription.h new file mode 100644 index 000000000..b24ff895a --- /dev/null +++ b/Engine/source/T3D/lightDescription.h @@ -0,0 +1,115 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _LIGHTDESCRIPTION_H_ +#define _LIGHTDESCRIPTION_H_ + +#ifndef _SIMDATABLOCK_H_ +#include "console/simDatablock.h" +#endif +#ifndef _CONSOLETYPES_H_ +#include "console/consoleTypes.h" +#endif +#ifndef _COLOR_H_ +#include "core/color.h" +#endif +#ifndef _LIGHTANIMDATA_H_ +#include "T3D/lightAnimData.h" +#endif +#ifndef _LIGHTFLAREDATA_H_ +#include "T3D/lightFlareData.h" +#endif + +struct LightState +{ + LightInfo *lightInfo; + F32 fullBrightness; + + LightAnimState animState; + LightFlareState flareState; + + void clear() + { + lightInfo = NULL; + fullBrightness = 1.0f; + flareState.clear(); + } + + void setLightInfo( LightInfo *li ) + { + flareState.lightInfo = lightInfo = li; + } +}; + +/// LightDescription is a helper datablock used by classes (such as shapebase) +/// that submit lights to the scene but do not use actual "LightBase" objects. +/// This datablock stores the properties of that light as fields that can be +/// initialized from script. + +class LightAnimData; +class LightFlareData; +class LightManager; +class ISceneLight; + +class LightDescription : public SimDataBlock +{ + typedef SimDataBlock Parent; + +public: + + LightDescription(); + virtual ~LightDescription(); + + DECLARE_CONOBJECT( LightDescription ); + + static void initPersistFields(); + virtual void inspectPostApply(); + + bool onAdd(); + + // SimDataBlock + virtual bool preload( bool server, String &errorStr ); + virtual void packData( BitStream *stream ); + virtual void unpackData( BitStream *stream ); + + //void animateLight( LightState *state ); + void submitLight( LightState *state, const MatrixF &xfm, LightManager *lm, SimObject *object ); + void prepRender( SceneRenderState *sceneState, LightState *lightState, const MatrixF &xfm ); + + bool _preload( bool server, String &errorStr ); + + ColorF color; + F32 brightness; + F32 range; + bool castShadows; + + LightAnimData *animationData; + S32 animationDataId; + F32 animationPeriod; + F32 animationPhase; + + LightFlareData *flareData; + S32 flareDataId; + F32 flareScale; +}; + +#endif // _LIGHTDESCRIPTION_H_ \ No newline at end of file diff --git a/Engine/source/T3D/lightFlareData.cpp b/Engine/source/T3D/lightFlareData.cpp new file mode 100644 index 000000000..8dfd9badc --- /dev/null +++ b/Engine/source/T3D/lightFlareData.cpp @@ -0,0 +1,674 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/lightFlareData.h" + +#include "core/stream/bitStream.h" +#include "console/engineAPI.h" +#include "lighting/lightInfo.h" +#include "math/mathUtils.h" +#include "math/mathIO.h" +#include "scene/sceneRenderState.h" +#include "gfx/gfxOcclusionQuery.h" +#include "gfx/gfxDrawUtil.h" +#include "renderInstance/renderPassManager.h" +#include "T3D/gameBase/gameConnection.h" +#include "T3D/gameBase/processList.h" +#include "collision/collision.h" + + +const U32 LightFlareData::LosMask = STATIC_COLLISION_TYPEMASK | + ShapeBaseObjectType | + StaticShapeObjectType | + ItemObjectType; + + +LightFlareState::~LightFlareState() +{ + delete occlusionQuery; + delete fullPixelQuery; +} + +void LightFlareState::clear() +{ + visChangedTime = 0; + visible = false; + scale = 1.0f; + fullBrightness = 1.0f; + lightMat = MatrixF::Identity; + lightInfo = NULL; + worldRadius = -1.0f; + occlusion = -1.0f; + occlusionQuery = NULL; + fullPixelQuery = NULL; +} + +Point3F LightFlareData::sBasePoints[] = +{ + Point3F( -0.5, 0.5, 0.0 ), + Point3F( -0.5, -0.5, 0.0 ), + Point3F( 0.5, -0.5, 0.0 ), + Point3F( 0.5, 0.5, 0.0 ) +}; + + +IMPLEMENT_CO_DATABLOCK_V1( LightFlareData ); + +ConsoleDocClass( LightFlareData, + "@brief Defines a light flare effect usable by scene lights.\n\n" + + "%LightFlareData is a datablock which defines a type of flare effect. " + "This may then be referenced by other classes which support the rendering " + "of a flare: Sun, ScatterSky, LightBase.\n\n" + + "A flare contains one or more elements defined in the element* named fields " + "of %LightFlareData, with a maximum of ten elements. Each element is rendered " + "as a 2D sprite in screenspace.\n\n" + + "@tsexample\n" + "// example from Full Template, core/art/datablocks/lights.cs\n" + "datablock LightFlareData( LightFlareExample0 )\n" + "{\n" + " overallScale = 2.0;\n" + " flareEnabled = true;\n" + " renderReflectPass = true;\n" + " flareTexture = \"./../special/lensFlareSheet1\";\n" + " occlusionRadius = 0.25;\n" + " \n" + " elementRect[0] = \"0 512 512 512\";\n" + " elementDist[0] = 0.0;\n" + " elementScale[0] = 0.5;\n" + " elementTint[0] = \"1.0 1.0 1.0\";\n" + " elementRotate[0] = false;\n" + " elementUseLightColor[0] = false;\n" + " \n" + " elementRect[1] = \"512 0 512 512\";\n" + " elementDist[1] = 0.0;\n" + " elementScale[1] = 2.0;\n" + " elementTint[1] = \"0.5 0.5 0.5\";\n" + " elementRotate[1] = false;\n" + " elementUseLightColor[1] = false;\n" + "};\n" + "@endtsexample\n" + "The elementDist field defines where along the flare's beam the element appears. " + "A distance of 0.0 is directly over the light source, a distance of 1.0 " + "is at the screen center, and a distance of 2.0 is at the position of the " + "light source mirrored across the screen center.\n" + "@image html images/lightFlareData_diagram.png\n" + "@ingroup Lighting" +); + +LightFlareData::LightFlareData() + : mFlareEnabled( true ), + mElementCount( 0 ), + mScale( 1.0f ), + mOcclusionRadius( 0.0f ), + mRenderReflectPass( true ) +{ + dMemset( mElementRect, 0, sizeof( RectF ) * MAX_ELEMENTS ); + dMemset( mElementScale, 0, sizeof( F32 ) * MAX_ELEMENTS ); + dMemset( mElementTint, 0, sizeof( ColorF ) * MAX_ELEMENTS ); + dMemset( mElementRotate, 0, sizeof( bool ) * MAX_ELEMENTS ); + dMemset( mElementUseLightColor, 0, sizeof( bool ) * MAX_ELEMENTS ); + + for ( U32 i = 0; i < MAX_ELEMENTS; i++ ) + mElementDist[i] = -1.0f; +} + +LightFlareData::~LightFlareData() +{ +} + +void LightFlareData::initPersistFields() +{ + addGroup( "LightFlareData" ); + + addField( "overallScale", TypeF32, Offset( mScale, LightFlareData ), + "Size scale applied to all elements of the flare." ); + + addField( "occlusionRadius", TypeF32, Offset( mOcclusionRadius, LightFlareData ), + "If positive an occlusion query is used to test flare visibility, else it uses simple raycasts." ); + + addField( "renderReflectPass", TypeBool, Offset( mRenderReflectPass, LightFlareData ), + "If false the flare does not render in reflections, else only non-zero distance elements are rendered." ); + + endGroup( "LightFlareData" ); + + addGroup( "FlareElements" ); + + addField( "flareEnabled", TypeBool, Offset( mFlareEnabled, LightFlareData ), + "Allows the user to disable this flare globally for any lights referencing it." ); + + addField( "flareTexture", TypeImageFilename, Offset( mFlareTextureName, LightFlareData ), + "The texture / sprite sheet for this flare." ); + + addArray( "Elements", MAX_ELEMENTS ); + + addField( "elementRect", TypeRectF, Offset( mElementRect, LightFlareData ), MAX_ELEMENTS, + "A rectangle specified in pixels of the flareTexture image." ); + + addField( "elementDist", TypeF32, Offset( mElementDist, LightFlareData ), MAX_ELEMENTS, + "Where this element appears along the flare beam." ); + + addField( "elementScale", TypeF32, Offset( mElementScale, LightFlareData ), MAX_ELEMENTS, + "Size scale applied to this element." ); + + addField( "elementTint", TypeColorF, Offset( mElementTint, LightFlareData ), MAX_ELEMENTS, + "Used to modulate this element's color if elementUseLightColor " + "is false.\n" + "@see elementUseLightColor" ); + + addField( "elementRotate", TypeBool, Offset( mElementRotate, LightFlareData ), MAX_ELEMENTS, + "Defines if this element orients to point along the flare beam " + "or if it is always upright." ); + + addField( "elementUseLightColor", TypeBool, Offset( mElementUseLightColor, LightFlareData ), MAX_ELEMENTS, + "If true this element's color is modulated by the light color. " + "If false, elementTint will be used.\n" + "@see elementTint" ); + + endArray( "FlareElements" ); + + endGroup( "Flares" ); + + Parent::initPersistFields(); +} + +void LightFlareData::inspectPostApply() +{ + Parent::inspectPostApply(); + + // Hack to allow changing properties in game. + // Do the same work as preload. + + String str; + _preload( false, str ); +} + +bool LightFlareData::preload( bool server, String &errorStr ) +{ + if ( !Parent::preload( server, errorStr ) ) + return false; + + return _preload( server, errorStr ); +} + +void LightFlareData::packData( BitStream *stream ) +{ + Parent::packData( stream ); + + stream->writeFlag( mFlareEnabled ); + stream->write( mFlareTextureName ); + stream->write( mScale ); + stream->write( mOcclusionRadius ); + stream->writeFlag( mRenderReflectPass ); + + stream->write( mElementCount ); + + for ( U32 i = 0; i < mElementCount; i++ ) + { + mathWrite( *stream, mElementRect[i] ); + stream->write( mElementDist[i] ); + stream->write( mElementScale[i] ); + stream->write( mElementTint[i] ); + stream->writeFlag( mElementRotate[i] ); + stream->writeFlag( mElementUseLightColor[i] ); + } +} + +void LightFlareData::unpackData( BitStream *stream ) +{ + Parent::unpackData( stream ); + + mFlareEnabled = stream->readFlag(); + stream->read( &mFlareTextureName ); + stream->read( &mScale ); + stream->read( &mOcclusionRadius ); + mRenderReflectPass = stream->readFlag(); + + stream->read( &mElementCount ); + + for ( U32 i = 0; i < mElementCount; i++ ) + { + mathRead( *stream, &mElementRect[i] ); + stream->read( &mElementDist[i] ); + stream->read( &mElementScale[i] ); + stream->read( &mElementTint[i] ); + mElementRotate[i] = stream->readFlag(); + mElementUseLightColor[i] = stream->readFlag(); + } +} + +bool LightFlareData::_testVisibility(const SceneRenderState *state, LightFlareState *flareState, U32 *outVisDelta, F32 *outOcclusionFade, Point3F *outLightPosSS) +{ + // Reflections use the results from the last forward + // render so we don't need multiple queries. + if ( state->isReflectPass() ) + { + *outOcclusionFade = flareState->occlusion; + *outVisDelta = Sim::getCurrentTime() - flareState->visChangedTime; + return flareState->visible; + } + + // Initialize it to something first. + *outOcclusionFade = 0; + + // First check to see if the flare point + // is on scren at all... if not then return + // the last result. + const Point3F &lightPos = flareState->lightMat.getPosition(); + const RectI &viewport = GFX->getViewport(); + bool onScreen = MathUtils::mProjectWorldToScreen( lightPos, outLightPosSS, viewport, GFX->getWorldMatrix(), state->getSceneManager()->getNonClipProjection() ); + + // It is onscreen, so raycast as a simple occlusion test. + const LightInfo *lightInfo = flareState->lightInfo; + const bool isVectorLight = lightInfo->getType() == LightInfo::Vector; + + const bool useOcclusionQuery = isVectorLight ? flareState->worldRadius > 0.0f : mOcclusionRadius > 0.0f; + bool needsRaycast = true; + + // NOTE: if hardware does not support HOQ it will return NULL + // and we will retry every time but there is not currently a good place + // for one-shot initialization of LightFlareState + if ( useOcclusionQuery ) + { + if ( flareState->occlusionQuery == NULL ) + flareState->occlusionQuery = GFX->createOcclusionQuery(); + if ( flareState->fullPixelQuery == NULL ) + flareState->fullPixelQuery = GFX->createOcclusionQuery(); + + // Always treat light as onscreen if using HOQ + // it will be faded out if offscreen anyway. + onScreen = true; + + // NOTE: These queries frame lock us as we block to get the + // results. This is ok as long as long as we're not too GPU + // bound... else we waste CPU time here waiting for it when + // we could have been doing other CPU work instead. + + // Test the hardware queries for rendered pixels. + U32 pixels = 0, fullPixels = 0; + GFXOcclusionQuery::OcclusionQueryStatus status = flareState->occlusionQuery->getStatus( true, &pixels ); + flareState->fullPixelQuery->getStatus( true, &fullPixels ); + if ( status != GFXOcclusionQuery::Occluded && fullPixels != 0 ) + *outOcclusionFade = mClampF( (F32)pixels / (F32)fullPixels, 0.0f, 1.0f ); + + // If we got a result then we don't need to fallback to the raycast. + if ( status != GFXOcclusionQuery::Unset ) + needsRaycast = false; + + // Setup the new queries. + RenderPassManager *rpm = state->getRenderPass(); + OccluderRenderInst *ri = rpm->allocInst(); + ri->type = RenderPassManager::RIT_Occluder; + ri->query = flareState->occlusionQuery; + ri->query2 = flareState->fullPixelQuery; + ri->isSphere = true; + ri->position = lightPos; + if ( isVectorLight && flareState->worldRadius > 0.0f ) + ri->scale.set( flareState->worldRadius ); + else + ri->scale.set( mOcclusionRadius ); + ri->orientation = rpm->allocUniqueXform( lightInfo->getTransform() ); + + // Submit the queries. + state->getRenderPass()->addInst( ri ); + } + + const Point3F &camPos = state->getCameraPosition(); + + if ( needsRaycast ) + { + // Use a raycast to determine occlusion. + GameConnection *conn = GameConnection::getConnectionToServer(); + if ( !conn ) + return false; + + const bool fps = conn->isFirstPerson(); + GameBase *control = conn->getControlObject(); + if ( control && fps ) + control->disableCollision(); + + RayInfo rayInfo; + + if ( !gClientContainer.castRay( camPos, lightPos, LosMask, &rayInfo ) ) + *outOcclusionFade = 1.0f; + + if ( control && fps ) + control->enableCollision(); + } + + // The raycast and hardware occlusion query only calculate if + // the flare is on screen... if does not account for being + // partially offscreen. + // + // The code here clips a box against the viewport to + // get an approximate percentage of onscreen area. + // + F32 worldRadius = flareState->worldRadius > 0 ? flareState->worldRadius : mOcclusionRadius; + if ( worldRadius > 0.0f ) + { + F32 dist = ( camPos - lightPos ).len(); + F32 pixelRadius = state->projectRadius(dist, worldRadius); + + RectI visRect( outLightPosSS->x - pixelRadius, outLightPosSS->y - pixelRadius, + pixelRadius * 2.0f, pixelRadius * 2.0f ); + F32 fullArea = visRect.area(); + + if ( visRect.intersect( viewport ) ) + { + F32 visArea = visRect.area(); + *outOcclusionFade *= visArea / fullArea; + onScreen = true; + } + else + *outOcclusionFade = 0.0f; + } + + const bool lightVisible = onScreen && *outOcclusionFade > 0.0f; + + // To perform a fade in/out when we gain or lose visibility + // we must update/store the visibility state and time. + const U32 currentTime = Sim::getCurrentTime(); + if ( lightVisible != flareState->visible ) + { + flareState->visible = lightVisible; + flareState->visChangedTime = currentTime; + } + + // Return the visibility delta for time fading. + *outVisDelta = currentTime - flareState->visChangedTime; + + // Store the final occlusion fade so that it can + // be used in reflection rendering later. + flareState->occlusion = *outOcclusionFade; + + return lightVisible; +} + +void LightFlareData::prepRender( SceneRenderState *state, LightFlareState *flareState ) +{ + PROFILE_SCOPE( LightFlareData_prepRender ); + + const LightInfo *lightInfo = flareState->lightInfo; + + if ( mIsZero( flareState->fullBrightness ) || + mIsZero( lightInfo->getBrightness() ) ) + return; + + // Figure out the element count to render. + U32 elementCount = mElementCount; + const bool isReflectPass = state->isReflectPass(); + if ( isReflectPass ) + { + // Then we don't render anything this pass. + if ( !mRenderReflectPass ) + return; + + // Find the zero distance elements which make + // up the corona of the light flare. + elementCount = 0.0f; + for ( U32 i=0; i < mElementCount; i++ ) + if ( mIsZero( mElementDist[i] ) ) + elementCount++; + } + + // Better have something to render. + if ( elementCount == 0 ) + return; + + U32 visDelta = U32_MAX; + F32 occlusionFade = 1.0f; + Point3F lightPosSS; + bool lightVisible = _testVisibility( state, flareState, &visDelta, &occlusionFade, &lightPosSS ); + + // We can only skip rendering if the light is not + // visible, and it has elapsed the fade out time. + if ( mIsZero( occlusionFade ) || + !lightVisible && visDelta > FadeOutTime ) + return; + + const RectI &viewport = GFX->getViewport(); + Point3F oneOverViewportExtent( 1.0f / (F32)viewport.extent.x, 1.0f / (F32)viewport.extent.y, 0.0f ); + + // Really convert it to screen space. + lightPosSS.y -= viewport.point.y; + lightPosSS *= oneOverViewportExtent; + lightPosSS = ( lightPosSS * 2.0f ) - Point3F::One; + lightPosSS.y = -lightPosSS.y; + lightPosSS.z = 0.0f; + + Point3F flareVec( -lightPosSS ); + const F32 flareLength = flareVec.len(); + if ( flareLength > 0.0f ) + flareVec *= 1.0f / flareLength; + + // Setup the flare quad points. + Point3F rotatedBasePoints[4]; + dMemcpy(rotatedBasePoints, sBasePoints, sizeof( sBasePoints )); + + // Rotate the flare quad. + F32 rot = mAcos( -1.0f * flareVec.x ); + rot *= flareVec.y > 0.0f ? -1.0f : 1.0f; + MathUtils::vectorRotateZAxis( rot, rotatedBasePoints, 4 ); + + // Here we calculate a the light source's influence on + // the effect's size and brightness. + + // Scale based on the current light brightness compared to its normal output. + F32 lightSourceBrightnessScale = lightInfo->getBrightness() / flareState->fullBrightness; + + const Point3F &camPos = state->getCameraPosition(); + const Point3F &lightPos = flareState->lightMat.getPosition(); + const bool isVectorLight = lightInfo->getType() == LightInfo::Vector; + + // Scale based on world space distance from camera to light source. + F32 distToCamera = ( camPos - lightPos ).len(); + F32 lightSourceWSDistanceScale = isVectorLight && distToCamera > 0.0f ? 1.0f : getMin( 10.0f / distToCamera, 10.0f ); + + // Scale based on screen space distance from screen position of light source to the screen center. + F32 lightSourceSSDistanceScale = getMax( ( 1.5f - flareLength ) / 1.5f, 0.0f ); + + // Scale based on recent visibility changes, fading in or out. + F32 fadeInOutScale = 1.0f; + if ( lightVisible && + visDelta < FadeInTime && + flareState->occlusion > 0.0f ) + fadeInOutScale = (F32)visDelta / (F32)FadeInTime; + else if ( !lightVisible && + visDelta < FadeOutTime ) + fadeInOutScale = 1.0f - (F32)visDelta / (F32)FadeOutTime; + + // This combined scale influences the size of all elements this effect renders. + // Note we also add in a scale that is user specified in the Light. + F32 lightSourceIntensityScale = lightSourceBrightnessScale * + lightSourceWSDistanceScale * + lightSourceSSDistanceScale * + fadeInOutScale * + flareState->scale * + occlusionFade; + + if ( mIsZero( lightSourceIntensityScale ) ) + return; + + // The baseColor which modulates the color of all elements. + // + // These are the factors which affect the "alpha" of the flare effect. + // Modulate more in as appropriate. + ColorF baseColor = ColorF::WHITE * lightSourceBrightnessScale * occlusionFade; + + // Setup the vertex buffer for the maximum flare elements. + const U32 vertCount = 4 * mElementCount; + if ( flareState->vertBuffer.isNull() || + flareState->vertBuffer->mNumVerts != vertCount ) + flareState->vertBuffer.set( GFX, vertCount, GFXBufferTypeDynamic ); + + GFXVertexPCT *vert = flareState->vertBuffer.lock(); + + const Point2F oneOverTexSize( 1.0f / (F32)mFlareTexture.getWidth(), 1.0f / (F32)mFlareTexture.getHeight() ); + + for ( U32 i = 0; i < mElementCount; i++ ) + { + // Skip non-zero elements for reflections. + if ( isReflectPass && mElementDist[i] > 0.0f ) + continue; + + Point3F *basePos = mElementRotate[i] ? rotatedBasePoints : sBasePoints; + + ColorF color( baseColor * mElementTint[i] ); + if ( mElementUseLightColor[i] ) + color *= lightInfo->getColor(); + color.clamp(); + + Point3F pos( lightPosSS + flareVec * mElementDist[i] * flareLength ); + + const RectF &rect = mElementRect[i]; + Point3F size( rect.extent.x, rect.extent.y, 1.0f ); + size *= mElementScale[i] * mScale * lightSourceIntensityScale; + + AssertFatal( size.x >= 0.0f, "LightFlareData::prepRender - Got a negative element size?" ); + + if ( size.x < 100.0f ) + { + F32 alphaScale = mPow( size.x / 100.0f, 2 ); + color *= alphaScale; + } + + Point2F texCoordMin, texCoordMax; + texCoordMin = rect.point * oneOverTexSize; + texCoordMax = ( rect.point + rect.extent ) * oneOverTexSize; + + size.x = getMax( size.x, 1.0f ); + size.y = getMax( size.y, 1.0f ); + size *= oneOverViewportExtent; + + vert->color = color; + vert->point = ( basePos[0] * size ) + pos; + vert->texCoord.set( texCoordMin.x, texCoordMax.y ); + vert++; + + vert->color = color; + vert->point = ( basePos[1] * size ) + pos; + vert->texCoord.set( texCoordMax.x, texCoordMax.y ); + vert++; + + vert->color = color; + vert->point = ( basePos[2] * size ) + pos; + vert->texCoord.set( texCoordMax.x, texCoordMin.y ); + vert++; + + vert->color = color; + vert->point = ( basePos[3] * size ) + pos; + vert->texCoord.set( texCoordMin.x, texCoordMin.y ); + vert++; + } + + flareState->vertBuffer.unlock(); + + RenderPassManager *rpm = state->getRenderPass(); + + // Create and submit the render instance. + ParticleRenderInst *ri = rpm->allocInst(); + ri->type = RenderPassManager::RIT_Particle; + ri->vertBuff = &flareState->vertBuffer; + ri->primBuff = &mFlarePrimBuffer; + ri->translucentSort = true; + ri->sortDistSq = ( lightPos - camPos ).lenSquared(); + ri->modelViewProj = &MatrixF::Identity; + ri->bbModelViewProj = &MatrixF::Identity; + ri->count = elementCount; + ri->blendStyle = ParticleRenderInst::BlendGreyscale; + ri->diffuseTex = mFlareTexture; + ri->softnessDistance = 1.0f; + ri->defaultKey = ri->diffuseTex ? (U32)ri->diffuseTex : (U32)ri->vertBuff; // Sort by texture too. + + // NOTE: Offscreen partical code is currently disabled. + ri->systemState = PSS_AwaitingHighResDraw; + + rpm->addInst( ri ); +} + +bool LightFlareData::_preload( bool server, String &errorStr ) +{ + mElementCount = 0; + for ( U32 i = 0; i < MAX_ELEMENTS; i++ ) + { + if ( mElementDist[i] == -1 ) + break; + mElementCount = i + 1; + } + + if ( mElementCount > 0 ) + _makePrimBuffer( &mFlarePrimBuffer, mElementCount ); + + if ( !server ) + { + if ( mFlareTextureName.isNotEmpty() ) + mFlareTexture.set( mFlareTextureName, &GFXDefaultStaticDiffuseProfile, "FlareTexture" ); + } + + return true; +} + +void LightFlareData::_makePrimBuffer( GFXPrimitiveBufferHandle *pb, U32 count ) +{ + // create index buffer based on that size + U32 indexListSize = count * 6; // 6 indices per particle + U16 *indices = new U16[ indexListSize ]; + + for ( U32 i = 0; i < count; i++ ) + { + // this index ordering should be optimal (hopefully) for the vertex cache + U16 *idx = &indices[i*6]; + volatile U32 offset = i * 4; // set to volatile to fix VC6 Release mode compiler bug + idx[0] = 0 + offset; + idx[1] = 1 + offset; + idx[2] = 3 + offset; + idx[3] = 1 + offset; + idx[4] = 3 + offset; + idx[5] = 2 + offset; + } + + U16 *ibIndices; + GFXBufferType bufferType = GFXBufferTypeStatic; + +#ifdef TORQUE_OS_XENON + // Because of the way the volatile buffers work on Xenon this is the only + // way to do this. + bufferType = GFXBufferTypeVolatile; +#endif + pb->set( GFX, indexListSize, 0, bufferType ); + pb->lock( &ibIndices ); + dMemcpy( ibIndices, indices, indexListSize * sizeof(U16) ); + pb->unlock(); + + delete [] indices; +} + +DefineEngineMethod( LightFlareData, apply, void, (),, + "Intended as a helper to developers and editor scripts.\n" + "Force trigger an inspectPostApply" + ) +{ + object->inspectPostApply(); +} \ No newline at end of file diff --git a/Engine/source/T3D/lightFlareData.h b/Engine/source/T3D/lightFlareData.h new file mode 100644 index 000000000..a450d4e56 --- /dev/null +++ b/Engine/source/T3D/lightFlareData.h @@ -0,0 +1,134 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _LIGHTFLAREDATA_H_ +#define _LIGHTFLAREDATA_H_ + +#ifndef _SIMDATABLOCK_H_ +#include "console/simDatablock.h" +#endif +#ifndef _GFXTEXTUREHANDLE_H_ +#include "gfx/gfxTextureHandle.h" +#endif +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif +#ifndef _CONSOLETYPES_H_ +#include "console/consoleTypes.h" +#endif +#ifndef _GFXVERTEXBUFFER_H_ +#include "gfx/gfxVertexBuffer.h" +#endif +#ifndef _GFXSTATEBLOCK_H_ +#include "gfx/gfxStateBlock.h" +#endif + +class LightInfo; +struct ObjectRenderInst; +class SceneRenderState; +class BaseMatInstance; +class GFXOcclusionQuery; + +struct LightFlareState +{ + ~LightFlareState(); + void clear(); + + /// Object calling LightFlareData::prepRender fills these in! + F32 scale; + F32 fullBrightness; + MatrixF lightMat; + LightInfo *lightInfo; + F32 worldRadius; + + /// Used internally by LightFlareData! + U32 visChangedTime; + bool visible; + F32 occlusion; + GFXVertexBufferHandle vertBuffer; + GFXOcclusionQuery *occlusionQuery; + GFXOcclusionQuery *fullPixelQuery; +}; + +class LightFlareData : public SimDataBlock +{ + typedef SimDataBlock Parent; + + #define MAX_ELEMENTS 20 + +public: + + LightFlareData(); + virtual ~LightFlareData(); + + DECLARE_CONOBJECT( LightFlareData ); + + static void initPersistFields(); + virtual void inspectPostApply(); + + // SimDataBlock + virtual bool preload( bool server, String &errorStr ); + virtual void packData( BitStream *stream ); + virtual void unpackData( BitStream *stream ); + + /// Submits render instances for corona and flare effects. + void prepRender( SceneRenderState *state, LightFlareState *flareState ); + +protected: + + bool _testVisibility( const SceneRenderState *state, LightFlareState *flareState, + U32 *outVisDelta, F32 *outOcclusionFade, Point3F *outLightPosSS ); + + bool _preload( bool server, String &errorStr ); + void _makePrimBuffer( GFXPrimitiveBufferHandle *pb, U32 count ); + void _renderCorona( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); + +protected: + + static const U32 LosMask; + static const U32 FadeOutTime = 20; + static const U32 FadeInTime = 125; + static Point3F sBasePoints[4]; + + // Fields... + + F32 mScale; + bool mFlareEnabled; + String mFlareTextureName; + GFXTexHandle mFlareTexture; + F32 mOcclusionRadius; + bool mRenderReflectPass; + + RectF mElementRect[MAX_ELEMENTS]; + F32 mElementDist[MAX_ELEMENTS]; + F32 mElementScale[MAX_ELEMENTS]; + ColorF mElementTint[MAX_ELEMENTS]; + bool mElementRotate[MAX_ELEMENTS]; + bool mElementUseLightColor[MAX_ELEMENTS]; + +protected: + + U32 mElementCount; + GFXPrimitiveBufferHandle mFlarePrimBuffer; +}; + +#endif // _LIGHTFLAREDATA_H_ \ No newline at end of file diff --git a/Engine/source/T3D/missionArea.cpp b/Engine/source/T3D/missionArea.cpp new file mode 100644 index 000000000..b8d4d6f6b --- /dev/null +++ b/Engine/source/T3D/missionArea.cpp @@ -0,0 +1,210 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "T3D/missionArea.h" +#include "console/consoleTypes.h" +#include "core/stream/bitStream.h" +#include "math/mathIO.h" +#include "console/engineAPI.h" + +IMPLEMENT_CO_NETOBJECT_V1(MissionArea); + +ConsoleDocClass( MissionArea, + "@brief Level object which defines the boundaries of the level.\n\n" + + "This is a simple box with starting points, width, depth, and height. It does not have " + "any default functionality. Instead, when objects hit the boundaries certain " + "script callbacks will be made allowing you to control the reaction.\n\n" + + "@tsexample\n" + "new MissionArea(GlobalMissionArea)\n" + "{\n" + " Area = \"-152 -352 1008 864\";\n" + " flightCeiling = \"300\";\n" + " flightCeilingRange = \"20\";\n" + " canSaveDynamicFields = \"1\";\n" + " enabled = \"1\";\n" + " TypeBool locked = \"false\";\n" + "};\n" + "@endtsexample\n\n" + + "@ingroup enviroMisc\n" +); + +RectI MissionArea::smMissionArea(Point2I(768, 768), Point2I(512, 512)); + +//------------------------------------------------------------------------------ + +MissionArea::MissionArea() +{ + mArea.set(Point2I(768, 768), Point2I(512, 512)); + mNetFlags.set(Ghostable | ScopeAlways); + + mFlightCeiling = 2000; + mFlightCeilingRange = 50; +} + +//------------------------------------------------------------------------------ + +void MissionArea::setArea(const RectI & area) +{ + // set it + mArea = MissionArea::smMissionArea = area; + + // pass along.. + if(isServerObject()) + mNetFlags.set(UpdateMask); +} + +//------------------------------------------------------------------------------ + +MissionArea * MissionArea::getServerObject() +{ + SimSet * scopeAlwaysSet = Sim::getGhostAlwaysSet(); + for(SimSet::iterator itr = scopeAlwaysSet->begin(); itr != scopeAlwaysSet->end(); itr++) + { + MissionArea * ma = dynamic_cast(*itr); + if(ma) + { + AssertFatal(ma->isServerObject(), "MissionArea::getServerObject: found client object in ghost always set!"); + return(ma); + } + } + return(0); +} + +//------------------------------------------------------------------------------ + +bool MissionArea::onAdd() +{ + if(isServerObject() && MissionArea::getServerObject()) + { + Con::errorf(ConsoleLogEntry::General, "MissionArea::onAdd - MissionArea already instantiated!"); + return(false); + } + + if(!Parent::onAdd()) + return(false); + + setArea(mArea); + return(true); +} + +//------------------------------------------------------------------------------ + +void MissionArea::inspectPostApply() +{ + // Set Parent. + Parent::inspectPostApply(); + + setMaskBits(UpdateMask); +} + +//------------------------------------------------------------------------------ + +void MissionArea::initPersistFields() +{ + addGroup("Dimensions"); + addField("area", TypeRectI, Offset(mArea, MissionArea), "Four corners (X1, X2, Y1, Y2) that makes up the level's boundaries"); + addField("flightCeiling", TypeF32, Offset(mFlightCeiling, MissionArea), "Represents the top of the mission area, used by FlyingVehicle. "); + addField("flightCeilingRange", TypeF32, Offset(mFlightCeilingRange, MissionArea), "Distance from ceiling before FlyingVehicle thrust is cut off. "); + endGroup("Dimensions"); + + Parent::initPersistFields(); +} + + +//------------------------------------------------------------------------------ + +void MissionArea::unpackUpdate(NetConnection *, BitStream * stream) +{ + // ghost (initial) and regular updates share flag.. + if(stream->readFlag()) + { + mathRead(*stream, &mArea); + stream->read(&mFlightCeiling); + stream->read(&mFlightCeilingRange); + } +} + +U32 MissionArea::packUpdate(NetConnection *, U32 mask, BitStream * stream) +{ + if(stream->writeFlag(mask & UpdateMask)) + { + mathWrite(*stream, mArea); + stream->write(mFlightCeiling); + stream->write(mFlightCeilingRange); + } + return(0); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction(getMissionAreaServerObject, MissionArea*, (),, + "Get the MissionArea object, if any.\n\n" + "@ingroup enviroMisc\n\n") +{ + return MissionArea::getServerObject(); +} + +DefineEngineMethod( MissionArea, getArea, const char *, (),, + "Returns 4 fields: starting x, starting y, extents x, extents y.\n") +{ + char* returnBuffer = Con::getReturnBuffer(48); + + RectI area = object->getArea(); + dSprintf(returnBuffer, sizeof(returnBuffer), "%d %d %d %d", area.point.x, area.point.y, area.extent.x, area.extent.y); + return(returnBuffer); +} + +DefineEngineMethod( MissionArea, setArea, void, (S32 x, S32 y, S32 width, S32 height),, + "@brief - Defines the size of the MissionArea\n\n" + "param x Starting X coordinate position for MissionArea\n" + "param y Starting Y coordinate position for MissionArea\n" + "param width New width of the MissionArea\n" + "param height New height of the MissionArea\n" + "@note Only the server object may be set.\n" + ) +{ + if(object->isClientObject()) + { + Con::errorf(ConsoleLogEntry::General, "MissionArea::cSetArea - cannot alter client object!"); + return; + } + + RectI rect; + rect.point.x = x; + rect.point.y = y; + rect.extent.x = width; + rect.extent.y = height; + + object->setArea(rect); +} + +DefineEngineMethod( MissionArea, postApply, void, (),, + "Intended as a helper to developers and editor scripts.\n" + "Force trigger an inspectPostApply. This will transmit " + "material and other fields ( not including nodes ) to client objects." + ) +{ + object->inspectPostApply(); +} \ No newline at end of file diff --git a/Engine/source/T3D/missionArea.h b/Engine/source/T3D/missionArea.h new file mode 100644 index 000000000..d4ee5e294 --- /dev/null +++ b/Engine/source/T3D/missionArea.h @@ -0,0 +1,75 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MISSIONAREA_H_ +#define _MISSIONAREA_H_ + +#ifndef _NETOBJECT_H_ +#include "sim/netObject.h" +#endif + +class MissionArea : public NetObject +{ + private: + typedef NetObject Parent; + RectI mArea; + + F32 mFlightCeiling; + F32 mFlightCeilingRange; + + public: + MissionArea(); + + static RectI smMissionArea; + + static MissionArea * getServerObject(); + + F32 getFlightCeiling() const { return mFlightCeiling; } + F32 getFlightCeilingRange() const { return mFlightCeilingRange; } + + // + const RectI & getArea(){return(mArea);} + void setArea(const RectI & area); + + /// @name SimObject Inheritance + /// @{ + bool onAdd(); + + void inspectPostApply(); + + static void initPersistFields(); + /// @} + + /// @name NetObject Inheritance + /// @{ + enum NetMaskBits { + UpdateMask = BIT(0) + }; + + U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream); + void unpackUpdate(NetConnection *conn, BitStream *stream); + /// @} + + DECLARE_CONOBJECT(MissionArea); +}; + +#endif diff --git a/Engine/source/T3D/missionMarker.cpp b/Engine/source/T3D/missionMarker.cpp new file mode 100644 index 000000000..431947b27 --- /dev/null +++ b/Engine/source/T3D/missionMarker.cpp @@ -0,0 +1,645 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "T3D/missionMarker.h" +#include "console/consoleTypes.h" +#include "core/color.h" +#include "console/engineAPI.h" + +extern bool gEditingMission; +IMPLEMENT_CO_DATABLOCK_V1(MissionMarkerData); + +ConsoleDocClass( MissionMarkerData, + "@brief A very basic class containing information used by MissionMarker objects for rendering\n\n" + + "MissionMarkerData, is an extremely barebones class derived from ShapeBaseData. It is solely used by " + "MissionMarker classes (such as SpawnSphere), so that you can see the object while editing a level.\n\n" + + "@tsexample\n" + "datablock MissionMarkerData(SpawnSphereMarker)\n" + "{\n" + " category = \"Misc\";\n" + " shapeFile = \"core/art/shapes/octahedron.dts\";\n" + "};\n" + "@endtsexample\n\n" + + "@see MissionMarker\n\n" + "@see SpawnSphere\n\n" + "@see WayPoint\n\n" + "@ingroup enviroMisc\n" +); + +//------------------------------------------------------------------------------ +// Class: MissionMarker +//------------------------------------------------------------------------------ +IMPLEMENT_CO_NETOBJECT_V1(MissionMarker); + +ConsoleDocClass( MissionMarker, + "@brief This is a base class for all \"marker\" related objets. It is a 3D representation of a point in the level.\n\n" + + "The main use of a MissionMarker is to represent a point in 3D space with a mesh and basic ShapeBase information. " + "If you simply need to mark a spot in your level, with no overhead from additional fields, this is a useful object.\n\n" + + "@tsexample\n" + "new MissionMarker()\n" + "{\n" + " dataBlock = \"WayPointMarker\";\n" + " position = \"295.699 -171.817 280.124\";\n" + " rotation = \"0 0 -1 13.8204\";\n" + " scale = \"1 1 1\";\n" + " isRenderEnabled = \"true\";\n" + " canSaveDynamicFields = \"1\";\n" + " enabled = \"1\";\n" + "};\n" + "@endtsexample\n\n" + + "@note MissionMarkers will not add themselves to the scene except when in the editor.\n\n" + + "@see MissionMarkerData\n\n" + "@see SpawnSphere\n\n" + "@see WayPoint\n\n" + "@ingroup enviroMisc\n" +); + +MissionMarker::MissionMarker() +{ + mTypeMask |= StaticObjectType; + mDataBlock = 0; + mAddedToScene = false; + mNetFlags.set(Ghostable | ScopeAlways); +} + +bool MissionMarker::onAdd() +{ + if(!Parent::onAdd() || !mDataBlock) + return(false); + + if(gEditingMission) + { + addToScene(); + mAddedToScene = true; + } + + return(true); +} + +void MissionMarker::onRemove() +{ + if( mAddedToScene ) + { + removeFromScene(); + mAddedToScene = false; + } + + Parent::onRemove(); +} + +void MissionMarker::inspectPostApply() +{ + Parent::inspectPostApply(); + setMaskBits(PositionMask); +} + +void MissionMarker::onEditorEnable() +{ + if(!mAddedToScene) + { + addToScene(); + mAddedToScene = true; + } +} + +void MissionMarker::onEditorDisable() +{ + if(mAddedToScene) + { + removeFromScene(); + mAddedToScene = false; + } +} + +bool MissionMarker::onNewDataBlock( GameBaseData *dptr, bool reload ) +{ + mDataBlock = dynamic_cast( dptr ); + if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) ) + return(false); + scriptOnNewDataBlock(); + return(true); +} + +void MissionMarker::setTransform(const MatrixF& mat) +{ + Parent::setTransform(mat); + setMaskBits(PositionMask); +} + +U32 MissionMarker::packUpdate(NetConnection * con, U32 mask, BitStream * stream) +{ + U32 retMask = Parent::packUpdate(con, mask, stream); + if(stream->writeFlag(mask & PositionMask)) + { + stream->writeAffineTransform(mObjToWorld); + mathWrite(*stream, mObjScale); + } + + return(retMask); +} + +void MissionMarker::unpackUpdate(NetConnection * con, BitStream * stream) +{ + Parent::unpackUpdate(con, stream); + if(stream->readFlag()) + { + MatrixF mat; + stream->readAffineTransform(&mat); + Parent::setTransform(mat); + + Point3F scale; + mathRead(*stream, &scale); + setScale(scale); + } +} + +void MissionMarker::initPersistFields() { + Parent::initPersistFields(); +} + +//------------------------------------------------------------------------------ +// Class: WayPoint +//------------------------------------------------------------------------------ +IMPLEMENT_CO_NETOBJECT_V1(WayPoint); + +ConsoleDocClass( WayPoint, + "@brief Special type of marker, distinguished by a name and team ID number\n\n" + + "The original Torque engines were built from a multi-player game called Tribes. " + "The Tribes series featured various team based game modes, such as capture the flag. " + "The WayPoint class survived the conversion from game (Tribes) to game engine (Torque).\n\n" + + "Essentially, this is a MissionMarker with the addition of two variables: markerName and team. " + "Whenever a WayPoint is created, it is added to a unique global list called WayPointSet. " + "You can iterate through this set, seeking out specific markers determined by their markerName and team ID. " + "This avoids the overhead of constantly calling commandToClient and commandToServer to determine " + "a WayPoint object's name, unique ID, etc.\n\n" + + "@note The markerName field was previously called name, but was changed " + "because this conflicted with the SimObject name field. Existing scripts that relied " + "on the WayPoint name field will need to be updated.\n\n" + + "@tsexample\n" + "new WayPoint()\n" + "{\n" + " team = \"1\";\n" + " dataBlock = \"WayPointMarker\";\n" + " position = \"-0.0224786 1.53471 2.93219\";\n" + " rotation = \"1 0 0 0\";\n" + " scale = \"1 1 1\";\n" + " canSave = \"1\";\n" + " canSaveDynamicFields = \"1\";\n" + "};\n" + "@endtsexample\n\n" + + "@see MissionMarker\n\n" + "@see MissionMarkerData\n\n" + "@ingroup enviroMisc\n" +); + +WayPointTeam::WayPointTeam() +{ + mTeamId = 0; + mWayPoint = 0; +} + +WayPoint::WayPoint() +{ + mName = StringTable->insert(""); +} + +void WayPoint::setHidden(bool hidden) +{ + // Skip ShapeBase::setHidden (only ever added to scene if in the editor) + ShapeBase::Parent::setHidden( hidden ); + if(isServerObject()) + setMaskBits(UpdateHiddenMask); +} + +bool WayPoint::onAdd() +{ + if(!Parent::onAdd()) + return(false); + + // + if(isClientObject()) + Sim::getWayPointSet()->addObject(this); + else + { + mTeam.mWayPoint = this; + setMaskBits(UpdateNameMask|UpdateTeamMask); + } + + return(true); +} + +void WayPoint::inspectPostApply() +{ + Parent::inspectPostApply(); + if(!mName || !mName[0]) + mName = StringTable->insert(""); + setMaskBits(UpdateNameMask|UpdateTeamMask); +} + +U32 WayPoint::packUpdate(NetConnection * con, U32 mask, BitStream * stream) +{ + U32 retMask = Parent::packUpdate(con, mask, stream); + if(stream->writeFlag(mask & UpdateNameMask)) + stream->writeString(mName); + if(stream->writeFlag(mask & UpdateTeamMask)) + stream->write(mTeam.mTeamId); + if(stream->writeFlag(mask & UpdateHiddenMask)) + stream->writeFlag(isHidden()); + return(retMask); +} + +void WayPoint::unpackUpdate(NetConnection * con, BitStream * stream) +{ + Parent::unpackUpdate(con, stream); + if(stream->readFlag()) + mName = stream->readSTString(true); + if(stream->readFlag()) + stream->read(&mTeam.mTeamId); + if(stream->readFlag()) + setHidden(stream->readFlag()); +} + +//----------------------------------------------------------------------------- +// TypeWayPointTeam +//----------------------------------------------------------------------------- + +IMPLEMENT_STRUCT( WayPointTeam, WayPointTeam,, + "" ) +END_IMPLEMENT_STRUCT; + +//FIXME: this should work but does not; need to check the stripping down to base types within TYPE +//ConsoleType( WayPointTeam, TypeWayPointTeam, WayPointTeam* ) +ConsoleType( WayPointTeam, TypeWayPointTeam, WayPointTeam ) + +ConsoleGetType( TypeWayPointTeam ) +{ + char * buf = Con::getReturnBuffer(32); + dSprintf(buf, 32, "%d", ((WayPointTeam*)dptr)->mTeamId); + return(buf); +} + +ConsoleSetType( TypeWayPointTeam ) +{ + WayPointTeam * pTeam = (WayPointTeam*)dptr; + pTeam->mTeamId = dAtoi(argv[0]); + + if(pTeam->mWayPoint && pTeam->mWayPoint->isServerObject()) + pTeam->mWayPoint->setMaskBits(WayPoint::UpdateTeamMask); +} + +void WayPoint::initPersistFields() +{ + addGroup("Misc"); + addField("markerName", TypeCaseString, Offset(mName, WayPoint), "Unique name representing this waypoint"); + addField("team", TypeWayPointTeam, Offset(mTeam, WayPoint), "Unique numerical ID assigned to this waypoint, or set of waypoints"); + endGroup("Misc"); + + Parent::initPersistFields(); +} + +//------------------------------------------------------------------------------ +// Class: SpawnSphere +//------------------------------------------------------------------------------ +IMPLEMENT_CO_NETOBJECT_V1(SpawnSphere); + +ConsoleDocClass( SpawnSphere, + "@brief This class is used for creating any type of game object, assigning it a class, datablock, and other " + "properties when it is spawned.\n\n" + + "Torque 3D uses a simple spawn system, which can be easily modified to spawn any kind of object (of any class). " + "Each new level already contains at least one SpawnSphere, which is represented by a green octahedron in stock Torque 3D. " + "The spawnClass field determines the object type, such as Player, AIPlayer, etc. The spawnDataBlock field applies the " + "pre-defined datablock to each spawned object instance. The really powerful feature of this class is provided by " + "the spawnScript field which allows you to define a simple script (multiple lines) that will be executed once the " + "object has been spawned.\n\n" + + "@tsexample\n" + "// Define an SpawnSphere that essentially performs the following each time an object is spawned\n" + "//$SpawnObject = new Player()\n" + "//{\n" + "// dataBlock = \"DefaultPlayerData\";\n" + "// name = \"Bob\";\n" + "// lifeTotal = 3;\n" + "//};\n" + "//echo(\"Spawned a Player: \" @ $SpawnObject);\n\n" + "new SpawnSphere(DefaultSpawnSphere)\n" + "{\n" + " spawnClass = \"Player\";\n" + " spawnDatablock = \"DefaultPlayerData\";\n" + " spawnScript = \"echo(\\\"Spawned a Player: \\\" @ $SpawnObject);\"; // embedded quotes must be escaped with \\ \n" + " spawnProperties = \"name = \\\"Bob\\\";lifeTotal = 3;\"; // embedded quotes must be escaped with \\ \n" + " autoSpawn = \"1\";\n" + " dataBlock = \"SpawnSphereMarker\";\n" + " position = \"-0.77266 -19.882 17.8153\";\n" + " rotation = \"1 0 0 0\";\n" + " scale = \"1 1 1\";\n" + " canSave = \"1\";\n" + " canSaveDynamicFields = \"1\";\n" + "};\n\n" + "// Because autoSpawn is set to true in the above example, the following lines\n" + "// of code will execute AFTER the Player object has been spawned.\n" + "echo(\"Object Spawned\");\n" + "echo(\"Hello World\");\n\n" + "@endtsexample\n\n" + + "@see MissionMarker\n\n" + "@see MissionMarkerData\n\n" + "@ingroup gameObjects\n" + "@ingroup enviroMisc\n" +); + +SpawnSphere::SpawnSphere() +{ + mAutoSpawn = false; + + mRadius = 100.f; + mSphereWeight = 100.f; + mIndoorWeight = 100.f; + mOutdoorWeight = 100.f; +} + +IMPLEMENT_CALLBACK( SpawnSphere, onAdd, void, ( U32 objectId ), ( objectId ), + "Called when the SpawnSphere is being created.\n" + "@param objectId The unique SimObjectId generated when SpawnSphere is created (%%this in script)\n" ); + +bool SpawnSphere::onAdd() +{ + if(!Parent::onAdd()) + return(false); + + if(!isClientObject()) + setMaskBits(UpdateSphereMask); + + if (!isGhost()) + { + onAdd_callback( getId()); + + if (mAutoSpawn) + spawnObject(); + } + + return true; +} + +SimObject* SpawnSphere::spawnObject(String additionalProps) +{ + SimObject* spawnObject = Sim::spawnObject(mSpawnClass, mSpawnDataBlock, mSpawnName, + mSpawnProperties + " " + additionalProps, mSpawnScript); + + // If we have a spawnObject add it to the MissionCleanup group + if (spawnObject) + { + SimObject* cleanup = Sim::findObject("MissionCleanup"); + + if (cleanup) + { + SimGroup* missionCleanup = dynamic_cast(cleanup); + + missionCleanup->addObject(spawnObject); + } + } + + return spawnObject; +} + +void SpawnSphere::inspectPostApply() +{ + Parent::inspectPostApply(); + setMaskBits(UpdateSphereMask); +} + +U32 SpawnSphere::packUpdate(NetConnection * con, U32 mask, BitStream * stream) +{ + U32 retMask = Parent::packUpdate(con, mask, stream); + + // + if(stream->writeFlag(mask & UpdateSphereMask)) + { + stream->writeFlag(mAutoSpawn); + + stream->write(mSpawnClass); + stream->write(mSpawnDataBlock); + stream->write(mSpawnName); + stream->write(mSpawnProperties); + stream->write(mSpawnScript); + } + return(retMask); +} + +void SpawnSphere::unpackUpdate(NetConnection * con, BitStream * stream) +{ + Parent::unpackUpdate(con, stream); + if(stream->readFlag()) + { + mAutoSpawn = stream->readFlag(); + + stream->read(&mSpawnClass); + stream->read(&mSpawnDataBlock); + stream->read(&mSpawnName); + stream->read(&mSpawnProperties); + stream->read(&mSpawnScript); + } +} + +void SpawnSphere::processTick( const Move *move ) +{ + if ( isServerObject() && isMounted() ) + { + MatrixF mat( true ); + mMount.object->getRenderMountTransform( 0.f, mMount.node, mMount.xfm, &mat ); + setTransform( mat ); + } +} + +void SpawnSphere::advanceTime( F32 timeDelta ) +{ + if ( isMounted() ) + { + MatrixF mat( true ); + mMount.object->getRenderMountTransform( 0.f, mMount.node, mMount.xfm, &mat ); + setTransform( mat ); + } +} + +void SpawnSphere::initPersistFields() +{ + addGroup( "Spawn" ); + addField( "spawnClass", TypeRealString, Offset(mSpawnClass, SpawnSphere), + "Object class to create (eg. Player, AIPlayer, Debris etc)" ); + addField( "spawnDatablock", TypeRealString, Offset(mSpawnDataBlock, SpawnSphere), + "Predefined datablock assigned to the object when created" ); + addField( "spawnProperties", TypeRealString, Offset(mSpawnProperties, SpawnSphere), + "String containing semicolon (;) delimited properties to set when the object is created." ); + addField( "spawnScript", TypeCommand, Offset(mSpawnScript, SpawnSphere), + "Command to execute immediately after spawning an object. New object id is stored in $SpawnObject. Max 255 characters." ); + addField( "autoSpawn", TypeBool, Offset(mAutoSpawn, SpawnSphere), + "Flag to spawn object as soon as SpawnSphere is created, true to enable or false to disable." ); + endGroup( "Spawn" ); + + addGroup( "Dimensions" ); + addField( "radius", TypeF32, Offset(mRadius, SpawnSphere), "Deprecated" ); + endGroup( "Dimensions" ); + + addGroup( "Weight" ); + addField( "sphereWeight", TypeF32, Offset(mSphereWeight, SpawnSphere), "Deprecated" ); + addField( "indoorWeight", TypeF32, Offset(mIndoorWeight, SpawnSphere), "Deprecated" ); + addField( "outdoorWeight", TypeF32, Offset(mOutdoorWeight, SpawnSphere), "Deprecated" ); + endGroup( "Weight" ); + + Parent::initPersistFields(); +} + +ConsoleDocFragment _SpawnSpherespawnObject1( + "@brief Dynamically create a new game object with a specified class, datablock, and optional properties.\n\n" + "This is called on the actual SpawnSphere, not to be confused with the Sim::spawnObject() " + "global function\n\n" + "@param additionalProps Optional set of semiconlon delimited parameters applied to the spawn object during creation.\n\n" + "@tsexample\n" + "// Use the SpawnSphere::spawnObject function to create a game object\n" + "// No additional properties assigned\n" + "%player = DefaultSpawnSphere.spawnObject();\n\n" + "@endtsexample\n\n", + "SpawnSphere", + "bool spawnObject(string additionalProps);" +); + +ConsoleMethod(SpawnSphere, spawnObject, S32, 2, 3, + "([string additionalProps]) Spawns the object based on the SpawnSphere's " + "class, datablock, properties, and script settings. Allows you to pass in " + "extra properties." + "@hide" ) +{ + String additionalProps; + + if (argc == 3) + additionalProps = String(argv[2]); + + SimObject* obj = object->spawnObject(additionalProps); + + if (obj) + return obj->getId(); + + return -1; +} + + +//------------------------------------------------------------------------------ +// Class: CameraBookmark +//------------------------------------------------------------------------------ +IMPLEMENT_CO_NETOBJECT_V1(CameraBookmark); + +ConsoleDocClass( CameraBookmark, + "@brief Special type of mission marker which allows a level editor's camera to jump to specific points.\n\n" + "For Torque 3D editors only, not for actual game development\n\n" + "@internal" +); + +CameraBookmark::CameraBookmark() +{ + mName = StringTable->insert(""); +} + +bool CameraBookmark::onAdd() +{ + if(!Parent::onAdd()) + return(false); + + // + if(!isClientObject()) + { + setMaskBits(UpdateNameMask); + } + + if( isServerObject() && isMethod("onAdd") ) + Con::executef( this, "onAdd" ); + + return(true); +} + +void CameraBookmark::onRemove() +{ + if( isServerObject() && isMethod("onRemove") ) + Con::executef( this, "onRemove" ); + + Parent::onRemove(); +} + +void CameraBookmark::onGroupAdd() +{ + if( isServerObject() && isMethod("onGroupAdd") ) + Con::executef( this, "onGroupAdd" ); +} + +void CameraBookmark::onGroupRemove() +{ + if( isServerObject() && isMethod("onGroupRemove") ) + Con::executef( this, "onGroupRemove" ); +} + +void CameraBookmark::inspectPostApply() +{ + Parent::inspectPostApply(); + if(!mName || !mName[0]) + mName = StringTable->insert(""); + setMaskBits(UpdateNameMask); + + if( isMethod("onInspectPostApply") ) + Con::executef( this, "onInspectPostApply" ); +} + +U32 CameraBookmark::packUpdate(NetConnection * con, U32 mask, BitStream * stream) +{ + U32 retMask = Parent::packUpdate(con, mask, stream); + if(stream->writeFlag(mask & UpdateNameMask)) + stream->writeString(mName); + return(retMask); +} + +void CameraBookmark::unpackUpdate(NetConnection * con, BitStream * stream) +{ + Parent::unpackUpdate(con, stream); + if(stream->readFlag()) + mName = stream->readSTString(true); +} + +void CameraBookmark::initPersistFields() +{ + //addGroup("Misc"); + //addField("name", TypeCaseString, Offset(mName, CameraBookmark)); + //endGroup("Misc"); + + Parent::initPersistFields(); + + removeField("nameTag"); // From GameBase +} diff --git a/Engine/source/T3D/missionMarker.h b/Engine/source/T3D/missionMarker.h new file mode 100644 index 000000000..7064113aa --- /dev/null +++ b/Engine/source/T3D/missionMarker.h @@ -0,0 +1,236 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MISSIONMARKER_H_ +#define _MISSIONMARKER_H_ + +#ifndef _BITSTREAM_H_ +#include "core/stream/bitStream.h" +#endif +#ifndef _SIMBASE_H_ +#include "console/simBase.h" +#endif +#ifndef _SHAPEBASE_H_ +#include "T3D/shapeBase.h" +#endif +#ifndef _MATHIO_H_ +#include "math/mathIO.h" +#endif +#ifndef _COLOR_H_ +#include "core/color.h" +#endif + +class MissionMarkerData : public ShapeBaseData +{ + private: + typedef ShapeBaseData Parent; + public: + DECLARE_CONOBJECT(MissionMarkerData); +}; + +//------------------------------------------------------------------------------ +// Class: MissionMarker +//------------------------------------------------------------------------------ +class MissionMarker : public ShapeBase +{ + private: + typedef ShapeBase Parent; + + protected: + enum MaskBits { + PositionMask = Parent::NextFreeMask, + NextFreeMask = Parent::NextFreeMask << 1 + }; + MissionMarkerData * mDataBlock; + bool mAddedToScene; + + public: + MissionMarker(); + + // GameBase + bool onNewDataBlock( GameBaseData *dptr, bool reload ); + + // SceneObject + void setTransform(const MatrixF &mat); + + // SimObject + bool onAdd(); + void onRemove(); + void onEditorEnable(); + void onEditorDisable(); + + void inspectPostApply(); + + // NetObject + U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream); + void unpackUpdate(NetConnection *conn, BitStream *stream); + + DECLARE_CONOBJECT(MissionMarker); + static void initPersistFields(); +}; + +//------------------------------------------------------------------------------ +// Class: WayPoint +//------------------------------------------------------------------------------ +class WayPoint; +class WayPointTeam +{ + public: + WayPointTeam(); + + S32 mTeamId; + WayPoint * mWayPoint; +}; + +DECLARE_STRUCT( WayPointTeam ); +DefineConsoleType( TypeWayPointTeam, WayPointTeam * ); + +class WayPoint : public MissionMarker +{ + private: + typedef MissionMarker Parent; + + public: + enum WayPointMasks { + UpdateNameMask = Parent::NextFreeMask, + UpdateTeamMask = Parent::NextFreeMask << 1, + UpdateHiddenMask = Parent::NextFreeMask << 2, + NextFreeMask = Parent::NextFreeMask << 3 + }; + + WayPoint(); + + // ShapeBase: only ever added to scene if in the editor + void setHidden(bool hidden); + + // SimObject + bool onAdd(); + void inspectPostApply(); + + // NetObject + U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream); + void unpackUpdate(NetConnection *conn, BitStream *stream); + + // field data + StringTableEntry mName; + WayPointTeam mTeam; + + static void initPersistFields(); + + DECLARE_CONOBJECT(WayPoint); +}; + +//------------------------------------------------------------------------------ +// Class: SpawnSphere +//------------------------------------------------------------------------------ + +class SpawnSphere : public MissionMarker +{ + private: + typedef MissionMarker Parent; + + public: + SpawnSphere(); + + // SimObject + bool onAdd(); + void inspectPostApply(); + + // NetObject + enum SpawnSphereMasks + { + UpdateSphereMask = Parent::NextFreeMask, + NextFreeMask = Parent::NextFreeMask << 1 + }; + + U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream); + void unpackUpdate(NetConnection *conn, BitStream *stream); + + // ProcessObject + void advanceTime( F32 timeDelta ); + void processTick( const Move *move ); + + // Spawn info + String mSpawnClass; + String mSpawnDataBlock; + String mSpawnName; + String mSpawnProperties; + String mSpawnScript; + bool mAutoSpawn; + + // Radius/weight info + F32 mRadius; + F32 mSphereWeight; + F32 mIndoorWeight; + F32 mOutdoorWeight; + + SimObject* spawnObject(String additionalProps = String::EmptyString); + + static void initPersistFields(); + + DECLARE_CONOBJECT(SpawnSphere); + DECLARE_CALLBACK( void, onAdd, ( U32 objectId ) ); +}; + + +//------------------------------------------------------------------------------ +// Class: CameraBookmark +//------------------------------------------------------------------------------ + +class CameraBookmark : public MissionMarker +{ + private: + typedef MissionMarker Parent; + + public: + enum WayPointMasks { + UpdateNameMask = Parent::NextFreeMask, + NextFreeMask = Parent::NextFreeMask << 1 + }; + + CameraBookmark(); + + // SimObject + virtual bool onAdd(); + virtual void onRemove(); + virtual void onGroupAdd(); + virtual void onGroupRemove(); + void inspectPostApply(); + + // NetObject + U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream); + void unpackUpdate(NetConnection *conn, BitStream *stream); + + // field data + StringTableEntry mName; + + static void initPersistFields(); + + DECLARE_CONOBJECT(CameraBookmark); + /*DECLARE_CALLBACK( void, onAdd, () ); + DECLARE_CALLBACK( void, onRemove, () ); + DECLARE_CALLBACK( void, onGroupAdd, () ); + DECLARE_CALLBACK( void, onGroupRemove, () ); + DECLARE_CALLBACK( void, onInspectPostApply, () );*/ +}; + +#endif // _MISSIONMARKER_H_ \ No newline at end of file diff --git a/Engine/source/T3D/objectTypes.h b/Engine/source/T3D/objectTypes.h new file mode 100644 index 000000000..47363ea85 --- /dev/null +++ b/Engine/source/T3D/objectTypes.h @@ -0,0 +1,207 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _OBJECTTYPES_H_ +#define _OBJECTTYPES_H_ + +#include "platform/types.h" + +/// Types used for SceneObject type masks (SceneObject::mTypeMask) +/// +/// @note If a new object type is added, don't forget to add it to +/// RegisterGameFunctions(). +enum SceneObjectTypes +{ + /// @name Types used by the SceneObject class + /// @{ + + /// Default value for type masks. + DefaultObjectType = 0, + + /// @} + + /// @name Basic Engine Types + /// @{ + + /// Any kind of SceneObject that is not supposed to change transforms + /// except during editing (or not at all). + StaticObjectType = BIT( 0 ), + + /// Environment objects such as clouds, skies, forests, etc. + EnvironmentObjectType = BIT( 1 ), + + /// A terrain object. + /// @see TerrainBlock + TerrainObjectType = BIT( 2 ), + + /// A legacy DIF interior object. + /// @see InteriorInstance + InteriorObjectType = BIT( 3 ), + + /// An object defining a water volume. + /// @see WaterObject + WaterObjectType = BIT( 4 ), + + /// An object defining an invisible trigger volume. + /// @see Trigger + TriggerObjectType = BIT( 5 ), + + /// An object defining an invisible marker. + /// @see MissionMarker + MarkerObjectType = BIT( 6 ), + + /// A light emitter. + /// @see LightBase + LightObjectType = BIT( 7 ), + + /// An object that manages zones. This is automatically set by + /// SceneZoneSpaceManager when a SceneZoneSpace registers zones. Should + /// not be manually set. + /// + /// @see SceneZoneSpace + /// @see SceneZoneSpaceManager + ZoneObjectType = BIT( 8 ), + + /// Any object that defines one or more solid, renderable static geometries that + /// should be included in collision and raycasts. + /// + /// Use this mask to find objects that are part of the static level geometry. + /// + /// @note If you set this, you will also want to set StaticObjectType. + StaticShapeObjectType = BIT( 9 ), + + /// Any object that defines one or more solid, renderable dynamic geometries that + /// should be included in collision and raycasts. + /// + /// Use this mask to find objects that are part of the dynamic game geometry. + DynamicShapeObjectType = BIT( 10 ), + + /// @} + + /// @name Game Types + /// @{ + + /// Any GameBase-derived object. + /// @see GameBase + GameBaseObjectType = BIT( 11 ), + + /// An object that uses hifi networking. + GameBaseHiFiObjectType = BIT( 12 ), + + /// Any ShapeBase-derived object. + /// @see ShapeBase + ShapeBaseObjectType = BIT( 13 ), + + /// A camera object. + /// @see Camera + CameraObjectType = BIT( 14 ), + + /// A human or AI player object. + /// @see Player + PlayerObjectType = BIT( 15 ), + + /// An item pickup. + /// @see Item + ItemObjectType = BIT( 16 ), + + /// A vehicle. + /// @see Vehicle + VehicleObjectType = BIT( 17 ), + + /// An object that blocks vehicles. + /// @see VehicleBlocker + VehicleBlockerObjectType = BIT( 18 ), + + /// A weapon projectile. + /// @see Projectile + ProjectileObjectType = BIT( 19 ), + + /// An explosion object. + /// @see Explosion + ExplosionObjectType = BIT( 20 ), + + /// A dead player. This is dynamically set and unset. + /// @see Player + CorpseObjectType = BIT( 21 ), + + /// A debris object. + /// @see Debris + DebrisObjectType = BIT( 22 ), + + /// A volume that asserts forces on player objects. + /// @see PhysicalZone + PhysicalZoneObjectType = BIT( 23 ), + + /// @} +}; + +enum SceneObjectTypeMasks +{ + STATIC_COLLISION_TYPEMASK = StaticShapeObjectType, + + DAMAGEABLE_TYPEMASK = ( PlayerObjectType | + VehicleObjectType ), + + /// Typemask for objects that should be rendered into shadow passes. + /// These should be all objects that are either meant to receive or cast + /// shadows or both. + SHADOW_TYPEMASK = ( StaticShapeObjectType | + DynamicShapeObjectType ), + + /// Typemask for objects that should be subjected to more fine-grained + /// culling tests. Anything that is trivial rendering stuff or doesn't + /// render except when in the editor should be excluded here. + /// + /// Also, objects that do their own culling internally (terrains, forests, etc.) + /// should be excluded. + CULLING_INCLUDE_TYPEMASK = ( InteriorObjectType | + GameBaseObjectType | // Includes most other renderable types; but broader than we ideally want. + StaticShapeObjectType | + DynamicShapeObjectType | + ZoneObjectType ), // This improves the result of zone traversals. + + /// Mask for objects that should be specifically excluded from zone culling. + CULLING_EXCLUDE_TYPEMASK = ( TerrainObjectType | + EnvironmentObjectType ), + + /// Default object type mask to use for render queries. + DEFAULT_RENDER_TYPEMASK = ( EnvironmentObjectType | + TerrainObjectType | + WaterObjectType | + StaticShapeObjectType | + DynamicShapeObjectType | + LightObjectType | // Flares. + GameBaseObjectType ), + + /// Typemask to use for rendering when inside the editor. + EDITOR_RENDER_TYPEMASK = U32( -1 ), + + /// All objects that fit this type mask will be exclusively assigned to the outdoor (root) + /// zone and not be assigned to individual interior objects. + /// + /// @note Terrains have their own means for rendering inside interior zones. + OUTDOOR_OBJECT_TYPEMASK = ( TerrainObjectType | + InteriorObjectType | + EnvironmentObjectType ) +}; + +#endif diff --git a/Engine/source/T3D/occlusionVolume.cpp b/Engine/source/T3D/occlusionVolume.cpp new file mode 100644 index 000000000..7accbd507 --- /dev/null +++ b/Engine/source/T3D/occlusionVolume.cpp @@ -0,0 +1,189 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/occlusionVolume.h" + +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxDrawUtil.h" +#include "gfx/sim/debugDraw.h" +#include "util/tempAlloc.h" + +#include "math/mPolyhedron.impl.h" + + +//#define DEBUG_DRAW + + +IMPLEMENT_CO_NETOBJECT_V1( OcclusionVolume ); + +ConsoleDocClass( OcclusionVolume, + "@brief An invisible shape that causes objects hidden from view behind it to not be rendered.\n\n" + + "OcclusionVolume is a class for scene optimization. It's main use is for outdoor spaces where zones " + "and portals do not help in optimizing scene culling as they almost only make sense for modeling visibility " + "in indoor scenarios (and for connecting indoor spaces to outdoor spaces).\n\n" + + "During rendering, every object that is fully behind an occluder \n\n" + + "Be aware that occluders add overhead to scene culling. Only if this overhead is outweighed by the time " + "saved by not rendering hidden objects, is the occluder actually effective. Because of this, chose only those " + "spots for placing occluders where a significant number of objects will be culled from points that the player " + "will actually be at during the game.\n\n" + + "Like zones and portals, OcclusionVolumes may have a default box shape or a more complex \n\n" + + "@see Scene::maxOccludersPerZone\n" + "@see Scene::occluderMinWidthPercentage\n" + "@see Scene::occluderMinHeightPercentage\n" + "@ingroup enviroMisc" +); + + +//----------------------------------------------------------------------------- + +OcclusionVolume::OcclusionVolume() + : mTransformDirty( true ), + mSilhouetteExtractor( mPolyhedron ) +{ + VECTOR_SET_ASSOCIATION( mWSPoints ); + + mObjectFlags.set( VisualOccluderFlag ); + + mObjScale.set( 1.f, 1.f, 1.f ); + mObjBox.set( + Point3F( -0.5f, -0.5f, -0.5f ), + Point3F( 0.5f, 0.5f, 0.5f ) + ); + + mObjToWorld.identity(); + mWorldToObj.identity(); + + resetWorldBox(); +} + +//----------------------------------------------------------------------------- + +void OcclusionVolume::consoleInit() +{ + // Disable rendering of occlusion volumes by default. + getStaticClassRep()->mIsRenderEnabled = false; +} + +//----------------------------------------------------------------------------- + +bool OcclusionVolume::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + // Set up the silhouette extractor. + mSilhouetteExtractor = SilhouetteExtractorType( mPolyhedron ); + + return true; +} + +//----------------------------------------------------------------------------- + +void OcclusionVolume::_renderObject( ObjectRenderInst* ri, SceneRenderState* state, BaseMatInstance* overrideMat ) +{ + Parent::_renderObject( ri, state, overrideMat ); + + #ifdef DEBUG_DRAW + if( state->isDiffusePass() ) + DebugDrawer::get()->drawPolyhedronDebugInfo( mPolyhedron, getTransform(), getScale() ); + #endif +} + +//----------------------------------------------------------------------------- + +void OcclusionVolume::setTransform( const MatrixF& mat ) +{ + Parent::setTransform( mat ); + mTransformDirty = true; +} + +//----------------------------------------------------------------------------- + +void OcclusionVolume::buildSilhouette( const SceneCameraState& cameraState, Vector< Point3F >& outPoints ) +{ + // Extract the silhouette of the polyhedron. This works differently + // depending on whether we project orthogonally or in perspective. + + TempAlloc< U32 > indices( mPolyhedron.getNumPoints() ); + U32 numPoints; + + if( cameraState.getFrustum().isOrtho() ) + { + // Transform the view direction into object space. + + Point3F osViewDir; + getWorldTransform().mulV( cameraState.getViewDirection(), &osViewDir ); + + // And extract the silhouette. + + SilhouetteExtractorOrtho< PolyhedronType > extractor( mPolyhedron ); + numPoints = extractor.extractSilhouette( osViewDir, indices, indices.size ); + } + else + { + // Create a transform to go from view space to object space. + + MatrixF camView( true ); + camView.scale( Point3F( 1.0f / getScale().x, 1.0f / getScale().y, 1.0f / getScale().z ) ); + camView.mul( getRenderWorldTransform() ); + camView.mul( cameraState.getViewWorldMatrix() ); + + // Do a perspective-correct silhouette extraction. + + numPoints = mSilhouetteExtractor.extractSilhouette( + camView, + indices, indices.size ); + } + + // If we haven't yet, transform the polyhedron's points + // to world space. + + if( mTransformDirty ) + { + const U32 numPoints = mPolyhedron.getNumPoints(); + const PolyhedronType::PointType* points = getPolyhedron().getPoints(); + + mWSPoints.setSize( numPoints ); + for( U32 i = 0; i < numPoints; ++ i ) + { + Point3F p = points[ i ]; + p.convolve( getScale() ); + getTransform().mulP( p, &mWSPoints[ i ] ); + } + + mTransformDirty = false; + } + + // Now store the points. + + outPoints.setSize( numPoints ); + for( U32 i = 0; i < numPoints; ++ i ) + outPoints[ i ] = mWSPoints[ indices[ i ] ]; +} diff --git a/Engine/source/T3D/occlusionVolume.h b/Engine/source/T3D/occlusionVolume.h new file mode 100644 index 000000000..49222bb94 --- /dev/null +++ b/Engine/source/T3D/occlusionVolume.h @@ -0,0 +1,78 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _OCCLUSIONVOLUME_H_ +#define _OCCLUSIONVOLUME_H_ + +#ifndef _SCENEPOLYHEDRALSPACE_H_ +#include "scene/scenePolyhedralSpace.h" +#endif + +#ifndef _MSILHOUETTEEXTRACTOR_H_ +#include "math/mSilhouetteExtractor.h" +#endif + + + +/// A volume in space that blocks visibility. +class OcclusionVolume : public ScenePolyhedralSpace +{ + public: + + typedef ScenePolyhedralSpace Parent; + + protected: + + typedef SilhouetteExtractorPerspective< PolyhedronType > SilhouetteExtractorType; + + /// Whether the volume's transform has changed and we need to recompute + /// transform-based data. + bool mTransformDirty; + + /// World-space points of the volume's polyhedron. + Vector< Point3F > mWSPoints; + + /// Silhouette extractor when using perspective projections. + SilhouetteExtractorType mSilhouetteExtractor; + + // SceneSpace. + virtual void _renderObject( ObjectRenderInst* ri, SceneRenderState* state, BaseMatInstance* overrideMat ); + + public: + + OcclusionVolume(); + + // SimObject. + DECLARE_CONOBJECT( OcclusionVolume ); + DECLARE_DESCRIPTION( "A visibility blocking volume." ); + DECLARE_CATEGORY( "3D Scene" ); + + virtual bool onAdd(); + + static void consoleInit(); + + // SceneObject. + virtual void buildSilhouette( const SceneCameraState& cameraState, Vector< Point3F >& outPoints ); + virtual void setTransform( const MatrixF& mat ); +}; + +#endif // !_OCCLUSIONVOLUME_H_ diff --git a/Engine/source/T3D/pathCamera.cpp b/Engine/source/T3D/pathCamera.cpp new file mode 100644 index 000000000..185e7a675 --- /dev/null +++ b/Engine/source/T3D/pathCamera.cpp @@ -0,0 +1,640 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "math/mMath.h" +#include "math/mathIO.h" +#include "console/simBase.h" +#include "console/console.h" +#include "console/consoleTypes.h" +#include "core/stream/bitStream.h" +#include "core/dnet.h" +#include "scene/pathManager.h" +#include "app/game.h" +#include "T3D/gameBase/gameConnection.h" +#include "T3D/fx/cameraFXMgr.h" +#include "console/engineAPI.h" +#include "math/mTransform.h" + +#include "T3D/pathCamera.h" + + +//---------------------------------------------------------------------------- + +IMPLEMENT_CO_DATABLOCK_V1(PathCameraData); + +ConsoleDocClass( PathCameraData, + "@brief General interface to control a PathCamera object from the script level.\n" + "@see PathCamera\n" + "@tsexample\n" + "datablock PathCameraData(LoopingCam)\n" + " {\n" + " mode = \"\";\n" + " };\n" + "@endtsexample\n" + "@ingroup PathCameras\n" + "@ingroup Datablocks\n" +); + +void PathCameraData::consoleInit() +{ +} + +void PathCameraData::initPersistFields() +{ + Parent::initPersistFields(); +} + +void PathCameraData::packData(BitStream* stream) +{ + Parent::packData(stream); +} + +void PathCameraData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); +} + + +//---------------------------------------------------------------------------- + +IMPLEMENT_CO_NETOBJECT_V1(PathCamera); + +ConsoleDocClass( PathCamera, + "@brief A camera that moves along a path. The camera can then be made to travel along this path forwards or backwards.\n\n" + + "A camera's path is made up of knots, which define a position, rotation and speed for the camera. Traversal from one knot to " + "another may be either linear or using a Catmull-Rom spline. If the knot is part of a spline, then it may be a normal knot " + "or defined as a kink. Kinked knots are a hard transition on the spline rather than a smooth one. A knot may also be defined " + "as a position only. In this case the knot is treated as a normal knot but is ignored when determining how to smoothly rotate " + "the camera while it is travelling along the path (the algorithm moves on to the next knot in the path for determining rotation).\n\n" + + "The datablock field for a PathCamera is a previously created PathCameraData, which acts as the interface between the script and the engine " + "for this PathCamera object.\n\n" + + "@see PathCameraData\n" + + "@tsexample\n" + "%newPathCamera = new PathCamera()\n" + "{\n" + " dataBlock = LoopingCam;\n" + " position = \"0 0 300 1 0 0 0\";\n" + "};\n" + "@endtsexample\n" + + "@ingroup PathCameras\n" +); + +IMPLEMENT_CALLBACK( PathCamera, onNode, void, (const char* node), (node), + "A script callback that indicates the path camera has arrived at a specific node in its path. Server side only.\n" + "@param Node Unique ID assigned to this node.\n"); + +PathCamera::PathCamera() +{ + mNetFlags.clear(Ghostable); + mTypeMask |= CameraObjectType; + delta.time = 0; + delta.timeVec = 0; + + mDataBlock = 0; + mState = Forward; + mNodeBase = 0; + mNodeCount = 0; + mPosition = 0; + mTarget = 0; + mTargetSet = false; + + MatrixF mat(1); + mat.setPosition(Point3F(0,0,700)); + Parent::setTransform(mat); +} + +PathCamera::~PathCamera() +{ +} + + +//---------------------------------------------------------------------------- + +bool PathCamera::onAdd() +{ + if(!Parent::onAdd()) + return false; + + // Initialize from the current transform. + if (!mNodeCount) { + QuatF rot(getTransform()); + Point3F pos = getPosition(); + mSpline.removeAll(); + mSpline.push_back(new CameraSpline::Knot(pos,rot,1, + CameraSpline::Knot::NORMAL, CameraSpline::Knot::SPLINE)); + mNodeCount = 1; + } + + // + mObjBox.maxExtents = mObjScale; + mObjBox.minExtents = mObjScale; + mObjBox.minExtents.neg(); + resetWorldBox(); + + if (mShapeInstance) + { + mNetFlags.set(Ghostable); + setScopeAlways(); + } + + addToScene(); + + return true; +} + +void PathCamera::onRemove() +{ + removeFromScene(); + + Parent::onRemove(); +} + +bool PathCamera::onNewDataBlock( GameBaseData *dptr, bool reload ) +{ + mDataBlock = dynamic_cast< PathCameraData* >( dptr ); + if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) ) + return false; + + scriptOnNewDataBlock(); + return true; +} + +//---------------------------------------------------------------------------- + +void PathCamera::onEditorEnable() +{ + mNetFlags.set(Ghostable); +} + +void PathCamera::onEditorDisable() +{ + mNetFlags.clear(Ghostable); +} + + +//---------------------------------------------------------------------------- + +void PathCamera::initPersistFields() +{ + Parent::initPersistFields(); +} + +void PathCamera::consoleInit() +{ +} + + +//---------------------------------------------------------------------------- + +void PathCamera::processTick(const Move* move) +{ + // client and server + Parent::processTick(move); + + // Move to new time + advancePosition(TickMs); + + // Set new position + MatrixF mat; + interpolateMat(mPosition,&mat); + Parent::setTransform(mat); + + updateContainer(); +} + +void PathCamera::interpolateTick(F32 dt) +{ + Parent::interpolateTick(dt); + MatrixF mat; + interpolateMat(delta.time + (delta.timeVec * dt),&mat); + Parent::setRenderTransform(mat); +} + +void PathCamera::interpolateMat(F32 pos,MatrixF* mat) +{ + CameraSpline::Knot knot; + mSpline.value(pos - mNodeBase,&knot); + knot.mRotation.setMatrix(mat); + mat->setPosition(knot.mPosition); +} + +void PathCamera::advancePosition(S32 ms) +{ + delta.timeVec = mPosition; + + // Advance according to current speed + if (mState == Forward) { + mPosition = mSpline.advanceTime(mPosition - mNodeBase,ms); + if (mPosition > F32(mNodeCount - 1)) + mPosition = F32(mNodeCount - 1); + mPosition += (F32)mNodeBase; + if (mTargetSet && mPosition >= mTarget) { + mTargetSet = false; + mPosition = mTarget; + mState = Stop; + } + } + else + if (mState == Backward) { + mPosition = mSpline.advanceTime(mPosition - mNodeBase,-ms); + if (mPosition < 0) + mPosition = 0; + mPosition += mNodeBase; + if (mTargetSet && mPosition <= mTarget) { + mTargetSet = false; + mPosition = mTarget; + mState = Stop; + } + } + + // Script callbacks + if (int(mPosition) != int(delta.timeVec)) + onNode(int(mPosition)); + + // Set frame interpolation + delta.time = mPosition; + delta.timeVec -= mPosition; +} + + +//---------------------------------------------------------------------------- + +void PathCamera::getCameraTransform(F32* pos, MatrixF* mat) +{ + // Overide the ShapeBase method to skip all the first/third person support. + getRenderEyeTransform(mat); + + // Apply Camera FX. + mat->mul( gCamFXMgr.getTrans() ); +} + + +//---------------------------------------------------------------------------- + +void PathCamera::setPosition(F32 pos) +{ + mPosition = mClampF(pos, (F32)mNodeBase, (F32)(mNodeBase + mNodeCount - 1)); + MatrixF mat; + interpolateMat(mPosition,&mat); + Parent::setTransform(mat); + setMaskBits(PositionMask); +} + +void PathCamera::setTarget(F32 pos) +{ + mTarget = pos; + mTargetSet = true; + if (mTarget > mPosition) + mState = Forward; + else + if (mTarget < mPosition) + mState = Backward; + else { + mTargetSet = false; + mState = Stop; + } + setMaskBits(TargetMask | StateMask); +} + +void PathCamera::setState(State s) +{ + mState = s; + setMaskBits(StateMask); +} + + +//----------------------------------------------------------------------------- + +void PathCamera::reset(F32 speed) +{ + CameraSpline::Knot *knot = new CameraSpline::Knot; + mSpline.value(mPosition - mNodeBase,knot); + if (speed) + knot->mSpeed = speed; + mSpline.removeAll(); + mSpline.push_back(knot); + + mNodeBase = 0; + mNodeCount = 1; + mPosition = 0; + mTargetSet = false; + mState = Forward; + setMaskBits(StateMask | PositionMask | WindowMask | TargetMask); +} + +void PathCamera::pushBack(CameraSpline::Knot *knot) +{ + // Make room at the end + if (mNodeCount == NodeWindow) { + delete mSpline.remove(mSpline.getKnot(0)); + mNodeBase++; + } + else + mNodeCount++; + + // Fill in the new node + mSpline.push_back(knot); + setMaskBits(WindowMask); + + // Make sure the position doesn't fall off + if (mPosition < mNodeBase) { + mPosition = (F32)mNodeBase; + setMaskBits(PositionMask); + } +} + +void PathCamera::pushFront(CameraSpline::Knot *knot) +{ + // Make room at the front + if (mNodeCount == NodeWindow) + delete mSpline.remove(mSpline.getKnot(mNodeCount)); + else + mNodeCount++; + mNodeBase--; + + // Fill in the new node + mSpline.push_front(knot); + setMaskBits(WindowMask); + + // Make sure the position doesn't fall off + if (mPosition > F32(mNodeBase + (NodeWindow - 1))) + { + mPosition = F32(mNodeBase + (NodeWindow - 1)); + setMaskBits(PositionMask); + } +} + +void PathCamera::popFront() +{ + if (mNodeCount < 2) + return; + + // Remove the first node. Node base and position are unaffected. + mNodeCount--; + delete mSpline.remove(mSpline.getKnot(0)); + + if( mPosition > 0 ) + mPosition --; +} + + +//---------------------------------------------------------------------------- + +void PathCamera::onNode(S32 node) +{ + if (!isGhost()) + onNode_callback(Con::getIntArg(node)); + +} + +U32 PathCamera::packUpdate(NetConnection *con, U32 mask, BitStream *stream) +{ + Parent::packUpdate(con,mask,stream); + + if (stream->writeFlag(mask & StateMask)) + stream->writeInt(mState,StateBits); + + if (stream->writeFlag(mask & PositionMask)) + stream->write(mPosition); + + if (stream->writeFlag(mask & TargetMask)) + if (stream->writeFlag(mTargetSet)) + stream->write(mTarget); + + if (stream->writeFlag(mask & WindowMask)) { + stream->write(mNodeBase); + stream->write(mNodeCount); + for (int i = 0; i < mNodeCount; i++) { + CameraSpline::Knot *knot = mSpline.getKnot(i); + mathWrite(*stream, knot->mPosition); + mathWrite(*stream, knot->mRotation); + stream->write(knot->mSpeed); + stream->writeInt(knot->mType, CameraSpline::Knot::NUM_TYPE_BITS); + stream->writeInt(knot->mPath, CameraSpline::Knot::NUM_PATH_BITS); + } + } + + // The rest of the data is part of the control object packet update. + // If we're controlled by this client, we don't need to send it. + if(stream->writeFlag(getControllingClient() == con && !(mask & InitialUpdateMask))) + return 0; + + return 0; +} + +void PathCamera::unpackUpdate(NetConnection *con, BitStream *stream) +{ + Parent::unpackUpdate(con,stream); + + // StateMask + if (stream->readFlag()) + mState = stream->readInt(StateBits); + + // PositionMask + if (stream->readFlag()) + { + stream->read(&mPosition); + delta.time = mPosition; + delta.timeVec = 0; + } + + // TargetMask + if (stream->readFlag()) + { + mTargetSet = stream->readFlag(); + if (mTargetSet) + stream->read(&mTarget); + } + + // WindowMask + if (stream->readFlag()) + { + mSpline.removeAll(); + stream->read(&mNodeBase); + stream->read(&mNodeCount); + for (int i = 0; i < mNodeCount; i++) + { + CameraSpline::Knot *knot = new CameraSpline::Knot(); + mathRead(*stream, &knot->mPosition); + mathRead(*stream, &knot->mRotation); + stream->read(&knot->mSpeed); + knot->mType = (CameraSpline::Knot::Type)stream->readInt(CameraSpline::Knot::NUM_TYPE_BITS); + knot->mPath = (CameraSpline::Knot::Path)stream->readInt(CameraSpline::Knot::NUM_PATH_BITS); + mSpline.push_back(knot); + } + } + + // Controlled by the client? + if (stream->readFlag()) + return; + +} + + +//----------------------------------------------------------------------------- +// Console access methods +//----------------------------------------------------------------------------- + +DefineEngineMethod(PathCamera, setPosition, void, (F32 position),(0.0f), "Set the current position of the camera along the path.\n" + "@param position Position along the path, from 0.0 (path start) - 1.0 (path end), to place the camera.\n" + "@tsexample\n" + "// Set the camera on a position along its path from 0.0 - 1.0.\n" + "%position = \"0.35\";\n\n" + "// Force the pathCamera to its new position along the path.\n" + "%pathCamera.setPosition(%position);\n" + "@endtsexample\n") +{ + object->setPosition(position); +} + +DefineEngineMethod(PathCamera, setTarget, void, (F32 position),(1.0f), "@brief Set the movement target for this camera along its path.\n\n" + "The camera will attempt to move along the path to the given target in the direction provided " + "by setState() (the default is forwards). Once the camera moves past this target it will come " + "to a stop, and the target state will be cleared.\n" + "@param position Target position, between 0.0 (path start) and 1.0 (path end), for the camera to move to along its path.\n" + "@tsexample\n" + "// Set the position target, between 0.0 (path start) and 1.0 (path end), for this camera to move to.\n" + "%position = \"0.50\";\n\n" + "// Inform the pathCamera of the new target position it will move to.\n" + "%pathCamera.setTarget(%position);\n" + "@endtsexample\n") +{ + object->setTarget(position); +} + +DefineEngineMethod(PathCamera, setState, void, (const char* newState),("forward"), "Set the movement state for this path camera.\n" + "@param newState New movement state type for this camera. Forward, Backward or Stop.\n" + "@tsexample\n" + "// Set the state type (forward, backward, stop).\n" + "// In this example, the camera will travel from the first node\n" + "// to the last node (or target if given with setTarget())\n" + "%state = \"forward\";\n\n" + "// Inform the pathCamera to change its movement state to the defined value.\n" + "%pathCamera.setState(%state);\n" + "@endtsexample\n") +{ + if (!dStricmp(newState,"forward")) + object->setState(PathCamera::Forward); + else + if (!dStricmp(newState,"backward")) + object->setState(PathCamera::Backward); + else + object->setState(PathCamera::Stop); +} + +DefineEngineMethod(PathCamera, reset, void, (F32 speed),(1.0f), "@brief Clear the camera's path and set the camera's current transform as the start of the new path.\n\n" + "What specifically occurs is a new knot is created from the camera's current transform. Then the current path " + "is cleared and the new knot is pushed onto the path. Any previous target is cleared and the camera's movement " + "state is set to Forward. The camera is now ready for a new path to be defined.\n" + "@param speed Speed for the camera to move along its path after being reset.\n" + "@tsexample\n" + "//Determine the new movement speed of this camera. If not set, the speed will default to 1.0.\n" + "%speed = \"0.50\";\n\n" + "// Inform the path camera to start a new path at" + "// the camera's current position, and set the new " + "// path's speed value.\n" + "%pathCamera.reset(%speed);\n" + "@endtsexample\n") +{ + object->reset(speed); +} + +static CameraSpline::Knot::Type resolveKnotType(const char *arg) +{ + if (dStricmp(arg, "Position Only") == 0) + return CameraSpline::Knot::POSITION_ONLY; + if (dStricmp(arg, "Kink") == 0) + return CameraSpline::Knot::KINK; + return CameraSpline::Knot::NORMAL; +} + +static CameraSpline::Knot::Path resolveKnotPath(const char *arg) +{ + if (!dStricmp(arg, "Linear")) + return CameraSpline::Knot::LINEAR; + return CameraSpline::Knot::SPLINE; +} + +DefineEngineMethod(PathCamera, pushBack, void, (TransformF transform, F32 speed, const char* type, const char* path), + (1.0, "Normal", "Linear"), + "@brief Adds a new knot to the back of a path camera's path.\n" + "@param transform Transform for the new knot. In the form of \"x y z ax ay az aa\" such as returned by SceneObject::getTransform()\n" + "@param speed Speed setting for this knot.\n" + "@param type Knot type (Normal, Position Only, Kink).\n" + "@param path %Path type (Linear, Spline).\n" + "@tsexample\n" + "// Transform vector for new knot. (Pos_X Pos_Y Pos_Z Rot_X Rot_Y Rot_Z Angle)\n" + "%transform = \"15.0 5.0 5.0 1.4 1.0 0.2 1.0\"\n\n" + "// Speed setting for knot.\n" + "%speed = \"1.0\"\n\n" + "// Knot type. (Normal, Position Only, Kink)\n" + "%type = \"Normal\";\n\n" + "// Path Type. (Linear, Spline)\n" + "%path = \"Linear\";\n\n" + "// Inform the path camera to add a new knot to the back of its path\n" + "%pathCamera.pushBack(%transform,%speed,%type,%path);\n" + "@endtsexample\n") +{ + QuatF rot(transform.getOrientation()); + + object->pushBack( new CameraSpline::Knot(transform.getPosition(), rot, speed, resolveKnotType(type), resolveKnotPath(path)) ); +} + +DefineEngineMethod(PathCamera, pushFront, void, (TransformF transform, F32 speed, const char* type, const char* path), + (1.0, "Normal", "Linear"), + "@brief Adds a new knot to the front of a path camera's path.\n" + "@param transform Transform for the new knot. In the form of \"x y z ax ay az aa\" such as returned by SceneObject::getTransform()\n" + "@param speed Speed setting for this knot.\n" + "@param type Knot type (Normal, Position Only, Kink).\n" + "@param path %Path type (Linear, Spline).\n" + "@tsexample\n" + "// Transform vector for new knot. (Pos_X,Pos_Y,Pos_Z,Rot_X,Rot_Y,Rot_Z,Angle)\n" + "%transform = \"15.0 5.0 5.0 1.4 1.0 0.2 1.0\"\n\n" + "// Speed setting for knot.\n" + "%speed = \"1.0\";\n\n" + "// Knot type. (Normal, Position Only, Kink)\n" + "%type = \"Normal\";\n\n" + "// Path Type. (Linear, Spline)\n" + "%path = \"Linear\";\n\n" + "// Inform the path camera to add a new knot to the front of its path\n" + "%pathCamera.pushFront(%transform, %speed, %type, %path);\n" + "@endtsexample\n") +{ + QuatF rot(transform.getOrientation()); + + object->pushFront( new CameraSpline::Knot(transform.getPosition(), rot, speed, resolveKnotType(type), resolveKnotPath(path)) ); +} + +DefineEngineMethod(PathCamera, popFront, void, (),, "Removes the knot at the front of the camera's path.\n" + "@tsexample\n" + "// Remove the first knot in the camera's path.\n" + "%pathCamera.popFront();\n" + "@endtsexample\n") +{ + object->popFront(); +} \ No newline at end of file diff --git a/Engine/source/T3D/pathCamera.h b/Engine/source/T3D/pathCamera.h new file mode 100644 index 000000000..c94e2ad57 --- /dev/null +++ b/Engine/source/T3D/pathCamera.h @@ -0,0 +1,129 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PATHCAMERA_H_ +#define _PATHCAMERA_H_ + +#ifndef _SHAPEBASE_H_ +#include "T3D/shapeBase.h" +#endif + +#ifndef _CAMERASPLINE_H_ +#include "T3D/cameraSpline.h" +#endif + + +//---------------------------------------------------------------------------- +struct PathCameraData: public ShapeBaseData { + typedef ShapeBaseData Parent; + + // + DECLARE_CONOBJECT(PathCameraData); + static void consoleInit(); + static void initPersistFields(); + virtual void packData(BitStream* stream); + virtual void unpackData(BitStream* stream); +}; + + +//---------------------------------------------------------------------------- +class PathCamera: public ShapeBase +{ +public: + enum State { + Forward, + Backward, + Stop, + StateBits = 3 + }; + +private: + typedef ShapeBase Parent; + + enum MaskBits { + WindowMask = Parent::NextFreeMask, + PositionMask = Parent::NextFreeMask + 1, + TargetMask = Parent::NextFreeMask + 2, + StateMask = Parent::NextFreeMask + 3, + NextFreeMask = Parent::NextFreeMask << 1 + }; + + struct StateDelta { + F32 time; + F32 timeVec; + }; + StateDelta delta; + + enum Constants { + NodeWindow = 128 // Maximum number of active nodes + }; + + // + PathCameraData* mDataBlock; + CameraSpline mSpline; + S32 mNodeBase; + S32 mNodeCount; + F32 mPosition; + int mState; + F32 mTarget; + bool mTargetSet; + + void interpolateMat(F32 pos,MatrixF* mat); + void advancePosition(S32 ms); + +public: + DECLARE_CONOBJECT(PathCamera); + + DECLARE_CALLBACK( void, onNode, (const char* node)); + + PathCamera(); + ~PathCamera(); + static void initPersistFields(); + static void consoleInit(); + + void onEditorEnable(); + void onEditorDisable(); + + bool onAdd(); + void onRemove(); + bool onNewDataBlock( GameBaseData *dptr, bool reload ); + void onNode(S32 node); + + void processTick(const Move*); + void interpolateTick(F32 dt); + void getCameraTransform(F32* pos,MatrixF* mat); + + U32 packUpdate(NetConnection *, U32 mask, BitStream *stream); + void unpackUpdate(NetConnection *, BitStream *stream); + + void reset(F32 speed = 1); + void pushFront(CameraSpline::Knot *knot); + void pushBack(CameraSpline::Knot *knot); + void popFront(); + + void setPosition(F32 pos); + void setTarget(F32 pos); + void setState(State s); +}; + + +#endif diff --git a/Engine/source/T3D/physicalZone.cpp b/Engine/source/T3D/physicalZone.cpp new file mode 100644 index 000000000..dc62f7760 --- /dev/null +++ b/Engine/source/T3D/physicalZone.cpp @@ -0,0 +1,445 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "T3D/physicalZone.h" +#include "core/stream/bitStream.h" +#include "collision/boxConvex.h" +#include "collision/clippedPolyList.h" +#include "console/consoleTypes.h" +#include "math/mathIO.h" +#include "scene/sceneRenderState.h" +#include "T3D/trigger.h" +#include "gfx/gfxTransformSaver.h" +#include "renderInstance/renderPassManager.h" +#include "gfx/gfxDrawUtil.h" +#include "console/engineAPI.h" + +IMPLEMENT_CO_NETOBJECT_V1(PhysicalZone); + +ConsoleDocClass( PhysicalZone, + "@brief Physical Zones are areas that modify the player's gravity and/or velocity and/or applied force.\n\n" + + "The datablock properties determine how the physics, velocity and applied forces affect a player who enters this zone.\n" + + "@tsexample\n" + "new PhysicalZone(Team1JumpPad) {\n" + "velocityMod = \"1\";" + "gravityMod = \"0\";\n" + "appliedForce = \"0 0 20000\";\n" + "polyhedron = \"0.0000000 0.0000000 0.0000000 1.0000000 0.0000000 0.0000000 0.0000000 -1.0000000 0.0000000 0.0000000 0.0000000 1.0000000\";\n" + "position = \"273.559 -166.371 249.856\";\n" + "rotation = \"0 0 1 13.0216\";\n" + "scale = \"8 4.95 28.31\";\n" + "isRenderEnabled = \"true\";\n" + "canSaveDynamicFields = \"1\";\n" + "enabled = \"1\";\n" + "};\n" + "@endtsexample\n\n" + "@ingroup enviroMisc\n" +); + +bool PhysicalZone::smRenderPZones = false; + +DefineEngineMethod(PhysicalZone, activate, void, (),, "Activate the physical zone's effects.\n" + "@tsexample\n" + "// Activate effects for a specific physical zone.\n" + "%thisPhysicalZone.activate();\n" + "@endtsexample\n" + "@ingroup Datablocks\n" + ) +{ + if (object->isClientObject()) + return; + + object->activate(); +} + +DefineEngineMethod(PhysicalZone, deactivate, void, (),, "Deactivate the physical zone's effects.\n" + "@tsexample\n" + "// Deactivate effects for a specific physical zone.\n" + "%thisPhysicalZone.deactivate();\n" + "@endtsexample\n" + "@ingroup Datablocks\n" + ) +{ + if (object->isClientObject()) + return; + + object->deactivate(); +} + + +//-------------------------------------------------------------------------- +//-------------------------------------- +// +PhysicalZone::PhysicalZone() +{ + mNetFlags.set(Ghostable | ScopeAlways); + + mTypeMask |= PhysicalZoneObjectType; + + mVelocityMod = 1.0f; + mGravityMod = 1.0f; + mAppliedForce.set(0, 0, 0); + + mConvexList = new Convex; + mActive = true; +} + +PhysicalZone::~PhysicalZone() +{ + delete mConvexList; + mConvexList = NULL; +} + +//-------------------------------------------------------------------------- +void PhysicalZone::consoleInit() +{ + Con::addVariable( "$PhysicalZone::renderZones", TypeBool, &smRenderPZones, "If true, a box will render around the location of all PhysicalZones.\n" + "@ingroup EnviroMisc\n"); +} + +void PhysicalZone::initPersistFields() +{ + addGroup("Misc"); + addField("velocityMod", TypeF32, Offset(mVelocityMod, PhysicalZone), "Multiply velocity of objects entering zone by this value every tick."); + addField("gravityMod", TypeF32, Offset(mGravityMod, PhysicalZone), "Gravity in PhysicalZone. Multiplies against standard gravity."); + addField("appliedForce", TypePoint3F, Offset(mAppliedForce, PhysicalZone), "Three-element floating point value representing forces in three axes to apply to objects entering PhysicalZone."); + addField("polyhedron", TypeTriggerPolyhedron, Offset(mPolyhedron, PhysicalZone), + "The polyhedron type is really a quadrilateral and consists of a corner" + "point followed by three vectors representing the edges extending from the corner." ); + endGroup("Misc"); + + Parent::initPersistFields(); +} + +//-------------------------------------------------------------------------- +bool PhysicalZone::onAdd() +{ + if(!Parent::onAdd()) + return false; + + if (mVelocityMod < -40.0f || mVelocityMod > 40.0f) { + Con::errorf("PhysicalZone: velocity mod out of range. [-40, 40]"); + mVelocityMod = mVelocityMod < -40.0f ? -40.0f : 40.0f; + } + if (mGravityMod < -40.0f || mGravityMod > 40.0f) { + Con::errorf("PhysicalZone: GravityMod out of range. [-40, 40]"); + mGravityMod = mGravityMod < -40.0f ? -40.0f : 40.0f; + } + static const char* coordString[] = { "x", "y", "z" }; + F32* p = mAppliedForce; + for (U32 i = 0; i < 3; i++) { + if (p[i] < -40000.0f || p[i] > 40000.0f) { + Con::errorf("PhysicalZone: applied force: %s out of range. [-40000, 40000]", coordString[i]); + p[i] = p[i] < -40000.0f ? -40000.0f : 40000.0f; + } + } + + Polyhedron temp = mPolyhedron; + setPolyhedron(temp); + + addToScene(); + + return true; +} + + +void PhysicalZone::onRemove() +{ + mConvexList->nukeList(); + + removeFromScene(); + Parent::onRemove(); +} + +void PhysicalZone::inspectPostApply() +{ + setPolyhedron(mPolyhedron); + Parent::inspectPostApply(); +} + +//------------------------------------------------------------------------------ +void PhysicalZone::setTransform(const MatrixF & mat) +{ + Parent::setTransform(mat); + + MatrixF base(true); + base.scale(Point3F(1.0/mObjScale.x, + 1.0/mObjScale.y, + 1.0/mObjScale.z)); + base.mul(mWorldToObj); + mClippedList.setBaseTransform(base); + + if (isServerObject()) + setMaskBits(InitialUpdateMask); +} + + +void PhysicalZone::prepRenderImage( SceneRenderState *state ) +{ + // only render if selected or render flag is set + if ( !smRenderPZones && !isSelected() ) + return; + + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &PhysicalZone::renderObject ); + ri->type = RenderPassManager::RIT_Editor; + ri->defaultKey = 0; + ri->defaultKey2 = 0; + state->getRenderPass()->addInst( ri ); +} + + +void PhysicalZone::renderObject( ObjectRenderInst *ri, + SceneRenderState *state, + BaseMatInstance *overrideMat ) +{ + if (overrideMat) + return; + + GFXStateBlockDesc desc; + desc.setZReadWrite( true, false ); + desc.setBlend( true ); + desc.setCullMode( GFXCullNone ); + + GFXTransformSaver saver; + + MatrixF mat = getRenderTransform(); + mat.scale( getScale() ); + + GFX->multWorld( mat ); + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + drawer->drawPolyhedron( desc, mPolyhedron, ColorI( 0, 255, 0, 45 ) ); + + desc.setFillModeWireframe(); + drawer->drawPolyhedron( desc, mPolyhedron, ColorF::BLACK ); +} + +//-------------------------------------------------------------------------- +U32 PhysicalZone::packUpdate(NetConnection* con, U32 mask, BitStream* stream) +{ + U32 i; + U32 retMask = Parent::packUpdate(con, mask, stream); + + if (stream->writeFlag((mask & InitialUpdateMask) != 0)) { + // Note that we don't really care about efficiency here, since this is an + // edit-only ghost... + mathWrite(*stream, mObjToWorld); + mathWrite(*stream, mObjScale); + + // Write the polyhedron + stream->write(mPolyhedron.pointList.size()); + for (i = 0; i < mPolyhedron.pointList.size(); i++) + mathWrite(*stream, mPolyhedron.pointList[i]); + + stream->write(mPolyhedron.planeList.size()); + for (i = 0; i < mPolyhedron.planeList.size(); i++) + mathWrite(*stream, mPolyhedron.planeList[i]); + + stream->write(mPolyhedron.edgeList.size()); + for (i = 0; i < mPolyhedron.edgeList.size(); i++) { + const Polyhedron::Edge& rEdge = mPolyhedron.edgeList[i]; + + stream->write(rEdge.face[0]); + stream->write(rEdge.face[1]); + stream->write(rEdge.vertex[0]); + stream->write(rEdge.vertex[1]); + } + + stream->write(mVelocityMod); + stream->write(mGravityMod); + mathWrite(*stream, mAppliedForce); + stream->writeFlag(mActive); + } else { + stream->writeFlag(mActive); + } + + return retMask; +} + +void PhysicalZone::unpackUpdate(NetConnection* con, BitStream* stream) +{ + Parent::unpackUpdate(con, stream); + + if (stream->readFlag()) { + U32 i, size; + MatrixF temp; + Point3F tempScale; + Polyhedron tempPH; + + // Transform + mathRead(*stream, &temp); + mathRead(*stream, &tempScale); + + // Read the polyhedron + stream->read(&size); + tempPH.pointList.setSize(size); + for (i = 0; i < tempPH.pointList.size(); i++) + mathRead(*stream, &tempPH.pointList[i]); + + stream->read(&size); + tempPH.planeList.setSize(size); + for (i = 0; i < tempPH.planeList.size(); i++) + mathRead(*stream, &tempPH.planeList[i]); + + stream->read(&size); + tempPH.edgeList.setSize(size); + for (i = 0; i < tempPH.edgeList.size(); i++) { + Polyhedron::Edge& rEdge = tempPH.edgeList[i]; + + stream->read(&rEdge.face[0]); + stream->read(&rEdge.face[1]); + stream->read(&rEdge.vertex[0]); + stream->read(&rEdge.vertex[1]); + } + + stream->read(&mVelocityMod); + stream->read(&mGravityMod); + mathRead(*stream, &mAppliedForce); + + setPolyhedron(tempPH); + setScale(tempScale); + setTransform(temp); + mActive = stream->readFlag(); + } else { + mActive = stream->readFlag(); + } +} + + +//-------------------------------------------------------------------------- +void PhysicalZone::setPolyhedron(const Polyhedron& rPolyhedron) +{ + mPolyhedron = rPolyhedron; + + if (mPolyhedron.pointList.size() != 0) { + mObjBox.minExtents.set(1e10, 1e10, 1e10); + mObjBox.maxExtents.set(-1e10, -1e10, -1e10); + for (U32 i = 0; i < mPolyhedron.pointList.size(); i++) { + mObjBox.minExtents.setMin(mPolyhedron.pointList[i]); + mObjBox.maxExtents.setMax(mPolyhedron.pointList[i]); + } + } else { + mObjBox.minExtents.set(-0.5, -0.5, -0.5); + mObjBox.maxExtents.set( 0.5, 0.5, 0.5); + } + + MatrixF xform = getTransform(); + setTransform(xform); + + mClippedList.clear(); + mClippedList.mPlaneList = mPolyhedron.planeList; + + MatrixF base(true); + base.scale(Point3F(1.0/mObjScale.x, + 1.0/mObjScale.y, + 1.0/mObjScale.z)); + base.mul(mWorldToObj); + + mClippedList.setBaseTransform(base); +} + + +//-------------------------------------------------------------------------- +void PhysicalZone::buildConvex(const Box3F& box, Convex* convex) +{ + // These should really come out of a pool + mConvexList->collectGarbage(); + + Box3F realBox = box; + mWorldToObj.mul(realBox); + realBox.minExtents.convolveInverse(mObjScale); + realBox.maxExtents.convolveInverse(mObjScale); + + if (realBox.isOverlapped(getObjBox()) == false) + return; + + // Just return a box convex for the entire shape... + Convex* cc = 0; + CollisionWorkingList& wl = convex->getWorkingList(); + for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) { + if (itr->mConvex->getType() == BoxConvexType && + itr->mConvex->getObject() == this) { + cc = itr->mConvex; + break; + } + } + if (cc) + return; + + // Create a new convex. + BoxConvex* cp = new BoxConvex; + mConvexList->registerObject(cp); + convex->addToWorkingList(cp); + cp->init(this); + + mObjBox.getCenter(&cp->mCenter); + cp->mSize.x = mObjBox.len_x() / 2.0f; + cp->mSize.y = mObjBox.len_y() / 2.0f; + cp->mSize.z = mObjBox.len_z() / 2.0f; +} + +bool PhysicalZone::testObject(SceneObject* enter) +{ + // TODO: This doesn't look like it's testing against the polyhedron at + // all. And whats the point of building a convex if no collision methods + // are implemented? + + if (mPolyhedron.pointList.size() == 0) + return false; + + mClippedList.clear(); + + SphereF sphere; + sphere.center = (mWorldBox.minExtents + mWorldBox.maxExtents) * 0.5; + VectorF bv = mWorldBox.maxExtents - sphere.center; + sphere.radius = bv.len(); + + enter->buildPolyList(PLC_Collision, &mClippedList, mWorldBox, sphere); + return mClippedList.isEmpty() == false; +} + +bool PhysicalZone::testBox( const Box3F &box ) const +{ + return mWorldBox.isOverlapped( box ); +} + +void PhysicalZone::activate() +{ + AssertFatal(isServerObject(), "Client objects not allowed in ForceFieldInstance::open()"); + + if (mActive != true) + setMaskBits(ActiveMask); + mActive = true; +} + +void PhysicalZone::deactivate() +{ + AssertFatal(isServerObject(), "Client objects not allowed in ForceFieldInstance::close()"); + + if (mActive != false) + setMaskBits(ActiveMask); + mActive = false; +} + diff --git a/Engine/source/T3D/physicalZone.h b/Engine/source/T3D/physicalZone.h new file mode 100644 index 000000000..2f1d72593 --- /dev/null +++ b/Engine/source/T3D/physicalZone.h @@ -0,0 +1,102 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _H_PHYSICALZONE +#define _H_PHYSICALZONE + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _EARLYOUTPOLYLIST_H_ +#include "collision/earlyOutPolyList.h" +#endif +#ifndef _MPOLYHEDRON_H_ +#include "math/mPolyhedron.h" +#endif + +class Convex; + + +class PhysicalZone : public SceneObject +{ + typedef SceneObject Parent; + + enum UpdateMasks { + ActiveMask = Parent::NextFreeMask << 0, + NextFreeMask = Parent::NextFreeMask << 1 + }; + + protected: + static bool smRenderPZones; + + F32 mVelocityMod; + F32 mGravityMod; + Point3F mAppliedForce; + + // Basically ripped from trigger + Polyhedron mPolyhedron; + EarlyOutPolyList mClippedList; + + bool mActive; + + Convex* mConvexList; + void buildConvex(const Box3F& box, Convex* convex); + + public: + PhysicalZone(); + ~PhysicalZone(); + + // SimObject + DECLARE_CONOBJECT(PhysicalZone); + static void consoleInit(); + static void initPersistFields(); + bool onAdd(); + void onRemove(); + void inspectPostApply(); + + // NetObject + U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream); + void unpackUpdate(NetConnection *conn, BitStream *stream); + + // SceneObject + void setTransform(const MatrixF &mat); + void prepRenderImage( SceneRenderState* state ); + + inline F32 getVelocityMod() const { return mVelocityMod; } + inline F32 getGravityMod() const { return mGravityMod; } + inline const Point3F& getForce() const { return mAppliedForce; } + + void setPolyhedron(const Polyhedron&); + bool testObject(SceneObject*); + + bool testBox( const Box3F &box ) const; + + void renderObject( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); + + void activate(); + void deactivate(); + inline bool isActive() const { return mActive; } + +}; + +#endif // _H_PHYSICALZONE + diff --git a/Engine/source/T3D/physics/bullet/bt.h b/Engine/source/T3D/physics/bullet/bt.h new file mode 100644 index 000000000..45ae15414 --- /dev/null +++ b/Engine/source/T3D/physics/bullet/bt.h @@ -0,0 +1,44 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _BULLET_H_ +#define _BULLET_H_ + +// NOTE: We set these defines which bullet needs here. +#ifdef TORQUE_OS_WIN32 +#define WIN32 +#endif + +// NOTE: All the Bullet includes we use should be here and +// nowhere else.... beware! + +#include +#include +#include + +#include +#include +#include +#include + + +#endif // _BULLET_H_ \ No newline at end of file diff --git a/Engine/source/T3D/physics/bullet/btBody.cpp b/Engine/source/T3D/physics/bullet/btBody.cpp new file mode 100644 index 000000000..0c37de0c5 --- /dev/null +++ b/Engine/source/T3D/physics/bullet/btBody.cpp @@ -0,0 +1,374 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/bullet/btBody.h" + +#include "T3D/physics/bullet/bt.h" +#include "T3D/physics/bullet/btCasts.h" +#include "T3D/physics/bullet/btWorld.h" +#include "T3D/physics/bullet/btCollision.h" +#include "math/mBox.h" +#include "console/console.h" + + +BtBody::BtBody() : + mActor( NULL ), + mWorld( NULL ), + mMass( 0.0f ), + mCompound( NULL ), + mCenterOfMass( NULL ), + mInvCenterOfMass( NULL ), + mIsDynamic( false ), + mIsEnabled( false ) +{ +} + +BtBody::~BtBody() +{ + _releaseActor(); +} + +void BtBody::_releaseActor() +{ + if ( mActor ) + { + mWorld->getDynamicsWorld()->removeRigidBody( mActor ); + mActor->setUserPointer( NULL ); + SAFE_DELETE( mActor ); + } + + SAFE_DELETE( mCompound ); + SAFE_DELETE( mCenterOfMass ); + SAFE_DELETE( mInvCenterOfMass ); + + mColShape = NULL; +} + +bool BtBody::init( PhysicsCollision *shape, + F32 mass, + U32 bodyFlags, + SceneObject *obj, + PhysicsWorld *world ) +{ + AssertFatal( obj, "BtBody::init - Got a null scene object!" ); + AssertFatal( world, "BtBody::init - Got a null world!" ); + AssertFatal( dynamic_cast( world ), "BtBody::init - The world is the wrong type!" ); + AssertFatal( shape, "BtBody::init - Got a null collision shape!" ); + AssertFatal( dynamic_cast( shape ), "BtBody::init - The collision shape is the wrong type!" ); + AssertFatal( ((BtCollision*)shape)->getShape(), "BtBody::init - Got empty collision shape!" ); + + // Cleanup any previous actor. + _releaseActor(); + + mWorld = (BtWorld*)world; + + mColShape = (BtCollision*)shape; + btCollisionShape *btColShape = mColShape->getShape(); + MatrixF localXfm = mColShape->getLocalTransform(); + btVector3 localInertia( 0, 0, 0 ); + + // If we have a mass then we're dynamic. + mIsDynamic = mass > 0.0f; + if ( mIsDynamic ) + { + if ( btColShape->isCompound() ) + { + btCompoundShape *btCompound = (btCompoundShape*)btColShape; + + btScalar *masses = new btScalar[ btCompound->getNumChildShapes() ]; + for ( U32 j=0; j < btCompound->getNumChildShapes(); j++ ) + masses[j] = mass / btCompound->getNumChildShapes(); + + btVector3 principalInertia; + btTransform principal; + btCompound->calculatePrincipalAxisTransform( masses, principal, principalInertia ); + delete [] masses; + + // Create a new compound with the shifted children. + btColShape = mCompound = new btCompoundShape(); + for ( U32 i=0; i < btCompound->getNumChildShapes(); i++ ) + { + btTransform newChildTransform = principal.inverse() * btCompound->getChildTransform(i); + mCompound->addChildShape( newChildTransform, btCompound->getChildShape(i) ); + } + + localXfm = btCast( principal ); + } + + // Note... this looks like we're changing the shape, but + // we're not. All this does is ask the shape to calculate the + // local inertia vector from the mass... the shape doesn't change. + btColShape->calculateLocalInertia( mass, localInertia ); + } + + // If we have a local transform then we need to + // store it and the inverse to offset the center + // of mass from the graphics origin. + if ( !localXfm.isIdentity() ) + { + mCenterOfMass = new MatrixF( localXfm ); + mInvCenterOfMass = new MatrixF( *mCenterOfMass ); + mInvCenterOfMass->inverse(); + } + + mMass = mass; + mActor = new btRigidBody( mass, NULL, btColShape, localInertia ); + + int btFlags = mActor->getCollisionFlags(); + + if ( bodyFlags & BF_TRIGGER ) + btFlags |= btCollisionObject::CF_NO_CONTACT_RESPONSE; + if ( bodyFlags & BF_KINEMATIC ) + { + btFlags &= ~btCollisionObject::CF_STATIC_OBJECT; + btFlags |= btCollisionObject::CF_KINEMATIC_OBJECT; + } + + mActor->setCollisionFlags( btFlags ); + + mWorld->getDynamicsWorld()->addRigidBody( mActor ); + mIsEnabled = true; + + mUserData.setObject( obj ); + mUserData.setBody( this ); + mActor->setUserPointer( &mUserData ); + + return true; +} + +void BtBody::setMaterial( F32 restitution, + F32 friction, + F32 staticFriction ) +{ + AssertFatal( mActor, "BtBody::setMaterial - The actor is null!" ); + + mActor->setRestitution( restitution ); + + // TODO: Weird.. Bullet doesn't have seperate dynamic + // and static friction. + // + // Either add it and submit it as an official patch + // or hack it via contact reporting or something + // like that. + + mActor->setFriction( friction ); + + // Wake it up... it may need to move. + mActor->activate(); +} + +void BtBody::setSleepThreshold( F32 linear, F32 angular ) +{ + AssertFatal( mActor, "BtBody::setSleepThreshold - The actor is null!" ); + mActor->setSleepingThresholds( linear, angular ); +} + +void BtBody::setDamping( F32 linear, F32 angular ) +{ + AssertFatal( mActor, "BtBody::setDamping - The actor is null!" ); + mActor->setDamping( linear, angular ); +} + +void BtBody::getState( PhysicsState *outState ) +{ + AssertFatal( isDynamic(), "BtBody::getState - This call is only for dynamics!" ); + + // TODO: Fix this to do what we intended... to return + // false so that the caller can early out of the state + // hasn't changed since the last tick. + + MatrixF trans; + if ( mInvCenterOfMass ) + trans.mul( btCast( mActor->getCenterOfMassTransform() ), *mInvCenterOfMass ); + else + trans = btCast( mActor->getCenterOfMassTransform() ); + + outState->position = trans.getPosition(); + outState->orientation.set( trans ); + outState->linVelocity = btCast( mActor->getLinearVelocity() ); + outState->angVelocity = btCast( mActor->getAngularVelocity() ); + outState->sleeping = !mActor->isActive(); + + // Bullet doesn't keep the momentum... recalc it. + outState->momentum = ( 1.0f / mActor->getInvMass() ) * outState->linVelocity; +} + +Point3F BtBody::getCMassPosition() const +{ + AssertFatal( mActor, "BtBody::getCMassPosition - The actor is null!" ); + return btCast( mActor->getCenterOfMassTransform().getOrigin() ); +} + +void BtBody::setLinVelocity( const Point3F &vel ) +{ + AssertFatal( mActor, "BtBody::setLinVelocity - The actor is null!" ); + AssertFatal( isDynamic(), "BtBody::setLinVelocity - This call is only for dynamics!" ); + + mActor->setLinearVelocity( btCast( vel ) ); +} + +void BtBody::setAngVelocity( const Point3F &vel ) +{ + AssertFatal( mActor, "BtBody::setAngVelocity - The actor is null!" ); + AssertFatal( isDynamic(), "BtBody::setAngVelocity - This call is only for dynamics!" ); + + mActor->setAngularVelocity( btCast( vel ) ); +} + +Point3F BtBody::getLinVelocity() const +{ + AssertFatal( mActor, "BtBody::getLinVelocity - The actor is null!" ); + AssertFatal( isDynamic(), "BtBody::getLinVelocity - This call is only for dynamics!" ); + + return btCast( mActor->getLinearVelocity() ); +} + +Point3F BtBody::getAngVelocity() const +{ + AssertFatal( mActor, "BtBody::getAngVelocity - The actor is null!" ); + AssertFatal( isDynamic(), "BtBody::getAngVelocity - This call is only for dynamics!" ); + + return btCast( mActor->getAngularVelocity() ); +} + +void BtBody::setSleeping( bool sleeping ) +{ + AssertFatal( mActor, "BtBody::setSleeping - The actor is null!" ); + AssertFatal( isDynamic(), "BtBody::setSleeping - This call is only for dynamics!" ); + + if ( sleeping ) + { + //mActor->setCollisionFlags( mActor->getCollisionFlags() | btCollisionObject::CF_KINEMATIC_OBJECT ); + mActor->setActivationState( WANTS_DEACTIVATION ); + mActor->setDeactivationTime( 0.0f ); + } + else + { + //mActor->setCollisionFlags( mActor->getCollisionFlags() & ~btCollisionObject::CF_KINEMATIC_OBJECT ); + mActor->activate(); + } +} + +PhysicsWorld* BtBody::getWorld() +{ + return mWorld; +} + +PhysicsCollision* BtBody::getColShape() +{ + return mColShape; +} + +MatrixF& BtBody::getTransform( MatrixF *outMatrix ) +{ + AssertFatal( mActor, "BtBody::getTransform - The actor is null!" ); + + if ( mInvCenterOfMass ) + outMatrix->mul( *mInvCenterOfMass, btCast( mActor->getCenterOfMassTransform() ) ); + else + *outMatrix = btCast( mActor->getCenterOfMassTransform() ); + + return *outMatrix; +} + +void BtBody::setTransform( const MatrixF &transform ) +{ + AssertFatal( mActor, "BtBody::setTransform - The actor is null!" ); + + if ( mCenterOfMass ) + { + MatrixF xfm; + xfm.mul( transform, *mCenterOfMass ); + mActor->setCenterOfMassTransform( btCast( xfm ) ); + } + else + mActor->setCenterOfMassTransform( btCast( transform ) ); + + // If its dynamic we have more to do. + if ( isDynamic() ) + { + // Clear any velocity and forces... this is a warp. + mActor->clearForces(); + mActor->setLinearVelocity( btVector3( 0, 0, 0 ) ); + mActor->setAngularVelocity( btVector3( 0, 0, 0 ) ); + mActor->activate(); + } +} + +void BtBody::applyCorrection( const MatrixF &transform ) +{ + AssertFatal( mActor, "BtBody::applyCorrection - The actor is null!" ); + AssertFatal( isDynamic(), "BtBody::applyCorrection - This call is only for dynamics!" ); + + if ( mCenterOfMass ) + { + MatrixF xfm; + xfm.mul( transform, *mCenterOfMass ); + mActor->setCenterOfMassTransform( btCast( xfm ) ); + } + else + mActor->setCenterOfMassTransform( btCast( transform ) ); +} + +void BtBody::applyImpulse( const Point3F &origin, const Point3F &force ) +{ + AssertFatal( mActor, "BtBody::applyImpulse - The actor is null!" ); + AssertFatal( isDynamic(), "BtBody::applyImpulse - This call is only for dynamics!" ); + + if ( mCenterOfMass ) + { + Point3F relOrigin( origin ); + mCenterOfMass->mulP( relOrigin ); + Point3F relForce( force ); + mCenterOfMass->mulV( relForce ); + mActor->applyImpulse( btCast( relForce ), btCast( relOrigin ) ); + } + else + mActor->applyImpulse( btCast( force ), btCast( origin ) ); + + if ( !mActor->isActive() ) + mActor->activate(); +} + +Box3F BtBody::getWorldBounds() +{ + btVector3 min, max; + mActor->getAabb( min, max ); + + Box3F bounds( btCast( min ), btCast( max ) ); + + return bounds; +} + +void BtBody::setSimulationEnabled( bool enabled ) +{ + if ( mIsEnabled == enabled ) + return; + + if ( !enabled ) + mWorld->getDynamicsWorld()->removeRigidBody( mActor ); + else + mWorld->getDynamicsWorld()->addRigidBody( mActor ); + + mIsEnabled = enabled; +} diff --git a/Engine/source/T3D/physics/bullet/btBody.h b/Engine/source/T3D/physics/bullet/btBody.h new file mode 100644 index 000000000..0f1ab669c --- /dev/null +++ b/Engine/source/T3D/physics/bullet/btBody.h @@ -0,0 +1,116 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _T3D_PHYSICS_BTBODY_H_ +#define _T3D_PHYSICS_BTBODY_H_ + +#ifndef _T3D_PHYSICS_PHYSICSBODY_H_ +#include "T3D/physics/physicsBody.h" +#endif +#ifndef _REFBASE_H_ +#include "core/util/refBase.h" +#endif +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif + +class BtWorld; +class btRigidBody; +class btCompoundShape; +class BtCollision; + + +class BtBody : public PhysicsBody +{ +protected: + + /// The physics world we are in. + BtWorld *mWorld; + + /// The physics actor. + btRigidBody *mActor; + + /// The collision representation. + StrongRefPtr mColShape; + + /// Our local compound if we had to adjust + /// the mass center on a dynamic. + btCompoundShape *mCompound; + + /// + F32 mMass; + + /// + bool mIsDynamic; + + /// Is the body participating in the physics simulation. + bool mIsEnabled; + + /// The center of mass offset used if the graphical + /// transform is not at the mass center. + MatrixF *mCenterOfMass; + + /// The inverse center of mass offset. + MatrixF *mInvCenterOfMass; + + /// + void _releaseActor(); + +public: + + BtBody(); + virtual ~BtBody(); + + // PhysicsObject + virtual PhysicsWorld* getWorld(); + virtual void setTransform( const MatrixF &xfm ); + virtual MatrixF& getTransform( MatrixF *outMatrix ); + virtual Box3F getWorldBounds(); + virtual void setSimulationEnabled( bool enabled ); + virtual bool isSimulationEnabled() { return mIsEnabled; } + + // PhysicsBody + virtual bool init( PhysicsCollision *shape, + F32 mass, + U32 bodyFlags, + SceneObject *obj, + PhysicsWorld *world ); + virtual bool isDynamic() const { return mIsDynamic; } + virtual PhysicsCollision* getColShape(); + virtual void setSleepThreshold( F32 linear, F32 angular ); + virtual void setDamping( F32 linear, F32 angular ); + virtual void getState( PhysicsState *outState ); + virtual F32 getMass() const { return mMass; } + virtual Point3F getCMassPosition() const; + virtual void setLinVelocity( const Point3F &vel ); + virtual void setAngVelocity( const Point3F &vel ); + virtual Point3F getLinVelocity() const; + virtual Point3F getAngVelocity() const; + virtual void setSleeping( bool sleeping ); + virtual void setMaterial( F32 restitution, + F32 friction, + F32 staticFriction ); + virtual void applyCorrection( const MatrixF &xfm ); + virtual void applyImpulse( const Point3F &origin, const Point3F &force ); +}; + +#endif // _T3D_PHYSICS_BTBODY_H_ diff --git a/Engine/source/T3D/physics/bullet/btCasts.h b/Engine/source/T3D/physics/bullet/btCasts.h new file mode 100644 index 000000000..fbb971ee1 --- /dev/null +++ b/Engine/source/T3D/physics/bullet/btCasts.h @@ -0,0 +1,102 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _BULLET_CASTS_H_ +#define _BULLET_CASTS_H_ + +#ifndef _BULLET_H_ +#include "T3D/physics/bullet/bt.h" +#endif +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _MQUAT_H_ +#include "math/mQuat.h" +#endif + +template inline T btCast( const F &from ); + +//------------------------------------------------------------------------- + +template<> +inline Point3F btCast( const btVector3 &vec ) +{ + return Point3F( vec.x(), vec.y(), vec.z() ); +} + +template<> +inline btVector3 btCast( const Point3F &point ) +{ + return btVector3( point.x, point.y, point.z ); +} + +template<> +inline QuatF btCast( const btQuaternion &quat ) +{ + /// The Torque quat has the opposite winding order. + return QuatF( -quat.x(), -quat.y(), -quat.z(), quat.w() ); +} + +template<> +inline btQuaternion btCast( const QuatF &quat ) +{ + /// The Torque quat has the opposite winding order. + return btQuaternion( -quat.x, -quat.y, -quat.z, quat.w ); +} + +template<> +inline btTransform btCast( const MatrixF &xfm ) +{ + btTransform out; + out.getBasis().setValue( xfm[0], xfm[1], xfm[2], + xfm[4], xfm[5], xfm[6], + xfm[8], xfm[9], xfm[10] ); + out.getOrigin().setValue( xfm[3], xfm[7], xfm[11] ); + return out; +} + +template<> +inline MatrixF btCast( const btTransform &xfm ) +{ + MatrixF out; + + // Set the rotation. + out.setRow( 0, btCast( xfm.getBasis()[0] ) ); + out.setRow( 1, btCast( xfm.getBasis()[1] ) ); + out.setRow( 2, btCast( xfm.getBasis()[2] ) ); + + // The position. + out[3] = xfm.getOrigin().x(); + out[7] = xfm.getOrigin().y(); + out[11] = xfm.getOrigin().z(); + + // Clear out the rest. + out[12] = out[13] = out[14] = 0.0f; + out[15] = 1.0f; + + return out; +} + +#endif // _BULLET_CASTS_H_ \ No newline at end of file diff --git a/Engine/source/T3D/physics/bullet/btCollision.cpp b/Engine/source/T3D/physics/bullet/btCollision.cpp new file mode 100644 index 000000000..d81bd2c5b --- /dev/null +++ b/Engine/source/T3D/physics/bullet/btCollision.cpp @@ -0,0 +1,205 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/bullet/btCollision.h" + +#include "math/mPoint3.h" +#include "math/mMatrix.h" +#include "T3D/physics/bullet/bt.h" +#include "T3D/physics/bullet/btCasts.h" + + +BtCollision::BtCollision() + : mCompound( NULL ), + mLocalXfm( true ) +{ +} + +BtCollision::~BtCollision() +{ + SAFE_DELETE( mCompound ); + + for ( U32 i=0; i < mShapes.size(); i++ ) + delete mShapes[i]; + + for ( U32 i=0; i < mMeshInterfaces.size(); i++ ) + delete mMeshInterfaces[i]; +} + +btCollisionShape* BtCollision::getShape() +{ + if ( mCompound ) + return mCompound; + + if ( mShapes.empty() ) + return NULL; + + return mShapes.first(); +} + +void BtCollision::_addShape( btCollisionShape *shape, const MatrixF &localXfm ) +{ + AssertFatal( !shape->isCompound(), "BtCollision::_addShape - Shape should not be a compound!" ); + + // Stick the shape into the array to delete later. Remember + // that the compound shape doesn't delete its children. + mShapes.push_back( shape ); + + // If this is the first shape then just store the + // local transform and we're done. + if ( mShapes.size() == 1 ) + { + mLocalXfm = localXfm; + return; + } + + // We use a compound to store the shapes with their + // local transforms... so create it if we haven't already. + if ( !mCompound ) + { + mCompound = new btCompoundShape(); + + // There should only be one shape now... add it and + // clear the local transform. + mCompound->addChildShape( btCast( mLocalXfm ), mShapes.first() ); + mLocalXfm = MatrixF::Identity; + } + + // Add the new shape to the compound. + mCompound->addChildShape( btCast( localXfm ), shape ); +} + +void BtCollision::addPlane( const PlaneF &plane ) +{ + // NOTE: Torque uses a negative D... thats why we flip it here. + btStaticPlaneShape *shape = new btStaticPlaneShape( btVector3( plane.x, plane.y, plane.z ), -plane.d ); + _addShape( shape, MatrixF::Identity ); +} + +void BtCollision::addBox( const Point3F &halfWidth, + const MatrixF &localXfm ) +{ + btBoxShape *shape = new btBoxShape( btVector3( halfWidth.x, halfWidth.y, halfWidth.z ) ); + shape->setMargin( 0.01f ); + _addShape( shape, localXfm ); +} + +void BtCollision::addSphere( const F32 radius, + const MatrixF &localXfm ) +{ + btSphereShape *shape = new btSphereShape( radius ); + shape->setMargin( 0.01f ); + _addShape( shape, localXfm ); +} + +void BtCollision::addCapsule( F32 radius, + F32 height, + const MatrixF &localXfm ) +{ + btCapsuleShape *shape = new btCapsuleShape( radius, height ); + shape->setMargin( 0.01f ); + _addShape( shape, localXfm ); +} + +bool BtCollision::addConvex( const Point3F *points, + U32 count, + const MatrixF &localXfm ) +{ + btConvexHullShape *shape = new btConvexHullShape( (btScalar*)points, count, sizeof( Point3F ) ); + shape->setMargin( 0.01f ); + _addShape( shape, localXfm ); + return true; +} + +bool BtCollision::addTriangleMesh( const Point3F *vert, + U32 vertCount, + const U32 *index, + U32 triCount, + const MatrixF &localXfm ) +{ + // Setup the interface for loading the triangles. + btTriangleMesh *meshInterface = new btTriangleMesh( true, false ); + for ( ; triCount-- ; ) + { + meshInterface->addTriangle( btCast( vert[ *( index + 0 ) ] ), + btCast( vert[ *( index + 1 ) ] ), + btCast( vert[ *( index + 2 ) ] ), + false ); + + index += 3; + } + mMeshInterfaces.push_back( meshInterface ); + + btBvhTriangleMeshShape *shape = new btBvhTriangleMeshShape( meshInterface, true, true ); + shape->setMargin( 0.01f ); + _addShape( shape, localXfm ); + + return true; +} + +bool BtCollision::addHeightfield( const U16 *heights, + const bool *holes, // TODO: Bullet height fields do not support holes + U32 blockSize, + F32 metersPerSample, + const MatrixF &localXfm ) +{ + // We pass the absolute maximum and minimum of a U16 height + // field and not the actual min and max. This helps with + // placement. + const F32 heightScale = 0.03125f; + const F32 minHeight = 0; + const F32 maxHeight = 65535 * heightScale; + + btHeightfieldTerrainShape *shape = new btHeightfieldTerrainShape( blockSize, blockSize, + (void*)heights, + heightScale, + minHeight, maxHeight, + 2, // Z up! + PHY_SHORT, + false ); + shape->setMargin( 0.01f ); + shape->setLocalScaling( btVector3( metersPerSample, metersPerSample, 1.0f ) ); + shape->setUseDiamondSubdivision( true ); + + // The local axis of the heightfield is the exact center of + // its bounds defined as... + // + // ( blockSize * samplesPerMeter, blockSize * samplesPerMeter, maxHeight ) / 2.0f + // + // So we create a local transform to move it to the min point + // of the bounds so it matched Torque terrain. + Point3F offset( (F32)blockSize * metersPerSample / 2.0f, + (F32)blockSize * metersPerSample / 2.0f, + maxHeight / 2.0f ); + + // And also bump it by half a sample square size. + offset.x -= metersPerSample / 2.0f; + offset.y -= metersPerSample / 2.0f; + + MatrixF offsetXfm( true ); + offsetXfm.setPosition( offset ); + + _addShape( shape, offsetXfm ); + + return true; +} diff --git a/Engine/source/T3D/physics/bullet/btCollision.h b/Engine/source/T3D/physics/bullet/btCollision.h new file mode 100644 index 000000000..2a38010c4 --- /dev/null +++ b/Engine/source/T3D/physics/bullet/btCollision.h @@ -0,0 +1,98 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _T3D_PHYSICS_BTCOLLISION_H_ +#define _T3D_PHYSICS_BTCOLLISION_H_ + +#ifndef _T3D_PHYSICS_PHYSICSCOLLISION_H_ +#include "T3D/physics/physicsCollision.h" +#endif +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +class btCollisionShape; +class btCompoundShape; +class btTriangleMesh; + + +class BtCollision : public PhysicsCollision +{ +protected: + + /// The compound if we have more than one collision shape. + btCompoundShape *mCompound; + + /// The concrete collision shapes. + Vector mShapes; + + /// The local transform for the collision shape + /// or identity if this is a compound. + MatrixF mLocalXfm; + + /// If we have any triangle mesh collision shapes then + /// we need to store the mesh data. + Vector mMeshInterfaces; + + /// Helper for adding shapes. + void _addShape( btCollisionShape *shape, const MatrixF &localXfm ); + +public: + + BtCollision(); + virtual ~BtCollision(); + + /// Return the Bullet collision shape. + btCollisionShape* getShape(); + + // The local transform used to offset the collsion + // to its correct graphics position. + const MatrixF& getLocalTransform() const { return mLocalXfm; } + + // PhysicsCollision + virtual void addPlane( const PlaneF &plane ); + virtual void addBox( const Point3F &halfWidth, + const MatrixF &localXfm ); + virtual void addSphere( F32 radius, + const MatrixF &localXfm ); + virtual void addCapsule( F32 radius, + F32 height, + const MatrixF &localXfm ); + virtual bool addConvex( const Point3F *points, + U32 count, + const MatrixF &localXfm ); + virtual bool addTriangleMesh( const Point3F *vert, + U32 vertCount, + const U32 *index, + U32 triCount, + const MatrixF &localXfm ); + virtual bool addHeightfield( const U16 *heights, + const bool *holes, + U32 blockSize, + F32 metersPerSample, + const MatrixF &localXfm ); +}; + +#endif // _T3D_PHYSICS_BTCOLLISION_H_ \ No newline at end of file diff --git a/Engine/source/T3D/physics/bullet/btDebugDraw.cpp b/Engine/source/T3D/physics/bullet/btDebugDraw.cpp new file mode 100644 index 000000000..7a8dbb052 --- /dev/null +++ b/Engine/source/T3D/physics/bullet/btDebugDraw.cpp @@ -0,0 +1,85 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/bullet/btDebugDraw.h" + +#include "T3D/physics/bullet/btCasts.h" +#include "gfx/gfxDevice.h" +#include "math/util/frustum.h" +#include "gfx/primBuilder.h" + + +void BtDebugDraw::drawLine( const btVector3 &fromBt, const btVector3 &toBt, const btVector3 &color ) +{ + Point3F from = btCast( fromBt ); + Point3F to = btCast( toBt ); + + // Cull first if we have a frustum. + //F32 distSquared = ( mCuller->getPosition() - from ).lenSquared(); + //if ( mCuller && distSquared > ( 150 * 150 ) ) //!mCuller->clipSegment( from, to ) ) + //return; + + // Do we need to flush the builder? + if ( mVertexCount + 2 >= 1000 ) + flush(); + + // Are we starting a new primitive? + if ( mVertexCount == 0 ) + PrimBuild::begin( GFXLineList, 1000 ); + + PrimBuild::color3f( color.x(), color.y(), color.z() ); + PrimBuild::vertex3f( from.x, from.y, from.z ); + PrimBuild::vertex3f( to.x, to.y, to.z ); + + mVertexCount += 2; +} + +void BtDebugDraw::drawTriangle( const btVector3 &v0, + const btVector3 &v1, + const btVector3 &v2, + const btVector3 &color, + btScalar /*alpha*/ ) +{ + drawLine(v0,v1,color); + drawLine(v1,v2,color); + drawLine(v2,v0,color); +} + +void BtDebugDraw::drawContactPoint( const btVector3 &pointOnB, + const btVector3 &normalOnB, + btScalar distance, + int lifeTime, const + btVector3 &color ) +{ + drawLine( pointOnB, pointOnB+normalOnB*distance, color ); +} + +void BtDebugDraw::flush() +{ + // Do we have verts to render? + if ( mVertexCount == 0 ) + return; + + PrimBuild::end(); + mVertexCount = 0; +} diff --git a/Engine/source/T3D/physics/bullet/btDebugDraw.h b/Engine/source/T3D/physics/bullet/btDebugDraw.h new file mode 100644 index 000000000..d1d33ee46 --- /dev/null +++ b/Engine/source/T3D/physics/bullet/btDebugDraw.h @@ -0,0 +1,70 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _T3D_PHYSICS_BTDEBUGDRAW_H_ +#define _T3D_PHYSICS_BTDEBUGDRAW_H_ + +#ifndef _BULLET_H_ +#include "T3D/physics/bullet/bt.h" +#endif + +class Frustum; + + +class BtDebugDraw : public btIDebugDraw +{ +protected: + + /// The number of verts we've used in rendering. + U32 mVertexCount; + + /// The frustum to use for culling or NULL. + const Frustum *mCuller; + +public: + + BtDebugDraw() + : mVertexCount( 0 ), + mCuller( NULL ) + { + } + + /// Sets the culler which we use to cull out primitives + /// that are completely offscreen. + void setCuller( const Frustum *culler ) { mCuller = culler; } + + /// Call this after debug drawing to submit any + /// remaining primitives for rendering. + void flush(); + + // btIDebugDraw + virtual void drawLine( const btVector3 &from, const btVector3 &to, const btVector3 &color ); + virtual void drawTriangle(const btVector3& v0,const btVector3& v1,const btVector3& v2,const btVector3& color, btScalar /*alpha*/); + virtual void drawContactPoint( const btVector3 &PointOnB, const btVector3 &normalOnB, btScalar distance, int lifeTime, const btVector3 &color ); + virtual void reportErrorWarning( const char *warningString ) {} + virtual void draw3dText( const btVector3 &location, const char *textString ) {} + virtual void setDebugMode( int debugMode ) {} + virtual int getDebugMode() const { return DBG_DrawWireframe; } +}; + + +#endif // _T3D_PHYSICS_BTDEBUGDRAW_H_ \ No newline at end of file diff --git a/Engine/source/T3D/physics/bullet/btPlayer.cpp b/Engine/source/T3D/physics/bullet/btPlayer.cpp new file mode 100644 index 000000000..307abed6a --- /dev/null +++ b/Engine/source/T3D/physics/bullet/btPlayer.cpp @@ -0,0 +1,513 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/bullet/btPlayer.h" + +#include "T3D/physics/physicsPlugin.h" +#include "T3D/physics/bullet/btWorld.h" +#include "T3D/physics/bullet/btCasts.h" +#include "collision/collision.h" + +BtPlayer::BtPlayer() + : PhysicsPlayer(), + mWorld( NULL ), + mObject( NULL ), + mGhostObject( NULL ), + mColShape( NULL ), + mOriginOffset( 0.0f ) +{ +} + +BtPlayer::~BtPlayer() +{ + _releaseController(); +} + +void BtPlayer::_releaseController() +{ + if ( !mGhostObject ) + return; + + mWorld->getDynamicsWorld()->removeCollisionObject( mGhostObject ); + + SAFE_DELETE( mGhostObject ); + SAFE_DELETE( mColShape ); +} + +void BtPlayer::init( const char *type, + const Point3F &size, + F32 runSurfaceCos, + F32 stepHeight, + SceneObject *obj, + PhysicsWorld *world ) +{ + AssertFatal( obj, "BtPlayer::init - Got a null scene object!" ); + AssertFatal( world, "BtPlayer::init - Got a null world!" ); + AssertFatal( dynamic_cast( world ), "BtPlayer::init - The world is the wrong type!" ); + + // Cleanup any previous controller. + _releaseController(); + + mObject = obj; + mWorld = (BtWorld*)world; + + mStepHeight = stepHeight; + + //if ( dStricmp( type, "Capsule" ) == 0 ) + { + F32 radius = getMax( size.x, size.y ) * 0.5f; + F32 height = size.z - ( radius * 2.0f ); + mColShape = new btCapsuleShapeZ( radius, height ); + mColShape->setMargin( 0.05f ); + mOriginOffset = ( height * 0.5 ) + radius; + } + //else + { + //mColShape = new btBoxShape( btVector3( 0.5f, 0.5f, 1.0f ) ); + //mOriginOffset = 1.0f; + } + + mGhostObject = new btPairCachingGhostObject(); + mGhostObject->setCollisionShape( mColShape ); + mGhostObject->setCollisionFlags( btCollisionObject::CF_CHARACTER_OBJECT ); + mWorld->getDynamicsWorld()->addCollisionObject( mGhostObject, + btBroadphaseProxy::CharacterFilter, + btBroadphaseProxy::StaticFilter | btBroadphaseProxy::DefaultFilter ); + + mUserData.setObject( obj ); + mGhostObject->setUserPointer( &mUserData ); +} + +Point3F BtPlayer::move( const VectorF &disp, CollisionList &outCol ) +{ + AssertFatal( mGhostObject, "BtPlayer::move - The controller is null!" ); + + // First recover from any penetrations from the previous tick. + U32 numPenetrationLoops = 0; + bool touchingContact = false; + while ( _recoverFromPenetration() ) + { + numPenetrationLoops++; + touchingContact = true; + if ( numPenetrationLoops > 4 ) + break; + } + + btTransform newTrans = mGhostObject->getWorldTransform(); + btVector3 newPos = newTrans.getOrigin(); + + // The move consists of 3 steps... the up step, the forward + // step, and the down step. + + btVector3 forwardSweep( disp.x, disp.y, 0.0f ); + const bool hasForwardSweep = forwardSweep.length2() > 0.0f; + F32 upSweep = 0.0f; + F32 downSweep = 0.0f; + if ( disp[2] < 0.0f ) + downSweep = disp[2]; + else + upSweep = disp[2]; + + // Only do auto stepping if the character is moving forward. + F32 stepOffset = mStepHeight; + if ( hasForwardSweep ) + upSweep += stepOffset; + + // First we do the up step which includes the passed in + // upward displacement as well as the auto stepping. + if ( upSweep > 0.0f && + _sweep( &newPos, btVector3( 0.0f, 0.0f, upSweep ), NULL ) ) + { + // Keep track of how far we actually swept to make sure + // we do not remove too much in the down sweep. + F32 delta = newPos[2] - newTrans.getOrigin()[2]; + if ( delta < stepOffset ) + stepOffset = delta; + } + + // Now do the forward step. + _stepForward( &newPos, forwardSweep, &outCol ); + + // Now remove what remains of our auto step + // from the down sweep. + if ( hasForwardSweep ) + downSweep -= stepOffset; + + // Do the downward sweep. + if ( downSweep < 0.0f ) + _sweep( &newPos, btVector3( 0.0f, 0.0f, downSweep ), &outCol ); + + // Finally update the ghost with its new position. + newTrans.setOrigin( newPos ); + mGhostObject->setWorldTransform( newTrans ); + + // Return the current position of the ghost. + newPos[2] -= mOriginOffset; + return btCast( newPos ); +} + +bool BtPlayer::_recoverFromPenetration() +{ + bool penetration = false; + + btDynamicsWorld *collWorld = mWorld->getDynamicsWorld(); + + collWorld->getDispatcher()->dispatchAllCollisionPairs( mGhostObject->getOverlappingPairCache(), + collWorld->getDispatchInfo(), + collWorld->getDispatcher() ); + + btVector3 currPos = mGhostObject->getWorldTransform().getOrigin(); + btScalar maxPen = 0.0f; + btManifoldArray manifoldArray; + + for ( U32 i = 0; i < mGhostObject->getOverlappingPairCache()->getNumOverlappingPairs(); i++ ) + { + btBroadphasePair *collisionPair = &mGhostObject->getOverlappingPairCache()->getOverlappingPairArray()[i]; + + if ( ((btCollisionObject*)collisionPair->m_pProxy0->m_clientObject)->getCollisionFlags() & btCollisionObject::CF_NO_CONTACT_RESPONSE || + ((btCollisionObject*)collisionPair->m_pProxy1->m_clientObject)->getCollisionFlags() & btCollisionObject::CF_NO_CONTACT_RESPONSE ) + continue; + + manifoldArray.resize(0); + if (collisionPair->m_algorithm) + collisionPair->m_algorithm->getAllContactManifolds(manifoldArray); + + for ( U32 j=0; j < manifoldArray.size(); j++ ) + { + btPersistentManifold* manifold = manifoldArray[j]; + btScalar directionSign = manifold->getBody0() == mGhostObject ? -1.0f : 1.0f; + + for ( U32 p=0; p < manifold->getNumContacts(); p++ ) + { + const btManifoldPoint&pt = manifold->getContactPoint(p); + + if ( pt.getDistance() < -mColShape->getMargin() ) + { + if ( pt.getDistance() < maxPen ) + { + maxPen = pt.getDistance(); + //m_touchingNormal = pt.m_normalWorldOnB * directionSign;//?? + } + + currPos += pt.m_normalWorldOnB * directionSign * pt.getDistance(); // * 0.25f; + penetration = true; + } + else + { + //printf("touching %f\n", pt.getDistance()); + } + } + + //manifold->clearManifold(); + } + } + + // Update the ghost transform. + btTransform newTrans = mGhostObject->getWorldTransform(); + newTrans.setOrigin( currPos ); + mGhostObject->setWorldTransform( newTrans ); + + return penetration; +} + + +class BtPlayerSweepCallback : public btCollisionWorld::ClosestConvexResultCallback +{ + typedef btCollisionWorld::ClosestConvexResultCallback Parent; + +public: + + BtPlayerSweepCallback( btCollisionObject *me, const btVector3 &moveVec ) + : Parent( btVector3(0.0, 0.0, 0.0), btVector3(0.0, 0.0, 0.0) ), + mMe( me ), + mMoveVec( moveVec ) + { + } + + virtual bool needsCollision(btBroadphaseProxy* proxy0) const + { + if ( proxy0->m_clientObject == mMe ) + return false; + + return Parent::needsCollision( proxy0 ); + } + + virtual btScalar addSingleResult( btCollisionWorld::LocalConvexResult &convexResult, + bool normalInWorldSpace ) + { + // NOTE: I shouldn't have to do any of this, but Bullet + // has some weird bugs. + // + // For one the plane type will return hits on a Z up surface + // for sweeps that have no Z sweep component. + // + // Second the normal returned here is sometimes backwards + // to the sweep direction... no clue why. + // + F32 dotN = mMoveVec.dot( convexResult.m_hitNormalLocal ); + if ( mFabs( dotN ) < 0.1f ) + return 1.0f; + + if ( convexResult.m_hitCollisionObject->getCollisionFlags() & btCollisionObject::CF_NO_CONTACT_RESPONSE ) + return 1.0f; + + return Parent::addSingleResult( convexResult, normalInWorldSpace ); + } + +protected: + btVector3 mMoveVec; + btCollisionObject *mMe; +}; + +bool BtPlayer::_sweep( btVector3 *inOutCurrPos, const btVector3 &disp, CollisionList *outCol ) +{ + btTransform start( btTransform::getIdentity() ); + start.setOrigin ( *inOutCurrPos ); + + btTransform end( btTransform::getIdentity() ); + end.setOrigin ( *inOutCurrPos + disp ); + + BtPlayerSweepCallback callback( mGhostObject, disp.normalized() ); + callback.m_collisionFilterGroup = mGhostObject->getBroadphaseHandle()->m_collisionFilterGroup; + callback.m_collisionFilterMask = mGhostObject->getBroadphaseHandle()->m_collisionFilterMask; + + mGhostObject->convexSweepTest( mColShape, start, end, callback, 0.0f ); + + inOutCurrPos->setInterpolate3( start.getOrigin(), end.getOrigin(), callback.m_closestHitFraction ); + if ( callback.hasHit() ) + { + if ( outCol ) + { + Collision& col = outCol->increment(); + dMemset( &col, 0, sizeof( col ) ); + + col.normal = btCast( callback.m_hitNormalWorld ); + col.object = PhysicsUserData::getObject( callback.m_hitCollisionObject->getUserPointer() ); + + if (disp.z() < 0.0f) + { + // We're sweeping down as part of the stepping routine. In this + // case we want to have the collision normal only point in the opposite direction. + // i.e. up If we include the sideways part of the normal then the Player class + // velocity calculations using this normal will affect the player's forwards + // momentum. This is especially noticable on stairs as the rounded bottom of + // the capsule slides up the corner of a stair. + col.normal.set(0.0f, 0.0f, 1.0f); + } + } + + return true; + } + + return false; +} + +void BtPlayer::_stepForward( btVector3 *inOutCurrPos, const btVector3 &displacement, CollisionList *outCol ) +{ + btTransform start( btTransform::getIdentity() ); + btTransform end( btTransform::getIdentity() ); + F32 fraction = 1.0f; + S32 maxIter = 10; + btVector3 disp = displacement; + + while ( fraction > 0.01f && maxIter-- > 0 ) + { + // Setup the sweep start and end transforms. + start.setOrigin( *inOutCurrPos ); + end.setOrigin( *inOutCurrPos + disp ); + + BtPlayerSweepCallback callback( mGhostObject, disp.length2() > 0.0f ? disp.normalized() : disp ); + callback.m_collisionFilterGroup = mGhostObject->getBroadphaseHandle()->m_collisionFilterGroup; + callback.m_collisionFilterMask = mGhostObject->getBroadphaseHandle()->m_collisionFilterMask; + + mGhostObject->convexSweepTest( mColShape, start, end, callback, 0.0f ); + + // Subtract from the travel fraction. + fraction -= callback.m_closestHitFraction; + + // Did we get a hit? + if ( callback.hasHit() ) + { + /* + // Get the real hit normal... Bullet returns the 'seperating normal' and not + // the normal of the hit object. + btTransform rayStart( btTransform::getIdentity() ); + rayStart.setOrigin( callback.m_hitPointWorld + callback.m_hitNormalWorld ); + btTransform rayEnd( btTransform::getIdentity() ); + rayEnd.setOrigin( callback.m_hitPointWorld - callback.m_hitNormalWorld ); + + btCollisionWorld::ClosestRayResultCallback rayHit( rayStart.getOrigin(), rayEnd.getOrigin() ); + mWorld->getDynamicsWorld()->rayTestSingle( rayStart, + rayEnd, + callback.m_hitCollisionObject, + callback.m_hitCollisionObject->getCollisionShape(), + callback.m_hitCollisionObject->getWorldTransform(), + rayHit ); + + if ( !rayHit.hasHit() ) + break; + */ + + Collision& col = outCol->increment(); + dMemset( &col, 0, sizeof( col ) ); + + col.normal = btCast( callback.m_hitNormalWorld ); + col.object = PhysicsUserData::getObject( callback.m_hitCollisionObject->getUserPointer() ); + + // If the collision direction is sideways then modify the collision normal + // to remove any z component. This takes care of any sideways collisions + // with the round bottom of the capsule when it comes to the Player class + // velocity calculations. We want all sideways collisions to be treated + // as if they hit the side of a cylinder. + if (col.normal.z > 0.0f) + { + // This will only remove the z component of the collision normal + // for the bottom of the character controller, which would hit during + // a step. We'll leave the top hemisphere of the character's capsule + // alone as bumping one's head is an entirely different story. This + // helps with low doorways. + col.normal.z = 0.0f; + col.normal.normalizeSafe(); + } + + // Interpolate to the new position. + inOutCurrPos->setInterpolate3( start.getOrigin(), end.getOrigin(), callback.m_closestHitFraction ); + + // Subtract out the displacement along the collision normal. + F32 bd = -disp.dot( callback.m_hitNormalWorld ); + btVector3 dv = callback.m_hitNormalWorld * bd; + disp += dv; + } + else + { + // we moved whole way + *inOutCurrPos = end.getOrigin(); + break; + } + } +} + +void BtPlayer::findContact( SceneObject **contactObject, + VectorF *contactNormal, + Vector *outOverlapObjects ) const +{ + AssertFatal( mGhostObject, "BtPlayer::findContact - The controller is null!" ); + + VectorF normal; + F32 maxDot = -1.0f; + + // Go thru the contact points... get the first contact. + btHashedOverlappingPairCache *pairCache = mGhostObject->getOverlappingPairCache(); + btBroadphasePairArray& pairArray = pairCache->getOverlappingPairArray(); + U32 numPairs = pairArray.size(); + btManifoldArray manifoldArray; + + for ( U32 i=0; i < numPairs; i++ ) + { + const btBroadphasePair &pair = pairArray[i]; + + btBroadphasePair *collisionPair = pairCache->findPair( pair.m_pProxy0, pair.m_pProxy1 ); + if ( !collisionPair || !collisionPair->m_algorithm ) + continue; + + btCollisionObject *other = (btCollisionObject*)pair.m_pProxy0->m_clientObject; + if ( other == mGhostObject ) + other = (btCollisionObject*)pair.m_pProxy1->m_clientObject; + + AssertFatal( !outOverlapObjects->contains( PhysicsUserData::getObject( other->getUserPointer() ) ), + "Got multiple pairs of the same object!" ); + outOverlapObjects->push_back( PhysicsUserData::getObject( other->getUserPointer() ) ); + + if ( other->getCollisionFlags() & btCollisionObject::CF_NO_CONTACT_RESPONSE ) + continue; + + manifoldArray.clear(); + collisionPair->m_algorithm->getAllContactManifolds( manifoldArray ); + + for ( U32 j=0; j < manifoldArray.size(); j++ ) + { + btPersistentManifold *manifold = manifoldArray[j]; + btScalar directionSign = manifold->getBody0() == mGhostObject ? 1.0f : -1.0f; + + for ( U32 p=0; p < manifold->getNumContacts(); p++ ) + { + const btManifoldPoint &pt = manifold->getContactPoint(p); + + // Test the normal... is it the most vertical one we got? + normal = btCast( pt.m_normalWorldOnB * directionSign ); + F32 dot = mDot( normal, VectorF( 0, 0, 1 ) ); + if ( dot > maxDot ) + { + maxDot = dot; + + btCollisionObject *colObject = (btCollisionObject*)collisionPair->m_pProxy0->m_clientObject; + *contactObject = PhysicsUserData::getObject( colObject->getUserPointer() ); + *contactNormal = normal; + } + } + } + } +} + +void BtPlayer::enableCollision() +{ + AssertFatal( mGhostObject, "BtPlayer::enableCollision - The controller is null!" ); + + //mController->setCollision( true ); +} + +void BtPlayer::disableCollision() +{ + AssertFatal( mGhostObject, "BtPlayer::disableCollision - The controller is null!" ); + + //mController->setCollision( false ); +} + +PhysicsWorld* BtPlayer::getWorld() +{ + return mWorld; +} + +void BtPlayer::setTransform( const MatrixF &transform ) +{ + AssertFatal( mGhostObject, "BtPlayer::setTransform - The ghost object is null!" ); + + btTransform xfm = btCast( transform ); + xfm.getOrigin()[2] += mOriginOffset; + + mGhostObject->setWorldTransform( xfm ); +} + +MatrixF& BtPlayer::getTransform( MatrixF *outMatrix ) +{ + AssertFatal( mGhostObject, "BtPlayer::getTransform - The ghost object is null!" ); + + *outMatrix = btCast( mGhostObject->getWorldTransform() ); + *outMatrix[11] -= mOriginOffset; + + return *outMatrix; +} + +void BtPlayer::setScale( const Point3F &scale ) +{ +} diff --git a/Engine/source/T3D/physics/bullet/btPlayer.h b/Engine/source/T3D/physics/bullet/btPlayer.h new file mode 100644 index 000000000..388035431 --- /dev/null +++ b/Engine/source/T3D/physics/bullet/btPlayer.h @@ -0,0 +1,104 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _BTPLAYER_H +#define _BTPLAYER_H + +#ifndef _T3D_PHYSICS_PHYSICSPLAYER_H_ +#include "T3D/physics/physicsPlayer.h" +#endif + + +class BtWorld; +//class btKinematicCharacterController; +class btPairCachingGhostObject; +class btConvexShape; +class btVector3; + + +class BtPlayer : public PhysicsPlayer +{ +protected: + + //F32 mSkinWidth; + + BtWorld *mWorld; + + SceneObject *mObject; + + /// + //btKinematicCharacterController *mController; + + /// + btPairCachingGhostObject *mGhostObject; + + /// + btConvexShape *mColShape; + + /// + F32 mOriginOffset; + + /// + F32 mStepHeight; + /// + void _releaseController(); + + /// + bool _recoverFromPenetration(); + + /// + bool _sweep( btVector3 *inOutCurrPos, const btVector3 &disp, CollisionList *outCol ); + + /// + void _stepForward( btVector3 *inOutCurrPos, const btVector3 &displacement, CollisionList *outCol ); + +public: + + BtPlayer(); + virtual ~BtPlayer(); + + // PhysicsObject + virtual PhysicsWorld* getWorld(); + virtual void setTransform( const MatrixF &transform ); + virtual MatrixF& getTransform( MatrixF *outMatrix ); + virtual void setScale( const Point3F &scale ); + virtual Box3F getWorldBounds() { return Box3F::Invalid; } + virtual void setSimulationEnabled( bool enabled ) {} + virtual bool isSimulationEnabled() { return true; } + + // PhysicsPlayer + virtual void init( const char *type, + const Point3F &size, + F32 runSurfaceCos, + F32 stepHeight, + SceneObject *obj, + PhysicsWorld *world ); + virtual Point3F move( const VectorF &displacement, CollisionList &outCol ); + virtual void findContact( SceneObject **contactObject, VectorF *contactNormal, Vector *outOverlapObjects ) const; + virtual bool testSpacials( const Point3F &nPos, const Point3F &nSize ) const { return true; } + virtual void setSpacials( const Point3F &nPos, const Point3F &nSize ) {} + virtual void enableCollision(); + virtual void disableCollision(); +}; + + +#endif // _BTPLAYER_H diff --git a/Engine/source/T3D/physics/bullet/btPlugin.cpp b/Engine/source/T3D/physics/bullet/btPlugin.cpp new file mode 100644 index 000000000..0a46a1c71 --- /dev/null +++ b/Engine/source/T3D/physics/bullet/btPlugin.cpp @@ -0,0 +1,218 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/bullet/btPlugin.h" + +#include "T3D/physics/physicsShape.h" +#include "T3D/physics/bullet/btWorld.h" +#include "T3D/physics/bullet/btBody.h" +#include "T3D/physics/bullet/btPlayer.h" +#include "T3D/physics/bullet/btCollision.h" +#include "T3D/gameBase/gameProcess.h" +#include "core/util/tNamedFactory.h" + + +AFTER_MODULE_INIT( Sim ) +{ + NamedFactory::add( "Bullet", &BtPlugin::create ); + + #if defined(TORQUE_OS_MAC) + NamedFactory::add( "default", &BtPlugin::create ); + #endif +} + + +PhysicsPlugin* BtPlugin::create() +{ + return new BtPlugin(); +} + +BtPlugin::BtPlugin() +{ +} + +BtPlugin::~BtPlugin() +{ +} + +void BtPlugin::destroyPlugin() +{ + // Cleanup any worlds that are still kicking. + Map::Iterator iter = mPhysicsWorldLookup.begin(); + for ( ; iter != mPhysicsWorldLookup.end(); iter++ ) + { + iter->value->destroyWorld(); + delete iter->value; + } + mPhysicsWorldLookup.clear(); + + delete this; +} + +void BtPlugin::reset() +{ + // First delete all the cleanup objects. + if ( getPhysicsCleanup() ) + getPhysicsCleanup()->deleteAllObjects(); + + getPhysicsResetSignal().trigger( PhysicsResetEvent_Restore ); + + // Now let each world reset itself. + Map::Iterator iter = mPhysicsWorldLookup.begin(); + for ( ; iter != mPhysicsWorldLookup.end(); iter++ ) + iter->value->reset(); +} + +PhysicsCollision* BtPlugin::createCollision() +{ + return new BtCollision(); +} + +PhysicsBody* BtPlugin::createBody() +{ + return new BtBody(); +} + +PhysicsPlayer* BtPlugin::createPlayer() +{ + return new BtPlayer(); +} + +bool BtPlugin::isSimulationEnabled() const +{ + bool ret = false; + BtWorld *world = static_cast( getWorld( smClientWorldName ) ); + if ( world ) + { + ret = world->getEnabled(); + return ret; + } + + world = static_cast( getWorld( smServerWorldName ) ); + if ( world ) + { + ret = world->getEnabled(); + return ret; + } + + return ret; +} + +void BtPlugin::enableSimulation( const String &worldName, bool enable ) +{ + BtWorld *world = static_cast( getWorld( worldName ) ); + if ( world ) + world->setEnabled( enable ); +} + +void BtPlugin::setTimeScale( const F32 timeScale ) +{ + // Grab both the client and + // server worlds and set their time + // scales to the passed value. + BtWorld *world = static_cast( getWorld( smClientWorldName ) ); + if ( world ) + world->setEditorTimeScale( timeScale ); + + world = static_cast( getWorld( smServerWorldName ) ); + if ( world ) + world->setEditorTimeScale( timeScale ); +} + +const F32 BtPlugin::getTimeScale() const +{ + // Grab both the client and + // server worlds and call + // setEnabled( true ) on them. + BtWorld *world = static_cast( getWorld( smClientWorldName ) ); + if ( !world ) + { + world = static_cast( getWorld( smServerWorldName ) ); + if ( !world ) + return 0.0f; + } + + return world->getEditorTimeScale(); +} + +bool BtPlugin::createWorld( const String &worldName ) +{ + Map::Iterator iter = mPhysicsWorldLookup.find( worldName ); + PhysicsWorld *world = NULL; + + iter != mPhysicsWorldLookup.end() ? world = (*iter).value : world = NULL; + + if ( world ) + { + Con::errorf( "BtPlugin::createWorld - %s world already exists!", worldName.c_str() ); + return false; + } + + world = new BtWorld(); + + if ( worldName.equal( smClientWorldName, String::NoCase ) ) + world->initWorld( false, ClientProcessList::get() ); + else + world->initWorld( true, ServerProcessList::get() ); + + mPhysicsWorldLookup.insert( worldName, world ); + + return world != NULL; +} + +void BtPlugin::destroyWorld( const String &worldName ) +{ + Map::Iterator iter = mPhysicsWorldLookup.find( worldName ); + if ( iter == mPhysicsWorldLookup.end() ) + return; + + PhysicsWorld *world = (*iter).value; + world->destroyWorld(); + delete world; + + mPhysicsWorldLookup.erase( iter ); +} + +PhysicsWorld* BtPlugin::getWorld( const String &worldName ) const +{ + if ( mPhysicsWorldLookup.isEmpty() ) + return NULL; + + Map::ConstIterator iter = mPhysicsWorldLookup.find( worldName ); + + return iter != mPhysicsWorldLookup.end() ? (*iter).value : NULL; +} + +PhysicsWorld* BtPlugin::getWorld() const +{ + if ( mPhysicsWorldLookup.size() == 0 ) + return NULL; + + Map::ConstIterator iter = mPhysicsWorldLookup.begin(); + return iter->value; +} + +U32 BtPlugin::getWorldCount() const +{ + return mPhysicsWorldLookup.size(); +} \ No newline at end of file diff --git a/Engine/source/T3D/physics/bullet/btPlugin.h b/Engine/source/T3D/physics/bullet/btPlugin.h new file mode 100644 index 000000000..bc10801fc --- /dev/null +++ b/Engine/source/T3D/physics/bullet/btPlugin.h @@ -0,0 +1,58 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _T3D_PHYSICS_BTPLUGIN_H_ +#define _T3D_PHYSICS_BTPLUGIN_H_ + +#ifndef _T3D_PHYSICS_PHYSICSPLUGIN_H_ +#include "T3D/physics/physicsPlugin.h" +#endif + + +class BtPlugin : public PhysicsPlugin +{ +public: + + BtPlugin(); + ~BtPlugin(); + + /// Create function for factory. + static PhysicsPlugin* create(); + + // PhysicsPlugin + virtual void destroyPlugin(); + virtual void reset(); + virtual PhysicsCollision* createCollision(); + virtual PhysicsBody* createBody(); + virtual PhysicsPlayer* createPlayer(); + virtual bool isSimulationEnabled() const; + virtual void enableSimulation( const String &worldName, bool enable ); + virtual void setTimeScale( const F32 timeScale ); + virtual const F32 getTimeScale() const; + virtual bool createWorld( const String &worldName ); + virtual void destroyWorld( const String &worldName ); + virtual PhysicsWorld* getWorld( const String &worldName ) const; + virtual PhysicsWorld* getWorld() const; + virtual U32 getWorldCount() const; +}; + +#endif // _T3D_PHYSICS_PXPLUGIN_H_ \ No newline at end of file diff --git a/Engine/source/T3D/physics/bullet/btWorld.cpp b/Engine/source/T3D/physics/bullet/btWorld.cpp new file mode 100644 index 000000000..3ca43d793 --- /dev/null +++ b/Engine/source/T3D/physics/bullet/btWorld.cpp @@ -0,0 +1,377 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/bullet/btWorld.h" + +#include "T3D/physics/bullet/btPlugin.h" +#include "T3D/physics/bullet/btCasts.h" +#include "T3D/physics/physicsUserData.h" +#include "core/stream/bitStream.h" +#include "platform/profiler.h" +#include "sim/netConnection.h" +#include "console/console.h" +#include "console/consoleTypes.h" +#include "scene/sceneRenderState.h" +#include "T3D/gameBase/gameProcess.h" + + +BtWorld::BtWorld() : + mProcessList( NULL ), + mIsSimulating( false ), + mErrorReport( false ), + mTickCount( 0 ), + mIsEnabled( false ), + mEditorTimeScale( 1.0f ), + mDynamicsWorld( NULL ), + mThreadSupportCollision( NULL ) +{ +} + +BtWorld::~BtWorld() +{ +} + +bool BtWorld::initWorld( bool isServer, ProcessList *processList ) +{ + // Collision configuration contains default setup for memory, collision setup. + mCollisionConfiguration = new btDefaultCollisionConfiguration(); + + // TODO: There is something wrong with multithreading + // and compound convex shapes... so disable it for now. + static const U32 smMaxThreads = 1; + + // Different initialization with threading enabled. + if ( smMaxThreads > 1 ) + { + + // TODO: ifdef assumes smMaxThread is always one at this point. MACOSX support to be decided +#ifdef WIN32 + mThreadSupportCollision = new Win32ThreadSupport( + Win32ThreadSupport::Win32ThreadConstructionInfo( isServer ? "bt_servercol" : "bt_clientcol", + processCollisionTask, + createCollisionLocalStoreMemory, + smMaxThreads ) ); + + mDispatcher = new SpuGatheringCollisionDispatcher( mThreadSupportCollision, + smMaxThreads, + mCollisionConfiguration ); +#endif // WIN32 + } + else + { + mThreadSupportCollision = NULL; + mDispatcher = new btCollisionDispatcher( mCollisionConfiguration ); + } + + btVector3 worldMin( -2000, -2000, -1000 ); + btVector3 worldMax( 2000, 2000, 1000 ); + btAxisSweep3 *sweepBP = new btAxisSweep3( worldMin, worldMax ); + mBroadphase = sweepBP; + sweepBP->getOverlappingPairCache()->setInternalGhostPairCallback( new btGhostPairCallback() ); + + // The default constraint solver. For parallel processing you can use a different solver (see Extras/BulletMultiThreaded). + mSolver = new btSequentialImpulseConstraintSolver; + + mDynamicsWorld = new btDiscreteDynamicsWorld( mDispatcher, mBroadphase, mSolver, mCollisionConfiguration ); + if ( !mDynamicsWorld ) + { + Con::errorf( "BtWorld - %s failed to create dynamics world!", isServer ? "Server" : "Client" ); + return false; + } + + // Removing the randomization in the solver is required + // to make the simulation deterministic. + mDynamicsWorld->getSolverInfo().m_solverMode &= ~SOLVER_RANDMIZE_ORDER; + + mDynamicsWorld->setGravity( btCast( mGravity ) ); + + AssertFatal( processList, "BtWorld::init() - We need a process list to create the world!" ); + mProcessList = processList; + mProcessList->preTickSignal().notify( this, &BtWorld::getPhysicsResults ); + mProcessList->postTickSignal().notify( this, &BtWorld::tickPhysics, 1000.0f ); + + return true; +} + +void BtWorld::_destroy() +{ + // Release the tick processing signals. + if ( mProcessList ) + { + mProcessList->preTickSignal().remove( this, &BtWorld::getPhysicsResults ); + mProcessList->postTickSignal().remove( this, &BtWorld::tickPhysics ); + mProcessList = NULL; + } + + // TODO: Release any remaining + // orphaned rigid bodies here. + + SAFE_DELETE( mDynamicsWorld ); + SAFE_DELETE( mSolver ); + SAFE_DELETE( mBroadphase ); + SAFE_DELETE( mDispatcher ); + SAFE_DELETE( mThreadSupportCollision ); + SAFE_DELETE( mCollisionConfiguration ); +} + +void BtWorld::tickPhysics( U32 elapsedMs ) +{ + if ( !mDynamicsWorld || !mIsEnabled ) + return; + + // Did we forget to call getPhysicsResults somewhere? + AssertFatal( !mIsSimulating, "PhysXWorld::tickPhysics() - Already simulating!" ); + + // The elapsed time should be non-zero and + // a multiple of TickMs! + AssertFatal( elapsedMs != 0 && + ( elapsedMs % TickMs ) == 0 , "PhysXWorld::tickPhysics() - Got bad elapsed time!" ); + + PROFILE_SCOPE(BtWorld_TickPhysics); + + // Convert it to seconds. + const F32 elapsedSec = (F32)elapsedMs * 0.001f; + + // Simulate... it is recommended to always use Bullet's default fixed timestep/ + mDynamicsWorld->stepSimulation( elapsedSec * mEditorTimeScale ); + + mIsSimulating = true; + + //Con::printf( "%s BtWorld::tickPhysics!", this == smClientWorld ? "Client" : "Server" ); +} + +void BtWorld::getPhysicsResults() +{ + if ( !mDynamicsWorld || !mIsSimulating ) + return; + + PROFILE_SCOPE(BtWorld_GetPhysicsResults); + + // Get results from scene. + // mScene->fetchResults( NX_RIGID_BODY_FINISHED, true ); + mIsSimulating = false; + mTickCount++; +} + +void BtWorld::setEnabled( bool enabled ) +{ + mIsEnabled = enabled; + + if ( !mIsEnabled ) + getPhysicsResults(); +} + +void BtWorld::destroyWorld() +{ + _destroy(); +} + +bool BtWorld::castRay( const Point3F &startPnt, const Point3F &endPnt, RayInfo *ri, const Point3F &impulse ) +{ + btCollisionWorld::ClosestRayResultCallback result( btCast( startPnt ), btCast( endPnt ) ); + mDynamicsWorld->rayTest( btCast( startPnt ), btCast( endPnt ), result ); + + if ( !result.hasHit() || !result.m_collisionObject ) + return false; + + if ( ri ) + { + ri->object = PhysicsUserData::getObject( result.m_collisionObject->getUserPointer() ); + + // If we were passed a RayInfo, we can only return true signifying a collision + // if we hit an object that actually has a torque object associated with it. + // + // In some ways this could be considered an error, either a physx object + // has raycast-collision enabled that shouldn't or someone did not set + // an object in this actor's userData. + // + if ( ri->object == NULL ) + return false; + + ri->distance = ( endPnt - startPnt ).len() * result.m_closestHitFraction; + ri->normal = btCast( result.m_hitNormalWorld ); + ri->point = btCast( result.m_hitPointWorld ); + ri->t = result.m_closestHitFraction; + } + + /* + if ( impulse.isZero() || + !actor.isDynamic() || + actor.readBodyFlag( NX_BF_KINEMATIC ) ) + return true; + + NxVec3 force = pxCast( impulse );//worldRay.dir * forceAmt; + actor.addForceAtPos( force, hitInfo.worldImpact, NX_IMPULSE ); + */ + + return true; +} + +PhysicsBody* BtWorld::castRay( const Point3F &start, const Point3F &end, U32 bodyTypes ) +{ + btVector3 startPt = btCast( start ); + btVector3 endPt = btCast( end ); + + btCollisionWorld::ClosestRayResultCallback result( startPt, endPt ); + mDynamicsWorld->rayTest( startPt, endPt, result ); + + if ( !result.hasHit() || !result.m_collisionObject ) + return NULL; + + PhysicsUserData *userData = PhysicsUserData::cast( result.m_collisionObject->getUserPointer() ); + if ( !userData ) + return NULL; + + return userData->getBody(); +} + +void BtWorld::explosion( const Point3F &pos, F32 radius, F32 forceMagnitude ) +{ + /* + // Find Actors at the position within the radius + // and apply force to them. + + NxVec3 nxPos = pxCast( pos ); + NxShape **shapes = (NxShape**)NxAlloca(10*sizeof(NxShape*)); + NxSphere worldSphere( nxPos, radius ); + + NxU32 numHits = mScene->overlapSphereShapes( worldSphere, NX_ALL_SHAPES, 10, shapes, NULL ); + + for ( NxU32 i = 0; i < numHits; i++ ) + { + NxActor &actor = shapes[i]->getActor(); + + bool dynamic = actor.isDynamic(); + + if ( !dynamic ) + continue; + + bool kinematic = actor.readBodyFlag( NX_BF_KINEMATIC ); + + if ( kinematic ) + continue; + + NxVec3 force = actor.getGlobalPosition() - nxPos; + force.normalize(); + force *= forceMagnitude; + + actor.addForceAtPos( force, nxPos, NX_IMPULSE, true ); + } + */ +} + +void BtWorld::onDebugDraw( const SceneRenderState *state ) +{ + mDebugDraw.setCuller( &state->getFrustum() ); + + mDynamicsWorld->setDebugDrawer( &mDebugDraw ); + mDynamicsWorld->debugDrawWorld(); + mDynamicsWorld->setDebugDrawer( NULL ); + + mDebugDraw.flush(); +} + +void BtWorld::reset() +{ + if ( !mDynamicsWorld ) + return; + + ///create a copy of the array, not a reference! + btCollisionObjectArray copyArray = mDynamicsWorld->getCollisionObjectArray(); + + S32 numObjects = mDynamicsWorld->getNumCollisionObjects(); + for ( S32 i=0; i < numObjects; i++ ) + { + btCollisionObject* colObj = copyArray[i]; + btRigidBody* body = btRigidBody::upcast(colObj); + + if (body) + { + if (body->getMotionState()) + { + //btDefaultMotionState* myMotionState = (btDefaultMotionState*)body->getMotionState(); + //myMotionState->m_graphicsWorldTrans = myMotionState->m_startWorldTrans; + //body->setCenterOfMassTransform( myMotionState->m_graphicsWorldTrans ); + //colObj->setInterpolationWorldTransform( myMotionState->m_startWorldTrans ); + colObj->forceActivationState(ACTIVE_TAG); + colObj->activate(); + colObj->setDeactivationTime(0); + //colObj->setActivationState(WANTS_DEACTIVATION); + } + + //removed cached contact points (this is not necessary if all objects have been removed from the dynamics world) + //m_dynamicsWorld->getBroadphase()->getOverlappingPairCache()->cleanProxyFromPairs(colObj->getBroadphaseHandle(),getDynamicsWorld()->getDispatcher()); + + btRigidBody* body = btRigidBody::upcast(colObj); + if (body && !body->isStaticObject()) + { + btRigidBody::upcast(colObj)->setLinearVelocity(btVector3(0,0,0)); + btRigidBody::upcast(colObj)->setAngularVelocity(btVector3(0,0,0)); + } + } + + } + + // reset some internal cached data in the broadphase + mDynamicsWorld->getBroadphase()->resetPool( mDynamicsWorld->getDispatcher() ); + mDynamicsWorld->getConstraintSolver()->reset(); +} + +/* +ConsoleFunction( castForceRay, const char*, 4, 4, "( Point3F startPnt, Point3F endPnt, VectorF impulseVec )" ) +{ + PhysicsWorld *world = PHYSICSPLUGIN->getWorld( "server" ); + if ( !world ) + return NULL; + + char *returnBuffer = Con::getReturnBuffer(256); + + Point3F impulse; + Point3F startPnt, endPnt; + dSscanf( argv[1], "%f %f %f", &startPnt.x, &startPnt.y, &startPnt.z ); + dSscanf( argv[2], "%f %f %f", &endPnt.x, &endPnt.y, &endPnt.z ); + dSscanf( argv[3], "%f %f %f", &impulse.x, &impulse.y, &impulse.z ); + + Point3F hitPoint; + + RayInfo rinfo; + + bool hit = world->castRay( startPnt, endPnt, &rinfo, impulse ); + + DebugDrawer *ddraw = DebugDrawer::get(); + if ( ddraw ) + { + ddraw->drawLine( startPnt, endPnt, hit ? ColorF::RED : ColorF::GREEN ); + ddraw->setLastTTL( 3000 ); + } + + if ( hit ) + { + dSprintf(returnBuffer, 256, "%g %g %g", + rinfo.point.x, rinfo.point.y, rinfo.point.z ); + return returnBuffer; + } + else + return NULL; +} +*/ \ No newline at end of file diff --git a/Engine/source/T3D/physics/bullet/btWorld.h b/Engine/source/T3D/physics/bullet/btWorld.h new file mode 100644 index 000000000..e859a671a --- /dev/null +++ b/Engine/source/T3D/physics/bullet/btWorld.h @@ -0,0 +1,100 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _BTWORLD_H_ +#define _BTWORLD_H_ + +#ifndef _T3D_PHYSICS_PHYSICSWORLD_H_ +#include "T3D/physics/physicsWorld.h" +#endif +#ifndef _T3D_PHYSICS_BTDEBUGDRAW_H_ +#include "T3D/physics/bullet/btDebugDraw.h" +#endif +#ifndef _BULLET_H_ +#include "T3D/physics/bullet/bt.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +class ProcessList; +class btThreadSupportInterface; +class PhysicsBody; + + +class BtWorld : public PhysicsWorld +{ +protected: + + BtDebugDraw mDebugDraw; + + F32 mEditorTimeScale; + + btDynamicsWorld *mDynamicsWorld; + btBroadphaseInterface *mBroadphase; + btCollisionDispatcher *mDispatcher; + btConstraintSolver *mSolver; + btDefaultCollisionConfiguration *mCollisionConfiguration; + btThreadSupportInterface *mThreadSupportCollision; + + bool mErrorReport; + + bool mIsEnabled; + + bool mIsSimulating; + + U32 mTickCount; + + ProcessList *mProcessList; + + void _destroy(); + +public: + + BtWorld(); + virtual ~BtWorld(); + + // PhysicWorld + virtual bool initWorld( bool isServer, ProcessList *processList ); + virtual void destroyWorld(); + virtual bool castRay( const Point3F &startPnt, const Point3F &endPnt, RayInfo *ri, const Point3F &impulse ); + virtual PhysicsBody* castRay( const Point3F &start, const Point3F &end, U32 bodyTypes ); + virtual void explosion( const Point3F &pos, F32 radius, F32 forceMagnitude ); + virtual void onDebugDraw( const SceneRenderState *state ); + virtual void reset(); + virtual bool isEnabled() const { return mIsEnabled; } + + btDynamicsWorld* getDynamicsWorld() const { return mDynamicsWorld; } + + void tickPhysics( U32 elapsedMs ); + void getPhysicsResults(); + bool isWritable() const { return !mIsSimulating; } + + void setEnabled( bool enabled ); + bool getEnabled() const { return mIsEnabled; } + + void setEditorTimeScale( F32 timeScale ) { mEditorTimeScale = timeScale; } + const F32 getEditorTimeScale() const { return mEditorTimeScale; } + +}; + +#endif // _BTWORLD_H_ diff --git a/Engine/source/T3D/physics/physicsBody.cpp b/Engine/source/T3D/physics/physicsBody.cpp new file mode 100644 index 000000000..7f5605c58 --- /dev/null +++ b/Engine/source/T3D/physics/physicsBody.cpp @@ -0,0 +1,24 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physicsBody.h" diff --git a/Engine/source/T3D/physics/physicsBody.h b/Engine/source/T3D/physics/physicsBody.h new file mode 100644 index 000000000..15e94bcbd --- /dev/null +++ b/Engine/source/T3D/physics/physicsBody.h @@ -0,0 +1,119 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _T3D_PHYSICS_PHYSICSBODY_H_ +#define _T3D_PHYSICS_PHYSICSBODY_H_ + +#ifndef _T3D_PHYSICSCOMMON_H_ +#include "T3D/physics/physicsCommon.h" +#endif +#ifndef _T3D_PHYSICS_PHYSICSOBJECT_H_ +#include "T3D/physics/physicsObject.h" +#endif + +class PhysicsCollision; +class SceneObject; + + +/// Simple physics object that represents a single rigid body. +class PhysicsBody : public PhysicsObject +{ +public: + + virtual ~PhysicsBody() {} + + enum + { + /// Marks the body as a trigger object which is only used + /// to get collision events and not get collision response. + BF_TRIGGER = BIT( 0 ), + + /// The body is kinematic and assumed to be moved by + /// the game code via transforms. + BF_KINEMATIC = BIT( 1 ), + + /// The body responds to contacts but does not push forces into others. + BF_DEBRIS = BIT( 2 ) + }; + + /// Initialize the body with a collision shape + /// and basic physics properties. + virtual bool init( PhysicsCollision *shape, + F32 mass, + U32 bodyFlags, + SceneObject *obj, + PhysicsWorld *world ) = 0; + + /// Returns true if the object is a dynamic rigid body + /// animated by the physics simulation. + /// + /// Kinematics are not considered to be dynamic. + /// + virtual bool isDynamic() const = 0; + + /// Returns the collision shape used to create the body. + virtual PhysicsCollision* getColShape() = 0; + + /// + virtual void setSleepThreshold( F32 linear, F32 angular ) = 0; + + /// + virtual void setDamping( F32 linear, F32 angular ) = 0; + + /// + virtual void getState( PhysicsState *outState ) = 0; + + /// + virtual F32 getMass() const = 0; + + /// + virtual Point3F getCMassPosition() const = 0; + + /// + virtual void setLinVelocity( const Point3F &vel ) = 0; + + /// + virtual void setAngVelocity( const Point3F &vel ) = 0; + + /// + virtual Point3F getLinVelocity() const = 0; + + /// + virtual Point3F getAngVelocity() const = 0; + + /// + virtual void setSleeping( bool sleeping ) = 0; + + /// + virtual void setMaterial( F32 restitution, + F32 friction, + F32 staticFriction ) = 0; + + /// + virtual void applyCorrection( const MatrixF &xfm ) = 0; + + /// + virtual void applyImpulse( const Point3F &origin, const Point3F &force ) = 0; +}; + + +#endif // _T3D_PHYSICS_PHYSICSBODY_H_ \ No newline at end of file diff --git a/Engine/source/T3D/physics/physicsCollision.h b/Engine/source/T3D/physics/physicsCollision.h new file mode 100644 index 000000000..d6e51f843 --- /dev/null +++ b/Engine/source/T3D/physics/physicsCollision.h @@ -0,0 +1,87 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _T3D_PHYSICS_PHYSICSCOLLISION_H_ +#define _T3D_PHYSICS_PHYSICSCOLLISION_H_ + +#ifndef _REFBASE_H_ +#include "core/util/refBase.h" +#endif + +class Point3F; +class MatrixF; +class PlaneF; + + +/// The shared collision representation for a instance of a +/// static or dynamic physics body. +/// +/// Note that making very big convex primitives can cause bad +/// queries and collisions in some physics providers. +/// +/// @see PhysicsBody +/// +class PhysicsCollision : public StrongRefBase +{ +public: + + /// Add an infinite plane to the collision shape. + /// + /// This shape is assumed to be static in some physics + /// providers and will at times be faked with a large box. + /// + virtual void addPlane( const PlaneF &plane ) = 0; + + /// Add a box to the collision shape. + virtual void addBox( const Point3F &halfWidth, + const MatrixF &localXfm ) = 0; + + /// Add a sphere to the collision shape. + virtual void addSphere( F32 radius, + const MatrixF &localXfm ) = 0; + + /// Add a Y axis capsule to the collision shape. + virtual void addCapsule( F32 radius, + F32 height, + const MatrixF &localXfm ) = 0; + + /// Add a point cloud convex hull to the collision shape. + virtual bool addConvex( const Point3F *points, + U32 count, + const MatrixF &localXfm ) = 0; + + /// Add a triangle mesh to the collision shape. + virtual bool addTriangleMesh( const Point3F *vert, + U32 vertCount, + const U32 *index, + U32 triCount, + const MatrixF &localXfm ) = 0; + + /// Add a heightfield to the collision shape. + virtual bool addHeightfield( const U16 *heights, + const bool *holes, + U32 blockSize, + F32 metersPerSample, + const MatrixF &localXfm ) = 0; +}; + +#endif // _T3D_PHYSICS_PHYSICSCOLLISION_H_ \ No newline at end of file diff --git a/Engine/source/T3D/physics/physicsCommon.h b/Engine/source/T3D/physics/physicsCommon.h new file mode 100644 index 000000000..bface037d --- /dev/null +++ b/Engine/source/T3D/physics/physicsCommon.h @@ -0,0 +1,141 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _T3D_PHYSICSCOMMON_H_ +#define _T3D_PHYSICSCOMMON_H_ + +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _MQUAT_H_ +#include "math/mQuat.h" +#endif +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif +#ifndef _REFBASE_H_ +#include "core/util/refBase.h" +#endif + + +/// Helper structure which defines the state of a single physics body. +struct PhysicsState +{ + /// Constructor. + PhysicsState() + : position( Point3F::Zero ), + momentum( Point3F::Zero ), + orientation( QuatF::Identity ), + angularMomentum( Point3F::Zero ), + linVelocity( Point3F::Zero ), + angVelocity( Point3F::Zero ), + sleeping( false ) + { + } + + /// The primary physics state. + // @{ + + /// The position of the body. + Point3F position; + + /// The momentum in kilogram meters per second. + Point3F momentum; + + /// The orientation of the body. + QuatF orientation; + + /// The angular momentum. + Point3F angularMomentum; + + /// Is true if the shape is asleep. + bool sleeping; + + // @} + + /// The secondary physics state derived from the primary state. + /// @{ + + /// The linear velocity derived from the momentum. + Point3F linVelocity; + + /// + Point3F angVelocity; + + /* + Vector velocity; ///< velocity in meters per second (calculated from momentum). + Quaternion spin; ///< quaternion rate of change in orientation. + Vector angularVelocity; ///< angular velocity (calculated from angularMomentum). + Matrix bodyToWorld; ///< body to world coordinates matrix. + Matrix worldToBody; ///< world to body coordinates matrix. + */ + + /// @} + + + /// Interpolates between two physics states leaving the + /// result in this physics state. + inline PhysicsState& interpolate( const PhysicsState &a, const PhysicsState &b, F32 t ) + { + F32 inverseT = 1.0f - t; + position = a.position*inverseT + b.position*t; + momentum = a.momentum*inverseT + b.momentum*t; + orientation.interpolate( a.orientation, b.orientation, t ); + angularMomentum = a.angularMomentum*inverseT + b.angularMomentum*t; + + // Recalculate the velocities + //linVelocity = + //angVelocity + + return *this; + } + + /// Helper builds the transform from the state. + inline MatrixF getTransform() const + { + MatrixF xfm; + orientation.setMatrix( &xfm ); + xfm.setPosition( position ); + return xfm; + } + +}; + + +/// The event type passed to the physics reset signal. +/// @see PhysicsPlugin::getPhysicsResetSignal(). +enum PhysicsResetEvent +{ + PhysicsResetEvent_Store, + PhysicsResetEvent_Restore +}; + +/// The signal for system wide physics events. +/// @see PhysicsPlugin +typedef Signal PhysicsResetSignal; + +class PhysicsCollision; + +/// A strong reference to a physics collision shape. +typedef StrongRefPtr PhysicsCollisionRef; + +#endif // _T3D_PHYSICSCOMMON_H_ \ No newline at end of file diff --git a/Engine/source/T3D/physics/physicsDebris.cpp b/Engine/source/T3D/physics/physicsDebris.cpp new file mode 100644 index 000000000..38549980a --- /dev/null +++ b/Engine/source/T3D/physics/physicsDebris.cpp @@ -0,0 +1,716 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physicsDebris.h" + +#include "core/stream/bitStream.h" +#include "math/mathUtils.h" +#include "console/consoleTypes.h" +#include "console/consoleObject.h" +#include "sim/netConnection.h" +#include "scene/sceneRenderState.h" +#include "scene/sceneManager.h" +#include "ts/tsShapeInstance.h" +#include "T3D/gameBase/gameProcess.h" +#include "core/resourceManager.h" +#include "gfx/gfxTransformSaver.h" +#include "lighting/lightQuery.h" +#include "T3D/physics/physicsBody.h" +#include "T3D/physics/physicsCollision.h" +#include "T3D/physics/physicsWorld.h" +#include "collision/concretePolyList.h" +#include "T3D/physics/physicsPlugin.h" +#include "math/mathUtils.h" +#include "gui/worldEditor/worldEditor.h" +#include "T3D/containerQuery.h" + + +F32 PhysicsDebris::smLifetimeScale = 1.0f; + + +IMPLEMENT_CO_DATABLOCK_V1( PhysicsDebrisData ); + +ConsoleDocClass( PhysicsDebrisData, + + "@brief Defines the properties of a PhysicsDebris object.\n\n" + "@see PhysicsDebris.\n" + "@ingroup Physics" +); + +PhysicsDebrisData::PhysicsDebrisData() +: mass( 1.0f ), + dynamicFriction( 0.0f ), + staticFriction( 0.0f ), + restitution( 0.0f ), + linearDamping( 0.0f ), + angularDamping( 0.0f ), + linearSleepThreshold( 1.0f ), + angularSleepThreshold( 1.0f ), + waterDampingScale( 1.0f ), + buoyancyDensity( 0.0f ), + castShadows( true ) +{ + lifetime = 5.0f; + lifetimeVariance = 0.0f; + shapeName = NULL; +} + +bool PhysicsDebrisData::onAdd() +{ + if(!Parent::onAdd()) + return false; + + return true; +} + +bool PhysicsDebrisData::preload( bool server, String &errorStr ) +{ + if ( Parent::preload( server, errorStr ) == false ) + return false; + + if ( server ) return true; + + if ( shapeName && shapeName[0] != '\0' && !bool(shape) ) + { + shape = ResourceManager::get().load( shapeName ); + if ( bool(shape) == false ) + { + errorStr = String::ToString( "PhysicsDebrisData::load: Couldn't load shape \"%s\"", shapeName ); + return false; + } + else + { + // Create a dummy shape to force the generation of shaders and materials + // during the level load and not during gameplay. + TSShapeInstance *pDummy = new TSShapeInstance( shape, !server ); + delete pDummy; + } + } + + return true; +} + +void PhysicsDebrisData::initPersistFields() +{ + addGroup( "Display" ); + + addField( "shapeFile", TypeShapeFilename, Offset( shapeName, PhysicsDebrisData ), + "@brief Path to the .DAE or .DTS file to use for this shape.\n\n" + "Compatable with Live-Asset Reloading."); + + addField( "castShadows", TypeBool, Offset( castShadows, PhysicsDebrisData ), + "@brief Determines if the shape's shadow should be cast onto the environment.\n\n" ); + + endGroup( "Display" ); + + addGroup( "Physical Properties" ); + + addField("lifetime", TypeF32, Offset( lifetime, PhysicsDebrisData ), + "@brief Base time, in seconds, that debris persists after time of creation.\n\n" + "@note A %PhysicsDebris' lifetime multiplied by it's $pref::PhysicsDebris::lifetimeScale " + "must be equal to or greater than 1.0.\n\n"); + + addField("lifetimeVariance", TypeF32, Offset( lifetimeVariance, PhysicsDebrisData ), + "@brief Range of variation randomly applied to lifetime when debris is created.\n\n" + "Represents the maximum amount of seconds that will be added or subtracted to a shape's base lifetime. " + "A value of 0 will apply the same lifetime to each shape created.\n\n"); + + addField( "mass", TypeF32, Offset( mass, PhysicsDebrisData ), + "@brief Value representing the mass of the shape.\n\n" + "A shape's mass influences the magnitude of any force applied to it. " + "@note All PhysicsDebris objects are dynamic."); + + addField( "friction", TypeF32, Offset( dynamicFriction, PhysicsDebrisData ), + "@brief Coefficient of kinetic %friction to be applied to the shape.\n\n" + "Kinetic %friction reduces the velocity of a moving object while it is in contact with a surface. " + "A larger coefficient will result in a larger reduction in velocity. " + "A shape's friction should be smaller than it's staticFriction, but greater than 0.\n\n" + "@note This value is only applied while an object is in motion. For an object starting at rest, see PhysicsDebrisData::staticFriction"); + + addField( "staticFriction", TypeF32, Offset( staticFriction, PhysicsDebrisData ), + "@brief Coefficient of static %friction to be applied to the shape.\n\n" + "Static %friction determines the force needed to start moving an at-rest object in contact with a surface. " + "If the force applied onto shape cannot overcome the force of static %friction, the shape will remain at rest. " + "A higher coefficient will require a larger force to start motion. " + "This value should be both greater than 0 and the PhysicsDebrisData::friction.\n\n" + "@note This value is only applied while an object is at rest. For an object in motion, see PhysicsDebrisData::friction"); + + addField( "restitution", TypeF32, Offset( restitution, PhysicsDebrisData ), + "@brief Bounce coeffecient applied to the shape in response to a collision.\n\n" + "Restitution is a ratio of a shape's velocity before and after a collision. " + "A value of 0 will zero out a shape's post-collision velocity, making it stop on contact. " + "Larger values will remove less velocity after a collision, making it \'bounce\' with greater force. " + "Normal %restitution values range between 0 and 1.0." + "@note Values near or equaling 1.0 are likely to cause undesirable results in the physics simulation." + " Because of this, it is reccomended to avoid values close to 1.0"); + + addField( "linearDamping", TypeF32, Offset( linearDamping, PhysicsDebrisData ), + "@brief Value that reduces an object's linear velocity over time.\n\n" + "Larger values will cause velocity to decay quicker.\n\n" ); + + addField( "angularDamping", TypeF32, Offset( angularDamping, PhysicsDebrisData ), + "@brief Value that reduces an object's rotational velocity over time.\n\n" + "Larger values will cause velocity to decay quicker.\n\n" ); + + addField( "linearSleepThreshold", TypeF32, Offset( linearSleepThreshold, PhysicsDebrisData ), + "@brief Minimum linear velocity before the shape can be put to sleep.\n\n" + "This should be a positive value. Shapes put to sleep will not be simulated in order to save system resources.\n\n" + "@note The shape must be dynamic."); + + addField( "angularSleepThreshold", TypeF32, Offset( angularSleepThreshold, PhysicsDebrisData ), + "@brief Minimum rotational velocity before the shape can be put to sleep.\n\n" + "This should be a positive value. Shapes put to sleep will not be simulated in order to save system resources.\n\n" + "@note The shape must be dynamic."); + + addField( "waterDampingScale", TypeF32, Offset( waterDampingScale, PhysicsDebrisData ), + "@brief Scale to apply to linear and angular dampening while underwater.\n\n " + "@see angularDamping linearDamping" ); + + addField( "buoyancyDensity", TypeF32, Offset( buoyancyDensity, PhysicsDebrisData ), + "@brief The density of this shape for purposes of calculating buoyant forces.\n\n" + "The result of the calculated buoyancy is relative to the density of the WaterObject the PhysicsDebris is within." + "@see WaterObject::density"); + + endGroup( "Physical Properties" ); + + Parent::initPersistFields(); +} + +void PhysicsDebrisData::packData(BitStream* stream) +{ + Parent::packData(stream); + + stream->writeFlag( castShadows ); + stream->write( lifetime ); + stream->write( lifetimeVariance ); + stream->write( mass ); + stream->write( dynamicFriction ); + stream->write( staticFriction ); + stream->write( restitution ); + stream->write( linearDamping ); + stream->write( angularDamping ); + stream->write( linearSleepThreshold ); + stream->write( angularSleepThreshold ); + stream->write( waterDampingScale ); + stream->write( buoyancyDensity ); + stream->writeString( shapeName ); +} + +void PhysicsDebrisData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + + castShadows = stream->readFlag(); + stream->read( &lifetime ); + stream->read( &lifetimeVariance ); + stream->read( &mass ); + stream->read( &dynamicFriction ); + stream->read( &staticFriction ); + stream->read( &restitution ); + stream->read( &linearDamping ); + stream->read( &angularDamping ); + stream->read( &linearSleepThreshold ); + stream->read( &angularSleepThreshold ); + stream->read( &waterDampingScale ); + stream->read( &buoyancyDensity ); + + shapeName = stream->readSTString(); +} + +ConsoleMethod( PhysicsDebrisData, preload, void, 2, 2, + "@brief Loads some information to have readily available at simulation time.\n\n" + "Forces generation of shaders, materials, and other data used by the %PhysicsDebris object. " + "This function should be used while a level is loading in order to shorten " + "the amount of time to create a PhysicsDebris in game.\n\n") +{ + String errorStr; + + object->shape = NULL; + if( !object->preload( false, errorStr ) ) + Con::errorf( "PhsysicsDebrisData::preload - error: %s", errorStr.c_str() ); +} + + +IMPLEMENT_CO_NETOBJECT_V1( PhysicsDebris ); + +ConsoleDocClass( PhysicsDebris, + + "@brief Represents one or more rigid bodies defined in a single mesh file with " + "a limited lifetime.\n\n" + + "A PhysicsDebris object can be viewed as a single system capable of generating multiple " + "@link PhysicsBody PhysicsBodies@endlink as debris when triggered. Vaguely similar to how " + "a ParticleEmitter is capable of creating Particles, but isn't a particle in itself. " + + "After it's lifetime has elapsed, the object will be deleted.\n\n" + + "%PhysicsDebris loads a standard .DAE or .DTS file and creates a rigid body for " + "each defined collision node.\n\n" + + "For collision nodes to work correctly, they must be setup as follows:\n" + " - Visible mesh nodes are siblings of the collision node under a common parent dummy node.\n" + " - Collision node is a child of its visible mesh node.\n\n" + + "Colmesh type nodes are NOT supported; physx and most standard rigid " + "body simulations do not support arbitrary triangle meshs for dynamics " + "do to the computational expense.\n\n" + "Therefore, collision nodes must be one of the following:\n" + " - Colbox\n" + " - Colsphere\n" + " - Colcapsule\n" + " - Col (convex).\n\n" + + "%PhysicsDebris should NOT be created on the server.\n\n" + + "@ingroup Physics" +); + +PhysicsDebris* PhysicsDebris::create( PhysicsDebrisData *datablock, + const MatrixF &transform, + const VectorF &linVel ) +{ + // Skip out if we don't have a datablock or the + // global lifetime scale has it living less than + // a second. + if ( !datablock || + ( datablock->lifetime > 0.0f && + datablock->lifetime * smLifetimeScale < 1.0f ) ) + return NULL; + + PhysicsDebris *debris = new PhysicsDebris(); + debris->setDataBlock( datablock ); + debris->setTransform( transform ); + debris->mInitialLinVel = linVel; + if ( !debris->registerObject() ) + { + delete debris; + return NULL; + } + + return debris; +} + +PhysicsDebris::PhysicsDebris() + : mLifetime( 0.0f ), + mShapeInstance( NULL ), + mWorld( NULL ), + mInitialLinVel( Point3F::Zero ) +{ + mTypeMask |= DebrisObjectType | DynamicShapeObjectType; + + // Only allocated client side. + mNetFlags.set( IsGhost ); +} + +PhysicsDebris::~PhysicsDebris() +{ +} + +void PhysicsDebris::initPersistFields() +{ + Con::addVariable( "$pref::PhysicsDebris::lifetimeScale", TypeF32, &smLifetimeScale, + "@brief Scales how long %PhysicsDebris will live before being removed.\n" + "@note A value of 0 will disable PhysicsDebris entirely."); + + Parent::initPersistFields(); +} + +bool PhysicsDebris::onAdd() +{ + AssertFatal( isClientObject(), "PhysicsDebris::onAdd - This shouldn't be added on the server!" ); + + if ( !Parent::onAdd() ) + return false; + + // If it has a fixed lifetime then calculate it. + if ( mDataBlock->lifetime > 0.0f ) + { + F32 lifeVar = (mDataBlock->lifetimeVariance * 2.0f * gRandGen.randF(-1.0,1.0)) - mDataBlock->lifetimeVariance; + mLifetime = mDataBlock->lifetime + lifeVar; + } + + // Setup our bounding box + mObjBox = mDataBlock->shape->bounds; + resetWorldBox(); + + // Add it to the client scene. + addToScene(); + + // We add the debris to the net connection so that + // it is cleaned up when the client disconnects. + NetConnection *conn = NetConnection::getConnectionToServer(); + AssertFatal( conn != NULL, "PhysicsDebris::onAdd - Got null net connection!"); + conn->addObject(this); + + PhysicsPlugin::getPhysicsResetSignal().notify( this, &PhysicsDebris::_onPhysicsReset ); + _createFragments(); + + return true; +} + +bool PhysicsDebris::onNewDataBlock( GameBaseData *dptr, bool reload ) +{ + if ( !dptr ) + return false; + + mDataBlock = dynamic_cast< PhysicsDebrisData* >( dptr ); + if ( !mDataBlock ) + { + Con::errorf( ConsoleLogEntry::General, "PhysicsDebris::onNewDataBlock - datablock ( %i ) is not of type PhysicsDebrisData.", dptr->getId() ); + return false; + } + + return true; +} + +void PhysicsDebris::onRemove() +{ + PhysicsPlugin::getPhysicsResetSignal().remove( this, &PhysicsDebris::_onPhysicsReset ); + + _deleteFragments(); + + removeFromScene(); + + Parent::onRemove(); +} + +void PhysicsDebris::processTick( const Move* ) +{ + PROFILE_SCOPE( PhysicsDebris_processTick ); + + // Delete the debris if our lifetime has expired. + if ( mDataBlock->lifetime > 0.0f && + mIsZero( mLifetime ) ) + { + deleteObject(); + return; + } + + MatrixF mat; + mWorldBox = Box3F::Invalid; + Box3F bounds; + + FragmentVector::iterator fragment = mFragments.begin(); + for ( ; fragment != mFragments.end(); fragment++ ) + { + // Store the last position. + fragment->lastPos = fragment->pos; + fragment->lastRot = fragment->rot; + + // Get the new position. + fragment->body->getTransform( &mat ); + + // Calculate the delta between the current + // global pose and the last global pose. + fragment->pos = mat.getPosition(); + fragment->rot.set( mat ); + + // Update the bounds. + bounds = fragment->body->getWorldBounds(); + mWorldBox.intersect( bounds ); + + // Apply forces for the next tick. + _updateForces( fragment->body, bounds ); + } + + // Finish up the bounds update. + mWorldSphere.radius = (mWorldBox.maxExtents - mWorldSphere.center).len(); + mObjBox = mWorldBox; + mWorldToObj.mul(mObjBox); + mRenderWorldBox = mWorldBox; + mRenderWorldSphere = mWorldSphere; +} + +void PhysicsDebris::_updateForces( PhysicsBody *body, const Box3F &bounds ) +{ + PROFILE_SCOPE( PhysicsDebris_updateForces ); + + // If we're not simulating don't update forces. + if ( !mWorld->isEnabled() ) + return; + + ContainerQueryInfo info; + info.box = bounds; + info.mass = mDataBlock->mass; + + // Find and retreive physics info from intersecting WaterObject(s) + getContainer()->findObjects( bounds, WaterObjectType|PhysicalZoneObjectType, findRouter, &info ); + + // Calculate buoyancy and drag + F32 angDrag = mDataBlock->angularDamping; + F32 linDrag = mDataBlock->linearDamping; + F32 buoyancy = 0.0f; + Point3F cmass = body->getCMassPosition(); + + F32 density = mDataBlock->buoyancyDensity; + if ( density > 0.0f ) + { + if ( info.waterCoverage > 0.0f ) + { + F32 waterDragScale = info.waterViscosity * mDataBlock->waterDampingScale; + F32 powCoverage = mPow( info.waterCoverage, 0.25f ); + + angDrag = mLerp( angDrag, angDrag * waterDragScale, powCoverage ); + linDrag = mLerp( linDrag, linDrag * waterDragScale, powCoverage ); + } + + // A little hackery to prevent oscillation + // Based on this blog post: + // (http://reinot.blogspot.com/2005/11/oh-yes-they-float-georgie-they-all.html) + + buoyancy = ( info.waterDensity / density ) * mPow( info.waterCoverage, 2.0f ); + + Point3F buoyancyForce = buoyancy * -mWorld->getGravity() * TickSec * mDataBlock->mass; + body->applyImpulse( cmass, buoyancyForce ); + } + + // Update the dampening as the container might have changed. + body->setDamping( linDrag, angDrag ); + + // Apply physical zone forces. + if ( !info.appliedForce.isZero() ) + body->applyImpulse( cmass, info.appliedForce ); +} + +void PhysicsDebris::advanceTime( F32 dt ) +{ + // Decrement the lifetime. + if ( smLifetimeScale > 0.0f ) + mLifetime = getMax( 0.0f, mLifetime - ( dt / smLifetimeScale ) ); + else + mLifetime = 0.0f; +} + +void PhysicsDebris::interpolateTick( F32 dt ) +{ + PROFILE_SCOPE( PhysicsDebris_interpolateTick ); + + mShapeInstance->animate(); + if ( mShapeInstance->getCurrentDetail() < 0 ) + return; + + const MatrixF &objectXfm = getRenderWorldTransform(); + Vector &nodeXfms = mShapeInstance->mNodeTransforms; + + MatrixF globalXfm; + MatrixF tempXfm; + QuatF newRot; + Point3F newPos; + + FragmentVector::iterator fragment = mFragments.begin(); + for ( ; fragment != mFragments.end(); fragment++ ) + { + // Do the interpolation. + newRot.interpolate( fragment->rot, fragment->lastRot, dt ); + newRot.setMatrix( &globalXfm ); + newPos.interpolate( fragment->pos, fragment->lastPos, dt ); + globalXfm.setPosition( newPos ); + + tempXfm = objectXfm * globalXfm; + + for ( S32 i = 0; i < fragment->nodeIds.size(); i++ ) + { + S32 n = fragment->nodeIds[i]; + nodeXfms[n] = tempXfm; + } + } +} + +void PhysicsDebris::prepRenderImage( SceneRenderState *state ) +{ + if( !mShapeInstance ) + return; + + // Skip shadow rendering if this debris doesn't support it. + if ( state->isShadowPass() && + !mDataBlock->castShadows ) + return; + + // If the debris is completed LOD'd out then skip it. + if ( mShapeInstance->setDetailFromPosAndScale( state, getRenderPosition(), getScale() ) < 0 ) + return; + + // Fade out the debris over the last second of its lifetime. + F32 alpha = 1.0; + if ( mDataBlock->lifetime > 0.0f ) + alpha = getMin( mLifetime * smLifetimeScale, 1.0f ); + + // Set up our TS render state. + TSRenderState rdata; + rdata.setSceneState( state ); + rdata.setFadeOverride( alpha ); + + // We might have some forward lit materials + // so pass down a query to gather lights. + LightQuery query; + query.init( getWorldSphere() ); + rdata.setLightQuery( &query ); + + GFXTransformSaver saver; + + MatrixF mat = getRenderTransform(); + mat.scale( getScale() ); + GFX->setWorldMatrix( mat ); + + mShapeInstance->animate(); + mShapeInstance->render( rdata ); +} + +void PhysicsDebris::applyImpulse( const Point3F &pos, const VectorF &vec ) +{ + FragmentVector::iterator fragment = mFragments.begin(); + for ( ; fragment != mFragments.end(); fragment++ ) + fragment->body->applyImpulse( pos, vec ); +} + +void PhysicsDebris::applyRadialImpulse( const Point3F &origin, F32 radius, F32 magnitude ) +{ + FragmentVector::iterator fragment = mFragments.begin(); + for ( ; fragment != mFragments.end(); fragment++ ) + { + PhysicsBody &body = *fragment->body; + + Box3F bounds = body.getWorldBounds(); + + VectorF force = bounds.getCenter() - origin; + F32 dist = force.magnitudeSafe(); + force.normalize(); + + if ( dist == 0.0f ) + force *= magnitude; + else + force *= mClampF( radius / dist, 0.0f, 1.0f ) * magnitude; + + body.applyImpulse( origin, force ); + } +} + +void PhysicsDebris::_createFragments() +{ + _deleteFragments(); + + mWorld = PHYSICSMGR->getWorld( "client" ); + if ( !mWorld ) + return; + + TSShape *shape = mDataBlock->shape; + + mShapeInstance = new TSShapeInstance( shape, true ); + mShapeInstance->animate(); + + Vector< CollisionShapeInfo > infoList; + shape->buildColShapes( false, Point3F::One, &infoList ); + + mFragments.setSize( infoList.size() ); + dMemset( mFragments.address(), 0, mFragments.memSize() ); + + const Point3F damageDir( 0, 0, 1 ); + + MatrixF bodyMat( true ); + bodyMat = getTransform(); + + const U32 bodyFlags = PhysicsBody::BF_DEBRIS; + mWorldBox = Box3F::Invalid; + + for ( S32 i = 0; i < infoList.size(); i++ ) + { + const CollisionShapeInfo &info = infoList[i]; + + Fragment &fragment = mFragments[i]; + + if ( info.colNode == -1 ) + Con::errorf( "PhysicsDebris::_createFragments, Missing or couldnt find a colNode." ); + else + _findNodes( info.colNode, fragment.nodeIds ); + + PhysicsBody *body = PHYSICSMGR->createBody(); + body->init( info.colShape, mDataBlock->mass, bodyFlags, this, mWorld ); + body->setMaterial( mDataBlock->restitution, mDataBlock->dynamicFriction, mDataBlock->staticFriction ); + body->setDamping( mDataBlock->linearDamping, mDataBlock->angularDamping ); + body->setSleepThreshold( mDataBlock->linearSleepThreshold, mDataBlock->angularSleepThreshold ); + body->setTransform( bodyMat ); + body->setLinVelocity( mInitialLinVel ); + fragment.body = body; + + // Set the initial delta state. + fragment.pos = bodyMat.getPosition(); + fragment.rot.set( bodyMat ); + fragment.lastPos = fragment.pos; + fragment.lastRot = fragment.rot; + + // Update the bounds. + mWorldBox.intersect( body->getWorldBounds() ); + } + + // Finish up updating the bounds. + mWorldSphere.radius = (mWorldBox.maxExtents - mWorldSphere.center).len(); + mObjBox = mWorldBox; + mWorldToObj.mul(mObjBox); + mRenderWorldBox = mWorldBox; + mRenderWorldSphere = mWorldSphere; +} + +void PhysicsDebris::_deleteFragments() +{ + FragmentVector::iterator fragment = mFragments.begin(); + for ( ; fragment != mFragments.end(); fragment++ ) + delete fragment->body; + + mFragments.clear(); + + SAFE_DELETE( mShapeInstance ); +} + +void PhysicsDebris::_findNodes( U32 colNode, Vector &nodeIds ) +{ + // Two possible cases: + // 1. Visible mesh nodes are siblings of the collision node under a common parent dummy node + // 2. Collision node is a child of its visible mesh node + + TSShape *shape = mDataBlock->shape; + S32 itr = shape->nodes[colNode].parentIndex; + itr = shape->nodes[itr].firstChild; + + while ( itr != -1 ) + { + if ( itr != colNode ) + nodeIds.push_back(itr); + itr = shape->nodes[itr].nextSibling; + } + + // If we didn't find any siblings of the collision node we assume + // it is case #2 and the collision nodes direct parent is the visible mesh. + if ( nodeIds.size() == 0 && shape->nodes[colNode].parentIndex != -1 ) + nodeIds.push_back( shape->nodes[colNode].parentIndex ); +} + +extern bool gEditingMission; + +void PhysicsDebris::_onPhysicsReset( PhysicsResetEvent reset ) +{ + if ( gEditingMission ) + { + // Editing stuff, clean up the trash! + safeDeleteObject(); + } +} \ No newline at end of file diff --git a/Engine/source/T3D/physics/physicsDebris.h b/Engine/source/T3D/physics/physicsDebris.h new file mode 100644 index 000000000..94c183e27 --- /dev/null +++ b/Engine/source/T3D/physics/physicsDebris.h @@ -0,0 +1,190 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PHYSICS_DEBRIS_H_ +#define _PHYSICS_DEBRIS_H_ + +#ifndef __RESOURCE_H__ +#include "core/resource.h" +#endif +#ifndef _GAMEBASE_H_ +#include "T3D/gameBase/gameBase.h" +#endif +#ifndef _T3D_PHYSICSCOMMON_H_ +#include "T3D/physics/physicsCommon.h" +#endif + + +class TSShapeInstance; +class TSShape; + +//************************************************************************** +// Debris Data +//************************************************************************** +class PhysicsDebrisData : public GameBaseData +{ + typedef GameBaseData Parent; + +public: + + + F32 lifetime; + F32 lifetimeVariance; + + /// + F32 mass; + + /// + F32 dynamicFriction; + + /// + F32 staticFriction; + + /// + F32 restitution; + + /// + F32 linearDamping; + + /// + F32 angularDamping; + + /// + F32 linearSleepThreshold; + + /// + F32 angularSleepThreshold; + + // A scale applied to the normal linear and angular damping + // when the object enters a water volume. + F32 waterDampingScale; + + // The density of this object used for water buoyancy effects. + F32 buoyancyDensity; + + /// Is rendererd during shadow passes. + bool castShadows; + + const char* shapeName; + Resource shape; + + PhysicsDebrisData(); + + bool onAdd(); + bool preload( bool server, String &errorStr ); + static void initPersistFields(); + void packData( BitStream *stream ); + void unpackData( BitStream *stream ); + + DECLARE_CONOBJECT( PhysicsDebrisData ); + +}; + + + +class PhysicsBody; +class PhysicsWorld; + + +class PhysicsDebris : public GameBase +{ + typedef GameBase Parent; + +public: + + /// Helper method which creates debris based on the + /// datablock and initial state. + /// + /// It can return NULL if the system quality settings + /// are set to disable the debris. + /// + static PhysicsDebris* create( PhysicsDebrisData *datablock, + const MatrixF &transform, + const VectorF &linVel ); + + PhysicsDebris(); + virtual ~PhysicsDebris(); + + DECLARE_CONOBJECT(PhysicsDebris); + static void initPersistFields(); + + bool onAdd(); + void onRemove(); + + void applyImpulse( const Point3F &pos, const VectorF &vec ); + void applyRadialImpulse( const Point3F &origin, F32 radius, F32 magnitude ); + +protected: + + /// The global object lifetime scalar. + static F32 smLifetimeScale; + + void processTick( const Move *move ); + void advanceTime( F32 dt ); + void interpolateTick( F32 delta ); + bool onNewDataBlock( GameBaseData *dptr, bool reload ); + + void prepRenderImage( SceneRenderState *state ); + void prepBatchRender( SceneRenderState *state ); + + void _deleteFragments(); + void _createFragments(); + + void _updateForces( PhysicsBody *body, const Box3F &bounds ); + + void _findNodes( U32 objId, Vector &nodeIds ); + + void _onPhysicsReset( PhysicsResetEvent reset ); + +protected: + + F32 mLifetime; + + Point3F mInitialLinVel; + + PhysicsDebrisData *mDataBlock; + + TSShapeInstance *mShapeInstance; + + PhysicsWorld *mWorld; + + struct Fragment + { + Vector nodeIds; + + PhysicsBody *body; + + // The delta state. + Point3F pos; + Point3F lastPos; + QuatF rot; + QuatF lastRot; + }; + + typedef Vector FragmentVector; + + FragmentVector mFragments; + +}; + + +#endif // _PHYSICS_DEBRIS_H_ diff --git a/Engine/source/T3D/physics/physicsEvents.cpp b/Engine/source/T3D/physics/physicsEvents.cpp new file mode 100644 index 000000000..52eeaf4f9 --- /dev/null +++ b/Engine/source/T3D/physics/physicsEvents.cpp @@ -0,0 +1,148 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physicsEvents.h" + +#include "math/mathIO.h" +#include "core/stream/bitStream.h" +#include "T3D/physics/physicsPlugin.h" +#include "T3D/physics/physicsWorld.h" +#include "scene/sceneObject.h" +#include "T3D/gameBase/gameConnection.h" +#include "console/engineAPI.h" + + +RadialImpulseEvent::RadialImpulseEvent() + : mPosition( 0, 0, 0 ), + mRadius( 0 ), + mMagnitude( 0 ) +{ +} + +RadialImpulseEvent::RadialImpulseEvent( const Point3F &pos, F32 radius, F32 magnitude ) + : mPosition( pos ), + mRadius( radius ), + mMagnitude( magnitude ) +{ +} + +RadialImpulseEvent::~RadialImpulseEvent() +{ +} + +void RadialImpulseEvent::pack( NetConnection* /*ps*/, BitStream *bstream ) +{ + mathWrite( *bstream, mPosition ); + bstream->write( mRadius ); + bstream->write( mMagnitude ); +} + +void RadialImpulseEvent::write( NetConnection*, BitStream *bstream ) +{ + mathWrite( *bstream, mPosition ); + bstream->write( mRadius ); + bstream->write( mMagnitude ); +} + +void RadialImpulseEvent::unpack( NetConnection *ps, BitStream *bstream ) +{ + mathRead( *bstream, &mPosition ); + bstream->read( &mRadius ); + bstream->read( &mMagnitude ); +} + +void RadialImpulseEvent::process(NetConnection *con) +{ + impulse( &gClientContainer, mPosition, mRadius, mMagnitude ); +} + +void RadialImpulseEvent::_impulseCallback( SceneObject *obj, void *key ) +{ + ImpulseInfo *info = (ImpulseInfo*)key; + obj->applyRadialImpulse( info->pos, info->radius, info->magnitude ); +} + +void RadialImpulseEvent::impulse( SceneContainer *con, const Point3F &position, F32 radius, F32 magnitude ) +{ + Point3F offset( radius, radius, radius ); + Box3F bounds( position - offset, position + offset ); + + ImpulseInfo info; + info.pos = position; + info.radius = radius; + info.magnitude = magnitude; + + con->findObjects( bounds, -1, _impulseCallback, &info ); +} + +IMPLEMENT_CO_NETEVENT_V1( RadialImpulseEvent ); + +ConsoleDocClass( RadialImpulseEvent, + "@brief Creates a physics-based impulse effect from a defined central point and magnitude.\n\n" + "@see RadialImpulseEvent::send\n" + "@ingroup Physics\n" +); + + +DefineEngineStaticMethod(RadialImpulseEvent, send, void, (const char* inPosition, F32 radius, F32 magnitude), ("1.0 1.0 1.0", 10.0f, 20.0f), + "@brief Applies a radial impulse to any SceneObjects within the area of effect.\n\n" + "This event is performed both server and client-side.\n\n" + "@param position Center point for this radial impulse.\n" + "@param radius Distance from the position for this radial impulse to affect.\n" + "@param magnitude The force applied to objects within the radius from the position of this radial impulse effect.\n\n" + "@tsexample\n" + "// Define the Position\n" + "%position = \"10.0 15.0 10.0\";\n\n" + "// Define the Radius\n" + "%radius = \"25.0\";\n\n" + "// Define the Magnitude\n" + "%magnitude = \"30.0\"\n\n" + "// Create a globalRadialImpulse physics effect.\n" + "RadialImpulseEvent::send(%position,%radius,%magnitude);\n" + "@endtsexample\n\n") +{ + // Scan out arguments... + Point3F position; + + dSscanf( inPosition, "%f %f %f", &position.x, &position.y, &position.z ); + + // Apply server-side. + RadialImpulseEvent::impulse( &gServerContainer, position, radius, magnitude ); + + // Transmit event to each client to perform client-side... + + SimGroup *pClientGroup = Sim::getClientGroup(); + if ( !pClientGroup ) + { + Con::errorf( "globalRadialImpulse() - Client group not found!" ); + return; + } + + SimGroup::iterator itr = pClientGroup->begin(); + for ( ; itr != pClientGroup->end(); itr++ ) + { + GameConnection* gc = static_cast(*itr); + if ( gc ) + gc->postNetEvent( new RadialImpulseEvent( position, radius, magnitude ) ); + } +} diff --git a/Engine/source/T3D/physics/physicsEvents.h b/Engine/source/T3D/physics/physicsEvents.h new file mode 100644 index 000000000..3f4ac2d14 --- /dev/null +++ b/Engine/source/T3D/physics/physicsEvents.h @@ -0,0 +1,73 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PHYSICSEVENTS_H_ +#define _PHYSICSEVENTS_H_ + +#ifndef _NETCONNECTION_H_ +#include "sim/netConnection.h" +#endif + +class SceneObject; +class SceneContainer; + + +/// When this NetEvent is processed on the client-side it +/// applies a radial impulse to objects in the physics +/// simulation. +class RadialImpulseEvent : public NetEvent +{ + typedef NetEvent Parent; + +protected: + + struct ImpulseInfo + { + Point3F pos; + F32 radius; + F32 magnitude; + }; + + Point3F mPosition; + F32 mRadius; + F32 mMagnitude; + + static void _impulseCallback( SceneObject *obj, void *key ); + +public: + + RadialImpulseEvent(); + RadialImpulseEvent( const Point3F &pos, F32 radius, F32 magnitude ); + ~RadialImpulseEvent(); + + virtual void pack( NetConnection* /*ps*/, BitStream *bstream ); + virtual void write( NetConnection*, BitStream *bstream ); + virtual void unpack( NetConnection *ps, BitStream *bstream ); + virtual void process(NetConnection *); + + static void impulse( SceneContainer *con, const Point3F &position, F32 radius, F32 magnitude ); + + DECLARE_CONOBJECT( RadialImpulseEvent ); +}; + + +#endif // _PHYSICSEVENTS_H_ \ No newline at end of file diff --git a/Engine/source/T3D/physics/physicsForce.cpp b/Engine/source/T3D/physics/physicsForce.cpp new file mode 100644 index 000000000..720b709bc --- /dev/null +++ b/Engine/source/T3D/physics/physicsForce.cpp @@ -0,0 +1,220 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physicsForce.h" + +#include "T3D/physics/physicsPlugin.h" +#include "T3D/physics/physicsWorld.h" +#include "T3D/physics/physicsBody.h" +#include "console/engineAPI.h" + + +IMPLEMENT_CONOBJECT(PhysicsForce); + + +ConsoleDocClass( PhysicsForce, + "@brief Helper object for gameplay physical forces.\n\n" + "%PhysicsForces can be created and \"attached\" to other @link PhysicsBody PhysicsBodies@endlink " + "to attract them to the position of the PhysicsForce." + "@ingroup Physics\n" +); + + +PhysicsForce::PhysicsForce() + : mWorld( NULL ), + mBody( NULL ), + mPhysicsTick( false ) +{ +} + +PhysicsForce::~PhysicsForce() +{ +} + +void PhysicsForce::initPersistFields() +{ + Parent::initPersistFields(); +} + + +bool PhysicsForce::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + // Find the physics world we're in. + if ( PHYSICSMGR ) + mWorld = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" ); + + setProcessTick( true ); + getProcessList()->preTickSignal().notify( this, &PhysicsForce::_preTick ); + + return true; +} + +void PhysicsForce::onRemove() +{ + mWorld = NULL; + mBody = NULL; + getProcessList()->preTickSignal().remove( this, &PhysicsForce::_preTick ); + setProcessTick( false ); + + Parent::onRemove(); +} + +void PhysicsForce::attach( const Point3F &start, const Point3F &direction, F32 maxDist ) +{ + detach(); + + // If there is no physics world then we cannot apply any forces. + if ( !mWorld ) + return; + + PhysicsBody *body = mWorld->castRay( start, start + ( direction * maxDist ), PhysicsWorld::BT_Dynamic ); + if ( !body ) + return; + + mBody = body; +} + +void PhysicsForce::detach( const Point3F &force ) +{ + if ( mBody && !force.isZero() ) + { + Point3F cMass = mBody->getCMassPosition(); + F32 mass = mBody->getMass(); + + Point3F impulse = ( mass * force ) / TickSec; + mBody->applyImpulse( cMass, impulse ); + } + + mBody = NULL; +} + +void PhysicsForce::onMount( SceneObject *obj, S32 node ) +{ + Parent::onMount( obj, node ); + + processAfter( obj ); + + MatrixF mat( true ); + mMount.object->getMountTransform( mMount.node, mMount.xfm, &mat ); + setTransform( mat ); +} + +void PhysicsForce::onUnmount( SceneObject *obj, S32 node ) +{ + clearProcessAfter(); + Parent::onUnmount( obj, node ); +} + +void PhysicsForce::_preTick() +{ + // How Torque works we sometimes get double or more + // ticks within a single simulation step. This occurs + // for various reasons including odd timesteps and low + // framerate. + // + // In order to keep performance from plumeting we do + // not tick physics multiple times... instead it just + // looses time. + // + // We set this variable below to let processTick know + // that physics hasn't stepped yet and to skip processing. + // + // This doesn't completely solve the issue, but it does + // make things much better. + // + mPhysicsTick = true; +} + +void PhysicsForce::processTick( const Move * ) +{ + if ( isMounted() ) + { + MatrixF test( true ); + test.setPosition( Point3F( 0, 4, 0 ) ); + AssertFatal( test != mMount.xfm, "Error!" ); + + MatrixF mat( true ); + mMount.object->getMountTransform( mMount.node, mMount.xfm, &mat ); + setTransform( mat ); + } + + // Nothing to do without a body or if physics hasn't ticked. + if ( !mBody || !mPhysicsTick ) + return; + + mPhysicsTick = false; + + // If we lost the body then release it. + if ( !mBody->isDynamic() || !mBody->isSimulationEnabled() ) + { + detach(); + return; + } + + // Get our distance to the body. + Point3F cMass = mBody->getCMassPosition(); + Point3F vector = getPosition() - cMass; + + // Apply the force! + F32 mass = mBody->getMass(); + Point3F impulse = ( mass * vector ) / TickSec; + + // Counter balance the linear impulse. + Point3F linVel = mBody->getLinVelocity(); + Point3F currentForce = linVel * mass; + + // Apply it. + mBody->applyImpulse( cMass, impulse - currentForce ); +} + +DefineEngineMethod( PhysicsForce, attach, void, ( Point3F start, Point3F direction, F32 maxDist ),, + "@brief Attempts to associate the PhysicsForce with a PhysicsBody.\n\n" + "Performs a physics ray cast of the provided length and direction. The %PhysicsForce " + "will attach itself to the first dynamic PhysicsBody the ray collides with. " + "On every tick, the attached body will be attracted towards the position of the %PhysicsForce.\n\n" + "A %PhysicsForce can only be attached to one body at a time.\n\n" + "@note To determine if an %attach was successful, check isAttached() immediately after " + "calling this function.n\n") +{ + object->attach( start, direction, maxDist ); +} + +DefineEngineMethod( PhysicsForce, detach, void, ( Point3F force ), ( Point3F::Zero ), + "@brief Disassociates the PhysicsForce from any attached PhysicsBody.\n\n" + "@param force Optional force to apply to the attached PhysicsBody " + "before detaching.\n\n" + "@note Has no effect if the %PhysicsForce is not attached to anything.\n\n") +{ + object->detach( force ); +} + +DefineEngineMethod( PhysicsForce, isAttached, bool, (),, + "@brief Returns true if the %PhysicsForce is currently attached to an object.\n\n" + "@see PhysicsForce::attach()") +{ + return object->isAttached(); +} + diff --git a/Engine/source/T3D/physics/physicsForce.h b/Engine/source/T3D/physics/physicsForce.h new file mode 100644 index 000000000..15257fd06 --- /dev/null +++ b/Engine/source/T3D/physics/physicsForce.h @@ -0,0 +1,91 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _T3D_PHYSICS_PHYSICSFORCE_H_ +#define _T3D_PHYSICS_PHYSICSFORCE_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _T3D_PHYSICSCOMMON_H_ +#include "T3D/physics/physicsCommon.h" +#endif +#ifndef _T3D_PHYSICS_PHYSICSOBJECT_H_ +#include "T3D/physics/physicsObject.h" +#endif + +class PhysicsBody; +class PhysicsWorld; + + +/// A physics force controller used for gameplay effects. +class PhysicsForce : public SceneObject +{ + typedef SceneObject Parent; + +public: + + PhysicsForce(); + virtual ~PhysicsForce(); + + DECLARE_CONOBJECT( PhysicsForce ); + + // SimObject + static void initPersistFields(); + bool onAdd(); + void onRemove(); + + // SceneObject + void onMount( SceneObject *obj, S32 node ); + void onUnmount( SceneObject *obj, S32 node ); + + // ProcessObject + void processTick( const Move *move ); + + /// + void attach( const Point3F &start, const Point3F &direction, F32 maxDist ); + + /// + void detach( const Point3F &force = Point3F::Zero ); + + /// + bool isAttached() const { return mBody != NULL; } + +protected: + + void _preTick(); + + /// + PhysicsWorld *mWorld; + + F32 mForce; + + /// + bool mPhysicsTick; + + /// + WeakRefPtr mBody; + +}; + + +#endif // _T3D_PHYSICS_PHYSICSFORCE_H_ \ No newline at end of file diff --git a/Engine/source/T3D/physics/physicsObject.cpp b/Engine/source/T3D/physics/physicsObject.cpp new file mode 100644 index 000000000..8dc3540ad --- /dev/null +++ b/Engine/source/T3D/physics/physicsObject.cpp @@ -0,0 +1,52 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physicsObject.h" + +#include "console/simEvents.h" +#include "console/simSet.h" + + +PhysicsObject::PhysicsObject() + : mQueuedEvent( InvalidEventId ) +{ +} + +PhysicsObject::~PhysicsObject() +{ + if ( mQueuedEvent != InvalidEventId ) + Sim::cancelEvent( mQueuedEvent ); +} + +void PhysicsObject::queueCallback( U32 ms, Delegate callback ) +{ + // Cancel any existing event we may have pending. + if ( mQueuedEvent != InvalidEventId ) + Sim::cancelEvent( mQueuedEvent ); + + // Fire off a new event. + SimDelegateEvent *event_ = new SimDelegateEvent(); + event_->mCallback = callback; + event_->mEventId = &mQueuedEvent; + mQueuedEvent = Sim::postEvent( Sim::getRootGroup(), event_, Sim::getCurrentTime() + ms ); +} diff --git a/Engine/source/T3D/physics/physicsObject.h b/Engine/source/T3D/physics/physicsObject.h new file mode 100644 index 000000000..12697ce96 --- /dev/null +++ b/Engine/source/T3D/physics/physicsObject.h @@ -0,0 +1,91 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _T3D_PHYSICS_PHYSICSOBJECT_H_ +#define _T3D_PHYSICS_PHYSICSOBJECT_H_ + +#ifndef _PHYSICS_PHYSICSUSERDATA_H_ +#include "T3D/physics/physicsUserData.h" +#endif +#ifndef _UTIL_DELEGATE_H_ +#include "core/util/delegate.h" +#endif +#ifndef _REFBASE_H_ +#include "core/util/refBase.h" +#endif + +class PhysicsWorld; +class MatrixF; +class Point3F; +class Box3F; + + +/// +class PhysicsObject : public WeakRefBase +{ +public: + + virtual ~PhysicsObject(); + + /// Returns the physics world this object is a member of. + virtual PhysicsWorld* getWorld() = 0; + + /// Sets the transform on the physics object. + /// + /// For static objects this is only intended to be used for + /// for infrequent changes when editing the mission. + /// + virtual void setTransform( const MatrixF &transform ) = 0; + + /// Returns the transform of the physics body at + /// the last processed simulation tick. + virtual MatrixF& getTransform( MatrixF *outMatrix ) = 0; + + /// Returns the world aligned bounding box containing the PhysicsObject. + virtual Box3F getWorldBounds() = 0; + + /// + void queueCallback( U32 ms, Delegate callback ); + + const PhysicsUserData& getUserData() const { return mUserData; } + + PhysicsUserData& getUserData() { return mUserData; } + + /// Set false to skip simulation of this object or temporarily remove + /// it from the physics simulation. Implementation is PhysicsPlugin specific. + virtual void setSimulationEnabled( bool enabled ) = 0; + virtual bool isSimulationEnabled() = 0; + +protected: + + /// You shouldn't be creating this object directly. + PhysicsObject(); + + /// The user data object assigned to this object. + PhysicsUserData mUserData; + + /// The last queued callback event. + /// @see queueCallback + U32 mQueuedEvent; +}; + +#endif // _T3D_PHYSICS_PHYSICSOBJECT_H_ \ No newline at end of file diff --git a/Engine/source/T3D/physics/physicsPlayer.cpp b/Engine/source/T3D/physics/physicsPlayer.cpp new file mode 100644 index 000000000..6642a001d --- /dev/null +++ b/Engine/source/T3D/physics/physicsPlayer.cpp @@ -0,0 +1,25 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physicsPlayer.h" + diff --git a/Engine/source/T3D/physics/physicsPlayer.h b/Engine/source/T3D/physics/physicsPlayer.h new file mode 100644 index 000000000..97d2874fe --- /dev/null +++ b/Engine/source/T3D/physics/physicsPlayer.h @@ -0,0 +1,74 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _T3D_PHYSICS_PHYSICSPLAYER_H_ +#define _T3D_PHYSICS_PHYSICSPLAYER_H_ + +#ifndef _T3D_PHYSICS_PHYSICSOBJECT_H_ +#include "T3D/physics/physicsObject.h" +#endif +#ifndef _MMATH_H_ +#include "math/mMath.h" +#endif + +class CollisionList; +//struct ObjectRenderInst; +//class BaseMatInstance; +//class Player; +//class SceneState; +class SceneObject; + + +/// +class PhysicsPlayer : public PhysicsObject +{ +public: + + PhysicsPlayer() {} + + virtual ~PhysicsPlayer() {}; + + /// + virtual void init( const char *type, + const Point3F &size, + F32 runSurfaceCos, + F32 stepHeight, + SceneObject *obj, + PhysicsWorld *world ) = 0; + + virtual void findContact( SceneObject **contactObject, + VectorF *contactNormal, + Vector *outOverlapObjects ) const = 0; + + virtual Point3F move( const VectorF &displacement, CollisionList &outCol ) = 0; + + virtual bool testSpacials( const Point3F &nPos, const Point3F &nSize ) const = 0; + + virtual void setSpacials( const Point3F &nPos, const Point3F &nSize ) = 0; + + virtual void enableCollision() = 0; + + virtual void disableCollision() = 0; +}; + + +#endif // _T3D_PHYSICS_PHYSICSPLAYER_H_ \ No newline at end of file diff --git a/Engine/source/T3D/physics/physicsPlugin.cpp b/Engine/source/T3D/physics/physicsPlugin.cpp new file mode 100644 index 000000000..afb06ccb8 --- /dev/null +++ b/Engine/source/T3D/physics/physicsPlugin.cpp @@ -0,0 +1,216 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physicsPlugin.h" + +#include "console/console.h" +#include "console/consoleTypes.h" +#include "console/simSet.h" +#include "core/strings/stringFunctions.h" +#include "scene/sceneObject.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "T3D/physics/physicsObject.h" +#include "T3D/physics/physicsWorld.h" +#include "core/util/tNamedFactory.h" + + +PhysicsPlugin* PhysicsPlugin::smSingleton = NULL; +PhysicsResetSignal PhysicsPlugin::smPhysicsResetSignal; +bool PhysicsPlugin::smSinglePlayer = false; +U32 PhysicsPlugin::smThreadCount = 2; + + +String PhysicsPlugin::smServerWorldName( "server" ); +String PhysicsPlugin::smClientWorldName( "client" ); + +AFTER_MODULE_INIT( Sim ) +{ + Con::addVariable( "$Physics::isSinglePlayer", TypeBool, &PhysicsPlugin::smSinglePlayer, + "@brief Informs the physics simulation if only a single player exists.\n\n" + "If true, optimizations will be implemented to better cater to a single player environmnent.\n\n" + "@ingroup Physics\n"); + Con::addVariable( "$pref::Physics::threadCount", TypeS32, &PhysicsPlugin::smThreadCount, + "@brief Number of threads to use in a single pass of the physics engine.\n\n" + "Defaults to 2 if not set.\n\n" + "@ingroup Physics\n"); +} + +bool PhysicsPlugin::activate( const char *library ) +{ + // Cleanup any previous plugin. + if ( smSingleton ) + { + smSingleton->destroyPlugin(); + AssertFatal( smSingleton == NULL, + "PhysicsPlugin::activate - destroyPlugin didn't delete the plugin!" ); + } + + // Create it thru the factory. + PhysicsPlugin *plugin = NamedFactory::create( library ); + if ( !plugin ) + { + // One last try... try the first available one. + plugin = NamedFactory::create(); + if ( !plugin ) + return false; + } + + smSingleton = plugin; + return true; +} + +PhysicsPlugin::PhysicsPlugin() +{ + mPhysicsCleanup = new SimSet(); + mPhysicsCleanup->assignName( "PhysicsCleanupSet" ); + mPhysicsCleanup->registerObject(); + Sim::getRootGroup()->addObject( mPhysicsCleanup ); +} + +PhysicsPlugin::~PhysicsPlugin() +{ + AssertFatal( smSingleton == this, "PhysicsPlugin::~PhysicsPlugin() - Wrong active plugin!" ); + + if ( mPhysicsCleanup ) + mPhysicsCleanup->deleteObject(); + + smSingleton = NULL; +} + +void PhysicsPlugin::enableDebugDraw( bool enabled ) +{ + if ( enabled ) + SceneManager::getPostRenderSignal().notify( &PhysicsPlugin::_debugDraw ); + else + SceneManager::getPostRenderSignal().remove( &PhysicsPlugin::_debugDraw ); + + _onDebugDrawEnabled( enabled ); +} + +void PhysicsPlugin::_debugDraw( SceneManager *graph, const SceneRenderState *state ) +{ + // We only debug draw in the diffuse pass if we have a physics object. + if ( !PHYSICSMGR || !state->isDiffusePass() ) + return; + + // Render the server by default... else the client. + PhysicsWorld *world = PHYSICSMGR->getWorld( smServerWorldName ); + if ( !world ) + world = PHYSICSMGR->getWorld( smClientWorldName ); + + if ( world ) + world->onDebugDraw( state ); +} + +ConsoleFunction( physicsPluginPresent, bool, 1, 1, "physicsPluginPresent()\n" + "@brief Returns true if a physics plugin exists and is initialized.\n\n" + "@ingroup Physics" ) +{ + return PHYSICSMGR != NULL; +} + +ConsoleFunction( physicsInit, bool, 1, 2, "physicsInit( [string library] )" ) +{ + const char *library = "default"; + if ( argc > 1 ) + library = argv[1]; + + return PhysicsPlugin::activate( library ); +} + +ConsoleFunction( physicsDestroy, void, 1, 1, "physicsDestroy()" ) +{ + if ( PHYSICSMGR ) + PHYSICSMGR->destroyPlugin(); +} + +ConsoleFunction( physicsInitWorld, bool, 2, 2, "physicsInitWorld( String worldName )" ) +{ + return PHYSICSMGR && PHYSICSMGR->createWorld( String( argv[1] ) ); +} + +ConsoleFunction( physicsDestroyWorld, void, 2, 2, "physicsDestroyWorld( String worldName )" ) +{ + if ( PHYSICSMGR ) + PHYSICSMGR->destroyWorld( String( argv[1] ) ); +} + + +// Control/query of the stop/started state +// of the currently running simulation. +ConsoleFunction( physicsStartSimulation, void, 2, 2, "physicsStartSimulation( String worldName )" ) +{ + if ( PHYSICSMGR ) + PHYSICSMGR->enableSimulation( String( argv[1] ), true ); +} + +ConsoleFunction( physicsStopSimulation, void, 2, 2, "physicsStopSimulation( String worldName )" ) +{ + if ( PHYSICSMGR ) + PHYSICSMGR->enableSimulation( String( argv[1] ), false ); +} + +ConsoleFunction( physicsSimulationEnabled, bool, 1, 1, "physicsSimulationEnabled()" ) +{ + return PHYSICSMGR && PHYSICSMGR->isSimulationEnabled(); +} + +// Used for slowing down time on the +// physics simulation, and for pausing/restarting +// the simulation. +ConsoleFunction( physicsSetTimeScale, void, 2, 2, "physicsSetTimeScale( F32 scale )" ) +{ + if ( PHYSICSMGR ) + PHYSICSMGR->setTimeScale( dAtof( argv[1] ) ); +} + +// Get the currently set time scale. +ConsoleFunction( physicsGetTimeScale, F32, 1, 1, "physicsGetTimeScale()" ) +{ + return PHYSICSMGR && PHYSICSMGR->getTimeScale(); +} + +// Used to send a signal to objects in the +// physics simulation that they should store +// their current state for later restoration, +// such as when the editor is closed. +ConsoleFunction( physicsStoreState, void, 1, 1, "physicsStoreState()" ) +{ + PhysicsPlugin::getPhysicsResetSignal().trigger( PhysicsResetEvent_Store ); +} + +// Used to send a signal to objects in the +// physics simulation that they should restore +// their saved state, such as when the editor is opened. +ConsoleFunction( physicsRestoreState, void, 1, 1, "physicsRestoreState()" ) +{ + if ( PHYSICSMGR ) + PHYSICSMGR->reset(); +} + +ConsoleFunction( physicsDebugDraw, void, 2, 2, "physicsDebugDraw( bool enable )" ) +{ + if ( PHYSICSMGR ) + PHYSICSMGR->enableDebugDraw( dAtoi( argv[1] ) ); +} \ No newline at end of file diff --git a/Engine/source/T3D/physics/physicsPlugin.h b/Engine/source/T3D/physics/physicsPlugin.h new file mode 100644 index 000000000..91c187473 --- /dev/null +++ b/Engine/source/T3D/physics/physicsPlugin.h @@ -0,0 +1,147 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _T3D_PHYSICS_PHYSICSPLUGIN_H_ +#define _T3D_PHYSICS_PHYSICSPLUGIN_H_ + +#ifndef _SIMSET_H_ +#include "console/simSet.h" +#endif +#ifndef _TSIGNAL_H_ +#include "core/util/tSignal.h" +#endif +#ifndef _TORQUE_STRING_H_ +#include "core/util/str.h" +#endif +#ifndef _TDICTIONARY_H_ +#include "core/util/tDictionary.h" +#endif +#ifndef _UTIL_DELEGATE_H_ +#include "core/util/delegate.h" +#endif +#ifndef _T3D_PHYSICSCOMMON_H_ +#include "T3D/physics/physicsCommon.h" +#endif + + +class Player; +class SceneRenderState; +class SceneManager; +class SceneObject; +class PhysicsObject; +class PhysicsBody; +class PhysicsWorld; +class PhysicsPlayer; +class PhysicsCollision; + + +typedef Delegate CreatePhysicsObjectFn; +typedef Map CreateFnMap; + + +/// +class PhysicsPlugin +{ + +protected: + + /// The current active physics plugin. + static PhysicsPlugin* smSingleton; + + static PhysicsResetSignal smPhysicsResetSignal; + + // Our map of Strings to PhysicsWorld pointers. + Map mPhysicsWorldLookup; + + static String smServerWorldName; + static String smClientWorldName; + + /// A SimSet of objects to delete before the + /// physics reset/restore event occurs. + SimObjectPtr mPhysicsCleanup; + + /// Delegate method for debug drawing. + static void _debugDraw( SceneManager *graph, const SceneRenderState *state ); + +public: + + /// Note this should go away when we have "real" singleplayer. + static bool smSinglePlayer; + static bool isSinglePlayer() { return smSinglePlayer; } + + /// Number of threads to use if supported by the plugin. + static U32 smThreadCount; + static U32 getThreadCount() { return smThreadCount; } + + /// Returns the active physics plugin. + /// @see PHYSICSPLUGIN + static PhysicsPlugin* getSingleton() { return smSingleton; } + + /// + static bool activate( const char *library ); + + PhysicsPlugin(); + virtual ~PhysicsPlugin(); + + /// Cleans up, deactivates, and deletes the plugin. + virtual void destroyPlugin() = 0; + + virtual void reset() = 0; + + /// Returns the physics cleanup set. + SimSet* getPhysicsCleanup() const { return mPhysicsCleanup; } + + void enableDebugDraw( bool enabled ); + + virtual PhysicsCollision* createCollision() = 0; + + virtual PhysicsBody* createBody() = 0; + + virtual PhysicsPlayer* createPlayer() = 0; + + virtual bool isSimulationEnabled() const = 0; + virtual void enableSimulation( const String &worldName, bool enable ) = 0; + + virtual void setTimeScale( const F32 timeScale ) = 0; + virtual const F32 getTimeScale() const = 0; + + static PhysicsResetSignal& getPhysicsResetSignal() { return smPhysicsResetSignal; } + + virtual bool createWorld( const String &worldName ) = 0; + virtual void destroyWorld( const String &worldName ) = 0; + + virtual PhysicsWorld* getWorld( const String &worldName ) const = 0; + +protected: + + /// Overload this to toggle any physics engine specific stuff + /// when debug rendering is enabled or disabled. + virtual void _onDebugDrawEnabled( bool enabled ) {} + +}; + +/// Helper macro for accessing the physics plugin. It will +/// return NULL if no plugin is initialized. +/// @see PhysicsPlugin +#define PHYSICSMGR PhysicsPlugin::getSingleton() + +#endif // _T3D_PHYSICS_PHYSICSPLUGIN_H_ \ No newline at end of file diff --git a/Engine/source/T3D/physics/physicsShape.cpp b/Engine/source/T3D/physics/physicsShape.cpp new file mode 100644 index 000000000..1d4e6d84a --- /dev/null +++ b/Engine/source/T3D/physics/physicsShape.cpp @@ -0,0 +1,1172 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physicsShape.h" + +#include "console/consoleTypes.h" +#include "core/stream/bitStream.h" +#include "core/resourceManager.h" +#include "math/mathIO.h" +#include "T3D/physics/physicsPlugin.h" +#include "T3D/physics/physicsBody.h" +#include "T3D/physics/physicsWorld.h" +#include "T3D/physics/physicsCollision.h" +#include "collision/concretePolyList.h" +#include "ts/tsShapeInstance.h" +#include "scene/sceneRenderState.h" +#include "gfx/gfxTransformSaver.h" +#include "T3D/physics/physicsDebris.h" +#include "T3D/fx/explosion.h" +#include "T3D/containerQuery.h" +#include "lighting/lightQuery.h" +#include "console/engineAPI.h" + + +bool PhysicsShape::smNoCorrections = false; +bool PhysicsShape::smNoSmoothing = false; + +ImplementEnumType( PhysicsSimType, + "How to handle the physics simulation with the client's and server.\n" + "@ingroup Physics\n\n") + { PhysicsShapeData::SimType_ClientOnly, "ClientOnly", "Only handle physics on the client.\n" }, + { PhysicsShapeData::SimType_ServerOnly, "ServerOnly", "Only handle physics on the server.\n" }, + { PhysicsShapeData::SimType_ClientServer, "ClientServer", "Handle physics on both the client and server.\n" } +EndImplementEnumType; + + +IMPLEMENT_CO_DATABLOCK_V1( PhysicsShapeData ); + +ConsoleDocClass( PhysicsShapeData, + + "@brief Defines the properties of a PhysicsShape.\n\n" + "@see PhysicsShape.\n" + "@ingroup Physics" +); + +PhysicsShapeData::PhysicsShapeData() + : shapeName( NULL ), + mass( 1.0f ), + dynamicFriction( 0.0f ), + staticFriction( 0.0f ), + restitution( 0.0f ), + linearDamping( 0.0f ), + angularDamping( 0.0f ), + linearSleepThreshold( 1.0f ), + angularSleepThreshold( 1.0f ), + waterDampingScale( 1.0f ), + buoyancyDensity( 0.0f ), + simType( SimType_ClientServer ) +{ +} + +PhysicsShapeData::~PhysicsShapeData() +{ +} + +void PhysicsShapeData::initPersistFields() +{ + Parent::initPersistFields(); + + addGroup("Media"); + + addField( "shapeName", TypeShapeFilename, Offset( shapeName, PhysicsShapeData ), + "@brief Path to the .DAE or .DTS file to use for this shape.\n\n" + "Compatable with Live-Asset Reloading. "); + + addField( "debris", TYPEID< SimObjectRef >(), Offset( debris, PhysicsShapeData ), + "@brief Name of a PhysicsDebrisData to spawn when this shape is destroyed (optional)." ); + + addField( "explosion", TYPEID< SimObjectRef >(), Offset( explosion, PhysicsShapeData ), + "@brief Name of an ExplosionData to spawn when this shape is destroyed (optional)." ); + + addField( "destroyedShape", TYPEID< SimObjectRef >(), Offset( destroyedShape, PhysicsShapeData ), + "@brief Name of a PhysicsShapeData to spawn when this shape is destroyed (optional)." ); + + endGroup("Media"); + + addGroup( "Physics" ); + + addField( "mass", TypeF32, Offset( mass, PhysicsShapeData ), + "@brief Value representing the mass of the shape.\n\n" + "A shape's mass influences the magnitude of any force exerted on it. " + "For example, a PhysicsShape with a large mass requires a much larger force to move than " + "the same shape with a smaller mass.\n" + "@note A mass of zero will create a kinematic shape while anything greater will create a dynamic shape."); + + addField( "friction", TypeF32, Offset( dynamicFriction, PhysicsShapeData ), + "@brief Coefficient of kinetic %friction to be applied to the shape.\n\n" + "Kinetic %friction reduces the velocity of a moving object while it is in contact with a surface. " + "A higher coefficient will result in a larger velocity reduction. " + "A shape's friction should be lower than it's staticFriction, but larger than 0.\n\n" + "@note This value is only applied while an object is in motion. For an object starting at rest, see PhysicsShape::staticFriction"); + + addField( "staticFriction", TypeF32, Offset( staticFriction, PhysicsShapeData ), + "@brief Coefficient of static %friction to be applied to the shape.\n\n" + "Static %friction determines the force needed to start moving an at-rest object in contact with a surface. " + "If the force applied onto shape cannot overcome the force of static %friction, the shape will remain at rest. " + "A larger coefficient will require a larger force to start motion. " + "This value should be larger than zero and the physicsShape's friction.\n\n" + "@note This value is only applied while an object is at rest. For an object in motion, see PhysicsShape::friction"); + + addField( "restitution", TypeF32, Offset( restitution, PhysicsShapeData ), + "@brief Coeffecient of a bounce applied to the shape in response to a collision.\n\n" + "Restitution is a ratio of a shape's velocity before and after a collision. " + "A value of 0 will zero out a shape's post-collision velocity, making it stop on contact. " + "Larger values will remove less velocity after a collision, making it \'bounce\' with a greater force. " + "Normal %restitution values range between 0 and 1.0." + "@note Values near or equaling 1.0 are likely to cause undesirable results in the physics simulation." + " Because of this it is reccomended to avoid values close to 1.0"); + + addField( "linearDamping", TypeF32, Offset( linearDamping, PhysicsShapeData ), + "@brief Value that reduces an object's linear velocity over time.\n\n" + "Larger values will cause velocity to decay quicker.\n\n" ); + + addField( "angularDamping", TypeF32, Offset( angularDamping, PhysicsShapeData ), + "@brief Value that reduces an object's rotational velocity over time.\n\n" + "Larger values will cause velocity to decay quicker.\n\n" ); + + addField( "linearSleepThreshold", TypeF32, Offset( linearSleepThreshold, PhysicsShapeData ), + "@brief Minimum linear velocity before the shape can be put to sleep.\n\n" + "This should be a positive value. Shapes put to sleep will not be simulated in order to save system resources.\n\n" + "@note The shape must be dynamic."); + + addField( "angularSleepThreshold", TypeF32, Offset( angularSleepThreshold, PhysicsShapeData ), + "@brief Minimum rotational velocity before the shape can be put to sleep.\n\n" + "This should be a positive value. Shapes put to sleep will not be simulated in order to save system resources.\n\n" + "@note The shape must be dynamic."); + + addField( "waterDampingScale", TypeF32, Offset( waterDampingScale, PhysicsShapeData ), + "@brief Scale to apply to linear and angular dampening while underwater.\n\n " + "Used with the waterViscosity of the " + "@see angularDamping linearDamping" ); + + addField( "buoyancyDensity", TypeF32, Offset( buoyancyDensity, PhysicsShapeData ), + "@brief The density of the shape for calculating buoyant forces.\n\n" + "The result of the calculated buoyancy is relative to the density of the WaterObject the PhysicsShape is within.\n\n" + "@see WaterObject::density"); + + endGroup( "Physics" ); + + addGroup( "Networking" ); + + addField( "simType", TYPEID< PhysicsShapeData::SimType >(), Offset( simType, PhysicsShapeData ), + "@brief Controls whether this shape is simulated on the server, client, or both physics simulations.\n\n" ); + + endGroup( "Networking" ); +} + +void PhysicsShapeData::packData( BitStream *stream ) +{ + Parent::packData( stream ); + + stream->writeString( shapeName ); + + stream->write( mass ); + stream->write( dynamicFriction ); + stream->write( staticFriction ); + stream->write( restitution ); + stream->write( linearDamping ); + stream->write( angularDamping ); + stream->write( linearSleepThreshold ); + stream->write( angularSleepThreshold ); + stream->write( waterDampingScale ); + stream->write( buoyancyDensity ); + + stream->writeInt( simType, SimType_Bits ); + + stream->writeRangedU32( debris ? debris->getId() : 0, 0, DataBlockObjectIdLast ); + stream->writeRangedU32( explosion ? explosion->getId() : 0, 0, DataBlockObjectIdLast ); + stream->writeRangedU32( destroyedShape ? destroyedShape->getId() : 0, 0, DataBlockObjectIdLast ); +} + +void PhysicsShapeData::unpackData( BitStream *stream ) +{ + Parent::unpackData(stream); + + shapeName = stream->readSTString(); + + stream->read( &mass ); + stream->read( &dynamicFriction ); + stream->read( &staticFriction ); + stream->read( &restitution ); + stream->read( &linearDamping ); + stream->read( &angularDamping ); + stream->read( &linearSleepThreshold ); + stream->read( &angularSleepThreshold ); + stream->read( &waterDampingScale ); + stream->read( &buoyancyDensity ); + + simType = (SimType)stream->readInt( SimType_Bits ); + + debris = stream->readRangedU32( 0, DataBlockObjectIdLast ); + explosion = stream->readRangedU32( 0, DataBlockObjectIdLast ); + destroyedShape = stream->readRangedU32( 0, DataBlockObjectIdLast ); +} + +bool PhysicsShapeData::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + ResourceManager::get().getChangedSignal().notify( this, &PhysicsShapeData::_onResourceChanged ); + return true; +} + +void PhysicsShapeData::onRemove() +{ + ResourceManager::get().getChangedSignal().remove( this, &PhysicsShapeData::_onResourceChanged ); + Parent::onRemove(); +} + +void PhysicsShapeData::_onResourceChanged( const Torque::Path &path ) +{ + if ( path != Path( shapeName ) ) + return; + + // Reload the changed shape. + Resource reloadShape; + PhysicsCollisionRef reloadcolShape; + + reloadShape = ResourceManager::get().load( shapeName ); + if ( !bool(reloadShape) ) + { + Con::warnf( ConsoleLogEntry::General, "PhysicsShapeData::_onResourceChanged: Could not reload %s.", path.getFileName().c_str() ); + return; + } + + // Reload the collision shape. + reloadcolShape = shape->buildColShape( false, Point3F::One ); + + if ( bool(reloadShape) && bool(colShape)) + { + shape = reloadShape; + colShape = reloadcolShape; + } + + mReloadSignal.trigger(); +} + +bool PhysicsShapeData::preload( bool server, String &errorBuffer ) +{ + if ( !Parent::preload( server, errorBuffer ) ) + return false; + + // If we don't have a physics plugin active then + // we have to fail completely. + if ( !PHYSICSMGR ) + { + errorBuffer = "PhysicsShapeData::preload - No physics plugin is active!"; + return false; + } + + if ( !shapeName || !shapeName[0] ) + { + errorBuffer = "PhysicsShapeData::preload - No shape name defined."; + return false; + } + + // Load the shape. + shape = ResourceManager::get().load( shapeName ); + if ( bool(shape) == false ) + { + errorBuffer = String::ToString( "PhysicsShapeData::preload - Unable to load shape '%s'.", shapeName ); + return false; + } + + // Prepare the shared physics collision shape. + if ( !colShape ) + { + colShape = shape->buildColShape( false, Point3F::One ); + + // If we got here and didn't get a collision shape then + // we need to fail... can't have a shape without collision. + if ( !colShape ) + { + errorBuffer = String::ToString( "PhysicsShapeData::preload - No collision found for shape '%s'.", shapeName ); + return false; + } + } + + // My convex demcomposion test + /* + // Get the verts and triangles for the first visible detail. + ConcretePolyList polyList; + polyList.setTransform( &MatrixF::Identity, Point3F::One ); + TSShapeInstance shapeInst( shape, false ); + shapeInst.animate(0); + if ( !shapeInst.buildPolyList( &polyList, 0 ) ) + return false; + + // Gah... Ratcliff's lib works on doubles... why, oh why? + Vector doubleVerts; + doubleVerts.setSize( polyList.mVertexList.size() * 3 ); + for ( U32 i=0; i < polyList.mVertexList.size(); i++ ) + { + doubleVerts[ ( i * 3 ) + 0 ] = (F64)polyList.mVertexList[i].x; + doubleVerts[ ( i * 3 ) + 1 ] = (F64)polyList.mVertexList[i].y; + doubleVerts[ ( i * 3 ) + 2 ] = (F64)polyList.mVertexList[i].z; + } + + using namespace ConvexDecomposition; + + class ConvexBuilder : public ConvexDecompInterface + { + public: + + ConvexBuilder() { } + + ~ConvexBuilder() + { + for ( U32 i=0; i < mHulls.size(); i++ ) + delete mHulls[i]; + } + + virtual void ConvexDecompResult( ConvexResult &result ) + { + FConvexResult *hull = new FConvexResult( result ); + mHulls.push_back( hull ); + } + + Vector mHulls; + }; + + DecompDesc d; + d.mVcount = polyList.mVertexList.size(); + d.mVertices = doubleVerts.address(); + d.mTcount = polyList.mIndexList.size() / 3; + d.mIndices = polyList.mIndexList.address(); + d.mDepth = 3; + d.mCpercent = 20.0f; + d.mPpercent = 30.0f; + d.mMaxVertices = 32; + d.mSkinWidth = 0.05f; // Need to expose this! + + ConvexBuilder builder; + d.mCallback = &builder; + + if ( performConvexDecomposition( d ) < 1 || builder.mHulls.empty() ) + return false; + + // Add all the convex hull results into the collision shape. + colShape = PHYSICSMGR->createCollision(); + for ( U32 i=0; i < builder.mHulls.size(); i++ ) + colShape->addConvex( (const Point3F*)builder.mHulls[i]->mHullVertices, + builder.mHulls[i]->mHullVcount, + MatrixF::Identity ); + */ + + return true; +} + + +IMPLEMENT_CO_NETOBJECT_V1(PhysicsShape); + +ConsoleDocClass( PhysicsShape, + + "@brief Represents a destructible physical object simulated through the plugin system.\n\n" + "@see PhysicsShapeData.\n" + "@ingroup Physics" +); + +PhysicsShape::PhysicsShape() + : mPhysicsRep( NULL ), + mWorld( NULL ), + mShapeInst( NULL ), + mResetPos( MatrixF::Identity ), + mDestroyed( false ), + mPlayAmbient( false ), + mAmbientThread( NULL ), + mAmbientSeq( -1 ) +{ + mNetFlags.set( Ghostable | ScopeAlways ); + mTypeMask |= DynamicShapeObjectType; +} + +PhysicsShape::~PhysicsShape() +{ +} + +void PhysicsShape::consoleInit() +{ + Con::addVariable( "$PhysicsShape::noCorrections", TypeBool, &PhysicsShape::smNoCorrections, + "@brief Determines if the shape will recieve corrections from the server or " + "will instead be allowed to diverge.\n\n" + "In the event that the client and server object positions/orientations " + "differ and if this variable is true, the server will attempt to \'correct\' " + "the client object to keep it in sync. Otherwise, client and server objects may fall out of sync.\n\n"); + + Con::addVariable( "$PhysicsShape::noSmoothing", TypeBool, &PhysicsShape::smNoSmoothing, + "@brief Determines if client-side shapes will attempt to smoothly transition to " + "their new position after reciving a correction.\n\n" + "If true, shapes will immediately render at the position they are corrected to.\n\n"); + + Parent::consoleInit(); +} + +void PhysicsShape::initPersistFields() +{ + addGroup( "PhysicsShape" ); + + addField( "playAmbient", TypeBool, Offset( mPlayAmbient, PhysicsShape ), + "@brief Enables or disables playing of an ambient animation upon loading the shape.\n\n" + "@note The ambient animation must be named \"ambient\"." ); + + endGroup( "PhysicsShape" ); + + Parent::initPersistFields(); + + removeField( "scale" ); +} + +void PhysicsShape::inspectPostApply() +{ + Parent::inspectPostApply(); + + setMaskBits( InitialUpdateMask ); +} + +U32 PhysicsShape::packUpdate( NetConnection *con, U32 mask, BitStream *stream ) +{ + U32 retMask = Parent::packUpdate( con, mask, stream ); + + if ( stream->writeFlag( mask & InitialUpdateMask ) ) + { + stream->writeAffineTransform( getTransform() ); + stream->writeFlag( mPlayAmbient ); + + stream->writeFlag( mDestroyed ); + + return retMask; + } + + // If we got here its not an initial update. So only send + // the least amount of data possible. + + if ( stream->writeFlag( mask & StateMask ) ) + { + // This will encode the position relative to the control + // object position. + // + // This will compress the position to as little as 6.25 + // bytes if the position is within about 30 meters of the + // control object. + // + // Worst case its a full 12 bytes + 2 bits if the position + // is more than 500 meters from the control object. + // + stream->writeCompressedPoint( mState.position ); + + // Use only 3.5 bytes to send the orientation. + stream->writeQuat( mState.orientation, 9 ); + + // If the server object has been set to sleep then + // we don't need to send any velocity. + if ( !stream->writeFlag( mState.sleeping ) ) + { + // This gives me ~0.015f resolution in velocity magnitude + // while only costing me 1 bit of the velocity is zero length, + // <5 bytes in normal cases, and <8 bytes if the velocity is + // greater than 1000. + AssertWarn( mState.linVelocity.len() < 1000.0f, + "PhysicsShape::packUpdate - The linVelocity is out of range!" ); + stream->writeVector( mState.linVelocity, 1000.0f, 16, 9 ); + + // For angular velocity we get < 0.01f resolution in magnitude + // with the most common case being under 4 bytes. + AssertWarn( mState.angVelocity.len() < 10.0f, + "PhysicsShape::packUpdate - The angVelocity is out of range!" ); + stream->writeVector( mState.angVelocity, 10.0f, 10, 9 ); + } + } + + if ( stream->writeFlag( mask & DamageMask ) ) + stream->writeFlag( mDestroyed ); + + return retMask; +} + +void PhysicsShape::unpackUpdate( NetConnection *con, BitStream *stream ) +{ + Parent::unpackUpdate( con, stream ); + + if ( stream->readFlag() ) // InitialUpdateMask + { + MatrixF mat; + stream->readAffineTransform( &mat ); + setTransform( mat ); + mPlayAmbient = stream->readFlag(); + + if ( isProperlyAdded() ) + _initAmbient(); + + if ( stream->readFlag() ) + { + if ( isProperlyAdded() ) + { + // Destroy immediately if we've already been added + // to the scene. + destroy(); + } + else + { + // Indicate the shape should be destroyed when the + // shape is added. + mDestroyed = true; + } + } + + return; + } + + if ( stream->readFlag() ) // StateMask + { + PhysicsState state; + + // Read the encoded and compressed position... commonly only 6.25 bytes. + stream->readCompressedPoint( &state.position ); + + // Read the compressed quaternion... 3.5 bytes. + stream->readQuat( &state.orientation, 9 ); + + state.sleeping = stream->readFlag(); + if ( !state.sleeping ) + { + stream->readVector( &state.linVelocity, 1000.0f, 16, 9 ); + stream->readVector( &state.angVelocity, 10.0f, 10, 9 ); + } + + if ( !smNoCorrections && mPhysicsRep && mPhysicsRep->isDynamic() && !mDestroyed ) + { + // Set the new state on the physics object immediately. + mPhysicsRep->applyCorrection( state.getTransform() ); + + mPhysicsRep->setSleeping( state.sleeping ); + if ( !state.sleeping ) + { + mPhysicsRep->setLinVelocity( state.linVelocity ); + mPhysicsRep->setAngVelocity( state.angVelocity ); + } + + mPhysicsRep->getState( &mState ); + } + + // If there is no physics object then just set the + // new state... the tick will take care of the + // interpolation and extrapolation. + if ( !mPhysicsRep || !mPhysicsRep->isDynamic() ) + mState = state; + } + + if ( stream->readFlag() ) // DamageMask + { + if ( stream->readFlag() ) + destroy(); + else + restore(); + } +} + +bool PhysicsShape::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + // If we don't have a physics plugin active then + // we have to fail completely. + if ( !PHYSICSMGR ) + { + Con::errorf( "PhysicsShape::onAdd - No physics plugin is active!" ); + return false; + } + + // + if ( !mPhysicsRep && !_createShape() ) + { + Con::errorf( "PhysicsShape::onAdd() - Shape creation failed!" ); + return false; + } + + // The reset position is the transform on the server + // at creation time... its not used on the client. + if ( isServerObject() ) + { + storeRestorePos(); + PhysicsPlugin::getPhysicsResetSignal().notify( this, &PhysicsShape::_onPhysicsReset ); + } + + // Register for the resource change signal. + //ResourceManager::get().getChangedSignal().notify( this, &PhysicsShape::_onResourceChanged ); + + // Only add server objects and non-destroyed client objects to the scene. + if ( isServerObject() || !mDestroyed) + addToScene(); + + if ( isClientObject() && mDestroyed ) + { + // Disable all simulation of the body... no collision or dynamics. + if ( mPhysicsRep ) + mPhysicsRep->setSimulationEnabled( false ); + + // Stop doing tick processing for this SceneObject. + setProcessTick( false ); + } + + return true; +} + +void PhysicsShape::onRemove() +{ + removeFromScene(); + + SAFE_DELETE( mPhysicsRep ); + SAFE_DELETE( mShapeInst ); + mAmbientThread = NULL; + mAmbientSeq = -1; + mWorld = NULL; + + if ( isServerObject() ) + { + PhysicsPlugin::getPhysicsResetSignal().remove( this, &PhysicsShape::_onPhysicsReset ); + + if ( mDestroyedShape ) + mDestroyedShape->deleteObject(); + } + + // Remove the resource change signal. + //ResourceManager::get().getChangedSignal().remove( this, &PhysicsShape::_onResourceChanged ); + + Parent::onRemove(); +} + +bool PhysicsShape::onNewDataBlock( GameBaseData *dptr, bool reload ) +{ + if ( !Parent::onNewDataBlock( dptr, reload ) ) + return false; + + if ( !isProperlyAdded() ) + return true; + + // If we don't have a physics plugin active then + // we have to fail completely. + if ( !PHYSICSMGR ) + { + Con::errorf( "PhysicsShape::onNewDataBlock - No physics plugin is active!" ); + return false; + } + + // + if ( !_createShape() ) + { + Con::errorf( "PhysicsShape::onNewDataBlock() - Shape creation failed!" ); + return false; + } + + return true; +} + +bool PhysicsShape::_createShape() +{ + SAFE_DELETE( mPhysicsRep ); + SAFE_DELETE( mShapeInst ); + mAmbientThread = NULL; + mWorld = NULL; + mAmbientSeq = -1; + + PhysicsShapeData *db = getDataBlock(); + if ( !db ) + return false; + + // Set the world box. + mObjBox = db->shape->bounds; + resetWorldBox(); + + // If this is the server and its a client only simulation + // object then disable our tick... the server doesn't do + // any work for this shape. + if ( isServerObject() && + db->simType == PhysicsShapeData::SimType_ClientOnly ) + { + setProcessTick( false ); + return true; + } + + // Create the shape instance. + mShapeInst = new TSShapeInstance( db->shape, isClientObject() ); + + if ( isClientObject() ) + { + mAmbientSeq = db->shape->findSequence( "ambient" ); + _initAmbient(); + } + + // If the shape has a mass then its dynamic... else + // its a kinematic shape. + // + // While a kinematic is less optimal than a static body + // it allows for us to enable/disable collision and having + // all dynamic actors react correctly... waking up. + // + const bool isDynamic = db->mass > 0.0f; + + // If we aren't dynamic we don't need to tick. + setProcessTick( isDynamic || mPlayAmbient ); + + // If this is the client and we're a server only object then + // we don't need any physics representation... we're done. + if ( isClientObject() && + db->simType == PhysicsShapeData::SimType_ServerOnly ) + return true; + + mWorld = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" ); + + mPhysicsRep = PHYSICSMGR->createBody(); + mPhysicsRep->init( db->colShape, + db->mass, + isDynamic ? 0 : PhysicsBody::BF_KINEMATIC, + this, + mWorld ); + + mPhysicsRep->setMaterial( db->restitution, db->dynamicFriction, db->staticFriction ); + + if ( isDynamic ) + { + mPhysicsRep->setDamping( db->linearDamping, db->angularDamping ); + mPhysicsRep->setSleepThreshold( db->linearSleepThreshold, db->angularSleepThreshold ); + } + + mPhysicsRep->setTransform( getTransform() ); + + return true; +} + +void PhysicsShape::_initAmbient() +{ + if ( isServerObject() ) + return; + + bool willPlay = mPlayAmbient && mAmbientSeq != -1; + + if ( willPlay ) + { + // Create thread if we dont already have. + if ( mAmbientThread == NULL ) + mAmbientThread = mShapeInst->addThread(); + + // Play the sequence. + mShapeInst->setSequence( mAmbientThread, mAmbientSeq, 0); + + setProcessTick(true); + } + else + { + if ( mAmbientThread != NULL ) + { + mShapeInst->destroyThread( mAmbientThread ); + mAmbientThread = NULL; + } + } +} + +void PhysicsShape::_onPhysicsReset( PhysicsResetEvent reset ) +{ + if ( reset == PhysicsResetEvent_Store ) + mResetPos = getTransform(); + + else if ( reset == PhysicsResetEvent_Restore ) + { + setTransform( mResetPos ); + + // Restore to un-destroyed state. + restore(); + + // Cheat and reset the client from here. + if ( getClientObject() ) + { + PhysicsShape *clientObj = (PhysicsShape*)getClientObject(); + clientObj->setTransform( mResetPos ); + clientObj->restore(); + } + } +} + +void PhysicsShape::setTransform( const MatrixF &newMat ) +{ + Parent::setTransform( newMat ); + + // This is only called to set an absolute position + // so we discard the delta state. + mState.position = getPosition(); + mState.orientation.set( newMat ); + mRenderState[0] = mRenderState[1] = mState; + setMaskBits( StateMask ); + + if ( mPhysicsRep ) + mPhysicsRep->setTransform( newMat ); +} + +void PhysicsShape::setScale( const VectorF &scale ) +{ + // Cannot scale PhysicsShape. + return; +} + +void PhysicsShape::storeRestorePos() +{ + mResetPos = getTransform(); +} + +F32 PhysicsShape::getMass() const +{ + const PhysicsShapeData *db = const_cast( this )->getDataBlock(); + return db->mass; +} + +void PhysicsShape::applyImpulse( const Point3F &pos, const VectorF &vec ) +{ + if ( mPhysicsRep && mPhysicsRep->isDynamic() ) + mPhysicsRep->applyImpulse( pos, vec ); +} + +void PhysicsShape::applyRadialImpulse( const Point3F &origin, F32 radius, F32 magnitude ) +{ + if ( !mPhysicsRep || !mPhysicsRep->isDynamic() ) + return; + + // TODO: Find a better approximation of the + // force vector using the object box. + + VectorF force = getWorldBox().getCenter() - origin; + F32 dist = force.magnitudeSafe(); + force.normalize(); + + if ( dist == 0.0f ) + force *= magnitude; + else + force *= mClampF( radius / dist, 0.0f, 1.0f ) * magnitude; + + mPhysicsRep->applyImpulse( origin, force ); + + // TODO: There is no simple way to really sync this sort of an + // event with the client. + // + // The best is to send the current physics snapshot, calculate the + // time difference from when this event occured and the time when the + // client recieves it, and then extrapolate where it should be. + // + // Even then its impossible to be absolutely sure its synced. + // + // Bottom line... you shouldn't use physics over the network like this. + // + + // Cheat for single player. + //if ( getClientObject() ) + //((PhysicsShape*)getClientObject())->mPhysicsRep->applyImpulse( origin, force ); +} + +void PhysicsShape::interpolateTick( F32 delta ) +{ + AssertFatal( !mDestroyed, "PhysicsShape::interpolateTick - Shouldn't be processing a destroyed shape!" ); + + if ( !mPhysicsRep->isDynamic() ) + return; + + // Interpolate the position and rotation based on the delta. + PhysicsState state; + state.interpolate( mRenderState[1], mRenderState[0], delta ); + + // Set the transform to the interpolated transform. + setRenderTransform( state.getTransform() ); +} + +void PhysicsShape::processTick( const Move *move ) +{ + AssertFatal( mPhysicsRep && !mDestroyed, "PhysicsShape::processTick - Shouldn't be processing a destroyed shape!" ); + + // Note that unlike TSStatic, the serverside PhysicsShape does not + // need to play the ambient animation because even if the animation were + // to move collision shapes it would not affect the physx representation. + + if ( !mPhysicsRep->isDynamic() ) + return; + + // SINGLE PLAYER HACK!!!! + if ( PHYSICSMGR->isSinglePlayer() && isClientObject() && getServerObject() ) + { + PhysicsShape *servObj = (PhysicsShape*)getServerObject(); + setTransform( servObj->mState.getTransform() ); + mRenderState[0] = servObj->mRenderState[0]; + mRenderState[1] = servObj->mRenderState[1]; + + return; + } + + // Store the last render state. + mRenderState[0] = mRenderState[1]; + + // If the last render state doesn't match the last simulation + // state then we got a correction and need to + Point3F errorDelta = mRenderState[1].position - mState.position; + const bool doSmoothing = !errorDelta.isZero() && !smNoSmoothing; + + const bool wasSleeping = mState.sleeping; + + // Get the new physics state. + if ( mPhysicsRep ) + { + mPhysicsRep->getState( &mState ); + _updateContainerForces(); + } + else + { + // This is where we could extrapolate. + } + + // Smooth the correction back into the render state. + mRenderState[1] = mState; + if ( doSmoothing ) + { + F32 correction = mClampF( errorDelta.len() / 20.0f, 0.1f, 0.9f ); + mRenderState[1].position.interpolate( mState.position, mRenderState[0].position, correction ); + mRenderState[1].orientation.interpolate( mState.orientation, mRenderState[0].orientation, correction ); + } + + // If we haven't been sleeping then update our transform + // and set ourselves as dirty for the next client update. + if ( !wasSleeping || !mState.sleeping ) + { + // Set the transform on the parent so that + // the physics object isn't moved. + Parent::setTransform( mState.getTransform() ); + + // If we're doing server simulation then we need + // to send the client a state update. + if ( isServerObject() && mPhysicsRep && !smNoCorrections && + + !PHYSICSMGR->isSinglePlayer() // SINGLE PLAYER HACK!!!! + + ) + setMaskBits( StateMask ); + } +} + +void PhysicsShape::advanceTime( F32 timeDelta ) +{ + if ( isClientObject() && mPlayAmbient && mAmbientThread != NULL ) + mShapeInst->advanceTime( timeDelta, mAmbientThread ); +} + +void PhysicsShape::_updateContainerForces() +{ + PROFILE_SCOPE( PhysicsShape_updateContainerForces ); + + // If we're not simulating don't update forces. + if ( !mWorld->isEnabled() ) + return; + + ContainerQueryInfo info; + info.box = getWorldBox(); + info.mass = getDataBlock()->mass; + + // Find and retreive physics info from intersecting WaterObject(s) + getContainer()->findObjects( getWorldBox(), WaterObjectType|PhysicalZoneObjectType, findRouter, &info ); + + // Calculate buoyancy and drag + F32 angDrag = getDataBlock()->angularDamping; + F32 linDrag = getDataBlock()->linearDamping; + F32 buoyancy = 0.0f; + Point3F cmass = mPhysicsRep->getCMassPosition(); + + F32 density = getDataBlock()->buoyancyDensity; + if ( density > 0.0f ) + { + if ( info.waterCoverage > 0.0f ) + { + F32 waterDragScale = info.waterViscosity * getDataBlock()->waterDampingScale; + F32 powCoverage = mPow( info.waterCoverage, 0.25f ); + + angDrag = mLerp( angDrag, angDrag * waterDragScale, powCoverage ); + linDrag = mLerp( linDrag, linDrag * waterDragScale, powCoverage ); + } + + buoyancy = ( info.waterDensity / density ) * mPow( info.waterCoverage, 2.0f ); + + // A little hackery to prevent oscillation + // Based on this blog post: + // (http://reinot.blogspot.com/2005/11/oh-yes-they-float-georgie-they-all.html) + // JCF: disabled! + Point3F buoyancyForce = buoyancy * -mWorld->getGravity() * TickSec * getDataBlock()->mass; + mPhysicsRep->applyImpulse( cmass, buoyancyForce ); + } + + // Update the dampening as the container might have changed. + mPhysicsRep->setDamping( linDrag, angDrag ); + + // Apply physical zone forces. + if ( !info.appliedForce.isZero() ) + mPhysicsRep->applyImpulse( cmass, info.appliedForce ); +} + +void PhysicsShape::prepRenderImage( SceneRenderState *state ) +{ + AssertFatal( !mDestroyed, "PhysicsShape::prepRenderImage - Shouldn't be processing a destroyed shape!" ); + + PROFILE_SCOPE( PhysicsShape_prepRenderImage ); + + if( !mShapeInst ) + return; + + Point3F cameraOffset; + getRenderTransform().getColumn(3,&cameraOffset); + cameraOffset -= state->getDiffuseCameraPosition(); + F32 dist = cameraOffset.len(); + if (dist < 0.01f) + dist = 0.01f; + + F32 invScale = (1.0f/getMax(getMax(mObjScale.x,mObjScale.y),mObjScale.z)); + if ( mShapeInst->setDetailFromDistance( state, dist * invScale ) < 0 ) + return; + + GFXTransformSaver saver; + + // Set up our TS render state. + TSRenderState rdata; + rdata.setSceneState( state ); + rdata.setFadeOverride( 1.0f ); + + // We might have some forward lit materials + // so pass down a query to gather lights. + LightQuery query; + query.init( getWorldSphere() ); + rdata.setLightQuery( &query ); + + MatrixF mat = getRenderTransform(); + mat.scale( mObjScale ); + GFX->setWorldMatrix( mat ); + + mShapeInst->animate(); + mShapeInst->render( rdata ); +} + +void PhysicsShape::destroy() +{ + if ( mDestroyed ) + return; + + mDestroyed = true; + setMaskBits( DamageMask ); + + const Point3F lastLinVel = mPhysicsRep->isDynamic() ? mPhysicsRep->getLinVelocity() : Point3F::Zero; + + // Disable all simulation of the body... no collision or dynamics. + mPhysicsRep->setSimulationEnabled( false ); + + // On the client side we remove it from the scene graph + // to disable rendering and volume queries. + if ( isClientObject() ) + removeFromScene(); + + // Stop doing tick processing for this SceneObject. + setProcessTick( false ); + + PhysicsShapeData *db = getDataBlock(); + if ( !db ) + return; + + const MatrixF &mat = getTransform(); + if ( isServerObject() ) + { + // We only create the destroyed object on the server + // and let ghosting deal with updating the client. + + if ( db->destroyedShape ) + { + mDestroyedShape = new PhysicsShape(); + mDestroyedShape->setDataBlock( db->destroyedShape ); + mDestroyedShape->setTransform( mat ); + if ( !mDestroyedShape->registerObject() ) + delete mDestroyedShape.getObject(); + } + + return; + } + + // Let the physics debris create itself. + PhysicsDebris::create( db->debris, mat, lastLinVel ); + + if ( db->explosion ) + { + Explosion *splod = new Explosion(); + splod->setDataBlock( db->explosion ); + splod->setTransform( mat ); + splod->setInitialState( getPosition(), mat.getUpVector(), 1.0f ); + if ( !splod->registerObject() ) + delete splod; + } +} + +void PhysicsShape::restore() +{ + if ( !mDestroyed ) + return; + + PhysicsShapeData *db = getDataBlock(); + const bool isDynamic = db && db->mass > 0.0f; + + if ( mDestroyedShape ) + mDestroyedShape->deleteObject(); + + // Restore tick processing, add it back to + // the scene, and enable collision and simulation. + setProcessTick( isDynamic || mPlayAmbient ); + if ( isClientObject() ) + addToScene(); + mPhysicsRep->setSimulationEnabled( true ); + + mDestroyed = false; + setMaskBits( DamageMask ); +} + +DefineEngineMethod( PhysicsShape, isDestroyed, bool, (),, + "@brief Returns if a PhysicsShape has been destroyed or not.\n\n" ) +{ + return object->isDestroyed(); +} + +DefineEngineMethod( PhysicsShape, destroy, void, (),, + "@brief Disables rendering and physical simulation.\n\n" + "Calling destroy() will also spawn any explosions, debris, and/or destroyedShape " + "defined for it, as well as remove it from the scene graph.\n\n" + "Destroyed objects are only created on the server. Ghosting will later update the client.\n\n" + "@note This does not actually delete the PhysicsShape." ) +{ + object->destroy(); +} + +DefineEngineMethod( PhysicsShape, restore, void, (),, + "@brief Restores the shape to its state before being destroyed.\n\n" + "Re-enables rendering and physical simulation on the object and " + "adds it to the client's scene graph. " + "Has no effect if the shape is not destroyed.\n\n") +{ + object->restore(); +} \ No newline at end of file diff --git a/Engine/source/T3D/physics/physicsShape.h b/Engine/source/T3D/physics/physicsShape.h new file mode 100644 index 000000000..65394183e --- /dev/null +++ b/Engine/source/T3D/physics/physicsShape.h @@ -0,0 +1,269 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PHYSICSSHAPE_H_ +#define _PHYSICSSHAPE_H_ + +#ifndef _GAMEBASE_H_ + #include "T3D/gameBase/gameBase.h" +#endif +#ifndef __RESOURCE_H__ + #include "core/resource.h" +#endif +#ifndef _TSSHAPE_H_ + #include "ts/tsShape.h" +#endif +#ifndef _T3D_PHYSICSCOMMON_H_ + #include "T3D/physics/physicsCommon.h" +#endif +#ifndef _DYNAMIC_CONSOLETYPES_H_ + #include "console/dynamicTypes.h" +#endif +#ifndef _SIMOBJECTREF_H_ + #include "console/simObjectRef.h" +#endif + +class TSShapeInstance; +class PhysicsBody; +class PhysicsWorld; +class PhysicsDebrisData; +class ExplosionData; + + +class PhysicsShapeData : public GameBaseData +{ + typedef GameBaseData Parent; + + void _onResourceChanged( const Torque::Path &path ); + +public: + + PhysicsShapeData(); + virtual ~PhysicsShapeData(); + + DECLARE_CONOBJECT(PhysicsShapeData); + static void initPersistFields(); + bool onAdd(); + void onRemove(); + + // GameBaseData + void packData(BitStream* stream); + void unpackData(BitStream* stream); + bool preload(bool server, String &errorBuffer ); + +public: + + /// The shape to load. + StringTableEntry shapeName; + + /// The shape resource. + Resource shape; + + /// The shared unscaled collision shape. + PhysicsCollisionRef colShape; + + /// + F32 mass; + + /// + F32 dynamicFriction; + + /// + F32 staticFriction; + + /// + F32 restitution; + + /// + F32 linearDamping; + + /// + F32 angularDamping; + + /// + F32 linearSleepThreshold; + + /// + F32 angularSleepThreshold; + + // A scale applied to the normal linear and angular damping + // when the object enters a water volume. + F32 waterDampingScale; + + // The density of this object used for water buoyancy effects. + F32 buoyancyDensity; + + + enum SimType + { + /// This physics representation only exists on the client + /// world and the server only does ghosting. + SimType_ClientOnly, + + /// The physics representation only exists on the server world + /// and the client gets delta updates for rendering. + SimType_ServerOnly, + + /// The physics representation exists on the client and the server + /// worlds with corrections occuring when the client gets out of sync. + SimType_ClientServer, + + /// The bits used to pack the SimType field. + SimType_Bits = 3, + + } simType; + + SimObjectRef< PhysicsDebrisData > debris; + SimObjectRef< ExplosionData > explosion; + SimObjectRef< PhysicsShapeData > destroyedShape; +}; + +typedef PhysicsShapeData::SimType PhysicsSimType; +DefineEnumType( PhysicsSimType ); + +class TSThread; + + +/// A simple single body dynamic physics object. +class PhysicsShape : public GameBase +{ + typedef GameBase Parent; + +protected: + + /// The abstracted physics actor. + PhysicsBody *mPhysicsRep; + + /// + PhysicsWorld *mWorld; + + /// The starting position to place the shape when + /// the level begins or is reset. + MatrixF mResetPos; + + //VectorF mBuildScale; + //F32 mBuildAngDrag; + //F32 mBuildLinDrag; + + /// The rendered shape. + TSShapeInstance *mShapeInst; + + /// The current physics state. + PhysicsState mState; + + /// The previous and current render states. + PhysicsState mRenderState[2]; + + /// True if the PhysicsShape has been destroyed ( gameplay ). + bool mDestroyed; + + /// Enables automatic playing of the animation named "ambient" (if it exists) + /// when the PhysicsShape is loaded. + bool mPlayAmbient; + S32 mAmbientSeq; + TSThread* mAmbientThread; + + /// If a specified to create one in the PhysicsShape data, this is the + /// subshape created when this PhysicsShape is destroyed. + /// Is only assigned (non null) on the serverside PhysicsShape. + SimObjectPtr< PhysicsShape > mDestroyedShape; + + /// + enum MaskBits + { + StateMask = Parent::NextFreeMask << 0, + ResetPosMask = Parent::NextFreeMask << 1, + DamageMask = Parent::NextFreeMask << 2, + + NextFreeMask = Parent::NextFreeMask << 3 + }; + + bool _createShape(); + + void _initAmbient(); + + /// + void _applyCorrection( const MatrixF &mat ); + + void _onPhysicsReset( PhysicsResetEvent reset ); + + void _updateContainerForces(); + + /// If true then no corrections are sent from the server + /// and/or applied from the client. + /// + /// This is only ment for debugging. + /// + static bool smNoCorrections; + + /// If true then no smoothing is done on the client when + /// applying server corrections. + /// + /// This is only ment for debugging. + /// + static bool smNoSmoothing; + +public: + + PhysicsShape(); + virtual ~PhysicsShape(); + + DECLARE_CONOBJECT( PhysicsShape ); + + /// Returns the PhysicsShapeData datablock. + PhysicsShapeData* getDataBlock() { return static_cast( Parent::getDataBlock() ); } + + // SimObject + static void consoleInit(); + static void initPersistFields(); + void inspectPostApply(); + bool onAdd(); + void onRemove(); + + // SceneObject + void prepRenderImage( SceneRenderState *state ); + void setTransform( const MatrixF &mat ); + F32 getMass() const; + Point3F getVelocity() const { return mState.linVelocity; } + void applyImpulse( const Point3F &pos, const VectorF &vec ); + void applyRadialImpulse( const Point3F &origin, F32 radius, F32 magnitude ); + void setScale(const VectorF & scale); + + // GameBase + bool onNewDataBlock( GameBaseData *dptr, bool reload ); + void interpolateTick( F32 delta ); + void processTick( const Move *move ); + void advanceTime( F32 timeDelta ); + U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream ); + void unpackUpdate( NetConnection *conn, BitStream *stream ); + + bool isDestroyed() const { return mDestroyed; } + void destroy(); + void restore(); + + /// Save the current transform as where we return to when a physics reset + /// event occurs. This is automatically set in onAdd but some manipulators + /// such as Prefab need to make use of this. + void storeRestorePos(); +}; + +#endif // _PHYSICSSHAPE_H_ diff --git a/Engine/source/T3D/physics/physicsUserData.cpp b/Engine/source/T3D/physics/physicsUserData.cpp new file mode 100644 index 000000000..40903377e --- /dev/null +++ b/Engine/source/T3D/physics/physicsUserData.cpp @@ -0,0 +1,29 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physicsUserData.h" + + +#ifdef TORQUE_DEBUG +const char* PhysicsUserData::smTypeName = "PhysicsUserData"; +#endif diff --git a/Engine/source/T3D/physics/physicsUserData.h b/Engine/source/T3D/physics/physicsUserData.h new file mode 100644 index 000000000..9c6f21e32 --- /dev/null +++ b/Engine/source/T3D/physics/physicsUserData.h @@ -0,0 +1,120 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PHYSICS_PHYSICSUSERDATA_H_ +#define _PHYSICS_PHYSICSUSERDATA_H_ + +#ifndef _SIGNAL_H_ +#include "core/util/tSignal.h" +#endif + +class PhysicsUserData; +class SceneObject; +class Point3F; +class PhysicsBody; + + +/// Signal used for contact reports. +/// +/// @param us The physics user data for the signaling object. +/// @param them The other physics user data involved in the contact. +/// @param hitPoint The approximate position of the impact. +/// @param hitForce +/// +/// @see PhysicsUserData +/// +typedef Signal PhysicsContactSignal; + + +/// The base class for physics user data. +class PhysicsUserData +{ +public: + + /// The constructor. + PhysicsUserData() + : mObject( NULL ), + mBody( NULL ) + #ifdef TORQUE_DEBUG + , mTypeId( smTypeName ) + #endif + {} + + /// The destructor. + virtual ~PhysicsUserData() {} + + /// + void setObject( SceneObject *object ) { mObject = object; } + SceneObject* getObject() const { return mObject; } + + void setBody( PhysicsBody *body ) { mBody = body; } + PhysicsBody* getBody() const { return mBody; } + + /// Helper method for casting a void pointer to a userdata pointer. + static inline SceneObject* getObject( void *data ) + { + PhysicsUserData *result = cast( data ); + return result ? result->getObject() : NULL; + } + + PhysicsContactSignal& getContactSignal() { return mContactSignal; } + + /// Helper method for casting a void pointer to a userdata pointer. + static inline PhysicsUserData* cast( void *data ) + { + PhysicsUserData *result = (PhysicsUserData*)data; + + // If the typeid doesn't equal the value we assigned to it at + // construction then this isn't a PhysicsUserData object. + #ifdef TORQUE_DEBUG + AssertFatal( !result || result->mTypeId == smTypeName, + "PhysicsUserData::cast - The pointer is the wrong type!" ); + #endif + + return result; + } + +protected: + + #ifdef TORQUE_DEBUG + + /// The type string used to validate the void* cast. + /// @see cast + static const char *smTypeName; + + /// The type string assigned at construction used to + /// validate the void* cast. + /// @see cast + const char *mTypeId; + #endif + + PhysicsContactSignal mContactSignal; + + SceneObject *mObject; + + PhysicsBody *mBody; +}; + +#endif // _PHYSICS_PHYSICSUSERDATA_H_ \ No newline at end of file diff --git a/Engine/source/T3D/physics/physicsWorld.cpp b/Engine/source/T3D/physics/physicsWorld.cpp new file mode 100644 index 000000000..7b1f49b3a --- /dev/null +++ b/Engine/source/T3D/physics/physicsWorld.cpp @@ -0,0 +1,30 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physicsWorld.h" + + +PhysicsWorld::PhysicsWorld() + : mGravity( 0, 0, -20.0f ) // NOTE: This matches the gravity used for player objects. +{ +} diff --git a/Engine/source/T3D/physics/physicsWorld.h b/Engine/source/T3D/physics/physicsWorld.h new file mode 100644 index 000000000..0db190098 --- /dev/null +++ b/Engine/source/T3D/physics/physicsWorld.h @@ -0,0 +1,112 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _T3D_PHYSICS_PHYSICSWORLD_H_ +#define _T3D_PHYSICS_PHYSICSWORLD_H_ + +#ifndef _SIGNAL_H_ +#include "core/util/tSignal.h" +#endif +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif + +class ProcessList; +class Point3F; +struct RayInfo; +class SceneRenderState; +class PhysicsBody; + + + +class PhysicsWorld +{ +protected: + + Signal mUpdateSignal; + + /// The current gravity force. + Point3F mGravity; + +public: + + /// The constructor. + PhysicsWorld(); + + /// The destructor. + virtual ~PhysicsWorld() {}; + + /// + Signal& getUpdateSignal() { return mUpdateSignal; } + + /// Overloaded to do debug drawing. + /// + /// It is assumed the GFX state is setup prior to this call for + /// rendering in world space. + /// + virtual void onDebugDraw( const SceneRenderState *state ) = 0; + + /// Prepare the physics world for use. + virtual bool initWorld( bool isServer, ProcessList *processList ) = 0; + + /// Tears down the physics world destroying any existing + /// bodies, joints, and controllers. + virtual void destroyWorld() = 0; + + /// + virtual void reset() = 0; + + /// Returns true if the physics world is active and simulating. + virtual bool isEnabled() const = 0; + + /// Returns the active gravity force. + const Point3F& getGravity() const { return mGravity; } + + /// An abstract way to raycast into any type of PhysicsWorld, in a way + /// that mirrors a Torque-style raycast. + // + /// This method is not fully developed or very sophisticated. For example, + /// there is no system of collision groups or raycast masks, which could + /// be very complex to write in a PhysicsPlugin-Abstract way... + // + // Optional forceAmt parameter will also apply a force to hit objects. + virtual bool castRay( const Point3F &startPnt, const Point3F &endPnt, RayInfo *ri, const Point3F &impulse ) = 0; + + + /// + enum BodyType + { + BT_Static = BIT( 0 ), + BT_Dynamic = BIT( 1 ), + BT_Player = BIT( 2 ), + + BT_All = BT_Static | BT_Dynamic | BT_Player + }; + + /// + virtual PhysicsBody* castRay( const Point3F &start, const Point3F &end, U32 bodyTypes = BT_All ) = 0; + + virtual void explosion( const Point3F &pos, F32 radius, F32 forceMagnitude ) = 0; +}; + + +#endif // _T3D_PHYSICS_PHYSICSWORLD_H_ \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx/px.h b/Engine/source/T3D/physics/physx/px.h new file mode 100644 index 000000000..c4dd9dccb --- /dev/null +++ b/Engine/source/T3D/physics/physx/px.h @@ -0,0 +1,80 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// +// This PhysX implementation for Torque was originally based on +// the "PhysX in TGEA" resource written by Shannon Scarvaci. +// +// http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=12711 +// + +#ifndef _PHYSX_H_ +#define _PHYSX_H_ + +/* +#ifndef _TORQUE_TYPES_H_ +# include "platform/types.h" +#endif +*/ + +#include "platform/tmm_off.h" + +#ifdef TORQUE_DEBUG +#include +#endif + +#if defined(TORQUE_OS_MAC) && !defined(__APPLE__) + #define __APPLE__ +#elif defined(TORQUE_OS_LINUX) && !defined(LINUX) + #define LINUX +#elif defined(TORQUE_OS_WIN32) && !defined(WIN32) + #define WIN32 +#endif + +#ifndef NX_PHYSICS_NXPHYSICS +#include +#endif +#ifndef NX_FOUNDATION_NXSTREAM +#include +#endif +#ifndef NX_COOKING_H +#include +#endif +#ifndef NX_FOUNDATION_NXUSEROUTPUTSTREAM +#include +#endif +#ifndef NX_PHYSICS_NXBIG +#include "NxExtended.h" +#endif +#include +#include +#include +#include +#include +#include + +/// The single global physx sdk object for this process. +extern NxPhysicsSDK *gPhysicsSDK; + +#include "platform/tmm_on.h" + +#endif // _PHYSX_H_ \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx/pxBody.cpp b/Engine/source/T3D/physics/physx/pxBody.cpp new file mode 100644 index 000000000..de889139c --- /dev/null +++ b/Engine/source/T3D/physics/physx/pxBody.cpp @@ -0,0 +1,404 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physX/pxBody.h" + +#include "T3D/physics/physX/px.h" +#include "T3D/physics/physX/pxCasts.h" +#include "T3D/physics/physX/pxWorld.h" +#include "T3D/physics/physX/pxCollision.h" + + +PxBody::PxBody() : + mActor( NULL ), + mMaterial( NULL ), + mWorld( NULL ), + mBodyFlags( 0 ), + mIsEnabled( true ) +{ +} + +PxBody::~PxBody() +{ + _releaseActor(); +} + +void PxBody::_releaseActor() +{ + if ( !mActor ) + return; + + // This sucks, but it has to happen if we want + // to avoid write lock errors from PhysX right now. + mWorld->releaseWriteLock(); + + mActor->userData = NULL; + + mWorld->releaseActor( *mActor ); + mActor = NULL; + mBodyFlags = 0; + + if ( mMaterial ) + { + mWorld->releaseMaterial( *mMaterial ); + mMaterial = NULL; + } + + mColShape = NULL; +} + +bool PxBody::init( PhysicsCollision *shape, + F32 mass, + U32 bodyFlags, + SceneObject *obj, + PhysicsWorld *world ) +{ + AssertFatal( obj, "PxBody::init - Got a null scene object!" ); + AssertFatal( world, "PxBody::init - Got a null world!" ); + AssertFatal( dynamic_cast( world ), "PxBody::init - The world is the wrong type!" ); + AssertFatal( shape, "PxBody::init - Got a null collision shape!" ); + AssertFatal( dynamic_cast( shape ), "PxBody::init - The collision shape is the wrong type!" ); + AssertFatal( !((PxCollision*)shape)->getShapes().empty(), "PxBody::init - Got empty collision shape!" ); + + // Cleanup any previous actor. + _releaseActor(); + + mWorld = (PxWorld*)world; + mColShape = (PxCollision*)shape; + mBodyFlags = bodyFlags; + + NxActorDesc actorDesc; + NxBodyDesc bodyDesc; + + const bool isKinematic = mBodyFlags & BF_KINEMATIC; + const bool isTrigger = mBodyFlags & BF_TRIGGER; + const bool isDebris = mBodyFlags & BF_DEBRIS; + + if ( isKinematic ) + { + // Kinematics are dynamics... so they need + // a body description. + actorDesc.body = &bodyDesc; + bodyDesc.mass = getMax( mass, 1.0f ); + bodyDesc.flags |= NX_BF_KINEMATIC; + } + else if ( mass > 0.0f ) + { + // We have mass so its a dynamic. + actorDesc.body = &bodyDesc; + bodyDesc.mass = mass; + } + + if ( isTrigger ) + actorDesc.flags |= NX_AF_DISABLE_RESPONSE; + + // Add all the shapes. + const Vector &shapes = mColShape->getShapes(); + for ( U32 i=0; i < shapes.size(); i++ ) + { + NxShapeDesc *desc = shapes[i]; + + // If this hits then something is broken with + // this descrption... check all the fields to be + // sure their values are correctly filled out. + AssertFatal( desc->isValid(), "PxBody::init - Got invalid shape description!" ); + + if ( isTrigger ) + desc->group = 31; + + if ( isDebris ) + desc->group = 30; + + actorDesc.shapes.push_back( desc ); + } + + // This sucks, but it has to happen if we want + // to avoid write lock errors from PhysX right now. + mWorld->releaseWriteLock(); + + mActor = mWorld->getScene()->createActor( actorDesc ); + mIsEnabled = true; + + if ( isDebris ) + mActor->setDominanceGroup( 31 ); + + mUserData.setObject( obj ); + mUserData.setBody( this ); + mActor->userData = &mUserData; + + return true; +} + +void PxBody::setMaterial( F32 restitution, + F32 friction, + F32 staticFriction ) +{ + AssertFatal( mActor, "PxBody::setMaterial - The actor is null!" ); + + // If the body is dynamic then wake it up as + // it may need to change behavior. + if ( isDynamic() ) + mActor->wakeUp(); + + NxMaterialDesc desc; + desc.restitution = restitution; + desc.dynamicFriction = friction; + desc.staticFriction = staticFriction; + + // If we have a material then just update it as the shapes + // should already have them mapped. + if ( mMaterial ) + { + mMaterial->loadFromDesc( desc ); + return; + } + + // If we got here then create a new material and + // assign it to all our shapes. + mMaterial = mWorld->createMaterial( desc ); + U32 matIndex = mMaterial->getMaterialIndex(); + U32 count = mActor->getNbShapes(); + NxShape*const* shapes = mActor->getShapes(); + for ( U32 i=0; i < count; i++ ) + shapes[i]->setMaterial( matIndex ); +} + +void PxBody::setSleepThreshold( F32 linear, F32 angular ) +{ + AssertFatal( mActor, "PxBody::setSleepThreshold - The actor is null!" ); + + mActor->setSleepLinearVelocity( linear ); + mActor->setSleepAngularVelocity( angular ); +} + +void PxBody::setDamping( F32 linear, F32 angular ) +{ + AssertFatal( mActor, "PxBody::setDamping - The actor is null!" ); + mActor->setLinearDamping( linear ); + mActor->setAngularDamping( angular ); +} + +void PxBody::getState( PhysicsState *outState ) +{ + AssertFatal( mActor, "PxBody::getState - The actor is null!" ); + AssertFatal( isDynamic(), "PxBody::getState - This call is only for dynamics!" ); + + // TODO: Fix this to do what we intended... to return + // false so that the caller can early out of the state + // hasn't changed since the last tick. + + outState->position = pxCast( mActor->getGlobalPosition() ); + outState->orientation = pxCast( mActor->getGlobalOrientationQuat() ); + outState->linVelocity = pxCast( mActor->getLinearVelocity() ); + outState->angVelocity = pxCast( mActor->getAngularVelocity() ); + outState->sleeping = mActor->isSleeping(); + outState->momentum = pxCast( mActor->getLinearMomentum() ); +} + +F32 PxBody::getMass() const +{ + AssertFatal( mActor, "PxBody::getCMassPosition - The actor is null!" ); + return mActor->getMass(); +} + +Point3F PxBody::getCMassPosition() const +{ + AssertFatal( mActor, "PxBody::getCMassPosition - The actor is null!" ); + return pxCast( mActor->getCMassGlobalPosition() ); +} + +void PxBody::setLinVelocity( const Point3F &vel ) +{ + AssertFatal( mActor, "PxBody::setLinVelocity - The actor is null!" ); + AssertFatal( isDynamic(), "PxBody::setLinVelocity - This call is only for dynamics!" ); + + mActor->setLinearVelocity( pxCast( vel ) ); +} + +void PxBody::setAngVelocity( const Point3F &vel ) +{ + AssertFatal( mActor, "PxBody::setAngVelocity - The actor is null!" ); + AssertFatal( isDynamic(), "PxBody::setAngVelocity - This call is only for dynamics!" ); + + mActor->setAngularVelocity( pxCast( vel ) ); +} + +Point3F PxBody::getLinVelocity() const +{ + AssertFatal( mActor, "PxBody::getLinVelocity - The actor is null!" ); + AssertFatal( isDynamic(), "PxBody::getLinVelocity - This call is only for dynamics!" ); + + return pxCast( mActor->getLinearVelocity() ); +} + +Point3F PxBody::getAngVelocity() const +{ + AssertFatal( mActor, "PxBody::getAngVelocity - The actor is null!" ); + AssertFatal( isDynamic(), "PxBody::getAngVelocity - This call is only for dynamics!" ); + + return pxCast( mActor->getAngularVelocity() ); +} + +void PxBody::setSleeping( bool sleeping ) +{ + AssertFatal( mActor, "PxBody::setSleeping - The actor is null!" ); + AssertFatal( isDynamic(), "PxBody::setSleeping - This call is only for dynamics!" ); + + if ( sleeping ) + mActor->putToSleep(); + else + mActor->wakeUp(); +} + +bool PxBody::isDynamic() const +{ + AssertFatal( mActor, "PxBody::isDynamic - The actor is null!" ); + return mActor->isDynamic() && ( mBodyFlags & BF_KINEMATIC ) == 0; +} + +PhysicsWorld* PxBody::getWorld() +{ + return mWorld; +} + +PhysicsCollision* PxBody::getColShape() +{ + return mColShape; +} + +MatrixF& PxBody::getTransform( MatrixF *outMatrix ) +{ + AssertFatal( mActor, "PxBody::getTransform - The actor is null!" ); + + mActor->getGlobalPose().getRowMajor44( *outMatrix ); + + return *outMatrix; +} + +Box3F PxBody::getWorldBounds() +{ + AssertFatal( mActor, "PxBody::getTransform - The actor is null!" ); + + NxBounds3 bounds; + bounds.setEmpty(); + NxBounds3 shapeBounds; + + NxShape *const* pShapeArray = mActor->getShapes(); + U32 shapeCount = mActor->getNbShapes(); + + for ( U32 i = 0; i < shapeCount; i++ ) + { + // Get the shape's bounds. + pShapeArray[i]->getWorldBounds( shapeBounds ); + + // Combine them into the total bounds. + bounds.combine( shapeBounds ); + } + + return pxCast( bounds ); +} + +void PxBody::setSimulationEnabled( bool enabled ) +{ + if ( mIsEnabled == enabled ) + return; + + // This sucks, but it has to happen if we want + // to avoid write lock errors from PhysX right now. + mWorld->releaseWriteLock(); + + if ( enabled ) + { + mIsEnabled = true; + mActor->clearActorFlag( NX_AF_DISABLE_RESPONSE ); + mActor->clearActorFlag( NX_AF_DISABLE_COLLISION ); + + // Don't clear the flag if its supposed to be kinematic. + if ( !(mBodyFlags & BF_KINEMATIC) ) + mActor->clearBodyFlag( NX_BF_KINEMATIC ); + + if ( isDynamic() ) + mActor->wakeUp(); + } + else + { + mIsEnabled = false; + mActor->raiseActorFlag( NX_AF_DISABLE_RESPONSE ); + mActor->raiseActorFlag( NX_AF_DISABLE_COLLISION ); + mActor->raiseBodyFlag( NX_BF_KINEMATIC ); + } + + NxShape *const* shapes = mActor->getShapes(); + for ( S32 i = 0; i < mActor->getNbShapes(); i++ ) + shapes[i]->setFlag( NX_SF_DISABLE_RAYCASTING, !mIsEnabled ); +} + +void PxBody::setTransform( const MatrixF &transform ) +{ + AssertFatal( mActor, "PxBody::setTransform - The actor is null!" ); + + // This sucks, but it has to happen if we want + // to avoid write lock errors from PhysX right now. + mWorld->releaseWriteLock(); + + NxMat34 xfm; + xfm.setRowMajor44( transform ); + mActor->setGlobalPose( xfm ); + + // If its dynamic we have more to do. + if ( mActor->isDynamic() && !mActor->readBodyFlag( NX_BF_KINEMATIC ) ) + { + mActor->setLinearVelocity( NxVec3( 0, 0, 0 ) ); + mActor->setAngularVelocity( NxVec3( 0, 0, 0 ) ); + mActor->wakeUp(); + } +} + +void PxBody::applyCorrection( const MatrixF &transform ) +{ + AssertFatal( mActor, "PxBody::applyCorrection - The actor is null!" ); + AssertFatal( isDynamic(), "PxBody::applyCorrection - This call is only for dynamics!" ); + + // This sucks, but it has to happen if we want + // to avoid write lock errors from PhysX right now. + mWorld->releaseWriteLock(); + + NxMat34 xfm; + xfm.setRowMajor44( transform ); + mActor->setGlobalPose( xfm ); +} + +void PxBody::applyImpulse( const Point3F &origin, const Point3F &force ) +{ + AssertFatal( mActor, "PxBody::applyImpulse - The actor is null!" ); + + // This sucks, but it has to happen if we want + // to avoid write lock errors from PhysX right now. + mWorld->releaseWriteLock(); + + if ( mIsEnabled && isDynamic() ) + mActor->addForceAtPos( pxCast( force ), + pxCast( origin ), + NX_IMPULSE ); +} + diff --git a/Engine/source/T3D/physics/physx/pxBody.h b/Engine/source/T3D/physics/physx/pxBody.h new file mode 100644 index 000000000..2aeec6e0f --- /dev/null +++ b/Engine/source/T3D/physics/physx/pxBody.h @@ -0,0 +1,114 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _T3D_PHYSICS_PXBODY_H_ +#define _T3D_PHYSICS_PXBODY_H_ + +#ifndef _T3D_PHYSICS_PHYSICSBODY_H_ +#include "T3D/physics/physicsBody.h" +#endif +#ifndef _PHYSICS_PHYSICSUSERDATA_H_ +#include "T3D/physics/physicsUserData.h" +#endif +#ifndef _REFBASE_H_ +#include "core/util/refBase.h" +#endif +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif + +class PxWorld; +class NxActor; +class PxCollision; +class NxMaterial; + + +class PxBody : public PhysicsBody +{ +protected: + + /// The physics world we are in. + PxWorld *mWorld; + + /// The physics actor. + NxActor *mActor; + + /// The unshared local material used on all the + /// shapes on this actor. + NxMaterial *mMaterial; + + /// We hold the collision reference as it contains + /// allocated objects that we own and must free. + StrongRefPtr mColShape; + + /// + MatrixF mInternalTransform; + + /// The body flags set at creation time. + U32 mBodyFlags; + + /// Is true if this body is enabled and active + /// in the simulation of the scene. + bool mIsEnabled; + + /// + void _releaseActor(); + +public: + + PxBody(); + virtual ~PxBody(); + + // PhysicsObject + virtual PhysicsWorld* getWorld(); + virtual void setTransform( const MatrixF &xfm ); + virtual MatrixF& getTransform( MatrixF *outMatrix ); + virtual Box3F getWorldBounds(); + virtual void setSimulationEnabled( bool enabled ); + virtual bool isSimulationEnabled() { return mIsEnabled; } + + // PhysicsBody + virtual bool init( PhysicsCollision *shape, + F32 mass, + U32 bodyFlags, + SceneObject *obj, + PhysicsWorld *world ); + virtual bool isDynamic() const; + virtual PhysicsCollision* getColShape(); + virtual void setSleepThreshold( F32 linear, F32 angular ); + virtual void setDamping( F32 linear, F32 angular ); + virtual void getState( PhysicsState *outState ); + virtual F32 getMass() const; + virtual Point3F getCMassPosition() const; + virtual void setLinVelocity( const Point3F &vel ); + virtual void setAngVelocity( const Point3F &vel ); + virtual Point3F getLinVelocity() const; + virtual Point3F getAngVelocity() const; + virtual void setSleeping( bool sleeping ); + virtual void setMaterial( F32 restitution, + F32 friction, + F32 staticFriction ); + virtual void applyCorrection( const MatrixF &xfm ); + virtual void applyImpulse( const Point3F &origin, const Point3F &force ); +}; + +#endif // _T3D_PHYSICS_PXBODY_H_ diff --git a/Engine/source/T3D/physics/physx/pxCasts.h b/Engine/source/T3D/physics/physx/pxCasts.h new file mode 100644 index 000000000..ee9555702 --- /dev/null +++ b/Engine/source/T3D/physics/physx/pxCasts.h @@ -0,0 +1,150 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PHYSX_CASTS_H_ +#define _PHYSX_CASTS_H_ + +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _MBOX_H_ +#include "math/mBox.h" +#endif +#ifndef _MQUAT_H_ +#include "math/mQuat.h" +#endif + + +template inline T pxCast( const F &from ); + +//------------------------------------------------------------------------- + +template<> +inline Point3F pxCast( const NxVec3 &vec ) +{ + return Point3F( vec.x, vec.y, vec.z ); +} + +template<> +inline NxVec3 pxCast( const Point3F &point ) +{ + return NxVec3( point.x, point.y, point.z ); +} + +//------------------------------------------------------------------------- + +template<> +inline QuatF pxCast( const NxQuat &quat ) +{ + /// The Torque quat has the opposite winding order. + return QuatF( -quat.x, -quat.y, -quat.z, quat.w ); +} + +template<> +inline NxQuat pxCast( const QuatF &quat ) +{ + /// The Torque quat has the opposite winding order. + NxQuat result; + result.setWXYZ( quat.w, -quat.x, -quat.y, -quat.z ); + return result; +} + +//------------------------------------------------------------------------- + +template<> +inline NxBounds3 pxCast( const Box3F &box ) +{ + NxBounds3 bounds; + bounds.set( box.minExtents.x, + box.minExtents.y, + box.minExtents.z, + box.maxExtents.x, + box.maxExtents.y, + box.maxExtents.z ); + return bounds; +} + +template<> +inline Box3F pxCast( const NxBounds3 &bounds ) +{ + return Box3F( bounds.min.x, + bounds.min.y, + bounds.min.z, + bounds.max.x, + bounds.max.y, + bounds.max.z ); +} + +//------------------------------------------------------------------------- + +template<> +inline NxVec3 pxCast( const NxExtendedVec3 &xvec ) +{ + return NxVec3( xvec.x, xvec.y, xvec.z ); +} + +template<> +inline NxExtendedVec3 pxCast( const NxVec3 &vec ) +{ + return NxExtendedVec3( vec.x, vec.y, vec.z ); +} + +//------------------------------------------------------------------------- + +template<> +inline NxExtendedVec3 pxCast( const Point3F &point ) +{ + return NxExtendedVec3( point.x, point.y, point.z ); +} + +template<> +inline Point3F pxCast( const NxExtendedVec3 &xvec ) +{ + return Point3F( xvec.x, xvec.y, xvec.z ); +} + +//------------------------------------------------------------------------- + +template<> +inline NxBox pxCast( const NxExtendedBounds3 &exBounds ) +{ + NxExtendedVec3 center; + exBounds.getCenter( center ); + NxVec3 extents; + exBounds.getExtents( extents ); + + NxBox box; + box.center.set( center.x, center.y, center.z ); + box.extents = extents; + box.rot.id(); + + return box; +} + +template<> +inline NxExtendedBounds3 pxCast( const NxBox &box ) +{ + AssertFatal( false, "Casting a NxBox to NxExtendedBounds3 is impossible without losing rotation data!" ); + return NxExtendedBounds3(); +} + +#endif // _PHYSX_CASTS_H_ \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx/pxCloth.cpp b/Engine/source/T3D/physics/physx/pxCloth.cpp new file mode 100644 index 000000000..723e71b67 --- /dev/null +++ b/Engine/source/T3D/physics/physx/pxCloth.cpp @@ -0,0 +1,923 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physX/pxCloth.h" + +#include "console/consoleTypes.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "renderInstance/renderPassManager.h" +#include "lighting/lightQuery.h" +#include "T3D/physics/physicsPlugin.h" +#include "T3D/physics/physx/pxWorld.h" +#include "T3D/physics/physx/pxStream.h" +#include "T3D/physics/physx/pxCasts.h" +#include "gfx/gfxDrawUtil.h" +#include "math/mathIO.h" +#include "core/stream/bitStream.h" +#include "materials/materialManager.h" +#include "materials/baseMatInstance.h" + + +IMPLEMENT_CO_NETOBJECT_V1( PxCloth ); + +ConsoleDocClass( PxCloth, + + "@brief Rectangular patch of cloth simulated by PhysX.\n\n" + + "PxCloth is affected by other objects in the simulation but does not itself " + "affect others, it is essentially a visual effect. Eg, shooting at cloth will " + "disturb it but will not explode the projectile.\n\n" + + "Be careful with the cloth size and resolution because it can easily become " + "performance intensive to simulate. A single piece of cloth that is very " + "large or high resolution is also much more expensive than multiple pieces " + "that add up to the same number of verts.\n\n" + + "Note that most field docs have been copied from their PhysX counterpart.\n\n" + + "@ingroup Physics" +); + +enum PxClothAttachment {}; +DefineBitfieldType( PxClothAttachment ); + +ImplementBitfieldType( PxClothAttachment, + "Soon to be deprecated\n" + "@internal" ) + { 0, "Bottom Right" }, + { 1, "Bottom Left" }, + { 2, "Top Right" }, + { 3, "Top Left" }, + { 4, "Top Center" }, + { 5, "Bottom Center" }, + { 6, "Right Center" }, + { 7, "Left Center" }, + { 8, "Top Edge" }, + { 9, "Bottom Edge" }, + { 10, "Right Edge" }, + { 11, "Left Edge" } +EndImplementBitfieldType; + + +PxCloth::PxCloth() + : mWorld( NULL ), + mScene( NULL ), + mMatInst( NULL ) +{ + mVertexRenderBuffer = NULL; + mIndexRenderBuffer = NULL; + + mMaxVertices = 0; + mMaxIndices = 0; + + mClothMesh = NULL; + mCloth = NULL; + + mPatchVerts.set( 8, 8 ); + mPatchSize.set( 8.0f, 8.0f ); + + mNetFlags.set( Ghostable | ScopeAlways ); + mTypeMask |= StaticObjectType | StaticShapeObjectType; + + mReceiveBuffers.setToDefault(); + + mBendingEnabled = false; + mDampingEnabled = false; + mTriangleCollisionEnabled = false; + mSelfCollisionEnabled = false; + + mDensity = 1.0f; + mThickness = 0.1f; + mFriction = 0.25f; + mBendingStiffness = 0.5f; + mDampingCoefficient = 0.25f; + + mAttachmentMask = 0; +} + +PxCloth::~PxCloth() +{ +} + +bool PxCloth::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + // Cloth is only created on the client. + if ( isClientObject() ) + { + mWorld = dynamic_cast( PHYSICSMGR->getWorld( "client" ) ); + + if ( !mWorld || !mWorld->getScene() ) + { + Con::errorf( "PxCloth::onAdd() - PhysXWorld not initialized... cloth disabled!" ); + return true; + } + + mScene = mWorld->getScene(); + + mResetXfm = getTransform(); + + _createClothPatch(); + + PhysicsPlugin::getPhysicsResetSignal().notify( this, &PxCloth::onPhysicsReset, 1053.0f ); + } + + // On the server we use the static update + // to setup the bounds of the cloth. + if ( isServerObject() ) + _updateStaticCloth(); + + addToScene(); + + // Also the server object never ticks. + if ( isServerObject() ) + setProcessTick( false ); + + return true; +} + +void PxCloth::onRemove() +{ + SAFE_DELETE( mMatInst ); + + if ( isClientObject() ) + { + _releaseCloth(); + _releaseMesh(); + + PhysicsPlugin::getPhysicsResetSignal().remove( this, &PxCloth::onPhysicsReset ); + } + + removeFromScene(); + + Parent::onRemove(); +} + +void PxCloth::onPhysicsReset( PhysicsResetEvent reset ) +{ + // Store the reset transform for later use. + if ( reset == PhysicsResetEvent_Store ) + mResetXfm = getTransform(); + + // Recreate the cloth at the last reset position. + _recreateCloth( mResetXfm ); +} + +void PxCloth::initPersistFields() +{ + Parent::initPersistFields(); + + addField( "material", TypeMaterialName, Offset( mMaterialName, PxCloth ), + "@brief Name of the material to render.\n\n" ); + + addField( "samples", TypePoint2I, Offset( mPatchVerts, PxCloth ), + "@brief The number of cloth vertices in width and length.\n\n" + "At least two verts should be defined.\n\n"); + + addField( "size", TypePoint2F, Offset( mPatchSize, PxCloth ), + "@brief The width and height of the cloth.\n\n" ); + + addField( "bending", TypeBool, Offset( mBendingEnabled, PxCloth ), + "@brief Enables or disables bending resistance.\n\n" + "Set the bending resistance through PxCloth::bendingStiffness." ); + + addField( "damping", TypeBool, Offset( mDampingEnabled, PxCloth ), + "@brief Enable/disable damping of internal velocities.\n\n" ); + + addField( "triangleCollision", TypeBool, Offset( mTriangleCollisionEnabled, PxCloth ), + "@brief Not supported in current release (according to PhysX docs).\n\n" + "Enables or disables collision detection of cloth triangles against the scene. " + "If not set, only collisions of cloth particles are detected. If set, " + "collisions of cloth triangles are detected as well." ); + + addField( "selfCollision", TypeBool, Offset( mSelfCollisionEnabled, PxCloth ), + "@brief Enables or disables self-collision handling within a single piece of cloth.\n\n" ); + + addField( "density", TypeF32, Offset( mDensity, PxCloth ), + "@brief Density of the cloth (Mass per Area).\n\n" ); + + addField( "thickness", TypeF32, Offset( mThickness, PxCloth ), + "@brief Value representing how thick the cloth is.\n\n" + "The thickness is usually a fraction of the overall extent of the cloth and " + "should not be set to a value greater than that. A good value is the maximal " + "distance between two adjacent cloth particles in their rest pose. Visual " + "artifacts or collision problems may appear if the thickness is too small.\n\n" ); + + addField( "friction", TypeF32, Offset( mFriction, PxCloth ), + "@brief Friction coefficient in the range 0 to 1.\n\n" + "Defines the damping of the velocities of cloth particles that are in contact." ); + + addField( "bendingStiffness", TypeF32, Offset( mBendingStiffness, PxCloth ), + "@brief Bending stiffness of the cloth in the range 0 to 1.\n\n" ); + + addField( "dampingCoefficient", TypeF32, Offset( mDampingCoefficient, PxCloth ), + "@brief Spring damping of the cloth in the range 0 to 1.\n\n" ); + + addField( "attachments", TYPEID< PxClothAttachment >(), Offset( mAttachmentMask, PxCloth ), + "@brief Optional way to specify cloth verts that will be attached to the world position " + "it is created at.\n\n" ); + + // Cloth doesn't support scale. + removeField( "scale" ); +} + +void PxCloth::inspectPostApply() +{ + Parent::inspectPostApply(); + + // Must have at least 2 verts. + mPatchVerts.x = getMax( 2, mPatchVerts.x ); + mPatchVerts.y = getMax( 2, mPatchVerts.y ); + if ( isServerObject() ) + _updateStaticCloth(); + + setMaskBits( TransformMask | MaterialMask | ClothMask ); +} + +U32 PxCloth::packUpdate( NetConnection *conn, U32 mask, BitStream *stream ) +{ + U32 retMask = Parent::packUpdate( conn, mask, stream ); + + if ( stream->writeFlag( mask & TransformMask ) ) + mathWrite( *stream, getTransform() ); + + if ( stream->writeFlag( mask & MaterialMask ) ) + stream->write( mMaterialName ); + + if ( stream->writeFlag( mask & ClothMask ) ) + { + mathWrite( *stream, mPatchVerts ); + mathWrite( *stream, mPatchSize ); + + stream->write( mAttachmentMask ); + + stream->writeFlag( mBendingEnabled ); + stream->writeFlag( mDampingEnabled ); + stream->writeFlag( mTriangleCollisionEnabled ); + stream->writeFlag( mSelfCollisionEnabled ); + stream->write( mThickness ); + stream->write( mFriction ); + stream->write( mBendingStiffness ); + stream->write( mDampingCoefficient ); + + stream->write( mDensity ); + } + + return retMask; +} + +void PxCloth::unpackUpdate( NetConnection *conn, BitStream *stream ) +{ + Parent::unpackUpdate( conn, stream ); + + // TransformMask + if ( stream->readFlag() ) + { + MatrixF mat; + mathRead( *stream, &mat ); + setTransform( mat ); + } + + // MaterialMask + if ( stream->readFlag() ) + { + stream->read( &mMaterialName ); + SAFE_DELETE( mMatInst ); + } + + // ClothMask + if ( stream->readFlag() ) + { + Point2I patchVerts; + Point2F patchSize; + mathRead( *stream, &patchVerts ); + mathRead( *stream, &patchSize ); + + if ( patchVerts != mPatchVerts || + !patchSize.equal( mPatchSize ) ) + { + mPatchVerts = patchVerts; + mPatchSize = patchSize; + _releaseMesh(); + } + + U32 attachMask; + stream->read( &attachMask ); + if ( attachMask != mAttachmentMask ) + { + mAttachmentMask = attachMask; + _releaseCloth(); + } + + mBendingEnabled = stream->readFlag(); + mDampingEnabled = stream->readFlag(); + mTriangleCollisionEnabled = stream->readFlag(); + mSelfCollisionEnabled = stream->readFlag(); + stream->read( &mThickness ); + stream->read( &mFriction ); + stream->read( &mBendingStiffness ); + stream->read( &mDampingCoefficient ); + + F32 density; + stream->read( &density ); + if ( density != mDensity ) + { + mDensity = density; + _releaseCloth(); + } + + if ( isClientObject() && + isProperlyAdded() && + mWorld && + !mCloth ) + { + _createClothPatch(); + } + + _updateClothProperties(); + } +} + +void PxCloth::_recreateCloth( const MatrixF &transform ) +{ + if ( !mWorld ) + return; + + mWorld->getPhysicsResults(); + + Parent::setTransform( transform ); + + _createClothPatch(); +} + +void PxCloth::setTransform( const MatrixF &mat ) +{ + Parent::setTransform( mat ); + setMaskBits( TransformMask ); + + // Only need to do this if we're on the server + // or if we're not currently ticking physics. + if ( !mWorld || !mWorld->isEnabled() ) + _updateStaticCloth(); +} + +void PxCloth::setScale( const VectorF &scale ) +{ + // Cloth doesn't support scale as it has plenty + // of complications... sharing meshes, thickness, + // transform origin, etc. + return; +} + +void PxCloth::prepRenderImage( SceneRenderState *state ) +{ + if ( mIsVBDirty ) + _updateVBIB(); + + // Recreate the material if we need to. + if ( !mMatInst ) + _initMaterial(); + + // If we don't have a material instance after the override then + // we can skip rendering all together. + BaseMatInstance *matInst = state->getOverrideMaterial( mMatInst ); + if ( !matInst ) + return; + + MeshRenderInst *ri = state->getRenderPass()->allocInst(); + + // If we need lights then set them up. + if ( matInst->isForwardLit() ) + { + LightQuery query; + query.init( getWorldSphere() ); + query.getLights( ri->lights, 8 ); + } + + ri->projection = state->getRenderPass()->allocSharedXform(RenderPassManager::Projection); + ri->objectToWorld = &MatrixF::Identity; + + ri->worldToCamera = state->getRenderPass()->allocSharedXform(RenderPassManager::View); + ri->type = RenderPassManager::RIT_Mesh; + + ri->primBuff = &mPrimBuffer; + ri->vertBuff = &mVB; + + ri->matInst = matInst; + ri->prim = state->getRenderPass()->allocPrim(); + ri->prim->type = GFXTriangleList; + ri->prim->minIndex = 0; + ri->prim->startIndex = 0; + ri->prim->numPrimitives = mNumIndices / 3; + + ri->prim->startVertex = 0; + ri->prim->numVertices = mNumVertices; + + ri->defaultKey = matInst->getStateHint(); + ri->defaultKey2 = (U32)ri->vertBuff; + + state->getRenderPass()->addInst( ri ); +} + +void PxCloth::_releaseMesh() +{ + if ( !mClothMesh ) + return; + + _releaseCloth(); + + mWorld->releaseClothMesh( *mClothMesh ); + mClothMesh = NULL; + + delete [] mVertexRenderBuffer; + mVertexRenderBuffer = NULL; + delete [] mIndexRenderBuffer; + mIndexRenderBuffer = NULL; +} + +void PxCloth::_releaseCloth() +{ + if ( !mCloth ) + return; + + mWorld->releaseCloth( *mCloth ); + mCloth = NULL; +} + +void PxCloth::_initClothMesh() +{ + // Make sure we can change the world. + mWorld->releaseWriteLock(); + + _releaseMesh(); + + // Must have at least 2 verts. + mPatchVerts.x = getMax( 2, mPatchVerts.x ); + mPatchVerts.y = getMax( 2, mPatchVerts.y ); + + // Generate a uniform cloth patch, + // w and h are the width and height, + // d is the distance between vertices. + mNumVertices = mPatchVerts.x * mPatchVerts.y; + mNumIndices = (mPatchVerts.x-1) * (mPatchVerts.y-1) * 2; + + NxClothMeshDesc desc; + desc.numVertices = mNumVertices; + desc.numTriangles = mNumIndices; + desc.pointStrideBytes = sizeof(NxVec3); + desc.triangleStrideBytes = 3*sizeof(NxU32); + desc.points = (NxVec3*)dMalloc(sizeof(NxVec3)*desc.numVertices); + desc.triangles = (NxU32*)dMalloc(sizeof(NxU32)*desc.numTriangles*3); + desc.flags = 0; + + U32 i,j; + NxVec3 *p = (NxVec3*)desc.points; + + F32 patchWidth = mPatchSize.x / (F32)( mPatchVerts.x - 1 ); + F32 patchHeight = mPatchSize.y / (F32)( mPatchVerts.y - 1 ); + + for (i = 0; i < mPatchVerts.y; i++) + { + for (j = 0; j < mPatchVerts.x; j++) + { + p->set( patchWidth * j, 0.0f, patchHeight * i ); + p++; + } + } + + NxU32 *id = (NxU32*)desc.triangles; + + for (i = 0; i < mPatchVerts.y-1; i++) + { + for (j = 0; j < mPatchVerts.x-1; j++) + { + NxU32 i0 = i * mPatchVerts.x + j; + NxU32 i1 = i0 + 1; + NxU32 i2 = i0 + mPatchVerts.x; + NxU32 i3 = i2 + 1; + if ( (j+i) % 2 ) + { + *id++ = i0; + *id++ = i2; + *id++ = i1; + *id++ = i1; + *id++ = i2; + *id++ = i3; + } + else + { + *id++ = i0; + *id++ = i2; + *id++ = i3; + *id++ = i0; + *id++ = i3; + *id++ = i1; + } + } + } + + NxCookingInterface *cooker = PxWorld::getCooking(); + cooker->NxInitCooking(); + + // Ok... cook the mesh! + NxCookingParams params; + params.targetPlatform = PLATFORM_PC; + params.skinWidth = 0.01f; + params.hintCollisionSpeed = false; + + cooker->NxSetCookingParams( params ); + + PxMemStream cooked; + + if ( cooker->NxCookClothMesh( desc, cooked ) ) + { + cooked.resetPosition(); + mClothMesh = gPhysicsSDK->createClothMesh( cooked ); + } + + cooker->NxCloseCooking(); + + NxVec3 *ppoints = (NxVec3*)desc.points; + NxU32 *triangs = (NxU32*)desc.triangles; + + dFree( ppoints ); + dFree( triangs ); + + if ( mClothMesh ) + _initReceiveBuffers(); +} + +void PxCloth::_initReceiveBuffers() +{ + // here we setup the buffers through which the SDK returns the dynamic cloth data + // we reserve more memory for vertices than the initial mesh takes + // because tearing creates new vertices + // the SDK only tears cloth as long as there is room in these buffers + + mMaxVertices = 3 * mNumVertices; + mMaxIndices = 3 * mNumIndices; + + // Allocate Render Buffer for Vertices if it hasn't been done before + mVertexRenderBuffer = new GFXVertexPNTT[mMaxVertices]; + mIndexRenderBuffer = new U16[mMaxIndices]; + + mReceiveBuffers.verticesPosBegin = &(mVertexRenderBuffer[0].point); + mReceiveBuffers.verticesNormalBegin = &(mVertexRenderBuffer[0].normal); + mReceiveBuffers.verticesPosByteStride = sizeof(GFXVertexPNTT); + mReceiveBuffers.verticesNormalByteStride = sizeof(GFXVertexPNTT); + mReceiveBuffers.maxVertices = mMaxVertices; + mReceiveBuffers.numVerticesPtr = &mNumVertices; + + // the number of triangles is constant, even if the cloth is torn + mReceiveBuffers.indicesBegin = &mIndexRenderBuffer[0]; + mReceiveBuffers.indicesByteStride = sizeof(NxU16); + mReceiveBuffers.maxIndices = mMaxIndices; + mReceiveBuffers.numIndicesPtr = &mNumIndices; + + // Set up texture coords. + + F32 dx = 1.0f / (F32)(mPatchVerts.x-1); + F32 dy = 1.0f / (F32)(mPatchVerts.y-1); + + F32 *coord = (F32*)&mVertexRenderBuffer[0].texCoord; + for ( U32 i = 0; i < mPatchVerts.y; i++) + { + for ( U32 j = 0; j < mPatchVerts.x; j++) + { + coord[0] = j*dx; + coord[1] = i*-dy; + coord += sizeof( GFXVertexPNTT ) / sizeof( F32 ); + } + } + + // the parent index information would be needed if we used textured cloth + //mReceiveBuffers.parentIndicesBegin = (U32*)malloc(sizeof(U32)*mMaxVertices); + //mReceiveBuffers.parentIndicesByteStride = sizeof(U32); + //mReceiveBuffers.maxParentIndices = mMaxVertices; + //mReceiveBuffers.numParentIndicesPtr = &mNumParentIndices; + + mMeshDirtyFlags = 0; + mReceiveBuffers.dirtyBufferFlagsPtr = &mMeshDirtyFlags; + + // init the buffers in case we want to draw the mesh + // before the SDK as filled in the correct values + + mReceiveBuffers.flags |= NX_MDF_16_BIT_INDICES; +} + +bool PxCloth::_createClothPatch() +{ + // Make sure we have a mesh. + if ( !mClothMesh ) + { + _initClothMesh(); + if ( !mClothMesh ) + return false; + } + + // Make sure we can change the world. + mWorld->releaseWriteLock(); + + _releaseCloth(); + + NxClothDesc desc; + desc.globalPose.setRowMajor44( getTransform() ); + desc.thickness = mThickness; + desc.density = mDensity; + desc.bendingStiffness = mBendingStiffness; + desc.dampingCoefficient = mDampingCoefficient; + desc.friction = mFriction; + + if ( mBendingEnabled ) + desc.flags |= NX_CLF_BENDING; + if ( mDampingEnabled ) + desc.flags |= NX_CLF_DAMPING; + if ( mTriangleCollisionEnabled ) + desc.flags |= NX_CLF_TRIANGLE_COLLISION; + if ( mSelfCollisionEnabled ) + desc.flags |= NX_CLF_SELFCOLLISION; + + desc.clothMesh = mClothMesh; + desc.meshData = mReceiveBuffers; + + if ( !desc.isValid() ) + return false; + + mCloth = mScene->createCloth( desc ); + mIsVBDirty = true; + + _updateStaticCloth(); + _setupAttachments(); + + return true; +} + +void PxCloth::_updateClothProperties() +{ + if ( !mCloth ) + return; + + mCloth->setThickness( mThickness ); + mCloth->setBendingStiffness( mBendingStiffness ); + mCloth->setDampingCoefficient( mDampingCoefficient ); + mCloth->setFriction( mFriction ); + + NxU32 flags = NX_CLF_GRAVITY; // TODO: Expose this? + if ( mBendingEnabled ) + flags |= NX_CLF_BENDING; + if ( mDampingEnabled ) + flags |= NX_CLF_DAMPING; + if ( mTriangleCollisionEnabled ) + flags |= NX_CLF_TRIANGLE_COLLISION; + if ( mSelfCollisionEnabled ) + flags |= NX_CLF_SELFCOLLISION; + mCloth->setFlags( flags ); +} + +void PxCloth::_initMaterial() +{ + SAFE_DELETE( mMatInst ); + + Material *material = NULL; + if (mMaterialName.isNotEmpty() ) + Sim::findObject( mMaterialName, material ); + + if ( material ) + mMatInst = material->createMatInstance(); + else + mMatInst = MATMGR->createMatInstance( "WarningMaterial" ); + + GFXStateBlockDesc desc; + desc.setCullMode( GFXCullNone ); + mMatInst->addStateBlockDesc( desc ); + + mMatInst->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat() ); +} + +void PxCloth::_updateVBIB() +{ + PROFILE_SCOPE( PxCloth_UpdateVBIB ); + + mIsVBDirty = false; + + // Don't set the VB if the vertex count is the same! + if ( mVB.isNull() || mVB->mNumVerts < mNumVertices ) + mVB.set( GFX, mNumVertices, GFXBufferTypeDynamic ); + + GFXVertexPNTT *vert = mVertexRenderBuffer; + GFXVertexPNTT *secondVert = NULL; + + for ( U32 i = 0; i < mNumVertices; i++ ) + { + if ( i % (U32)mPatchSize.x == 0 && i != 0 ) + { + secondVert = vert; + secondVert--; + vert->tangent = -(vert->point - secondVert->point); + } + else + { + secondVert = vert; + secondVert++; + vert->tangent = vert->point - secondVert->point; + } + + vert->tangent.normalize(); + vert++; + } + + GFXVertexPNTT *vpPtr = mVB.lock(); + dMemcpy( vpPtr, mVertexRenderBuffer, sizeof( GFXVertexPNTT ) * mNumVertices ); + mVB.unlock(); + + if ( mPrimBuffer.isNull() || mPrimBuffer->mIndexCount < mNumIndices ) + mPrimBuffer.set( GFX, mNumIndices, 0, GFXBufferTypeDynamic ); + + U16 *pbPtr; + mPrimBuffer.lock( &pbPtr ); + dMemcpy( pbPtr, mIndexRenderBuffer, sizeof( U16 ) * mNumIndices ); + mPrimBuffer.unlock(); +} + +void PxCloth::_updateStaticCloth() +{ + // Setup the unsimulated world bounds. + mObjBox.set( 0, mThickness * -0.5f, 0, + mPatchSize.x, mThickness * 0.5f, mPatchSize.y ); + resetWorldBox(); + + // If we don't have render buffers then we're done. + if ( !mVertexRenderBuffer || !mIndexRenderBuffer ) + return; + + // Make sure the VBs are updated. + mIsVBDirty = true; + + F32 patchWidth = mPatchSize.x / (F32)(mPatchVerts.x-1); + F32 patchHeight = mPatchSize.y / (F32)(mPatchVerts.y-1); + + Point3F normal( 0, 1, 0 ); + getTransform().mulV( normal ); + + GFXVertexPNTT *vert = mVertexRenderBuffer; + + for (U32 y = 0; y < mPatchVerts.y; y++) + { + for (U32 x = 0; x < mPatchVerts.x; x++) + { + vert->point.set( patchWidth * x, 0.0f, patchHeight * y ); + getTransform().mulP( vert->point ); + vert->normal = normal; + vert++; + } + } + + U16 *index = mIndexRenderBuffer; + mNumIndices = (mPatchVerts.x-1) * (mPatchVerts.y-1) * 6; + U16 yOffset = mPatchVerts.x; + + for (U32 y = 0; y < mPatchVerts.y-1; y++) + { + for (U32 x = 0; x < mPatchVerts.x-1; x++) + { + U16 base = x + ( yOffset * y ); + + index[0] = base; + index[1] = base + 1; + index[2] = base + 1 + yOffset; + + index[3] = base + 1 + yOffset; + index[4] = base + yOffset; + index[5] = base; + + index += 6; + } + } +} + +void PxCloth::processTick( const Move *move ) +{ + // Make sure the cloth is created. + if ( !mCloth ) + return; + + // TODO: Remove this hack! + const bool enableWind = Con::getBoolVariable( "$PxCloth::enableWind", false ); + + if ( enableWind ) + { + NxVec3 windVec( 25.0f + NxMath::rand(-5.0f, 5.0f), + NxMath::rand(-5.0f, 5.0f), + NxMath::rand(-5.0f, 5.0f) ); + + mCloth->setWindAcceleration( windVec ); + + // Wake the cloth! + mCloth->wakeUp(); + } + else + mCloth->setWindAcceleration( NxVec3( 0, 0, 0 ) ); + + // Update bounds. + if ( mWorld->getEnabled() ) + { + NxBounds3 box; + mCloth->getWorldBounds( box ); + + Point3F min = pxCast( box.min ); + Point3F max = pxCast( box.max ); + + mWorldBox.set( min, max ); + mObjBox = mWorldBox; + + getWorldTransform().mul( mObjBox ); + } + else + { + mObjBox.set( 0, mThickness * -0.5f, 0, + mPatchSize.x, mThickness * 0.5f, mPatchSize.y ); + } + + resetWorldBox(); + + // Update the VB on the next render. + mIsVBDirty = true; +} + +void PxCloth::interpolateTick( F32 delta ) +{ + // Nothing to do for now! +} + +bool PxCloth::onNewDataBlock( GameBaseData *dptr, bool reload ) +{ + return false; +} + +void PxCloth::_setupAttachments() +{ + if ( !mCloth || !mWorld ) + return; + + // Set up attachments + // Bottom right = bit 0 + // Bottom left = bit 1 + // Top right = bit 2 + // Top left = bit 3 + + if ( mAttachmentMask & BIT( 0 ) ) + mCloth->attachVertexToGlobalPosition( 0, mCloth->getPosition( 0 ) ); + if ( mAttachmentMask & BIT( 1 ) ) + mCloth->attachVertexToGlobalPosition( mPatchVerts.x-1, mCloth->getPosition( mPatchVerts.x-1 ) ); + if ( mAttachmentMask & BIT( 2 ) ) + mCloth->attachVertexToGlobalPosition( mPatchVerts.x * mPatchVerts.y - mPatchVerts.x, mCloth->getPosition( mPatchVerts.x * mPatchVerts.y - mPatchVerts.x ) ); + if ( mAttachmentMask & BIT( 3 ) ) + mCloth->attachVertexToGlobalPosition( mPatchVerts.x * mPatchVerts.y - 1, mCloth->getPosition( mPatchVerts.x * mPatchVerts.y - 1 ) ); + if ( mAttachmentMask & BIT( 4 ) ) + mCloth->attachVertexToGlobalPosition( mPatchVerts.x * mPatchVerts.y - (mPatchVerts.x/2), mCloth->getPosition( mPatchVerts.x * mPatchVerts.y - (mPatchVerts.x/2) ) ); + if ( mAttachmentMask & BIT( 5 ) ) + mCloth->attachVertexToGlobalPosition( (mPatchVerts.x/2), mCloth->getPosition( (mPatchVerts.x/2) ) ); + if ( mAttachmentMask & BIT( 6 ) ) + mCloth->attachVertexToGlobalPosition( mPatchVerts.x * (mPatchVerts.y/2), mCloth->getPosition( mPatchVerts.x * (mPatchVerts.y/2) ) ); + if ( mAttachmentMask & BIT( 7 ) ) + mCloth->attachVertexToGlobalPosition( mPatchVerts.x * (mPatchVerts.y/2) + (mPatchVerts.x-1), mCloth->getPosition( mPatchVerts.x * (mPatchVerts.y/2) + (mPatchVerts.x-1) ) ); + + if ( mAttachmentMask & BIT( 8 ) ) + for ( U32 i = mPatchVerts.x * mPatchVerts.y - mPatchVerts.x; i < mPatchVerts.x * mPatchVerts.y; i++ ) + mCloth->attachVertexToGlobalPosition( i, mCloth->getPosition( i ) ); + + if ( mAttachmentMask & BIT( 9 ) ) + for ( U32 i = 0; i < mPatchVerts.x; i++ ) + mCloth->attachVertexToGlobalPosition( i, mCloth->getPosition( i ) ); + + if ( mAttachmentMask & BIT( 10 ) ) + for ( U32 i = 0; i < mPatchVerts.x * mPatchVerts.y; i+=mPatchVerts.x ) + mCloth->attachVertexToGlobalPosition( i, mCloth->getPosition( i ) ); + + if ( mAttachmentMask & BIT( 11 ) ) + for ( U32 i = mPatchVerts.x-1; i < mPatchVerts.x * mPatchVerts.y; i+=mPatchVerts.x ) + mCloth->attachVertexToGlobalPosition( i, mCloth->getPosition( i ) ); +} \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx/pxCloth.h b/Engine/source/T3D/physics/physx/pxCloth.h new file mode 100644 index 000000000..5df158861 --- /dev/null +++ b/Engine/source/T3D/physics/physx/pxCloth.h @@ -0,0 +1,176 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PXCLOTH_H_ +#define _PXCLOTH_H_ + +#ifndef _GAMEBASE_H_ +#include "T3D/gameBase/gameBase.h" +#endif +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif +#ifndef _GFXVERTEXBUFFER_H_ +#include "gfx/gfxVertexBuffer.h" +#endif +#ifndef _PHYSX_H_ +#include "T3D/physics/physx/px.h" +#endif +#ifndef _T3D_PHYSICS_PHYSICSPLUGIN_H_ +#include "T3D/physics/physicsPlugin.h" +#endif + +class Material; +class BaseMatInstance; +class PxWorld; +class NxScene; +class NxClothMesh; +class NxCloth; + + +class PxCloth : public GameBase +{ + typedef GameBase Parent; + + enum MaskBits + { + TransformMask = Parent::NextFreeMask << 0, + ClothMask = Parent::NextFreeMask << 1, + MaterialMask = Parent::NextFreeMask << 3, + NextFreeMask = Parent::NextFreeMask << 4 + }; + +public: + + PxCloth(); + virtual ~PxCloth(); + + DECLARE_CONOBJECT( PxCloth ); + + // SimObject + virtual bool onAdd(); + virtual void onRemove(); + static void initPersistFields(); + virtual void inspectPostApply(); + void onPhysicsReset( PhysicsResetEvent reset ); + + // NetObject + virtual U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream ); + virtual void unpackUpdate( NetConnection *conn, BitStream *stream ); + + // SceneObject + virtual void setTransform( const MatrixF &mat ); + virtual void setScale( const VectorF &scale ); + virtual void prepRenderImage( SceneRenderState *state ); + + // GameBase + virtual bool onNewDataBlock( GameBaseData *dptr, bool reload ); + virtual void processTick( const Move *move ); + virtual void interpolateTick( F32 delta ); + +protected: + + PxWorld *mWorld; + + NxScene *mScene; + + /// Cooked cloth collision mesh. + NxClothMesh *mClothMesh; + + /// The cloth actor used + NxCloth *mCloth; + + NxMeshData mReceiveBuffers; + + bool mBendingEnabled; + bool mDampingEnabled; + bool mTriangleCollisionEnabled; + bool mSelfCollisionEnabled; + + F32 mDensity; + F32 mThickness; + F32 mFriction; + F32 mBendingStiffness; + F32 mStretchingStiffness; + F32 mDampingCoefficient; + F32 mCollisionResponseCoefficient; + F32 mAttachmentResponseCoefficient; + + U32 mAttachmentMask; + + static EnumTable mAttachmentFlagTable; + + String mMaterialName; + SimObjectPtr mMaterial; + BaseMatInstance *mMatInst; + + String lookupName; + + /// The output verts from the PhysX simulation. + GFXVertexPNTT *mVertexRenderBuffer; + + /// The output indices from the PhysX simulation. + U16 *mIndexRenderBuffer; + + U32 mMaxVertices; + U32 mMaxIndices; + + /// The number of indices in the cloth which + /// is updated by the PhysX simulation. + U32 mNumIndices; + + /// The number of verts in the cloth which + /// is updated by the PhysX simulation. + U32 mNumVertices; + + U32 mMeshDirtyFlags; + bool mIsVBDirty; + + GFXPrimitiveBufferHandle mPrimBuffer; + GFXVertexBufferHandle mVB; + + Point2I mPatchVerts; + Point2F mPatchSize; + + MatrixF mResetXfm; + + void _initMaterial(); + + void _releaseMesh(); + void _releaseCloth(); + + bool _createClothPatch(); + + void _recreateCloth( const MatrixF &transform ); + + void _updateClothProperties(); + + void _initClothMesh(); + void _initReceiveBuffers(); + void _setupAttachments(); + + void _updateStaticCloth(); + + void _updateVBIB(); +}; + +#endif // _PXCLOTH_H_ \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx/pxCollision.cpp b/Engine/source/T3D/physics/physx/pxCollision.cpp new file mode 100644 index 000000000..b08636d64 --- /dev/null +++ b/Engine/source/T3D/physics/physx/pxCollision.cpp @@ -0,0 +1,291 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physX/pxCollision.h" + +#include "math/mPoint3.h" +#include "math/mMatrix.h" +#include "T3D/physics/physX/px.h" +#include "T3D/physics/physX/pxCasts.h" +#include "T3D/physics/physX/pxWorld.h" +#include "T3D/physics/physX/pxStream.h" + + +PxCollision::PxCollision() +{ +} + +PxCollision::~PxCollision() +{ + // We may be deleteting SDK data... so make + // sure we have the the scene write lock. + PxWorld::releaseWriteLocks(); + + for ( U32 i=0; i < mColShapes.size(); i++ ) + { + // Check for special types which need cleanup. + NxShapeDesc *desc = mColShapes[i]; + + if ( desc->getType() == NX_SHAPE_CONVEX ) + gPhysicsSDK->releaseConvexMesh( *((NxConvexShapeDesc*)desc)->meshData ); + else if ( desc->getType() == NX_SHAPE_MESH ) + gPhysicsSDK->releaseTriangleMesh( *((NxTriangleMeshShapeDesc*)desc)->meshData ); + else if ( desc->getType() == NX_SHAPE_HEIGHTFIELD ) + gPhysicsSDK->releaseHeightField( *((NxHeightFieldShapeDesc*)desc)->heightField ); + + // Delete the descriptor. + delete desc; + } + + mColShapes.clear(); +} + +void PxCollision::addPlane( const PlaneF &plane ) +{ + NxBoxShapeDesc *desc = new NxBoxShapeDesc; + desc->skinWidth = 0.01f; + desc->dimensions.set( 10000.0f, 10000.0f, 100.0f ); + desc->localPose.t.z = -100.0f; + + // TODO: Fix rotation to match plane normal! + //boxDesc->localPose.M.setColumn( 0, NxVec3( plane.x, plane.y, plane.z ) ); + //boxDesc->localPose.M.setColumn( 1, NxVec3( plane.x, plane.y, plane.z ) ); + //boxDesc->localPose.M.setColumn( 2, NxVec3( plane.x, plane.y, plane.z ) ); + + mColShapes.push_back( desc ); +} + +void PxCollision::addBox( const Point3F &halfWidth, + const MatrixF &localXfm ) +{ + NxBoxShapeDesc *desc = new NxBoxShapeDesc; + desc->skinWidth = 0.01f; + desc->dimensions.set( halfWidth.x, halfWidth.y, halfWidth.z ); + desc->localPose.setRowMajor44( localXfm ); + mColShapes.push_back( desc ); +} + +void PxCollision::addSphere( F32 radius, + const MatrixF &localXfm ) +{ + NxSphereShapeDesc *desc = new NxSphereShapeDesc; + desc->skinWidth = 0.01f; + desc->radius = radius; + desc->localPose.setRowMajor44( localXfm ); + mColShapes.push_back( desc ); +} + +void PxCollision::addCapsule( F32 radius, + F32 height, + const MatrixF &localXfm ) +{ + NxCapsuleShapeDesc *desc = new NxCapsuleShapeDesc; + desc->skinWidth = 0.01f; + desc->radius = radius; + desc->height = height; + desc->localPose.setRowMajor44( localXfm ); + mColShapes.push_back( desc ); +} + +bool PxCollision::addConvex( const Point3F *points, + U32 count, + const MatrixF &localXfm ) +{ + // Mesh cooking requires that both + // scenes not be write locked! + PxWorld::releaseWriteLocks(); + + NxCookingInterface *cooker = PxWorld::getCooking(); + cooker->NxInitCooking(); + + NxConvexMeshDesc meshDesc; + meshDesc.numVertices = count; + meshDesc.pointStrideBytes = sizeof(Point3F); + meshDesc.points = points; + meshDesc.flags = NX_CF_COMPUTE_CONVEX | NX_CF_INFLATE_CONVEX; + + // Cook it! + NxCookingParams params; + #ifdef TORQUE_OS_XENON + params.targetPlatform = PLATFORM_XENON; + #else + params.targetPlatform = PLATFORM_PC; + #endif + params.skinWidth = 0.01f; + params.hintCollisionSpeed = true; + cooker->NxSetCookingParams( params ); + + PxMemStream stream; + bool cooked = cooker->NxCookConvexMesh( meshDesc, stream ); + cooker->NxCloseCooking(); + + if ( !cooked ) + return false; + + stream.resetPosition(); + NxConvexMesh *meshData = gPhysicsSDK->createConvexMesh( stream ); + if ( !meshData ) + return false; + + NxConvexShapeDesc *desc = new NxConvexShapeDesc; + desc->skinWidth = 0.01f; + desc->meshData = meshData; + desc->localPose.setRowMajor44( localXfm ); + mColShapes.push_back( desc ); + + return true; +} + +bool PxCollision::addTriangleMesh( const Point3F *vert, + U32 vertCount, + const U32 *index, + U32 triCount, + const MatrixF &localXfm ) +{ + // Mesh cooking requires that both + // scenes not be write locked! + PxWorld::releaseWriteLocks(); + + NxCookingInterface *cooker = PxWorld::getCooking(); + cooker->NxInitCooking(); + + NxTriangleMeshDesc meshDesc; + meshDesc.numVertices = vertCount; + meshDesc.numTriangles = triCount; + meshDesc.pointStrideBytes = sizeof(Point3F); + meshDesc.triangleStrideBytes = 3*sizeof(U32); + meshDesc.points = vert; + meshDesc.triangles = index; + meshDesc.flags = NX_MF_FLIPNORMALS; + + // Cook it! + NxCookingParams params; + #ifdef TORQUE_OS_XENON + params.targetPlatform = PLATFORM_XENON; + #else + params.targetPlatform = PLATFORM_PC; + #endif + params.skinWidth = 0.01f; + params.hintCollisionSpeed = true; + cooker->NxSetCookingParams( params ); + + PxMemStream stream; + bool cooked = cooker->NxCookTriangleMesh( meshDesc, stream ); + cooker->NxCloseCooking(); + if ( !cooked ) + return false; + + stream.resetPosition(); + NxTriangleMesh *meshData = gPhysicsSDK->createTriangleMesh( stream ); + if ( !meshData ) + return false; + + NxTriangleMeshShapeDesc *desc = new NxTriangleMeshShapeDesc; + desc->skinWidth = 0.01f; + desc->meshData = meshData; + desc->localPose.setRowMajor44( localXfm ); + mColShapes.push_back( desc ); + + return true; +} + +bool PxCollision::addHeightfield( const U16 *heights, + const bool *holes, + U32 blockSize, + F32 metersPerSample, + const MatrixF &localXfm ) +{ + // Since we're creating SDK level data we + // have to have access to all active worlds. + PxWorld::releaseWriteLocks(); + + // Init the heightfield description. + NxHeightFieldDesc heightFieldDesc; + heightFieldDesc.nbColumns = blockSize; + heightFieldDesc.nbRows = blockSize; + heightFieldDesc.thickness = -10.0f; + heightFieldDesc.convexEdgeThreshold = 0; + + // Allocate the samples. + heightFieldDesc.samples = new NxU32[ blockSize * blockSize ]; + heightFieldDesc.sampleStride = sizeof(NxU32); + NxU8 *currentByte = (NxU8*)heightFieldDesc.samples; + + for ( U32 row = 0; row < blockSize; row++ ) + { + const U32 tess = ( row + 1 ) % 2; + + for ( U32 column = 0; column < blockSize; column++ ) + { + NxHeightFieldSample *currentSample = (NxHeightFieldSample*)currentByte; + + U32 index = ( blockSize - row - 1 ) + ( column * blockSize ); + currentSample->height = heights[ index ]; + + if ( holes && holes[ getMax( (S32)index - 1, 0 ) ] ) // row index for holes adjusted so PhysX collision shape better matches rendered terrain + { + currentSample->materialIndex0 = 0; + currentSample->materialIndex1 = 0; + } + else + { + currentSample->materialIndex0 = 1; //materialIds[0]; + currentSample->materialIndex1 = 1; //materialIds[0]; + } + + currentSample->tessFlag = ( column + tess ) % 2; + + currentByte += heightFieldDesc.sampleStride; + } + } + + // Build it. + NxHeightFieldShapeDesc *desc = new NxHeightFieldShapeDesc; + desc->heightField = gPhysicsSDK->createHeightField( heightFieldDesc ); + + // Destroy the temp sample array. + delete [] heightFieldDesc.samples; + + // TerrainBlock uses a 11.5 fixed point height format + // giving it a maximum height range of 0 to 2048. + desc->heightScale = 0.03125f; + + desc->rowScale = metersPerSample; + desc->columnScale = metersPerSample; + desc->materialIndexHighBits = 0; + desc->skinWidth = 0.01f; + + // Use the local pose to align the heightfield + // to what Torque will expect. + NxMat33 rotX; + rotX.rotX( Float_HalfPi ); + NxMat33 rotZ; + rotZ.rotZ( Float_Pi ); + NxMat34 rot; + rot.M.multiply( rotZ, rotX ); + rot.t.set( ( blockSize - 1 ) * metersPerSample, 0, 0 ); + desc->localPose = rot; + + mColShapes.push_back( desc ); + return true; +} diff --git a/Engine/source/T3D/physics/physx/pxCollision.h b/Engine/source/T3D/physics/physx/pxCollision.h new file mode 100644 index 000000000..54263b792 --- /dev/null +++ b/Engine/source/T3D/physics/physx/pxCollision.h @@ -0,0 +1,78 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _T3D_PHYSICS_PXCOLLISION_H_ +#define _T3D_PHYSICS_PXCOLLISION_H_ + +#ifndef _T3D_PHYSICS_PHYSICSCOLLISION_H_ +#include "T3D/physics/physicsCollision.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +class NxShapeDesc; + + +class PxCollision : public PhysicsCollision +{ +protected: + + /// The collision representation. + Vector mColShapes; + + /// Helper for adding shapes. + //void _addShape( btCollisionShape *shape, const MatrixF &localXfm ); + +public: + + PxCollision(); + virtual ~PxCollision(); + + /// Return the PhysX shape descriptions. + const Vector& getShapes() const { return mColShapes; } + + // PhysicsCollision + virtual void addPlane( const PlaneF &plane ); + virtual void addBox( const Point3F &halfWidth, + const MatrixF &localXfm ); + virtual void addSphere( F32 radius, + const MatrixF &localXfm ); + virtual void addCapsule( F32 radius, + F32 height, + const MatrixF &localXfm ); + virtual bool addConvex( const Point3F *points, + U32 count, + const MatrixF &localXfm ); + virtual bool addTriangleMesh( const Point3F *vert, + U32 vertCount, + const U32 *index, + U32 triCount, + const MatrixF &localXfm ); + virtual bool addHeightfield( const U16 *heights, + const bool *holes, + U32 blockSize, + F32 metersPerSample, + const MatrixF &localXfm ); +}; + +#endif // _T3D_PHYSICS_PXCOLLISION_H_ \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx/pxContactReporter.cpp b/Engine/source/T3D/physics/physx/pxContactReporter.cpp new file mode 100644 index 000000000..33b1c3dab --- /dev/null +++ b/Engine/source/T3D/physics/physx/pxContactReporter.cpp @@ -0,0 +1,108 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physX/pxContactReporter.h" + +#include "T3D/physics/physX/pxCasts.h" +#include "T3D/physics/physicsUserData.h" +#include "T3D/physics/physX/pxMultiActor.h" +#include "platform/profiler.h" + + +PxContactReporter::PxContactReporter() +{ +} + +PxContactReporter::~PxContactReporter() +{ +} + +void PxContactReporter::onContactNotify( NxContactPair &pair, NxU32 events ) +{ + PROFILE_SCOPE( PxContactReporter_OnContactNotify ); + + // For now we only care about start touch events. + if ( !( events & NX_NOTIFY_ON_START_TOUCH ) ) + return; + + // Skip if either actor is deleted. + if ( pair.isDeletedActor[0] || pair.isDeletedActor[1] ) + return; + + NxActor *actor0 = pair.actors[0]; + NxActor *actor1 = pair.actors[1]; + + PhysicsUserData *userData0 = PhysicsUserData::cast( actor0->userData ); + PhysicsUserData *userData1 = PhysicsUserData::cast( actor1->userData ); + + // Early out if we don't have user data or signals to notify. + if ( ( !userData0 || userData0->getContactSignal().isEmpty() ) && + ( !userData1 || userData1->getContactSignal().isEmpty() ) ) + return; + + // Get an average contact point. + U32 points = 0; + NxVec3 hitPoint( 0.0f ); + NxContactStreamIterator iter( pair.stream ); + while( iter.goNextPair() ) + { + while( iter.goNextPatch() ) + { + while( iter.goNextPoint() ) + { + hitPoint += iter.getPoint(); + ++points; + } + } + } + hitPoint /= (F32)points; + + if ( userData0 ) + userData0->getContactSignal().trigger( userData0, + userData1, + pxCast( hitPoint ), + pxCast( pair.sumNormalForce ) ); + + if ( userData1 ) + userData1->getContactSignal().trigger( userData1, + userData0, + pxCast( hitPoint ), + pxCast( -pair.sumNormalForce ) ); +} + +bool PxUserNotify::onJointBreak( NxReal breakingForce, NxJoint &brokenJoint ) +{ + PROFILE_SCOPE( PxUserNotify_OnJointBreak ); + + PxUserData *userData = PxUserData::getData( brokenJoint ); + + if ( userData ) + userData->getOnJointBreakSignal().trigger( breakingForce, brokenJoint ); + + // NOTE: Returning true here will tell the + // PhysX SDK to delete the joint, which will + // cause MANY problems if any of the user app's + // objects still hold references to it. + + return false; +} \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx/pxContactReporter.h b/Engine/source/T3D/physics/physx/pxContactReporter.h new file mode 100644 index 000000000..883d61b9c --- /dev/null +++ b/Engine/source/T3D/physics/physx/pxContactReporter.h @@ -0,0 +1,54 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PXCONTACTREPORTER_H_ +#define _PXCONTACTREPORTER_H_ + +#ifndef _PHYSX_H_ +#include "T3D/physics/physX/px.h" +#endif + + +class PxContactReporter : public NxUserContactReport +{ +protected: + + virtual void onContactNotify( NxContactPair& pair, NxU32 events ); + +public: + + PxContactReporter(); + virtual ~PxContactReporter(); +}; + + + +class PxUserNotify : public NxUserNotify +{ +public: + virtual bool onJointBreak( NxReal breakingForce, NxJoint &brokenJoint ); + virtual void onWake( NxActor **actors, NxU32 count ) {} + virtual void onSleep ( NxActor **actors, NxU32 count ) {} +}; + + +#endif // _PXCONTACTREPORTER_H_ diff --git a/Engine/source/T3D/physics/physx/pxFluid.cpp b/Engine/source/T3D/physics/physx/pxFluid.cpp new file mode 100644 index 000000000..efe5d453b --- /dev/null +++ b/Engine/source/T3D/physics/physx/pxFluid.cpp @@ -0,0 +1,310 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physx/pxFluid.h" + +#include "console/consoleTypes.h" +#include "scene/sceneRenderState.h" +#include "renderInstance/renderPassManager.h" +#include "T3D/physics/physicsPlugin.h" +#include "T3D/physics/physx/pxWorld.h" +#include "T3D/physics/physx/pxCasts.h" +#include "gfx/gfxDrawUtil.h" +#include "math/mathIO.h" +#include "core/stream/bitStream.h" + + +IMPLEMENT_CO_NETOBJECT_V1( PxFluid ); + +ConsoleDocClass( PxFluid, + "@brief Experimental and unfinished Torque wrapper class for NxFluid.\n\n" + "@internal\n" +); + +PxFluid::PxFluid() + : mWorld( NULL ), + mScene( NULL ), + mParticles( NULL ), + mFluid( NULL ), + mEmitter( NULL ), + mParticleCount( 0 ) +{ + mNetFlags.set( Ghostable | ScopeAlways ); + mTypeMask |= StaticObjectType | StaticShapeObjectType; +} + +PxFluid::~PxFluid() +{ + +} + +bool PxFluid::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + mWorld = dynamic_cast( PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" ) ); + + if ( !mWorld || !mWorld->getScene() ) + { + Con::errorf( "PxMultiActor::onAdd() - PhysXWorld not initialized!" ); + return false; + } + + mScene = mWorld->getScene(); + + if ( isClientObject() ) + _createFluid(); + + Point3F halfScale = Point3F::One * 0.5f; + mObjBox.minExtents = -halfScale; + mObjBox.maxExtents = halfScale; + resetWorldBox(); + + addToScene(); + + return true; +} + +void PxFluid::onRemove() +{ + if ( isClientObject() ) + _destroyFluid(); + + removeFromScene(); + + Parent::onRemove(); +} + +void PxFluid::initPersistFields() +{ + Parent::initPersistFields(); +} + +void PxFluid::inspectPostApply() +{ + Parent::inspectPostApply(); + + setMaskBits( UpdateMask ); +} + +U32 PxFluid::packUpdate( NetConnection *conn, U32 mask, BitStream *stream ) +{ + U32 retMask = Parent::packUpdate( conn, mask, stream ); + + if ( stream->writeFlag( mask & UpdateMask ) ) + { + mathWrite( *stream, getTransform() ); + mathWrite( *stream, getScale() ); + + stream->write( mEmitter ? mEmitter->getRate() : 0 ); + } + + stream->writeFlag( isProperlyAdded() && mask & ResetMask ); + + return retMask; +} + +void PxFluid::unpackUpdate( NetConnection *conn, BitStream *stream ) +{ + Parent::unpackUpdate( conn, stream ); + + // UpdateMask + if ( stream->readFlag() ) + { + MatrixF mat; + mathRead( *stream, &mat ); + Point3F scale; + mathRead( *stream, &scale ); + + setScale( scale ); + setTransform( mat ); + + F32 rate; + stream->read( &rate ); + setRate( rate ); + } + + // ResetMask + if ( stream->readFlag() ) + resetParticles(); +} + +void PxFluid::setTransform( const MatrixF &mat ) +{ + Parent::setTransform( mat ); + + if ( mEmitter ) + { + NxMat34 nxMat; + nxMat.setRowMajor44( mat ); + mEmitter->setGlobalPose( nxMat ); + } +} + +void PxFluid::setScale( const VectorF &scale ) +{ + Point3F lastScale = getScale(); + + Point3F halfScale = Point3F::One * 0.5f; + mObjBox.minExtents = -halfScale; + mObjBox.maxExtents = halfScale; + resetWorldBox(); + + Parent::setScale( scale ); + + if ( lastScale != getScale() && + mEmitter ) + { + _destroyFluid(); + _createFluid(); + } +} + +void PxFluid::prepRenderImage( SceneRenderState *state ) +{ + if ( !state->isDiffusePass() ) + return; + + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &PxFluid::renderObject ); + ri->type = RenderPassManager::RIT_Object; + state->getRenderPass()->addInst( ri ); +} + +void PxFluid::resetParticles() +{ + if ( mEmitter ) + mEmitter->resetEmission( MAX_PARTICLES ); + setMaskBits( ResetMask ); +} + +void PxFluid::setRate( F32 rate ) +{ + if ( mEmitter ) + mEmitter->setRate( rate ); + setMaskBits( UpdateMask ); +} + +void PxFluid::renderObject( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ) +{ + GFXStateBlockDesc desc; + desc.setBlend( true ); + desc.setZReadWrite( true, false ); + + for ( U32 i = 0; i < mParticleCount; i++ ) + { + FluidParticle &particle = mParticles[i]; + Point3F pnt = pxCast( particle.position ); + + Box3F box( 0.2f ); + box.minExtents += pnt; + box.maxExtents += pnt; + + GFX->getDrawUtil()->drawCube( desc, box, ColorI::BLUE ); + } +} + +void PxFluid::_createFluid() +{ + /* + // Set structure to pass particles, and receive them after every simulation step + NxParticleData particleData; + particleData.numParticlesPtr = &mParticleCount; + particleData.bufferPos = &mParticles[0].position.x; + particleData.bufferPosByteStride = sizeof(FluidParticle); + particleData.bufferVel = &mParticles[0].velocity.x; + particleData.bufferVelByteStride = sizeof(FluidParticle); + particleData.bufferLife = &mParticles[0].lifetime; + particleData.bufferLifeByteStride = sizeof(FluidParticle); + + // Create a fluid descriptor + NxFluidDesc fluidDesc; + fluidDesc.kernelRadiusMultiplier = 2.3f; + fluidDesc.restParticlesPerMeter = 10.0f; + fluidDesc.stiffness = 200.0f; + fluidDesc.viscosity = 22.0f; + fluidDesc.restDensity = 1000.0f; + fluidDesc.damping = 0.0f; + fluidDesc.simulationMethod = NX_F_SPH; + fluidDesc.initialParticleData = particleData; + fluidDesc.particlesWriteData = particleData; + */ + + NxFluidDesc fluidDesc; + fluidDesc.setToDefault(); + fluidDesc.simulationMethod = NX_F_SPH; + fluidDesc.maxParticles = MAX_PARTICLES; + fluidDesc.restParticlesPerMeter = 50; + fluidDesc.stiffness = 1; + fluidDesc.viscosity = 6; + fluidDesc.flags = NX_FF_VISUALIZATION|NX_FF_ENABLED; + + mParticles = new FluidParticle[MAX_PARTICLES]; + dMemset( mParticles, 0, sizeof(FluidParticle) * MAX_PARTICLES ); + + NxParticleData &particleData = fluidDesc.particlesWriteData; + + particleData.numParticlesPtr = &mParticleCount; + particleData.bufferPos = &mParticles[0].position.x; + particleData.bufferPosByteStride = sizeof(FluidParticle); + particleData.bufferVel = &mParticles[0].velocity.x; + particleData.bufferVelByteStride = sizeof(FluidParticle); + particleData.bufferLife = &mParticles[0].lifetime; + particleData.bufferLifeByteStride = sizeof(FluidParticle); + + mFluid = mScene->createFluid( fluidDesc ); + + + //Create Emitter. + NxFluidEmitterDesc emitterDesc; + emitterDesc.setToDefault(); + emitterDesc.dimensionX = getScale().x; + emitterDesc.dimensionY = getScale().y; + emitterDesc.relPose.setColumnMajor44( getTransform() ); + emitterDesc.rate = 5.0f; + emitterDesc.randomAngle = 0.1f; + emitterDesc.fluidVelocityMagnitude = 6.5f; + emitterDesc.maxParticles = 0; + emitterDesc.particleLifetime = 4.0f; + emitterDesc.type = NX_FE_CONSTANT_FLOW_RATE; + emitterDesc.shape = NX_FE_ELLIPSE; + mEmitter = mFluid->createEmitter(emitterDesc); +} + +void PxFluid::_destroyFluid() +{ + delete[] mParticles; + mScene->releaseFluid( *mFluid ); + mEmitter = NULL; +} + +ConsoleMethod( PxFluid, resetParticles, void, 2, 2, "" ) +{ + object->resetParticles(); +} + +ConsoleMethod( PxFluid, setRate, void, 2, 2, "" ) +{ + object->setRate( dAtof(argv[2]) ); +} \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx/pxFluid.h b/Engine/source/T3D/physics/physx/pxFluid.h new file mode 100644 index 000000000..ae7fe4f3c --- /dev/null +++ b/Engine/source/T3D/physics/physx/pxFluid.h @@ -0,0 +1,107 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PXFLUID_H_ +#define _PXFLUID_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _PHYSX_H_ +#include "T3D/physics/physx/px.h" +#endif + +class BaseMatInstance; +class PxWorld; +class NxScene; + + +class PxFluid : public SceneObject +{ + typedef SceneObject Parent; + +protected: + + enum NetMasks + { + UpdateMask = Parent::NextFreeMask, + ResetMask = Parent::NextFreeMask << 1, + NextFreeMask = Parent::NextFreeMask << 2 + }; + + struct FluidParticle + { + NxVec3 position; + NxVec3 velocity; + NxReal density; + NxReal lifetime; + NxU32 id; + NxVec3 collisionNormal; + }; + + #define MAX_PARTICLES 100 + +public: + + PxFluid(); + virtual ~PxFluid(); + + DECLARE_CONOBJECT( PxFluid ); + + // SimObject + virtual bool onAdd(); + virtual void onRemove(); + static void initPersistFields(); + virtual void inspectPostApply(); + + // NetObject + virtual U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream ); + virtual void unpackUpdate( NetConnection *conn, BitStream *stream ); + + // SceneObject + virtual void setTransform( const MatrixF &mat ); + virtual void setScale( const VectorF &scale ); + virtual void prepRenderImage( SceneRenderState *state ); + + void resetParticles(); + void setRate( F32 rate ); + +protected: + + void renderObject( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); + + void _createFluid(); + void _destroyFluid(); + +protected: + + PxWorld *mWorld; + NxScene *mScene; + + FluidParticle *mParticles; + //NxParticleData *mParticleData; + NxFluid *mFluid; + U32 mParticleCount; + NxFluidEmitter *mEmitter; +}; + +#endif // _PXFLUID_H_ \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx/pxMaterial.cpp b/Engine/source/T3D/physics/physx/pxMaterial.cpp new file mode 100644 index 000000000..9a5656394 --- /dev/null +++ b/Engine/source/T3D/physics/physx/pxMaterial.cpp @@ -0,0 +1,150 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physX/px.h" + +#include "T3D/physics/physX/pxMaterial.h" + +#include "T3D/physics/physX/pxWorld.h" +#include "T3D/physics/physicsPlugin.h" +#include "console/consoleTypes.h" +#include "core/stream/bitStream.h" + + +IMPLEMENT_CO_DATABLOCK_V1( PxMaterial ); + +ConsoleDocClass( PxMaterial, + + "@brief Defines a PhysX material assignable to a PxMaterial.\n\n" + + "When two actors collide, the collision behavior that results depends on the material properties " + "of the actors' surfaces. For example, the surface properties determine if the actors will or will " + "not bounce, or if they will slide or stick. Currently, the only special feature supported by materials " + "is anisotropic friction, but according to Nvidia, other effects such as moving surfaces and more types " + "of friction are slotted for future release.\n\n" + + "For more information, refer to Nvidia's PhysX docs.\n\n" + + "@ingroup Physics" +); + +PxMaterial::PxMaterial() +: mNxMat( NULL ), + mNxMatId( -1 ), + restitution( 0.0f ), + staticFriction( 0.1f ), + dynamicFriction( 0.95f ), + mServer( false ) +{ +} + +PxMaterial::~PxMaterial() +{ +} + +void PxMaterial::consoleInit() +{ + Parent::consoleInit(); +} + +void PxMaterial::initPersistFields() +{ + Parent::initPersistFields(); + + addGroup("PxMaterial"); + + addField( "restitution", TypeF32, Offset( restitution, PxMaterial ), + "@brief Coeffecient of a bounce applied to the shape in response to a collision.\n\n" + "A value of 0 makes the object bounce as little as possible, while higher values up to 1.0 result in more bounce.\n\n" + "@note Values close to or above 1.0 may cause stability problems and/or increasing energy."); + addField( "staticFriction", TypeF32, Offset( staticFriction, PxMaterial ), + "@brief Coefficient of static %friction to be applied.\n\n" + "Static %friction determines the force needed to start moving an at-rest object in contact with a surface. " + "If the force applied onto shape cannot overcome the force of static %friction, the shape will remain at rest. " + "A higher coefficient will require a larger force to start motion. " + "@note This value should be larger than 0.\n\n"); + addField( "dynamicFriction", TypeF32, Offset( dynamicFriction, PxMaterial ), + "@brief Coefficient of dynamic %friction to be applied.\n\n" + "Dynamic %friction reduces the velocity of a moving object while it is in contact with a surface. " + "A higher coefficient will result in a larger reduction in velocity. " + "A shape's dynamicFriction should be equal to or larger than 0.\n\n"); + + endGroup("PxMaterial"); +} + +void PxMaterial::onStaticModified( const char *slotName, const char *newValue ) +{ + if ( isProperlyAdded() && mNxMat != NULL ) + { + mNxMat->setRestitution( restitution ); + mNxMat->setStaticFriction( staticFriction ); + mNxMat->setDynamicFriction( dynamicFriction ); + } +} + +bool PxMaterial::preload( bool server, String &errorBuffer ) +{ + mServer = server; + + PxWorld *world = dynamic_cast( PHYSICSMGR->getWorld( server ? "server" : "client" ) ); + + if ( !world ) + { + // TODO: Error... in error buffer? + return false; + } + + NxMaterialDesc material; + material.restitution = restitution; + material.staticFriction = staticFriction; + material.dynamicFriction = dynamicFriction; + + mNxMat = world->createMaterial( material ); + mNxMatId = mNxMat->getMaterialIndex(); + + if ( mNxMatId == -1 ) + { + errorBuffer = "PxMaterial::preload() - unable to create material!"; + return false; + } + + return Parent::preload( server, errorBuffer ); +} + +void PxMaterial::packData( BitStream* stream ) +{ + Parent::packData( stream ); + + stream->write( restitution ); + stream->write( staticFriction ); + stream->write( dynamicFriction ); +} + +void PxMaterial::unpackData( BitStream* stream ) +{ + Parent::unpackData( stream ); + + stream->read( &restitution ); + stream->read( &staticFriction ); + stream->read( &dynamicFriction ); +} diff --git a/Engine/source/T3D/physics/physx/pxMaterial.h b/Engine/source/T3D/physics/physx/pxMaterial.h new file mode 100644 index 000000000..b1e0dd7f0 --- /dev/null +++ b/Engine/source/T3D/physics/physx/pxMaterial.h @@ -0,0 +1,69 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PHYSX_MATERIAL_H +#define _PHYSX_MATERIAL_H + +#ifndef _SIMBASE_H_ +#include "console/simBase.h" +#endif +#ifndef _DYNAMIC_CONSOLETYPES_H_ +#include "console/dynamicTypes.h" +#endif + +class NxMaterial; + +class PxMaterial : public SimDataBlock +{ + typedef SimDataBlock Parent; + +protected: + + F32 restitution; + F32 staticFriction; + F32 dynamicFriction; + + NxMaterial *mNxMat; + S32 mNxMatId; + + bool mServer; + +public: + + DECLARE_CONOBJECT( PxMaterial ); + + PxMaterial(); + ~PxMaterial(); + + static void consoleInit(); + static void initPersistFields(); + virtual void onStaticModified( const char *slotName, const char *newValue ); + + bool preload( bool server, String &errorBuffer ); + virtual void packData( BitStream* stream ); + virtual void unpackData( BitStream* stream ); + + S32 getMaterialId() const { return mNxMatId; } + +}; + +#endif // _PHYSX_MATERIAL_H \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx/pxMultiActor.cpp b/Engine/source/T3D/physics/physx/pxMultiActor.cpp new file mode 100644 index 000000000..903405713 --- /dev/null +++ b/Engine/source/T3D/physics/physx/pxMultiActor.cpp @@ -0,0 +1,2651 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physX/pxMultiActor.h" + +#include "console/consoleTypes.h" +#include "core/stream/fileStream.h" +#include "core/stream/bitStream.h" +#include "core/resourceManager.h" +#include "core/strings/stringUnit.h" +#include "sim/netConnection.h" +#include "math/mathIO.h" +#include "math/mathUtils.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/gfxDrawUtil.h" +#include "gfx/primBuilder.h" +#include "collision/collision.h" +#include "collision/abstractPolyList.h" +#include "ts/tsShapeInstance.h" +#include "ts/tsPartInstance.h" +#include "lighting/lightManager.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "scene/sceneObjectLightingPlugin.h" +#include "T3D/objectTypes.h" +#include "T3D/containerQuery.h" +#include "T3D/fx/particleEmitter.h" +#include "T3D/debris.h" +#include "renderInstance/renderPassManager.h" +#include "gui/worldEditor/editor.h" // For gEditingMission +#include "T3D/physics/physX/px.h" +#include "T3D/physics/physX/pxWorld.h" +#include "T3D/physics/physX/pxMaterial.h" +#include "T3D/physics/physX/pxCasts.h" +#include "T3D/physics/physx/pxUtils.h" +#include "sfx/sfxSystem.h" + +#include +#include +#include + + +class PxMultiActor_Notify : public NXU_userNotify +{ +protected: + + Vector mActors; + + Vector mShapes; + + Vector mJoints; + + const NxMat34 mTransform; + + const Point3F mScale; + + F32 mMassScale; + + NxCompartment *mCompartment; + + PxMaterial *mMaterial; + + Vector *mActorUserProperties; + + Vector *mJointUserProperties; + +public: + + void NXU_notifyJoint( NxJoint *joint, const char *userProperties ) + { + if ( mJointUserProperties ) + mJointUserProperties->push_back( userProperties ); + mJoints.push_back( joint ); + } + + bool NXU_preNotifyJoint( NxJointDesc &joint, const char *userProperties ) + { + joint.localAnchor[0].x *= mScale.x; + joint.localAnchor[0].y *= mScale.y; + joint.localAnchor[0].z *= mScale.z; + + joint.localAnchor[1].x *= mScale.x; + joint.localAnchor[1].y *= mScale.y; + joint.localAnchor[1].z *= mScale.z; + + // The PhysX exporter from 3dsMax doesn't allow creation + // of fixed joints. It also doesn't seem to export the + // joint names! So look for joints which all all the + // motion axes are locked... make those fixed joints. + if ( joint.getType() == NX_JOINT_D6 ) + { + NxD6JointDesc *d6Joint = static_cast( &joint ); + + if ( d6Joint->xMotion == NX_D6JOINT_MOTION_LOCKED && + d6Joint->yMotion == NX_D6JOINT_MOTION_LOCKED && + d6Joint->zMotion == NX_D6JOINT_MOTION_LOCKED && + d6Joint->swing1Motion == NX_D6JOINT_MOTION_LOCKED && + d6Joint->swing2Motion == NX_D6JOINT_MOTION_LOCKED && + d6Joint->twistMotion == NX_D6JOINT_MOTION_LOCKED ) + { + // Ok... build a new fixed joint. + NxFixedJointDesc fixed; + fixed.actor[0] = joint.actor[0]; + fixed.actor[1] = joint.actor[1]; + fixed.localNormal[0] = joint.localNormal[0]; + fixed.localNormal[1] = joint.localNormal[1]; + fixed.localAxis[0] = joint.localAxis[0]; + fixed.localAxis[1] = joint.localAxis[1]; + fixed.localAnchor[0] = joint.localAnchor[0]; + fixed.localAnchor[1] = joint.localAnchor[1]; + fixed.maxForce = joint.maxForce; + fixed.maxTorque = joint.maxTorque; + fixed.name = joint.name; + fixed.userData = joint.userData; + fixed.jointFlags = joint.jointFlags; + + // What scene are we adding this to? + NxActor *actor = fixed.actor[0] ? fixed.actor[0] : fixed.actor[1]; + NxScene &scene = actor->getScene(); + + NxJoint* theJoint = scene.createJoint( fixed ); + mJoints.push_back( theJoint ); + if ( mJointUserProperties ) + mJointUserProperties->push_back( userProperties ); + + // Don't generate this joint. + return false; + } + } + + return true; + } + + void NXU_notifyActor( NxActor *actor, const char *userProperties ) + { + mActors.push_back( actor ); + + // Save the shapes. + for ( U32 i=0; i < actor->getNbShapes(); i++ ) + mShapes.push_back( actor->getShapes()[i] ); + + mActorUserProperties->push_back( userProperties ); + }; + + bool NXU_preNotifyMaterial( NxMaterialDesc &t, const char *userProperties ) + { + // Don't generate materials if we have one defined! + return !mMaterial; + } + + bool NXU_preNotifyActor( NxActorDesc &actor, const char *userProperties ) + { + // Set the right compartment. + actor.compartment = mCompartment; + + if ( actor.shapes.size() == 0 ) + Con::warnf( "PxMultiActor_Notify::NXU_preNotifyActor, got an actor (%s) with no shapes, was this intentional?", actor.name ); + + // For every shape, cast to its particular type + // and apply the scale to size, mass and localPosition. + for( S32 i = 0; i < actor.shapes.size(); i++ ) + { + // If we have material then set it. + if ( mMaterial ) + actor.shapes[i]->materialIndex = mMaterial->getMaterialId(); + + switch( actor.shapes[i]->getType() ) + { + case NX_SHAPE_BOX: + { + NxBoxShapeDesc *boxDesc = (NxBoxShapeDesc*)actor.shapes[i]; + + boxDesc->mass *= mMassScale; + + boxDesc->dimensions.x *= mScale.x; + boxDesc->dimensions.y *= mScale.y; + boxDesc->dimensions.z *= mScale.z; + + boxDesc->localPose.t.x *= mScale.x; + boxDesc->localPose.t.y *= mScale.y; + boxDesc->localPose.t.z *= mScale.z; + break; + } + + case NX_SHAPE_SPHERE: + { + NxSphereShapeDesc *sphereDesc = (NxSphereShapeDesc*)actor.shapes[i]; + + sphereDesc->mass *= mMassScale; + + // TODO: Spheres do not work with non-uniform + // scales very well... how do we fix this? + sphereDesc->radius *= mScale.x; + + sphereDesc->localPose.t.x *= mScale.x; + sphereDesc->localPose.t.y *= mScale.y; + sphereDesc->localPose.t.z *= mScale.z; + break; + } + + case NX_SHAPE_CAPSULE: + { + NxCapsuleShapeDesc *capsuleDesc = (NxCapsuleShapeDesc*)actor.shapes[i]; + + capsuleDesc->mass *= mMassScale; + + // TODO: Capsules do not work with non-uniform + // scales very well... how do we fix this? + capsuleDesc->radius *= mScale.x; + capsuleDesc->height *= mScale.y; + + capsuleDesc->localPose.t.x *= mScale.x; + capsuleDesc->localPose.t.y *= mScale.y; + capsuleDesc->localPose.t.z *= mScale.z; + break; + } + + default: + { + static String lookup[] = + { + "PLANE", + "SPHERE", + "BOX", + "CAPSULE", + "WHEEL", + "CONVEX", + "MESH", + "HEIGHTFIELD" + }; + + Con::warnf( "PxMultiActor_Notify::NXU_preNotifyActor, unsupported shape type (%s), on Actor (%s)", lookup[actor.shapes[i]->getType()].c_str(), actor.name ); + + delete actor.shapes[i]; + actor.shapes.erase( actor.shapes.begin() + i ); + --i; + break; + } + } + } + + NxBodyDesc *body = const_cast( actor.body ); + if ( body ) + { + // Must scale all of these parameters, else there will be odd results! + body->mass *= mMassScale; + body->massLocalPose.t.multiply( mMassScale, body->massLocalPose.t ); + body->massSpaceInertia.multiply( mMassScale, body->massSpaceInertia ); + + // Ragdoll damping! + //body->sleepDamping = 1.7f; + //body->linearDamping = 0.4f; + //body->angularDamping = 0.08f; + //body->wakeUpCounter = 0.3f; + } + + return true; + }; + +public: + + PxMultiActor_Notify( NxCompartment *compartment, + PxMaterial *material, + const NxMat34& mat, + const Point3F& scale, + Vector *actorProps = NULL, + Vector *jointProps = NULL ) + : mCompartment( compartment ), + mMaterial( material ), + mScale( scale ), + mTransform( mat ), + mActorUserProperties( actorProps ), + mJointUserProperties( jointProps ) + { + const F32 unit = VectorF( 1.0f, 1.0f, 1.0f ).len(); + mMassScale = mScale.len() / unit; + } + + virtual ~PxMultiActor_Notify() + { + } + + const Vector& getActors() { return mActors; } + const Vector& getShapes() { return mShapes; } + const Vector& getJoints() { return mJoints; } +}; + +ConsoleDocClass( PxMultiActorData, + + "@brief Defines the properties of a type of PxMultiActor.\n\n" + + "Usually it is prefered to use PhysicsShape rather than PxMultiActor because " + "a PhysicsShape is not PhysX specific and can be much easier to setup.\n\n" + + "For more information, refer to Nvidia's PhysX docs.\n\n" + + "@ingroup Physics" +); + +IMPLEMENT_CO_DATABLOCK_V1(PxMultiActorData); + +PxMultiActorData::PxMultiActorData() + : material( NULL ), + collection( NULL ), + waterDragScale( 1.0f ), + buoyancyDensity( 1.0f ), + angularDrag( 0.0f ), + linearDrag( 0.0f ), + clientOnly( false ), + singlePlayerOnly( false ), + shapeName( StringTable->insert( "" ) ), + physXStream( StringTable->insert( "" ) ), + breakForce( 0.0f ) +{ + for ( S32 i = 0; i < MaxCorrectionNodes; i++ ) + correctionNodeNames[i] = StringTable->insert( "" ); + + for ( S32 i = 0; i < MaxCorrectionNodes; i++ ) + correctionNodes[i] = -1; + + for ( S32 i = 0; i < NumMountPoints; i++ ) + { + mountNodeNames[i] = StringTable->insert( "" ); + mountPointNode[i] = -1; + } +} + +PxMultiActorData::~PxMultiActorData() +{ + if ( collection ) + NXU::releaseCollection( collection ); +} + +void PxMultiActorData::initPersistFields() +{ + Parent::initPersistFields(); + + addGroup("Media"); + addField( "shapeName", TypeFilename, Offset( shapeName, PxMultiActorData ), + "@brief Path to the .DAE or .DTS file to render.\n\n"); + endGroup("Media"); + + // PhysX collision properties. + addGroup( "Physics" ); + + addField( "physXStream", TypeFilename, Offset( physXStream, PxMultiActorData ), + "@brief .XML file containing data such as actors, shapes, and joints.\n\n" + "These files can be created using a free PhysX plugin for 3DS Max.\n\n"); + addField( "material", TYPEID< PxMaterial >(), Offset( material, PxMultiActorData ), + "@brief An optional PxMaterial to be used for the PxMultiActor.\n\n" + "Defines properties such as friction and restitution. " + "Unrelated to the material used for rendering. The physXStream will contain " + "defined materials that can be customized in 3DS Max. " + "To override the material for all physics shapes in the physXStream, specify a material here.\n\n"); + + addField( "noCorrection", TypeBool, Offset( noCorrection, PxMultiActorData ), + "@hide" ); + + UTF8 buff[256]; + for ( S32 i=0; i < MaxCorrectionNodes; i++ ) + { + //dSprintf( buff, sizeof(buff), "correctionNode%d", i ); + addField( buff, TypeString, Offset( correctionNodeNames[i], PxMultiActorData ), "@hide" ); + } + + for ( S32 i=0; i < NumMountPoints; i++ ) + { + //dSprintf( buff, sizeof(buff), "mountNode%d", i ); + addField( buff, TypeString, Offset( mountNodeNames[i], PxMultiActorData ), "@hide" ); + } + + addField( "angularDrag", TypeF32, Offset( angularDrag, PxMultiActorData ), + "@brief Value used to help calculate rotational drag force while submerged in water.\n\n"); + addField( "linearDrag", TypeF32, Offset( linearDrag, PxMultiActorData ), + "@brief Value used to help calculate linear drag force while submerged in water.\n\n"); + addField( "waterDragScale", TypeF32, Offset( waterDragScale, PxMultiActorData ), + "@brief Scale to apply to linear and angular dampening while submerged in water.\n\n "); + addField( "buoyancyDensity", TypeF32, Offset( buoyancyDensity, PxMultiActorData ), + "@brief The density used to calculate buoyant forces.\n\n" + "The result of the calculated buoyancy is relative to the density of the WaterObject the PxMultiActor is within.\n\n" + "@note This value is necessary because Torque 3D does its own buoyancy simulation. It is not handled by PhysX." + "@see WaterObject::density"); + + endGroup( "Physics" ); + + addField( "clientOnly", TypeBool, Offset( clientOnly, PxMultiActorData ), + "@hide"); + addField( "singlePlayerOnly", TypeBool, Offset( singlePlayerOnly, PxMultiActorData ), + "@hide"); + addField( "breakForce", TypeF32, Offset( breakForce, PxMultiActorData ), + "@brief Force required to break an actor.\n\n" + "This value does not apply to joints. " + "If an actor is associated with a joint it will break whenever the joint does. " + "This allows an actor \"not\" associated with a joint to also be breakable.\n\n"); +} + +void PxMultiActorData::packData(BitStream* stream) +{ + Parent::packData(stream); + + stream->writeString( shapeName ); + stream->writeString( physXStream ); + + if( stream->writeFlag( material ) ) + stream->writeRangedU32( packed ? SimObjectId( material ) : material->getId(), + DataBlockObjectIdFirst, DataBlockObjectIdLast ); + + if ( !stream->writeFlag( noCorrection ) ) + { + // Write the correction node indices for the client. + for ( S32 i = 0; i < MaxCorrectionNodes; i++ ) + stream->write( correctionNodes[i] ); + } + + for ( S32 i = 0; i < NumMountPoints; i++ ) + stream->write( mountPointNode[i] ); + + stream->write( waterDragScale ); + stream->write( buoyancyDensity ); + stream->write( angularDrag ); + stream->write( linearDrag ); + + stream->writeFlag( clientOnly ); + stream->writeFlag( singlePlayerOnly ); + stream->write( breakForce ); +} + +void PxMultiActorData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + + shapeName = stream->readSTString(); + physXStream = stream->readSTString(); + + if( stream->readFlag() ) + material = (PxMaterial*)stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); + + noCorrection = stream->readFlag(); + if ( !noCorrection ) + { + for ( S32 i = 0; i < MaxCorrectionNodes; i++ ) + stream->read( &correctionNodes[i] ); + } + + for ( S32 i = 0; i < NumMountPoints; i++ ) + stream->read( &mountPointNode[i] ); + + stream->read( &waterDragScale ); + stream->read( &buoyancyDensity ); + stream->read( &angularDrag ); + stream->read( &linearDrag ); + + clientOnly = stream->readFlag(); + singlePlayerOnly = stream->readFlag(); + stream->read( &breakForce ); +} + +bool PxMultiActorData::preload( bool server, String &errorBuffer ) +{ + if ( !Parent::preload( server, errorBuffer ) ) + return false; + + // If the stream is null, exit. + if ( !physXStream || !physXStream[0] ) + { + errorBuffer = "PxMultiActorData::preload: physXStream is unset!"; + return false; + } + + // Set up our buffer for the binary stream filename path. + UTF8 binPhysXStream[260] = { 0 }; + const UTF8* ext = dStrrchr( physXStream, '.' ); + + // Copy the xml stream path except for the extension. + if ( ext ) + dStrncpy( binPhysXStream, physXStream, getMin( 260, ext - physXStream ) ); + else + dStrncpy( binPhysXStream, physXStream, 260 ); + + // Concatenate the binary extension. + dStrcat( binPhysXStream, ".nxb" ); + + // Get the modified times of the two files. + FileTime xmlTime = {0}, binTime = {0}; + Platform::getFileTimes( physXStream, NULL, &xmlTime ); + Platform::getFileTimes( binPhysXStream, NULL, &binTime ); + + // If the binary is newer... load that. + if ( Platform::compareFileTimes( binTime, xmlTime ) >= 0 ) + _loadCollection( binPhysXStream, true ); + + // If the binary failed... then load the xml. + if ( !collection ) + { + _loadCollection( physXStream, false ); + + // If loaded... resave the xml in binary format + // for quicker subsequent loads. + if ( collection ) + NXU::saveCollection( collection, binPhysXStream, NXU::FT_BINARY ); + } + + // If it still isn't loaded then we've failed! + if ( !collection ) + { + errorBuffer = String::ToString( "PxMultiActorDatas::preload: could not load '%s'!", physXStream ); + return false; + } + + if (!shapeName || shapeName == '\0') + { + errorBuffer = "PxMultiActorDatas::preload: no shape name!"; + return false; + } + + shape = ResourceManager::get().load( shapeName ); + + if (bool(shape) == false) + { + errorBuffer = String::ToString( "PxMultiActorData::preload: unable to load shape: %s", shapeName ); + return false; + } + + // Find the client side material. + if ( !server && material ) + Sim::findObject( SimObjectId(material), material ); + + // Get the ignore node indexes from the names. + for ( S32 i = 0; i < MaxCorrectionNodes; i++ ) + { + if( !correctionNodeNames[i] || !correctionNodeNames[i][0] ) + continue; + + correctionNodes[i] = shape->findNode( correctionNodeNames[i] ); + } + + // Resolve mount point node indexes + for ( S32 i = 0; i < NumMountPoints; i++) + { + char fullName[256]; + + if ( !mountNodeNames[i] || !mountNodeNames[i][0] ) + { + dSprintf(fullName,sizeof(fullName),"mount%d",i); + mountPointNode[i] = shape->findNode(fullName); + } + else + mountPointNode[i] = shape->findNode(mountNodeNames[i]); + } + + // Register for file change notification to reload the collection + if ( server ) + FS::AddChangeNotification( physXStream, this, &PxMultiActorData::_onFileChanged ); + + return true; +} + +void PxMultiActorData::_onFileChanged( const Torque::Path &path ) +{ + reload(); +} + +void PxMultiActorData::reload() +{ + bool result = _loadCollection( physXStream, false ); + + if ( !result ) + Con::errorf( "PxMultiActorData::reload(), _loadCollection failed..." ); + + // Inform MultiActors who use this datablock to reload. + mReloadSignal.trigger(); +} + +bool PxMultiActorData::_loadCollection( const UTF8 *path, bool isBinary ) +{ + if ( collection ) + { + NXU::releaseCollection( collection ); + collection = NULL; + } + + FileStream fs; + if ( !fs.open( path, Torque::FS::File::Read ) ) + return false; + + // Load the data into memory. + U32 size = fs.getStreamSize(); + FrameTemp buff( size ); + fs.read( size, buff ); + + // If the stream didn't read anything, there's a problem. + if ( size <= 0 ) + return false; + + // Ok... try to load it. + collection = NXU::loadCollection( path, + isBinary ? NXU::FT_BINARY : NXU::FT_XML, + buff, + size ); + + return collection != NULL; +} + + +bool PxMultiActorData::createActors( NxScene *scene, + NxCompartment *compartment, + const NxMat34 *nxMat, + const Point3F& scale, + Vector *outActors, + Vector *outShapes, + Vector *outJoints, + Vector *outActorUserProperties, + Vector *outJointUserProperties ) +{ + if ( !scene ) + { + Con::errorf( "PxMultiActorData::createActor() - returned null NxScene" ); + return NULL; + } + + PxMultiActor_Notify pxNotify( compartment, material, *nxMat, scale, outActorUserProperties, outJointUserProperties ); + + NXU::instantiateCollection( collection, *gPhysicsSDK, scene, nxMat, &pxNotify ); + + *outActors = pxNotify.getActors(); + *outJoints = pxNotify.getJoints(); + if ( outShapes ) + *outShapes = pxNotify.getShapes(); + + if ( outActors->empty() ) + { + Con::errorf( "PxMultiActorData::createActors() - NXUStream notifier returned empty actors or joints!" ); + return false; + } + + return true; +} + +ConsoleDocClass( PxMultiActor, + + "@brief Represents a destructible physical object simulated using PhysX.\n\n" + + "Usually it is prefered to use PhysicsShape and not PxMultiActor because " + "it is not PhysX specific and much easier to setup.\n" + + "@see PxMultiActorData.\n" + "@ingroup Physics" +); + +IMPLEMENT_CO_NETOBJECT_V1(PxMultiActor); + +PxMultiActor::PxMultiActor() + : mShapeInstance( NULL ), + mRootActor( NULL ), + mWorld( NULL ), + mStartImpulse( 0, 0, 0 ), + mResetXfm( true ), + mActorScale( 0, 0, 0 ), + mDebugRender( false ), + mIsDummy( false ), + mBroken( false ), + mDataBlock( NULL ) +{ + mNetFlags.set( Ghostable | ScopeAlways ); + + mTypeMask |= StaticObjectType | StaticShapeObjectType; + + //mUserData.setObject( this ); +} + +void PxMultiActor::initPersistFields() +{ + Parent::initPersistFields(); + + /* + // We're overloading these fields from SceneObject + // in order to force it to go thru setTransform! + removeField( "position" ); + removeField( "rotation" ); + removeField( "scale" ); + + addGroup( "Transform" ); + + addProtectedField( "position", TypeMatrixPosition, 0, + &PxMultiActor::_setPositionField, + &PxMultiActor::_getPositionField, + "" ); + + addProtectedField( "rotation", TypeMatrixRotation, 0, + &PxMultiActor::_setRotationField, + &PxMultiActor::_getRotationField, + "" ); + + addField( "scale", TypePoint3F, Offset( mObjScale, PxMultiActor ) ); + + endGroup( "Transform" ); + */ + + //addGroup("Physics"); + // addField( "AngularDrag", TypeF32, ) + //endGroup("Physics"); + + addGroup( "Debug" ); + addField( "debugRender", TypeBool, Offset( mDebugRender, PxMultiActor ), "@hide"); + addField( "broken", TypeBool, Offset( mBroken, PxMultiActor ), "@hide"); + endGroup( "Debug" ); + + //addGroup("Collision"); + //endGroup("Collision"); +} + +bool PxMultiActor::onAdd() +{ + PROFILE_SCOPE( PxMultiActor_OnAdd ); + + if (!Parent::onAdd() || !mDataBlock ) + return false; + + mIsDummy = isClientObject() && PHYSICSMGR->isSinglePlayer(); //&& mDataBlock->singlePlayerOnly; + + mShapeInstance = new TSShapeInstance( mDataBlock->shape, isClientObject() ); + + mObjBox = mDataBlock->shape->bounds; + resetWorldBox(); + + addToScene(); + + String worldName = isServerObject() ? "server" : "client"; + + // SinglePlayer objects only have server-side physics representations. + if ( mIsDummy ) + worldName = "server"; + + mWorld = dynamic_cast( PHYSICSMGR->getWorld( worldName ) ); + if ( !mWorld || !mWorld->getScene() ) + { + Con::errorf( "PxMultiActor::onAdd() - PhysXWorld not initialized!" ); + return false; + } + + applyWarp( getTransform(), true, false ); + mResetXfm = getTransform(); + + if ( !_createActors( getTransform() ) ) + { + Con::errorf( "PxMultiActor::onAdd(), _createActors failed" ); + return false; + } + + if ( !mIsDummy ) + mDataBlock->mReloadSignal.notify( this, &PxMultiActor::onFileNotify ); + + // If the editor is on... let it know! + //if ( gEditingMission ) + //onEditorEnable(); // TODO: Fix this up. + + PhysicsPlugin::getPhysicsResetSignal().notify( this, &PxMultiActor::onPhysicsReset, 1050.0f ); + + setAllBroken( false ); + + if ( isServerObject() ) + scriptOnAdd(); + + return true; +} + + +void PxMultiActor::onRemove() +{ + removeFromScene(); + + _destroyActors(); + mWorld = NULL; + + SAFE_DELETE( mShapeInstance ); + + PhysicsPlugin::getPhysicsResetSignal().remove( this, &PxMultiActor::onPhysicsReset ); + + if ( !mIsDummy && mDataBlock ) + mDataBlock->mReloadSignal.remove( this, &PxMultiActor::onFileNotify ); + + Parent::onRemove(); +} + +void PxMultiActor::_destroyActors() +{ + // Dummies don't have physics objects. + if ( mIsDummy || !mWorld ) + return; + + mWorld->releaseWriteLock(); + + // Clear the root actor. + mRootActor = NULL; + + // Clear the relative transforms. + //mRelXfms.clear(); + + // The shapes are owned by the actors, so + // we just need to clear them. + mShapes.clear(); + + // Release the joints first. + for( S32 i = 0; i < mJoints.size(); i++ ) + { + NxJoint *joint = mJoints[i]; + + if ( !joint ) + continue; + + // We allocate per joint userData and we must free it. + PxUserData *jointData = PxUserData::getData( *joint ); + if ( jointData ) + delete jointData; + + mWorld->releaseJoint( *joint ); + } + mJoints.clear(); + + // Now release the actors. + for( S32 i = 0; i < mActors.size(); i++ ) + { + NxActor *actor = mActors[i]; + + PxUserData *actorData = PxUserData::getData( *actor ); + if ( actorData ) + delete actorData; + + if ( actor ) + mWorld->releaseActor( *actor ); + } + mActors.clear(); +} + +bool PxMultiActor::_createActors( const MatrixF &xfm ) +{ + if ( mIsDummy ) + { + // Dummies don't have physics objects, but + // they do handle actor deltas. + + PxMultiActor *serverObj = static_cast( mServerObject.getObject() ); + mActorDeltas.setSize( serverObj->mActors.size() ); + dMemset( mActorDeltas.address(), 0, mActorDeltas.memSize() ); + + return true; + } + + NxMat34 nxMat; + nxMat.setRowMajor44( xfm ); + + // Store the scale for comparison in setScale(). + mActorScale = getScale(); + + // Release the write lock so we can create actors. + mWorld->releaseWriteLock(); + + Vector actorUserProperties; + Vector jointUserProperties; + bool created = mDataBlock->createActors( mWorld->getScene(), + NULL, + &nxMat, + mActorScale, + &mActors, + &mShapes, + &mJoints, + &actorUserProperties, + &jointUserProperties ); + + // Debug output... + //for ( U32 i = 0; i < mJoints.size(); i++ ) + // Con::printf( "Joint0 name: '%s'", mJoints[i]->getName() ); + //for ( U32 i = 0; i < actorUserProperties.size(); i++ ) + //Con::printf( "actor%i UserProperties: '%s'", i, actorUserProperties[i].c_str() ); + //for ( U32 i = 0; i < jointUserProperties.size(); i++ ) + // Con::printf( "joint%i UserProperties: '%s'", i, jointUserProperties[i].c_str() ); + + if ( !created ) + { + Con::errorf( "PxMultiActor::_createActors() - failed!" ); + return false; + } + + // Make the first actor the root actor by default, but + // if we have a kinematic actor then use that. + mRootActor = mActors[0]; + for ( S32 i = 0; i < mActors.size(); i++ ) + { + if ( mActors[i]->readBodyFlag( NX_BF_KINEMATIC ) ) + { + mRootActor = mActors[i]; + break; + } + } + + mDelta.pos = mDelta.lastPos = getPosition(); + mDelta.rot = mDelta.lastRot = getTransform(); + + bool *usedActors = new bool[mActors.size()]; + dMemset( usedActors, 0, sizeof(bool) * mActors.size() ); + + TSShape *shape = mShapeInstance->getShape(); + + // Should already be done when actors are destroyed. + mMappedActors.clear(); + Vector mappedActorProperties; + + // Remap the actors to the shape instance's bone indices. + for( S32 i = 0; i < mShapeInstance->mNodeTransforms.size(); i++ ) + { + if ( !shape ) + break; + + UTF8 comparisonName[260] = { 0 }; + NxActor *actor = NULL; + NxActor *pushActor = NULL; + String actorProperties; + + S32 nodeNameIdx = shape->nodes[i].nameIndex; + const UTF8 *nodeName = shape->getName( nodeNameIdx ); + + S32 dl = -1; + dStrcpy( comparisonName, String::GetTrailingNumber( nodeName, dl ) ); //, ext - nodeName ); + dSprintf( comparisonName, sizeof( comparisonName ), "%s_pxactor", comparisonName ); + + //String test( nodeName ); + //AssertFatal( test.find("gableone",0,String::NoCase) == String::NPos, "found it" ); + + // If we find an actor that corresponds to this node we will + // push it back into the remappedActors vector, otherwise + // we will push back NULL. + for ( S32 j = 0; j < mActors.size(); j++ ) + { + actor = mActors[j]; + const UTF8 *actorName = actor->getName(); + + if ( dStricmp( comparisonName, actorName ) == 0 ) + { + pushActor = actor; + actorProperties = actorUserProperties[j]; + usedActors[j] = true; + break; + } + } + + mMappedActors.push_back( pushActor ); + mappedActorProperties.push_back( actorProperties ); + if ( !pushActor ) + dl = -1; + mMappedActorDL.push_back( dl ); + + // Increase the sleep tolerance. + if ( pushActor ) + { + //pushActor->raiseBodyFlag( NX_BF_ENERGY_SLEEP_TEST ); + //pushActor->setSleepEnergyThreshold( 2 ); + //pushActor->userData = NULL; + } + } + + // Delete any unused/orphaned actors. + for ( S32 i = 0; i < mActors.size(); i++ ) + { + if ( usedActors[i] ) + continue; + + NxActor *actor = mActors[i]; + + Con::errorf( "PxMultiActor::_createActors() - Orphan NxActor - '%s'!", actor->getName() ); + + if ( actor == mRootActor ) + { + Con::errorf( "PxMultiActor::_createActors() - root actor (%s) was orphan, cannot continue.", actor->getName() ); + return false; + } + + // Remove references to shapes of the deleted actor. + for ( S32 i = 0; i < mShapes.size(); i++ ) + { + if ( &(mShapes[i]->getActor()) == actor ) + { + mShapes.erase_fast(i); + i--; + } + } + + mWorld->releaseActor( *actor ); + } + + // Done with this helper. + delete [] usedActors; + + // Repopulate mActors with one entry per real actor we own. + mActors.clear(); + mMappedToActorIndex.clear(); + actorUserProperties.clear(); + for ( S32 i = 0; i < mMappedActors.size(); i++ ) + { + S32 index = -1; + if ( mMappedActors[i] ) + { + index = mActors.push_back_unique( mMappedActors[i] ); + while ( index >= actorUserProperties.size() ) + actorUserProperties.push_back( String::EmptyString ); + actorUserProperties[index] = mappedActorProperties[i]; + } + mMappedToActorIndex.push_back( index ); + } + + if ( mActors.size() == 0 ) + { + Con::errorf( "PxMultiActor::_createActors, got zero actors! Were all actors orphans?" ); + return false; + } + + // Initialize the actor deltas. + mActorDeltas.setSize( mActors.size() ); + dMemset( mActorDeltas.address(), 0, mActorDeltas.memSize() ); + + // Assign user data for actors. + for ( U32 i = 0; i < mActors.size(); i++ ) + { + NxActor *actor = mActors[i]; + if ( !actor ) + continue; + + actor->userData = _createActorUserData( actor, actorUserProperties[i] ); + } + + //NxActor *actor1; + //NxActor *actor2; + //PxUserData *pUserData; + + // Allocate user data for joints. + for ( U32 i = 0; i < mJoints.size(); i++ ) + { + NxJoint *joint = mJoints[i]; + if ( !joint ) + continue; + + joint->userData = _createJointUserData( joint, jointUserProperties[i] ); + + /* + // Set actors attached to joints as not-pushable (by the player). + joint->getActors( &actor1, &actor2 ); + if ( actor1 ) + { + pUserData = PxUserData::getData( *actor1 ); + if ( pUserData ) + pUserData->mCanPush = false; + } + if ( actor2 ) + { + pUserData = PxUserData::getData( *actor2 ); + if ( pUserData ) + pUserData->mCanPush = false; + } + */ + } + + // Set actors and meshes to the unbroken state. + setAllBroken( false ); + + return true; +} + +PxUserData* PxMultiActor::_createActorUserData( NxActor *actor, String &userProperties ) +{ + PxUserData *actorData = new PxUserData(); + actorData->setObject( this ); + + // We use this for saving relative xfms for 'broken' actors. + NxMat34 actorPose = actor->getGlobalPose(); + NxMat34 actorSpaceXfm; + actorPose.getInverse( actorSpaceXfm ); + + const String actorName( actor->getName() ); + + static const String showStr( "PxBrokenShow" ); + static const String hideStr( "PxBrokenHide" ); + + // 3DSMax saves out double newlines, replace them with one. + userProperties.replace( "\r\n", "\n" ); + + U32 propertyCount = StringUnit::getUnitCount( userProperties, "\n" ); + for ( U32 i = 0; i < propertyCount; i++ ) + { + String propertyStr = StringUnit::getUnit( userProperties, i, "\n" ); + U32 wordCount = StringUnit::getUnitCount( propertyStr, "=" ); + + if ( wordCount == 0 ) + { + // We sometimes get empty lines between properties, + // which doesn't break anything. + continue; + } + + if ( wordCount != 2 ) + { + Con::warnf( "PxMultiActor::_createActorUserData, malformed UserProperty string (%s) for actor (%s)", propertyStr.c_str(), actorName.c_str() ); + continue; + } + + String propertyName = StringUnit::getUnit( propertyStr, 0, "=" ); + String propertyValue = StringUnit::getUnit( propertyStr, 1, "=" ); + + Vector *dstVector = NULL; + if ( propertyName.equal( showStr, String::NoCase ) ) + dstVector = &actorData->mBrokenActors; + else if ( propertyName.equal( hideStr, String::NoCase ) ) + dstVector = &actorData->mUnbrokenActors; + + if ( !dstVector ) + continue; + + U32 valueCount = StringUnit::getUnitCount( propertyValue, "," ); + for ( U32 j = 0; j < valueCount; j++ ) + { + String val = StringUnit::getUnit( propertyValue, j, "," ); + + NxActor *pActor = _findActor( val ); + if ( !pActor ) + Con::warnf( "PxMultiActor::_createActorUserData, actor (%s) was not found when parsing UserProperties for actor (%s)", val.c_str(), actorName.c_str() ); + else + { + dstVector->push_back( pActor ); + + if ( dstVector == &actorData->mBrokenActors ) + { + NxMat34 relXfm = pActor->getGlobalPose(); + relXfm.multiply( relXfm, actorSpaceXfm ); + actorData->mRelXfm.push_back( relXfm ); + } + } + } + } + + // Only add a contact signal to this actor if + // we have objects we can break. + if ( actorData->mBrokenActors.size() > 0 && + mDataBlock->breakForce > 0.0f ) + { + actor->setContactReportFlags( NX_NOTIFY_ON_START_TOUCH_FORCE_THRESHOLD | NX_NOTIFY_FORCES ); + actor->setContactReportThreshold( mDataBlock->breakForce ); + actorData->getContactSignal().notify( this, &PxMultiActor::_onContact ); + } + + return actorData; +} + +PxUserData* PxMultiActor::_createJointUserData( NxJoint *joint, String &userProperties ) +{ + PxUserData *jointData = new PxUserData(); + jointData->setObject( this ); + + // We use this for saving relative xfms for 'broken' actors. + NxActor *actor0; + NxActor *actor1; + joint->getActors( &actor0, &actor1 ); + NxMat34 actorPose = actor0->getGlobalPose(); + NxMat34 actorSpaceXfm; + actorPose.getInverse( actorSpaceXfm ); + + // The PxMultiActor will live longer than the joint + // so this notify shouldn't ever need to be removed. Although if someone + // other than this multiactor were to register for this notify and their + // lifetime could be shorter, then 'they' might have to. + jointData->getOnJointBreakSignal().notify( this, &PxMultiActor::_onJointBreak ); + + // JCFHACK: put this in userProperties too. + Sim::findObject( "JointBreakEmitter", jointData->mParticleEmitterData ); + + String showStr( "PxBrokenShow" ); + String hideStr( "PxBrokenHide" ); + + // Max saves out double newlines, replace them with one. + userProperties.replace( "\r\n", "\n" ); + + U32 propertyCount = StringUnit::getUnitCount( userProperties, "\n" ); + for ( U32 i = 0; i < propertyCount; i++ ) + { + String propertyStr = StringUnit::getUnit( userProperties, i, "\n" ); + U32 wordCount = StringUnit::getUnitCount( propertyStr, "=" ); + + if ( wordCount == 0 ) + { + // We sometimes get empty lines between properties, + // which doesn't break anything. + continue; + } + + if ( wordCount != 2 ) + { + Con::warnf( "PxMultiActor::_createJointUserData, malformed UserProperty string (%s) for joint (%s)", propertyStr.c_str(), joint->getName() ); + continue; + } + + String propertyName = StringUnit::getUnit( propertyStr, 0, "=" ); + String propertyValue = StringUnit::getUnit( propertyStr, 1, "=" ); + + Vector *dstVector = NULL; + if ( propertyName.equal( showStr, String::NoCase ) ) + dstVector = &jointData->mBrokenActors; + else if ( propertyName.equal( hideStr, String::NoCase ) ) + dstVector = &jointData->mUnbrokenActors; + + if ( !dstVector ) + continue; + + U32 valueCount = StringUnit::getUnitCount( propertyValue, "," ); + for ( U32 j = 0; j < valueCount; j++ ) + { + String val = StringUnit::getUnit( propertyValue, j, "," ); + + NxActor *pActor = _findActor( val ); + if ( !pActor ) + Con::warnf( "PxMultiActor::_createJointUserData, actor (%s) was not found when parsing UserProperties for joint (%s)", val.c_str(), joint->getName() ); + else + { + dstVector->push_back( pActor ); + + if ( dstVector == &jointData->mBrokenActors ) + { + NxMat34 relXfm = pActor->getGlobalPose(); + relXfm.multiply( relXfm, actorSpaceXfm ); + jointData->mRelXfm.push_back( relXfm ); + } + } + } + } + + return jointData; +} + +NxActor* PxMultiActor::_findActor( const String &actorName ) const +{ + for ( U32 i = 0; i < mActors.size(); i++ ) + { + NxActor *actor = mActors[i]; + if ( !actor ) + continue; + + if ( dStricmp( actor->getName(), actorName ) == 0 ) + return actor; + } + + return NULL; +} + +String PxMultiActor::_getMeshName( const NxActor *actor ) const +{ + String meshName = actor->getName(); + meshName.replace( "_pxactor", "" ); + //meshName = StringUnit::getUnit( meshName, 0, "_" ); + return meshName; +} + +bool PxMultiActor::onNewDataBlock( GameBaseData *dptr, bool reload ) +{ + mDataBlock = dynamic_cast(dptr); + + if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) ) + return false; + + // JCF: if we supported it, we would recalculate the value of mIsDummy now, + // but that would really hose everything since an object that was a dummy + // wouldn't have any actors and would need to create them, etc... + + scriptOnNewDataBlock(); + + return true; +} + +void PxMultiActor::inspectPostApply() +{ + // Make sure we call the parent... else + // we won't get transform and scale updates! + Parent::inspectPostApply(); + + //setMaskBits( LightMask ); + setMaskBits( UpdateMask ); +} + +void PxMultiActor::onStaticModified( const char *slotName, const char *newValue ) +{ + if ( isProperlyAdded() && dStricmp( slotName, "broken" ) == 0 ) + setAllBroken( dAtob(newValue) ); +} + +void PxMultiActor::onDeleteNotify( SimObject *obj ) +{ + Parent::onDeleteNotify(obj); + if ( obj == mMount.object ) + unmount(); +} + +void PxMultiActor::onFileNotify() +{ + // Destroy the existing actors and recreate them... + + mWorld->getPhysicsResults(); + _destroyActors(); + _createActors( mResetXfm ); +} + +void PxMultiActor::onPhysicsReset( PhysicsResetEvent reset ) +{ + // Dummies don't create or destroy actors, they just reuse the + // server object's ones. + if ( mIsDummy ) + return; + + // Store the reset transform for later use. + if ( reset == PhysicsResetEvent_Store ) + { + mRootActor->getGlobalPose().getRowMajor44( mResetXfm ); + } + else if ( reset == PhysicsResetEvent_Restore ) + { + // Destroy the existing actors and recreate them to + // ensure they are in the proper mission startup state. + mWorld->getPhysicsResults(); + + _destroyActors(); + _createActors( mResetXfm ); + } + + for ( U32 i = 0; i < mActors.size(); i++ ) + { + if ( !mActors[i] ) + continue; + + mActors[i]->wakeUp(); + } +} + +void PxMultiActor::prepRenderImage( SceneRenderState *state ) +{ + PROFILE_SCOPE( PxMultiActor_PrepRenderImage ); + + if ( !mShapeInstance ) + return; + + Point3F cameraOffset; + getTransform().getColumn(3,&cameraOffset); + cameraOffset -= state->getDiffuseCameraPosition(); + F32 dist = cameraOffset.len(); + if ( dist < 0.01f ) + dist = 0.01f; + + F32 invScale = (1.0f/getMax(getMax(mObjScale.x,mObjScale.y),mObjScale.z)); + + S32 dl = mShapeInstance->setDetailFromDistance( state, dist * invScale ); + if ( dl < 0 ) + return; + + GFXTransformSaver saver; + + // Set up our TS render state here. + TSRenderState rdata; + rdata.setSceneState( state ); + + // We might have some forward lit materials + // so pass down a query to gather lights. + LightQuery query; + query.init( getWorldSphere() ); + rdata.setLightQuery( &query ); + + MatrixF mat = getRenderTransform(); + mat.scale( getScale() ); + GFX->setWorldMatrix( mat ); + + if ( mDebugRender || Con::getBoolVariable( "$PxDebug::render", false ) ) + { + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &PxMultiActor::_debugRender ); + ri->type = RenderPassManager::RIT_Object; + state->getRenderPass()->addInst( ri ); + } + else + mShapeInstance->render( rdata ); +} + +void PxMultiActor::_debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ) +{ + if ( mShapeInstance ) + { + GFXTransformSaver saver; + + MatrixF mat = getRenderTransform(); + mat.scale( mObjScale ); + GFX->multWorld( mat ); + + //mShapeInstance->renderDebugNodes(); + } + + Vector *actors = &mActors; + if ( mIsDummy ) + { + PxMultiActor *serverObj = static_cast( mServerObject.getObject() ); + if ( serverObj ) + actors = &serverObj->mActors; + } + + if ( !actors ) + return; + + for ( U32 i = 0; i < actors->size(); i++ ) + { + NxActor *pActor = (*actors)[i]; + if ( !pActor ) + continue; + + PxUtils::drawActor( pActor ); + } +} + +void PxMultiActor::_onJointBreak( NxReal breakForce, NxJoint &brokenJoint ) +{ + // Dummies do not have physics objects + // and shouldn't receive this callback. + if ( mIsDummy ) + return; + + NxActor *actor0 = NULL; + NxActor *actor1 = NULL; + brokenJoint.getActors( &actor0, &actor1 ); + NxMat34 parentPose = actor0->getGlobalPose(); + + Point3F jointPos = pxCast( brokenJoint.getGlobalAnchor() ); + + PxUserData *jointData = PxUserData::getData( brokenJoint ); + setBroken( parentPose, NxVec3( 0.0f ), jointData, true ); + + // NOTE: We do not NULL the joint in the list, + // or release it here, as we allow it to be released + // by the _destroyActors function on a reset or destruction + // of the PxMultiActor. +} + +void PxMultiActor::_onContact( PhysicsUserData *us, + PhysicsUserData *them, + const Point3F &hitPoint, + const Point3F &hitForce ) +{ + PxUserData *data = (PxUserData*)us; + if ( data && + !data->mIsBroken && + hitForce.len() > mDataBlock->breakForce ) + setAllBroken( true ); +} + +U32 PxMultiActor::packUpdate(NetConnection *con, U32 mask, BitStream *stream) +{ + U32 retMask = Parent::packUpdate(con, mask, stream); + + stream->writeFlag( mDebugRender ); + + stream->writeFlag( mask & SleepMask ); + + if ( stream->writeFlag( mask & WarpMask ) ) + { + stream->writeAffineTransform( getTransform() ); + } + else if ( stream->writeFlag( mask & MoveMask ) ) + { + /* + stream->writeAffineTransform( getTransform() ); + + NxActor *actor = mActors[ mDataBlock->correctionNodes[0] ]; + + const NxVec3& linVel = actor->getLinearVelocity(); + stream->write( linVel.x ); + stream->write( linVel.y ); + stream->write( linVel.z ); + */ + } + + // This internally uses the mask passed to it. + if ( mLightPlugin ) + retMask |= mLightPlugin->packUpdate( this, LightMask, con, mask, stream ); + + return retMask; +} + + +void PxMultiActor::unpackUpdate(NetConnection *con, BitStream *stream) +{ + Parent::unpackUpdate(con, stream); + + mDebugRender = stream->readFlag(); + + if ( stream->readFlag() ) // SleepMask + { + for ( S32 i = 0; i < mActors.size(); i++ ) + { + NxActor *actor = mActors[i]; + + if ( !actor ) + continue; + + if ( actor ) + actor->putToSleep(); + } + } + + if ( stream->readFlag() ) // WarpMask + { + // If we set a warp mask, + // we need to instantly move + // the actor to the new position + // without applying any corrections. + MatrixF mat; + stream->readAffineTransform( &mat ); + + applyWarp( mat, true, false ); + } + else if ( stream->readFlag() ) // MoveMask + { + /* + MatrixF mat; + stream->readAffineTransform( &mat ); + + NxVec3 linVel, angVel; + stream->read( &linVel.x ); + stream->read( &linVel.y ); + stream->read( &linVel.z ); + + applyCorrection( mat, linVel, angVel ); + */ + } +/* + if ( stream->readFlag() ) // ImpulseMask + { + // TODO : Set up correction nodes. + + NxVec3 linVel; + stream->read( &linVel.x ); + stream->read( &linVel.y ); + stream->read( &linVel.z ); + + NxActor *actor = mActors[ mDataBlock->correctionNodes[0] ]; + + if ( actor ) + { + mWorld->releaseWriteLock(); + actor->setLinearVelocity( linVel ); + mStartImpulse.zero(); + } + else + mStartImpulse.set( linVel.x, linVel.y, linVel.z ); + } +*/ + if ( mLightPlugin ) + mLightPlugin->unpackUpdate( this, con, stream ); +} + +void PxMultiActor::setScale( const VectorF& scale ) +{ + if ( scale == getScale() ) + return; + + // This is so that the level + // designer can change the scale + // of a PhysXSingleActor in the editor + // and have the PhysX representation updated properly. + + // First we call the parent's setScale + // so that the ScaleMask can be set. + Parent::setScale( scale ); + + // Check to see if the scale has really changed. + if ( !isProperlyAdded() || mActorScale.equal( scale ) ) + return; + + // Recreate the physics actors. + _destroyActors(); + _createActors( getTransform() ); +} + +void PxMultiActor::applyWarp( const MatrixF& newMat, bool interpRender, bool sweep ) +{ + // Do we have actors to move? + if ( mRootActor ) + { + // Get ready to change the physics state. + mWorld->releaseWriteLock(); + + /// Convert the new transform to nx. + NxMat34 destXfm; + destXfm.setRowMajor44( newMat ); + + // Get the inverse of the root actor transform + // so we can move all the actors relative to it. + NxMat34 rootInverseXfm; + mRootActor->getGlobalPose().getInverse( rootInverseXfm ); + + // Offset all the actors. + MatrixF tMat; + NxMat34 newXfm, relXfm; + for ( S32 i = 0; i < mActors.size(); i++ ) + { + NxActor *actor = mActors[i]; + if ( !actor ) + continue; + + const bool isKinematic = actor->readBodyFlag( NX_BF_KINEMATIC ); + + // Stop any velocity on it. + if ( !isKinematic ) + { + actor->setAngularVelocity( NxVec3( 0.0f ) ); + actor->setLinearVelocity( NxVec3( 0.0f ) ); + } + + // Get the transform relative to the current root. + relXfm.multiply( actor->getGlobalPose(), rootInverseXfm ); + + /* + if ( sweep ) + { + actor->getGl obalPose().getRowMajor44( mResetPos[i] ); + sweepTest( &newMat ); + } + */ + + // + newXfm.multiply( relXfm, destXfm ); + //if ( isKinematic ) + //actor->moveGlobalPose( newXfm ); + //else + actor->setGlobalPose( newXfm ); + + // Reset the delta. + Delta &delta = mActorDeltas[i]; + delta.pos = pxCast( newXfm.t ); + newXfm.getRowMajor44( tMat ); + delta.rot.set( tMat ); + + if ( !interpRender ) + { + mActorDeltas[i].lastPos = mActorDeltas[i].pos; + mActorDeltas[i].lastRot = mActorDeltas[i].rot; + } + } + } + + Parent::setTransform( newMat ); + + mDelta.pos = newMat.getPosition(); + mDelta.rot = newMat; + + if ( !interpRender ) + { + mDelta.lastPos = mDelta.pos; + mDelta.lastRot = mDelta.rot; + } +} + +/* +bool PxMultiActor::_setPositionField( void *obj, const char *data ) +{ + PxMultiActor *object = reinterpret_cast( obj ); + + MatrixF transform( object->getTransform() ); + Con::setData( TypeMatrixPosition, &transform, 0, 1, &data ); + + object->setTransform( transform ); + + return false; +} + +const char* PxMultiActor::_getPositionField( void *obj, const char *data ) +{ + PxMultiActor *object = reinterpret_cast( obj ); + return Con::getData( TypeMatrixPosition, + &object->mObjToWorld, + 0 ); +} + +bool PxMultiActor::_setRotationField( void *obj, const char *data ) +{ + PxMultiActor *object = reinterpret_cast( obj ); + + MatrixF transform( object->getTransform() ); + Con::setData( TypeMatrixRotation, &transform, 0, 1, &data ); + + object->setTransform( transform ); + + return false; +} + +const char* PxMultiActor::_getRotationField( void *obj, const char *data ) +{ + PxMultiActor *object = reinterpret_cast( obj ); + return Con::getData( TypeMatrixRotation, + &object->mObjToWorld, + 0 ); +} +*/ + +void PxMultiActor::setTransform( const MatrixF& mat ) +{ + applyWarp( mat, false, true ); + setMaskBits( WarpMask ); +} + +void PxMultiActor::mountObject( SceneObject *obj, U32 node ) +{ + if (obj->getObjectMount()) + obj->unmount(); + + obj->mountObject( this, (node >= 0 && node < PxMultiActorData::NumMountPoints) ? node: 0 ); +} + + +void PxMultiActor::unmountObject( SceneObject *obj ) +{ + obj->unmountObject( this ); +} + +bool PxMultiActor::_getNodeTransform( U32 nodeIdx, MatrixF *outXfm ) +{ + if ( !mShapeInstance ) + return false; + + PxMultiActor *actorOwner = this; + if ( mIsDummy ) + { + actorOwner = static_cast( mServerObject.getObject() ); + if ( !actorOwner ) + return false; + } + + TSShape *shape = mShapeInstance->getShape(); + String nodeName = shape->getNodeName( nodeIdx ); + + NxActor *pActor = NULL; + UTF8 comparisonName[260] = { 0 }; + S32 dummy = -1; + + // Convert the passed node name to a valid actor name. + dStrcpy( comparisonName, String::GetTrailingNumber( nodeName, dummy ) ); + dSprintf( comparisonName, sizeof( comparisonName ), "%s_pxactor", comparisonName ); + + // If we have an actor with that name, we are done. + pActor = actorOwner->_findActor( comparisonName ); + if ( pActor ) + { + pActor->getGlobalPose().getRowMajor44( *outXfm ); + return true; + } + + // Check if the parent node has an actor... + + S32 parentIdx = shape->nodes[nodeIdx].parentIndex; + if ( parentIdx == -1 ) + return false; + + const String &parentName = shape->getNodeName( parentIdx ); + dStrcpy( comparisonName, String::GetTrailingNumber( parentName, dummy ) ); + dSprintf( comparisonName, sizeof( comparisonName ), "%s_pxactor", comparisonName ); + + pActor = actorOwner->_findActor( comparisonName ); + if ( !pActor ) + return false; + + MatrixF actorMat; + pActor->getGlobalPose().getRowMajor44( actorMat ); + + MatrixF nmat; + QuatF q; + TSTransform::setMatrix( shape->defaultRotations[nodeIdx].getQuatF(&q),shape->defaultTranslations[nodeIdx],&nmat); + *outXfm->mul( actorMat, nmat ); + + return true; +} + +void PxMultiActor::getMountTransform(U32 mountPoint,MatrixF* mat) +{ + // Returns mount point to world space transform + if (mountPoint < PxMultiActorData::NumMountPoints) { + S32 ni = mDataBlock->mountPointNode[mountPoint]; + if (ni != -1) { + if ( _getNodeTransform( ni, mat ) ) + return; + } + } + *mat = mObjToWorld; +} + +void PxMultiActor::getRenderMountTransform(U32 mountPoint,MatrixF* mat) +{ + // Returns mount point to world space transform + if (mountPoint < PxMultiActorData::NumMountPoints) { + S32 ni = mDataBlock->mountPointNode[mountPoint]; + if (ni != -1) { + if ( _getNodeTransform( ni, mat ) ) + return; + } + } + *mat = getRenderTransform(); +} + +void PxMultiActor::processTick( const Move *move ) +{ + PROFILE_SCOPE( PxMultiActor_ProcessTick ); + + // Set the last pos/rot to the + // values of the previous tick for interpolateTick. + mDelta.lastPos = mDelta.pos; + mDelta.lastRot = mDelta.rot; + + /* + NxActor *corrActor = mActors[ mDataBlock->correctionNodes[0] ]; + + if ( corrActor->isSleeping() || corrActor->readBodyFlag( NX_BF_FROZEN ) ) + { + if ( !mSleepingLastTick ) + setMaskBits( WarpMask | SleepMask ); + + mSleepingLastTick = true; + + // HACK! Refactor sleeping so that we don't + // sleep when only one correction actor does. + _updateBounds(); + + return; + } + + mSleepingLastTick = false; + */ + + MatrixF mat; + Vector *actors; + + if ( mIsDummy ) + { + PxMultiActor *serverObj = static_cast( mServerObject.getObject() ); + if ( !serverObj ) + return; + + mat = serverObj->getTransform(); + actors = &serverObj->mActors; + } + else + { + // Container buoyancy & drag + _updateContainerForces(); + + // Save the transform from the root actor. + mRootActor->getGlobalPose().getRowMajor44( mat ); + actors = &mActors; + } + + // Update the transform and the root delta. + Parent::setTransform( mat ); + mDelta.pos = mat.getPosition(); + mDelta.rot.set( mat ); + + // On the client we update the individual + // actor deltas as well for interpolation. + if ( isClientObject() ) + { + PROFILE_SCOPE( PxMultiActor_ProcessTick_UpdateDeltas ); + + for ( U32 i = 0; i < mActorDeltas.size(); i++ ) + { + if ( !(*actors)[i] ) + continue; + + Delta &delta = mActorDeltas[i]; + + // Store the last position. + delta.lastPos = delta.pos; + delta.lastRot = delta.rot; + + // Get the new position. + (*actors)[i]->getGlobalPose().getRowMajor44( (NxF32*)mat ); + + // Calculate the delta between the current + // global pose and the last global pose. + delta.pos = mat.getPosition(); + delta.rot.set( mat ); + } + } + + // Update the bounding box to match the physics. + _updateBounds(); + + // Set the MoveMask so this will be updated to the client. + //setMaskBits( MoveMask ); +} + +void PxMultiActor::interpolateTick( F32 delta ) +{ + PROFILE_SCOPE( PxMultiActor_InterpolateTick ); + + Point3F interpPos; + QuatF interpRot; + { + // Interpolate the position based on the delta. + interpPos.interpolate( mDelta.pos, mDelta.lastPos, delta ); + + // Interpolate the rotation based on the delta. + interpRot.interpolate( mDelta.rot, mDelta.lastRot, delta ); + + // Set up the interpolated transform. + MatrixF interpMat; + interpRot.setMatrix( &interpMat ); + interpMat.setPosition( interpPos ); + + Parent::setRenderTransform( interpMat ); + } + + PxMultiActor *srcObj = NULL; + if ( mIsDummy ) + srcObj = static_cast( mServerObject.getObject() ); + else + srcObj = this; + + // JCF: to disable applying NxActor positions to the renderable mesh + // you can uncomment this line. + //srcObj = NULL; + if ( mShapeInstance && srcObj != NULL ) + { + mShapeInstance->animate(); + getDynamicXfms( srcObj, delta ); + } +} + +/* +void PxMultiActor::sweepTest( MatrixF *mat ) +{ + NxVec3 nxCurrPos = getPosition(); + + // If the position is zero, + // the parent hasn't been updated yet + // and we don't even need to do the sweep test. + // This is a fix for a problem that was happening + // where on the add of the PhysXSingleActor, it would + // set the position to a very small value because it would be getting a hit + // even though the current position was 0. + if ( nxCurrPos.isZero() ) + return; + + // Set up the flags and the query structure. + NxU32 flags = NX_SF_STATICS | NX_SF_DYNAMICS; + + NxSweepQueryHit sweepResult; + dMemset( &sweepResult, 0, sizeof( sweepResult ) ); + + NxVec3 nxNewPos = mat->getPosition(); + + // Get the velocity which will be our sweep direction and distance. + NxVec3 nxDir = nxNewPos - nxCurrPos; + if ( nxDir.isZero() ) + return; + + NxActor *corrActor = mActors[ mDataBlock->correctionNodes[0] ]; + + // Get the scene and do the sweep. + corrActor->wakeUp(); + corrActor->linearSweep( nxDir, flags, NULL, 1, &sweepResult, NULL ); + + if ( sweepResult.hitShape && sweepResult.t < nxDir.magnitude() ) + { + nxDir.normalize(); + nxDir *= sweepResult.t; + nxCurrPos += nxDir; + + mat->setPosition( Point3F( nxCurrPos.x, nxCurrPos.y, nxCurrPos.z ) ); + } +} +*/ + +/* +void PxMultiActor::applyCorrection( const MatrixF& mat, const NxVec3& linVel, const NxVec3& angVel ) +{ + // Sometimes the actor hasn't been + // created yet during the call from unpackUpdate. + NxActor *corrActor = mActors[ mDataBlock->correctionNodes[0] ]; + + if ( !corrActor || mForceSleep ) + return; + + NxVec3 newPos = mat.getPosition(); + NxVec3 currPos = getPosition(); + + NxVec3 offset = newPos - currPos; + + // If the difference isn't large enough, + // just set the new transform, no correction. + if ( offset.magnitude() > 0.3f ) + { + // If we're going to set the linear or angular velocity, + // we do it before we add a corrective force, since it would be overwritten otherwise. + NxVec3 currLinVel, currAngVel; + currLinVel = corrActor->getLinearVelocity(); + currAngVel = corrActor->getAngularVelocity(); + + // Scale the corrective force by half, + // otherwise it will over correct and oscillate. + NxVec3 massCent = corrActor->getCMassGlobalPosition(); + corrActor->addForceAtPos( offset, massCent, NX_SMOOTH_VELOCITY_CHANGE ); + + // If the linear velocity is divergent enough, change to server linear velocity. + if ( (linVel - currLinVel).magnitude() > 0.3f ) + corrActor->setLinearVelocity( linVel ); + // Same for angular. + if ( (angVel - currAngVel).magnitude() > 0.3f ) + corrActor->setAngularVelocity( angVel ); + } + + Parent::setTransform( mat ); +} +*/ + +void PxMultiActor::_updateBounds() +{ + PROFILE_SCOPE( PxMultiActor_UpdateBounds ); + + if ( mIsDummy ) + { + PxMultiActor *serverObj = static_cast( mServerObject.getObject() ); + if ( !serverObj ) + return; + + mWorldBox = serverObj->getWorldBox(); + mWorldSphere = serverObj->getWorldSphere(); + mObjBox = serverObj->getObjBox(); + mRenderWorldBox = serverObj->getRenderWorldBox(); + mRenderWorldSphere = mWorldSphere; + + return; + } + + NxBounds3 bounds; + bounds.setEmpty(); + + NxBounds3 shapeBounds; + + for ( U32 i = 0; i < mActors.size(); i++ ) + { + NxActor *pActor = mActors[i]; + + if ( !pActor || pActor->readActorFlag( NX_AF_DISABLE_COLLISION ) ) + continue; + + NxShape *const* pShapeArray = pActor->getShapes(); + U32 shapeCount = pActor->getNbShapes(); + for ( U32 i = 0; i < shapeCount; i++ ) + { + // Get the shape's bounds. + pShapeArray[i]->getWorldBounds( shapeBounds ); + + // Combine them into the total bounds. + bounds.combine( shapeBounds ); + } + } + + mWorldBox = pxCast( bounds ); + + mWorldBox.getCenter(&mWorldSphere.center); + mWorldSphere.radius = (mWorldBox.maxExtents - mWorldSphere.center).len(); + + mObjBox = mWorldBox; + mWorldToObj.mul(mObjBox); + + mRenderWorldBox = mWorldBox; + mRenderWorldSphere = mWorldSphere; +} + +void PxMultiActor::getDynamicXfms( PxMultiActor *srcObj, F32 dt ) +{ + PROFILE_SCOPE( PxMultiActor_getDynamicXfms ); + + Vector *torqueXfms = &mShapeInstance->mNodeTransforms; + const MatrixF &objectXfm = getRenderWorldTransform(); + + AssertFatal( torqueXfms->size() == srcObj->mMappedActors.size(), "The two skeletons are different!" ); + + TSShape *shape = mShapeInstance->getShape(); + + // TODO: We're currently preparing deltas and getting + // dynamic xforms even if the object isn't visible. + // we should probably try to delay all this until + // we're about to render. + // + /* + // TODO: Set up deltas! + if ( mCurrPos.empty() || mCurrRot.empty() ) + _prepareDeltas(); + */ + + MatrixF globalXfm; + MatrixF mat, tmp; + QuatF newRot; + Point3F newPos; + + S32 dl = mShapeInstance->getCurrentDetail(); + if ( dl < 0 ) + return; + + const String &detailName = shape->getName( shape->details[dl].nameIndex ); + S32 detailSize = -1; + String::GetTrailingNumber( detailName, detailSize ); + + for( S32 i = 0; i < srcObj->mMappedActors.size(); i++ ) + { + NxActor *actor = srcObj->mMappedActors[i]; + + if ( !actor || actor->readBodyFlag( NX_BF_KINEMATIC ) ) + continue; + + // see if the node at this index is part of the + // currently visible detail level. + if ( srcObj->mMappedActorDL[i] != detailSize ) + continue; + + // Get the right actor delta structure. + U32 index = srcObj->mMappedToActorIndex[i]; + const Delta &delta = mActorDeltas[index]; + + // Do the interpolation. + newRot.interpolate( delta.rot, delta.lastRot, dt ); + newRot.setMatrix( &globalXfm ); + newPos.interpolate( delta.pos, delta.lastPos, dt ); + globalXfm.setPosition( newPos ); + + (*torqueXfms)[i].mul( objectXfm, globalXfm ); + } +} + +void PxMultiActor::applyImpulse( const Point3F &pos, const VectorF &vec ) +{ + // TODO : Implement this based on correction nodes. + /* + if ( !mWorld || !mActor ) + return; + + mWorld->releaseWriteLock(); + + NxVec3 linVel = mActor->getLinearVelocity(); + NxVec3 nxVel( vel.x, vel.y, vel.z ); + + mActor->setLinearVelocity(linVel + nxVel); + */ + + // JCF: something more complex is required to apply forces / breakage + // on only individual actors, and we don't have enough data to do that + // within this method. + + if ( vec.len() > mDataBlock->breakForce ) + setAllBroken( true ); + + NxVec3 nxvec = pxCast( vec ); + NxVec3 nxpos = pxCast( pos ); + + for ( U32 i = 0; i < mActors.size(); i++ ) + { + NxActor *actor = mActors[i]; + if ( actor->isDynamic() && + !actor->readBodyFlag( NX_BF_KINEMATIC ) && + !actor->readActorFlag( NX_AF_DISABLE_COLLISION ) ) + { + actor->addForceAtPos( nxvec, nxpos, NX_IMPULSE ); + } + } + + //setMaskBits( ImpulseMask ); +} + +void PxMultiActor::applyRadialImpulse( const Point3F &origin, F32 radius, F32 magnitude ) +{ + mWorld->releaseWriteLock(); + + // Find all currently enabled actors hit by the impulse radius... + Vector hitActors; + NxVec3 nxorigin = pxCast(origin); + NxSphere impulseSphere( nxorigin, radius ); + + for ( U32 i = 0; i < mActors.size(); i++ ) + { + NxActor *pActor = mActors[i]; + + if ( pActor->readActorFlag( NX_AF_DISABLE_COLLISION ) || + !pActor->isDynamic() || + pActor->readBodyFlag( NX_BF_KINEMATIC ) ) + continue; + + U32 numShapes = pActor->getNbShapes(); + NxShape *const* pShapeArray = pActor->getShapes(); + + for ( U32 j = 0; j < numShapes; j++ ) + { + const NxShape *pShape = pShapeArray[j]; + + if ( pShape->checkOverlapSphere( impulseSphere ) ) + { + hitActors.push_back( pActor ); + break; + } + } + } + + // Apply forces to hit actors, but swap out for broken + // actors first if appropriate... + for ( U32 i = 0; i < hitActors.size(); i++ ) + { + NxActor *pActor = hitActors[i]; + + PxUserData *pUserData = PxUserData::getData( *pActor ); + + // TODO: We should calculate the real force accounting + // for falloff before we break things with it. + + // If we have enough force, and this is an actor that + // can be 'broken' by impacts, break it now. + if ( pUserData && + //pUserData->mCanPush && + pUserData->mBrokenActors.size() > 0 && + magnitude > mDataBlock->breakForce ) + { + setBroken( pActor->getGlobalPose(), + pActor->getLinearVelocity(), + pUserData, + true ); + + // apply force that would have been applied to this actor + // to the broken actors we just enabled. + + for ( U32 j = 0; j < pUserData->mBrokenActors.size(); j++ ) + { + NxActor *pBrokenActor = pUserData->mBrokenActors[j]; + _applyActorRadialForce( pBrokenActor, nxorigin, radius, magnitude ); + } + } + else + { + // Apply force to the actor. + _applyActorRadialForce( pActor, nxorigin, radius, magnitude ); + } + } +} + +void PxMultiActor::_applyActorRadialForce( NxActor *inActor, const NxVec3 &origin, F32 radius, F32 magnitude ) +{ + // TODO: We're not getting a good torque force + // out of explosions because we're not picking + // the nearest point on the actor to the origin + // of the radial force. + + NxVec3 force = inActor->getCMassGlobalPosition() - origin; + NxF32 dist = force.magnitude(); + force.normalize(); + + if ( dist == 0.0f ) + force *= magnitude; + else + force *= mClampF( radius / dist, 0.0f, 1.0f ) * magnitude; + + // HACK: Make the position we push the force thru between the + // actor pos and its center of mass. This gives us some + // rotational force as well as make the covered structure + // explode better. + NxVec3 forcePos = ( inActor->getGlobalPosition() + inActor->getCMassGlobalPosition() ) / 2.0f; + inActor->addForceAtPos( force, forcePos, NX_VELOCITY_CHANGE ); +} + +void PxMultiActor::_updateContainerForces() +{ + if ( !mWorld->getEnabled() ) + return; + + PROFILE_SCOPE( PxMultiActor_updateContainerForces ); + + // Update container drag and buoyancy properties ( for each Actor ) + + for ( U32 i = 0; i < mActors.size(); i++ ) + { + NxActor *pActor = mActors[i]; + + if ( !pActor || + pActor->readBodyFlag(NX_BF_KINEMATIC) || + pActor->readActorFlag(NX_AF_DISABLE_COLLISION) ) + continue; + + // Get world bounds of this actor ( the combination of all shape bounds ) + NxShape *const* shapes = pActor->getShapes(); + NxBounds3 bounds; + bounds.setEmpty(); + NxBounds3 shapeBounds; + + for ( U32 i = 0; i < pActor->getNbShapes(); i++ ) + { + NxShape *pShape = shapes[i]; + pShape->getWorldBounds(shapeBounds); + + bounds.combine( shapeBounds ); + } + + Box3F boundsBox = pxCast(bounds); + + ContainerQueryInfo info; + info.box = boundsBox; + info.mass = pActor->getMass(); + + // Find and retreive physics info from intersecting WaterObject(s) + mContainer->findObjects( boundsBox, WaterObjectType|PhysicalZoneObjectType, findRouter, &info ); + + // Calculate buoyancy and drag + F32 angDrag = mDataBlock->angularDrag; + F32 linDrag = mDataBlock->linearDrag; + F32 buoyancy = 0.0f; + + if ( true ) //info.waterCoverage >= 0.1f) + { + F32 waterDragScale = info.waterViscosity * mDataBlock->waterDragScale; + F32 powCoverage = mPow( info.waterCoverage, 0.25f ); + + if ( info.waterCoverage > 0.0f ) + { + //angDrag = mBuildAngDrag * waterDragScale; + //linDrag = mBuildLinDrag * waterDragScale; + angDrag = mLerp( angDrag, angDrag * waterDragScale, powCoverage ); + linDrag = mLerp( linDrag, linDrag * waterDragScale, powCoverage ); + } + + buoyancy = ( info.waterDensity / mDataBlock->buoyancyDensity ) * mPow( info.waterCoverage, 2.0f ); + } + + // Apply drag (dampening) + pActor->setLinearDamping( linDrag ); + pActor->setAngularDamping( angDrag ); + + // Apply buoyancy force + if ( buoyancy != 0 ) + { + // A little hackery to prevent oscillation + // Based on this blog post: + // (http://reinot.blogspot.com/2005/11/oh-yes-they-float-georgie-they-all.html) + // JCF: disabled! + NxVec3 gravity; + mWorld->getScene()->getGravity(gravity); + //NxVec3 velocity = pActor->getLinearVelocity(); + + NxVec3 buoyancyForce = buoyancy * -gravity * TickSec * pActor->getMass(); + //F32 currHeight = getPosition().z; + //const F32 C = 2.0f; + //const F32 M = 0.1f; + + //if ( currHeight + velocity.z * TickSec * C > info.waterHeight ) + // buoyancyForce *= M; + + pActor->addForceAtPos( buoyancyForce, pActor->getCMassGlobalPosition(), NX_IMPULSE ); + } + + // Apply physical zone forces + if ( info.appliedForce.len() > 0.001f ) + pActor->addForceAtPos( pxCast(info.appliedForce), pActor->getCMassGlobalPosition(), NX_IMPULSE ); + } +} + +/* +ConsoleMethod( PxMultiActor, applyImpulse, void, 3, 3, "applyImpulse - takes a velocity vector to apply") +{ + VectorF vec; + dSscanf( argv[2],"%g %g %g", + &vec.x,&vec.y,&vec.z ); + + object->applyImpulse( vec ); +} +*/ + +void PxMultiActor::setAllBroken( bool isBroken ) +{ + PROFILE_SCOPE( PxMultiActor_SetAllBroken ); + + if ( mIsDummy ) + { + PxMultiActor *serverObj = static_cast( mServerObject.getObject() ); + serverObj->setAllBroken( isBroken ); + return; + } + + mWorld->releaseWriteLock(); + + NxActor *actor0 = NULL; + NxActor *actor1 = NULL; + NxMat34 parentPose; + + for ( U32 i = 0; i < mJoints.size(); i++ ) + { + NxJoint *joint = mJoints[i]; + if ( !joint ) + continue; + + PxUserData *jointData = PxUserData::getData( *joint ); + if ( !jointData ) + continue; + + joint->getActors( &actor0, &actor1 ); + parentPose = actor0->getGlobalPose(); + + setBroken( parentPose, NxVec3(0.0f), jointData, isBroken ); + } + + for ( U32 i = 0; i < mActors.size(); i++ ) + { + NxActor *actor = mActors[i]; + if ( !actor ) + continue; + + PxUserData *actorData = PxUserData::getData( *actor ); + if ( !actorData ) + continue; + + setBroken( actor->getGlobalPose(), + actor->getLinearVelocity(), + actorData, + isBroken ); + } +} + +void PxMultiActor::setBroken( const NxMat34 &parentPose, + const NxVec3 &parentVel, + PxUserData *userData, + bool isBroken ) +{ + PROFILE_SCOPE( PxMultiActor_SetBroken ); + + // TODO: This function is highly inefficent and + // way too complex to follow... the hacked single + // player mode doesn't help. + + // Be careful not to set something broken twice. + if ( isBroken && + userData->mIsBroken == isBroken ) + return; + + userData->mIsBroken = isBroken; + + Vector *hideActors = NULL; + Vector *showActors = NULL; + + if ( isBroken ) + { + hideActors = &userData->mUnbrokenActors; + showActors = &userData->mBrokenActors; + } + else + { + hideActors = &userData->mBrokenActors; + showActors = &userData->mUnbrokenActors; + } + + NxActor *pActor = NULL; + MatrixF tMat; + for ( U32 i = 0; i < hideActors->size(); i++ ) + { + pActor = (*hideActors)[i]; + + pActor->raiseActorFlag( NX_AF_DISABLE_COLLISION ); + pActor->raiseBodyFlag( NX_BF_KINEMATIC ); + pActor->putToSleep(); + + NxShape *const* pShapeArray = pActor->getShapes(); + U32 shapeCount = pActor->getNbShapes(); + for ( U32 i = 0; i < shapeCount; i++ ) + pShapeArray[i]->setFlag( NX_SF_DISABLE_RAYCASTING, true ); + + setMeshHidden( _getMeshName( pActor ), true ); + } + + // Get the client side delta array. + Vector *actorDeltas = NULL; + if ( isClientObject() ) + actorDeltas = &mActorDeltas; + else if ( isServerObject() && PHYSICSMGR->isSinglePlayer() ) + { + PxMultiActor *clientObj = static_cast( getClientObject() ); + if ( clientObj ) + actorDeltas = &clientObj->mActorDeltas; + } + U32 index; + + for ( U32 i = 0; i < showActors->size(); i++ ) + { + pActor = (*showActors)[i]; + + if ( showActors == &userData->mBrokenActors ) + { + NxMat34 pose; + pose.multiply( parentPose, userData->mRelXfm[i] ); + pActor->setGlobalPose( pose ); + + if ( actorDeltas ) + { + for ( U32 j=0; j < mMappedActors.size(); j++ ) + { + if ( mMappedActors[j] == pActor ) + { + index = mMappedToActorIndex[j]; + + // Reset the delta. + Delta &delta = (*actorDeltas)[index]; + delta.pos = pxCast( pose.t ); + pose.getRowMajor44( tMat ); + delta.rot.set( tMat ); + delta.lastPos = delta.pos; + delta.lastRot = delta.rot; + + break; + } + } + } + } + + pActor->clearActorFlag( NX_AF_DISABLE_COLLISION ); + pActor->clearBodyFlag( NX_BF_KINEMATIC ); + pActor->setLinearVelocity( parentVel ); + pActor->wakeUp(); + + NxShape *const* pShapeArray = pActor->getShapes(); + U32 shapeCount = pActor->getNbShapes(); + for ( U32 i = 0; i < shapeCount; i++ ) + pShapeArray[i]->setFlag( NX_SF_DISABLE_RAYCASTING, false ); + + setMeshHidden( _getMeshName(pActor), false ); + } +} + +void PxMultiActor::setAllHidden( bool hide ) +{ + for ( U32 i = 0; i < mShapeInstance->mMeshObjects.size(); i++ ) + mShapeInstance->setMeshForceHidden( i, hide ); +} + +ConsoleMethod( PxMultiActor, setAllHidden, void, 3, 3, "( bool )" + "@brief Hides or unhides all meshes contained in the PxMultiActor.\n\n" + "Hidden meshes will not be rendered.") +{ + object->setAllHidden( dAtob(argv[2]) ); +} + +void PxMultiActor::setMeshHidden( String namePrefix, bool hidden ) +{ + if ( isServerObject() && PHYSICSMGR->isSinglePlayer() ) + { + PxMultiActor *clientObj = static_cast( getClientObject() ); + if ( clientObj ) + clientObj->setMeshHidden( namePrefix, hidden ); + } + + for ( U32 i = 0; i < mShapeInstance->mMeshObjects.size(); i++ ) + { + String meshName = mShapeInstance->getShape()->getMeshName( i ); + + if ( meshName.find( namePrefix ) != String::NPos ) + { + mShapeInstance->setMeshForceHidden( i, hidden ); + return; + } + } + + Con::warnf( "PxMultiActor::setMeshHidden - could not find mesh containing substring (%s)", namePrefix.c_str() ); +} + +ConsoleMethod( PxMultiActor, setBroken, void, 3, 3, "( bool )" + "@brief Sets the PxMultiActor to a broken or unbroken state.\n\n") +{ + object->setAllBroken( dAtob( argv[2] ) ); +} + +void PxMultiActorData::dumpModel() +{ + TSShapeInstance *inst = new TSShapeInstance( shape, true ); + + String path = Platform::getMainDotCsDir(); + path += "/model.dump"; + + FileStream *st; + if((st = FileStream::createAndOpen( path, Torque::FS::File::Write )) != NULL) + { + if ( inst ) + inst->dump( *st ); + else + Con::errorf( "PxMultiActor::dumpModel, no ShapeInstance." ); + + delete st; + } + else + Con::errorf( "PxMultiActor::dumpModel, error opening dump file." ); +} + +ConsoleMethod( PxMultiActorData, dumpModel, void, 2, 2, + "@brief Dumps model hierarchy and details to a file.\n\n" + "The file will be created as \'model.dump\' in the game folder. " + "If model.dump already exists, it will be overwritten.\n\n") +{ + object->dumpModel(); +} + +ConsoleMethod( PxMultiActor, setMeshHidden, void, 4, 4, "(string meshName, bool isHidden)" + "@brief Prevents the provided mesh from being rendered.\n\n") +{ + object->setMeshHidden( argv[2], dAtob( argv[3] ) ); +} + +void PxMultiActor::listMeshes( const String &state ) const +{ + if ( mShapeInstance ) + mShapeInstance->listMeshes( state ); +} + +ConsoleMethod( PxMultiActor, listMeshes, void, 3, 3, "(enum Hidden/Shown/All)" + "@brief Lists all meshes of the provided type in the console window.\n\n" + "@param All Lists all of the %PxMultiActor's meshes.\n" + "@param Hidden Lists all of the %PxMultiActor's hidden meshes.\n" + "@param Shown Lists all of the %PxMultiActor's visible meshes.\n") +{ + object->listMeshes( argv[2] ); +}; + +ConsoleMethod( PxMultiActorData, reload, void, 2, 2, "" + "@brief Reloads all data used for the PxMultiActorData.\n\n" + "If the reload sucessfully completes, all PxMultiActor's will be notified.\n\n") +{ + object->reload(); +} \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx/pxMultiActor.h b/Engine/source/T3D/physics/physx/pxMultiActor.h new file mode 100644 index 000000000..891ca4ac8 --- /dev/null +++ b/Engine/source/T3D/physics/physx/pxMultiActor.h @@ -0,0 +1,398 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PXMULTIACTOR_H +#define _PXMULTIACTOR_H + +#ifndef _GAMEBASE_H_ +#include "T3D/gameBase/gameBase.h" +#endif +#ifndef __RESOURCE_H__ +#include "core/resource.h" +#endif +#ifndef _T3D_PHYSICS_PHYSICSPLUGIN_H_ +#include "T3D/physics/physicsPlugin.h" +#endif +#ifndef _PHYSX_H_ +#include "T3D/physics/physx/px.h" +#endif +#ifndef _STRINGUNIT_H_ +#include "core/strings/stringUnit.h" +#endif +#ifndef _PHYSICS_PHYSICSUSERDATA_H_ +#include "T3D/physics/physicsUserData.h" +#endif +#ifndef _TSSHAPE_H_ +#include "ts/tsShape.h" +#endif + + +class TSShapeInstance; +class BaseMatInstance; +class PxMultiActor; +class PxWorld; +class PxMaterial; +class NxScene; +class NxActor; +class NxShape; +class NxCompartment; +class NxJoint; +class NxMat34; +class NxVec3; +class ParticleEmitterData; + + +namespace NXU +{ + class NxuPhysicsCollection; +} + + +class PxUserData : public PhysicsUserData +{ +public: + + /// The constructor. + PxUserData() + : PhysicsUserData(), + mIsBroken( false ), + mParticleEmitterData( NULL ) + { + } + + static PxUserData* getData( const NxActor &actor ) + { + PxUserData *result = (PxUserData*)actor.userData; + + AssertFatal( !result || typeid( *result ) == typeid( PxUserData ), + "PxUserData::getData - The pointer is the wrong type!" ); + + return result; + } + + static PxUserData* getData( const NxJoint &joint ) + { + PxUserData *result = (PxUserData*)joint.userData; + + AssertFatal( !result || typeid( *result ) == typeid( PxUserData ), + "PxUserData::getData - The pointer is the wrong type!" ); + + return result; + } + + typedef Signal JointBreakSignal; + + JointBreakSignal& getOnJointBreakSignal() { return mOnJointBreakSignal; } + + // Breakable stuff... + Vector mUnbrokenActors; + Vector mBrokenActors; + Vector mRelXfm; + ParticleEmitterData *mParticleEmitterData; + bool mIsBroken; + JointBreakSignal mOnJointBreakSignal; +}; + + +class ParticleEmitterData; + +class PxMultiActorData : public GameBaseData +{ + typedef GameBaseData Parent; + +public: + + PxMultiActorData(); + virtual ~PxMultiActorData(); + + DECLARE_CONOBJECT(PxMultiActorData); + + static void initPersistFields(); + + void packData(BitStream* stream); + void unpackData(BitStream* stream); + + bool preload( bool server, String &errorBuffer ); + //bool onAdd(); + + void allocPrimBuffer( S32 overrideSize = -1 ); + + bool _loadCollection( const UTF8 *path, bool isBinary ); + + void _onFileChanged( const Torque::Path &path ); + + void reload(); + + void dumpModel(); + + Signal mReloadSignal; + +public: + + // Rendering + StringTableEntry shapeName; + Resource shape; + + PxMaterial *material; + + /// Filename to load the physics actor from. + StringTableEntry physXStream; + + enum + { + NumMountPoints = 32, + MaxCorrectionNodes = 2 + }; + + StringTableEntry correctionNodeNames[MaxCorrectionNodes]; + StringTableEntry mountNodeNames[NumMountPoints]; + S32 correctionNodes[MaxCorrectionNodes]; + S32 mountPointNode[NumMountPoints]; ///< Node index of mountPoint + + /// If true no network corrections will + /// be done during gameplay. + bool noCorrection; + + /// Physics collection that holds the actor + /// and all associated shapes and data. + NXU::NxuPhysicsCollection *collection; + + bool createActors( NxScene *scene, + NxCompartment *compartment, + const NxMat34 *nxMat, + const Point3F& scale, + Vector *outActors, + Vector *outShapes, + Vector *outJoints, + Vector *outActorUserProperties, + Vector *outJointUserProperties ); + + /// Angular and Linear Drag (dampening) is scaled by this when in water. + F32 waterDragScale; + + /// The density of this object (for purposes of buoyancy calculation only). + F32 buoyancyDensity; + + F32 angularDrag; + F32 linearDrag; + + /// If this flag is set to true, + /// the physics actors will only be + /// created on the client, and the server + /// object is only responsible for ghosting. + /// Objects with this flag set will never stop + /// the physics player from moving through them. + bool clientOnly; + + bool singlePlayerOnly; + + /// When applyImpulse is passed a force of this magnitude or greater + /// any actors hit by the force vector that have broken versions + /// will become 'broken'. + F32 breakForce; +}; + + +class PxMultiActor : public GameBase +{ + typedef GameBase Parent; + + enum MaskBits + { + MoveMask = Parent::NextFreeMask << 0, + WarpMask = Parent::NextFreeMask << 1, + LightMask = Parent::NextFreeMask << 2, + SleepMask = Parent::NextFreeMask << 3, + ForceSleepMask = Parent::NextFreeMask << 4, + ImpulseMask = Parent::NextFreeMask << 5, + UpdateMask = Parent::NextFreeMask << 6, + MountedMask = Parent::NextFreeMask << 7, + NextFreeMask = Parent::NextFreeMask << 8 + }; + +public: + + PxMultiActor(); + + DECLARE_CONOBJECT( PxMultiActor ); + static void initPersistFields(); + + // SimObject + bool onAdd(); + void onRemove(); + void inspectPostApply(); + void onPhysicsReset( PhysicsResetEvent reset ); + void onStaticModified( const char *slotName, const char *newValue ); + void onDeleteNotify( SimObject *obj ); + + // NetObject + U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream ); + void unpackUpdate( NetConnection *conn, BitStream *stream ); + + // SceneObject + void prepRenderImage( SceneRenderState *state ); + void setScale( const VectorF &scale ); + void setTransform( const MatrixF &mat ); + virtual void mountObject( SceneObject *obj, U32 node ); + virtual void unmountObject( SceneObject *obj ); + virtual void getMountTransform( U32 mountPoint, MatrixF *mat ); + virtual void getRenderMountTransform( U32 index, MatrixF *mat ); + + // GameBase + virtual bool onNewDataBlock( GameBaseData *dptr, bool reload ); + virtual void processTick( const Move *move ); + virtual void interpolateTick( F32 delta ); + virtual void applyImpulse( const Point3F &pos, const VectorF &vec ); + virtual void applyRadialImpulse( const Point3F &origin, F32 radius, F32 magnitude ); + + /// PxMultiActor + /// @{ + + /// Set visibility of all broken/unbroken meshes to match this state. + void setAllBroken( bool isBroken ); + + /// Sets up actors and meshes associated with the passed joint to reflect + /// the desired state. + void setBroken( const NxMat34 &parentPose, + const NxVec3 &parentVel, + PxUserData *userData, + bool isBroken ); + + /// + void setMeshHidden( String namePrefix, bool hidden ); + + void setAllHidden( bool hide ); + + void listMeshes( const String &state ) const; + + void _onJointBreak( NxReal breakForce, NxJoint &brokenJoint ); + + void _onContact( PhysicsUserData *us, + PhysicsUserData *them, + const Point3F &hitPoint, + const Point3F &hitForce ); + + void applyWarp( const MatrixF& mat, bool interpRender, bool sweep ); + + void getDynamicXfms( PxMultiActor *srcObj, F32 dt ); + + /// @} + +protected: + + /// This creates the physics objects. + bool _createActors( const MatrixF &xfm ); + + /// Creates a PxUserData for a joint and parses userProperties into it. + PxUserData* _createJointUserData( NxJoint *joint, String &userProperties ); + + /// Creates a PxUserData and parses userProperties into it. + PxUserData* _createActorUserData( NxActor *actor, String &userProperties ); + + /// Called to cleanup the physics objects. + void _destroyActors(); + + NxActor* _findActor( const String &actorName ) const; + + /// Get the corresponding meshName for a given actor. + String _getMeshName( const NxActor *actor ) const; + + /// + void _updateBounds(); + + void _updateContainerForces(); + + void _debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); + + void onFileNotify(); + + void _applyActorRadialForce( NxActor *inActor, const NxVec3 &origin, F32 radius, F32 magnitude ); + + void _updateDeltas( bool clearDelta ); + + bool _getNodeTransform( U32 nodeIdx, MatrixF *outXfm ); + +protected: + + PxMultiActorData *mDataBlock; + + PxWorld *mWorld; + + Vector mActors; + Vector mMappedActors; + Vector mMappedToActorIndex; + Vector mMappedActorDL; + Vector mJoints; + Vector mShapes; + + /// This is the root actor whose transform is the + /// transform of this SceneObject. + NxActor *mRootActor; + + TSShapeInstance *mShapeInstance; + Resource mDebrisShape; + + struct Delta + { + Point3F pos; + Point3F lastPos; + QuatF rot; + QuatF lastRot; + }; + + Delta mDelta; + + Vector mActorDeltas; + + /// The transform of this actor when it was first + /// created. It is used to reset the physics state + /// when the editor is enabled. + MatrixF mResetXfm; + + + /// The userdata object assigned to all actors + /// and joints of this multi-actor. + //PxUserData mUserData; + + /// + //Vector mRelXfms; + + /// This is the scale the actors were built at and + /// is used to decide if we need to recreate them. + VectorF mActorScale; + //F32 mBuildAngDrag; + //F32 mBuildLinDrag; + + VectorF mStartImpulse; + + bool mDebugRender; + + /// A helper set to true if is a client object and + /// is a singlePlayerOnly object. + bool mIsDummy; + + /// Helper for + bool mBroken; +}; + +#endif // _PXMULTIACTOR_H + diff --git a/Engine/source/T3D/physics/physx/pxPlayer.cpp b/Engine/source/T3D/physics/physx/pxPlayer.cpp new file mode 100644 index 000000000..3aec57495 --- /dev/null +++ b/Engine/source/T3D/physics/physx/pxPlayer.cpp @@ -0,0 +1,420 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physX/pxPlayer.h" + +#include "T3D/physics/physicsPlugin.h" +#include "T3D/physics/physX/pxWorld.h" +#include "T3D/physics/physX/pxCasts.h" +#include "collision/collision.h" +//#include "gfx/gfxDrawUtil.h" +//#include "sim/netConnection.h" + + +PxPlayer::PxPlayer() + : PhysicsPlayer(), + mController( NULL ), + mWorld( NULL ), + mObject( NULL ), + mSkinWidth( 0.1f ), + mOriginOffset( 0.0f ) +{ + PHYSICSMGR->getPhysicsResetSignal().notify( this, &PxPlayer::_onPhysicsReset ); +} + +PxPlayer::~PxPlayer() +{ + _releaseController(); + PHYSICSMGR->getPhysicsResetSignal().remove( this, &PxPlayer::_onPhysicsReset ); +} + +void PxPlayer::_releaseController() +{ + if ( mController ) + { + mController->getActor()->userData = NULL; + mWorld->releaseController( *mController ); + mController = NULL; + } +} + +void PxPlayer::init( const char *type, + const Point3F &size, + F32 runSurfaceCos, + F32 stepHeight, + SceneObject *obj, + PhysicsWorld *world ) +{ + AssertFatal( obj, "PxPlayer::init - Got a null scene object!" ); + AssertFatal( world, "PxPlayer::init - Got a null world!" ); + AssertFatal( dynamic_cast( world ), "PxPlayer::init - The world is the wrong type!" ); + + // Cleanup any previous controller. + _releaseController(); + + mObject = obj; + mWorld = (PxWorld*)world; + mOriginOffset = size.z * 0.5f; + + //if ( dStricmp( type, "Capsule" ) == 0 ) + { + NxCapsuleControllerDesc desc; + desc.skinWidth = 0.05f; // Expose? + desc.radius = getMax( size.x, size.y ) * 0.5f; + desc.radius -= desc.skinWidth; + desc.height = size.z - ( desc.radius * 2.0f ); + desc.height -= desc.skinWidth * 2.0f; + + desc.climbingMode = CLIMB_CONSTRAINED; + desc.position.set( 0, 0, 0 ); + desc.upDirection = NX_Z; + desc.callback = this; // TODO: Fix this as well! + desc.slopeLimit = runSurfaceCos; + desc.stepOffset = stepHeight; + mController = mWorld->createController( desc ); + } + //else + { + //mColShape = new btBoxShape( btVector3( 0.5f, 0.5f, 1.0f ) ); + //mOriginOffset = 1.0f; + } + + // Put the kinematic actor on group 29. + NxActor *kineActor = mController->getActor(); + kineActor->setGroup( 29 ); + NxShape *const *shapes = kineActor->getShapes(); + for ( U32 i=0; i < kineActor->getNbShapes(); i++ ) + shapes[i]->setGroup( 29 ); + + mUserData.setObject( obj ); + kineActor->userData = &mUserData; +} + +void PxPlayer::_onPhysicsReset( PhysicsResetEvent reset ) +{ + // The PhysX controller will crash out if it doesn't clear its + // list of static elements when they are deleted. By calling this + // on physics events we clear the cache and we don't get crashes. + // + // This all depends on not doing moves and sweeps when the + // simulation is paused... we need to stop operating. + + if ( mController ) + mController->reportSceneChanged(); +} + +Point3F PxPlayer::move( const VectorF &disp, CollisionList &outCol ) +{ + AssertFatal( mController, "PxPlayer::move - The controller is null!" ); + + // Return the last position if the simulation is stopped. + // + // See PxPlayer::_onPhysicsReset + if ( !mWorld->isEnabled() ) + { + Point3F newPos = pxCast( mController->getDebugPosition() ); + newPos.z -= mOriginOffset; + //outCol->point = newPos; + //outCol->normal.set( 0, 0, 1 ); + return newPos; + } + + mWorld->releaseWriteLock(); + + mCollisionList = &outCol; + + // PhysX 2.8.4 checks up an up displacement and if found will assume + // the player is flying and remove the step offset. If we have a small + // z displacement here, zero it out. + NxVec3 dispNx( disp.x, disp.y, disp.z ); + if (mIsZero(disp.z)) + dispNx.z = 0.0f; + + NxU32 activeGroups = 0xFFFFFFFF; + activeGroups &= ~( 1<<31 ); // Skip activeGroup for triggers ( 31 ) + activeGroups &= ~( 1<<30 ); // Skip activeGroup for debris / non interactive dynamics ( 30 ) + + NxU32 collisionFlags = NXCC_COLLISION_SIDES | NXCC_COLLISION_DOWN | NXCC_COLLISION_UP; + + mController->move( dispNx, activeGroups, 0.0001f, collisionFlags ); + + Point3F newPos = pxCast( mController->getDebugPosition() ); + newPos.z -= mOriginOffset; + + mCollisionList = NULL; + + return newPos; +} + +NxControllerAction PxPlayer::onShapeHit( const NxControllerShapeHit& hit ) +{ + if (!mCollisionList || mCollisionList->getCount() >= CollisionList::MaxCollisions) + return NX_ACTION_NONE; + + NxActor *actor = &hit.shape->getActor(); + PhysicsUserData *userData = PhysicsUserData::cast( actor->userData ); + + if ( actor->readActorFlag( NX_AF_DISABLE_RESPONSE ) ) + return NX_ACTION_NONE; + + // Fill out the Collision + // structure for use later. + Collision &col = mCollisionList->increment(); + dMemset( &col, 0, sizeof( col ) ); + + col.normal = pxCast( hit.worldNormal ); + col.point.set( hit.worldPos.x, hit.worldPos.y, hit.worldPos.z ); + col.distance = hit.length; + if ( userData ) + col.object = userData->getObject(); + + // If the collision direction is sideways then modify the collision normal + // to remove any z component. This takes care of any sideways collisions + // with the round bottom of the capsule when it comes to the Player class + // velocity calculations. We want all sideways collisions to be treated + // as if they hit the side of a cylinder. + if (mIsZero(hit.dir.z)) + { + if (col.normal.z > 0.0f) + { + // This will only remove the z component of the collision normal + // for the bottom of the character controller, which would hit during + // a step. We'll leave the top hemisphere of the character's capsule + // alone as bumping one's head is an entirely different story. This + // helps with low doorways. + col.normal.z = 0.0f; + col.normal.normalizeSafe(); + } + } + else + { + // PhysX doesn't perform callbacks in its upwards collision check so if + // this isn't a sideways collision then it must be a downwards one. In this + // case we want to have the collision normal only point in the opposite direction. + // i.e. up If we include the sideways part of the normal then the Player class + // velocity calculations using this normal will affect the player's forwards + // momentum. This is especially noticable on stairs as the rounded bottom of + // the capsule slides up the corner of a stair. + col.normal.set(0.0f, 0.0f, 1.0f); + } + + /* + if ( userData && + userData->mCanPush && + actor->isDynamic() && + !actor->readBodyFlag( NX_BF_KINEMATIC ) && + !mDummyMove ) + { + NxActor *ctrlActor = mController->getActor(); + + // So the object is neither + // a static or a kinematic, + // meaning we need to figure out + // if we have enough force to push it. + + // Get the hit object's force + // and scale it by the amount + // that it's acceleration is going + // against our acceleration. + const Point3F &hitObjLinVel = pxCast( actor->getLinearVelocity() ); + + F32 hitObjMass = actor->getMass(); + + VectorF hitObjDeltaVel = hitObjLinVel * TickSec; + VectorF hitObjAccel = hitObjDeltaVel / TickSec; + + VectorF controllerLinVel = pxCast( controllerActor->getLinearVelocity() ); + VectorF controllerDeltaVel = controllerLinVel * TickSec; + VectorF controllerAccel = controllerDeltaVel / TickSec; + + Point3F hitObjForce = (hitObjMass * hitObjAccel); + Point3F playerForce = (controllerActor->getMass() * controllerAccel); + + VectorF normalizedObjVel( hitObjLinVel ); + normalizedObjVel.normalizeSafe(); + + VectorF normalizedPlayerVel( pxCast( controllerActor->getLinearVelocity() ) ); + normalizedPlayerVel.normalizeSafe(); + + F32 forceDot = mDot( normalizedObjVel, normalizedPlayerVel ); + + hitObjForce *= forceDot; + + playerForce = playerForce - hitObjForce; + + if ( playerForce.x > 0.0f || playerForce.y > 0.0f || playerForce.z > 0.0f ) + actor->addForceAtPos( NxVec3( playerForce.x, playerForce.y, playerForce.z ), actor->getCMassGlobalPosition() ); + + //Con::printf( "onShapeHit: %f %f %f", playerForce.x, playerForce.y, playerForce.z ); + } + */ + + return NX_ACTION_PUSH; +} + +NxControllerAction PxPlayer::onControllerHit( const NxControllersHit& hit ) +{ + if (!mCollisionList || mCollisionList->getCount() >= CollisionList::MaxCollisions) + return NX_ACTION_NONE; + + NxActor *actor = hit.other->getActor(); + PhysicsUserData *userData = PhysicsUserData::cast( actor->userData ); + + if ( actor->readActorFlag( NX_AF_DISABLE_RESPONSE ) ) + return NX_ACTION_NONE; + + // For controller-to-controller hit we don't have an actual hit point, so all + // we can do is set the hit object. + Collision &col = mCollisionList->increment(); + dMemset( &col, 0, sizeof( col ) ); + if ( userData ) + col.object = userData->getObject(); + + return NX_ACTION_NONE; +} + +void PxPlayer::findContact( SceneObject **contactObject, + VectorF *contactNormal, + Vector *outOverlapObjects ) const +{ + AssertFatal( mController, "PxPlayer::findContact - The controller is null!" ); + + // See PxPlayer::_onPhysicsReset + if ( !mWorld->isEnabled() ) + return; + + // Calculate the sweep motion... + F32 halfCapSize = mOriginOffset; + F32 halfSmallCapSize = halfCapSize * 0.8f; + F32 diff = halfCapSize - halfSmallCapSize; + + const F32 mSkinWidth = 0.1f; + + F32 offsetDist = diff + mSkinWidth + 0.01f; + NxVec3 motion(0,0,-offsetDist); + + /* + // Construct the capsule... + F32 radius = mCapsuleController->getRadius(); + F32 halfHeight = mCapsuleController->getHeight() * 0.5f; + + NxCapsule capsule; + capsule.p0 = capsule.p1 = pxCast( mCapsuleController->getDebugPosition() ); + capsule.p0.z -= halfHeight; + capsule.p1.z += halfHeight; + capsule.radius = radius; + */ + + NxSweepQueryHit sweepHit; + NxU32 hitCount = mController->getActor()->linearSweep( motion, NX_SF_STATICS | NX_SF_DYNAMICS, NULL, 1, &sweepHit, NULL ); + + if ( hitCount > 0 ) + { + PhysicsUserData *data = PhysicsUserData::cast( sweepHit.hitShape->getActor().userData ); + if ( data ) + { + *contactObject = data->getObject(); + *contactNormal = pxCast( sweepHit.normal ); + } + } + + // Check for overlapped objects ( triggers ) + + if ( !outOverlapObjects ) + return; + + NxCapsuleShape *shape = reinterpret_cast( mController->getActor()->getShapes()[0] ); + NxCapsule worldCapsule; + shape->getWorldCapsule( worldCapsule ); + + // Test only against activeGroup with triggers ( 31 ). + NxU32 activeGroups = 1 << 31; + + NxShape *shapes[10]; + + hitCount = mWorld->getScene()->overlapCapsuleShapes( worldCapsule, NX_ALL_SHAPES, 10, shapes, NULL, activeGroups ); + + for ( S32 i = 0; i < hitCount; i++ ) + { + PhysicsUserData *data = PhysicsUserData::cast( shapes[i]->getActor().userData ); + if ( data ) + outOverlapObjects->push_back( data->getObject() ); + } +} + +void PxPlayer::enableCollision() +{ + AssertFatal( mController, "PxPlayer::enableCollision - The controller is null!" ); + + mWorld->releaseWriteLock(); + mController->setCollision( true ); +} + +void PxPlayer::disableCollision() +{ + AssertFatal( mController, "PxPlayer::disableCollision - The controller is null!" ); + + mWorld->releaseWriteLock(); + mController->setCollision( false ); +} + +PhysicsWorld* PxPlayer::getWorld() +{ + return mWorld; +} + +void PxPlayer::setTransform( const MatrixF &transform ) +{ + AssertFatal( mController, "PxPlayer::setTransform - The controller is null!" ); + + mWorld->releaseWriteLock(); + + Point3F newPos = transform.getPosition(); + newPos.z += mOriginOffset; + + const Point3F &curPos = pxCast(mController->getDebugPosition()); + + if ( !(newPos - curPos ).isZero() ) + mController->setPosition( pxCast(newPos) ); +} + +MatrixF& PxPlayer::getTransform( MatrixF *outMatrix ) +{ + AssertFatal( mController, "PxPlayer::getTransform - The controller is null!" ); + + Point3F newPos = pxCast( mController->getDebugPosition() ); + newPos.z -= mOriginOffset; + outMatrix->setPosition( newPos ); + + return *outMatrix; +} + +void PxPlayer::setScale( const Point3F &scale ) +{ +} + +Box3F PxPlayer::getWorldBounds() +{ + Con::warnf( "PxPlayer::getWorldBounds - not implemented" ); + return Box3F::Invalid; +} \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx/pxPlayer.h b/Engine/source/T3D/physics/physx/pxPlayer.h new file mode 100644 index 000000000..2c35817c9 --- /dev/null +++ b/Engine/source/T3D/physics/physx/pxPlayer.h @@ -0,0 +1,104 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PXPLAYER_H +#define _PXPLAYER_H + +#ifndef _PHYSX_H_ +#include "T3D/physics/physX/px.h" +#endif +#ifndef _T3D_PHYSICS_PHYSICSPLAYER_H_ +#include "T3D/physics/physicsPlayer.h" +#endif +#ifndef _T3D_PHYSICSCOMMON_H_ +#include "T3D/physics/physicsCommon.h" +#endif + + +class PxWorld; +class NxController; + + +class PxPlayer : public PhysicsPlayer, public NxUserControllerHitReport +{ +protected: + + NxController *mController; + + F32 mSkinWidth; + + PxWorld *mWorld; + + SceneObject *mObject; + + /// Used to get collision info out of the + /// NxUserControllerHitReport callbacks. + CollisionList *mCollisionList; + + /// + F32 mOriginOffset; + + /// + F32 mStepHeight; + + /// + void _releaseController(); + + // NxUserControllerHitReport + virtual NxControllerAction onShapeHit( const NxControllerShapeHit& hit ); + virtual NxControllerAction onControllerHit( const NxControllersHit& hit ); + + void _findContact( SceneObject **contactObject, VectorF *contactNormal ) const; + + void _onPhysicsReset( PhysicsResetEvent reset ); + +public: + + PxPlayer(); + virtual ~PxPlayer(); + + // PhysicsObject + virtual PhysicsWorld* getWorld(); + virtual void setTransform( const MatrixF &transform ); + virtual MatrixF& getTransform( MatrixF *outMatrix ); + virtual void setScale( const Point3F &scale ); + virtual Box3F getWorldBounds(); + virtual void setSimulationEnabled( bool enabled ) {} + virtual bool isSimulationEnabled() { return true; } + + // PhysicsPlayer + virtual void init( const char *type, + const Point3F &size, + F32 runSurfaceCos, + F32 stepHeight, + SceneObject *obj, + PhysicsWorld *world ); + virtual Point3F move( const VectorF &displacement, CollisionList &outCol ); + virtual void findContact( SceneObject **contactObject, VectorF *contactNormal, Vector *outOverlapObjects ) const; + virtual bool testSpacials( const Point3F &nPos, const Point3F &nSize ) const { return true; } + virtual void setSpacials( const Point3F &nPos, const Point3F &nSize ) {} + virtual void enableCollision(); + virtual void disableCollision(); +}; + + +#endif // _PXPLAYER_H \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx/pxPlugin.cpp b/Engine/source/T3D/physics/physx/pxPlugin.cpp new file mode 100644 index 000000000..7fff0559e --- /dev/null +++ b/Engine/source/T3D/physics/physx/pxPlugin.cpp @@ -0,0 +1,297 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/consoleTypes.h" +#include "T3D/physics/physX/pxPlugin.h" + +#include "T3D/physics/physicsShape.h" +#include "T3D/physics/physX/pxWorld.h" +#include "T3D/physics/physX/pxBody.h" +#include "T3D/physics/physX/pxPlayer.h" +#include "T3D/physics/physX/pxCollision.h" +#include "T3D/gameBase/gameProcess.h" +#include "core/util/tNamedFactory.h" + + +extern bool gPhysXLogWarnings; + +AFTER_MODULE_INIT( Sim ) +{ + NamedFactory::add( "PhysX", &PxPlugin::create ); + + #if defined(TORQUE_OS_WIN32) || defined(TORQUE_OS_XBOX) || defined(TORQUE_OS_XENON) + NamedFactory::add( "default", &PxPlugin::create ); + #endif + + Con::addVariable( "$PhysXLogWarnings", TypeBool, &gPhysXLogWarnings, + "@brief Output PhysX warnings to the console.\n\n" + "@ingroup Physics\n"); +} + + +PhysicsPlugin* PxPlugin::create() +{ + // Only create the plugin if it hasn't been set up AND + // the PhysX world is successfully initialized. + bool success = PxWorld::restartSDK( false ); + if ( success ) + return new PxPlugin(); + + return NULL; +} + +PxPlugin::PxPlugin() +{ +} + +PxPlugin::~PxPlugin() +{ +} + +void PxPlugin::destroyPlugin() +{ + // Cleanup any worlds that are still kicking. + Map::Iterator iter = mPhysicsWorldLookup.begin(); + for ( ; iter != mPhysicsWorldLookup.end(); iter++ ) + { + iter->value->destroyWorld(); + delete iter->value; + } + mPhysicsWorldLookup.clear(); + + PxWorld::restartSDK( true ); + + delete this; +} + +void PxPlugin::reset() +{ + // First delete all the cleanup objects. + if ( getPhysicsCleanup() ) + getPhysicsCleanup()->deleteAllObjects(); + + getPhysicsResetSignal().trigger( PhysicsResetEvent_Restore ); + + // Now let each world reset itself. + Map::Iterator iter = mPhysicsWorldLookup.begin(); + for ( ; iter != mPhysicsWorldLookup.end(); iter++ ) + iter->value->reset(); +} + +PhysicsCollision* PxPlugin::createCollision() +{ + return new PxCollision(); +} + +PhysicsBody* PxPlugin::createBody() +{ + return new PxBody(); +} + +PhysicsPlayer* PxPlugin::createPlayer() +{ + return new PxPlayer(); +} + +bool PxPlugin::isSimulationEnabled() const +{ + bool ret = false; + PxWorld *world = static_cast( getWorld( smClientWorldName ) ); + if ( world ) + { + ret = world->getEnabled(); + return ret; + } + + world = static_cast( getWorld( smServerWorldName ) ); + if ( world ) + { + ret = world->getEnabled(); + return ret; + } + + return ret; +} + +void PxPlugin::enableSimulation( const String &worldName, bool enable ) +{ + PxWorld *world = static_cast( getWorld( worldName ) ); + if ( world ) + world->setEnabled( enable ); +} + +void PxPlugin::setTimeScale( const F32 timeScale ) +{ + // Grab both the client and + // server worlds and set their time + // scales to the passed value. + PxWorld *world = static_cast( getWorld( smClientWorldName ) ); + if ( world ) + world->setEditorTimeScale( timeScale ); + + world = static_cast( getWorld( smServerWorldName ) ); + if ( world ) + world->setEditorTimeScale( timeScale ); +} + +const F32 PxPlugin::getTimeScale() const +{ + // Grab both the client and + // server worlds and call + // setEnabled( true ) on them. + PxWorld *world = static_cast( getWorld( smClientWorldName ) ); + if ( !world ) + { + world = static_cast( getWorld( smServerWorldName ) ); + if ( !world ) + return 0.0f; + } + + return world->getEditorTimeScale(); +} + +bool PxPlugin::createWorld( const String &worldName ) +{ + Map::Iterator iter = mPhysicsWorldLookup.find( worldName ); + PhysicsWorld *world = NULL; + + iter != mPhysicsWorldLookup.end() ? world = (*iter).value : world = NULL; + + if ( world ) + { + Con::errorf( "PxPlugin::createWorld - %s world already exists!", worldName.c_str() ); + return false; + } + + world = new PxWorld(); + + if ( worldName.equal( smClientWorldName, String::NoCase ) ) + world->initWorld( false, ClientProcessList::get() ); + else + world->initWorld( true, ServerProcessList::get() ); + + mPhysicsWorldLookup.insert( worldName, world ); + + return world != NULL; +} + +void PxPlugin::destroyWorld( const String &worldName ) +{ + Map::Iterator iter = mPhysicsWorldLookup.find( worldName ); + if ( iter == mPhysicsWorldLookup.end() ) + return; + + PhysicsWorld *world = (*iter).value; + world->destroyWorld(); + delete world; + + mPhysicsWorldLookup.erase( iter ); +} + +PhysicsWorld* PxPlugin::getWorld( const String &worldName ) const +{ + if ( mPhysicsWorldLookup.isEmpty() ) + return NULL; + + Map::ConstIterator iter = mPhysicsWorldLookup.find( worldName ); + + return iter != mPhysicsWorldLookup.end() ? (*iter).value : NULL; +} + +PhysicsWorld* PxPlugin::getWorld() const +{ + if ( mPhysicsWorldLookup.size() == 0 ) + return NULL; + + Map::ConstIterator iter = mPhysicsWorldLookup.begin(); + return iter->value; +} + +U32 PxPlugin::getWorldCount() const +{ + return mPhysicsWorldLookup.size(); +} + +void PxPlugin::_onDebugDrawEnabled( bool enabled ) +{ + if ( !enabled ) + gPhysicsSDK->setParameter( NX_VISUALIZATION_SCALE, 0.0f ); +} + +ConsoleFunction( physXRemoteDebuggerConnect, bool, 1, 3, "" ) +{ + if ( !gPhysicsSDK ) + { + Con::errorf( "PhysX SDK not initialized!" ); + return false; + } + + NxRemoteDebugger *debugger = gPhysicsSDK->getFoundationSDK().getRemoteDebugger(); + + if ( debugger->isConnected() ) + { + Con::errorf( "RemoteDebugger already connected... call disconnect first!" ); + return false; + } + + const UTF8 *host = "localhost"; + U32 port = 5425; + + if ( argc >= 2 ) + host = argv[1]; + if ( argc >= 3 ) + port = dAtoi( argv[2] ); + + // Before we connect we need to have write access + // to both the client and server worlds. + PxWorld::releaseWriteLocks(); + + // Connect! + debugger->connect( host, port ); + if ( !debugger->isConnected() ) + { + Con::errorf( "RemoteDebugger failed to connect!" ); + return false; + } + + Con::printf( "RemoteDebugger connected to %s at port %u!", host, port ); + return true; +} + +ConsoleFunction( physXRemoteDebuggerDisconnect, void, 1, 1, "" ) +{ + if ( !gPhysicsSDK ) + { + Con::errorf( "PhysX SDK not initialized!" ); + return; + } + + NxRemoteDebugger *debugger = gPhysicsSDK->getFoundationSDK().getRemoteDebugger(); + + if ( debugger->isConnected() ) + { + debugger->flush(); + debugger->disconnect(); + Con::printf( "RemoteDebugger disconnected!" ); + } +} diff --git a/Engine/source/T3D/physics/physx/pxPlugin.h b/Engine/source/T3D/physics/physx/pxPlugin.h new file mode 100644 index 000000000..c32c1162c --- /dev/null +++ b/Engine/source/T3D/physics/physx/pxPlugin.h @@ -0,0 +1,59 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _T3D_PHYSICS_PXPLUGIN_H_ +#define _T3D_PHYSICS_PXPLUGIN_H_ + +#ifndef _T3D_PHYSICS_PHYSICSPLUGIN_H_ +#include "T3D/physics/physicsPlugin.h" +#endif + + +class PxPlugin : public PhysicsPlugin +{ +public: + + PxPlugin(); + ~PxPlugin(); + + /// Create function for factory. + static PhysicsPlugin* create(); + + // PhysicsPlugin + virtual void destroyPlugin(); + virtual void reset(); + virtual PhysicsCollision* createCollision(); + virtual PhysicsBody* createBody(); + virtual PhysicsPlayer* createPlayer(); + virtual bool isSimulationEnabled() const; + virtual void enableSimulation( const String &worldName, bool enable ); + virtual void setTimeScale( const F32 timeScale ); + virtual const F32 getTimeScale() const; + virtual bool createWorld( const String &worldName ); + virtual void destroyWorld( const String &worldName ); + virtual PhysicsWorld* getWorld( const String &worldName ) const; + virtual PhysicsWorld* getWorld() const; + virtual U32 getWorldCount() const; + virtual void _onDebugDrawEnabled( bool enabled ); +}; + +#endif // _T3D_PHYSICS_PXPLUGIN_H_ \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx/pxStream.cpp b/Engine/source/T3D/physics/physx/pxStream.cpp new file mode 100644 index 000000000..6c7565ee6 --- /dev/null +++ b/Engine/source/T3D/physics/physx/pxStream.cpp @@ -0,0 +1,174 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physX/pxStream.h" + +#include "console/console.h" +#include "console/consoleTypes.h" +#include "core/strings/stringFunctions.h" + + +PxMemStream::PxMemStream() + : mMemStream( 1024 ) +{ +} + +PxMemStream::~PxMemStream() +{ +} + +void PxMemStream::resetPosition() +{ + mMemStream.setPosition( 0 ); +} + +NxU8 PxMemStream::readByte() const +{ + NxU8 out; + mMemStream.read( &out ); + return out; +} + +NxU16 PxMemStream::readWord() const +{ + NxU16 out; + mMemStream.read( &out ); + return out; +} + +NxU32 PxMemStream::readDword() const +{ + NxU32 out; + mMemStream.read( &out ); + return out; +} + +float PxMemStream::readFloat() const +{ + float out; + mMemStream.read( &out ); + return out; +} + +double PxMemStream::readDouble() const +{ + double out; + mMemStream.read( &out ); + return out; +} + +void PxMemStream::readBuffer( void *buffer, NxU32 size ) const +{ + mMemStream.read( size, buffer ); +} + +NxStream& PxMemStream::storeByte( NxU8 b ) +{ + mMemStream.write( b ); + return *this; +} + +NxStream& PxMemStream::storeWord( NxU16 w ) +{ + mMemStream.write( w ); + return *this; +} + +NxStream& PxMemStream::storeDword( NxU32 d ) +{ + mMemStream.write( d ); + return *this; +} + +NxStream& PxMemStream::storeFloat( NxReal f ) +{ + mMemStream.write( f ); + return *this; +} + +NxStream& PxMemStream::storeDouble( NxF64 f ) +{ + mMemStream.write( f ); + return *this; +} + +NxStream& PxMemStream::storeBuffer( const void *buffer, NxU32 size ) +{ + mMemStream.write( size, buffer ); + return *this; +} + + +bool gPhysXLogWarnings = false; + +PxConsoleStream::PxConsoleStream() +{ +} + +PxConsoleStream::~PxConsoleStream() +{ +} + +void PxConsoleStream::reportError( NxErrorCode code, const char *message, const char* file, int line ) +{ + #ifdef TORQUE_DEBUG + + // If we're in debug mode and the error code is serious then + // pop up a message box to make sure we see it. + if ( code < NXE_DB_INFO ) + { + UTF8 info[1024]; + dSprintf( info, 1024, "File: %s\nLine: %d\n%s", file, line, message ); + Platform::AlertOK( "PhysX Error", info ); + } + + #endif + + // In all other cases we just dump the message to the console. + if ( code == NXE_DB_WARNING ) + { + if ( gPhysXLogWarnings ) + Con::printf( "PhysX Warning:\n %s(%d) : %s\n", file, line, message ); + } + else + Con::printf( "PhysX Error:\n %s(%d) : %s\n", file, line, message ); +} + +NxAssertResponse PxConsoleStream::reportAssertViolation (const char *message, const char *file,int line) +{ + // Assert if we're in debug mode... + bool triggerBreak = false; + #ifdef TORQUE_DEBUG + triggerBreak = PlatformAssert::processAssert( PlatformAssert::Fatal, file, line, message ); + #endif + + // In all other cases we just dump the message to the console. + Con::errorf( "PhysX Assert:\n %s(%d) : %s\n", file, line, message ); + + return triggerBreak ? NX_AR_BREAKPOINT : NX_AR_CONTINUE; +} + +void PxConsoleStream::print( const char *message ) +{ + Con::printf( "PhysX Says: %s\n", message ); +} diff --git a/Engine/source/T3D/physics/physx/pxStream.h b/Engine/source/T3D/physics/physx/pxStream.h new file mode 100644 index 000000000..8dc875fa3 --- /dev/null +++ b/Engine/source/T3D/physics/physx/pxStream.h @@ -0,0 +1,78 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _T3D_PHYSICS_PXSTREAM_H_ +#define _T3D_PHYSICS_PXSTREAM_H_ + +#ifndef _PHYSX_H_ +#include "T3D/physics/physX/px.h" +#endif +#ifndef _MEMSTREAM_H_ +#include "core/stream/memStream.h" +#endif + + +class PxMemStream : public NxStream +{ +public: + + PxMemStream(); + virtual ~PxMemStream(); + + void resetPosition(); + + // NxStream + NxU8 readByte() const; + NxU16 readWord() const; + NxU32 readDword() const; + float readFloat() const; + double readDouble() const; + void readBuffer( void *buffer, NxU32 size ) const; + NxStream& storeByte( NxU8 b ); + NxStream& storeWord( NxU16 w ); + NxStream& storeDword( NxU32 d ); + NxStream& storeFloat( NxReal f ); + NxStream& storeDouble( NxF64 f ); + NxStream& storeBuffer( const void* buffer, NxU32 size ); + +protected: + + mutable MemStream mMemStream; +}; + + +class PxConsoleStream : public NxUserOutputStream +{ +protected: + + // NxUserOutputStream + void reportError( NxErrorCode code, const char *message, const char* file, int line ); + NxAssertResponse reportAssertViolation( const char *message, const char *file, int line ); + void print( const char *message ); + +public: + + PxConsoleStream(); + ~PxConsoleStream(); +}; + +#endif // _T3D_PHYSICS_PXSTREAM_H_ \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx/pxUtils.cpp b/Engine/source/T3D/physics/physx/pxUtils.cpp new file mode 100644 index 000000000..05671806f --- /dev/null +++ b/Engine/source/T3D/physics/physx/pxUtils.cpp @@ -0,0 +1,109 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physx/pxUtils.h" + +#include "gfx/gfxTransformSaver.h" +#include "gfx/gfxDrawUtil.h" +#include "math/mMatrix.h" +#include "math/mPoint3.h" +#include "T3D/physics/physX/px.h" +#include "T3D/physics/physX/pxCasts.h" + +namespace PxUtils { + +void drawActor( NxActor *inActor ) +{ + GFXDrawUtil *drawer = GFX->getDrawUtil(); + //drawer->setZRead( false ); + + // Determine alpha we render shapes with. + const U8 enabledAlpha = 255; + const U8 disabledAlpha = 100; + U8 renderAlpha = inActor->readActorFlag( NX_AF_DISABLE_COLLISION ) ? disabledAlpha : enabledAlpha; + + // Determine color we render actors and shapes with. + ColorI actorColor( 0, 0, 255, 200 ); + ColorI shapeColor = ( inActor->isSleeping() ? ColorI( 0, 0, 255, renderAlpha ) : ColorI( 255, 0, 255, renderAlpha ) ); + + MatrixF actorMat(true); + inActor->getGlobalPose().getRowMajor44( actorMat ); + + GFXStateBlockDesc desc; + desc.setBlend( true ); + desc.setZReadWrite( true, false ); + desc.setCullMode( GFXCullNone ); + + // Draw an xfm gizmo for the actor's globalPose... + //drawer->drawTransform( desc, actorMat, Point3F::One, actorColor ); + + // Loop through and render all the actor's shapes.... + + NxShape *const*pShapeArray = inActor->getShapes(); + U32 numShapes = inActor->getNbShapes(); + + for ( U32 i = 0; i < numShapes; i++ ) + { + const NxShape *shape = pShapeArray[i]; + + Point3F shapePos = pxCast( shape->getGlobalPosition() ); + MatrixF shapeMat(true); + shape->getGlobalPose().getRowMajor44(shapeMat); + shapeMat.setPosition( Point3F::Zero ); + + switch ( shape->getType() ) + { + case NX_SHAPE_SPHERE: + { + NxSphereShape *sphere = (NxSphereShape*)shape; + drawer->drawSphere( desc, sphere->getRadius(), shapePos, shapeColor ); + + break; + } + case NX_SHAPE_BOX: + { + NxBoxShape *box = (NxBoxShape*)shape; + Point3F size = pxCast( box->getDimensions() ); + drawer->drawCube( desc, size*2, shapePos, shapeColor, &shapeMat ); + break; + } + case NX_SHAPE_CAPSULE: + { + shapeMat.mul( MatrixF( EulerF( mDegToRad(90.0f), mDegToRad(90.0f), 0 ) ) ); + + NxCapsuleShape *capsule = (NxCapsuleShape*)shape; + drawer->drawCapsule( desc, shapePos, capsule->getRadius(), capsule->getHeight(), shapeColor, &shapeMat ); + + break; + } + default: + { + break; + } + } + } + + //drawer->clearZDefined(); +} + +} // namespace PxUtils \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx/pxUtils.h b/Engine/source/T3D/physics/physx/pxUtils.h new file mode 100644 index 000000000..1c1d9873b --- /dev/null +++ b/Engine/source/T3D/physics/physx/pxUtils.h @@ -0,0 +1,38 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PXUTILS_H_ +#define _PXUTILS_H_ + + +class NxActor; + + +namespace PxUtils { + + /// Debug render an actor, loops through all shapes + /// and translates primitive types into drawUtil calls. + void drawActor( NxActor *inActor ); + +} // namespace PxUtils + +#endif // _PXUTILS_H_ \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx/pxWorld.cpp b/Engine/source/T3D/physics/physx/pxWorld.cpp new file mode 100644 index 000000000..bbccc105c --- /dev/null +++ b/Engine/source/T3D/physics/physx/pxWorld.cpp @@ -0,0 +1,872 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physX/pxWorld.h" + +#include "T3D/physics/physX/px.h" +#include "T3D/physics/physX/pxPlugin.h" +#include "T3D/physics/physX/pxMaterial.h" +#include "T3D/physics/physX/pxContactReporter.h" +#include "T3D/physics/physX/pxStream.h" +#include "T3D/physics/physX/pxCasts.h" +#include "T3D/physics/physicsUserData.h" + +#include "core/stream/bitStream.h" +#include "platform/profiler.h" +#include "sim/netConnection.h" +#include "console/console.h" +#include "console/consoleTypes.h" +#include "core/util/safeDelete.h" +#include "T3D/tsstatic.h" +#include "T3D/gameBase/gameProcess.h" +#include "gfx/sim/debugDraw.h" +#include "gfx/primBuilder.h" + +#include + + +static const F32 PhysicsStepTime = (F32)TickMs / 1000.0f; +static const U32 PhysicsMaxIterations = 8; +static const F32 PhysicsMaxTimeStep = PhysicsStepTime / 2.0f; + +NxPhysicsSDK *gPhysicsSDK = NULL; +NxCookingInterface *PxWorld::smCooking = NULL; +PxConsoleStream *PxWorld::smConsoleStream = NULL; + + +PxWorld::PxWorld() : + mScene( NULL ), + mConactReporter( NULL ), + mProcessList( NULL ), + mIsSimulating( false ), + mErrorReport( false ), + mTickCount( 0 ), + mIsEnabled( false ), + mEditorTimeScale( 1.0f ) +{ + if ( !CCTAllocator::mAllocator ) + CCTAllocator::mAllocator = new NxUserAllocatorDefault(); + mControllerManager = new CharacterControllerManager( CCTAllocator::mAllocator ); +} + +PxWorld::~PxWorld() +{ + delete mControllerManager; +} + +NxCookingInterface* PxWorld::getCooking() +{ + if ( !smCooking ) + smCooking = NxGetCookingLib( NX_PHYSICS_SDK_VERSION ); + + return smCooking; +} + +bool PxWorld::_init( bool isServer, ProcessList *processList ) +{ + if ( !gPhysicsSDK ) + { + Con::errorf( "PhysXWorld::init - PhysXSDK not initialized!" ); + return false; + } + + // Create the scene description. + NxSceneDesc sceneDesc; + sceneDesc.userData = this; + + // Set up default gravity. + sceneDesc.gravity.set( mGravity.x, mGravity.y, mGravity.z ); + + // The master scene is always on the CPU and is used + // mostly for static shapes. + sceneDesc.simType = NX_SIMULATION_SW; // [9/28/2009 Pat] Why is this software? Should be software server, hardware client? + + // Threading... seems to improve performance. + // + // TODO: I was getting random crashes in debug when doing + // edit and continue... lets see if i still get them with + // the threading disabled. + // + sceneDesc.flags |= NX_SF_ENABLE_MULTITHREAD | NX_SF_DISABLE_SCENE_MUTEX; + sceneDesc.threadMask = 0xfffffffe; + sceneDesc.internalThreadCount = PHYSICSMGR->getThreadCount(); + + // Create the scene. + mScene = gPhysicsSDK->createScene(sceneDesc); + if ( !mScene ) + { + Con::errorf( "PhysXWorld - %s world createScene returned a null scene!", isServer ? "Server" : "Client" ); + return false; + } + + /* + // Make note of what we've created. + String simType = sceneDesc.simType == NX_SIMULATION_SW ? "software" : "hardware"; + String clientOrServer = this == isServer ? "server" : "client"; + Con::printf( "PhysXWorld::init() - Created %s %s simulation!", + clientOrServer.c_str(), + simType.c_str() ); + */ + + mScene->setTiming( PhysicsMaxTimeStep, PhysicsMaxIterations, NX_TIMESTEP_FIXED ); + + // TODO: Add dummy actor with scene name! + + // Set the global contact reporter. + + mConactReporter = new PxContactReporter(); + mScene->setUserContactReport( mConactReporter ); + + // Set the global PxUserNotify + + mUserNotify = new PxUserNotify(); + mScene->setUserNotify( mUserNotify ); + + // Now create the dynamic rigid body compartment which + // can reside on the hardware when hardware is present. + /* + NxCompartmentDesc compartmentDesc; + compartmentDesc.type = NX_SCT_RIGIDBODY; + compartmentDesc.deviceCode = NX_DC_PPU_AUTO_ASSIGN; + mRigidCompartment = mScene->createCompartment( compartmentDesc ); + if ( !mRigidCompartment ) + { + Con::errorf( "PhysXWorld - Creation of rigid body compartment failed!" ); + return false; + } + */ + + // Hook up the tick processing signals for advancing physics. + // + // First an overview of how physics and the game ticks + // interact with each other. + // + // In Torque you normally tick the server and then the client + // approximately every 32ms. So before the game tick we call + // getPhysicsResults() to get the new physics state and call + // tickPhysics() when the game tick is done to start processing + // the next physics state. This means PhysX is happily doing + // physics in a separate thread while we're doing rendering, + // sound, input, networking, etc. + // + // Because your frame rate is rarely perfectly even you can + // get cases where you may tick the server or the client + // several times in a row. This happens most often in debug + // mode, but can also happen in release. + // + // The simple implementation is to do a getPhysicsResults() and + // tickPhysics() for each tick. But this very bad! It forces + // immediate results from PhysX which blocks the primary thread + // and further slows down processing. It leads to slower and + // slower frame rates as the simulation is never able to catch + // up to the current tick. + // + // The trick is processing physics once for backlogged ticks + // with the total of the elapsed tick time. This is a huge + // performance gain and keeps you from blocking on PhysX. + // + // This does have a side effect that when it occurs you'll get + // ticks where the physics state hasn't advanced, but this beats + // single digit frame rates. + // + AssertFatal( processList, "PxWorld::init() - We need a process list to create the world!" ); + mProcessList = processList; + mProcessList->preTickSignal().notify( this, &PxWorld::getPhysicsResults ); + mProcessList->postTickSignal().notify( this, &PxWorld::tickPhysics, 1000.0f ); + + // Setup the default material. + NxMaterial *dmat = mScene->getMaterialFromIndex( 0 ); + dmat->setRestitution( 0.2f ); + dmat->setStaticFriction( 0.6f ); + dmat->setDynamicFriction( 0.4f ); + + // Setup dominance groups. + + // Group 31 is for debris and other objects which can be pushed but cannot push back. + // Group 0 is for everything else. + + NxConstraintDominance debrisDominance( 0.0f, 1.0f ); + mScene->setDominanceGroupPair( 0, 31, debrisDominance ); + + return true; +} + +void PxWorld::_destroy() +{ + // Make sure the simulation is stopped! + getPhysicsResults(); + _releaseQueues(); + + #ifdef TORQUE_DEBUG + + U32 actorCount = mScene->getNbActors(); + U32 jointCount = mScene->getNbJoints(); + + if ( actorCount != 0 || jointCount != 0 ) + { + // Dump the names of any actors or joints that + // were not released before the destruction of + // this scene. + + for ( U32 i=0; i < actorCount; i++ ) + { + const NxActor *actor = mScene->getActors()[i]; + Con::errorf( "Orphan NxActor - '%s'!", actor->getName() ); + } + + mScene->resetJointIterator(); + for ( ;; ) + { + const NxJoint *joint = mScene->getNextJoint(); + if ( !joint ) + break; + + Con::errorf( "Orphan NxJoint - '%s'!", joint->getName() ); + } + + AssertFatal( false, "PhysXWorld::_destroy() - Some actors and/or joints were not released!" ); + } + + #endif // TORQUE_DEBUG + + //NxCloseCooking(); + + // Release the tick processing signals. + if ( mProcessList ) + { + mProcessList->preTickSignal().remove( this, &PxWorld::getPhysicsResults ); + mProcessList->postTickSignal().remove( this, &PxWorld::tickPhysics ); + mProcessList = NULL; + } + + // Destroy the scene. + if ( mScene ) + { + // Delete the contact reporter. + mScene->setUserContactReport( NULL ); + SAFE_DELETE( mConactReporter ); + + // First shut down threads... this makes it + // safe to release the scene. + mScene->shutdownWorkerThreads(); + + // Release the scene. + gPhysicsSDK->releaseScene( *mScene ); + mScene = NULL; + } + + // Try to restart the sdk if we can. + //restartSDK(); +} + +bool PxWorld::restartSDK( bool destroyOnly, PxWorld *clientWorld, PxWorld *serverWorld ) +{ + // If either the client or the server still exist + // then we cannot reset the SDK. + if ( clientWorld || serverWorld ) + return false; + + // Destroy the existing SDK. + if ( gPhysicsSDK ) + { + NXU::releasePersistentMemory(); + gPhysicsSDK->release(); + gPhysicsSDK = NULL; + smCooking = NULL; + SAFE_DELETE( smConsoleStream ); + } + + // If we're not supposed to restart... return. + if ( destroyOnly ) + return true; + + smConsoleStream = new PxConsoleStream(); + + NxPhysicsSDKDesc sdkDesc; + sdkDesc.flags |= NX_SDKF_NO_HARDWARE; // [9/28/2009 Pat] Why is this disabled? + + NxSDKCreateError error; + gPhysicsSDK = NxCreatePhysicsSDK( NX_PHYSICS_SDK_VERSION, + NULL, + smConsoleStream, + sdkDesc, + &error ); + if ( !gPhysicsSDK ) + { + Con::errorf( "PhysX failed to initialize! Error code: %d", error ); + Platform::messageBox( Con::getVariable( "$appName" ), + avar("PhysX could not be started!\r\n" + "Please be sure you have the latest version of PhysX installed.\r\n" + "Error Code: %d", error), + MBOk, MIStop ); + Platform::forceShutdown( -1 ); + + // We shouldn't get here, but this shuts up + // source diagnostic tools. + return false; + } + + // Set the default skin width for all actors. + gPhysicsSDK->setParameter( NX_SKIN_WIDTH, 0.01f ); + + return true; +} + +void PxWorld::tickPhysics( U32 elapsedMs ) +{ + if ( !mScene || !mIsEnabled ) + return; + + // Did we forget to call getPhysicsResults somewhere? + AssertFatal( !mIsSimulating, "PhysXWorld::tickPhysics() - Already simulating!" ); + + // The elapsed time should be non-zero and + // a multiple of TickMs! + AssertFatal( elapsedMs != 0 && + ( elapsedMs % TickMs ) == 0 , "PhysXWorld::tickPhysics() - Got bad elapsed time!" ); + + PROFILE_SCOPE(PxWorld_TickPhysics); + + // Convert it to seconds. + const F32 elapsedSec = (F32)elapsedMs * 0.001f; + + // For some reason this gets reset all the time + // and it must be called before the simulate. + mScene->setFilterOps( NX_FILTEROP_OR, + NX_FILTEROP_OR, + NX_FILTEROP_AND ); + mScene->setFilterBool( false ); + NxGroupsMask zeroMask; + zeroMask.bits0=zeroMask.bits1=zeroMask.bits2=zeroMask.bits3=0; + mScene->setFilterConstant0( zeroMask ); + mScene->setFilterConstant1( zeroMask ); + + mScene->simulate( elapsedSec * mEditorTimeScale ); + mScene->flushStream(); + mIsSimulating = true; + + //Con::printf( "%s PhysXWorld::tickPhysics!", this == smClientWorld ? "Client" : "Server" ); +} + +void PxWorld::releaseWriteLocks() +{ + PxWorld *world = dynamic_cast( PHYSICSMGR->getWorld( "server" ) ); + + if ( world ) + world->releaseWriteLock(); + + world = dynamic_cast( PHYSICSMGR->getWorld( "client" ) ); + + if ( world ) + world->releaseWriteLock(); +} + +void PxWorld::releaseWriteLock() +{ + if ( !mScene || !mIsSimulating ) + return; + + PROFILE_SCOPE(PxWorld_ReleaseWriteLock); + + // We use checkResults here to release the write lock + // but we do not change the simulation flag or increment + // the tick count... we may have gotten results, but the + // simulation hasn't really ticked! + mScene->checkResults( NX_RIGID_BODY_FINISHED, true ); + AssertFatal( mScene->isWritable(), "PhysXWorld::releaseWriteLock() - We should have been writable now!" ); +} + +void PxWorld::getPhysicsResults() +{ + if ( !mScene || !mIsSimulating ) + return; + + PROFILE_SCOPE(PxWorld_GetPhysicsResults); + + // Get results from scene. + mScene->fetchResults( NX_RIGID_BODY_FINISHED, true ); + mIsSimulating = false; + mTickCount++; + + // Release any joints/actors that were waiting + // for the scene to become writable. + _releaseQueues(); + + //Con::printf( "%s PhysXWorld::getPhysicsResults!", this == smClientWorld ? "Client" : "Server" ); +} + +NxMaterial* PxWorld::createMaterial( NxMaterialDesc &material ) +{ + if ( !mScene ) + return NULL; + + // We need the writelock to create a material! + releaseWriteLock(); + + NxMaterial *mat = mScene->createMaterial( material ); + if ( !mat ) + return NULL; + + return mat; +} + +NxController* PxWorld::createController( NxControllerDesc &desc ) +{ + if ( !mScene ) + return NULL; + + // We need the writelock! + releaseWriteLock(); + + return mControllerManager->createController( mScene, desc ); +} + +void PxWorld::releaseActor( NxActor &actor ) +{ + AssertFatal( &actor.getScene() == mScene, "PhysXWorld::releaseActor() - Bad scene!" ); + + // Clear the userdata. + actor.userData = NULL; + + // If the scene is not simulating then we have the + // write lock and can safely delete it now. + if ( !mIsSimulating ) + { + mScene->releaseActor( actor ); + } + else + { + mReleaseActorQueue.push_back( &actor ); + } +} + +void PxWorld::releaseMaterial( NxMaterial &mat ) +{ + AssertFatal( &mat.getScene() == mScene, "PhysXWorld::releaseMaterial() - Bad scene!" ); + + // If the scene is not simulating then we have the + // write lock and can safely delete it now. + if ( !mIsSimulating ) + mScene->releaseMaterial( mat ); + else + mReleaseMaterialQueue.push_back( &mat ); +} + +void PxWorld::releaseHeightField( NxHeightField &heightfield ) +{ + // Always delay releasing a heightfield, for whatever reason, + // it causes lots of deadlock asserts if we do it here, even if + // the scene "says" its writable. + // + // Actually this is probably because a heightfield is owned by the "sdk" and + // not an individual scene so if either the client "or" server scene are + // simulating it asserts, thats just my theory. + + mReleaseHeightFieldQueue.push_back( &heightfield ); +} + +void PxWorld::releaseJoint( NxJoint &joint ) +{ + AssertFatal( &joint.getScene() == mScene, "PhysXWorld::releaseJoint() - Bad scene!" ); + + AssertFatal( !mReleaseJointQueue.contains( &joint ), + "PhysXWorld::releaseJoint() - Joint already exists in the release queue!" ); + + // Clear the userdata. + joint.userData = NULL; + + // If the scene is not simulating then we have the + // write lock and can safely delete it now. + if ( !mIsSimulating ) + mScene->releaseJoint( joint ); + else + mReleaseJointQueue.push_back( &joint ); +} + +void PxWorld::releaseCloth( NxCloth &cloth ) +{ + AssertFatal( &cloth.getScene() == mScene, "PhysXWorld::releaseCloth() - Bad scene!" ); + + // Clear the userdata. + cloth.userData = NULL; + + // If the scene is not simulating then we have the + // write lock and can safely delete it now. + if ( !mIsSimulating ) + mScene->releaseCloth( cloth ); + else + mReleaseClothQueue.push_back( &cloth ); +} + +void PxWorld::releaseFluid( NxFluid &fluid ) +{ + AssertFatal( &fluid.getScene() == mScene, "PhysXWorld::releaseFluid() - Bad scene!" ); + + // Clear the userdata. + fluid.userData = NULL; + + // If the scene is not simulating then we have the + // write lock and can safely delete it now. + if ( !mIsSimulating ) + mScene->releaseFluid( fluid ); + else + mReleaseFluidQueue.push_back( &fluid ); +} + +void PxWorld::releaseClothMesh( NxClothMesh &clothMesh ) +{ + // We need the writelock to release. + releaseWriteLock(); + + gPhysicsSDK->releaseClothMesh( clothMesh ); +} + +void PxWorld::releaseController( NxController &controller ) +{ + // TODO: This isn't safe to do with actors and + // joints, so we probably need a queue like we + // do for them. + + // We need the writelock to release. + releaseWriteLock(); + + mControllerManager->releaseController( controller ); +} + +void PxWorld::_releaseQueues() +{ + AssertFatal( mScene, "PhysXWorld::_releaseQueues() - The scene is null!" ); + + // We release joints still pending in the queue + // first as they depend on the actors. + for ( S32 i = 0; i < mReleaseJointQueue.size(); i++ ) + { + NxJoint *currJoint = mReleaseJointQueue[i]; + mScene->releaseJoint( *currJoint ); + } + + // All the joints should be released, clear the queue. + mReleaseJointQueue.clear(); + + // Now release any actors still pending in the queue. + for ( S32 i = 0; i < mReleaseActorQueue.size(); i++ ) + { + NxActor *currActor = mReleaseActorQueue[i]; + mScene->releaseActor( *currActor ); + } + + // All the actors should be released, clear the queue. + mReleaseActorQueue.clear(); + + // Now release any materials still pending in the queue. + for ( S32 i = 0; i < mReleaseMaterialQueue.size(); i++ ) + { + NxMaterial *currMat = mReleaseMaterialQueue[i]; + mScene->releaseMaterial( *currMat ); + } + + // All the materials should be released, clear the queue. + mReleaseMaterialQueue.clear(); + + // Now release any cloth still pending in the queue. + for ( S32 i = 0; i < mReleaseClothQueue.size(); i++ ) + { + NxCloth *currCloth = mReleaseClothQueue[i]; + mScene->releaseCloth( *currCloth ); + } + + // All the actors should be released, clear the queue. + mReleaseClothQueue.clear(); + + // Release heightfields that don't still have references. + for ( S32 i = 0; i < mReleaseHeightFieldQueue.size(); i++ ) + { + NxHeightField *currHeightfield = mReleaseHeightFieldQueue[i]; + + if ( currHeightfield->getReferenceCount() == 0 ) + { + gPhysicsSDK->releaseHeightField( *currHeightfield ); + mReleaseHeightFieldQueue.erase_fast( i ); + i--; + } + } + + // Clear fluid queue + for ( S32 i = 0; i < mReleaseFluidQueue.size(); i++ ) + { + NxFluid *currFluid = mReleaseFluidQueue[i]; + mScene->releaseFluid( *currFluid ); + } + mReleaseFluidQueue.clear(); +} + +void PxWorld::setEnabled( bool enabled ) +{ + mIsEnabled = enabled; + + if ( !mIsEnabled ) + getPhysicsResults(); +} + +bool PxWorld::initWorld( bool isServer, ProcessList *processList ) +{ + /* This stuff is handled outside. + PxWorld* world = PxWorld::getWorld( isServer ); + if ( world ) + { + Con::errorf( "PhysXWorld::initWorld - %s world already exists!", isServer ? "Server" : "Client" ); + return false; + } + */ + + if ( !_init( isServer, processList ) ) + return false; + + return true; +} + +void PxWorld::destroyWorld() +{ + //PxWorld* world = PxWorld::getWorld( serverWorld ); + /* + if ( !world ) + { + Con::errorf( "PhysXWorld::destroyWorld - %s world already destroyed!", serverWorld ? "Server" : "Client" ); + return; + } + */ + //world->_destroy(); + //delete world; + + _destroy(); +} + +bool PxWorld::castRay( const Point3F &startPnt, const Point3F &endPnt, RayInfo *ri, const Point3F &impulse ) +{ + NxRay worldRay; + worldRay.orig = pxCast( startPnt ); + worldRay.dir = pxCast( endPnt - startPnt ); + NxF32 maxDist = worldRay.dir.magnitude(); + worldRay.dir.normalize(); + + U32 groups = 0xffffffff; + groups &= ~( 1<<31 ); // No trigger shapes! + + NxRaycastHit hitInfo; + NxShape *hitShape = mScene->raycastClosestShape( worldRay, NX_ALL_SHAPES, hitInfo, groups, maxDist ); + + if ( !hitShape ) + return false; + + //if ( hitShape->userData != NULL ) + // return false; + + NxActor &actor = hitShape->getActor(); + PhysicsUserData *userData = PhysicsUserData::cast( actor.userData ); + + if ( ri ) + { + ri->object = ( userData != NULL ) ? userData->getObject() : NULL; + + // If we were passed a RayInfo, we can only return true signifying a collision + // if we hit an object that actually has a torque object associated with it. + // + // In some ways this could be considered an error, either a physx object + // has raycast-collision enabled that shouldn't or someone did not set + // an object in this actor's userData. + // + if ( ri->object == NULL ) + return false; + + ri->distance = hitInfo.distance; + ri->normal = pxCast( hitInfo.worldNormal ); + ri->point = pxCast( hitInfo.worldImpact ); + ri->t = maxDist / hitInfo.distance; + } + + if ( impulse.isZero() || + !actor.isDynamic() || + actor.readBodyFlag( NX_BF_KINEMATIC ) ) + return true; + + NxVec3 force = pxCast( impulse );//worldRay.dir * forceAmt; + actor.addForceAtPos( force, hitInfo.worldImpact, NX_IMPULSE ); + + return true; +} + +PhysicsBody* PxWorld::castRay( const Point3F &start, const Point3F &end, U32 bodyTypes ) +{ + NxRay worldRay; + worldRay.orig = pxCast( start ); + worldRay.dir = pxCast( end - start ); + F32 maxDist = worldRay.dir.normalize(); + + U32 groups = 0xFFFFFFFF; + if ( !( bodyTypes & BT_Player ) ) + groups &= ~( 1<<29 ); + + // TODO: For now always skip triggers and debris, + // but we should consider how game specifc this API + // should be in the future. + groups &= ~( 1<<31 ); // triggers + groups &= ~( 1<<30 ); // debris + + U32 shapesType = 0; + if ( bodyTypes & BT_Static ) + shapesType |= NX_STATIC_SHAPES; + if ( bodyTypes & BT_Dynamic ) + shapesType |= NX_DYNAMIC_SHAPES; + + NxRaycastHit hitInfo; + NxShape *hitShape = mScene->raycastClosestShape( worldRay, (NxShapesType)shapesType, hitInfo, groups, maxDist ); + if ( !hitShape ) + return NULL; + + NxActor &actor = hitShape->getActor(); + PhysicsUserData *userData = PhysicsUserData::cast( actor.userData ); + if ( !userData ) + return NULL; + + return userData->getBody(); +} + +void PxWorld::explosion( const Point3F &pos, F32 radius, F32 forceMagnitude ) +{ + // Find Actors at the position within the radius + // and apply force to them. + + NxVec3 nxPos = pxCast( pos ); + NxShape **shapes = (NxShape**)NxAlloca(10*sizeof(NxShape*)); + NxSphere worldSphere( nxPos, radius ); + + NxU32 numHits = mScene->overlapSphereShapes( worldSphere, NX_ALL_SHAPES, 10, shapes, NULL ); + + for ( NxU32 i = 0; i < numHits; i++ ) + { + NxActor &actor = shapes[i]->getActor(); + + bool dynamic = actor.isDynamic(); + + if ( !dynamic ) + continue; + + bool kinematic = actor.readBodyFlag( NX_BF_KINEMATIC ); + + if ( kinematic ) + continue; + + NxVec3 force = actor.getGlobalPosition() - nxPos; + force.normalize(); + force *= forceMagnitude; + + actor.addForceAtPos( force, nxPos, NX_IMPULSE, true ); + } +} + +static ColorI getDebugColor( NxU32 packed ) +{ + ColorI col; + col.blue = (packed)&0xff; + col.green = (packed>>8)&0xff; + col.red = (packed>>16)&0xff; + col.alpha = 255; + + return col; +} + +void PxWorld::onDebugDraw( const SceneRenderState *state ) +{ + if ( !mScene ) + return; + + // We need the write lock to be able to request + // the NxDebugRenderable object. + releaseWriteLock(); + + // TODO: We need to expose the different types of visualization + // options to script and add a GUI for toggling them! + + gPhysicsSDK->setParameter( NX_VISUALIZATION_SCALE, 1.0f ); + //gPhysicsSDK->setParameter( NX_VISUALIZE_BODY_MASS_AXES, 0.0f ); + gPhysicsSDK->setParameter( NX_VISUALIZE_BODY_AXES, 1.0f ); + gPhysicsSDK->setParameter( NX_VISUALIZE_COLLISION_SHAPES, 1.0f ); + + const NxDebugRenderable *data = mScene->getDebugRenderable(); + if ( !data ) + return; + + // Render points + { + NxU32 numPoints = data->getNbPoints(); + const NxDebugPoint *points = data->getPoints(); + + PrimBuild::begin( GFXPointList, numPoints ); + + while ( numPoints-- ) + { + PrimBuild::color( getDebugColor(points->color) ); + PrimBuild::vertex3fv( &points->p.x ); + points++; + } + + PrimBuild::end(); + } + + // Render lines + { + NxU32 numLines = data->getNbLines(); + const NxDebugLine *lines = data->getLines(); + + PrimBuild::begin( GFXLineList, numLines * 2 ); + + while ( numLines-- ) + { + PrimBuild::color( getDebugColor( lines->color ) ); + PrimBuild::vertex3fv( &lines->p0.x ); + PrimBuild::vertex3fv( &lines->p1.x ); + lines++; + } + + PrimBuild::end(); + } + + // Render triangles + { + NxU32 numTris = data->getNbTriangles(); + const NxDebugTriangle *triangles = data->getTriangles(); + + PrimBuild::begin( GFXTriangleList, numTris * 3 ); + + while ( numTris-- ) + { + PrimBuild::color( getDebugColor( triangles->color ) ); + PrimBuild::vertex3fv( &triangles->p0.x ); + PrimBuild::vertex3fv( &triangles->p1.x ); + PrimBuild::vertex3fv( &triangles->p2.x ); + triangles++; + } + + PrimBuild::end(); + } +} diff --git a/Engine/source/T3D/physics/physx/pxWorld.h b/Engine/source/T3D/physics/physx/pxWorld.h new file mode 100644 index 000000000..d9d570336 --- /dev/null +++ b/Engine/source/T3D/physics/physx/pxWorld.h @@ -0,0 +1,193 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PHYSX_WORLD_H_ +#define _PHYSX_WORLD_H_ + +#ifndef _T3D_PHYSICS_PHYSICSWORLD_H_ +#include "T3D/physics/physicsWorld.h" +#endif +#ifndef _MMATH_H_ +#include "math/mMath.h" +#endif +#ifndef _PHYSX_H_ +#include "T3D/physics/physX/px.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +class PxContactReporter; +class PxUserNotify; +class NxController; +class NxControllerDesc; +class ShapeBase; +class TSStatic; +class SceneObject; +class ProcessList; +class GameBase; +class CharacterControllerManager; +class PxConsoleStream; + + +class PxWorld : public PhysicsWorld +{ +protected: + + F32 mEditorTimeScale; + + Vector mReleaseClothQueue; + Vector mReleaseJointQueue; + Vector mReleaseActorQueue; + Vector mReleaseMaterialQueue; + Vector mReleaseHeightFieldQueue; + Vector mReleaseFluidQueue; + //Vector> mReleaseColQueue; + + Vector mCatchupQueue; + + PxContactReporter *mConactReporter; + + PxUserNotify *mUserNotify; + + NxScene *mScene; + + CharacterControllerManager *mControllerManager; + + bool mErrorReport; + + bool mIsEnabled; + + bool mIsSimulating; + + U32 mTickCount; + + ProcessList *mProcessList; + + bool _init( bool isServer, ProcessList *processList ); + + void _destroy(); + + void _releaseQueues(); + + void _updateScheduledStatics(); + + /// The mesh cooking interface which is loaded on first use. + /// @see getCooking + static NxCookingInterface *smCooking; + + /// The console stream for PhysX error reporting. + static PxConsoleStream *smConsoleStream; + +public: + + // PhysicWorld + virtual bool initWorld( bool isServer, ProcessList *processList ); + virtual void destroyWorld(); + virtual bool castRay( const Point3F &startPnt, const Point3F &endPnt, RayInfo *ri, const Point3F &impulse ); + virtual PhysicsBody* castRay( const Point3F &start, const Point3F &end, U32 bodyTypes = BT_All ); + virtual void explosion( const Point3F &pos, F32 radius, F32 forceMagnitude ); + virtual void onDebugDraw( const SceneRenderState *state ); + virtual void reset() {} + virtual bool isEnabled() const { return mIsEnabled; } + + /// @name Static Methods + /// @{ + + static bool restartSDK( bool destroyOnly = false, PxWorld *clientWorld = NULL, PxWorld *serverWorld = NULL ); + + static void releaseWriteLocks(); + + /// @} + + PxWorld(); + virtual ~PxWorld(); + +public: + + NxScene* getScene() { return mScene; } + + /// Returns the cooking interface. Will only return NULL + /// in the case of a missing or bad PhysX install. + static NxCookingInterface* getCooking(); + + U32 getTick() { return mTickCount; } + + void tickPhysics( U32 elapsedMs ); + + void getPhysicsResults(); + + //void enableCatchupMode( GameBase *obj ); + + bool isWritable() const { return !mIsSimulating; /* mScene->isWritable(); */ } + + void releaseWriteLock(); + + void setEnabled( bool enabled ); + bool getEnabled() const { return mIsEnabled; } + + NxMaterial* createMaterial( NxMaterialDesc &material ); + + /// + /// @see releaseController + NxController* createController( NxControllerDesc &desc ); + + //U16 setMaterial(NxMaterialDesc &material, U16 id); + + // NOTE: This is all a mess, but its a side effect of how + // PhysX works. Many objects cannot be deleted without write + // access to the scene. Worse some objects cannot be deleted + // until their owner objects are deleted first. + // + // For these reasons we have these methods to register objects to be + // released after the Scene has been ticked. + // + // Since there is no common base to PhysX objects we're stuck with + // this list of release methods. + // + + void releaseActor( NxActor &actor ); + + void releaseMaterial( NxMaterial &mat ); + + void releaseJoint( NxJoint &joint ); + + void releaseCloth( NxCloth &cloth ); + + void releaseClothMesh( NxClothMesh &clothMesh ); + + void releaseController( NxController &controller ); + + void releaseHeightField( NxHeightField &heightfield ); + + void releaseFluid( NxFluid &fluid ); + + //void releaseCol( PxCollision *col ); + + /// Returns the contact reporter for this scene. + PxContactReporter* getContactReporter() { return mConactReporter; } + + void setEditorTimeScale( F32 timeScale ) { mEditorTimeScale = timeScale; } + const F32 getEditorTimeScale() const { return mEditorTimeScale; } +}; + +#endif // _PHYSX_WORLD_H_ diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp new file mode 100644 index 000000000..468cb138b --- /dev/null +++ b/Engine/source/T3D/player.cpp @@ -0,0 +1,6971 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/player.h" + +#include "platform/profiler.h" +#include "math/mMath.h" +#include "math/mathIO.h" +#include "core/resourceManager.h" +#include "core/stringTable.h" +#include "core/volume.h" +#include "core/stream/bitStream.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "collision/extrudedPolyList.h" +#include "collision/clippedPolyList.h" +#include "collision/earlyOutPolyList.h" +#include "ts/tsShapeInstance.h" +#include "sfx/sfxSystem.h" +#include "sfx/sfxTrack.h" +#include "sfx/sfxSource.h" +#include "sfx/sfxTypes.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "T3D/gameBase/gameConnection.h" +#include "T3D/trigger.h" +#include "T3D/physicalZone.h" +#include "T3D/item.h" +#include "T3D/missionArea.h" +#include "T3D/fx/particleEmitter.h" +#include "T3D/fx/cameraFXMgr.h" +#include "T3D/fx/splash.h" +#include "T3D/tsStatic.h" +#include "T3D/physics/physicsPlugin.h" +#include "T3D/physics/physicsPlayer.h" +#include "T3D/decal/decalManager.h" +#include "T3D/decal/decalData.h" +#include "materials/baseMatInstance.h" + +// Amount of time if takes to transition to a new action sequence. +static F32 sAnimationTransitionTime = 0.25f; +static bool sUseAnimationTransitions = true; +static F32 sLandReverseScale = 0.25f; +static F32 sSlowStandThreshSquared = 1.69f; +static S32 sRenderMyPlayer = true; +static S32 sRenderMyItems = true; +static bool sRenderPlayerCollision = false; + +// Chooses new action animations every n ticks. +static const F32 sNewAnimationTickTime = 4.0f; +static const F32 sMountPendingTickWait = 13.0f * F32(TickMs); + +// Number of ticks before we pick non-contact animations +static const S32 sContactTickTime = 10; + +// Movement constants +static F32 sVerticalStepDot = 0.173f; // 80 +static F32 sMinFaceDistance = 0.01f; +static F32 sTractionDistance = 0.04f; +static F32 sNormalElasticity = 0.01f; +static U32 sMoveRetryCount = 5; +static F32 sMaxImpulseVelocity = 200.0f; + +// Move triggers +static S32 sJumpTrigger = 2; +static S32 sCrouchTrigger = 3; +static S32 sProneTrigger = 4; +static S32 sSprintTrigger = 5; +static S32 sImageTrigger0 = 0; +static S32 sImageTrigger1 = 1; +static S32 sJumpJetTrigger = 1; +static S32 sVehicleDismountTrigger = 2; + +// Client prediction +static F32 sMinWarpTicks = 0.5f; // Fraction of tick at which instant warp occurs +static S32 sMaxWarpTicks = 3; // Max warp duration in ticks +static S32 sMaxPredictionTicks = 30; // Number of ticks to predict + +// Anchor point compression +const F32 sAnchorMaxDistance = 32.0f; + +// +static U32 sCollisionMoveMask = TerrainObjectType | + InteriorObjectType | + WaterObjectType | + PlayerObjectType | + StaticShapeObjectType | + VehicleObjectType | + PhysicalZoneObjectType; + +static U32 sServerCollisionContactMask = sCollisionMoveMask | + ItemObjectType | + TriggerObjectType | + CorpseObjectType; + +static U32 sClientCollisionContactMask = sCollisionMoveMask | + TriggerObjectType; + +enum PlayerConstants { + JumpSkipContactsMax = 8 +}; + +//---------------------------------------------------------------------------- +// Player shape animation sequences: + +// look Used to control the upper body arm motion. Must animate +// vertically +-80 deg. +Player::Range Player::mArmRange(mDegToRad(-80.0f),mDegToRad(+80.0f)); + +// head Used to control the direction the head is looking. Must +// animated vertically +-80 deg . +Player::Range Player::mHeadVRange(mDegToRad(-80.0f),mDegToRad(+80.0f)); + +// Action Animations: +PlayerData::ActionAnimationDef PlayerData::ActionAnimationList[NumTableActionAnims] = +{ + // *** WARNING *** + // This array is indexed using the enum values defined in player.h + + // Root is the default animation + { "root" }, // RootAnim, + + // These are selected in the move state based on velocity + { "run", { 0.0f, 1.0f, 0.0f } }, // RunForwardAnim, + { "back", { 0.0f,-1.0f, 0.0f } }, // BackBackwardAnim + { "side", {-1.0f, 0.0f, 0.0f } }, // SideLeftAnim, + { "side_right", { 1.0f, 0.0f, 0.0f } }, // SideRightAnim, + + { "sprint_root" }, + { "sprint_forward", { 0.0f, 1.0f, 0.0f } }, + { "sprint_backward", { 0.0f,-1.0f, 0.0f } }, + { "sprint_side", {-1.0f, 0.0f, 0.0f } }, + { "sprint_right", { 1.0f, 0.0f, 0.0f } }, + + { "crouch_root" }, + { "crouch_forward", { 0.0f, 1.0f, 0.0f } }, + { "crouch_backward", { 0.0f,-1.0f, 0.0f } }, + { "crouch_side", {-1.0f, 0.0f, 0.0f } }, + { "crouch_right", { 1.0f, 0.0f, 0.0f } }, + + { "prone_root" }, + { "prone_forward", { 0.0f, 1.0f, 0.0f } }, + { "prone_backward", { 0.0f,-1.0f, 0.0f } }, + + { "swim_root" }, + { "swim_forward", { 0.0f, 1.0f, 0.0f } }, + { "swim_backward", { 0.0f,-1.0f, 0.0f } }, + { "swim_left", {-1.0f, 0.0f, 0.0f } }, + { "swim_right", { 1.0f, 0.0f, 0.0f } }, + + // These are set explicitly based on player actions + { "fall" }, // FallAnim + { "jump" }, // JumpAnim + { "standjump" }, // StandJumpAnim + { "land" }, // LandAnim + { "jet" }, // JetAnim +}; + + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +//---------------------------------------------------------------------------- + +IMPLEMENT_CO_DATABLOCK_V1(PlayerData); + +ConsoleDocClass( PlayerData, + "@brief Defines properties for a Player object.\n\n" + "@see Player\n" + "@ingroup gameObjects\n" +); + +IMPLEMENT_CALLBACK( PlayerData, onPoseChange, void, ( Player* obj, const char* oldPose, const char* newPose ), ( obj, oldPose, newPose ), + "@brief Called when the player changes poses.\n\n" + "@param obj The Player object\n" + "@param oldPose The pose the player is switching from.\n" + "@param newPose The pose the player is switching to.\n"); + +IMPLEMENT_CALLBACK( PlayerData, onStartSwim, void, ( Player* obj ), ( obj ), + "@brief Called when the player starts swimming.\n\n" + "@param obj The Player object\n" ); + +IMPLEMENT_CALLBACK( PlayerData, onStopSwim, void, ( Player* obj ), ( obj ), + "@brief Called when the player stops swimming.\n\n" + "@param obj The Player object\n" ); + +IMPLEMENT_CALLBACK( PlayerData, onStartSprintMotion, void, ( Player* obj ), ( obj ), + "@brief Called when the player starts moving while in a Sprint pose.\n\n" + "@param obj The Player object\n" ); + +IMPLEMENT_CALLBACK( PlayerData, onStopSprintMotion, void, ( Player* obj ), ( obj ), + "@brief Called when the player stops moving while in a Sprint pose.\n\n" + "@param obj The Player object\n" ); + +IMPLEMENT_CALLBACK( PlayerData, doDismount, void, ( Player* obj ), ( obj ), + "@brief Called when attempting to dismount the player from a vehicle.\n\n" + "It is up to the doDismount() method to actually perform the dismount. Often " + "there are some conditions that prevent this, such as the vehicle moving too fast.\n" + "@param obj The Player object\n" ); + +IMPLEMENT_CALLBACK( PlayerData, onEnterLiquid, void, ( Player* obj, F32 coverage, const char* type ), ( obj, coverage, type ), + "@brief Called when the player enters a liquid.\n\n" + "@param obj The Player object\n" + "@param coverage Percentage of the player's bounding box covered by the liquid\n" + "@param type The type of liquid the player has entered\n" ); + +IMPLEMENT_CALLBACK( PlayerData, onLeaveLiquid, void, ( Player* obj, const char* type ), ( obj, type ), + "@brief Called when the player leaves a liquid.\n\n" + "@param obj The Player object\n" + "@param type The type of liquid the player has left\n" ); + +IMPLEMENT_CALLBACK( PlayerData, animationDone, void, ( Player* obj ), ( obj ), + "@brief Called on the server when a scripted animation completes.\n\n" + "@param obj The Player object\n" + "@see Player::setActionThread() for setting a scripted animation and its 'hold' parameter to " + "determine if this callback is used.\n" ); + +IMPLEMENT_CALLBACK( PlayerData, onEnterMissionArea, void, ( Player* obj ), ( obj ), + "@brief Called when the player enters the mission area.\n\n" + "@param obj The Player object\n" + "@see MissionArea\n" ); + +IMPLEMENT_CALLBACK( PlayerData, onLeaveMissionArea, void, ( Player* obj ), ( obj ), + "@brief Called when the player leaves the mission area.\n" + "@param obj The Player object\n" + "@see MissionArea\n" ); + +PlayerData::PlayerData() +{ + shadowEnable = true; + shadowSize = 256; + shadowProjectionDistance = 14.0f; + + renderFirstPerson = true; + + // Used for third person image rendering + imageAnimPrefix = StringTable->insert(""); + + allowImageStateAnimation = false; + + // Used for first person image rendering + imageAnimPrefixFP = StringTable->insert(""); + for (U32 i=0; iinsert(""); + mCRCFP[i] = 0; + mValidShapeFP[i] = false; + } + + pickupRadius = 0.0f; + minLookAngle = -1.4f; + maxLookAngle = 1.4f; + maxFreelookAngle = 3.0f; + maxTimeScale = 1.5f; + + mass = 9.0f; // from ShapeBase + maxEnergy = 60.0f; // from ShapeBase + drag = 0.3f; // from ShapeBase + density = 1.1f; // from ShapeBase + + maxStepHeight = 1.0f; + runSurfaceAngle = 80.0f; + + fallingSpeedThreshold = -10.0f; + + recoverDelay = 30; + recoverRunForceScale = 1.0f; + landSequenceTime = 0.0f; + transitionToLand = false; + + // Running/Walking + runForce = 40.0f * 9.0f; + runEnergyDrain = 0.0f; + minRunEnergy = 0.0f; + maxForwardSpeed = 10.0f; + maxBackwardSpeed = 10.0f; + maxSideSpeed = 10.0f; + + // Jumping + jumpForce = 75.0f; + jumpEnergyDrain = 0.0f; + minJumpEnergy = 0.0f; + jumpSurfaceAngle = 78.0f; + jumpDelay = 30; + minJumpSpeed = 500.0f; + maxJumpSpeed = 2.0f * minJumpSpeed; + + // Sprinting + sprintForce = 50.0f * 9.0f; + sprintEnergyDrain = 0.0f; + minSprintEnergy = 0.0f; + maxSprintForwardSpeed = 15.0f; + maxSprintBackwardSpeed = 10.0f; + maxSprintSideSpeed = 10.0f; + sprintStrafeScale = 1.0f; + sprintYawScale = 1.0f; + sprintPitchScale = 1.0f; + sprintCanJump = true; + + // Swimming + swimForce = 55.0f * 9.0f; + maxUnderwaterForwardSpeed = 6.0f; + maxUnderwaterBackwardSpeed = 6.0f; + maxUnderwaterSideSpeed = 6.0f; + + // Crouching + crouchForce = 45.0f * 9.0f; + maxCrouchForwardSpeed = 4.0f; + maxCrouchBackwardSpeed = 4.0f; + maxCrouchSideSpeed = 4.0f; + + // Prone + proneForce = 45.0f * 9.0f; + maxProneForwardSpeed = 2.0f; + maxProneBackwardSpeed = 2.0f; + maxProneSideSpeed = 0.0f; + + // Jetting + jetJumpForce = 0; + jetJumpEnergyDrain = 0; + jetMinJumpEnergy = 0; + jetJumpSurfaceAngle = 78; + jetMinJumpSpeed = 20; + jetMaxJumpSpeed = 100; + + horizMaxSpeed = 80.0f; + horizResistSpeed = 38.0f; + horizResistFactor = 1.0f; + + upMaxSpeed = 80.0f; + upResistSpeed = 38.0f; + upResistFactor = 1.0f; + + minImpactSpeed = 25.0f; + minLateralImpactSpeed = 25.0f; + + decalData = NULL; + decalID = 0; + decalOffset = 0.0f; + + lookAction = 0; + + // size of bounding box + boxSize.set(1.0f, 1.0f, 2.3f); + crouchBoxSize.set(1.0f, 1.0f, 2.0f); + proneBoxSize.set(1.0f, 2.3f, 1.0f); + swimBoxSize.set(1.0f, 2.3f, 1.0f); + + // location of head, torso, legs + boxHeadPercentage = 0.85f; + boxTorsoPercentage = 0.55f; + + // damage locations + boxHeadLeftPercentage = 0; + boxHeadRightPercentage = 1; + boxHeadBackPercentage = 0; + boxHeadFrontPercentage = 1; + + for (S32 i = 0; i < MaxSounds; i++) + sound[i] = NULL; + + footPuffEmitter = NULL; + footPuffID = 0; + footPuffNumParts = 15; + footPuffRadius = .25f; + + dustEmitter = NULL; + dustID = 0; + + splash = NULL; + splashId = 0; + splashVelocity = 1.0f; + splashAngle = 45.0f; + splashFreqMod = 300.0f; + splashVelEpsilon = 0.25f; + bubbleEmitTime = 0.4f; + + medSplashSoundVel = 2.0f; + hardSplashSoundVel = 3.0f; + exitSplashSoundVel = 2.0f; + footSplashHeight = 0.1f; + + dMemset( splashEmitterList, 0, sizeof( splashEmitterList ) ); + dMemset( splashEmitterIDList, 0, sizeof( splashEmitterIDList ) ); + + groundImpactMinSpeed = 10.0f; + groundImpactShakeFreq.set( 10.0f, 10.0f, 10.0f ); + groundImpactShakeAmp.set( 20.0f, 20.0f, 20.0f ); + groundImpactShakeDuration = 1.0f; + groundImpactShakeFalloff = 10.0f; + + // Air control + airControl = 0.0f; + + jumpTowardsNormal = true; + + physicsPlayerType = StringTable->insert(""); + + dMemset( actionList, 0, sizeof(actionList) ); +} + +bool PlayerData::preload(bool server, String &errorStr) +{ + if(!Parent::preload(server, errorStr)) + return false; + + // Resolve objects transmitted from server + if( !server ) + { + for( U32 i = 0; i < MaxSounds; ++ i ) + { + String errorStr; + if( !sfxResolve( &sound[ i ], errorStr ) ) + Con::errorf( "PlayerData::preload: %s", errorStr.c_str() ); + } + } + + // + runSurfaceCos = mCos(mDegToRad(runSurfaceAngle)); + jumpSurfaceCos = mCos(mDegToRad(jumpSurfaceAngle)); + if (minJumpEnergy < jumpEnergyDrain) + minJumpEnergy = jumpEnergyDrain; + + // Jetting + if (jetMinJumpEnergy < jetJumpEnergyDrain) + jetMinJumpEnergy = jetJumpEnergyDrain; + + // Validate some of the data + if (fallingSpeedThreshold > 0.0f) + Con::printf("PlayerData:: Falling speed threshold should be downwards (negative)"); + + if (recoverDelay > (1 << RecoverDelayBits) - 1) { + recoverDelay = (1 << RecoverDelayBits) - 1; + Con::printf("PlayerData:: Recover delay exceeds range (0-%d)",recoverDelay); + } + if (jumpDelay > (1 << JumpDelayBits) - 1) { + jumpDelay = (1 << JumpDelayBits) - 1; + Con::printf("PlayerData:: Jump delay exceeds range (0-%d)",jumpDelay); + } + + // If we don't have a shape don't crash out trying to + // setup animations and sequences. + if ( mShape ) + { + // Go ahead a pre-load the player shape + TSShapeInstance* si = new TSShapeInstance(mShape, false); + TSThread* thread = si->addThread(); + + // Extract ground transform velocity from animations + // Get the named ones first so they can be indexed directly. + ActionAnimation *dp = &actionList[0]; + for (int i = 0; i < NumTableActionAnims; i++,dp++) + { + ActionAnimationDef *sp = &ActionAnimationList[i]; + dp->name = sp->name; + dp->dir.set(sp->dir.x,sp->dir.y,sp->dir.z); + dp->sequence = mShape->findSequence(sp->name); + + // If this is a sprint action and is missing a sequence, attempt to use + // the standard run ones. + if(dp->sequence == -1 && i >= SprintRootAnim && i <= SprintRightAnim) + { + S32 offset = i-SprintRootAnim; + ActionAnimationDef *standDef = &ActionAnimationList[RootAnim+offset]; + dp->sequence = mShape->findSequence(standDef->name); + } + + dp->velocityScale = true; + dp->death = false; + if (dp->sequence != -1) + getGroundInfo(si,thread,dp); + + // No real reason to spam the console about a missing jet animation + if (dStricmp(sp->name, "jet") != 0) + AssertWarn(dp->sequence != -1, avar("PlayerData::preload - Unable to find named animation sequence '%s'!", sp->name)); + } + for (int b = 0; b < mShape->sequences.size(); b++) + { + if (!isTableSequence(b)) + { + dp->sequence = b; + dp->name = mShape->getName(mShape->sequences[b].nameIndex); + dp->velocityScale = false; + getGroundInfo(si,thread,dp++); + } + } + actionCount = dp - actionList; + AssertFatal(actionCount <= NumActionAnims, "Too many action animations!"); + delete si; + + // Resolve lookAction index + dp = &actionList[0]; + String lookName("look"); + for (int c = 0; c < actionCount; c++,dp++) + if( dStricmp( dp->name, lookName ) == 0 ) + lookAction = c; + + // Resolve spine + spineNode[0] = mShape->findNode("Bip01 Pelvis"); + spineNode[1] = mShape->findNode("Bip01 Spine"); + spineNode[2] = mShape->findNode("Bip01 Spine1"); + spineNode[3] = mShape->findNode("Bip01 Spine2"); + spineNode[4] = mShape->findNode("Bip01 Neck"); + spineNode[5] = mShape->findNode("Bip01 Head"); + + // Recoil animations + recoilSequence[0] = mShape->findSequence("light_recoil"); + recoilSequence[1] = mShape->findSequence("medium_recoil"); + recoilSequence[2] = mShape->findSequence("heavy_recoil"); + } + + // Convert pickupRadius to a delta of boundingBox + // + // NOTE: it is not really correct to precalculate a pickupRadius based + // on boxSize since the actual player's bounds can vary by "pose". + // + F32 dr = (boxSize.x > boxSize.y)? boxSize.x: boxSize.y; + if (pickupRadius < dr) + pickupRadius = dr; + else + if (pickupRadius > 2.0f * dr) + pickupRadius = 2.0f * dr; + pickupDelta = (S32)(pickupRadius - dr); + + // Validate jump speed + if (maxJumpSpeed <= minJumpSpeed) + maxJumpSpeed = minJumpSpeed + 0.1f; + + // Load up all the emitters + if (!footPuffEmitter && footPuffID != 0) + if (!Sim::findObject(footPuffID, footPuffEmitter)) + Con::errorf(ConsoleLogEntry::General, "PlayerData::preload - Invalid packet, bad datablockId(footPuffEmitter): 0x%x", footPuffID); + + if (!decalData && decalID != 0 ) + if (!Sim::findObject(decalID, decalData)) + Con::errorf(ConsoleLogEntry::General, "PlayerData::preload Invalid packet, bad datablockId(decalData): 0x%x", decalID); + + if (!dustEmitter && dustID != 0 ) + if (!Sim::findObject(dustID, dustEmitter)) + Con::errorf(ConsoleLogEntry::General, "PlayerData::preload - Invalid packet, bad datablockId(dustEmitter): 0x%x", dustID); + + for (int i=0; ipreloadMaterialList(mShapeFP[i].getPath()) && NetConnection::filesWereDownloaded()) + shapeError = true; + + if(computeCRC) + { + Con::printf("Validation required for mounted image %d shape: %s", i, shapeNameFP[i]); + + Torque::FS::FileNodeRef fileRef = Torque::FS::GetFileNode(mShapeFP[i].getPath()); + + if (!fileRef) + return false; + + if(server) + mCRCFP[i] = fileRef->getChecksum(); + else if(mCRCFP[i] != fileRef->getChecksum()) + { + errorStr = String::ToString("PlayerData: Mounted image %d shape \"%s\" does not match version on server.",i,shapeNameFP[i]); + return false; + } + } + + mValidShapeFP[i] = true; + } + } + + return true; +} + +void PlayerData::getGroundInfo(TSShapeInstance* si, TSThread* thread,ActionAnimation *dp) +{ + dp->death = !dStrnicmp(dp->name, "death", 5); + if (dp->death) + { + // Death animations use roll frame-to-frame changes in ground transform into position + dp->speed = 0.0f; + dp->dir.set(0.0f, 0.0f, 0.0f); + + // Death animations MUST define ground transforms, so add dummy ones if required + if (si->getShape()->sequences[dp->sequence].numGroundFrames == 0) + si->getShape()->setSequenceGroundSpeed(dp->name, Point3F(0, 0, 0), Point3F(0, 0, 0)); + } + else + { + VectorF save = dp->dir; + si->setSequence(thread,dp->sequence,0); + si->animate(); + si->advanceTime(1); + si->animateGround(); + si->getGroundTransform().getColumn(3,&dp->dir); + if ((dp->speed = dp->dir.len()) < 0.01f) + { + // No ground displacement... In this case we'll use the + // default table entry, if there is one. + if (save.len() > 0.01f) + { + dp->dir = save; + dp->speed = 1.0f; + dp->velocityScale = false; + } + else + dp->speed = 0.0f; + } + else + dp->dir *= 1.0f / dp->speed; + } +} + +bool PlayerData::isTableSequence(S32 seq) +{ + // The sequences from the table must already have + // been loaded for this to work. + for (int i = 0; i < NumTableActionAnims; i++) + if (actionList[i].sequence == seq) + return true; + return false; +} + +bool PlayerData::isJumpAction(U32 action) +{ + return (action == JumpAnim || action == StandJumpAnim); +} + +void PlayerData::initPersistFields() +{ + addField( "pickupRadius", TypeF32, Offset(pickupRadius, PlayerData), + "@brief Radius around the player to collide with Items in the scene (on server).\n\n" + "Internally the pickupRadius is added to the larger side of the initial bounding box " + "to determine the actual distance, to a maximum of 2 times the bounding box size. The " + "initial bounding box is that used for the root pose, and therefore doesn't take into " + "account the change in pose.\n"); + addField( "maxTimeScale", TypeF32, Offset(maxTimeScale, PlayerData), + "@brief Maximum time scale for action animations.\n\n" + "If an action animation has a defined ground frame, it is automatically scaled to match the " + "player's ground velocity. This field limits the maximum time scale used even if " + "the player's velocity exceeds it." ); + + addGroup( "Camera" ); + + addField( "renderFirstPerson", TypeBool, Offset(renderFirstPerson, PlayerData), + "@brief Flag controlling whether to render the player shape in first person view.\n\n" ); + + addField( "minLookAngle", TypeF32, Offset(minLookAngle, PlayerData), + "@brief Lowest angle (in radians) the player can look.\n\n" + "@note An angle of zero is straight ahead, with positive up and negative down." ); + addField( "maxLookAngle", TypeF32, Offset(maxLookAngle, PlayerData), + "@brief Highest angle (in radians) the player can look.\n\n" + "@note An angle of zero is straight ahead, with positive up and negative down." ); + addField( "maxFreelookAngle", TypeF32, Offset(maxFreelookAngle, PlayerData), + "@brief Defines the maximum left and right angles (in radians) the player can " + "look in freelook mode.\n\n" ); + + endGroup( "Camera" ); + + addGroup( "Movement" ); + + addField( "maxStepHeight", TypeF32, Offset(maxStepHeight, PlayerData), + "@brief Maximum height the player can step up.\n\n" + "The player will automatically step onto changes in ground height less " + "than maxStepHeight. The player will collide with ground height changes " + "greater than this." ); + + addField( "runForce", TypeF32, Offset(runForce, PlayerData), + "@brief Force used to accelerate the player when running.\n\n" ); + + addField( "runEnergyDrain", TypeF32, Offset(runEnergyDrain, PlayerData), + "@brief Energy value drained each tick that the player is moving.\n\n" + "The player will not be able to move when his energy falls below " + "minRunEnergy.\n" + "@note Setting this to zero will disable any energy drain.\n" + "@see minRunEnergy\n"); + addField( "minRunEnergy", TypeF32, Offset(minRunEnergy, PlayerData), + "@brief Minimum energy level required to run or swim.\n\n" + "@see runEnergyDrain\n"); + + addField( "maxForwardSpeed", TypeF32, Offset(maxForwardSpeed, PlayerData), + "@brief Maximum forward speed when running." ); + addField( "maxBackwardSpeed", TypeF32, Offset(maxBackwardSpeed, PlayerData), + "@brief Maximum backward speed when running." ); + addField( "maxSideSpeed", TypeF32, Offset(maxSideSpeed, PlayerData), + "@brief Maximum sideways speed when running." ); + + addField( "runSurfaceAngle", TypeF32, Offset(runSurfaceAngle, PlayerData), + "@brief Maximum angle from vertical (in degrees) the player can run up.\n\n" ); + + addField( "minImpactSpeed", TypeF32, Offset(minImpactSpeed, PlayerData), + "@brief Minimum impact speed to apply falling damage.\n\n" + "This field also sets the minimum speed for the onImpact callback " + "to be invoked.\n" + "@see ShapeBaseData::onImpact()\n"); + addField( "minLateralImpactSpeed", TypeF32, Offset(minLateralImpactSpeed, PlayerData), + "@brief Minimum impact speed to apply non-falling damage.\n\n" + "This field also sets the minimum speed for the onLateralImpact callback " + "to be invoked.\n" + "@see ShapeBaseData::onLateralImpact()\n"); + + addField( "horizMaxSpeed", TypeF32, Offset(horizMaxSpeed, PlayerData), + "@brief Maximum horizontal speed.\n\n" + "@note This limit is only enforced if the player's horizontal speed " + "exceeds horizResistSpeed.\n" + "@see horizResistSpeed\n" + "@see horizResistFactor\n" ); + addField( "horizResistSpeed", TypeF32, Offset(horizResistSpeed, PlayerData), + "@brief Horizontal speed at which resistence will take place.\n\n" + "@see horizMaxSpeed\n" + "@see horizResistFactor\n" ); + addField( "horizResistFactor", TypeF32, Offset(horizResistFactor, PlayerData), + "@brief Factor of resistence once horizResistSpeed has been reached.\n\n" + "@see horizMaxSpeed\n" + "@see horizResistSpeed\n" ); + + addField( "upMaxSpeed", TypeF32, Offset(upMaxSpeed, PlayerData), + "@brief Maximum upwards speed.\n\n" + "@note This limit is only enforced if the player's upward speed exceeds " + "upResistSpeed.\n" + "@see upResistSpeed\n" + "@see upResistFactor\n" ); + addField( "upResistSpeed", TypeF32, Offset(upResistSpeed, PlayerData), + "@brief Upwards speed at which resistence will take place.\n\n" + "@see upMaxSpeed\n" + "@see upResistFactor\n" ); + addField( "upResistFactor", TypeF32, Offset(upResistFactor, PlayerData), + "@brief Factor of resistence once upResistSpeed has been reached.\n\n" + "@see upMaxSpeed\n" + "@see upResistSpeed\n" ); + + endGroup( "Movement" ); + + addGroup( "Movement: Jumping" ); + + addField( "jumpForce", TypeF32, Offset(jumpForce, PlayerData), + "@brief Force used to accelerate the player when a jump is initiated.\n\n" ); + + addField( "jumpEnergyDrain", TypeF32, Offset(jumpEnergyDrain, PlayerData), + "@brief Energy level drained each time the player jumps.\n\n" + "@note Setting this to zero will disable any energy drain\n" + "@see minJumpEnergy\n"); + addField( "minJumpEnergy", TypeF32, Offset(minJumpEnergy, PlayerData), + "@brief Minimum energy level required to jump.\n\n" + "@see jumpEnergyDrain\n"); + + addField( "minJumpSpeed", TypeF32, Offset(minJumpSpeed, PlayerData), + "@brief Minimum speed needed to jump.\n\n" + "If the player's own z velocity is greater than this, then it is used to scale " + "the jump speed, up to maxJumpSpeed.\n" + "@see maxJumpSpeed\n"); + addField( "maxJumpSpeed", TypeF32, Offset(maxJumpSpeed, PlayerData), + "@brief Maximum vertical speed before the player can no longer jump.\n\n" ); + addField( "jumpSurfaceAngle", TypeF32, Offset(jumpSurfaceAngle, PlayerData), + "@brief Angle from vertical (in degrees) where the player can jump.\n\n" ); + addField( "jumpDelay", TypeS32, Offset(jumpDelay, PlayerData), + "@brief Delay time in number of ticks ticks between jumps.\n\n" ); + addField( "airControl", TypeF32, Offset(airControl, PlayerData), + "@brief Amount of movement control the player has when in the air.\n\n" + "This is applied as a multiplier to the player's x and y motion.\n"); + addField( "jumpTowardsNormal", TypeBool, Offset(jumpTowardsNormal, PlayerData), + "@brief Controls the direction of the jump impulse.\n" + "When false, jumps are always in the vertical (+Z) direction. When true " + "jumps are in the direction of the ground normal so long as the player is not " + "directly facing the surface. If the player is directly facing the surface, then " + "they will jump straight up.\n" ); + + endGroup( "Movement: Jumping" ); + + addGroup( "Movement: Sprinting" ); + + addField( "sprintForce", TypeF32, Offset(sprintForce, PlayerData), + "@brief Force used to accelerate the player when sprinting.\n\n" ); + + addField( "sprintEnergyDrain", TypeF32, Offset(sprintEnergyDrain, PlayerData), + "@brief Energy value drained each tick that the player is sprinting.\n\n" + "The player will not be able to move when his energy falls below " + "sprintEnergyDrain.\n" + "@note Setting this to zero will disable any energy drain.\n" + "@see minSprintEnergy\n"); + addField( "minSprintEnergy", TypeF32, Offset(minSprintEnergy, PlayerData), + "@brief Minimum energy level required to sprint.\n\n" + "@see sprintEnergyDrain\n"); + + addField( "maxSprintForwardSpeed", TypeF32, Offset(maxSprintForwardSpeed, PlayerData), + "@brief Maximum forward speed when sprinting." ); + addField( "maxSprintBackwardSpeed", TypeF32, Offset(maxSprintBackwardSpeed, PlayerData), + "@brief Maximum backward speed when sprinting." ); + addField( "maxSprintSideSpeed", TypeF32, Offset(maxSprintSideSpeed, PlayerData), + "@brief Maximum sideways speed when sprinting." ); + + addField( "sprintStrafeScale", TypeF32, Offset(sprintStrafeScale, PlayerData), + "@brief Amount to scale strafing motion vector while sprinting." ); + addField( "sprintYawScale", TypeF32, Offset(sprintYawScale, PlayerData), + "@brief Amount to scale yaw motion while sprinting." ); + addField( "sprintPitchScale", TypeF32, Offset(sprintPitchScale, PlayerData), + "@brief Amount to scale pitch motion while sprinting." ); + + addField( "sprintCanJump", TypeBool, Offset(sprintCanJump, PlayerData), + "@brief Can the player jump while sprinting." ); + + endGroup( "Movement: Sprinting" ); + + addGroup( "Movement: Swimming" ); + + addField( "swimForce", TypeF32, Offset(swimForce, PlayerData), + "@brief Force used to accelerate the player when swimming.\n\n" ); + addField( "maxUnderwaterForwardSpeed", TypeF32, Offset(maxUnderwaterForwardSpeed, PlayerData), + "@brief Maximum forward speed when underwater.\n\n" ); + addField( "maxUnderwaterBackwardSpeed", TypeF32, Offset(maxUnderwaterBackwardSpeed, PlayerData), + "@brief Maximum backward speed when underwater.\n\n" ); + addField( "maxUnderwaterSideSpeed", TypeF32, Offset(maxUnderwaterSideSpeed, PlayerData), + "@brief Maximum sideways speed when underwater.\n\n" ); + + endGroup( "Movement: Swimming" ); + + addGroup( "Movement: Crouching" ); + + addField( "crouchForce", TypeF32, Offset(crouchForce, PlayerData), + "@brief Force used to accelerate the player when crouching.\n\n" ); + addField( "maxCrouchForwardSpeed", TypeF32, Offset(maxCrouchForwardSpeed, PlayerData), + "@brief Maximum forward speed when crouching.\n\n" ); + addField( "maxCrouchBackwardSpeed", TypeF32, Offset(maxCrouchBackwardSpeed, PlayerData), + "@brief Maximum backward speed when crouching.\n\n" ); + addField( "maxCrouchSideSpeed", TypeF32, Offset(maxCrouchSideSpeed, PlayerData), + "@brief Maximum sideways speed when crouching.\n\n" ); + + endGroup( "Movement: Crouching" ); + + addGroup( "Movement: Prone" ); + + addField( "proneForce", TypeF32, Offset(proneForce, PlayerData), + "@brief Force used to accelerate the player when prone (laying down).\n\n" ); + addField( "maxProneForwardSpeed", TypeF32, Offset(maxProneForwardSpeed, PlayerData), + "@brief Maximum forward speed when prone (laying down).\n\n" ); + addField( "maxProneBackwardSpeed", TypeF32, Offset(maxProneBackwardSpeed, PlayerData), + "@brief Maximum backward speed when prone (laying down).\n\n" ); + addField( "maxProneSideSpeed", TypeF32, Offset(maxProneSideSpeed, PlayerData), + "@brief Maximum sideways speed when prone (laying down).\n\n" ); + + endGroup( "Movement: Prone" ); + + addGroup( "Movement: Jetting" ); + + addField( "jetJumpForce", TypeF32, Offset(jetJumpForce, PlayerData), + "@brief Force used to accelerate the player when a jet jump is initiated.\n\n" ); + + addField( "jetJumpEnergyDrain", TypeF32, Offset(jetJumpEnergyDrain, PlayerData), + "@brief Energy level drained each time the player jet jumps.\n\n" + "@note Setting this to zero will disable any energy drain\n" + "@see jetMinJumpEnergy\n"); + addField( "jetMinJumpEnergy", TypeF32, Offset(jetMinJumpEnergy, PlayerData), + "@brief Minimum energy level required to jet jump.\n\n" + "@see jetJumpEnergyDrain\n"); + + addField( "jetMinJumpSpeed", TypeF32, Offset(jetMinJumpSpeed, PlayerData), + "@brief Minimum speed needed to jet jump.\n\n" + "If the player's own z velocity is greater than this, then it is used to scale " + "the jet jump speed, up to jetMaxJumpSpeed.\n" + "@see jetMaxJumpSpeed\n"); + addField( "jetMaxJumpSpeed", TypeF32, Offset(jetMaxJumpSpeed, PlayerData), + "@brief Maximum vertical speed before the player can no longer jet jump.\n\n" ); + addField( "jetJumpSurfaceAngle", TypeF32, Offset(jetJumpSurfaceAngle, PlayerData), + "@brief Angle from vertical (in degrees) where the player can jet jump.\n\n" ); + + endGroup( "Movement: Jetting" ); + + addGroup( "Falling" ); + + addField( "fallingSpeedThreshold", TypeF32, Offset(fallingSpeedThreshold, PlayerData), + "@brief Downward speed at which we consider the player falling.\n\n" ); + + addField( "recoverDelay", TypeS32, Offset(recoverDelay, PlayerData), + "@brief Number of ticks for the player to recover from falling.\n\n" ); + + addField( "recoverRunForceScale", TypeF32, Offset(recoverRunForceScale, PlayerData), + "@brief Scale factor applied to runForce while in the recover state.\n\n" + "This can be used to temporarily slow the player's movement after a fall, or " + "prevent the player from moving at all if set to zero.\n" ); + + addField( "landSequenceTime", TypeF32, Offset(landSequenceTime, PlayerData), + "@brief Time of land sequence play back when using new recover system.\n\n" + "If greater than 0 then the legacy fall recovery system will be bypassed " + "in favour of just playing the player's land sequence. The time to " + "recover from a fall then becomes this parameter's time and the land " + "sequence's playback will be scaled to match.\n" + "@see transitionToLand\n" ); + + addField( "transitionToLand", TypeBool, Offset(transitionToLand, PlayerData), + "@brief When going from a fall to a land, should we transition between the two.\n\n" + "@note Only takes affect when landSequenceTime is greater than 0.\n" + "@see landSequenceTime\n" ); + + endGroup( "Falling" ); + + addGroup( "Collision" ); + + addField( "boundingBox", TypePoint3F, Offset(boxSize, PlayerData), + "@brief Size of the bounding box used by the player for collision.\n\n" + "Dimensions are given as \"width depth height\"." ); + addField( "crouchBoundingBox", TypePoint3F, Offset(crouchBoxSize, PlayerData), + "@brief Collision bounding box used when the player is crouching.\n\n" + "@see boundingBox" ); + addField( "proneBoundingBox", TypePoint3F, Offset(proneBoxSize, PlayerData), + "@brief Collision bounding box used when the player is prone (laying down).\n\n" + "@see boundingBox" ); + addField( "swimBoundingBox", TypePoint3F, Offset(swimBoxSize, PlayerData), + "@brief Collision bounding box used when the player is swimming.\n\n" + "@see boundingBox" ); + + addField( "boxHeadPercentage", TypeF32, Offset(boxHeadPercentage, PlayerData), + "@brief Percentage of the player's bounding box height that represents the head.\n\n" + "Used when computing the damage location.\n" + "@see Player::getDamageLocation" ); + addField( "boxTorsoPercentage", TypeF32, Offset(boxTorsoPercentage, PlayerData), + "@brief Percentage of the player's bounding box height that represents the torso.\n\n" + "Used when computing the damage location.\n" + "@see Player::getDamageLocation" ); + addField( "boxHeadLeftPercentage", TypeF32, Offset(boxHeadLeftPercentage, PlayerData), + "@brief Percentage of the player's bounding box width that represents the left side of the head.\n\n" + "Used when computing the damage location.\n" + "@see Player::getDamageLocation" ); + addField( "boxHeadRightPercentage", TypeF32, Offset(boxHeadRightPercentage, PlayerData), + "@brief Percentage of the player's bounding box width that represents the right side of the head.\n\n" + "Used when computing the damage location.\n" + "@see Player::getDamageLocation" ); + addField( "boxHeadBackPercentage", TypeF32, Offset(boxHeadBackPercentage, PlayerData), + "@brief Percentage of the player's bounding box depth that represents the back side of the head.\n\n" + "Used when computing the damage location.\n" + "@see Player::getDamageLocation" ); + addField( "boxHeadFrontPercentage", TypeF32, Offset(boxHeadFrontPercentage, PlayerData), + "@brief Percentage of the player's bounding box depth that represents the front side of the head.\n\n" + "Used when computing the damage location.\n" + "@see Player::getDamageLocation" ); + + endGroup( "Collision" ); + + addGroup( "Interaction: Footsteps" ); + + addField( "footPuffEmitter", TYPEID< ParticleEmitterData >(), Offset(footPuffEmitter, PlayerData), + "@brief Particle emitter used to generate footpuffs (particles created as the player " + "walks along the ground).\n\n" + "@note The generation of foot puffs requires the appropriate triggeres to be defined in the " + "player's animation sequences. Without these, no foot puffs will be generated.\n"); + addField( "footPuffNumParts", TypeS32, Offset(footPuffNumParts, PlayerData), + "@brief Number of footpuff particles to generate each step.\n\n" + "Each foot puff is randomly placed within the defined foot puff radius. This " + "includes having footPuffNumParts set to one.\n" + "@see footPuffRadius\n"); + addField( "footPuffRadius", TypeF32, Offset(footPuffRadius, PlayerData), + "@brief Particle creation radius for footpuff particles.\n\n" + "This is applied to each foot puff particle, even if footPuffNumParts is set to one. So " + "set this value to zero if you want a single foot puff placed at exactly the same location " + "under the player each time.\n"); + addField( "dustEmitter", TYPEID< ParticleEmitterData >(), Offset(dustEmitter, PlayerData), + "@brief Emitter used to generate dust particles.\n\n" + "@note Currently unused." ); + + addField( "decalData", TYPEID< DecalData >(), Offset(decalData, PlayerData), + "@brief Decal to place on the ground for player footsteps.\n\n" ); + addField( "decalOffset",TypeF32, Offset(decalOffset, PlayerData), + "@brief Distance from the center of the model to the right foot.\n\n" + "While this defines the distance to the right foot, it is also used to place " + "the left foot decal as well. Just on the opposite side of the player." ); + + endGroup( "Interaction: Footsteps" ); + + addGroup( "Interaction: Sounds" ); + + addField( "FootSoftSound", TypeSFXTrackName, Offset(sound[FootSoft], PlayerData), + "@brief Sound to play when walking on a surface with Material footstepSoundId 0.\n\n" ); + addField( "FootHardSound", TypeSFXTrackName, Offset(sound[FootHard], PlayerData), + "@brief Sound to play when walking on a surface with Material footstepSoundId 1.\n\n" ); + addField( "FootMetalSound", TypeSFXTrackName, Offset(sound[FootMetal], PlayerData), + "@brief Sound to play when walking on a surface with Material footstepSoundId 2.\n\n" ); + addField( "FootSnowSound", TypeSFXTrackName, Offset(sound[FootSnow], PlayerData), + "@brief Sound to play when walking on a surface with Material footstepSoundId 3.\n\n" ); + + addField( "FootShallowSound", TypeSFXTrackName, Offset(sound[FootShallowSplash], PlayerData), + "@brief Sound to play when walking in water and coverage is less than " + "footSplashHeight.\n\n" + "@see footSplashHeight\n" ); + addField( "FootWadingSound", TypeSFXTrackName, Offset(sound[FootWading], PlayerData), + "@brief Sound to play when walking in water and coverage is less than 1, " + "but > footSplashHeight.\n\n" + "@see footSplashHeight\n" ); + addField( "FootUnderwaterSound", TypeSFXTrackName, Offset(sound[FootUnderWater], PlayerData), + "@brief Sound to play when walking in water and coverage equals 1.0 " + "(fully underwater).\n\n" ); + addField( "FootBubblesSound", TypeSFXTrackName, Offset(sound[FootBubbles], PlayerData), + "@brief Sound to play when walking in water and coverage equals 1.0 " + "(fully underwater).\n\n" ); + addField( "movingBubblesSound", TypeSFXTrackName, Offset(sound[MoveBubbles], PlayerData), + "@brief Sound to play when in water and coverage equals 1.0 (fully underwater).\n\n" + "Note that unlike FootUnderwaterSound, this sound plays even if the " + "player is not moving around in the water.\n" ); + addField( "waterBreathSound", TypeSFXTrackName, Offset(sound[WaterBreath], PlayerData), + "@brief Sound to play when in water and coverage equals 1.0 (fully underwater).\n\n" + "Note that unlike FootUnderwaterSound, this sound plays even if the " + "player is not moving around in the water.\n" ); + + addField( "impactSoftSound", TypeSFXTrackName, Offset(sound[ImpactSoft], PlayerData), + "@brief Sound to play after falling on a surface with Material footstepSoundId 0.\n\n" ); + addField( "impactHardSound", TypeSFXTrackName, Offset(sound[ImpactHard], PlayerData), + "@brief Sound to play after falling on a surface with Material footstepSoundId 1.\n\n" ); + addField( "impactMetalSound", TypeSFXTrackName, Offset(sound[ImpactMetal], PlayerData), + "@brief Sound to play after falling on a surface with Material footstepSoundId 2.\n\n" ); + addField( "impactSnowSound", TypeSFXTrackName, Offset(sound[ImpactSnow], PlayerData), + "@brief Sound to play after falling on a surface with Material footstepSoundId 3.\n\n" ); + + addField( "impactWaterEasy", TypeSFXTrackName, Offset(sound[ImpactWaterEasy], PlayerData), + "@brief Sound to play when entering the water with velocity < " + "mediumSplashSoundVelocity.\n\n" + "@see mediumSplashSoundVelocity\n"); + addField( "impactWaterMedium", TypeSFXTrackName, Offset(sound[ImpactWaterMedium], PlayerData), + "@brief Sound to play when entering the water with velocity >= " + "mediumSplashSoundVelocity and < hardSplashSoundVelocity.\n\n" + "@see mediumSplashSoundVelocity\n" + "@see hardSplashSoundVelocity\n"); + addField( "impactWaterHard", TypeSFXTrackName, Offset(sound[ImpactWaterHard], PlayerData), + "@brief Sound to play when entering the water with velocity >= " + "hardSplashSoundVelocity.\n\n" + "@see hardSplashSoundVelocity\n"); + addField( "exitingWater", TypeSFXTrackName, Offset(sound[ExitWater], PlayerData), + "@brief Sound to play when exiting the water with velocity >= exitSplashSoundVelocity.\n\n" + "@see exitSplashSoundVelocity\n"); + + endGroup( "Interaction: Sounds" ); + + addGroup( "Interaction: Splashes" ); + + addField( "splash", TYPEID< SplashData >(), Offset(splash, PlayerData), + "@brief SplashData datablock used to create splashes when the player moves " + "through water.\n\n" ); + addField( "splashVelocity", TypeF32, Offset(splashVelocity, PlayerData), + "@brief Minimum velocity when moving through water to generate splashes.\n\n" ); + addField( "splashAngle", TypeF32, Offset(splashAngle, PlayerData), + "@brief Maximum angle (in degrees) from pure vertical movement in water to " + "generate splashes.\n\n" ); + + addField( "splashFreqMod", TypeF32, Offset(splashFreqMod, PlayerData), + "@brief Multipled by speed to determine the number of splash particles to generate.\n\n" ); + addField( "splashVelEpsilon", TypeF32, Offset(splashVelEpsilon, PlayerData), + "@brief Minimum speed to generate splash particles.\n\n" ); + addField( "bubbleEmitTime", TypeF32, Offset(bubbleEmitTime, PlayerData), + "@brief Time in seconds to generate bubble particles after entering the water.\n\n" ); + addField( "splashEmitter", TYPEID< ParticleEmitterData >(), Offset(splashEmitterList, PlayerData), NUM_SPLASH_EMITTERS, + "@brief Particle emitters used to generate splash particles.\n\n" ); + + addField( "footstepSplashHeight", TypeF32, Offset(footSplashHeight, PlayerData), + "@brief Water coverage level to choose between FootShallowSound and FootWadingSound.\n\n" + "@see FootShallowSound\n" + "@see FootWadingSound\n"); + + addField( "mediumSplashSoundVelocity", TypeF32, Offset(medSplashSoundVel, PlayerData), + "@brief Minimum velocity when entering the water for choosing between the impactWaterEasy and " + "impactWaterMedium sounds to play.\n\n" + "@see impactWaterEasy\n" + "@see impactWaterMedium\n" ); + addField( "hardSplashSoundVelocity", TypeF32, Offset(hardSplashSoundVel, PlayerData), + "@brief Minimum velocity when entering the water for choosing between the impactWaterMedium and " + "impactWaterHard sound to play.\n\n" + "@see impactWaterMedium\n" + "@see impactWaterHard\n" ); + addField( "exitSplashSoundVelocity", TypeF32, Offset(exitSplashSoundVel, PlayerData), + "@brief Minimum velocity when leaving the water for the exitingWater sound to " + "play.\n\n" + "@see exitingWater"); + + endGroup( "Interaction: Splashes" ); + + addGroup( "Interaction: Ground Impact" ); + + addField( "groundImpactMinSpeed", TypeF32, Offset(groundImpactMinSpeed, PlayerData), + "@brief Minimum falling impact speed to apply damage and initiate the camera " + "shaking effect.\n\n" ); + addField( "groundImpactShakeFreq", TypePoint3F, Offset(groundImpactShakeFreq, PlayerData), + "@brief Frequency of the camera shake effect after falling.\n\n" + "This is how fast to shake the camera.\n"); + addField( "groundImpactShakeAmp", TypePoint3F, Offset(groundImpactShakeAmp, PlayerData), + "@brief Amplitude of the camera shake effect after falling.\n\n" + "This is how much to shake the camera.\n"); + addField( "groundImpactShakeDuration", TypeF32, Offset(groundImpactShakeDuration, PlayerData), + "@brief Duration (in seconds) of the camera shake effect after falling.\n\n" + "This is how long to shake the camera.\n"); + addField( "groundImpactShakeFalloff", TypeF32, Offset(groundImpactShakeFalloff, PlayerData), + "@brief Falloff factor of the camera shake effect after falling.\n\n" + "This is how to fade the camera shake over the duration.\n"); + + endGroup( "Interaction: Ground Impact" ); + + addGroup( "Physics" ); + + // PhysicsPlayer + addField( "physicsPlayerType", TypeString, Offset(physicsPlayerType, PlayerData), + "@brief Specifies the type of physics used by the player.\n\n" + "This depends on the physics module used. An example is 'Capsule'.\n" + "@note Not current used.\n"); + + endGroup( "Physics" ); + + addGroup( "First Person Arms" ); + + addField( "imageAnimPrefixFP", TypeCaseString, Offset(imageAnimPrefixFP, PlayerData), + "@brief Optional prefix to all mounted image animation sequences in first person.\n\n" + "This defines a prefix that will be added when looking up mounted image " + "animation sequences while in first person. It allows for the customization " + "of a first person image based on the type of player.\n"); + + // Mounted images arrays + addArray( "Mounted Images", ShapeBase::MaxMountedImages ); + + addField( "shapeNameFP", TypeShapeFilename, Offset(shapeNameFP, PlayerData), ShapeBase::MaxMountedImages, + "@brief File name of this player's shape that will be used in conjunction with the corresponding mounted image.\n\n" + "These optional parameters correspond to each mounted image slot to indicate a shape that is rendered " + "in addition to the mounted image shape. Typically these are a player's arms (or arm) that is " + "animated along with the mounted image's state animation sequences.\n"); + + endArray( "Mounted Images" ); + + endGroup( "First Person Arms" ); + + addGroup( "Third Person" ); + + addField( "imageAnimPrefix", TypeCaseString, Offset(imageAnimPrefix, PlayerData), + "@brief Optional prefix to all mounted image animation sequences in third person.\n\n" + "This defines a prefix that will be added when looking up mounted image " + "animation sequences while in third person. It allows for the customization " + "of a third person image based on the type of player.\n"); + + addField( "allowImageStateAnimation", TypeBool, Offset(allowImageStateAnimation, PlayerData), + "@brief Allow mounted images to request a sequence be played on the Player.\n\n" + "When true a new thread is added to the player to allow for " + "mounted images to request a sequence be played on the player " + "through the image's state machine. It is only optional so " + "that we don't create a TSThread on the player if we don't " + "need to.\n"); + + endGroup( "Third Person" ); + + Parent::initPersistFields(); +} + +void PlayerData::packData(BitStream* stream) +{ + Parent::packData(stream); + + stream->writeFlag(renderFirstPerson); + + stream->write(minLookAngle); + stream->write(maxLookAngle); + stream->write(maxFreelookAngle); + stream->write(maxTimeScale); + + stream->write(mass); + stream->write(maxEnergy); + stream->write(drag); + stream->write(density); + + stream->write(maxStepHeight); + + stream->write(runForce); + stream->write(runEnergyDrain); + stream->write(minRunEnergy); + stream->write(maxForwardSpeed); + stream->write(maxBackwardSpeed); + stream->write(maxSideSpeed); + stream->write(runSurfaceAngle); + + stream->write(fallingSpeedThreshold); + + stream->write(recoverDelay); + stream->write(recoverRunForceScale); + stream->write(landSequenceTime); + stream->write(transitionToLand); + + // Jumping + stream->write(jumpForce); + stream->write(jumpEnergyDrain); + stream->write(minJumpEnergy); + stream->write(minJumpSpeed); + stream->write(maxJumpSpeed); + stream->write(jumpSurfaceAngle); + stream->writeInt(jumpDelay,JumpDelayBits); + + // Sprinting + stream->write(sprintForce); + stream->write(sprintEnergyDrain); + stream->write(minSprintEnergy); + stream->write(maxSprintForwardSpeed); + stream->write(maxSprintBackwardSpeed); + stream->write(maxSprintSideSpeed); + stream->write(sprintStrafeScale); + stream->write(sprintYawScale); + stream->write(sprintPitchScale); + stream->writeFlag(sprintCanJump); + + // Swimming + stream->write(swimForce); + stream->write(maxUnderwaterForwardSpeed); + stream->write(maxUnderwaterBackwardSpeed); + stream->write(maxUnderwaterSideSpeed); + + // Crouching + stream->write(crouchForce); + stream->write(maxCrouchForwardSpeed); + stream->write(maxCrouchBackwardSpeed); + stream->write(maxCrouchSideSpeed); + + // Prone + stream->write(proneForce); + stream->write(maxProneForwardSpeed); + stream->write(maxProneBackwardSpeed); + stream->write(maxProneSideSpeed); + + // Jetting + stream->write(jetJumpForce); + stream->write(jetJumpEnergyDrain); + stream->write(jetMinJumpEnergy); + stream->write(jetMinJumpSpeed); + stream->write(jetMaxJumpSpeed); + stream->write(jetJumpSurfaceAngle); + + stream->write(horizMaxSpeed); + stream->write(horizResistSpeed); + stream->write(horizResistFactor); + + stream->write(upMaxSpeed); + stream->write(upResistSpeed); + stream->write(upResistFactor); + + stream->write(splashVelocity); + stream->write(splashAngle); + stream->write(splashFreqMod); + stream->write(splashVelEpsilon); + stream->write(bubbleEmitTime); + + stream->write(medSplashSoundVel); + stream->write(hardSplashSoundVel); + stream->write(exitSplashSoundVel); + stream->write(footSplashHeight); + // Don't need damage scale on the client + stream->write(minImpactSpeed); + stream->write(minLateralImpactSpeed); + + for( U32 i = 0; i < MaxSounds; i++) + sfxWrite( stream, sound[ i ] ); + + mathWrite(*stream, boxSize); + mathWrite(*stream, crouchBoxSize); + mathWrite(*stream, proneBoxSize); + mathWrite(*stream, swimBoxSize); + + if( stream->writeFlag( footPuffEmitter ) ) + { + stream->writeRangedU32( footPuffEmitter->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + + stream->write( footPuffNumParts ); + stream->write( footPuffRadius ); + + if( stream->writeFlag( decalData ) ) + { + stream->writeRangedU32( decalData->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + stream->write(decalOffset); + + if( stream->writeFlag( dustEmitter ) ) + { + stream->writeRangedU32( dustEmitter->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + + + if (stream->writeFlag( splash )) + { + stream->writeRangedU32(splash->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast); + } + + for( U32 i=0; iwriteFlag( splashEmitterList[i] != NULL ) ) + { + stream->writeRangedU32( splashEmitterList[i]->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + } + + + stream->write(groundImpactMinSpeed); + stream->write(groundImpactShakeFreq.x); + stream->write(groundImpactShakeFreq.y); + stream->write(groundImpactShakeFreq.z); + stream->write(groundImpactShakeAmp.x); + stream->write(groundImpactShakeAmp.y); + stream->write(groundImpactShakeAmp.z); + stream->write(groundImpactShakeDuration); + stream->write(groundImpactShakeFalloff); + + // Air control + stream->write(airControl); + + // Jump off at normal + stream->writeFlag(jumpTowardsNormal); + + stream->writeString(physicsPlayerType); + + // Third person mounted image shapes + stream->writeString(imageAnimPrefix); + + stream->writeFlag(allowImageStateAnimation); + + // First person mounted image shapes + stream->writeString(imageAnimPrefixFP); + for (U32 i=0; iwriteString(shapeNameFP[i]); + + // computeCRC is handled in ShapeBaseData + if (computeCRC) + { + stream->write(mCRCFP[i]); + } + } +} + +void PlayerData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + + renderFirstPerson = stream->readFlag(); + + stream->read(&minLookAngle); + stream->read(&maxLookAngle); + stream->read(&maxFreelookAngle); + stream->read(&maxTimeScale); + + stream->read(&mass); + stream->read(&maxEnergy); + stream->read(&drag); + stream->read(&density); + + stream->read(&maxStepHeight); + + stream->read(&runForce); + stream->read(&runEnergyDrain); + stream->read(&minRunEnergy); + stream->read(&maxForwardSpeed); + stream->read(&maxBackwardSpeed); + stream->read(&maxSideSpeed); + stream->read(&runSurfaceAngle); + + stream->read(&fallingSpeedThreshold); + stream->read(&recoverDelay); + stream->read(&recoverRunForceScale); + stream->read(&landSequenceTime); + stream->read(&transitionToLand); + + // Jumping + stream->read(&jumpForce); + stream->read(&jumpEnergyDrain); + stream->read(&minJumpEnergy); + stream->read(&minJumpSpeed); + stream->read(&maxJumpSpeed); + stream->read(&jumpSurfaceAngle); + jumpDelay = stream->readInt(JumpDelayBits); + + // Sprinting + stream->read(&sprintForce); + stream->read(&sprintEnergyDrain); + stream->read(&minSprintEnergy); + stream->read(&maxSprintForwardSpeed); + stream->read(&maxSprintBackwardSpeed); + stream->read(&maxSprintSideSpeed); + stream->read(&sprintStrafeScale); + stream->read(&sprintYawScale); + stream->read(&sprintPitchScale); + sprintCanJump = stream->readFlag(); + + // Swimming + stream->read(&swimForce); + stream->read(&maxUnderwaterForwardSpeed); + stream->read(&maxUnderwaterBackwardSpeed); + stream->read(&maxUnderwaterSideSpeed); + + // Crouching + stream->read(&crouchForce); + stream->read(&maxCrouchForwardSpeed); + stream->read(&maxCrouchBackwardSpeed); + stream->read(&maxCrouchSideSpeed); + + // Prone + stream->read(&proneForce); + stream->read(&maxProneForwardSpeed); + stream->read(&maxProneBackwardSpeed); + stream->read(&maxProneSideSpeed); + + // Jetting + stream->read(&jetJumpForce); + stream->read(&jetJumpEnergyDrain); + stream->read(&jetMinJumpEnergy); + stream->read(&jetMinJumpSpeed); + stream->read(&jetMaxJumpSpeed); + stream->read(&jetJumpSurfaceAngle); + + stream->read(&horizMaxSpeed); + stream->read(&horizResistSpeed); + stream->read(&horizResistFactor); + + stream->read(&upMaxSpeed); + stream->read(&upResistSpeed); + stream->read(&upResistFactor); + + stream->read(&splashVelocity); + stream->read(&splashAngle); + stream->read(&splashFreqMod); + stream->read(&splashVelEpsilon); + stream->read(&bubbleEmitTime); + + stream->read(&medSplashSoundVel); + stream->read(&hardSplashSoundVel); + stream->read(&exitSplashSoundVel); + stream->read(&footSplashHeight); + + stream->read(&minImpactSpeed); + stream->read(&minLateralImpactSpeed); + + for( U32 i = 0; i < MaxSounds; i++) + sfxRead( stream, &sound[ i ] ); + + mathRead(*stream, &boxSize); + mathRead(*stream, &crouchBoxSize); + mathRead(*stream, &proneBoxSize); + mathRead(*stream, &swimBoxSize); + + if( stream->readFlag() ) + { + footPuffID = (S32) stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); + } + + stream->read(&footPuffNumParts); + stream->read(&footPuffRadius); + + if( stream->readFlag() ) + { + decalID = (S32) stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); + } + stream->read(&decalOffset); + + if( stream->readFlag() ) + { + dustID = (S32) stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); + } + + + if (stream->readFlag()) + { + splashId = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + + for( U32 i=0; ireadFlag() ) + { + splashEmitterIDList[i] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + } + + stream->read(&groundImpactMinSpeed); + stream->read(&groundImpactShakeFreq.x); + stream->read(&groundImpactShakeFreq.y); + stream->read(&groundImpactShakeFreq.z); + stream->read(&groundImpactShakeAmp.x); + stream->read(&groundImpactShakeAmp.y); + stream->read(&groundImpactShakeAmp.z); + stream->read(&groundImpactShakeDuration); + stream->read(&groundImpactShakeFalloff); + + // Air control + stream->read(&airControl); + + // Jump off at normal + jumpTowardsNormal = stream->readFlag(); + + physicsPlayerType = stream->readSTString(); + + // Third person mounted image shapes + imageAnimPrefix = stream->readSTString(); + + allowImageStateAnimation = stream->readFlag(); + + // First person mounted image shapes + imageAnimPrefixFP = stream->readSTString(); + for (U32 i=0; ireadSTString(); + + // computeCRC is handled in ShapeBaseData + if (computeCRC) + { + stream->read(&(mCRCFP[i])); + } + } +} + + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +ImplementEnumType( PlayerPose, + "@brief The pose of the Player.\n\n" + "@ingroup gameObjects\n\n") + { Player::StandPose, "Stand", "Standard movement pose.\n" }, + { Player::SprintPose, "Sprint", "Sprinting pose.\n" }, + { Player::CrouchPose, "Crouch", "Crouch pose.\n" }, + { Player::PronePose, "Prone", "Prone pose.\n" }, + { Player::SwimPose, "Swim", "Swimming pose.\n" }, +EndImplementEnumType; + +//---------------------------------------------------------------------------- + +IMPLEMENT_CO_NETOBJECT_V1(Player); + +ConsoleDocClass( Player, + "@ingroup gameObjects\n" +); + +F32 Player::mGravity = -20; + +//---------------------------------------------------------------------------- + +Player::Player() +{ + mTypeMask |= PlayerObjectType | DynamicShapeObjectType; + + delta.pos = mAnchorPoint = Point3F(0,0,100); + delta.rot = delta.head = Point3F(0,0,0); + delta.rotOffset.set(0.0f,0.0f,0.0f); + delta.warpOffset.set(0.0f,0.0f,0.0f); + delta.posVec.set(0.0f,0.0f,0.0f); + delta.rotVec.set(0.0f,0.0f,0.0f); + delta.headVec.set(0.0f,0.0f,0.0f); + delta.warpTicks = 0; + delta.dt = 1.0f; + delta.move = NullMove; + mPredictionCount = sMaxPredictionTicks; + mObjToWorld.setColumn(3,delta.pos); + mRot = delta.rot; + mHead = delta.head; + mVelocity.set(0.0f, 0.0f, 0.0f); + mDataBlock = 0; + mHeadHThread = mHeadVThread = mRecoilThread = mImageStateThread = 0; + mArmAnimation.action = PlayerData::NullAnimation; + mArmAnimation.thread = 0; + mActionAnimation.action = PlayerData::NullAnimation; + mActionAnimation.thread = 0; + mActionAnimation.delayTicks = 0; + mActionAnimation.forward = true; + mActionAnimation.firstPerson = false; + //mActionAnimation.time = 1.0f; //ActionAnimation::Scale; + mActionAnimation.waitForEnd = false; + mActionAnimation.holdAtEnd = false; + mActionAnimation.animateOnServer = false; + mActionAnimation.atEnd = false; + mState = MoveState; + mJetting = false; + mFalling = false; + mSwimming = false; + mInWater = false; + mPose = StandPose; + mContactTimer = 0; + mJumpDelay = 0; + mJumpSurfaceLastContact = 0; + mJumpSurfaceNormal.set(0.0f, 0.0f, 1.0f); + mControlObject = 0; + dMemset( mSplashEmitter, 0, sizeof( mSplashEmitter ) ); + + mUseHeadZCalc = true; + + allowAllPoses(); + + mImpactSound = 0; + mRecoverTicks = 0; + mReversePending = 0; + + mLastPos.set( 0.0f, 0.0f, 0.0f ); + + mMoveBubbleSound = 0; + mWaterBreathSound = 0; + + mConvex.init(this); + mWorkingQueryBox.minExtents.set(-1e9f, -1e9f, -1e9f); + mWorkingQueryBox.maxExtents.set(-1e9f, -1e9f, -1e9f); + + mWeaponBackFraction = 0.0f; + + mInMissionArea = true; + + mBubbleEmitterTime = 10.0; + mLastWaterPos.set( 0.0, 0.0, 0.0 ); + + mMountPending = 0; + + mPhysicsRep = NULL; + + for (U32 i=0; iclearTransition(mActionAnimation.thread); + mShapeInstance->setPos(mActionAnimation.thread, + mActionAnimation.forward ? 1.0f : 0.0f); + if (inDeathAnim()) + mDeath.lastPos = 1.0f; + } + + // We have to leave them sitting for a while since mounts don't come through right + // away (and sometimes not for a while). Still going to let this time out because + // I'm not sure if we're guaranteed another anim will come through and cancel. + if (!isServerObject() && inSittingAnim()) + mMountPending = (S32) sMountPendingTickWait; + else + mMountPending = 0; + } + if (mArmAnimation.action != PlayerData::NullAnimation) + setArmThread(mArmAnimation.action); + + // + if (isServerObject()) + { + scriptOnAdd(); + } + else + { + U32 i; + for( i=0; isplashEmitterList[i] ) + { + mSplashEmitter[i] = new ParticleEmitter; + mSplashEmitter[i]->onNewDataBlock( mDataBlock->splashEmitterList[i], false ); + if( !mSplashEmitter[i]->registerObject() ) + { + Con::warnf( ConsoleLogEntry::General, "Could not register splash emitter for class: %s", mDataBlock->getName() ); + mSplashEmitter[i].getPointer()->destroySelf(); + mSplashEmitter[i] = NULL; + } + } + } + mLastWaterPos = getPosition(); + + // clear out all camera effects + gCamFXMgr.clear(); + } + + if ( PHYSICSMGR ) + { + PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" ); + + mPhysicsRep = PHYSICSMGR->createPlayer(); + mPhysicsRep->init( mDataBlock->physicsPlayerType, + mDataBlock->boxSize, + mDataBlock->runSurfaceCos, + mDataBlock->maxStepHeight, + this, + world ); + mPhysicsRep->setTransform( getTransform() ); + } + + return true; +} + +void Player::onRemove() +{ + setControlObject(0); + scriptOnRemove(); + removeFromScene(); + + U32 i; + for( i=0; ideleteWhenEmpty(); + mSplashEmitter[i] = NULL; + } + } + + mWorkingQueryBox.minExtents.set(-1e9f, -1e9f, -1e9f); + mWorkingQueryBox.maxExtents.set(-1e9f, -1e9f, -1e9f); + + SAFE_DELETE( mPhysicsRep ); + + Parent::onRemove(); +} + +void Player::onScaleChanged() +{ + const Point3F& scale = getScale(); + mScaledBox = mObjBox; + mScaledBox.minExtents.convolve( scale ); + mScaledBox.maxExtents.convolve( scale ); +} + + +//---------------------------------------------------------------------------- + +bool Player::onNewDataBlock( GameBaseData *dptr, bool reload ) +{ + PlayerData* prevData = mDataBlock; + mDataBlock = dynamic_cast(dptr); + if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) ) + return false; + + // Player requires a shape instance. + if ( mShapeInstance == NULL ) + return false; + + // Initialize arm thread, preserve arm sequence from last datablock. + // Arm animation can be from last datablock, or sent from the server. + U32 prevAction = mArmAnimation.action; + mArmAnimation.action = PlayerData::NullAnimation; + if (mDataBlock->lookAction) { + mArmAnimation.thread = mShapeInstance->addThread(); + mShapeInstance->setTimeScale(mArmAnimation.thread,0); + if (prevData) { + if (prevAction != prevData->lookAction && prevAction != PlayerData::NullAnimation) + setArmThread(prevData->actionList[prevAction].name); + prevAction = PlayerData::NullAnimation; + } + if (mArmAnimation.action == PlayerData::NullAnimation) { + mArmAnimation.action = (prevAction != PlayerData::NullAnimation)? + prevAction: mDataBlock->lookAction; + mShapeInstance->setSequence(mArmAnimation.thread, + mDataBlock->actionList[mArmAnimation.action].sequence,0); + } + } + else + mArmAnimation.thread = 0; + + // Initialize head look thread + TSShape const* shape = mShapeInstance->getShape(); + S32 headSeq = shape->findSequence("head"); + if (headSeq != -1) { + mHeadVThread = mShapeInstance->addThread(); + mShapeInstance->setSequence(mHeadVThread,headSeq,0); + mShapeInstance->setTimeScale(mHeadVThread,0); + } + else + mHeadVThread = 0; + + headSeq = shape->findSequence("headside"); + if (headSeq != -1) { + mHeadHThread = mShapeInstance->addThread(); + mShapeInstance->setSequence(mHeadHThread,headSeq,0); + mShapeInstance->setTimeScale(mHeadHThread,0); + } + else + mHeadHThread = 0; + + // Create Recoil thread if any recoil sequences are specified. + // Note that the server player does not play this animation. + mRecoilThread = 0; + if (isGhost()) + for (U32 s = 0; s < PlayerData::NumRecoilSequences; s++) + if (mDataBlock->recoilSequence[s] != -1) { + mRecoilThread = mShapeInstance->addThread(); + mShapeInstance->setSequence(mRecoilThread, mDataBlock->recoilSequence[s], 0); + mShapeInstance->setTimeScale(mRecoilThread, 0); + break; + } + + // Reset the image state driven animation thread. This will be properly built + // in onImageStateAnimation() when needed. + mImageStateThread = 0; + + // Initialize the primary thread, the actual sequence is + // set later depending on player actions. + mActionAnimation.action = PlayerData::NullAnimation; + mActionAnimation.thread = mShapeInstance->addThread(); + updateAnimationTree(!isGhost()); + + // First person mounted image shapes. Only on client. + if ( isGhost() ) + { + for (U32 i=0; imShapeFP[i])) + { + mShapeFPInstance[i] = new TSShapeInstance(mDataBlock->mShapeFP[i], isClientObject()); + + mShapeFPInstance[i]->cloneMaterialList(); + + // Ambient animation + if (mShapeFPAmbientThread[i]) + { + S32 seq = mShapeFPInstance[i]->getShape()->findSequence("ambient"); + if (seq != -1) + { + mShapeFPAmbientThread[i] = mShapeFPInstance[i]->addThread(); + mShapeFPInstance[i]->setTimeScale(mShapeFPAmbientThread[i], 1); + mShapeFPInstance[i]->setSequence(mShapeFPAmbientThread[i], seq, 0); + } + } + + // Standard state animation + mShapeFPAnimThread[i] = mShapeFPInstance[i]->addThread(); + if (mShapeFPAnimThread[i]) + { + mShapeFPInstance[i]->setTimeScale(mShapeFPAnimThread[i],0); + } + } + } + } + + if ( isGhost() ) + { + // Create the sounds ahead of time. This reduces runtime + // costs and makes the system easier to understand. + + SFX_DELETE( mMoveBubbleSound ); + SFX_DELETE( mWaterBreathSound ); + + if ( mDataBlock->sound[PlayerData::MoveBubbles] ) + mMoveBubbleSound = SFX->createSource( mDataBlock->sound[PlayerData::MoveBubbles] ); + + if ( mDataBlock->sound[PlayerData::WaterBreath] ) + mWaterBreathSound = SFX->createSource( mDataBlock->sound[PlayerData::WaterBreath] ); + } + + mObjBox.maxExtents.x = mDataBlock->boxSize.x * 0.5f; + mObjBox.maxExtents.y = mDataBlock->boxSize.y * 0.5f; + mObjBox.maxExtents.z = mDataBlock->boxSize.z; + mObjBox.minExtents.x = -mObjBox.maxExtents.x; + mObjBox.minExtents.y = -mObjBox.maxExtents.y; + mObjBox.minExtents.z = 0.0f; + + // Setup the box for our convex object... + mObjBox.getCenter(&mConvex.mCenter); + mConvex.mSize.x = mObjBox.len_x() / 2.0f; + mConvex.mSize.y = mObjBox.len_y() / 2.0f; + mConvex.mSize.z = mObjBox.len_z() / 2.0f; + + // Initialize our scaled attributes as well + onScaleChanged(); + resetWorldBox(); + + scriptOnNewDataBlock(); + return true; +} + +//---------------------------------------------------------------------------- + +void Player::reSkin() +{ + if ( isGhost() && mShapeInstance && mSkinNameHandle.isValidString() ) + { + Vector skins; + String(mSkinNameHandle.getString()).split( ";", skins ); + + for ( int i = 0; i < skins.size(); i++ ) + { + String oldSkin( mAppliedSkinName.c_str() ); + String newSkin( skins[i] ); + + // Check if the skin handle contains an explicit "old" base string. This + // allows all models to support skinning, even if they don't follow the + // "base_xxx" material naming convention. + S32 split = newSkin.find( '=' ); // "old=new" format skin? + if ( split != String::NPos ) + { + oldSkin = newSkin.substr( 0, split ); + newSkin = newSkin.erase( 0, split+1 ); + } + + // Apply skin to both 3rd person and 1st person shape instances + mShapeInstance->reSkin( newSkin, oldSkin ); + for ( int j = 0; j < ShapeBase::MaxMountedImages; j++ ) + { + if (mShapeFPInstance[j]) + mShapeFPInstance[j]->reSkin( newSkin, oldSkin ); + } + + mAppliedSkinName = newSkin; + } + } +} + +//---------------------------------------------------------------------------- + +void Player::setControllingClient(GameConnection* client) +{ + Parent::setControllingClient(client); + if (mControlObject) + mControlObject->setControllingClient(client); +} + +void Player::setControlObject(ShapeBase* obj) +{ + if (mControlObject == obj) + return; + + if (mControlObject) { + mControlObject->setControllingObject(0); + mControlObject->setControllingClient(0); + } + if (obj == this || obj == 0) + mControlObject = 0; + else { + if (ShapeBase* coo = obj->getControllingObject()) + coo->setControlObject(0); + if (GameConnection* con = obj->getControllingClient()) + con->setControlObject(0); + + mControlObject = obj; + mControlObject->setControllingObject(this); + mControlObject->setControllingClient(getControllingClient()); + } +} + +void Player::onCameraScopeQuery(NetConnection *connection, CameraScopeQuery *query) +{ + // First, we are certainly in scope, and whatever we're riding is too... + if(mControlObject.isNull() || mControlObject == mMount.object) + Parent::onCameraScopeQuery(connection, query); + else + { + connection->objectInScope(this); + if (isMounted()) + connection->objectInScope(mMount.object); + mControlObject->onCameraScopeQuery(connection, query); + } +} + +ShapeBase* Player::getControlObject() +{ + return mControlObject; +} + +void Player::processTick(const Move* move) +{ + PROFILE_SCOPE(Player_ProcessTick); + + bool prevMoveMotion = mMoveMotion; + Pose prevPose = getPose(); + + // If we're not being controlled by a client, let the + // AI sub-module get a chance at producing a move. + Move aiMove; + if (!move && isServerObject() && getAIMove(&aiMove)) + move = &aiMove; + + // Manage the control object and filter moves for the player + Move pMove,cMove; + if (mControlObject) { + if (!move) + mControlObject->processTick(0); + else { + pMove = NullMove; + cMove = *move; + //if (isMounted()) { + // Filter Jump trigger if mounted + //pMove.trigger[2] = move->trigger[2]; + //cMove.trigger[2] = false; + //} + if (move->freeLook) { + // Filter yaw/picth/roll when freelooking. + pMove.yaw = move->yaw; + pMove.pitch = move->pitch; + pMove.roll = move->roll; + pMove.freeLook = true; + cMove.freeLook = false; + cMove.yaw = cMove.pitch = cMove.roll = 0.0f; + } + mControlObject->processTick((mDamageState == Enabled)? &cMove: &NullMove); + move = &pMove; + } + } + + Parent::processTick(move); + // Warp to catch up to server + if (delta.warpTicks > 0) { + delta.warpTicks--; + + // Set new pos + getTransform().getColumn(3, &delta.pos); + delta.pos += delta.warpOffset; + delta.rot += delta.rotOffset; + + // Wrap yaw to +/-PI + if (delta.rot.z < - M_PI_F) + delta.rot.z += M_2PI_F; + else if (delta.rot.z > M_PI_F) + delta.rot.z -= M_2PI_F; + + setPosition(delta.pos,delta.rot); + updateDeathOffsets(); + updateLookAnimation(); + + // Backstepping + delta.posVec = -delta.warpOffset; + delta.rotVec = -delta.rotOffset; + } + else { + // If there is no move, the player is either an + // unattached player on the server, or a player's + // client ghost. + if (!move) { + if (isGhost()) { + // If we haven't run out of prediction time, + // predict using the last known move. + if (mPredictionCount-- <= 0) + return; + + move = &delta.move; + } + else + move = &NullMove; + } + if (!isGhost()) + updateAnimation(TickSec); + + PROFILE_START(Player_PhysicsSection); + if ( isServerObject() || didRenderLastRender() || getControllingClient() ) + { + if ( !mPhysicsRep ) + { + if ( isMounted() ) + { + // If we're mounted then do not perform any collision checks + // and clear our previous working list. + mConvex.clearWorkingList(); + } + else + { + updateWorkingCollisionSet(); + } + } + + updateState(); + updateMove(move); + updateLookAnimation(); + updateDeathOffsets(); + updatePos(); + } + PROFILE_END(); + + if (!isGhost()) + { + // Animations are advanced based on frame rate on the + // client and must be ticked on the server. + updateActionThread(); + updateAnimationTree(true); + + // Check for sprinting motion changes + Pose currentPose = getPose(); + // Player has just switched into Sprint pose and is moving + if (currentPose == SprintPose && prevPose != SprintPose && mMoveMotion) + { + mDataBlock->onStartSprintMotion_callback( this ); + } + // Player has just switched out of Sprint pose and is moving, or was just moving + else if (currentPose != SprintPose && prevPose == SprintPose && (mMoveMotion || prevMoveMotion)) + { + mDataBlock->onStopSprintMotion_callback( this ); + } + // Player is in Sprint pose and has modified their motion + else if (currentPose == SprintPose && prevMoveMotion != mMoveMotion) + { + if (mMoveMotion) + { + mDataBlock->onStartSprintMotion_callback( this ); + } + else + { + mDataBlock->onStopSprintMotion_callback( this ); + } + } + } + } +} + +void Player::interpolateTick(F32 dt) +{ + if (mControlObject) + mControlObject->interpolateTick(dt); + + // Client side interpolation + Parent::interpolateTick(dt); + + Point3F pos = delta.pos + delta.posVec * dt; + Point3F rot = delta.rot + delta.rotVec * dt; + + setRenderPosition(pos,rot,dt); + +/* + // apply camera effects - is this the best place? - bramage + GameConnection* connection = GameConnection::getConnectionToServer(); + if( connection->isFirstPerson() ) + { + ShapeBase *obj = dynamic_cast(connection->getControlObject()); + if( obj == this ) + { + MatrixF curTrans = getRenderTransform(); + curTrans.mul( gCamFXMgr.getTrans() ); + Parent::setRenderTransform( curTrans ); + } + } +*/ + + updateLookAnimation(dt); + delta.dt = dt; +} + +void Player::advanceTime(F32 dt) +{ + // Client side animations + Parent::advanceTime(dt); + updateActionThread(); + updateAnimation(dt); + updateSplash(); + updateFroth(dt); + updateWaterSounds(dt); + + mLastPos = getPosition(); + + if (mImpactSound) + playImpactSound(); + + // update camera effects. Definitely need to find better place for this - bramage + if( isControlObject() ) + { + if( mDamageState == Disabled || mDamageState == Destroyed ) + { + // clear out all camera effects being applied to player if dead + gCamFXMgr.clear(); + } + } +} + +bool Player::getAIMove(Move* move) +{ + return false; +} + +void Player::setState(ActionState state, U32 recoverTicks) +{ + if (state != mState) { + // Skip initialization if there is no manager, the state + // will get reset when the object is added to a manager. + if (isProperlyAdded()) { + switch (state) { + case RecoverState: { + if (mDataBlock->landSequenceTime > 0.0f) + { + // Use the land sequence as the basis for the recovery + setActionThread(PlayerData::LandAnim, true, false, true, true); + F32 timeScale = mShapeInstance->getDuration(mActionAnimation.thread) / mDataBlock->landSequenceTime; + mShapeInstance->setTimeScale(mActionAnimation.thread,timeScale); + mRecoverDelay = mDataBlock->landSequenceTime; + } + else + { + // Legacy recover system + mRecoverTicks = recoverTicks; + mReversePending = U32(F32(mRecoverTicks) / sLandReverseScale); + setActionThread(PlayerData::LandAnim, true, false, true, true); + } + break; + } + + default: + break; + } + } + + mState = state; + } +} + +void Player::updateState() +{ + switch (mState) + { + case RecoverState: + if (mDataBlock->landSequenceTime > 0.0f) + { + // Count down the land time + mRecoverDelay -= TickSec; + if (mRecoverDelay <= 0.0f) + { + setState(MoveState); + } + } + else + { + // Legacy recover system + if (mRecoverTicks-- <= 0) + { + if (mReversePending && mActionAnimation.action != PlayerData::NullAnimation) + { + // this serves and counter, and direction state + mRecoverTicks = mReversePending; + mActionAnimation.forward = false; + + S32 seq = mDataBlock->actionList[mActionAnimation.action].sequence; + S32 imageBasedSeq = convertActionToImagePrefix(mActionAnimation.action); + if (imageBasedSeq != -1) + seq = imageBasedSeq; + + F32 pos = mShapeInstance->getPos(mActionAnimation.thread); + + mShapeInstance->setTimeScale(mActionAnimation.thread, -sLandReverseScale); + mShapeInstance->transitionToSequence(mActionAnimation.thread, + seq, pos, sAnimationTransitionTime, true); + mReversePending = 0; + } + else + { + setState(MoveState); + } + } // Stand back up slowly only if not moving much- + else if (!mReversePending && mVelocity.lenSquared() > sSlowStandThreshSquared) + { + mActionAnimation.waitForEnd = false; + setState(MoveState); + } + } + break; + + default: + break; + } +} + +const char* Player::getStateName() +{ + if (mDamageState != Enabled) + return "Dead"; + if (isMounted()) + return "Mounted"; + if (mState == RecoverState) + return "Recover"; + return "Move"; +} + +void Player::getDamageLocation(const Point3F& in_rPos, const char *&out_rpVert, const char *&out_rpQuad) +{ + // TODO: This will be WRONG when player is prone or swimming! + + Point3F newPoint; + mWorldToObj.mulP(in_rPos, &newPoint); + + Point3F boxSize = mObjBox.getExtents(); + F32 zHeight = boxSize.z; + F32 zTorso = mDataBlock->boxTorsoPercentage; + F32 zHead = mDataBlock->boxHeadPercentage; + + zTorso *= zHeight; + zHead *= zHeight; + + if (newPoint.z <= zTorso) + out_rpVert = "legs"; + else if (newPoint.z <= zHead) + out_rpVert = "torso"; + else + out_rpVert = "head"; + + if(dStrcmp(out_rpVert, "head") != 0) + { + if (newPoint.y >= 0.0f) + { + if (newPoint.x <= 0.0f) + out_rpQuad = "front_left"; + else + out_rpQuad = "front_right"; + } + else + { + if (newPoint.x <= 0.0f) + out_rpQuad = "back_left"; + else + out_rpQuad = "back_right"; + } + } + else + { + F32 backToFront = boxSize.x; + F32 leftToRight = boxSize.y; + + F32 backPoint = backToFront * mDataBlock->boxHeadBackPercentage; + F32 frontPoint = backToFront * mDataBlock->boxHeadFrontPercentage; + F32 leftPoint = leftToRight * mDataBlock->boxHeadLeftPercentage; + F32 rightPoint = leftToRight * mDataBlock->boxHeadRightPercentage; + + S32 index = 0; + if (newPoint.y < backPoint) + index += 0; + else if (newPoint.y >= frontPoint) + index += 3; + else + index += 6; + + if (newPoint.x < leftPoint) + index += 0; + else if (newPoint.x >= rightPoint) + index += 1; + else + index += 2; + + switch (index) + { + case 0: out_rpQuad = "left_back"; break; + case 1: out_rpQuad = "middle_back"; break; + case 2: out_rpQuad = "right_back"; break; + case 3: out_rpQuad = "left_middle"; break; + case 4: out_rpQuad = "middle_middle"; break; + case 5: out_rpQuad = "right_middle"; break; + case 6: out_rpQuad = "left_front"; break; + case 7: out_rpQuad = "middle_front"; break; + case 8: out_rpQuad = "right_front"; break; + + default: + AssertFatal(0, "Bad non-tant index"); + }; + } +} + +const char* Player::getPoseName() const +{ + return EngineMarshallData< PlayerPose >(getPose()); +} + +void Player::setPose( Pose pose ) +{ + // Already the set pose, return. + if ( pose == mPose ) + return; + + Pose oldPose = mPose; + + mPose = pose; + + // Not added yet, just assign the pose and return. + if ( !isProperlyAdded() ) + return; + + Point3F boxSize(1,1,1); + + // Resize the player boxes + switch (pose) + { + case StandPose: + case SprintPose: + boxSize = mDataBlock->boxSize; + break; + case CrouchPose: + boxSize = mDataBlock->crouchBoxSize; + break; + case PronePose: + boxSize = mDataBlock->proneBoxSize; + break; + case SwimPose: + boxSize = mDataBlock->swimBoxSize; + break; + } + + // Object and World Boxes... + mObjBox.maxExtents.x = boxSize.x * 0.5f; + mObjBox.maxExtents.y = boxSize.y * 0.5f; + mObjBox.maxExtents.z = boxSize.z; + mObjBox.minExtents.x = -mObjBox.maxExtents.x; + mObjBox.minExtents.y = -mObjBox.maxExtents.y; + mObjBox.minExtents.z = 0.0f; + + resetWorldBox(); + + // Setup the box for our convex object... + mObjBox.getCenter(&mConvex.mCenter); + mConvex.mSize.x = mObjBox.len_x() / 2.0f; + mConvex.mSize.y = mObjBox.len_y() / 2.0f; + mConvex.mSize.z = mObjBox.len_z() / 2.0f; + + // Initialize our scaled attributes as well... + onScaleChanged(); + + // Resize the PhysicsPlayer rep. should we have one + if ( mPhysicsRep ) + mPhysicsRep->setSpacials( getPosition(), boxSize ); + + if ( isServerObject() ) + mDataBlock->onPoseChange_callback( this, EngineMarshallData< PlayerPose >(oldPose), EngineMarshallData< PlayerPose >(mPose)); +} + +void Player::allowAllPoses() +{ + mAllowJumping = true; + mAllowJetJumping = true; + mAllowSprinting = true; + mAllowCrouching = true; + mAllowProne = true; + mAllowSwimming = true; +} + +void Player::updateMove(const Move* move) +{ + delta.move = *move; + + // Is waterCoverage high enough to be 'swimming'? + { + bool swimming = mWaterCoverage > 0.65f && canSwim(); + + if ( swimming != mSwimming ) + { + if ( !isGhost() ) + { + if ( swimming ) + mDataBlock->onStartSwim_callback( this ); + else + mDataBlock->onStopSwim_callback( this ); + } + + mSwimming = swimming; + } + } + + // Trigger images + if (mDamageState == Enabled) + { + setImageTriggerState( 0, move->trigger[sImageTrigger0] ); + + // If you have a secondary mounted image then + // send the second trigger to it. Else give it + // to the first image as an alt fire. + if ( getMountedImage( 1 ) ) + setImageTriggerState( 1, move->trigger[sImageTrigger1] ); + else + setImageAltTriggerState( 0, move->trigger[sImageTrigger1] ); + } + + // Update current orientation + if (mDamageState == Enabled) { + F32 prevZRot = mRot.z; + delta.headVec = mHead; + + F32 p = move->pitch * (mPose == SprintPose ? mDataBlock->sprintPitchScale : 1.0f); + if (p > M_PI_F) + p -= M_2PI_F; + mHead.x = mClampF(mHead.x + p,mDataBlock->minLookAngle, + mDataBlock->maxLookAngle); + + F32 y = move->yaw * (mPose == SprintPose ? mDataBlock->sprintYawScale : 1.0f); + if (y > M_PI_F) + y -= M_2PI_F; + + GameConnection* con = getControllingClient(); + if (move->freeLook && ((isMounted() && getMountNode() == 0) || (con && !con->isFirstPerson()))) + { + mHead.z = mClampF(mHead.z + y, + -mDataBlock->maxFreelookAngle, + mDataBlock->maxFreelookAngle); + } + else + { + mRot.z += y; + // Rotate the head back to the front, center horizontal + // as well if we're controlling another object. + mHead.z *= 0.5f; + if (mControlObject) + mHead.x *= 0.5f; + } + + // constrain the range of mRot.z + while (mRot.z < 0.0f) + mRot.z += M_2PI_F; + while (mRot.z > M_2PI_F) + mRot.z -= M_2PI_F; + + delta.rot = mRot; + delta.rotVec.x = delta.rotVec.y = 0.0f; + delta.rotVec.z = prevZRot - mRot.z; + if (delta.rotVec.z > M_PI_F) + delta.rotVec.z -= M_2PI_F; + else if (delta.rotVec.z < -M_PI_F) + delta.rotVec.z += M_2PI_F; + + delta.head = mHead; + delta.headVec -= mHead; + } + MatrixF zRot; + zRot.set(EulerF(0.0f, 0.0f, mRot.z)); + + // Desired move direction & speed + VectorF moveVec; + F32 moveSpeed; + if ((mState == MoveState || (mState == RecoverState && mDataBlock->recoverRunForceScale > 0.0f)) && mDamageState == Enabled) + { + zRot.getColumn(0,&moveVec); + moveVec *= (move->x * (mPose == SprintPose ? mDataBlock->sprintStrafeScale : 1.0f)); + VectorF tv; + zRot.getColumn(1,&tv); + moveVec += tv * move->y; + + // Clamp water movement + if (move->y > 0.0f) + { + if ( mSwimming ) + moveSpeed = getMax(mDataBlock->maxUnderwaterForwardSpeed * move->y, + mDataBlock->maxUnderwaterSideSpeed * mFabs(move->x)); + else if ( mPose == PronePose ) + moveSpeed = getMax(mDataBlock->maxProneForwardSpeed * move->y, + mDataBlock->maxProneSideSpeed * mFabs(move->x)); + else if ( mPose == CrouchPose ) + moveSpeed = getMax(mDataBlock->maxCrouchForwardSpeed * move->y, + mDataBlock->maxCrouchSideSpeed * mFabs(move->x)); + else if ( mPose == SprintPose ) + moveSpeed = getMax(mDataBlock->maxSprintForwardSpeed * move->y, + mDataBlock->maxSprintSideSpeed * mFabs(move->x)); + + else // StandPose + moveSpeed = getMax(mDataBlock->maxForwardSpeed * move->y, + mDataBlock->maxSideSpeed * mFabs(move->x)); + } + else + { + if ( mSwimming ) + moveSpeed = getMax(mDataBlock->maxUnderwaterBackwardSpeed * mFabs(move->y), + mDataBlock->maxUnderwaterSideSpeed * mFabs(move->x)); + else if ( mPose == PronePose ) + moveSpeed = getMax(mDataBlock->maxProneBackwardSpeed * mFabs(move->y), + mDataBlock->maxProneSideSpeed * mFabs(move->x)); + else if ( mPose == CrouchPose ) + moveSpeed = getMax(mDataBlock->maxCrouchBackwardSpeed * mFabs(move->y), + mDataBlock->maxCrouchSideSpeed * mFabs(move->x)); + else if ( mPose == SprintPose ) + moveSpeed = getMax(mDataBlock->maxSprintBackwardSpeed * mFabs(move->y), + mDataBlock->maxSprintSideSpeed * mFabs(move->x)); + else // StandPose + moveSpeed = getMax(mDataBlock->maxBackwardSpeed * mFabs(move->y), + mDataBlock->maxSideSpeed * mFabs(move->x)); + } + + // Cancel any script driven animations if we are going to move. + if (moveVec.x + moveVec.y + moveVec.z != 0.0f && + (mActionAnimation.action >= PlayerData::NumTableActionAnims + || mActionAnimation.action == PlayerData::LandAnim)) + mActionAnimation.action = PlayerData::NullAnimation; + } + else + { + moveVec.set(0.0f, 0.0f, 0.0f); + moveSpeed = 0.0f; + } + + // Acceleration due to gravity + VectorF acc(0.0f, 0.0f, mGravity * mGravityMod * TickSec); + + // Determine ground contact normal. Only look for contacts if + // we can move and aren't mounted. + VectorF contactNormal(0,0,0); + bool jumpSurface = false, runSurface = false; + if ( !isMounted() ) + findContact( &runSurface, &jumpSurface, &contactNormal ); + if ( jumpSurface ) + mJumpSurfaceNormal = contactNormal; + + // If we don't have a runSurface but we do have a contactNormal, + // then we are standing on something that is too steep. + // Deflect the force of gravity by the normal so we slide. + // We could also try aligning it to the runSurface instead, + // but this seems to work well. + if ( !runSurface && !contactNormal.isZero() ) + acc = ( acc - 2 * contactNormal * mDot( acc, contactNormal ) ); + + // Acceleration on run surface + if (runSurface && !mSwimming) { + mContactTimer = 0; + + // Remove acc into contact surface (should only be gravity) + // Clear out floating point acc errors, this will allow + // the player to "rest" on the ground. + // However, no need to do that if we're using a physics library. + // It will take care of itself. + if (!mPhysicsRep) + { + F32 vd = -mDot(acc,contactNormal); + if (vd > 0.0f) { + VectorF dv = contactNormal * (vd + 0.002f); + acc += dv; + if (acc.len() < 0.0001f) + acc.set(0.0f, 0.0f, 0.0f); + } + } + + // Force a 0 move if there is no energy, and only drain + // move energy if we're moving. + VectorF pv; + if (mPose == SprintPose && mEnergy >= mDataBlock->minSprintEnergy) { + if (moveSpeed) + mEnergy -= mDataBlock->sprintEnergyDrain; + pv = moveVec; + } + else if (mEnergy >= mDataBlock->minRunEnergy) { + if (moveSpeed) + mEnergy -= mDataBlock->runEnergyDrain; + pv = moveVec; + } + else + pv.set(0.0f, 0.0f, 0.0f); + + // Adjust the player's requested dir. to be parallel + // to the contact surface. + F32 pvl = pv.len(); + if(mJetting) + { + pvl = moveVec.len(); + if (pvl) + { + VectorF nn; + mCross(pv,VectorF(0.0f, 0.0f, 0.0f),&nn); + nn *= 1 / pvl; + VectorF cv(0.0f, 0.0f, 0.0f); + cv -= nn * mDot(nn,cv); + pv -= cv * mDot(pv,cv); + pvl = pv.len(); + } + } + else if (!mPhysicsRep) + { + // We only do this if we're not using a physics library. The + // library will take care of itself. + if (pvl) + { + VectorF nn; + mCross(pv,VectorF(0.0f, 0.0f, 1.0f),&nn); + nn *= 1.0f / pvl; + VectorF cv = contactNormal; + cv -= nn * mDot(nn,cv); + pv -= cv * mDot(pv,cv); + pvl = pv.len(); + } + } + + // Convert to acceleration + if ( pvl ) + pv *= moveSpeed / pvl; + VectorF runAcc = pv - (mVelocity + acc); + F32 runSpeed = runAcc.len(); + + // Clamp acceleration, player also accelerates faster when + // in his hard landing recover state. + F32 maxAcc; + if (mPose == SprintPose) + { + maxAcc = (mDataBlock->sprintForce / getMass()) * TickSec; + } + else + { + maxAcc = (mDataBlock->runForce / getMass()) * TickSec; + } + if (mState == RecoverState) + maxAcc *= mDataBlock->recoverRunForceScale; + if (runSpeed > maxAcc) + runAcc *= maxAcc / runSpeed; + acc += runAcc; + + // If we are running on the ground, then we're not jumping + if (mDataBlock->isJumpAction(mActionAnimation.action)) + mActionAnimation.action = PlayerData::NullAnimation; + } + else if (!mSwimming && mDataBlock->airControl > 0.0f) + { + VectorF pv; + pv = moveVec; + F32 pvl = pv.len(); + + if (pvl) + pv *= moveSpeed / pvl; + + VectorF runAcc = pv - acc; + runAcc.z = 0; + runAcc.x = runAcc.x * mDataBlock->airControl; + runAcc.y = runAcc.y * mDataBlock->airControl; + F32 runSpeed = runAcc.len(); + // We don't test for sprinting when performing air control + F32 maxAcc = (mDataBlock->runForce / getMass()) * TickSec * 0.3f; + + if (runSpeed > maxAcc) + runAcc *= maxAcc / runSpeed; + + acc += runAcc; + + // There are no special air control animations + // so... increment this unless you really want to + // play the run anims in the air. + mContactTimer++; + } + else if (mSwimming) + { + // Remove acc into contact surface (should only be gravity) + // Clear out floating point acc errors, this will allow + // the player to "rest" on the ground. + F32 vd = -mDot(acc,contactNormal); + if (vd > 0.0f) { + VectorF dv = contactNormal * (vd + 0.002f); + acc += dv; + if (acc.len() < 0.0001f) + acc.set(0.0f, 0.0f, 0.0f); + } + + // get the head pitch and add it to the moveVec + // This more accurate swim vector calc comes from Matt Fairfax + MatrixF xRot, zRot; + xRot.set(EulerF(mHead.x, 0, 0)); + zRot.set(EulerF(0, 0, mRot.z)); + MatrixF rot; + rot.mul(zRot, xRot); + rot.getColumn(0,&moveVec); + + moveVec *= move->x; + VectorF tv; + rot.getColumn(1,&tv); + moveVec += tv * move->y; + rot.getColumn(2,&tv); + moveVec += tv * move->z; + + // Force a 0 move if there is no energy, and only drain + // move energy if we're moving. + VectorF swimVec; + if (mEnergy >= mDataBlock->minRunEnergy) { + if (moveSpeed) + mEnergy -= mDataBlock->runEnergyDrain; + swimVec = moveVec; + } + else + swimVec.set(0.0f, 0.0f, 0.0f); + + // If we are swimming but close enough to the shore/ground + // we can still have a surface-normal. In this case align the + // velocity to the normal to make getting out of water easier. + + moveVec.normalize(); + F32 isSwimUp = mDot( moveVec, contactNormal ); + + if ( !contactNormal.isZero() && isSwimUp < 0.1f ) + { + F32 pvl = swimVec.len(); + + if ( true && pvl ) + { + VectorF nn; + mCross(swimVec,VectorF(0.0f, 0.0f, 1.0f),&nn); + nn *= 1.0f / pvl; + VectorF cv = contactNormal; + cv -= nn * mDot(nn,cv); + swimVec -= cv * mDot(swimVec,cv); + } + } + + F32 swimVecLen = swimVec.len(); + + // Convert to acceleration. + if ( swimVecLen ) + swimVec *= moveSpeed / swimVecLen; + VectorF swimAcc = swimVec - (mVelocity + acc); + F32 swimSpeed = swimAcc.len(); + + // Clamp acceleration. + F32 maxAcc = (mDataBlock->swimForce / getMass()) * TickSec; + if ( false && swimSpeed > maxAcc ) + swimAcc *= maxAcc / swimSpeed; + + acc += swimAcc; + + mContactTimer++; + } + else + mContactTimer++; + + // Acceleration from Jumping + if (move->trigger[sJumpTrigger] && canJump())// !isMounted() && + { + // Scale the jump impulse base on maxJumpSpeed + F32 zSpeedScale = mVelocity.z; + if (zSpeedScale <= mDataBlock->maxJumpSpeed) + { + zSpeedScale = (zSpeedScale <= mDataBlock->minJumpSpeed)? 1: + 1 - (zSpeedScale - mDataBlock->minJumpSpeed) / + (mDataBlock->maxJumpSpeed - mDataBlock->minJumpSpeed); + + // Desired jump direction + VectorF pv = moveVec; + F32 len = pv.len(); + if (len > 0) + pv *= 1 / len; + + // We want to scale the jump size by the player size, somewhat + // in reduced ratio so a smaller player can jump higher in + // proportion to his size, than a larger player. + F32 scaleZ = (getScale().z * 0.25) + 0.75; + + // Calculate our jump impulse + F32 impulse = mDataBlock->jumpForce / getMass(); + + if (mDataBlock->jumpTowardsNormal) + { + // If we are facing into the surface jump up, otherwise + // jump away from surface. + F32 dot = mDot(pv,mJumpSurfaceNormal); + if (dot <= 0) + acc.z += mJumpSurfaceNormal.z * scaleZ * impulse * zSpeedScale; + else + { + acc.x += pv.x * impulse * dot; + acc.y += pv.y * impulse * dot; + acc.z += mJumpSurfaceNormal.z * scaleZ * impulse * zSpeedScale; + } + } + else + acc.z += scaleZ * impulse * zSpeedScale; + + mJumpDelay = mDataBlock->jumpDelay; + mEnergy -= mDataBlock->jumpEnergyDrain; + + // If we don't have a StandJumpAnim, just play the JumpAnim... + S32 seq = (mVelocity.len() < 0.5) ? PlayerData::StandJumpAnim: PlayerData::JumpAnim; + if ( mDataBlock->actionList[seq].sequence == -1 ) + seq = PlayerData::JumpAnim; + setActionThread( seq, true, false, true ); + + mJumpSurfaceLastContact = JumpSkipContactsMax; + } + } + else + { + if (jumpSurface) + { + if (mJumpDelay > 0) + mJumpDelay--; + mJumpSurfaceLastContact = 0; + } + else + mJumpSurfaceLastContact++; + } + + if (move->trigger[sJumpJetTrigger] && !isMounted() && canJetJump()) + { + mJetting = true; + + // Scale the jump impulse base on maxJumpSpeed + F32 zSpeedScale = mVelocity.z; + + if (zSpeedScale <= mDataBlock->jetMaxJumpSpeed) + { + zSpeedScale = (zSpeedScale <= mDataBlock->jetMinJumpSpeed)? 1: + 1 - (zSpeedScale - mDataBlock->jetMinJumpSpeed) / (mDataBlock->jetMaxJumpSpeed - mDataBlock->jetMinJumpSpeed); + + // Desired jump direction + VectorF pv = moveVec; + F32 len = pv.len(); + + if (len > 0.0f) + pv *= 1 / len; + + // If we are facing into the surface jump up, otherwise + // jump away from surface. + F32 dot = mDot(pv,mJumpSurfaceNormal); + F32 impulse = mDataBlock->jetJumpForce / getMass(); + + if (dot <= 0) + acc.z += mJumpSurfaceNormal.z * impulse * zSpeedScale; + else + { + acc.x += pv.x * impulse * dot; + acc.y += pv.y * impulse * dot; + acc.z += mJumpSurfaceNormal.z * impulse * zSpeedScale; + } + + mEnergy -= mDataBlock->jetJumpEnergyDrain; + } + } + else + { + mJetting = false; + } + + // Add in force from physical zones... + acc += (mAppliedForce / getMass()) * TickSec; + + // Adjust velocity with all the move & gravity acceleration + // TG: I forgot why doesn't the TickSec multiply happen here... + mVelocity += acc; + + // apply horizontal air resistance + + F32 hvel = mSqrt(mVelocity.x * mVelocity.x + mVelocity.y * mVelocity.y); + + if(hvel > mDataBlock->horizResistSpeed) + { + F32 speedCap = hvel; + if(speedCap > mDataBlock->horizMaxSpeed) + speedCap = mDataBlock->horizMaxSpeed; + speedCap -= mDataBlock->horizResistFactor * TickSec * (speedCap - mDataBlock->horizResistSpeed); + F32 scale = speedCap / hvel; + mVelocity.x *= scale; + mVelocity.y *= scale; + } + if(mVelocity.z > mDataBlock->upResistSpeed) + { + if(mVelocity.z > mDataBlock->upMaxSpeed) + mVelocity.z = mDataBlock->upMaxSpeed; + mVelocity.z -= mDataBlock->upResistFactor * TickSec * (mVelocity.z - mDataBlock->upResistSpeed); + } + + // Container buoyancy & drag + if (mBuoyancy != 0) + { + // Applying buoyancy when standing still causing some jitters- + if (mBuoyancy > 1.0 || !mVelocity.isZero() || !runSurface) + { + // A little hackery to prevent oscillation + // based on http://reinot.blogspot.com/2005/11/oh-yes-they-float-georgie-they-all.html + + F32 buoyancyForce = mBuoyancy * mGravity * mGravityMod * TickSec; + F32 currHeight = getPosition().z; + const F32 C = 2.0f; + const F32 M = 0.1f; + + if ( currHeight + mVelocity.z * TickSec * C > mLiquidHeight ) + buoyancyForce *= M; + + //mVelocity.z -= buoyancyForce; + } + } + + // Apply drag + if ( mSwimming ) + mVelocity -= mVelocity * mDrag * TickSec * ( mVelocity.len() / mDataBlock->maxUnderwaterForwardSpeed ); + else + mVelocity -= mVelocity * mDrag * TickSec; + + // Clamp very small velocity to zero + if ( mVelocity.isZero() ) + mVelocity = Point3F::Zero; + + // If we are not touching anything and have sufficient -z vel, + // we are falling. + if (runSurface) + mFalling = false; + else + { + VectorF vel; + mWorldToObj.mulV(mVelocity,&vel); + mFalling = vel.z < mDataBlock->fallingSpeedThreshold; + } + + // Vehicle Dismount + if ( !isGhost() && move->trigger[sVehicleDismountTrigger] && canJump()) + mDataBlock->doDismount_callback( this ); + + // Enter/Leave Liquid + if ( !mInWater && mWaterCoverage > 0.0f ) + { + mInWater = true; + + if ( !isGhost() ) + mDataBlock->onEnterLiquid_callback( this, mWaterCoverage, mLiquidType.c_str() ); + } + else if ( mInWater && mWaterCoverage <= 0.0f ) + { + mInWater = false; + + if ( !isGhost() ) + mDataBlock->onLeaveLiquid_callback( this, mLiquidType.c_str() ); + else + { + // exit-water splash sound happens for client only + if ( getSpeed() >= mDataBlock->exitSplashSoundVel && !isMounted() ) + SFX->playOnce( mDataBlock->sound[PlayerData::ExitWater], &getTransform() ); + } + } + + // Update the PlayerPose + Pose desiredPose = mPose; + + if ( mSwimming ) + desiredPose = SwimPose; + else if ( runSurface && move->trigger[sCrouchTrigger] && canCrouch() ) + desiredPose = CrouchPose; + else if ( runSurface && move->trigger[sProneTrigger] && canProne() ) + desiredPose = PronePose; + else if ( move->trigger[sSprintTrigger] && canSprint() ) + desiredPose = SprintPose; + else if ( canStand() ) + desiredPose = StandPose; + + setPose( desiredPose ); +} + + +//---------------------------------------------------------------------------- + +bool Player::checkDismountPosition(const MatrixF& oldMat, const MatrixF& mat) +{ + AssertFatal(getContainer() != NULL, "Error, must have a container!"); + AssertFatal(getObjectMount() != NULL, "Error, must be mounted!"); + + Point3F pos; + Point3F oldPos; + mat.getColumn(3, &pos); + oldMat.getColumn(3, &oldPos); + RayInfo info; + disableCollision(); + getObjectMount()->disableCollision(); + if (getContainer()->castRay(oldPos, pos, sCollisionMoveMask, &info)) + { + enableCollision(); + getObjectMount()->enableCollision(); + return false; + } + + Box3F wBox = mObjBox; + wBox.minExtents += pos; + wBox.maxExtents += pos; + + EarlyOutPolyList polyList; + polyList.mNormal.set(0.0f, 0.0f, 0.0f); + polyList.mPlaneList.clear(); + polyList.mPlaneList.setSize(6); + polyList.mPlaneList[0].set(wBox.minExtents,VectorF(-1.0f, 0.0f, 0.0f)); + polyList.mPlaneList[1].set(wBox.maxExtents,VectorF(0.0f, 1.0f, 0.0f)); + polyList.mPlaneList[2].set(wBox.maxExtents,VectorF(1.0f, 0.0f, 0.0f)); + polyList.mPlaneList[3].set(wBox.minExtents,VectorF(0.0f, -1.0f, 0.0f)); + polyList.mPlaneList[4].set(wBox.minExtents,VectorF(0.0f, 0.0f, -1.0f)); + polyList.mPlaneList[5].set(wBox.maxExtents,VectorF(0.0f, 0.0f, 1.0f)); + + if (getContainer()->buildPolyList(PLC_Collision, wBox, sCollisionMoveMask, &polyList)) + { + enableCollision(); + getObjectMount()->enableCollision(); + return false; + } + + enableCollision(); + getObjectMount()->enableCollision(); + return true; +} + + +//---------------------------------------------------------------------------- + +bool Player::canJump() +{ + return mAllowJumping && mState == MoveState && mDamageState == Enabled && !isMounted() && !mJumpDelay && mEnergy >= mDataBlock->minJumpEnergy && mJumpSurfaceLastContact < JumpSkipContactsMax && !mSwimming && (mPose != SprintPose || mDataBlock->sprintCanJump); +} + +bool Player::canJetJump() +{ + return mAllowJetJumping && mState == MoveState && mDamageState == Enabled && !isMounted() && mEnergy >= mDataBlock->jetMinJumpEnergy && mDataBlock->jetJumpForce != 0.0f; +} + +bool Player::canSwim() +{ + // Not used! + //return mState == MoveState && mDamageState == Enabled && !isMounted() && mEnergy >= mDataBlock->minSwimEnergy && mWaterCoverage >= 0.8f; + return mAllowSwimming; +} + +bool Player::canCrouch() +{ + if (!mAllowCrouching) + return false; + + if ( mState != MoveState || + mDamageState != Enabled || + isMounted() || + mSwimming || + mFalling ) + return false; + + // Can't crouch if no crouch animation! + if ( mDataBlock->actionList[PlayerData::CrouchRootAnim].sequence == -1 ) + return false; + + // We are already in this pose, so don't test it again... + if ( mPose == CrouchPose ) + return true; + + // Do standard Torque physics test here! + if ( !mPhysicsRep ) + { + F32 radius; + + if ( mPose == PronePose ) + radius = mDataBlock->proneBoxSize.z; + else + return true; + + // use our X and Y dimentions on our boxsize as the radii for our search, and the difference between a standing position + // and the position we currently are in. + Point3F extent( mDataBlock->crouchBoxSize.x / 2, mDataBlock->crouchBoxSize.y / 2, mDataBlock->crouchBoxSize.z - radius ); + + Point3F position = getPosition(); + position.z += radius; + + // Use these radii to create a box that represents the difference between a standing position and the position + // we want to move into. + Box3F B(position - extent, position + extent, true); + + EarlyOutPolyList polyList; + polyList.mPlaneList.clear(); + polyList.mNormal.set( 0,0,0 ); + polyList.mPlaneList.setSize( 6 ); + polyList.mPlaneList[0].set( B.minExtents, VectorF( -1,0,0 ) ); + polyList.mPlaneList[1].set( B.maxExtents, VectorF( 0,1,0 ) ); + polyList.mPlaneList[2].set( B.maxExtents, VectorF( 1,0,0 ) ); + polyList.mPlaneList[3].set( B.minExtents, VectorF( 0,-1,0 ) ); + polyList.mPlaneList[4].set( B.minExtents, VectorF( 0,0,-1 ) ); + polyList.mPlaneList[5].set( B.maxExtents, VectorF( 0,0,1 ) ); + + // If an object exists in this space, we must stay prone. Otherwise we are free to crouch. + return !getContainer()->buildPolyList( PLC_Collision, B, StaticShapeObjectType, &polyList ); + } + + return mPhysicsRep->testSpacials( getPosition(), mDataBlock->crouchBoxSize ); +} + +bool Player::canStand() +{ + if ( mState != MoveState || + mDamageState != Enabled || + isMounted() || + mSwimming ) + return false; + + // We are already in this pose, so don't test it again... + if ( mPose == StandPose ) + return true; + + // Do standard Torque physics test here! + if ( !mPhysicsRep ) + { + F32 radius; + + if (mPose == CrouchPose) + radius = mDataBlock->crouchBoxSize.z; + else if (mPose == PronePose) + radius = mDataBlock->proneBoxSize.z; + else + return true; + + // use our X and Y dimentions on our boxsize as the radii for our search, and the difference between a standing position + // and the position we currently are in. + Point3F extent( mDataBlock->boxSize.x / 2, mDataBlock->boxSize.y / 2, mDataBlock->boxSize.z - radius ); + + Point3F position = getPosition(); + position.z += radius; + + // Use these radii to create a box that represents the difference between a standing position and the position + // we want to move into. + Box3F B(position - extent, position + extent, true); + + EarlyOutPolyList polyList; + polyList.mPlaneList.clear(); + polyList.mNormal.set(0,0,0); + polyList.mPlaneList.setSize(6); + polyList.mPlaneList[0].set(B.minExtents, VectorF(-1,0,0)); + polyList.mPlaneList[1].set(B.maxExtents, VectorF(0,1,0)); + polyList.mPlaneList[2].set(B.maxExtents, VectorF(1,0,0)); + polyList.mPlaneList[3].set(B.minExtents, VectorF(0,-1,0)); + polyList.mPlaneList[4].set(B.minExtents, VectorF(0,0,-1)); + polyList.mPlaneList[5].set(B.maxExtents, VectorF(0,0,1)); + + // If an object exists in this space, we must stay crouched/prone. Otherwise we are free to stand. + return !getContainer()->buildPolyList(PLC_Collision, B, StaticShapeObjectType, &polyList); + } + + return mPhysicsRep->testSpacials( getPosition(), mDataBlock->boxSize ); +} + +bool Player::canProne() +{ + if (!mAllowProne) + return false; + + if ( mState != MoveState || + mDamageState != Enabled || + isMounted() || + mSwimming || + mFalling ) + return false; + + // Can't go prone if no prone animation! + if ( mDataBlock->actionList[PlayerData::ProneRootAnim].sequence == -1 ) + return false; + + // Do standard Torque physics test here! + if ( !mPhysicsRep ) + return true; + + // We are already in this pose, so don't test it again... + if ( mPose == PronePose ) + return true; + + return mPhysicsRep->testSpacials( getPosition(), mDataBlock->proneBoxSize ); +} + +bool Player::canSprint() +{ + return mAllowSprinting && mState == MoveState && mDamageState == Enabled && !isMounted() && mEnergy >= mDataBlock->minSprintEnergy && !mSwimming; +} + +//---------------------------------------------------------------------------- + +void Player::updateDamageLevel() +{ + if (!isGhost()) + setDamageState((mDamage >= mDataBlock->maxDamage)? Disabled: Enabled); + if (mDamageThread) + mShapeInstance->setPos(mDamageThread, mDamage / mDataBlock->destroyedLevel); +} + +void Player::updateDamageState() +{ + // Become a corpse when we're disabled (dead). + if (mDamageState == Enabled) { + mTypeMask &= ~CorpseObjectType; + mTypeMask |= PlayerObjectType; + } + else { + mTypeMask &= ~PlayerObjectType; + mTypeMask |= CorpseObjectType; + } + + Parent::updateDamageState(); +} + + +//---------------------------------------------------------------------------- + +void Player::updateLookAnimation(F32 dT) +{ + // Calculate our interpolated head position. + Point3F renderHead = delta.head + delta.headVec * dT; + + // Adjust look pos. This assumes that the animations match + // the min and max look angles provided in the datablock. + if (mArmAnimation.thread) + { + // TG: Adjust arm position to avoid collision. + F32 tp = mControlObject? 0.5: + (renderHead.x - mArmRange.min) / mArmRange.delta; + mShapeInstance->setPos(mArmAnimation.thread,mClampF(tp,0,1)); + } + + if (mHeadVThread) + { + F32 tp = (renderHead.x - mHeadVRange.min) / mHeadVRange.delta; + mShapeInstance->setPos(mHeadVThread,mClampF(tp,0,1)); + } + + if (mHeadHThread) + { + F32 dt = 2 * mDataBlock->maxFreelookAngle; + F32 tp = (renderHead.z + mDataBlock->maxFreelookAngle) / dt; + mShapeInstance->setPos(mHeadHThread,mClampF(tp,0,1)); + } +} + + +//---------------------------------------------------------------------------- +// Methods to get delta (as amount to affect velocity by) + +bool Player::inDeathAnim() +{ + if (mActionAnimation.thread && mActionAnimation.action >= 0) + if (mActionAnimation.action < mDataBlock->actionCount) + return mDataBlock->actionList[mActionAnimation.action].death; + + return false; +} + +// Get change from mLastDeathPos - return current pos. Assumes we're in death anim. +F32 Player::deathDelta(Point3F & delta) +{ + // Get ground delta from the last time we offset this. + MatrixF mat; + F32 pos = mShapeInstance->getPos(mActionAnimation.thread); + mShapeInstance->deltaGround1(mActionAnimation.thread, mDeath.lastPos, pos, mat); + mat.getColumn(3, & delta); + return pos; +} + +// Called before updatePos() to prepare it's needed change to velocity, which +// must roll over. Should be updated on tick, this is where we remember last +// position of animation that was used to roll into velocity. +void Player::updateDeathOffsets() +{ + if (inDeathAnim()) + // Get ground delta from the last time we offset this. + mDeath.lastPos = deathDelta(mDeath.posAdd); + else + mDeath.clear(); +} + + +//---------------------------------------------------------------------------- + +static const U32 sPlayerConformMask = InteriorObjectType|StaticShapeObjectType| + StaticObjectType|TerrainObjectType; + +static void accel(F32& from, F32 to, F32 rate) +{ + if (from < to) + from = getMin(from += rate, to); + else + from = getMax(from -= rate, to); +} + +// if (dt == -1) +// normal tick, so we advance. +// else +// interpolate with dt as % of tick, don't advance +// +MatrixF * Player::Death::fallToGround(F32 dt, const Point3F& loc, F32 curZ, F32 boxRad) +{ + static const F32 sConformCheckDown = 4.0f; + RayInfo coll; + bool conformToStairs = false; + Point3F pos(loc.x, loc.y, loc.z + 0.1f); + Point3F below(pos.x, pos.y, loc.z - sConformCheckDown); + MatrixF * retVal = NULL; + + PROFILE_SCOPE(ConformToGround); + + if (gClientContainer.castRay(pos, below, sPlayerConformMask, &coll)) + { + F32 adjust, height = (loc.z - coll.point.z), sink = curSink; + VectorF desNormal = coll.normal; + VectorF normal = curNormal; + + // dt >= 0 means we're interpolating and don't accel the numbers + if (dt >= 0.0f) + adjust = dt * TickSec; + else + adjust = TickSec; + + // Try to get them to conform to stairs by doing several LOS calls. We do this if + // normal is within about 5 deg. of vertical. + if (desNormal.z > 0.995f) + { + Point3F corners[3], downpts[3]; + S32 c; + + for (c = 0; c < 3; c++) { // Build 3 corners to cast down from- + corners[c].set(loc.x - boxRad, loc.y - boxRad, loc.z + 1.0f); + if (c) // add (0,boxWidth) and (boxWidth,0) + corners[c][c - 1] += (boxRad * 2.0f); + downpts[c].set(corners[c].x, corners[c].y, loc.z - sConformCheckDown); + } + + // Do the three casts- + for (c = 0; c < 3; c++) + if (gClientContainer.castRay(corners[c], downpts[c], sPlayerConformMask, &coll)) + downpts[c] = coll.point; + else + break; + + // Do the math if everything hit below- + if (c == 3) { + mCross(downpts[1] - downpts[0], downpts[2] - downpts[1], &desNormal); + AssertFatal(desNormal.z > 0, "Abnormality in Player::Death::fallToGround()"); + downpts[2] = downpts[2] - downpts[1]; + downpts[1] = downpts[1] - downpts[0]; + desNormal.normalize(); + conformToStairs = true; + } + } + + // Move normal in direction we want- + F32 * cur = normal, * des = desNormal; + for (S32 i = 0; i < 3; i++) + accel(*cur++, *des++, adjust * 0.25f); + + if (mFabs(height) < 2.2f && !normal.isZero() && desNormal.z > 0.01f) + { + VectorF upY(0.0f, 1.0f, 0.0f), ahead; + VectorF sideVec; + MatrixF mat(true); + + normal.normalize(); + mat.set(EulerF (0.0f, 0.0f, curZ)); + mat.mulV(upY, & ahead); + mCross(ahead, normal, &sideVec); + sideVec.normalize(); + mCross(normal, sideVec, &ahead); + + static MatrixF resMat(true); + resMat.setColumn(0, sideVec); + resMat.setColumn(1, ahead); + resMat.setColumn(2, normal); + + // Adjust Z down to account for box offset on slope. Figure out how + // much we want to sink, and gradually accel to this amount. Don't do if + // we're conforming to stairs though + F32 xy = mSqrt(desNormal.x * desNormal.x + desNormal.y * desNormal.y); + F32 desiredSink = (boxRad * xy / desNormal.z); + if (conformToStairs) + desiredSink *= 0.5f; + + accel(sink, desiredSink, adjust * 0.15f); + + Point3F position(pos); + position.z -= sink; + resMat.setColumn(3, position); + + if (dt < 0.0f) + { // we're moving, so update normal and sink amount + curNormal = normal; + curSink = sink; + } + + retVal = &resMat; + } + } + return retVal; +} + + +//------------------------------------------------------------------------------------- + +// This is called ::onAdd() to see if we're in a sitting animation. These then +// can use a longer tick delay for the mount to get across. +bool Player::inSittingAnim() +{ + U32 action = mActionAnimation.action; + if (mActionAnimation.thread && action < mDataBlock->actionCount) { + const char * name = mDataBlock->actionList[action].name; + if (!dStricmp(name, "Sitting") || !dStricmp(name, "Scoutroot")) + return true; + } + return false; +} + + +//---------------------------------------------------------------------------- + +const String& Player::getArmThread() const +{ + if (mArmAnimation.thread && mArmAnimation.thread->hasSequence()) + { + return mArmAnimation.thread->getSequenceName(); + } + + return String::EmptyString; +} + +bool Player::setArmThread(const char* sequence) +{ + // The arm sequence must be in the action list. + for (U32 i = 1; i < mDataBlock->actionCount; i++) + if (!dStricmp(mDataBlock->actionList[i].name,sequence)) + return setArmThread(i); + return false; +} + +bool Player::setArmThread(U32 action) +{ + PlayerData::ActionAnimation &anim = mDataBlock->actionList[action]; + if (anim.sequence != -1 && + anim.sequence != mShapeInstance->getSequence(mArmAnimation.thread)) + { + mShapeInstance->setSequence(mArmAnimation.thread,anim.sequence,0); + mArmAnimation.action = action; + setMaskBits(ActionMask); + return true; + } + return false; +} + + +//---------------------------------------------------------------------------- + +bool Player::setActionThread(const char* sequence,bool hold,bool wait,bool fsp) +{ + for (U32 i = 1; i < mDataBlock->actionCount; i++) + { + PlayerData::ActionAnimation &anim = mDataBlock->actionList[i]; + if (!dStricmp(anim.name,sequence)) + { + setActionThread(i,true,hold,wait,fsp); + setMaskBits(ActionMask); + return true; + } + } + return false; +} + +void Player::setActionThread(U32 action,bool forward,bool hold,bool wait,bool fsp, bool forceSet) +{ + if (!mDataBlock || (mActionAnimation.action == action && mActionAnimation.forward == forward && !forceSet)) + return; + + if (action >= PlayerData::NumActionAnims) + { + Con::errorf("Player::setActionThread(%d): Player action out of range", action); + return; + } + + PlayerData::ActionAnimation &anim = mDataBlock->actionList[action]; + if (anim.sequence != -1) + { + U32 lastAction = mActionAnimation.action; + + mActionAnimation.action = action; + mActionAnimation.forward = forward; + mActionAnimation.firstPerson = fsp; + mActionAnimation.holdAtEnd = hold; + mActionAnimation.waitForEnd = hold? true: wait; + mActionAnimation.animateOnServer = fsp; + mActionAnimation.atEnd = false; + mActionAnimation.delayTicks = (S32)sNewAnimationTickTime; + mActionAnimation.atEnd = false; + + if (sUseAnimationTransitions && (action != PlayerData::LandAnim || !(mDataBlock->landSequenceTime > 0.0f && !mDataBlock->transitionToLand)) && (isGhost()/* || mActionAnimation.animateOnServer*/)) + { + // The transition code needs the timeScale to be set in the + // right direction to know which way to go. + F32 transTime = sAnimationTransitionTime; + if (mDataBlock && mDataBlock->isJumpAction(action)) + transTime = 0.15f; + + F32 timeScale = mActionAnimation.forward ? 1.0f : -1.0f; + if (mDataBlock && mDataBlock->isJumpAction(action)) + timeScale *= 1.5f; + + mShapeInstance->setTimeScale(mActionAnimation.thread,timeScale); + + S32 seq = anim.sequence; + S32 imageBasedSeq = convertActionToImagePrefix(mActionAnimation.action); + if (imageBasedSeq != -1) + seq = imageBasedSeq; + + // If we're transitioning into the same sequence (an action may use the + // same sequence as a previous action) then we want to start at the same + // position. + F32 pos = mActionAnimation.forward ? 0.0f : 1.0f; + PlayerData::ActionAnimation &lastAnim = mDataBlock->actionList[lastAction]; + if (lastAnim.sequence == anim.sequence) + { + pos = mShapeInstance->getPos(mActionAnimation.thread); + } + + mShapeInstance->transitionToSequence(mActionAnimation.thread,seq, + pos, transTime, true); + } + else + { + S32 seq = anim.sequence; + S32 imageBasedSeq = convertActionToImagePrefix(mActionAnimation.action); + if (imageBasedSeq != -1) + seq = imageBasedSeq; + + mShapeInstance->setSequence(mActionAnimation.thread,seq, + mActionAnimation.forward ? 0.0f : 1.0f); + } + } +} + +void Player::updateActionThread() +{ + PROFILE_START(UpdateActionThread); + + // Select an action animation sequence, this assumes that + // this function is called once per tick. + if(mActionAnimation.action != PlayerData::NullAnimation) + if (mActionAnimation.forward) + mActionAnimation.atEnd = mShapeInstance->getPos(mActionAnimation.thread) == 1; + else + mActionAnimation.atEnd = mShapeInstance->getPos(mActionAnimation.thread) == 0; + + // Only need to deal with triggers on the client + if( isGhost() ) + { + bool triggeredLeft = false; + bool triggeredRight = false; + + F32 offset = 0.0f; + if( mShapeInstance->getTriggerState( 1 ) ) + { + triggeredLeft = true; + offset = -mDataBlock->decalOffset * getScale().x; + } + else if(mShapeInstance->getTriggerState( 2 ) ) + { + triggeredRight = true; + offset = mDataBlock->decalOffset * getScale().x; + } + + if( triggeredLeft || triggeredRight ) + { + Point3F rot, pos; + RayInfo rInfo; + MatrixF mat = getRenderTransform(); + mat.getColumn( 1, &rot ); + mat.mulP( Point3F( offset, 0.0f, 0.0f), &pos ); + + if( gClientContainer.castRay( Point3F( pos.x, pos.y, pos.z + 0.01f ), + Point3F( pos.x, pos.y, pos.z - 2.0f ), + STATIC_COLLISION_TYPEMASK | VehicleObjectType, &rInfo ) ) + { + Material* material = ( rInfo.material ? dynamic_cast< Material* >( rInfo.material->getMaterial() ) : 0 ); + + // Put footprints on surface, if appropriate for material. + + if( material && material->mShowFootprints + && mDataBlock->decalData ) + { + Point3F normal; + Point3F tangent; + mObjToWorld.getColumn( 0, &tangent ); + mObjToWorld.getColumn( 2, &normal ); + gDecalManager->addDecal( rInfo.point, normal, tangent, mDataBlock->decalData, getScale().y ); + } + + // Emit footpuffs. + + if( rInfo.t <= 0.5 && mWaterCoverage == 0.0 + && material && material->mShowDust ) + { + // New emitter every time for visibility reasons + ParticleEmitter * emitter = new ParticleEmitter; + emitter->onNewDataBlock( mDataBlock->footPuffEmitter, false ); + + ColorF colorList[ ParticleData::PDC_NUM_KEYS]; + + for( U32 x = 0; x < getMin( Material::NUM_EFFECT_COLOR_STAGES, ParticleData::PDC_NUM_KEYS ); ++ x ) + colorList[ x ].set( material->mEffectColor[ x ].red, + material->mEffectColor[ x ].green, + material->mEffectColor[ x ].blue, + material->mEffectColor[ x ].alpha ); + for( U32 x = Material::NUM_EFFECT_COLOR_STAGES; x < ParticleData::PDC_NUM_KEYS; ++ x ) + colorList[ x ].set( 1.0, 1.0, 1.0, 0.0 ); + + emitter->setColors( colorList ); + if( !emitter->registerObject() ) + { + Con::warnf( ConsoleLogEntry::General, "Could not register emitter for particle of class: %s", mDataBlock->getName() ); + delete emitter; + emitter = NULL; + } + else + { + emitter->emitParticles( pos, Point3F( 0.0, 0.0, 1.0 ), mDataBlock->footPuffRadius, + Point3F( 0, 0, 0 ), mDataBlock->footPuffNumParts ); + emitter->deleteWhenEmpty(); + } + } + + // Play footstep sound. + + playFootstepSound( triggeredLeft, material, rInfo.object ); + } + } + } + + // Mount pending variable puts a hold on the delayTicks below so players don't + // inadvertently stand up because their mount has not come over yet. + if (mMountPending) + mMountPending = (isMounted() ? 0 : (mMountPending - 1)); + + if (mActionAnimation.action == PlayerData::NullAnimation || + ((!mActionAnimation.waitForEnd || mActionAnimation.atEnd)) && + !mActionAnimation.holdAtEnd && (mActionAnimation.delayTicks -= !mMountPending) <= 0) + { + //The scripting language will get a call back when a script animation has finished... + // example: When the chat menu animations are done playing... + if ( isServerObject() && mActionAnimation.action >= PlayerData::NumTableActionAnims ) + mDataBlock->animationDone_callback( this ); + pickActionAnimation(); + } + + if ( (mActionAnimation.action != PlayerData::LandAnim) && + (mActionAnimation.action != PlayerData::NullAnimation) ) + { + // Update action animation time scale to match ground velocity + PlayerData::ActionAnimation &anim = + mDataBlock->actionList[mActionAnimation.action]; + F32 scale = 1; + if (anim.velocityScale && anim.speed) { + VectorF vel; + mWorldToObj.mulV(mVelocity,&vel); + scale = mFabs(mDot(vel, anim.dir) / anim.speed); + + if (scale > mDataBlock->maxTimeScale) + scale = mDataBlock->maxTimeScale; + } + + mShapeInstance->setTimeScale(mActionAnimation.thread, + mActionAnimation.forward? scale: -scale); + } + PROFILE_END(); +} + +void Player::pickBestMoveAction(U32 startAnim, U32 endAnim, U32 * action, bool * forward) const +{ + *action = startAnim; + *forward = false; + + VectorF vel; + mWorldToObj.mulV(mVelocity,&vel); + + if (vel.lenSquared() > 0.01f) + { + // Bias the velocity towards picking the forward/backward anims over + // the sideways ones to prevent oscillation between anims. + vel *= VectorF(0.5f, 1.0f, 0.5f); + + // Pick animation that is the best fit for our current (local) velocity. + // Assumes that the root (stationary) animation is at startAnim. + F32 curMax = -0.1f; + for (U32 i = startAnim+1; i <= endAnim; i++) + { + const PlayerData::ActionAnimation &anim = mDataBlock->actionList[i]; + if (anim.sequence != -1 && anim.speed) + { + F32 d = mDot(vel, anim.dir); + if (d > curMax) + { + curMax = d; + *action = i; + *forward = true; + } + else + { + // Check if reversing this animation would fit (bias against this + // so that when moving right, the real right anim is still chosen, + // but if not present, the reversed left anim will be used instead) + d *= -0.75f; + if (d > curMax) + { + curMax = d; + *action = i; + *forward = false; + } + } + } + } + } +} + +void Player::pickActionAnimation() +{ + // Only select animations in our normal move state. + if (mState != MoveState || mDamageState != Enabled) + return; + + if (isMounted() || mMountPending) + { + // Go into root position unless something was set explicitly + // from a script. + if (mActionAnimation.action != PlayerData::RootAnim && + mActionAnimation.action < PlayerData::NumTableActionAnims) + setActionThread(PlayerData::RootAnim,true,false,false); + return; + } + + bool forward = true; + U32 action = PlayerData::RootAnim; + bool fsp = false; + + // Jetting overrides the fall animation condition + if (mJetting) + { + // Play the jetting animation + action = PlayerData::JetAnim; + } + else if (mFalling) + { + // Not in contact with any surface and falling + action = PlayerData::FallAnim; + } + else if ( mSwimming ) + { + pickBestMoveAction(PlayerData::SwimRootAnim, PlayerData::SwimRightAnim, &action, &forward); + } + else if ( mPose == StandPose ) + { + if (mContactTimer >= sContactTickTime) + { + // Nothing under our feet + action = PlayerData::RootAnim; + } + else + { + // Our feet are on something + pickBestMoveAction(PlayerData::RootAnim, PlayerData::SideRightAnim, &action, &forward); + } + } + else if ( mPose == CrouchPose ) + { + pickBestMoveAction(PlayerData::CrouchRootAnim, PlayerData::CrouchRightAnim, &action, &forward); + } + else if ( mPose == PronePose ) + { + pickBestMoveAction(PlayerData::ProneRootAnim, PlayerData::ProneBackwardAnim, &action, &forward); + } + else if ( mPose == SprintPose ) + { + pickBestMoveAction(PlayerData::SprintRootAnim, PlayerData::SprintRightAnim, &action, &forward); + } + setActionThread(action,forward,false,false,fsp); +} + +void Player::onImage(U32 imageSlot, bool unmount) +{ + // Update 3rd person sequences based on images used. Start be getting a + // list of all possible image prefix sequences. + String prefixPaths[ShapeBase::MaxMountedImages]; + buildImagePrefixPaths(prefixPaths); + + // Clear out any previous image state animation + if (mImageStateThread) + { + mShapeInstance->destroyThread(mImageStateThread); + mImageStateThread = 0; + } + + // Attempt to update the action thread + U32 action = mActionAnimation.action; + if (action != PlayerData::NullAnimation) + { + String actionSeq = mDataBlock->actionList[action].name; + if (actionSeq.isNotEmpty()) + { + S32 seqIndex = mDataBlock->actionList[action].sequence; + S32 prefixIndex = findPrefixSequence(prefixPaths, actionSeq); + if (prefixIndex != -1) + { + seqIndex = prefixIndex; + } + + // Only change the sequence if it isn't already playing. + if (seqIndex != mShapeInstance->getSequence(mActionAnimation.thread)) + { + F32 pos = mShapeInstance->getPos(mActionAnimation.thread); + mShapeInstance->setSequence(mActionAnimation.thread, seqIndex, pos); + } + } + } + + // Attempt to update the arm thread + U32 armAction = getArmAction(); + if (armAction != PlayerData::NullAnimation) + { + String armSeq = mDataBlock->actionList[armAction].name; + if (armSeq.isNotEmpty()) + { + S32 seqIndex = mDataBlock->actionList[armAction].sequence; + S32 prefixIndex = findPrefixSequence(prefixPaths, armSeq); + if (prefixIndex != -1) + { + seqIndex = prefixIndex; + } + + // Only change the sequence if it isn't already playing. + if (seqIndex != mShapeInstance->getSequence(mArmAnimation.thread)) + { + F32 pos = mShapeInstance->getPos(mArmAnimation.thread); + mShapeInstance->setSequence(mArmAnimation.thread, seqIndex, pos); + } + } + } + + // Attempt to update the head threads + if (mHeadVThread) + { + TSShape const* shape = mShapeInstance->getShape(); + S32 seqIndex = shape->findSequence("head"); + S32 prefixIndex = findPrefixSequence(prefixPaths, "head"); + if (prefixIndex != -1) + { + seqIndex = prefixIndex; + } + + // Only change the sequence if it isn't already playing. + if (seqIndex != mShapeInstance->getSequence(mHeadVThread)) + { + F32 pos = mShapeInstance->getPos(mHeadVThread); + mShapeInstance->setSequence(mHeadVThread, seqIndex, pos); + } + } + + if (mHeadHThread) + { + TSShape const* shape = mShapeInstance->getShape(); + S32 seqIndex = shape->findSequence("headside"); + S32 prefixIndex = findPrefixSequence(prefixPaths, "headside"); + if (prefixIndex != -1) + { + seqIndex = prefixIndex; + } + + // Only change the sequence if it isn't already playing. + if (seqIndex != mShapeInstance->getSequence(mHeadHThread)) + { + F32 pos = mShapeInstance->getPos(mHeadHThread); + mShapeInstance->setSequence(mHeadHThread, seqIndex, pos); + } + } +} + +void Player::buildImagePrefixPaths(String* prefixPaths) +{ + // We begin obtaining the anim prefix for each image. + String prefix[ShapeBase::MaxMountedImages]; + for (U32 i=0; iimageAnimPrefix && image.dataBlock->imageAnimPrefix[0]) + { + prefix[i] = String(image.dataBlock->imageAnimPrefix); + } + } + + // Build out the full prefix names we will be searching for. + S32 counter = ShapeBase::MaxMountedImages-1; + for (U32 i=0; igetShape()->findSequence(seq); + if (seqIndex != -1) + { + return seqIndex; + } + } + } + + return -1; +} + +S32 Player::convertActionToImagePrefix(U32 action) +{ + String prefixPaths[ShapeBase::MaxMountedImages]; + buildImagePrefixPaths(prefixPaths); + + if (action != PlayerData::NullAnimation) + { + String actionSeq; + S32 seq = -1; + + // We'll first attempt to find the action sequence by name + // as defined within the action list. + actionSeq = mDataBlock->actionList[action].name; + if (actionSeq.isNotEmpty()) + { + seq = findPrefixSequence(prefixPaths, actionSeq); + } + + if (seq == -1) + { + // Couldn't find a valid sequence. If this is a sprint action + // then we also need to search through the standard movement + // sequences. + if (action >= PlayerData::SprintRootAnim && action <= PlayerData::SprintRightAnim) + { + U32 standardAction = action - PlayerData::SprintRootAnim; + actionSeq = mDataBlock->actionList[standardAction].name; + if (actionSeq.isNotEmpty()) + { + seq = findPrefixSequence(prefixPaths, actionSeq); + } + } + } + + return seq; + } + + return -1; +} + +void Player::onImageRecoil( U32, ShapeBaseImageData::StateData::RecoilState state ) +{ + if ( mRecoilThread ) + { + if ( state != ShapeBaseImageData::StateData::NoRecoil ) + { + S32 stateIndex = state - ShapeBaseImageData::StateData::LightRecoil; + if ( mDataBlock->recoilSequence[stateIndex] != -1 ) + { + mShapeInstance->setSequence( mRecoilThread, mDataBlock->recoilSequence[stateIndex], 0 ); + mShapeInstance->setTimeScale( mRecoilThread, 1 ); + } + } + } +} + +void Player::onImageStateAnimation(U32 imageSlot, const char* seqName, bool direction, bool scaleToState, F32 stateTimeOutValue) +{ + if (mDataBlock->allowImageStateAnimation && isGhost()) + { + MountedImage& image = mMountedImageList[imageSlot]; + + // Just as with onImageAnimThreadChange we're going to apply various prefixes to determine the final sequence to use. + // Here is the order: + // imageBasePrefix_scriptPrefix_baseAnimName + // imageBasePrefix_baseAnimName + // scriptPrefix_baseAnimName + // baseAnimName + + // Collect the prefixes + const char* imageBasePrefix = ""; + bool hasImageBasePrefix = image.dataBlock && image.dataBlock->imageAnimPrefix && image.dataBlock->imageAnimPrefix[0]; + if (hasImageBasePrefix) + imageBasePrefix = image.dataBlock->imageAnimPrefix; + const char* scriptPrefix = getImageScriptAnimPrefix(imageSlot).getString(); + bool hasScriptPrefix = scriptPrefix && scriptPrefix[0]; + + S32 seqIndex = mShapeInstance->getShape()->findSequence(seqName); + + // Find the final sequence based on the prefix combinations + if (hasImageBasePrefix || hasScriptPrefix) + { + bool found = false; + String baseSeqName(seqName); + + if (!found && hasImageBasePrefix && hasScriptPrefix) + { + String seqName = String(imageBasePrefix) + String("_") + String(scriptPrefix) + String("_") + baseSeqName; + S32 index = mShapeInstance->getShape()->findSequence(seqName); + if (index != -1) + { + seqIndex = index; + found = true; + } + } + + if (!found && hasImageBasePrefix) + { + String seqName = String(imageBasePrefix) + String("_") + baseSeqName; + S32 index = mShapeInstance->getShape()->findSequence(seqName); + if (index != -1) + { + seqIndex = index; + found = true; + } + } + + if (!found && hasScriptPrefix) + { + String seqName = String(scriptPrefix) + String("_") + baseSeqName; + S32 index = mShapeInstance->getShape()->findSequence(seqName); + if (index != -1) + { + seqIndex = index; + found = true; + } + } + } + + if (seqIndex != -1) + { + if (!mImageStateThread) + { + mImageStateThread = mShapeInstance->addThread(); + } + + mShapeInstance->setSequence( mImageStateThread, seqIndex, 0 ); + + F32 timeScale = (scaleToState && stateTimeOutValue) ? + mShapeInstance->getDuration(mImageStateThread) / stateTimeOutValue : 1.0f; + + mShapeInstance->setTimeScale( mImageStateThread, direction ? timeScale : -timeScale ); + } + } +} + +const char* Player::getImageAnimPrefix(U32 imageSlot, S32 imageShapeIndex) +{ + if (!mDataBlock) + return ""; + + switch (imageShapeIndex) + { + case ShapeBaseImageData::StandardImageShape: + { + return mDataBlock->imageAnimPrefix; + } + + case ShapeBaseImageData::FirstPersonImageShape: + { + return mDataBlock->imageAnimPrefixFP; + } + + default: + { + return ""; + } + } +} + +void Player::onImageAnimThreadChange(U32 imageSlot, S32 imageShapeIndex, ShapeBaseImageData::StateData* lastState, const char* anim, F32 pos, F32 timeScale, bool reset) +{ + if (!mShapeFPInstance[imageSlot] || !mShapeFPAnimThread[imageSlot]) + return; + + MountedImage& image = mMountedImageList[imageSlot]; + ShapeBaseImageData::StateData& stateData = *image.state; + + if (reset) + { + // Reset cyclic sequences back to the first frame to turn it off + // (the first key frame should be it's off state). + if (mShapeFPAnimThread[imageSlot]->getSequence()->isCyclic() && (stateData.sequenceNeverTransition || !(stateData.sequenceTransitionIn || (lastState && lastState->sequenceTransitionOut))) ) + { + mShapeFPInstance[imageSlot]->setPos(mShapeFPAnimThread[imageSlot],0); + mShapeFPInstance[imageSlot]->setTimeScale(mShapeFPAnimThread[imageSlot],0); + } + + return; + } + + // Just as with ShapeBase::udpateAnimThread we're going to apply various prefixes to determine the final sequence to use. + // Here is the order: + // imageBasePrefix_scriptPrefix_baseAnimName + // imageBasePrefix_baseAnimName + // scriptPrefix_baseAnimName + // baseAnimName + + // Collect the prefixes + const char* imageBasePrefix = ""; + bool hasImageBasePrefix = image.dataBlock && image.dataBlock->imageAnimPrefixFP && image.dataBlock->imageAnimPrefixFP[0]; + if (hasImageBasePrefix) + imageBasePrefix = image.dataBlock->imageAnimPrefixFP; + const char* scriptPrefix = getImageScriptAnimPrefix(imageSlot).getString(); + bool hasScriptPrefix = scriptPrefix && scriptPrefix[0]; + + S32 seqIndex = mShapeFPInstance[imageSlot]->getShape()->findSequence(anim); + + // Find the final sequence based on the prefix combinations + if (hasImageBasePrefix || hasScriptPrefix) + { + bool found = false; + String baseSeqName(anim); + + if (!found && hasImageBasePrefix && hasScriptPrefix) + { + String seqName = String(imageBasePrefix) + String("_") + String(scriptPrefix) + String("_") + baseSeqName; + S32 index = mShapeFPInstance[imageSlot]->getShape()->findSequence(seqName); + if (index != -1) + { + seqIndex = index; + found = true; + } + } + + if (!found && hasImageBasePrefix) + { + String seqName = String(imageBasePrefix) + String("_") + baseSeqName; + S32 index = mShapeFPInstance[imageSlot]->getShape()->findSequence(seqName); + if (index != -1) + { + seqIndex = index; + found = true; + } + } + + if (!found && hasScriptPrefix) + { + String seqName = String(scriptPrefix) + String("_") + baseSeqName; + S32 index = mShapeFPInstance[imageSlot]->getShape()->findSequence(seqName); + if (index != -1) + { + seqIndex = index; + found = true; + } + } + } + + if (seqIndex != -1) + { + if (!lastState) + { + // No lastState indicates that we are just switching animation sequences, not states. Transition into this new sequence, but only + // if it is different than what we're currently playing. + S32 prevSeq = -1; + if (mShapeFPAnimThread[imageSlot]->hasSequence()) + { + prevSeq = mShapeFPInstance[imageSlot]->getSequence(mShapeFPAnimThread[imageSlot]); + } + if (seqIndex != prevSeq) + { + mShapeFPInstance[imageSlot]->transitionToSequence(mShapeFPAnimThread[imageSlot], seqIndex, pos, image.dataBlock->scriptAnimTransitionTime, true); + } + } + else if (!stateData.sequenceNeverTransition && stateData.sequenceTransitionTime && (stateData.sequenceTransitionIn || (lastState && lastState->sequenceTransitionOut)) ) + { + mShapeFPInstance[imageSlot]->transitionToSequence(mShapeFPAnimThread[imageSlot], seqIndex, pos, stateData.sequenceTransitionTime, true); + } + else + { + mShapeFPInstance[imageSlot]->setSequence(mShapeFPAnimThread[imageSlot], seqIndex, pos); + } + mShapeFPInstance[imageSlot]->setTimeScale(mShapeFPAnimThread[imageSlot], timeScale); + } +} + +void Player::onImageAnimThreadUpdate(U32 imageSlot, S32 imageShapeIndex, F32 dt) +{ + if (!mShapeFPInstance[imageSlot]) + return; + + if (mShapeFPAmbientThread[imageSlot] && mShapeFPAmbientThread[imageSlot]->hasSequence()) + { + mShapeFPInstance[imageSlot]->advanceTime(dt,mShapeFPAmbientThread[imageSlot]); + } + + if (mShapeFPAnimThread[imageSlot] && mShapeFPAnimThread[imageSlot]->hasSequence()) + { + mShapeFPInstance[imageSlot]->advanceTime(dt,mShapeFPAnimThread[imageSlot]); + } +} + +void Player::onUnmount( ShapeBase *obj, S32 node ) +{ + // Reset back to root position during dismount. + setActionThread(PlayerData::RootAnim,true,false,false); + + // Re-orient the player straight up + Point3F pos,vec; + getTransform().getColumn(1,&vec); + getTransform().getColumn(3,&pos); + Point3F rot(0.0f,0.0f,-mAtan2(-vec.x,vec.y)); + setPosition(pos,rot); + + // Parent function will call script + Parent::onUnmount( obj, node ); +} + +void Player::unmount() +{ + // Reset back to root position during dismount. This copies what is + // done on the server and corrects the fact that the RootAnim change + // is not sent across to the client using the standard ActionMask. + setActionThread(PlayerData::RootAnim,true,false,false); + + Parent::unmount(); +} + + +//---------------------------------------------------------------------------- + +void Player::updateAnimation(F32 dt) +{ + // If dead then remove any image animations + if ((mDamageState == Disabled || mDamageState == Destroyed) && mImageStateThread) + { + // Remove the image state animation + mShapeInstance->destroyThread(mImageStateThread); + mImageStateThread = 0; + } + + if ((isGhost() || mActionAnimation.animateOnServer) && mActionAnimation.thread) + mShapeInstance->advanceTime(dt,mActionAnimation.thread); + if (mRecoilThread) + mShapeInstance->advanceTime(dt,mRecoilThread); + if (mImageStateThread) + mShapeInstance->advanceTime(dt,mImageStateThread); + + // If we are the client's player on this machine, then we need + // to make sure the transforms are up to date as they are used + // to setup the camera. + if (isGhost()) + { + if (getControllingClient()) + { + updateAnimationTree(isFirstPerson()); + mShapeInstance->animate(); + } + else + { + updateAnimationTree(false); + } + } +} + +void Player::updateAnimationTree(bool firstPerson) +{ + S32 mode = 0; + if (firstPerson) + if (mActionAnimation.firstPerson) + mode = 0; +// TSShapeInstance::MaskNodeRotation; +// TSShapeInstance::MaskNodePosX | +// TSShapeInstance::MaskNodePosY; + else + mode = TSShapeInstance::MaskNodeAllButBlend; + + for (U32 i = 0; i < PlayerData::NumSpineNodes; i++) + if (mDataBlock->spineNode[i] != -1) + mShapeInstance->setNodeAnimationState(mDataBlock->spineNode[i],mode); +} + + +//---------------------------------------------------------------------------- + +bool Player::step(Point3F *pos,F32 *maxStep,F32 time) +{ + const Point3F& scale = getScale(); + Box3F box; + VectorF offset = mVelocity * time; + box.minExtents = mObjBox.minExtents + offset + *pos; + box.maxExtents = mObjBox.maxExtents + offset + *pos; + box.maxExtents.z += mDataBlock->maxStepHeight * scale.z + sMinFaceDistance; + + SphereF sphere; + sphere.center = (box.minExtents + box.maxExtents) * 0.5f; + VectorF bv = box.maxExtents - sphere.center; + sphere.radius = bv.len(); + + ClippedPolyList polyList; + polyList.mPlaneList.clear(); + polyList.mNormal.set(0.0f, 0.0f, 0.0f); + polyList.mPlaneList.setSize(6); + polyList.mPlaneList[0].set(box.minExtents,VectorF(-1.0f, 0.0f, 0.0f)); + polyList.mPlaneList[1].set(box.maxExtents,VectorF(0.0f, 1.0f, 0.0f)); + polyList.mPlaneList[2].set(box.maxExtents,VectorF(1.0f, 0.0f, 0.0f)); + polyList.mPlaneList[3].set(box.minExtents,VectorF(0.0f, -1.0f, 0.0f)); + polyList.mPlaneList[4].set(box.minExtents,VectorF(0.0f, 0.0f, -1.0f)); + polyList.mPlaneList[5].set(box.maxExtents,VectorF(0.0f, 0.0f, 1.0f)); + + CollisionWorkingList& rList = mConvex.getWorkingList(); + CollisionWorkingList* pList = rList.wLink.mNext; + while (pList != &rList) { + Convex* pConvex = pList->mConvex; + + // Alright, here's the deal... a polysoup mesh really needs to be + // designed with stepping in mind. If there are too many smallish polygons + // the stepping system here gets confused and allows you to run up walls + // or on the edges/seams of meshes. + + TSStatic *st = dynamic_cast (pConvex->getObject()); + bool skip = false; + if (st && !st->allowPlayerStep()) + skip = true; + + if ((pConvex->getObject()->getTypeMask() & StaticObjectType) != 0 && !skip) + { + Box3F convexBox = pConvex->getBoundingBox(); + if (box.isOverlapped(convexBox)) + pConvex->getPolyList(&polyList); + } + pList = pList->wLink.mNext; + } + + // Find max step height + F32 stepHeight = pos->z - sMinFaceDistance; + U32* vp = polyList.mIndexList.begin(); + U32* ep = polyList.mIndexList.end(); + for (; vp != ep; vp++) { + F32 h = polyList.mVertexList[*vp].point.z + sMinFaceDistance; + if (h > stepHeight) + stepHeight = h; + } + + F32 step = stepHeight - pos->z; + if (stepHeight > pos->z && step < *maxStep) { + // Go ahead and step + pos->z = stepHeight; + *maxStep -= step; + return true; + } + + return false; +} + + +//---------------------------------------------------------------------------- +inline Point3F createInterpPos(const Point3F& s, const Point3F& e, const F32 t, const F32 d) +{ + Point3F ret; + ret.interpolate(s, e, t/d); + return ret; +} + +Point3F Player::_move( const F32 travelTime, Collision *outCol ) +{ + // Try and move to new pos + F32 totalMotion = 0.0f; + + // TODO: not used? + //F32 initialSpeed = mVelocity.len(); + + Point3F start; + Point3F initialPosition; + getTransform().getColumn(3,&start); + initialPosition = start; + + static CollisionList collisionList; + static CollisionList physZoneCollisionList; + + collisionList.clear(); + physZoneCollisionList.clear(); + + MatrixF collisionMatrix(true); + collisionMatrix.setColumn(3, start); + + VectorF firstNormal(0.0f, 0.0f, 0.0f); + F32 maxStep = mDataBlock->maxStepHeight; + F32 time = travelTime; + U32 count = 0; + + const Point3F& scale = getScale(); + + static Polyhedron sBoxPolyhedron; + static ExtrudedPolyList sExtrudedPolyList; + static ExtrudedPolyList sPhysZonePolyList; + + for (; count < sMoveRetryCount; count++) { + F32 speed = mVelocity.len(); + if (!speed && !mDeath.haveVelocity()) + break; + + Point3F end = start + mVelocity * time; + if (mDeath.haveVelocity()) { + // Add in death movement- + VectorF deathVel = mDeath.getPosAdd(); + VectorF resVel; + getTransform().mulV(deathVel, & resVel); + end += resVel; + } + Point3F distance = end - start; + + if (mFabs(distance.x) < mObjBox.len_x() && + mFabs(distance.y) < mObjBox.len_y() && + mFabs(distance.z) < mObjBox.len_z()) + { + // We can potentially early out of this. If there are no polys in the clipped polylist at our + // end position, then we can bail, and just set start = end; + Box3F wBox = mScaledBox; + wBox.minExtents += end; + wBox.maxExtents += end; + + static EarlyOutPolyList eaPolyList; + eaPolyList.clear(); + eaPolyList.mNormal.set(0.0f, 0.0f, 0.0f); + eaPolyList.mPlaneList.clear(); + eaPolyList.mPlaneList.setSize(6); + eaPolyList.mPlaneList[0].set(wBox.minExtents,VectorF(-1.0f, 0.0f, 0.0f)); + eaPolyList.mPlaneList[1].set(wBox.maxExtents,VectorF(0.0f, 1.0f, 0.0f)); + eaPolyList.mPlaneList[2].set(wBox.maxExtents,VectorF(1.0f, 0.0f, 0.0f)); + eaPolyList.mPlaneList[3].set(wBox.minExtents,VectorF(0.0f, -1.0f, 0.0f)); + eaPolyList.mPlaneList[4].set(wBox.minExtents,VectorF(0.0f, 0.0f, -1.0f)); + eaPolyList.mPlaneList[5].set(wBox.maxExtents,VectorF(0.0f, 0.0f, 1.0f)); + + // Build list from convex states here... + CollisionWorkingList& rList = mConvex.getWorkingList(); + CollisionWorkingList* pList = rList.wLink.mNext; + while (pList != &rList) { + Convex* pConvex = pList->mConvex; + if (pConvex->getObject()->getTypeMask() & sCollisionMoveMask) { + Box3F convexBox = pConvex->getBoundingBox(); + if (wBox.isOverlapped(convexBox)) + { + // No need to separate out the physical zones here, we want those + // to cause a fallthrough as well... + pConvex->getPolyList(&eaPolyList); + } + } + pList = pList->wLink.mNext; + } + + if (eaPolyList.isEmpty()) + { + totalMotion += (end - start).len(); + start = end; + break; + } + } + + collisionMatrix.setColumn(3, start); + sBoxPolyhedron.buildBox(collisionMatrix, mScaledBox, true); + + // Setup the bounding box for the extrudedPolyList + Box3F plistBox = mScaledBox; + collisionMatrix.mul(plistBox); + Point3F oldMin = plistBox.minExtents; + Point3F oldMax = plistBox.maxExtents; + plistBox.minExtents.setMin(oldMin + (mVelocity * time) - Point3F(0.1f, 0.1f, 0.1f)); + plistBox.maxExtents.setMax(oldMax + (mVelocity * time) + Point3F(0.1f, 0.1f, 0.1f)); + + // Build extruded polyList... + VectorF vector = end - start; + sExtrudedPolyList.extrude(sBoxPolyhedron,vector); + sExtrudedPolyList.setVelocity(mVelocity); + sExtrudedPolyList.setCollisionList(&collisionList); + + sPhysZonePolyList.extrude(sBoxPolyhedron,vector); + sPhysZonePolyList.setVelocity(mVelocity); + sPhysZonePolyList.setCollisionList(&physZoneCollisionList); + + // Build list from convex states here... + CollisionWorkingList& rList = mConvex.getWorkingList(); + CollisionWorkingList* pList = rList.wLink.mNext; + while (pList != &rList) { + Convex* pConvex = pList->mConvex; + if (pConvex->getObject()->getTypeMask() & sCollisionMoveMask) { + Box3F convexBox = pConvex->getBoundingBox(); + if (plistBox.isOverlapped(convexBox)) + { + if (pConvex->getObject()->getTypeMask() & PhysicalZoneObjectType) + pConvex->getPolyList(&sPhysZonePolyList); + else + pConvex->getPolyList(&sExtrudedPolyList); + } + } + pList = pList->wLink.mNext; + } + + // Take into account any physical zones... + for (U32 j = 0; j < physZoneCollisionList.getCount(); j++) + { + AssertFatal(dynamic_cast(physZoneCollisionList[j].object), "Bad phys zone!"); + const PhysicalZone* pZone = (PhysicalZone*)physZoneCollisionList[j].object; + if (pZone->isActive()) + mVelocity *= pZone->getVelocityMod(); + } + + if (collisionList.getCount() != 0 && collisionList.getTime() < 1.0f) + { + // Set to collision point + F32 velLen = mVelocity.len(); + + F32 dt = time * getMin(collisionList.getTime(), 1.0f); + start += mVelocity * dt; + time -= dt; + + totalMotion += velLen * dt; + + bool wasFalling = mFalling; + mFalling = false; + + // Back off... + if ( velLen > 0.f ) { + F32 newT = getMin(0.01f / velLen, dt); + start -= mVelocity * newT; + totalMotion -= velLen * newT; + } + + // Try stepping if there is a vertical surface + if (collisionList.getMaxHeight() < start.z + mDataBlock->maxStepHeight * scale.z) + { + bool stepped = false; + for (U32 c = 0; c < collisionList.getCount(); c++) + { + const Collision& cp = collisionList[c]; + // if (mFabs(mDot(cp.normal,VectorF(0,0,1))) < sVerticalStepDot) + // Dot with (0,0,1) just extracts Z component [lh]- + if (mFabs(cp.normal.z) < sVerticalStepDot) + { + stepped = step(&start,&maxStep,time); + break; + } + } + if (stepped) + { + continue; + } + } + + // Pick the surface most parallel to the face that was hit. + const Collision *collision = &collisionList[0]; + const Collision *cp = collision + 1; + const Collision *ep = collision + collisionList.getCount(); + for (; cp != ep; cp++) + { + if (cp->faceDot > collision->faceDot) + collision = cp; + } + + F32 bd = _doCollisionImpact( collision, wasFalling ); + + // Copy this collision out so + // we can use it to do impacts + // and query collision. + *outCol = *collision; + + // Subtract out velocity + VectorF dv = collision->normal * (bd + sNormalElasticity); + mVelocity += dv; + if (count == 0) + { + firstNormal = collision->normal; + } + else + { + if (count == 1) + { + // Re-orient velocity along the crease. + if (mDot(dv,firstNormal) < 0.0f && + mDot(collision->normal,firstNormal) < 0.0f) + { + VectorF nv; + mCross(collision->normal,firstNormal,&nv); + F32 nvl = nv.len(); + if (nvl) + { + if (mDot(nv,mVelocity) < 0.0f) + nvl = -nvl; + nv *= mVelocity.len() / nvl; + mVelocity = nv; + } + } + } + } + } + else + { + totalMotion += (end - start).len(); + start = end; + break; + } + } + + if (count == sMoveRetryCount) + { + // Failed to move + start = initialPosition; + mVelocity.set(0.0f, 0.0f, 0.0f); + } + + return start; +} + +F32 Player::_doCollisionImpact( const Collision *collision, bool fallingCollision) +{ + F32 bd = -mDot( mVelocity, collision->normal); + + // shake camera on ground impact + if( bd > mDataBlock->groundImpactMinSpeed && isControlObject() ) + { + F32 ampScale = (bd - mDataBlock->groundImpactMinSpeed) / mDataBlock->minImpactSpeed; + + CameraShake *groundImpactShake = new CameraShake; + groundImpactShake->setDuration( mDataBlock->groundImpactShakeDuration ); + groundImpactShake->setFrequency( mDataBlock->groundImpactShakeFreq ); + + VectorF shakeAmp = mDataBlock->groundImpactShakeAmp * ampScale; + groundImpactShake->setAmplitude( shakeAmp ); + groundImpactShake->setFalloff( mDataBlock->groundImpactShakeFalloff ); + groundImpactShake->init(); + gCamFXMgr.addFX( groundImpactShake ); + } + + if ( ((bd > mDataBlock->minImpactSpeed && fallingCollision) || bd > mDataBlock->minLateralImpactSpeed) + && !mMountPending ) + { + if ( !isGhost() ) + onImpact( collision->object, collision->normal * bd ); + + if (mDamageState == Enabled && mState != RecoverState) + { + // Scale how long we're down for + if (mDataBlock->landSequenceTime > 0.0f) + { + // Recover time is based on the land sequence + setState(RecoverState); + } + else + { + // Legacy recover system + F32 value = (bd - mDataBlock->minImpactSpeed); + F32 range = (mDataBlock->minImpactSpeed * 0.9f); + U32 recover = mDataBlock->recoverDelay; + if (value < range) + recover = 1 + S32(mFloor( F32(recover) * value / range) ); + //Con::printf("Used %d recover ticks", recover); + //Con::printf(" minImpact = %g, this one = %g", mDataBlock->minImpactSpeed, bd); + setState(RecoverState, recover); + } + } + } + + if ( isServerObject() && + (bd > (mDataBlock->minImpactSpeed / 3.0f) || bd > (mDataBlock->minLateralImpactSpeed / 3.0f )) ) + { + mImpactSound = PlayerData::ImpactNormal; + setMaskBits(ImpactMask); + } + + return bd; +} + +void Player::_handleCollision( const Collision &collision ) +{ + // Track collisions + if ( !isGhost() && + collision.object && + collision.object != mContactInfo.contactObject ) + queueCollision( collision.object, mVelocity - collision.object->getVelocity() ); +} + +bool Player::updatePos(const F32 travelTime) +{ + PROFILE_SCOPE(Player_UpdatePos); + getTransform().getColumn(3,&delta.posVec); + + // When mounted to another object, only Z rotation used. + if (isMounted()) { + mVelocity = mMount.object->getVelocity(); + setPosition(Point3F(0.0f, 0.0f, 0.0f), mRot); + setMaskBits(MoveMask); + return true; + } + + Point3F newPos; + + Collision col; + dMemset( &col, 0, sizeof( col ) ); + + // DEBUG: + //Point3F savedVelocity = mVelocity; + + if ( mPhysicsRep ) + { + static CollisionList collisionList; + collisionList.clear(); + + newPos = mPhysicsRep->move( mVelocity * travelTime, collisionList ); + + bool haveCollisions = false; + bool wasFalling = mFalling; + if (collisionList.getCount() > 0) + { + mFalling = false; + haveCollisions = true; + } + + if (haveCollisions) + { + // Pick the collision that most closely matches our direction + VectorF velNormal = mVelocity; + velNormal.normalizeSafe(); + const Collision *collision = &collisionList[0]; + F32 collisionDot = mDot(velNormal, collision->normal); + const Collision *cp = collision + 1; + const Collision *ep = collision + collisionList.getCount(); + for (; cp != ep; cp++) + { + F32 dp = mDot(velNormal, cp->normal); + if (dp < collisionDot) + { + collisionDot = dp; + collision = cp; + } + } + + _doCollisionImpact( collision, wasFalling ); + + // Modify our velocity based on collisions + for (U32 i=0; i 0) + col = collisionList[collisionList.getCount() - 1]; + + // We'll handle any player-to-player collision, and the last collision + // with other obejct types. + for (U32 i=0; i(col.object); + if (obj->getTypeMask() & PlayerObjectType) + { + _handleCollision( colCheck ); + } + else + { + col = colCheck; + } + } + } + + _handleCollision( col ); + } + } + else + { + if ( mVelocity.isZero() ) + newPos = delta.posVec; + else + newPos = _move( travelTime, &col ); + + _handleCollision( col ); + } + + // DEBUG: + //if ( isClientObject() ) + // Con::printf( "(client) vel: %g %g %g", mVelocity.x, mVelocity.y, mVelocity.z ); + //else + // Con::printf( "(server) vel: %g %g %g", mVelocity.x, mVelocity.y, mVelocity.z ); + + // Set new position + // If on the client, calc delta for backstepping + if (isClientObject()) + { + delta.pos = newPos; + delta.posVec = delta.posVec - delta.pos; + delta.dt = 1.0f; + } + + setPosition( newPos, mRot ); + setMaskBits( MoveMask ); + updateContainer(); + + if (!isGhost()) + { + // Collisions are only queued on the server and can be + // generated by either updateMove or updatePos + notifyCollision(); + + // Do mission area callbacks on the server as well + checkMissionArea(); + } + + // Check the total distance moved. If it is more than 1000th of the velocity, then + // we moved a fair amount... + //if (totalMotion >= (0.001f * initialSpeed)) + return true; + //else + //return false; +} + + +//---------------------------------------------------------------------------- + +void Player::_findContact( SceneObject **contactObject, + VectorF *contactNormal, + Vector *outOverlapObjects ) +{ + Point3F pos; + getTransform().getColumn(3,&pos); + + Box3F wBox; + Point3F exp(0,0,sTractionDistance); + wBox.minExtents = pos + mScaledBox.minExtents - exp; + wBox.maxExtents.x = pos.x + mScaledBox.maxExtents.x; + wBox.maxExtents.y = pos.y + mScaledBox.maxExtents.y; + wBox.maxExtents.z = pos.z + mScaledBox.minExtents.z + sTractionDistance; + + static ClippedPolyList polyList; + polyList.clear(); + polyList.doConstruct(); + polyList.mNormal.set(0.0f, 0.0f, 0.0f); + polyList.setInterestNormal(Point3F(0.0f, 0.0f, -1.0f)); + + polyList.mPlaneList.setSize(6); + polyList.mPlaneList[0].setYZ(wBox.minExtents, -1.0f); + polyList.mPlaneList[1].setXZ(wBox.maxExtents, 1.0f); + polyList.mPlaneList[2].setYZ(wBox.maxExtents, 1.0f); + polyList.mPlaneList[3].setXZ(wBox.minExtents, -1.0f); + polyList.mPlaneList[4].setXY(wBox.minExtents, -1.0f); + polyList.mPlaneList[5].setXY(wBox.maxExtents, 1.0f); + Box3F plistBox = wBox; + + // Expand build box as it will be used to collide with items. + // PickupRadius will be at least the size of the box. + F32 pd = (F32)mDataBlock->pickupDelta; + wBox.minExtents.x -= pd; wBox.minExtents.y -= pd; + wBox.maxExtents.x += pd; wBox.maxExtents.y += pd; + wBox.maxExtents.z = pos.z + mScaledBox.maxExtents.z; + + // Build list from convex states here... + CollisionWorkingList& rList = mConvex.getWorkingList(); + CollisionWorkingList* pList = rList.wLink.mNext; + while (pList != &rList) + { + Convex* pConvex = pList->mConvex; + + U32 objectMask = pConvex->getObject()->getTypeMask(); + + if ( ( objectMask & sCollisionMoveMask ) && + !( objectMask & PhysicalZoneObjectType ) ) + { + Box3F convexBox = pConvex->getBoundingBox(); + if (plistBox.isOverlapped(convexBox)) + pConvex->getPolyList(&polyList); + } + else + outOverlapObjects->push_back( pConvex->getObject() ); + + pList = pList->wLink.mNext; + } + + if (!polyList.isEmpty()) + { + // Pick flattest surface + F32 bestVd = -1.0f; + ClippedPolyList::Poly* poly = polyList.mPolyList.begin(); + ClippedPolyList::Poly* end = polyList.mPolyList.end(); + for (; poly != end; poly++) + { + F32 vd = poly->plane.z; // i.e. mDot(Point3F(0,0,1), poly->plane); + if (vd > bestVd) + { + bestVd = vd; + *contactObject = poly->object; + *contactNormal = poly->plane; + } + } + } +} + +void Player::findContact( bool *run, bool *jump, VectorF *contactNormal ) +{ + SceneObject *contactObject = NULL; + + Vector overlapObjects; + if ( mPhysicsRep ) + mPhysicsRep->findContact( &contactObject, contactNormal, &overlapObjects ); + else + _findContact( &contactObject, contactNormal, &overlapObjects ); + + // Check for triggers, corpses and items. + const U32 filterMask = isGhost() ? sClientCollisionContactMask : sServerCollisionContactMask; + for ( U32 i=0; i < overlapObjects.size(); i++ ) + { + SceneObject *obj = overlapObjects[i]; + U32 objectMask = obj->getTypeMask(); + + if ( !( objectMask & filterMask ) ) + continue; + + // Check: triggers, corpses and items... + // + if (objectMask & TriggerObjectType) + { + Trigger* pTrigger = static_cast( obj ); + pTrigger->potentialEnterObject(this); + } + else if (objectMask & CorpseObjectType) + { + // If we've overlapped the worldbounding boxes, then that's it... + if ( getWorldBox().isOverlapped( obj->getWorldBox() ) ) + { + ShapeBase* col = static_cast( obj ); + queueCollision(col,getVelocity() - col->getVelocity()); + } + } + else if (objectMask & ItemObjectType) + { + // If we've overlapped the worldbounding boxes, then that's it... + Item* item = static_cast( obj ); + if ( getWorldBox().isOverlapped(item->getWorldBox()) && + item->getCollisionObject() != this && + !item->isHidden() ) + queueCollision(item,getVelocity() - item->getVelocity()); + } + } + + F32 vd = (*contactNormal).z; + *run = vd > mDataBlock->runSurfaceCos; + *jump = vd > mDataBlock->jumpSurfaceCos; + + mContactInfo.clear(); + + mContactInfo.contacted = contactObject != NULL; + mContactInfo.contactObject = contactObject; + + if ( mContactInfo.contacted ) + mContactInfo.contactNormal = *contactNormal; + + mContactInfo.run = *run; + mContactInfo.jump = *jump; +} + +//---------------------------------------------------------------------------- + +void Player::checkMissionArea() +{ + // Checks to see if the player is in the Mission Area... + Point3F pos; + MissionArea * obj = MissionArea::getServerObject(); + + if(!obj) + return; + + const RectI &area = obj->getArea(); + getTransform().getColumn(3, &pos); + + if ((pos.x < area.point.x || pos.x > area.point.x + area.extent.x || + pos.y < area.point.y || pos.y > area.point.y + area.extent.y)) { + if(mInMissionArea) { + mInMissionArea = false; + mDataBlock->onLeaveMissionArea_callback( this ); + } + } + else if(!mInMissionArea) + { + mInMissionArea = true; + mDataBlock->onEnterMissionArea_callback( this ); + } +} + + +//---------------------------------------------------------------------------- + +bool Player::isDisplacable() const +{ + return true; +} + +Point3F Player::getMomentum() const +{ + return mVelocity * getMass(); +} + +void Player::setMomentum(const Point3F& newMomentum) +{ + Point3F newVelocity = newMomentum / getMass(); + mVelocity = newVelocity; +} + +#define LH_HACK 1 +// Hack for short-term soln to Training crash - +#if LH_HACK +static U32 sBalance; + +bool Player::displaceObject(const Point3F& displacement) +{ + F32 vellen = mVelocity.len(); + if (vellen < 0.001f || sBalance > 16) { + mVelocity.set(0.0f, 0.0f, 0.0f); + return false; + } + + F32 dt = displacement.len() / vellen; + + sBalance++; + + bool result = updatePos(dt); + + sBalance--; + + getTransform().getColumn(3, &delta.pos); + delta.posVec.set(0.0f, 0.0f, 0.0f); + + return result; +} + +#else + +bool Player::displaceObject(const Point3F& displacement) +{ + F32 vellen = mVelocity.len(); + if (vellen < 0.001f) { + mVelocity.set(0.0f, 0.0f, 0.0f); + return false; + } + + F32 dt = displacement.len() / vellen; + + bool result = updatePos(dt); + + mObjToWorld.getColumn(3, &delta.pos); + delta.posVec.set(0.0f, 0.0f, 0.0f); + + return result; +} + +#endif + +//---------------------------------------------------------------------------- + +void Player::setPosition(const Point3F& pos,const Point3F& rot) +{ + MatrixF mat; + if (isMounted()) { + // Use transform from mounted object + MatrixF nmat,zrot; + mMount.object->getMountTransform( mMount.node, mMount.xfm, &nmat ); + zrot.set(EulerF(0.0f, 0.0f, rot.z)); + mat.mul(nmat,zrot); + } + else { + mat.set(EulerF(0.0f, 0.0f, rot.z)); + mat.setColumn(3,pos); + } + Parent::setTransform(mat); + mRot = rot; + + if ( mPhysicsRep ) + mPhysicsRep->setTransform( mat ); +} + + +void Player::setRenderPosition(const Point3F& pos, const Point3F& rot, F32 dt) +{ + MatrixF mat; + if (isMounted()) { + // Use transform from mounted object + MatrixF nmat,zrot; + mMount.object->getRenderMountTransform( dt, mMount.node, mMount.xfm, &nmat ); + zrot.set(EulerF(0.0f, 0.0f, rot.z)); + mat.mul(nmat,zrot); + } + else { + EulerF orient(0.0f, 0.0f, rot.z); + + mat.set(orient); + mat.setColumn(3, pos); + + if (inDeathAnim()) { + F32 boxRad = (mDataBlock->boxSize.x * 0.5f); + if (MatrixF * fallMat = mDeath.fallToGround(dt, pos, rot.z, boxRad)) + mat = * fallMat; + } + else + mDeath.initFall(); + } + Parent::setRenderTransform(mat); +} + +//---------------------------------------------------------------------------- + +void Player::setTransform(const MatrixF& mat) +{ + // This method should never be called on the client. + + // This currently converts all rotation in the mat into + // rotations around the z axis. + Point3F pos,vec; + mat.getColumn(1,&vec); + mat.getColumn(3,&pos); + Point3F rot(0.0f, 0.0f, -mAtan2(-vec.x,vec.y)); + setPosition(pos,rot); + setMaskBits(MoveMask | NoWarpMask); +} + +void Player::getEyeTransform(MatrixF* mat) +{ + getEyeBaseTransform(mat); + + // The shape instance is animated in getEyeBaseTransform() so we're + // good here when attempting to get the eye node position on the server. + S32 imageIndex = -1; + S32 shapeIndex = -1; + MountedImage* image = NULL; + ShapeBaseImageData* data = NULL; + for (U32 i=0; idataBlock) + { + data = image->dataBlock; + shapeIndex = getImageShapeIndex(*image); + if ( data->useEyeNode && (data->animateOnServer || isGhost()) && isFirstPerson() && data->eyeMountNode[shapeIndex] != -1 && data->eyeNode[shapeIndex] != -1 ) + { + imageIndex = i; + break; + } + } + } + + if (imageIndex >= 0) + { + // Get the image's eye node's position relative to the eye mount node + MatrixF mountTransform = image->shapeInstance[shapeIndex]->mNodeTransforms[data->eyeMountNode[shapeIndex]]; + Point3F eyeMountNodePos = mountTransform.getPosition(); + mountTransform = image->shapeInstance[shapeIndex]->mNodeTransforms[data->eyeNode[shapeIndex]]; + Point3F eyeNodePos = mountTransform.getPosition() - eyeMountNodePos; + + // Now transform to the image's eye node (position only) + MatrixF xfm(true); + xfm.setPosition(eyeNodePos); + mat->mul(xfm); + } +} + +void Player::getEyeBaseTransform(MatrixF* mat) +{ + // Eye transform in world space. We only use the eye position + // from the animation and supply our own rotation. + MatrixF pmat,xmat,zmat; + + if(!isGhost()) + mShapeInstance->animate(); + + xmat.set(EulerF(mHead.x, 0.0f, 0.0f)); + + if (mUseHeadZCalc) + zmat.set(EulerF(0.0f, 0.0f, mHead.z)); + else + zmat.identity(); + + pmat.mul(zmat,xmat); + + F32 *dp = pmat; + + F32* sp; + MatrixF eyeMat(true); + if (mDataBlock->eyeNode != -1) + { + sp = mShapeInstance->mNodeTransforms[mDataBlock->eyeNode]; + } + else + { + Point3F center; + mObjBox.getCenter(¢er); + eyeMat.setPosition(center); + sp = eyeMat; + } + + const Point3F& scale = getScale(); + dp[3] = sp[3] * scale.x; + dp[7] = sp[7] * scale.y; + dp[11] = sp[11] * scale.z; + mat->mul(getTransform(),pmat); +} + +void Player::getRenderEyeTransform(MatrixF* mat) +{ + getRenderEyeBaseTransform(mat); + + // Use the first image that is set to use the eye node + for (U32 i=0; imNodeTransforms[data.eyeMountNode[shapeIndex]]; + Point3F eyeMountNodePos = mountTransform.getPosition(); + mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeNode[shapeIndex]]; + Point3F eyeNodePos = mountTransform.getPosition() - eyeMountNodePos; + + // Now transform to the image's eye node (position only) + MatrixF xfm(true); + xfm.setPosition(eyeNodePos); + mat->mul(xfm); + + return; + } + } + } +} + +void Player::getRenderEyeBaseTransform(MatrixF* mat) +{ + // Eye transform in world space. We only use the eye position + // from the animation and supply our own rotation. + MatrixF pmat,xmat,zmat; + xmat.set(EulerF(delta.head.x + delta.headVec.x * delta.dt, 0.0f, 0.0f)); + + if (mUseHeadZCalc) + zmat.set(EulerF(0.0f, 0.0f, delta.head.z + delta.headVec.z * delta.dt)); + else + zmat.identity(); + + pmat.mul(zmat,xmat); + + F32 *dp = pmat; + + F32* sp; + MatrixF eyeMat(true); + if (mDataBlock->eyeNode != -1) + { + sp = mShapeInstance->mNodeTransforms[mDataBlock->eyeNode]; + } + else + { + // Use the center of the Player's bounding box for the eye position. + Point3F center; + mObjBox.getCenter(¢er); + eyeMat.setPosition(center); + sp = eyeMat; + } + + // Only use position of eye node, and take Player's scale + // into account. + const Point3F& scale = getScale(); + dp[3] = sp[3] * scale.x; + dp[7] = sp[7] * scale.y; + dp[11] = sp[11] * scale.z; + + mat->mul(getRenderTransform(), pmat); +} + +void Player::getMuzzleTransform(U32 imageSlot,MatrixF* mat) +{ + disableHeadZCalc(); + + MatrixF nmat; + Parent::getRetractionTransform(imageSlot,&nmat); + MatrixF smat; + Parent::getImageTransform(imageSlot,&smat); + + disableCollision(); + + // See if we are pushed into a wall... + if (getDamageState() == Enabled) { + Point3F start, end; + smat.getColumn(3, &start); + nmat.getColumn(3, &end); + + RayInfo rinfo; + if (getContainer()->castRay(start, end, sCollisionMoveMask, &rinfo)) { + Point3F finalPoint; + finalPoint.interpolate(start, end, rinfo.t); + nmat.setColumn(3, finalPoint); + } + else + Parent::getMuzzleTransform(imageSlot,&nmat); + } + else + Parent::getMuzzleTransform(imageSlot,&nmat); + + enableCollision(); + + enableHeadZCalc(); + + *mat = nmat; +} + + +void Player::getRenderMuzzleTransform(U32 imageSlot,MatrixF* mat) +{ + disableHeadZCalc(); + + MatrixF nmat; + Parent::getRenderRetractionTransform(imageSlot,&nmat); + MatrixF smat; + Parent::getRenderImageTransform(imageSlot,&smat); + + disableCollision(); + + // See if we are pushed into a wall... + if (getDamageState() == Enabled) + { + Point3F start, end; + smat.getColumn(3, &start); + nmat.getColumn(3, &end); + + RayInfo rinfo; + if (getContainer()->castRay(start, end, sCollisionMoveMask, &rinfo)) { + Point3F finalPoint; + finalPoint.interpolate(start, end, rinfo.t); + nmat.setColumn(3, finalPoint); + } + else + { + Parent::getRenderMuzzleTransform(imageSlot,&nmat); + } + } + else + { + Parent::getRenderMuzzleTransform(imageSlot,&nmat); + } + + enableCollision(); + + enableHeadZCalc(); + + *mat = nmat; +} + +void Player::getMuzzleVector(U32 imageSlot,VectorF* vec) +{ + MatrixF mat; + getMuzzleTransform(imageSlot,&mat); + + GameConnection * gc = getControllingClient(); + if (gc && !gc->isAIControlled()) + { + MountedImage& image = mMountedImageList[imageSlot]; + + bool fp = gc->isFirstPerson(); + if ((fp && image.dataBlock->correctMuzzleVector) || + (!fp && image.dataBlock->correctMuzzleVectorTP)) + { + disableHeadZCalc(); + if (getCorrectedAim(mat, vec)) + { + enableHeadZCalc(); + return; + } + enableHeadZCalc(); + } + } + + mat.getColumn(1,vec); +} + +void Player::renderMountedImage( U32 imageSlot, TSRenderState &rstate, SceneRenderState *state ) +{ + GFX->pushWorldMatrix(); + + MatrixF world; + + MountedImage& image = mMountedImageList[imageSlot]; + ShapeBaseImageData& data = *image.dataBlock; + U32 imageShapeIndex; + if ( state->isShadowPass() ) + { + // Force the standard image shapes for the shadow pass. + imageShapeIndex = ShapeBaseImageData::StandardImageShape; + } + else + { + imageShapeIndex = getImageShapeIndex(image); + } + + if ( !state->isShadowPass() && isFirstPerson() && (data.useEyeOffset || (data.useEyeNode && data.eyeMountNode[imageShapeIndex] != -1)) ) + { + if (data.useEyeNode && data.eyeMountNode[imageShapeIndex] != -1) + { + MatrixF nmat; + getRenderEyeBaseTransform(&nmat); + MatrixF offsetMat = image.shapeInstance[imageShapeIndex]->mNodeTransforms[data.eyeMountNode[imageShapeIndex]]; + offsetMat.affineInverse(); + world.mul(nmat,offsetMat); + } + else + { + MatrixF nmat; + getRenderEyeBaseTransform(&nmat); + world.mul(nmat,data.eyeOffset); + } + + if ( imageSlot == 0 ) + { + MatrixF nmat; + MatrixF smat; + + getRenderRetractionTransform(0,&nmat); + getRenderImageTransform(0,&smat); + + // See if we are pushed into a wall... + Point3F start, end; + smat.getColumn(3, &start); + nmat.getColumn(3, &end); + + Point3F displace = (start - end) * mWeaponBackFraction; + + world.setPosition( world.getPosition() + displace ); + } + } + else + { + MatrixF nmat; + getRenderMountTransform( 0.0f, data.mountPoint, MatrixF::Identity, &nmat); + world.mul(nmat,data.mountTransform[imageShapeIndex]); + } + + GFX->setWorldMatrix( world ); + + image.shapeInstance[imageShapeIndex]->animate(); + image.shapeInstance[imageShapeIndex]->render( rstate ); + + // Render the first person mount image shape? + if (!state->isShadowPass() && imageShapeIndex == ShapeBaseImageData::FirstPersonImageShape && mShapeFPInstance[imageSlot]) + { + mShapeFPInstance[imageSlot]->animate(); + mShapeFPInstance[imageSlot]->render( rstate ); + } + + GFX->popWorldMatrix(); +} + +// Bot aiming code calls this frequently and will work fine without the check +// for being pushed into a wall, which shows up on profile at ~ 3% (eight bots) +void Player::getMuzzlePointAI(U32 imageSlot, Point3F* point) +{ + MatrixF nmat; + Parent::getMuzzleTransform(imageSlot, &nmat); + + // If we are in one of the standard player animations, adjust the + // muzzle to point in the direction we are looking. + if (mActionAnimation.action < PlayerData::NumTableActionAnims) + { + MatrixF xmat; + xmat.set(EulerF(mHead.x, 0, 0)); + MatrixF result; + result.mul(getTransform(), xmat); + F32 *sp = nmat, *dp = result; + dp[3] = sp[3]; dp[7] = sp[7]; dp[11] = sp[11]; + result.getColumn(3, point); + } + else + nmat.getColumn(3, point); +} + +void Player::getCameraParameters(F32 *min,F32* max,Point3F* off,MatrixF* rot) +{ + if (!mControlObject.isNull() && mControlObject == getObjectMount()) { + mControlObject->getCameraParameters(min,max,off,rot); + return; + } + const Point3F& scale = getScale(); + *min = mDataBlock->cameraMinDist * scale.y; + *max = mDataBlock->cameraMaxDist * scale.y; + off->set(0.0f, 0.0f, 0.0f); + rot->identity(); +} + + +//---------------------------------------------------------------------------- + +Point3F Player::getVelocity() const +{ + return mVelocity; +} + +F32 Player::getSpeed() const +{ + return mVelocity.len(); +} + +void Player::setVelocity(const VectorF& vel) +{ + AssertFatal( !mIsNaN( vel ), "Player::setVelocity() - The velocity is NaN!" ); + + mVelocity = vel; + setMaskBits(MoveMask); +} + +void Player::applyImpulse(const Point3F&,const VectorF& vec) +{ + AssertFatal( !mIsNaN( vec ), "Player::applyImpulse() - The vector is NaN!" ); + + // Players ignore angular velocity + VectorF vel; + vel.x = vec.x / getMass(); + vel.y = vec.y / getMass(); + vel.z = vec.z / getMass(); + + // Make sure the impulse isn't too big + F32 len = vel.magnitudeSafe(); + if (len > sMaxImpulseVelocity) + { + Point3F excess = vel * ( 1.0f - (sMaxImpulseVelocity / len ) ); + vel -= excess; + } + + setVelocity(mVelocity + vel); +} + + +//---------------------------------------------------------------------------- + +bool Player::castRay(const Point3F &start, const Point3F &end, RayInfo* info) +{ + if (getDamageState() != Enabled) + return false; + + // Collide against bounding box. Need at least this for the editor. + F32 st,et,fst = 0.0f,fet = 1.0f; + F32 *bmin = &mObjBox.minExtents.x; + F32 *bmax = &mObjBox.maxExtents.x; + F32 const *si = &start.x; + F32 const *ei = &end.x; + + for (int i = 0; i < 3; i++) { + if (*si < *ei) { + if (*si > *bmax || *ei < *bmin) + return false; + F32 di = *ei - *si; + st = (*si < *bmin)? (*bmin - *si) / di: 0.0f; + et = (*ei > *bmax)? (*bmax - *si) / di: 1.0f; + } + else { + if (*ei > *bmax || *si < *bmin) + return false; + F32 di = *ei - *si; + st = (*si > *bmax)? (*bmax - *si) / di: 0.0f; + et = (*ei < *bmin)? (*bmin - *si) / di: 1.0f; + } + if (st > fst) fst = st; + if (et < fet) fet = et; + if (fet < fst) + return false; + bmin++; bmax++; + si++; ei++; + } + + info->normal = start - end; + info->normal.normalizeSafe(); + getTransform().mulV( info->normal ); + + info->t = fst; + info->object = this; + info->point.interpolate(start,end,fst); + info->material = 0; + return true; +} + + +//---------------------------------------------------------------------------- + +static MatrixF IMat(1); + +bool Player::buildPolyList(PolyListContext, AbstractPolyList* polyList, const Box3F&, const SphereF&) +{ + // Collision with the player is always against the player's object + // space bounding box axis aligned in world space. + Point3F pos; + getTransform().getColumn(3,&pos); + IMat.setColumn(3,pos); + polyList->setTransform(&IMat, Point3F(1.0f,1.0f,1.0f)); + polyList->setObject(this); + polyList->addBox(mObjBox); + return true; +} + + +void Player::buildConvex(const Box3F& box, Convex* convex) +{ + if (mShapeInstance == NULL) + return; + + // These should really come out of a pool + mConvexList->collectGarbage(); + + Box3F realBox = box; + mWorldToObj.mul(realBox); + realBox.minExtents.convolveInverse(mObjScale); + realBox.maxExtents.convolveInverse(mObjScale); + + if (realBox.isOverlapped(getObjBox()) == false) + return; + + Convex* cc = 0; + CollisionWorkingList& wl = convex->getWorkingList(); + for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) { + if (itr->mConvex->getType() == BoxConvexType && + itr->mConvex->getObject() == this) { + cc = itr->mConvex; + break; + } + } + if (cc) + return; + + // Create a new convex. + BoxConvex* cp = new OrthoBoxConvex; + mConvexList->registerObject(cp); + convex->addToWorkingList(cp); + cp->init(this); + + mObjBox.getCenter(&cp->mCenter); + cp->mSize.x = mObjBox.len_x() / 2.0f; + cp->mSize.y = mObjBox.len_y() / 2.0f; + cp->mSize.z = mObjBox.len_z() / 2.0f; +} + + +//---------------------------------------------------------------------------- + +void Player::updateWorkingCollisionSet() +{ + // First, we need to adjust our velocity for possible acceleration. It is assumed + // that we will never accelerate more than 20 m/s for gravity, plus 10 m/s for + // jetting, and an equivalent 10 m/s for jumping. We also assume that the + // working list is updated on a Tick basis, which means we only expand our + // box by the possible movement in that tick. + Point3F scaledVelocity = mVelocity * TickSec; + F32 len = scaledVelocity.len(); + F32 newLen = len + (10.0f * TickSec); + + // Check to see if it is actually necessary to construct the new working list, + // or if we can use the cached version from the last query. We use the x + // component of the min member of the mWorkingQueryBox, which is lame, but + // it works ok. + bool updateSet = false; + + Box3F convexBox = mConvex.getBoundingBox(getTransform(), getScale()); + F32 l = (newLen * 1.1f) + 0.1f; // from Convex::updateWorkingList + const Point3F lPoint( l, l, l ); + convexBox.minExtents -= lPoint; + convexBox.maxExtents += lPoint; + + // Check containment + if (mWorkingQueryBox.minExtents.x != -1e9f) + { + if (mWorkingQueryBox.isContained(convexBox) == false) + // Needed region is outside the cached region. Update it. + updateSet = true; + } + else + { + // Must update + updateSet = true; + } + // Actually perform the query, if necessary + if (updateSet == true) { + const Point3F twolPoint( 2.0f * l, 2.0f * l, 2.0f * l ); + mWorkingQueryBox = convexBox; + mWorkingQueryBox.minExtents -= twolPoint; + mWorkingQueryBox.maxExtents += twolPoint; + + disableCollision(); + mConvex.updateWorkingList(mWorkingQueryBox, + isGhost() ? sClientCollisionContactMask : sServerCollisionContactMask); + enableCollision(); + } +} + + +//---------------------------------------------------------------------------- + +void Player::writePacketData(GameConnection *connection, BitStream *stream) +{ + Parent::writePacketData(connection, stream); + + stream->writeInt(mState,NumStateBits); + if (stream->writeFlag(mState == RecoverState)) + stream->writeInt(mRecoverTicks,PlayerData::RecoverDelayBits); + if (stream->writeFlag(mJumpDelay > 0)) + stream->writeInt(mJumpDelay,PlayerData::JumpDelayBits); + + Point3F pos; + getTransform().getColumn(3,&pos); + if (stream->writeFlag(!isMounted())) { + // Will get position from mount + stream->setCompressionPoint(pos); + stream->write(pos.x); + stream->write(pos.y); + stream->write(pos.z); + stream->write(mVelocity.x); + stream->write(mVelocity.y); + stream->write(mVelocity.z); + stream->writeInt(mJumpSurfaceLastContact > 15 ? 15 : mJumpSurfaceLastContact, 4); + + if (stream->writeFlag(!mAllowSprinting || !mAllowCrouching || !mAllowProne || !mAllowJumping || !mAllowJetJumping || !mAllowSwimming)) + { + stream->writeFlag(mAllowJumping); + stream->writeFlag(mAllowJetJumping); + stream->writeFlag(mAllowSprinting); + stream->writeFlag(mAllowCrouching); + stream->writeFlag(mAllowProne); + stream->writeFlag(mAllowSwimming); + } + } + stream->write(mHead.x); + stream->write(mHead.z); + stream->write(mRot.z); + + if (mControlObject) { + S32 gIndex = connection->getGhostIndex(mControlObject); + if (stream->writeFlag(gIndex != -1)) { + stream->writeInt(gIndex,NetConnection::GhostIdBitSize); + mControlObject->writePacketData(connection, stream); + } + } + else + stream->writeFlag(false); +} + + +void Player::readPacketData(GameConnection *connection, BitStream *stream) +{ + Parent::readPacketData(connection, stream); + + mState = (ActionState)stream->readInt(NumStateBits); + if (stream->readFlag()) + mRecoverTicks = stream->readInt(PlayerData::RecoverDelayBits); + if (stream->readFlag()) + mJumpDelay = stream->readInt(PlayerData::JumpDelayBits); + else + mJumpDelay = 0; + + Point3F pos,rot; + if (stream->readFlag()) { + // Only written if we are not mounted + stream->read(&pos.x); + stream->read(&pos.y); + stream->read(&pos.z); + stream->read(&mVelocity.x); + stream->read(&mVelocity.y); + stream->read(&mVelocity.z); + stream->setCompressionPoint(pos); + delta.pos = pos; + mJumpSurfaceLastContact = stream->readInt(4); + + if (stream->readFlag()) + { + mAllowJumping = stream->readFlag(); + mAllowJetJumping = stream->readFlag(); + mAllowSprinting = stream->readFlag(); + mAllowCrouching = stream->readFlag(); + mAllowProne = stream->readFlag(); + mAllowSwimming = stream->readFlag(); + } + else + { + mAllowJumping = true; + mAllowJetJumping = true; + mAllowSprinting = true; + mAllowCrouching = true; + mAllowProne = true; + mAllowSwimming = true; + } + } + else + pos = delta.pos; + stream->read(&mHead.x); + stream->read(&mHead.z); + stream->read(&rot.z); + rot.x = rot.y = 0; + setPosition(pos,rot); + delta.head = mHead; + delta.rot = rot; + + if (stream->readFlag()) { + S32 gIndex = stream->readInt(NetConnection::GhostIdBitSize); + ShapeBase* obj = static_cast(connection->resolveGhost(gIndex)); + setControlObject(obj); + obj->readPacketData(connection, stream); + } + else + setControlObject(0); +} + +U32 Player::packUpdate(NetConnection *con, U32 mask, BitStream *stream) +{ + U32 retMask = Parent::packUpdate(con, mask, stream); + + if (stream->writeFlag((mask & ImpactMask) && !(mask & InitialUpdateMask))) + stream->writeInt(mImpactSound, PlayerData::ImpactBits); + + if (stream->writeFlag(mask & ActionMask && + mActionAnimation.action != PlayerData::NullAnimation && + mActionAnimation.action >= PlayerData::NumTableActionAnims)) { + stream->writeInt(mActionAnimation.action,PlayerData::ActionAnimBits); + stream->writeFlag(mActionAnimation.holdAtEnd); + stream->writeFlag(mActionAnimation.atEnd); + stream->writeFlag(mActionAnimation.firstPerson); + if (!mActionAnimation.atEnd) { + // If somewhere in middle on initial update, must send position- + F32 where = mShapeInstance->getPos(mActionAnimation.thread); + if (stream->writeFlag((mask & InitialUpdateMask) != 0 && where > 0)) + stream->writeSignedFloat(where, 6); + } + } + + if (stream->writeFlag(mask & ActionMask && + mArmAnimation.action != PlayerData::NullAnimation && + (!(mask & InitialUpdateMask) || + mArmAnimation.action != mDataBlock->lookAction))) { + stream->writeInt(mArmAnimation.action,PlayerData::ActionAnimBits); + } + + // The rest of the data is part of the control object packet update. + // If we're controlled by this client, we don't need to send it. + // we only need to send it if this is the initial update - in that case, + // the client won't know this is the control object yet. + if(stream->writeFlag(getControllingClient() == con && !(mask & InitialUpdateMask))) + return(retMask); + + if (stream->writeFlag(mask & MoveMask)) + { + stream->writeFlag(mFalling); + + stream->writeInt(mState,NumStateBits); + if (stream->writeFlag(mState == RecoverState)) + stream->writeInt(mRecoverTicks,PlayerData::RecoverDelayBits); + + Point3F pos; + getTransform().getColumn(3,&pos); + stream->writeCompressedPoint(pos); + F32 len = mVelocity.len(); + if(stream->writeFlag(len > 0.02f)) + { + Point3F outVel = mVelocity; + outVel *= 1.0f/len; + stream->writeNormalVector(outVel, 10); + len *= 32.0f; // 5 bits of fraction + if(len > 8191) + len = 8191; + stream->writeInt((S32)len, 13); + } + stream->writeFloat(mRot.z / M_2PI_F, 7); + stream->writeSignedFloat(mHead.x / mDataBlock->maxLookAngle, 6); + stream->writeSignedFloat(mHead.z / mDataBlock->maxFreelookAngle, 6); + delta.move.pack(stream); + stream->writeFlag(!(mask & NoWarpMask)); + } + // Ghost need energy to predict reliably + stream->writeFloat(getEnergyLevel() / mDataBlock->maxEnergy,EnergyLevelBits); + return retMask; +} + +void Player::unpackUpdate(NetConnection *con, BitStream *stream) +{ + Parent::unpackUpdate(con,stream); + + if (stream->readFlag()) + mImpactSound = stream->readInt(PlayerData::ImpactBits); + + // Server specified action animation + if (stream->readFlag()) { + U32 action = stream->readInt(PlayerData::ActionAnimBits); + bool hold = stream->readFlag(); + bool atEnd = stream->readFlag(); + bool fsp = stream->readFlag(); + + F32 animPos = -1.0f; + if (!atEnd && stream->readFlag()) + animPos = stream->readSignedFloat(6); + + if (isProperlyAdded()) { + setActionThread(action,true,hold,true,fsp); + bool inDeath = inDeathAnim(); + if (atEnd) + { + mShapeInstance->clearTransition(mActionAnimation.thread); + mShapeInstance->setPos(mActionAnimation.thread, + mActionAnimation.forward? 1: 0); + if (inDeath) + mDeath.lastPos = 1.0f; + } + else if (animPos > 0) { + mShapeInstance->setPos(mActionAnimation.thread, animPos); + if (inDeath) + mDeath.lastPos = animPos; + } + + // mMountPending suppresses tickDelay countdown so players will sit until + // their mount, or another animation, comes through (or 13 seconds elapses). + mMountPending = (S32) (inSittingAnim() ? sMountPendingTickWait : 0); + } + else { + mActionAnimation.action = action; + mActionAnimation.holdAtEnd = hold; + mActionAnimation.atEnd = atEnd; + mActionAnimation.firstPerson = fsp; + } + } + + // Server specified arm animation + if (stream->readFlag()) { + U32 action = stream->readInt(PlayerData::ActionAnimBits); + if (isProperlyAdded()) + setArmThread(action); + else + mArmAnimation.action = action; + } + + // Done if controlled by client ( and not initial update ) + if(stream->readFlag()) + return; + + // MoveMask + if (stream->readFlag()) { + mPredictionCount = sMaxPredictionTicks; + mFalling = stream->readFlag(); + + ActionState actionState = (ActionState)stream->readInt(NumStateBits); + if (stream->readFlag()) { + mRecoverTicks = stream->readInt(PlayerData::RecoverDelayBits); + setState(actionState, mRecoverTicks); + } + else + setState(actionState); + + Point3F pos,rot; + stream->readCompressedPoint(&pos); + F32 speed = mVelocity.len(); + if(stream->readFlag()) + { + stream->readNormalVector(&mVelocity, 10); + mVelocity *= stream->readInt(13) / 32.0f; + } + else + { + mVelocity.set(0.0f, 0.0f, 0.0f); + } + + rot.y = rot.x = 0.0f; + rot.z = stream->readFloat(7) * M_2PI_F; + mHead.x = stream->readSignedFloat(6) * mDataBlock->maxLookAngle; + mHead.z = stream->readSignedFloat(6) * mDataBlock->maxFreelookAngle; + delta.move.unpack(stream); + + delta.head = mHead; + delta.headVec.set(0.0f, 0.0f, 0.0f); + + if (stream->readFlag() && isProperlyAdded()) + { + // Determine number of ticks to warp based on the average + // of the client and server velocities. + delta.warpOffset = pos - delta.pos; + F32 as = (speed + mVelocity.len()) * 0.5f * TickSec; + F32 dt = (as > 0.00001f) ? delta.warpOffset.len() / as: sMaxWarpTicks; + delta.warpTicks = (S32)((dt > sMinWarpTicks) ? getMax(mFloor(dt + 0.5f), 1.0f) : 0.0f); + + if (delta.warpTicks) + { + // Setup the warp to start on the next tick. + if (delta.warpTicks > sMaxWarpTicks) + delta.warpTicks = sMaxWarpTicks; + delta.warpOffset /= (F32)delta.warpTicks; + + delta.rotOffset = rot - delta.rot; + + // Ignore small rotation differences + if (mFabs(delta.rotOffset.z) < 0.001f) + delta.rotOffset.z = 0; + + // Wrap rotation to +/-PI + if(delta.rotOffset.z < - M_PI_F) + delta.rotOffset.z += M_2PI_F; + else if(delta.rotOffset.z > M_PI_F) + delta.rotOffset.z -= M_2PI_F; + + delta.rotOffset /= (F32)delta.warpTicks; + } + else + { + // Going to skip the warp, server and client are real close. + // Adjust the frame interpolation to move smoothly to the + // new position within the current tick. + Point3F cp = delta.pos + delta.posVec * delta.dt; + if (delta.dt == 0) + { + delta.posVec.set(0.0f, 0.0f, 0.0f); + delta.rotVec.set(0.0f, 0.0f, 0.0f); + } + else + { + F32 dti = 1.0f / delta.dt; + delta.posVec = (cp - pos) * dti; + delta.rotVec.z = mRot.z - rot.z; + + if(delta.rotVec.z > M_PI_F) + delta.rotVec.z -= M_2PI_F; + else if(delta.rotVec.z < -M_PI_F) + delta.rotVec.z += M_2PI_F; + + delta.rotVec.z *= dti; + } + delta.pos = pos; + delta.rot = rot; + setPosition(pos,rot); + } + } + else + { + // Set the player to the server position + delta.pos = pos; + delta.rot = rot; + delta.posVec.set(0.0f, 0.0f, 0.0f); + delta.rotVec.set(0.0f, 0.0f, 0.0f); + delta.warpTicks = 0; + delta.dt = 0.0f; + setPosition(pos,rot); + } + } + F32 energy = stream->readFloat(EnergyLevelBits) * mDataBlock->maxEnergy; + setEnergyLevel(energy); +} + + +//---------------------------------------------------------------------------- +DefineEngineMethod( Player, getPose, const char*, (),, + "@brief Get the name of the player's current pose.\n\n" + + "The pose is one of the following:\n\n
        " + "
      • Stand - Standard movement pose.
      • " + "
      • Sprint - Sprinting pose.
      • " + "
      • Crouch - Crouch pose.
      • " + "
      • Prone - Prone pose.
      • " + "
      • Swim - Swimming pose.
      \n" + + "@return The current pose; one of: \"Stand\", \"Sprint\", \"Crouch\", \"Prone\", \"Swim\"\n" ) +{ + return object->getPoseName(); +} + +DefineEngineMethod( Player, allowAllPoses, void, (),, + "@brief Allow all poses a chance to occur.\n\n" + "This method resets any poses that have manually been blocked from occuring. " + "This includes the regular pose states such as sprinting, crouch, being prone " + "and swimming. It also includes being able to jump and jet jump. While this " + "is allowing these poses to occur it doesn't mean that they all can due to other " + "conditions. We're just not manually blocking them from being allowed.\n" + "@see allowJumping()\n" + "@see allowJetJumping()\n" + "@see allowSprinting()\n" + "@see allowCrouching()\n" + "@see allowProne()\n" + "@see allowSwimming()\n" ) +{ + object->allowAllPoses(); +} + +DefineEngineMethod( Player, allowJumping, void, (bool state),, + "@brief Set if the Player is allowed to jump.\n\n" + "The default is to allow jumping unless there are other environmental concerns " + "that prevent it. This method is mainly used to explicitly disallow jumping " + "at any time.\n" + "@param state Set to true to allow jumping, false to disable it.\n" + "@see allowAllPoses()\n" ) +{ + object->allowJumping(state); +} + +DefineEngineMethod( Player, allowJetJumping, void, (bool state),, + "@brief Set if the Player is allowed to jet jump.\n\n" + "The default is to allow jet jumping unless there are other environmental concerns " + "that prevent it. This method is mainly used to explicitly disallow jet jumping " + "at any time.\n" + "@param state Set to true to allow jet jumping, false to disable it.\n" + "@see allowAllPoses()\n" ) +{ + object->allowJetJumping(state); +} + +DefineEngineMethod( Player, allowSprinting, void, (bool state),, + "@brief Set if the Player is allowed to sprint.\n\n" + "The default is to allow sprinting unless there are other environmental concerns " + "that prevent it. This method is mainly used to explicitly disallow sprinting " + "at any time.\n" + "@param state Set to true to allow sprinting, false to disable it.\n" + "@see allowAllPoses()\n" ) +{ + object->allowSprinting(state); +} + +DefineEngineMethod( Player, allowCrouching, void, (bool state),, + "@brief Set if the Player is allowed to crouch.\n\n" + "The default is to allow crouching unless there are other environmental concerns " + "that prevent it. This method is mainly used to explicitly disallow crouching " + "at any time.\n" + "@param state Set to true to allow crouching, false to disable it.\n" + "@see allowAllPoses()\n" ) +{ + object->allowCrouching(state); +} + +DefineEngineMethod( Player, allowProne, void, (bool state),, + "@brief Set if the Player is allowed to go prone.\n\n" + "The default is to allow being prone unless there are other environmental concerns " + "that prevent it. This method is mainly used to explicitly disallow going prone " + "at any time.\n" + "@param state Set to true to allow being prone, false to disable it.\n" + "@see allowAllPoses()\n" ) +{ + object->allowProne(state); +} + +DefineEngineMethod( Player, allowSwimming, void, (bool state),, + "@brief Set if the Player is allowed to swim.\n\n" + "The default is to allow swimming unless there are other environmental concerns " + "that prevent it. This method is mainly used to explicitly disallow swimming " + "at any time.\n" + "@param state Set to true to allow swimming, false to disable it.\n" + "@see allowAllPoses()\n" ) +{ + object->allowSwimming(state); +} + +//---------------------------------------------------------------------------- + +DefineEngineMethod( Player, getState, const char*, (),, + "@brief Get the name of the player's current state.\n\n" + + "The state is one of the following:\n\n
        " + "
      • Dead - The Player is dead.
      • " + "
      • Mounted - The Player is mounted to an object such as a vehicle.
      • " + "
      • Move - The Player is free to move. The usual state.
      • " + "
      • Recover - The Player is recovering from a fall. See PlayerData::recoverDelay.
      \n" + + "@return The current state; one of: \"Dead\", \"Mounted\", \"Move\", \"Recover\"\n" ) +{ + return object->getStateName(); +} + +DefineEngineMethod( Player, getDamageLocation, const char*, ( Point3F pos ),, + "@brief Get the named damage location and modifier for a given world position.\n\n" + + "the Player object can simulate different hit locations based on a pre-defined set " + "of PlayerData defined percentages. These hit percentages divide up the Player's " + "bounding box into different regions. The diagram below demonstrates how the various " + "PlayerData properties split up the bounding volume:\n\n" + + "\n\n" + + "While you may pass in any world position and getDamageLocation() will provide a best-fit " + "location, you should be aware that this can produce some interesting results. For example, " + "any position that is above PlayerData::boxHeadPercentage will be considered a 'head' hit, even " + "if the world position is high in the sky. Therefore it may be wise to keep the passed in point " + "to somewhere on the surface of, or within, the Player's bounding volume.\n\n" + + "@note This method will not return an accurate location when the player is " + "prone or swimming.\n\n" + + "@param pos A world position for which to retrieve a body region on this player.\n" + + "@return a string containing two words (space separated strings), where the " + "first is a location and the second is a modifier.\n\n" + + "Posible locations:
        " + "
      • head
      • " + "
      • torso
      • " + "
      • legs
      \n" + + "Head modifiers:
        " + "
      • left_back
      • " + "
      • middle_back
      • " + "
      • right_back
      • " + "
      • left_middle
      • " + "
      • middle_middle
      • " + "
      • right_middle
      • " + "
      • left_front
      • " + "
      • middle_front
      • " + "
      • right_front
      \n" + + "Legs/Torso modifiers:
        " + "
      • front_left
      • " + "
      • front_right
      • " + "
      • back_left
      • " + "
      • back_right
      \n" + + "@see PlayerData::boxHeadPercentage\n" + "@see PlayerData::boxHeadFrontPercentage\n" + "@see PlayerData::boxHeadBackPercentage\n" + "@see PlayerData::boxHeadLeftPercentage\n" + "@see PlayerData::boxHeadRightPercentage\n" + "@see PlayerData::boxTorsoPercentage\n" + ) +{ + const char *buffer1; + const char *buffer2; + + object->getDamageLocation(pos, buffer1, buffer2); + + char *buff = Con::getReturnBuffer(128); + dSprintf(buff, 128, "%s %s", buffer1, buffer2); + return buff; +} + +DefineEngineMethod( Player, setArmThread, bool, ( const char* name ),, + "@brief Set the sequence that controls the player's arms (dynamically adjusted " + "to match look direction).\n\n" + "@param name Name of the sequence to play on the player's arms.\n" + "@return true if successful, false if failed.\n" + "@note By default the 'look' sequence is used, if available.\n") +{ + return object->setArmThread( name ); +} + +DefineEngineMethod( Player, setActionThread, bool, ( const char* name, bool hold, bool fsp ), ( false, true ), + "@brief Set the main action sequence to play for this player.\n\n" + "@param name Name of the action sequence to set\n" + "@param hold Set to false to get a callback on the datablock when the sequence ends (PlayerData::animationDone()). " + "When set to true no callback is made.\n" + "@param fsp True if first person and none of the spine nodes in the shape should animate. False will allow the shape's " + "spine nodes to animate.\n" + "@return True if succesful, false if failed\n" + + "@note The spine nodes for the Player's shape are named as follows:\n\n
        " + "
      • Bip01 Pelvis
      • " + "
      • Bip01 Spine
      • " + "
      • Bip01 Spine1
      • " + "
      • Bip01 Spine2
      • " + "
      • Bip01 Neck
      • " + "
      • Bip01 Head
      \n\n" + + "You cannot use setActionThread() to have the Player play one of the motion " + "determined action animation sequences. These sequences are chosen based on how " + "the Player moves and the Player's current pose. The names of these sequences are:\n\n
        " + "
      • root
      • " + "
      • run
      • " + "
      • side
      • " + "
      • side_right
      • " + "
      • crouch_root
      • " + "
      • crouch_forward
      • " + "
      • crouch_backward
      • " + "
      • crouch_side
      • " + "
      • crouch_right
      • " + "
      • prone_root
      • " + "
      • prone_forward
      • " + "
      • prone_backward
      • " + "
      • swim_root
      • " + "
      • swim_forward
      • " + "
      • swim_backward
      • " + "
      • swim_left
      • " + "
      • swim_right
      • " + "
      • fall
      • " + "
      • jump
      • " + "
      • standjump
      • " + "
      • land
      • " + "
      • jet
      \n\n" + + "If the player moves in any direction then the animation sequence set using this " + "method will be cancelled and the chosen mation-based sequence will take over. This makes " + "great for times when the Player cannot move, such as when mounted, or when it doesn't matter " + "if the action sequence changes, such as waving and saluting.\n" + + "@tsexample\n" + "// Place the player in a sitting position after being mounted\n" + "%player.setActionThread( \"sitting\", true, true );\n" + "@endtsexample\n") +{ + return object->setActionThread( name, hold, true, fsp); +} + +DefineEngineMethod( Player, setControlObject, bool, ( ShapeBase* obj ),, + "@brief Set the object to be controlled by this player\n\n" + + "It is possible to have the moves sent to the Player object from the " + "GameConnection to be passed along to another object. This happens, for example " + "when a player is mounted to a vehicle. The move commands pass through the Player " + "and on to the vehicle (while the player remains stationary within the vehicle). " + "With setControlObject() you can have the Player pass along its moves to any object. " + "One possible use is for a player to move a remote controlled vehicle. In this case " + "the player does not mount the vehicle directly, but still wants to be able to control it.\n" + + "@param obj Object to control with this player\n" + "@return True if the object is valid, false if not\n" + + "@see getControlObject()\n" + "@see clearControlObject()\n" + "@see GameConnection::setControlObject()") +{ + if (obj) { + object->setControlObject(obj); + return true; + } + else + object->setControlObject(0); + return false; +} + +DefineEngineMethod( Player, getControlObject, S32, (),, + "@brief Get the current object we are controlling.\n\n" + "@return ID of the ShapeBase object we control, or 0 if not controlling an " + "object.\n" + "@see setControlObject()\n" + "@see clearControlObject()") +{ + ShapeBase* controlObject = object->getControlObject(); + return controlObject ? controlObject->getId(): 0; +} + +DefineEngineMethod( Player, clearControlObject, void, (),, + "@brief Clears the player's current control object.\n\n" + "Returns control to the player. This internally calls " + "Player::setControlObject(0).\n" + "@tsexample\n" + "%player.clearControlObject();\n" + "echo(%player.getControlObject()); //<-- Returns 0, player assumes control\n" + "%player.setControlObject(%vehicle);\n" + "echo(%player.getControlObject()); //<-- Returns %vehicle, player controls the vehicle now.\n" + "@endtsexample\n" + "@note If the player does not have a control object, the player will receive all moves " + "from its GameConnection. If you're looking to remove control from the player itself " + "(i.e. stop sending moves to the player) use GameConnection::setControlObject() to transfer " + "control to another object, such as a camera.\n" + "@see setControlObject()\n" + "@see getControlObject()\n" + "@see GameConnection::setControlObject()\n") +{ + object->setControlObject(0); +} + +DefineEngineMethod( Player, checkDismountPoint, bool, ( Point3F oldPos, Point3F pos ),, + "@brief Check if it is safe to dismount at this position.\n\n" + + "Internally this method casts a ray from oldPos to pos to determine if it hits the " + "terrain, an interior object, a water object, another player, a static shape, " + "a vehicle (exluding the one currently mounted), or physical zone. If this ray " + "is in the clear, then the player's bounding box is also checked for a collision at " + "the pos position. If this displaced bounding box is also in the clear, then " + "checkDismountPoint() returns true.\n" + + "@param oldPos The player's current position\n" + "@param pos The dismount position to check\n" + "@return True if the dismount position is clear, false if not\n" + + "@note The player must be already mounted for this method to not assert.\n") +{ + MatrixF oldPosMat(true); + oldPosMat.setColumn(3, oldPos); + MatrixF posMat(true); + posMat.setColumn(3, pos); + return object->checkDismountPosition(oldPosMat, posMat); +} + +DefineEngineMethod( Player, getNumDeathAnimations, S32, ( ),, + "@brief Get the number of death animations available to this player.\n\n" + "Death animations are assumed to be named death1-N using consecutive indices." ) +{ + S32 count = 0; + const PlayerData * db = dynamic_cast( object->getDataBlock() ); + if ( db ) + { + + for ( S32 i = 0; i < db->actionCount; i++ ) + if ( db->actionList[i].death ) + count++; + } + return count; +} + +//---------------------------------------------------------------------------- +void Player::consoleInit() +{ + Con::addVariable("$player::renderMyPlayer",TypeBool, &sRenderMyPlayer, + "@brief Determines if the player is rendered or not.\n\n" + "Used on the client side to disable the rendering of all Player objects. This is " + "mainly for the tools or debugging.\n" + "@ingroup GameObjects\n"); + Con::addVariable("$player::renderMyItems",TypeBool, &sRenderMyItems, + "@brief Determines if mounted shapes are rendered or not.\n\n" + "Used on the client side to disable the rendering of all Player mounted objects. This is " + "mainly used for the tools or debugging.\n" + "@ingroup GameObjects\n"); + Con::addVariable("$player::renderCollision", TypeBool, &sRenderPlayerCollision, + "@brief Determines if the player's collision mesh should be rendered.\n\n" + "This is mainly used for the tools and debugging.\n" + "@ingroup GameObjects\n"); + + Con::addVariable("$player::minWarpTicks",TypeF32,&sMinWarpTicks, + "@brief Fraction of tick at which instant warp occures on the client.\n\n" + "@ingroup GameObjects\n"); + Con::addVariable("$player::maxWarpTicks",TypeS32,&sMaxWarpTicks, + "@brief When a warp needs to occur due to the client being too far off from the server, this is the " + "maximum number of ticks we'll allow the client to warp to catch up.\n\n" + "@ingroup GameObjects\n"); + Con::addVariable("$player::maxPredictionTicks",TypeS32,&sMaxPredictionTicks, + "@brief Maximum number of ticks to predict on the client from the last known move obtained from the server.\n\n" + "@ingroup GameObjects\n"); + + Con::addVariable("$player::maxImpulseVelocity", TypeF32, &sMaxImpulseVelocity, + "@brief The maximum velocity allowed due to a single impulse.\n\n" + "@ingroup GameObjects\n"); + + // Move triggers + Con::addVariable("$player::jumpTrigger", TypeS32, &sJumpTrigger, + "@brief The move trigger index used for player jumping.\n\n" + "@ingroup GameObjects\n"); + Con::addVariable("$player::crouchTrigger", TypeS32, &sCrouchTrigger, + "@brief The move trigger index used for player crouching.\n\n" + "@ingroup GameObjects\n"); + Con::addVariable("$player::proneTrigger", TypeS32, &sProneTrigger, + "@brief The move trigger index used for player prone pose.\n\n" + "@ingroup GameObjects\n"); + Con::addVariable("$player::sprintTrigger", TypeS32, &sSprintTrigger, + "@brief The move trigger index used for player sprinting.\n\n" + "@ingroup GameObjects\n"); + Con::addVariable("$player::imageTrigger0", TypeS32, &sImageTrigger0, + "@brief The move trigger index used to trigger mounted image 0.\n\n" + "@ingroup GameObjects\n"); + Con::addVariable("$player::imageTrigger1", TypeS32, &sImageTrigger1, + "@brief The move trigger index used to trigger mounted image 1 or alternate fire " + "on mounted image 0.\n\n" + "@ingroup GameObjects\n"); + Con::addVariable("$player::jumpJetTrigger", TypeS32, &sJumpJetTrigger, + "@brief The move trigger index used for player jump jetting.\n\n" + "@ingroup GameObjects\n"); + Con::addVariable("$player::vehicleDismountTrigger", TypeS32, &sVehicleDismountTrigger, + "@brief The move trigger index used to dismount player.\n\n" + "@ingroup GameObjects\n"); +} + +//-------------------------------------------------------------------------- +void Player::calcClassRenderData() +{ + Parent::calcClassRenderData(); + + // If nothing is mounted do not perform the calculations below. Otherwise, + // we'll end up with a bad ray cast as both nmat and smat will be the + // Player's transform. + MountedImage& image = mMountedImageList[0]; + if (!image.dataBlock) + { + mWeaponBackFraction = 0.0f; + return; + } + + disableCollision(); + MatrixF nmat; + MatrixF smat; + Parent::getRetractionTransform(0,&nmat); + Parent::getImageTransform(0, &smat); + + // See if we are pushed into a wall... + Point3F start, end; + smat.getColumn(3, &start); + nmat.getColumn(3, &end); + + RayInfo rinfo; + if (getContainer()->castRay(start, end, sCollisionMoveMask & ~(WaterObjectType|PhysicalZoneObjectType|MarkerObjectType), &rinfo)) { + if (rinfo.t < 1.0f) + mWeaponBackFraction = 1.0f - rinfo.t; + else + mWeaponBackFraction = 0.0f; + } else { + mWeaponBackFraction = 0.0f; + } + enableCollision(); +} + +//----------------------------------------------------------------------------- + +void Player::playFootstepSound( bool triggeredLeft, Material* contactMaterial, SceneObject* contactObject ) +{ + MatrixF footMat = getTransform(); + if( mWaterCoverage > 0.0 ) + { + // Treading water. + + if ( mWaterCoverage < mDataBlock->footSplashHeight ) + SFX->playOnce( mDataBlock->sound[ PlayerData::FootShallowSplash ], &footMat ); + else + { + if ( mWaterCoverage < 1.0 ) + SFX->playOnce( mDataBlock->sound[ PlayerData::FootWading ], &footMat ); + else + { + if ( triggeredLeft ) + { + SFX->playOnce( mDataBlock->sound[ PlayerData::FootUnderWater ], &footMat ); + SFX->playOnce( mDataBlock->sound[ PlayerData::FootBubbles ], &footMat ); + } + } + } + } + else if( contactMaterial && contactMaterial->mFootstepSoundCustom ) + { + // Footstep sound defined on material. + + SFX->playOnce( contactMaterial->mFootstepSoundCustom, &footMat ); + } + else + { + // Play default sound. + + S32 sound = -1; + if( contactMaterial && contactMaterial->mFootstepSoundId != -1 ) + sound = contactMaterial->mFootstepSoundId; + else if( contactObject && contactObject->getTypeMask() & VehicleObjectType ) + sound = 2; + + switch ( sound ) + { + case 0: // Soft + SFX->playOnce( mDataBlock->sound[PlayerData::FootSoft], &footMat ); + break; + case 1: // Hard + SFX->playOnce( mDataBlock->sound[PlayerData::FootHard], &footMat ); + break; + case 2: // Metal + SFX->playOnce( mDataBlock->sound[PlayerData::FootMetal], &footMat ); + break; + case 3: // Snow + SFX->playOnce( mDataBlock->sound[PlayerData::FootSnow], &footMat ); + break; + /* + default: //Hard + SFX->playOnce( mDataBlock->sound[PlayerData::FootHard], &footMat ); + break; + */ + } + } +} + +void Player:: playImpactSound() +{ + if( mWaterCoverage == 0.0f ) + { + Point3F pos; + RayInfo rInfo; + MatrixF mat = getTransform(); + mat.mulP(Point3F(mDataBlock->decalOffset,0.0f,0.0f), &pos); + + if( gClientContainer.castRay( Point3F( pos.x, pos.y, pos.z + 0.01f ), + Point3F( pos.x, pos.y, pos.z - 2.0f ), + STATIC_COLLISION_TYPEMASK | VehicleObjectType, + &rInfo ) ) + { + Material* material = ( rInfo.material ? dynamic_cast< Material* >( rInfo.material->getMaterial() ) : 0 ); + + if( material && material->mImpactSoundCustom ) + SFX->playOnce( material->mImpactSoundCustom, &getTransform() ); + else + { + S32 sound = -1; + if( material && material->mImpactSoundId ) + sound = material->mImpactSoundId; + else if( rInfo.object->getTypeMask() & VehicleObjectType ) + sound = 2; // Play metal; + + switch( sound ) + { + case 0: + //Soft + SFX->playOnce( mDataBlock->sound[ PlayerData::ImpactSoft ], &getTransform() ); + break; + case 1: + //Hard + SFX->playOnce( mDataBlock->sound[ PlayerData::ImpactHard ], &getTransform() ); + break; + case 2: + //Metal + SFX->playOnce( mDataBlock->sound[ PlayerData::ImpactMetal ], &getTransform() ); + break; + case 3: + //Snow + SFX->playOnce( mDataBlock->sound[ PlayerData::ImpactSnow ], &getTransform() ); + break; + /* + default: + //Hard + alxPlay(mDataBlock->sound[PlayerData::ImpactHard], &getTransform()); + break; + */ + } + } + } + } + + mImpactSound = 0; +} + +//-------------------------------------------------------------------------- +// Update splash +//-------------------------------------------------------------------------- + +void Player::updateSplash() +{ + F32 speed = getVelocity().len(); + if( speed < mDataBlock->splashVelocity || isMounted() ) return; + + Point3F curPos = getPosition(); + + if ( curPos.equal( mLastPos ) ) + return; + + if (pointInWater( curPos )) { + if (!pointInWater( mLastPos )) { + Point3F norm = getVelocity(); + norm.normalize(); + + // make sure player is moving vertically at good pace before playing splash + F32 splashAng = mDataBlock->splashAngle / 360.0; + if( mDot( norm, Point3F(0.0, 0.0, -1.0) ) < splashAng ) + return; + + + RayInfo rInfo; + if (gClientContainer.castRay(mLastPos, curPos, + WaterObjectType, &rInfo)) { + createSplash( rInfo.point, speed ); + mBubbleEmitterTime = 0.0; + } + + } + } +} + + +//-------------------------------------------------------------------------- + +void Player::updateFroth( F32 dt ) +{ + // update bubbles + Point3F moveDir = getVelocity(); + mBubbleEmitterTime += dt; + + if (mBubbleEmitterTime < mDataBlock->bubbleEmitTime) { + if (mSplashEmitter[PlayerData::BUBBLE_EMITTER]) { + Point3F emissionPoint = getRenderPosition(); + U32 emitNum = PlayerData::BUBBLE_EMITTER; + mSplashEmitter[emitNum]->emitParticles(mLastPos, emissionPoint, + Point3F( 0.0, 0.0, 1.0 ), moveDir, (U32)(dt * 1000.0)); + } + } + + Point3F contactPoint; + if (!collidingWithWater(contactPoint)) { + mLastWaterPos = mLastPos; + return; + } + + F32 speed = moveDir.len(); + if ( speed < mDataBlock->splashVelEpsilon ) + speed = 0.0; + + U32 emitRate = (U32) (speed * mDataBlock->splashFreqMod * dt); + + // If we're in the water, swimming, but not + // moving, then lets emit some particles because + // we're treading water. + if ( mSwimming && speed == 0.0 ) + { + emitRate = (U32) (2.0f * mDataBlock->splashFreqMod * dt); + } + + U32 i; + for ( i=0; iemitParticles( mLastWaterPos, + contactPoint, Point3F( 0.0, 0.0, 1.0 ), + moveDir, emitRate ); + } + mLastWaterPos = contactPoint; +} + +void Player::updateWaterSounds(F32 dt) +{ + if ( mWaterCoverage < 1.0f || mDamageState != Enabled ) + { + // Stop everything + if ( mMoveBubbleSound ) + mMoveBubbleSound->stop(); + if ( mWaterBreathSound ) + mWaterBreathSound->stop(); + return; + } + + if ( mMoveBubbleSound ) + { + // We're under water and still alive, so let's play something + if ( mVelocity.len() > 1.0f ) + { + if ( !mMoveBubbleSound->isPlaying() ) + mMoveBubbleSound->play(); + + mMoveBubbleSound->setTransform( getTransform() ); + } + else + mMoveBubbleSound->stop(); + } + + if ( mWaterBreathSound ) + { + if ( !mWaterBreathSound->isPlaying() ) + mWaterBreathSound->play(); + + mWaterBreathSound->setTransform( getTransform() ); + } +} + + +//-------------------------------------------------------------------------- +// Returns true if player is intersecting a water surface +//-------------------------------------------------------------------------- +bool Player::collidingWithWater( Point3F &waterHeight ) +{ + if ( !mCurrentWaterObject ) + return false; + + Point3F curPos = getPosition(); + + if ( mWorldBox.maxExtents.z < mLiquidHeight ) + return false; + + curPos.z = mLiquidHeight; + + waterHeight = getPosition(); + waterHeight.z = mLiquidHeight; + + return true; +} + +//-------------------------------------------------------------------------- + +void Player::createSplash( Point3F &pos, F32 speed ) +{ + if ( speed >= mDataBlock->hardSplashSoundVel ) + SFX->playOnce( mDataBlock->sound[PlayerData::ImpactWaterHard], &getTransform() ); + else if ( speed >= mDataBlock->medSplashSoundVel ) + SFX->playOnce( mDataBlock->sound[PlayerData::ImpactWaterMedium], &getTransform() ); + else + SFX->playOnce( mDataBlock->sound[PlayerData::ImpactWaterEasy], &getTransform() ); + + if( mDataBlock->splash ) + { + MatrixF trans = getTransform(); + trans.setPosition( pos ); + Splash *splash = new Splash; + splash->onNewDataBlock( mDataBlock->splash, false ); + splash->setTransform( trans ); + splash->setInitialState( trans.getPosition(), Point3F( 0.0, 0.0, 1.0 ) ); + if (!splash->registerObject()) + delete splash; + } +} + + +bool Player::isControlObject() +{ + GameConnection* connection = GameConnection::getConnectionToServer(); + if( !connection ) return false; + ShapeBase *obj = dynamic_cast(connection->getControlObject()); + return ( obj == this ); +} + + +void Player::prepRenderImage( SceneRenderState* state ) +{ + bool renderPlayer = true; + bool renderItems = true; + + /* + if ( mPhysicsRep && Con::getBoolVariable("$PhysicsPlayer::DebugRender",false) ) + { + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( mPhysicsRep, &PhysicsPlayer::renderDebug ); + ri->objectIndex = -1; + ri->type = RenderPassManager::RIT_Editor; + state->getRenderPass()->addInst( ri ); + } + */ + + // Debug rendering for all convexes in the Players working list. + if ( sRenderPlayerCollision ) + { + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &Player::renderConvex ); + ri->objectIndex = -1; + ri->type = RenderPassManager::RIT_Editor; + state->getRenderPass()->addInst( ri ); + } + + GameConnection* connection = GameConnection::getConnectionToServer(); + if( connection && connection->getControlObject() == this && connection->isFirstPerson() ) + { + renderPlayer = mDataBlock->renderFirstPerson || !state->isDiffusePass(); + + if( !sRenderMyPlayer ) + renderPlayer = false; + if( !sRenderMyItems ) + renderItems = false; + } + + // Call the protected base class to do the work + // now that we know if we're rendering the player + // and mounted shapes. + return ShapeBase::_prepRenderImage( state, + renderPlayer, + renderItems ); +} + +void Player::renderConvex( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ) +{ + GFX->enterDebugEvent( ColorI(255,0,255), "Player_renderConvex" ); + mConvex.renderWorkingList(); + GFX->leaveDebugEvent(); +} diff --git a/Engine/source/T3D/player.h b/Engine/source/T3D/player.h new file mode 100644 index 000000000..10046c4c6 --- /dev/null +++ b/Engine/source/T3D/player.h @@ -0,0 +1,776 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PLAYER_H_ +#define _PLAYER_H_ + +#ifndef _SHAPEBASE_H_ +#include "T3D/shapeBase.h" +#endif +#ifndef _BOXCONVEX_H_ +#include "collision/boxConvex.h" +#endif + +#include "T3D/gameBase/gameProcess.h" + +class Material; +class ParticleEmitter; +class ParticleEmitterData; +class DecalData; +class SplashData; +class PhysicsPlayer; +class Player; + +//---------------------------------------------------------------------------- + +struct PlayerData: public ShapeBaseData { + typedef ShapeBaseData Parent; + enum Constants { + RecoverDelayBits = 7, + JumpDelayBits = 7, + NumSpineNodes = 6, + ImpactBits = 3, + NUM_SPLASH_EMITTERS = 3, + BUBBLE_EMITTER = 2, + }; + bool renderFirstPerson; ///< Render the player shape in first person + + StringTableEntry imageAnimPrefix; ///< Passed along to mounted images to modify + /// animation sequences played in third person. [optional] + bool allowImageStateAnimation; ///< When true a new thread is added to the player to allow for + /// mounted images to request a sequence be played on the player + /// through the image's state machine. It is only optional so + /// that we don't create a TSThread on the player if we don't + /// need to. + + StringTableEntry shapeNameFP[ShapeBase::MaxMountedImages]; ///< Used to render with mounted images in first person [optional] + StringTableEntry imageAnimPrefixFP; ///< Passed along to mounted images to modify + /// animation sequences played in first person. [optional] + Resource mShapeFP[ShapeBase::MaxMountedImages]; ///< First person mounted image shape resources [optional] + U32 mCRCFP[ShapeBase::MaxMountedImages]; ///< Computed CRC values for the first person mounted image shapes + /// Depends on the ShapeBaseData computeCRC field. + bool mValidShapeFP[ShapeBase::MaxMountedImages]; ///< Indicates that there is a valid first person mounted image shape + + F32 pickupRadius; ///< Radius around player for items (on server) + F32 maxTimeScale; ///< Max timeScale for action animations + + F32 minLookAngle; ///< Lowest angle (radians) the player can look + F32 maxLookAngle; ///< Highest angle (radians) the player can look + F32 maxFreelookAngle; ///< Max left/right angle the player can look + + /// @name Physics constants + /// @{ + + F32 maxStepHeight; ///< Maximum height the player can step up + F32 runSurfaceAngle; ///< Maximum angle from vertical in degrees the player can run up + + F32 horizMaxSpeed; ///< Max speed attainable in the horizontal + F32 horizResistSpeed; ///< Speed at which resistance will take place + F32 horizResistFactor; ///< Factor of resistance once horizResistSpeed has been reached + + F32 upMaxSpeed; ///< Max vertical speed attainable + F32 upResistSpeed; ///< Speed at which resistance will take place + F32 upResistFactor; ///< Factor of resistance once upResistSpeed has been reached + + F32 fallingSpeedThreshold; ///< Downward speed at which we consider the player falling + + S32 recoverDelay; ///< # tick + F32 recoverRunForceScale; ///< RunForce multiplier in recover state + F32 landSequenceTime; ///< If greater than 0 then the legacy fall recovery system will be bypassed + /// in favour of just playing the player's land sequence. The time to + /// recover from a fall then becomes this parameter's time and the land + /// sequence's playback will be scaled to match. + bool transitionToLand; ///< When going from a fall to a land, should we transition between the two? + + // Running/Walking + F32 runForce; ///< Force used to accelerate player + F32 runEnergyDrain; ///< Energy drain/tick + F32 minRunEnergy; ///< Minimum energy required to run + F32 maxForwardSpeed; ///< Maximum forward speed when running + F32 maxBackwardSpeed; ///< Maximum backward speed when running + F32 maxSideSpeed; ///< Maximum side speed when running + + // Jumping + F32 jumpForce; ///< Force exerted per jump + F32 jumpEnergyDrain; ///< Energy drained per jump + F32 minJumpEnergy; ///< Minimum energy required to jump + F32 minJumpSpeed; ///< Minimum speed needed to jump + F32 maxJumpSpeed; ///< Maximum speed before the player can no longer jump + F32 jumpSurfaceAngle; ///< Angle from vertical in degrees where the player can jump + S32 jumpDelay; ///< Delay time in ticks between jumps + + // Sprinting + F32 sprintForce; ///< Force used to accelerate player + F32 sprintEnergyDrain; ///< Energy drain/tick + F32 minSprintEnergy; ///< Minimum energy required to sprint + F32 maxSprintForwardSpeed; ///< Maximum forward speed when sprinting + F32 maxSprintBackwardSpeed; ///< Maximum backward speed when sprinting + F32 maxSprintSideSpeed; ///< Maximum side speed when sprinting + F32 sprintStrafeScale; ///< Amount to scale strafing motion vector while sprinting + F32 sprintYawScale; ///< Amount to scale yaw motion while sprinting + F32 sprintPitchScale; ///< Amount to scale pitch motion while sprinting + bool sprintCanJump; ///< Can the player jump while sprinting + + // Swimming + F32 swimForce; ///< Force used to accelerate player while swimming + F32 maxUnderwaterForwardSpeed; ///< Maximum underwater forward speed when running + F32 maxUnderwaterBackwardSpeed; ///< Maximum underwater backward speed when running + F32 maxUnderwaterSideSpeed; ///< Maximum underwater side speed when running + + // Crouching + F32 crouchForce; ///< Force used to accelerate player while crouching + F32 maxCrouchForwardSpeed; ///< Maximum forward speed when crouching + F32 maxCrouchBackwardSpeed; ///< Maximum backward speed when crouching + F32 maxCrouchSideSpeed; ///< Maximum side speed when crouching + + // Prone + F32 proneForce; ///< Force used to accelerate player while prone + F32 maxProneForwardSpeed; ///< Maximum forward speed when prone + F32 maxProneBackwardSpeed; ///< Maximum backward speed when prone + F32 maxProneSideSpeed; ///< Maximum side speed when prone + + // Jetting + F32 jetJumpForce; + F32 jetJumpEnergyDrain; ///< Energy per jump + F32 jetMinJumpEnergy; + F32 jetMinJumpSpeed; + F32 jetMaxJumpSpeed; + F32 jetJumpSurfaceAngle; ///< Angle vertical degrees + /// @} + + /// @name Hitboxes + /// @{ + + F32 boxHeadPercentage; + F32 boxTorsoPercentage; + + F32 boxHeadLeftPercentage; + F32 boxHeadRightPercentage; + F32 boxHeadBackPercentage; + F32 boxHeadFrontPercentage; + /// @} + + F32 minImpactSpeed; ///< Minimum impact speed required to apply fall damage + F32 minLateralImpactSpeed; ///< Minimum impact speed required to apply non-falling damage. + + F32 decalOffset; + + F32 groundImpactMinSpeed; ///< Minimum impact speed required to apply fall damage with the ground + VectorF groundImpactShakeFreq; ///< Frequency in each direction for the camera to shake + VectorF groundImpactShakeAmp; ///< How much to shake + F32 groundImpactShakeDuration; ///< How long to shake + F32 groundImpactShakeFalloff; ///< How fast the shake disapates + + /// Zounds! + enum Sounds { + FootSoft, + FootHard, + FootMetal, + FootSnow, + FootShallowSplash, + FootWading, + FootUnderWater, + FootBubbles, + MoveBubbles, + WaterBreath, + ImpactSoft, + ImpactHard, + ImpactMetal, + ImpactSnow, + ImpactWaterEasy, + ImpactWaterMedium, + ImpactWaterHard, + ExitWater, + MaxSounds + }; + SFXTrack* sound[MaxSounds]; + + Point3F boxSize; ///< Width, depth, height + Point3F crouchBoxSize; + Point3F proneBoxSize; + Point3F swimBoxSize; + + /// Animation and other data initialized in onAdd + struct ActionAnimationDef { + const char* name; ///< Sequence name + struct Vector { + F32 x,y,z; + } dir; ///< Default direction + }; + struct ActionAnimation { + const char* name; ///< Sequence name + S32 sequence; ///< Sequence index + VectorF dir; ///< Dir of animation ground transform + F32 speed; ///< Speed in m/s + bool velocityScale; ///< Scale animation by velocity + bool death; ///< Are we dying? + }; + enum { + // *** WARNING *** + // These enum values are used to index the ActionAnimationList + // array instantiated in player.cc + // The first several are selected in the move state based on velocity + RootAnim, + RunForwardAnim, + BackBackwardAnim, + SideLeftAnim, + SideRightAnim, + + SprintRootAnim, + SprintForwardAnim, + SprintBackwardAnim, + SprintLeftAnim, + SprintRightAnim, + + CrouchRootAnim, + CrouchForwardAnim, + CrouchBackwardAnim, + CrouchLeftAnim, + CrouchRightAnim, + + ProneRootAnim, + ProneForwardAnim, + ProneBackwardAnim, + + SwimRootAnim, + SwimForwardAnim, + SwimBackwardAnim, + SwimLeftAnim, + SwimRightAnim, + + // These are set explicitly based on player actions + FallAnim, + JumpAnim, + StandJumpAnim, + LandAnim, + JetAnim, + + // + NumTableActionAnims = JetAnim + 1, + + NumExtraActionAnims = 512 - NumTableActionAnims, + NumActionAnims = NumTableActionAnims + NumExtraActionAnims, + ActionAnimBits = 9, + NullAnimation = (1 << ActionAnimBits) - 1 + }; + + static ActionAnimationDef ActionAnimationList[NumTableActionAnims]; + ActionAnimation actionList[NumActionAnims]; + U32 actionCount; + U32 lookAction; + S32 spineNode[NumSpineNodes]; + S32 pickupDelta; ///< Base off of pcikupRadius + F32 runSurfaceCos; ///< Angle from vertical in cos(runSurfaceAngle) + F32 jumpSurfaceCos; ///< Angle from vertical in cos(jumpSurfaceAngle) + + enum Impacts { + ImpactNone, + ImpactNormal, + }; + + enum Recoil { + LightRecoil, + MediumRecoil, + HeavyRecoil, + NumRecoilSequences + }; + S32 recoilSequence[NumRecoilSequences]; + + /// @name Particles + /// All of the data relating to environmental effects + /// @{ + + ParticleEmitterData * footPuffEmitter; + S32 footPuffID; + S32 footPuffNumParts; + F32 footPuffRadius; + + DecalData* decalData; + S32 decalID; + + ParticleEmitterData * dustEmitter; + S32 dustID; + + SplashData* splash; + S32 splashId; + F32 splashVelocity; + F32 splashAngle; + F32 splashFreqMod; + F32 splashVelEpsilon; + F32 bubbleEmitTime; + + F32 medSplashSoundVel; + F32 hardSplashSoundVel; + F32 exitSplashSoundVel; + F32 footSplashHeight; + + // Air control + F32 airControl; + + // Jump off surfaces at their normal rather than straight up + bool jumpTowardsNormal; + + // For use if/when mPhysicsPlayer is created + StringTableEntry physicsPlayerType; + + ParticleEmitterData* splashEmitterList[NUM_SPLASH_EMITTERS]; + S32 splashEmitterIDList[NUM_SPLASH_EMITTERS]; + /// @} + + // + DECLARE_CONOBJECT(PlayerData); + PlayerData(); + bool preload(bool server, String &errorStr); + void getGroundInfo(TSShapeInstance*,TSThread*,ActionAnimation*); + bool isTableSequence(S32 seq); + bool isJumpAction(U32 action); + + static void initPersistFields(); + virtual void packData(BitStream* stream); + virtual void unpackData(BitStream* stream); + + /// @name Callbacks + /// @{ + DECLARE_CALLBACK( void, onPoseChange, ( Player* obj, const char* oldPose, const char* newPose ) ); + DECLARE_CALLBACK( void, onStartSwim, ( Player* obj ) ); + DECLARE_CALLBACK( void, onStopSwim, ( Player* obj ) ); + DECLARE_CALLBACK( void, onStartSprintMotion, ( Player* obj ) ); + DECLARE_CALLBACK( void, onStopSprintMotion, ( Player* obj ) ); + DECLARE_CALLBACK( void, doDismount, ( Player* obj ) ); + DECLARE_CALLBACK( void, onEnterLiquid, ( Player* obj, F32 coverage, const char* type ) ); + DECLARE_CALLBACK( void, onLeaveLiquid, ( Player* obj, const char* type ) ); + DECLARE_CALLBACK( void, animationDone, ( Player* obj ) ); + DECLARE_CALLBACK( void, onEnterMissionArea, ( Player* obj ) ); + DECLARE_CALLBACK( void, onLeaveMissionArea, ( Player* obj ) ); + /// @} +}; + + +//---------------------------------------------------------------------------- + +class Player: public ShapeBase +{ + typedef ShapeBase Parent; + +public: + enum Pose { + StandPose = 0, + SprintPose, + CrouchPose, + PronePose, + SwimPose, + NumPoseBits = 3 + }; + +protected: + + /// Bit masks for different types of events + enum MaskBits { + ActionMask = Parent::NextFreeMask << 0, + MoveMask = Parent::NextFreeMask << 1, + ImpactMask = Parent::NextFreeMask << 2, + NextFreeMask = Parent::NextFreeMask << 3 + }; + + struct Range { + Range(F32 _min,F32 _max) { + min = _min; + max = _max; + delta = _max - _min; + }; + F32 min,max; + F32 delta; + }; + + SimObjectPtr mSplashEmitter[PlayerData::NUM_SPLASH_EMITTERS]; + F32 mBubbleEmitterTime; + + /// Client interpolation/warp data + struct StateDelta { + Move move; ///< Last move from server + F32 dt; ///< Last interpolation time + /// @name Interpolation data + /// @{ + + Point3F pos; + Point3F rot; + Point3F head; + VectorF posVec; + VectorF rotVec; + VectorF headVec; + /// @} + + /// @name Warp data + /// @{ + + S32 warpTicks; + Point3F warpOffset; + Point3F rotOffset; + /// @} + }; + StateDelta delta; ///< Used for interpolation on the client. @see StateDelta + S32 mPredictionCount; ///< Number of ticks to predict + + // Current pos, vel etc. + Point3F mHead; ///< Head rotation, uses only x & z + Point3F mRot; ///< Body rotation, uses only z + VectorF mVelocity; ///< Velocity + Point3F mAnchorPoint; ///< Pos compression anchor + static F32 mGravity; ///< Gravity + S32 mImpactSound; + + bool mUseHeadZCalc; ///< Including mHead.z in transform calculations + + S32 mMountPending; ///< mMountPending suppresses tickDelay countdown so players will sit until + ///< their mount, or another animation, comes through (or 13 seconds elapses). + + /// Main player state + enum ActionState { + NullState, + MoveState, + RecoverState, + NumStateBits = 3 + }; + ActionState mState; ///< What is the player doing? @see ActionState + bool mFalling; ///< Falling in mid-air? + S32 mJumpDelay; ///< Delay till next jump + + Pose mPose; + bool mAllowJumping; + bool mAllowJetJumping; + bool mAllowSprinting; + bool mAllowCrouching; + bool mAllowProne; + bool mAllowSwimming; + + S32 mContactTimer; ///< Ticks since last contact + + Point3F mJumpSurfaceNormal; ///< Normal of the surface the player last jumped on + U32 mJumpSurfaceLastContact; ///< How long it's been since the player landed (ticks) + F32 mWeaponBackFraction; ///< Amount to slide the weapon back (if it's up against something) + + SFXSource* mMoveBubbleSound; ///< Sound for moving bubbles + SFXSource* mWaterBreathSound; ///< Sound for underwater breath + + SimObjectPtr mControlObject; ///< Controlling object + + /// @name Animation threads & data + /// @{ + + struct ActionAnimation { + U32 action; + TSThread* thread; + S32 delayTicks; // before picking another. + bool forward; + bool firstPerson; + bool waitForEnd; + bool holdAtEnd; + bool animateOnServer; + bool atEnd; + } mActionAnimation; + + struct ArmAnimation { + U32 action; + TSThread* thread; + } mArmAnimation; + TSThread* mArmThread; + + TSThread* mHeadVThread; + TSThread* mHeadHThread; + TSThread* mRecoilThread; + TSThread* mImageStateThread; + static Range mArmRange; + static Range mHeadVRange; + static Range mHeadHRange; + /// @} + + bool mInMissionArea; ///< Are we in the mission area? + // + S32 mRecoverTicks; ///< same as recoverTicks in the player datablock + U32 mReversePending; + F32 mRecoverDelay; ///< When bypassing the legacy recover system and only using the land sequence, + /// this is how long the player will be in the land sequence. + + bool mInWater; ///< Is true if WaterCoverage is greater than zero + bool mSwimming; ///< Is true if WaterCoverage is above the swimming threshold + // + PlayerData* mDataBlock; ///< MMmmmmm...datablock... + + Point3F mLastPos; ///< Holds the last position for physics updates + Point3F mLastWaterPos; ///< Same as mLastPos, but for water + + struct ContactInfo + { + bool contacted, jump, run; + SceneObject *contactObject; + VectorF contactNormal; + + void clear() + { + contacted=jump=run=false; + contactObject = NULL; + contactNormal.set(1,1,1); + } + + ContactInfo() { clear(); } + + } mContactInfo; + + struct Death { + F32 lastPos; + Point3F posAdd; + VectorF rotate; + VectorF curNormal; + F32 curSink; + void clear() {dMemset(this, 0, sizeof(*this)); initFall();} + VectorF getPosAdd() {VectorF ret(posAdd); posAdd.set(0,0,0); return ret;} + bool haveVelocity() {return posAdd.x != 0 || posAdd.y != 0;} + void initFall() {curNormal.set(0,0,1); curSink = 0;} + Death() {clear();} + MatrixF* fallToGround(F32 adjust, const Point3F& pos, F32 zrot, F32 boxRad); + } mDeath; + + PhysicsPlayer *mPhysicsRep; + + // First person mounted image shapes + TSShapeInstance* mShapeFPInstance[ShapeBase::MaxMountedImages]; + TSThread *mShapeFPAmbientThread[ShapeBase::MaxMountedImages]; + TSThread *mShapeFPVisThread[ShapeBase::MaxMountedImages]; + TSThread *mShapeFPAnimThread[ShapeBase::MaxMountedImages]; + TSThread *mShapeFPFlashThread[ShapeBase::MaxMountedImages]; + TSThread *mShapeFPSpinThread[ShapeBase::MaxMountedImages]; + + + public: + + // New collision + OrthoBoxConvex mConvex; + Box3F mWorkingQueryBox; + + /// Standing / Crouched / Prone or Swimming + Pose getPose() const { return mPose; } + virtual const char* getPoseName() const; + + /// Setting this from script directly might not actually work, + /// This is really just a helper for the player class so that its bounding box + /// will get resized appropriately when the pose changes + void setPose( Pose pose ); + + PhysicsPlayer* getPhysicsRep() const { return mPhysicsRep; } + + protected: + virtual void reSkin(); + + void setState(ActionState state, U32 ticks=0); + void updateState(); + + // Jetting + bool mJetting; + + ///Update the movement + virtual void updateMove(const Move *move); + + ///Interpolate movement + Point3F _move( const F32 travelTime, Collision *outCol ); + F32 _doCollisionImpact( const Collision *collision, bool fallingCollision); + void _handleCollision( const Collision &collision ); + virtual bool updatePos(const F32 travelTime = TickSec); + + ///Update head animation + void updateLookAnimation(F32 dT = 0.f); + + ///Update other animations + void updateAnimation(F32 dt); + void updateAnimationTree(bool firstPerson); + bool step(Point3F *pos,F32 *maxStep,F32 time); + + ///See if the player is still in the mission area + void checkMissionArea(); + + virtual U32 getArmAction() const { return mArmAnimation.action; } + virtual bool setArmThread(U32 action); + virtual void setActionThread(U32 action,bool forward,bool hold = false,bool wait = false,bool fsp = false, bool forceSet = false); + virtual void updateActionThread(); + virtual void pickBestMoveAction(U32 startAnim, U32 endAnim, U32 * action, bool * forward) const; + virtual void pickActionAnimation(); + + /// @name Mounted objects + /// @{ + virtual void onUnmount( ShapeBase *obj, S32 node ); + virtual void unmount(); + /// @} + + void setPosition(const Point3F& pos,const Point3F& viewRot); + void setRenderPosition(const Point3F& pos,const Point3F& viewRot,F32 dt=-1); + void _findContact( SceneObject **contactObject, VectorF *contactNormal, Vector *outOverlapObjects ); + void findContact( bool *run, bool *jump, VectorF *contactNormal ); + + void buildImagePrefixPaths(String* prefixPaths); + S32 findPrefixSequence(String* prefixPaths, const String& baseSeq); + S32 convertActionToImagePrefix(U32 action); + + virtual void onImage(U32 imageSlot, bool unmount); + virtual void onImageRecoil(U32 imageSlot,ShapeBaseImageData::StateData::RecoilState); + virtual void onImageStateAnimation(U32 imageSlot, const char* seqName, bool direction, bool scaleToState, F32 stateTimeOutValue); + virtual const char* getImageAnimPrefix(U32 imageSlot, S32 imageShapeIndex); + virtual void onImageAnimThreadChange(U32 imageSlot, S32 imageShapeIndex, ShapeBaseImageData::StateData* lastState, const char* anim, F32 pos, F32 timeScale, bool reset=false); + virtual void onImageAnimThreadUpdate(U32 imageSlot, S32 imageShapeIndex, F32 dt); + + virtual void updateDamageLevel(); + virtual void updateDamageState(); + /// Set which client is controlling this player + void setControllingClient(GameConnection* client); + + void calcClassRenderData(); + + /// Play sound for foot contact. + /// + /// @param triggeredLeft If true, left foot hit; right otherwise. + /// @param contactMaterial Material onto which the player stepped; may be NULL. + /// @param contactObject Object onto which the player stepped; may be NULL. + void playFootstepSound( bool triggeredLeft, Material* contactMaterial, SceneObject* contactObject ); + + /// Play an impact sound. + void playImpactSound(); + + /// Are we in the process of dying? + bool inDeathAnim(); + F32 deathDelta(Point3F &delta); + void updateDeathOffsets(); + bool inSittingAnim(); + + /// @name Water + /// @{ + + void updateSplash(); ///< Update the splash effect + void updateFroth( F32 dt ); ///< Update any froth + void updateWaterSounds( F32 dt ); ///< Update water sounds + void createSplash( Point3F &pos, F32 speed ); ///< Creates a splash + bool collidingWithWater( Point3F &waterHeight ); ///< Are we colliding with water? + /// @} + + void disableHeadZCalc() { mUseHeadZCalc = false; } + void enableHeadZCalc() { mUseHeadZCalc = true; } + +public: + DECLARE_CONOBJECT(Player); + + Player(); + ~Player(); + static void consoleInit(); + + /// @name Transforms + /// @{ + + void setTransform(const MatrixF &mat); + void getEyeTransform(MatrixF* mat); + void getEyeBaseTransform(MatrixF* mat); + void getRenderEyeTransform(MatrixF* mat); + void getRenderEyeBaseTransform(MatrixF* mat); + void getCameraParameters(F32 *min, F32 *max, Point3F *offset, MatrixF *rot); + void getMuzzleTransform(U32 imageSlot,MatrixF* mat); + void getRenderMuzzleTransform(U32 imageSlot,MatrixF* mat); + + virtual void getMuzzleVector(U32 imageSlot,VectorF* vec); + /// @} + + F32 getSpeed() const; + Point3F getVelocity() const; + void setVelocity(const VectorF& vel); + /// Apply an impulse at the given point, with magnitude/direction of vec + void applyImpulse(const Point3F& pos,const VectorF& vec); + /// Get the rotation of the player + const Point3F& getRotation() { return mRot; } + /// Get the rotation of the head of the player + const Point3F& getHeadRotation() { return mHead; } + void getDamageLocation(const Point3F& in_rPos, const char *&out_rpVert, const char *&out_rpQuad); + + void allowAllPoses(); + void allowJumping(bool state) { mAllowJumping = state; } + void allowJetJumping(bool state) { mAllowJetJumping = state; } + void allowSprinting(bool state) { mAllowSprinting = state; } + void allowCrouching(bool state) { mAllowCrouching = state; } + void allowProne(bool state) { mAllowProne = state; } + void allowSwimming(bool state) { mAllowSwimming = state; } + + bool canJump(); ///< Can the player jump? + bool canJetJump(); ///< Can the player jet? + bool canSwim(); ///< Can the player swim? + bool canCrouch(); + bool canStand(); + bool canProne(); + bool canSprint(); + bool haveContact() const { return !mContactTimer; } ///< Is it in contact with something + void getMuzzlePointAI( U32 imageSlot, Point3F *point ); + F32 getMaxForwardVelocity() const { return (mDataBlock != NULL ? mDataBlock->maxForwardSpeed : 0); } + + virtual bool isDisplacable() const; + virtual Point3F getMomentum() const; + virtual void setMomentum(const Point3F &momentum); + virtual bool displaceObject(const Point3F& displaceVector); + virtual bool getAIMove(Move*); + + bool checkDismountPosition(const MatrixF& oldPos, const MatrixF& newPos); ///< Is it safe to dismount here? + + // + bool onAdd(); + void onRemove(); + bool onNewDataBlock( GameBaseData *dptr, bool reload ); + void onScaleChanged(); + Box3F mScaledBox; + + // Animation + const char* getStateName(); + bool setActionThread(const char* sequence,bool hold,bool wait,bool fsp = false); + const String& getArmThread() const; + bool setArmThread(const char* sequence); + + // Object control + void setControlObject(ShapeBase *obj); + ShapeBase* getControlObject(); + + // + void updateWorkingCollisionSet(); + virtual void processTick(const Move *move); + void interpolateTick(F32 delta); + void advanceTime(F32 dt); + bool castRay(const Point3F &start, const Point3F &end, RayInfo* info); + bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere); + void buildConvex(const Box3F& box, Convex* convex); + bool isControlObject(); + + void onCameraScopeQuery(NetConnection *cr, CameraScopeQuery *); + void writePacketData(GameConnection *conn, BitStream *stream); + void readPacketData (GameConnection *conn, BitStream *stream); + U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream); + void unpackUpdate(NetConnection *conn, BitStream *stream); + + virtual void prepRenderImage( SceneRenderState* state ); + virtual void renderConvex( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); + virtual void renderMountedImage( U32 imageSlot, TSRenderState &rstate, SceneRenderState *state ); +}; + +typedef Player::Pose PlayerPose; + +DefineEnumType( PlayerPose ); + +#endif diff --git a/Engine/source/T3D/pointLight.cpp b/Engine/source/T3D/pointLight.cpp new file mode 100644 index 000000000..4b2a9d983 --- /dev/null +++ b/Engine/source/T3D/pointLight.cpp @@ -0,0 +1,172 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/pointLight.h" + +#include "console/consoleTypes.h" +#include "core/stream/bitStream.h" +#include "gfx/gfxDrawUtil.h" + + +IMPLEMENT_CO_NETOBJECT_V1( PointLight ); + +ConsoleDocClass( PointLight, + "@brief Lighting object that radiates light in all directions.\n\n" + + "PointLight is one of the two types of lighting objects that can be added " + "to a Torque 3D level, the other being SpotLight. Unlike directional or conical light, " + "the PointLight emits lighting in all directions. The attenuation is controlled " + "by a single variable: LightObject::radius.\n\n" + + "@tsexample\n" + "// Declaration of a point light in script, or created by World Editor\n" + "new PointLight(CrystalLight)\n" + "{\n" + " radius = \"10\";\n" + " isEnabled = \"1\";\n" + " color = \"1 0.905882 0 1\";\n" + " brightness = \"0.5\";\n" + " castShadows = \"1\";\n" + " priority = \"1\";\n" + " animate = \"1\";\n" + " animationType = \"SubtlePulseLightAnim\";\n" + " animationPeriod = \"3\";\n" + " animationPhase = \"3\";\n" + " flareScale = \"1\";\n" + " attenuationRatio = \"0 1 1\";\n" + " shadowType = \"DualParaboloidSinglePass\";\n" + " texSize = \"512\";\n" + " overDarkFactor = \"2000 1000 500 100\";\n" + " shadowDistance = \"400\";\n" + " shadowSoftness = \"0.15\";\n" + " numSplits = \"1\";\n" + " logWeight = \"0.91\";\n" + " fadeStartDistance = \"0\";\n" + " lastSplitTerrainOnly = \"0\";\n" + " splitFadeDistances = \"10 20 30 40\";\n" + " representedInLightmap = \"0\";\n" + " shadowDarkenColor = \"0 0 0 -1\";\n" + " includeLightmappedGeometryInShadow = \"1\";\n" + " position = \"-61.3866 1.69186 5.1464\";\n" + " rotation = \"1 0 0 0\";\n" + "};\n" + "@endtsexample\n\n" + + "@see LightBase\n\n" + "@see SpotLight\n\n" + "@ingroup Lighting\n" +); + +PointLight::PointLight() + : mRadius( 5.0f ) +{ + // We set the type here to ensure the extended + // parameter validation works when setting fields. + mLight->setType( LightInfo::Point ); +} + +PointLight::~PointLight() +{ +} + +void PointLight::initPersistFields() +{ + addGroup( "Light" ); + + addField( "radius", TypeF32, Offset( mRadius, PointLight ), "Controls the falloff of the light emission" ); + + endGroup( "Light" ); + + // We do the parent fields at the end so that + // they show up that way in the inspector. + Parent::initPersistFields(); + + // Remove the scale field... it's already + // defined by the light radius. + removeField( "scale" ); +} + +void PointLight::_conformLights() +{ + mLight->setTransform( getRenderTransform() ); + + mLight->setRange( mRadius ); + + mLight->setColor( mColor ); + + mLight->setBrightness( mBrightness ); + mLight->setCastShadows( mCastShadows ); + mLight->setPriority( mPriority ); + + // Update the bounds and scale to fit our light. + mObjBox.minExtents.set( -1, -1, -1 ); + mObjBox.maxExtents.set( 1, 1, 1 ); + mObjScale.set( mRadius, mRadius, mRadius ); + + // Skip our transform... it just dirties mask bits. + Parent::setTransform( mObjToWorld ); +} + +U32 PointLight::packUpdate(NetConnection *conn, U32 mask, BitStream *stream ) +{ + if ( stream->writeFlag( mask & UpdateMask ) ) + stream->write( mRadius ); + + return Parent::packUpdate( conn, mask, stream ); +} + +void PointLight::unpackUpdate( NetConnection *conn, BitStream *stream ) +{ + if ( stream->readFlag() ) // UpdateMask + stream->read( &mRadius ); + + Parent::unpackUpdate( conn, stream ); +} + +void PointLight::setScale( const VectorF &scale ) +{ + // Use the average of the three coords. + mRadius = ( scale.x + scale.y + scale.z ) / 3.0f; + + // We changed our settings so notify the client. + setMaskBits( UpdateMask ); + + // Let the parent do the final scale. + Parent::setScale( VectorF( mRadius, mRadius, mRadius ) ); +} + +void PointLight::_renderViz( SceneRenderState *state ) +{ + GFXDrawUtil *draw = GFX->getDrawUtil(); + + GFXStateBlockDesc desc; + desc.setZReadWrite( true, false ); + desc.setCullMode( GFXCullNone ); + desc.setBlend( true ); + + // Base the sphere color on the light color. + ColorI color( mColor ); + color.alpha = 16; + + draw->drawSphere( desc, mRadius, getPosition(), color ); +} diff --git a/Engine/source/T3D/pointLight.h b/Engine/source/T3D/pointLight.h new file mode 100644 index 000000000..1a0923970 --- /dev/null +++ b/Engine/source/T3D/pointLight.h @@ -0,0 +1,60 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _POINTLIGHT_H_ +#define _POINTLIGHT_H_ + +#ifndef _LIGHTBASE_H_ +#include "T3D/lightBase.h" +#endif + + +class PointLight : public LightBase +{ + typedef LightBase Parent; + +protected: + + F32 mRadius; + + // LightBase + void _conformLights(); + void _renderViz( SceneRenderState *state ); + +public: + + PointLight(); + virtual ~PointLight(); + + // ConsoleObject + DECLARE_CONOBJECT( PointLight ); + static void initPersistFields(); + + // SceneObject + virtual void setScale( const VectorF &scale ); + + // NetObject + U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream ); + void unpackUpdate( NetConnection *conn, BitStream *stream ); +}; + +#endif // _POINTLIGHT_H_ diff --git a/Engine/source/T3D/portal.cpp b/Engine/source/T3D/portal.cpp new file mode 100644 index 000000000..1318c632a --- /dev/null +++ b/Engine/source/T3D/portal.cpp @@ -0,0 +1,772 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/portal.h" + +#include "core/stream/bitStream.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "scene/zones/sceneRootZone.h" +#include "scene/culling/sceneCullingState.h" +#include "scene/zones/sceneTraversalState.h" +#include "math/mPlaneSet.h" +#include "gfx/gfxDrawUtil.h" +#include "gfx/gfxTransformSaver.h" + +#include "scene/mixin/sceneAmbientSoundObject.impl.h" +#include "scene/mixin/scenePolyhedralObject.impl.h" +#include "math/mPolyhedron.impl.h" + + +IMPLEMENT_CO_NETOBJECT_V1( Portal ); + +ConsoleDocClass( Portal, + "@brief An object that provides a \"window\" into a zone, allowing a viewer " + "to see what's rendered in the zone.\n\n" + + "A portal is an object that connects zones such that the content of one zone becomes " + "visible in the other when looking through the portal.\n\n" + + "Each portal is a full zone which is divided into two sides by the portal plane that " + "intersects it. This intersection polygon is shown in red in the editor. Either of the " + "sides of a portal can be connected to one or more zones.\n\n" + + "A connection from a specific portal side to a zone is made in either of two ways:\n\n" + + "
        \n" + "
      1. By moving a Zone object to intersect with the portal at the respective side. While usually it makes " + "sense for this overlap to be small, the connection is established correctly as long as the center of the Zone " + "object that should connect is on the correct side of the portal plane.
      2. \n" + "
      3. By the respective side of the portal free of Zone objects that would connect to it. In this case, given " + "that the other side is connected to one or more Zones, the portal will automatically connect itself to the " + "outdoor \"zone\" which implicitly is present in any level.
      4. \n" + "
      \n\n" + + "From this, it follows that there are two types of portals:\n\n" + + "
      \n" + "
      Exterior Portals
      " + "
      An exterior portal is one that is connected to one or more Zone objects on one side and to the outdoor " + "zone at the other side. This kind of portal is most useful for covering transitions from outdoor spaces to " + "indoor spaces.
      " + "
      Interior Portals
      " + "
      An interior portal is one that is connected to one or more Zone objects on both sides. This kind of portal " + "is most useful for covering transitions between indoor spaces./dd>" + "
      \n\n" + + "Strictly speaking, there is a third type of portal called an \"invalid portal\". This is a portal that is not " + "connected to a Zone object on either side in which case the portal serves no use.\n\n" + + "Portals in Torque are bidirectional meaning that they connect zones both ways and " + "you can look through the portal's front side as well as through its back-side.\n\n" + + "Like Zones, Portals can either be box-shaped or use custom convex polyhedral shapes.\n\n" + + "Portals will usually be created in the editor but can, of course, also be created " + "in script code as such:\n\n" + + "@tsexample\n" + "// Example declaration of a Portal. This will create a box-shaped portal.\n" + "new Portal( PortalToTestZone )\n" + "{\n" + " position = \"12.8467 -4.02246 14.8017\";\n" + " rotation = \"0 0 -1 97.5085\";\n" + " scale = \"1 0.25 1\";\n" + " canSave = \"1\";\n" + " canSaveDynamicFields = \"1\";\n" + "};\n" + "@endtsexample\n\n" + + "@note Keep in mind that zones and portals are more or less strictly a scene optimization mechanism meant to " + "improve render times.\n\n" + + "@see Zone\n" + "@ingroup enviroMisc\n" +); + + +// Notes: +// - This class implements portal spaces as single zones. A different, interesting take +// on this is to turn portal spaces into two zones with the portal plane acting as +// the separator. +// - One downside to our treatment of portals as full zones in their own right is that +// in certain cases we end up including space in the traversal that is clearly not visible. +// Take a situation where you are in the outside zone and you are looking straight into +// the wall of a house. On the other side of that house is a portal leading into the house. +// While the traversal will not step through that portal into the house since that would +// be leading towards the camera rather than away from it, it will still add the frustum +// to the visible space of the portal zone and thus make everything in the portal zone +// visible. It has to do this since, while it can easily tell whether to step through the +// portal or not, it cannot easily tell whether the whole portal zone is visible or not as +// that depends on the occlusion situation in the outdoor zone. + + +//----------------------------------------------------------------------------- + +Portal::Portal() + : mClassification( InvalidPortal ), + mIsGeometryDirty( true ) +{ + VECTOR_SET_ASSOCIATION( mPortalPolygonWS ); + + mNetFlags.set( Ghostable | ScopeAlways ); + mTypeMask |= StaticObjectType; + + mPassableSides[ FrontSide ] = true; + mPassableSides[ BackSide ] = true; + + mObjScale.set( 1.0f, 0.25f, 1.0f ); + + // We're not closed off. + mZoneFlags.clear( ZoneFlag_IsClosedOffSpace ); +} + +//----------------------------------------------------------------------------- + +void Portal::initPersistFields() +{ + addGroup( "Zoning" ); + + addProtectedField( "frontSidePassable", TypeBool, Offset( mPassableSides[ FrontSide ], Portal ), + &_setFrontSidePassable, &defaultProtectedGetFn, + "Whether one can view through the front-side of the portal." ); + addProtectedField( "backSidePassable", TypeBool, Offset( mPassableSides[ BackSide ], Portal ), + &_setBackSidePassable, &defaultProtectedGetFn, + "Whether one can view through the back-side of the portal." ); + + endGroup( "Zoning" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +void Portal::consoleInit() +{ + // Disable rendering of portals by default. + getStaticClassRep()->mIsRenderEnabled = false; +} + +//----------------------------------------------------------------------------- + +String Portal::describeSelf() const +{ + String str = Parent::describeSelf(); + + switch( getClassification() ) + { + case InvalidPortal: str += "|InvalidPortal"; break; + case ExteriorPortal: str += "|ExteriorPortal"; break; + case InteriorPortal: str += "|InteriorPortal"; break; + } + + return str; +} + +//----------------------------------------------------------------------------- + +bool Portal::writeField( StringTableEntry fieldName, const char* value ) +{ + static StringTableEntry sFrontSidePassable = StringTable->insert( "frontSidePassable" ); + static StringTableEntry sBackSidePassable = StringTable->insert( "backSidePassable" ); + + // Don't write passable flags if at default. + if( ( fieldName == sFrontSidePassable || fieldName == sBackSidePassable ) && + dAtob( value ) ) + return false; + + return Parent::writeField( fieldName, value ); +} + +//----------------------------------------------------------------------------- + +void Portal::onSceneRemove() +{ + // Disconnect from root zone, if it's an exterior portal. + + if( mClassification == ExteriorPortal ) + { + AssertFatal( getSceneManager()->getZoneManager(), "Portal::onSceneRemove - Portal classified as exterior without having a zone manager!" ); + getSceneManager()->getZoneManager()->getRootZone()->disconnectZoneSpace( this ); + } + + Parent::onSceneRemove(); +} + +//----------------------------------------------------------------------------- + +void Portal::setTransform( const MatrixF& mat ) +{ + // Portal geometry needs updating. Set this before calling + // parent because the transform change will cause an immediate + // update of the portal's zoning state. + mIsGeometryDirty = true; + + Parent::setTransform( mat ); +} + +//----------------------------------------------------------------------------- + +void Portal::setSidePassable( Side side, bool value ) +{ + if( mPassableSides[ side ] == value ) + return; + + mPassableSides[ side ] = value; + + if( isServerObject() ) + setMaskBits( PassableMask ); +} + +//----------------------------------------------------------------------------- + +U32 Portal::packUpdate( NetConnection *con, U32 mask, BitStream *stream ) +{ + U32 retMask = Parent::packUpdate( con, mask, stream ); + + stream->writeFlag( mPassableSides[ FrontSide ] ); + stream->writeFlag( mPassableSides[ BackSide ] ); + + return retMask; +} + +//----------------------------------------------------------------------------- + +void Portal::unpackUpdate( NetConnection *con, BitStream *stream ) +{ + Parent::unpackUpdate( con, stream ); + + mPassableSides[ FrontSide ] = stream->readFlag(); + mPassableSides[ BackSide ] = stream->readFlag(); +} + +//----------------------------------------------------------------------------- + +void Portal::_renderObject( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* overrideMat ) +{ + if( overrideMat ) + return; + + // Update geometry if necessary. + + if( mIsGeometryDirty ) + _updateGeometry(); + + // Render portal polygon. + + GFXStateBlockDesc desc; + desc.setBlend( true ); + desc.setZReadWrite( true, false ); + desc.setCullMode( GFXCullNone ); + + PlaneF::Side viewSide = mPortalPlane.whichSide( state->getCameraPosition() ); + + ColorI color; + switch( mClassification ) + { + case InvalidPortal: color = ColorI( 255, 255, 255, 45 ); break; + case ExteriorPortal: color = viewSide == PlaneF::Front ? ColorI( 0, 128, 128, 45 ) : ColorI( 0, 255, 255, 45 ); break; + case InteriorPortal: color = viewSide == PlaneF::Front ? ColorI( 128, 128, 0, 45 ) : ColorI( 255, 255, 0, 45 ); break; + } + + GFX->getDrawUtil()->drawPolygon( desc, mPortalPolygonWS.address(), mPortalPolygonWS.size(), color ); + + desc.setFillModeWireframe(); + GFX->getDrawUtil()->drawPolygon( desc, mPortalPolygonWS.address(), mPortalPolygonWS.size(), ColorF::RED ); + + // Render rest. + + Parent::_renderObject( ri, state, overrideMat ); +} + +//----------------------------------------------------------------------------- + +void Portal::traverseZones( SceneTraversalState* state, U32 startZoneId ) +{ + // Check whether the portal is occluded. + + if( state->getTraversalDepth() > 0 && + state->getCullingState()->isOccluded( this ) ) + return; + + Parent::traverseZones( state, startZoneId ); +} + +//----------------------------------------------------------------------------- + +void Portal::_traverseConnectedZoneSpaces( SceneTraversalState* state ) +{ + PROFILE_SCOPE( Portal_traverseConnectedZoneSpaces ); + + // Don't traverse out from the portal if it is invalid. + + if( mClassification == InvalidPortal ) + return; + + AssertFatal( !mIsGeometryDirty, "Portal::_traverseConnectedZoneSpaces - Geometry not up-to-date!" ); + + // When starting traversal within a portal zone, we cannot really use the portal + // plane itself to direct our visibility queries. For example, the camera might + // actually be located in front of the portal plane and thus cannot actually look + // through the portal, though it will still see what lies on front of where the + // portal leads. + // + // So if we're the start of the traversal chain, i.e. the traversal has started + // out in the portal zone, then just put the traversal through to SceneZoneSpace + // so it can hand it over to all connected zone managers. + // + // Otherwise, just do a normal traversal by stepping through the portal. + + if( state->getTraversalDepth() == 1 ) + { + Parent::_traverseConnectedZoneSpaces( state ); + return; + } + + SceneCullingState* cullingState = state->getCullingState(); + const SceneCameraState& cameraState = cullingState->getCameraState(); + + // Get the data of the zone we're coming from. Note that at this point + // the portal zone itself is already on top of the traversal stack, so + // we skip over the bottom-most entry. + + const U32 sourceZoneId = state->getZoneIdFromStack( 1 ); + const SceneZoneSpace* sourceZoneSpace = state->getZoneFromStack( 1 ); + + // Find out which side of the portal we are on given the + // source zone. + + const Portal::Side currentZoneSide = + sourceZoneId == SceneZoneSpaceManager::RootZoneId + ? ( getInteriorSideOfExteriorPortal() == FrontSide ? BackSide : FrontSide ) + : getSideRelativeToPortalPlane( sourceZoneSpace->getPosition() ); + + // Don't step through portal if the side we're interested in isn't passable. + + if( !isSidePassable( currentZoneSide ) ) + return; + + // If the viewpoint isn't on the same side of the portal as the source zone, + // then stepping through the portal would mean we are stepping back towards + // the viewpoint which doesn't make sense; so, skip the portal. + + const Point3F& viewPos = cameraState.getViewPosition(); + const F32 viewPosDistToPortal = mFabs( getPortalPlane().distToPlane( viewPos ) ); + if( !mIsZero( viewPosDistToPortal ) && getSideRelativeToPortalPlane( viewPos ) != currentZoneSide ) + return; + + // Before we go ahead and do the real work, try to find out whether + // the portal is at a perpendicular or near-perpendicular angle to the view + // direction. If so, there's no point in going further since we can't really + // see much through the portal anyway. It also prevents us from stepping + // over front/back side ambiguities. + + Point3F viewDirection = cameraState.getViewDirection(); + const F32 dotProduct = mDot( viewDirection, getPortalPlane() ); + if( mIsZero( dotProduct ) ) + return; + + // Finally, if we have come through a portal to the current zone, check if the target + // portal we are trying to step through now completely lies on the "backside"--i.e. + // the side of portal on which our current zone lies--of the source portal. If so, + // we can be sure this portal leads us in the wrong direction. This prevents the + // outdoor zone from having just arrived through a window on one side of a house just + // to go round the house and re-enter it from the other side. + + Portal* sourcePortal = state->getTraversalDepth() > 2 ? dynamic_cast< Portal* >( state->getZoneFromStack( 2 ) ) : NULL; + if( sourcePortal != NULL ) + { + const Side sourcePortalFrontSide = + sourceZoneId == SceneZoneSpaceManager::RootZoneId + ? sourcePortal->getInteriorSideOfExteriorPortal() + : sourcePortal->getSideRelativeToPortalPlane( sourceZoneSpace->getPosition() ); + const PlaneF::Side sourcePortalPlaneFrontSide = + sourcePortalFrontSide == FrontSide ? PlaneF::Front : PlaneF::Back; + + bool allPortalVerticesOnBackside = true; + const U32 numVertices = mPortalPolygonWS.size(); + for( U32 i = 0; i < numVertices; ++ i ) + { + // Not using getSideRelativeToPortalPlane here since we want PlaneF::On to be + // counted as backside here. + if( sourcePortal->mPortalPlane.whichSide( mPortalPolygonWS[ i ] ) == sourcePortalPlaneFrontSide ) + { + allPortalVerticesOnBackside = false; + break; + } + } + + if( allPortalVerticesOnBackside ) + return; + } + + // If we come from the outdoor zone, then we don't want to step through any portal + // where the interior zones are actually on the same side as our camera since that + // would mean we are stepping into an interior through the backside of a portal. + + if( sourceZoneId == SceneZoneSpaceManager::RootZoneId ) + { + const Portal::Side cameraSide = getSideRelativeToPortalPlane( viewPos ); + if( cameraSide == getInteriorSideOfExteriorPortal() ) + return; + } + + // Clip the current culling volume against the portal's polygon. If the polygon + // lies completely outside the volume or for some other reason there's no good resulting + // volume, _generateCullingVolume() will return false and we terminate this portal sequence + // here. + // + // However, don't attempt to clip the portal if we are standing really close to or + // even below the near distance away from the portal plane. In that case, trying to + // clip the portal will only result in trouble so we stick to the original culling volume + // in that case. + + bool haveClipped = false; + if( viewPosDistToPortal > ( cameraState.getFrustum().getNearDist() + 0.1f ) ) + { + SceneCullingVolume volume; + if( !_generateCullingVolume( state, volume ) ) + return; + + state->pushCullingVolume( volume ); + haveClipped = true; + } + + // Short-circuit things if we are stepping from an interior zone outside. In this + // case we know that the only zone we care about is the outdoor zone so head straight + // into it. + + if( isExteriorPortal() && sourceZoneId != SceneZoneSpaceManager::RootZoneId ) + getSceneManager()->getZoneManager()->getRootZone()->traverseZones( state ); + else + { + // Go through the zones that the portal connects to and + // traverse into them. + + for( ZoneSpaceRef* ref = mConnectedZoneSpaces; ref != NULL; ref = ref->mNext ) + { + SceneZoneSpace* targetSpace = ref->mZoneSpace; + if( targetSpace == sourceZoneSpace ) + continue; // Skip space we originated from. + + // We have handled the case of stepping into the outdoor zone above and + // by skipping the zone we originated from, we have implicitly handled the + // case of stepping out of the outdoor zone. Thus, we should not see the + // outdoor zone here. Important as getPosition() is meaningless for it. + AssertFatal( targetSpace->getZoneRangeStart() != SceneZoneSpaceManager::RootZoneId, + "Portal::_traverseConnectedZoneSpaces - Outdoor zone must have been handled already" ); + + // Skip zones that lie on the same side as the zone + // we originated from. + + if( getSideRelativeToPortalPlane( targetSpace->getPosition() ) == currentZoneSide ) + continue; + + // Traverse into the space. + + targetSpace->traverseZones( state ); + } + } + + // If we have pushed our own clipping volume, + // remove that from the stack now. + + if( haveClipped ) + state->popCullingVolume(); +} + +//----------------------------------------------------------------------------- + +bool Portal::_generateCullingVolume( SceneTraversalState* state, SceneCullingVolume& outVolume ) const +{ + PROFILE_SCOPE( Portal_generateCullingVolume ); + + SceneCullingState* cullingState = state->getCullingState(); + const SceneCullingVolume& currentVolume = state->getCurrentCullingVolume(); + + // Clip the portal polygon against the current culling volume. + + Point3F vertices[ 64 ]; + U32 numVertices = 0; + + numVertices = currentVolume.getPlanes().clipPolygon( + mPortalPolygonWS.address(), + mPortalPolygonWS.size(), + vertices, + sizeof( vertices ) /sizeof( vertices[ 0 ] ) + ); + + AssertFatal( numVertices == 0 || numVertices >= 3, + "Portal::_generateCullingVolume - Clipping produced degenerate polygon" ); + + if( !numVertices ) + return false; + + // Create a culling volume. + + return cullingState->createCullingVolume( + vertices, numVertices, + SceneCullingVolume::Includer, + outVolume + ); +} + +//----------------------------------------------------------------------------- + +void Portal::connectZoneSpace( SceneZoneSpace* zoneSpace ) +{ + Parent::connectZoneSpace( zoneSpace ); + + // Update portal state. Unfortunately, we can't do that on demand + // easily since everything must be in place before a traversal starts. + + _update(); +} + +//----------------------------------------------------------------------------- + +void Portal::disconnectZoneSpace( SceneZoneSpace* zoneSpace ) +{ + Parent::disconnectZoneSpace( zoneSpace ); + + // Update portal state. + + _update(); +} + +//----------------------------------------------------------------------------- + +void Portal::_disconnectAllZoneSpaces() +{ + Parent::_disconnectAllZoneSpaces(); + + // Update portal state. + + _update(); +} + +//----------------------------------------------------------------------------- + +void Portal::_update() +{ + if( mIsGeometryDirty ) + _updateGeometry(); + + _updateConnectivity(); +} + +//----------------------------------------------------------------------------- + +void Portal::_updateGeometry() +{ + const F32 boxHalfWidth = getScale().x * 0.5f; + const F32 boxHalfHeight = getScale().z * 0.5f; + + const Point3F center = getTransform().getPosition(); + const Point3F up = getTransform().getUpVector() * boxHalfHeight; + const Point3F right = getTransform().getRightVector() * boxHalfWidth; + + // Update the portal polygon and plane. + + if( mIsBox ) + { + // It's a box so the portal polygon is a rectangle. + // Simply compute the corner points by stepping from the + // center to the corners using the up and right vector. + + mPortalPolygonWS.setSize( 4 ); + + mPortalPolygonWS[ 0 ] = center + right - up; // bottom right + mPortalPolygonWS[ 1 ] = center - right - up; // bottom left + mPortalPolygonWS[ 2 ] = center - right + up; // top left + mPortalPolygonWS[ 3 ] = center + right + up; // top right + + // Update the plane by going through three of the points. + + mPortalPlane = PlaneF( + mPortalPolygonWS[ 0 ], + mPortalPolygonWS[ 1 ], + mPortalPolygonWS[ 2 ] + ); + } + else + { + // It's not necessarily a box so we must use the general + // routine. + + // Update the portal plane by building a plane that cuts the + // OBB in half vertically along its Y axis. + + mPortalPlane = PlaneF( + center + right - up, + center - right - up, + center - right + up + ); + + // Slice the polyhedron along the same plane in object space. + + const PlaneF slicePlane = PlaneF( Point3F::Zero, Point3F( 0.f, 1.f, 0.f ) ); + + mPortalPolygonWS.setSize( mPolyhedron.getNumEdges() ); + U32 numPoints = mPolyhedron.constructIntersection( slicePlane, mPortalPolygonWS.address(), mPortalPolygonWS.size() ); + mPortalPolygonWS.setSize( numPoints ); + + // Transform the polygon to world space. + + for( U32 i = 0; i < numPoints; ++ i ) + { + mPortalPolygonWS[ i ].convolve( getScale() ); + mObjToWorld.mulP( mPortalPolygonWS[ i ] ); + } + } + + mIsGeometryDirty = false; +} + +//----------------------------------------------------------------------------- + +void Portal::_updateConnectivity() +{ + SceneZoneSpaceManager* zoneManager = getSceneManager()->getZoneManager(); + if( !zoneManager ) + return; + + // Find out where our connected zones are in respect to the portal + // plane. + + bool haveInteriorZonesOnFrontSide = false; + bool haveInteriorZonesOnBackSide = false; + bool isConnectedToRootZone = ( mClassification == ExteriorPortal ); + + for( ZoneSpaceRef* ref = mConnectedZoneSpaces; + ref != NULL; ref = ref->mNext ) + { + SceneZoneSpace* zone = dynamic_cast< SceneZoneSpace* >( ref->mZoneSpace ); + if( !zone || zone->isRootZone() ) + continue; + + if( getSideRelativeToPortalPlane( zone->getPosition() ) == FrontSide ) + haveInteriorZonesOnFrontSide = true; + else + haveInteriorZonesOnBackSide = true; + } + + // If we have zones connected to us on only one side, we are an exterior + // portal. Otherwise, we're an interior portal. + + SceneRootZone* rootZone = zoneManager->getRootZone(); + if( haveInteriorZonesOnFrontSide && haveInteriorZonesOnBackSide ) + { + mClassification = InteriorPortal; + } + else if( haveInteriorZonesOnFrontSide || haveInteriorZonesOnBackSide ) + { + mClassification = ExteriorPortal; + + // Remember where our interior zones are. + + if( haveInteriorZonesOnBackSide ) + mInteriorSide = BackSide; + else + mInteriorSide = FrontSide; + + // If we aren't currently connected to the root zone, + // establish the connection now. + + if( !isConnectedToRootZone ) + { + Parent::connectZoneSpace( rootZone ); + rootZone->connectZoneSpace( this ); + } + } + else + mClassification = InvalidPortal; + + // If we have been connected to the outdoor zone already but the + // portal got classified as invalid or interior now, break the + // connection to the outdoor zone. + + if( isConnectedToRootZone && + ( mClassification == InvalidPortal || mClassification == InteriorPortal ) ) + { + Parent::disconnectZoneSpace( rootZone ); + rootZone->disconnectZoneSpace( this ); + } +} + +//----------------------------------------------------------------------------- + +Portal::Side Portal::getSideRelativeToPortalPlane( const Point3F& point ) const +{ + // For our purposes, we consider PlaneF::Front and PlaneF::On + // placement as FrontSide. + + PlaneF::Side planeSide = getPortalPlane().whichSide( point ); + if( planeSide == PlaneF::Front || planeSide == PlaneF::On ) + return FrontSide; + else + return BackSide; +} + +//----------------------------------------------------------------------------- + +bool Portal::_setFrontSidePassable( void* object, const char* index, const char* data ) +{ + Portal* portal = reinterpret_cast< Portal* >( object ); + portal->setSidePassable( Portal::FrontSide, EngineUnmarshallData< bool >()( data ) ); + return false; +} + +//----------------------------------------------------------------------------- + +bool Portal::_setBackSidePassable( void* object, const char* index, const char* data ) +{ + Portal* portal = reinterpret_cast< Portal* >( object ); + portal->setSidePassable( Portal::BackSide, EngineUnmarshallData< bool >()( data ) ); + return false; +} + +//============================================================================= +// Console API. +//============================================================================= +// MARK: ---- Console API ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Portal, isInteriorPortal, bool, (),, + "Test whether the portal connects interior zones only.\n\n" + "@return True if the portal is an interior portal." ) +{ + return object->isInteriorPortal(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Portal, isExteriorPortal, bool, (),, + "Test whether the portal connects interior zones to the outdoor zone.\n\n" + "@return True if the portal is an exterior portal." ) +{ + return object->isExteriorPortal(); +} diff --git a/Engine/source/T3D/portal.h b/Engine/source/T3D/portal.h new file mode 100644 index 000000000..994e50dac --- /dev/null +++ b/Engine/source/T3D/portal.h @@ -0,0 +1,213 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PORTAL_H_ +#define _PORTAL_H_ + +#ifndef _ZONE_H_ +#include "T3D/zone.h" +#endif + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + + +class SceneCullingState; +class SceneCullingVolume; + + +/// A transitioning zone that connects other zones. +/// +/// Basically a portal is two things: +/// +/// 1) A zone that overlaps multiple other zones and thus connects them. +/// 2) A polygon standing upright in the middle of the portal zone's world box. +/// +/// When traversing from zone to zone, portals serve as both zones in their own +/// right (i.e. objects may be located in a portal zone) as well as a peek hole +/// that determines what area of a target zone is visible through a portal. +/// +/// Torque's portals are special in that they are two-way by default. This greatly +/// simplifies zone setups but it also complicates handling in the engine somewhat. +/// Also, these portals here are nothing but peek holes--they do not define transform +/// portals that could be looking at a different location in space altogether. +/// +/// Portals can be marked explicitly as being one-sided by flagging either of the portal's +/// sides as impassable. This flagging can also be used dynamically to, for example, block +/// a portal while a door is still down and then unblock the portal when the door is +/// opened. +/// +/// Portals are classified as either interior or exterior portals. An exterior portal is +/// a portal that has only non-SceneRootZone zones on side of the portal plane and only the +/// SceneRootZone on the other side of it. An interior portal is a portal that has only +/// non-SceneRootZone zones on both sides of the portal plane. A mixture of the two is not +/// allowed – when adding SceneRootZone to a portal, it must exist alone on its portal +/// side. +class Portal : public Zone +{ + public: + + typedef Zone Parent; + + /// Identifies the subspaces defined by the portal plane. + enum Side + { + FrontSide, ///< Subspace on front side of portal plane. + BackSide ///< Subspace on back side of portal plane. + }; + + /// Identifies the type of portal. + enum Classification + { + InvalidPortal, ///< Portal does not connect anything. + InteriorPortal, ///< Portal between interior zones. + ExteriorPortal ///< Portal between interior zones on one and side and SceneRootZone on the other. + }; + + protected: + + enum + { + PassableMask = Parent::NextFreeMask << 0, ///< #mPassableSides has changed. + NextFreeMask = Parent::NextFreeMask << 1, + }; + + /// Flags that allow preventing traversal through specific + /// sides of the portal. By default, both sides are passable. + bool mPassableSides[ 2 ]; + + /// @name Derived Portal Data + /// @{ + + /// Classification of this portal as interior or exterior portal. + Classification mClassification; + + /// For exterior portals, this is the side of the portal on which + /// the connected interior zones lie. + Side mInteriorSide; + + /// Whether the portal plane and polygon need to be updated. + bool mIsGeometryDirty; + + /// Portal polygon in world space. + Vector< Point3F > mPortalPolygonWS; + + /// The plane defined by the portal's rectangle. + PlaneF mPortalPlane; + + /// Update derived data, if necessary. + void _update(); + + /// Update the world space portal geometry. + void _updateGeometry(); + + /// Detect whether this is an exterior, interior, or invalid portal. + void _updateConnectivity(); + + /// @} + + /// Compute a clipped culling volume from the portal geometry and current + /// traversal state. If successful, store the resulting culling volume in + /// @a outVolume and return true. + bool _generateCullingVolume( SceneTraversalState* state, SceneCullingVolume& outVolume ) const; + + // SceneSpace. + virtual void _renderObject( ObjectRenderInst* ri, SceneRenderState* state, BaseMatInstance* overrideMat ); + virtual ColorI _getDefaultEditorSolidColor() const { return ColorI( 0, 255, 0, 45 ); } + virtual ColorI _getDefaultEditorWireframeColor() const + { + switch( mClassification ) + { + case ExteriorPortal: return ColorI( 0, 128, 128, 255 ); break; + case InteriorPortal: return ColorI( 128, 128, 0, 255 ); + default: return ColorI( 255, 255, 255, 255 ); break; + } + } + + // SceneObject. + virtual void onSceneRemove(); + + // SceneZoneSpace. + virtual void _traverseConnectedZoneSpaces( SceneTraversalState* state ); + virtual void _disconnectAllZoneSpaces(); + + public: + + Portal(); + + /// Return what kind of portal this is (interior or exterior). + Classification getClassification() const { return mClassification; } + + /// Return the plane that is defined by the portal's rectangle. + const PlaneF& getPortalPlane() const { return mPortalPlane; } + + /// Return the side that the given point is in relative to the portal plane. + Side getSideRelativeToPortalPlane( const Point3F& point ) const; + + /// Test whether the given side of the portal is open for traversal. + bool isSidePassable( Side side ) const { return mPassableSides[ side ]; } + + /// Set whether the given portal side is passable. + void setSidePassable( Side side, bool value ); + + /// Return true if the portal leads to the outdoor zone. + bool isExteriorPortal() const { return ( getClassification() == ExteriorPortal ); } + + /// Return true if the portal connects interior zones only. + bool isInteriorPortal() const { return ( getClassification() == InteriorPortal ); } + + /// For exterior portals, get the side on which the interior zones of the portal lie. + Side getInteriorSideOfExteriorPortal() const + { + AssertFatal( isExteriorPortal(), "Portal::getInteriorSideOfExteriorPortal - Not an exterior portal!" ); + return mInteriorSide; + } + + // SimObject. + DECLARE_CONOBJECT( Portal ); + + static void initPersistFields(); + static void consoleInit(); + + virtual bool writeField( StringTableEntry fieldName, const char* value ); + virtual String describeSelf() const; + + // NetObject. + virtual U32 packUpdate( NetConnection* conn, U32 mask, BitStream* stream ); + virtual void unpackUpdate( NetConnection* conn, BitStream* stream ); + + // SceneObject. + virtual void setTransform( const MatrixF &mat ); + + // SceneZoneSpace. + virtual void traverseZones( SceneTraversalState* state, U32 startZoneId ); + virtual void connectZoneSpace( SceneZoneSpace* zoneSpace ); + virtual void disconnectZoneSpace( SceneZoneSpace* zoneSpace ); + + private: + + static bool _setFrontSidePassable( void* object, const char* index, const char* data ); + static bool _setBackSidePassable( void* object, const char* index, const char* data ); +}; + +#endif // _PORTAL_H_ diff --git a/Engine/source/T3D/prefab.cpp b/Engine/source/T3D/prefab.cpp new file mode 100644 index 000000000..f9cf743a0 --- /dev/null +++ b/Engine/source/T3D/prefab.cpp @@ -0,0 +1,571 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/prefab.h" + +#include "math/mathIO.h" +#include "core/stream/bitStream.h" +#include "scene/sceneRenderState.h" +#include "gfx/gfxTransformSaver.h" +#include "renderInstance/renderPassManager.h" +#include "console/consoleTypes.h" +#include "core/volume.h" +#include "console/engineAPI.h" +#include "T3D/physics/physicsShape.h" +#include "core/util/path.h" + +// We use this locally ( within this file ) to prevent infinite recursion +// while loading prefab files that contain other prefabs. +static Vector sPrefabFileStack; + +Map Prefab::smChildToPrefabMap; + +IMPLEMENT_CO_NETOBJECT_V1(Prefab); + +ConsoleDocClass( Prefab, + "@brief A collection of arbitrary objects which can be allocated and manipulated as a group.\n\n" + + "%Prefab always points to a (.prefab) file which defines its objects. In " + "fact more than one %Prefab can reference this file and both will update " + "if the file is modified.\n\n" + + "%Prefab is a very simple object and only exists on the server. When it is " + "created it allocates children objects by reading the (.prefab) file like " + "a list of instructions. It then sets their transform relative to the %Prefab " + "and Torque networking handles the rest by ghosting the new objects to clients. " + "%Prefab itself is not ghosted.\n\n" + + "@ingroup enviroMisc" +); + +IMPLEMENT_CALLBACK( Prefab, onLoad, void, ( SimGroup *children ), ( children ), + "Called when the prefab file is loaded and children objects are created.\n" + "@param children SimGroup containing all children objects.\n" +); + +Prefab::Prefab() +{ + // Not ghosted unless we're editing + mNetFlags.clear(Ghostable); + + mTypeMask |= StaticObjectType; +} + +Prefab::~Prefab() +{ +} + +void Prefab::initPersistFields() +{ + addGroup( "Prefab" ); + + addProtectedField( "filename", TypePrefabFilename, Offset( mFilename, Prefab ), + &protectedSetFile, &defaultProtectedGetFn, + "(.prefab) File describing objects within this prefab." ); + + endGroup( "Prefab" ); + + Parent::initPersistFields(); +} + +extern bool gEditingMission; + +bool Prefab::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + mObjBox.set( Point3F( -0.5f, -0.5f, -0.5f ), + Point3F( 0.5f, 0.5f, 0.5f ) ); + + resetWorldBox(); + + // Not added to the scene unless we are editing. + if ( gEditingMission ) + onEditorEnable(); + + // Only the server-side prefab needs to create/update child objects. + // We rely on regular Torque ghosting of the individual child objects + // to take care of the rest. + if ( isServerObject() ) + { + _loadFile( true ); + _updateChildren(); + } + + return true; +} + +void Prefab::onRemove() +{ + if ( isServerObject() ) + _closeFile( true ); + + removeFromScene(); + Parent::onRemove(); +} + +void Prefab::onEditorEnable() +{ + if ( isClientObject() ) + return; + + // Just in case we are already in the scene, lets not cause an assert. + if ( mContainer != NULL ) + return; + + // Enable ghosting so we can see this on the client. + mNetFlags.set(Ghostable); + setScopeAlways(); + addToScene(); + + Parent::onEditorEnable(); +} + +void Prefab::onEditorDisable() +{ + if ( isClientObject() ) + return; + + // Just in case we are not in the scene, lets not cause an assert. + if ( mContainer == NULL ) + return; + + // Do not need this on the client if we are not editing. + removeFromScene(); + mNetFlags.clear(Ghostable); + clearScopeAlways(); + + Parent::onEditorDisable(); +} + +void Prefab::inspectPostApply() +{ + Parent::inspectPostApply(); +} + +void Prefab::setTransform(const MatrixF & mat) +{ + Parent::setTransform( mat ); + + if ( isServerObject() ) + { + setMaskBits( TransformMask ); + _updateChildren(); + } +} + +void Prefab::setScale(const VectorF & scale) +{ + Parent::setScale( scale ); + + if ( isServerObject() ) + { + setMaskBits( TransformMask ); + _updateChildren(); + } +} + +U32 Prefab::packUpdate( NetConnection *conn, U32 mask, BitStream *stream ) +{ + U32 retMask = Parent::packUpdate( conn, mask, stream ); + + mathWrite(*stream,mObjBox); + + if ( stream->writeFlag( mask & FileMask ) ) + { + stream->write( mFilename ); + } + + if ( stream->writeFlag( mask & TransformMask ) ) + { + mathWrite(*stream, getTransform()); + mathWrite(*stream, getScale()); + } + + return retMask; +} + +void Prefab::unpackUpdate(NetConnection *conn, BitStream *stream) +{ + Parent::unpackUpdate(conn, stream); + + mathRead(*stream, &mObjBox); + resetWorldBox(); + + // FileMask + if ( stream->readFlag() ) + { + stream->read( &mFilename ); + } + + // TransformMask + if ( stream->readFlag() ) + { + mathRead(*stream, &mObjToWorld); + mathRead(*stream, &mObjScale); + + setTransform( mObjToWorld ); + } +} + +bool Prefab::protectedSetFile( void *object, const char *index, const char *data ) +{ + Prefab *prefab = static_cast(object); + + String file = String( Platform::makeRelativePathName(data, Platform::getMainDotCsDir()) ); + + prefab->setFile( file ); + + return false; +} + +void Prefab::setFile( String file ) +{ + AssertFatal( isServerObject(), "Prefab-bad" ); + + if ( !isProperlyAdded() ) + { + mFilename = file; + return; + } + + // Client-side Prefab(s) do not create/update/reference children, everything + // is handled on the server-side. In normal usage this will never actually + // be called for the client-side prefab but maybe the user did so accidentally. + if ( isClientObject() ) + { + Con::errorf( "Prefab::setFile( %s ) - Should not be called on a client-side Prefab.", file.c_str() ); + return; + } + + _closeFile( true ); + + mFilename = file; + + if ( isProperlyAdded() ) + _loadFile( true ); +} + +SimGroup* Prefab::explode() +{ + SimGroup *missionGroup; + + if ( !Sim::findObject( "MissionGroup", missionGroup ) ) + { + Con::errorf( "Prefab::explode, MissionGroup was not found." ); + return NULL; + } + + if ( !mChildGroup ) + return NULL; + + SimGroup *group = mChildGroup; + Vector foundObjects; + + group->findObjectByType( foundObjects ); + + if ( foundObjects.empty() ) + return NULL; + + for ( S32 i = 0; i < foundObjects.size(); i++ ) + { + SceneObject *child = foundObjects[i]; + _updateChildTransform( child ); + smChildToPrefabMap.erase( child->getId() ); + } + + missionGroup->addObject(group); + mChildGroup = NULL; + mChildMap.clear(); + + return group; +} + +void Prefab::_closeFile( bool removeFileNotify ) +{ + AssertFatal( isServerObject(), "Prefab-bad" ); + + mChildMap.clear(); + + if ( mChildGroup ) + { + // Get a flat vector of all our children. + Vector foundObjects; + mChildGroup->findObjectByType( foundObjects ); + + // Remove them all from the ChildToPrefabMap. + for ( S32 i = 0; i < foundObjects.size(); i++ ) + smChildToPrefabMap.erase( foundObjects[i]->getId() ); + + mChildGroup->deleteObject(); + mChildGroup = NULL; + } + + if ( removeFileNotify ) + Torque::FS::RemoveChangeNotification( mFilename, this, &Prefab::_onFileChanged ); + + // Back to a default bounding box size. + mObjBox.set( Point3F( -0.5f, -0.5f, -0.5f ), Point3F( 0.5f, 0.5f, 0.5f ) ); + resetWorldBox(); +} + +void Prefab::_loadFile( bool addFileNotify ) +{ + AssertFatal( isServerObject(), "Prefab-bad" ); + + if ( mFilename.isEmpty() ) + return; + + if ( !Platform::isFile( mFilename ) ) + { + Con::errorf( "Prefab::_loadFile() - file %s was not found.", mFilename.c_str() ); + return; + } + + if ( sPrefabFileStack.contains(mFilename) ) + { + Con::errorf( + "Prefab::_loadFile - failed loading prefab file (%s). \n" + "File was referenced recursively by both a Parent and Child prefab.", mFilename.c_str() ); + return; + } + + sPrefabFileStack.push_back(mFilename); + + String command = String::ToString( "exec( \"%s\" );", mFilename.c_str() ); + Con::evaluate( command ); + + SimGroup *group; + if ( !Sim::findObject( Con::getVariable( "$ThisPrefab" ), group ) ) + { + Con::errorf( "Prefab::_loadFile() - file %s did not create $ThisPrefab.", mFilename.c_str() ); + return; + } + + if ( addFileNotify ) + Torque::FS::AddChangeNotification( mFilename, this, &Prefab::_onFileChanged ); + + mChildGroup = group; + + Vector foundObjects; + mChildGroup->findObjectByType( foundObjects ); + + if ( !foundObjects.empty() ) + { + mWorldBox = Box3F::Invalid; + + for ( S32 i = 0; i < foundObjects.size(); i++ ) + { + SceneObject *child = foundObjects[i]; + mChildMap.insert( child->getId(), Transform( child->getTransform(), child->getScale() ) ); + smChildToPrefabMap.insert( child->getId(), getId() ); + + _updateChildTransform( child ); + + mWorldBox.intersect( child->getWorldBox() ); + } + + resetObjectBox(); + } + + sPrefabFileStack.pop_back(); + + onLoad_callback( mChildGroup ); +} + +void Prefab::_updateChildTransform( SceneObject* child ) +{ + ChildToMatMap::Iterator itr = mChildMap.find(child->getId()); + AssertFatal( itr != mChildMap.end(), "Prefab, mChildMap out of synch with mChildGroup." ); + + MatrixF mat( itr->value.mat ); + Point3F pos = mat.getPosition(); + pos.convolve( mObjScale ); + mat.setPosition( pos ); + mat.mulL( mObjToWorld ); + + child->setTransform( mat ); + child->setScale( itr->value.scale * mObjScale ); + + // Hack for PhysicsShape... need to store the "editor" position to return to + // when a physics reset event occurs. Normally this would be where it is + // during onAdd, but in this case it is not because the prefab stores its + // child objects in object space... + + PhysicsShape *childPS = dynamic_cast( child ); + if ( childPS ) + childPS->storeRestorePos(); + +} + +void Prefab::_updateChildren() +{ + if ( !mChildGroup ) + return; + + Vector foundObjects; + mChildGroup->findObjectByType( foundObjects ); + + for ( S32 i = 0; i < foundObjects.size(); i++ ) + { + SceneObject *child = foundObjects[i]; + + _updateChildTransform( child ); + + if ( child->getClientObject() ) + { + ((SceneObject*)child->getClientObject())->setTransform( child->getTransform() ); + ((SceneObject*)child->getClientObject())->setScale( child->getScale() ); + } + } +} + +void Prefab::_onFileChanged( const Torque::Path &path ) +{ + AssertFatal( path == mFilename, "Prefab::_onFileChanged - path does not match filename." ); + + _closeFile(false); + _loadFile(false); + setMaskBits(U32_MAX); +} + +Prefab* Prefab::getPrefabByChild( SimObject *child ) +{ + ChildToPrefabMap::Iterator itr = smChildToPrefabMap.find( child->getId() ); + if ( itr == smChildToPrefabMap.end() ) + return NULL; + + Prefab *prefab; + if ( !Sim::findObject( itr->value, prefab ) ) + { + Con::errorf( "Prefab::getPrefabByChild - child object mapped to a prefab that no longer exists." ); + return NULL; + } + + return prefab; +} + +bool Prefab::isValidChild( SimObject *simobj, bool logWarnings ) +{ + if ( simobj->getName() && dStricmp(simobj->getName(),"MissionGroup") == 0 ) + { + if ( logWarnings ) + Con::warnf( "MissionGroup is not valid within a Prefab." ); + return false; + } + + if ( simobj->getClassRep()->isClass( AbstractClassRep::findClassRep("LevelInfo") ) ) + { + if ( logWarnings ) + Con::warnf( "LevelInfo objects are not valid within a Prefab" ); + return false; + } + + if ( simobj->getClassRep()->isClass( AbstractClassRep::findClassRep("TimeOfDay") ) ) + { + if ( logWarnings ) + Con::warnf( "TimeOfDay objects are not valid within a Prefab" ); + return false; + } + + SceneObject *sceneobj = dynamic_cast(simobj); + + if ( !sceneobj ) + return false; + + if ( sceneobj->isGlobalBounds() ) + { + if ( logWarnings ) + Con::warnf( "SceneObject's with global bounds are not valid within a Prefab." ); + return false; + } + + if ( sceneobj->getClassRep()->isClass( AbstractClassRep::findClassRep("TerrainBlock") ) ) + { + if ( logWarnings ) + Con::warnf( "TerrainBlock objects are not valid within a Prefab" ); + return false; + } + + if ( sceneobj->getClassRep()->isClass( AbstractClassRep::findClassRep("Player") ) ) + { + if ( logWarnings ) + Con::warnf( "Player objects are not valid within a Prefab" ); + return false; + } + + if ( sceneobj->getClassRep()->isClass( AbstractClassRep::findClassRep("DecalRoad") ) ) + { + if ( logWarnings ) + Con::warnf( "DecalRoad objects are not valid within a Prefab" ); + return false; + } + + return true; +} + +ExplodePrefabUndoAction::ExplodePrefabUndoAction( Prefab *prefab ) +: UndoAction( "Explode Prefab" ) +{ + mPrefabId = prefab->getId(); + mGroup = NULL; + + // Do the action. + redo(); +} + +void ExplodePrefabUndoAction::undo() +{ + if ( !mGroup ) + { + Con::errorf( "ExplodePrefabUndoAction::undo - NULL Group" ); + return; + } + + mGroup->deleteObject(); + mGroup = NULL; +} + +void ExplodePrefabUndoAction::redo() +{ + Prefab *prefab; + if ( !Sim::findObject( mPrefabId, prefab ) ) + { + Con::errorf( "ExplodePrefabUndoAction::redo - Prefab (%i) not found.", mPrefabId ); + return; + } + + mGroup = prefab->explode(); + + String name; + + if ( prefab->getName() && prefab->getName()[0] != '\0' ) + name = prefab->getName(); + else + name = "prefab"; + + name += "_exploded"; + name = Sim::getUniqueName( name ); + mGroup->assignName( name ); +} \ No newline at end of file diff --git a/Engine/source/T3D/prefab.h b/Engine/source/T3D/prefab.h new file mode 100644 index 000000000..ac61a7458 --- /dev/null +++ b/Engine/source/T3D/prefab.h @@ -0,0 +1,165 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PREFAB_H_ +#define _PREFAB_H_ + +#ifndef _SCENEOBJECT_H_ + #include "scene/sceneObject.h" +#endif +#ifndef _PATH_H_ + #include "core/util/path.h" +#endif +#ifndef _UNDO_H_ + #include "util/undo.h" +#endif +#ifndef _TDICTIONARY_H_ + #include "core/util/tDictionary.h" +#endif + + +class BaseMatInstance; + + +class Prefab : public SceneObject +{ + typedef SceneObject Parent; + + enum MaskBits + { + TransformMask = Parent::NextFreeMask << 0, + FileMask = Parent::NextFreeMask << 1, + NextFreeMask = Parent::NextFreeMask << 2 + }; + +public: + + Prefab(); + virtual ~Prefab(); + + DECLARE_CONOBJECT(Prefab); + + static void initPersistFields(); + + // SimObject + virtual bool onAdd(); + virtual void onRemove(); + virtual void onEditorEnable(); + virtual void onEditorDisable(); + virtual void inspectPostApply(); + + // NetObject + U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream ); + void unpackUpdate( NetConnection *conn, BitStream *stream ); + + // SceneObject + virtual void setTransform( const MatrixF &mat ); + virtual void setScale(const VectorF & scale); + + // Prefab + + /// If the passed object is a child of any Prefab return that Prefab. + /// Note that this call is only valid if the editor is open and when + /// passed server-side objects. + static Prefab* getPrefabByChild( SimObject *child ); + + /// Returns false if the passed object is of a type that is not allowed + /// as a child within a Prefab. + static bool isValidChild( SimObject *child, bool logWarnings ); + + /// + void render( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); + + /// + void setFile( String file ); + + /// Removes all children from this Prefab and puts them into a SimGroup + /// which is added to the MissionGroup and returned to the caller. + SimGroup* explode(); + +protected: + + void _closeFile( bool removeFileNotify ); + void _loadFile( bool addFileNotify ); + void _updateChildTransform( SceneObject* child ); + void _updateChildren(); + void _onFileChanged( const Torque::Path &path ); + + static bool protectedSetFile( void *object, const char *index, const char *data ); + + /// @name Callbacks + /// @{ + + DECLARE_CALLBACK( void, onLoad, ( SimGroup *children ) ); + + /// @} + +protected: + + /// Prefab file which defines our children objects. + String mFilename; + + /// Group which holds all children objects. + SimObjectPtr mChildGroup; + + /// Structure to keep track of child object initial transform and scale + struct Transform + { + MatrixF mat; + VectorF scale; + Transform() : mat(true), scale(Point3F::One) { } + Transform( const MatrixF& m, const VectorF& s ) : mat(m), scale(s) { } + }; + typedef Map ChildToMatMap; + + /// Lookup from a child object's id to its transform in + /// this Prefab's object space. + ChildToMatMap mChildMap; + + typedef Map ChildToPrefabMap; + + /// Lookup from a SimObject to its parent Prefab if it has one. + static ChildToPrefabMap smChildToPrefabMap; +}; + + +class ExplodePrefabUndoAction : public UndoAction +{ + typedef UndoAction Parent; + friend class WorldEditor; + +public: + + ExplodePrefabUndoAction( Prefab *prefab ); + + // UndoAction + virtual void undo(); + virtual void redo(); + +protected: + + SimGroup *mGroup; + SimObjectId mPrefabId; +}; + + +#endif // _PREFAB_H_ \ No newline at end of file diff --git a/Engine/source/T3D/projectile.cpp b/Engine/source/T3D/projectile.cpp new file mode 100644 index 000000000..9dddb8fde --- /dev/null +++ b/Engine/source/T3D/projectile.cpp @@ -0,0 +1,1450 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/projectile.h" + +#include "scene/sceneRenderState.h" +#include "scene/sceneManager.h" +#include "lighting/lightInfo.h" +#include "lighting/lightManager.h" +#include "console/consoleTypes.h" +#include "console/typeValidators.h" +#include "core/resourceManager.h" +#include "core/stream/bitStream.h" +#include "T3D/fx/explosion.h" +#include "T3D/shapeBase.h" +#include "ts/tsShapeInstance.h" +#include "sfx/sfxTrack.h" +#include "sfx/sfxSource.h" +#include "sfx/sfxSystem.h" +#include "sfx/sfxTypes.h" +#include "math/mathUtils.h" +#include "math/mathIO.h" +#include "sim/netConnection.h" +#include "T3D/fx/particleEmitter.h" +#include "T3D/fx/splash.h" +#include "T3D/physics/physicsPlugin.h" +#include "T3D/physics/physicsWorld.h" +#include "gfx/gfxTransformSaver.h" +#include "T3D/containerQuery.h" +#include "T3D/decal/decalManager.h" +#include "T3D/decal/decalData.h" +#include "T3D/lightDescription.h" +#include "console/engineAPI.h" + + +IMPLEMENT_CO_DATABLOCK_V1(ProjectileData); + +ConsoleDocClass( ProjectileData, + "@brief Stores properties for an individual projectile type.\n" + + "@tsexample\n" + "datablock ProjectileData(GrenadeLauncherProjectile)\n" + "{\n" + " projectileShapeName = \"art/shapes/weapons/SwarmGun/rocket.dts\";\n" + "directDamage = 30;\n" + "radiusDamage = 30;\n" + "damageRadius = 5;\n" + "areaImpulse = 2000;\n" + + "explosion = GrenadeLauncherExplosion;\n" + "waterExplosion = GrenadeLauncherWaterExplosion;\n" + + "decal = ScorchRXDecal;\n" + "splash = GrenadeSplash;\n" + + "particleEmitter = GrenadeProjSmokeTrailEmitter;\n" + "particleWaterEmitter = GrenadeTrailWaterEmitter;\n" + + "muzzleVelocity = 30;\n" + "velInheritFactor = 0.3;\n" + + "armingDelay = 2000;\n" + "lifetime = 10000;\n" + "fadeDelay = 4500;\n" + + "bounceElasticity = 0.4;\n" + "bounceFriction = 0.3;\n" + "isBallistic = true;\n" + "gravityMod = 0.9;\n" + + "lightDesc = GrenadeLauncherLightDesc;\n" + + "damageType = \"GrenadeDamage\";\n" + "};\n" + "@endtsexample\n" + + "@ingroup gameObjects\n" +); + +IMPLEMENT_CO_NETOBJECT_V1(Projectile); + +ConsoleDocClass( Projectile, + "@brief Base projectile class. Uses the ProjectileData class for properties of individual projectiles.\n" + "@ingroup gameObjects\n" +); + +IMPLEMENT_CALLBACK( ProjectileData, onExplode, void, ( Projectile* proj, Point3F pos, F32 fade ), + ( proj, pos, fade ), + "@brief Called when a projectile explodes.\n\n" + "This function is only called on server objects.\n" + "@param proj The exploding projectile.\n" + "@param pos The position of the explosion.\n" + "@param fade The current fadeValue of the projectile, affects its visibility.\n\n" + "@see Projectile\n" + ); + +IMPLEMENT_CALLBACK( ProjectileData, onCollision, void, ( Projectile* proj, SceneObject* col, F32 fade, Point3F pos, Point3F normal ), + ( proj, col, fade, pos, normal ), + "@brief Called when a projectile collides with another object.\n\n" + "This function is only called on server objects." + "@param proj The projectile colliding with SceneObject col.\n" + "@param col The SceneObject hit by the projectile.\n" + "@param fade The current fadeValue of the projectile, affects its visibility.\n" + "@param pos The position of the collision.\n" + "@param normal The normal of the collision.\n" + "@see Projectile\n" + ); + +const U32 Projectile::csmStaticCollisionMask = TerrainObjectType | + InteriorObjectType | + StaticShapeObjectType; + +const U32 Projectile::csmDynamicCollisionMask = PlayerObjectType | + VehicleObjectType; + +const U32 Projectile::csmDamageableMask = Projectile::csmDynamicCollisionMask; + +U32 Projectile::smProjectileWarpTicks = 5; + + +//-------------------------------------------------------------------------- +// +ProjectileData::ProjectileData() +{ + projectileShapeName = NULL; + + sound = NULL; + + explosion = NULL; + explosionId = 0; + + waterExplosion = NULL; + waterExplosionId = 0; + + //hasLight = false; + //lightRadius = 1; + //lightColor.set(1, 1, 1); + lightDesc = NULL; + + faceViewer = false; + scale.set( 1.0f, 1.0f, 1.0f ); + + isBallistic = false; + + velInheritFactor = 1.0f; + muzzleVelocity = 50; + impactForce = 0.0f; + + armingDelay = 0; + fadeDelay = 20000 / 32; + lifetime = 20000 / 32; + + activateSeq = -1; + maintainSeq = -1; + + gravityMod = 1.0; + bounceElasticity = 0.999f; + bounceFriction = 0.3f; + + particleEmitter = NULL; + particleEmitterId = 0; + + particleWaterEmitter = NULL; + particleWaterEmitterId = 0; + + splash = NULL; + splashId = 0; + + decal = NULL; + decalId = 0; + + lightDesc = NULL; + lightDescId = 0; +} + +//-------------------------------------------------------------------------- + +void ProjectileData::initPersistFields() +{ + addField("particleEmitter", TYPEID< ParticleEmitterData >(), Offset(particleEmitter, ProjectileData), + "@brief Particle emitter datablock used to generate particles while the projectile is outside of water.\n\n" + "@note If datablocks are defined for both particleEmitter and particleWaterEmitter, both effects will play " + "as the projectile enters or leaves water.\n\n" + "@see particleWaterEmitter\n"); + addField("particleWaterEmitter", TYPEID< ParticleEmitterData >(), Offset(particleWaterEmitter, ProjectileData), + "@brief Particle emitter datablock used to generate particles while the projectile is submerged in water.\n\n" + "@note If datablocks are defined for both particleWaterEmitter and particleEmitter , both effects will play " + "as the projectile enters or leaves water.\n\n" + "@see particleEmitter\n"); + + addField("projectileShapeName", TypeShapeFilename, Offset(projectileShapeName, ProjectileData), + "@brief File path to the model of the projectile.\n\n"); + addField("scale", TypePoint3F, Offset(scale, ProjectileData), + "@brief Scale to apply to the projectile's size.\n\n" + "@note This is applied after SceneObject::scale\n"); + + addField("sound", TypeSFXTrackName, Offset(sound, ProjectileData), + "@brief SFXTrack datablock used to play sounds while in flight.\n\n"); + + addField("explosion", TYPEID< ExplosionData >(), Offset(explosion, ProjectileData), + "@brief Explosion datablock used when the projectile explodes outside of water.\n\n"); + addField("waterExplosion", TYPEID< ExplosionData >(), Offset(waterExplosion, ProjectileData), + "@brief Explosion datablock used when the projectile explodes underwater.\n\n"); + + addField("splash", TYPEID< SplashData >(), Offset(splash, ProjectileData), + "@brief Splash datablock used to create splash effects as the projectile enters or leaves water\n\n"); + + addField("decal", TYPEID< DecalData >(), Offset(decal, ProjectileData), + "@brief Decal datablock used for decals placed at projectile explosion points.\n\n"); + + addField("lightDesc", TYPEID< LightDescription >(), Offset(lightDesc, ProjectileData), + "@brief LightDescription datablock used for lights attached to the projectile.\n\n"); + + addField("isBallistic", TypeBool, Offset(isBallistic, ProjectileData), + "@brief Detetmines if the projectile should be affected by gravity and whether or not " + "it bounces before exploding.\n\n"); + + addField("velInheritFactor", TypeF32, Offset(velInheritFactor, ProjectileData), + "@brief Amount of velocity the projectile recieves from the source that created it.\n\n" + "Use an amount between 0 and 1 for the best effect. " + "This value is never modified by the engine.\n" + "@note This value by default is not transmitted between the server and the client."); + addField("muzzleVelocity", TypeF32, Offset(muzzleVelocity, ProjectileData), + "@brief Amount of velocity the projectile recieves from the \"muzzle\" of the gun.\n\n" + "Used with velInheritFactor to determine the initial velocity of the projectile. " + "This value is never modified by the engine.\n\n" + "@note This value by default is not transmitted between the server and the client.\n\n" + "@see velInheritFactor"); + + addField("impactForce", TypeF32, Offset(impactForce, ProjectileData)); + + addProtectedField("lifetime", TypeS32, Offset(lifetime, ProjectileData), &setLifetime, &getScaledValue, + "@brief Amount of time, in milliseconds, before the projectile is removed from the simulation.\n\n" + "Used with fadeDelay to determine the transparency of the projectile at a given time. " + "A projectile may exist up to a maximum of 131040ms (or 4095 ticks) as defined by Projectile::MaxLivingTicks in the source code." + "@see fadeDelay"); + + addProtectedField("armingDelay", TypeS32, Offset(armingDelay, ProjectileData), &setArmingDelay, &getScaledValue, + "@brief Amount of time, in milliseconds, before the projectile will cause damage or explode on impact.\n\n" + "This value must be equal to or less than the projectile's lifetime.\n\n" + "@see lifetime"); + addProtectedField("fadeDelay", TypeS32, Offset(fadeDelay, ProjectileData), &setFadeDelay, &getScaledValue, + "@brief Amount of time, in milliseconds, before the projectile begins to fade out.\n\n" + "This value must be smaller than the projectile's lifetime to have an affect."); + + addField("bounceElasticity", TypeF32, Offset(bounceElasticity, ProjectileData), + "@brief Influences post-bounce velocity of a projectile that does not explode on contact.\n\n" + "Scales the velocity from a bounce after friction is taken into account. " + "A value of 1.0 will leave it's velocity unchanged while values greater than 1.0 will increase it.\n"); + addField("bounceFriction", TypeF32, Offset(bounceFriction, ProjectileData), + "@brief Factor to reduce post-bounce velocity of a projectile that does not explode on contact.\n\n" + "Reduces bounce velocity by this factor and a multiple of the tangent to impact. " + "Used to simulate surface friction.\n"); + addField("gravityMod", TypeF32, Offset(gravityMod, ProjectileData ), + "@brief Scales the influence of gravity on the projectile.\n\n" + "The larger this value is, the more that gravity will affect the projectile. " + "A value of 1.0 will assume \"normal\" influence upon it.\n" + "The magnitude of gravity is assumed to be 9.81 m/s/s\n\n" + "@note ProjectileData::isBallistic must be true for this to have any affect."); + + Parent::initPersistFields(); +} + + +//-------------------------------------------------------------------------- +bool ProjectileData::onAdd() +{ + if(!Parent::onAdd()) + return false; + + return true; +} + + +bool ProjectileData::preload(bool server, String &errorStr) +{ + if (Parent::preload(server, errorStr) == false) + return false; + + if( !server ) + { + if (!particleEmitter && particleEmitterId != 0) + if (Sim::findObject(particleEmitterId, particleEmitter) == false) + Con::errorf(ConsoleLogEntry::General, "ProjectileData::preload: Invalid packet, bad datablockId(particleEmitter): %d", particleEmitterId); + + if (!particleWaterEmitter && particleWaterEmitterId != 0) + if (Sim::findObject(particleWaterEmitterId, particleWaterEmitter) == false) + Con::errorf(ConsoleLogEntry::General, "ProjectileData::preload: Invalid packet, bad datablockId(particleWaterEmitter): %d", particleWaterEmitterId); + + if (!explosion && explosionId != 0) + if (Sim::findObject(explosionId, explosion) == false) + Con::errorf(ConsoleLogEntry::General, "ProjectileData::preload: Invalid packet, bad datablockId(explosion): %d", explosionId); + + if (!waterExplosion && waterExplosionId != 0) + if (Sim::findObject(waterExplosionId, waterExplosion) == false) + Con::errorf(ConsoleLogEntry::General, "ProjectileData::preload: Invalid packet, bad datablockId(waterExplosion): %d", waterExplosionId); + + if (!splash && splashId != 0) + if (Sim::findObject(splashId, splash) == false) + Con::errorf(ConsoleLogEntry::General, "ProjectileData::preload: Invalid packet, bad datablockId(splash): %d", splashId); + + if (!decal && decalId != 0) + if (Sim::findObject(decalId, decal) == false) + Con::errorf(ConsoleLogEntry::General, "ProjectileData::preload: Invalid packet, bad datablockId(decal): %d", decalId); + + String errorStr; + if( !sfxResolve( &sound, errorStr ) ) + Con::errorf(ConsoleLogEntry::General, "ProjectileData::preload: Invalid packet: %s", errorStr.c_str()); + + if (!lightDesc && lightDescId != 0) + if (Sim::findObject(lightDescId, lightDesc) == false) + Con::errorf(ConsoleLogEntry::General, "ProjectileData::preload: Invalid packet, bad datablockid(lightDesc): %d", lightDescId); + } + + if (projectileShapeName && projectileShapeName[0] != '\0') + { + projectileShape = ResourceManager::get().load(projectileShapeName); + if (bool(projectileShape) == false) + { + errorStr = String::ToString("ProjectileData::load: Couldn't load shape \"%s\"", projectileShapeName); + return false; + } + activateSeq = projectileShape->findSequence("activate"); + maintainSeq = projectileShape->findSequence("maintain"); + } + + if (bool(projectileShape)) // create an instance to preload shape data + { + TSShapeInstance* pDummy = new TSShapeInstance(projectileShape, !server); + delete pDummy; + } + + return true; +} + +//-------------------------------------------------------------------------- +void ProjectileData::packData(BitStream* stream) +{ + Parent::packData(stream); + + stream->writeString(projectileShapeName); + stream->writeFlag(faceViewer); + if(stream->writeFlag(scale.x != 1 || scale.y != 1 || scale.z != 1)) + { + stream->write(scale.x); + stream->write(scale.y); + stream->write(scale.z); + } + + if (stream->writeFlag(particleEmitter != NULL)) + stream->writeRangedU32(particleEmitter->getId(), DataBlockObjectIdFirst, + DataBlockObjectIdLast); + + if (stream->writeFlag(particleWaterEmitter != NULL)) + stream->writeRangedU32(particleWaterEmitter->getId(), DataBlockObjectIdFirst, + DataBlockObjectIdLast); + + if (stream->writeFlag(explosion != NULL)) + stream->writeRangedU32(explosion->getId(), DataBlockObjectIdFirst, + DataBlockObjectIdLast); + + if (stream->writeFlag(waterExplosion != NULL)) + stream->writeRangedU32(waterExplosion->getId(), DataBlockObjectIdFirst, + DataBlockObjectIdLast); + + if (stream->writeFlag(splash != NULL)) + stream->writeRangedU32(splash->getId(), DataBlockObjectIdFirst, + DataBlockObjectIdLast); + + if (stream->writeFlag(decal != NULL)) + stream->writeRangedU32(decal->getId(), DataBlockObjectIdFirst, + DataBlockObjectIdLast); + + sfxWrite( stream, sound ); + + if ( stream->writeFlag(lightDesc != NULL)) + stream->writeRangedU32(lightDesc->getId(), DataBlockObjectIdFirst, + DataBlockObjectIdLast); + + stream->write(impactForce); + +// stream->writeRangedU32(lifetime, 0, Projectile::MaxLivingTicks); +// stream->writeRangedU32(armingDelay, 0, Projectile::MaxLivingTicks); +// stream->writeRangedU32(fadeDelay, 0, Projectile::MaxLivingTicks); + + // [tom, 3/21/2007] Changing these to write all 32 bits as the previous + // code limited these to a max value of 4095. + stream->write(lifetime); + stream->write(armingDelay); + stream->write(fadeDelay); + + if(stream->writeFlag(isBallistic)) + { + stream->write(gravityMod); + stream->write(bounceElasticity); + stream->write(bounceFriction); + } + +} + +void ProjectileData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + + projectileShapeName = stream->readSTString(); + + faceViewer = stream->readFlag(); + if(stream->readFlag()) + { + stream->read(&scale.x); + stream->read(&scale.y); + stream->read(&scale.z); + } + else + scale.set(1,1,1); + + if (stream->readFlag()) + particleEmitterId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); + + if (stream->readFlag()) + particleWaterEmitterId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); + + if (stream->readFlag()) + explosionId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); + + if (stream->readFlag()) + waterExplosionId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); + + if (stream->readFlag()) + splashId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); + + if (stream->readFlag()) + decalId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); + + sfxRead( stream, &sound ); + + if (stream->readFlag()) + lightDescId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); + + // [tom, 3/21/2007] See comment in packData() +// lifetime = stream->readRangedU32(0, Projectile::MaxLivingTicks); +// armingDelay = stream->readRangedU32(0, Projectile::MaxLivingTicks); +// fadeDelay = stream->readRangedU32(0, Projectile::MaxLivingTicks); + + stream->read(&impactForce); + + stream->read(&lifetime); + stream->read(&armingDelay); + stream->read(&fadeDelay); + + isBallistic = stream->readFlag(); + if(isBallistic) + { + stream->read(&gravityMod); + stream->read(&bounceElasticity); + stream->read(&bounceFriction); + } +} + +bool ProjectileData::setLifetime( void *obj, const char *index, const char *data ) +{ + S32 value = dAtoi(data); + value = scaleValue(value); + + ProjectileData *object = static_cast(obj); + object->lifetime = value; + + return false; +} + +bool ProjectileData::setArmingDelay( void *obj, const char *index, const char *data ) +{ + S32 value = dAtoi(data); + value = scaleValue(value); + + ProjectileData *object = static_cast(obj); + object->armingDelay = value; + + return false; +} + +bool ProjectileData::setFadeDelay( void *obj, const char *index, const char *data ) +{ + S32 value = dAtoi(data); + value = scaleValue(value); + + ProjectileData *object = static_cast(obj); + object->fadeDelay = value; + + return false; +} + +const char *ProjectileData::getScaledValue( void *obj, const char *data) +{ + + S32 value = dAtoi(data); + value = scaleValue(value, false); + + String stringData = String::ToString(value); + char *strBuffer = Con::getReturnBuffer(stringData.size()); + dMemcpy( strBuffer, stringData, stringData.size() ); + + return strBuffer; +} + +S32 ProjectileData::scaleValue( S32 value, bool down ) +{ + S32 minV = 0; + S32 maxV = Projectile::MaxLivingTicks; + + // scale down to ticks before we validate + if( down ) + value /= TickMs; + + if(value < minV || value > maxV) + { + Con::errorf("ProjectileData::scaleValue(S32 value = %d, bool down = %b) - Scaled value must be between %d and %d", value, down, minV, maxV); + if(value < minV) + value = minV; + else if(value > maxV) + value = maxV; + } + + // scale up from ticks after we validate + if( !down ) + value *= TickMs; + + return value; +} + +//-------------------------------------------------------------------------- +//-------------------------------------- +// +Projectile::Projectile() + : mPhysicsWorld( NULL ), + mCurrPosition( 0, 0, 0 ), + mCurrVelocity( 0, 0, 1 ), + mSourceObjectId( -1 ), + mSourceObjectSlot( -1 ), + mCurrTick( 0 ), + mParticleEmitter( NULL ), + mParticleWaterEmitter( NULL ), + mSound( NULL ), + mProjectileShape( NULL ), + mActivateThread( NULL ), + mMaintainThread( NULL ), + mHasExploded( false ), + mFadeValue( 1.0f ) +{ + // Todo: ScopeAlways? + mNetFlags.set(Ghostable); + mTypeMask |= ProjectileObjectType | LightObjectType | DynamicShapeObjectType; + + mLight = LightManager::createLightInfo(); + mLight->setType( LightInfo::Point ); + + mLightState.clear(); + mLightState.setLightInfo( mLight ); +} + +Projectile::~Projectile() +{ + SAFE_DELETE(mLight); + + delete mProjectileShape; + mProjectileShape = NULL; +} + +//-------------------------------------------------------------------------- +void Projectile::initPersistFields() +{ + addGroup("Physics"); + + addProtectedField("initialPosition", TypePoint3F, Offset(mInitialPosition, Projectile), &_setInitialPosition, &defaultProtectedGetFn, + "@brief Starting position for the projectile.\n\n"); + //addField("initialPosition", TypePoint3F, Offset(mCurrPosition, Projectile), + // "@brief Starting position for the projectile.\n\n"); + addProtectedField("initialVelocity", TypePoint3F, Offset(mInitialVelocity, Projectile), &_setInitialVelocity, &defaultProtectedGetFn, + "@brief Starting velocity for the projectile.\n\n"); + //addField("initialVelocity", TypePoint3F, Offset(mCurrVelocity, Projectile), + // "@brief Starting velocity for the projectile.\n\n"); + + endGroup("Physics"); + + addGroup("Source"); + + addField("sourceObject", TypeS32, Offset(mSourceObjectId, Projectile), + "@brief ID number of the object that fired the projectile.\n\n" + "@note If the projectile was fired by a WeaponImage, sourceObject will be " + "the object that owns the WeaponImage. This is usually the player."); + addField("sourceSlot", TypeS32, Offset(mSourceObjectSlot, Projectile), + "@brief The sourceObject's weapon slot that the projectile originates from.\n\n"); + + endGroup("Source"); + + + Parent::initPersistFields(); +} + +bool Projectile::_setInitialPosition( void *object, const char *index, const char *data ) +{ + Projectile* p = static_cast( object ); + if ( p ) + { + Point3F pos; + + S32 count = dSscanf( data, "%f %f %f", + &pos.x, &pos.y, &pos.z); + + if ( (count != 3) ) + { + Con::printf("Projectile: Failed to parse initial position \"px py pz\" from '%s'", data); + return false; + } + + p->setInitialPosition( pos ); + } + return false; +} + +void Projectile::setInitialPosition( const Point3F& pos ) +{ + mInitialPosition = pos; + mCurrPosition = pos; +} + +bool Projectile::_setInitialVelocity( void *object, const char *index, const char *data ) +{ + Projectile* p = static_cast( object ); + if ( p ) + { + Point3F vel; + + S32 count = dSscanf( data, "%f %f %f", + &vel.x, &vel.y, &vel.z); + + if ( (count != 3) ) + { + Con::printf("Projectile: Failed to parse initial velocity \"vx vy vz\" from '%s'", data); + return false; + } + + p->setInitialVelocity( vel ); + } + return false; +} + +void Projectile::setInitialVelocity( const Point3F& vel ) +{ + mInitialVelocity = vel; + mCurrVelocity = vel; +} + +//-------------------------------------------------------------------------- + +bool Projectile::calculateImpact(float, + Point3F& pointOfImpact, + float& impactTime) +{ + Con::warnf(ConsoleLogEntry::General, "Projectile::calculateImpact: Should never be called"); + + impactTime = 0; + pointOfImpact.set(0, 0, 0); + return false; +} + + +//-------------------------------------------------------------------------- +F32 Projectile::getUpdatePriority(CameraScopeQuery *camInfo, U32 updateMask, S32 updateSkips) +{ + F32 ret = Parent::getUpdatePriority(camInfo, updateMask, updateSkips); + // if the camera "owns" this object, it should have a slightly higher priority + if(mSourceObject == camInfo->camera) + return ret + 0.2; + return ret; +} + +bool Projectile::onAdd() +{ + if(!Parent::onAdd()) + return false; + + if (isServerObject()) + { + ShapeBase* ptr; + if (Sim::findObject(mSourceObjectId, ptr)) + { + mSourceObject = ptr; + + // Since we later do processAfter( mSourceObject ) we must clearProcessAfter + // if it is deleted. SceneObject already handles this in onDeleteNotify so + // all we need to do is register for the notification. + deleteNotify( ptr ); + } + else + { + if (mSourceObjectId != -1) + Con::errorf(ConsoleLogEntry::General, "Projectile::onAdd: mSourceObjectId is invalid"); + mSourceObject = NULL; + } + + // If we're on the server, we need to inherit some of our parent's velocity + // + mCurrTick = 0; + } + else + { + if (bool(mDataBlock->projectileShape)) + { + mProjectileShape = new TSShapeInstance(mDataBlock->projectileShape, isClientObject()); + + if (mDataBlock->activateSeq != -1) + { + mActivateThread = mProjectileShape->addThread(); + mProjectileShape->setTimeScale(mActivateThread, 1); + mProjectileShape->setSequence(mActivateThread, mDataBlock->activateSeq, 0); + } + } + if (mDataBlock->particleEmitter != NULL) + { + ParticleEmitter* pEmitter = new ParticleEmitter; + pEmitter->onNewDataBlock(mDataBlock->particleEmitter,false); + if (pEmitter->registerObject() == false) + { + Con::warnf(ConsoleLogEntry::General, "Could not register particle emitter for particle of class: %s", mDataBlock->getName()); + delete pEmitter; + pEmitter = NULL; + } + mParticleEmitter = pEmitter; + } + + if (mDataBlock->particleWaterEmitter != NULL) + { + ParticleEmitter* pEmitter = new ParticleEmitter; + pEmitter->onNewDataBlock(mDataBlock->particleWaterEmitter,false); + if (pEmitter->registerObject() == false) + { + Con::warnf(ConsoleLogEntry::General, "Could not register particle emitter for particle of class: %s", mDataBlock->getName()); + delete pEmitter; + pEmitter = NULL; + } + mParticleWaterEmitter = pEmitter; + } + } + if (mSourceObject.isValid()) + processAfter(mSourceObject); + + // Setup our bounding box + if (bool(mDataBlock->projectileShape) == true) + mObjBox = mDataBlock->projectileShape->bounds; + else + mObjBox = Box3F(Point3F(0, 0, 0), Point3F(0, 0, 0)); + + MatrixF initialTransform( true ); + initialTransform.setPosition( mCurrPosition ); + setTransform( initialTransform ); // calls resetWorldBox + + addToScene(); + + if ( PHYSICSMGR ) + mPhysicsWorld = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" ); + + return true; +} + + +void Projectile::onRemove() +{ + if( !mParticleEmitter.isNull() ) + { + mParticleEmitter->deleteWhenEmpty(); + mParticleEmitter = NULL; + } + + if( !mParticleWaterEmitter.isNull() ) + { + mParticleWaterEmitter->deleteWhenEmpty(); + mParticleWaterEmitter = NULL; + } + + SFX_DELETE( mSound ); + + removeFromScene(); + Parent::onRemove(); +} + + +bool Projectile::onNewDataBlock( GameBaseData *dptr, bool reload ) +{ + mDataBlock = dynamic_cast( dptr ); + if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) ) + return false; + + if ( isGhost() ) + { + // Create the sound ahead of time. This reduces runtime + // costs and makes the system easier to understand. + + SFX_DELETE( mSound ); + + if ( mDataBlock->sound ) + mSound = SFX->createSource( mDataBlock->sound ); + } + + return true; +} + +void Projectile::submitLights( LightManager *lm, bool staticLighting ) +{ + if ( staticLighting || mHasExploded || !mDataBlock->lightDesc ) + return; + + mDataBlock->lightDesc->submitLight( &mLightState, getRenderTransform(), lm, this ); +} + +bool Projectile::pointInWater(const Point3F &point) +{ + // This is pretty much a hack so we can use the existing ContainerQueryInfo + // and findObject router. + + // We only care if we intersect with water at all + // so build a box at the point that has only 1 z extent. + // And test if water coverage is anything other than zero. + + Box3F boundsBox( point, point ); + boundsBox.maxExtents.z += 1.0f; + + ContainerQueryInfo info; + info.box = boundsBox; + info.mass = 0.0f; + + // Find and retreive physics info from intersecting WaterObject(s) + if(mContainer != NULL) + { + mContainer->findObjects( boundsBox, WaterObjectType, findRouter, &info ); + } + else + { + // Handle special case where the projectile has exploded prior to having + // called onAdd() on the client. This occurs when the projectile on the + // server is created and then explodes in the same network update tick. + // On the client end in NetConnection::ghostReadPacket() the ghost is + // created and then Projectile::unpackUpdate() is called prior to the + // projectile being registered. Within unpackUpdate() the explosion + // is triggered, but without being registered onAdd() isn't called and + // the container is not set. As all we're doing is checking if the + // given explosion point is within water, we should be able to use the + // global container here. We could likely always get away with this, + // but using the actual defined container when possible is the right + // thing to do. DAW + AssertFatal(isClientObject(), "Server projectile has not been properly added"); + gClientContainer.findObjects( boundsBox, WaterObjectType, findRouter, &info ); + } + + return ( info.waterCoverage > 0.0f ); +} + +//---------------------------------------------------------------------------- + +void Projectile::emitParticles(const Point3F& from, const Point3F& to, const Point3F& vel, const U32 ms) +{ + if ( mHasExploded ) + return; + + Point3F axis = -vel; + + if( axis.isZero() ) + axis.set( 0.0, 0.0, 1.0 ); + else + axis.normalize(); + + bool fromWater = pointInWater(from); + bool toWater = pointInWater(to); + + if (!fromWater && !toWater && bool(mParticleEmitter)) // not in water + mParticleEmitter->emitParticles(from, to, axis, vel, ms); + else if (fromWater && toWater && bool(mParticleWaterEmitter)) // in water + mParticleWaterEmitter->emitParticles(from, to, axis, vel, ms); + else if (!fromWater && toWater && mDataBlock->splash) // entering water + { + // cast the ray to get the surface point of the water + RayInfo rInfo; + if (gClientContainer.castRay(from, to, WaterObjectType, &rInfo)) + { + MatrixF trans = getTransform(); + trans.setPosition(rInfo.point); + + Splash *splash = new Splash(); + splash->onNewDataBlock(mDataBlock->splash, false); + splash->setTransform(trans); + splash->setInitialState(trans.getPosition(), Point3F(0.0, 0.0, 1.0)); + if (!splash->registerObject()) + { + delete splash; + splash = NULL; + } + + // create an emitter for the particles out of water and the particles in water + if (mParticleEmitter) + mParticleEmitter->emitParticles(from, rInfo.point, axis, vel, ms); + + if (mParticleWaterEmitter) + mParticleWaterEmitter->emitParticles(rInfo.point, to, axis, vel, ms); + } + } + else if (fromWater && !toWater && mDataBlock->splash) // leaving water + { + // cast the ray in the opposite direction since that point is out of the water, otherwise + // we hit water immediately and wont get the appropriate surface point + RayInfo rInfo; + if (gClientContainer.castRay(to, from, WaterObjectType, &rInfo)) + { + MatrixF trans = getTransform(); + trans.setPosition(rInfo.point); + + Splash *splash = new Splash(); + splash->onNewDataBlock(mDataBlock->splash,false); + splash->setTransform(trans); + splash->setInitialState(trans.getPosition(), Point3F(0.0, 0.0, 1.0)); + if (!splash->registerObject()) + { + delete splash; + splash = NULL; + } + + // create an emitter for the particles out of water and the particles in water + if (mParticleEmitter) + mParticleEmitter->emitParticles(rInfo.point, to, axis, vel, ms); + + if (mParticleWaterEmitter) + mParticleWaterEmitter->emitParticles(from, rInfo.point, axis, vel, ms); + } + } +} + +void Projectile::explode( const Point3F &p, const Point3F &n, const U32 collideType ) +{ + // Make sure we don't explode twice... + if ( mHasExploded ) + return; + + mHasExploded = true; + + // Move the explosion point slightly off the surface to avoid problems with radius damage + Point3F explodePos = p + n * 0.001f; + + if ( isServerObject() ) + { + // Do what the server needs to do, damage the surrounding objects, etc. + mExplosionPosition = explodePos; + mExplosionNormal = n; + mCollideHitType = collideType; + + mDataBlock->onExplode_callback( this, mExplosionPosition, mFadeValue ); + + setMaskBits(ExplosionMask); + + // Just wait till the timeout to self delete. This + // gives server object time to get ghosted to the client. + } + else + { + // Client just plays the explosion at the right place... + // + Explosion* pExplosion = NULL; + + if (mDataBlock->waterExplosion && pointInWater(p)) + { + pExplosion = new Explosion; + pExplosion->onNewDataBlock(mDataBlock->waterExplosion, false); + } + else + if (mDataBlock->explosion) + { + pExplosion = new Explosion; + pExplosion->onNewDataBlock(mDataBlock->explosion, false); + } + + if( pExplosion ) + { + MatrixF xform(true); + xform.setPosition(explodePos); + pExplosion->setTransform(xform); + pExplosion->setInitialState(explodePos, n); + pExplosion->setCollideType( collideType ); + if (pExplosion->registerObject() == false) + { + Con::errorf(ConsoleLogEntry::General, "Projectile(%s)::explode: couldn't register explosion", + mDataBlock->getName() ); + delete pExplosion; + pExplosion = NULL; + } + } + + // Client (impact) decal. + if ( mDataBlock->decal ) + gDecalManager->addDecal( p, n, 0.0f, mDataBlock->decal ); + + // Client object + updateSound(); + } + + /* + // Client and Server both should apply forces to PhysicsWorld objects + // within the explosion. + if ( false && mPhysicsWorld ) + { + F32 force = 200.0f; + mPhysicsWorld->explosion( p, 15.0f, force ); + } + */ +} + +void Projectile::updateSound() +{ + if (!mDataBlock->sound) + return; + + if ( mSound ) + { + if ( mHasExploded ) + mSound->stop(); + else + { + if ( !mSound->isPlaying() ) + mSound->play(); + + mSound->setVelocity( getVelocity() ); + mSound->setTransform( getRenderTransform() ); + } + } +} + +void Projectile::processTick( const Move *move ) +{ + Parent::processTick( move ); + mCurrTick++; + + simulate( TickSec ); +} + +void Projectile::simulate( F32 dt ) +{ + if ( isServerObject() && mCurrTick >= mDataBlock->lifetime ) + { + deleteObject(); + return; + } + + if ( mHasExploded ) + return; + + // ... otherwise, we have to do some simulation work. + RayInfo rInfo; + Point3F oldPosition; + Point3F newPosition; + + oldPosition = mCurrPosition; + if ( mDataBlock->isBallistic ) + mCurrVelocity.z -= 9.81 * mDataBlock->gravityMod * dt; + + newPosition = oldPosition + mCurrVelocity * dt; + + // disable the source objects collision reponse for a short time while we + // determine if the projectile is capable of moving from the old position + // to the new position, otherwise we'll hit ourself + bool disableSourceObjCollision = (mSourceObject.isValid() && mCurrTick <= SourceIdTimeoutTicks); + if ( disableSourceObjCollision ) + mSourceObject->disableCollision(); + disableCollision(); + + // Determine if the projectile is going to hit any object between the previous + // position and the new position. This code is executed both on the server + // and on the client (for prediction purposes). It is possible that the server + // will have registered a collision while the client prediction has not. If this + // happens the client will be corrected in the next packet update. + + // Raycast the abstract PhysicsWorld if a PhysicsPlugin exists. + bool hit = false; + + if ( mPhysicsWorld ) + hit = mPhysicsWorld->castRay( oldPosition, newPosition, &rInfo, Point3F( newPosition - oldPosition) * mDataBlock->impactForce ); + else + hit = getContainer()->castRay(oldPosition, newPosition, csmDynamicCollisionMask | csmStaticCollisionMask, &rInfo); + + if ( hit ) + { + // make sure the client knows to bounce + if ( isServerObject() && ( rInfo.object->getTypeMask() & csmStaticCollisionMask ) == 0 ) + setMaskBits( BounceMask ); + + // Next order of business: do we explode on this hit? + if ( mCurrTick > mDataBlock->armingDelay || mDataBlock->armingDelay == 0 ) + { + MatrixF xform( true ); + xform.setColumn( 3, rInfo.point ); + setTransform( xform ); + mCurrPosition = rInfo.point; + mCurrVelocity = Point3F::Zero; + + // Get the object type before the onCollision call, in case + // the object is destroyed. + U32 objectType = rInfo.object->getTypeMask(); + + // re-enable the collision response on the source object since + // we need to process the onCollision and explode calls + if ( disableSourceObjCollision ) + mSourceObject->enableCollision(); + + // Ok, here is how this works: + // onCollision is called to notify the server scripts that a collision has occurred, then + // a call to explode is made to start the explosion process. The call to explode is made + // twice, once on the server and once on the client. + // The server process is responsible for two things: + // 1) setting the ExplosionMask network bit to guarantee that the client calls explode + // 2) initiate the explosion process on the server scripts + // The client process is responsible for only one thing: + // 1) drawing the appropriate explosion + + // It is possible that during the processTick the server may have decided that a hit + // has occurred while the client prediction has decided that a hit has not occurred. + // In this particular scenario the client will have failed to call onCollision and + // explode during the processTick. However, the explode function will be called + // during the next packet update, due to the ExplosionMask network bit being set. + // onCollision will remain uncalled on the client however, therefore no client + // specific code should be placed inside the function! + onCollision( rInfo.point, rInfo.normal, rInfo.object ); + explode( rInfo.point, rInfo.normal, objectType ); + + // break out of the collision check, since we've exploded + // we don't want to mess with the position and velocity + } + else + { + if ( mDataBlock->isBallistic ) + { + // Otherwise, this represents a bounce. First, reflect our velocity + // around the normal... + Point3F bounceVel = mCurrVelocity - rInfo.normal * (mDot( mCurrVelocity, rInfo.normal ) * 2.0); + mCurrVelocity = bounceVel; + + // Add in surface friction... + Point3F tangent = bounceVel - rInfo.normal * mDot(bounceVel, rInfo.normal); + mCurrVelocity -= tangent * mDataBlock->bounceFriction; + + // Now, take elasticity into account for modulating the speed of the grenade + mCurrVelocity *= mDataBlock->bounceElasticity; + + // Set the new position to the impact and the bounce + // will apply on the next frame. + //F32 timeLeft = 1.0f - rInfo.t; + newPosition = oldPosition = rInfo.point + rInfo.normal * 0.05f; + } + } + } + + // re-enable the collision response on the source object now + // that we are done processing the ballistic movement + if ( disableSourceObjCollision ) + mSourceObject->enableCollision(); + enableCollision(); + + if ( isClientObject() ) + { + emitParticles( mCurrPosition, newPosition, mCurrVelocity, U32( dt * 1000.0f ) ); + updateSound(); + } + + mCurrDeltaBase = newPosition; + mCurrBackDelta = mCurrPosition - newPosition; + mCurrPosition = newPosition; + + MatrixF xform( true ); + xform.setColumn( 3, mCurrPosition ); + setTransform( xform ); +} + + +void Projectile::advanceTime(F32 dt) +{ + Parent::advanceTime(dt); + + if ( mHasExploded || dt == 0.0) + return; + + if (mActivateThread && + mProjectileShape->getDuration(mActivateThread) > mProjectileShape->getTime(mActivateThread) + dt) + { + mProjectileShape->advanceTime(dt, mActivateThread); + } + else + { + + if (mMaintainThread) + { + mProjectileShape->advanceTime(dt, mMaintainThread); + } + else if (mActivateThread && mDataBlock->maintainSeq != -1) + { + mMaintainThread = mProjectileShape->addThread(); + mProjectileShape->setTimeScale(mMaintainThread, 1); + mProjectileShape->setSequence(mMaintainThread, mDataBlock->maintainSeq, 0); + mProjectileShape->advanceTime(dt, mMaintainThread); + } + } +} + +void Projectile::interpolateTick(F32 delta) +{ + Parent::interpolateTick(delta); + + if( mHasExploded ) + return; + + Point3F interpPos = mCurrDeltaBase + mCurrBackDelta * delta; + Point3F dir = mCurrVelocity; + if(dir.isZero()) + dir.set(0,0,1); + else + dir.normalize(); + + MatrixF xform(true); + xform = MathUtils::createOrientFromDir(dir); + xform.setPosition(interpPos); + setRenderTransform(xform); + + // fade out the projectile image + S32 time = (S32)(mCurrTick - delta); + if(time > mDataBlock->fadeDelay) + { + F32 fade = F32(time - mDataBlock->fadeDelay); + mFadeValue = 1.0 - (fade / F32(mDataBlock->lifetime)); + } + else + mFadeValue = 1.0; + + updateSound(); +} + + + +//-------------------------------------------------------------------------- +void Projectile::onCollision(const Point3F& hitPosition, const Point3F& hitNormal, SceneObject* hitObject) +{ + // No client specific code should be placed or branched from this function + if(isClientObject()) + return; + + if (hitObject != NULL && isServerObject()) + { + mDataBlock->onCollision_callback( this, hitObject, mFadeValue, hitPosition, hitNormal ); + } +} + +//-------------------------------------------------------------------------- +U32 Projectile::packUpdate( NetConnection *con, U32 mask, BitStream *stream ) +{ + U32 retMask = Parent::packUpdate( con, mask, stream ); + + const bool isInitalUpdate = mask & GameBase::InitialUpdateMask; + + // InitialUpdateMask + if ( stream->writeFlag( isInitalUpdate ) ) + { + stream->writeRangedU32( mCurrTick, 0, MaxLivingTicks ); + + if ( mSourceObject.isValid() ) + { + // Potentially have to write this to the client, let's make sure it has a + // ghost on the other side... + S32 ghostIndex = con->getGhostIndex( mSourceObject ); + if ( stream->writeFlag( ghostIndex != -1 ) ) + { + stream->writeRangedU32( U32(ghostIndex), + 0, + NetConnection::MaxGhostCount ); + + stream->writeRangedU32( U32(mSourceObjectSlot), + 0, + ShapeBase::MaxMountedImages - 1 ); + } + else + // have not recieved the ghost for the source object yet, try again later + retMask |= GameBase::InitialUpdateMask; + } + else + stream->writeFlag( false ); + } + + // ExplosionMask + // + // ExplosionMask will be set during the initial update but hidden is + // only true if we have really exploded. + if ( stream->writeFlag( ( mask & ExplosionMask ) && mHasExploded ) ) + { + mathWrite(*stream, mExplosionPosition); + mathWrite(*stream, mExplosionNormal); + stream->write(mCollideHitType); + } + + // BounceMask + if ( stream->writeFlag( mask & BounceMask ) ) + { + // Bounce against dynamic object + mathWrite(*stream, mCurrPosition); + mathWrite(*stream, mCurrVelocity); + } + + return retMask; +} + +void Projectile::unpackUpdate(NetConnection* con, BitStream* stream) +{ + Parent::unpackUpdate(con, stream); + + if ( stream->readFlag() ) // InitialUpdateMask + { + mCurrTick = stream->readRangedU32( 0, MaxLivingTicks ); + if ( stream->readFlag() ) + { + mSourceObjectId = stream->readRangedU32( 0, NetConnection::MaxGhostCount ); + mSourceObjectSlot = stream->readRangedU32( 0, ShapeBase::MaxMountedImages - 1 ); + + NetObject* pObject = con->resolveGhost( mSourceObjectId ); + if ( pObject != NULL ) + mSourceObject = dynamic_cast( pObject ); + } + else + { + mSourceObjectId = -1; + mSourceObjectSlot = -1; + mSourceObject = NULL; + } + } + + if ( stream->readFlag() ) // ExplosionMask + { + Point3F explodePoint; + Point3F explodeNormal; + mathRead( *stream, &explodePoint ); + mathRead( *stream, &explodeNormal ); + stream->read( &mCollideHitType ); + + // start the explosion visuals + explode( explodePoint, explodeNormal, mCollideHitType ); + } + + if ( stream->readFlag() ) // BounceMask + { + Point3F pos; + mathRead( *stream, &pos ); + mathRead( *stream, &mCurrVelocity ); + + mCurrDeltaBase = pos; + mCurrBackDelta = mCurrPosition - pos; + mCurrPosition = pos; + setPosition( mCurrPosition ); + } +} + +//-------------------------------------------------------------------------- +void Projectile::prepRenderImage( SceneRenderState* state ) +{ + if (mHasExploded || mFadeValue <= (1.0/255.0)) + return; + + if ( mDataBlock->lightDesc ) + { + mDataBlock->lightDesc->prepRender( state, &mLightState, getRenderTransform() ); + } + + /* + if ( mFlareData ) + { + mFlareState.fullBrightness = mDataBlock->lightDesc->mBrightness; + mFlareState.scale = mFlareScale; + mFlareState.lightInfo = mLight; + mFlareState.lightMat = getTransform(); + + mFlareData->prepRender( state, &mFlareState ); + } + */ + + prepBatchRender( state ); +} + +void Projectile::prepBatchRender( SceneRenderState *state ) +{ + if ( !mProjectileShape ) + return; + + GFXTransformSaver saver; + + // Set up our TS render state. + TSRenderState rdata; + rdata.setSceneState( state ); + + // We might have some forward lit materials + // so pass down a query to gather lights. + LightQuery query; + query.init( getWorldSphere() ); + rdata.setLightQuery( &query ); + + MatrixF mat = getRenderTransform(); + mat.scale( mObjScale ); + mat.scale( mDataBlock->scale ); + GFX->setWorldMatrix( mat ); + + mProjectileShape->setDetailFromPosAndScale( state, mat.getPosition(), mObjScale ); + mProjectileShape->animate(); + + mProjectileShape->render( rdata ); +} + +DefineEngineMethod(Projectile, presimulate, void, (F32 seconds), (1.0f), + "@brief Updates the projectile's positional and collision information.\n\n" + "This function will first delete the projectile if it is a server object and is outside it's ProjectileData::lifetime. " + "Also responsible for applying gravity, determining collisions, triggering explosions, " + "emitting trail particles, and calculating bounces if necessary." + "@param seconds Amount of time, in seconds since the simulation's start, to advance.\n" + "@tsexample\n" + "// Tell the projectile to process a simulation event, and provide the amount of time\n" + "// that has passed since the simulation began.\n" + "%seconds = 2.0;\n" + "%projectile.presimulate(%seconds);\n" + "@endtsexample\n" + "@note This function is not called if the SimObject::hidden is true.") +{ + object->simulate( seconds ); +} diff --git a/Engine/source/T3D/projectile.h b/Engine/source/T3D/projectile.h new file mode 100644 index 000000000..fa7c9ffbf --- /dev/null +++ b/Engine/source/T3D/projectile.h @@ -0,0 +1,285 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PROJECTILE_H_ +#define _PROJECTILE_H_ + +#ifndef _GAMEBASE_H_ +#include "T3D/gameBase/gameBase.h" +#endif +#ifndef __RESOURCE_H__ +#include "core/resource.h" +#endif +#ifndef _TSSHAPE_H_ +#include "ts/tsShape.h" +#endif +#ifndef _LIGHTDESCRIPTION_H_ +#include "T3D/lightDescription.h" +#endif +#ifndef _LIGHTINFO_H_ +#include "lighting/lightInfo.h" +#endif + + +class ExplosionData; +class SplashData; +class ShapeBase; +class TSShapeInstance; +class TSThread; +class PhysicsWorld; +class DecalData; +class LightDescription; +class SFXTrack; +class SFXSource; +class ParticleEmitterData; +class ParticleEmitter; +class Projectile; + +//-------------------------------------------------------------------------- +/// Datablock for projectiles. This class is the base class for all other projectiles. +class ProjectileData : public GameBaseData +{ + typedef GameBaseData Parent; + +protected: + bool onAdd(); + +public: + // variables set in datablock definition: + // Shape related + const char* projectileShapeName; + + /// Set to true if it is a billboard and want it to always face the viewer, false otherwise + bool faceViewer; + Point3F scale; + + + /// [0,1] scale of how much velocity should be inherited from the parent object + F32 velInheritFactor; + /// Speed of the projectile when fired + F32 muzzleVelocity; + + /// Force imparted on a hit object. + F32 impactForce; + + /// Should it arc? + bool isBallistic; + + /// How HIGH should it bounce (parallel to normal), [0,1] + F32 bounceElasticity; + /// How much momentum should be lost when it bounces (perpendicular to normal), [0,1] + F32 bounceFriction; + + /// Should this projectile fall/rise different than a default object? + F32 gravityMod; + + /// How long the projectile should exist before deleting itself + U32 lifetime; // all times are internally represented as ticks + /// How long it should not detonate on impact + S32 armingDelay; // the values are converted on initialization with + S32 fadeDelay; // the IRangeValidatorScaled field validator + + ExplosionData* explosion; + S32 explosionId; + + ExplosionData* waterExplosion; // Water Explosion Datablock + S32 waterExplosionId; // Water Explosion ID + + SplashData* splash; // Water Splash Datablock + S32 splashId; // Water splash ID + + DecalData *decal; // (impact) Decal Datablock + S32 decalId; // (impact) Decal ID + + SFXTrack* sound; // Projectile Sound + + LightDescription *lightDesc; + S32 lightDescId; + + // variables set on preload: + Resource projectileShape; + S32 activateSeq; + S32 maintainSeq; + + ParticleEmitterData* particleEmitter; + S32 particleEmitterId; + + ParticleEmitterData* particleWaterEmitter; + S32 particleWaterEmitterId; + + ProjectileData(); + + void packData(BitStream*); + void unpackData(BitStream*); + bool preload(bool server, String &errorStr); + + static bool setLifetime( void *object, const char *index, const char *data ); + static bool setArmingDelay( void *object, const char *index, const char *data ); + static bool setFadeDelay( void *object, const char *index, const char *data ); + static const char *getScaledValue( void *obj, const char *data); + static S32 scaleValue( S32 value, bool down = true ); + + static void initPersistFields(); + DECLARE_CONOBJECT(ProjectileData); + + + DECLARE_CALLBACK( void, onExplode, ( Projectile* proj, Point3F pos, F32 fade ) ); + DECLARE_CALLBACK( void, onCollision, ( Projectile* proj, SceneObject* col, F32 fade, Point3F pos, Point3F normal ) ); +}; + + +//-------------------------------------------------------------------------- +/// Base class for all projectiles. +class Projectile : public GameBase, public ISceneLight +{ + typedef GameBase Parent; + + static bool _setInitialPosition( void* object, const char* index, const char* data ); + static bool _setInitialVelocity( void* object, const char* index, const char* data ); + +public: + + // Initial conditions + enum ProjectileConstants { + SourceIdTimeoutTicks = 7, // = 231 ms + DeleteWaitTime = 500, ///< 500 ms delete timeout (for network transmission delays) + ExcessVelDirBits = 7, + MaxLivingTicks = 4095, + }; + enum UpdateMasks { + BounceMask = Parent::NextFreeMask, + ExplosionMask = Parent::NextFreeMask << 1, + NextFreeMask = Parent::NextFreeMask << 2 + }; + + + Projectile(); + ~Projectile(); + + DECLARE_CONOBJECT(Projectile); + + // SimObject + bool onAdd(); + void onRemove(); + static void initPersistFields(); + + // NetObject + F32 getUpdatePriority(CameraScopeQuery *focusObject, U32 updateMask, S32 updateSkips); + U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream); + void unpackUpdate(NetConnection *conn, BitStream *stream); + + // SceneObject + Point3F getVelocity() const { return mCurrVelocity; } + void processTick( const Move *move ); + void advanceTime( F32 dt ); + void interpolateTick( F32 delta ); + + // GameBase + bool onNewDataBlock( GameBaseData *dptr, bool reload ); + + // Rendering + void prepRenderImage( SceneRenderState *state ); + void prepBatchRender( SceneRenderState *state ); + + /// Updates velocity and position, and performs collision testing. + void simulate( F32 dt ); + + /// What to do once this projectile collides with something + virtual void onCollision(const Point3F& p, const Point3F& n, SceneObject*); + + /// What to do when this projectile explodes + virtual void explode(const Point3F& p, const Point3F& n, const U32 collideType ); + + bool pointInWater(const Point3F &point); + + void emitParticles(const Point3F&, const Point3F&, const Point3F&, const U32); + + void updateSound(); + + virtual bool calculateImpact( float simTime, + Point3F &pointOfImpact, + float &impactTime ); + + void setInitialPosition( const Point3F& pos ); + void setInitialVelocity( const Point3F& vel ); + +protected: + + static const U32 csmStaticCollisionMask; + static const U32 csmDynamicCollisionMask; + static const U32 csmDamageableMask; + static U32 smProjectileWarpTicks; + + PhysicsWorld *mPhysicsWorld; + + ProjectileData* mDataBlock; + + SimObjectPtr< ParticleEmitter > mParticleEmitter; + SimObjectPtr< ParticleEmitter > mParticleWaterEmitter; + + SFXSource* mSound; + + // These two are server-side only + Point3F mInitialPosition; + Point3F mInitialVelocity; + + Point3F mCurrPosition; + Point3F mCurrVelocity; + S32 mSourceObjectId; + S32 mSourceObjectSlot; + + // Time related variables common to all projectiles, managed by processTick + U32 mCurrTick; ///< Current time in ticks + SimObjectPtr mSourceObject; ///< Actual pointer to the source object, times out after SourceIdTimeoutTicks + + // Rendering related variables + TSShapeInstance* mProjectileShape; + TSThread* mActivateThread; + TSThread* mMaintainThread; + + // ISceneLight + virtual void submitLights( LightManager *lm, bool staticLighting ); + virtual LightInfo* getLight() { return mLight; } + + LightInfo *mLight; + LightState mLightState; + + bool mHasExploded; ///< Prevent rendering, lighting, and duplicate explosions. + F32 mFadeValue; ///< set in processTick, interpolation between fadeDelay and lifetime + ///< in data block + + // Warping and back delta variables. Only valid on the client + // + Point3F mWarpStart; + Point3F mWarpEnd; + U32 mWarpTicksRemaining; + + Point3F mCurrDeltaBase; + Point3F mCurrBackDelta; + + Point3F mExplosionPosition; + Point3F mExplosionNormal; + U32 mCollideHitType; +}; + +#endif // _PROJECTILE_H_ + diff --git a/Engine/source/T3D/proximityMine.cpp b/Engine/source/T3D/proximityMine.cpp new file mode 100644 index 000000000..c37947a16 --- /dev/null +++ b/Engine/source/T3D/proximityMine.cpp @@ -0,0 +1,681 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "core/stream/bitStream.h" +#include "console/console.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "sim/netConnection.h" +#include "math/mMath.h" +#include "math/mathIO.h" +#include "math/mathUtils.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/gfxDrawUtil.h" +#include "sfx/sfxTrack.h" +#include "sfx/sfxTypes.h" +#include "sfx/sfxSystem.h" +#include "ts/tsShapeInstance.h" +#include "T3D/fx/explosion.h" +#include "T3D/proximityMine.h" +#include "T3D/physics/physicsBody.h" + +const U32 sTriggerCollisionMask = ( VehicleObjectType | PlayerObjectType ); + +static S32 gAutoDeleteTicks = 16; // Provides about half a second for all clients to be updated + // before a mine is deleted on the server. + +//---------------------------------------------------------------------------- + +IMPLEMENT_CO_DATABLOCK_V1( ProximityMineData ); + +ConsoleDocClass( ProximityMineData, + "@brief Stores common properties for a ProximityMine.\n\n" + "@see ProximityMine\n" + "@ingroup gameObjects\n" +); + +IMPLEMENT_CALLBACK( ProximityMineData, onTriggered, void, ( ProximityMine* obj, SceneObject *target ),( obj, target ), + "Callback invoked when an object triggers the ProximityMine.\n\n" + "@param obj The ProximityMine object\n" + "@param target The object that triggered the mine\n" + "@note This callback is only invoked on the server.\n" + "@see ProximityMine\n" +); + +IMPLEMENT_CALLBACK( ProximityMineData, onExplode, void, ( ProximityMine* obj, Point3F pos ),( obj, pos ), + "Callback invoked when a ProximityMine is about to explode.\n\n" + "@param obj The ProximityMine object\n" + "@param pos The position of the mine explosion\n" + "@note This callback is only invoked on the server.\n" + "@see ProximityMine\n" +); + +ProximityMineData::ProximityMineData() + : armingDelay( 0 ), + armingSequence( -1 ), + armingSound( NULL ), + triggerRadius( 5.0f ), + triggerSpeed( 1.0f ), + autoTriggerDelay( 0 ), + triggerOnOwner( false ), + triggerDelay( 0 ), + triggerSequence( -1 ), + triggerSound( NULL ), + explosionOffset( 0.05f ) +{ +} + +void ProximityMineData::initPersistFields() +{ + addGroup( "Arming" ); + addField( "armingDelay", TypeF32, Offset(armingDelay, ProximityMineData), + "Delay (in seconds) from when the mine is placed to when it becomes active." ); + addField( "armingSound", TypeSFXTrackName, Offset(armingSound, ProximityMineData), + "Sound to play when the mine is armed (starts at the same time as " + "the armed sequence if defined)." ); + endGroup( "Arming" ); + + addGroup( "Triggering" ); + addField( "autoTriggerDelay", TypeF32, Offset(autoTriggerDelay, ProximityMineData), + "@brief Delay (in seconds) from arming until the mine automatically " + "triggers and explodes, even if no object has entered the trigger area.\n\n" + "Set to 0 to disable." ); + addField( "triggerOnOwner", TypeBool, Offset(triggerOnOwner, ProximityMineData), + "@brief Controls whether the mine can be triggered by the object that owns it.\n\n" + "For example, a player could deploy mines that are only dangerous to other " + "players and not himself." ); + addField( "triggerRadius", TypeF32, Offset(triggerRadius, ProximityMineData), + "Distance at which an activated mine will detect other objects and explode." ); + addField( "triggerSpeed", TypeF32, Offset(triggerSpeed, ProximityMineData), + "Speed above which moving objects within the trigger radius will trigger the mine" ); + addField( "triggerDelay", TypeF32, Offset(triggerDelay, ProximityMineData), + "Delay (in seconds) from when the mine is triggered until it explodes." ); + addField( "triggerSound", TypeSFXTrackName, Offset(triggerSound, ProximityMineData), + "Sound to play when the mine is triggered (starts at the same time as " + "the triggered sequence if defined)." ); + endGroup( "Triggering" ); + + addGroup( "Explosion" ); + addField( "explosionOffset", TypeF32, Offset(explosionOffset, ProximityMineData), + "@brief Offset from the mine's origin where the explosion emanates from." + "Sometimes a thrown mine may be slightly sunk into the ground. This can be just " + "enough to cause the explosion to occur under the ground, especially on flat " + "ground, which can end up blocking the explosion. This offset along the mine's " + "'up' normal allows you to raise the explosion origin to a better height."); + endGroup( "Explosion" ); + + Parent::initPersistFields(); +} + +bool ProximityMineData::preload( bool server, String& errorStr ) +{ + if ( Parent::preload( server, errorStr ) == false ) + return false; + + if ( !server ) + { + // Resolve sounds + String errorStr; + if( !sfxResolve( &armingSound, errorStr ) ) + Con::errorf( ConsoleLogEntry::General, "ProximityMineData::preload: Invalid packet: %s", errorStr.c_str() ); + if( !sfxResolve( &triggerSound, errorStr ) ) + Con::errorf( ConsoleLogEntry::General, "ProximityMineData::preload: Invalid packet: %s", errorStr.c_str() ); + } + + if ( mShape ) + { + // Lookup animation sequences + armingSequence = mShape->findSequence( "armed" ); + triggerSequence = mShape->findSequence( "triggered" ); + } + + return true; +} + +void ProximityMineData::packData( BitStream* stream ) +{ + Parent::packData( stream ); + + stream->write( armingDelay ); + sfxWrite( stream, armingSound ); + + stream->write( autoTriggerDelay ); + stream->writeFlag( triggerOnOwner ); + stream->write( triggerRadius ); + stream->write( triggerSpeed ); + stream->write( triggerDelay ); + sfxWrite( stream, triggerSound ); +} + +void ProximityMineData::unpackData( BitStream* stream ) +{ + Parent::unpackData(stream); + + stream->read( &armingDelay ); + sfxRead( stream, &armingSound ); + + stream->read( &autoTriggerDelay ); + triggerOnOwner = stream->readFlag(); + stream->read( &triggerRadius ); + stream->read( &triggerSpeed ); + stream->read( &triggerDelay ); + sfxRead( stream, &triggerSound ); +} + +//---------------------------------------------------------------------------- + +IMPLEMENT_CO_NETOBJECT_V1( ProximityMine ); + +ConsoleDocClass( ProximityMine, + "@brief A simple proximity mine.\n\n" + + "Proximity mines can be deployed using the world editor or thrown by an " + "in-game object. Once armed, any Player or Vehicle object that moves within " + "the mine's trigger area will cause it to explode.\n\n" + + "Internally, the ProximityMine object transitions through the following states:\n" + "
        \n" + "
      1. Thrown: Mine has been thrown, but has not yet attached to a surface
      2. \n" + "
      3. Deployed: Mine has attached to a surface but is not yet armed. Start " + "playing the #armingSound and armed sequence.
      4. \n" + "
      5. Armed: Mine is armed and will trigger if a Vehicle or Player object moves " + "within the trigger area.
      6. \n" + "
      7. Triggered: Mine has been triggered and will explode soon. Invoke the " + "onTriggered callback, and start playing the #triggerSound and triggered " + "sequence.
      8. \n" + "
      9. Exploded: Mine has exploded and will be deleted on the server shortly. " + "Invoke the onExplode callback on the server and generate the explosion effects " + "on the client.
      10. \n" + "
      \n\n" + + "@note Proximity mines with the #static field set to true will start in the " + "Armed state. Use this for mines placed with the World Editor.\n\n" + + "The shape used for the mine may optionally define the following sequences:\n" + "
      \n" + "
      armed
      Sequence to play when the mine is deployed, but before " + "it becomes active and triggerable (#armingDelay should be set appropriately).
      \n" + "
      triggered
      Sequence to play when the mine is triggered, just " + "before it explodes (#triggerDelay should be set appropriately).
      \n" + "
      \n\n" + + "@tsexample\n" + "datablock ProximityMineData( SimpleMine )\n" + "{\n" + " // ShapeBaseData fields\n" + " category = \"Weapon\";\n" + " shapeFile = \"art/shapes/weapons/misc/proximityMine.dts\";\n\n" + + " // ItemData fields\n" + " sticky = true;\n\n" + + " // ProximityMineData fields\n" + " armingDelay = 0.5;\n" + " armingSound = MineArmedSound;\n\n" + + " autoTriggerDelay = 0;\n" + " triggerOnOwner = true;\n" + " triggerRadius = 5.0;\n" + " triggerSpeed = 1.0;\n" + " triggerDelay = 0.5;\n" + " triggerSound = MineTriggeredSound;\n" + " explosion = RocketLauncherExplosion;\n\n" + + " // dynamic fields\n" + " pickUpName = \"Proximity Mines\";\n" + " maxInventory = 20;\n\n" + + " damageType = \"MineDamage\"; // type of damage applied to objects in radius\n" + " radiusDamage = 30; // amount of damage to apply to objects in radius\n" + " damageRadius = 8; // search radius to damage objects when exploding\n" + " areaImpulse = 2000; // magnitude of impulse to apply to objects in radius\n" + "};\n\n" + + "function ProximityMineData::onTriggered( %this, %obj, %target )\n" + "{\n" + " echo( %this.name SPC \"triggered by \" @ %target.getClassName() );\n" + "}\n\n" + + "function ProximityMineData::onExplode( %this, %obj, %position )\n" + "{\n" + " // Damage objects within the mine's damage radius\n" + " if ( %this.damageRadius > 0 )\n" + " radiusDamage( %obj.sourceObject, %position, %this.damageRadius, %this.radiusDamage, %this.damageType, %this.areaImpulse );\n" + "}\n\n" + + "function ProximityMineData::damage( %this, %obj, %position, %source, %amount, %damageType )\n" + "{\n" + " // Explode if any damage is applied to the mine\n" + " %obj.schedule(50 + getRandom(50), explode);\n" + "}\n\n" + + "%obj = new ProximityMine()\n" + "{\n" + " dataBlock = SimpleMine;\n" + "};\n" + "@endtsexample\n\n" + + "@see ProximityMineData\n" + + "@ingroup gameObjects\n" +); + +ProximityMine::ProximityMine() +{ + mTypeMask |= StaticShapeObjectType; + mDataBlock = 0; + + mStickyCollisionPos.zero(); + + mOwner = NULL; + + mState = Thrown; + mStateTimeout = 0; + + mAnimThread = NULL; + + // For the Item class + mSubclassItemHandlesScene = true; +} + +ProximityMine::~ProximityMine() +{ +} + +//---------------------------------------------------------------------------- + +void ProximityMine::consoleInit() +{ + Parent::consoleInit(); + + Con::addVariable("$ProxMine::autoDeleteTicks", TypeS32, &gAutoDeleteTicks, + "@brief Number of ticks until an exploded mine is deleted on the server.\n\n" + + "After a mine has exploded it remains in the server's scene graph for a time " + "to allow its exploded state to be passed along to each client. This variable " + "controls how long a mine remains before it is deleted. Any client that has not " + "received the exploded state by then (perhaps due to lag) will not see any " + "explosion produced by the mine.\n\n" + + "@ingroup GameObjects"); +} + +//---------------------------------------------------------------------------- + +bool ProximityMine::onAdd() +{ + if ( !Parent::onAdd() || !mDataBlock ) + return false; + + addToScene(); + + if (isServerObject()) + scriptOnAdd(); + + if ( mStatic ) + { + // static mines are armed immediately + mState = Deployed; + mStateTimeout = 0; + } + + return true; +} + +bool ProximityMine::onNewDataBlock( GameBaseData* dptr, bool reload ) +{ + mDataBlock = dynamic_cast( dptr ); + if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) ) + return false; + + scriptOnNewDataBlock(); + return true; +} + +void ProximityMine::onRemove() +{ + scriptOnRemove(); + removeFromScene(); + + Parent::onRemove(); +} + +//---------------------------------------------------------------------------- + +void ProximityMine::setTransform( const MatrixF& mat ) +{ + ShapeBase::setTransform( mat ); // Skip Item::setTransform as it restricts rotation to the Z axis + + if ( !mStatic ) + { + mAtRest = false; + mAtRestCounter = 0; + } + + if ( mPhysicsRep ) + mPhysicsRep->setTransform( getTransform() ); + + setMaskBits( Item::RotationMask | Item::PositionMask | Item::NoWarpMask ); +} + +void ProximityMine::setDeployedPos( const Point3F& pos, const Point3F& normal ) +{ + // Align to deployed surface normal + MatrixF mat( true ); + MathUtils::getMatrixFromUpVector( normal, &mat ); + mat.setPosition( pos + normal * mObjBox.minExtents.z ); + + delta.pos = pos; + delta.posVec.set(0, 0, 0); + + ShapeBase::setTransform( mat ); + if ( mPhysicsRep ) + mPhysicsRep->setTransform( getTransform() ); + + setMaskBits( DeployedMask ); +} + +void ProximityMine::processTick( const Move* move ) +{ + Parent::processTick( move ); + + // Process state machine + mStateTimeout -= TickSec; + + State lastState = NumStates;; + while ( mState != lastState ) + { + lastState = mState; + switch ( mState ) + { + case Thrown: + if ( mAtRest ) + { + mState = Deployed; + mStateTimeout = mDataBlock->armingDelay; + + // Get deployed position if mine was not stuck to another surface + if ( mStickyCollisionPos.isZero() ) + { + mObjToWorld.getColumn( 2, &mStickyCollisionNormal ); + mObjToWorld.getColumn( 3, &mStickyCollisionPos ); + } + setDeployedPos( mStickyCollisionPos, mStickyCollisionNormal ); + + if ( mDataBlock->armingSequence != -1 ) + { + mAnimThread = mShapeInstance->addThread(); + mShapeInstance->setSequence( mAnimThread, mDataBlock->armingSequence, 0.0f ); + } + if ( mDataBlock->armingSound ) + SFX->playOnce( mDataBlock->armingSound, &getRenderTransform() ); + } + break; + + case Deployed: + // Timeout into Armed state + if ( mStateTimeout <= 0 ) + { + mState = Armed; + mStateTimeout = mDataBlock->autoTriggerDelay ? mDataBlock->autoTriggerDelay : F32_MAX; + } + break; + + case Armed: + { + // Check for objects within the trigger area + Box3F triggerBox( mDataBlock->triggerRadius * 2 ); + triggerBox.setCenter( getTransform().getPosition() ); + + SimpleQueryList sql; + getContainer()->findObjects( triggerBox, sTriggerCollisionMask, + SimpleQueryList::insertionCallback, &sql ); + for ( int i = 0; i < sql.mList.size(); i++ ) + { + // Detect movement in the trigger area + if ( ( sql.mList[i] == mOwner && !mDataBlock->triggerOnOwner ) || + sql.mList[i]->getVelocity().len() < mDataBlock->triggerSpeed ) + continue; + + // Mine has been triggered + mShapeInstance->destroyThread( mAnimThread ); + mAnimThread = NULL; + + mState = Triggered; + mStateTimeout = mDataBlock->triggerDelay; + if ( mDataBlock->triggerSequence != -1 ) + { + mAnimThread = mShapeInstance->addThread(); + mShapeInstance->setSequence( mAnimThread, mDataBlock->triggerSequence, 0.0f ); + } + if ( mDataBlock->triggerSound ) + SFX->playOnce( mDataBlock->triggerSound, &getRenderTransform() ); + + if ( isServerObject() ) + mDataBlock->onTriggered_callback( this, sql.mList[0] ); + } + break; + } + + case Triggered: + // Timeout into exploded state + if ( mStateTimeout <= 0 ) + { + explode(); + } + break; + + case Exploded: + // Mine's delete themselves on the server after exploding + if ( isServerObject() && ( mStateTimeout <= 0 ) ) + { + deleteObject(); + return; + } + break; + } + } +} + +//---------------------------------------------------------------------------- + +void ProximityMine::explode() +{ + // Make sure we don't explode twice + if ( mState == Exploded ) + { + return; + } + + mState = Exploded; + mStateTimeout = TickSec * gAutoDeleteTicks; // auto-delete on server N ticks after exploding + + // Move the explosion point slightly off the surface to avoid problems with radius damage + Point3F normal = getTransform().getUpVector(); + Point3F explodePos = getTransform().getPosition() + normal * mDataBlock->explosionOffset; + + if ( isServerObject() ) + { + // Do what the server needs to do, damage the surrounding objects, etc. + mDataBlock->onExplode_callback( this, explodePos ); + setMaskBits( ExplosionMask ); + + // Wait till the timeout to self delete. This gives the server object time + // to get ghosted to the client + } + else + { + // Client just plays the explosion effect at the right place + if ( mDataBlock->explosion ) + { + Explosion *pExplosion = new Explosion; + pExplosion->onNewDataBlock( mDataBlock->explosion, false ); + + MatrixF xform( true ); + xform.setPosition( explodePos ); + pExplosion->setTransform( xform ); + pExplosion->setInitialState( explodePos, normal ); + pExplosion->setCollideType( sTriggerCollisionMask ); + if ( pExplosion->registerObject() == false ) + { + Con::errorf( ConsoleLogEntry::General, "ProximityMine(%s)::explode: couldn't register explosion", + mDataBlock->getName() ); + delete pExplosion; + } + } + } +} + +void ProximityMine::advanceTime( F32 dt ) +{ + Parent::advanceTime( dt ); + + if ( mAnimThread ) + mShapeInstance->advancePos( dt, mAnimThread ); +} + +//---------------------------------------------------------------------------- + +U32 ProximityMine::packUpdate( NetConnection* connection, U32 mask, BitStream* stream ) +{ + // Handle rotation ourselves (so it is not locked to the Z axis like for Items) + U32 retMask = Parent::packUpdate( connection, mask & (~Item::RotationMask), stream ); + + if ( stream->writeFlag( mask & Item::RotationMask ) ) + { + QuatF rot( mObjToWorld ); + mathWrite( *stream, rot ); + } + + if ( stream->writeFlag( !mStatic && ( mask & DeployedMask ) && ( mState > Thrown ) ) ) + { + mathWrite( *stream, mStickyCollisionPos ); + mathWrite( *stream, mStickyCollisionNormal ); + } + + stream->writeFlag( ( mask & ExplosionMask ) && ( mState == Exploded ) ); + + return retMask; +} + +void ProximityMine::unpackUpdate( NetConnection* connection, BitStream* stream ) +{ + Parent::unpackUpdate( connection, stream ); + + // Item::RotationMask + if ( stream->readFlag() ) + { + QuatF rot; + mathRead( *stream, &rot ); + + Point3F pos = mObjToWorld.getPosition(); + rot.setMatrix( &mObjToWorld ); + mObjToWorld.setPosition( pos ); + } + + // !mStatic && ( mask & DeployedMask ) && ( mState > Thrown ) + if ( stream->readFlag() ) + { + mathRead( *stream, &mStickyCollisionPos ); + mathRead( *stream, &mStickyCollisionNormal ); + + mAtRest = true; + + setDeployedPos( mStickyCollisionPos, mStickyCollisionNormal ); + } + + // ( mask & ExplosionMask ) && ( mState == Exploded ) + if ( stream->readFlag() ) + { + // start the explosion visuals on the client + explode(); + } + + if ( mStatic && mState <= Deployed ) + { + // static mines are armed immediately + mState = Deployed; + mStateTimeout = 0; + } +} + +//---------------------------------------------------------------------------- + +void ProximityMine::prepRenderImage( SceneRenderState* state ) +{ + // Don't render the mine if exploded + if ( mState == Exploded ) + return; + + // Use ShapeBase to render the 3D shape + Parent::prepRenderImage( state ); + + // Add a custom render instance to draw the trigger area + if ( !state->isShadowPass() ) + { + ObjectRenderInst* ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &ProximityMine::renderObject ); + ri->type = RenderPassManager::RIT_ObjectTranslucent; + ri->translucentSort = true; + ri->defaultKey = 1; + state->getRenderPass()->addInst( ri ); + } +} + +void ProximityMine::renderObject( ObjectRenderInst* ri, + SceneRenderState* state, + BaseMatInstance* overrideMat ) +{ + if ( overrideMat ) + return; +/* + // Render the trigger area + if ( mState == Armed || mState == Triggered ) + { + const ColorF drawColor(1, 0, 0, 0.05f); + if ( drawColor.alpha > 0 ) + { + GFXStateBlockDesc desc; + desc.setZReadWrite( true, false ); + desc.setBlend( true ); + + GFXTransformSaver saver; + + MatrixF mat = getRenderTransform(); + mat.scale( getScale() ); + + GFX->getDrawUtil()->drawSphere( desc, mDataBlock->triggerRadius, mat.getPosition(), + drawColor, true, false, &mat ); + } + } +*/ +} + +//---------------------------------------------------------------------------- + +DefineEngineMethod( ProximityMine, explode, void, (),, + "@brief Manually cause the mine to explode.\n\n") +{ + object->explode(); +} diff --git a/Engine/source/T3D/proximityMine.h b/Engine/source/T3D/proximityMine.h new file mode 100644 index 000000000..b14f5051a --- /dev/null +++ b/Engine/source/T3D/proximityMine.h @@ -0,0 +1,124 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PROXIMITYMINE_H_ +#define _PROXIMITYMINE_H_ + +#ifndef _ITEM_H_ + #include "T3D/item.h" +#endif + +class ExplosionData; +class SFXTrack; +class ProximityMine; + +//---------------------------------------------------------------------------- + +struct ProximityMineData: public ItemData +{ + typedef ItemData Parent; + + DECLARE_CALLBACK( void, onTriggered, ( ProximityMine* obj, SceneObject* target ) ); + DECLARE_CALLBACK( void, onExplode, ( ProximityMine* obj, Point3F pos ) ); + +public: + F32 armingDelay; + S32 armingSequence; + SFXTrack* armingSound; + + F32 autoTriggerDelay; + bool triggerOnOwner; + F32 triggerRadius; + F32 triggerSpeed; + F32 triggerDelay; + S32 triggerSequence; + SFXTrack* triggerSound; + + F32 explosionOffset; + + ProximityMineData(); + DECLARE_CONOBJECT( ProximityMineData ); + static void initPersistFields(); + bool preload( bool server, String& errorStr ); + virtual void packData( BitStream* stream ); + virtual void unpackData( BitStream* stream ); +}; + +//---------------------------------------------------------------------------- + +class ProximityMine: public Item +{ + typedef Item Parent; + +protected: + enum MaskBits { + DeployedMask = Parent::NextFreeMask, + ExplosionMask = Parent::NextFreeMask << 1, + NextFreeMask = Parent::NextFreeMask << 2 + }; + + enum State + { + Thrown = 0, //!< Mine has been thrown, but not yet attached to a surface + Deployed, //!< Mine has attached to a surface but is not yet armed + Armed, //!< Mine is armed and will trigger if any object enters the trigger area + Triggered, //!< Mine has been triggered and will explode soon + Exploded, //!< Mine has exploded and will be deleted on the server shortly + NumStates + }; + + ProximityMineData* mDataBlock; + + TSThread* mAnimThread; + SceneObject* mOwner; + + State mState; + F32 mStateTimeout; + + void setDeployedPos( const Point3F& pos, const Point3F& normal ); + + void prepRenderImage( SceneRenderState* state ); + void renderObject( ObjectRenderInst* ri, SceneRenderState* state, BaseMatInstance* overrideMat ); + +public: + DECLARE_CONOBJECT( ProximityMine ); + + ProximityMine(); + ~ProximityMine(); + + static void consoleInit(); + + bool onAdd(); + void onRemove(); + bool onNewDataBlock( GameBaseData* dptr, bool reload ); + + virtual void setTransform( const MatrixF& mat ); + void processTick( const Move* move ); + void explode(); + + void advanceTime( F32 dt ); + + U32 packUpdate ( NetConnection* conn, U32 mask, BitStream* stream ); + void unpackUpdate( NetConnection* conn, BitStream* stream ); +}; + +#endif // _PROXIMITYMINE_H_ diff --git a/Engine/source/T3D/resource.h b/Engine/source/T3D/resource.h new file mode 100644 index 000000000..49bed9d3c --- /dev/null +++ b/Engine/source/T3D/resource.h @@ -0,0 +1,35 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#define IDI_ICON1 103 +#define IDI_ICON2 107 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 108 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Engine/source/T3D/rigid.cpp b/Engine/source/T3D/rigid.cpp new file mode 100644 index 000000000..079f97caf --- /dev/null +++ b/Engine/source/T3D/rigid.cpp @@ -0,0 +1,366 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "T3D/rigid.h" +#include "console/console.h" + + +//---------------------------------------------------------------------------- + +Rigid::Rigid() +{ + force.set(0.0f,0.0f,0.0f); + torque.set(0.0f,0.0f,0.0f); + linVelocity.set(0.0f,0.0f,0.0f); + linPosition.set(0.0f,0.0f,0.0f); + linMomentum.set(0.0f,0.0f,0.0f); + angVelocity.set(0.0f,0.0f,0.0f); + angMomentum.set(0.0f,0.0f,0.0f); + + angPosition.identity(); + invWorldInertia.identity(); + + centerOfMass.set(0.0f,0.0f,0.0f); + worldCenterOfMass = linPosition; + mass = oneOverMass = 1.0f; + invObjectInertia.identity(); + restitution = 0.3f; + friction = 0.5f; + atRest = false; +} + +void Rigid::clearForces() +{ + force.set(0.0f,0.0f,0.0f); + torque.set(0.0f,0.0f,0.0f); +} + +//----------------------------------------------------------------------------- +void Rigid::integrate(F32 delta) +{ + // Update Angular position + F32 angle = angVelocity.len(); + if (angle != 0.0f) { + QuatF dq; + F32 sinHalfAngle; + mSinCos(angle * delta * -0.5f, sinHalfAngle, dq.w); + sinHalfAngle *= 1.0f / angle; + dq.x = angVelocity.x * sinHalfAngle; + dq.y = angVelocity.y * sinHalfAngle; + dq.z = angVelocity.z * sinHalfAngle; + QuatF tmp = angPosition; + angPosition.mul(tmp, dq); + angPosition.normalize(); + + // Rotate the position around the center of mass + Point3F lp = linPosition - worldCenterOfMass; + dq.mulP(lp,&linPosition); + linPosition += worldCenterOfMass; + } + + // Update angular momentum + angMomentum = angMomentum + torque * delta; + + // Update linear position, momentum + linPosition = linPosition + linVelocity * delta; + linMomentum = linMomentum + force * delta; + linVelocity = linMomentum * oneOverMass; + + // Update dependent state variables + updateInertialTensor(); + updateVelocity(); + updateCenterOfMass(); +} + +void Rigid::updateVelocity() +{ + linVelocity.x = linMomentum.x * oneOverMass; + linVelocity.y = linMomentum.y * oneOverMass; + linVelocity.z = linMomentum.z * oneOverMass; + invWorldInertia.mulV(angMomentum,&angVelocity); +} + +void Rigid::updateInertialTensor() +{ + MatrixF iv,qmat; + angPosition.setMatrix(&qmat); + iv.mul(qmat,invObjectInertia); + qmat.transpose(); + invWorldInertia.mul(iv,qmat); +} + +void Rigid::updateCenterOfMass() +{ + // Move the center of mass into world space + angPosition.mulP(centerOfMass,&worldCenterOfMass); + worldCenterOfMass += linPosition; +} + +void Rigid::applyImpulse(const Point3F &r, const Point3F &impulse) +{ + atRest = false; + + // Linear momentum and velocity + linMomentum += impulse; + linVelocity.x = linMomentum.x * oneOverMass; + linVelocity.y = linMomentum.y * oneOverMass; + linVelocity.z = linMomentum.z * oneOverMass; + + // Rotational momentum and velocity + Point3F tv; + mCross(r,impulse,&tv); + angMomentum += tv; + invWorldInertia.mulV(angMomentum, &angVelocity); +} + +//----------------------------------------------------------------------------- +/** Resolve collision with another rigid body + Computes & applies the collision impulses needed to keep the bodies + from interpenetrating. + + tg: This function was commented out... I uncommented it, but haven't + double checked the math. +*/ +bool Rigid::resolveCollision(const Point3F& p, const Point3F &normal, Rigid* rigid) +{ + atRest = false; + Point3F v1,v2,r1,r2; + getOriginVector(p,&r1); + getVelocity(r1,&v1); + rigid->getOriginVector(p,&r2); + rigid->getVelocity(r2,&v2); + + // Make sure they are converging + F32 nv = mDot(v1,normal); + nv -= mDot(v2,normal); + if (nv > 0.0f) + return false; + + // Compute impulse + F32 d, n = -nv * (1.0f + restitution * rigid->restitution); + Point3F a1,b1,c1; + mCross(r1,normal,&a1); + invWorldInertia.mulV(a1,&b1); + mCross(b1,r1,&c1); + + Point3F a2,b2,c2; + mCross(r2,normal,&a2); + rigid->invWorldInertia.mulV(a2,&b2); + mCross(b2,r2,&c2); + + Point3F c3 = c1 + c2; + d = oneOverMass + rigid->oneOverMass + mDot(c3,normal); + Point3F impulse = normal * (n / d); + + applyImpulse(r1,impulse); + impulse.neg(); + applyImpulse(r2,impulse); + return true; +} + +//----------------------------------------------------------------------------- +/** Resolve collision with an immovable object + Computes & applies the collision impulse needed to keep the body + from penetrating the given surface. +*/ +bool Rigid::resolveCollision(const Point3F& p, const Point3F &normal) +{ + atRest = false; + Point3F v,r; + getOriginVector(p,&r); + getVelocity(r,&v); + F32 n = -mDot(v,normal); + if (n >= 0.0f) { + + // Collision impulse, straight forward force stuff. + F32 d = getZeroImpulse(r,normal); + F32 j = n * (1.0f + restitution) * d; + Point3F impulse = normal * j; + + // Friction impulse, calculated as a function of the + // amount of force it would take to stop the motion + // perpendicular to the normal. + Point3F uv = v + (normal * n); + F32 ul = uv.len(); + if (ul) { + uv /= -ul; + F32 u = ul * getZeroImpulse(r,uv); + j *= friction; + if (u > j) + u = j; + impulse += uv * u; + } + + // + applyImpulse(r,impulse); + } + return true; +} + +//----------------------------------------------------------------------------- +/** Calculate the inertia along the given vector + This function can be used to calculate the amount of force needed to + affect a change in velocity along the specified normal applied at + the given point. +*/ +F32 Rigid::getZeroImpulse(const Point3F& r,const Point3F& normal) +{ + Point3F a,b,c; + mCross(r,normal,&a); + invWorldInertia.mulV(a,&b); + mCross(b,r,&c); + return 1 / (oneOverMass + mDot(c,normal)); +} + +F32 Rigid::getKineticEnergy() +{ + Point3F w; + QuatF qmat = angPosition; + qmat.inverse(); + qmat.mulP(angVelocity,&w); + const F32* f = invObjectInertia; + return 0.5f * ((mass * mDot(linVelocity,linVelocity)) + + w.x * w.x / f[0] + + w.y * w.y / f[5] + + w.z * w.z / f[10]); +} + +void Rigid::getOriginVector(const Point3F &p,Point3F* r) +{ + *r = p - worldCenterOfMass; +} + +void Rigid::setCenterOfMass(const Point3F &newCenter) +{ + // Sets the center of mass relative to the origin. + centerOfMass = newCenter; + + // Update world center of mass + angPosition.mulP(centerOfMass,&worldCenterOfMass); + worldCenterOfMass += linPosition; +} + +void Rigid::translateCenterOfMass(const Point3F &oldPos,const Point3F &newPos) +{ + // I + mass * (crossmatrix(centerOfMass)^2 - crossmatrix(newCenter)^2) + MatrixF oldx,newx; + oldx.setCrossProduct(oldPos); + newx.setCrossProduct(newPos); + for (int row = 0; row < 3; row++) + for (int col = 0; col < 3; col++) { + F32 n = newx(row,col), o = oldx(row,col); + objectInertia(row,col) += mass * ((o * o) - (n * n)); + } + + // Make sure the matrix is symetrical + objectInertia(1,0) = objectInertia(0,1); + objectInertia(2,0) = objectInertia(0,2); + objectInertia(2,1) = objectInertia(1,2); +} + +void Rigid::getVelocity(const Point3F& r, Point3F* v) +{ + mCross(angVelocity, r, v); + *v += linVelocity; +} + +void Rigid::getTransform(MatrixF* mat) +{ + angPosition.setMatrix(mat); + mat->setColumn(3,linPosition); +} + +void Rigid::setTransform(const MatrixF& mat) +{ + angPosition.set(mat); + mat.getColumn(3,&linPosition); + + // Update center of mass + angPosition.mulP(centerOfMass,&worldCenterOfMass); + worldCenterOfMass += linPosition; +} + + +//---------------------------------------------------------------------------- +/** Set the rigid body moment of inertia + The moment is calculated as a box with the given dimensions. +*/ +void Rigid::setObjectInertia(const Point3F& r) +{ + // Rotational moment of inertia of a box + F32 ot = mass / 12.0f; + F32 a = r.x * r.x; + F32 b = r.y * r.y; + F32 c = r.z * r.z; + + objectInertia.identity(); + F32* f = objectInertia; + f[0] = ot * (b + c); + f[5] = ot * (c + a); + f[10] = ot * (a + b); + + invertObjectInertia(); + updateInertialTensor(); +} + + +//---------------------------------------------------------------------------- +/** Set the rigid body moment of inertia + The moment is calculated as a unit sphere. +*/ +void Rigid::setObjectInertia() +{ + objectInertia.identity(); + F32 radius = 1.0f; + F32* f = objectInertia; + f[0] = f[5] = f[10] = (0.4f * mass * radius * radius); + invertObjectInertia(); + updateInertialTensor(); +} + +void Rigid::invertObjectInertia() +{ + invObjectInertia = objectInertia; + invObjectInertia.fullInverse(); +} + + +//---------------------------------------------------------------------------- + +bool Rigid::checkRestCondition() +{ +// F32 k = getKineticEnergy(mWorldToObj); +// F32 G = -force.z * oneOverMass * 0.032; +// F32 Kg = 0.5 * mRigid.mass * G * G; +// if (k < Kg * restTol) +// mRigid.setAtRest(); + return atRest; +} + +void Rigid::setAtRest() +{ + atRest = true; + linVelocity.set(0.0f,0.0f,0.0f); + linMomentum.set(0.0f,0.0f,0.0f); + angVelocity.set(0.0f,0.0f,0.0f); + angMomentum.set(0.0f,0.0f,0.0f); +} diff --git a/Engine/source/T3D/rigid.h b/Engine/source/T3D/rigid.h new file mode 100644 index 000000000..17e2fe0aa --- /dev/null +++ b/Engine/source/T3D/rigid.h @@ -0,0 +1,100 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _RIGID_H_ +#define _RIGID_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif +#ifndef _MQUAT_H_ +#include "math/mQuat.h" +#endif + +//---------------------------------------------------------------------------- + +class Rigid +{ +public: + MatrixF objectInertia; ///< Moment of inertia + MatrixF invObjectInertia; ///< Inverse moment of inertia + MatrixF invWorldInertia; ///< Inverse moment of inertia in world space + + Point3F force; + Point3F torque; + + Point3F linVelocity; ///< Linear velocity + Point3F linPosition; ///< Current position + Point3F linMomentum; ///< Linear momentum + Point3F angVelocity; ///< Angular velocity + QuatF angPosition; ///< Current rotation + Point3F angMomentum; ///< Angular momentum + + Point3F centerOfMass; ///< Center of mass in object space + Point3F worldCenterOfMass; ///< CofM in world space + F32 mass; ///< Rigid body mass + F32 oneOverMass; ///< 1 / mass + F32 restitution; ///< Collision restitution + F32 friction; ///< Friction coefficient + bool atRest; + +private: + void translateCenterOfMass(const Point3F &oldPos,const Point3F &newPos); + +public: + // + Rigid(); + void clearForces(); + void integrate(F32 delta); + + void updateInertialTensor(); + void updateVelocity(); + void updateCenterOfMass(); + + void applyImpulse(const Point3F &v,const Point3F &impulse); + bool resolveCollision(const Point3F& p,const Point3F &normal,Rigid*); + bool resolveCollision(const Point3F& p,const Point3F &normal); + + F32 getZeroImpulse(const Point3F& r,const Point3F& normal); + F32 getKineticEnergy(); + void getOriginVector(const Point3F &r,Point3F* v); + void setCenterOfMass(const Point3F &v); + void getVelocity(const Point3F &p,Point3F* r); + void getTransform(MatrixF* mat); + void setTransform(const MatrixF& mat); + + void setObjectInertia(const Point3F& r); + void setObjectInertia(); + void invertObjectInertia(); + + bool checkRestCondition(); + void setAtRest(); +}; + + +#endif diff --git a/Engine/source/T3D/rigidShape.cpp b/Engine/source/T3D/rigidShape.cpp new file mode 100644 index 000000000..f81ca8752 --- /dev/null +++ b/Engine/source/T3D/rigidShape.cpp @@ -0,0 +1,1712 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/rigidShape.h" + +#include "app/game.h" +#include "math/mMath.h" +#include "console/simBase.h" +#include "console/console.h" +#include "console/consoleTypes.h" +#include "collision/clippedPolyList.h" +#include "collision/planeExtractor.h" +#include "T3D/gameBase/moveManager.h" +#include "core/stream/bitStream.h" +#include "core/dnet.h" +#include "T3D/gameBase/gameConnection.h" +#include "ts/tsShapeInstance.h" +#include "math/mathIO.h" +#include "scene/sceneRenderState.h" +#include "scene/sceneManager.h" +#include "T3D/fx/cameraFXMgr.h" +#include "T3D/trigger.h" +#include "T3D/item.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxDrawUtil.h" +#include "sfx/sfxTypes.h" +#include "sfx/sfxSystem.h" +#include "T3D/fx/particleEmitter.h" +#include "console/engineAPI.h" + + +IMPLEMENT_CO_DATABLOCK_V1(RigidShapeData); + +ConsoleDocClass( RigidShapeData, + "@brief Defines the physics properties for an individual RigidShapeData physics object.\n\n" + + "@tsexample\n" + " datablock RigidShapeData( BouncingBoulder )\n" + " {\n" + " category = \"RigidShape\";\n" + "\n" + " shapeFile = \"~/data/shapes/boulder/boulder.dts\";\n" + " emap = true;\n" + "\n" + " // Rigid Body\n" + " mass = 500;\n" + " massCenter = \"0 0 0\"; // Center of mass for rigid body\n" + " massBox = \"0 0 0\"; // Size of box used for moment of inertia,\n" + " // if zero it defaults to object bounding box\n" + " drag = 0.2; // Drag coefficient\n" + " bodyFriction = 0.2;\n" + " bodyRestitution = 0.1;\n" + " minImpactSpeed = 5; // Impacts over this invoke the script callback\n" + " softImpactSpeed = 5; // Play SoftImpact Sound\n" + " hardImpactSpeed = 15; // Play HardImpact Sound\n" + " integration = 4; // Physics integration: TickSec/Rate\n" + " collisionTol = 0.1; // Collision distance tolerance\n" + " contactTol = 0.1; // Contact velocity tolerance\n" + "\n" + " minRollSpeed = 10;\n" + "\n" + " maxDrag = 0.5;\n" + " minDrag = 0.01;\n" + "\n" + " dustHeight = 10;\n" + "\n" + " dragForce = 0.05;\n" + " vertFactor = 0.05;\n" + " };\n" + "@endtsexample\n\n" + + "@see RigidShape\n" + "@see ShapeBase\n\n" + + "@ingroup Platform\n" +); + + +IMPLEMENT_CO_NETOBJECT_V1(RigidShape); + +ConsoleDocClass( RigidShape, + "@brief The RigidShape class implements rigid-body physics for DTS objects in the world.\n\n" + + "\"Rigid body physics\" refers to a system whereby objects are assumed to have a finite size,\n" + "equally distributed masses, and where deformations of the objects themselves are not accounted for.\n" + "Uses the RigidShape class to control its physics.\n\n" + + "@tsexample\n" + " datablock RigidShapeData( BouncingBoulder )\n" + " {\n" + " category = \"RigidShape\";\n" + "\n" + " shapeFile = \"~/data/shapes/boulder/boulder.dts\";\n" + " emap = true;\n" + "\n" + " // Rigid Body\n" + " mass = 500;\n" + " massCenter = \"0 0 0\"; // Center of mass for rigid body\n" + " massBox = \"0 0 0\"; // Size of box used for moment of inertia,\n" + " // if zero it defaults to object bounding box\n" + " drag = 0.2; // Drag coefficient\n" + " bodyFriction = 0.2;\n" + " bodyRestitution = 0.1;\n" + " minImpactSpeed = 5; // Impacts over this invoke the script callback\n" + " softImpactSpeed = 5; // Play SoftImpact Sound\n" + " hardImpactSpeed = 15; // Play HardImpact Sound\n" + " integration = 4; // Physics integration: TickSec/Rate\n" + " collisionTol = 0.1; // Collision distance tolerance\n" + " contactTol = 0.1; // Contact velocity tolerance\n" + "\n" + " minRollSpeed = 10;\n" + "\n" + " maxDrag = 0.5;\n" + " minDrag = 0.01;\n" + "\n" + " dustHeight = 10;\n" + "\n" + " dragForce = 0.05;\n" + " vertFactor = 0.05;\n" + " };\n" + "\n" + " new RigidShape()\n" + " {\n" + " dataBlock = \"BouncingBoulder\";\n" + " parentGroup = EWCreatorWindow.objectGroup;\n" + " };\n" + "@endtsexample\n\n" + + "@see RigidShapeData\n" + "@see ShapeBase\n\n" + + "@ingroup Platform\n" +); + + +IMPLEMENT_CALLBACK( RigidShape, onEnterLiquid, void, ( const char* objId, const char* waterCoverage, const char* liquidType ), + ( objId, waterCoverage, liquidType ), + "@brief Called whenever this RigidShape object enters liquid.\n\n" + "@param objId The ID of the rigidShape object.\n" + "@param waterCoverage Amount of water coverage the RigidShape has.\n" + "@param liquidType Type of liquid that was entered.\n\n" + "@tsexample\n" + "// The RigidShape object falls in a body of liquid, causing the callback to occur.\n" + "RigidShape::onEnterLiquid(%this,%objId,%waterCoverage,%liquidType)\n" + " {\n" + " // Code to run whenever this callback occurs.\n" + " }\n" + "@endtsexample\n\n" + "@see ShapeBase\n\n" +); + +IMPLEMENT_CALLBACK( RigidShape, onLeaveLiquid, void, ( const char* objId, const char* liquidType ),( objId, liquidType ), + "@brief Called whenever the RigidShape object exits liquid.\n\n" + "@param objId The ID of the RigidShape object.\n" + "@param liquidType Type if liquid that was exited.\n\n" + "@tsexample\n" + "// The RigidShape object exits in a body of liquid, causing the callback to occur.\n" + "RigidShape::onLeaveLiquid(%this,%objId,%liquidType)\n" + " {\n" + " // Code to run whenever this callback occurs.\n" + " }\n" + "@endtsexample\n\n" + "@see ShapeBase\n\n" +); + +//---------------------------------------------------------------------------- + +namespace { + + const U32 sMoveRetryCount = 3; + + // Client prediction + const S32 sMaxWarpTicks = 3; // Max warp duration in ticks + const S32 sMaxPredictionTicks = 30; // Number of ticks to predict + const F32 sRigidShapeGravity = -20; + + // Physics and collision constants + static F32 sRestTol = 0.5; // % of gravity energy to be at rest + static int sRestCount = 10; // Consecutive ticks before comming to rest + + const U32 sCollisionMoveMask = (TerrainObjectType | InteriorObjectType | + PlayerObjectType | StaticShapeObjectType | VehicleObjectType | + VehicleBlockerObjectType); + + const U32 sServerCollisionMask = sCollisionMoveMask; // ItemObjectType + const U32 sClientCollisionMask = sCollisionMoveMask; + + void nonFilter(SceneObject* object,void *key) + { + SceneContainer::CallbackInfo* info = reinterpret_cast(key); + object->buildPolyList(info->context,info->polyList,info->boundingBox,info->boundingSphere); + } + +} // namespace {} + + +// Trigger objects that are not normally collided with. +static U32 sTriggerMask = ItemObjectType | +TriggerObjectType | +CorpseObjectType; + + +//---------------------------------------------------------------------------- + +RigidShapeData::RigidShapeData() +{ + shadowEnable = true; + + body.friction = 0; + body.restitution = 1; + + minImpactSpeed = 25; + softImpactSpeed = 25; + hardImpactSpeed = 50; + minRollSpeed = 0; + + cameraRoll = true; + cameraLag = 0; + cameraDecay = 0; + cameraOffset = 0; + + minDrag = 0; + maxDrag = 0; + integration = 1; + collisionTol = 0.1f; + contactTol = 0.1f; + massCenter.set(0,0,0); + massBox.set(0,0,0); + + drag = 0.7f; + density = 4; + + for (S32 i = 0; i < Body::MaxSounds; i++) + body.sound[i] = 0; + + dustEmitter = NULL; + dustID = 0; + dustHeight = 1.0; + + dMemset( splashEmitterList, 0, sizeof( splashEmitterList ) ); + dMemset( splashEmitterIDList, 0, sizeof( splashEmitterIDList ) ); + + splashFreqMod = 300.0; + splashVelEpsilon = 0.50; + exitSplashSoundVel = 2.0; + softSplashSoundVel = 1.0; + medSplashSoundVel = 2.0; + hardSplashSoundVel = 3.0; + + dMemset(waterSound, 0, sizeof(waterSound)); + + dragForce = 0; + vertFactor = 0.25; + + dustTrailEmitter = NULL; + dustTrailID = 0; +} + +RigidShapeData::~RigidShapeData() +{ + +} + +//---------------------------------------------------------------------------- + + +bool RigidShapeData::onAdd() +{ + if(!Parent::onAdd()) + return false; + + return true; +} + + +bool RigidShapeData::preload(bool server, String &errorStr) +{ + if (!Parent::preload(server, errorStr)) + return false; + + // RigidShape objects must define a collision detail + if (!collisionDetails.size() || collisionDetails[0] == -1) + { + Con::errorf("RigidShapeData::preload failed: Rigid shapes must define a collision-1 detail"); + return false; + } + + // Resolve objects transmitted from server + if (!server) { + for (S32 i = 0; i < Body::MaxSounds; i++) + sfxResolve( &body.sound[ i ], errorStr ); + } + + if( !dustEmitter && dustID != 0 ) + { + if( !Sim::findObject( dustID, dustEmitter ) ) + { + Con::errorf( ConsoleLogEntry::General, "RigidShapeData::preload Invalid packet, bad datablockId(dustEmitter): 0x%x", dustID ); + } + } + + U32 i; + for( i=0; i 1.0f) + { + Con::warnf("RigidShapeData::preload: vert factor must be [0, 1]"); + vertFactor = vertFactor < 0.0f ? 0.0f : 1.0f; + } + + if( !dustTrailEmitter && dustTrailID != 0 ) + { + if( !Sim::findObject( dustTrailID, dustTrailEmitter ) ) + { + Con::errorf( ConsoleLogEntry::General, "RigidShapeData::preload Invalid packet, bad datablockId(dustTrailEmitter): 0x%x", dustTrailID ); + } + } + + return true; +} + + +//---------------------------------------------------------------------------- + +void RigidShapeData::packData(BitStream* stream) +{ + Parent::packData(stream); + + stream->write(body.restitution); + stream->write(body.friction); + for( U32 i = 0; i < Body::MaxSounds; ++ i ) + sfxWrite( stream, body.sound[ i ] ); + + stream->write(minImpactSpeed); + stream->write(softImpactSpeed); + stream->write(hardImpactSpeed); + stream->write(minRollSpeed); + + stream->write(maxDrag); + stream->write(minDrag); + stream->write(integration); + stream->write(collisionTol); + stream->write(contactTol); + mathWrite(*stream,massCenter); + mathWrite(*stream,massBox); + + stream->writeFlag(cameraRoll); + stream->write(cameraLag); + stream->write(cameraDecay); + stream->write(cameraOffset); + + stream->write( dustHeight ); + + stream->write(exitSplashSoundVel); + stream->write(softSplashSoundVel); + stream->write(medSplashSoundVel); + stream->write(hardSplashSoundVel); + + // write the water sound profiles + for( U32 i = 0; i < MaxSounds; ++ i ) + sfxWrite( stream, waterSound[ i ] ); + + if (stream->writeFlag( dustEmitter )) + stream->writeRangedU32( dustEmitter->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); + + for( U32 i = 0; i < VC_NUM_SPLASH_EMITTERS; ++ i ) + { + if( stream->writeFlag( splashEmitterList[i] != NULL ) ) + stream->writeRangedU32( splashEmitterList[i]->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + + stream->write(splashFreqMod); + stream->write(splashVelEpsilon); + + stream->write(dragForce); + stream->write(vertFactor); + + if (stream->writeFlag( dustTrailEmitter )) + stream->writeRangedU32( dustTrailEmitter->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); +} + +void RigidShapeData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + + stream->read(&body.restitution); + stream->read(&body.friction); + + for( U32 i = 0; i < Body::MaxSounds; i++) + sfxRead( stream, &body.sound[ i ] ); + + stream->read(&minImpactSpeed); + stream->read(&softImpactSpeed); + stream->read(&hardImpactSpeed); + stream->read(&minRollSpeed); + + stream->read(&maxDrag); + stream->read(&minDrag); + stream->read(&integration); + stream->read(&collisionTol); + stream->read(&contactTol); + mathRead(*stream,&massCenter); + mathRead(*stream,&massBox); + + cameraRoll = stream->readFlag(); + stream->read(&cameraLag); + stream->read(&cameraDecay); + stream->read(&cameraOffset); + + stream->read( &dustHeight ); + + stream->read(&exitSplashSoundVel); + stream->read(&softSplashSoundVel); + stream->read(&medSplashSoundVel); + stream->read(&hardSplashSoundVel); + + // write the water sound profiles + for( U32 i = 0; i < MaxSounds; ++ i ) + sfxRead( stream, &waterSound[ i ] ); + + if( stream->readFlag() ) + dustID = (S32) stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); + + for( U32 i = 0; i < VC_NUM_SPLASH_EMITTERS; ++ i ) + { + if( stream->readFlag() ) + splashEmitterIDList[i] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + + stream->read(&splashFreqMod); + stream->read(&splashVelEpsilon); + + stream->read(&dragForce); + stream->read(&vertFactor); + + if( stream->readFlag() ) + dustTrailID = (S32) stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); +} + + +//---------------------------------------------------------------------------- + +void RigidShapeData::initPersistFields() +{ + addField("massCenter", TypePoint3F, Offset(massCenter, RigidShapeData), "Center of mass for rigid body."); + addField("massBox", TypePoint3F, Offset(massBox, RigidShapeData), "Size of inertial box."); + addField("bodyRestitution", TypeF32, Offset(body.restitution, RigidShapeData), "The percentage of kinetic energy kept by this object in a collision."); + addField("bodyFriction", TypeF32, Offset(body.friction, RigidShapeData), "How much friction this object has. Lower values will cause the object to appear to be more slippery."); + + addField("minImpactSpeed", TypeF32, Offset(minImpactSpeed, RigidShapeData), + "Minimum collision speed to classify collision as impact (triggers onImpact on server object)." ); + addField("softImpactSpeed", TypeF32, Offset(softImpactSpeed, RigidShapeData), "Minimum speed at which this object must be travelling for the soft impact sound to be played."); + addField("hardImpactSpeed", TypeF32, Offset(hardImpactSpeed, RigidShapeData), "Minimum speed at which the object must be travelling for the hard impact sound to be played."); + addField("minRollSpeed", TypeF32, Offset(minRollSpeed, RigidShapeData)); + + addField("maxDrag", TypeF32, Offset(maxDrag, RigidShapeData), "Maximum drag available to this object."); + addField("minDrag", TypeF32, Offset(minDrag, RigidShapeData), "Minimum drag available to this object."); + addField("integration", TypeS32, Offset(integration, RigidShapeData), "Number of physics steps to process per tick."); + addField("collisionTol", TypeF32, Offset(collisionTol, RigidShapeData), "Collision distance tolerance."); + addField("contactTol", TypeF32, Offset(contactTol, RigidShapeData), "Contact velocity tolerance."); + + addGroup( "Forces" ); + + addField("dragForce", TypeF32, Offset(dragForce, RigidShapeData), "Used to simulate the constant drag acting on the object"); + addField("vertFactor", TypeF32, Offset(vertFactor, RigidShapeData), "The scalar applied to the vertical portion of the velocity drag acting on a object."); + + endGroup( "Forces" ); + + addGroup( "Particle Effects" ); + + addField("dustEmitter", TYPEID< ParticleEmitterData >(), Offset(dustEmitter, RigidShapeData), "Array of pointers to ParticleEmitterData datablocks which will be used to emit particles at object/terrain contact point.\n"); + addField("triggerDustHeight", TypeF32, Offset(triggerDustHeight, RigidShapeData), "Maximum height from the ground at which the object will generate dust.\n"); + addField("dustHeight", TypeF32, Offset(dustHeight, RigidShapeData), "Height of dust effects.\n"); + + addField("dustTrailEmitter", TYPEID< ParticleEmitterData >(), Offset(dustTrailEmitter, RigidShapeData), "Particle emitter used to create a dust trail for the moving object.\n"); + + addField("splashEmitter", TYPEID< ParticleEmitterData >(), Offset(splashEmitterList, RigidShapeData), VC_NUM_SPLASH_EMITTERS, "Array of pointers to ParticleEmitterData datablocks which will generate splash effects.\n"); + + addField("splashFreqMod", TypeF32, Offset(splashFreqMod, RigidShapeData), "The simulated frequency modulation of a splash generated by this object. Multiplied along with speed and time elapsed when determining splash emition rate.\n"); + addField("splashVelEpsilon", TypeF32, Offset(splashVelEpsilon, RigidShapeData), "The threshold speed at which we consider the object's movement to have stopped when updating splash effects.\n"); + + endGroup( "Particle Effects" ); + + addGroup( "Sounds" ); + + addField("softImpactSound", TypeSFXTrackName, Offset(body.sound[Body::SoftImpactSound], RigidShapeData), + "Sound to play when body impacts with at least softImageSpeed but less than hardImpactSpeed." ); + addField("hardImpactSound", TypeSFXTrackName, Offset(body.sound[Body::HardImpactSound], RigidShapeData), + "Sound to play when body impacts with at least hardImpactSpeed." ); + + addField("exitSplashSoundVelocity", TypeF32, Offset(exitSplashSoundVel, RigidShapeData), "The minimum velocity at which the exit splash sound will be played when emerging from water.\n"); + addField("softSplashSoundVelocity", TypeF32, Offset(softSplashSoundVel, RigidShapeData),"The minimum velocity at which the soft splash sound will be played when impacting water.\n"); + addField("mediumSplashSoundVelocity", TypeF32, Offset(medSplashSoundVel, RigidShapeData), "The minimum velocity at which the medium splash sound will be played when impacting water.\n"); + addField("hardSplashSoundVelocity", TypeF32, Offset(hardSplashSoundVel, RigidShapeData), "The minimum velocity at which the hard splash sound will be played when impacting water.\n"); + addField("exitingWater", TypeSFXTrackName, Offset(waterSound[ExitWater], RigidShapeData), "The AudioProfile will be used to produce sounds when emerging from water.\n"); + addField("impactWaterEasy", TypeSFXTrackName, Offset(waterSound[ImpactSoft], RigidShapeData), "The AudioProfile will be used to produce sounds when a soft impact with water occurs.\n"); + addField("impactWaterMedium", TypeSFXTrackName, Offset(waterSound[ImpactMedium], RigidShapeData), "The AudioProfile will be used to produce sounds when a medium impact with water occurs.\n"); + addField("impactWaterHard", TypeSFXTrackName, Offset(waterSound[ImpactHard], RigidShapeData), "The AudioProfile will be used to produce sounds when a hard impact with water occurs.\n"); + addField("waterWakeSound", TypeSFXTrackName, Offset(waterSound[Wake], RigidShapeData), "The AudioProfile will be used to produce sounds when a water wake is displayed.\n"); + + endGroup( "Sounds" ); + + addGroup( "Camera" ); + + addField("cameraRoll", TypeBool, Offset(cameraRoll, RigidShapeData), "Specifies whether the camera's rotation matrix, and the render eye transform are multiplied during camera updates.\n"); + addField("cameraLag", TypeF32, Offset(cameraLag, RigidShapeData), "Scalar amount by which the third person camera lags the object, relative to the object's linear velocity.\n"); + addField("cameraDecay", TypeF32, Offset(cameraDecay, RigidShapeData), "Scalar rate at which the third person camera offset decays, per tick.\n"); + addField("cameraOffset", TypeF32, Offset(cameraOffset, RigidShapeData), "The vertical offset of the object's camera.\n"); + + endGroup( "Camera" ); + + Parent::initPersistFields(); +} + + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +//---------------------------------------------------------------------------- + +RigidShape::RigidShape() +{ + + mNetFlags.set(Ghostable); + + mDustTrailEmitter = NULL; + + mDataBlock = 0; + // [rene, 27-Apr-11] WTH is a RigidShape a vehicle??? + mTypeMask |= VehicleObjectType | DynamicShapeObjectType; + + mDelta.pos = Point3F(0,0,0); + mDelta.posVec = Point3F(0,0,0); + mDelta.warpTicks = mDelta.warpCount = 0; + mDelta.dt = 1; + mDelta.move = NullMove; + mPredictionCount = 0; + mDelta.cameraOffset.set(0,0,0); + mDelta.cameraVec.set(0,0,0); + mDelta.cameraRot.set(0,0,0); + mDelta.cameraRotVec.set(0,0,0); + + mRigid.linPosition.set(0, 0, 0); + mRigid.linVelocity.set(0, 0, 0); + mRigid.angPosition.identity(); + mRigid.angVelocity.set(0, 0, 0); + mRigid.linMomentum.set(0, 0, 0); + mRigid.angMomentum.set(0, 0, 0); + mContacts.clear(); + + mCameraOffset.set(0,0,0); + + dMemset( mDustEmitterList, 0, sizeof( mDustEmitterList ) ); + dMemset( mSplashEmitterList, 0, sizeof( mSplashEmitterList ) ); + + mDisableMove = false; // start frozen by default + restCount = 0; + + inLiquid = false; +} + +RigidShape::~RigidShape() +{ + // +} + +U32 RigidShape::getCollisionMask() +{ + if (isServerObject()) + return sServerCollisionMask; + else + return sClientCollisionMask; +} + +Point3F RigidShape::getVelocity() const +{ + return mRigid.linVelocity; +} + +//---------------------------------------------------------------------------- + +bool RigidShape::onAdd() +{ + if (!Parent::onAdd()) + return false; + + // When loading from a mission script, the base SceneObject's transform + // will have been set and needs to be transfered to the rigid body. + mRigid.setTransform(mObjToWorld); + + // Initialize interpolation vars. + mDelta.rot[1] = mDelta.rot[0] = mRigid.angPosition; + mDelta.pos = mRigid.linPosition; + mDelta.posVec = Point3F(0,0,0); + + // Create Emitters on the client + if( isClientObject() ) + { + if( mDataBlock->dustEmitter ) + { + for( U32 i=0; ionNewDataBlock( mDataBlock->dustEmitter, false ); + if( !mDustEmitterList[i]->registerObject() ) + { + Con::warnf( ConsoleLogEntry::General, "Could not register dust emitter for class: %s", mDataBlock->getName() ); + delete mDustEmitterList[i]; + mDustEmitterList[i] = NULL; + } + } + } + + for( U32 j=0; jsplashEmitterList[j] ) + { + mSplashEmitterList[j] = new ParticleEmitter; + mSplashEmitterList[j]->onNewDataBlock( mDataBlock->splashEmitterList[j], false ); + if( !mSplashEmitterList[j]->registerObject() ) + { + Con::warnf( ConsoleLogEntry::General, "Could not register splash emitter for class: %s", mDataBlock->getName() ); + delete mSplashEmitterList[j]; + mSplashEmitterList[j] = NULL; + } + + } + } + } + + // Create a new convex. + AssertFatal(mDataBlock->collisionDetails[0] != -1, "Error, a rigid shape must have a collision-1 detail!"); + mConvex.mObject = this; + mConvex.pShapeBase = this; + mConvex.hullId = 0; + mConvex.box = mObjBox; + mConvex.box.minExtents.convolve(mObjScale); + mConvex.box.maxExtents.convolve(mObjScale); + mConvex.findNodeTransform(); + + addToScene(); + + + if( !isServerObject() ) + { + if( mDataBlock->dustTrailEmitter ) + { + mDustTrailEmitter = new ParticleEmitter; + mDustTrailEmitter->onNewDataBlock( mDataBlock->dustTrailEmitter, false ); + if( !mDustTrailEmitter->registerObject() ) + { + Con::warnf( ConsoleLogEntry::General, "Could not register dust emitter for class: %s", mDataBlock->getName() ); + delete mDustTrailEmitter; + mDustTrailEmitter = NULL; + } + } + } + + + if (isServerObject()) + scriptOnAdd(); + + return true; +} + +void RigidShape::onRemove() +{ + scriptOnRemove(); + removeFromScene(); + + U32 i=0; + for( i=0; ideleteWhenEmpty(); + mDustEmitterList[i] = NULL; + } + } + + for( i=0; ideleteWhenEmpty(); + mSplashEmitterList[i] = NULL; + } + } + + Parent::onRemove(); +} + + +//---------------------------------------------------------------------------- + +void RigidShape::processTick(const Move* move) +{ + Parent::processTick(move); + + // Warp to catch up to server + if (mDelta.warpCount < mDelta.warpTicks) + { + mDelta.warpCount++; + + // Set new pos. + mObjToWorld.getColumn(3,&mDelta.pos); + mDelta.pos += mDelta.warpOffset; + mDelta.rot[0] = mDelta.rot[1]; + mDelta.rot[1].interpolate(mDelta.warpRot[0],mDelta.warpRot[1],F32(mDelta.warpCount)/mDelta.warpTicks); + setPosition(mDelta.pos,mDelta.rot[1]); + + // Pos backstepping + mDelta.posVec.x = -mDelta.warpOffset.x; + mDelta.posVec.y = -mDelta.warpOffset.y; + mDelta.posVec.z = -mDelta.warpOffset.z; + } + else + { + if (!move) + { + if (isGhost()) + { + // If we haven't run out of prediction time, + // predict using the last known move. + if (mPredictionCount-- <= 0) + return; + move = &mDelta.move; + } + else + move = &NullMove; + } + + // Process input move + updateMove(move); + + // Save current rigid state interpolation + mDelta.posVec = mRigid.linPosition; + mDelta.rot[0] = mRigid.angPosition; + + // Update the physics based on the integration rate + S32 count = mDataBlock->integration; + updateWorkingCollisionSet(getCollisionMask()); + for (U32 i = 0; i < count; i++) + updatePos(TickSec / count); + + // Wrap up interpolation info + mDelta.pos = mRigid.linPosition; + mDelta.posVec -= mRigid.linPosition; + mDelta.rot[1] = mRigid.angPosition; + + // Update container database + setPosition(mRigid.linPosition, mRigid.angPosition); + setMaskBits(PositionMask); + updateContainer(); + } +} + +void RigidShape::interpolateTick(F32 dt) +{ + Parent::interpolateTick(dt); + + if(dt == 0.0f) + setRenderPosition(mDelta.pos, mDelta.rot[1]); + else + { + QuatF rot; + rot.interpolate(mDelta.rot[1], mDelta.rot[0], dt); + Point3F pos = mDelta.pos + mDelta.posVec * dt; + setRenderPosition(pos,rot); + } + mDelta.dt = dt; +} + +void RigidShape::advanceTime(F32 dt) +{ + Parent::advanceTime(dt); + + updateFroth(dt); + + // Update 3rd person camera offset. Camera update is done + // here as it's a client side only animation. + mCameraOffset -= + (mCameraOffset * mDataBlock->cameraDecay + + mRigid.linVelocity * mDataBlock->cameraLag) * dt; +} + + +//---------------------------------------------------------------------------- + +bool RigidShape::onNewDataBlock(GameBaseData* dptr, bool reload) +{ + mDataBlock = dynamic_cast(dptr); + if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload)) + return false; + + // Update Rigid Info + mRigid.mass = mDataBlock->mass; + mRigid.oneOverMass = 1 / mRigid.mass; + mRigid.friction = mDataBlock->body.friction; + mRigid.restitution = mDataBlock->body.restitution; + mRigid.setCenterOfMass(mDataBlock->massCenter); + + // Ignores massBox, just set sphere for now. Derived objects + // can set what they want. + mRigid.setObjectInertia(); + + scriptOnNewDataBlock(); + + return true; +} + + +//---------------------------------------------------------------------------- + +void RigidShape::getCameraParameters(F32 *min,F32* max,Point3F* off,MatrixF* rot) +{ + *min = mDataBlock->cameraMinDist; + *max = mDataBlock->cameraMaxDist; + + off->set(0,0,mDataBlock->cameraOffset); + rot->identity(); +} + + +//---------------------------------------------------------------------------- + +void RigidShape::getCameraTransform(F32* pos,MatrixF* mat) +{ + // Returns camera to world space transform + // Handles first person / third person camera position + if (isServerObject() && mShapeInstance) + mShapeInstance->animateNodeSubtrees(true); + + if (*pos == 0) + { + getRenderEyeTransform(mat); + return; + } + + // Get the shape's camera parameters. + F32 min,max; + MatrixF rot; + Point3F offset; + getCameraParameters(&min,&max,&offset,&rot); + + // Start with the current eye position + MatrixF eye; + getRenderEyeTransform(&eye); + + // Build a transform that points along the eye axis + // but where the Z axis is always up. + if (mDataBlock->cameraRoll) + mat->mul(eye,rot); + else + { + MatrixF cam(1); + VectorF x,y,z(0,0,1); + eye.getColumn(1, &y); + + mCross(y, z, &x); + x.normalize(); + mCross(x, y, &z); + z.normalize(); + + cam.setColumn(0,x); + cam.setColumn(1,y); + cam.setColumn(2,z); + mat->mul(cam,rot); + } + + // Camera is positioned straight back along the eye's -Y axis. + // A ray is cast to make sure the camera doesn't go through + // anything solid. + VectorF vp,vec; + vp.x = vp.z = 0; + vp.y = -(max - min) * *pos; + eye.mulV(vp,&vec); + + // Use the camera node as the starting position if it exists. + Point3F osp,sp; + if (mDataBlock->cameraNode != -1) + { + mShapeInstance->mNodeTransforms[mDataBlock->cameraNode].getColumn(3,&osp); + getRenderTransform().mulP(osp,&sp); + } + else + eye.getColumn(3,&sp); + + // Make sure we don't hit ourself... + disableCollision(); + if (isMounted()) + getObjectMount()->disableCollision(); + + // Cast the ray into the container database to see if we're going + // to hit anything. + RayInfo collision; + Point3F ep = sp + vec + offset + mCameraOffset; + if (mContainer->castRay(sp, ep, + ~(WaterObjectType | GameBaseObjectType | DefaultObjectType), + &collision) == true) + { + + // Shift the collision point back a little to try and + // avoid clipping against the front camera plane. + F32 t = collision.t - (-mDot(vec, collision.normal) / vec.len()) * 0.1; + if (t > 0.0f) + ep = sp + offset + mCameraOffset + (vec * t); + else + eye.getColumn(3,&ep); + } + mat->setColumn(3,ep); + + // Re-enable our collision. + if (isMounted()) + getObjectMount()->enableCollision(); + enableCollision(); + + // Apply Camera FX. + mat->mul( gCamFXMgr.getTrans() ); +} + + +//---------------------------------------------------------------------------- + +void RigidShape::getVelocity(const Point3F& r, Point3F* v) +{ + mRigid.getVelocity(r, v); +} + +void RigidShape::applyImpulse(const Point3F &pos, const Point3F &impulse) +{ + Point3F r; + mRigid.getOriginVector(pos,&r); + mRigid.applyImpulse(r, impulse); +} + + +//---------------------------------------------------------------------------- + +void RigidShape::updateMove(const Move* move) +{ + mDelta.move = *move; +} + +//---------------------------------------------------------------------------- + +void RigidShape::setPosition(const Point3F& pos,const QuatF& rot) +{ + MatrixF mat; + rot.setMatrix(&mat); + mat.setColumn(3,pos); + Parent::setTransform(mat); +} + +void RigidShape::setRenderPosition(const Point3F& pos, const QuatF& rot) +{ + MatrixF mat; + rot.setMatrix(&mat); + mat.setColumn(3,pos); + Parent::setRenderTransform(mat); +} + +void RigidShape::setTransform(const MatrixF& newMat) +{ + mRigid.setTransform(newMat); + Parent::setTransform(newMat); + mRigid.atRest = false; + mContacts.clear(); +} + + +//----------------------------------------------------------------------------- + +void RigidShape::disableCollision() +{ + Parent::disableCollision(); +} + +void RigidShape::enableCollision() +{ + Parent::enableCollision(); +} + + +//---------------------------------------------------------------------------- +/** Update the physics +*/ + +void RigidShape::updatePos(F32 dt) +{ + Point3F origVelocity = mRigid.linVelocity; + + // Update internal forces acting on the body. + mRigid.clearForces(); + updateForces(dt); + + // Update collision information based on our current pos. + bool collided = false; + if (!mRigid.atRest && !mDisableMove) + { + collided = updateCollision(dt); + + // Now that all the forces have been processed, lets + // see if we're at rest. Basically, if the kinetic energy of + // the shape is less than some percentage of the energy added + // by gravity for a short period, we're considered at rest. + // This should really be part of the rigid class... + if (mCollisionList.getCount()) + { + F32 k = mRigid.getKineticEnergy(); + F32 G = sRigidShapeGravity * dt; + F32 Kg = 0.5 * mRigid.mass * G * G; + if (k < sRestTol * Kg && ++restCount > sRestCount) + mRigid.setAtRest(); + } + else + restCount = 0; + } + + // Integrate forward + if (!mRigid.atRest && !mDisableMove) + mRigid.integrate(dt); + + // Deal with client and server scripting, sounds, etc. + if (isServerObject()) + { + + // Check triggers and other objects that we normally don't + // collide with. This function must be called before notifyCollision + // as it will queue collision. + checkTriggers(); + + // Invoke the onCollision notify callback for all the objects + // we've just hit. + notifyCollision(); + + // Server side impact script callback + if (collided) + { + VectorF collVec = mRigid.linVelocity - origVelocity; + F32 collSpeed = collVec.len(); + if (collSpeed > mDataBlock->minImpactSpeed) + onImpact(collVec); + } + + // Water script callbacks + if (!inLiquid && mWaterCoverage != 0.0f) + { + onEnterLiquid_callback(getIdString(), Con::getFloatArg(mWaterCoverage), mLiquidType.c_str() ); + inLiquid = true; + } + else if (inLiquid && mWaterCoverage == 0.0f) + { + onLeaveLiquid_callback(getIdString(), mLiquidType.c_str() ); + inLiquid = false; + } + + } + else { + + // Play impact sounds on the client. + if (collided) { + F32 collSpeed = (mRigid.linVelocity - origVelocity).len(); + S32 impactSound = -1; + if (collSpeed >= mDataBlock->hardImpactSpeed) + impactSound = RigidShapeData::Body::HardImpactSound; + else + if (collSpeed >= mDataBlock->softImpactSpeed) + impactSound = RigidShapeData::Body::SoftImpactSound; + + if (impactSound != -1 && mDataBlock->body.sound[impactSound] != NULL) + SFX->playOnce(mDataBlock->body.sound[impactSound], &getTransform()); + } + + // Water volume sounds + F32 vSpeed = getVelocity().len(); + if (!inLiquid && mWaterCoverage >= 0.8f) { + if (vSpeed >= mDataBlock->hardSplashSoundVel) + SFX->playOnce(mDataBlock->waterSound[RigidShapeData::ImpactHard], &getTransform()); + else + if (vSpeed >= mDataBlock->medSplashSoundVel) + SFX->playOnce(mDataBlock->waterSound[RigidShapeData::ImpactMedium], &getTransform()); + else + if (vSpeed >= mDataBlock->softSplashSoundVel) + SFX->playOnce(mDataBlock->waterSound[RigidShapeData::ImpactSoft], &getTransform()); + inLiquid = true; + } + else + if(inLiquid && mWaterCoverage < 0.8f) { + if (vSpeed >= mDataBlock->exitSplashSoundVel) + SFX->playOnce(mDataBlock->waterSound[RigidShapeData::ExitWater], &getTransform()); + inLiquid = false; + } + } +} + + +//---------------------------------------------------------------------------- + +void RigidShape::updateForces(F32 /*dt*/) +{ + Point3F gravForce(0, 0, sRigidShapeGravity * mRigid.mass * mGravityMod); + + MatrixF currTransform; + mRigid.getTransform(&currTransform); + mRigid.atRest = false; + + Point3F torque(0, 0, 0); + Point3F force(0, 0, 0); + + Point3F vel = mRigid.linVelocity; + + // Gravity + force += gravForce; + + // Apply drag + Point3F vDrag = mRigid.linVelocity; + vDrag.convolve(Point3F(1, 1, mDataBlock->vertFactor)); + force -= vDrag * mDataBlock->dragForce; + + // Add in physical zone force + force += mAppliedForce; + + // Container buoyancy & drag + force += Point3F(0, 0,-mBuoyancy * sRigidShapeGravity * mRigid.mass * mGravityMod); + force -= mRigid.linVelocity * mDrag; + torque -= mRigid.angMomentum * mDrag; + + mRigid.force = force; + mRigid.torque = torque; +} + + +//----------------------------------------------------------------------------- +/** Update collision information +Update the convex state and check for collisions. If the object is in +collision, impact and contact forces are generated. +*/ + +bool RigidShape::updateCollision(F32 dt) +{ + // Update collision information + MatrixF mat,cmat; + mConvex.transform = &mat; + mRigid.getTransform(&mat); + cmat = mConvex.getTransform(); + + mCollisionList.clear(); + CollisionState *state = mConvex.findClosestState(cmat, getScale(), mDataBlock->collisionTol); + if (state && state->dist <= mDataBlock->collisionTol) + { + //resolveDisplacement(ns,state,dt); + mConvex.getCollisionInfo(cmat, getScale(), &mCollisionList, mDataBlock->collisionTol); + } + + // Resolve collisions + bool collided = resolveCollision(mRigid,mCollisionList); + resolveContacts(mRigid,mCollisionList,dt); + return collided; +} + + +//---------------------------------------------------------------------------- +/** Resolve collision impacts +Handle collision impacts, as opposed to contacts. Impulses are calculated based +on standard collision resolution formulas. +*/ +bool RigidShape::resolveCollision(Rigid& ns,CollisionList& cList) +{ + // Apply impulses to resolve collision + bool colliding, collided = false; + + do + { + colliding = false; + for (S32 i = 0; i < cList.getCount(); i++) + { + Collision& c = cList[i]; + if (c.distance < mDataBlock->collisionTol) + { + // Velocity into surface + Point3F v,r; + ns.getOriginVector(c.point,&r); + ns.getVelocity(r,&v); + F32 vn = mDot(v,c.normal); + + // Only interested in velocities greater than sContactTol, + // velocities less than that will be dealt with as contacts + // "constraints". + if (vn < -mDataBlock->contactTol) + { + + // Apply impulses to the rigid body to keep it from + // penetrating the surface. + ns.resolveCollision(cList[i].point, + cList[i].normal); + colliding = collided = true; + + // Keep track of objects we collide with + if (!isGhost() && c.object->getTypeMask() & ShapeBaseObjectType) + { + ShapeBase* col = static_cast(c.object); + queueCollision(col,v - col->getVelocity()); + } + } + } + } + } while (colliding); + + return collided; +} + +//---------------------------------------------------------------------------- +/** Resolve contact forces +Resolve contact forces using the "penalty" method. Forces are generated based +on the depth of penetration and the moment of inertia at the point of contact. +*/ +bool RigidShape::resolveContacts(Rigid& ns,CollisionList& cList,F32 dt) +{ + // Use spring forces to manage contact constraints. + bool collided = false; + Point3F t,p(0,0,0),l(0,0,0); + for (S32 i = 0; i < cList.getCount(); i++) + { + Collision& c = cList[i]; + if (c.distance < mDataBlock->collisionTol) + { + + // Velocity into the surface + Point3F v,r; + ns.getOriginVector(c.point,&r); + ns.getVelocity(r,&v); + F32 vn = mDot(v,c.normal); + + // Only interested in velocities less than mDataBlock->contactTol, + // velocities greater than that are dealt with as collisions. + if (mFabs(vn) < mDataBlock->contactTol) + { + collided = true; + + // Penetration force. This is actually a spring which + // will seperate the body from the collision surface. + F32 zi = 2 * mFabs(mRigid.getZeroImpulse(r,c.normal)); + F32 s = (mDataBlock->collisionTol - c.distance) * zi - ((vn / mDataBlock->contactTol) * zi); + Point3F f = c.normal * s; + + // Friction impulse, calculated as a function of the + // amount of force it would take to stop the motion + // perpendicular to the normal. + Point3F uv = v - (c.normal * vn); + F32 ul = uv.len(); + if (s > 0 && ul) + { + uv /= -ul; + F32 u = ul * ns.getZeroImpulse(r,uv); + s *= mRigid.friction; + if (u > s) + u = s; + f += uv * u; + } + + // Accumulate forces + p += f; + mCross(r,f,&t); + l += t; + } + } + } + + // Contact constraint forces act over time... + ns.linMomentum += p * dt; + ns.angMomentum += l * dt; + ns.updateVelocity(); + return true; +} + + +//---------------------------------------------------------------------------- + +bool RigidShape::resolveDisplacement(Rigid& ns,CollisionState *state, F32 dt) +{ + SceneObject* obj = (state->a->getObject() == this)? + state->b->getObject(): state->a->getObject(); + + if (obj->isDisplacable() && ((obj->getTypeMask() & ShapeBaseObjectType) != 0)) + { + // Try to displace the object by the amount we're trying to move + Point3F objNewMom = ns.linVelocity * obj->getMass() * 1.1f; + Point3F objOldMom = obj->getMomentum(); + Point3F objNewVel = objNewMom / obj->getMass(); + + Point3F myCenter; + Point3F theirCenter; + getWorldBox().getCenter(&myCenter); + obj->getWorldBox().getCenter(&theirCenter); + if (mDot(myCenter - theirCenter, objNewMom) >= 0.0f || objNewVel.len() < 0.01) + { + objNewMom = (theirCenter - myCenter); + objNewMom.normalize(); + objNewMom *= 1.0f * obj->getMass(); + objNewVel = objNewMom / obj->getMass(); + } + + obj->setMomentum(objNewMom); + if (obj->displaceObject(objNewVel * 1.1f * dt) == true) + { + // Queue collision and change in velocity + VectorF dv = (objOldMom - objNewMom) / obj->getMass(); + queueCollision(static_cast(obj), dv); + return true; + } + } + + return false; +} + + +//---------------------------------------------------------------------------- + +void RigidShape::updateWorkingCollisionSet(const U32 mask) +{ + Box3F convexBox = mConvex.getBoundingBox(getTransform(), getScale()); + F32 len = (mRigid.linVelocity.len() + 50) * TickSec; + F32 l = (len * 1.1) + 0.1; // fudge factor + convexBox.minExtents -= Point3F(l, l, l); + convexBox.maxExtents += Point3F(l, l, l); + + disableCollision(); + mConvex.updateWorkingList(convexBox, mask); + enableCollision(); +} + + +//---------------------------------------------------------------------------- +/** Check collisions with trigger and items +Perform a container search using the current bounding box +of the main body, wheels are not included. This method should +only be called on the server. +*/ +void RigidShape::checkTriggers() +{ + Box3F bbox = mConvex.getBoundingBox(getTransform(), getScale()); + gServerContainer.findObjects(bbox,sTriggerMask,findCallback,this); +} + +/** The callback used in by the checkTriggers() method. +The checkTriggers method uses a container search which will +invoke this callback on each obj that matches. +*/ +void RigidShape::findCallback(SceneObject* obj,void *key) +{ + RigidShape* shape = reinterpret_cast(key); + U32 objectMask = obj->getTypeMask(); + + // Check: triggers, corpses and items, basically the same things + // that the player class checks for + if (objectMask & TriggerObjectType) { + Trigger* pTrigger = static_cast(obj); + pTrigger->potentialEnterObject(shape); + } + else if (objectMask & CorpseObjectType) { + ShapeBase* col = static_cast(obj); + shape->queueCollision(col,shape->getVelocity() - col->getVelocity()); + } + else if (objectMask & ItemObjectType) { + Item* item = static_cast(obj); + if (shape != item->getCollisionObject()) + shape->queueCollision(item,shape->getVelocity() - item->getVelocity()); + } +} + + +//---------------------------------------------------------------------------- + +void RigidShape::writePacketData(GameConnection *connection, BitStream *stream) +{ + Parent::writePacketData(connection, stream); + + mathWrite(*stream, mRigid.linPosition); + mathWrite(*stream, mRigid.angPosition); + mathWrite(*stream, mRigid.linMomentum); + mathWrite(*stream, mRigid.angMomentum); + stream->writeFlag(mRigid.atRest); + stream->writeFlag(mContacts.getCount() == 0); + + stream->writeFlag(mDisableMove); + stream->setCompressionPoint(mRigid.linPosition); +} + +void RigidShape::readPacketData(GameConnection *connection, BitStream *stream) +{ + Parent::readPacketData(connection, stream); + + mathRead(*stream, &mRigid.linPosition); + mathRead(*stream, &mRigid.angPosition); + mathRead(*stream, &mRigid.linMomentum); + mathRead(*stream, &mRigid.angMomentum); + mRigid.atRest = stream->readFlag(); + if (stream->readFlag()) + mContacts.clear(); + mRigid.updateInertialTensor(); + mRigid.updateVelocity(); + + mDisableMove = stream->readFlag(); + stream->setCompressionPoint(mRigid.linPosition); +} + + +//---------------------------------------------------------------------------- + +U32 RigidShape::packUpdate(NetConnection *con, U32 mask, BitStream *stream) +{ + U32 retMask = Parent::packUpdate(con, mask, stream); + + // The rest of the data is part of the control object packet update. + // If we're controlled by this client, we don't need to send it. + if (stream->writeFlag(getControllingClient() == con && !(mask & InitialUpdateMask))) + return retMask; + + mDelta.move.pack(stream); + + if (stream->writeFlag(mask & PositionMask)) + { + stream->writeCompressedPoint(mRigid.linPosition); + mathWrite(*stream, mRigid.angPosition); + mathWrite(*stream, mRigid.linMomentum); + mathWrite(*stream, mRigid.angMomentum); + stream->writeFlag(mRigid.atRest); + } + + if(stream->writeFlag(mask & FreezeMask)) + stream->writeFlag(mDisableMove); + + return retMask; +} + +void RigidShape::unpackUpdate(NetConnection *con, BitStream *stream) +{ + Parent::unpackUpdate(con,stream); + + if (stream->readFlag()) + return; + + mDelta.move.unpack(stream); + + if (stream->readFlag()) + { + mPredictionCount = sMaxPredictionTicks; + F32 speed = mRigid.linVelocity.len(); + mDelta.warpRot[0] = mRigid.angPosition; + + // Read in new position and momentum values + stream->readCompressedPoint(&mRigid.linPosition); + mathRead(*stream, &mRigid.angPosition); + mathRead(*stream, &mRigid.linMomentum); + mathRead(*stream, &mRigid.angMomentum); + mRigid.atRest = stream->readFlag(); + mRigid.updateVelocity(); + + if (isProperlyAdded()) + { + // Determine number of ticks to warp based on the average + // of the client and server velocities. + Point3F cp = mDelta.pos + mDelta.posVec * mDelta.dt; + mDelta.warpOffset = mRigid.linPosition - cp; + + // Calc the distance covered in one tick as the average of + // the old speed and the new speed from the server. + F32 dt,as = (speed + mRigid.linVelocity.len()) * 0.5 * TickSec; + + // Cal how many ticks it will take to cover the warp offset. + // If it's less than what's left in the current tick, we'll just + // warp in the remaining time. + if (!as || (dt = mDelta.warpOffset.len() / as) > sMaxWarpTicks) + dt = mDelta.dt + sMaxWarpTicks; + else + dt = (dt <= mDelta.dt)? mDelta.dt : mCeil(dt - mDelta.dt) + mDelta.dt; + + // Adjust current frame interpolation + if (mDelta.dt) + { + mDelta.pos = cp + (mDelta.warpOffset * (mDelta.dt / dt)); + mDelta.posVec = (cp - mDelta.pos) / mDelta.dt; + QuatF cr; + cr.interpolate(mDelta.rot[1],mDelta.rot[0],mDelta.dt); + mDelta.rot[1].interpolate(cr,mRigid.angPosition,mDelta.dt / dt); + mDelta.rot[0].extrapolate(mDelta.rot[1],cr,mDelta.dt); + } + + // Calculated multi-tick warp + mDelta.warpCount = 0; + mDelta.warpTicks = (S32)(mFloor(dt)); + if (mDelta.warpTicks) + { + mDelta.warpOffset = mRigid.linPosition - mDelta.pos; + mDelta.warpOffset /= mDelta.warpTicks; + mDelta.warpRot[0] = mDelta.rot[1]; + mDelta.warpRot[1] = mRigid.angPosition; + } + } + else + { + // Set the shape to the server position + mDelta.dt = 0; + mDelta.pos = mRigid.linPosition; + mDelta.posVec.set(0,0,0); + mDelta.rot[1] = mDelta.rot[0] = mRigid.angPosition; + mDelta.warpCount = mDelta.warpTicks = 0; + setPosition(mRigid.linPosition, mRigid.angPosition); + } + } + + if(stream->readFlag()) + mDisableMove = stream->readFlag(); +} + + +//---------------------------------------------------------------------------- + +void RigidShape::initPersistFields() +{ + Parent::initPersistFields(); +} + +//---------------------------------------------------------------------------- + +void RigidShape::updateLiftoffDust( F32 dt ) +{ + Point3F offset( 0.0, 0.0, mDataBlock->dustHeight ); + emitDust( mDustEmitterList[ 0 ], mDataBlock->triggerDustHeight, offset, + ( U32 )( dt * 1000 ) ); +} + +//-------------------------------------------------------------------------- +void RigidShape::updateFroth( F32 dt ) +{ + // update bubbles + Point3F moveDir = getVelocity(); + + Point3F contactPoint; + + F32 speed = moveDir.len(); + if( speed < mDataBlock->splashVelEpsilon ) speed = 0.0; + + U32 emitRate = (U32)(speed * mDataBlock->splashFreqMod * dt); + + U32 i; + for( i=0; iemitParticles( contactPoint, contactPoint, Point3F( 0.0, 0.0, 1.0 ), + moveDir, emitRate ); + } + } + +} + +//-------------------------------------------------------------------------- +// Returns true if shape is intersecting a water surface (roughly) +//-------------------------------------------------------------------------- +bool RigidShape::collidingWithWater( Point3F &waterHeight ) +{ + Point3F curPos = getPosition(); + + F32 height = mFabs( mObjBox.maxExtents.z - mObjBox.minExtents.z ); + + RayInfo rInfo; + if( gClientContainer.castRay( curPos + Point3F(0.0, 0.0, height), curPos, WaterObjectType, &rInfo) ) + { + waterHeight = rInfo.point; + return true; + } + + return false; +} + +void RigidShape::setEnergyLevel(F32 energy) +{ + Parent::setEnergyLevel(energy); + setMaskBits(EnergyMask); +} + +void RigidShape::prepBatchRender( SceneRenderState *state, S32 mountedImageIndex ) +{ + Parent::prepBatchRender( state, mountedImageIndex ); + + if ( !gShowBoundingBox ) + return; + + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &RigidShape::_renderMassAndContacts ); + ri->type = RenderPassManager::RIT_Editor; + state->getRenderPass()->addInst( ri ); +} + +void RigidShape::_renderMassAndContacts( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ) +{ + // Box for the center of Mass + GFXStateBlockDesc desc; + desc.setBlend(false, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha); + desc.setZReadWrite(false); + desc.fillMode = GFXFillWireframe; + + GFX->getDrawUtil()->drawCube( desc, Point3F(0.1f,0.1f,0.1f), mDataBlock->massCenter, ColorI(255, 255, 255), &mRenderObjToWorld ); + + // Collision points... + for (int i = 0; i < mCollisionList.getCount(); i++) + { + const Collision& collision = mCollisionList[i]; + GFX->getDrawUtil()->drawCube( desc, Point3F(0.05f,0.05f,0.05f), collision.point, ColorI(0, 0, 255) ); + } + + // Render the normals as one big batch... + PrimBuild::begin(GFXLineList, mCollisionList.getCount() * 2); + for (int i = 0; i < mCollisionList.getCount(); i++) + { + + const Collision& collision = mCollisionList[i]; + PrimBuild::color3f(1, 1, 1); + PrimBuild::vertex3fv(collision.point); + PrimBuild::vertex3fv(collision.point + collision.normal * 0.05f); + } + PrimBuild::end(); + + // Build and render the collision polylist which is returned + // in the server's world space. + ClippedPolyList polyList; + polyList.mPlaneList.setSize(6); + polyList.mPlaneList[0].set(getWorldBox().minExtents,VectorF(-1,0,0)); + polyList.mPlaneList[1].set(getWorldBox().minExtents,VectorF(0,-1,0)); + polyList.mPlaneList[2].set(getWorldBox().minExtents,VectorF(0,0,-1)); + polyList.mPlaneList[3].set(getWorldBox().maxExtents,VectorF(1,0,0)); + polyList.mPlaneList[4].set(getWorldBox().maxExtents,VectorF(0,1,0)); + polyList.mPlaneList[5].set(getWorldBox().maxExtents,VectorF(0,0,1)); + Box3F dummyBox; + SphereF dummySphere; + buildPolyList(PLC_Collision, &polyList, dummyBox, dummySphere); + //polyList.render(); +} + +void RigidShape::reset() +{ + mRigid.clearForces(); + mRigid.setAtRest(); +} + +void RigidShape::freezeSim(bool frozen) +{ + mDisableMove = frozen; + setMaskBits(FreezeMask); +} + +DefineEngineMethod( RigidShape, reset, void, (),, + "@brief Clears physic forces from the shape and sets it at rest.\n\n" + "@tsexample\n" + "// Inform the RigidShape object to reset.\n" + "%thisRigidShape.reset();\n" + "@endtsexample\n\n" + "@see ShapeBaseData") +{ + object->reset(); +} + +DefineEngineMethod( RigidShape, freezeSim, void, (bool isFrozen),, + "@brief Enables or disables the physics simulation on the RigidShape object.\n\n" + "@param isFrozen Boolean frozen state to set the object.\n" + "@tsexample\n" + "// Define the frozen state.\n" + "%isFrozen = \"true\";\n\n" + "// Inform the object of the defined frozen state\n" + "%thisRigidShape.freezeSim(%isFrozen);\n" + "@endtsexample\n\n" + "@see ShapeBaseData") +{ + object->freezeSim(isFrozen); +} diff --git a/Engine/source/T3D/rigidShape.h b/Engine/source/T3D/rigidShape.h new file mode 100644 index 000000000..2a05f445a --- /dev/null +++ b/Engine/source/T3D/rigidShape.h @@ -0,0 +1,299 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _RIGIDSHAPE_H_ +#define _RIGIDSHAPE_H_ + +#ifndef _SHAPEBASE_H_ +#include "T3D/shapeBase.h" +#endif +#ifndef _RIGID_H_ +#include "T3D/rigid.h" +#endif +#ifndef _BOXCONVEX_H_ +#include "collision/boxConvex.h" +#endif + +class ParticleEmitter; +class ParticleEmitterData; +class ClippedPolyList; + + +class RigidShapeData : public ShapeBaseData +{ + typedef ShapeBaseData Parent; + + protected: + bool onAdd(); + + //-------------------------------------- Console set variables + public: + + struct Body + { + enum Sounds + { + SoftImpactSound, + HardImpactSound, + MaxSounds, + }; + SFXTrack* sound[MaxSounds]; + F32 restitution; + F32 friction; + } body; + + enum RigidShapeConsts + { + VC_NUM_DUST_EMITTERS = 1, + VC_NUM_BUBBLE_EMITTERS = 1, + VC_NUM_SPLASH_EMITTERS = 2, + VC_BUBBLE_EMITTER = VC_NUM_BUBBLE_EMITTERS, + }; + + enum Sounds + { + ExitWater, + ImpactSoft, + ImpactMedium, + ImpactHard, + Wake, + MaxSounds + }; + SFXTrack* waterSound[MaxSounds]; + + F32 exitSplashSoundVel; + F32 softSplashSoundVel; + F32 medSplashSoundVel; + F32 hardSplashSoundVel; + + F32 minImpactSpeed; + F32 softImpactSpeed; + F32 hardImpactSpeed; + F32 minRollSpeed; + + bool cameraRoll; ///< Roll the 3rd party camera + F32 cameraLag; ///< Amount of camera lag (lag += car velocity * lag) + F32 cameraDecay; ///< Rate at which camera returns to target pos. + F32 cameraOffset; ///< Vertical offset + + F32 minDrag; + F32 maxDrag; + S32 integration; ///< # of physics steps per tick + F32 collisionTol; ///< Collision distance tolerance + F32 contactTol; ///< Contact velocity tolerance + Point3F massCenter; ///< Center of mass for rigid body + Point3F massBox; ///< Size of inertial box + + ParticleEmitterData * dustEmitter; + S32 dustID; + F32 triggerDustHeight; ///< height shape has to be under to kick up dust + F32 dustHeight; ///< dust height above ground + + ParticleEmitterData* splashEmitterList[VC_NUM_SPLASH_EMITTERS]; + S32 splashEmitterIDList[VC_NUM_SPLASH_EMITTERS]; + F32 splashFreqMod; + F32 splashVelEpsilon; + + + F32 dragForce; + F32 vertFactor; + + ParticleEmitterData * dustTrailEmitter; + S32 dustTrailID; + + //-------------------------------------- load set variables + + public: + RigidShapeData(); + ~RigidShapeData(); + + static void initPersistFields(); + void packData(BitStream*); + void unpackData(BitStream*); + bool preload(bool server, String &errorStr); + + DECLARE_CONOBJECT(RigidShapeData); + +}; + + +//---------------------------------------------------------------------------- + +class RigidShape: public ShapeBase +{ + typedef ShapeBase Parent; + + private: + RigidShapeData* mDataBlock; + SimObjectPtr mDustTrailEmitter; + + protected: + enum CollisionFaceFlags + { + BodyCollision = BIT(0), + WheelCollision = BIT(1), + }; + enum MaskBits { + PositionMask = Parent::NextFreeMask << 0, + EnergyMask = Parent::NextFreeMask << 1, + FreezeMask = Parent::NextFreeMask << 2, + NextFreeMask = Parent::NextFreeMask << 3 + }; + + void updateDustTrail( F32 dt ); + + + struct StateDelta + { + Move move; ///< Last move from server + F32 dt; ///< Last interpolation time + // Interpolation data + Point3F pos; + Point3F posVec; + QuatF rot[2]; + // Warp data + S32 warpTicks; ///< Number of ticks to warp + S32 warpCount; ///< Current pos in warp + Point3F warpOffset; + QuatF warpRot[2]; + // + Point3F cameraOffset; + Point3F cameraVec; + Point3F cameraRot; + Point3F cameraRotVec; + }; + + StateDelta mDelta; + S32 mPredictionCount; ///< Number of ticks to predict + bool inLiquid; + + Point3F mCameraOffset; ///< 3rd person camera + + // Rigid Body + bool mDisableMove; + + CollisionList mCollisionList; + CollisionList mContacts; + Rigid mRigid; + ShapeBaseConvex mConvex; + int restCount; + + SimObjectPtr mDustEmitterList[RigidShapeData::VC_NUM_DUST_EMITTERS]; + SimObjectPtr mSplashEmitterList[RigidShapeData::VC_NUM_SPLASH_EMITTERS]; + + GFXStateBlockRef mSolidSB; + + // + bool onNewDataBlock( GameBaseData *dptr, bool reload ); + void updatePos(F32 dt); + bool updateCollision(F32 dt); + bool resolveCollision(Rigid& ns,CollisionList& cList); + bool resolveContacts(Rigid& ns,CollisionList& cList,F32 dt); + bool resolveDisplacement(Rigid& ns,CollisionState *state,F32 dt); + bool findContacts(Rigid& ns,CollisionList& cList); + void checkTriggers(); + static void findCallback(SceneObject* obj,void * key); + + void setPosition(const Point3F& pos,const QuatF& rot); + void setRenderPosition(const Point3F& pos,const QuatF& rot); + void setTransform(const MatrixF& mat); + +// virtual bool collideBody(const MatrixF& mat,Collision* info) = 0; + void updateMove(const Move* move); + + void writePacketData(GameConnection * conn, BitStream *stream); + void readPacketData (GameConnection * conn, BitStream *stream); + + void updateLiftoffDust( F32 dt ); + + void updateWorkingCollisionSet(const U32 mask); + U32 getCollisionMask(); + + void updateFroth( F32 dt ); + bool collidingWithWater( Point3F &waterHeight ); + + void _renderMassAndContacts( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); + + void updateForces(F32); + +public: + // Test code... + static ClippedPolyList* sPolyList; + + // + RigidShape(); + ~RigidShape(); + + static void initPersistFields(); + void processTick(const Move *move); + bool onAdd(); + void onRemove(); + + /// Interpolates between move ticks @see processTick + /// @param dt Change in time between the last call and this call to the function + void interpolateTick(F32 dt); + void advanceTime(F32 dt); + + /// Disables collisions for this shape + void disableCollision(); + + /// Enables collisions for this shape + void enableCollision(); + + /// Returns the velocity of the shape + Point3F getVelocity() const; + + void setEnergyLevel(F32 energy); + + void prepBatchRender( SceneRenderState *state, S32 mountedImageIndex ); + + // xgalaxy cool hacks + void reset(); + void freezeSim(bool frozen); + + ///@name Rigid body methods + ///@{ + + /// This method will get the velocity of the object, taking into account + /// angular velocity. + /// @param r Point on the object you want the velocity of, relative to Center of Mass + /// @param vel Velocity (out) + void getVelocity(const Point3F& r, Point3F* vel); + + /// Applies an impulse force + /// @param r Point on the object to apply impulse to, r is relative to Center of Mass + /// @param impulse Impulse vector to apply. + void applyImpulse(const Point3F &r, const Point3F &impulse); + + void getCameraParameters(F32 *min, F32* max, Point3F* offset, MatrixF* rot); + void getCameraTransform(F32* pos, MatrixF* mat); + ///@} + + U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream); + void unpackUpdate(NetConnection *conn, BitStream *stream); + + DECLARE_CONOBJECT(RigidShape); + DECLARE_CALLBACK( void, onEnterLiquid, ( const char* objId, const char* waterCoverage, const char* liquidType )); + DECLARE_CALLBACK( void, onLeaveLiquid, ( const char* objId, const char* liquidType )); +}; + + +#endif diff --git a/Engine/source/T3D/sceneComponent/T3DSceneClient.cpp b/Engine/source/T3D/sceneComponent/T3DSceneClient.cpp new file mode 100644 index 000000000..fdb2069f7 --- /dev/null +++ b/Engine/source/T3D/sceneComponent/T3DSceneClient.cpp @@ -0,0 +1,107 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "T3DSceneClient.h" + +//--------------------------------------------------- +// T3DSceneClient +//--------------------------------------------------- + +void T3DSceneClient::setSceneGroupName(const char * name) +{ + _sceneGroupName = StringTable->insert(name); + if (getOwner() != NULL) + { + if (_sceneGroup != NULL) + _sceneGroup->RemoveClientObject(this); + _sceneGroup = NULL; + + ValueWrapperInterface * iface = getInterface >("sceneComponent", _sceneGroupName); + if (iface != NULL) + { + _sceneGroup = iface->get(); + _sceneGroup->AddSceneClient(this); + } + } +} + +bool T3DSceneClient::onComponentRegister(SimComponent * owner) +{ + if (!Parent::onComponentRegister(owner)) + return false; + + // lookup scene group and add ourself + setSceneGroupName(_sceneGroupName); + + if (_sceneGroupName != NULL && dStricmp(_sceneGroupName, "none") && _sceneGroup == NULL) + // tried to add ourself to a scene group but failed, fail to add component + return false; + + return true; +} + +void T3DSceneClient::registerInterfaces(SimComponent * owner) +{ + Parent::registerInterfaces(owner); + registerCachedInterface("sceneClient", NULL, this, new ValueWrapperInterface()); +} + +//--------------------------------------------------- +// T3DSceneClient +//--------------------------------------------------- + +Box3F T3DSolidSceneClient::getWorldBox() +{ + MatrixF mat = getTransform(); + Box3F box = _objectBox->get(); + mat.mul(box); + return box; +} + +const MatrixF & T3DSolidSceneClient::getTransform() +{ + if (_transform != NULL) + return _transform->getWorldMatrix(); + else if (getSceneGroup() != NULL) + return getSceneGroup()->getTransform3D()->getWorldMatrix(); + else + return MatrixF::smIdentity; +} + +void T3DSolidSceneClient::setTransform3D(Transform3D * transform) +{ + if (_transform != NULL) + _transform->setDirtyListener(NULL); + _transform = transform; + + _transform->setDirtyListener(this); + OnTransformDirty(); +} + +void T3DSolidSceneClient::OnTransformDirty() +{ + // TODO: need a way to skip this...a flag, but we don't want to add a bool just for that + // reason we might want to skip it is if we have a renderable that orbits an object but always + // stays within object box. Want to be able to use that info to skip object box updates. + if (getSceneGroup() != NULL) + getSceneGroup()->setDirtyObjectBox(true); +} diff --git a/Engine/source/T3D/sceneComponent/T3DSceneClient.h b/Engine/source/T3D/sceneComponent/T3DSceneClient.h new file mode 100644 index 000000000..ca65c0ee4 --- /dev/null +++ b/Engine/source/T3D/sceneComponent/T3DSceneClient.h @@ -0,0 +1,93 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _T3DSCENECLIENT_H_ +#define _T3DSCENECLIENT_H_ + +#include "component/simComponent.h" +#include "T3DSceneComponent.h" + +class T3DSceneClient : public SimComponent +{ + typedef SimComponent Parent; + +public: + T3DSceneClient() + { + _nextClient = NULL; + _sceneGroup = NULL; + _sceneGroupName = NULL; + _sceneClientName = NULL; + } + + T3DSceneClient * getNextSceneClient() { return _nextClient; } + // TODO: internal + void setNextSceneClient(T3DSceneClient * client) { _nextClient = client; } + + T3DSceneComponent * getSceneGroup() { return _sceneGroup; } + + StringTableEntry getSceneGroupName() { return _sceneGroupName; } + void setSceneGroupName(const char * name); + + StringTableEntry getSceneClientName() { return _sceneClientName; } + void setSceneClientName(const char * name) { _sceneClientName = StringTable->insert(name); } + +protected: + + bool onComponentRegister(SimComponent * owner); + void registerInterfaces(SimComponent * owner); + + T3DSceneClient * _nextClient; + T3DSceneComponent * _sceneGroup; + StringTableEntry _sceneGroupName; + StringTableEntry _sceneClientName; +}; + +class T3DSolidSceneClient : public T3DSceneClient, public ISolid3D, public Transform3D::IDirtyListener +{ +public: + + T3DSolidSceneClient() + { + _transform = NULL; + _objectBox = new ValueWrapperInterface(); + } + + Box3F getObjectBox() { return _objectBox->get(); } + void setObjectBox(const Box3F & box) { _objectBox->set(box); } + Box3F getWorldBox(); + const MatrixF & getTransform(); + Transform3D * getTransform3D() { return _transform; } + + void OnTransformDirty(); + +protected: + + + // TODO: internal + void setTransform3D(Transform3D * transform); + + Transform3D * _transform; + ValueWrapperInterface * _objectBox; +}; + +#endif // #ifndef _T3DSCENECLIENT_H_ diff --git a/Engine/source/T3D/sceneComponent/T3DSceneComponent.cpp b/Engine/source/T3D/sceneComponent/T3DSceneComponent.cpp new file mode 100644 index 000000000..08a16d605 --- /dev/null +++ b/Engine/source/T3D/sceneComponent/T3DSceneComponent.cpp @@ -0,0 +1,289 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "T3DSceneComponent.h" +#include "T3DSceneClient.h" + +void T3DSceneComponent::setSceneGroup(const char * sceneGroup) +{ + AssertFatal(getOwner()==NULL, "Changing scene group name after registration will have no effect."); + if (sceneGroup == NULL) + sceneGroup = StringTable->insert(""); + _sceneGroup = StringTable->insert(sceneGroup); +} + +void T3DSceneComponent::setParentTransformName(const char * name) +{ + _parentTransformName = StringTable->insert(name); + if (getOwner() != NULL) + { + Transform3D * old = _transform->getParentTransform(); + ValueWrapperInterface * iface = NULL; + if (_parentTransformName != NULL) + iface = getInterface >("transform3D", _parentTransformName); + _transform->setParentTransform(iface == NULL ? NULL : iface->get()); + if (_transform->getParentTransform() != old) + setDirtyWorldBox(true); + } +} + +void T3DSceneComponent::setObjectType(U32 objectTypeMask) +{ + _objectType = objectTypeMask; + setUseOwnerObjectType(true); +} + +void T3DSceneComponent::setDirtyObjectBox(bool val) +{ + _SetFlag(T3DSceneComponent::DirtyObjectBox, val); + if (val && !isObjectBoxLocked()) + _ComputeObjectBox(); +} + +void T3DSceneComponent::setDirtyWorldBox(bool val) +{ + _SetFlag(T3DSceneComponent::DirtyWorldBox, val); + if (val && !isWorldBoxLocked()) + _UpdateWorldBox(); +} + +void T3DSceneComponent::setObjectBoxLocked(bool val) +{ + _SetFlag(T3DSceneComponent::LockObjectBox, val); + if (!val && isDirtyObjectBox()) + _ComputeObjectBox(); +} + +void T3DSceneComponent::setWorldBoxLocked(bool val) +{ + _SetFlag(T3DSceneComponent::LockWorldBox, val); + if (!val && isDirtyWorldBox()) + _UpdateWorldBox(); +} + +void T3DSceneComponent::setPosition(const Point3F & pos) +{ + _transform->setPosition(pos); + setDirtyWorldBox(true); +} + +void T3DSceneComponent::setRotation(const QuatF & rotation) +{ + _transform->setRotation(rotation); + setDirtyWorldBox(true); +} + +void T3DSceneComponent::setScale(const Point3F & scale) +{ + _transform->setScale (scale); + Parent::setScale(scale); + setDirtyWorldBox(true); +} + +void T3DSceneComponent::setTransform3D(Transform3D * transform) +{ + if (transform == NULL) + // never let it be null + return; + if (_transform != NULL) + delete _transform; + _transform = transform; + _transform->setDirtyListener(this); +} + +void T3DSceneComponent::setTransform(const MatrixF & mat) +{ + _transform->setLocalMatrix(mat); + Parent::setTransform(mat); + setDirtyWorldBox(true); +} + +Box3F T3DSceneComponent::getObjectBox() +{ + if (DirtyObjectBox && !LockObjectBox) + _ComputeObjectBox(); + + return _objectBox->get(); +} + +Box3F T3DSceneComponent::getWorldBox() +{ + if (DirtyWorldBox && !LockWorldBox) + _UpdateWorldBox(); + + MatrixF mat = getTransform3D()->getWorldMatrix(); + Box3F box = getObjectBox(); + + mat.mul(box); + return box; +} + +void T3DSceneComponent::Render() +{ + if (_sceneClientList == NULL) + return; + + PROFILE_SCOPE(T3DSceneComponent_Render); + + GFX->multWorld(_transform->getWorldMatrix()); + + if (doRenderObjectBounds()) + { + // TODO + } + if (doRenderWorldBounds()) + { + // TODO + } + + for (T3DSceneClient * walk = _sceneClientList; walk != NULL; walk = walk->getNextSceneClient()) + { + IRenderable3D * render = dynamic_cast(walk); + if (render != NULL) + render->Render(); + } + + GFX->popWorldMatrix(); +} + +void T3DSceneComponent::AddSceneClient(T3DSceneClient * sceneClient) +{ + AssertFatal(sceneClient,"Cannot add a NULL scene client"); + + // add to the front of the list + sceneClient->setNextSceneClient(_sceneClientList); + _sceneClientList = sceneClient; + + // extend object box + ISolid3D * solid = dynamic_cast(sceneClient); + if (solid != NULL) + { + if (isObjectBoxLocked()) + setDirtyObjectBox(true); + else + { + Box3F box = solid->getObjectBox(); + if (solid->getTransform3D() != NULL) + { + MatrixF mat; + solid->getTransform3D()->getObjectMatrix(mat, true); + mat.mul(box); + } + box.extend(_objectBox->get().min); + box.extend(_objectBox->get().max); + _objectBox->set(box); + } + + // policy is that we become parent transform + if (solid->getTransform3D() != NULL && solid->getTransform3D()->isChildOf(_transform, true)) + solid->getTransform3D()->setParentTransform(_transform); + } +} + +void T3DSceneComponent::RemoveClientObject(T3DSceneClient * sceneClient) +{ + AssertFatal(sceneClient != NULL, "Removing null scene client"); + if (sceneClient == NULL) + return; + if (_sceneClientList == sceneClient) + _sceneClientList = _sceneClientList->getNextSceneClient(); + else + { + T3DSceneClient * walk = _sceneClientList; + while (walk->getNextSceneClient() != sceneClient && walk != NULL) + walk = walk->getNextSceneClient(); + if (walk != NULL) + walk->setNextSceneClient(sceneClient->getNextSceneClient()); + } + + if (dynamic_cast(sceneClient)) + setDirtyObjectBox(true); +} + +bool T3DSceneComponent::onComponentRegister(SimComponent * owner) +{ + if (!Parent::onComponentRegister(owner) || !registerObject()) + return false; + + // Note: was added to scene graph in register object + + setTransform3D(_transform); + setParentTransformName(_parentTransformName); + + return true; +} + +void T3DSceneComponent::onComponentUnRegister() +{ + _sceneClientList = NULL; + Parent::onComponentUnRegister(); +} + +void T3DSceneComponent::registerInterfaces(SimComponent * owner) +{ + Parent::registerInterfaces(owner); + + // TODO: need to figure out the best way to wrap these + // we don't need to track these interfaces ourselves -- just create them here + ComponentInterface * sceneComponent = new ComponentInterface(); + ValueWrapperInterface * transform = new ValueWrapperInterface(_transform); + + registerCachedInterface("sceneComponent", _sceneGroup, owner, sceneComponent); + registerCachedInterface("transform3D", _sceneGroup, owner, transform); + registerCachedInterface("box", _sceneGroup, this, _objectBox); +} + +void T3DSceneComponent::_ComputeObjectBox() +{ + _objectBox->set(Box3F(10E30f, 10E30f, 10E30f, -10E30f, -10E30f, -10E30f)); + bool gotone = false; + for (T3DSceneClient * walk = _sceneClientList; walk != NULL; walk = walk->getNextSceneClient()) + { + ISolid3D * solid = dynamic_cast(walk); + if (solid == NULL) + continue; + + Box3F box = solid->getObjectBox(); + if (solid->getTransform3D() != NULL) + { + MatrixF mat; + solid->getTransform3D()->getObjectMatrix(mat, true); + mat.mul(box); + } + box.extend(_objectBox->get().min); + box.extend(_objectBox->get().max); + _objectBox->set(box); + gotone = true; + } + if (!gotone) + _objectBox->set(Box3F()); + + setDirtyObjectBox(false); + setDirtyWorldBox(true); +} + +void T3DSceneComponent::_UpdateWorldBox() +{ + setDirtyWorldBox(false); + // TODO: + // T3DSceneGraph.Instance.UpdateObject(this); +} diff --git a/Engine/source/T3D/sceneComponent/T3DSceneComponent.h b/Engine/source/T3D/sceneComponent/T3DSceneComponent.h new file mode 100644 index 000000000..7532d03cc --- /dev/null +++ b/Engine/source/T3D/sceneComponent/T3DSceneComponent.h @@ -0,0 +1,178 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _T3DSCENECOMPONENT_H_ +#define _T3DSCENECOMPONENT_H_ + +#include "T3DTransform.h" +#include "component/simComponent.h" +#include "sceneGraph/sceneobject.h" + +class T3DSceneClient; + +class IRenderable3D +{ +public: + // TODO: what parameters to use? + virtual void Render() = 0; +}; + +class ISolid3D +{ +public: + virtual Box3F getObjectBox() = 0; + virtual Transform3D * getTransform3D() = 0; +}; + +class T3DSceneComponent : public SceneObject, public Transform3D::IDirtyListener +{ + typedef SceneObject Parent; + +public: + + T3DSceneComponent() + { + _transform = new Transform3DInPlace(); + _objectBox = new ValueWrapperInterface(); + _flags = T3DSceneComponent::Visible | T3DSceneComponent::DirtyObjectBox | T3DSceneComponent::DirtyWorldBox | T3DSceneComponent::UseOwnerObjectType; + _visibilityLevel = 1.0f; + _objectType = 0; + _sceneGroup = NULL; + _parentTransformName = NULL; + setSceneGroup(NULL); + } + + StringTableEntry getSceneGroup() const { return _sceneGroup; } + void setSceneGroup(const char * sceneGroup); + + StringTableEntry getParentTransformName() const { return _parentTransformName; } + void setParentTransformName(const char * name); + + U32 getObjectType() const { return _objectType; } // TODO: use owner if useowner set + void setObjectType(U32 objectTypeMask); + + void setUseOwnerObjectType(bool val) { _SetFlag(T3DSceneComponent::UseOwnerObjectType, val); } + bool useOwnerObjectType() const { return _TestFlag(T3DSceneComponent::UseOwnerObjectType); } + + bool isDirtyObjectBox() const { return _TestFlag(T3DSceneComponent::DirtyObjectBox); } + void setDirtyObjectBox(bool val); + + bool isDirtyWorldBox() const { return _TestFlag(T3DSceneComponent::DirtyWorldBox); } + void setDirtyWorldBox(bool val); + + bool isObjectBoxLocked() const { return _TestFlag(T3DSceneComponent::LockObjectBox); } + void setObjectBoxLocked(bool val); + + bool isWorldBoxLocked() const { return _TestFlag(T3DSceneComponent::LockWorldBox); } + void setWorldBoxLocked(bool val); + + bool doRenderObjectBounds() const { return _TestFlag(T3DSceneComponent::RenderObjectBounds); } + void setRenderObjectBounds(bool val) { _SetFlag(T3DSceneComponent::RenderObjectBounds, val); } + + bool doRenderWorldBounds() const { return _TestFlag(T3DSceneComponent::RenderWorldBounds); } + void setRenderWorldBounds(bool val) { _SetFlag(T3DSceneComponent::RenderWorldBounds, val); } + + bool doRenderSubBounds() const { return _TestFlag(T3DSceneComponent::RenderSubBounds); } + void setRenderSubBounds(bool val) { _SetFlag(T3DSceneComponent::RenderSubBounds, val); } + + bool isVisible() const { return _TestFlag(T3DSceneComponent::Visible); } + void setVisible(bool val) { _SetFlag(T3DSceneComponent::Visible, val); } + + float getVisibilityLevel() const { return _visibilityLevel; } + void setVisibilityLevel(float val) { _visibilityLevel = val; } + + Point3F getPosition() const { return _transform->getPosition(); } + void setPosition(const Point3F & pos); + + QuatF getRotation() const { return _transform->getRotation(); } + void setRotation(const QuatF & rotation); + + Point3F getScale() const { return _transform->getScale(); } + void setScale(const Point3F & scale); + + Transform3D * getTransform3D(){ return _transform; } + + void setTransform(const MatrixF & mat); + + Box3F getObjectBox(); + Box3F getWorldBox(); + + T3DSceneClient * getSceneClientList() const { return _sceneClientList; } + + void Render(); + + void AddSceneClient(T3DSceneClient * sceneClient); + void RemoveClientObject(T3DSceneClient * sceneClient); + + void OnTransformDirty() { setDirtyWorldBox(true); } + +protected: + + bool onComponentRegister(SimComponent * owner); + void onComponentUnRegister(); + + void registerInterfaces(SimComponent * owner); + + void _ComputeObjectBox(); + void _UpdateWorldBox(); + void setTransform3D(Transform3D * transform); + + bool _TestFlag(U32 test) const + { + return (_flags & test) != T3DSceneComponent::None; + } + + void _SetFlag(U32 test, bool value) + { + if (value) + _flags |= test; + else + _flags &= ~test; + } + + enum SceneFlags + { + None = 0, + Visible = 1 << 0, + DirtyObjectBox = 1 << 1, + DirtyWorldBox = 1 << 2, + LockObjectBox = 1 << 3, + LockWorldBox = 1 << 4, + UseOwnerObjectType = 1 << 5, + RenderDebug = 1 << 6, + RenderObjectBounds = 1 << 7, + RenderWorldBounds = 1 << 8, + RenderSubBounds = 1 << 9, + LastFlag = 1 << 9 + }; + + Transform3D * _transform; + T3DSceneClient * _sceneClientList; + ValueWrapperInterface * _objectBox; + U32 _flags; + float _visibilityLevel; + U32 _objectType; + StringTableEntry _sceneGroup; + StringTableEntry _parentTransformName ; +}; + +#endif // #ifndef _T3DSCENECOMPONENT_H_ diff --git a/Engine/source/T3D/sceneComponent/T3DTransform.cpp b/Engine/source/T3D/sceneComponent/T3DTransform.cpp new file mode 100644 index 000000000..761ac24f2 --- /dev/null +++ b/Engine/source/T3D/sceneComponent/T3DTransform.cpp @@ -0,0 +1,386 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "T3DTransform.h" +#include "math/mPoint.h" +#include "math/mQuat.h" + +//--------------------------------------------------------- +// T3DTransform +//--------------------------------------------------------- + +void Transform3D::setParentTransform(Transform3D * parent) +{ + if (_parentTransform == parent) + return; + _flags |= Transform3D::ParentDirty; + _parentTransform = parent; + if (_dirtyListener != NULL) + _dirtyListener->onTransformDirty(); +} + +bool Transform3D::hasObjectScale() const +{ + if (hasLocalScale()) + return true; + + // check all parent transforms except last for scale + Transform3D * walk = _parentTransform; + while (walk != NULL && walk->_parentTransform != NULL) + { + if (walk->hasLocalScale()) + return true; + walk = walk->getParentTransform(); + } + return false; +} + +bool Transform3D::hasWorldScale() const +{ + if (hasLocalScale()) + return true; + + // check all parent transforms for scale + Transform3D * walk = _parentTransform; + while (walk != NULL) + { + if (walk->hasLocalScale()) + return true; + walk = walk->getParentTransform(); + } + return false; +} + +void Transform3D::setWorldMatrix(const MatrixF & world) +{ + if (_parentTransform != NULL) + { + MatrixF parentMatrix; + _parentTransform->getWorldMatrix(parentMatrix, true); + MatrixF parentMatrixInv = parentMatrix; + parentMatrixInv.inverse(); + MatrixF localMat; + localMat = parentMatrixInv * world; + setLocalMatrix(localMat); + } + else + setLocalMatrix(world); +} + +void Transform3D::setObjectMatrix(const MatrixF & objMatrix) +{ + if (_parentTransform != NULL) + { + MatrixF parentMatrix; + _parentTransform->getObjectMatrix(parentMatrix, true); + MatrixF parentMatrixInv = parentMatrix; + parentMatrixInv.inverse(); + MatrixF localMat; + localMat = parentMatrixInv * objMatrix; + setLocalMatrix(localMat); + } + else + setLocalMatrix(objMatrix); +} + +bool Transform3D::isChildOf(Transform3D * parent, bool recursive) const +{ + if (_parentTransform != NULL) + { + if (_parentTransform == parent) + return true; + else if (recursive) + return _parentTransform->isChildOf(parent, true); + else + return false; + } + else + { + return false; + } +} + +//--------------------------------------------------------- +// Transform3DInPlace +//--------------------------------------------------------- + +Point3F Transform3DInPlace::getPosition() const +{ + return _position; +} + +void Transform3DInPlace::setPosition(const Point3F & position) +{ + _position = position; + _flags |= Transform3D::LocalPositionDirty; + if (_dirtyListener != NULL) + _dirtyListener->onTransformDirty(); +} + +QuatF Transform3DInPlace::getRotation() const +{ + return _rotation; +} + +void Transform3DInPlace::setRotation(const QuatF & rotation) +{ + _rotation = rotation; + _flags |= Transform3D::LocalRotationDirty; + if (_dirtyListener != NULL) + _dirtyListener->onTransformDirty(); +} + +Point3F Transform3DInPlace::getScale() const +{ + return _scale; +} + +void Transform3DInPlace::setScale(const Point3F & scale) +{ + _scale = scale; + _flags |= Transform3D::LocalScaleDirty; + if (_dirtyListener != NULL) + _dirtyListener->onTransformDirty(); +} + +void Transform3DInPlace::getWorldMatrix(MatrixF & worldMat, bool includeLocalScale) const +{ + if (_parentTransform == NULL) + getLocalMatrix(worldMat, includeLocalScale); + else + { + MatrixF localMat, parentMat; + getLocalMatrix(localMat, includeLocalScale); + _parentTransform->getWorldMatrix(parentMat, true); + worldMat = parentMat * localMat; + } +} + +void Transform3DInPlace::getObjectMatrix(MatrixF & objectMat, bool includeLocalScale) const +{ + if (_parentTransform == NULL) + objectMat = MatrixF::smIdentity; + else if (_parentTransform->getParentTransform() == NULL) + getLocalMatrix(objectMat, includeLocalScale); + else + { + MatrixF localMat, parentMat; + getLocalMatrix(localMat, includeLocalScale); + _parentTransform->getObjectMatrix(parentMat, true); + objectMat = parentMat * localMat; + } +} + +void Transform3DInPlace::getLocalMatrix(MatrixF & localMat, bool includeLocalScale) const +{ + _rotation.setMatrix(&localMat); + localMat.setColumn(3,_position); + if (includeLocalScale) + localMat.scale(_scale); +} + +void Transform3DInPlace::setLocalMatrix(const MatrixF & localMat) +{ + _rotation.set(localMat); + _position = localMat.getPosition(); + _scale = localMat.getScale(); + _flags |= Transform3D::LocalScaleDirty | Transform3D::LocalRotationDirty | Transform3D::LocalPositionDirty; + if (_dirtyListener != NULL) + _dirtyListener->onTransformDirty(); +} + +//--------------------------------------------------------- +// TSTransform3D +//--------------------------------------------------------- + +TSTransform3D::TSTransform3D(TSShapeInstance * si, S32 nodeIndex) +{ + _shapeInstance = si; + _nodeIndex = nodeIndex; + AssertFatal(_nodeIndex >= 0 && _nodeIndex < _shapeInstance->mNodeTransforms.size(), "TSTransform3D nodeIndex out of range"); +} + +Point3F TSTransform3D::getPosition() const +{ + if (!doHandleLocal()) + { + MatrixF mat; + return getTSLocal(mat).getPosition(); + } + return _position; +} + +void TSTransform3D::setPosition(const Point3F & position) +{ + setHandleLocal(true); + _position = position; + _flags |= Transform3D::LocalPositionDirty; + if (_dirtyListener != NULL) + _dirtyListener->onTransformDirty(); +} + +QuatF TSTransform3D::getRotation() const +{ + if (!doHandleLocal()) + { + MatrixF mat; + return QuatF(getTSLocal(mat)); + } + return _rotation; +} +void TSTransform3D::setRotation(const QuatF & rotation) +{ + setHandleLocal(true); + _rotation = rotation; + _flags |= Transform3D::LocalRotationDirty; + if (_dirtyListener != NULL) + _dirtyListener->onTransformDirty(); +} + +Point3F TSTransform3D::getScale() const +{ + if (!doHandleLocal()) + { + MatrixF mat; + return getTSLocal(mat).getScale(); + } + return _scale; +} +void TSTransform3D::setScale(const Point3F & scale) +{ + setHandleLocal(true); + _scale = scale; + _flags |= Transform3D::LocalScaleDirty; + if (_dirtyListener != NULL) + _dirtyListener->onTransformDirty(); +} + +void TSTransform3D::getWorldMatrix(MatrixF & worldMat, bool includeLocalScale) const +{ + _shapeInstance->animate(); + if (_parentTransform == NULL) + { + worldMat = _shapeInstance->mNodeTransforms[_nodeIndex]; + } + else + { + MatrixF parentMat; + _parentTransform->getWorldMatrix(parentMat, true); + worldMat = parentMat * _shapeInstance->mNodeTransforms[_nodeIndex]; + } +} + +void TSTransform3D::getObjectMatrix(MatrixF & objectMat, bool includeLocalScale) const +{ + if (_parentTransform == NULL) + objectMat = MatrixF::smIdentity; + else if (_parentTransform->getParentTransform() == NULL) + { + _shapeInstance->animate(); + objectMat = _shapeInstance->mNodeTransforms[_nodeIndex]; + } + else + { + _shapeInstance->animate(); + + MatrixF parentMat; + _parentTransform->getObjectMatrix(parentMat, true); + objectMat = parentMat * _shapeInstance->mNodeTransforms[_nodeIndex]; + } +} + +void TSTransform3D::getLocalMatrix(MatrixF & localMat, bool includeLocalScale) const +{ + if (doHandleLocal()) + { + _rotation.setMatrix(&localMat); + localMat.setPosition(_position); + if (includeLocalScale) + localMat.scale(_scale); + } + else + { + _shapeInstance->animate(); + localMat = _shapeInstance->mNodeTransforms[_nodeIndex]; + if (!includeLocalScale && (_flags & Transform3D::LocalHasScale) != Transform3D::None) + { + // reverse any scale on matrix -- this is a inconvenient, but not a common case + Point3F scale = localMat.getScale(); + scale.x = 1.0f / scale.x; + scale.y = 1.0f / scale.y; + scale.z = 1.0f / scale.z; + localMat.scale(scale); + } + } +} + +void TSTransform3D::setLocalMatrix(const MatrixF & localMatrix) +{ + setHandleLocal(true); + _position = localMatrix.getPosition(); + _rotation.set(localMatrix); + _scale = localMatrix.getScale(); + _flags |= Transform3D::LocalScaleDirty | Transform3D::LocalRotationDirty | Transform3D::LocalPositionDirty; + if (_dirtyListener != NULL) + _dirtyListener->onTransformDirty(); +} + +MatrixF & TSTransform3D::getTSLocal(MatrixF & mat) const +{ + _shapeInstance->animate(); + + // if node has no parent, easy enough to just grab the matrix of the node + int parentIdx = _shapeInstance->getShape()->nodes[_nodeIndex].parentIndex; + if (parentIdx < 0) + { + return _shapeInstance->mNodeTransforms[_nodeIndex]; + } + + // has parent, local is transform from this node to parent so get local matrix the hard way + MatrixF parentMat = _shapeInstance->mNodeTransforms[parentIdx]; + parentMat.inverse(); + mat = parentMat * _shapeInstance->mNodeTransforms[_nodeIndex]; + return mat; +} + +void TSTransform3D::setHandleLocal(bool handleLocal) +{ + if (handleLocal == doHandleLocal()) + return; + + if (handleLocal) + { + _position = getPosition(); + _rotation = getRotation(); + _scale = getScale(); + _shapeInstance->setNodeAnimationState(_nodeIndex, 0, this); + } + else + _shapeInstance->setNodeAnimationState(_nodeIndex, 0); + _flags ^= TSTransform3D::HandleLocal; +} + +void TSTransform3D::setNodeTransform(TSShapeInstance * si, S32 nodeIndex, MatrixF & localTransform) +{ + AssertFatal(si == _shapeInstance,"TSTransform3D hooked up to wrong shape."); + getLocalMatrix(localTransform, true); +} diff --git a/Engine/source/T3D/sceneComponent/T3DTransform.h b/Engine/source/T3D/sceneComponent/T3DTransform.h new file mode 100644 index 000000000..2c3546202 --- /dev/null +++ b/Engine/source/T3D/sceneComponent/T3DTransform.h @@ -0,0 +1,218 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _T3DTRANSFORM_H_ +#define _T3DTRANSFORM_H_ + +#include "ts/tsShapeInstance.h" +#include "math/mMatrix.h" + +//--------------------------------------------------------- +// T3DTransform +//--------------------------------------------------------- + +class Transform3D +{ +public: + + class IDirtyListener + { + public: + virtual void onTransformDirty() = 0; + }; + + // local/object/world matrix access + + void setWorldMatrix(const MatrixF & world); + void setObjectMatrix(const MatrixF & objMatrix); + virtual void setLocalMatrix(const MatrixF & localMatrix) = 0; + + virtual void getWorldMatrix(MatrixF & worldMatrix, bool includeLocalScale) const = 0; + virtual void getObjectMatrix(MatrixF & objectMatrix, bool includeLocalScale) const = 0; + virtual void getLocalMatrix(MatrixF & localMatrix, bool includeLocalScale) const = 0; + + MatrixF getWorldMatrix() const + { + MatrixF world; + getWorldMatrix(world, true); + return world; + } + + MatrixF getObjectMatrix() const + { + MatrixF objMatrix; + getObjectMatrix(objMatrix, true); + return objMatrix; + } + + MatrixF getLocalMatrix() const + { + MatrixF loc; + getLocalMatrix(loc, true); + return loc; + } + + // local position/rotation/scale + + virtual Point3F getPosition() const = 0; + virtual void setPosition(const Point3F & position) = 0; + + virtual QuatF getRotation() const = 0; + virtual void setRotation(const QuatF & rotation) = 0; + + virtual Point3F getScale() const = 0; + virtual void setScale(const Point3F & scale) = 0; + + // scale tests + + bool hasLocalScale() const + { + return (_flags & Transform3D::LocalHasScale) != Transform3D::None; + } + + bool hasObjectScale() const; + bool hasWorldScale() const; + + // parent/child methods + + Transform3D * getParentTransform() const + { + return _parentTransform; + } + + void setParentTransform(Transform3D * parent); + + IDirtyListener * getDirtyListener() const + { + return _dirtyListener; + } + + void setDirtyListener(IDirtyListener * dirtyListener) + { + _dirtyListener = dirtyListener; + } + + bool isChildOf(Transform3D * parent, bool recursive) const; + +protected: + + enum TransformFlags + { + None = 0, + LocalHasScale = 1 << 0, + LocalPositionDirty = 1 << 1, + LocalRotationDirty = 1 << 2, + LocalScaleDirty = 1 << 3, + LocalDirty = LocalPositionDirty | LocalRotationDirty | LocalScaleDirty, + ParentDirty = 1 << 4, + LastFlag = 1 << 4 + }; + + Transform3D * _parentTransform; + IDirtyListener * _dirtyListener; + U32 _flags; +}; + +//--------------------------------------------------------- +// Transform3DInPlace +//--------------------------------------------------------- + +class Transform3DInPlace : public Transform3D +{ +public: + + Transform3DInPlace() : _position(0,0,0), _rotation(0,0,0,1), _scale(1,1,1) + { + } + + Point3F getPosition() const; + void setPosition(const Point3F & position); + + QuatF getRotation() const; + void setRotation(const QuatF & rotation); + + Point3F getScale() const; + void setScale(const Point3F & scale); + + void getWorldMatrix(MatrixF & worldMat, bool includeLocalScale) const; + void getObjectMatrix(MatrixF & objectMat, bool includeLocalScale) const; + void getLocalMatrix(MatrixF & localMat, bool includeLocalScale) const; + void setLocalMatrix(const MatrixF & localMat); + +protected: + + Point3F _position; + QuatF _rotation; + Point3F _scale; +}; + +//--------------------------------------------------------- +// TSTransform3D +//--------------------------------------------------------- + +class TSTransform3D : public Transform3D, public TSCallback +{ +public: + TSTransform3D(TSShapeInstance * si, S32 nodeIndex); + + Point3F getPosition() const; + void setPosition(const Point3F & position); + + QuatF getRotation() const; + void setRotation(const QuatF & rotation); + + Point3F getScale() const; + void setScale(const Point3F & scale); + + void getWorldMatrix(MatrixF & worldMat, bool includeLocalScale) const; + void getObjectMatrix(MatrixF & objectMat, bool includeLocalScale) const; + void getLocalMatrix(MatrixF & localMat, bool includeLocalScale) const; + void setLocalMatrix(const MatrixF & localMatrix); + + // Define TSCallback interface + void setNodeTransform(TSShapeInstance * si, S32 nodeIndex, MatrixF & localTransform); + +protected: + + enum TSTransformFlags + { + HandleLocal = Transform3D::LastFlag << 1, + LastFlag = Transform3D::LastFlag << 1 + }; + + bool doHandleLocal() const + { + return (_flags & (TransformFlags)TSTransform3D::HandleLocal) != Transform3D::None; + } + + void setHandleLocal(bool handleLocal); + MatrixF & getTSLocal(MatrixF & mat) const; + + TSShapeInstance * _shapeInstance; + int _nodeIndex; + + Point3F _position; + QuatF _rotation; + Point3F _scale; +}; + +#endif // _T3DTRANSFORM_H_ \ No newline at end of file diff --git a/Engine/source/T3D/scopeAlwaysShape.cpp b/Engine/source/T3D/scopeAlwaysShape.cpp new file mode 100644 index 000000000..b2c8551a0 --- /dev/null +++ b/Engine/source/T3D/scopeAlwaysShape.cpp @@ -0,0 +1,54 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "T3D/staticShape.h" + +class ScopeAlwaysShape : public StaticShape +{ + typedef StaticShape Parent; + + public: + ScopeAlwaysShape(); + static void initPersistFields(); + DECLARE_CONOBJECT(ScopeAlwaysShape); +}; + +ScopeAlwaysShape::ScopeAlwaysShape() +{ + mNetFlags.set(Ghostable|ScopeAlways); + mTypeMask |= StaticShapeObjectType; +} + +void ScopeAlwaysShape::initPersistFields() +{ + Parent::initPersistFields(); +} + +IMPLEMENT_CO_NETOBJECT_V1(ScopeAlwaysShape); + +ConsoleDocClass( ScopeAlwaysShape, + "@brief StaticShape object which is always scoped.\n\n" + + "@note Exists for backwards compatibility, no real use anymore. Go with TSStatic or a standard StaticShape instead.\n\n" + + "@internal\n" +); diff --git a/Engine/source/T3D/sfx/sfx3DWorld.cpp b/Engine/source/T3D/sfx/sfx3DWorld.cpp new file mode 100644 index 000000000..b04338460 --- /dev/null +++ b/Engine/source/T3D/sfx/sfx3DWorld.cpp @@ -0,0 +1,357 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "T3D/sfx/sfx3DWorld.h" +#include "T3D/portal.h" +#include "T3D/shapeBase.h" +#include "ts/tsShapeInstance.h" +#include "sfx/sfxSystem.h" +#include "math/mBox.h" +#include "core/module.h" +#include "T3D/gameBase/gameConnection.h" + + +//#define DEBUG_SPEW + + +MODULE_BEGIN( SFX3D ) + + MODULE_INIT_AFTER( Scene ) + MODULE_SHUTDOWN_BEFORE( Scene ) + + MODULE_INIT + { + if( !Con::getBoolVariable( "$SFX::noSFXWorld", false ) ) + gSFX3DWorld = new SFX3DWorld; + } + + MODULE_SHUTDOWN + { + if( gSFX3DWorld ) + SAFE_DELETE( gSFX3DWorld ); + } + +MODULE_END; + + +SFX3DWorld* gSFX3DWorld; + + +//============================================================================= +// SFX3DObject. +//============================================================================= + +//----------------------------------------------------------------------------- + +SFX3DObject::SFX3DObject( SFX3DWorld* world, SceneObject* object ) + : Parent( world, object ) +{ + if( mObject->isOccludingSound() ) + setFlags( SFXObjectOccluder ); + if( dynamic_cast< Portal* >( mObject ) ) + setFlags( SFXObjectPortal ); + if( object->getSoundAmbience() ) + setFlags( SFXObjectZone ); +} + +//----------------------------------------------------------------------------- + +void SFX3DObject::getEarTransform( MatrixF& transform ) const +{ + // If it's not a ShapeBase, just use the object transform. + ShapeBase* shape = dynamic_cast< ShapeBase* >( mObject ); + if ( !shape ) + { + transform = mObject->getTransform(); + return; + } + + // It it's ShapeBase, use the earNode transform if one was defined. + // Otherwise, use the camera transform. + TSShapeInstance* shapeInstance = shape->getShapeInstance(); + if ( !shapeInstance ) + { + // Just in case. + transform = mObject->getTransform(); + return; + } + + ShapeBaseData* datablock = dynamic_cast< ShapeBaseData* >( shape->getDataBlock() ); + AssertFatal( datablock, "SFX3DObject::getEarTransform() - shape without ShapeBaseData datablock!" ); + + // Get the transform for the ear node. + + const S32 earNode = datablock->earNode; + + if ( earNode != -1 && earNode != datablock->eyeNode ) + { + transform = shape->getTransform(); + transform *= shapeInstance->mNodeTransforms[ earNode ]; + } + else + { + GameConnection* connection = dynamic_cast(NetConnection::getConnectionToServer()); + if ( !connection || !connection->getControlCameraTransform( 0.0f, &transform ) ) + transform = mObject->getTransform(); + } +} + +//----------------------------------------------------------------------------- + +void SFX3DObject::getReferenceCenter( F32 position[ 3 ] ) const +{ + MatrixF transform; + getEarTransform( transform ); + Point3F pos = transform.getPosition(); + + AssertFatal( TypeTraits< F32 >::MIN <= pos.x && pos.x <= TypeTraits< F32 >::MAX, + "SFX3DObject::getReferenceCenter - invalid float in reference center X position" ); + AssertFatal( TypeTraits< F32 >::MIN <= pos.y && pos.y <= TypeTraits< F32 >::MAX, + "SFX3DObject::getReferenceCenter - invalid float in reference center Y position" ); + AssertFatal( TypeTraits< F32 >::MIN <= pos.z && pos.z <= TypeTraits< F32 >::MAX, + "SFX3DObject::getReferenceCenter - invalid float in reference center Z position" ); + + dMemcpy( position, &pos.x, sizeof( F32 ) * 3 ); +} + +//----------------------------------------------------------------------------- + +void SFX3DObject::getBounds( F32 minBounds[ 3 ], F32 maxBounds[ 3 ] ) const +{ + getRealBounds( minBounds, maxBounds ); +} + +//----------------------------------------------------------------------------- + +void SFX3DObject::getRealBounds( F32 minBounds[ 3 ], F32 maxBounds[ 3 ] ) const +{ + const Box3F& worldBox = mObject->getWorldBox(); + dMemcpy( minBounds, &worldBox.minExtents.x, sizeof( F32 ) * 3 ); + dMemcpy( maxBounds, &worldBox.maxExtents.x, sizeof( F32 ) * 3 ); +} + +//----------------------------------------------------------------------------- + +SFXAmbience* SFX3DObject::getAmbience() const +{ + return mObject->getSoundAmbience(); +} + +//----------------------------------------------------------------------------- + +bool SFX3DObject::containsPoint( const F32 point[ 3 ] ) const +{ + return mObject->containsPoint( *( reinterpret_cast< const Point3F* >( &point[ 0 ] ) ) ); +} + +//----------------------------------------------------------------------------- + +String SFX3DObject::describeSelf() const +{ + return mObject->describeSelf(); +} + +//============================================================================= +// SFX3DWorld. +//============================================================================= + +//----------------------------------------------------------------------------- + +SFX3DWorld::SFX3DWorld() + : Parent( true, TYPEMASK ) +{ +} + +//----------------------------------------------------------------------------- + +bool SFX3DWorld::_isTrackableObject( SceneObject* object ) const +{ + if( !Parent::_isTrackableObject( object ) ) + return false; + + // We are only interested in occluders, zones, and portals. + + if( object->isOccludingSound() ) + return true; + else if( object->getSoundAmbience() ) + return true; + else if( dynamic_cast< Portal* >( object ) ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- + +void SFX3DWorld::update() +{ + mSFXWorld.update(); +} + +//----------------------------------------------------------------------------- + +void SFX3DWorld::registerObject( SceneObject* object ) +{ + if( !_isTrackableObject( object ) ) + return; + + // Construct a new scene object link. + + SFX3DObject* sfxObject = mChunker.alloc(); + constructInPlace( sfxObject, this, object ); + + // Register it with the SFX world. + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFX3DWorld] Registering %i:%s as 0x%x", + object->getId(), object->getClassName(), sfxObject ); + #endif + + mSFXWorld.registerObject( sfxObject ); +} + +//----------------------------------------------------------------------------- + +void SFX3DWorld::unregisterObject( SceneObject* object ) +{ + SFX3DObject* sfxObject = dynamic_cast< SFX3DObject* >( SFX3DObject::getLinkForTracker( this, object ) ); + if( !sfxObject ) + return; + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFX3DWorld] Unregistering %i:%s (was 0x%x)", + object->getId(), object->getClassName(), sfxObject ); + #endif + + // Remove the object from the SFX world. + + if( sfxObject->isListener() ) + mSFXWorld.setReferenceObject( NULL ); + else + mSFXWorld.unregisterObject( sfxObject ); + + // Destroy the scene object link. + + destructInPlace( sfxObject ); + mChunker.free( sfxObject ); +} + +//----------------------------------------------------------------------------- + +void SFX3DWorld::updateObject( SceneObjectLink* object ) +{ + SFX3DObject* sfxObject = dynamic_cast< SFX3DObject* >( object ); + AssertFatal( sfxObject, "SFX3DWorld::updateObject - invalid object type" ); + + mSFXWorld.updateObject( sfxObject ); + + // If this is the listener object, update its + // properties on the SFX system. + + if( sfxObject->isListener() ) + { + SFXListenerProperties listener; + sfxObject->getEarTransform( listener.getTransform() ); + listener.getVelocity() = sfxObject->getObject()->getVelocity(); + + SFX->setListener( 0, listener ); + } +} + +//----------------------------------------------------------------------------- + +SceneObject* SFX3DWorld::getListener() const +{ + SFX3DObject* object = mSFXWorld.getReferenceObject(); + if( !object ) + return NULL; + + return object->getObject(); +} + +//----------------------------------------------------------------------------- + +void SFX3DWorld::setListener( SceneObject* object ) +{ + SFX3DObject* oldListener = mSFXWorld.getReferenceObject(); + + // If it's the same object as our current listener, + // return. + + if( oldListener && oldListener->getObject() == object ) + return; + + // Create a SFX3DObject for the given SceneObject. + + SFX3DObject* sfxObject = NULL; + if( object ) + { + AssertFatal( !dynamic_cast< SFX3DObject* >( SFX3DObject::getLinkForTracker( this, object ) ), + "SFX3DWorld::setListener - listener objects must not be registered for tracking" ); + + sfxObject = mChunker.alloc(); + constructInPlace( sfxObject, this, object ); + sfxObject->setFlags( SFXObjectListener ); + } + +#ifdef DEBUG_SPEW + if( object ) + Platform::outputDebugString( "[SFX3DWorld] Listener is now %i:%s (%s)", + object->getId(), object->getClassName(), object->getName() ); + else + Platform::outputDebugString( "[SFX3DWorld] Unsetting listener" ); +#endif + + // Make this object the center of our SFX world. + + mSFXWorld.setReferenceObject( sfxObject ); + + // Remove the tracking links from the old listener so we + // don't see further updates on it. + + if( oldListener ) + { + destructInPlace( oldListener ); + mChunker.free( oldListener ); + } +} + +//----------------------------------------------------------------------------- + +void SFX3DWorld::notifyChanged( SceneObject* object ) +{ + SFX3DObject* sfxObject = dynamic_cast< SFX3DObject* >( SFX3DObject::getLinkForTracker( this, object ) ); + + if( !sfxObject && _isTrackableObject( object ) ) + registerObject( object ); + else if( sfxObject && !_isTrackableObject( object ) ) + unregisterObject( object ); + else if( sfxObject ) + mSFXWorld.notifyChanged( sfxObject ); +} + +//----------------------------------------------------------------------------- + +void SFX3DWorld::debugDump() +{ + mSFXWorld.debugDump(); +} diff --git a/Engine/source/T3D/sfx/sfx3DWorld.h b/Engine/source/T3D/sfx/sfx3DWorld.h new file mode 100644 index 000000000..86f233b33 --- /dev/null +++ b/Engine/source/T3D/sfx/sfx3DWorld.h @@ -0,0 +1,130 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFX3DWORLD_H_ +#define _SFX3DWORLD_H_ + +#ifndef _SCENETRACKER_H_ + #include "scene/sceneTracker.h" +#endif +#ifndef _SFXWORLD_H_ + #include "sfx/sfxWorld.h" +#endif +#ifndef _DATACHUNKER_H_ + #include "core/dataChunker.h" +#endif +#ifndef _OBJECTTYPES_H_ + #include "T3D/objectTypes.h" +#endif + + +class SFX3DWorld; + + +/// SFXObject implementation for the 3D system. +class SFX3DObject : public SceneObjectLink, public SFXObject< 3 > +{ + public: + + typedef SceneObjectLink Parent; + + /// + SFX3DObject( SFX3DWorld* world, SceneObject* object ); + + /// Return the transform for the ears on this object. + void getEarTransform( MatrixF& transform ) const; + + // SFXObject. + void getReferenceCenter( F32 position[ 3 ] ) const; + void getBounds( F32 minBounds[ 3 ], F32 maxBounds[ 3 ] ) const; + void getRealBounds( F32 minBounds[ 3 ], F32 maxBounds[ 3 ] ) const; + SFXAmbience* getAmbience() const; + bool containsPoint( const F32 point[ 3 ] ) const; + String describeSelf() const; +}; + + +/// Manager for the 3D sound world. +/// +/// Any SceneObject in the world can be made the current listener. It's ear position (if it's a ShapeBase) +/// will then be used as the reference center for attenuating 3D sounds, traversing ambient spaces, etc. +/// +class SFX3DWorld : public SceneTracker +{ + public: + + typedef SceneTracker Parent; + typedef SFXWorld< 3, SFX3DObject* > SFXWorldType; + + enum + { + /// The scene object type mask used to filter out the + /// type of objects we are interested in. + TYPEMASK = WaterObjectType // Sound ambience. + | StaticShapeObjectType // Occlusion and sound ambience. + | StaticObjectType // Portals and zones. + }; + + protected: + + /// The SFX world tracking system. + SFXWorldType mSFXWorld; + + /// Allocator for the SFX3DObject SceneObjectLinks we attach to SceneObjects. + FreeListChunker< SFX3DObject > mChunker; + + // SceneTracker. + virtual bool _isTrackableObject( SceneObject* object ) const; + + public: + + /// + SFX3DWorld(); + + /// + void update(); + + /// Return the current listener object. + SceneObject* getListener() const; + + /// Make the given object the current listener object. + void setListener( SceneObject* object ); + + /// Notify the SFX world that the given object had a (potential) change in its + /// sound-related properties. + void notifyChanged( SceneObject* object ); + + /// + void debugDump(); + + // SceneTracker. + virtual void registerObject( SceneObject* object ); + virtual void unregisterObject( SceneObject* object ); + virtual void updateObject( SceneObjectLink* object ); +}; + + +/// The singleton instance of SFX3DWorld, if there is one. +extern SFX3DWorld* gSFX3DWorld; + +#endif // !_SFX3DWORLD_H_ + diff --git a/Engine/source/T3D/sfx/sfxEmitter.cpp b/Engine/source/T3D/sfx/sfxEmitter.cpp new file mode 100644 index 000000000..064922a14 --- /dev/null +++ b/Engine/source/T3D/sfx/sfxEmitter.cpp @@ -0,0 +1,1177 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/sfx/sfxEmitter.h" +#include "sfx/sfxSystem.h" +#include "sfx/sfxTrack.h" +#include "sfx/sfxSource.h" +#include "sfx/sfxTypes.h" +#include "scene/sceneRenderState.h" +#include "core/stream/bitStream.h" +#include "sim/netConnection.h" +#include "math/mathIO.h" +#include "math/mQuat.h" +#include "renderInstance/renderPassManager.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/gfxDrawUtil.h" +#include "gfx/primBuilder.h" +#include "console/engineAPI.h" + + +IMPLEMENT_CO_NETOBJECT_V1( SFXEmitter ); + +ConsoleDocClass( SFXEmitter, + "@brief An invisible 3D object that emits sound.\n\n" + + "Sound emitters are used to place sounds in the level. They are full 3D objects with their own position and orientation and " + "when assigned 3D sounds, the transform and velocity of the sound emitter object will be applied to the 3D sound.\n\n" + + "Sound emitters can be set up of in either of two ways:\n" + + "
        \n" + "
      • By assigning an existing SFXTrack to the emitter's #track property.

        \n" + "

        In this case the general sound setup (3D, streaming, looping, etc.) will be taken from #track. However, the emitter's " + "own properties will still override their corresponding properties in the #track's SFXDescription.

      • \n" + "
      • By directly assigning a sound file to the emitter's #fileName property.

        \n" + "

        In this case, the sound file will be set up for playback according to the properties defined on the emitter.

        \n" + "
      \n\n" + + "Using #playOnAdd emitters can be configured to start playing immediately when they are added to the system (e.g. when the level " + "objects are loaded from the mission file).\n\n" + + "@note A sound emitter need not necessarily emit a 3D sound. Instead, sound emitters may also be used to play " + "non-positional sounds. For placing background audio to a level, however, it is usually easier to use LevelInfo::soundAmbience.\n\n" + + "@section SFXEmitter_networking Sound Emitters and Networking\n\n" + + "It is important to be aware of the fact that sounds will only play client-side whereas SFXEmitter objects are server-side " + "entities. This means that a server-side object has no connection to the actual sound playing on the client. It is thus " + "not possible for the server-object to perform queries about playback status and other source-related properties as these " + "may in fact differ from client to client.\n\n" + + "@ingroup SFX\n" +); + + +extern bool gEditingMission; +bool SFXEmitter::smRenderEmitters; +F32 SFXEmitter::smRenderPointSize = 0.8f; +F32 SFXEmitter::smRenderRadialIncrements = 5.f; +F32 SFXEmitter::smRenderSweepIncrements = 5.f; +F32 SFXEmitter::smRenderPointDistance = 5.f; +ColorI SFXEmitter::smRenderColorPlayingInRange( 50, 255, 50, 255 ); +ColorI SFXEmitter::smRenderColorPlayingOutOfRange( 50, 128, 50, 255 ); +ColorI SFXEmitter::smRenderColorStoppedInRange( 0, 0, 0, 255 ); +ColorI SFXEmitter::smRenderColorStoppedOutOfRange( 128, 128, 128, 255 ); +ColorI SFXEmitter::smRenderColorInnerCone( 0, 0, 255, 255 ); +ColorI SFXEmitter::smRenderColorOuterCone( 255, 0, 255, 255 ); +ColorI SFXEmitter::smRenderColorOutsideVolume( 255, 0, 0, 255 ); +ColorI SFXEmitter::smRenderColorRangeSphere( 200, 0, 0, 90 ); + + +//----------------------------------------------------------------------------- + +SFXEmitter::SFXEmitter() + : SceneObject(), + mSource( NULL ), + mTrack( NULL ), + mLocalProfile( &mDescription ), + mPlayOnAdd( true ), + mUseTrackDescriptionOnly( false ) +{ + mTypeMask |= MarkerObjectType; + mNetFlags.set( Ghostable | ScopeAlways ); + + mDescription.mIs3D = true; + mDescription.mIsLooping = true; + mDescription.mIsStreaming = false; + mDescription.mFadeInTime = -1.f; + mDescription.mFadeOutTime = -1.f; + + mLocalProfile._registerSignals(); + + mObjBox.minExtents.set( -1.f, -1.f, -1.f ); + mObjBox.maxExtents.set( 1.f, 1.f, 1.f ); +} + +//----------------------------------------------------------------------------- + +SFXEmitter::~SFXEmitter() +{ + mLocalProfile._unregisterSignals(); + + if( mSource ) + SFX_DELETE( mSource ); +} + +//----------------------------------------------------------------------------- + +void SFXEmitter::consoleInit() +{ + Con::addVariable( "$SFXEmitter::renderEmitters", TypeBool, &smRenderEmitters, + "Whether to render enhanced range feedback in the editor on all emitters regardless of selection state.\n" + "@ingroup SFX\n"); + + //TODO: not implemented ATM + //Con::addVariable( "$SFXEmitter::renderPointSize", TypeF32, &smRenderPointSize ); + + Con::addVariable( "$SFXEmitter::renderPointDistance", TypeF32, &smRenderPointDistance, + "The distance between individual points in the sound emitter rendering in the editor as the points move from the emitter's center away to maxDistance.\n" + "@ingroup SFX\n"); + Con::addVariable( "$SFXEmitter::renderRadialIncrements", TypeF32, &smRenderRadialIncrements, + "The stepping (in degrees) for the radial sweep along the axis of the XY plane sweep for sound emitter rendering in the editor.\n" + "@ingroup SFX\n"); + Con::addVariable( "$SFXEmitter::renderSweepIncrements", TypeF32, &smRenderSweepIncrements, + "The stepping (in degrees) for the radial sweep on the XY plane for sound emitter rendering in the editor.\n" + "@ingroup SFX\n"); + Con::addVariable( "$SFXEmitter::renderColorPlayingInRange", TypeColorI, &smRenderColorPlayingInRange, + "The color with which to render a sound emitter's marker cube in the editor when the emitter's sound is playing and in range of the listener.\n" + "@ingroup SFX\n" ); + Con::addVariable( "$SFXEmitter::renderColorPlayingOutOfRange", TypeColorI, &smRenderColorPlayingOutOfRange, + "The color with which to render a sound emitter's marker cube in the editor when the emitter's sound is playing but out of the range of the listener.\n" + "@ingroup SFX\n" ); + Con::addVariable( "$SFXEmitter::renderColorStoppedInRange", TypeColorI, &smRenderColorStoppedInRange, + "The color with which to render a sound emitter's marker cube in the editor when the emitter's sound is not playing but the emitter is in range of the listener.\n" + "@ingroup SFX\n" ); + Con::addVariable( "$SFXEmitter::renderColorStoppedOutOfRange", TypeColorI, &smRenderColorStoppedOutOfRange, + "The color with which to render a sound emitter's marker cube in the editor when the emitter's sound is not playing and the emitter is out of range of the listener.\n" + "@ingroup SFX\n" ); + Con::addVariable( "$SFXEmitter::renderColorInnerCone", TypeColorI, &smRenderColorInnerCone, + "The color with which to render dots in the inner sound cone (Editor only).\n" + "@ingroup SFX\n"); + Con::addVariable( "$SFXEmitter::renderColorOuterCone", TypeColorI, &smRenderColorOuterCone, + "The color with which to render dots in the outer sound cone (Editor only).\n" + "@ingroup SFX\n" ); + Con::addVariable( "$SFXEmitter::renderColorOutsideVolume", TypeColorI, &smRenderColorOutsideVolume, + "The color with which to render dots outside of the outer sound cone (Editor only).\n" + "@ingroup SFX\n" ); + Con::addVariable( "$SFXEmitter::renderColorRangeSphere", TypeColorI, &smRenderColorRangeSphere, + "The color of the range sphere with which to render sound emitters in the editor.\n" + "@ingroup SFX\n" ); +} + +//----------------------------------------------------------------------------- + +void SFXEmitter::initPersistFields() +{ + addGroup( "Media" ); + + addField( "track", TypeSFXTrackName, Offset( mTrack, SFXEmitter), + "The track which the emitter should play.\n" + "@note If assigned, this field will take precedence over a #fileName that may also be assigned to the " + "emitter." ); + addField( "fileName", TypeStringFilename, Offset( mLocalProfile.mFilename, SFXEmitter), + "The sound file to play.\n" + "Use @b either this property @b or #track. If both are assigned, #track takes precendence. The primary purpose of this " + "field is to avoid the need for the user to define SFXTrack datablocks for all sounds used in a level." ); + + endGroup( "Media"); + + addGroup( "Sound" ); + + addField( "playOnAdd", TypeBool, Offset( mPlayOnAdd, SFXEmitter ), + "Whether playback of the emitter's sound should start as soon as the emitter object is added to the level.\n" + "If this is true, the emitter will immediately start to play when the level is loaded." ); + addField( "useTrackDescriptionOnly", TypeBool, Offset( mUseTrackDescriptionOnly, SFXEmitter ), + "If this is true, all fields except for #playOnAdd and #track are ignored on the emitter object.\n" + "This is useful to prevent fields in the #track's description from being overridden by emitter fields." ); + addField( "isLooping", TypeBool, Offset( mDescription.mIsLooping, SFXEmitter ), + "Whether to play #fileName in an infinite loop.\n" + "If a #track is assigned, the value of this field is ignored.\n" + "@see SFXDescription::isLooping" ); + addField( "isStreaming", TypeBool, Offset( mDescription.mIsStreaming, SFXEmitter ), + "Whether to use streamed playback for #fileName.\n" + "If a #track is assigned, the value of this field is ignored.\n" + "@see SFXDescription::isStreaming\n\n" + "@ref SFX_streaming" ); + addField( "sourceGroup", TypeSFXSourceName, Offset( mDescription.mSourceGroup, SFXEmitter ), + "The SFXSource to which to assign the sound of this emitter as a child.\n" + "@note This field is ignored if #useTrackDescriptionOnly is true.\n\n" + "@see SFXDescription::sourceGroup" ); + addField( "volume", TypeF32, Offset( mDescription.mVolume, SFXEmitter ), + "Volume level to apply to the sound.\n" + "@note This field is ignored if #useTrackDescriptionOnly is true.\n\n" + "@see SFXDescription::volume" ); + addField( "pitch", TypeF32, Offset( mDescription.mPitch, SFXEmitter ), + "Pitch shift to apply to the sound. Default is 1 = play at normal speed.\n" + "@note This field is ignored if #useTrackDescriptionOnly is true.\n\n" + "@see SFXDescription::pitch" ); + addField( "fadeInTime", TypeF32, Offset( mDescription.mFadeInTime, SFXEmitter ), + "Number of seconds to gradually fade in volume from zero when playback starts.\n" + "@note This field is ignored if #useTrackDescriptionOnly is true.\n\n" + "@see SFXDescription::fadeInTime" ); + addField( "fadeOutTime", TypeF32, Offset( mDescription.mFadeOutTime, SFXEmitter ), + "Number of seconds to gradually fade out volume down to zero when playback is stopped or paused.\n" + "@note This field is ignored if #useTrackDescriptionOnly is true.\n\n" + "@see SFXDescription::fadeOutTime" ); + + endGroup( "Sound"); + + addGroup( "3D Sound" ); + + addField( "is3D", TypeBool, Offset( mDescription.mIs3D, SFXEmitter ), + "Whether to play #fileName as a positional (3D) sound or not.\n" + "If a #track is assigned, the value of this field is ignored.\n\n" + "@see SFXDescription::is3D" ); + addField( "referenceDistance", TypeF32, Offset( mDescription.mMinDistance, SFXEmitter ), + "Distance at which to start volume attenuation of the 3D sound.\n" + "@note This field is ignored if #useTrackDescriptionOnly is true.\n\n" + "@see SFXDescription::referenceDistance" ); + addField( "maxDistance", TypeF32, Offset( mDescription.mMaxDistance, SFXEmitter ), + "Distance at which to stop volume attenuation of the 3D sound.\n" + "@note This field is ignored if #useTrackDescriptionOnly is true.\n\n" + "@see SFXDescription::maxDistance" ); + addField( "scatterDistance", TypePoint3F, Offset( mDescription.mScatterDistance, SFXEmitter ), + "Bounds on random offset to apply to initial 3D sound position.\n" + "@note This field is ignored if #useTrackDescriptionOnly is true.\n\n" + "@see SFXDescription::scatterDistance" ); + addField( "coneInsideAngle", TypeS32, Offset( mDescription.mConeInsideAngle, SFXEmitter ), + "Angle of inner volume cone of 3D sound in degrees.\n" + "@note This field is ignored if #useTrackDescriptionOnly is true.\n\n" + "@see SFXDescription::coneInsideAngle" ); + addField( "coneOutsideAngle", TypeS32, Offset( mDescription.mConeOutsideAngle, SFXEmitter ), + "Angle of outer volume cone of 3D sound in degrees\n" + "@note This field is ignored if #useTrackDescriptionOnly is true.\n\n" + "@see SFXDescription::coneOutsideAngle" ); + addField( "coneOutsideVolume", TypeF32, Offset( mDescription.mConeOutsideVolume, SFXEmitter ), + "Volume scale factor of outside of outer volume 3D sound cone.\n" + "@note This field is ignored if #useTrackDescriptionOnly is true.\n\n" + "@see SFXDescription::coneOutsideVolume" ); + + endGroup( "3D Sound" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +U32 SFXEmitter::packUpdate( NetConnection *con, U32 mask, BitStream *stream ) +{ + U32 retMask = Parent::packUpdate( con, mask, stream ); + + if( stream->writeFlag( mask & InitialUpdateMask ) ) + { + // If this is the initial update then all the source + // values are dirty and must be transmitted. + mask |= TransformUpdateMask; + mDirty = AllDirtyMask; + + // Clear the source masks... they are not + // used during an initial update! + mask &= ~AllSourceMasks; + } + + stream->writeFlag( mPlayOnAdd ); + + // transform + if( stream->writeFlag( mask & TransformUpdateMask ) ) + stream->writeAffineTransform( mObjToWorld ); + + // track + if( stream->writeFlag( mDirty.test( Track ) ) ) + sfxWrite( stream, mTrack ); + + // filename + if( stream->writeFlag( mDirty.test( Filename ) ) ) + stream->writeString( mLocalProfile.mFilename ); + + // volume + if( stream->writeFlag( mDirty.test( Volume ) ) ) + stream->write( mDescription.mVolume ); + + // pitch + if( stream->writeFlag( mDirty.test( Pitch ) ) ) + stream->write( mDescription.mPitch ); + + // islooping + if( stream->writeFlag( mDirty.test( IsLooping ) ) ) + stream->writeFlag( mDescription.mIsLooping ); + + // isStreaming + if( stream->writeFlag( mDirty.test( IsStreaming ) ) ) + stream->writeFlag( mDescription.mIsStreaming ); + + // is3d + if( stream->writeFlag( mDirty.test( Is3D ) ) ) + stream->writeFlag( mDescription.mIs3D ); + + // minDistance + if( stream->writeFlag( mDirty.test( MinDistance ) ) ) + stream->write( mDescription.mMinDistance ); + + // maxdistance + if( stream->writeFlag( mDirty.test( MaxDistance) ) ) + stream->write( mDescription.mMaxDistance ); + + // coneinsideangle + if( stream->writeFlag( mDirty.test( ConeInsideAngle ) ) ) + stream->write( mDescription.mConeInsideAngle ); + + // coneoutsideangle + if( stream->writeFlag( mDirty.test( ConeOutsideAngle ) ) ) + stream->write( mDescription.mConeOutsideAngle ); + + // coneoutsidevolume + if( stream->writeFlag( mDirty.test( ConeOutsideVolume ) ) ) + stream->write( mDescription.mConeOutsideVolume ); + + // sourcegroup + if( stream->writeFlag( mDirty.test( SourceGroup ) ) ) + sfxWrite( stream, mDescription.mSourceGroup ); + + // fadein + if( stream->writeFlag( mDirty.test( FadeInTime ) ) ) + stream->write( mDescription.mFadeInTime ); + + // fadeout + if( stream->writeFlag( mDirty.test( FadeOutTime ) ) ) + stream->write( mDescription.mFadeOutTime ); + + // scatterdistance + if( stream->writeFlag( mDirty.test( ScatterDistance ) ) ) + mathWrite( *stream, mDescription.mScatterDistance ); + + mDirty.clear(); + + stream->writeFlag( mUseTrackDescriptionOnly ); + + // We should never have both source masks + // enabled at the same time! + AssertFatal( ( mask & AllSourceMasks ) != AllSourceMasks, + "SFXEmitter::packUpdate() - Bad source mask!" ); + + // Write the source playback state. + stream->writeFlag( mask & SourcePlayMask ); + stream->writeFlag( mask & SourceStopMask ); + + return retMask; +} + +//----------------------------------------------------------------------------- + +bool SFXEmitter::_readDirtyFlag( BitStream* stream, U32 mask ) +{ + bool flag = stream->readFlag(); + if ( flag ) + mDirty.set( mask ); + + return flag; +} + +//----------------------------------------------------------------------------- + +void SFXEmitter::unpackUpdate( NetConnection *conn, BitStream *stream ) +{ + Parent::unpackUpdate( conn, stream ); + + // initial update? + bool initialUpdate = stream->readFlag(); + + mPlayOnAdd = stream->readFlag(); + + // transform + if ( _readDirtyFlag( stream, Transform ) ) + { + MatrixF mat; + stream->readAffineTransform(&mat); + Parent::setTransform(mat); + } + + // track + if ( _readDirtyFlag( stream, Track ) ) + { + String errorStr; + if( !sfxReadAndResolve( stream, &mTrack, errorStr ) ) + Con::errorf( "%s", errorStr.c_str() ); + } + + // filename + if ( _readDirtyFlag( stream, Filename ) ) + mLocalProfile.mFilename = stream->readSTString(); + + // volume + if ( _readDirtyFlag( stream, Volume ) ) + stream->read( &mDescription.mVolume ); + + // pitch + if( _readDirtyFlag( stream, Pitch ) ) + stream->read( &mDescription.mPitch ); + + // islooping + if ( _readDirtyFlag( stream, IsLooping ) ) + mDescription.mIsLooping = stream->readFlag(); + + if( _readDirtyFlag( stream, IsStreaming ) ) + mDescription.mIsStreaming = stream->readFlag(); + + // is3d + if ( _readDirtyFlag( stream, Is3D ) ) + mDescription.mIs3D = stream->readFlag(); + + // mindistance + if ( _readDirtyFlag( stream, MinDistance ) ) + stream->read( &mDescription.mMinDistance ); + + // maxdistance + if ( _readDirtyFlag( stream, MaxDistance ) ) + { + stream->read( &mDescription.mMaxDistance ); + mObjScale.set( mDescription.mMaxDistance, mDescription.mMaxDistance, mDescription.mMaxDistance ); + } + + // coneinsideangle + if ( _readDirtyFlag( stream, ConeInsideAngle ) ) + stream->read( &mDescription.mConeInsideAngle ); + + // coneoutsideangle + if ( _readDirtyFlag( stream, ConeOutsideAngle ) ) + stream->read( &mDescription.mConeOutsideAngle ); + + // coneoutsidevolume + if ( _readDirtyFlag( stream, ConeOutsideVolume ) ) + stream->read( &mDescription.mConeOutsideVolume ); + + // sourcegroup + if ( _readDirtyFlag( stream, SourceGroup ) ) + { + String errorStr; + if( !sfxReadAndResolve( stream, &mDescription.mSourceGroup, errorStr ) ) + Con::errorf( "%s", errorStr.c_str() ); + } + + // fadein + if ( _readDirtyFlag( stream, FadeInTime ) ) + stream->read( &mDescription.mFadeInTime ); + + // fadeout + if( _readDirtyFlag( stream, FadeOutTime ) ) + stream->read( &mDescription.mFadeOutTime ); + + // scatterdistance + if( _readDirtyFlag( stream, ScatterDistance ) ) + mathRead( *stream, &mDescription.mScatterDistance ); + + mUseTrackDescriptionOnly = stream->readFlag(); + + // update the emitter now? + if ( !initialUpdate ) + _update(); + + // Check the source playback masks. + if ( stream->readFlag() ) // SourcePlayMask + play(); + if ( stream->readFlag() ) // SourceStopMask + stop(); +} + +//----------------------------------------------------------------------------- + +void SFXEmitter::onStaticModified( const char* slotName, const char* newValue ) +{ + // NOTE: The signature for this function is very + // misleading... slotName is a StringTableEntry. + + // We don't check for changes on the client side. + if ( isClientObject() ) + return; + + // Lookup and store the property names once here + // and we can then just do pointer compares. + static StringTableEntry slotPosition = StringTable->lookup( "position" ); + static StringTableEntry slotRotation = StringTable->lookup( "rotation" ); + static StringTableEntry slotScale = StringTable->lookup( "scale" ); + static StringTableEntry slotTrack = StringTable->lookup( "track" ); + static StringTableEntry slotFilename = StringTable->lookup( "fileName" ); + static StringTableEntry slotVolume = StringTable->lookup( "volume" ); + static StringTableEntry slotPitch = StringTable->lookup( "pitch" ); + static StringTableEntry slotIsLooping = StringTable->lookup( "isLooping" ); + static StringTableEntry slotIsStreaming= StringTable->lookup( "isStreaming" ); + static StringTableEntry slotIs3D = StringTable->lookup( "is3D" ); + static StringTableEntry slotRefDist = StringTable->lookup( "referenceDistance" ); + static StringTableEntry slotMaxDist = StringTable->lookup( "maxDistance" ); + static StringTableEntry slotConeInAng = StringTable->lookup( "coneInsideAngle" ); + static StringTableEntry slotConeOutAng = StringTable->lookup( "coneOutsideAngle" ); + static StringTableEntry slotConeOutVol = StringTable->lookup( "coneOutsideVolume" ); + static StringTableEntry slotFadeInTime = StringTable->lookup( "fadeInTime" ); + static StringTableEntry slotFadeOutTime= StringTable->lookup( "fadeOutTime" ); + static StringTableEntry slotScatterDistance = StringTable->lookup( "scatterDistance" ); + static StringTableEntry slotSourceGroup= StringTable->lookup( "sourceGroup" ); + static StringTableEntry slotUseTrackDescriptionOnly = StringTable->lookup( "useTrackDescriptionOnly" ); + + // Set the dirty flags. + mDirty.clear(); + if( slotName == slotPosition || + slotName == slotRotation || + slotName == slotScale ) + mDirty.set( Transform ); + + else if( slotName == slotTrack ) + mDirty.set( Track ); + + else if( slotName == slotFilename ) + mDirty.set( Filename ); + + else if( slotName == slotVolume ) + mDirty.set( Volume ); + + else if( slotName == slotPitch ) + mDirty.set( Pitch ); + + else if( slotName == slotIsLooping ) + mDirty.set( IsLooping ); + + else if( slotName == slotIsStreaming ) + mDirty.set( IsStreaming ); + + else if( slotName == slotIs3D ) + mDirty.set( Is3D ); + + else if( slotName == slotRefDist ) + mDirty.set( MinDistance ); + + else if( slotName == slotMaxDist ) + mDirty.set( MaxDistance ); + + else if( slotName == slotConeInAng ) + mDirty.set( ConeInsideAngle ); + + else if( slotName == slotConeOutAng ) + mDirty.set( ConeOutsideAngle ); + + else if( slotName == slotConeOutVol ) + mDirty.set( ConeOutsideVolume ); + + else if( slotName == slotFadeInTime ) + mDirty.set( FadeInTime ); + + else if( slotName == slotFadeOutTime ) + mDirty.set( FadeOutTime ); + + else if( slotName == slotScatterDistance ) + mDirty.set( ScatterDistance ); + + else if( slotName == slotSourceGroup ) + mDirty.set( SourceGroup ); + + else if( slotName == slotUseTrackDescriptionOnly ) + mDirty.set( TrackOnly ); + + if( mDirty ) + setMaskBits( DirtyUpdateMask ); +} + +//----------------------------------------------------------------------------- + +void SFXEmitter::inspectPostApply() +{ + // Parent will call setScale so sync up scale with distance. + + F32 maxDistance = mDescription.mMaxDistance; + if( mUseTrackDescriptionOnly && mTrack ) + maxDistance = mTrack->getDescription()->mMaxDistance; + + mObjScale.set( maxDistance, maxDistance, maxDistance ); + + Parent::inspectPostApply(); +} + +//----------------------------------------------------------------------------- + +bool SFXEmitter::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + if( isServerObject() ) + { + // Validate the data we'll be passing across + // the network to the client. + mDescription.validate(); + + // Read an old 'profile' field for backwards-compatibility. + + if( !mTrack ) + { + static const char* sProfile = StringTable->insert( "profile" ); + const char* profileName = getDataField( sProfile, NULL ); + if( profileName && profileName[ 0 ] ) + { + if( !Sim::findObject( profileName, mTrack ) ) + Con::errorf( "SFXEmitter::onAdd - No SFXTrack '%s' in SFXEmitter '%i' (%s)", profileName, getId(), getName() ); + else + { + // Remove the old 'profile' field. + setDataField( sProfile, NULL, "" ); + } + } + } + + // Convert a legacy 'channel' field, if we have one. + + static const char* sChannel = StringTable->insert( "channel" ); + const char* channelValue = getDataField( sChannel, NULL ); + if( channelValue && channelValue[ 0 ] ) + { + const char* group = Con::evaluatef( "return sfxOldChannelToGroup( %s );", channelValue ); + SFXSource* sourceGroup; + if( !Sim::findObject( group, sourceGroup ) ) + Con::errorf( "SFXEmitter::onAdd - could not resolve channel '%s' to SFXSource", channelValue ); + else + { + static const char* sSourceGroup = StringTable->insert( "sourceGroup" ); + setDataField( sSourceGroup, NULL, sourceGroup->getIdString() ); + + // Remove the old 'channel' field. + setDataField( sChannel, NULL, "" ); + } + } + } + else + { + _update(); + + // Do we need to start playback? + if( mPlayOnAdd && mSource ) + mSource->play(); + } + + // Setup the bounds. + + mObjScale.set( mDescription.mMaxDistance, mDescription.mMaxDistance, mDescription.mMaxDistance ); + resetWorldBox(); + + addToScene(); + return true; +} + +//----------------------------------------------------------------------------- + +void SFXEmitter::onRemove() +{ + SFX_DELETE( mSource ); + + removeFromScene(); + Parent::onRemove(); +} + +//----------------------------------------------------------------------------- + +void SFXEmitter::_update() +{ + AssertFatal( isClientObject(), "SFXEmitter::_update() - This shouldn't happen on the server!" ); + + // Store the playback status so we + // we can restore it. + SFXStatus prevState = mSource ? mSource->getStatus() : SFXStatusNull; + + // Make sure all the settings are valid. + mDescription.validate(); + + const MatrixF& transform = getTransform(); + const VectorF& velocity = getVelocity(); + + // Did we change the source? + if( mDirty.test( Track | Filename | Is3D | IsLooping | IsStreaming | TrackOnly ) ) + { + SFX_DELETE( mSource ); + + // Do we have a track? + if( mTrack ) + { + mSource = SFX->createSource( mTrack, &transform, &velocity ); + if( !mSource ) + Con::errorf( "SFXEmitter::_update() - failed to create sound for track %i (%s)", + mTrack->getId(), mTrack->getName() ); + + // If we're supposed to play when the emitter is + // added to the scene then also restart playback + // when the profile changes. + prevState = mPlayOnAdd ? SFXStatusPlaying : prevState; + + // Force an update of properties set on the local description. + + mDirty.set( AllDirtyMask ); + } + + // Else take the local profile + else + { + // Clear the resource and buffer on profile + // to force reload. + + mLocalProfile.mResource = NULL; + mLocalProfile.mBuffer = NULL; + + if( !mLocalProfile.mFilename.isEmpty() ) + { + mSource = SFX->createSource( &mLocalProfile, &transform, &velocity ); + if( !mSource ) + Con::errorf( "SFXEmitter::_update() - failed to create sound for: %s", + mLocalProfile.mFilename.c_str() ); + + prevState = mPlayOnAdd ? SFXStatusPlaying : prevState; + } + } + + mDirty.clear( Track | Filename | Is3D | IsLooping | IsStreaming | TrackOnly ); + } + + // Cheat if the editor is open and the looping state + // is toggled on a local profile sound. It makes the + // editor feel responsive and that things are working. + if( gEditingMission && + !mTrack && + mPlayOnAdd && + mDirty.test( IsLooping ) ) + prevState = SFXStatusPlaying; + + bool useTrackDescriptionOnly = ( mUseTrackDescriptionOnly && mTrack ); + + // The rest only applies if we have a source. + if( mSource ) + { + // Set the volume irrespective of the profile. + if( mDirty.test( Volume ) && !useTrackDescriptionOnly ) + mSource->setVolume( mDescription.mVolume ); + + if( mDirty.test( Pitch ) && !useTrackDescriptionOnly ) + mSource->setPitch( mDescription.mPitch ); + + if( mDirty.test( FadeInTime | FadeOutTime ) && !useTrackDescriptionOnly ) + mSource->setFadeTimes( mDescription.mFadeInTime, mDescription.mFadeOutTime ); + + if( mDirty.test( SourceGroup ) && mDescription.mSourceGroup && !useTrackDescriptionOnly ) + mDescription.mSourceGroup->addObject( mSource ); + + // Skip these 3d only settings. + if( mDescription.mIs3D ) + { + if( mDirty.test( Transform ) ) + { + mSource->setTransform( transform ); + mSource->setVelocity( velocity ); + } + + if( mDirty.test( MinDistance | MaxDistance ) && !useTrackDescriptionOnly ) + { + mSource->setMinMaxDistance( mDescription.mMinDistance, + mDescription.mMaxDistance ); + } + + if( mDirty.test( ConeInsideAngle | ConeOutsideAngle | ConeOutsideVolume ) && !useTrackDescriptionOnly ) + { + mSource->setCone( F32( mDescription.mConeInsideAngle ), + F32( mDescription.mConeOutsideAngle ), + mDescription.mConeOutsideVolume ); + } + + mDirty.clear( Transform | MinDistance | MaxDistance | ConeInsideAngle | ConeOutsideAngle | ConeOutsideVolume ); + } + + // Restore the pre-update playback state. + if( prevState == SFXStatusPlaying ) + mSource->play(); + + mDirty.clear( Volume | Pitch | Transform | FadeInTime | FadeOutTime | SourceGroup ); + } +} + +//----------------------------------------------------------------------------- + +void SFXEmitter::prepRenderImage( SceneRenderState* state ) +{ + // Only render in editor. + if( !gEditingMission ) + return; + + ObjectRenderInst* ri = state->getRenderPass()->allocInst< ObjectRenderInst >(); + + ri->renderDelegate.bind( this, &SFXEmitter::_renderObject ); + ri->type = RenderPassManager::RIT_Editor; + ri->defaultKey = 0; + ri->defaultKey2 = 0; + + state->getRenderPass()->addInst( ri ); +} + +//----------------------------------------------------------------------------- + +void SFXEmitter::_renderObject( ObjectRenderInst* ri, SceneRenderState* state, BaseMatInstance* overrideMat ) +{ + // Check to see if the emitter is in range and playing + // and assign a proper color depending on this. + + ColorI color; + if( _getPlaybackStatus() == SFXStatusPlaying ) + { + if( isInRange() ) + color = smRenderColorPlayingInRange; + else + color = smRenderColorPlayingOutOfRange; + } + else + { + if( isInRange() ) + color = smRenderColorStoppedInRange; + else + color = smRenderColorStoppedOutOfRange; + } + + // Draw the cube. + + GFXStateBlockDesc desc; + desc.setZReadWrite( true, false ); + desc.setBlend( true ); + desc.setCullMode( GFXCullNone ); + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + drawer->drawCube( desc, Point3F( 0.5f, 0.5f, 0.5f ), getBoxCenter(), color ); + + // Render visual feedback for 3D sounds. + + if( ( smRenderEmitters || isSelected() ) && is3D() ) + _render3DVisualFeedback(); +} + +//----------------------------------------------------------------------------- + +void SFXEmitter::_render3DVisualFeedback() +{ + GFXTransformSaver saver; + + GFX->multWorld( getRenderTransform() ); + + GFXStateBlockDesc desc; + desc.setZReadWrite( true, false ); + desc.setBlend( true ); + desc.setCullMode( GFXCullNone ); + + if( mRenderSB == NULL ) + mRenderSB = GFX->createStateBlock( desc ); + + GFX->setStateBlock( mRenderSB ); + + // Render the max range sphere. + + if( smRenderColorRangeSphere.alpha > 0 ) + GFX->getDrawUtil()->drawSphere( desc, mDescription.mMaxDistance, Point3F( 0.f, 0.f, 0.f ), smRenderColorRangeSphere ); + + //TODO: some point size support in GFX would be nice + + // Prepare primitive list. Make sure we stay within limits. + + F32 radialIncrements = smRenderRadialIncrements; + F32 sweepIncrements = smRenderSweepIncrements; + F32 pointDistance = smRenderPointDistance; + + F32 numPoints; + while( 1 ) + { + numPoints = mCeil( 360.f / radialIncrements ) * + mCeil( 360.f / sweepIncrements ) * + ( mDescription.mMaxDistance / pointDistance ); + + if( numPoints < 65536 ) + break; + + radialIncrements *= 1.1f; + sweepIncrements *= 1.1f; + pointDistance *= 1.5; + } + + PrimBuild::begin( GFXPointList, numPoints ); + + // Render inner cone. + + _renderCone( + radialIncrements, + sweepIncrements, + pointDistance, + mDescription.mConeInsideAngle, 0.f, + mDescription.mVolume, mDescription.mVolume, + smRenderColorInnerCone ); + + // Outer Cone and Outside volume only get rendered if mConeOutsideVolume > 0 + + if( mDescription.mConeOutsideVolume > 0.f ) + { + const F32 outsideVolume = mDescription.mVolume * mDescription.mConeOutsideVolume; + + // Render outer cone. + + _renderCone( + radialIncrements, + sweepIncrements, + pointDistance, + mDescription.mConeOutsideAngle, mDescription.mConeInsideAngle, + outsideVolume, mDescription.mVolume, + smRenderColorOuterCone ); + + // Render outside volume. + + _renderCone( + radialIncrements, + sweepIncrements, + pointDistance, + 360.f, mDescription.mConeOutsideAngle, + outsideVolume, outsideVolume, + smRenderColorOutsideVolume ); + } + + // Commit primitive list. + + PrimBuild::end(); +} + +//----------------------------------------------------------------------------- + +void SFXEmitter::_renderCone( F32 radialIncrements, F32 sweepIncrements, + F32 pointDistance, + F32 startAngle, F32 stopAngle, + F32 startVolume, F32 stopVolume, + const ColorI& color ) +{ + if( startAngle == stopAngle ) + return; + + const F32 startAngleRadians = mDegToRad( startAngle ); + const F32 stopAngleRadians = mDegToRad( stopAngle ); + const F32 radialIncrementsRadians = mDegToRad( radialIncrements ); + + // Unit quaternions representing the start and end angle so we + // can interpolate between the two without flipping. + + QuatF rotateZStart( EulerF( 0.f, 0.f, startAngleRadians / 2.f ) ); + QuatF rotateZEnd( EulerF( 0.f, 0.f, stopAngleRadians / 2.f ) ); + + // Do an angular sweep on one side of our XY disc. Since we do a full 360 radial sweep + // around Y for each angle, we only need to sweep over one side. + + const F32 increment = 1.f / ( ( ( startAngle / 2.f ) - ( stopAngle / 2.f ) ) / sweepIncrements ); + for( F32 t = 0.f; t < 1.0f; t += increment ) + { + // Quaternion to rotate point into place on XY disc. + QuatF rotateZ; + rotateZ.interpolate( rotateZStart, rotateZEnd, t ); + + // Quaternion to rotate one position around Y axis. Used for radial sweep. + QuatF rotateYOne( EulerF( 0.f, radialIncrementsRadians, 0.f ) ); + + // Do a radial sweep each step along the distance axis. For each step, volume is + // the same for any point on the sweep circle. + + for( F32 y = pointDistance; y <= mDescription.mMaxDistance; y += pointDistance ) + { + ColorI c = color; + + // Compute volume at current point. First off, find the interpolated volume + // in the cone. Only for the outer cone will this actually result in + // interpolation. For the remaining angles, the cone volume is constant. + + F32 volume = mLerp( startVolume, stopVolume, t ); + if( volume == 0.f ) + c.alpha = 0; + else + { + // Apply distance attenuation. + + F32 attenuatedVolume = SFXDistanceAttenuation( + SFX->getDistanceModel(), + mDescription.mMinDistance, + mDescription.mMaxDistance, + y, + volume, + SFX->getRolloffFactor() ); //RDTODO + + // Fade alpha according to how much volume we + // have left at the current point. + + c.alpha = F32( c.alpha ) * ( attenuatedVolume / 1.f ); + } + + PrimBuild::color( c ); + + // Create points by doing a full 360 degree radial sweep around Y. + + Point3F p( 0.f, y, 0.f ); + rotateZ.mulP( p, &p ); + + for( F32 radialAngle = 0.f; radialAngle < 360.f; radialAngle += radialIncrements ) + { + PrimBuild::vertex3f( p.x, p.y, p.z ); + rotateYOne.mulP( p, &p ); + } + } + } +} + +//----------------------------------------------------------------------------- + +void SFXEmitter::play() +{ + if( mSource ) + mSource->play(); + else + { + // By clearing the playback masks first we + // ensure the last playback command called + // within a single tick is the one obeyed. + clearMaskBits( AllSourceMasks ); + + setMaskBits( SourcePlayMask ); + } +} + +//----------------------------------------------------------------------------- + +void SFXEmitter::stop() +{ + if ( mSource ) + mSource->stop(); + else + { + // By clearing the playback masks first we + // ensure the last playback command called + // within a single tick is the one obeyed. + clearMaskBits( AllSourceMasks ); + + setMaskBits( SourceStopMask ); + } +} + +//----------------------------------------------------------------------------- + +SFXStatus SFXEmitter::_getPlaybackStatus() const +{ + const SFXEmitter* emitter = this; + + // We only have a source playing on client objects, so if this is a server + // object, we want to know the playback status on the local client connection's + // version of this emitter. + + if( isServerObject() ) + { + S32 index = NetConnection::getLocalClientConnection()->getGhostIndex( ( NetObject* ) this ); + if( index != -1 ) + emitter = dynamic_cast< SFXEmitter* >( NetConnection::getConnectionToServer()->resolveGhost( index ) ); + else + emitter = NULL; + } + + if( emitter && emitter->mSource ) + return emitter->mSource->getStatus(); + + return SFXStatusNull; +} + +//----------------------------------------------------------------------------- + +bool SFXEmitter::is3D() const +{ + if( mTrack != NULL ) + return mTrack->getDescription()->mIs3D; + else + return mDescription.mIs3D; +} + +//----------------------------------------------------------------------------- + +bool SFXEmitter::isInRange() const +{ + if( !mDescription.mIs3D ) + return false; + + const SFXListenerProperties& listener = SFX->getListener(); + const Point3F listenerPos = listener.getTransform().getPosition(); + const Point3F emitterPos = getPosition(); + const F32 dist = mDescription.mMaxDistance; + + return ( ( emitterPos - listenerPos ).len() <= dist ); +} + +//----------------------------------------------------------------------------- + +void SFXEmitter::setTransform( const MatrixF &mat ) +{ + // Set the transform directly from the + // matrix created by inspector. + Parent::setTransform( mat ); + setMaskBits( TransformUpdateMask ); +} + +//----------------------------------------------------------------------------- + +void SFXEmitter::setScale( const VectorF &scale ) +{ + F32 maxDistance; + + if( mUseTrackDescriptionOnly && mTrack ) + maxDistance = mTrack->getDescription()->mMaxDistance; + else + { + // Use the average of the three coords. + maxDistance = ( scale.x + scale.y + scale.z ) / 3.0f; + maxDistance = getMax( maxDistance, mDescription.mMinDistance ); + + mDescription.mMaxDistance = maxDistance; + + mDirty.set( MaxDistance ); + setMaskBits( DirtyUpdateMask ); + } + + Parent::setScale( VectorF( maxDistance, maxDistance, maxDistance ) ); +} + +//============================================================================= +// Console Methods. +//============================================================================= +// MARK: ---- Console Methods ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXEmitter, play, void, (),, + "Manually start playback of the emitter's sound.\n" + "If this is called on the server-side object, the play command will be related to all client-side ghosts.\n" ) +{ + object->play(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXEmitter, stop, void, (),, + "Manually stop playback of the emitter's sound.\n" + "If this is called on the server-side object, the stop command will be related to all client-side ghosts.\n" ) +{ + object->stop(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXEmitter, getSource, SFXSource*, (),, + "Get the sound source object from the emitter.\n\n" + "@return The sound source used by the emitter or null." + "@note This method will return null when called on the server-side SFXEmitter object. Only client-side ghosts " + "actually hold on to %SFXSources.\n\n" ) +{ + return object->getSource(); +} diff --git a/Engine/source/T3D/sfx/sfxEmitter.h b/Engine/source/T3D/sfx/sfxEmitter.h new file mode 100644 index 000000000..226cf5ce5 --- /dev/null +++ b/Engine/source/T3D/sfx/sfxEmitter.h @@ -0,0 +1,243 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXEMITTER_H_ +#define _SFXEMITTER_H_ + +#ifndef _SCENEOBJECT_H_ + #include "scene/sceneObject.h" +#endif +#ifndef _SFXPROFILE_H_ + #include "sfx/sfxProfile.h" +#endif +#ifndef _SFXDESCRIPTION_H_ + #include "sfx/sfxDescription.h" +#endif +#ifndef _GFXSTATEBLOCK_H_ + #include "gfx/gfxStateBlock.h" +#endif + + +class SFXSource; +class SFXTrack; + +//RDTODO: make 3D sound emitters yield their source when being culled + +/// The SFXEmitter is used to place 2D or 3D sounds into a +/// mission. +/// +/// If the profile is set then the emitter plays that. If the +/// profile is null and the filename is set then the local emitter +/// options are used. +/// +/// Note that you can call SFXEmitter.play() and SFXEmitter.stop() +/// to control playback from script. +/// +class SFXEmitter : public SceneObject +{ + public: + + typedef SceneObject Parent; + + protected: + + /// Network update masks. + enum UpdateMasks + { + InitialUpdateMask = BIT(0), + TransformUpdateMask = BIT(1), + DirtyUpdateMask = BIT(2), + + SourcePlayMask = BIT(3), + SourceStopMask = BIT(4), + + AllSourceMasks = SourcePlayMask | SourceStopMask, + }; + + /// Dirty flags used to handle sound property + /// updates locally and across the network. + enum Dirty + { + Track = BIT( 0 ), + Filename = BIT( 2 ), + Volume = BIT( 4 ), + IsLooping = BIT( 5 ), + Is3D = BIT( 6 ), + MinDistance = BIT( 7 ), + MaxDistance = BIT( 8 ), + ConeInsideAngle = BIT( 9 ), + ConeOutsideAngle = BIT( 10 ), + ConeOutsideVolume = BIT( 11 ), + Transform = BIT( 12 ), + SourceGroup = BIT( 13 ), + OutsideAmbient = BIT( 14 ), + IsStreaming = BIT( 15 ), + FadeInTime = BIT( 16 ), + FadeOutTime = BIT( 17 ), + Pitch = BIT( 18 ), + ScatterDistance = BIT( 19 ), + TrackOnly = BIT( 20 ), + + AllDirtyMask = 0xFFFFFFFF, + }; + + /// The current dirty flags. + BitSet32 mDirty; + + /// The sound source for the emitter. + SFXSource *mSource; + + /// The selected track or null if the local + /// profile should be used. + SFXTrack *mTrack; + + /// Whether to leave sound setup exclusively to the assigned mTrack and not + /// override part of the track's description with emitter properties. + bool mUseTrackDescriptionOnly; + + /// A local profile object used to coax the + /// sound system to play a custom sound. + SFXProfile mLocalProfile; + + /// The description used by the local profile. + SFXDescription mDescription; + + /// If true playback starts when the emitter + /// is added to the scene. + bool mPlayOnAdd; + + /// State block for cone rendering in editor. + GFXStateBlockRef mRenderSB; + + /// If true, render all emitters when in editor (not only selected one). + static bool smRenderEmitters; + + /// Point size for rendering point clouds of emitter cones in editor. + /// @todo Currently not implemented. + static F32 smRenderPointSize; + + /// + static F32 smRenderRadialIncrements; + + /// + static F32 smRenderSweepIncrements; + + /// + static F32 smRenderPointDistance; + + /// Point color when emitter is playing and in range of listener. + static ColorI smRenderColorPlayingInRange; + + /// Point color when emitter is playing but out of range of listern. + static ColorI smRenderColorPlayingOutOfRange; + + /// Point color when emitter is not playing but in range of listener. + static ColorI smRenderColorStoppedInRange; + + /// Point color when emitter is not playing and not in range of listener. + static ColorI smRenderColorStoppedOutOfRange; + + /// + static ColorI smRenderColorInnerCone; + + /// + static ColorI smRenderColorOuterCone; + + /// + static ColorI smRenderColorOutsideVolume; + + /// + static ColorI smRenderColorRangeSphere; + + /// Helper which reads a flag from the stream and + /// updates the mDirty bits. + bool _readDirtyFlag( BitStream *stream, U32 flag ); + + /// Called when the emitter state has been marked + /// dirty and the source needs to be updated. + void _update(); + + /// Render emitter object in editor. + void _renderObject( ObjectRenderInst* ri, SceneRenderState* state, BaseMatInstance* overrideMat ); + + /// Render visual feedback for 3D sounds in editor. + void _render3DVisualFeedback(); + + /// + void _renderCone( F32 radialIncrements, + F32 sweepIncrements, + F32 pointDistance, + F32 startAngle, + F32 stopAngle, + F32 startVolume, + F32 stopVolume, + const ColorI& color ); + + /// Return the playback status of the emitter's associated sound. + /// This should only be called on either the ghost or the server object if the server is running + /// in-process. Otherwise, the method will not return a meaningful value. + SFXStatus _getPlaybackStatus() const; + + public: + + SFXEmitter(); + virtual ~SFXEmitter(); + + /// Return the sound source object associated with the emitter. + /// @note This will only return a meaningful result when called on ghost objects. + SFXSource* getSource() const { return mSource; } + + /// Return true if this object emits a 3D sound. + bool is3D() const; + + /// Return true if the SFX system's listener is in range of this emitter. + bool isInRange() const; + + /// Sends network event to start playback if + /// the emitter source is not already playing. + void play(); + + /// Sends network event to stop emitter + /// playback on all ghosted clients. + void stop(); + + // SimObject + bool onAdd(); + void onRemove(); + void onStaticModified( const char *slotName, const char *newValue = NULL ); + U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream ); + void unpackUpdate( NetConnection *conn, BitStream *stream ); + void setTransform( const MatrixF &mat ); + void setScale( const VectorF &scale ); + bool containsPoint( const Point3F& point ) { return false; } + void prepRenderImage( SceneRenderState* state ); + void inspectPostApply(); + + static void initPersistFields(); + static void consoleInit(); + + DECLARE_CONOBJECT( SFXEmitter ); + DECLARE_DESCRIPTION( "A 3D object emitting sound." ); + DECLARE_CATEGORY( "3D Sound" ); +}; + +#endif // _SFXEMITTER_H_ diff --git a/Engine/source/T3D/sfx/sfxSpace.cpp b/Engine/source/T3D/sfx/sfxSpace.cpp new file mode 100644 index 000000000..b0130c2d3 --- /dev/null +++ b/Engine/source/T3D/sfx/sfxSpace.cpp @@ -0,0 +1,56 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/sfx/sfxSpace.h" + +#include "scene/mixin/sceneAmbientSoundObject.impl.h" +#include "scene/mixin/scenePolyhedralObject.impl.h" +#include "console/engineAPI.h" + + +IMPLEMENT_CO_NETOBJECT_V1( SFXSpace ); + +ConsoleDocClass( SFXSpace, + "@brief A volume in space that defines an ambient sound zone.\n\n" + + "@ingroup SFX" +); + +//----------------------------------------------------------------------------- + +SFXSpace::SFXSpace() +{ + mObjScale.set( 10, 10, 10 ); + mObjBox.set( + Point3F( -0.5f, -0.5f, -0.5f ), + Point3F( 0.5f, 0.5f, 0.5f ) + ); +} + +//----------------------------------------------------------------------------- + +void SFXSpace::consoleInit() +{ + // Disable rendering of SFXSpaces by default. + getStaticClassRep()->mIsRenderEnabled = false; +} diff --git a/Engine/source/T3D/sfx/sfxSpace.h b/Engine/source/T3D/sfx/sfxSpace.h new file mode 100644 index 000000000..028a85fdf --- /dev/null +++ b/Engine/source/T3D/sfx/sfxSpace.h @@ -0,0 +1,63 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXSPACE_H_ +#define _SFXSPACE_H_ + +#ifndef _SCENESPACE_H_ +#include "scene/sceneSpace.h" +#endif + +#ifndef _SCENEAMBIENTSOUNDOBJECT_H_ +#include "scene/mixin/sceneAmbientSoundObject.h" +#endif + +#ifndef _SCENEPOLYHEDRALOBJECT_H_ +#include "scene/mixin/scenePolyhedralObject.h" +#endif + + +/// A convex space that defines a custom ambient sound space. +class SFXSpace : public SceneAmbientSoundObject< ScenePolyhedralObject< SceneSpace > > +{ + public: + + typedef SceneAmbientSoundObject< ScenePolyhedralObject< SceneSpace > > Parent; + + protected: + + // SceneSpace. + virtual ColorI _getDefaultEditorSolidColor() const { return ColorI( 244, 135, 18, 45 ); } + + public: + + SFXSpace(); + + // SimObject. + DECLARE_CONOBJECT( SFXSpace ); + DECLARE_DESCRIPTION( "A box volume that defines an ambient sound space." ); + DECLARE_CATEGORY( "3D Sound" ); + + static void consoleInit(); +}; + +#endif // !_SFXSPACE_H_ diff --git a/Engine/source/T3D/shapeBase.cpp b/Engine/source/T3D/shapeBase.cpp new file mode 100644 index 000000000..666f64e32 --- /dev/null +++ b/Engine/source/T3D/shapeBase.cpp @@ -0,0 +1,5152 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/shapeBase.h" + +#include "core/dnet.h" +#include "sfx/sfxSystem.h" +#include "sfx/sfxSource.h" +#include "sfx/sfxTrack.h" +#include "sfx/sfxDescription.h" +#include "T3D/sfx/sfx3DWorld.h" +#include "T3D/gameBase/gameConnection.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "core/stream/bitStream.h" +#include "ts/tsPartInstance.h" +#include "ts/tsShapeInstance.h" +#include "ts/tsMaterialList.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "scene/sceneObjectLightingPlugin.h" +#include "T3D/fx/explosion.h" +#include "T3D/fx/particleEmitter.h" +#include "T3D/fx/cameraFXMgr.h" +#include "environment/waterBlock.h" +#include "T3D/debris.h" +#include "T3D/physicalZone.h" +#include "T3D/containerQuery.h" +#include "math/mathUtils.h" +#include "math/mMatrix.h" +#include "math/mTransform.h" +#include "math/mRandom.h" +#include "platform/profiler.h" +#include "gfx/gfxCubemap.h" +#include "gfx/gfxDrawUtil.h" +#include "gfx/gfxTransformSaver.h" +#include "renderInstance/renderPassManager.h" +#include "collision/earlyOutPolyList.h" +#include "core/resourceManager.h" +#include "scene/reflectionManager.h" +#include "gfx/sim/cubemapData.h" +#include "materials/materialManager.h" +#include "materials/materialFeatureTypes.h" +#include "renderInstance/renderOcclusionMgr.h" +#include "core/stream/fileStream.h" + +IMPLEMENT_CO_DATABLOCK_V1(ShapeBaseData); + +ConsoleDocClass( ShapeBaseData, + "@brief Defines properties for a ShapeBase object.\n\n" + "@see ShapeBase\n" + "@ingroup gameObjects\n" +); + +IMPLEMENT_CALLBACK( ShapeBaseData, onEnabled, void, ( ShapeBase* obj, const char* lastState ), ( obj, lastState ), + "@brief Called when the object damage state changes to Enabled.\n\n" + "@param obj The ShapeBase object\n" + "@param lastState The previous damage state\n" ); + +IMPLEMENT_CALLBACK( ShapeBaseData, onDisabled, void, ( ShapeBase* obj, const char* lastState ), ( obj, lastState ), + "@brief Called when the object damage state changes to Disabled.\n\n" + "@param obj The ShapeBase object\n" + "@param lastState The previous damage state\n" ); + +IMPLEMENT_CALLBACK( ShapeBaseData, onDestroyed, void, ( ShapeBase* obj, const char* lastState ), ( obj, lastState ), + "@brief Called when the object damage state changes to Destroyed.\n\n" + "@param obj The ShapeBase object\n" + "@param lastState The previous damage state\n" ); + +IMPLEMENT_CALLBACK( ShapeBaseData, onImpact, void, ( ShapeBase* obj, SceneObject *collObj, VectorF vec, F32 len ), ( obj, collObj, vec, len ), + "@brief Called when we collide with another object beyond some impact speed.\n\n" + "The Player class makes use of this callback when a collision speed is more than PlayerData::minImpactSpeed.\n" + "@param obj The ShapeBase object\n" + "@param collObj The object we collided with\n" + "@param vec Collision impact vector\n" + "@param len Length of the impact vector\n" ); + +IMPLEMENT_CALLBACK( ShapeBaseData, onCollision, void, ( ShapeBase* obj, SceneObject *collObj, VectorF vec, F32 len ), ( obj, collObj, vec, len ), + "@brief Called when we collide with another object.\n\n" + "@param obj The ShapeBase object\n" + "@param collObj The object we collided with\n" + "@param vec Collision impact vector\n" + "@param len Length of the impact vector\n" ); + +IMPLEMENT_CALLBACK( ShapeBaseData, onDamage, void, ( ShapeBase* obj, F32 delta ), ( obj, delta ), + "@brief Called when the object is damaged.\n\n" + "@param obj The ShapeBase object\n" + "@param obj The ShapeBase object\n" + "@param delta The amount of damage received." ); + +IMPLEMENT_CALLBACK( ShapeBaseData, onTrigger, void, ( ShapeBase* obj, S32 index, bool state ), ( obj, index, state ), + "@brief Called when a move trigger input changes state.\n\n" + "@param obj The ShapeBase object\n" + "@param index Index of the trigger that changed\n" + "@param state New state of the trigger\n" ); + +IMPLEMENT_CALLBACK( ShapeBaseData, onEndSequence, void, ( ShapeBase* obj, S32 slot ), ( obj, slot ), + "@brief Called when a thread playing a non-cyclic sequence reaches the end of the " + "sequence.\n\n" + "@param obj The ShapeBase object\n" + "@param slot Thread slot that finished playing\n" ); + +IMPLEMENT_CALLBACK( ShapeBaseData, onForceUncloak, void, ( ShapeBase* obj, const char* reason ), ( obj, reason ), + "@brief Called when the object is forced to uncloak.\n\n" + "@param obj The ShapeBase object\n" + "@param reason String describing why the object was uncloaked\n" ); + +//---------------------------------------------------------------------------- +// Timeout for non-looping sounds on a channel +static SimTime sAudioTimeout = 500; +F32 ShapeBase::sWhiteoutDec = 0.007f; +F32 ShapeBase::sDamageFlashDec = 0.02f; +F32 ShapeBase::sFullCorrectionDistance = 0.5f; +F32 ShapeBase::sCloakSpeed = 0.5; +U32 ShapeBase::sLastRenderFrame = 0; + +static const char *sDamageStateName[] = +{ + // Index by enum ShapeBase::DamageState + "Enabled", + "Disabled", + "Destroyed" +}; + + +//---------------------------------------------------------------------------- + +ShapeBaseData::ShapeBaseData() + : shadowEnable( false ), + shadowSize( 128 ), + shadowMaxVisibleDistance( 80.0f ), + shadowProjectionDistance( 10.0f ), + shadowSphereAdjust( 1.0f ), + shapeName( StringTable->insert("") ), + cloakTexName( StringTable->insert("") ), + mass( 1.0f ), + drag( 0.0f ), + density( 1.0f ), + maxEnergy( 0.0f ), + maxDamage( 1.0f ), + disabledLevel( 1.0f ), + destroyedLevel( 1.0f ), + repairRate( 0.0033f ), + eyeNode( -1 ), + earNode( -1 ), + cameraNode( -1 ), + damageSequence( -1 ), + hulkSequence( -1 ), + cameraMaxDist( 0.0f ), + cameraMinDist( 0.2f ), + cameraDefaultFov( 75.0f ), + cameraMinFov( 5.0f ), + cameraMaxFov( 120.f ), + isInvincible( false ), + renderWhenDestroyed( true ), + debris( NULL ), + debrisID( 0 ), + debrisShapeName( StringTable->insert("") ), + explosion( NULL ), + explosionID( 0 ), + underwaterExplosion( NULL ), + underwaterExplosionID( 0 ), + firstPersonOnly( false ), + useEyePoint( false ), + cubeDescId( 0 ), + reflectorDesc( NULL ), + observeThroughObject( false ), + computeCRC( false ), + inheritEnergyFromMount( false ), + mCRC( 0 ), + debrisDetail( -1 ) +{ + dMemset( mountPointNode, -1, sizeof( S32 ) * SceneObject::NumMountPoints ); +} + +struct ShapeBaseDataProto +{ + F32 mass; + F32 drag; + F32 density; + F32 maxEnergy; + F32 cameraMaxDist; + F32 cameraMinDist; + F32 cameraDefaultFov; + F32 cameraMinFov; + F32 cameraMaxFov; + + + ShapeBaseDataProto() + { + mass = 1; + drag = 0; + density = 1; + maxEnergy = 0; + cameraMaxDist = 0; + cameraMinDist = 0.2f; + cameraDefaultFov = 75.f; + cameraMinFov = 5.0f; + cameraMaxFov = 120.f; + } +}; + +static ShapeBaseDataProto gShapeBaseDataProto; + +ShapeBaseData::~ShapeBaseData() +{ + +} + +bool ShapeBaseData::preload(bool server, String &errorStr) +{ + if (!Parent::preload(server, errorStr)) + return false; + bool shapeError = false; + + // Resolve objects transmitted from server + if (!server) { + + if( !explosion && explosionID != 0 ) + { + if( Sim::findObject( explosionID, explosion ) == false) + { + Con::errorf( ConsoleLogEntry::General, "ShapeBaseData::preload: Invalid packet, bad datablockId(explosion): 0x%x", explosionID ); + } + AssertFatal(!(explosion && ((explosionID < DataBlockObjectIdFirst) || (explosionID > DataBlockObjectIdLast))), + "ShapeBaseData::preload: invalid explosion data"); + } + + if( !underwaterExplosion && underwaterExplosionID != 0 ) + { + if( Sim::findObject( underwaterExplosionID, underwaterExplosion ) == false) + { + Con::errorf( ConsoleLogEntry::General, "ShapeBaseData::preload: Invalid packet, bad datablockId(underwaterExplosion): 0x%x", underwaterExplosionID ); + } + AssertFatal(!(underwaterExplosion && ((underwaterExplosionID < DataBlockObjectIdFirst) || (underwaterExplosionID > DataBlockObjectIdLast))), + "ShapeBaseData::preload: invalid underwaterExplosion data"); + } + + if( !debris && debrisID != 0 ) + { + Sim::findObject( debrisID, debris ); + AssertFatal(!(debris && ((debrisID < DataBlockObjectIdFirst) || (debrisID > DataBlockObjectIdLast))), + "ShapeBaseData::preload: invalid debris data"); + } + + + if( debrisShapeName && debrisShapeName[0] != '\0' && !bool(debrisShape) ) + { + debrisShape = ResourceManager::get().load(debrisShapeName); + if( bool(debrisShape) == false ) + { + errorStr = String::ToString("ShapeBaseData::load: Couldn't load shape \"%s\"", debrisShapeName); + return false; + } + else + { + if(!server && !debrisShape->preloadMaterialList(debrisShape.getPath()) && NetConnection::filesWereDownloaded()) + shapeError = true; + + TSShapeInstance* pDummy = new TSShapeInstance(debrisShape, !server); + delete pDummy; + } + } + } + + // + if (shapeName && shapeName[0]) { + S32 i; + + // Resolve shapename + mShape = ResourceManager::get().load(shapeName); + if (bool(mShape) == false) + { + errorStr = String::ToString("ShapeBaseData: Couldn't load shape \"%s\"",shapeName); + return false; + } + if(!server && !mShape->preloadMaterialList(mShape.getPath()) && NetConnection::filesWereDownloaded()) + shapeError = true; + + if(computeCRC) + { + Con::printf("Validation required for shape: %s", shapeName); + + Torque::FS::FileNodeRef fileRef = Torque::FS::GetFileNode(mShape.getPath()); + + if (!fileRef) + return false; + + if(server) + mCRC = fileRef->getChecksum(); + else if(mCRC != fileRef->getChecksum()) + { + errorStr = String::ToString("Shape \"%s\" does not match version on server.",shapeName); + return false; + } + } + // Resolve details and camera node indexes. + static const String sCollisionStr( "collision-" ); + + for (i = 0; i < mShape->details.size(); i++) + { + const String &name = mShape->names[mShape->details[i].nameIndex]; + + if (name.compare( sCollisionStr, sCollisionStr.length(), String::NoCase ) == 0) + { + collisionDetails.push_back(i); + collisionBounds.increment(); + + mShape->computeBounds(collisionDetails.last(), collisionBounds.last()); + mShape->getAccelerator(collisionDetails.last()); + + if (!mShape->bounds.isContained(collisionBounds.last())) + { + Con::warnf("Warning: shape %s collision detail %d (Collision-%d) bounds exceed that of shape.", shapeName, collisionDetails.size() - 1, collisionDetails.last()); + collisionBounds.last() = mShape->bounds; + } + else if (collisionBounds.last().isValidBox() == false) + { + Con::errorf("Error: shape %s-collision detail %d (Collision-%d) bounds box invalid!", shapeName, collisionDetails.size() - 1, collisionDetails.last()); + collisionBounds.last() = mShape->bounds; + } + + // The way LOS works is that it will check to see if there is a LOS detail that matches + // the the collision detail + 1 + MaxCollisionShapes (this variable name should change in + // the future). If it can't find a matching LOS it will simply use the collision instead. + // We check for any "unmatched" LOS's further down + LOSDetails.increment(); + + String buff = String::ToString("LOS-%d", i + 1 + MaxCollisionShapes); + U32 los = mShape->findDetail(buff); + if (los == -1) + LOSDetails.last() = i; + else + LOSDetails.last() = los; + } + } + + // Snag any "unmatched" LOS details + static const String sLOSStr( "LOS-" ); + + for (i = 0; i < mShape->details.size(); i++) + { + const String &name = mShape->names[mShape->details[i].nameIndex]; + + if (name.compare( sLOSStr, sLOSStr.length(), String::NoCase ) == 0) + { + // See if we already have this LOS + bool found = false; + for (U32 j = 0; j < LOSDetails.size(); j++) + { + if (LOSDetails[j] == i) + { + found = true; + break; + } + } + + if (!found) + LOSDetails.push_back(i); + } + } + + debrisDetail = mShape->findDetail("Debris-17"); + eyeNode = mShape->findNode("eye"); + earNode = mShape->findNode( "ear" ); + if( earNode == -1 ) + earNode = eyeNode; + cameraNode = mShape->findNode("cam"); + if (cameraNode == -1) + cameraNode = eyeNode; + + // Resolve mount point node indexes + for (i = 0; i < SceneObject::NumMountPoints; i++) { + char fullName[256]; + dSprintf(fullName,sizeof(fullName),"mount%d",i); + mountPointNode[i] = mShape->findNode(fullName); + } + + // find the AIRepairNode - hardcoded to be the last node in the array... + mountPointNode[AIRepairNode] = mShape->findNode("AIRepairNode"); + + // + hulkSequence = mShape->findSequence("Visibility"); + damageSequence = mShape->findSequence("Damage"); + + // + F32 w = mShape->bounds.len_y() / 2; + if (cameraMaxDist < w) + cameraMaxDist = w; + } + + if(!server) + { +/* + // grab all the hud images + for(U32 i = 0; i < NumHudRenderImages; i++) + { + if(hudImageNameFriendly[i] && hudImageNameFriendly[i][0]) + hudImageFriendly[i] = TextureHandle(hudImageNameFriendly[i], BitmapTexture); + + if(hudImageNameEnemy[i] && hudImageNameEnemy[i][0]) + hudImageEnemy[i] = TextureHandle(hudImageNameEnemy[i], BitmapTexture); + } +*/ + } + + // Resolve CubeReflectorDesc. + if ( cubeDescName.isNotEmpty() ) + { + Sim::findObject( cubeDescName, reflectorDesc ); + } + else if( cubeDescId > 0 ) + { + Sim::findObject( cubeDescId, reflectorDesc ); + } + + return !shapeError; +} + +bool ShapeBaseData::_setMass( void* object, const char* index, const char* data ) +{ + ShapeBaseData* shape = reinterpret_cast< ShapeBaseData* >( object ); + + F32 mass = dAtof(data); + + if (mass <= 0) + mass = 0.01f; + + shape->mass = mass; + + return false; +} + + +void ShapeBaseData::initPersistFields() +{ + addGroup( "Shadows" ); + + addField( "shadowEnable", TypeBool, Offset(shadowEnable, ShapeBaseData), + "Enable shadows for this shape (currently unused, shadows are always enabled)." ); + addField( "shadowSize", TypeS32, Offset(shadowSize, ShapeBaseData), + "Size of the projected shadow texture (must be power of 2)." ); + addField( "shadowMaxVisibleDistance", TypeF32, Offset(shadowMaxVisibleDistance, ShapeBaseData), + "Maximum distance at which shadow is visible (currently unused)." ); + addField( "shadowProjectionDistance", TypeF32, Offset(shadowProjectionDistance, ShapeBaseData), + "Maximum height above ground to project shadow. If the object is higher " + "than this no shadow will be rendered." ); + addField( "shadowSphereAdjust", TypeF32, Offset(shadowSphereAdjust, ShapeBaseData), + "Scalar applied to the radius of spot shadows (initial radius is based " + "on the shape bounds but can be adjusted with this field)." ); + + endGroup( "Shadows" ); + + addGroup( "Render" ); + + addField( "shapeFile", TypeShapeFilename, Offset(shapeName, ShapeBaseData), + "The DTS or DAE model to use for this object." ); + + endGroup( "Render" ); + + addGroup( "Destruction", "Parameters related to the destruction effects of this object." ); + + addField( "explosion", TYPEID< ExplosionData >(), Offset(explosion, ShapeBaseData), + "%Explosion to generate when this shape is blown up." ); + addField( "underwaterExplosion", TYPEID< ExplosionData >(), Offset(underwaterExplosion, ShapeBaseData), + "%Explosion to generate when this shape is blown up underwater." ); + addField( "debris", TYPEID< DebrisData >(), Offset(debris, ShapeBaseData), + "%Debris to generate when this shape is blown up." ); + addField( "renderWhenDestroyed", TypeBool, Offset(renderWhenDestroyed, ShapeBaseData), + "Whether to render the shape when it is in the \"Destroyed\" damage state." ); + addField( "debrisShapeName", TypeShapeFilename, Offset(debrisShapeName, ShapeBaseData), + "The DTS or DAE model to use for auto-generated breakups. @note may not be functional." ); + + endGroup( "Destruction" ); + + addGroup( "Physics" ); + + addProtectedField("mass", TypeF32, Offset(mass, ShapeBaseData), &_setMass, &defaultProtectedGetFn, "Shape mass.\nUsed in simulation of moving objects.\n" ); + addField( "drag", TypeF32, Offset(drag, ShapeBaseData), + "Drag factor.\nReduces velocity of moving objects." ); + addField( "density", TypeF32, Offset(density, ShapeBaseData), + "Shape density.\nUsed when computing buoyancy when in water.\n" ); + + endGroup( "Physics" ); + + addGroup( "Damage/Energy" ); + + addField( "maxEnergy", TypeF32, Offset(maxEnergy, ShapeBaseData), + "Maximum energy level for this object." ); + addField( "maxDamage", TypeF32, Offset(maxDamage, ShapeBaseData), + "Maximum damage level for this object." ); + addField( "disabledLevel", TypeF32, Offset(disabledLevel, ShapeBaseData), + "Damage level above which the object is disabled.\n" + "Currently unused." ); + addField( "destroyedLevel", TypeF32, Offset(destroyedLevel, ShapeBaseData), + "Damage level above which the object is destroyed.\n" + "When the damage level increases above this value, the object damage " + "state is set to \"Destroyed\"." ); + addField( "repairRate", TypeF32, Offset(repairRate, ShapeBaseData), + "Rate at which damage is repaired in damage units/tick.\n" + "This value is subtracted from the damage level until it reaches 0." ); + addField( "inheritEnergyFromMount", TypeBool, Offset(inheritEnergyFromMount, ShapeBaseData), + "Flag controlling whether to manage our own energy level, or to use " + "the energy level of the object we are mounted to." ); + addField( "isInvincible", TypeBool, Offset(isInvincible, ShapeBaseData), + "Invincible flag; when invincible, the object cannot be damaged or " + "repaired." ); + + endGroup( "Damage/Energy" ); + + addGroup( "Camera", "The settings used by the shape when it is the camera." ); + + addField( "cameraMaxDist", TypeF32, Offset(cameraMaxDist, ShapeBaseData), + "The maximum distance from the camera to the object.\n" + "Used when computing a custom camera transform for this object.\n\n" + "@see observeThroughObject" ); + addField( "cameraMinDist", TypeF32, Offset(cameraMinDist, ShapeBaseData), + "The minimum distance from the camera to the object.\n" + "Used when computing a custom camera transform for this object.\n\n" + "@see observeThroughObject" ); + addField( "cameraDefaultFov", TypeF32, Offset(cameraDefaultFov, ShapeBaseData), + "The default camera vertical FOV in degrees." ); + addField( "cameraMinFov", TypeF32, Offset(cameraMinFov, ShapeBaseData), + "The minimum camera vertical FOV allowed in degrees." ); + addField( "cameraMaxFov", TypeF32, Offset(cameraMaxFov, ShapeBaseData), + "The maximum camera vertical FOV allowed in degrees." ); + addField( "firstPersonOnly", TypeBool, Offset(firstPersonOnly, ShapeBaseData), + "Flag controlling whether the view from this object is first person " + "only." ); + addField( "useEyePoint", TypeBool, Offset(useEyePoint, ShapeBaseData), + "Flag controlling whether the client uses this object's eye point to " + "view from." ); + addField( "observeThroughObject", TypeBool, Offset(observeThroughObject, ShapeBaseData), + "Observe this object through its camera transform and default fov.\n" + "If true, when this object is the camera it can provide a custom camera " + "transform and FOV (instead of the default eye transform)." ); + + endGroup("Camera"); + + addGroup( "Misc" ); + + addField( "computeCRC", TypeBool, Offset(computeCRC, ShapeBaseData), + "If true, verify that the CRC of the client's shape model matches the " + "server's CRC for the shape model when loaded by the client." ); + + endGroup( "Misc" ); + + addGroup( "Reflection" ); + + addField( "cubeReflectorDesc", TypeRealString, Offset( cubeDescName, ShapeBaseData ), + "References a ReflectorDesc datablock that defines performance and quality properties for dynamic reflections.\n"); + //addField( "reflectMaxRateMs", TypeS32, Offset( reflectMaxRateMs, ShapeBaseData ), "reflection will not be updated more frequently than this" ); + //addField( "reflectMaxDist", TypeF32, Offset( reflectMaxDist, ShapeBaseData ), "distance at which reflection is never updated" ); + //addField( "reflectMinDist", TypeF32, Offset( reflectMinDist, ShapeBaseData ), "distance at which reflection is always updated" ); + //addField( "reflectDetailAdjust", TypeF32, Offset( reflectDetailAdjust, ShapeBaseData ), "scale up or down the detail level for objects rendered in a reflection" ); + + endGroup( "Reflection" ); + + Parent::initPersistFields(); +} + +DefineEngineMethod( ShapeBaseData, checkDeployPos, bool, ( TransformF txfm ),, + "@brief Check if there is the space at the given transform is free to spawn into.\n\n" + + "The shape's bounding box volume is used to check for collisions at the given world " + "transform. Only interior and static objects are checked for collision.\n" + + "@param txfm Deploy transform to check\n" + "@return True if the space is free, false if there is already something in " + "the way.\n" + + "@note This is a server side only check, and is not actually limited to spawning.\n") +{ + if (bool(object->mShape) == false) + return false; + + MatrixF mat = txfm.getMatrix(); + + Box3F objBox = object->mShape->bounds; + Point3F boxCenter = (objBox.minExtents + objBox.maxExtents) * 0.5f; + objBox.minExtents = boxCenter + (objBox.minExtents - boxCenter) * 0.9f; + objBox.maxExtents = boxCenter + (objBox.maxExtents - boxCenter) * 0.9f; + + Box3F wBox = objBox; + mat.mul(wBox); + + EarlyOutPolyList polyList; + polyList.mNormal.set(0,0,0); + polyList.mPlaneList.clear(); + polyList.mPlaneList.setSize(6); + polyList.mPlaneList[0].set(objBox.minExtents,VectorF(-1,0,0)); + polyList.mPlaneList[1].set(objBox.maxExtents,VectorF(0,1,0)); + polyList.mPlaneList[2].set(objBox.maxExtents,VectorF(1,0,0)); + polyList.mPlaneList[3].set(objBox.minExtents,VectorF(0,-1,0)); + polyList.mPlaneList[4].set(objBox.minExtents,VectorF(0,0,-1)); + polyList.mPlaneList[5].set(objBox.maxExtents,VectorF(0,0,1)); + + for (U32 i = 0; i < 6; i++) + { + PlaneF temp; + mTransformPlane(mat, Point3F(1, 1, 1), polyList.mPlaneList[i], &temp); + polyList.mPlaneList[i] = temp; + } + + if (gServerContainer.buildPolyList(PLC_Collision, wBox, InteriorObjectType | StaticShapeObjectType, &polyList)) + return false; + return true; +} + + +DefineEngineMethod(ShapeBaseData, getDeployTransform, TransformF, ( Point3F pos, Point3F normal ),, + "@brief Helper method to get a transform from a position and vector (suitable for use with setTransform).\n\n" + "@param pos Desired transform position\n" + "@param normal Vector of desired direction\n" + "@return The deploy transform\n" ) +{ + normal.normalize(); + + VectorF xAxis; + if( mFabs(normal.z) > mFabs(normal.x) && mFabs(normal.z) > mFabs(normal.y)) + mCross( VectorF( 0, 1, 0 ), normal, &xAxis ); + else + mCross( VectorF( 0, 0, 1 ), normal, &xAxis ); + + VectorF yAxis; + mCross( normal, xAxis, &yAxis ); + + MatrixF testMat(true); + testMat.setColumn( 0, xAxis ); + testMat.setColumn( 1, yAxis ); + testMat.setColumn( 2, normal ); + testMat.setPosition( pos ); + + return testMat; +} + +void ShapeBaseData::packData(BitStream* stream) +{ + Parent::packData(stream); + + if(stream->writeFlag(computeCRC)) + stream->write(mCRC); + + stream->writeFlag(shadowEnable); + stream->write(shadowSize); + stream->write(shadowMaxVisibleDistance); + stream->write(shadowProjectionDistance); + stream->write(shadowSphereAdjust); + + + stream->writeString(shapeName); + stream->writeString(cloakTexName); + if(stream->writeFlag(mass != gShapeBaseDataProto.mass)) + stream->write(mass); + if(stream->writeFlag(drag != gShapeBaseDataProto.drag)) + stream->write(drag); + if(stream->writeFlag(density != gShapeBaseDataProto.density)) + stream->write(density); + if(stream->writeFlag(maxEnergy != gShapeBaseDataProto.maxEnergy)) + stream->write(maxEnergy); + if(stream->writeFlag(cameraMaxDist != gShapeBaseDataProto.cameraMaxDist)) + stream->write(cameraMaxDist); + if(stream->writeFlag(cameraMinDist != gShapeBaseDataProto.cameraMinDist)) + stream->write(cameraMinDist); + cameraDefaultFov = mClampF(cameraDefaultFov, cameraMinFov, cameraMaxFov); + if(stream->writeFlag(cameraDefaultFov != gShapeBaseDataProto.cameraDefaultFov)) + stream->write(cameraDefaultFov); + if(stream->writeFlag(cameraMinFov != gShapeBaseDataProto.cameraMinFov)) + stream->write(cameraMinFov); + if(stream->writeFlag(cameraMaxFov != gShapeBaseDataProto.cameraMaxFov)) + stream->write(cameraMaxFov); + stream->writeString( debrisShapeName ); + + stream->writeFlag(observeThroughObject); + + if( stream->writeFlag( debris != NULL ) ) + { + stream->writeRangedU32(packed? SimObjectId(debris): + debris->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast); + } + + stream->writeFlag(isInvincible); + stream->writeFlag(renderWhenDestroyed); + + if( stream->writeFlag( explosion != NULL ) ) + { + stream->writeRangedU32( explosion->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + + if( stream->writeFlag( underwaterExplosion != NULL ) ) + { + stream->writeRangedU32( underwaterExplosion->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + + stream->writeFlag(inheritEnergyFromMount); + stream->writeFlag(firstPersonOnly); + stream->writeFlag(useEyePoint); + + if( stream->writeFlag( reflectorDesc != NULL ) ) + { + stream->writeRangedU32( reflectorDesc->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + + //stream->write(reflectPriority); + //stream->write(reflectMaxRateMs); + //stream->write(reflectMinDist); + //stream->write(reflectMaxDist); + //stream->write(reflectDetailAdjust); +} + +void ShapeBaseData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + computeCRC = stream->readFlag(); + if(computeCRC) + stream->read(&mCRC); + + shadowEnable = stream->readFlag(); + stream->read(&shadowSize); + stream->read(&shadowMaxVisibleDistance); + stream->read(&shadowProjectionDistance); + stream->read(&shadowSphereAdjust); + + shapeName = stream->readSTString(); + cloakTexName = stream->readSTString(); + if(stream->readFlag()) + stream->read(&mass); + else + mass = gShapeBaseDataProto.mass; + + if(stream->readFlag()) + stream->read(&drag); + else + drag = gShapeBaseDataProto.drag; + + if(stream->readFlag()) + stream->read(&density); + else + density = gShapeBaseDataProto.density; + + if(stream->readFlag()) + stream->read(&maxEnergy); + else + maxEnergy = gShapeBaseDataProto.maxEnergy; + + if(stream->readFlag()) + stream->read(&cameraMaxDist); + else + cameraMaxDist = gShapeBaseDataProto.cameraMaxDist; + + if(stream->readFlag()) + stream->read(&cameraMinDist); + else + cameraMinDist = gShapeBaseDataProto.cameraMinDist; + + if(stream->readFlag()) + stream->read(&cameraDefaultFov); + else + cameraDefaultFov = gShapeBaseDataProto.cameraDefaultFov; + + if(stream->readFlag()) + stream->read(&cameraMinFov); + else + cameraMinFov = gShapeBaseDataProto.cameraMinFov; + + if(stream->readFlag()) + stream->read(&cameraMaxFov); + else + cameraMaxFov = gShapeBaseDataProto.cameraMaxFov; + + debrisShapeName = stream->readSTString(); + + observeThroughObject = stream->readFlag(); + + if( stream->readFlag() ) + { + debrisID = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + + isInvincible = stream->readFlag(); + renderWhenDestroyed = stream->readFlag(); + + if( stream->readFlag() ) + { + explosionID = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + + if( stream->readFlag() ) + { + underwaterExplosionID = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + + inheritEnergyFromMount = stream->readFlag(); + firstPersonOnly = stream->readFlag(); + useEyePoint = stream->readFlag(); + + if( stream->readFlag() ) + { + cubeDescId = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + + //stream->read(&reflectPriority); + //stream->read(&reflectMaxRateMs); + //stream->read(&reflectMinDist); + //stream->read(&reflectMaxDist); + //stream->read(&reflectDetailAdjust); +} + + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +Chunker sTimeoutChunker; +ShapeBase::CollisionTimeout* ShapeBase::sFreeTimeoutList = 0; + + +//---------------------------------------------------------------------------- + +IMPLEMENT_CO_NETOBJECT_V1(ShapeBase); + +ConsoleDocClass( ShapeBase, + "@ingroup gameObjects" +); + +IMPLEMENT_CALLBACK( ShapeBase, validateCameraFov, F32, (F32 fov), (fov), + "@brief Called on the server when the client has requested a FOV change.\n\n" + + "When the client requests that its field of view should be changed (because " + "they want to use a sniper scope, for example) this new FOV needs to be validated " + "by the server. This method is called if it exists (it is optional) to validate " + "the requested FOV, and modify it if necessary. This could be as simple as checking " + "that the FOV falls within a correct range, to making sure that the FOV matches the " + "capabilities of the current weapon.\n\n" + + "Following this method, ShapeBase ensures that the given FOV still falls within " + "the datablock's cameraMinFov and cameraMaxFov. If that is good enough for your " + "purposes, then you do not need to define the validateCameraFov() callback for " + "your ShapeBase.\n\n" + + "@param fov The FOV that has been requested by the client.\n" + "@return The FOV as validated by the server.\n\n" + + "@see ShapeBaseData\n\n"); + +ShapeBase::ShapeBase() + : mDrag( 0.0f ), + mBuoyancy( 0.0f ), + mWaterCoverage( 0.0f ), + mLiquidHeight( 0.0f ), + mControllingObject( NULL ), + mGravityMod( 1.0f ), + mAppliedForce( Point3F::Zero ), + mTimeoutList( NULL ), + mDataBlock( NULL ), + mShapeInstance( NULL ), + mEnergy( 0.0f ), + mRechargeRate( 0.0f ), + mDamage( 0.0f ), + mRepairRate( 0.0f ), + mRepairReserve( 0.0f ), + mDamageState( Enabled ), + mDamageThread( NULL ), + mHulkThread( NULL ), + mLastRenderFrame( 0 ), + mLastRenderDistance( 0.0f ), + mCloaked( false ), + mCloakLevel( 0.0f ), + mDamageFlash( 0.0f ), + mWhiteOut( 0.0f ), + mInvincibleEffect( 0.0f ), + mInvincibleDelta( 0.0f ), + mInvincibleCount( 0.0f ), + mInvincibleSpeed( 0.0f ), + mInvincibleTime( 0.0f ), + mInvincibleFade( 0.1f ), + mInvincibleOn( false ), + mIsControlled( false ), + mConvexList( new Convex ), + mCameraFov( 90.0f ), + mShieldNormal( 0.0f, 0.0f, 1.0f ), + mFadeOut( true ), + mFading( false ), + mFadeVal( 1.0f ), + mFadeTime( 1.0f ), + mFadeElapsedTime( 0.0f ), + mFadeDelay( 0.0f ), + mFlipFadeVal( false ), + damageDir( 0.0f, 0.0f, 1.0f ), + mShapeBaseMount( NULL ), + mMass( 1.0f ), + mOneOverMass( 1.0f ), + mMoveMotion( false ), + mIsAiControlled( false ) +{ + mTypeMask |= ShapeBaseObjectType | LightObjectType; + + S32 i; + + for (i = 0; i < MaxSoundThreads; i++) { + mSoundThread[i].play = false; + mSoundThread[i].profile = 0; + mSoundThread[i].sound = 0; + } + + for (i = 0; i < MaxScriptThreads; i++) { + mScriptThread[i].sequence = -1; + mScriptThread[i].thread = 0; + mScriptThread[i].sound = 0; + mScriptThread[i].state = Thread::Stop; + mScriptThread[i].atEnd = false; + mScriptThread[i].timescale = 1.f; + mScriptThread[i].position = -1.f; + } + + for (i = 0; i < MaxTriggerKeys; i++) + mTrigger[i] = false; + + mWeaponCamShake = NULL; +} + + +ShapeBase::~ShapeBase() +{ + SAFE_DELETE( mConvexList ); + + AssertFatal(mMount.link == 0,"ShapeBase::~ShapeBase: An object is still mounted"); + if( mShapeInstance && (mShapeInstance->getDebrisRefCount() == 0) ) + { + delete mShapeInstance; + } + + CollisionTimeout* ptr = mTimeoutList; + while (ptr) { + CollisionTimeout* cur = ptr; + ptr = ptr->next; + cur->next = sFreeTimeoutList; + sFreeTimeoutList = cur; + } +} + +void ShapeBase::initPersistFields() +{ + addProtectedField( "skin", TypeRealString, Offset(mAppliedSkinName, ShapeBase), &_setFieldSkin, &_getFieldSkin, + "@brief The skin applied to the shape.\n\n" + + "'Skinning' the shape effectively renames the material targets, allowing " + "different materials to be used on different instances of the same model. " + "Using getSkinName() and setSkinName() is equivalent to reading and " + "writing the skin field directly.\n\n" + + "Any material targets that start with the old skin name have that part " + "of the name replaced with the new skin name. The initial old skin name is " + "\"base\". For example, if a new skin of \"blue\" was applied to a model " + "that had material targets base_body and face, the new targets " + "would be blue_body and face. Note that face was not " + "renamed since it did not start with the old skin name of \"base\".\n\n" + + "To support models that do not use the default \"base\" naming convention, " + "you can also specify the part of the name to replace in the skin field " + "itself. For example, if a model had a material target called shapemat, " + "we could apply a new skin \"shape=blue\", and the material target would be " + "renamed to bluemat (note \"shape\" has been replaced with \"blue\").\n\n" + + "Multiple skin updates can also be applied at the same time by separating " + "them with a semicolon. For example: \"base=blue;face=happy_face\".\n\n" + + "Material targets are only renamed if an existing Material maps to that " + "name, or if there is a diffuse texture in the model folder with the same " + "name as the new target.\n\n" ); + + addField( "isAiControlled", TypeBool, Offset(mIsAiControlled, ShapeBase), + "@brief Is this object AI controlled.\n\n" + "If True then this object is considered AI controlled and not player controlled.\n" ); + + Parent::initPersistFields(); +} + +bool ShapeBase::_setFieldSkin( void *object, const char *index, const char *data ) +{ + ShapeBase* shape = static_cast( object ); + if ( shape ) + shape->setSkinName( data ); + return false; +} + +const char *ShapeBase::_getFieldSkin( void *object, const char *data ) +{ + ShapeBase* shape = static_cast( object ); + return shape ? shape->getSkinName() : ""; +} + +//---------------------------------------------------------------------------- + +bool ShapeBase::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + if( !mDataBlock ) + { + Con::errorf( "ShapeBase::onAdd - no datablock on shape %i:%s (%s)", + getId(), getClassName(), getName() ); + return false; + } + + // Resolve sounds that arrived in the initial update + S32 i; + for (i = 0; i < MaxSoundThreads; i++) + updateAudioState(mSoundThread[i]); + + for (i = 0; i < MaxScriptThreads; i++) + { + Thread& st = mScriptThread[i]; + if(st.thread) + updateThread(st); + } + +/* + if(mDataBlock->cloakTexName != StringTable->insert("")) + mCloakTexture = TextureHandle(mDataBlock->cloakTexName, MeshTexture, false); +*/ + + return true; +} + +void ShapeBase::onRemove() +{ + mConvexList->nukeList(); + + Parent::onRemove(); + + // Stop any running sounds on the client + if (isGhost()) + for (S32 i = 0; i < MaxSoundThreads; i++) + stopAudio(i); + + if ( isClientObject() ) + { + mCubeReflector.unregisterReflector(); + + if ( mWeaponCamShake ) + { + if ( mWeaponCamShake->isAdded ) + gCamFXMgr.removeFX( mWeaponCamShake ); + + SAFE_DELETE( mWeaponCamShake ); + } + } +} + + +void ShapeBase::onSceneRemove() +{ + mConvexList->nukeList(); + Parent::onSceneRemove(); +} + +bool ShapeBase::onNewDataBlock( GameBaseData *dptr, bool reload ) +{ + ShapeBaseData *prevDB = dynamic_cast( mDataBlock ); + + bool isInitialDataBlock = ( mDataBlock == 0 ); + + if ( Parent::onNewDataBlock( dptr, reload ) == false ) + return false; + + mDataBlock = dynamic_cast(dptr); + if (!mDataBlock) + return false; + + setMaskBits(DamageMask); + mDamageThread = 0; + mHulkThread = 0; + + // Even if loadShape succeeds, there may not actually be + // a shape assigned to this object. + if (bool(mDataBlock->mShape)) { + delete mShapeInstance; + mShapeInstance = new TSShapeInstance(mDataBlock->mShape, isClientObject()); + if (isClientObject()) + mShapeInstance->cloneMaterialList(); + + mObjBox = mDataBlock->mShape->bounds; + resetWorldBox(); + + // Set the initial mesh hidden state. + mMeshHidden.setSize( mDataBlock->mShape->objects.size() ); + mMeshHidden.clear(); + + // Initialize the threads + for (U32 i = 0; i < MaxScriptThreads; i++) { + Thread& st = mScriptThread[i]; + if (st.sequence != -1) { + // TG: Need to see about suppressing non-cyclic sounds + // if the sequences were activated before the object was + // ghosted. + // TG: Cyclic animations need to have a random pos if + // they were started before the object was ghosted. + + // If there was something running on the old shape, the thread + // needs to be reset. Otherwise we assume that it's been + // initialized either by the constructor or from the server. + bool reset = st.thread != 0; + st.thread = 0; + + // New datablock/shape may not actually HAVE this sequence. + // In that case stop playing it. + + AssertFatal( prevDB != NULL, "ShapeBase::onNewDataBlock - how did you have a sequence playing without a prior datablock?" ); + + const TSShape *prevShape = prevDB->mShape; + const TSShape::Sequence &prevSeq = prevShape->sequences[st.sequence]; + const String &prevSeqName = prevShape->names[prevSeq.nameIndex]; + + st.sequence = mDataBlock->mShape->findSequence( prevSeqName ); + + if ( st.sequence != -1 ) + { + setThreadSequence( i, st.sequence, reset ); + } + } + } + + if (mDataBlock->damageSequence != -1) { + mDamageThread = mShapeInstance->addThread(); + mShapeInstance->setSequence(mDamageThread, + mDataBlock->damageSequence,0); + } + if (mDataBlock->hulkSequence != -1) { + mHulkThread = mShapeInstance->addThread(); + mShapeInstance->setSequence(mHulkThread, + mDataBlock->hulkSequence,0); + } + + if( isGhost() ) + { + // Reapply the current skin + mAppliedSkinName = ""; + reSkin(); + } + } + + // + mEnergy = 0; + mDamage = 0; + mDamageState = Enabled; + mRepairReserve = 0; + updateMass(); + updateDamageLevel(); + updateDamageState(); + + mDrag = mDataBlock->drag; + mCameraFov = mDataBlock->cameraDefaultFov; + updateMass(); + + if( !isInitialDataBlock && mLightPlugin ) + mLightPlugin->reset(); + + if ( isClientObject() ) + { + mCubeReflector.unregisterReflector(); + + if ( mDataBlock->reflectorDesc ) + mCubeReflector.registerReflector( this, mDataBlock->reflectorDesc ); + } + + return true; +} + +void ShapeBase::onDeleteNotify( SimObject *obj ) +{ + if ( obj == mCurrentWaterObject ) + mCurrentWaterObject = NULL; + + Parent::onDeleteNotify( obj ); +} + +void ShapeBase::onImpact(SceneObject* obj, VectorF vec) +{ + if (!isGhost()) + mDataBlock->onImpact_callback( this, obj, vec, vec.len() ); +} + +void ShapeBase::onImpact(VectorF vec) +{ + if (!isGhost()) + mDataBlock->onImpact_callback( this, NULL, vec, vec.len() ); +} + + +//---------------------------------------------------------------------------- + +void ShapeBase::processTick(const Move* move) +{ + PROFILE_SCOPE( ShapeBase_ProcessTick ); + + // Energy management + if (mDamageState == Enabled && mDataBlock->inheritEnergyFromMount == false) { + F32 store = mEnergy; + mEnergy += mRechargeRate; + if (mEnergy > mDataBlock->maxEnergy) + mEnergy = mDataBlock->maxEnergy; + else + if (mEnergy < 0) + mEnergy = 0; + + // Virtual setEnergyLevel is used here by some derived classes to + // decide whether they really want to set the energy mask bit. + if (mEnergy != store) + setEnergyLevel(mEnergy); + } + + // Repair management + if (mDataBlock->isInvincible == false) + { + F32 store = mDamage; + mDamage -= mRepairRate; + mDamage = mClampF(mDamage, 0.f, mDataBlock->maxDamage); + + if (mRepairReserve > mDamage) + mRepairReserve = mDamage; + if (mRepairReserve > 0.0) + { + F32 rate = getMin(mDataBlock->repairRate, mRepairReserve); + mDamage -= rate; + mRepairReserve -= rate; + } + + if (store != mDamage) + { + updateDamageLevel(); + if (isServerObject()) { + setMaskBits(DamageMask); + mDataBlock->onDamage_callback( this, mDamage - store ); + } + } + } + + if (isServerObject()) { + // Server only... + advanceThreads(TickSec); + updateServerAudio(); + + // update wet state + setImageWetState(0, mWaterCoverage > 0.4); // more than 40 percent covered + + // update motion state + mMoveMotion = false; + if (move && (move->x || move->y || move->z)) + { + mMoveMotion = true; + } + for (int i = 0; i < MaxMountedImages; i++) + { + setImageMotionState(i, mMoveMotion); + } + + if(mFading) + { + F32 dt = TickMs / 1000.0f; + F32 newFadeET = mFadeElapsedTime + dt; + if(mFadeElapsedTime < mFadeDelay && newFadeET >= mFadeDelay) + setMaskBits(CloakMask); + mFadeElapsedTime = newFadeET; + if(mFadeElapsedTime > mFadeTime + mFadeDelay) + { + mFadeVal = F32(!mFadeOut); + mFading = false; + } + } + } + + // Advance images + if (isServerObject()) + { + for (int i = 0; i < MaxMountedImages; i++) + { + if (mMountedImageList[i].dataBlock) + updateImageState(i, TickSec); + } + } + + // Call script on trigger state changes + if (move && mDataBlock && isServerObject()) + { + for (S32 i = 0; i < MaxTriggerKeys; i++) + { + if (move->trigger[i] != mTrigger[i]) + { + mTrigger[i] = move->trigger[i]; + mDataBlock->onTrigger_callback( this, i, move->trigger[i] ); + } + } + } + + // Update the damage flash and the whiteout + // + if (mDamageFlash > 0.0) + { + mDamageFlash -= sDamageFlashDec; + if (mDamageFlash <= 0.0) + mDamageFlash = 0.0; + } + if (mWhiteOut > 0.0) + { + mWhiteOut -= sWhiteoutDec; + if (mWhiteOut <= 0.0) + mWhiteOut = 0.0; + } +} + +void ShapeBase::advanceTime(F32 dt) +{ + // On the client, the shape threads and images are + // advanced at framerate. + advanceThreads(dt); + updateAudioPos(); + for (int i = 0; i < MaxMountedImages; i++) + if (mMountedImageList[i].dataBlock) + { + updateImageState(i, dt); + updateImageAnimation(i, dt); + } + + // Cloaking + if (mCloaked && mCloakLevel != 1.0) { + if (sCloakSpeed <= 0.0f) + { + // Instantaneous + mCloakLevel = 1.0; + } + else + { + // Over time determined by sCloakSpeed + mCloakLevel += dt / sCloakSpeed; + if (mCloakLevel >= 1.0) + mCloakLevel = 1.0; + } + } else if (!mCloaked && mCloakLevel != 0.0) { + if (sCloakSpeed <= 0.0f) + { + // Instantaneous + mCloakLevel = 0.0; + } + else + { + // Over time determined by sCloakSpeed + mCloakLevel -= dt / sCloakSpeed; + if (mCloakLevel <= 0.0) + mCloakLevel = 0.0; + } + } + if(mInvincibleOn) + updateInvincibleEffect(dt); + + if(mFading) + { + mFadeElapsedTime += dt; + if(mFadeElapsedTime > mFadeTime) + { + mFadeVal = F32(!mFadeOut); + mFading = false; + } + else + { + mFadeVal = mFadeElapsedTime / mFadeTime; + if(mFadeOut) + mFadeVal = 1 - mFadeVal; + } + } +} + +void ShapeBase::setControllingClient( GameConnection* client ) +{ + if( isGhost() && gSFX3DWorld ) + { + if( gSFX3DWorld->getListener() == this && !client && getControllingClient() && getControllingClient()->isConnectionToServer() ) + { + // We are the current listener and are no longer a controller object on the + // connection, so clear our listener status. + + gSFX3DWorld->setListener( NULL ); + } + else if( client && client->isConnectionToServer() && !getControllingObject() ) + { + // We're on the local client and not controlled by another object, so make + // us the current SFX listener. + + gSFX3DWorld->setListener( this ); + } + } + + Parent::setControllingClient( client ); + + // Update all of the mounted images' shapes so they may + // optimize their animation threads. + for (U32 i=0; imMount.object == this) + mControllingObject->processAfter(this); + } + mControllingObject = obj; +} + +ShapeBase* ShapeBase::getControlObject() +{ + return 0; +} + +void ShapeBase::setControlObject(ShapeBase*) +{ +} + +bool ShapeBase::isFirstPerson() const +{ + // Always first person as far as the server is concerned. + if (!isGhost()) + return true; + + if (const GameConnection* con = getControllingClient()) + return con->getControlObject() == this && con->isFirstPerson(); + return false; +} + +bool ShapeBase::isValidCameraFov(F32 fov) +{ + return((fov >= mDataBlock->cameraMinFov) && (fov <= mDataBlock->cameraMaxFov)); +} + +void ShapeBase::setCameraFov(F32 fov) +{ + // On server allow for script side checking + if ( !isGhost() && isMethod( "validateCameraFov" ) ) + { + fov = validateCameraFov_callback( fov ); + } + + mCameraFov = mClampF(fov, mDataBlock->cameraMinFov, mDataBlock->cameraMaxFov); +} + +void ShapeBase::onCameraScopeQuery(NetConnection *cr, CameraScopeQuery * query) +{ + // update the camera query + query->camera = this; + + if(GameConnection * con = dynamic_cast(cr)) + { + // get the fov from the connection (in deg) + F32 fov; + if (con->getControlCameraFov(&fov)) + { + query->fov = mDegToRad(fov/2); + query->sinFov = mSin(query->fov); + query->cosFov = mCos(query->fov); + } + } + + // use eye rather than camera transform (good enough and faster) + MatrixF eyeTransform; + getEyeTransform(&eyeTransform); + eyeTransform.getColumn(3, &query->pos); + eyeTransform.getColumn(1, &query->orientation); + + // Get the visible distance. + if (getSceneManager() != NULL) + query->visibleDistance = getSceneManager()->getVisibleDistance(); + + Parent::onCameraScopeQuery( cr, query ); +} + + +//---------------------------------------------------------------------------- +F32 ShapeBase::getEnergyLevel() +{ + if ( mDataBlock->inheritEnergyFromMount && mShapeBaseMount ) + return mShapeBaseMount->getEnergyLevel(); + return mEnergy; +} + +F32 ShapeBase::getEnergyValue() +{ + if ( mDataBlock->inheritEnergyFromMount && mShapeBaseMount ) + { + F32 maxEnergy = mShapeBaseMount->mDataBlock->maxEnergy; + if ( maxEnergy > 0.f ) + return (mShapeBaseMount->getEnergyLevel() / maxEnergy); + } + else + { + F32 maxEnergy = mDataBlock->maxEnergy; + if ( maxEnergy > 0.f ) + return (mEnergy / mDataBlock->maxEnergy); + } + + return 0.0f; +} + +void ShapeBase::setEnergyLevel(F32 energy) +{ + if (mDataBlock->inheritEnergyFromMount == false || !mShapeBaseMount) { + if (mDamageState == Enabled) { + mEnergy = (energy > mDataBlock->maxEnergy)? + mDataBlock->maxEnergy: (energy < 0)? 0: energy; + } + } else { + // Pass the set onto whatever we're mounted to... + if ( mShapeBaseMount ) + { + mShapeBaseMount->setEnergyLevel(energy); + } + } +} + +void ShapeBase::setDamageLevel(F32 damage) +{ + if (!mDataBlock->isInvincible) { + F32 store = mDamage; + mDamage = mClampF(damage, 0.f, mDataBlock->maxDamage); + + if (store != mDamage) { + updateDamageLevel(); + if (isServerObject()) { + setMaskBits(DamageMask); + mDataBlock->onDamage_callback( this, mDamage - store ); + } + } + } +} + +void ShapeBase::updateContainer() +{ + PROFILE_SCOPE( ShapeBase_updateContainer ); + + // Update container drag and buoyancy properties + + // Set default values. + mDrag = mDataBlock->drag; + mBuoyancy = 0.0f; + mGravityMod = 1.0; + mAppliedForce.set(0,0,0); + + ContainerQueryInfo info; + info.box = getWorldBox(); + info.mass = getMass(); + + mContainer->findObjects(info.box, WaterObjectType|PhysicalZoneObjectType,findRouter,&info); + + mWaterCoverage = info.waterCoverage; + mLiquidType = info.liquidType; + mLiquidHeight = info.waterHeight; + setCurrentWaterObject( info.waterObject ); + + // This value might be useful as a datablock value, + // This is what allows the player to stand in shallow water (below this coverage) + // without jiggling from buoyancy + if (mWaterCoverage >= 0.25f) + { + // water viscosity is used as drag for in water. + // ShapeBaseData drag is used for drag outside of water. + // Combine these two components to calculate this ShapeBase object's + // current drag. + mDrag = ( info.waterCoverage * info.waterViscosity ) + + ( 1.0f - info.waterCoverage ) * mDataBlock->drag; + mBuoyancy = (info.waterDensity / mDataBlock->density) * info.waterCoverage; + } + + mAppliedForce = info.appliedForce; + mGravityMod = info.gravityScale; + + //Con::printf( "WaterCoverage: %f", mWaterCoverage ); + //Con::printf( "Drag: %f", mDrag ); +} + + +//---------------------------------------------------------------------------- + +void ShapeBase::applyRepair(F32 amount) +{ + // Repair increases the repair reserve + if (amount > 0 && ((mRepairReserve += amount) > mDamage)) + mRepairReserve = mDamage; +} + +void ShapeBase::applyDamage(F32 amount) +{ + if (amount > 0) + setDamageLevel(mDamage + amount); +} + +F32 ShapeBase::getDamageValue() +{ + // Return a 0-1 damage value. + return mDamage / mDataBlock->maxDamage; +} + +void ShapeBase::updateDamageLevel() +{ + if (mDamageThread) { + // mDamage is already 0-1 on the client + if (mDamage >= mDataBlock->destroyedLevel) { + if (getDamageState() == Destroyed) + mShapeInstance->setPos(mDamageThread, 0); + else + mShapeInstance->setPos(mDamageThread, 1); + } else { + mShapeInstance->setPos(mDamageThread, mDamage / mDataBlock->destroyedLevel); + } + } +} + + +//---------------------------------------------------------------------------- + +void ShapeBase::setDamageState(DamageState state) +{ + if (mDamageState == state) + return; + + bool invokeCallback = false; + const char* lastState = 0; + + if (!isGhost()) { + if (state != getDamageState()) + setMaskBits(DamageMask); + + lastState = getDamageStateName(); + switch (state) { + case Destroyed: { + if (mDamageState == Enabled) + { + setDamageState(Disabled); + + // It is possible that the setDamageState() call above has already + // upgraded our state to Destroyed. If that is the case, no need + // to continue. + if (mDamageState == state) + return; + } + invokeCallback = true; + break; + } + case Disabled: + if (mDamageState == Enabled) + invokeCallback = true; + break; + case Enabled: + invokeCallback = true; + break; + default: + AssertFatal(false, "Invalid damage state"); + break; + } + } + + mDamageState = state; + if (mDamageState != Enabled) { + mRepairReserve = 0; + mEnergy = 0; + } + if (invokeCallback) { + // Like to call the scripts after the state has been intialize. + // This should only end up being called on the server. + switch (state) { + case Destroyed: + mDataBlock->onDestroyed_callback( this, lastState ); + break; + case Disabled: + mDataBlock->onDisabled_callback( this, lastState ); + break; + case Enabled: + mDataBlock->onEnabled_callback( this, lastState ); + break; + default: + break; + } + } + updateDamageState(); + updateDamageLevel(); +} + +bool ShapeBase::setDamageState(const char* state) +{ + for (S32 i = 0; i < NumDamageStates; i++) + if (!dStricmp(state,sDamageStateName[i])) { + setDamageState(DamageState(i)); + return true; + } + return false; +} + +const char* ShapeBase::getDamageStateName() +{ + return sDamageStateName[mDamageState]; +} + +void ShapeBase::updateDamageState() +{ + if (mHulkThread) { + F32 pos = (mDamageState == Destroyed) ? 1.0f : 0.0f; + if (mShapeInstance->getPos(mHulkThread) != pos) { + mShapeInstance->setPos(mHulkThread,pos); + + if (isClientObject()) + mShapeInstance->animate(); + } + } +} + + +//---------------------------------------------------------------------------- + +void ShapeBase::blowUp() +{ + Point3F center; + mObjBox.getCenter(¢er); + center += getPosition(); + MatrixF trans = getTransform(); + trans.setPosition( center ); + + // explode + Explosion* pExplosion = NULL; + + if( pointInWater( (Point3F &)center ) && mDataBlock->underwaterExplosion ) + { + pExplosion = new Explosion; + pExplosion->onNewDataBlock(mDataBlock->underwaterExplosion, false); + } + else + { + if (mDataBlock->explosion) + { + pExplosion = new Explosion; + pExplosion->onNewDataBlock(mDataBlock->explosion, false); + } + } + + if( pExplosion ) + { + pExplosion->setTransform(trans); + pExplosion->setInitialState(center, damageDir); + if (pExplosion->registerObject() == false) + { + Con::errorf(ConsoleLogEntry::General, "ShapeBase(%s)::explode: couldn't register explosion", + mDataBlock->getName() ); + delete pExplosion; + pExplosion = NULL; + } + } + + TSShapeInstance *debShape = NULL; + + if( mDataBlock->debrisShape == NULL ) + { + return; + } + else + { + debShape = new TSShapeInstance( mDataBlock->debrisShape, true); + } + + + Vector< TSPartInstance * > partList; + TSPartInstance::breakShape( debShape, 0, partList, NULL, NULL, 0 ); + + if( !mDataBlock->debris ) + { + mDataBlock->debris = new DebrisData; + } + + // cycle through partlist and create debris pieces + for( U32 i=0; isetPartInstance( partList[i] ); + debris->init( center, randomDir ); + debris->onNewDataBlock( mDataBlock->debris, false ); + debris->setTransform( trans ); + + if( !debris->registerObject() ) + { + Con::warnf( ConsoleLogEntry::General, "Could not register debris for class: %s", mDataBlock->getName() ); + delete debris; + debris = NULL; + } + else + { + debShape->incDebrisRefCount(); + } + } + + damageDir.set(0, 0, 1); +} + +//---------------------------------------------------------------------------- + +void ShapeBase::onMount( SceneObject *obj, S32 node ) +{ + mConvexList->nukeList(); + + // Are we mounting to a ShapeBase object? + mShapeBaseMount = dynamic_cast( obj ); + + Parent::onMount( obj, node ); +} + +void ShapeBase::onUnmount( SceneObject *obj, S32 node ) +{ + Parent::onUnmount( obj, node ); + + mShapeBaseMount = NULL; +} + +Point3F ShapeBase::getAIRepairPoint() +{ + if (mDataBlock->mountPointNode[ShapeBaseData::AIRepairNode] < 0) + return Point3F(0, 0, 0); + MatrixF xf(true); + getMountTransform( ShapeBaseData::AIRepairNode, MatrixF::Identity, &xf ); + Point3F pos(0, 0, 0); + xf.getColumn(3,&pos); + return pos; +} + +//---------------------------------------------------------------------------- + +void ShapeBase::getEyeTransform(MatrixF* mat) +{ + getEyeBaseTransform(mat); +} + +void ShapeBase::getEyeBaseTransform(MatrixF* mat) +{ + // Returns eye to world space transform + S32 eyeNode = mDataBlock->eyeNode; + if (eyeNode != -1) + mat->mul(getTransform(), mShapeInstance->mNodeTransforms[eyeNode]); + else + *mat = getTransform(); +} + +void ShapeBase::getRenderEyeTransform(MatrixF* mat) +{ + getRenderEyeBaseTransform(mat); +} + +void ShapeBase::getRenderEyeBaseTransform(MatrixF* mat) +{ + // Returns eye to world space transform + S32 eyeNode = mDataBlock->eyeNode; + if (eyeNode != -1) + mat->mul(getRenderTransform(), mShapeInstance->mNodeTransforms[eyeNode]); + else + *mat = getRenderTransform(); +} + +void ShapeBase::getCameraTransform(F32* pos,MatrixF* mat) +{ + // Returns camera to world space transform + // Handles first person / third person camera position + + if (isServerObject() && mShapeInstance) + mShapeInstance->animateNodeSubtrees(true); + + if (*pos != 0) + { + F32 min,max; + Point3F offset; + MatrixF eye,rot; + getCameraParameters(&min,&max,&offset,&rot); + getRenderEyeTransform(&eye); + mat->mul(eye,rot); + + // Use the eye transform to orient the camera + VectorF vp,vec; + vp.x = vp.z = 0; + vp.y = -(max - min) * *pos; + eye.mulV(vp,&vec); + + VectorF minVec; + vp.y = -min; + eye.mulV( vp, &minVec ); + + // Use the camera node's pos. + Point3F osp,sp; + if (mDataBlock->cameraNode != -1) { + mShapeInstance->mNodeTransforms[mDataBlock->cameraNode].getColumn(3,&osp); + + // Scale the camera position before applying the transform + const Point3F& scale = getScale(); + osp.convolve( scale ); + + getRenderTransform().mulP(osp,&sp); + } + else + getRenderTransform().getColumn(3,&sp); + + // Make sure we don't extend the camera into anything solid + Point3F ep = sp + minVec + vec + offset; + disableCollision(); + if (isMounted()) + getObjectMount()->disableCollision(); + RayInfo collision; + if( mContainer && mContainer->castRay(sp, ep, + (0xFFFFFFFF & ~(WaterObjectType | + GameBaseObjectType | + TriggerObjectType | + DefaultObjectType)), + &collision) == true) { + F32 vecLenSq = vec.lenSquared(); + F32 adj = (-mDot(vec, collision.normal) / vecLenSq) * 0.1; + F32 newPos = getMax(0.0f, collision.t - adj); + if (newPos == 0.0f) + eye.getColumn(3,&ep); + else + ep = sp + offset + (vec * newPos); + } + mat->setColumn(3,ep); + if (isMounted()) + getObjectMount()->enableCollision(); + enableCollision(); + } + else + { + getRenderEyeTransform(mat); + } + + // Apply Camera FX. + mat->mul( gCamFXMgr.getTrans() ); +} + +// void ShapeBase::getCameraTransform(F32* pos,MatrixF* mat) +// { +// // Returns camera to world space transform +// // Handles first person / third person camera position + +// if (isServerObject() && mShapeInstance) +// mShapeInstance->animateNodeSubtrees(true); + +// if (*pos != 0) { +// F32 min,max; +// Point3F offset; +// MatrixF eye,rot; +// getCameraParameters(&min,&max,&offset,&rot); +// getRenderEyeTransform(&eye); +// mat->mul(eye,rot); + +// // Use the eye transform to orient the camera +// VectorF vp,vec; +// vp.x = vp.z = 0; +// vp.y = -(max - min) * *pos; +// eye.mulV(vp,&vec); + +// // Use the camera node's pos. +// Point3F osp,sp; +// if (mDataBlock->cameraNode != -1) { +// mShapeInstance->mNodeTransforms[mDataBlock->cameraNode].getColumn(3,&osp); +// getRenderTransform().mulP(osp,&sp); +// } +// else +// getRenderTransform().getColumn(3,&sp); + +// // Make sure we don't extend the camera into anything solid +// Point3F ep = sp + vec; +// ep += offset; +// disableCollision(); +// if (isMounted()) +// getObjectMount()->disableCollision(); +// RayInfo collision; +// if (mContainer->castRay(sp,ep,(0xFFFFFFFF & ~(WaterObjectType|ForceFieldObjectType|GameBaseObjectType|DefaultObjectType)),&collision)) { +// *pos = collision.t *= 0.9; +// if (*pos == 0) +// eye.getColumn(3,&ep); +// else +// ep = sp + vec * *pos; +// } +// mat->setColumn(3,ep); +// if (isMounted()) +// getObjectMount()->enableCollision(); +// enableCollision(); +// } +// else +// { +// getRenderEyeTransform(mat); +// } +// } + + +// void ShapeBase::getRenderCameraTransform(F32* pos,MatrixF* mat) +// { +// // Returns camera to world space transform +// // Handles first person / third person camera position + +// if (isServerObject() && mShapeInstance) +// mShapeInstance->animateNodeSubtrees(true); + +// if (*pos != 0) { +// F32 min,max; +// Point3F offset; +// MatrixF eye,rot; +// getCameraParameters(&min,&max,&offset,&rot); +// getRenderEyeTransform(&eye); +// mat->mul(eye,rot); + +// // Use the eye transform to orient the camera +// VectorF vp,vec; +// vp.x = vp.z = 0; +// vp.y = -(max - min) * *pos; +// eye.mulV(vp,&vec); + +// // Use the camera node's pos. +// Point3F osp,sp; +// if (mDataBlock->cameraNode != -1) { +// mShapeInstance->mNodeTransforms[mDataBlock->cameraNode].getColumn(3,&osp); +// getRenderTransform().mulP(osp,&sp); +// } +// else +// getRenderTransform().getColumn(3,&sp); + +// // Make sure we don't extend the camera into anything solid +// Point3F ep = sp + vec; +// ep += offset; +// disableCollision(); +// if (isMounted()) +// getObjectMount()->disableCollision(); +// RayInfo collision; +// if (mContainer->castRay(sp,ep,(0xFFFFFFFF & ~(WaterObjectType|ForceFieldObjectType|GameBaseObjectType|DefaultObjectType)),&collision)) { +// *pos = collision.t *= 0.9; +// if (*pos == 0) +// eye.getColumn(3,&ep); +// else +// ep = sp + vec * *pos; +// } +// mat->setColumn(3,ep); +// if (isMounted()) +// getObjectMount()->enableCollision(); +// enableCollision(); +// } +// else +// { +// getRenderEyeTransform(mat); +// } +// } + +void ShapeBase::getCameraParameters(F32 *min,F32* max,Point3F* off,MatrixF* rot) +{ + *min = mDataBlock->cameraMinDist; + *max = mDataBlock->cameraMaxDist; + off->set(0,0,0); + rot->identity(); +} + + +//---------------------------------------------------------------------------- +F32 ShapeBase::getDamageFlash() const +{ + return mDamageFlash; +} + +void ShapeBase::setDamageFlash(const F32 flash) +{ + mDamageFlash = flash; + if (mDamageFlash < 0.0) + mDamageFlash = 0; + else if (mDamageFlash > 1.0) + mDamageFlash = 1.0; +} + + +//---------------------------------------------------------------------------- +F32 ShapeBase::getWhiteOut() const +{ + return mWhiteOut; +} + +void ShapeBase::setWhiteOut(const F32 flash) +{ + mWhiteOut = flash; + if (mWhiteOut < 0.0) + mWhiteOut = 0; + else if (mWhiteOut > 1.5) + mWhiteOut = 1.5; +} + + +//---------------------------------------------------------------------------- + +bool ShapeBase::onlyFirstPerson() const +{ + return mDataBlock->firstPersonOnly; +} + +bool ShapeBase::useObjsEyePoint() const +{ + return mDataBlock->useEyePoint; +} + + +//---------------------------------------------------------------------------- +F32 ShapeBase::getInvincibleEffect() const +{ + return mInvincibleEffect; +} + +void ShapeBase::setupInvincibleEffect(F32 time, F32 speed) +{ + if(isClientObject()) + { + mInvincibleCount = mInvincibleTime = time; + mInvincibleSpeed = mInvincibleDelta = speed; + mInvincibleEffect = 0.0f; + mInvincibleOn = true; + mInvincibleFade = 1.0f; + } + else + { + mInvincibleTime = time; + mInvincibleSpeed = speed; + setMaskBits(InvincibleMask); + } +} + +void ShapeBase::updateInvincibleEffect(F32 dt) +{ + if(mInvincibleCount > 0.0f ) + { + if(mInvincibleEffect >= ((0.3 * mInvincibleFade) + 0.05f) && mInvincibleDelta > 0.0f) + mInvincibleDelta = -mInvincibleSpeed; + else if(mInvincibleEffect <= 0.05f && mInvincibleDelta < 0.0f) + { + mInvincibleDelta = mInvincibleSpeed; + mInvincibleFade = mInvincibleCount / mInvincibleTime; + } + mInvincibleEffect += mInvincibleDelta; + mInvincibleCount -= dt; + } + else + { + mInvincibleEffect = 0.0f; + mInvincibleOn = false; + } +} + +//---------------------------------------------------------------------------- +void ShapeBase::setVelocity(const VectorF&) +{ +} + +void ShapeBase::applyImpulse(const Point3F&,const VectorF&) +{ +} + + +//---------------------------------------------------------------------------- + +void ShapeBase::playAudio(U32 slot,SFXTrack* profile) +{ + AssertFatal( slot < MaxSoundThreads, "ShapeBase::playAudio() bad slot index" ); + Sound& st = mSoundThread[slot]; + if( profile && ( !st.play || st.profile != profile ) ) + { + setMaskBits(SoundMaskN << slot); + st.play = true; + st.profile = profile; + updateAudioState(st); + } +} + +void ShapeBase::stopAudio(U32 slot) +{ + AssertFatal( slot < MaxSoundThreads, "ShapeBase::stopAudio() bad slot index" ); + + Sound& st = mSoundThread[slot]; + if ( st.play ) + { + st.play = false; + setMaskBits(SoundMaskN << slot); + updateAudioState(st); + } +} + +void ShapeBase::updateServerAudio() +{ + // Timeout non-looping sounds + for (int i = 0; i < MaxSoundThreads; i++) { + Sound& st = mSoundThread[i]; + if (st.play && st.timeout && st.timeout < Sim::getCurrentTime()) { + clearMaskBits(SoundMaskN << i); + st.play = false; + } + } +} + +void ShapeBase::updateAudioState(Sound& st) +{ + SFX_DELETE( st.sound ); + + if ( st.play && st.profile ) + { + if ( isGhost() ) + { + if ( Sim::findObject( SimObjectId( st.profile ), st.profile ) ) + { + st.sound = SFX->createSource( st.profile, &getTransform() ); + if ( st.sound ) + st.sound->play(); + } + else + st.play = false; + } + else + { + // Non-looping sounds timeout on the server + st.timeout = 0; + if ( !st.profile->getDescription()->mIsLooping ) + st.timeout = Sim::getCurrentTime() + sAudioTimeout; + } + } + else + st.play = false; +} + +void ShapeBase::updateAudioPos() +{ + for (int i = 0; i < MaxSoundThreads; i++) + { + SFXSource* source = mSoundThread[i].sound; + if ( source ) + source->setTransform( getTransform() ); + } +} + +//---------------------------------------------------------------------------- + +const char *ShapeBase::getThreadSequenceName( U32 slot ) +{ + Thread& st = mScriptThread[slot]; + if ( st.sequence == -1 ) + { + // Invalid Animation. + return ""; + } + + // Name Index + const U32 nameIndex = getShape()->sequences[st.sequence].nameIndex; + + // Return Name. + return getShape()->getName( nameIndex ); +} + +bool ShapeBase::setThreadSequence(U32 slot, S32 seq, bool reset) +{ + Thread& st = mScriptThread[slot]; + if (st.thread && st.sequence == seq && st.state == Thread::Play) + return true; + + // Handle a -1 sequence, as this may be set when a thread has been destroyed. + if(seq == -1) + return true; + + if (seq < MaxSequenceIndex) { + setMaskBits(ThreadMaskN << slot); + st.sequence = seq; + if (reset) { + st.state = Thread::Play; + st.atEnd = false; + st.timescale = 1.f; + st.position = 0.f; + } + if (mShapeInstance) { + if (!st.thread) + st.thread = mShapeInstance->addThread(); + mShapeInstance->setSequence(st.thread,seq,0); + stopThreadSound(st); + updateThread(st); + } + return true; + } + return false; +} + +void ShapeBase::updateThread(Thread& st) +{ + switch (st.state) + { + case Thread::Stop: + { + mShapeInstance->setTimeScale( st.thread, 1.f ); + mShapeInstance->setPos( st.thread, ( st.timescale > 0.f ) ? 0.0f : 1.0f ); + } // Drop through to pause state + + case Thread::Pause: + { + if ( st.position != -1.f ) + { + mShapeInstance->setTimeScale( st.thread, 1.f ); + mShapeInstance->setPos( st.thread, st.position ); + } + + mShapeInstance->setTimeScale( st.thread, 0.f ); + stopThreadSound( st ); + } break; + + case Thread::Play: + { + if (st.atEnd) + { + mShapeInstance->setTimeScale(st.thread,1); + mShapeInstance->setPos( st.thread, ( st.timescale > 0.f ) ? 1.0f : 0.0f ); + mShapeInstance->setTimeScale(st.thread,0); + stopThreadSound(st); + st.state = Thread::Stop; + } + else + { + if ( st.position != -1.f ) + { + mShapeInstance->setTimeScale( st.thread, 1.f ); + mShapeInstance->setPos( st.thread, st.position ); + } + + mShapeInstance->setTimeScale(st.thread, st.timescale ); + if (!st.sound) + { + startSequenceSound(st); + } + } + } break; + + case Thread::Destroy: + { + stopThreadSound(st); + st.atEnd = true; + st.sequence = -1; + if(st.thread) + { + mShapeInstance->destroyThread(st.thread); + st.thread = 0; + } + } break; + } +} + +bool ShapeBase::stopThread(U32 slot) +{ + Thread& st = mScriptThread[slot]; + if (st.sequence != -1 && st.state != Thread::Stop) { + setMaskBits(ThreadMaskN << slot); + st.state = Thread::Stop; + updateThread(st); + return true; + } + return false; +} + +bool ShapeBase::destroyThread(U32 slot) +{ + Thread& st = mScriptThread[slot]; + if (st.sequence != -1 && st.state != Thread::Destroy) { + setMaskBits(ThreadMaskN << slot); + st.state = Thread::Destroy; + updateThread(st); + return true; + } + return false; +} + +bool ShapeBase::pauseThread(U32 slot) +{ + Thread& st = mScriptThread[slot]; + if (st.sequence != -1 && st.state != Thread::Pause) { + setMaskBits(ThreadMaskN << slot); + st.state = Thread::Pause; + updateThread(st); + return true; + } + return false; +} + +bool ShapeBase::playThread(U32 slot) +{ + Thread& st = mScriptThread[slot]; + if (st.sequence != -1 && st.state != Thread::Play) { + setMaskBits(ThreadMaskN << slot); + st.state = Thread::Play; + updateThread(st); + return true; + } + return false; +} + +bool ShapeBase::setThreadPosition( U32 slot, F32 pos ) +{ + Thread& st = mScriptThread[slot]; + if (st.sequence != -1) + { + setMaskBits(ThreadMaskN << slot); + st.position = pos; + st.atEnd = false; + updateThread(st); + + return true; + } + return false; +} + +bool ShapeBase::setThreadDir(U32 slot,bool forward) +{ + Thread& st = mScriptThread[slot]; + if (st.sequence != -1) + { + if ( ( st.timescale >= 0.f ) != forward ) + { + setMaskBits(ThreadMaskN << slot); + st.timescale *= -1.f ; + st.atEnd = false; + updateThread(st); + } + return true; + } + return false; +} + +bool ShapeBase::setThreadTimeScale( U32 slot, F32 timeScale ) +{ + Thread& st = mScriptThread[slot]; + if (st.sequence != -1) + { + if (st.timescale != timeScale) + { + setMaskBits(ThreadMaskN << slot); + st.timescale = timeScale; + updateThread(st); + } + return true; + } + return false; +} + +void ShapeBase::stopThreadSound(Thread& thread) +{ + if (thread.sound) { + } +} + +void ShapeBase::startSequenceSound(Thread& thread) +{ + if (!isGhost() || !thread.thread) + return; + stopThreadSound(thread); +} + +void ShapeBase::advanceThreads(F32 dt) +{ + for (U32 i = 0; i < MaxScriptThreads; i++) { + Thread& st = mScriptThread[i]; + if (st.thread) { + if (!mShapeInstance->getShape()->sequences[st.sequence].isCyclic() && !st.atEnd && + ( ( st.timescale > 0.f )? mShapeInstance->getPos(st.thread) >= 1.0: + mShapeInstance->getPos(st.thread) <= 0)) { + st.atEnd = true; + updateThread(st); + if (!isGhost()) { + mDataBlock->onEndSequence_callback( this, i ); + } + } + + // Make sure the thread is still valid after the call to onEndSequence_callback(). + // Someone could have called destroyThread() while in there. + if(st.thread) + { + mShapeInstance->advanceTime(dt,st.thread); + } + } + } +} + +//---------------------------------------------------------------------------- + +/// Emit particles on the given emitter, if we're within triggerHeight above some static surface with a +/// material that has 'showDust' set to true. The particles will have a lifetime of 'numMilliseconds' +/// and be emitted at the given offset from the contact point having a direction of 'axis'. + +void ShapeBase::emitDust( ParticleEmitter* emitter, F32 triggerHeight, const Point3F& offset, U32 numMilliseconds, const Point3F& axis ) +{ + if( !emitter ) + return; + + Point3F startPos = getPosition(); + Point3F endPos = startPos + Point3F( 0.0, 0.0, - triggerHeight ); + + RayInfo rayInfo; + if( getContainer()->castRay( startPos, endPos, STATIC_COLLISION_TYPEMASK, &rayInfo ) ) + { + Material* material = ( rayInfo.material ? dynamic_cast< Material* >( rayInfo.material->getMaterial() ) : 0 ); + if( material && material->mShowDust ) + { + ColorF colorList[ ParticleData::PDC_NUM_KEYS ]; + + for( U32 x = 0; x < getMin( Material::NUM_EFFECT_COLOR_STAGES, ParticleData::PDC_NUM_KEYS ); ++ x ) + colorList[ x ] = material->mEffectColor[ x ]; + for( U32 x = Material::NUM_EFFECT_COLOR_STAGES; x < ParticleData::PDC_NUM_KEYS; ++ x ) + colorList[ x ].set( 1.0, 1.0, 1.0, 0.0 ); + + emitter->setColors( colorList ); + + Point3F contactPoint = rayInfo.point + offset; + emitter->emitParticles( contactPoint, true, ( axis == Point3F::Zero ? rayInfo.normal : axis ), + getVelocity(), numMilliseconds ); + } + } +} + +//---------------------------------------------------------------------------- + +TSShape const* ShapeBase::getShape() +{ + return mShapeInstance? mShapeInstance->getShape(): 0; +} + +void ShapeBase::prepRenderImage( SceneRenderState *state ) +{ + _prepRenderImage( state, true, true ); +} + +void ShapeBase::_prepRenderImage( SceneRenderState *state, + bool renderSelf, + bool renderMountedImages ) +{ + PROFILE_SCOPE( ShapeBase_PrepRenderImage ); + + //if ( mIsCubemapUpdate ) + // return false; + + if( ( getDamageState() == Destroyed ) && ( !mDataBlock->renderWhenDestroyed ) ) + return; + + // We don't need to render if all the meshes are forced hidden. + if ( mMeshHidden.testAll() ) + return; + + // If we're rendering shadows don't render the mounted + // images unless the shape is also rendered. + if ( state->isShadowPass() && !renderSelf ) + return; + + // If we're currently rendering our own reflection we + // don't want to render ourselves into it. + if ( mCubeReflector.isRendering() ) + return; + + // We force all the shapes to use the highest detail + // if we're the control object or mounted. + bool forceHighestDetail = false; + { + GameConnection *con = GameConnection::getConnectionToServer(); + ShapeBase *co = NULL; + if(con && ( (co = dynamic_cast(con->getControlObject())) != NULL) ) + { + if(co == this || co->getObjectMount() == this) + forceHighestDetail = true; + } + } + + mLastRenderFrame = sLastRenderFrame; + + // get shape detail...we might not even need to be drawn + Point3F cameraOffset = getWorldBox().getClosestPoint( state->getDiffuseCameraPosition() ) - state->getDiffuseCameraPosition(); + F32 dist = cameraOffset.len(); + if (dist < 0.01f) + dist = 0.01f; + + F32 invScale = (1.0f/getMax(getMax(mObjScale.x,mObjScale.y),mObjScale.z)); + + if (mShapeInstance) + { + if ( forceHighestDetail ) + mShapeInstance->setCurrentDetail( 0 ); + else + mShapeInstance->setDetailFromDistance( state, dist * invScale ); + + mShapeInstance->animate(); + } + + if ( ( mShapeInstance && mShapeInstance->getCurrentDetail() < 0 ) || + ( !mShapeInstance && !gShowBoundingBox ) ) + { + // no, don't draw anything + return; + } + + if( renderMountedImages ) + { + for (U32 i = 0; i < MaxMountedImages; i++) + { + MountedImage& image = mMountedImageList[i]; + U32 imageShapeIndex = getImageShapeIndex(image); + if (image.dataBlock && image.shapeInstance[imageShapeIndex]) + { + // Select detail levels on mounted items but... always + // draw the control object's mounted images in high detail. + if ( forceHighestDetail ) + image.shapeInstance[imageShapeIndex]->setCurrentDetail( 0 ); + else + image.shapeInstance[imageShapeIndex]->setDetailFromDistance( state, dist * invScale ); + + if (!mIsZero( (1.0f - mCloakLevel) * mFadeVal)) + { + prepBatchRender( state, i ); + + // Debug rendering of the mounted shape bounds. + if ( gShowBoundingBox ) + { + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &ShapeBase::_renderBoundingBox ); + ri->objectIndex = i; + ri->type = RenderPassManager::RIT_Editor; + state->getRenderPass()->addInst( ri ); + } + } + } + } + } + + // Debug rendering of the shape bounding box. + if ( gShowBoundingBox ) + { + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &ShapeBase::_renderBoundingBox ); + ri->objectIndex = -1; + ri->type = RenderPassManager::RIT_Editor; + state->getRenderPass()->addInst( ri ); + } + + if ( mShapeInstance && renderSelf ) + prepBatchRender( state, -1 ); + + calcClassRenderData(); +} + +//---------------------------------------------------------------------------- +// prepBatchRender +//---------------------------------------------------------------------------- +void ShapeBase::prepBatchRender(SceneRenderState* state, S32 mountedImageIndex ) +{ + // CHANGES IN HERE SHOULD BE DUPLICATED IN TSSTATIC! + + GFXTransformSaver saver; + + // Set up our TS render state. + TSRenderState rdata; + rdata.setSceneState( state ); + if ( mCubeReflector.isEnabled() ) + rdata.setCubemap( mCubeReflector.getCubemap() ); + rdata.setFadeOverride( (1.0f - mCloakLevel) * mFadeVal ); + + // We might have some forward lit materials + // so pass down a query to gather lights. + LightQuery query; + query.init( getWorldSphere() ); + rdata.setLightQuery( &query ); + + if( mountedImageIndex != -1 ) + { + MountedImage& image = mMountedImageList[mountedImageIndex]; + + if( image.dataBlock && image.shapeInstance ) + { + renderMountedImage( mountedImageIndex, rdata, state ); + } + } + else + { + MatrixF mat = getRenderTransform(); + mat.scale( mObjScale ); + GFX->setWorldMatrix( mat ); + + if ( state->isDiffusePass() && mCubeReflector.isEnabled() && mCubeReflector.getOcclusionQuery() ) + { + RenderPassManager *pass = state->getRenderPass(); + + OccluderRenderInst *ri = pass->allocInst(); + + ri->type = RenderPassManager::RIT_Occluder; + ri->query = mCubeReflector.getOcclusionQuery(); + mObjToWorld.mulP( mObjBox.getCenter(), &ri->position ); + ri->scale.set( mObjBox.getExtents() ); + ri->orientation = pass->allocUniqueXform( mObjToWorld ); + ri->isSphere = false; + state->getRenderPass()->addInst( ri ); + } + + mShapeInstance->animate(); + mShapeInstance->render( rdata ); + } +} + +void ShapeBase::renderMountedImage( U32 imageSlot, TSRenderState &rstate, SceneRenderState *state ) +{ + GFX->pushWorldMatrix(); + + MatrixF mat; + getRenderImageTransform(imageSlot, &mat, rstate.getSceneState()->isShadowPass()); + GFX->setWorldMatrix( mat ); + + MountedImage& image = mMountedImageList[imageSlot]; + U32 imageShapeIndex = getImageShapeIndex(image); + image.shapeInstance[imageShapeIndex]->animate(); + image.shapeInstance[imageShapeIndex]->render( rstate ); + + GFX->popWorldMatrix(); +} + +void ShapeBase::_renderBoundingBox( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ) +{ + // If we got an override mat then this some + // special rendering pass... skip out of it. + if ( overrideMat ) + return; + + GFXStateBlockDesc desc; + desc.setZReadWrite( true, false ); + desc.setBlend( true ); + desc.fillMode = GFXFillWireframe; + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + + if ( ri->objectIndex != -1 ) + { + MountedImage &image = mMountedImageList[ ri->objectIndex ]; + + if ( image.shapeInstance ) + { + MatrixF mat; + getRenderImageTransform( ri->objectIndex, &mat ); + + const Box3F &objBox = image.shapeInstance[getImageShapeIndex(image)]->getShape()->bounds; + + drawer->drawCube( desc, objBox, ColorI( 255, 255, 255 ), &mat ); + } + } + else + drawer->drawCube( desc, mObjBox, ColorI( 255, 255, 255 ), &mRenderObjToWorld ); +} + +bool ShapeBase::castRay(const Point3F &start, const Point3F &end, RayInfo* info) +{ + if (mShapeInstance) + { + RayInfo shortest; + shortest.t = 1e8; + + info->object = NULL; + for (U32 i = 0; i < mDataBlock->LOSDetails.size(); i++) + { + mShapeInstance->animate(mDataBlock->LOSDetails[i]); + if (mShapeInstance->castRay(start, end, info, mDataBlock->LOSDetails[i])) + { + info->object = this; + if (info->t < shortest.t) + shortest = *info; + } + } + + if (info->object == this) + { + // Copy out the shortest time... + *info = shortest; + return true; + } + } + + return false; +} + +bool ShapeBase::castRayRendered(const Point3F &start, const Point3F &end, RayInfo* info) +{ + if (mShapeInstance) + { + RayInfo localInfo; + mShapeInstance->animate(); + bool res = mShapeInstance->castRayRendered(start, end, &localInfo, mShapeInstance->getCurrentDetail()); + if (res) + { + *info = localInfo; + info->object = this; + return true; + } + } + + return false; +} + + +//---------------------------------------------------------------------------- + +bool ShapeBase::buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &, const SphereF &) +{ + if ( !mShapeInstance ) + return false; + + polyList->setTransform(&mObjToWorld, mObjScale); + polyList->setObject(this); + + if ( context == PLC_Selection ) + { + mShapeInstance->animate(); + mShapeInstance->buildPolyList(polyList,mShapeInstance->getCurrentDetail()); + return true; + } + else if ( context == PLC_Export ) + { + // Try to call on the client so we can export materials + ShapeBase* exportObj = this; + if ( isServerObject() && getClientObject() ) + exportObj = dynamic_cast(getClientObject()); + + S32 dl = 0; + exportObj->mShapeInstance->animate(); + exportObj->mShapeInstance->buildPolyList(polyList, dl); + return true; + } + else + { + bool ret = false; + for (U32 i = 0; i < mDataBlock->collisionDetails.size(); i++) + { + mShapeInstance->buildPolyList(polyList,mDataBlock->collisionDetails[i]); + ret = true; + } + + return ret; + } +} + +void ShapeBase::buildConvex(const Box3F& box, Convex* convex) +{ + if (mShapeInstance == NULL) + return; + + // These should really come out of a pool + mConvexList->collectGarbage(); + + Box3F realBox = box; + mWorldToObj.mul(realBox); + realBox.minExtents.convolveInverse(mObjScale); + realBox.maxExtents.convolveInverse(mObjScale); + + if (realBox.isOverlapped(getObjBox()) == false) + return; + + for (U32 i = 0; i < mDataBlock->collisionDetails.size(); i++) + { + Box3F newbox = mDataBlock->collisionBounds[i]; + newbox.minExtents.convolve(mObjScale); + newbox.maxExtents.convolve(mObjScale); + mObjToWorld.mul(newbox); + if (box.isOverlapped(newbox) == false) + continue; + + // See if this hull exists in the working set already... + Convex* cc = 0; + CollisionWorkingList& wl = convex->getWorkingList(); + for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) { + if (itr->mConvex->getType() == ShapeBaseConvexType && + (static_cast(itr->mConvex)->pShapeBase == this && + static_cast(itr->mConvex)->hullId == i)) { + cc = itr->mConvex; + break; + } + } + if (cc) + continue; + + // Create a new convex. + ShapeBaseConvex* cp = new ShapeBaseConvex; + mConvexList->registerObject(cp); + convex->addToWorkingList(cp); + cp->mObject = this; + cp->pShapeBase = this; + cp->hullId = i; + cp->box = mDataBlock->collisionBounds[i]; + cp->transform = 0; + cp->findNodeTransform(); + } +} + + +//---------------------------------------------------------------------------- + +void ShapeBase::queueCollision( SceneObject *obj, const VectorF &vec) +{ + // Add object to list of collisions. + SimTime time = Sim::getCurrentTime(); + S32 num = obj->getId(); + + CollisionTimeout** adr = &mTimeoutList; + CollisionTimeout* ptr = mTimeoutList; + while (ptr) { + if (ptr->objectNumber == num) { + if (ptr->expireTime < time) { + ptr->expireTime = time + CollisionTimeoutValue; + ptr->object = obj; + ptr->vector = vec; + } + return; + } + // Recover expired entries + if (ptr->expireTime < time) { + CollisionTimeout* cur = ptr; + *adr = ptr->next; + ptr = ptr->next; + cur->next = sFreeTimeoutList; + sFreeTimeoutList = cur; + } + else { + adr = &ptr->next; + ptr = ptr->next; + } + } + + // New entry for the object + if (sFreeTimeoutList != NULL) + { + ptr = sFreeTimeoutList; + sFreeTimeoutList = ptr->next; + ptr->next = NULL; + } + else + { + ptr = sTimeoutChunker.alloc(); + } + + ptr->object = obj; + ptr->objectNumber = obj->getId(); + ptr->vector = vec; + ptr->expireTime = time + CollisionTimeoutValue; + ptr->next = mTimeoutList; + + mTimeoutList = ptr; +} + +void ShapeBase::notifyCollision() +{ + // Notify all the objects that were just stamped during the queueing + // process. + SimTime expireTime = Sim::getCurrentTime() + CollisionTimeoutValue; + for (CollisionTimeout* ptr = mTimeoutList; ptr; ptr = ptr->next) + { + if (ptr->expireTime == expireTime && ptr->object) + { + SimObjectPtr safePtr(ptr->object); + SimObjectPtr safeThis(this); + onCollision(ptr->object,ptr->vector); + ptr->object = 0; + + if(!bool(safeThis)) + return; + + if(bool(safePtr)) + safePtr->onCollision(this,ptr->vector); + + if(!bool(safeThis)) + return; + } + } +} + +void ShapeBase::onCollision( SceneObject *object, const VectorF &vec ) +{ + if (!isGhost()) + mDataBlock->onCollision_callback( this, object, vec, vec.len() ); +} + +//-------------------------------------------------------------------------- +bool ShapeBase::pointInWater( Point3F &point ) +{ + if ( mCurrentWaterObject == NULL ) + return false; + + return mCurrentWaterObject->isUnderwater( point ); +} + +//---------------------------------------------------------------------------- + +void ShapeBase::writePacketData(GameConnection *connection, BitStream *stream) +{ + Parent::writePacketData(connection, stream); + + stream->write(getEnergyLevel()); + stream->write(mRechargeRate); +} + +void ShapeBase::readPacketData(GameConnection *connection, BitStream *stream) +{ + Parent::readPacketData(connection, stream); + + F32 energy; + stream->read(&energy); + setEnergyLevel(energy); + + stream->read(&mRechargeRate); +} + +F32 ShapeBase::getUpdatePriority(CameraScopeQuery *camInfo, U32 updateMask, S32 updateSkips) +{ + // If it's the scope object, must be high priority + if (camInfo->camera == this) { + // Most priorities are between 0 and 1, so this + // should be something larger. + return 10.0f; + } + if( camInfo->camera ) + { + ShapeBase* camera = dynamic_cast< ShapeBase* >( camInfo->camera ); + // see if the camera is mounted to this... + // if it is, this should have a high priority + if( camera && camera->getObjectMount() == this) + return 10.0f; + } + return Parent::getUpdatePriority(camInfo, updateMask, updateSkips); +} + +U32 ShapeBase::packUpdate(NetConnection *con, U32 mask, BitStream *stream) +{ + U32 retMask = Parent::packUpdate(con, mask, stream); + + if (mask & InitialUpdateMask) { + // mask off sounds that aren't playing + S32 i; + for (i = 0; i < MaxSoundThreads; i++) + if (!mSoundThread[i].play) + mask &= ~(SoundMaskN << i); + + // mask off threads that aren't running + for (i = 0; i < MaxScriptThreads; i++) + if (mScriptThread[i].sequence == -1) + mask &= ~(ThreadMaskN << i); + + // mask off images that aren't updated + for(i = 0; i < MaxMountedImages; i++) + if(!mMountedImageList[i].dataBlock) + mask &= ~(ImageMaskN << i); + } + + if(!stream->writeFlag(mask & (NameMask | DamageMask | SoundMask | MeshHiddenMask | + ThreadMask | ImageMask | CloakMask | InvincibleMask | + ShieldMask | SkinMask))) + return retMask; + + if (stream->writeFlag(mask & DamageMask)) { + stream->writeFloat(mClampF(mDamage / mDataBlock->maxDamage, 0.f, 1.f), DamageLevelBits); + stream->writeInt(mDamageState,NumDamageStateBits); + stream->writeNormalVector( damageDir, 8 ); + } + + if (stream->writeFlag(mask & ThreadMask)) { + for (int i = 0; i < MaxScriptThreads; i++) { + Thread& st = mScriptThread[i]; + if (stream->writeFlag( (st.sequence != -1 || st.state == Thread::Destroy) && (mask & (ThreadMaskN << i)) ) ) { + stream->writeInt(st.sequence,ThreadSequenceBits); + stream->writeInt(st.state,2); + stream->write(st.timescale); + stream->write(st.position); + stream->writeFlag(st.atEnd); + } + } + } + + if (stream->writeFlag(mask & SoundMask)) { + for (int i = 0; i < MaxSoundThreads; i++) { + Sound& st = mSoundThread[i]; + if (stream->writeFlag(mask & (SoundMaskN << i))) + if (stream->writeFlag(st.play)) + stream->writeRangedU32(st.profile->getId(),DataBlockObjectIdFirst, + DataBlockObjectIdLast); + } + } + + if (stream->writeFlag(mask & ImageMask)) { + for (int i = 0; i < MaxMountedImages; i++) + if (stream->writeFlag(mask & (ImageMaskN << i))) { + MountedImage& image = mMountedImageList[i]; + if (stream->writeFlag(image.dataBlock)) + stream->writeInt(image.dataBlock->getId() - DataBlockObjectIdFirst, + DataBlockObjectIdBitSize); + con->packNetStringHandleU(stream, image.skinNameHandle); + con->packNetStringHandleU(stream, image.scriptAnimPrefix); + + // Used to force the 1st person rendering on the client. This is required + // as this object could be ghosted to the client prior to its controlling client + // being set. Therefore there is a network tick when the object is in limbo... + stream->writeFlag(image.dataBlock && image.dataBlock->animateAllShapes && getControllingClient() == con); + + stream->writeFlag(image.wet); + stream->writeFlag(image.motion); + stream->writeFlag(image.ammo); + stream->writeFlag(image.loaded); + stream->writeFlag(image.target); + stream->writeFlag(image.triggerDown); + stream->writeFlag(image.altTriggerDown); + + for (U32 i=0; iwriteFlag(image.genericTrigger[i]); + } + + stream->writeInt(image.fireCount,3); + stream->writeInt(image.altFireCount,3); + stream->writeInt(image.reloadCount,3); + stream->writeFlag(isImageFiring(i)); + stream->writeFlag(isImageAltFiring(i)); + stream->writeFlag(isImageReloading(i)); + } + } + + // Group some of the uncommon stuff together. + if (stream->writeFlag(mask & (NameMask | ShieldMask | CloakMask | InvincibleMask | SkinMask | MeshHiddenMask ))) { + + if (stream->writeFlag(mask & CloakMask)) + { + // cloaking + stream->writeFlag( mCloaked ); + + // piggyback control update + stream->writeFlag(bool(getControllingClient())); + + // fading + if(stream->writeFlag(mFading && mFadeElapsedTime >= mFadeDelay)) { + stream->writeFlag(mFadeOut); + stream->write(mFadeTime); + } + else + stream->writeFlag(mFadeVal == 1.0f); + } + if (stream->writeFlag(mask & NameMask)) { + con->packNetStringHandleU(stream, mShapeNameHandle); + } + if (stream->writeFlag(mask & ShieldMask)) { + stream->writeNormalVector(mShieldNormal, ShieldNormalBits); + stream->writeFloat( getEnergyValue(), EnergyLevelBits ); + } + if (stream->writeFlag(mask & InvincibleMask)) { + stream->write(mInvincibleTime); + stream->write(mInvincibleSpeed); + } + + if ( stream->writeFlag( mask & MeshHiddenMask ) ) + stream->writeBits( mMeshHidden ); + + if (stream->writeFlag(mask & SkinMask)) + con->packNetStringHandleU(stream, mSkinNameHandle); + } + + return retMask; +} + +void ShapeBase::unpackUpdate(NetConnection *con, BitStream *stream) +{ + Parent::unpackUpdate(con, stream); + mLastRenderFrame = sLastRenderFrame; // make sure we get a process after the event... + + if(!stream->readFlag()) + return; + + if (stream->readFlag()) { + mDamage = mClampF(stream->readFloat(DamageLevelBits) * mDataBlock->maxDamage, 0.f, mDataBlock->maxDamage); + DamageState prevState = mDamageState; + mDamageState = DamageState(stream->readInt(NumDamageStateBits)); + stream->readNormalVector( &damageDir, 8 ); + if (prevState != Destroyed && mDamageState == Destroyed && isProperlyAdded()) + blowUp(); + updateDamageLevel(); + updateDamageState(); + } + + if (stream->readFlag()) { + for (S32 i = 0; i < MaxScriptThreads; i++) { + if (stream->readFlag()) { + Thread& st = mScriptThread[i]; + U32 seq = stream->readInt(ThreadSequenceBits); + st.state = stream->readInt(2); + stream->read( &st.timescale ); + stream->read( &st.position ); + st.atEnd = stream->readFlag(); + if (st.sequence != seq && st.state != Thread::Destroy) + setThreadSequence(i,seq,false); + else + updateThread(st); + } + } + } + + if ( stream->readFlag() ) + { + for ( S32 i = 0; i < MaxSoundThreads; i++ ) + { + if ( stream->readFlag() ) + { + Sound& st = mSoundThread[i]; + st.play = stream->readFlag(); + if ( st.play ) + { + st.profile = (SFXTrack*) stream->readRangedU32( DataBlockObjectIdFirst, + DataBlockObjectIdLast ); + } + + if ( isProperlyAdded() ) + updateAudioState( st ); + } + } + } + + // Mounted Images + if (stream->readFlag()) { + for (int i = 0; i < MaxMountedImages; i++) { + if (stream->readFlag()) { + MountedImage& image = mMountedImageList[i]; + ShapeBaseImageData* imageData = 0; + if (stream->readFlag()) { + SimObjectId id = stream->readInt(DataBlockObjectIdBitSize) + + DataBlockObjectIdFirst; + if (!Sim::findObject(id,imageData)) { + con->setLastError("Invalid packet (mounted images)."); + return; + } + } + + NetStringHandle skinDesiredNameHandle = con->unpackNetStringHandleU(stream); + + NetStringHandle scriptDesiredAnimPrefix = con->unpackNetStringHandleU(stream); + + image.forceAnimateAllShapes = stream->readFlag(); + + image.wet = stream->readFlag(); + + image.motion = stream->readFlag(); + + image.ammo = stream->readFlag(); + + image.loaded = stream->readFlag(); + + image.target = stream->readFlag(); + + image.triggerDown = stream->readFlag(); + image.altTriggerDown = stream->readFlag(); + + for (U32 i=0; ireadFlag(); + } + + int count = stream->readInt(3); + int altCount = stream->readInt(3); + int reloadCount = stream->readInt(3); + + bool datablockChange = image.dataBlock != imageData; + if (datablockChange || (image.skinNameHandle != skinDesiredNameHandle)) + { + MountedImage& image = mMountedImageList[i]; + image.scriptAnimPrefix = scriptDesiredAnimPrefix; + + setImage( i, imageData, + skinDesiredNameHandle, image.loaded, + image.ammo, image.triggerDown, image.altTriggerDown, + image.motion, image.genericTrigger[0], image.genericTrigger[1], image.genericTrigger[2], image.genericTrigger[3], + image.target); + } + + if (!datablockChange && image.scriptAnimPrefix != scriptDesiredAnimPrefix) + { + // We don't have a new image, but we do have a new script anim prefix to work with. + // Notify the image of this change. + MountedImage& image = mMountedImageList[i]; + image.scriptAnimPrefix = scriptDesiredAnimPrefix; + updateAnimThread(i, getImageShapeIndex(image)); + } + + bool isFiring = stream->readFlag(); + bool isAltFiring = stream->readFlag(); + bool isReloading = stream->readFlag(); + + if (isProperlyAdded()) { + // Normal processing + bool processFiring = false; + if (count != image.fireCount) + { + image.fireCount = count; + setImageState(i,getImageFireState(i),true); + processFiring = true; + } + else if (altCount != image.altFireCount) + { + image.altFireCount = altCount; + setImageState(i,getImageAltFireState(i),true); + processFiring = true; + } + else if (reloadCount != image.reloadCount) + { + image.reloadCount = reloadCount; + setImageState(i,getImageReloadState(i),true); + } + + if (processFiring && imageData) + { + if ( imageData->lightType == ShapeBaseImageData::WeaponFireLight ) + image.lightStart = Sim::getCurrentTime(); + + // HACK: Only works properly if you are in control + // of the one and only shapeBase object in the scene + // which fires an image that uses camera shake. + if ( imageData->shakeCamera ) + { + if ( !mWeaponCamShake ) + { + mWeaponCamShake = new CameraShake(); + mWeaponCamShake->remoteControlled = true; + } + + mWeaponCamShake->init(); + mWeaponCamShake->setFrequency( imageData->camShakeFreq ); + mWeaponCamShake->setAmplitude( imageData->camShakeAmp ); + + if ( !mWeaponCamShake->isAdded ) + { + gCamFXMgr.addFX( mWeaponCamShake ); + mWeaponCamShake->isAdded = true; + } + } + } + + updateImageState(i,0); + + if ( !image.triggerDown && !image.altTriggerDown ) + { + if ( mWeaponCamShake && mWeaponCamShake->isAdded ) + { + gCamFXMgr.removeFX( mWeaponCamShake ); + mWeaponCamShake->isAdded = false; + } + } + } + else + { + if(imageData) + { + // Initial state + image.fireCount = count; + image.altFireCount = altCount; + image.reloadCount = reloadCount; + if (isFiring) + setImageState(i,getImageFireState(i),true); + else if (isAltFiring) + setImageState(i,getImageAltFireState(i),true); + else if (isReloading) + setImageState(i,getImageReloadState(i),true); + } + } + } + } + } + + if (stream->readFlag()) + { + if(stream->readFlag()) // CloakMask and control + { + // Read cloaking state. + + setCloakedState(stream->readFlag()); + mIsControlled = stream->readFlag(); + + if (( mFading = stream->readFlag()) == true) { + mFadeOut = stream->readFlag(); + if(mFadeOut) + mFadeVal = 1.0f; + else + mFadeVal = 0; + stream->read(&mFadeTime); + mFadeDelay = 0; + mFadeElapsedTime = 0; + } + else + mFadeVal = F32(stream->readFlag()); + } + if (stream->readFlag()) { // NameMask + mShapeNameHandle = con->unpackNetStringHandleU(stream); + } + if(stream->readFlag()) // ShieldMask + { + // Cloaking, Shield, and invul masking + Point3F shieldNormal; + stream->readNormalVector(&shieldNormal, ShieldNormalBits); + + // CodeReview [bjg 4/6/07] This is our energy level - why aren't we storing it? Was in a + // local variable called energyPercent. + stream->readFloat(EnergyLevelBits); + } + + if (stream->readFlag()) + { + // InvincibleMask + F32 time, speed; + stream->read(&time); + stream->read(&speed); + setupInvincibleEffect(time, speed); + } + + if ( stream->readFlag() ) // MeshHiddenMask + { + stream->readBits( &mMeshHidden ); + _updateHiddenMeshes(); + } + + if (stream->readFlag()) // SkinMask + { + NetStringHandle skinDesiredNameHandle = con->unpackNetStringHandleU(stream);; + if (mSkinNameHandle != skinDesiredNameHandle) + { + mSkinNameHandle = skinDesiredNameHandle; + reSkin(); + } + } + } +} + + +//-------------------------------------------------------------------------- + +void ShapeBase::forceUncloak(const char * reason) +{ + AssertFatal(isServerObject(), "ShapeBase::forceUncloak: server only call"); + if(!mCloaked) + return; + + mDataBlock->onForceUncloak_callback( this, reason ? reason : "" ); +} + +void ShapeBase::setCloakedState(bool cloaked) +{ + if (cloaked == mCloaked) + return; + + if (isServerObject()) + setMaskBits(CloakMask); + + // Have to do this for the client, if we are ghosted over in the initial + // packet as cloaked, we set the state immediately to the extreme + if (isProperlyAdded() == false) { + mCloaked = cloaked; + if (mCloaked) + mCloakLevel = 1.0; + else + mCloakLevel = 0.0; + } else { + mCloaked = cloaked; + } +} + + +//-------------------------------------------------------------------------- + +void ShapeBase::setHidden( bool hidden ) +{ + if( hidden != isHidden() ) + { + Parent::setHidden( hidden ); + + if( hidden ) + setProcessTick( false ); + else + setProcessTick( true ); + } +} + +//-------------------------------------------------------------------------- + +void ShapeBaseConvex::findNodeTransform() +{ + S32 dl = pShapeBase->mDataBlock->collisionDetails[hullId]; + + TSShapeInstance* si = pShapeBase->getShapeInstance(); + TSShape* shape = si->getShape(); + + const TSShape::Detail* detail = &shape->details[dl]; + const S32 subs = detail->subShapeNum; + const S32 start = shape->subShapeFirstObject[subs]; + const S32 end = start + shape->subShapeNumObjects[subs]; + + // Find the first object that contains a mesh for this + // detail level. There should only be one mesh per + // collision detail level. + for (S32 i = start; i < end; i++) + { + const TSShape::Object* obj = &shape->objects[i]; + if (obj->numMeshes && detail->objectDetailNum < obj->numMeshes) + { + nodeTransform = &si->mNodeTransforms[obj->nodeIndex]; + return; + } + } + return; +} + +const MatrixF& ShapeBaseConvex::getTransform() const +{ + // If the transform isn't specified, it's assumed to be the + // origin of the shape. + const MatrixF& omat = (transform != 0)? *transform: mObject->getTransform(); + + // Multiply on the mesh shape offset + // tg: Returning this static here is not really a good idea, but + // all this Convex code needs to be re-organized. + if (nodeTransform) { + static MatrixF mat; + mat.mul(omat,*nodeTransform); + return mat; + } + return omat; +} + +Box3F ShapeBaseConvex::getBoundingBox() const +{ + const MatrixF& omat = (transform != 0)? *transform: mObject->getTransform(); + return getBoundingBox(omat, mObject->getScale()); +} + +Box3F ShapeBaseConvex::getBoundingBox(const MatrixF& mat, const Point3F& scale) const +{ + Box3F newBox = box; + newBox.minExtents.convolve(scale); + newBox.maxExtents.convolve(scale); + mat.mul(newBox); + return newBox; +} + +Point3F ShapeBaseConvex::support(const VectorF& v) const +{ + TSShape::ConvexHullAccelerator* pAccel = + pShapeBase->mShapeInstance->getShape()->getAccelerator(pShapeBase->mDataBlock->collisionDetails[hullId]); + AssertFatal(pAccel != NULL, "Error, no accel!"); + + F32 currMaxDP = mDot(pAccel->vertexList[0], v); + U32 index = 0; + for (U32 i = 1; i < pAccel->numVerts; i++) { + F32 dp = mDot(pAccel->vertexList[i], v); + if (dp > currMaxDP) { + currMaxDP = dp; + index = i; + } + } + + return pAccel->vertexList[index]; +} + + +void ShapeBaseConvex::getFeatures(const MatrixF& mat, const VectorF& n, ConvexFeature* cf) +{ + cf->material = 0; + cf->object = mObject; + + TSShape::ConvexHullAccelerator* pAccel = + pShapeBase->mShapeInstance->getShape()->getAccelerator(pShapeBase->mDataBlock->collisionDetails[hullId]); + AssertFatal(pAccel != NULL, "Error, no accel!"); + + F32 currMaxDP = mDot(pAccel->vertexList[0], n); + U32 index = 0; + U32 i; + for (i = 1; i < pAccel->numVerts; i++) { + F32 dp = mDot(pAccel->vertexList[i], n); + if (dp > currMaxDP) { + currMaxDP = dp; + index = i; + } + } + + const U8* emitString = pAccel->emitStrings[index]; + U32 currPos = 0; + U32 numVerts = emitString[currPos++]; + for (i = 0; i < numVerts; i++) { + cf->mVertexList.increment(); + U32 index = emitString[currPos++]; + mat.mulP(pAccel->vertexList[index], &cf->mVertexList.last()); + } + + U32 numEdges = emitString[currPos++]; + for (i = 0; i < numEdges; i++) { + U32 ev0 = emitString[currPos++]; + U32 ev1 = emitString[currPos++]; + cf->mEdgeList.increment(); + cf->mEdgeList.last().vertex[0] = ev0; + cf->mEdgeList.last().vertex[1] = ev1; + } + + U32 numFaces = emitString[currPos++]; + for (i = 0; i < numFaces; i++) { + cf->mFaceList.increment(); + U32 plane = emitString[currPos++]; + mat.mulV(pAccel->normalList[plane], &cf->mFaceList.last().normal); + for (U32 j = 0; j < 3; j++) + cf->mFaceList.last().vertex[j] = emitString[currPos++]; + } +} + + +void ShapeBaseConvex::getPolyList(AbstractPolyList* list) +{ + list->setTransform(&pShapeBase->getTransform(), pShapeBase->getScale()); + list->setObject(pShapeBase); + + pShapeBase->mShapeInstance->animate(pShapeBase->mDataBlock->collisionDetails[hullId]); + pShapeBase->mShapeInstance->buildPolyList(list,pShapeBase->mDataBlock->collisionDetails[hullId]); +} + + +//-------------------------------------------------------------------------- + +bool ShapeBase::isInvincible() +{ + if( mDataBlock ) + { + return mDataBlock->isInvincible; + } + return false; +} + +void ShapeBase::startFade( F32 fadeTime, F32 fadeDelay, bool fadeOut ) +{ + setMaskBits(CloakMask); + mFadeElapsedTime = 0; + mFading = true; + if(fadeDelay < 0) + fadeDelay = 0; + if(fadeTime < 0) + fadeTime = 0; + mFadeTime = fadeTime; + mFadeDelay = fadeDelay; + mFadeOut = fadeOut; + mFadeVal = F32(mFadeOut); +} + +//-------------------------------------------------------------------------- + +void ShapeBase::setShapeName(const char* name) +{ + if (!isGhost()) { + if (name[0] != '\0') { + // Use tags for better network performance + // Should be a tag, but we'll convert to one if it isn't. + if (name[0] == StringTagPrefixByte) + mShapeNameHandle = NetStringHandle(U32(dAtoi(name + 1))); + else + mShapeNameHandle = NetStringHandle(name); + } + else { + mShapeNameHandle = NetStringHandle(); + } + setMaskBits(NameMask); + } +} + +void ShapeBase::setSkinName(const char* name) +{ + if (!isGhost()) { + if (name[0] != '\0') { + // Use tags for better network performance + // Should be a tag, but we'll convert to one if it isn't. + if (name[0] == StringTagPrefixByte) + mSkinNameHandle = NetStringHandle(U32(dAtoi(name + 1))); + else + mSkinNameHandle = NetStringHandle(name); + } + else + mSkinNameHandle = NetStringHandle(); + setMaskBits(SkinMask); + } +} + +//---------------------------------------------------------------------------- + +void ShapeBase::reSkin() +{ + if ( isGhost() && mShapeInstance && mSkinNameHandle.isValidString() ) + { + Vector skins; + String(mSkinNameHandle.getString()).split( ";", skins ); + + for (int i = 0; i < skins.size(); i++) + { + String oldSkin( mAppliedSkinName.c_str() ); + String newSkin( skins[i] ); + + // Check if the skin handle contains an explicit "old" base string. This + // allows all models to support skinning, even if they don't follow the + // "base_xxx" material naming convention. + S32 split = newSkin.find( '=' ); // "old=new" format skin? + if ( split != String::NPos ) + { + oldSkin = newSkin.substr( 0, split ); + newSkin = newSkin.erase( 0, split+1 ); + } + + mShapeInstance->reSkin( newSkin, oldSkin ); + mAppliedSkinName = newSkin; + } + } +} + +void ShapeBase::setCurrentWaterObject( WaterObject *obj ) +{ + if ( obj ) + deleteNotify( obj ); + if ( mCurrentWaterObject ) + clearNotify( mCurrentWaterObject ); + + mCurrentWaterObject = obj; +} + +//-------------------------------------------------------------------------- +//---------------------------------------------------------------------------- +DefineEngineMethod( ShapeBase, setHidden, void, ( bool show ),, + "@brief Add or remove this object from the scene.\n\n" + "When removed from the scene, the object will not be processed or rendered.\n" + "@param show False to hide the object, true to re-show it\n\n" ) +{ + object->setHidden( show ); +} + +DefineEngineMethod( ShapeBase, isHidden, bool, (),, + "Check if the object is hidden.\n" + "@return true if the object is hidden, false if visible.\n\n" ) +{ + return object->isHidden(); +} + +//---------------------------------------------------------------------------- +DefineEngineMethod( ShapeBase, playAudio, bool, ( S32 slot, SFXTrack* track ),, + "@brief Attach a sound to this shape and start playing it.\n\n" + + "@param slot Audio slot index for the sound (valid range is 0 - 3)\n" // 3 = ShapeBase::MaxSoundThreads-1 + "@param track SFXTrack to play\n" + "@return true if the sound was attached successfully, false if failed\n\n" + + "@see stopAudio()\n") +{ + if (track && slot >= 0 && slot < ShapeBase::MaxSoundThreads) { + object->playAudio(slot,track); + return true; + } + return false; +} + +DefineEngineMethod( ShapeBase, stopAudio, bool, ( S32 slot ),, + "@brief Stop a sound started with playAudio.\n\n" + + "@param slot audio slot index (started with playAudio)\n" + "@return true if the sound was stopped successfully, false if failed\n\n" + + "@see playAudio()\n") +{ + if (slot >= 0 && slot < ShapeBase::MaxSoundThreads) { + object->stopAudio(slot); + return true; + } + return false; +} + + +//---------------------------------------------------------------------------- +DefineEngineMethod( ShapeBase, playThread, bool, ( S32 slot, const char* name ), ( "" ), + "@brief Start a new animation thread, or restart one that has been paused or " + "stopped.\n\n" + + "@param slot thread slot to play. Valid range is 0 - 3)\n" // 3 = ShapeBase::MaxScriptThreads-1 + "@param name name of the animation sequence to play in this slot. If not " + "specified, the paused or stopped thread in this slot will be resumed.\n" + "@return true if successful, false if failed\n\n" + + "@tsexample\n" + "%obj.playThread( 0, \"ambient\" ); // Play the ambient sequence in slot 0\n" + "%obj.setThreadTimeScale( 0, 0.5 ); // Play at half-speed\n" + "%obj.pauseThread( 0 ); // Pause the sequence\n" + "%obj.playThread( 0 ); // Resume playback\n" + "%obj.playThread( 0, \"spin\" ); // Replace the sequence in slot 0\n" + "@endtsexample\n" + + "@see pauseThread()\n" + "@see stopThread()\n" + "@see setThreadDir()\n" + "@see setThreadTimeScale()\n" + "@see destroyThread()\n") +{ + if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) { + if (!dStrEqual(name, "")) { + if (object->getShape()) { + S32 seq = object->getShape()->findSequence(name); + if (seq != -1 && object->setThreadSequence(slot,seq)) + return true; + } + } + else + if (object->playThread(slot)) + return true; + } + return false; +} + +DefineEngineMethod( ShapeBase, setThreadDir, bool, ( S32 slot, bool fwd ),, + "@brief Set the playback direction of an animation thread.\n\n" + + "@param slot thread slot to modify\n" + "@param fwd true to play the animation forwards, false to play backwards\n" + "@return true if successful, false if failed\n\n" + + "@see playThread()\n" ) +{ + if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) { + if (object->setThreadDir(slot,fwd)) + return true; + } + return false; +} + +DefineEngineMethod( ShapeBase, setThreadTimeScale, bool, ( S32 slot, F32 scale ),, + "@brief Set the playback time scale of an animation thread.\n\n" + + "@param slot thread slot to modify\n" + "@param scale new thread time scale (1=normal speed, 0.5=half speed etc)\n" + "@return true if successful, false if failed\n\n" + + "@see playThread\n" ) +{ + if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) { + if (object->setThreadTimeScale(slot,scale)) + return true; + } + return false; +} + +DefineEngineMethod( ShapeBase, setThreadPosition, bool, ( S32 slot, F32 pos ),, + "@brief Set the position within an animation thread.\n\n" + + "@param slot thread slot to modify\n" + "@param pos position within thread\n" + "@return true if successful, false if failed\n\n" + + "@see playThread\n" ) +{ + if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) { + if (object->setThreadPosition(slot,pos)) + return true; + } + return false; +} + +DefineEngineMethod( ShapeBase, stopThread, bool, ( S32 slot ),, + "@brief Stop an animation thread.\n\n" + + "If restarted using playThread, the animation " + "will start from the beginning again.\n" + "@param slot thread slot to stop\n" + "@return true if successful, false if failed\n\n" + + "@see playThread\n" ) +{ + if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) { + if (object->stopThread(slot)) + return true; + } + return false; +} + +DefineEngineMethod( ShapeBase, destroyThread, bool, ( S32 slot ),, + "@brief Destroy an animation thread, which prevents it from playing.\n\n" + + "@param slot thread slot to destroy\n" + "@return true if successful, false if failed\n\n" + + "@see playThread\n" ) +{ + if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) { + if (object->destroyThread(slot)) + return true; + } + return false; +} + +DefineEngineMethod( ShapeBase, pauseThread, bool, ( S32 slot ),, + "@brief Pause an animation thread.\n\n" + + "If restarted using playThread, the animation " + "will resume from the paused position.\n" + "@param slot thread slot to stop\n" + "@return true if successful, false if failed\n\n" + + "@see playThread\n" ) +{ + if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) { + if (object->pauseThread(slot)) + return true; + } + return false; +} + +//---------------------------------------------------------------------------- +DefineEngineMethod( ShapeBase, mountImage, bool, + ( ShapeBaseImageData* image, S32 slot, bool loaded, const char* skinTag ), ( true, "" ), + "@brief Mount a new Image.\n\n" + + "@param image the Image to mount\n" + "@param slot Image slot to mount into (valid range is 0 - 3)\n" + "@param loaded initial loaded state for the Image\n" + "@param skinTag tagged string to reskin the mounted Image\n" + "@return true if successful, false if failed\n\n" + + "@tsexample\n" + "%player.mountImage( PistolImage, 1 );\n" + "%player.mountImage( CrossbowImage, 0, false );\n" + "%player.mountImage( RocketLauncherImage, 0, true, 'blue' );\n" + "@endtsexample\n" + + "@see unmountImage()\n" + "@see getMountedImage()\n" + "@see getPendingImage()\n" + "@see isImageMounted()\n") +{ + if (image && slot >= 0 && slot < ShapeBase::MaxMountedImages) { + + NetStringHandle team; + if (skinTag[0] == StringTagPrefixByte) + team = NetStringHandle(U32(dAtoi(skinTag+1))); + + return object->mountImage( image, slot, loaded, team ); + } + return false; +} + +DefineEngineMethod( ShapeBase, unmountImage, bool, ( S32 slot ),, + "@brief Unmount the mounted Image in the specified slot.\n\n" + + "@param slot Image slot to unmount\n" + "@return true if successful, false if failed\n\n" + + "@see mountImage()\n") +{ + if (slot >= 0 && slot < ShapeBase::MaxMountedImages) + return object->unmountImage(slot); + return false; +} + +DefineEngineMethod( ShapeBase, getMountedImage, S32, ( S32 slot ),, + "@brief Get the Image mounted in the specified slot.\n\n" + + "@param slot Image slot to query\n" + "@return ID of the ShapeBaseImageData datablock mounted in the slot, or 0 " + "if no Image is mounted there.\n\n" ) +{ + if (slot >= 0 && slot < ShapeBase::MaxMountedImages) + if (ShapeBaseImageData* data = object->getMountedImage(slot)) + return data->getId(); + return 0; +} + +DefineEngineMethod( ShapeBase, getPendingImage, S32, ( S32 slot ),, + "@brief Get the Image that will be mounted next in the specified slot.\n\n" + + "Calling mountImage when an Image is already mounted does one of two things: " + "
      1. Mount the new Image immediately, the old Image is discarded and " + "whatever state it was in is ignored.
      2. " + "
      3. If the current Image state does not allow Image changes, the new " + "Image is marked as pending, and will not be mounted until the current " + "state completes. eg. if the user changes weapons, you may wish to ensure " + "that the current weapon firing state plays to completion first.
      \n" + "This command retrieves the ID of the pending Image (2nd case above).\n" + + "@param slot Image slot to query\n" + "@return ID of the pending ShapeBaseImageData datablock, or 0 if none.\n\n" ) +{ + if (slot >= 0 && slot < ShapeBase::MaxMountedImages) + if (ShapeBaseImageData* data = object->getPendingImage(slot)) + return data->getId(); + return 0; +} + +DefineEngineMethod( ShapeBase, isImageFiring, bool, ( S32 slot ),, + "@brief Check if the current Image state is firing.\n\n" + + "@param slot Image slot to query\n" + "@return true if the current Image state in this slot has the 'stateFire' flag set.\n" ) +{ + if (slot >= 0 && slot < ShapeBase::MaxMountedImages) + return object->isImageFiring(slot); + return false; +} + +DefineEngineMethod( ShapeBase, isImageMounted, bool, ( ShapeBaseImageData* image ),, + "@brief Check if the given datablock is mounted to any slot on this object.\n\n" + + "@param image ShapeBaseImageData datablock to query\n" + "@return true if the Image is mounted to any slot, false otherwise.\n\n" ) +{ + return (image && object->isImageMounted(image)); +} + +DefineEngineMethod( ShapeBase, getMountSlot, S32, ( ShapeBaseImageData* image ),, + "@brief Get the first slot the given datablock is mounted to on this object.\n\n" + + "@param image ShapeBaseImageData datablock to query\n" + "@return index of the first slot the Image is mounted in, or -1 if the Image " + "is not mounted in any slot on this object.\n\n" ) + +{ + return image ? object->getMountSlot(image) : -1; +} + +DefineEngineMethod( ShapeBase, getImageSkinTag, S32, ( S32 slot ),, + "@brief Get the skin tag ID for the Image mounted in the specified slot.\n\n" + + "@param slot Image slot to query\n" + "@return the skinTag value passed to mountImage when the image was " + "mounted\n\n" ) +{ + if (slot >= 0 && slot < ShapeBase::MaxMountedImages) + return object->getImageSkinTag(slot).getIndex(); + return -1; +} + +DefineEngineMethod( ShapeBase, getImageState, const char*, ( S32 slot ),, + "@brief Get the name of the current state of the Image in the specified slot.\n\n" + + "@param slot Image slot to query\n" + "@return name of the current Image state, or \"Error\" if slot is invalid\n\n" ) +{ + if (slot >= 0 && slot < ShapeBase::MaxMountedImages) + return object->getImageState(slot); + return "Error"; +} + +DefineEngineMethod( ShapeBase, hasImageState, bool, ( S32 slot, const char* state ),, + "@brief Check if the given state exists on the mounted Image.\n\n" + + "@param slot Image slot to query\n" + "@param state Image state to check for\n" + "@return true if the Image has the requested state defined.\n\n" ) +{ + if (slot >= 0 && slot < ShapeBase::MaxMountedImages) + return object->hasImageState(slot, state); + return false; +} + +DefineEngineMethod( ShapeBase, getImageTrigger, bool, ( S32 slot ),, + "@brief Get the trigger state of the Image mounted in the specified slot.\n\n" + + "@param slot Image slot to query\n" + "@return the Image's current trigger state\n\n" ) +{ + if (slot >= 0 && slot < ShapeBase::MaxMountedImages) + return object->getImageTriggerState(slot); + return false; +} + +DefineEngineMethod( ShapeBase, setImageTrigger, bool, ( S32 slot, bool state ),, + "@brief Set the trigger state of the Image mounted in the specified slot.\n\n" + + "@param slot Image slot to modify\n" + "@param state new trigger state for the Image\n" + "@return the Image's new trigger state\n\n" ) +{ + if (slot >= 0 && slot < ShapeBase::MaxMountedImages) { + object->setImageTriggerState(slot,state); + return object->getImageTriggerState(slot); + } + return false; +} + +DefineEngineMethod( ShapeBase, getImageGenericTrigger, bool, ( S32 slot, S32 trigger ),, + "@brief Get the generic trigger state of the Image mounted in the specified slot.\n\n" + + "@param slot Image slot to query\n" + "@param trigger Generic trigger number\n" + "@return the Image's current generic trigger state\n\n" ) +{ + if (slot >= 0 && slot < ShapeBase::MaxMountedImages && trigger >= 0 && trigger < ShapeBaseImageData::MaxGenericTriggers) + return object->getImageGenericTriggerState(slot, trigger); + return false; +} + +DefineEngineMethod( ShapeBase, setImageGenericTrigger, S32, ( S32 slot, S32 trigger, bool state ),, + "@brief Set the generic trigger state of the Image mounted in the specified slot.\n\n" + + "@param slot Image slot to modify\n" + "@param trigger Generic trigger number\n" + "@param state new generic trigger state for the Image\n" + "@return the Image's new generic trigger state or -1 if there was a problem.\n\n" ) +{ + if (slot >= 0 && slot < ShapeBase::MaxMountedImages && trigger >= 0 && trigger < ShapeBaseImageData::MaxGenericTriggers) { + object->setImageGenericTriggerState(slot,trigger,state); + return object->getImageGenericTriggerState(slot,trigger); + } + return -1; +} + +DefineEngineMethod( ShapeBase, getImageAltTrigger, bool, ( S32 slot ),, + "@brief Get the alt trigger state of the Image mounted in the specified slot.\n\n" + + "@param slot Image slot to query\n" + "@return the Image's current alt trigger state\n\n" ) +{ + if (slot >= 0 && slot < ShapeBase::MaxMountedImages) + return object->getImageAltTriggerState(slot); + return false; +} + +DefineEngineMethod( ShapeBase, setImageAltTrigger, bool, ( S32 slot, bool state ),, + "@brief Set the alt trigger state of the Image mounted in the specified slot.\n\n" + + "@param slot Image slot to modify\n" + "@param state new alt trigger state for the Image\n" + "@return the Image's new alt trigger state\n\n" ) +{ + if (slot >= 0 && slot < ShapeBase::MaxMountedImages) { + object->setImageAltTriggerState(slot,state); + return object->getImageAltTriggerState(slot); + } + return false; +} + +DefineEngineMethod( ShapeBase, getImageAmmo, bool, ( S32 slot ),, + "@brief Get the ammo state of the Image mounted in the specified slot.\n\n" + + "@param slot Image slot to query\n" + "@return the Image's current ammo state\n\n" ) +{ + if (slot >= 0 && slot < ShapeBase::MaxMountedImages) + return object->getImageAmmoState(slot); + return false; +} + +DefineEngineMethod( ShapeBase, setImageAmmo, bool, ( S32 slot, bool state ),, + "@brief Set the ammo state of the Image mounted in the specified slot.\n\n" + + "@param slot Image slot to modify\n" + "@param state new ammo state for the Image\n" + "@return the Image's new ammo state\n\n" ) +{ + if (slot >= 0 && slot < ShapeBase::MaxMountedImages) { + object->setImageAmmoState(slot,state); + return state; + } + return false; +} + +DefineEngineMethod( ShapeBase, getImageLoaded, bool, ( S32 slot ),, + "@brief Get the loaded state of the Image mounted in the specified slot.\n\n" + + "@param slot Image slot to query\n" + "@return the Image's current loaded state\n\n" ) +{ + if (slot >= 0 && slot < ShapeBase::MaxMountedImages) + return object->getImageLoadedState(slot); + return false; +} + +DefineEngineMethod( ShapeBase, setImageLoaded, bool, ( S32 slot, bool state ),, + "@brief Set the loaded state of the Image mounted in the specified slot.\n\n" + + "@param slot Image slot to modify\n" + "@param state new loaded state for the Image\n" + "@return the Image's new loaded state\n\n" ) +{ + if (slot >= 0 && slot < ShapeBase::MaxMountedImages) { + object->setImageLoadedState(slot, state); + return state; + } + return false; +} + +DefineEngineMethod( ShapeBase, getImageTarget, bool, ( S32 slot ),, + "@brief Get the target state of the Image mounted in the specified slot.\n\n" + + "@param slot Image slot to query\n" + "@return the Image's current target state\n\n" ) +{ + if (slot >= 0 && slot < ShapeBase::MaxMountedImages) + return object->getImageTargetState(slot); + return false; +} + +DefineEngineMethod( ShapeBase, setImageTarget, bool, ( S32 slot, bool state ),, + "@brief Set the target state of the Image mounted in the specified slot.\n\n" + + "@param slot Image slot to modify\n" + "@param state new target state for the Image\n" + "@return the Image's new target state\n\n" ) +{ + if (slot >= 0 && slot < ShapeBase::MaxMountedImages) { + object->setImageTargetState(slot,state); + return state; + } + return false; +} + +DefineEngineMethod( ShapeBase, getImageScriptAnimPrefix, const char*, ( S32 slot ),, + "@brief Get the script animation prefix of the Image mounted in the specified slot.\n\n" + + "@param slot Image slot to query\n" + "@return the Image's current script animation prefix\n\n" ) +{ + if (slot >= 0 && slot < ShapeBase::MaxMountedImages) + return object->getImageScriptAnimPrefix(slot).getString(); + return ""; +} + +DefineEngineMethod( ShapeBase, setImageScriptAnimPrefix, void, ( S32 slot, const char* prefix ),, + "@brief Set the script animation prefix for the Image mounted in the specified slot.\n\n" + "This is used to further modify the prefix used when deciding which animation sequence to " + "play while this image is mounted.\n" + + "@param slot Image slot to modify\n" + "@param prefix The prefix applied to the image\n" ) +{ + if (slot >= 0 && slot < ShapeBase::MaxMountedImages) { + + NetStringHandle prefixHandle; + if (prefix[0] == StringTagPrefixByte) + prefixHandle = NetStringHandle(U32(dAtoi(prefix+1))); + + object->setImageScriptAnimPrefix(slot, prefixHandle); + } +} + +DefineEngineMethod( ShapeBase, getMuzzleVector, VectorF, ( S32 slot ),, + "@brief Get the muzzle vector of the Image mounted in the specified slot.\n\n" + + "If the Image shape contains a node called 'muzzlePoint', then the muzzle " + "vector is the forward direction vector of that node's transform in world " + "space. If no such node is specified, the slot's mount node is used " + "instead.\n\n" + + "If the correctMuzzleVector flag (correctMuzzleVectorTP in 3rd person) " + "is set in the Image, the muzzle vector is computed to point at whatever " + "object is right in front of the object's 'eye' node.\n" + + "@param slot Image slot to query\n" + "@return the muzzle vector, or \"0 1 0\" if the slot is invalid\n\n" ) +{ + VectorF vec(0, 1, 0); + if (slot >= 0 && slot < ShapeBase::MaxMountedImages) + object->getMuzzleVector(slot, &vec); + + return vec; +} + +DefineEngineMethod( ShapeBase, getMuzzlePoint, Point3F, ( S32 slot ),, + "@brief Get the muzzle position of the Image mounted in the specified slot.\n\n" + + "If the Image shape contains a node called 'muzzlePoint', then the muzzle " + "position is the position of that node in world space. If no such node " + "is specified, the slot's mount node is used instead.\n" + + "@param slot Image slot to query\n" + "@return the muzzle position, or \"0 0 0\" if the slot is invalid\n\n" ) +{ + Point3F pos(0, 0, 0); + if (slot >= 0 && slot < ShapeBase::MaxMountedImages) + object->getMuzzlePoint(slot, &pos); + + return pos; +} + +DefineEngineMethod( ShapeBase, getSlotTransform, TransformF, ( S32 slot ),, + "@brief Get the world transform of the specified mount slot.\n\n" + + "@param slot Image slot to query\n" + "@return the mount transform\n\n" ) +{ + MatrixF xf(true); + if (slot >= 0 && slot < ShapeBase::MaxMountedImages) + object->getMountTransform( slot, MatrixF::Identity, &xf ); + + return xf; +} + +//---------------------------------------------------------------------------- + +DefineEngineMethod( ShapeBase, getAIRepairPoint, Point3F, (),, + "@brief Get the position at which the AI should stand to repair things.\n\n" + + "If the shape defines a node called \"AIRepairNode\", this method will " + "return the current world position of that node, otherwise \"0 0 0\".\n" + "@return the AI repair position\n\n" ) +{ + return object->getAIRepairPoint(); +} + +DefineEngineMethod( ShapeBase, getVelocity, VectorF, (),, + "@brief Get the object's current velocity.\n\n" + + "@return the current velocity\n\n" ) +{ + return object->getVelocity(); +} + +DefineEngineMethod( ShapeBase, setVelocity, bool, ( Point3F vel ),, + "@brief Set the object's velocity.\n\n" + + "@param vel new velocity for the object\n" + "@return true\n\n" ) +{ + object->setVelocity( vel ); + return true; +} + +DefineEngineMethod( ShapeBase, applyImpulse, bool, ( Point3F pos, Point3F vec ),, + "@brief Apply an impulse to the object.\n\n" + + "@param pos world position of the impulse\n" + "@param vec impulse momentum (velocity * mass)\n" + "@return true\n\n" ) +{ + object->applyImpulse( pos, vec ); + return true; +} + +DefineEngineMethod( ShapeBase, getEyeVector, VectorF, (),, + "@brief Get the forward direction of the 'eye' for this object.\n\n" + + "If the object model has a node called 'eye', this method will return that " + "node's current forward direction vector, otherwise it will return the " + "object's current forward direction vector.\n" + + "@return the eye vector for this object\n" + + "@see getEyePoint\n" + "@see getEyeTransform\n" ) +{ + MatrixF mat; + object->getEyeTransform(&mat); + return mat.getForwardVector(); +} + +DefineEngineMethod( ShapeBase, getEyePoint, Point3F, (),, + "@brief Get the position of the 'eye' for this object.\n\n" + + "If the object model has a node called 'eye', this method will return that " + "node's current world position, otherwise it will return the object's current " + "world position.\n" + + "@return the eye position for this object\n" + + "@see getEyeVector\n" + "@see getEyeTransform\n" ) +{ + MatrixF mat; + object->getEyeTransform(&mat); + return mat.getPosition(); +} + +DefineEngineMethod( ShapeBase, getEyeTransform, TransformF, (),, + "@brief Get the 'eye' transform for this object.\n\n" + + "If the object model has a node called 'eye', this method will return that " + "node's current transform, otherwise it will return the object's current " + "transform.\n" + + "@return the eye transform for this object\n" + + "@see getEyeVector\n" + "@see getEyePoint\n" ) +{ + MatrixF mat; + object->getEyeTransform(&mat); + return mat; +} + +DefineEngineMethod( ShapeBase, getLookAtPoint, const char*, ( F32 distance, S32 typeMask ), ( 2000, 0xFFFFFFFF ), + "@brief Get the world position this object is looking at.\n\n" + + "Casts a ray from the eye and returns information about what the ray hits.\n" + + "@param distance maximum distance of the raycast\n" + "@param typeMask typeMask of objects to include for raycast collision testing\n" + "@return look-at information as \"Object HitX HitY HitZ [Material]\" or empty string for no hit\n\n" + + "@tsexample\n" + "%lookat = %obj.getLookAtPoint();\n" + "echo( \"Looking at: \" @ getWords( %lookat, 1, 3 ) );\n" + "@endtsexample\n" ) +{ + MatrixF mat; + object->getEyeTransform( &mat ); + + // Get eye vector. + + VectorF eyeVector; + mat.getColumn( 1, &eyeVector ); + + // Get eye position. + + VectorF eyePos; + mat.getColumn( 3, &eyePos ); + + // Make sure the eye vector covers the distance. + + eyeVector *= distance; + + // Do a container search. + + VectorF start = eyePos; + VectorF end = eyePos + eyeVector; + + RayInfo ri; + if( !gServerContainer.castRay( start, end, typeMask, &ri ) || !ri.object ) + return ""; // No hit. + + // Gather hit info. + + enum { BUFFER_SIZE = 256 }; + char* buffer = Con::getReturnBuffer( BUFFER_SIZE ); + if( ri.material ) + dSprintf( buffer, BUFFER_SIZE, "%u %f %f %f %u", + ri.object->getId(), + ri.point.x, + ri.point.y, + ri.point.z, + ri.material->getMaterial()->getId() ); + else + dSprintf( buffer, BUFFER_SIZE, "%u %f %f %f", + ri.object->getId(), + ri.point.x, + ri.point.y, + ri.point.z ); + + return buffer; +} + +DefineEngineMethod( ShapeBase, setEnergyLevel, void, ( F32 level ),, + "@brief Set this object's current energy level.\n\n" + + "@param level new energy level\n" + + "@see getEnergyLevel()\n" + "@see getEnergyPercent()\n") +{ + object->setEnergyLevel( level ); +} + +DefineEngineMethod( ShapeBase, getEnergyLevel, F32, (),, + "@brief Get the object's current energy level.\n\n" + + "@return energy level\n" + + "@see setEnergyLevel()\n") +{ + return object->getEnergyLevel(); +} + +DefineEngineMethod( ShapeBase, getEnergyPercent, F32, (),, + "@brief Get the object's current energy level as a percentage of maxEnergy.\n\n" + "@return energyLevel / datablock.maxEnergy\n" + + "@see setEnergyLevel()\n") +{ + return object->getEnergyValue(); +} + +DefineEngineMethod( ShapeBase, setDamageLevel, void, ( F32 level ),, + "@brief Set the object's current damage level.\n\n" + + "@param level new damage level\n" + + "@see getDamageLevel()\n" + "@see getDamagePercent()\n") +{ + object->setDamageLevel( level ); +} + +DefineEngineMethod( ShapeBase, getDamageLevel, F32, (),, + "@brief Get the object's current damage level.\n\n" + + "@return damage level\n" + + "@see setDamageLevel()\n") +{ + return object->getDamageLevel(); +} + +DefineEngineMethod( ShapeBase, getDamagePercent, F32, (),, + "@brief Get the object's current damage level as a percentage of maxDamage.\n\n" + + "@return damageLevel / datablock.maxDamage\n" + + "@see setDamageLevel()\n") +{ + return object->getDamageValue(); +} + +DefineEngineMethod( ShapeBase, setDamageState, bool, ( const char* state ),, + "@brief Set the object's damage state.\n\n" + + "@param state should be one of \"Enabled\", \"Disabled\", \"Destroyed\"\n" + "@return true if successful, false if failed\n" + + "@see getDamageState()\n") +{ + return object->setDamageState( state ); +} + +DefineEngineMethod( ShapeBase, getDamageState, const char*, (),, + "@brief Get the object's damage state.\n\n" + + "@return the damage state; one of \"Enabled\", \"Disabled\", \"Destroyed\"\n" + + "@see setDamageState()\n") +{ + return object->getDamageStateName(); +} + +DefineEngineMethod( ShapeBase, isDestroyed, bool, (),, + "@brief Check if the object is in the Destroyed damage state.\n\n" + + "@return true if damage state is \"Destroyed\", false if not\n" + + "@see isDisabled()\n" + "@see isEnabled()\n") +{ + return object->isDestroyed(); +} + +DefineEngineMethod( ShapeBase, isDisabled, bool, (),, + "@brief Check if the object is in the Disabled or Destroyed damage state.\n\n" + + "@return true if damage state is not \"Enabled\", false if it is\n" + + "@see isDestroyed()\n" + "@see isEnabled()\n") +{ + return object->getDamageState() != ShapeBase::Enabled; +} + +DefineEngineMethod( ShapeBase, isEnabled, bool, (),, + "@brief Check if the object is in the Enabled damage state.\n\n" + + "@return true if damage state is \"Enabled\", false if not\n" + + "@see isDestroyed()\n" + "@see isDisabled()\n") +{ + return object->getDamageState() == ShapeBase::Enabled; +} + +DefineEngineMethod( ShapeBase, applyDamage, void, ( F32 amount ),, + "@brief Increment the current damage level by the specified amount.\n\n" + + "@param amount value to add to current damage level\n" ) +{ + object->applyDamage( amount ); +} + +DefineEngineMethod( ShapeBase, applyRepair, void, ( F32 amount ),, + "@brief Repair damage by the specified amount.\n\n" + + "Note that the damage level is only reduced by repairRate per tick, so it may " + "take several ticks for the total repair to complete.\n" + + "@param amount total repair value (subtracted from damage level over time)\n" ) +{ + object->applyRepair( amount ); +} + +DefineEngineMethod( ShapeBase, setRepairRate, void, ( F32 rate ),, + "@brief Set amount to repair damage by each tick.\n\n" + + "Note that this value is separate to the repairRate field in ShapeBaseData. " + "This value will be subtracted from the damage level each tick, whereas the " + "ShapeBaseData field limits how much of the applyRepair value is subtracted " + "each tick. Both repair types can be active at the same time.\n" + + "@param rate value to subtract from damage level each tick (must be > 0)\n" + + "@see getRepairRate()\n") +{ + if(rate < 0) + rate = 0; + object->setRepairRate( rate ); +} + +DefineEngineMethod( ShapeBase, getRepairRate, F32, (),, + "@brief Get the per-tick repair amount.\n\n" + + "@return the current value to be subtracted from damage level each tick\n" + + "@see setRepairRate\n" ) +{ + return object->getRepairRate(); +} + +DefineEngineMethod( ShapeBase, setRechargeRate, void, ( F32 rate ),, + "@brief Set the recharge rate.\n\n" + + "The recharge rate is added to the object's current energy level each tick, " + "up to the maxEnergy level set in the ShapeBaseData datablock.\n" + + "@param rate the recharge rate (per tick)\n" + + "@see getRechargeRate()\n") +{ + object->setRechargeRate( rate ); +} + +DefineEngineMethod( ShapeBase, getRechargeRate, F32, (),, + "@brief Get the current recharge rate.\n\n" + + "@return the recharge rate (per tick)\n" + + "@see setRechargeRate()\n") +{ + return object->getRechargeRate(); +} + +DefineEngineMethod( ShapeBase, getControllingClient, S32, (),, + "@brief Get the client (if any) that controls this object.\n\n" + + "The controlling client is the one that will send moves to us to act on.\n" + + "@return the ID of the controlling GameConnection, or 0 if this object is not " + "controlled by any client.\n" + + "@see GameConnection\n") +{ + if (GameConnection* con = object->getControllingClient()) + return con->getId(); + return 0; +} + +DefineEngineMethod( ShapeBase, getControllingObject, S32, (),, + "@brief Get the object (if any) that controls this object.\n\n" + + "@return the ID of the controlling ShapeBase object, or 0 if this object is " + "not controlled by another object.\n" ) +{ + if (ShapeBase* con = object->getControllingObject()) + return con->getId(); + return 0; +} + +DefineEngineMethod( ShapeBase, canCloak, bool, (),, + "@brief Check if this object can cloak.\n\n" + "@return true\n" + + "@note Not implemented as it always returns true.") +{ + return true; +} + +DefineEngineMethod( ShapeBase, setCloaked, void, ( bool cloak ),, + "@brief Set the cloaked state of this object.\n\n" + + "When an object is cloaked it is not rendered.\n" + + "@param cloak true to cloak the object, false to uncloak\n" + + "@see isCloaked()\n") +{ + if (object->isServerObject()) + object->setCloakedState( cloak ); +} + +DefineEngineMethod( ShapeBase, isCloaked, bool, (),, + "@brief Check if this object is cloaked.\n\n" + + "@return true if cloaked, false if not\n" + + "@see setCloaked()\n") +{ + return object->getCloakedState(); +} + +DefineEngineMethod( ShapeBase, setDamageFlash, void, ( F32 level ),, + "@brief Set the damage flash level.\n\n" + + "Damage flash may be used as a postfx effect to flash the screen when the " + "client is damaged.\n" + + "@note Relies on the flash postFx.\n" + + "@param level flash level (0-1)\n" + + "@see getDamageFlash()\n") +{ + if (object->isServerObject()) + object->setDamageFlash( level ); +} + +DefineEngineMethod( ShapeBase, getDamageFlash, F32, (),, + "@brief Get the damage flash level.\n\n" + + "@return flash level\n" + + "@see setDamageFlash\n" ) +{ + return object->getDamageFlash(); +} + +DefineEngineMethod( ShapeBase, setWhiteOut, void, ( F32 level ),, + "@brief Set the white-out level.\n\n" + + "White-out may be used as a postfx effect to brighten the screen in response " + "to a game event.\n" + + "@note Relies on the flash postFx.\n" + + "@param level flash level (0-1)\n" + + "@see getWhiteOut()\n") +{ + if (object->isServerObject()) + object->setWhiteOut( level ); +} + +DefineEngineMethod( ShapeBase, getWhiteOut, F32, (),, + "@brief Get the white-out level.\n\n" + + "@return white-out level\n" + + "@see setWhiteOut\n" ) +{ + return object->getWhiteOut(); +} + +DefineEngineMethod( ShapeBase, getDefaultCameraFov, F32, (),, + "@brief Returns the default vertical field of view in degrees for this object if used as a camera.\n\n" + + "@return Default FOV\n" ) +{ + if (object->isServerObject()) + return object->getDefaultCameraFov(); + return 0.0; +} + +DefineEngineMethod( ShapeBase, getCameraFov, F32, (),, + "@brief Returns the vertical field of view in degrees for this object if used as a camera.\n\n" + + "@return current FOV as defined in ShapeBaseData::cameraDefaultFov\n" ) +{ + if (object->isServerObject()) + return object->getCameraFov(); + return 0.0; +} + +DefineEngineMethod( ShapeBase, setCameraFov, void, ( F32 fov ),, + "@brief Set the vertical field of view in degrees for this object if used as a camera.\n\n" + + "@param fov new FOV value\n" ) +{ + if (object->isServerObject()) + object->setCameraFov( fov ); +} + +DefineEngineMethod( ShapeBase, setInvincibleMode, void, ( F32 time, F32 speed ),, + "@brief Setup the invincible effect.\n\n" + + "This effect is used for HUD feedback to the user that they are invincible.\n" + "@note Currently not implemented\n" + + "@param time duration in seconds for the invincible effect\n" + "@param speed speed at which the invincible effect progresses\n" ) +{ + object->setupInvincibleEffect( time, speed ); +} + +DefineEngineMethod( ShapeBase, startFade, void, ( S32 time, S32 delay, bool fadeOut ),, + "@brief Fade the object in or out without removing it from the scene.\n\n" + + "A faded out object is still in the scene and can still be collided with, " + "so if you want to disable collisions for this shape after it fades out " + "use setHidden to temporarily remove this shape from the scene.\n" + + "@note Items have the ability to light their surroundings. When an Item with " + "an active light is fading out, the light it emits is correspondingly " + "reduced until it goes out. Likewise, when the item fades in, the light is " + "turned-up till it reaches it's normal brightntess.\n" + + "@param time duration of the fade effect in ms\n" + "@param delay delay in ms before the fade effect begins\n" + "@param fadeOut true to fade-out to invisible, false to fade-in to full visibility\n" ) +{ + object->startFade( (F32)time / (F32)1000.0, delay / 1000.0, fadeOut ); +} + +DefineEngineMethod( ShapeBase, setDamageVector, void, ( Point3F vec ),, + "@brief Set the damage direction vector.\n\n" + + "Currently this is only used to initialise the explosion if this object " + "is blown up.\n" + + "@param vec damage direction vector\n\n" + + "@tsexample\n" + "%obj.setDamageVector( \"0 0 1\" );\n" + "@endtsexample\n" ) +{ + vec.normalize(); + object->setDamageDir( vec ); +} + +DefineEngineMethod( ShapeBase, setShapeName, void, ( const char* name ),, + "@brief Set the name of this shape.\n\n" + + "@note This is the name of the shape object that is sent to the client, " + "not the DTS or DAE model filename.\n" + + "@param name new name for the shape\n\n" + + "@see getShapeName()\n") +{ + object->setShapeName( name ); +} + +DefineEngineMethod( ShapeBase, getShapeName, const char*, (),, + "@brief Get the name of the shape.\n\n" + + "@note This is the name of the shape object that is sent to the client, " + "not the DTS or DAE model filename.\n" + + "@return the name of the shape\n\n" + + "@see setShapeName()\n") +{ + return object->getShapeName(); +} + +DefineEngineMethod( ShapeBase, setSkinName, void, ( const char* name ),, + "@brief Apply a new skin to this shape.\n\n" + + "'Skinning' the shape effectively renames the material targets, allowing " + "different materials to be used on different instances of the same model.\n\n" + + "@param name name of the skin to apply\n\n" + + "@see skin\n" + "@see getSkinName()\n") +{ + object->setSkinName( name ); +} + +DefineEngineMethod( ShapeBase, getSkinName, const char*, (),, + "@brief Get the name of the skin applied to this shape.\n\n" + + "@return the name of the skin\n\n" + + "@see skin\n" + "@see setSkinName()\n") +{ + return object->getSkinName(); +} + +//---------------------------------------------------------------------------- +void ShapeBase::consoleInit() +{ + Con::addVariable("SB::DFDec", TypeF32, &sDamageFlashDec, "Speed to reduce the damage flash effect per tick.\n\n" + "@see ShapeBase::setDamageFlash()\n" + "@see ShapeBase::getDamageFlash()\n" + "@note Relies on the flash postFx.\n" + "@ingroup gameObjects\n"); + Con::addVariable("SB::WODec", TypeF32, &sWhiteoutDec, "Speed to reduce the whiteout effect per tick.\n\n" + "@see ShapeBase::setWhiteOut()\n" + "@see ShapeBase::getWhiteOut" + "@note Relies on the flash postFx.\n" + "@ingroup gameObjects\n"); + Con::addVariable("SB::FullCorrectionDistance", TypeF32, &sFullCorrectionDistance, + "@brief Distance at which a weapon's muzzle vector is fully corrected to match where the player is looking.\n\n" + "When a weapon image has correctMuzzleVector set and the Player is in 1st person, the muzzle vector from the " + "weapon is modified to match where the player is looking. Beyond the FullCorrectionDistance the muzzle vector " + "is always corrected. Between FullCorrectionDistance and the player, the weapon's muzzle vector is adjusted so that " + "the closer the aim point is to the player, the closer the muzzle vector is to the true (non-corrected) one.\n" + "@ingroup gameObjects\n"); + Con::addVariable("SB::CloakSpeed", TypeF32, &sCloakSpeed, + "@brief Time to cloak, in seconds.\n\n" + "@ingroup gameObjects\n"); +} + +void ShapeBase::_updateHiddenMeshes() +{ + if ( !mShapeInstance ) + return; + + // This may happen at some point in the future... lets + // detect it so that it can be fixed at that time. + AssertFatal( mMeshHidden.getSize() == mShapeInstance->mMeshObjects.size(), + "ShapeBase::_updateMeshVisibility() - Mesh visibility size mismatch!" ); + + for ( U32 i = 0; i < mMeshHidden.getSize(); i++ ) + setMeshHidden( i, mMeshHidden.test( i ) ); +} + +void ShapeBase::setMeshHidden( const char *meshName, bool forceHidden ) +{ + setMeshHidden( mDataBlock->mShape->findObject( meshName ), forceHidden ); +} + +void ShapeBase::setMeshHidden( S32 meshIndex, bool forceHidden ) +{ + if ( meshIndex == -1 || meshIndex >= mMeshHidden.getSize() ) + return; + + if ( forceHidden ) + mMeshHidden.set( meshIndex ); + else + mMeshHidden.clear( meshIndex ); + + if ( mShapeInstance ) + mShapeInstance->setMeshForceHidden( meshIndex, forceHidden ); + + setMaskBits( MeshHiddenMask ); +} + +void ShapeBase::setAllMeshesHidden( bool forceHidden ) +{ + if ( forceHidden ) + mMeshHidden.set(); + else + mMeshHidden.clear(); + + if ( mShapeInstance ) + { + for ( U32 i = 0; i < mMeshHidden.getSize(); i++ ) + mShapeInstance->setMeshForceHidden( i, forceHidden ); + } + + setMaskBits( MeshHiddenMask ); +} + +DefineEngineMethod( ShapeBase, setAllMeshesHidden, void, ( bool hide ),, + "@brief Set the hidden state on all the shape meshes.\n\n" + + "This allows you to hide all meshes in the shape, for example, and then only " + "enable a few.\n" + + "@param hide new hidden state for all meshes\n\n" ) +{ + object->setAllMeshesHidden( hide ); +} + +DefineEngineMethod( ShapeBase, setMeshHidden, void, ( const char* name, bool hide ),, + "@brief Set the hidden state on the named shape mesh.\n\n" + + "@param name name of the mesh to hide/show\n" + "@param hide new hidden state for the mesh\n\n" ) +{ + object->setMeshHidden( name, hide ); +} + +// Some development-handy functions +#ifndef TORQUE_SHIPPING + +void ShapeBase::dumpMeshVisibility() +{ + if ( !mShapeInstance ) + return; + + const Vector &meshes = mShapeInstance->mMeshObjects; + + for ( U32 i = 0; i < meshes.size(); i++) + { + const TSShapeInstance::MeshObjectInstance &mesh = meshes[i]; + + const String &meshName = mDataBlock->mShape->getMeshName( i ); + + Con::printf( "%d - %s - forceHidden = %s, visibility = %f", + i, + meshName.c_str(), + mesh.forceHidden ? "true" : "false", + mesh.visible ); + } +} + +DefineEngineMethod( ShapeBase, dumpMeshVisibility, void, (),, + "@brief Print a list of visible and hidden meshes in the shape to the console " + "for debugging purposes.\n\n" + "@note Only in a SHIPPING build.\n") +{ + object->dumpMeshVisibility(); +} + +#endif // #ifndef TORQUE_SHIPPING + +//------------------------------------------------------------------------ +//These functions are duplicated in tsStatic, shapeBase, and interiorInstance. +//They each function a little differently; but achieve the same purpose of gathering +//target names/counts without polluting simObject. + +DefineEngineMethod( ShapeBase, getTargetName, const char*, ( S32 index ),, + "@brief Get the name of the indexed shape material.\n\n" + + "@param index index of the material to get (valid range is 0 - getTargetCount()-1).\n" + "@return the name of the indexed material.\n\n" + + "@see getTargetCount()\n") +{ + ShapeBase *obj = dynamic_cast< ShapeBase* > ( object ); + if(obj) + { + // Try to use the client object (so we get the reskinned targets in the Material Editor) + if ((ShapeBase*)obj->getClientObject()) + obj = (ShapeBase*)obj->getClientObject(); + + return obj->getShapeInstance()->getTargetName(index); + } + + return ""; +} + +DefineEngineMethod( ShapeBase, getTargetCount, S32, (),, + "@brief Get the number of materials in the shape.\n\n" + + "@return the number of materials in the shape.\n\n" + + "@see getTargetName()\n") +{ + ShapeBase *obj = dynamic_cast< ShapeBase* > ( object ); + if(obj) + { + // Try to use the client object (so we get the reskinned targets in the Material Editor) + if ((ShapeBase*)obj->getClientObject()) + obj = (ShapeBase*)obj->getClientObject(); + + return obj->getShapeInstance()->getTargetCount(); + } + + return -1; +} + +DefineEngineMethod( ShapeBase, changeMaterial, void, ( const char* mapTo, Material* oldMat, Material* newMat ),, + "@brief Change one of the materials on the shape.\n\n" + + "This method changes materials per mapTo with others. The material that " + "is being replaced is mapped to unmapped_mat as a part of this transition.\n" + + "@note Warning, right now this only sort of works. It doesn't do a live " + "update like it should.\n" + + "@param mapTo the name of the material target to remap (from getTargetName)\n" + "@param oldMat the old Material that was mapped \n" + "@param newMat the new Material to map\n\n" + + "@tsexample\n" + "// remap the first material in the shape\n" + "%mapTo = %obj.getTargetName( 0 );\n" + "%obj.changeMaterial( %mapTo, 0, MyMaterial );\n" + "@endtsexample\n" ) +{ + // if no valid new material, theres no reason for doing this + if( !newMat ) + { + Con::errorf("ShapeBase::changeMaterial failed: New material does not exist!"); + return; + } + + // initilize server/client versions + ShapeBase *serverObj = object; + ShapeBase *clientObj = dynamic_cast< ShapeBase* > ( object->getClientObject() ); + + // Check the mapTo name exists for this shape + S32 matIndex = serverObj->getShape()->materialList->getMaterialNameList().find_next(String(mapTo)); + if (matIndex < 0) + { + Con::errorf("ShapeBase::changeMaterial failed: Invalid mapTo name '%s'", mapTo); + return; + } + + // Lets remap the old material off, so as to let room for our current material room to claim its spot + if( oldMat ) + oldMat->mMapTo = String("unmapped_mat"); + + newMat->mMapTo = mapTo; + + // Map the material in the in the matmgr + MATMGR->mapMaterial( mapTo, newMat->mMapTo ); + + // Replace instances with the new material being traded in. For ShapeBase + // class we have to update the server/client objects separately so both + // represent our changes + delete serverObj->getShape()->materialList->mMatInstList[matIndex]; + serverObj->getShape()->materialList->mMatInstList[matIndex] = newMat->createMatInstance(); + if (clientObj) + { + delete clientObj->getShape()->materialList->mMatInstList[matIndex]; + clientObj->getShape()->materialList->mMatInstList[matIndex] = newMat->createMatInstance(); + } + + // Finish up preparing the material instances for rendering + const GFXVertexFormat *flags = getGFXVertexFormat(); + FeatureSet features = MATMGR->getDefaultFeatures(); + + serverObj->getShape()->materialList->getMaterialInst(matIndex)->init( features, flags ); + if (clientObj) + clientObj->getShapeInstance()->mMaterialList->getMaterialInst(matIndex)->init( features, flags ); +} + +DefineEngineMethod( ShapeBase, getModelFile, const char *, (),, + "@brief Get the model filename used by this shape.\n\n" + + "@return the shape filename\n\n" ) +{ + GameBaseData * datablock = object->getDataBlock(); + if( !datablock ) + return String::EmptyString; + + const char *fieldName = StringTable->insert( String("shapeFile") ); + return datablock->getDataField( fieldName, NULL ); +} diff --git a/Engine/source/T3D/shapeBase.h b/Engine/source/T3D/shapeBase.h new file mode 100644 index 000000000..96392041a --- /dev/null +++ b/Engine/source/T3D/shapeBase.h @@ -0,0 +1,1915 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SHAPEBASE_H_ +#define _SHAPEBASE_H_ + +#ifndef __RESOURCE_H__ + #include "core/resource.h" +#endif +#ifndef _GAMEBASE_H_ + #include "T3D/gameBase/gameBase.h" +#endif +#ifndef _MOVEMANAGER_H_ + #include "T3D/gameBase/moveManager.h" +#endif +#ifndef _COLOR_H_ + #include "core/color.h" +#endif +#ifndef _CONVEX_H_ + #include "collision/convex.h" +#endif +#ifndef _SCENERENDERSTATE_H_ + #include "scene/sceneRenderState.h" +#endif +#ifndef _NETSTRINGTABLE_H_ + #include "sim/netStringTable.h" +#endif +#ifndef _RENDERPASSMANAGER_H_ + #include "renderInstance/renderPassManager.h" +#endif +#ifndef _TSSHAPE_H_ + #include "ts/tsShape.h" +#endif +#ifndef _BITVECTOR_H_ + #include "core/bitVector.h" +#endif +#ifndef _LIGHTINFO_H_ + #include "lighting/lightInfo.h" +#endif +#ifndef _REFLECTOR_H_ + #include "scene/reflector.h" +#endif +#ifndef _DYNAMIC_CONSOLETYPES_H_ + #include "console/dynamicTypes.h" +#endif + + +class GFXCubemap; +class TSShapeInstance; +class SceneRenderState; +class TSThread; +class GameConnection; +struct CameraScopeQuery; +class ParticleEmitter; +class ParticleEmitterData; +class ProjectileData; +class ExplosionData; +struct DebrisData; +class ShapeBase; +class SFXSource; +class SFXTrack; +class SFXProfile; + +typedef void* Light; + + +//-------------------------------------------------------------------------- + +extern void collisionFilter(SceneObject* object,S32 key); +extern void defaultFilter(SceneObject* object,S32 key); + + +//-------------------------------------------------------------------------- +class ShapeBaseConvex : public Convex +{ + typedef Convex Parent; + friend class ShapeBase; + friend class Vehicle; + friend class RigidShape; + + protected: + ShapeBase* pShapeBase; + MatrixF* nodeTransform; + + public: + MatrixF* transform; + U32 hullId; + Box3F box; + + public: + ShapeBaseConvex() { mType = ShapeBaseConvexType; nodeTransform = 0; } + ShapeBaseConvex(const ShapeBaseConvex& cv) { + mObject = cv.mObject; + pShapeBase = cv.pShapeBase; + hullId = cv.hullId; + nodeTransform = cv.nodeTransform; + box = cv.box; + transform = 0; + } + + void findNodeTransform(); + const MatrixF& getTransform() const; + Box3F getBoundingBox() const; + Box3F getBoundingBox(const MatrixF& mat, const Point3F& scale) const; + Point3F support(const VectorF& v) const; + void getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf); + void getPolyList(AbstractPolyList* list); +}; + +//-------------------------------------------------------------------------- + +struct ShapeBaseImageData: public GameBaseData { + private: + typedef GameBaseData Parent; + + public: + enum Constants { + MaxStates = 31, ///< We get one less than state bits because of + /// the way data is packed. + + MaxShapes = 2, ///< The number of allowed shapes per image. Only + /// the first shape is required. + + MaxGenericTriggers = 4, ///< The number of generic triggers for the image. + + StandardImageShape = 0, ///< Shape index used for the standard image shape + FirstPersonImageShape = 1, ///< Shape index used for the optional first person image shape + + NumStateBits = 5, + }; + enum LightType { + NoLight = 0, + ConstantLight, + SpotLight, + PulsingLight, + WeaponFireLight, + NumLightTypes + }; + struct StateData { + StateData(); + const char* name; ///< State name + + /// @name Transition states + /// + /// @{ + + /// + struct Transition { + S32 loaded[2]; ///< NotLoaded/Loaded + S32 ammo[2]; ///< Noammo/ammo + S32 target[2]; ///< target/noTarget + S32 trigger[2]; ///< Trigger up/down + S32 altTrigger[2]; ///< Second trigger up/down + S32 wet[2]; ///< wet/notWet + S32 motion[2]; ///< NoMotion/Motion + S32 timeout; ///< Transition after delay + S32 genericTrigger[ShapeBaseImageData::MaxGenericTriggers][2]; ///< Generic trigger Out/In + } transition; + bool ignoreLoadedForReady; + + /// @} + + /// @name State attributes + /// @{ + + bool fire; ///< Can only have one fire state + bool altFire; ///< Can only have one alternate fire state + bool reload; ///< Can only have one reload state + bool ejectShell; ///< Should we eject a shell casing in this state? + bool allowImageChange; ///< Can we switch to another image while in this state? + /// + /// For instance, if we have a rocket launcher, the player + /// shouldn't be able to switch out while firing. So, + /// you'd set allowImageChange to false in firing states, and + /// true the rest of the time. + bool scaleAnimation; ///< Scale animation to fit the state timeout + bool scaleAnimationFP; ///< Scale animation to fit the state timeout while in first person + bool direction; ///< Animation direction + bool sequenceTransitionIn; ///< Do we transition to the state's sequence when we enter the state? + bool sequenceTransitionOut; ///< Do we transition to the new state's sequence when we leave the state? + bool sequenceNeverTransition; ///< Never allow a transition to this sequence. Often used for a fire sequence. + F32 sequenceTransitionTime; ///< The time to transition in or out of a sequence. + bool waitForTimeout; ///< Require the timeout to pass before advancing to the next + /// state. + F32 timeoutValue; ///< A timeout value; the effect of this value is determined + /// by the flags scaleAnimation and waitForTimeout + F32 energyDrain; ///< Sets the energy drain rate per second of this state. + /// + /// Energy is drained at energyDrain units/sec as long as + /// we are in this state. + enum LoadedState { + IgnoreLoaded, ///< Don't change loaded state. + Loaded, ///< Set us as loaded. + NotLoaded, ///< Set us as not loaded. + NumLoadedBits = 3 ///< How many bits to allocate to representing this state. (3 states needs 2 bits) + } loaded; ///< Is the image considered loaded? + enum SpinState { + IgnoreSpin, ///< Don't change spin state. + NoSpin, ///< Mark us as having no spin (ie, stop spinning). + SpinUp, ///< Mark us as spinning up. + SpinDown, ///< Mark us as spinning down. + FullSpin, ///< Mark us as being at full spin. + NumSpinBits = 3 ///< How many bits to allocate to representing this state. (5 states needs 3 bits) + } spin; ///< Spin thread state. (Used to control spinning weapons, e.g. chainguns) + enum RecoilState { + NoRecoil, + LightRecoil, + MediumRecoil, + HeavyRecoil, + NumRecoilBits = 3 + } recoil; ///< Recoil thread state. + /// + /// @note At this point, the only check being done is to see if we're in a + /// state which isn't NoRecoil; ie, no differentiation is made between + /// Light/Medium/Heavy recoils. Player::onImageRecoil() is the place + /// where this is handled. + bool flashSequence[MaxShapes];///< Is this a muzzle flash sequence? + /// + /// A muzzle flash sequence is used as a source to randomly display frames from, + /// so if this is a flashSequence, we'll display a random piece each frame. + S32 sequence[MaxShapes]; ///< Main thread sequence ID. + /// + /// + S32 sequenceVis[MaxShapes]; ///< Visibility thread sequence. + + StringTableEntry shapeSequence; ///< Sequence that is played on mounting shape + bool shapeSequenceScale; ///< Should the mounting shape's sequence playback be scaled + /// to the length of the state. + + const char* script; ///< Function on datablock to call when we enter this state; passed the id of + /// the imageSlot. + ParticleEmitterData* emitter; ///< A particle emitter; this emitter will emit as long as the gun is in this + /// this state. + SFXTrack* sound; + F32 emitterTime; ///< + S32 emitterNode[MaxShapes]; ///< Node ID on the shape to emit from + }; + + /// @name State Data + /// Individual state data used to initialize struct array + /// @{ + const char* fireStateName; + + const char* stateName [MaxStates]; + + const char* stateTransitionLoaded [MaxStates]; + const char* stateTransitionNotLoaded [MaxStates]; + const char* stateTransitionAmmo [MaxStates]; + const char* stateTransitionNoAmmo [MaxStates]; + const char* stateTransitionTarget [MaxStates]; + const char* stateTransitionNoTarget [MaxStates]; + const char* stateTransitionWet [MaxStates]; + const char* stateTransitionNotWet [MaxStates]; + const char* stateTransitionMotion [MaxStates]; + const char* stateTransitionNoMotion [MaxStates]; + const char* stateTransitionTriggerUp [MaxStates]; + const char* stateTransitionTriggerDown [MaxStates]; + const char* stateTransitionAltTriggerUp[MaxStates]; + const char* stateTransitionAltTriggerDown[MaxStates]; + const char* stateTransitionGeneric0In [MaxStates]; + const char* stateTransitionGeneric0Out [MaxStates]; + const char* stateTransitionGeneric1In [MaxStates]; + const char* stateTransitionGeneric1Out [MaxStates]; + const char* stateTransitionGeneric2In [MaxStates]; + const char* stateTransitionGeneric2Out [MaxStates]; + const char* stateTransitionGeneric3In [MaxStates]; + const char* stateTransitionGeneric3Out [MaxStates]; + const char* stateTransitionTimeout [MaxStates]; + F32 stateTimeoutValue [MaxStates]; + bool stateWaitForTimeout [MaxStates]; + + bool stateFire [MaxStates]; + bool stateAlternateFire [MaxStates]; + bool stateReload [MaxStates]; + bool stateEjectShell [MaxStates]; + F32 stateEnergyDrain [MaxStates]; + bool stateAllowImageChange [MaxStates]; + bool stateScaleAnimation [MaxStates]; + bool stateScaleAnimationFP [MaxStates]; + bool stateSequenceTransitionIn [MaxStates]; + bool stateSequenceTransitionOut [MaxStates]; + bool stateSequenceNeverTransition [MaxStates]; + F32 stateSequenceTransitionTime [MaxStates]; + bool stateDirection [MaxStates]; + StateData::LoadedState stateLoaded [MaxStates]; + StateData::SpinState stateSpin [MaxStates]; + StateData::RecoilState stateRecoil [MaxStates]; + const char* stateSequence [MaxStates]; + bool stateSequenceRandomFlash [MaxStates]; + + const char* stateShapeSequence [MaxStates]; + bool stateScaleShapeSequence [MaxStates]; + + bool stateIgnoreLoadedForReady [MaxStates]; + + SFXTrack* stateSound [MaxStates]; + const char* stateScript [MaxStates]; + + ParticleEmitterData* stateEmitter [MaxStates]; + F32 stateEmitterTime [MaxStates]; + const char* stateEmitterNode [MaxStates]; + /// @} + + /// @name Camera Shake ( while firing ) + /// @{ + bool shakeCamera; + VectorF camShakeFreq; + VectorF camShakeAmp; + /// @} + + /// Maximum number of sounds this image can play at a time. + /// Any value <= 0 indicates that it can play an infinite number of sounds. + S32 maxConcurrentSounds; + + /// If true it we will allow multiple timeout transitions to occur within + /// a single tick ( eg. they have a very small timeout ). + bool useRemainderDT; + + // + bool emap; ///< Environment mapping on? + bool correctMuzzleVector; ///< Adjust 1st person firing vector to eye's LOS point? + bool correctMuzzleVectorTP; ///< Adjust 3rd person firing vector to camera's LOS point? + bool firstPerson; ///< Render the image when in first person? + bool useFirstPersonShape; ///< Indicates the special first person shape should be used (true when shapeNameFP and useEyeOffset are defined) + bool useEyeOffset; ///< In first person, should we use the eyeTransform? + bool useEyeNode; ///< In first person, should we attach the camera to the image's eye node? Player still ultimately decides on what to do, + /// especially for multiple mounted images. + + bool animateAllShapes; ///< Indicates that all shapes should be animated in sync. + bool animateOnServer; ///< Indicates that the image should be animated on the server. In most cases + /// you'll want this set if you're using useEyeNode. You may also want to + /// set this if the muzzlePoint is animated while it shoots. You can set this + /// to false even if these previous cases are true if the image's shape is set + /// up in the correct position and orientation in the 'root' pose and none of + /// the nodes are animated at key times, such as the muzzlePoint essentially + /// remaining at the same position at the start of the fire state (it could + /// animate just fine after the projectile is away as the muzzle vector is only + /// calculated at the start of the state). You'll also want to set this to true + /// if you're animating the camera using an image's 'eye' node -- unless the movement + /// is very subtle and doesn't need to be reflected on the server. + + F32 scriptAnimTransitionTime; ///< The amount of time to transition between the previous sequence and new sequence + ///< when the script prefix has changed. + + StringTableEntry shapeName; ///< Name of shape to render. + StringTableEntry shapeNameFP; ///< Name of shape to render in first person (optional). + + StringTableEntry imageAnimPrefix; ///< Passed along to the mounting shape to modify + /// animation sequences played in 3rd person. [optional] + StringTableEntry imageAnimPrefixFP; ///< Passed along to the mounting shape to modify + /// animation sequences played in first person. [optional] + + U32 mountPoint; ///< Mount point for the image. + MatrixF mountOffset; ///< Mount point offset, so we know where the image is. + MatrixF eyeOffset; ///< Offset from eye for first person. + + ProjectileData* projectile; ///< Information about what projectile this + /// image fires. + + F32 mass; ///< Mass! + bool usesEnergy; ///< Does this use energy instead of ammo? + F32 minEnergy; ///< Minimum energy for the weapon to be operable. + bool accuFire; ///< Should we automatically make image's aim converge with the crosshair? + bool cloakable; ///< Is this image cloakable when mounted? + + /// @name Lighting + /// @{ + S32 lightType; ///< Indicates the type of the light. + /// + /// One of: ConstantLight, PulsingLight, WeaponFireLight. + ColorF lightColor; + S32 lightDuration; ///< The duration in SimTime of Pulsing or WeaponFire type lights. + F32 lightRadius; ///< Extent of light. + F32 lightBrightness; ///< Brightness of the light ( if it is WeaponFireLight ). + /// @} + + /// @name Shape Data + /// @{ + Resource shape[MaxShapes]; ///< Shape handle + bool shapeIsValid[MaxShapes]; ///< Indicates that the shape has been loaded and is valid + + U32 mCRC[MaxShapes]; ///< Checksum of shape. + /// + /// Calculated by the ResourceManager, see + /// ResourceManager::load(). + bool computeCRC; ///< Should the shape's CRC be checked? + MatrixF mountTransform[MaxShapes]; ///< Transformation to get to the mountNode. + /// @} + + /// @name Nodes + /// @{ + S32 retractNode[MaxShapes]; ///< Retraction node ID. + /// + /// When the player bumps against an object and the image is retracted to + /// avoid having it interpenetrating the object, it is retracted towards + /// this node. + S32 muzzleNode[MaxShapes]; ///< Muzzle node ID. + /// + /// + S32 ejectNode[MaxShapes]; ///< Ejection node ID. + /// + /// The eject node is the node on the image from which shells are ejected. + S32 emitterNode[MaxShapes]; ///< Emitter node ID. + /// + /// The emitter node is the node from which particles are emitted. + S32 eyeMountNode[MaxShapes]; ///< eyeMount node ID. Optionally used to mount an image to the player's + /// eye node for first person. + S32 eyeNode[MaxShapes]; ///< Eye node ID. Optionally used to attach the camera to for camera motion + /// control from the image. + /// @} + + /// @name Animation + /// @{ + S32 spinSequence[MaxShapes]; ///< ID of the spin animation sequence. + S32 ambientSequence[MaxShapes]; ///< ID of the ambient animation sequence. + + bool isAnimated[MaxShapes]; ///< This image contains at least one animated states + bool hasFlash[MaxShapes]; ///< This image contains at least one muzzle flash animation state + S32 fireState; ///< The ID of the fire state. + S32 altFireState; ///< The ID of the alternate fire state. + S32 reloadState; ///< The ID of the reload state + /// @} + + /// @name Shell casing data + /// @{ + DebrisData * casing; ///< Information about shell casings. + + S32 casingID; ///< ID of casing datablock. + /// + /// When the network tells the client about the casing, it + /// it transmits the ID of the datablock. The datablocks + /// having previously been transmitted, all the client + /// needs to do is call Sim::findObject() and look up the + /// the datablock. + + Point3F shellExitDir; ///< Vector along which to eject shells from the image. + F32 shellExitVariance; ///< Variance from this vector in degrees. + F32 shellVelocity; ///< Velocity with which to eject shell casings. + /// @} + + /// @name State Array + /// + /// State array is initialized onAdd from the individual state + /// struct array elements. + /// + /// @{ + StateData state[MaxStates]; ///< Array of states. + bool statesLoaded; ///< Are the states loaded yet? + /// @} + + /// @name Infrastructure + /// + /// Miscellaneous inherited methods. + /// @{ + + DECLARE_CONOBJECT(ShapeBaseImageData); + ShapeBaseImageData(); + ~ShapeBaseImageData(); + bool onAdd(); + bool preload(bool server, String &errorStr); + S32 lookupState(const char* name); ///< Get a state by name. + static void initPersistFields(); + virtual void packData(BitStream* stream); + virtual void unpackData(BitStream* stream); + + void inspectPostApply(); + + /// @} + + /// @name Callbacks + /// @{ + DECLARE_CALLBACK( void, onMount, ( ShapeBase* obj, S32 slot, F32 dt ) ); + DECLARE_CALLBACK( void, onUnmount, ( ShapeBase* obj, S32 slot, F32 dt ) ); + /// @} +}; + +typedef ShapeBaseImageData::LightType ShapeBaseImageLightType; +typedef ShapeBaseImageData::StateData::LoadedState ShapeBaseImageLoadedState; +typedef ShapeBaseImageData::StateData::SpinState ShapeBaseImageSpinState; +typedef ShapeBaseImageData::StateData::RecoilState ShapeBaseImageRecoilState; + +DefineEnumType( ShapeBaseImageLightType ); +DefineEnumType( ShapeBaseImageLoadedState ); +DefineEnumType( ShapeBaseImageSpinState ); +DefineEnumType( ShapeBaseImageRecoilState ); + +//-------------------------------------------------------------------------- +/// @nosubgrouping +struct ShapeBaseData : public GameBaseData { + private: + typedef GameBaseData Parent; + + static bool _setMass( void* object, const char* index, const char* data ); + +public: + /// Various constants relating to the ShapeBaseData + enum Constants { + MaxCollisionShapes = 8, + AIRepairNode = 31 + }; + + // TODO: These are only really used in Basic Lighting + // mode... we should probably move them somewhere else. + bool shadowEnable; + U32 shadowSize; + F32 shadowMaxVisibleDistance; + F32 shadowProjectionDistance; + F32 shadowSphereAdjust; + + + StringTableEntry shapeName; + StringTableEntry cloakTexName; + + String cubeDescName; + U32 cubeDescId; + ReflectorDesc *reflectorDesc; + + /// @name Destruction + /// + /// Everyone likes to blow things up! + /// @{ + DebrisData * debris; + S32 debrisID; + StringTableEntry debrisShapeName; + Resource debrisShape; + + ExplosionData* explosion; + S32 explosionID; + + ExplosionData* underwaterExplosion; + S32 underwaterExplosionID; + /// @} + + /// @name Physical Properties + /// @{ + F32 mass; + F32 drag; + F32 density; + F32 maxEnergy; + F32 maxDamage; + F32 repairRate; ///< Rate per tick. + + F32 disabledLevel; + F32 destroyedLevel; + /// @} + + /// @name 3rd Person Camera + /// @{ + F32 cameraMaxDist; ///< Maximum distance from eye + F32 cameraMinDist; ///< Minumumistance from eye + /// @} + + /// @name Camera FOV + /// + /// These are specified in degrees. + /// @{ + F32 cameraDefaultFov; ///< Default vertical FOV in degrees. + F32 cameraMinFov; ///< Min vertical FOV allowed in degrees. + F32 cameraMaxFov; ///< Max vertical FOV allowed in degrees. + /// @} + + /// @name Data initialized on preload + /// @{ + + Resource mShape; ///< Shape handle + U32 mCRC; + bool computeCRC; + + S32 eyeNode; ///< Shape's eye node index + S32 earNode; ///< Shape's ear node index + S32 cameraNode; ///< Shape's camera node index + S32 mountPointNode[SceneObject::NumMountPoints]; ///< Node index of mountPoint + S32 debrisDetail; ///< Detail level used to generate debris + S32 damageSequence; ///< Damage level decals + S32 hulkSequence; ///< Destroyed hulk + + bool observeThroughObject; // observe this object through its camera transform and default fov + + /// @name Collision Data + /// @{ + Vector collisionDetails; ///< Detail level used to collide with. + /// + /// These are detail IDs, see TSShape::findDetail() + Vector collisionBounds; ///< Detail level bounding boxes. + + Vector LOSDetails; ///< Detail level used to perform line-of-sight queries against. + /// + /// These are detail IDs, see TSShape::findDetail() + /// @} + + /// @name Misc. Settings + /// @{ + bool firstPersonOnly; ///< Do we allow only first person view of this image? + bool useEyePoint; ///< Do we use this object's eye point to view from? + bool isInvincible; ///< If set, object cannot take damage (won't show up with damage bar either) + bool renderWhenDestroyed; ///< If set, will not render this object when destroyed. + + bool inheritEnergyFromMount; + + /// @} + + virtual bool preload(bool server, String &errorStr); + void computeAccelerator(U32 i); + S32 findMountPoint(U32 n); + + /// @name Infrastructure + /// The derived class should provide the following: + /// @{ + DECLARE_CONOBJECT(ShapeBaseData); + ShapeBaseData(); + ~ShapeBaseData(); + static void initPersistFields(); + virtual void packData(BitStream* stream); + virtual void unpackData(BitStream* stream); + /// @} + + /// @name Callbacks + /// @{ + DECLARE_CALLBACK( void, onEnabled, ( ShapeBase* obj, const char* lastState ) ); + DECLARE_CALLBACK( void, onDisabled, ( ShapeBase* obj, const char* lastState ) ); + DECLARE_CALLBACK( void, onDestroyed, ( ShapeBase* obj, const char* lastState ) ); + DECLARE_CALLBACK( void, onImpact, ( ShapeBase* obj, SceneObject* collObj, VectorF vec, F32 len ) ); + DECLARE_CALLBACK( void, onCollision, ( ShapeBase* obj, SceneObject* collObj, VectorF vec, F32 len ) ); + DECLARE_CALLBACK( void, onDamage, ( ShapeBase* obj, F32 delta ) ); + DECLARE_CALLBACK( void, onTrigger, ( ShapeBase* obj, S32 index, bool state ) ); + DECLARE_CALLBACK( void, onEndSequence, ( ShapeBase* obj, S32 slot ) ); + DECLARE_CALLBACK( void, onForceUncloak, ( ShapeBase* obj, const char* reason ) ); + /// @} +}; + + +//---------------------------------------------------------------------------- + +class WaterObject; +class CameraShake; + +/// ShapeBase is the renderable shape from which most of the scriptable objects +/// are derived, including the player, vehicle and items classes. ShapeBase +/// provides basic shape loading, audio channels, and animation as well as damage +/// (and damage states), energy, and the ability to mount images and objects. +/// +/// @nosubgrouping +class ShapeBase : public GameBase, public ISceneLight +{ + friend class ShapeBaseConvex; + friend struct ShapeBaseImageData; + friend void waterFind(SceneObject*, void*); + friend void physicalZoneFind(SceneObject*, void*); + +public: + typedef GameBase Parent; + + enum PublicConstants { + ThreadSequenceBits = 6, + MaxSequenceIndex = (1 << ThreadSequenceBits) - 1, + EnergyLevelBits = 5, + DamageLevelBits = 6, + DamageStateBits = 2, + // The thread and image limits should not be changed without + // also changing the ShapeBaseMasks enum values declared + // further down. + MaxSoundThreads = 4, ///< Should be a power of 2 + MaxScriptThreads = 4, ///< Should be a power of 2 + MaxMountedImages = 4, ///< Should be a power of 2 + MaxImageEmitters = 3, + NumImageBits = 3, + ShieldNormalBits = 8, + CollisionTimeoutValue = 250 ///< Timeout in ms. + }; + + /// This enum indexes into the sDamageStateName array + enum DamageState { + Enabled, + Disabled, + Destroyed, + NumDamageStates, + NumDamageStateBits = 2, ///< Should be log2 of the number of states. + }; + +protected: + ShapeBaseData* mDataBlock; ///< Datablock + bool mIsAiControlled; ///< Is this object AI controlled? + //GameConnection* mControllingClient; ///< Controlling client + ShapeBase* mControllingObject; ///< Controlling object + bool mTrigger[MaxTriggerKeys]; ///< What triggers are set, if any. + + + /// @name Scripted Sound + /// @{ + struct Sound { + bool play; ///< Are we playing this sound? + SimTime timeout; ///< Time until we stop playing this sound. + SFXTrack* profile; ///< Profile on server + SFXSource* sound; ///< Sound on client + }; + Sound mSoundThread[MaxSoundThreads]; + /// @} + + /// @name Scripted Animation Threads + /// @{ + + struct Thread { + /// State of the animation thread. + enum State { + Play, Stop, Pause, Destroy + }; + TSThread* thread; ///< Pointer to 3space data. + U32 state; ///< State of the thread + /// + /// @see Thread::State + S32 sequence; ///< The animation sequence which is running in this thread. + F32 timescale; ///< Timescale + U32 sound; ///< Handle to sound. + bool atEnd; ///< Are we at the end of this thread? + F32 position; + }; + Thread mScriptThread[MaxScriptThreads]; + + /// @} + + /// @name Invincibility + /// @{ + F32 mInvincibleCount; + F32 mInvincibleTime; + F32 mInvincibleSpeed; + F32 mInvincibleDelta; + F32 mInvincibleEffect; + F32 mInvincibleFade; + bool mInvincibleOn; + /// @} + + /// @name Motion + /// @{ + bool mMoveMotion; ///< Indicates that a Move has come in requesting x, y or z motion + /// @} + +protected: + + // ShapeBase pointer to our mount object if it is ShapeBase, else it is NULL. + ShapeBase *mShapeBaseMount; + + /// @name Mounted Images + /// @{ + + /// An image mounted on a shapebase. + struct MountedImage { + ShapeBaseImageData* dataBlock; + ShapeBaseImageData::StateData *state; + ShapeBaseImageData* nextImage; + NetStringHandle skinNameHandle; + NetStringHandle nextSkinNameHandle; + String appliedSkinName; + NetStringHandle scriptAnimPrefix; ///< The script based anim prefix + + /// @name State + /// + /// Variables tracking the state machine + /// representing this specific mounted image. + /// @{ + + bool loaded; ///< Is the image loaded? + bool nextLoaded; ///< Is the next state going to result in the image being loaded? + F32 delayTime; ///< Time till next state. + F32 rDT; ///< Remainder delta time. Used internally. + U32 fireCount; ///< Fire skip count. + /// + /// This is incremented every time the triggerDown bit is changed, + /// so that the engine won't be too confused if the player toggles the + /// trigger a bunch of times in a short period. + /// + /// @note The network deals with this variable at 3-bit precision, so it + /// can only range 0-7. + /// + /// @see ShapeBase::setImageState() + U32 altFireCount; ///< Alternate fire skip count. + ///< @see fireCount + + U32 reloadCount; ///< Reload skip count. + ///< @see fireCount + + bool triggerDown; ///< Is the trigger down? + bool altTriggerDown; ///< Is the second trigger down? + + bool ammo; ///< Do we have ammo? + /// + /// May be true based on either energy OR ammo. + + bool target; ///< Have we acquired a targer? + bool wet; ///< Is the weapon wet? + + bool motion; ///< Is the player in motion? + + bool genericTrigger[ShapeBaseImageData::MaxGenericTriggers]; ///< Generic triggers not assigned to anything in particular. These + /// may be used to indicate some transition should occur. + /// @} + + /// @name 3space + /// + /// Handles to threads and shapeinstances in the 3space system. + /// @{ + TSShapeInstance* shapeInstance[ShapeBaseImageData::MaxShapes]; + TSThread *ambientThread[ShapeBaseImageData::MaxShapes]; + TSThread *visThread[ShapeBaseImageData::MaxShapes]; + TSThread *animThread[ShapeBaseImageData::MaxShapes]; + TSThread *flashThread[ShapeBaseImageData::MaxShapes]; + TSThread *spinThread[ShapeBaseImageData::MaxShapes]; + + bool doAnimateAllShapes; ///< Should all threads animate across all shapes to keep them in sync? + bool forceAnimateAllShapes; ///< If the mounted image's owner is being controlled by the client + /// and the image's datablock animateAllShapes field is true + /// then set this to true and pass along to the client. This will help + /// in the cases where the client's control object is ghosted but does + /// not yet have its controlling client set correctly due to networking + /// order of operations. All this for the MountedImage::updateDoAnimateAllShapes() + /// optimization. + U32 lastShapeIndex; ///< Tracks the last shape index. + /// @} + + /// @name Effects + /// + /// Variables relating to lights, sounds, and particles. + /// @{ + SimTime lightStart; ///< Starting time for light flashes. + LightInfo* lightInfo; ///< The real light (if any) associated with this weapon image. + + Vector mSoundSources; ///< Vector of currently playing sounds + void updateSoundSources(const MatrixF& renderTransform); + void addSoundSource(SFXSource* source); + + /// Represent the state of a specific particle emitter on the image. + struct ImageEmitter { + S32 node; + F32 time; + SimObjectPtr emitter; + }; + ImageEmitter emitter[MaxImageEmitters]; + + /// @} + + // + MountedImage(); + ~MountedImage(); + + void updateDoAnimateAllShapes(const ShapeBase* owner); + }; + MountedImage mMountedImageList[MaxMountedImages]; + + /// @} + + /// @name Render settings + /// @{ + + TSShapeInstance* mShapeInstance; + Convex * mConvexList; + NetStringHandle mSkinNameHandle; + String mAppliedSkinName; + + NetStringHandle mShapeNameHandle; ///< Name sent to client + /// @} + + /// @name Physical Properties + /// @{ + + F32 mEnergy; ///< Current enery level. + F32 mRechargeRate; ///< Energy recharge rate (in units/tick). + + F32 mMass; ///< Mass. + F32 mOneOverMass; ///< Inverse of mass. + /// @note This is used to optimize certain physics calculations. + + /// @} + + /// @name Physical Properties + /// + /// Properties for the current object, which are calculated + /// based on the container we are in. + /// + /// @see ShapeBase::updateContainer() + /// @see ShapeBase::mContainer + /// @{ + F32 mDrag; ///< Drag. + F32 mBuoyancy; ///< Buoyancy factor. + String mLiquidType; ///< Type of liquid (if any) we are in. + F32 mLiquidHeight; ///< Height of liquid around us (from 0..1). + F32 mWaterCoverage; ///< Percent of this object covered by water + + Point3F mAppliedForce; + F32 mGravityMod; + /// @} + + F32 mDamageFlash; + F32 mWhiteOut; + + bool mFlipFadeVal; + + /// Last shield direction (cur. unused) + Point3F mShieldNormal; + + /// Camera shake caused by weapon fire. + CameraShake *mWeaponCamShake; + + public: + + /// @name Collision Notification + /// This is used to keep us from spamming collision notifications. When + /// a collision occurs, we add to this list; then we don't notify anyone + /// of the collision until the CollisionTimeout expires (which by default + /// occurs in 1/10 of a second). + /// + /// @see notifyCollision(), queueCollision() + /// @{ + struct CollisionTimeout + { + CollisionTimeout* next; + SceneObject* object; + U32 objectNumber; + SimTime expireTime; + VectorF vector; + }; + CollisionTimeout* mTimeoutList; + static CollisionTimeout* sFreeTimeoutList; + + /// Go through all the items in the collision queue and call onCollision on them all + /// @see onCollision + void notifyCollision(); + + /// Add a collision to the queue of collisions waiting to be handled @see onCollision + /// @param object Object collision occurs with + /// @param vec Vector along which collision occurs + void queueCollision( SceneObject *object, const VectorF &vec); + + /// @see SceneObject + virtual void onCollision( SceneObject *object, const VectorF &vec ); + + /// @} + protected: + + /// @name Damage + /// @{ + F32 mDamage; + F32 mRepairRate; + F32 mRepairReserve; + DamageState mDamageState; + TSThread *mDamageThread; + TSThread *mHulkThread; + VectorF damageDir; + /// @} + + /// @name Cloaking + /// @{ + bool mCloaked; + F32 mCloakLevel; +// TextureHandle mCloakTexture; + /// @} + + /// @name Fading + /// @{ + bool mFadeOut; + bool mFading; + F32 mFadeVal; + F32 mFadeElapsedTime; + F32 mFadeTime; + F32 mFadeDelay; +public: + F32 getFadeVal() { return mFadeVal; } + /// @} +protected: + + /// @name Control info + /// @{ + F32 mCameraFov; ///< The camera vertical FOV in degrees. + bool mIsControlled; ///< Client side controlled flag + + /// @} +public: + static U32 sLastRenderFrame; + +protected: + + U32 mLastRenderFrame; + F32 mLastRenderDistance; + + /// Do a reskin if necessary. + virtual void reSkin(); + + /// This recalculates the total mass of the object, and all mounted objects + void updateMass(); + + /// @name Image Manipulation + /// @{ + + /// Utility function to call script functions which have to do with ShapeBase + /// objects. + /// @param imageSlot Image Slot id + /// @param function Function + void scriptCallback(U32 imageSlot,const char* function); + + /// Assign a ShapeBaseImage to an image slot + /// @param imageSlot Image Slot ID + /// @param imageData ShapeBaseImageData to assign + /// @param skinNameHandle Skin texture name + /// @param loaded Is the image loaded? + /// @param ammo Does the image have ammo? + /// @param triggerDown Is the trigger on this image down? + /// @param altTriggerDown Is the second trigger on this image down? + /// @param target Does the image have a target? + virtual void setImage( U32 imageSlot, + ShapeBaseImageData* imageData, + NetStringHandle &skinNameHandle, + bool loaded = true, bool ammo = false, + bool triggerDown = false, + bool altTriggerDown = false, + bool motion = false, + bool genericTrigger0 = false, + bool genericTrigger1 = false, + bool genericTrigger2 = false, + bool genericTrigger3 = false, + bool target = false ); + + /// Clear out an image slot + /// @param imageSlot Image slot id + void resetImageSlot(U32 imageSlot); + + /// Get the firing action state of the image + /// @param imageSlot Image slot id + U32 getImageFireState(U32 imageSlot); + + /// Get the alternate firing action state of the image + /// @param imageSlot Image slot id + U32 getImageAltFireState(U32 imageSlot); + + /// Get the reload action state of the image + /// @param imageSlot Image slot id + U32 getImageReloadState(U32 imageSlot); + + /// Sets the state of the image by state index + /// @param imageSlot Image slot id + /// @param state State id + /// @param force Force image to state or let it finish then change + void setImageState(U32 imageSlot, U32 state, bool force = false); + + void updateAnimThread(U32 imageSlot, S32 imageShapeIndex, ShapeBaseImageData::StateData* lastState=NULL); + + /// Get the animation prefix for the image + /// @param imageSlot Image slot id + /// @param imageShapeIndex Shape index (1st person, etc.) used to look up the prefix text + virtual const char* getImageAnimPrefix(U32 imageSlot, S32 imageShapeIndex) { return ""; } + + /// Advance animation on a image + /// @param imageSlot Image slot id + /// @param dt Change in time since last animation update + void updateImageAnimation(U32 imageSlot, F32 dt); + + /// Advance state of image + /// @param imageSlot Image slot id + /// @param dt Change in time since last state update + void updateImageState(U32 imageSlot,F32 dt); + + /// Start up the particle emitter for the this shapebase + /// @param image Mounted image + /// @param state State of shape base image + void startImageEmitter(MountedImage &image,ShapeBaseImageData::StateData &state); + + /// Get light information for a mounted image + /// @param imageSlot Image slot id + Light* getImageLight(U32 imageSlot); + + /// Get the shape index to use for a mounted image + /// @param image Mounted image + U32 getImageShapeIndex(const MountedImage& image) const; + + /// @} + + /// Prune out non looping sounds from the sound manager which have expired + void updateServerAudio(); + + /// Updates the audio state of the supplied sound + /// @param st Sound + void updateAudioState(Sound& st); + + /// Recalculates the spacial sound based on the current position of the object + /// emitting the sound. + void updateAudioPos(); + + /// Update bouyency and drag properties + void updateContainer(); + + /// @name Events + /// @{ + virtual void onDeleteNotify(SimObject*); + virtual void onImage(U32 imageSlot, bool unmount); + virtual void onImageRecoil(U32 imageSlot,ShapeBaseImageData::StateData::RecoilState); + virtual void onImageStateAnimation(U32 imageSlot, const char* seqName, bool direction, bool scaleToState, F32 stateTimeOutValue); + virtual void onImageAnimThreadChange(U32 imageSlot, S32 imageShapeIndex, ShapeBaseImageData::StateData* lastState, const char* anim, F32 pos, F32 timeScale, bool reset=false); + virtual void onImageAnimThreadUpdate(U32 imageSlot, S32 imageShapeIndex, F32 dt); + virtual void ejectShellCasing( U32 imageSlot ); + virtual void updateDamageLevel(); + virtual void updateDamageState(); + virtual void blowUp(); + virtual void onImpact(SceneObject* obj, VectorF vec); + virtual void onImpact(VectorF vec); + /// @} + + /// The inner prep render function that does the + /// standard work to render the shapes. + void _prepRenderImage( SceneRenderState* state, + bool renderSelf, + bool renderMountedImages ); + + /// Renders the shape bounds as well as the + /// bounds of all mounted shape images. + void _renderBoundingBox( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* ); + + void emitDust( ParticleEmitter* emitter, F32 triggerHeight, const Point3F& offset, U32 numMilliseconds, const Point3F& axis = Point3F::Zero ); + +public: + ShapeBase(); + ~ShapeBase(); + + TSShapeInstance* getShapeInstance() { return mShapeInstance; } + + static void initPersistFields(); + static bool _setFieldSkin( void *object, const char *index, const char *data ); + static const char *_getFieldSkin( void *object, const char *data ); + + /// @name Network state masks + /// @{ + + /// + enum ShapeBaseMasks { + NameMask = Parent::NextFreeMask, + DamageMask = Parent::NextFreeMask << 1, + NoWarpMask = Parent::NextFreeMask << 2, + CloakMask = Parent::NextFreeMask << 3, + ShieldMask = Parent::NextFreeMask << 4, + InvincibleMask = Parent::NextFreeMask << 5, + SkinMask = Parent::NextFreeMask << 6, + MeshHiddenMask = Parent::NextFreeMask << 7, + SoundMaskN = Parent::NextFreeMask << 8, ///< Extends + MaxSoundThreads bits + ThreadMaskN = SoundMaskN << MaxSoundThreads, ///< Extends + MaxScriptThreads bits + ImageMaskN = ThreadMaskN << MaxScriptThreads, ///< Extends + MaxMountedImage bits + NextFreeMask = ImageMaskN << MaxMountedImages + }; + + enum BaseMaskConstants { + SoundMask = (SoundMaskN << MaxSoundThreads) - SoundMaskN, + ThreadMask = (ThreadMaskN << MaxScriptThreads) - ThreadMaskN, + ImageMask = (ImageMaskN << MaxMountedImages) - ImageMaskN + }; + + /// @} + + static F32 sWhiteoutDec; + static F32 sDamageFlashDec; + static F32 sFullCorrectionDistance; + static F32 sCloakSpeed; // Time to cloak, in seconds + + CubeReflector mCubeReflector; + + /// @name Initialization + /// @{ + + bool onAdd(); + void onRemove(); + void onSceneRemove(); + static void consoleInit(); + bool onNewDataBlock( GameBaseData *dptr, bool reload ); + + /// @} + + /// @name Name & Skin tags + /// @{ + void setShapeName(const char*); + const char* getShapeName(); + void setSkinName(const char*); + const char* getSkinName(); + /// @} + + /// @name Mesh Visibility + /// @{ + +protected: + + /// A bit vector of the meshes forced to be hidden. + BitVector mMeshHidden; + + /// Sync the shape instance with the hidden mesh bit vector. + void _updateHiddenMeshes(); + +public: + + /// Change the hidden state on all the meshes. + void setAllMeshesHidden( bool forceHidden ); + + /// Set the force hidden state on a mesh. + void setMeshHidden( S32 meshIndex, bool forceHidden ); + + /// Set the force hidden state on a named mesh. + void setMeshHidden( const char *meshName, bool forceHidden ); + +#ifndef TORQUE_SHIPPING + + /// Prints the list of meshes and their visibility state + /// to the console for debugging purposes. + void dumpMeshVisibility(); + +#endif + + /// @} + +public: + + /// @name Basic attributes + /// @{ + + /// Sets the amount of damage on this object. + void setDamageLevel(F32 damage); + + /// Changes the object's damage state. + /// @param state New state of the object + void setDamageState(DamageState state); + + /// Changes the object's damage state, based on a named state. + /// @see setDamageState + /// @param state New state of the object as a string. + bool setDamageState(const char* state); + + /// Returns the name of the current damage state as a string. + const char* getDamageStateName(); + + /// Returns the current damage state. + DamageState getDamageState() { return mDamageState; } + + /// Returns true if the object is destroyed. + bool isDestroyed() { return mDamageState == Destroyed; } + + /// Sets the rate at which the object regenerates damage. + /// + /// @param rate Repair rate in units/second. + void setRepairRate(F32 rate) { mRepairRate = rate; } + + /// Returns damage amount. + F32 getDamageLevel() { return mDamage; } + + /// Returns the damage percentage. + /// + /// @return Damage factor, between 0.0 - 1.0 + F32 getDamageValue(); + + /// Returns the rate at which the object regenerates damage + F32 getRepairRate() { return mRepairRate; } + + /// Adds damage to an object + /// @param amount Amount of of damage to add + void applyDamage(F32 amount); + + /// Removes damage to an object + /// @param amount Amount to repair object by + void applyRepair(F32 amount); + + /// Sets the direction from which the damage is coming + /// @param vec Vector indicating the direction of the damage + void setDamageDir(const VectorF& vec) { damageDir = vec; } + + /// Sets the level of energy for this object + /// @param energy Level of energy to assign to this object + virtual void setEnergyLevel(F32 energy); + + /// Sets the rate at which the energy replentishes itself + /// @param rate Rate at which energy restores + void setRechargeRate(F32 rate) { mRechargeRate = rate; } + + /// Returns the amount of energy in the object + F32 getEnergyLevel(); + + /// Returns the percentage of energy, 0.0 - 1.0 + F32 getEnergyValue(); + + /// Returns the recharge rate + F32 getRechargeRate() { return mRechargeRate; } + + /// @} + + /// @name Script sounds + /// @{ + + /// Plays an audio sound from a mounted object + /// @param slot Mount slot ID + /// @param track Audio track to play + void playAudio(U32 slot,SFXTrack* track); + void playAudio( U32 slot, SFXProfile* profile ) { playAudio( slot, ( SFXTrack* ) profile ); } + + /// Stops audio from a mounted object + /// @param slot Mount slot ID + void stopAudio(U32 slot); + /// @} + + /// @name Script animation + /// @{ + + const char *getThreadSequenceName( U32 slot ); + + /// Sets the animation thread for a mounted object + /// @param slot Mount slot ID + /// @param seq Sequence id + /// @param reset Reset the sequence + bool setThreadSequence(U32 slot, S32 seq, bool reset = true); + + /// Update the animation thread + /// @param st Thread to update + void updateThread(Thread& st); + + /// Stop the current thread from playing on a mounted object + /// @param slot Mount slot ID + bool stopThread(U32 slot); + + /// Destroys the given animation thread + /// @param slot Mount slot ID + bool destroyThread(U32 slot); + + /// Pause the running animation thread + /// @param slot Mount slot ID + bool pauseThread(U32 slot); + + /// Start playing the running animation thread again + /// @param slot Mount slot ID + bool playThread(U32 slot); + + /// Set the thread position + /// @param slot Mount slot ID + /// @param pos Position + bool setThreadPosition( U32 slot, F32 pos ); + + /// Toggle the thread as reversed or normal (For example, sidestep-right reversed is sidestep-left) + /// @param slot Mount slot ID + /// @param forward True if the animation is to be played normally + bool setThreadDir(U32 slot,bool forward); + + /// Set the thread time scale + /// @param slot Mount slot ID + /// @param timescale Timescale + bool setThreadTimeScale( U32 slot, F32 timeScale ); + + /// Start the sound associated with an animation thread + /// @param thread Thread + void startSequenceSound(Thread& thread); + + /// Stop the sound associated with an animation thread + /// @param thread Thread + void stopThreadSound(Thread& thread); + + /// Advance all animation threads attached to this shapebase + /// @param dt Change in time from last call to this function + void advanceThreads(F32 dt); + /// @} + + /// @name Cloaking + /// @{ + + /// Force uncloaking of object + /// @param reason Reason this is being forced to uncloak, this is passed directly to script control + void forceUncloak(const char *reason); + + /// Set cloaked state of object + /// @param cloaked True if object is cloaked + void setCloakedState(bool cloaked); + + /// Returns true if object is cloaked + bool getCloakedState(); + + /// Returns level of cloaking, as it's not an instant "now you see it, now you don't" + F32 getCloakLevel(); + /// @} + + /// @name Mounted objects + /// @{ + virtual void onMount( SceneObject *obj, S32 node ); + virtual void onUnmount( SceneObject *obj,S32 node ); + virtual void getMountTransform( S32 index, const MatrixF &xfm, MatrixF *outMat ); + virtual void getRenderMountTransform( F32 delta, S32 index, const MatrixF &xfm, MatrixF *outMat ); + /// @} + + /// Returns where the AI should be to repair this object + /// + /// @note Legacy code from Tribes 2, but still works + Point3F getAIRepairPoint(); + + /// @name Mounted Images + /// @{ + + /// Mount an image (ShapeBaseImage) onto an image slot + /// @param image ShapeBaseImage to mount + /// @param imageSlot Image mount point + /// @param loaded True if weapon is loaded (it assumes it's a weapon) + /// @param skinNameHandle Skin name for object + virtual bool mountImage(ShapeBaseImageData* image,U32 imageSlot,bool loaded, NetStringHandle &skinNameHandle); + + /// Unmount an image from a slot + /// @param imageSlot Mount point + virtual bool unmountImage(U32 imageSlot); + + /// Gets the information on the image mounted in a slot + /// @param imageSlot Mount point + ShapeBaseImageData* getMountedImage(U32 imageSlot); + + /// Gets the mounted image on on a slot + /// @param imageSlot Mount Point + MountedImage* getImageStruct(U32 imageSlot); + + TSShapeInstance* getImageShapeInstance(U32 imageSlot) + { + const MountedImage &image = mMountedImageList[imageSlot]; + U32 imageShapeIndex = getImageShapeIndex(image); + if(image.dataBlock && image.shapeInstance[imageShapeIndex]) + return image.shapeInstance[imageShapeIndex]; + return NULL; + } + + /// Gets the next image which will be put in an image slot + /// @see setImageState + /// @param imageSlot mount Point + ShapeBaseImageData* getPendingImage(U32 imageSlot); + + + /// Returns true if the mounted image is firing + /// @param imageSlot Mountpoint + bool isImageFiring(U32 imageSlot); + + /// Returns true if the mounted image is alternate firing + /// @param imageSlot Mountpoint + bool isImageAltFiring(U32 imageSlot); + + /// Returns true if the mounted image is reloading + /// @param imageSlot Mountpoint + bool isImageReloading(U32 imageSlot); + + /// This will return true if, when triggered, the object will fire. + /// @param imageSlot mount point + /// @param ns Used internally for recursion, do not mess with + /// @param depth Used internally for recursion, do not mess with + bool isImageReady(U32 imageSlot,U32 ns = (U32)-1,U32 depth = 0); + + /// Returns true if the specified image is mounted + /// @param image ShapeBase image + bool isImageMounted(ShapeBaseImageData* image); + + /// Returns the slot which the image specified is mounted on + /// @param image Image to test for + S32 getMountSlot(ShapeBaseImageData* image); + + /// Returns the skin for the image in a slot + /// @param imageSlot Image slot to get the skin from + NetStringHandle getImageSkinTag(U32 imageSlot); + + /// Check if the given state exists on the mounted Image + /// @param imageSlot Image slot id + /// @param state Image state to check for + bool hasImageState(U32 imageSlot, const char* state); + + /// Returns the image state as a string + /// @param imageSlot Image slot to check state + const char* getImageState(U32 imageSlot); + + /// Sets the generic trigger state of the image + /// @param imageSlot Image slot + /// @param trigger Generic trigger number 0-3 + /// @param state True if generic trigger is down + void setImageGenericTriggerState(U32 imageSlot, U32 trigger, bool state); + + /// Returns the generic trigger state of the image + /// @param imageSlot Image slot + /// @param trigger Generic trigger number 0-3 + bool getImageGenericTriggerState(U32 imageSlot, U32 trigger); + + /// Sets the trigger state of the image (Ie trigger pulled down on gun) + /// @param imageSlot Image slot + /// @param trigger True if trigger is down + void setImageTriggerState(U32 imageSlot,bool trigger); + + /// Returns the trigger state of the image + /// @param imageSlot Image slot + bool getImageTriggerState(U32 imageSlot); + + /// Sets the alt trigger state of the image (Ie trigger pulled down on gun) + /// @param imageSlot Image slot + /// @param trigger True if trigger is down + void setImageAltTriggerState( U32 imageSlot, bool trigger ); + + /// Returns the alt trigger state of the image + /// @param imageSlot Image slot + bool getImageAltTriggerState( U32 imageSlot ); + + /// Sets the flag if the image uses ammo or energy + /// @param imageSlot Image slot + /// @param ammo True if the weapon uses ammo, not energy + void setImageAmmoState(U32 imageSlot,bool ammo); + + /// Returns true if the image uses ammo, not energy + /// @param imageSlot Image slot + bool getImageAmmoState(U32 imageSlot); + + /// Sets the image as wet or not, IE if you wanted a gun not to function underwater + /// @param imageSlot Image slot + /// @param wet True if image is wet + void setImageWetState(U32 imageSlot,bool wet); + + /// Returns true if image is wet + /// @param imageSlot image slot + bool getImageWetState(U32 imageSlot); + + /// Sets the image as in motion or not, IE if you wanted a gun not to sway while the player is in motion + /// @param imageSlot Image slot + /// @param motion True if image is in motion + void setImageMotionState(U32 imageSlot,bool motion); + + /// Returns true if image is in motion + /// @param imageSlot image slot + bool getImageMotionState(U32 imageSlot); + + /// Sets the flag if the image has a target + /// @param imageSlot Image slot + /// @param ammo True if the weapon has a target + void setImageTargetState(U32 imageSlot,bool ammo); + + /// Returns true if the image has a target + /// @param imageSlot Image slot + bool getImageTargetState(U32 imageSlot); + + /// Sets the flag of if the image is loaded with ammo + /// @param imageSlot Image slot + /// @param loaded True if object is loaded with ammo + void setImageLoadedState(U32 imageSlot,bool loaded); + + /// Returns true if object is loaded with ammo + /// @param imageSlot Image slot + bool getImageLoadedState(U32 imageSlot); + + /// Set the script animation prefix for the image + /// @param imageSlot Image slot id + /// @param prefix The prefix applied to the image + void setImageScriptAnimPrefix(U32 imageSlot, NetStringHandle prefix); + + /// Get the script animation prefix for the image + /// @param imageSlot Image slot id + /// @param imageShapeIndex Shape index (1st person, etc.) used to look up the prefix text + NetStringHandle getImageScriptAnimPrefix(U32 imageSlot); + + /// Modify muzzle, if needed, to aim at whatever is straight in front of eye. + /// Returns true if result is actually modified. + /// @param muzMat Muzzle transform (in/out) + /// @param result Corrected muzzle vector (out) + bool getCorrectedAim(const MatrixF& muzMat, VectorF* result); + + /// Gets the muzzle vector of a specified slot + /// @param imageSlot Image slot to check transform for + /// @param vec Muzzle vector (out) + virtual void getMuzzleVector(U32 imageSlot,VectorF* vec); + + /// Gets the point of the muzzle of the image + /// @param imageSlot Image slot + /// @param pos Muzzle point (out) + void getMuzzlePoint(U32 imageSlot,Point3F* pos); + + /// @} + + /// @name Transforms + /// @{ + + /// Gets the minimum viewing distance, maximum viewing distance, camera offsetand rotation + /// for this object, if the world were to be viewed through its eyes + /// @param min Minimum viewing distance + /// @param max Maximum viewing distance + /// @param offset Offset of the camera from the origin in local space + /// @param rot Rotation matrix + virtual void getCameraParameters(F32 *min,F32* max,Point3F* offset,MatrixF* rot); + + /// Gets the camera to world space transform matrix + /// @todo Find out what pos does + /// @param pos TODO: Find out what this does + /// @param mat Camera transform (out) + virtual void getCameraTransform(F32* pos,MatrixF* mat); + + /// Gets the index of a node inside a mounted image given the name + /// @param imageSlot Image slot + /// @param nodeName Node name + S32 getNodeIndex(U32 imageSlot,StringTableEntry nodeName); + + /// @} + + /// @name Object Transforms + /// @{ + + /// Returns the eye transform of this shape, IE the eyes of a player + /// @param mat Eye transform (out) + virtual void getEyeTransform(MatrixF* mat); + + /// Returns the eye transform of this shape without including mounted images, IE the eyes of a player + /// @param mat Eye transform (out) + virtual void getEyeBaseTransform(MatrixF* mat); + + /// The retraction transform is the muzzle transform in world space. + /// + /// If the gun is pushed back, for instance, if the player ran against something, + /// the muzzle of the gun gets pushed back towards the player, towards this location. + /// @param imageSlot Image slot + /// @param mat Transform (out) + virtual void getRetractionTransform(U32 imageSlot,MatrixF* mat); + + /// Muzzle transform of mounted object in world space + /// @param imageSlot Image slot + /// @param mat Muzzle transform (out) + virtual void getMuzzleTransform(U32 imageSlot,MatrixF* mat); + + /// Gets the transform of a mounted image in world space + /// @param imageSlot Image slot + /// @param mat Transform (out) + virtual void getImageTransform(U32 imageSlot,MatrixF* mat); + + /// Gets the transform of a node on a mounted image in world space + /// @param imageSlot Image Slot + /// @param node node on image + /// @param mat Transform (out) + virtual void getImageTransform(U32 imageSlot,S32 node, MatrixF* mat); + + /// Gets the transform of a node on a mounted image in world space + /// @param imageSlot Image Slot + /// @param nodeName Name of node on image + /// @param mat Transform (out) + virtual void getImageTransform(U32 imageSlot, StringTableEntry nodeName, MatrixF* mat); + + ///@} + + /// @name Render transforms + /// Render transforms are different from object transforms in that the render transform of an object + /// is where, in world space, the object is actually rendered. The object transform is the + /// absolute position of the object, as in where it should be. + /// + /// The render transforms typically vary from object transforms due to client side prediction. + /// + /// Other than that, these functions are identical to their object-transform counterparts + /// + /// @note These are meaningless on the server. + /// @{ + virtual void getRenderRetractionTransform(U32 index,MatrixF* mat); + virtual void getRenderMuzzleTransform(U32 index,MatrixF* mat); + virtual void getRenderImageTransform(U32 imageSlot,MatrixF* mat,bool noEyeOffset=false); + virtual void getRenderImageTransform(U32 index,S32 node, MatrixF* mat); + virtual void getRenderImageTransform(U32 index, StringTableEntry nodeName, MatrixF* mat); + virtual void getRenderMuzzleVector(U32 imageSlot,VectorF* vec); + virtual void getRenderMuzzlePoint(U32 imageSlot,Point3F* pos); + virtual void getRenderEyeTransform(MatrixF* mat); + virtual void getRenderEyeBaseTransform(MatrixF* mat); + /// @} + + + + /// @name Screen Flashing + /// @{ + + /// Returns the level of screenflash that should be used + virtual F32 getDamageFlash() const; + + /// Sets the flash level + /// @param amt Level of flash + virtual void setDamageFlash(const F32 amt); + + /// White out is the flash-grenade blindness effect + /// This returns the level of flash to create + virtual F32 getWhiteOut() const; + + /// Set the level of flash blindness + virtual void setWhiteOut(const F32); + /// @} + + /// @name Invincibility effect + /// This is the screen effect when invincible in the HUD + /// @see GameRenderFilters() + /// @{ + + /// Returns the level of invincibility effect + virtual F32 getInvincibleEffect() const; + + /// Initializes invincibility effect and interpolation parameters + /// + /// @param time Time it takes to become invincible + /// @param speed Speed at which invincibility effects progress + virtual void setupInvincibleEffect(F32 time, F32 speed); + + /// Advance invincibility effect animation + /// @param dt Time since last call of this function + virtual void updateInvincibleEffect(F32 dt); + + /// @} + + /// @name Movement & velocity + /// @{ + + /// Sets the velocity of this object + /// @param vel Velocity vector + virtual void setVelocity(const VectorF& vel); + + /// Applies an impulse force to this object + /// @param pos Position where impulse came from in world space + /// @param vec Velocity vector (Impulse force F = m * v) + virtual void applyImpulse(const Point3F& pos,const VectorF& vec); + + /// @} + + /// @name Cameras and Control + /// @{ + + /// Returns the object controlling this object + ShapeBase* getControllingObject() { return mControllingObject; } + + /// Sets the controlling object + /// @param obj New controlling object + virtual void setControllingObject(ShapeBase* obj); + + /// + virtual void setControllingClient( GameConnection* connection ); + + /// Returns the object this is controlling + virtual ShapeBase* getControlObject(); + + /// sets the object this is controlling + /// @param obj New controlled object + virtual void setControlObject(ShapeBase *obj); + + /// Returns true if this object is controlling by something + bool isControlled() { return(mIsControlled); } + + /// Returns true if this object is being used as a camera in first person + bool isFirstPerson() const; + + /// Returns true if the camera uses this objects eye point (defined by modeler) + bool useObjsEyePoint() const; + + /// Returns true if this object can only be used as a first person camera + bool onlyFirstPerson() const; + + /// Returns the vertical field of view in degrees for + /// this object if used as a camera. + virtual F32 getCameraFov() { return mCameraFov; } + + /// Returns the default vertical field of view in degrees + /// if this object is used as a camera. + virtual F32 getDefaultCameraFov() { return mDataBlock->cameraDefaultFov; } + + /// Sets the vertical field of view in degrees for this + /// object if used as a camera. + /// @param yfov The vertical FOV in degrees to test. + virtual void setCameraFov(F32 fov); + + /// Returns true if the vertical FOV in degrees is within + /// allowable parameters of the datablock. + /// @param yfov The vertical FOV in degrees to test. + /// @see ShapeBaseData::cameraMinFov + /// @see ShapeBaseData::cameraMaxFov + virtual bool isValidCameraFov(F32 fov); + /// @} + + + void processTick(const Move *move); + void advanceTime(F32 dt); + + /// @name Rendering + /// @{ + + /// Returns the renderable shape of this object + TSShape const* getShape(); + + /// @see SceneObject + virtual void prepRenderImage( SceneRenderState* state ); + + /// Used from ShapeBase::_prepRenderImage() to submit render + /// instances for the main shape or its mounted elements. + virtual void prepBatchRender( SceneRenderState *state, S32 mountedImageIndex ); + + /// Preprender logic + virtual void calcClassRenderData() { } + + /// Virtualize this so other classes may override it for custom reasons. + virtual void renderMountedImage( U32 imageSlot, TSRenderState &rstate, SceneRenderState *state ); + /// @} + + /// Control object scoping + void onCameraScopeQuery(NetConnection *cr, CameraScopeQuery *camInfo); + + bool castRay(const Point3F &start, const Point3F &end, RayInfo* info); + bool castRayRendered(const Point3F &start, const Point3F &end, RayInfo* info); + bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF& sphere); + void buildConvex(const Box3F& box, Convex* convex); + + /// @name Rendering + /// @{ + + /// Increments the last rendered frame number + static void incRenderFrame() { sLastRenderFrame++; } + + /// Returns true if the last frame calculated rendered + bool didRenderLastRender() { return mLastRenderFrame == sLastRenderFrame; } + + /// Sets the state of this object as hidden or not. If an object is hidden + /// it is removed entirely from collisions, it is not ghosted and is + /// essentially "non existant" as far as simulation is concerned. + /// @param hidden True if object is to be hidden + virtual void setHidden(bool hidden); + + /// Returns true if this object can be damaged + bool isInvincible(); + + /// Start fade of object in/out + /// @param fadeTime Time fade should take + /// @param fadeDelay Delay before starting fade + /// @param fadeOut True if object is fading out, false if fading in. + void startFade( F32 fadeTime, F32 fadeDelay = 0.0, bool fadeOut = true ); + + /// Traverses mounted objects and registers light sources with the light manager + /// @param lightManager Light manager to register with + /// @param lightingScene Set to true if the scene is being lit, in which case these lights will not be used + //void registerLights(LightManager * lightManager, bool lightingScene); + + // ISceneLight + virtual void submitLights( LightManager *lm, bool staticLighting ); + virtual LightInfo* getLight() { return NULL; } + + /// @} + + /// Returns true if the point specified is in the water + /// @param point Point to test in world space + bool pointInWater( Point3F &point ); + + /// Returns the percentage of this object covered by water + F32 getWaterCoverage() { return mWaterCoverage; } + + /// Returns the height of the liquid on this object + F32 getLiquidHeight() { return mLiquidHeight; } + + virtual WaterObject* getCurrentWaterObject(); + + void setCurrentWaterObject( WaterObject *obj ); + + virtual F32 getMass() const { return mMass; } + + /// @name Network + /// @{ + + F32 getUpdatePriority(CameraScopeQuery *focusObject, U32 updateMask, S32 updateSkips); + U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream); + void unpackUpdate(NetConnection *conn, BitStream *stream); + void writePacketData(GameConnection *conn, BitStream *stream); + void readPacketData(GameConnection *conn, BitStream *stream); + + /// @} + + DECLARE_CONOBJECT(ShapeBase); + +protected: + DECLARE_CALLBACK( F32, validateCameraFov, (F32 fov) ); + +}; + + +//------------------------------------------------------------------------------ +// inlines +//------------------------------------------------------------------------------ + +inline bool ShapeBase::getCloakedState() +{ + return(mCloaked); +} + +inline F32 ShapeBase::getCloakLevel() +{ + return(mCloakLevel); +} + +inline const char* ShapeBase::getShapeName() +{ + return mShapeNameHandle.getString(); +} + +inline const char* ShapeBase::getSkinName() +{ + return mSkinNameHandle.getString(); +} + +inline WaterObject* ShapeBase::getCurrentWaterObject() +{ + if ( isMounted() && mShapeBaseMount ) + return mShapeBaseMount->getCurrentWaterObject(); + + return mCurrentWaterObject; +} + +#endif // _H_SHAPEBASE_ diff --git a/Engine/source/T3D/shapeCollision.cpp b/Engine/source/T3D/shapeCollision.cpp new file mode 100644 index 000000000..2fea51b3e --- /dev/null +++ b/Engine/source/T3D/shapeCollision.cpp @@ -0,0 +1,61 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "T3D/shapeBase.h" +#include "T3D/item.h" +#include "T3D/trigger.h" + +//---------------------------------------------------------------------------- + +void collisionFilter(SceneObject* object,void *key) +{ + SceneContainer::CallbackInfo* info = reinterpret_cast(key); + ShapeBase* ptr = reinterpret_cast(info->key); + + if (object->getTypeMask() & ItemObjectType) { + // We've hit it's bounding box, that's close enough for items. + Item* item = static_cast(object); + if (ptr != item->getCollisionObject()) + ptr->queueCollision(item,ptr->getVelocity() - item->getVelocity()); + } + else + if (object->getTypeMask() & TriggerObjectType) { + // We've hit it's bounding box, that's close enough for triggers + Trigger* pTrigger = static_cast(object); + pTrigger->potentialEnterObject(ptr); + } + else + if (object->getTypeMask() & CorpseObjectType) { + // Ok, guess it's close enough for corpses too... + ShapeBase* col = static_cast(object); + ptr->queueCollision(col,ptr->getVelocity() - col->getVelocity()); + } + else + object->buildPolyList(info->context,info->polyList,info->boundingBox,info->boundingSphere); +} + +void defaultFilter(SceneObject* object,void * key) +{ + SceneContainer::CallbackInfo* info = reinterpret_cast(key); + object->buildPolyList(info->context,info->polyList,info->boundingBox,info->boundingSphere); +} + diff --git a/Engine/source/T3D/shapeImage.cpp b/Engine/source/T3D/shapeImage.cpp new file mode 100644 index 000000000..6784977d1 --- /dev/null +++ b/Engine/source/T3D/shapeImage.cpp @@ -0,0 +1,3342 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "T3D/shapeBase.h" +#include "core/resourceManager.h" +#include "core/stream/bitStream.h" +#include "ts/tsShapeInstance.h" +#include "console/consoleInternal.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "lighting/lightInfo.h" +#include "lighting/lightManager.h" +#include "T3D/fx/particleEmitter.h" +#include "T3D/projectile.h" +#include "T3D/gameBase/gameConnection.h" +#include "math/mathIO.h" +#include "T3D/debris.h" +#include "math/mathUtils.h" +#include "sim/netObject.h" +#include "sfx/sfxTrack.h" +#include "sfx/sfxSource.h" +#include "sfx/sfxSystem.h" +#include "sfx/sfxTypes.h" +#include "scene/sceneManager.h" +#include "core/stream/fileStream.h" + +//---------------------------------------------------------------------------- + +ShapeBaseImageData* InvalidImagePtr = (ShapeBaseImageData*) 1; + +ImplementEnumType( ShapeBaseImageLoadedState, + "@brief The loaded state of this ShapeBaseImage.\n\n" + "@ingroup gameObjects\n\n") + { ShapeBaseImageData::StateData::IgnoreLoaded, "Ignore", "Ignore the loaded state.\n" }, + { ShapeBaseImageData::StateData::Loaded, "Loaded", "ShapeBaseImage is loaded.\n" }, + { ShapeBaseImageData::StateData::NotLoaded, "Empty", "ShapeBaseImage is not loaded.\n" }, +EndImplementEnumType; + +ImplementEnumType( ShapeBaseImageSpinState, + "@brief How the spin animation should be played.\n\n" + "@ingroup gameObjects\n\n") + { ShapeBaseImageData::StateData::IgnoreSpin,"Ignore", "No changes to the spin sequence.\n" }, + { ShapeBaseImageData::StateData::NoSpin, "Stop", "Stops the spin sequence at its current position\n" }, + { ShapeBaseImageData::StateData::SpinUp, "SpinUp", "Increase spin sequence timeScale from 0 (on state entry) to 1 (after stateTimeoutValue seconds).\n" }, + { ShapeBaseImageData::StateData::SpinDown, "SpinDown", "Decrease spin sequence timeScale from 1 (on state entry) to 0 (after stateTimeoutValue seconds).\n" }, + { ShapeBaseImageData::StateData::FullSpin, "FullSpeed", "Resume the spin sequence playback at its current position with timeScale = 1.\n"}, +EndImplementEnumType; + +ImplementEnumType( ShapeBaseImageRecoilState, + "@brief What kind of recoil this ShapeBaseImage should emit when fired.\n\n" + "@ingroup gameObjects\n\n") + { ShapeBaseImageData::StateData::NoRecoil, "NoRecoil", "No recoil occurs.\n" }, + { ShapeBaseImageData::StateData::LightRecoil, "LightRecoil", "A light recoil occurs.\n" }, + { ShapeBaseImageData::StateData::MediumRecoil, "MediumRecoil", "A medium recoil occurs.\n" }, + { ShapeBaseImageData::StateData::HeavyRecoil, "HeavyRecoil", "A heavy recoil occurs.\n" }, +EndImplementEnumType; + +ImplementEnumType( ShapeBaseImageLightType, + "@brief The type of light to attach to this ShapeBaseImage.\n\n" + "@ingroup gameObjects\n\n") + { ShapeBaseImageData::NoLight, "NoLight", "No light is attached.\n" }, + { ShapeBaseImageData::ConstantLight, "ConstantLight", "A constant emitting light is attached.\n" }, + { ShapeBaseImageData::SpotLight, "SpotLight", "A spotlight is attached.\n" }, + { ShapeBaseImageData::PulsingLight, "PulsingLight", "A pusling light is attached.\n" }, + { ShapeBaseImageData::WeaponFireLight, "WeaponFireLight", "Light emits when the weapon is fired, then dissipates.\n" } +EndImplementEnumType; + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +IMPLEMENT_CO_DATABLOCK_V1(ShapeBaseImageData); + +ConsoleDocClass( ShapeBaseImageData, + "@brief Represents geometry to be mounted to a ShapeBase object.\n\n" + "@ingroup gameObjects\n" +); + +IMPLEMENT_CALLBACK( ShapeBaseImageData, onMount, void, ( ShapeBase* obj, S32 slot, F32 dt ), ( obj, slot, dt ), + "@brief Called when the Image is first mounted to the object.\n\n" + + "@param obj object that this Image has been mounted to\n" + "@param slot Image mount slot on the object\n" + "@param dt time remaining in this Image update\n" ); + +IMPLEMENT_CALLBACK( ShapeBaseImageData, onUnmount, void, ( ShapeBase* obj, S32 slot, F32 dt ), ( obj, slot, dt ), + "@brief Called when the Image is unmounted from the object.\n\n" + + "@param obj object that this Image has been unmounted from\n" + "@param slot Image mount slot on the object\n" + "@param dt time remaining in this Image update\n" ); + +ShapeBaseImageData::StateData::StateData() +{ + name = 0; + transition.loaded[0] = transition.loaded[1] = -1; + transition.ammo[0] = transition.ammo[1] = -1; + transition.target[0] = transition.target[1] = -1; + transition.trigger[0] = transition.trigger[1] = -1; + transition.altTrigger[0] = transition.altTrigger[1] = -1; + transition.wet[0] = transition.wet[1] = -1; + transition.motion[0] = transition.motion[1] = -1; + transition.timeout = -1; + waitForTimeout = true; + timeoutValue = 0; + fire = false; + altFire = false; + reload = false; + energyDrain = 0; + allowImageChange = true; + loaded = IgnoreLoaded; + spin = IgnoreSpin; + recoil = NoRecoil; + sound = 0; + emitter = NULL; + script = 0; + ignoreLoadedForReady = false; + + ejectShell = false; + scaleAnimation = false; + scaleAnimationFP = false; + sequenceTransitionIn = false; + sequenceTransitionOut = false; + sequenceNeverTransition = false; + sequenceTransitionTime = 0; + direction = false; + emitterTime = 0.0f; + + for( U32 i=0; igetChecksum(); + } + else if(mCRC[i] != fileRef->getChecksum()) + { + errorStr = String::ToString("Shape \"%s\" does not match version on server.",name); + return false; + } + } + + // Resolve nodes & build mount transform + eyeMountNode[i] = shape[i]->findNode("eyeMount"); + eyeNode[i] = shape[i]->findNode("eye"); + if (eyeNode[i] == -1) + eyeNode[i] = eyeMountNode[i]; + ejectNode[i] = shape[i]->findNode("ejectPoint"); + muzzleNode[i] = shape[i]->findNode("muzzlePoint"); + retractNode[i] = shape[i]->findNode("retractionPoint"); + mountTransform[i] = mountOffset; + S32 node = shape[i]->findNode("mountPoint"); + if (node != -1) { + MatrixF total(1); + do { + MatrixF nmat; + QuatF q; + TSTransform::setMatrix(shape[i]->defaultRotations[node].getQuatF(&q),shape[i]->defaultTranslations[node],&nmat); + total.mul(nmat); + node = shape[i]->nodes[node].parentIndex; + } + while(node != -1); + total.inverse(); + mountTransform[i].mul(total); + } + + // Resolve state sequence names & emitter nodes + isAnimated[i] = false; + hasFlash[i] = false; + for (U32 j = 0; j < MaxStates; j++) { + StateData& s = state[j]; + if (stateSequence[j] && stateSequence[j][0]) + s.sequence[i] = shape[i]->findSequence(stateSequence[j]); + if (s.sequence[i] != -1) + { + // This state has an animation sequence + isAnimated[i] = true; + } + + if (stateSequence[j] && stateSequence[j][0] && stateSequenceRandomFlash[j]) { + char bufferVis[128]; + dStrncpy(bufferVis, stateSequence[j], 100); + dStrcat(bufferVis, "_vis"); + s.sequenceVis[i] = shape[i]->findSequence(bufferVis); + } + if (s.sequenceVis[i] != -1) + { + // This state has a flash animation sequence + s.flashSequence[i] = true; + hasFlash[i] = true; + } + + s.ignoreLoadedForReady = stateIgnoreLoadedForReady[j]; + + if (stateEmitterNode[j] && stateEmitterNode[j][0]) + s.emitterNode[i] = shape[i]->findNode(stateEmitterNode[j]); + if (s.emitterNode[i] == -1) + s.emitterNode[i] = muzzleNode[i]; + } + + ambientSequence[i] = shape[i]->findSequence("ambient"); + spinSequence[i] = shape[i]->findSequence("spin"); + + shapeIsValid[i] = true; + } + else { + errorStr = "Bad Datablock from server"; + return false; + } + } + + if( !casing && casingID != 0 ) + { + if( !Sim::findObject( SimObjectId( casingID ), casing ) ) + { + Con::errorf( ConsoleLogEntry::General, "ShapeBaseImageData::preload: Invalid packet, bad datablockId(casing): 0x%x", casingID ); + } + } + + + // Preload the shapes + for( U32 i=0; i(), Offset(projectile, ShapeBaseImageData), + "@brief The projectile fired by this Image\n\n" ); + + addField( "cloakable", TypeBool, Offset(cloakable, ShapeBaseImageData), + "@brief Whether this Image can be cloaked.\n\n" + "Currently unused." ); + + addField( "mountPoint", TypeS32, Offset(mountPoint, ShapeBaseImageData), + "@brief Mount node # to mount this Image to.\n\n" + "This should correspond to a mount# node on the ShapeBase derived object we are mounting to." ); + + addField( "offset", TypeMatrixPosition, Offset(mountOffset, ShapeBaseImageData), + "@brief \"X Y Z\" translation offset from this Image's mountPoint node to " + "attach to.\n\n" + "Defaults to \"0 0 0\". ie. attach this Image's " + "mountPoint node to the ShapeBase model's mount# node without any offset.\n" + "@see rotation"); + + addField( "rotation", TypeMatrixRotation, Offset(mountOffset, ShapeBaseImageData), + "@brief \"X Y Z ANGLE\" rotation offset from this Image's mountPoint node " + "to attach to.\n\n" + "Defaults to \"0 0 0\". ie. attach this Image's " + "mountPoint node to the ShapeBase model's mount# node without any additional rotation.\n" + "@see offset"); + + addField( "eyeOffset", TypeMatrixPosition, Offset(eyeOffset, ShapeBaseImageData), + "@brief \"X Y Z\" translation offset from the ShapeBase model's eye node.\n\n" + "When in first person view, this is the offset from the eye node to place the gun. This " + "gives the gun a fixed point in space, typical of a lot of FPS games.\n" + "@see eyeRotation"); + + addField( "eyeRotation", TypeMatrixRotation, Offset(eyeOffset, ShapeBaseImageData), + "@brief \"X Y Z ANGLE\" rotation offset from the ShapeBase model's eye node.\n\n" + "When in first person view, this is the rotation from the eye node to place the gun.\n" + "@see eyeOffset"); + + addField( "useEyeNode", TypeBool, Offset(useEyeNode, ShapeBaseImageData), + "@brief Mount image using image's eyeMount node and place the camera at the image's eye node (or " + "at the eyeMount node if the eye node is missing).\n\n" + "When in first person view, if an 'eyeMount' node is present in the image's shape, this indicates " + "that the image should mount eyeMount node to Player eye node for image placement. The " + "Player's camera should also mount to the image's eye node to inherit any animation (or the eyeMount " + "node if the image doesn't have an eye node).\n\n" + "@note Used instead of eyeOffset.\n\n" + "@note Read about the animateOnServer field as you may want to set it to true if you're using useEyeNode.\n\n" + "@see eyeOffset\n\n" + "@see animateOnServer\n\n"); + + addField( "correctMuzzleVector", TypeBool, Offset(correctMuzzleVector, ShapeBaseImageData), + "@brief Flag to adjust the aiming vector to the eye's LOS point.\n\n" + "@see ShapeBase::getMuzzleVector()" ); + + addField( "correctMuzzleVector", TypeBool, Offset(correctMuzzleVector, ShapeBaseImageData), + "@brief Flag to adjust the aiming vector to the eye's LOS point when in 1st person view.\n\n" + "@see ShapeBase::getMuzzleVector()" ); + + addField( "correctMuzzleVectorTP", TypeBool, Offset(correctMuzzleVectorTP, ShapeBaseImageData), + "@brief Flag to adjust the aiming vector to the camera's LOS point when in 3rd person view.\n\n" + "@see ShapeBase::getMuzzleVector()" ); + + addField( "firstPerson", TypeBool, Offset(firstPerson, ShapeBaseImageData), + "@brief Set to true to render the image in first person." ); + + addField( "mass", TypeF32, Offset(mass, ShapeBaseImageData), + "@brief Mass of this Image.\n\n" + "This is added to the total mass of the ShapeBase object." ); + + addField( "usesEnergy", TypeBool, Offset(usesEnergy,ShapeBaseImageData), + "@brief Flag indicating whether this Image uses energy instead of ammo. The energy level comes from the ShapeBase object we're mounted to.\n\n" + "@see ShapeBase::setEnergyLevel()"); + + addField( "minEnergy", TypeF32, Offset(minEnergy, ShapeBaseImageData), + "@brief Minimum Image energy for it to be operable.\n\n" + "@see usesEnergy"); + + addField( "accuFire", TypeBool, Offset(accuFire, ShapeBaseImageData), + "@brief Flag to control whether the Image's aim is automatically converged with " + "the crosshair.\n\n" + "Currently unused." ); + + addField( "lightType", TYPEID< ShapeBaseImageData::LightType >(), Offset(lightType, ShapeBaseImageData), + "@brief The type of light this Image emits.\n\n" + "@see ShapeBaseImageLightType"); + + addField( "lightColor", TypeColorF, Offset(lightColor, ShapeBaseImageData), + "@brief The color of light this Image emits.\n\n" + "@see lightType"); + + addField( "lightDuration", TypeS32, Offset(lightDuration, ShapeBaseImageData), + "@brief Duration in SimTime of Pulsing and WeaponFire type lights.\n\n" + "@see lightType"); + + addField( "lightRadius", TypeF32, Offset(lightRadius, ShapeBaseImageData), + "@brief Radius of the light this Image emits.\n\n" + "@see lightType"); + + addField( "lightBrightness", TypeF32, Offset(lightBrightness, ShapeBaseImageData), + "@brief Brightness of the light this Image emits.\n\n" + "Only valid for WeaponFireLight." + "@see lightType"); + + addField( "shakeCamera", TypeBool, Offset(shakeCamera, ShapeBaseImageData), + "@brief Flag indicating whether the camera should shake when this Image fires.\n\n" + "@note Camera shake only works properly if the player is in control of " + "the one and only shapeBase object in the scene which fires an Image that " + "uses camera shake." ); + + addField( "camShakeFreq", TypePoint3F, Offset(camShakeFreq, ShapeBaseImageData), + "@brief Frequency of the camera shaking effect.\n\n" + "@see shakeCamera" ); + + addField( "camShakeAmp", TypePoint3F, Offset(camShakeAmp, ShapeBaseImageData), + "@brief Amplitude of the camera shaking effect.\n\n" + "@see shakeCamera" ); + + addField( "casing", TYPEID< DebrisData >(), Offset(casing, ShapeBaseImageData), + "@brief DebrisData datablock to use for ejected casings.\n\n" + "@see stateEjectShell" ); + + addField( "shellExitDir", TypePoint3F, Offset(shellExitDir, ShapeBaseImageData), + "@brief Vector direction to eject shell casings.\n\n" + "@see casing"); + + addField( "shellExitVariance", TypeF32, Offset(shellExitVariance, ShapeBaseImageData), + "@brief Variance (in degrees) from the shellExitDir vector to eject casings.\n\n" + "@see shellExitDir"); + + addField( "shellVelocity", TypeF32, Offset(shellVelocity, ShapeBaseImageData), + "@brief Speed at which to eject casings.\n\n" + "@see casing"); + + // State arrays + addArray( "States", MaxStates ); + + addField( "stateName", TypeCaseString, Offset(stateName, ShapeBaseImageData), MaxStates, + "Name of this state." ); + + addField( "stateTransitionOnLoaded", TypeString, Offset(stateTransitionLoaded, ShapeBaseImageData), MaxStates, + "Name of the state to transition to when the loaded state of the Image " + "changes to 'Loaded'." ); + addField( "stateTransitionOnNotLoaded", TypeString, Offset(stateTransitionNotLoaded, ShapeBaseImageData), MaxStates, + "Name of the state to transition to when the loaded state of the Image " + "changes to 'Empty'." ); + addField( "stateTransitionOnAmmo", TypeString, Offset(stateTransitionAmmo, ShapeBaseImageData), MaxStates, + "Name of the state to transition to when the ammo state of the Image " + "changes to true." ); + addField( "stateTransitionOnNoAmmo", TypeString, Offset(stateTransitionNoAmmo, ShapeBaseImageData), MaxStates, + "Name of the state to transition to when the ammo state of the Image " + "changes to false." ); + addField( "stateTransitionOnTarget", TypeString, Offset(stateTransitionTarget, ShapeBaseImageData), MaxStates, + "Name of the state to transition to when the Image gains a target." ); + addField( "stateTransitionOnNoTarget", TypeString, Offset(stateTransitionNoTarget, ShapeBaseImageData), MaxStates, + "Name of the state to transition to when the Image loses a target." ); + addField( "stateTransitionOnWet", TypeString, Offset(stateTransitionWet, ShapeBaseImageData), MaxStates, + "Name of the state to transition to when the Image enters the water." ); + addField( "stateTransitionOnNotWet", TypeString, Offset(stateTransitionNotWet, ShapeBaseImageData), MaxStates, + "Name of the state to transition to when the Image exits the water." ); + addField( "stateTransitionOnMotion", TypeString, Offset(stateTransitionMotion, ShapeBaseImageData), MaxStates, + "Name of the state to transition to when the Player moves." ); + addField( "stateTransitionOnNoMotion", TypeString, Offset(stateTransitionNoMotion, ShapeBaseImageData), MaxStates, + "Name of the state to transition to when the Player stops moving." ); + addField( "stateTransitionOnTriggerUp", TypeString, Offset(stateTransitionTriggerUp, ShapeBaseImageData), MaxStates, + "Name of the state to transition to when the trigger state of the Image " + "changes to true (fire button down)." ); + addField( "stateTransitionOnTriggerDown", TypeString, Offset(stateTransitionTriggerDown, ShapeBaseImageData), MaxStates, + "Name of the state to transition to when the trigger state of the Image " + "changes to false (fire button released)." ); + addField( "stateTransitionOnAltTriggerUp", TypeString, Offset(stateTransitionAltTriggerUp, ShapeBaseImageData), MaxStates, + "Name of the state to transition to when the alt trigger state of the " + "Image changes to true (alt fire button down)." ); + addField( "stateTransitionOnAltTriggerDown", TypeString, Offset(stateTransitionAltTriggerDown, ShapeBaseImageData), MaxStates, + "Name of the state to transition to when the alt trigger state of the " + "Image changes to false (alt fire button up)." ); + addField( "stateTransitionOnTimeout", TypeString, Offset(stateTransitionTimeout, ShapeBaseImageData), MaxStates, + "Name of the state to transition to when we have been in this state " + "for stateTimeoutValue seconds." ); + + addField( "stateTransitionGeneric0In", TypeString, Offset(stateTransitionGeneric0In, ShapeBaseImageData), MaxStates, + "Name of the state to transition to when the generic trigger 0 state " + "changes to true." ); + addField( "stateTransitionGeneric0Out", TypeString, Offset(stateTransitionGeneric0Out, ShapeBaseImageData), MaxStates, + "Name of the state to transition to when the generic trigger 0 state " + "changes to false." ); + addField( "stateTransitionGeneric1In", TypeString, Offset(stateTransitionGeneric1In, ShapeBaseImageData), MaxStates, + "Name of the state to transition to when the generic trigger 1 state " + "changes to true." ); + addField( "stateTransitionGeneric1Out", TypeString, Offset(stateTransitionGeneric1Out, ShapeBaseImageData), MaxStates, + "Name of the state to transition to when the generic trigger 1 state " + "changes to false." ); + addField( "stateTransitionGeneric2In", TypeString, Offset(stateTransitionGeneric2In, ShapeBaseImageData), MaxStates, + "Name of the state to transition to when the generic trigger 2 state " + "changes to true." ); + addField( "stateTransitionGeneric2Out", TypeString, Offset(stateTransitionGeneric2Out, ShapeBaseImageData), MaxStates, + "Name of the state to transition to when the generic trigger 2 state " + "changes to false." ); + addField( "stateTransitionGeneric3In", TypeString, Offset(stateTransitionGeneric3In, ShapeBaseImageData), MaxStates, + "Name of the state to transition to when the generic trigger 3 state " + "changes to true." ); + addField( "stateTransitionGeneric3Out", TypeString, Offset(stateTransitionGeneric3Out, ShapeBaseImageData), MaxStates, + "Name of the state to transition to when the generic trigger 3 state " + "changes to false." ); + + addField( "stateTimeoutValue", TypeF32, Offset(stateTimeoutValue, ShapeBaseImageData), MaxStates, + "Time in seconds to wait before transitioning to stateTransitionOnTimeout." ); + addField( "stateWaitForTimeout", TypeBool, Offset(stateWaitForTimeout, ShapeBaseImageData), MaxStates, + "If false, this state ignores stateTimeoutValue and transitions " + "immediately if other transition conditions are met." ); + addField( "stateFire", TypeBool, Offset(stateFire, ShapeBaseImageData), MaxStates, + "The first state with this set to true is the state entered by the " + "client when it receives the 'fire' event." ); + addField( "stateAlternateFire", TypeBool, Offset(stateAlternateFire, ShapeBaseImageData), MaxStates, + "The first state with this set to true is the state entered by the " + "client when it receives the 'altFire' event." ); + addField( "stateReload", TypeBool, Offset(stateReload, ShapeBaseImageData), MaxStates, + "The first state with this set to true is the state entered by the " + "client when it receives the 'reload' event." ); + addField( "stateEjectShell", TypeBool, Offset(stateEjectShell, ShapeBaseImageData), MaxStates, + "If true, a shell casing will be ejected in this state." ); + addField( "stateEnergyDrain", TypeF32, Offset(stateEnergyDrain, ShapeBaseImageData), MaxStates, + "@brief Amount of energy to subtract from the Image in this state.\n\n" + "Energy is drained at stateEnergyDrain units/tick as long as we are in " + "this state.\n" + "@see usesEnergy"); + addField( "stateAllowImageChange", TypeBool, Offset(stateAllowImageChange, ShapeBaseImageData), MaxStates, + "@brief If false, other Images will temporarily be blocked from mounting " + "while the state machine is executing the tasks in this state.\n\n" + "For instance, if we have a rocket launcher, the player shouldn't " + "be able to switch out while firing. So, you'd set " + "stateAllowImageChange to false in firing states, and true the rest " + "of the time." ); + addField( "stateDirection", TypeBool, Offset(stateDirection, ShapeBaseImageData), MaxStates, + "@brief Direction of the animation to play in this state.\n\n" + "True is forward, false is backward." ); + addField( "stateLoadedFlag", TYPEID< ShapeBaseImageData::StateData::LoadedState >(), Offset(stateLoaded, ShapeBaseImageData), MaxStates, + "@brief Set the loaded state of the Image.\n\n" + "
      • IgnoreLoaded: Don't change Image loaded state.
      • " + "
      • Loaded: Set Image loaded state to true.
      • " + "
      • NotLoaded: Set Image loaded state to false.
      \n" + "@see ShapeBaseImageLoadedState"); + addField( "stateSpinThread", TYPEID< ShapeBaseImageData::StateData::SpinState >(), Offset(stateSpin, ShapeBaseImageData), MaxStates, + "@brief Controls how fast the 'spin' animation sequence will be played in " + "this state.\n\n" + "
      • Ignore: No change to the spin sequence.
      • " + "
      • Stop: Stops the spin sequence at its current position.
      • " + "
      • SpinUp: Increase spin sequence timeScale from 0 (on state entry) " + "to 1 (after stateTimeoutValue seconds).
      • " + "
      • SpinDown: Decrease spin sequence timeScale from 1 (on state entry) " + "to 0 (after stateTimeoutValue seconds).
      • " + "
      • FullSpeed: Resume the spin sequence playback at its current " + "position with timeScale=1.
      \n" + "@see ShapeBaseImageSpinState"); + addField( "stateRecoil", TYPEID< ShapeBaseImageData::StateData::RecoilState >(), Offset(stateRecoil, ShapeBaseImageData), MaxStates, + "@brief Type of recoil sequence to play on the ShapeBase object on entry to " + "this state.\n\n" + "
      • NoRecoil: Do not play a recoil sequence.
      • " + "
      • LightRecoil: Play the light_recoil sequence.
      • " + "
      • MediumRecoil: Play the medium_recoil sequence.
      • " + "
      • HeavyRecoil: Play the heavy_recoil sequence.
      \n" + "@see ShapeBaseImageRecoilState"); + addField( "stateSequence", TypeString, Offset(stateSequence, ShapeBaseImageData), MaxStates, + "Name of the sequence to play on entry to this state." ); + addField( "stateSequenceRandomFlash", TypeBool, Offset(stateSequenceRandomFlash, ShapeBaseImageData), MaxStates, + "@brief If true, the muzzle flash sequence will be played while in this state.\n\n" + "The name of the muzzle flash sequence is the same as stateSequence, " + "with \"_vis\" at the end." ); + addField( "stateScaleAnimation", TypeBool, Offset(stateScaleAnimation, ShapeBaseImageData), MaxStates, + "If true, the timeScale of the stateSequence animation will be adjusted " + "such that the sequence plays for stateTimeoutValue seconds. " ); + addField( "stateScaleAnimationFP", TypeBool, Offset(stateScaleAnimationFP, ShapeBaseImageData), MaxStates, + "If true, the timeScale of the first person stateSequence animation will be adjusted " + "such that the sequence plays for stateTimeoutValue seconds. " ); + addField( "stateSequenceTransitionIn", TypeBool, Offset(stateSequenceTransitionIn, ShapeBaseImageData), MaxStates, + "Do we transition to the state's sequence when we enter the state?" ); + addField( "stateSequenceTransitionOut", TypeBool, Offset(stateSequenceTransitionOut, ShapeBaseImageData), MaxStates, + "Do we transition to the new state's sequence when we leave the state?" ); + addField( "stateSequenceNeverTransition", TypeBool, Offset(stateSequenceNeverTransition, ShapeBaseImageData), MaxStates, + "Never allow a transition to this sequence. Often used for a fire sequence." ); + addField( "stateSequenceTransitionTime", TypeF32, Offset(stateSequenceTransitionTime, ShapeBaseImageData), MaxStates, + "The time to transition in or out of a sequence." ); + + addField( "stateShapeSequence", TypeString, Offset(stateShapeSequence, ShapeBaseImageData), MaxStates, + "Name of the sequence that is played on the mounting shape." ); + addField( "stateScaleShapeSequence", TypeBool, Offset(stateScaleShapeSequence, ShapeBaseImageData), MaxStates, + "Indicates if the sequence to be played on the mounting shape should be scaled to the length of the state." ); + + addField( "stateSound", TypeSFXTrackName, Offset(stateSound, ShapeBaseImageData), MaxStates, + "Sound to play on entry to this state." ); + addField( "stateScript", TypeCaseString, Offset(stateScript, ShapeBaseImageData), MaxStates, + "@brief Method to execute on entering this state.\n\n" + "Scoped to this image class name, then ShapeBaseImageData. The script " + "callback function takes the same arguments as the onMount callback.\n" + "@see onMount() for the same arguments as this callback."); + + addField( "stateEmitter", TYPEID< ParticleEmitterData >(), Offset(stateEmitter, ShapeBaseImageData), MaxStates, + "@brief Emitter to generate particles in this state (from muzzle point or " + "specified node).\n\n" + "@see stateEmitterNode" ); + addField( "stateEmitterTime", TypeF32, Offset(stateEmitterTime, ShapeBaseImageData), MaxStates, + "How long (in seconds) to emit particles on entry to this state." ); + addField( "stateEmitterNode", TypeString, Offset(stateEmitterNode, ShapeBaseImageData), MaxStates, + "@brief Name of the node to emit particles from.\n\n" + "@see stateEmitter" ); + addField( "stateIgnoreLoadedForReady", TypeBool, Offset(stateIgnoreLoadedForReady, ShapeBaseImageData), MaxStates, + "@brief If set to true, and both ready and loaded transitions are true, the " + "ready transition will be taken instead of the loaded transition.\n\n" + "A state is 'ready' if pressing the fire trigger in that state would " + "transition to the fire state." ); + + endArray( "States" ); + + addField( "computeCRC", TypeBool, Offset(computeCRC, ShapeBaseImageData), + "If true, verify that the CRC of the client's Image matches the server's " + "CRC for the Image when loaded by the client." ); + + addField( "maxConcurrentSounds", TypeS32, Offset(maxConcurrentSounds, ShapeBaseImageData), + "@brief Maximum number of sounds this Image can play at a time.\n\n" + "Any value <= 0 indicates that it can play an infinite number of sounds." ); + + addField( "useRemainderDT", TypeBool, Offset(useRemainderDT, ShapeBaseImageData), + "@brief If true, allow multiple timeout transitions to occur within a single " + "tick (useful if states have a very small timeout).\n\n" ); + + Parent::initPersistFields(); +} + +void ShapeBaseImageData::packData(BitStream* stream) +{ + Parent::packData(stream); + + if(stream->writeFlag(computeCRC)) + { + for( U32 j=0; jwrite(mCRC[j]); + } + } + + stream->writeString(shapeName); // shape 0 for normal use + stream->writeString(shapeNameFP); // shape 1 for first person use (optional) + + stream->writeString(imageAnimPrefix); + stream->writeString(imageAnimPrefixFP); + + stream->write(mountPoint); + if (!stream->writeFlag(mountOffset.isIdentity())) + stream->writeAffineTransform(mountOffset); + if (!stream->writeFlag(eyeOffset.isIdentity())) + stream->writeAffineTransform(eyeOffset); + + stream->writeFlag(animateOnServer); + + stream->write(scriptAnimTransitionTime); + + stream->writeFlag(useEyeNode); + + stream->writeFlag(correctMuzzleVector); + stream->writeFlag(correctMuzzleVectorTP); + stream->writeFlag(firstPerson); + stream->write(mass); + stream->writeFlag(usesEnergy); + stream->write(minEnergy); + + for( U32 j=0; jwriteFlag(hasFlash[j]); + } + + // Client doesn't need accuFire + + // Write the projectile datablock + if (stream->writeFlag(projectile)) + stream->writeRangedU32(packed? SimObjectId(projectile): + projectile->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast); + + stream->writeFlag(cloakable); + stream->writeRangedU32(lightType, 0, NumLightTypes-1); + if(lightType != NoLight) + { + stream->write(lightRadius); + stream->write(lightDuration); + stream->writeFloat(lightColor.red, 7); + stream->writeFloat(lightColor.green, 7); + stream->writeFloat(lightColor.blue, 7); + stream->writeFloat(lightColor.alpha, 7); + stream->write(lightBrightness); + } + + if ( stream->writeFlag( shakeCamera ) ) + { + mathWrite( *stream, camShakeFreq ); + mathWrite( *stream, camShakeAmp ); + } + + mathWrite( *stream, shellExitDir ); + stream->write(shellExitVariance); + stream->write(shellVelocity); + + if( stream->writeFlag( casing ) ) + { + stream->writeRangedU32(packed? SimObjectId(casing): + casing->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast); + } + + for (U32 i = 0; i < MaxStates; i++) + if (stream->writeFlag(state[i].name && state[i].name[0])) { + StateData& s = state[i]; + // States info not needed on the client: + // s.allowImageChange + // s.scriptNames + // Transitions are inc. one to account for -1 values + stream->writeString(state[i].name); + + stream->writeInt(s.transition.loaded[0]+1,NumStateBits); + stream->writeInt(s.transition.loaded[1]+1,NumStateBits); + stream->writeInt(s.transition.ammo[0]+1,NumStateBits); + stream->writeInt(s.transition.ammo[1]+1,NumStateBits); + stream->writeInt(s.transition.target[0]+1,NumStateBits); + stream->writeInt(s.transition.target[1]+1,NumStateBits); + stream->writeInt(s.transition.wet[0]+1,NumStateBits); + stream->writeInt(s.transition.wet[1]+1,NumStateBits); + stream->writeInt(s.transition.trigger[0]+1,NumStateBits); + stream->writeInt(s.transition.trigger[1]+1,NumStateBits); + stream->writeInt(s.transition.altTrigger[0]+1,NumStateBits); + stream->writeInt(s.transition.altTrigger[1]+1,NumStateBits); + stream->writeInt(s.transition.timeout+1,NumStateBits); + + // Most states don't make use of the motion transition. + if (stream->writeFlag(s.transition.motion[0] != -1 || s.transition.motion[1] != -1)) + { + // This state does + stream->writeInt(s.transition.motion[0]+1,NumStateBits); + stream->writeInt(s.transition.motion[1]+1,NumStateBits); + } + + // Most states don't make use of the generic trigger transitions. Don't transmit + // if that is the case here. + for (U32 j=0; jwriteFlag(s.transition.genericTrigger[j][0] != -1 || s.transition.genericTrigger[j][1] != -1)) + { + stream->writeInt(s.transition.genericTrigger[j][0]+1,NumStateBits); + stream->writeInt(s.transition.genericTrigger[j][1]+1,NumStateBits); + } + } + + if(stream->writeFlag(s.timeoutValue != gDefaultStateData.timeoutValue)) + stream->write(s.timeoutValue); + + stream->writeFlag(s.waitForTimeout); + stream->writeFlag(s.fire); + stream->writeFlag(s.altFire); + stream->writeFlag(s.reload); + stream->writeFlag(s.ejectShell); + stream->writeFlag(s.scaleAnimation); + stream->writeFlag(s.scaleAnimationFP); + stream->writeFlag(s.direction); + + stream->writeFlag(s.sequenceTransitionIn); + stream->writeFlag(s.sequenceTransitionOut); + stream->writeFlag(s.sequenceNeverTransition); + if(stream->writeFlag(s.sequenceTransitionTime != gDefaultStateData.sequenceTransitionTime)) + stream->write(s.sequenceTransitionTime); + + stream->writeString(s.shapeSequence); + stream->writeFlag(s.shapeSequenceScale); + + if(stream->writeFlag(s.energyDrain != gDefaultStateData.energyDrain)) + stream->write(s.energyDrain); + + stream->writeInt(s.loaded,StateData::NumLoadedBits); + stream->writeInt(s.spin,StateData::NumSpinBits); + stream->writeInt(s.recoil,StateData::NumRecoilBits); + + for( U32 j=0; jwriteFlag(s.sequence[j] != gDefaultStateData.sequence[j])) + stream->writeSignedInt(s.sequence[j], 16); + + if(stream->writeFlag(s.sequenceVis[j] != gDefaultStateData.sequenceVis[j])) + stream->writeSignedInt(s.sequenceVis[j],16); + + stream->writeFlag(s.flashSequence[j]); + } + + stream->writeFlag(s.ignoreLoadedForReady); + + if (stream->writeFlag(s.emitter)) + { + stream->writeRangedU32(packed? SimObjectId(s.emitter): + s.emitter->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast); + stream->write(s.emitterTime); + + for( U32 j=0; jwrite(s.emitterNode[j]); + } + } + + sfxWrite( stream, s.sound ); + } + stream->write(maxConcurrentSounds); + stream->writeFlag(useRemainderDT); +} + +void ShapeBaseImageData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + computeCRC = stream->readFlag(); + if(computeCRC) + { + for( U32 j=0; jread(&mCRC[j]); + } + } + + shapeName = stream->readSTString(); // shape 0 for normal use + shapeNameFP = stream->readSTString(); // shape 1 for first person use (optional) + + imageAnimPrefix = stream->readSTString(); + imageAnimPrefixFP = stream->readSTString(); + + stream->read(&mountPoint); + if (stream->readFlag()) + mountOffset.identity(); + else + stream->readAffineTransform(&mountOffset); + if (stream->readFlag()) + eyeOffset.identity(); + else + stream->readAffineTransform(&eyeOffset); + + animateOnServer = stream->readFlag(); + + stream->read(&scriptAnimTransitionTime); + + useEyeNode = stream->readFlag(); + + correctMuzzleVector = stream->readFlag(); + correctMuzzleVectorTP = stream->readFlag(); + firstPerson = stream->readFlag(); + stream->read(&mass); + usesEnergy = stream->readFlag(); + stream->read(&minEnergy); + + for( U32 j=0; jreadFlag(); + } + + projectile = (stream->readFlag() ? + (ProjectileData*)stream->readRangedU32(DataBlockObjectIdFirst, + DataBlockObjectIdLast) : 0); + + cloakable = stream->readFlag(); + lightType = stream->readRangedU32(0, NumLightTypes-1); + if(lightType != NoLight) + { + stream->read(&lightRadius); + stream->read(&lightDuration); + lightColor.red = stream->readFloat(7); + lightColor.green = stream->readFloat(7); + lightColor.blue = stream->readFloat(7); + lightColor.alpha = stream->readFloat(7); + stream->read( &lightBrightness ); + } + + shakeCamera = stream->readFlag(); + if ( shakeCamera ) + { + mathRead( *stream, &camShakeFreq ); + mathRead( *stream, &camShakeAmp ); + } + + mathRead( *stream, &shellExitDir ); + stream->read(&shellExitVariance); + stream->read(&shellVelocity); + + if(stream->readFlag()) + { + casingID = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); + } + + for (U32 i = 0; i < MaxStates; i++) { + if (stream->readFlag()) { + StateData& s = state[i]; + // States info not needed on the client: + // s.allowImageChange + // s.scriptNames + // Transitions are dec. one to restore -1 values + s.name = stream->readSTString(); + + s.transition.loaded[0] = stream->readInt(NumStateBits) - 1; + s.transition.loaded[1] = stream->readInt(NumStateBits) - 1; + s.transition.ammo[0] = stream->readInt(NumStateBits) - 1; + s.transition.ammo[1] = stream->readInt(NumStateBits) - 1; + s.transition.target[0] = stream->readInt(NumStateBits) - 1; + s.transition.target[1] = stream->readInt(NumStateBits) - 1; + s.transition.wet[0] = stream->readInt(NumStateBits) - 1; + s.transition.wet[1] = stream->readInt(NumStateBits) - 1; + s.transition.trigger[0] = stream->readInt(NumStateBits) - 1; + s.transition.trigger[1] = stream->readInt(NumStateBits) - 1; + s.transition.altTrigger[0] = stream->readInt(NumStateBits) - 1; + s.transition.altTrigger[1] = stream->readInt(NumStateBits) - 1; + s.transition.timeout = stream->readInt(NumStateBits) - 1; + + // Motion trigger + if (stream->readFlag()) + { + s.transition.motion[0] = stream->readInt(NumStateBits) - 1; + s.transition.motion[1] = stream->readInt(NumStateBits) - 1; + } + else + { + s.transition.motion[0] = -1; + s.transition.motion[1] = -1; + } + + // Generic triggers + for (U32 j=0; jreadFlag()) + { + s.transition.genericTrigger[j][0] = stream->readInt(NumStateBits) - 1; + s.transition.genericTrigger[j][1] = stream->readInt(NumStateBits) - 1; + } + else + { + s.transition.genericTrigger[j][0] = -1; + s.transition.genericTrigger[j][1] = -1; + } + } + + if(stream->readFlag()) + stream->read(&s.timeoutValue); + else + s.timeoutValue = gDefaultStateData.timeoutValue; + + s.waitForTimeout = stream->readFlag(); + s.fire = stream->readFlag(); + s.altFire = stream->readFlag(); + s.reload = stream->readFlag(); + s.ejectShell = stream->readFlag(); + s.scaleAnimation = stream->readFlag(); + s.scaleAnimationFP = stream->readFlag(); + s.direction = stream->readFlag(); + + s.sequenceTransitionIn = stream->readFlag(); + s.sequenceTransitionOut = stream->readFlag(); + s.sequenceNeverTransition = stream->readFlag(); + if (stream->readFlag()) + stream->read(&s.sequenceTransitionTime); + else + s.sequenceTransitionTime = gDefaultStateData.sequenceTransitionTime; + + s.shapeSequence = stream->readSTString(); + s.shapeSequenceScale = stream->readFlag(); + + if(stream->readFlag()) + stream->read(&s.energyDrain); + else + s.energyDrain = gDefaultStateData.energyDrain; + + s.loaded = (StateData::LoadedState)stream->readInt(StateData::NumLoadedBits); + s.spin = (StateData::SpinState)stream->readInt(StateData::NumSpinBits); + s.recoil = (StateData::RecoilState)stream->readInt(StateData::NumRecoilBits); + + for( U32 j=0; jreadFlag()) + s.sequence[j] = stream->readSignedInt(16); + else + s.sequence[j] = gDefaultStateData.sequence[j]; + + if(stream->readFlag()) + s.sequenceVis[j] = stream->readSignedInt(16); + else + s.sequenceVis[j] = gDefaultStateData.sequenceVis[j]; + + s.flashSequence[j] = stream->readFlag(); + } + + s.ignoreLoadedForReady = stream->readFlag(); + + if (stream->readFlag()) + { + s.emitter = (ParticleEmitterData*) stream->readRangedU32(DataBlockObjectIdFirst, + DataBlockObjectIdLast); + stream->read(&s.emitterTime); + + for( U32 j=0; jread(&(s.emitterNode[j])); + } + } + else + s.emitter = 0; + + sfxRead( stream, &s.sound ); + } + } + + stream->read(&maxConcurrentSounds); + useRemainderDT = stream->readFlag(); + + statesLoaded = true; +} + +void ShapeBaseImageData::inspectPostApply() +{ + Parent::inspectPostApply(); + + // This does not do a very good job of applying changes to states + // which may have occured in the editor, but at least we can do this... + useEyeOffset = !eyeOffset.isIdentity(); +} + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +//---------------------------------------------------------------------------- + +ShapeBase::MountedImage::MountedImage() +{ + for (U32 i=0; i::iterator i = mSoundSources.begin(); i != mSoundSources.end(); i++) + { + SFX_DELETE((*i)); + } + mSoundSources.clear(); + + for (S32 i = 0; i < MaxImageEmitters; i++) + if (bool(emitter[i].emitter)) + emitter[i].emitter->deleteWhenEmpty(); + + if ( lightInfo != NULL ) + delete lightInfo; +} + +void ShapeBase::MountedImage::addSoundSource(SFXSource* source) +{ + if(source != NULL) + { + if(dataBlock->maxConcurrentSounds > 0 && mSoundSources.size() > dataBlock->maxConcurrentSounds) + { + SFX_DELETE(mSoundSources.first()); + mSoundSources.pop_front(); + } + source->play(); + mSoundSources.push_back(source); + } +} + +void ShapeBase::MountedImage::updateSoundSources( const MatrixF &renderTransform ) +{ + // Update all the sounds removing any ones that have stopped. + for ( U32 i=0; i < mSoundSources.size(); ) + { + SFXSource *source = mSoundSources[i]; + + if ( source->isStopped() ) + { + SFX_DELETE( source ); + mSoundSources.erase_fast( i ); + continue; + } + + source->setTransform(renderTransform); + i++; + } +} + +void ShapeBase::MountedImage::updateDoAnimateAllShapes(const ShapeBase* owner) +{ + doAnimateAllShapes = false; + if (!dataBlock) + return; + + // According to ShapeBase::isFirstPerson() the server is always in first person mode. + // Therefore we don't need to animate any other shapes but the one that will be + // used for first person. + + // Sometimes this is forced externally, so honour it. + if (forceAnimateAllShapes) + { + doAnimateAllShapes = true; + return; + } + + if (owner->isClientObject()) + { + // If this client object doesn't have a controlling client, then according to + // ShapeBase::isFirstPerson() it cannot ever be in first person mode. So no need + // to animate any shapes beyond the current one. + if (!owner->getControllingClient()) + { + return; + } + + doAnimateAllShapes = dataBlock->animateAllShapes; + } +} + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +//---------------------------------------------------------------------------- +// Any item with an item image is selectable + +bool ShapeBase::mountImage(ShapeBaseImageData* imageData,U32 imageSlot,bool loaded,NetStringHandle &skinNameHandle) +{ + AssertFatal(imageSlotfire; +} + +bool ShapeBase::isImageAltFiring(U32 imageSlot) +{ + MountedImage& image = mMountedImageList[imageSlot]; + return image.dataBlock && image.state->altFire; +} + +bool ShapeBase::isImageReloading(U32 imageSlot) +{ + MountedImage& image = mMountedImageList[imageSlot]; + return image.dataBlock && image.state->reload; +} + +bool ShapeBase::isImageReady(U32 imageSlot,U32 ns,U32 depth) +{ + // Will pressing the trigger lead to a fire state? + MountedImage& image = mMountedImageList[imageSlot]; + if (depth++ > 5 || !image.dataBlock) + return false; + ShapeBaseImageData::StateData& stateData = (ns == -1) ? + *image.state : image.dataBlock->state[ns]; + if (stateData.fire) + return true; + + // Try the transitions... + if (stateData.ignoreLoadedForReady == true) { + if ((ns = stateData.transition.loaded[true]) != -1) + if (isImageReady(imageSlot,ns,depth)) + return true; + } else { + if ((ns = stateData.transition.loaded[image.loaded]) != -1) + if (isImageReady(imageSlot,ns,depth)) + return true; + } + for (U32 i=0; iname: 0; +} + +void ShapeBase::setImageGenericTriggerState(U32 imageSlot, U32 trigger, bool state) +{ + MountedImage& image = mMountedImageList[imageSlot]; + if (image.dataBlock && image.genericTrigger[trigger] != state) { + setMaskBits(ImageMaskN << imageSlot); + image.genericTrigger[trigger] = state; + } +} + +bool ShapeBase::getImageGenericTriggerState(U32 imageSlot, U32 trigger) +{ + MountedImage& image = mMountedImageList[imageSlot]; + if (!image.dataBlock) + return false; + return image.genericTrigger[trigger]; +} + +void ShapeBase::setImageAmmoState(U32 imageSlot,bool ammo) +{ + MountedImage& image = mMountedImageList[imageSlot]; + if (image.dataBlock && !image.dataBlock->usesEnergy && image.ammo != ammo) { + setMaskBits(ImageMaskN << imageSlot); + image.ammo = ammo; + } +} + +bool ShapeBase::getImageAmmoState(U32 imageSlot) +{ + MountedImage& image = mMountedImageList[imageSlot]; + if (!image.dataBlock) + return false; + return image.ammo; +} + +void ShapeBase::setImageWetState(U32 imageSlot,bool wet) +{ + MountedImage& image = mMountedImageList[imageSlot]; + if (image.dataBlock && image.wet != wet) { + setMaskBits(ImageMaskN << imageSlot); + image.wet = wet; + } +} + +bool ShapeBase::getImageWetState(U32 imageSlot) +{ + MountedImage& image = mMountedImageList[imageSlot]; + if (!image.dataBlock) + return false; + return image.wet; +} + +void ShapeBase::setImageMotionState(U32 imageSlot,bool motion) +{ + MountedImage& image = mMountedImageList[imageSlot]; + if (image.dataBlock && image.motion != motion) { + setMaskBits(ImageMaskN << imageSlot); + image.motion = motion; + } +} + +bool ShapeBase::getImageMotionState(U32 imageSlot) +{ + MountedImage& image = mMountedImageList[imageSlot]; + if (!image.dataBlock) + return false; + return image.motion; +} + +void ShapeBase::setImageTargetState(U32 imageSlot,bool target) +{ + MountedImage& image = mMountedImageList[imageSlot]; + if (image.dataBlock && image.target != target) { + setMaskBits(ImageMaskN << imageSlot); + image.target = target; + } +} + +bool ShapeBase::getImageTargetState(U32 imageSlot) +{ + MountedImage& image = mMountedImageList[imageSlot]; + if (!image.dataBlock) + return false; + return image.target; +} + +void ShapeBase::setImageLoadedState(U32 imageSlot,bool loaded) +{ + MountedImage& image = mMountedImageList[imageSlot]; + if (image.dataBlock && image.loaded != loaded) { + setMaskBits(ImageMaskN << imageSlot); + image.loaded = loaded; + } +} + +bool ShapeBase::getImageLoadedState(U32 imageSlot) +{ + MountedImage& image = mMountedImageList[imageSlot]; + if (!image.dataBlock) + return false; + return image.loaded; +} + +void ShapeBase::getMuzzleVector(U32 imageSlot,VectorF* vec) +{ + MatrixF mat; + getMuzzleTransform(imageSlot,&mat); + + GameConnection * gc = getControllingClient(); + if (gc && !gc->isAIControlled()) + { + MountedImage& image = mMountedImageList[imageSlot]; + + bool fp = gc->isFirstPerson(); + if ((fp && image.dataBlock->correctMuzzleVector) || + (!fp && image.dataBlock->correctMuzzleVectorTP)) + if (getCorrectedAim(mat, vec)) + return; + } + + mat.getColumn(1,vec); +} + +void ShapeBase::getMuzzlePoint(U32 imageSlot,Point3F* pos) +{ + MatrixF mat; + getMuzzleTransform(imageSlot,&mat); + mat.getColumn(3,pos); +} + + +void ShapeBase::getRenderMuzzleVector(U32 imageSlot,VectorF* vec) +{ + MatrixF mat; + getRenderMuzzleTransform(imageSlot,&mat); + + GameConnection * gc = getControllingClient(); + if (gc && !gc->isAIControlled()) + { + MountedImage& image = mMountedImageList[imageSlot]; + + bool fp = gc->isFirstPerson(); + if ((fp && image.dataBlock->correctMuzzleVector) || + (!fp && image.dataBlock->correctMuzzleVectorTP)) + if (getCorrectedAim(mat, vec)) + return; + } + + mat.getColumn(1,vec); +} + +void ShapeBase::getRenderMuzzlePoint(U32 imageSlot,Point3F* pos) +{ + MatrixF mat; + getRenderMuzzleTransform(imageSlot,&mat); + mat.getColumn(3,pos); +} + +//---------------------------------------------------------------------------- + +void ShapeBase::scriptCallback(U32 imageSlot,const char* function) +{ + MountedImage &image = mMountedImageList[imageSlot]; + + char buff1[32]; + dSprintf( buff1, 32, "%d", imageSlot ); + + char buff2[32]; + dSprintf( buff2, 32, "%f", image.dataBlock->useRemainderDT ? image.rDT : 0.0f ); + + Con::executef( image.dataBlock, function, getIdString(), buff1, buff2 ); +} + + +//---------------------------------------------------------------------------- + +void ShapeBase::getMountTransform( S32 index, const MatrixF &xfm, MatrixF *outMat ) +{ + // Returns mount point to world space transform + if ( index >= 0 && index < SceneObject::NumMountPoints) { + S32 ni = mDataBlock->mountPointNode[index]; + if (ni != -1) { + MatrixF mountTransform = mShapeInstance->mNodeTransforms[ni]; + mountTransform.mul( xfm ); + const Point3F& scale = getScale(); + + // The position of the mount point needs to be scaled. + Point3F position = mountTransform.getPosition(); + position.convolve( scale ); + mountTransform.setPosition( position ); + + // Also we would like the object to be scaled to the model. + outMat->mul(mObjToWorld, mountTransform); + return; + } + } + + // Then let SceneObject handle it. + Parent::getMountTransform( index, xfm, outMat ); +} + +void ShapeBase::getImageTransform(U32 imageSlot,MatrixF* mat) +{ + // Image transform in world space + MountedImage& image = mMountedImageList[imageSlot]; + if (image.dataBlock) { + ShapeBaseImageData& data = *image.dataBlock; + U32 shapeIndex = getImageShapeIndex(image); + + MatrixF nmat; + if (data.useEyeNode && isFirstPerson() && data.eyeMountNode[shapeIndex] != -1) { + // We need to animate, even on the server, to make sure the nodes are in the correct location. + image.shapeInstance[shapeIndex]->animate(); + + getEyeBaseTransform(&nmat); + + MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]]; + + mat->mul(nmat, mountTransform); + } + else if (data.useEyeOffset && isFirstPerson()) { + getEyeTransform(&nmat); + mat->mul(nmat,data.eyeOffset); + } + else { + getMountTransform( image.dataBlock->mountPoint, MatrixF::Identity, &nmat ); + mat->mul(nmat,data.mountTransform[shapeIndex]); + } + } + else + *mat = mObjToWorld; +} + +void ShapeBase::getImageTransform(U32 imageSlot,S32 node,MatrixF* mat) +{ + // Image transform in world space + MountedImage& image = mMountedImageList[imageSlot]; + if (image.dataBlock) + { + if (node != -1) + { + ShapeBaseImageData& data = *image.dataBlock; + U32 shapeIndex = getImageShapeIndex(image); + + MatrixF nmat = image.shapeInstance[shapeIndex]->mNodeTransforms[node]; + MatrixF mmat; + + if (data.useEyeNode && isFirstPerson() && data.eyeMountNode[shapeIndex] != -1) + { + // We need to animate, even on the server, to make sure the nodes are in the correct location. + image.shapeInstance[shapeIndex]->animate(); + + MatrixF emat; + getEyeBaseTransform(&emat); + + MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]]; + mountTransform.affineInverse(); + + mmat.mul(emat, mountTransform); + } + else if (data.useEyeOffset && isFirstPerson()) + { + MatrixF emat; + getEyeTransform(&emat); + mmat.mul(emat,data.eyeOffset); + } + else + { + MatrixF emat; + getMountTransform( image.dataBlock->mountPoint, MatrixF::Identity, &emat ); + mmat.mul(emat,data.mountTransform[shapeIndex]); + } + + mat->mul(mmat, nmat); + } + else + getImageTransform(imageSlot,mat); + } + else + *mat = mObjToWorld; +} + +void ShapeBase::getImageTransform(U32 imageSlot,StringTableEntry nodeName,MatrixF* mat) +{ + getImageTransform( imageSlot, getNodeIndex( imageSlot, nodeName ), mat ); +} + +void ShapeBase::getMuzzleTransform(U32 imageSlot,MatrixF* mat) +{ + // Muzzle transform in world space + MountedImage& image = mMountedImageList[imageSlot]; + if (image.dataBlock) + getImageTransform(imageSlot,image.dataBlock->muzzleNode[getImageShapeIndex(image)],mat); + else + *mat = mObjToWorld; +} + + +//---------------------------------------------------------------------------- + +void ShapeBase::getRenderMountTransform( F32 delta, S32 mountPoint, const MatrixF &xfm, MatrixF *outMat ) +{ + // Returns mount point to world space transform + if ( mountPoint >= 0 && mountPoint < SceneObject::NumMountPoints) { + S32 ni = mDataBlock->mountPointNode[mountPoint]; + if (ni != -1) { + MatrixF mountTransform = mShapeInstance->mNodeTransforms[ni]; + mountTransform.mul( xfm ); + const Point3F& scale = getScale(); + + // The position of the mount point needs to be scaled. + Point3F position = mountTransform.getPosition(); + position.convolve( scale ); + mountTransform.setPosition( position ); + + // Also we would like the object to be scaled to the model. + mountTransform.scale( scale ); + outMat->mul(getRenderTransform(), mountTransform); + return; + } + } + + // Then let SceneObject handle it. + Parent::getRenderMountTransform( delta, mountPoint, xfm, outMat ); +} + + +void ShapeBase::getRenderImageTransform( U32 imageSlot, MatrixF* mat, bool noEyeOffset ) +{ + // Image transform in world space + MountedImage& image = mMountedImageList[imageSlot]; + if (image.dataBlock) + { + ShapeBaseImageData& data = *image.dataBlock; + U32 shapeIndex = getImageShapeIndex(image); + + MatrixF nmat; + if ( data.useEyeNode && isFirstPerson() && data.eyeMountNode[shapeIndex] != -1 ) { + getRenderEyeBaseTransform(&nmat); + + MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]]; + + mat->mul(nmat, mountTransform); + } + else if ( !noEyeOffset && data.useEyeOffset && isFirstPerson() ) + { + getRenderEyeTransform(&nmat); + mat->mul(nmat,data.eyeOffset); + } + else + { + getRenderMountTransform( 0.0f, data.mountPoint, MatrixF::Identity, &nmat ); + mat->mul(nmat,data.mountTransform[shapeIndex]); + } + } + else + *mat = getRenderTransform(); +} + +void ShapeBase::getRenderImageTransform(U32 imageSlot,S32 node,MatrixF* mat) +{ + // Image transform in world space + MountedImage& image = mMountedImageList[imageSlot]; + if (image.dataBlock) + { + if (node != -1) + { + ShapeBaseImageData& data = *image.dataBlock; + U32 shapeIndex = getImageShapeIndex(image); + + MatrixF nmat = image.shapeInstance[shapeIndex]->mNodeTransforms[node]; + MatrixF mmat; + + if ( data.useEyeNode && isFirstPerson() && data.eyeMountNode[shapeIndex] != -1 ) + { + MatrixF emat; + getRenderEyeBaseTransform(&emat); + + MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]]; + mountTransform.affineInverse(); + + mmat.mul(emat, mountTransform); + } + else if ( data.useEyeOffset && isFirstPerson() ) + { + MatrixF emat; + getRenderEyeTransform(&emat); + mmat.mul(emat,data.eyeOffset); + } + else + { + MatrixF emat; + getRenderMountTransform( 0.0f, data.mountPoint, MatrixF::Identity, &emat ); + mmat.mul(emat,data.mountTransform[shapeIndex]); + } + + mat->mul(mmat, nmat); + } + else + getRenderImageTransform(imageSlot,mat); + } + else + *mat = getRenderTransform(); +} + +void ShapeBase::getRenderImageTransform(U32 imageSlot,StringTableEntry nodeName,MatrixF* mat) +{ + getRenderImageTransform( imageSlot, getNodeIndex( imageSlot, nodeName ), mat ); +} + +void ShapeBase::getRenderMuzzleTransform(U32 imageSlot,MatrixF* mat) +{ + // Muzzle transform in world space + MountedImage& image = mMountedImageList[imageSlot]; + if (image.dataBlock) + getRenderImageTransform(imageSlot,image.dataBlock->muzzleNode[getImageShapeIndex(image)],mat); + else + *mat = getRenderTransform(); +} + + +void ShapeBase::getRetractionTransform(U32 imageSlot,MatrixF* mat) +{ + // Muzzle transform in world space + MountedImage& image = mMountedImageList[imageSlot]; + if (image.dataBlock) { + ShapeBaseImageData& data = *image.dataBlock; + U32 imageShapeIndex = getImageShapeIndex(image); + if (data.retractNode[imageShapeIndex] != -1) + getImageTransform(imageSlot,data.retractNode[imageShapeIndex],mat); + else + getImageTransform(imageSlot,data.muzzleNode[imageShapeIndex],mat); + } else { + *mat = getTransform(); + } +} + + +void ShapeBase::getRenderRetractionTransform(U32 imageSlot,MatrixF* mat) +{ + // Muzzle transform in world space + MountedImage& image = mMountedImageList[imageSlot]; + if (image.dataBlock) { + ShapeBaseImageData& data = *image.dataBlock; + U32 imageShapeIndex = getImageShapeIndex(image); + if (data.retractNode[imageShapeIndex] != -1) + getRenderImageTransform(imageSlot,data.retractNode[imageShapeIndex],mat); + else + getRenderImageTransform(imageSlot,data.muzzleNode[imageShapeIndex],mat); + } else { + *mat = getRenderTransform(); + } +} + + +//---------------------------------------------------------------------------- + +S32 ShapeBase::getNodeIndex(U32 imageSlot,StringTableEntry nodeName) +{ + MountedImage& image = mMountedImageList[imageSlot]; + if (image.dataBlock) + return image.dataBlock->shape[getImageShapeIndex(image)]->findNode(nodeName); + else + return -1; +} + +// Modify muzzle if needed to aim at whatever is straight in front of the camera. Let the +// caller know if we actually modified the result. +bool ShapeBase::getCorrectedAim(const MatrixF& muzzleMat, VectorF* result) +{ + F32 pullInD = sFullCorrectionDistance; + const F32 maxAdjD = 500; + + VectorF aheadVec(0, maxAdjD, 0); + + MatrixF camMat; + Point3F camPos; + + F32 pos = 0; + GameConnection * gc = getControllingClient(); + if (gc && !gc->isFirstPerson()) + pos = 1.0f; + + getCameraTransform(&pos, &camMat); + + camMat.getColumn(3, &camPos); + camMat.mulV(aheadVec); + Point3F aheadPoint = (camPos + aheadVec); + + // Should we check if muzzle point is really close to camera? Does that happen? + Point3F muzzlePos; + muzzleMat.getColumn(3, &muzzlePos); + + Point3F collidePoint; + VectorF collideVector; + disableCollision(); + RayInfo rinfo; + if (getContainer()->castRay(camPos, aheadPoint, STATIC_COLLISION_TYPEMASK|DAMAGEABLE_TYPEMASK, &rinfo) && + (mDot(rinfo.point - mObjToWorld.getPosition(), mObjToWorld.getForwardVector()) > 0)) // Check if point is behind us (could happen in 3rd person view) + collideVector = ((collidePoint = rinfo.point) - camPos); + else + collideVector = ((collidePoint = aheadPoint) - camPos); + enableCollision(); + + // For close collision we want to NOT aim at ground since we're bending + // the ray here as it is. But we don't want to pop, so adjust continuously. + F32 lenSq = collideVector.lenSquared(); + if (lenSq < (pullInD * pullInD) && lenSq > 0.04) + { + F32 len = mSqrt(lenSq); + F32 mid = pullInD; // (pullInD + len) / 2.0; + // This gives us point beyond to focus on- + collideVector *= (mid / len); + collidePoint = (camPos + collideVector); + } + + VectorF muzzleToCollide = (collidePoint - muzzlePos); + lenSq = muzzleToCollide.lenSquared(); + if (lenSq > 0.04) + { + muzzleToCollide *= (1 / mSqrt(lenSq)); + * result = muzzleToCollide; + return true; + } + return false; +} + +//---------------------------------------------------------------------------- + +void ShapeBase::updateMass() +{ + if (mDataBlock) { + F32 imass = 0; + for (U32 i = 0; i < MaxMountedImages; i++) { + MountedImage& image = mMountedImageList[i]; + if (image.dataBlock) + imass += image.dataBlock->mass; + } + // + mMass = mDataBlock->mass + imass; + mOneOverMass = 1 / mMass; + } +} + +void ShapeBase::onImage(U32 imageSlot, bool unmount) +{ +} + +void ShapeBase::onImageRecoil(U32,ShapeBaseImageData::StateData::RecoilState) +{ +} + +void ShapeBase::onImageStateAnimation(U32 imageSlot, const char* seqName, bool direction, bool scaleToState, F32 stateTimeOutValue) +{ +} + +void ShapeBase::onImageAnimThreadChange(U32 imageSlot, S32 imageShapeIndex, ShapeBaseImageData::StateData* lastState, const char* anim, F32 pos, F32 timeScale, bool reset) +{ +} + +void ShapeBase::onImageAnimThreadUpdate(U32 imageSlot, S32 imageShapeIndex, F32 dt) +{ +} + + +//---------------------------------------------------------------------------- + +void ShapeBase::setImage( U32 imageSlot, + ShapeBaseImageData* imageData, + NetStringHandle& skinNameHandle, + bool loaded, + bool ammo, + bool triggerDown, + bool altTriggerDown, + bool motion, + bool genericTrigger0, + bool genericTrigger1, + bool genericTrigger2, + bool genericTrigger3, + bool target) +{ + AssertFatal(imageSlotreSkin(newSkin, image.appliedSkinName); + image.appliedSkinName = newSkin; + } + } + } + } + return; + } + + // Check to see if we need to delay image changes until state change. + if (!isGhost()) { + if (imageData && image.dataBlock && !image.state->allowImageChange) { + image.nextImage = imageData; + image.nextSkinNameHandle = skinNameHandle; + image.nextLoaded = loaded; + return; + } + } + + // Mark that updates are happenin'. + setMaskBits(ImageMaskN << imageSlot); + + // Notify script unmount since we're swapping datablocks. + if (image.dataBlock && !isGhost()) { + F32 dt = image.dataBlock->useRemainderDT ? image.rDT : 0.0f; + image.dataBlock->onUnmount_callback( this, imageSlot, dt ); + } + + // Stop anything currently going on with the image. + resetImageSlot(imageSlot); + + // If we're just unselecting the current shape without swapping + // in a new one, then bail. + if (!imageData) { + onImage( imageSlot, true); + return; + } + + // Otherwise, init the new shape. + image.dataBlock = imageData; + image.state = &image.dataBlock->state[0]; + image.skinNameHandle = skinNameHandle; + image.updateDoAnimateAllShapes(this); + + for (U32 i=0; ishapeIsValid[i]) + image.shapeInstance[i] = new TSShapeInstance(image.dataBlock->shape[i], isClientObject()); + } + + if (isClientObject()) + { + for (U32 i=0; icloneMaterialList(); + String newSkin = skinNameHandle.getString(); + image.shapeInstance[i]->reSkin(newSkin, image.appliedSkinName); + image.appliedSkinName = newSkin; + } + } + } + image.loaded = loaded; + image.ammo = ammo; + image.triggerDown = triggerDown; + image.altTriggerDown = altTriggerDown; + image.target = target; + image.motion = motion; + image.genericTrigger[0] = genericTrigger0; + image.genericTrigger[1] = genericTrigger1; + image.genericTrigger[2] = genericTrigger2; + image.genericTrigger[3] = genericTrigger3; + + // The server needs the shape loaded for muzzle mount nodes + // but it doesn't need to run any of the animations, unless the image + // has animateOnServer set. Then the server needs to animate as well. + // This is often set when using useEyeNode. + for (U32 i=0; ianimateOnServer || isGhost()) + { + for (U32 i=0; iisAnimated[i]) { + image.animThread[i] = image.shapeInstance[i]->addThread(); + image.shapeInstance[i]->setTimeScale(image.animThread[i],0); + } + if (image.dataBlock->hasFlash[i]) { + image.flashThread[i] = image.shapeInstance[i]->addThread(); + image.shapeInstance[i]->setTimeScale(image.flashThread[i],0); + } + if (image.dataBlock->ambientSequence[i] != -1) { + image.ambientThread[i] = image.shapeInstance[i]->addThread(); + image.shapeInstance[i]->setTimeScale(image.ambientThread[i],1); + image.shapeInstance[i]->setSequence(image.ambientThread[i], + image.dataBlock->ambientSequence[i],0); + } + if (image.dataBlock->spinSequence[i] != -1) { + image.spinThread[i] = image.shapeInstance[i]->addThread(); + image.shapeInstance[i]->setTimeScale(image.spinThread[i],1); + image.shapeInstance[i]->setSequence(image.spinThread[i], + image.dataBlock->spinSequence[i],0); + } + } + } + + // Set the image to its starting state. + setImageState(imageSlot, (U32)0, true); + + // Update the mass for the mount object. + updateMass(); + + // Notify script mount. + if ( !isGhost() ) + { + F32 dt = image.dataBlock->useRemainderDT ? image.rDT : 0.0f; + image.dataBlock->onMount_callback( this, imageSlot, dt ); + } + else + { + if ( imageData->lightType == ShapeBaseImageData::PulsingLight ) + image.lightStart = Sim::getCurrentTime(); + } + + onImage(imageSlot, false); + + // Done. +} + + +//---------------------------------------------------------------------------- + +void ShapeBase::resetImageSlot(U32 imageSlot) +{ + AssertFatal(imageSlot::iterator i = image.mSoundSources.begin(); i != image.mSoundSources.end(); i++) + { + SFX_DELETE((*i)); + } + image.mSoundSources.clear(); + + for (S32 i = 0; i < MaxImageEmitters; i++) { + MountedImage::ImageEmitter& em = image.emitter[i]; + if (bool(em.emitter)) { + em.emitter->deleteWhenEmpty(); + em.emitter = 0; + } + } + + image.dataBlock = 0; + image.nextImage = InvalidImagePtr; + image.skinNameHandle = NetStringHandle(); + image.nextSkinNameHandle = NetStringHandle(); + image.state = 0; + image.delayTime = 0; + image.rDT = 0; + image.ammo = false; + image.triggerDown = false; + image.altTriggerDown = false; + image.loaded = false; + image.motion = false; + + for (U32 i=0; ifireState != -1) + return image.dataBlock->fireState; + return 0; +} + +U32 ShapeBase::getImageAltFireState(U32 imageSlot) +{ + MountedImage& image = mMountedImageList[imageSlot]; + // If there is no alternate fire state, then try state 0 + if (image.dataBlock && image.dataBlock->altFireState != -1) + return image.dataBlock->altFireState; + return 0; +} + +U32 ShapeBase::getImageReloadState(U32 imageSlot) +{ + MountedImage& image = mMountedImageList[imageSlot]; + // If there is no reload state, then try state 0 + if (image.dataBlock && image.dataBlock->reloadState != -1) + return image.dataBlock->reloadState; + return 0; +} + + +//---------------------------------------------------------------------------- + +bool ShapeBase::hasImageState(U32 imageSlot, const char* state) +{ + if (!state || !state[0]) + return false; + + MountedImage& image = mMountedImageList[imageSlot]; + if (image.dataBlock) + { + for (U32 i = 0; i < ShapeBaseImageData::MaxStates; i++) + { + ShapeBaseImageData::StateData& sd = image.dataBlock->state[i]; + if (sd.name && !dStricmp(state, sd.name)) + return true; + } + } + + return false; +} + +void ShapeBase::setImageState(U32 imageSlot, U32 newState,bool force) +{ + if (!mMountedImageList[imageSlot].dataBlock) + return; + MountedImage& image = mMountedImageList[imageSlot]; + + + // The client never enters the initial fire state on its own, but it + // will continue to set that state... + if (isGhost() && !force && newState == image.dataBlock->fireState) { + if (image.state != &image.dataBlock->state[newState]) + return; + } + + // The client never enters the initial alternate fire state on its own, but it + // will continue to set that state... + if (isGhost() && !force && newState == image.dataBlock->altFireState) { + if (image.state != &image.dataBlock->state[newState]) + return; + } + + // The client never enters the initial reload state on its own, but it + // will continue to set that state... + if (isGhost() && !force && newState == image.dataBlock->reloadState) { + if (image.state != &image.dataBlock->state[newState]) + return; + } + + // Eject shell casing on every state change (client side only) + ShapeBaseImageData::StateData& nextStateData = image.dataBlock->state[newState]; + if (isGhost() && nextStateData.ejectShell) { + ejectShellCasing( imageSlot ); + } + + + // Server must animate the shape if it is a firestate... + if (isServerObject() && (image.dataBlock->state[newState].fire || image.dataBlock->state[newState].altFire)) + mShapeInstance->animate(); + + // Obtain the image's shape index for future use. + U32 imageShapeIndex = getImageShapeIndex(image); + image.lastShapeIndex = imageShapeIndex; + + // If going back into the same state, just reset the timer + // and invoke the script callback + if (!force && image.state == &image.dataBlock->state[newState]) { + image.delayTime = image.state->timeoutValue; + if (image.state->script && !isGhost()) + scriptCallback(imageSlot,image.state->script); + + // If this is a flash sequence, we need to select a new position for the + // animation if we're returning to that state... + F32 randomPos = Platform::getRandom(); + for (U32 i=0; ishapeIsValid[i] || i != imageShapeIndex && !image.doAnimateAllShapes) + continue; + + if (image.animThread[i] && image.state->sequence[i] != -1 && image.state->flashSequence[i]) { + image.shapeInstance[i]->setPos(image.animThread[i], randomPos); + image.shapeInstance[i]->setTimeScale(image.animThread[i], 0); + if (image.flashThread[i]) + image.shapeInstance[i]->setPos(image.flashThread[i], 0); + } + } + + return; + } + + F32 lastDelay = image.delayTime; + ShapeBaseImageData::StateData* lastState = image.state; + image.state = &image.dataBlock->state[newState]; + + // + // Do state cleanup first... + // + ShapeBaseImageData::StateData& stateData = *image.state; + image.delayTime = stateData.timeoutValue; + + // Mount pending images + if (image.nextImage != InvalidImagePtr && stateData.allowImageChange) { + setImage(imageSlot,image.nextImage,image.nextSkinNameHandle,image.nextLoaded); + return; + } + + // Reset cyclic sequences back to the first frame to turn it off + // (the first key frame should be it's off state). + // We need to do this across all image shapes to make sure we have no hold overs when switching + // rendering shapes while in the middle of a state change. + for (U32 i=0; igetSequence()->isCyclic() && (stateData.sequenceNeverTransition || !(stateData.sequenceTransitionIn || lastState->sequenceTransitionOut))) { + image.shapeInstance[i]->setPos(image.animThread[i],0); + image.shapeInstance[i]->setTimeScale(image.animThread[i],0); + } + if (image.flashThread[i]) { + image.shapeInstance[i]->setPos(image.flashThread[i],0); + image.shapeInstance[i]->setTimeScale(image.flashThread[i],0); + } + } + + // Broadcast the reset + onImageAnimThreadChange(imageSlot, imageShapeIndex, lastState, NULL, 0, 0, true); + + // Check for immediate transitions, but only if we don't need to wait for + // a time out. Only perform this wait if we're not forced to change. + S32 ns; + if (image.delayTime <= 0 || !stateData.waitForTimeout) + { + if ((ns = stateData.transition.loaded[image.loaded]) != -1) { + setImageState(imageSlot,ns); + return; + } + for (U32 i=0; istate[newState].fire) { + setMaskBits(ImageMaskN << imageSlot); + image.fireCount = (image.fireCount + 1) & 0x7; + } + if (!isGhost() && image.dataBlock->state[newState].altFire) { + setMaskBits(ImageMaskN << imageSlot); + image.altFireCount = (image.altFireCount + 1) & 0x7; + } + if (!isGhost() && image.dataBlock->state[newState].reload) { + setMaskBits(ImageMaskN << imageSlot); + image.reloadCount = (image.reloadCount + 1) & 0x7; + } + + // Apply recoil + if (stateData.recoil != ShapeBaseImageData::StateData::NoRecoil) + onImageRecoil(imageSlot,stateData.recoil); + + // Apply image state animation on mounting shape + if (stateData.shapeSequence && stateData.shapeSequence[0]) + { + onImageStateAnimation(imageSlot, stateData.shapeSequence, stateData.direction, stateData.shapeSequenceScale, stateData.timeoutValue); + } + + // Delete any loooping sounds that were in the previous state. + if (lastState->sound && lastState->sound->getDescription()->mIsLooping) + { + for(Vector::iterator i = image.mSoundSources.begin(); i != image.mSoundSources.end(); i++) + SFX_DELETE((*i)); + + image.mSoundSources.clear(); + } + + // Play sound + if( stateData.sound && isGhost() ) + { + const Point3F& velocity = getVelocity(); + image.addSoundSource(SFX->createSource( stateData.sound, &getRenderTransform(), &velocity )); + } + + // Play animation + updateAnimThread(imageSlot, imageShapeIndex, lastState); + for (U32 i=0; ishapeIsValid[i] || i != imageShapeIndex && !image.doAnimateAllShapes) + continue; + + // Start spin thread + if (image.spinThread[i]) { + switch (stateData.spin) { + case ShapeBaseImageData::StateData::IgnoreSpin: + image.shapeInstance[i]->setTimeScale(image.spinThread[i], image.shapeInstance[i]->getTimeScale(image.spinThread[i])); + break; + case ShapeBaseImageData::StateData::NoSpin: + image.shapeInstance[i]->setTimeScale(image.spinThread[i],0); + break; + case ShapeBaseImageData::StateData::SpinUp: + if (lastState->spin == ShapeBaseImageData::StateData::SpinDown) + image.delayTime *= 1.0f - (lastDelay / stateData.timeoutValue); + break; + case ShapeBaseImageData::StateData::SpinDown: + if (lastState->spin == ShapeBaseImageData::StateData::SpinUp) + image.delayTime *= 1.0f - (lastDelay / stateData.timeoutValue); + break; + case ShapeBaseImageData::StateData::FullSpin: + image.shapeInstance[i]->setTimeScale(image.spinThread[i],1); + break; + } + } + } + + // Start particle emitter on the client (client side only) + if (isGhost() && stateData.emitter) + startImageEmitter(image,stateData); + + // Script callback on server + if (stateData.script && stateData.script[0] && !isGhost()) + scriptCallback(imageSlot,stateData.script); + + // If there is a zero timeout, and a timeout transition, then + // go ahead and transition imediately. + if (!image.delayTime) + { + if ((ns = stateData.transition.timeout) != -1) + { + setImageState(imageSlot,ns); + return; + } + } +} + +void ShapeBase::updateAnimThread(U32 imageSlot, S32 imageShapeIndex, ShapeBaseImageData::StateData* lastState) +{ + MountedImage& image = mMountedImageList[imageSlot]; + ShapeBaseImageData::StateData& stateData = *image.state; + + F32 randomPos = Platform::getRandom(); + for (U32 i=0; ishapeIsValid[i] || i != imageShapeIndex && !image.doAnimateAllShapes) + continue; + + if (image.animThread[i] && stateData.sequence[i] != -1) + { + S32 seqIndex = stateData.sequence[i]; // Standard index without any prefix + bool scaleAnim = stateData.scaleAnimation; + if (i == ShapeBaseImageData::FirstPersonImageShape) + scaleAnim = stateData.scaleAnimationFP; + + // We're going to apply various prefixes to determine the final sequence to use. + // Here is the order: + // shapeBasePrefix_scriptPrefix_baseAnimName + // shapeBasePrefix_baseAnimName + // scriptPrefix_baseAnimName + // baseAnimName + + // Collect the prefixes + const char* shapeBasePrefix = getImageAnimPrefix(imageSlot, i); + bool hasShapeBasePrefix = shapeBasePrefix && shapeBasePrefix[0]; + const char* scriptPrefix = getImageScriptAnimPrefix(imageSlot).getString(); + bool hasScriptPrefix = scriptPrefix && scriptPrefix[0]; + + // Find the final sequence based on the prefix combinations + if (hasShapeBasePrefix || hasScriptPrefix) + { + bool found = false; + String baseSeqName(image.shapeInstance[i]->getShape()->getSequenceName(stateData.sequence[i])); + + if (!found && hasShapeBasePrefix && hasScriptPrefix) + { + String seqName = String(shapeBasePrefix) + String("_") + String(scriptPrefix) + String("_") + baseSeqName; + S32 index = image.shapeInstance[i]->getShape()->findSequence(seqName); + if (index != -1) + { + seqIndex = index; + found = true; + } + } + + if (!found && hasShapeBasePrefix) + { + String seqName = String(shapeBasePrefix) + String("_") + baseSeqName; + S32 index = image.shapeInstance[i]->getShape()->findSequence(seqName); + if (index != -1) + { + seqIndex = index; + found = true; + } + } + + if (!found && hasScriptPrefix) + { + String seqName = String(scriptPrefix) + String("_") + baseSeqName; + S32 index = image.shapeInstance[i]->getShape()->findSequence(seqName); + if (index != -1) + { + seqIndex = index; + found = true; + } + } + } + + if (seqIndex != -1) + { + if (!lastState) + { + // No lastState indicates that we are just switching animation sequences, not states. Transition into this new sequence, but only + // if it is different than what we're currently playing. + S32 prevSeq = -1; + if (image.animThread[i]->hasSequence()) + { + prevSeq = image.shapeInstance[i]->getSequence(image.animThread[i]); + } + if (seqIndex != prevSeq) + { + image.shapeInstance[i]->transitionToSequence(image.animThread[i], seqIndex, stateData.direction ? 0.0f : 1.0f, image.dataBlock->scriptAnimTransitionTime, true); + } + } + else if (!stateData.sequenceNeverTransition && stateData.sequenceTransitionTime && (stateData.sequenceTransitionIn || lastState->sequenceTransitionOut)) + { + image.shapeInstance[i]->transitionToSequence(image.animThread[i], seqIndex, stateData.direction ? 0.0f : 1.0f, stateData.sequenceTransitionTime, true); + } + else + { + image.shapeInstance[i]->setSequence(image.animThread[i], seqIndex, stateData.direction ? 0.0f : 1.0f); + } + + if (stateData.flashSequence[i] == false) + { + F32 timeScale = (scaleAnim && stateData.timeoutValue) ? + image.shapeInstance[i]->getDuration(image.animThread[i]) / stateData.timeoutValue : 1.0f; + image.shapeInstance[i]->setTimeScale(image.animThread[i], stateData.direction ? timeScale : -timeScale); + + // Broadcast the sequence change + String seqName = image.shapeInstance[i]->getShape()->getSequenceName(stateData.sequence[i]); + onImageAnimThreadChange(imageSlot, imageShapeIndex, lastState, seqName, stateData.direction ? 0.0f : 1.0f, stateData.direction ? timeScale : -timeScale); + } + else + { + image.shapeInstance[i]->setPos(image.animThread[i], randomPos); + image.shapeInstance[i]->setTimeScale(image.animThread[i], 0); + + S32 seqVisIndex = stateData.sequenceVis[i]; + + // Go through the same process as the animThread sequence to find the flashThread sequence + if (hasShapeBasePrefix || hasScriptPrefix) + { + bool found = false; + String baseVisSeqName(image.shapeInstance[i]->getShape()->getSequenceName(stateData.sequenceVis[i])); + + if (!found && hasShapeBasePrefix && hasScriptPrefix) + { + String seqName = String(shapeBasePrefix) + String("_") + String(scriptPrefix) + String("_") + baseVisSeqName; + S32 index = image.shapeInstance[i]->getShape()->findSequence(seqName); + if (index != -1) + { + seqVisIndex = index; + found = true; + } + } + + if (!found && hasShapeBasePrefix) + { + String seqName = String(shapeBasePrefix) + String("_") + baseVisSeqName; + S32 index = image.shapeInstance[i]->getShape()->findSequence(seqName); + if (index != -1) + { + seqVisIndex = index; + found = true; + } + } + + if (!found && hasScriptPrefix) + { + String seqName = String(scriptPrefix) + String("_") + baseVisSeqName; + S32 index = image.shapeInstance[i]->getShape()->findSequence(seqName); + if (index != -1) + { + seqVisIndex = index; + found = true; + } + } + } + + image.shapeInstance[i]->setSequence(image.flashThread[i], seqVisIndex, 0); + image.shapeInstance[i]->setPos(image.flashThread[i], 0); + F32 timeScale = (scaleAnim && stateData.timeoutValue) ? + image.shapeInstance[i]->getDuration(image.flashThread[i]) / stateData.timeoutValue : 1.0f; + image.shapeInstance[i]->setTimeScale(image.flashThread[i], timeScale); + + // Broadcast the sequence change + String seqName = image.shapeInstance[i]->getShape()->getSequenceName(stateData.sequenceVis[i]); + onImageAnimThreadChange(imageSlot, imageShapeIndex, lastState, seqName, stateData.direction ? 0.0f : 1.0f, stateData.direction ? timeScale : -timeScale); + } + } + } + } +} + + +//---------------------------------------------------------------------------- + +void ShapeBase::updateImageState(U32 imageSlot,F32 dt) +{ + if (!mMountedImageList[imageSlot].dataBlock) + return; + MountedImage& image = mMountedImageList[imageSlot]; + ShapeBaseImageData& imageData = *image.dataBlock; + + image.rDT = dt; + F32 elapsed; + +TICKAGAIN: + + ShapeBaseImageData::StateData& stateData = *image.state; + + if ( image.delayTime > dt ) + elapsed = dt; + else + elapsed = image.delayTime; + + dt = elapsed; + image.rDT -= elapsed; + + image.delayTime -= dt; + + // Energy management + if (imageData.usesEnergy) + { + F32 newEnergy = getEnergyLevel() - stateData.energyDrain * dt; + if (newEnergy < 0) + newEnergy = 0; + setEnergyLevel(newEnergy); + + if (!isGhost()) + { + bool ammo = newEnergy > imageData.minEnergy; + if (ammo != image.ammo) + { + setMaskBits(ImageMaskN << imageSlot); + image.ammo = ammo; + } + } + } + + // Check for transitions. On some states we must wait for the + // full timeout value before moving on. + if (image.delayTime <= 0 || !stateData.waitForTimeout) + { + S32 ns; + + if ((ns = stateData.transition.loaded[image.loaded]) != -1) + setImageState(imageSlot,ns); + else if ((ns = stateData.transition.genericTrigger[0][image.genericTrigger[0]]) != -1) + setImageState(imageSlot,ns); + else if ((ns = stateData.transition.genericTrigger[1][image.genericTrigger[1]]) != -1) + setImageState(imageSlot,ns); + else if ((ns = stateData.transition.genericTrigger[2][image.genericTrigger[2]]) != -1) + setImageState(imageSlot,ns); + else if ((ns = stateData.transition.genericTrigger[3][image.genericTrigger[3]]) != -1) + setImageState(imageSlot,ns); + else if ((ns = stateData.transition.ammo[image.ammo]) != -1) + setImageState(imageSlot,ns); + else if ((ns = stateData.transition.target[image.target]) != -1) + setImageState(imageSlot,ns); + else if ((ns = stateData.transition.wet[image.wet]) != -1) + setImageState(imageSlot,ns); + else if ((ns = stateData.transition.motion[image.motion]) != -1) + setImageState(imageSlot,ns); + else if ((ns = stateData.transition.trigger[image.triggerDown]) != -1) + setImageState(imageSlot,ns); + else if ((ns = stateData.transition.altTrigger[image.altTriggerDown]) != -1) + setImageState(imageSlot,ns); + else if (image.delayTime <= 0 && (ns = stateData.transition.timeout) != -1) + setImageState(imageSlot,ns); + } + + // Update the spinning thread timeScale + U32 imageShapeIndex = getImageShapeIndex(image); + for (U32 i=0; ishapeIsValid[i] || i != imageShapeIndex && !image.doAnimateAllShapes) + continue; + + if (image.spinThread[i]) + { + float timeScale; + + switch (stateData.spin) + { + case ShapeBaseImageData::StateData::IgnoreSpin: + case ShapeBaseImageData::StateData::NoSpin: + case ShapeBaseImageData::StateData::FullSpin: + { + timeScale = 0; + image.shapeInstance[i]->setTimeScale(image.spinThread[i], image.shapeInstance[i]->getTimeScale(image.spinThread[i])); + break; + } + + case ShapeBaseImageData::StateData::SpinUp: + { + timeScale = 1.0f - image.delayTime / stateData.timeoutValue; + image.shapeInstance[i]->setTimeScale(image.spinThread[i],timeScale); + break; + } + + case ShapeBaseImageData::StateData::SpinDown: + { + timeScale = image.delayTime / stateData.timeoutValue; + image.shapeInstance[i]->setTimeScale(image.spinThread[i],timeScale); + break; + } + } + } + } + + if ( image.rDT > 0.0f && image.delayTime > 0.0f && imageData.useRemainderDT && dt != 0.0f ) + goto TICKAGAIN; +} + + +//---------------------------------------------------------------------------- + +void ShapeBase::updateImageAnimation(U32 imageSlot, F32 dt) +{ + if (!mMountedImageList[imageSlot].dataBlock) + return; + MountedImage& image = mMountedImageList[imageSlot]; + U32 imageShapeIndex = getImageShapeIndex(image); + + // Advance animation threads + for (U32 i=0; ishapeIsValid[i] || i != imageShapeIndex && !image.doAnimateAllShapes) + continue; + + if (image.ambientThread[i]) + image.shapeInstance[i]->advanceTime(dt,image.ambientThread[i]); + if (image.animThread[i]) + image.shapeInstance[i]->advanceTime(dt,image.animThread[i]); + if (image.spinThread[i]) + image.shapeInstance[i]->advanceTime(dt,image.spinThread[i]); + if (image.flashThread[i]) + image.shapeInstance[i]->advanceTime(dt,image.flashThread[i]); + } + + // Broadcast the update + onImageAnimThreadUpdate(imageSlot, imageShapeIndex, dt); + + image.updateSoundSources(getRenderTransform()); + + // Particle emission + for (S32 i = 0; i < MaxImageEmitters; i++) { + MountedImage::ImageEmitter& em = image.emitter[i]; + if (bool(em.emitter)) { + if (em.time > 0) { + em.time -= dt; + + // Do we need to update the emitter's node due to the current shape changing? + if (imageShapeIndex != image.lastShapeIndex) + { + em.node = image.state->emitterNode[imageShapeIndex]; + } + + MatrixF mat; + getRenderImageTransform(imageSlot,em.node,&mat); + Point3F pos,axis; + mat.getColumn(3,&pos); + mat.getColumn(1,&axis); + em.emitter->emitParticles(pos,true,axis,getVelocity(),(U32) (dt * 1000)); + } + else { + em.emitter->deleteWhenEmpty(); + em.emitter = 0; + } + } + } + + image.lastShapeIndex = imageShapeIndex; +} + + +//---------------------------------------------------------------------------- + +void ShapeBase::setImageScriptAnimPrefix(U32 imageSlot, NetStringHandle prefix) +{ + MountedImage& image = mMountedImageList[imageSlot]; + if (image.dataBlock) { + setMaskBits(ImageMaskN << imageSlot); + image.scriptAnimPrefix = prefix; + } +} + +NetStringHandle ShapeBase::getImageScriptAnimPrefix(U32 imageSlot) +{ + MountedImage& image = mMountedImageList[imageSlot]; + return image.dataBlock? image.scriptAnimPrefix : NetStringHandle(); +} + + +//---------------------------------------------------------------------------- + +U32 ShapeBase::getImageShapeIndex(const MountedImage& image) const +{ + U32 shapeIndex = ShapeBaseImageData::StandardImageShape; + + const ShapeBaseImageData* data = image.dataBlock; + if (data && data->useFirstPersonShape && isFirstPerson()) + shapeIndex = ShapeBaseImageData::FirstPersonImageShape; + + return shapeIndex; +} + + +//---------------------------------------------------------------------------- + +void ShapeBase::startImageEmitter(MountedImage& image,ShapeBaseImageData::StateData& state) +{ + MountedImage::ImageEmitter* bem = 0; + MountedImage::ImageEmitter* em = image.emitter; + MountedImage::ImageEmitter* ee = &image.emitter[MaxImageEmitters]; + + U32 imageShapeIndex = getImageShapeIndex(image); + + // If we are already emitting the same particles from the same + // node, then simply extend the time. Otherwise, find an empty + // emitter slot, or grab the one with the least amount of time left. + for (; em != ee; em++) { + if (bool(em->emitter)) { + if (state.emitter == em->emitter->getDataBlock() && state.emitterNode[imageShapeIndex] == em->node) { + if (state.emitterTime > em->time) + em->time = state.emitterTime; + return; + } + if (!bem || (bool(bem->emitter) && bem->time > em->time)) + bem = em; + } + else + bem = em; + } + + bem->time = state.emitterTime; + bem->node = state.emitterNode[imageShapeIndex]; + bem->emitter = new ParticleEmitter; + bem->emitter->onNewDataBlock(state.emitter,false); + if( !bem->emitter->registerObject() ) + { + bem->emitter.getPointer()->destroySelf(); + bem->emitter = NULL; + } +} + +void ShapeBase::submitLights( LightManager *lm, bool staticLighting ) +{ + if ( staticLighting ) + return; + + // Submit lights for MountedImage(s) + for ( S32 i = 0; i < MaxMountedImages; i++ ) + { + ShapeBaseImageData *imageData = getMountedImage( i ); + + if ( imageData != NULL && imageData->lightType != ShapeBaseImageData::NoLight ) + { + MountedImage &image = mMountedImageList[i]; + + F32 intensity; + + switch ( imageData->lightType ) + { + case ShapeBaseImageData::ConstantLight: + case ShapeBaseImageData::SpotLight: + intensity = 1.0f; + break; + + case ShapeBaseImageData::PulsingLight: + intensity = 0.5f + 0.5f * mSin( M_PI_F * (F32)Sim::getCurrentTime() / (F32)imageData->lightDuration + image.lightStart ); + intensity = 0.15f + intensity * 0.85f; + break; + + case ShapeBaseImageData::WeaponFireLight: + { + S32 elapsed = Sim::getCurrentTime() - image.lightStart; + if ( elapsed > imageData->lightDuration ) + return; + intensity = ( 1.0 - (F32)elapsed / (F32)imageData->lightDuration ) * imageData->lightBrightness; + break; + } + default: + intensity = 1.0f; + return; + } + + if ( !image.lightInfo ) + image.lightInfo = LightManager::createLightInfo(); + + image.lightInfo->setColor( imageData->lightColor ); + image.lightInfo->setBrightness( intensity ); + image.lightInfo->setRange( imageData->lightRadius ); + + if ( imageData->lightType == ShapeBaseImageData::SpotLight ) + { + image.lightInfo->setType( LightInfo::Spot ); + // Do we want to expose these or not? + image.lightInfo->setInnerConeAngle( 15 ); + image.lightInfo->setOuterConeAngle( 40 ); + } + else + image.lightInfo->setType( LightInfo::Point ); + + MatrixF imageMat; + getRenderImageTransform( i, &imageMat ); + + image.lightInfo->setTransform( imageMat ); + + lm->registerGlobalLight( image.lightInfo, NULL ); + } + } +} + + +//---------------------------------------------------------------------------- + +void ShapeBase::ejectShellCasing( U32 imageSlot ) +{ + MountedImage& image = mMountedImageList[imageSlot]; + ShapeBaseImageData* imageData = image.dataBlock; + + if (!imageData->casing) + return; + + // Shell casings are client-side only, so use the render transform. + MatrixF ejectTrans; + getRenderImageTransform( imageSlot, imageData->ejectNode[getImageShapeIndex(image)], &ejectTrans ); + + Point3F ejectDir = imageData->shellExitDir; + ejectDir.normalize(); + + F32 ejectSpread = mDegToRad( imageData->shellExitVariance ); + MatrixF ejectOrient = MathUtils::createOrientFromDir( ejectDir ); + + Point3F randomDir; + randomDir.x = mSin( gRandGen.randF( -ejectSpread, ejectSpread ) ); + randomDir.y = 1.0; + randomDir.z = mSin( gRandGen.randF( -ejectSpread, ejectSpread ) ); + randomDir.normalizeSafe(); + + ejectOrient.mulV( randomDir ); + + MatrixF imageTrans = getRenderTransform(); + imageTrans.mulV( randomDir ); + + Point3F shellVel = randomDir * imageData->shellVelocity; + Point3F shellPos = ejectTrans.getPosition(); + + + Debris *casing = new Debris; + casing->onNewDataBlock( imageData->casing, false ); + casing->setTransform( imageTrans ); + + if (!casing->registerObject()) + delete casing; + + casing->init( shellPos, shellVel ); +} diff --git a/Engine/source/T3D/spotLight.cpp b/Engine/source/T3D/spotLight.cpp new file mode 100644 index 000000000..65e4a72ac --- /dev/null +++ b/Engine/source/T3D/spotLight.cpp @@ -0,0 +1,212 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/spotLight.h" + +#include "console/consoleTypes.h" +#include "core/stream/bitStream.h" +#include "gfx/gfxDrawUtil.h" + + +IMPLEMENT_CO_NETOBJECT_V1( SpotLight ); + +ConsoleDocClass( SpotLight, + "@brief Lighting object which emits conical light in a direction.\n\n" + + "SpotLight is one of the two types of lighting objects that can be added " + "to a Torque 3D level, the other being PointLight. Unlike directional or " + "point lights, the SpotLights emits lighting in a specific direction " + "within a cone. The distance of the cone is controlled by the SpotLight::range " + "variable.\n\n" + + "@tsexample\n" + "// Declaration of a point light in script, or created by World Editor\n" + "new SpotLight(SampleSpotLight)\n" + "{\n" + " range = \"10\";\n" + " innerAngle = \"40\";\n" + " outerAngle = \"45\";\n" + " isEnabled = \"1\";\n" + " color = \"1 1 1 1\";\n" + " brightness = \"1\";\n" + " castShadows = \"0\";\n" + " priority = \"1\";\n" + " animate = \"1\";\n" + " animationPeriod = \"1\";\n" + " animationPhase = \"1\";\n" + " flareType = \"LightFlareExample0\";\n" + " flareScale = \"1\";\n" + " attenuationRatio = \"0 1 1\";\n" + " shadowType = \"Spot\";\n" + " texSize = \"512\";\n" + " overDarkFactor = \"2000 1000 500 100\";\n" + " shadowDistance = \"400\";\n" + " shadowSoftness = \"0.15\";\n" + " numSplits = \"1\";\n" + " logWeight = \"0.91\";\n" + " fadeStartDistance = \"0\";\n" + " lastSplitTerrainOnly = \"0\";\n" + " representedInLightmap = \"0\";\n" + " shadowDarkenColor = \"0 0 0 -1\";\n" + " includeLightmappedGeometryInShadow = \"0\";\n" + " position = \"-29.4362 -5.86289 5.58602\";\n" + " rotation = \"1 0 0 0\";\n" + "};\n" + "@endtsexample\n\n" + + "@see LightBase\n\n" + "@see PointLight\n\n" + "@ingroup Lighting\n" +); + +SpotLight::SpotLight() + : mRange( 10.0f ), + mInnerConeAngle( 40.0f ), + mOuterConeAngle( 45.0f ) +{ + // We set the type here to ensure the extended + // parameter validation works when setting fields. + mLight->setType( LightInfo::Spot ); +} + +SpotLight::~SpotLight() +{ +} + +void SpotLight::initPersistFields() +{ + addGroup( "Light" ); + + addField( "range", TypeF32, Offset( mRange, SpotLight ) ); + addField( "innerAngle", TypeF32, Offset( mInnerConeAngle, SpotLight ) ); + addField( "outerAngle", TypeF32, Offset( mOuterConeAngle, SpotLight ) ); + + endGroup( "Light" ); + + // We do the parent fields at the end so that + // they show up that way in the inspector. + Parent::initPersistFields(); + + // Remove the scale field... it's already + // defined by the range and angle. + removeField( "scale" ); +} + +void SpotLight::_conformLights() +{ + mLight->setTransform( getTransform() ); + + mRange = getMax( mRange, 0.05f ); + mLight->setRange( mRange ); + + mLight->setColor( mColor ); + mLight->setBrightness( mBrightness ); + mLight->setCastShadows( mCastShadows ); + mLight->setPriority( mPriority ); + + mOuterConeAngle = getMax( 0.01f, mOuterConeAngle ); + mInnerConeAngle = getMin( mInnerConeAngle, mOuterConeAngle ); + + mLight->setInnerConeAngle( mInnerConeAngle ); + mLight->setOuterConeAngle( mOuterConeAngle ); + + // Update the bounds and scale to fit our spotlight. + F32 radius = mRange * mSin( mDegToRad( mOuterConeAngle ) * 0.5f ); + mObjBox.minExtents.set( -1, 0, -1 ); + mObjBox.maxExtents.set( 1, 1, 1 ); + mObjScale.set( radius, mRange, radius ); + + // Skip our transform... it just dirties mask bits. + Parent::setTransform( mObjToWorld ); +} + +U32 SpotLight::packUpdate(NetConnection *conn, U32 mask, BitStream *stream ) +{ + if ( stream->writeFlag( mask & UpdateMask ) ) + { + stream->write( mRange ); + stream->write( mInnerConeAngle ); + stream->write( mOuterConeAngle ); + } + + return Parent::packUpdate( conn, mask, stream ); +} + +void SpotLight::unpackUpdate( NetConnection *conn, BitStream *stream ) +{ + if ( stream->readFlag() ) // UpdateMask + { + stream->read( &mRange ); + stream->read( &mInnerConeAngle ); + stream->read( &mOuterConeAngle ); + } + + Parent::unpackUpdate( conn, stream ); +} + +void SpotLight::setScale( const VectorF &scale ) +{ + // The y coord is the spotlight range. + mRange = getMax( scale.y, 0.05f ); + + // Use the average of the x and z to get a radius. This + // is the best method i've found to make the manipulation + // from the WorldEditor gizmo to feel right. + F32 radius = mClampF( ( scale.x + scale.z ) * 0.5f, 0.05f, mRange ); + mOuterConeAngle = mRadToDeg( mAsin( radius / mRange ) ) * 2.0f; + + // Make sure the inner angle is less than the outer. + // + // TODO: Maybe we should make the inner angle a scale + // and not an absolute angle? + // + mInnerConeAngle = getMin( mInnerConeAngle, mOuterConeAngle ); + + // We changed a bunch of our settings + // so notify the client. + setMaskBits( UpdateMask ); + + // Let the parent do the final scale. + Parent::setScale( VectorF( radius, mRange, radius ) ); +} + +void SpotLight::_renderViz( SceneRenderState *state ) +{ + GFXDrawUtil *draw = GFX->getDrawUtil(); + + GFXStateBlockDesc desc; + desc.setZReadWrite( true, false ); + desc.setCullMode( GFXCullNone ); + desc.setBlend( true ); + + // Base the color on the light color. + ColorI color( mColor ); + color.alpha = 16; + + F32 radius = mRange * mSin( mDegToRad( mOuterConeAngle * 0.5f ) ); + draw->drawCone( desc, + getPosition() + ( getTransform().getForwardVector() * mRange ), + getPosition(), + radius, + color ); +} diff --git a/Engine/source/T3D/spotLight.h b/Engine/source/T3D/spotLight.h new file mode 100644 index 000000000..2552f2321 --- /dev/null +++ b/Engine/source/T3D/spotLight.h @@ -0,0 +1,64 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SPOTLIGHT_H_ +#define _SPOTLIGHT_H_ + +#ifndef _LIGHTBASE_H_ +#include "T3D/lightBase.h" +#endif + + +class SpotLight : public LightBase +{ + typedef LightBase Parent; + +protected: + + F32 mRange; + + F32 mInnerConeAngle; + + F32 mOuterConeAngle; + + // LightBase + void _conformLights(); + void _renderViz( SceneRenderState *state ); + +public: + + SpotLight(); + virtual ~SpotLight(); + + // ConsoleObject + DECLARE_CONOBJECT( SpotLight ); + static void initPersistFields(); + + // SceneObject + virtual void setScale( const VectorF &scale ); + + // NetObject + U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream ); + void unpackUpdate( NetConnection *conn, BitStream *stream ); +}; + +#endif // _SPOTLIGHT_H_ diff --git a/Engine/source/T3D/staticShape.cpp b/Engine/source/T3D/staticShape.cpp new file mode 100644 index 000000000..b40ee55bd --- /dev/null +++ b/Engine/source/T3D/staticShape.cpp @@ -0,0 +1,333 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/dnet.h" +#include "core/stream/bitStream.h" +#include "app/game.h" +#include "math/mMath.h" +#include "console/simBase.h" +#include "console/console.h" +#include "console/consoleTypes.h" +#include "T3D/gameBase/moveManager.h" +#include "ts/tsShapeInstance.h" +#include "T3D/staticShape.h" +#include "math/mathIO.h" +#include "sim/netConnection.h" +#include "scene/sceneObjectLightingPlugin.h" + +extern void wireCube(F32 size,Point3F pos); + +static const U32 sgAllowedDynamicTypes = 0xffffff; + +//---------------------------------------------------------------------------- + +IMPLEMENT_CO_DATABLOCK_V1(StaticShapeData); + +ConsoleDocClass( StaticShapeData, + "@brief The most basic ShapeBaseData derrived shape datablock available in Torque 3D.\n\n" + + "When it comes to placing 3D objects in the scene, you effectively have two options:\n\n" + "1. TSStatic objects\n\n" + "2. ShapeBase derived objects\n\n" + + "Since ShapeBase and ShapeBaseData are not meant to be instantiated in script, you " + "will use one of its child classes instead. Several game related objects are derived " + "from ShapeBase: Player, Vehicle, Item, and so on.\n\n" + + "When you need a 3D object with datablock capabilities, you will use an object derived " + "from ShapeBase. When you need an object with extremely low overhead, and with no other " + "purpose than to be a 3D object in the scene, you will use TSStatic.\n\n" + + "The most basic child of ShapeBase is StaticShape. It does not introduce any of the " + "additional functionality you see in Player, Item, Vehicle or the other game play " + "heavy classes. At its core, it is comparable to a TSStatic, but with a datbalock. Having " + "a datablock provides a location for common variables as well as having access to " + "various ShapeBaseData, GameBaseData and SimDataBlock callbacks.\n\n" + + "@tsexample\n" + "// Create a StaticShape using a datablock\n" + "datablock StaticShapeData(BasicShapeData)\n" + "{\n" + " shapeFile = \"art/shapes/items/kit/healthkit.dts\";\n" + " testVar = \"Simple string, not a stock variable\";\n" + "};\n\n" + "new StaticShape()\n" + "{\n" + " dataBlock = \"BasicShapeData\";\n" + " position = \"0.0 0.0 0.0\";\n" + " rotation = \"1 0 0 0\";\n" + " scale = \"1 1 1\";\n" + "};\n" + "@endtsexample\n\n" + + "@see StaticShape\n" + "@see ShapeBaseData\n" + "@see TSStatic\n\n" + + "@ingroup gameObjects\n" + "@ingroup Datablocks"); + +StaticShapeData::StaticShapeData() +{ + dynamicTypeField = 0; + + shadowEnable = true; + + noIndividualDamage = false; +} + +void StaticShapeData::initPersistFields() +{ + addField("noIndividualDamage", TypeBool, Offset(noIndividualDamage, StaticShapeData), "Deprecated\n\n @internal"); + addField("dynamicType", TypeS32, Offset(dynamicTypeField, StaticShapeData), + "@brief An integer value which, if speficied, is added to the value retured by getType().\n\n" + "This allows you to extend the type mask for a StaticShape that uses this datablock. Type masks " + "are used for container queries, etc."); + + Parent::initPersistFields(); +} + +void StaticShapeData::packData(BitStream* stream) +{ + Parent::packData(stream); + stream->writeFlag(noIndividualDamage); + stream->write(dynamicTypeField); +} + +void StaticShapeData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + noIndividualDamage = stream->readFlag(); + stream->read(&dynamicTypeField); +} + + +//---------------------------------------------------------------------------- + +IMPLEMENT_CO_NETOBJECT_V1(StaticShape); + +ConsoleDocClass( StaticShape, + "@brief The most basic 3D shape with a datablock available in Torque 3D.\n\n" + + "When it comes to placing 3D objects in the scene, you technically have two options:\n\n" + "1. TSStatic objects\n\n" + "2. ShapeBase derived objects\n\n" + + "Since ShapeBase and ShapeBaseData are not meant to be instantiated in script, you " + "will use one of its child classes instead. Several game related objects are derived " + "from ShapeBase: Player, Vehicle, Item, and so on.\n\n" + + "When you need a 3D object with datablock capabilities, you will use an object derived " + "from ShapeBase. When you need an object with extremely low overhead, and with no other " + "purpose than to be a 3D object in the scene, you will use TSStatic.\n\n" + + "The most basic child of ShapeBase is StaticShape. It does not introduce any of the " + "additional functionality you see in Player, Item, Vehicle or the other game play " + "heavy classes. At its core, it is comparable to a TSStatic, but with a datbalock. Having " + "a datablock provides a location for common variables as well as having access to " + "various ShapeBaseData, GameBaseData and SimDataBlock callbacks.\n\n" + + "@tsexample\n" + "// Create a StaticShape using a datablock\n" + "datablock StaticShapeData(BasicShapeData)\n" + "{\n" + " shapeFile = \"art/shapes/items/kit/healthkit.dts\";\n" + " testVar = \"Simple string, not a stock variable\";\n" + "};\n\n" + "new StaticShape()\n" + "{\n" + " dataBlock = \"BasicShapeData\";\n" + " position = \"0.0 0.0 0.0\";\n" + " rotation = \"1 0 0 0\";\n" + " scale = \"1 1 1\";\n" + "};\n" + "@endtsexample\n\n" + + "@see StaticShapeData\n" + "@see ShapeBase\n" + "@see TSStatic\n\n" + + "@ingroup gameObjects\n"); + +StaticShape::StaticShape() +{ + mTypeMask |= StaticShapeObjectType | StaticObjectType; + mDataBlock = 0; +} + +StaticShape::~StaticShape() +{ +} + + +//---------------------------------------------------------------------------- + +void StaticShape::initPersistFields() +{ + Parent::initPersistFields(); +} + +bool StaticShape::onAdd() +{ + if(!Parent::onAdd() || !mDataBlock) + return false; + + // We need to modify our type mask based on what our datablock says... + mTypeMask |= (mDataBlock->dynamicTypeField & sgAllowedDynamicTypes); + + addToScene(); + + if (isServerObject()) + scriptOnAdd(); + return true; +} + +bool StaticShape::onNewDataBlock(GameBaseData* dptr, bool reload) +{ + mDataBlock = dynamic_cast(dptr); + if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload)) + return false; + + scriptOnNewDataBlock(); + return true; +} + +void StaticShape::onRemove() +{ + scriptOnRemove(); + removeFromScene(); + Parent::onRemove(); +} + + +//---------------------------------------------------------------------------- + +void StaticShape::processTick(const Move* move) +{ + Parent::processTick(move); + + // Image Triggers + if (move && mDamageState == Enabled) { + setImageTriggerState(0,move->trigger[0]); + setImageTriggerState(1,move->trigger[1]); + } + + if (isMounted()) { + MatrixF mat; + mMount.object->getMountTransform( mMount.node, mMount.xfm, &mat ); + Parent::setTransform(mat); + Parent::setRenderTransform(mat); + } +} + +void StaticShape::interpolateTick(F32 delta) +{ + if (isMounted()) { + MatrixF mat; + mMount.object->getRenderMountTransform( delta, mMount.node, mMount.xfm, &mat ); + Parent::setRenderTransform(mat); + } +} + +void StaticShape::setTransform(const MatrixF& mat) +{ + Parent::setTransform(mat); + setMaskBits(PositionMask); +} + +void StaticShape::onUnmount(ShapeBase*,S32) +{ + // Make sure the client get's the final server pos. + setMaskBits(PositionMask); +} + + +//---------------------------------------------------------------------------- + +U32 StaticShape::packUpdate(NetConnection *connection, U32 mask, BitStream *bstream) +{ + U32 retMask = Parent::packUpdate(connection,mask,bstream); + if (bstream->writeFlag(mask & PositionMask | ExtendedInfoMask)) + { + + // Write the transform (do _not_ use writeAffineTransform. Since this is a static + // object, the transform must be RIGHT THE *&)*$&^ ON or it will goof up the + // synchronization between the client and the server. + mathWrite(*bstream,mObjToWorld); + mathWrite(*bstream, mObjScale); + } + + // powered? + bstream->writeFlag(mPowered); + + if (mLightPlugin) + { + retMask |= mLightPlugin->packUpdate(this, ExtendedInfoMask, connection, mask, bstream); + } + + return retMask; +} + +void StaticShape::unpackUpdate(NetConnection *connection, BitStream *bstream) +{ + Parent::unpackUpdate(connection,bstream); + if (bstream->readFlag()) + { + MatrixF mat; + mathRead(*bstream,&mat); + Parent::setTransform(mat); + Parent::setRenderTransform(mat); + + VectorF scale; + mathRead(*bstream, &scale); + setScale(scale); + } + + // powered? + mPowered = bstream->readFlag(); + + if (mLightPlugin) + { + mLightPlugin->unpackUpdate(this, connection, bstream); + } +} + + +//---------------------------------------------------------------------------- +// This appears to be legacy T2 stuff +// Marked internal, as this is flagged to be deleted +// [8/1/2010 mperry] +ConsoleMethod( StaticShape, setPoweredState, void, 3, 3, "(bool isPowered)" + "@internal") +{ + if(!object->isServerObject()) + return; + object->setPowered(dAtob(argv[2])); +} + +ConsoleMethod( StaticShape, getPoweredState, bool, 2, 2, "@internal") +{ + if(!object->isServerObject()) + return(false); + return(object->isPowered()); +} diff --git a/Engine/source/T3D/staticShape.h b/Engine/source/T3D/staticShape.h new file mode 100644 index 000000000..6dd9d0432 --- /dev/null +++ b/Engine/source/T3D/staticShape.h @@ -0,0 +1,93 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _STATICSHAPE_H_ +#define _STATICSHAPE_H_ + +#ifndef _SHAPEBASE_H_ +#include "T3D/shapeBase.h" +#endif + +//---------------------------------------------------------------------------- + +struct StaticShapeData: public ShapeBaseData { + typedef ShapeBaseData Parent; + + public: + StaticShapeData(); + + bool noIndividualDamage; + S32 dynamicTypeField; + bool isShielded; + F32 energyPerDamagePoint; + + // + DECLARE_CONOBJECT(StaticShapeData); + static void initPersistFields(); + virtual void packData(BitStream* stream); + virtual void unpackData(BitStream* stream); +}; + + +//---------------------------------------------------------------------------- + +class StaticShape: public ShapeBase +{ + typedef ShapeBase Parent; + + StaticShapeData* mDataBlock; + bool mPowered; + + void onUnmount(ShapeBase* obj,S32 node); + +protected: + enum MaskBits { + PositionMask = Parent::NextFreeMask, + NextFreeMask = Parent::NextFreeMask << 1 + }; + +public: + DECLARE_CONOBJECT(StaticShape); + + StaticShape(); + ~StaticShape(); + + bool onAdd(); + void onRemove(); + bool onNewDataBlock(GameBaseData *dptr, bool reload); + + void processTick(const Move *move); + void interpolateTick(F32 delta); + void setTransform(const MatrixF &mat); + + U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream); + void unpackUpdate(NetConnection *conn, BitStream *stream); + + // power + void setPowered(bool power) {mPowered = power;} + bool isPowered() {return(mPowered);} + + static void initPersistFields(); +}; + + +#endif diff --git a/Engine/source/T3D/trigger.cpp b/Engine/source/T3D/trigger.cpp new file mode 100644 index 000000000..b47bcabbc --- /dev/null +++ b/Engine/source/T3D/trigger.cpp @@ -0,0 +1,841 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/trigger.h" + +#include "scene/sceneRenderState.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "collision/boxConvex.h" + +#include "core/stream/bitStream.h" +#include "math/mathIO.h" +#include "gfx/gfxTransformSaver.h" +#include "renderInstance/renderPassManager.h" +#include "gfx/gfxDrawUtil.h" +#include "T3D/physics/physicsPlugin.h" +#include "T3D/physics/physicsBody.h" +#include "T3D/physics/physicsCollision.h" + + +bool Trigger::smRenderTriggers = false; + +//----------------------------------------------------------------------------- + +//---------------------------------------------------------------------------- + +IMPLEMENT_CO_DATABLOCK_V1(TriggerData); + +ConsoleDocClass( TriggerData, + "@brief Defines shared properties for Trigger objects.\n\n" + + "The primary focus of the TriggerData datablock is the callbacks it provides when an object is " + "within or leaves the Trigger bounds.\n" + + "@see Trigger.\n" + "@ingroup gameObjects\n" + "@ingroup Datablocks\n" +); + +IMPLEMENT_CALLBACK( TriggerData, onEnterTrigger, void, ( Trigger* trigger, GameBase* obj ), ( trigger, obj ), + "@brief Called when an object enters the volume of the Trigger instance using this TriggerData.\n\n" + + "@param trigger the Trigger instance whose volume the object entered\n" + "@param obj the object that entered the volume of the Trigger instance\n" ); + +IMPLEMENT_CALLBACK( TriggerData, onTickTrigger, void, ( Trigger* trigger ), ( trigger ), + "@brief Called every tickPeriodMS number of milliseconds (as specified in the TriggerData) whenever " + "one or more objects are inside the volume of the trigger.\n\n" + + "The Trigger has methods to retrieve the objects that are within the Trigger's bounds if you " + "want to do something with them in this callback.\n" + + "@param trigger the Trigger instance whose volume the object is inside\n" + + "@see tickPeriodMS\n" + "@see Trigger::getNumObjects()\n" + "@see Trigger::getObject()\n"); + +IMPLEMENT_CALLBACK( TriggerData, onLeaveTrigger, void, ( Trigger* trigger, GameBase* obj ), ( trigger, obj ), + "@brief Called when an object leaves the volume of the Trigger instance using this TriggerData.\n\n" + + "@param trigger the Trigger instance whose volume the object left\n" + "@param obj the object that left the volume of the Trigger instance\n" ); + +TriggerData::TriggerData() +{ + tickPeriodMS = 100; + isClientSide = false; +} + +bool TriggerData::onAdd() +{ + if (!Parent::onAdd()) + return false; + + return true; +} + +void TriggerData::initPersistFields() +{ + addGroup("Callbacks"); + + addField( "tickPeriodMS", TypeS32, Offset( tickPeriodMS, TriggerData ), + "@brief Time in milliseconds between calls to onTickTrigger() while at least one object is within a Trigger's bounds.\n\n" + "@see onTickTrigger()\n"); + addField( "clientSide", TypeBool, Offset( isClientSide, TriggerData ), + "Forces Trigger callbacks to only be called on clients."); + + endGroup("Callbacks"); + + Parent::initPersistFields(); +} + + +//-------------------------------------------------------------------------- +void TriggerData::packData(BitStream* stream) +{ + Parent::packData(stream); + stream->write(tickPeriodMS); + stream->write(isClientSide); +} + +void TriggerData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + stream->read(&tickPeriodMS); + stream->read(&isClientSide); +} + + +//-------------------------------------------------------------------------- + +IMPLEMENT_CO_NETOBJECT_V1(Trigger); + +ConsoleDocClass( Trigger, + "@brief A Trigger is a volume of space that initiates script callbacks " + "when objects pass through the Trigger.\n\n" + + "TriggerData provides the callbacks for the Trigger when an object enters, stays inside " + "or leaves the Trigger's volume.\n\n" + + "@see TriggerData\n" + "@ingroup gameObjects\n" +); + +IMPLEMENT_CALLBACK( Trigger, onAdd, void, ( U32 objectId ), ( objectId ), + "@brief Called when the Trigger is being created.\n\n" + "@param objectId the object id of the Trigger being created\n" ); + +IMPLEMENT_CALLBACK( Trigger, onRemove, void, ( U32 objectId ), ( objectId ), + "@brief Called just before the Trigger is deleted.\n\n" + "@param objectId the object id of the Trigger being deleted\n" ); + +Trigger::Trigger() +{ + // Don't ghost by default. + mNetFlags.set(Ghostable | ScopeAlways); + + mTypeMask |= TriggerObjectType; + + mObjScale.set(1, 1, 1); + mObjToWorld.identity(); + mWorldToObj.identity(); + + mDataBlock = NULL; + + mLastThink = 0; + mCurrTick = 0; + + mConvexList = new Convex; + + mPhysicsRep = NULL; +} + +Trigger::~Trigger() +{ + delete mConvexList; + mConvexList = NULL; + SAFE_DELETE( mPhysicsRep ); +} + +bool Trigger::castRay(const Point3F &start, const Point3F &end, RayInfo* info) +{ + // Collide against bounding box + F32 st,et,fst = 0,fet = 1; + F32 *bmin = &mObjBox.minExtents.x; + F32 *bmax = &mObjBox.maxExtents.x; + F32 const *si = &start.x; + F32 const *ei = &end.x; + + for (int i = 0; i < 3; i++) + { + if (*si < *ei) + { + if (*si > *bmax || *ei < *bmin) + return false; + F32 di = *ei - *si; + st = (*si < *bmin)? (*bmin - *si) / di: 0; + et = (*ei > *bmax)? (*bmax - *si) / di: 1; + } + else + { + if (*ei > *bmax || *si < *bmin) + return false; + F32 di = *ei - *si; + st = (*si > *bmax)? (*bmax - *si) / di: 0; + et = (*ei < *bmin)? (*bmin - *si) / di: 1; + } + if (st > fst) fst = st; + if (et < fet) fet = et; + if (fet < fst) + return false; + bmin++; bmax++; + si++; ei++; + } + + info->normal = start - end; + info->normal.normalizeSafe(); + getTransform().mulV( info->normal ); + + info->t = fst; + info->object = this; + info->point.interpolate(start,end,fst); + info->material = 0; + return true; +} + + +//-------------------------------------------------------------------------- +/* Console polyhedron data type exporter + The polyhedron type is really a quadrilateral and consists of a corner + point follow by three vectors representing the edges extending from the + corner. +*/ +DECLARE_STRUCT( Polyhedron ); +IMPLEMENT_STRUCT( Polyhedron, Polyhedron,, + "" ) +END_IMPLEMENT_STRUCT; +ConsoleType( floatList, TypeTriggerPolyhedron, Polyhedron ) + + +ConsoleGetType( TypeTriggerPolyhedron ) +{ + U32 i; + Polyhedron* pPoly = reinterpret_cast(dptr); + + // First point is corner, need to find the three vectors...` + Point3F origin = pPoly->pointList[0]; + U32 currVec = 0; + Point3F vecs[3]; + for (i = 0; i < pPoly->edgeList.size(); i++) { + const U32 *vertex = pPoly->edgeList[i].vertex; + if (vertex[0] == 0) + vecs[currVec++] = pPoly->pointList[vertex[1]] - origin; + else + if (vertex[1] == 0) + vecs[currVec++] = pPoly->pointList[vertex[0]] - origin; + } + AssertFatal(currVec == 3, "Internal error: Bad trigger polyhedron"); + + // Build output string. + char* retBuf = Con::getReturnBuffer(1024); + dSprintf(retBuf, 1023, "%7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f", + origin.x, origin.y, origin.z, + vecs[0].x, vecs[0].y, vecs[0].z, + vecs[2].x, vecs[2].y, vecs[2].z, + vecs[1].x, vecs[1].y, vecs[1].z); + + return retBuf; +} + +/* Console polyhedron data type loader + The polyhedron type is really a quadrilateral and consists of an corner + point follow by three vectors representing the edges extending from the + corner. +*/ +ConsoleSetType( TypeTriggerPolyhedron ) +{ + if (argc != 1) { + Con::printf("(TypeTriggerPolyhedron) multiple args not supported for polyhedra"); + return; + } + + Point3F origin; + Point3F vecs[3]; + + U32 numArgs = dSscanf(argv[0], "%g %g %g %g %g %g %g %g %g %g %g %g", + &origin.x, &origin.y, &origin.z, + &vecs[0].x, &vecs[0].y, &vecs[0].z, + &vecs[1].x, &vecs[1].y, &vecs[1].z, + &vecs[2].x, &vecs[2].y, &vecs[2].z); + if (numArgs != 12) { + Con::printf("Bad polyhedron!"); + return; + } + + Polyhedron* pPoly = reinterpret_cast(dptr); + + // This setup goes against conventions for Polyhedrons in that it a) sets up + // edges with CCW instead of CW order for face[0] and that it b) lets plane + // normals face outwards rather than inwards. + + pPoly->pointList.setSize(8); + pPoly->pointList[0] = origin; + pPoly->pointList[1] = origin + vecs[0]; + pPoly->pointList[2] = origin + vecs[1]; + pPoly->pointList[3] = origin + vecs[2]; + pPoly->pointList[4] = origin + vecs[0] + vecs[1]; + pPoly->pointList[5] = origin + vecs[0] + vecs[2]; + pPoly->pointList[6] = origin + vecs[1] + vecs[2]; + pPoly->pointList[7] = origin + vecs[0] + vecs[1] + vecs[2]; + + Point3F normal; + pPoly->planeList.setSize(6); + + mCross(vecs[2], vecs[0], &normal); + pPoly->planeList[0].set(origin, normal); + mCross(vecs[0], vecs[1], &normal); + pPoly->planeList[1].set(origin, normal); + mCross(vecs[1], vecs[2], &normal); + pPoly->planeList[2].set(origin, normal); + mCross(vecs[1], vecs[0], &normal); + pPoly->planeList[3].set(pPoly->pointList[7], normal); + mCross(vecs[2], vecs[1], &normal); + pPoly->planeList[4].set(pPoly->pointList[7], normal); + mCross(vecs[0], vecs[2], &normal); + pPoly->planeList[5].set(pPoly->pointList[7], normal); + + pPoly->edgeList.setSize(12); + pPoly->edgeList[0].vertex[0] = 0; pPoly->edgeList[0].vertex[1] = 1; pPoly->edgeList[0].face[0] = 0; pPoly->edgeList[0].face[1] = 1; + pPoly->edgeList[1].vertex[0] = 1; pPoly->edgeList[1].vertex[1] = 5; pPoly->edgeList[1].face[0] = 0; pPoly->edgeList[1].face[1] = 4; + pPoly->edgeList[2].vertex[0] = 5; pPoly->edgeList[2].vertex[1] = 3; pPoly->edgeList[2].face[0] = 0; pPoly->edgeList[2].face[1] = 3; + pPoly->edgeList[3].vertex[0] = 3; pPoly->edgeList[3].vertex[1] = 0; pPoly->edgeList[3].face[0] = 0; pPoly->edgeList[3].face[1] = 2; + pPoly->edgeList[4].vertex[0] = 3; pPoly->edgeList[4].vertex[1] = 6; pPoly->edgeList[4].face[0] = 3; pPoly->edgeList[4].face[1] = 2; + pPoly->edgeList[5].vertex[0] = 6; pPoly->edgeList[5].vertex[1] = 2; pPoly->edgeList[5].face[0] = 2; pPoly->edgeList[5].face[1] = 5; + pPoly->edgeList[6].vertex[0] = 2; pPoly->edgeList[6].vertex[1] = 0; pPoly->edgeList[6].face[0] = 2; pPoly->edgeList[6].face[1] = 1; + pPoly->edgeList[7].vertex[0] = 1; pPoly->edgeList[7].vertex[1] = 4; pPoly->edgeList[7].face[0] = 4; pPoly->edgeList[7].face[1] = 1; + pPoly->edgeList[8].vertex[0] = 4; pPoly->edgeList[8].vertex[1] = 2; pPoly->edgeList[8].face[0] = 1; pPoly->edgeList[8].face[1] = 5; + pPoly->edgeList[9].vertex[0] = 4; pPoly->edgeList[9].vertex[1] = 7; pPoly->edgeList[9].face[0] = 4; pPoly->edgeList[9].face[1] = 5; + pPoly->edgeList[10].vertex[0] = 5; pPoly->edgeList[10].vertex[1] = 7; pPoly->edgeList[10].face[0] = 3; pPoly->edgeList[10].face[1] = 4; + pPoly->edgeList[11].vertex[0] = 7; pPoly->edgeList[11].vertex[1] = 6; pPoly->edgeList[11].face[0] = 3; pPoly->edgeList[11].face[1] = 5; +} + + +//----------------------------------------------------------------------------- +void Trigger::consoleInit() +{ + Con::addVariable( "$Trigger::renderTriggers", TypeBool, &smRenderTriggers, + "@brief Forces all Trigger's to render.\n\n" + "Used by the Tools and debug render modes.\n" + "@ingroup gameObjects" ); +} + +void Trigger::initPersistFields() +{ + addField("polyhedron", TypeTriggerPolyhedron, Offset(mTriggerPolyhedron, Trigger), + "@brief Defines a non-rectangular area for the trigger.\n\n" + "Rather than the standard rectangular bounds, this optional parameter defines a quadrilateral " + "trigger area. The quadrilateral is defined as a corner point followed by three vectors " + "representing the edges extending from the corner.\n"); + + addProtectedField("enterCommand", TypeCommand, Offset(mEnterCommand, Trigger), &setEnterCmd, &defaultProtectedGetFn, + "The command to execute when an object enters this trigger. Object id stored in %%obj. Maximum 1023 characters." ); + addProtectedField("leaveCommand", TypeCommand, Offset(mLeaveCommand, Trigger), &setLeaveCmd, &defaultProtectedGetFn, + "The command to execute when an object leaves this trigger. Object id stored in %%obj. Maximum 1023 characters." ); + addProtectedField("tickCommand", TypeCommand, Offset(mTickCommand, Trigger), &setTickCmd, &defaultProtectedGetFn, + "The command to execute while an object is inside this trigger. Maximum 1023 characters." ); + + Parent::initPersistFields(); +} + +bool Trigger::setEnterCmd( void *object, const char *index, const char *data ) +{ + static_cast(object)->setMaskBits(EnterCmdMask); + return true; // to update the actual field +} + +bool Trigger::setLeaveCmd(void *object, const char *index, const char *data) +{ + static_cast(object)->setMaskBits(LeaveCmdMask); + return true; // to update the actual field +} + +bool Trigger::setTickCmd(void *object, const char *index, const char *data) +{ + static_cast(object)->setMaskBits(TickCmdMask); + return true; // to update the actual field +} + +//-------------------------------------------------------------------------- + +bool Trigger::onAdd() +{ + if(!Parent::onAdd()) + return false; + + onAdd_callback( getId() ); + + Polyhedron temp = mTriggerPolyhedron; + setTriggerPolyhedron(temp); + + addToScene(); + + if (isServerObject()) + scriptOnAdd(); + + return true; +} + +void Trigger::onRemove() +{ + onRemove_callback( getId() ); + + mConvexList->nukeList(); + + removeFromScene(); + Parent::onRemove(); +} + +bool Trigger::onNewDataBlock( GameBaseData *dptr, bool reload ) +{ + mDataBlock = dynamic_cast( dptr ); + if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) ) + return false; + + scriptOnNewDataBlock(); + return true; +} + +void Trigger::onDeleteNotify( SimObject *obj ) +{ + GameBase* pScene = dynamic_cast( obj ); + + if ( pScene != NULL && mDataBlock != NULL ) + { + for ( U32 i = 0; i < mObjects.size(); i++ ) + { + if ( pScene == mObjects[i] ) + { + mObjects.erase(i); + if (mDataBlock) + mDataBlock->onLeaveTrigger_callback( this, pScene ); + break; + } + } + } + + Parent::onDeleteNotify( obj ); +} + +void Trigger::inspectPostApply() +{ + setTriggerPolyhedron(mTriggerPolyhedron); + setMaskBits(PolyMask); + Parent::inspectPostApply(); +} + +//-------------------------------------------------------------------------- + +void Trigger::buildConvex(const Box3F& box, Convex* convex) +{ + // These should really come out of a pool + mConvexList->collectGarbage(); + + Box3F realBox = box; + mWorldToObj.mul(realBox); + realBox.minExtents.convolveInverse(mObjScale); + realBox.maxExtents.convolveInverse(mObjScale); + + if (realBox.isOverlapped(getObjBox()) == false) + return; + + // Just return a box convex for the entire shape... + Convex* cc = 0; + CollisionWorkingList& wl = convex->getWorkingList(); + for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) { + if (itr->mConvex->getType() == BoxConvexType && + itr->mConvex->getObject() == this) { + cc = itr->mConvex; + break; + } + } + if (cc) + return; + + // Create a new convex. + BoxConvex* cp = new BoxConvex; + mConvexList->registerObject(cp); + convex->addToWorkingList(cp); + cp->init(this); + + mObjBox.getCenter(&cp->mCenter); + cp->mSize.x = mObjBox.len_x() / 2.0f; + cp->mSize.y = mObjBox.len_y() / 2.0f; + cp->mSize.z = mObjBox.len_z() / 2.0f; +} + + +//------------------------------------------------------------------------------ + +void Trigger::setTransform(const MatrixF & mat) +{ + Parent::setTransform(mat); + + if ( mPhysicsRep ) + mPhysicsRep->setTransform( mat ); + + if (isServerObject()) { + MatrixF base(true); + base.scale(Point3F(1.0/mObjScale.x, + 1.0/mObjScale.y, + 1.0/mObjScale.z)); + base.mul(mWorldToObj); + mClippedList.setBaseTransform(base); + + setMaskBits(TransformMask | ScaleMask); + } +} + +void Trigger::prepRenderImage( SceneRenderState *state ) +{ + // only render if selected or render flag is set + if ( !smRenderTriggers && !isSelected() ) + return; + + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &Trigger::renderObject ); + ri->type = RenderPassManager::RIT_Editor; + ri->translucentSort = true; + ri->defaultKey = 1; + state->getRenderPass()->addInst( ri ); +} + +void Trigger::renderObject( ObjectRenderInst *ri, + SceneRenderState *state, + BaseMatInstance *overrideMat ) +{ + if(overrideMat) + return; + + GFXStateBlockDesc desc; + desc.setZReadWrite( true, false ); + desc.setBlend( true ); + + // Trigger polyhedrons are set up with outward facing normals and CCW ordering + // so can't enable backface culling. + desc.setCullMode( GFXCullNone ); + + GFXTransformSaver saver; + + MatrixF mat = getRenderTransform(); + mat.scale( getScale() ); + + GFX->multWorld( mat ); + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + + drawer->drawPolyhedron( desc, mTriggerPolyhedron, ColorI( 255, 192, 0, 45 ) ); + + // Render wireframe. + + desc.setFillModeWireframe(); + drawer->drawPolyhedron( desc, mTriggerPolyhedron, ColorI::BLACK ); +} + +void Trigger::setTriggerPolyhedron(const Polyhedron& rPolyhedron) +{ + mTriggerPolyhedron = rPolyhedron; + + if (mTriggerPolyhedron.pointList.size() != 0) { + mObjBox.minExtents.set(1e10, 1e10, 1e10); + mObjBox.maxExtents.set(-1e10, -1e10, -1e10); + for (U32 i = 0; i < mTriggerPolyhedron.pointList.size(); i++) { + mObjBox.minExtents.setMin(mTriggerPolyhedron.pointList[i]); + mObjBox.maxExtents.setMax(mTriggerPolyhedron.pointList[i]); + } + } else { + mObjBox.minExtents.set(-0.5, -0.5, -0.5); + mObjBox.maxExtents.set( 0.5, 0.5, 0.5); + } + + MatrixF xform = getTransform(); + setTransform(xform); + + mClippedList.clear(); + mClippedList.mPlaneList = mTriggerPolyhedron.planeList; +// for (U32 i = 0; i < mClippedList.mPlaneList.size(); i++) +// mClippedList.mPlaneList[i].neg(); + + MatrixF base(true); + base.scale(Point3F(1.0/mObjScale.x, + 1.0/mObjScale.y, + 1.0/mObjScale.z)); + base.mul(mWorldToObj); + + mClippedList.setBaseTransform(base); + + SAFE_DELETE( mPhysicsRep ); + + if ( PHYSICSMGR ) + { + PhysicsCollision *colShape = PHYSICSMGR->createCollision(); + + MatrixF colMat( true ); + colMat.displace( Point3F( 0, 0, mObjBox.getExtents().z * 0.5f * mObjScale.z ) ); + + colShape->addBox( mObjBox.getExtents() * 0.5f * mObjScale, colMat ); + //MatrixF colMat( true ); + //colMat.scale( mObjScale ); + //colShape->addConvex( mTriggerPolyhedron.pointList.address(), mTriggerPolyhedron.pointList.size(), colMat ); + + PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" ); + mPhysicsRep = PHYSICSMGR->createBody(); + mPhysicsRep->init( colShape, 0, PhysicsBody::BF_TRIGGER | PhysicsBody::BF_KINEMATIC, this, world ); + mPhysicsRep->setTransform( getTransform() ); + } +} + + +//-------------------------------------------------------------------------- + +bool Trigger::testObject(GameBase* enter) +{ + if (mTriggerPolyhedron.pointList.size() == 0) + return false; + + mClippedList.clear(); + + SphereF sphere; + sphere.center = (mWorldBox.minExtents + mWorldBox.maxExtents) * 0.5; + VectorF bv = mWorldBox.maxExtents - sphere.center; + sphere.radius = bv.len(); + + enter->buildPolyList(PLC_Collision, &mClippedList, mWorldBox, sphere); + return mClippedList.isEmpty() == false; +} + + +void Trigger::potentialEnterObject(GameBase* enter) +{ + if( (!mDataBlock || mDataBlock->isClientSide) && isServerObject() ) + return; + if( (mDataBlock && !mDataBlock->isClientSide) && isGhost() ) + return; + + for (U32 i = 0; i < mObjects.size(); i++) { + if (mObjects[i] == enter) + return; + } + + if (testObject(enter) == true) { + mObjects.push_back(enter); + deleteNotify(enter); + + if(!mEnterCommand.isEmpty()) + { + String command = String("%obj = ") + enter->getIdString() + ";" + mEnterCommand; + Con::evaluate(command.c_str()); + } + + if( mDataBlock ) + mDataBlock->onEnterTrigger_callback( this, enter ); + } +} + + +void Trigger::processTick(const Move* move) +{ + Parent::processTick(move); + + if (!mDataBlock) + return; + if (mDataBlock->isClientSide && isServerObject()) + return; + if (!mDataBlock->isClientSide && isClientObject()) + return; + + // + if (mObjects.size() == 0) + return; + + if (mLastThink + mDataBlock->tickPeriodMS < mCurrTick) + { + mCurrTick = 0; + mLastThink = 0; + + for (S32 i = S32(mObjects.size() - 1); i >= 0; i--) + { + if (testObject(mObjects[i]) == false) + { + GameBase* remove = mObjects[i]; + mObjects.erase(i); + clearNotify(remove); + + if (!mLeaveCommand.isEmpty()) + { + String command = String("%obj = ") + remove->getIdString() + ";" + mLeaveCommand; + Con::evaluate(command.c_str()); + } + + mDataBlock->onLeaveTrigger_callback( this, remove ); + } + } + + if (!mTickCommand.isEmpty()) + Con::evaluate(mTickCommand.c_str()); + + if (mObjects.size() != 0) + mDataBlock->onTickTrigger_callback( this ); + } + else + { + mCurrTick += TickMs; + } +} + +//-------------------------------------------------------------------------- + +U32 Trigger::packUpdate(NetConnection* con, U32 mask, BitStream* stream) +{ + U32 i; + U32 retMask = Parent::packUpdate(con, mask, stream); + + if( stream->writeFlag( mask & TransformMask ) ) + { + stream->writeAffineTransform(mObjToWorld); + } + + // Write the polyhedron + if( stream->writeFlag( mask & PolyMask ) ) + { + stream->write(mTriggerPolyhedron.pointList.size()); + for (i = 0; i < mTriggerPolyhedron.pointList.size(); i++) + mathWrite(*stream, mTriggerPolyhedron.pointList[i]); + + stream->write(mTriggerPolyhedron.planeList.size()); + for (i = 0; i < mTriggerPolyhedron.planeList.size(); i++) + mathWrite(*stream, mTriggerPolyhedron.planeList[i]); + + stream->write(mTriggerPolyhedron.edgeList.size()); + for (i = 0; i < mTriggerPolyhedron.edgeList.size(); i++) { + const Polyhedron::Edge& rEdge = mTriggerPolyhedron.edgeList[i]; + + stream->write(rEdge.face[0]); + stream->write(rEdge.face[1]); + stream->write(rEdge.vertex[0]); + stream->write(rEdge.vertex[1]); + } + } + + if( stream->writeFlag( mask & EnterCmdMask ) ) + stream->writeLongString(CMD_SIZE-1, mEnterCommand.c_str()); + if( stream->writeFlag( mask & LeaveCmdMask ) ) + stream->writeLongString(CMD_SIZE-1, mLeaveCommand.c_str()); + if( stream->writeFlag( mask & TickCmdMask ) ) + stream->writeLongString(CMD_SIZE-1, mTickCommand.c_str()); + + return retMask; +} + +void Trigger::unpackUpdate(NetConnection* con, BitStream* stream) +{ + Parent::unpackUpdate(con, stream); + + U32 i, size; + + // Transform + if( stream->readFlag() ) + { + MatrixF temp; + stream->readAffineTransform(&temp); + setTransform(temp); + } + + // Read the polyhedron + if( stream->readFlag() ) + { + Polyhedron tempPH; + stream->read(&size); + tempPH.pointList.setSize(size); + for (i = 0; i < tempPH.pointList.size(); i++) + mathRead(*stream, &tempPH.pointList[i]); + + stream->read(&size); + tempPH.planeList.setSize(size); + for (i = 0; i < tempPH.planeList.size(); i++) + mathRead(*stream, &tempPH.planeList[i]); + + stream->read(&size); + tempPH.edgeList.setSize(size); + for (i = 0; i < tempPH.edgeList.size(); i++) { + Polyhedron::Edge& rEdge = tempPH.edgeList[i]; + + stream->read(&rEdge.face[0]); + stream->read(&rEdge.face[1]); + stream->read(&rEdge.vertex[0]); + stream->read(&rEdge.vertex[1]); + } + setTriggerPolyhedron(tempPH); + } + + if( stream->readFlag() ) + { + char buf[CMD_SIZE]; + stream->readLongString(CMD_SIZE-1, buf); + mEnterCommand = buf; + } + if( stream->readFlag() ) + { + char buf[CMD_SIZE]; + stream->readLongString(CMD_SIZE-1, buf); + mLeaveCommand = buf; + } + if( stream->readFlag() ) + { + char buf[CMD_SIZE]; + stream->readLongString(CMD_SIZE-1, buf); + mTickCommand = buf; + } +} + +//ConsoleMethod( Trigger, getNumObjects, S32, 2, 2, "") +DefineEngineMethod( Trigger, getNumObjects, S32, (),, + "@brief Get the number of objects that are within the Trigger's bounds.\n\n" + "@see getObject()\n") +{ + return object->getNumTriggeringObjects(); +} + +//ConsoleMethod( Trigger, getObject, S32, 3, 3, "(int idx)") +DefineEngineMethod( Trigger, getObject, S32, ( S32 index ),, + "@brief Retrieve the requested object that is within the Trigger's bounds.\n\n" + "@param index Index of the object to get (range is 0 to getNumObjects()-1)\n" + "@returns The SimObjectID of the object, or -1 if the requested index is invalid.\n" + "@see getNumObjects()\n") +{ + if (index >= object->getNumTriggeringObjects() || index < 0) + return -1; + else + return object->getObject(U32(index))->getId(); +} diff --git a/Engine/source/T3D/trigger.h b/Engine/source/T3D/trigger.h new file mode 100644 index 000000000..f0f8bc339 --- /dev/null +++ b/Engine/source/T3D/trigger.h @@ -0,0 +1,168 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _H_TRIGGER +#define _H_TRIGGER + +#ifndef _GAMEBASE_H_ +#include "T3D/gameBase/gameBase.h" +#endif +#ifndef _MBOX_H_ +#include "math/mBox.h" +#endif +#ifndef _EARLYOUTPOLYLIST_H_ +#include "collision/earlyOutPolyList.h" +#endif +#ifndef _MPOLYHEDRON_H_ +#include "math/mPolyhedron.h" +#endif + +class Convex; +class PhysicsBody; +class Trigger; + +DefineConsoleType( TypeTriggerPolyhedron, Polyhedron ) + + +struct TriggerData: public GameBaseData { + typedef GameBaseData Parent; + + public: + S32 tickPeriodMS; + bool isClientSide; + + TriggerData(); + + DECLARE_CONOBJECT(TriggerData); + + DECLARE_CALLBACK( void, onEnterTrigger, ( Trigger* trigger, GameBase* obj ) ); + DECLARE_CALLBACK( void, onTickTrigger, ( Trigger* trigger ) ); + DECLARE_CALLBACK( void, onLeaveTrigger, ( Trigger* trigger, GameBase* obj ) ); + + bool onAdd(); + static void initPersistFields(); + virtual void packData (BitStream* stream); + virtual void unpackData(BitStream* stream); +}; + +class Trigger : public GameBase +{ + typedef GameBase Parent; + + /// Trigger polyhedron with *outward* facing normals and CCW ordered + /// vertices. + Polyhedron mTriggerPolyhedron; + + EarlyOutPolyList mClippedList; + Vector mObjects; + + PhysicsBody *mPhysicsRep; + + TriggerData* mDataBlock; + + U32 mLastThink; + U32 mCurrTick; + Convex *mConvexList; + + String mEnterCommand; + String mLeaveCommand; + String mTickCommand; + + enum TriggerUpdateBits + { + TransformMask = Parent::NextFreeMask << 0, + PolyMask = Parent::NextFreeMask << 1, + EnterCmdMask = Parent::NextFreeMask << 2, + LeaveCmdMask = Parent::NextFreeMask << 3, + TickCmdMask = Parent::NextFreeMask << 4, + NextFreeMask = Parent::NextFreeMask << 5, + }; + + static const U32 CMD_SIZE = 1024; + + protected: + + static bool smRenderTriggers; + bool testObject(GameBase* enter); + void processTick(const Move *move); + + void buildConvex(const Box3F& box, Convex* convex); + + static bool setEnterCmd(void *object, const char *index, const char *data); + static bool setLeaveCmd(void *object, const char *index, const char *data); + static bool setTickCmd(void *object, const char *index, const char *data); + + public: + Trigger(); + ~Trigger(); + + // SimObject + DECLARE_CONOBJECT(Trigger); + + DECLARE_CALLBACK( void, onAdd, ( U32 objectId ) ); + DECLARE_CALLBACK( void, onRemove, ( U32 objectId ) ); + + static void consoleInit(); + static void initPersistFields(); + bool onAdd(); + void onRemove(); + void onDeleteNotify(SimObject*); + void inspectPostApply(); + + // NetObject + U32 packUpdate (NetConnection *conn, U32 mask, BitStream* stream); + void unpackUpdate(NetConnection *conn, BitStream* stream); + + // SceneObject + void setTransform(const MatrixF &mat); + void prepRenderImage( SceneRenderState* state ); + + // GameBase + bool onNewDataBlock( GameBaseData *dptr, bool reload ); + + // Trigger + void setTriggerPolyhedron(const Polyhedron&); + + void potentialEnterObject(GameBase*); + U32 getNumTriggeringObjects() const; + GameBase* getObject(const U32); + const Vector& getObjects() const { return mObjects; } + + void renderObject( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); + + bool castRay(const Point3F &start, const Point3F &end, RayInfo* info); +}; + +inline U32 Trigger::getNumTriggeringObjects() const +{ + return mObjects.size(); +} + +inline GameBase* Trigger::getObject(const U32 index) +{ + AssertFatal(index < getNumTriggeringObjects(), "Error, out of range object index"); + + return mObjects[index]; +} + +#endif // _H_TRIGGER + diff --git a/Engine/source/T3D/tsStatic.cpp b/Engine/source/T3D/tsStatic.cpp new file mode 100644 index 000000000..520074298 --- /dev/null +++ b/Engine/source/T3D/tsStatic.cpp @@ -0,0 +1,1146 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/tsStatic.h" + +#include "core/resourceManager.h" +#include "core/stream/bitStream.h" +#include "scene/sceneRenderState.h" +#include "scene/sceneManager.h" +#include "scene/sceneObjectLightingPlugin.h" +#include "lighting/lightManager.h" +#include "math/mathIO.h" +#include "ts/tsShapeInstance.h" +#include "ts/tsMaterialList.h" +#include "console/consoleTypes.h" +#include "T3D/shapeBase.h" +#include "sim/netConnection.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxTransformSaver.h" +#include "ts/tsRenderState.h" +#include "collision/boxConvex.h" +#include "T3D/physics/physicsPlugin.h" +#include "T3D/physics/physicsBody.h" +#include "T3D/physics/physicsCollision.h" +#include "materials/materialDefinition.h" +#include "materials/materialManager.h" +#include "materials/matInstance.h" +#include "materials/materialFeatureData.h" +#include "materials/materialFeatureTypes.h" +#include "console/engineAPI.h" + +extern bool gEditingMission; + +IMPLEMENT_CO_NETOBJECT_V1(TSStatic); + +ConsoleDocClass( TSStatic, + "@brief A static object derived from a 3D model file and placed within the game world.\n\n" + + "TSStatic is the most basic 3D shape in Torque. Unlike StaticShape it doesn't make use of " + "a datablock. It derrives directly from SceneObject. This makes TSStatic extremely light " + "weight, which is why the Tools use this class when you want to drop in a DTS or DAE object.\n\n" + + "While a TSStatic doesn't provide any motion -- it stays were you initally put it -- it does allow for " + "a single ambient animation sequence to play when the object is first added to the scene.\n\n" + + "@tsexample\n" + "new TSStatic(Team1Base) {\n" + " shapeName = \"art/shapes/desertStructures/station01.dts\";\n" + " playAmbient = \"1\";\n" + " receiveSunLight = \"1\";\n" + " receiveLMLighting = \"1\";\n" + " useCustomAmbientLighting = \"0\";\n" + " customAmbientLighting = \"0 0 0 1\";\n" + " collisionType = \"Visible Mesh\";\n" + " decalType = \"Collision Mesh\";\n" + " allowPlayerStep = \"1\";\n" + " renderNormals = \"0\";\n" + " forceDetail = \"-1\";\n" + " position = \"315.18 -180.418 244.313\";\n" + " rotation = \"0 0 1 195.952\";\n" + " scale = \"1 1 1\";\n" + " isRenderEnabled = \"true\";\n" + " canSaveDynamicFields = \"1\";\n" + "};\n" + "@endtsexample\n" + + "@ingroup gameObjects\n" +); + +TSStatic::TSStatic() +{ + mNetFlags.set(Ghostable | ScopeAlways); + + mTypeMask |= StaticObjectType | StaticShapeObjectType; + + mShapeName = ""; + mShapeInstance = NULL; + + mPlayAmbient = true; + mAmbientThread = NULL; + + mAllowPlayerStep = true; + + mConvexList = new Convex; + + mRenderNormalScalar = 0; + mForceDetail = -1; + + mMeshCulling = false; + mUseOriginSort = false; + + mPhysicsRep = NULL; + + mCollisionType = CollisionMesh; + mDecalType = CollisionMesh; +} + +TSStatic::~TSStatic() +{ + delete mConvexList; + mConvexList = NULL; +} + +ImplementEnumType( TSMeshType, + "Type of mesh data available in a shape.\n" + "@ingroup gameObjects" ) + { TSStatic::None, "None", "No mesh data." }, + { TSStatic::Bounds, "Bounds", "Bounding box of the shape." }, + { TSStatic::CollisionMesh, "Collision Mesh", "Specifically desingated \"collision\" meshes." }, + { TSStatic::VisibleMesh, "Visible Mesh", "Rendered mesh polygons." }, +EndImplementEnumType; + + +void TSStatic::initPersistFields() +{ + addGroup("Media"); + + addField("shapeName", TypeShapeFilename, Offset( mShapeName, TSStatic ), + "%Path and filename of the model file (.DTS, .DAE) to use for this TSStatic." ); + + addProtectedField( "skin", TypeRealString, Offset( mAppliedSkinName, TSStatic ), &_setFieldSkin, &_getFieldSkin, + "@brief The skin applied to the shape.\n\n" + + "'Skinning' the shape effectively renames the material targets, allowing " + "different materials to be used on different instances of the same model.\n\n" + + "Any material targets that start with the old skin name have that part " + "of the name replaced with the new skin name. The initial old skin name is " + "\"base\". For example, if a new skin of \"blue\" was applied to a model " + "that had material targets base_body and face, the new targets " + "would be blue_body and face. Note that face was not " + "renamed since it did not start with the old skin name of \"base\".\n\n" + + "To support models that do not use the default \"base\" naming convention, " + "you can also specify the part of the name to replace in the skin field " + "itself. For example, if a model had a material target called shapemat, " + "we could apply a new skin \"shape=blue\", and the material target would be " + "renamed to bluemat (note \"shape\" has been replaced with \"blue\").\n\n" + + "Multiple skin updates can also be applied at the same time by separating " + "them with a semicolon. For example: \"base=blue;face=happy_face\".\n\n" + + "Material targets are only renamed if an existing Material maps to that " + "name, or if there is a diffuse texture in the model folder with the same " + "name as the new target.\n\n" ); + + endGroup("Media"); + + addGroup("Rendering"); + + addField( "playAmbient", TypeBool, Offset( mPlayAmbient, TSStatic ), + "Enables automatic playing of the animation sequence named \"ambient\" (if it exists) when the TSStatic is loaded."); + addField( "meshCulling", TypeBool, Offset( mMeshCulling, TSStatic ), + "Enables detailed culling of meshes within the TSStatic. Should only be used " + "with large complex shapes like buildings which contain many submeshes." ); + addField( "originSort", TypeBool, Offset( mUseOriginSort, TSStatic ), + "Enables translucent sorting of the TSStatic by its origin instead of the bounds." ); + + endGroup("Rendering"); + + addGroup("Collision"); + + addField( "collisionType", TypeTSMeshType, Offset( mCollisionType, TSStatic ), + "The type of mesh data to use for collision queries." ); + addField( "decalType", TypeTSMeshType, Offset( mDecalType, TSStatic ), + "The type of mesh data used to clip decal polygons against." ); + addField( "allowPlayerStep", TypeBool, Offset( mAllowPlayerStep, TSStatic ), + "@brief Allow a Player to walk up sloping polygons in the TSStatic (based on the collisionType).\n\n" + "When set to false, the slightest bump will stop the player from walking on top of the object.\n"); + + endGroup("Collision"); + + addGroup("Debug"); + + addField( "renderNormals", TypeF32, Offset( mRenderNormalScalar, TSStatic ), + "Debug rendering mode shows the normals for each point in the TSStatic's mesh." ); + addField( "forceDetail", TypeS32, Offset( mForceDetail, TSStatic ), + "Forces rendering to a particular detail level." ); + + endGroup("Debug"); + + Parent::initPersistFields(); +} + +bool TSStatic::_setFieldSkin( void *object, const char *index, const char *data ) +{ + TSStatic *ts = static_cast( object ); + if ( ts ) + ts->setSkinName( data ); + return false; +} + +const char *TSStatic::_getFieldSkin( void *object, const char *data ) +{ + TSStatic *ts = static_cast( object ); + return ts ? ts->mSkinNameHandle.getString() : ""; +} + +void TSStatic::inspectPostApply() +{ + // Apply any transformations set in the editor + Parent::inspectPostApply(); + + if(isServerObject()) + { + setMaskBits(AdvancedStaticOptionsMask); + prepCollision(); + } + + _updateShouldTick(); +} + +bool TSStatic::onAdd() +{ + PROFILE_SCOPE(TSStatic_onAdd); + + if ( isServerObject() ) + { + // Handle the old "usePolysoup" field + SimFieldDictionary* fieldDict = getFieldDictionary(); + + if ( fieldDict ) + { + StringTableEntry slotName = StringTable->insert( "usePolysoup" ); + + SimFieldDictionary::Entry * entry = fieldDict->findDynamicField( slotName ); + + if ( entry ) + { + // Was "usePolysoup" set? + bool usePolysoup = dAtob( entry->value ); + + // "usePolysoup" maps to the new VisibleMesh type + if ( usePolysoup ) + mCollisionType = VisibleMesh; + + // Remove the field in favor on the new "collisionType" field + fieldDict->setFieldValue( slotName, "" ); + } + } + } + + if ( !Parent::onAdd() ) + return false; + + // Setup the shape. + if ( !_createShape() ) + { + Con::errorf( "TSStatic::onAdd() - Shape creation failed!" ); + return false; + } + + setRenderTransform(mObjToWorld); + + // Register for the resource change signal. + ResourceManager::get().getChangedSignal().notify( this, &TSStatic::_onResourceChanged ); + + addToScene(); + + _updateShouldTick(); + + return true; +} + +bool TSStatic::_createShape() +{ + // Cleanup before we create. + mCollisionDetails.clear(); + mLOSDetails.clear(); + SAFE_DELETE( mPhysicsRep ); + SAFE_DELETE( mShapeInstance ); + mAmbientThread = NULL; + mShape = NULL; + + if (!mShapeName || mShapeName[0] == '\0') + { + Con::errorf( "TSStatic::_createShape() - No shape name!" ); + return false; + } + + mShapeHash = _StringTable::hashString(mShapeName); + + mShape = ResourceManager::get().load(mShapeName); + if ( bool(mShape) == false ) + { + Con::errorf( "TSStatic::_createShape() - Unable to load shape: %s", mShapeName ); + return false; + } + + if ( isClientObject() && + !mShape->preloadMaterialList(mShape.getPath()) && + NetConnection::filesWereDownloaded() ) + return false; + + mObjBox = mShape->bounds; + resetWorldBox(); + + mShapeInstance = new TSShapeInstance( mShape, isClientObject() ); + + if( isGhost() ) + { + // Reapply the current skin + mAppliedSkinName = ""; + reSkin(); + } + + prepCollision(); + + // Find the "ambient" animation if it exists + S32 ambientSeq = mShape->findSequence("ambient"); + + if ( ambientSeq > -1 && !mAmbientThread ) + mAmbientThread = mShapeInstance->addThread(); + + if ( mAmbientThread ) + mShapeInstance->setSequence( mAmbientThread, ambientSeq, 0); + + return true; +} + +void TSStatic::prepCollision() +{ + // Let the client know that the collision was updated + setMaskBits( UpdateCollisionMask ); + + // Allow the ShapeInstance to prep its collision if it hasn't already + if ( mShapeInstance ) + mShapeInstance->prepCollision(); + + // Cleanup any old collision data + mCollisionDetails.clear(); + mLOSDetails.clear(); + mConvexList->nukeList(); + + if ( mCollisionType == CollisionMesh || mCollisionType == VisibleMesh ) + mShape->findColDetails( mCollisionType == VisibleMesh, &mCollisionDetails, &mLOSDetails ); + + _updatePhysics(); +} + +void TSStatic::_updatePhysics() +{ + SAFE_DELETE( mPhysicsRep ); + + if ( !PHYSICSMGR || mCollisionType == None ) + return; + + PhysicsCollision *colShape = NULL; + if ( mCollisionType == Bounds ) + { + MatrixF offset( true ); + offset.setPosition( mShape->center ); + colShape = PHYSICSMGR->createCollision(); + colShape->addBox( getObjBox().getExtents() * 0.5f * mObjScale, offset ); + } + else + colShape = mShape->buildColShape( mCollisionType == VisibleMesh, getScale() ); + + if ( colShape ) + { + PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" ); + mPhysicsRep = PHYSICSMGR->createBody(); + mPhysicsRep->init( colShape, 0, 0, this, world ); + mPhysicsRep->setTransform( getTransform() ); + } +} + +void TSStatic::onRemove() +{ + SAFE_DELETE( mPhysicsRep ); + + mConvexList->nukeList(); + + removeFromScene(); + + // Remove the resource change signal. + ResourceManager::get().getChangedSignal().remove( this, &TSStatic::_onResourceChanged ); + + delete mShapeInstance; + mShapeInstance = NULL; + + mAmbientThread = NULL; + + Parent::onRemove(); +} + +void TSStatic::_onResourceChanged( const Torque::Path &path ) +{ + if ( path != Path( mShapeName ) ) + return; + + _createShape(); + _updateShouldTick(); +} + +void TSStatic::setSkinName( const char *name ) +{ + if ( !isGhost() ) + { + if ( name[0] != '\0' ) + { + // Use tags for better network performance + // Should be a tag, but we'll convert to one if it isn't. + if ( name[0] == StringTagPrefixByte ) + mSkinNameHandle = NetStringHandle( U32(dAtoi(name + 1)) ); + else + mSkinNameHandle = NetStringHandle( name ); + } + else + mSkinNameHandle = NetStringHandle(); + + setMaskBits( SkinMask ); + } +} + +void TSStatic::reSkin() +{ + if ( isGhost() && mShapeInstance && mSkinNameHandle.isValidString() ) + { + Vector skins; + String(mSkinNameHandle.getString()).split( ";", skins ); + + for (int i = 0; i < skins.size(); i++) + { + String oldSkin( mAppliedSkinName.c_str() ); + String newSkin( skins[i] ); + + // Check if the skin handle contains an explicit "old" base string. This + // allows all models to support skinning, even if they don't follow the + // "base_xxx" material naming convention. + S32 split = newSkin.find( '=' ); // "old=new" format skin? + if ( split != String::NPos ) + { + oldSkin = newSkin.substr( 0, split ); + newSkin = newSkin.erase( 0, split+1 ); + } + + mShapeInstance->reSkin( newSkin, oldSkin ); + mAppliedSkinName = newSkin; + } + } +} + +void TSStatic::processTick( const Move *move ) +{ + AssertFatal( mPlayAmbient && mAmbientThread, "TSSTatic::adanceTime called with nothing to play." ); + + if ( isServerObject() ) + mShapeInstance->advanceTime( TickSec, mAmbientThread ); +} + +void TSStatic::interpolateTick( F32 delta ) +{ +} + +void TSStatic::advanceTime( F32 dt ) +{ + AssertFatal( mPlayAmbient && mAmbientThread, "TSSTatic::advanceTime called with nothing to play." ); + + mShapeInstance->advanceTime( dt, mAmbientThread ); +} + +void TSStatic::_updateShouldTick() +{ + bool shouldTick = mPlayAmbient && mAmbientThread; + + if ( isTicking() != shouldTick ) + setProcessTick( shouldTick ); +} + +void TSStatic::prepRenderImage( SceneRenderState* state ) +{ + if( !mShapeInstance ) + return; + + Point3F cameraOffset; + getRenderTransform().getColumn(3,&cameraOffset); + cameraOffset -= state->getDiffuseCameraPosition(); + F32 dist = cameraOffset.len(); + if (dist < 0.01f) + dist = 0.01f; + + F32 invScale = (1.0f/getMax(getMax(mObjScale.x,mObjScale.y),mObjScale.z)); + + if ( mForceDetail == -1 ) + mShapeInstance->setDetailFromDistance( state, dist * invScale ); + else + mShapeInstance->setCurrentDetail( mForceDetail ); + + if ( mShapeInstance->getCurrentDetail() < 0 ) + return; + + GFXTransformSaver saver; + + // Set up our TS render state. + TSRenderState rdata; + rdata.setSceneState( state ); + rdata.setFadeOverride( 1.0f ); + rdata.setOriginSort( mUseOriginSort ); + + // If we have submesh culling enabled then prepare + // the object space frustum to pass to the shape. + Frustum culler; + if ( mMeshCulling ) + { + culler = state->getFrustum(); + MatrixF xfm( true ); + xfm.scale( Point3F::One / getScale() ); + xfm.mul( getRenderWorldTransform() ); + xfm.mul( culler.getTransform() ); + culler.setTransform( xfm ); + rdata.setCuller( &culler ); + } + + // We might have some forward lit materials + // so pass down a query to gather lights. + LightQuery query; + query.init( getWorldSphere() ); + rdata.setLightQuery( &query ); + + MatrixF mat = getRenderTransform(); + mat.scale( mObjScale ); + GFX->setWorldMatrix( mat ); + + mShapeInstance->animate(); + mShapeInstance->render( rdata ); + + if ( mRenderNormalScalar > 0 ) + { + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &TSStatic::_renderNormals ); + ri->type = RenderPassManager::RIT_Editor; + state->getRenderPass()->addInst( ri ); + } +} + +void TSStatic::_renderNormals( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ) +{ + PROFILE_SCOPE( TSStatic_RenderNormals ); + + GFXTransformSaver saver; + + MatrixF mat = getRenderTransform(); + mat.scale( mObjScale ); + GFX->multWorld( mat ); + + S32 dl = mShapeInstance->getCurrentDetail(); + mShapeInstance->renderDebugNormals( mRenderNormalScalar, dl ); +} + +void TSStatic::onScaleChanged() +{ + Parent::onScaleChanged(); + + if ( mPhysicsRep ) + { + // If the editor is enabled delay the scale operation + // by a few milliseconds so that we're not rebuilding + // during an active scale drag operation. + if ( gEditingMission ) + mPhysicsRep->queueCallback( 500, Delegate( this, &TSStatic::_updatePhysics ) ); + else + _updatePhysics(); + } +} + +void TSStatic::setTransform(const MatrixF & mat) +{ + Parent::setTransform(mat); + setMaskBits( TransformMask ); + + if ( mPhysicsRep ) + mPhysicsRep->setTransform( mat ); + + // Since this is a static it's render transform changes 1 + // to 1 with it's collision transform... no interpolation. + setRenderTransform(mat); +} + +U32 TSStatic::packUpdate(NetConnection *con, U32 mask, BitStream *stream) +{ + U32 retMask = Parent::packUpdate(con, mask, stream); + + mathWrite( *stream, getTransform() ); + mathWrite( *stream, getScale() ); + stream->writeString( mShapeName ); + + if ( stream->writeFlag( mask & UpdateCollisionMask ) ) + stream->write( (U32)mCollisionType ); + + if ( stream->writeFlag( mask & SkinMask ) ) + con->packNetStringHandleU( stream, mSkinNameHandle ); + + stream->write( (U32)mDecalType ); + + stream->writeFlag( mAllowPlayerStep ); + stream->writeFlag( mMeshCulling ); + stream->writeFlag( mUseOriginSort ); + + stream->write( mRenderNormalScalar ); + + stream->write( mForceDetail ); + + stream->writeFlag( mPlayAmbient ); + + if ( mLightPlugin ) + retMask |= mLightPlugin->packUpdate(this, AdvancedStaticOptionsMask, con, mask, stream); + + return retMask; +} + +void TSStatic::unpackUpdate(NetConnection *con, BitStream *stream) +{ + Parent::unpackUpdate(con, stream); + + MatrixF mat; + Point3F scale; + mathRead( *stream, &mat ); + mathRead( *stream, &scale ); + setScale( scale); + setTransform(mat); + + mShapeName = stream->readSTString(); + + if ( stream->readFlag() ) // UpdateCollisionMask + { + U32 collisionType = CollisionMesh; + + stream->read( &collisionType ); + + // Handle it if we have changed CollisionType's + if ( (MeshType)collisionType != mCollisionType ) + { + mCollisionType = (MeshType)collisionType; + + if ( isProperlyAdded() && mShapeInstance ) + prepCollision(); + } + } + + if (stream->readFlag()) // SkinMask + { + NetStringHandle skinDesiredNameHandle = con->unpackNetStringHandleU(stream);; + if (mSkinNameHandle != skinDesiredNameHandle) + { + mSkinNameHandle = skinDesiredNameHandle; + reSkin(); + } + } + + stream->read( (U32*)&mDecalType ); + + mAllowPlayerStep = stream->readFlag(); + mMeshCulling = stream->readFlag(); + mUseOriginSort = stream->readFlag(); + + stream->read( &mRenderNormalScalar ); + + stream->read( &mForceDetail ); + + mPlayAmbient = stream->readFlag(); + + if ( mLightPlugin ) + { + mLightPlugin->unpackUpdate(this, con, stream); + } + + if ( isProperlyAdded() ) + _updateShouldTick(); +} + +//---------------------------------------------------------------------------- +bool TSStatic::castRay(const Point3F &start, const Point3F &end, RayInfo* info) +{ + if ( mCollisionType == None ) + return false; + + if ( !mShapeInstance ) + return false; + + if ( mCollisionType == Bounds ) + { + F32 st, et, fst = 0.0f, fet = 1.0f; + F32 *bmin = &mObjBox.minExtents.x; + F32 *bmax = &mObjBox.maxExtents.x; + F32 const *si = &start.x; + F32 const *ei = &end.x; + + for ( U32 i = 0; i < 3; i++ ) + { + if (*si < *ei) + { + if ( *si > *bmax || *ei < *bmin ) + return false; + F32 di = *ei - *si; + st = ( *si < *bmin ) ? ( *bmin - *si ) / di : 0.0f; + et = ( *ei > *bmax ) ? ( *bmax - *si ) / di : 1.0f; + } + else + { + if ( *ei > *bmax || *si < *bmin ) + return false; + F32 di = *ei - *si; + st = ( *si > *bmax ) ? ( *bmax - *si ) / di : 0.0f; + et = ( *ei < *bmin ) ? ( *bmin - *si ) / di : 1.0f; + } + if ( st > fst ) fst = st; + if ( et < fet ) fet = et; + if ( fet < fst ) + return false; + bmin++; bmax++; + si++; ei++; + } + + info->normal = start - end; + info->normal.normalizeSafe(); + getTransform().mulV( info->normal ); + + info->t = fst; + info->object = this; + info->point.interpolate( start, end, fst ); + info->material = NULL; + return true; + } + else + { + RayInfo shortest = *info; + RayInfo localInfo; + shortest.t = 1e8f; + localInfo.generateTexCoord = info->generateTexCoord; + + for ( U32 i = 0; i < mLOSDetails.size(); i++ ) + { + mShapeInstance->animate( mLOSDetails[i] ); + + if ( mShapeInstance->castRayOpcode( mLOSDetails[i], start, end, &localInfo ) ) + { + localInfo.object = this; + + if (localInfo.t < shortest.t) + shortest = localInfo; + } + } + + if (shortest.object == this) + { + // Copy out the shortest time... + *info = shortest; + return true; + } + } + + return false; +} + +bool TSStatic::castRayRendered(const Point3F &start, const Point3F &end, RayInfo *info) +{ + if ( !mShapeInstance ) + return false; + + // Cast the ray against the currently visible detail + RayInfo localInfo; + bool res = mShapeInstance->castRayOpcode( mShapeInstance->getCurrentDetail(), start, end, &localInfo ); + + if ( res ) + { + *info = localInfo; + info->object = this; + return true; + } + + return false; +} + +bool TSStatic::buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &) +{ + if ( !mShapeInstance ) + return false; + + // This is safe to set even if we're not outputing + polyList->setTransform( &mObjToWorld, mObjScale ); + polyList->setObject( this ); + + if ( context == PLC_Export ) + { + // Use highest detail level + S32 dl = 0; + + // Try to call on the client so we can export materials + if ( isServerObject() && getClientObject() ) + dynamic_cast(getClientObject())->mShapeInstance->buildPolyList( polyList, dl ); + else + mShapeInstance->buildPolyList( polyList, dl ); + } + else if ( context == PLC_Selection ) + { + // Use the last rendered detail level + S32 dl = mShapeInstance->getCurrentDetail(); + mShapeInstance->buildPolyListOpcode( dl, polyList, box ); + } + else + { + // Figure out the mesh type we're looking for. + MeshType meshType = ( context == PLC_Decal ) ? mDecalType : mCollisionType; + + if ( meshType == None ) + return false; + else if ( meshType == Bounds ) + polyList->addBox( mObjBox ); + else + { + // Everything else is done from the collision meshes + // which may be built from either the visual mesh or + // special collision geometry. + for ( U32 i = 0; i < mCollisionDetails.size(); i++ ) + mShapeInstance->buildPolyListOpcode( mCollisionDetails[i], polyList, box ); + } + } + + return true; +} + +void TSStatic::buildConvex(const Box3F& box, Convex* convex) +{ + if ( mCollisionType == None ) + return; + + if ( mShapeInstance == NULL ) + return; + + // These should really come out of a pool + mConvexList->collectGarbage(); + + if ( mCollisionType == Bounds ) + { + // Just return a box convex for the entire shape... + Convex* cc = 0; + CollisionWorkingList& wl = convex->getWorkingList(); + for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) + { + if (itr->mConvex->getType() == BoxConvexType && + itr->mConvex->getObject() == this) + { + cc = itr->mConvex; + break; + } + } + if (cc) + return; + + // Create a new convex. + BoxConvex* cp = new BoxConvex; + mConvexList->registerObject(cp); + convex->addToWorkingList(cp); + cp->init(this); + + mObjBox.getCenter(&cp->mCenter); + cp->mSize.x = mObjBox.len_x() / 2.0f; + cp->mSize.y = mObjBox.len_y() / 2.0f; + cp->mSize.z = mObjBox.len_z() / 2.0f; + } + else // CollisionMesh || VisibleMesh + { + TSStaticPolysoupConvex::smCurObject = this; + + for (U32 i = 0; i < mCollisionDetails.size(); i++) + mShapeInstance->buildConvexOpcode( mObjToWorld, mObjScale, mCollisionDetails[i], box, convex, mConvexList ); + + TSStaticPolysoupConvex::smCurObject = NULL; + } +} + +SceneObject* TSStaticPolysoupConvex::smCurObject = NULL; + +TSStaticPolysoupConvex::TSStaticPolysoupConvex() +: box( 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f ), + normal( 0.0f, 0.0f, 0.0f, 0.0f ), + idx( 0 ), + mesh( NULL ) +{ + mType = TSPolysoupConvexType; + + for ( U32 i = 0; i < 4; ++i ) + { + verts[i].set( 0.0f, 0.0f, 0.0f ); + } +} + +Point3F TSStaticPolysoupConvex::support(const VectorF& vec) const +{ + F32 bestDot = mDot( verts[0], vec ); + + const Point3F *bestP = &verts[0]; + for(S32 i=1; i<4; i++) + { + F32 newD = mDot(verts[i], vec); + if(newD > bestDot) + { + bestDot = newD; + bestP = &verts[i]; + } + } + + return *bestP; +} + +Box3F TSStaticPolysoupConvex::getBoundingBox() const +{ + Box3F wbox = box; + wbox.minExtents.convolve( mObject->getScale() ); + wbox.maxExtents.convolve( mObject->getScale() ); + mObject->getTransform().mul(wbox); + return wbox; +} + +Box3F TSStaticPolysoupConvex::getBoundingBox(const MatrixF& mat, const Point3F& scale) const +{ + AssertISV(false, "TSStaticPolysoupConvex::getBoundingBox(m,p) - Not implemented. -- XEA"); + return box; +} + +void TSStaticPolysoupConvex::getPolyList(AbstractPolyList *list) +{ + // Transform the list into object space and set the pointer to the object + MatrixF i( mObject->getTransform() ); + Point3F iS( mObject->getScale() ); + list->setTransform(&i, iS); + list->setObject(mObject); + + // Add only the original collision triangle + S32 base = list->addPoint(verts[0]); + list->addPoint(verts[2]); + list->addPoint(verts[1]); + + list->begin(0, (U32)idx ^ (U32)mesh); + list->vertex(base + 2); + list->vertex(base + 1); + list->vertex(base + 0); + list->plane(base + 0, base + 1, base + 2); + list->end(); +} + +void TSStaticPolysoupConvex::getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf) +{ + cf->material = 0; + cf->object = mObject; + + // For a tetrahedron this is pretty easy... first + // convert everything into world space. + Point3F tverts[4]; + mat.mulP(verts[0], &tverts[0]); + mat.mulP(verts[1], &tverts[1]); + mat.mulP(verts[2], &tverts[2]); + mat.mulP(verts[3], &tverts[3]); + + // points... + S32 firstVert = cf->mVertexList.size(); + cf->mVertexList.increment(); cf->mVertexList.last() = tverts[0]; + cf->mVertexList.increment(); cf->mVertexList.last() = tverts[1]; + cf->mVertexList.increment(); cf->mVertexList.last() = tverts[2]; + cf->mVertexList.increment(); cf->mVertexList.last() = tverts[3]; + + // edges... + cf->mEdgeList.increment(); + cf->mEdgeList.last().vertex[0] = firstVert+0; + cf->mEdgeList.last().vertex[1] = firstVert+1; + + cf->mEdgeList.increment(); + cf->mEdgeList.last().vertex[0] = firstVert+1; + cf->mEdgeList.last().vertex[1] = firstVert+2; + + cf->mEdgeList.increment(); + cf->mEdgeList.last().vertex[0] = firstVert+2; + cf->mEdgeList.last().vertex[1] = firstVert+0; + + cf->mEdgeList.increment(); + cf->mEdgeList.last().vertex[0] = firstVert+3; + cf->mEdgeList.last().vertex[1] = firstVert+0; + + cf->mEdgeList.increment(); + cf->mEdgeList.last().vertex[0] = firstVert+3; + cf->mEdgeList.last().vertex[1] = firstVert+1; + + cf->mEdgeList.increment(); + cf->mEdgeList.last().vertex[0] = firstVert+3; + cf->mEdgeList.last().vertex[1] = firstVert+2; + + // triangles... + cf->mFaceList.increment(); + cf->mFaceList.last().normal = PlaneF(tverts[2], tverts[1], tverts[0]); + cf->mFaceList.last().vertex[0] = firstVert+2; + cf->mFaceList.last().vertex[1] = firstVert+1; + cf->mFaceList.last().vertex[2] = firstVert+0; + + cf->mFaceList.increment(); + cf->mFaceList.last().normal = PlaneF(tverts[1], tverts[0], tverts[3]); + cf->mFaceList.last().vertex[0] = firstVert+1; + cf->mFaceList.last().vertex[1] = firstVert+0; + cf->mFaceList.last().vertex[2] = firstVert+3; + + cf->mFaceList.increment(); + cf->mFaceList.last().normal = PlaneF(tverts[2], tverts[1], tverts[3]); + cf->mFaceList.last().vertex[0] = firstVert+2; + cf->mFaceList.last().vertex[1] = firstVert+1; + cf->mFaceList.last().vertex[2] = firstVert+3; + + cf->mFaceList.increment(); + cf->mFaceList.last().normal = PlaneF(tverts[0], tverts[2], tverts[3]); + cf->mFaceList.last().vertex[0] = firstVert+0; + cf->mFaceList.last().vertex[1] = firstVert+2; + cf->mFaceList.last().vertex[2] = firstVert+3; + + // All done! +} + +//------------------------------------------------------------------------ +//These functions are duplicated in tsStatic, shapeBase, and interiorInstance. +//They each function a little differently; but achieve the same purpose of gathering +//target names/counts without polluting simObject. + +DefineEngineMethod( TSStatic, getTargetName, const char*, ( S32 index ),(0), + "Get the name of the indexed shape material.\n" + "@param index index of the material to get (valid range is 0 - getTargetCount()-1).\n" + "@return the name of the indexed material.\n" + "@see getTargetCount()\n") +{ + TSStatic *obj = dynamic_cast< TSStatic* > ( object ); + if(obj) + { + // Try to use the client object (so we get the reskinned targets in the Material Editor) + if ((TSStatic*)obj->getClientObject()) + obj = (TSStatic*)obj->getClientObject(); + + return obj->getShapeInstance()->getTargetName(index); + } + + return ""; +} + +DefineEngineMethod( TSStatic, getTargetCount, S32,(),, + "Get the number of materials in the shape.\n" + "@return the number of materials in the shape.\n" + "@see getTargetName()\n") +{ + TSStatic *obj = dynamic_cast< TSStatic* > ( object ); + if(obj) + { + // Try to use the client object (so we get the reskinned targets in the Material Editor) + if ((TSStatic*)obj->getClientObject()) + obj = (TSStatic*)obj->getClientObject(); + + return obj->getShapeInstance()->getTargetCount(); + } + + return -1; +} + +// This method is able to change materials per map to with others. The material that is being replaced is being mapped to +// unmapped_mat as a part of this transition + +DefineEngineMethod( TSStatic, changeMaterial, void, ( const char* mapTo, Material* oldMat, Material* newMat ),("",NULL,NULL), + "@brief Change one of the materials on the shape.\n\n" + + "This method changes materials per mapTo with others. The material that " + "is being replaced is mapped to unmapped_mat as a part of this transition.\n" + + "@note Warning, right now this only sort of works. It doesn't do a live " + "update like it should.\n" + + "@param mapTo the name of the material target to remap (from getTargetName)\n" + "@param oldMat the old Material that was mapped \n" + "@param newMat the new Material to map\n\n" + + "@tsexample\n" + "// remap the first material in the shape\n" + "%mapTo = %obj.getTargetName( 0 );\n" + "%obj.changeMaterial( %mapTo, 0, MyMaterial );\n" + "@endtsexample\n" ) +{ + // if no valid new material, theres no reason for doing this + if( !newMat ) + { + Con::errorf("TSShape::changeMaterial failed: New material does not exist!"); + return; + } + + // Check the mapTo name exists for this shape + S32 matIndex = object->getShape()->materialList->getMaterialNameList().find_next(String(mapTo)); + if (matIndex < 0) + { + Con::errorf("TSShape::changeMaterial failed: Invalid mapTo name '%s'", mapTo); + return; + } + + // Lets remap the old material off, so as to let room for our current material room to claim its spot + if( oldMat ) + oldMat->mMapTo = String("unmapped_mat"); + + newMat->mMapTo = mapTo; + + // Map the material in the in the matmgr + MATMGR->mapMaterial( mapTo, newMat->mMapTo ); + + // Replace instances with the new material being traded in. Lets make sure that we only + // target the specific targets per inst, this is actually doing more than we thought + delete object->getShape()->materialList->mMatInstList[matIndex]; + object->getShape()->materialList->mMatInstList[matIndex] = newMat->createMatInstance(); + + // Finish up preparing the material instances for rendering + const GFXVertexFormat *flags = getGFXVertexFormat(); + FeatureSet features = MATMGR->getDefaultFeatures(); + object->getShape()->materialList->getMaterialInst(matIndex)->init( features, flags ); +} + +DefineEngineMethod( TSStatic, getModelFile, const char *, (),, + "@brief Get the model filename used by this shape.\n\n" + + "@return the shape filename\n\n" + "@tsexample\n" + "// Acquire the model filename used on this shape.\n" + "%modelFilename = %obj.getModelFile();\n" + "@endtsexample\n" + ) +{ + return object->getShapeFileName(); +} \ No newline at end of file diff --git a/Engine/source/T3D/tsStatic.h b/Engine/source/T3D/tsStatic.h new file mode 100644 index 000000000..fe7113294 --- /dev/null +++ b/Engine/source/T3D/tsStatic.h @@ -0,0 +1,221 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TSSTATIC_H_ +#define _TSSTATIC_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _CONVEX_H_ +#include "collision/convex.h" +#endif +#ifndef __RESOURCE_H__ +#include "core/resource.h" +#endif +#ifndef _NETSTRINGTABLE_H_ + #include "sim/netStringTable.h" +#endif +#ifndef _TSSHAPE_H_ +#include "ts/tsShape.h" +#endif + +class TSShapeInstance; +class TSThread; +class TSStatic; +class PhysicsBody; +struct ObjectRenderInst; + + +class TSStaticPolysoupConvex : public Convex +{ + typedef Convex Parent; + friend class TSMesh; + +public: + TSStaticPolysoupConvex(); + ~TSStaticPolysoupConvex() {}; + +public: + Box3F box; + Point3F verts[4]; + PlaneF normal; + S32 idx; + TSMesh *mesh; + + static SceneObject* smCurObject; + +public: + + // Returns the bounding box in world coordinates + Box3F getBoundingBox() const; + Box3F getBoundingBox(const MatrixF& mat, const Point3F& scale) const; + + void getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf); + + // This returns a list of convex faces to collide against + void getPolyList(AbstractPolyList* list); + + // This returns the furthest point from the input vector + Point3F support(const VectorF& v) const; +}; + + +/// A simple mesh shape with optional ambient animation. +class TSStatic : public SceneObject +{ + typedef SceneObject Parent; + + static U32 smUniqueIdentifier; + + enum MaskBits + { + TransformMask = Parent::NextFreeMask << 0, + AdvancedStaticOptionsMask = Parent::NextFreeMask << 1, + UpdateCollisionMask = Parent::NextFreeMask << 2, + SkinMask = Parent::NextFreeFlag << 3, + NextFreeMask = Parent::NextFreeMask << 4 + }; + +public: + + /// The different types of mesh data types + enum MeshType + { + None = 0, ///< No mesh + Bounds = 1, ///< Bounding box of the shape + CollisionMesh = 2, ///< Specifically designated collision meshes + VisibleMesh = 3 ///< Rendered mesh polygons + }; + +protected: + + bool onAdd(); + void onRemove(); + + // Collision + void prepCollision(); + bool castRay(const Point3F &start, const Point3F &end, RayInfo* info); + bool castRayRendered(const Point3F &start, const Point3F &end, RayInfo* info); + bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF& sphere); + void buildConvex(const Box3F& box, Convex* convex); + + bool _createShape(); + + void _updatePhysics(); + + void _renderNormals( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); + + void _onResourceChanged( const Torque::Path &path ); + + // ProcessObject + virtual void processTick( const Move *move ); + virtual void interpolateTick( F32 delta ); + virtual void advanceTime( F32 dt ); + + /// Start or stop processing ticks depending on our state. + void _updateShouldTick(); + +protected: + + Convex *mConvexList; + + StringTableEntry mShapeName; + U32 mShapeHash; + Resource mShape; + Vector mCollisionDetails; + Vector mLOSDetails; + TSShapeInstance *mShapeInstance; + + NetStringHandle mSkinNameHandle; + String mAppliedSkinName; + + bool mPlayAmbient; + TSThread* mAmbientThread; + + /// The type of mesh data to return for collision queries. + MeshType mCollisionType; + + /// The type of mesh data to return for decal polylist queries. + MeshType mDecalType; + + bool mAllowPlayerStep; + + /// If true each submesh within the TSShape is culled + /// against the object space frustum. + bool mMeshCulling; + + /// If true the shape is sorted by the origin of the + /// model instead of the nearest point of the bounds. + bool mUseOriginSort; + + PhysicsBody *mPhysicsRep; + + // Debug stuff + F32 mRenderNormalScalar; + S32 mForceDetail; + +public: + + TSStatic(); + ~TSStatic(); + + DECLARE_CONOBJECT(TSStatic); + static void initPersistFields(); + static bool _setFieldSkin( void *object, const char* index, const char* data ); + static const char *_getFieldSkin( void *object, const char *data ); + + // Skinning + void setSkinName( const char *name ); + void reSkin(); + + // NetObject + U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream ); + void unpackUpdate( NetConnection *conn, BitStream *stream ); + + // SceneObject + void setTransform( const MatrixF &mat ); + void onScaleChanged(); + void prepRenderImage( SceneRenderState *state ); + void inspectPostApply(); + + /// The type of mesh data use for collision queries. + MeshType getCollisionType() const { return mCollisionType; } + + bool allowPlayerStep() const { return mAllowPlayerStep; } + + Resource getShape() const { return mShape; } + StringTableEntry getShapeFileName() { return mShapeName; } + + TSShapeInstance* getShapeInstance() const { return mShapeInstance; } + + const Vector& getCollisionDetails() const { return mCollisionDetails; } + + const Vector& getLOSDetails() const { return mLOSDetails; } + +}; + +typedef TSStatic::MeshType TSMeshType; +DefineEnumType( TSMeshType ); + +#endif // _H_TSSTATIC + diff --git a/Engine/source/T3D/turret/aiTurretShape.cpp b/Engine/source/T3D/turret/aiTurretShape.cpp new file mode 100644 index 000000000..1f6a15d31 --- /dev/null +++ b/Engine/source/T3D/turret/aiTurretShape.cpp @@ -0,0 +1,1355 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "T3D/turret/aiTurretShape.h" + +#include "console/console.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "core/stream/bitStream.h" +#include "math/mMath.h" +#include "math/mathIO.h" +#include "math/mathUtils.h" +#include "gfx/gfxDrawUtil.h" +#include "ts/tsShapeInstance.h" +#include "math/mRandom.h" + +static U32 sScanTypeMask = PlayerObjectType | + VehicleObjectType; + +static U32 sAimTypeMask = TerrainObjectType | + InteriorObjectType | + WaterObjectType | + PlayerObjectType | + StaticShapeObjectType | + VehicleObjectType | + ItemObjectType; + +//---------------------------------------------------------------------------- + +AITurretShapeData::StateData::StateData() +{ + name = 0; + transition.rest[0] = transition.rest[1] = -1; + transition.target[0] = transition.target[1] = -1; + transition.activated[0] = transition.activated[1] = -1; + transition.timeout = -1; + waitForTimeout = true; + timeoutValue = 0; + fire = false; + scan = false; + script = 0; + + scaleAnimation = false; + direction = false; + + sequence = -1; +} + +static AITurretShapeData::StateData gDefaultStateData; + +//---------------------------------------------------------------------------- + +IMPLEMENT_CO_DATABLOCK_V1(AITurretShapeData); + +ConsoleDocClass( AITurretShapeData, + "@brief Defines properties for an AITurretShape object.\n\n" + "@see AITurretShape\n" + "@see TurretShapeData\n" + "@ingroup gameObjects\n" +); + +AITurretShapeData::AITurretShapeData() +{ + maxScanHeading = 90; + maxScanPitch = 90; + maxScanDistance = 20; + + // Do a full scan every three ticks + scanTickFrequency = 3; + + // Randomly add 0 to 1 ticks to the scan frequency as + // chosen every scan period. + scanTickFrequencyVariance = 1; + + trackLostTargetTime = 0; + + scanNode = -1; + aimNode = -1; + + maxWeaponRange = 100; + + weaponLeadVelocity = 0; + + for (int i = 0; i < MaxStates; i++) { + stateName[i] = 0; + stateTransitionAtRest[i] = 0; + stateTransitionNotAtRest[i] = 0; + stateTransitionTarget[i] = 0; + stateTransitionNoTarget[i] = 0; + stateTransitionActivated[i] = 0; + stateTransitionDeactivated[i] = 0; + stateTransitionTimeout[i] = 0; + stateWaitForTimeout[i] = true; + stateTimeoutValue[i] = 0; + stateFire[i] = false; + stateScan[i] = false; + stateScaleAnimation[i] = true; + stateDirection[i] = true; + stateSequence[i] = 0; + stateScript[i] = 0; + } + isAnimated = false; + statesLoaded = false; +} + +void AITurretShapeData::initPersistFields() +{ + addField("maxScanHeading", TypeF32, Offset(maxScanHeading, AITurretShapeData), + "@brief Maximum number of degrees to scan left and right.\n\n" + "@note Maximum scan heading is 90 degrees.\n"); + addField("maxScanPitch", TypeF32, Offset(maxScanPitch, AITurretShapeData), + "@brief Maximum number of degrees to scan up and down.\n\n" + "@note Maximum scan pitch is 90 degrees.\n"); + addField("maxScanDistance", TypeF32, Offset(maxScanDistance, AITurretShapeData), + "@brief Maximum distance to scan.\n\n" + "When combined with maxScanHeading and maxScanPitch this forms a 3D scanning wedge used to initially " + "locate a target.\n"); + + addField("scanTickFrequency", TypeS32, Offset(scanTickFrequency, AITurretShapeData), + "@brief How often should we perform a full scan when looking for a target.\n\n" + "Expressed as the number of ticks between full scans, but no less than 1.\n"); + addField("scanTickFrequencyVariance", TypeS32, Offset(scanTickFrequencyVariance, AITurretShapeData), + "@brief Random amount that should be added to the scan tick frequency each scan period.\n\n" + "Expressed as the number of ticks to randomly add, but no less than zero.\n"); + + addField("trackLostTargetTime", TypeF32, Offset(trackLostTargetTime, AITurretShapeData), + "@brief How long after the turret has lost the target should it still track it.\n\n" + "Expressed in seconds.\n"); + + addField("maxWeaponRange", TypeF32, Offset(maxWeaponRange, AITurretShapeData), + "@brief Maximum distance that the weapon will fire upon a target.\n\n"); + + addField("weaponLeadVelocity", TypeF32, Offset(weaponLeadVelocity, AITurretShapeData), + "@brief Velocity used to lead target.\n\n" + "If value <= 0, don't lead target.\n"); + + // State arrays + addArray( "States", MaxStates ); + + addField( "stateName", TypeCaseString, Offset(stateName, AITurretShapeData), MaxStates, + "Name of this state." ); + addField( "stateTransitionOnAtRest", TypeString, Offset(stateTransitionAtRest, AITurretShapeData), MaxStates, + "Name of the state to transition to when the turret is at rest (static)."); + addField( "stateTransitionOnNotAtRest", TypeString, Offset(stateTransitionNotAtRest, AITurretShapeData), MaxStates, + "Name of the state to transition to when the turret is not at rest (not static)."); + addField( "stateTransitionOnTarget", TypeString, Offset(stateTransitionTarget, AITurretShapeData), MaxStates, + "Name of the state to transition to when the turret gains a target." ); + addField( "stateTransitionOnNoTarget", TypeString, Offset(stateTransitionNoTarget, AITurretShapeData), MaxStates, + "Name of the state to transition to when the turret loses a target." ); + addField( "stateTransitionOnActivated", TypeString, Offset(stateTransitionActivated, AITurretShapeData), MaxStates, + "Name of the state to transition to when the turret goes from deactivated to activated."); + addField( "stateTransitionOnDeactivated", TypeString, Offset(stateTransitionDeactivated, AITurretShapeData), MaxStates, + "Name of the state to transition to when the turret goes from activated to deactivated"); + addField( "stateTransitionOnTimeout", TypeString, Offset(stateTransitionTimeout, AITurretShapeData), MaxStates, + "Name of the state to transition to when we have been in this state " + "for stateTimeoutValue seconds." ); + addField( "stateTimeoutValue", TypeF32, Offset(stateTimeoutValue, AITurretShapeData), MaxStates, + "Time in seconds to wait before transitioning to stateTransitionOnTimeout." ); + addField( "stateWaitForTimeout", TypeBool, Offset(stateWaitForTimeout, AITurretShapeData), MaxStates, + "If false, this state ignores stateTimeoutValue and transitions " + "immediately if other transition conditions are met." ); + addField( "stateFire", TypeBool, Offset(stateFire, AITurretShapeData), MaxStates, + "The first state with this set to true is the state entered by the " + "client when it receives the 'fire' event." ); + addField( "stateScan", TypeBool, Offset(stateScan, AITurretShapeData), MaxStates, + "Indicates the turret should perform a continuous scan looking for targets." ); + addField( "stateDirection", TypeBool, Offset(stateDirection, AITurretShapeData), MaxStates, + "@brief Direction of the animation to play in this state.\n\n" + "True is forward, false is backward." ); + addField( "stateSequence", TypeString, Offset(stateSequence, AITurretShapeData), MaxStates, + "Name of the sequence to play on entry to this state." ); + addField( "stateScaleAnimation", TypeBool, Offset(stateScaleAnimation, AITurretShapeData), MaxStates, + "If true, the timeScale of the stateSequence animation will be adjusted " + "such that the sequence plays for stateTimeoutValue seconds. " ); + addField( "stateScript", TypeCaseString, Offset(stateScript, AITurretShapeData), MaxStates, + "@brief Method to execute on entering this state.\n\n" + "Scoped to AITurretShapeData."); + + endArray( "States" ); + + Parent::initPersistFields(); +} + +bool AITurretShapeData::onAdd() +{ + if (!Parent::onAdd()) + return false; + + // Copy state data from the scripting arrays into the + // state structure array. If we have state data already, + // we are on the client and need to leave it alone. + for (U32 i = 0; i < MaxStates; i++) { + StateData& s = state[i]; + if (statesLoaded == false) { + s.name = stateName[i]; + s.transition.rest[0] = lookupState(stateTransitionNotAtRest[i]); + s.transition.rest[1] = lookupState(stateTransitionAtRest[i]); + s.transition.target[0] = lookupState(stateTransitionNoTarget[i]); + s.transition.target[1] = lookupState(stateTransitionTarget[i]); + s.transition.activated[0] = lookupState(stateTransitionDeactivated[i]); + s.transition.activated[1] = lookupState(stateTransitionActivated[i]); + s.transition.timeout = lookupState(stateTransitionTimeout[i]); + s.waitForTimeout = stateWaitForTimeout[i]; + s.timeoutValue = stateTimeoutValue[i]; + s.fire = stateFire[i]; + s.scan = stateScan[i]; + s.scaleAnimation = stateScaleAnimation[i]; + s.direction = stateDirection[i]; + s.script = stateScript[i]; + + // Resolved at load time + s.sequence = -1; + } + + // The first state marked as "fire" is the state entered on the + // client when it recieves a fire event. + if (s.fire && fireState == -1) + fireState = i; + } + + // Always preload images, this is needed to avoid problems with + // resolving sequences before transmission to a client. + return true; +} + +bool AITurretShapeData::preload(bool server, String &errorStr) +{ + if (!Parent::preload(server, errorStr)) + return false; + + // We have mShape at this point. Resolve nodes. + scanNode = mShape->findNode("scanPoint"); + aimNode = mShape->findNode("aimPoint"); + if (aimNode == -1) + { + aimNode = pitchNode; + } + if (aimNode == -1) + { + aimNode = headingNode; + } + + // Resolve state sequence names & emitter nodes + isAnimated = false; + for (U32 j = 0; j < MaxStates; j++) { + StateData& s = state[j]; + if (stateSequence[j] && stateSequence[j][0]) + s.sequence = mShape->findSequence(stateSequence[j]); + if (s.sequence != -1) + { + // This state has an animation sequence + isAnimated = true; + } + } + + return true; +} + +void AITurretShapeData::packData(BitStream* stream) +{ + Parent::packData(stream); + + stream->write(maxScanHeading); + stream->write(maxScanPitch); + stream->write(maxScanDistance); + stream->write(maxWeaponRange); + stream->write(weaponLeadVelocity); + + for (U32 i = 0; i < MaxStates; i++) + if (stream->writeFlag(state[i].name && state[i].name[0])) { + StateData& s = state[i]; + // States info not needed on the client: + // s.scriptNames + // Transitions are inc. one to account for -1 values + stream->writeString(state[i].name); + + stream->writeInt(s.transition.rest[0]+1,NumStateBits); + stream->writeInt(s.transition.rest[1]+1,NumStateBits); + stream->writeInt(s.transition.target[0]+1,NumStateBits); + stream->writeInt(s.transition.target[1]+1,NumStateBits); + stream->writeInt(s.transition.activated[0]+1,NumStateBits); + stream->writeInt(s.transition.activated[1]+1,NumStateBits); + stream->writeInt(s.transition.timeout+1,NumStateBits); + + if(stream->writeFlag(s.timeoutValue != gDefaultStateData.timeoutValue)) + stream->write(s.timeoutValue); + + stream->writeFlag(s.waitForTimeout); + stream->writeFlag(s.fire); + stream->writeFlag(s.scan); + stream->writeFlag(s.scaleAnimation); + stream->writeFlag(s.direction); + + if(stream->writeFlag(s.sequence != gDefaultStateData.sequence)) + stream->writeSignedInt(s.sequence, 16); + } +} + +void AITurretShapeData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + + stream->read(&maxScanHeading); + stream->read(&maxScanPitch); + stream->read(&maxScanDistance); + stream->read(&maxWeaponRange); + stream->read(&weaponLeadVelocity); + + for (U32 i = 0; i < MaxStates; i++) { + if (stream->readFlag()) { + StateData& s = state[i]; + // States info not needed on the client: + // s.scriptNames + // Transitions are dec. one to restore -1 values + s.name = stream->readSTString(); + + s.transition.rest[0] = stream->readInt(NumStateBits) - 1; + s.transition.rest[1] = stream->readInt(NumStateBits) - 1; + s.transition.target[0] = stream->readInt(NumStateBits) - 1; + s.transition.target[1] = stream->readInt(NumStateBits) - 1; + s.transition.activated[0] = stream->readInt(NumStateBits) - 1; + s.transition.activated[1] = stream->readInt(NumStateBits) - 1; + s.transition.timeout = stream->readInt(NumStateBits) - 1; + if(stream->readFlag()) + stream->read(&s.timeoutValue); + else + s.timeoutValue = gDefaultStateData.timeoutValue; + + s.waitForTimeout = stream->readFlag(); + s.fire = stream->readFlag(); + s.scan = stream->readFlag(); + s.scaleAnimation = stream->readFlag(); + s.direction = stream->readFlag(); + + if(stream->readFlag()) + s.sequence = stream->readSignedInt(16); + } + } + + statesLoaded = true; +} + +S32 AITurretShapeData::lookupState(const char* name) +{ + if (!name || !name[0]) + return -1; + for (U32 i = 0; i < MaxStates; i++) + if (stateName[i] && !dStricmp(name,stateName[i])) + return i; + Con::errorf(ConsoleLogEntry::General,"AITurretShapeData:: Could not resolve state \"%s\" for image \"%s\"",name,getName()); + return 0; +} + + +//---------------------------------------------------------------------------- + +// Used to build potential target list +static void _scanCallback( SceneObject* object, void* data ) +{ + AITurretShape* turret = (AITurretShape*)data; + + ShapeBase* shape = dynamic_cast(object); + if (shape && shape->getDamageState() == ShapeBase::Enabled) + { + Point3F targetPos = shape->getBoxCenter(); + + // Put target position into the scan node's space + turret->mScanWorkspaceScanWorldMat.mulP(targetPos); + + // Is the target within scanning distance + if (targetPos.lenSquared() > turret->getMaxScanDistanceSquared()) + return; + + // Make sure the target is in front and within the maximum + // heading range + Point2F targetXY(targetPos.x, targetPos.y); + targetXY.normalizeSafe(); + F32 headingDot = mDot(Point2F(0, 1), targetXY); + F32 heading = mAcos(headingDot); + if (headingDot < 0 || heading > turret->getMaxScanHeading()) + return; + + // Make sure the target is in front and within the maximum + // pitch range + Point2F targetZY(targetPos.z, targetPos.y); + targetZY.normalizeSafe(); + F32 pitchDot = mDot(Point2F(0, 1), targetZY); + F32 pitch = mAcos(pitchDot); + if (pitchDot < 0 || pitch > turret->getMaxScanPitch()) + return; + + turret->addPotentialTarget(shape); + } +} + +// Used to sort potential target list based on distance +static Point3F comparePoint; +static S32 QSORT_CALLBACK _sortCallback(const void* a,const void* b) +{ + const ShapeBase* s1 = (*reinterpret_cast(a)); + const ShapeBase* s2 = (*reinterpret_cast(b)); + + F32 s1Len = (s1->getPosition() - comparePoint).lenSquared(); + F32 s2Len = (s2->getPosition() - comparePoint).lenSquared(); + + return s1Len - s2Len; +} + +IMPLEMENT_CO_NETOBJECT_V1(AITurretShape); + +ConsoleDocClass( AITurretShape, + "@ingroup gameObjects" +); + +AITurretShape::AITurretShape() +{ + mTypeMask |= VehicleObjectType | DynamicShapeObjectType; + mDataBlock = 0; + + mScanHeading = 0; + mScanPitch = 0; + mScanDistance = 0; + mScanDistanceSquared = 0; + mScanBox = Box3F::Zero; + + mScanTickFrequency = 1; + mScanTickFrequencyVariance = 0; + mTicksToNextScan = mScanTickFrequency; + + mWeaponRangeSquared = 0; + mWeaponLeadVelocitySquared = 0; + + mScanForTargets = false; + mTrackTarget = false; + + mState = NULL; + mStateDelayTime = 0; + mStateActive = true; + mStateAnimThread = NULL; + + // For the TurretShape class + mSubclassTurretShapeHandlesScene = true; +} + +AITurretShape::~AITurretShape() +{ + _cleanupPotentialTargets(); +} + +//---------------------------------------------------------------------------- + +void AITurretShape::initPersistFields() +{ + Parent::initPersistFields(); +} + +bool AITurretShape::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + // Add this object to the scene + addToScene(); + + _setScanBox(); + + if (isServerObject()) + _initState(); + + if (isServerObject()) + scriptOnAdd(); + + return true; +} + +void AITurretShape::onRemove() +{ + Parent::onRemove(); + + scriptOnRemove(); + + mIgnoreObjects.clear(); + + // Remove this object from the scene + removeFromScene(); +} + +bool AITurretShape::onNewDataBlock(GameBaseData* dptr, bool reload) +{ + mDataBlock = dynamic_cast(dptr); + if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload)) + return false; + + mScanHeading = mDegToRad(mDataBlock->maxScanHeading); + if (mIsZero(mScanHeading)) + mScanHeading = M_PI_F; + mScanPitch = mDegToRad(mDataBlock->maxScanPitch); + if (mIsZero(mScanPitch)) + mScanPitch = M_PI_F; + + mScanDistance = mDataBlock->maxScanDistance; + mScanDistanceSquared = mScanDistance * mScanDistance; + + mWeaponRangeSquared = mDataBlock->maxWeaponRange * mDataBlock->maxWeaponRange; + mWeaponLeadVelocitySquared = (mDataBlock->weaponLeadVelocity > 0) ? (mDataBlock->weaponLeadVelocity * mDataBlock->weaponLeadVelocity) : 0; + + // The scan box is built such that the scanning origin is at (0,0,0) and the scanning distance + // is out along the y axis. When this is transformed to the turret's location is provides a + // scanning volume in front of the turret. + F32 scanX = mScanDistance*mSin(mScanHeading); + F32 scanY = mScanDistance; + F32 scanZ = mScanDistance*mSin(mScanPitch); + mScanBox.set(-scanX, 0, -scanZ, scanX, scanY, scanZ); + + mScanTickFrequency = mDataBlock->scanTickFrequency; + if (mScanTickFrequency < 1) + mScanTickFrequency = 1; + + mScanTickFrequencyVariance = mDataBlock->scanTickFrequencyVariance; + if (mScanTickFrequencyVariance < 0) + mScanTickFrequencyVariance = 0; + + mTicksToNextScan = mScanTickFrequency; + + // For states + mStateAnimThread = 0; + if (mDataBlock->isAnimated) + { + mStateAnimThread = mShapeInstance->addThread(); + mShapeInstance->setTimeScale(mStateAnimThread,0); + } + + scriptOnNewDataBlock(); + return true; +} + +//---------------------------------------------------------------------------- + +void AITurretShape::addToIgnoreList(ShapeBase* obj) +{ + mIgnoreObjects.addObject(obj); +} + +void AITurretShape::removeFromIgnoreList(ShapeBase* obj) +{ + mIgnoreObjects.removeObject(obj); +} + +//---------------------------------------------------------------------------- + +void AITurretShape::_initState() +{ + // Set the turret to its starting state. + setTurretState(0, true); +} + +void AITurretShape::setTurretStateName(const char* newState, bool force) +{ + S32 stateVal = mDataBlock->lookupState(newState); + if (stateVal >= 0) + { + setTurretState(stateVal, force); + } +} + +void AITurretShape::setTurretState(U32 newState, bool force) +{ + setMaskBits(TurretStateMask); + + // If going back into the same state, just reset the timer + // and invoke the script callback + if (!force && mState == &mDataBlock->state[newState]) + { + mStateDelayTime = mState->timeoutValue; + if (mState->script && !isGhost()) + _scriptCallback(mState->script); + + return; + } + + mState = &mDataBlock->state[newState]; + + // Reset cyclic sequences back to the first frame to turn it off + // (the first key frame should be it's off state). + if (mStateAnimThread && mStateAnimThread->getSequence()->isCyclic()) + { + mShapeInstance->setPos(mStateAnimThread,0); + mShapeInstance->setTimeScale(mStateAnimThread,0); + } + + AITurretShapeData::StateData& stateData = *mState; + + // Check for immediate transitions + S32 ns; + if ((ns = stateData.transition.rest[mAtRest]) != -1) + { + setTurretState(ns); + return; + } + if ((ns = stateData.transition.target[mTarget.isValid()]) != -1) + { + setTurretState(ns); + return; + } + if ((ns = stateData.transition.activated[mStateActive]) != -1) + { + setTurretState(ns); + return; + } + + // + // Initialize the new state... + // + mStateDelayTime = stateData.timeoutValue; + + // Play animation + if (mStateAnimThread && stateData.sequence != -1) + { + mShapeInstance->setSequence(mStateAnimThread,stateData.sequence, stateData.direction ? 0.0f : 1.0f); + F32 timeScale = (stateData.scaleAnimation && stateData.timeoutValue) ? + mShapeInstance->getDuration(mStateAnimThread) / stateData.timeoutValue : 1.0f; + mShapeInstance->setTimeScale(mStateAnimThread, stateData.direction ? timeScale : -timeScale); + } + + // Script callback on server + if (stateData.script && stateData.script[0] && !isGhost()) + _scriptCallback(stateData.script); + + // If there is a zero timeout, and a timeout transition, then + // go ahead and transition imediately. + if (!mStateDelayTime) + { + if ((ns = stateData.transition.timeout) != -1) + { + setTurretState(ns); + return; + } + } +} + +void AITurretShape::_updateTurretState(F32 dt) +{ + AITurretShapeData::StateData& stateData = *mState; + + mStateDelayTime -= dt; + + // Check for transitions. On some states we must wait for the + // full timeout value before moving on. + if (mStateDelayTime <= 0 || !stateData.waitForTimeout) + { + S32 ns; + + if ((ns = stateData.transition.rest[mAtRest]) != -1) + setTurretState(ns); + else if ((ns = stateData.transition.target[mTarget.isValid()]) != -1) + setTurretState(ns); + else if ((ns = stateData.transition.activated[mStateActive]) != -1) + setTurretState(ns); + else if (mStateDelayTime <= 0 && (ns = stateData.transition.timeout) != -1) + setTurretState(ns); + } +} + +void AITurretShape::_scriptCallback(const char* function) +{ + Con::executef( mDataBlock, function, getIdString() ); +} + +//---------------------------------------------------------------------------- + +void AITurretShape::_cleanupPotentialTargets() +{ + mPotentialTargets.clear(); +} + +void AITurretShape::_performScan() +{ + // Only on server + if (isClientObject()) + return; + + // Are we ready for a scan? + --mTicksToNextScan; + if (mTicksToNextScan > 0) + return; + + _cleanupPotentialTargets(); + + _setScanBox(); + + // Set up for the scan + getScanTransform(mScanWorkspaceScanMat); + mScanWorkspaceScanWorldMat = mScanWorkspaceScanMat; + mScanWorkspaceScanWorldMat.affineInverse(); + + disableCollision(); + for ( SimSetIterator iter(&mIgnoreObjects); *iter; ++iter ) + { + ShapeBase* obj = static_cast( *iter ); + obj->disableCollision(); + } + + gServerContainer.findObjects( mTransformedScanBox, sScanTypeMask, _scanCallback, (void*)this ); + + for ( SimSetIterator iter(&mIgnoreObjects); *iter; ++iter ) + { + ShapeBase* obj = static_cast( *iter ); + obj->enableCollision(); + } + enableCollision(); + + if (mPotentialTargets.size() == 0) + { + // No targets in range. Clear out our current target, if necessary. + _lostTarget(); + } + else + { + // Sort the targets + comparePoint = getPosition(); + dQsort(mPotentialTargets.address(),mPotentialTargets.size(),sizeof(SimObjectList::value_type),_sortCallback); + + // Go through the targets in order to find one that is not blocked from view + Point3F start; + mScanWorkspaceScanMat.getColumn(3, &start); + S32 index = 0; + bool los = false; + + disableCollision(); + for (index=0; index < mPotentialTargets.size(); ++index) + { + ShapeBase* shape = (ShapeBase*)mPotentialTargets[index]; + + Point3F sightPoint; + los = _testTargetLineOfSight(start, shape, sightPoint); + + // Check if we have a clear line of sight + if (los) + break; + } + enableCollision(); + + // If we found a valid, visible target (no hits between here and there), latch on to it + if (los) + { + _gainedTarget((ShapeBase*)mPotentialTargets[index]); + } + } + + // Prepare for next scan period + mTicksToNextScan = mScanTickFrequency; + if (mScanTickFrequencyVariance > 0) + { + mTicksToNextScan += gRandGen.randI(0, mScanTickFrequencyVariance); + } +} + +void AITurretShape::addPotentialTarget(ShapeBase* shape) +{ + mPotentialTargets.push_back(shape); +} + +void AITurretShape::_lostTarget() +{ + mTarget.target = NULL; +} + +void AITurretShape::_gainedTarget(ShapeBase* target) +{ + mTarget.target = target; + if (target) + { + mTarget.hadValidTarget = true; + } +} + +void AITurretShape::_trackTarget(F32 dt) +{ + // Only on server + if (isClientObject()) + return; + + // We can only track a target if we have one + if (!mTarget.isValid()) + return; + + Point3F targetPos = mTarget.target->getBoxCenter(); + + // Can we see the target? + MatrixF aimMat; + getAimTransform(aimMat); + Point3F start; + aimMat.getColumn(3, &start); + RayInfo ri; + + Point3F sightPoint; + + disableCollision(); + bool los = _testTargetLineOfSight(start, mTarget.target, sightPoint); + enableCollision(); + + if (!los) + { + // Target is blocked. Should we try to track from its last + // known position and velocity? + SimTime curTime = Sim::getCurrentTime(); + if ( (curTime - mTarget.lastSightTime) > (mDataBlock->trackLostTargetTime * 1000.0f) ) + { + // Time's up. Stop tracking. + _cleanupTargetAndTurret(); + return; + } + + // Use last known information to attempt to + // continue to track target for a while. + targetPos = mTarget.lastPos + mTarget.lastVel * F32(curTime - mTarget.lastSightTime) / 1000.0f; + } + else + { + // Target is visible + + // We only track targets that are alive + if (mTarget.target->getDamageState() != Enabled) + { + // We can't track any more + _cleanupTargetAndTurret(); + return; + } + + targetPos = sightPoint; + + // Store latest target info + mTarget.lastPos = targetPos; + mTarget.lastVel = mTarget.target->getVelocity(); + mTarget.lastSightTime = Sim::getCurrentTime(); + } + + // Calculate angles to face the target, specifically the part that we can see + VectorF toTarget; + MatrixF mat; + S32 node = mDataBlock->aimNode; + if (node != -1) + { + // Get the current position of our node + MatrixF* nodeTrans = &mShapeInstance->mNodeTransforms[node]; + Point3F currentPos; + nodeTrans->getColumn(3, ¤tPos); + + // Turn this into a matrix we can use to put the target + // position into our space. + MatrixF nodeMat(true); + nodeMat.setColumn(3, currentPos); + mat.mul(mObjToWorld, nodeMat); + mat.affineInverse(); + } + else + { + mat = mWorldToObj; + } + mat.mulP(targetPos, &toTarget); + + // lead the target + F32 timeToTargetSquared = (mWeaponLeadVelocitySquared > 0) ? toTarget.lenSquared() / mWeaponLeadVelocitySquared : 0; + if (timeToTargetSquared > 1.0) + { + targetPos = targetPos + (mTarget.lastVel * mSqrt(timeToTargetSquared)); + mat.mulP(targetPos, &toTarget); + } + + F32 yaw, pitch; + MathUtils::getAnglesFromVector(toTarget, yaw, pitch); + if (yaw > M_PI_F) + yaw = yaw - M_2PI_F; + //if (pitch > M_PI_F) + // pitch = -(pitch - M_2PI_F); + + Point3F rot(-pitch, 0.0f, yaw); + + // If we have a rotation rate make sure we follow it + if (mHeadingRate > 0) + { + F32 rate = mHeadingRate * dt; + F32 rateCheck = mFabs(rot.z - mRot.z); + if (rateCheck > rate) + { + // This will clamp the new value to the rate regardless if it + // is increasing or decreasing. + rot.z = mClampF(rot.z, mRot.z-rate, mRot.z+rate); + } + } + if (mPitchRate > 0) + { + F32 rate = mPitchRate * dt; + F32 rateCheck = mFabs(rot.x - mRot.x); + if (rateCheck > rate) + { + // This will clamp the new value to the rate regardless if it + // is increasing or decreasing. + rot.x = mClampF(rot.x, mRot.x-rate, mRot.x+rate); + } + } + + // Test if the rotation to the target is outside of our limits + if (_outsideLimits(rot)) + { + // We can't track any more + _cleanupTargetAndTurret(); + return; + } + + // Test if the target is out of weapons range + if (toTarget.lenSquared() > mWeaponRangeSquared) + { + // We can't track any more + _cleanupTargetAndTurret(); + return; + } + + mRot = rot; + + _setRotation( mRot ); + setMaskBits(TurretUpdateMask); +} + +void AITurretShape::_cleanupTargetAndTurret() +{ + _lostTarget(); + resetTarget(); +} + +bool AITurretShape::_testTargetLineOfSight(Point3F& aimPoint, ShapeBase* target, Point3F& sightPoint) +{ + Point3F targetCenter = target->getBoxCenter(); + RayInfo ri; + bool hit = false; + + target->disableCollision(); + + // First check for a clear line of sight to the target's center + Point3F testPoint = targetCenter; + hit = gServerContainer.castRay(aimPoint, testPoint, sAimTypeMask, &ri); + if (hit) + { + // No clear line of sight to center, so try to the target's right. Players holding + // a gun in their right hand will tend to stick their right shoulder out first if + // they're peering around some cover to shoot, like a wall. + Box3F targetBounds = target->getObjBox(); + F32 radius = targetBounds.len_x() > targetBounds.len_y() ? targetBounds.len_x() : targetBounds.len_y(); + radius *= 0.5; + + VectorF toTurret = aimPoint - targetCenter; + toTurret.normalizeSafe(); + VectorF toTurretRight = mCross(toTurret, Point3F::UnitZ); + + testPoint = targetCenter + toTurretRight * radius; + + hit = gServerContainer.castRay(aimPoint, testPoint, sAimTypeMask, &ri); + + if (hit) + { + // No clear line of sight to right, so try the target's left + VectorF toTurretLeft = toTurretRight * -1.0f; + testPoint = targetCenter + toTurretLeft * radius; + hit = gServerContainer.castRay(aimPoint, testPoint, sAimTypeMask, &ri); + } + + if (hit) + { + // No clear line of sight to left, so try the target's top + testPoint = targetCenter; + testPoint.z += targetBounds.len_z() * 0.5f; + hit = gServerContainer.castRay(aimPoint, testPoint, sAimTypeMask, &ri); + } + + if (hit) + { + // No clear line of sight to top, so try the target's bottom + testPoint = targetCenter; + testPoint.z -= targetBounds.len_z() * 0.5f; + hit = gServerContainer.castRay(aimPoint, testPoint, sAimTypeMask, &ri); + } + } + + target->enableCollision(); + + if (!hit) + { + // Line of sight point is that last one we tested + sightPoint = testPoint; + } + + return !hit; +} + +//---------------------------------------------------------------------------- + +void AITurretShape::setAllGunsFiring(bool fire) +{ + setImageTriggerState(0,fire); + setImageTriggerState(1,fire); + setImageTriggerState(2,fire); + setImageTriggerState(3,fire); +} + +void AITurretShape::setGunSlotFiring(S32 slot, bool fire) +{ + if (slot < 0 || slot > 3) + return; + + setImageTriggerState(slot, fire); +} + +//---------------------------------------------------------------------------- + +void AITurretShape::setTransform(const MatrixF& mat) +{ + Parent::setTransform(mat); + + // Set the scanning box + _setScanBox(); +} + +void AITurretShape::recenterTurret() +{ + mRot.set(0,0,0); + _setRotation( mRot ); + setMaskBits(TurretUpdateMask); +} + +void AITurretShape::_setScanBox() +{ + mTransformedScanBox = mScanBox; + MatrixF mat; + getScanTransform(mat); + mat.mul(mTransformedScanBox); +} + +void AITurretShape::getScanTransform(MatrixF& mat) +{ + if (mDataBlock && mDataBlock->scanNode != -1) + { + if (getNodeTransform(mDataBlock->scanNode, mat)) + { + return; + } + } + + mat = mObjToWorld; +} + +void AITurretShape::getAimTransform(MatrixF& mat) +{ + if (mDataBlock && mDataBlock->aimNode != -1) + { + if (getNodeTransform(mDataBlock->aimNode, mat)) + { + return; + } + } + + mat = mObjToWorld; +} + +//---------------------------------------------------------------------------- + +void AITurretShape::processTick(const Move* move) +{ + Parent::processTick(move); + + if (isServerObject() && mDamageState == Enabled) + { + _updateTurretState(TickSec); + + if (mScanForTargets) + { + // Perform a scan for targets + _performScan(); + + // If we found one, turn off the scan + if (mTarget.isValid()) + { + mScanForTargets = false; + } + } + + if (mTrackTarget) + { + _trackTarget(TickSec); + + // If the target is lost, no longer track it + if (!mTarget.isValid()) + { + mTrackTarget = false; + } + } + } +} + +void AITurretShape::advanceTime(F32 dt) +{ + // If there were any ShapeBase script threads that + // have played, then we need to update all code + // controlled nodes. This is done before the Parent + // call as script threads may play and be destroyed + // before our code is called. + bool updateNodes = false; + for (U32 i = 0; i < MaxScriptThreads; i++) + { + Thread& st = mScriptThread[i]; + if (st.thread) + { + updateNodes = true; + break; + } + } + + Parent::advanceTime(dt); + + // Update any state thread + AITurretShapeData::StateData& stateData = *mState; + if (mStateAnimThread && stateData.sequence != -1) + { + mShapeInstance->advanceTime(dt,mStateAnimThread); + + updateNodes = true; + } + + if (updateNodes) + { + // Update all code controlled nodes + _updateNodes(mRot); + } +} + +//---------------------------------------------------------------------------- + +U32 AITurretShape::packUpdate(NetConnection *connection, U32 mask, BitStream *bstream) +{ + U32 retMask = Parent::packUpdate(connection,mask,bstream); + + // Indicate that the transform has changed to update the scan box + bstream->writeFlag(mask & PositionMask | ExtendedInfoMask); + + // Handle any state changes that need to be passed along + if (bstream->writeFlag(mask & TurretStateMask)) + { + bstream->write(mDataBlock->lookupState(mState->name)); + } + + return retMask; +} + +void AITurretShape::unpackUpdate(NetConnection *connection, BitStream *bstream) +{ + Parent::unpackUpdate(connection,bstream); + + // Transform has changed + if (bstream->readFlag()) + { + _setScanBox(); + } + + //TurretStateMask + if (bstream->readFlag()) + { + S32 newstate; + bstream->read( &newstate ); + setTurretState(newstate); + } +} + +//---------------------------------------------------------------------------- + +void AITurretShape::prepBatchRender( SceneRenderState *state, S32 mountedImageIndex ) +{ + Parent::prepBatchRender( state, mountedImageIndex ); + + if ( !gShowBoundingBox ) + return; + + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &AITurretShape::_renderScanner ); + ri->type = RenderPassManager::RIT_Editor; + state->getRenderPass()->addInst( ri ); +} + +void AITurretShape::_renderScanner( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ) +{ + GFXStateBlockDesc desc; + desc.setBlend(false, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha); + desc.setZReadWrite(false,true); + desc.fillMode = GFXFillWireframe; + + // Render the scan box + GFX->getDrawUtil()->drawCube(desc, mTransformedScanBox.getExtents(), mTransformedScanBox.getCenter(), ColorI(255, 0, 0)); + + // Render a line from the scan node to the max scan distance + MatrixF nodeMat; + if (getNodeTransform(mDataBlock->scanNode, nodeMat)) + { + Point3F start; + nodeMat.getColumn(3, &start); + + Point3F end(0.0f, mScanDistance, 0.0f); + nodeMat.mulP(end); + + GFX->getDrawUtil()->drawLine(start, end, ColorI(255, 255, 0)); + } +} + +//---------------------------------------------------------------------------- + +DefineEngineMethod( AITurretShape, addToIgnoreList, void, (ShapeBase* obj),, + "@brief Adds object to the turret's ignore list.\n\n" + "All objects in this list will be ignored by the turret's targeting.\n" + "@param obj The ShapeBase object to ignore.\n") +{ + object->addToIgnoreList(obj); +} + +DefineEngineMethod( AITurretShape, removeFromIgnoreList, void, (ShapeBase* obj),, + "@brief Removes object from the turret's ignore list.\n\n" + "All objects in this list will be ignored by the turret's targeting.\n" + "@param obj The ShapeBase object to once again allow for targeting.\n") +{ + object->removeFromIgnoreList(obj); +} + +DefineEngineMethod( AITurretShape, setTurretState, void, (const char* newState, bool force), (false), + "@brief Set the turret's current state.\n\n" + "Normally the turret's state comes from updating the state machine but this method " + "allows you to override this and jump to the requested state immediately.\n" + "@param newState The name of the new state.\n" + "@param force Is true then force the full processing of the new state even if it is the " + "same as the current state. If false then only the time out value is reset and the state's " + "script method is called, if any.\n") +{ + object->setTurretStateName(newState, force); +} + +DefineEngineMethod( AITurretShape, activateTurret, void, ( ),, + "@brief Activate a turret from a deactive state.\n\n") +{ + object->activateTurret(); +} + +DefineEngineMethod( AITurretShape, deactivateTurret, void, ( ),, + "@brief Deactivate a turret from an active state.\n\n") +{ + object->deactivateTurret(); +} + +DefineEngineMethod( AITurretShape, startScanForTargets, void, ( ),, + "@brief Begin scanning for a target.\n\n") +{ + object->startScanForTargets(); +} + +DefineEngineMethod( AITurretShape, stopScanForTargets, void, ( ),, + "@brief Stop scanning for targets.\n\n" + "@note Only impacts the scanning for new targets. Does not effect a turret's current " + "target lock.\n") +{ + object->stopScanForTargets(); +} + +DefineEngineMethod( AITurretShape, startTrackingTarget, void, ( ),, + "@brief Have the turret track the current target.\n\n") +{ + object->startTrackingTarget(); +} + +DefineEngineMethod( AITurretShape, stopTrackingTarget, void, ( ),, + "@brief Stop the turret from tracking the current target.\n\n") +{ + object->stopTrackingTarget(); +} + +DefineEngineMethod( AITurretShape, hasTarget, bool, (),, + "@brief Indicates if the turret has a target.\n\n" + "@returns True if the turret has a target.\n") +{ + return object->hasTarget(); +} + +DefineEngineMethod( AITurretShape, getTarget, SimObject*, (),, + "@brief Get the turret's current target.\n\n" + "@returns The object that is the target's current target, or 0 if no target.\n") +{ + return object->getTarget(); +} + +DefineEngineMethod( AITurretShape, resetTarget, void, ( ),, + "@brief Resets the turret's target tracking.\n\n" + "Only resets the internal target tracking. Does not modify the turret's facing.\n") +{ + object->resetTarget(); +} + +DefineEngineMethod( AITurretShape, setWeaponLeadVelocity, void, (F32 velocity),, + "@brief Set the turret's projectile velocity to help lead the target.\n\n" + "This value normally comes from AITurretShapeData::weaponLeadVelocity but this method " + "allows you to override the datablock value. This can be useful if the turret changes " + "ammunition, uses a different weapon than the default, is damaged, etc.\n" + "@note Setting this to 0 will disable target leading.\n") +{ + object->setWeaponLeadVelocity(velocity); +} + +DefineEngineMethod( AITurretShape, getWeaponLeadVelocity, F32, (),, + "@brief Get the turret's defined projectile velocity that helps with target leading.\n\n" + "@returns The defined weapon projectile speed, or 0 if leading is disabled.\n") +{ + return object->getWeaponLeadVelocity(); +} + +DefineEngineMethod( AITurretShape, setAllGunsFiring, void, (bool fire),, + "@brief Set the firing state of the turret's guns.\n\n" + "@param fire Set to true to activate all guns. False to deactivate them.\n") +{ + object->setAllGunsFiring(fire); +} + +DefineEngineMethod( AITurretShape, setGunSlotFiring, void, (S32 slot, bool fire),, + "@brief Set the firing state of the given gun slot.\n\n" + "@param slot The gun to modify. Valid range is 0-3 that corresponds to the weapon mount point.\n" + "@param fire Set to true to activate the gun. False to deactivate it.\n") +{ + object->setGunSlotFiring(slot, fire); +} + +DefineEngineMethod( AITurretShape, recenterTurret, void, ( ),, + "@brief Recenter the turret's weapon.\n\n") +{ + object->recenterTurret(); +} diff --git a/Engine/source/T3D/turret/aiTurretShape.h b/Engine/source/T3D/turret/aiTurretShape.h new file mode 100644 index 000000000..18c0fb728 --- /dev/null +++ b/Engine/source/T3D/turret/aiTurretShape.h @@ -0,0 +1,307 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _AITURRETSHAPE_H_ +#define _AITURRETSHAPE_H_ + +#ifndef _TURRETSHAPE_H_ + #include "T3D/turret/turretShape.h" +#endif + + +//---------------------------------------------------------------------------- + +class AITurretShapeData: public TurretShapeData { + + typedef TurretShapeData Parent; + +public: + enum Constants { + MaxStates = 31, ///< We get one less than state bits because of + /// the way data is packed. + NumStateBits = 5, + }; + + struct StateData { + StateData(); + const char* name; ///< State name + + /// @name Transition states + /// + /// @{ + + /// + struct Transition { + S32 rest[2]; ///< NotAtRest/AtRest (NotStatic/Static) + S32 target[2]; ///< NoTarget/Target + S32 activated[2]; ///< Deactivated/Activated + S32 timeout; ///< Transition after delay + } transition; + + /// @} + + /// @name State attributes + /// @{ + + bool fire; ///< Can only have one fire state + bool scan; ///< Perform a continuous scan looking for targets + bool scaleAnimation; ///< Scale animation to fit the state timeout + bool direction; ///< Animation direction + bool waitForTimeout; ///< Require the timeout to pass before advancing to the next + /// state. + F32 timeoutValue; ///< A timeout value; the effect of this value is determined + /// by the flags scaleAnimation and waitForTimeout + S32 sequence; ///< Main thread sequence ID. + /// + /// + const char* script; ///< Function on datablock to call when we enter this state; passed the id of + /// the imageSlot. + + /// @} + }; + + /// @name State Data + /// Individual state data used to initialize struct array + /// @{ + const char* stateName [MaxStates]; + + const char* stateTransitionAtRest [MaxStates]; + const char* stateTransitionNotAtRest [MaxStates]; + const char* stateTransitionTarget [MaxStates]; + const char* stateTransitionNoTarget [MaxStates]; + const char* stateTransitionActivated [MaxStates]; + const char* stateTransitionDeactivated [MaxStates]; + const char* stateTransitionTimeout [MaxStates]; + F32 stateTimeoutValue [MaxStates]; + bool stateWaitForTimeout [MaxStates]; + + bool stateFire [MaxStates]; + bool stateScan [MaxStates]; + + bool stateScaleAnimation [MaxStates]; + bool stateDirection [MaxStates]; + const char* stateSequence [MaxStates]; + + const char* stateScript [MaxStates]; + + /// @} + + /// @name State Array + /// + /// State array is initialized onAdd from the individual state + /// struct array elements. + /// + /// @{ + StateData state[MaxStates]; ///< Array of states. + bool statesLoaded; ///< Are the states loaded yet? + S32 fireState; ///< The ID of the fire state. + bool isAnimated; ///< This image contains at least one animated states + /// @} + + F32 maxScanHeading; ///< Maximum heading angle from center to scan, in degrees + F32 maxScanPitch; ///< Maximum pitch angle from center to scan, in degrees + F32 maxScanDistance; ///< Maximum distance to scan to + + S32 scanTickFrequency; ///< How often should we perform a scan + S32 scanTickFrequencyVariance; ///< Random amount that should be added to the scan tick frequency + + F32 trackLostTargetTime; ///< How long after the turret has lost the target should it still track it (in seconds) + + S32 scanNode; ///< The node on the shape we will scan from + S32 aimNode; ///< The node on the shape we will aim from + + F32 maxWeaponRange; ///< Maximum range of the weapons, which may be different than the max scan distance + + F32 weaponLeadVelocity; ///< Velocity used to lead target (if value <= 0, don't lead target). + +public: + AITurretShapeData(); + + DECLARE_CONOBJECT(AITurretShapeData); + + static void initPersistFields(); + + virtual bool onAdd(); + virtual bool preload(bool server, String &errorStr); + + virtual void packData(BitStream* stream); + virtual void unpackData(BitStream* stream); + + S32 lookupState(const char* name); ///< Get a state by name. +}; + +//---------------------------------------------------------------------------- + +class AITurretShape: public TurretShape +{ + typedef TurretShape Parent; + +protected: + + enum MaskBits { + TurretStateMask = Parent::NextFreeMask, + NextFreeMask = Parent::NextFreeMask << 1 + }; + + struct TargetInfo + { + SimObjectPtr target; ///< Current target + Point3F lastPos; ///< The target's last known position + VectorF lastVel; ///< The target's last known velocity + SimTime lastSightTime; ///< The last time we saw the target + bool hadValidTarget; ///< Did we previously have a valid target? + + TargetInfo() {reset();} + + void reset() + { + target = NULL; + lastPos.zero(); + lastVel.zero(); + lastSightTime = 0; + hadValidTarget = false; + } + + // Check if we currently have a valid target + bool isValid() const {return target != NULL;} + + // Check if we used to have a target + bool hadTarget() const {return hadValidTarget;} + }; + + // Static attributes + AITurretShapeData* mDataBlock; + + F32 mScanHeading; + F32 mScanPitch; + F32 mScanDistance; + F32 mScanDistanceSquared; + Box3F mScanBox; + Box3F mTransformedScanBox; + + S32 mScanTickFrequency; + S32 mScanTickFrequencyVariance; + S32 mTicksToNextScan; + + F32 mWeaponRangeSquared; + F32 mWeaponLeadVelocitySquared; + + SimSet mIgnoreObjects; ///< Ignore these objects when targeting + + bool mScanForTargets; + bool mTrackTarget; + TargetInfo mTarget; ///< Information on the current target + + SimObjectList mPotentialTargets; + + AITurretShapeData::StateData *mState; + F32 mStateDelayTime; ///< Time till next state. + bool mStateActive; ///< Is the turret active? + TSThread *mStateAnimThread; + + void _initState(); + void _updateTurretState(F32 dt); + + /// Utility function to call state script functions on the datablock + /// @param function Function + void _scriptCallback(const char* function); + + void _setScanBox(); + + void _cleanupPotentialTargets(); + void _performScan(); + void _lostTarget(); + void _gainedTarget(ShapeBase* target); + void _trackTarget(F32 dt); + void _cleanupTargetAndTurret(); + bool _testTargetLineOfSight(Point3F& aimPoint, ShapeBase* target, Point3F& sightPoint); + + /// ObjectRenderInst delegate hooked up in prepBatchRender + /// if GameBase::gShowBoundingBox is true. + void _renderScanner( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); + +public: + + MatrixF mScanWorkspaceScanMat; + MatrixF mScanWorkspaceScanWorldMat; + +public: + + AITurretShape(); + virtual ~AITurretShape(); + + static void initPersistFields(); + + bool onAdd(); + void onRemove(); + bool onNewDataBlock(GameBaseData *dptr, bool reload); + + void addToIgnoreList(ShapeBase* obj); + void removeFromIgnoreList(ShapeBase* obj); + + void setTurretStateName(const char* newState, bool force=false); + void setTurretState(U32 newState, bool force=false); + + void activateTurret() {mStateActive = true;} + void deactivateTurret() {mStateActive = false;} + + void startScanForTargets() {mScanForTargets = true;} + void stopScanForTargets() {mScanForTargets = false;} + + void startTrackingTarget() {mTrackTarget = true;} + void stopTrackingTarget() {mTrackTarget = false;} + + ShapeBase* getTarget() {return mTarget.target;} + bool hasTarget() {return mTarget.target != NULL;} + void resetTarget() {mTarget.reset();} + + void addPotentialTarget(ShapeBase* shape); + + void setWeaponLeadVelocity(F32 velocity) {mWeaponLeadVelocitySquared = velocity * velocity;} + F32 getWeaponLeadVelocity() {return mSqrt(mWeaponLeadVelocitySquared);} + + void setAllGunsFiring(bool fire); + void setGunSlotFiring(S32 slot, bool fire); + + virtual void setTransform(const MatrixF &mat); + void getScanTransform(MatrixF& mat); + void getAimTransform(MatrixF& mat); + + F32 getMaxScanHeading() const {return mScanHeading;} + F32 getMaxScanPitch() const {return mScanPitch;} + F32 getMaxScanDistance() const {return mScanDistance;} + F32 getMaxScanDistanceSquared() const {return mScanDistanceSquared;} + + void recenterTurret(); + + virtual void processTick(const Move *move); + virtual void advanceTime(F32 dt); + + virtual U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream); + virtual void unpackUpdate(NetConnection *conn, BitStream *stream); + + void prepBatchRender( SceneRenderState *state, S32 mountedImageIndex ); + + DECLARE_CONOBJECT(AITurretShape); +}; + +#endif // _AITURRETSHAPE_H_ diff --git a/Engine/source/T3D/turret/turretShape.cpp b/Engine/source/T3D/turret/turretShape.cpp new file mode 100644 index 000000000..de86ee177 --- /dev/null +++ b/Engine/source/T3D/turret/turretShape.cpp @@ -0,0 +1,1436 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "T3D/turret/turretShape.h" + +#include "console/console.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "core/stream/bitStream.h" +#include "math/mMath.h" +#include "math/mathIO.h" +#include "ts/tsShapeInstance.h" +#include "T3D/fx/cameraFXMgr.h" +#include "T3D/gameBase/gameConnection.h" +#include "T3D/physics/physicsBody.h" + +//---------------------------------------------------------------------------- + +// Client prediction +static F32 sMinWarpTicks = 0.5 ; // Fraction of tick at which instant warp occures +static S32 sMaxWarpTicks = 3; // Max warp duration in ticks + +const U32 sClientCollisionMask = (TerrainObjectType | + InteriorObjectType | StaticShapeObjectType | + VehicleObjectType); + +const U32 sServerCollisionMask = (sClientCollisionMask); + +// Trigger objects that are not normally collided with. +static U32 sTriggerMask = ItemObjectType | + TriggerObjectType | + CorpseObjectType; + +//---------------------------------------------------------------------------- + +ImplementEnumType( TurretShapeFireLinkType, + "@brief How the weapons are linked to triggers for this TurretShape.\n\n" + "@ingroup gameObjects\n\n") + { TurretShapeData::FireTogether, "FireTogether", "All weapons fire under trigger 0.\n" }, + { TurretShapeData::GroupedFire, "GroupedFire", "Weapon mounts 0,2 fire under trigger 0, mounts 1,3 fire under trigger 1.\n" }, + { TurretShapeData::IndividualFire, "IndividualFire", "Each weapon mount fires under its own trigger 0-3.\n" }, +EndImplementEnumType; + +IMPLEMENT_CO_DATABLOCK_V1(TurretShapeData); + +ConsoleDocClass( TurretShapeData, + "@brief Defines properties for a TurretShape object.\n\n" + "@see TurretShape\n" + "@see TurretShapeData\n" + "@ingroup gameObjects\n" +); + +IMPLEMENT_CALLBACK( TurretShapeData, onMountObject, void, ( TurretShape* turret, SceneObject* obj, S32 node ),( turret, obj, node ), + "@brief Informs the TurretShapeData object that a player is mounting it.\n\n" + "@param turret The TurretShape object.\n" + "@param obj The player that is mounting.\n" + "@param node The node the player is mounting to.\n" + "@note Server side only.\n" +); + +IMPLEMENT_CALLBACK( TurretShapeData, onUnmountObject, void, ( TurretShape* turret, SceneObject* obj ),( turret, obj ), + "@brief Informs the TurretShapeData object that a player is unmounting it.\n\n" + "@param turret The TurretShape object.\n" + "@param obj The player that is unmounting.\n" + "@note Server side only.\n" +); + +IMPLEMENT_CALLBACK( TurretShapeData, onStickyCollision, void, ( TurretShape* obj ),( obj ), + "@brief Informs the TurretData object that it is now sticking to another object.\n\n" + "This callback is only called if the TurretData::sticky property for this Turret is true.\n" + "@param obj The Turret object that is colliding.\n" + "@note Server side only.\n" + "@see TurretShape, TurretData\n" +); + +TurretShapeData::TurretShapeData() +{ + weaponLinkType = FireTogether; + + shadowEnable = true; + + zRotOnly = false; + + startLoaded = true; + + friction = 0; + elasticity = 0; + + sticky = false; + gravityMod = 1.0; + maxVelocity = 25.0f; + + density = 2; + drag = 0.5; + + cameraOffset = 0; + + maxHeading = 180.0f; + minPitch = 90.0f; + maxPitch = 90.0f; + + headingRate = -1; + pitchRate = -1; + + headingNode = -1; + pitchNode = -1; + + for (U32 i=0; i(), Offset(weaponLinkType, TurretShapeData), + "@brief Set how the mounted weapons are linked and triggered.\n\n" + "
      • FireTogether: All weapons fire under trigger 0.
      • " + "
      • GroupedFire: Weapon mounts 0,2 fire under trigger 0, mounts 1,3 fire under trigger 1.
      • " + "
      • IndividualFire: Each weapon mount fires under its own trigger 0-3.
      \n" + "@see TurretShapeFireLinkType"); + + addField("startLoaded", TypeBool, Offset(startLoaded, TurretShapeData), + "@brief Does the turret's mounted weapon(s) start in a loaded state.\n\n" + "True indicates that all mounted weapons start in a loaded state.\n" + "@see ShapeBase::setImageLoaded()"); + + addField("cameraOffset", TypeF32, Offset(cameraOffset, TurretShapeData), + "Vertical (Z axis) height of the camera above the turret." ); + + addField("maxHeading", TypeF32, Offset(maxHeading, TurretShapeData), + "@brief Maximum number of degrees to rotate from center.\n\n" + "A value of 180 or more degrees indicates the turret may rotate completely around.\n"); + addField("minPitch", TypeF32, Offset(minPitch, TurretShapeData), + "@brief Minimum number of degrees to rotate down from straight ahead.\n\n"); + addField("maxPitch", TypeF32, Offset(maxPitch, TurretShapeData), + "@brief Maximum number of degrees to rotate up from straight ahead.\n\n"); + + addField("headingRate", TypeF32, Offset(headingRate, TurretShapeData), + "@brief Degrees per second rotation.\n\n" + "A value of 0 means no rotation is allowed. A value less than 0 means the rotation is instantaneous.\n"); + addField("pitchRate", TypeF32, Offset(pitchRate, TurretShapeData), + "@brief Degrees per second rotation.\n\n" + "A value of 0 means no rotation is allowed. A value less than 0 means the rotation is instantaneous.\n"); + + Parent::initPersistFields(); +} + +void TurretShapeData::packData(BitStream* stream) +{ + Parent::packData(stream); + + stream->writeFlag(zRotOnly); + + stream->writeInt(weaponLinkType,NumFireLinkTypeBits); + + stream->write(cameraOffset); + + stream->write(maxHeading); + stream->write(minPitch); + stream->write(maxPitch); + + stream->write(headingRate); + stream->write(pitchRate); +} + +void TurretShapeData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + + zRotOnly = stream->readFlag(); + + weaponLinkType = (FireLinkType)stream->readInt(NumFireLinkTypeBits); + + stream->read(&cameraOffset); + + stream->read(&maxHeading); + stream->read(&minPitch); + stream->read(&maxPitch); + + stream->read(&headingRate); + stream->read(&pitchRate); +} + +bool TurretShapeData::preload(bool server, String &errorStr) +{ + if (!Parent::preload(server, errorStr)) + return false; + + // We have mShape at this point. Resolve nodes. + headingNode = mShape->findNode("heading"); + pitchNode = mShape->findNode("pitch"); + + // Find any mirror pitch nodes + for (U32 i = 0; i < NumMirrorDirectionNodes; ++i) + { + char name[32]; + dSprintf(name, 31, "pitch%d", i+1); + pitchNodes[i] = mShape->findNode(name); + + dSprintf(name, 31, "heading%d", i+1); + headingNodes[i] = mShape->findNode(name); + } + + // Resolve weapon mount point node indexes + for (U32 i = 0; i < ShapeBase::MaxMountedImages; i++) { + char fullName[256]; + dSprintf(fullName,sizeof(fullName),"weaponMount%d",i); + weaponMountNode[i] = mShape->findNode(fullName); + } + + // Recoil animations + recoilSequence[0] = mShape->findSequence("light_recoil"); + recoilSequence[1] = mShape->findSequence("medium_recoil"); + recoilSequence[2] = mShape->findSequence("heavy_recoil"); + + // Optional sequences used when the turret rotates + pitchSequence = mShape->findSequence("pitch"); + headingSequence = mShape->findSequence("heading"); + + return true; +} + + +//---------------------------------------------------------------------------- + +IMPLEMENT_CO_NETOBJECT_V1(TurretShape); + +ConsoleDocClass( TurretShape, + "@ingroup gameObjects\n" +); + +TurretShape::TurretShape() +{ + mTypeMask |= VehicleObjectType | DynamicShapeObjectType; + mDataBlock = 0; + + allowManualRotation = true; + allowManualFire = true; + + mTurretDelta.rot = Point3F(0.0f, 0.0f, 0.0f); + mTurretDelta.rotVec = VectorF(0.0f, 0.0f, 0.0f); + mTurretDelta.dt = 1; + + mRot = mTurretDelta.rot; + + mPitchAllowed = true; + mHeadingAllowed = true; + + mPitchRate = -1; + mHeadingRate = -1; + + mPitchUp = 0; + mPitchDown = 0; + mHeadingMax = mDegToRad(180.0f); + + mRespawn = false; + + mPitchThread = 0; + mHeadingThread = 0; + + mSubclassTurretShapeHandlesScene = false; + + // For the Item class + mSubclassItemHandlesScene = true; +} + +TurretShape::~TurretShape() +{ +} + +//---------------------------------------------------------------------------- + +void TurretShape::initPersistFields() +{ + addField("respawn", TypeBool, Offset(mRespawn, TurretShape), + "@brief Respawn the turret after it has been destroyed.\n\n" + "If true, the turret will respawn after it is destroyed.\n"); + + Parent::initPersistFields(); +} + +bool TurretShape::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + // Add this object to the scene + if (!mSubclassTurretShapeHandlesScene) + { + addToScene(); + } + + if (isServerObject() && !mSubclassTurretShapeHandlesScene) + { + scriptOnAdd(); + } + + return true; +} + +void TurretShape::onRemove() +{ + Parent::onRemove(); + + if (!mSubclassTurretShapeHandlesScene) + { + scriptOnRemove(); + + // Remove this object from the scene + removeFromScene(); + } +} + +bool TurretShape::onNewDataBlock(GameBaseData* dptr, bool reload) +{ + mDataBlock = dynamic_cast(dptr); + if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload)) + return false; + + // Mark these nodes for control by code only (will not animate in a sequence) + if (mDataBlock->headingNode != -1) + mShapeInstance->setNodeAnimationState(mDataBlock->headingNode, TSShapeInstance::MaskNodeHandsOff); + if (mDataBlock->pitchNode != -1) + mShapeInstance->setNodeAnimationState(mDataBlock->pitchNode, TSShapeInstance::MaskNodeHandsOff); + for (U32 i=0; ipitchNodes[i] != -1) + { + mShapeInstance->setNodeAnimationState(mDataBlock->pitchNodes[i], TSShapeInstance::MaskNodeHandsOff); + } + + if (mDataBlock->headingNodes[i] != -1) + { + mShapeInstance->setNodeAnimationState(mDataBlock->headingNodes[i], TSShapeInstance::MaskNodeHandsOff); + } + } + + if (mIsZero(mDataBlock->pitchRate)) + { + mPitchAllowed = false; + } + else + { + mPitchAllowed = true; + if (mDataBlock->pitchRate > 0) + { + mPitchRate = mDegToRad(mDataBlock->pitchRate); + } + else + { + mPitchRate = -1; + } + } + + if (mIsZero(mDataBlock->headingRate)) + { + mHeadingAllowed = false; + } + else + { + mHeadingAllowed = true; + if (mDataBlock->headingRate > 0) + { + mHeadingRate = mDegToRad(mDataBlock->headingRate); + } + else + { + mHeadingRate = -1; + } + } + + mPitchUp = -mDegToRad(mDataBlock->maxPitch); + mPitchDown = mDegToRad(mDataBlock->minPitch); + mHeadingMax = mDegToRad(mDataBlock->maxHeading); + + // Create Recoil thread if any recoil sequences are specified. + // Note that the server player does not play this animation. + mRecoilThread = 0; + if (isGhost()) + for (U32 s = 0; s < TurretShapeData::NumRecoilSequences; s++) + if (mDataBlock->recoilSequence[s] != -1) { + mRecoilThread = mShapeInstance->addThread(); + mShapeInstance->setSequence(mRecoilThread, mDataBlock->recoilSequence[s], 0); + mShapeInstance->setTimeScale(mRecoilThread, 0); + break; + } + + // Reset the image state driven animation thread. This will be properly built + // in onImageStateAnimation() when needed. + mImageStateThread = 0; + + // Optional rotation threads. These only play on the client. + mPitchThread = 0; + mHeadingThread = 0; + if (isGhost()) + { + if (mDataBlock->pitchSequence != -1) + { + mPitchThread = mShapeInstance->addThread(); + mShapeInstance->setSequence(mPitchThread, mDataBlock->pitchSequence, 0); + mShapeInstance->setTimeScale(mPitchThread, 0); + } + if (mDataBlock->headingSequence != -1) + { + mHeadingThread = mShapeInstance->addThread(); + mShapeInstance->setSequence(mHeadingThread, mDataBlock->headingSequence, 0); + mShapeInstance->setTimeScale(mHeadingThread, 0); + } + } + + if (!mSubclassTurretShapeHandlesScene) + { + scriptOnNewDataBlock(); + } + + return true; +} + +//---------------------------------------------------------------------------- + +void TurretShape::updateAnimation(F32 dt) +{ + if (mRecoilThread) + mShapeInstance->advanceTime(dt,mRecoilThread); + if (mImageStateThread) + mShapeInstance->advanceTime(dt,mImageStateThread); + + // Update any pitch and heading threads + if (mPitchThread) + { + F32 d = mPitchDown - mPitchUp; + if (!mIsZero(d)) + { + F32 pos = (mRot.x - mPitchUp) / d; + mShapeInstance->setPos(mPitchThread, mClampF(pos, 0.0f, 1.0f)); + } + } + if (mHeadingThread) + { + F32 pos = 0.0f; + if (mHeadingMax < mDegToRad(180.0f)) + { + F32 d = mHeadingMax * 2.0f; + if (!mIsZero(d)) + { + pos = (mRot.z + mHeadingMax) / d; + } + } + else + { + pos = mRot.z / M_2PI; + if (pos < 0.0f) + { + // We don't want negative rotations to simply mirror the + // positive rotations but to animate into them as if -0.0 + // is equivalent to 1.0. + pos = mFmod(pos, 1.0f) + 1.0f; + } + if (pos > 1.0f) + { + pos = mFmod(pos, 1.0f); + } + } + mShapeInstance->setPos(mHeadingThread, mClampF(pos, 0.0f, 1.0f)); + } +} + +//---------------------------------------------------------------------------- + +void TurretShape::onImage(U32 imageSlot, bool unmount) +{ + // Clear out any previous image state animation + if (mImageStateThread) + { + mShapeInstance->destroyThread(mImageStateThread); + mImageStateThread = 0; + } +} + +void TurretShape::onImageRecoil( U32, ShapeBaseImageData::StateData::RecoilState state ) +{ + if ( mRecoilThread ) + { + if ( state != ShapeBaseImageData::StateData::NoRecoil ) + { + S32 stateIndex = state - ShapeBaseImageData::StateData::LightRecoil; + if ( mDataBlock->recoilSequence[stateIndex] != -1 ) + { + mShapeInstance->setSequence( mRecoilThread, mDataBlock->recoilSequence[stateIndex], 0 ); + mShapeInstance->setTimeScale( mRecoilThread, 1 ); + } + } + } +} + +void TurretShape::onImageStateAnimation(U32 imageSlot, const char* seqName, bool direction, bool scaleToState, F32 stateTimeOutValue) +{ + if (isGhost()) + { + S32 seqIndex = mShapeInstance->getShape()->findSequence(seqName); + + if (seqIndex != -1) + { + if (!mImageStateThread) + { + mImageStateThread = mShapeInstance->addThread(); + } + + mShapeInstance->setSequence( mImageStateThread, seqIndex, 0 ); + + F32 timeScale = (scaleToState && stateTimeOutValue) ? + mShapeInstance->getDuration(mImageStateThread) / stateTimeOutValue : 1.0f; + + mShapeInstance->setTimeScale( mImageStateThread, direction ? timeScale : -timeScale ); + } + } +} + +//---------------------------------------------------------------------------- + +const char* TurretShape::getStateName() +{ + if (mDamageState != Enabled) + return "Dead"; + if (isMounted()) + return "Mounted"; + return "Ready"; +} + +void TurretShape::updateDamageLevel() +{ + if (!isGhost()) + setDamageState((mDamage >= mDataBlock->maxDamage)? Destroyed: Enabled); + if (mDamageThread) + mShapeInstance->setPos(mDamageThread, mDamage / mDataBlock->destroyedLevel); +} + +//---------------------------------------------------------------------------- + +void TurretShape::processTick(const Move* move) +{ + // Image Triggers + if (getAllowManualFire() && move && mDamageState == Enabled) + { + switch(mDataBlock->weaponLinkType) + { + case TurretShapeData::FireTogether: + { + setImageTriggerState(0,move->trigger[0]); + setImageTriggerState(1,move->trigger[0]); + setImageTriggerState(2,move->trigger[0]); + setImageTriggerState(3,move->trigger[0]); + + setImageAltTriggerState(0,move->trigger[1]); + setImageAltTriggerState(1,move->trigger[1]); + setImageAltTriggerState(2,move->trigger[1]); + setImageAltTriggerState(3,move->trigger[1]); + + break; + } + + case TurretShapeData::GroupedFire: + { + setImageTriggerState(0,move->trigger[0]); + setImageTriggerState(1,move->trigger[1]); + setImageTriggerState(2,move->trigger[0]); + setImageTriggerState(3,move->trigger[1]); + + break; + } + + case TurretShapeData::IndividualFire: + { + setImageTriggerState(0,move->trigger[0]); + setImageTriggerState(1,move->trigger[1]); + setImageTriggerState(2,move->trigger[2]); + setImageTriggerState(3,move->trigger[3]); + + break; + } + } + } + + Parent::processTick(move); + + // Change our type based on our rest state + if (mAtRest) + { + // At rest so we're static + mTypeMask &= ~DynamicShapeObjectType; + mTypeMask |= StaticObjectType | StaticShapeObjectType; + } + else + { + // Not at rest so we're dynamic + mTypeMask &= ~StaticObjectType; + mTypeMask &= ~StaticShapeObjectType; + mTypeMask |= DynamicShapeObjectType; + } + + if (!isGhost()) + updateAnimation(TickSec); + + if (isMounted()) { + MatrixF mat; + mMount.object->getMountTransform( mMount.node, mMount.xfm, &mat ); + ShapeBase::setTransform(mat); + ShapeBase::setRenderTransform(mat); + } + + updateMove(move); +} + +void TurretShape::interpolateTick(F32 dt) +{ + Parent::interpolateTick(dt); + + if (isMounted()) { + MatrixF mat; + mMount.object->getRenderMountTransform( dt, mMount.node, mMount.xfm, &mat ); + ShapeBase::setRenderTransform(mat); + } + + // Orientation + Point3F rot = mTurretDelta.rot + mTurretDelta.rotVec * dt; + + // Make sure we don't interpolate past the limits + _applyLimits(rot); + + _setRotation(rot); +} + +void TurretShape::advanceTime(F32 dt) +{ + // If there were any ShapeBase script threads that + // have played, then we need to update all code + // controlled nodes. This is done before the Parent + // call as script threads may play and be destroyed + // before our code is called. + bool updateNodes = false; + for (U32 i = 0; i < MaxScriptThreads; i++) + { + Thread& st = mScriptThread[i]; + if (st.thread) + { + updateNodes = true; + break; + } + } + + // If there is a recoil or image-based thread then + // we also need to update the nodes. + if (mRecoilThread || mImageStateThread) + updateNodes = true; + + Parent::advanceTime(dt); + + updateAnimation(dt); + + if (updateNodes) + { + _updateNodes(mRot); + } +} + +void TurretShape::setTransform( const MatrixF& mat ) +{ + if (mDataBlock && mDataBlock->zRotOnly) + { + // Allow Item::setTransform() to do the work + Parent::setTransform( mat ); + } + else + { + // Do the transform work here to avoid Item's restriction on rotation + ShapeBase::setTransform( mat ); + + if ( !mStatic ) + { + mAtRest = false; + mAtRestCounter = 0; + } + + if ( mPhysicsRep ) + mPhysicsRep->setTransform( getTransform() ); + + setMaskBits( Item::RotationMask | Item::PositionMask | Item::NoWarpMask ); + } +} + +void TurretShape::updateMove(const Move* move) +{ + PROFILE_SCOPE( TurretShape_UpdateMove ); + + if (!move) + return; + + Point3F vec, pos; + + // Update orientation + mTurretDelta.rotVec = mRot; + + VectorF rotVec(0, 0, 0); + if (getAllowManualRotation()) + { + if (mPitchAllowed) + { + rotVec.x = move->pitch * 2.0f; // Assume that our -2PI to 2PI range was clamped to -PI to PI in script; + if (mPitchRate > 0) + { + rotVec.x *= mPitchRate * TickSec; + } + } + if (mHeadingAllowed) + { + rotVec.z = move->yaw * 2.0f; // Assume that our -2PI to 2PI range was clamped to -PI to PI in script + if (mHeadingRate > 0) + { + rotVec.z *= mHeadingRate * TickSec; + } + } + } + + mRot.x += rotVec.x; + mRot.z += rotVec.z; + _applyLimits(mRot); + + if (isServerObject()) + { + // As this ends up animating shape nodes, we have no sense of a transform and + // render transform. Therefore we treat this as the true transform and leave the + // client shape node changes to interpolateTick() as the render transform. Otherwise + // on the client we'll have this node change from processTick() and then backstepping + // and catching up to the true node change in interpolateTick(), which causes the + // turret to stutter. + _setRotation( mRot ); + } + else + { + // If on the client, calc delta for backstepping + mTurretDelta.rot = mRot; + mTurretDelta.rotVec = mTurretDelta.rotVec - mTurretDelta.rot; + } + + setMaskBits(TurretUpdateMask); +} + +bool TurretShape::getNodeTransform(S32 node, MatrixF& mat) +{ + if (node == -1) + return false; + + MatrixF nodeTransform = mShapeInstance->mNodeTransforms[node]; + const Point3F& scale = getScale(); + + // The position of the node needs to be scaled. + Point3F position = nodeTransform.getPosition(); + position.convolve( scale ); + nodeTransform.setPosition( position ); + + mat.mul(mObjToWorld, nodeTransform); + return true; +} + +bool TurretShape::getWorldNodeTransform(S32 node, MatrixF& mat) +{ + MatrixF nodeMat; + if (!getNodeTransform(node, nodeMat)) + return false; + + nodeMat.affineInverse(); + mat = nodeMat; + return true; +} + +void TurretShape::_setRotation(const Point3F& rot) +{ + _updateNodes(rot); + + mShapeInstance->animate(); + + mRot = rot; +} + +void TurretShape::_updateNodes(const Point3F& rot) +{ + EulerF xRot(rot.x, 0.0f, 0.0f); + EulerF zRot(0.0f, 0.0f, rot.z); + + // Set heading + S32 node = mDataBlock->headingNode; + if (node != -1) + { + MatrixF* mat = &mShapeInstance->mNodeTransforms[node]; + Point3F defaultPos = mShapeInstance->getShape()->defaultTranslations[node]; + mat->set(zRot); + mat->setColumn(3, defaultPos); + } + + // Set pitch + node = mDataBlock->pitchNode; + if (node != -1) + { + MatrixF* mat = &mShapeInstance->mNodeTransforms[node]; + Point3F defaultPos = mShapeInstance->getShape()->defaultTranslations[node]; + mat->set(xRot); + mat->setColumn(3, defaultPos); + } + + // Now the mirror direction nodes, if any + for (U32 i=0; ipitchNodes[i]; + if (node != -1) + { + MatrixF* mat = &mShapeInstance->mNodeTransforms[node]; + Point3F defaultPos = mShapeInstance->getShape()->defaultTranslations[node]; + mat->set(xRot); + mat->setColumn(3, defaultPos); + } + + node = mDataBlock->headingNodes[i]; + if (node != -1) + { + MatrixF* mat = &mShapeInstance->mNodeTransforms[node]; + Point3F defaultPos = mShapeInstance->getShape()->defaultTranslations[node]; + mat->set(zRot); + mat->setColumn(3, defaultPos); + } + } + + mShapeInstance->setDirty(TSShapeInstance::TransformDirty); +} + +void TurretShape::_applyLimits(Point3F& rot) +{ + rot.x = mClampF(rot.x, mPitchUp, mPitchDown); + if (mHeadingMax < mDegToRad(180.0f)) + { + rot.z = mClampF(rot.z, -mHeadingMax, mHeadingMax); + } +} + +bool TurretShape::_outsideLimits(Point3F& rot) +{ + if (rot.x < mPitchUp || rot.x > mPitchDown) + return true; + + if (mHeadingMax < mDegToRad(180.0f)) + { + if (rot.z < -mHeadingMax || rot.z > mHeadingMax) + return true; + } + + return false; +} + +//---------------------------------------------------------------------------- + +void TurretShape::mountObject( SceneObject *obj, S32 node, const MatrixF &xfm ) +{ + Parent::mountObject(obj, node, xfm); + + if (isClientObject()) + { + if (obj) + { + GameConnection* conn = obj->getControllingClient(); + if (conn) + { + // Allow the client to set up any action maps, HUD, etc. + Con::executef("turretMountCallback", Con::getIntArg(getId()), Con::getIntArg(obj->getId()), Con::getIntArg(true)); + } + } + } + else + { + mDataBlock->onMountObject_callback( this, obj, node ); + } +} + +void TurretShape::unmountObject( SceneObject *obj ) +{ + Parent::unmountObject(obj); + + if (isClientObject()) + { + if (obj) + { + GameConnection* conn = obj->getControllingClient(); + if (conn) + { + // Allow the client to set up any action maps, HUD, etc. + Con::executef("turretMountCallback", Con::getIntArg(getId()), Con::getIntArg(obj->getId()), Con::getIntArg(false)); + } + } + } + else + { + mDataBlock->onUnmountObject_callback( this, obj ); + } +} + +void TurretShape::onUnmount(ShapeBase*,S32) +{ + // Make sure the client get's the final server pos of this turret. + setMaskBits(PositionMask); +} + +//---------------------------------------------------------------------------- + +void TurretShape::getCameraParameters(F32 *min,F32* max,Point3F* off,MatrixF* rot) +{ + *min = mDataBlock->cameraMinDist; + *max = mDataBlock->cameraMaxDist; + + off->set(0,0,mDataBlock->cameraOffset); + rot->identity(); +} + +void TurretShape::getCameraTransform(F32* pos,MatrixF* mat) +{ + // Returns camera to world space transform + // Handles first person / third person camera position + if (isServerObject() && mShapeInstance) + mShapeInstance->animateNodeSubtrees(true); + + if (*pos == 0) { + getRenderEyeTransform(mat); + return; + } + + // Get the shape's camera parameters. + F32 min,max; + MatrixF rot; + Point3F offset; + getCameraParameters(&min,&max,&offset,&rot); + + // Start with the current eye position + MatrixF eye; + getRenderEyeTransform(&eye); + + // Build a transform that points along the eye axis + // but where the Z axis is always up. + { + MatrixF cam(1); + VectorF x,y,z(0,0,1); + eye.getColumn(1, &y); + mCross(y, z, &x); + x.normalize(); + mCross(x, y, &z); + z.normalize(); + cam.setColumn(0,x); + cam.setColumn(1,y); + cam.setColumn(2,z); + mat->mul(cam,rot); + } + + // Camera is positioned straight back along the eye's -Y axis. + // A ray is cast to make sure the camera doesn't go through + // anything solid. + VectorF vp,vec; + vp.x = vp.z = 0; + vp.y = -(max - min) * *pos; + eye.mulV(vp,&vec); + + // Use the camera node as the starting position if it exists. + Point3F osp,sp; + if (mDataBlock->cameraNode != -1) + { + mShapeInstance->mNodeTransforms[mDataBlock->cameraNode].getColumn(3,&osp); + getRenderTransform().mulP(osp,&sp); + } + else + eye.getColumn(3,&sp); + + // Make sure we don't hit ourself... + disableCollision(); + if (isMounted()) + getObjectMount()->disableCollision(); + + // Cast the ray into the container database to see if we're going + // to hit anything. + RayInfo collision; + Point3F ep = sp + vec + offset; + if (mContainer->castRay(sp, ep, + ~(WaterObjectType | GameBaseObjectType | DefaultObjectType | sTriggerMask), + &collision) == true) { + + // Shift the collision point back a little to try and + // avoid clipping against the front camera plane. + F32 t = collision.t - (-mDot(vec, collision.normal) / vec.len()) * 0.1; + if (t > 0.0f) + ep = sp + offset + (vec * t); + else + eye.getColumn(3,&ep); + } + mat->setColumn(3,ep); + + // Re-enable our collision. + if (isMounted()) + getObjectMount()->enableCollision(); + enableCollision(); + + // Apply Camera FX. + mat->mul( gCamFXMgr.getTrans() ); +} + +//---------------------------------------------------------------------------- + +void TurretShape::writePacketData(GameConnection *connection, BitStream *stream) +{ + // Update client regardless of status flags. + Parent::writePacketData(connection, stream); + + stream->write(mRot.x); + stream->write(mRot.z); +} + +void TurretShape::readPacketData(GameConnection *connection, BitStream *stream) +{ + Parent::readPacketData(connection, stream); + + Point3F rot(0.0f, 0.0f, 0.0f); + stream->read(&rot.x); + stream->read(&rot.z); + + _setRotation(rot); + + mTurretDelta.rot = rot; + mTurretDelta.rotVec.set(0.0f, 0.0f, 0.0f); +} + +U32 TurretShape::packUpdate(NetConnection *connection, U32 mask, BitStream *stream) +{ + // Handle rotation ourselves (so it is not locked to the Z axis like for Items) + U32 retMask = Parent::packUpdate( connection, mask & (~Item::RotationMask), stream ); + + if (stream->writeFlag(mask & InitialUpdateMask)) { + stream->writeFlag(mRespawn); + } + + if ( stream->writeFlag( mask & Item::RotationMask ) ) + { + QuatF rot( mObjToWorld ); + mathWrite( *stream, rot ); + } + + // The rest of the data is part of the control object packet update. + // If we're controlled by this client, we don't need to send it. + if(stream->writeFlag((NetConnection*)getControllingClient() == connection && !(mask & InitialUpdateMask))) + return 0; + + if (stream->writeFlag(mask & TurretUpdateMask)) + { + stream->write(mRot.x); + stream->write(mRot.z); + stream->write(allowManualRotation); + stream->write(allowManualFire); + } + + return retMask; +} + +void TurretShape::unpackUpdate(NetConnection *connection, BitStream *stream) +{ + Parent::unpackUpdate(connection,stream); + + // InitialUpdateMask + if (stream->readFlag()) { + mRespawn = stream->readFlag(); + } + + // Item::RotationMask + if ( stream->readFlag() ) + { + QuatF rot; + mathRead( *stream, &rot ); + + Point3F pos = mObjToWorld.getPosition(); + rot.setMatrix( &mObjToWorld ); + mObjToWorld.setPosition( pos ); + } + + // controlled by the client? + if(stream->readFlag()) + return; + + // TurretUpdateMask + if (stream->readFlag()) + { + Point3F rot(0.0f, 0.0f, 0.0f); + stream->read(&rot.x); + stream->read(&rot.z); + _setRotation(rot); + + // New delta for client side interpolation + mTurretDelta.rot = rot; + mTurretDelta.rotVec = VectorF(0.0f, 0.0f, 0.0f); + + stream->read(&allowManualRotation); + stream->read(&allowManualFire); + } +} + +//---------------------------------------------------------------------------- + +void TurretShape::getWeaponMountTransform( S32 index, const MatrixF &xfm, MatrixF *outMat ) +{ + // Returns mount point to world space transform + if ( index >= 0 && index < SceneObject::NumMountPoints) { + S32 ni = mDataBlock->weaponMountNode[index]; + if (ni != -1) { + MatrixF mountTransform = mShapeInstance->mNodeTransforms[ni]; + mountTransform.mul( xfm ); + const Point3F& scale = getScale(); + + // The position of the mount point needs to be scaled. + Point3F position = mountTransform.getPosition(); + position.convolve( scale ); + mountTransform.setPosition( position ); + + // Also we would like the object to be scaled to the model. + outMat->mul(mObjToWorld, mountTransform); + return; + } + } + + // Then let SceneObject handle it. + GrandParent::getMountTransform( index, xfm, outMat ); +} + +void TurretShape::getRenderWeaponMountTransform( F32 delta, S32 mountPoint, const MatrixF &xfm, MatrixF *outMat ) +{ + // Returns mount point to world space transform + if ( mountPoint >= 0 && mountPoint < SceneObject::NumMountPoints) { + S32 ni = mDataBlock->weaponMountNode[mountPoint]; + if (ni != -1) { + MatrixF mountTransform = mShapeInstance->mNodeTransforms[ni]; + mountTransform.mul( xfm ); + const Point3F& scale = getScale(); + + // The position of the mount point needs to be scaled. + Point3F position = mountTransform.getPosition(); + position.convolve( scale ); + mountTransform.setPosition( position ); + + // Also we would like the object to be scaled to the model. + mountTransform.scale( scale ); + outMat->mul(getRenderTransform(), mountTransform); + return; + } + } + + // Then let SceneObject handle it. + GrandParent::getRenderMountTransform( delta, mountPoint, xfm, outMat ); +} + +void TurretShape::getImageTransform(U32 imageSlot,MatrixF* mat) +{ + // Image transform in world space + MountedImage& image = mMountedImageList[imageSlot]; + if (image.dataBlock) { + ShapeBaseImageData& data = *image.dataBlock; + + MatrixF nmat; + if (data.useEyeOffset && isFirstPerson()) { + getEyeTransform(&nmat); + mat->mul(nmat,data.eyeOffset); + } + else { + getWeaponMountTransform( imageSlot, MatrixF::Identity, &nmat ); + mat->mul(nmat,data.mountTransform[getImageShapeIndex(image)]); + } + } + else + *mat = mObjToWorld; +} + +void TurretShape::getRenderImageTransform( U32 imageSlot, MatrixF* mat, bool noEyeOffset ) +{ + // Image transform in world space + MountedImage& image = mMountedImageList[imageSlot]; + if (image.dataBlock) + { + ShapeBaseImageData& data = *image.dataBlock; + + MatrixF nmat; + if ( !noEyeOffset && data.useEyeOffset && isFirstPerson() ) + { + getRenderEyeTransform(&nmat); + mat->mul(nmat,data.eyeOffset); + } + else + { + getRenderWeaponMountTransform( 0.0f, imageSlot, MatrixF::Identity, &nmat ); + mat->mul(nmat,data.mountTransform[getImageShapeIndex(image)]); + } + } + else + *mat = getRenderTransform(); +} + +void TurretShape::getImageTransform(U32 imageSlot,S32 node,MatrixF* mat) +{ + // Same as ShapeBase::getImageTransform() other than getRenderWeaponMountTransform() below + + // Image transform in world space + MountedImage& image = mMountedImageList[imageSlot]; + if (image.dataBlock) + { + if (node != -1) + { + ShapeBaseImageData& data = *image.dataBlock; + U32 shapeIndex = getImageShapeIndex(image); + + MatrixF nmat = image.shapeInstance[shapeIndex]->mNodeTransforms[node]; + MatrixF mmat; + + if (data.useEyeNode && isFirstPerson() && data.eyeMountNode[shapeIndex] != -1) + { + // We need to animate, even on the server, to make sure the nodes are in the correct location. + image.shapeInstance[shapeIndex]->animate(); + + MatrixF emat; + getEyeBaseTransform(&emat); + + MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]]; + mountTransform.affineInverse(); + + mmat.mul(emat, mountTransform); + } + else if (data.useEyeOffset && isFirstPerson()) + { + MatrixF emat; + getEyeTransform(&emat); + mmat.mul(emat,data.eyeOffset); + } + else + { + MatrixF emat; + getWeaponMountTransform( imageSlot, MatrixF::Identity, &emat ); + mmat.mul(emat,data.mountTransform[shapeIndex]); + } + + mat->mul(mmat, nmat); + } + else + getImageTransform(imageSlot,mat); + } + else + *mat = mObjToWorld; +} + +void TurretShape::getRenderImageTransform(U32 imageSlot,S32 node,MatrixF* mat) +{ + // Same as ShapeBase::getRenderImageTransform() other than getRenderWeaponMountTransform() below + + // Image transform in world space + MountedImage& image = mMountedImageList[imageSlot]; + if (image.dataBlock) + { + if (node != -1) + { + ShapeBaseImageData& data = *image.dataBlock; + U32 shapeIndex = getImageShapeIndex(image); + + MatrixF nmat = image.shapeInstance[shapeIndex]->mNodeTransforms[node]; + MatrixF mmat; + + if ( data.useEyeNode && isFirstPerson() && data.eyeMountNode[shapeIndex] != -1 ) + { + MatrixF emat; + getRenderEyeBaseTransform(&emat); + + MatrixF mountTransform = image.shapeInstance[shapeIndex]->mNodeTransforms[data.eyeMountNode[shapeIndex]]; + mountTransform.affineInverse(); + + mmat.mul(emat, mountTransform); + } + else if ( data.useEyeOffset && isFirstPerson() ) + { + MatrixF emat; + getRenderEyeTransform(&emat); + mmat.mul(emat,data.eyeOffset); + } + else + { + MatrixF emat; + getRenderWeaponMountTransform( 0.0f, imageSlot, MatrixF::Identity, &emat ); + mmat.mul(emat,data.mountTransform[shapeIndex]); + } + + mat->mul(mmat, nmat); + } + else + getRenderImageTransform(imageSlot,mat); + } + else + *mat = getRenderTransform(); +} + +//---------------------------------------------------------------------------- + +void TurretShape::prepRenderImage( SceneRenderState *state ) +{ + // Skip the Item class rendering + _prepRenderImage( state, true, true ); +} + +void TurretShape::prepBatchRender( SceneRenderState *state, S32 mountedImageIndex ) +{ + Parent::prepBatchRender( state, mountedImageIndex ); + + if ( !gShowBoundingBox ) + return; + + //if ( mountedImageIndex != -1 ) + //{ + // ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + // ri->renderDelegate.bind( this, &Vehicle::_renderMuzzleVector ); + // ri->objectIndex = mountedImageIndex; + // ri->type = RenderPassManager::RIT_Editor; + // state->getRenderPass()->addInst( ri ); + // return; + //} + + //ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + //ri->renderDelegate.bind( this, &Vehicle::_renderMassAndContacts ); + //ri->type = RenderPassManager::RIT_Editor; + //state->getRenderPass()->addInst( ri ); +} + +//---------------------------------------------------------------------------- + +DefineEngineMethod( TurretShape, getAllowManualRotation, bool, (),, + "@brief Get if the turret is allowed to rotate through moves.\n\n" + "@return True if the turret is allowed to rotate through moves.\n" ) +{ + return object->getAllowManualRotation(); +} + +DefineEngineMethod( TurretShape, setAllowManualRotation, void, (bool allow),, + "@brief Set if the turret is allowed to rotate through moves.\n\n" + "@param allow If true then the turret may be rotated through moves.\n") +{ + return object->setAllowManualRotation(allow); +} + +DefineEngineMethod( TurretShape, getAllowManualFire, bool, (),, + "@brief Get if the turret is allowed to fire through moves.\n\n" + "@return True if the turret is allowed to fire through moves.\n" ) +{ + return object->getAllowManualFire(); +} + +DefineEngineMethod( TurretShape, setAllowManualFire, void, (bool allow),, + "@brief Set if the turret is allowed to fire through moves.\n\n" + "@param allow If true then the turret may be fired through moves.\n") +{ + return object->setAllowManualFire(allow); +} + +DefineEngineMethod( TurretShape, getState, const char*, (),, + "@brief Get the name of the turret's current state.\n\n" + + "The state is one of the following:\n\n
        " + "
      • Dead - The TurretShape is destroyed.
      • " + "
      • Mounted - The TurretShape is mounted to an object such as a vehicle.
      • " + "
      • Ready - The TurretShape is free to move. The usual state.
      \n" + + "@return The current state; one of: \"Dead\", \"Mounted\", \"Ready\"\n" ) +{ + return object->getStateName(); +} + +DefineEngineMethod( TurretShape, getTurretEulerRotation, Point3F, (),, + "@brief Get Euler rotation of this turret's heading and pitch nodes.\n\n" + "@return the orientation of the turret's heading and pitch nodes in the " + "form of rotations around the X, Y and Z axes in degrees.\n" ) +{ + Point3F euler = object->getTurretRotation(); + + // Convert to degrees. + euler.x = mRadToDeg( euler.x ); + euler.y = mRadToDeg( euler.y ); + euler.z = mRadToDeg( euler.z ); + + return euler; +} + +DefineEngineMethod( TurretShape, setTurretEulerRotation, void, ( Point3F rot ),, + "@brief Set Euler rotation of this turret's heading and pitch nodes in degrees.\n\n" + "@param rot The rotation in degrees. The pitch is the X component and the " + "heading is the Z component. The Y component is ignored.\n") +{ + object->setTurretRotation( rot ); +} + +DefineEngineMethod( TurretShape, doRespawn, bool, (),, + "@brief Does the turret respawn after it has been destroyed.\n\n" + "@returns True if the turret respawns.\n") +{ + return object->doRespawn(); +} diff --git a/Engine/source/T3D/turret/turretShape.h b/Engine/source/T3D/turret/turretShape.h new file mode 100644 index 000000000..37af57ecf --- /dev/null +++ b/Engine/source/T3D/turret/turretShape.h @@ -0,0 +1,226 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TURRETSHAPE_H_ +#define _TURRETSHAPE_H_ + +#ifndef _SHAPEBASE_H_ + #include "T3D/item.h" +#endif + +class PhysicsBody; +class TurretShape; + +//---------------------------------------------------------------------------- + +class TurretShapeData: public ItemData { + + typedef ItemData Parent; + +public: + + enum FireLinkType { + FireTogether, ///< All weapons fire under trigger 0 + GroupedFire, ///< Weapon mounts 0,2 fire under trigger 0, mounts 1,3 fire under trigger 1 + IndividualFire, ///< Each weapon mount fires under its own trigger 0-3 + NumFireLinkTypeBits = 2 + }; + + FireLinkType weaponLinkType; ///< How are the weapons linked together and triggered + + enum { + NumMirrorDirectionNodes = 4, + }; + + enum Recoil { + LightRecoil, + MediumRecoil, + HeavyRecoil, + NumRecoilSequences + }; + S32 recoilSequence[NumRecoilSequences]; + + S32 pitchSequence; ///< Optional sequence played when the turret pitches + S32 headingSequence; ///< Optional sequence played when the turret's heading changes + + F32 cameraOffset; ///< Vertical offset + + F32 maxHeading; ///< Max degrees to rotate from center. 180 or more indicates full rotation. + F32 minPitch; ///< Min degrees to rotate down from straight ahead + F32 maxPitch; ///< Max degrees to rotate up from straight ahead + + F32 headingRate; ///< Degrees per second rotation. 0 means not allowed. Less than 0 means instantaneous. + F32 pitchRate; ///< Degrees per second rotation. 0 means not allowed. Less than 0 means instantaneous. + + S32 headingNode; ///< Code controlled node for heading changes + S32 pitchNode; ///< Code controlled node for pitch changes + S32 pitchNodes[NumMirrorDirectionNodes]; ///< Additional nodes that mirror the movements of the pitch node. + S32 headingNodes[NumMirrorDirectionNodes]; ///< Additional nodes that mirror the movements of the heading node. + S32 weaponMountNode[ShapeBase::MaxMountedImages]; ///< Where ShapeBaseImageData weapons are mounted + + bool startLoaded; ///< Should the turret's mounted weapon(s) start in a loaded state? + + bool zRotOnly; ///< Should the turret allow only z rotations (like an item)? + +public: + TurretShapeData(); + + DECLARE_CONOBJECT(TurretShapeData); + + static void initPersistFields(); + + virtual void packData(BitStream* stream); + virtual void unpackData(BitStream* stream); + + virtual bool preload(bool server, String &errorStr); + + DECLARE_CALLBACK( void, onMountObject, ( TurretShape* turret, SceneObject* obj, S32 node ) ); + DECLARE_CALLBACK( void, onUnmountObject, ( TurretShape* turret, SceneObject* obj ) ); + DECLARE_CALLBACK( void, onStickyCollision, ( TurretShape* obj ) ); +}; + +typedef TurretShapeData::FireLinkType TurretShapeFireLinkType; + +DefineEnumType( TurretShapeFireLinkType ); + +//---------------------------------------------------------------------------- + +class TurretShape: public Item +{ + typedef Item Parent; + typedef SceneObject GrandParent; + +protected: + enum MaskBits { + TurretUpdateMask = Parent::NextFreeMask << 0, ///< using one mask because we're running out of maskbits + NextFreeMask = Parent::NextFreeMask << 1 + }; + + // Client interpolation data for turret heading and pitch + struct TurretStateDelta { + Point3F rot; + VectorF rotVec; + F32 dt; + }; + TurretStateDelta mTurretDelta; + + Point3F mRot; ///< Current heading and pitch + bool mPitchAllowed; ///< Are pitch changes allowed + bool mHeadingAllowed; ///< Are heading changes allowed + F32 mPitchUp; ///< Pitch up limit, in radians + F32 mPitchDown; ///< Pitch down limit, in radians + F32 mHeadingMax; ///< Maximum heading limit from center, in radians + F32 mPitchRate; ///< How fast the turret may pitch, in radians per second + F32 mHeadingRate; ///< How fast the turret may yaw, in radians per second + + bool mRespawn; + + TSThread* mRecoilThread; + TSThread* mImageStateThread; + + TSThread* mPitchThread; + TSThread* mHeadingThread; + + // Static attributes + TurretShapeData* mDataBlock; + + bool mSubclassTurretShapeHandlesScene; ///< A subclass of TurretShape will handle all of the adding to the scene + + void _setRotation(const Point3F& rot); + void _updateNodes(const Point3F& rot); + void _applyLimits(Point3F& rot); + bool _outsideLimits(Point3F& rot); ///< Return true if any angle is outside of the limits + + void onUnmount(ShapeBase* obj,S32 node); + + // Script level control + bool allowManualRotation; + bool allowManualFire; + + void updateAnimation(F32 dt); + + virtual void onImage(U32 imageSlot, bool unmount); + virtual void onImageRecoil(U32 imageSlot,ShapeBaseImageData::StateData::RecoilState); + virtual void onImageStateAnimation(U32 imageSlot, const char* seqName, bool direction, bool scaleToState, F32 stateTimeOutValue); + +public: + + TurretShape(); + virtual ~TurretShape(); + + static void initPersistFields(); + + bool onAdd(); + void onRemove(); + bool onNewDataBlock(GameBaseData *dptr, bool reload); + + const char* getStateName(); + virtual void updateDamageLevel(); + + virtual void processTick(const Move *move); + virtual void interpolateTick(F32 dt); + virtual void advanceTime(F32 dt); + + virtual void setTransform( const MatrixF &mat ); + + virtual bool getAllowManualRotation() { return allowManualRotation; } + virtual void setAllowManualRotation(bool allow) { setMaskBits(TurretUpdateMask); allowManualRotation = allow; } + virtual bool getAllowManualFire() { return allowManualFire; } + virtual void setAllowManualFire(bool allow) { setMaskBits(TurretUpdateMask); allowManualFire = allow; } + + virtual void updateMove(const Move* move); + + bool getNodeTransform(S32 node, MatrixF& mat); + bool getWorldNodeTransform(S32 node, MatrixF& mat); + + Point3F getTurretRotation() {return mRot;} + void setTurretRotation(const Point3F& rot) {_setRotation(rot);} + + bool doRespawn() { return mRespawn; }; + + virtual void mountObject( SceneObject *obj, S32 node, const MatrixF &xfm = MatrixF::Identity ); + virtual void unmountObject( SceneObject *obj ); + + virtual void getCameraParameters(F32 *min,F32* max,Point3F* offset,MatrixF* rot); + virtual void getCameraTransform(F32* pos,MatrixF* mat); + + virtual void writePacketData( GameConnection* conn, BitStream* stream ); + virtual void readPacketData( GameConnection* conn, BitStream* stream ); + virtual U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream); + virtual void unpackUpdate(NetConnection *conn, BitStream *stream); + + virtual void getWeaponMountTransform( S32 index, const MatrixF &xfm, MatrixF *outMat ); + virtual void getRenderWeaponMountTransform( F32 delta, S32 index, const MatrixF &xfm, MatrixF *outMat ); + + virtual void getImageTransform(U32 imageSlot,MatrixF* mat); + virtual void getRenderImageTransform(U32 imageSlot,MatrixF* mat,bool noEyeOffset=false); + + virtual void getImageTransform(U32 imageSlot,S32 node, MatrixF* mat); + virtual void getRenderImageTransform(U32 imageSlot,S32 node, MatrixF* mat); + + virtual void prepRenderImage( SceneRenderState* state ); + virtual void prepBatchRender( SceneRenderState *state, S32 mountedImageIndex ); + + DECLARE_CONOBJECT(TurretShape); +}; + +#endif // _TURRETSHAPE_H_ diff --git a/Engine/source/T3D/vehicles/flyingVehicle.cpp b/Engine/source/T3D/vehicles/flyingVehicle.cpp new file mode 100644 index 000000000..6cfbf6005 --- /dev/null +++ b/Engine/source/T3D/vehicles/flyingVehicle.cpp @@ -0,0 +1,804 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/vehicles/flyingVehicle.h" + +#include "app/game.h" +#include "math/mMath.h" +#include "console/simBase.h" +#include "console/console.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "collision/clippedPolyList.h" +#include "collision/planeExtractor.h" +#include "core/stream/bitStream.h" +#include "core/dnet.h" +#include "T3D/gameBase/gameConnection.h" +#include "ts/tsShapeInstance.h" +#include "T3D/fx/particleEmitter.h" +#include "sfx/sfxSystem.h" +#include "sfx/sfxProfile.h" +#include "sfx/sfxSource.h" +#include "T3D/missionArea.h" + +//---------------------------------------------------------------------------- + +const static U32 sCollisionMoveMask = ( TerrainObjectType | InteriorObjectType | + WaterObjectType | PlayerObjectType | + StaticShapeObjectType | VehicleObjectType | + VehicleBlockerObjectType ); +static U32 sServerCollisionMask = sCollisionMoveMask; // ItemObjectType +static U32 sClientCollisionMask = sCollisionMoveMask; + +static F32 sFlyingVehicleGravity = -20.0f; + +// +const char* FlyingVehicle::sJetSequence[FlyingVehicle::JetAnimCount] = +{ + "activateBack", + "maintainBack", + "activateBot", + "maintainBot", +}; + +const char* FlyingVehicleData::sJetNode[FlyingVehicleData::MaxJetNodes] = +{ + "JetNozzle0", // Thrust Forward + "JetNozzle1", + "JetNozzleX", // Thrust Backward + "JetNozzleY", + "JetNozzle2", // Thrust Downward + "JetNozzle3", + "contrail0", // Trail + "contrail1", + "contrail2", + "contrail3", +}; + +// Convert thrust direction into nodes & emitters +FlyingVehicle::JetActivation FlyingVehicle::sJetActivation[NumThrustDirections] = { + { FlyingVehicleData::ForwardJetNode, FlyingVehicleData::ForwardJetEmitter }, + { FlyingVehicleData::BackwardJetNode, FlyingVehicleData::BackwardJetEmitter }, + { FlyingVehicleData::DownwardJetNode, FlyingVehicleData::DownwardJetEmitter }, +}; + + +//---------------------------------------------------------------------------- + +IMPLEMENT_CO_DATABLOCK_V1(FlyingVehicleData); + +ConsoleDocClass( FlyingVehicleData, + "@brief Defines the properties of a FlyingVehicle.\n\n" + "@ingroup Vehicles\n" +); + +FlyingVehicleData::FlyingVehicleData() +{ + maneuveringForce = 0; + horizontalSurfaceForce = 0; + verticalSurfaceForce = 0; + autoInputDamping = 1; + steeringForce = 1; + steeringRollForce = 1; + rollForce = 1; + autoAngularForce = 0; + rotationalDrag = 0; + autoLinearForce = 0; + maxAutoSpeed = 0; + hoverHeight = 2; + createHoverHeight = 2; + maxSteeringAngle = M_PI_F; + minTrailSpeed = 1; + maxSpeed = 100; + + for (S32 k = 0; k < MaxJetNodes; k++) + jetNode[k] = -1; + + for (S32 j = 0; j < MaxJetEmitters; j++) + jetEmitter[j] = 0; + + for (S32 i = 0; i < MaxSounds; i++) + sound[i] = 0; + + vertThrustMultiple = 1.0; +} + +bool FlyingVehicleData::preload(bool server, String &errorStr) +{ + if (!Parent::preload(server, errorStr)) + return false; + + TSShapeInstance* si = new TSShapeInstance(mShape, false); + + // Resolve objects transmitted from server + if (!server) { + for (S32 i = 0; i < MaxSounds; i++) + if (sound[i]) + Sim::findObject(SimObjectId(sound[i]),sound[i]); + + for (S32 j = 0; j < MaxJetEmitters; j++) + if (jetEmitter[j]) + Sim::findObject(SimObjectId(jetEmitter[j]),jetEmitter[j]); + } + + // Extract collision planes from shape collision detail level + if (collisionDetails[0] != -1) + { + MatrixF imat(1); + PlaneExtractorPolyList polyList; + polyList.mPlaneList = &rigidBody.mPlaneList; + polyList.setTransform(&imat, Point3F(1,1,1)); + si->animate(collisionDetails[0]); + si->buildPolyList(&polyList,collisionDetails[0]); + } + + // Resolve jet nodes + for (S32 j = 0; j < MaxJetNodes; j++) + jetNode[j] = mShape->findNode(sJetNode[j]); + + // + maxSpeed = maneuveringForce / minDrag; + + delete si; + return true; +} + +void FlyingVehicleData::initPersistFields() +{ + addField( "jetSound", TYPEID< SFXProfile >(), Offset(sound[JetSound], FlyingVehicleData), + "Looping sound to play while the vehicle is jetting." ); + addField( "engineSound", TYPEID< SFXProfile >(), Offset(sound[EngineSound], FlyingVehicleData), + "Looping engine sound." ); + + addField( "maneuveringForce", TypeF32, Offset(maneuveringForce, FlyingVehicleData), + "@brief Maximum X and Y (horizontal plane) maneuvering force.\n\n" + "The actual force applied depends on the current thrust." ); + addField( "horizontalSurfaceForce", TypeF32, Offset(horizontalSurfaceForce, FlyingVehicleData), + "@brief Damping force in the opposite direction to sideways velocity.\n\n" + "Provides \"bite\" into the wind for climbing/diving and turning)." ); + addField( "verticalSurfaceForce", TypeF32, Offset(verticalSurfaceForce, FlyingVehicleData), + "@brief Damping force in the opposite direction to vertical velocity.\n\n" + "Controls side slip; lower numbers give more slide." ); + addField( "vertThrustMultiple", TypeF32, Offset(vertThrustMultiple, FlyingVehicleData), + "Multiplier applied to the jetForce (defined in VehicleData) when thrusting vertically." ); + addField( "steeringForce", TypeF32, Offset(steeringForce, FlyingVehicleData), + "@brief Maximum X and Z (sideways and vertical) steering force.\n\n" + "The actual force applied depends on the current steering input." ); + addField( "steeringRollForce", TypeF32, Offset(steeringRollForce, FlyingVehicleData), + "Roll force induced by sideways steering input value (controls how much " + "the vehicle rolls when turning)." ); + addField( "rollForce", TypeF32, Offset(rollForce, FlyingVehicleData), + "@brief Damping torque against rolling maneuvers (rotation about the y-axis), " + "proportional to linear velocity.\n\n" + "Acts to adjust roll to a stable position over time as the vehicle moves." ); + addField( "rotationalDrag", TypeF32, Offset(rotationalDrag, FlyingVehicleData), + "Rotational drag factor (slows vehicle rotation speed in all axes)." ); + + addField( "maxAutoSpeed", TypeF32, Offset(maxAutoSpeed, FlyingVehicleData), + "Maximum speed for automatic vehicle control assistance - vehicles " + "travelling at speeds above this value do not get control assitance." ); + addField( "autoInputDamping", TypeF32, Offset(autoInputDamping, FlyingVehicleData), + "@brief Scale factor applied to steering input if speed is less than " + "maxAutoSpeed to.improve handling at very low speeds.\n\n" + "Smaller values make steering less sensitive." ); + addField( "autoLinearForce", TypeF32, Offset(autoLinearForce, FlyingVehicleData), + "@brief Corrective force applied to slow the vehicle when moving at less than " + "maxAutoSpeed.\n\n" + "The force is inversely proportional to vehicle speed." ); + addField( "autoAngularForce", TypeF32, Offset(autoAngularForce, FlyingVehicleData), + "@brief Corrective torque applied to level out the vehicle when moving at less " + "than maxAutoSpeed.\n\n" + "The torque is inversely proportional to vehicle speed." ); + + addField( "hoverHeight", TypeF32, Offset(hoverHeight, FlyingVehicleData), + "The vehicle's height off the ground when at rest." ); + addField( "createHoverHeight", TypeF32, Offset(createHoverHeight, FlyingVehicleData), + "@brief The vehicle's height off the ground when useCreateHeight is active.\n\n" + "This can help avoid problems with spawning the vehicle." ); + + addField( "forwardJetEmitter",TYPEID< ParticleEmitterData >(), Offset(jetEmitter[ForwardJetEmitter], FlyingVehicleData), + "@brief Emitter to generate particles for forward jet thrust.\n\n" + "Forward jet thrust particles are emitted from model nodes JetNozzle0 " + "and JetNozzle1." ); + addField( "backwardJetEmitter",TYPEID< ParticleEmitterData >(), Offset(jetEmitter[BackwardJetEmitter], FlyingVehicleData), + "@brief Emitter to generate particles for backward jet thrust.\n\n" + "Backward jet thrust particles are emitted from model nodes JetNozzleX " + "and JetNozzleY." ); + addField( "downJetEmitter",TYPEID< ParticleEmitterData >(), Offset(jetEmitter[DownwardJetEmitter], FlyingVehicleData), + "@brief Emitter to generate particles for downward jet thrust.\n\n" + "Downward jet thrust particles are emitted from model nodes JetNozzle2 " + "and JetNozzle3." ); + addField( "trailEmitter",TYPEID< ParticleEmitterData >(), Offset(jetEmitter[TrailEmitter], FlyingVehicleData), + "Emitter to generate contrail particles from model nodes contrail0 - contrail3." ); + addField( "minTrailSpeed", TypeF32, Offset(minTrailSpeed, FlyingVehicleData), + "Minimum speed at which to start generating contrail particles." ); + + Parent::initPersistFields(); +} + +void FlyingVehicleData::packData(BitStream* stream) +{ + Parent::packData(stream); + + for (S32 i = 0; i < MaxSounds; i++) + { + if (stream->writeFlag(sound[i])) + { + SimObjectId writtenId = packed ? SimObjectId(sound[i]) : sound[i]->getId(); + stream->writeRangedU32(writtenId, DataBlockObjectIdFirst, DataBlockObjectIdLast); + } + } + + for (S32 j = 0; j < MaxJetEmitters; j++) + { + if (stream->writeFlag(jetEmitter[j])) + { + SimObjectId writtenId = packed ? SimObjectId(jetEmitter[j]) : jetEmitter[j]->getId(); + stream->writeRangedU32(writtenId, DataBlockObjectIdFirst,DataBlockObjectIdLast); + } + } + + stream->write(maneuveringForce); + stream->write(horizontalSurfaceForce); + stream->write(verticalSurfaceForce); + stream->write(autoInputDamping); + stream->write(steeringForce); + stream->write(steeringRollForce); + stream->write(rollForce); + stream->write(autoAngularForce); + stream->write(rotationalDrag); + stream->write(autoLinearForce); + stream->write(maxAutoSpeed); + stream->write(hoverHeight); + stream->write(createHoverHeight); + stream->write(minTrailSpeed); + stream->write(vertThrustMultiple); +} + +void FlyingVehicleData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + + for (S32 i = 0; i < MaxSounds; i++) { + sound[i] = NULL; + if (stream->readFlag()) + sound[i] = (SFXProfile*)stream->readRangedU32(DataBlockObjectIdFirst, + DataBlockObjectIdLast); + } + + for (S32 j = 0; j < MaxJetEmitters; j++) { + jetEmitter[j] = NULL; + if (stream->readFlag()) + jetEmitter[j] = (ParticleEmitterData*)stream->readRangedU32(DataBlockObjectIdFirst, + DataBlockObjectIdLast); + } + + stream->read(&maneuveringForce); + stream->read(&horizontalSurfaceForce); + stream->read(&verticalSurfaceForce); + stream->read(&autoInputDamping); + stream->read(&steeringForce); + stream->read(&steeringRollForce); + stream->read(&rollForce); + stream->read(&autoAngularForce); + stream->read(&rotationalDrag); + stream->read(&autoLinearForce); + stream->read(&maxAutoSpeed); + stream->read(&hoverHeight); + stream->read(&createHoverHeight); + stream->read(&minTrailSpeed); + stream->read(&vertThrustMultiple); +} + + +//---------------------------------------------------------------------------- + +IMPLEMENT_CO_NETOBJECT_V1(FlyingVehicle); + +ConsoleDocClass( FlyingVehicle, + "@brief A flying vehicle.\n\n" + "@ingroup Vehicles\n" +); + +FlyingVehicle::FlyingVehicle() +{ + mSteering.set(0,0); + mThrottle = 0; + mJetting = false; + + mJetSound = 0; + mEngineSound = 0; + + mBackMaintainOn = false; + mBottomMaintainOn = false; + createHeightOn = false; + + for (S32 i = 0; i < JetAnimCount; i++) + mJetThread[i] = 0; +} + +FlyingVehicle::~FlyingVehicle() +{ +} + + +//---------------------------------------------------------------------------- + +bool FlyingVehicle::onAdd() +{ + if(!Parent::onAdd()) + return false; + + addToScene(); + + if (isServerObject()) + scriptOnAdd(); + return true; +} + +bool FlyingVehicle::onNewDataBlock(GameBaseData* dptr, bool reload) +{ + mDataBlock = dynamic_cast(dptr); + if (!mDataBlock || !Parent::onNewDataBlock(dptr,reload)) + return false; + + // Sounds + if ( isGhost() ) + { + // Create the sounds ahead of time. This reduces runtime + // costs and makes the system easier to understand. + + SFX_DELETE( mJetSound ); + SFX_DELETE( mEngineSound ); + + if ( mDataBlock->sound[FlyingVehicleData::EngineSound] ) + mEngineSound = SFX->createSource( mDataBlock->sound[FlyingVehicleData::EngineSound], &getTransform() ); + + if ( mDataBlock->sound[FlyingVehicleData::JetSound] ) + mJetSound = SFX->createSource( mDataBlock->sound[FlyingVehicleData::JetSound], &getTransform() ); + } + + // Jet Sequences + for (S32 i = 0; i < JetAnimCount; i++) { + TSShape const* shape = mShapeInstance->getShape(); + mJetSeq[i] = shape->findSequence(sJetSequence[i]); + if (mJetSeq[i] != -1) { + if (i == BackActivate || i == BottomActivate) { + mJetThread[i] = mShapeInstance->addThread(); + mShapeInstance->setSequence(mJetThread[i],mJetSeq[i],0); + mShapeInstance->setTimeScale(mJetThread[i],0); + } + } + else + mJetThread[i] = 0; + } + + scriptOnNewDataBlock(); + return true; +} + +void FlyingVehicle::onRemove() +{ + SFX_DELETE( mJetSound ); + SFX_DELETE( mEngineSound ); + + scriptOnRemove(); + removeFromScene(); + Parent::onRemove(); +} + + +//---------------------------------------------------------------------------- + +void FlyingVehicle::advanceTime(F32 dt) +{ + Parent::advanceTime(dt); + + updateEngineSound(1); + updateJet(dt); +} + + +//---------------------------------------------------------------------------- + +void FlyingVehicle::updateMove(const Move* move) +{ + PROFILE_SCOPE( FlyingVehicle_UpdateMove ); + + Parent::updateMove(move); + + if (move == &NullMove) + mSteering.set(0,0); + + F32 speed = mRigid.linVelocity.len(); + if (speed < mDataBlock->maxAutoSpeed) + mSteering *= mDataBlock->autoInputDamping; + + // Check the mission area to get the factor for the flight ceiling + MissionArea * obj = MissionArea::getServerObject(); + mCeilingFactor = 1.0f; + if (obj != NULL) + { + F32 flightCeiling = obj->getFlightCeiling(); + F32 ceilingRange = obj->getFlightCeilingRange(); + + if (mRigid.linPosition.z > flightCeiling) + { + // Thrust starts to fade at the ceiling, and is 0 at ceil + range + if (ceilingRange == 0) + { + mCeilingFactor = 0; + } + else + { + mCeilingFactor = 1.0f - ((mRigid.linPosition.z - flightCeiling) / (flightCeiling + ceilingRange)); + if (mCeilingFactor < 0.0f) + mCeilingFactor = 0.0f; + } + } + } + + mThrust.x = move->x; + mThrust.y = move->y; + + if (mThrust.y != 0.0f) + if (mThrust.y > 0) + mThrustDirection = ThrustForward; + else + mThrustDirection = ThrustBackward; + else + mThrustDirection = ThrustDown; + + if (mCeilingFactor != 1.0f) + mJetting = false; +} + + +//---------------------------------------------------------------------------- + +void FlyingVehicle::updateForces(F32 /*dt*/) +{ + PROFILE_SCOPE( FlyingVehicle_UpdateForces ); + + MatrixF currPosMat; + mRigid.getTransform(&currPosMat); + mRigid.atRest = false; + + Point3F massCenter; + currPosMat.mulP(mDataBlock->massCenter,&massCenter); + + Point3F xv,yv,zv; + currPosMat.getColumn(0,&xv); + currPosMat.getColumn(1,&yv); + currPosMat.getColumn(2,&zv); + F32 speed = mRigid.linVelocity.len(); + + Point3F force = Point3F(0, 0, sFlyingVehicleGravity * mRigid.mass * mGravityMod); + Point3F torque = Point3F(0, 0, 0); + + // Drag at any speed + force -= mRigid.linVelocity * mDataBlock->minDrag; + torque -= mRigid.angMomentum * mDataBlock->rotationalDrag; + + // Auto-stop at low speeds + if (speed < mDataBlock->maxAutoSpeed) { + F32 autoScale = 1 - speed / mDataBlock->maxAutoSpeed; + + // Gyroscope + F32 gf = mDataBlock->autoAngularForce * autoScale; + torque -= xv * gf * mDot(yv,Point3F(0,0,1)); + + // Manuevering jets + F32 sf = mDataBlock->autoLinearForce * autoScale; + force -= yv * sf * mDot(yv, mRigid.linVelocity); + force -= xv * sf * mDot(xv, mRigid.linVelocity); + } + + // Hovering Jet + F32 vf = -sFlyingVehicleGravity * mRigid.mass * mGravityMod; + F32 h = getHeight(); + if (h <= 1) { + if (h > 0) { + vf -= vf * h * 0.1; + } else { + vf += mDataBlock->jetForce * -h; + } + } + force += zv * vf; + + // Damping "surfaces" + force -= xv * mDot(xv,mRigid.linVelocity) * mDataBlock->horizontalSurfaceForce; + force -= zv * mDot(zv,mRigid.linVelocity) * mDataBlock->verticalSurfaceForce; + + // Turbo Jet + if (mJetting) { + if (mThrustDirection == ThrustForward) + force += yv * mDataBlock->jetForce * mCeilingFactor; + else if (mThrustDirection == ThrustBackward) + force -= yv * mDataBlock->jetForce * mCeilingFactor; + else + force += zv * mDataBlock->jetForce * mDataBlock->vertThrustMultiple * mCeilingFactor; + } + + // Maneuvering jets + force += yv * (mThrust.y * mDataBlock->maneuveringForce * mCeilingFactor); + force += xv * (mThrust.x * mDataBlock->maneuveringForce * mCeilingFactor); + + // Steering + Point2F steering; + steering.x = mSteering.x / mDataBlock->maxSteeringAngle; + steering.x *= mFabs(steering.x); + steering.y = mSteering.y / mDataBlock->maxSteeringAngle; + steering.y *= mFabs(steering.y); + torque -= xv * steering.y * mDataBlock->steeringForce; + torque -= zv * steering.x * mDataBlock->steeringForce; + + // Roll + torque += yv * steering.x * mDataBlock->steeringRollForce; + F32 ar = mDataBlock->autoAngularForce * mDot(xv,Point3F(0,0,1)); + ar -= mDataBlock->rollForce * mDot(xv, mRigid.linVelocity); + torque += yv * ar; + + // Add in force from physical zones... + force += mAppliedForce; + + // Container buoyancy & drag + force -= Point3F(0, 0, 1) * (mBuoyancy * sFlyingVehicleGravity * mRigid.mass * mGravityMod); + force -= mRigid.linVelocity * mDrag; + + // + mRigid.force = force; + mRigid.torque = torque; +} + + +//---------------------------------------------------------------------------- + +F32 FlyingVehicle::getHeight() +{ + Point3F sp,ep; + RayInfo collision; + F32 height = (createHeightOn) ? mDataBlock->createHoverHeight : mDataBlock->hoverHeight; + F32 r = 10 + height; + getTransform().getColumn(3, &sp); + ep.x = sp.x; + ep.y = sp.y; + ep.z = sp.z - r; + disableCollision(); + if( !mContainer->castRay(sp, ep, sClientCollisionMask, &collision) == true ) + collision.t = 1; + enableCollision(); + return (r * collision.t - height) / 10; +} + + +//---------------------------------------------------------------------------- +U32 FlyingVehicle::getCollisionMask() +{ + if (isServerObject()) + return sServerCollisionMask; + else + return sClientCollisionMask; +} + +//---------------------------------------------------------------------------- + +void FlyingVehicle::updateEngineSound(F32 level) +{ + if ( !mEngineSound ) + return; + + if ( !mEngineSound->isPlaying() ) + mEngineSound->play(); + + mEngineSound->setTransform( getTransform() ); + mEngineSound->setVelocity( getVelocity() ); + + mEngineSound->setPitch( level ); +} + +void FlyingVehicle::updateJet(F32 dt) +{ + // Thrust Animation threads + // Back + if (mJetSeq[BackActivate] >=0 ) { + if(!mBackMaintainOn || mThrustDirection != ThrustForward) { + if(mBackMaintainOn) { + mShapeInstance->setPos(mJetThread[BackActivate], 1); + mShapeInstance->destroyThread(mJetThread[BackMaintain]); + mBackMaintainOn = false; + } + mShapeInstance->setTimeScale(mJetThread[BackActivate], + (mThrustDirection == ThrustForward)? 1.0f : -1.0f); + mShapeInstance->advanceTime(dt,mJetThread[BackActivate]); + } + if(mJetSeq[BackMaintain] >= 0 && !mBackMaintainOn && + mShapeInstance->getPos(mJetThread[BackActivate]) >= 1.0) { + mShapeInstance->setPos(mJetThread[BackActivate], 0); + mShapeInstance->setTimeScale(mJetThread[BackActivate], 0); + mJetThread[BackMaintain] = mShapeInstance->addThread(); + mShapeInstance->setSequence(mJetThread[BackMaintain],mJetSeq[BackMaintain],0); + mShapeInstance->setTimeScale(mJetThread[BackMaintain],1); + mBackMaintainOn = true; + } + if(mBackMaintainOn) + mShapeInstance->advanceTime(dt,mJetThread[BackMaintain]); + } + + // Thrust Animation threads + // Bottom + if (mJetSeq[BottomActivate] >=0 ) { + if(!mBottomMaintainOn || mThrustDirection != ThrustDown || !mJetting) { + if(mBottomMaintainOn) { + mShapeInstance->setPos(mJetThread[BottomActivate], 1); + mShapeInstance->destroyThread(mJetThread[BottomMaintain]); + mBottomMaintainOn = false; + } + mShapeInstance->setTimeScale(mJetThread[BottomActivate], + (mThrustDirection == ThrustDown && mJetting)? 1.0f : -1.0f); + mShapeInstance->advanceTime(dt,mJetThread[BottomActivate]); + } + if(mJetSeq[BottomMaintain] >= 0 && !mBottomMaintainOn && + mShapeInstance->getPos(mJetThread[BottomActivate]) >= 1.0) { + mShapeInstance->setPos(mJetThread[BottomActivate], 0); + mShapeInstance->setTimeScale(mJetThread[BottomActivate], 0); + mJetThread[BottomMaintain] = mShapeInstance->addThread(); + mShapeInstance->setSequence(mJetThread[BottomMaintain],mJetSeq[BottomMaintain],0); + mShapeInstance->setTimeScale(mJetThread[BottomMaintain],1); + mBottomMaintainOn = true; + } + if(mBottomMaintainOn) + mShapeInstance->advanceTime(dt,mJetThread[BottomMaintain]); + } + + // Jet particles + for (S32 j = 0; j < NumThrustDirections; j++) { + JetActivation& jet = sJetActivation[j]; + updateEmitter(mJetting && j == mThrustDirection,dt,mDataBlock->jetEmitter[jet.emitter], + jet.node,FlyingVehicleData::MaxDirectionJets); + } + + // Trail jets + Point3F yv; + mObjToWorld.getColumn(1,&yv); + F32 speed = mFabs(mDot(yv,mRigid.linVelocity)); + F32 trail = 0; + if (speed > mDataBlock->minTrailSpeed) { + trail = dt; + if (speed < mDataBlock->maxSpeed) + trail *= (speed - mDataBlock->minTrailSpeed) / mDataBlock->maxSpeed; + } + updateEmitter(trail,trail,mDataBlock->jetEmitter[FlyingVehicleData::TrailEmitter], + FlyingVehicleData::TrailNode,FlyingVehicleData::MaxTrails); + + // Allocate/Deallocate voice on demand. + if ( !mJetSound ) + return; + + if ( !mJetting ) + mJetSound->stop(); + else + { + if ( !mJetSound->isPlaying() ) + mJetSound->play(); + + mJetSound->setTransform( getTransform() ); + mJetSound->setVelocity( getVelocity() ); + } +} + +//---------------------------------------------------------------------------- + +void FlyingVehicle::updateEmitter(bool active,F32 dt,ParticleEmitterData *emitter,S32 idx,S32 count) +{ + if (!emitter) + return; + for (S32 j = idx; j < idx + count; j++) + if (active) { + if (mDataBlock->jetNode[j] != -1) { + if (!bool(mJetEmitter[j])) { + mJetEmitter[j] = new ParticleEmitter; + mJetEmitter[j]->onNewDataBlock(emitter,false); + mJetEmitter[j]->registerObject(); + } + MatrixF mat; + Point3F pos,axis; + mat.mul(getRenderTransform(), + mShapeInstance->mNodeTransforms[mDataBlock->jetNode[j]]); + mat.getColumn(1,&axis); + mat.getColumn(3,&pos); + mJetEmitter[j]->emitParticles(pos,true,axis,getVelocity(),(U32)(dt * 1000)); + } + } + else { + for (S32 j = idx; j < idx + count; j++) + if (bool(mJetEmitter[j])) { + mJetEmitter[j]->deleteWhenEmpty(); + mJetEmitter[j] = 0; + } + } +} + + +//---------------------------------------------------------------------------- + +void FlyingVehicle::writePacketData(GameConnection *connection, BitStream *stream) +{ + Parent::writePacketData(connection, stream); +} + +void FlyingVehicle::readPacketData(GameConnection *connection, BitStream *stream) +{ + Parent::readPacketData(connection, stream); + + setPosition(mRigid.linPosition,mRigid.angPosition); + mDelta.pos = mRigid.linPosition; + mDelta.rot[1] = mRigid.angPosition; +} + +U32 FlyingVehicle::packUpdate(NetConnection *con, U32 mask, BitStream *stream) +{ + U32 retMask = Parent::packUpdate(con, mask, stream); + + // The rest of the data is part of the control object packet update. + // If we're controlled by this client, we don't need to send it. + if(stream->writeFlag(getControllingClient() == con && !(mask & InitialUpdateMask))) + return retMask; + + stream->writeFlag(createHeightOn); + + stream->writeInt(mThrustDirection,NumThrustBits); + + return retMask; +} + +void FlyingVehicle::unpackUpdate(NetConnection *con, BitStream *stream) +{ + Parent::unpackUpdate(con,stream); + + if(stream->readFlag()) + return; + + createHeightOn = stream->readFlag(); + + mThrustDirection = ThrustDirection(stream->readInt(NumThrustBits)); +} + +void FlyingVehicle::initPersistFields() +{ + Parent::initPersistFields(); +} + +DefineEngineMethod( FlyingVehicle, useCreateHeight, void, ( bool enabled ),, + "@brief Set whether the vehicle should temporarily use the createHoverHeight " + "specified in the datablock.\n\nThis can help avoid problems with spawning.\n" + "@param enabled true to use the datablock createHoverHeight, false otherwise\n" ) +{ + object->useCreateHeight( enabled ); +} + +void FlyingVehicle::useCreateHeight(bool val) +{ + createHeightOn = val; + setMaskBits(HoverHeight); +} diff --git a/Engine/source/T3D/vehicles/flyingVehicle.h b/Engine/source/T3D/vehicles/flyingVehicle.h new file mode 100644 index 000000000..2791af467 --- /dev/null +++ b/Engine/source/T3D/vehicles/flyingVehicle.h @@ -0,0 +1,200 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FLYINGVEHICLE_H_ +#define _FLYINGVEHICLE_H_ + +#ifndef _VEHICLE_H_ +#include "T3D/vehicles/vehicle.h" +#endif + +#ifndef _CLIPPEDPOLYLIST_H_ +#include "collision/clippedPolyList.h" +#endif + +class ParticleEmitter; +class ParticleEmitterData; + + +//---------------------------------------------------------------------------- + +struct FlyingVehicleData: public VehicleData { + typedef VehicleData Parent; + + enum Sounds { + JetSound, + EngineSound, + MaxSounds, + }; + SFXProfile* sound[MaxSounds]; + + enum Jets { + // These enums index into a static name list. + ForwardJetEmitter, // Thrust forward + BackwardJetEmitter, // Thrust backward + DownwardJetEmitter, // Thrust down + TrailEmitter, // Contrail + MaxJetEmitters, + }; + ParticleEmitterData* jetEmitter[MaxJetEmitters]; + F32 minTrailSpeed; + + // + F32 maneuveringForce; + F32 horizontalSurfaceForce; + F32 verticalSurfaceForce; + F32 autoInputDamping; + F32 steeringForce; + F32 steeringRollForce; + F32 rollForce; + F32 autoAngularForce; + F32 rotationalDrag; + F32 maxAutoSpeed; + F32 autoLinearForce; + F32 hoverHeight; + F32 createHoverHeight; + + F32 vertThrustMultiple; + + // Initialized in preload + ClippedPolyList rigidBody; + S32 surfaceCount; + F32 maxSpeed; + + enum JetNodes { + // These enums index into a static name list. + ForwardJetNode, + ForwardJetNode1, + BackwardJetNode, + BackwardJetNode1, + DownwardJetNode, + DownwardJetNode1, + // + TrailNode, + TrailNode1, + TrailNode2, + TrailNode3, + // + MaxJetNodes, + MaxDirectionJets = 2, + ThrustJetStart = ForwardJetNode, + NumThrustJets = TrailNode, + MaxTrails = 4, + }; + static const char *sJetNode[MaxJetNodes]; + S32 jetNode[MaxJetNodes]; + + // + FlyingVehicleData(); + DECLARE_CONOBJECT(FlyingVehicleData); + static void initPersistFields(); + bool preload(bool server, String &errorStr); + void packData(BitStream* stream); + void unpackData(BitStream* stream); +}; + + +//---------------------------------------------------------------------------- + +class FlyingVehicle: public Vehicle +{ + typedef Vehicle Parent; + + FlyingVehicleData* mDataBlock; + + SFXSource* mJetSound; + SFXSource* mEngineSound; + + enum NetMaskBits { + InitMask = BIT(0), + HoverHeight = BIT(1) + }; + bool createHeightOn; + F32 mCeilingFactor; + + enum ThrustDirection { + // Enums index into sJetActivationTable + ThrustForward, + ThrustBackward, + ThrustDown, + NumThrustDirections, + NumThrustBits = 3 + }; + Point2F mThrust; + ThrustDirection mThrustDirection; + + // Jet Threads + enum Jets { + // These enums index into a static name list. + BackActivate, + BackMaintain, + BottomActivate, + BottomMaintain, + JetAnimCount + }; + static const char* sJetSequence[FlyingVehicle::JetAnimCount]; + TSThread* mJetThread[JetAnimCount]; + S32 mJetSeq[JetAnimCount]; + bool mBackMaintainOn; + bool mBottomMaintainOn; + // Jet Particles + struct JetActivation { + // Convert thrust direction into nodes & emitters + S32 node; + S32 emitter; + }; + static JetActivation sJetActivation[NumThrustDirections]; + SimObjectPtr mJetEmitter[FlyingVehicleData::MaxJetNodes]; + + // + bool onNewDataBlock(GameBaseData* dptr,bool reload); + void updateMove(const Move *move); + void updateForces(F32); +// bool collideBody(const MatrixF& mat,Collision* info); + F32 getHeight(); + + // Client sounds & particles + void updateJet(F32 dt); + void updateEngineSound(F32 level); + void updateEmitter(bool active,F32 dt,ParticleEmitterData *emitter,S32 idx,S32 count); + + U32 getCollisionMask(); + public: + DECLARE_CONOBJECT(FlyingVehicle); + static void initPersistFields(); + + FlyingVehicle(); + ~FlyingVehicle(); + + bool onAdd(); + void onRemove(); + void advanceTime(F32 dt); + + void writePacketData(GameConnection *conn, BitStream *stream); + void readPacketData(GameConnection *conn, BitStream *stream); + U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream); + void unpackUpdate(NetConnection *conn, BitStream *stream); + void useCreateHeight(bool val); +}; + + +#endif diff --git a/Engine/source/T3D/vehicles/guiSpeedometer.cpp b/Engine/source/T3D/vehicles/guiSpeedometer.cpp new file mode 100644 index 000000000..efd30ef27 --- /dev/null +++ b/Engine/source/T3D/vehicles/guiSpeedometer.cpp @@ -0,0 +1,212 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/controls/guiBitmapCtrl.h" +#include "console/consoleTypes.h" +#include "T3D/gameBase/gameConnection.h" +#include "T3D/vehicles/vehicle.h" +#include "gfx/primBuilder.h" + +//----------------------------------------------------------------------------- +/// A Speedometer control. +/// This gui displays the speed of the current Vehicle based +/// control object. This control only works if a server +/// connection exists and its control object is a vehicle. If +/// either of these requirements is false, the control is not rendered. +class GuiSpeedometerHud : public GuiBitmapCtrl +{ + typedef GuiBitmapCtrl Parent; + + F32 mSpeed; ///< Current speed + F32 mMaxSpeed; ///< Max speed at max need pos + F32 mMaxAngle; ///< Max pos of needle + F32 mMinAngle; ///< Min pos of needle + Point2F mCenter; ///< Center of needle rotation + ColorF mColor; ///< Needle Color + F32 mNeedleLength; + F32 mNeedleWidth; + F32 mTailLength; + + GFXStateBlockRef mBlendSB; + +public: + GuiSpeedometerHud(); + + void onRender( Point2I, const RectI &); + static void initPersistFields(); + DECLARE_CONOBJECT( GuiSpeedometerHud ); + DECLARE_CATEGORY( "Gui Game" ); + DECLARE_DESCRIPTION( "Displays the speed of the current Vehicle-based control object." ); +}; + + +//----------------------------------------------------------------------------- + +IMPLEMENT_CONOBJECT( GuiSpeedometerHud ); + +ConsoleDocClass( GuiSpeedometerHud, + "@brief Displays the speed of the current Vehicle based control object.\n\n" + + "This control only works if a server connection exists, and its control " + "object is a Vehicle derived class. If either of these requirements is false, " + "the control is not rendered.
      " + + "The control renders the speedometer needle as a colored quad, rotated to " + "indicate the Vehicle speed as determined by the minAngle, " + "maxAngle, and maxSpeed properties. This control is normally " + "placed on top of a GuiBitmapCtrl representing the speedometer dial.\n\n" + + "@tsexample\n" + "new GuiSpeedometerHud()\n" + "{\n" + " maxSpeed = \"100\";\n" + " minAngle = \"215\";\n" + " maxAngle = \"0\";\n" + " color = \"1 0.3 0.3 1\";\n" + " center = \"130 123\";\n" + " length = \"100\";\n" + " width = \"2\";\n" + " tail = \"0\";\n" + " //Properties not specific to this control have been omitted from this example.\n" + "};\n" + "@endtsexample\n\n" + + "@ingroup GuiContainers" +); + +GuiSpeedometerHud::GuiSpeedometerHud() +{ + mSpeed = 0; + mMaxSpeed = 100; + mMaxAngle = 0; + mMinAngle = 200; + mCenter.set(0,0); + mNeedleWidth = 3; + mNeedleLength = 10; + mTailLength = 5; + mColor.set(1,0,0,1); +} + +void GuiSpeedometerHud::initPersistFields() +{ + addGroup("Needle"); + + addField("maxSpeed", TypeF32, Offset( mMaxSpeed, GuiSpeedometerHud ), + "Maximum Vehicle speed (in Torque units per second) to represent on the " + "speedo (Vehicle speeds greater than this are clamped to maxSpeed)." ); + + addField("minAngle", TypeF32, Offset( mMinAngle, GuiSpeedometerHud ), + "Angle (in radians) of the needle when the Vehicle speed is 0. An angle " + "of 0 points right, 90 points up etc)." ); + + addField("maxAngle", TypeF32, Offset( mMaxAngle, GuiSpeedometerHud ), + "Angle (in radians) of the needle when the Vehicle speed is >= maxSpeed. " + "An angle of 0 points right, 90 points up etc)." ); + + addField("color", TypeColorF, Offset( mColor, GuiSpeedometerHud ), + "Color of the needle" ); + + addField("center", TypePoint2F, Offset( mCenter, GuiSpeedometerHud ), + "Center of the needle, offset from the GuiSpeedometerHud control top " + "left corner" ); + + addField("length", TypeF32, Offset( mNeedleLength, GuiSpeedometerHud ), + "Length of the needle from center to end" ); + + addField("width", TypeF32, Offset( mNeedleWidth, GuiSpeedometerHud ), + "Width of the needle" ); + + addField("tail", TypeF32, Offset( mTailLength, GuiSpeedometerHud ), + "Length of the needle from center to tail" ); + + endGroup("Needle"); + + Parent::initPersistFields(); +} + + +//----------------------------------------------------------------------------- +/** + Gui onRender method. + Renders a health bar with filled background and border. +*/ +void GuiSpeedometerHud::onRender(Point2I offset, const RectI &updateRect) +{ + // Must have a connection and player control object + GameConnection* conn = GameConnection::getConnectionToServer(); + if (!conn) + return; + Vehicle* control = dynamic_cast(conn->getControlObject()); + if (!control) + return; + + Parent::onRender(offset,updateRect); + + // Use the vehicle's velocity as its speed... + mSpeed = control->getVelocity().len(); + if (mSpeed > mMaxSpeed) + mSpeed = mMaxSpeed; + + // Render the needle + GFX->pushWorldMatrix(); + Point2F center = mCenter; + if (mIsZero(center.x) && mIsZero(center.y)) + { + center.x = getExtent().x / 2.0f; + center.y = getExtent().y / 2.0f; + } + MatrixF newMat(1); + + newMat.setPosition(Point3F(getLeft() + center.x, getTop() + center.y, 0.0f)); + + F32 rotation = mMinAngle + (mMaxAngle - mMinAngle) * (mSpeed / mMaxSpeed); + AngAxisF newRot(Point3F(0.0f,0.0f,-1.0f), rotation); + + newRot.setMatrix(&newMat); + + if (mBlendSB.isNull()) + { + GFXStateBlockDesc desc; + desc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha); + desc.samplersDefined = true; + desc.samplers[0].textureColorOp = GFXTOPDisable; + mBlendSB = GFX->createStateBlock(desc); + } + + GFX->setStateBlock(mBlendSB); + + GFX->setTexture(0, NULL); + + PrimBuild::begin(GFXLineStrip, 5); + PrimBuild::color4f(mColor.red, mColor.green, mColor.blue, mColor.alpha); + + PrimBuild::vertex2f(+mNeedleLength,-mNeedleWidth); + PrimBuild::vertex2f(+mNeedleLength,+mNeedleWidth); + PrimBuild::vertex2f(-mTailLength ,+mNeedleWidth); + PrimBuild::vertex2f(-mTailLength ,-mNeedleWidth); + + //// Get back to the start! + PrimBuild::vertex2f(+mNeedleLength,-mNeedleWidth); + + PrimBuild::end(); +} + diff --git a/Engine/source/T3D/vehicles/hoverVehicle.cpp b/Engine/source/T3D/vehicles/hoverVehicle.cpp new file mode 100644 index 000000000..6ccfa7053 --- /dev/null +++ b/Engine/source/T3D/vehicles/hoverVehicle.cpp @@ -0,0 +1,976 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/vehicles/hoverVehicle.h" + +#include "core/stream/bitStream.h" +#include "scene/sceneRenderState.h" +#include "collision/clippedPolyList.h" +#include "collision/planeExtractor.h" +#include "T3D/gameBase/moveManager.h" +#include "ts/tsShapeInstance.h" +#include "console/consoleTypes.h" +#include "scene/sceneManager.h" +#include "sfx/sfxSystem.h" +#include "sfx/sfxProfile.h" +#include "sfx/sfxSource.h" +#include "T3D/fx/particleEmitter.h" +#include "math/mathIO.h" + + +IMPLEMENT_CO_DATABLOCK_V1(HoverVehicleData); +IMPLEMENT_CO_NETOBJECT_V1(HoverVehicle); + +ConsoleDocClass( HoverVehicleData, + "@brief Defines the properties of a HoverVehicle.\n\n" + "@ingroup Vehicles\n" +); + +ConsoleDocClass( HoverVehicle, + "@brief A hovering vehicle.\n\n" + "A hover vehicle is a vehicle that maintains a specific distance between the " + "vehicle and the ground at all times; unlike a flying vehicle which is free " + "to ascend and descend at will." + "The model used for the HoverVehicle has the following requirements:\n" + "
      " + "
      Collision mesh
      A convex collision mesh at detail size -1.
      " + "
      JetNozzle0-1 nodes
      Particle emitter nodes used when thrusting " + "forward.
      " + "
      JetNozzle2-3 nodes
      Particle emitter nodes used when thrusting " + "downward.
      " + "
      JetNozzleX node
      Particle emitter node used when thrusting " + "backward.
      " + "
      activateBack animation
      Non-cyclic animation sequence played " + "when the vehicle begins thrusting forwards.
      " + "
      maintainBack animation
      Cyclic animation sequence played after " + "activateBack when the vehicle continues thrusting forwards.
      " + "
      " + "@ingroup Vehicles\n" +); + +namespace { + +const U32 sIntergrationsPerTick = 1; +const F32 sHoverVehicleGravity = -20; + +const U32 sCollisionMoveMask = (TerrainObjectType | InteriorObjectType | + PlayerObjectType | StaticShapeObjectType | + VehicleObjectType | VehicleBlockerObjectType); + +const U32 sServerCollisionMask = sCollisionMoveMask; // ItemObjectType +const U32 sClientCollisionMask = sCollisionMoveMask; + +void nonFilter(SceneObject* object,void *key) +{ + SceneContainer::CallbackInfo* info = reinterpret_cast(key); + object->buildPolyList(info->context,info->polyList,info->boundingBox,info->boundingSphere); +} + +} // namespace {} + +const char* HoverVehicle::sJetSequence[HoverVehicle::JetAnimCount] = +{ + "activateBack", + "maintainBack", +}; + +const char* HoverVehicleData::sJetNode[HoverVehicleData::MaxJetNodes] = +{ + "JetNozzle0", // Thrust Forward + "JetNozzle1", + "JetNozzleX", // Thrust Backward + "JetNozzleX", + "JetNozzle2", // Thrust Downward + "JetNozzle3", +}; + +// Convert thrust direction into nodes & emitters +HoverVehicle::JetActivation HoverVehicle::sJetActivation[NumThrustDirections] = { + { HoverVehicleData::ForwardJetNode, HoverVehicleData::ForwardJetEmitter }, + { HoverVehicleData::BackwardJetNode, HoverVehicleData::BackwardJetEmitter }, + { HoverVehicleData::DownwardJetNode, HoverVehicleData::DownwardJetEmitter }, +}; + +//-------------------------------------------------------------------------- +//-------------------------------------- +// +HoverVehicleData::HoverVehicleData() +{ + dragForce = 0; + vertFactor = 0.25f; + floatingThrustFactor = 0.15f; + + mainThrustForce = 0; + reverseThrustForce = 0; + strafeThrustForce = 0; + turboFactor = 1.0f; + + stabLenMin = 0.5f; + stabLenMax = 2.0f; + stabSpringConstant = 30; + stabDampingConstant = 10; + + gyroDrag = 10; + normalForce = 30; + restorativeForce = 10; + steeringForce = 25; + rollForce = 2.5f; + pitchForce = 2.5f; + + dustTrailEmitter = NULL; + dustTrailID = 0; + dustTrailOffset.set( 0.0f, 0.0f, 0.0f ); + dustTrailFreqMod = 15.0f; + triggerTrailHeight = 2.5f; + + floatingGravMag = 1; + brakingForce = 0; + brakingActivationSpeed = 0; + + for (S32 k = 0; k < MaxJetNodes; k++) + jetNode[k] = -1; + + for (S32 j = 0; j < MaxJetEmitters; j++) + jetEmitter[j] = 0; + + for (S32 i = 0; i < MaxSounds; i++) + sound[i] = 0; +} + +HoverVehicleData::~HoverVehicleData() +{ + +} + + +//-------------------------------------------------------------------------- +void HoverVehicleData::initPersistFields() +{ + addField( "dragForce", TypeF32, Offset(dragForce, HoverVehicleData), + "Drag force factor that acts opposite to the vehicle velocity.\nAlso " + "used to determnine the vehicle's maxThrustSpeed.\n@see mainThrustForce" ); + addField( "vertFactor", TypeF32, Offset(vertFactor, HoverVehicleData), + "Scalar applied to the vertical portion of the velocity drag acting on " + "the vehicle.\nFor the horizontal (X and Y) components of velocity drag, " + "a factor of 0.25 is applied when the vehicle is floating, and a factor " + "of 1.0 is applied when the vehicle is not floating. This velocity drag " + "is multiplied by the vehicle's dragForce, as defined above, and the " + "result is subtracted from it's movement force.\n" + "@note The vertFactor must be between 0.0 and 1.0 (inclusive)." ); + addField( "floatingThrustFactor", TypeF32, Offset(floatingThrustFactor, HoverVehicleData), + "Scalar applied to the vehicle's thrust force when the vehicle is floating.\n" + "@note The floatingThrustFactor must be between 0.0 and 1.0 (inclusive)." ); + addField( "mainThrustForce", TypeF32, Offset(mainThrustForce, HoverVehicleData), + "Force generated by thrusting the vehicle forward.\nAlso used to determine " + "the maxThrustSpeed:\n\n" + "@tsexample\n" + "maxThrustSpeed = (mainThrustForce + strafeThrustForce) / dragForce;\n" + "@endtsexample\n" ); + addField( "reverseThrustForce", TypeF32, Offset(reverseThrustForce, HoverVehicleData), + "Force generated by thrusting the vehicle backward." ); + addField( "strafeThrustForce", TypeF32, Offset(strafeThrustForce, HoverVehicleData), + "Force generated by thrusting the vehicle to one side.\nAlso used to " + "determine the vehicle's maxThrustSpeed.\n@see mainThrustForce" ); + addField( "turboFactor", TypeF32, Offset(turboFactor, HoverVehicleData), + "Scale factor applied to the vehicle's thrust force when jetting." ); + + addField( "stabLenMin", TypeF32, Offset(stabLenMin, HoverVehicleData), + "Length of the base stabalizer when travelling at minimum speed (0).\n" + "Each tick, the vehicle performs 2 raycasts (from the center back and " + "center front of the vehicle) to check for contact with the ground. The " + "base stabalizer length determines the length of that raycast; if " + "neither raycast hit the ground, the vehicle is floating, stabalizer " + "spring and ground normal forces are not applied.\n\n" + "\n" + "@see stabSpringConstant" ); + addField( "stabLenMax", TypeF32, Offset(stabLenMax, HoverVehicleData), + "Length of the base stabalizer when travelling at maximum speed " + "(maxThrustSpeed).\n\n@see stabLenMin\n\n@see mainThrustForce" ); + + addField( "stabSpringConstant", TypeF32, Offset(stabSpringConstant, HoverVehicleData), + "Value used to generate stabalizer spring force. The force generated " + "depends on stabilizer compression, that is how close the vehicle is " + "to the ground proportional to current stabalizer length.\n\n" + "@see stabLenMin" ); + addField( "stabDampingConstant", TypeF32, Offset(stabDampingConstant, HoverVehicleData), + "Damping spring force acting against changes in the stabalizer length.\n\n" + "@see stabLenMin" ); + + addField( "gyroDrag", TypeF32, Offset(gyroDrag, HoverVehicleData), + "Damping torque that acts against the vehicle's current angular momentum." ); + addField( "normalForce", TypeF32, Offset(normalForce, HoverVehicleData), + "Force generated in the ground normal direction when the vehicle is not " + "floating (within stabalizer length from the ground).\n\n" + "@see stabLenMin" ); + addField( "restorativeForce", TypeF32, Offset(restorativeForce, HoverVehicleData), + "Force generated to stabalize the vehicle (return it to neutral pitch/roll) " + "when the vehicle is floating (more than stabalizer length from the " + "ground.\n\n@see stabLenMin" ); + addField( "steeringForce", TypeF32, Offset(steeringForce, HoverVehicleData), + "Yaw (rotation about the Z-axis) force applied when steering in the x-axis direction." + "about the vehicle's Z-axis)" ); + addField( "rollForce", TypeF32, Offset(rollForce, HoverVehicleData), + "Roll (rotation about the Y-axis) force applied when steering in the x-axis direction." ); + addField( "pitchForce", TypeF32, Offset(pitchForce, HoverVehicleData), + "Pitch (rotation about the X-axis) force applied when steering in the y-axis direction." ); + + addField( "jetSound", TYPEID< SFXProfile >(), Offset(sound[JetSound], HoverVehicleData), + "Looping sound played when the vehicle is jetting." ); + addField( "engineSound", TYPEID< SFXProfile >(), Offset(sound[EngineSound], HoverVehicleData), + "Looping engine sound.\nThe volume is dynamically adjusted based on the " + "current thrust level." ); + addField( "floatSound", TYPEID< SFXProfile >(), Offset(sound[FloatSound], HoverVehicleData), + "Looping sound played while the vehicle is floating.\n\n@see stabMinLen" ); + + addField( "dustTrailEmitter", TYPEID< ParticleEmitterData >(), Offset(dustTrailEmitter, HoverVehicleData), + "Emitter to generate particles for the vehicle's dust trail.\nThe trail " + "of dust particles is generated only while the vehicle is moving." ); + addField( "dustTrailOffset", TypePoint3F, Offset(dustTrailOffset, HoverVehicleData), + "\"X Y Z\" offset from the vehicle's origin from which to generate dust " + "trail particles.\nBy default particles are emitted directly beneath the " + "origin of the vehicle model." ); + addField( "triggerTrailHeight", TypeF32, Offset(triggerTrailHeight, HoverVehicleData), + "Maximum height above surface to emit dust trail particles.\nIf the vehicle " + "is less than triggerTrailHeight above a static surface with a material that " + "has 'showDust' set to true, the vehicle will emit particles from the " + "dustTrailEmitter." ); + addField( "dustTrailFreqMod", TypeF32, Offset(dustTrailFreqMod, HoverVehicleData), + "Number of dust trail particles to generate based on vehicle speed.\nThe " + "vehicle's speed is divided by this value to determine how many particles " + "to generate each frame. Lower values give a more dense trail, higher " + "values a more sparse trail." ); + + addField( "floatingGravMag", TypeF32, Offset(floatingGravMag, HoverVehicleData), + "Scale factor applied to the vehicle gravitational force when the vehicle " + "is floating.\n\n@see stabLenMin" ); + addField( "brakingForce", TypeF32, Offset(brakingForce, HoverVehicleData), + "Force generated by braking.\nThe vehicle is considered to be braking if " + "it is moving, but the throttle is off, and no left or right thrust is " + "being applied. This force is only applied when the vehicle's velocity is " + "less than brakingActivationSpeed." ); + addField( "brakingActivationSpeed", TypeF32, Offset(brakingActivationSpeed, HoverVehicleData), + "Maximum speed below which a braking force is applied.\n\n@see brakingForce" ); + + addField( "forwardJetEmitter", TYPEID< ParticleEmitterData >(), Offset(jetEmitter[ForwardJetEmitter], HoverVehicleData), + "Emitter to generate particles for forward jet thrust.\nForward jet " + "thrust particles are emitted from model nodes JetNozzle0 and JetNozzle1." ); + + Parent::initPersistFields(); +} + + +//-------------------------------------------------------------------------- +bool HoverVehicleData::onAdd() +{ + if(!Parent::onAdd()) + return false; + + return true; +} + + +bool HoverVehicleData::preload(bool server, String &errorStr) +{ + if (Parent::preload(server, errorStr) == false) + return false; + + if (dragForce <= 0.01f) { + Con::warnf("HoverVehicleData::preload: dragForce must be at least 0.01"); + dragForce = 0.01f; + } + if (vertFactor < 0.0f || vertFactor > 1.0f) { + Con::warnf("HoverVehicleData::preload: vert factor must be [0, 1]"); + vertFactor = vertFactor < 0.0f ? 0.0f : 1.0f; + } + if (floatingThrustFactor < 0.0f || floatingThrustFactor > 1.0f) { + Con::warnf("HoverVehicleData::preload: floatingThrustFactor must be [0, 1]"); + floatingThrustFactor = floatingThrustFactor < 0.0f ? 0.0f : 1.0f; + } + + maxThrustSpeed = (mainThrustForce + strafeThrustForce) / dragForce; + + massCenter = Point3F(0, 0, 0); + + // Resolve objects transmitted from server + if (!server) { + for (S32 i = 0; i < MaxSounds; i++) + if (sound[i]) + Sim::findObject(SimObjectId(sound[i]),sound[i]); + for (S32 j = 0; j < MaxJetEmitters; j++) + if (jetEmitter[j]) + Sim::findObject(SimObjectId(jetEmitter[j]),jetEmitter[j]); + } + + if( !dustTrailEmitter && dustTrailID != 0 ) + { + if( !Sim::findObject( dustTrailID, dustTrailEmitter ) ) + { + Con::errorf( ConsoleLogEntry::General, "HoverVehicleData::preload Invalid packet, bad datablockId(dustTrailEmitter): 0x%x", dustTrailID ); + } + } + // Resolve jet nodes + for (S32 j = 0; j < MaxJetNodes; j++) + jetNode[j] = mShape->findNode(sJetNode[j]); + + return true; +} + + +//-------------------------------------------------------------------------- +void HoverVehicleData::packData(BitStream* stream) +{ + Parent::packData(stream); + + stream->write(dragForce); + stream->write(vertFactor); + stream->write(floatingThrustFactor); + stream->write(mainThrustForce); + stream->write(reverseThrustForce); + stream->write(strafeThrustForce); + stream->write(turboFactor); + stream->write(stabLenMin); + stream->write(stabLenMax); + stream->write(stabSpringConstant); + stream->write(stabDampingConstant); + stream->write(gyroDrag); + stream->write(normalForce); + stream->write(restorativeForce); + stream->write(steeringForce); + stream->write(rollForce); + stream->write(pitchForce); + mathWrite(*stream, dustTrailOffset); + stream->write(triggerTrailHeight); + stream->write(dustTrailFreqMod); + + for (S32 i = 0; i < MaxSounds; i++) + if (stream->writeFlag(sound[i])) + stream->writeRangedU32(packed? SimObjectId(sound[i]): + sound[i]->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast); + + for (S32 j = 0; j < MaxJetEmitters; j++) + { + if (stream->writeFlag(jetEmitter[j])) + { + SimObjectId writtenId = packed ? SimObjectId(jetEmitter[j]) : jetEmitter[j]->getId(); + stream->writeRangedU32(writtenId, DataBlockObjectIdFirst,DataBlockObjectIdLast); + } + } + + if (stream->writeFlag( dustTrailEmitter )) + { + stream->writeRangedU32( dustTrailEmitter->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + stream->write(floatingGravMag); + stream->write(brakingForce); + stream->write(brakingActivationSpeed); +} + + +void HoverVehicleData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + + stream->read(&dragForce); + stream->read(&vertFactor); + stream->read(&floatingThrustFactor); + stream->read(&mainThrustForce); + stream->read(&reverseThrustForce); + stream->read(&strafeThrustForce); + stream->read(&turboFactor); + stream->read(&stabLenMin); + stream->read(&stabLenMax); + stream->read(&stabSpringConstant); + stream->read(&stabDampingConstant); + stream->read(&gyroDrag); + stream->read(&normalForce); + stream->read(&restorativeForce); + stream->read(&steeringForce); + stream->read(&rollForce); + stream->read(&pitchForce); + mathRead(*stream, &dustTrailOffset); + stream->read(&triggerTrailHeight); + stream->read(&dustTrailFreqMod); + + for (S32 i = 0; i < MaxSounds; i++) + sound[i] = stream->readFlag()? + (SFXProfile*) stream->readRangedU32(DataBlockObjectIdFirst, + DataBlockObjectIdLast): 0; + + for (S32 j = 0; j < MaxJetEmitters; j++) { + jetEmitter[j] = NULL; + if (stream->readFlag()) + jetEmitter[j] = (ParticleEmitterData*)stream->readRangedU32(DataBlockObjectIdFirst, + DataBlockObjectIdLast); + } + + if( stream->readFlag() ) + { + dustTrailID = (S32) stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); + } + stream->read(&floatingGravMag); + stream->read(&brakingForce); + stream->read(&brakingActivationSpeed); +} + + +//-------------------------------------------------------------------------- +//-------------------------------------- +// +HoverVehicle::HoverVehicle() +{ + // Todo: ScopeAlways? + mNetFlags.set(Ghostable); + + mFloating = false; + mForwardThrust = 0; + mReverseThrust = 0; + mLeftThrust = 0; + mRightThrust = 0; + + mJetSound = NULL; + mEngineSound = NULL; + mFloatSound = NULL; + + mDustTrailEmitter = NULL; + + mBackMaintainOn = false; + for (S32 i = 0; i < JetAnimCount; i++) + mJetThread[i] = 0; +} + +HoverVehicle::~HoverVehicle() +{ + // +} + +//-------------------------------------------------------------------------- +bool HoverVehicle::onAdd() +{ + if(!Parent::onAdd()) + return false; + + addToScene(); + + + if( !isServerObject() ) + { + if( mDataBlock->dustTrailEmitter ) + { + mDustTrailEmitter = new ParticleEmitter; + mDustTrailEmitter->onNewDataBlock( mDataBlock->dustTrailEmitter, false ); + if( !mDustTrailEmitter->registerObject() ) + { + Con::warnf( ConsoleLogEntry::General, "Could not register dust emitter for class: %s", mDataBlock->getName() ); + delete mDustTrailEmitter; + mDustTrailEmitter = NULL; + } + } + // Jet Sequences + for (S32 i = 0; i < JetAnimCount; i++) { + TSShape const* shape = mShapeInstance->getShape(); + mJetSeq[i] = shape->findSequence(sJetSequence[i]); + if (mJetSeq[i] != -1) { + if (i == BackActivate) { + mJetThread[i] = mShapeInstance->addThread(); + mShapeInstance->setSequence(mJetThread[i],mJetSeq[i],0); + mShapeInstance->setTimeScale(mJetThread[i],0); + } + } + else + mJetThread[i] = 0; + } + } + + + if (isServerObject()) + scriptOnAdd(); + + return true; +} + + +void HoverVehicle::onRemove() +{ + SFX_DELETE( mJetSound ); + SFX_DELETE( mEngineSound ); + SFX_DELETE( mFloatSound ); + + scriptOnRemove(); + removeFromScene(); + Parent::onRemove(); +} + + +bool HoverVehicle::onNewDataBlock(GameBaseData* dptr, bool reload) +{ + mDataBlock = dynamic_cast(dptr); + if (!mDataBlock || !Parent::onNewDataBlock(dptr,reload)) + return false; + + if (isGhost()) + { + // Create the sounds ahead of time. This reduces runtime + // costs and makes the system easier to understand. + + SFX_DELETE( mEngineSound ); + SFX_DELETE( mFloatSound ); + SFX_DELETE( mJetSound ); + + if ( mDataBlock->sound[HoverVehicleData::EngineSound] ) + mEngineSound = SFX->createSource( mDataBlock->sound[HoverVehicleData::EngineSound], &getTransform() ); + + if ( !mDataBlock->sound[HoverVehicleData::FloatSound] ) + mFloatSound = SFX->createSource( mDataBlock->sound[HoverVehicleData::FloatSound], &getTransform() ); + + if ( mDataBlock->sound[HoverVehicleData::JetSound] ) + mJetSound = SFX->createSource( mDataBlock->sound[HoverVehicleData::JetSound], &getTransform() ); + } + + // Todo: Uncomment if this is a "leaf" class + scriptOnNewDataBlock(); + + return true; +} + + + +//-------------------------------------------------------------------------- +void HoverVehicle::advanceTime(F32 dt) +{ + Parent::advanceTime(dt); + + // Update jetsound... + if ( mJetSound ) + { + if ( mJetting ) + { + if ( !mJetSound->isPlaying() ) + mJetSound->play(); + + mJetSound->setTransform( getTransform() ); + } + else + mJetSound->stop(); + } + + // Update engine sound... + if ( mEngineSound ) + { + if ( !mEngineSound->isPlaying() ) + mEngineSound->play(); + + mEngineSound->setTransform( getTransform() ); + + F32 denom = mDataBlock->mainThrustForce + mDataBlock->strafeThrustForce; + F32 factor = getMin(mThrustLevel, denom) / denom; + F32 vol = 0.25 + factor * 0.75; + mEngineSound->setVolume( vol ); + } + + // Are we floating? If so, start the floating sound... + if ( mFloatSound ) + { + if ( mFloating ) + { + if ( !mFloatSound->isPlaying() ) + mFloatSound->play(); + + mFloatSound->setTransform( getTransform() ); + } + else + mFloatSound->stop(); + } + + updateJet(dt); + updateDustTrail( dt ); +} + + +//-------------------------------------------------------------------------- + +U32 HoverVehicle::packUpdate(NetConnection* con, U32 mask, BitStream* stream) +{ + U32 retMask = Parent::packUpdate(con, mask, stream); + + // + stream->writeInt(mThrustDirection,NumThrustBits); + + return retMask; +} + +void HoverVehicle::unpackUpdate(NetConnection* con, BitStream* stream) +{ + Parent::unpackUpdate(con, stream); + + mThrustDirection = ThrustDirection(stream->readInt(NumThrustBits)); + // +} + + +//-------------------------------------------------------------------------- +void HoverVehicle::updateMove(const Move* move) +{ + Parent::updateMove(move); + + mForwardThrust = mThrottle > 0.0f ? mThrottle : 0.0f; + mReverseThrust = mThrottle < 0.0f ? -mThrottle : 0.0f; + mLeftThrust = move->x < 0.0f ? -move->x : 0.0f; + mRightThrust = move->x > 0.0f ? move->x : 0.0f; + + mThrustDirection = (!move->y)? ThrustDown: (move->y > 0)? ThrustForward: ThrustBackward; +} + +F32 HoverVehicle::getBaseStabilizerLength() const +{ + F32 base = mDataBlock->stabLenMin; + F32 lengthDiff = mDataBlock->stabLenMax - mDataBlock->stabLenMin; + F32 velLength = mRigid.linVelocity.len(); + F32 minVel = getMin(velLength, mDataBlock->maxThrustSpeed); + F32 velDiff = mDataBlock->maxThrustSpeed - minVel; + // Protect against divide by zero. + F32 velRatio = mDataBlock->maxThrustSpeed != 0.0f ? ( velDiff / mDataBlock->maxThrustSpeed ) : 0.0f; + F32 inc = lengthDiff * ( 1.0 - velRatio ); + base += inc; + + return base; +} + + +struct StabPoint +{ + Point3F osPoint; // + Point3F wsPoint; // + F32 extension; + Point3F wsExtension; // + Point3F wsVelocity; // +}; + + +void HoverVehicle::updateForces(F32 /*dt*/) +{ + PROFILE_SCOPE( HoverVehicle_UpdateForces ); + + Point3F gravForce(0, 0, sHoverVehicleGravity * mRigid.mass * mGravityMod); + + MatrixF currTransform; + mRigid.getTransform(&currTransform); + mRigid.atRest = false; + + mThrustLevel = (mForwardThrust * mDataBlock->mainThrustForce + + mReverseThrust * mDataBlock->reverseThrustForce + + mLeftThrust * mDataBlock->strafeThrustForce + + mRightThrust * mDataBlock->strafeThrustForce); + + Point3F thrustForce = ((Point3F( 0, 1, 0) * (mForwardThrust * mDataBlock->mainThrustForce)) + + (Point3F( 0, -1, 0) * (mReverseThrust * mDataBlock->reverseThrustForce)) + + (Point3F(-1, 0, 0) * (mLeftThrust * mDataBlock->strafeThrustForce)) + + (Point3F( 1, 0, 0) * (mRightThrust * mDataBlock->strafeThrustForce))); + currTransform.mulV(thrustForce); + if (mJetting) + thrustForce *= mDataBlock->turboFactor; + + Point3F torque(0, 0, 0); + Point3F force(0, 0, 0); + + Point3F vel = mRigid.linVelocity; + F32 baseStabLen = getBaseStabilizerLength(); + Point3F stabExtend(0, 0, -baseStabLen); + currTransform.mulV(stabExtend); + + StabPoint stabPoints[2]; + stabPoints[0].osPoint = Point3F((mObjBox.minExtents.x + mObjBox.maxExtents.x) * 0.5, + mObjBox.maxExtents.y, + (mObjBox.minExtents.z + mObjBox.maxExtents.z) * 0.5); + stabPoints[1].osPoint = Point3F((mObjBox.minExtents.x + mObjBox.maxExtents.x) * 0.5, + mObjBox.minExtents.y, + (mObjBox.minExtents.z + mObjBox.maxExtents.z) * 0.5); + U32 j, i; + for (i = 0; i < 2; i++) { + currTransform.mulP(stabPoints[i].osPoint, &stabPoints[i].wsPoint); + stabPoints[i].wsExtension = stabExtend; + stabPoints[i].extension = baseStabLen; + stabPoints[i].wsVelocity = mRigid.linVelocity; + } + + RayInfo rinfo; + + mFloating = true; + bool reallyFloating = true; + F32 compression[2] = { 0.0f, 0.0f }; + F32 normalMod[2] = { 0.0f, 0.0f }; + bool normalSet[2] = { false, false }; + Point3F normal[2]; + + for (j = 0; j < 2; j++) { + if (getContainer()->castRay(stabPoints[j].wsPoint, stabPoints[j].wsPoint + stabPoints[j].wsExtension * 2.0, + TerrainObjectType | + InteriorObjectType | WaterObjectType, &rinfo)) + { + reallyFloating = false; + + if (rinfo.t <= 0.5) { + // Ok, stab is in contact with the ground, let's calc the forces... + compression[j] = (1.0 - (rinfo.t * 2.0)) * baseStabLen; + } + normalSet[j] = true; + normalMod[j] = rinfo.t < 0.5 ? 1.0 : (1.0 - ((rinfo.t - 0.5) * 2.0)); + + normal[j] = rinfo.normal; + } + + if ( pointInWater( stabPoints[j].wsPoint ) ) + compression[j] = baseStabLen; + } + + for (j = 0; j < 2; j++) { + if (compression[j] != 0.0) { + mFloating = false; + + // Spring force and damping + Point3F springForce = -stabPoints[j].wsExtension; + springForce.normalize(); + springForce *= compression[j] * mDataBlock->stabSpringConstant; + + Point3F springDamping = -stabPoints[j].wsExtension; + springDamping.normalize(); + springDamping *= -getMin(mDot(springDamping, stabPoints[j].wsVelocity), 0.7f) * mDataBlock->stabDampingConstant; + + force += springForce + springDamping; + } + } + + // Gravity + if (reallyFloating == false) + force += gravForce; + else + force += gravForce * mDataBlock->floatingGravMag; + + // Braking + F32 vellen = mRigid.linVelocity.len(); + if (mThrottle == 0.0f && + mLeftThrust == 0.0f && + mRightThrust == 0.0f && + vellen != 0.0f && + vellen < mDataBlock->brakingActivationSpeed) + { + Point3F dir = mRigid.linVelocity; + dir.normalize(); + dir.neg(); + force += dir * mDataBlock->brakingForce; + } + + // Gyro Drag + torque = -mRigid.angMomentum * mDataBlock->gyroDrag; + + // Move to proper normal + Point3F sn, r; + currTransform.getColumn(2, &sn); + if (normalSet[0] || normalSet[1]) { + if (normalSet[0] && normalSet[1]) { + F32 dot = mDot(normal[0], normal[1]); + if (dot > 0.999) { + // Just pick the first normal. They're too close to call + if ((sn - normal[0]).lenSquared() > 0.00001) { + mCross(sn, normal[0], &r); + torque += r * mDataBlock->normalForce * normalMod[0]; + } + } else { + Point3F rotAxis; + mCross(normal[0], normal[1], &rotAxis); + rotAxis.normalize(); + + F32 angle = mAcos(dot) * (normalMod[0] / (normalMod[0] + normalMod[1])); + AngAxisF aa(rotAxis, angle); + QuatF q(aa); + MatrixF tempMat(true); + q.setMatrix(&tempMat); + Point3F newNormal; + tempMat.mulV(normal[1], &newNormal); + + if ((sn - newNormal).lenSquared() > 0.00001) { + mCross(sn, newNormal, &r); + torque += r * (mDataBlock->normalForce * ((normalMod[0] + normalMod[1]) * 0.5)); + } + } + } else { + Point3F useNormal; + F32 useMod; + if (normalSet[0]) { + useNormal = normal[0]; + useMod = normalMod[0]; + } else { + useNormal = normal[1]; + useMod = normalMod[1]; + } + + if ((sn - useNormal).lenSquared() > 0.00001) { + mCross(sn, useNormal, &r); + torque += r * mDataBlock->normalForce * useMod; + } + } + } else { + if ((sn - Point3F(0, 0, 1)).lenSquared() > 0.00001) { + mCross(sn, Point3F(0, 0, 1), &r); + torque += r * mDataBlock->restorativeForce; + } + } + + Point3F sn2; + currTransform.getColumn(0, &sn); + currTransform.getColumn(1, &sn2); + mCross(sn, sn2, &r); + r.normalize(); + torque -= r * (mSteering.x * mDataBlock->steeringForce); + + currTransform.getColumn(0, &sn); + currTransform.getColumn(2, &sn2); + mCross(sn, sn2, &r); + r.normalize(); + torque -= r * (mSteering.x * mDataBlock->rollForce); + + currTransform.getColumn(1, &sn); + currTransform.getColumn(2, &sn2); + mCross(sn, sn2, &r); + r.normalize(); + torque -= r * (mSteering.y * mDataBlock->pitchForce); + + // Apply drag + Point3F vDrag = mRigid.linVelocity; + if (!mFloating) { + vDrag.convolve(Point3F(1, 1, mDataBlock->vertFactor)); + } else { + vDrag.convolve(Point3F(0.25, 0.25, mDataBlock->vertFactor)); + } + force -= vDrag * mDataBlock->dragForce; + + force += mFloating ? thrustForce * mDataBlock->floatingThrustFactor : thrustForce; + + // Add in physical zone force + force += mAppliedForce; + + // Container buoyancy & drag + force += Point3F(0, 0,-mBuoyancy * sHoverVehicleGravity * mRigid.mass * mGravityMod); + force -= mRigid.linVelocity * mDrag; + torque -= mRigid.angMomentum * mDrag; + + mRigid.force = force; + mRigid.torque = torque; +} + + +//-------------------------------------------------------------------------- +U32 HoverVehicle::getCollisionMask() +{ + if (isServerObject()) + return sServerCollisionMask; + else + return sClientCollisionMask; +} + +void HoverVehicle::updateDustTrail( F32 dt ) +{ + // Check to see if we're moving. + + VectorF velocityVector = getVelocity(); + F32 velocity = velocityVector.len(); + + if( velocity > 2.0 ) + { + velocityVector.normalize(); + emitDust( mDustTrailEmitter, mDataBlock->triggerTrailHeight, mDataBlock->dustTrailOffset, + ( U32 )( dt * 1000 * ( velocity / mDataBlock->dustTrailFreqMod ) ), + velocityVector ); + } +} + +void HoverVehicle::updateJet(F32 dt) +{ + if (mJetThread[BackActivate] == NULL) + return; + + // Thrust Animation threads + // Back + if (mJetSeq[BackActivate] >=0 ) { + if (!mBackMaintainOn || mThrustDirection != ThrustForward) { + if (mBackMaintainOn) { + mShapeInstance->setPos(mJetThread[BackActivate], 1); + mShapeInstance->destroyThread(mJetThread[BackMaintain]); + mBackMaintainOn = false; + } + mShapeInstance->setTimeScale(mJetThread[BackActivate], + (mThrustDirection == ThrustForward)? 1.0f : -1.0f); + mShapeInstance->advanceTime(dt,mJetThread[BackActivate]); + } + } + + if (mJetSeq[BackMaintain] >= 0 && !mBackMaintainOn && + mShapeInstance->getPos(mJetThread[BackActivate]) >= 1.0f) + { + mShapeInstance->setPos(mJetThread[BackActivate], 0); + mShapeInstance->setTimeScale(mJetThread[BackActivate], 0); + mJetThread[BackMaintain] = mShapeInstance->addThread(); + mShapeInstance->setSequence(mJetThread[BackMaintain],mJetSeq[BackMaintain],0); + mShapeInstance->setTimeScale(mJetThread[BackMaintain],1); + mBackMaintainOn = true; + } + + if(mBackMaintainOn) + mShapeInstance->advanceTime(dt,mJetThread[BackMaintain]); + + // Jet particles + for (S32 j = 0; j < NumThrustDirections; j++) { + JetActivation& jet = sJetActivation[j]; + updateEmitter(mJetting && j == mThrustDirection,dt,mDataBlock->jetEmitter[jet.emitter], + jet.node,HoverVehicleData::MaxDirectionJets); + } +} + +void HoverVehicle::updateEmitter(bool active,F32 dt,ParticleEmitterData *emitter,S32 idx,S32 count) +{ + if (!emitter) + return; + for (S32 j = idx; j < idx + count; j++) + if (active) { + if (mDataBlock->jetNode[j] != -1) { + if (!bool(mJetEmitter[j])) { + mJetEmitter[j] = new ParticleEmitter; + mJetEmitter[j]->onNewDataBlock( emitter, false ); + mJetEmitter[j]->registerObject(); + } + MatrixF mat; + Point3F pos,axis; + mat.mul(getRenderTransform(), + mShapeInstance->mNodeTransforms[mDataBlock->jetNode[j]]); + mat.getColumn(1,&axis); + mat.getColumn(3,&pos); + mJetEmitter[j]->emitParticles(pos,true,axis,getVelocity(),(U32)(dt * 1000.0f)); + } + } + else { + for (S32 j = idx; j < idx + count; j++) + if (bool(mJetEmitter[j])) { + mJetEmitter[j]->deleteWhenEmpty(); + mJetEmitter[j] = 0; + } + } +} diff --git a/Engine/source/T3D/vehicles/hoverVehicle.h b/Engine/source/T3D/vehicles/hoverVehicle.h new file mode 100644 index 000000000..7b44e5987 --- /dev/null +++ b/Engine/source/T3D/vehicles/hoverVehicle.h @@ -0,0 +1,213 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _HOVERVEHICLE_H_ +#define _HOVERVEHICLE_H_ + +#ifndef _VEHICLE_H_ +#include "T3D/vehicles/vehicle.h" +#endif + +class ParticleEmitter; +class ParticleEmitterData; + +// ------------------------------------------------------------------------- +class HoverVehicleData : public VehicleData +{ + typedef VehicleData Parent; + + protected: + bool onAdd(); + + //-------------------------------------- Console set variables + public: + enum Sounds { + JetSound, + EngineSound, + FloatSound, + MaxSounds + }; + SFXProfile* sound[MaxSounds]; + + enum Jets { + // These enums index into a static name list. + ForwardJetEmitter, // Thrust forward + BackwardJetEmitter, // Thrust backward + DownwardJetEmitter, // Thrust down + MaxJetEmitters, + }; + ParticleEmitterData* jetEmitter[MaxJetEmitters]; + + enum JetNodes { + // These enums index into a static name list. + ForwardJetNode, + ForwardJetNode1, + BackwardJetNode, + BackwardJetNode1, + DownwardJetNode, + DownwardJetNode1, + // + MaxJetNodes, + MaxDirectionJets = 2, + ThrustJetStart = ForwardJetNode, + MaxTrails = 4, + }; + static const char *sJetNode[MaxJetNodes]; + S32 jetNode[MaxJetNodes]; + + + F32 dragForce; + F32 vertFactor; + F32 floatingThrustFactor; + + F32 mainThrustForce; + F32 reverseThrustForce; + F32 strafeThrustForce; + F32 turboFactor; + + F32 stabLenMin; + F32 stabLenMax; + F32 stabSpringConstant; + F32 stabDampingConstant; + + F32 gyroDrag; + F32 normalForce; + F32 restorativeForce; + F32 steeringForce; + F32 rollForce; + F32 pitchForce; + + F32 floatingGravMag; + + F32 brakingForce; + F32 brakingActivationSpeed; + + ParticleEmitterData * dustTrailEmitter; + S32 dustTrailID; + Point3F dustTrailOffset; + F32 triggerTrailHeight; + F32 dustTrailFreqMod; + + //-------------------------------------- load set variables + public: + F32 maxThrustSpeed; + + public: + HoverVehicleData(); + ~HoverVehicleData(); + + void packData(BitStream*); + void unpackData(BitStream*); + bool preload(bool server, String &errorStr); + + DECLARE_CONOBJECT(HoverVehicleData); + static void initPersistFields(); +}; + + +// ------------------------------------------------------------------------- +class HoverVehicle : public Vehicle +{ + typedef Vehicle Parent; + + private: + HoverVehicleData* mDataBlock; + SimObjectPtr mDustTrailEmitter; + + protected: + bool onAdd(); + void onRemove(); + bool onNewDataBlock(GameBaseData *dptr,bool reload); + void updateDustTrail( F32 dt ); + + // Vehicle overrides + protected: + void updateMove(const Move *move); + + // Physics + protected: + void updateForces(F32); + F32 getBaseStabilizerLength() const; + + bool mFloating; + F32 mThrustLevel; + + F32 mForwardThrust; + F32 mReverseThrust; + F32 mLeftThrust; + F32 mRightThrust; + + SFXSource* mJetSound; + SFXSource* mEngineSound; + SFXSource* mFloatSound; + + enum ThrustDirection { + // Enums index into sJetActivationTable + ThrustForward, + ThrustBackward, + ThrustDown, + NumThrustDirections, + NumThrustBits = 3 + }; + ThrustDirection mThrustDirection; + + // Jet Threads + enum Jets { + // These enums index into a static name list. + BackActivate, + BackMaintain, + JetAnimCount + }; + static const char* sJetSequence[HoverVehicle::JetAnimCount]; + TSThread* mJetThread[JetAnimCount]; + S32 mJetSeq[JetAnimCount]; + bool mBackMaintainOn; + + // Jet Particles + struct JetActivation { + // Convert thrust direction into nodes & emitters + S32 node; + S32 emitter; + }; + static JetActivation sJetActivation[NumThrustDirections]; + SimObjectPtr mJetEmitter[HoverVehicleData::MaxJetNodes]; + + U32 getCollisionMask(); + void updateJet(F32 dt); + void updateEmitter(bool active,F32 dt,ParticleEmitterData *emitter,S32 idx,S32 count); + public: + HoverVehicle(); + ~HoverVehicle(); + + // Time/Move Management + public: + void advanceTime(F32 dt); + + DECLARE_CONOBJECT(HoverVehicle); +// static void initPersistFields(); + + U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream); + void unpackUpdate(NetConnection *conn, BitStream *stream); +}; + +#endif // _H_HOVERVEHICLE + diff --git a/Engine/source/T3D/vehicles/vehicle.cpp b/Engine/source/T3D/vehicles/vehicle.cpp new file mode 100644 index 000000000..9496a8148 --- /dev/null +++ b/Engine/source/T3D/vehicles/vehicle.cpp @@ -0,0 +1,1965 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/vehicles/vehicle.h" + +#include "math/mMath.h" +#include "console/simBase.h" +#include "console/console.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "collision/clippedPolyList.h" +#include "collision/planeExtractor.h" +#include "core/stream/bitStream.h" +#include "core/dnet.h" +#include "T3D/gameBase/gameConnection.h" +#include "T3D/fx/cameraFXMgr.h" +#include "ts/tsShapeInstance.h" +#include "T3D/fx/particleEmitter.h" +#include "sfx/sfxSystem.h" +#include "sfx/sfxProfile.h" +#include "sfx/sfxSource.h" +#include "math/mathIO.h" +#include "scene/sceneRenderState.h" +#include "T3D/trigger.h" +#include "T3D/item.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxDrawUtil.h" +#include "materials/materialDefinition.h" + + +namespace { + +static U32 sWorkingQueryBoxStaleThreshold = 10; // The maximum number of ticks that go by before + // the mWorkingQueryBox is considered stale and + // needs updating. Set to -1 to disable. + +static F32 sWorkingQueryBoxSizeMultiplier = 2.0f; // How much larger should the mWorkingQueryBox be + // made when updating the working collision list. + // The larger this number the less often the working list + // will be updated due to motion, but any non-static shape + // that moves into the query box will not be noticed. + +const U32 sMoveRetryCount = 3; + +// Client prediction +const S32 sMaxWarpTicks = 3; // Max warp duration in ticks +const S32 sMaxPredictionTicks = 30; // Number of ticks to predict +const F32 sVehicleGravity = -20; + +// Physics and collision constants +static F32 sRestTol = 0.5; // % of gravity energy to be at rest +static int sRestCount = 10; // Consecutive ticks before comming to rest + +} // namespace {} + +// Trigger objects that are not normally collided with. +static U32 sTriggerMask = ItemObjectType | + TriggerObjectType | + CorpseObjectType; + +IMPLEMENT_CONOBJECT(VehicleData); + +ConsoleDocClass( VehicleData, + "@brief Base properties shared by all Vehicles (FlyingVehicle, HoverVehicle, " + "WheeledVehicle).\n\n" + "This datablock defines properties shared by all Vehicle types, but should " + "not be instantiated directly. Instead, set the desired properties in the " + "FlyingVehicleData, HoverVehicleData or WheeledVehicleData datablock.\n" + + "@section VehicleData_damage Damage\n\n" + + "The VehicleData class extends the basic energy/damage functionality provided " + "by ShapeBaseData to include damage from collisions, as well as particle " + "emitters activated automatically when damage levels reach user specified " + "thresholds.\n\n" + + "The example below shows how to setup a Vehicle to:\n" + "
        \n" + "
      • take damage when colliding with another object\n" + "
      • emit gray smoke particles from two locations on the Vehicle when damaged above 50%
      • \n" + "
      • emit black smoke particles from two locations on the Vehicle when damaged above 85%
      • \n" + "
      • emit bubbles when any active damage emitter point is underwater
      • \n" + "
      \n\n" + + "@tsexample\n" + "// damage from collisions\n" + "collDamageMultiplier = 0.05;\n" + "collDamageThresholdVel = 15;\n\n" + + "// damage levels\n" + "damageLevelTolerance[0] = 0.5;\n" + "damageEmitter[0] = GraySmokeEmitter; // emitter used when damage is >= 50%\n" + "damageLevelTolerance[1] = 0.85;\n" + "damageEmitter[1] = BlackSmokeEmitter; // emitter used when damage is >= 85%\n" + "damageEmitter[2] = DamageBubbleEmitter; // emitter used instead of damageEmitter[0:1]\n" + " // when offset point is underwater\n" + "// emit offsets (used for all active damage level emitters)\n" + "damageEmitterOffset[0] = \"0.5 3 1\";\n" + "damageEmitterOffset[1] = \"-0.5 3 1\";\n" + "numDmgEmitterAreas = 2;\n" + "@endtsexample\n" + + "@ingroup Vehicles\n" +); + +IMPLEMENT_CALLBACK( VehicleData, onEnterLiquid, void, ( Vehicle* obj, F32 coverage, const char* type ), ( obj, coverage, type ), + "Called when the vehicle enters liquid.\n" + "@param obj the Vehicle object\n" + "@param coverage percentage of the vehicle's bounding box covered by the liquid\n" + "@param type type of liquid the vehicle has entered\n" ); + +IMPLEMENT_CALLBACK( VehicleData, onLeaveLiquid, void, ( Vehicle* obj, const char* type ), ( obj, type ), + "Called when the vehicle leaves liquid.\n" + "@param obj the Vehicle object\n" + "@param type type of liquid the vehicle has left\n" ); + +//---------------------------------------------------------------------------- + +VehicleData::VehicleData() +{ + shadowEnable = true; + shadowSize = 256; + shadowProjectionDistance = 14.0f; + + + body.friction = 0; + body.restitution = 1; + + minImpactSpeed = 25; + softImpactSpeed = 25; + hardImpactSpeed = 50; + minRollSpeed = 0; + maxSteeringAngle = M_PI_F/4.0f; // 45 deg. + + cameraRoll = true; + cameraLag = 0; + cameraDecay = 0; + cameraOffset = 0; + + minDrag = 0; + maxDrag = 0; + integration = 1; + collisionTol = 0.1f; + contactTol = 0.1f; + massCenter.set(0,0,0); + massBox.set(0,0,0); + + drag = 0.7f; + density = 4; + + jetForce = 500; + jetEnergyDrain = 0.8f; + minJetEnergy = 1; + + for (S32 i = 0; i < Body::MaxSounds; i++) + body.sound[i] = 0; + + dustEmitter = NULL; + dustID = 0; + triggerDustHeight = 3.0; + dustHeight = 1.0; + + dMemset( damageEmitterList, 0, sizeof( damageEmitterList ) ); + dMemset( damageEmitterOffset, 0, sizeof( damageEmitterOffset ) ); + dMemset( damageEmitterIDList, 0, sizeof( damageEmitterIDList ) ); + dMemset( damageLevelTolerance, 0, sizeof( damageLevelTolerance ) ); + dMemset( splashEmitterList, 0, sizeof( splashEmitterList ) ); + dMemset( splashEmitterIDList, 0, sizeof( splashEmitterIDList ) ); + + numDmgEmitterAreas = 0; + + splashFreqMod = 300.0; + splashVelEpsilon = 0.50; + exitSplashSoundVel = 2.0; + softSplashSoundVel = 1.0; + medSplashSoundVel = 2.0; + hardSplashSoundVel = 3.0; + + dMemset(waterSound, 0, sizeof(waterSound)); + + collDamageThresholdVel = 20; + collDamageMultiplier = 0.05f; +} + + +//---------------------------------------------------------------------------- + +bool VehicleData::preload(bool server, String &errorStr) +{ + if (!Parent::preload(server, errorStr)) + return false; + + // Vehicle objects must define a collision detail + if (!collisionDetails.size() || collisionDetails[0] == -1) + { + Con::errorf("VehicleData::preload failed: Vehicle models must define a collision-1 detail"); + return false; + } + + // Resolve objects transmitted from server + if (!server) { + for (S32 i = 0; i < Body::MaxSounds; i++) + if (body.sound[i]) + Sim::findObject(SimObjectId(body.sound[i]),body.sound[i]); + } + + if( !dustEmitter && dustID != 0 ) + { + if( !Sim::findObject( dustID, dustEmitter ) ) + { + Con::errorf( ConsoleLogEntry::General, "VehicleData::preload Invalid packet, bad datablockId(dustEmitter): 0x%x", dustID ); + } + } + + U32 i; + for( i=0; iwrite(body.restitution); + stream->write(body.friction); + for (i = 0; i < Body::MaxSounds; i++) + if (stream->writeFlag(body.sound[i])) + stream->writeRangedU32(packed? SimObjectId(body.sound[i]): + body.sound[i]->getId(),DataBlockObjectIdFirst, + DataBlockObjectIdLast); + + stream->write(minImpactSpeed); + stream->write(softImpactSpeed); + stream->write(hardImpactSpeed); + stream->write(minRollSpeed); + stream->write(maxSteeringAngle); + + stream->write(maxDrag); + stream->write(minDrag); + stream->write(integration); + stream->write(collisionTol); + stream->write(contactTol); + mathWrite(*stream,massCenter); + mathWrite(*stream,massBox); + + stream->write(jetForce); + stream->write(jetEnergyDrain); + stream->write(minJetEnergy); + + stream->writeFlag(cameraRoll); + stream->write(cameraLag); + stream->write(cameraDecay); + stream->write(cameraOffset); + + stream->write( triggerDustHeight ); + stream->write( dustHeight ); + + stream->write( numDmgEmitterAreas ); + + stream->write(exitSplashSoundVel); + stream->write(softSplashSoundVel); + stream->write(medSplashSoundVel); + stream->write(hardSplashSoundVel); + + // write the water sound profiles + for(i = 0; i < MaxSounds; i++) + if(stream->writeFlag(waterSound[i])) + stream->writeRangedU32(waterSound[i]->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast); + + if (stream->writeFlag( dustEmitter )) + { + stream->writeRangedU32( dustEmitter->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + + for (i = 0; i < VC_NUM_DAMAGE_EMITTERS; i++) + { + if( stream->writeFlag( damageEmitterList[i] != NULL ) ) + { + stream->writeRangedU32( damageEmitterList[i]->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + } + + for (i = 0; i < VC_NUM_SPLASH_EMITTERS; i++) + { + if( stream->writeFlag( splashEmitterList[i] != NULL ) ) + { + stream->writeRangedU32( splashEmitterList[i]->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + } + + for (int j = 0; j < VC_NUM_DAMAGE_EMITTER_AREAS; j++) + { + stream->write( damageEmitterOffset[j].x ); + stream->write( damageEmitterOffset[j].y ); + stream->write( damageEmitterOffset[j].z ); + } + + for (int k = 0; k < VC_NUM_DAMAGE_LEVELS; k++) + { + stream->write( damageLevelTolerance[k] ); + } + + stream->write(splashFreqMod); + stream->write(splashVelEpsilon); + + stream->write(collDamageThresholdVel); + stream->write(collDamageMultiplier); +} + +void VehicleData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + + stream->read(&body.restitution); + stream->read(&body.friction); + S32 i; + for (i = 0; i < Body::MaxSounds; i++) { + body.sound[i] = NULL; + if (stream->readFlag()) + body.sound[i] = (SFXProfile*)stream->readRangedU32(DataBlockObjectIdFirst, + DataBlockObjectIdLast); + } + + stream->read(&minImpactSpeed); + stream->read(&softImpactSpeed); + stream->read(&hardImpactSpeed); + stream->read(&minRollSpeed); + stream->read(&maxSteeringAngle); + + stream->read(&maxDrag); + stream->read(&minDrag); + stream->read(&integration); + stream->read(&collisionTol); + stream->read(&contactTol); + mathRead(*stream,&massCenter); + mathRead(*stream,&massBox); + + stream->read(&jetForce); + stream->read(&jetEnergyDrain); + stream->read(&minJetEnergy); + + cameraRoll = stream->readFlag(); + stream->read(&cameraLag); + stream->read(&cameraDecay); + stream->read(&cameraOffset); + + stream->read( &triggerDustHeight ); + stream->read( &dustHeight ); + + stream->read( &numDmgEmitterAreas ); + + stream->read(&exitSplashSoundVel); + stream->read(&softSplashSoundVel); + stream->read(&medSplashSoundVel); + stream->read(&hardSplashSoundVel); + + // write the water sound profiles + for(i = 0; i < MaxSounds; i++) + if(stream->readFlag()) + { + U32 id = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); + waterSound[i] = dynamic_cast( Sim::findObject(id) ); + } + + if( stream->readFlag() ) + { + dustID = (S32) stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); + } + + for (i = 0; i < VC_NUM_DAMAGE_EMITTERS; i++) + { + if( stream->readFlag() ) + { + damageEmitterIDList[i] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + } + + for (i = 0; i < VC_NUM_SPLASH_EMITTERS; i++) + { + if( stream->readFlag() ) + { + splashEmitterIDList[i] = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } + } + + for( int j=0; jread( &damageEmitterOffset[j].x ); + stream->read( &damageEmitterOffset[j].y ); + stream->read( &damageEmitterOffset[j].z ); + } + + for( int k=0; kread( &damageLevelTolerance[k] ); + } + + stream->read(&splashFreqMod); + stream->read(&splashVelEpsilon); + + stream->read(&collDamageThresholdVel); + stream->read(&collDamageMultiplier); +} + + +//---------------------------------------------------------------------------- + +void VehicleData::initPersistFields() +{ + addField( "jetForce", TypeF32, Offset(jetForce, VehicleData), + "@brief Additional force applied to the vehicle when it is jetting.\n\n" + "For WheeledVehicles, the force is applied in the forward direction. For " + "FlyingVehicles, the force is applied in the thrust direction." ); + addField( "jetEnergyDrain", TypeF32, Offset(jetEnergyDrain, VehicleData), + "@brief Energy amount to drain for each tick the vehicle is jetting.\n\n" + "Once the vehicle's energy level reaches 0, it will no longer be able to jet." ); + addField( "minJetEnergy", TypeF32, Offset(minJetEnergy, VehicleData), + "Minimum vehicle energy level to begin jetting." ); + + addField( "massCenter", TypePoint3F, Offset(massCenter, VehicleData), + "Defines the vehicle's center of mass (offset from the origin of the model)." ); + addField( "massBox", TypePoint3F, Offset(massBox, VehicleData), + "@brief Define the box used to estimate the vehicle's moment of inertia.\n\n" + "Currently only used by WheeledVehicle; other vehicle types use a " + "unit sphere to compute inertia." ); + addField( "bodyRestitution", TypeF32, Offset(body.restitution, VehicleData), + "Collision 'bounciness'.\nNormally in the range 0 (not bouncy at all) to " + "1 (100% bounciness)." ); + addField( "bodyFriction", TypeF32, Offset(body.friction, VehicleData), + "Collision friction coefficient.\nHow well this object will slide against " + "objects it collides with." ); + addField( "softImpactSound", TYPEID< SFXProfile >(), Offset(body.sound[Body::SoftImpactSound], VehicleData), + "@brief Sound to play on a 'soft' impact.\n\n" + "This sound is played if the impact speed is < hardImpactSpeed and >= " + "softImpactSpeed.\n\n" + "@see softImpactSpeed" ); + addField( "hardImpactSound", TYPEID< SFXProfile >(), Offset(body.sound[Body::HardImpactSound], VehicleData), + "@brief Sound to play on a 'hard' impact.\n\n" + "This sound is played if the impact speed >= hardImpactSpeed.\n\n" + "@see hardImpactSpeed" ); + + addField( "minImpactSpeed", TypeF32, Offset(minImpactSpeed, VehicleData), + "Minimum collision speed for the onImpact callback to be invoked." ); + addField( "softImpactSpeed", TypeF32, Offset(softImpactSpeed, VehicleData), + "Minimum collision speed for the softImpactSound to be played." ); + addField( "hardImpactSpeed", TypeF32, Offset(hardImpactSpeed, VehicleData), + "Minimum collision speed for the hardImpactSound to be played." ); + addField( "minRollSpeed", TypeF32, Offset(minRollSpeed, VehicleData), + "Unused" ); + addField( "maxSteeringAngle", TypeF32, Offset(maxSteeringAngle, VehicleData), + "Maximum yaw (horizontal) and pitch (vertical) steering angle in radians." ); + + addField( "maxDrag", TypeF32, Offset(maxDrag, VehicleData), + "Maximum drag coefficient.\nCurrently unused." ); + addField( "minDrag", TypeF32, Offset(minDrag, VehicleData), + "Minimum drag coefficient.\nCurrently only used by FlyingVehicle." ); + addField( "integration", TypeS32, Offset(integration, VehicleData), + "Number of integration steps per tick.\nIncrease this to improve simulation " + "stability (also increases simulation processing time)." ); + addField( "collisionTol", TypeF32, Offset(collisionTol, VehicleData), + "Minimum distance between objects for them to be considered as colliding." ); + addField( "contactTol", TypeF32, Offset(contactTol, VehicleData), + "Maximum relative velocity between objects for collisions to be resolved " + "as contacts.\nVelocities greater than this are handled as collisions." ); + + addField( "cameraRoll", TypeBool, Offset(cameraRoll, VehicleData), + "If true, the camera will roll with the vehicle. If false, the camera will " + "always have the positive Z axis as up." ); + addField( "cameraLag", TypeF32, Offset(cameraLag, VehicleData), + "@brief How much the camera lags behind the vehicle depending on vehicle speed.\n\n" + "Increasing this value will make the camera fall further behind the vehicle " + "as it accelerates away.\n\n@see cameraDecay." ); + addField("cameraDecay", TypeF32, Offset(cameraDecay, VehicleData), + "How quickly the camera moves back towards the vehicle when stopped.\n\n" + "@see cameraLag." ); + addField("cameraOffset", TypeF32, Offset(cameraOffset, VehicleData), + "Vertical (Z axis) height of the camera above the vehicle." ); + + addField( "dustEmitter", TYPEID< ParticleEmitterData >(), Offset(dustEmitter, VehicleData), + "Dust particle emitter.\n\n@see triggerDustHeight\n\n@see dustHeight"); + addField( "triggerDustHeight", TypeF32, Offset(triggerDustHeight, VehicleData), + "@brief Maximum height above surface to emit dust particles.\n\n" + "If the vehicle is less than triggerDustHeight above a static surface " + "with a material that has 'showDust' set to true, the vehicle will emit " + "particles from the dustEmitter." ); + addField( "dustHeight", TypeF32, Offset(dustHeight, VehicleData), + "Height above ground at which to emit particles from the dustEmitter." ); + + addField( "damageEmitter", TYPEID< ParticleEmitterData >(), Offset(damageEmitterList, VehicleData), VC_NUM_DAMAGE_EMITTERS, + "@brief Array of particle emitters used to generate damage (dust, smoke etc) " + "effects.\n\n" + "Currently, the first two emitters (indices 0 and 1) are used when the damage " + "level exceeds the associated damageLevelTolerance. The 3rd emitter is used " + "when the emitter point is underwater.\n\n" + "@see damageEmitterOffset" ); + addField( "damageEmitterOffset", TypePoint3F, Offset(damageEmitterOffset, VehicleData), VC_NUM_DAMAGE_EMITTER_AREAS, + "@brief Object space \"x y z\" offsets used to emit particles for the " + "active damageEmitter.\n\n" + "@tsexample\n" + "// damage levels\n" + "damageLevelTolerance[0] = 0.5;\n" + "damageEmitter[0] = SmokeEmitter;\n" + "// emit offsets (used for all active damage level emitters)\n" + "damageEmitterOffset[0] = \"0.5 3 1\";\n" + "damageEmitterOffset[1] = \"-0.5 3 1\";\n" + "numDmgEmitterAreas = 2;\n" + "@endtsexample\n" ); + addField( "damageLevelTolerance", TypeF32, Offset(damageLevelTolerance, VehicleData), VC_NUM_DAMAGE_LEVELS, + "@brief Damage levels (as a percentage of maxDamage) above which to begin " + "emitting particles from the associated damageEmitter.\n\n" + "Levels should be in order of increasing damage.\n\n" + "@see damageEmitterOffset" ); + addField( "numDmgEmitterAreas", TypeF32, Offset(numDmgEmitterAreas, VehicleData), + "Number of damageEmitterOffset values to use for each damageEmitter.\n\n" + "@see damageEmitterOffset" ); + + addField( "splashEmitter", TYPEID< ParticleEmitterData >(), Offset(splashEmitterList, VehicleData), VC_NUM_SPLASH_EMITTERS, + "Array of particle emitters used to generate splash effects." ); + addField( "splashFreqMod", TypeF32, Offset(splashFreqMod, VehicleData), + "@brief Number of splash particles to generate based on vehicle speed.\n\n" + "This value is multiplied by the current speed to determine how many " + "particles to generate each frame." ); + addField( "splashVelEpsilon", TypeF32, Offset(splashVelEpsilon, VehicleData), + "Minimum speed when moving through water to generate splash particles." ); + + addField( "exitSplashSoundVelocity", TypeF32, Offset(exitSplashSoundVel, VehicleData), + "Minimum velocity when leaving the water for the exitingWater sound to play." ); + addField( "softSplashSoundVelocity", TypeF32, Offset(softSplashSoundVel, VehicleData), + "Minimum velocity when entering the water for the imapactWaterEasy sound " + "to play.\n\n@see impactWaterEasy" ); + addField( "mediumSplashSoundVelocity", TypeF32, Offset(medSplashSoundVel, VehicleData), + "Minimum velocity when entering the water for the imapactWaterMedium sound " + "to play.\n\n@see impactWaterMedium" ); + addField( "hardSplashSoundVelocity", TypeF32, Offset(hardSplashSoundVel, VehicleData), + "Minimum velocity when entering the water for the imapactWaterHard sound " + "to play.\n\n@see impactWaterHard" ); + addField( "exitingWater", TYPEID< SFXProfile >(), Offset(waterSound[ExitWater], VehicleData), + "Sound to play when exiting the water." ); + addField( "impactWaterEasy", TYPEID< SFXProfile >(), Offset(waterSound[ImpactSoft], VehicleData), + "Sound to play when entering the water with speed >= softSplashSoundVelocity " + "and < mediumSplashSoundVelocity." ); + addField( "impactWaterMedium", TYPEID< SFXProfile >(), Offset(waterSound[ImpactMedium], VehicleData), + "Sound to play when entering the water with speed >= mediumSplashSoundVelocity " + "and < hardSplashSoundVelocity." ); + addField( "impactWaterHard", TYPEID< SFXProfile >(), Offset(waterSound[ImpactHard], VehicleData), + "Sound to play when entering the water with speed >= hardSplashSoundVelocity." ); + addField( "waterWakeSound", TYPEID< SFXProfile >(), Offset(waterSound[Wake], VehicleData), + "Looping sound to play while moving through the water." ); + + addField( "collDamageThresholdVel", TypeF32, Offset(collDamageThresholdVel, VehicleData), + "Minimum collision velocity to cause damage to this vehicle.\nCurrently unused." ); + addField( "collDamageMultiplier", TypeF32, Offset(collDamageMultiplier, VehicleData), + "@brief Damage to this vehicle after a collision (multiplied by collision " + "velocity).\n\nCurrently unused." ); + + Parent::initPersistFields(); +} + + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +//---------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(Vehicle); + +ConsoleDocClass( Vehicle, + "@brief Base functionality shared by all Vehicles (FlyingVehicle, HoverVehicle, " + "WheeledVehicle).\n\n" + "This object implements functionality shared by all Vehicle types, but should " + "not be instantiated directly. Create a FlyingVehicle, HoverVehicle, or " + "WheeledVehicle instead.\n" + "@note The model used for any Vehicle must include a collision mesh at detail " + "size -1.\n" + "@ingroup Vehicles\n" +); + +Vehicle::Vehicle() +{ + mDataBlock = 0; + mTypeMask |= VehicleObjectType | DynamicShapeObjectType; + + mDelta.pos = Point3F(0,0,0); + mDelta.posVec = Point3F(0,0,0); + mDelta.warpTicks = mDelta.warpCount = 0; + mDelta.dt = 1; + mDelta.move = NullMove; + mPredictionCount = 0; + mDelta.cameraOffset.set(0,0,0); + mDelta.cameraVec.set(0,0,0); + mDelta.cameraRot.set(0,0,0); + mDelta.cameraRotVec.set(0,0,0); + + mRigid.linPosition.set(0, 0, 0); + mRigid.linVelocity.set(0, 0, 0); + mRigid.angPosition.identity(); + mRigid.angVelocity.set(0, 0, 0); + mRigid.linMomentum.set(0, 0, 0); + mRigid.angMomentum.set(0, 0, 0); + mContacts.clear(); + + mSteering.set(0,0); + mThrottle = 0; + mJetting = false; + + mCameraOffset.set(0,0,0); + + dMemset( mDustEmitterList, 0, sizeof( mDustEmitterList ) ); + dMemset( mDamageEmitterList, 0, sizeof( mDamageEmitterList ) ); + dMemset( mSplashEmitterList, 0, sizeof( mSplashEmitterList ) ); + + mDisableMove = false; + restCount = 0; + + inLiquid = false; + mWakeSound = NULL; + + mWorkingQueryBox.minExtents.set(-1e9f, -1e9f, -1e9f); + mWorkingQueryBox.maxExtents.set(-1e9f, -1e9f, -1e9f); + mWorkingQueryBoxCountDown = sWorkingQueryBoxStaleThreshold; +} + +U32 Vehicle::getCollisionMask() +{ + AssertFatal(false, "Vehicle::getCollisionMask is pure virtual!"); + return 0; +} + +Point3F Vehicle::getVelocity() const +{ + return mRigid.linVelocity; +} + +//---------------------------------------------------------------------------- + +bool Vehicle::onAdd() +{ + if (!Parent::onAdd()) + return false; + + mWorkingQueryBox.minExtents.set(-1e9f, -1e9f, -1e9f); + mWorkingQueryBox.maxExtents.set(-1e9f, -1e9f, -1e9f); + + // When loading from a mission script, the base SceneObject's transform + // will have been set and needs to be transfered to the rigid body. + mRigid.setTransform(mObjToWorld); + + // Initialize interpolation vars. + mDelta.rot[1] = mDelta.rot[0] = mRigid.angPosition; + mDelta.pos = mRigid.linPosition; + mDelta.posVec = Point3F(0,0,0); + + // Create Emitters on the client + if( isClientObject() ) + { + if( mDataBlock->dustEmitter ) + { + for( int i=0; ionNewDataBlock( mDataBlock->dustEmitter, false ); + if( !mDustEmitterList[i]->registerObject() ) + { + Con::warnf( ConsoleLogEntry::General, "Could not register dust emitter for class: %s", mDataBlock->getName() ); + delete mDustEmitterList[i]; + mDustEmitterList[i] = NULL; + } + } + } + + U32 j; + for( j=0; jdamageEmitterList[j] ) + { + mDamageEmitterList[j] = new ParticleEmitter; + mDamageEmitterList[j]->onNewDataBlock( mDataBlock->damageEmitterList[j], false ); + if( !mDamageEmitterList[j]->registerObject() ) + { + Con::warnf( ConsoleLogEntry::General, "Could not register damage emitter for class: %s", mDataBlock->getName() ); + delete mDamageEmitterList[j]; + mDamageEmitterList[j] = NULL; + } + + } + } + + for( j=0; jsplashEmitterList[j] ) + { + mSplashEmitterList[j] = new ParticleEmitter; + mSplashEmitterList[j]->onNewDataBlock( mDataBlock->splashEmitterList[j], false ); + if( !mSplashEmitterList[j]->registerObject() ) + { + Con::warnf( ConsoleLogEntry::General, "Could not register splash emitter for class: %s", mDataBlock->getName() ); + delete mSplashEmitterList[j]; + mSplashEmitterList[j] = NULL; + } + + } + } + } + + // Create a new convex. + AssertFatal(mDataBlock->collisionDetails[0] != -1, "Error, a vehicle must have a collision-1 detail!"); + mConvex.mObject = this; + mConvex.pShapeBase = this; + mConvex.hullId = 0; + mConvex.box = mObjBox; + mConvex.box.minExtents.convolve(mObjScale); + mConvex.box.maxExtents.convolve(mObjScale); + mConvex.findNodeTransform(); + + return true; +} + +void Vehicle::onRemove() +{ + U32 i=0; + for( i=0; ideleteWhenEmpty(); + mDustEmitterList[i] = NULL; + } + } + + for( i=0; ideleteWhenEmpty(); + mDamageEmitterList[i] = NULL; + } + } + + for( i=0; ideleteWhenEmpty(); + mSplashEmitterList[i] = NULL; + } + } + + mWorkingQueryBox.minExtents.set(-1e9f, -1e9f, -1e9f); + mWorkingQueryBox.maxExtents.set(-1e9f, -1e9f, -1e9f); + + Parent::onRemove(); +} + + +//---------------------------------------------------------------------------- + +void Vehicle::processTick(const Move* move) +{ + PROFILE_SCOPE( Vehicle_ProcessTick ); + + Parent::processTick(move); + + // Warp to catch up to server + if (mDelta.warpCount < mDelta.warpTicks) + { + mDelta.warpCount++; + + // Set new pos. + mObjToWorld.getColumn(3,&mDelta.pos); + mDelta.pos += mDelta.warpOffset; + mDelta.rot[0] = mDelta.rot[1]; + mDelta.rot[1].interpolate(mDelta.warpRot[0],mDelta.warpRot[1],F32(mDelta.warpCount)/mDelta.warpTicks); + setPosition(mDelta.pos,mDelta.rot[1]); + + // Pos backstepping + mDelta.posVec.x = -mDelta.warpOffset.x; + mDelta.posVec.y = -mDelta.warpOffset.y; + mDelta.posVec.z = -mDelta.warpOffset.z; + } + else + { + if (!move) + { + if (isGhost()) + { + // If we haven't run out of prediction time, + // predict using the last known move. + if (mPredictionCount-- <= 0) + return; + move = &mDelta.move; + } + else + move = &NullMove; + } + + // Process input move + updateMove(move); + + // Save current rigid state interpolation + mDelta.posVec = mRigid.linPosition; + mDelta.rot[0] = mRigid.angPosition; + + // Update the physics based on the integration rate + S32 count = mDataBlock->integration; + --mWorkingQueryBoxCountDown; + updateWorkingCollisionSet(getCollisionMask()); + for (U32 i = 0; i < count; i++) + updatePos(TickSec / count); + + // Wrap up interpolation info + mDelta.pos = mRigid.linPosition; + mDelta.posVec -= mRigid.linPosition; + mDelta.rot[1] = mRigid.angPosition; + + // Update container database + setPosition(mRigid.linPosition, mRigid.angPosition); + setMaskBits(PositionMask); + updateContainer(); + } +} + +void Vehicle::interpolateTick(F32 dt) +{ + PROFILE_SCOPE( Vehicle_InterpolateTick ); + + Parent::interpolateTick(dt); + + if(dt == 0.0f) + setRenderPosition(mDelta.pos, mDelta.rot[1]); + else + { + QuatF rot; + rot.interpolate(mDelta.rot[1], mDelta.rot[0], dt); + Point3F pos = mDelta.pos + mDelta.posVec * dt; + setRenderPosition(pos,rot); + } + mDelta.dt = dt; +} + +void Vehicle::advanceTime(F32 dt) +{ + PROFILE_SCOPE( Vehicle_AdvanceTime ); + + Parent::advanceTime(dt); + + updateLiftoffDust( dt ); + updateDamageSmoke( dt ); + updateFroth(dt); + + // Update 3rd person camera offset. Camera update is done + // here as it's a client side only animation. + mCameraOffset -= + (mCameraOffset * mDataBlock->cameraDecay + + mRigid.linVelocity * mDataBlock->cameraLag) * dt; +} + + +//---------------------------------------------------------------------------- + +bool Vehicle::onNewDataBlock(GameBaseData* dptr,bool reload) +{ + mDataBlock = dynamic_cast(dptr); + if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload)) + return false; + + // Update Rigid Info + mRigid.mass = mDataBlock->mass; + mRigid.oneOverMass = 1 / mRigid.mass; + mRigid.friction = mDataBlock->body.friction; + mRigid.restitution = mDataBlock->body.restitution; + mRigid.setCenterOfMass(mDataBlock->massCenter); + + // Ignores massBox, just set sphere for now. Derived objects + // can set what they want. + mRigid.setObjectInertia(); + + if (isGhost()) + { + // Create the sound ahead of time. This reduces runtime + // costs and makes the system easier to understand. + SFX_DELETE( mWakeSound ); + + if ( mDataBlock->waterSound[VehicleData::Wake] ) + mWakeSound = SFX->createSource( mDataBlock->waterSound[VehicleData::Wake], &getTransform() ); + } + + return true; +} + + +//---------------------------------------------------------------------------- + +void Vehicle::getCameraParameters(F32 *min,F32* max,Point3F* off,MatrixF* rot) +{ + *min = mDataBlock->cameraMinDist; + *max = mDataBlock->cameraMaxDist; + + off->set(0,0,mDataBlock->cameraOffset); + rot->identity(); +} + + +//---------------------------------------------------------------------------- + +void Vehicle::getCameraTransform(F32* pos,MatrixF* mat) +{ + // Returns camera to world space transform + // Handles first person / third person camera position + if (isServerObject() && mShapeInstance) + mShapeInstance->animateNodeSubtrees(true); + + if (*pos == 0) { + getRenderEyeTransform(mat); + return; + } + + // Get the shape's camera parameters. + F32 min,max; + MatrixF rot; + Point3F offset; + getCameraParameters(&min,&max,&offset,&rot); + + // Start with the current eye position + MatrixF eye; + getRenderEyeTransform(&eye); + + // Build a transform that points along the eye axis + // but where the Z axis is always up. + if (mDataBlock->cameraRoll) + mat->mul(eye,rot); + else + { + MatrixF cam(1); + VectorF x,y,z(0,0,1); + eye.getColumn(1, &y); + mCross(y, z, &x); + x.normalize(); + mCross(x, y, &z); + z.normalize(); + cam.setColumn(0,x); + cam.setColumn(1,y); + cam.setColumn(2,z); + mat->mul(cam,rot); + } + + // Camera is positioned straight back along the eye's -Y axis. + // A ray is cast to make sure the camera doesn't go through + // anything solid. + VectorF vp,vec; + vp.x = vp.z = 0; + vp.y = -(max - min) * *pos; + eye.mulV(vp,&vec); + + // Use the camera node as the starting position if it exists. + Point3F osp,sp; + if (mDataBlock->cameraNode != -1) + { + mShapeInstance->mNodeTransforms[mDataBlock->cameraNode].getColumn(3,&osp); + getRenderTransform().mulP(osp,&sp); + } + else + eye.getColumn(3,&sp); + + // Make sure we don't hit ourself... + disableCollision(); + if (isMounted()) + getObjectMount()->disableCollision(); + + // Cast the ray into the container database to see if we're going + // to hit anything. + RayInfo collision; + Point3F ep = sp + vec + offset + mCameraOffset; + if (mContainer->castRay(sp, ep, + ~(WaterObjectType | GameBaseObjectType | DefaultObjectType | sTriggerMask), + &collision) == true) { + + // Shift the collision point back a little to try and + // avoid clipping against the front camera plane. + F32 t = collision.t - (-mDot(vec, collision.normal) / vec.len()) * 0.1; + if (t > 0.0f) + ep = sp + offset + mCameraOffset + (vec * t); + else + eye.getColumn(3,&ep); + } + mat->setColumn(3,ep); + + // Re-enable our collision. + if (isMounted()) + getObjectMount()->enableCollision(); + enableCollision(); + + // Apply Camera FX. + mat->mul( gCamFXMgr.getTrans() ); +} + + +//---------------------------------------------------------------------------- + +void Vehicle::getVelocity(const Point3F& r, Point3F* v) +{ + mRigid.getVelocity(r, v); +} + +void Vehicle::applyImpulse(const Point3F &pos, const Point3F &impulse) +{ + Point3F r; + mRigid.getOriginVector(pos,&r); + mRigid.applyImpulse(r, impulse); +} + + +//---------------------------------------------------------------------------- + +void Vehicle::updateMove(const Move* move) +{ + PROFILE_SCOPE( Vehicle_UpdateMove ); + + mDelta.move = *move; + + // Image Triggers + if (mDamageState == Enabled) { + setImageTriggerState(0,move->trigger[0]); + setImageTriggerState(1,move->trigger[1]); + } + + // Throttle + if(!mDisableMove) + mThrottle = move->y; + + // Steering + if (move != &NullMove) { + F32 y = move->yaw; + mSteering.x = mClampF(mSteering.x + y,-mDataBlock->maxSteeringAngle, + mDataBlock->maxSteeringAngle); + F32 p = move->pitch; + mSteering.y = mClampF(mSteering.y + p,-mDataBlock->maxSteeringAngle, + mDataBlock->maxSteeringAngle); + } + else { + mSteering.x = 0; + mSteering.y = 0; + } + + // Jetting flag + if (move->trigger[3]) { + if (!mJetting && getEnergyLevel() >= mDataBlock->minJetEnergy) + mJetting = true; + if (mJetting) { + F32 newEnergy = getEnergyLevel() - mDataBlock->jetEnergyDrain; + if (newEnergy < 0) { + newEnergy = 0; + mJetting = false; + } + setEnergyLevel(newEnergy); + } + } + else + mJetting = false; +} + + +//---------------------------------------------------------------------------- + +void Vehicle::setPosition(const Point3F& pos,const QuatF& rot) +{ + MatrixF mat; + rot.setMatrix(&mat); + mat.setColumn(3,pos); + Parent::setTransform(mat); +} + +void Vehicle::setRenderPosition(const Point3F& pos, const QuatF& rot) +{ + MatrixF mat; + rot.setMatrix(&mat); + mat.setColumn(3,pos); + Parent::setRenderTransform(mat); +} + +void Vehicle::setTransform(const MatrixF& newMat) +{ + mRigid.setTransform(newMat); + Parent::setTransform(newMat); + mRigid.atRest = false; + mContacts.clear(); +} + + +//----------------------------------------------------------------------------- + +void Vehicle::disableCollision() +{ + Parent::disableCollision(); + for (SceneObject* ptr = getMountList(); ptr; ptr = ptr->getMountLink()) + ptr->disableCollision(); +} + +void Vehicle::enableCollision() +{ + Parent::enableCollision(); + for (SceneObject* ptr = getMountList(); ptr; ptr = ptr->getMountLink()) + ptr->enableCollision(); +} + + +//---------------------------------------------------------------------------- +/** Update the physics +*/ + +void Vehicle::updatePos(F32 dt) +{ + PROFILE_SCOPE( Vehicle_UpdatePos ); + + Point3F origVelocity = mRigid.linVelocity; + + // Update internal forces acting on the body. + mRigid.clearForces(); + updateForces(dt); + + // Update collision information based on our current pos. + bool collided = false; + if (!mRigid.atRest) { + collided = updateCollision(dt); + + // Now that all the forces have been processed, lets + // see if we're at rest. Basically, if the kinetic energy of + // the vehicles is less than some percentage of the energy added + // by gravity for a short period, we're considered at rest. + // This should really be part of the rigid class... + if (mCollisionList.getCount()) + { + F32 k = mRigid.getKineticEnergy(); + F32 G = sVehicleGravity * dt; + F32 Kg = 0.5 * mRigid.mass * G * G; + if (k < sRestTol * Kg && ++restCount > sRestCount) + mRigid.setAtRest(); + } + else + restCount = 0; + } + + // Integrate forward + if (!mRigid.atRest) + mRigid.integrate(dt); + + // Deal with client and server scripting, sounds, etc. + if (isServerObject()) { + + // Check triggers and other objects that we normally don't + // collide with. This function must be called before notifyCollision + // as it will queue collision. + checkTriggers(); + + // Invoke the onCollision notify callback for all the objects + // we've just hit. + notifyCollision(); + + // Server side impact script callback + if (collided) { + VectorF collVec = mRigid.linVelocity - origVelocity; + F32 collSpeed = collVec.len(); + if (collSpeed > mDataBlock->minImpactSpeed) + onImpact(collVec); + } + + // Water script callbacks + if (!inLiquid && mWaterCoverage != 0.0f) { + mDataBlock->onEnterLiquid_callback( this, mWaterCoverage, mLiquidType.c_str() ); + inLiquid = true; + } + else if (inLiquid && mWaterCoverage == 0.0f) { + mDataBlock->onLeaveLiquid_callback( this, mLiquidType.c_str() ); + inLiquid = false; + } + + } + else { + + // Play impact sounds on the client. + if (collided) { + F32 collSpeed = (mRigid.linVelocity - origVelocity).len(); + S32 impactSound = -1; + if (collSpeed >= mDataBlock->hardImpactSpeed) + impactSound = VehicleData::Body::HardImpactSound; + else + if (collSpeed >= mDataBlock->softImpactSpeed) + impactSound = VehicleData::Body::SoftImpactSound; + + if (impactSound != -1 && mDataBlock->body.sound[impactSound] != NULL) + SFX->playOnce( mDataBlock->body.sound[impactSound], &getTransform() ); + } + + // Water volume sounds + F32 vSpeed = getVelocity().len(); + if (!inLiquid && mWaterCoverage >= 0.8f) { + if (vSpeed >= mDataBlock->hardSplashSoundVel) + SFX->playOnce( mDataBlock->waterSound[VehicleData::ImpactHard], &getTransform() ); + else + if (vSpeed >= mDataBlock->medSplashSoundVel) + SFX->playOnce( mDataBlock->waterSound[VehicleData::ImpactMedium], &getTransform() ); + else + if (vSpeed >= mDataBlock->softSplashSoundVel) + SFX->playOnce( mDataBlock->waterSound[VehicleData::ImpactSoft], &getTransform() ); + inLiquid = true; + } + else + if(inLiquid && mWaterCoverage < 0.8f) { + if (vSpeed >= mDataBlock->exitSplashSoundVel) + SFX->playOnce( mDataBlock->waterSound[VehicleData::ExitWater], &getTransform() ); + inLiquid = false; + } + } +} + + +//---------------------------------------------------------------------------- + +void Vehicle::updateForces(F32 /*dt*/) +{ + // Nothing here. +} + + +//----------------------------------------------------------------------------- +/** Update collision information + Update the convex state and check for collisions. If the object is in + collision, impact and contact forces are generated. +*/ + +bool Vehicle::updateCollision(F32 dt) +{ + PROFILE_SCOPE( Vehicle_UpdateCollision ); + + // Update collision information + MatrixF mat,cmat; + mConvex.transform = &mat; + mRigid.getTransform(&mat); + cmat = mConvex.getTransform(); + + mCollisionList.clear(); + CollisionState *state = mConvex.findClosestState(cmat, getScale(), mDataBlock->collisionTol); + if (state && state->dist <= mDataBlock->collisionTol) + { + //resolveDisplacement(ns,state,dt); + mConvex.getCollisionInfo(cmat, getScale(), &mCollisionList, mDataBlock->collisionTol); + } + + // Resolve collisions + bool collided = resolveCollision(mRigid,mCollisionList); + resolveContacts(mRigid,mCollisionList,dt); + return collided; +} + + +//---------------------------------------------------------------------------- +/** Resolve collision impacts + Handle collision impacts, as opposed to contacts. Impulses are calculated based + on standard collision resolution formulas. +*/ +bool Vehicle::resolveCollision(Rigid& ns,CollisionList& cList) +{ + PROFILE_SCOPE( Vehicle_ResolveCollision ); + + // Apply impulses to resolve collision + bool collided = false; + for (S32 i = 0; i < cList.getCount(); i++) + { + Collision& c = cList[i]; + if (c.distance < mDataBlock->collisionTol) + { + // Velocity into surface + Point3F v,r; + ns.getOriginVector(c.point,&r); + ns.getVelocity(r,&v); + F32 vn = mDot(v,c.normal); + + // Only interested in velocities greater than sContactTol, + // velocities less than that will be dealt with as contacts + // "constraints". + if (vn < -mDataBlock->contactTol) + { + + // Apply impulses to the rigid body to keep it from + // penetrating the surface. + ns.resolveCollision(cList[i].point, + cList[i].normal); + collided = true; + + // Keep track of objects we collide with + if (!isGhost() && c.object->getTypeMask() & ShapeBaseObjectType) + { + ShapeBase* col = static_cast(c.object); + queueCollision(col,v - col->getVelocity()); + } + } + } + } + + return collided; +} + +//---------------------------------------------------------------------------- +/** Resolve contact forces + Resolve contact forces using the "penalty" method. Forces are generated based + on the depth of penetration and the moment of inertia at the point of contact. +*/ +bool Vehicle::resolveContacts(Rigid& ns,CollisionList& cList,F32 dt) +{ + PROFILE_SCOPE( Vehicle_ResolveContacts ); + + // Use spring forces to manage contact constraints. + bool collided = false; + Point3F t,p(0,0,0),l(0,0,0); + for (S32 i = 0; i < cList.getCount(); i++) + { + const Collision& c = cList[i]; + if (c.distance < mDataBlock->collisionTol) + { + + // Velocity into the surface + Point3F v,r; + ns.getOriginVector(c.point,&r); + ns.getVelocity(r,&v); + F32 vn = mDot(v,c.normal); + + // Only interested in velocities less than mDataBlock->contactTol, + // velocities greater than that are dealt with as collisions. + if (mFabs(vn) < mDataBlock->contactTol) + { + collided = true; + + // Penetration force. This is actually a spring which + // will seperate the body from the collision surface. + F32 zi = 2 * mFabs(mRigid.getZeroImpulse(r,c.normal)); + F32 s = (mDataBlock->collisionTol - c.distance) * zi - ((vn / mDataBlock->contactTol) * zi); + Point3F f = c.normal * s; + + // Friction impulse, calculated as a function of the + // amount of force it would take to stop the motion + // perpendicular to the normal. + Point3F uv = v - (c.normal * vn); + F32 ul = uv.len(); + if (s > 0 && ul) + { + uv /= -ul; + F32 u = ul * ns.getZeroImpulse(r,uv); + s *= mRigid.friction; + if (u > s) + u = s; + f += uv * u; + } + + // Accumulate forces + p += f; + mCross(r,f,&t); + l += t; + } + } + } + + // Contact constraint forces act over time... + ns.linMomentum += p * dt; + ns.angMomentum += l * dt; + ns.updateVelocity(); + return true; +} + + +//---------------------------------------------------------------------------- + +bool Vehicle::resolveDisplacement(Rigid& ns,CollisionState *state, F32 dt) +{ + PROFILE_SCOPE( Vehicle_ResolveDisplacement ); + + SceneObject* obj = (state->a->getObject() == this)? + state->b->getObject(): state->a->getObject(); + + if (obj->isDisplacable() && ((obj->getTypeMask() & ShapeBaseObjectType) != 0)) + { + // Try to displace the object by the amount we're trying to move + Point3F objNewMom = ns.linVelocity * obj->getMass() * 1.1f; + Point3F objOldMom = obj->getMomentum(); + Point3F objNewVel = objNewMom / obj->getMass(); + + Point3F myCenter; + Point3F theirCenter; + getWorldBox().getCenter(&myCenter); + obj->getWorldBox().getCenter(&theirCenter); + if (mDot(myCenter - theirCenter, objNewMom) >= 0.0f || objNewVel.len() < 0.01) + { + objNewMom = (theirCenter - myCenter); + objNewMom.normalize(); + objNewMom *= 1.0f * obj->getMass(); + objNewVel = objNewMom / obj->getMass(); + } + + obj->setMomentum(objNewMom); + if (obj->displaceObject(objNewVel * 1.1f * dt) == true) + { + // Queue collision and change in velocity + VectorF dv = (objOldMom - objNewMom) / obj->getMass(); + queueCollision(static_cast(obj), dv); + return true; + } + } + + return false; +} + + +//---------------------------------------------------------------------------- + +void Vehicle::updateWorkingCollisionSet(const U32 mask) +{ + PROFILE_SCOPE( Vehicle_UpdateWorkingCollisionSet ); + + // First, we need to adjust our velocity for possible acceleration. It is assumed + // that we will never accelerate more than 20 m/s for gravity, plus 30 m/s for + // jetting, and an equivalent 10 m/s for vehicle accel. We also assume that our + // working list is updated on a Tick basis, which means we only expand our box by + // the possible movement in that tick, plus some extra for caching purposes + Box3F convexBox = mConvex.getBoundingBox(getTransform(), getScale()); + F32 len = (mRigid.linVelocity.len() + 50) * TickSec; + F32 l = (len * 1.1) + 0.1; // fudge factor + convexBox.minExtents -= Point3F(l, l, l); + convexBox.maxExtents += Point3F(l, l, l); + + // Check to see if it is actually necessary to construct the new working list, + // or if we can use the cached version from the last query. We use the x + // component of the min member of the mWorkingQueryBox, which is lame, but + // it works ok. + bool updateSet = false; + + // Check containment + if ((sWorkingQueryBoxStaleThreshold == -1 || mWorkingQueryBoxCountDown > 0) && mWorkingQueryBox.minExtents.x != -1e9f) + { + if (mWorkingQueryBox.isContained(convexBox) == false) + // Needed region is outside the cached region. Update it. + updateSet = true; + } + else + { + // Must update + updateSet = true; + } + + // Actually perform the query, if necessary + if (updateSet == true) + { + mWorkingQueryBoxCountDown = sWorkingQueryBoxStaleThreshold; + + const Point3F lPoint( sWorkingQueryBoxSizeMultiplier * l ); + mWorkingQueryBox = convexBox; + mWorkingQueryBox.minExtents -= lPoint; + mWorkingQueryBox.maxExtents += lPoint; + + disableCollision(); + mConvex.updateWorkingList(mWorkingQueryBox, mask); + enableCollision(); + } +} + + +//---------------------------------------------------------------------------- +/** Check collisions with trigger and items + Perform a container search using the current bounding box + of the main body, wheels are not included. This method should + only be called on the server. +*/ +void Vehicle::checkTriggers() +{ + Box3F bbox = mConvex.getBoundingBox(getTransform(), getScale()); + gServerContainer.findObjects(bbox,sTriggerMask,findCallback,this); +} + +/** The callback used in by the checkTriggers() method. + The checkTriggers method uses a container search which will + invoke this callback on each obj that matches. +*/ +void Vehicle::findCallback(SceneObject* obj,void *key) +{ + Vehicle* vehicle = reinterpret_cast(key); + U32 objectMask = obj->getTypeMask(); + + // Check: triggers, corpses and items, basically the same things + // that the player class checks for + if (objectMask & TriggerObjectType) { + Trigger* pTrigger = static_cast(obj); + pTrigger->potentialEnterObject(vehicle); + } + else if (objectMask & CorpseObjectType) { + ShapeBase* col = static_cast(obj); + vehicle->queueCollision(col,vehicle->getVelocity() - col->getVelocity()); + } + else if (objectMask & ItemObjectType) { + Item* item = static_cast(obj); + if (vehicle != item->getCollisionObject()) + vehicle->queueCollision(item,vehicle->getVelocity() - item->getVelocity()); + } +} + + +//---------------------------------------------------------------------------- + +void Vehicle::writePacketData(GameConnection *connection, BitStream *stream) +{ + Parent::writePacketData(connection, stream); + mathWrite(*stream, mSteering); + + mathWrite(*stream, mRigid.linPosition); + mathWrite(*stream, mRigid.angPosition); + mathWrite(*stream, mRigid.linMomentum); + mathWrite(*stream, mRigid.angMomentum); + stream->writeFlag(mRigid.atRest); + stream->writeFlag(mContacts.getCount() == 0); + + stream->writeFlag(mDisableMove); + stream->setCompressionPoint(mRigid.linPosition); +} + +void Vehicle::readPacketData(GameConnection *connection, BitStream *stream) +{ + Parent::readPacketData(connection, stream); + mathRead(*stream, &mSteering); + + mathRead(*stream, &mRigid.linPosition); + mathRead(*stream, &mRigid.angPosition); + mathRead(*stream, &mRigid.linMomentum); + mathRead(*stream, &mRigid.angMomentum); + mRigid.atRest = stream->readFlag(); + if (stream->readFlag()) + mContacts.clear(); + mRigid.updateInertialTensor(); + mRigid.updateVelocity(); + mRigid.updateCenterOfMass(); + + mDisableMove = stream->readFlag(); + stream->setCompressionPoint(mRigid.linPosition); +} + + +//---------------------------------------------------------------------------- + +U32 Vehicle::packUpdate(NetConnection *con, U32 mask, BitStream *stream) +{ + U32 retMask = Parent::packUpdate(con, mask, stream); + + stream->writeFlag(mJetting); + + // The rest of the data is part of the control object packet update. + // If we're controlled by this client, we don't need to send it. + if (stream->writeFlag(getControllingClient() == con && !(mask & InitialUpdateMask))) + return retMask; + + F32 yaw = (mSteering.x + mDataBlock->maxSteeringAngle) / (2 * mDataBlock->maxSteeringAngle); + F32 pitch = (mSteering.y + mDataBlock->maxSteeringAngle) / (2 * mDataBlock->maxSteeringAngle); + stream->writeFloat(yaw,9); + stream->writeFloat(pitch,9); + mDelta.move.pack(stream); + + if (stream->writeFlag(mask & PositionMask)) + { + stream->writeCompressedPoint(mRigid.linPosition); + mathWrite(*stream, mRigid.angPosition); + mathWrite(*stream, mRigid.linMomentum); + mathWrite(*stream, mRigid.angMomentum); + stream->writeFlag(mRigid.atRest); + } + + + stream->writeFloat(mClampF(getEnergyValue(), 0.f, 1.f), 8); + + return retMask; +} + +void Vehicle::unpackUpdate(NetConnection *con, BitStream *stream) +{ + Parent::unpackUpdate(con,stream); + + mJetting = stream->readFlag(); + + if (stream->readFlag()) + return; + + F32 yaw = stream->readFloat(9); + F32 pitch = stream->readFloat(9); + mSteering.x = (2 * yaw * mDataBlock->maxSteeringAngle) - mDataBlock->maxSteeringAngle; + mSteering.y = (2 * pitch * mDataBlock->maxSteeringAngle) - mDataBlock->maxSteeringAngle; + mDelta.move.unpack(stream); + + if (stream->readFlag()) + { + mPredictionCount = sMaxPredictionTicks; + F32 speed = mRigid.linVelocity.len(); + mDelta.warpRot[0] = mRigid.angPosition; + + // Read in new position and momentum values + stream->readCompressedPoint(&mRigid.linPosition); + mathRead(*stream, &mRigid.angPosition); + mathRead(*stream, &mRigid.linMomentum); + mathRead(*stream, &mRigid.angMomentum); + mRigid.atRest = stream->readFlag(); + mRigid.updateVelocity(); + + if (isProperlyAdded()) + { + // Determine number of ticks to warp based on the average + // of the client and server velocities. + Point3F cp = mDelta.pos + mDelta.posVec * mDelta.dt; + mDelta.warpOffset = mRigid.linPosition - cp; + + // Calc the distance covered in one tick as the average of + // the old speed and the new speed from the server. + F32 dt,as = (speed + mRigid.linVelocity.len()) * 0.5 * TickSec; + + // Cal how many ticks it will take to cover the warp offset. + // If it's less than what's left in the current tick, we'll just + // warp in the remaining time. + if (!as || (dt = mDelta.warpOffset.len() / as) > sMaxWarpTicks) + dt = mDelta.dt + sMaxWarpTicks; + else + dt = (dt <= mDelta.dt)? mDelta.dt : mCeil(dt - mDelta.dt) + mDelta.dt; + + // Adjust current frame interpolation + if (mDelta.dt) { + mDelta.pos = cp + (mDelta.warpOffset * (mDelta.dt / dt)); + mDelta.posVec = (cp - mDelta.pos) / mDelta.dt; + QuatF cr; + cr.interpolate(mDelta.rot[1],mDelta.rot[0],mDelta.dt); + mDelta.rot[1].interpolate(cr,mRigid.angPosition,mDelta.dt / dt); + mDelta.rot[0].extrapolate(mDelta.rot[1],cr,mDelta.dt); + } + + // Calculated multi-tick warp + mDelta.warpCount = 0; + mDelta.warpTicks = (S32)(mFloor(dt)); + if (mDelta.warpTicks) + { + mDelta.warpOffset = mRigid.linPosition - mDelta.pos; + mDelta.warpOffset /= (F32)mDelta.warpTicks; + mDelta.warpRot[0] = mDelta.rot[1]; + mDelta.warpRot[1] = mRigid.angPosition; + } + } + else + { + // Set the vehicle to the server position + mDelta.dt = 0; + mDelta.pos = mRigid.linPosition; + mDelta.posVec.set(0,0,0); + mDelta.rot[1] = mDelta.rot[0] = mRigid.angPosition; + mDelta.warpCount = mDelta.warpTicks = 0; + setPosition(mRigid.linPosition, mRigid.angPosition); + } + mRigid.updateCenterOfMass(); + } + + setEnergyLevel(stream->readFloat(8) * mDataBlock->maxEnergy); +} + + +//---------------------------------------------------------------------------- + +void Vehicle::consoleInit() +{ + Con::addVariable("$vehicle::workingQueryBoxStaleThreshold",TypeS32,&sWorkingQueryBoxStaleThreshold, + "@brief The maximum number of ticks that go by before the mWorkingQueryBox is considered stale and needs updating.\n\n" + "Other factors can cause the collision working query box to become invalidated, such as the vehicle moving far " + "enough outside of this cached box. The smaller this number, the more times the working list of triangles that are " + "considered for collision is refreshed. This has the greatest impact with colliding with high triangle count meshes.\n\n" + "@note Set to -1 to disable any time-based forced check.\n\n" + "@ingroup GameObjects\n"); + + Con::addVariable("$vehicle::workingQueryBoxSizeMultiplier",TypeF32,&sWorkingQueryBoxSizeMultiplier, + "@brief How much larger the mWorkingQueryBox should be made when updating the working collision list.\n\n" + "The larger this number the less often the working list will be updated due to motion, but any non-static shape that " + "moves into the query box will not be noticed.\n\n" + "@ingroup GameObjects\n"); +} + +void Vehicle::initPersistFields() +{ + addField( "disableMove", TypeBool, Offset(mDisableMove, Vehicle), + "When this flag is set, the vehicle will ignore throttle changes." ); + + Parent::initPersistFields(); +} + + +void Vehicle::mountObject(SceneObject *obj, S32 node, const MatrixF &xfm ) +{ + Parent::mountObject( obj, node, xfm ); + + // Clear objects off the working list that are from objects mounted to us. + // (This applies mostly to players...) + for ( CollisionWorkingList* itr = mConvex.getWorkingList().wLink.mNext; + itr != &mConvex.getWorkingList(); + itr = itr->wLink.mNext) + { + if (itr->mConvex->getObject() == obj) + { + CollisionWorkingList* cl = itr; + itr = itr->wLink.mPrev; + cl->free(); + } + } +} + +//---------------------------------------------------------------------------- + +void Vehicle::updateLiftoffDust( F32 dt ) +{ + Point3F offset( 0.0, 0.0, mDataBlock->dustHeight ); + emitDust( mDustEmitterList[ 0 ], mDataBlock->triggerDustHeight, offset, + ( U32 )( dt * 1000 ) ); +} + +//---------------------------------------------------------------------------- + +void Vehicle::updateDamageSmoke( F32 dt ) +{ + + for( S32 j=VehicleData::VC_NUM_DAMAGE_LEVELS-1; j>=0; j-- ) + { + F32 damagePercent = mDamage / mDataBlock->maxDamage; + if( damagePercent >= mDataBlock->damageLevelTolerance[j] ) + { + for( int i=0; inumDmgEmitterAreas; i++ ) + { + MatrixF trans = getTransform(); + Point3F offset = mDataBlock->damageEmitterOffset[i]; + trans.mulP( offset ); + Point3F emitterPoint = offset; + + if( pointInWater(offset ) ) + { + U32 emitterOffset = VehicleData::VC_BUBBLE_EMITTER; + if( mDamageEmitterList[emitterOffset] ) + { + mDamageEmitterList[emitterOffset]->emitParticles( emitterPoint, emitterPoint, Point3F( 0.0, 0.0, 1.0 ), getVelocity(), (U32)( dt * 1000 ) ); + } + } + else + { + if( mDamageEmitterList[j] ) + { + mDamageEmitterList[j]->emitParticles( emitterPoint, emitterPoint, Point3F( 0.0, 0.0, 1.0 ), getVelocity(), (U32)(dt * 1000)); + } + } + } + break; + } + } + +} + + +//-------------------------------------------------------------------------- +void Vehicle::updateFroth( F32 dt ) +{ + // update bubbles + Point3F moveDir = getVelocity(); + + Point3F contactPoint; + if( !collidingWithWater( contactPoint ) ) + { + if ( mWakeSound ) + mWakeSound->stop(); + return; + } + + F32 speed = moveDir.len(); + if( speed < mDataBlock->splashVelEpsilon ) speed = 0.0; + + U32 emitRate = (U32)(speed * mDataBlock->splashFreqMod * dt); + + U32 i; + + if ( mWakeSound ) + { + if ( !mWakeSound->isPlaying() ) + mWakeSound->play(); + + mWakeSound->setTransform( getTransform() ); + mWakeSound->setVelocity( getVelocity() ); + } + + for( i=0; iemitParticles( contactPoint, contactPoint, Point3F( 0.0, 0.0, 1.0 ), + moveDir, emitRate ); + } + } + +} + + +//-------------------------------------------------------------------------- +// Returns true if vehicle is intersecting a water surface (roughly) +//-------------------------------------------------------------------------- +bool Vehicle::collidingWithWater( Point3F &waterHeight ) +{ + Point3F curPos = getPosition(); + + F32 height = mFabs( mObjBox.maxExtents.z - mObjBox.minExtents.z ); + + RayInfo rInfo; + if( gClientContainer.castRay( curPos + Point3F(0.0, 0.0, height), curPos, WaterObjectType, &rInfo) ) + { + waterHeight = rInfo.point; + return true; + } + + return false; +} + +void Vehicle::setEnergyLevel(F32 energy) +{ + Parent::setEnergyLevel(energy); + setMaskBits(EnergyMask); +} + +void Vehicle::prepBatchRender( SceneRenderState *state, S32 mountedImageIndex ) +{ + Parent::prepBatchRender( state, mountedImageIndex ); + + if ( !gShowBoundingBox ) + return; + + if ( mountedImageIndex != -1 ) + { + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &Vehicle::_renderMuzzleVector ); + ri->objectIndex = mountedImageIndex; + ri->type = RenderPassManager::RIT_Editor; + state->getRenderPass()->addInst( ri ); + return; + } + + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &Vehicle::_renderMassAndContacts ); + ri->type = RenderPassManager::RIT_Editor; + state->getRenderPass()->addInst( ri ); +} + +void Vehicle::_renderMassAndContacts( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ) +{ + GFXStateBlockDesc desc; + desc.setBlend(false, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha); + desc.setZReadWrite(false,true); + desc.fillMode = GFXFillWireframe; + + // Render the mass center. + GFX->getDrawUtil()->drawCube(desc, Point3F(0.1f,0.1f,0.1f),mDataBlock->massCenter, ColorI(255, 255, 255), &mRenderObjToWorld); + + // Now render all the contact points. + for (int i = 0; i < mCollisionList.getCount(); i++) + { + const Collision& collision = mCollisionList[i]; + GFX->getDrawUtil()->drawCube(desc, Point3F(0.05f,0.05f,0.05f),collision.point, ColorI(0, 0, 255)); + } + + // Finally render the normals as one big batch. + PrimBuild::begin(GFXLineList, mCollisionList.getCount() * 2); + for (int i = 0; i < mCollisionList.getCount(); i++) + { + const Collision& collision = mCollisionList[i]; + PrimBuild::color3f(1, 1, 1); + PrimBuild::vertex3fv(collision.point); + PrimBuild::vertex3fv(collision.point + collision.normal * 0.05f); + } + PrimBuild::end(); + + // Build and render the collision polylist which is returned + // in the server's world space. + ClippedPolyList polyList; + polyList.mPlaneList.setSize(6); + polyList.mPlaneList[0].set(getWorldBox().minExtents,VectorF(-1,0,0)); + polyList.mPlaneList[1].set(getWorldBox().minExtents,VectorF(0,-1,0)); + polyList.mPlaneList[2].set(getWorldBox().minExtents,VectorF(0,0,-1)); + polyList.mPlaneList[3].set(getWorldBox().maxExtents,VectorF(1,0,0)); + polyList.mPlaneList[4].set(getWorldBox().maxExtents,VectorF(0,1,0)); + polyList.mPlaneList[5].set(getWorldBox().maxExtents,VectorF(0,0,1)); + Box3F dummyBox; + SphereF dummySphere; + buildPolyList(PLC_Collision, &polyList, dummyBox, dummySphere); + //polyList.render(); +} + +void Vehicle::_renderMuzzleVector( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ) +{ + const U32 index = ri->objectIndex; + + AssertFatal( index > 0 && index < MaxMountedImages, "Vehicle::_renderMuzzleVector() - Bad object index!" ); + AssertFatal( mMountedImageList[index].dataBlock, "Vehicle::_renderMuzzleVector() - Bad object index!" ); + + Point3F muzzlePoint, muzzleVector, endpoint; + getMuzzlePoint(index, &muzzlePoint); + getMuzzleVector(index, &muzzleVector); + endpoint = muzzlePoint + muzzleVector * 250; + + if (mSolidSB.isNull()) + { + GFXStateBlockDesc desc; + desc.setBlend(false, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha); + desc.setZReadWrite(false); + mSolidSB = GFX->createStateBlock(desc); + } + + GFX->setStateBlock(mSolidSB); + + PrimBuild::begin(GFXLineList, 2); + + PrimBuild::color4f(0, 1, 0, 1); + PrimBuild::vertex3fv(muzzlePoint); + PrimBuild::vertex3fv(endpoint); + + PrimBuild::end(); +} diff --git a/Engine/source/T3D/vehicles/vehicle.h b/Engine/source/T3D/vehicles/vehicle.h new file mode 100644 index 000000000..e8f42acd9 --- /dev/null +++ b/Engine/source/T3D/vehicles/vehicle.h @@ -0,0 +1,306 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _VEHICLE_H_ +#define _VEHICLE_H_ + +#ifndef _SHAPEBASE_H_ +#include "T3D/shapeBase.h" +#endif +#ifndef _RIGID_H_ +#include "T3D/rigid.h" +#endif +#ifndef _BOXCONVEX_H_ +#include "collision/boxConvex.h" +#endif + +class ParticleEmitter; +class ParticleEmitterData; +class ClippedPolyList; +struct RenderInst; +class Vehicle; + +//---------------------------------------------------------------------------- + +struct VehicleData: public ShapeBaseData +{ + typedef ShapeBaseData Parent; + + struct Body { + enum Sounds { + SoftImpactSound, + HardImpactSound, + MaxSounds, + }; + SFXProfile* sound[MaxSounds]; + F32 restitution; + F32 friction; + } body; + + enum VehicleConsts + { + VC_NUM_DUST_EMITTERS = 1, + VC_NUM_DAMAGE_EMITTER_AREAS = 2, + VC_NUM_DAMAGE_LEVELS = 2, + VC_NUM_BUBBLE_EMITTERS = 1, + VC_NUM_DAMAGE_EMITTERS = VC_NUM_DAMAGE_LEVELS + VC_NUM_BUBBLE_EMITTERS, + VC_NUM_SPLASH_EMITTERS = 2, + VC_BUBBLE_EMITTER = VC_NUM_DAMAGE_EMITTERS - VC_NUM_BUBBLE_EMITTERS, + }; + + enum Sounds { + ExitWater, + ImpactSoft, + ImpactMedium, + ImpactHard, + Wake, + MaxSounds + }; + SFXProfile* waterSound[MaxSounds]; + F32 exitSplashSoundVel; + F32 softSplashSoundVel; + F32 medSplashSoundVel; + F32 hardSplashSoundVel; + + F32 minImpactSpeed; + F32 softImpactSpeed; + F32 hardImpactSpeed; + F32 minRollSpeed; + F32 maxSteeringAngle; + + F32 collDamageThresholdVel; + F32 collDamageMultiplier; + + bool cameraRoll; ///< Roll the 3rd party camera + F32 cameraLag; ///< Amount of camera lag (lag += car velocity * lag) + F32 cameraDecay; ///< Rate at which camera returns to target pos. + F32 cameraOffset; ///< Vertical offset + + F32 minDrag; + F32 maxDrag; + S32 integration; ///< # of physics steps per tick + F32 collisionTol; ///< Collision distance tolerance + F32 contactTol; ///< Contact velocity tolerance + Point3F massCenter; ///< Center of mass for rigid body + Point3F massBox; ///< Size of inertial box + + F32 jetForce; + F32 jetEnergyDrain; ///< Energy drain/tick + F32 minJetEnergy; + + ParticleEmitterData * dustEmitter; + S32 dustID; + F32 triggerDustHeight; ///< height vehicle has to be under to kick up dust + F32 dustHeight; ///< dust height above ground + + ParticleEmitterData * damageEmitterList[ VC_NUM_DAMAGE_EMITTERS ]; + Point3F damageEmitterOffset[ VC_NUM_DAMAGE_EMITTER_AREAS ]; + S32 damageEmitterIDList[ VC_NUM_DAMAGE_EMITTERS ]; + F32 damageLevelTolerance[ VC_NUM_DAMAGE_LEVELS ]; + F32 numDmgEmitterAreas; + + ParticleEmitterData* splashEmitterList[VC_NUM_SPLASH_EMITTERS]; + S32 splashEmitterIDList[VC_NUM_SPLASH_EMITTERS]; + F32 splashFreqMod; + F32 splashVelEpsilon; + + // + VehicleData(); + bool preload(bool server, String &errorStr); + static void initPersistFields(); + virtual void packData(BitStream* stream); + virtual void unpackData(BitStream* stream); + + DECLARE_CONOBJECT(VehicleData); + + DECLARE_CALLBACK( void, onEnterLiquid, ( Vehicle* obj, F32 coverage, const char* type ) ); + DECLARE_CALLBACK( void, onLeaveLiquid, ( Vehicle* obj, const char* type ) ); +}; + + +//---------------------------------------------------------------------------- + +class Vehicle: public ShapeBase +{ + typedef ShapeBase Parent; + + protected: + enum CollisionFaceFlags { + BodyCollision = 0x1, + WheelCollision = 0x2, + }; + enum MaskBits { + PositionMask = Parent::NextFreeMask << 0, + EnergyMask = Parent::NextFreeMask << 1, + NextFreeMask = Parent::NextFreeMask << 2 + }; + + struct StateDelta { + Move move; ///< Last move from server + F32 dt; ///< Last interpolation time + // Interpolation data + Point3F pos; + Point3F posVec; + QuatF rot[2]; + // Warp data + S32 warpTicks; ///< Number of ticks to warp + S32 warpCount; ///< Current pos in warp + Point3F warpOffset; + QuatF warpRot[2]; + // + Point3F cameraOffset; + Point3F cameraVec; + Point3F cameraRot; + Point3F cameraRotVec; + }; + + StateDelta mDelta; + S32 mPredictionCount; ///< Number of ticks to predict + VehicleData* mDataBlock; + bool inLiquid; + SFXSource* mWakeSound; + + Point3F mCameraOffset; ///< 3rd person camera + + // Control + Point2F mSteering; + F32 mThrottle; + bool mJetting; + + // Rigid Body + bool mDisableMove; + + GFXStateBlockRef mSolidSB; + + Box3F mWorkingQueryBox; + S32 mWorkingQueryBoxCountDown; + + CollisionList mCollisionList; + CollisionList mContacts; + Rigid mRigid; + ShapeBaseConvex mConvex; + int restCount; + + SimObjectPtr mDustEmitterList[VehicleData::VC_NUM_DUST_EMITTERS]; + SimObjectPtr mDamageEmitterList[VehicleData::VC_NUM_DAMAGE_EMITTERS]; + SimObjectPtr mSplashEmitterList[VehicleData::VC_NUM_SPLASH_EMITTERS]; + + // + virtual bool onNewDataBlock( GameBaseData *dptr, bool reload ); + void updatePos(F32 dt); + bool updateCollision(F32 dt); + bool resolveCollision(Rigid& ns,CollisionList& cList); + bool resolveContacts(Rigid& ns,CollisionList& cList,F32 dt); + bool resolveDisplacement(Rigid& ns,CollisionState *state,F32 dt); + bool findContacts(Rigid& ns,CollisionList& cList); + void checkTriggers(); + static void findCallback(SceneObject* obj,void * key); + + void setPosition(const Point3F& pos,const QuatF& rot); + void setRenderPosition(const Point3F& pos,const QuatF& rot); + void setTransform(const MatrixF& mat); + +// virtual bool collideBody(const MatrixF& mat,Collision* info) = 0; + virtual void updateMove(const Move* move); + virtual void updateForces(F32 dt); + + void writePacketData(GameConnection * conn, BitStream *stream); + void readPacketData (GameConnection * conn, BitStream *stream); + U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream); + void unpackUpdate(NetConnection *conn, BitStream *stream); + + void updateLiftoffDust( F32 dt ); + void updateDamageSmoke( F32 dt ); + + void updateWorkingCollisionSet(const U32 mask); + virtual U32 getCollisionMask(); + + void updateFroth( F32 dt ); + bool collidingWithWater( Point3F &waterHeight ); + + /// ObjectRenderInst delegate hooked up in prepBatchRender + /// if GameBase::gShowBoundingBox is true. + void _renderMassAndContacts( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); + + /// ObjectRenderInst delegate hooked up in prepBatchRender + /// if GameBase::gShowBoundingBox is true. + void _renderMuzzleVector( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); + +public: + // Test code... + static ClippedPolyList* sPolyList; + static S32 sVehicleCount; + + // + Vehicle(); + static void consoleInit(); + static void initPersistFields(); + void processTick(const Move *move); + bool onAdd(); + void onRemove(); + + /// Interpolates between move ticks @see processTick + /// @param dt Change in time between the last call and this call to the function + void interpolateTick(F32 dt); + void advanceTime(F32 dt); + + /// Disables collisions for this vehicle and all mounted objects + void disableCollision(); + + /// Enables collisions for this vehicle and all mounted objects + void enableCollision(); + + /// Returns the velocity of the vehicle + Point3F getVelocity() const; + + void setEnergyLevel(F32 energy); + + void prepBatchRender( SceneRenderState *state, S32 mountedImageIndex ); + + ///@name Rigid body methods + ///@{ + + /// This method will get the velocity of the object, taking into account + /// angular velocity. + /// @param r Point on the object you want the velocity of, relative to Center of Mass + /// @param vel Velocity (out) + void getVelocity(const Point3F& r, Point3F* vel); + + /// Applies an impulse force + /// @param r Point on the object to apply impulse to, r is relative to Center of Mass + /// @param impulse Impulse vector to apply. + void applyImpulse(const Point3F &r, const Point3F &impulse); + + void getCameraParameters(F32 *min, F32* max, Point3F* offset, MatrixF* rot); + void getCameraTransform(F32* pos, MatrixF* mat); + ///@} + + /// @name Mounted objects + /// @{ + virtual void mountObject( SceneObject *obj, S32 node, const MatrixF &xfm = MatrixF::Identity ); + /// @} + + DECLARE_CONOBJECT(Vehicle); +}; + + +#endif diff --git a/Engine/source/T3D/vehicles/vehicleBlocker.cpp b/Engine/source/T3D/vehicles/vehicleBlocker.cpp new file mode 100644 index 000000000..e85d44ff9 --- /dev/null +++ b/Engine/source/T3D/vehicles/vehicleBlocker.cpp @@ -0,0 +1,154 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/vehicles/vehicleBlocker.h" + +#include "core/stream/bitStream.h" +#include "scene/sceneRenderState.h" +#include "scene/sceneManager.h" +#include "math/mathIO.h" +#include "console/consoleTypes.h" + +IMPLEMENT_CO_NETOBJECT_V1(VehicleBlocker); + +ConsoleDocClass( VehicleBlocker, + "@brief Legacy class from Tribes, originally used for blocking Vehicle objects.\n\n" + + "@note This is no longer useful and should be deprecated soon.\n\n" + + "@internal\n" +); + +//-------------------------------------------------------------------------- +//-------------------------------------------------------------------------- +VehicleBlocker::VehicleBlocker() +{ + mNetFlags.set(Ghostable | ScopeAlways); + + mTypeMask = VehicleBlockerObjectType; + + mConvexList = new Convex; +} + +VehicleBlocker::~VehicleBlocker() +{ + delete mConvexList; + mConvexList = NULL; +} + +//-------------------------------------------------------------------------- +void VehicleBlocker::initPersistFields() +{ + addField("dimensions", TypePoint3F, Offset(mDimensions, VehicleBlocker)); + Parent::initPersistFields(); +} + +//-------------------------------------------------------------------------- +bool VehicleBlocker::onAdd() +{ + if(!Parent::onAdd()) + return false; + + mObjBox.minExtents.set(-mDimensions.x, -mDimensions.y, 0); + mObjBox.maxExtents.set( mDimensions.x, mDimensions.y, mDimensions.z); + resetWorldBox(); + setRenderTransform(mObjToWorld); + + addToScene(); + + return true; +} + + +void VehicleBlocker::onRemove() +{ + mConvexList->nukeList(); + removeFromScene(); + + Parent::onRemove(); +} + + +U32 VehicleBlocker::packUpdate(NetConnection *con, U32 mask, BitStream *stream) +{ + U32 retMask = Parent::packUpdate(con, mask, stream); + + mathWrite(*stream, getTransform()); + mathWrite(*stream, getScale()); + mathWrite(*stream, mDimensions); + + return retMask; +} + + +void VehicleBlocker::unpackUpdate(NetConnection *con, BitStream *stream) +{ + Parent::unpackUpdate(con, stream); + + MatrixF mat; + Point3F scale; + Box3F objBox; + mathRead(*stream, &mat); + mathRead(*stream, &scale); + mathRead(*stream, &mDimensions); + mObjBox.minExtents.set(-mDimensions.x, -mDimensions.y, 0); + mObjBox.maxExtents.set( mDimensions.x, mDimensions.y, mDimensions.z); + setScale(scale); + setTransform(mat); +} + + +void VehicleBlocker::buildConvex(const Box3F& box, Convex* convex) +{ + // These should really come out of a pool + mConvexList->collectGarbage(); + + if (box.isOverlapped(getWorldBox()) == false) + return; + + // Just return a box convex for the entire shape... + Convex* cc = 0; + CollisionWorkingList& wl = convex->getWorkingList(); + for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) + { + if (itr->mConvex->getType() == BoxConvexType && + itr->mConvex->getObject() == this) { + cc = itr->mConvex; + break; + } + } + if (cc) + return; + + // Create a new convex. + BoxConvex* cp = new BoxConvex; + mConvexList->registerObject(cp); + convex->addToWorkingList(cp); + cp->init(this); + + mObjBox.getCenter(&cp->mCenter); + cp->mSize.x = mObjBox.len_x() / 2.0f; + cp->mSize.y = mObjBox.len_y() / 2.0f; + cp->mSize.z = mObjBox.len_z() / 2.0f; +} + diff --git a/Engine/source/T3D/vehicles/vehicleBlocker.h b/Engine/source/T3D/vehicles/vehicleBlocker.h new file mode 100644 index 000000000..e2861bbe7 --- /dev/null +++ b/Engine/source/T3D/vehicles/vehicleBlocker.h @@ -0,0 +1,61 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _VEHICLEBLOCKER_H_ +#define _VEHICLEBLOCKER_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _BOXCONVEX_H_ +#include "collision/boxConvex.h" +#endif + +//-------------------------------------------------------------------------- +class VehicleBlocker : public SceneObject +{ + typedef SceneObject Parent; + friend class VehicleBlockerConvex; + + protected: + bool onAdd(); + void onRemove(); + + // Collision + void buildConvex(const Box3F& box, Convex* convex); + protected: + Convex* mConvexList; + + Point3F mDimensions; + + public: + VehicleBlocker(); + ~VehicleBlocker(); + + DECLARE_CONOBJECT(VehicleBlocker); + static void initPersistFields(); + + U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream); + void unpackUpdate(NetConnection *conn, BitStream *stream); +}; + +#endif // _H_VEHICLEBLOCKER diff --git a/Engine/source/T3D/vehicles/wheeledVehicle.cpp b/Engine/source/T3D/vehicles/wheeledVehicle.cpp new file mode 100644 index 000000000..9558b65dc --- /dev/null +++ b/Engine/source/T3D/vehicles/wheeledVehicle.cpp @@ -0,0 +1,1660 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/vehicles/wheeledVehicle.h" + +#include "math/mMath.h" +#include "math/mathIO.h" +#include "console/simBase.h" +#include "console/console.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "collision/clippedPolyList.h" +#include "collision/planeExtractor.h" +#include "core/stream/bitStream.h" +#include "core/dnet.h" +#include "T3D/gameBase/gameConnection.h" +#include "ts/tsShapeInstance.h" +#include "T3D/fx/particleEmitter.h" +#include "sfx/sfxSystem.h" +#include "sfx/sfxTrack.h" +#include "sfx/sfxSource.h" +#include "sfx/sfxTypes.h" +#include "scene/sceneManager.h" +#include "core/resourceManager.h" +#include "materials/materialDefinition.h" +#include "materials/baseMatInstance.h" +#include "lighting/lightQuery.h" + + +// Collision masks are used to determin what type of objects the +// wheeled vehicle will collide with. +static U32 sClientCollisionMask = + TerrainObjectType | InteriorObjectType | + PlayerObjectType | StaticShapeObjectType | + VehicleObjectType | VehicleBlockerObjectType; + +// Gravity constant +static F32 sWheeledVehicleGravity = -20; + +// Misc. sound constants +static F32 sMinSquealVolume = 0.05f; +static F32 sIdleEngineVolume = 0.2f; + + +//---------------------------------------------------------------------------- +// Vehicle Tire Data Block +//---------------------------------------------------------------------------- + +IMPLEMENT_CO_DATABLOCK_V1(WheeledVehicleTire); + +ConsoleDocClass( WheeledVehicleTire, + "@brief Defines the properties of a WheeledVehicle tire.\n\n" + "Tires act as springs and generate lateral and longitudinal forces to move " + "the vehicle. These distortion/spring forces are what convert wheel angular " + "velocity into forces that act on the rigid body.\n" + "@ingroup Vehicles\n" +); + +WheeledVehicleTire::WheeledVehicleTire() +{ + shape = 0; + shapeName = ""; + staticFriction = 1; + kineticFriction = 0.5f; + restitution = 1; + radius = 0.6f; + lateralForce = 10; + lateralDamping = 1; + lateralRelaxation = 1; + longitudinalForce = 10; + longitudinalDamping = 1; + longitudinalRelaxation = 1; + mass = 1.f; +} + +bool WheeledVehicleTire::preload(bool server, String &errorStr) +{ + // Load up the tire shape. ShapeBase has an option to force a + // CRC check, this is left out here, but could be easily added. + if (shapeName && shapeName[0]) + { + + // Load up the shape resource + shape = ResourceManager::get().load(shapeName); + if (!bool(shape)) + { + errorStr = String::ToString("WheeledVehicleTire: Couldn't load shape \"%s\"",shapeName); + return false; + } + + // Determinw wheel radius from the shape's bounding box. + // The tire should be built with it's hub axis along the + // object's Y axis. + radius = shape->bounds.len_z() / 2; + } + + return true; +} + +void WheeledVehicleTire::initPersistFields() +{ + addField( "shapeFile",TypeShapeFilename,Offset(shapeName,WheeledVehicleTire), + "The path to the shape to use for the wheel." ); + addField( "mass", TypeF32, Offset(mass, WheeledVehicleTire), + "The mass of the wheel.\nCurrently unused." ); + addField( "radius", TypeF32, Offset(radius, WheeledVehicleTire), + "@brief The radius of the wheel.\n\n" + "The radius is determined from the bounding box of the shape provided " + "in the shapefile field, and does not need to be specified in script. " + "The tire should be built with its hub axis along the object's Y-axis." ); + addField( "staticFriction", TypeF32, Offset(staticFriction, WheeledVehicleTire), + "Tire friction when the wheel is not slipping (has traction)." ); + addField( "kineticFriction", TypeF32, Offset(kineticFriction, WheeledVehicleTire), + "Tire friction when the wheel is slipping (no traction)." ); + addField( "restitution", TypeF32, Offset(restitution, WheeledVehicleTire), + "Tire restitution.\nCurrently unused." ); + addField( "lateralForce", TypeF32, Offset(lateralForce, WheeledVehicleTire), + "@brief Tire force perpendicular to the direction of movement.\n\n" + "Lateral force can in simple terms be considered left/right steering " + "force. WheeledVehicles are acted upon by forces generated by their tires " + "and the lateralForce measures the magnitude of the force exerted on the " + "vehicle when the tires are deformed along the x-axis. With real wheeled " + "vehicles, tires are constantly being deformed and it is the interplay of " + "deformation forces which determines how a vehicle moves. In Torque's " + "simulation of vehicle physics, tire deformation obviously can't be handled " + "with absolute realism, but the interplay of a vehicle's velocity, its " + "engine's torque and braking forces, and its wheels' friction, lateral " + "deformation, lateralDamping, lateralRelaxation, longitudinal deformation, " + "longitudinalDamping, and longitudinalRelaxation forces, along with its " + "wheels' angular velocity are combined to create a robust real-time " + "physical simulation.\n\n" + "For this field, the larger the value supplied for the lateralForce, the " + "larger the effect steering maneuvers can have. In Torque tire forces are " + "applied at a vehicle's wheel hubs." ); + addField( "lateralDamping", TypeF32, Offset(lateralDamping, WheeledVehicleTire), + "Damping force applied against lateral forces generated by the tire.\n\n" + "@see lateralForce" ); + addField( "lateralRelaxation", TypeF32, Offset(lateralRelaxation, WheeledVehicleTire), + "@brief Relaxing force applied against lateral forces generated by the tire.\n\n" + "The lateralRelaxation force measures how strongly the tire effectively " + "un-deforms.\n\n@see lateralForce" ); + addField( "longitudinalForce", TypeF32, Offset(longitudinalForce, WheeledVehicleTire), + "@brief Tire force in the direction of movement.\n\n" + "Longitudinal force can in simple terms be considered forward/backward " + "movement force. WheeledVehicles are acted upon by forces generated by " + "their tires and the longitudinalForce measures the magnitude of the " + "force exerted on the vehicle when the tires are deformed along the y-axis.\n\n" + "For this field, the larger the value, the larger the effect " + "acceleration/deceleration inputs have.\n\n" + "@see lateralForce" ); + addField( "longitudinalDamping", TypeF32, Offset(longitudinalDamping, WheeledVehicleTire), + "Damping force applied against longitudinal forces generated by the tire.\n\n" + "@see longitudinalForce" ); + addField( "longitudinalRelaxation", TypeF32, Offset(longitudinalRelaxation, WheeledVehicleTire), + "@brief Relaxing force applied against longitudinal forces generated by the tire.\n\n" + "The longitudinalRelaxation force measures how strongly the tire effectively " + "un-deforms.\n\n" + "@see longitudinalForce" ); + + Parent::initPersistFields(); +} + +void WheeledVehicleTire::packData(BitStream* stream) +{ + Parent::packData(stream); + + stream->writeString(shapeName); + stream->write(mass); + stream->write(staticFriction); + stream->write(kineticFriction); + stream->write(restitution); + stream->write(radius); + stream->write(lateralForce); + stream->write(lateralDamping); + stream->write(lateralRelaxation); + stream->write(longitudinalForce); + stream->write(longitudinalDamping); + stream->write(longitudinalRelaxation); +} + +void WheeledVehicleTire::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + + shapeName = stream->readSTString(); + stream->read(&mass); + stream->read(&staticFriction); + stream->read(&kineticFriction); + stream->read(&restitution); + stream->read(&radius); + stream->read(&lateralForce); + stream->read(&lateralDamping); + stream->read(&lateralRelaxation); + stream->read(&longitudinalForce); + stream->read(&longitudinalDamping); + stream->read(&longitudinalRelaxation); +} + + +//---------------------------------------------------------------------------- +// Vehicle Spring Data Block +//---------------------------------------------------------------------------- + +IMPLEMENT_CO_DATABLOCK_V1(WheeledVehicleSpring); + +ConsoleDocClass( WheeledVehicleSpring, + "@brief Defines the properties of a WheeledVehicle spring.\n\n" + "@ingroup Vehicles\n" +); + +WheeledVehicleSpring::WheeledVehicleSpring() +{ + length = 1; + force = 10; + damping = 1; + antiSway = 1; +} + +void WheeledVehicleSpring::initPersistFields() +{ + addField( "length", TypeF32, Offset(length, WheeledVehicleSpring), + "@brief Maximum spring length. ie. how far the wheel can extend from the " + "root hub position.\n\n" + "This should be set to the vertical (Z) distance the hub travels in the " + "associated spring animation." ); + addField( "force", TypeF32, Offset(force, WheeledVehicleSpring), + "@brief Maximum spring force (when compressed to minimum length, 0).\n\n" + "Increasing this will make the vehicle suspension ride higher (for a given " + "vehicle mass), and also make the vehicle more bouncy when landing jumps." ); + addField( "damping", TypeF32, Offset(damping, WheeledVehicleSpring), + "@brief Force applied to slow changes to the extension of this spring.\n\n" + "Increasing this makes the suspension stiffer which can help stabilise " + "bouncy vehicles." ); + addField( "antiSwayForce", TypeF32, Offset(antiSway, WheeledVehicleSpring), + "@brief Force applied to equalize extension of the spring on the opposite " + "wheel.\n\n" + "This force helps to keep the suspension balanced when opposite wheels " + "are at different heights." ); + + Parent::initPersistFields(); +} + +void WheeledVehicleSpring::packData(BitStream* stream) +{ + Parent::packData(stream); + + stream->write(length); + stream->write(force); + stream->write(damping); + stream->write(antiSway); +} + +void WheeledVehicleSpring::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + + stream->read(&length); + stream->read(&force); + stream->read(&damping); + stream->read(&antiSway); +} + + +//---------------------------------------------------------------------------- +// Wheeled Vehicle Data Block +//---------------------------------------------------------------------------- + +//---------------------------------------------------------------------------- + +IMPLEMENT_CO_DATABLOCK_V1(WheeledVehicleData); + +ConsoleDocClass( WheeledVehicleData, + "@brief Defines the properties of a WheeledVehicle.\n\n" + "@ingroup Vehicles\n" +); + +WheeledVehicleData::WheeledVehicleData() +{ + tireEmitter = 0; + maxWheelSpeed = 40; + engineTorque = 1; + engineBrake = 1; + brakeTorque = 1; + brakeLightSequence = -1; + steeringSequence = -1; + wheelCount = 0; + + for (S32 i = 0; i < MaxSounds; i++) + sound[i] = 0; +} + + +//---------------------------------------------------------------------------- +/** Load the vehicle shape + Loads and extracts information from the vehicle shape. + + Wheel Sequences + spring# Wheel spring motion: time 0 = wheel fully extended, + the hub must be displaced, but not directly animated + as it will be rotated in code. + Other Sequences + steering Wheel steering: time 0 = full right, 0.5 = center + brakeLight Brake light, time 0 = off, 1 = braking + + Wheel Nodes + hub# Wheel hub + + The steering and animation sequences are optional. +*/ +bool WheeledVehicleData::preload(bool server, String &errorStr) +{ + if (!Parent::preload(server, errorStr)) + return false; + + // A temporary shape instance is created so that we can + // animate the shape and extract wheel information. + TSShapeInstance* si = new TSShapeInstance(mShape, false); + + // Resolve objects transmitted from server + if (!server) { + for (S32 i = 0; i < MaxSounds; i++) + if( !sfxResolve( &sound[ i ], errorStr ) ) + return false; + + if (tireEmitter) + Sim::findObject(SimObjectId(tireEmitter),tireEmitter); + } + + // Extract wheel information from the shape + TSThread* thread = si->addThread(); + Wheel* wp = wheel; + char buff[10]; + for (S32 i = 0; i < MaxWheels; i++) { + + // The wheel must have a hub node to operate at all. + dSprintf(buff,sizeof(buff),"hub%d",i); + wp->springNode = mShape->findNode(buff); + if (wp->springNode != -1) { + + // Check for spring animation.. If there is none we just grab + // the current position of the hub. Otherwise we'll animate + // and get the position at time 0. + dSprintf(buff,sizeof(buff),"spring%d",i); + wp->springSequence = mShape->findSequence(buff); + if (wp->springSequence == -1) + si->mNodeTransforms[wp->springNode].getColumn(3, &wp->pos); + else { + si->setSequence(thread,wp->springSequence,0); + si->animate(); + si->mNodeTransforms[wp->springNode].getColumn(3, &wp->pos); + + // Determin the length of the animation so we can scale it + // according the actual wheel position. + Point3F downPos; + si->setSequence(thread,wp->springSequence,1); + si->animate(); + si->mNodeTransforms[wp->springNode].getColumn(3, &downPos); + wp->springLength = wp->pos.z - downPos.z; + if (!wp->springLength) + wp->springSequence = -1; + } + + // Match wheels that are mirrored along the Y axis. + mirrorWheel(wp); + wp++; + } + } + wheelCount = wp - wheel; + + // Check for steering. Should think about normalizing the + // steering animation the way the suspension is, but I don't + // think it's as critical. + steeringSequence = mShape->findSequence("steering"); + + // Brakes + brakeLightSequence = mShape->findSequence("brakelight"); + + // Extract collision planes from shape collision detail level + if (collisionDetails[0] != -1) { + MatrixF imat(1); + SphereF sphere; + sphere.center = mShape->center; + sphere.radius = mShape->radius; + PlaneExtractorPolyList polyList; + polyList.mPlaneList = &rigidBody.mPlaneList; + polyList.setTransform(&imat, Point3F(1,1,1)); + si->buildPolyList(&polyList,collisionDetails[0]); + } + + delete si; + return true; +} + + +//---------------------------------------------------------------------------- +/** Find a matching lateral wheel + Looks for a matching wheeling mirrored along the Y axis, within some + tolerance (current 0.5m), if one is found, the two wheels are lined up. +*/ +bool WheeledVehicleData::mirrorWheel(Wheel* we) +{ + we->opposite = -1; + for (Wheel* wp = wheel; wp != we; wp++) + if (mFabs(wp->pos.y - we->pos.y) < 0.5) + { + we->pos.x = -wp->pos.x; + we->pos.y = wp->pos.y; + we->pos.z = wp->pos.z; + we->opposite = wp - wheel; + wp->opposite = we - wheel; + return true; + } + return false; +} + + +//---------------------------------------------------------------------------- + +void WheeledVehicleData::initPersistFields() +{ + addField( "jetSound", TYPEID< SFXTrack >(), Offset(sound[JetSound], WheeledVehicleData), + "Looping sound played when the vehicle is jetting." ); + addField( "engineSound", TYPEID< SFXTrack >(), Offset(sound[EngineSound], WheeledVehicleData), + "@brief Looping engine sound.\n\n" + "The pitch is dynamically adjusted based on the current engine RPM" ); + addField("squealSound", TYPEID< SFXTrack >(), Offset(sound[SquealSound], WheeledVehicleData), + "@brief Looping sound played while any of the wheels is slipping.\n\n" + "The volume is dynamically adjusted based on how much the wheels are slipping." ); + addField("WheelImpactSound", TYPEID< SFXTrack >(), Offset(sound[WheelImpactSound], WheeledVehicleData), + "Sound played when the wheels impact the ground.\nCurrently unused." ); + + addField("tireEmitter",TYPEID< ParticleEmitterData >(), Offset(tireEmitter, WheeledVehicleData), + "ParticleEmitterData datablock used to generate particles from each wheel " + "when the vehicle is moving and the wheel is in contact with the ground."); + addField("maxWheelSpeed", TypeF32, Offset(maxWheelSpeed, WheeledVehicleData), + "@brief Maximum linear velocity of each wheel.\n\n" + "This caps the maximum speed of the vehicle." ); + addField("engineTorque", TypeF32, Offset(engineTorque, WheeledVehicleData), + "@brief Torque available from the engine at 100% throttle.\n\n" + "This controls vehicle acceleration. ie. how fast it will reach maximum speed." ); + addField("engineBrake", TypeF32, Offset(engineBrake, WheeledVehicleData), + "@brief Braking torque applied by the engine when the throttle and brake " + "are both 0.\n\n" + "This controls how quickly the vehicle will coast to a stop." ); + addField("brakeTorque", TypeF32, Offset(brakeTorque, WheeledVehicleData), + "@brief Torque applied when braking.\n\n" + "This controls how fast the vehicle will stop when the brakes are applied." ); + + Parent::initPersistFields(); +} + + +//---------------------------------------------------------------------------- + +void WheeledVehicleData::packData(BitStream* stream) +{ + Parent::packData(stream); + + if (stream->writeFlag(tireEmitter)) + stream->writeRangedU32(packed? SimObjectId(tireEmitter): + tireEmitter->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast); + + for (S32 i = 0; i < MaxSounds; i++) + sfxWrite( stream, sound[ i ] ); + + stream->write(maxWheelSpeed); + stream->write(engineTorque); + stream->write(engineBrake); + stream->write(brakeTorque); +} + +void WheeledVehicleData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + + tireEmitter = stream->readFlag()? + (ParticleEmitterData*) stream->readRangedU32(DataBlockObjectIdFirst, + DataBlockObjectIdLast): 0; + + for (S32 i = 0; i < MaxSounds; i++) + sfxRead( stream, &sound[ i ] ); + + stream->read(&maxWheelSpeed); + stream->read(&engineTorque); + stream->read(&engineBrake); + stream->read(&brakeTorque); +} + + +//---------------------------------------------------------------------------- +// Wheeled Vehicle Class +//---------------------------------------------------------------------------- + +//---------------------------------------------------------------------------- + +IMPLEMENT_CO_NETOBJECT_V1(WheeledVehicle); + +ConsoleDocClass( WheeledVehicle, + "@brief A wheeled vehicle.\n" + "@ingroup Vehicles\n" +); + +WheeledVehicle::WheeledVehicle() +{ + mDataBlock = 0; + mBraking = false; + mJetSound = NULL; + mEngineSound = NULL; + mSquealSound = NULL; + mTailLightThread = 0; + mSteeringThread = 0; + + for (S32 i = 0; i < WheeledVehicleData::MaxWheels; i++) { + mWheel[i].springThread = 0; + mWheel[i].Dy = mWheel[i].Dx = 0; + mWheel[i].tire = 0; + mWheel[i].spring = 0; + mWheel[i].shapeInstance = 0; + mWheel[i].steering = 0; + mWheel[i].powered = true; + mWheel[i].slipping = false; + } +} + +WheeledVehicle::~WheeledVehicle() +{ +} + +void WheeledVehicle::initPersistFields() +{ + Parent::initPersistFields(); +} + + +//---------------------------------------------------------------------------- + +bool WheeledVehicle::onAdd() +{ + if(!Parent::onAdd()) + return false; + + addToScene(); + if (isServerObject()) + scriptOnAdd(); + return true; +} + +void WheeledVehicle::onRemove() +{ + // Delete the wheel resources + if (mDataBlock != NULL) { + Wheel* wend = &mWheel[mDataBlock->wheelCount]; + for (Wheel* wheel = mWheel; wheel < wend; wheel++) { + if (!wheel->emitter.isNull()) + wheel->emitter->deleteWhenEmpty(); + delete wheel->shapeInstance; + } + } + + // Stop the sounds + SFX_DELETE( mJetSound ); + SFX_DELETE( mEngineSound ); + SFX_DELETE( mSquealSound ); + + // + scriptOnRemove(); + removeFromScene(); + Parent::onRemove(); +} + + +//---------------------------------------------------------------------------- + +bool WheeledVehicle::onNewDataBlock(GameBaseData* dptr, bool reload) +{ + // Delete any existing wheel resources if we're switching + // datablocks. + if (mDataBlock) + { + Wheel* wend = &mWheel[mDataBlock->wheelCount]; + for (Wheel* wheel = mWheel; wheel < wend; wheel++) + { + if (!wheel->emitter.isNull()) + { + wheel->emitter->deleteWhenEmpty(); + wheel->emitter = 0; + } + delete wheel->shapeInstance; + wheel->shapeInstance = 0; + } + } + + // Load up the new datablock + mDataBlock = dynamic_cast(dptr); + if (!mDataBlock || !Parent::onNewDataBlock(dptr,reload)) + return false; + + // Set inertial tensor, default for the vehicle is sphere + if (mDataBlock->massBox.x > 0 && mDataBlock->massBox.y > 0 && mDataBlock->massBox.z > 0) + mRigid.setObjectInertia(mDataBlock->massBox); + else + mRigid.setObjectInertia(mObjBox.maxExtents - mObjBox.minExtents); + + // Initialize the wheels... + for (S32 i = 0; i < mDataBlock->wheelCount; i++) + { + Wheel* wheel = &mWheel[i]; + wheel->data = &mDataBlock->wheel[i]; + wheel->tire = 0; + wheel->spring = 0; + + wheel->surface.contact = false; + wheel->surface.object = NULL; + wheel->avel = 0; + wheel->apos = 0; + wheel->extension = 1; + wheel->slip = 0; + + wheel->springThread = 0; + wheel->emitter = 0; + + // Steering on the front tires by default + if (wheel->data->pos.y > 0) + wheel->steering = 1; + + // Build wheel animation threads + if (wheel->data->springSequence != -1) { + wheel->springThread = mShapeInstance->addThread(); + mShapeInstance->setSequence(wheel->springThread,wheel->data->springSequence,0); + } + + // Each wheel get's it's own particle emitter + if( mDataBlock->tireEmitter && isGhost() ) + { + wheel->emitter = new ParticleEmitter; + wheel->emitter->onNewDataBlock( mDataBlock->tireEmitter, false ); + wheel->emitter->registerObject(); + } + } + + // Steering sequence + if (mDataBlock->steeringSequence != -1) { + mSteeringThread = mShapeInstance->addThread(); + mShapeInstance->setSequence(mSteeringThread,mDataBlock->steeringSequence,0); + } + else + mSteeringThread = 0; + + // Brake light sequence + if (mDataBlock->brakeLightSequence != -1) { + mTailLightThread = mShapeInstance->addThread(); + mShapeInstance->setSequence(mTailLightThread,mDataBlock->brakeLightSequence,0); + } + else + mTailLightThread = 0; + + if (isGhost()) + { + // Create the sounds ahead of time. This reduces runtime + // costs and makes the system easier to understand. + + SFX_DELETE( mEngineSound ); + SFX_DELETE( mSquealSound ); + SFX_DELETE( mJetSound ); + + if ( mDataBlock->sound[WheeledVehicleData::EngineSound] ) + mEngineSound = SFX->createSource( mDataBlock->sound[WheeledVehicleData::EngineSound], &getTransform() ); + + if ( mDataBlock->sound[WheeledVehicleData::SquealSound] ) + mSquealSound = SFX->createSource( mDataBlock->sound[WheeledVehicleData::SquealSound], &getTransform() ); + + if ( mDataBlock->sound[WheeledVehicleData::JetSound] ) + mJetSound = SFX->createSource( mDataBlock->sound[WheeledVehicleData::JetSound], &getTransform() ); + } + + scriptOnNewDataBlock(); + return true; +} + + +//---------------------------------------------------------------------------- + +S32 WheeledVehicle::getWheelCount() +{ + // Return # of hubs defined on the car body + return mDataBlock? mDataBlock->wheelCount: 0; +} + +void WheeledVehicle::setWheelSteering(S32 wheel,F32 steering) +{ + AssertFatal(wheel >= 0 && wheel < WheeledVehicleData::MaxWheels,"Wheel index out of bounds"); + mWheel[wheel].steering = mClampF(steering,-1,1); + setMaskBits(WheelMask); +} + +void WheeledVehicle::setWheelPowered(S32 wheel,bool powered) +{ + AssertFatal(wheel >= 0 && wheel < WheeledVehicleData::MaxWheels,"Wheel index out of bounds"); + mWheel[wheel].powered = powered; + setMaskBits(WheelMask); +} + +void WheeledVehicle::setWheelTire(S32 wheel,WheeledVehicleTire* tire) +{ + AssertFatal(wheel >= 0 && wheel < WheeledVehicleData::MaxWheels,"Wheel index out of bounds"); + mWheel[wheel].tire = tire; + setMaskBits(WheelMask); +} + +void WheeledVehicle::setWheelSpring(S32 wheel,WheeledVehicleSpring* spring) +{ + AssertFatal(wheel >= 0 && wheel < WheeledVehicleData::MaxWheels,"Wheel index out of bounds"); + mWheel[wheel].spring = spring; + setMaskBits(WheelMask); +} + +void WheeledVehicle::getWheelInstAndTransform( U32 index, TSShapeInstance** inst, MatrixF* xfrm ) const +{ + AssertFatal( index < WheeledVehicleData::MaxWheels, + "WheeledVehicle::getWheelInstAndTransform() - Bad wheel index!" ); + + const Wheel* wheel = &mWheel[index]; + *inst = wheel->shapeInstance; + + if ( !xfrm || !wheel->shapeInstance ) + return; + + MatrixF world = getRenderTransform(); + world.scale( mObjScale ); + + // Steering & spring extension + MatrixF hub(EulerF(0,0,mSteering.x * wheel->steering)); + Point3F pos = wheel->data->pos; + pos.z -= wheel->spring->length * wheel->extension; + hub.setColumn(3,pos); + world.mul(hub); + + // Wheel rotation + MatrixF rot(EulerF(wheel->apos * M_2PI,0,0)); + world.mul(rot); + + // Rotation the tire to face the right direction + // (could pre-calculate this) + MatrixF wrot(EulerF(0,0,(wheel->data->pos.x > 0)? M_PI/2: -M_PI/2)); + world.mul(wrot); + + *xfrm = world; +} + +//---------------------------------------------------------------------------- + +void WheeledVehicle::processTick(const Move* move) +{ + Parent::processTick(move); +} + +void WheeledVehicle::updateMove(const Move* move) +{ + Parent::updateMove(move); + + // Brake on trigger + mBraking = move->trigger[2]; + + // Set the tail brake light thread direction based on the brake state. + if (mTailLightThread) + mShapeInstance->setTimeScale(mTailLightThread, mBraking? 1.0f : -1.0f); +} + + +//---------------------------------------------------------------------------- + +void WheeledVehicle::advanceTime(F32 dt) +{ + PROFILE_SCOPE( WheeledVehicle_AdvanceTime ); + + Parent::advanceTime(dt); + + // Stick the wheels to the ground. This is purely so they look + // good while the vehicle is being interpolated. + extendWheels(); + + // Update wheel angular position and slip, this is a client visual + // feature only, it has no affect on the physics. + F32 slipTotal = 0; + F32 torqueTotal = 0; + + Wheel* wend = &mWheel[mDataBlock->wheelCount]; + for (Wheel* wheel = mWheel; wheel < wend; wheel++) + if (wheel->tire && wheel->spring) { + // Update angular position + wheel->apos += (wheel->avel * dt) / M_2PI; + wheel->apos -= mFloor(wheel->apos); + if (wheel->apos < 0) + wheel->apos = 1 - wheel->apos; + + // Keep track of largest slip + slipTotal += wheel->slip; + torqueTotal += wheel->torqueScale; + } + + // Update the sounds based on wheel slip and torque output + updateSquealSound(slipTotal / mDataBlock->wheelCount); + updateEngineSound(sIdleEngineVolume + (1 - sIdleEngineVolume) * + (1 - (torqueTotal / mDataBlock->wheelCount))); + updateJetSound(); + + updateWheelThreads(); + updateWheelParticles(dt); + + // Update the steering animation: sequence time 0 is full right, + // and time 0.5 is straight ahead. + if (mSteeringThread) { + F32 t = (mSteering.x * mFabs(mSteering.x)) / mDataBlock->maxSteeringAngle; + mShapeInstance->setPos(mSteeringThread,0.5 - t * 0.5); + } + + // Animate the tail light. The direction of the thread is + // set based on vehicle braking. + if (mTailLightThread) + mShapeInstance->advanceTime(dt,mTailLightThread); +} + + +//---------------------------------------------------------------------------- +/** Update the rigid body forces on the vehicle + This method calculates the forces acting on the body, including gravity, + suspension & tire forces. +*/ +void WheeledVehicle::updateForces(F32 dt) +{ + PROFILE_SCOPE( WheeledVehicle_UpdateForces ); + + extendWheels(); + + F32 aMomentum = mMass / mDataBlock->wheelCount; + + // Get the current matrix and extact vectors + MatrixF currMatrix; + mRigid.getTransform(&currMatrix); + + Point3F bx,by,bz; + currMatrix.getColumn(0,&bx); + currMatrix.getColumn(1,&by); + currMatrix.getColumn(2,&bz); + + // Steering angles from current steering wheel position + F32 quadraticSteering = -(mSteering.x * mFabs(mSteering.x)); + F32 cosSteering,sinSteering; + mSinCos(quadraticSteering, sinSteering, cosSteering); + + // Calculate Engine and brake torque values used later by in + // wheel calculations. + F32 engineTorque,brakeVel; + if (mBraking) + { + brakeVel = (mDataBlock->brakeTorque / aMomentum) * dt; + engineTorque = 0; + } + else + { + if (mThrottle) + { + engineTorque = mDataBlock->engineTorque * mThrottle; + brakeVel = 0; + // Double the engineTorque to help out the jets + if (mThrottle > 0 && mJetting) + engineTorque *= 2; + } + else + { + // Engine brake. + brakeVel = (mDataBlock->engineBrake / aMomentum) * dt; + engineTorque = 0; + } + } + + // Integrate forces, we'll do this ourselves here instead of + // relying on the rigid class which does it during movement. + Wheel* wend = &mWheel[mDataBlock->wheelCount]; + mRigid.force.set(0, 0, 0); + mRigid.torque.set(0, 0, 0); + + // Calculate vertical load for friction. Divide up the spring + // forces across all the wheels that are in contact with + // the ground. + U32 contactCount = 0; + F32 verticalLoad = 0; + for (Wheel* wheel = mWheel; wheel < wend; wheel++) + { + if (wheel->tire && wheel->spring && wheel->surface.contact) + { + verticalLoad += wheel->spring->force * (1 - wheel->extension); + contactCount++; + } + } + if (contactCount) + verticalLoad /= contactCount; + + // Sum up spring and wheel torque forces + for (Wheel* wheel = mWheel; wheel < wend; wheel++) + { + if (!wheel->tire || !wheel->spring) + continue; + + F32 Fy = 0; + if (wheel->surface.contact) + { + + // First, let's compute the wheel's position, and worldspace velocity + Point3F pos, r, localVel; + currMatrix.mulP(wheel->data->pos, &pos); + mRigid.getOriginVector(pos,&r); + mRigid.getVelocity(r, &localVel); + + // Spring force & damping + F32 spring = wheel->spring->force * (1 - wheel->extension); + + if (wheel->extension == 0) //spring fully compressed + { + // Apply impulses to the rigid body to keep it from + // penetrating the surface. + F32 n = -mDot(localVel,Point3F(0,0,1)); + if (n >= 0) + { + // Collision impulse, straight forward force stuff. + F32 d = mRigid.getZeroImpulse(r,Point3F(0,0,1)); + F32 j = n * (1 + mRigid.restitution) * d; + mRigid.force += Point3F(0,0,1) * j; + } + } + + F32 damping = wheel->spring->damping * -(mDot(bz, localVel) / wheel->spring->length); + if (damping < 0) + damping = 0; + + // Anti-sway force based on difference in suspension extension + F32 antiSway = 0; + if (wheel->data->opposite != -1) + { + Wheel* oppositeWheel = &mWheel[wheel->data->opposite]; + if (oppositeWheel->surface.contact) + antiSway = ((oppositeWheel->extension - wheel->extension) * + wheel->spring->antiSway); + if (antiSway < 0) + antiSway = 0; + } + + // Spring forces act straight up and are applied at the + // spring's root position. + Point3F t, forceVector = bz * (spring + damping + antiSway); + mCross(r, forceVector, &t); + mRigid.torque += t; + mRigid.force += forceVector; + + // Tire direction vectors perpendicular to surface normal + Point3F wheelXVec = bx * cosSteering; + wheelXVec += by * sinSteering * wheel->steering; + Point3F tireX, tireY; + mCross(wheel->surface.normal, wheelXVec, &tireY); + tireY.normalize(); + mCross(tireY, wheel->surface.normal, &tireX); + tireX.normalize(); + + // Velocity of tire at the surface contact + Point3F wheelContact, wheelVelocity; + mRigid.getOriginVector(wheel->surface.pos,&wheelContact); + mRigid.getVelocity(wheelContact, &wheelVelocity); + + F32 xVelocity = mDot(tireX, wheelVelocity); + F32 yVelocity = mDot(tireY, wheelVelocity); + + // Tires act as springs and generate lateral and longitudinal + // forces to move the vehicle. These distortion/spring forces + // are what convert wheel angular velocity into forces that + // act on the rigid body. + + // Longitudinal tire deformation force + F32 ddy = (wheel->avel * wheel->tire->radius - yVelocity) - + wheel->tire->longitudinalRelaxation * + mFabs(wheel->avel) * wheel->Dy; + wheel->Dy += ddy * dt; + Fy = (wheel->tire->longitudinalForce * wheel->Dy + + wheel->tire->longitudinalDamping * ddy); + + // Lateral tire deformation force + F32 ddx = xVelocity - wheel->tire->lateralRelaxation * + mFabs(wheel->avel) * wheel->Dx; + wheel->Dx += ddx * dt; + F32 Fx = -(wheel->tire->lateralForce * wheel->Dx + + wheel->tire->lateralDamping * ddx); + + // Vertical load on the tire + verticalLoad = spring + damping + antiSway; + if (verticalLoad < 0) + verticalLoad = 0; + + // Adjust tire forces based on friction + F32 surfaceFriction = 1; + F32 mu = surfaceFriction * (wheel->slipping ? wheel->tire->kineticFriction : wheel->tire->staticFriction); + F32 Fn = verticalLoad * mu; Fn *= Fn; + F32 Fw = Fx * Fx + Fy * Fy; + if (Fw > Fn) + { + F32 K = mSqrt(Fn / Fw); + Fy *= K; + Fx *= K; + wheel->Dy *= K; + wheel->Dx *= K; + wheel->slip = 1 - K; + wheel->slipping = true; + } + else + { + wheel->slipping = false; + wheel->slip = 0; + } + + // Tire forces act through the tire direction vectors parallel + // to the surface and are applied at the wheel hub. + forceVector = (tireX * Fx) + (tireY * Fy); + pos -= bz * (wheel->spring->length * wheel->extension); + mRigid.getOriginVector(pos,&r); + mCross(r, forceVector, &t); + mRigid.torque += t; + mRigid.force += forceVector; + } + else + { + // Wheel not in contact with the ground + wheel->torqueScale = 0; + wheel->slip = 0; + + // Relax the tire deformation + wheel->Dy += (-wheel->tire->longitudinalRelaxation * + mFabs(wheel->avel) * wheel->Dy) * dt; + wheel->Dx += (-wheel->tire->lateralRelaxation * + mFabs(wheel->avel) * wheel->Dx) * dt; + } + + // Adjust the wheel's angular velocity based on engine torque + // and tire deformation forces. + if (wheel->powered) + { + F32 maxAvel = mDataBlock->maxWheelSpeed / wheel->tire->radius; + wheel->torqueScale = (mFabs(wheel->avel) > maxAvel) ? 0 : + 1 - (mFabs(wheel->avel) / maxAvel); + } + else + wheel->torqueScale = 0; + wheel->avel += (((wheel->torqueScale * engineTorque) - Fy * + wheel->tire->radius) / aMomentum) * dt; + + // Adjust the wheel's angular velocity based on brake torque. + // This is done after avel update to make sure we come to a + // complete stop. + if (brakeVel > mFabs(wheel->avel)) + wheel->avel = 0; + else + if (wheel->avel > 0) + wheel->avel -= brakeVel; + else + wheel->avel += brakeVel; + } + + // Jet Force + if (mJetting) + mRigid.force += by * mDataBlock->jetForce; + + // Container drag & buoyancy + mRigid.force += Point3F(0, 0, -mBuoyancy * sWheeledVehicleGravity * mRigid.mass); + mRigid.force -= mRigid.linVelocity * mDrag; + mRigid.torque -= mRigid.angMomentum * mDrag; + + // If we've added anything other than gravity, then we're no + // longer at rest. Could test this a little more efficiently... + if (mRigid.atRest && (mRigid.force.len() || mRigid.torque.len())) + mRigid.atRest = false; + + // Gravity + mRigid.force += Point3F(0, 0, sWheeledVehicleGravity * mRigid.mass); + + // Integrate and update velocity + mRigid.linMomentum += mRigid.force * dt; + mRigid.angMomentum += mRigid.torque * dt; + mRigid.updateVelocity(); + + // Since we've already done all the work, just need to clear this out. + mRigid.force.set(0, 0, 0); + mRigid.torque.set(0, 0, 0); + + // If we're still atRest, make sure we're not accumulating anything + if (mRigid.atRest) + mRigid.setAtRest(); +} + + +//---------------------------------------------------------------------------- +/** Extend the wheels + The wheels are extended until they contact a surface. The extension + is instantaneous. The wheels are extended before force calculations and + also on during client side interpolation (so that the wheels are glued + to the ground). +*/ +void WheeledVehicle::extendWheels(bool clientHack) +{ + PROFILE_SCOPE( WheeledVehicle_ExtendWheels ); + + disableCollision(); + + MatrixF currMatrix; + + if(clientHack) + currMatrix = getRenderTransform(); + else + mRigid.getTransform(&currMatrix); + + + // Does a single ray cast down for now... this will have to be + // changed to something a little more complicated to avoid getting + // stuck in cracks. + Wheel* wend = &mWheel[mDataBlock->wheelCount]; + for (Wheel* wheel = mWheel; wheel < wend; wheel++) + { + if (wheel->tire && wheel->spring) + { + wheel->extension = 1; + + // The ray is cast from the spring mount point to the tip of + // the tire. If there is a collision the spring extension is + // adjust to remove the tire radius. + Point3F sp,vec; + currMatrix.mulP(wheel->data->pos,&sp); + currMatrix.mulV(VectorF(0,0,-wheel->spring->length),&vec); + F32 ts = wheel->tire->radius / wheel->spring->length; + Point3F ep = sp + (vec * (1 + ts)); + ts = ts / (1+ts); + + RayInfo rInfo; + if (mContainer->castRay(sp, ep, sClientCollisionMask & ~PlayerObjectType, &rInfo)) + { + wheel->surface.contact = true; + wheel->extension = (rInfo.t < ts)? 0: (rInfo.t - ts) / (1 - ts); + wheel->surface.normal = rInfo.normal; + wheel->surface.pos = rInfo.point; + wheel->surface.material = rInfo.material; + wheel->surface.object = rInfo.object; + } + else + { + wheel->surface.contact = false; + wheel->slipping = true; + } + } + } + enableCollision(); +} + + +//---------------------------------------------------------------------------- +/** Update wheel steering and suspension threads. + These animations are purely cosmetic and this method is only invoked + on the client. +*/ +void WheeledVehicle::updateWheelThreads() +{ + Wheel* wend = &mWheel[mDataBlock->wheelCount]; + for (Wheel* wheel = mWheel; wheel < wend; wheel++) + { + if (wheel->tire && wheel->spring && wheel->springThread) + { + // Scale the spring animation time to match the current + // position of the wheel. We'll also check to make sure + // the animation is long enough, if it isn't, just stick + // it at the end. + F32 pos = wheel->extension * wheel->spring->length; + if (pos > wheel->data->springLength) + pos = 1; + else + pos /= wheel->data->springLength; + mShapeInstance->setPos(wheel->springThread,pos); + } + } +} + +//---------------------------------------------------------------------------- +/** Update wheel particles effects + These animations are purely cosmetic and this method is only invoked + on the client. Particles are emitted as long as the moving. +*/ +void WheeledVehicle::updateWheelParticles(F32 dt) +{ + // OMG l33t hax + extendWheels(true); + + Point3F vel = Parent::getVelocity(); + F32 speed = vel.len(); + + // Don't bother if we're not moving. + if (speed > 1.0f) + { + Point3F axis = vel; + axis.normalize(); + + Wheel* wend = &mWheel[mDataBlock->wheelCount]; + for (Wheel* wheel = mWheel; wheel < wend; wheel++) + { + // Is this wheel in contact with the ground? + if (wheel->tire && wheel->spring && !wheel->emitter.isNull() && + wheel->surface.contact && wheel->surface.object ) + { + Material* material = ( wheel->surface.material ? dynamic_cast< Material* >( wheel->surface.material->getMaterial() ) : 0 ); + + if( material)//&& material->mShowDust ) + { + ColorF colorList[ ParticleData::PDC_NUM_KEYS ]; + + for( U32 x = 0; x < getMin( Material::NUM_EFFECT_COLOR_STAGES, ParticleData::PDC_NUM_KEYS ); ++ x ) + colorList[ x ] = material->mEffectColor[ x ]; + for( U32 x = Material::NUM_EFFECT_COLOR_STAGES; x < ParticleData::PDC_NUM_KEYS; ++ x ) + colorList[ x ].set( 1.0, 1.0, 1.0, 0.0 ); + + wheel->emitter->setColors( colorList ); + + // Emit the dust, the density (time) is scaled by the + // the vehicles velocity. + wheel->emitter->emitParticles( wheel->surface.pos, true, + axis, vel, (U32)(3/*dt * (speed / mDataBlock->maxWheelSpeed) * 1000 * wheel->slip*/)); + } + } + } + } +} + + +//---------------------------------------------------------------------------- +/** Update engine sound + This method is only invoked by clients. +*/ +void WheeledVehicle::updateEngineSound(F32 level) +{ + if ( !mEngineSound ) + return; + + if ( !mEngineSound->isPlaying() ) + mEngineSound->play(); + + mEngineSound->setTransform( getTransform() ); + mEngineSound->setVelocity( getVelocity() ); + //mEngineSound->setVolume( level ); + + // Adjust pitch + F32 pitch = ((level-sIdleEngineVolume) * 1.3f); + if (pitch < 0.4f) + pitch = 0.4f; + + mEngineSound->setPitch( pitch ); +} + + +//---------------------------------------------------------------------------- +/** Update wheel skid sound + This method is only invoked by clients. +*/ +void WheeledVehicle::updateSquealSound(F32 level) +{ + if ( !mSquealSound ) + return; + + if ( level < sMinSquealVolume ) + { + mSquealSound->stop(); + return; + } + + if ( !mSquealSound->isPlaying() ) + mSquealSound->play(); + + mSquealSound->setTransform( getTransform() ); + mSquealSound->setVolume( level ); +} + + +//---------------------------------------------------------------------------- +/** Update jet sound + This method is only invoked by clients. +*/ +void WheeledVehicle::updateJetSound() +{ + if ( !mJetSound ) + return; + + if ( !mJetting ) + { + mJetSound->stop(); + return; + } + + if ( !mJetSound->isPlaying() ) + mJetSound->play(); + + mJetSound->setTransform( getTransform() ); +} + + +//---------------------------------------------------------------------------- + +U32 WheeledVehicle::getCollisionMask() +{ + return sClientCollisionMask; +} + + +//---------------------------------------------------------------------------- +/** Build a collision polylist + The polylist is filled with polygons representing the collision volume + and the wheels. +*/ +bool WheeledVehicle::buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F& box, const SphereF& sphere) +{ + PROFILE_SCOPE( WheeledVehicle_BuildPolyList ); + + // Parent will take care of body collision. + Parent::buildPolyList(context, polyList,box,sphere); + + // Add wheels as boxes. + Wheel* wend = &mWheel[mDataBlock->wheelCount]; + for (Wheel* wheel = mWheel; wheel < wend; wheel++) { + if (wheel->tire && wheel->spring) { + Box3F wbox; + F32 radius = wheel->tire->radius; + wbox.minExtents.x = -(wbox.maxExtents.x = radius / 2); + wbox.minExtents.y = -(wbox.maxExtents.y = radius); + wbox.minExtents.z = -(wbox.maxExtents.z = radius); + MatrixF mat = mObjToWorld; + + Point3F sp,vec; + mObjToWorld.mulP(wheel->data->pos,&sp); + mObjToWorld.mulV(VectorF(0,0,-wheel->spring->length),&vec); + Point3F ep = sp + (vec * wheel->extension); + mat.setColumn(3,ep); + polyList->setTransform(&mat,Point3F(1,1,1)); + polyList->addBox(wbox); + } + } + return !polyList->isEmpty(); +} + +void WheeledVehicle::prepBatchRender(SceneRenderState* state, S32 mountedImageIndex ) +{ + Parent::prepBatchRender( state, mountedImageIndex ); + + if ( mountedImageIndex != -1 ) + return; + + // Set up our render state *here*, + // before the push world matrix, so + // that wheel rendering will be correct. + TSRenderState rdata; + rdata.setSceneState( state ); + + // We might have some forward lit materials + // so pass down a query to gather lights. + LightQuery query; + query.init( getWorldSphere() ); + rdata.setLightQuery( &query ); + + // Shape transform + GFX->pushWorldMatrix(); + + MatrixF mat = getRenderTransform(); + mat.scale( mObjScale ); + GFX->setWorldMatrix( mat ); + + Wheel* wend = &mWheel[mDataBlock->wheelCount]; + for (Wheel* wheel = mWheel; wheel < wend; wheel++) + { + if (wheel->shapeInstance) + { + GFX->pushWorldMatrix(); + + // Steering & spring extension + MatrixF hub(EulerF(0,0,mSteering.x * wheel->steering)); + Point3F pos = wheel->data->pos; + pos.z -= wheel->spring->length * wheel->extension; + hub.setColumn(3,pos); + + GFX->multWorld(hub); + + // Wheel rotation + MatrixF rot(EulerF(wheel->apos * M_2PI,0,0)); + GFX->multWorld(rot); + + // Rotation the tire to face the right direction + // (could pre-calculate this) + MatrixF wrot(EulerF(0,0,(wheel->data->pos.x > 0)? M_PI/2: -M_PI/2)); + GFX->multWorld(wrot); + + // Render! + wheel->shapeInstance->animate(); + wheel->shapeInstance->render( rdata ); + + if (mCloakLevel != 0.0f) + wheel->shapeInstance->setAlphaAlways(1.0f - mCloakLevel); + else + wheel->shapeInstance->setAlphaAlways(1.0f); + + GFX->popWorldMatrix(); + } + } + + GFX->popWorldMatrix(); + +} + +//---------------------------------------------------------------------------- + +void WheeledVehicle::writePacketData(GameConnection *connection, BitStream *stream) +{ + Parent::writePacketData(connection, stream); + stream->writeFlag(mBraking); + + Wheel* wend = &mWheel[mDataBlock->wheelCount]; + for (Wheel* wheel = mWheel; wheel < wend; wheel++) + { + stream->write(wheel->avel); + stream->write(wheel->Dy); + stream->write(wheel->Dx); + stream->writeFlag(wheel->slipping); + } +} + +void WheeledVehicle::readPacketData(GameConnection *connection, BitStream *stream) +{ + Parent::readPacketData(connection, stream); + mBraking = stream->readFlag(); + + Wheel* wend = &mWheel[mDataBlock->wheelCount]; + for (Wheel* wheel = mWheel; wheel < wend; wheel++) + { + stream->read(&wheel->avel); + stream->read(&wheel->Dy); + stream->read(&wheel->Dx); + wheel->slipping = stream->readFlag(); + } + + // Rigid state is transmitted by the parent... + setPosition(mRigid.linPosition,mRigid.angPosition); + mDelta.pos = mRigid.linPosition; + mDelta.rot[1] = mRigid.angPosition; +} + + +//---------------------------------------------------------------------------- + +U32 WheeledVehicle::packUpdate(NetConnection *con, U32 mask, BitStream *stream) +{ + U32 retMask = Parent::packUpdate(con, mask, stream); + + // Update wheel datablock information + if (stream->writeFlag(mask & WheelMask)) + { + Wheel* wend = &mWheel[mDataBlock->wheelCount]; + for (Wheel* wheel = mWheel; wheel < wend; wheel++) + { + if (stream->writeFlag(wheel->tire && wheel->spring)) + { + stream->writeRangedU32(wheel->tire->getId(), + DataBlockObjectIdFirst,DataBlockObjectIdLast); + stream->writeRangedU32(wheel->spring->getId(), + DataBlockObjectIdFirst,DataBlockObjectIdLast); + stream->writeFlag(wheel->powered); + + // Steering must be sent with full precision as it's + // used directly in state force calculations. + stream->write(wheel->steering); + } + } + } + + // The rest of the data is part of the control object packet update. + // If we're controlled by this client, we don't need to send it. + if (stream->writeFlag(getControllingClient() == con && !(mask & InitialUpdateMask))) + return retMask; + + stream->writeFlag(mBraking); + + if (stream->writeFlag(mask & PositionMask)) + { + Wheel* wend = &mWheel[mDataBlock->wheelCount]; + for (Wheel* wheel = mWheel; wheel < wend; wheel++) + { + stream->write(wheel->avel); + stream->write(wheel->Dy); + stream->write(wheel->Dx); + } + } + return retMask; +} + +void WheeledVehicle::unpackUpdate(NetConnection *con, BitStream *stream) +{ + Parent::unpackUpdate(con,stream); + + // Update wheel datablock information + if (stream->readFlag()) + { + Wheel* wend = &mWheel[mDataBlock->wheelCount]; + for (Wheel* wheel = mWheel; wheel < wend; wheel++) + { + if (stream->readFlag()) + { + SimObjectId tid = stream->readRangedU32(DataBlockObjectIdFirst,DataBlockObjectIdLast); + SimObjectId sid = stream->readRangedU32(DataBlockObjectIdFirst,DataBlockObjectIdLast); + if (!Sim::findObject(tid,wheel->tire) || !Sim::findObject(sid,wheel->spring)) + { + con->setLastError("Invalid packet WheeledVehicle::unpackUpdate()"); + return; + } + wheel->powered = stream->readFlag(); + stream->read(&wheel->steering); + + // Create an instance of the tire for rendering + delete wheel->shapeInstance; + wheel->shapeInstance = (wheel->tire->shape == NULL) ? 0: + new TSShapeInstance(wheel->tire->shape); + } + } + } + + // After this is data that we only need if we're not the + // controlling client. + if (stream->readFlag()) + return; + + mBraking = stream->readFlag(); + + if (stream->readFlag()) + { + Wheel* wend = &mWheel[mDataBlock->wheelCount]; + for (Wheel* wheel = mWheel; wheel < wend; wheel++) + { + stream->read(&wheel->avel); + stream->read(&wheel->Dy); + stream->read(&wheel->Dx); + } + } +} + + +//---------------------------------------------------------------------------- +// Console Methods +//---------------------------------------------------------------------------- + +//---------------------------------------------------------------------------- + +DefineEngineMethod( WheeledVehicle, setWheelSteering, bool, ( S32 wheel, F32 steering ),, + "@brief Set how much the wheel is affected by steering.\n\n" + "The steering factor controls how much the wheel is rotated by the vehicle " + "steering. For example, most cars would have their front wheels set to 1.0, " + "and their rear wheels set to 0 since only the front wheels should turn.\n\n" + "Negative values will turn the wheel in the opposite direction to the steering " + "angle.\n" + "@param wheel index of the wheel to set (hub node #)\n" + "@param steering steering factor from -1 (full inverse) to 1 (full)\n" + "@return true if successful, false if failed\n\n" ) +{ + if ( wheel >= 0 && wheel < object->getWheelCount() ) { + object->setWheelSteering( wheel, steering ); + return true; + } + else + Con::warnf("setWheelSteering: wheel index %d out of bounds, vehicle has %d hubs", + wheel, object->getWheelCount()); + return false; +} + +DefineEngineMethod( WheeledVehicle, setWheelPowered, bool, ( S32 wheel, bool powered ),, + "@brief Set whether the wheel is powered (has torque applied from the engine).\n\n" + "A rear wheel drive car for example would set the front wheels to false, " + "and the rear wheels to true.\n" + "@param wheel index of the wheel to set (hub node #)\n" + "@param powered flag indicating whether to power the wheel or not\n" + "@return true if successful, false if failed\n\n" ) +{ + if ( wheel >= 0 && wheel < object->getWheelCount() ) { + object->setWheelPowered( wheel, powered ); + return true; + } + else + Con::warnf("setWheelPowered: wheel index %d out of bounds, vehicle has %d hubs", + wheel, object->getWheelCount()); + return false; +} + +DefineEngineMethod( WheeledVehicle, setWheelTire, bool, ( S32 wheel, WheeledVehicleTire* tire ),, + "@brief Set the WheeledVehicleTire datablock for this wheel.\n" + "@param wheel index of the wheel to set (hub node #)\n" + "@param tire WheeledVehicleTire datablock\n" + "@return true if successful, false if failed\n\n" + "@tsexample\n" + "%obj.setWheelTire( 0, FrontTire );\n" + "@endtsexample\n" ) +{ + if (wheel >= 0 && wheel < object->getWheelCount()) { + object->setWheelTire(wheel,tire); + return true; + } + else { + Con::warnf("setWheelTire: invalid tire datablock or wheel index, vehicle has %d hubs", + object->getWheelCount()); + return false; + } +} + +DefineEngineMethod( WheeledVehicle, setWheelSpring, bool, ( S32 wheel, WheeledVehicleSpring* spring ),, + "@brief Set the WheeledVehicleSpring datablock for this wheel.\n" + "@param wheel index of the wheel to set (hub node #)\n" + "@param spring WheeledVehicleSpring datablock\n" + "@return true if successful, false if failed\n\n" + "@tsexample\n" + "%obj.setWheelSpring( 0, FrontSpring );\n" + "@endtsexample\n" ) +{ + if (spring && wheel >= 0 && wheel < object->getWheelCount()) { + object->setWheelSpring(wheel,spring); + return true; + } + else { + Con::warnf("setWheelSpring: invalid spring datablock or wheel index, vehicle has %d hubs", + object->getWheelCount()); + return false; + } +} + +DefineEngineMethod( WheeledVehicle, getWheelCount, S32, (),, + "@brief Get the number of wheels on this vehicle.\n" + "@return the number of wheels (equal to the number of hub nodes defined in the model)\n\n" ) +{ + return object->getWheelCount(); +} diff --git a/Engine/source/T3D/vehicles/wheeledVehicle.h b/Engine/source/T3D/vehicles/wheeledVehicle.h new file mode 100644 index 000000000..8efc03104 --- /dev/null +++ b/Engine/source/T3D/vehicles/wheeledVehicle.h @@ -0,0 +1,250 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _WHEELEDVEHICLE_H_ +#define _WHEELEDVEHICLE_H_ + +#ifndef _VEHICLE_H_ +#include "T3D/vehicles/vehicle.h" +#endif + +#ifndef _CLIPPEDPOLYLIST_H_ +#include "collision/clippedPolyList.h" +#endif + +class ParticleEmitter; +class ParticleEmitterData; + + +//---------------------------------------------------------------------------- + +struct WheeledVehicleTire: public SimDataBlock +{ + typedef SimDataBlock Parent; + + // + StringTableEntry shapeName;// Max shape to render + + // Physical properties + F32 mass; // Mass of the whole wheel + F32 kineticFriction; // Tire friction coefficient + F32 staticFriction; // Tire friction coefficient + F32 restitution; // Currently not used + + // Tires act as springs and generate lateral and longitudinal + // forces to move the vehicle. These distortion/spring forces + // are what convert wheel angular velocity into forces that + // act on the rigid body. + F32 lateralForce; // Spring force + F32 lateralDamping; // Damping force + F32 lateralRelaxation; // The tire will relax if left alone + F32 longitudinalForce; + F32 longitudinalDamping; + F32 longitudinalRelaxation; + + // Shape information initialized in the preload + Resource shape; // The loaded shape + F32 radius; // Tire radius + + // + WheeledVehicleTire(); + DECLARE_CONOBJECT(WheeledVehicleTire); + static void initPersistFields(); + bool preload(bool, String &errorStr); + virtual void packData(BitStream* stream); + virtual void unpackData(BitStream* stream); +}; + + +//---------------------------------------------------------------------------- + +struct WheeledVehicleSpring: public SimDataBlock +{ + typedef SimDataBlock Parent; + + F32 length; // Travel distance from root hub position + F32 force; // Spring force + F32 damping; // Damping force + F32 antiSway; // Opposite wheel anti-sway + + // + WheeledVehicleSpring(); + DECLARE_CONOBJECT(WheeledVehicleSpring); + static void initPersistFields(); + virtual void packData(BitStream* stream); + virtual void unpackData(BitStream* stream); +}; + + +//---------------------------------------------------------------------------- + +struct WheeledVehicleData: public VehicleData +{ + typedef VehicleData Parent; + + enum Constants + { + MaxWheels = 8, + MaxWheelBits = 3 + }; + + enum Sounds + { + JetSound, + EngineSound, + SquealSound, + WheelImpactSound, + MaxSounds, + }; + SFXTrack* sound[MaxSounds]; + + ParticleEmitterData* tireEmitter; + + F32 maxWheelSpeed; // Engine torque is scale based on wheel speed + F32 engineTorque; // Engine force controlled through throttle + F32 engineBrake; // Break force applied when throttle is 0 + F32 brakeTorque; // Force used when brakeing + + // Initialized onAdd + struct Wheel + { + S32 opposite; // Opposite wheel on Y axis (or -1 for none) + Point3F pos; // Root pos of spring + S32 springNode; // Wheel spring/hub node + S32 springSequence; // Suspension animation + F32 springLength; // Suspension animation length + } wheel[MaxWheels]; + U32 wheelCount; + ClippedPolyList rigidBody; // Extracted from shape + S32 brakeLightSequence; // Brakes + S32 steeringSequence; // Steering animation + + // + WheeledVehicleData(); + DECLARE_CONOBJECT(WheeledVehicleData); + static void initPersistFields(); + bool preload(bool, String &errorStr); + bool mirrorWheel(Wheel* we); + virtual void packData(BitStream* stream); + virtual void unpackData(BitStream* stream); +}; + + +//---------------------------------------------------------------------------- + +class WheeledVehicle: public Vehicle +{ + typedef Vehicle Parent; + + enum MaskBits + { + WheelMask = Parent::NextFreeMask << 0, + NextFreeMask = Parent::NextFreeMask << 1 + }; + + WheeledVehicleData* mDataBlock; + + bool mBraking; + TSThread* mTailLightThread; + SFXSource* mJetSound; + SFXSource* mEngineSound; + SFXSource* mSquealSound; + + struct Wheel + { + WheeledVehicleTire *tire; + WheeledVehicleSpring *spring; + WheeledVehicleData::Wheel* data; + + F32 extension; // Spring extension (0-1) + F32 avel; // Angular velocity + F32 apos; // Anuglar position (client side only) + F32 Dy,Dx; // Current tire deformation + + struct Surface + { + bool contact; // Wheel is touching a surface + Point3F normal; // Surface normal + BaseMatInstance* material; // Surface material + Point3F pos; // Point of contact + SceneObject* object; // Object in contact with + } surface; + + TSShapeInstance* shapeInstance; + TSThread* springThread; + + F32 steering; // Wheel steering scale + bool powered; // Powered by engine + bool slipping; // Traction on last tick + F32 torqueScale; // Max torque % applied to wheel (0-1) + F32 slip; // Amount of wheel slip (0-1) + SimObjectPtr emitter; + }; + Wheel mWheel[WheeledVehicleData::MaxWheels]; + TSThread* mSteeringThread; + + // + bool onNewDataBlock( GameBaseData *dptr, bool reload ); + void processTick(const Move *move); + void updateMove(const Move *move); + void updateForces(F32 dt); + void extendWheels(bool clientHack = false); + void prepBatchRender( SceneRenderState *state, S32 mountedImageIndex ); + + // Client sounds & particles + void updateWheelThreads(); + void updateWheelParticles(F32 dt); + void updateEngineSound(F32 level); + void updateSquealSound(F32 level); + void updateJetSound(); + + virtual U32 getCollisionMask(); + +public: + DECLARE_CONOBJECT(WheeledVehicle); + static void initPersistFields(); + + WheeledVehicle(); + ~WheeledVehicle(); + + bool onAdd(); + void onRemove(); + void advanceTime(F32 dt); + bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere); + + S32 getWheelCount(); + Wheel *getWheel(U32 index) {return &mWheel[index];} + void setWheelSteering(S32 wheel,F32 steering); + void setWheelPowered(S32 wheel,bool powered); + void setWheelTire(S32 wheel,WheeledVehicleTire*); + void setWheelSpring(S32 wheel,WheeledVehicleSpring*); + + void getWheelInstAndTransform( U32 wheel, TSShapeInstance** inst, MatrixF* xfrm ) const; + + void writePacketData(GameConnection * conn, BitStream *stream); + void readPacketData(GameConnection * conn, BitStream *stream); + U32 packUpdate(NetConnection * conn, U32 mask, BitStream *stream); + void unpackUpdate(NetConnection * conn, BitStream *stream); +}; + + +#endif diff --git a/Engine/source/T3D/zone.cpp b/Engine/source/T3D/zone.cpp new file mode 100644 index 000000000..f90267c38 --- /dev/null +++ b/Engine/source/T3D/zone.cpp @@ -0,0 +1,106 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/zone.h" + +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "scene/mixin/sceneAmbientSoundObject.impl.h" +#include "scene/mixin/scenePolyhedralObject.impl.h" + + +IMPLEMENT_CO_NETOBJECT_V1( Zone ); + +ConsoleDocClass( Zone, + "@brief An object that represents an interior space.\n\n" + + "A zone is an invisible volume that encloses an interior space. All objects that have " + "their world space axis-aligned bounding boxes (AABBs) intersect the zone's volume are " + "assigned to the zone. This assignment happens automatically as objects are placed " + "and transformed. Also, assignment is not exclusive meaning that an object can be assigned " + "to many zones at the same time if it intersects all of them.\n\n" + + "In itself, the volume of a zone is fully sealed off from the outside. This means that while " + "viewing the scene from inside the volume, only objects assigned to the zone are rendered while " + "when viewing the scene from outside the volume, objects exclusively only assigned the " + "zone are not rendered.\n\n" + + "Usually, you will want to connect zones to each other by means of portals. A portal overlapping " + "with a zone \n\n" + + "@tsexample\n" + "// Example declaration of a Zone. This creates a box-shaped zone.\n" + "new Zone( TestZone )\n" + "{\n" + " position = \"3.61793 -1.01945 14.7442\";\n" + " rotation = \"1 0 0 0\";\n" + " scale = \"10 10 10\";\n" + "};\n" + "@endtsexample\n\n" + + "@section Zone_zoneGroups Zone Groups\n\n" + + "Normally, Zones will not connect to each other when they overlap. This means that if viewing " + "the scene from one zone, the contents of the other zone will not be visible except when there " + "is a portal connecting the zones. However, sometimes it is convenient to represent a single interior " + "space through a combination of Zones so that when any of these zones is visible, all other zones " + "that are part of the same interior space are visible. This is possible by employing \"zone groups\".\n\n" + + "@see Portal\n" + + "@ingroup enviroMisc\n" +); + + +//----------------------------------------------------------------------------- + +void Zone::consoleInit() +{ + // Disable rendering of zones by default. + getStaticClassRep()->mIsRenderEnabled = false; +} + +//============================================================================= +// Console API. +//============================================================================= +// MARK: ---- Console API ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Zone, getZoneId, S32, (),, + "Get the unique numeric ID of the zone in its scene.\n\n" + "@return The ID of the zone." ) +{ + return object->getZoneRangeStart(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( Zone, dumpZoneState, void, ( bool updateFirst ), ( true ), + "Dump a list of all objects assigned to the zone to the console as well as a list " + "of all connected zone spaces.\n\n" + "@param updateFirst Whether to update the contents of the zone before dumping. Since zoning states of " + "objects are updated on demand, the zone contents can be outdated." ) +{ + object->dumpZoneState( updateFirst ); +} diff --git a/Engine/source/T3D/zone.h b/Engine/source/T3D/zone.h new file mode 100644 index 000000000..ac87d974c --- /dev/null +++ b/Engine/source/T3D/zone.h @@ -0,0 +1,72 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _ZONE_H_ +#define _ZONE_H_ + +#ifndef _SCENEPOLYHEDRALZONE_H_ +#include "scene/zones/scenePolyhedralZone.h" +#endif + +#ifndef _SCENEAMBIENTSOUNDOBJECT_H_ +#include "scene/mixin/sceneAmbientSoundObject.h" +#endif + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + + +/// A volume in space that encloses objects. +/// +/// Zones do not physically contain objects in the scene. Rather, any object +/// that has its world box coincide with the world box of a zone is considered +/// to be part of that zone. As such, objects can be in multiple zones at +/// the same time. +class Zone : public SceneAmbientSoundObject< ScenePolyhedralZone > +{ + public: + + typedef SceneAmbientSoundObject< ScenePolyhedralZone > Parent; + + protected: + + // SceneVolume. + virtual ColorI _getDefaultEditorSolidColor() const { return ColorI( 255, 0, 0, 45 ); } + + public: + + Zone() {} + Zone( const Polyhedron& polyhedron ) + { + mPolyhedron = polyhedron; + } + + // SimObject + DECLARE_CONOBJECT( Zone ); + DECLARE_DESCRIPTION( "A volume that encloses objects for visibility culling." ); + DECLARE_CATEGORY( "3D" ); + + static void consoleInit(); +}; + +#endif // _ZONE_H_ diff --git a/Engine/source/app/auth.h b/Engine/source/app/auth.h new file mode 100644 index 000000000..2f2098466 --- /dev/null +++ b/Engine/source/app/auth.h @@ -0,0 +1,59 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _AUTH_H_ +#define _AUTH_H_ + +#ifndef _EVENT_H_ +#include "platform/event.h" +#endif + +/// Formerly contained a certificate, showing that something was valid. +class Auth2Certificate +{ + U32 xxx; +}; + +/// Formerly contained data indicating whether a user is valid. +struct AuthInfo +{ + enum { + MaxNameLen = 31, + }; + + bool valid; + char name[MaxNameLen + 1]; +}; + +/// Formerly validated the server's authentication info. +inline bool validateAuthenticatedServer() +{ + return true; +} + +/// Formerly validated the client's authentication info. +inline bool validateAuthenticatedClient() +{ + return true; +} + +#endif diff --git a/Engine/source/app/badWordFilter.cpp b/Engine/source/app/badWordFilter.cpp new file mode 100644 index 000000000..74675370b --- /dev/null +++ b/Engine/source/app/badWordFilter.cpp @@ -0,0 +1,331 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/strings/stringFunctions.h" + +#include "console/consoleTypes.h" +#include "app/badWordFilter.h" +#include "core/module.h" +#include "console/engineAPI.h" + +MODULE_BEGIN( BadWordFilter ) + + MODULE_INIT + { + BadWordFilter::create(); + } + + MODULE_SHUTDOWN + { + BadWordFilter::destroy(); + } + +MODULE_END; + + +BadWordFilter *gBadWordFilter = NULL; +bool BadWordFilter::filteringEnabled = true; + +BadWordFilter::BadWordFilter() +{ + VECTOR_SET_ASSOCIATION( filterTables ); + + dStrcpy(defaultReplaceStr, "knqwrtlzs"); + filterTables.push_back(new FilterTable); + curOffset = 0; +} + +BadWordFilter::~BadWordFilter() +{ + for(U32 i = 0; i < filterTables.size(); i++) + delete filterTables[i]; +} + +void BadWordFilter::create() +{ + Con::addVariable("pref::enableBadWordFilter", TypeBool, &filteringEnabled, + "@brief If true, the bad word filter will be enabled.\n\n" + "@ingroup Game"); + gBadWordFilter = new BadWordFilter; + gBadWordFilter->addBadWord("shit"); + gBadWordFilter->addBadWord("fuck"); + gBadWordFilter->addBadWord("cock"); + gBadWordFilter->addBadWord("bitch"); + gBadWordFilter->addBadWord("cunt"); + gBadWordFilter->addBadWord("nigger"); + gBadWordFilter->addBadWord("bastard"); + gBadWordFilter->addBadWord("dick"); + gBadWordFilter->addBadWord("whore"); + gBadWordFilter->addBadWord("goddamn"); + gBadWordFilter->addBadWord("asshole"); +} + +void BadWordFilter::destroy() +{ + delete gBadWordFilter; + gBadWordFilter = NULL; +} + + +U8 BadWordFilter::remapTable[257] = "------------------------------------------------OI---------------ABCDEFGHIJKLMNOPQRSTUVWXYZ------ABCDEFGHIJKLMNOPQRSTUVWXYZ-----C--F--TT--S-C-Z-----------S-C-ZY--CLOY-S-CA---R---UT-UP--IO-----AAAAAAACEEEEIIIIDNOOOOOXOUUUUYDBAAAAAAACEEEEIIIIDNOOOOO-OUUUUYDY"; +U8 BadWordFilter::randomJunk[MaxBadwordLength+1] = "REMsg rk34n4ksqow;xnskq;KQoaWnZa"; + +BadWordFilter::FilterTable::FilterTable() +{ + for(U32 i = 0; i < 26; i++) + nextState[i] = TerminateNotFound; +} + +bool BadWordFilter::addBadWord(const char *cword) +{ + FilterTable *curFilterTable = filterTables[0]; + // prescan the word to see if it has any skip chars + const U8 *word = (const U8 *) cword; + const U8 *walk = word; + if(dStrlen(cword) > MaxBadwordLength) + return false; + while(*walk) + { + if(remapTable[*walk] == '-') + return false; + walk++; + } + while(*word) + { + U8 remap = remapTable[*word] - 'A'; + U16 state = curFilterTable->nextState[remap]; + + if(state < TerminateNotFound) + { + // this character is already in the state table... + curFilterTable = filterTables[state]; + } + else if(state == TerminateFound) + { + // a subset of this word is already in the table... + // exit out. + return false; + } + else if(state == TerminateNotFound) + { + if(word[1]) + { + curFilterTable->nextState[remap] = filterTables.size(); + filterTables.push_back(new FilterTable); + curFilterTable = filterTables[filterTables.size() - 1]; + } + else + curFilterTable->nextState[remap] = TerminateFound; + } + word++; + } + return true; +} + +bool BadWordFilter::setDefaultReplaceStr(const char *str) +{ + U32 len = dStrlen(str); + if(len < 2 || len >= sizeof(defaultReplaceStr)) + return false; + dStrcpy(defaultReplaceStr, str); + return true; +} + +void BadWordFilter::filterString(char *cstring, const char *replaceStr) +{ + if(!replaceStr) + replaceStr = defaultReplaceStr; + U8 *string = (U8 *) cstring; + U8 *starts[MaxBadwordLength]; + U8 *curStart = string; + U32 replaceLen = dStrlen(replaceStr); + while(*curStart) + { + FilterTable *curFilterTable = filterTables[0]; + S32 index = 0; + U8 *walk = curStart; + while(*walk) + { + U8 remap = remapTable[*walk]; + if(remap != '-') + { + starts[index++] = walk; + U16 table = curFilterTable->nextState[remap - 'A']; + if(table < TerminateNotFound) + curFilterTable = filterTables[table]; + else if(table == TerminateNotFound) + { + curStart++; + break; + } + else // terminate found + { + for(U32 i = 0; i < index; i++) + { + starts[i][0] = (U8 )replaceStr[curOffset % replaceLen]; + curOffset += randomJunk[curOffset & (MaxBadwordLength - 1)]; + } + curStart = walk + 1; + break; + } + } + walk++; + } + if(!*walk) + curStart++; + } +} + +bool BadWordFilter::containsBadWords(const char *cstring) +{ + U8 *string = (U8 *) cstring; + U8 *curStart = string; + while(*curStart) + { + FilterTable *curFilterTable = filterTables[0]; + U8 *walk = curStart; + while(*walk) + { + U8 remap = remapTable[*walk]; + if(remap != '-') + { + U16 table = curFilterTable->nextState[remap - 'A']; + if(table < TerminateNotFound) + curFilterTable = filterTables[table]; + else if(table == TerminateNotFound) + { + curStart++; + break; + } + else // terminate found + return true; + } + walk++; + } + if(!*walk) + curStart++; + } + return false; +} + +DefineEngineFunction(addBadWord, bool, (const char* badWord),, + "@brief Add a string to the bad word filter\n\n" + + "The bad word filter is a table containing words which will not be " + "displayed in chat windows. Instead, a designated replacement string will be displayed. " + "There are already a number of bad words automatically defined.\n\n" + + "@param badWord Exact text of the word to restrict.\n" + "@return True if word was successfully added, false if the word or a subset of it already exists in the table\n" + + "@see filterString()\n\n" + + "@tsexample\n" + "// In this game, \"Foobar\" is banned\n" + "%badWord = \"Foobar\";\n\n" + "// Returns true, word was successfully added\n" + "addBadWord(%badWord);\n\n" + "// Returns false, word has already been added\n" + "addBadWord(\"Foobar\");" + "@endtsexample\n" + + "@ingroup Game") +{ + return gBadWordFilter->addBadWord(badWord); +} + +DefineEngineFunction(filterString, const char *, (const char* baseString, const char* replacementChars), (NULL, NULL), + "@brief Replaces the characters in a string with designated text\n\n" + + "Uses the bad word filter to determine which characters within the string will be replaced.\n\n" + + "@param baseString The original string to filter.\n" + "@param replacementChars A string containing letters you wish to swap in the baseString.\n" + "@return The new scrambled string \n" + + "@see addBadWord()\n" + "@see containsBadWords()\n" + + "@tsexample\n" + "// Create the base string, can come from anywhere\n" + "%baseString = \"Foobar\";\n\n" + "// Create a string of random letters\n" + "%replacementChars = \"knqwrtlzs\";\n\n" + "// Filter the string\n" + "%newString = filterString(%baseString, %replacementChars);\n\n" + "// Print the new string to console\n" + "echo(%newString);" + "@endtsexample\n" + + "@ingroup Game") +{ + const char *replaceStr = NULL; + + if(replacementChars) + replaceStr = replacementChars; + else + replaceStr = gBadWordFilter->getDefaultReplaceStr(); + + char *ret = Con::getReturnBuffer(dStrlen(baseString) + 1); + dStrcpy(ret, baseString); + gBadWordFilter->filterString(ret, replaceStr); + return ret; +} + +DefineEngineFunction(containsBadWords, bool, (const char* text),, + "@brief Checks to see if text is a bad word\n\n" + + "The text is considered to be a bad word if it has been added to the bad word filter.\n\n" + + "@param text Text to scan for bad words\n" + "@return True if the text has bad word(s), false if it is clean\n" + + "@see addBadWord()\n" + "@see filterString()\n" + + "@tsexample\n" + "// In this game, \"Foobar\" is banned\n" + "%badWord = \"Foobar\";\n\n" + "// Add a banned word to the bad word filter\n" + "addBadWord(%badWord);\n\n" + "// Create the base string, can come from anywhere like user chat\n" + "%userText = \"Foobar\";\n\n" + "// Create a string of random letters\n" + "%replacementChars = \"knqwrtlzs\";\n\n" + "// If the text contains a bad word, filter it before printing\n" + "// Otherwise print the original text\n" + "if(containsBadWords(%userText))\n" + "{\n" + " // Filter the string\n" + " %filteredText = filterString(%userText, %replacementChars);\n\n" + " // Print filtered text\n" + " echo(%filteredText);\n" + "}\n" + "else\n" + " echo(%userText);\n\n" + "@endtsexample\n" + + "@ingroup Game") +{ + return gBadWordFilter->containsBadWords(text); +} + diff --git a/Engine/source/app/badWordFilter.h b/Engine/source/app/badWordFilter.h new file mode 100644 index 000000000..439cd9a3e --- /dev/null +++ b/Engine/source/app/badWordFilter.h @@ -0,0 +1,67 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _H_BADWORDFILTER +#define _H_BADWORDFILTER + +#include "core/util/tVector.h" + +class BadWordFilter +{ +private: + struct FilterTable + { + U16 nextState[26]; // only 26 alphabetical chars. + FilterTable(); + }; + friend struct FilterTable; + Vector filterTables; + + enum { + TerminateNotFound = 0xFFFE, + TerminateFound = 0xFFFF, + MaxBadwordLength = 32, + }; + char defaultReplaceStr[32]; + + BadWordFilter(); + ~BadWordFilter(); + U32 curOffset; + static U8 remapTable[257]; + static U8 randomJunk[MaxBadwordLength + 1]; + static bool filteringEnabled; + +public: + bool addBadWord(const char *word); + bool setDefaultReplaceStr(const char *str); + const char* getDefaultReplaceStr(){ return defaultReplaceStr; } + void filterString(char *string, const char *replaceStr = NULL); + bool containsBadWords(const char *string); + + static bool isEnabled() { return filteringEnabled; } + static void setEnabled(bool enable) { filteringEnabled = enable; } + static void create(); + static void destroy(); +}; + +extern BadWordFilter *gBadWordFilter; + +#endif \ No newline at end of file diff --git a/Engine/source/app/banList.cpp b/Engine/source/app/banList.cpp new file mode 100644 index 000000000..952b49a05 --- /dev/null +++ b/Engine/source/app/banList.cpp @@ -0,0 +1,301 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "app/banList.h" +#include "core/stream/fileStream.h" +#include "core/module.h" +#include "console/engineAPI.h" + + +IMPLEMENT_STATIC_CLASS( BanList,, "Functions for maintaing a list of banned users." ); + +ConsoleDoc( + "@class BanList\n" + "@ingroup Miscellaneous\n" + "@brief Used for kicking and banning players from a server.\n" + "There is only a single instance of BanList. It is very important to note that you do not ever create this object in script " + "like you would other game play objects. You simply reference it via namespace.\n\n" + "For this to be used effectively, make sure you are hooking up other functions to BanList. " + "For example, functions like GameConnection::onConnectRequestRejected( %this, %msg ) and function GameConnection::onConnectRequest are excellent " + "places to make use of the BanList. Other systems can be used in conjunction for strict control over a server\n\n" + "@see addBadWord\n" + "@see containsBadWords\n" +); + +BanList* BanList::smInstance; + +MODULE_BEGIN( BanList ) + MODULE_INIT + { + new BanList; + } + MODULE_SHUTDOWN + { + delete BanList::instance(); + } +MODULE_END; + + +//------------------------------------------------------------------------------ + +BanList::BanList() +{ + AssertFatal( !smInstance, "BanList::BanList - already instantiated" ); + VECTOR_SET_ASSOCIATION( list ); + + smInstance = this; +} + +//------------------------------------------------------------------------------ + +void BanList::addBan(S32 uniqueId, const char *TA, S32 banTime) +{ + S32 curTime = Platform::getTime(); + + if(banTime != 0 && banTime < curTime) + return; + + // make sure this bastard isn't already banned on this server + Vector::iterator i; + for(i = list.begin();i != list.end();i++) + { + if(uniqueId == i->uniqueId) + { + i->bannedUntil = banTime; + return; + } + } + + BanInfo b; + dStrcpy(b.transportAddress, TA); + b.uniqueId = uniqueId; + b.bannedUntil = banTime; + + if(!dStrnicmp(b.transportAddress, "ip:", 3)) + { + char *c = dStrchr(b.transportAddress+3, ':'); + if(c) + { + *(c+1) = '*'; + *(c+2) = 0; + } + } + + list.push_back(b); +} + +//------------------------------------------------------------------------------ + +void BanList::addBanRelative(S32 uniqueId, const char *TA, S32 numSeconds) +{ + S32 curTime = Platform::getTime(); + S32 banTime = 0; + if(numSeconds != -1) + banTime = curTime + numSeconds; + + addBan(uniqueId, TA, banTime); +} + +//------------------------------------------------------------------------------ + +void BanList::removeBan(S32 uniqueId, const char *) +{ + Vector::iterator i; + for(i = list.begin();i != list.end();i++) + { + if(uniqueId == i->uniqueId) + { + list.erase(i); + return; + } + } +} + +//------------------------------------------------------------------------------ + +bool BanList::isBanned(S32 uniqueId, const char *) +{ + S32 curTime = Platform::getTime(); + + Vector::iterator i; + for(i = list.begin();i != list.end();) + { + if(i->bannedUntil != 0 && i->bannedUntil < curTime) + { + list.erase(i); + continue; + } + else if(uniqueId == i->uniqueId) + return true; + i++; + } + return false; +} + +//------------------------------------------------------------------------------ + +bool BanList::isTAEq(const char *bannedTA, const char *TA) +{ + char a, b; + for(;;) + { + a = *bannedTA++; + b = *TA++; + if(a == '*' || (!a && b == ':')) // ignore port + return true; + if(dTolower(a) != dTolower(b)) + return false; + if(!a) + return true; + } +} + +//------------------------------------------------------------------------------ + +void BanList::exportToFile(const char *name) +{ + FileStream *banlist; + + char filename[1024]; + Con::expandScriptFilename(filename, sizeof(filename), name); + if((banlist = FileStream::createAndOpen( filename, Torque::FS::File::Write )) == NULL) + return; + + char buf[1024]; + Vector::iterator i; + for(i = list.begin(); i != list.end(); i++) + { + dSprintf(buf, sizeof(buf), "BanList::addAbsolute(%d, \"%s\", %d);\r\n", i->uniqueId, i->transportAddress, i->bannedUntil); + banlist->write(dStrlen(buf), buf); + } + + delete banlist; +} + +//============================================================================= +// API. +//============================================================================= +// MARK: ---- API ---- + +//----------------------------------------------------------------------------- + +DefineEngineStaticMethod( BanList, addAbsolute, void, ( S32 uniqueId, const char* transportAddress, S32 banTime ),, + "Ban a user until a given time.\n\n" + "@param uniqueId Unique ID of the player.\n" + "@param transportAddress Address from which the player connected.\n" + "@param banTime Time at which they will be allowed back in." + "@tsexample\n" + "// Kick someone off the server\n" + "// %client - This is the connection to the person we are kicking\n" + "function kick(%client)\n" + "{\n" + " // Let the server know what happened\n" + " messageAll( 'MsgAdminForce', '\\c2The Admin has kicked %1.', %client.playerName);\n\n" + " // If it is not an AI Player, execute the ban.\n" + " if (!%client.isAIControlled())\n" + " BanList::addAbsolute(%client.guid, %client.getAddress(), $pref::Server::KickBanTime);\n\n" + " // Let the player know they messed up\n" + " %client.delete(\"You have been kicked from this server\");\n" + "}\n" + "@endtsexample\n\n") +{ + BanList::instance()->addBan( uniqueId, transportAddress, banTime ); +} + +//----------------------------------------------------------------------------- + +DefineEngineStaticMethod( BanList, add, void, ( S32 uniqueId, const char* transportAddress, S32 banLength ),, + "Ban a user for banLength seconds.\n\n" + "@param uniqueId Unique ID of the player.\n" + "@param transportAddress Address from which the player connected.\n" + "@param banLength Time period over which to ban the player." + "@tsexample\n" + "// Kick someone off the server\n" + "// %client - This is the connection to the person we are kicking\n" + "function kick(%client)\n" + "{\n" + " // Let the server know what happened\n" + " messageAll( 'MsgAdminForce', '\\c2The Admin has kicked %1.', %client.playerName);\n\n" + " // If it is not an AI Player, execute the ban.\n" + " if (!%client.isAIControlled())\n" + " BanList::add(%client.guid, %client.getAddress(), $pref::Server::KickBanTime);\n\n" + " // Let the player know they messed up\n" + " %client.delete(\"You have been kicked from this server\");\n" + "}\n" + "@endtsexample\n\n") +{ + BanList::instance()->addBanRelative( uniqueId, transportAddress, banLength ); +} + +//----------------------------------------------------------------------------- + +DefineEngineStaticMethod( BanList, removeBan, void, ( S32 uniqueId, const char* transportAddress ),, + "Unban someone.\n\n" + "@param uniqueId Unique ID of the player.\n" + "@param transportAddress Address from which the player connected.\n" + "@tsexample\n" + "BanList::removeBan(%userID, %ipAddress);\n" + "@endtsexample\n\n") +{ + BanList::instance()->removeBan( uniqueId, transportAddress ); +} + +//----------------------------------------------------------------------------- + +DefineEngineStaticMethod( BanList, isBanned, bool, ( S32 uniqueId, const char* transportAddress ),, + "Is someone banned?\n\n" + "@param uniqueId Unique ID of the player.\n" + "@param transportAddress Address from which the player connected.\n\n" + "@tsexample\n" + "//-----------------------------------------------------------------------------\n" + "// This script function is called before a client connection\n" + "// is accepted. Returning "" will accept the connection,\n" + "// anything else will be sent back as an error to the client.\n" + "// All the connect args are passed also to onConnectRequest\n" + "function GameConnection::onConnectRequest( %client, %netAddress, %name )\n" + "{\n" + " // Find out who is trying to connect\n" + " echo(\"Connect request from: \" @ %netAddress);\n\n" + " // Are they allowed in?\n" + " if(BanList::isBanned(%client.guid, %netAddress))\n" + " return \"CR_YOUAREBANNED\";\n\n" + " // Is there room for an unbanned player?\n" + " if($Server::PlayerCount >= $pref::Server::MaxPlayers)\n" + " return \"CR_SERVERFULL\";\n" + " return "";\n" + "}\n" + "@endtsexample\n\n") +{ + return BanList::instance()->isBanned( uniqueId, transportAddress ); +} + +//----------------------------------------------------------------------------- + +DefineEngineStaticMethod( BanList, export, void, ( const char* filename ),, + "Dump the banlist to a file.\n\n" + "@param filename Path of the file to write the list to.\n\n" + "@tsexample\n" + "BanList::Export(\"./server/banlist.cs\");\n" + "@endtsexample\n\n") +{ + BanList::instance()->exportToFile( filename ); +} diff --git a/Engine/source/app/banList.h b/Engine/source/app/banList.h new file mode 100644 index 000000000..35d888114 --- /dev/null +++ b/Engine/source/app/banList.h @@ -0,0 +1,67 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _BANLIST_H_ +#define _BANLIST_H_ + +#ifndef _ENGINEAPI_H_ + #include "console/engineAPI.h" +#endif +#ifndef _TVECTOR_H_ + #include "core/util/tVector.h" +#endif + +/// Helper class to keep track of bans. +class BanList +{ + public: + + DECLARE_STATIC_CLASS( BanList ); + + struct BanInfo + { + S32 uniqueId; + char transportAddress[128]; + S32 bannedUntil; + }; + + protected: + + Vector< BanInfo > list; + + static BanList* smInstance; + + public: + + BanList(); + + static BanList* instance() { return smInstance; } + + void addBan(S32 uniqueId, const char *TA, S32 banTime); + void addBanRelative(S32 uniqueId, const char *TA, S32 numSeconds); + void removeBan(S32 uniqueId, const char *TA); + bool isBanned(S32 uniqueId, const char *TA); + bool isTAEq(const char *bannedTA, const char *TA); + void exportToFile(const char *fileName); +}; + +#endif diff --git a/Engine/source/app/game.cpp b/Engine/source/app/game.cpp new file mode 100644 index 000000000..c3300af93 --- /dev/null +++ b/Engine/source/app/game.cpp @@ -0,0 +1,258 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "platform/platformInput.h" + +#include "app/game.h" +#include "math/mMath.h" +#include "core/dnet.h" +#include "core/stream/fileStream.h" +#include "core/frameAllocator.h" +#include "core/iTickable.h" +#include "core/strings/findMatch.h" +#include "console/simBase.h" +#include "console/console.h" +#include "console/consoleTypes.h" +#include "gui/controls/guiMLTextCtrl.h" +#ifdef TORQUE_TGB_ONLY +#include "T2D/oldModel/networking/t2dGameConnection.h" +#include "T2D/oldModel/networking/t2dNetworkServerSceneProcess.h" +#include "T2D/oldModel/networking/t2dNetworkClientSceneProcess.h" +#else +#include "T3D/gameBase/gameConnection.h" +#include "T3D/gameFunctions.h" +#include "T3D/gameBase/gameProcess.h" +#endif +#include "platform/profiler.h" +#include "gfx/gfxCubemap.h" +#include "gfx/gfxTextureManager.h" +#include "sfx/sfxSystem.h" + +#ifdef TORQUE_PLAYER +// See matching #ifdef in editor/editor.cpp +bool gEditingMission = false; +#endif + +//-------------------------------------------------------------------------- + +ConsoleFunctionGroupBegin( InputManagement, "Functions that let you deal with input from scripts" ); + +ConsoleFunction( deactivateDirectInput, void, 1, 1, "()" + "@brief Disables DirectInput.\n\n" + "Also deactivates any connected joysticks.\n\n" + "@ingroup Input" ) +{ + TORQUE_UNUSED(argc); TORQUE_UNUSED(argv); + if ( Input::isActive() ) + Input::deactivate(); +} + +ConsoleFunction( activateDirectInput, void, 1, 1,"()" + "@brief Activates DirectInput.\n\n" + "Also activates any connected joysticks." + "@ingroup Input") +{ + TORQUE_UNUSED(argc); TORQUE_UNUSED(argv); + if ( !Input::isActive() ) + Input::activate(); +} +ConsoleFunctionGroupEnd( InputManagement ); + +//-------------------------------------------------------------------------- + +static const U32 MaxPlayerNameLength = 16; +ConsoleFunction( strToPlayerName, const char*, 2, 2, "strToPlayerName( string )" ) +{ + TORQUE_UNUSED(argc); + + const char* ptr = argv[1]; + + // Strip leading spaces and underscores: + while ( *ptr == ' ' || *ptr == '_' ) + ptr++; + + U32 len = dStrlen( ptr ); + if ( len ) + { + char* ret = Con::getReturnBuffer( MaxPlayerNameLength + 1 ); + char* rptr = ret; + ret[MaxPlayerNameLength - 1] = '\0'; + ret[MaxPlayerNameLength] = '\0'; + bool space = false; + + U8 ch; + while ( *ptr && dStrlen( ret ) < MaxPlayerNameLength ) + { + ch = (U8) *ptr; + + // Strip all illegal characters: + if ( ch < 32 || ch == ',' || ch == '.' || ch == '\'' || ch == '`' ) + { + ptr++; + continue; + } + + // Don't allow double spaces or space-underline combinations: + if ( ch == ' ' || ch == '_' ) + { + if ( space ) + { + ptr++; + continue; + } + else + space = true; + } + else + space = false; + + *rptr++ = *ptr; + ptr++; + } + *rptr = '\0'; + + //finally, strip out the ML text control chars... + return GuiMLTextCtrl::stripControlChars(ret); + } + + return( "" ); +} + +ConsoleFunctionGroupBegin( Platform , "General platform functions."); + +ConsoleFunction( lockMouse, void, 2, 2, "(bool isLocked)" + "@brief Lock or unlock the mouse to the window.\n\n" + "When true, prevents the mouse from leaving the bounds of the game window.\n\n" + "@ingroup Input") +{ + Platform::setWindowLocked(dAtob(argv[1])); +} + + +ConsoleFunction( setNetPort, bool, 2, 3, "(int port, bool bind=true)" + "@brief Set the network port for the game to use.\n\n" + + "@param port The port to use.\n" + "@param bind True if bind() should be called on the port.\n" + + "@returns True if the port was successfully opened.\n" + + "This will trigger a windows firewall prompt. " + "If you don't have firewall tunneling tech you can set this to false to avoid the prompt.\n\n" + "@ingroup Networking") +{ + bool bind = true; + if (argc == 3) + bind = dAtob(argv[2]); + return Net::openPort(dAtoi(argv[1]), bind); +} + +ConsoleFunction( closeNetPort, void, 1, 1, "()" + "@brief Closes the current network port\n\n" + "@ingroup Networking") +{ + Net::closePort(); +} + +ConsoleFunction( saveJournal, void, 2, 2, "(string filename)" + "Save the journal to the specified file.\n\n" + "@ingroup Platform") +{ + Journal::Record(argv[1]); +} + +ConsoleFunction( playJournal, void, 2, 3, "(string filename)" + "@brief Begin playback of a journal from a specified field.\n\n" + "@param filename Name and path of file journal file\n" + "@ingroup Platform") +{ + // CodeReview - BJG 4/24/2007 - The break flag needs to be wired back in. + // bool jBreak = (argc > 2)? dAtob(argv[2]): false; + Journal::Play(argv[1]); +} + +ConsoleFunction( getSimTime, S32, 1, 1, "()" + "Return the current sim time in milliseconds.\n\n" + "@brief Sim time is time since the game started.\n\n" + "@ingroup Platform") +{ + return Sim::getCurrentTime(); +} + +ConsoleFunction( getRealTime, S32, 1, 1, "()" + "@brief Return the current real time in milliseconds.\n\n" + "Real time is platform defined; typically time since the computer booted.\n\n" + "@ingroup Platform") +{ + return Platform::getRealMilliseconds(); +} + +ConsoleFunctionGroupEnd(Platform); + +//----------------------------------------------------------------------------- + +bool clientProcess(U32 timeDelta) +{ + bool ret = true; + +#ifndef TORQUE_TGB_ONLY + ret = ClientProcessList::get()->advanceTime(timeDelta); +#else + ret = gt2dNetworkClientProcess.advanceTime( timeDelta ); +#endif + + ITickable::advanceTime(timeDelta); + +#ifndef TORQUE_TGB_ONLY + // Determine if we're lagging + GameConnection* connection = GameConnection::getConnectionToServer(); + if(connection) + { + connection->detectLag(); + } +#else + // Determine if we're lagging + t2dGameConnection* connection = t2dGameConnection::getConnectionToServer(); + if(connection) + { + connection->detectLag(); + } +#endif + + // Let SFX process. + SFX->_update(); + + return ret; +} + +bool serverProcess(U32 timeDelta) +{ + bool ret = true; +#ifndef TORQUE_TGB_ONLY + ret = ServerProcessList::get()->advanceTime(timeDelta); +#else + ret = gt2dNetworkServerProcess.advanceTime( timeDelta ); +#endif + return ret; +} + diff --git a/Engine/source/app/game.h b/Engine/source/app/game.h new file mode 100644 index 000000000..814c32a28 --- /dev/null +++ b/Engine/source/app/game.h @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GAME_H_ +#define _GAME_H_ + +#ifndef _TORQUE_TYPES_H_ +#include "platform/types.h" +#endif + +/// Processes the next frame, including gui, rendering, and tick interpolation. +/// This function will only have an effect when executed on the client. +bool clientProcess(U32 timeDelta); + +/// Processes the next cycle on the server. This function will only have an effect when executed on the server. +bool serverProcess(U32 timeDelta); + +#endif diff --git a/Engine/source/app/mainLoop.cpp b/Engine/source/app/mainLoop.cpp new file mode 100644 index 000000000..cb31d6c0b --- /dev/null +++ b/Engine/source/app/mainLoop.cpp @@ -0,0 +1,627 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "app/mainLoop.h" +#include "app/game.h" + +#include "platform/platformTimer.h" +#include "platform/platformRedBook.h" +#include "platform/platformVolume.h" +#include "platform/platformMemory.h" +#include "platform/platformTimer.h" +#include "platform/platformNet.h" +#include "platform/nativeDialogs/fileDialog.h" +#include "platform/threads/thread.h" + +#include "core/module.h" +#include "core/threadStatic.h" +#include "core/iTickable.h" +#include "core/stream/fileStream.h" + +#include "windowManager/platformWindowMgr.h" + +#include "core/util/journal/process.h" +#include "util/fpsTracker.h" + +#include "console/debugOutputConsumer.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" + +#include "gfx/bitmap/gBitmap.h" +#include "gfx/gFont.h" +#include "gfx/video/videoCapture.h" +#include "gfx/gfxTextureManager.h" + +#include "sim/netStringTable.h" +#include "sim/actionMap.h" +#include "sim/netInterface.h" + +#include "util/sampler.h" +#include "platform/threads/threadPool.h" + +// For the TickMs define... fix this for T2D... +#include "T3D/gameBase/processList.h" + +#ifdef TORQUE_DEMO_PURCHASE +#include "demo/pestTimer/pestTimer.h" +#endif + +#ifdef TORQUE_ENABLE_VFS +#include "platform/platformVFS.h" +#endif + +DITTS( F32, gTimeScale, 1.0 ); +DITTS( U32, gTimeAdvance, 0 ); +DITTS( U32, gFrameSkip, 0 ); + +extern S32 sgBackgroundProcessSleepTime; +extern S32 sgTimeManagerProcessInterval; + +extern FPSTracker gFPS; + +TimeManager* tm = NULL; + +static bool gRequiresRestart = false; + +#ifdef TORQUE_DEBUG + +/// Temporary timer used to time startup times. +static PlatformTimer* gStartupTimer; + +#endif + +#if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE ) +StringTableEntry gMiniDumpDir; +StringTableEntry gMiniDumpExec; +StringTableEntry gMiniDumpParams; +StringTableEntry gMiniDumpExecDir; +#endif + + +namespace engineAPI +{ + // This is the magic switch for deciding which interop the engine + // should use. It will go away when we drop the console system + // entirely but for now it is necessary for several behaviors that + // differ between the interops to decide what to do. + bool gUseConsoleInterop = true; + + bool gIsInitialized = false; +} + + + +// The following are some tricks to make the memory leak checker run after global +// dtors have executed by placing some code in the termination segments. + +#if defined( TORQUE_DEBUG ) && !defined( TORQUE_DISABLE_MEMORY_MANAGER ) + + #ifdef TORQUE_COMPILER_VISUALC + # pragma data_seg( ".CRT$XTU" ) + + static void* sCheckMemBeforeTermination = &Memory::ensureAllFreed; + + # pragma data_seg() + #elif defined( TORQUE_COMPILER_GCC ) + + __attribute__ ( ( destructor ) ) static void _ensureAllFreed() + { + Memory::ensureAllFreed(); + } + + #endif + +#endif + +// Process a time event and update all sub-processes +void processTimeEvent(S32 elapsedTime) +{ + PROFILE_START(ProcessTimeEvent); + + // If recording a video and not playinb back a journal, override the elapsedTime + if (VIDCAP->isRecording() && !Journal::IsPlaying()) + elapsedTime = VIDCAP->getMsPerFrame(); + + // cap the elapsed time to one second + // if it's more than that we're probably in a bad catch-up situation + if(elapsedTime > 1024) + elapsedTime = 1024; + + U32 timeDelta; + if(ATTS(gTimeAdvance)) + timeDelta = ATTS(gTimeAdvance); + else + timeDelta = (U32) (elapsedTime * ATTS(gTimeScale)); + + Platform::advanceTime(elapsedTime); + + // Don't build up more time than a single tick... this makes the sim + // frame rate dependent but is a useful hack for singleplayer. + if ( ATTS(gFrameSkip) ) + if ( timeDelta > TickMs ) + timeDelta = TickMs; + + bool tickPass; + + PROFILE_START(ServerProcess); + tickPass = serverProcess(timeDelta); + PROFILE_END(); + + PROFILE_START(ServerNetProcess); + // only send packets if a tick happened + if(tickPass) + GNet->processServer(); + // Used to indicate if server was just ticked. + Con::setBoolVariable( "$pref::hasServerTicked", tickPass ); + PROFILE_END(); + + + PROFILE_START(SimAdvanceTime); + Sim::advanceTime(timeDelta); + PROFILE_END(); + + PROFILE_START(ClientProcess); + tickPass = clientProcess(timeDelta); + // Used to indicate if client was just ticked. + Con::setBoolVariable( "$pref::hasClientTicked", tickPass ); + PROFILE_END_NAMED(ClientProcess); + + PROFILE_START(ClientNetProcess); + if(tickPass) + GNet->processClient(); + PROFILE_END(); + + GNet->checkTimeouts(); + + gFPS.update(); + + // Give the texture manager a chance to cleanup any + // textures that haven't been referenced for a bit. + if( GFX ) + TEXMGR->cleanupCache( 5 ); + + PROFILE_END(); + + // Update the console time + Con::setFloatVariable("Sim::Time",F32(Platform::getVirtualMilliseconds()) / 1000); +} + +void StandardMainLoop::init() +{ + #ifdef TORQUE_DEBUG + gStartupTimer = PlatformTimer::create(); + #endif + + #ifdef TORQUE_DEBUG_GUARD + Memory::flagCurrentAllocs( Memory::FLAG_Global ); + #endif + + Platform::setMathControlStateKnown(); + + // Asserts should be created FIRST + PlatformAssert::create(); + + ManagedSingleton< ThreadManager >::createSingleton(); + FrameAllocator::init(TORQUE_FRAME_SIZE); // See comments in torqueConfig.h + + // Yell if we can't initialize the network. + if(!Net::init()) + { + AssertISV(false, "StandardMainLoop::initCore - could not initialize networking!"); + } + + _StringTable::create(); + + // Set up the resource manager and get some basic file types in it. + Con::init(); + Platform::initConsole(); + NetStringTable::create(); + + // Use debug output logging on the Xbox and OSX builds +#if defined( _XBOX ) || defined( TORQUE_OS_MAC ) + DebugOutputConsumer::init(); +#endif + + Processor::init(); + Math::init(); + Platform::init(); // platform specific initialization + RedBook::init(); + Platform::initConsole(); + + ThreadPool::GlobalThreadPool::createSingleton(); + + // Initialize modules. + + ModuleManager::initializeSystem(); + + // Initialise ITickable. +#ifdef TORQUE_TGB_ONLY + ITickable::init( 4 ); +#endif + +#ifdef TORQUE_ENABLE_VFS + // [tom, 10/28/2006] Load the VFS here so that it stays loaded + Zip::ZipArchive *vfs = openEmbeddedVFSArchive(); + gResourceManager->addVFSRoot(vfs); +#endif + + Con::addVariable("timeScale", TypeF32, &ATTS(gTimeScale), "Animation time scale.\n" + "@ingroup platform"); + Con::addVariable("timeAdvance", TypeS32, &ATTS(gTimeAdvance), "The speed at which system processing time advances.\n" + "@ingroup platform"); + Con::addVariable("frameSkip", TypeS32, &ATTS(gFrameSkip), "Sets the number of frames to skip while rendering the scene.\n" + "@ingroup platform"); + + Con::setVariable( "defaultGame", StringTable->insert("scripts") ); + + Con::addVariable( "_forceAllMainThread", TypeBool, &ThreadPool::getForceAllMainThread(), "Force all work items to execute on main thread. turns this into a single-threaded system. Primarily useful to find whether malfunctions are caused by parallel execution or not.\n" + "@ingroup platform" ); + +#if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE ) + Con::addVariable("MiniDump::Dir", TypeString, &gMiniDumpDir); + Con::addVariable("MiniDump::Exec", TypeString, &gMiniDumpExec); + Con::addVariable("MiniDump::Params", TypeString, &gMiniDumpParams); + Con::addVariable("MiniDump::ExecDir", TypeString, &gMiniDumpExecDir); +#endif + + ActionMap* globalMap = new ActionMap; + globalMap->registerObject("GlobalActionMap"); + Sim::getActiveActionMapSet()->pushObject(globalMap); + + // Do this before we init the process so that process notifiees can get the time manager + tm = new TimeManager; + tm->timeEvent.notify(&::processTimeEvent); + + Sampler::init(); + + // Hook in for UDP notification + Net::smPacketReceive.notify(GNet, &NetInterface::processPacketReceiveEvent); + + #ifdef TORQUE_DEMO_PURCHASE + PestTimerinit(); + #endif + + #ifdef TORQUE_DEBUG_GUARD + Memory::flagCurrentAllocs( Memory::FLAG_Static ); + #endif +} + +void StandardMainLoop::shutdown() +{ + delete tm; + preShutdown(); + + // Shut down modules. + + ModuleManager::shutdownSystem(); + + ThreadPool::GlobalThreadPool::deleteSingleton(); + +#ifdef TORQUE_ENABLE_VFS + closeEmbeddedVFSArchive(); +#endif + + RedBook::destroy(); + + Platform::shutdown(); + +#if defined( _XBOX ) || defined( TORQUE_OS_MAC ) + DebugOutputConsumer::destroy(); +#endif + + NetStringTable::destroy(); + Con::shutdown(); + + _StringTable::destroy(); + FrameAllocator::destroy(); + Net::shutdown(); + Sampler::destroy(); + + ManagedSingleton< ThreadManager >::deleteSingleton(); + + // asserts should be destroyed LAST + PlatformAssert::destroy(); + +#if defined( TORQUE_DEBUG ) && !defined( TORQUE_DISABLE_MEMORY_MANAGER ) + Memory::validate(); +#endif +} + +void StandardMainLoop::preShutdown() +{ +#ifdef TORQUE_TOOLS + // Tools are given a chance to do pre-quit processing + // - This is because for tools we like to do things such + // as prompting to save changes before shutting down + // and onExit is packaged which means we can't be sure + // where in the shutdown namespace chain we are when using + // onExit since some components of the tools may already be + // destroyed that may be vital to saving changes to avoid + // loss of work [1/5/2007 justind] + if( Con::isFunction("onPreExit") ) + Con::executef( "onPreExit"); +#endif + + //exec the script onExit() function + if ( Con::isFunction( "onExit" ) ) + Con::executef("onExit"); +} + +bool StandardMainLoop::handleCommandLine( S32 argc, const char **argv ) +{ + // Allow the window manager to process command line inputs; this is + // done to let web plugin functionality happen in a fairly transparent way. + PlatformWindowManager::get()->processCmdLineArgs(argc, argv); + + Process::handleCommandLine( argc, argv ); + + // Set up the command line args for the console scripts... + Con::setIntVariable("Game::argc", argc); + U32 i; + for (i = 0; i < argc; i++) + Con::setVariable(avar("Game::argv%d", i), argv[i]); + + Platform::FS::InstallFileSystems(); // install all drives for now until we have everything using the volume stuff + Platform::FS::MountDefaults(); + + // Set our working directory. + Torque::FS::SetCwd( "game:/" ); + + // Set our working directory. + Platform::setCurrentDirectory( Platform::getMainDotCsDir() ); + +#ifdef TORQUE_PLAYER + if(argc > 2 && dStricmp(argv[1], "-project") == 0) + { + char playerPath[1024]; + Platform::makeFullPathName(argv[2], playerPath, sizeof(playerPath)); + Platform::setCurrentDirectory(playerPath); + + argv += 2; + argc -= 2; + + // Re-locate the game:/ asset mount. + + Torque::FS::Unmount( "game" ); + Torque::FS::Mount( "game", Platform::FS::createNativeFS( playerPath ) ); + } +#endif + + // Executes an entry script file. This is "main.cs" + // by default, but any file name (with no whitespace + // in it) may be run if it is specified as the first + // command-line parameter. The script used, default + // or otherwise, is not compiled and is loaded here + // directly because the resource system restricts + // access to the "root" directory. + +#ifdef TORQUE_ENABLE_VFS + Zip::ZipArchive *vfs = openEmbeddedVFSArchive(); + bool useVFS = vfs != NULL; +#endif + + Stream *mainCsStream = NULL; + + // The working filestream. + FileStream str; + + const char *defaultScriptName = "main.cs"; + bool useDefaultScript = true; + + // Check if any command-line parameters were passed (the first is just the app name). + if (argc > 1) + { + // If so, check if the first parameter is a file to open. + if ( (dStrcmp(argv[1], "") != 0 ) && (str.open(argv[1], Torque::FS::File::Read)) ) + { + // If it opens, we assume it is the script to run. + useDefaultScript = false; +#ifdef TORQUE_ENABLE_VFS + useVFS = false; +#endif + mainCsStream = &str; + } + } + + if (useDefaultScript) + { + bool success = false; + +#ifdef TORQUE_ENABLE_VFS + if(useVFS) + success = (mainCsStream = vfs->openFile(defaultScriptName, Zip::ZipArchive::Read)) != NULL; + else +#endif + success = str.open(defaultScriptName, Torque::FS::File::Read); + +#if defined( TORQUE_DEBUG ) && defined (TORQUE_TOOLS) && !defined( _XBOX ) + if (!success) + { + OpenFileDialog ofd; + FileDialogData &fdd = ofd.getData(); + fdd.mFilters = StringTable->insert("Main Entry Script (main.cs)|main.cs|"); + fdd.mTitle = StringTable->insert("Locate Game Entry Script"); + + // Get the user's selection + if( !ofd.Execute() ) + return false; + + // Process and update CWD so we can run the selected main.cs + S32 pathLen = dStrlen( fdd.mFile ); + FrameTemp szPathCopy( pathLen + 1); + + dStrcpy( szPathCopy, fdd.mFile ); + //forwardslash( szPathCopy ); + + const char *path = dStrrchr(szPathCopy, '/'); + if(path) + { + U32 len = path - (const char*)szPathCopy; + szPathCopy[len+1] = 0; + + Platform::setCurrentDirectory(szPathCopy); + + // Re-locate the game:/ asset mount. + + Torque::FS::Unmount( "game" ); + Torque::FS::Mount( "game", Platform::FS::createNativeFS( ( const char* ) szPathCopy ) ); + + success = str.open(fdd.mFile, Torque::FS::File::Read); + if(success) + defaultScriptName = fdd.mFile; + } + } +#endif + if( !success ) + { + char msg[1024]; + dSprintf(msg, sizeof(msg), "Failed to open \"%s\".", defaultScriptName); + Platform::AlertOK("Error", msg); +#ifdef TORQUE_ENABLE_VFS + closeEmbeddedVFSArchive(); +#endif + + return false; + } + +#ifdef TORQUE_ENABLE_VFS + if(! useVFS) +#endif + mainCsStream = &str; + } + + // This should rarely happen, but lets deal with + // it gracefully if it does. + if ( mainCsStream == NULL ) + return false; + + U32 size = mainCsStream->getStreamSize(); + char *script = new char[size + 1]; + mainCsStream->read(size, script); + +#ifdef TORQUE_ENABLE_VFS + if(useVFS) + vfs->closeFile(mainCsStream); + else +#endif + str.close(); + + script[size] = 0; + + char buffer[1024], *ptr; + Platform::makeFullPathName(useDefaultScript ? defaultScriptName : argv[1], buffer, sizeof(buffer), Platform::getCurrentDirectory()); + ptr = dStrrchr(buffer, '/'); + if(ptr != NULL) + *ptr = 0; + Platform::setMainDotCsDir(buffer); + Platform::setCurrentDirectory(buffer); + + Con::evaluate(script, false, useDefaultScript ? defaultScriptName : argv[1]); + delete[] script; + +#ifdef TORQUE_ENABLE_VFS + closeEmbeddedVFSArchive(); +#endif + + return true; +} + +bool StandardMainLoop::doMainLoop() +{ + #ifdef TORQUE_DEBUG + if( gStartupTimer ) + { + Con::printf( "Started up in %.2f seconds...", + F32( gStartupTimer->getElapsedMs() ) / 1000.f ); + SAFE_DELETE( gStartupTimer ); + } + #endif + + bool keepRunning = true; +// while(keepRunning) + { + tm->setBackgroundThreshold(mClamp(sgBackgroundProcessSleepTime, 1, 200)); + tm->setForegroundThreshold(mClamp(sgTimeManagerProcessInterval, 1, 200)); + // update foreground/background status + if(WindowManager->getFirstWindow()) + { + static bool lastFocus = false; + bool newFocus = ( WindowManager->getFocusedWindow() != NULL ); + if(lastFocus != newFocus) + { +#ifndef TORQUE_SHIPPING + Con::printf("Window focus status changed: focus: %d", newFocus); + if (!newFocus) + Con::printf(" Using background sleep time: %u", Platform::getBackgroundSleepTime()); +#endif + +#ifdef TORQUE_OS_MAC + if (newFocus) + WindowManager->getFirstWindow()->show(); + +#endif + lastFocus = newFocus; + } + +#ifndef TORQUE_OS_MAC + // under the web plugin do not sleep the process when the child window loses focus as this will cripple the browser perfomance + if (!Platform::getWebDeployment()) + tm->setBackground(!newFocus); + else + tm->setBackground(false); +#else + tm->setBackground(false); +#endif + } + else + { + tm->setBackground(false); + } + + PROFILE_START(MainLoop); + Sampler::beginFrame(); + + if(!Process::processEvents()) + keepRunning = false; + + ThreadPool::processMainThreadWorkItems(); + Sampler::endFrame(); + PROFILE_END_NAMED(MainLoop); + + #ifdef TORQUE_DEMO_PURCHASE + CheckTimer(); + CheckBlocker(); + #endif + } + + return keepRunning; +} + +void StandardMainLoop::setRestart(bool restart ) +{ + gRequiresRestart = restart; +} + +bool StandardMainLoop::requiresRestart() +{ + return gRequiresRestart; +} diff --git a/Engine/source/app/mainLoop.h b/Engine/source/app/mainLoop.h new file mode 100644 index 000000000..4c2b186bd --- /dev/null +++ b/Engine/source/app/mainLoop.h @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _APP_MAINLOOP_H_ +#define _APP_MAINLOOP_H_ + +#include "platform/platform.h" + +/// Support class to simplify the process of writing a main loop for Torque apps. +class StandardMainLoop +{ +public: + /// Initialize core libraries and call registered init functions + static void init(); + + /// Pass command line arguments to registered functions and main.cs + static bool handleCommandLine(S32 argc, const char **argv); + + /// A standard mainloop implementation. + static bool doMainLoop(); + + /// Shut down the core libraries and call registered shutdown fucntions. + static void shutdown(); + + static void setRestart( bool restart ); + static bool requiresRestart(); + +private: + /// Handle "pre shutdown" tasks like notifying scripts BEFORE we delete + /// stuff from under them. + static void preShutdown(); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/app/net/httpObject.cpp b/Engine/source/app/net/httpObject.cpp new file mode 100644 index 000000000..91287d5ad --- /dev/null +++ b/Engine/source/app/net/httpObject.cpp @@ -0,0 +1,455 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "app/net/httpObject.h" + +#include "platform/platform.h" +#include "platform/event.h" +#include "core/stream/fileStream.h" +#include "console/simBase.h" +#include "console/consoleInternal.h" +#include "console/engineAPI.h" + +IMPLEMENT_CONOBJECT(HTTPObject); + +ConsoleDocClass( HTTPObject, + "@brief Allows communications between the game and a server using HTTP.\n\n" + + "HTTPObject is derrived from TCPObject and makes use of the same callbacks for dealing with " + "connections and received data. However, the way in which you use HTTPObject to connect " + "with a server is different than TCPObject. Rather than opening a connection, sending data, " + "waiting to receive data, and then closing the connection, you issue a get() or post() and " + "handle the response. The connection is automatically created and destroyed for you.\n\n" + + "@tsexample\n" + "// In this example we'll retrieve the weather in Las Vegas using\n" + "// Google's API. The response is in XML which could be processed\n" + "// and used by the game using SimXMLDocument, but we'll just output\n" + "// the results to the console in this example.\n\n" + + "// Define callbacks for our specific HTTPObject using our instance's\n" + "// name (WeatherFeed) as the namespace.\n\n" + + "// Handle an issue with resolving the server's name\n" + "function WeatherFeed::onDNSFailed(%this)\n" + "{\n" + " // Store this state\n" + " %this.lastState = \"DNSFailed\";\n\n" + + " // Handle DNS failure\n" + "}\n\n" + + "function WeatherFeed::onConnectFailed(%this)\n" + "{\n" + " // Store this state\n" + " %this.lastState = \"ConnectFailed\";\n\n" + " // Handle connection failure\n" + "}\n\n" + + "function WeatherFeed::onDNSResolved(%this)\n" + "{\n" + " // Store this state\n" + " %this.lastState = \"DNSResolved\";\n\n" + "}\n\n" + + "function WeatherFeed::onConnected(%this)\n" + "{\n" + " // Store this state\n" + " %this.lastState = \"Connected\";\n\n" + + " // Clear our buffer\n" + " %this.buffer = \"\";\n" + "}\n\n" + + "function WeatherFeed::onDisconnect(%this)\n" + "{\n" + " // Store this state\n" + " %this.lastState = \"Disconnected\";\n\n" + + " // Output the buffer to the console\n" + " echo(\"Google Weather Results:\");\n" + " echo(%this.buffer);\n" + "}\n\n" + + "// Handle a line from the server\n" + "function WeatherFeed::onLine(%this, %line)\n" + "{\n" + " // Store this line in out buffer\n" + " %this.buffer = %this.buffer @ %line;\n" + "}\n\n" + + "// Create the HTTPObject\n" + "%feed = new HTTPObject(WeatherFeed);\n\n" + + "// Define a dynamic field to store the last connection state\n" + "%feed.lastState = \"None\";\n\n" + + "// Send the GET command\n" + "%feed.get(\"www.google.com:80\", \"/ig/api\", \"weather=Las-Vegas,US\");\n" + "@endtsexample\n\n" + + "@see TCPObject\n" + + "@ingroup Networking\n" +); + +//-------------------------------------- + +HTTPObject::HTTPObject() +{ + mHostName = 0; + mPath = 0; + mQuery = 0; + mPost = 0; + mBufferSave = 0; +} + +HTTPObject::~HTTPObject() +{ + dFree(mHostName); + dFree(mPath); + dFree(mQuery); + dFree(mPost); + + mHostName = 0; + mPath = 0; + mQuery = 0; + mPost = 0; + dFree(mBufferSave); +} + +//-------------------------------------- +//-------------------------------------- +void HTTPObject::get(const char *host, const char *path, const char *query) +{ + if(mHostName) + dFree(mHostName); + if(mPath) + dFree(mPath); + if(mQuery) + dFree(mQuery); + if(mPost) + dFree(mPost); + if(mBufferSave) + dFree(mBufferSave); + + mBufferSave = 0; + mHostName = dStrdup(host); + mPath = dStrdup(path); + if(query) + mQuery = dStrdup(query); + else + mQuery = NULL; + mPost = NULL; + + connect(host); +} + +void HTTPObject::post(const char *host, const char *path, const char *query, const char *post) +{ + if(mHostName) + dFree(mHostName); + if(mPath) + dFree(mPath); + if(mQuery) + dFree(mQuery); + if(mPost) + dFree(mPost); + if(mBufferSave) + dFree(mBufferSave); + + mBufferSave = 0; + mHostName = dStrdup(host); + mPath = dStrdup(path); + if(query && query[0]) + mQuery = dStrdup(query); + else + mQuery = NULL; + mPost = dStrdup(post); + connect(host); +} + +static char getHex(char c) +{ + if(c <= 9) + return c + '0'; + return c - 10 + 'A'; +} + +static S32 getHexVal(char c) +{ + if(c >= '0' && c <= '9') + return c - '0'; + else if(c >= 'A' && c <= 'Z') + return c - 'A' + 10; + else if(c >= 'a' && c <= 'z') + return c - 'a' + 10; + return -1; +} + +void HTTPObject::expandPath(char *dest, const char *path, U32 destSize) +{ + static bool asciiEscapeTableBuilt = false; + static bool asciiEscapeTable[256]; + if(!asciiEscapeTableBuilt) + { + asciiEscapeTableBuilt = true; + U32 i; + for(i = 0; i <= ' '; i++) + asciiEscapeTable[i] = true; + for(;i <= 0x7F; i++) + asciiEscapeTable[i] = false; + for(;i <= 0xFF; i++) + asciiEscapeTable[i] = true; + asciiEscapeTable[static_cast('\"')] = true; + asciiEscapeTable[static_cast('_')] = true; + asciiEscapeTable[static_cast('\'')] = true; + asciiEscapeTable[static_cast('#')] = true; + asciiEscapeTable[static_cast('$')] = true; + asciiEscapeTable[static_cast('%')] = true; + asciiEscapeTable[static_cast('&')] = false; + asciiEscapeTable[static_cast('+')] = true; + asciiEscapeTable[static_cast('-')] = true; + asciiEscapeTable[static_cast('~')] = true; + } + + U32 destIndex = 0; + U32 srcIndex = 0; + while(path[srcIndex] && destIndex < destSize - 3) + { + char c = path[srcIndex++]; + if(asciiEscapeTable[static_cast(c)]) + { + dest[destIndex++] = '%'; + dest[destIndex++] = getHex((c >> 4) & 0xF); + dest[destIndex++] = getHex(c & 0xF); + } + else + dest[destIndex++] = c; + } + dest[destIndex] = 0; +} + +//-------------------------------------- +void HTTPObject::onConnected() +{ + Parent::onConnected(); + char expPath[8192]; + char buffer[8192]; + + if(mQuery) + { + dSprintf(buffer, sizeof(buffer), "%s?%s", mPath, mQuery); + expandPath(expPath, buffer, sizeof(expPath)); + } + else + expandPath(expPath, mPath, sizeof(expPath)); + + char *pt = dStrchr(mHostName, ':'); + if(pt) + *pt = 0; + dSprintf(buffer, sizeof(buffer), "GET %s HTTP/1.1\r\nHost: %s\r\n\r\n", expPath, mHostName); + if(pt) + *pt = ':'; + + send((U8*)buffer, dStrlen(buffer)); + mParseState = ParsingStatusLine; + mChunkedEncoding = false; +} + +void HTTPObject::onConnectFailed() +{ + dFree(mHostName); + dFree(mPath); + dFree(mQuery); + mHostName = 0; + mPath = 0; + mQuery = 0; + Parent::onConnectFailed(); +} + + +void HTTPObject::onDisconnect() +{ + dFree(mHostName); + dFree(mPath); + dFree(mQuery); + mHostName = 0; + mPath = 0; + mQuery = 0; + Parent::onDisconnect(); +} + +bool HTTPObject::processLine(U8 *line) +{ + if(mParseState == ParsingStatusLine) + { + mParseState = ParsingHeader; + } + else if(mParseState == ParsingHeader) + { + if(!dStricmp((char *) line, "transfer-encoding: chunked")) + mChunkedEncoding = true; + if(line[0] == 0) + { + if(mChunkedEncoding) + mParseState = ParsingChunkHeader; + else + mParseState = ProcessingBody; + return true; + } + } + else if(mParseState == ParsingChunkHeader) + { + if(line[0]) // strip off the crlf if necessary + { + mChunkSize = 0; + S32 hexVal; + while((hexVal = getHexVal(*line++)) != -1) + { + mChunkSize *= 16; + mChunkSize += hexVal; + } + if(mBufferSave) + { + mBuffer = mBufferSave; + mBufferSize = mBufferSaveSize; + mBufferSave = 0; + } + if(mChunkSize) + mParseState = ProcessingBody; + else + { + mParseState = ProcessingDone; + finishLastLine(); + } + } + } + else + { + return Parent::processLine((UTF8*)line); + } + return true; +} + +U32 HTTPObject::onDataReceive(U8 *buffer, U32 bufferLen) +{ + U32 start = 0; + parseLine(buffer, &start, bufferLen); + return start; +} + +//-------------------------------------- +U32 HTTPObject::onReceive(U8 *buffer, U32 bufferLen) +{ + if(mParseState == ProcessingBody) + { + if(mChunkedEncoding && bufferLen >= mChunkSize) + { + U32 ret = onDataReceive(buffer, mChunkSize); + mChunkSize -= ret; + if(mChunkSize == 0) + { + if(mBuffer) + { + mBufferSaveSize = mBufferSize; + mBufferSave = mBuffer; + mBuffer = 0; + mBufferSize = 0; + } + mParseState = ParsingChunkHeader; + } + return ret; + } + else + { + U32 ret = onDataReceive(buffer, bufferLen); + mChunkSize -= ret; + return ret; + } + } + else if(mParseState != ProcessingDone) + { + U32 start = 0; + parseLine(buffer, &start, bufferLen); + return start; + } + return bufferLen; +} + +//-------------------------------------- +DefineEngineMethod( HTTPObject, get, void, ( const char* Address, const char* requirstURI, const char* query ), ( "" ), + "@brief Send a GET command to a server to send or retrieve data.\n\n" + + "@param Address HTTP web address to send this get call to. Be sure to include the port at the end (IE: \"www.garagegames.com:80\").\n" + "@param requirstURI Specific location on the server to access (IE: \"index.php\".)\n" + "@param query Optional. Actual data to transmit to the server. Can be anything required providing it sticks with limitations of the HTTP protocol. " + "If you were building the URL manually, this is the text that follows the question mark. For example: http://www.google.com/ig/api?weather=Las-Vegas,US\n" + + "@tsexample\n" + "// Create an HTTP object for communications\n" + "%httpObj = new HTTPObject();\n\n" + "// Specify a URL to transmit to\n" + "%url = \"www.garagegames.com:80\";\n\n" + "// Specify a URI to communicate with\n" + "%URI = \"/index.php\";\n\n" + "// Specify a query to send.\n" + "%query = \"\";\n\n" + "// Send the GET command to the server\n" + "%httpObj.get(%url,%URI,%query);\n" + "@endtsexample\n\n" + ) +{ + if( !query || !query[ 0 ] ) + object->get(Address, requirstURI, NULL); + else + object->get(Address, requirstURI, query); +} + +DefineEngineMethod( HTTPObject, post, void, ( const char* Address, const char* requirstURI, const char* query, const char* post ),, + "@brief Send POST command to a server to send or retrieve data.\n\n" + + "@param Address HTTP web address to send this get call to. Be sure to include the port at the end (IE: \"www.garagegames.com:80\").\n" + "@param requirstURI Specific location on the server to access (IE: \"index.php\".)\n" + "@param query Actual data to transmit to the server. Can be anything required providing it sticks with limitations of the HTTP protocol. \n" + "@param post Submission data to be processed.\n" + + "@note The post() method is currently non-functional.\n" + + "@tsexample\n" + "// Create an HTTP object for communications\n" + "%httpObj = new HTTPObject();\n\n" + "// Specify a URL to transmit to\n" + "%url = \"www.garagegames.com:80\";\n\n" + "// Specify a URI to communicate with\n" + "%URI = \"/index.php\";\n\n" + "// Specify a query to send.\n" + "%query = \"\";\n\n" + "// Specify the submission data.\n" + "%post = \"\";\n\n" + "// Send the POST command to the server\n" + "%httpObj.POST(%url,%URI,%query,%post);\n" + "@endtsexample\n\n" + ) +{ + object->post(Address, requirstURI, query, post); +} diff --git a/Engine/source/app/net/httpObject.h b/Engine/source/app/net/httpObject.h new file mode 100644 index 000000000..76647401a --- /dev/null +++ b/Engine/source/app/net/httpObject.h @@ -0,0 +1,81 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _HTTPOBJECT_H_ +#define _HTTPOBJECT_H_ + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _TCPOBJECT_H_ +#include "app/net/tcpObject.h" +#endif + +class HTTPObject : public TCPObject +{ +private: + typedef TCPObject Parent; +protected: + enum ParseState { + ParsingStatusLine, + ParsingHeader, + ParsingChunkHeader, + ProcessingBody, + ProcessingDone, + }; + ParseState mParseState; + U32 mTotalBytes; + U32 mBytesRemaining; + public: + U32 mStatus; + F32 mVersion; + U32 mContentLength; + bool mChunkedEncoding; + U32 mChunkSize; + const char *mContentType; + char *mHostName; + char *mPath; + char *mQuery; + char *mPost; + U8 *mBufferSave; + U32 mBufferSaveSize; +public: + static void expandPath(char *dest, const char *path, U32 destSize); + void get(const char *hostName, const char *urlName, const char *query); + void post(const char *host, const char *path, const char *query, const char *post); + HTTPObject(); + ~HTTPObject(); + + //static HTTPObject *find(U32 tag); + + virtual U32 onDataReceive(U8 *buffer, U32 bufferLen); + virtual U32 onReceive(U8 *buffer, U32 bufferLen); // process a buffer of raw packet data + virtual void onConnected(); + virtual void onConnectFailed(); + virtual void onDisconnect(); + bool processLine(U8 *line); + + DECLARE_CONOBJECT(HTTPObject); +}; + + +#endif // _H_HTTPOBJECT_ diff --git a/Engine/source/app/net/net.cpp b/Engine/source/app/net/net.cpp new file mode 100644 index 000000000..7c5f47a0d --- /dev/null +++ b/Engine/source/app/net/net.cpp @@ -0,0 +1,417 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/dnet.h" +#include "core/idGenerator.h" +#include "core/stream/bitStream.h" +#include "console/simBase.h" +#include "console/console.h" +#include "console/consoleTypes.h" +#include "sim/netConnection.h" +#include "sim/netObject.h" +#include "app/net/serverQuery.h" +#include "console/engineAPI.h" + +//---------------------------------------------------------------- +// remote procedure call console functions +//---------------------------------------------------------------- + +class RemoteCommandEvent : public NetEvent +{ +public: + typedef NetEvent Parent; + enum { + MaxRemoteCommandArgs = 20, + CommandArgsBits = 5 + }; + +private: + S32 mArgc; + char *mArgv[MaxRemoteCommandArgs + 1]; + NetStringHandle mTagv[MaxRemoteCommandArgs + 1]; + static char mBuf[1024]; +public: + RemoteCommandEvent(S32 argc=0, const char **argv=NULL, NetConnection *conn = NULL) + { + mArgc = argc; + for(S32 i = 0; i < argc; i++) + { + if(argv[i][0] == StringTagPrefixByte) + { + char buffer[256]; + mTagv[i+1] = NetStringHandle(dAtoi(argv[i]+1)); + if(conn) + { + dSprintf(buffer + 1, sizeof(buffer) - 1, "%d", conn->getNetSendId(mTagv[i+1])); + buffer[0] = StringTagPrefixByte; + mArgv[i+1] = dStrdup(buffer); + } + } + else + mArgv[i+1] = dStrdup(argv[i]); + } + } + +#ifdef TORQUE_DEBUG_NET + const char *getDebugName() + { + static char buffer[256]; + dSprintf(buffer, sizeof(buffer), "%s [%s]", getClassName(), mTagv[1].isValidString() ? mTagv[1].getString() : "--unknown--" ); + return buffer; + } +#endif + + ~RemoteCommandEvent() + { + for(S32 i = 0; i < mArgc; i++) + dFree(mArgv[i+1]); + } + + virtual void pack(NetConnection* conn, BitStream *bstream) + { + bstream->writeInt(mArgc, CommandArgsBits); + // write it out reversed... why? + // automatic string substitution with later arguments - + // handled automatically by the system. + + for(S32 i = 0; i < mArgc; i++) + conn->packString(bstream, mArgv[i+1]); + } + + virtual void write(NetConnection* conn, BitStream *bstream) + { + pack(conn, bstream); + } + + virtual void unpack(NetConnection* conn, BitStream *bstream) + { + + mArgc = bstream->readInt(CommandArgsBits); + // read it out backwards + for(S32 i = 0; i < mArgc; i++) + { + conn->unpackString(bstream, mBuf); + mArgv[i+1] = dStrdup(mBuf); + } + } + + virtual void process(NetConnection *conn) + { + static char idBuf[10]; + + // de-tag the command name + + for(S32 i = mArgc - 1; i >= 0; i--) + { + char *arg = mArgv[i+1]; + if(*arg == StringTagPrefixByte) + { + // it's a tag: + U32 localTag = dAtoi(arg + 1); + NetStringHandle tag = conn->translateRemoteStringId(localTag); + NetStringTable::expandString( tag, + mBuf, + sizeof(mBuf), + (mArgc - 1) - i, + (const char**)(mArgv + i + 2) ); + dFree(mArgv[i+1]); + mArgv[i+1] = dStrdup(mBuf); + } + } + const char *rmtCommandName = dStrchr(mArgv[1], ' ') + 1; + if(conn->isConnectionToServer()) + { + dStrcpy(mBuf, "clientCmd"); + dStrcat(mBuf, rmtCommandName); + + char *temp = mArgv[1]; + mArgv[1] = mBuf; + + Con::execute(mArgc, (const char **) mArgv+1); + mArgv[1] = temp; + } + else + { + dStrcpy(mBuf, "serverCmd"); + dStrcat(mBuf, rmtCommandName); + char *temp = mArgv[1]; + + dSprintf(idBuf, sizeof(idBuf), "%d", conn->getId()); + mArgv[0] = mBuf; + mArgv[1] = idBuf; + + Con::execute(mArgc+1, (const char **) mArgv); + mArgv[1] = temp; + } + } + + DECLARE_CONOBJECT(RemoteCommandEvent); +}; +char RemoteCommandEvent::mBuf[1024]; + +IMPLEMENT_CO_NETEVENT_V1(RemoteCommandEvent); + +ConsoleDocClass( RemoteCommandEvent, + "@brief Object used for remote procedure calls.\n\n" + "Not intended for game development, for exposing ConsoleFunctions (such as commandToClient) only.\n\n" + "@internal"); + +static void sendRemoteCommand(NetConnection *conn, S32 argc, const char **argv) +{ + if(U8(argv[0][0]) != StringTagPrefixByte) + { + Con::errorf(ConsoleLogEntry::Script, "Remote Command Error - command must be a tag."); + return; + } + S32 i; + for(i = argc - 1; i >= 0; i--) + { + if(argv[i][0] != 0) + break; + argc = i; + } + for(i = 0; i < argc; i++) + conn->validateSendString(argv[i]); + RemoteCommandEvent *cevt = new RemoteCommandEvent(argc, argv, conn); + conn->postNetEvent(cevt); +} + +ConsoleFunctionGroupBegin( Net, "Functions for use with the network; tagged strings and remote commands."); + +ConsoleFunction( commandToServer, void, 2, RemoteCommandEvent::MaxRemoteCommandArgs + 1, "(string func, ...)" + "@brief Send a command to the server.\n\n" + + "@param func Name of the server command being called\n" + "@param ... Various parameters being passed to server command\n\n" + + "@tsexample\n" + "// Create a standard function. Needs to be executed on the client, such \n" + "// as within scripts/client/default.bind.cs\n" + "function toggleCamera(%val)\n" + "{\n" + " // If key was down, call a server command named 'ToggleCamera'\n" + " if (%val)\n" + " commandToServer('ToggleCamera');\n" + "}\n\n" + "// Server command being called from above. Needs to be executed on the \n" + "// server, such as within scripts/server/commands.cs\n" + "function serverCmdToggleCamera(%client)\n" + "{\n" + " if (%client.getControlObject() == %client.player)\n" + " {\n" + " %client.camera.setVelocity(\"0 0 0\");\n" + " %control = %client.camera;\n" + " }\n" + " else\n" + " {\n" + " %client.player.setVelocity(\"0 0 0\");\n" + " %control = %client.player;\n" + " }\n" + " %client.setControlObject(%control);\n" + " clientCmdSyncEditorGui();\n" + "}\n" + "@endtsexample\n\n" + + "@ingroup Networking") +{ + NetConnection *conn = NetConnection::getConnectionToServer(); + if(!conn) + return; + sendRemoteCommand(conn, argc - 1, argv + 1); +} + +ConsoleFunction( commandToClient, void, 3, RemoteCommandEvent::MaxRemoteCommandArgs + 2, "(NetConnection client, string func, ...)" + "@brief Send a command from the server to the client\n\n" + + "@param client The numeric ID of a client GameConnection\n" + "@param func Name of the client function being called\n" + "@param ... Various parameters being passed to client command\n\n" + + "@tsexample\n" + "// Set up the client command. Needs to be executed on the client, such as\n" + "// within scripts/client/client.cs\n" + "// Update the Ammo Counter with current ammo, if not any then hide the counter.\n" + "function clientCmdSetAmmoAmountHud(%amount)\n" + "{\n" + " if (!%amount)\n" + " AmmoAmount.setVisible(false);\n" + " else\n" + " {\n" + " AmmoAmount.setVisible(true);\n" + " AmmoAmount.setText(\"Ammo: \"@%amount);\n" + " }\n" + "}\n\n" + "// Call it from a server function. Needs to be executed on the server, \n" + "//such as within scripts/server/game.cs\n" + "function GameConnection::setAmmoAmountHud(%client, %amount)\n" + "{\n" + " commandToClient(%client, 'SetAmmoAmountHud', %amount);\n" + "}\n" + "@endtsexample\n\n" + + "@ingroup Networking\n") +{ + NetConnection *conn; + if(!Sim::findObject(argv[1], conn)) + return; + sendRemoteCommand(conn, argc - 2, argv + 2); +} + + +ConsoleFunction(removeTaggedString, void, 2, 2, "(int tag)" + "@brief Remove a tagged string from the Net String Table\n\n" + + "@param tag The tag associated with the string\n\n" + + "@see \\ref syntaxDataTypes under Tagged %Strings\n" + "@see addTaggedString()\n" + "@see getTaggedString()\n" + "@ingroup Networking\n") +{ + gNetStringTable->removeString(dAtoi(argv[1]+1), true); +} + +ConsoleFunction( addTaggedString, const char*, 2, 2, "(string str)" + "@brief Use the addTaggedString function to tag a new string and add it to the NetStringTable\n\n" + + "@param str The string to be tagged and placed in the NetStringTable. Tagging ignores case, " + "so tagging the same string (excluding case differences) will be ignored as a duplicated tag.\n\n" + + "@return Returns a string( containing a numeric value) equivalent to the string ID for the newly tagged string" + + "@see \\ref syntaxDataTypes under Tagged %Strings\n" + "@see removeTaggedString()\n" + "@see getTaggedString()\n" + "@ingroup Networking\n") +{ + NetStringHandle s(argv[1]); + gNetStringTable->incStringRefScript(s.getIndex()); + + char *ret = Con::getReturnBuffer(10); + ret[0] = StringTagPrefixByte; + dSprintf(ret + 1, 9, "%d", s.getIndex()); + return ret; +} + +ConsoleFunction( getTaggedString, const char*, 2, 2, "(int tag)" + "@brief Use the getTaggedString function to convert a tag to a string.\n\n" + + "This is not the same as detag() which can only be used within the context " + "of a function that receives a tag. This function can be used any time and " + "anywhere to convert a tag to a string.\n\n" + + "@param tag A numeric tag ID.\n" + + "@returns The string as found in the Net String table.\n" + + "@see \\ref syntaxDataTypes under Tagged %Strings\n" + "@see addTaggedString()\n" + "@see removeTaggedString()\n" + "@ingroup Networking\n") +{ + const char *indexPtr = argv[1]; + if (*indexPtr == StringTagPrefixByte) + indexPtr++; + return gNetStringTable->lookupString(dAtoi(indexPtr)); +} + +ConsoleFunction( buildTaggedString, const char*, 2, 11, "(string format, ...)" + "@brief Build a string using the specified tagged string format.\n\n" + + "This function takes an already tagged string (passed in as a tagged string ID) and one " + "or more additional strings. If the tagged string contains argument tags that range from " + "%%1 through %%9, then each additional string will be substituted into the tagged string. " + "The final (non-tagged) combined string will be returned. The maximum length of the tagged " + "string plus any inserted additional strings is 511 characters.\n\n" + + "@param format A tagged string ID that contains zero or more argument tags, in the form of " + "%%1 through %%9.\n" + "@param ... A variable number of arguments that are insterted into the tagged string " + "based on the argument tags within the format string.\n" + + "@returns An ordinary string that is a combination of the original tagged string with any additional " + "strings passed in inserted in place of each argument tag.\n" + + "@tsexample\n" + "// Create a tagged string with argument tags\n" + "%taggedStringID = addTaggedString(\"Welcome %1 to the game!\");\n\n" + + "// Some point later, combine the tagged string with some other string\n" + "%string = buildTaggedString(%taggedStringID, %playerName);\n" + "echo(%string);\n" + "@endtsexample\n\n" + + "@see \\ref syntaxDataTypes under Tagged %Strings\n" + "@see addTaggedString()\n" + "@see getTaggedString()\n" + "@ingroup Networking\n") +{ + const char *indexPtr = argv[1]; + if (*indexPtr == StringTagPrefixByte) + indexPtr++; + const char *fmtString = gNetStringTable->lookupString(dAtoi(indexPtr)); + char *strBuffer = Con::getReturnBuffer(512); + const char *fmtStrPtr = fmtString; + char *strBufPtr = strBuffer; + S32 strMaxLength = 511; + if (!fmtString) + goto done; + + //build the string + while (*fmtStrPtr) + { + //look for an argument tag + if (*fmtStrPtr == '%') + { + if (fmtStrPtr[1] >= '1' && fmtStrPtr[1] <= '9') + { + S32 argIndex = S32(fmtStrPtr[1] - '0') + 1; + if (argIndex >= argc) + goto done; + const char *argStr = argv[argIndex]; + if (!argStr) + goto done; + S32 strLength = dStrlen(argStr); + if (strLength > strMaxLength) + goto done; + dStrcpy(strBufPtr, argStr); + strBufPtr += strLength; + strMaxLength -= strLength; + fmtStrPtr += 2; + continue; + } + } + + //if we don't continue, just copy the character + if (strMaxLength <= 0) + goto done; + *strBufPtr++ = *fmtStrPtr++; + strMaxLength--; + } + +done: + *strBufPtr = '\0'; + return strBuffer; +} + +ConsoleFunctionGroupEnd( Net ); diff --git a/Engine/source/app/net/netTest.cpp b/Engine/source/app/net/netTest.cpp new file mode 100644 index 000000000..e9b172a71 --- /dev/null +++ b/Engine/source/app/net/netTest.cpp @@ -0,0 +1,192 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/simBase.h" +#include "platform/event.h" +#include "sim/netConnection.h" +#include "core/stream/bitStream.h" +#include "sim/netObject.h" +#include "console/engineAPI.h" + +class SimpleMessageEvent : public NetEvent +{ + char *msg; +public: + typedef NetEvent Parent; + SimpleMessageEvent(const char *message = NULL) + { + if(message) + msg = dStrdup(message); + else + msg = NULL; + } + ~SimpleMessageEvent() + { dFree(msg); } + + virtual void pack(NetConnection* /*ps*/, BitStream *bstream) + { bstream->writeString(msg); } + virtual void write(NetConnection*, BitStream *bstream) + { bstream->writeString(msg); } + virtual void unpack(NetConnection* /*ps*/, BitStream *bstream) + { char buf[256]; bstream->readString(buf); msg = dStrdup(buf); } + virtual void process(NetConnection *) + { Con::printf("RMSG %d %s", mSourceId, msg); } + + DECLARE_CONOBJECT(SimpleMessageEvent); +}; + +IMPLEMENT_CO_NETEVENT_V1(SimpleMessageEvent); + +ConsoleDocClass( SimpleMessageEvent, + "@brief A very simple example of a network event.\n\n" + + "This object exists purely for instructional purposes. It is primarily " + "geared toward developers that wish to understand the inner-working of " + "Torque 3D's networking system. This is not intended for actual game " + "development.\n\n " + + "@see NetEvent for the inner workings of network events\n\n" + + "@ingroup Networking\n"); + +DefineEngineStaticMethod( SimpleMessageEvent, msg, void, (NetConnection* con, const char* message),, + "@brief Send a SimpleMessageEvent message to the specified connection.\n\n" + + "The far end that receives the message will print the message out to the console.\n" + + "@param con The unique ID of the connection to transmit to\n" + "@param message The string containing the message to transmit\n\n" + + "@tsexample\n" + "// Send a message to the other end of the given NetConnection\n" + "SimpleMessageEvent::msg( %conn, \"A message from me!\");\n\n" + + "// The far end will see the following in the console\n" + "// (Note: The number may be something other than 1796 as it is the SimObjectID\n" + "// of the received event)\n" + "// \n" + "// RMSG 1796 A message from me!\n" + "@endtsexample\n\n" + ) +{ + //NetConnection *con = (NetConnection *) Sim::findObject(argv[1]); + + if(con) + con->postNetEvent(new SimpleMessageEvent(message)); +} + +//ConsoleFunction( msg, void, 3, 3, "(NetConnection id, string message)" +// "Send a SimpleNetObject message to the specified connection.") +//{ +// NetConnection *con = (NetConnection *) Sim::findObject(argv[1]); +// if(con) +// con->postNetEvent(new SimpleMessageEvent(argv[2])); +//} + +class SimpleNetObject : public NetObject +{ + typedef NetObject Parent; +public: + char message[256]; + SimpleNetObject() + { + mNetFlags.set(ScopeAlways | Ghostable); + dStrcpy(message, "Hello World!"); + } + U32 packUpdate(NetConnection *conn, U32 mask, BitStream *stream) + { + stream->writeString(message); + return 0; + } + void unpackUpdate(NetConnection *conn, BitStream *stream) + { + stream->readString(message); + Con::printf("Got message: %s", message); + } + void setMessage(const char *msg) + { + setMaskBits(1); + dStrcpy(message, msg); + } + + DECLARE_CONOBJECT(SimpleNetObject); +}; + +IMPLEMENT_CO_NETOBJECT_V1(SimpleNetObject); + +ConsoleDocClass( SimpleNetObject, + "@brief A very simple example of a class derived from NetObject.\n\n" + + "This object exists purely for instructional purposes. It is primarily " + "geared toward developers that wish to understand the inner-working of " + "Torque 3D's networking system. This is not intended for actual game " + "development.\n\n " + + "@tsexample\n" + "// On the server, create a new SimpleNetObject. This is a ghost always\n" + "// object so it will be immediately ghosted to all connected clients.\n" + "$s = new SimpleNetObject();\n\n" + + "// All connected clients will see the following in their console:\n" + "// \n" + "// Got message: Hello World!\n" + "@endtsexample\n\n" + + "@see NetObject for a full breakdown of this example object\n" + + "@ingroup Networking\n"); + +DefineEngineMethod( SimpleNetObject, setMessage, void, (const char* msg),, + "@brief Sets the internal message variable.\n\n" + + "SimpleNetObject is set up to automatically transmit this new message to " + "all connected clients. It will appear in the clients' console.\n" + + "@param msg The new message to send\n\n" + + "@tsexample\n" + "// On the server, create a new SimpleNetObject. This is a ghost always\n" + "// object so it will be immediately ghosted to all connected clients.\n" + "$s = new SimpleNetObject();\n\n" + + "// All connected clients will see the following in their console:\n" + "// \n" + "// Got message: Hello World!\n\n" + + "// Now again on the server, change the message. This will cause it to\n" + "// be sent to all connected clients.\n" + "$s.setMessage(\"A new message from me!\");\n\n" + + "// All connected clients will now see in their console:\n" + "// \n" + "// Go message: A new message from me!\n" + "@endtsexample\n\n" + ) +{ + object->setMessage(msg); +} + +//ConsoleMethod( SimpleNetObject, setMessage, void, 3, 3, "(string msg)") +//{ +// object->setMessage(argv[2]); +//} diff --git a/Engine/source/app/net/serverQuery.cpp b/Engine/source/app/net/serverQuery.cpp new file mode 100644 index 000000000..cf3dc8bf8 --- /dev/null +++ b/Engine/source/app/net/serverQuery.cpp @@ -0,0 +1,2139 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Server Query States: +// 1: Master server query status -> wait for master response +// 2: Master server packet status -> wait for master packets to arrive +// 3: Server ping status -> wait for servers to respond to pings +// 4: Server query status -> wait for servers to respond to queries +// 5: Done + +// Master Server Packets: +// Header +// message Message id +// flags Query flags +// sequenceNumber Packet sequence id + +// Server Query Filter Packet +// packetIndex Request specific page # (rest empty) +// gameType Game type string +// missionType Mission type string +// minPlayers At least this many players +// maxPlayers No more than this many +// regions Region mask, 0 = all +// version Server version, 0 = any +// filterFlags Server flags (dedicated, etc), 0 = any +// maxBots No more than maxBots +// minCPUSpeed At least this fast +// playerCount Buddy list search +// playerList[playerCount] + +// Master Server Info Packet +// gameType Game type string +// missionType Mission type string +// maxPlayers Max allowed +// regions Region mask +// version Server version # +// infoFlags Server flags (dedicated, etc) +// numBots Current bot count +// CPUSpeed Server CPU speed +// playerCount Current player count +// playerList[playerCount] + +// Game Info Query Packet +// gameType Game type string +// missionType Mission type string +// missionName You get one guess... +// satusFlags Dedicated, etc. +// playerCount Current player count +// maxPlayers Max allowed +// numBots Current bot count +// CPUSpeed Server CPU speed +// statusString Server info message +// statusString Server status message + +// Accessed Environment Vars +// Server::MissionType +// Server::MissionName +// Server::GameType +// Server::ServerType +// Server::PlayerCount +// Server::BotCount +// Server::GuidList[playerCount] +// Server::Dedicated +// Server::Status +// Pref::Server::Name +// Pref::Server::Password +// Pref::Server::Info +// Pref::Server::MaxPlayers +// Pref::Server::RegionMask +// Pref::Net::RegionMask +// Pref::Client::Master[n] +// Pref::Client::ServerFavoriteCount +// Pref::Client::ServerFavorite[ServerFavoriteCount] +//----------------------------------------------------------------------------- + +#include "app/net/serverQuery.h" + +#include "platform/platform.h" +#include "platform/event.h" +#include "core/dnet.h" +#include "core/util/tVector.h" +#include "core/stream/bitStream.h" +#include "console/console.h" +#include "console/simBase.h" +#include "app/banList.h" +#include "app/auth.h" +#include "sim/netConnection.h" +#include "sim/netInterface.h" + +// cafTODO: breaks T2D +#include "T3D/gameBase/gameConnection.h" + +// This is basically the server query protocol version now: +static const char* versionString = "VER1"; + +struct MasterInfo +{ + NetAddress address; + U32 region; +}; + +Vector gServerList(__FILE__, __LINE__); +static Vector gMasterServerList(__FILE__, __LINE__); +static Vector gFinishedList(__FILE__, __LINE__); // timed out servers and finished servers go here +NetAddress gMasterServerQueryAddress; +bool gServerBrowserDirty = false; + +static const U32 gHeartbeatInterval = 120000; +static const S32 gMasterServerRetryCount = 3; +static const S32 gMasterServerTimeout = 2000; +static const S32 gPacketRetryCount = 4; +static const S32 gPacketTimeout = 1000; +static const S32 gMaxConcurrentPings = 10; +static const S32 gMaxConcurrentQueries = 2; +static const S32 gPingRetryCount = 4; +static const S32 gPingTimeout = 800; +static const S32 gQueryRetryCount = 4; +static const S32 gQueryTimeout = 1000; + +// State variables: +static bool sgServerQueryActive = false; +static S32 gPingSession = 0; +static S32 gKey = 0; +static bool gGotFirstListPacket = false; + +// Variables used for the interface: +static U32 gServerPingCount = 0; +static U32 gServerQueryCount = 0; +static U32 gHeartbeatSeq = 0; + +class DemoNetInterface : public NetInterface +{ +public: + void handleInfoPacket(const NetAddress *address, U8 packetType, BitStream *stream); +}; + +DemoNetInterface gNetInterface; + +ConsoleFunctionGroupBegin( ServerQuery, "Functions which allow you to query the LAN or a master server for online games."); + +//----------------------------------------------------------------------------- + +struct Ping +{ + NetAddress address; + S32 session; + S32 key; + U32 time; + U32 tryCount; + bool broadcast; +}; + +static Ping gMasterServerPing; +static Vector gPingList(__FILE__, __LINE__); +static Vector gQueryList(__FILE__, __LINE__); + +//----------------------------------------------------------------------------- + +struct PacketStatus +{ + U8 index; + S32 key; + U32 time; + U32 tryCount; + + PacketStatus() : index( 0 ), key(-1), time(0), tryCount( gPacketRetryCount ) {}; + + PacketStatus( U8 _index, S32 _key, U32 _time ) + { + index = _index; + key = _key; + time = _time; + tryCount = gPacketRetryCount; + } +}; + +static Vector gPacketStatusList(__FILE__, __LINE__); + +//----------------------------------------------------------------------------- + +struct ServerFilter +{ + enum Type + { + Normal = 0, + Buddy = 1, + Offline = 2, + Favorites = 3, + }; + + Type type; + char* gameType; + char* missionType; + + enum // Query Flags + { + OnlineQuery = 0, // Authenticated with master + OfflineQuery = BIT(0), // On our own + NoStringCompress = BIT(1), + }; + + enum // Filter flags: + { + Dedicated = BIT(0), + NotPassworded = BIT(1), + Linux = BIT(2), + CurrentVersion = BIT(7), + NotXenon = BIT(6) + }; + + U8 queryFlags; + U8 minPlayers; + U8 maxPlayers; + U8 maxBots; + U32 regionMask; + U32 maxPing; + U8 filterFlags; + U16 minCPU; + U8 buddyCount; + U32* buddyList; + + ServerFilter() + { + queryFlags = 0; + gameType = NULL; + missionType = NULL; + minPlayers = 0; + maxPlayers = 255; + maxBots = 16; + regionMask = 0xFFFFFFFF; + maxPing = 0; + filterFlags = 0; + minCPU = 0; + buddyCount = 0; + buddyList = NULL; + } + + ~ServerFilter() + { + if ( gameType ) + dFree( gameType ); + if ( missionType ) + dFree( missionType ); + if ( buddyList ) + dFree( buddyList ); + } +}; + +static ServerFilter sActiveFilter; + + +//----------------------------------------------------------------------------- +// Forward function declarations: +//----------------------------------------------------------------------------- + +static void pushPingRequest( const NetAddress *addr ); +static void pushPingBroadcast( const NetAddress *addr ); +static void pushServerFavorites(); +static bool pickMasterServer(); +static S32 findPingEntry( Vector &v, const NetAddress* addr ); +static bool addressFinished( const NetAddress* addr ); +static ServerInfo* findServerInfo( const NetAddress* addr ); +static ServerInfo* findOrCreateServerInfo( const NetAddress* addr ); +static void removeServerInfo( const NetAddress* addr ); +static void sendPacket( U8 pType, const NetAddress* addr, U32 key, U32 session, U8 flags ); +static void writeCString( BitStream* stream, const char* string ); +static void readCString( BitStream* stream, char* buffer ); +static void writeLongCString( BitStream* stream, const char* string ); +static void readLongCString( BitStream* stream, char* buffer ); +static void processMasterServerQuery( U32 session ); +static void processPingsAndQueries( U32 session, bool schedule = true); +static void processServerListPackets( U32 session ); +static void processHeartbeat(U32); +static void updatePingProgress(); +static void updateQueryProgress(); +Vector* getMasterServerList(); +bool pickMasterServer(); +void clearServerList(); + + +//----------------------------------------------------------------------------- +// Events +//----------------------------------------------------------------------------- + +//---------------------------------------------------------------- +class ProcessMasterQueryEvent : public SimEvent +{ + U32 session; + public: + ProcessMasterQueryEvent( U32 _session ) + { + session = _session; + } + void process( SimObject *object ) + { + processMasterServerQuery( session ); + } +}; + +//---------------------------------------------------------------- +class ProcessPingEvent : public SimEvent +{ + U32 session; + public: + ProcessPingEvent( U32 _session ) + { + session = _session; + } + void process( SimObject *object ) + { + processPingsAndQueries( session ); + } +}; + +//---------------------------------------------------------------- +class ProcessPacketEvent : public SimEvent +{ + U32 session; + public: + ProcessPacketEvent( U32 _session ) + { + session = _session; + } + + void process( SimObject *object ) + { + processServerListPackets( session ); + } +}; + +//---------------------------------------------------------------- +class HeartbeatEvent : public SimEvent +{ + U32 mSeq; + public: + HeartbeatEvent(U32 seq) + { + mSeq = seq; + } + void process( SimObject *object ) + { + processHeartbeat(mSeq); + } +}; + + +//----------------------------------------------------------------------------- +// Public query methods +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- + +void queryLanServers(U32 port, U8 flags, const char* gameType, const char* missionType, + U8 minPlayers, U8 maxPlayers, U8 maxBots, U32 regionMask, U32 maxPing, U16 minCPU, + U8 filterFlags) +{ + sgServerQueryActive = true; + pushServerFavorites(); + + sActiveFilter.type = ServerFilter::Offline; + + // Clear the filter: + if ( !sActiveFilter.gameType || dStricmp( sActiveFilter.gameType, "Any" ) != 0 ) + { + sActiveFilter.gameType = (char*) dRealloc( sActiveFilter.gameType, 4 ); + dStrcpy( sActiveFilter.gameType, "Any" ); + } + if ( !sActiveFilter.missionType || dStricmp( sActiveFilter.missionType, "Any" ) != 0 ) + { + sActiveFilter.missionType = (char*) dRealloc( sActiveFilter.missionType, 4 ); + dStrcpy( sActiveFilter.missionType, "Any" ); + } + sActiveFilter.queryFlags = 0; + sActiveFilter.minPlayers = minPlayers; + sActiveFilter.maxPlayers = maxPlayers; + sActiveFilter.maxBots = maxBots; + sActiveFilter.regionMask = regionMask; + sActiveFilter.maxPing = maxPing; + sActiveFilter.minCPU = minCPU; + sActiveFilter.filterFlags = filterFlags; + + NetAddress addr; + char addrText[256]; + dSprintf( addrText, sizeof( addrText ), "IP:BROADCAST:%d", port ); + Net::stringToAddress( addrText, &addr ); + pushPingBroadcast( &addr ); + + Con::executef("onServerQueryStatus", "start", "Querying LAN servers", "0"); + processPingsAndQueries( gPingSession ); +} + +//----------------------------------------------------------------------------- + +ConsoleFunction( queryAllServers, void, 12, 12, "queryAllServers(...);" ) +{ + TORQUE_UNUSED(argc); + + U32 lanPort = dAtoi(argv[1]); + U8 flags = dAtoi(argv[2]); + + // It's not a good idea to hold onto args, recursive calls to + // console exec will trash them. + char* gameType = dStrdup(argv[3]); + char* missionType = dStrdup(argv[4]); + + U8 minPlayers = dAtoi(argv[5]); + U8 maxPlayers = dAtoi(argv[6]); + U8 maxBots = dAtoi(argv[7]); + U32 regionMask = dAtoi(argv[8]); + U32 maxPing = dAtoi(argv[9]); + U16 minCPU = dAtoi(argv[10]); + U8 filterFlags = dAtoi(argv[11]); + U32 buddyList = 0; + + clearServerList(); + + queryMasterServer(flags,gameType,missionType,minPlayers,maxPlayers, + maxBots,regionMask,maxPing,minCPU,filterFlags,0,&buddyList); + + queryLanServers(lanPort, flags, gameType, missionType, minPlayers, maxPlayers, maxBots, + regionMask, maxPing, minCPU, filterFlags); + dFree(gameType); + dFree(missionType); + +} +ConsoleFunction( queryLanServers, void, 12, 12, "queryLanServers(...);" ) +{ + TORQUE_UNUSED(argc); + + U32 lanPort = dAtoi(argv[1]); + U8 flags = dAtoi(argv[2]); + + // It's not a good idea to hold onto args, recursive calls to + // console exec will trash them. + char* gameType = dStrdup(argv[3]); + char* missionType = dStrdup(argv[4]); + + U8 minPlayers = dAtoi(argv[5]); + U8 maxPlayers = dAtoi(argv[6]); + U8 maxBots = dAtoi(argv[7]); + U32 regionMask = dAtoi(argv[8]); + U32 maxPing = dAtoi(argv[9]); + U16 minCPU = dAtoi(argv[10]); + U8 filterFlags = dAtoi(argv[11]); + + clearServerList(); + queryLanServers(lanPort, flags, gameType, missionType, minPlayers, maxPlayers, maxBots, + regionMask, maxPing, minCPU, filterFlags); + + dFree(gameType); + dFree(missionType); +} + +//----------------------------------------------------------------------------- + +void queryMasterGameTypes() +{ + Vector *masterList = getMasterServerList(); + if ( masterList->size() != 0 ) + { + U32 master = Sim::getCurrentTime() % masterList->size(); + // Send a request to the master server for the game types: + Con::printf( "Requesting game types from the master server..." ); + sendPacket( NetInterface::MasterServerGameTypesRequest, &(*masterList)[master].address, gKey, gPingSession, 0 ); + } +} + +//----------------------------------------------------------------------------- + +void queryMasterServer(U8 flags, const char* gameType, const char* missionType, + U8 minPlayers, U8 maxPlayers, U8 maxBots, U32 regionMask, U32 maxPing, + U16 minCPU, U8 filterFlags, U8 buddyCount, U32* buddyList ) +{ + // Reset the list packet flag: + gGotFirstListPacket = false; + sgServerQueryActive = true; + + Con::executef( "onServerQueryStatus", "start", "Querying master server", "0"); + + if ( buddyCount == 0 ) + { + sActiveFilter.type = ServerFilter::Normal; + + // Update the active filter: + if ( !sActiveFilter.gameType || dStrcmp( sActiveFilter.gameType, gameType ) != 0 ) + { + sActiveFilter.gameType = (char*) dRealloc( sActiveFilter.gameType, dStrlen( gameType ) + 1 ); + dStrcpy( sActiveFilter.gameType, gameType ); + } + + if ( !sActiveFilter.missionType || dStrcmp( sActiveFilter.missionType, missionType ) != 0 ) + { + sActiveFilter.missionType = (char*) dRealloc( sActiveFilter.missionType, dStrlen( missionType ) + 1 ); + dStrcpy( sActiveFilter.missionType, missionType ); + } + + sActiveFilter.queryFlags = flags; + sActiveFilter.minPlayers = minPlayers; + sActiveFilter.maxPlayers = maxPlayers; + sActiveFilter.maxBots = maxBots; + sActiveFilter.regionMask = regionMask; + sActiveFilter.maxPing = maxPing; + sActiveFilter.minCPU = minCPU; + sActiveFilter.filterFlags = filterFlags; + sActiveFilter.buddyCount = buddyCount; + dFree( sActiveFilter.buddyList ); + sActiveFilter.buddyList = NULL; + } + else + { + sActiveFilter.type = ServerFilter::Buddy; + sActiveFilter.buddyCount = buddyCount; + sActiveFilter.buddyList = (U32*) dRealloc( sActiveFilter.buddyList, buddyCount * 4 ); + dMemcpy( sActiveFilter.buddyList, buddyList, buddyCount * 4 ); + clearServerList(); + } + + // Pick a random master server from the list: + gMasterServerList.clear(); + Vector *masterList = getMasterServerList(); + for ( U32 i = 0; i < masterList->size(); i++ ) + gMasterServerList.push_back( (*masterList)[i] ); + + // Clear the master server ping: + gMasterServerPing.time = 0; + gMasterServerPing.tryCount = gMasterServerRetryCount; + + if ( !pickMasterServer() ) + Con::errorf( "No master servers found!" ); + else + processMasterServerQuery( gPingSession ); +} + +ConsoleFunction( queryMasterServer, void, 11, 11, "queryMasterServer(...);" ) +{ + TORQUE_UNUSED(argc); + + U8 flags = dAtoi(argv[1]); + + // It's not a good idea to hold onto args, recursive calls to + // console exec will trash them. + char* gameType = dStrdup(argv[2]); + char* missionType = dStrdup(argv[3]); + + U8 minPlayers = dAtoi(argv[4]); + U8 maxPlayers = dAtoi(argv[5]); + U8 maxBots = dAtoi(argv[6]); + U32 regionMask = dAtoi(argv[7]); + U32 maxPing = dAtoi(argv[8]); + U16 minCPU = dAtoi(argv[9]); + U8 filterFlags = dAtoi(argv[10]); + U32 buddyList = 0; + + clearServerList(); + queryMasterServer(flags,gameType,missionType,minPlayers,maxPlayers, + maxBots,regionMask,maxPing,minCPU,filterFlags,0,&buddyList); + + dFree(gameType); + dFree(missionType); +} + +//----------------------------------------------------------------------------- + +ConsoleFunction( querySingleServer, void, 3, 3, "querySingleServer(address, flags);" ) +{ + TORQUE_UNUSED(argc); + + NetAddress addr; + char* addrText; + + addrText = dStrdup(argv[1]); + U8 flags = dAtoi(argv[2]); + + + Net::stringToAddress( addrText, &addr ); + querySingleServer(&addr,flags); +} + +//----------------------------------------------------------------------------- + +void queryFavoriteServers( U8 /*flags*/ ) +{ + sgServerQueryActive = true; + clearServerList(); + sActiveFilter.type = ServerFilter::Favorites; + pushServerFavorites(); + + Con::executef( "onServerQueryStatus", "start", "Query favorites...", "0" ); + processPingsAndQueries( gPingSession ); +} + +//----------------------------------------------------------------------------- + +void querySingleServer( const NetAddress* addr, U8 /*flags*/ ) +{ + sgServerQueryActive = true; + ServerInfo* si = findServerInfo( addr ); + if ( si ) + si->status = ServerInfo::Status_New | ServerInfo::Status_Updating; + + // Remove the server from the finished list (if it's there): + for ( U32 i = 0; i < gFinishedList.size(); i++ ) + { + if ( Net::compareAddresses( addr, &gFinishedList[i] ) ) + { + gFinishedList.erase( i ); + break; + } + } + + Con::executef( "onServerQueryStatus", "start", "Refreshing server...", "0" ); + gServerPingCount = gServerQueryCount = 0; + pushPingRequest( addr ); + processPingsAndQueries( gPingSession ); +} + +//----------------------------------------------------------------------------- + +void cancelServerQuery() +{ + // Cancel the current query, if there is anything left + // on the ping list, it's dropped. + if ( sgServerQueryActive ) + { + Con::printf( "Server query canceled." ); + ServerInfo* si; + + // Clear the master server packet list: + gPacketStatusList.clear(); + + // Clear the ping list: + while ( gPingList.size() ) + { + si = findServerInfo( &gPingList[0].address ); + if ( si && !si->status.test( ServerInfo::Status_Responded ) ) + si->status = ServerInfo::Status_TimedOut; + + gPingList.erase( U32( 0 ) ); + } + + // Clear the query list: + while ( gQueryList.size() ) + { + si = findServerInfo( &gQueryList[0].address ); + if ( si && !si->status.test( ServerInfo::Status_Responded ) ) + si->status = ServerInfo::Status_TimedOut; + + gQueryList.erase( U32( 0 ) ); + } + + sgServerQueryActive = false; + gServerBrowserDirty = true; + } +} + +ConsoleFunction( cancelServerQuery, void, 1, 1, "cancelServerQuery()" ) +{ + TORQUE_UNUSED(argc); TORQUE_UNUSED(argv); + cancelServerQuery(); +} + +//----------------------------------------------------------------------------- + +void stopServerQuery() +{ + // Cancel the current query, anything left on the ping + // list is moved to the finished list as "done". + if ( sgServerQueryActive ) + { + gPacketStatusList.clear(); + + if ( gPingList.size() ) + { + while ( gPingList.size() ) + { + gFinishedList.push_back( gPingList[0].address ); + gPingList.erase( U32( 0 ) ); + } + } + else + cancelServerQuery(); + } +} + +ConsoleFunction( stopServerQuery, void, 1, 1, "stopServerQuery()" ) +{ + TORQUE_UNUSED(argc); TORQUE_UNUSED(argv); + stopServerQuery(); +} + +//----------------------------------------------------------------------------- + +ConsoleFunction(startHeartbeat, void, 1, 1, "startHeartbeat()") +{ + TORQUE_UNUSED(argc); TORQUE_UNUSED(argv); + + if (validateAuthenticatedServer()) { + gHeartbeatSeq++; + processHeartbeat(gHeartbeatSeq); // thump-thump... + } +} + +ConsoleFunction(stopHeartbeat, void, 1, 1, "stopHeartbeat();") +{ + TORQUE_UNUSED(argc); TORQUE_UNUSED(argv); + gHeartbeatSeq++; +} + +//----------------------------------------------------------------------------- + +ConsoleFunction( getServerCount, int, 1, 1, "getServerCount();" ) +{ + TORQUE_UNUSED(argv); TORQUE_UNUSED(argc); + return gServerList.size(); +} + +ConsoleFunction( setServerInfo, bool, 2, 2, "setServerInfo(index);" ) +{ + TORQUE_UNUSED(argc); + U32 index = dAtoi(argv[1]); + if (index >= 0 && index < gServerList.size()) { + ServerInfo& info = gServerList[index]; + + char addrString[256]; + Net::addressToString( &info.address, addrString ); + + Con::setIntVariable("ServerInfo::Status",info.status); + Con::setVariable("ServerInfo::Address",addrString); + Con::setVariable("ServerInfo::Name",info.name); + Con::setVariable("ServerInfo::GameType",info.gameType); + Con::setVariable("ServerInfo::MissionName",info.missionName); + Con::setVariable("ServerInfo::MissionType",info.missionType); + Con::setVariable("ServerInfo::State",info.statusString); + Con::setVariable("ServerInfo::Info",info.infoString); + Con::setIntVariable("ServerInfo::PlayerCount",info.numPlayers); + Con::setIntVariable("ServerInfo::MaxPlayers",info.maxPlayers); + Con::setIntVariable("ServerInfo::BotCount",info.numBots); + Con::setIntVariable("ServerInfo::Version",info.version); + Con::setIntVariable("ServerInfo::Ping",info.ping); + Con::setIntVariable("ServerInfo::CPUSpeed",info.cpuSpeed); + Con::setBoolVariable("ServerInfo::Favorite",info.isFavorite); + Con::setBoolVariable("ServerInfo::Dedicated",info.isDedicated()); + Con::setBoolVariable("ServerInfo::Password",info.isPassworded()); + return true; + } + return false; +} + + +//----------------------------------------------------------------------------- +// Internal +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- + +ServerInfo::~ServerInfo() +{ + if ( name ) + dFree( name ); + if ( gameType ) + dFree( gameType ); + if ( missionType ) + dFree( missionType ); + if( missionName ) + dFree( missionName ); + if ( statusString ) + dFree( statusString ); + if ( infoString ) + dFree( infoString ); +} + +//----------------------------------------------------------------------------- + +Vector* getMasterServerList() +{ + // This code used to get the master server list from the + // WON.net directory. + static Vector masterList; + masterList.clear(); + + for (U32 i = 0; i < 10; i++) { + char buffer[50]; + dSprintf(buffer,sizeof(buffer),"pref::Master%d",i); + const char* master = Con::getVariable(buffer); + if (master && *master) { + NetAddress address; + // Format for master server variable: + // regionMask:netAddress + U32 region = 1; // needs to default to something > 0 + dSscanf(master,"%d:",®ion); + const char* madd = dStrchr(master,':') + 1; + if (region && Net::stringToAddress(madd,&address)) { + masterList.increment(); + MasterInfo& info = masterList.last(); + info.address = address; + info.region = region; + } + else + Con::errorf("Bad master server address: %s",master); + } + } + + if (!masterList.size()) + Con::errorf("No master servers found"); + + return &masterList; +} + + +//----------------------------------------------------------------------------- + +bool pickMasterServer() +{ + // Reset the master server ping: + gMasterServerPing.time = 0; + gMasterServerPing.key = 0; + gMasterServerPing.tryCount = gMasterServerRetryCount; + gMasterServerPing.session = gPingSession; + + char addrString[256]; + U32 serverCount = gMasterServerList.size(); + if ( !serverCount ) + { + // There are no more servers left to try...:( + return( false ); + } + + U32 region = Con::getIntVariable( "$pref::Net::RegionMask" ); + U32 index = Sim::getCurrentTime() % serverCount; + + // First try to find a master server in the same region: + for ( U32 i = 0; i < serverCount; i++ ) + { + if ( gMasterServerList[index].region == region ) + { + Net::addressToString( &gMasterServerList[index].address, addrString ); + Con::printf( "Found master server %s in same region.", addrString ); + gMasterServerPing.address = gMasterServerList[index].address; + return( true ); + } + + index = index < serverCount - 1 ? index + 1 : 0; + } + + // Settle for the one we first picked: + Net::addressToString( &gMasterServerList[index].address, addrString ); + Con::printf( "No master servers found in this region, trying %s.", addrString ); + gMasterServerPing.address = gMasterServerList[index].address; + + return( true ); +} + +//----------------------------------------------------------------------------- + +void clearServerList() +{ + gPacketStatusList.clear(); + gServerList.clear(); + gFinishedList.clear(); + gPingList.clear(); + gQueryList.clear(); + gServerPingCount = gServerQueryCount = 0; + + gPingSession++; +} + +//----------------------------------------------------------------------------- + +void sendHeartbeat( U8 flags ) +{ + // send heartbeats to all of the master servers: + Vector *masterList = getMasterServerList(); + for(U32 i = 0; i < masterList->size(); i++) + { + char buffer[256]; + Net::addressToString(&(*masterList)[i].address, buffer); + // Send a request to the master server for the game types: + Con::printf( "Sending heartbeat to master server [%s]", buffer ); + sendPacket( NetInterface::GameHeartbeat, &(*masterList)[i].address, 0, gPingSession, flags ); + } +} + +//----------------------------------------------------------------------------- + +static void pushPingRequest( const NetAddress* addr ) +{ + if( addressFinished( addr ) ) + return; + + Ping p; + p.address = *addr; + p.session = gPingSession; + p.key = 0; + p.time = 0; + p.tryCount = gPingRetryCount; + p.broadcast = false; + gPingList.push_back( p ); + gServerPingCount++; +} + +//----------------------------------------------------------------------------- + +static void pushPingBroadcast( const NetAddress* addr ) +{ + if( addressFinished( addr ) ) + return; + + Ping p; + p.address = *addr; + p.session = gPingSession; + p.key = 0; + p.time = 0; + p.tryCount = 1; // only try this once + p.broadcast = true; + gPingList.push_back( p ); + // Don't increment gServerPingCount, broadcasts are not + // counted as requests. +} + +//----------------------------------------------------------------------------- + +static S32 countPingRequests() +{ + // Need a function here because the ping list also includes + // broadcast pings we don't want counted. + U32 pSize = gPingList.size(), count = pSize; + for (U32 i = 0; i < pSize; i++) + if (gPingList[i].broadcast) + count--; + return count; +} + + +//----------------------------------------------------------------------------- + +static void pushServerFavorites() +{ + S32 count = Con::getIntVariable( "$pref::Client::ServerFavoriteCount" ); + if ( count < 0 ) + { + Con::setIntVariable( "$pref::Client::ServerFavoriteCount", 0 ); + return; + } + + NetAddress addr; + const char* server = NULL; + char buf[256], serverName[25], addrString[256]; + U32 sz, len; + for ( S32 i = 0; i < count; i++ ) + { + dSprintf( buf, sizeof( buf ), "pref::Client::ServerFavorite%d", i ); + server = Con::getVariable( buf ); + if ( server ) + { + sz = dStrcspn( server, "\t" ); + if ( sz > 0 ) + { + len = sz > 24 ? 24 : sz; + dStrncpy( serverName, server, len ); + serverName[len] = 0; + dStrncpy( addrString, server + ( sz + 1 ), 255 ); + + //Con::errorf( "Pushing server favorite \"%s\" - %s...", serverName, addrString ); + Net::stringToAddress( addrString, &addr ); + ServerInfo* si = findOrCreateServerInfo( &addr ); + AssertFatal(si, "pushServerFavorites - failed to create Server Info!" ); + si->name = (char*) dRealloc( (void*) si->name, dStrlen( serverName ) + 1 ); + dStrcpy( si->name, serverName ); + si->isFavorite = true; + pushPingRequest( &addr ); + } + } + } +} + +//----------------------------------------------------------------------------- + +static S32 findPingEntry( Vector &v, const NetAddress* addr ) +{ + for ( U32 i = 0; i < v.size(); i++ ) + if ( Net::compareAddresses( addr, &v[i].address ) ) + return S32( i ); + return -1; +} + +//----------------------------------------------------------------------------- + +static bool addressFinished( const NetAddress* addr ) +{ + for ( U32 i = 0; i < gFinishedList.size(); i++ ) + if ( Net::compareAddresses( addr, &gFinishedList[i] ) ) + return true; + return false; +} + +//----------------------------------------------------------------------------- + +static ServerInfo* findServerInfo( const NetAddress* addr ) +{ + for ( U32 i = 0; i < gServerList.size(); i++ ) + if ( Net::compareAddresses( addr, &gServerList[i].address ) ) + return &gServerList[i]; + return NULL; +} + +//----------------------------------------------------------------------------- + +static ServerInfo* findOrCreateServerInfo( const NetAddress* addr ) +{ + ServerInfo* ret = findServerInfo( addr ); + if ( ret ) + return ret; + + ServerInfo si; + si.address = *addr; + gServerList.push_back( si ); + + return &gServerList.last(); +} + +//----------------------------------------------------------------------------- + +static void removeServerInfo( const NetAddress* addr ) +{ + for ( U32 i = 0; i < gServerList.size(); i++ ) + { + if ( Net::compareAddresses( addr, &gServerList[i].address ) ) + { + gServerList.erase( i ); + gServerBrowserDirty = true; + } + } +} + +//----------------------------------------------------------------------------- + +#if defined(TORQUE_DEBUG) +// This function is solely for testing the functionality of the server browser +// with more servers in the list. +void addFakeServers( S32 howMany ) +{ + static S32 sNumFakeServers = 1; + ServerInfo newServer; + + for ( S32 i = 0; i < howMany; i++ ) + { + newServer.numPlayers = (U8)(Platform::getRandom() * 64.0f); + newServer.maxPlayers = 64; + char buf[256]; + dSprintf( buf, 255, "Fake server #%d", sNumFakeServers ); + newServer.name = (char*) dMalloc( dStrlen( buf ) + 1 ); + dStrcpy( newServer.name, buf ); + newServer.gameType = (char*) dMalloc( 5 ); + dStrcpy( newServer.gameType, "Fake" ); + newServer.missionType = (char*) dMalloc( 4 ); + dStrcpy( newServer.missionType, "FakeMissionType" ); + newServer.missionName = (char*) dMalloc( 14 ); + dStrcpy( newServer.missionName, "FakeMapName" ); + Net::stringToAddress( "IP:198.74.33.35:28000", &newServer.address ); + newServer.ping = (U32)( Platform::getRandom() * 200.0f ); + newServer.cpuSpeed = 470; + newServer.status = ServerInfo::Status_Responded; + + gServerList.push_back( newServer ); + sNumFakeServers++; + } + + gServerBrowserDirty = true; +} +#endif // DEBUG + +//----------------------------------------------------------------------------- + +static void sendPacket( U8 pType, const NetAddress* addr, U32 key, U32 session, U8 flags ) +{ + BitStream *out = BitStream::getPacketStream(); + out->clearStringBuffer(); + out->write( pType ); + out->write( flags ); + out->write( U32( ( session << 16 ) | ( key & 0xFFFF ) ) ); + + BitStream::sendPacketStream(addr); +} + +//----------------------------------------------------------------------------- + +static void writeCString( BitStream* stream, const char* string ) +{ + U8 strLen = ( string != NULL ) ? dStrlen( string ) : 0; + stream->write( strLen ); + for ( U32 i = 0; i < strLen; i++ ) + stream->write( U8( string[i] ) ); +} + +//----------------------------------------------------------------------------- + +static void readCString( BitStream* stream, char* buffer ) +{ + U32 i; + U8 strLen; + stream->read( &strLen ); + for ( i = 0; i < strLen; i++ ) + { + U8* ptr = (U8*) buffer; + stream->read( &ptr[i] ); + } + buffer[i] = 0; +} + +//----------------------------------------------------------------------------- + +static void writeLongCString( BitStream* stream, const char* string ) +{ + U16 strLen = ( string != NULL ) ? dStrlen( string ) : 0; + stream->write( strLen ); + for ( U32 i = 0; i < strLen; i++ ) + stream->write( U8( string[i] ) ); +} + +//----------------------------------------------------------------------------- + +static void readLongCString( BitStream* stream, char* buffer ) +{ + U32 i; + U16 strLen; + stream->read( &strLen ); + for ( i = 0; i < strLen; i++ ) + { + U8* ptr = (U8*) buffer; + stream->read( &ptr[i] ); + } + buffer[i] = 0; +} + +//----------------------------------------------------------------------------- +// Event processing +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- + +static void processMasterServerQuery( U32 session ) +{ + if ( session != gPingSession || !sgServerQueryActive ) + return; + + if ( !gGotFirstListPacket ) + { + bool keepGoing = true; + U32 time = Platform::getVirtualMilliseconds(); + char addressString[256]; + + if ( gMasterServerPing.time + gMasterServerTimeout < time ) + { + Net::addressToString( &gMasterServerPing.address, addressString ); + if ( !gMasterServerPing.tryCount ) + { + // The query timed out. + Con::printf( "Server list request to %s timed out.", addressString ); + + // Remove this server from the list: + for ( U32 i = 0; i < gMasterServerList.size(); i++ ) + { + if ( Net::compareAddresses( &gMasterServerList[i].address, &gMasterServerPing.address ) ) + { + gMasterServerList.erase( i ); + break; + } + } + + // Pick a new master server to try: + keepGoing = pickMasterServer(); + if ( keepGoing ) + { + Con::executef( "onServerQueryStatus", "update", "Switching master servers...", "0" ); + Net::addressToString( &gMasterServerPing.address, addressString ); + } + } + + if ( keepGoing ) + { + gMasterServerPing.tryCount--; + gMasterServerPing.time = time; + gMasterServerPing.key = gKey++; + + // Send a request to the master server for the server list: + BitStream *out = BitStream::getPacketStream(); + out->clearStringBuffer(); + out->write( U8( NetInterface::MasterServerListRequest ) ); + out->write( U8( sActiveFilter.queryFlags) ); + out->write( ( gMasterServerPing.session << 16 ) | ( gMasterServerPing.key & 0xFFFF ) ); + out->write( U8( 255 ) ); + writeCString( out, sActiveFilter.gameType ); + writeCString( out, sActiveFilter.missionType ); + out->write( sActiveFilter.minPlayers ); + out->write( sActiveFilter.maxPlayers ); + out->write( sActiveFilter.regionMask ); + U32 version = ( sActiveFilter.filterFlags & ServerFilter::CurrentVersion ) ? getVersionNumber() : 0; + out->write( version ); + out->write( sActiveFilter.filterFlags ); + out->write( sActiveFilter.maxBots ); + out->write( sActiveFilter.minCPU ); + out->write( sActiveFilter.buddyCount ); + for ( U32 i = 0; i < sActiveFilter.buddyCount; i++ ) + out->write( sActiveFilter.buddyList[i] ); + + BitStream::sendPacketStream( &gMasterServerPing.address ); + + Con::printf( "Requesting the server list from master server %s (%d tries left)...", addressString, gMasterServerPing.tryCount ); + if ( gMasterServerPing.tryCount < gMasterServerRetryCount - 1 ) + Con::executef( "onServerQueryStatus", "update", "Retrying the master server...", "0" ); + } + } + + if ( keepGoing ) + { + // schedule another check: + Sim::postEvent( Sim::getRootGroup(), new ProcessMasterQueryEvent( session ), Sim::getTargetTime() + 1 ); + } + else + { + Con::errorf( "There are no more master servers to try!" ); + Con::executef( "onServerQueryStatus", "done", "No master servers found.", "0" ); + } + } +} + +//----------------------------------------------------------------------------- + +static void processPingsAndQueries( U32 session, bool schedule ) +{ + if( session != gPingSession ) + return; + + U32 i = 0; + U32 time = Platform::getVirtualMilliseconds(); + char addressString[256]; + U8 flags = ServerFilter::OnlineQuery; + bool waitingForMaster = ( sActiveFilter.type == ServerFilter::Normal ) && !gGotFirstListPacket && sgServerQueryActive; + + for ( i = 0; i < gPingList.size() && i < gMaxConcurrentPings; ) + { + Ping &p = gPingList[i]; + + if ( p.time + gPingTimeout < time ) + { + if ( !p.tryCount ) + { + // it's timed out. + if (!p.broadcast) + { + Net::addressToString( &p.address, addressString ); + Con::printf( "Ping to server %s timed out.", addressString ); + } + + // If server info is in list (favorite), set its status: + ServerInfo* si = findServerInfo( &p.address ); + if ( si ) + { + si->status = ServerInfo::Status_TimedOut; + gServerBrowserDirty = true; + } + + gFinishedList.push_back( p.address ); + gPingList.erase( i ); + + if ( !waitingForMaster ) + updatePingProgress(); + } + else + { + p.tryCount--; + p.time = time; + p.key = gKey++; + + Net::addressToString( &p.address, addressString ); + + if (p.broadcast) + Con::printf( "LAN server ping: %s...", addressString ); + else + Con::printf( "Pinging Server %s (%d)...", addressString, p.tryCount ); + sendPacket( NetInterface::GamePingRequest, &p.address, p.key, p.session, flags ); + i++; + } + } + else + i++; + } + + if ( !gPingList.size() && !waitingForMaster ) + { + // Start the query phase: + for ( U32 i = 0; i < gQueryList.size() && i < gMaxConcurrentQueries; ) + { + Ping &p = gQueryList[i]; + if ( p.time + gPingTimeout < time ) + { + ServerInfo* si = findServerInfo( &p.address ); + if ( !si ) + { + // Server info not found, so remove the query: + gQueryList.erase( i ); + gServerBrowserDirty = true; + continue; + } + + Net::addressToString( &p.address, addressString ); + if ( !p.tryCount ) + { + Con::printf( "Query to server %s timed out.", addressString ); + si->status = ServerInfo::Status_TimedOut; + gQueryList.erase( i ); + gServerBrowserDirty = true; + } + else + { + p.tryCount--; + p.time = time; + p.key = gKey++; + + Con::printf( "Querying Server %s (%d)...", addressString, p.tryCount ); + sendPacket( NetInterface::GameInfoRequest, &p.address, p.key, p.session, flags ); + if ( !si->isQuerying() ) + { + si->status |= ServerInfo::Status_Querying; + gServerBrowserDirty = true; + } + i++; + } + } + else + i++; + } + } + + if ( gPingList.size() || gQueryList.size() || waitingForMaster ) + { + // The LAN query function doesn't always want to schedule + // the next ping. + if (schedule) + Sim::postEvent( Sim::getRootGroup(), new ProcessPingEvent( session ), Sim::getTargetTime() + 1 ); + } + else + { + // All done! + char msg[64]; + U32 foundCount = gServerList.size(); + if ( foundCount == 0 ) + dStrcpy( msg, "No servers found." ); + else if ( foundCount == 1 ) + dStrcpy( msg, "One server found." ); + else + dSprintf( msg, sizeof( msg ), "%d servers found.", foundCount ); + + Con::executef( "onServerQueryStatus", "done", msg, "1"); + } +} + +//----------------------------------------------------------------------------- + +static void processServerListPackets( U32 session ) +{ + if ( session != gPingSession || !sgServerQueryActive ) + return; + + U32 currentTime = Platform::getVirtualMilliseconds(); + + // Loop through the packet status list and resend packet requests where necessary: + for ( U32 i = 0; i < gPacketStatusList.size(); i++ ) + { + PacketStatus &p = gPacketStatusList[i]; + if ( p.time + gPacketTimeout < currentTime ) + { + if ( !p.tryCount ) + { + // Packet timed out :( + Con::printf( "Server list packet #%d timed out.", p.index + 1 ); + gPacketStatusList.erase( i ); + } + else + { + // Try again... + Con::printf( "Rerequesting server list packet #%d...", p.index + 1 ); + p.tryCount--; + p.time = currentTime; + p.key = gKey++; + + BitStream *out = BitStream::getPacketStream(); + out->clearStringBuffer(); + out->write( U8( NetInterface::MasterServerListRequest ) ); + out->write( U8( sActiveFilter.queryFlags ) ); // flags + out->write( ( session << 16) | ( p.key & 0xFFFF ) ); + out->write( p.index ); // packet index + out->write( U8( 0 ) ); // game type + out->write( U8( 0 ) ); // mission type + out->write( U8( 0 ) ); // minPlayers + out->write( U8( 0 ) ); // maxPlayers + out->write( U32( 0 ) ); // region mask + out->write( U32( 0 ) ); // version + out->write( U8( 0 ) ); // filter flags + out->write( U8( 0 ) ); // max bots + out->write( U16( 0 ) ); // min CPU + out->write( U8( 0 ) ); // buddy count + + BitStream::sendPacketStream(&gMasterServerQueryAddress); + } + } + } + + if ( gPacketStatusList.size() ) + Sim::postEvent( Sim::getRootGroup(), new ProcessPacketEvent( session ), Sim::getCurrentTime() + 30 ); + else + processPingsAndQueries( gPingSession ); +} + +//----------------------------------------------------------------------------- + +static void processHeartbeat(U32 seq) +{ + if(seq != gHeartbeatSeq) + return; + sendHeartbeat( 0 ); + Sim::postEvent( Sim::getRootGroup(), new HeartbeatEvent(seq), Sim::getCurrentTime() + gHeartbeatInterval ); +} + +//----------------------------------------------------------------------------- + +static void updatePingProgress() +{ + if ( !gPingList.size() ) + { + updateQueryProgress(); + return; + } + + char msg[64]; + U32 pingsLeft = countPingRequests(); + dSprintf( msg, sizeof(msg), + (!pingsLeft && gPingList.size())? + "Waiting for lan servers...": + "Pinging servers: %d left...", + pingsLeft ); + + // Ping progress is 0 -> 0.5 + F32 progress = 0.0f; + if ( gServerPingCount ) + progress = F32( gServerPingCount - pingsLeft ) / F32( gServerPingCount * 2 ); + + //Con::errorf( ConsoleLogEntry::General, "Ping progress - %d of %d left - progress = %.2f", pingsLeft, gServerPingCount, progress ); + Con::executef( "onServerQueryStatus", "ping", msg, Con::getFloatArg( progress ) ); +} + +//----------------------------------------------------------------------------- + +static void updateQueryProgress() +{ + if ( gPingList.size() ) + return; + + char msg[64]; + U32 queriesLeft = gQueryList.size(); + dSprintf( msg, sizeof( msg ), "Querying servers: %d left...", queriesLeft ); + + // Query progress is 0.5 -> 1 + F32 progress = 0.5f; + if ( gServerQueryCount ) + progress += ( F32( gServerQueryCount - queriesLeft ) / F32( gServerQueryCount * 2 ) ); + + //Con::errorf( ConsoleLogEntry::General, "Query progress - %d of %d left - progress = %.2f", queriesLeft, gServerQueryCount, progress ); + Con::executef( "onServerQueryStatus", "query", msg, Con::getFloatArg( progress ) ); +} + + +//----------------------------------------------------------------------------- +// Server packet handlers: +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- + +static void handleMasterServerGameTypesResponse( BitStream* stream, U32 /*key*/, U8 /*flags*/ ) +{ + Con::printf( "Received game type list from the master server." ); + + U32 i; + U8 temp; + char stringBuf[256]; + stream->read( &temp ); + Con::executef("onClearGameTypes"); + for ( i = 0; i < U32( temp ); i++ ) + { + readCString( stream, stringBuf ); + Con::executef("onAddGameType", stringBuf); + } + + stream->read( &temp ); + Con::executef("onClearMissionTypes"); + for ( i = 0; i < U32( temp ); i++ ) + { + readCString( stream, stringBuf ); + Con::executef("onAddMissionType", stringBuf); + } +} + +//----------------------------------------------------------------------------- + +static void handleMasterServerListResponse( BitStream* stream, U32 key, U8 /*flags*/ ) +{ + U8 packetIndex, packetTotal; + U32 i; + U16 serverCount, port; + U8 netNum[4]; + char addressBuffer[256]; + NetAddress addr; + + stream->read( &packetIndex ); + // Validate the packet key: + U32 packetKey = gMasterServerPing.key; + if ( gGotFirstListPacket ) + { + for ( i = 0; i < gPacketStatusList.size(); i++ ) + { + if ( gPacketStatusList[i].index == packetIndex ) + { + packetKey = gPacketStatusList[i].key; + break; + } + } + } + + U32 testKey = ( gPingSession << 16 ) | ( packetKey & 0xFFFF ); + if ( testKey != key ) + return; + + stream->read( &packetTotal ); + stream->read( &serverCount ); + + Con::printf( "Received server list packet %d of %d from the master server (%d servers).", ( packetIndex + 1 ), packetTotal, serverCount ); + + // Enter all of the servers in this packet into the ping list: + for ( i = 0; i < serverCount; i++ ) + { + stream->read( &netNum[0] ); + stream->read( &netNum[1] ); + stream->read( &netNum[2] ); + stream->read( &netNum[3] ); + stream->read( &port ); + + dSprintf( addressBuffer, sizeof( addressBuffer ), "IP:%d.%d.%d.%d:%d", netNum[0], netNum[1], netNum[2], netNum[3], port ); + Net::stringToAddress( addressBuffer, &addr ); + pushPingRequest( &addr ); + } + + // If this is the first list packet we have received, fill the packet status list + // and start processing: + if ( !gGotFirstListPacket ) + { + gGotFirstListPacket = true; + gMasterServerQueryAddress = gMasterServerPing.address; + U32 currentTime = Platform::getVirtualMilliseconds(); + for ( i = 0; i < packetTotal; i++ ) + { + if ( i != packetIndex ) + { + PacketStatus* p = new PacketStatus( i, gMasterServerPing.key, currentTime ); + gPacketStatusList.push_back( *p ); + } + } + + processServerListPackets( gPingSession ); + } + else + { + // Remove the packet we just received from the status list: + for ( i = 0; i < gPacketStatusList.size(); i++ ) + { + if ( gPacketStatusList[i].index == packetIndex ) + { + gPacketStatusList.erase( i ); + break; + } + } + } +} + +//----------------------------------------------------------------------------- + +static void handleGameMasterInfoRequest( const NetAddress* address, U32 key, U8 flags ) +{ + if ( GNet->doesAllowConnections() ) + { + U8 temp8; + U32 temp32; + + char netString[256]; + Net::addressToString(address, netString); + + Vector *masterList = getMasterServerList(); + const NetAddress *masterAddr; + bool fromMaster = false; + for(U32 i = 0; i < masterList->size(); i++) + { + masterAddr = &(*masterList)[i].address; + if (*(U32*)(masterAddr->netNum) == *(U32*)(address->netNum)) + { + fromMaster = true; + break; + } + } + + Con::printf( "Received info request from %s [%s].", fromMaster?"a master server":"a machine", netString); + + BitStream *out = BitStream::getPacketStream(); + out->clearStringBuffer(); + + out->write( U8( NetInterface::GameMasterInfoResponse ) ); + out->write( U8( flags ) ); + out->write( key ); + + writeCString( out, Con::getVariable( "Server::GameType" ) ); + writeCString( out, Con::getVariable( "Server::MissionType" ) ); + temp8 = U8( Con::getIntVariable( "pref::Server::MaxPlayers" ) ); + out->write( temp8 ); + temp32 = Con::getIntVariable( "pref::Server::RegionMask" ); + out->write( temp32 ); + temp32 = getVersionNumber(); + out->write( temp32 ); + temp8 = 0; +#if defined(TORQUE_OS_LINUX) || defined(TORQUE_OS_OPENBSD) + temp8 |= ServerInfo::Status_Linux; +#endif +#if defined(TORQUE_OS_XENON) + temp8 |= ServerInfo::Status_Xenon; +#endif + if ( Con::getBoolVariable( "Server::Dedicated" ) ) + temp8 |= ServerInfo::Status_Dedicated; + if ( dStrlen( Con::getVariable( "pref::Server::Password" ) ) > 0 ) + temp8 |= ServerInfo::Status_Passworded; + out->write( temp8 ); + temp8 = U8( Con::getIntVariable( "Server::BotCount" ) ); + out->write( temp8 ); + out->write( Platform::SystemInfo.processor.mhz ); + + U8 playerCount = U8( Con::getIntVariable( "Server::PlayerCount" ) ); + out->write( playerCount ); + + const char* guidList = Con::getVariable( "Server::GuidList" ); + char* buf = new char[dStrlen( guidList ) + 1]; + dStrcpy( buf, guidList ); + char* temp = dStrtok( buf, "\t" ); + temp8 = 0; + for ( ; temp && temp8 < playerCount; temp8++ ) + { + out->write( U32( dAtoi( temp ) ) ); + temp = dStrtok( NULL, "\t" ); + temp8++; + } + + for ( ; temp8 < playerCount; temp8++ ) + out->write( U32( 0 ) ); + + delete [] buf; + + BitStream::sendPacketStream(address); + } +} + +//----------------------------------------------------------------------------- + +static void handleGamePingRequest( const NetAddress* address, U32 key, U8 flags ) +{ + // Do not respond if a mission is not running: + if ( GNet->doesAllowConnections() ) + { + // Do not respond if this is a single-player game: + if ( dStricmp( Con::getVariable( "Server::ServerType" ), "SinglePlayer" ) == 0 ) + return; + + // Do not respond to offline queries if this is an online server: + if ( flags & ServerFilter::OfflineQuery ) + return; + + // some banning code here (?) + + BitStream *out = BitStream::getPacketStream(); + out->clearStringBuffer(); + + out->write( U8( NetInterface::GamePingResponse ) ); + out->write( flags ); + out->write( key ); + if ( flags & ServerFilter::NoStringCompress ) + writeCString( out, versionString ); + else + out->writeString( versionString ); + out->write( GameConnection::CurrentProtocolVersion ); + out->write( GameConnection::MinRequiredProtocolVersion ); + out->write( getVersionNumber() ); + + // Enforce a 24-character limit on the server name: + char serverName[25]; + dStrncpy( serverName, Con::getVariable( "pref::Server::Name" ), 24 ); + serverName[24] = 0; + if ( flags & ServerFilter::NoStringCompress ) + writeCString( out, serverName ); + else + out->writeString( serverName ); + + BitStream::sendPacketStream(address); + } +} + +//----------------------------------------------------------------------------- + +static void handleGamePingResponse( const NetAddress* address, BitStream* stream, U32 key, U8 /*flags*/ ) +{ + // Broadcast has timed out or query has been cancelled: + if( !gPingList.size() ) + return; + + S32 index = findPingEntry( gPingList, address ); + if( index == -1 ) + { + // an anonymous ping response - if it's not already timed + // out or finished, ping it. Probably from a broadcast + if( !addressFinished( address ) ) + pushPingRequest( address ); + return; + } + Ping &p = gPingList[index]; + U32 infoKey = ( p.session << 16 ) | ( p.key & 0xFFFF ); + if( infoKey != key ) + return; + + // Find if the server info already exists (favorite or refreshing): + ServerInfo* si = findServerInfo( address ); + bool applyFilter = false; + if ( sActiveFilter.type == ServerFilter::Normal ) + applyFilter = si ? !si->isUpdating() : true; + + char addrString[256]; + Net::addressToString( address, addrString ); + bool waitingForMaster = ( sActiveFilter.type == ServerFilter::Normal ) && !gGotFirstListPacket; + + // Verify the version: + char buf[256]; + stream->readString( buf ); + if ( dStrcmp( buf, versionString ) != 0 ) + { + // Version is different, so remove it from consideration: + Con::printf( "Server %s is a different version.", addrString ); + Con::printf( "Wanted version %s, got version %s", versionString, buf); + gFinishedList.push_back( *address ); + gPingList.erase( index ); + if ( si ) + { + si->status = ServerInfo::Status_TimedOut; + gServerBrowserDirty = true; + } + if ( !waitingForMaster ) + updatePingProgress(); + return; + } + + // See if the server meets our minimum protocol: + U32 temp32; + stream->read( &temp32 ); + if ( temp32 < GameConnection::MinRequiredProtocolVersion ) + { + Con::printf( "Protocol for server %s does not meet minimum protocol.", addrString ); + gFinishedList.push_back( *address ); + gPingList.erase( index ); + if ( si ) + { + si->status = ServerInfo::Status_TimedOut; + gServerBrowserDirty = true; + } + if ( !waitingForMaster ) + updatePingProgress(); + return; + } + + // See if we meet the server's minimum protocol: + stream->read( &temp32 ); + if ( GameConnection::CurrentProtocolVersion < temp32 ) + { + Con::printf( "You do not meet the minimum protocol for server %s.", addrString ); + gFinishedList.push_back( *address ); + gPingList.erase( index ); + if ( si ) + { + si->status = ServerInfo::Status_TimedOut; + gServerBrowserDirty = true; + } + if ( !waitingForMaster ) + updatePingProgress(); + return; + } + + // Calculate the ping: + U32 time = Platform::getVirtualMilliseconds(); + U32 ping = ( time > p.time ) ? time - p.time : 0; + + // Check for max ping filter: + if ( applyFilter && sActiveFilter.maxPing > 0 && ping > sActiveFilter.maxPing ) + { + // Ping is too high, so remove this server from consideration: + Con::printf( "Server %s filtered out by maximum ping.", addrString ); + gFinishedList.push_back( *address ); + gPingList.erase( index ); + if ( si ) + removeServerInfo( address ); + if ( !waitingForMaster ) + updatePingProgress(); + return; + } + + // Get the server build version: + stream->read( &temp32 ); + if ( applyFilter + && ( sActiveFilter.filterFlags & ServerFilter::CurrentVersion ) + && ( temp32 != getVersionNumber() ) ) + { + Con::printf( "Server %s filtered out by version number.", addrString ); + gFinishedList.push_back( *address ); + gPingList.erase( index ); + if ( si ) + removeServerInfo( address ); + if ( !waitingForMaster ) + updatePingProgress(); + return; + } + + // OK, we can finally create the server info structure: + if ( !si ) + si = findOrCreateServerInfo( address ); + si->ping = ping; + si->version = temp32; + + // Get the server name: + stream->readString( buf ); + if ( !si->name ) + { + si->name = (char*) dMalloc( dStrlen( buf ) + 1 ); + dStrcpy( si->name, buf ); + } + + // Set the server up to be queried: + gFinishedList.push_back( *address ); + p.key = 0; + p.time = 0; + p.tryCount = gQueryRetryCount; + gQueryList.push_back( p ); + gServerQueryCount++; + gPingList.erase( index ); + if ( !waitingForMaster ) + updatePingProgress(); + + // Update the server browser gui! + gServerBrowserDirty = true; +} + +//----------------------------------------------------------------------------- + +static void handleGameInfoRequest( const NetAddress* address, U32 key, U8 flags ) +{ + // Do not respond unless there is a server running: + if ( GNet->doesAllowConnections() ) + { + // Do not respond to offline queries if this is an online server: + if ( flags & ServerFilter::OfflineQuery ) + return; + + bool compressStrings = !( flags & ServerFilter::NoStringCompress ); + BitStream *out = BitStream::getPacketStream(); + out->clearStringBuffer(); + + out->write( U8( NetInterface::GameInfoResponse ) ); + out->write( flags ); + out->write( key ); + + if ( compressStrings ) { + out->writeString( Con::getVariable( "Server::GameType" ) ); + out->writeString( Con::getVariable( "Server::MissionType" ) ); + out->writeString( Con::getVariable( "Server::MissionName" ) ); + } + else { + writeCString( out, Con::getVariable( "Server::GameType" ) ); + writeCString( out, Con::getVariable( "Server::MissionType" ) ); + writeCString( out, Con::getVariable( "Server::MissionName" ) ); + } + + U8 status = 0; +#if defined(TORQUE_OS_LINUX) || defined(TORQUE_OS_OPENBSD) + status |= ServerInfo::Status_Linux; +#endif +#if defined(TORQUE_OS_XENON) + status |= ServerInfo::Status_Xenon; +#endif + if ( Con::getBoolVariable( "Server::Dedicated" ) ) + status |= ServerInfo::Status_Dedicated; + if ( dStrlen( Con::getVariable( "pref::Server::Password" ) ) ) + status |= ServerInfo::Status_Passworded; + out->write( status ); + + out->write( U8( Con::getIntVariable( "Server::PlayerCount" ) ) ); + out->write( U8( Con::getIntVariable( "pref::Server::MaxPlayers" ) ) ); + out->write( U8( Con::getIntVariable( "Server::BotCount" ) ) ); + out->write( U16( Platform::SystemInfo.processor.mhz ) ); + if ( compressStrings ) + out->writeString( Con::getVariable( "pref::Server::Info" ) ); + else + writeCString( out, Con::getVariable( "pref::Server::Info" ) ); + writeLongCString( out, Con::evaluate( "onServerInfoQuery();" ) ); + + BitStream::sendPacketStream(address); + } +} + +//----------------------------------------------------------------------------- + +static void handleGameInfoResponse( const NetAddress* address, BitStream* stream, U32 /*key*/, U8 /*flags*/ ) +{ + if ( !gQueryList.size() ) + return; + + S32 index = findPingEntry( gQueryList, address ); + if ( index == -1 ) + return; + + // Remove the server from the query list since it has been so kind as to respond: + gQueryList.erase( index ); + updateQueryProgress(); + ServerInfo *si = findServerInfo( address ); + if ( !si ) + return; + + bool isUpdate = si->isUpdating(); + bool applyFilter = !isUpdate && ( sActiveFilter.type == ServerFilter::Normal ); + char addrString[256]; + Net::addressToString( address, addrString ); + + // Get the rules set: + char stringBuf[2048]; // Who knows how big this should be? + stream->readString( stringBuf ); + if ( !si->gameType || dStricmp( si->gameType, stringBuf ) != 0 ) + { + si->gameType = (char*) dRealloc( (void*) si->gameType, dStrlen( stringBuf ) + 1 ); + dStrcpy( si->gameType, stringBuf ); + + // Test against the active filter: + if ( applyFilter && dStricmp( sActiveFilter.gameType, "any" ) != 0 + && dStricmp( si->gameType, sActiveFilter.gameType ) != 0 ) + { + Con::printf( "Server %s filtered out by rules set. (%s:%s)", addrString, sActiveFilter.gameType, si->gameType ); + removeServerInfo( address ); + return; + } + } + + // Get the mission type: + stream->readString( stringBuf ); + if ( !si->missionType || dStrcmp( si->missionType, stringBuf ) != 0 ) + { + si->missionType = (char*) dRealloc( (void*) si->missionType, dStrlen( stringBuf ) + 1 ); + dStrcpy( si->missionType, stringBuf ); + + // Test against the active filter: + if ( applyFilter && dStricmp( sActiveFilter.missionType, "any" ) != 0 + && dStricmp( si->missionType, sActiveFilter.missionType ) != 0 ) + { + Con::printf( "Server %s filtered out by mission type. (%s:%s)", addrString, sActiveFilter.missionType, si->missionType ); + removeServerInfo( address ); + return; + } + } + + // Get the mission name: + stream->readString( stringBuf ); + // Clip the file extension off: + char* temp = dStrstr( static_cast( stringBuf ), const_cast( ".mis" ) ); + if ( temp ) + *temp = '\0'; + if ( !si->missionName || dStrcmp( si->missionName, stringBuf ) != 0 ) + { + si->missionName = (char*) dRealloc( (void*) si->missionName, dStrlen( stringBuf ) + 1 ); + dStrcpy( si->missionName, stringBuf ); + } + + // Get the server status: + U8 temp_U8; + stream->read( &temp_U8 ); + si->status = temp_U8; + + // Filter by the flags: + if ( applyFilter ) + { + if ( sActiveFilter.filterFlags & ServerFilter::Dedicated && !si->isDedicated() ) + { + Con::printf( "Server %s filtered out by dedicated flag.", addrString ); + removeServerInfo( address ); + return; + } + + if ( sActiveFilter.filterFlags & ServerFilter::NotPassworded && si->isPassworded() ) + { + Con::printf( "Server %s filtered out by no-password flag.", addrString ); + removeServerInfo( address ); + return; + } + + if ( sActiveFilter.filterFlags & ServerFilter::NotXenon && si->isXenon() ) + { + Con::printf( "Server %s filtered out by no-xenon flag.", addrString ); + removeServerInfo( address ); + return; + } + } + si->status.set( ServerInfo::Status_Responded ); + + // Get the player count: + stream->read( &si->numPlayers ); + + // Test player count against active filter: + if ( applyFilter && ( si->numPlayers < sActiveFilter.minPlayers || si->numPlayers > sActiveFilter.maxPlayers ) ) + { + Con::printf( "Server %s filtered out by player count.", addrString ); + removeServerInfo( address ); + return; + } + + // Get the max players and bot count: + stream->read( &si->maxPlayers ); + stream->read( &si->numBots ); + + // Test bot count against active filter: + if ( applyFilter && ( si->numBots > sActiveFilter.maxBots ) ) + { + Con::printf( "Server %s filtered out by maximum bot count.", addrString ); + removeServerInfo( address ); + return; + } + + // Get the CPU speed; + U16 temp_U16; + stream->read( &temp_U16 ); + si->cpuSpeed = temp_U16; + + // Test CPU speed against active filter: + if ( applyFilter && ( si->cpuSpeed < sActiveFilter.minCPU ) ) + { + Con::printf( "Server %s filtered out by minimum CPU speed.", addrString ); + removeServerInfo( address ); + return; + } + + // Get the server info: + stream->readString( stringBuf ); + if ( !si->statusString || ( isUpdate && dStrcmp( si->statusString, stringBuf ) != 0 ) ) + { + si->infoString = (char*) dRealloc( (void*) si->infoString, dStrlen( stringBuf ) + 1 ); + dStrcpy( si->infoString, stringBuf ); + } + + // Get the content string: + readLongCString( stream, stringBuf ); + if ( !si->statusString || ( isUpdate && dStrcmp( si->statusString, stringBuf ) != 0 ) ) + { + si->statusString = (char*) dRealloc( (void*) si->statusString, dStrlen( stringBuf ) + 1 ); + dStrcpy( si->statusString, stringBuf ); + } + + // Update the server browser gui! + gServerBrowserDirty = true; +} + +//----------------------------------------------------------------------------- +// Packet Dispatch + +void DemoNetInterface::handleInfoPacket( const NetAddress* address, U8 packetType, BitStream* stream ) +{ + U8 flags; + U32 key; + + stream->read( &flags ); + stream->read( &key ); + stream->clearStringBuffer(); + + switch( packetType ) + { + case GamePingRequest: + handleGamePingRequest( address, key, flags ); + break; + + case GamePingResponse: + handleGamePingResponse( address, stream, key, flags ); + break; + + case GameInfoRequest: + handleGameInfoRequest( address, key, flags ); + break; + + case GameInfoResponse: + handleGameInfoResponse( address, stream, key, flags ); + break; + + case MasterServerGameTypesResponse: + handleMasterServerGameTypesResponse( stream, key, flags ); + break; + + case MasterServerListResponse: + handleMasterServerListResponse( stream, key, flags ); + break; + + case GameMasterInfoRequest: + handleGameMasterInfoRequest( address, key, flags ); + break; + } +} + + +ConsoleFunctionGroupEnd( ServerQuery ); diff --git a/Engine/source/app/net/serverQuery.h b/Engine/source/app/net/serverQuery.h new file mode 100644 index 000000000..d59e3c355 --- /dev/null +++ b/Engine/source/app/net/serverQuery.h @@ -0,0 +1,128 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SERVERQUERY_H_ +#define _SERVERQUERY_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#ifndef _EVENT_H_ +#include "platform/event.h" +#endif +#ifndef _BITSET_H_ +#include "core/bitSet.h" +#endif + +#include "platform/platformNet.h" + +//----------------------------------------------------------------------------- +// Game Server Information + +struct ServerInfo +{ + enum StatusFlags + { + // Info flags (0-7): + Status_Dedicated = BIT(0), + Status_Passworded = BIT(1), + Status_Linux = BIT(2), + Status_Xenon = BIT(6), + + // Status flags: + Status_New = 0, + Status_Querying = BIT(28), + Status_Updating = BIT(29), + Status_Responded = BIT(30), + Status_TimedOut = BIT(31), + }; + + U8 numPlayers; + U8 maxPlayers; + U8 numBots; + char* name; + char* gameType; + char* missionName; + char* missionType; + char* statusString; + char* infoString; + NetAddress address; + U32 version; + U32 ping; + U32 cpuSpeed; + bool isFavorite; + BitSet32 status; + + ServerInfo() + { + numPlayers = 0; + maxPlayers = 0; + numBots = 0; + name = NULL; + gameType = NULL; + missionType = NULL; + missionName = NULL; + statusString = NULL; + infoString = NULL; + version = 0; + ping = 0; + cpuSpeed = 0; + isFavorite = false; + status = Status_New; + } + ~ServerInfo(); + + bool isNew() { return( status == Status_New ); } + bool isQuerying() { return( status.test( Status_Querying ) ); } + bool isUpdating() { return( status.test( Status_Updating ) ); } + bool hasResponded() { return( status.test( Status_Responded ) ); } + bool isTimedOut() { return( status.test( Status_TimedOut ) ); } + + bool isDedicated() { return( status.test( Status_Dedicated ) ); } + bool isPassworded() { return( status.test( Status_Passworded ) ); } + bool isLinux() { return( status.test( Status_Linux ) ); } + bool isXenon() { return( status.test( Status_Xenon ) ); } +}; + + +//----------------------------------------------------------------------------- + +extern Vector gServerList; +extern bool gServerBrowserDirty; +extern void clearServerList(); +extern void queryLanServers(U32 port, U8 flags, const char* gameType, const char* missionType, + U8 minPlayers, U8 maxPlayers, U8 maxBots, U32 regionMask, U32 maxPing, U16 minCPU, + U8 filterFlags); +extern void queryMasterGameTypes(); +extern void queryMasterServer(U8 flags, const char* gameType, const char* missionType, + U8 minPlayers, U8 maxPlayers, U8 maxBots, U32 regionMask, U32 maxPing, U16 minCPU, + U8 filterFlags, U8 buddyCount, U32* buddyList ); +extern void queryFavoriteServers( U8 flags ); +extern void querySingleServer(const NetAddress* addr, U8 flags); +extern void startHeartbeat(); +extern void sendHeartbeat( U8 flags ); + +#ifdef TORQUE_DEBUG +extern void addFakeServers( S32 howMany ); +#endif // DEBUG + +#endif diff --git a/Engine/source/app/net/tcpObject.cpp b/Engine/source/app/net/tcpObject.cpp new file mode 100644 index 000000000..202934fb1 --- /dev/null +++ b/Engine/source/app/net/tcpObject.cpp @@ -0,0 +1,521 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "app/net/tcpObject.h" + +#include "platform/platform.h" +#include "platform/event.h" +#include "console/simBase.h" +#include "console/consoleInternal.h" +#include "core/strings/stringUnit.h" +#include "console/engineAPI.h" + +TCPObject *TCPObject::table[TCPObject::TableSize] = {0, }; + +IMPLEMENT_CONOBJECT(TCPObject); + +ConsoleDocClass( TCPObject, + "@brief Allows communications between the game and a server using TCP/IP protocols.\n\n" + + "To use TCPObject you set up a connection to a server, send data to the server, and handle " + "each line of the server's response using a callback. Once you are done communicating with " + "the server, you disconnect.\n\n" + + "TCPObject is intended to be used with text based protocols which means you'll need to " + "delineate the server's response with an end-of-line character. i.e. the newline " + "character @\\n. You may optionally include the carriage return character @\\r prior to the newline " + "and TCPObject will strip it out before sending the line to the callback. If a newline " + "character is not included in the server's output, the received data will not be " + "processed until you disconnect from the server (which flushes the internal buffer).\n\n" + + "TCPObject may also be set up to listen to a specific port, making Torque into a TCP server. " + "When used in this manner, a callback is received when a client connection is made. Following " + "the outside connection, text may be sent and lines are processed in the usual manner.\n\n" + + "If you want to work with HTTP you may wish to use HTTPObject instead as it handles all of the " + "HTTP header setup and parsing.\n\n" + + "@tsexample\n" + "// In this example we'll retrieve the new forum threads RSS\n" + "// feed from garagegames.com. As we're using TCPObject, the\n" + "// raw text response will be received from the server, including\n" + "// the HTTP header.\n\n" + + "// Define callbacks for our specific TCPObject using our instance's\n" + "// name (RSSFeed) as the namespace.\n\n" + + "// Handle an issue with resolving the server's name\n" + "function RSSFeed::onDNSFailed(%this)\n" + "{\n" + " // Store this state\n" + " %this.lastState = \"DNSFailed\";\n\n" + + " // Handle DNS failure\n" + "}\n\n" + + "function RSSFeed::onConnectFailed(%this)\n" + "{\n" + " // Store this state\n" + " %this.lastState = \"ConnectFailed\";\n\n" + " // Handle connection failure\n" + "}\n\n" + + "function RSSFeed::onDNSResolved(%this)\n" + "{\n" + " // Store this state\n" + " %this.lastState = \"DNSResolved\";\n\n" + "}\n\n" + + "function RSSFeed::onConnected(%this)\n" + "{\n" + " // Store this state\n" + " %this.lastState = \"Connected\";\n\n" + "}\n\n" + + "function RSSFeed::onDisconnect(%this)\n" + "{\n" + " // Store this state\n" + " %this.lastState = \"Disconnected\";\n\n" + "}\n\n" + + "// Handle a line from the server\n" + "function RSSFeed::onLine(%this, %line)\n" + "{\n" + " // Print the line to the console\n" + " echo( %line );\n" + "}\n\n" + + "// Create the TCPObject\n" + "%rss = new TCPObject(RSSFeed);\n\n" + + "// Define a dynamic field to store the last connection state\n" + "%rss.lastState = \"None\";\n\n" + + "// Connect to the server\n" + "%rss.connect(\"www.garagegames.com:80\");\n\n" + + "// Send the RSS feed request to the server. Response will be\n" + "// handled in onLine() callback above\n" + "%rss.send(\"GET /feeds/rss/threads HTTP/1.1\\r\\nHost: www.garagegames.com\\r\\n\\r\\n\");\n" + "@endtsexample\n\n" + + "@see HTTPObject\n" + + "@ingroup Networking\n" +); + + +IMPLEMENT_CALLBACK(TCPObject, onConnectionRequest, void, (const char* address, const char* ID), (address, ID), + "@brief Called whenever a connection request is made.\n\n" + "This callback is used when the TCPObject is listening to a port and a client is attempting to connect.\n" + "@param address Server address connecting from.\n" + "@param ID Connection ID\n" + ); + +IMPLEMENT_CALLBACK(TCPObject, onLine, void, (const char* line), (line), + "@brief Called whenever a line of data is sent to this TCPObject.\n\n" + "This callback is called when the received data contains a newline @\\n character, or " + "the connection has been disconnected and the TCPObject's buffer is flushed.\n" + "@param line Data sent from the server.\n" + ); + +IMPLEMENT_CALLBACK(TCPObject, onDNSResolved, void, (),(), + "Called whenever the DNS has been resolved.\n" + ); + +IMPLEMENT_CALLBACK(TCPObject, onDNSFailed, void, (),(), + "Called whenever the DNS has failed to resolve.\n" + ); + +IMPLEMENT_CALLBACK(TCPObject, onConnected, void, (),(), + "Called whenever a connection is established with a server.\n" + ); + +IMPLEMENT_CALLBACK(TCPObject, onConnectFailed, void, (),(), + "Called whenever a connection has failed to be established with a server.\n" + ); + +IMPLEMENT_CALLBACK(TCPObject, onDisconnect, void, (),(), + "Called whenever the TCPObject disconnects from whatever it is currently connected to.\n" + ); + +TCPObject *TCPObject::find(NetSocket tag) +{ + for(TCPObject *walk = table[U32(tag) & TableMask]; walk; walk = walk->mNext) + if(walk->mTag == tag) + return walk; + return NULL; +} + +void TCPObject::addToTable(NetSocket newTag) +{ + removeFromTable(); + mTag = newTag; + mNext = table[U32(mTag) & TableMask]; + table[U32(mTag) & TableMask] = this; +} + +void TCPObject::removeFromTable() +{ + for(TCPObject **walk = &table[U32(mTag) & TableMask]; *walk; walk = &((*walk)->mNext)) + { + if(*walk == this) + { + *walk = mNext; + return; + } + } +} + +void processConnectedReceiveEvent(NetSocket sock, RawData incomingData); +void processConnectedAcceptEvent(NetSocket listeningPort, NetSocket newConnection, NetAddress originatingAddress); +void processConnectedNotifyEvent( NetSocket sock, U32 state ); + +S32 gTCPCount = 0; + +TCPObject::TCPObject() +{ + mBuffer = NULL; + mBufferSize = 0; + mPort = 0; + mTag = InvalidSocket; + mNext = NULL; + mState = Disconnected; + + gTCPCount++; + + if(gTCPCount == 1) + { + Net::smConnectionAccept.notify(processConnectedAcceptEvent); + Net::smConnectionReceive.notify(processConnectedReceiveEvent); + Net::smConnectionNotify.notify(processConnectedNotifyEvent); + } +} + +TCPObject::~TCPObject() +{ + disconnect(); + dFree(mBuffer); + + gTCPCount--; + + if(gTCPCount == 0) + { + Net::smConnectionAccept.remove(processConnectedAcceptEvent); + Net::smConnectionReceive.remove(processConnectedReceiveEvent); + Net::smConnectionNotify.remove(processConnectedNotifyEvent); + } +} + +bool TCPObject::processArguments(S32 argc, const char **argv) +{ + if(argc == 0) + return true; + else if(argc == 1) + { + addToTable(U32(dAtoi(argv[0]))); + return true; + } + return false; +} + +bool TCPObject::onAdd() +{ + if(!Parent::onAdd()) + return false; + + const char *name = getName(); + + if(name && name[0] && getClassRep()) + { + Namespace *parent = getClassRep()->getNameSpace(); + Con::linkNamespaces(parent->mName, name); + mNameSpace = Con::lookupNamespace(name); + + } + + Sim::getTCPGroup()->addObject(this); + + return true; +} + +U32 TCPObject::onReceive(U8 *buffer, U32 bufferLen) +{ + // we got a raw buffer event + // default action is to split the buffer into lines of text + // and call processLine on each + // for any incomplete lines we have mBuffer + U32 start = 0; + parseLine(buffer, &start, bufferLen); + return start; +} + +void TCPObject::parseLine(U8 *buffer, U32 *start, U32 bufferLen) +{ + // find the first \n in buffer + U32 i; + U8 *line = buffer + *start; + + for(i = *start; i < bufferLen; i++) + if(buffer[i] == '\n' || buffer[i] == 0) + break; + U32 len = i - *start; + + if(i == bufferLen || mBuffer) + { + // we've hit the end with no newline + mBuffer = (U8 *) dRealloc(mBuffer, mBufferSize + len + 1); + dMemcpy(mBuffer + mBufferSize, line, len); + mBufferSize += len; + *start = i; + + // process the line + if(i != bufferLen) + { + mBuffer[mBufferSize] = 0; + if(mBufferSize && mBuffer[mBufferSize-1] == '\r') + mBuffer[mBufferSize - 1] = 0; + U8 *temp = mBuffer; + mBuffer = 0; + mBufferSize = 0; + + processLine((UTF8*)temp); + dFree(temp); + } + } + else if(i != bufferLen) + { + line[len] = 0; + if(len && line[len-1] == '\r') + line[len-1] = 0; + processLine((UTF8*)line); + } + if(i != bufferLen) + *start = i + 1; +} + +void TCPObject::onConnectionRequest(const NetAddress *addr, U32 connectId) +{ + char idBuf[16]; + char addrBuf[256]; + Net::addressToString(addr, addrBuf); + dSprintf(idBuf, sizeof(idBuf), "%d", connectId); + onConnectionRequest_callback(addrBuf,idBuf); +} + +bool TCPObject::processLine(UTF8 *line) +{ + onLine_callback(line); + return true; +} + + + +void TCPObject::onDNSResolved() +{ + mState = DNSResolved; + onDNSResolved_callback(); +} + +void TCPObject::onDNSFailed() +{ + mState = Disconnected; + onDNSFailed_callback(); +} + + +void TCPObject::onConnected() +{ + mState = Connected; + onConnected_callback(); +} + +void TCPObject::onConnectFailed() +{ + mState = Disconnected; + onConnectFailed_callback(); +} + +void TCPObject::finishLastLine() +{ + if(mBufferSize) + { + mBuffer[mBufferSize] = 0; + processLine((UTF8*)mBuffer); + dFree(mBuffer); + mBuffer = 0; + mBufferSize = 0; + } +} + +void TCPObject::onDisconnect() +{ + finishLastLine(); + mState = Disconnected; + onDisconnect_callback(); +} + +void TCPObject::listen(U16 port) +{ + mState = Listening; + U32 newTag = Net::openListenPort(port); + addToTable(newTag); +} + +void TCPObject::connect(const char *address) +{ + NetSocket newTag = Net::openConnectTo(address); + addToTable(newTag); +} + +void TCPObject::disconnect() +{ + if( mTag != InvalidSocket ) { + Net::closeConnectTo(mTag); + } + removeFromTable(); +} + +void TCPObject::send(const U8 *buffer, U32 len) +{ + Net::sendtoSocket(mTag, buffer, S32(len)); +} + +DefineEngineMethod(TCPObject, send, void, (const char *data),, + "@brief Transmits the data string to the connected computer.\n\n" + + "This method is used to send text data to the connected computer regardless if we initiated the " + "connection using connect(), or listening to a port using listen().\n" + + "@param data The data string to send.\n" + + "@tsexample\n" + "// Set the command data\n" + "%data = \"GET \" @ $RSSFeed::serverURL @ \" HTTP/1.0\\r\\n\";\n" + "%data = %data @ \"Host: \" @ $RSSFeed::serverName @ \"\\r\\n\";\n" + "%data = %data @ \"User-Agent: \" @ $RSSFeed::userAgent @ \"\\r\\n\\r\\n\"\n\n" + + "// Send the command to the connected server.\n" + "%thisTCPObj.send(%data);\n" + "@endtsexample\n") +{ + object->send( (const U8*)data, dStrlen(data) ); +} + +DefineEngineMethod(TCPObject, listen, void, (int port),, + "@brief Start listening on the specified port for connections.\n\n" + + "@param port Port for this TCPObject to start listening for connections on.\n" + + "@tsexample\n" + "// Set the port number list\n" + "%portNumber = 80;\n\n" + + "// Inform this TCPObject to start listening at the specified port.\n" + "%thisTCPObj.send(%portNumber);\n" + "@endtsexample\n") +{ + object->listen(U32(port)); +} + +DefineEngineMethod(TCPObject, connect, void, (const char* address),, + "@brief Connect to the given address.\n\n" + + "@param address Server address (including port) to connect to.\n" + + "@tsexample\n" + "// Set the address.\n" + "%address = \"www.garagegames.com:80\";\n\n" + + "// Inform this TCPObject to connect to the specified address.\n" + "%thisTCPObj.connect(%address);\n" + "@endtsexample\n") +{ + object->connect(address); +} + +DefineEngineMethod(TCPObject, disconnect, void, (),, + "@brief Disconnect from whatever this TCPObject is currently connected to, if anything.\n\n" + + "@tsexample\n" + "// Inform this TCPObject to disconnect from anything it is currently connected to.\n" + "%thisTCPObj.disconnect();\n" + "@endtsexample\n") +{ + object->disconnect(); +} + +void processConnectedReceiveEvent(NetSocket sock, RawData incomingData) +{ + TCPObject *tcpo = TCPObject::find(sock); + if(!tcpo) + { + Con::printf("Got bad connected receive event."); + return; + } + + U32 size = incomingData.size; + U8 *buffer = (U8*)incomingData.data; + + while(size) + { + U32 ret = tcpo->onReceive(buffer, size); + AssertFatal(ret <= size, "Invalid return size"); + size -= ret; + buffer += ret; + } +} + +void processConnectedAcceptEvent(NetSocket listeningPort, NetSocket newConnection, NetAddress originatingAddress) +{ + TCPObject *tcpo = TCPObject::find(listeningPort); + if(!tcpo) + return; + + tcpo->onConnectionRequest(&originatingAddress, newConnection); +} + +void processConnectedNotifyEvent( NetSocket sock, U32 state ) +{ + TCPObject *tcpo = TCPObject::find(sock); + if(!tcpo) + return; + + switch(state) + { + case Net::DNSResolved: + tcpo->onDNSResolved(); + break; + case Net::DNSFailed: + tcpo->onDNSFailed(); + break; + case Net::Connected: + tcpo->onConnected(); + break; + case Net::ConnectFailed: + tcpo->onConnectFailed(); + break; + case Net::Disconnected: + tcpo->onDisconnect(); + break; + } +} diff --git a/Engine/source/app/net/tcpObject.h b/Engine/source/app/net/tcpObject.h new file mode 100644 index 000000000..a438f35bc --- /dev/null +++ b/Engine/source/app/net/tcpObject.h @@ -0,0 +1,96 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TCPOBJECT_H_ +#define _TCPOBJECT_H_ + +#ifndef _SIMBASE_H_ +#include "console/simBase.h" +#endif + +#include "platform/platformNet.h" + +class TCPObject : public SimObject +{ +public: + enum State {Disconnected, DNSResolved, Connected, Listening }; + + DECLARE_CALLBACK(void, onConnectionRequest, (const char* address, const char* ID)); + DECLARE_CALLBACK(void, onLine, (const char* line)); + DECLARE_CALLBACK(void, onDNSResolved,()); + DECLARE_CALLBACK(void, onDNSFailed, ()); + DECLARE_CALLBACK(void, onConnected, ()); + DECLARE_CALLBACK(void, onConnectFailed, ()); + DECLARE_CALLBACK(void, onDisconnect, ()); + +private: + NetSocket mTag; + TCPObject *mNext; + enum { TableSize = 256, TableMask = 0xFF }; + static TCPObject *table[TableSize]; + State mState; + +protected: + typedef SimObject Parent; + U8 *mBuffer; + U32 mBufferSize; + U16 mPort; + +public: + TCPObject(); + virtual ~TCPObject(); + + void parseLine(U8 *buffer, U32 *start, U32 bufferLen); + void finishLastLine(); + + static TCPObject *find(NetSocket tag); + + // onReceive gets called continuously until all bytes are processed + // return # of bytes processed each time. + virtual U32 onReceive(U8 *buffer, U32 bufferLen); // process a buffer of raw packet data + virtual bool processLine(UTF8 *line); // process a complete line of text... default action is to call into script + virtual void onDNSResolved(); + virtual void onDNSFailed(); + virtual void onConnected(); + virtual void onConnectFailed(); + virtual void onConnectionRequest(const NetAddress *addr, U32 connectId); + virtual void onDisconnect(); + void connect(const char *address); + void listen(U16 port); + void disconnect(); + State getState() { return mState; } + + bool processArguments(S32 argc, const char **argv); + void send(const U8 *buffer, U32 bufferLen); + void addToTable(NetSocket newTag); + void removeFromTable(); + + void setPort(U16 port) { mPort = port; } + + bool onAdd(); + + DECLARE_CONOBJECT(TCPObject); + +}; + + +#endif // _H_TCPOBJECT_ diff --git a/Engine/source/app/version.cpp b/Engine/source/app/version.cpp new file mode 100644 index 000000000..b373e98a4 --- /dev/null +++ b/Engine/source/app/version.cpp @@ -0,0 +1,132 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "app/version.h" +#include "console/console.h" + +static const U32 csgVersionNumber = TORQUE_GAME_ENGINE; + +U32 getVersionNumber() +{ + return csgVersionNumber; +} + +const char* getVersionString() +{ + return TORQUE_GAME_ENGINE_VERSION_STRING; +} + +/// TGE 0001 +/// TGEA 0002 +/// TGB 0003 +/// TGEA 360 0004 +/// TGE WII 0005 +/// Torque 3D 0006 + +const char* getEngineProductString() +{ +#ifndef TORQUE_ENGINE_PRODUCT + return "Torque Engine"; +#else + switch (TORQUE_ENGINE_PRODUCT) + { + case 0001: + return "Torque Game Engine"; + case 0002: + return "Torque Game Engine Advanced"; + case 0003: + return "Torque 2D"; + case 0004: + return "Torque 360"; + case 0005: + return "Torque for Wii"; + case 0006: + return "Torque 3D"; + + default: + return "Torque Engine"; + }; +#endif +} + +const char* getCompileTimeString() +{ + return __DATE__ " at " __TIME__; +} +//---------------------------------------------------------------- + +ConsoleFunctionGroupBegin( CompileInformation, "Functions to get version information about the current executable." ); + +ConsoleFunction( getVersionNumber, S32, 1, 1, "Get the version of the build, as a string.\n\n" + "@ingroup Debugging") +{ + return getVersionNumber(); +} + +ConsoleFunction( getVersionString, const char*, 1, 1, "Get the version of the build, as a string.\n\n" + "@ingroup Debugging") +{ + return getVersionString(); +} + +ConsoleFunction( getEngineName, const char*, 1, 1, "Get the name of the engine product that this is running from, as a string.\n\n" + "@ingroup Debugging") +{ + return getEngineProductString(); +} + +ConsoleFunction( getCompileTimeString, const char*, 1, 1, "Get the time of compilation.\n\n" + "@ingroup Debugging") +{ + return getCompileTimeString(); +} + +ConsoleFunction( getBuildString, const char*, 1, 1, "Get the type of build, \"Debug\" or \"Release\".\n\n" + "@ingroup Debugging") +{ +#ifdef TORQUE_DEBUG + return "Debug"; +#else + return "Release"; +#endif +} + +ConsoleFunctionGroupEnd( CompileInformation ); + +ConsoleFunction(isDemo, bool, 1, 1, "") +{ +#ifdef TORQUE_DEMO + return true; +#else + return false; +#endif +} + +ConsoleFunction(isWebDemo, bool, 1, 1, "") +{ +#ifdef TORQUE_DEMO + return Platform::getWebDeployment(); +#else + return false; +#endif +} \ No newline at end of file diff --git a/Engine/source/app/version.h b/Engine/source/app/version.h new file mode 100644 index 000000000..424138a93 --- /dev/null +++ b/Engine/source/app/version.h @@ -0,0 +1,45 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _VERSION_H_ +#define _VERSION_H_ + +/// This is our global version number for the engine source code that +/// we are using. See /source/torqueConfig.h for the game's source +/// code version, the game name, and which type of game it is (TGB, TGE, TGEA, etc.). +/// +/// Version number is major * 1000 + minor * 100 + revision * 10. +#define TORQUE_GAME_ENGINE 1100 + +/// Human readable engine version string. +#define TORQUE_GAME_ENGINE_VERSION_STRING "2011" + +/// Gets the specified version number. The version number is specified as a global in version.cc +U32 getVersionNumber(); +/// Gets the version number in string form +const char* getVersionString(); +/// Gets the engine product name in string form +const char* getEngineProductString(); +/// Gets the compile date and time +const char* getCompileTimeString(); + +#endif diff --git a/Engine/source/cinterface/c_consoleInterface.cpp b/Engine/source/cinterface/c_consoleInterface.cpp new file mode 100644 index 000000000..b23c0b56d --- /dev/null +++ b/Engine/source/cinterface/c_consoleInterface.cpp @@ -0,0 +1,173 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/compiler.h" +#include "console/consoleInternal.h" +#include "console/simSet.h" + +extern "C" { + + // SimObject C interface + const char *SimObject_GetName(SimObject *so) + { + return so->getName(); + } + + U32 SimObject_GetId(SimObject *so) + { + return so->getId(); + } + + const char *SimObject_GetClassName(SimObject *so) + { + return so->getClassName(); + } + + void *SimObject_GetFieldList(SimObject *so, int &outNumFields) + { + const AbstractClassRep::FieldList &fl = so->getFieldList(); + outNumFields = fl.size(); + return fl.address(); + } + + bool SimObject_IsLocked(SimObject *so) + { + return so->isLocked(); + } + + void SimObject_SetDataField(SimObject *so, const char *fieldName, const char *arr, const char *val) + { + so->setDataField(StringTable->insert(fieldName), arr, val); + } + + const char *SimObject_GetDataField(SimObject *so, const char *fieldName, const char *arr) + { + return so->getDataField(StringTable->insert(fieldName), arr); + } + + void SimObject_InspectPreApply(SimObject *so) + { + so->inspectPreApply(); + } + + void SimObject_InspectPostApply(SimObject *so) + { + so->inspectPostApply(); + } + + // Con C interface + void Con_AddConsumer(ConsumerCallback cb) + { + Con::addConsumer(cb); + } + + void Con_RemoveConsumer(ConsumerCallback cb) + { + Con::removeConsumer(cb); + } + + void Con_AddCommand_String(StringCallback cb, const char *nameSpace, const char *funcName, const char* usage, S32 minArgs, S32 maxArgs) + { + if (!nameSpace || !dStrlen(nameSpace)) + Con::addCommand(funcName, cb, usage, minArgs + 1, maxArgs + 1); + else + Con::addCommand(nameSpace, funcName, cb, usage, minArgs + 1, maxArgs + 1); + } + + // ConsoleBaseType C interface + ConsoleBaseType *ConsoleBaseType_GetTypeById(const S32 typeId) + { + return ConsoleBaseType::getType(typeId); + } + + S32 ConsoleBaseType_GetTypeId(ConsoleBaseType *cbt) + { + return cbt->getTypeID(); + } + + S32 ConsoleBaseType_GetTypeSize(ConsoleBaseType *cbt) + { + return cbt->getTypeSize(); + } + + const char *ConsoleBaseType_GetTypeName(ConsoleBaseType *cbt) + { + return cbt->getTypeName(); + } + + const char *ConsoleBaseType_GetInspectorFieldType(ConsoleBaseType *cbt) + { + return cbt->getInspectorFieldType(); + } + + void ConsoleBaseType_SetData(ConsoleBaseType *cbt, void *dptr, S32 argc, const char **argv, const EnumTable *tbl, BitSet32 flag) + { + return cbt->setData(dptr, argc, argv, tbl, flag); + } + + const char *ConsoleBaseType_GetData(ConsoleBaseType *cbt, void *dptr, const EnumTable *tbl, BitSet32 flag) + { + return cbt->getData(dptr, tbl, flag); + } + + // Abstract Class Rep + AbstractClassRep *AbstractClassRep_GetCommonParent(AbstractClassRep *acr, AbstractClassRep *otheracr) + { + return acr->getCommonParent(otheracr); + } + + AbstractClassRep *AbstractClassRep_FindClassRep(const char* in_pClassName) + { + return AbstractClassRep::findClassRep(in_pClassName); + } + + U32 AbstractClassRep_GetFieldStructSize() + { + return sizeof(AbstractClassRep::Field); + } + + // Sim C interface + SimObject *Sim_FindObjectByString(const char *param) + { + return Sim::findObject(param); + } + + SimObject *Sim_FindObjectById(S32 param) + { + return Sim::findObject(param); + } + + // Sim Set + SimObject **SimSet_Begin(SimObject *simObject) + { + return dynamic_cast(simObject)->begin(); + } + + SimObject **SimSet_End(SimObject *simObject) + { + return dynamic_cast(simObject)->end(); + } + +}; + + diff --git a/Engine/source/cinterface/c_scripting.cpp b/Engine/source/cinterface/c_scripting.cpp new file mode 100644 index 000000000..e6448089b --- /dev/null +++ b/Engine/source/cinterface/c_scripting.cpp @@ -0,0 +1,420 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/compiler.h" +#include "console/console.h" +#include "console/consoleInternal.h" +#include "core/util/tDictionary.h" +#include "app/mainLoop.h" + +// External scripting cinterface, suitable for import into any scripting system which support "C" interfaces (C#, Python, Lua, Java, etc) + +#ifdef TORQUE_OS_WIN32 +#include "windowManager/win32/win32Window.h" +#include "windowManager/win32/winDispatch.h" +#endif + +extern "C" { + + struct MarshalNativeEntry + { + const char* nameSpace; + const char* name; + Namespace::Entry* entry; + S32 minArgs; + S32 maxArgs; + S32 cbType; + }; + + + static Namespace::Entry* GetEntry(const char* nameSpace, const char* name) + { + Namespace* ns = NULL; + + if (!nameSpace || !dStrlen(nameSpace)) + ns = Namespace::mGlobalNamespace; + else + { + nameSpace = StringTable->insert(nameSpace); + ns = Namespace::find(nameSpace); //can specify a package here, maybe need, maybe not + } + + if (!ns) + return NULL; + + name = StringTable->insert(name); + + Namespace::Entry* entry = ns->lookupRecursive(name); + + return entry; + } + + const char * script_getconsolexml() + { + Namespace::Entry* entry = GetEntry("", "consoleExportXML"); + + if (!entry) + return ""; + + const char* argv[] = {"consoleExportXML", 0}; + + return entry->cb.mStringCallbackFunc(NULL, 1, argv); + } + + MarshalNativeEntry* script_get_namespace_entry(const char* nameSpace, const char* name) + { + static MarshalNativeEntry mentry; + + Namespace::Entry* e = GetEntry(nameSpace, name); + + if (!e) + return NULL; + + mentry.nameSpace = e->mNamespace->mName; + mentry.name = e->mFunctionName; + mentry.minArgs = e->mMinArgs; + mentry.maxArgs = e->mMaxArgs; + mentry.cbType = e->mType; + mentry.entry = e; + + return &mentry; + } + + void* script_get_stringtable_entry(const char* string) + { + return (void*)StringTable->insert(string); + } + + // FIELD ACCESS + + // fieldNames must be from stringTable coming in! See Engine.stringTable + + const char* script_simobject_getfield_string(U32 id, const char* fieldName) + { + SimObject *object = Sim::findObject( id ); + if( object ) + { + return (const char *) object->getDataField(fieldName, ""); + } + return ""; + } + + void script_simobject_setfield_string(U32 objectId, const char* fieldName, const char* v) + { + SimObject *object = Sim::findObject( objectId ); + if( object ) + { + object->setDataField(fieldName, "", v); + } + } + + + bool script_simobject_getfield_bool(U32 objectId, const char* fieldName) + { + SimObject *object = Sim::findObject( objectId ); + if( object ) + { + const char *v = object->getDataField(fieldName, ""); + + return dAtob(v); + } + + return false; + } + + void script_simobject_setfield_bool(U32 objectId, const char* fieldName, bool v) + { + SimObject *object = Sim::findObject( objectId ); + if( object ) + { + object->setDataField(fieldName, "", v ? "1" : "0"); + } + } + + S32 script_simobject_getfield_int(U32 objectId, const char* fieldName) + { + SimObject *object = Sim::findObject( objectId ); + if( object ) + { + const char *v = object->getDataField(fieldName, ""); + + return dAtoi(v); + } + + return false; + } + + void script_simobject_setfield_int(U32 objectId, const char* fieldName, int v) + { + SimObject *object = Sim::findObject( objectId ); + if( object ) + { + // this seems pretty lame, though it is how it is handled in consoleType.cpp + char buf[256]; + dSprintf(buf, 256, "%d", v ); + object->setDataField(fieldName, "", buf); + } + } + + F32 script_simobject_getfield_float(U32 objectId, const char* fieldName) + { + SimObject *object = Sim::findObject( objectId ); + if( object ) + { + const char *v = object->getDataField(fieldName, ""); + + return dAtof(v); + } + + return false; + } + + void script_simobject_setfield_float(U32 objectId, const char* fieldName, F32 v) + { + SimObject *object = Sim::findObject( objectId ); + if( object ) + { + char buf[256]; + dSprintf(buf, 256, "%g", v ); + object->setDataField(fieldName, "", buf); + } + } + + const char* script_call_namespace_entry_string(Namespace::Entry* entry, S32 argc, const char** argv) + { + // maxArgs improper on a number of console function/methods + if (argc < entry->mMinArgs)// || argc > entry->mMaxArgs) + return ""; + + SimObject* o = NULL; + + if (entry->mNamespace && entry->mNamespace->isClass()) + { + o = Sim::findObject(dAtoi(argv[1])); + if (!o) + return ""; + } + + return entry->cb.mStringCallbackFunc(o, argc, argv); + } + + bool script_call_namespace_entry_bool(Namespace::Entry* entry, S32 argc, const char** argv) + { + // maxArgs improper on a number of console function/methods + if (argc < entry->mMinArgs)// || argc > entry->mMaxArgs) + return ""; + + SimObject* o = NULL; + + if (entry->mNamespace && entry->mNamespace->isClass()) + { + o = Sim::findObject(dAtoi(argv[1])); + if (!o) + return ""; + } + + return entry->cb.mBoolCallbackFunc(o, argc, argv); + } + + S32 script_call_namespace_entry_int(Namespace::Entry* entry, S32 argc, const char** argv) + { + // maxArgs improper on a number of console function/methods + if (argc < entry->mMinArgs)// || argc > entry->mMaxArgs) + return 0; + + SimObject* o = NULL; + + if (entry->mNamespace && entry->mNamespace->isClass()) + { + o = Sim::findObject(dAtoi(argv[1])); + if (!o) + return 0; + } + + return entry->cb.mIntCallbackFunc(o, argc, argv); + } + + F32 script_call_namespace_entry_float(Namespace::Entry* entry, S32 argc, const char** argv) + { + // maxArgs improper on a number of console function/methods + if (argc < entry->mMinArgs)// || argc > entry->mMaxArgs) + return 0.0f; + + SimObject* o = NULL; + + if (entry->mNamespace && entry->mNamespace->isClass()) + { + o = Sim::findObject(dAtoi(argv[1])); + if (!o) + return 0.0f; + } + + return entry->cb.mFloatCallbackFunc(o, argc, argv); + } + + + void script_call_namespace_entry_void(Namespace::Entry* entry, S32 argc, const char** argv) + { + // maxArgs improper on a number of console function/methods + if (argc < entry->mMinArgs)// || argc > entry->mMaxArgs) + return; + + SimObject* o = NULL; + + if (entry->mNamespace && entry->mNamespace->isClass()) + { + Sim::findObject(dAtoi(argv[1])); + if (!o) + return; + } + + entry->cb.mVoidCallbackFunc(o, argc, argv); + } + + int script_simobject_get_id(SimObject* so) + { + return so->getId(); + } + + int script_simobject_find(const char* classname, const char* name) + { + SimObject *object; + if( Sim::findObject( name, object ) ) + { + // if we specified a classname do type checking + if (classname && dStrlen(classname)) + { + AbstractClassRep* ocr = object->getClassRep(); + while (ocr) + { + if (!dStricmp(ocr->getClassName(), classname)) + return object->getId(); + ocr = ocr->getParentClass(); + } + + } + + // invalid type + return 0; + } + + // didn't find object + return 0; + } + + void script_export_callback_string(StringCallback cb, const char *nameSpace, const char *funcName, const char* usage, S32 minArgs, S32 maxArgs) + { + if (!nameSpace || !dStrlen(nameSpace)) + Con::addCommand(funcName, cb, usage, minArgs + 1, maxArgs + 1); + else + Con::addCommand(nameSpace, funcName, cb, usage, minArgs + 1, maxArgs + 1); + } + + void script_export_callback_void(VoidCallback cb, const char *nameSpace, const char *funcName, const char* usage, S32 minArgs, S32 maxArgs) + { + + if (!nameSpace || !dStrlen(nameSpace)) + Con::addCommand(funcName, cb, usage, minArgs + 1, maxArgs + 1); + else + Con::addCommand(nameSpace, funcName, cb, usage, minArgs + 1, maxArgs + 1); + + + + // example of package support + // note that Parent:: does not work with this, at least not yet anyway + + /* + Namespace* ns; + + StringTableEntry nspace = NULL; + + if (nameSpace && dStrlen(nameSpace)) + nspace = StringTable->insert(nameSpace); + + Namespace::unlinkPackages(); + ns = Namespace::find(nspace, StringTable->insert("fps")); + ns->addCommand(StringTable->insert(funcName), cb, StringTable->insert(usage), minArgs + 1, maxArgs + 1 ); + Namespace::relinkPackages(); + */ + } + + void script_export_callback_bool(BoolCallback cb, const char *nameSpace, const char *funcName, const char* usage, S32 minArgs, S32 maxArgs) + { + if (!nameSpace || !dStrlen(nameSpace)) + Con::addCommand(funcName, cb, usage, minArgs + 1, maxArgs + 1); + else + Con::addCommand(nameSpace, funcName, cb, usage, minArgs + 1, maxArgs + 1); + } + + void script_export_callback_int(IntCallback cb, const char *nameSpace, const char *funcName, const char* usage, S32 minArgs, S32 maxArgs) + { + if (!nameSpace || !dStrlen(nameSpace)) + Con::addCommand(funcName, cb, usage, minArgs + 1, maxArgs + 1); + else + Con::addCommand(nameSpace, funcName, cb, usage, minArgs + 1, maxArgs + 1); + } + + void script_export_callback_float(FloatCallback cb, const char *nameSpace, const char *funcName, const char* usage, S32 minArgs, S32 maxArgs) + { + if (!nameSpace || !dStrlen(nameSpace)) + Con::addCommand(funcName, cb, usage, minArgs + 1, maxArgs + 1); + else + Con::addCommand(nameSpace, funcName, cb, usage, minArgs + 1, maxArgs + 1); + } + + +#ifdef TORQUE_OS_WIN32 + + void script_input_event(int type, int value1, int value2) + { + if (PlatformWindowManager::get() && PlatformWindowManager::get()->getFirstWindow()) + { + Win32Window* w = (Win32Window*) PlatformWindowManager::get()->getFirstWindow(); + WindowId devId = w->getWindowId(); + + switch (type) + { + case 0: + w->mouseEvent.trigger(devId,0,value1,value2,w->isMouseLocked()); + break; + case 1: + if (value2) + w->buttonEvent.trigger(devId,0,IA_MAKE,value1); + else + w->buttonEvent.trigger(devId,0,IA_BREAK,value1); + break; + + } + + } + + } +#endif + +} + + +ConsoleFunction(TestFunction2Args, const char *, 3, 3, "testFunction(arg1, arg2)") +{ + return "Return Value"; +} diff --git a/Engine/source/cinterface/cinterface.cpp b/Engine/source/cinterface/cinterface.cpp new file mode 100644 index 000000000..f5b1c1412 --- /dev/null +++ b/Engine/source/cinterface/cinterface.cpp @@ -0,0 +1,481 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/compiler.h" +#include "console/consoleInternal.h" +#include "core/util/tDictionary.h" +#include "core/strings/stringFunctions.h" +#include "app/mainLoop.h" +#include "windowManager/platformWindow.h" +#include "windowManager/platformWindowMgr.h" + +#ifdef TORQUE_OS_WIN32 +#include "windowManager/win32/win32Window.h" +#include "windowManager/win32/winDispatch.h" +extern void createFontInit(void); +extern void createFontShutdown(void); +#endif + +#if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE ) + extern INT CreateMiniDump(LPEXCEPTION_POINTERS ExceptionInfo); +#endif + +static HashTable gSecureScript; + +#ifdef TORQUE_OS_MAC + +// ObjC hooks for shared library support +// See: macMain.mm + +void torque_mac_engineinit(int argc, const char **argv); +void torque_mac_enginetick(); +void torque_mac_engineshutdown(); + +#endif + +extern bool LinkConsoleFunctions; + +extern "C" { + + // reset the engine, unloading any current level and returning to the main menu + void torque_reset() + { + Con::evaluate("disconnect();"); + } + + // initialize Torque 3D including argument handling + int torque_engineinit(S32 argc, const char **argv) + { + +#if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE ) + __try { +#endif + + LinkConsoleFunctions = true; + +#if !defined(TORQUE_OS_XENON) && !defined(TORQUE_OS_PS3) && defined(_MSC_VER) + createFontInit(); +#endif + + +#ifdef TORQUE_OS_MAC + torque_mac_engineinit(argc, argv); +#endif + // Initialize the subsystems. + StandardMainLoop::init(); + + // Handle any command line args. + if(!StandardMainLoop::handleCommandLine(argc, argv)) + { + Platform::AlertOK("Error", "Failed to initialize game, shutting down."); + return false; + } + +#if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE ) + } + + __except( CreateMiniDump(GetExceptionInformation()) ) + { + _exit(0); + } +#endif + + return true; + + } + + // tick Torque 3D's main loop + int torque_enginetick() + { + +#if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE ) + __try { +#endif + + +#ifdef TORQUE_OS_MAC + torque_mac_enginetick(); +#endif + + bool ret = StandardMainLoop::doMainLoop(); + return ret; + +#if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE ) + } + __except( CreateMiniDump(GetExceptionInformation()) ) + { + _exit(0); + } +#endif + + + + } + + // signal an engine shutdown (as with the quit(); console command) + void torque_enginesignalshutdown() + { + Con::evaluate("quit();"); + } + + // shutdown the engine + int torque_engineshutdown() + { + +#if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE ) + __try { +#endif + + // Clean everything up. + StandardMainLoop::shutdown(); + +#if !defined(TORQUE_OS_XENON) && !defined(TORQUE_OS_PS3) && defined(_MSC_VER) + createFontShutdown(); +#endif + +#ifdef TORQUE_OS_MAC + torque_mac_engineshutdown(); +#endif + +#if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE ) + } + + __except( CreateMiniDump(GetExceptionInformation()) ) + { + _exit(0); + } +#endif + + // Return. + return true; + + } + + bool torque_isdebugbuild() + { +#ifdef _DEBUG + return true; +#else + return false; +#endif + + } + + int torque_getconsolebool(const char* name) + { + return Con::getBoolVariable(name); + } + + void torque_setconsolebool(const char* name, bool value) + { + Con::setBoolVariable(name, value); + } + + static char* gExecutablePath = NULL; + + const char* torque_getexecutablepath() + { + return gExecutablePath; + } + + void torque_setexecutablepath(const char* directory) + { + gExecutablePath = new char[strlen(directory)+1]; + strcpy(gExecutablePath, directory); + } + + // set Torque 3D into web deployment mode (disable fullscreen exlusive mode, etc) + void torque_setwebdeployment() + { + Platform::setWebDeployment(true); + } + + // Get a console variable + const char* torque_getvariable(const char* name) + { + return Con::getVariable(StringTable->insert(name)); + } + + // Set a console variable + void torque_setvariable(const char* name, const char* value) + { + Con::setVariable(StringTable->insert(name), StringTable->insert(value)); + } + + static Namespace::Entry* GetEntry(const char* nameSpace, const char* name) + { + Namespace* ns = NULL; + + if (!nameSpace || !dStrlen(nameSpace)) + ns = Namespace::mGlobalNamespace; + else + { + nameSpace = StringTable->insert(nameSpace); + ns = Namespace::find(nameSpace); //can specify a package here, maybe need, maybe not + } + + if (!ns) + return NULL; + + name = StringTable->insert(name); + + Namespace::Entry* entry = ns->lookupRecursive(name); + + return entry; + } + + // Export a function to the Torque 3D console system which matches the StringCallback function prototype + // specify the nameSpace, functionName, usage, min and max arguments + void torque_exportstringcallback(StringCallback cb, const char *nameSpace, const char *funcName, const char* usage, S32 minArgs, S32 maxArgs) + { + if (!nameSpace || !dStrlen(nameSpace)) + Con::addCommand(funcName, cb, usage, minArgs + 1, maxArgs + 1); + else + Con::addCommand(nameSpace, funcName, cb, usage, minArgs + 1, maxArgs + 1); + } + + void torque_callvoidfunction(const char* nameSpace, const char* name, S32 argc, const char ** argv) + { + + Namespace::Entry* entry = GetEntry(nameSpace, name); + + if (!entry) + return; + + entry->cb.mVoidCallbackFunc(NULL, argc, argv); + } + + F32 torque_callfloatfunction(const char* nameSpace, const char* name, S32 argc, const char ** argv) + { + + Namespace::Entry* entry = GetEntry(nameSpace, name); + + if (!entry) + return 0.0f; + + return entry->cb.mFloatCallbackFunc(NULL, argc, argv); + } + + S32 torque_callintfunction(const char* nameSpace, const char* name, S32 argc, const char ** argv) + { + + Namespace::Entry* entry = GetEntry(nameSpace, name); + + if (!entry) + return 0; + + return entry->cb.mIntCallbackFunc(NULL, argc, argv); + } + + + const char * torque_callstringfunction(const char* nameSpace, const char* name, S32 argc, const char ** argv) + { + Namespace::Entry* entry = GetEntry(nameSpace, name); + + if (!entry) + return ""; + + return entry->cb.mStringCallbackFunc(NULL, argc, argv); + } + + bool torque_callboolfunction(const char* nameSpace, const char* name, S32 argc, const char ** argv) + { + Namespace::Entry* entry = GetEntry(nameSpace, name); + + if (!entry) + return ""; + + return entry->cb.mBoolCallbackFunc(NULL, argc, argv); + } + + + const char * torque_callscriptfunction(const char* nameSpace, const char* name, S32 argc, const char ** argv) + { + Namespace::Entry* entry = GetEntry(nameSpace, name); + + if (!entry) + return ""; + + if(!entry->mFunctionOffset) + return ""; + + const char* ret = entry->mCode->exec(entry->mFunctionOffset, StringTable->insert(name), entry->mNamespace, argc, argv, false, entry->mPackage); + + if (!ret || !dStrlen(ret)) + return ""; + + return ret; + + } + + + // Call a TorqueScript console function that has been marked as secure + const char* torque_callsecurefunction(const char* nameSpace, const char* name, S32 argc, const char ** argv) + { + static const char* invalidChars = "()=:{}"; + String s = nameSpace; + s += "::"; + s += name; + s = String::ToUpper(s); + + if (!gSecureScript.count(StringTable->insert(s.c_str()))) + { + Con::warnf("\nAttempt to call insecure script: %s\n", s.c_str()); + return ""; + } + + // scan through for invalid characters + for (S32 i = 0; i < argc ; i++) + for (S32 j = 0; j < dStrlen(invalidChars) ; j++) + for (S32 k = 0; k < dStrlen(argv[i]); k++) + if (invalidChars[j] == argv[i][k]) + { + Con::warnf("\nInvalid parameter passed to secure script: %s, %s\n", s.c_str(), argv[i]); + return ""; + } + + Namespace::Entry* entry = GetEntry(nameSpace, name); + + if (!entry) + return ""; + + static char returnBuffer[32]; + + switch(entry->mType) + { + case Namespace::Entry::ConsoleFunctionType: + return torque_callscriptfunction(nameSpace, name, argc, argv); + + case Namespace::Entry::StringCallbackType: + return torque_callstringfunction(nameSpace, name, argc, argv); + + case Namespace::Entry::IntCallbackType: + dSprintf(returnBuffer, sizeof(returnBuffer), "%d", torque_callintfunction(nameSpace, name, argc, argv)); + return returnBuffer; + + case Namespace::Entry::FloatCallbackType: + dSprintf(returnBuffer, sizeof(returnBuffer), "%g", torque_callfloatfunction(nameSpace, name, argc, argv)); + return returnBuffer; + + case Namespace::Entry::VoidCallbackType: + torque_callvoidfunction(nameSpace, name, argc, argv); + return ""; + + case Namespace::Entry::BoolCallbackType: + dSprintf(returnBuffer, sizeof(returnBuffer), "%d", (U32) torque_callboolfunction(nameSpace, name, argc, argv)); + return returnBuffer; + }; + + return ""; + + } + + // Set a TorqueScript console function as secure and available for JavaScript via the callScript plugin method + void torque_addsecurefunction(const char* nameSpace, const char* fname) + { + String s = nameSpace; + s += "::"; + s += fname; + s = String::ToUpper(s); + + gSecureScript.insertEqual(StringTable->insert(s.c_str()), StringTable->insert(s.c_str())); + } + + + // Evaluate arbitrary TorqueScript (ONLY CALL torque_evaluate FROM TRUSTED CODE!!!) + const char* torque_evaluate(const char* code) + { + return Con::evaluate(code); + } + + // resize the Torque 3D child window to the specified width and height + void torque_resizewindow(S32 width, S32 height) + { + if (PlatformWindowManager::get() && PlatformWindowManager::get()->getFirstWindow()) + PlatformWindowManager::get()->getFirstWindow()->setSize(Point2I(width,height)); + } + +#ifdef TORQUE_OS_WIN32 + // retrieve the hwnd of our render window + void* torque_gethwnd() + { + if (PlatformWindowManager::get() && PlatformWindowManager::get()->getFirstWindow()) + { + Win32Window* w = (Win32Window*) PlatformWindowManager::get()->getFirstWindow(); + return (void *) w->getHWND(); + } + + return NULL; + } + + // directly add a message to the Torque 3D event queue, bypassing the Windows event queue + // this is useful in the case of the IE plugin, where we are hooking into an application + // level message, and posting to the windows queue would cause a hang + void torque_directmessage(U32 message, U32 wparam, U32 lparam) + { + if (PlatformWindowManager::get() && PlatformWindowManager::get()->getFirstWindow()) + { + Win32Window* w = (Win32Window*) PlatformWindowManager::get()->getFirstWindow(); + Dispatch(DelayedDispatch,w->getHWND(),message,wparam,lparam); + } + } + +#endif +} + +// This function is solely to test the TorqueScript <-> Javascript binding +// By default, it is marked as secure by the web plugins and then can be called from +// Javascript on the web page to ensure that function calls across the language +// boundry are working with arguments and return values +ConsoleFunction(testJavaScriptBridge, const char *, 4, 4, "testBridge(arg1, arg2, arg3)") +{ + S32 failed = 0; + if(argc != 4) + failed = 1; + else + { + if (dStrcmp(argv[1],"one")) + failed = 2; + if (dStrcmp(argv[2],"two")) + failed = 2; + if (dStrcmp(argv[3],"three")) + failed = 2; + } + + //attempt to call from TorqueScript -> JavaScript + const char* jret = Con::evaluate("JS::bridgeCallback(\"one\",\"two\",\"three\");"); + + if (dStrcmp(jret,"42")) + failed = 3; + + char *ret = Con::getReturnBuffer(256); + + dSprintf(ret, 256, "%i", failed); + + return ret; +} + + + + + diff --git a/Engine/source/cinterface/cinterface.h b/Engine/source/cinterface/cinterface.h new file mode 100644 index 000000000..d7f839331 --- /dev/null +++ b/Engine/source/cinterface/cinterface.h @@ -0,0 +1,30 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#pragma once + +// cinterface can override this (useful for plugins, etc) +extern "C" { + +const char* torque_getexecutablepath(); + +} diff --git a/Engine/source/collision/abstractPolyList.cpp b/Engine/source/collision/abstractPolyList.cpp new file mode 100644 index 000000000..c33a963bf --- /dev/null +++ b/Engine/source/collision/abstractPolyList.cpp @@ -0,0 +1,114 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "collision/abstractPolyList.h" + + +//---------------------------------------------------------------------------- + +AbstractPolyList::~AbstractPolyList() +{ + mInterestNormalRegistered = false; +} + +static U32 PolyFace[6][4] = { + { 3, 2, 1, 0 }, + { 7, 4, 5, 6 }, + { 0, 5, 4, 3 }, + { 6, 5, 0, 1 }, + { 7, 6, 1, 2 }, + { 4, 7, 2, 3 }, +}; + +void AbstractPolyList::addBox(const Box3F &box, BaseMatInstance* material) +{ + Point3F pos = box.minExtents; + F32 dx = box.maxExtents.x - box.minExtents.x; + F32 dy = box.maxExtents.y - box.minExtents.y; + F32 dz = box.maxExtents.z - box.minExtents.z; + + U32 base = addPoint(pos); + pos.y += dy; addPoint(pos); + pos.x += dx; addPoint(pos); + pos.y -= dy; addPoint(pos); + pos.z += dz; addPoint(pos); + pos.x -= dx; addPoint(pos); + pos.y += dy; addPoint(pos); + pos.x += dx; addPoint(pos); + + for (S32 i = 0; i < 6; i++) { + begin(material, i); + S32 v1 = base + PolyFace[i][0]; + S32 v2 = base + PolyFace[i][1]; + S32 v3 = base + PolyFace[i][2]; + S32 v4 = base + PolyFace[i][3]; + vertex(v1); + vertex(v2); + vertex(v3); + vertex(v4); + plane(v1, v2, v3); + end(); + } +} + +bool AbstractPolyList::getMapping(MatrixF *, Box3F *) +{ + // return list transform and bounds in list space...optional + return false; +} + + +bool AbstractPolyList::isInterestedInPlane(const PlaneF& plane) +{ + if (mInterestNormalRegistered == false) { + return true; + } + else { + PlaneF xformed; + mPlaneTransformer.transform(plane, xformed); + if (mDot(xformed, mInterestNormal) >= 0.0f) + return false; + else + return true; + } +} + +bool AbstractPolyList::isInterestedInPlane(const U32 index) +{ + if (mInterestNormalRegistered == false) { + return true; + } + else { + const PlaneF& rPlane = getIndexedPlane(index); + if (mDot(rPlane, mInterestNormal) >= 0.0f) + return false; + else + return true; + } +} + +void AbstractPolyList::setInterestNormal(const Point3F& normal) +{ + mInterestNormalRegistered = true; + mInterestNormal = normal; +} + diff --git a/Engine/source/collision/abstractPolyList.h b/Engine/source/collision/abstractPolyList.h new file mode 100644 index 000000000..092cba435 --- /dev/null +++ b/Engine/source/collision/abstractPolyList.h @@ -0,0 +1,259 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _ABSTRACTPOLYLIST_H_ +#define _ABSTRACTPOLYLIST_H_ + +#ifndef _MMATH_H_ +#include "math/mMath.h" +#endif +#ifndef _MPLANETRANSFORMER_H_ +#include "math/mPlaneTransformer.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +class SceneObject; +class BaseMatInstance; + + +/// A polygon filtering interface. +/// +/// The various AbstractPolyList subclasses are used in Torque as an interface to +/// handle spatial queries. SceneObject::buildPolyList() takes an implementor of +/// AbstractPolyList (such as ConcretePolyList, ClippedPolyList, etc.) and a +/// bounding volume. The function runs geometry data from all the objects in the +/// bounding volume through the passed PolyList. +/// +/// This interface only provides a method to get data INTO your implementation. Different +/// subclasses provide different interfaces to get data back out, depending on their +/// specific quirks. +/// +/// The physics engine now uses convex hulls for collision detection. +/// +/// @see Convex +class AbstractPolyList +{ +protected: + // User set state + SceneObject* mCurrObject; + + MatrixF mBaseMatrix; // Base transform + MatrixF mTransformMatrix; // Current object transform + MatrixF mMatrix; // Base * current transform + Point3F mScale; + + PlaneTransformer mPlaneTransformer; + + bool mInterestNormalRegistered; + Point3F mInterestNormal; + +public: + AbstractPolyList(); + virtual ~AbstractPolyList(); + + /// @name Common Interface + /// @{ + void setBaseTransform(const MatrixF& mat); + + /// Sets the transform applying to the current stream of + /// vertices. + /// + /// @param mat Transformation of the object. (in) + /// @param scale Scaling of the object. (in) + void setTransform(const MatrixF* mat, const Point3F& scale); + + /// Gets the transform applying to the current stream of + /// vertices. + /// + /// @param mat Transformation of the object. (out) + /// @param scale Scaling of the object. (out) + void getTransform(MatrixF* mat, Point3F * scale); + + /// This is called by the object which is currently feeding us + /// vertices, to tell us who it is. + void setObject(SceneObject*); + + /// Add a box via the query interface (below). This wraps some calls + /// to addPoint and addPlane. + void addBox(const Box3F &box, BaseMatInstance* material = NULL); + + void doConstruct(); + /// @} + + /// @name Query Interface + /// + /// It is through this interface that geometry data is fed to the + /// PolyList. The order of calls are: + /// - begin() + /// - One or more calls to vertex() and one call to plane(), in any order. + /// - end() + /// + /// @code + /// // Example code that adds data to a PolyList. + /// // See AbstractPolyList::addBox() for the function this was taken from. + /// + /// // First, we add points... (note that we use base to track the start of adding.) + /// U32 base = addPoint(pos); + /// pos.y += dy; addPoint(pos); + /// pos.x += dx; addPoint(pos); + /// pos.y -= dy; addPoint(pos); + /// pos.z += dz; addPoint(pos); + /// pos.x -= dx; addPoint(pos); + /// pos.y += dy; addPoint(pos); + /// pos.x += dx; addPoint(pos); + /// + /// // Now we add our surfaces. (there are six, as we are adding a cube here) + /// for (S32 i = 0; i < 6; i++) { + /// // Begin a surface + /// begin(0,i); + /// + /// // Calculate the vertex ids; we have a lookup table to tell us offset from base. + /// // In your own code, you might use a different method. + /// S32 v1 = base + PolyFace[i][0]; + /// S32 v2 = base + PolyFace[i][1]; + /// S32 v3 = base + PolyFace[i][2]; + /// S32 v4 = base + PolyFace[i][3]; + /// + /// // Reference the four vertices that make up this surface. + /// vertex(v1); + /// vertex(v2); + /// vertex(v3); + /// vertex(v4); + /// + /// // Indicate the plane of this surface. + /// plane(v1,v2,v3); + /// + /// // End the surface. + /// end(); + /// } + /// @endcode + /// @{ + + /// Are we empty of data? + virtual bool isEmpty() const = 0; + + /// Adds a point to the poly list, and returns + /// an ID number for that point. + virtual U32 addPoint(const Point3F& p) = 0; + + /// Adds a plane to the poly list, and returns + /// an ID number for that point. + virtual U32 addPlane(const PlaneF& plane) = 0; + + /// Start a surface. + /// + /// @param material A material ID for this surface. + /// @param surfaceKey A key value to associate with this surface. + virtual void begin(BaseMatInstance* material,U32 surfaceKey) = 0; + + /// Indicate the plane of the surface. + virtual void plane(U32 v1,U32 v2,U32 v3) = 0; + /// Indicate the plane of the surface. + virtual void plane(const PlaneF& p) = 0; + /// Indicate the plane of the surface. + virtual void plane(const U32 index) = 0; + + /// Reference a vertex which is in this surface. + virtual void vertex(U32 vi) = 0; + + /// Mark the end of a surface. + virtual void end() = 0; + + /// Return list transform and bounds in list space. + /// + /// @returns False if no data is available. + virtual bool getMapping(MatrixF *, Box3F *); + /// @} + + /// @name Interest + /// + /// This is a mechanism to let you specify interest in a specific normal. + /// If you set a normal you're interested in, then any planes facing "away" + /// from that normal are culled from the results. + /// + /// This is handy if you're using this to do a physics check, as you're not + /// interested in polygons facing away from you (since you don't collide with + /// the backsides/insides of things). + /// @{ + + void setInterestNormal(const Point3F& normal); + void clearInterestNormal() { mInterestNormalRegistered = false; } + + + virtual bool isInterestedInPlane(const PlaneF& plane); + virtual bool isInterestedInPlane(const U32 index); + + /// @} + + protected: + /// A helper function to convert a plane index to a PlaneF structure. + virtual const PlaneF& getIndexedPlane(const U32 index) = 0; +}; + + +inline AbstractPolyList::AbstractPolyList() +{ + doConstruct(); +} + +inline void AbstractPolyList::doConstruct() +{ + mCurrObject = NULL; + mBaseMatrix.identity(); + mMatrix.identity(); + mScale.set(1, 1, 1); + + mPlaneTransformer.setIdentity(); + + mInterestNormalRegistered = false; +} + +inline void AbstractPolyList::setBaseTransform(const MatrixF& mat) +{ + mBaseMatrix = mat; +} + +inline void AbstractPolyList::setTransform(const MatrixF* mat, const Point3F& scale) +{ + mMatrix = mBaseMatrix; + mTransformMatrix = *mat; + mMatrix.mul(*mat); + mScale = scale; + + mPlaneTransformer.set(mMatrix, mScale); +} + +inline void AbstractPolyList::getTransform(MatrixF* mat, Point3F * scale) +{ + *mat = mTransformMatrix; + *scale = mScale; +} + +inline void AbstractPolyList::setObject(SceneObject* obj) +{ + mCurrObject = obj; +} + + +#endif // _ABSTRACTPOLYLIST_H_ diff --git a/Engine/source/collision/boxConvex.cpp b/Engine/source/collision/boxConvex.cpp new file mode 100644 index 000000000..48bcbbe13 --- /dev/null +++ b/Engine/source/collision/boxConvex.cpp @@ -0,0 +1,217 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "math/mMath.h" +#include "T3D/gameBase/gameBase.h" +#include "collision/boxConvex.h" +#include "abstractPolyList.h" + + +//---------------------------------------------------------------------------- + +static struct Corner { + S32 a,b,c; + S32 ab,ac,bc; +} sCorner[] = +{ + { 1,2,4, 4,0,1 }, + { 0,3,5, 4,0,3 }, + { 0,3,6, 4,1,2 }, + { 1,2,7, 4,3,2 }, + { 0,5,6, 0,1,5 }, + { 1,4,7, 0,3,5 }, + { 2,4,7, 1,2,5 }, + { 3,5,6, 3,2,5 }, +}; + +static struct Face { + S32 vertex[4]; + S32 axis; + bool flip; +} sFace[] = +{ + { {0,4,5,1}, 1,true }, + { {0,2,6,4}, 0,true }, + { {3,7,6,2}, 1,false }, + { {3,1,5,7}, 0,false }, + { {0,1,3,2}, 2,true }, + { {4,6,7,5}, 2,false }, +}; + +Point3F BoxConvex::support(const VectorF& v) const +{ + Point3F p = mCenter; + p.x += (v.x >= 0)? mSize.x: -mSize.x; + p.y += (v.y >= 0)? mSize.y: -mSize.y; + p.z += (v.z >= 0)? mSize.z: -mSize.z; + return p; +} + +Point3F BoxConvex::getVertex(S32 v) +{ + Point3F p = mCenter; + p.x += (v & 1)? mSize.x: -mSize.x; + p.y += (v & 2)? mSize.y: -mSize.y; + p.z += (v & 4)? mSize.z: -mSize.z; + return p; +} + +inline bool isOnPlane(Point3F p,PlaneF& plane) +{ + F32 dist = mDot(plane,p) + plane.d; + return dist < 0.1 && dist > -0.1; +} + +void BoxConvex::getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf) +{ + cf->material = 0; + cf->object = mObject; + + S32 v = 0; + v += (n.x >= 0)? 1: 0; + v += (n.y >= 0)? 2: 0; + v += (n.z >= 0)? 4: 0; + + PlaneF plane; + plane.set(getVertex(v),n); + + // Emit vertex and edge + S32 mask = 0; + Corner& corner = sCorner[v]; + mask |= isOnPlane(getVertex(corner.a),plane)? 1: 0; + mask |= isOnPlane(getVertex(corner.b),plane)? 2: 0; + mask |= isOnPlane(getVertex(corner.c),plane)? 4: 0; + + switch(mask) { + case 0: { + cf->mVertexList.increment(); + mat.mulP(getVertex(v),&cf->mVertexList.last()); + break; + } + case 1: + emitEdge(v,corner.a,mat,cf); + break; + case 2: + emitEdge(v,corner.b,mat,cf); + break; + case 4: + emitEdge(v,corner.c,mat,cf); + break; + case 1 | 2: + emitFace(corner.ab,mat,cf); + break; + case 2 | 4: + emitFace(corner.bc,mat,cf); + break; + case 1 | 4: + emitFace(corner.ac,mat,cf); + break; + } +} + +void BoxConvex::getPolyList(AbstractPolyList* list) +{ + list->setTransform(&getTransform(), getScale()); + list->setObject(getObject()); + + U32 base = list->addPoint(mCenter + Point3F(-mSize.x, -mSize.y, -mSize.z)); + list->addPoint(mCenter + Point3F( mSize.x, -mSize.y, -mSize.z)); + list->addPoint(mCenter + Point3F(-mSize.x, mSize.y, -mSize.z)); + list->addPoint(mCenter + Point3F( mSize.x, mSize.y, -mSize.z)); + list->addPoint(mCenter + Point3F(-mSize.x, -mSize.y, mSize.z)); + list->addPoint(mCenter + Point3F( mSize.x, -mSize.y, mSize.z)); + list->addPoint(mCenter + Point3F(-mSize.x, mSize.y, mSize.z)); + list->addPoint(mCenter + Point3F( mSize.x, mSize.y, mSize.z)); + + for (U32 i = 0; i < 6; i++) { + list->begin(0, i); + + list->vertex(base + sFace[i].vertex[0]); + list->vertex(base + sFace[i].vertex[1]); + list->vertex(base + sFace[i].vertex[2]); + list->vertex(base + sFace[i].vertex[3]); + + list->plane(base + sFace[i].vertex[0], + base + sFace[i].vertex[1], + base + sFace[i].vertex[2]); + list->end(); + } +} + + +void BoxConvex::emitEdge(S32 v1,S32 v2,const MatrixF& mat,ConvexFeature* cf) +{ + S32 vc = cf->mVertexList.size(); + cf->mVertexList.increment(2); + Point3F *vp = cf->mVertexList.begin(); + mat.mulP(getVertex(v1),&vp[vc]); + mat.mulP(getVertex(v2),&vp[vc + 1]); + + cf->mEdgeList.increment(); + ConvexFeature::Edge& edge = cf->mEdgeList.last(); + edge.vertex[0] = vc; + edge.vertex[1] = vc + 1; +} + +void BoxConvex::emitFace(S32 fi,const MatrixF& mat,ConvexFeature* cf) +{ + Face& face = sFace[fi]; + + // Emit vertices + S32 vc = cf->mVertexList.size(); + cf->mVertexList.increment(4); + Point3F *vp = cf->mVertexList.begin(); + for (S32 v = 0; v < 4; v++) + mat.mulP(getVertex(face.vertex[v]),&vp[vc + v]); + + // Emit edges + cf->mEdgeList.increment(4); + ConvexFeature::Edge* edge = cf->mEdgeList.end() - 4; + for (S32 e = 0; e < 4; e++) { + edge[e].vertex[0] = vc + e; + edge[e].vertex[1] = vc + ((e + 1) & 3); + } + + // Emit 2 triangle faces + cf->mFaceList.increment(2); + ConvexFeature::Face* ef = cf->mFaceList.end() - 2; + mat.getColumn(face.axis,&ef->normal); + if (face.flip) + ef[0].normal.neg(); + ef[1].normal = ef[0].normal; + ef[1].vertex[0] = ef[0].vertex[0] = vc; + ef[1].vertex[1] = ef[0].vertex[2] = vc + 2; + ef[0].vertex[1] = vc + 1; + ef[1].vertex[2] = vc + 3; +} + + + +const MatrixF& OrthoBoxConvex::getTransform() const +{ + Point3F translation; + Parent::getTransform().getColumn(3, &translation); + mOrthoMatrixCache.setColumn(3, translation); + return mOrthoMatrixCache; +} + diff --git a/Engine/source/collision/boxConvex.h b/Engine/source/collision/boxConvex.h new file mode 100644 index 000000000..581a88570 --- /dev/null +++ b/Engine/source/collision/boxConvex.h @@ -0,0 +1,63 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _BOXCONVEX_H_ +#define _BOXCONVEX_H_ + +#ifndef _CONVEX_H_ +#include "collision/convex.h" +#endif + + +//---------------------------------------------------------------------------- + +class BoxConvex: public Convex +{ + Point3F getVertex(S32 v); + void emitEdge(S32 v1,S32 v2,const MatrixF& mat,ConvexFeature* cf); + void emitFace(S32 fi,const MatrixF& mat,ConvexFeature* cf); +public: + // + Point3F mCenter; + VectorF mSize; + + BoxConvex() { mType = BoxConvexType; } + void init(SceneObject* obj) { mObject = obj; } + + Point3F support(const VectorF& v) const; + void getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf); + void getPolyList(AbstractPolyList* list); +}; + + +class OrthoBoxConvex: public BoxConvex +{ + typedef BoxConvex Parent; + mutable MatrixF mOrthoMatrixCache; + + public: + OrthoBoxConvex() { mOrthoMatrixCache.identity(); } + + virtual const MatrixF& getTransform() const; +}; + +#endif diff --git a/Engine/source/collision/clippedPolyList.cpp b/Engine/source/collision/clippedPolyList.cpp new file mode 100644 index 000000000..2f83da664 --- /dev/null +++ b/Engine/source/collision/clippedPolyList.cpp @@ -0,0 +1,454 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "collision/clippedPolyList.h" + +#include "math/mMath.h" +#include "console/console.h" +#include "platform/profiler.h" + +#include "core/tAlgorithm.h" + +bool ClippedPolyList::allowClipping = true; + + +//---------------------------------------------------------------------------- + +ClippedPolyList::ClippedPolyList() + : mNormal( Point3F::Zero ), + mNormalTolCosineRadians( 0.0f ) +{ + VECTOR_SET_ASSOCIATION(mPolyList); + VECTOR_SET_ASSOCIATION(mVertexList); + VECTOR_SET_ASSOCIATION(mIndexList); + VECTOR_SET_ASSOCIATION(mPolyPlaneList); + VECTOR_SET_ASSOCIATION(mPlaneList); + VECTOR_SET_ASSOCIATION(mNormalList); + + mIndexList.reserve(IndexListReserveSize); +} + +ClippedPolyList::~ClippedPolyList() +{ +} + + +//---------------------------------------------------------------------------- + +void ClippedPolyList::clear() +{ + // Only clears internal data + mPolyList.clear(); + mVertexList.clear(); + mIndexList.clear(); + mPolyPlaneList.clear(); + mNormalList.clear(); +} + +bool ClippedPolyList::isEmpty() const +{ + return mPolyList.size() == 0; +} + + +//---------------------------------------------------------------------------- + +U32 ClippedPolyList::addPoint(const Point3F& p) +{ + mVertexList.increment(); + Vertex& v = mVertexList.last(); + v.point.x = p.x * mScale.x; + v.point.y = p.y * mScale.y; + v.point.z = p.z * mScale.z; + mMatrix.mulP(v.point); + + // Build the plane mask + register U32 mask = 1; + register S32 count = mPlaneList.size(); + register PlaneF * plane = mPlaneList.address(); + + v.mask = 0; + while(--count >= 0) { + if (plane++->distToPlane(v.point) > 0) + v.mask |= mask; + mask <<= 1; + } + + return mVertexList.size() - 1; +} + + +U32 ClippedPolyList::addPlane(const PlaneF& plane) +{ + mPolyPlaneList.increment(); + mPlaneTransformer.transform(plane, mPolyPlaneList.last()); + + return mPolyPlaneList.size() - 1; +} + + +//---------------------------------------------------------------------------- + +void ClippedPolyList::begin(BaseMatInstance* material,U32 surfaceKey) +{ + mPolyList.increment(); + Poly& poly = mPolyList.last(); + poly.object = mCurrObject; + poly.material = material; + poly.vertexStart = mIndexList.size(); + poly.surfaceKey = surfaceKey; + + poly.polyFlags = 0; + if(ClippedPolyList::allowClipping) + poly.polyFlags = CLIPPEDPOLYLIST_FLAG_ALLOWCLIPPING; +} + + +//---------------------------------------------------------------------------- + +void ClippedPolyList::plane(U32 v1,U32 v2,U32 v3) +{ + mPolyList.last().plane.set(mVertexList[v1].point, + mVertexList[v2].point,mVertexList[v3].point); +} + +void ClippedPolyList::plane(const PlaneF& p) +{ + mPlaneTransformer.transform(p, mPolyList.last().plane); +} + +void ClippedPolyList::plane(const U32 index) +{ + AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!"); + mPolyList.last().plane = mPolyPlaneList[index]; +} + +const PlaneF& ClippedPolyList::getIndexedPlane(const U32 index) +{ + AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!"); + return mPolyPlaneList[index]; +} + + +//---------------------------------------------------------------------------- + +void ClippedPolyList::vertex(U32 vi) +{ + mIndexList.push_back(vi); +} + + +//---------------------------------------------------------------------------- + +void ClippedPolyList::end() +{ + PROFILE_SCOPE( ClippedPolyList_Clip ); + + Poly& poly = mPolyList.last(); + + // Reject polygons facing away from our normal. + if ( mDot( poly.plane, mNormal ) < mNormalTolCosineRadians ) + { + mIndexList.setSize(poly.vertexStart); + mPolyList.decrement(); + return; + } + + // Build initial inside/outside plane masks + U32 indexStart = poly.vertexStart; + U32 vertexCount = mIndexList.size() - indexStart; + + U32 frontMask = 0,backMask = 0; + U32 i; + for (i = indexStart; i < mIndexList.size(); i++) + { + U32 mask = mVertexList[mIndexList[i]].mask; + frontMask |= mask; + backMask |= ~mask; + } + + // Trivial accept if all the vertices are on the backsides of + // all the planes. + if (!frontMask) + { + poly.vertexCount = vertexCount; + return; + } + + // Trivial reject if any plane not crossed has all it's points + // on the front. + U32 crossMask = frontMask & backMask; + if (~crossMask & frontMask) + { + mIndexList.setSize(poly.vertexStart); + mPolyList.decrement(); + return; + } + + // Potentially, this will add up to mPlaneList.size() * (indexStart - indexEnd) + // elements to mIndexList, so ensure that it has enough space to store that + // so we can use push_back_noresize. If you find this code block getting hit + // frequently, changing the value of 'IndexListReserveSize' or doing some selective + // allocation is suggested + // + // TODO: Re-visit this, since it obviously does not work correctly, and than + // re-enable the push_back_noresize + //while(mIndexList.size() + mPlaneList.size() * (mIndexList.size() - indexStart) > mIndexList.capacity() ) + // mIndexList.reserve(mIndexList.capacity() * 2); + + // Need to do some clipping + for (U32 p = 0; p < mPlaneList.size(); p++) + { + U32 pmask = 1 << p; + + // Only test against this plane if we have something + // on both sides + if (!(crossMask & pmask)) + continue; + + U32 indexEnd = mIndexList.size(); + U32 i1 = indexEnd - 1; + U32 mask1 = mVertexList[mIndexList[i1]].mask; + + for (U32 i2 = indexStart; i2 < indexEnd; i2++) + { + U32 mask2 = mVertexList[mIndexList[i2]].mask; + if ((mask1 ^ mask2) & pmask) + { + // + mVertexList.increment(); + VectorF& v1 = mVertexList[mIndexList[i1]].point; + VectorF& v2 = mVertexList[mIndexList[i2]].point; + VectorF vv = v2 - v1; + F32 t = -mPlaneList[p].distToPlane(v1) / mDot(mPlaneList[p],vv); + + mIndexList.push_back/*_noresize*/(mVertexList.size() - 1); + Vertex& iv = mVertexList.last(); + iv.point.x = v1.x + vv.x * t; + iv.point.y = v1.y + vv.y * t; + iv.point.z = v1.z + vv.z * t; + iv.mask = 0; + + // Test against the remaining planes + for (U32 i = p + 1; i < mPlaneList.size(); i++) + if (mPlaneList[i].distToPlane(iv.point) > 0) + { + iv.mask = 1 << i; + break; + } + } + + if (!(mask2 & pmask)) + { + U32 index = mIndexList[i2]; + mIndexList.push_back/*_noresize*/(index); + } + + mask1 = mask2; + i1 = i2; + } + + // Check for degenerate + indexStart = indexEnd; + if (mIndexList.size() - indexStart < 3) + { + mIndexList.setSize(poly.vertexStart); + mPolyList.decrement(); + return; + } + } + + // Emit what's left and compress the index list. + poly.vertexCount = mIndexList.size() - indexStart; + memcpy(&mIndexList[poly.vertexStart], + &mIndexList[indexStart],poly.vertexCount); + mIndexList.setSize(poly.vertexStart + poly.vertexCount); +} + + +//---------------------------------------------------------------------------- + +void ClippedPolyList::memcpy(U32* dst, U32* src,U32 size) +{ + U32* end = src + size; + while (src != end) + *dst++ = *src++; +} + +void ClippedPolyList::cullUnusedVerts() +{ + PROFILE_SCOPE( ClippedPolyList_CullUnusedVerts ); + + U32 i = 0; + U32 k, n, numDeleted; + bool result; + + IndexListIterator iNextIter; + VertexListIterator nextVIter; + VertexListIterator vIter; + + for ( vIter = mVertexList.begin(); vIter != mVertexList.end(); vIter++, i++ ) + { + // Is this vertex used? + iNextIter = find( mIndexList.begin(), mIndexList.end(), i ); + if ( iNextIter != mIndexList.end() ) + continue; + + // If not, find the next used vertex. + + // i is an unused vertex + // k is a used vertex + // delete the vertices from i to j - 1 + k = 0; + n = i + 1; + result = false; + numDeleted = 0; + + for ( nextVIter = vIter + 1; nextVIter != mVertexList.end(); nextVIter++, n++ ) + { + iNextIter = find( mIndexList.begin(), mIndexList.end(), n ); + + // If we found a used vertex + // grab its index for later use + // and set our result bool. + if ( (*iNextIter) == n ) + { + k = n; + result = true; + break; + } + } + + // All the remaining verts are unused. + if ( !result ) + { + mVertexList.setSize( i ); + break; + } + + // Erase unused verts. + numDeleted = (k-1) - i + 1; + mVertexList.erase( i, numDeleted ); + + // Find any references to vertices after those deleted + // in the mIndexList and correct with an offset + for ( iNextIter = mIndexList.begin(); iNextIter != mIndexList.end(); iNextIter++ ) + { + if ( (*iNextIter) > i ) + (*iNextIter) -= numDeleted; + } + + // After the erase the current iter should + // point at the used vertex we found... the + // loop will continue with the next vert. + } +} + +void ClippedPolyList::triangulate() +{ + PROFILE_SCOPE( ClippedPolyList_Triangulate ); + + // Copy the source lists to our temp list and clear + // the originals which will recieve the results. + mTempPolyList.set( mPolyList.address(), mPolyList.size() ); + mTempIndexList.set( mIndexList.address(), mIndexList.size() ); + mPolyList.clear(); + mIndexList.clear(); + + U32 j, numTriangles; + + // + PolyListIterator polyIter = mTempPolyList.begin(); + for ( ; polyIter != mTempPolyList.end(); polyIter++ ) + { + const Poly &poly = *polyIter; + + // How many triangles in this poly? + numTriangles = poly.vertexCount - 2; + + // Build out the triangles. + for ( j = 0; j < numTriangles; j++ ) + { + mPolyList.increment(); + + Poly &triangle = mPolyList.last(); + triangle = poly; + triangle.vertexCount = 3; + triangle.vertexStart = mIndexList.size(); + + mIndexList.push_back( mTempIndexList[ poly.vertexStart ] ); + mIndexList.push_back( mTempIndexList[ poly.vertexStart + 1 + j ] ); + mIndexList.push_back( mTempIndexList[ poly.vertexStart + 2 + j ] ); + } + } +} + +void ClippedPolyList::generateNormals() +{ + PROFILE_SCOPE( ClippedPolyList_GenerateNormals ); + + mNormalList.setSize( mVertexList.size() ); + + U32 i, polyCount; + VectorF normal; + PolyListIterator polyIter; + IndexListIterator indexIter; + + Vector::iterator normalIter = mNormalList.begin(); + U32 n = 0; + for ( ; normalIter != mNormalList.end(); normalIter++, n++ ) + { + // Average all the face normals which + // share this vertex index. + indexIter = mIndexList.begin(); + normal.zero(); + polyCount = 0; + i = 0; + + for ( ; indexIter != mIndexList.end(); indexIter++, i++ ) + { + if ( n != *indexIter ) + continue; + + polyIter = mPolyList.begin(); + for ( ; polyIter != mPolyList.end(); polyIter++ ) + { + const Poly& poly = *polyIter; + if ( i < poly.vertexStart || i > poly.vertexStart + poly.vertexCount ) + continue; + + ++polyCount; + normal += poly.plane; + } + } + + // Average it. + if ( polyCount > 0 ) + normal /= (F32)polyCount; + + // Note: we use a temporary for the normal averaging + // then copy the result to limit the number of arrays + // we're touching during the innermost loop. + *normalIter = normal; + } +} diff --git a/Engine/source/collision/clippedPolyList.h b/Engine/source/collision/clippedPolyList.h new file mode 100644 index 000000000..c3eb4e5e7 --- /dev/null +++ b/Engine/source/collision/clippedPolyList.h @@ -0,0 +1,149 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _CLIPPEDPOLYLIST_H_ +#define _CLIPPEDPOLYLIST_H_ + +#ifndef _MMATH_H_ +#include "math/mMath.h" +#endif +#ifndef _TVECTORSPEC_H_ +#include "core/util/tVectorSpecializations.h" +#endif +#ifndef _ABSTRACTPOLYLIST_H_ +#include "collision/abstractPolyList.h" +#endif + + +#define CLIPPEDPOLYLIST_FLAG_ALLOWCLIPPING 0x01 + + +/// The clipped polylist class takes the geometry passed to it and clips +/// it against the PlaneList set. +/// +/// It also contains helper functions for +/// @see AbstractPolyList +class ClippedPolyList : public AbstractPolyList +{ + void memcpy(U32* d, U32* s,U32 size); + +public: + struct Vertex { + Point3F point; + U32 mask; + }; + + struct Poly { + PlaneF plane; + SceneObject* object; + BaseMatInstance* material; + + U32 vertexStart; + U32 vertexCount; + U32 surfaceKey; + U32 polyFlags; + }; + + /// ??? + static bool allowClipping; + + typedef Vector PlaneList; + typedef Vector VertexList; + typedef Vector PolyList; + typedef FastVector IndexList; + + typedef PlaneList::iterator PlaneListIterator; + typedef VertexList::iterator VertexListIterator; + typedef PolyList::iterator PolyListIterator; + typedef IndexList::iterator IndexListIterator; + + // Internal data + PolyList mPolyList; + VertexList mVertexList; + IndexList mIndexList; + + // Temporary lists used by triangulate and kept + // here to reduce memory allocations. + PolyList mTempPolyList; + IndexList mTempIndexList; + + const static U32 IndexListReserveSize = 128; + + /// The per-vertex normals. + /// @see generateNormals() + Vector mNormalList; + + PlaneList mPolyPlaneList; + + /// The list of planes to clip against. + /// + /// This should be set before filling the polylist. + PlaneList mPlaneList; + + /// If non-zero any poly facing away from this + /// normal is removed from the list. + /// + /// This should be set before filling the polylist. + VectorF mNormal; + + /// If the dot product result between a poly's normal and mNormal is greater + /// than this value it will be rejected. + /// The default value is 0. + /// 90 degrees = mCos( mDegToRad( 90.0f ) = 0 + F32 mNormalTolCosineRadians; + + // + ClippedPolyList(); + ~ClippedPolyList(); + void clear(); + + // AbstractPolyList + bool isEmpty() const; + U32 addPoint(const Point3F& p); + U32 addPlane(const PlaneF& plane); + void begin(BaseMatInstance* material,U32 surfaceKey); + void plane(U32 v1,U32 v2,U32 v3); + void plane(const PlaneF& p); + void plane(const U32 index); + void vertex(U32 vi); + void end(); + + /// Often after clipping you'll end up with orphan verticies + /// that are unused by the poly list. This removes these unused + /// verts and updates the index list. + void cullUnusedVerts(); + + /// This breaks all polys in the polylist into triangles. + void triangulate(); + + /// Generates averaged normals from the poly normals. + /// @see mNormalList + void generateNormals(); + + protected: + + // AbstractPolyList + const PlaneF& getIndexedPlane(const U32 index); +}; + + +#endif diff --git a/Engine/source/collision/collision.h b/Engine/source/collision/collision.h new file mode 100644 index 000000000..a2a69df23 --- /dev/null +++ b/Engine/source/collision/collision.h @@ -0,0 +1,186 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _COLLISION_H_ +#define _COLLISION_H_ + +#ifndef _DATACHUNKER_H_ +#include "core/dataChunker.h" +#endif +#ifndef _MPLANE_H_ +#include "math/mPlane.h" +#endif +#ifndef _MPOINT2_H_ +#include "math/mPoint2.h" +#endif + +class SceneObject; +class BaseMatInstance; + +//---------------------------------------------------------------------------- + +struct Collision +{ + SceneObject* object; + Point3F point; + VectorF normal; + BaseMatInstance* material; + + // generate UV coordinate across (TSStatic) mesh based on + // matching normals, this isn't done by default and is + // primarily of interest in matching a collision point to + // either a GUI control coordinate or finding a hit pixel in texture space + bool generateTexCoord; + + // If generateTexCoord is set this will either be invalid (-1, -1) + // or a value in texture space (assuming 0..1 UV mapping) + Point2F texCoord; + + // Face and Face dot are currently only set by the extrudedPolyList + // clipper. Values are otherwise undefined. + U32 face; // Which face was hit + F32 faceDot; // -Dot of face with poly normal + F32 distance; + + Collision() : + object( NULL ), + material( NULL ), + generateTexCoord(false), + texCoord(-1.0f, -1.0f) + { + } +}; + +class CollisionList +{ +public: + enum + { + MaxCollisions = 64 + }; + +protected: + dsize_t mCount; + Collision mCollision[MaxCollisions]; + F32 mT; + // MaxHeight is currently only set by the extrudedPolyList + // clipper. It represents the maximum vertex z value of + // the returned collision surfaces. + F32 mMaxHeight; + +public: + // Constructor + CollisionList( /* const dsize_t reserveSize = MaxCollisions */ ) : + mCount( 0 ), mT( 0.0f ), mMaxHeight( 0.0f ) + { + + } + + // Accessors + int getCount() const { return mCount; } + F32 getTime() const { return mT; } + F32 getMaxHeight() const { return mMaxHeight; } + + const Collision &operator[] ( const dsize_t idx ) const + { + AssertFatal( idx < mCount, "Out of bounds index." ); + return mCollision[idx]; + } + + Collision &operator[] ( const dsize_t idx ) + { + AssertFatal( idx < mCount, "Out of bounds index." ); + return mCollision[idx]; + } + + // Increment does NOT reset the collision which it returns. It is the job of + // the caller to make sure that the entry has data properly assigned to it. + Collision &increment() + { + return mCollision[mCount++]; + } + + void clear() + { + mCount = 0; + } + + void setTime( const F32 t ) + { + mT = t; + } + + void setMaxHeight( const F32 height ) + { + mMaxHeight = height; + } +}; + + +//---------------------------------------------------------------------------- +// BSP Collision tree +// Solid nodes are represented by structures with NULL frontNode and +// backNode pointers. The material field is only valid on a solid node. +// There is no structure for empty nodes, frontNode or backNode +// should be set to NULL to represent empty half-spaces. + +struct BSPNode +{ + U32 material; + PlaneF plane; + BSPNode *frontNode, *backNode; +}; + +typedef Chunker BSPTree; + +/// Extension of the collision structure to allow use with raycasting. +/// @see Collision +struct RayInfo : public Collision +{ + RayInfo() : userData( NULL ) {} + + // The collision struct has object, point, normal & material. + + /// Distance along ray to contact point. + F32 t; + + /// Set the point of intersection according to t and the given ray. + /// + /// Several pieces of code will not use ray information but rather rely + /// on contact points directly, so it is a good thing to always set + /// this in castRay functions. + void setContactPoint( const Point3F& start, const Point3F& end ) + { + Point3F startToEnd = end - start; + startToEnd *= t; + point = startToEnd + start; + } + + /// A generic data void pointer. + /// Passing a void* around to random objects of unknown class types that may + /// interpret it differently would be very dangerous. Only use userData when + /// you call castRay/etc on an individual object of a known type. + void *userData; +}; + + +#endif // _COLLISION_H_ diff --git a/Engine/source/collision/concretePolyList.cpp b/Engine/source/collision/concretePolyList.cpp new file mode 100644 index 000000000..ab6688151 --- /dev/null +++ b/Engine/source/collision/concretePolyList.cpp @@ -0,0 +1,223 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "collision/concretePolyList.h" + +#include "math/mMath.h" +#include "console/console.h" +#include "gfx/gfxDevice.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxStateBlock.h" + + +//---------------------------------------------------------------------------- + +ConcretePolyList::ConcretePolyList() +{ + VECTOR_SET_ASSOCIATION(mPolyList); + VECTOR_SET_ASSOCIATION(mVertexList); + VECTOR_SET_ASSOCIATION(mIndexList); + VECTOR_SET_ASSOCIATION(mPolyPlaneList); + + mIndexList.reserve(100); +} + +ConcretePolyList::~ConcretePolyList() +{ + +} + + +//---------------------------------------------------------------------------- +void ConcretePolyList::clear() +{ + // Only clears internal data + mPolyList.clear(); + mVertexList.clear(); + mIndexList.clear(); + mPolyPlaneList.clear(); +} + +//---------------------------------------------------------------------------- + +U32 ConcretePolyList::addPoint(const Point3F& p) +{ + mVertexList.increment(); + Point3F& v = mVertexList.last(); + v.x = p.x * mScale.x; + v.y = p.y * mScale.y; + v.z = p.z * mScale.z; + mMatrix.mulP(v); + + return mVertexList.size() - 1; +} + + +U32 ConcretePolyList::addPlane(const PlaneF& plane) +{ + mPolyPlaneList.increment(); + mPlaneTransformer.transform(plane, mPolyPlaneList.last()); + + return mPolyPlaneList.size() - 1; +} + + +//---------------------------------------------------------------------------- + +void ConcretePolyList::begin(BaseMatInstance* material,U32 surfaceKey) +{ + mPolyList.increment(); + Poly& poly = mPolyList.last(); + poly.object = mCurrObject; + poly.material = material; + poly.vertexStart = mIndexList.size(); + poly.surfaceKey = surfaceKey; +} + + +//---------------------------------------------------------------------------- + +void ConcretePolyList::plane(U32 v1,U32 v2,U32 v3) +{ + mPolyList.last().plane.set(mVertexList[v1], + mVertexList[v2],mVertexList[v3]); +} + +void ConcretePolyList::plane(const PlaneF& p) +{ + mPlaneTransformer.transform(p, mPolyList.last().plane); +} + +void ConcretePolyList::plane(const U32 index) +{ + AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!"); + mPolyList.last().plane = mPolyPlaneList[index]; +} + +const PlaneF& ConcretePolyList::getIndexedPlane(const U32 index) +{ + AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!"); + return mPolyPlaneList[index]; +} + + +//---------------------------------------------------------------------------- + +void ConcretePolyList::vertex(U32 vi) +{ + mIndexList.push_back(vi); +} + + +//---------------------------------------------------------------------------- + +bool ConcretePolyList::isEmpty() const +{ + return mPolyList.empty(); +} + +void ConcretePolyList::end() +{ + Poly& poly = mPolyList.last(); + poly.vertexCount = mIndexList.size() - poly.vertexStart; +} + +void ConcretePolyList::render() +{ + GFXStateBlockDesc solidZDisable; + solidZDisable.setCullMode( GFXCullNone ); + solidZDisable.setZReadWrite( false, false ); + GFXStateBlockRef sb = GFX->createStateBlock( solidZDisable ); + GFX->setStateBlock( sb ); + + PrimBuild::color3i( 255, 0, 255 ); + + Poly *p; + Point3F *pnt; + + for ( p = mPolyList.begin(); p < mPolyList.end(); p++ ) + { + PrimBuild::begin( GFXLineStrip, p->vertexCount + 1 ); + + for ( U32 i = 0; i < p->vertexCount; i++ ) + { + pnt = &mVertexList[mIndexList[p->vertexStart + i]]; + PrimBuild::vertex3fv( pnt ); + } + + pnt = &mVertexList[mIndexList[p->vertexStart]]; + PrimBuild::vertex3fv( pnt ); + + PrimBuild::end(); + } +} + +void ConcretePolyList::triangulate() +{ + PROFILE_SCOPE( ConcretePolyList_Triangulate ); + + // Build into a new polylist and index list. + // + // TODO: There are potential performance issues + // here as we're not reserving enough space for + // new generated triangles. + // + // We need to either over estimate and shrink or + // better yet fix vector to internally grow in + // large chunks. + // + PolyList polyList; + polyList.reserve( mPolyList.size() ); + IndexList indexList; + indexList.reserve( mIndexList.size() ); + + U32 j, numTriangles; + + // + PolyList::const_iterator polyIter = mPolyList.begin(); + for ( ; polyIter != mPolyList.end(); polyIter++ ) + { + const Poly &poly = *polyIter; + + // How many triangles in this poly? + numTriangles = poly.vertexCount - 2; + + // Build out the triangles. + for ( j = 0; j < numTriangles; j++ ) + { + polyList.increment(); + + Poly &triangle = polyList.last(); + triangle = poly; + triangle.vertexCount = 3; + triangle.vertexStart = indexList.size(); + + indexList.push_back( mIndexList[ poly.vertexStart ] ); + indexList.push_back( mIndexList[ poly.vertexStart + 1 + j ] ); + indexList.push_back( mIndexList[ poly.vertexStart + 2 + j ] ); + } + } + + mPolyList = polyList; + mIndexList = indexList; +} \ No newline at end of file diff --git a/Engine/source/collision/concretePolyList.h b/Engine/source/collision/concretePolyList.h new file mode 100644 index 000000000..adb23688e --- /dev/null +++ b/Engine/source/collision/concretePolyList.h @@ -0,0 +1,92 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _CONCRETEPOLYLIST_H_ +#define _CONCRETEPOLYLIST_H_ + +#ifndef _ABSTRACTPOLYLIST_H_ +#include "collision/abstractPolyList.h" +#endif + +/// A concrete, renderable PolyList +/// +/// This class is used to store geometry from a PolyList query. +/// +/// It allows you to render this data, as well. +/// +/// @see AbstractPolyList +class ConcretePolyList : public AbstractPolyList +{ + public: + + struct Poly { + PlaneF plane; + SceneObject* object; + BaseMatInstance* material; + U32 vertexStart; + U32 vertexCount; + U32 surfaceKey; + + Poly() + { + object = NULL; + material = NULL; + } + }; + + typedef Vector PlaneList; + typedef Vector VertexList; + typedef Vector PolyList; + typedef Vector IndexList; + + PolyList mPolyList; + VertexList mVertexList; + IndexList mIndexList; + + PlaneList mPolyPlaneList; + + public: + ConcretePolyList(); + ~ConcretePolyList(); + void clear(); + + // Virtual methods + U32 addPoint(const Point3F& p); + U32 addPlane(const PlaneF& plane); + void begin(BaseMatInstance* material,U32 surfaceKey); + void plane(U32 v1,U32 v2,U32 v3); + void plane(const PlaneF& p); + void plane(const U32 index); + void vertex(U32 vi); + void end(); + void render(); + + bool isEmpty() const; + + /// This breaks all polys in the polylist into triangles. + void triangulate(); + + protected: + const PlaneF& getIndexedPlane(const U32 index); +}; + +#endif // _CONCRETEPOLYLIST_H_ diff --git a/Engine/source/collision/convex.cpp b/Engine/source/collision/convex.cpp new file mode 100644 index 000000000..51635fc2d --- /dev/null +++ b/Engine/source/collision/convex.cpp @@ -0,0 +1,725 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "collision/convex.h" + +#include "platform/types.h" +#include "core/dataChunker.h" +#include "collision/collision.h" +#include "scene/sceneObject.h" +#include "collision/gjk.h" +#include "collision/concretePolyList.h" +#include "platform/profiler.h" + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +static DataChunker sChunker; + +CollisionStateList CollisionStateList::sFreeList; +CollisionWorkingList CollisionWorkingList::sFreeList; +F32 sqrDistanceEdges(const Point3F& start0, + const Point3F& end0, + const Point3F& start1, + const Point3F& end1, + Point3F* is, + Point3F* it); + + +//---------------------------------------------------------------------------- +// Collision State +//---------------------------------------------------------------------------- + +CollisionState::CollisionState() +{ + mLista = mListb = 0; +} + +CollisionState::~CollisionState() +{ + if (mLista) + mLista->free(); + if (mListb) + mListb->free(); +} + +void CollisionState::swap() +{ +} + +void CollisionState::set(Convex* a,Convex* b,const MatrixF& a2w, const MatrixF& b2w) +{ +} + + +F32 CollisionState::distance(const MatrixF& a2w, const MatrixF& b2w, const F32 dontCareDist, + const MatrixF* w2a, const MatrixF* _w2b) +{ + return 0; +} + +//---------------------------------------------------------------------------- +// Feature Collision +//---------------------------------------------------------------------------- + +void ConvexFeature::reset() +{ + material = NULL; + object = NULL; + mVertexList.clear(); + mEdgeList.clear(); + mFaceList.clear(); +} + +bool ConvexFeature::collide(ConvexFeature& cf,CollisionList* cList, F32 tol) +{ + // Our vertices vs. other faces + const Point3F* vert = mVertexList.begin(); + const Point3F* vend = mVertexList.end(); + while (vert != vend) { + cf.testVertex(*vert,cList,false, tol); + vert++; + } + + // Other vertices vs. our faces + vert = cf.mVertexList.begin(); + vend = cf.mVertexList.end(); + while (vert != vend) { + U32 storeCount = cList->getCount(); + testVertex(*vert,cList,true, tol); + + // Fix up last reference. material and object are copied from this rather + // than the object we're colliding against. + if (storeCount != cList->getCount()) + { + Collision &col = (*cList)[cList->getCount() - 1]; + col.material = cf.material; + col.object = cf.object; + } + vert++; + } + + // Edge vs. Edge + const Edge* edge = mEdgeList.begin(); + const Edge* eend = mEdgeList.end(); + while (edge != eend) { + cf.testEdge(this,mVertexList[edge->vertex[0]], + mVertexList[edge->vertex[1]],cList, tol); + edge++; + } + + return true; +} + +inline bool isInside(const Point3F& p, const Point3F& a, const Point3F& b, const VectorF& n) +{ + VectorF v; + mCross(n,b - a,&v); + return mDot(v,p - a) < 0.0f; +} + +void ConvexFeature::testVertex(const Point3F& v,CollisionList* cList,bool flip, F32 tol) +{ + // Test vertex against all faces + const Face* face = mFaceList.begin(); + const Face* end = mFaceList.end(); + for (; face != end; face++) { + if (cList->getCount() >= CollisionList::MaxCollisions) + return; + + const Point3F& p0 = mVertexList[face->vertex[0]]; + const Point3F& p1 = mVertexList[face->vertex[1]]; + const Point3F& p2 = mVertexList[face->vertex[2]]; + + // Point near the plane? + F32 distance = mDot(face->normal,v - p0); + if (distance > tol || distance < -tol) + continue; + + // Make sure it's within the bounding edges + if (isInside(v,p0,p1,face->normal) && isInside(v,p1,p2,face->normal) && + isInside(v,p2,p0,face->normal)) { + + // Add collision to this face + Collision& info = cList->increment(); + info.point = v; + info.normal = face->normal; + if (flip) + info.normal.neg(); + info.material = material; + info.object = object; + info.distance = distance; + } + } +} + +void ConvexFeature::testEdge(ConvexFeature* cf,const Point3F& s1, const Point3F& e1, CollisionList* cList, F32 tol) +{ + F32 tolSquared = tol*tol; + + // Test edges against edges + const Edge* edge = mEdgeList.begin(); + const Edge* end = mEdgeList.end(); + for (; edge != end; edge++) { + if (cList->getCount() >= CollisionList::MaxCollisions) + return; + + const Point3F& s2 = mVertexList[edge->vertex[0]]; + const Point3F& e2 = mVertexList[edge->vertex[1]]; + + // Get the distance and closest points + Point3F i1,i2; + F32 distance = sqrDistanceEdges(s1, e1, s2, e2, &i1, &i2); + if (distance > tolSquared) + continue; + distance = mSqrt(distance); + + // Need to figure out how to orient the collision normal. + // The current test involves checking to see whether the collision + // points are contained within the convex volumes, which is slow. + if (inVolume(i1) || cf->inVolume(i2)) + distance = -distance; + + // Contact normal + VectorF normal = i1 - i2; + if ( mIsZero( distance ) ) + normal.zero(); + else + normal *= 1 / distance; + + // Return a collision + Collision& info = cList->increment(); + info.point = i1; + info.normal = normal; + info.distance = distance; + info.material = material; + info.object = object; + } +} + +bool ConvexFeature::inVolume(const Point3F& v) +{ + // Test the point to see if it's inside the volume + const Face* face = mFaceList.begin(); + const Face* end = mFaceList.end(); + for (; face != end; face++) { + const Point3F& p0 = mVertexList[face->vertex[0]]; + if (mDot(face->normal,v - p0) > 0) + return false; + } + return true; +} + + +//---------------------------------------------------------------------------- +// Collision State management +//---------------------------------------------------------------------------- + +//---------------------------------------------------------------------------- + +CollisionStateList::CollisionStateList() +{ + mPrev = mNext = this; + mState = NULL; +} + +void CollisionStateList::linkAfter(CollisionStateList* ptr) +{ + mPrev = ptr; + mNext = ptr->mNext; + ptr->mNext = this; + mNext->mPrev = this; +} + +void CollisionStateList::unlink() +{ + mPrev->mNext = mNext; + mNext->mPrev = mPrev; + mPrev = mNext = this; +} + +CollisionStateList* CollisionStateList::alloc() +{ + if (!sFreeList.isEmpty()) { + CollisionStateList* nxt = sFreeList.mNext; + nxt->unlink(); + nxt->mState = NULL; + return nxt; + } + return constructInPlace((CollisionStateList*)sChunker.alloc(sizeof(CollisionStateList))); +} + +void CollisionStateList::free() +{ + unlink(); + linkAfter(&sFreeList); +} + + +//---------------------------------------------------------------------------- + +CollisionWorkingList::CollisionWorkingList() +{ + wLink.mPrev = wLink.mNext = this; + rLink.mPrev = rLink.mNext = this; +} + +void CollisionWorkingList::wLinkAfter(CollisionWorkingList* ptr) +{ + wLink.mPrev = ptr; + wLink.mNext = ptr->wLink.mNext; + ptr->wLink.mNext = this; + wLink.mNext->wLink.mPrev = this; +} + +void CollisionWorkingList::rLinkAfter(CollisionWorkingList* ptr) +{ + rLink.mPrev = ptr; + rLink.mNext = ptr->rLink.mNext; + ptr->rLink.mNext = this; + rLink.mNext->rLink.mPrev = this; +} + +void CollisionWorkingList::unlink() +{ + wLink.mPrev->wLink.mNext = wLink.mNext; + wLink.mNext->wLink.mPrev = wLink.mPrev; + wLink.mPrev = wLink.mNext = this; + + rLink.mPrev->rLink.mNext = rLink.mNext; + rLink.mNext->rLink.mPrev = rLink.mPrev; + rLink.mPrev = rLink.mNext = this; +} + +CollisionWorkingList* CollisionWorkingList::alloc() +{ + if (sFreeList.wLink.mNext != &sFreeList) { + CollisionWorkingList* nxt = sFreeList.wLink.mNext; + nxt->unlink(); + return nxt; + } + return constructInPlace((CollisionWorkingList*)sChunker.alloc(sizeof(CollisionWorkingList))); +} + +void CollisionWorkingList::free() +{ + unlink(); + wLinkAfter(&sFreeList); +} + + +//---------------------------------------------------------------------------- +// Convex Base Class +//---------------------------------------------------------------------------- + +U32 Convex::sTag = (U32)-1; + +//---------------------------------------------------------------------------- + +Convex::Convex() +{ + mNext = mPrev = this; + mTag = 0; +} + +Convex::~Convex() +{ + // Unlink from Convex Database + unlink(); + + // Delete collision states + while (mList.mNext != &mList) + delete mList.mNext->mState; + + // Free up working list + while (mWorking.wLink.mNext != &mWorking) + mWorking.wLink.mNext->free(); + + // Free up references + while (mReference.rLink.mNext != &mReference) + mReference.rLink.mNext->free(); +} + + +//---------------------------------------------------------------------------- + +void Convex::collectGarbage() +{ + // Delete unreferenced Convex Objects + for (Convex* itr = mNext; itr != this; itr = itr->mNext) { + if (itr->mReference.rLink.mNext == &itr->mReference) { + Convex* ptr = itr; + itr = itr->mPrev; + delete ptr; + } + } +} + +void Convex::nukeList() +{ + // Delete all Convex Objects + for (Convex* itr = mNext; itr != this; itr = itr->mNext) { + Convex* ptr = itr; + itr = itr->mPrev; + delete ptr; + } +} + +void Convex::registerObject(Convex *convex) +{ + convex->linkAfter(this); +} + + +//---------------------------------------------------------------------------- + +void Convex::linkAfter(Convex* ptr) +{ + mPrev = ptr; + mNext = ptr->mNext; + ptr->mNext = this; + mNext->mPrev = this; +} + +void Convex::unlink() +{ + mPrev->mNext = mNext; + mNext->mPrev = mPrev; + mPrev = mNext = this; +} + + +//---------------------------------------------------------------------------- + +Point3F Convex::support(const VectorF&) const +{ + return Point3F(0,0,0); +} + +void Convex::getFeatures(const MatrixF&,const VectorF&,ConvexFeature* f) +{ + f->object = NULL; +} + +const MatrixF& Convex::getTransform() const +{ + return mObject->getTransform(); +} + +const Point3F& Convex::getScale() const +{ + return mObject->getScale(); +} + +Box3F Convex::getBoundingBox() const +{ + return mObject->getWorldBox(); +} + +Box3F Convex::getBoundingBox(const MatrixF& mat, const Point3F& scale) const +{ + Box3F wBox = mObject->getObjBox(); + wBox.minExtents.convolve(scale); + wBox.maxExtents.convolve(scale); + mat.mul(wBox); + return wBox; +} + +//---------------------------------------------------------------------------- + +void Convex::addToWorkingList(Convex* ptr) +{ + CollisionWorkingList* cl = CollisionWorkingList::alloc(); + cl->wLinkAfter(&mWorking); + cl->rLinkAfter(&ptr->mReference); + cl->mConvex = ptr; +}; + + +//---------------------------------------------------------------------------- + +void Convex::updateWorkingList(const Box3F& box, const U32 colMask) +{ + PROFILE_SCOPE( Convex_UpdateWorkingList ); + + sTag++; + + // Clear objects off the working list that are no longer intersecting + for (CollisionWorkingList* itr = mWorking.wLink.mNext; itr != &mWorking; itr = itr->wLink.mNext) { + itr->mConvex->mTag = sTag; + if ((!box.isOverlapped(itr->mConvex->getBoundingBox())) || (!itr->mConvex->getObject()->isCollisionEnabled())) { + CollisionWorkingList* cl = itr; + itr = itr->wLink.mPrev; + cl->free(); + } + } + + // Special processing for the terrain and interiors... + AssertFatal(mObject->getContainer(), "Must be in a container!"); + + SimpleQueryList sql; + mObject->getContainer()->findObjects(box, colMask,SimpleQueryList::insertionCallback, &sql); + for (U32 i = 0; i < sql.mList.size(); i++) + sql.mList[i]->buildConvex(box, this); +} + +void Convex::clearWorkingList() +{ + PROFILE_SCOPE( Convex_ClearWorkingList ); + + sTag++; + + for (CollisionWorkingList* itr = mWorking.wLink.mNext; itr != &mWorking; itr = itr->wLink.mNext) + { + itr->mConvex->mTag = sTag; + CollisionWorkingList* cl = itr; + itr = itr->wLink.mPrev; + cl->free(); + } +} + +// --------------------------------------------------------------------------- + +void Convex::updateStateList(const MatrixF& mat, const Point3F& scale, const Point3F* displacement) +{ + PROFILE_SCOPE( Convex_UpdateStateList ); + + Box3F box1 = getBoundingBox(mat, scale); + box1.minExtents -= Point3F(1, 1, 1); + box1.maxExtents += Point3F(1, 1, 1); + if (displacement) { + Point3F oldMin = box1.minExtents; + Point3F oldMax = box1.maxExtents; + + box1.minExtents.setMin(oldMin + *displacement); + box1.minExtents.setMin(oldMax + *displacement); + box1.maxExtents.setMax(oldMin + *displacement); + box1.maxExtents.setMax(oldMax + *displacement); + } + sTag++; + + // Destroy states which are no longer intersecting + for (CollisionStateList* itr = mList.mNext; itr != &mList; itr = itr->mNext) { + Convex* cv = (itr->mState->a == this)? itr->mState->b: itr->mState->a; + cv->mTag = sTag; + if (!box1.isOverlapped(cv->getBoundingBox())) { + CollisionState* cs = itr->mState; + itr = itr->mPrev; + delete cs; + } + } + + // Add collision states for new overlapping objects + for (CollisionWorkingList* itr0 = mWorking.wLink.mNext; itr0 != &mWorking; itr0 = itr0->wLink.mNext) { + register Convex* cv = itr0->mConvex; + if (cv->mTag != sTag && box1.isOverlapped(cv->getBoundingBox())) { + CollisionState* state = new GjkCollisionState; + state->set(this,cv,mat,cv->getTransform()); + state->mLista->linkAfter(&mList); + state->mListb->linkAfter(&cv->mList); + } + } +} + + +//---------------------------------------------------------------------------- + +CollisionState* Convex::findClosestState(const MatrixF& mat, const Point3F& scale, const F32 dontCareDist) +{ + PROFILE_SCOPE( Convex_FindClosestState ); + + updateStateList(mat, scale); + F32 dist = +1E30f; + CollisionState *st = 0; + + // Prepare scaled version of transform + MatrixF axform = mat; + axform.scale(scale); + MatrixF axforminv(true); + MatrixF temp(mat); + axforminv.scale(Point3F(1.0f/scale.x, 1.0f/scale.y, 1.0f/scale.z)); + temp.affineInverse(); + axforminv.mul(temp); + + for (CollisionStateList* itr = mList.mNext; itr != &mList; itr = itr->mNext) + { + CollisionState* state = itr->mState; + if (state->mLista != itr) + state->swap(); + + // Prepare scaled version of transform + MatrixF bxform = state->b->getTransform(); + temp = bxform; + Point3F bscale = state->b->getScale(); + bxform.scale(bscale); + MatrixF bxforminv(true); + bxforminv.scale(Point3F(1.0f/bscale.x, 1.0f/bscale.y, 1.0f/bscale.z)); + temp.affineInverse(); + bxforminv.mul(temp); + + // + F32 dd = state->distance(axform, bxform, dontCareDist, &axforminv, &bxforminv); + if (dd < dist) + { + dist = dd; + st = state; + } + } + if (dist < dontCareDist) + return st; + else + return NULL; +} + + +//---------------------------------------------------------------------------- + +bool Convex::getCollisionInfo(const MatrixF& mat, const Point3F& scale, CollisionList* cList,F32 tol) +{ + PROFILE_SCOPE( Convex_GetCollisionInfo ); + + // Making these static prevents needless Vector resizing that occurs + // in the ConvexFeature constructor. + static ConvexFeature fa; + static ConvexFeature fb; + + for ( CollisionStateList* itr = mList.mNext; + itr != &mList; + itr = itr->mNext) + { + + CollisionState* state = itr->mState; + + if (state->mLista != itr) + state->swap(); + + if (state->dist <= tol) + { + fa.reset(); + fb.reset(); + VectorF v; + + // The idea is that we need to scale the matrix, so we need to + // make a copy of it, before we can pass it in to getFeatures. + // This is used to scale us for comparison against the other + // convex, which is correctly scaled. + MatrixF omat = mat; + omat.scale(scale); + + MatrixF imat = omat; + imat.inverse(); + imat.mulV(-state->v,&v); + + getFeatures(omat,v,&fa); + + imat = state->b->getTransform(); + imat.scale(state->b->getScale()); + + MatrixF bxform = imat; + imat.inverse(); + imat.mulV(state->v,&v); + + state->b->getFeatures(bxform,v,&fb); + + fa.collide(fb,cList,tol); + } + } + + return (cList->getCount() != 0); +} + +void Convex::getPolyList(AbstractPolyList*) +{ + +} + +void Convex::renderWorkingList() +{ + //bool rendered = false; + + CollisionWorkingList& rList = getWorkingList(); + CollisionWorkingList* pList = rList.wLink.mNext; + while (pList != &rList) { + Convex* pConvex = pList->mConvex; + pConvex->render(); + //rendered = true; + pList = pList->wLink.mNext; + } + + //Con::warnf( "convex rendered - %s", rendered ? "YES" : "NO" ); +} + +void Convex::render() +{ + ConcretePolyList polyList; + getPolyList( &polyList ); + polyList.render(); +} + +//----------------------------------------------------------------------------- +// This function based on code originally written for the book: +// 3D Game Engine Design, by David H. Eberly +// +F32 sqrDistanceEdges(const Point3F& start0, const Point3F& end0, + const Point3F& start1, const Point3F& end1, + Point3F* is, Point3F* it) +{ + Point3F direction0 = end0 - start0; + F32 fA00 = direction0.lenSquared(); + + Point3F direction1 = end1 - start1; + F32 fA11 = direction1.lenSquared(); + F32 fA01 = -mDot(direction0, direction1); + + Point3F kDiff = start0 - start1; + F32 fC = kDiff.lenSquared(); + F32 fB0 = mDot(kDiff, direction0); + F32 fDet = mFabs(fA00*fA11 - fA01*fA01); + + // Since the endpoints are tested as vertices, we're not interested + // in parallel lines, and intersections that don't involve end-points. + if (fDet >= 0.00001) { + + // Calculate time of intersection for each line + F32 fB1 = -mDot(kDiff, direction1); + F32 fS = fA01*fB1-fA11*fB0; + F32 fT = fA01*fB0-fA00*fB1; + + // Only interested in collisions that don't involve the end points + if (fS >= 0.0 && fS <= fDet && fT >= 0.0 && fT <= fDet) { + F32 fInvDet = 1.0 / fDet; + fS *= fInvDet; + fT *= fInvDet; + F32 fSqrDist = (fS*(fA00*fS + fA01*fT + 2.0*fB0) + + fT*(fA01*fS + fA11*fT + 2.0*fB1) + fC); + + // Intersection points. + *is = start0 + direction0 * fS; + *it = start1 + direction1 * fT; + return mFabs(fSqrDist); + } + } + + // Return a large number in the cases where endpoints are involved. + return 1e10f; +} diff --git a/Engine/source/collision/convex.h b/Engine/source/collision/convex.h new file mode 100644 index 000000000..8b289b6bf --- /dev/null +++ b/Engine/source/collision/convex.h @@ -0,0 +1,292 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _CONVEX_H_ +#define _CONVEX_H_ + +#ifndef _MMATH_H_ +#include "math/mMath.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +struct Collision; +class CollisionList; +struct CollisionStateList; +class AbstractPolyList; +class SceneObject; +class BaseMatInstance; +class Convex; + + +//---------------------------------------------------------------------------- + +class ConvexFeature +{ +public: + struct Edge { + S32 vertex[2]; + }; + struct Face { + VectorF normal; + S32 vertex[3]; + }; + + Vector mVertexList; + Vector mEdgeList; + Vector mFaceList; + BaseMatInstance* material; + SceneObject* object; + + ConvexFeature() + : mVertexList(64), mEdgeList(128), mFaceList(64), material( 0 ) + { + VECTOR_SET_ASSOCIATION(mVertexList); + VECTOR_SET_ASSOCIATION(mEdgeList); + VECTOR_SET_ASSOCIATION(mFaceList); + } + + void reset(); + + bool collide(ConvexFeature& cf,CollisionList* cList, F32 tol = 0.1); + void testVertex(const Point3F& v,CollisionList* cList,bool,F32 tol); + void testEdge(ConvexFeature* cf,const Point3F& s1, const Point3F& e1, CollisionList* cList, F32 tol); + bool inVolume(const Point3F& v); +}; + + +//---------------------------------------------------------------------------- + +// TODO: This sucks... replace with registration object +// for assigning type ids. +enum ConvexType { + TSConvexType, + BoxConvexType, + TerrainConvexType, + InteriorConvexType, + ShapeBaseConvexType, + TSStaticConvexType, + AtlasChunkConvexType, ///< @deprecated + AtlasConvexType, + TSPolysoupConvexType, + MeshRoadConvexType, + ConvexShapeCollisionConvexType, + ForestConvexType +}; + + +//---------------------------------------------------------------------------- + +struct CollisionState +{ + CollisionStateList* mLista; + CollisionStateList* mListb; + Convex* a; + Convex* b; + + F32 dist; // Current estimated distance + VectorF v; // Vector between closest points + + // + CollisionState(); + virtual ~CollisionState(); + virtual void swap(); + virtual void set(Convex* a,Convex* b,const MatrixF& a2w, const MatrixF& b2w); + virtual F32 distance(const MatrixF& a2w, const MatrixF& b2w, const F32 dontCareDist, + const MatrixF* w2a = NULL, const MatrixF* _w2b = NULL); +}; + + +//---------------------------------------------------------------------------- + +struct CollisionStateList +{ + static CollisionStateList sFreeList; + CollisionStateList* mNext; + CollisionStateList* mPrev; + CollisionState* mState; + + CollisionStateList(); + + void linkAfter(CollisionStateList* next); + void unlink(); + bool isEmpty() { return mNext == this; } + + static CollisionStateList* alloc(); + void free(); +}; + + +//---------------------------------------------------------------------------- + +struct CollisionWorkingList +{ + static CollisionWorkingList sFreeList; + struct WLink { + CollisionWorkingList* mNext; + CollisionWorkingList* mPrev; + } wLink; + struct RLink { + CollisionWorkingList* mNext; + CollisionWorkingList* mPrev; + } rLink; + Convex* mConvex; + + void wLinkAfter(CollisionWorkingList* next); + void rLinkAfter(CollisionWorkingList* next); + void unlink(); + CollisionWorkingList(); + + static CollisionWorkingList* alloc(); + void free(); +}; + + +//---------------------------------------------------------------------------- + +class Convex { + + /// @name Linked list management + /// @{ + + /// Next item in linked list of Convexes. + Convex* mNext; + + /// Previous item in linked list of Convexes. + Convex* mPrev; + + /// Insert this Convex after the provided convex + /// @param next + void linkAfter(Convex* next); + + /// Remove this Convex from the linked list + void unlink(); + /// @} + + U32 mTag; + static U32 sTag; + +protected: + CollisionStateList mList; ///< Objects we're testing against + CollisionWorkingList mWorking; ///< Objects within our bounds + CollisionWorkingList mReference; ///< Other convex testing against us + SceneObject* mObject; ///< Object this Convex is built around + ConvexType mType; ///< Type of Convex this is @see ConvexType + +public: + + /// Constructor + Convex(); + + /// Destructor + virtual ~Convex(); + + /// Registers another Convex by linking it after this one + void registerObject(Convex *convex); + + /// Runs through the linked list of Convex objects and removes the ones + /// with no references + void collectGarbage(); + + /// Deletes all convex objects in the list + void nukeList(); + + /// Returns the type of this Convex + ConvexType getType() const { return mType; } + + /// Returns the object this Convex is built from + SceneObject* getObject() const { return mObject; } + + /// Adds the provided Convex to the list of objects within the bounds of this Convex + /// @param ptr Convex to add to the working list of this object + void addToWorkingList(Convex* ptr); + + /// Returns the list of objects currently inside the bounds of this Convex + CollisionWorkingList& getWorkingList() { return mWorking; } + + /// Finds the closest + CollisionState* findClosestState(const MatrixF& mat, const Point3F& scale, const F32 dontCareDist = 1); + + /// Returns the list of objects this object is testing against + CollisionStateList* getStateList() const { return mList.mNext; } + + /// Updates the CollisionStateList (mList) with new collision states and removing + /// ones no longer under consideration + /// @param mat Used as the matrix to create a bounding box for updating the list + /// @param scale Used to scale the bounding box + /// @param displacement Bounding box displacement (optional) + void updateStateList(const MatrixF& mat, const Point3F& scale, const Point3F* displacement = NULL); + + /// Updates the working collision list of objects which are currently colliding with + /// (inside the bounds of) this Convex. + /// + /// @param box Used as the bounding box. + /// @param colMask Mask of objects to check against. + void updateWorkingList(const Box3F& box, const U32 colMask); + + /// Clear out the working collision list of objects + void clearWorkingList(); + + /// Returns the transform of the object this is built around + virtual const MatrixF& getTransform() const; + + /// Returns the scale of the object this is built around + virtual const Point3F& getScale() const; + + /// Returns the bounding box for the object this is built around in world space + virtual Box3F getBoundingBox() const; + + /// Returns the object space bounding box for the object this is built around + /// transformed and scaled + /// @param mat Matrix to transform the object-space box by + /// @param scale Scaling factor to scale the bounding box by + virtual Box3F getBoundingBox(const MatrixF& mat, const Point3F& scale) const; + + /// Returns the farthest point, along a vector, still bound by the convex + /// @param v Vector + virtual Point3F support(const VectorF& v) const; + + /// This is used by the GJK collision in Vehicle. + /// The Convex class should add verts, edges, and faces to the passed + /// ConvexFeature that face towards the passed normal vector. Verts added + /// in this way should also be transformed by the passed matrix. + /// @param mat Transform which should be applied to verts added to the ConvexFeature + /// @param n Normal vector + /// @param cf ConvexFeature to add data to. + virtual void getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf); + + /// Builds a collision poly list out of this convex + /// @param list (Out) Poly list built + virtual void getPolyList(AbstractPolyList* list); + + /// + bool getCollisionInfo(const MatrixF& mat, const Point3F& scale, CollisionList* cList,F32 tol); + + /// Render convex(s) for debugging purposes. + virtual void renderWorkingList(); + + /// Render this convex for debugging purposes. + virtual void render(); +}; + +#endif // _CONVEX_H_ diff --git a/Engine/source/collision/depthSortList.cpp b/Engine/source/collision/depthSortList.cpp new file mode 100644 index 000000000..510c38f2a --- /dev/null +++ b/Engine/source/collision/depthSortList.cpp @@ -0,0 +1,850 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "math/mMath.h" +#include "console/console.h" +#include "collision/depthSortList.h" +#include "core/color.h" +#include "core/stream/fileStream.h" // TODO, remove this + +//---------------------------------------------------------------------------- + +// some defines and global parameters that affect poly split routine +F32 SPLIT_TOL = 0.0005f; +bool ALWAYS_RETURN_FRONT_AND_BACK = false; // if false, split routine will return polys only if a split occurs + +// more global parameters +F32 XZ_TOL = 0.0f; +F32 DEPTH_TOL = 0.01f; +#define MIN_Y_DOT 0.05f +DepthSortList * gCurrentSort = NULL; +S32 gForceOverlap = -1; // force an overlap test to result in an overlap +S32 gNoOverlapCount; +S32 gBadSpots = 0; + +//---------------------------------------------------------------------------- + +DepthSortList::DepthSortList() +{ + VECTOR_SET_ASSOCIATION(mPolyExtentsList); + VECTOR_SET_ASSOCIATION(mPolyIndexList); +} + +DepthSortList::~DepthSortList() +{ +} + +//---------------------------------------------------------------------------- + +void DepthSortList::clear() +{ + Parent::clear(); + + mPolyExtentsList.clear(); + mPolyIndexList.clear(); + + clearSort(); +} + +void DepthSortList::clearSort() +{ + mBase = -1; + mMaxTouched = 0; + gNoOverlapCount = 0; +} + +//---------------------------------------------------------------------------- + +void DepthSortList::end() +{ + S32 numPoly = mPolyList.size(); + + if (mPolyList.last().plane.y >= -MIN_Y_DOT) + { + mIndexList.setSize(mPolyList.last().vertexStart); + mPolyList.decrement(); + return; + } + + Parent::end(); + + // we deleted this poly, be sure not to add anything more... + if (mPolyList.size()!=numPoly) + return; + + AssertFatal(mPolyList.last().vertexCount>2,"DepthSortList::end: only two vertices in poly"); + + mPolyExtentsList.increment(); + setExtents(mPolyList.last(),mPolyExtentsList.last()); + mPolyIndexList.push_back(numPoly-1); +} + +//---------------------------------------------------------------------------- + +bool DepthSortList::getMapping(MatrixF * mat, Box3F * box) +{ + // return list transform and bounds in list space...optional + *mat = mMatrix; + mat->inverse(); + box->minExtents.set(-mExtent.x, 0.0f, -mExtent.z); + box->maxExtents.set( mExtent.x, 2.0f * mExtent.y, mExtent.z); + + return true; +} + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +void DepthSortList::setExtents(Poly & poly, PolyExtents & polyExtents) +{ + Point3F p = mVertexList[mIndexList[poly.vertexStart]].point; + polyExtents.xMin = polyExtents.xMax = p.x; + polyExtents.yMin = polyExtents.yMax = p.y; + polyExtents.zMin = polyExtents.zMax = p.z; + for (S32 i=poly.vertexStart+1; i polyExtents.xMax) + polyExtents.xMax = p.x; + + // y + if (p.y < polyExtents.yMin) + polyExtents.yMin = p.y; + else if (p.y > polyExtents.yMax) + polyExtents.yMax = p.y; + + // z + if (p.z < polyExtents.zMin) + polyExtents.zMin = p.z; + else if (p.z > polyExtents.zMax) + polyExtents.zMax = p.z; + } +} + +//---------------------------------------------------------------------------- + +// function for comparing two poly indices +S32 FN_CDECL compareYExtents( const void* e1, const void* e2) +{ + DepthSortList::PolyExtents & poly1 = gCurrentSort->getExtents(*(U32*)e1); + DepthSortList::PolyExtents & poly2 = gCurrentSort->getExtents(*(U32*)e2); + + if (poly1.yMin < poly2.yMin) + return -1; + if (poly2.yMin < poly1.yMin) + return 1; + return 0; +} + +//---------------------------------------------------------------------------- + +void DepthSortList::sortByYExtents() +{ + gCurrentSort = this; + dQsort(mPolyIndexList.address(),mPolyIndexList.size(),sizeof(U32),compareYExtents); +} + +//---------------------------------------------------------------------------- + +void DepthSortList::set(const MatrixF & mat, Point3F & extents) +{ + setBaseTransform(mat); + mNormal.set(0,1,0); // ignore polys not facing up... + mExtent = extents; + mExtent *= 0.5f; + + // set clip planes + mPlaneList.clear(); + + mPlaneList.increment(); + mPlaneList.last().set(-1.0f, 0.0f, 0.0f); + mPlaneList.last().d = -mExtent.x; + mPlaneList.increment(); + mPlaneList.last().set( 1.0f, 0.0f, 0.0f); + mPlaneList.last().d = -mExtent.x; + + mPlaneList.increment(); + mPlaneList.last().set( 0.0f,-1.0f, 0.0f); + mPlaneList.last().d = 0; + mPlaneList.increment(); + mPlaneList.last().set( 0.0f, 1.0f, 0.0f); + mPlaneList.last().d = -2.0f * mExtent.y; + + mPlaneList.increment(); + mPlaneList.last().set( 0.0f, 0.0f,-1.0f); + mPlaneList.last().d = -mExtent.z; + mPlaneList.increment(); + mPlaneList.last().set( 0.0f, 0.0f, 1.0f); + mPlaneList.last().d = -mExtent.z; +} + +//---------------------------------------------------------------------------- + +void DepthSortList::setBase(S32 base) +{ + mBase = base; + getOrderedPoly(mBase, &mBasePoly, &mBaseExtents); + mBaseNormal = &mBasePoly->plane; + mBaseDot = -mBasePoly->plane.d; + mBaseYMax = mBaseExtents->yMax; +} + +//---------------------------------------------------------------------------- + +bool DepthSortList::sortNext() +{ + // find the next poly in the depth order + // NOTE: a closer poly may occur before a farther away poly so long as + // they don't overlap in the x-z plane... + if (++mBase>=mPolyIndexList.size()) + return false; + + setBase(mBase); + + gBadSpots = 0; + ALWAYS_RETURN_FRONT_AND_BACK = false; // split routine will return polys only if a split occurs + + bool switched = false; // haven't switched base poly yet + S32 i = 0; // currently comparing base to base+i + Poly * testPoly; + PolyExtents * testExtents; + + while (mBase+i+1plane; + F32 testDot = -testPoly->plane.d; + + // if base poly's y extents don't overlap test poly's, base poly can stay where it is... + if (mBase+i>mMaxTouched && mBaseYMax<=testExtents->yMin+DEPTH_TOL) + break; + + // if base poly and test poly don't have overlapping x & z extents, then order doesn't matter...stay the same + if (mBaseExtents->xMin>=testExtents->xMax-XZ_TOL || mBaseExtents->xMax<=testExtents->xMin+XZ_TOL || + mBaseExtents->zMin>=testExtents->zMax-XZ_TOL || mBaseExtents->zMax<=testExtents->zMin+XZ_TOL) + continue; + + // is test poly completely behind base poly? if so, order is fine as it is + S32 v; + for (v=0; vvertexCount; v++) + if (mDot(mVertexList[mIndexList[testPoly->vertexStart+v]].point,*mBaseNormal)>mBaseDot+DEPTH_TOL) + break; + if (v==testPoly->vertexCount) + // test behind base + continue; + + // is base poly completely in front of test poly? if so, order is fine as it is + for (v=0; vvertexCount; v++) + if (mDot(mVertexList[mIndexList[mBasePoly->vertexStart+v]].point,testNormal)vertexCount) + // base in front of test + continue; + + // if the polys don't overlap in the x-z plane, then order doesn't matter, stay as we are + if (!overlap(mBasePoly,testPoly)) + { + gNoOverlapCount++; + if (gNoOverlapCount!=gForceOverlap) + continue; + } + + // handle switching/splitting of polys due to overlap + handleOverlap(testPoly,testNormal,testDot,i,switched); + } + return true; +} + +//---------------------------------------------------------------------------- + +void DepthSortList::sort() +{ + // depth sort mPolyIndexList -- entries are indices into mPolyList (where poly is found) & mPolyExtentsList + + // sort by min y extent + sortByYExtents(); + + mBase = -1; + + while (sortNext()) + ; +} + +//---------------------------------------------------------------------------- + +void DepthSortList::handleOverlap(Poly * testPoly, Point3F & testNormal, F32 testDot, S32 & testOffset, bool & switched) +{ + // first reverse the plane tests (i.e., test to see if basePoly behind testPoly or testPoly in front of basePoly... + // if either succeeds, switch poly + // if they both fail, split base poly + // But split anyway if basePoly has already been switched... + bool doSwitch = false; + + if (!switched) + { + S32 v; + for (v=0; vvertexCount; v++) + if (mDot(mVertexList[mIndexList[mBasePoly->vertexStart+v]].point,testNormal)>testDot+DEPTH_TOL) + break; + if (v==mBasePoly->vertexCount) + doSwitch = true; + else + { + for (v=0; vvertexCount; v++) + if (mDot(mVertexList[mIndexList[testPoly->vertexStart+v]].point,*mBaseNormal)vertexCount) + doSwitch = true; + } + } + + // try to split base poly along plane of test poly + Poly frontPoly, backPoly; + bool splitBase = false, splitTest = false; + if (!doSwitch) + { + splitBase = splitPoly(*mBasePoly,testNormal,testDot,frontPoly,backPoly); + if (!splitBase) + // didn't take...no splitting happened...try splitting test poly by base poly + splitTest = splitPoly(*testPoly,*mBaseNormal,mBaseDot,frontPoly,backPoly); + } + + U32 testIdx = mPolyIndexList[mBase+testOffset]; + + // should we switch order of test and base poly? Might have to even if we + // don't want to if there's no splitting to do... + // Note: possibility that infinite loop can be introduced here...if that happens, + // then we need to split along edges of polys + if (doSwitch || (!splitTest && !splitBase)) + { + if (!doSwitch && gBadSpots++ > (mPolyIndexList.size()-mBase)<<1) + // got here one too many times...just leave and don't touch poly -- avoid infinite loop + return; + + // move test poly to the front of the order + dMemmove(&mPolyIndexList[mBase+1],&mPolyIndexList[mBase],testOffset*sizeof(U32)); + mPolyIndexList[mBase] = testIdx; + + // base poly changed... + setBase(mBase); + + if (mBase+testOffset>mMaxTouched) + mMaxTouched=mBase+testOffset; + + testOffset=1; // don't need to compare against old base + switched=true; + return; + } + + if (splitBase) + { + // need one more spot...frontPoly and backPoly replace basePoly + setExtents(frontPoly,mPolyExtentsList[mPolyIndexList[mBase]]); + mPolyExtentsList.increment(); + setExtents(backPoly,mPolyExtentsList.last()); + + mPolyList[mPolyIndexList[mBase]] = frontPoly; + mPolyIndexList.insert(mBase+1); + mPolyIndexList[mBase+1] = mPolyList.size(); + mPolyList.push_back(backPoly); + + // new base poly... + setBase(mBase); + + // increase tsetOffset & mMaxTouched because of insertion of back poly + testOffset++; + mMaxTouched++; + + // + switched=false; + return; + } + + // splitTest -- no other way to get here + AssertFatal(splitTest,"DepthSortList::handleOverlap: how did we get here like this?"); + + // put frontPoly in front of basePoly, leave backPoly where testPoly was + + // we need one more spot (testPoly broken into front and back) + // and we need to shift everything from base up to test down one spot + mPolyIndexList.insert(mBase); + + // need one more poly for front poly + mPolyIndexList[mBase] = mPolyList.size(); + mPolyList.push_back(frontPoly); + mPolyExtentsList.increment(); + setExtents(mPolyList.last(),mPolyExtentsList.last()); + + // set up back poly + mPolyList[testIdx] = backPoly; + setExtents(mPolyList[testIdx],mPolyExtentsList[testIdx]); + + // new base poly... + setBase(mBase); + + // we inserted an element, increase mMaxTouched... + mMaxTouched++; + + testOffset=0; + switched = false; +} + +//---------------------------------------------------------------------------- + +bool DepthSortList::overlap(Poly * poly1, Poly * poly2) +{ + // check for overlap without any shortcuts + S32 sz1 = poly1->vertexCount; + S32 sz2 = poly2->vertexCount; + + Point3F * a1, * b1; + Point3F * a2, * b2; + Point2F norm; + F32 dot; + b1 = &mVertexList[mIndexList[poly1->vertexStart+sz1-1]].point; + S32 i; + for (i=0; ivertexStart+i]].point; + + // get the mid-point of this edge + Point3F mid1 = *a1+*b1; + mid1 *= 0.5f; + bool midOutside = false; + + b2 = &mVertexList[mIndexList[poly2->vertexStart+sz2-1]].point; + for (S32 j=0; jvertexStart+j]].point; + + // test for intersection of a1-b1 and a2-b2 (on x-z plane) + + // they intersect if a1 & b1 are on opp. sides of line a2-b2 + // and a2 & b2 are on opp. sides of line a1-b1 + + norm.set(a2->z - b2->z, b2->x - a2->x); // normal to line a2-b2 + dot = norm.x * a2->x + norm.y * a2->z; // dot of a2 and norm + if (norm.x * mid1.x + norm.y * mid1.z - dot >= 0) // special check for midpoint of line + midOutside = true; + if ( ((norm.x * a1->x + norm.y * a1->z) - dot) * ((norm.x * b1->x + norm.y * b1->z) - dot) >= 0 ) + // a1 & b1 are on the same side of the line a2-b2...edges don't overlap + continue; + + norm.set(a1->z - b1->z, b1->x - a1->x); // normal to line a1-b1 + dot = norm.x * a1->x + norm.y * a1->z; // dot of a1 and norm + if ( ((norm.x * a2->x + norm.y * a2->z) - dot) * ((norm.x * b2->x + norm.y * b2->z) - dot) >= 0 ) + // a2 & b2 are on the same side of the line a1-b1...edges don't overlap + continue; + + return true; // edges overlap, so polys overlap + } + if (!midOutside) + return true; // midpoint of a1-b1 is inside the poly + } + + // edges don't overlap...but one poly might be contained inside the other + Point3F center = mVertexList[mIndexList[poly2->vertexStart]].point; + for (i=1; ivertexStart+i]].point; + center *= 1.0f / (F32)poly2->vertexCount; + b1 = &mVertexList[mIndexList[poly1->vertexStart+sz1-1]].point; + for (i=0; ivertexStart+i]].point; + + norm.set(a1->z - b1->z, b1->x - a1->x); // normal to line a1-b1 + dot = norm.x * a1->x + norm.y * a1->z; // dot of a1 and norm + if (center.x * norm.x + center.z * norm.y > dot) + // center is outside this edge, poly2 is not inside poly1 + break; + } + if (i==sz1) + return true; // first vert of poly2 inside poly1 (so all of poly2 inside poly1) + + center = mVertexList[mIndexList[poly1->vertexStart]].point; + for (i=1; ivertexStart+i]].point; + center *= 1.0f / (F32)poly1->vertexCount; + b2 = &mVertexList[mIndexList[poly2->vertexStart+sz2-1]].point; + for (i=0; ivertexStart+i]].point; + + norm.set(a2->z - b2->z, b2->x - a2->x); // normal to line a2-b2 + dot = norm.x * a2->x + norm.y * a2->z; // dot of a1 and norm + if (center.x * norm.x + center.z * norm.y > dot) + // v is outside this edge, poly1 is not inside poly2 + break; + } + if (i==sz2) + return true; // first vert of poly1 inside poly2 (so all of poly1 inside poly2) + + return false; // we survived, no overlap +} + +//---------------------------------------------------------------------------- + +Vector frontVerts(__FILE__, __LINE__); +Vector backVerts(__FILE__, __LINE__); + +// Split source poly into front and back. If either front or back is degenerate, don't do anything. +// If we have a front and a back, then add the verts to our vertex list and fill out the poly structures. +bool DepthSortList::splitPoly(const Poly & src, Point3F & normal, F32 k, Poly & frontPoly, Poly & backPoly) +{ + frontVerts.clear(); + backVerts.clear(); + + // already degenerate... + AssertFatal(src.vertexCount>=3,"DepthSortList::splitPoly - Don't need to split a triangle!"); + + S32 startSize = mVertexList.size(); + + // Assume back and front are degenerate polygons until proven otherwise. + bool backDegen = true, frontDegen = true; + + U32 bIdx; + Point3F * a, * b; + F32 dota, dotb; + S32 signA, signB; + + F32 splitTolSq = SPLIT_TOL * SPLIT_TOL * mDot(normal,normal); + + bIdx = mIndexList[src.vertexStart+src.vertexCount-1]; + b = &mVertexList[bIdx].point; + dotb = mDot(normal,*b)-k; + + // Sign variable coded as follows: 1 for outside, 0 on the plane and -1 for inside. + if (dotb*dotb > splitTolSq) + signB = dotb > 0.0f ? 1 : -1; + else + signB = 0; + + S32 i; + for (i = 0; i splitTolSq) + signB = dotb > 0.0f ? 1 : -1; + else + signB = 0; + + switch(signA*3 + signB + 4) // +4 is to make values go from 0 up...hopefully enticing compiler to make a jump-table + { + case 0: // A-, B- + case 3: // A., B- + backVerts.push_back(bIdx); + backDegen = false; + break; + case 8: // A+, B+ + case 5: // A., B+ + frontVerts.push_back(bIdx); + frontDegen = false; + break; + + case 1: // A-, B. + case 4: // A., B. + case 7: // A+, B. + backVerts.push_back(bIdx); + frontVerts.push_back(bIdx); + break; + + case 2: // A-, B+ + { + // intersect line A-B with plane + F32 dotA = mDot(*a,normal); + F32 dotB = mDot(*b,normal); + Vertex v; + v.point = *a-*b; + v.point *= (k-dotB)/(dotA-dotB); + v.point += *b; + v.mask = 0; + frontVerts.push_back(mVertexList.size()); + backVerts.push_back(mVertexList.size()); + frontVerts.push_back(bIdx); + mVertexList.push_back(v); + b = &mVertexList[bIdx].point; // better get this pointer again since we just incremented vector + frontDegen = false; + break; + } + case 6: // A+, B- + { + // intersect line A-B with plane + F32 dotA = mDot(*a,normal); + F32 dotB = mDot(*b,normal); + Vertex v; + v.point = *a-*b; + v.point *= (k-dotB)/(dotA-dotB); + v.point += *b; + v.mask = 0; + frontVerts.push_back(mVertexList.size()); + backVerts.push_back(mVertexList.size()); + backVerts.push_back(bIdx); + mVertexList.push_back(v); + b = &mVertexList[bIdx].point; // better get this pointer again since we just incremented vector + backDegen = false; + break; + } + } + } + + // Check for degeneracy. + + if (!ALWAYS_RETURN_FRONT_AND_BACK) + { + if (frontVerts.size()<3 || backVerts.size()<3 || frontDegen || backDegen) + { + // didn't need to be split...return two empty polys + // and restore vertex list to how it was + mVertexList.setSize(startSize); + frontPoly.vertexCount = backPoly.vertexCount = 0; + return false; + } + } + else + { + if (frontDegen) + frontVerts.clear(); + if (backDegen) + backVerts.clear(); + } + + // front poly + frontPoly.plane = src.plane; + frontPoly.object = src.object; + frontPoly.material = src.material; + frontPoly.vertexStart = mIndexList.size(); + frontPoly.vertexCount = frontVerts.size(); + frontPoly.surfaceKey = src.surfaceKey; + frontPoly.polyFlags = src.polyFlags; + + // back poly + backPoly.plane = src.plane; + backPoly.object = src.object; + backPoly.material = src.material; + backPoly.vertexStart = frontPoly.vertexStart + frontPoly.vertexCount; + backPoly.vertexCount = backVerts.size(); + backPoly.surfaceKey = src.surfaceKey; + backPoly.polyFlags = src.polyFlags; + + // add indices + mIndexList.setSize(backPoly.vertexStart+backPoly.vertexCount); + + if( frontPoly.vertexCount ) { + dMemcpy(&mIndexList[frontPoly.vertexStart], + frontVerts.address(), + sizeof(U32)*frontPoly.vertexCount); + } + + if( backPoly.vertexCount ) { + dMemcpy(&mIndexList[backPoly.vertexStart], + backVerts.address(), + sizeof(U32)*backPoly.vertexCount); + } + + return true; +} + +//---------------------------------------------------------------------------- + +Vector gWorkListA(256, __FILE__, __LINE__ ); +Vector gWorkListB(256, __FILE__, __LINE__ ); +Vector gWorkListJunkBin(256, __FILE__, __LINE__ ); + +void DepthSortList::depthPartition(const Point3F * sourceVerts, U32 numVerts, Vector & partition, Vector & partitionVerts) +{ + // create the depth partition of the passed poly + // a depth partition is a partition of the poly on the + // x-z plane so that each sub-poly in the partition can be + // mapped onto exactly one plane in the depth list (i.e., + // those polys found in mPolyIndexList... the ones that are + // depth sorted). The plane the sub-polys are mapped onto + // is the plane of the closest facing poly. + // + // y-coord of input polys are ignored, and are remapped + // on output to put the output polys on the + // corresponding planes. + + // This routine is confusing because there are three lists of polys. + // + // The source list (passed in as a single poly, but becomes a list as + // it is split up) comprises the poly to be partitioned. Verts for sourcePoly + // are held in sourceVerts when passed to this routine, but immediately copied + // to mVertexList (and indices are added for each vert to mIndexList). + // + // The scraps list is generated from the source poly (it contains the outside + // piece of each cut that is made). Indices for polys in the scraps list are + // found in mIndexList and verts are found in mVerts list. Note that the depthPartition + // routine will add verts and indices to the member lists, but not polys. + // + // Finally, the partition list is the end result -- the depth partition. These + // polys are not indexed polys. The vertexStart field indexes directly into partitionVerts + // array. + + if (mBase<0) + // begin the depth sort + sortByYExtents(); + + // apply cookie cutter to these polys + Vector * sourceList = &gWorkListA; + sourceList->clear(); + + // add source poly for to passed verts + sourceList->increment(); + sourceList->last().vertexStart = mIndexList.size(); + sourceList->last().vertexCount = numVerts; + + // add verts of source poly to mVertexList and mIndexList + mVertexList.setSize(mVertexList.size()+numVerts); + mIndexList.setSize(mIndexList.size()+numVerts); + for (S32 v=0; v * scraps = &gWorkListB; + scraps->clear(); + + gWorkListJunkBin.clear(); + + S32 i=0; + while (sourceList->size()) + { + if (i>=mBase && !sortNext()) + // that's it, no more polys to sort + break; + AssertFatal(i<=mBase,"DepthSortList::depthPartition - exceeded mBase."); + + // use the topmost poly as the cookie cutter + Poly & cutter = mPolyList[mPolyIndexList[i]]; + S32 startVert = partitionVerts.size(); + + bool allowclipping = cutter.polyFlags & CLIPPEDPOLYLIST_FLAG_ALLOWCLIPPING; + + S32 j; + for (j=0; jsize(); j++) + { + Poly toCut = (*sourceList)[j]; + + if(allowclipping) + cookieCutter(cutter,toCut,*scraps,partition,partitionVerts); + else + cookieCutter(cutter,toCut,gWorkListJunkBin,partition,partitionVerts); + } + + // project all the new verts onto the cutter's plane + AssertFatal(mFabs(cutter.plane.y)>=MIN_Y_DOT,"DepthSortList::depthPartition - below MIN_Y_DOT."); + F32 invY = -1.0f / cutter.plane.y; + for (j=startVert; jclear(); + // swap work lists -- scraps become source for next closest poly + Vector * tmpListPtr = sourceList; + sourceList = scraps; + scraps = tmpListPtr; + } + i++; + } +} + +//---------------------------------------------------------------------------- + +void DepthSortList::cookieCutter(Poly & cutter, Poly & cuttee, + Vector & scraps, // outsides + Vector & cookies, Vector & cookieVerts) // insides +{ + // perhaps going too far with the cookie cutter analogy, but... + // cutter is used to cut cuttee + // + // the part that is inside of all cutter edges (on x-z plane) + // is put into the "cookie" list, parts that are outside are put + // onto the "scraps" list. "scraps" are indexed polys with indices + // and vertices in mIndexList and mVertexList. Cookies aren't indexed + // and points go into cookieVerts list. + + ALWAYS_RETURN_FRONT_AND_BACK = true; // split routine will return polys even if no split occurs + + // save off current state in case nothing inside all the edges of cutter (i.e., no "cookie") + S32 vsStart = cuttee.vertexStart; + S32 vcStart = cuttee.vertexCount; + S32 milStart = mIndexList.size(); + S32 mvlStart = mVertexList.size(); + S32 scStart = scraps.size(); + + Point3F a, b; + Poly scrap; + a = mVertexList[mIndexList[cutter.vertexStart+cutter.vertexCount-1]].point; + for (S32 i=0; i PolyExtentsList; + typedef Vector PolyIndexList; + + // Internal data + PolyExtentsList mPolyExtentsList; + PolyIndexList mPolyIndexList; + Point3F mExtent; // dimensions of the sort area + S32 mBase; // base position in the list...everything before this is sorted correctly + Poly * mBasePoly; // poly currently in base position + Point3F * mBaseNormal; // normal of poly currently in base position + F32 mBaseDot; // dot of basePoly with baseNormal + F32 mBaseYMax; // max y extent of base poly + S32 mMaxTouched; // highest index swapped into thus far...y-extents may be improperly sorted before this index + PolyExtents * mBaseExtents; // x,y,z extents of basePoly + + // set the base position -- everything before this point should be correctly sorted + void setBase(S32); + + // some utilities used for sorting + bool splitPoly(const Poly & sourcePoly, Point3F & normal, F32 k, Poly & front, Poly & back); + bool overlap(Poly *, Poly *); + void handleOverlap(Poly * testPoly, Point3F & testNormal, F32 testDot, S32 & testOffset, bool & switched); + void sortByYExtents(); + void setExtents(Poly &, PolyExtents &); + + // one iteration of the sort routine -- finds a poly to fill current base position + bool sortNext(); + + // used by depthPartition + void cookieCutter(Poly & cutter, Poly & cuttee, + Vector & scraps, + Vector & cookies, Vector & cookieVerts); + + public: + + // + DepthSortList(); + ~DepthSortList(); + void set(const MatrixF & mat, Point3F & extents); + void clear(); + void clearSort(); + + // the point of this class + void sort(); + void depthPartition(const Point3F * sourceVerts, U32 numVerts, Vector & partition, Vector & partitionVerts); + + // Virtual methods + void end(); + // U32 addPoint(const Point3F& p); + // bool isEmpty() const; + // void begin(U32 material,U32 surfaceKey); + // void plane(U32 v1,U32 v2,U32 v3); + // void plane(const PlaneF& p); + // void vertex(U32 vi); + bool getMapping(MatrixF *, Box3F *); + + // access to the polys in order (note: returned pointers are volatile, may change if polys added). + void getOrderedPoly(U32 ith, Poly ** poly, PolyExtents ** polyExtent); + + // unordered access + PolyExtents & getExtents(U32 idx) { return mPolyExtentsList[idx]; } + Poly & getPoly(U32 idx) { return mPolyList[idx]; } +}; + +inline void DepthSortList::getOrderedPoly(U32 ith, Poly ** poly, PolyExtents ** polyExtent) +{ + *poly = &mPolyList[mPolyIndexList[ith]]; + *polyExtent = &mPolyExtentsList[mPolyIndexList[ith]]; +} + +#endif diff --git a/Engine/source/collision/earlyOutPolyList.cpp b/Engine/source/collision/earlyOutPolyList.cpp new file mode 100644 index 000000000..18ac89a30 --- /dev/null +++ b/Engine/source/collision/earlyOutPolyList.cpp @@ -0,0 +1,283 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "math/mMath.h" +#include "console/console.h" +#include "collision/earlyOutPolyList.h" + + +//---------------------------------------------------------------------------- + +EarlyOutPolyList::EarlyOutPolyList() +{ + VECTOR_SET_ASSOCIATION(mPolyList); + VECTOR_SET_ASSOCIATION(mVertexList); + VECTOR_SET_ASSOCIATION(mIndexList); + VECTOR_SET_ASSOCIATION(mPolyPlaneList); + VECTOR_SET_ASSOCIATION(mPlaneList); + + mNormal.set(0, 0, 0); + mIndexList.reserve(100); + + mEarlyOut = false; +} + +EarlyOutPolyList::~EarlyOutPolyList() +{ + +} + + +//---------------------------------------------------------------------------- +void EarlyOutPolyList::clear() +{ + // Only clears internal data + mPolyList.clear(); + mVertexList.clear(); + mIndexList.clear(); + mPolyPlaneList.clear(); + + mEarlyOut = false; +} + +bool EarlyOutPolyList::isEmpty() const +{ + return mEarlyOut == false; +} + + +//---------------------------------------------------------------------------- + +U32 EarlyOutPolyList::addPoint(const Point3F& p) +{ + if (mEarlyOut == true) + return 0; + + mVertexList.increment(); + Vertex& v = mVertexList.last(); + v.point.x = p.x * mScale.x; + v.point.y = p.y * mScale.y; + v.point.z = p.z * mScale.z; + mMatrix.mulP(v.point); + + // Build the plane mask + v.mask = 0; + for (U32 i = 0; i < mPlaneList.size(); i++) + if (mPlaneList[i].distToPlane(v.point) > 0) + v.mask |= 1 << i; + + // If the point is inside all the planes, then we're done! + if (v.mask == 0) + mEarlyOut = true; + + return mVertexList.size() - 1; +} + + +U32 EarlyOutPolyList::addPlane(const PlaneF& plane) +{ + mPolyPlaneList.increment(); + mPlaneTransformer.transform(plane, mPolyPlaneList.last()); + + return mPolyPlaneList.size() - 1; +} + + +//---------------------------------------------------------------------------- + +void EarlyOutPolyList::begin(BaseMatInstance* material,U32 surfaceKey) +{ + if (mEarlyOut == true) + return; + + mPolyList.increment(); + Poly& poly = mPolyList.last(); + poly.object = mCurrObject; + poly.material = material; + poly.vertexStart = mIndexList.size(); + poly.surfaceKey = surfaceKey; +} + + +//---------------------------------------------------------------------------- + +void EarlyOutPolyList::plane(U32 v1,U32 v2,U32 v3) +{ + if (mEarlyOut == true) + return; + + mPolyList.last().plane.set(mVertexList[v1].point, + mVertexList[v2].point,mVertexList[v3].point); +} + +void EarlyOutPolyList::plane(const PlaneF& p) +{ + if (mEarlyOut == true) + return; + + mPlaneTransformer.transform(p, mPolyList.last().plane); +} + +void EarlyOutPolyList::plane(const U32 index) +{ + if (mEarlyOut == true) + return; + + AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!"); + mPolyList.last().plane = mPolyPlaneList[index]; +} + +const PlaneF& EarlyOutPolyList::getIndexedPlane(const U32 index) +{ + AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!"); + return mPolyPlaneList[index]; +} + + +//---------------------------------------------------------------------------- + +void EarlyOutPolyList::vertex(U32 vi) +{ + if (mEarlyOut == true) + return; + + mIndexList.push_back(vi); +} + + +//---------------------------------------------------------------------------- + +void EarlyOutPolyList::end() +{ + if (mEarlyOut == true) + return; + + Poly& poly = mPolyList.last(); + + // Anything facing away from the mNormal is rejected + if (mDot(poly.plane,mNormal) > 0) { + mIndexList.setSize(poly.vertexStart); + mPolyList.decrement(); + return; + } + + // Build intial inside/outside plane masks + U32 indexStart = poly.vertexStart; + U32 vertexCount = mIndexList.size() - indexStart; + + U32 frontMask = 0,backMask = 0; + U32 i; + for (i = indexStart; i < mIndexList.size(); i++) { + U32 mask = mVertexList[mIndexList[i]].mask; + frontMask |= mask; + backMask |= ~mask; + } + + // Trivial accept if all the vertices are on the backsides of + // all the planes. + if (!frontMask) { + poly.vertexCount = vertexCount; + mEarlyOut = true; + return; + } + + // Trivial reject if any plane not crossed has all it's points + // on the front. + U32 crossMask = frontMask & backMask; + if (~crossMask & frontMask) { + mIndexList.setSize(poly.vertexStart); + mPolyList.decrement(); + return; + } + + // Need to do some clipping + for (U32 p = 0; p < mPlaneList.size(); p++) { + U32 pmask = 1 << p; + // Only test against this plane if we have something + // on both sides + if (crossMask & pmask) { + U32 indexEnd = mIndexList.size(); + U32 i1 = indexEnd - 1; + U32 mask1 = mVertexList[mIndexList[i1]].mask; + + for (U32 i2 = indexStart; i2 < indexEnd; i2++) { + U32 mask2 = mVertexList[mIndexList[i2]].mask; + if ((mask1 ^ mask2) & pmask) { + // + mVertexList.increment(); + VectorF& v1 = mVertexList[mIndexList[i1]].point; + VectorF& v2 = mVertexList[mIndexList[i2]].point; + VectorF vv = v2 - v1; + F32 t = -mPlaneList[p].distToPlane(v1) / mDot(mPlaneList[p],vv); + + mIndexList.push_back(mVertexList.size() - 1); + Vertex& iv = mVertexList.last(); + iv.point.x = v1.x + vv.x * t; + iv.point.y = v1.y + vv.y * t; + iv.point.z = v1.z + vv.z * t; + iv.mask = 0; + + // Test against the remaining planes + for (U32 i = p + 1; i < mPlaneList.size(); i++) + if (mPlaneList[i].distToPlane(iv.point) > 0) { + iv.mask = 1 << i; + break; + } + } + if (!(mask2 & pmask)) { + U32 index = mIndexList[i2]; + mIndexList.push_back(index); + } + mask1 = mask2; + i1 = i2; + } + + // Check for degenerate + indexStart = indexEnd; + if (mIndexList.size() - indexStart < 3) { + mIndexList.setSize(poly.vertexStart); + mPolyList.decrement(); + return; + } + } + } + + // If we reach here, then there's a poly! + mEarlyOut = true; + + // Emit what's left and compress the index list. + poly.vertexCount = mIndexList.size() - indexStart; + memcpy(&mIndexList[poly.vertexStart], + &mIndexList[indexStart],poly.vertexCount); + mIndexList.setSize(poly.vertexStart + poly.vertexCount); +} + + +//---------------------------------------------------------------------------- + +void EarlyOutPolyList::memcpy(U32* dst, U32* src,U32 size) +{ + U32* end = src + size; + while (src != end) + *dst++ = *src++; +} + diff --git a/Engine/source/collision/earlyOutPolyList.h b/Engine/source/collision/earlyOutPolyList.h new file mode 100644 index 000000000..971fe0519 --- /dev/null +++ b/Engine/source/collision/earlyOutPolyList.h @@ -0,0 +1,97 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _EARLYOUTPOLYLIST_H_ +#define _EARLYOUTPOLYLIST_H_ + +#ifndef _ABSTRACTPOLYLIST_H_ +#include "collision/abstractPolyList.h" +#endif + + +/// Early out check PolyList +/// +/// This class is used primarily for triggers and similar checks. It checks to see +/// if any of the geometry you feed it is inside its area, and if it is, it stops +/// checking for any more data and returns a true value. This is good if you want +/// to know if anything is in your "trigger" area, for instance. +/// +/// @see AbstractPolyList +class EarlyOutPolyList : public AbstractPolyList +{ + void memcpy(U32* d, U32* s,U32 size); + + // Internal data + struct Vertex { + Point3F point; + U32 mask; + }; + + struct Poly { + PlaneF plane; + SceneObject* object; + BaseMatInstance* material; + U32 vertexStart; + U32 vertexCount; + U32 surfaceKey; + }; + + public: + typedef Vector PlaneList; + private: + typedef Vector VertexList; + typedef Vector PolyList; + typedef Vector IndexList; + + PolyList mPolyList; + VertexList mVertexList; + IndexList mIndexList; + bool mEarlyOut; + + PlaneList mPolyPlaneList; + + public: + // Data set by caller + PlaneList mPlaneList; + VectorF mNormal; + + public: + EarlyOutPolyList(); + ~EarlyOutPolyList(); + void clear(); + + // Virtual methods + bool isEmpty() const; + U32 addPoint(const Point3F& p); + U32 addPlane(const PlaneF& plane); + void begin(BaseMatInstance* material,U32 surfaceKey); + void plane(U32 v1,U32 v2,U32 v3); + void plane(const PlaneF& p); + void plane(const U32 index); + void vertex(U32 vi); + void end(); + + protected: + const PlaneF& getIndexedPlane(const U32 index); +}; + +#endif // _H_EARLYOUTPOLYLIST_ diff --git a/Engine/source/collision/extrudedPolyList.cpp b/Engine/source/collision/extrudedPolyList.cpp new file mode 100644 index 000000000..0e3b744a8 --- /dev/null +++ b/Engine/source/collision/extrudedPolyList.cpp @@ -0,0 +1,510 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "math/mMath.h" +#include "console/console.h" +#include "collision/extrudedPolyList.h" +#include "math/mPolyhedron.h" +#include "collision/collision.h" + +// Minimum distance from a face +F32 ExtrudedPolyList::FaceEpsilon = 0.01f; + +// Value used to compare collision times +F32 ExtrudedPolyList::EqualEpsilon = 0.0001f; + +ExtrudedPolyList::ExtrudedPolyList() +{ + VECTOR_SET_ASSOCIATION(mVertexList); + VECTOR_SET_ASSOCIATION(mIndexList); + VECTOR_SET_ASSOCIATION(mExtrudedList); + VECTOR_SET_ASSOCIATION(mPlaneList); + VECTOR_SET_ASSOCIATION(mPolyPlaneList); + + mVelocity.set(0.0f,0.0f,0.0f); + mIndexList.reserve(128); + mVertexList.reserve(64); + mPolyPlaneList.reserve(64); + mPlaneList.reserve(64); + mCollisionList = 0; +} + +ExtrudedPolyList::~ExtrudedPolyList() +{ +} + +//---------------------------------------------------------------------------- + +bool ExtrudedPolyList::isEmpty() const +{ + return mCollisionList->getCount() == 0; +} + + +//---------------------------------------------------------------------------- + +void ExtrudedPolyList::extrude(const Polyhedron& pt, const VectorF& vector) +{ + // Clear state + mIndexList.clear(); + mVertexList.clear(); + mPlaneList.clear(); + mPolyPlaneList.clear(); + + // Determine which faces will be extruded. + mExtrudedList.setSize(pt.planeList.size()); + + for (U32 f = 0; f < pt.planeList.size(); f++) + { + const PlaneF& face = pt.planeList[f]; + ExtrudedFace& eface = mExtrudedList[f]; + F32 dot = mDot(face,vector); + eface.active = dot > EqualEpsilon; + + if (eface.active) + { + eface.maxDistance = dot; + eface.plane = face; + eface.planeMask = BIT(mPlaneList.size()); + + // Add the face as a plane to clip against. + mPlaneList.increment(2); + PlaneF* plane = mPlaneList.end() - 2; + plane[0] = plane[1] = face; + plane[0].invert(); + } + } + + // Produce extruded planes for bounding and internal edges + for (U32 e = 0; e < pt.edgeList.size(); e++) + { + Polyhedron::Edge const& edge = pt.edgeList[e]; + ExtrudedFace& ef1 = mExtrudedList[edge.face[0]]; + ExtrudedFace& ef2 = mExtrudedList[edge.face[1]]; + if (ef1.active || ef2.active) + { + + // Assumes that the edge points are clockwise + // for face[0]. + const Point3F& p1 = pt.pointList[edge.vertex[1]]; + const Point3F &p2 = pt.pointList[edge.vertex[0]]; + Point3F p3 = p2 + vector; + + mPlaneList.increment(2); + PlaneF* plane = mPlaneList.end() - 2; + plane[0].set(p3,p2,p1); + plane[1] = plane[0]; + plane[1].invert(); + + U32 pmask = BIT(mPlaneList.size()-2); + ef1.planeMask |= pmask; + ef2.planeMask |= pmask << 1; + } + } +} + + +//---------------------------------------------------------------------------- + +void ExtrudedPolyList::setCollisionList(CollisionList* info) +{ + mCollisionList = info; + mCollisionList->clear(); + mCollisionList->setTime( 2.0f ); +} + +//---------------------------------------------------------------------------- + +void ExtrudedPolyList::adjustCollisionTime() +{ + if( !mCollisionList->getCount() ) + return; + + mCollisionList->setTime( mClampF( mCollisionList->getTime(), 0.f, 1.f ) ); +} + + +//---------------------------------------------------------------------------- + +U32 ExtrudedPolyList::addPoint(const Point3F& p) +{ + mVertexList.increment(); + Vertex& v = mVertexList.last(); + + v.point.x = p.x * mScale.x; + v.point.y = p.y * mScale.y; + v.point.z = p.z * mScale.z; + mMatrix.mulP(v.point); + + // Build the plane mask, planes come in pairs + v.mask = 0; + for (U32 i = 0; i < mPlaneList.size(); i ++) + if (mPlaneList[i].distToPlane(v.point) >= 0.f) + v.mask |= BIT(i); + + return mVertexList.size() - 1; +} + + +U32 ExtrudedPolyList::addPlane(const PlaneF& plane) +{ + mPolyPlaneList.increment(); + mPlaneTransformer.transform(plane, mPolyPlaneList.last()); + + return mPolyPlaneList.size() - 1; +} + + +//---------------------------------------------------------------------------- + +void ExtrudedPolyList::begin(BaseMatInstance* material, U32 /*surfaceKey*/) +{ + mPoly.object = mCurrObject; + mPoly.material = material; + mIndexList.clear(); +} + +void ExtrudedPolyList::plane(U32 v1, U32 v2, U32 v3) +{ + mPoly.plane.set(mVertexList[v1].point, + mVertexList[v2].point, + mVertexList[v3].point); + + // We hope this isn't needed but we're leaving it in anyway -- BJG/EGH + mPoly.plane.normalizeSafe(); +} + +void ExtrudedPolyList::plane(const PlaneF& p) +{ + mPlaneTransformer.transform(p, mPoly.plane); +} + +void ExtrudedPolyList::plane(const U32 index) +{ + AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!"); + mPoly.plane = mPolyPlaneList[index]; +} + +const PlaneF& ExtrudedPolyList::getIndexedPlane(const U32 index) +{ + AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!"); + return mPolyPlaneList[index]; +} + + +void ExtrudedPolyList::vertex(U32 vi) +{ + mIndexList.push_back(vi); +} + +void ExtrudedPolyList::end() +{ + // Anything facing away from the mVelocity is rejected (and also + // cap to max collisions) + if (mDot(mPoly.plane, mNormalVelocity) > 0.f || + mCollisionList->getCount() >= CollisionList::MaxCollisions) + return; + + // Test the built up poly (stored in mPoly) against all our extruded + // faces. + U32 cFaceCount = 0; + ExtrudedFace* cFace[30]; + bool cEdgeColl[30]; + ExtrudedFace* face = mExtrudedList.begin(); + ExtrudedFace* end = mExtrudedList.end(); + + for (; face != end; face++) + { + // Skip inactive.. + if (!face->active) + continue; + + // Update the dot product. + face->faceDot = -mDot(face->plane,mPoly.plane); + + // Skip it if we're facing towards... + if(face->faceDot <= 0.f) + continue; + + // Test, and skip if colliding. + if (!testPoly(*face)) + continue; + + // Note collision. + cFace[cFaceCount] = face; + cEdgeColl[cFaceCount++] = false; + } + + if (!cFaceCount) + { + face = mExtrudedList.begin(); + end = mExtrudedList.end(); + for (; face != end; face++) + { + // Don't need to do dot product second time, so just check if it's + // active (we already did the dot product in the previous loop). + if (!face->active) + continue; + + // Skip it if we're facing away... + if(face->faceDot > 0.f) + continue; + + // Do collision as above. + if (!testPoly(*face)) + continue; + + // Note the collision. + cFace[cFaceCount] = face; + cEdgeColl[cFaceCount++] = true; + } + } + + // If we STILL don't have any collisions, just skip out. + if (!cFaceCount) + return; + + // Pick the best collision face based on best alignment with respective + // face. + face = cFace[0]; + bool edge = cEdgeColl[0]; + for (U32 f = 1; f < cFaceCount; f++) + { + if (cFace[f]->faceDot <= face->faceDot) + continue; + + face = cFace[f]; + edge = cEdgeColl[f]; + } + + // Add it to the collision list if it's better and/or equal + // to our current best. + + // Don't add it to the collision list if it's too far away. + if (face->time > mCollisionList->getTime() + EqualEpsilon || face->time >= 1.0) + return; + + if (face->time < mCollisionList->getTime() - EqualEpsilon) + { + // If this is significantly closer than before, then clear out the + // list, as it's a better match than the old stuff. + mCollisionList->clear(); + mCollisionList->setTime( face->time ); + mCollisionList->setMaxHeight( face->height ); + } + else + { + // Otherwise, just update some book-keeping stuff. + if ( face->height > mCollisionList->getMaxHeight() ) + mCollisionList->setMaxHeight( face->height ); + } + + // Note the collision in our collision list. + Collision& collision = mCollisionList->increment(); + collision.point = face->point; + collision.faceDot = face->faceDot; + collision.face = face - mExtrudedList.begin(); + collision.object = mPoly.object; + collision.normal = mPoly.plane; + collision.material = mPoly.material; +} + + +//---------------------------------------------------------------------------- + +bool ExtrudedPolyList::testPoly(ExtrudedFace& face) +{ + // Build intial inside/outside plane masks + U32 indexStart = 0; + U32 indexEnd = mIndexList.size(); + U32 oVertexSize = mVertexList.size(); + U32 oIndexSize = mIndexList.size(); + + U32 frontMask = 0,backMask = 0; + for (U32 i = indexStart; i < indexEnd; i++) + { + U32 mask = mVertexList[mIndexList[i]].mask & face.planeMask; + frontMask |= mask; + backMask |= ~mask; + } + + // Clip the mPoly against the planes that bound the face... + // Trivial accept if all the vertices are on the backsides of + // all the planes. + if (frontMask) + { + // Trivial reject if any plane not crossed has all it's points + // on the front. + U32 crossMask = frontMask & backMask; + if (~crossMask & frontMask) + return false; + + // Need to do some clipping + for (U32 p=0; p < mPlaneList.size(); p++) + { + U32 pmask = BIT(p); + U32 newStart = mIndexList.size(); + + // Only test against this plane if we have something + // on both sides - otherwise skip. + if (!(face.planeMask & crossMask & pmask)) + continue; + + U32 i1 = indexEnd - 1; + U32 mask1 = mVertexList[mIndexList[i1]].mask; + + for (U32 i2 = indexStart; i2 < indexEnd; i2++) + { + const U32 mask2 = mVertexList[mIndexList[i2]].mask; + if ((mask1 ^ mask2) & pmask) + { + // Clip the edge against the plane. + mVertexList.increment(); + VectorF& v1 = mVertexList[mIndexList[i1]].point; + VectorF& v2 = mVertexList[mIndexList[i2]].point; + VectorF vv = v2 - v1; + F32 t = -mPlaneList[p].distToPlane(v1) / mDot(mPlaneList[p],vv); + + mIndexList.push_back(mVertexList.size() - 1); + Vertex& iv = mVertexList.last(); + iv.point.x = v1.x + vv.x * t; + iv.point.y = v1.y + vv.y * t; + iv.point.z = v1.z + vv.z * t; + iv.mask = 0; + + // Test against the remaining planes + for (U32 i = p+1; i < mPlaneList.size(); i ++) + { + if (mPlaneList[i].distToPlane(iv.point) > 0.f) + iv.mask |= BIT(i); + } + } + + if (!(mask2 & pmask)) + { + U32 index = mIndexList[i2]; + mIndexList.push_back(index); + } + + mask1 = mask2; + i1 = i2; + } + + // Check for degenerate + indexStart = newStart; + indexEnd = mIndexList.size(); + if (mIndexList.size() - indexStart < 3) + { + mVertexList.setSize(oVertexSize); + mIndexList.setSize(oIndexSize); + return false; + } + } + } + + // Find closest point on the mPoly + Point3F bp(0.0f, 0.0f, 0.0f); + F32 bd = 1E30f; + F32 height = -1E30f; + for (U32 b = indexStart; b < indexEnd; b++) + { + Vertex& vertex = mVertexList[mIndexList[b]]; + F32 dist = face.plane.distToPlane(vertex.point); + if (dist <= bd) + { + bd = (dist < 0)? 0: dist; + bp = vertex.point; + } + + // Since we don't clip against the back plane, we'll + // only include vertex heights that are within range. + if (vertex.point.z > height && dist < face.maxDistance) + height = vertex.point.z; + } + + // hack for not jetting up through the cieling + F32 fudge = 0.01f; + F32 fudgeB = 0.2f; + if(mNormalVelocity.z > 0.0) + { + fudge = 0.01f; //0.015; + fudgeB = 0.2f; + } + + // Do extruded points for back-off. + F32 oldBd=bd; + for (U32 b = indexStart; b < indexEnd; b++) + { + Vertex& vertex = mVertexList[mIndexList[b]]; + + // Extrude out just a tad to make sure we don't end up getting too close to the + // geometry and getting stuck - but cap it so we don't introduce error into long + // sweeps. + F32 dist = face.plane.distToPlane( vertex.point + + Point3F(mPoly.plane) * getMin(face.maxDistance * fudgeB, fudge)); + + if (dist <= bd) + { + bd = (dist < 0)? 0: dist; + bp = vertex.point; + } + } + + // Remove temporary data + mVertexList.setSize(oVertexSize); + mIndexList.setSize(oIndexSize); + + // Don't add it to the collision list if it's worse then our current best. + if (oldBd >= face.maxDistance) + return false; + + // Update our info and indicate we should add to the model. + F32 oldT = oldBd / face.maxDistance; + F32 pushBackT = bd / face.maxDistance; + + if(oldT - pushBackT > 0.1) + face.time = oldT - fudge; + else + face.time = pushBackT; + + face.height = height; + face.point = bp; + return true; +} + +//-------------------------------------------------------------------------- +void ExtrudedPolyList::setVelocity(const VectorF& velocity) +{ + mVelocity = velocity; + if (velocity.isZero() == false) + { + mNormalVelocity = velocity; + mNormalVelocity.normalize(); + setInterestNormal(mNormalVelocity); + } + else + { + mNormalVelocity.set(0.0f, 0.0f, 0.0f); + clearInterestNormal(); + } +} diff --git a/Engine/source/collision/extrudedPolyList.h b/Engine/source/collision/extrudedPolyList.h new file mode 100644 index 000000000..a802e3eba --- /dev/null +++ b/Engine/source/collision/extrudedPolyList.h @@ -0,0 +1,130 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _EXTRUDEDPOLYLIST_H_ +#define _EXTRUDEDPOLYLIST_H_ + +#ifndef _MMATH_H_ +#include "math/mMath.h" +#endif + +#ifndef _MPOLYHEDRON_H_ +#include "math/mPolyhedron.h" +#endif + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +#ifndef _ABSTRACTPOLYLIST_H_ +#include "collision/abstractPolyList.h" +#endif + + +class CollisionList; + + +//---------------------------------------------------------------------------- +/// Extruded Polytope PolyList +/// +/// This class is used primarily for collision detection, by objects which need +/// to check for obstructions along their path. You feed it a polytope to +/// extrude along the direction of movement, and it gives you a list of collisions. +/// +/// @see AbstractPolyList +class ExtrudedPolyList: public AbstractPolyList +{ +public: + struct Vertex { + Point3F point; + U32 mask; + }; + + struct Poly { + PlaneF plane; + SceneObject* object; + BaseMatInstance* material; + }; + + struct ExtrudedFace { + bool active; + PlaneF plane; + F32 maxDistance; + U32 planeMask; + F32 faceDot; + F32 faceShift; + F32 time; + Point3F point; + F32 height; + }; + + typedef Vector ExtrudedList; + typedef Vector PlaneList; + typedef Vector VertexList; + typedef Vector IndexList; + + static F32 EqualEpsilon; + static F32 FaceEpsilon; + + // Internal data + VertexList mVertexList; + IndexList mIndexList; + ExtrudedList mExtrudedList; + PlaneList mPlaneList; + VectorF mVelocity; + VectorF mNormalVelocity; + F32 mFaceShift; + Poly mPoly; + + // Returned info + CollisionList* mCollisionList; + + PlaneList mPolyPlaneList; + + // +private: + bool testPoly(ExtrudedFace&); + +public: + ExtrudedPolyList(); + ~ExtrudedPolyList(); + void extrude(const Polyhedron&, const VectorF& vec); + void setVelocity(const VectorF& velocity); + void setCollisionList(CollisionList*); + void adjustCollisionTime(); + + // Virtual methods + bool isEmpty() const; + U32 addPoint(const Point3F& p); + U32 addPlane(const PlaneF& plane); + void begin(BaseMatInstance* material, U32 surfaceKey); + void plane(U32 v1,U32 v2,U32 v3); + void plane(const PlaneF& p); + void plane(const U32 index); + void vertex(U32 vi); + void end(); + + protected: + const PlaneF& getIndexedPlane(const U32 index); +}; + +#endif diff --git a/Engine/source/collision/gjk.cpp b/Engine/source/collision/gjk.cpp new file mode 100644 index 000000000..bd84e6d4f --- /dev/null +++ b/Engine/source/collision/gjk.cpp @@ -0,0 +1,387 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// The core algorithms in this file are inspired by public papers written +// by G. van den Bergen for his interference detection library, +// "SOLID 2.0" + +#include "core/dataChunker.h" +#include "collision/collision.h" +#include "scene/sceneObject.h" +#include "collision/convex.h" +#include "collision/gjk.h" + + +//---------------------------------------------------------------------------- + +static F32 rel_error = 1E-5f; // relative error in the computed distance +static F32 sTolerance = 1E-3f; // Distance tolerance +static F32 sEpsilon2 = 1E-20f; // Zero length vector +static U32 sIteration = 15; // Stuck in a loop? + +S32 num_iterations = 0; +S32 num_irregularities = 0; + + +//---------------------------------------------------------------------------- + +GjkCollisionState::GjkCollisionState() +{ + a = b = 0; +} + +GjkCollisionState::~GjkCollisionState() +{ +} + + +//---------------------------------------------------------------------------- + +void GjkCollisionState::swap() +{ + Convex* t = a; a = b; b = t; + CollisionStateList* l = mLista; mLista = mListb; mListb = l; + v.neg(); +} + + +//---------------------------------------------------------------------------- + +void GjkCollisionState::compute_det() +{ + // Dot new point with current set + for (int i = 0, bit = 1; i < 4; ++i, bit <<=1) + if (bits & bit) + dp[i][last] = dp[last][i] = mDot(y[i], y[last]); + dp[last][last] = mDot(y[last], y[last]); + + // Calulate the determinent + det[last_bit][last] = 1; + for (int j = 0, sj = 1; j < 4; ++j, sj <<= 1) { + if (bits & sj) { + int s2 = sj | last_bit; + det[s2][j] = dp[last][last] - dp[last][j]; + det[s2][last] = dp[j][j] - dp[j][last]; + for (int k = 0, sk = 1; k < j; ++k, sk <<= 1) { + if (bits & sk) { + int s3 = sk | s2; + det[s3][k] = det[s2][j] * (dp[j][j] - dp[j][k]) + + det[s2][last] * (dp[last][j] - dp[last][k]); + det[s3][j] = det[sk | last_bit][k] * (dp[k][k] - dp[k][j]) + + det[sk | last_bit][last] * (dp[last][k] - dp[last][j]); + det[s3][last] = det[sk | sj][k] * (dp[k][k] - dp[k][last]) + + det[sk | sj][j] * (dp[j][k] - dp[j][last]); + } + } + } + } + + if (all_bits == 15) { + det[15][0] = det[14][1] * (dp[1][1] - dp[1][0]) + + det[14][2] * (dp[2][1] - dp[2][0]) + + det[14][3] * (dp[3][1] - dp[3][0]); + det[15][1] = det[13][0] * (dp[0][0] - dp[0][1]) + + det[13][2] * (dp[2][0] - dp[2][1]) + + det[13][3] * (dp[3][0] - dp[3][1]); + det[15][2] = det[11][0] * (dp[0][0] - dp[0][2]) + + det[11][1] * (dp[1][0] - dp[1][2]) + + det[11][3] * (dp[3][0] - dp[3][2]); + det[15][3] = det[7][0] * (dp[0][0] - dp[0][3]) + + det[7][1] * (dp[1][0] - dp[1][3]) + + det[7][2] * (dp[2][0] - dp[2][3]); + } +} + + +//---------------------------------------------------------------------------- + +inline void GjkCollisionState::compute_vector(int bits, VectorF& v) +{ + F32 sum = 0; + v.set(0, 0, 0); + for (int i = 0, bit = 1; i < 4; ++i, bit <<= 1) { + if (bits & bit) { + sum += det[bits][i]; + v += y[i] * det[bits][i]; + } + } + v *= 1 / sum; +} + + +//---------------------------------------------------------------------------- + +inline bool GjkCollisionState::valid(int s) +{ + for (int i = 0, bit = 1; i < 4; ++i, bit <<= 1) { + if (all_bits & bit) { + if (s & bit) { + if (det[s][i] <= 0) + return false; + } + else + if (det[s | bit][i] > 0) + return false; + } + } + return true; +} + + +//---------------------------------------------------------------------------- + +inline bool GjkCollisionState::closest(VectorF& v) +{ + compute_det(); + for (int s = bits; s; --s) { + if ((s & bits) == s) { + if (valid(s | last_bit)) { + bits = s | last_bit; + if (bits != 15) + compute_vector(bits, v); + return true; + } + } + } + if (valid(last_bit)) { + bits = last_bit; + v = y[last]; + return true; + } + return false; +} + + +//---------------------------------------------------------------------------- + +inline bool GjkCollisionState::degenerate(const VectorF& w) +{ + for (int i = 0, bit = 1; i < 4; ++i, bit <<= 1) + if ((all_bits & bit) && y[i] == w) + return true; + return false; +} + + +//---------------------------------------------------------------------------- + +inline void GjkCollisionState::nextBit() +{ + last = 0; + last_bit = 1; + while (bits & last_bit) { + ++last; + last_bit <<= 1; + } +} + + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +//---------------------------------------------------------------------------- + +void GjkCollisionState::set(Convex* aa, Convex* bb, + const MatrixF& a2w, const MatrixF& b2w) +{ + a = aa; + b = bb; + + bits = 0; + all_bits = 0; + reset(a2w,b2w); + + // link + mLista = CollisionStateList::alloc(); + mLista->mState = this; + mListb = CollisionStateList::alloc(); + mListb->mState = this; +} + + +//---------------------------------------------------------------------------- + +void GjkCollisionState::reset(const MatrixF& a2w, const MatrixF& b2w) +{ + VectorF zero(0,0,0),sa,sb; + a2w.mulP(a->support(zero),&sa); + b2w.mulP(b->support(zero),&sb); + v = sa - sb; + dist = v.len(); +} + + +//---------------------------------------------------------------------------- + +void GjkCollisionState::getCollisionInfo(const MatrixF& mat, Collision* info) +{ + AssertFatal(false, "GjkCollisionState::getCollisionInfo() - There remain scaling problems here."); + // This assumes that the shapes do not intersect + Point3F pa,pb; + if (bits) { + getClosestPoints(pa,pb); + mat.mulP(pa,&info->point); + b->getTransform().mulP(pb,&pa); + info->normal = info->point - pa; + } + else { + mat.mulP(p[last],&info->point); + info->normal = v; + } + info->normal.normalize(); + info->object = b->getObject(); +} + +void GjkCollisionState::getClosestPoints(Point3F& p1, Point3F& p2) +{ + F32 sum = 0; + p1.set(0, 0, 0); + p2.set(0, 0, 0); + for (int i = 0, bit = 1; i < 4; ++i, bit <<= 1) { + if (bits & bit) { + sum += det[bits][i]; + p1 += p[i] * det[bits][i]; + p2 += q[i] * det[bits][i]; + } + } + F32 s = 1 / sum; + p1 *= s; + p2 *= s; +} + + +//---------------------------------------------------------------------------- + +bool GjkCollisionState::intersect(const MatrixF& a2w, const MatrixF& b2w) +{ + num_iterations = 0; + MatrixF w2a,w2b; + + w2a = a2w; + w2b = b2w; + w2a.inverse(); + w2b.inverse(); + reset(a2w,b2w); + + bits = 0; + all_bits = 0; + + do { + nextBit(); + + VectorF va,sa; + w2a.mulV(-v,&va); + p[last] = a->support(va); + a2w.mulP(p[last],&sa); + + VectorF vb,sb; + w2b.mulV(v,&vb); + q[last] = b->support(vb); + b2w.mulP(q[last],&sb); + + VectorF w = sa - sb; + if (mDot(v,w) > 0) + return false; + if (degenerate(w)) { + ++num_irregularities; + return false; + } + + y[last] = w; + all_bits = bits | last_bit; + + ++num_iterations; + if (!closest(v) || num_iterations > sIteration) { + ++num_irregularities; + return false; + } + } + while (bits < 15 && v.lenSquared() > sEpsilon2); + return true; +} + +F32 GjkCollisionState::distance(const MatrixF& a2w, const MatrixF& b2w, + const F32 dontCareDist, const MatrixF* _w2a, const MatrixF* _w2b) +{ + num_iterations = 0; + MatrixF w2a,w2b; + + if (_w2a == NULL || _w2b == NULL) { + w2a = a2w; + w2b = b2w; + w2a.inverse(); + w2b.inverse(); + } + else { + w2a = *_w2a; + w2b = *_w2b; + } + + reset(a2w,b2w); + bits = 0; + all_bits = 0; + F32 mu = 0; + + do { + nextBit(); + + VectorF va,sa; + w2a.mulV(-v,&va); + p[last] = a->support(va); + a2w.mulP(p[last],&sa); + + VectorF vb,sb; + w2b.mulV(v,&vb); + q[last] = b->support(vb); + b2w.mulP(q[last],&sb); + + VectorF w = sa - sb; + F32 nm = mDot(v, w) / dist; + if (nm > mu) + mu = nm; + if (mu > dontCareDist) + return mu; + if (mFabs(dist - mu) <= dist * rel_error) + return dist; + + ++num_iterations; + if (degenerate(w) || num_iterations > sIteration) { + ++num_irregularities; + return dist; + } + + y[last] = w; + all_bits = bits | last_bit; + + if (!closest(v)) { + ++num_irregularities; + return dist; + } + + dist = v.len(); + } + while (bits < 15 && dist > sTolerance) ; + + if (bits == 15 && mu <= 0) + dist = 0; + return dist; +} diff --git a/Engine/source/collision/gjk.h b/Engine/source/collision/gjk.h new file mode 100644 index 000000000..d78555f72 --- /dev/null +++ b/Engine/source/collision/gjk.h @@ -0,0 +1,82 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GJK_H_ +#define _GJK_H_ + +#ifndef _MMATH_H_ +#include "math/mMath.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _CONVEX_H_ +#include "collision/convex.h" +#endif + +class Convex; +struct CollisionStateList; +struct Collision; + +//---------------------------------------------------------------------------- + +struct GjkCollisionState: public CollisionState +{ + /// @name Temporary values + /// @{ + Point3F p[4]; ///< support points of object A in local coordinates + Point3F q[4]; ///< support points of object B in local coordinates + VectorF y[4]; ///< support points of A - B in world coordinates + + S32 bits; ///< identifies current simplex + S32 all_bits; ///< all_bits = bits | last_bit + F32 det[16][4]; ///< cached sub-determinants + F32 dp[4][4]; ///< cached dot products + + S32 last; ///< identifies last found support point + S32 last_bit; ///< last_bit = 1<(baseMat->getMaterial()); + + for (U32 i = 0; i < mMaterialList.size(); i++) + { + Material* testMat = dynamic_cast(mMaterialList[i]->getMaterial()); + + if (mat && testMat) + { + if (testMat == mat) + { + retIdx = i; + break; + } + } + else if (mMaterialList[i] == baseMat) + { + retIdx = i; + break; + } + } + + if (retIdx == -1) + { + retIdx = mMaterialList.size(); + mMaterialList.push_back(baseMat); + } + + return (U32)retIdx; +} + +U32 OptimizedPolyList::insertVertex(const Point3F& point, const Point3F& normal, + const Point2F& uv0, const Point2F& uv1) +{ + VertIndex vert; + + vert.vertIdx = insertPoint(point); + vert.normalIdx = insertNormal(normal); + vert.uv0Idx = insertUV0(uv0); + vert.uv1Idx = insertUV1(uv1); + + return mVertexList.push_back_unique(vert); +} + +U32 OptimizedPolyList::addPoint(const Point3F& p) +{ + return insertVertex(p); +} + +U32 OptimizedPolyList::addPlane(const PlaneF& plane) +{ + return insertPlane(plane); +} + + +//---------------------------------------------------------------------------- + +void OptimizedPolyList::begin(BaseMatInstance* material, U32 surfaceKey) +{ + mPolyList.increment(); + Poly& poly = mPolyList.last(); + poly.material = insertMaterial(material); + poly.vertexStart = mIndexList.size(); + poly.surfaceKey = surfaceKey; + poly.type = TriangleFan; + poly.object = mCurrObject; +} + +void OptimizedPolyList::begin(BaseMatInstance* material, U32 surfaceKey, PolyType type) +{ + begin(material, surfaceKey); + + // Set the type + mPolyList.last().type = type; +} + + +//---------------------------------------------------------------------------- + +void OptimizedPolyList::plane(U32 v1, U32 v2, U32 v3) +{ + /* + AssertFatal(v1 < mPoints.size() && v2 < mPoints.size() && v3 < mPoints.size(), + "OptimizedPolyList::plane(): Vertex indices are larger than vertex list size"); + + mPolyList.last().plane = addPlane(PlaneF(mPoints[v1], mPoints[v2], mPoints[v3])); + */ + + mPolyList.last().plane = addPlane( PlaneF( mPoints[mVertexList[v1].vertIdx], mPoints[mVertexList[v2].vertIdx], mPoints[mVertexList[v3].vertIdx] ) ); +} + +void OptimizedPolyList::plane(const PlaneF& p) +{ + mPolyList.last().plane = addPlane(p); +} + +void OptimizedPolyList::plane(const U32 index) +{ + AssertFatal(index < mPlaneList.size(), "Out of bounds index!"); + mPolyList.last().plane = index; +} + +const PlaneF& OptimizedPolyList::getIndexedPlane(const U32 index) +{ + AssertFatal(index < mPlaneList.size(), "Out of bounds index!"); + return mPlaneList[index]; +} + + +//---------------------------------------------------------------------------- + +void OptimizedPolyList::vertex(U32 vi) +{ + mIndexList.push_back(vi); +} + +void OptimizedPolyList::vertex(const Point3F& p) +{ + mIndexList.push_back(addPoint(p)); +} + +void OptimizedPolyList::vertex(const Point3F& p, + const Point3F& normal, + const Point2F& uv0, + const Point2F& uv1) +{ + mIndexList.push_back(insertVertex(p, normal, uv0, uv1)); +} + + +//---------------------------------------------------------------------------- + +bool OptimizedPolyList::isEmpty() const +{ + return !mPolyList.size(); +} + +void OptimizedPolyList::end() +{ + Poly& poly = mPolyList.last(); + poly.vertexCount = mIndexList.size() - poly.vertexStart; +} + +//---------------------------------------------------------------------------- + +Polyhedron OptimizedPolyList::toPolyhedron() const +{ + Polyhedron polyhedron; + + // Add the points, but filter out duplicates. + + Vector< S32 > pointRemap; + pointRemap.setSize( mPoints.size() ); + pointRemap.fill( -1 ); + + const U32 numPoints = mPoints.size(); + + for( U32 i = 0; i < numPoints; ++ i ) + { + bool isDuplicate = false; + for( U32 npoint = 0; npoint < polyhedron.pointList.size(); ++ npoint ) + { + if( npoint == i ) + continue; + + if( !polyhedron.pointList[ npoint ].equal( mPoints[ i ] ) ) + continue; + + pointRemap[ i ] = npoint; + isDuplicate = true; + } + + if( !isDuplicate ) + { + pointRemap[ i ] = polyhedron.pointList.size(); + polyhedron.pointList.push_back( mPoints[ i ] ); + } + } + + // Go through the polys and add all their edges and planes. + // We will consolidate edges in a second pass. + + const U32 numPolys = mPolyList.size(); + for( U32 i = 0; i < numPolys; ++ i ) + { + const Poly& poly = mPolyList[ i ]; + + // Add the plane. + + const U32 polyIndex = polyhedron.planeList.size(); + polyhedron.planeList.push_back( mPlaneList[ poly.plane ] ); + + // Account for polyhedrons expecting planes to + // face inwards. + + polyhedron.planeList.last().invert(); + + // Gather remapped indices according to the + // current polygon type. + + Vector< U32 > indexList; + switch( poly.type ) + { + case TriangleFan: + AssertFatal( false, "TriangleFan conversion not implemented" ); + case TriangleStrip: + AssertFatal( false, "TriangleStrip conversion not implemented" ); + case TriangleList: + { + Vector< Polyhedron::Edge > tempEdges; + + // Loop over the triangles and gather all unshared edges + // in tempEdges. These are the exterior edges of the polygon. + + for( U32 n = poly.vertexStart; n < poly.vertexStart + poly.vertexCount; n += 3 ) + { + U32 indices[ 3 ]; + + // Get the remapped indices of the three vertices. + + indices[ 0 ] = pointRemap[ mVertexList[ mIndexList[ n + 0 ] ].vertIdx ]; + indices[ 1 ] = pointRemap[ mVertexList[ mIndexList[ n + 1 ] ].vertIdx ]; + indices[ 2 ] = pointRemap[ mVertexList[ mIndexList[ n + 2 ] ].vertIdx ]; + + // Loop over the three edges. + + for( U32 d = 0; d < 3; ++ d ) + { + U32 index1 = indices[ d ]; + U32 index2 = indices[ ( d + 1 ) % 3 ]; + + // See if this edge is already in the list. If so, + // it's a shared edge and thus an interior one. Remove + // it. + + bool isShared = false; + for( U32 nedge = 0; nedge < tempEdges.size(); ++ nedge ) + { + Polyhedron::Edge& edge = tempEdges[ nedge ]; + if( ( edge.vertex[ 0 ] == index1 && edge.vertex[ 1 ] == index2 ) || + ( edge.vertex[ 0 ] == index2 && edge.vertex[ 1 ] == index1 ) ) + { + tempEdges.erase( nedge ); + + isShared = true; + break; + } + } + + // If it wasn't in the list, add a new edge. + + if( !isShared ) + tempEdges.push_back( + Polyhedron::Edge( -1, -1, index1, index2 ) + ); + } + } + + // Walk the edges and gather consecutive indices. + + U32 currentEdge = 0; + for( U32 n = 0; n < tempEdges.size(); ++ n ) + { + // Add first vertex of edge. + + indexList.push_back( tempEdges[ currentEdge ].vertex[ 0 ] ); + + // Find edge that begins at second vertex. + + for( U32 nedge = 0; nedge < tempEdges.size(); ++ nedge ) + { + if( nedge == currentEdge ) + continue; + + if( tempEdges[ nedge ].vertex[ 0 ] == tempEdges[ currentEdge ].vertex[ 1 ] ) + { + currentEdge = nedge; + break; + } + } + } + } + } + + // Create edges from the indices. Indices are CCW ordered and + // we want CW order, so step everything in reverse. + + U32 lastIndex = 0; + for( S32 n = indexList.size() - 1; n >= 0; -- n ) + { + polyhedron.edgeList.push_back( + Polyhedron::Edge( + polyIndex, 0, // face1 filled later + indexList[ lastIndex ], indexList[ n ] + ) + ); + + lastIndex = n; + } + } + + // Finally, consolidate the edge list by merging all edges that + // are shared by polygons. + + for( U32 i = 0; i < polyhedron.edgeList.size(); ++ i ) + { + Polyhedron::Edge& edge = polyhedron.edgeList[ i ]; + + // Find the corresponding duplicate edge, if any, and merge + // it into our current edge. + + for( U32 n = i + 1; n < polyhedron.edgeList.size(); ++ n ) + { + const Polyhedron::Edge& thisEdge = polyhedron.edgeList[ n ]; + + if( ( thisEdge.vertex[ 0 ] == edge.vertex[ 1 ] && + thisEdge.vertex[ 1 ] == edge.vertex[ 0 ] ) || + ( thisEdge.vertex[ 0 ] == edge.vertex[ 0 ] && + thisEdge.vertex[ 1 ] == edge.vertex[ 1 ] ) ) + { + edge.face[ 1 ] = thisEdge.face[ 0 ]; + polyhedron.edgeList.erase( n ); + break; + } + } + } + + return polyhedron; +} diff --git a/Engine/source/collision/optimizedPolyList.h b/Engine/source/collision/optimizedPolyList.h new file mode 100644 index 000000000..dc33c6a0e --- /dev/null +++ b/Engine/source/collision/optimizedPolyList.h @@ -0,0 +1,160 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _OPTIMIZEDPOLYLIST_H_ +#define _OPTIMIZEDPOLYLIST_H_ + +#ifndef _ABSTRACTPOLYLIST_H_ +#include "collision/abstractPolyList.h" +#endif + +#ifndef _MPOLYHEDRON_H_ +#include "math/mPolyhedron.h" +#endif + +#define DEV 0.01 + +/// A concrete, renderable PolyList +/// +/// This class is used to store geometry from a PolyList query. +/// +/// @see AbstractPolyList +class OptimizedPolyList : public AbstractPolyList +{ + public: + + enum PolyType + { + TriangleFan, + TriangleStrip, + TriangleList + }; + + struct VertIndex + { + S32 vertIdx; + S32 normalIdx; + S32 uv0Idx; + S32 uv1Idx; + + VertIndex() + : vertIdx( -1 ), + normalIdx ( -1 ), + uv0Idx( -1 ), + uv1Idx( -1 ) + { + } + + bool operator==(const VertIndex& _test) const + { + return ( vertIdx == _test.vertIdx && + normalIdx == _test.normalIdx && + uv0Idx == _test.uv0Idx && + uv1Idx == _test.uv1Idx ); + } + }; + + struct Poly + { + S32 plane; + S32 material; + U32 vertexStart; + U32 vertexCount; + U32 surfaceKey; + + SceneObject* object; + + PolyType type; + + Poly() + : plane( -1 ), + object( NULL ), + vertexCount( 0 ), + material( NULL ), + type( TriangleFan ) + { + } + }; + + // Vertex data + Vector mPoints; + Vector mNormals; + Vector mUV0s; + Vector mUV1s; + + // List of the VertIndex structure that puts + // all of the vertex data together + Vector mVertexList; + + // Polygon data + Vector mIndexList; + Vector mPlaneList; + + Vector mMaterialList; + + // The Polygon structure puts the vertex data + // and the polygon together + Vector mPolyList; + + public: + OptimizedPolyList(); + ~OptimizedPolyList(); + void clear(); + + // Virtual methods + U32 addPoint(const Point3F& p); + U32 addPlane(const PlaneF& plane); + + void begin(BaseMatInstance* material, U32 surfaceKey); + void begin(BaseMatInstance* material, U32 surfaceKey, PolyType type); + void plane(U32 v1, U32 v2, U32 v3); + void plane(const PlaneF& p); + void plane(const U32 index); + void vertex(U32 vi); + void vertex(const Point3F& p); + void vertex(const Point3F& p, + const Point3F& normal, + const Point2F& uv0 = Point2F(0.0f, 0.0f), + const Point2F& uv1 = Point2F(0.0f, 0.0f)); + void end(); + + U32 insertPoint(const Point3F& point); + U32 insertNormal(const Point3F& normal); + U32 insertUV0(const Point2F& uv); + U32 insertUV1(const Point2F& uv); + U32 insertPlane(const PlaneF& plane); + U32 insertMaterial(BaseMatInstance* baseMat); + + U32 insertVertex(const Point3F& point, + const Point3F& normal = Point3F(0.0f, 0.0f, 1.0f), + const Point2F& uv0 = Point2F(0.0f, 0.0f), + const Point2F& uv1 = Point2F(0.0f, 0.0f)); + + bool isEmpty() const; + + Polyhedron toPolyhedron() const; + + protected: + const PlaneF& getIndexedPlane(const U32 index); +}; + +#endif // _OPTIMIZEDPOLYLIST_H_ diff --git a/Engine/source/collision/planeExtractor.cpp b/Engine/source/collision/planeExtractor.cpp new file mode 100644 index 000000000..f4e9ffdbb --- /dev/null +++ b/Engine/source/collision/planeExtractor.cpp @@ -0,0 +1,129 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "math/mMath.h" +#include "console/console.h" +#include "collision/planeExtractor.h" + +//---------------------------------------------------------------------------- +// Plane matching parameters +static F32 NormalEpsilon = 0.93969f; // 20 deg. +static F32 DistanceEpsilon = 100.0f; + + +//---------------------------------------------------------------------------- + +PlaneExtractorPolyList::PlaneExtractorPolyList() +{ + VECTOR_SET_ASSOCIATION(mVertexList); + VECTOR_SET_ASSOCIATION(mPolyPlaneList); +} + +PlaneExtractorPolyList::~PlaneExtractorPolyList() +{ +} + + +//---------------------------------------------------------------------------- + +void PlaneExtractorPolyList::clear() +{ + mVertexList.clear(); + mPolyPlaneList.clear(); +} + +U32 PlaneExtractorPolyList::addPoint(const Point3F& p) +{ + mVertexList.increment(); + Point3F& v = mVertexList.last(); + v.x = p.x * mScale.x; + v.y = p.y * mScale.y; + v.z = p.z * mScale.z; + mMatrix.mulP(v); + return mVertexList.size() - 1; +} + +U32 PlaneExtractorPolyList::addPlane(const PlaneF& plane) +{ + mPolyPlaneList.increment(); + mPlaneTransformer.transform(plane, mPolyPlaneList.last()); + + return mPolyPlaneList.size() - 1; +} + + +//---------------------------------------------------------------------------- + +void PlaneExtractorPolyList::plane(U32 v1,U32 v2,U32 v3) +{ + mPlaneList->last().set(mVertexList[v1], + mVertexList[v2],mVertexList[v3]); +} + +void PlaneExtractorPolyList::plane(const PlaneF& p) +{ + mPlaneTransformer.transform(p, mPlaneList->last()); +} + +void PlaneExtractorPolyList::plane(const U32 index) +{ + AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!"); + mPlaneList->last() = mPolyPlaneList[index]; +} + +const PlaneF& PlaneExtractorPolyList::getIndexedPlane(const U32 index) +{ + AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!"); + return mPolyPlaneList[index]; +} + + +//---------------------------------------------------------------------------- + +bool PlaneExtractorPolyList::isEmpty() const +{ + return true; +} + +void PlaneExtractorPolyList::begin(BaseMatInstance*,U32) +{ + mPlaneList->increment(); +} + +void PlaneExtractorPolyList::end() +{ + // See if there are any duplicate planes + PlaneF &plane = mPlaneList->last(); + PlaneF *ptr = mPlaneList->begin(); + for (; ptr != &plane; ptr++) + if (mFabs(ptr->d - plane.d) < DistanceEpsilon && + mDot(*ptr,plane) > NormalEpsilon) { + mPlaneList->decrement(); + return; + } +} + +void PlaneExtractorPolyList::vertex(U32) +{ +} + diff --git a/Engine/source/collision/planeExtractor.h b/Engine/source/collision/planeExtractor.h new file mode 100644 index 000000000..2a3f6e6e2 --- /dev/null +++ b/Engine/source/collision/planeExtractor.h @@ -0,0 +1,76 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PLANEEXTRACTOR_H_ +#define _PLANEEXTRACTOR_H_ + +#ifndef _MMATH_H_ +#include "math/mMath.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _ABSTRACTPOLYLIST_H_ +#include "collision/abstractPolyList.h" +#endif + + +//---------------------------------------------------------------------------- + +/// Fill a Vector with the planes from the geometry passed through this +/// PolyList. +/// +/// @see AbstractPolyList +class PlaneExtractorPolyList: public AbstractPolyList +{ +public: + // Internal data + typedef Vector VertexList; + VertexList mVertexList; + + Vector mPolyPlaneList; + + // Set by caller + Vector* mPlaneList; + + // + PlaneExtractorPolyList(); + ~PlaneExtractorPolyList(); + void clear(); + + // Virtual methods + bool isEmpty() const; + U32 addPoint(const Point3F& p); + U32 addPlane(const PlaneF& plane); + void begin(BaseMatInstance* material,U32 surfaceKey); + void plane(U32 v1,U32 v2,U32 v3); + void plane(const PlaneF& p); + void plane(const U32 index); + void vertex(U32 vi); + void end(); + + protected: + const PlaneF& getIndexedPlane(const U32 index); +}; + + +#endif diff --git a/Engine/source/collision/polytope.cpp b/Engine/source/collision/polytope.cpp new file mode 100644 index 000000000..21d9d6a8e --- /dev/null +++ b/Engine/source/collision/polytope.cpp @@ -0,0 +1,429 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "math/mMath.h" +#include "collision/collision.h" +#include "collision/polytope.h" + +//---------------------------------------------------------------------------- + +Polytope::Polytope() +{ + VECTOR_SET_ASSOCIATION(mEdgeList); + VECTOR_SET_ASSOCIATION(mFaceList); + VECTOR_SET_ASSOCIATION(mVertexList); + VECTOR_SET_ASSOCIATION(mVolumeList); + + mVertexList.reserve(100); + mFaceList.reserve(200); + mEdgeList.reserve(100); + mVolumeList.reserve(20); + sideCount = 0; +} + + +//---------------------------------------------------------------------------- +// Box should be axis aligned in the transform space provided. + +void Polytope::buildBox(const MatrixF& transform,const Box3F& box) +{ + // Box is assumed to be axis aligned in the source space. + // Transform into geometry space + Point3F xvec,yvec,zvec,min; + transform.getColumn(0,&xvec); + xvec *= box.len_x(); + transform.getColumn(1,&yvec); + yvec *= box.len_y(); + transform.getColumn(2,&zvec); + zvec *= box.len_z(); + transform.mulP(box.minExtents,&min); + + // Initial vertices + mVertexList.setSize(8); + mVertexList[0].point = min; + mVertexList[1].point = min + yvec; + mVertexList[2].point = min + xvec + yvec; + mVertexList[3].point = min + xvec; + mVertexList[4].point = mVertexList[0].point + zvec; + mVertexList[5].point = mVertexList[1].point + zvec; + mVertexList[6].point = mVertexList[2].point + zvec; + mVertexList[7].point = mVertexList[3].point + zvec; + S32 i; + for (i = 0; i < 8; i++) + mVertexList[i].side = 0; + + // Initial faces + mFaceList.setSize(6); + for (S32 f = 0; f < 6; f++) { + Face& face = mFaceList[f]; + face.original = true; + face.vertex = 0; + } + + mFaceList[0].plane.set(mVertexList[0].point,xvec); + mFaceList[0].plane.invert(); + mFaceList[1].plane.set(mVertexList[2].point,yvec); + mFaceList[2].plane.set(mVertexList[2].point,xvec); + mFaceList[3].plane.set(mVertexList[0].point,yvec); + mFaceList[3].plane.invert(); + mFaceList[4].plane.set(mVertexList[0].point,zvec); + mFaceList[4].plane.invert(); + mFaceList[5].plane.set(mVertexList[4].point,zvec); + + // Initial edges + mEdgeList.setSize(12); + Edge* edge = mEdgeList.begin(); + S32 nextEdge = 0; + for (i = 0; i < 4; i++) { + S32 n = (i == 3)? 0: i + 1; + S32 p = (i == 0)? 3: i - 1; + edge->vertex[0] = i; + edge->vertex[1] = n; + edge->face[0] = i; + edge->face[1] = 4; + edge->next = ++nextEdge; + edge++; + edge->vertex[0] = 4 + i; + edge->vertex[1] = 4 + n; + edge->face[0] = i; + edge->face[1] = 5; + edge->next = ++nextEdge; + edge++; + edge->vertex[0] = i; + edge->vertex[1] = 4 + i; + edge->face[0] = i; + edge->face[1] = p; + edge->next = ++nextEdge; + edge++; + } + edge[-1].next = -1; + + // Volume + mVolumeList.setSize(1); + Volume& volume = mVolumeList.last(); + volume.edgeList = 0; + volume.material = -1; + volume.object = 0; + sideCount = 0; +} + + +//---------------------------------------------------------------------------- + +void Polytope::intersect(SimObject* object,const BSPNode* root) +{ + AssertFatal(mVolumeList.size() > 0,"Polytope::intersect: Missing initial volume"); + + // Always clips the first volume against the BSP + VolumeStack stack; + stack.reserve(50); + stack.increment(); + stack.last().edgeList = mVolumeList[0].edgeList; + stack.last().node = root; + + while (!stack.empty()) { + StackElement volume = stack.last(); + stack.pop_back(); + + // If it's a solid node, export the volume + const BSPNode* node = volume.node; + if (!node->backNode && !node->frontNode) { + mVolumeList.increment(); + Volume& vol = mVolumeList.last(); + vol.object = object; + vol.material = node->material; + vol.edgeList = volume.edgeList; + continue; + } + + // New front and back faces + mFaceList.increment(2); + Face& frontFace = mFaceList.last(); + Face& backFace = *(&frontFace - 1); + + backFace.original = frontFace.original = false; + backFace.vertex = frontFace.vertex = 0; + backFace.plane = frontFace.plane = node->plane; + backFace.plane.invert(); + + // New front and back volumes + StackElement frontVolume,backVolume; + frontVolume.edgeList = backVolume.edgeList = -1; + + const PlaneF& plane = node->plane; + S32 startVertex = mVertexList.size(); + + // Test & clip all the edges + S32 sideBase = ++sideCount << 1; + for (S32 i = volume.edgeList; i >= 0; i = mEdgeList[i].next) { + + // Copy into tmp first to avoid problems with the array. + Edge edge = mEdgeList[i]; + + Vertex& v0 = mVertexList[edge.vertex[0]]; + if (v0.side < sideBase) + v0.side = sideBase + ((plane.distToPlane(v0.point) >= 0)? 0: 1); + Vertex& v1 = mVertexList[edge.vertex[1]]; + if (v1.side < sideBase) + v1.side = sideBase + ((plane.distToPlane(v1.point) >= 0)? 0: 1); + + if (v0.side != v1.side) { + S32 s = v0.side - sideBase; + intersect(plane,v0.point,v1.point); + + // Split the edge into each volume + mEdgeList.increment(2); + Edge& e0 = mEdgeList.last(); + e0.next = frontVolume.edgeList; + frontVolume.edgeList = mEdgeList.size() - 1; + + Edge& e1 = *(&e0 - 1); + e1.next = backVolume.edgeList; + backVolume.edgeList = frontVolume.edgeList - 1; + + e0.vertex[0] = edge.vertex[s]; + e1.vertex[0] = edge.vertex[s ^ 1]; + e0.vertex[1] = e1.vertex[1] = mVertexList.size() - 1; + e0.face[0] = e1.face[0] = edge.face[0]; + e0.face[1] = e1.face[1] = edge.face[1]; + + // Add new edges on the plane, one to each volume + for (S32 f = 0; f < 2; f++) { + Face& face = mFaceList[edge.face[f]]; + if (face.vertex < startVertex) + face.vertex = mVertexList.size() - 1; + else { + mEdgeList.increment(2); + Edge& e0 = mEdgeList.last(); + e0.next = frontVolume.edgeList; + frontVolume.edgeList = mEdgeList.size() - 1; + + Edge& e1 = *(&e0 - 1); + e1.next = backVolume.edgeList; + backVolume.edgeList = frontVolume.edgeList - 1; + + e1.vertex[0] = e0.vertex[0] = face.vertex; + e1.vertex[1] = e0.vertex[1] = mVertexList.size() - 1; + e1.face[0] = e0.face[0] = edge.face[f]; + e1.face[1] = mFaceList.size() - 1; + e0.face[1] = e1.face[1] - 1; + } + } + } + else + if (v0.side == sideBase) { + mEdgeList.push_back(edge); + Edge& ne = mEdgeList.last(); + ne.next = frontVolume.edgeList; + frontVolume.edgeList = mEdgeList.size() - 1; + } + else { + mEdgeList.push_back(edge); + Edge& ne = mEdgeList.last(); + ne.next = backVolume.edgeList; + backVolume.edgeList = mEdgeList.size() - 1; + } + } + + // Push the front and back nodes + if (node->frontNode && frontVolume.edgeList >= 0) { + frontVolume.node = node->frontNode; + stack.push_back(frontVolume); + } + if (node->backNode && backVolume.edgeList >= 0) { + backVolume.node = node->backNode; + stack.push_back(backVolume); + } + } +} + + +//---------------------------------------------------------------------------- + +bool Polytope::intersect(const PlaneF& plane,const Point3F& sp,const Point3F& ep) +{ + // If den == 0 then the line and plane are parallel. + F32 den; + Point3F dt = ep - sp; + if ((den = plane.x * dt.x + plane.y * dt.y + plane.z * dt.z) == 0) + return false; + + mVertexList.increment(); + Vertex& v = mVertexList.last(); + F32 s = -(plane.x * sp.x + plane.y * sp.y + plane.z * sp.z + plane.d) / den; + v.point.x = sp.x + dt.x * s; + v.point.y = sp.y + dt.y * s; + v.point.z = sp.z + dt.z * s; + v.side = 0; + return true; +} + +//---------------------------------------------------------------------------- + +void Polytope::extrudeFace(int faceIdx,const VectorF& vec,Polytope* out) +{ + // Assumes the face belongs to the first volume. + out->mVertexList.clear(); + out->mFaceList.clear(); + out->mEdgeList.clear(); + out->mVolumeList.clear(); + sideCount++; + + // Front & end faces + Face nface; + nface.original = true; + nface.vertex = 0; + nface.plane = mFaceList[faceIdx].plane; + out->mFaceList.setSize(2); + out->mFaceList[0] = out->mFaceList[1] = nface; + out->mFaceList[0].plane.invert(); + + for (S32 e = mVolumeList[0].edgeList; e >= 0; e = mEdgeList[e].next) { + Edge& edge = mEdgeList[e]; + if (edge.face[0] == faceIdx || edge.face[1] == faceIdx) { + + // Build face for this edge + // Should think about calulating the plane + S32 fi = out->mFaceList.size(); + out->mFaceList.push_back(nface); + + // Reserve 4 entries to make sure the ve[] pointers + // into the list don't get invalidated. + out->mEdgeList.reserve(out->mEdgeList.size() + 4); + Edge* ve[2]; + + // Build edges for each vertex + for (S32 v = 0; v < 2; v++) { + if (mVertexList[edge.vertex[v]].side < sideCount) { + mVertexList[edge.vertex[v]].side = sideCount + out->mEdgeList.size(); + + out->mVertexList.increment(2); + out->mVertexList.end()[-1] = + out->mVertexList.end()[-2] = + mVertexList[edge.vertex[v]]; + out->mVertexList.last().point += vec; + + out->mEdgeList.increment(); + Edge& ne = out->mEdgeList.last(); + ne.next = out->mEdgeList.size(); + ne.vertex[1] = out->mVertexList.size() - 1; + ne.vertex[0] = ne.vertex[1] - 1; + ne.face[0] = ne.face[1] = -1; + ve[v] = ≠ + } + else { + S32 ei = mVertexList[edge.vertex[v]].side - sideCount; + ve[v] = &out->mEdgeList[ei]; + } + + // Edge should share this face + if (ve[v]->face[0] == -1) + ve[v]->face[0] = fi; + else + ve[v]->face[1] = fi; + } + + // Build parallel edges + out->mEdgeList.increment(2); + for (S32 i = 0; i < 2; i++ ) { + Edge& ne = out->mEdgeList.end()[i - 2]; + ne.next = out->mEdgeList.size() - 1 + i; + ne.vertex[0] = ve[0]->vertex[i]; + ne.vertex[1] = ve[1]->vertex[i]; + ne.face[0] = i; + ne.face[1] = fi; + } + } + } + + out->mEdgeList.last().next = -1; + out->mVolumeList.increment(); + Volume& nv = out->mVolumeList.last(); + nv.edgeList = 0; + nv.object = 0; + nv.material = -1; +} + + +//---------------------------------------------------------------------------- + +bool Polytope::findCollision(const VectorF& vec,Polytope::Collision *best) +{ + if (mVolumeList.size() <= 1) + return false; + if (!best->object) + best->distance = 1.0E30f; + int bestVertex = -1; + Polytope::Volume* bestVolume = NULL; + sideCount++; + + // Find the closest point + for (Volume* vol = mVolumeList.begin() + 1; + vol < mVolumeList.end(); vol++) { + for (S32 e = vol->edgeList; e >= 0; e = mEdgeList[e].next) { + Edge& edge = mEdgeList[e]; + if (mFaceList[edge.face[0]].original && + mFaceList[edge.face[1]].original) + continue; + for (S32 v = 0; v < 2; v++) { + S32 vi = edge.vertex[v]; + Vertex& vr = mVertexList[vi]; + if (vr.side != sideCount) { + vr.side = sideCount; + F32 dist = mDot(vr.point,vec); + if (dist < best->distance) { + best->distance = dist; + bestVertex = vi; + bestVolume = vol; + } + } + } + } + } + + if (bestVertex == -1) + return false; + + // Fill in the return value + best->point = mVertexList[bestVertex].point; + best->object = bestVolume->object; + best->material = bestVolume->material; + + // Pick the best face + F32 bestFaceDot = 1; + for (S32 e = bestVolume->edgeList; e >= 0; e = mEdgeList[e].next) { + Edge& edge = mEdgeList[e]; + if (edge.vertex[0] == bestVertex || edge.vertex[1] == bestVertex) { + for (S32 f = 0; f < 2; f++) { + Face& tf = mFaceList[edge.face[f]]; + F32 fd = mDot(tf.plane,vec); + if (fd < bestFaceDot) { + bestFaceDot = fd; + best->plane = tf.plane; + } + } + } + } + return true; +} + diff --git a/Engine/source/collision/polytope.h b/Engine/source/collision/polytope.h new file mode 100644 index 000000000..a28f08854 --- /dev/null +++ b/Engine/source/collision/polytope.h @@ -0,0 +1,112 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _POLYTOPE_H_ +#define _POLYTOPE_H_ + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + + +//---------------------------------------------------------------------------- + +class SimObject; + + +//---------------------------------------------------------------------------- + +class Polytope +{ + // Convex Polyhedron +public: + struct Vertex { + Point3F point; + /// Temp BSP clip info + S32 side; + }; + struct Edge { + S32 vertex[2]; + S32 face[2]; + S32 next; + }; + struct Face { + PlaneF plane; + S32 original; + /// Temp BSP clip info + S32 vertex; + }; + struct Volume + { + S32 edgeList; + S32 material; + SimObject* object; + }; + struct StackElement + { + S32 edgeList; + const BSPNode *node; + }; + struct Collision { + SimObject* object; + S32 material; + PlaneF plane; + Point3F point; + F32 distance; + + Collision() + { + object = NULL; + distance = 0.0; + } + }; + + typedef Vector EdgeList; + typedef Vector FaceList; + typedef Vector VertexList; + typedef Vector VolumeList; + typedef Vector VolumeStack; + + // + S32 sideCount; + EdgeList mEdgeList; + FaceList mFaceList; + VertexList mVertexList; + VolumeList mVolumeList; + +private: + bool intersect(const PlaneF& plane,const Point3F& sp,const Point3F& ep); + +public: + // + Polytope(); + void buildBox(const MatrixF& transform,const Box3F& box); + void intersect(SimObject*, const BSPNode* node); + inline bool didIntersect() { return mVolumeList.size() > 1; } + void extrudeFace(int fi,const VectorF& vec,Polytope* out); + bool findCollision(const VectorF& vec,Polytope::Collision *best); +}; + + + + +#endif diff --git a/Engine/source/collision/vertexPolyList.cpp b/Engine/source/collision/vertexPolyList.cpp new file mode 100644 index 000000000..1405c91ef --- /dev/null +++ b/Engine/source/collision/vertexPolyList.cpp @@ -0,0 +1,69 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "collision/vertexPolyList.h" + + +VertexPolyList::VertexPolyList() +{ + VECTOR_SET_ASSOCIATION(mVertexList); + mVertexList.reserve(100); + + mCurrObject = NULL; + mBaseMatrix = MatrixF::Identity; + mMatrix = MatrixF::Identity; + mTransformMatrix = MatrixF::Identity; + mScale.set(1.0f, 1.0f, 1.0f); + + mPlaneTransformer.setIdentity(); + + mInterestNormalRegistered = false; +} + +void VertexPolyList::clear() +{ + mVertexList.clear(); +} + +const PlaneF& VertexPolyList::getIndexedPlane(const U32 index) +{ + static const PlaneF dummy( 0, 0, 0, -1 ); + return dummy; +} + +U32 VertexPolyList::addPoint( const Point3F &p ) +{ + // Apply the transform + Point3F tp = p * mScale; + mMatrix.mulP( tp ); + + Vector::iterator iter = mVertexList.begin(); + for ( ; iter != mVertexList.end(); iter++ ) + { + if ( iter->equal( tp ) ) + return iter - mVertexList.begin(); + } + + mVertexList.push_back( tp ); + return mVertexList.size() - 1; +} diff --git a/Engine/source/collision/vertexPolyList.h b/Engine/source/collision/vertexPolyList.h new file mode 100644 index 000000000..c2fe5669b --- /dev/null +++ b/Engine/source/collision/vertexPolyList.h @@ -0,0 +1,70 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _VERTEXPOLYLIST_H_ +#define _VERTEXPOLYLIST_H_ + +#ifndef _ABSTRACTPOLYLIST_H_ +#include "collision/abstractPolyList.h" +#endif + + +/// A simple polylist which only gathers the unique verticies passed to it. +class VertexPolyList : public AbstractPolyList +{ +public: + + VertexPolyList(); + virtual ~VertexPolyList() {} + + // AbstractPolyList + U32 addPoint(const Point3F& p); + U32 addPlane(const PlaneF& plane) { return 0; } + void begin(BaseMatInstance* material,U32 surfaceKey) {} + void plane(U32 v1,U32 v2,U32 v3) {} + void plane(const PlaneF& p) {} + void plane(const U32 index) {} + void vertex(U32 vi) {} + void end() {} + const PlaneF& getIndexedPlane(const U32 index); + + /// Clears any captured verts. + void clear(); + + /// Returns true if the polylist contains no verts. + bool isEmpty() const { return mVertexList.empty(); } + + /// Returns the vertex list. + Vector& getVertexList() { return mVertexList; } + + /// Returns the constant vertex list. + const Vector& getVertexList() const { return mVertexList; } + +protected: + + /// The unique verts we captured. + Vector mVertexList; + +}; + + +#endif // _VERTEXPOLYLIST_H_ diff --git a/Engine/source/component/componentInterface.cpp b/Engine/source/component/componentInterface.cpp new file mode 100644 index 000000000..e489ca0f2 --- /dev/null +++ b/Engine/source/component/componentInterface.cpp @@ -0,0 +1,82 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "component/simComponent.h" +#include "component/componentInterface.h" +#include "core/strings/findMatch.h" +#include "core/stringTable.h" + +bool ComponentInterfaceCache::add( const char *type, const char *name, const SimComponent *owner, ComponentInterface *cinterface ) +{ + if( ( mInterfaceList.size() == 0 ) || ( enumerate( NULL, type, name, owner ) == 0 ) ) + { + mInterfaceList.increment(); + // CodeReview [tom, 3/9/2007] Seems silly to keep calling last(), why not cache the var? Yes, I know I am pedantic. + mInterfaceList.last().type = ( type == NULL ? NULL : StringTable->insert( type ) ); + mInterfaceList.last().name = ( name == NULL ? NULL : StringTable->insert( name ) ); + mInterfaceList.last().owner = owner; + mInterfaceList.last().iface = cinterface; + + return true; + } + + return false; +} + +//------------------------------------------------------------------------------ + +void ComponentInterfaceCache::clear() +{ + mInterfaceList.clear(); +} + +//------------------------------------------------------------------------------ + +U32 ComponentInterfaceCache::enumerate( ComponentInterfaceList *list, const char *type /* = NULL */, + const char *name /* = NULL */, const SimComponent *owner /* = NULL */, bool notOwner /* = false */ ) const +{ + U32 numMatches = 0; + + for( _InterfaceEntryItr i = mInterfaceList.begin(); i != mInterfaceList.end(); i++ ) + { + // Early out if limiting results by component owner + if( owner != NULL && ( + ( (*i).owner == owner && notOwner ) || + ( (*i).owner != owner && !notOwner ) ) ) + continue; + + // Match the type, short circuit if type == NULL + if( type == NULL || FindMatch::isMatch( type, (*i).type ) ) + { + // Match the name + if( name == NULL || FindMatch::isMatch( name, (*i).name ) ) + { + numMatches++; + + if( list != NULL ) + list->push_back( (*i).iface ); + } + } + } + + return numMatches; +} \ No newline at end of file diff --git a/Engine/source/component/componentInterface.h b/Engine/source/component/componentInterface.h new file mode 100644 index 000000000..311862a1c --- /dev/null +++ b/Engine/source/component/componentInterface.h @@ -0,0 +1,234 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _COMPONENTINTERFACE_H_ +#define _COMPONENTINTERFACE_H_ + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +#ifndef _SIMOBJECT_H_ +#include "console/simObject.h" +#endif + +#include "core/util/safeDelete.h" + + +class SimComponent; + + +// CodeReview [patw, 2, 13, 2007] The issue I have not addressed in this class is +// interface locking. I think that we want to do this, for sure, but I also want +// to keep it as light-weight as possible. For the most part, there should only +// ever be one thing doing something with a component at one time, but I can see +// many situations where this wouldn't be the case. When we decide to address +// the issues of locking, I believe it should be done here, at the ComponentInterface +// level. I would like lock functionality to be as centralized as possible, and +// so this is the place for it. The functionality is critical for safe useage of +// the ComponentProperty class, so implementation here would also be ideal. + +// CodeReview [patw, 2, 14, 2007] This really should be a ref-counted object +class ComponentInterface +{ + friend class SimComponent; +private: + SimObjectPtr mOwner; ///< SimComponent will directly modify this value + +public: + /// Default constructor + ComponentInterface() : mOwner(NULL) {}; + + /// Destructor + virtual ~ComponentInterface() + { + mOwner = NULL; + } + + /// This will return true if the interface is valid + virtual bool isValid() const + { + return mOwner != NULL; + } + + /// Get the owner of this interface + SimComponent *getOwner() { return mOwner; } + const SimComponent *getOwner() const { return mOwner; } +}; + +typedef VectorPtr ComponentInterfaceList; +typedef VectorPtr::iterator ComponentInterfaceListIterator; + +// These two asserts I found myself wanting a lot when doing interface methods +#ifdef TORQUE_ENABLE_ASSERTS +# define VALID_INTERFACE_ASSERT(OwningClassType) \ + AssertFatal( isValid(), "Interface validity check failed." ); \ + AssertFatal( dynamic_cast( getOwner() ) != NULL, avar( "Owner is not an instance of %s", #OwningClassType ) ) +#else +# define VALID_INTERFACE_ASSERT(OwningClassType) +#endif + +/// This class is designed to wrap an existing class or type easily to allow +/// a SimComponent to expose a property with custom processing code in an efficient +/// and safe way. Specialized templates could be written which include validation +/// on sets, and processing on gets. +/// +/// This class has a lot of "blow your leg off" potential, if you have bad aim. +/// I think that a lot of very intuitive functionality can be gained from using +/// this properly, however when implementing a specialized template, be mindful +/// of what you are doing, and + +// CodeReview [patw, 2, 13, 2007] I am very interested in making this as thin as +// possible. I really like the possibilities that it exposes as far as exposing +// "properties" to other components. I want it to be performant, however, so +// if anyone has notes on this, mark up the source, e-mail me, whatever. +template +class ComponentProperty : public ComponentInterface +{ + typedef ComponentInterface Parent; + +protected: + T *mValuePtr; + + // ComponentInterface Overrides +public: + + // Override this to add a check for valid memory. + virtual bool isValid() const + { + return ( mValuePtr != NULL ) && Parent::isValid(); + } + + // Operator overloads +public: + /// Dereferencing a value interface will allow get to do any processing and + /// return the reference to that + const T &operator*() + { + return get(); + } + + /// Assignment operator will invoke set. + const T &operator=( const T &lval ) + { + return set( lval ); + } + + // Constructors/Destructors, specialize these if needed +public: + /// Default Constructor. + ComponentProperty() : mValuePtr( NULL ) + { + mValuePtr = new T; + } + + /// Copy constructor + ComponentProperty( const T © ) + { + ComponentProperty(); + + // CodeReview [patw, 2, 13, 2007] So, the reasoning here is that I want to + // use the functionality that a specialized template implements in the set + // method. See the notes on the set method implementation. + set( copy ); + } + + /// Destructor, destroy memory + virtual ~ComponentProperty() + { + SAFE_DELETE( mValuePtr ); + } + + // This is the ComponentProperty interface that specializations of the class + // will be interested in. +public: + + /// Get the value associated with this interface. Processing code can be done + /// here for specialized implementations. + virtual const T &get() // 'const' is intentionally not used as a modifier here + { + return *mValuePtr; + } + + /// Set the value associated with this interface. Validation/processing code + /// can be done here. The copy-constructor uses the set method to do it's copy + /// so be mindful of that, or specialize the copy-constructor. + virtual const T &set( const T &t ) + { + // CodeReview [patw, 2, 13, 2007] So I am using the = operator here. Do you + // guys think that this should be the default behavior? I am trying to keep + // everything as object friendly as possible, so I figured I'd use this. + *mValuePtr = t; + return *mValuePtr; + } +}; + +/// This class is just designed to isolate the functionality of querying for, and +/// managing interfaces. +class ComponentInterfaceCache +{ + // CodeReview [patw, 2, 14, 2007] When we move this whole system to Juggernaught + // we may want to consider making safe pointers for ComponentInterfaces. Not + // sure why I put this note here. +private: + struct _InterfaceEntry + { + ComponentInterface *iface; + StringTableEntry type; + StringTableEntry name; + const SimComponent *owner; + }; + + Vector<_InterfaceEntry> mInterfaceList; + typedef Vector<_InterfaceEntry>::const_iterator _InterfaceEntryItr; + +public: + /// Add an interface to the cache. This function will return true if the interface + /// is added successfully. An interface will not be added successfully if an entry + /// in the list with the same values for 'type' and 'name' is present in the list. + /// + /// @param type Type of the interface being added. If NULL is passed, it will match any type string queried. + /// @param name Name of interface being added. If NULL is passed, it will match any name string queried. + /// @param owner The owner of the ComponentInterface being cached + /// @param cinterface The ComponentInterface being cached + virtual bool add( const char *type, const char *name, const SimComponent *owner, ComponentInterface *cinterface ); + + /// Clear the interface cache. This does not perform any operations on the contents + /// of the list. + virtual void clear(); + + /// Query the list for all of the interfaces it stores references to that match + /// the 'type' and 'name' parameters. The results of the query will be appended + /// to the list specified. Pattern matching is done using core/findMatch.h; for + /// more information on matching, see that code/header pair. Passing NULL for + /// one of these fields will match all values for that field. The return value + /// for the method will be the number of interfaces which match the query. + /// + /// @param list The list that this method will append search results on to. It is possible to pass NULL here and just receive the return value. + /// @param type An expression which the 'type' field on an added object must match to be included in results + /// @param name An expression which the 'name' field on an added object must match to be included in results + /// @param owner Limit results to components owned/not-owned by this SimComponent (see next param) + /// @param notOwner If set to true, this will enumerate only interfaces NOT owned by 'owner' + virtual U32 enumerate( ComponentInterfaceList *list, const char *type = NULL, const char *name = NULL, const SimComponent *owner = NULL, bool notOwner = false ) const; +}; + +#endif \ No newline at end of file diff --git a/Engine/source/component/dynamicConsoleMethodComponent.cpp b/Engine/source/component/dynamicConsoleMethodComponent.cpp new file mode 100644 index 000000000..c76cae60a --- /dev/null +++ b/Engine/source/component/dynamicConsoleMethodComponent.cpp @@ -0,0 +1,184 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "component/dynamicConsoleMethodComponent.h" + +IMPLEMENT_CO_NETOBJECT_V1(DynamicConsoleMethodComponent); + +ConsoleDocClass( DynamicConsoleMethodComponent, + "@brief Console object used for calling methods defined in script, from within other classes.\n\n" + "Not intended for game development, for editors or internal use only.\n\n " + "@internal"); + +//----------------------------------------------------------- +// Function name: SimComponent::handlesConsoleMethod +// Summary: +//----------------------------------------------------------- +bool DynamicConsoleMethodComponent::handlesConsoleMethod( const char *fname, S32 *routingId ) +{ + // CodeReview: Host object is now given priority over components for method + // redirection. [6/23/2007 Pat] + + // On this object? + if( isMethod( fname ) ) + { + *routingId = -1; // -1 denotes method on object +#ifdef TORQUE_DEBUG + // Inject Method. + injectMethodCall( fname ); +#endif + return true; + } + + // on this objects components? + S32 nI = 0; + VectorPtr &componentList = lockComponentList(); + for( SimComponentIterator nItr = componentList.begin(); nItr != componentList.end(); nItr++, nI++ ) + { + SimObject *pComponent = dynamic_cast(*nItr); + if( pComponent != NULL && pComponent->isMethod( fname ) ) + { + *routingId = -2; // -2 denotes method on component + unlockComponentList(); + +#ifdef TORQUE_DEBUG + // Inject Method. + injectMethodCall( fname ); +#endif + return true; + } + } + unlockComponentList(); + + return false; +} + +const char *DynamicConsoleMethodComponent::callMethod( S32 argc, const char* methodName, ... ) +{ + const char *argv[128]; + methodName = StringTable->insert( methodName ); + + argc++; + + va_list args; + va_start(args, methodName); + for(S32 i = 0; i < argc; i++) + argv[i+2] = va_arg(args, const char *); + va_end(args); + + // FIXME: the following seems a little excessive. I wonder why it's needed? + argv[0] = methodName; + argv[1] = methodName; + argv[2] = methodName; + + return callMethodArgList( argc , argv ); +} + +#ifdef TORQUE_DEBUG +/// Inject Method Call. +void DynamicConsoleMethodComponent::injectMethodCall( const char* method ) +{ + // Get Call Method. + StringTableEntry callMethod = StringTable->insert( method ); + + // Find Call Method Metric. + callMethodMetricType::Iterator itr = mCallMethodMetrics.find( callMethod ); + + // Did we find the method? + if ( itr == mCallMethodMetrics.end() ) + { + // No, so set the call count to initially be 1. + itr = mCallMethodMetrics.insert( callMethod, 1 ); + } + else + { + // Increment Call Count. + itr->value++; + } +} +#endif + +const char* DynamicConsoleMethodComponent::callMethodArgList( U32 argc, const char *argv[], bool callThis /* = true */ ) +{ +#ifdef TORQUE_DEBUG + injectMethodCall( argv[0] ); +#endif + + return _callMethod( argc, argv, callThis ); +} + +// Call all components that implement methodName giving them a chance to operate +// Components are called in reverse order of addition +const char *DynamicConsoleMethodComponent::_callMethod( U32 argc, const char *argv[], bool callThis /* = true */ ) +{ + // Set Owner + SimObject *pThis = dynamic_cast( this ); + AssertFatal( pThis, "DynamicConsoleMethodComponent::callMethod : this should always exist!" ); + + const char *cbName = StringTable->insert(argv[0]); + + if( getComponentCount() > 0 ) + { + lockComponentList(); + for( int i = getComponentCount() - 1; i >= 0; i-- ) + //for( SimComponentIterator nItr = componentList.end(); nItr != componentList.begin(); nItr-- ) + { + argv[0] = cbName; + + SimComponent *pComponent = dynamic_cast( getComponent( i ) ); + AssertFatal( pComponent, "DynamicConsoleMethodComponent::callMethod - NULL component in list!" ); + + DynamicConsoleMethodComponent *pThisComponent = dynamic_cast( pComponent ); + AssertFatal( pThisComponent, "DynamicConsoleMethodComponent::callMethod - Non DynamicConsoleMethodComponent component attempting to callback!"); + + // Only call on first depth components + // Should isMethod check these calls? [11/22/2006 justind] + if(pComponent->isEnabled()) + Con::execute( pThisComponent, argc, argv ); + + // Bail if this was the first element + //if( nItr == componentList.begin() ) + // break; + } + unlockComponentList(); + } + + // Set Owner Field + const char* result = ""; + if(callThis) + result = Con::execute( pThis, argc, argv, true ); // true - exec method onThisOnly, not on DCMCs + + return result; +} + +ConsoleMethod( DynamicConsoleMethodComponent, callMethod, void, 3, 64 , "(methodName, argi) Calls script defined method\n" + "@param methodName The method's name as a string\n" + "@param argi Any arguments to pass to the method\n" + "@return No return value" + "@note %obj.callMethod( %methodName, %arg1, %arg2, ... );\n") + +{ + object->callMethodArgList( argc - 1, argv + 2 ); +} + +////////////////////////////////////////////////////////////////////////// + diff --git a/Engine/source/component/dynamicConsoleMethodComponent.h b/Engine/source/component/dynamicConsoleMethodComponent.h new file mode 100644 index 000000000..479642f25 --- /dev/null +++ b/Engine/source/component/dynamicConsoleMethodComponent.h @@ -0,0 +1,89 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _DYNAMIC_CONSOLEMETHOD_COMPONENT_H_ +#define _DYNAMIC_CONSOLEMETHOD_COMPONENT_H_ + +#ifndef _SIMCOMPONENT_H_ +#include "component/simComponent.h" +#endif + +#ifndef _CONSOLEINTERNAL_H_ +#include "console/consoleInternal.h" +#endif + +#ifndef _ICALLMETHOD_H_ +#include "console/ICallMethod.h" +#endif + +#ifdef TORQUE_DEBUG +#ifndef _TDICTIONARY_H_ +#include "core/util/tDictionary.h" +#endif +#endif + +//----------------------------------------------------------------------------- + +class DynamicConsoleMethodComponent : public SimComponent, public ICallMethod +{ +#ifdef TORQUE_DEBUG +public: + typedef Map callMethodMetricType; +#endif + +private: + typedef SimComponent Parent; + +#ifdef TORQUE_DEBUG + // Call Method Debug Stat. + callMethodMetricType mCallMethodMetrics; +#endif + +protected: + /// Internal callMethod : Actually does component notification and script method execution + /// @attention This method does some magic to the argc argv to make Con::execute act properly + /// as such it's internal and should not be exposed or used except by this class + virtual const char* _callMethod( U32 argc, const char *argv[], bool callThis = true ); + +public: + +#ifdef TORQUE_DEBUG + /// Call Method Metrics. + const callMethodMetricType& getCallMethodMetrics( void ) const { return mCallMethodMetrics; }; + + /// Inject Method Call. + void injectMethodCall( const char* method ); +#endif + + /// Call Method + virtual const char* callMethodArgList( U32 argc, const char *argv[], bool callThis = true ); + + /// Call Method format string + const char* callMethod( S32 argc, const char* methodName, ... ); + + // query for console method data + virtual bool handlesConsoleMethod(const char * fname, S32 * routingId); + + DECLARE_CONOBJECT(DynamicConsoleMethodComponent); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/component/interfaces/IProcessInput.h b/Engine/source/component/interfaces/IProcessInput.h new file mode 100644 index 000000000..3526848c3 --- /dev/null +++ b/Engine/source/component/interfaces/IProcessInput.h @@ -0,0 +1,44 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _I_PROCESSINPUT_H_ +#define _I_PROCESSINPUT_H_ + +#include "platform/event.h" + + +// CodeReview : Input can come from a number of places to end up in +// torque, but in the torque world we don't want to expose this +// information to the user. This interface bridges the gap between +// other input devices working details and input as torque understands it. +// Thoughts? [7/6/2007 justind] +class IProcessInput +{ +public: + virtual bool processInputEvent( InputEventInfo &inputEvent ) = 0; +}; + + + + + +#endif \ No newline at end of file diff --git a/Engine/source/component/moreAdvancedComponent.cpp b/Engine/source/component/moreAdvancedComponent.cpp new file mode 100644 index 000000000..02f8f4fe8 --- /dev/null +++ b/Engine/source/component/moreAdvancedComponent.cpp @@ -0,0 +1,107 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "component/moreAdvancedComponent.h" +#include "unit/test.h" + +// unitTest_runTests("Component/MoreAdvancedComponent"); + +////////////////////////////////////////////////////////////////////////// + +IMPLEMENT_CONOBJECT(MoreAdvancedComponent); + +ConsoleDocClass( MoreAdvancedComponent, + "@brief This is a slightly more advanced component which will be used to demonstrate " + "components which are dependent on other components.\n\n" + "Not intended for game development, for editors or internal use only.\n\n " + "@internal"); + +bool MoreAdvancedComponent::onComponentRegister( SimComponent *owner ) +{ + if( !Parent::onComponentRegister( owner ) ) + return false; + + // This will return the first interface of type SimpleComponent that is cached + // on the parent object. + mSCInterface = owner->getInterface(); + + // If we can't find this interface, our component can't function, so false + // will be returned, and this will signify, to the top-level component, that it + // should fail the onAdd call. + return ( mSCInterface != NULL ); +} + +bool MoreAdvancedComponent::testDependentInterface() +{ + // These two requirements must be met in order for the test to proceed, so + // lets check them. + if( mSCInterface == NULL || !mSCInterface->isValid() ) + return false; + + return mSCInterface->isFortyTwo( 42 ); +} + +////////////////////////////////////////////////////////////////////////// + +using namespace UnitTesting; + +CreateUnitTest(MoreAdvancedComponentTest, "Component/MoreAdvancedComponent") +{ + void run() + { + // Create component instances and compose them. + SimComponent *parentComponent = new SimComponent(); + SimpleComponent *simpleComponent = new SimpleComponent(); + MoreAdvancedComponent *moreAdvComponent = new MoreAdvancedComponent(); + // CodeReview note that the interface pointer isn't initialized in a ctor + // on the components, so it's bad memory against which you might + // be checking in testDependentInterface [3/3/2007 justind] + parentComponent->addComponent( simpleComponent ); + parentComponent->addComponent( moreAdvComponent ); + + simpleComponent->registerObject(); + moreAdvComponent->registerObject(); + + // Put a break-point here, follow the onAdd call, and observe the order in + // which the SimComponent::onAdd function executes. You will see the interfaces + // get cached, and the dependent interface query being made. + parentComponent->registerObject(); + + // If the MoreAdvancedComponent found an interface, than the parentComponent + // should have returned true, from onAdd, and should therefore be registered + // properly with the Sim + test( parentComponent->isProperlyAdded(), "Parent component not properly added!" ); + + // Now lets test the interface. You can step through this, as well. + test( moreAdvComponent->testDependentInterface(), "Dependent interface test failed." ); + + // CodeReview is there a reason we can't just delete the parentComponent here? [3/3/2007 justind] + // + // Clean up + parentComponent->removeComponent( simpleComponent ); + parentComponent->removeComponent( moreAdvComponent ); + + parentComponent->deleteObject(); + moreAdvComponent->deleteObject(); + simpleComponent->deleteObject(); + } +}; \ No newline at end of file diff --git a/Engine/source/component/moreAdvancedComponent.h b/Engine/source/component/moreAdvancedComponent.h new file mode 100644 index 000000000..d4891202d --- /dev/null +++ b/Engine/source/component/moreAdvancedComponent.h @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MOREADVANCEDCOMPONENT_H_ +#define _MOREADVANCEDCOMPONENT_H_ + +#ifndef _SIMPLECOMPONENT_H_ +#include "component/simpleComponent.h" +#endif + +/// This is a slightly more advanced component which will be used to demonstrate +/// components which are dependent on other components. +class MoreAdvancedComponent : public SimComponent +{ + typedef SimComponent Parent; + +protected: + // This component is going to be dependent on a SimpleComponentInterface being + // queried off of it's parent object. This will store that interface that + // will get queried during onComponentRegister() + SimpleComponentInterface *mSCInterface; + +public: + DECLARE_CONOBJECT(MoreAdvancedComponent); + + // Firstly, take a look at the documentation for this function in simComponent.h. + // We will be overloading this method to query the component heirarchy for our + // dependent interface, as noted above. + virtual bool onComponentRegister( SimComponent *owner ); + + // This function will try to execute a function through the interface that this + // component is dependent on. + virtual bool testDependentInterface(); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/component/simComponent.cpp b/Engine/source/component/simComponent.cpp new file mode 100644 index 000000000..f886a4a86 --- /dev/null +++ b/Engine/source/component/simComponent.cpp @@ -0,0 +1,452 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/simObject.h" +#include "console/consoleTypes.h" +#include "component/simComponent.h" +#include "core/stream/stream.h" + +SimComponent::SimComponent() : mOwner( NULL ) +{ + mComponentList.clear(); + mMutex = Mutex::createMutex(); + + mEnabled = true; + mTemplate = false; +} + +SimComponent::~SimComponent() +{ + Mutex::destroyMutex( mMutex ); + mMutex = NULL; +} + +IMPLEMENT_CO_NETOBJECT_V1(SimComponent); + +ConsoleDocClass( SimComponent, + "@brief Legacy component system, soon to be deprecated.\n\n" + "Not intended for game development, for editors or internal use only.\n\n " + "@internal"); + +bool SimComponent::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + // Register + _registerInterfaces( this ); + + if( !_registerComponents( this ) ) + return false; + + //Con::executef( this, 1, "onAdd" ); + + return true; +} + +void SimComponent::_registerInterfaces( SimComponent *owner ) +{ + // First call this to expose the interfaces that this component will cache + // before examining the list of subcomponents + registerInterfaces( owner ); + + // Early out to avoid mutex lock and such + if( !hasComponents() ) + return; + + VectorPtr &components = lockComponentList(); + for( SimComponentIterator i = components.begin(); i != components.end(); i++ ) + { + (*i)->mOwner = owner; + + // Tell the component itself to register it's interfaces + (*i)->registerInterfaces( owner ); + + (*i)->mOwner = NULL; // This tests to see if the object's onComponentRegister call will call up to the parent. + + // Recurse + (*i)->_registerInterfaces( owner ); + } + + unlockComponentList(); +} + +bool SimComponent::_registerComponents( SimComponent *owner ) +{ + // This method will return true if the object contains no components. See the + // documentation for SimComponent::onComponentRegister for more information + // on this behavior. + bool ret = true; + + // If this doesn't contain components, don't even lock the list. + if( hasComponents() ) + { + VectorPtr &components = lockComponentList(); + for( SimComponentIterator i = components.begin(); i != components.end(); i++ ) + { + if( !(*i)->onComponentRegister( owner ) ) + { + ret = false; + break; + } + + AssertFatal( (*i)->mOwner == owner, "Component failed to call parent onComponentRegister!" ); + + // Recurse + if( !(*i)->_registerComponents( owner ) ) + { + ret = false; + break; + } + } + + unlockComponentList(); + } + + return ret; +} + +void SimComponent::_unregisterComponents() +{ + if( !hasComponents() ) + return; + + VectorPtr &components = lockComponentList(); + for( SimComponentIterator i = components.begin(); i != components.end(); i++ ) + { + (*i)->onComponentUnRegister(); + + AssertFatal( (*i)->mOwner == NULL, "Component failed to call parent onUnRegister" ); + + // Recurse + (*i)->_unregisterComponents(); + } + + unlockComponentList(); +} + +void SimComponent::onRemove() +{ + //Con::executef(this, 1, "onRemove"); + + _unregisterComponents(); + + // Delete all components + VectorPtr&componentList = lockComponentList(); + while(componentList.size() > 0) + { + SimComponent *c = componentList[0]; + componentList.erase( componentList.begin() ); + + if( c->isProperlyAdded() ) + c->deleteObject(); + else if( !c->isRemoved() && !c->isDeleted() ) + delete c; + // else, something else is deleting this, don't mess with it + } + unlockComponentList(); + + Parent::onRemove(); +} + +////////////////////////////////////////////////////////////////////////// + +bool SimComponent::processArguments(S32 argc, const char **argv) +{ + for(S32 i = 0; i < argc; i++) + { + SimComponent *obj = dynamic_cast (Sim::findObject(argv[i]) ); + if(obj) + addComponent(obj); + else + Con::printf("SimComponent::processArguments - Invalid Component Object \"%s\"", argv[i]); + } + return true; +} + +////////////////////////////////////////////////////////////////////////// + +void SimComponent::initPersistFields() +{ + addGroup("Component"); + + addProtectedField( "Template", TypeBool, Offset(mTemplate, SimComponent), + &setIsTemplate, &defaultProtectedGetFn, + "Places the object in a component set for later use in new levels." ); + + endGroup("Component"); + + // Call Parent. + Parent::initPersistFields(); +} + +//------------------------------------------------------------------------------ + +bool SimComponent::getInterfaces( ComponentInterfaceList *list, const char *type /* = NULL */, const char *name /* = NULL */, + const SimComponent *owner /* = NULL */, bool notOwner /* = false */ ) +{ + AssertFatal( list != NULL, "Passing NULL for a list is not supported functionality for SimComponents." ); + return ( mInterfaceCache.enumerate( list, type, name, owner, notOwner ) > 0 ); +} + +bool SimComponent::registerCachedInterface( const char *type, const char *name, SimComponent *interfaceOwner, ComponentInterface *cinterface ) +{ + if( mInterfaceCache.add( type, name, interfaceOwner, cinterface ) ) + { + cinterface->mOwner = interfaceOwner; + + // Recurse + if( mOwner != NULL ) + return mOwner->registerCachedInterface( type, name, interfaceOwner, cinterface ); + + return true; + } + + // So this is not a good assert, because it will get triggered due to the recursive + // nature of interface caching. I want to keep it here, though, just so nobody + // else thinks, "Oh I'll add an assert here." + // + //AssertFatal( false, avar( "registerCachedInterface failed, probably because interface with type '%s', name '%s' and owner with SimObjectId '%d' already exists", + // type, name, interfaceOwner->getId() ) ); + + return false; +} + +////////////////////////////////////////////////////////////////////////// +// Component Management +////////////////////////////////////////////////////////////////////////// + +bool SimComponent::addComponentFromField( void* obj, const char* data ) +{ + SimComponent *pComponent = dynamic_cast( Sim::findObject( data ) ); + if( pComponent != NULL ) + static_cast(obj)->addComponent( pComponent ); + return false; +} + +// Add Component to this one +bool SimComponent::addComponent( SimComponent *component ) +{ + AssertFatal( dynamic_cast(component), "SimComponent - Cannot add non SimObject derived components!" ); + + MutexHandle mh; + if( mh.lock( mMutex, true ) ) + { + for( SimComponentIterator nItr = mComponentList.begin(); nItr != mComponentList.end(); nItr++ ) + { + SimComponent *pComponent = dynamic_cast(*nItr); + AssertFatal( pComponent, "SimComponent::addComponent - NULL component in list!" ); + if( pComponent == component ) + return true; + } + + if(component->onComponentAdd(this)) + { + component->mOwner = this; + mComponentList.push_back( component ); + return true; + } + } + + return false; +} + +// Remove Component from this one +bool SimComponent::removeComponent( SimComponent *component ) +{ + MutexHandle mh; + if( mh.lock( mMutex, true ) ) + { + for( SimComponentIterator nItr = mComponentList.begin(); nItr != mComponentList.end(); nItr++ ) + { + SimComponent *pComponent = dynamic_cast(*nItr); + AssertFatal( pComponent, "SimComponent::removeComponent - NULL component in list!" ); + if( pComponent == component ) + { + AssertFatal( component->mOwner == this, "Somehow we contain a component who doesn't think we are it's owner." ); + (*nItr)->onComponentRemove(this); + component->mOwner = NULL; + mComponentList.erase( nItr ); + return true; + } + } + } + return false; +} + +////////////////////////////////////////////////////////////////////////// + +bool SimComponent::onComponentAdd(SimComponent *target) +{ + Con::executef(this, "onComponentAdd", Con::getIntArg(target->getId())); + return true; +} + +void SimComponent::onComponentRemove(SimComponent *target) +{ + Con::executef(this, "onComponentRemove", Con::getIntArg(target->getId())); +} + +////////////////////////////////////////////////////////////////////////// + +ComponentInterface *SimComponent::getInterface(const char *type /* = NULL */, const char *name /* = NULL */, + const SimComponent *owner /* = NULL */, bool notOwner /* = false */) +{ + ComponentInterfaceList iLst; + + if( getInterfaces( &iLst, type, name, owner, notOwner ) ) + return iLst[0]; + + return NULL; +} + +////////////////////////////////////////////////////////////////////////// + +bool SimComponent::writeField(StringTableEntry fieldname, const char* value) +{ + if (!Parent::writeField(fieldname, value)) + return false; + + if( fieldname == StringTable->insert("owner") ) + return false; + + return true; +} + +void SimComponent::write(Stream &stream, U32 tabStop, U32 flags /* = 0 */) +{ + MutexHandle handle; + handle.lock(mMutex); // When this goes out of scope, it will unlock it + + // export selected only? + if((flags & SelectedOnly) && !isSelected()) + { + for(U32 i = 0; i < mComponentList.size(); i++) + mComponentList[i]->write(stream, tabStop, flags); + + return; + } + + stream.writeTabs(tabStop); + char buffer[1024]; + dSprintf(buffer, sizeof(buffer), "new %s(%s) {\r\n", getClassName(), getName() ? getName() : ""); + stream.write(dStrlen(buffer), buffer); + writeFields(stream, tabStop + 1); + + if(mComponentList.size()) + { + stream.write(2, "\r\n"); + + stream.writeTabs(tabStop+1); + stream.writeLine((U8 *)"// Note: This is a list of behaviors, not arbitrary SimObjects as in a SimGroup or SimSet.\r\n"); + + for(U32 i = 0; i < mComponentList.size(); i++) + mComponentList[i]->write(stream, tabStop + 1, flags); + } + + stream.writeTabs(tabStop); + stream.write(4, "};\r\n"); +} + +////////////////////////////////////////////////////////////////////////// +// Console Methods +////////////////////////////////////////////////////////////////////////// + +ConsoleMethod( SimComponent, addComponents, bool, 3, 64, "%obj.addComponents( %compObjName, %compObjName2, ... );\n" + "Adds additional components to current list.\n" + "@param Up to 62 component names\n" + "@return Returns true on success, false otherwise.") +{ + for(S32 i = 2; i < argc; i++) + { + SimComponent *obj = dynamic_cast (Sim::findObject(argv[i]) ); + if(obj) + object->addComponent(obj); + else + Con::printf("SimComponent::addComponents - Invalid Component Object \"%s\"", argv[i]); + } + return true; +} + +ConsoleMethod( SimComponent, removeComponents, bool, 3, 64, "%obj.removeComponents( %compObjName, %compObjName2, ... );\n" + "Removes components by name from current list.\n" + "@param objNamex Up to 62 component names\n" + "@return Returns true on success, false otherwise.") +{ + for(S32 i = 2; i < argc; i++) + { + SimComponent *obj = dynamic_cast (Sim::findObject(argv[i]) ); + if(obj) + object->removeComponent(obj); + else + Con::printf("SimComponent::removeComponents - Invalid Component Object \"%s\"", argv[i]); + } + return true; +} + +ConsoleMethod( SimComponent, getComponentCount, S32, 2, 2, "() Get the current component count\n" + "@return The number of components in the list as an integer") +{ + return object->getComponentCount(); +} + +ConsoleMethod( SimComponent, getComponent, S32, 3, 3, "(idx) Get the component corresponding to the given index.\n" + "@param idx An integer index value corresponding to the desired component.\n" + "@return The id of the component at the given index as an integer") +{ + S32 idx = dAtoi(argv[2]); + if(idx < 0 || idx >= object->getComponentCount()) + { + Con::errorf("SimComponent::getComponent - Invalid index %d", idx); + return 0; + } + + SimComponent *c = object->getComponent(idx); + return c ? c->getId() : 0; +} + +ConsoleMethod(SimComponent, setEnabled, void, 3, 3, "(enabled) Sets or unsets the enabled flag\n" + "@param enabled Boolean value\n" + "@return No return value") +{ + object->setEnabled(dAtob(argv[2])); +} + +ConsoleMethod(SimComponent, isEnabled, bool, 2, 2, "() Check whether SimComponent is currently enabled\n" + "@return true if enabled and false if not") +{ + return object->isEnabled(); +} + +ConsoleMethod(SimComponent, setIsTemplate, void, 3, 3, "(template) Sets or unsets the template flag\n" + "@param template Boolean value\n" + "@return No return value") +{ + object->setIsTemplate(dAtob(argv[2])); +} + +ConsoleMethod(SimComponent, getIsTemplate, bool, 2, 2, "() Check whether SimComponent is currently a template\n" + "@return true if is a template and false if not") +{ + return object->getIsTemplate(); +} \ No newline at end of file diff --git a/Engine/source/component/simComponent.h b/Engine/source/component/simComponent.h new file mode 100644 index 000000000..2e46451d3 --- /dev/null +++ b/Engine/source/component/simComponent.h @@ -0,0 +1,256 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SIMCOMPONENT_H_ +#define _SIMCOMPONENT_H_ + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _STRINGTABLE_H_ +#include "core/stringTable.h" +#endif +#ifndef _NETOBJECT_H_ +#include "sim/netObject.h" +#endif +#ifndef _COMPONENTINTERFACE_H_ +#include "component/componentInterface.h" +#endif +#ifndef _PLATFORM_THREADS_MUTEX_H_ +#include "platform/threads/mutex.h" +#endif +#ifndef _STRINGFUNCTIONS_H_ +#include "core/strings/stringFunctions.h" +#endif + +// Forward refs +class Stream; +class ComponentInterface; +class ComponentInterfaceCache; + +class SimComponent : public NetObject +{ + typedef NetObject Parent; + +private: + VectorPtr mComponentList; ///< The Component List + void *mMutex; ///< Component List Mutex + + SimObjectPtr mOwner; ///< The component which owns this one. + + /// This is called internally to instruct the component to iterate over it's + // list of components and recursively call _registerInterfaces on their lists + // of components. + void _registerInterfaces( SimComponent *owner ); + + bool _registerComponents( SimComponent *owner ); + void _unregisterComponents(); + +protected: + ComponentInterfaceCache mInterfaceCache; ///< Stores the interfaces exposed by this component. + + bool mEnabled; + + bool mTemplate; + + // Non-const getOwner for derived classes + SimComponent *_getOwner() { return mOwner; } + + /// Returns a const reference to private mComponentList + typedef VectorPtr::iterator SimComponentIterator; + VectorPtr &lockComponentList() + { + Mutex::lockMutex( mMutex ); + return mComponentList; + }; + + void unlockComponentList() + { + Mutex::unlockMutex( mMutex ); + } + + /// onComponentRegister is called on each component by it's owner. If a component + /// has no owner, onComponentRegister will not be called on it. The purpose + /// of onComponentRegister is to allow a component to check for any external + /// interfaces, or other dependencies which it needs to function. If any component + /// in a component hierarchy returns false from it's onComponentRegister call + /// the entire hierarchy is invalid, and SimObject::onAdd will fail on the + /// top-level component. To put it another way, if a component contains other + /// components, it will be registered successfully with Sim iff each subcomponent + /// returns true from onComponentRegister. If a component does not contain + /// other components, it will not receive an onComponentRegister call. + /// + /// Overloads of this method must pass the call along to their parent, as is + /// shown in the example below. + /// + /// @code + /// bool FooComponent::onComponentRegister( SimComponent *owner ) + /// { + /// if( !Parent::onComponentRegister( owner ) ) + /// return false; + /// ... + /// } + /// @endcode + virtual bool onComponentRegister( SimComponent *owner ) + { + mOwner = owner; + return true; + } + + /// onUnregister is called when the owner is unregistering. Your object should + /// do cleanup here, as well as pass a call up the chain to the parent. + virtual void onComponentUnRegister() + { + mOwner = NULL; + } + + /// registerInterfaces is called on each component as it's owner is registering + /// it's interfaces. This is called before onComponentRegister, and should be used to + /// register all interfaces exposed by your component, as well as all callbacks + /// needed by your component. + virtual void registerInterfaces( SimComponent *owner ) + { + + } + +public: + DECLARE_CONOBJECT(SimComponent); + + /// Constructor + /// Add this component + SimComponent(); + + /// Destructor + /// Remove this component and destroy child references + virtual ~SimComponent(); + +public: + + virtual bool onAdd(); + virtual void onRemove(); + + static void initPersistFields(); + + virtual bool processArguments(S32 argc, const char **argv); + + bool isEnabled() const { return mEnabled; } + + void setEnabled( bool value ) { mEnabled = value; } + + /// Will return true if this object contains components. + bool hasComponents() const { return ( mComponentList.size() > 0 ); }; + + /// The component which owns this object + const SimComponent *getOwner() const { return mOwner; }; + + // Component Information + inline virtual StringTableEntry getComponentName() { return StringTable->insert( getClassName() ); }; + + /// Protected 'Component' Field setter that will add a component to the list. + static bool addComponentFromField(void* obj, const char* data); + + /// Add Component to this one + virtual bool addComponent( SimComponent *component ); + + /// Remove Component from this one + virtual bool removeComponent( SimComponent *component ); + + /// Clear Child components of this one + virtual bool clearComponents() { mComponentList.clear(); return true; }; + + virtual bool onComponentAdd(SimComponent *target); + virtual void onComponentRemove(SimComponent *target); + + S32 getComponentCount() { return mComponentList.size(); } + SimComponent *getComponent(S32 idx) { return mComponentList[idx]; } + + SimComponentIterator find(SimComponentIterator first, SimComponentIterator last, SimComponent *value) + { + return ::find(first, last, value); + } + + static bool setIsTemplate( void *object, const char *index, const char *data ) + { static_cast(object)->setIsTemplate( dAtob( data ) ); return false; }; + virtual void setIsTemplate( const bool pTemplate ) { mTemplate = pTemplate; } + bool getIsTemplate() const { return mTemplate; } + + virtual void write(Stream &stream, U32 tabStop, U32 flags = 0); + virtual bool writeField(StringTableEntry fieldname, const char* value); + + + /// getInterfaces allows the caller to enumerate the interfaces exposed by + /// this component. This method can be overwritten to expose interfaces + /// which are not cached on the object, before passing the call to the Parent. + /// This can be used delay interface creation until it is queried for, instead + /// of creating it on initialization, and caching it. Returns false if no results + /// were found + /// + /// @param list The list that this method will append search results on to. + /// @param type An expression which the 'type' field on an added object must match to be included in results + /// @param name An expression which the 'name' field on an added object must match to be included in results + /// @param owner Limit results to components owned/not-owned by this SimComponent (see next param) + /// @param notOwner If set to true, this will enumerate only interfaces NOT owned by 'owner' + virtual bool getInterfaces( ComponentInterfaceList *list, const char *type = NULL, const char *name = NULL, const SimComponent *owner = NULL, bool notOwner = false ); // const omission intentional + + + /// These two methods allow for easy query of component interfaces if you know + /// exactly what you are looking for, and don't mind being passed back the first + /// matching result. + ComponentInterface *getInterface( const char *type = NULL, const char *name = NULL, const SimComponent *owner = NULL, bool notOwner = false ); + + template + T *getInterface( const char *type = NULL, const char *name = NULL, const SimComponent *owner = NULL, bool notOwner = false ); + + /// Add an interface to the cache. This function will return true if the interface + /// is added successfully. An interface will not be added successfully if an entry + /// in this components cache with the same values for 'type' and 'name' is present. + /// + /// @param type Type of the interface being added. If NULL is passed, it will match any type string queried. + /// @param name Name of interface being added. If NULL is passed, it will match any name string queried. + /// @param interfaceOwner The component which owns the interface being cached + /// @param cinterface The ComponentInterface being cached + bool registerCachedInterface( const char *type, const char *name, SimComponent *interfaceOwner, ComponentInterface *cinterface ); +}; + +////////////////////////////////////////////////////////////////////////// + +template +T *SimComponent::getInterface( const char *type /* = NULL */, const char *name /* = NULL */, + const SimComponent *owner /* = NULL */, bool notOwner /* = false */ ) +{ + ComponentInterfaceList iLst; + + if( getInterfaces( &iLst, type, name, owner, notOwner ) ) + { + ComponentInterfaceListIterator itr = iLst.begin(); + + while( dynamic_cast( *itr ) == NULL ) + itr++; + + if( itr != iLst.end() ) + return static_cast( *itr ); + } + + return NULL; +} + +#endif // _SIMCOMPONENT_H_ diff --git a/Engine/source/component/simpleComponent.cpp b/Engine/source/component/simpleComponent.cpp new file mode 100644 index 000000000..aa1147c63 --- /dev/null +++ b/Engine/source/component/simpleComponent.cpp @@ -0,0 +1,151 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "component/simpleComponent.h" + +IMPLEMENT_CONOBJECT(SimpleComponent); + +ConsoleDocClass( SimpleComponent, + "@brief The purpose of this component is to provide a minimalistic component that " + "exposes a simple, cached interface\n\n" + "Soon to be deprecated, internal only.\n\n " + "@internal"); + +////////////////////////////////////////////////////////////////////////// +// It may seem like some weak sauce to use a unit test for this, however +// it is very, very easy to set breakpoints in a unit test, and trace +// execution in the debugger, so I will use a unit test. +// +// Note I am not using much actual 'test' functionality, just providing +// an easy place to examine the functionality that was described and implemented +// in the header file. +// +// If you want to run this code, simply run Torque, pull down the console, and +// type: +// unitTest_runTests("Components/SimpleComponent"); + +#include "unit/test.h" +using namespace UnitTesting; + +CreateUnitTest(TestSimpleComponent, "Components/SimpleComponent") +{ + void run() + { + // When instantiating, and working with a SimObject in C++ code, such as + // a unit test, you *may not* allocate a SimObject off of the stack. + // + // For example: + // SimpleComponent sc; + // is a stack allocation. This memory is allocated off of the program stack + // when the function is called. SimObject deletion is done via SimObject::deleteObject() + // and the last command of this method is 'delete this;' That command will + // cause an assert if it is called on stack-allocated memory. Therefor, when + // instantiating SimObjects in C++ code, it is imperitive that you keep in + // mind that if any script calls 'delete()' on that SimObject, or any other + // C++ code calls 'deleteObject()' on that SimObject, it will crash. + SimpleComponent *sc = new SimpleComponent(); + + // SimObject::registerObject must be called on a SimObject before it is + // fully 'hooked in' to the engine. + // + // Tracing execution of this function will let you see onAdd get called on + // the component, and you will see it cache the interface we exposed. + sc->registerObject(); + + // It is *not* required that a component always be owned by a component (obviously) + // however I am using an owner so that you can trace execution of recursive + // calls to cache interfaces and such. + SimComponent *testOwner = new SimComponent(); + + // Add the test component to it's owner. This will set the 'mOwner' field + // of 'sc' to the address of 'testOwner' + testOwner->addComponent( sc ); + + // If you step-into this registerObject the same way as the previous one, + // you will be able to see the recursive caching of the exposed interface. + testOwner->registerObject(); + + // Now to prove that object composition is working properly, lets ask + // both of these components for their interface lists... + + // The ComponentInterfaceList is a typedef for type 'VectorPtr' + // and it will be used by getInterfaces() to store the results of the interface + // query. This is the "complete" way to obtain an interface, and it is too + // heavy-weight for most cases. A simplified query will be performed next, + // to demonstrate the usage of both. + ComponentInterfaceList iLst; + + // This query requests all interfaces, on all components, regardless of name + // or owner. + sc->getInterfaces( &iLst, + // This is the type field. I am passing NULL here to signify that the query + // should match all values of 'type' in the list. + NULL, + + // The name field, let's pass NULL again just so when you trace execution + // you can see how queries work in the simple case, first. + NULL ); + + // Lets process the list that we've gotten back, and find the interface that + // we want. + SimpleComponentInterface *scQueriedInterface = NULL; + + for( ComponentInterfaceListIterator i = iLst.begin(); i != iLst.end(); i++ ) + { + scQueriedInterface = dynamic_cast( *i ); + + if( scQueriedInterface != NULL ) + break; + } + + AssertFatal( scQueriedInterface != NULL, "No valid SimpleComponentInterface was found in query" ); + + // Lets do it again, only we will execute the query on the parent instead, + // in a simplified way. Remember the parent component doesn't expose any + // interfaces at all, so the success of this behavior is entirely dependent + // on the recursive registration that occurs in registerInterfaces() + SimpleComponentInterface *ownerQueriedInterface = testOwner->getInterface(); + + AssertFatal( ownerQueriedInterface != NULL, "No valid SimpleComponentInterface was found in query" ); + + // We should now have two pointers to the same interface obtained by querying + // different components. + test( ownerQueriedInterface == scQueriedInterface, "This really shouldn't be possible to fail given the setup of the test" ); + + // Lets call the method that was exposed on the component via the interface. + // Trace the execution of this function, if you wish. + test( ownerQueriedInterface->isFortyTwo( 42 ), "Don't panic, but it's a bad day in the component system." ); + test( scQueriedInterface->isFortyTwo( 42 ), "Don't panic, but it's a bad day in the component system." ); + + // So there you have it. Writing a simple component that exposes a cached + // interface, and testing it. It's time to clean up. + testOwner->removeComponent( sc ); + + sc->deleteObject(); + testOwner->deleteObject(); + + // Interfaces do not need to be freed. In Juggernaught, these will be ref-counted + // for more robust behavior. Right now, however, the values of our two interface + // pointers, scQueriedInterface and ownerQueriedInterface, reference invalid + // memory. + } +}; \ No newline at end of file diff --git a/Engine/source/component/simpleComponent.h b/Engine/source/component/simpleComponent.h new file mode 100644 index 000000000..e4c32bfb3 --- /dev/null +++ b/Engine/source/component/simpleComponent.h @@ -0,0 +1,159 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SIMPLECOMPONENT_H_ +#define _SIMPLECOMPONENT_H_ + +#ifndef _SIMCOMPONENT_H_ +#include "component/simComponent.h" +#endif + +#ifndef _COMPONENTINTERFACE_H_ +#include "component/componentInterface.h" +#endif + +/// This is a very simple interface. Interfaces provide ways for components to +/// interact with each-other, and query each-other for functionality. It makes it +/// possible for two components to be interdependent on one another, as well. An +/// interface should make accessor calls to it's owner for functionality, and +/// generally be as thin of a layer as possible. +class SimpleComponentInterface : public ComponentInterface +{ +public: + bool isFortyTwo( const U32 test ); +}; + +////////////////////////////////////////////////////////////////////////// +/// The purpose of this component is to provide a minimalistic component that +/// exposes a simple, cached interface +class SimpleComponent : public SimComponent +{ + typedef SimComponent Parent; + +protected: + SimpleComponentInterface mSCInterface; + +public: + // Components are still SimObjects, and need to be declared and implemented + // with the standard macros + DECLARE_CONOBJECT(SimpleComponent); + + ////////////////////////////////////////////////////////////////////////// + // SimComponent overloads. + + // This method is called on each component as it's parent is getting onAdd + // called. The purpose of overloading this method is to expose cached interfaces + // before onComponentRegister is called, so that other components can depend on the + // interfaces you expose in order to register properly. This functionality + // will be demonstrated in a more advanced example. + virtual void registerInterfaces( SimComponent *owner ) + { + // While it is not imperative that we pass this call to the Parent in this + // example, if there existed a class-heirarchy of components, it would be + // critical, so for good practice, call up to the Parent. + Parent::registerInterfaces( owner ); + + // Now we should go ahead and register our cached interface. What we are doing + // is telling the component which contains this component (if it exists) + // all of the interfaces that we expose. When this call is made, it will + // recurse up the owner list. + // + // For example, there exists components A, B, and C. + // A owns B, and B owns C. + // + // If C exposes a cached interface, it will expose it via registerCachedInterface + // when registerInterfaces is recursively called. It will add it's interface to + // it's cache list, and then pass the register call up to it's parent. The parent + // will also cache the interface, and continue to pass the cache call up the + // child->parent chain until there exists no parent. + // + // The result is that, if C exposes an interface 'foo', and A owns B, and + // B owns C, an interface request for 'foo' given to component A will result + // in 'foo' being returned, even though A does not expose 'foo'. This makes + // it possible for a component to query it's owner for an interface, and + // not care where that interface is exposed. It also allows for game code + // to work with any SimComponent and query that component for any interface + // it wants without knowing or caring exactly where it is coming from. + // + // registerCachedInterface returns a boolean value if it was successful. + // Success results in the caching of this interface throughout the full + // child->parent chain. An interface will be added to a cache list + // successfully iff there exists no entry in that list that has matching + // values for 'type', 'name' and 'owner'. + owner->registerCachedInterface( + // The first parameter is the 'type' of the interface, this is not to be + // confused with any kind of existing console, or c++ type. It is simply + // a string which is can be set to any value + "example", + + // The next parameter is the 'name' of the interface. This is also a string + // which can be set to any value + "isfortytwo", + + // The owner of the interface. Note that the value being assigned here + // is this instance of SimpleComponent, and not the 'owner' argument + // of the function registerInterfaces that we are calling from. + this, + + // And finally the interface; a pointer to an object with type ComponentInterface + &mSCInterface ); + } + + ////////////////////////////////////////////////////////////////////////// + // Specific functionality + + /// This is the test method, it will return true if the number provided + /// is forty two + bool isFortyTwo( const U32 test ) const + { + return ( test == 42 ); + } +}; + +////////////////////////////////////////////////////////////////////////// +// Interface implementation +// +// Since interfaces themselves implement very little functionality, it is a good +// idea to inline them if at all possible. Interdependent components will be using +// these interfaces constantly, and so putting as thin of a layer between the +// functionality they expose, and the functionality the component implements is +// a good design practice. +inline bool SimpleComponentInterface::isFortyTwo( const U32 test ) +{ + // This code block will test for a valid owner in a debug build before + // performing operations on it's owner. It is worth noting that the + // ComponentInterface::isValid() method can be overridden to include + // validation specific to your interface and/or component. + AssertFatal( isValid(), "SimpleComponentInterface has not been registered properly by the component which exposes it." ); + + // This is a sanity check. The owner of this interface should have the type + // SimpleComponent, otherwise this won't work. (See further interface examples + // for some ways around this) + AssertFatal( dynamic_cast( getOwner() ) != NULL, "Owner of SimpleComponentInterface is not a SimpleComponent" ); + + // Component interfaces rely on being registered to set their mOwner + // field. This field is initialized to NULL, and then gets set by + // SimComponent when the interface is registered. + return static_cast( getOwner() )->isFortyTwo( test ); +} + +#endif \ No newline at end of file diff --git a/Engine/source/console/CMDgram.y b/Engine/source/console/CMDgram.y new file mode 100644 index 000000000..06830b7dd --- /dev/null +++ b/Engine/source/console/CMDgram.y @@ -0,0 +1,596 @@ +%{ + +// bison --defines=cmdgram.h --verbose -o cmdgram.cpp -p CMD CMDgram.y + +// Make sure we don't get gram.h twice. +#define _CMDGRAM_H_ + +#include +#include +#include "console/console.h" +#include "console/compiler.h" +#include "console/consoleInternal.h" +#include "core/strings/stringFunctions.h" + +#ifndef YYDEBUG +#define YYDEBUG 0 +#endif + +#define YYSSIZE 350 + +int outtext(char *fmt, ...); +extern int serrors; + +#define nil 0 +#undef YY_ARGS +#define YY_ARGS(x) x + +int CMDlex(); +void CMDerror(char *, ...); + +#ifdef alloca +#undef alloca +#endif +#define alloca dMalloc + +template< typename T > +struct Token +{ + T value; + U32 lineNumber; +}; + +%} +%{ + /* Reserved Word Definitions */ +%} +%token rwDEFINE rwENDDEF rwDECLARE rwDECLARESINGLETON +%token rwBREAK rwELSE rwCONTINUE rwGLOBAL +%token rwIF rwNIL rwRETURN rwWHILE rwDO +%token rwENDIF rwENDWHILE rwENDFOR rwDEFAULT +%token rwFOR rwFOREACH rwFOREACHSTR rwIN rwDATABLOCK rwSWITCH rwCASE rwSWITCHSTR +%token rwCASEOR rwPACKAGE rwNAMESPACE rwCLASS +%token rwASSERT +%token ILLEGAL_TOKEN +%{ + /* Constants and Identifier Definitions */ +%} +%token CHRCONST +%token INTCONST +%token TTAG +%token VAR +%token IDENT +%token TYPEIDENT +%token DOCBLOCK +%token STRATOM +%token TAGATOM +%token FLTCONST + +%{ + /* Operator Definitions */ +%} +%token '+' '-' '*' '/' '<' '>' '=' '.' '|' '&' '%' +%token '(' ')' ',' ':' ';' '{' '}' '^' '~' '!' '@' +%token opINTNAME opINTNAMER +%token opMINUSMINUS opPLUSPLUS +%token STMT_SEP +%token opSHL opSHR opPLASN opMIASN opMLASN opDVASN opMODASN opANDASN +%token opXORASN opORASN opSLASN opSRASN opCAT +%token opEQ opNE opGE opLE opAND opOR opSTREQ +%token opCOLONCOLON + +%union { + Token< char > c; + Token< int > i; + Token< const char* > s; + Token< char* > str; + Token< double > f; + StmtNode* stmt; + ExprNode* expr; + SlotAssignNode* slist; + VarNode* var; + SlotDecl slot; + InternalSlotDecl intslot; + ObjectBlockDecl odcl; + ObjectDeclNode* od; + AssignDecl asn; + IfStmtNode* ifnode; +} + +%type parent_block +%type case_block +%type switch_stmt +%type decl +%type decl_list +%type package_decl +%type fn_decl_stmt +%type fn_decl_list +%type statement_list +%type stmt +%type expr_list +%type expr_list_decl +%type aidx_expr +%type funcall_expr +%type assert_expr +%type object_name +%type object_args +%type stmt_expr +%type case_expr +%type class_name_expr +%type if_stmt +%type while_stmt +%type for_stmt +%type foreach_stmt +%type stmt_block +%type datablock_decl +%type object_decl +%type object_decl_list +%type object_declare_block +%type expr +%type slot_assign_list_opt +%type slot_assign_list +%type slot_assign +%type slot_acc +%type intslot_acc +%type expression_stmt +%type var_list +%type var_list_decl +%type assign_op_struct + +%left '[' +%right opMODASN opANDASN opXORASN opPLASN opMIASN opMLASN opDVASN opMDASN opNDASN opNTASN opORASN opSLASN opSRASN '=' +%left '?' ':' +%left opOR +%left opAND +%left '|' +%left '^' +%left '&' +%left opEQ opNE +%left '<' opLE '>' opGE +%left '@' opCAT opSTREQ opSTRNE +%left opSHL opSHR +%left '+' '-' +%left '*' '/' '%' +%right '!' '~' opPLUSPLUS opMINUSMINUS UNARY +%left '.' +%left opINTNAME opINTNAMER + +%% + +start + : decl_list + { } + ; + +decl_list + : + { $$ = nil; } + | decl_list decl + { if(!gStatementList) { gStatementList = $2; } else { gStatementList->append($2); } } + ; + +decl + : stmt + { $$ = $1; } + | fn_decl_stmt + { $$ = $1; } + | package_decl + { $$ = $1; } + ; + +package_decl + : rwPACKAGE IDENT '{' fn_decl_list '}' ';' + { $$ = $4; for(StmtNode *walk = ($4);walk;walk = walk->getNext() ) walk->setPackage($2.value); } + ; + +fn_decl_list + : fn_decl_stmt + { $$ = $1; } + | fn_decl_list fn_decl_stmt + { $$ = $1; ($1)->append($2); } + ; + +statement_list + : + { $$ = nil; } + | statement_list stmt + { if(!$1) { $$ = $2; } else { ($1)->append($2); $$ = $1; } } + ; + +stmt + : if_stmt + | while_stmt + | for_stmt + | foreach_stmt + | datablock_decl + | switch_stmt + | rwBREAK ';' + { $$ = BreakStmtNode::alloc( $1.lineNumber ); } + | rwCONTINUE ';' + { $$ = ContinueStmtNode::alloc( $1.lineNumber ); } + | rwRETURN ';' + { $$ = ReturnStmtNode::alloc( $1.lineNumber, NULL ); } + | rwRETURN expr ';' + { $$ = ReturnStmtNode::alloc( $1.lineNumber, $2 ); } + | expression_stmt ';' + { $$ = $1; } + | TTAG '=' expr ';' + { $$ = TTagSetStmtNode::alloc( $1.lineNumber, $1.value, $3, NULL ); } + | TTAG '=' expr ',' expr ';' + { $$ = TTagSetStmtNode::alloc( $1.lineNumber, $1.value, $3, $5 ); } + | DOCBLOCK + { $$ = StrConstNode::alloc( $1.lineNumber, $1.value, false, true ); } + ; + +fn_decl_stmt + : rwDEFINE IDENT '(' var_list_decl ')' '{' statement_list '}' + { $$ = FunctionDeclStmtNode::alloc( $1.lineNumber, $2.value, NULL, $4, $7 ); } + | rwDEFINE IDENT opCOLONCOLON IDENT '(' var_list_decl ')' '{' statement_list '}' + { $$ = FunctionDeclStmtNode::alloc( $1.lineNumber, $4.value, $2.value, $6, $9 ); } + ; + +var_list_decl + : + { $$ = NULL; } + | var_list + { $$ = $1; } + ; + +var_list + : VAR + { $$ = VarNode::alloc( $1.lineNumber, $1.value, NULL ); } + | var_list ',' VAR + { $$ = $1; ((StmtNode*)($1))->append((StmtNode*)VarNode::alloc( $3.lineNumber, $3.value, NULL ) ); } + ; + +datablock_decl + : rwDATABLOCK class_name_expr '(' expr parent_block ')' '{' slot_assign_list_opt '}' ';' + { $$ = ObjectDeclNode::alloc( $1.lineNumber, $2, $4, NULL, $5.value, $8, NULL, true, false, false); } + ; + +object_decl + : rwDECLARE class_name_expr '(' object_name parent_block object_args ')' '{' object_declare_block '}' + { $$ = ObjectDeclNode::alloc( $1.lineNumber, $2, $4, $6, $5.value, $9.slots, $9.decls, false, false, false); } + | rwDECLARE class_name_expr '(' object_name parent_block object_args ')' + { $$ = ObjectDeclNode::alloc( $1.lineNumber, $2, $4, $6, $5.value, NULL, NULL, false, false, false); } + | rwDECLARE class_name_expr '(' '[' object_name ']' parent_block object_args ')' '{' object_declare_block '}' + { $$ = ObjectDeclNode::alloc( $1.lineNumber, $2, $5, $8, $7.value, $11.slots, $11.decls, false, true, false); } + | rwDECLARE class_name_expr '(' '[' object_name ']' parent_block object_args ')' + { $$ = ObjectDeclNode::alloc( $1.lineNumber, $2, $5, $8, $7.value, NULL, NULL, false, true, false); } + | rwDECLARESINGLETON class_name_expr '(' object_name parent_block object_args ')' '{' object_declare_block '}' + { $$ = ObjectDeclNode::alloc( $1.lineNumber, $2, $4, $6, $5.value, $9.slots, $9.decls, false, false, true); } + | rwDECLARESINGLETON class_name_expr '(' object_name parent_block object_args ')' + { $$ = ObjectDeclNode::alloc( $1.lineNumber, $2, $4, $6, $5.value, NULL, NULL, false, false, true); } + ; + +parent_block + : + { $$.value = NULL; } + | ':' IDENT + { $$ = $2; } + ; + +object_name + : + { $$ = StrConstNode::alloc( CodeBlock::smCurrentParser->getCurrentLine(), "", false); } + | expr + { $$ = $1; } + ; + +object_args + : + { $$ = NULL; } + | ',' expr_list + { $$ = $2; } + ; + +object_declare_block + : + { $$.slots = NULL; $$.decls = NULL; } + | slot_assign_list + { $$.slots = $1; $$.decls = NULL; } + | object_decl_list + { $$.slots = NULL; $$.decls = $1; } + | slot_assign_list object_decl_list + { $$.slots = $1; $$.decls = $2; } + ; + +object_decl_list + : object_decl ';' + { $$ = $1; } + | object_decl_list object_decl ';' + { $1->append($2); $$ = $1; } + ; + +stmt_block + : '{' statement_list '}' + { $$ = $2; } + | stmt + { $$ = $1; } + ; + +switch_stmt + : rwSWITCH '(' expr ')' '{' case_block '}' + { $$ = $6; $6->propagateSwitchExpr($3, false); } + | rwSWITCHSTR '(' expr ')' '{' case_block '}' + { $$ = $6; $6->propagateSwitchExpr($3, true); } + ; + +case_block + : rwCASE case_expr ':' statement_list + { $$ = IfStmtNode::alloc( $1.lineNumber, $2, $4, NULL, false); } + | rwCASE case_expr ':' statement_list rwDEFAULT ':' statement_list + { $$ = IfStmtNode::alloc( $1.lineNumber, $2, $4, $7, false); } + | rwCASE case_expr ':' statement_list case_block + { $$ = IfStmtNode::alloc( $1.lineNumber, $2, $4, $5, true); } + ; + +case_expr + : expr + { $$ = $1;} + | case_expr rwCASEOR expr + { ($1)->append($3); $$=$1; } + ; + +if_stmt + : rwIF '(' expr ')' stmt_block + { $$ = IfStmtNode::alloc($1.lineNumber, $3, $5, NULL, false); } + | rwIF '(' expr ')' stmt_block rwELSE stmt_block + { $$ = IfStmtNode::alloc($1.lineNumber, $3, $5, $7, false); } + ; + +while_stmt + : rwWHILE '(' expr ')' stmt_block + { $$ = LoopStmtNode::alloc($1.lineNumber, nil, $3, nil, $5, false); } + | rwDO stmt_block rwWHILE '(' expr ')' + { $$ = LoopStmtNode::alloc($3.lineNumber, nil, $5, nil, $2, true); } + ; + +for_stmt + : rwFOR '(' expr ';' expr ';' expr ')' stmt_block + { $$ = LoopStmtNode::alloc($1.lineNumber, $3, $5, $7, $9, false); } + | rwFOR '(' expr ';' expr ';' ')' stmt_block + { $$ = LoopStmtNode::alloc($1.lineNumber, $3, $5, NULL, $8, false); } + | rwFOR '(' expr ';' ';' expr ')' stmt_block + { $$ = LoopStmtNode::alloc($1.lineNumber, $3, NULL, $6, $8, false); } + | rwFOR '(' expr ';' ';' ')' stmt_block + { $$ = LoopStmtNode::alloc($1.lineNumber, $3, NULL, NULL, $7, false); } + | rwFOR '(' ';' expr ';' expr ')' stmt_block + { $$ = LoopStmtNode::alloc($1.lineNumber, NULL, $4, $6, $8, false); } + | rwFOR '(' ';' expr ';' ')' stmt_block + { $$ = LoopStmtNode::alloc($1.lineNumber, NULL, $4, NULL, $7, false); } + | rwFOR '(' ';' ';' expr ')' stmt_block + { $$ = LoopStmtNode::alloc($1.lineNumber, NULL, NULL, $5, $7, false); } + | rwFOR '(' ';' ';' ')' stmt_block + { $$ = LoopStmtNode::alloc($1.lineNumber, NULL, NULL, NULL, $6, false); } + ; + +foreach_stmt + : rwFOREACH '(' VAR rwIN expr ')' stmt_block + { $$ = IterStmtNode::alloc( $1.lineNumber, $3.value, $5, $7, false ); } + | rwFOREACHSTR '(' VAR rwIN expr ')' stmt_block + { $$ = IterStmtNode::alloc( $1.lineNumber, $3.value, $5, $7, true ); } + ; + +expression_stmt + : stmt_expr + { $$ = $1; } + ; + +expr + : stmt_expr + { $$ = $1; } + | '(' expr ')' + { $$ = $2; } + | expr '^' expr + { $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); } + | expr '%' expr + { $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); } + | expr '&' expr + { $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); } + | expr '|' expr + { $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); } + | expr '+' expr + { $$ = FloatBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); } + | expr '-' expr + { $$ = FloatBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); } + | expr '*' expr + { $$ = FloatBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); } + | expr '/' expr + { $$ = FloatBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); } + | '-' expr %prec UNARY + { $$ = FloatUnaryExprNode::alloc( $1.lineNumber, $1.value, $2); } + | '*' expr %prec UNARY + { $$ = TTagDerefNode::alloc( $1.lineNumber, $2 ); } + | TTAG + { $$ = TTagExprNode::alloc( $1.lineNumber, $1.value ); } + | expr '?' expr ':' expr + { $$ = ConditionalExprNode::alloc( $1->dbgLineNumber, $1, $3, $5); } + | expr '<' expr + { $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); } + | expr '>' expr + { $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); } + | expr opGE expr + { $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); } + | expr opLE expr + { $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); } + | expr opEQ expr + { $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); } + | expr opNE expr + { $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); } + | expr opOR expr + { $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); } + | expr opSHL expr + { $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); } + | expr opSHR expr + { $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); } + | expr opAND expr + { $$ = IntBinaryExprNode::alloc( $1->dbgLineNumber, $2.value, $1, $3); } + | expr opSTREQ expr + { $$ = StreqExprNode::alloc( $1->dbgLineNumber, $1, $3, true); } + | expr opSTRNE expr + { $$ = StreqExprNode::alloc( $1->dbgLineNumber, $1, $3, false); } + | expr '@' expr + { $$ = StrcatExprNode::alloc( $1->dbgLineNumber, $1, $3, $2.value); } + | '!' expr + { $$ = IntUnaryExprNode::alloc($1.lineNumber, $1.value, $2); } + | '~' expr + { $$ = IntUnaryExprNode::alloc($1.lineNumber, $1.value, $2); } + | TAGATOM + { $$ = StrConstNode::alloc( $1.lineNumber, $1.value, true); } + | FLTCONST + { $$ = FloatNode::alloc( $1.lineNumber, $1.value ); } + | INTCONST + { $$ = IntNode::alloc( $1.lineNumber, $1.value ); } + | rwBREAK + { $$ = ConstantNode::alloc( $1.lineNumber, StringTable->insert("break")); } + | slot_acc + { $$ = SlotAccessNode::alloc( $1.lineNumber, $1.object, $1.array, $1.slotName ); } + | intslot_acc + { $$ = InternalSlotAccessNode::alloc( $1.lineNumber, $1.object, $1.slotExpr, $1.recurse); } + | IDENT + { $$ = ConstantNode::alloc( $1.lineNumber, $1.value ); } + | STRATOM + { $$ = StrConstNode::alloc( $1.lineNumber, $1.value, false); } + | VAR + { $$ = (ExprNode*)VarNode::alloc( $1.lineNumber, $1.value, NULL); } + | VAR '[' aidx_expr ']' + { $$ = (ExprNode*)VarNode::alloc( $1.lineNumber, $1.value, $3 ); } + ; + +slot_acc + : expr '.' IDENT + { $$.lineNumber = $1->dbgLineNumber; $$.object = $1; $$.slotName = $3.value; $$.array = NULL; } + | expr '.' IDENT '[' aidx_expr ']' + { $$.lineNumber = $1->dbgLineNumber; $$.object = $1; $$.slotName = $3.value; $$.array = $5; } + ; + +intslot_acc + : expr opINTNAME class_name_expr + { $$.lineNumber = $1->dbgLineNumber; $$.object = $1; $$.slotExpr = $3; $$.recurse = false; } + | expr opINTNAMER class_name_expr + { $$.lineNumber = $1->dbgLineNumber; $$.object = $1; $$.slotExpr = $3; $$.recurse = true; } + ; + +class_name_expr + : IDENT + { $$ = ConstantNode::alloc( $1.lineNumber, $1.value ); } + | '(' expr ')' + { $$ = $2; } + ; + +assign_op_struct + : opPLUSPLUS + { $$.lineNumber = $1.lineNumber; $$.token = '+'; $$.expr = FloatNode::alloc( $1.lineNumber, 1 ); } + | opMINUSMINUS + { $$.lineNumber = $1.lineNumber; $$.token = '-'; $$.expr = FloatNode::alloc( $1.lineNumber, 1 ); } + | opPLASN expr + { $$.lineNumber = $1.lineNumber; $$.token = '+'; $$.expr = $2; } + | opMIASN expr + { $$.lineNumber = $1.lineNumber; $$.token = '-'; $$.expr = $2; } + | opMLASN expr + { $$.lineNumber = $1.lineNumber; $$.token = '*'; $$.expr = $2; } + | opDVASN expr + { $$.lineNumber = $1.lineNumber; $$.token = '/'; $$.expr = $2; } + | opMODASN expr + { $$.lineNumber = $1.lineNumber; $$.token = '%'; $$.expr = $2; } + | opANDASN expr + { $$.lineNumber = $1.lineNumber; $$.token = '&'; $$.expr = $2; } + | opXORASN expr + { $$.lineNumber = $1.lineNumber; $$.token = '^'; $$.expr = $2; } + | opORASN expr + { $$.lineNumber = $1.lineNumber; $$.token = '|'; $$.expr = $2; } + | opSLASN expr + { $$.lineNumber = $1.lineNumber; $$.token = opSHL; $$.expr = $2; } + | opSRASN expr + { $$.lineNumber = $1.lineNumber; $$.token = opSHR; $$.expr = $2; } + ; + +stmt_expr + : funcall_expr + { $$ = $1; } + | assert_expr + { $$ = $1; } + | object_decl + { $$ = $1; } + | VAR '=' expr + { $$ = AssignExprNode::alloc( $1.lineNumber, $1.value, NULL, $3); } + | VAR '[' aidx_expr ']' '=' expr + { $$ = AssignExprNode::alloc( $1.lineNumber, $1.value, $3, $6); } + | VAR assign_op_struct + { $$ = AssignOpExprNode::alloc( $1.lineNumber, $1.value, NULL, $2.expr, $2.token); } + | VAR '[' aidx_expr ']' assign_op_struct + { $$ = AssignOpExprNode::alloc( $1.lineNumber, $1.value, $3, $5.expr, $5.token); } + | slot_acc assign_op_struct + { $$ = SlotAssignOpNode::alloc( $1.lineNumber, $1.object, $1.slotName, $1.array, $2.token, $2.expr); } + | slot_acc '=' expr + { $$ = SlotAssignNode::alloc( $1.lineNumber, $1.object, $1.array, $1.slotName, $3); } + | slot_acc '=' '{' expr_list '}' + { $$ = SlotAssignNode::alloc( $1.lineNumber, $1.object, $1.array, $1.slotName, $4); } + ; + +funcall_expr + : IDENT '(' expr_list_decl ')' + { $$ = FuncCallExprNode::alloc( $1.lineNumber, $1.value, NULL, $3, false); } + | IDENT opCOLONCOLON IDENT '(' expr_list_decl ')' + { $$ = FuncCallExprNode::alloc( $1.lineNumber, $3.value, $1.value, $5, false); } + | expr '.' IDENT '(' expr_list_decl ')' + { $1->append($5); $$ = FuncCallExprNode::alloc( $1->dbgLineNumber, $3.value, NULL, $1, true); } + ; + +assert_expr + : rwASSERT '(' expr ')' + { $$ = AssertCallExprNode::alloc( $1.lineNumber, $3, NULL ); } + | rwASSERT '(' expr ',' STRATOM ')' + { $$ = AssertCallExprNode::alloc( $1.lineNumber, $3, $5.value ); } + ; + +expr_list_decl + : + { $$ = NULL; } + | expr_list + { $$ = $1; } + ; + +expr_list + : expr + { $$ = $1; } + | expr_list ',' expr + { ($1)->append($3); $$ = $1; } + ; + +slot_assign_list_opt + : + { $$ = NULL; } + | slot_assign_list + { $$ = $1; } + ; + +slot_assign_list + : slot_assign + { $$ = $1; } + | slot_assign_list slot_assign + { $1->append($2); $$ = $1; } + ; + +slot_assign + : IDENT '=' expr ';' + { $$ = SlotAssignNode::alloc( $1.lineNumber, NULL, NULL, $1.value, $3); } + | TYPEIDENT IDENT '=' expr ';' + { $$ = SlotAssignNode::alloc( $1.lineNumber, NULL, NULL, $2.value, $4, $1.value); } + | rwDATABLOCK '=' expr ';' + { $$ = SlotAssignNode::alloc( $1.lineNumber, NULL, NULL, StringTable->insert("datablock"), $3); } + | IDENT '[' aidx_expr ']' '=' expr ';' + { $$ = SlotAssignNode::alloc( $1.lineNumber, NULL, $3, $1.value, $6); } + | TYPEIDENT IDENT '[' aidx_expr ']' '=' expr ';' + { $$ = SlotAssignNode::alloc( $1.lineNumber, NULL, $4, $2.value, $7, $1.value); } + ; + +aidx_expr + : expr + { $$ = $1; } + | aidx_expr ',' expr + { $$ = CommaCatExprNode::alloc( $1->dbgLineNumber, $1, $3); } + ; +%% + diff --git a/Engine/source/console/CMDscan.cpp b/Engine/source/console/CMDscan.cpp new file mode 100644 index 000000000..4ce606638 --- /dev/null +++ b/Engine/source/console/CMDscan.cpp @@ -0,0 +1,2735 @@ +#line 2 "CMDscan.cpp" + +#line 4 "CMDscan.cpp" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 33 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . Non-C99 systems may or may not. */ + +#if __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; +#endif /* ! C99 */ + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +#if __STDC__ + +#define YY_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN (yy_start) = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START (((yy_start) - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE CMDrestart(CMDin ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +extern int CMDleng; + +extern FILE *CMDin, *CMDout; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up CMDtext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = (yy_hold_char); \ + YY_RESTORE_YY_MORE_OFFSET \ + (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up CMDtext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, (yytext_ptr) ) + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef unsigned int yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via CMDrestart()), so that the user can continue scanning by + * just pointing CMDin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* Stack of input buffers. */ +static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ +static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ +static YY_BUFFER_STATE * yy_buffer_stack = 0; /**< Stack as an array. */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ + ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ + : NULL) + +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] + +/* yy_hold_char holds the character lost when CMDtext is formed. */ +static char yy_hold_char; +static int yy_n_chars; /* number of characters read into yy_ch_buf */ +int CMDleng; + +/* Points to current character in buffer. */ +static char *yy_c_buf_p = (char *) 0; +static int yy_init = 0; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* Flag which is used to allow CMDwrap()'s to do buffer switches + * instead of setting up a fresh CMDin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +void CMDrestart (FILE *input_file ); +void CMD_switch_to_buffer (YY_BUFFER_STATE new_buffer ); +YY_BUFFER_STATE CMD_create_buffer (FILE *file,int size ); +void CMD_delete_buffer (YY_BUFFER_STATE b ); +void CMD_flush_buffer (YY_BUFFER_STATE b ); +void CMDpush_buffer_state (YY_BUFFER_STATE new_buffer ); +void CMDpop_buffer_state (void ); + +static void CMDensure_buffer_stack (void ); +static void CMD_load_buffer_state (void ); +static void CMD_init_buffer (YY_BUFFER_STATE b,FILE *file ); + +#define YY_FLUSH_BUFFER CMD_flush_buffer(YY_CURRENT_BUFFER ) + +YY_BUFFER_STATE CMD_scan_buffer (char *base,yy_size_t size ); +YY_BUFFER_STATE CMD_scan_string (yyconst char *yy_str ); +YY_BUFFER_STATE CMD_scan_bytes (yyconst char *bytes,int len ); + +void *CMDalloc (yy_size_t ); +void *CMDrealloc (void *,yy_size_t ); +void CMDfree (void * ); + +#define yy_new_buffer CMD_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + CMDensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + CMD_create_buffer(CMDin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + CMDensure_buffer_stack (); \ + YY_CURRENT_BUFFER_LVALUE = \ + CMD_create_buffer(CMDin,YY_BUF_SIZE ); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +typedef unsigned char YY_CHAR; + +FILE *CMDin = (FILE *) 0, *CMDout = (FILE *) 0; + +typedef int yy_state_type; + +extern int CMDlineno; + +int CMDlineno = 1; + +extern char *CMDtext; +#define yytext_ptr CMDtext + +static yy_state_type yy_get_previous_state (void ); +static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); +static int yy_get_next_buffer (void ); +static void yy_fatal_error (yyconst char msg[] ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up CMDtext. + */ +#define YY_DO_BEFORE_ACTION \ + (yytext_ptr) = yy_bp; \ + CMDleng = (size_t) (yy_cp - yy_bp); \ + (yy_hold_char) = *yy_cp; \ + *yy_cp = '\0'; \ + (yy_c_buf_p) = yy_cp; + +#define YY_NUM_RULES 94 +#define YY_END_OF_BUFFER 95 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static yyconst flex_int16_t yy_accept[233] = + { 0, + 0, 0, 95, 93, 1, 5, 4, 51, 93, 93, + 58, 57, 93, 41, 42, 45, 43, 56, 44, 50, + 46, 90, 90, 52, 53, 47, 61, 48, 38, 36, + 88, 88, 88, 88, 39, 40, 59, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 54, 49, 55, 60, 1, 0, 9, 0, 6, + 0, 0, 17, 87, 25, 12, 26, 0, 7, 0, + 23, 16, 21, 15, 22, 31, 91, 37, 3, 24, + 0, 90, 0, 0, 14, 19, 11, 8, 10, 20, + 88, 33, 88, 88, 27, 88, 88, 88, 88, 88, + + 88, 69, 88, 88, 88, 88, 70, 62, 88, 88, + 63, 88, 88, 88, 88, 88, 88, 28, 13, 18, + 92, 87, 0, 32, 3, 3, 91, 0, 91, 89, + 29, 30, 35, 34, 88, 88, 88, 88, 88, 88, + 88, 88, 73, 88, 88, 76, 88, 88, 88, 88, + 88, 88, 92, 0, 2, 2, 88, 88, 79, 88, + 88, 88, 66, 88, 88, 88, 88, 88, 88, 88, + 88, 85, 88, 2, 2, 2, 2, 2, 88, 64, + 88, 88, 88, 86, 88, 88, 88, 88, 88, 88, + 88, 68, 0, 2, 2, 67, 88, 88, 88, 88, + + 88, 88, 88, 65, 88, 81, 0, 2, 2, 88, + 88, 82, 72, 88, 88, 83, 88, 80, 0, 2, + 2, 2, 74, 88, 71, 75, 88, 88, 78, 84, + 77, 0 + } ; + +static yyconst flex_int32_t yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 2, 2, 4, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 5, 6, 1, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 31, + 33, 33, 33, 33, 33, 34, 33, 35, 33, 36, + 33, 33, 37, 38, 33, 33, 33, 39, 33, 33, + 40, 41, 42, 43, 33, 1, 44, 45, 46, 47, + + 48, 49, 50, 51, 52, 33, 53, 54, 55, 56, + 57, 58, 33, 59, 60, 61, 62, 33, 63, 39, + 33, 33, 64, 65, 66, 67, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst flex_int32_t yy_meta[68] = + { 0, + 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3, 4, 4, + 5, 1, 1, 6, 1, 1, 1, 4, 4, 4, + 4, 4, 7, 7, 7, 7, 7, 7, 7, 1, + 1, 1, 1, 4, 4, 4, 4, 4, 4, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 1, 1, 1, 1 + } ; + +static yyconst flex_int16_t yy_base[248] = + { 0, + 0, 0, 380, 381, 377, 381, 381, 61, 63, 51, + 53, 65, 66, 381, 381, 354, 64, 381, 66, 60, + 68, 76, 80, 356, 381, 60, 352, 77, 381, 381, + 0, 341, 338, 345, 381, 381, 348, 311, 311, 54, + 61, 315, 59, 38, 62, 309, 323, 318, 62, 306, + 313, 381, 89, 381, 381, 361, 338, 381, 111, 381, + 357, 100, 381, 339, 381, 381, 381, 112, 381, 355, + 381, 381, 381, 333, 381, 381, 107, 381, 339, 381, + 110, 114, 121, 0, 381, 332, 381, 381, 381, 331, + 0, 0, 324, 324, 381, 292, 303, 290, 293, 287, + + 298, 0, 286, 291, 285, 287, 0, 0, 287, 278, + 0, 294, 278, 282, 285, 274, 283, 381, 381, 381, + 313, 312, 311, 381, 0, 139, 119, 125, 128, 0, + 381, 381, 0, 0, 283, 286, 281, 267, 283, 282, + 277, 264, 275, 276, 273, 0, 267, 257, 268, 256, + 268, 261, 293, 292, 146, 152, 253, 258, 0, 258, + 264, 246, 0, 259, 262, 244, 244, 259, 243, 247, + 254, 0, 251, 155, 157, 162, 165, 168, 237, 0, + 241, 242, 241, 0, 248, 241, 230, 199, 185, 190, + 183, 0, 215, 173, 175, 0, 168, 172, 161, 167, + + 154, 164, 159, 0, 145, 198, 185, 178, 181, 153, + 154, 0, 191, 141, 146, 0, 117, 381, 0, 184, + 186, 191, 0, 110, 381, 0, 88, 76, 0, 0, + 0, 381, 209, 213, 220, 224, 228, 232, 239, 119, + 243, 250, 257, 264, 271, 278, 285 + } ; + +static yyconst flex_int16_t yy_def[248] = + { 0, + 232, 1, 232, 232, 232, 232, 232, 232, 233, 234, + 234, 232, 235, 232, 232, 232, 232, 232, 232, 232, + 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, + 236, 236, 236, 236, 232, 232, 232, 236, 236, 236, + 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, + 236, 232, 232, 232, 232, 232, 232, 232, 233, 232, + 233, 237, 232, 238, 232, 232, 232, 235, 232, 235, + 232, 232, 232, 232, 232, 232, 232, 232, 239, 232, + 232, 232, 232, 240, 232, 232, 232, 232, 232, 232, + 236, 236, 236, 236, 232, 236, 236, 236, 236, 236, + + 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, + 236, 236, 236, 236, 236, 236, 236, 232, 232, 232, + 241, 238, 238, 232, 239, 242, 232, 232, 232, 240, + 232, 232, 236, 236, 236, 236, 236, 236, 236, 236, + 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, + 236, 236, 241, 241, 243, 244, 236, 236, 236, 236, + 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, + 236, 236, 236, 243, 232, 243, 244, 244, 236, 236, + 236, 236, 236, 236, 236, 236, 236, 236, 236, 236, + 236, 236, 232, 243, 244, 236, 236, 236, 236, 236, + + 236, 236, 236, 236, 236, 236, 232, 245, 246, 236, + 236, 236, 236, 236, 236, 236, 236, 232, 247, 243, + 244, 244, 236, 236, 232, 236, 236, 236, 236, 236, + 236, 0, 232, 232, 232, 232, 232, 232, 232, 232, + 232, 232, 232, 232, 232, 232, 232 + } ; + +static yyconst flex_int16_t yy_nxt[449] = + { 0, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 31, 31, + 31, 31, 31, 31, 32, 31, 33, 34, 31, 35, + 4, 36, 37, 38, 39, 40, 41, 42, 43, 31, + 31, 44, 31, 31, 31, 45, 46, 47, 48, 49, + 50, 31, 51, 52, 53, 54, 55, 57, 60, 62, + 62, 62, 62, 66, 63, 69, 65, 72, 77, 77, + 78, 74, 86, 87, 58, 79, 107, 73, 67, 75, + 76, 80, 81, 108, 82, 82, 81, 98, 82, 82, + + 89, 90, 104, 61, 100, 109, 70, 83, 101, 110, + 99, 83, 118, 114, 84, 105, 60, 102, 62, 62, + 106, 69, 130, 83, 115, 77, 77, 83, 127, 127, + 81, 231, 82, 82, 128, 230, 128, 127, 127, 129, + 129, 156, 156, 129, 129, 83, 129, 129, 175, 175, + 83, 61, 70, 119, 175, 175, 125, 175, 175, 175, + 175, 83, 229, 176, 175, 175, 83, 175, 175, 178, + 175, 175, 176, 228, 193, 175, 175, 175, 175, 194, + 221, 221, 178, 221, 221, 195, 175, 175, 175, 175, + 208, 227, 209, 175, 175, 208, 226, 225, 209, 224, + + 223, 176, 219, 178, 218, 217, 216, 215, 178, 59, + 214, 59, 59, 59, 59, 59, 64, 213, 64, 64, + 68, 212, 68, 68, 68, 68, 68, 91, 211, 210, + 91, 121, 207, 206, 121, 122, 122, 205, 122, 125, + 204, 125, 125, 125, 125, 125, 153, 153, 203, 153, + 155, 155, 155, 155, 155, 155, 155, 174, 174, 174, + 174, 174, 174, 174, 177, 177, 177, 177, 177, 177, + 177, 220, 220, 220, 220, 220, 220, 220, 222, 222, + 222, 222, 222, 222, 222, 156, 156, 202, 156, 156, + 156, 156, 201, 200, 199, 198, 197, 196, 192, 191, + + 190, 189, 188, 187, 186, 185, 184, 183, 182, 181, + 180, 179, 154, 154, 173, 172, 171, 170, 169, 168, + 167, 166, 165, 164, 163, 162, 161, 160, 159, 158, + 157, 123, 123, 154, 152, 151, 150, 149, 148, 147, + 146, 145, 144, 143, 142, 141, 140, 139, 138, 137, + 136, 135, 134, 133, 132, 131, 126, 124, 68, 123, + 59, 120, 56, 117, 116, 113, 112, 111, 103, 97, + 96, 95, 94, 93, 92, 88, 85, 71, 56, 232, + 3, 232, 232, 232, 232, 232, 232, 232, 232, 232, + 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, + + 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, + 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, + 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, + 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, + 232, 232, 232, 232, 232, 232, 232, 232 + } ; + +static yyconst flex_int16_t yy_chk[449] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 8, 9, 10, + 10, 11, 11, 12, 10, 13, 11, 17, 20, 20, + 21, 19, 26, 26, 8, 21, 44, 17, 12, 19, + 19, 21, 22, 44, 22, 22, 23, 40, 23, 23, + + 28, 28, 43, 9, 41, 45, 13, 22, 41, 45, + 40, 23, 53, 49, 22, 43, 59, 41, 62, 62, + 43, 68, 240, 22, 49, 77, 77, 23, 81, 81, + 82, 228, 82, 82, 83, 227, 83, 127, 127, 83, + 83, 126, 126, 128, 128, 82, 129, 129, 155, 155, + 127, 59, 68, 53, 156, 156, 126, 174, 174, 175, + 175, 82, 224, 155, 176, 176, 127, 177, 177, 156, + 178, 178, 174, 217, 175, 194, 194, 195, 195, 176, + 208, 208, 177, 209, 209, 178, 220, 220, 221, 221, + 194, 215, 195, 222, 222, 208, 214, 213, 209, 211, + + 210, 220, 207, 221, 206, 205, 203, 202, 222, 233, + 201, 233, 233, 233, 233, 233, 234, 200, 234, 234, + 235, 199, 235, 235, 235, 235, 235, 236, 198, 197, + 236, 237, 193, 191, 237, 238, 238, 190, 238, 239, + 189, 239, 239, 239, 239, 239, 241, 241, 188, 241, + 242, 242, 242, 242, 242, 242, 242, 243, 243, 243, + 243, 243, 243, 243, 244, 244, 244, 244, 244, 244, + 244, 245, 245, 245, 245, 245, 245, 245, 246, 246, + 246, 246, 246, 246, 246, 247, 247, 187, 247, 247, + 247, 247, 186, 185, 183, 182, 181, 179, 173, 171, + + 170, 169, 168, 167, 166, 165, 164, 162, 161, 160, + 158, 157, 154, 153, 152, 151, 150, 149, 148, 147, + 145, 144, 143, 142, 141, 140, 139, 138, 137, 136, + 135, 123, 122, 121, 117, 116, 115, 114, 113, 112, + 110, 109, 106, 105, 104, 103, 101, 100, 99, 98, + 97, 96, 94, 93, 90, 86, 79, 74, 70, 64, + 61, 57, 56, 51, 50, 48, 47, 46, 42, 39, + 38, 37, 34, 33, 32, 27, 24, 16, 5, 3, + 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, + 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, + + 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, + 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, + 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, + 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, + 232, 232, 232, 232, 232, 232, 232, 232 + } ; + +static yy_state_type yy_last_accepting_state; +static char *yy_last_accepting_cpos; + +extern int CMD_flex_debug; +int CMD_flex_debug = 0; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +char *CMDtext; +#line 1 "CMDscan.l" +#line 2 "CMDscan.l" + +// flex --nounput -o CMDscan.cpp -P CMD CMDscan.l + +#define YYLMAX 4096 +#define YY_NO_UNISTD_H + +#include +#include "platform/platform.h" +#include "core/stringTable.h" +#include "console/console.h" +#include "console/compiler.h" +#include "console/dynamicTypes.h" +#include "core/strings/stringFunctions.h" + +template< typename T > +struct Token +{ + T value; + S32 lineNumber; +}; + +// Can't have ctors in structs used in unions, so we have this. +template< typename T > +inline Token< T > MakeToken( T value, U32 lineNumber ) +{ + Token< T > result; + result.value = value; + result.lineNumber = lineNumber; + return result; +} + +#include "console/cmdgram.h" + +using namespace Compiler; + +#define YY_NEVER_INTERACTIVE 1 + +// Some basic parsing primitives... +static int Sc_ScanDocBlock(); +static int Sc_ScanString(int ret); +static int Sc_ScanNum(); +static int Sc_ScanVar(); +static int Sc_ScanHex(); +static int Sc_ScanIdent(); + +// Deal with debuggability of FLEX. +#ifdef TORQUE_DEBUG +#define FLEX_DEBUG 1 +#else +#define FLEX_DEBUG 0 +#endif + +// Install our own input code... +#undef CMDgetc +int CMDgetc(); + +// Hack to make windows lex happy. +#ifndef isatty +inline int isatty(int) { return 0; } +#endif + +// Wrap our getc, so that lex doesn't try to do its own buffering/file IO. +#define YY_INPUT(buf,result,max_size) \ + { \ + int c = '*', n; \ + for ( n = 0; n < max_size && \ + (c = CMDgetc()) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + result = n; \ + } + +// General helper stuff. +static int lineIndex; + +// File state +void CMDSetScanBuffer(const char *sb, const char *fn); +const char * CMDgetFileLine(int &lineNumber); + +// Error reporting +void CMDerror(char * s, ...); + +// Reset the parser. +void CMDrestart(FILE *in); + +#line 720 "CMDscan.cpp" + +#define INITIAL 0 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +static int yy_init_globals (void ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int CMDwrap (void ); +#else +extern int CMDwrap (void ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ); +#endif + +#ifndef YY_NO_INPUT + +#ifdef __cplusplus +static int yyinput (void ); +#else +static int input (void ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO (void) fwrite( CMDtext, CMDleng, 1, CMDout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + size_t n; \ + for ( n = 0; n < max_size && \ + (c = getc( CMDin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( CMDin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = fread(buf, 1, max_size, CMDin))==0 && ferror(CMDin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(CMDin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int CMDlex (void); + +#define YY_DECL int CMDlex (void) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after CMDtext and CMDleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + +#line 105 "CMDscan.l" + + ; +#line 874 "CMDscan.cpp" + + if ( !(yy_init) ) + { + (yy_init) = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! (yy_start) ) + (yy_start) = 1; /* first start state */ + + if ( ! CMDin ) + CMDin = stdin; + + if ( ! CMDout ) + CMDout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + CMDensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + CMD_create_buffer(CMDin,YY_BUF_SIZE ); + } + + CMD_load_buffer_state( ); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = (yy_c_buf_p); + + /* Support of CMDtext. */ + *yy_cp = (yy_hold_char); + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = (yy_start); +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 233 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 381 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = (yy_hold_char); + yy_cp = (yy_last_accepting_cpos); + yy_current_state = (yy_last_accepting_state); + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 107 "CMDscan.l" +{ } + YY_BREAK +case 2: +/* rule 2 can match eol */ +YY_RULE_SETUP +#line 108 "CMDscan.l" +{ return(Sc_ScanDocBlock()); } + YY_BREAK +case 3: +YY_RULE_SETUP +#line 109 "CMDscan.l" +; + YY_BREAK +case 4: +YY_RULE_SETUP +#line 110 "CMDscan.l" +; + YY_BREAK +case 5: +/* rule 5 can match eol */ +YY_RULE_SETUP +#line 111 "CMDscan.l" +{lineIndex++;} + YY_BREAK +case 6: +YY_RULE_SETUP +#line 112 "CMDscan.l" +{ return(Sc_ScanString(STRATOM)); } + YY_BREAK +case 7: +YY_RULE_SETUP +#line 113 "CMDscan.l" +{ return(Sc_ScanString(TAGATOM)); } + YY_BREAK +case 8: +YY_RULE_SETUP +#line 114 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( opEQ, lineIndex ); return opEQ; } + YY_BREAK +case 9: +YY_RULE_SETUP +#line 115 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( opNE, lineIndex ); return opNE; } + YY_BREAK +case 10: +YY_RULE_SETUP +#line 116 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( opGE, lineIndex ); return opGE; } + YY_BREAK +case 11: +YY_RULE_SETUP +#line 117 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( opLE, lineIndex ); return opLE; } + YY_BREAK +case 12: +YY_RULE_SETUP +#line 118 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( opAND, lineIndex ); return opAND; } + YY_BREAK +case 13: +YY_RULE_SETUP +#line 119 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( opOR, lineIndex ); return opOR; } + YY_BREAK +case 14: +YY_RULE_SETUP +#line 120 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( opCOLONCOLON, lineIndex ); return opCOLONCOLON; } + YY_BREAK +case 15: +YY_RULE_SETUP +#line 121 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( opMINUSMINUS, lineIndex ); return opMINUSMINUS; } + YY_BREAK +case 16: +YY_RULE_SETUP +#line 122 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( opPLUSPLUS, lineIndex ); return opPLUSPLUS; } + YY_BREAK +case 17: +YY_RULE_SETUP +#line 123 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( opSTREQ, lineIndex ); return opSTREQ; } + YY_BREAK +case 18: +YY_RULE_SETUP +#line 124 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( opSTRNE, lineIndex ); return opSTRNE; } + YY_BREAK +case 19: +YY_RULE_SETUP +#line 125 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( opSHL, lineIndex ); return opSHL; } + YY_BREAK +case 20: +YY_RULE_SETUP +#line 126 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( opSHR, lineIndex ); return opSHR; } + YY_BREAK +case 21: +YY_RULE_SETUP +#line 127 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( opPLASN, lineIndex ); return opPLASN; } + YY_BREAK +case 22: +YY_RULE_SETUP +#line 128 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( opMIASN, lineIndex ); return opMIASN; } + YY_BREAK +case 23: +YY_RULE_SETUP +#line 129 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( opMLASN, lineIndex ); return opMLASN; } + YY_BREAK +case 24: +YY_RULE_SETUP +#line 130 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( opDVASN, lineIndex ); return opDVASN; } + YY_BREAK +case 25: +YY_RULE_SETUP +#line 131 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( opMODASN, lineIndex ); return opMODASN; } + YY_BREAK +case 26: +YY_RULE_SETUP +#line 132 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( opANDASN, lineIndex ); return opANDASN; } + YY_BREAK +case 27: +YY_RULE_SETUP +#line 133 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( opXORASN, lineIndex ); return opXORASN; } + YY_BREAK +case 28: +YY_RULE_SETUP +#line 134 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( opORASN, lineIndex ); return opORASN; } + YY_BREAK +case 29: +YY_RULE_SETUP +#line 135 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( opSLASN, lineIndex ); return opSLASN; } + YY_BREAK +case 30: +YY_RULE_SETUP +#line 136 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( opSRASN, lineIndex ); return opSRASN; } + YY_BREAK +case 31: +YY_RULE_SETUP +#line 137 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( opINTNAME, lineIndex ); return opINTNAME; } + YY_BREAK +case 32: +YY_RULE_SETUP +#line 138 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( opINTNAMER, lineIndex ); return opINTNAMER; } + YY_BREAK +case 33: +YY_RULE_SETUP +#line 139 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( '\n', lineIndex ); return '@'; } + YY_BREAK +case 34: +YY_RULE_SETUP +#line 140 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( '\t', lineIndex ); return '@'; } + YY_BREAK +case 35: +YY_RULE_SETUP +#line 141 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( ' ', lineIndex ); return '@'; } + YY_BREAK +case 36: +YY_RULE_SETUP +#line 142 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( 0, lineIndex ); return '@'; } + YY_BREAK +case 37: +YY_RULE_SETUP +#line 143 "CMDscan.l" +{ /* this comment stops syntax highlighting from getting messed up when editing the lexer in TextPad */ + register int c = 0, l; + for ( ; ; ) + { + l = c; + c = yyinput(); + + // Is this an open comment? + if ( c == EOF ) + { + CMDerror( "unexpected end of file found in comment" ); + break; + } + + // Increment line numbers. + else if ( c == '\n' ) + lineIndex++; + + // Did we find the end of the comment? + else if ( l == '*' && c == '/' ) + break; + } + } + YY_BREAK +case 38: +#line 167 "CMDscan.l" +case 39: +#line 168 "CMDscan.l" +case 40: +#line 169 "CMDscan.l" +case 41: +#line 170 "CMDscan.l" +case 42: +#line 171 "CMDscan.l" +case 43: +#line 172 "CMDscan.l" +case 44: +#line 173 "CMDscan.l" +case 45: +#line 174 "CMDscan.l" +case 46: +#line 175 "CMDscan.l" +case 47: +#line 176 "CMDscan.l" +case 48: +#line 177 "CMDscan.l" +case 49: +#line 178 "CMDscan.l" +case 50: +#line 179 "CMDscan.l" +case 51: +#line 180 "CMDscan.l" +case 52: +#line 181 "CMDscan.l" +case 53: +#line 182 "CMDscan.l" +case 54: +#line 183 "CMDscan.l" +case 55: +#line 184 "CMDscan.l" +case 56: +#line 185 "CMDscan.l" +case 57: +#line 186 "CMDscan.l" +case 58: +#line 187 "CMDscan.l" +case 59: +#line 188 "CMDscan.l" +case 60: +#line 189 "CMDscan.l" +case 61: +YY_RULE_SETUP +#line 189 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( CMDtext[ 0 ], lineIndex ); return CMDtext[ 0 ]; } + YY_BREAK +case 62: +YY_RULE_SETUP +#line 190 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( rwIN, lineIndex ); return(rwIN); } + YY_BREAK +case 63: +YY_RULE_SETUP +#line 191 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( rwCASEOR, lineIndex ); return(rwCASEOR); } + YY_BREAK +case 64: +YY_RULE_SETUP +#line 192 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( rwBREAK, lineIndex ); return(rwBREAK); } + YY_BREAK +case 65: +YY_RULE_SETUP +#line 193 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( rwRETURN, lineIndex ); return(rwRETURN); } + YY_BREAK +case 66: +YY_RULE_SETUP +#line 194 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( rwELSE, lineIndex ); return(rwELSE); } + YY_BREAK +case 67: +YY_RULE_SETUP +#line 195 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( rwASSERT, lineIndex ); return(rwASSERT); } + YY_BREAK +case 68: +YY_RULE_SETUP +#line 196 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( rwWHILE, lineIndex ); return(rwWHILE); } + YY_BREAK +case 69: +YY_RULE_SETUP +#line 197 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( rwDO, lineIndex ); return(rwDO); } + YY_BREAK +case 70: +YY_RULE_SETUP +#line 198 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( rwIF, lineIndex ); return(rwIF); } + YY_BREAK +case 71: +YY_RULE_SETUP +#line 199 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( rwFOREACHSTR, lineIndex ); return(rwFOREACHSTR); } + YY_BREAK +case 72: +YY_RULE_SETUP +#line 200 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( rwFOREACH, lineIndex ); return(rwFOREACH); } + YY_BREAK +case 73: +YY_RULE_SETUP +#line 201 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( rwFOR, lineIndex ); return(rwFOR); } + YY_BREAK +case 74: +YY_RULE_SETUP +#line 202 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( rwCONTINUE, lineIndex ); return(rwCONTINUE); } + YY_BREAK +case 75: +YY_RULE_SETUP +#line 203 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( rwDEFINE, lineIndex ); return(rwDEFINE); } + YY_BREAK +case 76: +YY_RULE_SETUP +#line 204 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( rwDECLARE, lineIndex ); return(rwDECLARE); } + YY_BREAK +case 77: +YY_RULE_SETUP +#line 205 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( rwDECLARESINGLETON, lineIndex ); return(rwDECLARESINGLETON); } + YY_BREAK +case 78: +YY_RULE_SETUP +#line 206 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( rwDATABLOCK, lineIndex ); return(rwDATABLOCK); } + YY_BREAK +case 79: +YY_RULE_SETUP +#line 207 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( rwCASE, lineIndex ); return(rwCASE); } + YY_BREAK +case 80: +YY_RULE_SETUP +#line 208 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( rwSWITCHSTR, lineIndex ); return(rwSWITCHSTR); } + YY_BREAK +case 81: +YY_RULE_SETUP +#line 209 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( rwSWITCH, lineIndex ); return(rwSWITCH); } + YY_BREAK +case 82: +YY_RULE_SETUP +#line 210 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( rwDEFAULT, lineIndex ); return(rwDEFAULT); } + YY_BREAK +case 83: +YY_RULE_SETUP +#line 211 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( rwPACKAGE, lineIndex ); return(rwPACKAGE); } + YY_BREAK +case 84: +YY_RULE_SETUP +#line 212 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( rwNAMESPACE, lineIndex ); return(rwNAMESPACE); } + YY_BREAK +case 85: +YY_RULE_SETUP +#line 213 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( 1, lineIndex ); return INTCONST; } + YY_BREAK +case 86: +YY_RULE_SETUP +#line 214 "CMDscan.l" +{ CMDlval.i = MakeToken< int >( 0, lineIndex ); return INTCONST; } + YY_BREAK +case 87: +YY_RULE_SETUP +#line 215 "CMDscan.l" +{ return(Sc_ScanVar()); } + YY_BREAK +case 88: +YY_RULE_SETUP +#line 216 "CMDscan.l" +{ return Sc_ScanIdent(); } + YY_BREAK +case 89: +YY_RULE_SETUP +#line 217 "CMDscan.l" +return(Sc_ScanHex()); + YY_BREAK +case 90: +YY_RULE_SETUP +#line 218 "CMDscan.l" +{ CMDtext[CMDleng] = 0; CMDlval.i = MakeToken< int >( dAtoi(CMDtext), lineIndex ); return INTCONST; } + YY_BREAK +case 91: +YY_RULE_SETUP +#line 219 "CMDscan.l" +return Sc_ScanNum(); + YY_BREAK +case 92: +YY_RULE_SETUP +#line 220 "CMDscan.l" +return(ILLEGAL_TOKEN); + YY_BREAK +case 93: +YY_RULE_SETUP +#line 221 "CMDscan.l" +return(ILLEGAL_TOKEN); + YY_BREAK +case 94: +YY_RULE_SETUP +#line 222 "CMDscan.l" +ECHO; + YY_BREAK +#line 1382 "CMDscan.cpp" +case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = (yy_hold_char); + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed CMDin at a new source and called + * CMDlex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = CMDin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++(yy_c_buf_p); + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = (yy_c_buf_p); + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_END_OF_FILE: + { + (yy_did_buffer_switch_on_eof) = 0; + + if ( CMDwrap( ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * CMDtext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = + (yytext_ptr) + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + (yy_c_buf_p) = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; + + yy_current_state = yy_get_previous_state( ); + + yy_cp = (yy_c_buf_p); + yy_bp = (yytext_ptr) + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +} /* end of CMDlex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (void) +{ + register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + register char *source = (yytext_ptr); + register int number_to_move, i; + int ret_val; + + if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr)) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + + int yy_c_buf_p_offset = + (int) ((yy_c_buf_p) - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + CMDrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + (yy_n_chars), num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + if ( (yy_n_chars) == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + CMDrestart(CMDin ); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + (yy_n_chars) += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; + + (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (void) +{ + register yy_state_type yy_current_state; + register char *yy_cp; + + yy_current_state = (yy_start); + + for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 233 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) +{ + register int yy_is_jam; + register char *yy_cp = (yy_c_buf_p); + + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + (yy_last_accepting_state) = yy_current_state; + (yy_last_accepting_cpos) = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 233 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 232); + + return yy_is_jam ? 0 : yy_current_state; +} + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (void) +#else + static int input (void) +#endif + +{ + int c; + + *(yy_c_buf_p) = (yy_hold_char); + + if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) + /* This was really a NUL. */ + *(yy_c_buf_p) = '\0'; + + else + { /* need more input */ + int offset = (yy_c_buf_p) - (yytext_ptr); + ++(yy_c_buf_p); + + switch ( yy_get_next_buffer( ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + CMDrestart(CMDin ); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( CMDwrap( ) ) + return 0; + + if ( ! (yy_did_buffer_switch_on_eof) ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(); +#else + return input(); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + (yy_c_buf_p) = (yytext_ptr) + offset; + break; + } + } + } + + c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ + *(yy_c_buf_p) = '\0'; /* preserve CMDtext */ + (yy_hold_char) = *++(yy_c_buf_p); + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * + * @note This function does not reset the start condition to @c INITIAL . + */ + void CMDrestart (FILE * input_file ) +{ + + if ( ! YY_CURRENT_BUFFER ){ + CMDensure_buffer_stack (); + YY_CURRENT_BUFFER_LVALUE = + CMD_create_buffer(CMDin,YY_BUF_SIZE ); + } + + CMD_init_buffer(YY_CURRENT_BUFFER,input_file ); + CMD_load_buffer_state( ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * + */ + void CMD_switch_to_buffer (YY_BUFFER_STATE new_buffer ) +{ + + /* TODO. We should be able to replace this entire function body + * with + * CMDpop_buffer_state(); + * CMDpush_buffer_state(new_buffer); + */ + CMDensure_buffer_stack (); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + CMD_load_buffer_state( ); + + /* We don't actually know whether we did this switch during + * EOF (CMDwrap()) processing, but the only time this flag + * is looked at is after CMDwrap() is called, so it's safe + * to go ahead and always set it. + */ + (yy_did_buffer_switch_on_eof) = 1; +} + +static void CMD_load_buffer_state (void) +{ + (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + CMDin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + (yy_hold_char) = *(yy_c_buf_p); +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * + * @return the allocated buffer state. + */ + YY_BUFFER_STATE CMD_create_buffer (FILE * file, int size ) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) CMDalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in CMD_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) CMDalloc(b->yy_buf_size + 2 ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in CMD_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + CMD_init_buffer(b,file ); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with CMD_create_buffer() + * + */ + void CMD_delete_buffer (YY_BUFFER_STATE b ) +{ + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + CMDfree((void *) b->yy_ch_buf ); + + CMDfree((void *) b ); +} + +#ifndef __cplusplus +extern int isatty (int ); +#endif /* __cplusplus */ + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a CMDrestart() or at EOF. + */ + static void CMD_init_buffer (YY_BUFFER_STATE b, FILE * file ) + +{ + int oerrno = errno; + + CMD_flush_buffer(b ); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then CMD_init_buffer was _probably_ + * called from CMDrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * + */ + void CMD_flush_buffer (YY_BUFFER_STATE b ) +{ + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + CMD_load_buffer_state( ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * + */ +void CMDpush_buffer_state (YY_BUFFER_STATE new_buffer ) +{ + if (new_buffer == NULL) + return; + + CMDensure_buffer_stack(); + + /* This block is copied from CMD_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *(yy_c_buf_p) = (yy_hold_char); + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + (yy_buffer_stack_top)++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from CMD_switch_to_buffer. */ + CMD_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * + */ +void CMDpop_buffer_state (void) +{ + if (!YY_CURRENT_BUFFER) + return; + + CMD_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + if ((yy_buffer_stack_top) > 0) + --(yy_buffer_stack_top); + + if (YY_CURRENT_BUFFER) { + CMD_load_buffer_state( ); + (yy_did_buffer_switch_on_eof) = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void CMDensure_buffer_stack (void) +{ + int num_to_alloc; + + if (!(yy_buffer_stack)) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; + (yy_buffer_stack) = (struct yy_buffer_state**)CMDalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + ); + + memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + (yy_buffer_stack_max) = num_to_alloc; + (yy_buffer_stack_top) = 0; + return; + } + + if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + int grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = (yy_buffer_stack_max) + grow_size; + (yy_buffer_stack) = (struct yy_buffer_state**)CMDrealloc + ((yy_buffer_stack), + num_to_alloc * sizeof(struct yy_buffer_state*) + ); + + /* zero only the new slots.*/ + memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); + (yy_buffer_stack_max) = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE CMD_scan_buffer (char * base, yy_size_t size ) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) CMDalloc(sizeof( struct yy_buffer_state ) ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in CMD_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + CMD_switch_to_buffer(b ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to CMDlex() will + * scan from a @e copy of @a str. + * @param str a NUL-terminated string to scan + * + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * CMD_scan_bytes() instead. + */ +YY_BUFFER_STATE CMD_scan_string (yyconst char * yystr ) +{ + + return CMD_scan_bytes(yystr,strlen(yystr) ); +} + +/** Setup the input buffer state to scan the given bytes. The next call to CMDlex() will + * scan from a @e copy of @a bytes. + * @param bytes the byte buffer to scan + * @param len the number of bytes in the buffer pointed to by @a bytes. + * + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE CMD_scan_bytes (yyconst char * yybytes, int _yybytes_len ) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = _yybytes_len + 2; + buf = (char *) CMDalloc(n ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in CMD_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = CMD_scan_buffer(buf,n ); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in CMD_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yy_fatal_error (yyconst char* msg ) +{ + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up CMDtext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + CMDtext[CMDleng] = (yy_hold_char); \ + (yy_c_buf_p) = CMDtext + yyless_macro_arg; \ + (yy_hold_char) = *(yy_c_buf_p); \ + *(yy_c_buf_p) = '\0'; \ + CMDleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the current line number. + * + */ +int CMDget_lineno (void) +{ + + return CMDlineno; +} + +/** Get the input stream. + * + */ +FILE *CMDget_in (void) +{ + return CMDin; +} + +/** Get the output stream. + * + */ +FILE *CMDget_out (void) +{ + return CMDout; +} + +/** Get the length of the current token. + * + */ +int CMDget_leng (void) +{ + return CMDleng; +} + +/** Get the current token. + * + */ + +char *CMDget_text (void) +{ + return CMDtext; +} + +/** Set the current line number. + * @param line_number + * + */ +void CMDset_lineno (int line_number ) +{ + + CMDlineno = line_number; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param in_str A readable stream. + * + * @see CMD_switch_to_buffer + */ +void CMDset_in (FILE * in_str ) +{ + CMDin = in_str ; +} + +void CMDset_out (FILE * out_str ) +{ + CMDout = out_str ; +} + +int CMDget_debug (void) +{ + return CMD_flex_debug; +} + +void CMDset_debug (int bdebug ) +{ + CMD_flex_debug = bdebug ; +} + +static int yy_init_globals (void) +{ + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from CMDlex_destroy(), so don't allocate here. + */ + + (yy_buffer_stack) = 0; + (yy_buffer_stack_top) = 0; + (yy_buffer_stack_max) = 0; + (yy_c_buf_p) = (char *) 0; + (yy_init) = 0; + (yy_start) = 0; + +/* Defined in main.c */ +#ifdef YY_STDINIT + CMDin = stdin; + CMDout = stdout; +#else + CMDin = (FILE *) 0; + CMDout = (FILE *) 0; +#endif + + /* For future reference: Set errno on error, since we are called by + * CMDlex_init() + */ + return 0; +} + +/* CMDlex_destroy is for both reentrant and non-reentrant scanners. */ +int CMDlex_destroy (void) +{ + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + CMD_delete_buffer(YY_CURRENT_BUFFER ); + YY_CURRENT_BUFFER_LVALUE = NULL; + CMDpop_buffer_state(); + } + + /* Destroy the stack itself. */ + CMDfree((yy_buffer_stack) ); + (yy_buffer_stack) = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * CMDlex() is called, initialization will occur. */ + yy_init_globals( ); + + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) +{ + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * s ) +{ + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *CMDalloc (yy_size_t size ) +{ + return (void *) malloc( size ); +} + +void *CMDrealloc (void * ptr, yy_size_t size ) +{ + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); +} + +void CMDfree (void * ptr ) +{ + free( (char *) ptr ); /* see CMDrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 222 "CMDscan.l" + + + +static const char *scanBuffer; +static const char *fileName; +static int scanIndex; + +const char * CMDGetCurrentFile() +{ + return fileName; +} + +int CMDGetCurrentLine() +{ + return lineIndex; +} + +extern bool gConsoleSyntaxError; + +void CMDerror(char *format, ...) +{ + Compiler::gSyntaxError = true; + + const int BUFMAX = 1024; + char tempBuf[BUFMAX]; + va_list args; + va_start( args, format ); +#ifdef TORQUE_OS_WIN32 + _vsnprintf( tempBuf, BUFMAX, format, args ); +#else + vsnprintf( tempBuf, BUFMAX, format, args ); +#endif + + if(fileName) + { + Con::errorf(ConsoleLogEntry::Script, "%s Line: %d - %s", fileName, lineIndex, tempBuf); + +#ifndef NO_ADVANCED_ERROR_REPORT + // dhc - lineIndex is bogus. let's try to add some sanity back in. + int i,j,n; + char c; + int linediv = 1; + // first, walk the buffer, trying to detect line ending type. + // this is imperfect, if inconsistant line endings exist... + for (i=0; iBUFMAX>>2) break; // at least get a little data + n++; i++; + } + // find next lineending + while (n>1) // cap at half-buf-size forward + { + c = scanBuffer[scanIndex+j]; + if (c==0) break; + if ((c=='\r' || c=='\n') && j>BUFMAX>>2) break; // at least get a little data + n++; j++; + } + if (i) i--; // chop off extra linefeed. + if (j) j--; // chop off extra linefeed. + // build our little text block + if (i) dStrncpy(tempBuf,scanBuffer+scanIndex-i,i); + dStrncpy(tempBuf+i,"##", 2); // bracketing. + tempBuf[i+2] = scanBuffer[scanIndex]; // copy the halt character. + dStrncpy(tempBuf+i+3,"##", 2); // bracketing. + if (j) dStrncpy(tempBuf+i+5,scanBuffer+scanIndex+1,j); // +1 to go past current char. + tempBuf[i+j+5] = 0; // null terminate + for(n=0; n>> Advanced script error report. Line %d.", lineIndex); + Con::warnf(ConsoleLogEntry::Script, ">>> Some error context, with ## on sides of error halt:"); + Con::errorf(ConsoleLogEntry::Script, "%s", tempBuf); + Con::warnf(ConsoleLogEntry::Script, ">>> Error report complete.\n"); +#endif + + // Update the script-visible error buffer. + const char *prevStr = Con::getVariable("$ScriptError"); + if (prevStr[0]) + dSprintf(tempBuf, sizeof(tempBuf), "%s\n%s Line: %d - Syntax error.", prevStr, fileName, lineIndex); + else + dSprintf(tempBuf, sizeof(tempBuf), "%s Line: %d - Syntax error.", fileName, lineIndex); + Con::setVariable("$ScriptError", tempBuf); + + // We also need to mark that we came up with a new error. + static S32 sScriptErrorHash=1000; + Con::setIntVariable("$ScriptErrorHash", sScriptErrorHash++); + } + else + Con::errorf(ConsoleLogEntry::Script, tempBuf); +} + +void CMDSetScanBuffer(const char *sb, const char *fn) +{ + scanBuffer = sb; + fileName = fn; + scanIndex = 0; + lineIndex = 1; +} + +int CMDgetc() +{ + int ret = scanBuffer[scanIndex]; + if(ret) + scanIndex++; + else + ret = -1; + return ret; +} + +int CMDwrap() +{ + return 1; +} + +static int Sc_ScanVar() +{ + // Truncate the temp buffer... + CMDtext[CMDleng] = 0; + + // Make it a stringtable string! + CMDlval.s = MakeToken< StringTableEntry >( StringTable->insert(CMDtext), lineIndex ); + return(VAR); +} + +static int charConv(int in) +{ + switch(in) + { + case 'r': + return '\r'; + case 'n': + return '\n'; + case 't': + return '\t'; + default: + return in; + } +} + +static int getHexDigit(char c) +{ + if(c >= '0' && c <= '9') + return c - '0'; + if(c >= 'A' && c <= 'F') + return c - 'A' + 10; + if(c >= 'a' && c <= 'f') + return c - 'a' + 10; + return -1; +} + +static int Sc_ScanDocBlock() +{ + S32 len = dStrlen(CMDtext); + char* text = (char *) consoleAlloc(len + 1); + S32 line = lineIndex; + + for( S32 i = 0, j = 0; j <= len; j++ ) + { + if( ( j <= (len - 2) ) && ( CMDtext[j] == '/' ) && ( CMDtext[j + 1] == '/' ) && ( CMDtext[j + 2] == '/' ) ) + { + j += 2; + continue; + } + + if( CMDtext[j] == '\r' ) + continue; + + if( CMDtext[j] == '\n' ) + lineIndex++; + + text[i++] = CMDtext[j]; + } + + CMDlval.str = MakeToken< char* >( text, line ); + return(DOCBLOCK); +} + +static int Sc_ScanString(int ret) +{ + CMDtext[CMDleng - 1] = 0; + if(!collapseEscape(CMDtext+1)) + return -1; + + char* buffer = ( char* ) consoleAlloc( dStrlen( CMDtext ) ); + dStrcpy( buffer, CMDtext + 1 ); + + CMDlval.str = MakeToken< char* >( buffer, lineIndex ); + return ret; +} + +static int Sc_ScanIdent() +{ + ConsoleBaseType *type; + + CMDtext[CMDleng] = 0; + + if((type = ConsoleBaseType::getTypeByName(CMDtext)) != NULL) + { + /* It's a type */ + CMDlval.i = MakeToken< int >( type->getTypeID(), lineIndex ); + return TYPEIDENT; + } + + /* It's an identifier */ + CMDlval.s = MakeToken< StringTableEntry >( StringTable->insert(CMDtext), lineIndex ); + return IDENT; +} + +void expandEscape(char *dest, const char *src) +{ + U8 c; + while((c = (U8) *src++) != 0) + { + if(c == '\"') + { + *dest++ = '\\'; + *dest++ = '\"'; + } + else if(c == '\\') + { + *dest++ = '\\'; + *dest++ = '\\'; + } + else if(c == '\r') + { + *dest++ = '\\'; + *dest++ = 'r'; + } + else if(c == '\n') + { + *dest++ = '\\'; + *dest++ = 'n'; + } + else if(c == '\t') + { + *dest++ = '\\'; + *dest++ = 't'; + } + else if(c == '\'') + { + *dest++ = '\\'; + *dest++ = '\''; + } + else if((c >= 1 && c <= 7) || + (c >= 11 && c <= 12) || + (c >= 14 && c <= 15)) + { + /* Remap around: \b = 0x8, \t = 0x9, \n = 0xa, \r = 0xd */ + static U8 expandRemap[15] = { 0x0, + 0x0, + 0x1, + 0x2, + 0x3, + 0x4, + 0x5, + 0x6, + 0x0, + 0x0, + 0x0, + 0x7, + 0x8, + 0x0, + 0x9 }; + + *dest++ = '\\'; + *dest++ = 'c'; + if(c == 15) + *dest++ = 'r'; + else if(c == 16) + *dest++ = 'p'; + else if(c == 17) + *dest++ = 'o'; + else + *dest++ = expandRemap[c] + '0'; + } + else if(c < 32) + { + *dest++ = '\\'; + *dest++ = 'x'; + S32 dig1 = c >> 4; + S32 dig2 = c & 0xf; + if(dig1 < 10) + dig1 += '0'; + else + dig1 += 'A' - 10; + if(dig2 < 10) + dig2 += '0'; + else + dig2 += 'A' - 10; + *dest++ = dig1; + *dest++ = dig2; + } + else + *dest++ = c; + } + *dest = '\0'; +} + +bool collapseEscape(char *buf) +{ + S32 len = dStrlen(buf) + 1; + for(S32 i = 0; i < len;) + { + if(buf[i] == '\\') + { + if(buf[i+1] == 'x') + { + S32 dig1 = getHexDigit(buf[i+2]); + if(dig1 == -1) + return false; + + S32 dig2 = getHexDigit(buf[i+3]); + if(dig2 == -1) + return false; + buf[i] = dig1 * 16 + dig2; + dMemmove(buf + i + 1, buf + i + 4, len - i - 3); + len -= 3; + i++; + } + else if(buf[i+1] == 'c') + { + /* Remap around: \b = 0x8, \t = 0x9, \n = 0xa, \r = 0xd */ + static U8 collapseRemap[10] = { 0x1, + 0x2, + 0x3, + 0x4, + 0x5, + 0x6, + 0x7, + 0xb, + 0xc, + 0xe }; + + if(buf[i+2] == 'r') + buf[i] = 15; + else if(buf[i+2] == 'p') + buf[i] = 16; + else if(buf[i+2] == 'o') + buf[i] = 17; + else + { + int dig1 = buf[i+2] - '0'; + if(dig1 < 0 || dig1 > 9) + return false; + buf[i] = collapseRemap[dig1]; + } + // Make sure we don't put 0x1 at the beginning of the string. + if ((buf[i] == 0x1) && (i == 0)) + { + buf[i] = 0x2; + buf[i+1] = 0x1; + dMemmove(buf + i + 2, buf + i + 3, len - i - 1); + len -= 1; + } + else + { + dMemmove(buf + i + 1, buf + i + 3, len - i - 2); + len -= 2; + } + i++; + } + else + { + buf[i] = charConv(buf[i+1]); + dMemmove(buf + i + 1, buf + i + 2, len - i - 1); + len--; + i++; + } + } + else + i++; + } + return true; +} + +static int Sc_ScanNum() +{ + CMDtext[CMDleng] = 0; + CMDlval.f = MakeToken< double >( dAtof(CMDtext), lineIndex ); + return(FLTCONST); +} + +static int Sc_ScanHex() +{ + S32 val = 0; + dSscanf(CMDtext, "%x", &val); + CMDlval.i = MakeToken< int >( val, lineIndex ); + return INTCONST; +} + +void CMD_reset() +{ + CMDrestart(NULL); +} + diff --git a/Engine/source/console/CMDscan.l b/Engine/source/console/CMDscan.l new file mode 100644 index 000000000..d6d37a5c6 --- /dev/null +++ b/Engine/source/console/CMDscan.l @@ -0,0 +1,624 @@ +%{ + +// flex --nounput -o CMDscan.cpp -P CMD CMDscan.l + +#define YYLMAX 4096 +#define YY_NO_UNISTD_H + +#include +#include "platform/platform.h" +#include "core/stringTable.h" +#include "console/console.h" +#include "console/compiler.h" +#include "console/dynamicTypes.h" +#include "core/strings/stringFunctions.h" + +template< typename T > +struct Token +{ + T value; + S32 lineNumber; +}; + +// Can't have ctors in structs used in unions, so we have this. +template< typename T > +inline Token< T > MakeToken( T value, U32 lineNumber ) +{ + Token< T > result; + result.value = value; + result.lineNumber = lineNumber; + return result; +} + +#include "console/cmdgram.h" + +using namespace Compiler; + +#define YY_NEVER_INTERACTIVE 1 + +// Some basic parsing primitives... +static int Sc_ScanDocBlock(); +static int Sc_ScanString(int ret); +static int Sc_ScanNum(); +static int Sc_ScanVar(); +static int Sc_ScanHex(); +static int Sc_ScanIdent(); + +// Deal with debuggability of FLEX. +#ifdef TORQUE_DEBUG +#define FLEX_DEBUG 1 +#else +#define FLEX_DEBUG 0 +#endif + +// Install our own input code... +#undef CMDgetc +int CMDgetc(); + +// Hack to make windows lex happy. +#ifndef isatty +inline int isatty(int) { return 0; } +#endif + +// Wrap our getc, so that lex doesn't try to do its own buffering/file IO. +#define YY_INPUT(buf,result,max_size) \ + { \ + int c = '*', n; \ + for ( n = 0; n < max_size && \ + (c = CMDgetc()) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + result = n; \ + } + +// General helper stuff. +static int lineIndex; + +// File state +void CMDSetScanBuffer(const char *sb, const char *fn); +const char * CMDgetFileLine(int &lineNumber); + +// Error reporting +void CMDerror(char * s, ...); + +// Reset the parser. +void CMDrestart(FILE *in); + +%} + +DIGIT [0-9] +INTEGER {DIGIT}+ +FLOAT ({INTEGER}?\.{INTEGER})|({INTEGER}(\.{INTEGER})?[eE][+-]?{INTEGER}) +LETTER [A-Za-z_] +FILECHAR [A-Za-z_\.] +VARMID [:A-Za-z0-9_] +IDTAIL [A-Za-z0-9_] +VARTAIL {VARMID}*{IDTAIL} +VAR [$%]{LETTER}{VARTAIL}* +ID {LETTER}{IDTAIL}* +ILID [$%]{DIGIT}+{LETTER}{VARTAIL}* +FILENAME {FILECHAR}+ +SPACE [ \t\v\f] +HEXDIGIT [a-fA-F0-9] + +%% + ; +{SPACE}+ { } +("///"[^/][^\n\r]*[\n\r]*)+ { return(Sc_ScanDocBlock()); } +"//"[^\n\r]* ; +[\r] ; +[\n] {lineIndex++;} +\"(\\.|[^\\"\n\r])*\" { return(Sc_ScanString(STRATOM)); } +\'(\\.|[^\\'\n\r])*\' { return(Sc_ScanString(TAGATOM)); } +"==" { CMDlval.i = MakeToken< int >( opEQ, lineIndex ); return opEQ; } +"!=" { CMDlval.i = MakeToken< int >( opNE, lineIndex ); return opNE; } +">=" { CMDlval.i = MakeToken< int >( opGE, lineIndex ); return opGE; } +"<=" { CMDlval.i = MakeToken< int >( opLE, lineIndex ); return opLE; } +"&&" { CMDlval.i = MakeToken< int >( opAND, lineIndex ); return opAND; } +"||" { CMDlval.i = MakeToken< int >( opOR, lineIndex ); return opOR; } +"::" { CMDlval.i = MakeToken< int >( opCOLONCOLON, lineIndex ); return opCOLONCOLON; } +"--" { CMDlval.i = MakeToken< int >( opMINUSMINUS, lineIndex ); return opMINUSMINUS; } +"++" { CMDlval.i = MakeToken< int >( opPLUSPLUS, lineIndex ); return opPLUSPLUS; } +"$=" { CMDlval.i = MakeToken< int >( opSTREQ, lineIndex ); return opSTREQ; } +"!$=" { CMDlval.i = MakeToken< int >( opSTRNE, lineIndex ); return opSTRNE; } +"<<" { CMDlval.i = MakeToken< int >( opSHL, lineIndex ); return opSHL; } +">>" { CMDlval.i = MakeToken< int >( opSHR, lineIndex ); return opSHR; } +"+=" { CMDlval.i = MakeToken< int >( opPLASN, lineIndex ); return opPLASN; } +"-=" { CMDlval.i = MakeToken< int >( opMIASN, lineIndex ); return opMIASN; } +"*=" { CMDlval.i = MakeToken< int >( opMLASN, lineIndex ); return opMLASN; } +"/=" { CMDlval.i = MakeToken< int >( opDVASN, lineIndex ); return opDVASN; } +"%=" { CMDlval.i = MakeToken< int >( opMODASN, lineIndex ); return opMODASN; } +"&=" { CMDlval.i = MakeToken< int >( opANDASN, lineIndex ); return opANDASN; } +"^=" { CMDlval.i = MakeToken< int >( opXORASN, lineIndex ); return opXORASN; } +"|=" { CMDlval.i = MakeToken< int >( opORASN, lineIndex ); return opORASN; } +"<<=" { CMDlval.i = MakeToken< int >( opSLASN, lineIndex ); return opSLASN; } +">>=" { CMDlval.i = MakeToken< int >( opSRASN, lineIndex ); return opSRASN; } +"->" { CMDlval.i = MakeToken< int >( opINTNAME, lineIndex ); return opINTNAME; } +"-->" { CMDlval.i = MakeToken< int >( opINTNAMER, lineIndex ); return opINTNAMER; } +"NL" { CMDlval.i = MakeToken< int >( '\n', lineIndex ); return '@'; } +"TAB" { CMDlval.i = MakeToken< int >( '\t', lineIndex ); return '@'; } +"SPC" { CMDlval.i = MakeToken< int >( ' ', lineIndex ); return '@'; } +"@" { CMDlval.i = MakeToken< int >( 0, lineIndex ); return '@'; } +"/*" { /* this comment stops syntax highlighting from getting messed up when editing the lexer in TextPad */ + register int c = 0, l; + for ( ; ; ) + { + l = c; + c = yyinput(); + + // Is this an open comment? + if ( c == EOF ) + { + CMDerror( "unexpected end of file found in comment" ); + break; + } + + // Increment line numbers. + else if ( c == '\n' ) + lineIndex++; + + // Did we find the end of the comment? + else if ( l == '*' && c == '/' ) + break; + } + } +"?" | +"[" | +"]" | +"(" | +")" | +"+" | +"-" | +"*" | +"/" | +"<" | +">" | +"|" | +"." | +"!" | +":" | +";" | +"{" | +"}" | +"," | +"&" | +"%" | +"^" | +"~" | +"=" { CMDlval.i = MakeToken< int >( CMDtext[ 0 ], lineIndex ); return CMDtext[ 0 ]; } +"in" { CMDlval.i = MakeToken< int >( rwIN, lineIndex ); return(rwIN); } +"or" { CMDlval.i = MakeToken< int >( rwCASEOR, lineIndex ); return(rwCASEOR); } +"break" { CMDlval.i = MakeToken< int >( rwBREAK, lineIndex ); return(rwBREAK); } +"return" { CMDlval.i = MakeToken< int >( rwRETURN, lineIndex ); return(rwRETURN); } +"else" { CMDlval.i = MakeToken< int >( rwELSE, lineIndex ); return(rwELSE); } +"assert" { CMDlval.i = MakeToken< int >( rwASSERT, lineIndex ); return(rwASSERT); } +"while" { CMDlval.i = MakeToken< int >( rwWHILE, lineIndex ); return(rwWHILE); } +"do" { CMDlval.i = MakeToken< int >( rwDO, lineIndex ); return(rwDO); } +"if" { CMDlval.i = MakeToken< int >( rwIF, lineIndex ); return(rwIF); } +"foreach$" { CMDlval.i = MakeToken< int >( rwFOREACHSTR, lineIndex ); return(rwFOREACHSTR); } +"foreach" { CMDlval.i = MakeToken< int >( rwFOREACH, lineIndex ); return(rwFOREACH); } +"for" { CMDlval.i = MakeToken< int >( rwFOR, lineIndex ); return(rwFOR); } +"continue" { CMDlval.i = MakeToken< int >( rwCONTINUE, lineIndex ); return(rwCONTINUE); } +"function" { CMDlval.i = MakeToken< int >( rwDEFINE, lineIndex ); return(rwDEFINE); } +"new" { CMDlval.i = MakeToken< int >( rwDECLARE, lineIndex ); return(rwDECLARE); } +"singleton" { CMDlval.i = MakeToken< int >( rwDECLARESINGLETON, lineIndex ); return(rwDECLARESINGLETON); } +"datablock" { CMDlval.i = MakeToken< int >( rwDATABLOCK, lineIndex ); return(rwDATABLOCK); } +"case" { CMDlval.i = MakeToken< int >( rwCASE, lineIndex ); return(rwCASE); } +"switch$" { CMDlval.i = MakeToken< int >( rwSWITCHSTR, lineIndex ); return(rwSWITCHSTR); } +"switch" { CMDlval.i = MakeToken< int >( rwSWITCH, lineIndex ); return(rwSWITCH); } +"default" { CMDlval.i = MakeToken< int >( rwDEFAULT, lineIndex ); return(rwDEFAULT); } +"package" { CMDlval.i = MakeToken< int >( rwPACKAGE, lineIndex ); return(rwPACKAGE); } +"namespace" { CMDlval.i = MakeToken< int >( rwNAMESPACE, lineIndex ); return(rwNAMESPACE); } +"true" { CMDlval.i = MakeToken< int >( 1, lineIndex ); return INTCONST; } +"false" { CMDlval.i = MakeToken< int >( 0, lineIndex ); return INTCONST; } +{VAR} { return(Sc_ScanVar()); } +{ID} { return Sc_ScanIdent(); } +0[xX]{HEXDIGIT}+ return(Sc_ScanHex()); +{INTEGER} { CMDtext[CMDleng] = 0; CMDlval.i = MakeToken< int >( dAtoi(CMDtext), lineIndex ); return INTCONST; } +{FLOAT} return Sc_ScanNum(); +{ILID} return(ILLEGAL_TOKEN); +. return(ILLEGAL_TOKEN); +%% + +static const char *scanBuffer; +static const char *fileName; +static int scanIndex; + +const char * CMDGetCurrentFile() +{ + return fileName; +} + +int CMDGetCurrentLine() +{ + return lineIndex; +} + +extern bool gConsoleSyntaxError; + +void CMDerror(char *format, ...) +{ + Compiler::gSyntaxError = true; + + const int BUFMAX = 1024; + char tempBuf[BUFMAX]; + va_list args; + va_start( args, format ); +#ifdef TORQUE_OS_WIN32 + _vsnprintf( tempBuf, BUFMAX, format, args ); +#else + vsnprintf( tempBuf, BUFMAX, format, args ); +#endif + + if(fileName) + { + Con::errorf(ConsoleLogEntry::Script, "%s Line: %d - %s", fileName, lineIndex, tempBuf); + +#ifndef NO_ADVANCED_ERROR_REPORT + // dhc - lineIndex is bogus. let's try to add some sanity back in. + int i,j,n; + char c; + int linediv = 1; + // first, walk the buffer, trying to detect line ending type. + // this is imperfect, if inconsistant line endings exist... + for (i=0; iBUFMAX>>2) break; // at least get a little data + n++; i++; + } + // find next lineending + while (n>1) // cap at half-buf-size forward + { + c = scanBuffer[scanIndex+j]; + if (c==0) break; + if ((c=='\r' || c=='\n') && j>BUFMAX>>2) break; // at least get a little data + n++; j++; + } + if (i) i--; // chop off extra linefeed. + if (j) j--; // chop off extra linefeed. + // build our little text block + if (i) dStrncpy(tempBuf,scanBuffer+scanIndex-i,i); + dStrncpy(tempBuf+i,"##", 2); // bracketing. + tempBuf[i+2] = scanBuffer[scanIndex]; // copy the halt character. + dStrncpy(tempBuf+i+3,"##", 2); // bracketing. + if (j) dStrncpy(tempBuf+i+5,scanBuffer+scanIndex+1,j); // +1 to go past current char. + tempBuf[i+j+5] = 0; // null terminate + for(n=0; n>> Advanced script error report. Line %d.", lineIndex); + Con::warnf(ConsoleLogEntry::Script, ">>> Some error context, with ## on sides of error halt:"); + Con::errorf(ConsoleLogEntry::Script, "%s", tempBuf); + Con::warnf(ConsoleLogEntry::Script, ">>> Error report complete.\n"); +#endif + + // Update the script-visible error buffer. + const char *prevStr = Con::getVariable("$ScriptError"); + if (prevStr[0]) + dSprintf(tempBuf, sizeof(tempBuf), "%s\n%s Line: %d - Syntax error.", prevStr, fileName, lineIndex); + else + dSprintf(tempBuf, sizeof(tempBuf), "%s Line: %d - Syntax error.", fileName, lineIndex); + Con::setVariable("$ScriptError", tempBuf); + + // We also need to mark that we came up with a new error. + static S32 sScriptErrorHash=1000; + Con::setIntVariable("$ScriptErrorHash", sScriptErrorHash++); + } + else + Con::errorf(ConsoleLogEntry::Script, tempBuf); +} + +void CMDSetScanBuffer(const char *sb, const char *fn) +{ + scanBuffer = sb; + fileName = fn; + scanIndex = 0; + lineIndex = 1; +} + +int CMDgetc() +{ + int ret = scanBuffer[scanIndex]; + if(ret) + scanIndex++; + else + ret = -1; + return ret; +} + +int CMDwrap() +{ + return 1; +} + +static int Sc_ScanVar() +{ + // Truncate the temp buffer... + CMDtext[CMDleng] = 0; + + // Make it a stringtable string! + CMDlval.s = MakeToken< StringTableEntry >( StringTable->insert(CMDtext), lineIndex ); + return(VAR); +} + +static int charConv(int in) +{ + switch(in) + { + case 'r': + return '\r'; + case 'n': + return '\n'; + case 't': + return '\t'; + default: + return in; + } +} + +static int getHexDigit(char c) +{ + if(c >= '0' && c <= '9') + return c - '0'; + if(c >= 'A' && c <= 'F') + return c - 'A' + 10; + if(c >= 'a' && c <= 'f') + return c - 'a' + 10; + return -1; +} + +static int Sc_ScanDocBlock() +{ + S32 len = dStrlen(CMDtext); + char* text = (char *) consoleAlloc(len + 1); + S32 line = lineIndex; + + for( S32 i = 0, j = 0; j <= len; j++ ) + { + if( ( j <= (len - 2) ) && ( CMDtext[j] == '/' ) && ( CMDtext[j + 1] == '/' ) && ( CMDtext[j + 2] == '/' ) ) + { + j += 2; + continue; + } + + if( CMDtext[j] == '\r' ) + continue; + + if( CMDtext[j] == '\n' ) + lineIndex++; + + text[i++] = CMDtext[j]; + } + + CMDlval.str = MakeToken< char* >( text, line ); + return(DOCBLOCK); +} + +static int Sc_ScanString(int ret) +{ + CMDtext[CMDleng - 1] = 0; + if(!collapseEscape(CMDtext+1)) + return -1; + + char* buffer = ( char* ) consoleAlloc( dStrlen( CMDtext ) ); + dStrcpy( buffer, CMDtext + 1 ); + + CMDlval.str = MakeToken< char* >( buffer, lineIndex ); + return ret; +} + +static int Sc_ScanIdent() +{ + ConsoleBaseType *type; + + CMDtext[CMDleng] = 0; + + if((type = ConsoleBaseType::getTypeByName(CMDtext)) != NULL) + { + /* It's a type */ + CMDlval.i = MakeToken< int >( type->getTypeID(), lineIndex ); + return TYPEIDENT; + } + + /* It's an identifier */ + CMDlval.s = MakeToken< StringTableEntry >( StringTable->insert(CMDtext), lineIndex ); + return IDENT; +} + +void expandEscape(char *dest, const char *src) +{ + U8 c; + while((c = (U8) *src++) != 0) + { + if(c == '\"') + { + *dest++ = '\\'; + *dest++ = '\"'; + } + else if(c == '\\') + { + *dest++ = '\\'; + *dest++ = '\\'; + } + else if(c == '\r') + { + *dest++ = '\\'; + *dest++ = 'r'; + } + else if(c == '\n') + { + *dest++ = '\\'; + *dest++ = 'n'; + } + else if(c == '\t') + { + *dest++ = '\\'; + *dest++ = 't'; + } + else if(c == '\'') + { + *dest++ = '\\'; + *dest++ = '\''; + } + else if((c >= 1 && c <= 7) || + (c >= 11 && c <= 12) || + (c >= 14 && c <= 15)) + { + /* Remap around: \b = 0x8, \t = 0x9, \n = 0xa, \r = 0xd */ + static U8 expandRemap[15] = { 0x0, + 0x0, + 0x1, + 0x2, + 0x3, + 0x4, + 0x5, + 0x6, + 0x0, + 0x0, + 0x0, + 0x7, + 0x8, + 0x0, + 0x9 }; + + *dest++ = '\\'; + *dest++ = 'c'; + if(c == 15) + *dest++ = 'r'; + else if(c == 16) + *dest++ = 'p'; + else if(c == 17) + *dest++ = 'o'; + else + *dest++ = expandRemap[c] + '0'; + } + else if(c < 32) + { + *dest++ = '\\'; + *dest++ = 'x'; + S32 dig1 = c >> 4; + S32 dig2 = c & 0xf; + if(dig1 < 10) + dig1 += '0'; + else + dig1 += 'A' - 10; + if(dig2 < 10) + dig2 += '0'; + else + dig2 += 'A' - 10; + *dest++ = dig1; + *dest++ = dig2; + } + else + *dest++ = c; + } + *dest = '\0'; +} + +bool collapseEscape(char *buf) +{ + S32 len = dStrlen(buf) + 1; + for(S32 i = 0; i < len;) + { + if(buf[i] == '\\') + { + if(buf[i+1] == 'x') + { + S32 dig1 = getHexDigit(buf[i+2]); + if(dig1 == -1) + return false; + + S32 dig2 = getHexDigit(buf[i+3]); + if(dig2 == -1) + return false; + buf[i] = dig1 * 16 + dig2; + dMemmove(buf + i + 1, buf + i + 4, len - i - 3); + len -= 3; + i++; + } + else if(buf[i+1] == 'c') + { + /* Remap around: \b = 0x8, \t = 0x9, \n = 0xa, \r = 0xd */ + static U8 collapseRemap[10] = { 0x1, + 0x2, + 0x3, + 0x4, + 0x5, + 0x6, + 0x7, + 0xb, + 0xc, + 0xe }; + + if(buf[i+2] == 'r') + buf[i] = 15; + else if(buf[i+2] == 'p') + buf[i] = 16; + else if(buf[i+2] == 'o') + buf[i] = 17; + else + { + int dig1 = buf[i+2] - '0'; + if(dig1 < 0 || dig1 > 9) + return false; + buf[i] = collapseRemap[dig1]; + } + // Make sure we don't put 0x1 at the beginning of the string. + if ((buf[i] == 0x1) && (i == 0)) + { + buf[i] = 0x2; + buf[i+1] = 0x1; + dMemmove(buf + i + 2, buf + i + 3, len - i - 1); + len -= 1; + } + else + { + dMemmove(buf + i + 1, buf + i + 3, len - i - 2); + len -= 2; + } + i++; + } + else + { + buf[i] = charConv(buf[i+1]); + dMemmove(buf + i + 1, buf + i + 2, len - i - 1); + len--; + i++; + } + } + else + i++; + } + return true; +} + +static int Sc_ScanNum() +{ + CMDtext[CMDleng] = 0; + CMDlval.f = MakeToken< double >( dAtof(CMDtext), lineIndex ); + return(FLTCONST); +} + +static int Sc_ScanHex() +{ + S32 val = 0; + dSscanf(CMDtext, "%x", &val); + CMDlval.i = MakeToken< int >( val, lineIndex ); + return INTCONST; +} + +void CMD_reset() +{ + CMDrestart(NULL); +} diff --git a/Engine/source/console/ICallMethod.h b/Engine/source/console/ICallMethod.h new file mode 100644 index 000000000..dc4904262 --- /dev/null +++ b/Engine/source/console/ICallMethod.h @@ -0,0 +1,33 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _ICALLMETHOD_H_ +#define _ICALLMETHOD_H_ + +class ICallMethod +{ +public: + virtual const char* callMethod( S32 argc, const char* methodName, ... ) = 0; + virtual const char* callMethodArgList( U32 argc, const char *argv[], bool callThis = true ) = 0; +}; + +#endif \ No newline at end of file diff --git a/Engine/source/console/SimXMLDocument.cpp b/Engine/source/console/SimXMLDocument.cpp new file mode 100644 index 000000000..6076b9711 --- /dev/null +++ b/Engine/source/console/SimXMLDocument.cpp @@ -0,0 +1,1405 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "tinyxml/tinyxml.h" + +//----------------------------------------------------------------------------- +// Console implementation of STL map. +//----------------------------------------------------------------------------- + +#include "core/frameAllocator.h" +#include "console/simBase.h" +#include "console/consoleInternal.h" +#include "console/SimXMLDocument.h" +#include "console/engineAPI.h" + +IMPLEMENT_CONOBJECT(SimXMLDocument); + +ConsoleDocClass( SimXMLDocument, + "@brief File I/O object used for creating, reading, and writing XML documents.\n\n" + + "A SimXMLDocument is a container of various XML nodes. The Document level may contain " + "a header (sometimes called a declaration), comments and child Elements. Elements may " + "contain attributes, data (or text) and child Elements.\n\n" + + "You build new Elements using addNewElement(). This makes the new Element the current " + "one you're working with. You then use setAttribute() to add attributes to the Element. " + "You use addData() or addText() to write to the text area of an Element." + + "@tsexample\n" + "// Thanks to Rex Hiebert for this example\n" + "// Given the following XML\n" + "\n" + "\n" + " \n" + " Triangle\n" + " Square\n" + " Circle\n" + "
      \n" + " \n" + " Pyramid\n" + " Cube\n" + " Sphere\n" + "
      \n" + "
      \n\n" + "// Using SimXMLDocument by itself\n" + "function readXmlExample(%filename)\n" + "{\n" + " %xml = new SimXMLDocument() {};\n" + " %xml.loadFile(%filename);\n\n" + " %xml.pushChildElement(\"DataTables\");\n" + " %xml.pushFirstChildElement(\"table\");\n" + " while(true)\n" + " {\n" + " echo(\"TABLE:\" SPC %xml.attribute(\"tableName\"));\n" + " %xml.pushFirstChildElement(\"rec\");\n" + " while (true)\n" + " {\n" + " %id = %xml.attribute(\"id\");\n" + " %desc = %xml.getData();\n" + " echo(\" Shape\" SPC %id SPC %desc);\n" + " if (!%xml.nextSiblingElement(\"rec\")) break;\n" + " }\n" + " %xml.popElement();\n" + " if (!%xml.nextSiblingElement(\"table\")) break;\n" + " }\n" + "}\n\n" + + "// Thanks to Scott Peal for this example\n" + "// Using FileObject in conjunction with SimXMLDocument\n" + "// This example uses an XML file with a format of:\n" + "// \n" + "// \n" + "// \n" + "function getModelsInCatagory()\n" + "{\n" + " %file = \"./Catalog.xml\";\n" + " %fo = new FileObject();\n" + " %text = \"\";\n\n" + " if(%fo.openForRead(%file))\n" + " {\n" + " while(!%fo.isEOF())\n" + " {\n" + " %text = %text @ %fo.readLine();\n" + " if (!%fo.isEOF()) %text = %text @ \"\\n\";\n" + " }\n" + " }\n" + " else\n" + " {\n" + " echo(\"Unable to locate the file: \" @ %file);\n" + " }\n\n" + " %fo.delete();\n\n" + " %xml = new SimXMLDocument() {};\n" + " %xml.parse(%text);\n" + " // \"Get\" inside of the root element, \"Models\".\n" + " %xml.pushChildElement(0);\n\n" + " // \"Get\" into the first child element\n" + " if (%xml.pushFirstChildElement(\"Model\"))\n" + " {\n" + " while (true)\n" + " {\n" + " // \n" + " // Here, i read the element's attributes.\n" + " // You might want to save these values in an array or call the %xml.getElementValue()\n" + " // if you have a different XML structure.\n\n" + " %catagory = %xml.attribute(\"catagory\");\n" + " %name = %xml.attribute(\"name\");\n" + " %path = %xml.attribute(\"path\");\n\n" + " // now, read the next \"Model\"\n" + " if (!%xml.nextSiblingElement(\"Model\")) break;\n" + " }\n" + " }\n" + "}\n" + "@endtsexample\n\n" + + "@note SimXMLDocument is a wrapper around TinyXml, a standard XML library. If you're familiar " + "with its concepts, you'll find they also apply here.\n\n" + + "@see FileObject\n\n" + + "@ingroup FileSystem\n" +); + +// ----------------------------------------------------------------------------- +// Constructor. +// ----------------------------------------------------------------------------- +SimXMLDocument::SimXMLDocument(): +m_qDocument(0), +m_CurrentAttribute(0) +{ +} + +// ----------------------------------------------------------------------------- +// Destructor. +// ----------------------------------------------------------------------------- +SimXMLDocument::~SimXMLDocument() +{ +} + +// ----------------------------------------------------------------------------- +// Included for completeness. +// ----------------------------------------------------------------------------- +bool SimXMLDocument::processArguments(S32 argc, const char** argv) +{ + return argc == 0; +} + +// ----------------------------------------------------------------------------- +// Script constructor. +// ----------------------------------------------------------------------------- +bool SimXMLDocument::onAdd() +{ + if(!Parent::onAdd()) + { + return false; + } + + if(!m_qDocument) + { + m_qDocument = new TiXmlDocument(); + } + return true; +} + +// ----------------------------------------------------------------------------- +// Script destructor. +// ----------------------------------------------------------------------------- +void SimXMLDocument::onRemove() +{ + Parent::onRemove(); + if(m_qDocument) + { + m_qDocument->Clear(); + delete(m_qDocument); + } +} + +// ----------------------------------------------------------------------------- +// Initialize peristent fields (datablocks). +// ----------------------------------------------------------------------------- +void SimXMLDocument::initPersistFields() +{ + Parent::initPersistFields(); +} + +// ----------------------------------------------------------------------------- +// Set this to default state at construction. +// ----------------------------------------------------------------------------- +void SimXMLDocument::reset(void) +{ + m_qDocument->Clear(); + m_paNode.clear(); + m_CurrentAttribute = 0; +} + +DefineEngineMethod( SimXMLDocument, reset, void, (),, + "@brief Set this document to its default state.\n\n" + + "Clears all Elements from the documents. Equivalent to using clear()\n\n" + + "@see clear()") +{ + object->reset(); +} + +// ----------------------------------------------------------------------------- +// Get true if file loads successfully. +// ----------------------------------------------------------------------------- +bool SimXMLDocument::loadFile(const char* rFileName) +{ + reset(); + return m_qDocument->LoadFile(rFileName); +} + +DefineEngineMethod( SimXMLDocument, loadFile, bool, ( const char* fileName ),, + "@brief Load in given filename and prepare it for use.\n\n" + "@note Clears the current document's contents.\n\n" + "@param fileName Name and path of XML document\n" + "@return True if the file was loaded successfully.") +{ + return object->loadFile( fileName ); +} + +// ----------------------------------------------------------------------------- +// Get true if file saves successfully. +// ----------------------------------------------------------------------------- +bool SimXMLDocument::saveFile(const char* rFileName) +{ + return m_qDocument->SaveFile( rFileName ); +} + +// ----------------------------------------------------------------------------- +// Get true if file saves successfully to string. +// ----------------------------------------------------------------------------- +bool SimXMLDocument::saveToString(String& str) +{ + TiXmlPrinter printer; + bool ret = m_qDocument->Accept( &printer ); + if (ret) + str = printer.CStr(); + return ret; +} + +DefineEngineMethod( SimXMLDocument, saveFile, bool, ( const char* fileName ),, + "@brief Save document to the given file name.\n\n" + "@param fileName Path and name of XML file to save to.\n" + "@return True if the file was successfully saved.") +{ + return object->saveFile( fileName ); +} + +// ----------------------------------------------------------------------------- +// Get true if XML text parses correctly. +// ----------------------------------------------------------------------------- +S32 SimXMLDocument::parse(const char* rText) +{ + reset(); + m_qDocument->Parse( rText ); + return 1; +} + +DefineEngineMethod( SimXMLDocument, parse, void, ( const char* xmlString ),, + "@brief Create a document from a XML string.\n\n" + "@note Clears the current document's contents.\n\n" + "@param xmlString Valid XML to parse and store as a document.") +{ + object->parse( xmlString ); +} + +// ----------------------------------------------------------------------------- +// Clear contents of XML document. +// ----------------------------------------------------------------------------- +void SimXMLDocument::clear(void) +{ + reset(); +} + +DefineEngineMethod( SimXMLDocument, clear, void, (),, + "@brief Set this document to its default state.\n\n" + + "Clears all Elements from the documents. Equivalent to using reset()\n\n" + + "@see reset()") +{ + object->clear(); +} + +// ----------------------------------------------------------------------------- +// Get error description of XML document. +// ----------------------------------------------------------------------------- +const char* SimXMLDocument::getErrorDesc(void) const +{ + if(!m_qDocument) + { + return StringTable->insert("No document"); + } + return m_qDocument->ErrorDesc(); +} + +DefineEngineMethod( SimXMLDocument, getErrorDesc, const char*, (),, + "@brief Get last error description.\n\n" + "@return A string of the last error message.") +{ + return object->getErrorDesc(); +} + +// ----------------------------------------------------------------------------- +// Clear error description of this. +// ----------------------------------------------------------------------------- +void SimXMLDocument::clearError(void) +{ + m_qDocument->ClearError(); +} + +DefineEngineMethod( SimXMLDocument, clearError, void, (),, + "@brief Clear the last error description.\n\n") +{ + object->clearError(); +} + +// ----------------------------------------------------------------------------- +// Get true if first child element was successfully pushed onto stack. +// ----------------------------------------------------------------------------- +bool SimXMLDocument::pushFirstChildElement(const char* rName) +{ + // Clear the current attribute pointer + m_CurrentAttribute = 0; + + // Push the first element found under the current element of the given name + TiXmlElement* pElement; + if(!m_paNode.empty()) + { + const int iLastElement = m_paNode.size() - 1; + TiXmlElement* pNode = m_paNode[iLastElement]; + if(!pNode) + { + return false; + } + pElement = pNode->FirstChildElement(rName); + } + else + { + if(!m_qDocument) + { + return false; + } + pElement = m_qDocument->FirstChildElement(rName); + } + + if(!pElement) + { + return false; + } + m_paNode.push_back(pElement); + return true; +} + +DefineEngineMethod( SimXMLDocument, pushFirstChildElement, bool, ( const char* name ),, + "@brief Push the first child Element with the given name onto the stack, making it the current Element.\n\n" + + "@param name String containing name of the child Element.\n" + "@return True if the Element was found and made the current one.\n" + + "@tsexample\n" + "// Using the following test.xml file as an example:\n" + "// \n" + "// Some text\n\n" + + "// Load in the file\n" + "%x = new SimXMLDocument();\n" + "%x.loadFile(\"test.xml\");\n\n" + + "// Make the first Element the current one\n" + "%x.pushFirstChildElement(\"NewElement\");\n\n" + + "// Store the current Element's text ('Some text' in this example)\n" + "// into 'result'\n" + "%result = %x.getText();\n" + "echo( %result );\n" + "@endtsexample\n\n") +{ + return object->pushFirstChildElement( name ); +} + +// ----------------------------------------------------------------------------- +// Get true if first child element was successfully pushed onto stack. +// ----------------------------------------------------------------------------- +bool SimXMLDocument::pushChildElement(S32 index) +{ + // Clear the current attribute pointer + m_CurrentAttribute = 0; + + // Push the first element found under the current element of the given name + TiXmlElement* pElement; + if(!m_paNode.empty()) + { + const int iLastElement = m_paNode.size() - 1; + TiXmlElement* pNode = m_paNode[iLastElement]; + if(!pNode) + { + return false; + } + pElement = pNode->FirstChildElement(); + for( S32 i = 0; i < index; i++ ) + { + if( !pElement ) + return false; + + pElement = pElement->NextSiblingElement(); + } + } + else + { + if(!m_qDocument) + { + return false; + } + pElement = m_qDocument->FirstChildElement(); + for( S32 i = 0; i < index; i++ ) + { + if( !pElement ) + return false; + + pElement = pElement->NextSiblingElement(); + } + } + + if(!pElement) + { + return false; + } + m_paNode.push_back(pElement); + return true; +} + +DefineEngineMethod( SimXMLDocument, pushChildElement, bool, ( S32 index ),, + "@brief Push the child Element at the given index onto the stack, making it the current one.\n\n" + "@param index Numerical index of Element being pushed." + "@return True if the Element was found and made the current one.\n") +{ + return object->pushChildElement( index ); +} + +// ----------------------------------------------------------------------------- +// Convert top stack element into its next sibling element. +// ----------------------------------------------------------------------------- +bool SimXMLDocument::nextSiblingElement(const char* rName) +{ + // Clear the current attribute pointer + m_CurrentAttribute = 0; + + // Attempt to find the next sibling element + if(m_paNode.empty()) + { + return false; + } + const int iLastElement = m_paNode.size() - 1; + TiXmlElement*& pElement = m_paNode[iLastElement]; + if(!pElement) + { + return false; + } + + pElement = pElement->NextSiblingElement(rName); + if(!pElement) + { + return false; + } + + return true; +} + +DefineEngineMethod( SimXMLDocument, nextSiblingElement, bool, ( const char* name ),, + "@brief Put the next sibling Element with the given name on the stack, making it the current one.\n\n" + "@param name String containing name of the next sibling." + "@return True if the Element was found and made the current one.\n") +{ + return object->nextSiblingElement( name ); +} + +// ----------------------------------------------------------------------------- +// Get element value if it exists. Used to extract a text node from the element. +// for example. +// ----------------------------------------------------------------------------- +const char* SimXMLDocument::elementValue() +{ + if(m_paNode.empty()) + { + return StringTable->insert(""); + } + const int iLastElement = m_paNode.size() - 1; + TiXmlElement* pNode = m_paNode[iLastElement]; + if(!pNode) + { + return StringTable->insert(""); + } + + return pNode->Value(); +} + +DefineEngineMethod( SimXMLDocument, elementValue, const char*, (),, + "@brief Get the Element's value if it exists.\n\n" + "Usually returns the text from the Element.\n" + "@return The value from the Element, or an empty string if none is found.") +{ + return object->elementValue(); +} + +// ----------------------------------------------------------------------------- +// Pop last element off of stack. +// ----------------------------------------------------------------------------- +void SimXMLDocument::popElement(void) +{ + m_paNode.pop_back(); +} + +DefineEngineMethod( SimXMLDocument, popElement, void, (),, + "@brief Pop the last Element off the stack.\n\n") +{ + object->popElement(); +} + +// ----------------------------------------------------------------------------- +// Get attribute value if it exists. +// ----------------------------------------------------------------------------- +const char* SimXMLDocument::attribute(const char* rAttribute) +{ + if(m_paNode.empty()) + { + return StringTable->insert(""); + } + const int iLastElement = m_paNode.size() - 1; + TiXmlElement* pNode = m_paNode[iLastElement]; + if(!pNode) + { + return StringTable->insert(""); + } + + if(!pNode->Attribute(rAttribute)) + { + return StringTable->insert(""); + } + + return pNode->Attribute(rAttribute); +} + +DefineEngineMethod( SimXMLDocument, attribute, const char*, ( const char* attributeName ),, + "@brief Get a string attribute from the current Element on the stack.\n\n" + "@param attributeName Name of attribute to retrieve.\n" + "@return The attribute string if found. Otherwise returns an empty string.\n") +{ + return object->attribute( attributeName ); +} + +// These two methods don't make a lot of sense the way TS works. Leaving them in for backwards-compatibility. +ConsoleMethod( SimXMLDocument, attributeF32, F32, 3, 3, "(string attributeName)" + "@brief Get float attribute from the current Element on the stack.\n\n" + "@param attributeName Name of attribute to retrieve.\n" + "@return The value of the given attribute in the form of a float.\n" + "@deprecated Use attribute().") +{ + return dAtof( object->attribute( argv[2] ) ); +} +ConsoleMethod(SimXMLDocument, attributeS32, S32, 3, 3, "(string attributeName)" + "@brief Get int attribute from the current Element on the stack.\n\n" + "@param attributeName Name of attribute to retrieve.\n" + "@return The value of the given attribute in the form of an integer.\n" + "@deprecated Use attribute().") +{ + return dAtoi( object->attribute( argv[2] ) ); +} + +// ----------------------------------------------------------------------------- +// Get true if given attribute exists. +// ----------------------------------------------------------------------------- +bool SimXMLDocument::attributeExists(const char* rAttribute) +{ + if(m_paNode.empty()) + { + return false; + } + const int iLastElement = m_paNode.size() - 1; + TiXmlElement* pNode = m_paNode[iLastElement]; + if(!pNode) + { + return false; + } + + if(!pNode->Attribute(rAttribute)) + { + return false; + } + + return true; +} + +DefineEngineMethod( SimXMLDocument, attributeExists, bool, ( const char* attributeName ),, + "@brief Tests if the requested attribute exists.\n\n" + "@param attributeName Name of attribute being queried for.\n\n" + "@return True if the attribute exists.") +{ + return object->attributeExists( attributeName ); +} + +// ----------------------------------------------------------------------------- +// Obtain the name of the current element's first attribute +// ----------------------------------------------------------------------------- +const char* SimXMLDocument::firstAttribute() +{ + // Get the current element + if(m_paNode.empty()) + { + return StringTable->insert(""); + } + const int iLastElement = m_paNode.size() - 1; + TiXmlElement* pNode = m_paNode[iLastElement]; + if(!pNode) + { + return StringTable->insert(""); + } + + // Gets its first attribute, if any + m_CurrentAttribute = pNode->FirstAttribute(); + if(!m_CurrentAttribute) + { + return StringTable->insert(""); + } + + return m_CurrentAttribute->Name(); +} + +DefineEngineMethod( SimXMLDocument, firstAttribute, const char*, (),, + "@brief Obtain the name of the current Element's first attribute.\n\n" + "@return String containing the first attribute's name, or an empty string if none is found.\n\n" + "@see nextAttribute()\n" + "@see lastAttribute()\n" + "@see prevAttribute()") +{ + return object->firstAttribute(); +} + +// ----------------------------------------------------------------------------- +// Obtain the name of the current element's last attribute +// ----------------------------------------------------------------------------- +const char* SimXMLDocument::lastAttribute() +{ + // Get the current element + if(m_paNode.empty()) + { + return StringTable->insert(""); + } + const int iLastElement = m_paNode.size() - 1; + TiXmlElement* pNode = m_paNode[iLastElement]; + if(!pNode) + { + return StringTable->insert(""); + } + + // Gets its last attribute, if any + m_CurrentAttribute = pNode->LastAttribute(); + if(!m_CurrentAttribute) + { + return StringTable->insert(""); + } + + return m_CurrentAttribute->Name(); +} + +DefineEngineMethod( SimXMLDocument, lastAttribute, const char*, (),, + "@brief Obtain the name of the current Element's last attribute.\n\n" + "@return String containing the last attribute's name, or an empty string if none is found.\n\n" + "@see prevAttribute()\n" + "@see firstAttribute()\n" + "@see lastAttribute()\n") +{ + return object->lastAttribute(); +} + +// ----------------------------------------------------------------------------- +// Get the name of the next attribute for the current element after a call +// to firstAttribute(). +// ----------------------------------------------------------------------------- +const char* SimXMLDocument::nextAttribute() +{ + if(!m_CurrentAttribute) + { + return StringTable->insert(""); + } + + // Gets its next attribute, if any + m_CurrentAttribute = m_CurrentAttribute->Next(); + if(!m_CurrentAttribute) + { + return StringTable->insert(""); + } + + return m_CurrentAttribute->Name(); +} + +DefineEngineMethod( SimXMLDocument, nextAttribute, const char*, (),, + "@brief Get the name of the next attribute for the current Element after a call to firstAttribute().\n\n" + "@return String containing the next attribute's name, or an empty string if none is found." + "@see firstAttribute()\n" + "@see lastAttribute()\n" + "@see prevAttribute()\n") +{ + return object->nextAttribute(); +} + +// ----------------------------------------------------------------------------- +// Get the name of the previous attribute for the current element after a call +// to lastAttribute(). +// ----------------------------------------------------------------------------- +const char* SimXMLDocument::prevAttribute() +{ + if(!m_CurrentAttribute) + { + return StringTable->insert(""); + } + + // Gets its next attribute, if any + m_CurrentAttribute = m_CurrentAttribute->Previous(); + if(!m_CurrentAttribute) + { + return StringTable->insert(""); + } + + return m_CurrentAttribute->Name(); +} + +DefineEngineMethod( SimXMLDocument, prevAttribute, const char*, (),, + "@brief Get the name of the previous attribute for the current Element after a call to lastAttribute().\n\n" + "@return String containing the previous attribute's name, or an empty string if none is found." + "@see lastAttribute()\n" + "@see firstAttribute()\n" + "@see nextAttribute()\n") +{ + return object->prevAttribute(); +} + +// ----------------------------------------------------------------------------- +// Set attribute of top stack element to given value. +// ----------------------------------------------------------------------------- +void SimXMLDocument::setAttribute(const char* rAttribute, const char* rVal) +{ + if(m_paNode.empty()) + { + return; + } + + const int iLastElement = m_paNode.size() - 1; + TiXmlElement* pElement = m_paNode[iLastElement]; + if(!pElement) + { + return; + } + pElement->SetAttribute(rAttribute, rVal); +} +DefineEngineMethod( SimXMLDocument, setAttribute, void, ( const char* attributeName, const char* value ),, + "@brief Set the attribute of the current Element on the stack to the given value.\n\n" + "@param attributeName Name of attribute being changed\n" + "@param value New value to assign to the attribute\n") +{ + object->setAttribute( attributeName, value ); +} + +// ----------------------------------------------------------------------------- +// Set attribute of top stack element to given value. +// ----------------------------------------------------------------------------- +void SimXMLDocument::setObjectAttributes(const char* objectID) +{ + if( !objectID || !objectID[0] ) + return; + + if(m_paNode.empty()) + return; + + SimObject *pObject = Sim::findObject( objectID ); + + if( pObject == NULL ) + return; + + const int iLastElement = m_paNode.size() - 1; + TiXmlElement* pElement = m_paNode[iLastElement]; + if(!pElement) + return; + + char textbuf[1024]; + TiXmlElement field( "Field" ); + TiXmlElement group( "FieldGroup" ); + pElement->SetAttribute( "Name", pObject->getName() ); + + + // Iterate over our filed list and add them to the XML document... + AbstractClassRep::FieldList fieldList = pObject->getFieldList(); + AbstractClassRep::FieldList::iterator itr; + for(itr = fieldList.begin(); itr != fieldList.end(); itr++) + { + + if( itr->type == AbstractClassRep::DeprecatedFieldType || + itr->type == AbstractClassRep::StartGroupFieldType || + itr->type == AbstractClassRep::EndGroupFieldType) continue; + + // Not an Array + if(itr->elementCount == 1) + { + // get the value of the field as a string. + ConsoleBaseType *cbt = ConsoleBaseType::getType(itr->type); + + const char *val = Con::getData(itr->type, (void *) (((const char *)pObject) + itr->offset), 0, itr->table, itr->flag); + + // Make a copy for the field check. + if (!val) + continue; + + FrameTemp valCopy( dStrlen( val ) + 1 ); + dStrcpy( (char *)valCopy, val ); + + if (!pObject->writeField(itr->pFieldname, valCopy)) + continue; + + val = valCopy; + + + expandEscape(textbuf, val); + + if( !pObject->writeField( itr->pFieldname, textbuf ) ) + continue; + + field.SetValue( "Property" ); + field.SetAttribute( "name", itr->pFieldname ); + if( cbt != NULL ) + field.SetAttribute( "type", cbt->getTypeName() ); + else + field.SetAttribute( "type", "TypeString" ); + field.SetAttribute( "data", textbuf ); + + pElement->InsertEndChild( field ); + + continue; + } + } + + //// IS An Array + //for(U32 j = 0; S32(j) < f->elementCount; j++) + //{ + + // // If the start of a group create an element for the group and + // // the our chache to it + // const char *val = Con::getData(itr->type, (void *) (((const char *)pObject) + itr->offset), j, itr->table, itr->flag); + + // // Make a copy for the field check. + // if (!val) + // continue; + + // FrameTemp valCopy( dStrlen( val ) + 1 ); + // dStrcpy( (char *)valCopy, val ); + + // if (!pObject->writeField(itr->pFieldname, valCopy)) + // continue; + + // val = valCopy; + + // // get the value of the field as a string. + // ConsoleBaseType *cbt = ConsoleBaseType::getType(itr->type); + // const char * dstr = Con::getData(itr->type, (void *)(((const char *)pObject) + itr->offset), 0, itr->table, itr->flag); + // if(!dstr) + // dstr = ""; + // expandEscape(textbuf, dstr); + + + // if( !pObject->writeField( itr->pFieldname, dstr ) ) + // continue; + + // field.SetValue( "Property" ); + // field.SetAttribute( "name", itr->pFieldname ); + // if( cbt != NULL ) + // field.SetAttribute( "type", cbt->getTypeName() ); + // else + // field.SetAttribute( "type", "TypeString" ); + // field.SetAttribute( "data", textbuf ); + + // pElement->InsertEndChild( field ); + //} + +} + +DefineEngineMethod( SimXMLDocument, setObjectAttributes, void, ( const char* objectID ),, + "@brief Add the given SimObject's fields as attributes of the current Element on the stack.\n\n" + "@param objectID ID of SimObject being copied.") +{ + object->setObjectAttributes( objectID ); +} + +// ----------------------------------------------------------------------------- +// Create a new element and set to child of current stack element. +// New element is placed on top of element stack. +// ----------------------------------------------------------------------------- +void SimXMLDocument::pushNewElement(const char* rName) +{ + TiXmlElement cElement( rName ); + TiXmlElement* pStackTop = 0; + if(m_paNode.empty()) + { + pStackTop = dynamic_cast + (m_qDocument->InsertEndChild( cElement ) ); + } + else + { + const int iFinalElement = m_paNode.size() - 1; + TiXmlElement* pNode = m_paNode[iFinalElement]; + if(!pNode) + { + return; + } + pStackTop = dynamic_cast + (pNode->InsertEndChild( cElement )); + } + if(!pStackTop) + { + return; + } + m_paNode.push_back(pStackTop); +} + +DefineEngineMethod( SimXMLDocument, pushNewElement, void, ( const char* name ),, + "@brief Create a new element with the given name as child of current Element " + "and push it onto the Element stack making it the current one.\n\n" + + "@note This differs from addNewElement() in that it adds the new Element as a " + "child of the current Element (or a child of the document if no Element exists).\n\n" + + "@param name XML tag for the new Element.\n" + + "@see addNewElement()") +{ + object->pushNewElement( name ); +} + +// ----------------------------------------------------------------------------- +// Create a new element and set to child of current stack element. +// New element is placed on top of element stack. +// ----------------------------------------------------------------------------- +void SimXMLDocument::addNewElement(const char* rName) +{ + TiXmlElement cElement( rName ); + TiXmlElement* pStackTop = 0; + if(m_paNode.empty()) + { + pStackTop = dynamic_cast + (m_qDocument->InsertEndChild( cElement )); + if(!pStackTop) + { + return; + } + m_paNode.push_back(pStackTop); + return; + } + + const int iParentElement = m_paNode.size() - 2; + if(iParentElement < 0) + { + pStackTop = dynamic_cast + (m_qDocument->InsertEndChild( cElement )); + if(!pStackTop) + { + return; + } + m_paNode.push_back(pStackTop); + return; + } + else + { + TiXmlElement* pNode = m_paNode[iParentElement]; + if(!pNode) + { + return; + } + pStackTop = dynamic_cast + (pNode->InsertEndChild( cElement )); + if(!pStackTop) + { + return; + } + + // Overwrite top stack position. + const int iFinalElement = m_paNode.size() - 1; + m_paNode[iFinalElement] = pStackTop; + //pNode = pStackTop; + } +} + +DefineEngineMethod( SimXMLDocument, addNewElement, void, ( const char* name ),, + "@brief Create a new element with the given name as child of current Element's " + "parent and push it onto the Element stack making it the current one.\n\n" + + "@note This differs from pushNewElement() in that it adds the new Element to the " + "current Element's parent (or document if there is no parent Element). This makes " + "the new Element a sibling of the current one.\n\n" + + "@param name XML tag for the new Element.\n" + + "@see pushNewElement()") +{ + object->addNewElement( name ); +} + +// ----------------------------------------------------------------------------- +// Write XML document declaration. +// ----------------------------------------------------------------------------- +void SimXMLDocument::addHeader(void) +{ + TiXmlDeclaration cDeclaration("1.0", "utf-8", "yes"); + m_qDocument->InsertEndChild(cDeclaration); +} + +DefineEngineMethod( SimXMLDocument, addHeader, void, (),, + "@brief Add a XML header to a document.\n\n" + + "Sometimes called a declaration, you typically add a standard header to " + "the document before adding any elements. SimXMLDocument always produces " + "the following header:\n\n" + "\n\n" + + "@tsexample\n" + "// Create a new XML document with just a header and single element.\n" + "%x = new SimXMLDocument();\n" + "%x.addHeader();\n" + "%x.addNewElement(\"NewElement\");\n" + "%x.saveFile(\"test.xml\");\n\n" + "// Produces the following file:\n" + "// \n" + "// \n" + "@endtsexample\n\n") +{ + object->addHeader(); +} + +void SimXMLDocument::addComment(const char* comment) +{ + TiXmlComment cComment; + cComment.SetValue(comment); + m_qDocument->InsertEndChild(cComment); +} + +DefineEngineMethod(SimXMLDocument, addComment, void, ( const char* comment ),, + "@brief Add the given comment as a child of the document.\n\n" + "@param comment String containing the comment." + + "@tsexample\n" + "// Create a new XML document with a header, a comment and single element.\n" + "%x = new SimXMLDocument();\n" + "%x.addHeader();\n" + "%x.addComment(\"This is a test comment\");\n" + "%x.addNewElement(\"NewElement\");\n" + "%x.saveFile(\"test.xml\");\n\n" + "// Produces the following file:\n" + "// \n" + "// \n" + "// \n" + "@endtsexample\n\n" + + "@see readComment()") +{ + object->addComment( comment ); +} + +const char* SimXMLDocument::readComment( S32 index ) +{ + // Clear the current attribute pointer + m_CurrentAttribute = 0; + + // Push the first element found under the current element of the given name + if(!m_paNode.empty()) + { + const int iLastElement = m_paNode.size() - 1; + TiXmlElement* pNode = m_paNode[iLastElement]; + if(!pNode) + { + return ""; + } + TiXmlNode* node = pNode->FirstChild(); + for( S32 i = 0; i < index; i++ ) + { + if( !node ) + return ""; + + node = node->NextSiblingElement(); + } + + if( node ) + { + TiXmlComment* comment = node->ToComment(); + if( comment ) + return comment->Value(); + } + } + else + { + if(!m_qDocument) + { + return ""; + } + TiXmlNode* node = m_qDocument->FirstChild(); + for( S32 i = 0; i < index; i++ ) + { + if( !node ) + return ""; + + node = node->NextSibling(); + } + + if( node ) + { + TiXmlComment* comment = node->ToComment(); + if( comment ) + return comment->Value(); + } + } + return ""; +} + +DefineEngineMethod( SimXMLDocument, readComment, const char*, ( S32 index ),, + "Gives the comment at the specified index, if any.\n\n" + + "Unlike addComment() that only works at the document level, readComment() may read " + "comments from the document or any child Element. The current Element (or document " + "if no Elements have been pushed to the stack) is the parent for any comments, and the " + "provided index is the number of comments in to read back." + + "@param index Comment index number to query from the current Element stack\n\n" + "@return String containing the comment, or an empty string if no comment is found.\n\n" + + "@see addComment()") +{ + return object->readComment( index ); +} + +void SimXMLDocument::addText(const char* text) +{ + if(m_paNode.empty()) + return; + + const int iFinalElement = m_paNode.size() - 1; + TiXmlElement* pNode = m_paNode[iFinalElement]; + if(!pNode) + return; + + TiXmlText cText(text); + pNode->InsertEndChild( cText ); +} + +DefineEngineMethod( SimXMLDocument, addText, void, ( const char* text ),, + "@brief Add the given text as a child of current Element.\n\n" + + "Use getText() to retrieve any text from the current Element and removeText() " + "to clear any text.\n\n" + + "addText() and addData() may be used interchangeably." + + "@param text String containing the text.\n\n" + + "@tsexample\n" + "// Create a new XML document with a header and single element\n" + "// with some added text.\n" + "%x = new SimXMLDocument();\n" + "%x.addHeader();\n" + "%x.addNewElement(\"NewElement\");\n" + "%x.addText(\"Some text\");\n" + "%x.saveFile(\"test.xml\");\n\n" + "// Produces the following file:\n" + "// \n" + "// Some text\n" + "@endtsexample\n\n" + + "@see getText()\n" + "@see removeText()\n" + "@see addData()\n" + "@see getData()") +{ + object->addText( text ); +} + +const char* SimXMLDocument::getText() +{ + if(m_paNode.empty()) + return ""; + + const int iFinalElement = m_paNode.size() - 1; + TiXmlNode* pNode = m_paNode[iFinalElement]; + if(!pNode) + return ""; + + if(!pNode->FirstChild()) + return ""; + + TiXmlText* text = pNode->FirstChild()->ToText(); + if( !text ) + return ""; + + return text->Value(); +} + +DefineEngineMethod( SimXMLDocument, getText, const char*, (),, + "@brief Gets the text from the current Element.\n\n" + + "Use addText() to add text to the current Element and removeText() " + "to clear any text.\n\n" + + "getText() and getData() may be used interchangeably." + + "@return String containing the text in the current Element." + + "@tsexample\n" + "// Using the following test.xml file as an example:\n" + "// \n" + "// Some text\n\n" + + "// Load in the file\n" + "%x = new SimXMLDocument();\n" + "%x.loadFile(\"test.xml\");\n\n" + + "// Make the first Element the current one\n" + "%x.pushFirstChildElement(\"NewElement\");\n\n" + + "// Store the current Element's text ('Some text' in this example)\n" + "// into 'result'\n" + "%result = %x.getText();\n" + "echo( %result );\n" + "@endtsexample\n\n" + + "@see addText()\n" + "@see removeText()\n" + "@see addData()\n" + "@see getData()\n") +{ + const char* text = object->getText(); + if( !text ) + return ""; + + return text; +} + +void SimXMLDocument::removeText() +{ + if(m_paNode.empty()) + return; + + const int iFinalElement = m_paNode.size() - 1; + TiXmlElement* pNode = m_paNode[iFinalElement]; + if(!pNode) + return; + + if( !pNode->FirstChild() ) + return; + + TiXmlText* text = pNode->FirstChild()->ToText(); + if( !text ) + return; + + pNode->RemoveChild(text); +} + +DefineEngineMethod( SimXMLDocument, removeText, void, (),, + "@brief Remove any text on the current Element.\n\n" + + "Use getText() to retrieve any text from the current Element and addText() " + "to add text to the current Element. As getData() and addData() are equivalent " + "to getText() and addText(), removeText() will also remove any data from the " + "current Element.\n\n" + + "@see addText()\n" + "@see getText()\n" + "@see addData()\n" + "@see getData()\n") +{ + object->removeText(); +} + +void SimXMLDocument::addData(const char* text) +{ + if(m_paNode.empty()) + return; + + const int iFinalElement = m_paNode.size() - 1; + TiXmlElement* pNode = m_paNode[iFinalElement]; + if(!pNode) + return; + + TiXmlText cText(text); + pNode->InsertEndChild( cText ); +} + +DefineEngineMethod( SimXMLDocument, addData, void, ( const char* text ),, + "@brief Add the given text as a child of current Element.\n\n" + + "Use getData() to retrieve any text from the current Element.\n\n" + + "addData() and addText() may be used interchangeably. As there is no " + "difference between data and text, you may also use removeText() to clear " + "any data from the current Element.\n\n" + + "@param text String containing the text.\n\n" + + "@tsexample\n" + "// Create a new XML document with a header and single element\n" + "// with some added data.\n" + "%x = new SimXMLDocument();\n" + "%x.addHeader();\n" + "%x.addNewElement(\"NewElement\");\n" + "%x.addData(\"Some text\");\n" + "%x.saveFile(\"test.xml\");\n\n" + "// Produces the following file:\n" + "// \n" + "// Some text\n" + "@endtsexample\n\n" + + "@see getData()" + "@see addText()\n" + "@see getText()\n" + "@see removeText()\n") +{ + object->addData( text ); +} + +const char* SimXMLDocument::getData() +{ + if(m_paNode.empty()) + return ""; + + const int iFinalElement = m_paNode.size() - 1; + TiXmlNode* pNode = m_paNode[iFinalElement]; + if(!pNode) + return ""; + + if( !pNode->FirstChild() ) + return ""; + + TiXmlText* text = pNode->FirstChild()->ToText(); + if( !text ) + return ""; + + return text->Value(); +} + +DefineEngineMethod( SimXMLDocument, getData, const char*, (),, + "@brief Gets the text from the current Element.\n\n" + + "Use addData() to add text to the current Element.\n\n" + + "getData() and getText() may be used interchangeably. As there is no " + "difference between data and text, you may also use removeText() to clear " + "any data from the current Element.\n\n" + + "@return String containing the text in the current Element." + + "@tsexample\n" + "// Using the following test.xml file as an example:\n" + "// \n" + "// Some data\n\n" + + "// Load in the file\n" + "%x = new SimXMLDocument();\n" + "%x.loadFile(\"test.xml\");\n\n" + + "// Make the first Element the current one\n" + "%x.pushFirstChildElement(\"NewElement\");\n\n" + + "// Store the current Element's data ('Some data' in this example)\n" + "// into 'result'\n" + "%result = %x.getData();\n" + "echo( %result );\n" + "@endtsexample\n\n" + + "@see addData()\n" + "@see addText()\n" + "@see getText()\n" + "@see removeText()\n") +{ + const char* text = object->getData(); + if( !text ) + return ""; + + return text; +} + +////EOF///////////////////////////////////////////////////////////////////////// diff --git a/Engine/source/console/SimXMLDocument.h b/Engine/source/console/SimXMLDocument.h new file mode 100644 index 000000000..7154e68e3 --- /dev/null +++ b/Engine/source/console/SimXMLDocument.h @@ -0,0 +1,150 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Console implementation of STL map. +//----------------------------------------------------------------------------- + +#ifndef _XMLDOC_H_ +#define _XMLDOC_H_ + +#ifndef _SIMBASE_H_ +#include "console/simBase.h" +#endif + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif // _TVECTOR_H_ + + +class TiXmlDocument; +class TiXmlElement; +class TiXmlAttribute; + + +class SimXMLDocument: public SimObject +{ + // This typedef is required for tie ins with the script language. + // -------------------------------------------------------------------------- + protected: + typedef SimObject Parent; + // -------------------------------------------------------------------------- + + public: + SimXMLDocument(); + ~SimXMLDocument(); + + // These are overloaded functions from SimObject that we handle for + // tie in to the script language. The .cc file has more in depth + // comments on these. + //----------------------------------------------------------------------- + bool processArguments(S32 argc, const char** argv); + bool onAdd(); + void onRemove(); + static void initPersistFields(); + + // Set this to default state at construction. + void reset(void); + + // Read / write / parse XML. + bool loadFile(const char* rFileName); + bool saveFile(const char* rFileName); + S32 parse(const char* rText); + bool saveToString(String& str); + + // Clear XML document. + void clear(void); + + // Get error description if it exists. + const char* getErrorDesc(void) const; + // Clear previously set error. + void clearError(void); + + // Push first/last child element onto element stack. + bool pushFirstChildElement(const char* rName); + // Convert top stack element into sibling with given name. + bool nextSiblingElement(const char* rName); + // push child element at index onto stack. + bool pushChildElement(S32 index); + // Get element value + const char* elementValue(); + + // Pop last element off of stack. + void popElement(void); + + // Get attribute from top element on element stack. + const char* attribute(const char* rAttribute); + + // Does the attribute exist in the current element + bool attributeExists(const char* rAttribute); + + // Obtain the name of the current element's first or last attribute + const char* firstAttribute(); + const char* lastAttribute(); + + // Move through the current element's attributes to obtain their names + const char* nextAttribute(); + const char* prevAttribute(); + + // Set attribute of top element on element stack. + void setAttribute(const char* rAttribute, const char* rVal); + // Set attributes of a simObject on top element of the stack. + void setObjectAttributes(const char* objectID); + + // Remove attribute with given name from top element on stack. + void removeAttribute(const char* rAttribute); + + // Create a new element and push it onto stack as a new level. + void pushNewElement(const char* rName); + // Create a new element and push it onto stack on current level. + void addNewElement(const char* rName); + // Write XML declaration to current level. + void addHeader(void); + // Write a XML comment to the current level. + void addComment(const char* comment); + // Read a comment from the current level at the specified index. + const char* readComment( S32 index ); + // Write text to the current level. + void addText(const char* text); + // Retrieve text from the current level. + const char* getText(); + // Remove Text + void removeText(); + // Write data to the current level. + void addData(const char* text); + // Retrieve data from the current level. + const char* getData(); + + private: + // Document. + TiXmlDocument* m_qDocument; + // Stack of nodes. + Vector m_paNode; + // The current attribute + TiXmlAttribute* m_CurrentAttribute; + + public: + DECLARE_CONOBJECT(SimXMLDocument); +}; + +#endif // _XMLDOC_H_ +////EOF///////////////////////////////////////////////////////////////////////// diff --git a/Engine/source/console/arrayObject.cpp b/Engine/source/console/arrayObject.cpp new file mode 100644 index 000000000..180461cb1 --- /dev/null +++ b/Engine/source/console/arrayObject.cpp @@ -0,0 +1,929 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/arrayObject.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "math/mMathFn.h" + + +IMPLEMENT_CONOBJECT(ArrayObject); + +ConsoleDocClass( ArrayObject, + "@brief Data structure for storing indexed sequences of key/value pairs.\n\n" + + "This is a powerful array class providing PHP style arrays in TorqueScript.\n\n" + + "The following features are supported:
        \n" + "
      • array pointers: this allows you to move forwards or backwards through " + "the array as if it was a list, including jumping to the start or end.
      • \n" + "
      • sorting: the array can be sorted in either alphabetic or numeric mode, " + "on the key or the value, and in ascending or descending order
      • \n" + "
      • add/remove elements: elements can be pushed/popped from the start or " + "end of the array, or can be inserted/erased from anywhere in the middle
      • \n" + "
      • removal of duplicates: remove duplicate keys or duplicate values
      • \n" + "
      • searching: search the array and return the index of a particular key or " + "value
      • \n" + "
      • counting: count the number of instaces of a particular value or key in " + "the array, as well as the total number of elements
      • \n" + "
      • advanced features: array append, array crop and array duplicate
      • \n" + "
      \n\n" + + "Array element keys and values can be strings or numbers\n\n" + + "@ingroup Scripting" +); + + +bool ArrayObject::smIncreasing = false; +bool ArrayObject::smCaseSensitive = false; +const char* ArrayObject::smCompareFunction; + + +S32 QSORT_CALLBACK ArrayObject::_valueCompare( const void* a, const void* b ) +{ + ArrayObject::Element *ea = (ArrayObject::Element *) (a); + ArrayObject::Element *eb = (ArrayObject::Element *) (b); + S32 result = smCaseSensitive ? dStrnatcmp(ea->value, eb->value) : dStrnatcasecmp(ea->value, eb->value); + return ( smIncreasing ? -result : result ); +} + +S32 QSORT_CALLBACK ArrayObject::_valueNumCompare( const void* a, const void* b ) +{ + ArrayObject::Element *ea = (ArrayObject::Element *) (a); + ArrayObject::Element *eb = (ArrayObject::Element *) (b); + F32 aCol = dAtof(ea->value); + F32 bCol = dAtof(eb->value); + F32 result = aCol - bCol; + S32 res = result < 0 ? -1 : (result > 0 ? 1 : 0); + return ( smIncreasing ? res : -res ); +} + +S32 QSORT_CALLBACK ArrayObject::_keyCompare( const void* a, const void* b ) +{ + ArrayObject::Element *ea = (ArrayObject::Element *) (a); + ArrayObject::Element *eb = (ArrayObject::Element *) (b); + S32 result = smCaseSensitive ? dStrnatcmp(ea->key, eb->key) : dStrnatcasecmp(ea->key, eb->key); + return ( smIncreasing ? -result : result ); +} + +S32 QSORT_CALLBACK ArrayObject::_keyNumCompare( const void* a, const void* b ) +{ + ArrayObject::Element *ea = (ArrayObject::Element *) (a); + ArrayObject::Element *eb = (ArrayObject::Element *) (b); + const char* aCol = ea->key; + const char* bCol = eb->key; + F32 result = dAtof(aCol) - dAtof(bCol); + S32 res = result < 0 ? -1 : (result > 0 ? 1 : 0); + return ( smIncreasing ? res : -res ); +} + +S32 QSORT_CALLBACK ArrayObject::_keyFunctionCompare( const void* a, const void* b ) +{ + ArrayObject::Element* ea = ( ArrayObject::Element* )( a ); + ArrayObject::Element* eb = ( ArrayObject::Element* )( b ); + + const char* argv[ 3 ]; + argv[ 0 ] = smCompareFunction; + argv[ 1 ] = ea->key; + argv[ 2 ] = eb->key; + + S32 result = dAtoi( Con::execute( 3, argv ) ); + S32 res = result < 0 ? -1 : ( result > 0 ? 1 : 0 ); + return ( smIncreasing ? res : -res ); +} + +S32 QSORT_CALLBACK ArrayObject::_valueFunctionCompare( const void* a, const void* b ) +{ + ArrayObject::Element* ea = ( ArrayObject::Element* )( a ); + ArrayObject::Element* eb = ( ArrayObject::Element* )( b ); + + const char* argv[ 3 ]; + argv[ 0 ] = smCompareFunction; + argv[ 1 ] = ea->value; + argv[ 2 ] = eb->value; + + S32 result = dAtoi( Con::execute( 3, argv ) ); + S32 res = result < 0 ? -1 : ( result > 0 ? 1 : 0 ); + return ( smIncreasing ? res : -res ); +} + + +//----------------------------------------------------------------------------- + +ArrayObject::ArrayObject() + : mCurrentIndex( NULL ), + mCaseSensitive( false ) +{ +} + +//----------------------------------------------------------------------------- + +void ArrayObject::initPersistFields() +{ + addField( "caseSensitive", TypeBool, Offset( mCaseSensitive, ArrayObject ), + "Makes the keys and values case-sensitive.\n" + "By default, comparison of key and value strings will be case-insensitive." ); + + addProtectedField( "key", TypeCaseString, NULL, &_addKeyFromField, &emptyStringProtectedGetFn, + "Helper field which allows you to add new key['keyname'] = value pairs." ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +bool ArrayObject::_addKeyFromField( void *object, const char *index, const char *data ) +{ + static_cast( object )->push_back( index, data ); + return false; +} + +//----------------------------------------------------------------------------- + +S32 ArrayObject::getIndexFromValue( const String &value ) const +{ + S32 foundIndex = -1; + for ( S32 i = mCurrentIndex; i < mArray.size(); i++ ) + { + if ( isEqual( mArray[i].value, value ) ) + { + foundIndex = i; + break; + } + } + + if( foundIndex < 0 ) + { + for ( S32 i = 0; i < mCurrentIndex; i++ ) + { + if ( isEqual( mArray[i].value, value ) ) + { + foundIndex = i; + break; + } + } + } + + return foundIndex; +} + +//----------------------------------------------------------------------------- + +S32 ArrayObject::getIndexFromKey( const String &key ) const +{ + S32 foundIndex = -1; + for ( S32 i = mCurrentIndex; i < mArray.size(); i++ ) + { + if ( isEqual( mArray[i].key, key ) ) + { + foundIndex = i; + break; + } + } + + if( foundIndex < 0 ) + { + for ( S32 i = 0; i < mCurrentIndex; i++ ) + { + if ( isEqual( mArray[i].key, key ) ) + { + foundIndex = i; + break; + } + } + } + + return foundIndex; +} + +//----------------------------------------------------------------------------- + +S32 ArrayObject::getIndexFromKeyValue( const String &key, const String &value ) const +{ + S32 foundIndex = -1; + for ( S32 i = mCurrentIndex; i < mArray.size(); i++ ) + { + if ( isEqual( mArray[i].key, key ) && isEqual( mArray[i].value, value ) ) + { + foundIndex = i; + break; + } + } + + if ( foundIndex < 0 ) + { + for ( S32 i = 0; i < mCurrentIndex; i++ ) + { + if ( isEqual( mArray[i].key, key ) && isEqual( mArray[i].value, value ) ) + { + foundIndex = i; + break; + } + } + } + + return foundIndex; +} + +//----------------------------------------------------------------------------- + +const String& ArrayObject::getKeyFromIndex( S32 index ) const +{ + if ( index >= mArray.size() || index < 0 ) + return String::EmptyString; + + return mArray[index].key; +} + +//----------------------------------------------------------------------------- + +const String& ArrayObject::getValueFromIndex( S32 index ) const +{ + if( index >= mArray.size() || index < 0 ) + return String::EmptyString; + + return mArray[index].value; +} + +//----------------------------------------------------------------------------- + +S32 ArrayObject::countValue( const String &value ) const +{ + S32 count = 0; + for ( S32 i = 0; i < mArray.size(); i++ ) + { + if ( isEqual( mArray[i].value, value ) ) + count++; + } + + return count; +} + +//----------------------------------------------------------------------------- + +S32 ArrayObject::countKey( const String &key) const +{ + S32 count = 0; + for ( S32 i = 0; i < mArray.size(); i++ ) + { + if ( isEqual( mArray[i].key, key ) ) + count++; + } + + return count; +} + +//----------------------------------------------------------------------------- + +void ArrayObject::push_back( const String &key, const String &value ) +{ + mArray.push_back( Element( key, value ) ); +} + +//----------------------------------------------------------------------------- + +void ArrayObject::push_front( const String &key, const String &value ) +{ + mArray.push_front( Element( key, value ) ); +} + +//----------------------------------------------------------------------------- + +void ArrayObject::insert( const String &key, const String &value, S32 index ) +{ + index = mClamp( index, 0, mArray.size() ); + mArray.insert( index, Element( key, value ) ); +} + +//----------------------------------------------------------------------------- + +void ArrayObject::pop_back() +{ + if(mArray.size() <= 0) + return; + + mArray.pop_back(); + + if( mCurrentIndex >= mArray.size() ) + mCurrentIndex = mArray.size() - 1; +} + +//----------------------------------------------------------------------------- + +void ArrayObject::pop_front() +{ + if( mArray.size() <= 0 ) + return; + + mArray.pop_front(); + + if( mCurrentIndex >= mArray.size() ) + mCurrentIndex = mArray.size() - 1; +} + +//----------------------------------------------------------------------------- + +void ArrayObject::erase( S32 index ) +{ + if(index < 0 || index >= mArray.size()) + return; + + mArray.erase( index ); +} + +//----------------------------------------------------------------------------- + +void ArrayObject::empty() +{ + mArray.clear(); + mCurrentIndex = 0; +} + +//----------------------------------------------------------------------------- + +void ArrayObject::moveIndex(S32 prev, S32 index) +{ + if(index >= mArray.size()) + push_back(mArray[prev].key, mArray[prev].value); + else + mArray[index] = mArray[prev]; + mArray[prev].value = String::EmptyString; + mArray[prev].key = String::EmptyString; +} + +//----------------------------------------------------------------------------- + +void ArrayObject::uniqueValue() +{ + for(S32 i=0; icount(); i++) + { + const String& tempval = obj->getValueFromIndex(i); + const String& tempkey = obj->getKeyFromIndex(i); + push_back(tempkey, tempval); + } + mCurrentIndex = obj->getCurrent(); +} + +//----------------------------------------------------------------------------- + +void ArrayObject::crop( ArrayObject *obj ) +{ + for( S32 i = 0; i < obj->count(); i++ ) + { + const String &tempkey = obj->getKeyFromIndex( i ); + for( S32 j = 0; j < mArray.size(); j++ ) + { + if( isEqual( mArray[j].key, tempkey ) ) + { + mArray.erase( j ); + j--; + } + } + } +} + +//----------------------------------------------------------------------------- + +void ArrayObject::append(ArrayObject* obj) +{ + for(S32 i=0; icount(); i++) + { + const String& tempval = obj->getValueFromIndex(i); + const String& tempkey = obj->getKeyFromIndex(i); + push_back(tempkey, tempval); + } +} + +//----------------------------------------------------------------------------- + +void ArrayObject::setKey( const String &key, S32 index ) +{ + if ( index >= mArray.size() ) + return; + + mArray[index].key = key; +} + +//----------------------------------------------------------------------------- + +void ArrayObject::setValue( const String &value, S32 index ) +{ + if ( index >= mArray.size() ) + return; + + mArray[index].value = value; +} + +//----------------------------------------------------------------------------- + +void ArrayObject::sort( bool valsort, bool desc, bool numeric ) +{ + if ( mArray.size() <= 1 ) + return; + + smIncreasing = desc ? false : true; + smCaseSensitive = isCaseSensitive(); + + if ( numeric ) + { + if ( valsort ) + dQsort( (void *)&(mArray[0]), mArray.size(), sizeof(Element), _valueNumCompare) ; + else + dQsort( (void *)&(mArray[0]), mArray.size(), sizeof(Element), _keyNumCompare ); + } + else + { + if( valsort ) + dQsort( (void *)&(mArray[0]), mArray.size(), sizeof(Element), _valueCompare ); + else + dQsort( (void *)&(mArray[0]), mArray.size(), sizeof(Element), _keyCompare ); + } +} + +//----------------------------------------------------------------------------- + +void ArrayObject::sort( bool valsort, bool desc, const char* callbackFunctionName ) +{ + if( mArray.size() <= 1 ) + return; + + smIncreasing = desc ? false : true; + smCompareFunction = callbackFunctionName; + + if( valsort ) + dQsort( ( void* ) &( mArray[ 0 ] ), mArray.size(), sizeof( Element ), _valueFunctionCompare ) ; + else + dQsort( ( void* ) &( mArray[ 0 ] ), mArray.size(), sizeof( Element ), _keyFunctionCompare ); + + smCompareFunction = NULL; +} + +//----------------------------------------------------------------------------- + +S32 ArrayObject::moveFirst() +{ + mCurrentIndex = 0; + return mCurrentIndex; +} + +//----------------------------------------------------------------------------- + +S32 ArrayObject::moveLast() +{ + if ( mArray.empty() ) + mCurrentIndex = 0; + else + mCurrentIndex = mArray.size() - 1; + return mCurrentIndex; +} + +//----------------------------------------------------------------------------- + +S32 ArrayObject::moveNext() +{ + if ( mCurrentIndex >= mArray.size() - 1 ) + return -1; + + mCurrentIndex++; + + return mCurrentIndex; +} + +//----------------------------------------------------------------------------- + +S32 ArrayObject::movePrev() +{ + if ( mCurrentIndex <= 0 ) + return -1; + + mCurrentIndex--; + + return mCurrentIndex; +} + +//----------------------------------------------------------------------------- + +void ArrayObject::setCurrent( S32 idx ) +{ + if ( idx < 0 || idx >= mArray.size() ) + { + Con::errorf( "ArrayObject::setCurrent( %d ) is out of the array bounds!", idx ); + return; + } + + mCurrentIndex = idx; +} + +//----------------------------------------------------------------------------- + +void ArrayObject::echo() +{ + Con::printf( "ArrayObject Listing:" ); + Con::printf( "Index Key Value" ); + for ( U32 i = 0; i < mArray.size(); i++ ) + { + const String& key = mArray[i].key; + const String& val = mArray[i].value; + Con::printf( "%d [%s] => %s", i, key.c_str(), val.c_str() ); + } +} + +//============================================================================= +// Console Methods. +//============================================================================= + +DefineEngineMethod( ArrayObject, getIndexFromValue, S32, ( const char* value ),, + "Search the array from the current position for the element " + "@param value Array value to search for\n" + "@return Index of the first element found, or -1 if none\n" ) +{ + return object->getIndexFromValue( value ); +} + +DefineEngineMethod( ArrayObject, getIndexFromKey, S32, ( const char* key ),, + "Search the array from the current position for the key " + "@param value Array key to search for\n" + "@return Index of the first element found, or -1 if none\n" ) +{ + return object->getIndexFromKey( key ); +} + +DefineEngineMethod( ArrayObject, getValue, const char*, ( S32 index ),, + "Get the value of the array element at the submitted index.\n" + "@param index 0-based index of the array element to get\n" + "@return The value of the array element at the specified index, " + "or \"\" if the index is out of range\n" ) +{ + return object->getValueFromIndex( index ).c_str(); +} + +DefineEngineMethod( ArrayObject, getKey, const char*, ( S32 index ),, + "Get the key of the array element at the submitted index.\n" + "@param index 0-based index of the array element to get\n" + "@return The key associated with the array element at the " + "specified index, or \"\" if the index is out of range\n" ) +{ + return object->getKeyFromIndex( index ).c_str(); +} + +DefineEngineMethod( ArrayObject, setKey, void, ( const char* key, S32 index ),, + "Set the key at the given index.\n" + "@param key New key value\n" + "@param index 0-based index of the array element to update\n" ) +{ + object->setKey( key, index ); +} + +DefineEngineMethod( ArrayObject, setValue, void, ( const char* value, S32 index ),, + "Set the value at the given index.\n" + "@param value New array element value\n" + "@param index 0-based index of the array element to update\n" ) +{ + object->setValue( value, index ); +} + +DefineEngineMethod( ArrayObject, count, S32, (),, + "Get the number of elements in the array." ) +{ + return (S32)object->count(); +} + +DefineEngineMethod( ArrayObject, countValue, S32, ( const char* value ),, + "Get the number of times a particular value is found in the array.\n" + "@param value Array element value to count\n" ) +{ + return (S32)object->countValue( value ); +} + +DefineEngineMethod( ArrayObject, countKey, S32, ( const char* key ),, + "Get the number of times a particular key is found in the array.\n" + "@param key Key value to count\n" ) +{ + return (S32)object->countKey( key ); +} + +DefineEngineMethod( ArrayObject, add, void, ( const char* key, const char* value ), ( "" ), + "Adds a new element to the end of an array (same as push_back()).\n" + "@param key Key for the new element\n" + "@param value Value for the new element\n" ) +{ + object->push_back( key, value ); +} + +DefineEngineMethod( ArrayObject, push_back, void, ( const char* key, const char* value ), ( "" ), + "Adds a new element to the end of an array.\n" + "@param key Key for the new element\n" + "@param value Value for the new element\n" ) +{ + object->push_back( key, value ); +} + +DefineEngineMethod( ArrayObject, push_front, void, ( const char* key, const char* value ), ( "" ), + "Adds a new element to the front of an array" ) +{ + object->push_front( key, value ); +} + +DefineEngineMethod( ArrayObject, insert, void, ( const char* key, const char* value, S32 index ),, + "Adds a new element to a specified position in the array.\n" + "- @a index = 0 will insert an element at the start of the array (same as push_front())\n" + "- @a index = %array.count() will insert an element at the end of the array (same as push_back())\n\n" + "@param key Key for the new element\n" + "@param value Value for the new element\n" + "@param index 0-based index at which to insert the new element" ) +{ + object->insert( key, value, index ); +} + +DefineEngineMethod( ArrayObject, pop_back, void, (),, + "Removes the last element from the array" ) +{ + object->pop_back(); +} + +DefineEngineMethod( ArrayObject, pop_front, void, (),, + "Removes the first element from the array" ) +{ + object->pop_front(); +} + +DefineEngineMethod( ArrayObject, erase, void, ( S32 index ),, + "Removes an element at a specific position from the array.\n" + "@param index 0-based index of the element to remove\n" ) +{ + object->erase( index ); +} + +DefineEngineMethod( ArrayObject, empty, void, (),, + "Emptys all elements from an array" ) +{ + object->empty(); +} + +DefineEngineMethod( ArrayObject, uniqueValue, void, (),, + "Removes any elements that have duplicated values (leaving the first instance)" ) +{ + object->uniqueValue(); +} + +DefineEngineMethod( ArrayObject, uniqueKey, void, (),, + "Removes any elements that have duplicated keys (leaving the first instance)" ) +{ + object->uniqueKey(); +} + +DefineEngineMethod( ArrayObject, duplicate, bool, ( ArrayObject* target ),, + "Alters array into an exact duplicate of the target array.\n" + "@param target ArrayObject to duplicate\n" ) +{ + if ( target ) + { + object->duplicate( target ); + return true; + } + + return false; +} + +DefineEngineMethod( ArrayObject, crop, bool, ( ArrayObject* target ),, + "Removes elements with matching keys from array.\n" + "@param target ArrayObject containing keys to remove from this array\n" ) +{ + if ( target ) + { + object->crop( target ); + return true; + } + + return false; +} + +DefineEngineMethod( ArrayObject, append, bool, ( ArrayObject* target ),, + "Appends the target array to the array object.\n" + "@param target ArrayObject to append to the end of this array\n" ) +{ + if ( target ) + { + object->append( target ); + return true; + } + + return false; +} + +DefineEngineMethod( ArrayObject, sort, void, ( bool descending ), ( false ), + "Alpha sorts the array by value\n\n" + "@param descending [optional] True for descending sort, false for ascending sort\n" ) +{ + object->sort( true, descending, false ); +} + +DefineEngineMethod( ArrayObject, sorta, void, (),, + "Alpha sorts the array by value in ascending order" ) +{ + object->sort( true, false, false ); +} + +DefineEngineMethod( ArrayObject, sortd, void, (),, + "Alpha sorts the array by value in descending order" ) +{ + object->sort( true, true, false ); +} + +DefineEngineMethod( ArrayObject, sortk, void, ( bool descending ), ( false ), + "Alpha sorts the array by key\n\n" + "@param descending [optional] True for descending sort, false for ascending sort\n" ) +{ + object->sort( false, descending, false ); +} + +DefineEngineMethod( ArrayObject, sortka, void, (),, + "Alpha sorts the array by key in ascending order" ) +{ + object->sort( false, false, false ); +} + +DefineEngineMethod( ArrayObject, sortkd, void, (),, + "Alpha sorts the array by key in descending order" ) +{ + object->sort( false, true, false ); +} + +DefineEngineMethod( ArrayObject, sortn, void, ( bool descending ), ( false ), + "Numerically sorts the array by value\n\n" + "@param descending [optional] True for descending sort, false for ascending sort\n" ) +{ + object->sort( true, descending, true ); +} + +DefineEngineMethod( ArrayObject, sortna, void, (),, + "Numerically sorts the array by value in ascending order" ) +{ + object->sort( true, false, true ); +} + +DefineEngineMethod( ArrayObject, sortnd, void, (),, + "Numerically sorts the array by value in descending order" ) +{ + object->sort( true, true, true ); +} + +DefineEngineMethod( ArrayObject, sortnk, void, ( bool descending ), ( false ), + "Numerically sorts the array by key\n\n" + "@param descending [optional] True for descending sort, false for ascending sort\n" ) +{ + object->sort( false, descending, true ); +} + +DefineEngineMethod( ArrayObject, sortnka, void, (),, + "Numerical sorts the array by key in ascending order" ) +{ + object->sort( false, false, true ); +} + +DefineEngineMethod( ArrayObject, sortnkd, void, (),, + "Numerical sorts the array by key in descending order" ) +{ + object->sort( false, true, true ); +} + +DefineEngineMethod( ArrayObject, sortf, void, ( const char* functionName ),, + "Sorts the array by value in ascending order using the given callback function.\n" + "@param functionName Name of a function that takes two arguments A and B and returns -1 if A is less, 1 if B is less, and 0 if both are equal.\n\n" + "@tsexample\n" + "function mySortCallback(%a, %b)\n" + "{\n" + " return strcmp( %a.name, %b.name );\n" + "}\n\n" + "%array.sortf( \"mySortCallback\" );\n" + "@endtsexample\n" ) +{ + object->sort( true, false, functionName ); +} + +DefineEngineMethod( ArrayObject, sortfk, void, ( const char* functionName ),, + "Sorts the array by key in ascending order using the given callback function.\n" + "@param functionName Name of a function that takes two arguments A and B and returns -1 if A is less, 1 if B is less, and 0 if both are equal." + "@see sortf\n" ) +{ + object->sort( false, false, functionName ); +} + +DefineEngineMethod( ArrayObject, sortfd, void, ( const char* functionName ),, + "Sorts the array by value in descending order using the given callback function.\n" + "@param functionName Name of a function that takes two arguments A and B and returns -1 if A is less, 1 if B is less, and 0 if both are equal." + "@see sortf\n" ) +{ + object->sort( true, true, functionName ); +} + +DefineEngineMethod( ArrayObject, sortfkd, void, ( const char* functionName ),, + "Sorts the array by key in descending order using the given callback function.\n" + "@param functionName Name of a function that takes two arguments A and B and returns -1 if A is less, 1 if B is less, and 0 if both are equal." + "@see sortf\n" ) +{ + object->sort( false, true, functionName ); +} + +DefineEngineMethod( ArrayObject, moveFirst, S32, (),, + "Moves array pointer to start of array\n\n" + "@return Returns the new array pointer" ) +{ + return object->moveFirst(); +} + +DefineEngineMethod( ArrayObject, moveLast, S32, (),, + "Moves array pointer to end of array\n\n" + "@return Returns the new array pointer" ) +{ + return object->moveLast(); +} + +DefineEngineMethod( ArrayObject, moveNext, S32, (),, + "Moves array pointer to next position\n\n" + "@return Returns the new array pointer, or -1 if already at the end" ) +{ + return object->moveNext(); +} + +DefineEngineMethod( ArrayObject, movePrev, S32, (),, + "Moves array pointer to prev position\n\n" + "@return Returns the new array pointer, or -1 if already at the start" ) +{ + return object->movePrev(); +} + +DefineEngineMethod( ArrayObject, getCurrent, S32, (),, + "Gets the current pointer index" ) +{ + return object->getCurrent(); +} + +DefineEngineMethod( ArrayObject, setCurrent, void, ( S32 index ),, + "Sets the current pointer index.\n" + "@param index New 0-based pointer index\n" ) +{ + object->setCurrent( index ); +} + +DefineEngineMethod( ArrayObject, echo, void, (),, + "Echos the array contents to the console" ) +{ + object->echo(); +} diff --git a/Engine/source/console/arrayObject.h b/Engine/source/console/arrayObject.h new file mode 100644 index 000000000..d905467c0 --- /dev/null +++ b/Engine/source/console/arrayObject.h @@ -0,0 +1,232 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _ARRAYOBJECT_H_ +#define _ARRAYOBJECT_H_ + +#ifndef _SIMBASE_H_ +#include "console/simBase.h" +#endif + +// This class is based on original code by community +// member Daniel Neilsen: +// +// http://www.garagegames.com/community/resources/view/4711 + + +/// A data structure holding indexed sequences of key/value pairs for script use. +class ArrayObject : public SimObject +{ + typedef SimObject Parent; + +protected: + + struct Element + { + String key; + String value; + Element() { } + Element( const String& _key, const String& _value ) : key(_key), value(_value) { } + }; + + bool mCaseSensitive; + S32 mCurrentIndex; + + /// The array of key/value pairs. + Vector< Element > mArray; + + /// @name Sorting + /// @{ + + static bool smIncreasing; + static bool smCaseSensitive; + static const char* smCompareFunction; + + static S32 QSORT_CALLBACK _valueCompare( const void *a, const void *b ); + static S32 QSORT_CALLBACK _valueNumCompare( const void *a, const void *b ); + static S32 QSORT_CALLBACK _keyCompare( const void *a, const void *b ); + static S32 QSORT_CALLBACK _keyNumCompare( const void *a, const void *b ); + static S32 QSORT_CALLBACK _keyFunctionCompare( const void* a, const void* b ); + static S32 QSORT_CALLBACK _valueFunctionCompare( const void* a, const void* b ); + + /// @} + + static bool _addKeyFromField( void *object, const char *index, const char *data ); + +public: + + ArrayObject(); + + /// @name Data Query + /// @{ + + /// Returns true if string handling by the array is case-sensitive. + bool isCaseSensitive() const { return mCaseSensitive; } + + bool isEqual( const String &valA, const String &valB ) const + { + return valA.equal( valB, isCaseSensitive() ? String::Case : String::NoCase ); + } + + /// Searches the array for the first matching value from the + /// current array position. It will return -1 if no matching + /// index is found. + S32 getIndexFromValue( const String &value ) const; + + /// Searches the array for the first matching key from the current + /// array position. It will return -1 if no matching index found. + S32 getIndexFromKey( const String &key ) const; + + /// Returns the key for a given index. + /// Will return a null value for an invalid index + const String& getKeyFromIndex( S32 index ) const; + + /// Returns the value for a given index. + /// Will return a null value for an invalid index + const String& getValueFromIndex( S32 index ) const; + + /// + S32 getIndexFromKeyValue( const String &key, const String &value ) const; + + /// Counts the number of elements in the array + S32 count() const { return mArray.size(); } + + /// Counts the number of instances of a particular value in the array + S32 countValue( const String &value ) const; + + /// Counts the number of instances of a particular key in the array + S32 countKey( const String &key ) const; + + /// @} + + /// @name Data Alteration + /// @{ + + /// Adds a new array item to the end of the array + void push_back( const String &key, const String &value ); + + /// Adds a new array item to the front of the array + void push_front( const String &key, const String &value ); + + /// Adds a new array item to a particular index of the array + void insert( const String &key, const String &value, S32 index ); + + /// Removes an array item from the end of the array + void pop_back(); + + /// Removes an array item from the end of the array + void pop_front(); + + /// Removes an array item from a particular index of the array + void erase( S32 index ); + + /// Clears an array + void empty(); + + /// Moves a key and value from one index location to another. + void moveIndex( S32 prev, S32 index ); + + /// @} + + /// @name Complex Data Alteration + /// @{ + + /// Removes any duplicate values from the array + /// (keeps the first instance only) + void uniqueValue(); + + /// Removes any duplicate keys from the array + /// (keeps the first instance only) + void uniqueKey(); + + /// Makes this array an exact duplicate of another array + void duplicate( ArrayObject *obj ); + + /// Crops the keys that exists in the target array from our current array + void crop( ArrayObject *obj ); + + /// Appends the target array to our current array + void append( ArrayObject *obj ); + + /// Sets the key at the given index + void setKey( const String &key, S32 index ); + + /// Sets the key at the given index + void setValue( const String &value, S32 index ); + + /// This sorts the array. + /// @param valtest Determines whether sorting by value or key. + /// @param desc Determines if sorting ascending or descending. + /// @param numeric Determines if sorting alpha or numeric search. + void sort( bool valtest, bool desc, bool numeric ); + + /// This sorts the array using a script callback. + /// @param valtest Determines whether sorting by value or key. + /// @param desc Determines if sorting ascending or descending. + /// @param callbackFunctionName Name of the script function. + void sort( bool valtest, bool desc, const char* callbackFunctionName ); + + /// @} + + /// @name Pointer Manipulation + /// @{ + + /// Moves pointer to arrays first position + S32 moveFirst(); + + /// Moves pointer to arrays last position + S32 moveLast(); + + /// Moves pointer to arrays next position + /// If last position it returns -1 and no move occurs; + S32 moveNext(); + + /// Moves pointer to arrays prev position + /// If first position it returns -1 and no move occurs; + S32 movePrev(); + + /// Returns current pointer index. + S32 getCurrent() const { return mCurrentIndex; } + + /// + void setCurrent( S32 idx ); + + /// @} + + + /// @name Data Listing + /// @{ + + /// Echos the content of the array to the console. + void echo(); + + /// @} + + // SimObject + DECLARE_CONOBJECT( ArrayObject ); + DECLARE_CATEGORY( "Core" ); + DECLARE_DESCRIPTION( "An object storing an indexed sequence of key/value pairs." ); + + static void initPersistFields(); +}; + +#endif // _ARRAYOBJECT_H_ \ No newline at end of file diff --git a/Engine/source/console/ast.h b/Engine/source/console/ast.h new file mode 100644 index 000000000..71cfbeec0 --- /dev/null +++ b/Engine/source/console/ast.h @@ -0,0 +1,587 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _AST_H_ +#define _AST_H_ + +class ExprEvalState; +class Namespace; +class SimObject; +class SimGroup; + +/// Enable this #define if you are seeing the message "precompile size mismatch" in the console. +/// This will help track down which node type is causing the error. It could be +/// due to incorrect compiler optimization. +//#define DEBUG_AST_NODES + +enum TypeReq +{ + TypeReqNone, + TypeReqUInt, + TypeReqFloat, + TypeReqString +}; + +/// Representation of a node for the scripting language parser. +/// +/// When the scripting language is evaluated, it is turned from a string representation, +/// into a parse tree, thence into byte code, which is ultimately interpreted by the VM. +/// +/// This is the base class for the nodes in the parse tree. There are a great many subclasses, +/// each representing a different language construct. +struct StmtNode +{ + StmtNode *next; ///< Next entry in parse tree. + + StmtNode(); + virtual ~StmtNode() {} + + /// @name next Accessors + /// @{ + + /// + void append(StmtNode *next); + StmtNode *getNext() const { return next; } + + /// @} + + /// @name Debug Info + /// @{ + + StringTableEntry dbgFileName; ///< Name of file this node is associated with. + S32 dbgLineNumber; ///< Line number this node is associated with. +#ifdef DEBUG_AST_NODES + virtual String dbgStmtType() const = 0; +#endif + /// @} + + /// @name Breaking + /// @{ + + void addBreakCount(); + void addBreakLine(U32 ip); + /// @} + + /// @name Compilation + /// @{ + + virtual U32 precompileStmt(U32 loopCount) = 0; + virtual U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint) = 0; + virtual void setPackage(StringTableEntry packageName); + /// @} +}; + +/// Helper macro +#ifndef DEBUG_AST_NODES +# define DBG_STMT_TYPE(s) virtual String dbgStmtType() const { return String(#s); } +#else +# define DBG_STMT_TYPE(s) +#endif + +struct BreakStmtNode : StmtNode +{ + static BreakStmtNode *alloc( S32 lineNumber ); + + U32 precompileStmt(U32 loopCount); + U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint); + DBG_STMT_TYPE(BreakStmtNode); +}; + +struct ContinueStmtNode : StmtNode +{ + static ContinueStmtNode *alloc( S32 lineNumber ); + U32 precompileStmt(U32 loopCount); + U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint); + DBG_STMT_TYPE(ContinueStmtNode); +}; + +/// A mathematical expression. +struct ExprNode : StmtNode +{ + U32 precompileStmt(U32 loopCount); + U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint); + + virtual U32 precompile(TypeReq type) = 0; + virtual U32 compile(U32 *codeStream, U32 ip, TypeReq type) = 0; + virtual TypeReq getPreferredType() = 0; +}; + +struct ReturnStmtNode : StmtNode +{ + ExprNode *expr; + + static ReturnStmtNode *alloc( S32 lineNumber, ExprNode *expr ); + U32 precompileStmt(U32 loopCount); + U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint); + DBG_STMT_TYPE(ReturnStmtNode); +}; + +struct IfStmtNode : StmtNode +{ + ExprNode *testExpr; + StmtNode *ifBlock, *elseBlock; + U32 endifOffset; + U32 elseOffset; + bool integer; + bool propagate; + + static IfStmtNode *alloc( S32 lineNumber, ExprNode *testExpr, StmtNode *ifBlock, StmtNode *elseBlock, bool propagateThrough ); + void propagateSwitchExpr(ExprNode *left, bool string); + ExprNode *getSwitchOR(ExprNode *left, ExprNode *list, bool string); + U32 precompileStmt(U32 loopCount); + U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint); + DBG_STMT_TYPE(IfStmtNode); +}; + +struct LoopStmtNode : StmtNode +{ + ExprNode *testExpr; + ExprNode *initExpr; + ExprNode *endLoopExpr; + StmtNode *loopBlock; + bool isDoLoop; + U32 breakOffset; + U32 continueOffset; + U32 loopBlockStartOffset; + bool integer; + + static LoopStmtNode *alloc( S32 lineNumber, ExprNode *testExpr, ExprNode *initExpr, ExprNode *endLoopExpr, StmtNode *loopBlock, bool isDoLoop ); + U32 precompileStmt(U32 loopCount); + U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint); + DBG_STMT_TYPE(LoopStmtNode); +}; + +/// A "foreach" statement. +struct IterStmtNode : StmtNode +{ + /// Local variable name to use for the container element. + StringTableEntry varName; + + /// Expression evaluating to a SimSet object. + ExprNode* containerExpr; + + /// The statement body. + StmtNode* body; + + /// If true, this is a 'foreach$'. + bool isStringIter; + + /// Bytecode size of body statement. Set by precompileStmt. + U32 bodySize; + + static IterStmtNode* alloc( S32 lineNumber, StringTableEntry varName, ExprNode* containerExpr, StmtNode* body, bool isStringIter ); + + U32 precompileStmt( U32 loopCount ); + U32 compileStmt( U32* codeStream, U32 ip, U32 continuePoint, U32 breakPoint ); +}; + +/// A binary mathematical expression (ie, left op right). +struct BinaryExprNode : ExprNode +{ + S32 op; + ExprNode *left; + ExprNode *right; +}; + +struct FloatBinaryExprNode : BinaryExprNode +{ + static FloatBinaryExprNode *alloc( S32 lineNumber, S32 op, ExprNode *left, ExprNode *right ); + U32 precompile(TypeReq type); + U32 compile(U32 *codeStream, U32 ip, TypeReq type); + TypeReq getPreferredType(); + DBG_STMT_TYPE(FloatBinaryExprNode); +}; + +struct ConditionalExprNode : ExprNode +{ + ExprNode *testExpr; + ExprNode *trueExpr; + ExprNode *falseExpr; + bool integer; + static ConditionalExprNode *alloc( S32 lineNumber, ExprNode *testExpr, ExprNode *trueExpr, ExprNode *falseExpr ); + virtual U32 precompile(TypeReq type); + virtual U32 compile(U32 *codeStream, U32 ip, TypeReq type); + virtual TypeReq getPreferredType(); + DBG_STMT_TYPE(ConditionalExprNode); +}; + +struct IntBinaryExprNode : BinaryExprNode +{ + TypeReq subType; + U32 operand; + + static IntBinaryExprNode *alloc( S32 lineNumber, S32 op, ExprNode *left, ExprNode *right ); + + void getSubTypeOperand(); + U32 precompile(TypeReq type); + U32 compile(U32 *codeStream, U32 ip, TypeReq type); + TypeReq getPreferredType(); + DBG_STMT_TYPE(IntBinaryExprNode); +}; + +struct StreqExprNode : BinaryExprNode +{ + bool eq; + static StreqExprNode *alloc( S32 lineNumber, ExprNode *left, ExprNode *right, bool eq ); + U32 precompile(TypeReq type); + U32 compile(U32 *codeStream, U32 ip, TypeReq type); + TypeReq getPreferredType(); + DBG_STMT_TYPE(StreqExprNode); +}; + +struct StrcatExprNode : BinaryExprNode +{ + int appendChar; + static StrcatExprNode *alloc( S32 lineNumber, ExprNode *left, ExprNode *right, int appendChar ); + U32 precompile(TypeReq type); + U32 compile(U32 *codeStream, U32 ip, TypeReq type); + TypeReq getPreferredType(); + DBG_STMT_TYPE(StrcatExprNode); +}; + +struct CommaCatExprNode : BinaryExprNode +{ + static CommaCatExprNode *alloc( S32 lineNumber, ExprNode *left, ExprNode *right ); + + U32 precompile(TypeReq type); + U32 compile(U32 *codeStream, U32 ip, TypeReq type); + TypeReq getPreferredType(); + DBG_STMT_TYPE(CommaCatExprNode); +}; + +struct IntUnaryExprNode : ExprNode +{ + S32 op; + ExprNode *expr; + bool integer; + + static IntUnaryExprNode *alloc( S32 lineNumber, S32 op, ExprNode *expr ); + U32 precompile(TypeReq type); + U32 compile(U32 *codeStream, U32 ip, TypeReq type); + TypeReq getPreferredType(); + DBG_STMT_TYPE(IntUnaryExprNode); +}; + +struct FloatUnaryExprNode : ExprNode +{ + S32 op; + ExprNode *expr; + + static FloatUnaryExprNode *alloc( S32 lineNumber, S32 op, ExprNode *expr ); + U32 precompile(TypeReq type); + U32 compile(U32 *codeStream, U32 ip, TypeReq type); + TypeReq getPreferredType(); + DBG_STMT_TYPE(FloatUnaryExprNode); +}; + +struct VarNode : ExprNode +{ + StringTableEntry varName; + ExprNode *arrayIndex; + + static VarNode *alloc( S32 lineNumber, StringTableEntry varName, ExprNode *arrayIndex ); + U32 precompile(TypeReq type); + U32 compile(U32 *codeStream, U32 ip, TypeReq type); + TypeReq getPreferredType(); + DBG_STMT_TYPE(VarNode); +}; + +struct IntNode : ExprNode +{ + S32 value; + U32 index; // if it's converted to float/string + + static IntNode *alloc( S32 lineNumber, S32 value ); + U32 precompile(TypeReq type); + U32 compile(U32 *codeStream, U32 ip, TypeReq type); + TypeReq getPreferredType(); + DBG_STMT_TYPE(IntNode); +}; + +struct FloatNode : ExprNode +{ + F64 value; + U32 index; + + static FloatNode *alloc( S32 lineNumber, F64 value ); + U32 precompile(TypeReq type); + U32 compile(U32 *codeStream, U32 ip, TypeReq type); + TypeReq getPreferredType(); + DBG_STMT_TYPE(FloatNode); +}; + +struct StrConstNode : ExprNode +{ + char *str; + F64 fVal; + U32 index; + bool tag; + bool doc; // Specifies that this string is a documentation block. + + static StrConstNode *alloc( S32 lineNumber, char *str, bool tag, bool doc = false ); + U32 precompile(TypeReq type); + U32 compile(U32 *codeStream, U32 ip, TypeReq type); + TypeReq getPreferredType(); + DBG_STMT_TYPE(StrConstNode); +}; + +struct ConstantNode : ExprNode +{ + StringTableEntry value; + F64 fVal; + U32 index; + + static ConstantNode *alloc( S32 lineNumber, StringTableEntry value ); + U32 precompile(TypeReq type); + U32 compile(U32 *codeStream, U32 ip, TypeReq type); + TypeReq getPreferredType(); + DBG_STMT_TYPE(ConstantNode); +}; + +struct AssignExprNode : ExprNode +{ + StringTableEntry varName; + ExprNode *expr; + ExprNode *arrayIndex; + TypeReq subType; + + static AssignExprNode *alloc( S32 lineNumber, StringTableEntry varName, ExprNode *arrayIndex, ExprNode *expr ); + U32 precompile(TypeReq type); + U32 compile(U32 *codeStream, U32 ip, TypeReq type); + TypeReq getPreferredType(); + DBG_STMT_TYPE(AssignExprNode); +}; + +struct AssignDecl +{ + S32 lineNumber; + S32 token; + ExprNode *expr; + bool integer; +}; + +struct AssignOpExprNode : ExprNode +{ + StringTableEntry varName; + ExprNode *expr; + ExprNode *arrayIndex; + S32 op; + U32 operand; + TypeReq subType; + + static AssignOpExprNode *alloc( S32 lineNumber, StringTableEntry varName, ExprNode *arrayIndex, ExprNode *expr, S32 op ); + U32 precompile(TypeReq type); + U32 compile(U32 *codeStream, U32 ip, TypeReq type); + TypeReq getPreferredType(); + DBG_STMT_TYPE(AssignOpExprNode); +}; + +struct TTagSetStmtNode : StmtNode +{ + StringTableEntry tag; + ExprNode *valueExpr; + ExprNode *stringExpr; + + static TTagSetStmtNode *alloc( S32 lineNumber, StringTableEntry tag, ExprNode *valueExpr, ExprNode *stringExpr ); + U32 precompileStmt(U32 loopCount); + U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint); + DBG_STMT_TYPE(TTagSetStmtNode); +}; + +struct TTagDerefNode : ExprNode +{ + ExprNode *expr; + + static TTagDerefNode *alloc( S32 lineNumber, ExprNode *expr ); + U32 precompile(TypeReq type); + U32 compile(U32 *codeStream, U32 ip, TypeReq type); + TypeReq getPreferredType(); + DBG_STMT_TYPE(TTagDerefNode); +}; + +struct TTagExprNode : ExprNode +{ + StringTableEntry tag; + + static TTagExprNode *alloc( S32 lineNumber, StringTableEntry tag ); + U32 precompile(TypeReq type); + U32 compile(U32 *codeStream, U32 ip, TypeReq type); + TypeReq getPreferredType(); + DBG_STMT_TYPE(TTagExprNode); +}; + +struct FuncCallExprNode : ExprNode +{ + StringTableEntry funcName; + StringTableEntry nameSpace; + ExprNode *args; + U32 callType; + enum { + FunctionCall, + MethodCall, + ParentCall + }; + + static FuncCallExprNode *alloc( S32 lineNumber, StringTableEntry funcName, StringTableEntry nameSpace, ExprNode *args, bool dot ); + U32 precompile(TypeReq type); + U32 compile(U32 *codeStream, U32 ip, TypeReq type); + TypeReq getPreferredType(); + DBG_STMT_TYPE(FuncCallExprNode); +}; + +struct AssertCallExprNode : ExprNode +{ + ExprNode *testExpr; + const char *message; + U32 messageIndex; + + static AssertCallExprNode *alloc( S32 lineNumber, ExprNode *testExpr, const char *message ); + U32 precompile(TypeReq type); + U32 compile(U32 *codeStream, U32 ip, TypeReq type); + TypeReq getPreferredType(); + DBG_STMT_TYPE(AssertCallExprNode); +}; + +struct SlotDecl +{ + S32 lineNumber; + ExprNode *object; + StringTableEntry slotName; + ExprNode *array; +}; + +struct SlotAccessNode : ExprNode +{ + ExprNode *objectExpr, *arrayExpr; + StringTableEntry slotName; + + static SlotAccessNode *alloc( S32 lineNumber, ExprNode *objectExpr, ExprNode *arrayExpr, StringTableEntry slotName ); + U32 precompile(TypeReq type); + U32 compile(U32 *codeStream, U32 ip, TypeReq type); + TypeReq getPreferredType(); + DBG_STMT_TYPE(SlotAccessNode); +}; + +struct InternalSlotDecl +{ + S32 lineNumber; + ExprNode *object; + ExprNode *slotExpr; + bool recurse; +}; + +struct InternalSlotAccessNode : ExprNode +{ + ExprNode *objectExpr, *slotExpr; + bool recurse; + + static InternalSlotAccessNode *alloc( S32 lineNumber, ExprNode *objectExpr, ExprNode *slotExpr, bool recurse ); + U32 precompile(TypeReq type); + U32 compile(U32 *codeStream, U32 ip, TypeReq type); + TypeReq getPreferredType(); + DBG_STMT_TYPE(InternalSlotAccessNode); +}; + +struct SlotAssignNode : ExprNode +{ + ExprNode *objectExpr, *arrayExpr; + StringTableEntry slotName; + ExprNode *valueExpr; + U32 typeID; + + static SlotAssignNode *alloc( S32 lineNumber, ExprNode *objectExpr, ExprNode *arrayExpr, StringTableEntry slotName, ExprNode *valueExpr, U32 typeID = -1 ); + U32 precompile(TypeReq type); + U32 compile(U32 *codeStream, U32 ip, TypeReq type); + TypeReq getPreferredType(); + DBG_STMT_TYPE(SlotAssignNode); +}; + +struct SlotAssignOpNode : ExprNode +{ + ExprNode *objectExpr, *arrayExpr; + StringTableEntry slotName; + S32 op; + ExprNode *valueExpr; + U32 operand; + TypeReq subType; + + static SlotAssignOpNode *alloc( S32 lineNumber, ExprNode *objectExpr, StringTableEntry slotName, ExprNode *arrayExpr, S32 op, ExprNode *valueExpr ); + U32 precompile(TypeReq type); + U32 compile(U32 *codeStream, U32 ip, TypeReq type); + TypeReq getPreferredType(); + DBG_STMT_TYPE(SlotAssignOpNode); +}; + +struct ObjectDeclNode : ExprNode +{ + ExprNode *classNameExpr; + StringTableEntry parentObject; + ExprNode *objectNameExpr; + ExprNode *argList; + SlotAssignNode *slotDecls; + ObjectDeclNode *subObjects; + bool isDatablock; + U32 failOffset; + bool isClassNameInternal; + bool isSingleton; + + static ObjectDeclNode *alloc( S32 lineNumber, ExprNode *classNameExpr, ExprNode *objectNameExpr, ExprNode *argList, StringTableEntry parentObject, SlotAssignNode *slotDecls, ObjectDeclNode *subObjects, bool isDatablock, bool classNameInternal, bool isSingleton ); + U32 precompile(TypeReq type); + U32 precompileSubObject(bool); + U32 compile(U32 *codeStream, U32 ip, TypeReq type); + U32 compileSubObject(U32 *codeStream, U32 ip, bool); + TypeReq getPreferredType(); + DBG_STMT_TYPE(ObjectDeclNode); +}; + +struct ObjectBlockDecl +{ + SlotAssignNode *slots; + ObjectDeclNode *decls; +}; + +struct FunctionDeclStmtNode : StmtNode +{ + StringTableEntry fnName; + VarNode *args; + StmtNode *stmts; + StringTableEntry nameSpace; + StringTableEntry package; + U32 endOffset; + U32 argc; + + static FunctionDeclStmtNode *alloc( S32 lineNumber, StringTableEntry fnName, StringTableEntry nameSpace, VarNode *args, StmtNode *stmts ); + U32 precompileStmt(U32 loopCount); + U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint); + void setPackage(StringTableEntry packageName); + DBG_STMT_TYPE(FunctionDeclStmtNode); +}; + +extern StmtNode *gStatementList; +extern void createFunction(const char *fnName, VarNode *args, StmtNode *statements); +extern ExprEvalState gEvalState; +extern bool lookupFunction(const char *fnName, VarNode **args, StmtNode **statements); +typedef const char *(*cfunc)(S32 argc, char **argv); +extern bool lookupCFunction(const char *fnName, cfunc *f); + + +#endif diff --git a/Engine/source/console/astAlloc.cpp b/Engine/source/console/astAlloc.cpp new file mode 100644 index 000000000..842429822 --- /dev/null +++ b/Engine/source/console/astAlloc.cpp @@ -0,0 +1,429 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/console.h" +#include "console/compiler.h" +#include "console/consoleInternal.h" + +using namespace Compiler; + +/// @file +/// +/// TorqueScript AST node allocators. +/// +/// These static methods exist to allocate new AST node for the compiler. They +/// all allocate memory from the consoleAllocator for efficiency, and often take +/// arguments relating to the state of the nodes. They are called from gram.y +/// (really gram.c) as the lexer analyzes the script code. + +//------------------------------------------------------------ + +BreakStmtNode *BreakStmtNode::alloc( S32 lineNumber ) +{ + BreakStmtNode *ret = (BreakStmtNode *) consoleAlloc(sizeof(BreakStmtNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + return ret; +} + +ContinueStmtNode *ContinueStmtNode::alloc( S32 lineNumber ) +{ + ContinueStmtNode *ret = (ContinueStmtNode *) consoleAlloc(sizeof(ContinueStmtNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + return ret; +} + +ReturnStmtNode *ReturnStmtNode::alloc( S32 lineNumber, ExprNode *expr) +{ + ReturnStmtNode *ret = (ReturnStmtNode *) consoleAlloc(sizeof(ReturnStmtNode)); + constructInPlace(ret); + ret->expr = expr; + ret->dbgLineNumber = lineNumber; + + return ret; +} + +IfStmtNode *IfStmtNode::alloc( S32 lineNumber, ExprNode *testExpr, StmtNode *ifBlock, StmtNode *elseBlock, bool propagate ) +{ + IfStmtNode *ret = (IfStmtNode *) consoleAlloc(sizeof(IfStmtNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + + ret->testExpr = testExpr; + ret->ifBlock = ifBlock; + ret->elseBlock = elseBlock; + ret->propagate = propagate; + + return ret; +} + +LoopStmtNode *LoopStmtNode::alloc( S32 lineNumber, ExprNode *initExpr, ExprNode *testExpr, ExprNode *endLoopExpr, StmtNode *loopBlock, bool isDoLoop ) +{ + LoopStmtNode *ret = (LoopStmtNode *) consoleAlloc(sizeof(LoopStmtNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + ret->testExpr = testExpr; + ret->initExpr = initExpr; + ret->endLoopExpr = endLoopExpr; + ret->loopBlock = loopBlock; + ret->isDoLoop = isDoLoop; + + // Deal with setting some dummy constant nodes if we weren't provided with + // info... This allows us to play nice with missing parts of for(;;) for + // instance. + if(!ret->testExpr) ret->testExpr = IntNode::alloc( lineNumber, 1 ); + + return ret; +} + +IterStmtNode* IterStmtNode::alloc( S32 lineNumber, StringTableEntry varName, ExprNode* containerExpr, StmtNode* body, bool isStringIter ) +{ + IterStmtNode* ret = ( IterStmtNode* ) consoleAlloc( sizeof( IterStmtNode ) ); + constructInPlace( ret ); + + ret->dbgLineNumber = lineNumber; + ret->varName = varName; + ret->containerExpr = containerExpr; + ret->body = body; + ret->isStringIter = isStringIter; + + return ret; +} + +FloatBinaryExprNode *FloatBinaryExprNode::alloc( S32 lineNumber, S32 op, ExprNode *left, ExprNode *right ) +{ + FloatBinaryExprNode *ret = (FloatBinaryExprNode *) consoleAlloc(sizeof(FloatBinaryExprNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + + ret->op = op; + ret->left = left; + ret->right = right; + + return ret; +} + +IntBinaryExprNode *IntBinaryExprNode::alloc( S32 lineNumber, S32 op, ExprNode *left, ExprNode *right ) +{ + IntBinaryExprNode *ret = (IntBinaryExprNode *) consoleAlloc(sizeof(IntBinaryExprNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + + ret->op = op; + ret->left = left; + ret->right = right; + + return ret; +} + +StreqExprNode *StreqExprNode::alloc( S32 lineNumber, ExprNode *left, ExprNode *right, bool eq ) +{ + StreqExprNode *ret = (StreqExprNode *) consoleAlloc(sizeof(StreqExprNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + ret->left = left; + ret->right = right; + ret->eq = eq; + + return ret; +} + +StrcatExprNode *StrcatExprNode::alloc( S32 lineNumber, ExprNode *left, ExprNode *right, int appendChar ) +{ + StrcatExprNode *ret = (StrcatExprNode *) consoleAlloc(sizeof(StrcatExprNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + ret->left = left; + ret->right = right; + ret->appendChar = appendChar; + + return ret; +} + +CommaCatExprNode *CommaCatExprNode::alloc( S32 lineNumber, ExprNode *left, ExprNode *right ) +{ + CommaCatExprNode *ret = (CommaCatExprNode *) consoleAlloc(sizeof(CommaCatExprNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + ret->left = left; + ret->right = right; + + return ret; +} + +IntUnaryExprNode *IntUnaryExprNode::alloc( S32 lineNumber, S32 op, ExprNode *expr ) +{ + IntUnaryExprNode *ret = (IntUnaryExprNode *) consoleAlloc(sizeof(IntUnaryExprNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + ret->op = op; + ret->expr = expr; + return ret; +} + +FloatUnaryExprNode *FloatUnaryExprNode::alloc( S32 lineNumber, S32 op, ExprNode *expr ) +{ + FloatUnaryExprNode *ret = (FloatUnaryExprNode *) consoleAlloc(sizeof(FloatUnaryExprNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + ret->op = op; + ret->expr = expr; + return ret; +} + +VarNode *VarNode::alloc( S32 lineNumber, StringTableEntry varName, ExprNode *arrayIndex ) +{ + VarNode *ret = (VarNode *) consoleAlloc(sizeof(VarNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + ret->varName = varName; + ret->arrayIndex = arrayIndex; + return ret; +} + +IntNode *IntNode::alloc( S32 lineNumber, S32 value ) +{ + IntNode *ret = (IntNode *) consoleAlloc(sizeof(IntNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + ret->value = value; + return ret; +} + +ConditionalExprNode *ConditionalExprNode::alloc( S32 lineNumber, ExprNode *testExpr, ExprNode *trueExpr, ExprNode *falseExpr ) +{ + ConditionalExprNode *ret = (ConditionalExprNode *) consoleAlloc(sizeof(ConditionalExprNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + ret->testExpr = testExpr; + ret->trueExpr = trueExpr; + ret->falseExpr = falseExpr; + ret->integer = false; + return ret; +} + +FloatNode *FloatNode::alloc( S32 lineNumber, F64 value ) +{ + FloatNode *ret = (FloatNode *) consoleAlloc(sizeof(FloatNode)); + constructInPlace(ret); + + ret->dbgLineNumber = lineNumber; + ret->value = value; + return ret; +} + +StrConstNode *StrConstNode::alloc( S32 lineNumber, char *str, bool tag, bool doc ) +{ + StrConstNode *ret = (StrConstNode *) consoleAlloc(sizeof(StrConstNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + ret->str = (char *) consoleAlloc(dStrlen(str) + 1); + ret->tag = tag; + ret->doc = doc; + dStrcpy(ret->str, str); + + return ret; +} + +ConstantNode *ConstantNode::alloc( S32 lineNumber, StringTableEntry value ) +{ + ConstantNode *ret = (ConstantNode *) consoleAlloc(sizeof(ConstantNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + ret->value = value; + return ret; +} + +AssignExprNode *AssignExprNode::alloc( S32 lineNumber, StringTableEntry varName, ExprNode *arrayIndex, ExprNode *expr ) +{ + AssignExprNode *ret = (AssignExprNode *) consoleAlloc(sizeof(AssignExprNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + ret->varName = varName; + ret->expr = expr; + ret->arrayIndex = arrayIndex; + + return ret; +} + +AssignOpExprNode *AssignOpExprNode::alloc( S32 lineNumber, StringTableEntry varName, ExprNode *arrayIndex, ExprNode *expr, S32 op ) +{ + AssignOpExprNode *ret = (AssignOpExprNode *) consoleAlloc(sizeof(AssignOpExprNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + ret->varName = varName; + ret->expr = expr; + ret->arrayIndex = arrayIndex; + ret->op = op; + return ret; +} + +TTagSetStmtNode *TTagSetStmtNode::alloc( S32 lineNumber, StringTableEntry tag, ExprNode *valueExpr, ExprNode *stringExpr ) +{ + TTagSetStmtNode *ret = (TTagSetStmtNode *) consoleAlloc(sizeof(TTagSetStmtNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + ret->tag = tag; + ret->valueExpr = valueExpr; + ret->stringExpr = stringExpr; + return ret; +} + +TTagDerefNode *TTagDerefNode::alloc( S32 lineNumber, ExprNode *expr ) +{ + TTagDerefNode *ret = (TTagDerefNode *) consoleAlloc(sizeof(TTagDerefNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + ret->expr = expr; + return ret; +} + +TTagExprNode *TTagExprNode::alloc( S32 lineNumber, StringTableEntry tag ) +{ + TTagExprNode *ret = (TTagExprNode *) consoleAlloc(sizeof(TTagExprNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + ret->tag = tag; + return ret; +} + +FuncCallExprNode *FuncCallExprNode::alloc( S32 lineNumber, StringTableEntry funcName, StringTableEntry nameSpace, ExprNode *args, bool dot ) +{ + FuncCallExprNode *ret = (FuncCallExprNode *) consoleAlloc(sizeof(FuncCallExprNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + ret->funcName = funcName; + ret->nameSpace = nameSpace; + ret->args = args; + if(dot) + ret->callType = MethodCall; + else + { + if(nameSpace && !dStricmp(nameSpace, "Parent")) + ret->callType = ParentCall; + else + ret->callType = FunctionCall; + } + return ret; +} + +AssertCallExprNode *AssertCallExprNode::alloc( S32 lineNumber, ExprNode *testExpr, const char *message ) +{ + #ifdef TORQUE_ENABLE_SCRIPTASSERTS + + AssertCallExprNode *ret = (AssertCallExprNode *) consoleAlloc(sizeof(FuncCallExprNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + ret->testExpr = testExpr; + ret->message = message ? message : "TorqueScript assert!"; + return ret; + + #else + + return NULL; + + #endif +} + +SlotAccessNode *SlotAccessNode::alloc( S32 lineNumber, ExprNode *objectExpr, ExprNode *arrayExpr, StringTableEntry slotName ) +{ + SlotAccessNode *ret = (SlotAccessNode *) consoleAlloc(sizeof(SlotAccessNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + ret->objectExpr = objectExpr; + ret->arrayExpr = arrayExpr; + ret->slotName = slotName; + return ret; +} + +InternalSlotAccessNode *InternalSlotAccessNode::alloc( S32 lineNumber, ExprNode *objectExpr, ExprNode *slotExpr, bool recurse ) +{ + InternalSlotAccessNode *ret = (InternalSlotAccessNode *) consoleAlloc(sizeof(InternalSlotAccessNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + ret->objectExpr = objectExpr; + ret->slotExpr = slotExpr; + ret->recurse = recurse; + return ret; +} + +SlotAssignNode *SlotAssignNode::alloc( S32 lineNumber, ExprNode *objectExpr, ExprNode *arrayExpr, StringTableEntry slotName, ExprNode *valueExpr, U32 typeID /* = -1 */ ) +{ + SlotAssignNode *ret = (SlotAssignNode *) consoleAlloc(sizeof(SlotAssignNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + ret->objectExpr = objectExpr; + ret->arrayExpr = arrayExpr; + ret->slotName = slotName; + ret->valueExpr = valueExpr; + ret->typeID = typeID; + return ret; +} + +SlotAssignOpNode *SlotAssignOpNode::alloc( S32 lineNumber, ExprNode *objectExpr, StringTableEntry slotName, ExprNode *arrayExpr, S32 op, ExprNode *valueExpr ) +{ + SlotAssignOpNode *ret = (SlotAssignOpNode *) consoleAlloc(sizeof(SlotAssignOpNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + ret->objectExpr = objectExpr; + ret->arrayExpr = arrayExpr; + ret->slotName = slotName; + ret->op = op; + ret->valueExpr = valueExpr; + return ret; +} + +ObjectDeclNode *ObjectDeclNode::alloc( S32 lineNumber, ExprNode *classNameExpr, ExprNode *objectNameExpr, ExprNode *argList, StringTableEntry parentObject, SlotAssignNode *slotDecls, ObjectDeclNode *subObjects, bool isDatablock, bool classNameInternal, bool isSingleton ) +{ + ObjectDeclNode *ret = (ObjectDeclNode *) consoleAlloc(sizeof(ObjectDeclNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + ret->classNameExpr = classNameExpr; + ret->objectNameExpr = objectNameExpr; + ret->argList = argList; + ret->slotDecls = slotDecls; + ret->subObjects = subObjects; + ret->isDatablock = isDatablock; + ret->isClassNameInternal = classNameInternal; + ret->isSingleton = isSingleton; + ret->failOffset = 0; + if(parentObject) + ret->parentObject = parentObject; + else + ret->parentObject = StringTable->insert(""); + return ret; +} + +FunctionDeclStmtNode *FunctionDeclStmtNode::alloc( S32 lineNumber, StringTableEntry fnName, StringTableEntry nameSpace, VarNode *args, StmtNode *stmts ) +{ + FunctionDeclStmtNode *ret = (FunctionDeclStmtNode *) consoleAlloc(sizeof(FunctionDeclStmtNode)); + constructInPlace(ret); + ret->dbgLineNumber = lineNumber; + ret->fnName = fnName; + ret->args = args; + ret->stmts = stmts; + ret->nameSpace = nameSpace; + ret->package = NULL; + return ret; +} diff --git a/Engine/source/console/astNodes.cpp b/Engine/source/console/astNodes.cpp new file mode 100644 index 000000000..d055801a0 --- /dev/null +++ b/Engine/source/console/astNodes.cpp @@ -0,0 +1,1883 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/console.h" +#include "console/telnetDebugger.h" +#include "platform/event.h" + +#include "console/ast.h" +#include "core/tAlgorithm.h" + +#include "core/strings/findMatch.h" +#include "console/consoleInternal.h" +#include "core/stream/fileStream.h" +#include "console/compiler.h" + +#include "console/simBase.h" + +template< typename T > +struct Token +{ + T value; + S32 lineNumber; +}; +#include "console/cmdgram.h" + + +namespace Compiler +{ + U32 precompileBlock(StmtNode *block, U32 loopCount) + { + U32 sum = 0; + for(StmtNode *walk = block; walk; walk = walk->getNext()) + { + const U32 temp = walk->precompileStmt(loopCount); +#ifdef DEBUG_AST_NODES + if(temp > 1000) + Con::printf("suspect %s '%s' line %d", walk->dbgStmtType().c_str(), walk->dbgFileName, walk->dbgLineNumber); +#endif + sum += temp; + } + return sum; + } + + U32 compileBlock(StmtNode *block, U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint) + { + for(StmtNode *walk = block; walk; walk = walk->getNext()) + ip = walk->compileStmt(codeStream, ip, continuePoint, breakPoint); + return ip; + } +} + +using namespace Compiler; + +//----------------------------------------------------------------------------- + +void StmtNode::addBreakCount() +{ + CodeBlock::smBreakLineCount++; +} + +void StmtNode::addBreakLine(U32 ip) +{ + U32 line = CodeBlock::smBreakLineCount * 2; + CodeBlock::smBreakLineCount++; + + if(getBreakCodeBlock()->lineBreakPairs) + { + getBreakCodeBlock()->lineBreakPairs[line] = dbgLineNumber; + getBreakCodeBlock()->lineBreakPairs[line+1] = ip; + } +} + +//------------------------------------------------------------ + +StmtNode::StmtNode() +{ + next = NULL; + dbgFileName = CodeBlock::smCurrentParser->getCurrentFile(); +} + +void StmtNode::setPackage(StringTableEntry) +{ +} + +void StmtNode::append(StmtNode *next) +{ + StmtNode *walk = this; + while(walk->next) + walk = walk->next; + walk->next = next; +} + + +void FunctionDeclStmtNode::setPackage(StringTableEntry packageName) +{ + package = packageName; +} + +//------------------------------------------------------------ +// +// Console language compilers +// +//------------------------------------------------------------ + +static U32 conversionOp(TypeReq src, TypeReq dst) +{ + if(src == TypeReqString) + { + switch(dst) + { + case TypeReqUInt: + return OP_STR_TO_UINT; + case TypeReqFloat: + return OP_STR_TO_FLT; + case TypeReqNone: + return OP_STR_TO_NONE; + default: + break; + } + } + else if(src == TypeReqFloat) + { + switch(dst) + { + case TypeReqUInt: + return OP_FLT_TO_UINT; + case TypeReqString: + return OP_FLT_TO_STR; + case TypeReqNone: + return OP_FLT_TO_NONE; + default: + break; + } + } + else if(src == TypeReqUInt) + { + switch(dst) + { + case TypeReqFloat: + return OP_UINT_TO_FLT; + case TypeReqString: + return OP_UINT_TO_STR; + case TypeReqNone: + return OP_UINT_TO_NONE; + default: + break; + } + } + return OP_INVALID; +} + +//------------------------------------------------------------ + +U32 BreakStmtNode::precompileStmt(U32 loopCount) +{ + if(loopCount) + { + addBreakCount(); + return 2; + } + Con::warnf(ConsoleLogEntry::General, "%s (%d): break outside of loop... ignoring.", dbgFileName, dbgLineNumber); + return 0; +} + +U32 BreakStmtNode::compileStmt(U32 *codeStream, U32 ip, U32, U32 breakPoint) +{ + if(breakPoint) + { + addBreakLine(ip); + codeStream[ip++] = OP_JMP; + codeStream[ip++] = breakPoint; + } + return ip; +} + +//------------------------------------------------------------ + +U32 ContinueStmtNode::precompileStmt(U32 loopCount) +{ + if(loopCount) + { + addBreakCount(); + return 2; + } + Con::warnf(ConsoleLogEntry::General, "%s (%d): continue outside of loop... ignoring.", dbgFileName, dbgLineNumber); + return 0; +} + +U32 ContinueStmtNode::compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32) +{ + if(continuePoint) + { + addBreakLine(ip); + codeStream[ip++] = OP_JMP; + codeStream[ip++] = continuePoint; + } + return ip; +} + +//------------------------------------------------------------ + +U32 ExprNode::precompileStmt(U32) +{ + addBreakCount(); + return precompile(TypeReqNone); +} + +U32 ExprNode::compileStmt(U32 *codeStream, U32 ip, U32, U32) +{ + addBreakLine(ip); + return compile(codeStream, ip, TypeReqNone); +} + +//------------------------------------------------------------ + +U32 ReturnStmtNode::precompileStmt(U32) +{ + addBreakCount(); + return 1 + (expr ? expr->precompile(TypeReqString) : 0); +} + +U32 ReturnStmtNode::compileStmt(U32 *codeStream, U32 ip, U32, U32) +{ + addBreakLine(ip); + if(!expr) + codeStream[ip++] = OP_RETURN_VOID; + else + { + ip = expr->compile(codeStream, ip, TypeReqString); + codeStream[ip++] = OP_RETURN; + } + return ip; +} + +//------------------------------------------------------------ + +ExprNode *IfStmtNode::getSwitchOR(ExprNode *left, ExprNode *list, bool string) +{ + ExprNode *nextExpr = (ExprNode *) list->getNext(); + ExprNode *test; + if(string) + test = StreqExprNode::alloc( left->dbgLineNumber, left, list, true ); + else + test = IntBinaryExprNode::alloc( left->dbgLineNumber, opEQ, left, list ); + if(!nextExpr) + return test; + return IntBinaryExprNode::alloc( test->dbgLineNumber, opOR, test, getSwitchOR( left, nextExpr, string ) ); +} + +void IfStmtNode::propagateSwitchExpr(ExprNode *left, bool string) +{ + testExpr = getSwitchOR(left, testExpr, string); + if(propagate && elseBlock) + ((IfStmtNode *) elseBlock)->propagateSwitchExpr(left, string); +} + +U32 IfStmtNode::precompileStmt(U32 loopCount) +{ + U32 exprSize; + addBreakCount(); + + if(testExpr->getPreferredType() == TypeReqUInt) + { + exprSize = testExpr->precompile(TypeReqUInt); + integer = true; + } + else + { + exprSize = testExpr->precompile(TypeReqFloat); + integer = false; + } + // next is the JMPIFNOT or JMPIFFNOT - size of 2 + U32 ifSize = precompileBlock(ifBlock, loopCount); + if(!elseBlock) + endifOffset = ifSize + 2 + exprSize; + else + { + elseOffset = exprSize + 2 + ifSize + 2; + U32 elseSize = precompileBlock(elseBlock, loopCount); + endifOffset = elseOffset + elseSize; + } + return endifOffset; +} + +U32 IfStmtNode::compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint) +{ + U32 start = ip; + addBreakLine(ip); + + ip = testExpr->compile(codeStream, ip, integer ? TypeReqUInt : TypeReqFloat); + codeStream[ip++] = integer ? OP_JMPIFNOT : OP_JMPIFFNOT; + + if(elseBlock) + { + codeStream[ip++] = start + elseOffset; + ip = compileBlock(ifBlock, codeStream, ip, continuePoint, breakPoint); + codeStream[ip++] = OP_JMP; + codeStream[ip++] = start + endifOffset; + ip = compileBlock(elseBlock, codeStream, ip, continuePoint, breakPoint); + } + else + { + codeStream[ip++] = start + endifOffset; + ip = compileBlock(ifBlock, codeStream, ip, continuePoint, breakPoint); + } + return ip; +} + +//------------------------------------------------------------ + +U32 LoopStmtNode::precompileStmt(U32 loopCount) +{ + U32 initSize = 0; + addBreakCount(); + + if(initExpr) + initSize = initExpr->precompile(TypeReqNone); + + U32 testSize; + + if(testExpr->getPreferredType() == TypeReqUInt) + { + integer = true; + testSize = testExpr->precompile(TypeReqUInt); + } + else + { + integer = false; + testSize = testExpr->precompile(TypeReqFloat); + } + + U32 blockSize = precompileBlock(loopBlock, loopCount + 1); + + U32 endLoopSize = 0; + if(endLoopExpr) + endLoopSize = endLoopExpr->precompile(TypeReqNone); + + // if it's a for loop or a while loop it goes: + // initExpr + // testExpr + // OP_JMPIFNOT to break point + // loopStartPoint: + // loopBlock + // continuePoint: + // endLoopExpr + // testExpr + // OP_JMPIF loopStartPoint + // breakPoint: + + // otherwise if it's a do ... while() it goes: + // initExpr + // loopStartPoint: + // loopBlock + // continuePoint: + // endLoopExpr + // testExpr + // OP_JMPIF loopStartPoint + // breakPoint: + + if(!isDoLoop) + { + loopBlockStartOffset = initSize + testSize + 2; + continueOffset = loopBlockStartOffset + blockSize; + breakOffset = continueOffset + endLoopSize + testSize + 2; + } + else + { + loopBlockStartOffset = initSize; + continueOffset = initSize + blockSize; + breakOffset = continueOffset + endLoopSize + testSize + 2; + } + return breakOffset; +} + +U32 LoopStmtNode::compileStmt(U32 *codeStream, U32 ip, U32, U32) +{ + addBreakLine(ip); + U32 start = ip; + if(initExpr) + ip = initExpr->compile(codeStream, ip, TypeReqNone); + + if(!isDoLoop) + { + ip = testExpr->compile(codeStream, ip, integer ? TypeReqUInt : TypeReqFloat); + codeStream[ip++] = integer ? OP_JMPIFNOT : OP_JMPIFFNOT; + codeStream[ip++] = start + breakOffset; + } + + // Compile internals of loop. + ip = compileBlock(loopBlock, codeStream, ip, start + continueOffset, start + breakOffset); + + if(endLoopExpr) + ip = endLoopExpr->compile(codeStream, ip, TypeReqNone); + + ip = testExpr->compile(codeStream, ip, integer ? TypeReqUInt : TypeReqFloat); + + codeStream[ip++] = integer ? OP_JMPIF : OP_JMPIFF; + codeStream[ip++] = start + loopBlockStartOffset; + + return ip; +} + +//------------------------------------------------------------ + +U32 IterStmtNode::precompileStmt( U32 loopCount ) +{ + addBreakCount(); + + // Instruction sequence: + // + // containerExpr + // OP_ITER_BEGIN varName .fail + // .continue: + // OP_ITER .break + // body + // OP_JMP .continue + // .break: + // OP_ITER_END + // .fail: + + U32 exprSize = containerExpr->precompile( TypeReqString ); + bodySize = precompileBlock( body, loopCount + 1 ); + + return + exprSize + + 3 // OP_ITER_BEGIN + + 2 // OP_ITER + + bodySize + + 2 // OP_JMP + + 1 // OP_ITER_END + ; +} + +U32 IterStmtNode::compileStmt( U32* codeStream, U32 ip, U32 continuePoint, U32 breakPoint ) +{ + addBreakLine( ip ); + + const U32 startIp = ip; + const U32 iterBeginIp = containerExpr->compile( codeStream, startIp, TypeReqString ); + + const U32 continueIp = iterBeginIp + 3; + const U32 bodyIp = continueIp + 2; + const U32 jmpIp = bodyIp + bodySize; + const U32 breakIp = jmpIp + 2; + const U32 finalIp = breakIp + 1; + + codeStream[ iterBeginIp ] = isStringIter ? OP_ITER_BEGIN_STR : OP_ITER_BEGIN; + codeStream[ iterBeginIp + 1 ] = STEtoU32( varName, iterBeginIp + 1 ); + codeStream[ iterBeginIp + 2 ] = finalIp; + codeStream[ continueIp ] = OP_ITER; + codeStream[ continueIp + 1 ] = breakIp; + + compileBlock( body, codeStream, bodyIp, continueIp, breakIp ); + + codeStream[ jmpIp ] = OP_JMP; + codeStream[ jmpIp + 1 ] = continueIp; + codeStream[ breakIp ] = OP_ITER_END; + + return finalIp; +} + +//------------------------------------------------------------ + +U32 ConditionalExprNode::precompile(TypeReq type) +{ + // code is testExpr + // JMPIFNOT falseStart + // trueExpr + // JMP end + // falseExpr + U32 exprSize; + + if(testExpr->getPreferredType() == TypeReqUInt) + { + exprSize = testExpr->precompile(TypeReqUInt); + integer = true; + } + else + { + exprSize = testExpr->precompile(TypeReqFloat); + integer = false; + } + return exprSize + + trueExpr->precompile(type) + + falseExpr->precompile(type) + 4; +} + +U32 ConditionalExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) +{ + ip = testExpr->compile(codeStream, ip, integer ? TypeReqUInt : TypeReqFloat); + codeStream[ip++] = integer ? OP_JMPIFNOT : OP_JMPIFFNOT; + U32 jumpElseIp = ip++; + ip = trueExpr->compile(codeStream, ip, type); + codeStream[ip++] = OP_JMP; + U32 jumpEndIp = ip++; + codeStream[jumpElseIp] = ip; + ip = falseExpr->compile(codeStream, ip, type); + codeStream[jumpEndIp] = ip; + return ip; +} + +TypeReq ConditionalExprNode::getPreferredType() +{ + return trueExpr->getPreferredType(); +} + +//------------------------------------------------------------ + +U32 FloatBinaryExprNode::precompile(TypeReq type) +{ + U32 addSize = left->precompile(TypeReqFloat) + right->precompile(TypeReqFloat) + 1; + if(type != TypeReqFloat) + addSize++; + + return addSize; +} + +U32 FloatBinaryExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) +{ + ip = right->compile(codeStream, ip, TypeReqFloat); + ip = left->compile(codeStream, ip, TypeReqFloat); + U32 operand = OP_INVALID; + switch(op) + { + case '+': + operand = OP_ADD; + break; + case '-': + operand = OP_SUB; + break; + case '/': + operand = OP_DIV; + break; + case '*': + operand = OP_MUL; + break; + } + codeStream[ip++] = operand; + if(type != TypeReqFloat) + codeStream[ip++] =conversionOp(TypeReqFloat, type); + return ip; +} + +TypeReq FloatBinaryExprNode::getPreferredType() +{ + return TypeReqFloat; +} + +//------------------------------------------------------------ + +void IntBinaryExprNode::getSubTypeOperand() +{ + subType = TypeReqUInt; + switch(op) + { + case '^': + operand = OP_XOR; + break; + case '%': + operand = OP_MOD; + break; + case '&': + operand = OP_BITAND; + break; + case '|': + operand = OP_BITOR; + break; + case '<': + operand = OP_CMPLT; + subType = TypeReqFloat; + break; + case '>': + operand = OP_CMPGR; + subType = TypeReqFloat; + break; + case opGE: + operand = OP_CMPGE; + subType = TypeReqFloat; + break; + case opLE: + operand = OP_CMPLE; + subType = TypeReqFloat; + break; + case opEQ: + operand = OP_CMPEQ; + subType = TypeReqFloat; + break; + case opNE: + operand = OP_CMPNE; + subType = TypeReqFloat; + break; + case opOR: + operand = OP_OR; + break; + case opAND: + operand = OP_AND; + break; + case opSHR: + operand = OP_SHR; + break; + case opSHL: + operand = OP_SHL; + break; + } +} + +U32 IntBinaryExprNode::precompile(TypeReq type) +{ + getSubTypeOperand(); + U32 addSize = left->precompile(subType) + right->precompile(subType) + 1; + if(operand == OP_OR || operand == OP_AND) + addSize++; + + if(type != TypeReqUInt) + addSize++; + + return addSize; +} + +U32 IntBinaryExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) +{ + if(operand == OP_OR || operand == OP_AND) + { + ip = left->compile(codeStream, ip, subType); + codeStream[ip++] = operand == OP_OR ? OP_JMPIF_NP : OP_JMPIFNOT_NP; + U32 jmpIp = ip++; + ip = right->compile(codeStream, ip, subType); + codeStream[jmpIp] = ip; + } + else + { + ip = right->compile(codeStream, ip, subType); + ip = left->compile(codeStream, ip, subType); + codeStream[ip++] = operand; + } + if(type != TypeReqUInt) + codeStream[ip++] =conversionOp(TypeReqUInt, type); + return ip; +} + +TypeReq IntBinaryExprNode::getPreferredType() +{ + return TypeReqUInt; +} + +//------------------------------------------------------------ + +U32 StreqExprNode::precompile(TypeReq type) +{ + // eval str left + // OP_ADVANCE_STR_NUL + // eval str right + // OP_COMPARE_STR + // optional conversion + U32 addSize = left->precompile(TypeReqString) + right->precompile(TypeReqString) + 2; + if(!eq) + addSize ++; + if(type != TypeReqUInt) + addSize ++; + return addSize; +} + +U32 StreqExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) +{ + ip = left->compile(codeStream, ip, TypeReqString); + codeStream[ip++] = OP_ADVANCE_STR_NUL; + ip = right->compile(codeStream, ip, TypeReqString); + codeStream[ip++] = OP_COMPARE_STR; + if(!eq) + codeStream[ip++] = OP_NOT; + if(type != TypeReqUInt) + codeStream[ip++] = conversionOp(TypeReqUInt, type); + return ip; +} + +TypeReq StreqExprNode::getPreferredType() +{ + return TypeReqUInt; +} + +//------------------------------------------------------------ + +U32 StrcatExprNode::precompile(TypeReq type) +{ + U32 addSize = left->precompile(TypeReqString) + right->precompile(TypeReqString) + 2; + if(appendChar) + addSize++; + + if(type != TypeReqString) + addSize ++; + return addSize; +} + +U32 StrcatExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) +{ + ip = left->compile(codeStream, ip, TypeReqString); + if(!appendChar) + codeStream[ip++] = OP_ADVANCE_STR; + else + { + codeStream[ip++] = OP_ADVANCE_STR_APPENDCHAR; + codeStream[ip++] = appendChar; + } + ip = right->compile(codeStream, ip, TypeReqString); + codeStream[ip++] = OP_REWIND_STR; + if(type == TypeReqUInt) + codeStream[ip++] = OP_STR_TO_UINT; + else if(type == TypeReqFloat) + codeStream[ip++] = OP_STR_TO_FLT; + return ip; +} + +TypeReq StrcatExprNode::getPreferredType() +{ + return TypeReqString; +} + +//------------------------------------------------------------ + +U32 CommaCatExprNode::precompile(TypeReq type) +{ + U32 addSize = left->precompile(TypeReqString) + right->precompile(TypeReqString) + 2; + if(type != TypeReqString) + addSize ++; + return addSize; +} + +U32 CommaCatExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) +{ + ip = left->compile(codeStream, ip, TypeReqString); + codeStream[ip++] = OP_ADVANCE_STR_COMMA; + ip = right->compile(codeStream, ip, TypeReqString); + codeStream[ip++] = OP_REWIND_STR; + + // At this point the stack has the concatenated string. + + // But we're paranoid, so accept (but whine) if we get an oddity... + if(type == TypeReqUInt || type == TypeReqFloat) + Con::warnf(ConsoleLogEntry::General, "%s (%d): converting comma string to a number... probably wrong.", dbgFileName, dbgLineNumber); + if(type == TypeReqUInt) + codeStream[ip++] = OP_STR_TO_UINT; + else if(type == TypeReqFloat) + codeStream[ip++] = OP_STR_TO_FLT; + return ip; +} + +TypeReq CommaCatExprNode::getPreferredType() +{ + return TypeReqString; +} + +//------------------------------------------------------------ + +U32 IntUnaryExprNode::precompile(TypeReq type) +{ + integer = true; + TypeReq prefType = expr->getPreferredType(); + if(op == '!' && (prefType == TypeReqFloat || prefType == TypeReqString)) + integer = false; + + U32 exprSize = expr->precompile(integer ? TypeReqUInt : TypeReqFloat); + if(type != TypeReqUInt) + return exprSize + 2; + else + return exprSize + 1; +} + +U32 IntUnaryExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) +{ + ip = expr->compile(codeStream, ip, integer ? TypeReqUInt : TypeReqFloat); + if(op == '!') + codeStream[ip++] = integer ? OP_NOT : OP_NOTF; + else if(op == '~') + codeStream[ip++] = OP_ONESCOMPLEMENT; + if(type != TypeReqUInt) + codeStream[ip++] =conversionOp(TypeReqUInt, type); + return ip; +} + +TypeReq IntUnaryExprNode::getPreferredType() +{ + return TypeReqUInt; +} + +//------------------------------------------------------------ + +U32 FloatUnaryExprNode::precompile(TypeReq type) +{ + U32 exprSize = expr->precompile(TypeReqFloat); + if(type != TypeReqFloat) + return exprSize + 2; + else + return exprSize + 1; +} + +U32 FloatUnaryExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) +{ + ip = expr->compile(codeStream, ip, TypeReqFloat); + codeStream[ip++] = OP_NEG; + if(type != TypeReqFloat) + codeStream[ip++] =conversionOp(TypeReqFloat, type); + return ip; +} + +TypeReq FloatUnaryExprNode::getPreferredType() +{ + return TypeReqFloat; +} + +//------------------------------------------------------------ + +U32 VarNode::precompile(TypeReq type) +{ + // if this has an arrayIndex... + // OP_LOADIMMED_IDENT + // varName + // OP_ADVANCE_STR + // evaluate arrayIndex TypeReqString + // OP_REWIND_STR + // OP_SETCURVAR_ARRAY + // OP_LOADVAR (type) + + // else + // OP_SETCURVAR + // varName + // OP_LOADVAR (type) + if(type == TypeReqNone) + return 0; + + precompileIdent(varName); + return (arrayIndex ? arrayIndex->precompile(TypeReqString) + 6 : 3); +} + +U32 VarNode::compile(U32 *codeStream, U32 ip, TypeReq type) +{ + if(type == TypeReqNone) + return ip; + + codeStream[ip++] = arrayIndex ? OP_LOADIMMED_IDENT : OP_SETCURVAR; + codeStream[ip] = STEtoU32(varName, ip); + ip++; + if(arrayIndex) + { + codeStream[ip++] = OP_ADVANCE_STR; + ip = arrayIndex->compile(codeStream, ip, TypeReqString); + codeStream[ip++] = OP_REWIND_STR; + codeStream[ip++] = OP_SETCURVAR_ARRAY; + } + switch(type) + { + case TypeReqUInt: + codeStream[ip++] = OP_LOADVAR_UINT; + break; + case TypeReqFloat: + codeStream[ip++] = OP_LOADVAR_FLT; + break; + case TypeReqString: + codeStream[ip++] = OP_LOADVAR_STR; + break; + case TypeReqNone: + break; + } + return ip; +} + +TypeReq VarNode::getPreferredType() +{ + return TypeReqNone; // no preferred type +} + +//------------------------------------------------------------ + +U32 IntNode::precompile(TypeReq type) +{ + if(type == TypeReqNone) + return 0; + if(type == TypeReqString) + index = getCurrentStringTable()->addIntString(value); + else if(type == TypeReqFloat) + index = getCurrentFloatTable()->add(value); + return 2; +} + +U32 IntNode::compile(U32 *codeStream, U32 ip, TypeReq type) +{ + switch(type) + { + case TypeReqUInt: + codeStream[ip++] = OP_LOADIMMED_UINT; + codeStream[ip++] = value; + break; + case TypeReqString: + codeStream[ip++] = OP_LOADIMMED_STR; + codeStream[ip++] = index; + break; + case TypeReqFloat: + codeStream[ip++] = OP_LOADIMMED_FLT; + codeStream[ip++] = index; + break; + case TypeReqNone: + break; + } + return ip; +} + +TypeReq IntNode::getPreferredType() +{ + return TypeReqUInt; +} + +//------------------------------------------------------------ + +U32 FloatNode::precompile(TypeReq type) +{ + if(type == TypeReqNone) + return 0; + if(type == TypeReqString) + index = getCurrentStringTable()->addFloatString(value); + else if(type == TypeReqFloat) + index = getCurrentFloatTable()->add(value); + return 2; +} + +U32 FloatNode::compile(U32 *codeStream, U32 ip, TypeReq type) +{ + switch(type) + { + case TypeReqUInt: + codeStream[ip++] = OP_LOADIMMED_UINT; + codeStream[ip++] = U32(value); + break; + case TypeReqString: + codeStream[ip++] = OP_LOADIMMED_STR; + codeStream[ip++] = index; + break; + case TypeReqFloat: + codeStream[ip++] = OP_LOADIMMED_FLT; + codeStream[ip++] = index; + break; + case TypeReqNone: + break; + } + return ip; +} + +TypeReq FloatNode::getPreferredType() +{ + return TypeReqFloat; +} + +//------------------------------------------------------------ + +U32 StrConstNode::precompile(TypeReq type) +{ + // Early out for documentation block. + if( doc ) + { + index = getCurrentStringTable()->add(str, true, tag); + return 2; + } + + if(type == TypeReqString) + { + index = getCurrentStringTable()->add(str, true, tag); + return 2; + } + else if(type == TypeReqNone) + { + return 0; + } + + fVal = consoleStringToNumber(str, dbgFileName, dbgLineNumber); + if(type == TypeReqFloat) + index = getCurrentFloatTable()->add(fVal); + return 2; +} + +U32 StrConstNode::compile(U32 *codeStream, U32 ip, TypeReq type) +{ + // If this is a DOCBLOCK, then process w/ appropriate op... + if( doc ) + { + codeStream[ip++] = OP_DOCBLOCK_STR; + codeStream[ip++] = index; + return ip; + } + + // Otherwise, deal with it normally as a string literal case. + switch(type) + { + case TypeReqString: + codeStream[ip++] = tag ? OP_TAG_TO_STR : OP_LOADIMMED_STR; + codeStream[ip++] = index; + break; + case TypeReqUInt: + codeStream[ip++] = OP_LOADIMMED_UINT; + codeStream[ip++] = U32(fVal); + break; + case TypeReqFloat: + codeStream[ip++] = OP_LOADIMMED_FLT; + codeStream[ip++] = index; + break; + case TypeReqNone: + break; + } + return ip; +} + +TypeReq StrConstNode::getPreferredType() +{ + return TypeReqString; +} + +//------------------------------------------------------------ + +U32 ConstantNode::precompile(TypeReq type) +{ + if(type == TypeReqString) + { + precompileIdent(value); + return 2; + } + else if(type == TypeReqNone) + return 0; + + fVal = consoleStringToNumber(value, dbgFileName, dbgLineNumber); + if(type == TypeReqFloat) + index = getCurrentFloatTable()->add(fVal); + return 2; +} + +U32 ConstantNode::compile(U32 *codeStream, U32 ip, TypeReq type) +{ + switch(type) + { + case TypeReqString: + codeStream[ip++] = OP_LOADIMMED_IDENT; + codeStream[ip] = STEtoU32(value, ip); + ip++; + break; + case TypeReqUInt: + codeStream[ip++] = OP_LOADIMMED_UINT; + codeStream[ip++] = U32(fVal); + break; + case TypeReqFloat: + codeStream[ip++] = OP_LOADIMMED_FLT; + codeStream[ip++] = index; + break; + case TypeReqNone: + break; + } + return ip; +} + +TypeReq ConstantNode::getPreferredType() +{ + return TypeReqString; +} + +//------------------------------------------------------------ + +U32 AssignExprNode::precompile(TypeReq type) +{ + subType = expr->getPreferredType(); + if(subType == TypeReqNone) + subType = type; + if(subType == TypeReqNone) + subType = TypeReqString; + // if it's an array expr, the formula is: + // eval expr + // (push and pop if it's TypeReqString) OP_ADVANCE_STR + // OP_LOADIMMED_IDENT + // varName + // OP_ADVANCE_STR + // eval array + // OP_REWIND_STR + // OP_SETCURVAR_ARRAY_CREATE + // OP_TERMINATE_REWIND_STR + // OP_SAVEVAR + + //else + // eval expr + // OP_SETCURVAR_CREATE + // varname + // OP_SAVEVAR + const U32 addSize = (type != subType ? 1 : 0); + const U32 retSize = expr->precompile(subType); + +#ifdef DEBUG_AST_NODES + if(retSize > 1000) + Con::printf("Bad expr %s", expr->dbgStmtType().c_str()); +#endif + + precompileIdent(varName); + + return retSize + addSize + (arrayIndex ? arrayIndex->precompile(TypeReqString) + (subType == TypeReqString ? 8 : 6 ) : 3); +} + +U32 AssignExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) +{ + ip = expr->compile(codeStream, ip, subType); + if(arrayIndex) + { + if(subType == TypeReqString) + codeStream[ip++] = OP_ADVANCE_STR; + + codeStream[ip++] = OP_LOADIMMED_IDENT; + codeStream[ip] = STEtoU32(varName, ip); + ip++; + codeStream[ip++] = OP_ADVANCE_STR; + ip = arrayIndex->compile(codeStream, ip, TypeReqString); + codeStream[ip++] = OP_REWIND_STR; + codeStream[ip++] = OP_SETCURVAR_ARRAY_CREATE; + if(subType == TypeReqString) + codeStream[ip++] = OP_TERMINATE_REWIND_STR; + } + else + { + codeStream[ip++] = OP_SETCURVAR_CREATE; + codeStream[ip] = STEtoU32(varName, ip); + ip++; + } + switch(subType) + { + case TypeReqString: + codeStream[ip++] = OP_SAVEVAR_STR; + break; + case TypeReqUInt: + codeStream[ip++] = OP_SAVEVAR_UINT; + break; + case TypeReqFloat: + codeStream[ip++] = OP_SAVEVAR_FLT; + break; + case TypeReqNone: + break; + } + if(type != subType) + codeStream[ip++] = conversionOp(subType, type); + return ip; +} + +TypeReq AssignExprNode::getPreferredType() +{ + return expr->getPreferredType(); +} + +//------------------------------------------------------------ + +static void getAssignOpTypeOp(S32 op, TypeReq &type, U32 &operand) +{ + switch(op) + { + case '+': + type = TypeReqFloat; + operand = OP_ADD; + break; + case '-': + type = TypeReqFloat; + operand = OP_SUB; + break; + case '*': + type = TypeReqFloat; + operand = OP_MUL; + break; + case '/': + type = TypeReqFloat; + operand = OP_DIV; + break; + case '%': + type = TypeReqUInt; + operand = OP_MOD; + break; + case '&': + type = TypeReqUInt; + operand = OP_BITAND; + break; + case '^': + type = TypeReqUInt; + operand = OP_XOR; + break; + case '|': + type = TypeReqUInt; + operand = OP_BITOR; + break; + case opSHL: + type = TypeReqUInt; + operand = OP_SHL; + break; + case opSHR: + type = TypeReqUInt; + operand = OP_SHR; + break; + } +} + +U32 AssignOpExprNode::precompile(TypeReq type) +{ + // goes like this... + // eval expr as float or int + // if there's an arrayIndex + + // OP_LOADIMMED_IDENT + // varName + // OP_ADVANCE_STR + // eval arrayIndex stringwise + // OP_REWIND_STR + // OP_SETCURVAR_ARRAY_CREATE + + // else + // OP_SETCURVAR_CREATE + // varName + + // OP_LOADVAR_FLT or UINT + // operand + // OP_SAVEVAR_FLT or UINT + + // conversion OP if necessary. + getAssignOpTypeOp(op, subType, operand); + precompileIdent(varName); + U32 size = expr->precompile(subType); + if(type != subType) + size++; + if(!arrayIndex) + return size + 5; + else + { + size += arrayIndex->precompile(TypeReqString); + return size + 8; + } +} + +U32 AssignOpExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) +{ + ip = expr->compile(codeStream, ip, subType); + if(!arrayIndex) + { + codeStream[ip++] = OP_SETCURVAR_CREATE; + codeStream[ip] = STEtoU32(varName, ip); + ip++; + } + else + { + codeStream[ip++] = OP_LOADIMMED_IDENT; + codeStream[ip] = STEtoU32(varName, ip); + ip++; + codeStream[ip++] = OP_ADVANCE_STR; + ip = arrayIndex->compile(codeStream, ip, TypeReqString); + codeStream[ip++] = OP_REWIND_STR; + codeStream[ip++] = OP_SETCURVAR_ARRAY_CREATE; + } + codeStream[ip++] = (subType == TypeReqFloat) ? OP_LOADVAR_FLT : OP_LOADVAR_UINT; + codeStream[ip++] = operand; + codeStream[ip++] = (subType == TypeReqFloat) ? OP_SAVEVAR_FLT : OP_SAVEVAR_UINT; + if(subType != type) + codeStream[ip++] = conversionOp(subType, type); + return ip; +} + +TypeReq AssignOpExprNode::getPreferredType() +{ + getAssignOpTypeOp(op, subType, operand); + return subType; +} + +//------------------------------------------------------------ + +U32 TTagSetStmtNode::precompileStmt(U32 loopCount) +{ + TORQUE_UNUSED(loopCount); + return 0; +} + +U32 TTagSetStmtNode::compileStmt(U32*, U32 ip, U32, U32) +{ + return ip; +} + +//------------------------------------------------------------ + +U32 TTagDerefNode::precompile(TypeReq) +{ + return 0; +} + +U32 TTagDerefNode::compile(U32*, U32 ip, TypeReq) +{ + return ip; +} + +TypeReq TTagDerefNode::getPreferredType() +{ + return TypeReqNone; +} + +//------------------------------------------------------------ + +U32 TTagExprNode::precompile(TypeReq) +{ + return 0; +} + +U32 TTagExprNode::compile(U32*, U32 ip, TypeReq) +{ + return ip; +} + +TypeReq TTagExprNode::getPreferredType() +{ + return TypeReqNone; +} + +//------------------------------------------------------------ + +U32 FuncCallExprNode::precompile(TypeReq type) +{ + // OP_PUSH_FRAME + // arg OP_PUSH arg OP_PUSH arg OP_PUSH + // eval all the args, then call the function. + + // OP_CALLFUNC + // function + // namespace + // isDot + + U32 size = 0; + if(type != TypeReqString) + size++; + precompileIdent(funcName); + precompileIdent(nameSpace); + for(ExprNode *walk = args; walk; walk = (ExprNode *) walk->getNext()) + size += walk->precompile(TypeReqString) + 1; + return size + 5; +} + +U32 FuncCallExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) +{ + codeStream[ip++] = OP_PUSH_FRAME; + for(ExprNode *walk = args; walk; walk = (ExprNode *) walk->getNext()) + { + ip = walk->compile(codeStream, ip, TypeReqString); + codeStream[ip++] = OP_PUSH; + } + if(callType == MethodCall || callType == ParentCall) + codeStream[ip++] = OP_CALLFUNC; + else + codeStream[ip++] = OP_CALLFUNC_RESOLVE; + + codeStream[ip] = STEtoU32(funcName, ip); + ip++; + codeStream[ip] = STEtoU32(nameSpace, ip); + ip++; + codeStream[ip++] = callType; + if(type != TypeReqString) + codeStream[ip++] = conversionOp(TypeReqString, type); + return ip; +} + +TypeReq FuncCallExprNode::getPreferredType() +{ + return TypeReqString; +} + + +//------------------------------------------------------------ + +U32 AssertCallExprNode::precompile( TypeReq type ) +{ + #ifdef TORQUE_ENABLE_SCRIPTASSERTS + + messageIndex = getCurrentStringTable()->add( message, true, false ); + + U32 exprSize = testExpr->precompile(TypeReqUInt); + return exprSize + 2; + + #else + + return 0; + + #endif +} + +U32 AssertCallExprNode::compile( U32 *codeStream, U32 ip, TypeReq type ) +{ + #ifdef TORQUE_ENABLE_SCRIPTASSERTS + + ip = testExpr->compile( codeStream, ip, TypeReqUInt ); + codeStream[ip++] = OP_ASSERT; + codeStream[ip++] = messageIndex; + + #endif + + return ip; +} + +TypeReq AssertCallExprNode::getPreferredType() +{ + return TypeReqNone; +} + +//------------------------------------------------------------ + +U32 SlotAccessNode::precompile(TypeReq type) +{ + if(type == TypeReqNone) + return 0; + U32 size = 0; + precompileIdent(slotName); + if(arrayExpr) + { + // eval array + // OP_ADVANCE_STR + // evaluate object expression sub (OP_SETCURFIELD) + // OP_TERMINATE_REWIND_STR + // OP_SETCURFIELDARRAY + // total add of 4 + array precomp + size += 3 + arrayExpr->precompile(TypeReqString); + } + // eval object expression sub + 3 (op_setCurField + OP_SETCUROBJECT) + size += objectExpr->precompile(TypeReqString) + 3; + + // get field in desired type: + return size + 1; +} + +U32 SlotAccessNode::compile(U32 *codeStream, U32 ip, TypeReq type) +{ + if(type == TypeReqNone) + return ip; + + if(arrayExpr) + { + ip = arrayExpr->compile(codeStream, ip, TypeReqString); + codeStream[ip++] = OP_ADVANCE_STR; + } + ip = objectExpr->compile(codeStream, ip, TypeReqString); + codeStream[ip++] = OP_SETCUROBJECT; + + codeStream[ip++] = OP_SETCURFIELD; + + codeStream[ip] = STEtoU32(slotName, ip); + ip++; + + if(arrayExpr) + { + codeStream[ip++] = OP_TERMINATE_REWIND_STR; + codeStream[ip++] = OP_SETCURFIELD_ARRAY; + } + + switch(type) + { + case TypeReqUInt: + codeStream[ip++] = OP_LOADFIELD_UINT; + break; + case TypeReqFloat: + codeStream[ip++] = OP_LOADFIELD_FLT; + break; + case TypeReqString: + codeStream[ip++] = OP_LOADFIELD_STR; + break; + case TypeReqNone: + break; + } + return ip; +} + +TypeReq SlotAccessNode::getPreferredType() +{ + return TypeReqNone; +} + +//----------------------------------------------------------------------------- + +U32 InternalSlotAccessNode::precompile(TypeReq type) +{ + if(type == TypeReqNone) + return 0; + + U32 size = 3; + + // eval object expression sub + 3 (op_setCurField + OP_SETCUROBJECT) + size += objectExpr->precompile(TypeReqString); + size += slotExpr->precompile(TypeReqString); + if(type != TypeReqUInt) + size++; + + // get field in desired type: + return size; +} + +U32 InternalSlotAccessNode::compile(U32 *codeStream, U32 ip, TypeReq type) +{ + if(type == TypeReqNone) + return ip; + + ip = objectExpr->compile(codeStream, ip, TypeReqString); + codeStream[ip++] = OP_SETCUROBJECT; + + ip = slotExpr->compile(codeStream, ip, TypeReqString); + codeStream[ip++] = OP_SETCUROBJECT_INTERNAL; + codeStream[ip++] = recurse; + + if(type != TypeReqUInt) + codeStream[ip++] = conversionOp(TypeReqUInt, type); + return ip; +} + +TypeReq InternalSlotAccessNode::getPreferredType() +{ + return TypeReqUInt; +} + +//----------------------------------------------------------------------------- + +//------------------------------------------------------------ + +U32 SlotAssignNode::precompile(TypeReq type) +{ + // first eval the expression TypeReqString + + // if it's an array: + + // if OP_ADVANCE_STR 1 + // eval array + + // OP_ADVANCE_STR 1 + // evaluate object expr + // OP_SETCUROBJECT 1 + // OP_SETCURFIELD 1 + // fieldName 1 + // OP_TERMINATE_REWIND_STR 1 + + // OP_SETCURFIELDARRAY 1 + // OP_TERMINATE_REWIND_STR 1 + + // else + // OP_ADVANCE_STR + // evaluate object expr + // OP_SETCUROBJECT + // OP_SETCURFIELD + // fieldName + // OP_TERMINATE_REWIND_STR + + // OP_SAVEFIELD + // convert to return type if necessary. + + U32 size = 0; + if(type != TypeReqString) + size++; + + precompileIdent(slotName); + + size += valueExpr->precompile(TypeReqString); + + if(objectExpr) + size += objectExpr->precompile(TypeReqString) + 5; + else + size += 5; + + if(arrayExpr) + size += arrayExpr->precompile(TypeReqString) + 3; + + if(typeID != -1) + size += 2; + + return size + 1; +} + +U32 SlotAssignNode::compile(U32 *codeStream, U32 ip, TypeReq type) +{ + ip = valueExpr->compile(codeStream, ip, TypeReqString); + codeStream[ip++] = OP_ADVANCE_STR; + if(arrayExpr) + { + ip = arrayExpr->compile(codeStream, ip, TypeReqString); + codeStream[ip++] = OP_ADVANCE_STR; + } + if(objectExpr) + { + ip = objectExpr->compile(codeStream, ip, TypeReqString); + codeStream[ip++] = OP_SETCUROBJECT; + } + else + codeStream[ip++] = OP_SETCUROBJECT_NEW; + codeStream[ip++] = OP_SETCURFIELD; + codeStream[ip] = STEtoU32(slotName, ip); + ip++; + if(arrayExpr) + { + codeStream[ip++] = OP_TERMINATE_REWIND_STR; + codeStream[ip++] = OP_SETCURFIELD_ARRAY; + } + + codeStream[ip++] = OP_TERMINATE_REWIND_STR; + codeStream[ip++] = OP_SAVEFIELD_STR; + + if(typeID != -1) + { + codeStream[ip++] = OP_SETCURFIELD_TYPE; + codeStream[ip++] = typeID; + } + + if(type != TypeReqString) + codeStream[ip++] = conversionOp(TypeReqString, type); + return ip; +} + +TypeReq SlotAssignNode::getPreferredType() +{ + return TypeReqString; +} + +//------------------------------------------------------------ + +U32 SlotAssignOpNode::precompile(TypeReq type) +{ + // first eval the expression as its type + + // if it's an array: + // eval array + // OP_ADVANCE_STR + // evaluate object expr + // OP_SETCUROBJECT + // OP_SETCURFIELD + // fieldName + // OP_TERMINATE_REWIND_STR + // OP_SETCURFIELDARRAY + + // else + // evaluate object expr + // OP_SETCUROBJECT + // OP_SETCURFIELD + // fieldName + + // OP_LOADFIELD of appropriate type + // operand + // OP_SAVEFIELD of appropriate type + // convert to return type if necessary. + + getAssignOpTypeOp(op, subType, operand); + precompileIdent(slotName); + U32 size = valueExpr->precompile(subType); + if(type != subType) + size++; + if(arrayExpr) + return size + 9 + arrayExpr->precompile(TypeReqString) + objectExpr->precompile(TypeReqString); + else + return size + 6 + objectExpr->precompile(TypeReqString); +} + +U32 SlotAssignOpNode::compile(U32 *codeStream, U32 ip, TypeReq type) +{ + ip = valueExpr->compile(codeStream, ip, subType); + if(arrayExpr) + { + ip = arrayExpr->compile(codeStream, ip, TypeReqString); + codeStream[ip++] = OP_ADVANCE_STR; + } + ip = objectExpr->compile(codeStream, ip, TypeReqString); + codeStream[ip++] = OP_SETCUROBJECT; + codeStream[ip++] = OP_SETCURFIELD; + codeStream[ip] = STEtoU32(slotName, ip); + ip++; + if(arrayExpr) + { + codeStream[ip++] = OP_TERMINATE_REWIND_STR; + codeStream[ip++] = OP_SETCURFIELD_ARRAY; + } + codeStream[ip++] = (subType == TypeReqFloat) ? OP_LOADFIELD_FLT : OP_LOADFIELD_UINT; + codeStream[ip++] = operand; + codeStream[ip++] = (subType == TypeReqFloat) ? OP_SAVEFIELD_FLT : OP_SAVEFIELD_UINT; + if(subType != type) + codeStream[ip++] = conversionOp(subType, type); + return ip; +} + +TypeReq SlotAssignOpNode::getPreferredType() +{ + getAssignOpTypeOp(op, subType, operand); + return subType; +} + +//------------------------------------------------------------ + +U32 ObjectDeclNode::precompileSubObject(bool) +{ + // goes + + // OP_PUSHFRAME 1 + // name expr + // OP_PUSH 1 + // args... PUSH + // OP_CREATE_OBJECT 1 + // parentObject 1 + // isDatablock 1 + // internalName 1 + // isSingleton 1 + // lineNumber 1 + // fail point 1 + + // for each field, eval + // OP_ADD_OBJECT (to UINT[0]) 1 + // root? 1 + + // add all the sub objects. + // OP_END_OBJECT 1 + // root? 1 + // To fix the stack issue [7/9/2007 Black] + // OP_FINISH_OBJECT <-- fail point jumps to this opcode + + U32 argSize = 0; + precompileIdent(parentObject); + for(ExprNode *exprWalk = argList; exprWalk; exprWalk = (ExprNode *) exprWalk->getNext()) + argSize += exprWalk->precompile(TypeReqString) + 1; + argSize += classNameExpr->precompile(TypeReqString) + 1; + + U32 nameSize = objectNameExpr->precompile(TypeReqString) + 1; + + U32 slotSize = 0; + for(SlotAssignNode *slotWalk = slotDecls; slotWalk; slotWalk = (SlotAssignNode *) slotWalk->getNext()) + slotSize += slotWalk->precompile(TypeReqNone); + + // OP_ADD_OBJECT + U32 subObjSize = 0; + for(ObjectDeclNode *objectWalk = subObjects; objectWalk; objectWalk = (ObjectDeclNode *) objectWalk->getNext()) + subObjSize += objectWalk->precompileSubObject(false); + + failOffset = 12 + nameSize + argSize + slotSize + subObjSize; + // +1 because the failOffset should jump to OP_FINISH_OBJECT [7/9/2007 Black] + return failOffset + 1; +} + +U32 ObjectDeclNode::precompile(TypeReq type) +{ + // root object decl does: + + // push 0 onto the UINT stack OP_LOADIMMED_UINT + // precompiles the subObject(true) + // UINT stack now has object id + // type conv to type + + U32 ret = 2 + precompileSubObject(true); + if(type != TypeReqUInt) + return ret + 1; + return ret; +} + +U32 ObjectDeclNode::compileSubObject(U32 *codeStream, U32 ip, bool root) +{ + U32 start = ip; + codeStream[ip++] = OP_PUSH_FRAME; + ip = classNameExpr->compile(codeStream, ip, TypeReqString); + codeStream[ip++] = OP_PUSH; + + ip = objectNameExpr->compile(codeStream, ip, TypeReqString); + codeStream[ip++] = OP_PUSH; + for(ExprNode *exprWalk = argList; exprWalk; exprWalk = (ExprNode *) exprWalk->getNext()) + { + ip = exprWalk->compile(codeStream, ip, TypeReqString); + codeStream[ip++] = OP_PUSH; + } + codeStream[ip++] = OP_CREATE_OBJECT; + codeStream[ip] = STEtoU32(parentObject, ip); + ip++; + codeStream[ip++] = isDatablock; + codeStream[ip++] = isClassNameInternal; + codeStream[ip++] = isSingleton; + codeStream[ip++] = dbgLineNumber; + codeStream[ip++] = start + failOffset; + for(SlotAssignNode *slotWalk = slotDecls; slotWalk; slotWalk = (SlotAssignNode *) slotWalk->getNext()) + ip = slotWalk->compile(codeStream, ip, TypeReqNone); + codeStream[ip++] = OP_ADD_OBJECT; + codeStream[ip++] = root; + for(ObjectDeclNode *objectWalk = subObjects; objectWalk; objectWalk = (ObjectDeclNode *) objectWalk->getNext()) + ip = objectWalk->compileSubObject(codeStream, ip, false); + codeStream[ip++] = OP_END_OBJECT; + codeStream[ip++] = root || isDatablock; + // Added to fix the object creation issue [7/9/2007 Black] + codeStream[ip++] = OP_FINISH_OBJECT; + return ip; +} + +U32 ObjectDeclNode::compile(U32 *codeStream, U32 ip, TypeReq type) +{ + codeStream[ip++] = OP_LOADIMMED_UINT; + codeStream[ip++] = 0; + ip = compileSubObject(codeStream, ip, true); + if(type != TypeReqUInt) + codeStream[ip++] = conversionOp(TypeReqUInt, type); + return ip; +} +TypeReq ObjectDeclNode::getPreferredType() +{ + return TypeReqUInt; +} + +//------------------------------------------------------------ + +U32 FunctionDeclStmtNode::precompileStmt(U32) +{ + // OP_FUNC_DECL + // func name + // namespace + // package + // hasBody? + // func end ip + // argc + // ident array[argc] + // code + // OP_RETURN_VOID + setCurrentStringTable(&getFunctionStringTable()); + setCurrentFloatTable(&getFunctionFloatTable()); + + argc = 0; + for(VarNode *walk = args; walk; walk = (VarNode *)((StmtNode*)walk)->getNext()) + argc++; + + CodeBlock::smInFunction = true; + + precompileIdent(fnName); + precompileIdent(nameSpace); + precompileIdent(package); + + U32 subSize = precompileBlock(stmts, 0); + CodeBlock::smInFunction = false; + + addBreakCount(); + + setCurrentStringTable(&getGlobalStringTable()); + setCurrentFloatTable(&getGlobalFloatTable()); + + endOffset = argc + subSize + 8; + return endOffset; +} + +U32 FunctionDeclStmtNode::compileStmt(U32 *codeStream, U32 ip, U32, U32) +{ + U32 start = ip; + codeStream[ip++] = OP_FUNC_DECL; + codeStream[ip] = STEtoU32(fnName, ip); + ip++; + codeStream[ip] = STEtoU32(nameSpace, ip); + ip++; + codeStream[ip] = STEtoU32(package, ip); + ip++; + codeStream[ip++] = U32( bool(stmts != NULL) ? 1 : 0 ) + U32( dbgLineNumber << 1 ); + codeStream[ip++] = start + endOffset; + codeStream[ip++] = argc; + for(VarNode *walk = args; walk; walk = (VarNode *)((StmtNode*)walk)->getNext()) + { + codeStream[ip] = STEtoU32(walk->varName, ip); + ip++; + } + CodeBlock::smInFunction = true; + ip = compileBlock(stmts, codeStream, ip, 0, 0); + + // Add break so breakpoint can be set at closing brace or + // in empty function. + addBreakLine( ip ); + + CodeBlock::smInFunction = false; + codeStream[ip++] = OP_RETURN_VOID; + return ip; +} diff --git a/Engine/source/console/bison.bat b/Engine/source/console/bison.bat new file mode 100644 index 000000000..d48aaebbb --- /dev/null +++ b/Engine/source/console/bison.bat @@ -0,0 +1,6 @@ +echo Changing to %4 ... +cd %4 +echo Generating %2 and %3 with prefix %1. +..\..\bin\bison\bison.exe -o %2 %3 --defines -p %1 +echo Renaming %2 to %5 . +move /Y %2 %5 \ No newline at end of file diff --git a/Engine/source/console/bison.simple b/Engine/source/console/bison.simple new file mode 100644 index 000000000..0fb74ce71 --- /dev/null +++ b/Engine/source/console/bison.simple @@ -0,0 +1,686 @@ +/* -*-C-*- Note some compilers choke on comments on `#line' lines. */ +#line 3 "bison.simple" + +/* Skeleton output parser for bison, + Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* As a special exception, when this file is copied by Bison into a + Bison output file, you may use that output file without restriction. + This special exception was added by the Free Software Foundation + in version 1.24 of Bison. */ + +#ifndef alloca +#ifdef __GNUC__ +#define alloca __builtin_alloca +#else /* not GNU C. */ +#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi) +#include +#else /* not sparc */ +#if defined (MSDOS) && !defined (__TURBOC__) +#include +#else /* not MSDOS, or __TURBOC__ */ +#if defined(_AIX) +#include + #pragma alloca +#else /* not MSDOS, __TURBOC__, or _AIX */ +#ifdef __hpux +#ifdef __cplusplus +extern "C" { +void *alloca (unsigned int); +}; +#else /* not __cplusplus */ +void *alloca (); +#endif /* not __cplusplus */ +#endif /* __hpux */ +#endif /* not _AIX */ +#endif /* not MSDOS, or __TURBOC__ */ +#endif /* not sparc. */ +#endif /* not GNU C. */ +#endif /* alloca not defined. */ + +/* This is the parser code that is written into each bison parser + when the %semantic_parser declaration is not specified in the grammar. + It was written by Richard Stallman by simplifying the hairy parser + used when %semantic_parser is specified. */ + +/* Note: there must be only one dollar sign in this file. + It is replaced by the list of actions, each action + as one case of the switch. */ + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY -2 +#define YYEOF 0 +#define YYACCEPT return(0) +#define YYABORT return(1) +#define YYERROR goto yyerrlab1 +/* Like YYERROR except do call yyerror. + This remains here temporarily to ease the + transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ +#define YYFAIL goto yyerrlab +#define YYRECOVERING() (!!yyerrstatus) +#define YYBACKUP(token, value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { yychar = (token), yylval = (value); \ + yychar1 = YYTRANSLATE (yychar); \ + YYPOPSTACK; \ + goto yybackup; \ + } \ + else \ + { yyerror ("syntax error: cannot back up"); YYERROR; } \ +while (0) + +#define YYTERROR 1 +#define YYERRCODE 256 + +#ifndef YYPURE +#define YYLEX yylex() +#endif + +#ifdef YYPURE +#ifdef YYLSP_NEEDED +#ifdef YYLEX_PARAM +#define YYLEX yylex(&yylval, &yylloc, YYLEX_PARAM) +#else +#define YYLEX yylex(&yylval, &yylloc) +#endif +#else /* not YYLSP_NEEDED */ +#ifdef YYLEX_PARAM +#define YYLEX yylex(&yylval, YYLEX_PARAM) +#else +#define YYLEX yylex(&yylval) +#endif +#endif /* not YYLSP_NEEDED */ +#endif + +/* If nonreentrant, generate the variables here */ + +#ifndef YYPURE + +int yychar; /* the lookahead symbol */ +YYSTYPE yylval; /* the semantic value of the */ + /* lookahead symbol */ + +#ifdef YYLSP_NEEDED +YYLTYPE yylloc; /* location data for the lookahead */ + /* symbol */ +#endif + +int yynerrs; /* number of parse errors so far */ +#endif /* not YYPURE */ + +#if YYDEBUG != 0 +int yydebug; /* nonzero means print parse trace */ +/* Since this is uninitialized, it does not stop multiple parsers + from coexisting. */ +#endif + +/* YYINITDEPTH indicates the initial size of the parser's stacks */ + +#ifndef YYINITDEPTH +#define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH is the maximum size the stacks can grow to + (effective only if the built-in stack extension method is used). */ + +#if YYMAXDEPTH == 0 +#undef YYMAXDEPTH +#endif + +#ifndef YYMAXDEPTH +#define YYMAXDEPTH 10000 +#endif + +/* Prevent warning if -Wstrict-prototypes. */ +#ifdef __GNUC__ +int yyparse (void); +#endif + +#if __GNUC__ > 1 /* GNU C and GNU C++ define this. */ +#define __yy_memcpy(FROM,TO,COUNT) __builtin_memcpy(TO,FROM,COUNT) +#else /* not GNU C or C++ */ +#ifndef __cplusplus + +/* This is the most reliable way to avoid incompatibilities + in available built-in functions on various systems. */ +static void +__yy_memcpy (from, to, count) + char *from; + char *to; + int count; +{ + register char *f = from; + register char *t = to; + register int i = count; + + while (i-- > 0) + *t++ = *f++; +} + +#else /* __cplusplus */ + +/* This is the most reliable way to avoid incompatibilities + in available built-in functions on various systems. */ +static void +__yy_memcpy (char *from, char *to, int count) +{ + register char *f = from; + register char *t = to; + register int i = count; + + while (i-- > 0) + *t++ = *f++; +} + +#endif +#endif + +#line 192 "bison.simple" + +/* The user can define YYPARSE_PARAM as the name of an argument to be passed + into yyparse. The argument should have type void *. + It should actually point to an object. + Grammar actions can access the variable by casting it + to the proper pointer type. */ + +#ifdef YYPARSE_PARAM +#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM; +#else +#define YYPARSE_PARAM +#define YYPARSE_PARAM_DECL +#endif + +int +yyparse(YYPARSE_PARAM) + YYPARSE_PARAM_DECL +{ + register int yystate; + register int yyn; + register short *yyssp; + register YYSTYPE *yyvsp; + int yyerrstatus; /* number of tokens to shift before error messages enabled */ + int yychar1 = 0; /* lookahead token as an internal (translated) token number */ + + short yyssa[YYINITDEPTH]; /* the state stack */ + YYSTYPE yyvsa[YYINITDEPTH]; /* the semantic value stack */ + + short *yyss = yyssa; /* refer to the stacks thru separate pointers */ + YYSTYPE *yyvs = yyvsa; /* to allow yyoverflow to reallocate them elsewhere */ + +#ifdef YYLSP_NEEDED + YYLTYPE yylsa[YYINITDEPTH]; /* the location stack */ + YYLTYPE *yyls = yylsa; + YYLTYPE *yylsp; + +#define YYPOPSTACK (yyvsp--, yyssp--, yylsp--) +#else +#define YYPOPSTACK (yyvsp--, yyssp--) +#endif + + int yystacksize = YYINITDEPTH; + +#ifdef YYPURE + int yychar; + YYSTYPE yylval; + int yynerrs; +#ifdef YYLSP_NEEDED + YYLTYPE yylloc; +#endif +#endif + + YYSTYPE yyval; /* the variable used to return */ + /* semantic values from the action */ + /* routines */ + + int yylen; + +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Starting parse\n"); +#endif + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss - 1; + yyvsp = yyvs; +#ifdef YYLSP_NEEDED + yylsp = yyls; +#endif + +/* Push a new state, which is found in yystate . */ +/* In all cases, when you get here, the value and location stacks + have just been pushed. so pushing a state here evens the stacks. */ +yynewstate: + + *++yyssp = yystate; + + if (yyssp >= yyss + yystacksize - 1) + { + /* Give user a chance to reallocate the stack */ + /* Use copies of these so that the &'s don't force the real ones into memory. */ + YYSTYPE *yyvs1 = yyvs; + short *yyss1 = yyss; +#ifdef YYLSP_NEEDED + YYLTYPE *yyls1 = yyls; +#endif + + /* Get the current used size of the three stacks, in elements. */ + int size = yyssp - yyss + 1; + +#ifdef yyoverflow + /* Each stack pointer address is followed by the size of + the data in use in that stack, in bytes. */ +#ifdef YYLSP_NEEDED + /* This used to be a conditional around just the two extra args, + but that might be undefined if yyoverflow is a macro. */ + yyoverflow("parser stack overflow", + &yyss1, size * sizeof (*yyssp), + &yyvs1, size * sizeof (*yyvsp), + &yyls1, size * sizeof (*yylsp), + &yystacksize); +#else + yyoverflow("parser stack overflow", + &yyss1, size * sizeof (*yyssp), + &yyvs1, size * sizeof (*yyvsp), + &yystacksize); +#endif + + yyss = yyss1; yyvs = yyvs1; +#ifdef YYLSP_NEEDED + yyls = yyls1; +#endif +#else /* no yyoverflow */ + /* Extend the stack our own way. */ + if (yystacksize >= YYMAXDEPTH) + { + yyerror("parser stack overflow"); + return 2; + } + yystacksize *= 2; + if (yystacksize > YYMAXDEPTH) + yystacksize = YYMAXDEPTH; + yyss = (short *) alloca (yystacksize * sizeof (*yyssp)); + __yy_memcpy ((char *)yyss1, (char *)yyss, size * sizeof (*yyssp)); + yyvs = (YYSTYPE *) alloca (yystacksize * sizeof (*yyvsp)); + __yy_memcpy ((char *)yyvs1, (char *)yyvs, size * sizeof (*yyvsp)); +#ifdef YYLSP_NEEDED + yyls = (YYLTYPE *) alloca (yystacksize * sizeof (*yylsp)); + __yy_memcpy ((char *)yyls1, (char *)yyls, size * sizeof (*yylsp)); +#endif +#endif /* no yyoverflow */ + + yyssp = yyss + size - 1; + yyvsp = yyvs + size - 1; +#ifdef YYLSP_NEEDED + yylsp = yyls + size - 1; +#endif + +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Stack size increased to %d\n", yystacksize); +#endif + + if (yyssp >= yyss + yystacksize - 1) + YYABORT; + } + +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Entering state %d\n", yystate); +#endif + + goto yybackup; + yybackup: + +/* Do appropriate processing given the current state. */ +/* Read a lookahead token if we need one and don't already have one. */ +/* yyresume: */ + + /* First try to decide what to do without reference to lookahead token. */ + + yyn = yypact[yystate]; + if (yyn == YYFLAG) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* yychar is either YYEMPTY or YYEOF + or a valid token in external form. */ + + if (yychar == YYEMPTY) + { +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Reading a token: "); +#endif + yychar = YYLEX; + } + + /* Convert token to internal form (in yychar1) for indexing tables with */ + + if (yychar <= 0) /* This means end of input. */ + { + yychar1 = 0; + yychar = YYEOF; /* Don't call YYLEX any more */ + +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Now at end of input.\n"); +#endif + } + else + { + yychar1 = YYTRANSLATE(yychar); + +#if YYDEBUG != 0 + if (yydebug) + { + fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]); + /* Give the individual parser a way to print the precise meaning + of a token, for further debugging info. */ +#ifdef YYPRINT + YYPRINT (stderr, yychar, yylval); +#endif + fprintf (stderr, ")\n"); + } +#endif + } + + yyn += yychar1; + if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1) + goto yydefault; + + yyn = yytable[yyn]; + + /* yyn is what to do for this token type in this state. + Negative => reduce, -yyn is rule number. + Positive => shift, yyn is new state. + New state is final state => don't bother to shift, + just return success. + 0, or most negative number => error. */ + + if (yyn < 0) + { + if (yyn == YYFLAG) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + else if (yyn == 0) + goto yyerrlab; + + if (yyn == YYFINAL) + YYACCEPT; + + /* Shift the lookahead token. */ + +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]); +#endif + + /* Discard the token being shifted unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + *++yyvsp = yylval; +#ifdef YYLSP_NEEDED + *++yylsp = yylloc; +#endif + + /* count tokens shifted since error; after three, turn off error status. */ + if (yyerrstatus) yyerrstatus--; + + yystate = yyn; + goto yynewstate; + +/* Do the default action for the current state. */ +yydefault: + + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + +/* Do a reduction. yyn is the number of a rule to reduce with. */ +yyreduce: + yylen = yyr2[yyn]; + if (yylen > 0) + yyval = yyvsp[1-yylen]; /* implement default value of the action */ + +#if YYDEBUG != 0 + if (yydebug) + { + int i; + + fprintf (stderr, "Reducing via rule %d (line %d), ", + yyn, yyrline[yyn]); + + /* Print the symbols being reduced, and their result. */ + for (i = yyprhs[yyn]; yyrhs[i] > 0; i++) + fprintf (stderr, "%s ", yytname[yyrhs[i]]); + fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]); + } +#endif + +$ /* the action file gets copied in in place of this dollarsign */ +#line 487 "bison.simple" + + yyvsp -= yylen; + yyssp -= yylen; +#ifdef YYLSP_NEEDED + yylsp -= yylen; +#endif + +#if YYDEBUG != 0 + if (yydebug) + { + short *ssp1 = yyss - 1; + fprintf (stderr, "state stack now"); + while (ssp1 != yyssp) + fprintf (stderr, " %d", *++ssp1); + fprintf (stderr, "\n"); + } +#endif + + *++yyvsp = yyval; + +#ifdef YYLSP_NEEDED + yylsp++; + if (yylen == 0) + { + yylsp->first_line = yylloc.first_line; + yylsp->first_column = yylloc.first_column; + yylsp->last_line = (yylsp-1)->last_line; + yylsp->last_column = (yylsp-1)->last_column; + yylsp->text = 0; + } + else + { + yylsp->last_line = (yylsp+yylen-1)->last_line; + yylsp->last_column = (yylsp+yylen-1)->last_column; + } +#endif + + /* Now "shift" the result of the reduction. + Determine what state that goes to, + based on the state we popped back to + and the rule number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTBASE] + *yyssp; + if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTBASE]; + + goto yynewstate; + +yyerrlab: /* here on detecting error */ + + if (! yyerrstatus) + /* If not already recovering from an error, report this error. */ + { + ++yynerrs; + +#ifdef YYERROR_VERBOSE + yyn = yypact[yystate]; + + if (yyn > YYFLAG && yyn < YYLAST) + { + int size = 0; + char *msg; + int x, count; + + count = 0; + /* Start X at -yyn if nec to avoid negative indexes in yycheck. */ + for (x = (yyn < 0 ? -yyn : 0); + x < (sizeof(yytname) / sizeof(char *)); x++) + if (yycheck[x + yyn] == x) + size += strlen(yytname[x]) + 15, count++; + msg = (char *) malloc(size + 15); + if (msg != 0) + { + strcpy(msg, "parse error"); + + if (count < 5) + { + count = 0; + for (x = (yyn < 0 ? -yyn : 0); + x < (sizeof(yytname) / sizeof(char *)); x++) + if (yycheck[x + yyn] == x) + { + strcat(msg, count == 0 ? ", expecting `" : " or `"); + strcat(msg, yytname[x]); + strcat(msg, "'"); + count++; + } + } + yyerror(msg); + free(msg); + } + else + yyerror ("parse error; also virtual memory exceeded"); + } + else +#endif /* YYERROR_VERBOSE */ + yyerror("parse error"); + } + + goto yyerrlab1; +yyerrlab1: /* here on error raised explicitly by an action */ + + if (yyerrstatus == 3) + { + /* if just tried and failed to reuse lookahead token after an error, discard it. */ + + /* return failure if at end of input */ + if (yychar == YYEOF) + YYABORT; + +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]); +#endif + + yychar = YYEMPTY; + } + + /* Else will try to reuse lookahead token + after shifting the error token. */ + + yyerrstatus = 3; /* Each real token shifted decrements this */ + + goto yyerrhandle; + +yyerrdefault: /* current state does not do anything special for the error token. */ + +#if 0 + /* This is wrong; only states that explicitly want error tokens + should shift them. */ + yyn = yydefact[yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/ + if (yyn) goto yydefault; +#endif + +yyerrpop: /* pop the current state because it cannot handle the error token */ + + if (yyssp == yyss) YYABORT; + yyvsp--; + yystate = *--yyssp; +#ifdef YYLSP_NEEDED + yylsp--; +#endif + +#if YYDEBUG != 0 + if (yydebug) + { + short *ssp1 = yyss - 1; + fprintf (stderr, "Error: state stack now"); + while (ssp1 != yyssp) + fprintf (stderr, " %d", *++ssp1); + fprintf (stderr, "\n"); + } +#endif + +yyerrhandle: + + yyn = yypact[yystate]; + if (yyn == YYFLAG) + goto yyerrdefault; + + yyn += YYTERROR; + if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR) + goto yyerrdefault; + + yyn = yytable[yyn]; + if (yyn < 0) + { + if (yyn == YYFLAG) + goto yyerrpop; + yyn = -yyn; + goto yyreduce; + } + else if (yyn == 0) + goto yyerrpop; + + if (yyn == YYFINAL) + YYACCEPT; + +#if YYDEBUG != 0 + if (yydebug) + fprintf(stderr, "Shifting error token, "); +#endif + + *++yyvsp = yylval; +#ifdef YYLSP_NEEDED + *++yylsp = yylloc; +#endif + + yystate = yyn; + goto yynewstate; +} diff --git a/Engine/source/console/cmdgram.cpp b/Engine/source/console/cmdgram.cpp new file mode 100644 index 000000000..35ed58bd0 --- /dev/null +++ b/Engine/source/console/cmdgram.cpp @@ -0,0 +1,3364 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton implementation for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.3" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + +/* Substitute the variable and function names. */ +#define yyparse CMDparse +#define yylex CMDlex +#define yyerror CMDerror +#define yylval CMDlval +#define yychar CMDchar +#define yydebug CMDdebug +#define yynerrs CMDnerrs + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + rwDEFINE = 258, + rwENDDEF = 259, + rwDECLARE = 260, + rwDECLARESINGLETON = 261, + rwBREAK = 262, + rwELSE = 263, + rwCONTINUE = 264, + rwGLOBAL = 265, + rwIF = 266, + rwNIL = 267, + rwRETURN = 268, + rwWHILE = 269, + rwDO = 270, + rwENDIF = 271, + rwENDWHILE = 272, + rwENDFOR = 273, + rwDEFAULT = 274, + rwFOR = 275, + rwFOREACH = 276, + rwFOREACHSTR = 277, + rwIN = 278, + rwDATABLOCK = 279, + rwSWITCH = 280, + rwCASE = 281, + rwSWITCHSTR = 282, + rwCASEOR = 283, + rwPACKAGE = 284, + rwNAMESPACE = 285, + rwCLASS = 286, + rwASSERT = 287, + ILLEGAL_TOKEN = 288, + CHRCONST = 289, + INTCONST = 290, + TTAG = 291, + VAR = 292, + IDENT = 293, + TYPEIDENT = 294, + DOCBLOCK = 295, + STRATOM = 296, + TAGATOM = 297, + FLTCONST = 298, + opINTNAME = 299, + opINTNAMER = 300, + opMINUSMINUS = 301, + opPLUSPLUS = 302, + STMT_SEP = 303, + opSHL = 304, + opSHR = 305, + opPLASN = 306, + opMIASN = 307, + opMLASN = 308, + opDVASN = 309, + opMODASN = 310, + opANDASN = 311, + opXORASN = 312, + opORASN = 313, + opSLASN = 314, + opSRASN = 315, + opCAT = 316, + opEQ = 317, + opNE = 318, + opGE = 319, + opLE = 320, + opAND = 321, + opOR = 322, + opSTREQ = 323, + opCOLONCOLON = 324, + opNTASN = 325, + opNDASN = 326, + opMDASN = 327, + opSTRNE = 328, + UNARY = 329 + }; +#endif +/* Tokens. */ +#define rwDEFINE 258 +#define rwENDDEF 259 +#define rwDECLARE 260 +#define rwDECLARESINGLETON 261 +#define rwBREAK 262 +#define rwELSE 263 +#define rwCONTINUE 264 +#define rwGLOBAL 265 +#define rwIF 266 +#define rwNIL 267 +#define rwRETURN 268 +#define rwWHILE 269 +#define rwDO 270 +#define rwENDIF 271 +#define rwENDWHILE 272 +#define rwENDFOR 273 +#define rwDEFAULT 274 +#define rwFOR 275 +#define rwFOREACH 276 +#define rwFOREACHSTR 277 +#define rwIN 278 +#define rwDATABLOCK 279 +#define rwSWITCH 280 +#define rwCASE 281 +#define rwSWITCHSTR 282 +#define rwCASEOR 283 +#define rwPACKAGE 284 +#define rwNAMESPACE 285 +#define rwCLASS 286 +#define rwASSERT 287 +#define ILLEGAL_TOKEN 288 +#define CHRCONST 289 +#define INTCONST 290 +#define TTAG 291 +#define VAR 292 +#define IDENT 293 +#define TYPEIDENT 294 +#define DOCBLOCK 295 +#define STRATOM 296 +#define TAGATOM 297 +#define FLTCONST 298 +#define opINTNAME 299 +#define opINTNAMER 300 +#define opMINUSMINUS 301 +#define opPLUSPLUS 302 +#define STMT_SEP 303 +#define opSHL 304 +#define opSHR 305 +#define opPLASN 306 +#define opMIASN 307 +#define opMLASN 308 +#define opDVASN 309 +#define opMODASN 310 +#define opANDASN 311 +#define opXORASN 312 +#define opORASN 313 +#define opSLASN 314 +#define opSRASN 315 +#define opCAT 316 +#define opEQ 317 +#define opNE 318 +#define opGE 319 +#define opLE 320 +#define opAND 321 +#define opOR 322 +#define opSTREQ 323 +#define opCOLONCOLON 324 +#define opNTASN 325 +#define opNDASN 326 +#define opMDASN 327 +#define opSTRNE 328 +#define UNARY 329 + + + + +/* Copy the first part of user declarations. */ +#line 1 "CMDgram.y" + + +// bison --defines=cmdgram.h --verbose -o cmdgram.cpp -p CMD CMDgram.y + +// Make sure we don't get gram.h twice. +#define _CMDGRAM_H_ + +#include +#include +#include "console/console.h" +#include "console/compiler.h" +#include "console/consoleInternal.h" +#include "core/strings/stringFunctions.h" + +#ifndef YYDEBUG +#define YYDEBUG 0 +#endif + +#define YYSSIZE 350 + +int outtext(char *fmt, ...); +extern int serrors; + +#define nil 0 +#undef YY_ARGS +#define YY_ARGS(x) x + +int CMDlex(); +void CMDerror(char *, ...); + +#ifdef alloca +#undef alloca +#endif +#define alloca dMalloc + +template< typename T > +struct Token +{ + T value; + U32 lineNumber; +}; + +#line 44 "CMDgram.y" + + /* Reserved Word Definitions */ +#line 55 "CMDgram.y" + + /* Constants and Identifier Definitions */ +#line 69 "CMDgram.y" + + /* Operator Definitions */ + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +#line 82 "CMDgram.y" +{ + Token< char > c; + Token< int > i; + Token< const char* > s; + Token< char* > str; + Token< double > f; + StmtNode* stmt; + ExprNode* expr; + SlotAssignNode* slist; + VarNode* var; + SlotDecl slot; + InternalSlotDecl intslot; + ObjectBlockDecl odcl; + ObjectDeclNode* od; + AssignDecl asn; + IfStmtNode* ifnode; +} +/* Line 193 of yacc.c. */ +#line 323 "cmdgram.cpp" + YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + +/* Copy the second part of user declarations. */ + + +/* Line 216 of yacc.c. */ +#line 336 "cmdgram.cpp" + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int i) +#else +static int +YYID (i) + int i; +#endif +{ + return i; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined _STDLIB_H \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss; + YYSTYPE yyvs; + }; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 3 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 2858 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 100 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 41 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 162 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 380 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 329 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 64, 2, 2, 2, 54, 53, 2, + 55, 56, 46, 44, 57, 45, 51, 47, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 58, 59, + 48, 50, 49, 96, 65, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 92, 2, 99, 62, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 60, 52, 61, 63, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 93, 94, 95, 97, 98 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint16 yyprhs[] = +{ + 0, 0, 3, 5, 6, 9, 11, 13, 15, 22, + 24, 27, 28, 31, 33, 35, 37, 39, 41, 43, + 46, 49, 52, 56, 59, 64, 71, 73, 82, 93, + 94, 96, 98, 102, 113, 124, 132, 145, 155, 166, + 174, 175, 178, 179, 181, 182, 185, 186, 188, 190, + 193, 196, 200, 204, 206, 214, 222, 227, 235, 241, + 243, 247, 253, 261, 267, 274, 284, 293, 302, 310, + 319, 327, 335, 342, 350, 358, 360, 362, 366, 370, + 374, 378, 382, 386, 390, 394, 398, 401, 404, 406, + 412, 416, 420, 424, 428, 432, 436, 440, 444, 448, + 452, 456, 460, 464, 467, 470, 472, 474, 476, 478, + 480, 482, 484, 486, 488, 493, 497, 504, 508, 512, + 514, 518, 520, 522, 525, 528, 531, 534, 537, 540, + 543, 546, 549, 552, 554, 556, 558, 562, 569, 572, + 578, 581, 585, 591, 596, 603, 610, 615, 622, 623, + 625, 627, 631, 632, 634, 636, 639, 644, 650, 655, + 663, 672, 674 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int16 yyrhs[] = +{ + 101, 0, -1, 102, -1, -1, 102, 103, -1, 107, + -1, 108, -1, 104, -1, 29, 38, 60, 105, 61, + 59, -1, 108, -1, 105, 108, -1, -1, 106, 107, + -1, 122, -1, 123, -1, 124, -1, 125, -1, 111, + -1, 119, -1, 7, 59, -1, 9, 59, -1, 13, + 59, -1, 13, 127, 59, -1, 126, 59, -1, 36, + 50, 127, 59, -1, 36, 50, 127, 57, 127, 59, + -1, 40, -1, 3, 38, 55, 109, 56, 60, 106, + 61, -1, 3, 38, 91, 38, 55, 109, 56, 60, + 106, 61, -1, -1, 110, -1, 37, -1, 110, 57, + 37, -1, 24, 130, 55, 127, 113, 56, 60, 137, + 61, 59, -1, 5, 130, 55, 114, 113, 115, 56, + 60, 116, 61, -1, 5, 130, 55, 114, 113, 115, + 56, -1, 5, 130, 55, 92, 114, 99, 113, 115, + 56, 60, 116, 61, -1, 5, 130, 55, 92, 114, + 99, 113, 115, 56, -1, 6, 130, 55, 114, 113, + 115, 56, 60, 116, 61, -1, 6, 130, 55, 114, + 113, 115, 56, -1, -1, 58, 38, -1, -1, 127, + -1, -1, 57, 136, -1, -1, 138, -1, 117, -1, + 138, 117, -1, 112, 59, -1, 117, 112, 59, -1, + 60, 106, 61, -1, 107, -1, 25, 55, 127, 56, + 60, 120, 61, -1, 27, 55, 127, 56, 60, 120, + 61, -1, 26, 121, 58, 106, -1, 26, 121, 58, + 106, 19, 58, 106, -1, 26, 121, 58, 106, 120, + -1, 127, -1, 121, 28, 127, -1, 11, 55, 127, + 56, 118, -1, 11, 55, 127, 56, 118, 8, 118, + -1, 14, 55, 127, 56, 118, -1, 15, 118, 14, + 55, 127, 56, -1, 20, 55, 127, 59, 127, 59, + 127, 56, 118, -1, 20, 55, 127, 59, 127, 59, + 56, 118, -1, 20, 55, 127, 59, 59, 127, 56, + 118, -1, 20, 55, 127, 59, 59, 56, 118, -1, + 20, 55, 59, 127, 59, 127, 56, 118, -1, 20, + 55, 59, 127, 59, 56, 118, -1, 20, 55, 59, + 59, 127, 56, 118, -1, 20, 55, 59, 59, 56, + 118, -1, 21, 55, 37, 23, 127, 56, 118, -1, + 22, 55, 37, 23, 127, 56, 118, -1, 132, -1, + 132, -1, 55, 127, 56, -1, 127, 62, 127, -1, + 127, 54, 127, -1, 127, 53, 127, -1, 127, 52, + 127, -1, 127, 44, 127, -1, 127, 45, 127, -1, + 127, 46, 127, -1, 127, 47, 127, -1, 45, 127, + -1, 46, 127, -1, 36, -1, 127, 96, 127, 58, + 127, -1, 127, 48, 127, -1, 127, 49, 127, -1, + 127, 86, 127, -1, 127, 87, 127, -1, 127, 84, + 127, -1, 127, 85, 127, -1, 127, 89, 127, -1, + 127, 71, 127, -1, 127, 72, 127, -1, 127, 88, + 127, -1, 127, 90, 127, -1, 127, 97, 127, -1, + 127, 65, 127, -1, 64, 127, -1, 63, 127, -1, + 42, -1, 43, -1, 35, -1, 7, -1, 128, -1, + 129, -1, 38, -1, 41, -1, 37, -1, 37, 92, + 140, 99, -1, 127, 51, 38, -1, 127, 51, 38, + 92, 140, 99, -1, 127, 66, 130, -1, 127, 67, + 130, -1, 38, -1, 55, 127, 56, -1, 69, -1, + 68, -1, 73, 127, -1, 74, 127, -1, 75, 127, + -1, 76, 127, -1, 77, 127, -1, 78, 127, -1, + 79, 127, -1, 80, 127, -1, 81, 127, -1, 82, + 127, -1, 133, -1, 134, -1, 112, -1, 37, 50, + 127, -1, 37, 92, 140, 99, 50, 127, -1, 37, + 131, -1, 37, 92, 140, 99, 131, -1, 128, 131, + -1, 128, 50, 127, -1, 128, 50, 60, 136, 61, + -1, 38, 55, 135, 56, -1, 38, 91, 38, 55, + 135, 56, -1, 127, 51, 38, 55, 135, 56, -1, + 32, 55, 127, 56, -1, 32, 55, 127, 57, 41, + 56, -1, -1, 136, -1, 127, -1, 136, 57, 127, + -1, -1, 138, -1, 139, -1, 138, 139, -1, 38, + 50, 127, 59, -1, 39, 38, 50, 127, 59, -1, + 24, 50, 127, 59, -1, 38, 92, 140, 99, 50, + 127, 59, -1, 39, 38, 92, 140, 99, 50, 127, + 59, -1, 127, -1, 140, 57, 127, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 161, 161, 167, 168, 173, 175, 177, 182, 187, + 189, 195, 196, 201, 202, 203, 204, 205, 206, 207, + 209, 211, 213, 215, 217, 219, 221, 226, 228, 234, + 235, 240, 242, 247, 252, 254, 256, 258, 260, 262, + 268, 269, 275, 276, 282, 283, 289, 290, 292, 294, + 299, 301, 306, 308, 313, 315, 320, 322, 324, 329, + 331, 336, 338, 343, 345, 350, 352, 354, 356, 358, + 360, 362, 364, 369, 371, 376, 381, 383, 385, 387, + 389, 391, 393, 395, 397, 399, 401, 403, 405, 407, + 409, 411, 413, 415, 417, 419, 421, 423, 425, 427, + 429, 431, 433, 435, 437, 439, 441, 443, 445, 447, + 449, 451, 453, 455, 457, 462, 464, 469, 471, 476, + 478, 483, 485, 487, 489, 491, 493, 495, 497, 499, + 501, 503, 505, 510, 512, 514, 516, 518, 520, 522, + 524, 526, 528, 533, 535, 537, 542, 544, 550, 551, + 556, 558, 564, 565, 570, 572, 577, 579, 581, 583, + 585, 590, 592 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "rwDEFINE", "rwENDDEF", "rwDECLARE", + "rwDECLARESINGLETON", "rwBREAK", "rwELSE", "rwCONTINUE", "rwGLOBAL", + "rwIF", "rwNIL", "rwRETURN", "rwWHILE", "rwDO", "rwENDIF", "rwENDWHILE", + "rwENDFOR", "rwDEFAULT", "rwFOR", "rwFOREACH", "rwFOREACHSTR", "rwIN", + "rwDATABLOCK", "rwSWITCH", "rwCASE", "rwSWITCHSTR", "rwCASEOR", + "rwPACKAGE", "rwNAMESPACE", "rwCLASS", "rwASSERT", "ILLEGAL_TOKEN", + "CHRCONST", "INTCONST", "TTAG", "VAR", "IDENT", "TYPEIDENT", "DOCBLOCK", + "STRATOM", "TAGATOM", "FLTCONST", "'+'", "'-'", "'*'", "'/'", "'<'", + "'>'", "'='", "'.'", "'|'", "'&'", "'%'", "'('", "')'", "','", "':'", + "';'", "'{'", "'}'", "'^'", "'~'", "'!'", "'@'", "opINTNAME", + "opINTNAMER", "opMINUSMINUS", "opPLUSPLUS", "STMT_SEP", "opSHL", "opSHR", + "opPLASN", "opMIASN", "opMLASN", "opDVASN", "opMODASN", "opANDASN", + "opXORASN", "opORASN", "opSLASN", "opSRASN", "opCAT", "opEQ", "opNE", + "opGE", "opLE", "opAND", "opOR", "opSTREQ", "opCOLONCOLON", "'['", + "opNTASN", "opNDASN", "opMDASN", "'?'", "opSTRNE", "UNARY", "']'", + "$accept", "start", "decl_list", "decl", "package_decl", "fn_decl_list", + "statement_list", "stmt", "fn_decl_stmt", "var_list_decl", "var_list", + "datablock_decl", "object_decl", "parent_block", "object_name", + "object_args", "object_declare_block", "object_decl_list", "stmt_block", + "switch_stmt", "case_block", "case_expr", "if_stmt", "while_stmt", + "for_stmt", "foreach_stmt", "expression_stmt", "expr", "slot_acc", + "intslot_acc", "class_name_expr", "assign_op_struct", "stmt_expr", + "funcall_expr", "assert_expr", "expr_list_decl", "expr_list", + "slot_assign_list_opt", "slot_assign_list", "slot_assign", "aidx_expr", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 43, 45, 42, 47, 60, 62, + 61, 46, 124, 38, 37, 40, 41, 44, 58, 59, + 123, 125, 94, 126, 33, 64, 299, 300, 301, 302, + 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, + 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, + 323, 324, 91, 325, 326, 327, 63, 328, 329, 93 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 100, 101, 102, 102, 103, 103, 103, 104, 105, + 105, 106, 106, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 108, 108, 109, + 109, 110, 110, 111, 112, 112, 112, 112, 112, 112, + 113, 113, 114, 114, 115, 115, 116, 116, 116, 116, + 117, 117, 118, 118, 119, 119, 120, 120, 120, 121, + 121, 122, 122, 123, 123, 124, 124, 124, 124, 124, + 124, 124, 124, 125, 125, 126, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 128, 128, 129, 129, 130, + 130, 131, 131, 131, 131, 131, 131, 131, 131, 131, + 131, 131, 131, 132, 132, 132, 132, 132, 132, 132, + 132, 132, 132, 133, 133, 133, 134, 134, 135, 135, + 136, 136, 137, 137, 138, 138, 139, 139, 139, 139, + 139, 140, 140 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 1, 0, 2, 1, 1, 1, 6, 1, + 2, 0, 2, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 3, 2, 4, 6, 1, 8, 10, 0, + 1, 1, 3, 10, 10, 7, 12, 9, 10, 7, + 0, 2, 0, 1, 0, 2, 0, 1, 1, 2, + 2, 3, 3, 1, 7, 7, 4, 7, 5, 1, + 3, 5, 7, 5, 6, 9, 8, 8, 7, 8, + 7, 7, 6, 7, 7, 1, 1, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 2, 2, 1, 5, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 2, 2, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 4, 3, 6, 3, 3, 1, + 3, 1, 1, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 1, 1, 1, 3, 6, 2, 5, + 2, 3, 5, 4, 6, 6, 4, 6, 0, 1, + 1, 3, 0, 1, 1, 2, 4, 5, 4, 7, + 8, 1, 3 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 3, 0, 2, 1, 0, 0, 0, 108, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 107, 88, 113, 111, 26, 112, 105, 106, 0, + 0, 0, 0, 0, 4, 7, 5, 6, 17, 135, + 18, 13, 14, 15, 16, 0, 0, 109, 110, 76, + 133, 134, 0, 119, 0, 0, 0, 19, 20, 0, + 108, 88, 21, 0, 76, 0, 11, 53, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 122, + 121, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 138, 148, 0, 86, 87, 0, 104, 103, + 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 140, 29, 0, + 0, 42, 42, 0, 22, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 136, 123, + 124, 125, 126, 127, 128, 129, 130, 131, 132, 161, + 0, 150, 0, 149, 0, 77, 82, 83, 84, 85, + 90, 91, 115, 81, 80, 79, 78, 102, 117, 118, + 97, 98, 94, 95, 92, 93, 99, 96, 100, 0, + 101, 0, 141, 31, 0, 30, 0, 120, 42, 40, + 43, 40, 0, 0, 52, 12, 0, 0, 0, 0, + 0, 0, 40, 0, 0, 0, 9, 146, 0, 0, + 24, 0, 114, 143, 0, 148, 148, 0, 0, 0, + 0, 0, 29, 0, 0, 44, 44, 61, 63, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 10, 0, 0, 162, 0, 139, 151, 0, 0, + 0, 89, 142, 11, 32, 0, 40, 41, 0, 0, + 0, 0, 64, 72, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 8, 147, 25, 137, + 144, 145, 116, 0, 0, 44, 45, 35, 39, 62, + 71, 70, 0, 68, 0, 0, 0, 73, 74, 152, + 0, 59, 54, 55, 27, 11, 0, 46, 46, 69, + 67, 66, 0, 0, 0, 0, 0, 153, 154, 0, + 11, 0, 37, 0, 0, 48, 47, 0, 65, 0, + 0, 0, 0, 0, 155, 60, 56, 28, 46, 50, + 34, 0, 49, 38, 0, 0, 0, 0, 0, 33, + 0, 58, 0, 51, 158, 156, 0, 0, 0, 11, + 36, 0, 157, 0, 57, 0, 0, 159, 0, 160 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int16 yydefgoto[] = +{ + -1, 1, 2, 34, 35, 215, 136, 67, 37, 194, + 195, 38, 39, 235, 199, 269, 334, 335, 68, 40, + 284, 310, 41, 42, 43, 44, 45, 46, 47, 48, + 55, 92, 64, 50, 51, 162, 163, 326, 336, 328, + 160 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -310 +static const yytype_int16 yypact[] = +{ + -310, 31, 397, -310, 4, 2, 2, -3, 32, 27, + 167, 34, 489, 40, 41, 43, 2, 44, 47, 66, + 55, -310, 67, 115, -33, -310, -310, -310, -310, 1002, + 1002, 1002, 1002, 1002, -310, -310, -310, -310, -310, -310, + -310, -310, -310, -310, -310, 59, 2465, 2727, -310, 71, + -310, -310, -12, -310, 1002, 81, 82, -310, -310, 1002, + -310, -310, -310, 1115, -310, 1002, -310, -310, 124, 708, + 108, 129, 97, 1002, 1002, 107, 1002, 1002, 1002, -310, + -310, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1002, + 1002, 1002, -310, 1002, 133, -34, -34, 1169, -34, -34, + -310, 1002, 1002, 1002, 1002, 1002, 1002, 137, 1002, 1002, + 1002, 1002, 1002, 2, 2, 1002, 1002, 1002, 1002, 1002, + 1002, 1002, 1002, 1002, 1002, 1002, 750, -310, 141, 143, + 1223, 9, 1002, 1277, -310, 1331, 549, 127, 792, 1385, + 156, 162, 1002, 1439, 1493, 183, 1007, 1061, 2465, 2465, + 2465, 2465, 2465, 2465, 2465, 2465, 2465, 2465, 2465, 2465, + -39, 2465, 131, 149, 159, -310, 292, 292, -34, -34, + 2722, 2722, -43, 2606, 2664, -34, 2635, 2786, -310, -310, + 39, 39, 2693, 2693, 2722, 2722, 2577, 2548, 2786, 1547, + 2786, 1002, 2465, -310, 142, 154, 160, -310, 1002, 158, + 2465, 158, 489, 489, -310, -310, 1002, 319, 1601, 834, + 1002, 1002, 1655, 157, 161, 5, -310, -310, 179, 1002, + -310, 1002, 2747, -310, 1002, 1002, 1002, 1002, 1002, -31, + 165, 190, 141, 135, 197, 171, 171, 231, -310, 1709, + 489, 1763, 876, 918, 1817, 1871, 1925, 184, 217, 217, + 187, -310, 194, 1979, 2465, 1002, -310, 2465, 199, 200, + -38, 2519, -310, -310, -310, 204, 158, -310, 1002, 205, + 211, 489, -310, -310, 489, 489, 2033, 489, 2087, 960, + 489, 489, 193, 1002, 207, 210, -310, -310, -310, 2465, + -310, -310, -310, 594, 214, 171, 149, 216, 222, -310, + -310, -310, 489, -310, 489, 489, 2141, -310, -310, 70, + -5, 2465, -310, -310, -310, -310, 221, 213, 213, -310, + -310, -310, 489, 233, -30, 246, 224, 70, -310, 1002, + -310, 639, 226, 228, 227, 23, 213, 229, -310, 1002, + 1002, 1002, -29, 230, -310, 2465, 444, -310, 213, -310, + -310, 235, 23, -310, 2195, 2249, -23, 1002, 1002, -310, + 237, -310, 236, -310, -310, -310, 248, 2303, -19, -310, + -310, 1002, -310, 249, 684, 2357, 1002, -310, 2411, -310 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int16 yypgoto[] = +{ + -310, -310, -310, -310, -310, -310, -256, -1, -140, 64, + -310, -310, -94, -188, -121, -230, -309, -28, 30, -310, + -246, -310, -310, -310, -310, -310, -310, 38, -310, -310, + 19, -45, -2, -310, -310, -138, -187, -310, 0, -300, + -190 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -76 +static const yytype_int16 yytable[] = +{ + 49, 36, 127, 285, 229, 216, 270, 293, 4, 337, + 49, 201, 226, 236, 5, 6, 60, 107, 221, 221, + 340, 357, 93, 329, 247, 56, 224, 344, 5, 6, + 262, 3, 113, 114, 221, 72, 344, 260, 221, 362, + 53, 20, 52, 128, 21, 61, 23, 24, 63, 227, + 26, 27, 28, 330, 29, 30, 57, 54, 94, 331, + 222, 292, 341, 358, 31, 316, 250, 95, 96, 97, + 98, 99, 32, 33, 346, 251, 366, 233, 295, 129, + 373, 296, 59, 101, 102, 103, 104, 258, 259, 65, + 107, 58, 130, 110, 323, 69, 70, 133, 71, 73, + 361, 198, 74, 135, 75, 113, 114, 139, 324, 325, + 76, 143, 144, 374, 146, 147, 148, 77, 100, 149, + 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + -75, 161, 178, 179, 49, 205, 131, 132, 137, 166, + 167, 168, 169, 170, 171, 140, 173, 174, 175, 176, + 177, 356, 142, 180, 181, 182, 183, 184, 185, 186, + 187, 188, 189, 190, 192, 78, 141, 145, 368, 200, + 200, 164, 5, 6, 60, 172, 208, 256, 193, 210, + 212, 196, 206, 79, 80, 211, 4, 223, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, 230, 20, + 49, 49, 21, 61, 23, 24, 224, 91, 26, 27, + 28, 231, 29, 30, 225, 232, 234, 248, 5, 6, + 252, 249, 31, 333, 333, 263, 62, 264, 268, 161, + 32, 33, 237, 238, 266, 267, 200, 323, 49, 271, + 282, 351, 333, 283, 239, 241, 286, 244, 245, 246, + 287, 324, 325, 309, 333, 290, 291, 253, 351, 254, + 294, 297, 257, 161, 161, 159, 261, 298, 312, 49, + 273, 313, 49, 49, 315, 49, 317, 332, 49, 49, + 276, 278, 318, 339, 342, 343, 348, 349, 350, 359, + 353, 49, 205, 289, 363, 369, 265, 370, 371, 376, + 49, 299, 49, 49, 300, 301, 161, 303, 352, 327, + 307, 308, 0, 0, 0, 0, 0, 306, 0, 0, + 49, 311, 0, 0, 5, 6, 60, 0, 0, 49, + 205, 0, 319, 0, 320, 321, 0, 0, 103, 104, + 0, 0, 0, 107, 49, 205, 110, 0, 0, 0, + 0, 20, 338, 0, 21, 61, 23, 24, 113, 114, + 26, 27, 28, 0, 29, 30, 0, 345, 0, 0, + 0, 0, 49, 205, 31, 240, 0, 354, 355, 159, + 0, 0, 32, 33, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 367, 159, 0, 0, 0, + 4, 0, 5, 6, 7, 0, 8, 0, 9, 375, + 10, 11, 12, 0, 378, 0, 0, 13, 14, 15, + 0, 16, 17, 0, 18, 0, 19, 0, 0, 20, + 0, 0, 21, 22, 23, 24, 0, 25, 26, 27, + 28, 0, 29, 30, 0, 0, 0, 0, 0, 5, + 6, 7, 31, 8, 0, 9, 0, 10, 11, 12, + 32, 33, 0, 360, 13, 14, 15, 0, 16, 17, + 283, 18, 0, 0, 0, 0, 20, 0, 0, 21, + 22, 23, 24, 0, 25, 26, 27, 28, 0, 29, + 30, 0, 0, 0, 5, 6, 7, 0, 8, 31, + 9, 0, 10, 11, 12, 0, 0, 32, 33, 13, + 14, 15, 0, 16, 17, 0, 18, 0, 0, 0, + 0, 20, 0, 0, 21, 22, 23, 24, 0, 25, + 26, 27, 28, 0, 29, 30, 0, 0, 0, 0, + 0, 0, 0, 0, 31, 0, 0, 0, 0, 66, + 0, 0, 32, 33, 5, 6, 7, 0, 8, 0, + 9, 0, 10, 11, 12, 0, 0, 0, 0, 13, + 14, 15, 0, 16, 17, 0, 18, 0, 0, 0, + 0, 20, 0, 0, 21, 22, 23, 24, 0, 25, + 26, 27, 28, 0, 29, 30, 0, 0, 0, 5, + 6, 7, 0, 8, 31, 9, 0, 10, 11, 12, + 204, 0, 32, 33, 13, 14, 15, 0, 16, 17, + 0, 18, 0, 0, 0, 0, 20, 0, 0, 21, + 22, 23, 24, 0, 25, 26, 27, 28, 0, 29, + 30, 0, 0, 0, 5, 6, 7, 0, 8, 31, + 9, 0, 10, 11, 12, 314, 0, 32, 33, 13, + 14, 15, 0, 16, 17, 0, 18, 0, 0, 0, + 0, 20, 0, 0, 21, 22, 23, 24, 0, 25, + 26, 27, 28, 0, 29, 30, 0, 0, 0, 5, + 6, 7, 0, 8, 31, 9, 0, 10, 11, 12, + 347, 0, 32, 33, 13, 14, 15, 0, 16, 17, + 0, 18, 0, 5, 6, 60, 20, 0, 0, 21, + 22, 23, 24, 0, 25, 26, 27, 28, 0, 29, + 30, 0, 0, 0, 0, 0, 0, 0, 0, 31, + 20, 0, 0, 21, 61, 23, 24, 32, 33, 26, + 27, 28, 0, 29, 30, 5, 6, 60, 0, 0, + 0, 0, 0, 31, 0, 0, 0, 138, 0, 0, + 0, 32, 33, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 20, 0, 0, 21, 61, 23, 24, 0, + 0, 26, 27, 28, 0, 29, 30, 5, 6, 60, + 0, 0, 0, 0, 0, 31, 0, 0, 0, 0, + 191, 0, 0, 32, 33, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 20, 0, 0, 21, 61, 23, + 24, 0, 0, 26, 27, 28, 0, 29, 30, 5, + 6, 60, 0, 0, 0, 0, 0, 31, 0, 0, + 0, 207, 0, 0, 0, 32, 33, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 20, 0, 0, 21, + 61, 23, 24, 0, 0, 26, 27, 28, 0, 29, + 30, 5, 6, 60, 0, 0, 0, 0, 0, 31, + 0, 0, 0, 243, 0, 0, 0, 32, 33, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, + 0, 21, 61, 23, 24, 0, 0, 26, 27, 28, + 0, 29, 30, 5, 6, 60, 0, 0, 0, 0, + 0, 31, 275, 0, 0, 0, 0, 0, 0, 32, + 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 20, 0, 0, 21, 61, 23, 24, 0, 0, 26, + 27, 28, 0, 29, 30, 5, 6, 60, 0, 0, + 0, 0, 0, 31, 277, 0, 0, 0, 0, 0, + 0, 32, 33, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 20, 0, 0, 21, 61, 23, 24, 0, + 0, 26, 27, 28, 0, 29, 30, 5, 6, 60, + 0, 0, 0, 0, 0, 31, 305, 0, 0, 0, + 0, 0, 0, 32, 33, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 20, 0, 0, 21, 61, 23, + 24, 0, 0, 26, 27, 28, 0, 29, 30, 0, + 0, 101, 102, 103, 104, 105, 106, 31, 107, 108, + 109, 110, 0, 217, 218, 32, 33, 0, 0, 111, + 0, 0, 112, 113, 114, 0, 0, 0, 115, 116, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 117, 118, 119, 120, 121, 122, 123, 0, 0, + 0, 0, 0, 124, 125, 101, 102, 103, 104, 105, + 106, 0, 107, 108, 109, 110, 0, 0, 219, 0, + 220, 0, 0, 111, 0, 0, 112, 113, 114, 0, + 0, 0, 115, 116, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 117, 118, 119, 120, 121, + 122, 123, 0, 0, 0, 0, 0, 124, 125, 101, + 102, 103, 104, 105, 106, 0, 107, 108, 109, 110, + 0, 0, 0, 0, 134, 0, 0, 111, 0, 0, + 112, 113, 114, 0, 0, 0, 115, 116, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, + 118, 119, 120, 121, 122, 123, 0, 0, 0, 0, + 0, 124, 125, 101, 102, 103, 104, 105, 106, 0, + 107, 108, 109, 110, 0, 165, 0, 0, 0, 0, + 0, 111, 0, 0, 112, 113, 114, 0, 0, 0, + 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 117, 118, 119, 120, 121, 122, 123, + 0, 0, 0, 0, 0, 124, 125, 101, 102, 103, + 104, 105, 106, 0, 107, 108, 109, 110, 0, 197, + 0, 0, 0, 0, 0, 111, 0, 0, 112, 113, + 114, 0, 0, 0, 115, 116, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 117, 118, 119, + 120, 121, 122, 123, 0, 0, 0, 0, 0, 124, + 125, 101, 102, 103, 104, 105, 106, 0, 107, 108, + 109, 110, 0, 202, 0, 0, 0, 0, 0, 111, + 0, 0, 112, 113, 114, 0, 0, 0, 115, 116, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 117, 118, 119, 120, 121, 122, 123, 0, 0, + 0, 0, 0, 124, 125, 101, 102, 103, 104, 105, + 106, 0, 107, 108, 109, 110, 0, 203, 0, 0, + 0, 0, 0, 111, 0, 0, 112, 113, 114, 0, + 0, 0, 115, 116, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 117, 118, 119, 120, 121, + 122, 123, 0, 0, 0, 0, 0, 124, 125, 101, + 102, 103, 104, 105, 106, 0, 107, 108, 109, 110, + 0, 0, 0, 0, 209, 0, 0, 111, 0, 0, + 112, 113, 114, 0, 0, 0, 115, 116, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, + 118, 119, 120, 121, 122, 123, 0, 0, 0, 0, + 0, 124, 125, 101, 102, 103, 104, 105, 106, 0, + 107, 108, 109, 110, 0, 213, 0, 0, 0, 0, + 0, 111, 0, 0, 112, 113, 114, 0, 0, 0, + 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 117, 118, 119, 120, 121, 122, 123, + 0, 0, 0, 0, 0, 124, 125, 101, 102, 103, + 104, 105, 106, 0, 107, 108, 109, 110, 0, 214, + 0, 0, 0, 0, 0, 111, 0, 0, 112, 113, + 114, 0, 0, 0, 115, 116, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 117, 118, 119, + 120, 121, 122, 123, 0, 0, 0, 0, 0, 124, + 125, 101, 102, 103, 104, 105, 106, 0, 107, 108, + 109, 110, 0, 0, 0, 228, 0, 0, 0, 111, + 0, 0, 112, 113, 114, 0, 0, 0, 115, 116, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 117, 118, 119, 120, 121, 122, 123, 0, 0, + 0, 0, 0, 124, 125, 101, 102, 103, 104, 105, + 106, 0, 107, 108, 109, 110, 0, 0, 0, 0, + 242, 0, 0, 111, 0, 0, 112, 113, 114, 0, + 0, 0, 115, 116, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 117, 118, 119, 120, 121, + 122, 123, 0, 0, 0, 0, 0, 124, 125, 101, + 102, 103, 104, 105, 106, 0, 107, 108, 109, 110, + 0, 0, 0, 234, 0, 0, 0, 111, 0, 0, + 112, 113, 114, 0, 0, 0, 115, 116, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, + 118, 119, 120, 121, 122, 123, 0, 0, 0, 0, + 0, 124, 125, 101, 102, 103, 104, 105, 106, 0, + 107, 108, 109, 110, 0, 272, 0, 0, 0, 0, + 0, 111, 0, 0, 112, 113, 114, 0, 0, 0, + 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 117, 118, 119, 120, 121, 122, 123, + 0, 0, 0, 0, 0, 124, 125, 101, 102, 103, + 104, 105, 106, 0, 107, 108, 109, 110, 0, 274, + 0, 0, 0, 0, 0, 111, 0, 0, 112, 113, + 114, 0, 0, 0, 115, 116, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 117, 118, 119, + 120, 121, 122, 123, 0, 0, 0, 0, 0, 124, + 125, 101, 102, 103, 104, 105, 106, 0, 107, 108, + 109, 110, 0, 0, 0, 0, 279, 0, 0, 111, + 0, 0, 112, 113, 114, 0, 0, 0, 115, 116, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 117, 118, 119, 120, 121, 122, 123, 0, 0, + 0, 0, 0, 124, 125, 101, 102, 103, 104, 105, + 106, 0, 107, 108, 109, 110, 0, 280, 0, 0, + 0, 0, 0, 111, 0, 0, 112, 113, 114, 0, + 0, 0, 115, 116, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 117, 118, 119, 120, 121, + 122, 123, 0, 0, 0, 0, 0, 124, 125, 101, + 102, 103, 104, 105, 106, 0, 107, 108, 109, 110, + 0, 281, 0, 0, 0, 0, 0, 111, 0, 0, + 112, 113, 114, 0, 0, 0, 115, 116, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, + 118, 119, 120, 121, 122, 123, 0, 0, 0, 0, + 0, 124, 125, 101, 102, 103, 104, 105, 106, 0, + 107, 108, 109, 110, 0, 0, 0, 0, 288, 0, + 0, 111, 0, 0, 112, 113, 114, 0, 0, 0, + 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 117, 118, 119, 120, 121, 122, 123, + 0, 0, 0, 0, 0, 124, 125, 101, 102, 103, + 104, 105, 106, 0, 107, 108, 109, 110, 0, 302, + 0, 0, 0, 0, 0, 111, 0, 0, 112, 113, + 114, 0, 0, 0, 115, 116, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 117, 118, 119, + 120, 121, 122, 123, 0, 0, 0, 0, 0, 124, + 125, 101, 102, 103, 104, 105, 106, 0, 107, 108, + 109, 110, 0, 304, 0, 0, 0, 0, 0, 111, + 0, 0, 112, 113, 114, 0, 0, 0, 115, 116, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 117, 118, 119, 120, 121, 122, 123, 0, 0, + 0, 0, 0, 124, 125, 101, 102, 103, 104, 105, + 106, 0, 107, 108, 109, 110, 0, 322, 0, 0, + 0, 0, 0, 111, 0, 0, 112, 113, 114, 0, + 0, 0, 115, 116, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 117, 118, 119, 120, 121, + 122, 123, 0, 0, 0, 0, 0, 124, 125, 101, + 102, 103, 104, 105, 106, 0, 107, 108, 109, 110, + 0, 0, 0, 0, 364, 0, 0, 111, 0, 0, + 112, 113, 114, 0, 0, 0, 115, 116, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, + 118, 119, 120, 121, 122, 123, 0, 0, 0, 0, + 0, 124, 125, 101, 102, 103, 104, 105, 106, 0, + 107, 108, 109, 110, 0, 0, 0, 0, 365, 0, + 0, 111, 0, 0, 112, 113, 114, 0, 0, 0, + 115, 116, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 117, 118, 119, 120, 121, 122, 123, + 0, 0, 0, 0, 0, 124, 125, 101, 102, 103, + 104, 105, 106, 0, 107, 108, 109, 110, 0, 0, + 0, 0, 372, 0, 0, 111, 0, 0, 112, 113, + 114, 0, 0, 0, 115, 116, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 117, 118, 119, + 120, 121, 122, 123, 0, 0, 0, 0, 0, 124, + 125, 101, 102, 103, 104, 105, 106, 0, 107, 108, + 109, 110, 0, 0, 0, 0, 377, 0, 0, 111, + 0, 0, 112, 113, 114, 0, 0, 0, 115, 116, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 117, 118, 119, 120, 121, 122, 123, 0, 0, + 0, 0, 0, 124, 125, 101, 102, 103, 104, 105, + 106, 0, 107, 108, 109, 110, 0, 0, 0, 0, + 379, 0, 0, 111, 0, 0, 112, 113, 114, 0, + 0, 0, 115, 116, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 117, 118, 119, 120, 121, + 122, 123, 0, 0, 0, 0, 0, 124, 125, 101, + 102, 103, 104, 105, 106, 0, 107, 108, 109, 110, + 0, 0, 0, 0, 0, 0, 0, 111, 0, 0, + 112, 113, 114, 0, 0, 0, 115, 116, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 117, + 118, 119, 120, 121, 122, 123, 0, 0, 0, 0, + 0, 124, 125, 101, 102, 103, 104, 105, 106, 0, + 107, 108, 109, 110, 0, 0, 0, 0, 0, 0, + 0, 111, 0, 0, 112, 113, 114, 0, 0, 0, + 115, 116, 101, 102, 103, 104, 105, 106, 0, 107, + 108, 109, 110, 117, 118, 119, 120, 121, 122, 123, + 111, 0, 0, 112, 113, 114, 125, 0, 0, 115, + 116, 101, 102, 103, 104, 105, 106, 0, 107, 108, + 109, 110, 117, 118, 119, 120, 121, 0, 123, 111, + 0, 0, 112, 113, 114, 125, 0, 0, 115, 116, + 101, 102, 103, 104, 105, 106, 0, 107, 0, 109, + 110, 117, 118, 119, 120, 0, 0, 123, 111, 0, + 0, 112, 113, 114, 125, 0, 0, 115, 116, 101, + 102, 103, 104, 105, 106, 0, 107, 0, 109, 110, + 117, 118, 119, 120, 0, 0, 123, 0, 0, 0, + 112, 113, 114, 125, 0, 0, 115, 116, 101, 102, + 103, 104, 105, 106, 0, 107, 0, 0, 110, 117, + 118, 119, 120, 0, 0, 123, 0, 0, 0, 112, + 113, 114, 125, 0, 0, 115, 116, 101, 102, 103, + 104, 105, 106, 0, 107, 0, 0, 110, 117, 118, + 119, 120, 0, 0, 123, 0, 0, 0, 112, 113, + 114, 125, 0, 0, 115, 116, 101, 102, 103, 104, + 0, 0, 0, 107, 0, 0, 110, 126, 0, 119, + 120, 0, 0, 123, 0, 0, 0, 112, 113, 114, + 125, 0, 0, 115, 116, 79, 80, 255, 0, 0, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + 0, 0, 123, 0, 0, 79, 80, 0, 0, 125, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, + 101, 102, 103, 104, 0, 0, 0, 107, 0, 0, + 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 113, 114, 0, 0, 0, 115, 116 +}; + +static const yytype_int16 yycheck[] = +{ + 2, 2, 47, 249, 191, 145, 236, 263, 3, 318, + 12, 132, 55, 201, 5, 6, 7, 51, 57, 57, + 50, 50, 55, 28, 212, 6, 57, 327, 5, 6, + 61, 0, 66, 67, 57, 16, 336, 227, 57, 348, + 38, 32, 38, 55, 35, 36, 37, 38, 10, 92, + 41, 42, 43, 58, 45, 46, 59, 55, 91, 315, + 99, 99, 92, 92, 55, 295, 61, 29, 30, 31, + 32, 33, 63, 64, 330, 215, 99, 198, 266, 91, + 99, 268, 55, 44, 45, 46, 47, 225, 226, 55, + 51, 59, 54, 54, 24, 55, 55, 59, 55, 55, + 346, 92, 55, 65, 38, 66, 67, 69, 38, 39, + 55, 73, 74, 369, 76, 77, 78, 50, 59, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 59, 93, 113, 114, 136, 136, 55, 55, 14, 101, + 102, 103, 104, 105, 106, 37, 108, 109, 110, 111, + 112, 341, 55, 115, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 50, 37, 60, 358, 131, + 132, 38, 5, 6, 7, 38, 138, 222, 37, 23, + 142, 38, 55, 68, 69, 23, 3, 56, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 56, 32, + 202, 203, 35, 36, 37, 38, 57, 92, 41, 42, + 43, 57, 45, 46, 55, 55, 58, 60, 5, 6, + 41, 60, 55, 317, 318, 60, 59, 37, 57, 191, + 63, 64, 202, 203, 99, 38, 198, 24, 240, 8, + 56, 335, 336, 26, 206, 207, 59, 209, 210, 211, + 56, 38, 39, 60, 348, 56, 56, 219, 352, 221, + 56, 56, 224, 225, 226, 227, 228, 56, 61, 271, + 240, 61, 274, 275, 60, 277, 60, 56, 280, 281, + 242, 243, 60, 50, 38, 61, 60, 59, 61, 59, + 61, 293, 293, 255, 59, 58, 232, 61, 50, 50, + 302, 271, 304, 305, 274, 275, 268, 277, 336, 309, + 280, 281, -1, -1, -1, -1, -1, 279, -1, -1, + 322, 283, -1, -1, 5, 6, 7, -1, -1, 331, + 331, -1, 302, -1, 304, 305, -1, -1, 46, 47, + -1, -1, -1, 51, 346, 346, 54, -1, -1, -1, + -1, 32, 322, -1, 35, 36, 37, 38, 66, 67, + 41, 42, 43, -1, 45, 46, -1, 329, -1, -1, + -1, -1, 374, 374, 55, 56, -1, 339, 340, 341, + -1, -1, 63, 64, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 357, 358, -1, -1, -1, + 3, -1, 5, 6, 7, -1, 9, -1, 11, 371, + 13, 14, 15, -1, 376, -1, -1, 20, 21, 22, + -1, 24, 25, -1, 27, -1, 29, -1, -1, 32, + -1, -1, 35, 36, 37, 38, -1, 40, 41, 42, + 43, -1, 45, 46, -1, -1, -1, -1, -1, 5, + 6, 7, 55, 9, -1, 11, -1, 13, 14, 15, + 63, 64, -1, 19, 20, 21, 22, -1, 24, 25, + 26, 27, -1, -1, -1, -1, 32, -1, -1, 35, + 36, 37, 38, -1, 40, 41, 42, 43, -1, 45, + 46, -1, -1, -1, 5, 6, 7, -1, 9, 55, + 11, -1, 13, 14, 15, -1, -1, 63, 64, 20, + 21, 22, -1, 24, 25, -1, 27, -1, -1, -1, + -1, 32, -1, -1, 35, 36, 37, 38, -1, 40, + 41, 42, 43, -1, 45, 46, -1, -1, -1, -1, + -1, -1, -1, -1, 55, -1, -1, -1, -1, 60, + -1, -1, 63, 64, 5, 6, 7, -1, 9, -1, + 11, -1, 13, 14, 15, -1, -1, -1, -1, 20, + 21, 22, -1, 24, 25, -1, 27, -1, -1, -1, + -1, 32, -1, -1, 35, 36, 37, 38, -1, 40, + 41, 42, 43, -1, 45, 46, -1, -1, -1, 5, + 6, 7, -1, 9, 55, 11, -1, 13, 14, 15, + 61, -1, 63, 64, 20, 21, 22, -1, 24, 25, + -1, 27, -1, -1, -1, -1, 32, -1, -1, 35, + 36, 37, 38, -1, 40, 41, 42, 43, -1, 45, + 46, -1, -1, -1, 5, 6, 7, -1, 9, 55, + 11, -1, 13, 14, 15, 61, -1, 63, 64, 20, + 21, 22, -1, 24, 25, -1, 27, -1, -1, -1, + -1, 32, -1, -1, 35, 36, 37, 38, -1, 40, + 41, 42, 43, -1, 45, 46, -1, -1, -1, 5, + 6, 7, -1, 9, 55, 11, -1, 13, 14, 15, + 61, -1, 63, 64, 20, 21, 22, -1, 24, 25, + -1, 27, -1, 5, 6, 7, 32, -1, -1, 35, + 36, 37, 38, -1, 40, 41, 42, 43, -1, 45, + 46, -1, -1, -1, -1, -1, -1, -1, -1, 55, + 32, -1, -1, 35, 36, 37, 38, 63, 64, 41, + 42, 43, -1, 45, 46, 5, 6, 7, -1, -1, + -1, -1, -1, 55, -1, -1, -1, 59, -1, -1, + -1, 63, 64, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 32, -1, -1, 35, 36, 37, 38, -1, + -1, 41, 42, 43, -1, 45, 46, 5, 6, 7, + -1, -1, -1, -1, -1, 55, -1, -1, -1, -1, + 60, -1, -1, 63, 64, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 32, -1, -1, 35, 36, 37, + 38, -1, -1, 41, 42, 43, -1, 45, 46, 5, + 6, 7, -1, -1, -1, -1, -1, 55, -1, -1, + -1, 59, -1, -1, -1, 63, 64, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 32, -1, -1, 35, + 36, 37, 38, -1, -1, 41, 42, 43, -1, 45, + 46, 5, 6, 7, -1, -1, -1, -1, -1, 55, + -1, -1, -1, 59, -1, -1, -1, 63, 64, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 32, -1, + -1, 35, 36, 37, 38, -1, -1, 41, 42, 43, + -1, 45, 46, 5, 6, 7, -1, -1, -1, -1, + -1, 55, 56, -1, -1, -1, -1, -1, -1, 63, + 64, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 32, -1, -1, 35, 36, 37, 38, -1, -1, 41, + 42, 43, -1, 45, 46, 5, 6, 7, -1, -1, + -1, -1, -1, 55, 56, -1, -1, -1, -1, -1, + -1, 63, 64, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 32, -1, -1, 35, 36, 37, 38, -1, + -1, 41, 42, 43, -1, 45, 46, 5, 6, 7, + -1, -1, -1, -1, -1, 55, 56, -1, -1, -1, + -1, -1, -1, 63, 64, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 32, -1, -1, 35, 36, 37, + 38, -1, -1, 41, 42, 43, -1, 45, 46, -1, + -1, 44, 45, 46, 47, 48, 49, 55, 51, 52, + 53, 54, -1, 56, 57, 63, 64, -1, -1, 62, + -1, -1, 65, 66, 67, -1, -1, -1, 71, 72, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 84, 85, 86, 87, 88, 89, 90, -1, -1, + -1, -1, -1, 96, 97, 44, 45, 46, 47, 48, + 49, -1, 51, 52, 53, 54, -1, -1, 57, -1, + 59, -1, -1, 62, -1, -1, 65, 66, 67, -1, + -1, -1, 71, 72, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 84, 85, 86, 87, 88, + 89, 90, -1, -1, -1, -1, -1, 96, 97, 44, + 45, 46, 47, 48, 49, -1, 51, 52, 53, 54, + -1, -1, -1, -1, 59, -1, -1, 62, -1, -1, + 65, 66, 67, -1, -1, -1, 71, 72, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, + 85, 86, 87, 88, 89, 90, -1, -1, -1, -1, + -1, 96, 97, 44, 45, 46, 47, 48, 49, -1, + 51, 52, 53, 54, -1, 56, -1, -1, -1, -1, + -1, 62, -1, -1, 65, 66, 67, -1, -1, -1, + 71, 72, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 84, 85, 86, 87, 88, 89, 90, + -1, -1, -1, -1, -1, 96, 97, 44, 45, 46, + 47, 48, 49, -1, 51, 52, 53, 54, -1, 56, + -1, -1, -1, -1, -1, 62, -1, -1, 65, 66, + 67, -1, -1, -1, 71, 72, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 84, 85, 86, + 87, 88, 89, 90, -1, -1, -1, -1, -1, 96, + 97, 44, 45, 46, 47, 48, 49, -1, 51, 52, + 53, 54, -1, 56, -1, -1, -1, -1, -1, 62, + -1, -1, 65, 66, 67, -1, -1, -1, 71, 72, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 84, 85, 86, 87, 88, 89, 90, -1, -1, + -1, -1, -1, 96, 97, 44, 45, 46, 47, 48, + 49, -1, 51, 52, 53, 54, -1, 56, -1, -1, + -1, -1, -1, 62, -1, -1, 65, 66, 67, -1, + -1, -1, 71, 72, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 84, 85, 86, 87, 88, + 89, 90, -1, -1, -1, -1, -1, 96, 97, 44, + 45, 46, 47, 48, 49, -1, 51, 52, 53, 54, + -1, -1, -1, -1, 59, -1, -1, 62, -1, -1, + 65, 66, 67, -1, -1, -1, 71, 72, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, + 85, 86, 87, 88, 89, 90, -1, -1, -1, -1, + -1, 96, 97, 44, 45, 46, 47, 48, 49, -1, + 51, 52, 53, 54, -1, 56, -1, -1, -1, -1, + -1, 62, -1, -1, 65, 66, 67, -1, -1, -1, + 71, 72, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 84, 85, 86, 87, 88, 89, 90, + -1, -1, -1, -1, -1, 96, 97, 44, 45, 46, + 47, 48, 49, -1, 51, 52, 53, 54, -1, 56, + -1, -1, -1, -1, -1, 62, -1, -1, 65, 66, + 67, -1, -1, -1, 71, 72, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 84, 85, 86, + 87, 88, 89, 90, -1, -1, -1, -1, -1, 96, + 97, 44, 45, 46, 47, 48, 49, -1, 51, 52, + 53, 54, -1, -1, -1, 58, -1, -1, -1, 62, + -1, -1, 65, 66, 67, -1, -1, -1, 71, 72, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 84, 85, 86, 87, 88, 89, 90, -1, -1, + -1, -1, -1, 96, 97, 44, 45, 46, 47, 48, + 49, -1, 51, 52, 53, 54, -1, -1, -1, -1, + 59, -1, -1, 62, -1, -1, 65, 66, 67, -1, + -1, -1, 71, 72, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 84, 85, 86, 87, 88, + 89, 90, -1, -1, -1, -1, -1, 96, 97, 44, + 45, 46, 47, 48, 49, -1, 51, 52, 53, 54, + -1, -1, -1, 58, -1, -1, -1, 62, -1, -1, + 65, 66, 67, -1, -1, -1, 71, 72, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, + 85, 86, 87, 88, 89, 90, -1, -1, -1, -1, + -1, 96, 97, 44, 45, 46, 47, 48, 49, -1, + 51, 52, 53, 54, -1, 56, -1, -1, -1, -1, + -1, 62, -1, -1, 65, 66, 67, -1, -1, -1, + 71, 72, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 84, 85, 86, 87, 88, 89, 90, + -1, -1, -1, -1, -1, 96, 97, 44, 45, 46, + 47, 48, 49, -1, 51, 52, 53, 54, -1, 56, + -1, -1, -1, -1, -1, 62, -1, -1, 65, 66, + 67, -1, -1, -1, 71, 72, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 84, 85, 86, + 87, 88, 89, 90, -1, -1, -1, -1, -1, 96, + 97, 44, 45, 46, 47, 48, 49, -1, 51, 52, + 53, 54, -1, -1, -1, -1, 59, -1, -1, 62, + -1, -1, 65, 66, 67, -1, -1, -1, 71, 72, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 84, 85, 86, 87, 88, 89, 90, -1, -1, + -1, -1, -1, 96, 97, 44, 45, 46, 47, 48, + 49, -1, 51, 52, 53, 54, -1, 56, -1, -1, + -1, -1, -1, 62, -1, -1, 65, 66, 67, -1, + -1, -1, 71, 72, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 84, 85, 86, 87, 88, + 89, 90, -1, -1, -1, -1, -1, 96, 97, 44, + 45, 46, 47, 48, 49, -1, 51, 52, 53, 54, + -1, 56, -1, -1, -1, -1, -1, 62, -1, -1, + 65, 66, 67, -1, -1, -1, 71, 72, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, + 85, 86, 87, 88, 89, 90, -1, -1, -1, -1, + -1, 96, 97, 44, 45, 46, 47, 48, 49, -1, + 51, 52, 53, 54, -1, -1, -1, -1, 59, -1, + -1, 62, -1, -1, 65, 66, 67, -1, -1, -1, + 71, 72, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 84, 85, 86, 87, 88, 89, 90, + -1, -1, -1, -1, -1, 96, 97, 44, 45, 46, + 47, 48, 49, -1, 51, 52, 53, 54, -1, 56, + -1, -1, -1, -1, -1, 62, -1, -1, 65, 66, + 67, -1, -1, -1, 71, 72, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 84, 85, 86, + 87, 88, 89, 90, -1, -1, -1, -1, -1, 96, + 97, 44, 45, 46, 47, 48, 49, -1, 51, 52, + 53, 54, -1, 56, -1, -1, -1, -1, -1, 62, + -1, -1, 65, 66, 67, -1, -1, -1, 71, 72, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 84, 85, 86, 87, 88, 89, 90, -1, -1, + -1, -1, -1, 96, 97, 44, 45, 46, 47, 48, + 49, -1, 51, 52, 53, 54, -1, 56, -1, -1, + -1, -1, -1, 62, -1, -1, 65, 66, 67, -1, + -1, -1, 71, 72, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 84, 85, 86, 87, 88, + 89, 90, -1, -1, -1, -1, -1, 96, 97, 44, + 45, 46, 47, 48, 49, -1, 51, 52, 53, 54, + -1, -1, -1, -1, 59, -1, -1, 62, -1, -1, + 65, 66, 67, -1, -1, -1, 71, 72, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, + 85, 86, 87, 88, 89, 90, -1, -1, -1, -1, + -1, 96, 97, 44, 45, 46, 47, 48, 49, -1, + 51, 52, 53, 54, -1, -1, -1, -1, 59, -1, + -1, 62, -1, -1, 65, 66, 67, -1, -1, -1, + 71, 72, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 84, 85, 86, 87, 88, 89, 90, + -1, -1, -1, -1, -1, 96, 97, 44, 45, 46, + 47, 48, 49, -1, 51, 52, 53, 54, -1, -1, + -1, -1, 59, -1, -1, 62, -1, -1, 65, 66, + 67, -1, -1, -1, 71, 72, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 84, 85, 86, + 87, 88, 89, 90, -1, -1, -1, -1, -1, 96, + 97, 44, 45, 46, 47, 48, 49, -1, 51, 52, + 53, 54, -1, -1, -1, -1, 59, -1, -1, 62, + -1, -1, 65, 66, 67, -1, -1, -1, 71, 72, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 84, 85, 86, 87, 88, 89, 90, -1, -1, + -1, -1, -1, 96, 97, 44, 45, 46, 47, 48, + 49, -1, 51, 52, 53, 54, -1, -1, -1, -1, + 59, -1, -1, 62, -1, -1, 65, 66, 67, -1, + -1, -1, 71, 72, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 84, 85, 86, 87, 88, + 89, 90, -1, -1, -1, -1, -1, 96, 97, 44, + 45, 46, 47, 48, 49, -1, 51, 52, 53, 54, + -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, + 65, 66, 67, -1, -1, -1, 71, 72, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 84, + 85, 86, 87, 88, 89, 90, -1, -1, -1, -1, + -1, 96, 97, 44, 45, 46, 47, 48, 49, -1, + 51, 52, 53, 54, -1, -1, -1, -1, -1, -1, + -1, 62, -1, -1, 65, 66, 67, -1, -1, -1, + 71, 72, 44, 45, 46, 47, 48, 49, -1, 51, + 52, 53, 54, 84, 85, 86, 87, 88, 89, 90, + 62, -1, -1, 65, 66, 67, 97, -1, -1, 71, + 72, 44, 45, 46, 47, 48, 49, -1, 51, 52, + 53, 54, 84, 85, 86, 87, 88, -1, 90, 62, + -1, -1, 65, 66, 67, 97, -1, -1, 71, 72, + 44, 45, 46, 47, 48, 49, -1, 51, -1, 53, + 54, 84, 85, 86, 87, -1, -1, 90, 62, -1, + -1, 65, 66, 67, 97, -1, -1, 71, 72, 44, + 45, 46, 47, 48, 49, -1, 51, -1, 53, 54, + 84, 85, 86, 87, -1, -1, 90, -1, -1, -1, + 65, 66, 67, 97, -1, -1, 71, 72, 44, 45, + 46, 47, 48, 49, -1, 51, -1, -1, 54, 84, + 85, 86, 87, -1, -1, 90, -1, -1, -1, 65, + 66, 67, 97, -1, -1, 71, 72, 44, 45, 46, + 47, 48, 49, -1, 51, -1, -1, 54, 84, 85, + 86, 87, -1, -1, 90, -1, -1, -1, 65, 66, + 67, 97, -1, -1, 71, 72, 44, 45, 46, 47, + -1, -1, -1, 51, -1, -1, 54, 50, -1, 86, + 87, -1, -1, 90, -1, -1, -1, 65, 66, 67, + 97, -1, -1, 71, 72, 68, 69, 50, -1, -1, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + -1, -1, 90, -1, -1, 68, 69, -1, -1, 97, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + 44, 45, 46, 47, -1, -1, -1, 51, -1, -1, + 54, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 66, 67, -1, -1, -1, 71, 72 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 101, 102, 0, 3, 5, 6, 7, 9, 11, + 13, 14, 15, 20, 21, 22, 24, 25, 27, 29, + 32, 35, 36, 37, 38, 40, 41, 42, 43, 45, + 46, 55, 63, 64, 103, 104, 107, 108, 111, 112, + 119, 122, 123, 124, 125, 126, 127, 128, 129, 132, + 133, 134, 38, 38, 55, 130, 130, 59, 59, 55, + 7, 36, 59, 127, 132, 55, 60, 107, 118, 55, + 55, 55, 130, 55, 55, 38, 55, 50, 50, 68, + 69, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 92, 131, 55, 91, 127, 127, 127, 127, 127, + 59, 44, 45, 46, 47, 48, 49, 51, 52, 53, + 54, 62, 65, 66, 67, 71, 72, 84, 85, 86, + 87, 88, 89, 90, 96, 97, 50, 131, 55, 91, + 127, 55, 55, 127, 59, 127, 106, 14, 59, 127, + 37, 37, 55, 127, 127, 60, 127, 127, 127, 127, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 140, 127, 135, 136, 38, 56, 127, 127, 127, 127, + 127, 127, 38, 127, 127, 127, 127, 127, 130, 130, + 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, + 127, 60, 127, 37, 109, 110, 38, 56, 92, 114, + 127, 114, 56, 56, 61, 107, 55, 59, 127, 59, + 23, 23, 127, 56, 56, 105, 108, 56, 57, 57, + 59, 57, 99, 56, 57, 55, 55, 92, 58, 136, + 56, 57, 55, 114, 58, 113, 113, 118, 118, 127, + 56, 127, 59, 59, 127, 127, 127, 113, 60, 60, + 61, 108, 41, 127, 127, 50, 131, 127, 135, 135, + 140, 127, 61, 60, 37, 109, 99, 38, 57, 115, + 115, 8, 56, 118, 56, 56, 127, 56, 127, 59, + 56, 56, 56, 26, 120, 120, 59, 56, 59, 127, + 56, 56, 99, 106, 56, 113, 136, 56, 56, 118, + 118, 118, 56, 118, 56, 56, 127, 118, 118, 60, + 121, 127, 61, 61, 61, 60, 115, 60, 60, 118, + 118, 118, 56, 24, 38, 39, 137, 138, 139, 28, + 58, 106, 56, 112, 116, 117, 138, 116, 118, 50, + 50, 92, 38, 61, 139, 127, 106, 61, 60, 59, + 61, 112, 117, 61, 127, 127, 140, 50, 92, 59, + 19, 120, 116, 59, 59, 59, 99, 127, 140, 58, + 61, 50, 59, 99, 106, 127, 50, 59, 127, 59 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK (1); \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (YYID (N)) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (YYID (0)) +#endif + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL +# define YY_LOCATION_PRINT(File, Loc) \ + fprintf (File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (YYLEX_PARAM) +#else +# define YYLEX yylex () +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *bottom, yytype_int16 *top) +#else +static void +yy_stack_print (bottom, top) + yytype_int16 *bottom; + yytype_int16 *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, int yyrule) +#else +static void +yy_reduce_print (yyvsp, yyrule) + YYSTYPE *yyvsp; + int yyrule; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + fprintf (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + ); + fprintf (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, Rule); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into YYRESULT an error message about the unexpected token + YYCHAR while in state YYSTATE. Return the number of bytes copied, + including the terminating null byte. If YYRESULT is null, do not + copy anything; just return the number of bytes that would be + copied. As a special case, return 0 if an ordinary "syntax error" + message will do. Return YYSIZE_MAXIMUM if overflow occurs during + size calculation. */ +static YYSIZE_T +yysyntax_error (char *yyresult, int yystate, int yychar) +{ + int yyn = yypact[yystate]; + + if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) + return 0; + else + { + int yytype = YYTRANSLATE (yychar); + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + int yysize_overflow = 0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + int yyx; + +# if 0 + /* This is so xgettext sees the translatable formats that are + constructed on the fly. */ + YY_("syntax error, unexpected %s"); + YY_("syntax error, unexpected %s, expecting %s"); + YY_("syntax error, unexpected %s, expecting %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); +# endif + char *yyfmt; + char const *yyf; + static char const yyunexpected[] = "syntax error, unexpected %s"; + static char const yyexpecting[] = ", expecting %s"; + static char const yyor[] = " or %s"; + char yyformat[sizeof yyunexpected + + sizeof yyexpecting - 1 + + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) + * (sizeof yyor - 1))]; + char const *yyprefix = yyexpecting; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 1; + + yyarg[0] = yytname[yytype]; + yyfmt = yystpcpy (yyformat, yyunexpected); + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + yyformat[sizeof yyunexpected - 1] = '\0'; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + yyfmt = yystpcpy (yyfmt, yyprefix); + yyprefix = yyor; + } + + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + + if (yysize_overflow) + return YYSIZE_MAXIMUM; + + if (yyresult) + { + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + char *yyp = yyresult; + int yyi = 0; + while ((*yyp = *yyf) != '\0') + { + if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyf += 2; + } + else + { + yyp++; + yyf++; + } + } + } + return yysize; + } +} +#endif /* YYERROR_VERBOSE */ + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yymsg, yytype, yyvaluep) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + YYUSE (yyvaluep); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + +/* The look-ahead symbol. */ +int yychar; + +/* The semantic value of the look-ahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + + int yystate; + int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Look-ahead token as an internal (translated) token number. */ + int yytoken = 0; +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss = yyssa; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp; + + + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + look-ahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to look-ahead token. */ + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a look-ahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the look-ahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +#line 162 "CMDgram.y" + { ;} + break; + + case 3: +#line 167 "CMDgram.y" + { (yyval.stmt) = nil; ;} + break; + + case 4: +#line 169 "CMDgram.y" + { if(!gStatementList) { gStatementList = (yyvsp[(2) - (2)].stmt); } else { gStatementList->append((yyvsp[(2) - (2)].stmt)); } ;} + break; + + case 5: +#line 174 "CMDgram.y" + { (yyval.stmt) = (yyvsp[(1) - (1)].stmt); ;} + break; + + case 6: +#line 176 "CMDgram.y" + { (yyval.stmt) = (yyvsp[(1) - (1)].stmt); ;} + break; + + case 7: +#line 178 "CMDgram.y" + { (yyval.stmt) = (yyvsp[(1) - (1)].stmt); ;} + break; + + case 8: +#line 183 "CMDgram.y" + { (yyval.stmt) = (yyvsp[(4) - (6)].stmt); for(StmtNode *walk = ((yyvsp[(4) - (6)].stmt));walk;walk = walk->getNext() ) walk->setPackage((yyvsp[(2) - (6)].s).value); ;} + break; + + case 9: +#line 188 "CMDgram.y" + { (yyval.stmt) = (yyvsp[(1) - (1)].stmt); ;} + break; + + case 10: +#line 190 "CMDgram.y" + { (yyval.stmt) = (yyvsp[(1) - (2)].stmt); ((yyvsp[(1) - (2)].stmt))->append((yyvsp[(2) - (2)].stmt)); ;} + break; + + case 11: +#line 195 "CMDgram.y" + { (yyval.stmt) = nil; ;} + break; + + case 12: +#line 197 "CMDgram.y" + { if(!(yyvsp[(1) - (2)].stmt)) { (yyval.stmt) = (yyvsp[(2) - (2)].stmt); } else { ((yyvsp[(1) - (2)].stmt))->append((yyvsp[(2) - (2)].stmt)); (yyval.stmt) = (yyvsp[(1) - (2)].stmt); } ;} + break; + + case 19: +#line 208 "CMDgram.y" + { (yyval.stmt) = BreakStmtNode::alloc( (yyvsp[(1) - (2)].i).lineNumber ); ;} + break; + + case 20: +#line 210 "CMDgram.y" + { (yyval.stmt) = ContinueStmtNode::alloc( (yyvsp[(1) - (2)].i).lineNumber ); ;} + break; + + case 21: +#line 212 "CMDgram.y" + { (yyval.stmt) = ReturnStmtNode::alloc( (yyvsp[(1) - (2)].i).lineNumber, NULL ); ;} + break; + + case 22: +#line 214 "CMDgram.y" + { (yyval.stmt) = ReturnStmtNode::alloc( (yyvsp[(1) - (3)].i).lineNumber, (yyvsp[(2) - (3)].expr) ); ;} + break; + + case 23: +#line 216 "CMDgram.y" + { (yyval.stmt) = (yyvsp[(1) - (2)].stmt); ;} + break; + + case 24: +#line 218 "CMDgram.y" + { (yyval.stmt) = TTagSetStmtNode::alloc( (yyvsp[(1) - (4)].s).lineNumber, (yyvsp[(1) - (4)].s).value, (yyvsp[(3) - (4)].expr), NULL ); ;} + break; + + case 25: +#line 220 "CMDgram.y" + { (yyval.stmt) = TTagSetStmtNode::alloc( (yyvsp[(1) - (6)].s).lineNumber, (yyvsp[(1) - (6)].s).value, (yyvsp[(3) - (6)].expr), (yyvsp[(5) - (6)].expr) ); ;} + break; + + case 26: +#line 222 "CMDgram.y" + { (yyval.stmt) = StrConstNode::alloc( (yyvsp[(1) - (1)].str).lineNumber, (yyvsp[(1) - (1)].str).value, false, true ); ;} + break; + + case 27: +#line 227 "CMDgram.y" + { (yyval.stmt) = FunctionDeclStmtNode::alloc( (yyvsp[(1) - (8)].i).lineNumber, (yyvsp[(2) - (8)].s).value, NULL, (yyvsp[(4) - (8)].var), (yyvsp[(7) - (8)].stmt) ); ;} + break; + + case 28: +#line 229 "CMDgram.y" + { (yyval.stmt) = FunctionDeclStmtNode::alloc( (yyvsp[(1) - (10)].i).lineNumber, (yyvsp[(4) - (10)].s).value, (yyvsp[(2) - (10)].s).value, (yyvsp[(6) - (10)].var), (yyvsp[(9) - (10)].stmt) ); ;} + break; + + case 29: +#line 234 "CMDgram.y" + { (yyval.var) = NULL; ;} + break; + + case 30: +#line 236 "CMDgram.y" + { (yyval.var) = (yyvsp[(1) - (1)].var); ;} + break; + + case 31: +#line 241 "CMDgram.y" + { (yyval.var) = VarNode::alloc( (yyvsp[(1) - (1)].s).lineNumber, (yyvsp[(1) - (1)].s).value, NULL ); ;} + break; + + case 32: +#line 243 "CMDgram.y" + { (yyval.var) = (yyvsp[(1) - (3)].var); ((StmtNode*)((yyvsp[(1) - (3)].var)))->append((StmtNode*)VarNode::alloc( (yyvsp[(3) - (3)].s).lineNumber, (yyvsp[(3) - (3)].s).value, NULL ) ); ;} + break; + + case 33: +#line 248 "CMDgram.y" + { (yyval.stmt) = ObjectDeclNode::alloc( (yyvsp[(1) - (10)].i).lineNumber, (yyvsp[(2) - (10)].expr), (yyvsp[(4) - (10)].expr), NULL, (yyvsp[(5) - (10)].s).value, (yyvsp[(8) - (10)].slist), NULL, true, false, false); ;} + break; + + case 34: +#line 253 "CMDgram.y" + { (yyval.od) = ObjectDeclNode::alloc( (yyvsp[(1) - (10)].i).lineNumber, (yyvsp[(2) - (10)].expr), (yyvsp[(4) - (10)].expr), (yyvsp[(6) - (10)].expr), (yyvsp[(5) - (10)].s).value, (yyvsp[(9) - (10)].odcl).slots, (yyvsp[(9) - (10)].odcl).decls, false, false, false); ;} + break; + + case 35: +#line 255 "CMDgram.y" + { (yyval.od) = ObjectDeclNode::alloc( (yyvsp[(1) - (7)].i).lineNumber, (yyvsp[(2) - (7)].expr), (yyvsp[(4) - (7)].expr), (yyvsp[(6) - (7)].expr), (yyvsp[(5) - (7)].s).value, NULL, NULL, false, false, false); ;} + break; + + case 36: +#line 257 "CMDgram.y" + { (yyval.od) = ObjectDeclNode::alloc( (yyvsp[(1) - (12)].i).lineNumber, (yyvsp[(2) - (12)].expr), (yyvsp[(5) - (12)].expr), (yyvsp[(8) - (12)].expr), (yyvsp[(7) - (12)].s).value, (yyvsp[(11) - (12)].odcl).slots, (yyvsp[(11) - (12)].odcl).decls, false, true, false); ;} + break; + + case 37: +#line 259 "CMDgram.y" + { (yyval.od) = ObjectDeclNode::alloc( (yyvsp[(1) - (9)].i).lineNumber, (yyvsp[(2) - (9)].expr), (yyvsp[(5) - (9)].expr), (yyvsp[(8) - (9)].expr), (yyvsp[(7) - (9)].s).value, NULL, NULL, false, true, false); ;} + break; + + case 38: +#line 261 "CMDgram.y" + { (yyval.od) = ObjectDeclNode::alloc( (yyvsp[(1) - (10)].i).lineNumber, (yyvsp[(2) - (10)].expr), (yyvsp[(4) - (10)].expr), (yyvsp[(6) - (10)].expr), (yyvsp[(5) - (10)].s).value, (yyvsp[(9) - (10)].odcl).slots, (yyvsp[(9) - (10)].odcl).decls, false, false, true); ;} + break; + + case 39: +#line 263 "CMDgram.y" + { (yyval.od) = ObjectDeclNode::alloc( (yyvsp[(1) - (7)].i).lineNumber, (yyvsp[(2) - (7)].expr), (yyvsp[(4) - (7)].expr), (yyvsp[(6) - (7)].expr), (yyvsp[(5) - (7)].s).value, NULL, NULL, false, false, true); ;} + break; + + case 40: +#line 268 "CMDgram.y" + { (yyval.s).value = NULL; ;} + break; + + case 41: +#line 270 "CMDgram.y" + { (yyval.s) = (yyvsp[(2) - (2)].s); ;} + break; + + case 42: +#line 275 "CMDgram.y" + { (yyval.expr) = StrConstNode::alloc( CodeBlock::smCurrentParser->getCurrentLine(), "", false); ;} + break; + + case 43: +#line 277 "CMDgram.y" + { (yyval.expr) = (yyvsp[(1) - (1)].expr); ;} + break; + + case 44: +#line 282 "CMDgram.y" + { (yyval.expr) = NULL; ;} + break; + + case 45: +#line 284 "CMDgram.y" + { (yyval.expr) = (yyvsp[(2) - (2)].expr); ;} + break; + + case 46: +#line 289 "CMDgram.y" + { (yyval.odcl).slots = NULL; (yyval.odcl).decls = NULL; ;} + break; + + case 47: +#line 291 "CMDgram.y" + { (yyval.odcl).slots = (yyvsp[(1) - (1)].slist); (yyval.odcl).decls = NULL; ;} + break; + + case 48: +#line 293 "CMDgram.y" + { (yyval.odcl).slots = NULL; (yyval.odcl).decls = (yyvsp[(1) - (1)].od); ;} + break; + + case 49: +#line 295 "CMDgram.y" + { (yyval.odcl).slots = (yyvsp[(1) - (2)].slist); (yyval.odcl).decls = (yyvsp[(2) - (2)].od); ;} + break; + + case 50: +#line 300 "CMDgram.y" + { (yyval.od) = (yyvsp[(1) - (2)].od); ;} + break; + + case 51: +#line 302 "CMDgram.y" + { (yyvsp[(1) - (3)].od)->append((yyvsp[(2) - (3)].od)); (yyval.od) = (yyvsp[(1) - (3)].od); ;} + break; + + case 52: +#line 307 "CMDgram.y" + { (yyval.stmt) = (yyvsp[(2) - (3)].stmt); ;} + break; + + case 53: +#line 309 "CMDgram.y" + { (yyval.stmt) = (yyvsp[(1) - (1)].stmt); ;} + break; + + case 54: +#line 314 "CMDgram.y" + { (yyval.stmt) = (yyvsp[(6) - (7)].ifnode); (yyvsp[(6) - (7)].ifnode)->propagateSwitchExpr((yyvsp[(3) - (7)].expr), false); ;} + break; + + case 55: +#line 316 "CMDgram.y" + { (yyval.stmt) = (yyvsp[(6) - (7)].ifnode); (yyvsp[(6) - (7)].ifnode)->propagateSwitchExpr((yyvsp[(3) - (7)].expr), true); ;} + break; + + case 56: +#line 321 "CMDgram.y" + { (yyval.ifnode) = IfStmtNode::alloc( (yyvsp[(1) - (4)].i).lineNumber, (yyvsp[(2) - (4)].expr), (yyvsp[(4) - (4)].stmt), NULL, false); ;} + break; + + case 57: +#line 323 "CMDgram.y" + { (yyval.ifnode) = IfStmtNode::alloc( (yyvsp[(1) - (7)].i).lineNumber, (yyvsp[(2) - (7)].expr), (yyvsp[(4) - (7)].stmt), (yyvsp[(7) - (7)].stmt), false); ;} + break; + + case 58: +#line 325 "CMDgram.y" + { (yyval.ifnode) = IfStmtNode::alloc( (yyvsp[(1) - (5)].i).lineNumber, (yyvsp[(2) - (5)].expr), (yyvsp[(4) - (5)].stmt), (yyvsp[(5) - (5)].ifnode), true); ;} + break; + + case 59: +#line 330 "CMDgram.y" + { (yyval.expr) = (yyvsp[(1) - (1)].expr);;} + break; + + case 60: +#line 332 "CMDgram.y" + { ((yyvsp[(1) - (3)].expr))->append((yyvsp[(3) - (3)].expr)); (yyval.expr)=(yyvsp[(1) - (3)].expr); ;} + break; + + case 61: +#line 337 "CMDgram.y" + { (yyval.stmt) = IfStmtNode::alloc((yyvsp[(1) - (5)].i).lineNumber, (yyvsp[(3) - (5)].expr), (yyvsp[(5) - (5)].stmt), NULL, false); ;} + break; + + case 62: +#line 339 "CMDgram.y" + { (yyval.stmt) = IfStmtNode::alloc((yyvsp[(1) - (7)].i).lineNumber, (yyvsp[(3) - (7)].expr), (yyvsp[(5) - (7)].stmt), (yyvsp[(7) - (7)].stmt), false); ;} + break; + + case 63: +#line 344 "CMDgram.y" + { (yyval.stmt) = LoopStmtNode::alloc((yyvsp[(1) - (5)].i).lineNumber, nil, (yyvsp[(3) - (5)].expr), nil, (yyvsp[(5) - (5)].stmt), false); ;} + break; + + case 64: +#line 346 "CMDgram.y" + { (yyval.stmt) = LoopStmtNode::alloc((yyvsp[(3) - (6)].i).lineNumber, nil, (yyvsp[(5) - (6)].expr), nil, (yyvsp[(2) - (6)].stmt), true); ;} + break; + + case 65: +#line 351 "CMDgram.y" + { (yyval.stmt) = LoopStmtNode::alloc((yyvsp[(1) - (9)].i).lineNumber, (yyvsp[(3) - (9)].expr), (yyvsp[(5) - (9)].expr), (yyvsp[(7) - (9)].expr), (yyvsp[(9) - (9)].stmt), false); ;} + break; + + case 66: +#line 353 "CMDgram.y" + { (yyval.stmt) = LoopStmtNode::alloc((yyvsp[(1) - (8)].i).lineNumber, (yyvsp[(3) - (8)].expr), (yyvsp[(5) - (8)].expr), NULL, (yyvsp[(8) - (8)].stmt), false); ;} + break; + + case 67: +#line 355 "CMDgram.y" + { (yyval.stmt) = LoopStmtNode::alloc((yyvsp[(1) - (8)].i).lineNumber, (yyvsp[(3) - (8)].expr), NULL, (yyvsp[(6) - (8)].expr), (yyvsp[(8) - (8)].stmt), false); ;} + break; + + case 68: +#line 357 "CMDgram.y" + { (yyval.stmt) = LoopStmtNode::alloc((yyvsp[(1) - (7)].i).lineNumber, (yyvsp[(3) - (7)].expr), NULL, NULL, (yyvsp[(7) - (7)].stmt), false); ;} + break; + + case 69: +#line 359 "CMDgram.y" + { (yyval.stmt) = LoopStmtNode::alloc((yyvsp[(1) - (8)].i).lineNumber, NULL, (yyvsp[(4) - (8)].expr), (yyvsp[(6) - (8)].expr), (yyvsp[(8) - (8)].stmt), false); ;} + break; + + case 70: +#line 361 "CMDgram.y" + { (yyval.stmt) = LoopStmtNode::alloc((yyvsp[(1) - (7)].i).lineNumber, NULL, (yyvsp[(4) - (7)].expr), NULL, (yyvsp[(7) - (7)].stmt), false); ;} + break; + + case 71: +#line 363 "CMDgram.y" + { (yyval.stmt) = LoopStmtNode::alloc((yyvsp[(1) - (7)].i).lineNumber, NULL, NULL, (yyvsp[(5) - (7)].expr), (yyvsp[(7) - (7)].stmt), false); ;} + break; + + case 72: +#line 365 "CMDgram.y" + { (yyval.stmt) = LoopStmtNode::alloc((yyvsp[(1) - (6)].i).lineNumber, NULL, NULL, NULL, (yyvsp[(6) - (6)].stmt), false); ;} + break; + + case 73: +#line 370 "CMDgram.y" + { (yyval.stmt) = IterStmtNode::alloc( (yyvsp[(1) - (7)].i).lineNumber, (yyvsp[(3) - (7)].s).value, (yyvsp[(5) - (7)].expr), (yyvsp[(7) - (7)].stmt), false ); ;} + break; + + case 74: +#line 372 "CMDgram.y" + { (yyval.stmt) = IterStmtNode::alloc( (yyvsp[(1) - (7)].i).lineNumber, (yyvsp[(3) - (7)].s).value, (yyvsp[(5) - (7)].expr), (yyvsp[(7) - (7)].stmt), true ); ;} + break; + + case 75: +#line 377 "CMDgram.y" + { (yyval.stmt) = (yyvsp[(1) - (1)].expr); ;} + break; + + case 76: +#line 382 "CMDgram.y" + { (yyval.expr) = (yyvsp[(1) - (1)].expr); ;} + break; + + case 77: +#line 384 "CMDgram.y" + { (yyval.expr) = (yyvsp[(2) - (3)].expr); ;} + break; + + case 78: +#line 386 "CMDgram.y" + { (yyval.expr) = IntBinaryExprNode::alloc( (yyvsp[(1) - (3)].expr)->dbgLineNumber, (yyvsp[(2) - (3)].i).value, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); ;} + break; + + case 79: +#line 388 "CMDgram.y" + { (yyval.expr) = IntBinaryExprNode::alloc( (yyvsp[(1) - (3)].expr)->dbgLineNumber, (yyvsp[(2) - (3)].i).value, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); ;} + break; + + case 80: +#line 390 "CMDgram.y" + { (yyval.expr) = IntBinaryExprNode::alloc( (yyvsp[(1) - (3)].expr)->dbgLineNumber, (yyvsp[(2) - (3)].i).value, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); ;} + break; + + case 81: +#line 392 "CMDgram.y" + { (yyval.expr) = IntBinaryExprNode::alloc( (yyvsp[(1) - (3)].expr)->dbgLineNumber, (yyvsp[(2) - (3)].i).value, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); ;} + break; + + case 82: +#line 394 "CMDgram.y" + { (yyval.expr) = FloatBinaryExprNode::alloc( (yyvsp[(1) - (3)].expr)->dbgLineNumber, (yyvsp[(2) - (3)].i).value, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); ;} + break; + + case 83: +#line 396 "CMDgram.y" + { (yyval.expr) = FloatBinaryExprNode::alloc( (yyvsp[(1) - (3)].expr)->dbgLineNumber, (yyvsp[(2) - (3)].i).value, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); ;} + break; + + case 84: +#line 398 "CMDgram.y" + { (yyval.expr) = FloatBinaryExprNode::alloc( (yyvsp[(1) - (3)].expr)->dbgLineNumber, (yyvsp[(2) - (3)].i).value, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); ;} + break; + + case 85: +#line 400 "CMDgram.y" + { (yyval.expr) = FloatBinaryExprNode::alloc( (yyvsp[(1) - (3)].expr)->dbgLineNumber, (yyvsp[(2) - (3)].i).value, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); ;} + break; + + case 86: +#line 402 "CMDgram.y" + { (yyval.expr) = FloatUnaryExprNode::alloc( (yyvsp[(1) - (2)].i).lineNumber, (yyvsp[(1) - (2)].i).value, (yyvsp[(2) - (2)].expr)); ;} + break; + + case 87: +#line 404 "CMDgram.y" + { (yyval.expr) = TTagDerefNode::alloc( (yyvsp[(1) - (2)].i).lineNumber, (yyvsp[(2) - (2)].expr) ); ;} + break; + + case 88: +#line 406 "CMDgram.y" + { (yyval.expr) = TTagExprNode::alloc( (yyvsp[(1) - (1)].s).lineNumber, (yyvsp[(1) - (1)].s).value ); ;} + break; + + case 89: +#line 408 "CMDgram.y" + { (yyval.expr) = ConditionalExprNode::alloc( (yyvsp[(1) - (5)].expr)->dbgLineNumber, (yyvsp[(1) - (5)].expr), (yyvsp[(3) - (5)].expr), (yyvsp[(5) - (5)].expr)); ;} + break; + + case 90: +#line 410 "CMDgram.y" + { (yyval.expr) = IntBinaryExprNode::alloc( (yyvsp[(1) - (3)].expr)->dbgLineNumber, (yyvsp[(2) - (3)].i).value, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); ;} + break; + + case 91: +#line 412 "CMDgram.y" + { (yyval.expr) = IntBinaryExprNode::alloc( (yyvsp[(1) - (3)].expr)->dbgLineNumber, (yyvsp[(2) - (3)].i).value, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); ;} + break; + + case 92: +#line 414 "CMDgram.y" + { (yyval.expr) = IntBinaryExprNode::alloc( (yyvsp[(1) - (3)].expr)->dbgLineNumber, (yyvsp[(2) - (3)].i).value, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); ;} + break; + + case 93: +#line 416 "CMDgram.y" + { (yyval.expr) = IntBinaryExprNode::alloc( (yyvsp[(1) - (3)].expr)->dbgLineNumber, (yyvsp[(2) - (3)].i).value, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); ;} + break; + + case 94: +#line 418 "CMDgram.y" + { (yyval.expr) = IntBinaryExprNode::alloc( (yyvsp[(1) - (3)].expr)->dbgLineNumber, (yyvsp[(2) - (3)].i).value, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); ;} + break; + + case 95: +#line 420 "CMDgram.y" + { (yyval.expr) = IntBinaryExprNode::alloc( (yyvsp[(1) - (3)].expr)->dbgLineNumber, (yyvsp[(2) - (3)].i).value, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); ;} + break; + + case 96: +#line 422 "CMDgram.y" + { (yyval.expr) = IntBinaryExprNode::alloc( (yyvsp[(1) - (3)].expr)->dbgLineNumber, (yyvsp[(2) - (3)].i).value, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); ;} + break; + + case 97: +#line 424 "CMDgram.y" + { (yyval.expr) = IntBinaryExprNode::alloc( (yyvsp[(1) - (3)].expr)->dbgLineNumber, (yyvsp[(2) - (3)].i).value, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); ;} + break; + + case 98: +#line 426 "CMDgram.y" + { (yyval.expr) = IntBinaryExprNode::alloc( (yyvsp[(1) - (3)].expr)->dbgLineNumber, (yyvsp[(2) - (3)].i).value, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); ;} + break; + + case 99: +#line 428 "CMDgram.y" + { (yyval.expr) = IntBinaryExprNode::alloc( (yyvsp[(1) - (3)].expr)->dbgLineNumber, (yyvsp[(2) - (3)].i).value, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); ;} + break; + + case 100: +#line 430 "CMDgram.y" + { (yyval.expr) = StreqExprNode::alloc( (yyvsp[(1) - (3)].expr)->dbgLineNumber, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr), true); ;} + break; + + case 101: +#line 432 "CMDgram.y" + { (yyval.expr) = StreqExprNode::alloc( (yyvsp[(1) - (3)].expr)->dbgLineNumber, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr), false); ;} + break; + + case 102: +#line 434 "CMDgram.y" + { (yyval.expr) = StrcatExprNode::alloc( (yyvsp[(1) - (3)].expr)->dbgLineNumber, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr), (yyvsp[(2) - (3)].i).value); ;} + break; + + case 103: +#line 436 "CMDgram.y" + { (yyval.expr) = IntUnaryExprNode::alloc((yyvsp[(1) - (2)].i).lineNumber, (yyvsp[(1) - (2)].i).value, (yyvsp[(2) - (2)].expr)); ;} + break; + + case 104: +#line 438 "CMDgram.y" + { (yyval.expr) = IntUnaryExprNode::alloc((yyvsp[(1) - (2)].i).lineNumber, (yyvsp[(1) - (2)].i).value, (yyvsp[(2) - (2)].expr)); ;} + break; + + case 105: +#line 440 "CMDgram.y" + { (yyval.expr) = StrConstNode::alloc( (yyvsp[(1) - (1)].str).lineNumber, (yyvsp[(1) - (1)].str).value, true); ;} + break; + + case 106: +#line 442 "CMDgram.y" + { (yyval.expr) = FloatNode::alloc( (yyvsp[(1) - (1)].f).lineNumber, (yyvsp[(1) - (1)].f).value ); ;} + break; + + case 107: +#line 444 "CMDgram.y" + { (yyval.expr) = IntNode::alloc( (yyvsp[(1) - (1)].i).lineNumber, (yyvsp[(1) - (1)].i).value ); ;} + break; + + case 108: +#line 446 "CMDgram.y" + { (yyval.expr) = ConstantNode::alloc( (yyvsp[(1) - (1)].i).lineNumber, StringTable->insert("break")); ;} + break; + + case 109: +#line 448 "CMDgram.y" + { (yyval.expr) = SlotAccessNode::alloc( (yyvsp[(1) - (1)].slot).lineNumber, (yyvsp[(1) - (1)].slot).object, (yyvsp[(1) - (1)].slot).array, (yyvsp[(1) - (1)].slot).slotName ); ;} + break; + + case 110: +#line 450 "CMDgram.y" + { (yyval.expr) = InternalSlotAccessNode::alloc( (yyvsp[(1) - (1)].intslot).lineNumber, (yyvsp[(1) - (1)].intslot).object, (yyvsp[(1) - (1)].intslot).slotExpr, (yyvsp[(1) - (1)].intslot).recurse); ;} + break; + + case 111: +#line 452 "CMDgram.y" + { (yyval.expr) = ConstantNode::alloc( (yyvsp[(1) - (1)].s).lineNumber, (yyvsp[(1) - (1)].s).value ); ;} + break; + + case 112: +#line 454 "CMDgram.y" + { (yyval.expr) = StrConstNode::alloc( (yyvsp[(1) - (1)].str).lineNumber, (yyvsp[(1) - (1)].str).value, false); ;} + break; + + case 113: +#line 456 "CMDgram.y" + { (yyval.expr) = (ExprNode*)VarNode::alloc( (yyvsp[(1) - (1)].s).lineNumber, (yyvsp[(1) - (1)].s).value, NULL); ;} + break; + + case 114: +#line 458 "CMDgram.y" + { (yyval.expr) = (ExprNode*)VarNode::alloc( (yyvsp[(1) - (4)].s).lineNumber, (yyvsp[(1) - (4)].s).value, (yyvsp[(3) - (4)].expr) ); ;} + break; + + case 115: +#line 463 "CMDgram.y" + { (yyval.slot).lineNumber = (yyvsp[(1) - (3)].expr)->dbgLineNumber; (yyval.slot).object = (yyvsp[(1) - (3)].expr); (yyval.slot).slotName = (yyvsp[(3) - (3)].s).value; (yyval.slot).array = NULL; ;} + break; + + case 116: +#line 465 "CMDgram.y" + { (yyval.slot).lineNumber = (yyvsp[(1) - (6)].expr)->dbgLineNumber; (yyval.slot).object = (yyvsp[(1) - (6)].expr); (yyval.slot).slotName = (yyvsp[(3) - (6)].s).value; (yyval.slot).array = (yyvsp[(5) - (6)].expr); ;} + break; + + case 117: +#line 470 "CMDgram.y" + { (yyval.intslot).lineNumber = (yyvsp[(1) - (3)].expr)->dbgLineNumber; (yyval.intslot).object = (yyvsp[(1) - (3)].expr); (yyval.intslot).slotExpr = (yyvsp[(3) - (3)].expr); (yyval.intslot).recurse = false; ;} + break; + + case 118: +#line 472 "CMDgram.y" + { (yyval.intslot).lineNumber = (yyvsp[(1) - (3)].expr)->dbgLineNumber; (yyval.intslot).object = (yyvsp[(1) - (3)].expr); (yyval.intslot).slotExpr = (yyvsp[(3) - (3)].expr); (yyval.intslot).recurse = true; ;} + break; + + case 119: +#line 477 "CMDgram.y" + { (yyval.expr) = ConstantNode::alloc( (yyvsp[(1) - (1)].s).lineNumber, (yyvsp[(1) - (1)].s).value ); ;} + break; + + case 120: +#line 479 "CMDgram.y" + { (yyval.expr) = (yyvsp[(2) - (3)].expr); ;} + break; + + case 121: +#line 484 "CMDgram.y" + { (yyval.asn).lineNumber = (yyvsp[(1) - (1)].i).lineNumber; (yyval.asn).token = '+'; (yyval.asn).expr = FloatNode::alloc( (yyvsp[(1) - (1)].i).lineNumber, 1 ); ;} + break; + + case 122: +#line 486 "CMDgram.y" + { (yyval.asn).lineNumber = (yyvsp[(1) - (1)].i).lineNumber; (yyval.asn).token = '-'; (yyval.asn).expr = FloatNode::alloc( (yyvsp[(1) - (1)].i).lineNumber, 1 ); ;} + break; + + case 123: +#line 488 "CMDgram.y" + { (yyval.asn).lineNumber = (yyvsp[(1) - (2)].i).lineNumber; (yyval.asn).token = '+'; (yyval.asn).expr = (yyvsp[(2) - (2)].expr); ;} + break; + + case 124: +#line 490 "CMDgram.y" + { (yyval.asn).lineNumber = (yyvsp[(1) - (2)].i).lineNumber; (yyval.asn).token = '-'; (yyval.asn).expr = (yyvsp[(2) - (2)].expr); ;} + break; + + case 125: +#line 492 "CMDgram.y" + { (yyval.asn).lineNumber = (yyvsp[(1) - (2)].i).lineNumber; (yyval.asn).token = '*'; (yyval.asn).expr = (yyvsp[(2) - (2)].expr); ;} + break; + + case 126: +#line 494 "CMDgram.y" + { (yyval.asn).lineNumber = (yyvsp[(1) - (2)].i).lineNumber; (yyval.asn).token = '/'; (yyval.asn).expr = (yyvsp[(2) - (2)].expr); ;} + break; + + case 127: +#line 496 "CMDgram.y" + { (yyval.asn).lineNumber = (yyvsp[(1) - (2)].i).lineNumber; (yyval.asn).token = '%'; (yyval.asn).expr = (yyvsp[(2) - (2)].expr); ;} + break; + + case 128: +#line 498 "CMDgram.y" + { (yyval.asn).lineNumber = (yyvsp[(1) - (2)].i).lineNumber; (yyval.asn).token = '&'; (yyval.asn).expr = (yyvsp[(2) - (2)].expr); ;} + break; + + case 129: +#line 500 "CMDgram.y" + { (yyval.asn).lineNumber = (yyvsp[(1) - (2)].i).lineNumber; (yyval.asn).token = '^'; (yyval.asn).expr = (yyvsp[(2) - (2)].expr); ;} + break; + + case 130: +#line 502 "CMDgram.y" + { (yyval.asn).lineNumber = (yyvsp[(1) - (2)].i).lineNumber; (yyval.asn).token = '|'; (yyval.asn).expr = (yyvsp[(2) - (2)].expr); ;} + break; + + case 131: +#line 504 "CMDgram.y" + { (yyval.asn).lineNumber = (yyvsp[(1) - (2)].i).lineNumber; (yyval.asn).token = opSHL; (yyval.asn).expr = (yyvsp[(2) - (2)].expr); ;} + break; + + case 132: +#line 506 "CMDgram.y" + { (yyval.asn).lineNumber = (yyvsp[(1) - (2)].i).lineNumber; (yyval.asn).token = opSHR; (yyval.asn).expr = (yyvsp[(2) - (2)].expr); ;} + break; + + case 133: +#line 511 "CMDgram.y" + { (yyval.expr) = (yyvsp[(1) - (1)].expr); ;} + break; + + case 134: +#line 513 "CMDgram.y" + { (yyval.expr) = (yyvsp[(1) - (1)].expr); ;} + break; + + case 135: +#line 515 "CMDgram.y" + { (yyval.expr) = (yyvsp[(1) - (1)].od); ;} + break; + + case 136: +#line 517 "CMDgram.y" + { (yyval.expr) = AssignExprNode::alloc( (yyvsp[(1) - (3)].s).lineNumber, (yyvsp[(1) - (3)].s).value, NULL, (yyvsp[(3) - (3)].expr)); ;} + break; + + case 137: +#line 519 "CMDgram.y" + { (yyval.expr) = AssignExprNode::alloc( (yyvsp[(1) - (6)].s).lineNumber, (yyvsp[(1) - (6)].s).value, (yyvsp[(3) - (6)].expr), (yyvsp[(6) - (6)].expr)); ;} + break; + + case 138: +#line 521 "CMDgram.y" + { (yyval.expr) = AssignOpExprNode::alloc( (yyvsp[(1) - (2)].s).lineNumber, (yyvsp[(1) - (2)].s).value, NULL, (yyvsp[(2) - (2)].asn).expr, (yyvsp[(2) - (2)].asn).token); ;} + break; + + case 139: +#line 523 "CMDgram.y" + { (yyval.expr) = AssignOpExprNode::alloc( (yyvsp[(1) - (5)].s).lineNumber, (yyvsp[(1) - (5)].s).value, (yyvsp[(3) - (5)].expr), (yyvsp[(5) - (5)].asn).expr, (yyvsp[(5) - (5)].asn).token); ;} + break; + + case 140: +#line 525 "CMDgram.y" + { (yyval.expr) = SlotAssignOpNode::alloc( (yyvsp[(1) - (2)].slot).lineNumber, (yyvsp[(1) - (2)].slot).object, (yyvsp[(1) - (2)].slot).slotName, (yyvsp[(1) - (2)].slot).array, (yyvsp[(2) - (2)].asn).token, (yyvsp[(2) - (2)].asn).expr); ;} + break; + + case 141: +#line 527 "CMDgram.y" + { (yyval.expr) = SlotAssignNode::alloc( (yyvsp[(1) - (3)].slot).lineNumber, (yyvsp[(1) - (3)].slot).object, (yyvsp[(1) - (3)].slot).array, (yyvsp[(1) - (3)].slot).slotName, (yyvsp[(3) - (3)].expr)); ;} + break; + + case 142: +#line 529 "CMDgram.y" + { (yyval.expr) = SlotAssignNode::alloc( (yyvsp[(1) - (5)].slot).lineNumber, (yyvsp[(1) - (5)].slot).object, (yyvsp[(1) - (5)].slot).array, (yyvsp[(1) - (5)].slot).slotName, (yyvsp[(4) - (5)].expr)); ;} + break; + + case 143: +#line 534 "CMDgram.y" + { (yyval.expr) = FuncCallExprNode::alloc( (yyvsp[(1) - (4)].s).lineNumber, (yyvsp[(1) - (4)].s).value, NULL, (yyvsp[(3) - (4)].expr), false); ;} + break; + + case 144: +#line 536 "CMDgram.y" + { (yyval.expr) = FuncCallExprNode::alloc( (yyvsp[(1) - (6)].s).lineNumber, (yyvsp[(3) - (6)].s).value, (yyvsp[(1) - (6)].s).value, (yyvsp[(5) - (6)].expr), false); ;} + break; + + case 145: +#line 538 "CMDgram.y" + { (yyvsp[(1) - (6)].expr)->append((yyvsp[(5) - (6)].expr)); (yyval.expr) = FuncCallExprNode::alloc( (yyvsp[(1) - (6)].expr)->dbgLineNumber, (yyvsp[(3) - (6)].s).value, NULL, (yyvsp[(1) - (6)].expr), true); ;} + break; + + case 146: +#line 543 "CMDgram.y" + { (yyval.expr) = AssertCallExprNode::alloc( (yyvsp[(1) - (4)].i).lineNumber, (yyvsp[(3) - (4)].expr), NULL ); ;} + break; + + case 147: +#line 545 "CMDgram.y" + { (yyval.expr) = AssertCallExprNode::alloc( (yyvsp[(1) - (6)].i).lineNumber, (yyvsp[(3) - (6)].expr), (yyvsp[(5) - (6)].str).value ); ;} + break; + + case 148: +#line 550 "CMDgram.y" + { (yyval.expr) = NULL; ;} + break; + + case 149: +#line 552 "CMDgram.y" + { (yyval.expr) = (yyvsp[(1) - (1)].expr); ;} + break; + + case 150: +#line 557 "CMDgram.y" + { (yyval.expr) = (yyvsp[(1) - (1)].expr); ;} + break; + + case 151: +#line 559 "CMDgram.y" + { ((yyvsp[(1) - (3)].expr))->append((yyvsp[(3) - (3)].expr)); (yyval.expr) = (yyvsp[(1) - (3)].expr); ;} + break; + + case 152: +#line 564 "CMDgram.y" + { (yyval.slist) = NULL; ;} + break; + + case 153: +#line 566 "CMDgram.y" + { (yyval.slist) = (yyvsp[(1) - (1)].slist); ;} + break; + + case 154: +#line 571 "CMDgram.y" + { (yyval.slist) = (yyvsp[(1) - (1)].slist); ;} + break; + + case 155: +#line 573 "CMDgram.y" + { (yyvsp[(1) - (2)].slist)->append((yyvsp[(2) - (2)].slist)); (yyval.slist) = (yyvsp[(1) - (2)].slist); ;} + break; + + case 156: +#line 578 "CMDgram.y" + { (yyval.slist) = SlotAssignNode::alloc( (yyvsp[(1) - (4)].s).lineNumber, NULL, NULL, (yyvsp[(1) - (4)].s).value, (yyvsp[(3) - (4)].expr)); ;} + break; + + case 157: +#line 580 "CMDgram.y" + { (yyval.slist) = SlotAssignNode::alloc( (yyvsp[(1) - (5)].i).lineNumber, NULL, NULL, (yyvsp[(2) - (5)].s).value, (yyvsp[(4) - (5)].expr), (yyvsp[(1) - (5)].i).value); ;} + break; + + case 158: +#line 582 "CMDgram.y" + { (yyval.slist) = SlotAssignNode::alloc( (yyvsp[(1) - (4)].i).lineNumber, NULL, NULL, StringTable->insert("datablock"), (yyvsp[(3) - (4)].expr)); ;} + break; + + case 159: +#line 584 "CMDgram.y" + { (yyval.slist) = SlotAssignNode::alloc( (yyvsp[(1) - (7)].s).lineNumber, NULL, (yyvsp[(3) - (7)].expr), (yyvsp[(1) - (7)].s).value, (yyvsp[(6) - (7)].expr)); ;} + break; + + case 160: +#line 586 "CMDgram.y" + { (yyval.slist) = SlotAssignNode::alloc( (yyvsp[(1) - (8)].i).lineNumber, NULL, (yyvsp[(4) - (8)].expr), (yyvsp[(2) - (8)].s).value, (yyvsp[(7) - (8)].expr), (yyvsp[(1) - (8)].i).value); ;} + break; + + case 161: +#line 591 "CMDgram.y" + { (yyval.expr) = (yyvsp[(1) - (1)].expr); ;} + break; + + case 162: +#line 593 "CMDgram.y" + { (yyval.expr) = CommaCatExprNode::alloc( (yyvsp[(1) - (3)].expr)->dbgLineNumber, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); ;} + break; + + +/* Line 1267 of yacc.c. */ +#line 3148 "cmdgram.cpp" + default: break; + } + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (YY_("syntax error")); +#else + { + YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); + if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) + { + YYSIZE_T yyalloc = 2 * yysize; + if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) + yyalloc = YYSTACK_ALLOC_MAXIMUM; + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yyalloc); + if (yymsg) + yymsg_alloc = yyalloc; + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + } + } + + if (0 < yysize && yysize <= yymsg_alloc) + { + (void) yysyntax_error (yymsg, yystate, yychar); + yyerror (yymsg); + } + else + { + yyerror (YY_("syntax error")); + if (yysize != 0) + goto yyexhaustedlab; + } + } +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse look-ahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse look-ahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + *++yyvsp = yylval; + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEOF && yychar != YYEMPTY) + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + +#line 595 "CMDgram.y" + + + diff --git a/Engine/source/console/cmdgram.h b/Engine/source/console/cmdgram.h new file mode 100644 index 000000000..f08cde081 --- /dev/null +++ b/Engine/source/console/cmdgram.h @@ -0,0 +1,222 @@ +/* A Bison parser, made by GNU Bison 2.3. */ + +/* Skeleton interface for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + rwDEFINE = 258, + rwENDDEF = 259, + rwDECLARE = 260, + rwDECLARESINGLETON = 261, + rwBREAK = 262, + rwELSE = 263, + rwCONTINUE = 264, + rwGLOBAL = 265, + rwIF = 266, + rwNIL = 267, + rwRETURN = 268, + rwWHILE = 269, + rwDO = 270, + rwENDIF = 271, + rwENDWHILE = 272, + rwENDFOR = 273, + rwDEFAULT = 274, + rwFOR = 275, + rwFOREACH = 276, + rwFOREACHSTR = 277, + rwIN = 278, + rwDATABLOCK = 279, + rwSWITCH = 280, + rwCASE = 281, + rwSWITCHSTR = 282, + rwCASEOR = 283, + rwPACKAGE = 284, + rwNAMESPACE = 285, + rwCLASS = 286, + rwASSERT = 287, + ILLEGAL_TOKEN = 288, + CHRCONST = 289, + INTCONST = 290, + TTAG = 291, + VAR = 292, + IDENT = 293, + TYPEIDENT = 294, + DOCBLOCK = 295, + STRATOM = 296, + TAGATOM = 297, + FLTCONST = 298, + opINTNAME = 299, + opINTNAMER = 300, + opMINUSMINUS = 301, + opPLUSPLUS = 302, + STMT_SEP = 303, + opSHL = 304, + opSHR = 305, + opPLASN = 306, + opMIASN = 307, + opMLASN = 308, + opDVASN = 309, + opMODASN = 310, + opANDASN = 311, + opXORASN = 312, + opORASN = 313, + opSLASN = 314, + opSRASN = 315, + opCAT = 316, + opEQ = 317, + opNE = 318, + opGE = 319, + opLE = 320, + opAND = 321, + opOR = 322, + opSTREQ = 323, + opCOLONCOLON = 324, + opNTASN = 325, + opNDASN = 326, + opMDASN = 327, + opSTRNE = 328, + UNARY = 329 + }; +#endif +/* Tokens. */ +#define rwDEFINE 258 +#define rwENDDEF 259 +#define rwDECLARE 260 +#define rwDECLARESINGLETON 261 +#define rwBREAK 262 +#define rwELSE 263 +#define rwCONTINUE 264 +#define rwGLOBAL 265 +#define rwIF 266 +#define rwNIL 267 +#define rwRETURN 268 +#define rwWHILE 269 +#define rwDO 270 +#define rwENDIF 271 +#define rwENDWHILE 272 +#define rwENDFOR 273 +#define rwDEFAULT 274 +#define rwFOR 275 +#define rwFOREACH 276 +#define rwFOREACHSTR 277 +#define rwIN 278 +#define rwDATABLOCK 279 +#define rwSWITCH 280 +#define rwCASE 281 +#define rwSWITCHSTR 282 +#define rwCASEOR 283 +#define rwPACKAGE 284 +#define rwNAMESPACE 285 +#define rwCLASS 286 +#define rwASSERT 287 +#define ILLEGAL_TOKEN 288 +#define CHRCONST 289 +#define INTCONST 290 +#define TTAG 291 +#define VAR 292 +#define IDENT 293 +#define TYPEIDENT 294 +#define DOCBLOCK 295 +#define STRATOM 296 +#define TAGATOM 297 +#define FLTCONST 298 +#define opINTNAME 299 +#define opINTNAMER 300 +#define opMINUSMINUS 301 +#define opPLUSPLUS 302 +#define STMT_SEP 303 +#define opSHL 304 +#define opSHR 305 +#define opPLASN 306 +#define opMIASN 307 +#define opMLASN 308 +#define opDVASN 309 +#define opMODASN 310 +#define opANDASN 311 +#define opXORASN 312 +#define opORASN 313 +#define opSLASN 314 +#define opSRASN 315 +#define opCAT 316 +#define opEQ 317 +#define opNE 318 +#define opGE 319 +#define opLE 320 +#define opAND 321 +#define opOR 322 +#define opSTREQ 323 +#define opCOLONCOLON 324 +#define opNTASN 325 +#define opNDASN 326 +#define opMDASN 327 +#define opSTRNE 328 +#define UNARY 329 + + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +#line 82 "CMDgram.y" +{ + Token< char > c; + Token< int > i; + Token< const char* > s; + Token< char* > str; + Token< double > f; + StmtNode* stmt; + ExprNode* expr; + SlotAssignNode* slist; + VarNode* var; + SlotDecl slot; + InternalSlotDecl intslot; + ObjectBlockDecl odcl; + ObjectDeclNode* od; + AssignDecl asn; + IfStmtNode* ifnode; +} +/* Line 1529 of yacc.c. */ +#line 215 "cmdgram.h" + YYSTYPE; +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + +extern YYSTYPE CMDlval; + diff --git a/Engine/source/console/codeBlock.cpp b/Engine/source/console/codeBlock.cpp new file mode 100644 index 000000000..896d2a4cf --- /dev/null +++ b/Engine/source/console/codeBlock.cpp @@ -0,0 +1,1340 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/console.h" +#include "console/compiler.h" +#include "console/codeBlock.h" +#include "console/telnetDebugger.h" +#include "console/ast.h" +#include "core/strings/unicode.h" +#include "core/strings/stringFunctions.h" +#include "core/stringTable.h" +#include "core/stream/fileStream.h" + +using namespace Compiler; + +bool CodeBlock::smInFunction = false; +U32 CodeBlock::smBreakLineCount = 0; +CodeBlock * CodeBlock::smCodeBlockList = NULL; +CodeBlock * CodeBlock::smCurrentCodeBlock = NULL; +ConsoleParser *CodeBlock::smCurrentParser = NULL; + +//------------------------------------------------------------------------- + +CodeBlock::CodeBlock() +{ + globalStrings = NULL; + functionStrings = NULL; + functionStringsMaxLen = 0; + globalStringsMaxLen = 0; + globalFloats = NULL; + functionFloats = NULL; + lineBreakPairs = NULL; + breakList = NULL; + breakListSize = 0; + + refCount = 0; + code = NULL; + name = NULL; + fullPath = NULL; + modPath = NULL; +} + +CodeBlock::~CodeBlock() +{ + // Make sure we aren't lingering in the current code block... + AssertFatal(smCurrentCodeBlock != this, "CodeBlock::~CodeBlock - Caught lingering in smCurrentCodeBlock!") + + if(name) + removeFromCodeList(); + delete[] const_cast(globalStrings); + delete[] const_cast(functionStrings); + + functionStringsMaxLen = 0; + globalStringsMaxLen = 0; + + delete[] globalFloats; + delete[] functionFloats; + delete[] code; + delete[] breakList; +} + +//------------------------------------------------------------------------- + +StringTableEntry CodeBlock::getCurrentCodeBlockName() +{ + if (CodeBlock::getCurrentBlock()) + return CodeBlock::getCurrentBlock()->name; + else + return NULL; +} + +StringTableEntry CodeBlock::getCurrentCodeBlockFullPath() +{ + if (CodeBlock::getCurrentBlock()) + return CodeBlock::getCurrentBlock()->fullPath; + else + return NULL; +} + +StringTableEntry CodeBlock::getCurrentCodeBlockModName() +{ + if (CodeBlock::getCurrentBlock()) + return CodeBlock::getCurrentBlock()->modPath; + else + return NULL; +} + +CodeBlock *CodeBlock::find(StringTableEntry name) +{ + for(CodeBlock *walk = CodeBlock::getCodeBlockList(); walk; walk = walk->nextFile) + if(walk->name == name) + return walk; + return NULL; +} + +//------------------------------------------------------------------------- + +void CodeBlock::addToCodeList() +{ + // remove any code blocks with my name + for(CodeBlock **walk = &smCodeBlockList; *walk;walk = &((*walk)->nextFile)) + { + if((*walk)->name == name) + { + *walk = (*walk)->nextFile; + break; + } + } + nextFile = smCodeBlockList; + smCodeBlockList = this; +} + +void CodeBlock::clearAllBreaks() +{ + if(!lineBreakPairs) + return; + for(U32 i = 0; i < lineBreakPairCount; i++) + { + U32 *p = lineBreakPairs + i * 2; + code[p[1]] = p[0] & 0xFF; + } +} + +void CodeBlock::clearBreakpoint(U32 lineNumber) +{ + if(!lineBreakPairs) + return; + for(U32 i = 0; i < lineBreakPairCount; i++) + { + U32 *p = lineBreakPairs + i * 2; + if((p[0] >> 8) == lineNumber) + { + code[p[1]] = p[0] & 0xFF; + return; + } + } +} + +void CodeBlock::setAllBreaks() +{ + if(!lineBreakPairs) + return; + for(U32 i = 0; i < lineBreakPairCount; i++) + { + U32 *p = lineBreakPairs + i * 2; + code[p[1]] = OP_BREAK; + } +} + +bool CodeBlock::setBreakpoint(U32 lineNumber) +{ + if(!lineBreakPairs) + return false; + + for(U32 i = 0; i < lineBreakPairCount; i++) + { + U32 *p = lineBreakPairs + i * 2; + if((p[0] >> 8) == lineNumber) + { + code[p[1]] = OP_BREAK; + return true; + } + } + + return false; +} + +U32 CodeBlock::findFirstBreakLine(U32 lineNumber) +{ + if(!lineBreakPairs) + return 0; + + for(U32 i = 0; i < lineBreakPairCount; i++) + { + U32 *p = lineBreakPairs + i * 2; + U32 line = (p[0] >> 8); + + if( lineNumber <= line ) + return line; + } + + return 0; +} + +struct LinePair +{ + U32 instLine; + U32 ip; +}; + +void CodeBlock::findBreakLine(U32 ip, U32 &line, U32 &instruction) +{ + U32 min = 0; + U32 max = lineBreakPairCount - 1; + LinePair *p = (LinePair *) lineBreakPairs; + + U32 found; + if(!lineBreakPairCount || p[min].ip > ip || p[max].ip < ip) + { + line = 0; + instruction = OP_INVALID; + return; + } + else if(p[min].ip == ip) + found = min; + else if(p[max].ip == ip) + found = max; + else + { + for(;;) + { + if(min == max - 1) + { + found = min; + break; + } + U32 mid = (min + max) >> 1; + if(p[mid].ip == ip) + { + found = mid; + break; + } + else if(p[mid].ip > ip) + max = mid; + else + min = mid; + } + } + instruction = p[found].instLine & 0xFF; + line = p[found].instLine >> 8; +} + +const char *CodeBlock::getFileLine(U32 ip) +{ + static char nameBuffer[256]; + U32 line, inst; + findBreakLine(ip, line, inst); + + dSprintf(nameBuffer, sizeof(nameBuffer), "%s (%d)", name ? name : "", line); + return nameBuffer; +} + +void CodeBlock::removeFromCodeList() +{ + for(CodeBlock **walk = &smCodeBlockList; *walk; walk = &((*walk)->nextFile)) + { + if(*walk == this) + { + *walk = nextFile; + + // clear out all breakpoints + clearAllBreaks(); + break; + } + } + + // Let the telnet debugger know that this code + // block has been unloaded and that it needs to + // remove references to it. + if ( TelDebugger ) + TelDebugger->clearCodeBlockPointers( this ); +} + +void CodeBlock::calcBreakList() +{ + U32 size = 0; + S32 line = -1; + U32 seqCount = 0; + U32 i; + for(i = 0; i < lineBreakPairCount; i++) + { + U32 lineNumber = lineBreakPairs[i * 2]; + if(lineNumber == U32(line + 1)) + seqCount++; + else + { + if(seqCount) + size++; + size++; + seqCount = 1; + } + line = lineNumber; + } + if(seqCount) + size++; + + breakList = new U32[size]; + breakListSize = size; + line = -1; + seqCount = 0; + size = 0; + + for(i = 0; i < lineBreakPairCount; i++) + { + U32 lineNumber = lineBreakPairs[i * 2]; + + if(lineNumber == U32(line + 1)) + seqCount++; + else + { + if(seqCount) + breakList[size++] = seqCount; + breakList[size++] = lineNumber - getMax(0, line) - 1; + seqCount = 1; + } + + line = lineNumber; + } + + if(seqCount) + breakList[size++] = seqCount; + + for(i = 0; i < lineBreakPairCount; i++) + { + U32 *p = lineBreakPairs + i * 2; + p[0] = (p[0] << 8) | code[p[1]]; + } + + // Let the telnet debugger know that this code + // block has been loaded and that it can add break + // points it has for it. + if ( TelDebugger ) + TelDebugger->addAllBreakpoints( this ); +} + +bool CodeBlock::read(StringTableEntry fileName, Stream &st) +{ + const StringTableEntry exePath = Platform::getMainDotCsDir(); + const StringTableEntry cwd = Platform::getCurrentDirectory(); + + name = fileName; + + if(fileName) + { + fullPath = NULL; + + if(Platform::isFullPath(fileName)) + fullPath = fileName; + + if(dStrnicmp(exePath, fileName, dStrlen(exePath)) == 0) + name = StringTable->insert(fileName + dStrlen(exePath) + 1, true); + else if(dStrnicmp(cwd, fileName, dStrlen(cwd)) == 0) + name = StringTable->insert(fileName + dStrlen(cwd) + 1, true); + + if(fullPath == NULL) + { + char buf[1024]; + fullPath = StringTable->insert(Platform::makeFullPathName(fileName, buf, sizeof(buf)), true); + } + + modPath = Con::getModNameFromPath(fileName); + } + + // + addToCodeList(); + + U32 globalSize,size,i; + st.read(&size); + if(size) + { + globalStrings = new char[size]; + globalStringsMaxLen = size; + st.read(size, globalStrings); + } + globalSize = size; + st.read(&size); + if(size) + { + functionStrings = new char[size]; + functionStringsMaxLen = size; + st.read(size, functionStrings); + } + st.read(&size); + if(size) + { + globalFloats = new F64[size]; + for(U32 i = 0; i < size; i++) + st.read(&globalFloats[i]); + } + st.read(&size); + if(size) + { + functionFloats = new F64[size]; + for(U32 i = 0; i < size; i++) + st.read(&functionFloats[i]); + } + U32 codeSize; + st.read(&codeSize); + st.read(&lineBreakPairCount); + + U32 totSize = codeSize + lineBreakPairCount * 2; + code = new U32[totSize]; + + for(i = 0; i < codeSize; i++) + { + U8 b; + st.read(&b); + if(b == 0xFF) + st.read(&code[i]); + else + code[i] = b; + } + + for(i = codeSize; i < totSize; i++) + st.read(&code[i]); + + lineBreakPairs = code + codeSize; + + // StringTable-ize our identifiers. + U32 identCount; + st.read(&identCount); + while(identCount--) + { + U32 offset; + st.read(&offset); + StringTableEntry ste; + if(offset < globalSize) + ste = StringTable->insert(globalStrings + offset); + else + ste = StringTable->insert(""); + U32 count; + st.read(&count); + while(count--) + { + U32 ip; + st.read(&ip); + code[ip] = *((U32 *) &ste); + } + } + + if(lineBreakPairCount) + calcBreakList(); + + return true; +} + + +bool CodeBlock::compile(const char *codeFileName, StringTableEntry fileName, const char *inScript, bool overrideNoDso) +{ + // This will return true, but return value is ignored + char *script; + chompUTF8BOM( inScript, &script ); + + gSyntaxError = false; + + consoleAllocReset(); + + STEtoU32 = compileSTEtoU32; + + gStatementList = NULL; + + // Set up the parser. + smCurrentParser = getParserForFile(fileName); + AssertISV(smCurrentParser, avar("CodeBlock::compile - no parser available for '%s'!", fileName)); + + // Now do some parsing. + smCurrentParser->setScanBuffer(script, fileName); + smCurrentParser->restart(NULL); + smCurrentParser->parse(); + + if(gSyntaxError) + { + consoleAllocReset(); + return false; + } + +#ifdef TORQUE_NO_DSO_GENERATION + if(!overrideNoDso) + return false; +#endif // !TORQUE_NO_DSO_GENERATION + + FileStream st; + if(!st.open(codeFileName, Torque::FS::File::Write)) + return false; + st.write(U32(Con::DSOVersion)); + + // Reset all our value tables... + resetTables(); + + smInFunction = false; + smBreakLineCount = 0; + setBreakCodeBlock(this); + + if(gStatementList) + codeSize = precompileBlock(gStatementList, 0) + 1; + else + codeSize = 1; + + lineBreakPairCount = smBreakLineCount; + code = new U32[codeSize + smBreakLineCount * 2]; + lineBreakPairs = code + codeSize; + + // Write string table data... + getGlobalStringTable().write(st); + getFunctionStringTable().write(st); + + // Write float table data... + getGlobalFloatTable().write(st); + getFunctionFloatTable().write(st); + + smBreakLineCount = 0; + U32 lastIp; + if(gStatementList) + lastIp = compileBlock(gStatementList, code, 0, 0, 0); + else + lastIp = 0; + + if(lastIp != codeSize - 1) + Con::errorf(ConsoleLogEntry::General, "CodeBlock::compile - precompile size mismatch, a precompile/compile function pair is probably mismatched."); + + code[lastIp++] = OP_RETURN; + U32 totSize = codeSize + smBreakLineCount * 2; + st.write(codeSize); + st.write(lineBreakPairCount); + + // Write out our bytecode, doing a bit of compression for low numbers. + U32 i; + for(i = 0; i < codeSize; i++) + { + if(code[i] < 0xFF) + st.write(U8(code[i])); + else + { + st.write(U8(0xFF)); + st.write(code[i]); + } + } + + // Write the break info... + for(i = codeSize; i < totSize; i++) + st.write(code[i]); + + getIdentTable().write(st); + + consoleAllocReset(); + st.close(); + + return true; + + +} + +const char *CodeBlock::compileExec(StringTableEntry fileName, const char *inString, bool noCalls, int setFrame) +{ + // Check for a UTF8 script file + char *string; + chompUTF8BOM( inString, &string ); + + STEtoU32 = evalSTEtoU32; + consoleAllocReset(); + + name = fileName; + + if(fileName) + { + const StringTableEntry exePath = Platform::getMainDotCsDir(); + const StringTableEntry cwd = Platform::getCurrentDirectory(); + + fullPath = NULL; + + if(Platform::isFullPath(fileName)) + fullPath = fileName; + + if(dStrnicmp(exePath, fileName, dStrlen(exePath)) == 0) + name = StringTable->insert(fileName + dStrlen(exePath) + 1, true); + else if(dStrnicmp(cwd, fileName, dStrlen(cwd)) == 0) + name = StringTable->insert(fileName + dStrlen(cwd) + 1, true); + + if(fullPath == NULL) + { + char buf[1024]; + fullPath = StringTable->insert(Platform::makeFullPathName(fileName, buf, sizeof(buf)), true); + } + + modPath = Con::getModNameFromPath(fileName); + } + + if(name) + addToCodeList(); + + gStatementList = NULL; + + // Set up the parser. + smCurrentParser = getParserForFile(fileName); + AssertISV(smCurrentParser, avar("CodeBlock::compile - no parser available for '%s'!", fileName)); + + // Now do some parsing. + smCurrentParser->setScanBuffer(string, fileName); + smCurrentParser->restart(NULL); + smCurrentParser->parse(); + + if(!gStatementList) + { + delete this; + return ""; + } + + resetTables(); + + smInFunction = false; + smBreakLineCount = 0; + setBreakCodeBlock(this); + + codeSize = precompileBlock(gStatementList, 0) + 1; + + lineBreakPairCount = smBreakLineCount; + + globalStrings = getGlobalStringTable().build(); + globalStringsMaxLen = getGlobalStringTable().totalLen; + + functionStrings = getFunctionStringTable().build(); + functionStringsMaxLen = getFunctionStringTable().totalLen; + + globalFloats = getGlobalFloatTable().build(); + functionFloats = getFunctionFloatTable().build(); + + code = new U32[codeSize + lineBreakPairCount * 2]; + lineBreakPairs = code + codeSize; + + smBreakLineCount = 0; + U32 lastIp = compileBlock(gStatementList, code, 0, 0, 0); + code[lastIp++] = OP_RETURN; + + consoleAllocReset(); + + if(lineBreakPairCount && fileName) + calcBreakList(); + + if(lastIp != codeSize) + Con::warnf(ConsoleLogEntry::General, "precompile size mismatch, precompile: %d compile: %d", codeSize, lastIp); + + return exec(0, fileName, NULL, 0, 0, noCalls, NULL, setFrame); +} + +//------------------------------------------------------------------------- + +void CodeBlock::incRefCount() +{ + refCount++; +} + +void CodeBlock::decRefCount() +{ + refCount--; + if(!refCount) + delete this; +} + +//------------------------------------------------------------------------- + +String CodeBlock::getFunctionArgs( U32 ip ) +{ + StringBuilder str; + + U32 fnArgc = code[ ip + 5 ]; + for( U32 i = 0; i < fnArgc; ++ i ) + { + StringTableEntry var = U32toSTE( code[ ip + i + 6 ] ); + + if( i != 0 ) + str.append( ", " ); + + str.append( "string " ); + + // Try to capture junked parameters + if( var[ 0 ] ) + str.append( var + 1 ); + else + str.append( "JUNK" ); + } + + return str.end(); +} + +//------------------------------------------------------------------------- + +void CodeBlock::dumpInstructions( U32 startIp, bool upToReturn ) +{ + U32 ip = startIp; + while( ip < codeSize ) + { + switch( code[ ip ++ ] ) + { + case OP_FUNC_DECL: + { + StringTableEntry fnName = U32toSTE(code[ip]); + StringTableEntry fnNamespace = U32toSTE(code[ip+1]); + StringTableEntry fnPackage = U32toSTE(code[ip+2]); + bool hasBody = bool(code[ip+3]); + U32 newIp = code[ ip + 4 ]; + U32 argc = code[ ip + 5 ]; + + Con::printf( "%i: OP_FUNC_DECL name=%s nspace=%s package=%s hasbody=%i newip=%i argc=%i", + ip - 1, fnName, fnNamespace, fnPackage, hasBody, newIp, argc ); + + // Skip args. + + ip += 6 + argc; + break; + } + + case OP_CREATE_OBJECT: + { + StringTableEntry objParent = U32toSTE(code[ip ]); + bool isDataBlock = code[ip + 1]; + bool isInternal = code[ip + 2]; + bool isSingleton = code[ip + 3]; + U32 lineNumber = code[ip + 4]; + U32 failJump = code[ip + 5]; + + Con::printf( "%i: OP_CREATE_OBJECT objParent=%s isDataBlock=%i isInternal=%i isSingleton=%i lineNumber=%i failJump=%i", + ip - 1, objParent, isDataBlock, isInternal, isSingleton, lineNumber, failJump ); + + ip += 6; + break; + } + + case OP_ADD_OBJECT: + { + bool placeAtRoot = code[ip++]; + Con::printf( "%i: OP_ADD_OBJECT placeAtRoot=%i", ip - 1, placeAtRoot ); + break; + } + + case OP_END_OBJECT: + { + bool placeAtRoot = code[ip++]; + Con::printf( "%i: OP_END_OBJECT placeAtRoot=%i", ip - 1, placeAtRoot ); + break; + } + + case OP_FINISH_OBJECT: + { + Con::printf( "%i: OP_FINISH_OBJECT", ip - 1 ); + break; + } + + case OP_JMPIFFNOT: + { + Con::printf( "%i: OP_JMPIFFNOT ip=%i", ip - 1, code[ ip ] ); + ++ ip; + break; + } + + case OP_JMPIFNOT: + { + Con::printf( "%i: OP_JMPIFNOT ip=%i", ip - 1, code[ ip ] ); + ++ ip; + break; + } + + case OP_JMPIFF: + { + Con::printf( "%i: OP_JMPIFF ip=%i", ip - 1, code[ ip ] ); + ++ ip; + break; + } + + case OP_JMPIF: + { + Con::printf( "%i: OP_JMPIF ip=%i", ip - 1, code[ ip ] ); + ++ ip; + break; + } + + case OP_JMPIFNOT_NP: + { + Con::printf( "%i: OP_JMPIFNOT_NP ip=%i", ip - 1, code[ ip ] ); + ++ ip; + break; + } + + case OP_JMPIF_NP: + { + Con::printf( "%i: OP_JMPIF_NP ip=%i", ip - 1, code[ ip ] ); + ++ ip; + break; + } + + case OP_JMP: + { + Con::printf( "%i: OP_JMP ip=%i", ip - 1, code[ ip ] ); + ++ ip; + break; + } + + case OP_RETURN: + { + Con::printf( "%i: OP_RETURN", ip - 1 ); + + if( upToReturn ) + return; + + break; + } + + case OP_RETURN_VOID: + { + Con::printf( "%i: OP_RETURNVOID", ip - 1 ); + + if( upToReturn ) + return; + + break; + } + + case OP_CMPEQ: + { + Con::printf( "%i: OP_CMPEQ", ip - 1 ); + break; + } + + case OP_CMPGR: + { + Con::printf( "%i: OP_CMPGR", ip - 1 ); + break; + } + + case OP_CMPGE: + { + Con::printf( "%i: OP_CMPGE", ip - 1 ); + break; + } + + case OP_CMPLT: + { + Con::printf( "%i: OP_CMPLT", ip - 1 ); + break; + } + + case OP_CMPLE: + { + Con::printf( "%i: OP_CMPLE", ip - 1 ); + break; + } + + case OP_CMPNE: + { + Con::printf( "%i: OP_CMPNE", ip - 1 ); + break; + } + + case OP_XOR: + { + Con::printf( "%i: OP_XOR", ip - 1 ); + break; + } + + case OP_MOD: + { + Con::printf( "%i: OP_MOD", ip - 1 ); + break; + } + + case OP_BITAND: + { + Con::printf( "%i: OP_BITAND", ip - 1 ); + break; + } + + case OP_BITOR: + { + Con::printf( "%i: OP_BITOR", ip - 1 ); + break; + } + + case OP_NOT: + { + Con::printf( "%i: OP_NOT", ip - 1 ); + break; + } + + case OP_NOTF: + { + Con::printf( "%i: OP_NOTF", ip - 1 ); + break; + } + + case OP_ONESCOMPLEMENT: + { + Con::printf( "%i: OP_ONESCOMPLEMENT", ip - 1 ); + break; + } + + case OP_SHR: + { + Con::printf( "%i: OP_SHR", ip - 1 ); + break; + } + + case OP_SHL: + { + Con::printf( "%i: OP_SHL", ip - 1 ); + break; + } + + case OP_AND: + { + Con::printf( "%i: OP_AND", ip - 1 ); + break; + } + + case OP_OR: + { + Con::printf( "%i: OP_OR", ip - 1 ); + break; + } + + case OP_ADD: + { + Con::printf( "%i: OP_ADD", ip - 1 ); + break; + } + + case OP_SUB: + { + Con::printf( "%i: OP_SUB", ip - 1 ); + break; + } + + case OP_MUL: + { + Con::printf( "%i: OP_MUL", ip - 1 ); + break; + } + + case OP_DIV: + { + Con::printf( "%i: OP_DIV", ip - 1 ); + break; + } + + case OP_NEG: + { + Con::printf( "%i: OP_NEG", ip - 1 ); + break; + } + + case OP_SETCURVAR: + { + StringTableEntry var = U32toSTE(code[ip]); + + Con::printf( "%i: OP_SETCURVAR var=%s", ip - 1, var ); + ip++; + break; + } + + case OP_SETCURVAR_CREATE: + { + StringTableEntry var = U32toSTE(code[ip]); + + Con::printf( "%i: OP_SETCURVAR_CREATE var=%s", ip - 1, var ); + ip++; + break; + } + + case OP_SETCURVAR_ARRAY: + { + Con::printf( "%i: OP_SETCURVAR_ARRAY", ip - 1 ); + break; + } + + case OP_SETCURVAR_ARRAY_CREATE: + { + Con::printf( "%i: OP_SETCURVAR_ARRAY_CREATE", ip - 1 ); + break; + } + + case OP_LOADVAR_UINT: + { + Con::printf( "%i: OP_LOADVAR_UINT", ip - 1 ); + break; + } + + case OP_LOADVAR_FLT: + { + Con::printf( "%i: OP_LOADVAR_FLT", ip - 1 ); + break; + } + + case OP_LOADVAR_STR: + { + Con::printf( "%i: OP_LOADVAR_STR", ip - 1 ); + break; + } + + case OP_SAVEVAR_UINT: + { + Con::printf( "%i: OP_SAVEVAR_UINT", ip - 1 ); + break; + } + + case OP_SAVEVAR_FLT: + { + Con::printf( "%i: OP_SAVEVAR_FLT", ip - 1 ); + break; + } + + case OP_SAVEVAR_STR: + { + Con::printf( "%i: OP_SAVEVAR_STR", ip - 1 ); + break; + } + + case OP_SETCUROBJECT: + { + Con::printf( "%i: OP_SETCUROBJECT", ip - 1 ); + break; + } + + case OP_SETCUROBJECT_NEW: + { + Con::printf( "%i: OP_SETCUROBJECT_NEW", ip - 1 ); + break; + } + + case OP_SETCUROBJECT_INTERNAL: + { + Con::printf( "%i: OP_SETCUROBJECT_INTERNAL", ip - 1 ); + ++ ip; + break; + } + + case OP_SETCURFIELD: + { + StringTableEntry curField = U32toSTE(code[ip]); + Con::printf( "%i: OP_SETCURFIELD field=%s", ip - 1, curField ); + ++ ip; + } + + case OP_SETCURFIELD_ARRAY: + { + Con::printf( "%i: OP_SETCURFIELD_ARRAY", ip - 1 ); + break; + } + + case OP_SETCURFIELD_TYPE: + { + U32 type = code[ ip ]; + Con::printf( "%i: OP_SETCURFIELD_TYPE type=%i", ip - 1, type ); + ++ ip; + break; + } + + case OP_LOADFIELD_UINT: + { + Con::printf( "%i: OP_LOADFIELD_UINT", ip - 1 ); + break; + } + + case OP_LOADFIELD_FLT: + { + Con::printf( "%i: OP_LOADFIELD_FLT", ip - 1 ); + break; + } + + case OP_LOADFIELD_STR: + { + Con::printf( "%i: OP_LOADFIELD_STR", ip - 1 ); + break; + } + + case OP_SAVEFIELD_UINT: + { + Con::printf( "%i: OP_SAVEFIELD_UINT", ip - 1 ); + break; + } + + case OP_SAVEFIELD_FLT: + { + Con::printf( "%i: OP_SAVEFIELD_FLT", ip - 1 ); + break; + } + + case OP_SAVEFIELD_STR: + { + Con::printf( "%i: OP_SAVEFIELD_STR", ip - 1 ); + break; + } + + case OP_STR_TO_UINT: + { + Con::printf( "%i: OP_STR_TO_UINT", ip - 1 ); + break; + } + + case OP_STR_TO_FLT: + { + Con::printf( "%i: OP_STR_TO_FLT", ip - 1 ); + break; + } + + case OP_STR_TO_NONE: + { + Con::printf( "%i: OP_STR_TO_NONE", ip - 1 ); + break; + } + + case OP_FLT_TO_UINT: + { + Con::printf( "%i: OP_FLT_TO_UINT", ip - 1 ); + break; + } + + case OP_FLT_TO_STR: + { + Con::printf( "%i: OP_FLT_TO_STR", ip - 1 ); + break; + } + + case OP_FLT_TO_NONE: + { + Con::printf( "%i: OP_FLT_TO_NONE", ip - 1 ); + break; + } + + case OP_UINT_TO_FLT: + { + Con::printf( "%i: OP_SAVEFIELD_STR", ip - 1 ); + break; + } + + case OP_UINT_TO_STR: + { + Con::printf( "%i: OP_UINT_TO_STR", ip - 1 ); + break; + } + + case OP_UINT_TO_NONE: + { + Con::printf( "%i: OP_UINT_TO_NONE", ip - 1 ); + break; + } + + case OP_LOADIMMED_UINT: + { + U32 val = code[ ip ]; + Con::printf( "%i: OP_LOADIMMED_UINT val=%i", ip - 1, val ); + ++ ip; + break; + } + + case OP_LOADIMMED_FLT: + { + F64 val = functionFloats[ code[ ip ] ]; + Con::printf( "%i: OP_LOADIMMED_FLT val=%f", ip - 1, val ); + ++ ip; + break; + } + + case OP_TAG_TO_STR: + { + const char* str = functionStrings + code[ ip ]; + Con::printf( "%i: OP_TAG_TO_STR str=%s", ip - 1, str ); + ++ ip; + break; + } + + case OP_LOADIMMED_STR: + { + const char* str = functionStrings + code[ ip ]; + Con::printf( "%i: OP_LOADIMMED_STR str=%s", ip - 1, str ); + ++ ip; + break; + } + + case OP_DOCBLOCK_STR: + { + const char* str = functionStrings + code[ ip ]; + Con::printf( "%i: OP_DOCBLOCK_STR str=%s", ip - 1, str ); + ++ ip; + break; + } + + case OP_LOADIMMED_IDENT: + { + StringTableEntry str = U32toSTE( code[ ip ] ); + Con::printf( "%i: OP_LOADIMMED_IDENT str=%s", ip - 1, str ); + ++ ip; + break; + } + + case OP_CALLFUNC_RESOLVE: + { + StringTableEntry fnNamespace = U32toSTE(code[ip+1]); + StringTableEntry fnName = U32toSTE(code[ip]); + U32 callType = code[ip+2]; + + Con::printf( "%i: OP_CALLFUNC_RESOLVE name=%s nspace=%s callType=%s", ip - 1, fnName, fnNamespace, + callType == FuncCallExprNode::FunctionCall ? "FunctionCall" + : callType == FuncCallExprNode::MethodCall ? "MethodCall" : "ParentCall" ); + + ip += 3; + break; + } + + case OP_CALLFUNC: + { + StringTableEntry fnNamespace = U32toSTE(code[ip+1]); + StringTableEntry fnName = U32toSTE(code[ip]); + U32 callType = code[ip+2]; + + Con::printf( "%i: OP_CALLFUNC name=%s nspace=%s callType=%s", ip - 1, fnName, fnNamespace, + callType == FuncCallExprNode::FunctionCall ? "FunctionCall" + : callType == FuncCallExprNode::MethodCall ? "MethodCall" : "ParentCall" ); + + ip += 3; + break; + } + + case OP_ADVANCE_STR: + { + Con::printf( "%i: OP_ADVANCE_STR", ip - 1 ); + break; + } + + case OP_ADVANCE_STR_APPENDCHAR: + { + char ch = code[ ip ]; + Con::printf( "%i: OP_ADVANCE_STR_APPENDCHAR char=%c", ip - 1, ch ); + ++ ip; + break; + } + + case OP_ADVANCE_STR_COMMA: + { + Con::printf( "%i: OP_ADVANCE_STR_COMMA", ip - 1 ); + break; + } + + case OP_ADVANCE_STR_NUL: + { + Con::printf( "%i: OP_ADVANCE_STR_NUL", ip - 1 ); + break; + } + + case OP_REWIND_STR: + { + Con::printf( "%i: OP_REWIND_STR", ip - 1 ); + break; + } + + case OP_TERMINATE_REWIND_STR: + { + Con::printf( "%i: OP_TERMINATE_REWIND_STR", ip - 1 ); + break; + } + + case OP_COMPARE_STR: + { + Con::printf( "%i: OP_COMPARE_STR", ip - 1 ); + break; + } + + case OP_PUSH: + { + Con::printf( "%i: OP_PUSH", ip - 1 ); + break; + } + + case OP_PUSH_FRAME: + { + Con::printf( "%i: OP_PUSH_FRAME", ip - 1 ); + break; + } + + case OP_ASSERT: + { + const char* message = functionStrings + code[ ip ]; + Con::printf( "%i: OP_ASSERT message=%s", ip - 1, message ); + ++ ip; + break; + } + + case OP_BREAK: + { + Con::printf( "%i: OP_BREAK", ip - 1 ); + break; + } + + case OP_ITER_BEGIN: + { + StringTableEntry varName = U32toSTE( code[ ip ] ); + U32 failIp = code[ ip + 1 ]; + + Con::printf( "%i: OP_ITER_BEGIN varName=%s failIp=%i", varName, failIp ); + + ++ ip; + } + + case OP_ITER_BEGIN_STR: + { + StringTableEntry varName = U32toSTE( code[ ip ] ); + U32 failIp = code[ ip + 1 ]; + + Con::printf( "%i: OP_ITER_BEGIN varName=%s failIp=%i", varName, failIp ); + + ip += 2; + } + + case OP_ITER: + { + U32 breakIp = code[ ip ]; + + Con::printf( "%i: OP_ITER breakIp=%i", breakIp ); + + ++ ip; + } + + case OP_ITER_END: + { + Con::printf( "%i: OP_ITER_END", ip - 1 ); + break; + } + + default: + Con::printf( "%i: !!INVALID!!", ip - 1 ); + break; + } + } +} diff --git a/Engine/source/console/codeBlock.h b/Engine/source/console/codeBlock.h new file mode 100644 index 000000000..98e581e46 --- /dev/null +++ b/Engine/source/console/codeBlock.h @@ -0,0 +1,154 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _CODEBLOCK_H_ +#define _CODEBLOCK_H_ + +#include "console/compiler.h" +#include "console/consoleParser.h" + +class Stream; + +/// Core TorqueScript code management class. +/// +/// This class represents a block of code, usually mapped directly to a file. +class CodeBlock +{ +private: + static CodeBlock* smCodeBlockList; + static CodeBlock* smCurrentCodeBlock; + +public: + static U32 smBreakLineCount; + static bool smInFunction; + static Compiler::ConsoleParser * smCurrentParser; + + static CodeBlock* getCurrentBlock() + { + return smCurrentCodeBlock; + } + + static CodeBlock *getCodeBlockList() + { + return smCodeBlockList; + } + + static StringTableEntry getCurrentCodeBlockName(); + static StringTableEntry getCurrentCodeBlockFullPath(); + static StringTableEntry getCurrentCodeBlockModName(); + static CodeBlock *find(StringTableEntry); + + CodeBlock(); + ~CodeBlock(); + + StringTableEntry name; + StringTableEntry fullPath; + StringTableEntry modPath; + + char *globalStrings; + char *functionStrings; + + U32 functionStringsMaxLen; + U32 globalStringsMaxLen; + + F64 *globalFloats; + F64 *functionFloats; + + U32 codeSize; + U32 *code; + + U32 refCount; + U32 lineBreakPairCount; + U32 *lineBreakPairs; + U32 breakListSize; + U32 *breakList; + CodeBlock *nextFile; + + void addToCodeList(); + void removeFromCodeList(); + void calcBreakList(); + void clearAllBreaks(); + void setAllBreaks(); + void dumpInstructions( U32 startIp = 0, bool upToReturn = false ); + + /// Returns the first breakable line or 0 if none was found. + /// @param lineNumber The one based line number. + U32 findFirstBreakLine(U32 lineNumber); + + void clearBreakpoint(U32 lineNumber); + + /// Set a OP_BREAK instruction on a line. If a break + /// is not possible on that line it returns false. + /// @param lineNumber The one based line number. + bool setBreakpoint(U32 lineNumber); + + void findBreakLine(U32 ip, U32 &line, U32 &instruction); + const char *getFileLine(U32 ip); + + /// + String getFunctionArgs( U32 offset ); + + bool read(StringTableEntry fileName, Stream &st); + bool compile(const char *dsoName, StringTableEntry fileName, const char *script, bool overrideNoDso = false); + + void incRefCount(); + void decRefCount(); + + /// Compiles and executes a block of script storing the compiled code in this + /// CodeBlock. If there is no filename breakpoints will not be generated and + /// the CodeBlock will not be added to the linked list of loaded CodeBlocks. + /// Note that if the script contains no executable statements the CodeBlock + /// will delete itself on return an empty string. The return string is any + /// result of the code executed, if any, or an empty string. + /// + /// @param fileName The file name, including path and extension, for the + /// block of code or an empty string. + /// @param script The script code to compile and execute. + /// @param noCalls Skips calling functions from the script. + /// @param setFrame A zero based index of the stack frame to execute the code + /// with, zero being the top of the stack. If the the index is + /// -1 a new frame is created. If the index is out of range the + /// top stack frame is used. + const char *compileExec(StringTableEntry fileName, const char *script, + bool noCalls, int setFrame = -1 ); + + /// Executes the existing code in the CodeBlock. The return string is any + /// result of the code executed, if any, or an empty string. + /// + /// @param offset The instruction offset to start executing from. + /// @param fnName The name of the function to execute or null. + /// @param ns The namespace of the function to execute or null. + /// @param argc The number of parameters passed to the function or + /// zero to execute code outside of a function. + /// @param argv The function parameter list. + /// @param noCalls Skips calling functions from the script. + /// @param setFrame A zero based index of the stack frame to execute the code + /// with, zero being the top of the stack. If the the index is + /// -1 a new frame is created. If the index is out of range the + /// top stack frame is used. + /// @param packageName The code package name or null. + const char *exec(U32 offset, const char *fnName, Namespace *ns, U32 argc, + const char **argv, bool noCalls, StringTableEntry packageName, + S32 setFrame = -1); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/console/compiledEval.cpp b/Engine/source/console/compiledEval.cpp new file mode 100644 index 000000000..e2cfcf280 --- /dev/null +++ b/Engine/source/console/compiledEval.cpp @@ -0,0 +1,2018 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/console.h" + +#include "console/ast.h" +#include "core/tAlgorithm.h" + +#include "core/strings/findMatch.h" +#include "core/strings/stringUnit.h" +#include "console/consoleInternal.h" +#include "core/stream/fileStream.h" +#include "console/compiler.h" + +#include "console/simBase.h" +#include "console/telnetDebugger.h" +#include "sim/netStringTable.h" +#include "console/ICallMethod.h" +#include "console/stringStack.h" +#include "util/messaging/message.h" +#include "core/frameAllocator.h" + +#ifndef TORQUE_TGB_ONLY +#include "materials/materialDefinition.h" +#include "materials/materialManager.h" +#endif + +using namespace Compiler; + +enum EvalConstants { + MaxStackSize = 1024, + MethodOnComponent = -2 +}; + +namespace Con +{ +// Current script file name and root, these are registered as +// console variables. +extern StringTableEntry gCurrentFile; +extern StringTableEntry gCurrentRoot; +extern S32 gObjectCopyFailures; +} + +/// Frame data for a foreach/foreach$ loop. +struct IterStackRecord +{ + /// If true, this is a foreach$ loop; if not, it's a foreach loop. + bool mIsStringIter; + + /// The iterator variable. + Dictionary::Entry* mVariable; + + /// Information for an object iterator loop. + struct ObjectPos + { + /// The set being iterated over. + SimSet* mSet; + + /// Current index in the set. + U32 mIndex; + }; + + /// Information for a string iterator loop. + struct StringPos + { + /// The raw string data on the string stack. + const char* mString; + + /// Current parsing position. + U32 mIndex; + }; + + union + { + ObjectPos mObj; + StringPos mStr; + } mData; +}; + +IterStackRecord iterStack[ MaxStackSize ]; + +F64 floatStack[MaxStackSize]; +S64 intStack[MaxStackSize]; + +StringStack STR; + +U32 _FLT = 0; ///< Stack pointer for floatStack. +U32 _UINT = 0; ///< Stack pointer for intStack. +U32 _ITER = 0; ///< Stack pointer for iterStack. + +namespace Con +{ + const char *getNamespaceList(Namespace *ns) + { + U32 size = 1; + Namespace * walk; + for(walk = ns; walk; walk = walk->mParent) + size += dStrlen(walk->mName) + 4; + char *ret = Con::getReturnBuffer(size); + ret[0] = 0; + for(walk = ns; walk; walk = walk->mParent) + { + dStrcat(ret, walk->mName); + if(walk->mParent) + dStrcat(ret, " -> "); + } + return ret; + } +} + +//------------------------------------------------------------ + +F64 consoleStringToNumber(const char *str, StringTableEntry file, U32 line) +{ + F64 val = dAtof(str); + if(val != 0) + return val; + else if(!dStricmp(str, "true")) + return 1; + else if(!dStricmp(str, "false")) + return 0; + else if(file) + { + Con::warnf(ConsoleLogEntry::General, "%s (%d): string always evaluates to 0.", file, line); + return 0; + } + return 0; +} + +//------------------------------------------------------------ + +namespace Con +{ + + char *getReturnBuffer(U32 bufferSize) + + { + return STR.getReturnBuffer(bufferSize); + } + + char *getReturnBuffer( const char *stringToCopy ) + { + U32 len = dStrlen( stringToCopy ) + 1; + char *ret = STR.getReturnBuffer( len); + dMemcpy( ret, stringToCopy, len ); + return ret; + } + + char* getReturnBuffer( const String& str ) + { + const U32 size = str.size(); + char* ret = STR.getReturnBuffer( size ); + dMemcpy( ret, str.c_str(), size ); + return ret; + } + + char* getReturnBuffer( const StringBuilder& str ) + { + char* buffer = Con::getReturnBuffer( str.length() + 1 ); + str.copy( buffer ); + buffer[ str.length() ] = '\0'; + + return buffer; + } + + char *getArgBuffer(U32 bufferSize) + { + return STR.getArgBuffer(bufferSize); + } + + char *getFloatArg(F64 arg) + { + char *ret = STR.getArgBuffer(32); + dSprintf(ret, 32, "%g", arg); + return ret; + } + + char *getIntArg(S32 arg) + { + char *ret = STR.getArgBuffer(32); + dSprintf(ret, 32, "%d", arg); + return ret; + } + + char *getStringArg( const char *arg ) + { + U32 len = dStrlen( arg ) + 1; + char *ret = STR.getArgBuffer( len ); + dMemcpy( ret, arg, len ); + return ret; + } + + char* getStringArg( const String& arg ) + { + const U32 size = arg.size(); + char* ret = STR.getArgBuffer( size ); + dMemcpy( ret, arg.c_str(), size ); + return ret; + } +} + +//------------------------------------------------------------ + +inline void ExprEvalState::setCurVarName(StringTableEntry name) +{ + if(name[0] == '$') + currentVariable = globalVars.lookup(name); + else if( getStackDepth() > 0 ) + currentVariable = getCurrentFrame().lookup(name); + if(!currentVariable && gWarnUndefinedScriptVariables) + Con::warnf(ConsoleLogEntry::Script, "Variable referenced before assignment: %s", name); +} + +inline void ExprEvalState::setCurVarNameCreate(StringTableEntry name) +{ + if(name[0] == '$') + currentVariable = globalVars.add(name); + else if( getStackDepth() > 0 ) + currentVariable = getCurrentFrame().add(name); + else + { + currentVariable = NULL; + Con::warnf(ConsoleLogEntry::Script, "Accessing local variable in global scope... failed: %s", name); + } +} + +//------------------------------------------------------------ + +inline S32 ExprEvalState::getIntVariable() +{ + return currentVariable ? currentVariable->getIntValue() : 0; +} + +inline F64 ExprEvalState::getFloatVariable() +{ + return currentVariable ? currentVariable->getFloatValue() : 0; +} + +inline const char *ExprEvalState::getStringVariable() +{ + return currentVariable ? currentVariable->getStringValue() : ""; +} + +//------------------------------------------------------------ + +inline void ExprEvalState::setIntVariable(S32 val) +{ + AssertFatal(currentVariable != NULL, "Invalid evaluator state - trying to set null variable!"); + currentVariable->setIntValue(val); +} + +inline void ExprEvalState::setFloatVariable(F64 val) +{ + AssertFatal(currentVariable != NULL, "Invalid evaluator state - trying to set null variable!"); + currentVariable->setFloatValue(val); +} + +inline void ExprEvalState::setStringVariable(const char *val) +{ + AssertFatal(currentVariable != NULL, "Invalid evaluator state - trying to set null variable!"); + currentVariable->setStringValue(val); +} + +//------------------------------------------------------------ + +// Gets a component of an object's field value or a variable and returns it +// in val. +static void getFieldComponent( SimObject* object, StringTableEntry field, const char* array, StringTableEntry subField, char val[] ) +{ + const char* prevVal = NULL; + + // Grab value from object. + if( object && field ) + prevVal = object->getDataField( field, array ); + + // Otherwise, grab from the string stack. The value coming in will always + // be a string because that is how multicomponent variables are handled. + else + prevVal = STR.getStringValue(); + + // Make sure we got a value. + if ( prevVal && *prevVal ) + { + static const StringTableEntry xyzw[] = + { + StringTable->insert( "x" ), + StringTable->insert( "y" ), + StringTable->insert( "z" ), + StringTable->insert( "w" ) + }; + + static const StringTableEntry rgba[] = + { + StringTable->insert( "r" ), + StringTable->insert( "g" ), + StringTable->insert( "b" ), + StringTable->insert( "a" ) + }; + + // Translate xyzw and rgba into the indexed component + // of the variable or field. + if ( subField == xyzw[0] || subField == rgba[0] ) + dStrcpy( val, StringUnit::getUnit( prevVal, 0, " \t\n") ); + + else if ( subField == xyzw[1] || subField == rgba[1] ) + dStrcpy( val, StringUnit::getUnit( prevVal, 1, " \t\n") ); + + else if ( subField == xyzw[2] || subField == rgba[2] ) + dStrcpy( val, StringUnit::getUnit( prevVal, 2, " \t\n") ); + + else if ( subField == xyzw[3] || subField == rgba[3] ) + dStrcpy( val, StringUnit::getUnit( prevVal, 3, " \t\n") ); + + else + val[0] = 0; + } + else + val[0] = 0; +} + +// Sets a component of an object's field value based on the sub field. 'x' will +// set the first field, 'y' the second, and 'z' the third. +static void setFieldComponent( SimObject* object, StringTableEntry field, const char* array, StringTableEntry subField ) +{ + // Copy the current string value + char strValue[1024]; + dStrncpy( strValue, STR.getStringValue(), 1024 ); + + char val[1024] = ""; + const char* prevVal = NULL; + + // Set the value on an object field. + if( object && field ) + prevVal = object->getDataField( field, array ); + + // Set the value on a variable. + else if( gEvalState.currentVariable ) + prevVal = gEvalState.getStringVariable(); + + // Ensure that the variable has a value + if (!prevVal) + return; + + static const StringTableEntry xyzw[] = + { + StringTable->insert( "x" ), + StringTable->insert( "y" ), + StringTable->insert( "z" ), + StringTable->insert( "w" ) + }; + + static const StringTableEntry rgba[] = + { + StringTable->insert( "r" ), + StringTable->insert( "g" ), + StringTable->insert( "b" ), + StringTable->insert( "a" ) + }; + + // Insert the value into the specified + // component of the string. + if ( subField == xyzw[0] || subField == rgba[0] ) + dStrcpy( val, StringUnit::setUnit( prevVal, 0, strValue, " \t\n") ); + + else if ( subField == xyzw[1] || subField == rgba[1] ) + dStrcpy( val, StringUnit::setUnit( prevVal, 1, strValue, " \t\n") ); + + else if ( subField == xyzw[2] || subField == rgba[2] ) + dStrcpy( val, StringUnit::setUnit( prevVal, 2, strValue, " \t\n") ); + + else if ( subField == xyzw[3] || subField == rgba[3] ) + dStrcpy( val, StringUnit::setUnit( prevVal, 3, strValue, " \t\n") ); + + if ( val[0] != 0 ) + { + // Update the field or variable. + if( object && field ) + object->setDataField( field, 0, val ); + else if( gEvalState.currentVariable ) + gEvalState.setStringVariable( val ); + } +} + +const char *CodeBlock::exec(U32 ip, const char *functionName, Namespace *thisNamespace, U32 argc, const char **argv, bool noCalls, StringTableEntry packageName, S32 setFrame) +{ +#ifdef TORQUE_DEBUG + U32 stackStart = STR.mStartStackSize; +#endif + + static char traceBuffer[1024]; + U32 i; + + U32 iterDepth = 0; + + incRefCount(); + F64 *curFloatTable; + char *curStringTable; + S32 curStringTableLen = 0; //clint to ensure we dont overwrite it + STR.clearFunctionOffset(); + StringTableEntry thisFunctionName = NULL; + bool popFrame = false; + if(argv) + { + // assume this points into a function decl: + U32 fnArgc = code[ip + 5]; + thisFunctionName = U32toSTE(code[ip]); + argc = getMin(argc-1, fnArgc); // argv[0] is func name + if(gEvalState.traceOn) + { + traceBuffer[0] = 0; + dStrcat(traceBuffer, "Entering "); + if(packageName) + { + dStrcat(traceBuffer, "["); + dStrcat(traceBuffer, packageName); + dStrcat(traceBuffer, "]"); + } + if(thisNamespace && thisNamespace->mName) + { + dSprintf(traceBuffer + dStrlen(traceBuffer), sizeof(traceBuffer) - dStrlen(traceBuffer), + "%s::%s(", thisNamespace->mName, thisFunctionName); + } + else + { + dSprintf(traceBuffer + dStrlen(traceBuffer), sizeof(traceBuffer) - dStrlen(traceBuffer), + "%s(", thisFunctionName); + } + for(i = 0; i < argc; i++) + { + dStrcat(traceBuffer, argv[i+1]); + if(i != argc - 1) + dStrcat(traceBuffer, ", "); + } + dStrcat(traceBuffer, ")"); + Con::printf("%s", traceBuffer); + } + gEvalState.pushFrame(thisFunctionName, thisNamespace); + popFrame = true; + for(i = 0; i < argc; i++) + { + StringTableEntry var = U32toSTE(code[ip + i + 6]); + gEvalState.setCurVarNameCreate(var); + gEvalState.setStringVariable(argv[i+1]); + } + ip = ip + fnArgc + 6; + curFloatTable = functionFloats; + curStringTable = functionStrings; + curStringTableLen = functionStringsMaxLen; + } + else + { + curFloatTable = globalFloats; + curStringTable = globalStrings; + curStringTableLen = globalStringsMaxLen; + + // If requested stack frame isn't available, request a new one + // (this prevents assert failures when creating local + // variables without a stack frame) + if (gEvalState.getStackDepth() <= setFrame) + setFrame = -1; + + // Do we want this code to execute using a new stack frame? + if (setFrame < 0) + { + gEvalState.pushFrame(NULL, NULL); + popFrame = true; + } + else + { + // We want to copy a reference to an existing stack frame + // on to the top of the stack. Any change that occurs to + // the locals during this new frame will also occur in the + // original frame. + S32 stackIndex = gEvalState.getStackDepth() - setFrame - 1; + gEvalState.pushFrameRef( stackIndex ); + popFrame = true; + } + } + + // Grab the state of the telenet debugger here once + // so that the push and pop frames are always balanced. + const bool telDebuggerOn = TelDebugger && TelDebugger->isConnected(); + if ( telDebuggerOn && setFrame < 0 ) + TelDebugger->pushStackFrame(); + + StringTableEntry var, objParent; + StringTableEntry fnName; + StringTableEntry fnNamespace, fnPackage; + + // Add local object creation stack [7/9/2007 Black] + static const U32 objectCreationStackSize = 32; + U32 objectCreationStackIndex = 0; + struct { + SimObject *newObject; + U32 failJump; + } objectCreationStack[ objectCreationStackSize ]; + + SimObject *currentNewObject = 0; + U32 failJump = 0; + StringTableEntry prevField = NULL; + StringTableEntry curField = NULL; + SimObject *prevObject = NULL; + SimObject *curObject = NULL; + SimObject *saveObject=NULL; + Namespace::Entry *nsEntry; + Namespace *ns; + const char* curFNDocBlock = NULL; + const char* curNSDocBlock = NULL; + const S32 nsDocLength = 128; + char nsDocBlockClass[nsDocLength]; + + U32 callArgc; + const char **callArgv; + + static char curFieldArray[256]; + static char prevFieldArray[256]; + + CodeBlock *saveCodeBlock = smCurrentCodeBlock; + smCurrentCodeBlock = this; + if(this->name) + { + Con::gCurrentFile = this->name; + Con::gCurrentRoot = this->modPath; + } + const char * val; + + // The frame temp is used by the variable accessor ops (OP_SAVEFIELD_* and + // OP_LOADFIELD_*) to store temporary values for the fields. + static S32 VAL_BUFFER_SIZE = 1024; + FrameTemp valBuffer( VAL_BUFFER_SIZE ); + + for(;;) + { + U32 instruction = code[ip++]; + nsEntry = NULL; +breakContinue: + switch(instruction) + { + case OP_FUNC_DECL: + if(!noCalls) + { + fnName = U32toSTE(code[ip]); + fnNamespace = U32toSTE(code[ip+1]); + fnPackage = U32toSTE(code[ip+2]); + bool hasBody = ( code[ ip + 3 ] & 0x01 ) != 0; + U32 lineNumber = code[ ip + 3 ] >> 1; + + Namespace::unlinkPackages(); + ns = Namespace::find(fnNamespace, fnPackage); + ns->addFunction(fnName, this, hasBody ? ip : 0, curFNDocBlock ? dStrdup( curFNDocBlock ) : NULL, lineNumber );// if no body, set the IP to 0 + if( curNSDocBlock ) + { + if( fnNamespace == StringTable->lookup( nsDocBlockClass ) ) + { + char *usageStr = dStrdup( curNSDocBlock ); + usageStr[dStrlen(usageStr)] = '\0'; + ns->mUsage = usageStr; + ns->mCleanUpUsage = true; + curNSDocBlock = NULL; + } + } + Namespace::relinkPackages(); + + // If we had a docblock, it's definitely not valid anymore, so clear it out. + curFNDocBlock = NULL; + + //Con::printf("Adding function %s::%s (%d)", fnNamespace, fnName, ip); + } + ip = code[ip + 4]; + break; + + case OP_CREATE_OBJECT: + { + // Read some useful info. + objParent = U32toSTE(code[ip ]); + bool isDataBlock = code[ip + 1]; + bool isInternal = code[ip + 2]; + bool isSingleton = code[ip + 3]; + U32 lineNumber = code[ip + 4]; + failJump = code[ip + 5]; + + // If we don't allow calls, we certainly don't allow creating objects! + // Moved this to after failJump is set. Engine was crashing when + // noCalls = true and an object was being created at the beginning of + // a file. ADL. + if(noCalls) + { + ip = failJump; + break; + } + + // Push the old info to the stack + //Assert( objectCreationStackIndex < objectCreationStackSize ); + objectCreationStack[ objectCreationStackIndex ].newObject = currentNewObject; + objectCreationStack[ objectCreationStackIndex++ ].failJump = failJump; + + // Get the constructor information off the stack. + STR.getArgcArgv(NULL, &callArgc, &callArgv); + const char* objectName = callArgv[ 2 ]; + + // Con::printf("Creating object..."); + + // objectName = argv[1]... + currentNewObject = NULL; + + // Are we creating a datablock? If so, deal with case where we override + // an old one. + if(isDataBlock) + { + // Con::printf(" - is a datablock"); + + // Find the old one if any. + SimObject *db = Sim::getDataBlockGroup()->findObject( objectName ); + + // Make sure we're not changing types on ourselves... + if(db && dStricmp(db->getClassName(), callArgv[1])) + { + Con::errorf(ConsoleLogEntry::General, "%s: Cannot re-declare data block %s with a different class.", getFileLine(ip), objectName); + ip = failJump; + STR.popFrame(); + break; + } + + // If there was one, set the currentNewObject and move on. + if(db) + currentNewObject = db; + } + else if (!isInternal) + { + // IF we aren't looking at a local/internal object, then check if + // this object already exists in the global space + + SimObject *obj = Sim::findObject( objectName ); + if (obj /*&& !obj->isLocalName()*/) + { + if ( isSingleton ) + { + // Make sure we're not trying to change types + if ( dStricmp( obj->getClassName(), callArgv[1] ) != 0 ) + { + Con::errorf(ConsoleLogEntry::General, "%s: Cannot re-declare object [%s] with a different class [%s] - was [%s].", + getFileLine(ip), objectName, callArgv[1], obj->getClassName()); + ip = failJump; + STR.popFrame(); + break; + } + + // We're creating a singleton, so use the found object + // instead of creating a new object. + currentNewObject = obj; + } + else + { + const char* redefineBehavior = Con::getVariable( "$Con::redefineBehavior" ); + + if( dStricmp( redefineBehavior, "replaceExisting" ) == 0 ) + { + // Save our constructor args as the argv vector is stored on the + // string stack and may get stomped if deleteObject triggers + // script execution. + + const char* savedArgv[ StringStack::MaxArgs ]; + dMemcpy( savedArgv, callArgv, sizeof( savedArgv[ 0 ] ) * callArgc ); + + obj->deleteObject(); + obj = NULL; + + dMemcpy( callArgv, savedArgv, sizeof( callArgv[ 0 ] ) * callArgc ); + } + else if( dStricmp( redefineBehavior, "renameNew" ) == 0 ) + { + for( U32 i = 1;; ++ i ) + { + String newName = String::ToString( "%s%i", objectName, i ); + if( !Sim::findObject( newName ) ) + { + objectName = StringTable->insert( newName ); + break; + } + } + } + else if( dStricmp( redefineBehavior, "unnameNew" ) == 0 ) + { + objectName = StringTable->insert( "" ); + } + else if( dStricmp( redefineBehavior, "postfixNew" ) == 0 ) + { + const char* postfix = Con::getVariable( "$Con::redefineBehaviorPostfix" ); + String newName = String::ToString( "%s%s", objectName, postfix ); + + if( Sim::findObject( newName ) ) + { + Con::errorf( ConsoleLogEntry::General, "%s: Cannot re-declare object with postfix [%s].", + getFileLine(ip), newName.c_str() ); + ip = failJump; + STR.popFrame(); + break; + } + else + objectName = StringTable->insert( newName ); + } + else + { + Con::errorf(ConsoleLogEntry::General, "%s: Cannot re-declare object [%s].", + getFileLine(ip), objectName); + ip = failJump; + STR.popFrame(); + break; + } + } + } + } + + STR.popFrame(); + + if(!currentNewObject) + { + // Well, looks like we have to create a new object. + ConsoleObject *object = ConsoleObject::create(callArgv[1]); + + // Deal with failure! + if(!object) + { + Con::errorf(ConsoleLogEntry::General, "%s: Unable to instantiate non-conobject class %s.", getFileLine(ip), callArgv[1]); + ip = failJump; + break; + } + + // Do special datablock init if appropros + if(isDataBlock) + { + SimDataBlock *dataBlock = dynamic_cast(object); + if(dataBlock) + { + dataBlock->assignId(); + } + else + { + // They tried to make a non-datablock with a datablock keyword! + Con::errorf(ConsoleLogEntry::General, "%s: Unable to instantiate non-datablock class %s.", getFileLine(ip), callArgv[1]); + // Clean up... + delete object; + ip = failJump; + break; + } + } + + // Finally, set currentNewObject to point to the new one. + currentNewObject = dynamic_cast(object); + + // Deal with the case of a non-SimObject. + if(!currentNewObject) + { + Con::errorf(ConsoleLogEntry::General, "%s: Unable to instantiate non-SimObject class %s.", getFileLine(ip), callArgv[1]); + delete object; + ip = failJump; + break; + } + + // Set the declaration line + currentNewObject->setDeclarationLine(lineNumber); + + // Set the file that this object was created in + currentNewObject->setFilename(name); + + // Does it have a parent object? (ie, the copy constructor : syntax, not inheriance) + if(*objParent) + { + // Find it! + SimObject *parent; + if(Sim::findObject(objParent, parent)) + { + // Con::printf(" - Parent object found: %s", parent->getClassName()); + + currentNewObject->setCopySource( parent ); + currentNewObject->assignFieldsFrom( parent ); + } + else + { + if ( Con::gObjectCopyFailures == -1 ) + Con::errorf(ConsoleLogEntry::General, "%s: Unable to find parent object %s for %s.", getFileLine(ip), objParent, callArgv[1]); + else + ++Con::gObjectCopyFailures; + + // Fail to create the object. + delete object; + ip = failJump; + break; + } + } + + // If a name was passed, assign it. + if( objectName[ 0 ] ) + { + if( !isInternal ) + currentNewObject->assignName( objectName ); + else + currentNewObject->setInternalName( objectName ); + + // Set the original name + currentNewObject->setOriginalName( objectName ); + } + + // Do the constructor parameters. + if(!currentNewObject->processArguments(callArgc-3, callArgv+3)) + { + delete currentNewObject; + currentNewObject = NULL; + ip = failJump; + break; + } + + // If it's not a datablock, allow people to modify bits of it. + if(!isDataBlock) + { + currentNewObject->setModStaticFields(true); + currentNewObject->setModDynamicFields(true); + } + } + + // Advance the IP past the create info... + ip += 6; + break; + } + + case OP_ADD_OBJECT: + { + // See OP_SETCURVAR for why we do this. + curFNDocBlock = NULL; + curNSDocBlock = NULL; + + // Do we place this object at the root? + bool placeAtRoot = code[ip++]; + + // Con::printf("Adding object %s", currentNewObject->getName()); + + // Make sure it wasn't already added, then add it. + if(currentNewObject->isProperlyAdded() == false) + { + bool ret = false; + + Message *msg = dynamic_cast(currentNewObject); + if(msg) + { + SimObjectId id = Message::getNextMessageID(); + if(id != 0xffffffff) + ret = currentNewObject->registerObject(id); + else + Con::errorf("%s: No more object IDs available for messages", getFileLine(ip)); + } + else + ret = currentNewObject->registerObject(); + + if(! ret) + { + // This error is usually caused by failing to call Parent::initPersistFields in the class' initPersistFields(). + Con::warnf(ConsoleLogEntry::General, "%s: Register object failed for object %s of class %s.", getFileLine(ip), currentNewObject->getName(), currentNewObject->getClassName()); + delete currentNewObject; + ip = failJump; + break; + } + } + + // Are we dealing with a datablock? + SimDataBlock *dataBlock = dynamic_cast(currentNewObject); + static String errorStr; + + // If so, preload it. + if(dataBlock && !dataBlock->preload(true, errorStr)) + { + Con::errorf(ConsoleLogEntry::General, "%s: preload failed for %s: %s.", getFileLine(ip), + currentNewObject->getName(), errorStr.c_str()); + dataBlock->deleteObject(); + ip = failJump; + break; + } + + // What group will we be added to, if any? + U32 groupAddId = intStack[_UINT]; + SimGroup *grp = NULL; + SimSet *set = NULL; + bool isMessage = dynamic_cast(currentNewObject) != NULL; + + if(!placeAtRoot || !currentNewObject->getGroup()) + { + if(! isMessage) + { + if(! placeAtRoot) + { + // Otherwise just add to the requested group or set. + if(!Sim::findObject(groupAddId, grp)) + Sim::findObject(groupAddId, set); + } + + if(placeAtRoot) + { + // Deal with the instantGroup if we're being put at the root or we're adding to a component. + if( Con::gInstantGroup.isEmpty() + || !Sim::findObject( Con::gInstantGroup, grp ) ) + grp = Sim::getRootGroup(); + } + } + + // If we didn't get a group, then make sure we have a pointer to + // the rootgroup. + if(!grp) + grp = Sim::getRootGroup(); + + // add to the parent group + grp->addObject(currentNewObject); + + // If for some reason the add failed, add the object to the + // root group so it won't leak. + if( !currentNewObject->getGroup() ) + Sim::getRootGroup()->addObject( currentNewObject ); + + // add to any set we might be in + if(set) + set->addObject(currentNewObject); + } + + // store the new object's ID on the stack (overwriting the group/set + // id, if one was given, otherwise getting pushed) + if(placeAtRoot) + intStack[_UINT] = currentNewObject->getId(); + else + intStack[++_UINT] = currentNewObject->getId(); + + break; + } + + case OP_END_OBJECT: + { + // If we're not to be placed at the root, make sure we clean up + // our group reference. + bool placeAtRoot = code[ip++]; + if(!placeAtRoot) + _UINT--; + break; + } + + case OP_FINISH_OBJECT: + { + //Assert( objectCreationStackIndex >= 0 ); + // Restore the object info from the stack [7/9/2007 Black] + currentNewObject = objectCreationStack[ --objectCreationStackIndex ].newObject; + failJump = objectCreationStack[ objectCreationStackIndex ].failJump; + break; + } + + case OP_JMPIFFNOT: + if(floatStack[_FLT--]) + { + ip++; + break; + } + ip = code[ip]; + break; + case OP_JMPIFNOT: + if(intStack[_UINT--]) + { + ip++; + break; + } + ip = code[ip]; + break; + case OP_JMPIFF: + if(!floatStack[_FLT--]) + { + ip++; + break; + } + ip = code[ip]; + break; + case OP_JMPIF: + if(!intStack[_UINT--]) + { + ip ++; + break; + } + ip = code[ip]; + break; + case OP_JMPIFNOT_NP: + if(intStack[_UINT]) + { + _UINT--; + ip++; + break; + } + ip = code[ip]; + break; + case OP_JMPIF_NP: + if(!intStack[_UINT]) + { + _UINT--; + ip++; + break; + } + ip = code[ip]; + break; + case OP_JMP: + ip = code[ip]; + break; + + // This fixes a bug when not explicitly returning a value. + case OP_RETURN_VOID: + STR.setStringValue(""); + // We're falling thru here on purpose. + + case OP_RETURN: + + if( iterDepth > 0 ) + { + // Clear iterator state. + while( iterDepth > 0 ) + { + iterStack[ -- _ITER ].mIsStringIter = false; + -- iterDepth; + } + + const char* returnValue = STR.getStringValue(); + STR.rewind(); + STR.setStringValue( returnValue ); // Not nice but works. + } + + goto execFinished; + + case OP_CMPEQ: + intStack[_UINT+1] = bool(floatStack[_FLT] == floatStack[_FLT-1]); + _UINT++; + _FLT -= 2; + break; + + case OP_CMPGR: + intStack[_UINT+1] = bool(floatStack[_FLT] > floatStack[_FLT-1]); + _UINT++; + _FLT -= 2; + break; + + case OP_CMPGE: + intStack[_UINT+1] = bool(floatStack[_FLT] >= floatStack[_FLT-1]); + _UINT++; + _FLT -= 2; + break; + + case OP_CMPLT: + intStack[_UINT+1] = bool(floatStack[_FLT] < floatStack[_FLT-1]); + _UINT++; + _FLT -= 2; + break; + + case OP_CMPLE: + intStack[_UINT+1] = bool(floatStack[_FLT] <= floatStack[_FLT-1]); + _UINT++; + _FLT -= 2; + break; + + case OP_CMPNE: + intStack[_UINT+1] = bool(floatStack[_FLT] != floatStack[_FLT-1]); + _UINT++; + _FLT -= 2; + break; + + case OP_XOR: + intStack[_UINT-1] = intStack[_UINT] ^ intStack[_UINT-1]; + _UINT--; + break; + + case OP_MOD: + if( intStack[_UINT-1] != 0 ) + intStack[_UINT-1] = intStack[_UINT] % intStack[_UINT-1]; + else + intStack[_UINT-1] = 0; + _UINT--; + break; + + case OP_BITAND: + intStack[_UINT-1] = intStack[_UINT] & intStack[_UINT-1]; + _UINT--; + break; + + case OP_BITOR: + intStack[_UINT-1] = intStack[_UINT] | intStack[_UINT-1]; + _UINT--; + break; + + case OP_NOT: + intStack[_UINT] = !intStack[_UINT]; + break; + + case OP_NOTF: + intStack[_UINT+1] = !floatStack[_FLT]; + _FLT--; + _UINT++; + break; + + case OP_ONESCOMPLEMENT: + intStack[_UINT] = ~intStack[_UINT]; + break; + + case OP_SHR: + intStack[_UINT-1] = intStack[_UINT] >> intStack[_UINT-1]; + _UINT--; + break; + + case OP_SHL: + intStack[_UINT-1] = intStack[_UINT] << intStack[_UINT-1]; + _UINT--; + break; + + case OP_AND: + intStack[_UINT-1] = intStack[_UINT] && intStack[_UINT-1]; + _UINT--; + break; + + case OP_OR: + intStack[_UINT-1] = intStack[_UINT] || intStack[_UINT-1]; + _UINT--; + break; + + case OP_ADD: + floatStack[_FLT-1] = floatStack[_FLT] + floatStack[_FLT-1]; + _FLT--; + break; + + case OP_SUB: + floatStack[_FLT-1] = floatStack[_FLT] - floatStack[_FLT-1]; + _FLT--; + break; + + case OP_MUL: + floatStack[_FLT-1] = floatStack[_FLT] * floatStack[_FLT-1]; + _FLT--; + break; + case OP_DIV: + floatStack[_FLT-1] = floatStack[_FLT] / floatStack[_FLT-1]; + _FLT--; + break; + case OP_NEG: + floatStack[_FLT] = -floatStack[_FLT]; + break; + + case OP_SETCURVAR: + var = U32toSTE(code[ip]); + ip++; + + // If a variable is set, then these must be NULL. It is necessary + // to set this here so that the vector parser can appropriately + // identify whether it's dealing with a vector. + prevField = NULL; + prevObject = NULL; + curObject = NULL; + + gEvalState.setCurVarName(var); + + // In order to let docblocks work properly with variables, we have + // clear the current docblock when we do an assign. This way it + // won't inappropriately carry forward to following function decls. + curFNDocBlock = NULL; + curNSDocBlock = NULL; + break; + + case OP_SETCURVAR_CREATE: + var = U32toSTE(code[ip]); + ip++; + + // See OP_SETCURVAR + prevField = NULL; + prevObject = NULL; + curObject = NULL; + + gEvalState.setCurVarNameCreate(var); + + // See OP_SETCURVAR for why we do this. + curFNDocBlock = NULL; + curNSDocBlock = NULL; + break; + + case OP_SETCURVAR_ARRAY: + var = STR.getSTValue(); + + // See OP_SETCURVAR + prevField = NULL; + prevObject = NULL; + curObject = NULL; + + gEvalState.setCurVarName(var); + + // See OP_SETCURVAR for why we do this. + curFNDocBlock = NULL; + curNSDocBlock = NULL; + break; + + case OP_SETCURVAR_ARRAY_CREATE: + var = STR.getSTValue(); + + // See OP_SETCURVAR + prevField = NULL; + prevObject = NULL; + curObject = NULL; + + gEvalState.setCurVarNameCreate(var); + + // See OP_SETCURVAR for why we do this. + curFNDocBlock = NULL; + curNSDocBlock = NULL; + break; + + case OP_LOADVAR_UINT: + intStack[_UINT+1] = gEvalState.getIntVariable(); + _UINT++; + break; + + case OP_LOADVAR_FLT: + floatStack[_FLT+1] = gEvalState.getFloatVariable(); + _FLT++; + break; + + case OP_LOADVAR_STR: + val = gEvalState.getStringVariable(); + STR.setStringValue(val); + break; + + case OP_SAVEVAR_UINT: + gEvalState.setIntVariable(intStack[_UINT]); + break; + + case OP_SAVEVAR_FLT: + gEvalState.setFloatVariable(floatStack[_FLT]); + break; + + case OP_SAVEVAR_STR: + gEvalState.setStringVariable(STR.getStringValue()); + break; + + case OP_SETCUROBJECT: + // Save the previous object for parsing vector fields. + prevObject = curObject; + val = STR.getStringValue(); + + // Sim::findObject will sometimes find valid objects from + // multi-component strings. This makes sure that doesn't + // happen. + for( const char* check = val; *check; check++ ) + { + if( *check == ' ' ) + { + val = ""; + break; + } + } + curObject = Sim::findObject(val); + break; + + case OP_SETCUROBJECT_INTERNAL: + ++ip; // To skip the recurse flag if the object wasn't found + if(curObject) + { + SimSet *set = dynamic_cast(curObject); + if(set) + { + StringTableEntry intName = StringTable->insert(STR.getStringValue()); + bool recurse = code[ip-1]; + SimObject *obj = set->findObjectByInternalName(intName, recurse); + intStack[_UINT+1] = obj ? obj->getId() : 0; + _UINT++; + } + else + { + Con::errorf(ConsoleLogEntry::Script, "%s: Attempt to use -> on non-set %s of class %s.", getFileLine(ip-2), curObject->getName(), curObject->getClassName()); + intStack[_UINT] = 0; + } + } + break; + + case OP_SETCUROBJECT_NEW: + curObject = currentNewObject; + break; + + case OP_SETCURFIELD: + // Save the previous field for parsing vector fields. + prevField = curField; + dStrcpy( prevFieldArray, curFieldArray ); + curField = U32toSTE(code[ip]); + curFieldArray[0] = 0; + ip++; + break; + + case OP_SETCURFIELD_ARRAY: + dStrcpy(curFieldArray, STR.getStringValue()); + break; + + case OP_SETCURFIELD_TYPE: + if(curObject) + curObject->setDataFieldType(code[ip], curField, curFieldArray); + ip++; + break; + + case OP_LOADFIELD_UINT: + if(curObject) + intStack[_UINT+1] = U32(dAtoi(curObject->getDataField(curField, curFieldArray))); + else + { + // The field is not being retrieved from an object. Maybe it's + // a special accessor? + getFieldComponent( prevObject, prevField, prevFieldArray, curField, valBuffer ); + intStack[_UINT+1] = dAtoi( valBuffer ); + } + _UINT++; + break; + + case OP_LOADFIELD_FLT: + if(curObject) + floatStack[_FLT+1] = dAtof(curObject->getDataField(curField, curFieldArray)); + else + { + // The field is not being retrieved from an object. Maybe it's + // a special accessor? + getFieldComponent( prevObject, prevField, prevFieldArray, curField, valBuffer ); + floatStack[_FLT+1] = dAtof( valBuffer ); + } + _FLT++; + break; + + case OP_LOADFIELD_STR: + if(curObject) + { + val = curObject->getDataField(curField, curFieldArray); + STR.setStringValue( val ); + } + else + { + // The field is not being retrieved from an object. Maybe it's + // a special accessor? + getFieldComponent( prevObject, prevField, prevFieldArray, curField, valBuffer ); + STR.setStringValue( valBuffer ); + } + break; + + case OP_SAVEFIELD_UINT: + STR.setIntValue(intStack[_UINT]); + if(curObject) + curObject->setDataField(curField, curFieldArray, STR.getStringValue()); + else + { + // The field is not being set on an object. Maybe it's + // a special accessor? + setFieldComponent( prevObject, prevField, prevFieldArray, curField ); + prevObject = NULL; + } + break; + + case OP_SAVEFIELD_FLT: + STR.setFloatValue(floatStack[_FLT]); + if(curObject) + curObject->setDataField(curField, curFieldArray, STR.getStringValue()); + else + { + // The field is not being set on an object. Maybe it's + // a special accessor? + setFieldComponent( prevObject, prevField, prevFieldArray, curField ); + prevObject = NULL; + } + break; + + case OP_SAVEFIELD_STR: + if(curObject) + curObject->setDataField(curField, curFieldArray, STR.getStringValue()); + else + { + // The field is not being set on an object. Maybe it's + // a special accessor? + setFieldComponent( prevObject, prevField, prevFieldArray, curField ); + prevObject = NULL; + } + break; + + case OP_STR_TO_UINT: + intStack[_UINT+1] = STR.getIntValue(); + _UINT++; + break; + + case OP_STR_TO_FLT: + floatStack[_FLT+1] = STR.getFloatValue(); + _FLT++; + break; + + case OP_STR_TO_NONE: + // This exists simply to deal with certain typecast situations. + break; + + case OP_FLT_TO_UINT: + intStack[_UINT+1] = (S64)floatStack[_FLT]; + _FLT--; + _UINT++; + break; + + case OP_FLT_TO_STR: + STR.setFloatValue(floatStack[_FLT]); + _FLT--; + break; + + case OP_FLT_TO_NONE: + _FLT--; + break; + + case OP_UINT_TO_FLT: + floatStack[_FLT+1] = (F32)intStack[_UINT]; + _UINT--; + _FLT++; + break; + + case OP_UINT_TO_STR: + STR.setIntValue(intStack[_UINT]); + _UINT--; + break; + + case OP_UINT_TO_NONE: + _UINT--; + break; + + case OP_LOADIMMED_UINT: + intStack[_UINT+1] = code[ip++]; + _UINT++; + break; + + case OP_LOADIMMED_FLT: + floatStack[_FLT+1] = curFloatTable[code[ip]]; + ip++; + _FLT++; + break; + + case OP_TAG_TO_STR: + code[ip-1] = OP_LOADIMMED_STR; + // it's possible the string has already been converted + if(U8(curStringTable[code[ip]]) != StringTagPrefixByte) + { + U32 id = GameAddTaggedString(curStringTable + code[ip]); + dSprintf(curStringTable + code[ip] + 1, 7, "%d", id); + *(curStringTable + code[ip]) = StringTagPrefixByte; + } + case OP_LOADIMMED_STR: + STR.setStringValue(curStringTable + code[ip++]); + break; + + case OP_DOCBLOCK_STR: + { + // If the first word of the doc is '\class' or '@class', then this + // is a namespace doc block, otherwise it is a function doc block. + const char* docblock = curStringTable + code[ip++]; + + const char* sansClass = dStrstr( docblock, "@class" ); + if( !sansClass ) + sansClass = dStrstr( docblock, "\\class" ); + + if( sansClass ) + { + // Don't save the class declaration. Scan past the 'class' + // keyword and up to the first whitespace. + sansClass += 7; + S32 index = 0; + while( ( *sansClass != ' ' ) && ( *sansClass != '\n' ) && *sansClass && ( index < ( nsDocLength - 1 ) ) ) + { + nsDocBlockClass[index++] = *sansClass; + sansClass++; + } + nsDocBlockClass[index] = '\0'; + + curNSDocBlock = sansClass + 1; + } + else + curFNDocBlock = docblock; + } + + break; + + case OP_LOADIMMED_IDENT: + STR.setStringValue(U32toSTE(code[ip++])); + break; + + case OP_CALLFUNC_RESOLVE: + // This deals with a function that is potentially living in a namespace. + fnNamespace = U32toSTE(code[ip+1]); + fnName = U32toSTE(code[ip]); + + // Try to look it up. + ns = Namespace::find(fnNamespace); + nsEntry = ns->lookup(fnName); + if(!nsEntry) + { + ip+= 3; + Con::warnf(ConsoleLogEntry::General, + "%s: Unable to find function %s%s%s", + getFileLine(ip-4), fnNamespace ? fnNamespace : "", + fnNamespace ? "::" : "", fnName); + STR.popFrame(); + break; + } + // Now fall through to OP_CALLFUNC... + + case OP_CALLFUNC: + { + // This routingId is set when we query the object as to whether + // it handles this method. It is set to an enum from the table + // above indicating whether it handles it on a component it owns + // or just on the object. + S32 routingId = 0; + + fnName = U32toSTE(code[ip]); + + //if this is called from inside a function, append the ip and codeptr + if( gEvalState.getStackDepth() > 0 ) + { + gEvalState.getCurrentFrame().code = this; + gEvalState.getCurrentFrame().ip = ip - 1; + } + + U32 callType = code[ip+2]; + + ip += 3; + STR.getArgcArgv(fnName, &callArgc, &callArgv); + + const char *componentReturnValue = ""; + + if(callType == FuncCallExprNode::FunctionCall) + { + if( !nsEntry ) + { + // We must not have come from OP_CALLFUNC_RESOLVE, so figure out + // our own entry. + nsEntry = Namespace::global()->lookup( fnName ); + } + ns = NULL; + } + else if(callType == FuncCallExprNode::MethodCall) + { + saveObject = gEvalState.thisObject; + gEvalState.thisObject = Sim::findObject(callArgv[1]); + if(!gEvalState.thisObject) + { + // Go back to the previous saved object. + gEvalState.thisObject = saveObject; + + Con::warnf(ConsoleLogEntry::General,"%s: Unable to find object: '%s' attempting to call function '%s'", getFileLine(ip-4), callArgv[1], fnName); + STR.popFrame(); + break; + } + + bool handlesMethod = gEvalState.thisObject->handlesConsoleMethod(fnName,&routingId); + if( handlesMethod && routingId == MethodOnComponent ) + { + ICallMethod *pComponent = dynamic_cast( gEvalState.thisObject ); + if( pComponent ) + componentReturnValue = pComponent->callMethodArgList( callArgc, callArgv, false ); + } + + ns = gEvalState.thisObject->getNamespace(); + if(ns) + nsEntry = ns->lookup(fnName); + else + nsEntry = NULL; + } + else // it's a ParentCall + { + if(thisNamespace) + { + ns = thisNamespace->mParent; + if(ns) + nsEntry = ns->lookup(fnName); + else + nsEntry = NULL; + } + else + { + ns = NULL; + nsEntry = NULL; + } + } + + Namespace::Entry::CallbackUnion * nsCb = NULL; + const char * nsUsage = NULL; + if (nsEntry) + { + nsCb = &nsEntry->cb; + nsUsage = nsEntry->mUsage; + routingId = 0; + } + if(!nsEntry || noCalls) + { + if(!noCalls && !( routingId == MethodOnComponent ) ) + { + Con::warnf(ConsoleLogEntry::General,"%s: Unknown command %s.", getFileLine(ip-4), fnName); + if(callType == FuncCallExprNode::MethodCall) + { + Con::warnf(ConsoleLogEntry::General, " Object %s(%d) %s", + gEvalState.thisObject->getName() ? gEvalState.thisObject->getName() : "", + gEvalState.thisObject->getId(), Con::getNamespaceList(ns) ); + } + } + STR.popFrame(); + + if( routingId == MethodOnComponent ) + STR.setStringValue( componentReturnValue ); + else + STR.setStringValue( "" ); + + break; + } + if(nsEntry->mType == Namespace::Entry::ConsoleFunctionType) + { + const char *ret = ""; + if(nsEntry->mFunctionOffset) + ret = nsEntry->mCode->exec(nsEntry->mFunctionOffset, fnName, nsEntry->mNamespace, callArgc, callArgv, false, nsEntry->mPackage); + + STR.popFrame(); + STR.setStringValue(ret); + } + else + { + const char* nsName = ns? ns->mName: ""; +#ifndef TORQUE_DEBUG + // [tom, 12/13/2006] This stops tools functions from working in the console, + // which is useful behavior when debugging so I'm ifdefing this out for debug builds. + if(nsEntry->mToolOnly && ! Con::isCurrentScriptToolScript()) + { + Con::errorf(ConsoleLogEntry::Script, "%s: %s::%s - attempting to call tools only function from outside of tools.", getFileLine(ip-4), nsName, fnName); + } + else +#endif + if((nsEntry->mMinArgs && S32(callArgc) < nsEntry->mMinArgs) || (nsEntry->mMaxArgs && S32(callArgc) > nsEntry->mMaxArgs)) + { + Con::warnf(ConsoleLogEntry::Script, "%s: %s::%s - wrong number of arguments (got %i, expected min %i and max %i).", + getFileLine(ip-4), nsName, fnName, + callArgc, nsEntry->mMinArgs, nsEntry->mMaxArgs); + Con::warnf(ConsoleLogEntry::Script, "%s: usage: %s", getFileLine(ip-4), nsEntry->mUsage); + STR.popFrame(); + } + else + { + switch(nsEntry->mType) + { + case Namespace::Entry::StringCallbackType: + { + const char *ret = nsEntry->cb.mStringCallbackFunc(gEvalState.thisObject, callArgc, callArgv); + STR.popFrame(); + if(ret != STR.getStringValue()) + STR.setStringValue(ret); + else + STR.setLen(dStrlen(ret)); + break; + } + case Namespace::Entry::IntCallbackType: + { + S32 result = nsEntry->cb.mIntCallbackFunc(gEvalState.thisObject, callArgc, callArgv); + STR.popFrame(); + if(code[ip] == OP_STR_TO_UINT) + { + ip++; + intStack[++_UINT] = result; + break; + } + else if(code[ip] == OP_STR_TO_FLT) + { + ip++; + floatStack[++_FLT] = result; + break; + } + else if(code[ip] == OP_STR_TO_NONE) + ip++; + else + STR.setIntValue(result); + break; + } + case Namespace::Entry::FloatCallbackType: + { + F64 result = nsEntry->cb.mFloatCallbackFunc(gEvalState.thisObject, callArgc, callArgv); + STR.popFrame(); + if(code[ip] == OP_STR_TO_UINT) + { + ip++; + intStack[++_UINT] = (S64)result; + break; + } + else if(code[ip] == OP_STR_TO_FLT) + { + ip++; + floatStack[++_FLT] = result; + break; + } + else if(code[ip] == OP_STR_TO_NONE) + ip++; + else + STR.setFloatValue(result); + break; + } + case Namespace::Entry::VoidCallbackType: + nsEntry->cb.mVoidCallbackFunc(gEvalState.thisObject, callArgc, callArgv); + if( code[ ip ] != OP_STR_TO_NONE && Con::getBoolVariable( "$Con::warnVoidAssignment", true ) ) + Con::warnf(ConsoleLogEntry::General, "%s: Call to %s in %s uses result of void function call.", getFileLine(ip-4), fnName, functionName); + + STR.popFrame(); + STR.setStringValue(""); + break; + case Namespace::Entry::BoolCallbackType: + { + bool result = nsEntry->cb.mBoolCallbackFunc(gEvalState.thisObject, callArgc, callArgv); + STR.popFrame(); + if(code[ip] == OP_STR_TO_UINT) + { + ip++; + intStack[++_UINT] = result; + break; + } + else if(code[ip] == OP_STR_TO_FLT) + { + ip++; + floatStack[++_FLT] = result; + break; + } + else if(code[ip] == OP_STR_TO_NONE) + ip++; + else + STR.setIntValue(result); + break; + } + } + } + } + + if(callType == FuncCallExprNode::MethodCall) + gEvalState.thisObject = saveObject; + break; + } + case OP_ADVANCE_STR: + STR.advance(); + break; + case OP_ADVANCE_STR_APPENDCHAR: + STR.advanceChar(code[ip++]); + break; + + case OP_ADVANCE_STR_COMMA: + STR.advanceChar('_'); + break; + + case OP_ADVANCE_STR_NUL: + STR.advanceChar(0); + break; + + case OP_REWIND_STR: + STR.rewind(); + break; + + case OP_TERMINATE_REWIND_STR: + STR.rewindTerminate(); + break; + + case OP_COMPARE_STR: + intStack[++_UINT] = STR.compare(); + break; + case OP_PUSH: + STR.push(); + break; + + case OP_PUSH_FRAME: + STR.pushFrame(); + break; + + case OP_ASSERT: + { + if( !intStack[_UINT--] ) + { + const char *message = curStringTable + code[ip]; + + U32 breakLine, inst; + findBreakLine( ip - 1, breakLine, inst ); + + if ( PlatformAssert::processAssert( PlatformAssert::Fatal, + name ? name : "eval", + breakLine, + message ) ) + { + if ( TelDebugger && TelDebugger->isConnected() && breakLine > 0 ) + { + TelDebugger->breakProcess(); + } + else + Platform::debugBreak(); + } + } + + ip++; + break; + } + + case OP_BREAK: + { + //append the ip and codeptr before managing the breakpoint! + AssertFatal( gEvalState.getStackDepth() > 0, "Empty eval stack on break!"); + gEvalState.getCurrentFrame().code = this; + gEvalState.getCurrentFrame().ip = ip - 1; + + U32 breakLine; + findBreakLine(ip-1, breakLine, instruction); + if(!breakLine) + goto breakContinue; + TelDebugger->executionStopped(this, breakLine); + goto breakContinue; + } + + case OP_ITER_BEGIN_STR: + { + iterStack[ _ITER ].mIsStringIter = true; + /* fallthrough */ + } + + case OP_ITER_BEGIN: + { + StringTableEntry varName = U32toSTE( code[ ip ] ); + U32 failIp = code[ ip + 1 ]; + + IterStackRecord& iter = iterStack[ _ITER ]; + + iter.mVariable = gEvalState.getCurrentFrame().add( varName ); + + if( iter.mIsStringIter ) + { + iter.mData.mStr.mString = STR.getStringValue(); + iter.mData.mStr.mIndex = 0; + } + else + { + // Look up the object. + + SimSet* set; + if( !Sim::findObject( STR.getStringValue(), set ) ) + { + Con::errorf( ConsoleLogEntry::General, "No SimSet object '%s'", STR.getStringValue() ); + Con::errorf( ConsoleLogEntry::General, "Did you mean to use 'foreach$' instead of 'foreach'?" ); + ip = failIp; + continue; + } + + // Set up. + + iter.mData.mObj.mSet = set; + iter.mData.mObj.mIndex = 0; + } + + _ITER ++; + iterDepth ++; + + STR.push(); + + ip += 2; + break; + } + + case OP_ITER: + { + U32 breakIp = code[ ip ]; + IterStackRecord& iter = iterStack[ _ITER - 1 ]; + + if( iter.mIsStringIter ) + { + const char* str = iter.mData.mStr.mString; + + U32 startIndex = iter.mData.mStr.mIndex; + U32 endIndex = startIndex; + + // Break if at end. + + if( !str[ startIndex ] ) + { + ip = breakIp; + continue; + } + + // Find right end of current component. + + if( !dIsspace( str[ endIndex ] ) ) + do ++ endIndex; + while( str[ endIndex ] && !dIsspace( str[ endIndex ] ) ); + + // Extract component. + + if( endIndex != startIndex ) + { + char savedChar = str[ endIndex ]; + const_cast< char* >( str )[ endIndex ] = '\0'; // We are on the string stack so this is okay. + iter.mVariable->setStringValue( &str[ startIndex ] ); + const_cast< char* >( str )[ endIndex ] = savedChar; + } + else + iter.mVariable->setStringValue( "" ); + + // Skip separator. + if( str[ endIndex ] != '\0' ) + ++ endIndex; + + iter.mData.mStr.mIndex = endIndex; + } + else + { + U32 index = iter.mData.mObj.mIndex; + SimSet* set = iter.mData.mObj.mSet; + + if( index >= set->size() ) + { + ip = breakIp; + continue; + } + + iter.mVariable->setIntValue( set->at( index )->getId() ); + iter.mData.mObj.mIndex = index + 1; + } + + ++ ip; + break; + } + + case OP_ITER_END: + { + -- _ITER; + -- iterDepth; + + STR.rewind(); + + iterStack[ _ITER ].mIsStringIter = false; + break; + } + + case OP_INVALID: + + default: + // error! + goto execFinished; + } + } +execFinished: + + if ( telDebuggerOn && setFrame < 0 ) + TelDebugger->popStackFrame(); + + if ( popFrame ) + gEvalState.popFrame(); + + if(argv) + { + if(gEvalState.traceOn) + { + traceBuffer[0] = 0; + dStrcat(traceBuffer, "Leaving "); + + if(packageName) + { + dStrcat(traceBuffer, "["); + dStrcat(traceBuffer, packageName); + dStrcat(traceBuffer, "]"); + } + if(thisNamespace && thisNamespace->mName) + { + dSprintf(traceBuffer + dStrlen(traceBuffer), sizeof(traceBuffer) - dStrlen(traceBuffer), + "%s::%s() - return %s", thisNamespace->mName, thisFunctionName, STR.getStringValue()); + } + else + { + dSprintf(traceBuffer + dStrlen(traceBuffer), sizeof(traceBuffer) - dStrlen(traceBuffer), + "%s() - return %s", thisFunctionName, STR.getStringValue()); + } + Con::printf("%s", traceBuffer); + } + } + else + { + delete[] globalStrings; + globalStringsMaxLen = 0; + + delete[] globalFloats; + globalStrings = NULL; + globalFloats = NULL; + } + smCurrentCodeBlock = saveCodeBlock; + if(saveCodeBlock && saveCodeBlock->name) + { + Con::gCurrentFile = saveCodeBlock->name; + Con::gCurrentRoot = saveCodeBlock->modPath; + } + + decRefCount(); + +#ifdef TORQUE_DEBUG + AssertFatal(!(STR.mStartStackSize > stackStart), "String stack not popped enough in script exec"); + AssertFatal(!(STR.mStartStackSize < stackStart), "String stack popped too much in script exec"); +#endif + return STR.getStringValue(); +} + +//------------------------------------------------------------ diff --git a/Engine/source/console/compiler.cpp b/Engine/source/console/compiler.cpp new file mode 100644 index 000000000..b3a9bfb5f --- /dev/null +++ b/Engine/source/console/compiler.cpp @@ -0,0 +1,289 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/console.h" +#include "console/telnetDebugger.h" +#include "platform/event.h" + +#include "console/ast.h" +#include "core/tAlgorithm.h" + +#include "core/strings/findMatch.h" +#include "console/consoleInternal.h" +#include "core/stream/fileStream.h" +#include "console/compiler.h" + +#include "console/simBase.h" + +namespace Compiler +{ + + F64 consoleStringToNumber(const char *str, StringTableEntry file, U32 line) + { + F64 val = dAtof(str); + if(val != 0) + return val; + else if(!dStricmp(str, "true")) + return 1; + else if(!dStricmp(str, "false")) + return 0; + else if(file) + { + Con::warnf(ConsoleLogEntry::General, "%s (%d): string always evaluates to 0.", file, line); + return 0; + } + return 0; + } + + //------------------------------------------------------------ + + CompilerStringTable *gCurrentStringTable, gGlobalStringTable, gFunctionStringTable; + CompilerFloatTable *gCurrentFloatTable, gGlobalFloatTable, gFunctionFloatTable; + DataChunker gConsoleAllocator; + CompilerIdentTable gIdentTable; + CodeBlock *gCurBreakBlock; + + //------------------------------------------------------------ + + + CodeBlock *getBreakCodeBlock() { return gCurBreakBlock; } + void setBreakCodeBlock(CodeBlock *cb) { gCurBreakBlock = cb; } + + //------------------------------------------------------------ + + U32 evalSTEtoU32(StringTableEntry ste, U32) + { + return *((U32 *) &ste); + } + + U32 compileSTEtoU32(StringTableEntry ste, U32 ip) + { + if(ste) + getIdentTable().add(ste, ip); + return 0; + } + + U32 (*STEtoU32)(StringTableEntry ste, U32 ip) = evalSTEtoU32; + + //------------------------------------------------------------ + + bool gSyntaxError = false; + + //------------------------------------------------------------ + + CompilerStringTable *getCurrentStringTable() { return gCurrentStringTable; } + CompilerStringTable &getGlobalStringTable() { return gGlobalStringTable; } + CompilerStringTable &getFunctionStringTable() { return gFunctionStringTable; } + + void setCurrentStringTable (CompilerStringTable* cst) { gCurrentStringTable = cst; } + + CompilerFloatTable *getCurrentFloatTable() { return gCurrentFloatTable; } + CompilerFloatTable &getGlobalFloatTable() { return gGlobalFloatTable; } + CompilerFloatTable &getFunctionFloatTable() { return gFunctionFloatTable; } + + void setCurrentFloatTable (CompilerFloatTable* cst) { gCurrentFloatTable = cst; } + + CompilerIdentTable &getIdentTable() { return gIdentTable; } + + void precompileIdent(StringTableEntry ident) + { + if(ident) + gGlobalStringTable.add(ident); + } + + void resetTables() + { + setCurrentStringTable(&gGlobalStringTable); + setCurrentFloatTable(&gGlobalFloatTable); + getGlobalFloatTable().reset(); + getGlobalStringTable().reset(); + getFunctionFloatTable().reset(); + getFunctionStringTable().reset(); + getIdentTable().reset(); + } + + void *consoleAlloc(U32 size) { return gConsoleAllocator.alloc(size); } + void consoleAllocReset() { gConsoleAllocator.freeBlocks(); } + +} + +//------------------------------------------------------------------------- + +using namespace Compiler; + +//------------------------------------------------------------------------- + + +U32 CompilerStringTable::add(const char *str, bool caseSens, bool tag) +{ + // Is it already in? + Entry **walk; + for(walk = &list; *walk; walk = &((*walk)->next)) + { + if((*walk)->tag != tag) + continue; + + if(caseSens) + { + if(!dStrcmp((*walk)->string, str)) + return (*walk)->start; + } + else + { + if(!dStricmp((*walk)->string, str)) + return (*walk)->start; + } + } + + // Write it out. + Entry *newStr = (Entry *) consoleAlloc(sizeof(Entry)); + *walk = newStr; + newStr->next = NULL; + newStr->start = totalLen; + U32 len = dStrlen(str) + 1; + if(tag && len < 7) // alloc space for the numeric tag 1 for tag, 5 for # and 1 for nul + len = 7; + totalLen += len; + newStr->string = (char *) consoleAlloc(len); + newStr->len = len; + newStr->tag = tag; + dStrcpy(newStr->string, str); + return newStr->start; +} + +U32 CompilerStringTable::addIntString(U32 value) +{ + dSprintf(buf, sizeof(buf), "%d", value); + return add(buf); +} + +U32 CompilerStringTable::addFloatString(F64 value) +{ + dSprintf(buf, sizeof(buf), "%g", value); + return add(buf); +} + +void CompilerStringTable::reset() +{ + list = NULL; + totalLen = 0; +} + +char *CompilerStringTable::build() +{ + char *ret = new char[totalLen]; + for(Entry *walk = list; walk; walk = walk->next) + dStrcpy(ret + walk->start, walk->string); + return ret; +} + +void CompilerStringTable::write(Stream &st) +{ + st.write(totalLen); + for(Entry *walk = list; walk; walk = walk->next) + st.write(walk->len, walk->string); +} + +//------------------------------------------------------------ + +U32 CompilerFloatTable::add(F64 value) +{ + Entry **walk; + U32 i = 0; + for(walk = &list; *walk; walk = &((*walk)->next), i++) + if(value == (*walk)->val) + return i; + Entry *newFloat = (Entry *) consoleAlloc(sizeof(Entry)); + newFloat->val = value; + newFloat->next = NULL; + count++; + *walk = newFloat; + return count-1; +} +void CompilerFloatTable::reset() +{ + list = NULL; + count = 0; +} +F64 *CompilerFloatTable::build() +{ + F64 *ret = new F64[count]; + U32 i = 0; + for(Entry *walk = list; walk; walk = walk->next, i++) + ret[i] = walk->val; + return ret; +} + +void CompilerFloatTable::write(Stream &st) +{ + st.write(count); + for(Entry *walk = list; walk; walk = walk->next) + st.write(walk->val); +} + +//------------------------------------------------------------ + +void CompilerIdentTable::reset() +{ + list = NULL; +} + +void CompilerIdentTable::add(StringTableEntry ste, U32 ip) +{ + U32 index = gGlobalStringTable.add(ste, false); + Entry *newEntry = (Entry *) consoleAlloc(sizeof(Entry)); + newEntry->offset = index; + newEntry->ip = ip; + for(Entry *walk = list; walk; walk = walk->next) + { + if(walk->offset == index) + { + newEntry->nextIdent = walk->nextIdent; + walk->nextIdent = newEntry; + return; + } + } + newEntry->next = list; + list = newEntry; + newEntry->nextIdent = NULL; +} + +void CompilerIdentTable::write(Stream &st) +{ + U32 count = 0; + Entry * walk; + for(walk = list; walk; walk = walk->next) + count++; + st.write(count); + for(walk = list; walk; walk = walk->next) + { + U32 ec = 0; + Entry * el; + for(el = walk; el; el = el->nextIdent) + ec++; + st.write(walk->offset); + st.write(ec); + for(el = walk; el; el = el->nextIdent) + st.write(el->ip); + } +} diff --git a/Engine/source/console/compiler.h b/Engine/source/console/compiler.h new file mode 100644 index 000000000..66e3966b9 --- /dev/null +++ b/Engine/source/console/compiler.h @@ -0,0 +1,260 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + + +#ifndef _COMPILER_H_ +#define _COMPILER_H_ + +class Stream; +class DataChunker; + +#include "platform/platform.h" +#include "console/ast.h" +#include "console/codeBlock.h" + + +namespace Compiler +{ + /// The opcodes for the TorqueScript VM. + enum CompiledInstructions + { + OP_FUNC_DECL, + OP_CREATE_OBJECT, + OP_ADD_OBJECT, + OP_END_OBJECT, + // Added to fix the stack issue [7/9/2007 Black] + OP_FINISH_OBJECT, + + OP_JMPIFFNOT, + OP_JMPIFNOT, + OP_JMPIFF, + OP_JMPIF, + OP_JMPIFNOT_NP, + OP_JMPIF_NP, + OP_JMP, + OP_RETURN, + // fixes a bug when not explicitly returning a value + OP_RETURN_VOID, + OP_CMPEQ, + OP_CMPGR, + OP_CMPGE, + OP_CMPLT, + OP_CMPLE, + OP_CMPNE, + OP_XOR, + OP_MOD, + OP_BITAND, + OP_BITOR, + OP_NOT, + OP_NOTF, + OP_ONESCOMPLEMENT, + + OP_SHR, + OP_SHL, + OP_AND, + OP_OR, + + OP_ADD, + OP_SUB, + OP_MUL, + OP_DIV, + OP_NEG, + + OP_SETCURVAR, + OP_SETCURVAR_CREATE, + OP_SETCURVAR_ARRAY, + OP_SETCURVAR_ARRAY_CREATE, + + OP_LOADVAR_UINT, + OP_LOADVAR_FLT, + OP_LOADVAR_STR, + + OP_SAVEVAR_UINT, + OP_SAVEVAR_FLT, + OP_SAVEVAR_STR, + + OP_SETCUROBJECT, + OP_SETCUROBJECT_NEW, + OP_SETCUROBJECT_INTERNAL, + + OP_SETCURFIELD, + OP_SETCURFIELD_ARRAY, + OP_SETCURFIELD_TYPE, + + OP_LOADFIELD_UINT, + OP_LOADFIELD_FLT, + OP_LOADFIELD_STR, + + OP_SAVEFIELD_UINT, + OP_SAVEFIELD_FLT, + OP_SAVEFIELD_STR, + + OP_STR_TO_UINT, + OP_STR_TO_FLT, + OP_STR_TO_NONE, + OP_FLT_TO_UINT, + OP_FLT_TO_STR, + OP_FLT_TO_NONE, + OP_UINT_TO_FLT, + OP_UINT_TO_STR, + OP_UINT_TO_NONE, + + OP_LOADIMMED_UINT, + OP_LOADIMMED_FLT, + OP_TAG_TO_STR, + OP_LOADIMMED_STR, + OP_DOCBLOCK_STR, + OP_LOADIMMED_IDENT, + + OP_CALLFUNC_RESOLVE, + OP_CALLFUNC, + + OP_ADVANCE_STR, + OP_ADVANCE_STR_APPENDCHAR, + OP_ADVANCE_STR_COMMA, + OP_ADVANCE_STR_NUL, + OP_REWIND_STR, + OP_TERMINATE_REWIND_STR, + OP_COMPARE_STR, + + OP_PUSH, + OP_PUSH_FRAME, + + OP_ASSERT, + OP_BREAK, + + OP_ITER_BEGIN, ///< Prepare foreach iterator. + OP_ITER_BEGIN_STR, ///< Prepare foreach$ iterator. + OP_ITER, ///< Enter foreach loop. + OP_ITER_END, ///< End foreach loop. + + OP_INVALID + }; + + //------------------------------------------------------------ + + F64 consoleStringToNumber(const char *str, StringTableEntry file = 0, U32 line = 0); + U32 precompileBlock(StmtNode *block, U32 loopCount); + U32 compileBlock(StmtNode *block, U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint); + + //------------------------------------------------------------ + + struct CompilerIdentTable + { + struct Entry + { + U32 offset; + U32 ip; + Entry *next; + Entry *nextIdent; + }; + Entry *list; + void add(StringTableEntry ste, U32 ip); + void reset(); + void write(Stream &st); + }; + + //------------------------------------------------------------ + + struct CompilerStringTable + { + U32 totalLen; + struct Entry + { + char *string; + U32 start; + U32 len; + bool tag; + Entry *next; + }; + Entry *list; + + char buf[256]; + + U32 add(const char *str, bool caseSens = true, bool tag = false); + U32 addIntString(U32 value); + U32 addFloatString(F64 value); + void reset(); + char *build(); + void write(Stream &st); + }; + + //------------------------------------------------------------ + + struct CompilerFloatTable + { + struct Entry + { + F64 val; + Entry *next; + }; + U32 count; + Entry *list; + + U32 add(F64 value); + void reset(); + F64 *build(); + void write(Stream &st); + }; + + //------------------------------------------------------------ + + inline StringTableEntry U32toSTE(U32 u) + { + return *((StringTableEntry *) &u); + } + + extern U32 (*STEtoU32)(StringTableEntry ste, U32 ip); + + U32 evalSTEtoU32(StringTableEntry ste, U32); + U32 compileSTEtoU32(StringTableEntry ste, U32 ip); + + CompilerStringTable *getCurrentStringTable(); + CompilerStringTable &getGlobalStringTable(); + CompilerStringTable &getFunctionStringTable(); + + void setCurrentStringTable (CompilerStringTable* cst); + + CompilerFloatTable *getCurrentFloatTable(); + CompilerFloatTable &getGlobalFloatTable(); + CompilerFloatTable &getFunctionFloatTable(); + + void setCurrentFloatTable (CompilerFloatTable* cst); + + CompilerIdentTable &getIdentTable(); + + void precompileIdent(StringTableEntry ident); + + CodeBlock *getBreakCodeBlock(); + void setBreakCodeBlock(CodeBlock *cb); + + /// Helper function to reset the float, string, and ident tables to a base + /// starting state. + void resetTables(); + + void *consoleAlloc(U32 size); + void consoleAllocReset(); + + extern bool gSyntaxError; +}; + +#endif diff --git a/Engine/source/console/console.cpp b/Engine/source/console/console.cpp new file mode 100644 index 000000000..0e7099ff2 --- /dev/null +++ b/Engine/source/console/console.cpp @@ -0,0 +1,1457 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "platform/platformTLS.h" +#include "platform/threads/thread.h" +#include "console/console.h" +#include "console/consoleInternal.h" +#include "console/consoleObject.h" +#include "console/consoleParser.h" +#include "core/stream/fileStream.h" +#include "console/ast.h" +#include "core/tAlgorithm.h" +#include "console/consoleTypes.h" +#include "console/telnetDebugger.h" +#include "console/simBase.h" +#include "console/compiler.h" +#include "console/stringStack.h" +#include "console/ICallMethod.h" +#include "console/engineAPI.h" +#include +#include "platform/threads/mutex.h" + + +extern StringStack STR; + +ConsoleDocFragment* ConsoleDocFragment::smFirst; +ExprEvalState gEvalState; +StmtNode *gStatementList; +ConsoleConstructor *ConsoleConstructor::first = NULL; +bool gWarnUndefinedScriptVariables; + +static char scratchBuffer[4096]; + +CON_DECLARE_PARSER(CMD); + +static const char * prependDollar ( const char * name ) +{ + if(name[0] != '$') + { + S32 len = dStrlen(name); + AssertFatal(len < sizeof(scratchBuffer)-2, "CONSOLE: name too long"); + scratchBuffer[0] = '$'; + dMemcpy(scratchBuffer + 1, name, len + 1); + name = scratchBuffer; + } + return name; +} + +static const char * prependPercent ( const char * name ) +{ + if(name[0] != '%') + { + S32 len = dStrlen(name); + AssertFatal(len < sizeof(scratchBuffer)-2, "CONSOLE: name too long"); + scratchBuffer[0] = '%'; + dMemcpy(scratchBuffer + 1, name, len + 1); + name = scratchBuffer; + } + return name; +} + +//-------------------------------------- +void ConsoleConstructor::init( const char *cName, const char *fName, const char *usg, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) +{ + mina = minArgs; + maxa = maxArgs; + funcName = fName; + usage = usg; + className = cName; + sc = 0; fc = 0; vc = 0; bc = 0; ic = 0; + callback = group = false; + next = first; + ns = false; + first = this; + toolOnly = isToolOnly; + this->header = header; +} + +void ConsoleConstructor::setup() +{ + for(ConsoleConstructor *walk = first; walk; walk = walk->next) + { +#ifdef TORQUE_DEBUG + walk->validate(); +#endif + + if( walk->sc ) + Con::addCommand( walk->className, walk->funcName, walk->sc, walk->usage, walk->mina, walk->maxa, walk->toolOnly, walk->header ); + else if( walk->ic ) + Con::addCommand( walk->className, walk->funcName, walk->ic, walk->usage, walk->mina, walk->maxa, walk->toolOnly, walk->header ); + else if( walk->fc ) + Con::addCommand( walk->className, walk->funcName, walk->fc, walk->usage, walk->mina, walk->maxa, walk->toolOnly, walk->header ); + else if( walk->vc ) + Con::addCommand( walk->className, walk->funcName, walk->vc, walk->usage, walk->mina, walk->maxa, walk->toolOnly, walk->header ); + else if( walk->bc ) + Con::addCommand( walk->className, walk->funcName, walk->bc, walk->usage, walk->mina, walk->maxa, walk->toolOnly, walk->header ); + else if( walk->group ) + Con::markCommandGroup( walk->className, walk->funcName, walk->usage ); + else if( walk->callback ) + Con::noteScriptCallback( walk->className, walk->funcName, walk->usage, walk->header ); + else if( walk->ns ) + { + Namespace* ns = Namespace::find( StringTable->insert( walk->className ) ); + if( ns ) + ns->mUsage = walk->usage; + } + else + { + AssertISV( false, "Found a ConsoleConstructor with an indeterminate type!" ); + } + } +} + +ConsoleConstructor::ConsoleConstructor(const char *className, const char *funcName, StringCallback sfunc, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) +{ + init( className, funcName, usage, minArgs, maxArgs, isToolOnly, header ); + sc = sfunc; +} + +ConsoleConstructor::ConsoleConstructor(const char *className, const char *funcName, IntCallback ifunc, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) +{ + init( className, funcName, usage, minArgs, maxArgs, isToolOnly, header ); + ic = ifunc; +} + +ConsoleConstructor::ConsoleConstructor(const char *className, const char *funcName, FloatCallback ffunc, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) +{ + init( className, funcName, usage, minArgs, maxArgs, isToolOnly, header ); + fc = ffunc; +} + +ConsoleConstructor::ConsoleConstructor(const char *className, const char *funcName, VoidCallback vfunc, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) +{ + init( className, funcName, usage, minArgs, maxArgs, isToolOnly, header ); + vc = vfunc; +} + +ConsoleConstructor::ConsoleConstructor(const char *className, const char *funcName, BoolCallback bfunc, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) +{ + init( className, funcName, usage, minArgs, maxArgs, isToolOnly, header ); + bc = bfunc; +} + +ConsoleConstructor::ConsoleConstructor(const char* className, const char* groupName, const char* aUsage) +{ + init(className, groupName, usage, -1, -2); + + group = true; + + // Somewhere, the entry list is getting flipped, partially. + // so we have to do tricks to deal with making sure usage + // is properly populated. + + // This is probably redundant. + static char * lastUsage = NULL; + if(aUsage) + lastUsage = (char *)aUsage; + + usage = lastUsage; +} + +ConsoleConstructor::ConsoleConstructor(const char *className, const char *callbackName, const char *usage, ConsoleFunctionHeader* header ) +{ + init( className, callbackName, usage, -2, -3, false, header ); + callback = true; + ns = true; +} + +void ConsoleConstructor::validate() +{ +#ifdef TORQUE_DEBUG + // Don't do the following check if we're not a method/func. + if(this->group) + return; + + // In debug, walk the list and make sure this isn't a duplicate. + for(ConsoleConstructor *walk = first; walk; walk = walk->next) + { + // Skip mismatching func/method names. + if(dStricmp(walk->funcName, this->funcName)) + continue; + + // Don't compare functions with methods or vice versa. + if(bool(this->className) != bool(walk->className)) + continue; + + // Skip mismatching classnames, if they're present. + if(this->className && walk->className && dStricmp(walk->className, this->className)) + continue; + + // If we encounter ourselves, stop searching; this prevents duplicate + // firing of the assert, instead only firing for the latter encountered + // entry. + if(this == walk) + break; + + // Match! + if(this->className) + { + AssertISV(false, avar("ConsoleConstructor::setup - ConsoleMethod '%s::%s' collides with another of the same name.", this->className, this->funcName)); + } + else + { + AssertISV(false, avar("ConsoleConstructor::setup - ConsoleFunction '%s' collides with another of the same name.", this->funcName)); + } + } +#endif +} + +// We comment out the implementation of the Con namespace when doxygenizing because +// otherwise Doxygen decides to ignore our docs in console.h +#ifndef DOXYGENIZING + +namespace Con +{ + +static Vector gConsumers(__FILE__, __LINE__); +static Vector< String > sInstantGroupStack( __FILE__, __LINE__ ); +static DataChunker consoleLogChunker; +static Vector consoleLog(__FILE__, __LINE__); +static bool consoleLogLocked; +static bool logBufferEnabled=true; +static S32 printLevel = 10; +static FileStream consoleLogFile; +static const char *defLogFileName = "console.log"; +static S32 consoleLogMode = 0; +static bool active = false; +static bool newLogFile; +static const char *logFileName; + +static const int MaxCompletionBufferSize = 4096; +static char completionBuffer[MaxCompletionBufferSize]; +static char tabBuffer[MaxCompletionBufferSize] = {0}; +static SimObjectPtr tabObject; +static U32 completionBaseStart; +static U32 completionBaseLen; + +String gInstantGroup; +Con::ConsoleInputEvent smConsoleInput; + +/// Current script file name and root, these are registered as +/// console variables. +/// @{ + +/// +StringTableEntry gCurrentFile; +StringTableEntry gCurrentRoot; +/// @} + +S32 gObjectCopyFailures = -1; + +bool alwaysUseDebugOutput = true; +bool useTimestamp = false; + +ConsoleFunctionGroupBegin( Clipboard, "Miscellaneous functions to control the clipboard and clear the console."); + +ConsoleFunction( cls, void, 1, 1, "()" + "@brief Clears the console output.\n\n" + "@ingroup Console") +{ + if(consoleLogLocked) + return; + consoleLogChunker.freeBlocks(); + consoleLog.setSize(0); +}; + +ConsoleFunction( getClipboard, const char*, 1, 1, "()" + "@brief Get text from the clipboard.\n\n" + "@internal") +{ + return Platform::getClipboard(); +}; + +ConsoleFunction( setClipboard, bool, 2, 2, "(string text)" + "@brief Set the system clipboard.\n\n" + "@internal") +{ + return Platform::setClipboard(argv[1]); +}; + +ConsoleFunctionGroupEnd( Clipboard ); + + +void postConsoleInput( RawData data ); + +void init() +{ + AssertFatal(active == false, "Con::init should only be called once."); + + // Set up general init values. + active = true; + logFileName = NULL; + newLogFile = true; + gWarnUndefinedScriptVariables = false; + + // Initialize subsystems. + Namespace::init(); + ConsoleConstructor::setup(); + + // Set up the parser(s) + CON_ADD_PARSER(CMD, "cs", true); // TorqueScript + + // Setup the console types. + ConsoleBaseType::initialize(); + + // And finally, the ACR... + AbstractClassRep::initialize(); + + // Variables + setVariable("Con::prompt", "% "); + addVariable("Con::logBufferEnabled", TypeBool, &logBufferEnabled, "If true, the log buffer will be enabled.\n" + "@ingroup Console\n"); + addVariable("Con::printLevel", TypeS32, &printLevel, + "@brief This is deprecated.\n\n" + "It is no longer in use and does nothing.\n" + "@ingroup Console\n"); + addVariable("Con::warnUndefinedVariables", TypeBool, &gWarnUndefinedScriptVariables, "If true, a warning will be displayed in the console whenever a undefined variable is used in script.\n" + "@ingroup Console\n"); + addVariable( "instantGroup", TypeRealString, &gInstantGroup, "The group that objects will be added to when they are created.\n" + "@ingroup Console\n"); + + addVariable("Con::objectCopyFailures", TypeS32, &gObjectCopyFailures, "If greater than zero then it counts the number of object creation " + "failures based on a missing copy object and does not report an error..\n" + "@ingroup Console\n"); + + // Current script file name and root + addVariable( "Con::File", TypeString, &gCurrentFile, "The currently executing script file.\n" + "@ingroup FileSystem\n"); + addVariable( "Con::Root", TypeString, &gCurrentRoot, "The mod folder for the currently executing script file.\n" + "@ingroup FileSystem\n" ); + + // alwaysUseDebugOutput determines whether to send output to the platform's + // "debug" system. see winConsole for an example. + // in ship builds we don't expose this variable to script + // and we set it to false by default (don't want to provide more information + // to potential hackers). platform code should also ifdef out the code that + // pays attention to this in ship builds (see winConsole.cpp) + // note that enabling this can slow down your game + // if you are running from the debugger and printing a lot of console messages. +#ifndef TORQUE_SHIPPING + addVariable("Con::alwaysUseDebugOutput", TypeBool, &alwaysUseDebugOutput, + "@brief Determines whether to send output to the platform's \"debug\" system.\n\n" + "@note This is disabled in shipping builds.\n" + "@ingroup Console"); +#else + alwaysUseDebugOutput = false; +#endif + + // controls whether a timestamp is prepended to every console message + addVariable("Con::useTimestamp", TypeBool, &useTimestamp, "If true a timestamp is prepended to every console message.\n" + "@ingroup Console\n"); + + // Plug us into the journaled console input signal. + smConsoleInput.notify(postConsoleInput); +} + +//-------------------------------------- + +void shutdown() +{ + AssertFatal(active == true, "Con::shutdown should only be called once."); + active = false; + + smConsoleInput.remove(postConsoleInput); + + consoleLogFile.close(); + Namespace::shutdown(); + AbstractClassRep::shutdown(); + Compiler::freeConsoleParserList(); +} + +bool isActive() +{ + return active; +} + +bool isMainThread() +{ +#ifdef TORQUE_MULTITHREAD + return ThreadManager::isMainThread(); +#else + // If we're single threaded we're always in the main thread. + return true; +#endif +} + +//-------------------------------------- + +void getLockLog(ConsoleLogEntry *&log, U32 &size) +{ + consoleLogLocked = true; + log = consoleLog.address(); + size = consoleLog.size(); +} + +void unlockLog() +{ + consoleLogLocked = false; +} + +U32 tabComplete(char* inputBuffer, U32 cursorPos, U32 maxResultLength, bool forwardTab) +{ + // Check for null input. + if (!inputBuffer[0]) + { + return cursorPos; + } + + // Cap the max result length. + if (maxResultLength > MaxCompletionBufferSize) + { + maxResultLength = MaxCompletionBufferSize; + } + + // See if this is the same partial text as last checked. + if (dStrcmp(tabBuffer, inputBuffer)) + { + // If not... + // Save it for checking next time. + dStrcpy(tabBuffer, inputBuffer); + // Scan backward from the cursor position to find the base to complete from. + S32 p = cursorPos; + while ((p > 0) && (inputBuffer[p - 1] != ' ') && (inputBuffer[p - 1] != '.') && (inputBuffer[p - 1] != '(')) + { + p--; + } + completionBaseStart = p; + completionBaseLen = cursorPos - p; + // Is this function being invoked on an object? + if (inputBuffer[p - 1] == '.') + { + // If so... + if (p <= 1) + { + // Bail if no object identifier. + return cursorPos; + } + + // Find the object identifier. + S32 objLast = --p; + while ((p > 0) && (inputBuffer[p - 1] != ' ') && (inputBuffer[p - 1] != '(')) + { + p--; + } + + if (objLast == p) + { + // Bail if no object identifier. + return cursorPos; + } + + // Look up the object identifier. + dStrncpy(completionBuffer, inputBuffer + p, objLast - p); + completionBuffer[objLast - p] = 0; + tabObject = Sim::findObject(completionBuffer); + if (tabObject == NULL) + { + // Bail if not found. + return cursorPos; + } + } + else + { + // Not invoked on an object; we'll use the global namespace. + tabObject = 0; + } + } + + // Chop off the input text at the cursor position. + inputBuffer[cursorPos] = 0; + + // Try to find a completion in the appropriate namespace. + const char *newText; + + if (tabObject != 0) + { + newText = tabObject->tabComplete(inputBuffer + completionBaseStart, completionBaseLen, forwardTab); + } + else + { + // In the global namespace, we can complete on global vars as well as functions. + if (inputBuffer[completionBaseStart] == '$') + { + newText = gEvalState.globalVars.tabComplete(inputBuffer + completionBaseStart, completionBaseLen, forwardTab); + } + else + { + newText = Namespace::global()->tabComplete(inputBuffer + completionBaseStart, completionBaseLen, forwardTab); + } + } + + if (newText) + { + // If we got something, append it to the input text. + S32 len = dStrlen(newText); + if (len + completionBaseStart > maxResultLength) + { + len = maxResultLength - completionBaseStart; + } + dStrncpy(inputBuffer + completionBaseStart, newText, len); + inputBuffer[completionBaseStart + len] = 0; + // And set the cursor after it. + cursorPos = completionBaseStart + len; + } + + // Save the modified input buffer for checking next time. + dStrcpy(tabBuffer, inputBuffer); + + // Return the new (maybe) cursor position. + return cursorPos; +} + +//------------------------------------------------------------------------------ +static void log(const char *string) +{ + // Bail if we ain't logging. + if (!consoleLogMode) + { + return; + } + + // In mode 1, we open, append, close on each log write. + if ((consoleLogMode & 0x3) == 1) + { + consoleLogFile.open(defLogFileName, Torque::FS::File::ReadWrite); + } + + // Write to the log if its status is hunky-dory. + if ((consoleLogFile.getStatus() == Stream::Ok) || (consoleLogFile.getStatus() == Stream::EOS)) + { + consoleLogFile.setPosition(consoleLogFile.getStreamSize()); + // If this is the first write... + if (newLogFile) + { + // Make a header. + Platform::LocalTime lt; + Platform::getLocalTime(lt); + char buffer[128]; + dSprintf(buffer, sizeof(buffer), "//-------------------------- %d/%d/%d -- %02d:%02d:%02d -----\r\n", + lt.month + 1, + lt.monthday, + lt.year + 1900, + lt.hour, + lt.min, + lt.sec); + consoleLogFile.write(dStrlen(buffer), buffer); + newLogFile = false; + if (consoleLogMode & 0x4) + { + // Dump anything that has been printed to the console so far. + consoleLogMode -= 0x4; + U32 size, line; + ConsoleLogEntry *log; + getLockLog(log, size); + for (line = 0; line < size; line++) + { + consoleLogFile.write(dStrlen(log[line].mString), log[line].mString); + consoleLogFile.write(2, "\r\n"); + } + unlockLog(); + } + } + // Now write what we came here to write. + consoleLogFile.write(dStrlen(string), string); + consoleLogFile.write(2, "\r\n"); + } + + if ((consoleLogMode & 0x3) == 1) + { + consoleLogFile.close(); + } +} + +//------------------------------------------------------------------------------ + +static void _printf(ConsoleLogEntry::Level level, ConsoleLogEntry::Type type, const char* fmt, va_list argptr) +{ + if (!active) + return; + Con::active = false; + + char buffer[8192]; + U32 offset = 0; + if( gEvalState.traceOn && gEvalState.getStackDepth() > 0 ) + { + offset = gEvalState.getStackDepth() * 3; + for(U32 i = 0; i < offset; i++) + buffer[i] = ' '; + } + + if (useTimestamp) + { + static U32 startTime = Platform::getRealMilliseconds(); + U32 curTime = Platform::getRealMilliseconds() - startTime; + offset += dSprintf(buffer + offset, sizeof(buffer) - offset, "[+%4d.%03d]", U32(curTime * 0.001), curTime % 1000); + } + dVsprintf(buffer + offset, sizeof(buffer) - offset, fmt, argptr); + + for(S32 i = 0; i < gConsumers.size(); i++) + gConsumers[i](level, buffer); + + if(logBufferEnabled || consoleLogMode) + { + char *pos = buffer; + while(*pos) + { + if(*pos == '\t') + *pos = '^'; + pos++; + } + pos = buffer; + + for(;;) + { + char *eofPos = dStrchr(pos, '\n'); + if(eofPos) + *eofPos = 0; + + log(pos); + if(logBufferEnabled && !consoleLogLocked) + { + ConsoleLogEntry entry; + entry.mLevel = level; + entry.mType = type; +#ifndef TORQUE_SHIPPING // this is equivalent to a memory leak, turn it off in ship build + entry.mString = (const char *)consoleLogChunker.alloc(dStrlen(pos) + 1); + dStrcpy(const_cast(entry.mString), pos); + + // This prevents infinite recursion if the console itself needs to + // re-allocate memory to accommodate the new console log entry, and + // LOG_PAGE_ALLOCS is defined. It is kind of a dirty hack, but the + // uses for LOG_PAGE_ALLOCS are limited, and it is not worth writing + // a lot of special case code to support this situation. -patw + const bool save = Con::active; + Con::active = false; + consoleLog.push_back(entry); + Con::active = save; +#endif + } + if(!eofPos) + break; + pos = eofPos + 1; + } + } + + Con::active = true; +} + +//------------------------------------------------------------------------------ +void printf(const char* fmt,...) +{ + va_list argptr; + va_start(argptr, fmt); + _printf(ConsoleLogEntry::Normal, ConsoleLogEntry::General, fmt, argptr); + va_end(argptr); +} + +void warnf(ConsoleLogEntry::Type type, const char* fmt,...) +{ + va_list argptr; + va_start(argptr, fmt); + _printf(ConsoleLogEntry::Warning, type, fmt, argptr); + va_end(argptr); +} + +void errorf(ConsoleLogEntry::Type type, const char* fmt,...) +{ + va_list argptr; + va_start(argptr, fmt); + _printf(ConsoleLogEntry::Error, type, fmt, argptr); + va_end(argptr); +} + +void warnf(const char* fmt,...) +{ + va_list argptr; + va_start(argptr, fmt); + _printf(ConsoleLogEntry::Warning, ConsoleLogEntry::General, fmt, argptr); + va_end(argptr); +} + +void errorf(const char* fmt,...) +{ + va_list argptr; + va_start(argptr, fmt); + _printf(ConsoleLogEntry::Error, ConsoleLogEntry::General, fmt, argptr); + va_end(argptr); +} + +//--------------------------------------------------------------------------- + +void setVariable(const char *name, const char *value) +{ + // get the field info from the object.. + if(name[0] != '$' && dStrchr(name, '.') && !isFunction(name)) + { + S32 len = dStrlen(name); + AssertFatal(len < sizeof(scratchBuffer)-1, "Sim::getVariable - name too long"); + dMemcpy(scratchBuffer, name, len+1); + + char * token = dStrtok(scratchBuffer, "."); + SimObject * obj = Sim::findObject(token); + if(!obj) + return; + + token = dStrtok(0, ".\0"); + if(!token) + return; + + while(token != NULL) + { + const char * val = obj->getDataField(StringTable->insert(token), 0); + if(!val) + return; + + char *fieldToken = token; + token = dStrtok(0, ".\0"); + if(token) + { + obj = Sim::findObject(token); + if(!obj) + return; + } + else + { + obj->setDataField(StringTable->insert(fieldToken), 0, value); + } + } + } + + name = prependDollar(name); + gEvalState.globalVars.setVariable(StringTable->insert(name), value); +} + +void setLocalVariable(const char *name, const char *value) +{ + name = prependPercent(name); + gEvalState.getCurrentFrame().setVariable(StringTable->insert(name), value); +} + +void setBoolVariable(const char *varName, bool value) +{ + setVariable(varName, value ? "1" : "0"); +} + +void setIntVariable(const char *varName, S32 value) +{ + char scratchBuffer[32]; + dSprintf(scratchBuffer, sizeof(scratchBuffer), "%d", value); + setVariable(varName, scratchBuffer); +} + +void setFloatVariable(const char *varName, F32 value) +{ + char scratchBuffer[32]; + dSprintf(scratchBuffer, sizeof(scratchBuffer), "%g", value); + setVariable(varName, scratchBuffer); +} + +//--------------------------------------------------------------------------- +void addConsumer(ConsumerCallback consumer) +{ + gConsumers.push_back(consumer); +} + +// dhc - found this empty -- trying what I think is a reasonable impl. +void removeConsumer(ConsumerCallback consumer) +{ + for(S32 i = 0; i < gConsumers.size(); i++) + { + if (gConsumers[i] == consumer) + { + // remove it from the list. + gConsumers.erase(i); + break; + } + } +} + +void stripColorChars(char* line) +{ + char* c = line; + char cp = *c; + while (cp) + { + if (cp < 18) + { + // Could be a color control character; let's take a closer look. + if ((cp != 8) && (cp != 9) && (cp != 10) && (cp != 13)) + { + // Yep... copy following chars forward over this. + char* cprime = c; + char cpp; + do + { + cpp = *++cprime; + *(cprime - 1) = cpp; + } + while (cpp); + // Back up 1 so we'll check this position again post-copy. + c--; + } + } + cp = *++c; + } +} + +const char *getVariable(const char *name) +{ + // get the field info from the object.. + if(name[0] != '$' && dStrchr(name, '.') && !isFunction(name)) + { + S32 len = dStrlen(name); + AssertFatal(len < sizeof(scratchBuffer)-1, "Sim::getVariable - name too long"); + dMemcpy(scratchBuffer, name, len+1); + + char * token = dStrtok(scratchBuffer, "."); + SimObject * obj = Sim::findObject(token); + if(!obj) + return(""); + + token = dStrtok(0, ".\0"); + if(!token) + return(""); + + while(token != NULL) + { + const char * val = obj->getDataField(StringTable->insert(token), 0); + if(!val) + return(""); + + token = dStrtok(0, ".\0"); + if(token) + { + obj = Sim::findObject(token); + if(!obj) + return(""); + } + else + return(val); + } + } + + name = prependDollar(name); + return gEvalState.globalVars.getVariable(StringTable->insert(name)); +} + +const char *getLocalVariable(const char *name) +{ + name = prependPercent(name); + + return gEvalState.getCurrentFrame().getVariable(StringTable->insert(name)); +} + +bool getBoolVariable(const char *varName, bool def) +{ + const char *value = getVariable(varName); + return *value ? dAtob(value) : def; +} + +S32 getIntVariable(const char *varName, S32 def) +{ + const char *value = getVariable(varName); + return *value ? dAtoi(value) : def; +} + +F32 getFloatVariable(const char *varName, F32 def) +{ + const char *value = getVariable(varName); + return *value ? dAtof(value) : def; +} + +//--------------------------------------------------------------------------- + +void addVariable( const char *name, + S32 type, + void *dptr, + const char* usage ) +{ + gEvalState.globalVars.addVariable( name, type, dptr, usage ); +} + +void addConstant( const char *name, + S32 type, + const void *dptr, + const char* usage ) +{ + Dictionary::Entry* entry = gEvalState.globalVars.addVariable( name, type, const_cast< void* >( dptr ), usage ); + entry->mIsConstant = true; +} + +bool removeVariable(const char *name) +{ + name = StringTable->lookup(prependDollar(name)); + return name!=0 && gEvalState.globalVars.removeVariable(name); +} + +void addVariableNotify( const char *name, const NotifyDelegate &callback ) +{ + gEvalState.globalVars.addVariableNotify( name, callback ); +} + +void removeVariableNotify( const char *name, const NotifyDelegate &callback ) +{ + gEvalState.globalVars.removeVariableNotify( name, callback ); +} + +//--------------------------------------------------------------------------- + +void addCommand( const char *nsName, const char *name,StringCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) +{ + Namespace *ns = lookupNamespace(nsName); + ns->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header ); +} + +void addCommand( const char *nsName, const char *name,VoidCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) +{ + Namespace *ns = lookupNamespace(nsName); + ns->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header ); +} + +void addCommand( const char *nsName, const char *name,IntCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) +{ + Namespace *ns = lookupNamespace(nsName); + ns->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header ); +} + +void addCommand( const char *nsName, const char *name,FloatCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) +{ + Namespace *ns = lookupNamespace(nsName); + ns->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header ); +} + +void addCommand( const char *nsName, const char *name,BoolCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) +{ + Namespace *ns = lookupNamespace(nsName); + ns->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header ); +} + +void noteScriptCallback( const char *className, const char *funcName, const char *usage, ConsoleFunctionHeader* header ) +{ + Namespace *ns = lookupNamespace(className); + ns->addScriptCallback( StringTable->insert(funcName), usage, header ); +} + +void markCommandGroup(const char * nsName, const char *name, const char* usage) +{ + Namespace *ns = lookupNamespace(nsName); + ns->markGroup(name,usage); +} + +void beginCommandGroup(const char * nsName, const char *name, const char* usage) +{ + markCommandGroup(nsName, name, usage); +} + +void endCommandGroup(const char * nsName, const char *name) +{ + markCommandGroup(nsName, name, NULL); +} + +void addCommand( const char *name,StringCallback cb,const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) +{ + Namespace::global()->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header ); +} + +void addCommand( const char *name,VoidCallback cb,const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) +{ + Namespace::global()->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header ); +} + +void addCommand( const char *name,IntCallback cb,const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) +{ + Namespace::global()->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header ); +} + +void addCommand( const char *name,FloatCallback cb,const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) +{ + Namespace::global()->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header ); +} + +void addCommand( const char *name,BoolCallback cb,const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) +{ + Namespace::global()->addCommand( StringTable->insert(name), cb, usage, minArgs, maxArgs, isToolOnly, header ); +} + +const char *evaluate(const char* string, bool echo, const char *fileName) +{ + if (echo) + { + if (string[0] == '%') + Con::printf("%s", string); + else + Con::printf("%s%s", getVariable( "$Con::Prompt" ), string); + } + + if(fileName) + fileName = StringTable->insert(fileName); + + CodeBlock *newCodeBlock = new CodeBlock(); + return newCodeBlock->compileExec(fileName, string, false, fileName ? -1 : 0); +} + +//------------------------------------------------------------------------------ +const char *evaluatef(const char* string, ...) +{ + char buffer[4096]; + va_list args; + va_start(args, string); + dVsprintf(buffer, sizeof(buffer), string, args); + CodeBlock *newCodeBlock = new CodeBlock(); + return newCodeBlock->compileExec(NULL, buffer, false, 0); +} + +const char *execute(S32 argc, const char *argv[]) +{ +#ifdef TORQUE_MULTITHREAD + if(isMainThread()) + { +#endif + Namespace::Entry *ent; + StringTableEntry funcName = StringTable->insert(argv[0]); + ent = Namespace::global()->lookup(funcName); + + if(!ent) + { + warnf(ConsoleLogEntry::Script, "%s: Unknown command.", argv[0]); + + // Clean up arg buffers, if any. + STR.clearFunctionOffset(); + return ""; + } + return ent->execute(argc, argv, &gEvalState); +#ifdef TORQUE_MULTITHREAD + } + else + { + SimConsoleThreadExecCallback cb; + SimConsoleThreadExecEvent *evt = new SimConsoleThreadExecEvent(argc, argv, false, &cb); + Sim::postEvent(Sim::getRootGroup(), evt, Sim::getCurrentTime()); + + return cb.waitForResult(); + } +#endif +} + +//------------------------------------------------------------------------------ +const char *execute(SimObject *object, S32 argc, const char *argv[], bool thisCallOnly) +{ + static char idBuf[16]; + if(argc < 2) + return ""; + + // [neo, 10/05/2007 - #3010] + // Make sure we don't get recursive calls, respect the flag! + // Should we be calling handlesMethod() first? + if( !thisCallOnly ) + { + ICallMethod *com = dynamic_cast(object); + if(com) + com->callMethodArgList(argc, argv, false); + } + + if(object->getNamespace()) + { + dSprintf(idBuf, sizeof(idBuf), "%d", object->getId()); + argv[1] = idBuf; + + StringTableEntry funcName = StringTable->insert(argv[0]); + Namespace::Entry *ent = object->getNamespace()->lookup(funcName); + + if(ent == NULL) + { + //warnf(ConsoleLogEntry::Script, "%s: undefined for object '%s' - id %d", funcName, object->getName(), object->getId()); + + // Clean up arg buffers, if any. + STR.clearFunctionOffset(); + return ""; + } + + // Twiddle %this argument + const char *oldArg1 = argv[1]; + dSprintf(idBuf, sizeof(idBuf), "%d", object->getId()); + argv[1] = idBuf; + + SimObject *save = gEvalState.thisObject; + gEvalState.thisObject = object; + const char *ret = ent->execute(argc, argv, &gEvalState); + gEvalState.thisObject = save; + + // Twiddle it back + argv[1] = oldArg1; + + return ret; + } + warnf(ConsoleLogEntry::Script, "Con::execute - %d has no namespace: %s", object->getId(), argv[0]); + return ""; +} + +#define B( a ) const char* a = NULL +#define A const char* +inline const char*_executef(SimObject *obj, S32 checkArgc, S32 argc, + A a, B(b), B(c), B(d), B(e), B(f), B(g), B(h), B(i), B(j), B(k)) +{ +#undef A +#undef B + const U32 maxArg = 12; + AssertWarn(checkArgc == argc, "Incorrect arg count passed to Con::executef(SimObject*)"); + AssertFatal(argc <= maxArg - 1, "Too many args passed to Con::_executef(SimObject*). Please update the function to handle more."); + const char* argv[maxArg]; + argv[0] = a; + argv[1] = a; + argv[2] = b; + argv[3] = c; + argv[4] = d; + argv[5] = e; + argv[6] = f; + argv[7] = g; + argv[8] = h; + argv[9] = i; + argv[10] = j; + argv[11] = k; + return execute(obj, argc+1, argv); +} + +#define A const char* +#define OBJ SimObject* obj +const char *executef(OBJ, A a) { return _executef(obj, 1, 1, a); } +const char *executef(OBJ, A a, A b) { return _executef(obj, 2, 2, a, b); } +const char *executef(OBJ, A a, A b, A c) { return _executef(obj, 3, 3, a, b, c); } +const char *executef(OBJ, A a, A b, A c, A d) { return _executef(obj, 4, 4, a, b, c, d); } +const char *executef(OBJ, A a, A b, A c, A d, A e) { return _executef(obj, 5, 5, a, b, c, d, e); } +const char *executef(OBJ, A a, A b, A c, A d, A e, A f) { return _executef(obj, 6, 6, a, b, c, d, e, f); } +const char *executef(OBJ, A a, A b, A c, A d, A e, A f, A g) { return _executef(obj, 7, 7, a, b, c, d, e, f, g); } +const char *executef(OBJ, A a, A b, A c, A d, A e, A f, A g, A h) { return _executef(obj, 8, 8, a, b, c, d, e, f, g, h); } +const char *executef(OBJ, A a, A b, A c, A d, A e, A f, A g, A h, A i) { return _executef(obj, 9, 9, a, b, c, d, e, f, g, h, i); } +const char *executef(OBJ, A a, A b, A c, A d, A e, A f, A g, A h, A i, A j) { return _executef(obj,10,10, a, b, c, d, e, f, g, h, i, j); } +const char *executef(OBJ, A a, A b, A c, A d, A e, A f, A g, A h, A i, A j, A k) { return _executef(obj,11,11, a, b, c, d, e, f, g, h, i, j, k); } +#undef A + +//------------------------------------------------------------------------------ +#define B( a ) const char* a = NULL +#define A const char* +inline const char*_executef(S32 checkArgc, S32 argc, A a, B(b), B(c), B(d), B(e), B(f), B(g), B(h), B(i), B(j)) +{ +#undef A +#undef B + const U32 maxArg = 10; + AssertFatal(checkArgc == argc, "Incorrect arg count passed to Con::executef()"); + AssertFatal(argc <= maxArg, "Too many args passed to Con::_executef(). Please update the function to handle more."); + const char* argv[maxArg]; + argv[0] = a; + argv[1] = b; + argv[2] = c; + argv[3] = d; + argv[4] = e; + argv[5] = f; + argv[6] = g; + argv[7] = h; + argv[8] = i; + argv[9] = j; + return execute(argc, argv); +} + +#define A const char* +const char *executef(A a) { return _executef(1, 1, a); } +const char *executef(A a, A b) { return _executef(2, 2, a, b); } +const char *executef(A a, A b, A c) { return _executef(3, 3, a, b, c); } +const char *executef(A a, A b, A c, A d) { return _executef(4, 4, a, b, c, d); } +const char *executef(A a, A b, A c, A d, A e) { return _executef(5, 5, a, b, c, d, e); } +const char *executef(A a, A b, A c, A d, A e, A f) { return _executef(6, 6, a, b, c, d, e, f); } +const char *executef(A a, A b, A c, A d, A e, A f, A g) { return _executef(7, 7, a, b, c, d, e, f, g); } +const char *executef(A a, A b, A c, A d, A e, A f, A g, A h) { return _executef(8, 8, a, b, c, d, e, f, g, h); } +const char *executef(A a, A b, A c, A d, A e, A f, A g, A h, A i) { return _executef(9, 9, a, b, c, d, e, f, g, h, i); } +const char *executef(A a, A b, A c, A d, A e, A f, A g, A h, A i, A j) { return _executef(10,10,a, b, c, d, e, f, g, h, i, j); } +#undef A + + +//------------------------------------------------------------------------------ +bool isFunction(const char *fn) +{ + const char *string = StringTable->lookup(fn); + if(!string) + return false; + else + return Namespace::global()->lookup(string) != NULL; +} + +//------------------------------------------------------------------------------ + +void setLogMode(S32 newMode) +{ + if ((newMode & 0x3) != (consoleLogMode & 0x3)) { + if (newMode && !consoleLogMode) { + // Enabling logging when it was previously disabled. + newLogFile = true; + } + if ((consoleLogMode & 0x3) == 2) { + // Changing away from mode 2, must close logfile. + consoleLogFile.close(); + } + else if ((newMode & 0x3) == 2) { +#ifdef _XBOX + // Xbox is not going to support logging to a file. Use the OutputDebugStr + // log consumer + Platform::debugBreak(); +#endif + // Starting mode 2, must open logfile. + consoleLogFile.open(defLogFileName, Torque::FS::File::Write); + } + consoleLogMode = newMode; + } +} + +Namespace *lookupNamespace(const char *ns) +{ + if(!ns) + return Namespace::global(); + return Namespace::find(StringTable->insert(ns)); +} + +bool linkNamespaces(const char *parent, const char *child) +{ + Namespace *pns = lookupNamespace(parent); + Namespace *cns = lookupNamespace(child); + if(pns && cns) + return cns->classLinkTo(pns); + return false; +} + +bool unlinkNamespaces(const char *parent, const char *child) +{ + Namespace *pns = lookupNamespace(parent); + Namespace *cns = lookupNamespace(child); + + if(pns == cns) + { + Con::warnf("Con::unlinkNamespaces - trying to unlink '%s' from itself, aborting.", parent); + return false; + } + + if(pns && cns) + return cns->unlinkClass(pns); + + return false; +} + +bool classLinkNamespaces(Namespace *parent, Namespace *child) +{ + if(parent && child) + return child->classLinkTo(parent); + return false; +} + +void setData(S32 type, void *dptr, S32 index, S32 argc, const char **argv, const EnumTable *tbl, BitSet32 flag) +{ + ConsoleBaseType *cbt = ConsoleBaseType::getType(type); + AssertFatal(cbt, "Con::setData - could not resolve type ID!"); + cbt->setData((void *) (((const char *)dptr) + index * cbt->getTypeSize()),argc, argv, tbl, flag); +} + +const char *getData(S32 type, void *dptr, S32 index, const EnumTable *tbl, BitSet32 flag) +{ + ConsoleBaseType *cbt = ConsoleBaseType::getType(type); + AssertFatal(cbt, "Con::getData - could not resolve type ID!"); + return cbt->getData((void *) (((const char *)dptr) + index * cbt->getTypeSize()), tbl, flag); +} + +const char *getFormattedData(S32 type, const char *data, const EnumTable *tbl, BitSet32 flag) +{ + ConsoleBaseType *cbt = ConsoleBaseType::getType( type ); + AssertFatal(cbt, "Con::getData - could not resolve type ID!"); + + // Datablock types are just a datablock + // name and don't ever need formatting. + if ( cbt->isDatablock() ) + return data; + + bool currWarn = gWarnUndefinedScriptVariables; + gWarnUndefinedScriptVariables = false; + + const char* globalValue = Con::getVariable(data); + + gWarnUndefinedScriptVariables = currWarn; + + if (dStrlen(globalValue) > 0) + return globalValue; + + void* variable = cbt->getNativeVariable(); + + if (variable) + { + Con::setData(type, variable, 0, 1, &data, tbl, flag); + const char* formattedVal = Con::getData(type, variable, 0, tbl, flag); + + char* returnBuffer = Con::getReturnBuffer(2048); + dSprintf(returnBuffer, 2048, "%s\0", formattedVal ); + + cbt->deleteNativeVariable(variable); + + return returnBuffer; + } + else + return data; +} + +//------------------------------------------------------------------------------ + +bool isCurrentScriptToolScript() +{ + // With a player build we ALWAYS return false +#ifndef TORQUE_TOOLS + return false; +#else + const StringTableEntry cbFullPath = CodeBlock::getCurrentCodeBlockFullPath(); + if(cbFullPath == NULL) + return false; + const StringTableEntry exePath = Platform::getMainDotCsDir(); + + return dStrnicmp(exePath, cbFullPath, dStrlen(exePath)) == 0; +#endif +} + +//------------------------------------------------------------------------------ + +StringTableEntry getModNameFromPath(const char *path) +{ + if(path == NULL || *path == 0) + return NULL; + + char buf[1024]; + buf[0] = 0; + + if(path[0] == '/' || path[1] == ':') + { + // It's an absolute path + const StringTableEntry exePath = Platform::getMainDotCsDir(); + U32 len = dStrlen(exePath); + if(dStrnicmp(exePath, path, len) == 0) + { + const char *ptr = path + len + 1; + const char *slash = dStrchr(ptr, '/'); + if(slash) + { + dStrncpy(buf, ptr, slash - ptr); + buf[slash - ptr] = 0; + } + else + return NULL; + } + else + return NULL; + } + else + { + const char *slash = dStrchr(path, '/'); + if(slash) + { + dStrncpy(buf, path, slash - path); + buf[slash - path] = 0; + } + else + return NULL; + } + + return StringTable->insert(buf); +} + +void postConsoleInput( RawData data ) +{ + // Schedule this to happen at the next time event. + char *argv[2]; + argv[0] = "eval"; + argv[1] = ( char* ) data.data; + Sim::postCurrentEvent(Sim::getRootGroup(), new SimConsoleEvent(2, const_cast(argv), false)); +} + +//------------------------------------------------------------------------------ + +void pushInstantGroup( String name ) +{ + sInstantGroupStack.push_back( gInstantGroup ); + gInstantGroup = name; +} + +void popInstantGroup() +{ + if( sInstantGroupStack.empty() ) + gInstantGroup = String::EmptyString; + else + { + gInstantGroup = sInstantGroupStack.last(); + sInstantGroupStack.pop_back(); + } +} + +} // end of Console namespace + +#endif + +//============================================================================= +// API. +//============================================================================= +// MARK: ---- API ---- + +//----------------------------------------------------------------------------- + +DefineEngineFunction( log, void, ( const char* message ),, + "@brief Logs a message to the console.\n\n" + "@param message The message text.\n" + "@note By default, messages will appear white in the console.\n" + "@ingroup Logging") +{ + Con::printf( "%s", message ); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( logError, void, ( const char* message ),, + "@brief Logs an error message to the console.\n\n" + "@param message The message text.\n" + "@note By default, errors will appear red in the console.\n" + "@ingroup Logging") +{ + Con::errorf( "%s", message ); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( logWarning, void, ( const char* message ),, + "@brief Logs a warning message to the console.\n\n" + "@param message The message text.\n\n" + "@note By default, warnings will appear turquoise in the console.\n" + "@ingroup Logging") +{ + Con::warnf( "%s", message ); +} diff --git a/Engine/source/console/console.h b/Engine/source/console/console.h new file mode 100644 index 000000000..3c5718c48 --- /dev/null +++ b/Engine/source/console/console.h @@ -0,0 +1,1037 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _CONSOLE_H_ +#define _CONSOLE_H_ + +#ifndef _PLATFORM_H_ + #include "platform/platform.h" +#endif +#ifndef _BITSET_H_ + #include "core/bitSet.h" +#endif +#include + +#include "core/util/journal/journaledSignal.h" + +class SimObject; +class Namespace; +struct ConsoleFunctionHeader; + + +class EngineEnumTable; +typedef EngineEnumTable EnumTable; + +template< typename T > S32 TYPEID(); + + +/// @defgroup console_system Console System +/// The Console system is the basis for logging, SimObject, and TorqueScript itself. +/// +/// @{ + +/// Indicates that warnings about undefined script variables should be displayed. +/// +/// @note This is set and controlled by script. +extern bool gWarnUndefinedScriptVariables; + +enum StringTableConstants +{ + StringTagPrefixByte = 0x01 ///< Magic value prefixed to tagged strings. +}; + +/// Represents an entry in the log. +struct ConsoleLogEntry +{ + /// This field indicates the severity of the log entry. + /// + /// Log entries are filtered and displayed differently based on + /// their severity. Errors are highlighted red, while normal entries + /// are displayed as normal text. Often times, the engine will be + /// configured to hide all log entries except warnings or errors, + /// or to perform a special notification when it encounters an error. + enum Level + { + Normal = 0, + Warning, + Error, + NUM_CLASS + } mLevel; + + /// Used to associate a log entry with a module. + /// + /// Log entries can come from different sources; for instance, + /// the scripting engine, or the network code. This allows the + /// logging system to be aware of where different log entries + /// originated from. + enum Type + { + General = 0, + Assert, + Script, + GUI, + Network, + GGConnect, + NUM_TYPE + } mType; + + /// Indicates the actual log entry. + /// + /// This contains a description of the event being logged. + /// For instance, "unable to access file", or "player connected + /// successfully", or nearly anything else you might imagine. + /// + /// Typically, the description should contain a concise, descriptive + /// string describing whatever is being logged. Whenever possible, + /// include useful details like the name of the file being accessed, + /// or the id of the player or GuiControl, so that if a log needs + /// to be used to locate a bug, it can be done as painlessly as + /// possible. + const char *mString; +}; + +typedef const char *StringTableEntry; + +/// @defgroup console_callbacks Scripting Engine Callbacks +/// +/// The scripting engine makes heavy use of callbacks to represent +/// function exposed to the scripting language. StringCallback, +/// IntCallback, FloatCallback, VoidCallback, and BoolCallback all +/// represent exposed script functions returning different types. +/// +/// ConsumerCallback is used with the function Con::addConsumer; functions +/// registered with Con::addConsumer are called whenever something is outputted +/// to the console. For instance, the TelnetConsole registers itself with the +/// console so it can echo the console over the network. +/// +/// @note Callbacks to the scripting language - for instance, onExit(), which is +/// a script function called when the engine is shutting down - are handled +/// using Con::executef() and kin. +/// @{ + +/// +typedef const char * (*StringCallback)(SimObject *obj, S32 argc, const char *argv[]); +typedef S32 (*IntCallback)(SimObject *obj, S32 argc, const char *argv[]); +typedef F32 (*FloatCallback)(SimObject *obj, S32 argc, const char *argv[]); +typedef void (*VoidCallback)(SimObject *obj, S32 argc, const char *argv[]); // We have it return a value so things don't break.. +typedef bool (*BoolCallback)(SimObject *obj, S32 argc, const char *argv[]); + +typedef void (*ConsumerCallback)(U32 level, const char *consoleLine); +/// @} + +/// @defgroup console_types Scripting Engine Type Functions +/// +/// @see Con::registerType +/// @{ +typedef const char* (*GetDataFunction)(void *dptr, EnumTable *tbl, BitSet32 flag); +typedef void (*SetDataFunction)(void *dptr, S32 argc, const char **argv, EnumTable *tbl, BitSet32 flag); +/// @} + +/// This namespace contains the core of the console functionality. +/// +/// @section con_intro Introduction +/// +/// The console is a key part of Torque's architecture. It allows direct run-time control +/// of many aspects of the engine. +/// +/// @nosubgrouping +namespace Con +{ + /// Various configuration constants. + enum Constants + { + /// This is the version number associated with DSO files. + /// + /// If you make any changes to the way the scripting language works + /// (such as DSO format changes, adding/removing op-codes) that would + /// break compatibility, then you should increment this. + /// + /// If you make a really major change, increment it to the next multiple + /// of ten. + /// + /// 12/29/04 - BJG - 33->34 Removed some opcodes, part of namespace upgrade. + /// 12/30/04 - BJG - 34->35 Reordered some things, further general shuffling. + /// 11/03/05 - BJG - 35->36 Integrated new debugger code. + // 09/08/06 - THB - 36->37 New opcode for internal names + // 09/15/06 - THB - 37->38 Added unit conversions + // 11/23/06 - THB - 38->39 Added recursive internal name operator + // 02/15/07 - THB - 39->40 Bumping to 40 for TGB since the console has been + // majorly hacked without the version number being bumped + // 02/16/07 - THB - 40->41 newmsg operator + // 06/15/07 - THB - 41->42 script types + /// 07/31/07 - THB - 42->43 Patch from Andreas Kirsch: Added opcode to support nested new declarations. + /// 09/12/07 - CAF - 43->44 remove newmsg operator + /// 09/27/07 - RDB - 44->45 Patch from Andreas Kirsch: Added opcode to support correct void return + /// 01/13/09 - TMS - 45->46 Added script assert + DSOVersion = 46, + + MaxLineLength = 512, ///< Maximum length of a line of console input. + MaxDataTypes = 256 ///< Maximum number of registered data types. + }; + + /// @name Control Functions + /// + /// The console must be initialized and shutdown appropriately during the + /// lifetime of the app. These functions are used to manage this behavior. + /// + /// @note Torque deals with this aspect of console management, so you don't need + /// to call these functions in normal usage of the engine. + /// @{ + + /// Initializes the console. + /// + /// This performs the following steps: + /// - Calls Namespace::init() to initialize the scripting namespace hierarchy. + /// - Calls ConsoleConstructor::setup() to initialize globally defined console + /// methods and functions. + /// - Registers some basic global script variables. + /// - Calls AbstractClassRep::init() to initialize Torque's class database. + /// - Registers some basic global script functions that couldn't usefully + /// be defined anywhere else. + void init(); + + /// Shuts down the console. + /// + /// This performs the following steps: + /// - Closes the console log file. + /// - Calls Namespace::shutdown() to shut down the scripting namespace hierarchy. + void shutdown(); + + /// Is the console active at this time? + bool isActive(); + + /// @} + + /// @name Console Consumers + /// + /// The console distributes its output through Torque by using + /// consumers. Every time a new line is printed to the console, + /// all the ConsumerCallbacks registered using addConsumer are + /// called, in order. + /// + /// @note The GuiConsole control, which provides the on-screen + /// in-game console, uses a different technique to render + /// the console. It calls getLockLog() to lock the Vector + /// of on-screen console entries, then it renders them as + /// needed. While the Vector is locked, the console will + /// not change the Vector. When the GuiConsole control is + /// done with the console entries, it calls unlockLog() + /// to tell the console that it is again safe to modify + /// the Vector. + /// + /// @see TelnetConsole + /// @see TelnetDebugger + /// @see WinConsole + /// @see MacCarbConsole + /// @see StdConsole + /// @see ConsoleLogger + /// + /// @{ + + /// + void addConsumer(ConsumerCallback cb); + void removeConsumer(ConsumerCallback cb); + + typedef JournaledSignal ConsoleInputEvent; + + /// Called from the native consoles to provide lines of console input + /// to process. This will schedule it for execution ASAP. + extern ConsoleInputEvent smConsoleInput; + + /// @} + + /// @name Miscellaneous + /// @{ + + /// Remove color marking information from a string. + /// + /// @note It does this in-place, so be careful! It may + /// potentially blast data if you're not careful. + /// When in doubt, make a copy of the string first. + void stripColorChars(char* line); + + /// Convert from a relative script path to an absolute script path. + /// + /// This is used in (among other places) the exec() script function, which + /// takes a parameter indicating a script file and executes it. Script paths + /// can be one of: + /// - Absolute: fps/foo/bar.cs Paths of this sort are passed + /// through. + /// - Mod-relative: ~/foo/bar.cs Paths of this sort have their + /// replaced with the name of the current mod. + /// - File-relative: ./baz/blip.cs Paths of this sort are + /// calculated relative to the path of the current scripting file. + /// + /// @note This function determines paths relative to the currently executing + /// CodeBlock. Calling it outside of script execution will result in + /// it directly copying src to filename, since it won't know to what the + /// path is relative! + /// + /// @param filename Pointer to string buffer to fill with absolute path. + /// @param size Size of buffer pointed to by filename. + /// @param src Original, possibly relative script path. + bool expandScriptFilename(char *filename, U32 size, const char *src); + bool expandGameScriptFilename(char *filename, U32 size, const char *src); + bool expandToolScriptFilename(char *filename, U32 size, const char *src); + bool collapseScriptFilename(char *filename, U32 size, const char *src); + + bool isCurrentScriptToolScript(); + + StringTableEntry getModNameFromPath(const char *path); + + /// Returns true if fn is a global scripting function. + /// + /// This looks in the global namespace. It also checks to see if fn + /// is in the StringTable; if not, it returns false. + bool isFunction(const char *fn); + + /// This is the basis for tab completion in the console. + /// + /// @note This is an internally used function. You probably don't + /// care much about how this works. + /// + /// This function does some basic parsing to try to ascertain the namespace in which + /// we are attempting to do tab completion, then bumps control off to the appropriate + /// tabComplete function, either in SimObject or Namespace. + /// + /// @param inputBuffer Pointer to buffer containing starting data, or last result. + /// @param cursorPos Location of cursor in this buffer. This is used to indicate + /// what part of the string should be kept and what part should + /// be advanced to the next match if any. + /// @param maxResultLength Maximum amount of result data to put into inputBuffer. This + /// is capped by MaxCompletionBufferSize. + /// @param forwardTab Should we go forward to next match or backwards to previous + /// match? True indicates forward. + U32 tabComplete(char* inputBuffer, U32 cursorPos, U32 maxResultLength, bool forwardTab); + + /// @} + + + /// @name Variable Management + /// @{ + + /// The delegate signature for the variable assignment notifications. + /// + /// @see addVariableNotify, removeVariableNotify + typedef Delegate NotifyDelegate; + + /// Add a console variable that references the value of a variable in C++ code. + /// + /// If a value is assigned to the console variable the C++ variable is updated, + /// and vice-versa. + /// + /// @param name Global console variable name to create. + /// @param type The type of the C++ variable; see the ConsoleDynamicTypes enum for a complete list. + /// @param pointer Pointer to the variable. + /// @param usage Documentation string. + /// + /// @see ConsoleDynamicTypes + void addVariable( const char *name, + S32 type, + void *pointer, + const char* usage = NULL ); + + /// Add a console constant that references the value of a constant in C++ code. + /// + /// @param name Global console constant name to create. + /// @param type The type of the C++ constant; see the ConsoleDynamicTypes enum for a complete list. + /// @param pointer Pointer to the constant. + /// @param usage Documentation string. + /// + /// @see ConsoleDynamicTypes + void addConstant( const char *name, + S32 type, + const void *pointer, + const char* usage = NULL ); + + /// Remove a console variable. + /// + /// @param name Global console variable name to remove + /// @return true if variable existed before removal. + bool removeVariable(const char *name); + + /// Add a callback for notification when a variable + /// is assigned a new value. + /// + /// @param name An existing global console variable name. + /// @param callback The notification delegate function. + /// + void addVariableNotify( const char *name, const NotifyDelegate &callback ); + + /// Remove an existing variable assignment notification callback. + /// + /// @param name An existing global console variable name. + /// @param callback The notification delegate function. + /// + void removeVariableNotify( const char *name, const NotifyDelegate &callback ); + + /// Assign a string value to a locally scoped console variable + /// + /// @note The context of the variable is determined by gEvalState; that is, + /// by the currently executing code. + /// + /// @param name Local console variable name to set + /// @param value String value to assign to name + void setLocalVariable(const char *name, const char *value); + + /// Retrieve the string value to a locally scoped console variable + /// + /// @note The context of the variable is determined by gEvalState; that is, + /// by the currently executing code. + /// + /// @param name Local console variable name to get + const char* getLocalVariable(const char* name); + + /// @} + + /// @name Global Variable Accessors + /// @{ + /// Assign a string value to a global console variable + /// @param name Global console variable name to set + /// @param value String value to assign to this variable. + void setVariable(const char *name, const char *value); + + /// Retrieve the string value of a global console variable + /// @param name Global Console variable name to query + /// @return The string value of the variable or "" if the variable does not exist. + const char* getVariable(const char* name); + + /// Same as setVariable(), but for bools. + void setBoolVariable (const char* name,bool var); + + /// Same as getVariable(), but for bools. + /// + /// @param name Name of the variable. + /// @param def Default value to supply if no matching variable is found. + bool getBoolVariable (const char* name,bool def = false); + + /// Same as setVariable(), but for ints. + void setIntVariable (const char* name,S32 var); + + /// Same as getVariable(), but for ints. + /// + /// @param name Name of the variable. + /// @param def Default value to supply if no matching variable is found. + S32 getIntVariable (const char* name,S32 def = 0); + + /// Same as setVariable(), but for floats. + void setFloatVariable(const char* name,F32 var); + + /// Same as getVariable(), but for floats. + /// + /// @param name Name of the variable. + /// @param def Default value to supply if no matching variable is found. + F32 getFloatVariable(const char* name,F32 def = .0f); + + /// @} + + /// @name Global Function Registration + /// @{ + + /// Register a C++ function with the console making it a global function callable from the scripting engine. + /// + /// @param name Name of the new function. + /// @param cb Pointer to the function implementing the scripting call; a console callback function returning a specific type value. + /// @param usage Documentation for this function. @ref console_autodoc + /// @param minArgs Minimum number of arguments this function accepts + /// @param maxArgs Maximum number of arguments this function accepts + /// @param toolOnly Wether this is a TORQUE_TOOLS only function. + /// @param header The extended function header. + void addCommand( const char* name, StringCallback cb, const char* usage, S32 minArgs, S32 maxArgs, bool toolOnly = false, ConsoleFunctionHeader* header = NULL ); + + void addCommand( const char* name, IntCallback cb, const char* usage, S32 minArgs, S32 maxArgs, bool toolOnly = false, ConsoleFunctionHeader* header = NULL ); ///< @copydoc addCommand( const char *, StringCallback, const char *, S32, S32, bool, ConsoleFunctionHeader* ) + void addCommand( const char* name, FloatCallback cb, const char* usage, S32 minArgs, S32 maxArgs, bool toolOnly = false, ConsoleFunctionHeader* header = NULL ); ///< @copydoc addCommand( const char *, StringCallback, const char *, S32, S32, bool, ConsoleFunctionHeader* ) + void addCommand( const char* name, VoidCallback cb, const char* usage, S32 minArgs, S32 maxArgs, bool toolOnly = false, ConsoleFunctionHeader* header = NULL ); ///< @copydoc addCommand( const char *, StringCallback, const char *, S32, S32, bool, ConsoleFunctionHeader* ) + void addCommand( const char* name, BoolCallback cb, const char* usage, S32 minArgs, S32 maxArgs, bool toolOnly = false, ConsoleFunctionHeader* header = NULL ); ///< @copydoc addCommand( const char *, StringCallback, const char *, S32, S32, bool, ConsoleFunctionHeader* ) + + /// @} + + /// @name Namespace Function Registration + /// @{ + + /// Register a C++ function with the console making it callable + /// as a method of the given namespace from the scripting engine. + /// + /// @param nameSpace Name of the namespace to associate the new function with; this is usually the name of a class. + /// @param name Name of the new function. + /// @param cb Pointer to the function implementing the scripting call; a console callback function returning a specific type value. + /// @param usage Documentation for this function. @ref console_autodoc + /// @param minArgs Minimum number of arguments this function accepts + /// @param maxArgs Maximum number of arguments this function accepts + /// @param toolOnly Wether this is a TORQUE_TOOLS only function. + /// @param header The extended function header. + void addCommand(const char *nameSpace, const char *name,StringCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool toolOnly = false, ConsoleFunctionHeader* header = NULL ); + + void addCommand(const char *nameSpace, const char *name,IntCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool toolOnly = false, ConsoleFunctionHeader* header = NULL ); ///< @copydoc addCommand( const char*, const char *, StringCallback, const char *, S32, S32, bool, ConsoleFunctionHeader* ) + void addCommand(const char *nameSpace, const char *name,FloatCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool toolOnly = false, ConsoleFunctionHeader* header = NULL ); ///< @copydoc addCommand( const char*, const char *, StringCallback, const char *, S32, S32, bool, ConsoleFunctionHeader* ) + void addCommand(const char *nameSpace, const char *name,VoidCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool toolOnly = false, ConsoleFunctionHeader* header = NULL ); ///< @copydoc addCommand( const char*, const char *, StringCallback, const char *, S32, S32, bool, ConsoleFunctionHeader* ) + void addCommand(const char *nameSpace, const char *name,BoolCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool toolOnly = false, ConsoleFunctionHeader* header = NULL ); ///< @copydoc addCommand( const char*, const char *, StringCallback, const char *, S32, S32, bool, ConsoleFunctionHeader* ) + + /// @} + + /// @name Special Purpose Registration + /// + /// These are special-purpose functions that exist to allow commands to be grouped, so + /// that when we generate console docs, they can be more meaningfully presented. + /// + /// @ref console_autodoc "Click here for more information about console docs and grouping." + /// + /// @{ + + void markCommandGroup (const char * nsName, const char *name, const char* usage=NULL); + void beginCommandGroup(const char * nsName, const char *name, const char* usage); + void endCommandGroup (const char * nsName, const char *name); + + void noteScriptCallback( const char *className, const char *funcName, const char *usage, ConsoleFunctionHeader* header = NULL ); + + /// @} + + /// @name Console Output + /// + /// These functions process the formatted string and pass it to all the ConsumerCallbacks that are + /// currently registered. The console log file and the console window callbacks are installed by default. + /// + /// @see addConsumer() + /// @see removeConsumer() + /// @{ + + /// @param _format A stdlib printf style formatted out put string + /// @param ... Variables to be written + void printf(const char *_format, ...); + + /// @note The console window colors warning text as LIGHT GRAY. + /// @param _format A stdlib printf style formatted out put string + /// @param ... Variables to be written + void warnf(const char *_format, ...); + + /// @note The console window colors warning text as RED. + /// @param _format A stdlib printf style formatted out put string + /// @param ... Variables to be written + void errorf(const char *_format, ...); + + /// @note The console window colors warning text as LIGHT GRAY. + /// @param type Allows you to associate the warning message with an internal module. + /// @param _format A stdlib printf style formatted out put string + /// @param ... Variables to be written + /// @see Con::warnf() + void warnf(ConsoleLogEntry::Type type, const char *_format, ...); + + /// @note The console window colors warning text as RED. + /// @param type Allows you to associate the warning message with an internal module. + /// @param _format A stdlib printf style formatted out put string + /// @param ... Variables to be written + /// @see Con::errorf() + void errorf(ConsoleLogEntry::Type type, const char *_format, ...); + + /// @} + + /// Returns true when called from the main thread, false otherwise + bool isMainThread(); + + + /// @name Console Execution + /// + /// These are functions relating to the execution of script code. + /// + /// @{ + + /// Call a script function from C/C++ code. + /// + /// @param argc Number of elements in the argv parameter + /// @param argv A character string array containing the name of the function + /// to call followed by the arguments to that function. + /// @code + /// // Call a Torque script function called mAbs, having one parameter. + /// char* argv[] = {"abs", "-9"}; + /// char* result = execute(2, argv); + /// @endcode + const char *execute(S32 argc, const char* argv[]); + + /// @see execute(S32 argc, const char* argv[]) +#define ARG const char* + const char *executef( ARG); + const char *executef( ARG, ARG); + const char *executef( ARG, ARG, ARG); + const char *executef( ARG, ARG, ARG, ARG); + const char *executef( ARG, ARG, ARG, ARG, ARG); + const char *executef( ARG, ARG, ARG, ARG, ARG, ARG); + const char *executef( ARG, ARG, ARG, ARG, ARG, ARG, ARG); + const char *executef( ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG); + const char *executef( ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG); + const char *executef( ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG); +#undef ARG + + + /// Call a Torque Script member function of a SimObject from C/C++ code. + /// @param object Object on which to execute the method call. + /// @param argc Number of elements in the argv parameter (must be >2, see argv) + /// @param argv A character string array containing the name of the member function + /// to call followed by an empty parameter (gets filled with object ID) + /// followed by arguments to that function. + /// @code + /// // Call the method setMode() on an object, passing it one parameter. + /// + /// char* argv[] = {"setMode", "", "2"}; + /// char* result = execute(mysimobject, 3, argv); + /// @endcode + const char *execute(SimObject *object, S32 argc, const char *argv[], bool thisCallOnly = false); + + /// @see execute(SimObject *, S32 argc, const char *argv[]) +#define ARG const char* + const char *executef(SimObject *, ARG); + const char *executef(SimObject *, ARG, ARG); + const char *executef(SimObject *, ARG, ARG, ARG); + const char *executef(SimObject *, ARG, ARG, ARG, ARG); + const char *executef(SimObject *, ARG, ARG, ARG, ARG, ARG); + const char *executef(SimObject *, ARG, ARG, ARG, ARG, ARG, ARG); + const char *executef(SimObject *, ARG, ARG, ARG, ARG, ARG, ARG, ARG); + const char *executef(SimObject *, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG); + const char *executef(SimObject *, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG); + const char *executef(SimObject *, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG); + const char *executef(SimObject *, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG); +#undef ARG + + /// Evaluate an arbitrary chunk of code. + /// + /// @param string Buffer containing code to execute. + /// @param echo Should we echo the string to the console? + /// @param fileName Indicate what file this code is coming from; used in error reporting and such. + const char *evaluate(const char* string, bool echo = false, const char *fileName = NULL); + + /// Evaluate an arbitrary line of script. + /// + /// This wraps dVsprintf(), so you can substitute parameters into the code being executed. + const char *evaluatef(const char* string, ...); + + /// @} + + /// @name Console Function Implementation Helpers + /// + /// The functions Con::getIntArg, Con::getFloatArg and Con::getArgBuffer(size) are used to + /// allocate on the console stack string variables that will be passed into the next console + // function called. This allows the console to avoid copying some data. + /// + /// getReturnBuffer lets you allocate stack space to return data in. + /// @{ + + /// + char* getReturnBuffer(U32 bufferSize); + char* getReturnBuffer(const char *stringToCopy); + char* getReturnBuffer( const String& str ); + char* getReturnBuffer( const StringBuilder& str ); + + char* getArgBuffer(U32 bufferSize); + char* getFloatArg(F64 arg); + char* getIntArg (S32 arg); + char* getStringArg( const char *arg ); + char* getStringArg( const String& arg ); + /// @} + + /// @name Namespaces + /// @{ + + Namespace *lookupNamespace(const char *nsName); + bool linkNamespaces(const char *parentName, const char *childName); + bool unlinkNamespaces(const char *parentName, const char *childName); + + /// @note This should only be called from consoleObject.h + bool classLinkNamespaces(Namespace *parent, Namespace *child); + + const char *getNamespaceList(Namespace *ns); + /// @} + + /// @name Logging + /// @{ + + void getLockLog(ConsoleLogEntry * &log, U32 &size); + void unlockLog(void); + void setLogMode(S32 mode); + + /// @} + + /// @name Instant Group + /// @{ + + void pushInstantGroup( String name = String() ); + void popInstantGroup(); + + /// @} + + /// @name Dynamic Type System + /// @{ + + /// +/* void registerType( const char *typeName, S32 type, S32 size, GetDataFunction gdf, SetDataFunction sdf, bool isDatablockType = false ); + void registerType( const char* typeName, S32 type, S32 size, bool isDatablockType = false ); + void registerTypeGet( S32 type, GetDataFunction gdf ); + void registerTypeSet( S32 type, SetDataFunction sdf ); + + const char *getTypeName(S32 type); + bool isDatablockType( S32 type ); */ + + void setData(S32 type, void *dptr, S32 index, S32 argc, const char **argv, const EnumTable *tbl = NULL, BitSet32 flag = 0); + const char *getData(S32 type, void *dptr, S32 index, const EnumTable *tbl = NULL, BitSet32 flag = 0); + const char *getFormattedData(S32 type, const char *data, const EnumTable *tbl = NULL, BitSet32 flag = 0); + /// @} +}; + +extern void expandEscape(char *dest, const char *src); +extern bool collapseEscape(char *buf); +extern S32 HashPointer(StringTableEntry ptr); + + +/// Extended information about a console function. +struct ConsoleFunctionHeader +{ + /// Return type string. + const char* mReturnString; + + /// List of arguments taken by the function. Used for documentation. + const char* mArgString; + + /// List of default argument values. Used for documentation. + const char* mDefaultArgString; + + /// Whether this is a static method in a class. + bool mIsStatic; + + ConsoleFunctionHeader( + const char* returnString, + const char* argString, + const char* defaultArgString, + bool isStatic = false ) + : mReturnString( returnString ), + mArgString( argString ), + mDefaultArgString( defaultArgString ), + mIsStatic( isStatic ) {} +}; + + +/// This is the backend for the ConsoleMethod()/ConsoleFunction() macros. +/// +/// See the group ConsoleConstructor Innards for specifics on how this works. +/// +/// @see @ref console_autodoc +/// @nosubgrouping +class ConsoleConstructor +{ +public: + /// @name Entry Type Fields + /// + /// One of these is set based on the type of entry we want + /// inserted in the console. + /// + /// @ref console_autodoc + /// @{ + + StringCallback sc; ///< A function/method that returns a string. + IntCallback ic; ///< A function/method that returns an int. + FloatCallback fc; ///< A function/method that returns a float. + VoidCallback vc; ///< A function/method that returns nothing. + BoolCallback bc; ///< A function/method that returns a bool. + bool group; ///< Indicates that this is a group marker. + bool ns; ///< Indicates that this is a namespace marker. + /// @deprecated Unused. + bool callback; ///< Is this a callback into script? + + /// @} + + /// Minimum number of arguments expected by the function. + S32 mina; + + /// Maximum number of arguments accepted by the funtion. Zero for varargs. + S32 maxa; + + /// Name of the function/method. + const char* funcName; + + /// Name of the class namespace to which to add the method. + const char* className; + + /// Usage string for documentation. + const char* usage; + + /// Whether this is a TORQUE_TOOLS only function. + bool toolOnly; + + /// The extended function header. + ConsoleFunctionHeader* header; + + /// @name ConsoleConstructor Innards + /// + /// The ConsoleConstructor class is used as the backend for the ConsoleFunction() and + /// ConsoleMethod() macros. The way it works takes advantage of several properties of + /// C++. + /// + /// The ConsoleFunction()/ConsoleMethod() macros wrap the declaration of a ConsoleConstructor. + /// + /// @code + /// // The definition of a ConsoleFunction using the macro + /// ConsoleFunction(ExpandFilename, const char*, 2, 2, "(string filename)") + /// { + /// argc; + /// char* ret = Con::getReturnBuffer( 1024 ); + /// Con::expandScriptFilename(ret, 1024, argv[1]); + /// return ret; + /// } + /// + /// // Resulting code + /// static const char* cExpandFilename(SimObject *, S32, const char **argv); + /// static ConsoleConstructor + /// gExpandFilenameobj(NULL,"ExpandFilename", cExpandFilename, + /// "(string filename)", 2, 2); + /// static const char* cExpandFilename(SimObject *, S32 argc, const char **argv) + /// { + /// argc; + /// char* ret = Con::getReturnBuffer( 1024 ); + /// Con::expandScriptFilename(ret, 1024, argv[1]); + /// return ret; + /// } + /// + /// // A similar thing happens when you do a ConsoleMethod. + /// @endcode + /// + /// As you can see, several global items are defined when you use the ConsoleFunction method. + /// The macro constructs the name of these items from the parameters you passed it. Your + /// implementation of the console function is is placed in a function with a name based on + /// the actual name of the console funnction. In addition, a ConsoleConstructor is declared. + /// + /// Because it is defined as a global, the constructor for the ConsoleConstructor is called + /// before execution of main() is started. The constructor is called once for each global + /// ConsoleConstructor variable, in the order in which they were defined (this property only holds true + /// within file scope). + /// + /// We have ConsoleConstructor create a linked list at constructor time, by storing a static + /// pointer to the head of the list, and keeping a pointer to the next item in each instance + /// of ConsoleConstructor. init() is a helper function in this process, automatically filling + /// in commonly used fields and updating first and next as needed. In this way, a list of + /// items to add to the console is assemble in memory, ready for use, before we start + /// execution of the program proper. + /// + /// In Con::init(), ConsoleConstructor::setup() is called to process this prepared list. Each + /// item in the list is iterated over, and the appropriate Con namespace functions (usually + /// Con::addCommand) are invoked to register the ConsoleFunctions and ConsoleMethods in + /// the appropriate namespaces. + /// + /// @see Namespace + /// @see Con + /// @{ + + /// + ConsoleConstructor *next; + static ConsoleConstructor *first; + + void init( const char* cName, const char* fName, const char *usg, S32 minArgs, S32 maxArgs, bool toolOnly = false, ConsoleFunctionHeader* header = NULL ); + + static void setup(); + + /// Validate there are no duplicate entries for this item. + void validate(); + + /// @} + + /// @name Basic Console Constructors + /// @{ + + ConsoleConstructor( const char* className, const char* funcName, StringCallback sfunc, const char* usage, S32 minArgs, S32 maxArgs, bool toolOnly = false, ConsoleFunctionHeader* header = NULL ); + ConsoleConstructor( const char* className, const char* funcName, IntCallback ifunc, const char* usage, S32 minArgs, S32 maxArgs, bool toolOnly = false, ConsoleFunctionHeader* header = NULL ); + ConsoleConstructor( const char* className, const char* funcName, FloatCallback ffunc, const char* usage, S32 minArgs, S32 maxArgs, bool toolOnly = false, ConsoleFunctionHeader* header = NULL ); + ConsoleConstructor( const char* className, const char* funcName, VoidCallback vfunc, const char* usage, S32 minArgs, S32 maxArgs, bool toolOnly = false, ConsoleFunctionHeader* header = NULL ); + ConsoleConstructor( const char* className, const char* funcName, BoolCallback bfunc, const char* usage, S32 minArgs, S32 maxArgs, bool toolOnly = false, ConsoleFunctionHeader* header = NULL ); + + /// @} + + /// @name Magic Console Constructors + /// + /// These perform various pieces of "magic" related to consoleDoc functionality. + /// @ref console_autodoc + /// @{ + + /// Indicates a group marker. (A doxygen illusion) + /// + /// @see Con::markCommandGroup + /// @ref console_autodoc + ConsoleConstructor( const char *className, const char *groupName, const char* usage ); + + /// Indicates a callback declared with the DECLARE_SCRIPT_CALLBACK macro and friends. + ConsoleConstructor( const char *className, const char *callbackName, const char *usage, ConsoleFunctionHeader* header ); + + /// @} +}; + + +/// An arbitrary fragment of auto-doc text for the script reference. +struct ConsoleDocFragment +{ + /// The class in which to put the fragment. If NULL, the fragment + /// will be placed globally. + const char* mClass; + + /// The definition to output for this fragment. NULL for fragments + /// not associated with a definition. + const char* mDefinition; + + /// The documentation text. + const char* mText; + + /// Next fragment in the global link chain. + ConsoleDocFragment* mNext; + + /// First fragment in the global link chain. + static ConsoleDocFragment* smFirst; + + ConsoleDocFragment( const char* text, const char* inClass = NULL, const char* definition = NULL ) + : mText( text ), + mClass( inClass ), + mDefinition( definition ), + mNext( smFirst ) + { + smFirst = this; + } +}; + + +/// @name Global Console Definition Macros +/// +/// @note If TORQUE_DEBUG is defined, then we gather documentation information, and +/// do some extra sanity checks. +/// +/// @see ConsoleConstructor +/// @ref console_autodoc +/// @{ + + +/// Define a C++ method that calls back to script on an object. +/// +/// @see consoleCallback.h +#define DECLARE_CALLBACK( returnType, name, args ) \ + virtual returnType name ## _callback args + +// O hackery of hackeries +#define conmethod_return_const return (const +#define conmethod_return_S32 return (S32 +#define conmethod_return_F32 return (F32 +#define conmethod_nullify(val) +#define conmethod_return_void conmethod_nullify(void +#define conmethod_return_bool return (bool + +#if !defined(TORQUE_SHIPPING) + +// Console function macros +# define ConsoleFunctionGroupBegin(groupName, usage) \ + static ConsoleConstructor cfg_ConsoleFunctionGroup_##groupName##_GroupBegin(NULL,#groupName,usage) + +# define ConsoleFunction(name,returnType,minArgs,maxArgs,usage1) \ + returnType cf_##name(SimObject *, S32, const char **argv); \ + ConsoleConstructor cc_##name##_obj(NULL,#name,cf_##name,usage1,minArgs,maxArgs); \ + returnType cf_##name(SimObject *, S32 argc, const char **argv) + +# define ConsoleToolFunction(name,returnType,minArgs,maxArgs,usage1) \ + returnType ctf_##name(SimObject *, S32, const char **argv); \ + ConsoleConstructor cc_##name##_obj(NULL,#name,ctf_##name,usage1,minArgs,maxArgs, true); \ + returnType ctf_##name(SimObject *, S32 argc, const char **argv) + +# define ConsoleFunctionGroupEnd(groupName) \ + static ConsoleConstructor cfg_##groupName##_GroupEnd(NULL,#groupName,NULL) + +// Console method macros +# define ConsoleNamespace(className, usage) \ + ConsoleConstructor cc_##className##_Namespace(#className, usage) + +# define ConsoleMethodGroupBegin(className, groupName, usage) \ + static ConsoleConstructor cc_##className##_##groupName##_GroupBegin(#className,#groupName,usage) + +# define ConsoleMethod(className,name,returnType,minArgs,maxArgs,usage1) \ + inline returnType cm_##className##_##name(className *, S32, const char **argv); \ + returnType cm_##className##_##name##_caster(SimObject *object, S32 argc, const char **argv) { \ + AssertFatal( dynamic_cast( object ), "Object passed to " #name " is not a " #className "!" ); \ + conmethod_return_##returnType ) cm_##className##_##name(static_cast(object),argc,argv); \ + }; \ + ConsoleConstructor cc_##className##_##name##_obj(#className,#name,cm_##className##_##name##_caster,usage1,minArgs,maxArgs); \ + inline returnType cm_##className##_##name(className *object, S32 argc, const char **argv) + +# define ConsoleStaticMethod(className,name,returnType,minArgs,maxArgs,usage1) \ + inline returnType cm_##className##_##name(S32, const char **); \ + returnType cm_##className##_##name##_caster(SimObject *object, S32 argc, const char **argv) { \ + conmethod_return_##returnType ) cm_##className##_##name(argc,argv); \ + }; \ + ConsoleConstructor \ + cc_##className##_##name##_obj(#className,#name,cm_##className##_##name##_caster,usage1,minArgs,maxArgs); \ + inline returnType cm_##className##_##name(S32 argc, const char **argv) + +# define ConsoleMethodGroupEnd(className, groupName) \ + static ConsoleConstructor cc_##className##_##groupName##_GroupEnd(#className,#groupName,NULL) + +/// Add a fragment of auto-doc text to the console API reference. +/// @note There can only be one ConsoleDoc per source file. +# define ConsoleDoc( text ) \ + namespace { \ + ConsoleDocFragment _sDocFragment( text ); \ + } + +#else + +// These do nothing if we don't want doc information. +# define ConsoleFunctionGroupBegin(groupName, usage) +# define ConsoleFunctionGroupEnd(groupName) +# define ConsoleNamespace(className, usage) +# define ConsoleMethodGroupBegin(className, groupName, usage) +# define ConsoleMethodGroupEnd(className, groupName) + +// These are identical to what's above, we just want to null out the usage strings. +# define ConsoleFunction(name,returnType,minArgs,maxArgs,usage1) \ + static returnType c##name(SimObject *, S32, const char **); \ + static ConsoleConstructor g##name##obj(NULL,#name,c##name,"",minArgs,maxArgs);\ + static returnType c##name(SimObject *, S32 argc, const char **argv) + +# define ConsoleToolFunction(name,returnType,minArgs,maxArgs,usage1) \ + static returnType c##name(SimObject *, S32, const char **); \ + static ConsoleConstructor g##name##obj(NULL,#name,c##name,"",minArgs,maxArgs, true);\ + static returnType c##name(SimObject *, S32 argc, const char **argv) + +# define ConsoleMethod(className,name,returnType,minArgs,maxArgs,usage1) \ + static inline returnType c##className##name(className *, S32, const char **argv); \ + static returnType c##className##name##caster(SimObject *object, S32 argc, const char **argv) { \ + conmethod_return_##returnType ) c##className##name(static_cast(object),argc,argv); \ + }; \ + static ConsoleConstructor \ + className##name##obj(#className,#name,c##className##name##caster,"",minArgs,maxArgs); \ + static inline returnType c##className##name(className *object, S32 argc, const char **argv) + +# define ConsoleStaticMethod(className,name,returnType,minArgs,maxArgs,usage1) \ + static inline returnType c##className##name(S32, const char **); \ + static returnType c##className##name##caster(SimObject *object, S32 argc, const char **argv) { \ + conmethod_return_##returnType ) c##className##name(argc,argv); \ + }; \ + static ConsoleConstructor \ + className##name##obj(#className,#name,c##className##name##caster,"",minArgs,maxArgs); \ + static inline returnType c##className##name(S32 argc, const char **argv) + +#define ConsoleDoc( text ) + +#endif + +/// @} + +/// @} + +#endif // _CONSOLE_H_ diff --git a/Engine/source/console/consoleDoc.cpp b/Engine/source/console/consoleDoc.cpp new file mode 100644 index 000000000..1170b256d --- /dev/null +++ b/Engine/source/console/consoleDoc.cpp @@ -0,0 +1,540 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/console.h" + +#include "console/ast.h" +#include "core/tAlgorithm.h" + +#include "core/strings/findMatch.h" +#include "console/consoleInternal.h" +#include "console/consoleObject.h" +#include "core/stream/fileStream.h" +#include "console/compiler.h" +#include "core/frameAllocator.h" +#include "console/engineAPI.h" + +//--- Information pertaining to this page... ------------------ +/// @file +/// +/// For specifics on using the consoleDoc functionality, see @ref console_autodoc + +ConsoleFunctionGroupBegin(ConsoleDoc, "Console self-documentation functions. These output psuedo C++ suitable for feeeding through Doxygen or another auto documentation tool."); + +DefineConsoleFunction( dumpConsoleClasses, void, (bool dumpScript, bool dumpEngine), ( true, true ), + "@brief Dumps all declared console classes to the console.\n\n" + "@param dumpScript Optional parameter specifying whether or not classes defined in script should be dumped.\n" + "@param dumpEngine Optional parameter specifying whether or not classes defined in the engine should be dumped.\n" + "@ingroup Logging") +{ + Namespace::dumpClasses( dumpScript, dumpEngine ); +} + +DefineConsoleFunction(dumpConsoleFunctions, void, ( bool dumpScript, bool dumpEngine ), ( true, true ), + "@brief Dumps all declared console functions to the console.\n" + "@param dumpScript Optional parameter specifying whether or not functions defined in script should be dumped.\n" + "@param dumpEngine Optional parameter specitying whether or not functions defined in the engine should be dumped.\n" + "@ingroup Logging") +{ + Namespace::dumpFunctions( dumpScript, dumpEngine ); +} + +ConsoleFunctionGroupEnd(ConsoleDoc); + +/// Helper table to convert type ids to human readable names. +const char *typeNames[] = +{ + "Script", + "string", + "int", + "float", + "void", + "bool", + "", + "", + "unknown_overload" +}; + +void printClassHeader(const char* usage, const char * className, const char * superClassName, const bool stub) +{ + if(stub) + { + Con::printf("/// Stub class"); + Con::printf("/// "); + Con::printf("/// @note This is a stub class to ensure a proper class hierarchy. No "); + Con::printf("/// information was available for this class."); + } + + if( usage != NULL ) + { + // Copy Usage Document + S32 usageLen = dStrlen( usage ); + FrameTemp usageStr( usageLen ); + dStrcpy( usageStr, usage ); + + // Print Header + Con::printf( "/*!" ); + + // Print line by line, skipping the @field lines. + // + // fetch first line end + char *newLine = dStrchr( usageStr, '\n' ); + char *usagePtr = usageStr; + do + { + // Copy of one line + static char lineStr[2048] = {0}; + // Keyword will hold the last keyword (word following '@' or '\') encountered. + static char keyword[8] = {0}; + + S32 lineLen = 0; + + // If not the last line, increment pointer + if( newLine != NULL ) + { + *newLine = '\0'; + newLine ++; + } + + // Copy line and update usagePtr + dStrcpy( lineStr, usagePtr ); + usagePtr = (newLine != NULL ) ? newLine : usagePtr; + lineLen = dStrlen( lineStr ); + + // Get the keyword. This is the first word after an '@' or '\'. + const char* tempkw = dStrchr( lineStr, '@' ); + if( !tempkw ) + tempkw = dStrchr( lineStr, '\\' ); + + // If we found a new keyword, set it, otherwise, keep using the + // most recently found. + if( tempkw ) + { + dStrncpy( keyword, tempkw + 1, 5 ); + keyword[5] = '\0'; + } + + // Print all fields that aren't associated with the 'field' keyword. + if( dStrcmp( keyword, "field" ) ) + Con::printf( "%s", lineStr ); // print lineStr as an unformatted string (otherwise '%' characters in the string could cause problems) + + + // Fetch next line ending + newLine = dStrchr( usagePtr, '\n' ); + } while( newLine != NULL ); + + // DocBlock Footer + Con::printf( " */" ); + + } + + // Print out appropriate class header + if(superClassName) + Con::printf("class %s : public %s {", className, superClassName ? superClassName : ""); + else if(!className) + Con::printf("namespace Global {"); + else + Con::printf("class %s {", className); + + if(className) + Con::printf(" public:"); + +} + +void printClassMethod(const bool isVirtual, const char *retType, const char *methodName, const char* args, const char*usage) +{ + if(usage && usage[0] != ';' && usage[0] != 0) + Con::printf(" /*! %s */", usage); + Con::printf(" %s%s %s(%s) {}", isVirtual ? "virtual " : "", retType, methodName, args); +} + +void printGroupStart(const char * aName, const char * aDocs) +{ + Con::printf(""); + Con::printf(" /*! @name %s", aName); + + if(aDocs) + { + Con::printf(" "); + Con::printf(" %s", aDocs); + } + + Con::printf(" @{ */"); + + // Add a blank comment in order to make sure groups are parsed properly. + Con::printf(" /*! */"); +} + +void printClassMember(const bool isDeprec, const char * aType, const char * aName, const char * aDocs) +{ + Con::printf(" /*!"); + + if(aDocs) + { + Con::printf(" %s", aDocs); + Con::printf(" "); + } + + if(isDeprec) + Con::printf(" @deprecated This member is deprecated, which means that its value is always undefined."); + + Con::printf(" */"); + + Con::printf(" %s %s;", isDeprec ? "deprecated" : aType, aName); +} + +void printGroupEnd() +{ + Con::printf(" /// @}"); + Con::printf(""); +} + +void printClassFooter() +{ + Con::printf("};"); + Con::printf(""); +} + +void Namespace::printNamespaceEntries(Namespace * g, bool dumpScript, bool dumpEngine ) +{ + static bool inGroup = false; + + // Go through all the entries. + // Iterate through the methods of the namespace... + for(Entry *ewalk = g->mEntryList; ewalk; ewalk = ewalk->mNext) + { + int eType = ewalk->mType; + const char * funcName = ewalk->mFunctionName; + + if( ( eType == Entry::ConsoleFunctionType ) && !dumpScript ) + continue; + + if( ( eType != Entry::ConsoleFunctionType ) && !dumpEngine ) + continue; + + // If it's a function + if( eType >= Entry::ConsoleFunctionType ) + { + printClassMethod(true, typeNames[eType], funcName, ewalk->getArgumentsString().c_str(), + ewalk->getDocString().c_str()); + } + else if(ewalk->mType == Entry::GroupMarker) + { + if(!inGroup) + printGroupStart(ewalk->cb.mGroupName, ewalk->mUsage); + else + printGroupEnd(); + + inGroup = !inGroup; + } + else if(ewalk->mType == Entry::ScriptCallbackType) + { + // It's a script callback - emit some sort of appropriate info. + Con::printf(" /*! %s */", ewalk->getDocString().c_str()); + Con::printf(" %s;", ewalk->getPrototypeString().c_str()); + Con::printf(""); + } + else if(ewalk->mFunctionOffset) // If it's a builtin function... + { + String args = ewalk->mCode->getFunctionArgs(ewalk->mFunctionOffset); + printClassMethod(false, typeNames[ewalk->mType], ewalk->mFunctionName, args, ""); + } + else + { + Con::printf(" // got an unknown thing?? %d", ewalk->mType ); + } + } +} + +void Namespace::dumpClasses( bool dumpScript, bool dumpEngine ) +{ + VectorPtr vec; + trashCache(); + vec.reserve( 1024 ); + + // We use mHashSequence to mark if we have traversed... + // so mark all as zero to start. + for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext) + walk->mHashSequence = 0; + + for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext) + { + VectorPtr stack; + stack.reserve( 1024 ); + + // Get all the parents of this namespace... (and mark them as we go) + Namespace *parentWalk = walk; + while(parentWalk) + { + if(parentWalk->mHashSequence != 0) + break; + if(parentWalk->mPackage == 0) + { + parentWalk->mHashSequence = 1; // Mark as traversed. + stack.push_back(parentWalk); + } + parentWalk = parentWalk->mParent; + } + + // Load stack into our results vector. + while(stack.size()) + { + vec.push_back(stack[stack.size() - 1]); + stack.pop_back(); + } + } + + // Go through previously discovered classes + U32 i; + for(i = 0; i < vec.size(); i++) + { + const char *className = vec[i]->mName; + const char *superClassName = vec[i]->mParent ? vec[i]->mParent->mName : NULL; + + // Skip the global namespace, that gets dealt with in dumpFunctions + if(!className) continue; + + // If we're just dumping script functions, then we don't want to dump + // a class that only contains script functions. So, we iterate over all + // the functions. + if( !dumpScript ) + { + bool found = false; + for(Entry *ewalk = vec[i]->mEntryList; ewalk; ewalk = ewalk->mNext) + { + if( ewalk->mType != Entry::ConsoleFunctionType ) + { + found = true; + break; + } + } + + // If we don't have engine functions and the namespace name + // doesn't match the class name... then its a script class. + if ( !found && !vec[i]->isClass() ) + continue; + } + + // And we do the same for engine functions. + if( !dumpEngine ) + { + bool found = false; + for(Entry *ewalk = vec[i]->mEntryList; ewalk; ewalk = ewalk->mNext) + { + if( ewalk->mType == Entry::ConsoleFunctionType ) + { + found = true; + break; + } + } + if( !found ) + continue; + } + + // If we hit a class with no members and no classRep, do clever filtering. + if(vec[i]->mEntryList == NULL && vec[i]->mClassRep == NULL) + { + // Print out a short stub so we get a proper class hierarchy. + if(superClassName) { // Filter hack; we don't want non-inheriting classes... + printClassHeader( NULL, className,superClassName, true); + printClassFooter(); + } + continue; + } + + // Print the header for the class.. + printClassHeader(vec[i]->mUsage, className, superClassName, false); + + // Deal with entries. + printNamespaceEntries(vec[i], dumpScript, dumpEngine); + + // Deal with the classRep (to get members)... + AbstractClassRep *rep = vec[i]->mClassRep; + AbstractClassRep::FieldList emptyList; + AbstractClassRep::FieldList *parentList = &emptyList; + AbstractClassRep::FieldList *fieldList = &emptyList; + + // Since all fields are defined in the engine, if we're not dumping + // engine stuff, than we shouldn't dump the fields. + if(dumpEngine && rep) + { + // Get information about the parent's fields... + AbstractClassRep *parentRep = vec[i]->mParent ? vec[i]->mParent->mClassRep : NULL; + if(parentRep) + parentList = &(parentRep->mFieldList); + + // Get information about our fields + fieldList = &(rep->mFieldList); + + // Go through all our fields... + for(U32 j = 0; j < fieldList->size(); j++) + { + switch((*fieldList)[j].type) + { + case AbstractClassRep::StartArrayFieldType: + case AbstractClassRep::EndArrayFieldType: + break; + case AbstractClassRep::StartGroupFieldType: + printGroupStart((*fieldList)[j].pGroupname, (*fieldList)[j].pFieldDocs); + break; + case AbstractClassRep::EndGroupFieldType: + printGroupEnd(); + break; + default: + case AbstractClassRep::DeprecatedFieldType: + { + // Skip over fields that are already defined in + // our parent class. + if ( parentRep && parentRep->findField( (*fieldList)[j].pFieldname ) ) + continue; + + bool isDeprecated = ((*fieldList)[j].type == AbstractClassRep::DeprecatedFieldType); + + if(isDeprecated) + { + printClassMember( + true, + "", + (*fieldList)[j].pFieldname, + (*fieldList)[j].pFieldDocs + ); + } + else + { + ConsoleBaseType *cbt = ConsoleBaseType::getType((*fieldList)[j].type); + + printClassMember( + false, + cbt ? cbt->getTypeClassName() : "", + (*fieldList)[j].pFieldname, + (*fieldList)[j].pFieldDocs + ); + } + } + } + } + } + + if( dumpScript ) + { + // Print out fields defined in script docs for this namespace. + // These fields are specified by the 'field' keyword in the usage + // string. + + // The field type and name. + char fieldName[256]; + char fieldDoc[1024]; + + // Usage string iterator. + const char* field = vec[i]->mUsage; + + while( field ) + { + // Find the first field keyword. + const char* tempField = dStrstr( field, "@field" ); + if( !tempField ) + tempField = dStrstr( field, "\\field" ); + + field = tempField; + + if( !field ) + break; + + // Move to the field name. + field += 7; + + // Copy the field type and name. These should both be followed by a + // space so only in this case will we actually store it. + S32 spaceCount = 0; + S32 index = 0; + bool valid = false; + while( field && ( *field != '\n' ) ) + { + if( index >= 255 ) + break; + + if( *field == ' ' ) + spaceCount++; + + if( spaceCount == 2 ) + { + valid = true; + break; + } + + fieldName[index++] = *field; + field++; + } + + if( !valid ) + continue; + + fieldName[index] = '\0'; + + // Now copy from field to the next keyword. + const char* nextKeyword = dStrchr( field, '@' ); + if( !nextKeyword ) + nextKeyword = dStrchr( field, '\\' ); + + // Grab the length of the doc string. + S32 docLen = dStrlen( field ); + if( nextKeyword ) + docLen = nextKeyword - field; + + // Make sure it will fit in the buffer. + if( docLen > 1023 ) + docLen = 1023; + + // Copy. + dStrncpy( fieldDoc, field, docLen ); + fieldDoc[docLen] = '\0'; + field += docLen; + + // Print + Con::printf( " /*!" ); + Con::printf( " %s", fieldDoc ); + Con::printf( " */" ); + Con::printf( " %s;", fieldName ); + } + } + + // Close the class/namespace. + printClassFooter(); + } +} + +void Namespace::dumpFunctions( bool dumpScript, bool dumpEngine ) +{ + // Get the global namespace. + Namespace* g = find(NULL); //->mParent; + + printClassHeader(NULL, NULL,NULL, false); + + while(g) + { + printNamespaceEntries(g, dumpScript, dumpEngine ); + g = g->mParent; + } + + printClassFooter(); +} diff --git a/Engine/source/console/consoleDoc.h b/Engine/source/console/consoleDoc.h new file mode 100644 index 000000000..77107ee75 --- /dev/null +++ b/Engine/source/console/consoleDoc.h @@ -0,0 +1,192 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// This file exists solely to document consoleDoc.cpp + +/// @defgroup script_autodoc Script Auto-Documentation +/// @ingroup console_system Console System +/// +/// @section script_autodoc_using Using Script Auto-Documentation +/// +/// There are on the order of three hundred functions exposed to the script language +/// through the console. It is therefore extremely important that they be documented, +/// but due to their number, it is difficult to maintain a seperate reference document. +/// +/// Therefore, a simple documentation system has been built into the scripting engine. It +/// was initially added by Mark Frohnmayer, and later enhanced by Ben Garney. The +/// scripting engine supports grouping functions and methods, to help organize the +/// several hundred functions, as well as associating a "usage string" with functions and +/// groups. +/// +/// @note The results of a console doc dump will vary depending on when you run it. If +/// you run it, for example, while in the game menu, it won't output any data for +/// the script-defined classes which are defined for gameplay. To get comprehensive +/// documentation, you may need to write a special script that will get all your +/// classes loaded into the scripting engine. +/// +/// The script documentation system is designed to output a dump of the current state +/// of the scripting engine in a format understandable by Doxygen. It does this by +/// traversing the namespace/class hierarchy in memory at the time of the dump, and +/// outputting psuedo-C++ code equivalent to this class hierarchy. +/// +/// @subsection script_autodoc_using_script For the Scripter... +/// +/// Currently, there is no way to associate usage strings or other documentation with script code +/// like you can with C++ code. +/// +/// You can get a list of all the methods and fields of an object from any object which inherits +/// from SimObject (ie, every object), as well as the documentation on those objects by using the +/// dump() method from the console: +/// +/// @code +/// ==>$foo = new SimObject(); +/// ==>$foo.dump(); +/// Member Fields: +/// Tagged Fields: +/// Methods: +/// delete() - obj.delete() +/// dump() - obj.dump() +/// getClassName() - obj.getClassName() +/// getGroup() - obj.getGroup() +/// getId() - obj.getId() +/// getName() - obj.getName() +/// getType() - obj.getType() +/// save() - obj.save(fileName, ) +/// schedule() - object.schedule(time, command, ); +/// setName() - obj.setName(newName) +/// @endcode +/// +/// In the Torque example app, there are two functions defined in common\\client\\scriptDoc.cs +/// which automate the process of dumping the documentation. They make use of the ConsoleLogger +/// object to output the documentation to a file, and look like this: +/// +/// @note You may want to add this code, or code like it, to your project if you have +/// rewritten the script code in common. +/// +/// @code +/// // Writes out all script functions to a file +/// function writeOutFunctions() { +/// new ConsoleLogger( logger, "ConsoleFunctions.txt", false ); +/// dumpConsoleFunctions(); +/// logger.delete(); +/// } +/// +/// // Writes out all script classes to a file +/// function writeOutClasses() { +/// new ConsoleLogger( logger, "scriptClasses.txt", false ); +/// dumpConsoleClasses(); +/// logger.delete(); +/// } +/// @endcode +/// +/// @subsection script_autodoc_using_coder For the C++ Coder... +/// +/// @note It is of the utmost important that you keep your usage strings up to date! +/// Usage strings are the only way that a scripter has to know how to use the methods, +/// functions, and variables you expose. Misleading, missing, or out of date documentation +/// will make their lives much harder - and yours, too, because you'll have to keep +/// explaining things to them! So make everyone's lives easier - keep your usage strings +/// clear, concise, and up to date. +/// +/// There are four types of items which can be documented using the autodocumentation system: +/// - Fields, which are defined using the addField() calls. They are documented +/// by passing a string to the usage parameter. +/// - Field groups, which are defined using the beginGroup() and endGroup() calls. +/// They are documented by passing a descriptive string to the usage parameter. +/// - Method groups, which are defined using beginCommandGroup(), endCommandGroup(), +/// ConsoleMethodGroupEnd(), ConsoleMethodGroupBegin(), ConsoleFunctionGroupEnd(), and +/// ConsoleFunctionGroupBegin(). +/// - Methods and functions, which are defined using either SimObject::addCommand(), +/// the ConsoleMethod() macro, or the ConsoleFunction() macro. Methods and functions are +/// special in that the usage strings should be in a specific format, so +/// that parameter information can be extracted from them and placed into the Doxygen +/// output. +/// +/// You can use standard Doxygen commands in your comments, to make the documentation clearer. +/// Of particular use are \@returns, \@param, \@note, and \@deprecated. +/// +/// Examples using global definitions. +/// +/// @code +/// // Example of using Doxygen commands. +/// ConsoleFunction(alxGetWaveLen, S32, 2, 2, "(string filename)" +/// "Get length of a wave file\n\n" +/// "@param filename File to determine length of.\n" +/// "@returns Length in milliseconds.") +/// +/// // A function group... +/// ConsoleFunctionGroupBegin(Example, "This is an example group! Notice that the name for the group" +/// "must be a valid identifier, due to limitations in the C preprocessor."); +/// +/// // ConsoleFunction definitions go here. +/// +/// ConsoleFunctionGroupEnd(Example); +/// +/// // You can do similar things with methods... +/// ConsoleMethodGroupBegin(SimSet, UsefulFuncs, "Here are some useful functions involving a SimSet."); +/// ConsoleMethod(SimSet, listObjects, void, 2, 2, "set.listObjects();") +/// ConsoleMethodGroupEnd(SimSet, UsefulFuncs, "Here are some more useful functions involving a SimSet."); +/// @endcode +/// +/// Examples using addField +/// +/// @note Using addCommand is strongly deprecated. +/// +/// @code +/// // Example of a field group. +/// addGroup( "Logging", "Things relating to logging." ); +/// addField( "level", TYPEID< ConsoleLogLevel >(), Offset( mLevel, ConsoleLogger ) ); +/// endGroup( "Logging" ); +/// @endcode +/// +/// @section script_autodoc_makingdocs How to Generate Script Docs +/// +/// Script docs can be generated by running the dumpConsoleFunctions() and +/// dumpConsoleClasses(), then running the output through Doxygen. There is an +/// example Doxygen configuration file to do this at +/// doc\\doxygen\\html\\script_doxygen.html.cfg. Doxygen will parse the psuedo-C++ +/// generated by the console doc code and produce a class hierarchy and documentation +/// of the global namespace. You may need to tweak the paths in the configuration file +/// slightly to reflect your individual setup. +/// +/// @section script_autodoc_internals Script Auto-Documentation Internals +/// +/// The consoleDoc system works by inserting "hidden" entries in Namespace and +/// AbstractClassRep; these hidden entries are assigned special type IDs so that +/// they aren't touched by the standard name resolution code. At documentation +/// creation time, the dumpConsole functions iterate through the Namespace hierarchy +/// and the AbstractClassRep data and extract this "hidden" information, outputting +/// it in a Doxygen-compatible format. +/// +/// @note You can customize the output of the console documentation system by modifying +/// these functions: +/// - printClassHeader() +/// - printClassMethod() +/// - printGroupStart() +/// - printClassMember() +/// - printGroupEnd() +/// - printClassFooter() +/// +/// @note There was once support for 'overloaded' script functions; ie, script functions +/// with multiple usage strings. Certain functions in the audio library used this. +/// However, it was deemed too complex, and removed from the scripting engine. There +/// are still some latent traces of it, however. diff --git a/Engine/source/console/consoleFunctions.cpp b/Engine/source/console/consoleFunctions.cpp new file mode 100644 index 000000000..a2a6b1137 --- /dev/null +++ b/Engine/source/console/consoleFunctions.cpp @@ -0,0 +1,2571 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/console.h" +#include "console/consoleInternal.h" +#include "console/engineAPI.h" +#include "console/ast.h" +#include "core/strings/findMatch.h" +#include "core/strings/stringUnit.h" +#include "core/strings/unicode.h" +#include "core/stream/fileStream.h" +#include "console/compiler.h" +#include "platform/event.h" +#include "platform/platformInput.h" +#include "core/util/journal/journal.h" +#include "core/util/uuid.h" + +#ifdef TORQUE_DEMO_PURCHASE +#include "gui/core/guiCanvas.h" +#endif + +// This is a temporary hack to get tools using the library to +// link in this module which contains no other references. +bool LinkConsoleFunctions = false; + +// Buffer for expanding script filenames. +static char scriptFilenameBuffer[1024]; + + +//============================================================================= +// String Functions. +//============================================================================= +// MARK: ---- String Functions ---- + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( strasc, int, ( const char* chr ),, + "Return the integer character code value corresponding to the first character in the given string.\n" + "@param chr a (one-character) string.\n" + "@return the UTF32 code value for the first character in the given string.\n" + "@ingroup Strings" ) +{ + return oneUTF8toUTF32( chr ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( strformat, const char*, ( const char* format, const char* value ),, + "Format the given value as a string using printf-style formatting.\n" + "@param format A printf-style format string.\n" + "@param value The value argument matching the given format string.\n\n" + "@tsexample\n" + "// Convert the given integer value to a string in a hex notation.\n" + "%hex = strformat( \"%x\", %value );\n" + "@endtsexample\n" + "@ingroup Strings\n" + "@see http://en.wikipedia.org/wiki/Printf" ) +{ + char* pBuffer = Con::getReturnBuffer(64); + const char *pch = format; + + pBuffer[0] = '\0'; + while (*pch != '\0' && *pch !='%') + pch++; + while (*pch != '\0' && !dIsalpha(*pch)) + pch++; + if (*pch == '\0') + { + Con::errorf("strFormat: Invalid format string!\n"); + return pBuffer; + } + + switch(*pch) + { + case 'c': + case 'C': + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + dSprintf( pBuffer, 64, format, dAtoi( value ) ); + break; + + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + dSprintf( pBuffer, 64, format, dAtof( value ) ); + break; + + default: + Con::errorf("strFormat: Invalid format string!\n"); + break; + } + + return pBuffer; +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( strcmp, S32, ( const char* str1, const char* str2 ),, + "Compares two strings using case-sensitive comparison.\n" + "@param str1 The first string.\n" + "@param str2 The second string.\n" + "@return 0 if both strings are equal, a value <0 if the first character different in str1 has a smaller character code " + "value than the character at the same position in str2, and a value >1 otherwise.\n\n" + "@tsexample\n" + "if( strcmp( %var, \"foobar\" ) == 0 )\n" + " echo( \"%var is equal to 'foobar'\" );\n" + "@endtsexample\n" + "@see stricmp\n" + "@see strnatcmp\n" + "@ingroup Strings" ) +{ + return dStrcmp( str1, str2 ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( stricmp, S32, ( const char* str1, const char* str2 ),, + "Compares two strings using case-insensitive comparison.\n" + "@param str1 The first string.\n" + "@param str2 The second string.\n" + "@return 0 if both strings are equal, a value <0 if the first character different in str1 has a smaller character code " + "value than the character at the same position in str2, and a value >0 otherwise.\n\n" + "@tsexample\n" + "if( stricmp( \"FOObar\", \"foobar\" ) == 0 )\n" + " echo( \"this is always true\" );\n" + "@endtsexample\n" + "@see strcmp\n" + "@see strinatcmp\n" + "@ingroup Strings" ) +{ + return dStricmp( str1, str2 ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( strnatcmp, S32, ( const char* str1, const char* str2 ),, + "Compares two strings using \"natural order\" case-sensitive comparison.\n" + "Natural order means that rather than solely comparing single character code values, strings are ordered in a " + "natural way. For example, the string \"hello10\" is considered greater than the string \"hello2\" even though " + "the first numeric character in \"hello10\" actually has a smaller character value than the corresponding character " + "in \"hello2\". However, since 10 is greater than 2, strnatcmp will put \"hello10\" after \"hello2\".\n" + "@param str1 The first string.\n" + "@param str2 The second string.\n\n" + "@return 0 if the strings are equal, a value >0 if @a str1 comes after @a str2 in a natural order, and a value " + "<0 if @a str1 comes before @a str2 in a natural order.\n\n" + "@tsexample\n" + "// Bubble sort 10 elements of %array using natural order\n" + "do\n" + "{\n" + " %swapped = false;\n" + " for( %i = 0; %i < 10 - 1; %i ++ )\n" + " if( strnatcmp( %array[ %i ], %array[ %i + 1 ] ) > 0 )\n" + " {\n" + " %temp = %array[ %i ];\n" + " %array[ %i ] = %array[ %i + 1 ];\n" + " %array[ %i + 1 ] = %temp;\n" + " %swapped = true;\n" + " }\n" + "}\n" + "while( %swapped );\n" + "@endtsexample\n" + "@see strcmp\n" + "@see strinatcmp\n" + "@ingroup Strings" ) +{ + return dStrnatcmp( str1, str2 ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( strinatcmp, S32, ( const char* str1, const char* str2 ),, + "Compares two strings using \"natural order\" case-insensitive comparison.\n" + "Natural order means that rather than solely comparing single character code values, strings are ordered in a " + "natural way. For example, the string \"hello10\" is considered greater than the string \"hello2\" even though " + "the first numeric character in \"hello10\" actually has a smaller character value than the corresponding character " + "in \"hello2\". However, since 10 is greater than 2, strnatcmp will put \"hello10\" after \"hello2\".\n" + "@param str1 The first string.\n" + "@param str2 The second string.\n" + "@return 0 if the strings are equal, a value >0 if @a str1 comes after @a str2 in a natural order, and a value " + "<0 if @a str1 comes before @a str2 in a natural order.\n\n" + "@tsexample\n\n" + "// Bubble sort 10 elements of %array using natural order\n" + "do\n" + "{\n" + " %swapped = false;\n" + " for( %i = 0; %i < 10 - 1; %i ++ )\n" + " if( strnatcmp( %array[ %i ], %array[ %i + 1 ] ) > 0 )\n" + " {\n" + " %temp = %array[ %i ];\n" + " %array[ %i ] = %array[ %i + 1 ];\n" + " %array[ %i + 1 ] = %temp;\n" + " %swapped = true;\n" + " }\n" + "}\n" + "while( %swapped );\n" + "@endtsexample\n" + "@see stricmp\n" + "@see strnatcmp\n" + "@ingroup Strings" ) +{ + return dStrnatcasecmp( str1, str2 ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( strlen, S32, ( const char* str ),, + "Get the length of the given string in bytes.\n" + "@note This does not return a true character count for strings with multi-byte characters!\n" + "@param str A string.\n" + "@return The length of the given string in bytes.\n" + "@ingroup Strings" ) +{ + return dStrlen( str ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( strstr, S32, ( const char* string, const char* substring ),, + "Find the start of @a substring in the given @a string searching from left to right.\n" + "@param string The string to search.\n" + "@param substring The string to search for.\n" + "@return The index into @a string at which the first occurrence of @a substring was found or -1 if @a substring could not be found.\n\n" + "@tsexample\n" + "strstr( \"abcd\", \"c\" ) // Returns 2.\n" + "@endtsexample\n" + "@ingroup Strings" ) +{ + const char* retpos = dStrstr( string, substring ); + if( !retpos ) + return -1; + + return retpos - string; +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( strpos, S32, ( const char* haystack, const char* needle, int offset ), ( 0 ), + "Find the start of @a needle in @a haystack searching from left to right beginning at the given offset.\n" + "@param haystack The string to search.\n" + "@param needle The string to search for.\n" + "@return The index at which the first occurrence of @a needle was found in @a haystack or -1 if no match was found.\n\n" + "@tsexample\n" + "strpos( \"b ab\", \"b\", 1 ) // Returns 3.\n" + "@endtsexample\n" + "@ingroup Strings" ) +{ + S32 start = offset; + U32 sublen = dStrlen( needle ); + U32 strlen = dStrlen( haystack ); + if(start < 0) + return -1; + if(sublen + start > strlen) + return -1; + for(; start + sublen <= strlen; start++) + if(!dStrncmp(haystack + start, needle, sublen)) + return start; + return -1; +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( ltrim, const char*, ( const char* str ),, + "Remove leading whitespace from the string.\n" + "@param str A string.\n" + "@return A string that is the same as @a str but with any leading (i.e. leftmost) whitespace removed.\n\n" + "@tsexample\n" + "ltrim( \" string \" ); // Returns \"string \".\n" + "@endtsexample\n" + "@see rtrim\n" + "@see trim\n" + "@ingroup Strings" ) +{ + const char *ret = str; + while(*ret == ' ' || *ret == '\n' || *ret == '\t') + ret++; + return ret; +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( rtrim, const char*, ( const char* str ),, + "Remove trailing whitespace from the string.\n" + "@param str A string.\n" + "@return A string that is the same as @a str but with any trailing (i.e. rightmost) whitespace removed.\n\n" + "@tsexample\n" + "rtrim( \" string \" ); // Returns \" string\".\n" + "@endtsexample\n" + "@see ltrim\n" + "@see trim\n" + "@ingroup Strings" ) +{ + S32 firstWhitespace = 0; + S32 pos = 0; + while(str[pos]) + { + if(str[pos] != ' ' && str[pos] != '\n' && str[pos] != '\t') + firstWhitespace = pos + 1; + pos++; + } + char *ret = Con::getReturnBuffer(firstWhitespace + 1); + dStrncpy(ret, str, firstWhitespace); + ret[firstWhitespace] = 0; + return ret; +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( trim, const char*, ( const char* str ),, + "Remove leading and trailing whitespace from the string.\n" + "@param str A string.\n" + "@return A string that is the same as @a str but with any leading (i.e. leftmost) and trailing (i.e. rightmost) whitespace removed.\n\n" + "@tsexample\n" + "trim( \" string \" ); // Returns \"string\".\n" + "@endtsexample\n" + "@ingroup Strings" ) +{ + const char *ptr = str; + while(*ptr == ' ' || *ptr == '\n' || *ptr == '\t') + ptr++; + S32 firstWhitespace = 0; + S32 pos = 0; + while(ptr[pos]) + { + if(ptr[pos] != ' ' && ptr[pos] != '\n' && ptr[pos] != '\t') + firstWhitespace = pos + 1; + pos++; + } + char *ret = Con::getReturnBuffer(firstWhitespace + 1); + dStrncpy(ret, ptr, firstWhitespace); + ret[firstWhitespace] = 0; + return ret; +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( stripChars, const char*, ( const char* str, const char* chars ),, + "Remove all occurrences of characters contained in @a chars from @a str.\n" + "@param str The string to filter characters out from.\n" + "@param chars A string of characters to filter out from @a str.\n" + "@return A version of @a str with all occurrences of characters contained in @a chars filtered out.\n\n" + "@tsexample\n" + "stripChars( \"teststring\", \"se\" ); // Returns \"tttring\"." + "@endtsexample\n" + "@ingroup Strings" ) +{ + char* ret = Con::getReturnBuffer( dStrlen( str ) + 1 ); + dStrcpy( ret, str ); + U32 pos = dStrcspn( ret, chars ); + while ( pos < dStrlen( ret ) ) + { + dStrcpy( ret + pos, ret + pos + 1 ); + pos = dStrcspn( ret, chars ); + } + return( ret ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( strlwr, const char*, ( const char* str ),, + "Return an all lower-case version of the given string.\n" + "@param str A string.\n" + "@return A version of @a str with all characters converted to lower-case.\n\n" + "@tsexample\n" + "strlwr( \"TesT1\" ) // Returns \"test1\"\n" + "@endtsexample\n" + "@see strupr\n" + "@ingroup Strings" ) +{ + char *ret = Con::getReturnBuffer(dStrlen(str) + 1); + dStrcpy(ret, str); + return dStrlwr(ret); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( strupr, const char*, ( const char* str ),, + "Return an all upper-case version of the given string.\n" + "@param str A string.\n" + "@return A version of @a str with all characters converted to upper-case.\n\n" + "@tsexample\n" + "strupr( \"TesT1\" ) // Returns \"TEST1\"\n" + "@endtsexample\n" + "@see strlwr\n" + "@ingroup Strings" ) +{ + char *ret = Con::getReturnBuffer(dStrlen(str) + 1); + dStrcpy(ret, str); + return dStrupr(ret); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( strchr, const char*, ( const char* str, const char* chr ),, + "Find the first occurrence of the given character in @a str.\n" + "@param str The string to search.\n" + "@param chr The character to search for. Only the first character from the string is taken.\n" + "@return The remainder of the input string starting with the given character or the empty string if the character could not be found.\n\n" + "@see strrchr\n" + "@ingroup Strings" ) +{ + const char *ret = dStrchr( str, chr[ 0 ] ); + return ret ? ret : ""; +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( strrchr, const char*, ( const char* str, const char* chr ),, + "Find the last occurrence of the given character in @a str." + "@param str The string to search.\n" + "@param chr The character to search for. Only the first character from the string is taken.\n" + "@return The remainder of the input string starting with the given character or the empty string if the character could not be found.\n\n" + "@see strchr\n" + "@ingroup Strings" ) +{ + const char *ret = dStrrchr( str, chr[ 0 ] ); + return ret ? ret : ""; +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( strreplace, const char*, ( const char* source, const char* from, const char* to ),, + "Replace all occurrences of @a from in @a source with @a to.\n" + "@param source The string in which to replace the occurrences of @a from.\n" + "@param from The string to replace in @a source.\n" + "@param to The string with which to replace occurrences of @from.\n" + "@return A string with all occurrences of @a from in @a source replaced by @a to.\n\n" + "@tsexample\n" + "strreplace( \"aabbccbb\", \"bb\", \"ee\" ) // Returns \"aaeeccee\".\n" + "@endtsexample\n" + "@ingroup Strings" ) +{ + S32 fromLen = dStrlen( from ); + if(!fromLen) + return source; + + S32 toLen = dStrlen( to ); + S32 count = 0; + const char *scan = source; + while(scan) + { + scan = dStrstr(scan, from); + if(scan) + { + scan += fromLen; + count++; + } + } + char *ret = Con::getReturnBuffer(dStrlen(source) + 1 + (toLen - fromLen) * count); + U32 scanp = 0; + U32 dstp = 0; + for(;;) + { + const char *scan = dStrstr(source + scanp, from); + if(!scan) + { + dStrcpy(ret + dstp, source + scanp); + return ret; + } + U32 len = scan - (source + scanp); + dStrncpy(ret + dstp, source + scanp, len); + dstp += len; + dStrcpy(ret + dstp, to); + dstp += toLen; + scanp += len + fromLen; + } + return ret; +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( strrepeat, const char*, ( const char* str, S32 numTimes, const char* delimiter ), ( "" ), + "Return a string that repeats @a str @a numTimes number of times delimiting each occurrence with @a delimiter.\n" + "@param str The string to repeat multiple times.\n" + "@param numTimes The number of times to repeat @a str in the result string.\n" + "@param delimiter The string to put between each repetition of @a str.\n" + "@return A string containing @a str repeated @a numTimes times.\n\n" + "@tsexample\n" + "strrepeat( \"a\", 5, \"b\" ) // Returns \"ababababa\".\n" + "@endtsexample\n" + "@ingroup Strings" ) +{ + StringBuilder result; + bool isFirst = false; + for( U32 i = 0; i < numTimes; ++ i ) + { + if( !isFirst ) + result.append( delimiter ); + + result.append( str ); + isFirst = false; + } + + return Con::getReturnBuffer( result ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( getSubStr, const char*, ( const char* str, S32 start, S32 numChars ), ( -1 ), + "@brief Return a substring of @a str starting at @a start and continuing either through to the end of @a str " + "(if @a numChars is -1) or for @a numChars characters (except if this would exceed the actual source " + "string length).\n" + "@param str The string from which to extract a substring.\n" + "@param start The offset at which to start copying out characters.\n" + "@param numChars Optional argument to specify the number of characters to copy. If this is -1, all characters up the end " + "of the input string are copied.\n" + "@return A string that contains the given portion of the input string.\n\n" + "@tsexample\n" + "getSubStr( \"foobar\", 1, 2 ) // Returns \"oo\".\n" + "@endtsexample\n\n" + "@ingroup Strings" ) +{ + S32 baseLen = dStrlen( str ); + + if( numChars == -1 ) + numChars = baseLen - start; + + if (start < 0 || numChars < 0) { + Con::errorf(ConsoleLogEntry::Script, "getSubStr(...): error, starting position and desired length must be >= 0: (%d, %d)", start, numChars); + + return ""; + } + + if (baseLen < start) + return ""; + + U32 actualLen = numChars; + if (start + numChars > baseLen) + actualLen = baseLen - start; + + char *ret = Con::getReturnBuffer(actualLen + 1); + dStrncpy(ret, str + start, actualLen); + ret[actualLen] = '\0'; + + return ret; +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( strIsMatchExpr, bool, ( const char* pattern, const char* str, bool caseSensitive ), ( false ), + "Match a pattern against a string.\n" + "@param pattern The wildcard pattern to match against. The pattern can include characters, '*' to match " + "any number of characters and '?' to match a single character.\n" + "@param str The string which should be matched against @a pattern.\n" + "@param caseSensitive If true, characters in the pattern are matched in case-sensitive fashion against " + "this string. If false, differences in casing are ignored.\n" + "@return True if @a str matches the given @a pattern.\n\n" + "@tsexample\n" + "strIsMatchExpr( \"f?o*R\", \"foobar\" ) // Returns true.\n" + "@endtsexample\n" + "@see strIsMatchMultipleExpr\n" + "@ingroup Strings" ) +{ + return FindMatch::isMatch( pattern, str, caseSensitive ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( strIsMatchMultipleExpr, bool, ( const char* patterns, const char* str, bool caseSensitive ), ( false ), + "Match a multiple patterns against a single string.\n" + "@param patterns A tab-separated list of patterns. Each pattern can include charaters, '*' to match " + "any number of characters and '?' to match a single character. Each of the patterns is tried in turn.\n" + "@param str The string which should be matched against @a patterns.\n" + "@param caseSensitive If true, characters in the pattern are matched in case-sensitive fashion against " + "this string. If false, differences in casing are ignored.\n" + "@return True if @a str matches any of the given @a patterns.\n\n" + "@tsexample\n" + "strIsMatchMultipleExpr( \"*.cs *.gui *.mis\", \"mymission.mis\" ) // Returns true.\n" + "@endtsexample\n" + "@see strIsMatchExpr\n" + "@ingroup Strings" ) +{ + return FindMatch::isMatchMultipleExprs( patterns, str, caseSensitive ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( getTrailingNumber, S32, ( const char* str ),, + "Get the numeric suffix of the given input string.\n" + "@param str The string from which to read out the numeric suffix.\n" + "@return The numeric value of the number suffix of @a str or -1 if @a str has no such suffix.\n\n" + "@tsexample\n" + "getTrailingNumber( \"test123\" ) // Returns '123'.\n" + "@endtsexample\n\n" + "@see stripTrailingNumber\n" + "@ingroup Strings" ) +{ + S32 suffix = -1; + String outStr( String::GetTrailingNumber( str, suffix ) ); + return suffix; +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( stripTrailingNumber, String, ( const char* str ),, + "Strip a numeric suffix from the given string.\n" + "@param str The string from which to strip its numeric suffix.\n" + "@return The string @a str without its number suffix or the original string @a str if it has no such suffix.\n\n" + "@tsexample\n" + "stripTrailingNumber( \"test123\" ) // Returns \"test\".\n" + "@endtsexample\n\n" + "@see getTrailingNumber\n" + "@ingroup Strings" ) +{ + S32 suffix; + return String::GetTrailingNumber( str, suffix ); +} + +//---------------------------------------------------------------- + +DefineConsoleFunction( isspace, bool, ( const char* str, S32 index ),, + "Test whether the character at the given position is a whitespace character.\n" + "Characters such as tab, space, or newline are considered whitespace.\n" + "@param str The string to test.\n" + "@param index The index of a character in @a str.\n" + "@return True if the character at the given index in @a str is a whitespace character; false otherwise.\n\n" + "@see isalnum\n" + "@ingroup Strings" ) +{ + if( index >= 0 && index < dStrlen( str ) ) + return dIsspace( str[ index ] ); + else + return false; +} + +//---------------------------------------------------------------- + +DefineConsoleFunction( isalnum, bool, ( const char* str, S32 index ),, + "Test whether the character at the given position is an alpha-numeric character.\n" + "Alpha-numeric characters are characters that are either alphabetic (a-z, A-Z) or numbers (0-9).\n" + "@param str The string to test.\n" + "@param index The index of a character in @a str.\n" + "@return True if the character at the given index in @a str is an alpha-numeric character; false otherwise.\n\n" + "@see isspace\n" + "@ingroup Strings" ) +{ + if( index >= 0 && index < dStrlen( str ) ) + return dIsalnum( str[ index ] ); + else + return false; +} + +//---------------------------------------------------------------- + +DefineConsoleFunction( startsWith, bool, ( const char* str, const char* prefix, bool caseSensitive ), ( false ), + "Test whether the given string begins with the given prefix.\n" + "@param str The string to test.\n" + "@param prefix The potential prefix of @a str.\n" + "@param caseSensitive If true, the comparison will be case-sensitive; if false, differences in casing will " + "not be taken into account.\n" + "@return True if the first characters in @a str match the complete contents of @a prefix; false otherwise.\n\n" + "@tsexample\n" + "startsWith( \"TEST123\", \"test\" ) // Returns true.\n" + "@endtsexample\n" + "@see endsWith\n" + "@ingroup Strings" ) +{ + // if the target string is empty, return true (all strings start with the empty string) + S32 srcLen = dStrlen( str ); + S32 targetLen = dStrlen( prefix ); + if( targetLen == 0 ) + return true; + // else if the src string is empty, return false (empty src does not start with non-empty target) + else if( srcLen == 0 ) + return false; + + if( caseSensitive ) + return ( dStrncmp( str, prefix, targetLen ) == 0 ); + + // both src and target are non empty, create temp buffers for lowercase operation + char* srcBuf = new char[ srcLen + 1 ]; + char* targetBuf = new char[ targetLen + 1 ]; + + // copy src and target into buffers + dStrcpy( srcBuf, str ); + dStrcpy( targetBuf, prefix ); + + // reassign src/target pointers to lowercase versions + str = dStrlwr( srcBuf ); + prefix = dStrlwr( targetBuf ); + + // do the comparison + bool startsWith = dStrncmp( str, prefix, targetLen ) == 0; + + // delete temp buffers + delete [] srcBuf; + delete [] targetBuf; + + return startsWith; +} + +//---------------------------------------------------------------- + +DefineConsoleFunction( endsWith, bool, ( const char* str, const char* suffix, bool caseSensitive ), ( false ), + "@brief Test whether the given string ends with the given suffix.\n\n" + "@param str The string to test.\n" + "@param suffix The potential suffix of @a str.\n" + "@param caseSensitive If true, the comparison will be case-sensitive; if false, differences in casing will " + "not be taken into account.\n" + "@return True if the last characters in @a str match the complete contents of @a suffix; false otherwise.\n\n" + "@tsexample\n" + "startsWith( \"TEST123\", \"123\" ) // Returns true.\n" + "@endtsexample\n\n" + "@see startsWith\n" + "@ingroup Strings" ) +{ + // if the target string is empty, return true (all strings end with the empty string) + S32 srcLen = dStrlen( str ); + S32 targetLen = dStrlen( suffix ); + if (targetLen == 0) + return true; + // else if the src string is empty, return false (empty src does not end with non-empty target) + else if (srcLen == 0) + return false; + else if( targetLen > srcLen ) + return false; + + if( caseSensitive ) + return ( dStrcmp( &str[ srcLen - targetLen ], suffix ) == 0 ); + + // both src and target are non empty, create temp buffers for lowercase operation + char* srcBuf = new char[ srcLen + 1 ]; + char* targetBuf = new char[ targetLen + 1 ]; + + // copy src and target into buffers + dStrcpy( srcBuf, str ); + dStrcpy( targetBuf, suffix ); + + // reassign src/target pointers to lowercase versions + str = dStrlwr( srcBuf ); + suffix = dStrlwr( targetBuf ); + + // set the src pointer to the appropriate place to check the end of the string + str += srcLen - targetLen; + + // do the comparison + bool endsWith = dStrcmp( str, suffix ) == 0; + + // delete temp buffers + delete [] srcBuf; + delete [] targetBuf; + + return endsWith; +} + +//---------------------------------------------------------------- + +DefineConsoleFunction( strchrpos, S32, ( const char* str, const char* chr, S32 start ), ( 0 ), + "Find the first occurrence of the given character in the given string.\n" + "@param str The string to search.\n" + "@param chr The character to look for. Only the first character of this string will be searched for.\n" + "@param start The index into @a str at which to start searching for the given character.\n" + "@return The index of the first occurrence of @a chr in @a str or -1 if @a str does not contain the given character.\n\n" + "@tsexample\n" + "strchrpos( \"test\", \"s\" ) // Returns 2.\n" + "@endtsexample\n" + "@ingroup Strings" ) +{ + if( start != 0 && start >= dStrlen( str ) ) + return -1; + + const char* ret = dStrchr( &str[ start ], chr[ 0 ] ); + return ret ? ret - str : -1; +} + +//---------------------------------------------------------------- + +DefineConsoleFunction( strrchrpos, S32, ( const char* str, const char* chr, S32 start ), ( 0 ), + "Find the last occurrence of the given character in the given string.\n" + "@param str The string to search.\n" + "@param chr The character to look for. Only the first character of this string will be searched for.\n" + "@param start The index into @a str at which to start searching for the given character.\n" + "@return The index of the last occurrence of @a chr in @a str or -1 if @a str does not contain the given character.\n\n" + "@tsexample\n" + "strrchrpos( \"test\", \"t\" ) // Returns 3.\n" + "@endtsexample\n" + "@ingroup Strings" ) +{ + if( start != 0 && start >= dStrlen( str ) ) + return -1; + + const char* ret = dStrrchr( str, chr[ 0 ] ); + if( !ret ) + return -1; + + S32 index = ret - str; + if( index < start ) + return -1; + + return index; +} + +//============================================================================= +// Field Manipulators. +//============================================================================= +// MARK: ---- Field Manipulators ---- + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( getWord, const char*, ( const char* text, S32 index ),, + "Extract the word at the given @a index in the whitespace-separated list in @a text.\n" + "Words in @a text must be separated by newlines, spaces, and/or tabs.\n" + "@param text A whitespace-separated list of words.\n" + "@param index The zero-based index of the word to extract.\n" + "@return The word at the given index or \"\" if the index is out of range.\n\n" + "@tsexample\n" + "getWord( \"a b c\", 1 ) // Returns \"b\"\n" + "@endtsexample\n\n" + "@see getWords\n" + "@see getWordCount\n" + "@see getField\n" + "@see getRecord\n" + "@ingroup FieldManip" ) +{ + return Con::getReturnBuffer( StringUnit::getUnit( text, index, " \t\n") ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( getWords, const char*, ( const char* text, S32 startIndex, S32 endIndex ), ( -1 ), + "Extract a range of words from the given @a startIndex onwards thru @a endIndex.\n" + "Words in @a text must be separated by newlines, spaces, and/or tabs.\n" + "@param text A whitespace-separated list of words.\n" + "@param startIndex The zero-based index of the first word to extract from @a text.\n" + "@param endIndex The zero-based index of the last word to extract from @a text. If this is -1, all words beginning " + "with @a startIndex are extracted from @a text.\n" + "@return A string containing the specified range of words from @a text or \"\" if @a startIndex " + "is out of range or greater than @a endIndex.\n\n" + "@tsexample\n" + "getWords( \"a b c d\", 1, 2, ) // Returns \"b c\"\n" + "@endtsexample\n\n" + "@see getWord\n" + "@see getWordCount\n" + "@see getFields\n" + "@see getRecords\n" + "@ingroup FieldManip" ) +{ + if( endIndex < 0 ) + endIndex = 1000000; + + return Con::getReturnBuffer( StringUnit::getUnits( text, startIndex, endIndex, " \t\n" ) ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( setWord, const char*, ( const char* text, S32 index, const char* replacement ),, + "Replace the word in @a text at the given @a index with @a replacement.\n" + "Words in @a text must be separated by newlines, spaces, and/or tabs.\n" + "@param text A whitespace-separated list of words.\n" + "@param index The zero-based index of the word to replace.\n" + "@param replacement The string with which to replace the word.\n" + "@return A new string with the word at the given @a index replaced by @a replacement or the original " + "string if @a index is out of range.\n\n" + "@tsexample\n" + "setWord( \"a b c d\", 2, \"f\" ) // Returns \"a b f d\"\n" + "@endtsexample\n\n" + "@see getWord\n" + "@see setField\n" + "@see setRecord\n" + "@ingroup FieldManip" ) +{ + return Con::getReturnBuffer( StringUnit::setUnit( text, index, replacement, " \t\n") ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( removeWord, const char*, ( const char* text, S32 index ),, + "Remove the word in @a text at the given @a index.\n" + "Words in @a text must be separated by newlines, spaces, and/or tabs.\n" + "@param text A whitespace-separated list of words.\n" + "@param index The zero-based index of the word in @a text.\n" + "@return A new string with the word at the given index removed or the original string if @a index is " + "out of range.\n\n" + "@tsexample\n" + "removeWord( \"a b c d\", 2 ) // Returns \"a b d\"\n" + "@endtsexample\n\n" + "@see removeField\n" + "@see removeRecord\n" + "@ingroup FieldManip" ) +{ + return Con::getReturnBuffer( StringUnit::removeUnit( text, index, " \t\n" ) ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( getWordCount, S32, ( const char* text ),, + "Return the number of whitespace-separated words in @a text.\n" + "Words in @a text must be separated by newlines, spaces, and/or tabs.\n" + "@param text A whitespace-separated list of words.\n" + "@return The number of whitespace-separated words in @a text.\n\n" + "@tsexample\n" + "getWordCount( \"a b c d e\" ) // Returns 5\n" + "@endtsexample\n\n" + "@see getFieldCount\n" + "@see getRecordCount\n" + "@ingroup FieldManip" ) +{ + return StringUnit::getUnitCount( text, " \t\n" ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( getField, const char*, ( const char* text, S32 index ),, + "Extract the field at the given @a index in the newline and/or tab separated list in @a text.\n" + "Fields in @a text must be separated by newlines and/or tabs.\n" + "@param text A list of fields separated by newlines and/or tabs.\n" + "@param index The zero-based index of the field to extract.\n" + "@return The field at the given index or \"\" if the index is out of range.\n\n" + "@tsexample\n" + "getField( \"a b\" TAB \"c d\" TAB \"e f\", 1 ) // Returns \"c d\"\n" + "@endtsexample\n\n" + "@see getFields\n" + "@see getFieldCount\n" + "@see getWord\n" + "@see getRecord\n" + "@ingroup FieldManip" ) +{ + return Con::getReturnBuffer( StringUnit::getUnit( text, index, "\t\n" ) ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( getFields, const char*, ( const char* text, S32 startIndex, S32 endIndex ), ( -1 ), + "Extract a range of fields from the given @a startIndex onwards thru @a endIndex.\n" + "Fields in @a text must be separated by newlines and/or tabs.\n" + "@param text A list of fields separated by newlines and/or tabs.\n" + "@param startIndex The zero-based index of the first field to extract from @a text.\n" + "@param endIndex The zero-based index of the last field to extract from @a text. If this is -1, all fields beginning " + "with @a startIndex are extracted from @a text.\n" + "@return A string containing the specified range of fields from @a text or \"\" if @a startIndex " + "is out of range or greater than @a endIndex.\n\n" + "@tsexample\n" + "getFields( \"a b\" TAB \"c d\" TAB \"e f\", 1 ) // Returns \"c d\" TAB \"e f\"\n" + "@endtsexample\n\n" + "@see getField\n" + "@see getFieldCount\n" + "@see getWords\n" + "@see getRecords\n" + "@ingroup FieldManip" ) +{ + if( endIndex < 0 ) + endIndex = 1000000; + + return Con::getReturnBuffer( StringUnit::getUnits( text, startIndex, endIndex, "\t\n" ) ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( setField, const char*, ( const char* text, S32 index, const char* replacement ),, + "Replace the field in @a text at the given @a index with @a replacement.\n" + "Fields in @a text must be separated by newlines and/or tabs.\n" + "@param text A list of fields separated by newlines and/or tabs.\n" + "@param index The zero-based index of the field to replace.\n" + "@param replacement The string with which to replace the field.\n" + "@return A new string with the field at the given @a index replaced by @a replacement or the original " + "string if @a index is out of range.\n\n" + "@tsexample\n" + "setField( \"a b\" TAB \"c d\" TAB \"e f\", 1, \"g h\" ) // Returns \"a b\" TAB \"g h\" TAB \"e f\"\n" + "@endtsexample\n\n" + "@see getField\n" + "@see setWord\n" + "@see setRecord\n" + "@ingroup FieldManip" ) +{ + return Con::getReturnBuffer( StringUnit::setUnit( text, index, replacement, "\t\n" ) ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( removeField, const char*, ( const char* text, S32 index ),, + "Remove the field in @a text at the given @a index.\n" + "Fields in @a text must be separated by newlines and/or tabs.\n" + "@param text A list of fields separated by newlines and/or tabs.\n" + "@param index The zero-based index of the field in @a text.\n" + "@return A new string with the field at the given index removed or the original string if @a index is " + "out of range.\n\n" + "@tsexample\n" + "removeField( \"a b\" TAB \"c d\" TAB \"e f\", 1 ) // Returns \"a b\" TAB \"e f\"\n" + "@endtsexample\n\n" + "@see removeWord\n" + "@see removeRecord\n" + "@ingroup FieldManip" ) +{ + return Con::getReturnBuffer( StringUnit::removeUnit( text, index, "\t\n" ) ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( getFieldCount, S32, ( const char* text ),, + "Return the number of newline and/or tab separated fields in @a text.\n" + "@param text A list of fields separated by newlines and/or tabs.\n" + "@return The number of newline and/or tab sepearated elements in @a text.\n\n" + "@tsexample\n" + "getFieldCount( \"a b\" TAB \"c d\" TAB \"e f\" ) // Returns 3\n" + "@endtsexample\n\n" + "@see getWordCount\n" + "@see getRecordCount\n" + "@ingroup FieldManip" ) +{ + return StringUnit::getUnitCount( text, "\t\n" ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( getRecord, const char*, ( const char* text, S32 index ),, + "Extract the record at the given @a index in the newline-separated list in @a text.\n" + "Records in @a text must be separated by newlines.\n" + "@param text A list of records separated by newlines.\n" + "@param index The zero-based index of the record to extract.\n" + "@return The record at the given index or \"\" if @a index is out of range.\n\n" + "@tsexample\n" + "getRecord( \"a b\" NL \"c d\" NL \"e f\", 1 ) // Returns \"c d\"\n" + "@endtsexample\n\n" + "@see getRecords\n" + "@see getRecordCount\n" + "@see getWord\n" + "@see getField\n" + "@ingroup FieldManip" ) +{ + return Con::getReturnBuffer( StringUnit::getUnit( text, index, "\n" ) ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( getRecords, const char*, ( const char* text, S32 startIndex, S32 endIndex ), ( -1 ), + "Extract a range of records from the given @a startIndex onwards thru @a endIndex.\n" + "Records in @a text must be separated by newlines.\n" + "@param text A list of records separated by newlines.\n" + "@param startIndex The zero-based index of the first record to extract from @a text.\n" + "@param endIndex The zero-based index of the last record to extract from @a text. If this is -1, all records beginning " + "with @a startIndex are extracted from @a text.\n" + "@return A string containing the specified range of records from @a text or \"\" if @a startIndex " + "is out of range or greater than @a endIndex.\n\n" + "@tsexample\n" + "getRecords( \"a b\" NL \"c d\" NL \"e f\", 1 ) // Returns \"c d\" NL \"e f\"\n" + "@endtsexample\n\n" + "@see getRecord\n" + "@see getRecordCount\n" + "@see getWords\n" + "@see getFields\n" + "@ingroup FieldManip" ) +{ + if( endIndex < 0 ) + endIndex = 1000000; + + return Con::getReturnBuffer( StringUnit::getUnits( text, startIndex, endIndex, "\n" ) ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( setRecord, const char*, ( const char* text, S32 index, const char* replacement ),, + "Replace the record in @a text at the given @a index with @a replacement.\n" + "Records in @a text must be separated by newlines.\n" + "@param text A list of records separated by newlines.\n" + "@param index The zero-based index of the record to replace.\n" + "@param replacement The string with which to replace the record.\n" + "@return A new string with the record at the given @a index replaced by @a replacement or the original " + "string if @a index is out of range.\n\n" + "@tsexample\n" + "setRecord( \"a b\" NL \"c d\" NL \"e f\", 1, \"g h\" ) // Returns \"a b\" NL \"g h\" NL \"e f\"\n" + "@endtsexample\n\n" + "@see getRecord\n" + "@see setWord\n" + "@see setField\n" + "@ingroup FieldManip" ) +{ + return Con::getReturnBuffer( StringUnit::setUnit( text, index, replacement, "\n" ) ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( removeRecord, const char*, ( const char* text, S32 index ),, + "Remove the record in @a text at the given @a index.\n" + "Records in @a text must be separated by newlines.\n" + "@param text A list of records separated by newlines.\n" + "@param index The zero-based index of the record in @a text.\n" + "@return A new string with the record at the given @a index removed or the original string if @a index is " + "out of range.\n\n" + "@tsexample\n" + "removeRecord( \"a b\" NL \"c d\" NL \"e f\", 1 ) // Returns \"a b\" NL \"e f\"\n" + "@endtsexample\n\n" + "@see removeWord\n" + "@see removeField\n" + "@ingroup FieldManip" ) +{ + return Con::getReturnBuffer( StringUnit::removeUnit( text, index, "\n" ) ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( getRecordCount, S32, ( const char* text ),, + "Return the number of newline-separated records in @a text.\n" + "@param text A list of records separated by newlines.\n" + "@return The number of newline-sepearated elements in @a text.\n\n" + "@tsexample\n" + "getRecordCount( \"a b\" NL \"c d\" NL \"e f\" ) // Returns 3\n" + "@endtsexample\n\n" + "@see getWordCount\n" + "@see getFieldCount\n" + "@ingroup FieldManip" ) +{ + return StringUnit::getUnitCount( text, "\n" ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( firstWord, const char*, ( const char* text ),, + "Return the first word in @a text.\n" + "@param text A list of words separated by newlines, spaces, and/or tabs.\n" + "@return The word at index 0 in @a text or \"\" if @a text is empty.\n\n" + "@note This is equal to \n" + "@tsexample_nopar\n" + "getWord( text, 0 )\n" + "@endtsexample\n\n" + "@see getWord\n" + "@ingroup FieldManip" ) +{ + return Con::getReturnBuffer( StringUnit::getUnit( text, 0, " \t\n" ) ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( restWords, const char*, ( const char* text ),, + "Return all but the first word in @a text.\n" + "@param text A list of words separated by newlines, spaces, and/or tabs.\n" + "@return @a text with the first word removed.\n\n" + "@note This is equal to \n" + "@tsexample_nopar\n" + "getWords( text, 1 )\n" + "@endtsexample\n\n" + "@see getWords\n" + "@ingroup FieldManip" ) +{ + const char* ptr = text; + while( *ptr && *ptr != ' ' && *ptr != '\t' && *ptr != '\n' ) + ptr ++; + + // Skip separator. + if( *ptr ) + ptr ++; + + return Con::getReturnBuffer( ptr ); +} + +//----------------------------------------------------------------------------- + +static bool isInSet(char c, const char *set) +{ + if (set) + while (*set) + if (c == *set++) + return true; + + return false; +} + +ConsoleFunction( nextToken, const char *, 4, 4, "( string str, string token, string delimiters ) " + "Tokenize a string using a set of delimiting characters.\n" + "This function first skips all leading charaters in @a str that are contained in @a delimiters. " + "From that position, it then scans for the next character in @a str that is contained in @a delimiters and stores all characters " + "from the starting position up to the first delimiter in a variable in the current scope called @a token. Finally, it " + "skips all characters in @a delimiters after the token and then returns the remaining string contents in @a str.\n\n" + "To scan out all tokens in a string, call this function repeatedly by passing the result it returns each time as the new @a str " + "until the function returns \"\".\n\n" + "@param str A string.\n" + "@param token The name of the variable in which to store the current token. This variable is set in the " + "scope in which nextToken is called.\n" + "@param delimiters A string of characters. Each character is considered a delimiter.\n" + "@return The remainder of @a str after the token has been parsed out or \"\" if no more tokens were found in @a str.\n\n" + "@tsexample\n" + "// Prints:\n" + "// a\n" + "// b\n" + "// c\n" + "%str = \"a b c\";\n" + "while ( %str !$= \"\" )\n" + "{\n" + " // First time, stores \"a\" in the variable %token and sets %str to \"b c\".\n" + " %str = nextToken( %str, \"token\", \" \" );\n" + " echo( %token );\n" + "}\n" + "@endtsexample\n\n" + "@ingroup Strings" ) +{ + char *str = (char *) argv[1]; + const char *token = argv[2]; + const char *delim = argv[3]; + + if( str ) + { + // skip over any characters that are a member of delim + // no need for special '\0' check since it can never be in delim + while (isInSet(*str, delim)) + str++; + + // skip over any characters that are NOT a member of delim + const char *tmp = str; + + while (*str && !isInSet(*str, delim)) + str++; + + // terminate the token + if (*str) + *str++ = 0; + + // set local variable if inside a function + if (gEvalState.getStackDepth() > 0 && + gEvalState.getCurrentFrame().scopeName) + Con::setLocalVariable(token,tmp); + else + Con::setVariable(token,tmp); + + // advance str past the 'delim space' + while (isInSet(*str, delim)) + str++; + } + + return str; +} + +//============================================================================= +// Tagged Strings. +//============================================================================= +// MARK: ---- Tagged Strings ---- + +//----------------------------------------------------------------------------- + +DefineEngineFunction( detag, const char*, ( const char* str ),, + "@brief Returns the string from a tag string.\n\n" + + "Should only be used within the context of a function that receives a tagged " + "string, and is not meant to be used outside of this context. Use getTaggedString() " + "to convert a tagged string ID back into a regular string at any time.\n\n" + + "@tsexample\n" + "// From scripts/client/message.cs\n" + "function clientCmdChatMessage(%sender, %voice, %pitch, %msgString, %a1, %a2, %a3, %a4, %a5, %a6, %a7, %a8, %a9, %a10)\n" + "{\n" + " onChatMessage(detag(%msgString), %voice, %pitch);\n" + "}\n" + "@endtsexample\n\n" + + "@see \\ref syntaxDataTypes under Tagged %Strings\n" + "@see getTag()\n" + "@see getTaggedString()\n" + + "@ingroup Networking") +{ + if( str[ 0 ] == StringTagPrefixByte ) + { + const char* word = dStrchr( str, ' ' ); + if( word == NULL ) + return ""; + + char* ret = Con::getReturnBuffer( dStrlen( word + 1 ) + 1 ); + dStrcpy( ret, word + 1 ); + return ret; + } + else + return str; +} + +ConsoleFunction(getTag, const char *, 2, 2, "(string textTagString)" + "@brief Extracts the tag from a tagged string\n\n" + + "Should only be used within the context of a function that receives a tagged " + "string, and is not meant to be used outside of this context.\n\n" + + "@param textTagString The tagged string to extract.\n" + + "@returns The tag ID of the string.\n" + + "@see \\ref syntaxDataTypes under Tagged %Strings\n" + "@see detag()\n" + "@ingroup Networking") +{ + TORQUE_UNUSED(argc); + if(argv[1][0] == StringTagPrefixByte) + { + const char * space = dStrchr(argv[1], ' '); + + U32 len; + if(space) + len = space - argv[1]; + else + len = dStrlen(argv[1]) + 1; + + char * ret = Con::getReturnBuffer(len); + dStrncpy(ret, argv[1] + 1, len - 1); + ret[len - 1] = 0; + + return(ret); + } + else + return(argv[1]); +} + + +//============================================================================= +// Output. +//============================================================================= +// MARK: ---- Output ---- + +//----------------------------------------------------------------------------- + +ConsoleFunction( echo, void, 2, 0, "( string message... ) " + "@brief Logs a message to the console.\n\n" + "Concatenates all given arguments to a single string and prints the string to the console. " + "A newline is added automatically after the text.\n\n" + "@param message Any number of string arguments.\n\n" + "@ingroup Logging" ) +{ + U32 len = 0; + S32 i; + for(i = 1; i < argc; i++) + len += dStrlen(argv[i]); + + char *ret = Con::getReturnBuffer(len + 1); + ret[0] = 0; + for(i = 1; i < argc; i++) + dStrcat(ret, argv[i]); + + Con::printf("%s", ret); + ret[0] = 0; +} + +//----------------------------------------------------------------------------- + +ConsoleFunction( warn, void, 2, 0, "( string message... ) " + "@brief Logs a warning message to the console.\n\n" + "Concatenates all given arguments to a single string and prints the string to the console as a warning " + "message (in the in-game console, these will show up using a turquoise font by default). " + "A newline is added automatically after the text.\n\n" + "@param message Any number of string arguments.\n\n" + "@ingroup Logging" ) +{ + U32 len = 0; + S32 i; + for(i = 1; i < argc; i++) + len += dStrlen(argv[i]); + + char *ret = Con::getReturnBuffer(len + 1); + ret[0] = 0; + for(i = 1; i < argc; i++) + dStrcat(ret, argv[i]); + + Con::warnf(ConsoleLogEntry::General, "%s", ret); + ret[0] = 0; +} + +//----------------------------------------------------------------------------- + +ConsoleFunction( error, void, 2, 0, "( string message... ) " + "@brief Logs an error message to the console.\n\n" + "Concatenates all given arguments to a single string and prints the string to the console as an error " + "message (in the in-game console, these will show up using a red font by default). " + "A newline is added automatically after the text.\n\n" + "@param message Any number of string arguments.\n\n" + "@ingroup Logging" ) +{ + U32 len = 0; + S32 i; + for(i = 1; i < argc; i++) + len += dStrlen(argv[i]); + + char *ret = Con::getReturnBuffer(len + 1); + ret[0] = 0; + for(i = 1; i < argc; i++) + dStrcat(ret, argv[i]); + + Con::errorf(ConsoleLogEntry::General, "%s", ret); + ret[0] = 0; +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( debugv, void, ( const char* variableName ),, + "@brief Logs the value of the given variable to the console.\n\n" + "Prints a string of the form \" = \" to the console.\n\n" + "@param variableName Name of the local or global variable to print.\n\n" + "@tsexample\n" + "%var = 1;\n" + "debugv( \"%var\" ); // Prints \"%var = 1\"\n" + "@endtsexample\n\n" + "@ingroup Debugging" ) +{ + if( variableName[ 0 ] == '%' ) + Con::errorf( "%s = %s", variableName, Con::getLocalVariable( variableName ) ); + else + Con::errorf( "%s = %s", variableName, Con::getVariable( variableName ) ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( expandEscape, const char*, ( const char* text ),, + "@brief Replace all characters in @a text that need to be escaped for the string to be a valid string literal with their " + "respective escape sequences.\n\n" + "All characters in @a text that cannot appear in a string literal will be replaced by an escape sequence (\\\\n, \\\\t, etc).\n\n" + "The primary use of this function is for converting strings suitable for being passed as string literals " + "to the TorqueScript compiler.\n\n" + "@param text A string\n" + "@return A duplicate of the text parameter with all unescaped characters that cannot appear in string literals replaced by their respective " + "escape sequences.\n\n" + "@tsxample\n" + "expandEscape( \"str\" NL \"ing\" ) // Returns \"str\\ning\".\n" + "@endtsxample\n\n" + "@see collapseEscape\n" + "@ingroup Strings") +{ + char* ret = Con::getReturnBuffer(dStrlen( text ) * 2 + 1 ); // worst case situation + expandEscape( ret, text ); + return ret; +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( collapseEscape, const char*, ( const char* text ),, + "Replace all escape sequences in @a text with their respective character codes.\n\n" + "This function replaces all escape sequences (\\\\n, \\\\t, etc) in the given string " + "with the respective characters they represent.\n\n" + "The primary use of this function is for converting strings from their literal form into " + "their compiled/translated form, as is normally done by the TorqueScript compiler.\n\n" + "@param text A string.\n" + "@return A duplicate of @a text with all escape sequences replaced by their respective character codes.\n\n" + "@tsexample\n" + "// Print:\n" + "//\n" + "// str\n" + "// ing\n" + "//\n" + "// to the console. Note how the backslash in the string must be escaped here\n" + "// in order to prevent the TorqueScript compiler from collapsing the escape\n" + "// sequence in the resulting string.\n" + "echo( collapseEscape( \"str\\ning\" ) );\n" + "@endtsexample\n\n" + "@see expandEscape\n\n" + "@ingroup Strings" ) +{ + char* ret = Con::getReturnBuffer( text ); + collapseEscape( ret ); + return ret; +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( setLogMode, void, ( S32 mode ),, + "@brief Determines how log files are written.\n\n" + "Sets the operational mode of the console logging system.\n\n" + "@param mode Parameter specifying the logging mode. This can be:\n" + "- 1: Open and close the console log file for each seperate string of output. This will ensure that all " + "parts get written out to disk and that no parts remain in intermediate buffers even if the process crashes.\n" + "- 2: Keep the log file open and write to it continuously. This will make the system operate faster but " + "if the process crashes, parts of the output may not have been written to disk yet and will be missing from " + "the log.\n\n" + + "Additionally, when changing the log mode and thus opening a new log file, either of the two mode values may be " + "combined by binary OR with 0x4 to cause the logging system to flush all console log messages that had already been " + "issued to the console system into the newly created log file.\n\n" + + "@note Xbox 360 does not support logging to a file. Use Platform::OutputDebugStr in C++ instead." + "@ingroup Logging" ) +{ + Con::setLogMode( mode ); +} + +//============================================================================= +// Misc. +//============================================================================= +// MARK: ---- Misc ---- + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( quit, void, ( ),, + "Shut down the engine and exit its process.\n" + "This function cleanly uninitializes the engine and then exits back to the system with a process " + "exit status indicating a clean exit.\n\n" + "@see quitWithErrorMessage\n\n" + "@ingroup Platform" ) +{ + Platform::postQuitMessage(0); +} + +//----------------------------------------------------------------------------- + +#ifdef TORQUE_DEMO_PURCHASE +ConsoleFunction( realQuit, void, 1, 1, "" ) +{ + TORQUE_UNUSED(argc); TORQUE_UNUSED(argv); + Platform::postQuitMessage(0); +} +#endif + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( quitWithErrorMessage, void, ( const char* message ),, + "Display an error message box showing the given @a message and then shut down the engine and exit its process.\n" + "This function cleanly uninitialized the engine and then exits back to the system with a process " + "exit status indicating an error.\n\n" + "@param message The message to log to the console and show in an error message box.\n\n" + "@see quit\n\n" + "@ingroup Platform" ) +{ + Con::errorf( message ); + Platform::AlertOK( "Error", message ); + + // [rene 03/30/10] This was previously using forceShutdown which is a bad thing + // as the script code should not be allowed to pretty much hard-crash the engine + // and prevent proper shutdown. Changed this to use postQuitMessage. + + Platform::postQuitMessage( -1 ); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( gotoWebPage, void, ( const char* address ),, + "Open the given URL or file in the user's web browser.\n\n" + "@param address The address to open. If this is not prefixed by a protocol specifier (\"...://\"), then " + "the function checks whether the address refers to a file or directory and if so, prepends \"file://\" " + "to @a adress; if the file check fails, \"http://\" is prepended to @a address.\n\n" + "@tsexample\n" + "gotoWebPage( \"http://www.garagegames.com\" );\n" + "@endtsexample\n\n" + "@ingroup Platform" ) +{ + // If there's a protocol prefix in the address, just invoke + // the browser on the given address. + + char* protocolSep = dStrstr( address,"://"); + if( protocolSep != NULL ) + { + Platform::openWebBrowser( address ); + return; + } + + // If we don't see a protocol seperator, then we know that some bullethead + // sent us a bad url. We'll first check to see if a file inside the sandbox + // with that name exists, then we'll just glom "http://" onto the front of + // the bogus url, and hope for the best. + + String addr; + if( Platform::isFile( address ) || Platform::isDirectory( address ) ) + { +#ifdef TORQUE2D_TOOLS_FIXME + addr = String::ToString( "file://%s", address ); +#else + addr = String::ToString( "file://%s/%s", Platform::getCurrentDirectory(), address ); +#endif + } + else + addr = String::ToString( "http://%s", address ); + + Platform::openWebBrowser( addr ); + return; +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( displaySplashWindow, bool, (),, + "Display a startup splash window suitable for showing while the engine still starts up.\n\n" + "@note This is currently only implemented on Windows.\n\n" + "@return True if the splash window could be successfully initialized.\n\n" + "@ingroup Platform" ) +{ + return Platform::displaySplashWindow(); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( getWebDeployment, bool, (),, + "Test whether Torque is running in web-deployment mode.\n" + "In this mode, Torque will usually run within a browser and certain restrictions apply (e.g. Torque will not " + "be able to enter fullscreen exclusive mode).\n" + "@return True if Torque is running in web-deployment mode.\n" + "@ingroup Platform" ) +{ + return Platform::getWebDeployment(); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( countBits, S32, ( S32 v ),, + "Count the number of bits that are set in the given 32 bit integer.\n" + "@param v An integer value.\n\n" + "@return The number of bits that are set in @a v.\n\n" + "@ingroup Utilities" ) +{ + S32 c = 0; + + // from + // http://graphics.stanford.edu/~seander/bithacks.html + + // for at most 32-bit values in v: + c = ((v & 0xfff) * 0x1001001001001ULL & 0x84210842108421ULL) % 0x1f; + c += (((v & 0xfff000) >> 12) * 0x1001001001001ULL & 0x84210842108421ULL) % + 0x1f; + c += ((v >> 24) * 0x1001001001001ULL & 0x84210842108421ULL) % 0x1f; + +#ifndef TORQUE_SHIPPING + // since the above isn't very obvious, for debugging compute the count in a more + // traditional way and assert if it is different + { + S32 c2 = 0; + S32 v2 = v; + for (c2 = 0; v2; v2 >>= 1) + { + c2 += v2 & 1; + } + if (c2 != c) + Con::errorf("countBits: Uh oh bit count mismatch"); + AssertFatal(c2 == c, "countBits: uh oh, bit count mismatch"); + } +#endif + + return c; +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( generateUUID, Torque::UUID, (),, + "Generate a new universally unique identifier (UUID).\n\n" + "@return A newly generated UUID.\n\n" + "@ingroup Utilities" ) +{ + Torque::UUID uuid; + + uuid.generate(); + return uuid; +} + +//============================================================================= +// Meta Scripting. +//============================================================================= +// MARK: ---- Meta Scripting ---- + +//----------------------------------------------------------------------------- + +ConsoleFunction( call, const char *, 2, 0, "( string functionName, string args... ) " + "Apply the given arguments to the specified global function and return the result of the call.\n\n" + "@param functionName The name of the function to call. This function must be in the global namespace, i.e. " + "you cannot call a function in a namespace through #call. Use eval() for that.\n" + "@return The result of the function call.\n\n" + "@tsexample\n" + "function myFunction( %arg )\n" + "{\n" + " return ( %arg SPC \"World!\" );\n" + "}\n" + "\n" + "echo( call( \"myFunction\", \"Hello\" ) ); // Prints \"Hello World!\" to the console.\n" + "@endtsexample\n\n" + "@ingroup Scripting" ) +{ + return Con::execute( argc - 1, argv + 1 ); +} + +//----------------------------------------------------------------------------- + +static U32 execDepth = 0; +static U32 journalDepth = 1; + +static StringTableEntry getDSOPath(const char *scriptPath) +{ +#ifndef TORQUE2D_TOOLS_FIXME + + // [tom, 11/17/2006] Force old behavior for the player. May not want to do this. + const char *slash = dStrrchr(scriptPath, '/'); + if(slash != NULL) + return StringTable->insertn(scriptPath, slash - scriptPath, true); + + slash = dStrrchr(scriptPath, ':'); + if(slash != NULL) + return StringTable->insertn(scriptPath, (slash - scriptPath) + 1, true); + + return ""; + +#else + + char relPath[1024], dsoPath[1024]; + bool isPrefs = false; + + // [tom, 11/17/2006] Prefs are handled slightly differently to avoid dso name clashes + StringTableEntry prefsPath = Platform::getPrefsPath(); + if(dStrnicmp(scriptPath, prefsPath, dStrlen(prefsPath)) == 0) + { + relPath[0] = 0; + isPrefs = true; + } + else + { + StringTableEntry strippedPath = Platform::stripBasePath(scriptPath); + dStrcpy(relPath, strippedPath); + + char *slash = dStrrchr(relPath, '/'); + if(slash) + *slash = 0; + } + + const char *overridePath; + if(! isPrefs) + overridePath = Con::getVariable("$Scripts::OverrideDSOPath"); + else + overridePath = prefsPath; + + if(overridePath && *overridePath) + Platform::makeFullPathName(relPath, dsoPath, sizeof(dsoPath), overridePath); + else + { + char t[1024]; + dSprintf(t, sizeof(t), "compiledScripts/%s", relPath); + Platform::makeFullPathName(t, dsoPath, sizeof(dsoPath), Platform::getPrefsPath()); + } + + return StringTable->insert(dsoPath); + +#endif +} + +DefineConsoleFunction( getDSOPath, const char*, ( const char* scriptFileName ),, + "Get the absolute path to the file in which the compiled code for the given script file will be stored.\n" + "@param scriptFileName %Path to the .cs script file.\n" + "@return The absolute path to the .dso file for the given script file.\n\n" + "@note The compiler will store newly compiled DSOs in the prefs path but pre-existing DSOs will be loaded " + "from the current paths.\n\n" + "@see compile\n" + "@see getPrefsPath\n" + "@ingroup Scripting" ) +{ + Con::expandScriptFilename( scriptFilenameBuffer, sizeof(scriptFilenameBuffer), scriptFileName ); + + const char* filename = getDSOPath(scriptFilenameBuffer); + if(filename == NULL || *filename == 0) + return ""; + + return filename; +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( compile, bool, ( const char* fileName, bool overrideNoDSO ), ( false ), + "Compile a file to bytecode.\n\n" + "This function will read the TorqueScript code in the specified file, compile it to internal bytecode, and, " + "if DSO generation is enabled or @a overrideNoDDSO is true, will store the compiled code in a .dso file " + "in the current DSO path mirrorring the path of @a fileName.\n\n" + "@param fileName Path to the file to compile to bytecode.\n" + "@param overrideNoDSO If true, force generation of DSOs even if the engine is compiled to not " + "generate write compiled code to DSO files.\n\n" + "@return True if the file was successfully compiled, false if not.\n\n" + "@note The definitions contained in the given file will not be made available and no code will actually " + "be executed. Use exec() for that.\n\n" + "@see getDSOPath\n" + "@see exec\n" + "@ingroup Scripting" ) +{ + Con::expandScriptFilename( scriptFilenameBuffer, sizeof( scriptFilenameBuffer ), fileName ); + + // Figure out where to put DSOs + StringTableEntry dsoPath = getDSOPath(scriptFilenameBuffer); + if(dsoPath && *dsoPath == 0) + return false; + + // If the script file extention is '.ed.cs' then compile it to a different compiled extention + bool isEditorScript = false; + const char *ext = dStrrchr( scriptFilenameBuffer, '.' ); + if( ext && ( dStricmp( ext, ".cs" ) == 0 ) ) + { + const char* ext2 = ext - 3; + if( dStricmp( ext2, ".ed.cs" ) == 0 ) + isEditorScript = true; + } + else if( ext && ( dStricmp( ext, ".gui" ) == 0 ) ) + { + const char* ext2 = ext - 3; + if( dStricmp( ext2, ".ed.gui" ) == 0 ) + isEditorScript = true; + } + + const char *filenameOnly = dStrrchr(scriptFilenameBuffer, '/'); + if(filenameOnly) + ++filenameOnly; + else + filenameOnly = scriptFilenameBuffer; + + char nameBuffer[512]; + + if( isEditorScript ) + dStrcpyl(nameBuffer, sizeof(nameBuffer), dsoPath, "/", filenameOnly, ".edso", NULL); + else + dStrcpyl(nameBuffer, sizeof(nameBuffer), dsoPath, "/", filenameOnly, ".dso", NULL); + + void *data = NULL; + U32 dataSize = 0; + Torque::FS::ReadFile(scriptFilenameBuffer, data, dataSize, true); + if(data == NULL) + { + Con::errorf(ConsoleLogEntry::Script, "compile: invalid script file %s.", scriptFilenameBuffer); + return false; + } + + const char *script = static_cast(data); + +#ifdef TORQUE_DEBUG + Con::printf("Compiling %s...", scriptFilenameBuffer); +#endif + + CodeBlock *code = new CodeBlock(); + code->compile(nameBuffer, scriptFilenameBuffer, script, overrideNoDSO); + delete code; + delete[] script; + + return true; +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( exec, bool, ( const char* fileName, bool noCalls, bool journalScript ), ( false, false ), + "Execute the given script file.\n" + "@param fileName Path to the file to execute\n" + "@param noCalls Deprecated\n" + "@param journalScript Deprecated\n" + "@return True if the script was successfully executed, false if not.\n\n" + "@tsexample\n" + "// Execute the init.cs script file found in the same directory as the current script file.\n" + "exec( \"./init.cs\" );\n" + "@endtsexample\n\n" + "@see compile\n" + "@see eval\n" + "@ingroup Scripting" ) +{ + bool journal = false; + + execDepth++; + if(journalDepth >= execDepth) + journalDepth = execDepth + 1; + else + journal = true; + + bool ret = false; + + if( journalScript && !journal ) + { + journal = true; + journalDepth = execDepth; + } + + // Determine the filename we actually want... + Con::expandScriptFilename( scriptFilenameBuffer, sizeof( scriptFilenameBuffer ), fileName ); + + // since this function expects a script file reference, if it's a .dso + // lets terminate the string before the dso so it will act like a .cs + if(dStrEndsWith(scriptFilenameBuffer, ".dso")) + { + scriptFilenameBuffer[dStrlen(scriptFilenameBuffer) - dStrlen(".dso")] = '\0'; + } + + // Figure out where to put DSOs + StringTableEntry dsoPath = getDSOPath(scriptFilenameBuffer); + + const char *ext = dStrrchr(scriptFilenameBuffer, '.'); + + if(!ext) + { + // We need an extension! + Con::errorf(ConsoleLogEntry::Script, "exec: invalid script file name %s.", scriptFilenameBuffer); + execDepth--; + return false; + } + + // Check Editor Extensions + bool isEditorScript = false; + + // If the script file extension is '.ed.cs' then compile it to a different compiled extension + if( dStricmp( ext, ".cs" ) == 0 ) + { + const char* ext2 = ext - 3; + if( dStricmp( ext2, ".ed.cs" ) == 0 ) + isEditorScript = true; + } + else if( dStricmp( ext, ".gui" ) == 0 ) + { + const char* ext2 = ext - 3; + if( dStricmp( ext2, ".ed.gui" ) == 0 ) + isEditorScript = true; + } + + + StringTableEntry scriptFileName = StringTable->insert(scriptFilenameBuffer); + +#ifndef TORQUE_OS_XENON + // Is this a file we should compile? (anything in the prefs path should not be compiled) + StringTableEntry prefsPath = Platform::getPrefsPath(); + bool compiled = dStricmp(ext, ".mis") && !journal && !Con::getBoolVariable("Scripts::ignoreDSOs"); + + // [tom, 12/5/2006] stripBasePath() fucks up if the filename is not in the exe + // path, current directory or prefs path. Thus, getDSOFilename() will also screw + // up and so this allows the scripts to still load but without a DSO. + if(Platform::isFullPath(Platform::stripBasePath(scriptFilenameBuffer))) + compiled = false; + + // [tom, 11/17/2006] It seems to make sense to not compile scripts that are in the + // prefs directory. However, getDSOPath() can handle this situation and will put + // the dso along with the script to avoid name clashes with tools/game dsos. + if( (dsoPath && *dsoPath == 0) || (prefsPath && prefsPath[ 0 ] && dStrnicmp(scriptFileName, prefsPath, dStrlen(prefsPath)) == 0) ) + compiled = false; +#else + bool compiled = false; // Don't try to compile things on the 360, ignore DSO's when debugging + // because PC prefs will screw up stuff like SFX. +#endif + + // If we're in a journaling mode, then we will read the script + // from the journal file. + if(journal && Journal::IsPlaying()) + { + char fileNameBuf[256]; + bool fileRead = false; + U32 fileSize; + + Journal::ReadString(fileNameBuf); + Journal::Read(&fileRead); + + if(!fileRead) + { + Con::errorf(ConsoleLogEntry::Script, "Journal script read (failed) for %s", fileNameBuf); + execDepth--; + return false; + } + Journal::Read(&fileSize); + char *script = new char[fileSize + 1]; + Journal::Read(fileSize, script); + script[fileSize] = 0; + Con::printf("Executing (journal-read) %s.", scriptFileName); + CodeBlock *newCodeBlock = new CodeBlock(); + newCodeBlock->compileExec(scriptFileName, script, noCalls, 0); + delete [] script; + + execDepth--; + return true; + } + + // Ok, we let's try to load and compile the script. + Torque::FS::FileNodeRef scriptFile = Torque::FS::GetFileNode(scriptFileName); + Torque::FS::FileNodeRef dsoFile; + +// ResourceObject *rScr = gResourceManager->find(scriptFileName); +// ResourceObject *rCom = NULL; + + char nameBuffer[512]; + char* script = NULL; + U32 version; + + Stream *compiledStream = NULL; + Torque::Time scriptModifiedTime, dsoModifiedTime; + + // Check here for .edso + bool edso = false; + if( dStricmp( ext, ".edso" ) == 0 && scriptFile != NULL ) + { + edso = true; + dsoFile = scriptFile; + scriptFile = NULL; + + dsoModifiedTime = dsoFile->getModifiedTime(); + dStrcpy( nameBuffer, scriptFileName ); + } + + // If we're supposed to be compiling this file, check to see if there's a DSO + if(compiled && !edso) + { + const char *filenameOnly = dStrrchr(scriptFileName, '/'); + if(filenameOnly) + ++filenameOnly; + else + filenameOnly = scriptFileName; + + char pathAndFilename[1024]; + Platform::makeFullPathName(filenameOnly, pathAndFilename, sizeof(pathAndFilename), dsoPath); + + if( isEditorScript ) + dStrcpyl(nameBuffer, sizeof(nameBuffer), pathAndFilename, ".edso", NULL); + else + dStrcpyl(nameBuffer, sizeof(nameBuffer), pathAndFilename, ".dso", NULL); + + dsoFile = Torque::FS::GetFileNode(nameBuffer); + + if(scriptFile != NULL) + scriptModifiedTime = scriptFile->getModifiedTime(); + + if(dsoFile != NULL) + dsoModifiedTime = dsoFile->getModifiedTime(); + } + + // Let's do a sanity check to complain about DSOs in the future. + // + // MM: This doesn't seem to be working correctly for now so let's just not issue + // the warning until someone knows how to resolve it. + // + //if(compiled && rCom && rScr && Platform::compareFileTimes(comModifyTime, scrModifyTime) < 0) + //{ + //Con::warnf("exec: Warning! Found a DSO from the future! (%s)", nameBuffer); + //} + + // If we had a DSO, let's check to see if we should be reading from it. + if(compiled && dsoFile != NULL && (scriptFile == NULL|| (scriptModifiedTime - dsoModifiedTime) > Torque::Time(0))) + { + compiledStream = FileStream::createAndOpen( nameBuffer, Torque::FS::File::Read ); + if (compiledStream) + { + // Check the version! + compiledStream->read(&version); + if(version != Con::DSOVersion) + { + Con::warnf("exec: Found an old DSO (%s, ver %d < %d), ignoring.", nameBuffer, version, Con::DSOVersion); + delete compiledStream; + compiledStream = NULL; + } + } + } + + // If we're journalling, let's write some info out. + if(journal && Journal::IsRecording()) + Journal::WriteString(scriptFileName); + + if(scriptFile != NULL && !compiledStream) + { + // If we have source but no compiled version, then we need to compile + // (and journal as we do so, if that's required). + + void *data; + U32 dataSize = 0; + Torque::FS::ReadFile(scriptFileName, data, dataSize, true); + + if(journal && Journal::IsRecording()) + Journal::Write(bool(data != NULL)); + + if( data == NULL ) + { + Con::errorf(ConsoleLogEntry::Script, "exec: invalid script file %s.", scriptFileName); + execDepth--; + return false; + } + else + { + if( !dataSize ) + { + execDepth --; + return false; + } + + script = (char *)data; + + if(journal && Journal::IsRecording()) + { + Journal::Write(dataSize); + Journal::Write(dataSize, data); + } + } + +#ifndef TORQUE_NO_DSO_GENERATION + if(compiled) + { + // compile this baddie. +#ifdef TORQUE_DEBUG + Con::printf("Compiling %s...", scriptFileName); +#endif + + CodeBlock *code = new CodeBlock(); + code->compile(nameBuffer, scriptFileName, script); + delete code; + code = NULL; + + compiledStream = FileStream::createAndOpen( nameBuffer, Torque::FS::File::Read ); + if(compiledStream) + { + compiledStream->read(&version); + } + else + { + // We have to exit out here, as otherwise we get double error reports. + delete [] script; + execDepth--; + return false; + } + } +#endif + } + else + { + if(journal && Journal::IsRecording()) + Journal::Write(bool(false)); + } + + if(compiledStream) + { + // Delete the script object first to limit memory used + // during recursive execs. + delete [] script; + script = 0; + + // We're all compiled, so let's run it. +#ifdef TORQUE_DEBUG + Con::printf("Loading compiled script %s.", scriptFileName); +#endif + CodeBlock *code = new CodeBlock; + code->read(scriptFileName, *compiledStream); + delete compiledStream; + code->exec(0, scriptFileName, NULL, 0, NULL, noCalls, NULL, 0); + ret = true; + } + else + if(scriptFile) + { + // No compiled script, let's just try executing it + // directly... this is either a mission file, or maybe + // we're on a readonly volume. +#ifdef TORQUE_DEBUG + Con::printf("Executing %s.", scriptFileName); +#endif + + CodeBlock *newCodeBlock = new CodeBlock(); + StringTableEntry name = StringTable->insert(scriptFileName); + + newCodeBlock->compileExec(name, script, noCalls, 0); + ret = true; + } + else + { + // Don't have anything. + Con::warnf(ConsoleLogEntry::Script, "Missing file: %s!", scriptFileName); + ret = false; + } + + delete [] script; + execDepth--; + return ret; +} + +ConsoleFunction(eval, const char *, 2, 2, "eval(consoleString)") +{ + TORQUE_UNUSED(argc); + return Con::evaluate(argv[1], false, NULL); +} + +ConsoleFunction(getVariable, const char *, 2, 2, "(string varName)\n" + "@brief Returns the value of the named variable or an empty string if not found.\n\n" + "@varName Name of the variable to search for\n" + "@return Value contained by varName, \"\" if the variable does not exist\n" + "@ingroup Scripting") +{ + return Con::getVariable(argv[1]); +} + +ConsoleFunction(setVariable, void, 3, 3, "(string varName, string value)\n" + "@brief Sets the value of the named variable.\n\n" + "@param varName Name of the variable to locate\n" + "@param value New value of the variable\n" + "@return True if variable was successfully found and set\n" + "@ingroup Scripting") +{ + return Con::setVariable(argv[1], argv[2]); +} + +ConsoleFunction(isFunction, bool, 2, 2, "(string funcName)" + "@brief Determines if a function exists or not\n\n" + "@param funcName String containing name of the function\n" + "@return True if the function exists, false if not\n" + "@ingroup Scripting") +{ + return Con::isFunction(argv[1]); +} + +ConsoleFunction(getFunctionPackage, const char*, 2, 2, "(string funcName)" + "@brief Provides the name of the package the function belongs to\n\n" + "@param funcName String containing name of the function\n" + "@return The name of the function's package\n" + "@ingroup Packages") +{ + Namespace::Entry* nse = Namespace::global()->lookup( StringTable->insert( argv[1] ) ); + if( !nse ) + return ""; + + return nse->mPackage; +} + +ConsoleFunction(isMethod, bool, 3, 3, "(string namespace, string method)" + "@brief Determines if a class/namespace method exists\n\n" + "@param namespace Class or namespace, such as Player\n" + "@param method Name of the function to search for\n" + "@return True if the method exists, false if not\n" + "@ingroup Scripting\n") +{ + Namespace* ns = Namespace::find( StringTable->insert( argv[1] ) ); + Namespace::Entry* nse = ns->lookup( StringTable->insert( argv[2] ) ); + if( !nse ) + return false; + + return true; +} + +ConsoleFunction(getMethodPackage, const char*, 3, 3, "(string namespace, string method)" + "@brief Provides the name of the package the method belongs to\n\n" + "@param namespace Class or namespace, such as Player\n" + "@param method Name of the funciton to search for\n" + "@return The name of the method's package\n" + "@ingroup Packages") +{ + Namespace* ns = Namespace::find( StringTable->insert( argv[1] ) ); + if( !ns ) + return ""; + + Namespace::Entry* nse = ns->lookup( StringTable->insert( argv[2] ) ); + if( !nse ) + return ""; + + return nse->mPackage; +} + +ConsoleFunction(isDefined, bool, 2, 3, "(string varName)" + "@brief Determines if a variable exists and contains a value\n" + "@param varName Name of the variable to search for\n" + "@return True if the variable was defined in script, false if not\n" + "@tsexample\n" + "isDefined( \"$myVar\" );\n" + "@endtsexample\n\n" + "@ingroup Scripting") +{ + if(dStrlen(argv[1]) == 0) + { + Con::errorf("isDefined() - did you forget to put quotes around the variable name?"); + return false; + } + + StringTableEntry name = StringTable->insert(argv[1]); + + // Deal with . + if (dStrchr(name, '.')) + { + static char scratchBuffer[4096]; + + S32 len = dStrlen(name); + AssertFatal(len < sizeof(scratchBuffer)-1, "isDefined() - name too long"); + dMemcpy(scratchBuffer, name, len+1); + + char * token = dStrtok(scratchBuffer, "."); + + if (!token || token[0] == '\0') + return false; + + StringTableEntry objName = StringTable->insert(token); + + // Attempt to find the object + SimObject * obj = Sim::findObject(objName); + + // If we didn't find the object then we can safely + // assume that the field variable doesn't exist + if (!obj) + return false; + + // Get the name of the field + token = dStrtok(0, ".\0"); + if (!token) + return false; + + while (token != NULL) + { + StringTableEntry valName = StringTable->insert(token); + + // Store these so we can restore them after we search for the variable + bool saveModStatic = obj->canModStaticFields(); + bool saveModDyn = obj->canModDynamicFields(); + + // Set this so that we can search both static and dynamic fields + obj->setModStaticFields(true); + obj->setModDynamicFields(true); + + const char* value = obj->getDataField(valName, 0); + + // Restore our mod flags to be safe + obj->setModStaticFields(saveModStatic); + obj->setModDynamicFields(saveModDyn); + + if (!value) + { + obj->setDataField(valName, 0, argv[2]); + + return false; + } + else + { + // See if we are field on a field + token = dStrtok(0, ".\0"); + if (token) + { + // The previous field must be an object + obj = Sim::findObject(value); + if (!obj) + return false; + } + else + { + if (dStrlen(value) > 0) + return true; + else if (argc > 2) + obj->setDataField(valName, 0, argv[2]); + } + } + } + } + else if (name[0] == '%') + { + // Look up a local variable + if( gEvalState.getStackDepth() > 0 ) + { + Dictionary::Entry* ent = gEvalState.getCurrentFrame().lookup(name); + + if (ent) + return true; + else if (argc > 2) + gEvalState.getCurrentFrame().setVariable(name, argv[2]); + } + else + Con::errorf("%s() - no local variable frame.", __FUNCTION__); + } + else if (name[0] == '$') + { + // Look up a global value + Dictionary::Entry* ent = gEvalState.globalVars.lookup(name); + + if (ent) + return true; + else if (argc > 2) + gEvalState.globalVars.setVariable(name, argv[2]); + } + else + { + // Is it an object? + if (dStrcmp(argv[1], "0") && dStrcmp(argv[1], "") && (Sim::findObject(argv[1]) != NULL)) + return true; + else if (argc > 2) + Con::errorf("%s() - can't assign a value to a variable of the form \"%s\"", __FUNCTION__, argv[1]); + } + + return false; +} + +//----------------------------------------------------------------------------- + +ConsoleFunction( isCurrentScriptToolScript, bool, 1, 1, + "() Returns true if the calling script is a tools script.\n" + "@hide") +{ + return Con::isCurrentScriptToolScript(); +} + +ConsoleFunction(getModNameFromPath, const char *, 2, 2, "(string path)" + "@brief Attempts to extract a mod directory from path. Returns empty string on failure.\n\n" + "@param File path of mod folder\n" + "@note This is no longer relevant in Torque 3D (which does not use mod folders), should be deprecated\n" + "@internal") +{ + StringTableEntry modPath = Con::getModNameFromPath(argv[1]); + return modPath ? modPath : ""; +} + +//----------------------------------------------------------------------------- + +ConsoleFunction( pushInstantGroup, void, 1, 2, "([group])" + "@brief Pushes the current $instantGroup on a stack " + "and sets it to the given value (or clears it).\n\n" + "@note Currently only used for editors\n" + "@ingroup Editors\n" + "@internal") +{ + if( argc > 1 ) + Con::pushInstantGroup( argv[ 1 ] ); + else + Con::pushInstantGroup(); +} + +ConsoleFunction( popInstantGroup, void, 1, 1, "()" + "@brief Pop and restore the last setting of $instantGroup off the stack.\n\n" + "@note Currently only used for editors\n\n" + "@ingroup Editors\n" + "@internal") +{ + Con::popInstantGroup(); +} + +//----------------------------------------------------------------------------- + +ConsoleFunction(getPrefsPath, const char *, 1, 2, "([relativeFileName])" + "@note Appears to be useless in Torque 3D, should be deprecated\n" + "@internal") +{ + const char *filename = Platform::getPrefsPath(argc > 1 ? argv[1] : NULL); + if(filename == NULL || *filename == 0) + return ""; + + return filename; +} + +//----------------------------------------------------------------------------- + +ConsoleFunction( execPrefs, bool, 2, 4, "( string relativeFileName, bool noCalls=false, bool journalScript=false )" + "@brief Manually execute a special script file that contains game or editor preferences\n\n" + "@param relativeFileName Name and path to file from project folder\n" + "@param noCalls Deprecated\n" + "@param journalScript Deprecated\n" + "@return True if script was successfully executed\n" + "@note Appears to be useless in Torque 3D, should be deprecated\n" + "@ingroup Scripting") +{ + const char *filename = Platform::getPrefsPath(argv[1]); + if(filename == NULL || *filename == 0) + return false; + + // Scripts do this a lot, so we may as well help them out + if(! Platform::isFile(filename) && ! Torque::FS::IsFile(filename)) + return true; + + argv[0] = "exec"; + argv[1] = filename; + return dAtob(Con::execute(argc, argv)); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( export, void, ( const char* pattern, const char* filename, bool append ), ( "", false ), + "Write out the definitions of all global variables matching the given name @a pattern.\n" + "If @a fileName is not \"\", the variable definitions are written to the specified file. Otherwise the " + "definitions will be printed to the console.\n\n" + "The output are valid TorqueScript statements that can be executed to restore the global variable " + "values.\n\n" + "@param pattern A global variable name pattern. Must begin with '$'.\n" + "@param filename %Path of the file to which to write the definitions or \"\" to write the definitions " + "to the console.\n" + "@param append If true and @a fileName is not \"\", then the definitions are appended to the specified file. " + "Otherwise existing contents of the file (if any) will be overwritten.\n\n" + "@tsexample\n" + "// Write out all preference variables to a prefs.cs file.\n" + "export( \"$prefs::*\", \"prefs.cs\" );\n" + "@endtsexample\n\n" + "@ingroup Scripting" ) +{ + if( filename && filename[ 0 ] ) + { +#ifndef TORQUE2D_TOOLS_FIXME + if(Con::expandScriptFilename(scriptFilenameBuffer, sizeof(scriptFilenameBuffer), filename)) + filename = scriptFilenameBuffer; +#else + filename = Platform::getPrefsPath( filename ); + if(filename == NULL || *filename == 0) + return; +#endif + } + else + filename = NULL; + + gEvalState.globalVars.exportVariables( pattern, filename, append ); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( deleteVariables, void, ( const char* pattern ),, + "Undefine all global variables matching the given name @a pattern.\n" + "@param pattern A global variable name pattern. Must begin with '$'.\n" + "@tsexample\n" + "// Define a global variable in the \"My\" namespace.\n" + "$My::Variable = \"value\";\n\n" + "// Undefine all variable in the \"My\" namespace.\n" + "deleteVariables( \"$My::*\" );\n" + "@endtsexample\n\n" + "@see strIsMatchExpr\n" + "@ingroup Scripting" ) +{ + gEvalState.globalVars.deleteVariables( pattern ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( trace, void, ( bool enable ), ( true ), + "Enable or disable tracing in the script code VM.\n\n" + "When enabled, the script code runtime will trace the invocation and returns " + "from all functions that are called and log them to the console. This is helpful in " + "observing the flow of the script program.\n\n" + "@param enable New setting for script trace execution, on by default.\n" + "@ingroup Debugging" ) +{ + gEvalState.traceOn = enable; + Con::printf( "Console trace %s", gEvalState.traceOn ? "enabled." : "disabled." ); +} + +//----------------------------------------------------------------------------- + +#if defined(TORQUE_DEBUG) || !defined(TORQUE_SHIPPING) + +DefineConsoleFunction( debug, void, (),, + "Drops the engine into the native C++ debugger.\n\n" + "This function triggers a debug break and drops the process into the IDE's debugger. If the process is not " + "running with a debugger attached it will generate a runtime error on most platforms.\n\n" + "@note This function is not available in shipping builds." + "@ingroup Debugging" ) +{ + Platform::debugBreak(); +} + +#endif + +//----------------------------------------------------------------------------- + +DefineEngineFunction( isShippingBuild, bool, (),, + "Test whether the engine has been compiled with TORQUE_SHIPPING, i.e. in a form meant for final release.\n\n" + "@return True if this is a shipping build; false otherwise.\n\n" + "@ingroup Platform" ) +{ +#ifdef TORQUE_SHIPPING + return true; +#else + return false; +#endif +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( isDebugBuild, bool, (),, + "Test whether the engine has been compiled with TORQUE_DEBUG, i.e. if it includes debugging functionality.\n\n" + "@return True if this is a debug build; false otherwise.\n\n" + "@ingroup Platform" ) +{ +#ifdef TORQUE_DEBUG + return true; +#else + return false; +#endif +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( isToolBuild, bool, (),, + "Test whether the engine has been compiled with TORQUE_TOOLS, i.e. if it includes tool-related functionality.\n\n" + "@return True if this is a tool build; false otherwise.\n\n" + "@ingroup Platform" ) +{ +#ifdef TORQUE_TOOLS + return true; +#else + return false; +#endif +} diff --git a/Engine/source/console/consoleInternal.cpp b/Engine/source/console/consoleInternal.cpp new file mode 100644 index 000000000..250326460 --- /dev/null +++ b/Engine/source/console/consoleInternal.cpp @@ -0,0 +1,1873 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/console.h" + +#include "console/ast.h" +#include "core/tAlgorithm.h" + +#include "core/strings/findMatch.h" +#include "console/consoleInternal.h" +#include "core/stream/fileStream.h" +#include "console/compiler.h" +#include "console/engineAPI.h" + +//#define DEBUG_SPEW + + +#define ST_INIT_SIZE 15 + +static char scratchBuffer[1024]; +U32 Namespace::mCacheSequence = 0; +DataChunker Namespace::mCacheAllocator; +DataChunker Namespace::mAllocator; +Namespace *Namespace::mNamespaceList = NULL; +Namespace *Namespace::mGlobalNamespace = NULL; + + +bool canTabComplete(const char *prevText, const char *bestMatch, + const char *newText, S32 baseLen, bool fForward) +{ + // test if it matches the first baseLen chars: + if(dStrnicmp(newText, prevText, baseLen)) + return false; + + if (fForward) + { + if(!bestMatch) + return dStricmp(newText, prevText) > 0; + else + return (dStricmp(newText, prevText) > 0) && + (dStricmp(newText, bestMatch) < 0); + } + else + { + if (dStrlen(prevText) == (U32) baseLen) + { + // look for the 'worst match' + if(!bestMatch) + return dStricmp(newText, prevText) > 0; + else + return dStricmp(newText, bestMatch) > 0; + } + else + { + if (!bestMatch) + return (dStricmp(newText, prevText) < 0); + else + return (dStricmp(newText, prevText) < 0) && + (dStricmp(newText, bestMatch) > 0); + } + } +} + +//--------------------------------------------------------------- +// +// Dictionary functions +// +//--------------------------------------------------------------- +struct StringValue +{ + S32 size; + char *val; + + operator char *() { return val; } + StringValue &operator=(const char *string); + + StringValue() { size = 0; val = NULL; } + ~StringValue() { dFree(val); } +}; + + +StringValue & StringValue::operator=(const char *string) +{ + if(!val) + { + val = dStrdup(string); + size = dStrlen(val); + } + else + { + S32 len = dStrlen(string); + if(len < size) + dStrcpy(val, string); + else + { + size = len; + dFree(val); + val = dStrdup(string); + } + } + return *this; +} + +static S32 QSORT_CALLBACK varCompare(const void* a,const void* b) +{ + return dStricmp( (*((Dictionary::Entry **) a))->name, (*((Dictionary::Entry **) b))->name ); +} + +void Dictionary::exportVariables(const char *varString, const char *fileName, bool append) +{ + const char *searchStr = varString; + Vector sortList(__FILE__, __LINE__); + + for(S32 i = 0; i < hashTable->size;i ++) + { + Entry *walk = hashTable->data[i]; + while(walk) + { + if(FindMatch::isMatch((char *) searchStr, (char *) walk->name)) + sortList.push_back(walk); + + walk = walk->nextEntry; + } + } + + if(!sortList.size()) + return; + + dQsort((void *) &sortList[0], sortList.size(), sizeof(Entry *), varCompare); + + Vector::iterator s; + char expandBuffer[1024]; + FileStream *strm = NULL; + + if(fileName) + { + if((strm = FileStream::createAndOpen( fileName, append ? Torque::FS::File::ReadWrite : Torque::FS::File::Write )) == NULL) + { + Con::errorf(ConsoleLogEntry::General, "Unable to open file '%s for writing.", fileName); + return; + } + if(append) + strm->setPosition(strm->getStreamSize()); + } + + char buffer[1024]; + const char *cat = fileName ? "\r\n" : ""; + + for(s = sortList.begin(); s != sortList.end(); s++) + { + switch((*s)->type) + { + case Entry::TypeInternalInt: + dSprintf(buffer, sizeof(buffer), "%s = %d;%s", (*s)->name, (*s)->ival, cat); + break; + case Entry::TypeInternalFloat: + dSprintf(buffer, sizeof(buffer), "%s = %g;%s", (*s)->name, (*s)->fval, cat); + break; + default: + expandEscape(expandBuffer, (*s)->getStringValue()); + dSprintf(buffer, sizeof(buffer), "%s = \"%s\";%s", (*s)->name, expandBuffer, cat); + break; + } + if(strm) + strm->write(dStrlen(buffer), buffer); + else + Con::printf("%s", buffer); + } + if(strm) + delete strm; +} + +void Dictionary::exportVariables( const char *varString, Vector *names, Vector *values ) +{ + const char *searchStr = varString; + Vector sortList(__FILE__, __LINE__); + + for ( S32 i = 0; i < hashTable->size; i++ ) + { + Entry *walk = hashTable->data[i]; + while ( walk ) + { + if ( FindMatch::isMatch( (char*)searchStr, (char*)walk->name ) ) + sortList.push_back( walk ); + + walk = walk->nextEntry; + } + } + + if ( !sortList.size() ) + return; + + dQsort((void *) &sortList[0], sortList.size(), sizeof(Entry *), varCompare); + + if ( names ) + names->reserve( sortList.size() ); + if ( values ) + values->reserve( sortList.size() ); + + char expandBuffer[1024]; + + Vector::iterator s; + + for ( s = sortList.begin(); s != sortList.end(); s++ ) + { + if ( names ) + names->push_back( String( (*s)->name ) ); + + if ( values ) + { + switch ( (*s)->type ) + { + case Entry::TypeInternalInt: + values->push_back( String::ToString( (*s)->ival ) ); + break; + case Entry::TypeInternalFloat: + values->push_back( String::ToString( (*s)->fval ) ); + break; + default: + expandEscape( expandBuffer, (*s)->getStringValue() ); + values->push_back( expandBuffer ); + break; + } + } + } +} + +void Dictionary::deleteVariables(const char *varString) +{ + const char *searchStr = varString; + + for(S32 i = 0; i < hashTable->size; i++) + { + Entry *walk = hashTable->data[i]; + while(walk) + { + Entry *matchedEntry = (FindMatch::isMatch((char *) searchStr, (char *) walk->name)) ? walk : NULL; + walk = walk->nextEntry; + if (matchedEntry) + remove(matchedEntry); // assumes remove() is a stable remove (will not reorder entries on remove) + } + } +} + +S32 HashPointer(StringTableEntry ptr) +{ + return (S32)(((dsize_t)ptr) >> 2); +} + +Dictionary::Entry *Dictionary::lookup(StringTableEntry name) +{ + Entry *walk = hashTable->data[HashPointer(name) % hashTable->size]; + while(walk) + { + if(walk->name == name) + return walk; + else + walk = walk->nextEntry; + } + + return NULL; +} + +Dictionary::Entry *Dictionary::add(StringTableEntry name) +{ + // Try to find an existing match. + + Entry* ret = lookup( name ); + if( ret ) + return ret; + + // Rehash if the table get's too crowded. Be aware that this might + // modify a table that we don't own. + + hashTable->count ++; + if( hashTable->count > hashTable->size * 2 ) + { + // Allocate a new table. + + const U32 newTableSize = hashTable->size * 4 - 1; + Entry** newTableData = new Entry*[ newTableSize ]; + dMemset( newTableData, 0, newTableSize * sizeof( Entry* ) ); + + // Move the entries over. + + for( U32 i = 0; i < hashTable->size; ++ i ) + for( Entry* entry = hashTable->data[ i ]; entry != NULL; ) + { + Entry* next = entry->nextEntry; + S32 index = HashPointer( entry->name ) % newTableSize; + + entry->nextEntry = newTableData[ index ]; + newTableData[ index ] = entry; + + entry = next; + } + + // Switch the tables. + + delete[] hashTable->data; + hashTable->data = newTableData; + hashTable->size = newTableSize; + } + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[ConsoleInternal] Adding entry '%s'", name ); + #endif + + // Add the new entry. + + ret = hashTable->mChunker.alloc(); + constructInPlace( ret, name ); + S32 idx = HashPointer(name) % hashTable->size; + ret->nextEntry = hashTable->data[idx]; + hashTable->data[idx] = ret; + + return ret; +} + +// deleteVariables() assumes remove() is a stable remove (will not reorder entries on remove) +void Dictionary::remove(Dictionary::Entry *ent) +{ + Entry **walk = &hashTable->data[HashPointer(ent->name) % hashTable->size]; + while(*walk != ent) + walk = &((*walk)->nextEntry); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[ConsoleInternal] Removing entry '%s'", ent->name ); + #endif + + *walk = (ent->nextEntry); + + destructInPlace( ent ); + hashTable->mChunker.free( ent ); + + hashTable->count--; +} + +Dictionary::Dictionary() + : hashTable( NULL ), +#pragma warning( disable : 4355 ) + ownHashTable( this ), // Warning with VC++ but this is safe. +#pragma warning( default : 4355 ) + exprState( NULL ), + scopeName( NULL ), + scopeNamespace( NULL ), + code( NULL ), + ip( 0 ) +{ +} + +void Dictionary::setState(ExprEvalState *state, Dictionary* ref) +{ + exprState = state; + + if( ref ) + { + hashTable = ref->hashTable; + return; + } + + if( !ownHashTable.data ) + { + ownHashTable.count = 0; + ownHashTable.size = ST_INIT_SIZE; + ownHashTable.data = new Entry *[ ownHashTable.size ]; + + dMemset( ownHashTable.data, 0, ownHashTable.size * sizeof( Entry* ) ); + } + + hashTable = &ownHashTable; +} + +Dictionary::~Dictionary() +{ + reset(); + if( ownHashTable.data ) + delete [] ownHashTable.data; +} + +void Dictionary::reset() +{ + if( hashTable && hashTable->owner != this ) + { + hashTable = NULL; + return; + } + + for( U32 i = 0; i < ownHashTable.size; ++ i ) + { + Entry* walk = ownHashTable.data[i]; + while( walk ) + { + Entry* temp = walk->nextEntry; + destructInPlace( walk ); + walk = temp; + } + } + + dMemset( ownHashTable.data, 0, ownHashTable.size * sizeof( Entry* ) ); + ownHashTable.mChunker.freeBlocks( true ); + + ownHashTable.count = 0; + hashTable = NULL; + + scopeName = NULL; + scopeNamespace = NULL; + code = NULL; + ip = 0; +} + + +const char *Dictionary::tabComplete(const char *prevText, S32 baseLen, bool fForward) +{ + S32 i; + + const char *bestMatch = NULL; + for(i = 0; i < hashTable->size; i++) + { + Entry *walk = hashTable->data[i]; + while(walk) + { + if(canTabComplete(prevText, bestMatch, walk->name, baseLen, fForward)) + bestMatch = walk->name; + walk = walk->nextEntry; + } + } + return bestMatch; +} + + +char *typeValueEmpty = ""; + +Dictionary::Entry::Entry(StringTableEntry in_name) +{ + name = in_name; + type = TypeInternalString; + notify = NULL; + nextEntry = NULL; + mUsage = NULL; + mIsConstant = false; + + // NOTE: This is data inside a nameless + // union, so we don't need to init the rest. + ival = 0; + fval = 0; + sval = typeValueEmpty; + bufferLen = 0; +} + +Dictionary::Entry::~Entry() +{ + if ( type <= TypeInternalString && + sval != typeValueEmpty ) + dFree(sval); + + if ( notify ) + delete notify; +} + +const char *Dictionary::getVariable(StringTableEntry name, bool *entValid) +{ + Entry *ent = lookup(name); + if(ent) + { + if(entValid) + *entValid = true; + return ent->getStringValue(); + } + if(entValid) + *entValid = false; + + // Warn users when they access a variable that isn't defined. + if(gWarnUndefinedScriptVariables) + Con::warnf(" *** Accessed undefined variable '%s'", name); + + return ""; +} + +void Dictionary::Entry::setStringValue(const char * value) +{ + if( mIsConstant ) + { + Con::errorf( "Cannot assign value to constant '%s'.", name ); + return; + } + + if(type <= TypeInternalString) + { + // Let's not remove empty-string-valued global vars from the dict. + // If we remove them, then they won't be exported, and sometimes + // it could be necessary to export such a global. There are very + // few empty-string global vars so there's no performance-related + // need to remove them from the dict. +/* + if(!value[0] && name[0] == '$') + { + gEvalState.globalVars.remove(this); + return; + } +*/ + + U32 stringLen = dStrlen(value); + + // If it's longer than 256 bytes, it's certainly not a number. + // + // (This decision may come back to haunt you. Shame on you if it + // does.) + if(stringLen < 256) + { + fval = dAtof(value); + ival = dAtoi(value); + } + else + { + fval = 0.f; + ival = 0; + } + + type = TypeInternalString; + + // may as well pad to the next cache line + U32 newLen = ((stringLen + 1) + 15) & ~15; + + if(sval == typeValueEmpty) + sval = (char *) dMalloc(newLen); + else if(newLen > bufferLen) + sval = (char *) dRealloc(sval, newLen); + + bufferLen = newLen; + dStrcpy(sval, value); + } + else + Con::setData(type, dataPtr, 0, 1, &value, enumTable); + + // Fire off the notification if we have one. + if ( notify ) + notify->trigger(); +} + +void Dictionary::setVariable(StringTableEntry name, const char *value) +{ + Entry *ent = add(name); + if(!value) + value = ""; + ent->setStringValue(value); +} + +Dictionary::Entry* Dictionary::addVariable( const char *name, + S32 type, + void *dataPtr, + const char* usage ) +{ + AssertFatal( type >= 0, "Dictionary::addVariable - Got bad type!" ); + + if(name[0] != '$') + { + scratchBuffer[0] = '$'; + dStrcpy(scratchBuffer + 1, name); + name = scratchBuffer; + } + + Entry *ent = add(StringTable->insert(name)); + + if ( ent->type <= Entry::TypeInternalString && + ent->sval != typeValueEmpty ) + dFree(ent->sval); + + ent->type = type; + ent->dataPtr = dataPtr; + ent->mUsage = usage; + + // Fetch enum table, if any. + + ConsoleBaseType* conType = ConsoleBaseType::getType( type ); + AssertFatal( conType, "Dictionary::addVariable - invalid console type" ); + ent->enumTable = conType->getEnumTable(); + + return ent; +} + +bool Dictionary::removeVariable(StringTableEntry name) +{ + if( Entry *ent = lookup(name) ) + { + remove( ent ); + return true; + } + return false; +} + +void Dictionary::addVariableNotify( const char *name, const Con::NotifyDelegate &callback ) +{ + Entry *ent = lookup(StringTable->insert(name)); + if ( !ent ) + return; + + if ( !ent->notify ) + ent->notify = new Entry::NotifySignal(); + + ent->notify->notify( callback ); +} + +void Dictionary::removeVariableNotify( const char *name, const Con::NotifyDelegate &callback ) +{ + Entry *ent = lookup(StringTable->insert(name)); + if ( ent && ent->notify ) + ent->notify->remove( callback ); +} + +void Dictionary::validate() +{ + AssertFatal( ownHashTable.owner == this, + "Dictionary::validate() - Dictionary not owner of own hashtable!" ); +} + +void ExprEvalState::pushFrame(StringTableEntry frameName, Namespace *ns) +{ + #ifdef DEBUG_SPEW + validate(); + + Platform::outputDebugString( "[ConsoleInternal] Pushing new frame for '%s' at %i", + frameName, mStackDepth ); + #endif + + if( mStackDepth + 1 > stack.size() ) + { + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[ConsoleInternal] Growing stack by one frame" ); + #endif + + stack.push_back( new Dictionary ); + } + + Dictionary& newFrame = *( stack[ mStackDepth ] ); + newFrame.setState( this ); + + newFrame.scopeName = frameName; + newFrame.scopeNamespace = ns; + + mStackDepth ++; + currentVariable = NULL; + + AssertFatal( !newFrame.getCount(), "ExprEvalState::pushFrame - Dictionary not empty!" ); + + #ifdef DEBUG_SPEW + validate(); + #endif +} + +void ExprEvalState::popFrame() +{ + AssertFatal( mStackDepth > 0, "ExprEvalState::popFrame - Stack Underflow!" ); + + #ifdef DEBUG_SPEW + validate(); + + Platform::outputDebugString( "[ConsoleInternal] Popping %sframe at %i", + getCurrentFrame().isOwner() ? "" : "shared ", mStackDepth - 1 ); + #endif + + mStackDepth --; + stack[ mStackDepth ]->reset(); + currentVariable = NULL; + + #ifdef DEBUG_SPEW + validate(); + #endif +} + +void ExprEvalState::pushFrameRef(S32 stackIndex) +{ + AssertFatal( stackIndex >= 0 && stackIndex < stack.size(), "You must be asking for a valid frame!" ); + + #ifdef DEBUG_SPEW + validate(); + + Platform::outputDebugString( "[ConsoleInternal] Cloning frame from %i to %i", + stackIndex, mStackDepth ); + #endif + + if( mStackDepth + 1 > stack.size() ) + { + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[ConsoleInternal] Growing stack by one frame" ); + #endif + + stack.push_back( new Dictionary ); + } + + Dictionary& newFrame = *( stack[ mStackDepth ] ); + newFrame.setState( this, stack[ stackIndex ] ); + + mStackDepth ++; + currentVariable = NULL; + + #ifdef DEBUG_SPEW + validate(); + #endif +} + +ExprEvalState::ExprEvalState() +{ + VECTOR_SET_ASSOCIATION(stack); + globalVars.setState(this); + thisObject = NULL; + traceOn = false; + currentVariable = NULL; + mStackDepth = 0; + stack.reserve( 64 ); +} + +ExprEvalState::~ExprEvalState() +{ + // Delete callframes. + + while( !stack.empty() ) + { + delete stack.last(); + stack.decrement(); + } +} + +void ExprEvalState::validate() +{ + AssertFatal( mStackDepth <= stack.size(), + "ExprEvalState::validate() - Stack depth pointing beyond last stack frame!" ); + + for( U32 i = 0; i < stack.size(); ++ i ) + stack[ i ]->validate(); +} + +DefineEngineFunction(backtrace, void, ( ),, + "@brief Prints the scripting call stack to the console log.\n\n" + "Used to trace functions called from within functions. Can help discover what functions were called " + "(and not yet exited) before the current point in scripts.\n\n" + "@ingroup Debugging") +{ + U32 totalSize = 1; + + for(U32 i = 0; i < gEvalState.getStackDepth(); i++) + { + if(gEvalState.stack[i]->scopeNamespace && gEvalState.stack[i]->scopeNamespace->mEntryList->mPackage) + totalSize += dStrlen(gEvalState.stack[i]->scopeNamespace->mEntryList->mPackage) + 2; + if(gEvalState.stack[i]->scopeName) + totalSize += dStrlen(gEvalState.stack[i]->scopeName) + 3; + if(gEvalState.stack[i]->scopeNamespace && gEvalState.stack[i]->scopeNamespace->mName) + totalSize += dStrlen(gEvalState.stack[i]->scopeNamespace->mName) + 2; + } + + char *buf = Con::getReturnBuffer(totalSize); + buf[0] = 0; + for(U32 i = 0; i < gEvalState.getStackDepth(); i++) + { + dStrcat(buf, "->"); + + if(gEvalState.stack[i]->scopeNamespace && gEvalState.stack[i]->scopeNamespace->mEntryList->mPackage) + { + dStrcat(buf, "["); + dStrcat(buf, gEvalState.stack[i]->scopeNamespace->mEntryList->mPackage); + dStrcat(buf, "]"); + } + if(gEvalState.stack[i]->scopeNamespace && gEvalState.stack[i]->scopeNamespace->mName) + { + dStrcat(buf, gEvalState.stack[i]->scopeNamespace->mName); + dStrcat(buf, "::"); + } + if(gEvalState.stack[i]->scopeName) + dStrcat(buf, gEvalState.stack[i]->scopeName); + } + + Con::printf("BackTrace: %s", buf); +} + +Namespace::Entry::Entry() +{ + mCode = NULL; + mType = InvalidFunctionType; + mUsage = NULL; + mHeader = NULL; + mNamespace = NULL; +} + +void Namespace::Entry::clear() +{ + if(mCode) + { + mCode->decRefCount(); + mCode = NULL; + } + + // Clean up usage strings generated for script functions. + if( ( mType == Namespace::Entry::ConsoleFunctionType ) && mUsage ) + { + delete mUsage; + mUsage = NULL; + } +} + +Namespace::Namespace() +{ + mPackage = NULL; + mUsage = NULL; + mCleanUpUsage = false; + mName = NULL; + mParent = NULL; + mNext = NULL; + mEntryList = NULL; + mHashSize = 0; + mHashTable = 0; + mHashSequence = 0; + mRefCountToParent = 0; + mClassRep = 0; +} + +Namespace::~Namespace() +{ + clearEntries(); + if( mUsage && mCleanUpUsage ) + { + delete mUsage; + mUsage = NULL; + mCleanUpUsage = false; + } +} + +void Namespace::clearEntries() +{ + for(Entry *walk = mEntryList; walk; walk = walk->mNext) + walk->clear(); +} + +Namespace *Namespace::find(StringTableEntry name, StringTableEntry package) +{ + if ( name == NULL && package == NULL ) + return mGlobalNamespace; + + for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext) + { + if(walk->mName == name && walk->mPackage == package) + return walk; + } + + Namespace *ret = (Namespace *) mAllocator.alloc(sizeof(Namespace)); + constructInPlace(ret); + ret->mPackage = package; + ret->mName = name; + ret->mNext = mNamespaceList; + mNamespaceList = ret; + return ret; +} + +bool Namespace::unlinkClass( Namespace *parent ) +{ + AssertFatal( mPackage == NULL, "Namespace::unlinkClass - Must not be called on a namespace coming from a package!" ); + + // Skip additions to this namespace coming from packages. + + Namespace* walk = getPackageRoot(); + + // Make sure "parent" is the direct parent namespace. + + if( parent != NULL && walk->mParent && walk->mParent != parent ) + { + Con::errorf(ConsoleLogEntry::General, "Namespace::unlinkClass - cannot unlink namespace parent linkage for %s for %s.", + walk->mName, walk->mParent->mName); + return false; + } + + // Decrease the reference count. Note that we do this on + // the bottom-most namespace, i.e. the one guaranteed not + // to come from a package. + + mRefCountToParent --; + AssertFatal( mRefCountToParent >= 0, "Namespace::unlinkClass - reference count to parent is less than 0" ); + + // Unlink if the count dropped to zero. + + if( mRefCountToParent == 0 ) + { + walk->mParent = NULL; + trashCache(); + } + + return true; +} + + +bool Namespace::classLinkTo(Namespace *parent) +{ + Namespace* walk = getPackageRoot(); + + if(walk->mParent && walk->mParent != parent) + { + Con::errorf(ConsoleLogEntry::General, "Error: cannot change namespace parent linkage of %s from %s to %s.", + walk->mName, walk->mParent->mName, parent->mName); + return false; + } + + trashCache(); + walk->mParent = parent; + + mRefCountToParent++; + + return true; +} + +void Namespace::buildHashTable() +{ + if(mHashSequence == mCacheSequence) + return; + + if(!mEntryList && mParent) + { + mParent->buildHashTable(); + mHashTable = mParent->mHashTable; + mHashSize = mParent->mHashSize; + mHashSequence = mCacheSequence; + return; + } + + U32 entryCount = 0; + Namespace * ns; + for(ns = this; ns; ns = ns->mParent) + for(Entry *walk = ns->mEntryList; walk; walk = walk->mNext) + if(lookupRecursive(walk->mFunctionName) == walk) + entryCount++; + + mHashSize = entryCount + (entryCount >> 1) + 1; + + if(!(mHashSize & 1)) + mHashSize++; + + mHashTable = (Entry **) mCacheAllocator.alloc(sizeof(Entry *) * mHashSize); + for(U32 i = 0; i < mHashSize; i++) + mHashTable[i] = NULL; + + for(ns = this; ns; ns = ns->mParent) + { + for(Entry *walk = ns->mEntryList; walk; walk = walk->mNext) + { + U32 index = HashPointer(walk->mFunctionName) % mHashSize; + while(mHashTable[index] && mHashTable[index]->mFunctionName != walk->mFunctionName) + { + index++; + if(index >= mHashSize) + index = 0; + } + + if(!mHashTable[index]) + mHashTable[index] = walk; + } + } + + mHashSequence = mCacheSequence; +} + +void Namespace::getUniqueEntryLists( Namespace *other, VectorPtr *outThisList, VectorPtr *outOtherList ) +{ + // All namespace entries in the common ACR should be + // ignored when checking for duplicate entry names. + static VectorPtr commonEntries; + commonEntries.clear(); + + AbstractClassRep *commonACR = mClassRep->getCommonParent( other->mClassRep ); + commonACR->getNameSpace()->getEntryList( &commonEntries ); + + // Make life easier + VectorPtr &thisEntries = *outThisList; + VectorPtr &compEntries = *outOtherList; + + // Clear, just in case they aren't + thisEntries.clear(); + compEntries.clear(); + + getEntryList( &thisEntries ); + other->getEntryList( &compEntries ); + + // Run through all of the entries in the common ACR, and remove them from + // the other two entry lists + for( NamespaceEntryListIterator itr = commonEntries.begin(); itr != commonEntries.end(); itr++ ) + { + // Check this entry list + for( NamespaceEntryListIterator thisItr = thisEntries.begin(); thisItr != thisEntries.end(); thisItr++ ) + { + if( *thisItr == *itr ) + { + thisEntries.erase( thisItr ); + break; + } + } + + // Same check for component entry list + for( NamespaceEntryListIterator compItr = compEntries.begin(); compItr != compEntries.end(); compItr++ ) + { + if( *compItr == *itr ) + { + compEntries.erase( compItr ); + break; + } + } + } +} + +void Namespace::init() +{ + // create the global namespace + mGlobalNamespace = (Namespace *) mAllocator.alloc(sizeof(Namespace)); + constructInPlace(mGlobalNamespace); + mGlobalNamespace->mPackage = NULL; + mGlobalNamespace->mName = NULL; + mGlobalNamespace->mNext = NULL; + mNamespaceList = mGlobalNamespace; +} + +Namespace *Namespace::global() +{ + return mGlobalNamespace; +} + +void Namespace::shutdown() +{ + // The data chunker will release all memory in one go + // without calling destructors, so we do this manually here. + + for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext) + walk->~Namespace(); +} + +void Namespace::trashCache() +{ + mCacheSequence++; + mCacheAllocator.freeBlocks(); +} + +const char *Namespace::tabComplete(const char *prevText, S32 baseLen, bool fForward) +{ + if(mHashSequence != mCacheSequence) + buildHashTable(); + + const char *bestMatch = NULL; + for(U32 i = 0; i < mHashSize; i++) + if(mHashTable[i] && canTabComplete(prevText, bestMatch, mHashTable[i]->mFunctionName, baseLen, fForward)) + bestMatch = mHashTable[i]->mFunctionName; + return bestMatch; +} + +Namespace::Entry *Namespace::lookupRecursive(StringTableEntry name) +{ + for(Namespace *ns = this; ns; ns = ns->mParent) + for(Entry *walk = ns->mEntryList; walk; walk = walk->mNext) + if(walk->mFunctionName == name) + return walk; + + return NULL; +} + +Namespace::Entry *Namespace::lookup(StringTableEntry name) +{ + if(mHashSequence != mCacheSequence) + buildHashTable(); + + U32 index = HashPointer(name) % mHashSize; + while(mHashTable[index] && mHashTable[index]->mFunctionName != name) + { + index++; + if(index >= mHashSize) + index = 0; + } + return mHashTable[index]; +} + +static S32 QSORT_CALLBACK compareEntries(const void* a,const void* b) +{ + const Namespace::Entry* fa = *((Namespace::Entry**)a); + const Namespace::Entry* fb = *((Namespace::Entry**)b); + + return dStricmp(fa->mFunctionName, fb->mFunctionName); +} + +void Namespace::getEntryList(VectorPtr *vec) +{ + if(mHashSequence != mCacheSequence) + buildHashTable(); + + for(U32 i = 0; i < mHashSize; i++) + if(mHashTable[i]) + vec->push_back(mHashTable[i]); + + dQsort(vec->address(),vec->size(),sizeof(Namespace::Entry *),compareEntries); +} + +Namespace::Entry *Namespace::createLocalEntry(StringTableEntry name) +{ + for(Entry *walk = mEntryList; walk; walk = walk->mNext) + { + if(walk->mFunctionName == name) + { + walk->clear(); + return walk; + } + } + + Entry *ent = (Entry *) mAllocator.alloc(sizeof(Entry)); + constructInPlace(ent); + + ent->mNamespace = this; + ent->mFunctionName = name; + ent->mNext = mEntryList; + ent->mPackage = mPackage; + ent->mToolOnly = false; + mEntryList = ent; + return ent; +} + +void Namespace::addFunction( StringTableEntry name, CodeBlock *cb, U32 functionOffset, const char* usage, U32 lineNumber ) +{ + Entry *ent = createLocalEntry(name); + trashCache(); + + ent->mUsage = usage; + ent->mCode = cb; + ent->mFunctionOffset = functionOffset; + ent->mCode->incRefCount(); + ent->mType = Entry::ConsoleFunctionType; + ent->mFunctionLineNumber = lineNumber; +} + +void Namespace::addCommand( StringTableEntry name, StringCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) +{ + Entry *ent = createLocalEntry(name); + trashCache(); + + ent->mUsage = usage; + ent->mHeader = header; + ent->mMinArgs = minArgs; + ent->mMaxArgs = maxArgs; + ent->mToolOnly = isToolOnly; + + ent->mType = Entry::StringCallbackType; + ent->cb.mStringCallbackFunc = cb; +} + +void Namespace::addCommand( StringTableEntry name, IntCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) +{ + Entry *ent = createLocalEntry(name); + trashCache(); + + ent->mUsage = usage; + ent->mHeader = header; + ent->mMinArgs = minArgs; + ent->mMaxArgs = maxArgs; + ent->mToolOnly = isToolOnly; + + ent->mType = Entry::IntCallbackType; + ent->cb.mIntCallbackFunc = cb; +} + +void Namespace::addCommand( StringTableEntry name, VoidCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) +{ + Entry *ent = createLocalEntry(name); + trashCache(); + + ent->mUsage = usage; + ent->mHeader = header; + ent->mMinArgs = minArgs; + ent->mMaxArgs = maxArgs; + ent->mToolOnly = isToolOnly; + + ent->mType = Entry::VoidCallbackType; + ent->cb.mVoidCallbackFunc = cb; +} + +void Namespace::addCommand( StringTableEntry name, FloatCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) +{ + Entry *ent = createLocalEntry(name); + trashCache(); + + ent->mUsage = usage; + ent->mHeader = header; + ent->mMinArgs = minArgs; + ent->mMaxArgs = maxArgs; + ent->mToolOnly = isToolOnly; + + ent->mType = Entry::FloatCallbackType; + ent->cb.mFloatCallbackFunc = cb; +} + +void Namespace::addCommand( StringTableEntry name, BoolCallback cb, const char *usage, S32 minArgs, S32 maxArgs, bool isToolOnly, ConsoleFunctionHeader* header ) +{ + Entry *ent = createLocalEntry(name); + trashCache(); + + ent->mUsage = usage; + ent->mHeader = header; + ent->mMinArgs = minArgs; + ent->mMaxArgs = maxArgs; + ent->mToolOnly = isToolOnly; + + ent->mType = Entry::BoolCallbackType; + ent->cb.mBoolCallbackFunc = cb; +} + +void Namespace::addScriptCallback( const char *funcName, const char *usage, ConsoleFunctionHeader* header ) +{ + static U32 uid=0; + char buffer[1024]; + char lilBuffer[32]; + dStrcpy(buffer, funcName); + dSprintf(lilBuffer, 32, "_%d_cb", uid++); + dStrcat(buffer, lilBuffer); + + Entry *ent = createLocalEntry(StringTable->insert( buffer )); + trashCache(); + + ent->mUsage = usage; + ent->mHeader = header; + ent->mMinArgs = -2; + ent->mMaxArgs = -3; + + ent->mType = Entry::ScriptCallbackType; + ent->cb.mCallbackName = funcName; +} + +void Namespace::markGroup(const char* name, const char* usage) +{ + static U32 uid=0; + char buffer[1024]; + char lilBuffer[32]; + dStrcpy(buffer, name); + dSprintf(lilBuffer, 32, "_%d", uid++); + dStrcat(buffer, lilBuffer); + + Entry *ent = createLocalEntry(StringTable->insert( buffer )); + trashCache(); + + if(usage != NULL) + lastUsage = (char*)(ent->mUsage = usage); + else + ent->mUsage = lastUsage; + + ent->mMinArgs = -1; // Make sure it explodes if somehow we run this entry. + ent->mMaxArgs = -2; + + ent->mType = Entry::GroupMarker; + ent->cb.mGroupName = name; +} + +extern S32 executeBlock(StmtNode *block, ExprEvalState *state); + +const char *Namespace::Entry::execute(S32 argc, const char **argv, ExprEvalState *state) +{ + if(mType == ConsoleFunctionType) + { + if(mFunctionOffset) + return mCode->exec(mFunctionOffset, argv[0], mNamespace, argc, argv, false, mPackage); + else + return ""; + } + +#ifndef TORQUE_DEBUG + // [tom, 12/13/2006] This stops tools functions from working in the console, + // which is useful behavior when debugging so I'm ifdefing this out for debug builds. + if(mToolOnly && ! Con::isCurrentScriptToolScript()) + { + Con::errorf(ConsoleLogEntry::Script, "%s::%s - attempting to call tools only function from outside of tools", mNamespace->mName, mFunctionName); + return ""; + } +#endif + + if((mMinArgs && argc < mMinArgs) || (mMaxArgs && argc > mMaxArgs)) + { + Con::warnf(ConsoleLogEntry::Script, "%s::%s - wrong number of arguments.", mNamespace->mName, mFunctionName); + Con::warnf(ConsoleLogEntry::Script, "usage: %s", mUsage); + return ""; + } + + static char returnBuffer[32]; + switch(mType) + { + case StringCallbackType: + return cb.mStringCallbackFunc(state->thisObject, argc, argv); + case IntCallbackType: + dSprintf(returnBuffer, sizeof(returnBuffer), "%d", + cb.mIntCallbackFunc(state->thisObject, argc, argv)); + return returnBuffer; + case FloatCallbackType: + dSprintf(returnBuffer, sizeof(returnBuffer), "%g", + cb.mFloatCallbackFunc(state->thisObject, argc, argv)); + return returnBuffer; + case VoidCallbackType: + cb.mVoidCallbackFunc(state->thisObject, argc, argv); + return ""; + case BoolCallbackType: + dSprintf(returnBuffer, sizeof(returnBuffer), "%d", + (U32)cb.mBoolCallbackFunc(state->thisObject, argc, argv)); + return returnBuffer; + } + + return ""; +} + +//----------------------------------------------------------------------------- +// Doc string code. + +namespace { + + /// Scan the given usage string for an argument list description. With the + /// old console macros, these were usually included as the first part of the + /// usage string. + bool sFindArgumentListSubstring( const char* usage, const char*& start, const char*& end ) + { + if( !usage ) + return false; + + const char* ptr = usage; + while( *ptr && *ptr != '(' && *ptr != '\n' ) // Only scan first line of usage string. + { + // Stop on the first alphanumeric character as we expect + // argument lists to precede descriptions. + if( dIsalnum( *ptr ) ) + return false; + + ptr ++; + } + + if( *ptr != '(' ) + return false; + + start = ptr; + ptr ++; + + bool inString = false; + U32 nestingCount = 0; + + while( *ptr && ( *ptr != ')' || nestingCount > 0 || inString ) ) + { + if( *ptr == '(' ) + nestingCount ++; + else if( *ptr == ')' ) + nestingCount --; + else if( *ptr == '"' ) + inString = !inString; + else if( *ptr == '\\' && ptr[ 1 ] == '"' ) + ptr ++; + ptr ++; + } + + if( *ptr ) + ptr ++; + end = ptr; + + return true; + } + + /// + void sParseList( const char* str, Vector< String >& outList ) + { + // Skip the initial '( '. + + const char* ptr = str; + while( *ptr && dIsspace( *ptr ) ) + ptr ++; + + if( *ptr == '(' ) + { + ptr ++; + while( *ptr && dIsspace( *ptr ) ) + ptr ++; + } + + // Parse out list items. + + while( *ptr && *ptr != ')' ) + { + // Find end of element. + + const char* start = ptr; + + bool inString = false; + U32 nestingCount = 0; + + while( *ptr && ( ( *ptr != ')' && *ptr != ',' ) || nestingCount > 0 || inString ) ) + { + if( *ptr == '(' ) + nestingCount ++; + else if( *ptr == ')' ) + nestingCount --; + else if( *ptr == '"' ) + inString = !inString; + else if( *ptr == '\\' && ptr[ 1 ] == '"' ) + ptr ++; + ptr ++; + } + + // Backtrack to remove trailing whitespace. + + const char* end = ptr; + if( *end == ',' || *end == ')' ) + end --; + while( end > start && dIsspace( *end ) ) + end --; + if( *end ) + end ++; + + // Add to list. + + if( start != end ) + outList.push_back( String( start, end - start ) ); + + // Skip comma and whitespace. + + if( *ptr == ',' ) + ptr ++; + while( *ptr && dIsspace( *ptr ) ) + ptr ++; + } + } + + /// + void sGetArgNameAndType( const String& str, String& outType, String& outName ) + { + if( !str.length() ) + { + outType = String::EmptyString; + outName = String::EmptyString; + return; + } + + // Find first non-ID character from right. + + S32 index = str.length() - 1; + while( index >= 0 && ( dIsalnum( str[ index ] ) || str[ index ] == '_' ) ) + index --; + + const U32 nameStartIndex = index + 1; + + // Find end of type name by skipping rightmost whitespace inwards. + + while( index >= 0 && dIsspace( str[ index ] ) ) + index --; + + // + + outName = String( &( ( const char* ) str )[ nameStartIndex ] ); + outType = String( str, index + 1 ); + } + + /// Return the type name to show in documentation for the given C++ type. + const char* sGetDocTypeString( const char* nativeType ) + { + if( dStrncmp( nativeType, "const ", 6 ) == 0 ) + nativeType += 6; + + if( dStrcmp( nativeType, "char*" ) == 0 || dStrcmp( nativeType, "char *" ) == 0 ) + return "string"; + else if( dStrcmp( nativeType, "S32" ) == 0 || dStrcmp( nativeType, "U32" ) == 0 ) + return "int"; + else if( dStrcmp( nativeType, "F32" ) == 0 ) + return "float"; + + const U32 length = dStrlen( nativeType ); + if( nativeType[ length - 1 ] == '&' || nativeType[ length - 1 ] == '*' ) + return StringTable->insertn( nativeType, length - 1 ); + + return nativeType; + } +} + +String Namespace::Entry::getBriefDescription( String* outRemainingDocText ) const +{ + String docString = getDocString(); + + S32 newline = docString.find( '\n' ); + if( newline == -1 ) + { + if( outRemainingDocText ) + *outRemainingDocText = String(); + return docString; + } + + String brief = docString.substr( 0, newline ); + if( outRemainingDocText ) + *outRemainingDocText = docString.substr( newline + 1 ); + + return brief; +} + +String Namespace::Entry::getDocString() const +{ + const char* argListStart; + const char* argListEnd; + + if( sFindArgumentListSubstring( mUsage, argListStart, argListEnd ) ) + { + // Skip the " - " part present in some old doc strings. + + const char* ptr = argListEnd; + while( *ptr && dIsspace( *ptr ) ) + ptr ++; + + if( *ptr == '-' ) + { + ptr ++; + while( *ptr && dIsspace( *ptr ) ) + ptr ++; + } + + return ptr; + } + + return mUsage; +} + +String Namespace::Entry::getArgumentsString() const +{ + StringBuilder str; + + if( mHeader ) + { + // Parse out the argument list string supplied with the extended + // function header and add default arguments as we go. + + Vector< String > argList; + Vector< String > defaultArgList; + + sParseList( mHeader->mArgString, argList ); + sParseList( mHeader->mDefaultArgString, defaultArgList ); + + str.append( '(' ); + + const U32 numArgs = argList.size(); + const U32 numDefaultArgs = defaultArgList.size(); + const U32 firstDefaultArgIndex = numArgs - numDefaultArgs; + + for( U32 i = 0; i < numArgs; ++ i ) + { + // Add separator if not first arg. + + if( i > 0 ) + str.append( ',' ); + + // Add type and name. + + String name; + String type; + + sGetArgNameAndType( argList[ i ], type, name ); + + str.append( ' ' ); + str.append( sGetDocTypeString( type ) ); + str.append( ' ' ); + str.append( name ); + + // Add default value, if any. + + if( i >= firstDefaultArgIndex ) + { + str.append( '=' ); + str.append( defaultArgList[ i - firstDefaultArgIndex ] ); + } + } + + if( numArgs > 0 ) + str.append( ' ' ); + str.append( ')' ); + } + else + { + // No extended function header. Try to parse out the argument + // list from the usage string. + + const char* argListStart; + const char* argListEnd; + + if( sFindArgumentListSubstring( mUsage, argListStart, argListEnd ) ) + str.append( argListStart, argListEnd - argListStart ); + else if( mType == ConsoleFunctionType && mCode ) + { + // This isn't correct but the nonsense console stuff is set up such that all + // functions that have no function bodies are keyed to offset 0 to indicate "no code." + // This loses the association with the original function definition so we can't really + // tell here what the actual prototype is except if we searched though the entire opcode + // stream for the corresponding OP_FUNC_DECL (which would require dealing with the + // variable-size instructions). + + if( !mFunctionOffset ) + return "()"; + + String args = mCode->getFunctionArgs( mFunctionOffset ); + if( args.isEmpty() ) + return "()"; + + str.append( "( " ); + str.append( args ); + str.append( " )" ); + } + } + + return str.end(); +} + +String Namespace::Entry::getPrototypeString() const +{ + StringBuilder str; + + // Start with return type. + + if( mHeader && mHeader->mReturnString ) + { + str.append( sGetDocTypeString( mHeader->mReturnString ) ); + str.append( ' ' ); + } + else + switch( mType ) + { + case StringCallbackType: + str.append( "string " ); + break; + + case IntCallbackType: + str.append( "int " ); + break; + + case FloatCallbackType: + str.append( "float " ); + break; + + case VoidCallbackType: + str.append( "void " ); + break; + + case BoolCallbackType: + str.append( "bool " ); + break; + + case ScriptCallbackType: + break; + } + + // Add function name and arguments. + + if( mType == ScriptCallbackType ) + str.append( cb.mCallbackName ); + else + str.append( mFunctionName ); + + str.append( getArgumentsString() ); + + return str.end(); +} + +//----------------------------------------------------------------------------- + +StringTableEntry Namespace::mActivePackages[Namespace::MaxActivePackages]; +U32 Namespace::mNumActivePackages = 0; +U32 Namespace::mOldNumActivePackages = 0; + +bool Namespace::isPackage(StringTableEntry name) +{ + for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext) + if(walk->mPackage == name) + return true; + return false; +} + +U32 Namespace::getActivePackagesCount() +{ + return mNumActivePackages; +} + +StringTableEntry Namespace::getActivePackage(U32 index) +{ + if( index >= mNumActivePackages ) + return StringTable->EmptyString(); + + return mActivePackages[index]; +} + +void Namespace::activatePackage(StringTableEntry name) +{ + if(mNumActivePackages == MaxActivePackages) + { + Con::printf("ActivatePackage(%s) failed - Max package limit reached: %d", name, MaxActivePackages); + return; + } + if(!name) + return; + + // see if this one's already active + for(U32 i = 0; i < mNumActivePackages; i++) + if(mActivePackages[i] == name) + return; + + // kill the cache + trashCache(); + + // find all the package namespaces... + for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext) + { + if(walk->mPackage == name) + { + Namespace *parent = Namespace::find(walk->mName); + // hook the parent + walk->mParent = parent->mParent; + parent->mParent = walk; + + // now swap the entries: + Entry *ew; + for(ew = parent->mEntryList; ew; ew = ew->mNext) + ew->mNamespace = walk; + + for(ew = walk->mEntryList; ew; ew = ew->mNext) + ew->mNamespace = parent; + + ew = walk->mEntryList; + walk->mEntryList = parent->mEntryList; + parent->mEntryList = ew; + } + } + mActivePackages[mNumActivePackages++] = name; +} + +void Namespace::deactivatePackage(StringTableEntry name) +{ + U32 oldNumActivePackages = mNumActivePackages; + + // Remove all packages down to the given one + deactivatePackageStack( name ); + + // Now add back all packages that followed the given one + if(!oldNumActivePackages) + return; + for(U32 i = mNumActivePackages+1; i < oldNumActivePackages; i++) + activatePackage(mActivePackages[i]); +} + +void Namespace::deactivatePackageStack(StringTableEntry name) +{ + S32 i, j; + for(i = 0; i < mNumActivePackages; i++) + if(mActivePackages[i] == name) + break; + if(i == mNumActivePackages) + return; + + trashCache(); + + // Remove all packages down to the given one + for(j = mNumActivePackages - 1; j >= i; j--) + { + // gotta unlink em in reverse order... + for(Namespace *walk = mNamespaceList; walk; walk = walk->mNext) + { + if(walk->mPackage == mActivePackages[j]) + { + Namespace *parent = Namespace::find(walk->mName); + // hook the parent + parent->mParent = walk->mParent; + walk->mParent = NULL; + + // now swap the entries: + Entry *ew; + for(ew = parent->mEntryList; ew; ew = ew->mNext) + ew->mNamespace = walk; + + for(ew = walk->mEntryList; ew; ew = ew->mNext) + ew->mNamespace = parent; + + ew = walk->mEntryList; + walk->mEntryList = parent->mEntryList; + parent->mEntryList = ew; + } + } + } + mNumActivePackages = i; +} + +void Namespace::unlinkPackages() +{ + mOldNumActivePackages = mNumActivePackages; + if(!mNumActivePackages) + return; + deactivatePackageStack(mActivePackages[0]); +} + +void Namespace::relinkPackages() +{ + if(!mOldNumActivePackages) + return; + for(U32 i = 0; i < mOldNumActivePackages; i++) + activatePackage(mActivePackages[i]); +} + + +DefineEngineFunction(isPackage, bool, ( String identifier ),, + "@brief Returns true if the identifier is the name of a declared package.\n\n" + "@ingroup Packages\n") +{ + StringTableEntry name = StringTable->insert(identifier.c_str()); + return Namespace::isPackage(name); +} + +DefineEngineFunction(activatePackage, void, ( String packageName ),, + "@brief Activates an existing package.\n\n" + "The activation occurs by updating the namespace linkage of existing functions and methods. " + "If the package is already activated the function does nothing.\n" + "@ingroup Packages\n") +{ + StringTableEntry name = StringTable->insert(packageName.c_str()); + Namespace::activatePackage(name); +} + +DefineEngineFunction(deactivatePackage, void, ( String packageName ),, + "@brief Deactivates a previously activated package.\n\n" + "The package is deactivated by removing its namespace linkages to any function or method. " + "If there are any packages above this one in the stack they are deactivated as well. " + "If the package is not on the stack this function does nothing.\n" + "@ingroup Packages\n") +{ + StringTableEntry name = StringTable->insert(packageName.c_str()); + Namespace::deactivatePackage(name); +} + +DefineEngineFunction(getPackageList, const char*, (),, + "@brief Returns a space delimited list of the active packages in stack order.\n\n" + "@ingroup Packages\n") +{ + if( Namespace::getActivePackagesCount() == 0 ) + return ""; + + // Determine size of return buffer + dsize_t buffersize = 0; + for( U32 i = 0; i < Namespace::getActivePackagesCount(); ++i ) + { + buffersize += dStrlen(Namespace::getActivePackage(i)) + 1; + } + + U32 maxBufferSize = buffersize + 1; + char* returnBuffer = Con::getReturnBuffer(maxBufferSize); + U32 returnLen = 0; + for( U32 i = 0; i < Namespace::getActivePackagesCount(); ++i ) + { + dSprintf(returnBuffer + returnLen, maxBufferSize - returnLen, "%s ", Namespace::getActivePackage(i)); + returnLen = dStrlen(returnBuffer); + } + + // Trim off the last extra space + if (returnLen > 0 && returnBuffer[returnLen - 1] == ' ') + returnBuffer[returnLen - 1] = '\0'; + + return returnBuffer; +} diff --git a/Engine/source/console/consoleInternal.h b/Engine/source/console/consoleInternal.h new file mode 100644 index 000000000..0a9c9060e --- /dev/null +++ b/Engine/source/console/consoleInternal.h @@ -0,0 +1,577 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _CONSOLEINTERNAL_H_ +#define _CONSOLEINTERNAL_H_ + +#ifndef _STRINGFUNCTIONS_H_ + #include "core/strings/stringFunctions.h" +#endif +#ifndef _STRINGTABLE_H_ + #include "core/stringTable.h" +#endif +#ifndef _CONSOLETYPES_H + #include "console/consoleTypes.h" +#endif +#ifndef _CONSOLEOBJECT_H_ + #include "console/simObject.h" +#endif +#ifndef _DATACHUNKER_H_ + #include "core/dataChunker.h" +#endif + + +/// @ingroup console_system Console System +/// @{ + + +class ExprEvalState; +struct FunctionDecl; +class CodeBlock; +class AbstractClassRep; + + +/// A dictionary of function entries. +/// +/// Namespaces are used for dispatching calls in the console system. +class Namespace +{ + enum { + MaxActivePackages = 512, + }; + + static U32 mNumActivePackages; + static U32 mOldNumActivePackages; + static StringTableEntry mActivePackages[MaxActivePackages]; + + public: + StringTableEntry mName; + StringTableEntry mPackage; + + Namespace *mParent; + Namespace *mNext; + AbstractClassRep *mClassRep; + U32 mRefCountToParent; + + const char* mUsage; + + /// Script defined usage strings need to be cleaned up. This + /// field indicates whether or not the usage was set from script. + bool mCleanUpUsage; + + /// A function entry in the namespace. + struct Entry + { + enum + { + ScriptCallbackType = -3, + GroupMarker = -2, + InvalidFunctionType = -1, + ConsoleFunctionType, + StringCallbackType, + IntCallbackType, + FloatCallbackType, + VoidCallbackType, + BoolCallbackType + }; + + /// Link back to the namespace to which the entry belongs. + Namespace* mNamespace; + + /// Next function entry in the hashtable link chain of the namespace. + Entry* mNext; + + /// Name of this function. + StringTableEntry mFunctionName; + + /// + S32 mType; + + /// Min number of arguments expected by this function. + S32 mMinArgs; + + /// Max number of arguments expected by this function. If zero, + /// function takes an arbitrary number of arguments. + S32 mMaxArgs; + + /// Name of the package to which this function belongs. + StringTableEntry mPackage; + + /// Whether this function is included only in TORQUE_TOOLS builds. + bool mToolOnly; + + /// Usage string for documentation. + const char* mUsage; + + /// Extended console function information. + ConsoleFunctionHeader* mHeader; + + /// The compiled script code if this is a script function. + CodeBlock* mCode; + + /// The offset in the compiled script code at which this function begins. + U32 mFunctionOffset; + + /// If it's a script function, this is the line of the declaration in code. + /// @note 0 for functions read from legacy DSOs that have no line number information. + U32 mFunctionLineNumber; + + union CallbackUnion { + StringCallback mStringCallbackFunc; + IntCallback mIntCallbackFunc; + VoidCallback mVoidCallbackFunc; + FloatCallback mFloatCallbackFunc; + BoolCallback mBoolCallbackFunc; + const char *mGroupName; + const char *mCallbackName; + } cb; + + Entry(); + + /// + void clear(); + + /// + const char *execute( S32 argc, const char** argv, ExprEvalState* state ); + + /// Return a one-line documentation text string for the function. + String getBriefDescription( String* outRemainingDocText = NULL ) const; + + /// Get the auto-doc string for this function. This string does not included prototype information. + String getDocString() const; + + /// Return a string describing the arguments the function takes including default argument values. + String getArgumentsString() const; + + /// Return a full prototype string for the function including return type, function name, + /// and arguments. + String getPrototypeString() const; + }; + + Entry* mEntryList; + + Entry** mHashTable; + + U32 mHashSize; + U32 mHashSequence; ///< @note The hash sequence is used by the autodoc console facility + /// as a means of testing reference state. + + Namespace(); + ~Namespace(); + + void addFunction( StringTableEntry name, CodeBlock* cb, U32 functionOffset, const char* usage = NULL, U32 lineNumber = 0 ); + void addCommand( StringTableEntry name, StringCallback, const char *usage, S32 minArgs, S32 maxArgs, bool toolOnly = false, ConsoleFunctionHeader* header = NULL ); + void addCommand( StringTableEntry name, IntCallback, const char *usage, S32 minArgs, S32 maxArgs, bool toolOnly = false, ConsoleFunctionHeader* header = NULL ); + void addCommand( StringTableEntry name, FloatCallback, const char *usage, S32 minArgs, S32 maxArgs, bool toolOnly = false, ConsoleFunctionHeader* header = NULL ); + void addCommand( StringTableEntry name, VoidCallback, const char *usage, S32 minArgs, S32 maxArgs, bool toolOnly = false, ConsoleFunctionHeader* header = NULL ); + void addCommand( StringTableEntry name, BoolCallback, const char *usage, S32 minArgs, S32 maxArgs, bool toolOnly = false, ConsoleFunctionHeader* header = NULL ); + + void addScriptCallback( const char *funcName, const char *usage, ConsoleFunctionHeader* header = NULL ); + + void markGroup(const char* name, const char* usage); + char * lastUsage; + + /// Returns true if this namespace represents an engine defined + /// class and is not just a script defined class namespace. + bool isClass() const { return mClassRep && mClassRep->getNameSpace() == this; } + + void getEntryList(VectorPtr *); + + /// Return the name of this namespace. + StringTableEntry getName() const { return mName; } + + /// Return the superordinate namespace to this namespace. Symbols are inherited from + /// this namespace. + Namespace* getParent() const { return mParent; } + + /// Return the topmost package in the parent hierarchy of this namespace + /// that still refers to the same namespace. If packages are active and + /// adding to this namespace, then they will be linked in-between the namespace + /// they are adding to and its real parent namespace. + Namespace* getPackageRoot() + { + Namespace* walk = this; + while( walk->mParent && walk->mParent->mName == mName ) + walk = walk->mParent; + + return walk; + } + + /// Return the package in which this namespace is defined. + StringTableEntry getPackage() const { return mPackage; } + + /// Increase the count on the reference that this namespace + /// holds to its parent. + /// @note Must not be called on namespaces coming from packages. + void incRefCountToParent() + { + AssertFatal( mPackage == NULL, "Namespace::incRefCountToParent - Must not be called on a namespace coming from a package!" ); + mRefCountToParent ++; + } + + /// Decrease the count on the reference that this namespace + /// holds to its parent. + /// @note Must not be called on namespaces coming from packages. + void decRefCountToParent() + { + unlinkClass( NULL ); + } + + Entry *lookup(StringTableEntry name); + Entry *lookupRecursive(StringTableEntry name); + Entry *createLocalEntry(StringTableEntry name); + void buildHashTable(); + void clearEntries(); + bool classLinkTo(Namespace *parent); + bool unlinkClass(Namespace *parent); + void getUniqueEntryLists( Namespace *other, VectorPtr *outThisList, VectorPtr *outOtherList ); + + const char *tabComplete(const char *prevText, S32 baseLen, bool fForward); + + static U32 mCacheSequence; + static DataChunker mCacheAllocator; + static DataChunker mAllocator; + static void trashCache(); + static Namespace *mNamespaceList; + static Namespace *mGlobalNamespace; + + static void init(); + static void shutdown(); + static Namespace *global(); + + static Namespace *find(StringTableEntry name, StringTableEntry package=NULL); + + static void activatePackage(StringTableEntry name); + static void deactivatePackage(StringTableEntry name); + static void deactivatePackageStack(StringTableEntry name); + static void dumpClasses( bool dumpScript = true, bool dumpEngine = true ); + static void dumpFunctions( bool dumpScript = true, bool dumpEngine = true ); + static void printNamespaceEntries(Namespace * g, bool dumpScript = true, bool dumpEngine = true); + static void unlinkPackages(); + static void relinkPackages(); + static bool isPackage(StringTableEntry name); + static U32 getActivePackagesCount(); + static StringTableEntry getActivePackage(U32 index); +}; + +typedef VectorPtr::iterator NamespaceEntryListIterator; + +extern char *typeValueEmpty; + +class Dictionary +{ +public: + + struct Entry + { + enum + { + TypeInternalInt = -3, + TypeInternalFloat = -2, + TypeInternalString = -1, + }; + + StringTableEntry name; + Entry *nextEntry; + S32 type; + + typedef Signal NotifySignal; + + /// The optional notification signal called when + /// a value is assigned to this variable. + NotifySignal *notify; + + /// Usage doc string. + const char* mUsage; + + /// Whether this is a constant that cannot be assigned to. + bool mIsConstant; + + protected: + + // NOTE: This is protected to ensure no one outside + // of this structure is messing with it. + + #pragma warning( push ) + #pragma warning( disable : 4201 ) // warning C4201: nonstandard extension used : nameless struct/union + + // An variable is either a real dynamic type or + // its one exposed from C++ using a data pointer. + // + // We use this nameless union and struct setup + // to optimize the memory usage. + union + { + struct + { + char *sval; + U32 ival; // doubles as strlen when type is TypeInternalString + F32 fval; + U32 bufferLen; + }; + + struct + { + /// The real data pointer. + void *dataPtr; + + /// The enum lookup table for enumerated types. + const EnumTable *enumTable; + }; + }; + + #pragma warning( pop ) // C4201 + + public: + + Entry(StringTableEntry name); + ~Entry(); + + U32 getIntValue() + { + if(type <= TypeInternalString) + return ival; + else + return dAtoi(Con::getData(type, dataPtr, 0, enumTable)); + } + + F32 getFloatValue() + { + if(type <= TypeInternalString) + return fval; + else + return dAtof(Con::getData(type, dataPtr, 0, enumTable)); + } + + const char *getStringValue() + { + if(type == TypeInternalString) + return sval; + if(type == TypeInternalFloat) + return Con::getData(TypeF32, &fval, 0); + else if(type == TypeInternalInt) + return Con::getData(TypeS32, &ival, 0); + else + return Con::getData(type, dataPtr, 0, enumTable); + } + + void setIntValue(U32 val) + { + if( mIsConstant ) + { + Con::errorf( "Cannot assign value to constant '%s'.", name ); + return; + } + + if(type <= TypeInternalString) + { + fval = (F32)val; + ival = val; + if(sval != typeValueEmpty) + { + dFree(sval); + sval = typeValueEmpty; + } + type = TypeInternalInt; + } + else + { + const char *dptr = Con::getData(TypeS32, &val, 0); + Con::setData(type, dataPtr, 0, 1, &dptr, enumTable); + } + + // Fire off the notification if we have one. + if ( notify ) + notify->trigger(); + } + + void setFloatValue(F32 val) + { + if( mIsConstant ) + { + Con::errorf( "Cannot assign value to constant '%s'.", name ); + return; + } + + if(type <= TypeInternalString) + { + fval = val; + ival = static_cast(val); + if(sval != typeValueEmpty) + { + dFree(sval); + sval = typeValueEmpty; + } + type = TypeInternalFloat; + } + else + { + const char *dptr = Con::getData(TypeF32, &val, 0); + Con::setData(type, dataPtr, 0, 1, &dptr, enumTable); + } + + // Fire off the notification if we have one. + if ( notify ) + notify->trigger(); + } + + void setStringValue(const char *value); + }; + + struct HashTableData + { + Dictionary* owner; + S32 size; + S32 count; + Entry **data; + FreeListChunker< Entry > mChunker; + + HashTableData( Dictionary* owner ) + : owner( owner ), size( 0 ), count( 0 ), data( NULL ) {} + }; + + HashTableData* hashTable; + HashTableData ownHashTable; + ExprEvalState *exprState; + + StringTableEntry scopeName; + Namespace *scopeNamespace; + CodeBlock *code; + U32 ip; + + Dictionary(); + ~Dictionary(); + + Entry *lookup(StringTableEntry name); + Entry *add(StringTableEntry name); + void setState(ExprEvalState *state, Dictionary* ref=NULL); + void remove(Entry *); + void reset(); + + void exportVariables( const char *varString, const char *fileName, bool append ); + void exportVariables( const char *varString, Vector *names, Vector *values ); + void deleteVariables( const char *varString ); + + void setVariable(StringTableEntry name, const char *value); + const char *getVariable(StringTableEntry name, bool *valid = NULL); + + U32 getCount() const + { + return hashTable->count; + } + bool isOwner() const + { + return hashTable->owner; + } + + /// @see Con::addVariable + Entry* addVariable( const char *name, + S32 type, + void *dataPtr, + const char* usage ); + + /// @see Con::removeVariable + bool removeVariable(StringTableEntry name); + + /// @see Con::addVariableNotify + void addVariableNotify( const char *name, const Con::NotifyDelegate &callback ); + + /// @see Con::removeVariableNotify + void removeVariableNotify( const char *name, const Con::NotifyDelegate &callback ); + + /// Return the best tab completion for prevText, with the length + /// of the pre-tab string in baseLen. + const char *tabComplete(const char *prevText, S32 baseLen, bool); + + /// Run integrity checks for debugging. + void validate(); +}; + +class ExprEvalState +{ +public: + /// @name Expression Evaluation + /// @{ + + /// + SimObject *thisObject; + Dictionary::Entry *currentVariable; + bool traceOn; + + U32 mStackDepth; + + ExprEvalState(); + ~ExprEvalState(); + + /// @} + + /// @name Stack Management + /// @{ + + /// The stack of callframes. The extra redirection is necessary since Dictionary holds + /// an interior pointer that will become invalid when the object changes address. + Vector< Dictionary* > stack; + + /// + Dictionary globalVars; + + void setCurVarName(StringTableEntry name); + void setCurVarNameCreate(StringTableEntry name); + S32 getIntVariable(); + F64 getFloatVariable(); + const char *getStringVariable(); + void setIntVariable(S32 val); + void setFloatVariable(F64 val); + void setStringVariable(const char *str); + + void pushFrame(StringTableEntry frameName, Namespace *ns); + void popFrame(); + + /// Puts a reference to an existing stack frame + /// on the top of the stack. + void pushFrameRef(S32 stackIndex); + + U32 getStackDepth() const + { + return mStackDepth; + } + + Dictionary& getCurrentFrame() + { + return *( stack[ mStackDepth - 1 ] ); + } + + /// @} + + /// Run integrity checks for debugging. + void validate(); +}; + +namespace Con +{ + /// The current $instantGroup setting. + extern String gInstantGroup; +} + +/// @} + +#endif diff --git a/Engine/source/console/consoleLogger.cpp b/Engine/source/console/consoleLogger.cpp new file mode 100644 index 000000000..aedc9baab --- /dev/null +++ b/Engine/source/console/consoleLogger.cpp @@ -0,0 +1,268 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#include "console/consoleLogger.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" + +Vector ConsoleLogger::mActiveLoggers; +bool ConsoleLogger::smInitialized = false; + +IMPLEMENT_CONOBJECT( ConsoleLogger ); +ConsoleDocClass( ConsoleLogger, + "A class designed to be used as a console consumer and log the data it receives to a file.\n\n" + + "@see dumpConsoleFunctions\n" + "@see dumpConsoleClasses\n" + "@ingroup Logging\n" +); +//----------------------------------------------------------------------------- + +ConsoleLogger::ConsoleLogger() +{ + mFilename = NULL; + mLogging = false; + mAppend = false; + + mLevel = ConsoleLogEntry::Normal; +} + +//----------------------------------------------------------------------------- + +ConsoleLogger::ConsoleLogger( const char *fileName, bool append ) +{ + mLogging = false; + + mLevel = ConsoleLogEntry::Normal; + mFilename = StringTable->insert( fileName ); + mAppend = append; + + init(); +} + +//----------------------------------------------------------------------------- + +ImplementEnumType( LogLevel, + "@brief Priority levels for logging entries\n\n" + "@ingroup Logging") + { ConsoleLogEntry::Normal, "normal", "Lowest priority level, no highlighting." }, + { ConsoleLogEntry::Warning, "warning", "Mid level priority, tags and highlights possible issues in blue." }, + { ConsoleLogEntry::Error, "error", "Highest priority level, extreme emphasis on this entry. Highlighted in red." }, +EndImplementEnumType; + +void ConsoleLogger::initPersistFields() +{ + addGroup( "Logging" ); + addField( "level", TYPEID< ConsoleLogEntry::Level >(), Offset( mLevel, ConsoleLogger ), "Determines the priority level and attention the logged entry gets when recorded\n\n" ); + endGroup( "Logging" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +bool ConsoleLogger::processArguments( S32 argc, const char **argv ) +{ + if( argc == 0 ) + return false; + + bool append = false; + + if( argc == 2 ) + append = dAtob( argv[1] ); + + mAppend = append; + mFilename = StringTable->insert( argv[0] ); + + if( init() ) + { + attach(); + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- + +ConsoleLogger::~ConsoleLogger() +{ + detach(); +} + +//----------------------------------------------------------------------------- + +bool ConsoleLogger::init() +{ + if( smInitialized ) + return true; + + Con::addConsumer( ConsoleLogger::logCallback ); + smInitialized = true; + + return true; +} + +//----------------------------------------------------------------------------- + +bool ConsoleLogger::attach() +{ + if( mFilename == NULL ) + { + Con::errorf( "ConsoleLogger failed to attach: no filename supplied." ); + return false; + } + + // Check to see if this is initialized before using it + if( !smInitialized ) + { + if( !init() ) + { + Con::errorf( "ConsoleLogger failed to initalize." ); + return false; + } + } + + if( mLogging ) + return false; + + // Open the filestream + mStream.open( mFilename, ( mAppend ? Torque::FS::File::WriteAppend : Torque::FS::File::Write ) ); + + // Add this to list of active loggers + mActiveLoggers.push_back( this ); + mLogging = true; + + return true; +} + +//----------------------------------------------------------------------------- + +bool ConsoleLogger::detach() +{ + + // Make sure this is valid before messing with it + if( !smInitialized ) + { + if( !init() ) + { + return false; + } + } + + if( !mLogging ) + return false; + + // Close filestream + mStream.close(); + + // Remove this object from the list of active loggers + for( int i = 0; i < mActiveLoggers.size(); i++ ) + { + if( mActiveLoggers[i] == this ) + { + mActiveLoggers.erase( i ); + mLogging = false; + return true; + } + } + + return false; // If this happens, it's bad... +} + +//----------------------------------------------------------------------------- + +void ConsoleLogger::logCallback( U32 level, const char *consoleLine ) +{ + + ConsoleLogger *curr; + + // Loop through active consumers and send them the message + for( int i = 0; i < mActiveLoggers.size(); i++ ) + { + curr = mActiveLoggers[i]; + + // If the log level is within the log threshhold, log it + if( curr->mLevel <= level ) + curr->log( consoleLine ); + } +} + +//----------------------------------------------------------------------------- + +void ConsoleLogger::log( const char *consoleLine ) +{ + // Check to see if this is intalized before using it + if( !smInitialized ) + { + if( !init() ) + { + Con::errorf( "I don't know how this happened, but log called on this without it being initialized" ); + return; + } + } + + mStream.writeLine( (U8 *)consoleLine ); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( ConsoleLogger, attach, bool, 2, 2, "() Attaches the logger to the console and begins writing to file" + "@tsexample\n" + "// Create the logger\n" + "// Will automatically start writing to testLogging.txt with normal priority\n" + "new ConsoleLogger(logger, \"testLogging.txt\", false);\n\n" + "// Send something to the console, with the logger consumes and writes to file\n" + "echo(\"This is logged to the file\");\n\n" + "// Stop logging, but do not delete the logger\n" + "logger.detach();\n\n" + "echo(\"This is not logged to the file\");\n\n" + "// Attach the logger to the console again\n" + "logger.attach();\n\n" + "// Logging has resumed\n" + "echo(\"Logging has resumed\");" + "@endtsexample\n\n") +{ + ConsoleLogger *logger = static_cast( object ); + return logger->attach(); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( ConsoleLogger, detach, bool, 2, 2, "() Detaches the logger from the console and stops writing to file" + "@tsexample\n" + "// Create the logger\n" + "// Will automatically start writing to testLogging.txt with normal priority\n" + "new ConsoleLogger(logger, \"testLogging.txt\", false);\n\n" + "// Send something to the console, with the logger consumes and writes to file\n" + "echo(\"This is logged to the file\");\n\n" + "// Stop logging, but do not delete the logger\n" + "logger.detach();\n\n" + "echo(\"This is not logged to the file\");\n\n" + "// Attach the logger to the console again\n" + "logger.attach();\n\n" + "// Logging has resumed\n" + "echo(\"Logging has resumed\");" + "@endtsexample\n\n") +{ + ConsoleLogger *logger = static_cast( object ); + return logger->detach(); +} diff --git a/Engine/source/console/consoleLogger.h b/Engine/source/console/consoleLogger.h new file mode 100644 index 000000000..dfcb14251 --- /dev/null +++ b/Engine/source/console/consoleLogger.h @@ -0,0 +1,126 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _CONSOLE_LOGGER_H_ +#define _CONSOLE_LOGGER_H_ + +#ifndef _SIMOBJECT_H_ + #include "console/simObject.h" +#endif +#ifndef _CONSOLE_H_ + #include "console/console.h" +#endif +#ifndef _FILESTREAM_H_ + #include "core/stream/fileStream.h" +#endif + + +/// @ingroup console_system Console System +/// @{ + +typedef ConsoleLogEntry::Level LogLevel; +DefineEnumType( LogLevel ); + +/// A class designed to be used as a console consumer and log +/// the data it receives to a file. +class ConsoleLogger : public SimObject +{ + typedef SimObject Parent; + + private: + + bool mLogging; ///< True if it is currently consuming and logging + FileStream mStream; ///< File stream this object writes to + static bool smInitialized; ///< This is for use with the default constructor + bool mAppend; ///< If false, it will clear the file before logging to it. + StringTableEntry mFilename; ///< The file name to log to. + + /// List of active ConsoleLoggers to send log messages to + static Vector mActiveLoggers; + + /// The log function called by the consumer callback + /// @param consoleLine Line of text to log + void log( const char *consoleLine ); + + /// Utility function, sets up the object (for script interface) returns true if successful + bool init(); + + public: + + // @name Public console variables + /// @{ + ConsoleLogEntry::Level mLevel; ///< The level of log messages to log + /// @} + + DECLARE_CONOBJECT( ConsoleLogger ); + + static void initPersistFields(); + + /// Console constructor + /// + /// @code + /// // Example script constructor usage. + /// %obj = new ConsoleLogger( objName, logFileName, [append = false] ); + /// @endcode + bool processArguments( S32 argc, const char **argv ); + + /// Default constructor, make sure to initalize + ConsoleLogger(); + + /// Constructor + /// @param fileName File name to log to + /// @param append If false, it will clear the file, then start logging, else it will append + ConsoleLogger( const char *fileName, bool append = false ); + + /// Destructor + ~ConsoleLogger(); + + /// Attach to the console and begin logging + /// + /// Returns true if the action is successful + bool attach(); + + /// Detach from the console and stop logging + /// + /// Returns true if the action is successful + bool detach(); + + /// Sets the level of console messages to log. + /// + /// @param level Log level. Only items of the specified level or + /// lower are logged. + /// @see ConsoleLogEntry::Level + void setLogLevel( ConsoleLogEntry::Level level ); + + /// Returns the level of console messages to log + ConsoleLogEntry::Level getLogLevel() const; + + /// The callback for the console consumer + /// + /// @note This is a global callback, not executed per-instance. + /// @see Con::addConsumer + static void logCallback( U32 level, const char *consoleLine ); +}; + +/// @} + +#endif // _CONSOLE_LOGGER_H_ diff --git a/Engine/source/console/consoleObject.cpp b/Engine/source/console/consoleObject.cpp new file mode 100644 index 000000000..e4cd744ad --- /dev/null +++ b/Engine/source/console/consoleObject.cpp @@ -0,0 +1,836 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/consoleObject.h" + +#include "core/stringTable.h" +#include "core/crc.h" +#include "core/dataChunker.h" +#include "console/console.h" +#include "console/consoleInternal.h" +#include "console/typeValidators.h" +#include "console/simObject.h" +#include "console/engineTypes.h" +#include "console/engineAPI.h" + + +IMPLEMENT_SCOPE( ConsoleAPI, Console,, + "Functionality related to the legacy TorqueScript console system." ); + +IMPLEMENT_NONINSTANTIABLE_CLASS( ConsoleObject, + "Legacy console system root class. Will disappear." ) +END_IMPLEMENT_CLASS; + + +AbstractClassRep * AbstractClassRep::classLinkList = NULL; +AbstractClassRep::FieldList sg_tempFieldList( __FILE__, __LINE__ ); +U32 AbstractClassRep::NetClassCount [NetClassGroupsCount][NetClassTypesCount] = {{0, },}; +U32 AbstractClassRep::NetClassBitSize[NetClassGroupsCount][NetClassTypesCount] = {{0, },}; + +AbstractClassRep ** AbstractClassRep::classTable[NetClassGroupsCount][NetClassTypesCount]; + +U32 AbstractClassRep::classCRC[NetClassGroupsCount] = {CRC::INITIAL_CRC_VALUE, }; +bool AbstractClassRep::initialized = false; + + + +//----------------------------------------------------------------------------- + + +void AbstractClassRep::init() +{ + // Only add the renderable and selectable globals for + // classes derived from SceneObject which are the only + // objects for which these work. + if ( isSubclassOf( "SceneObject" ) ) + { + Con::addVariable( avar( "$%s::isRenderable", getClassName() ), TypeBool, &mIsRenderEnabled, + "@brief Disables rendering of all instances of this type.\n\n" ); + + Con::addVariable( avar( "$%s::isSelectable", getClassName() ), TypeBool, &mIsSelectionEnabled, + "@brief Disables selection of all instances of this type.\n\n" ); + } +} + +const AbstractClassRep::Field *AbstractClassRep::findField(StringTableEntry name) const +{ + for(U32 i = 0; i < mFieldList.size(); i++) + if(mFieldList[i].pFieldname == name) + return &mFieldList[i]; + + return NULL; +} + +AbstractClassRep* AbstractClassRep::findClassRep(const char* in_pClassName) +{ + AssertFatal(initialized, + "AbstractClassRep::findClassRep() - Tried to find an AbstractClassRep before AbstractClassRep::initialize()."); + + for (AbstractClassRep *walk = classLinkList; walk; walk = walk->nextClass) + if (!dStricmp(walk->getClassName(), in_pClassName)) + return walk; + + return NULL; +} + +AbstractClassRep* AbstractClassRep::findClassRep( U32 groupId, U32 typeId, U32 classId ) +{ + AssertFatal(initialized, + "AbstractClassRep::findClasRep() - Tried to create an object before AbstractClassRep::initialize()."); + AssertFatal(classId < NetClassCount[groupId][typeId], + "AbstractClassRep::findClassRep() - Class id out of range."); + AssertFatal(classTable[groupId][typeId][classId] != NULL, + "AbstractClassRep::findClassRep() - No class with requested ID type."); + + // Look up the specified class and create it. + if(classTable[groupId][typeId][classId]) + return classTable[groupId][typeId][classId]; + + return NULL; +} + +//-------------------------------------- +void AbstractClassRep::registerClassRep(AbstractClassRep* in_pRep) +{ + AssertFatal(in_pRep != NULL, "AbstractClassRep::registerClassRep was passed a NULL pointer!"); + +#ifdef TORQUE_DEBUG // assert if this class is already registered. + for(AbstractClassRep *walk = classLinkList; walk; walk = walk->nextClass) + { + AssertFatal(dStrcmp(in_pRep->mClassName, walk->mClassName), + "Duplicate class name registered in AbstractClassRep::registerClassRep()"); + } +#endif + + in_pRep->nextClass = classLinkList; + classLinkList = in_pRep; +} + +//-------------------------------------- +void AbstractClassRep::removeClassRep(AbstractClassRep* in_pRep) +{ + for( AbstractClassRep *walk = classLinkList; walk; walk = walk->nextClass ) + { + // This is the case that will most likely get hit. + if( walk->nextClass == in_pRep ) + walk->nextClass = walk->nextClass->nextClass; + else if( walk == in_pRep ) + { + AssertFatal( in_pRep == classLinkList, "Pat failed in his logic for un linking RuntimeClassReps from the class linked list" ); + classLinkList = in_pRep->nextClass; + } + } +} + +//-------------------------------------- + +ConsoleObject* AbstractClassRep::create(const char* in_pClassName) +{ + AssertFatal(initialized, + "AbstractClassRep::create() - Tried to create an object before AbstractClassRep::initialize()."); + + const AbstractClassRep *rep = AbstractClassRep::findClassRep(in_pClassName); + if(rep) + return rep->create(); + + AssertWarn(0, avar("Couldn't find class rep for dynamic class: %s", in_pClassName)); + return NULL; +} + +//-------------------------------------- +ConsoleObject* AbstractClassRep::create(const U32 groupId, const U32 typeId, const U32 in_classId) +{ + AbstractClassRep* classRep = findClassRep( groupId, typeId, in_classId ); + if( !classRep ) + return NULL; + + return classRep->create(); +} + +//-------------------------------------- + +static S32 QSORT_CALLBACK ACRCompare(const void *aptr, const void *bptr) +{ + const AbstractClassRep *a = *((const AbstractClassRep **) aptr); + const AbstractClassRep *b = *((const AbstractClassRep **) bptr); + + if(a->mClassType != b->mClassType) + return a->mClassType - b->mClassType; + return dStrnatcasecmp(a->getClassName(), b->getClassName()); +} + +void AbstractClassRep::initialize() +{ + AssertFatal(!initialized, "Duplicate call to AbstractClassRep::initialize()!"); + Vector dynamicTable(__FILE__, __LINE__); + + AbstractClassRep *walk; + + // Initialize namespace references... + for (walk = classLinkList; walk; walk = walk->nextClass) + { + walk->mNamespace = Con::lookupNamespace(StringTable->insert(walk->getClassName())); + walk->mNamespace->mUsage = walk->getDocString(); + walk->mNamespace->mClassRep = walk; + } + + // Initialize field lists... (and perform other console registration). + for (walk = classLinkList; walk; walk = walk->nextClass) + { + // sg_tempFieldList is used as a staging area for field lists + // (see addField, addGroup, etc.) + sg_tempFieldList.setSize(0); + + walk->init(); + + // So if we have things in it, copy it over... + if (sg_tempFieldList.size() != 0) + walk->mFieldList = sg_tempFieldList; + + // And of course delete it every round. + sg_tempFieldList.clear(); + } + + // Calculate counts and bit sizes for the various NetClasses. + for (U32 group = 0; group < NetClassGroupsCount; group++) + { + U32 groupMask = 1 << group; + + // Specifically, for each NetClass of each NetGroup... + for(U32 type = 0; type < NetClassTypesCount; type++) + { + // Go through all the classes and find matches... + for (walk = classLinkList; walk; walk = walk->nextClass) + { + if(walk->mClassType == type && walk->mClassGroupMask & groupMask) + dynamicTable.push_back(walk); + } + + // Set the count for this NetGroup and NetClass + NetClassCount[group][type] = dynamicTable.size(); + if(!NetClassCount[group][type]) + continue; // If no classes matched, skip to next. + + // Sort by type and then by name. + dQsort((void *) &dynamicTable[0], dynamicTable.size(), sizeof(AbstractClassRep *), ACRCompare); + + // Allocate storage in the classTable + classTable[group][type] = new AbstractClassRep*[NetClassCount[group][type]]; + + // Fill this in and assign class ids for this group. + for(U32 i = 0; i < NetClassCount[group][type];i++) + { + classTable[group][type][i] = dynamicTable[i]; + dynamicTable[i]->mClassId[group] = i; + } + + // And calculate the size of bitfields for this group and type. + NetClassBitSize[group][type] = + getBinLog2(getNextPow2(NetClassCount[group][type] + 1)); + AssertFatal(NetClassCount[group][type] < (1 << NetClassBitSize[group][type]), "NetClassBitSize too small!"); + + dynamicTable.clear(); + } + } + + // Ok, we're golden! + initialized = true; +} + +void AbstractClassRep::shutdown() +{ + AssertFatal( initialized, "AbstractClassRep::shutdown - not initialized" ); + + // Release storage allocated to the class table. + + for (U32 group = 0; group < NetClassGroupsCount; group++) + for(U32 type = 0; type < NetClassTypesCount; type++) + if( classTable[ group ][ type ] ) + SAFE_DELETE_ARRAY( classTable[ group ][ type ] ); + + initialized = false; +} + +AbstractClassRep *AbstractClassRep::getCommonParent( const AbstractClassRep *otherClass ) const +{ + // CodeReview: This may be a noob way of doing it. There may be some kind of + // super-spiffy algorithm to do what the code below does, but this appeared + // to make sense to me, and it is pretty easy to see what it is doing [6/23/2007 Pat] + + static VectorPtr thisClassHeirarchy; + thisClassHeirarchy.clear(); + + AbstractClassRep *walk = const_cast( this ); + + while( walk != NULL ) + { + thisClassHeirarchy.push_front( walk ); + walk = walk->getParentClass(); + } + + static VectorPtr compClassHeirarchy; + compClassHeirarchy.clear(); + walk = const_cast( otherClass ); + while( walk != NULL ) + { + compClassHeirarchy.push_front( walk ); + walk = walk->getParentClass(); + } + + // Make sure we only iterate over the list the number of times we can + S32 maxIterations = getMin( compClassHeirarchy.size(), thisClassHeirarchy.size() ); + + U32 i = 0; + for( ; i < maxIterations; i++ ) + { + if( compClassHeirarchy[i] != thisClassHeirarchy[i] ) + break; + } + + return compClassHeirarchy[i]; +} + +//------------------------------------------------------------------------------ +//-------------------------------------- ConsoleObject + +static char replacebuf[1024]; +static char* suppressSpaces(const char* in_pname) +{ + U32 i = 0; + char chr; + do + { + chr = in_pname[i]; + replacebuf[i++] = (chr != 32) ? chr : '_'; + } while(chr); + + return replacebuf; +} + +void ConsoleObject::addGroup(const char* in_pGroupname, const char* in_pGroupDocs) +{ + // Remove spaces. + char* pFieldNameBuf = suppressSpaces(in_pGroupname); + + // Append group type to fieldname. + dStrcat(pFieldNameBuf, "_begingroup"); + + // Create Field. + AbstractClassRep::Field f; + f.pFieldname = StringTable->insert(pFieldNameBuf); + f.pGroupname = in_pGroupname; + + if(in_pGroupDocs) + f.pFieldDocs = in_pGroupDocs; + + f.type = AbstractClassRep::StartGroupFieldType; + f.elementCount = 0; + f.groupExpand = false; + f.validator = NULL; + f.setDataFn = &defaultProtectedSetFn; + f.getDataFn = &defaultProtectedGetFn; + + // Add to field list. + sg_tempFieldList.push_back(f); +} + +void ConsoleObject::endGroup(const char* in_pGroupname) +{ + // Remove spaces. + char* pFieldNameBuf = suppressSpaces(in_pGroupname); + + // Append group type to fieldname. + dStrcat(pFieldNameBuf, "_endgroup"); + + // Create Field. + AbstractClassRep::Field f; + f.pFieldname = StringTable->insert(pFieldNameBuf); + f.pGroupname = in_pGroupname; + f.type = AbstractClassRep::EndGroupFieldType; + f.groupExpand = false; + f.validator = NULL; + f.setDataFn = &defaultProtectedSetFn; + f.getDataFn = &defaultProtectedGetFn; + f.elementCount = 0; + + // Add to field list. + sg_tempFieldList.push_back(f); +} + +void ConsoleObject::addArray( const char *arrayName, S32 count ) +{ + char *nameBuff = suppressSpaces(arrayName); + dStrcat(nameBuff, "_beginarray"); + + // Create Field. + AbstractClassRep::Field f; + f.pFieldname = StringTable->insert(nameBuff); + f.pGroupname = arrayName; + + f.type = AbstractClassRep::StartArrayFieldType; + f.elementCount = count; + f.groupExpand = false; + f.validator = NULL; + f.setDataFn = &defaultProtectedSetFn; + f.getDataFn = &defaultProtectedGetFn; + + // Add to field list. + sg_tempFieldList.push_back(f); +} + +void ConsoleObject::endArray( const char *arrayName ) +{ + char *nameBuff = suppressSpaces(arrayName); + dStrcat(nameBuff, "_endarray"); + + // Create Field. + AbstractClassRep::Field f; + f.pFieldname = StringTable->insert(nameBuff); + f.pGroupname = arrayName; + f.type = AbstractClassRep::EndArrayFieldType; + f.groupExpand = false; + f.validator = NULL; + f.setDataFn = &defaultProtectedSetFn; + f.getDataFn = &defaultProtectedGetFn; + f.elementCount = 0; + + // Add to field list. + sg_tempFieldList.push_back(f); +} + +void ConsoleObject::addField(const char* in_pFieldname, + const U32 in_fieldType, + const dsize_t in_fieldOffset, + const char* in_pFieldDocs, + U32 flags ) +{ + addField( + in_pFieldname, + in_fieldType, + in_fieldOffset, + 1, + in_pFieldDocs, + flags ); +} + +void ConsoleObject::addProtectedField(const char* in_pFieldname, + const U32 in_fieldType, + const dsize_t in_fieldOffset, + AbstractClassRep::SetDataNotify in_setDataFn, + AbstractClassRep::GetDataNotify in_getDataFn, + const char* in_pFieldDocs, + U32 flags ) +{ + addProtectedField( + in_pFieldname, + in_fieldType, + in_fieldOffset, + in_setDataFn, + in_getDataFn, + 1, + in_pFieldDocs, + flags ); +} + + +void ConsoleObject::addField(const char* in_pFieldname, + const U32 in_fieldType, + const dsize_t in_fieldOffset, + const U32 in_elementCount, + const char* in_pFieldDocs, + U32 flags ) +{ + AbstractClassRep::Field f; + f.pFieldname = StringTable->insert(in_pFieldname); + + if(in_pFieldDocs) + f.pFieldDocs = in_pFieldDocs; + + f.type = in_fieldType; + f.offset = in_fieldOffset; + f.elementCount = in_elementCount; + f.validator = NULL; + f.flag = flags; + + f.setDataFn = &defaultProtectedSetFn; + f.getDataFn = &defaultProtectedGetFn; + + ConsoleBaseType* conType = ConsoleBaseType::getType( in_fieldType ); + AssertFatal( conType, "ConsoleObject::addField - invalid console type" ); + f.table = conType->getEnumTable(); + + sg_tempFieldList.push_back(f); +} + +void ConsoleObject::addProtectedField(const char* in_pFieldname, + const U32 in_fieldType, + const dsize_t in_fieldOffset, + AbstractClassRep::SetDataNotify in_setDataFn, + AbstractClassRep::GetDataNotify in_getDataFn, + const U32 in_elementCount, + const char* in_pFieldDocs, + U32 flags ) +{ + AbstractClassRep::Field f; + f.pFieldname = StringTable->insert(in_pFieldname); + + if(in_pFieldDocs) + f.pFieldDocs = in_pFieldDocs; + + f.type = in_fieldType; + f.offset = in_fieldOffset; + f.elementCount = in_elementCount; + f.validator = NULL; + f.flag = flags; + + f.setDataFn = in_setDataFn; + f.getDataFn = in_getDataFn; + + ConsoleBaseType* conType = ConsoleBaseType::getType( in_fieldType ); + AssertFatal( conType, "ConsoleObject::addProtectedField - invalid console type" ); + f.table = conType->getEnumTable(); + + sg_tempFieldList.push_back(f); +} + +void ConsoleObject::addFieldV(const char* in_pFieldname, + const U32 in_fieldType, + const dsize_t in_fieldOffset, + TypeValidator *v, + const char* in_pFieldDocs) +{ + AbstractClassRep::Field f; + f.pFieldname = StringTable->insert(in_pFieldname); + if(in_pFieldDocs) + f.pFieldDocs = in_pFieldDocs; + f.type = in_fieldType; + f.offset = in_fieldOffset; + f.elementCount = 1; + f.table = NULL; + f.setDataFn = &defaultProtectedSetFn; + f.getDataFn = &defaultProtectedGetFn; + f.validator = v; + v->fieldIndex = sg_tempFieldList.size(); + + sg_tempFieldList.push_back(f); +} + +void ConsoleObject::addDeprecatedField(const char *fieldName) +{ + AbstractClassRep::Field f; + f.pFieldname = StringTable->insert(fieldName); + f.type = AbstractClassRep::DeprecatedFieldType; + f.offset = 0; + f.elementCount = 0; + f.table = NULL; + f.validator = NULL; + f.setDataFn = &defaultProtectedSetFn; + f.getDataFn = &defaultProtectedGetFn; + + sg_tempFieldList.push_back(f); +} + + +bool ConsoleObject::removeField(const char* in_pFieldname) +{ + for (U32 i = 0; i < sg_tempFieldList.size(); i++) { + if (dStricmp(in_pFieldname, sg_tempFieldList[i].pFieldname) == 0) { + sg_tempFieldList.erase(i); + return true; + } + } + + return false; +} + +//-------------------------------------- +void ConsoleObject::initPersistFields() +{ +} + +//-------------------------------------- +void ConsoleObject::consoleInit() +{ +} + +//-------------------------------------- +AbstractClassRep* ConsoleObject::getClassRep() const +{ + return NULL; +} + +String ConsoleObject::_getLogMessage(const char* fmt, void* args) const +{ + String objClass = "UnknownClass"; + if(getClassRep()) + objClass = getClassRep()->getClassName(); + + String formattedMessage = String::VToString(fmt, args); + return String::ToString("%s - Object at %x - %s", + objClass.c_str(), this, formattedMessage.c_str()); +} + +void ConsoleObject::logMessage(const char* fmt, ...) const +{ + va_list args; + va_start(args, fmt); + Con::printf(_getLogMessage(fmt, args)); + va_end(args); +} + +void ConsoleObject::logWarning(const char* fmt, ...) const +{ + va_list args; + va_start(args, fmt); + Con::warnf(_getLogMessage(fmt, args)); + va_end(args); +} + +void ConsoleObject::logError(const char* fmt, ...) const +{ + va_list args; + va_start(args, fmt); + Con::errorf(_getLogMessage(fmt, args)); + va_end(args); +} + + +//------------------------------------------------------------------------------ + +static const char* returnClassList( Vector< AbstractClassRep* >& classes, U32 bufSize ) +{ + if( !classes.size() ) + return ""; + + dQsort( classes.address(), classes.size(), sizeof( AbstractClassRep* ), ACRCompare ); + + char* ret = Con::getReturnBuffer( bufSize ); + dStrcpy( ret, classes[ 0 ]->getClassName() ); + for( U32 i = 1; i < classes.size(); i ++ ) + { + dStrcat( ret, "\t" ); + dStrcat( ret, classes[ i ]->getClassName() ); + } + + return ret; +} + +//------------------------------------------------------------------------------ + +DefineEngineFunction( isClass, bool, ( const char* identifier ),, + "@brief Returns true if the passed identifier is the name of a declared class.\n\n" + "@ingroup Console") +{ + AbstractClassRep* rep = AbstractClassRep::findClassRep( identifier ); + return rep != NULL; +} + +DefineEngineFunction( isMemberOfClass, bool, ( const char* className, const char* superClassName ),, + "@brief Returns true if the class is derived from the super class.\n\n" + "If either class doesn't exist this returns false.\n" + "@param className The class name.\n" + "@param superClassName The super class to look for.\n" + "@ingroup Console") +{ + AbstractClassRep *pRep = AbstractClassRep::findClassRep( className ); + while (pRep) + { + if( !dStricmp( pRep->getClassName(), superClassName ) ) + return true; + pRep = pRep->getParentClass(); + } + return false; +} + +DefineEngineFunction( getDescriptionOfClass, const char*, ( const char* className ),, + "@brief Returns the description string for the named class.\n\n" + "@param className The name of the class.\n" + "@return The class description in string format.\n" + "@ingroup Console") +{ + AbstractClassRep* rep = AbstractClassRep::findClassRep( className ); + if( rep ) + return rep->getDescription(); + + Con::errorf( "getDescriptionOfClass - no class called '%s'", className ); + return ""; +} + +DefineEngineFunction( getCategoryOfClass, const char*, ( const char* className ),, + "@brief Returns the category of the given class.\n\n" + "@param className The name of the class.\n" + "@ingroup Console") +{ + AbstractClassRep* rep = AbstractClassRep::findClassRep( className ); + if( rep ) + return rep->getCategory(); + + Con::errorf( "getCategoryOfClass - no class called '%s'", className ); + return ""; +} + +DefineEngineFunction( enumerateConsoleClasses, const char*, ( const char* className ), ( "" ), + "@brief Returns a list of classes that derive from the named class.\n\n" + "If the named class is omitted this dumps all the classes.\n" + "@param className The optional base class name.\n" + "@return A tab delimited list of classes.\n" + "@ingroup Editors\n" + "@internal") +{ + AbstractClassRep *base = NULL; + if(className && *className) + { + base = AbstractClassRep::findClassRep(className); + if(!base) + return ""; + } + + Vector classes; + U32 bufSize = 0; + for(AbstractClassRep *rep = AbstractClassRep::getClassList(); rep; rep = rep->getNextClass()) + { + if( !base || rep->isClass(base)) + { + classes.push_back(rep); + bufSize += dStrlen(rep->getClassName()) + 1; + } + } + + return returnClassList( classes, bufSize ); +} + +DefineEngineFunction( enumerateConsoleClassesByCategory, const char*, ( String category ),, + "@brief Provide a list of classes that belong to the given category.\n\n" + "@param category The category name.\n" + "@return A tab delimited list of classes.\n" + "@ingroup Editors\n" + "@internal") +{ + U32 categoryLength = category.length(); + + U32 bufSize = 0; + Vector< AbstractClassRep* > classes; + + for( AbstractClassRep* rep = AbstractClassRep::getClassList(); rep != NULL; rep = rep->getNextClass() ) + { + const String& repCategory = rep->getCategory(); + + if( repCategory.length() >= categoryLength + && ( repCategory.compare( category, categoryLength, String::NoCase ) == 0 ) + && ( repCategory[ categoryLength ] == ' ' || repCategory[ categoryLength ] == '\0' ) ) + { + classes.push_back( rep ); + bufSize += dStrlen( rep->getClassName() + 1 ); + } + } + + return returnClassList( classes, bufSize ); +} + +DefineEngineFunction( dumpNetStats, void, (),, + "@brief Dumps network statistics for each class to the console.\n\n" + + "The returned avg, min and max values are in bits sent per update. " + "The num value is the total number of events collected.\n" + + "@note This method only works when TORQUE_NET_STATS is defined in torqueConfig.h.\n" + "@ingroup Networking\n" ) +{ +#ifdef TORQUE_NET_STATS + for (AbstractClassRep * rep = AbstractClassRep::getClassList(); rep; rep = rep->getNextClass()) + { + if (rep->mNetStatPack.numEvents || rep->mNetStatUnpack.numEvents || rep->mNetStatWrite.numEvents || rep->mNetStatRead.numEvents) + { + Con::printf("class %s net info",rep->getClassName()); + if (rep->mNetStatPack.numEvents) + Con::printf(" packUpdate: avg (%f), min (%i), max (%i), num (%i)", + F32(rep->mNetStatPack.total)/F32(rep->mNetStatPack.numEvents), + rep->mNetStatPack.min, + rep->mNetStatPack.max, + rep->mNetStatPack.numEvents); + if (rep->mNetStatUnpack.numEvents) + Con::printf(" unpackUpdate: avg (%f), min (%i), max (%i), num (%i)", + F32(rep->mNetStatUnpack.total)/F32(rep->mNetStatUnpack.numEvents), + rep->mNetStatUnpack.min, + rep->mNetStatUnpack.max, + rep->mNetStatUnpack.numEvents); + if (rep->mNetStatWrite.numEvents) + Con::printf(" write: avg (%f), min (%i), max (%i), num (%i)", + F32(rep->mNetStatWrite.total)/F32(rep->mNetStatWrite.numEvents), + rep->mNetStatWrite.min, + rep->mNetStatWrite.max, + rep->mNetStatWrite.numEvents); + if (rep->mNetStatRead.numEvents) + Con::printf(" read: avg (%f), min (%i), max (%i), num (%i)", + F32(rep->mNetStatRead.total)/F32(rep->mNetStatRead.numEvents), + rep->mNetStatRead.min, + rep->mNetStatRead.max, + rep->mNetStatRead.numEvents); + S32 sum = 0; + for (S32 i=0; i<32; i++) + sum += rep->mDirtyMaskFrequency[i]; + if (sum) + { + Con::printf(" Mask bits:"); + for (S32 i=0; i<8; i++) + { + F32 avg0 = rep->mDirtyMaskFrequency[i] ? F32(rep->mDirtyMaskTotal[i])/F32(rep->mDirtyMaskFrequency[i]) : 0.0f; + F32 avg8 = rep->mDirtyMaskFrequency[i+8] ? F32(rep->mDirtyMaskTotal[i+8])/F32(rep->mDirtyMaskFrequency[i+8]) : 0.0f; + F32 avg16 = rep->mDirtyMaskFrequency[i+16] ? F32(rep->mDirtyMaskTotal[i+16])/F32(rep->mDirtyMaskFrequency[i+16]) : 0.0f; + F32 avg24 = rep->mDirtyMaskFrequency[i+24] ? F32(rep->mDirtyMaskTotal[i+24])/F32(rep->mDirtyMaskFrequency[i+24]) : 0.0f; + Con::printf(" %2i - %4i (%6.2f) %2i - %4i (%6.2f) %2i - %4i (%6.2f) %2i - %4i, (%6.2f)", + i ,rep->mDirtyMaskFrequency[i],avg0, + i+8 ,rep->mDirtyMaskFrequency[i+8],avg8, + i+16,rep->mDirtyMaskFrequency[i+16],avg16, + i+24,rep->mDirtyMaskFrequency[i+24],avg24); + } + } + } + rep->resetNetStats(); + } +#endif +} + +DefineEngineFunction( sizeof, S32, ( const char *objectOrClass ),, + "@brief Determines the memory consumption of a class or object.\n\n" + "@param objectOrClass The object or class being measured.\n" + "@return Returns the total size of an object in bytes.\n" + "@ingroup Debugging\n") +{ + AbstractClassRep *acr = NULL; + SimObject *obj = Sim::findObject(objectOrClass); + if(obj) + acr = obj->getClassRep(); + + if(!acr) + acr = AbstractClassRep::findClassRep(objectOrClass); + + if(acr) + return acr->getSizeof(); + + if(dStricmp("ConsoleObject", objectOrClass) == 0) + return sizeof(ConsoleObject); + + Con::warnf("could not find a class rep for that object or class name."); + return 0; +} diff --git a/Engine/source/console/consoleObject.h b/Engine/source/console/consoleObject.h new file mode 100644 index 000000000..6b9ae3588 --- /dev/null +++ b/Engine/source/console/consoleObject.h @@ -0,0 +1,1138 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _CONSOLEOBJECT_H_ +#define _CONSOLEOBJECT_H_ + +#ifndef _TVECTOR_H_ + #include "core/util/tVector.h" +#endif +#ifndef _STRINGTABLE_H_ + #include "core/stringTable.h" +#endif +#ifndef _STRINGFUNCTIONS_H_ + #include "core/strings/stringFunctions.h" +#endif +#ifndef _BITSET_H_ + #include "core/bitSet.h" +#endif +#ifndef _DYNAMIC_CONSOLETYPES_H_ + #include "console/dynamicTypes.h" +#endif +#ifndef _ENGINEOBJECT_H_ + #include "console/engineObject.h" +#endif +#ifndef _ENGINEFUNCTIONS_H_ + #include "console/engineFunctions.h" +#endif +#ifndef _SIMOBJECTREF_H_ + #include "console/simObjectRef.h" +#endif + + +/// @file +/// Legacy console object system. + + +/// @ingroup console_system Console System +/// @{ + +class Namespace; +class ConsoleObject; + +enum NetClassTypes +{ + NetClassTypeObject = 0, + NetClassTypeDataBlock, + NetClassTypeEvent, + NetClassTypesCount, +}; + +enum NetClassGroups +{ + NetClassGroupGame = 0, + NetClassGroupCommunity, + NetClassGroup3, + NetClassGroup4, + NetClassGroupsCount, +}; + +enum NetClassMasks +{ + NetClassGroupGameMask = BIT(NetClassGroupGame), + NetClassGroupCommunityMask = BIT(NetClassGroupCommunity), +}; + +enum NetDirection +{ + NetEventDirAny, + NetEventDirServerToClient, + NetEventDirClientToServer, +}; + +class SimObject; +class TypeValidator; +class ConsoleClassObject; + +DECLARE_SCOPE( ConsoleAPI ); + + +//============================================================================= +// AbstractClassRep. +//============================================================================= + + +/// Core functionality for class manipulation. +/// +/// @section AbstractClassRep_intro Introduction (or, Why AbstractClassRep?) +/// +/// Many of Torque's subsystems, especially network, console, and sim, +/// require the ability to programatically instantiate classes. For instance, +/// when objects are ghosted, the networking layer needs to be able to create +/// an instance of the object on the client. When the console scripting +/// language runtime encounters the "new" keyword, it has to be able to fill +/// that request. +/// +/// Since standard C++ doesn't provide a function to create a new instance of +/// an arbitrary class at runtime, one must be created. This is what +/// AbstractClassRep and ConcreteClassRep are all about. They allow the registration +/// and instantiation of arbitrary classes at runtime. +/// +/// In addition, ACR keeps track of the fields (registered via addField() and co.) of +/// a class, allowing programmatic access of class fields. +/// +/// @see ConsoleObject +/// +/// @note In general, you will only access the functionality implemented in this class via +/// ConsoleObject::create(). Most of the time, you will only ever need to use this part +/// part of the engine indirectly - ie, you will use the networking system or the console, +/// or ConsoleObject, and they will indirectly use this code. The following discussion +/// is really only relevant for advanced engine users. +/// +/// @section AbstractClassRep_netstuff NetClasses and Class IDs +/// +/// Torque supports a notion of group, type, and direction for objects passed over +/// the network. Class IDs are assigned sequentially per-group, per-type, so that, for instance, +/// the IDs assigned to Datablocks are seperate from the IDs assigned to NetObjects or NetEvents. +/// This can translate into significant bandwidth savings (especially since the size of the fields +/// for transmitting these bits are determined at run-time based on the number of IDs given out. +/// +/// @section AbstractClassRep_details AbstractClassRep Internals +/// +/// Much like ConsoleConstructor, ACR does some preparatory work at runtime before execution +/// is passed to main(). In actual fact, this preparatory work is done by the ConcreteClassRep +/// template. Let's examine this more closely. +/// +/// If we examine ConsoleObject, we see that two macros must be used in the definition of a +/// properly integrated objects. From the ConsoleObject example: +/// +/// @code +/// // This is from inside the class definition... +/// DECLARE_CONOBJECT(TorqueObject); +/// +/// // And this is from outside the class definition... +/// IMPLEMENT_CONOBJECT(TorqueObject); +/// @endcode +/// +/// What do these things actually do? +/// +/// Not all that much, in fact. They expand to code something like this: +/// +/// @code +/// // This is from inside the class definition... +/// static ConcreteClassRep dynClassRep; +/// static AbstractClassRep* getParentStaticClassRep(); +/// static AbstractClassRep* getStaticClassRep(); +/// virtual AbstractClassRep* getClassRep() const; +/// @endcode +/// +/// @code +/// // And this is from outside the class definition... +/// AbstractClassRep* TorqueObject::getClassRep() const { return &TorqueObject::dynClassRep; } +/// AbstractClassRep* TorqueObject::getStaticClassRep() { return &dynClassRep; } +/// AbstractClassRep* TorqueObject::getParentStaticClassRep() { return Parent::getStaticClassRep(); } +/// ConcreteClassRep TorqueObject::dynClassRep("TorqueObject", 0, -1, 0); +/// @endcode +/// +/// As you can see, getClassRep(), getStaticClassRep(), and getParentStaticClassRep() are just +/// accessors to allow access to various ConcreteClassRep instances. This is where the Parent +/// typedef comes into play as well - it lets getParentStaticClassRep() get the right +/// class rep. +/// +/// In addition, dynClassRep is declared as a member of TorqueObject, and defined later +/// on. Much like ConsoleConstructor, ConcreteClassReps add themselves to a global linked +/// list in their constructor. +/// +/// Then, when AbstractClassRep::initialize() is called, from Con::init(), we iterate through +/// the list and perform the following tasks: +/// - Sets up a Namespace for each class. +/// - Call the init() method on each ConcreteClassRep. This method: +/// - Links namespaces between parent and child classes, using Con::classLinkNamespaces. +/// - Calls initPersistFields() and consoleInit(). +/// - As a result of calling initPersistFields, the field list for the class is populated. +/// - Assigns network IDs for classes based on their NetGroup membership. Determines +/// bit allocations for network ID fields. +/// +/// @nosubgrouping +class AbstractClassRep : public ConsoleBaseType +{ + friend class ConsoleObject; + +public: + + typedef ConsoleBaseType Parent; + + /// @name 'Tructors + /// @{ + + /// + /// @param conIdPtr Pointer to the static S32 console ID. + /// @param conTypeName Console type name. + AbstractClassRep( S32* conIdPtr, const char* typeName ) + : Parent( sizeof( void* ), conIdPtr, typeName ) + { + VECTOR_SET_ASSOCIATION( mFieldList ); + + parentClass = NULL; + mIsRenderEnabled = true; + mIsSelectionEnabled = true; + } + + /// @} + + /// @name Representation Interface + /// @{ + +//TODO: move over to EngineTypeNetInfo + S32 mClassGroupMask; ///< Mask indicating in which NetGroups this object belongs. + S32 mClassType; ///< Stores the NetClass of this class. + S32 mNetEventDir; ///< Stores the NetDirection of this class. + S32 mClassId[ NetClassGroupsCount ]; ///< Stores the IDs assigned to this class for each group. + S32 mClassSizeof; ///< Size of instances in bytes. + +//TODO: move over to EngineTypeNetInfo +#ifdef TORQUE_NET_STATS + struct NetStatInstance + { + U32 numEvents; + U32 total; + S32 min; + S32 max; + + void reset() + { + numEvents = 0; + total = 0; + min = S32_MAX; + max = S32_MIN; + } + + void update(U32 amount) + { + numEvents++; + total += amount; + min = getMin((S32)amount, min); + max = getMax((S32)amount, max); + } + + NetStatInstance() + { + reset(); + } + }; + + NetStatInstance mNetStatPack; + NetStatInstance mNetStatUnpack; + NetStatInstance mNetStatWrite; + NetStatInstance mNetStatRead; + + U32 mDirtyMaskFrequency[32]; + U32 mDirtyMaskTotal[32]; + + void resetNetStats() + { + mNetStatPack.reset(); + mNetStatUnpack.reset(); + mNetStatWrite.reset(); + mNetStatRead.reset(); + + for(S32 i=0; i<32; i++) + { + mDirtyMaskFrequency[i] = 0; + mDirtyMaskTotal[i] = 0; + } + } + + void updateNetStatPack(U32 dirtyMask, U32 length) + { + mNetStatPack.update(length); + + for(S32 i=0; i<32; i++) + if(BIT(i) & dirtyMask) + { + mDirtyMaskFrequency[i]++; + mDirtyMaskTotal[i] += length; + } + } + + void updateNetStatUnpack(U32 length) + { + mNetStatUnpack.update(length); + } + + void updateNetStatWriteData(U32 length) + { + mNetStatWrite.update(length); + } + + void updateNetStatReadData(U32 length) + { + mNetStatRead.update(length); + } +#endif + S32 getClassId (U32 netClassGroup) const { return mClassId[ netClassGroup ]; } + static U32 getClassCRC (U32 netClassGroup) { return classCRC[ netClassGroup ]; } + AbstractClassRep* getCommonParent( const AbstractClassRep *otherClass ) const; + + /// Return the name of this class. + StringTableEntry getClassName() const { return mClassName; } + + /// Return the namespace that contains the methods of this class. + Namespace* getNameSpace() const { return mNamespace; } + + /// Return the AbstractClassRep of the class that this class is derived from. + AbstractClassRep* getParentClass() const { return parentClass; } + + /// Return the size of instances of this class in bytes. + S32 getSizeof() const { return mClassSizeof; } + + /// Return the next class in the global class list link chain. + AbstractClassRep* getNextClass() const { return nextClass; } + + /// Return the head of the global class list. + static AbstractClassRep* getClassList() { return classLinkList; } + + /// Helper class to see if we are a given class, or a subclass thereof by + /// comparing AbstractClassRep pointers. + bool isSubclassOf( const AbstractClassRep* klass ) const + { + const AbstractClassRep *walk = this; + + // Walk up parents, checking for equivalence. + while ( walk ) + { + if ( walk == klass ) + return true; + + walk = walk->parentClass; + }; + + return false; + } + + /// Helper class to see if we are a given class, or a subclass thereof by + /// comparing the class name strings. + bool isSubclassOf( const char *klass ) const + { + klass = StringTable->insert( klass ); + + // Walk up parents, checking for equivalence. + const AbstractClassRep *walk = this; + while ( walk ) + { + if ( walk->mClassName == klass ) + return true; + + walk = walk->parentClass; + }; + + return false; + } + + /// @deprecated Use isSubclassOf. + bool isClass( const AbstractClassRep* acr ) const + { + return isSubclassOf( acr ); + } + + virtual ConsoleObject* create () const = 0; + +protected: + + virtual void init(); + + const char * mClassName; + AbstractClassRep * nextClass; + AbstractClassRep * parentClass; + Namespace * mNamespace; + + /// @} + +public: + + bool mIsRenderEnabled; + bool mIsSelectionEnabled; + + bool isRenderEnabled() const { return mIsRenderEnabled; } + bool isSelectionEnabled() const { return mIsSelectionEnabled; } + + /// @name Categories + /// @{ + +protected: + + const char* mCategory; + const char* mDescription; + +public: + + /// Return the space separated category path for the class. + const char* getCategory() const { return mCategory; } + + /// Return a short description string suitable for displaying in tooltips. + const char* getDescription() const { return mDescription; } + + /// @} + + /// @name Fields + /// @{ +public: + + /// This is a function pointer typedef to support get/set callbacks for fields + typedef bool (*SetDataNotify)( void *obj, const char *array, const char *data ); + typedef const char *(*GetDataNotify)( void *obj, const char *data ); + + /// These are special field type values used to mark + /// groups and arrays in the field list. + /// @see Field::type + /// @see addArray, endArray + /// @see addGroup, endGroup + /// @see addGroup, endGroup + /// @see addDeprecatedField + enum ACRFieldTypes + { + /// The first custom field type... all fields + /// types greater or equal to this one are not + /// console data types. + ARCFirstCustomField = 0xFFFFFFFB, + + /// Marks the start of a fixed size array of fields. + /// @see addArray + StartArrayFieldType = 0xFFFFFFFB, + + /// Marks the end of a fixed size array of fields. + /// @see endArray + EndArrayFieldType = 0xFFFFFFFC, + + /// Marks the beginning of a group of fields. + /// @see addGroup + StartGroupFieldType = 0xFFFFFFFD, + + /// Marks the beginning of a group of fields. + /// @see endGroup + EndGroupFieldType = 0xFFFFFFFE, + + /// Marks a field that is depreciated and no + /// longer stores a value. + /// @see addDeprecatedField + DeprecatedFieldType = 0xFFFFFFFF + }; + + enum FieldFlags + { + FIELD_HideInInspectors = BIT( 0 ), ///< Do not show the field in inspectors. + }; + + struct Field + { + Field() + : pFieldname( NULL ), + pGroupname( NULL ), + pFieldDocs( NULL ), + groupExpand( false ), + type( 0 ), + offset( 0 ), + elementCount( 0 ), + table( NULL ), + validator( NULL ), + setDataFn( NULL ), + getDataFn( NULL ) + { + } + + StringTableEntry pFieldname; ///< Name of the field. + const char* pGroupname; ///< Optionally filled field containing the group name. + /// + /// This is filled when type is StartField or EndField + + const char* pFieldDocs; ///< Documentation about this field; see consoleDoc.cc. + bool groupExpand; ///< Flag to track expanded/not state of this group in the editor. + U32 type; ///< A data type ID or one of the special custom fields. @see ACRFieldTypes + U32 offset; ///< Memory offset from beginning of class for this field. + S32 elementCount; ///< Number of elements, if this is an array. + const EnumTable * table; ///< If this is an enum, this points to the table defining it. + BitSet32 flag; ///< Stores various flags + TypeValidator *validator; ///< Validator, if any. + SetDataNotify setDataFn; ///< Set data notify Fn + GetDataNotify getDataFn; ///< Get data notify Fn + }; + typedef Vector FieldList; + + FieldList mFieldList; + + bool mDynamicGroupExpand; + + const Field* findField( StringTableEntry fieldName ) const; + + /// @} + + /// @name Console Type Interface + /// @{ + + virtual void* getNativeVariable() { return new ( AbstractClassRep* ); } // Any pointer-sized allocation will do. + virtual void deleteNativeVariable( void* var ) { delete reinterpret_cast< AbstractClassRep** >( var ); } + + /// @} + + /// @name Abstract Class Database + /// @{ + +protected: + static AbstractClassRep ** classTable[NetClassGroupsCount][NetClassTypesCount]; + static AbstractClassRep * classLinkList; + static U32 classCRC[NetClassGroupsCount]; + static bool initialized; + + static ConsoleObject* create(const char* in_pClassName); + static ConsoleObject* create(const U32 groupId, const U32 typeId, const U32 in_classId); + +public: + static U32 NetClassCount [NetClassGroupsCount][NetClassTypesCount]; + static U32 NetClassBitSize[NetClassGroupsCount][NetClassTypesCount]; + + static void registerClassRep(AbstractClassRep*); + static AbstractClassRep* findClassRep(const char* in_pClassName); + static AbstractClassRep* findClassRep( U32 groupId, U32 typeId, U32 classId ); + static void removeClassRep(AbstractClassRep*); // This should not be used lightly + static void initialize(); // Called from Con::init once on startup + static void shutdown(); + + + /// @} +}; + +extern AbstractClassRep::FieldList sg_tempFieldList; + + +//============================================================================= +// ConcreteClassRep. +//============================================================================= + + +/// Helper class for AbstractClassRep. +/// +/// @see AbtractClassRep +/// @see ConsoleObject +template< class T > +class ConcreteClassRep : public AbstractClassRep +{ + public: + + static EnginePropertyTable _smPropertyTable; + static EnginePropertyTable& smPropertyTable; + + ConcreteClassRep( const char* name, + const char* conTypeName, + S32* conTypeIdPtr, + S32 netClassGroupMask, + S32 netClassType, + S32 netEventDir, + AbstractClassRep* parent, + const char* ( *parentDesc )() ) + : AbstractClassRep( conTypeIdPtr, conTypeName ) + { + mClassName = StringTable->insert( name ); + mCategory = T::__category(); + mTypeInfo = _MAPTYPE< T >(); + + if( mTypeInfo ) + const_cast< EngineTypeInfo* >( mTypeInfo )->mPropertyTable = &smPropertyTable; + + if( &T::__description != parentDesc ) + mDescription = T::__description(); + + // Clean up mClassId + for(U32 i = 0; i < NetClassGroupsCount; i++) + mClassId[i] = -1; + + // Set properties for this ACR + mClassType = netClassType; + mClassGroupMask = netClassGroupMask; + mNetEventDir = netEventDir; + parentClass = parent; + mClassSizeof = sizeof(T); + + // Finally, register ourselves. + registerClassRep(this); + }; + + /// Perform class specific initialization tasks. + /// + /// Link namespaces, call initPersistFields() and consoleInit(). + void init() + { + // Get handle to our parent class, if any, and ourselves (we are our parent's child). + AbstractClassRep *parent = T::getParentStaticClassRep(); + AbstractClassRep *child = T::getStaticClassRep(); + + // If we got reps, then link those namespaces! (To get proper inheritance.) + if(parent && child) + Con::classLinkNamespaces(parent->getNameSpace(), child->getNameSpace()); + + // Finally, do any class specific initialization... + T::initPersistFields(); + T::consoleInit(); + + // Let the base finish up. + AbstractClassRep::init(); + } + + /// Wrap constructor. + ConsoleObject* create() const { return new T; } + + /// @name Console Type Interface + /// @{ + + virtual void setData( void* dptr, S32 argc, const char** argv, const EnumTable* tbl, BitSet32 flag ) + { + if( argc == 1 ) + { + T** obj = ( T** ) dptr; + *obj = dynamic_cast< T* >( T::__findObject( argv[ 0 ] ) ); + } + else + Con::errorf( "Cannot set multiple args to a single ConsoleObject*."); + } + + virtual const char* getData( void* dptr, const EnumTable* tbl, BitSet32 flag ) + { + T** obj = ( T** ) dptr; + return Con::getReturnBuffer( T::__getObjectId( *obj ) ); + } + + virtual const char* getTypeClassName() { return mClassName; } + virtual const bool isDatablock() { return T::__smIsDatablock; }; + + /// @} +}; + +template< typename T > EnginePropertyTable ConcreteClassRep< T >::_smPropertyTable( 0, NULL ); +template< typename T > EnginePropertyTable& ConcreteClassRep< T >::smPropertyTable = ConcreteClassRep< T >::_smPropertyTable; + + +//------------------------------------------------------------------------------ +// Forward declaration of this function so it can be used in the class +const char *defaultProtectedGetFn( void *obj, const char *data ); + + +//============================================================================= +// ConsoleObject. +//============================================================================= + + +/// Interface class to the console. +/// +/// @section ConsoleObject_basics The Basics +/// +/// Any object which you want to work with the console system should derive from this, +/// and access functionality through the static interface. +/// +/// This class is always used with the DECLARE_CONOBJECT and IMPLEMENT_* macros. +/// +/// @code +/// // A very basic example object. It will do nothing! +/// class TorqueObject : public ConsoleObject { +/// // Must provide a Parent typedef so the console system knows what we inherit from. +/// typedef ConsoleObject Parent; +/// +/// // This does a lot of menial declaration for you. +/// DECLARE_CONOBJECT(TorqueObject); +/// +/// // This is for us to register our fields in. +/// static void initPersistFields(); +/// +/// // A sample field. +/// S8 mSample; +/// } +/// @endcode +/// +/// @code +/// // And the accordant implementation... +/// IMPLEMENT_CONOBJECT(TorqueObject); +/// +/// void TorqueObject::initPersistFields() +/// { +/// // If you want to inherit any fields from the parent (you do), do this: +/// Parent::initPersistFields(); +/// +/// // Pass the field, the type, the offset, and a usage string. +/// addField("sample", TypeS8, Offset(mSample, TorqueObject), "A test field."); +/// } +/// @endcode +/// +/// That's all you need to do to get a class registered with the console system. At this point, +/// you can instantiate it via script, tie methods to it using ConsoleMethod, register fields, +/// and so forth. You can also register any global variables related to the class by creating +/// a consoleInit() method. +/// +/// You will need to use different IMPLEMENT_ macros in different cases; for instance, if you +/// are making a NetObject (for ghosting), a DataBlock, or a NetEvent. +/// +/// @see AbstractClassRep for gory implementation details. +/// @nosubgrouping +class ConsoleObject : public EngineObject +{ + DECLARE_ABSTRACT_CLASS( ConsoleObject, EngineObject ); + +protected: + + /// @deprecated This is disallowed. + ConsoleObject(const ConsoleObject&); + +public: + + ConsoleObject() {} + + /// Get a reference to a field by name. + const AbstractClassRep::Field *findField(StringTableEntry fieldName) const; + + /// Gets the ClassRep. + virtual AbstractClassRep* getClassRep() const; + + /// Set the value of a field. + bool setField(const char *fieldName, const char *value); + +public: + + /// @name Object Creation + /// @{ + static ConsoleObject* create(const char* in_pClassName); + static ConsoleObject* create(const U32 groupId, const U32 typeId, const U32 in_classId); + /// @} + +public: + + /// Get the classname from a class tag. + static const char* lookupClassName(const U32 in_classTag); + + /// @name Fields + /// @{ + + /// Mark the beginning of a group of fields. + /// + /// This is used in the consoleDoc system. + /// @see console_autodoc + static void addGroup(const char* in_pGroupname, const char* in_pGroupDocs = NULL); + + /// Mark the end of a group of fields. + /// + /// This is used in the consoleDoc system. + /// @see console_autodoc + static void endGroup(const char* in_pGroupname); + + /// Marks the start of a fixed size array of fields. + /// @see console_autodoc + static void addArray( const char *arrayName, S32 count ); + + /// Marks the end of an array of fields. + /// @see console_autodoc + static void endArray( const char *arrayName ); + + /// Register a complex field. + /// + /// @param in_pFieldname Name of the field. + /// @param in_fieldType Type of the field. @see ConsoleDynamicTypes + /// @param in_fieldOffset Offset to the field from the start of the class; calculated using the Offset() macro. + /// @param in_elementCount Number of elements in this field. Arrays of elements are assumed to be contiguous in memory. + /// @param in_pFieldDocs Usage string for this field. @see console_autodoc + static void addField(const char* in_pFieldname, + const U32 in_fieldType, + const dsize_t in_fieldOffset, + const U32 in_elementCount = 1, + const char* in_pFieldDocs = NULL, + U32 flags = 0 ); + + /// Register a simple field. + /// + /// @param in_pFieldname Name of the field. + /// @param in_fieldType Type of the field. @see ConsoleDynamicTypes + /// @param in_fieldOffset Offset to the field from the start of the class; calculated using the Offset() macro. + /// @param in_pFieldDocs Usage string for this field. @see console_autodoc + static void addField(const char* in_pFieldname, + const U32 in_fieldType, + const dsize_t in_fieldOffset, + const char* in_pFieldDocs, + U32 flags = 0 ); + + /// Register a validated field. + /// + /// A validated field is just like a normal field except that you can't + /// have it be an array, and that you give it a pointer to a TypeValidator + /// subclass, which is then used to validate any value placed in it. Invalid + /// values are ignored and an error is printed to the console. + /// + /// @see addField + /// @see typeValidators.h + static void addFieldV(const char* in_pFieldname, + const U32 in_fieldType, + const dsize_t in_fieldOffset, + TypeValidator *v, + const char * in_pFieldDocs = NULL); + + /// Register a complex protected field. + /// + /// @param in_pFieldname Name of the field. + /// @param in_fieldType Type of the field. @see ConsoleDynamicTypes + /// @param in_fieldOffset Offset to the field from the start of the class; calculated using the Offset() macro. + /// @param in_setDataFn When this field gets set, it will call the callback provided. @see console_protected + /// @param in_getDataFn When this field is accessed for it's data, it will return the value of this function + /// @param in_elementCount Number of elements in this field. Arrays of elements are assumed to be contiguous in memory. + /// @param in_pFieldDocs Usage string for this field. @see console_autodoc + static void addProtectedField(const char* in_pFieldname, + const U32 in_fieldType, + const dsize_t in_fieldOffset, + AbstractClassRep::SetDataNotify in_setDataFn, + AbstractClassRep::GetDataNotify in_getDataFn, + const U32 in_elementCount, + const char* in_pFieldDocs = NULL, + U32 flags = 0 ); + + /// Register a simple protected field. + /// + /// @param in_pFieldname Name of the field. + /// @param in_fieldType Type of the field. @see ConsoleDynamicTypes + /// @param in_fieldOffset Offset to the field from the start of the class; calculated using the Offset() macro. + /// @param in_setDataFn When this field gets set, it will call the callback provided. @see console_protected + /// @param in_getDataFn When this field is accessed for it's data, it will return the value of this function + /// @param in_pFieldDocs Usage string for this field. @see console_autodoc + static void addProtectedField(const char* in_pFieldname, + const U32 in_fieldType, + const dsize_t in_fieldOffset, + AbstractClassRep::SetDataNotify in_setDataFn, + AbstractClassRep::GetDataNotify in_getDataFn = &defaultProtectedGetFn, + const char* in_pFieldDocs = NULL, + U32 flags = 0 ); + + /// Add a deprecated field. + /// + /// A deprecated field will always be undefined, even if you assign a value to it. This + /// is useful when you need to make sure that a field is not being used anymore. + static void addDeprecatedField(const char *fieldName); + + /// Remove a field. + /// + /// Sometimes, you just have to remove a field! + /// @returns True on success. + static bool removeField(const char* in_pFieldname); + + /// @} + + /// @name Logging + /// @{ + + /// Overload this in subclasses to change the message formatting. + /// @param fmt A printf style format string. + /// @param args A va_list containing the args passed ot a log function. + /// @note It is suggested that you use String::VToString. + virtual String _getLogMessage(const char* fmt, void* args) const; + + /// @} + +public: + + /// @name Logging + /// These functions will try to print out a message along the lines + /// of "ObjectClass - ObjectName(ObjectId) - formatted message" + /// @{ + + /// Logs with Con::printf. + void logMessage(const char* fmt, ...) const; + + /// Logs with Con::warnf. + void logWarning(const char* fmt, ...) const; + + /// Logs with Con::errorf. + void logError(const char* fmt, ...) const; + + /// @} + + /// Register dynamic fields in a subclass of ConsoleObject. + /// + /// @see addField(), addFieldV(), addDeprecatedField(), addGroup(), endGroup() + static void initPersistFields(); + + /// Register global constant variables and do other one-time initialization tasks in + /// a subclass of ConsoleObject. + /// + /// @deprecated You should use ConsoleMethod and ConsoleFunction, not this, to + /// register methods or commands. + /// @see console + static void consoleInit(); + + /// @name Field List + /// @{ + + /// Get a list of all the fields. This information cannot be modified. + const AbstractClassRep::FieldList& getFieldList() const; + + /// Get a list of all the fields, set up so we can modify them. + /// + /// @note This is a bad trick to pull if you aren't very careful, + /// since you can blast field data! + AbstractClassRep::FieldList& getModifiableFieldList(); + + /// Get a handle to a boolean telling us if we expanded the dynamic group. + /// + /// @see GuiInspector::Inspect() + bool& getDynamicGroupExpand(); + /// @} + + /// @name ConsoleObject Implementation + /// + /// These functions are implemented in every subclass of + /// ConsoleObject by an IMPLEMENT_CONOBJECT or IMPLEMENT_CO_* macro. + /// @{ + + /// Get the abstract class information for this class. + static AbstractClassRep *getStaticClassRep() { return NULL; } + + /// Get the abstract class information for this class's superclass. + static AbstractClassRep *getParentStaticClassRep() { return NULL; } + + /// Get our network-layer class id. + /// + /// @param netClassGroup The net class for which we want our ID. + /// @see + S32 getClassId(U32 netClassGroup) const; + + /// Get our compiler and platform independent class name. + /// + /// @note This name can be used to instantiate another instance using create() + StringTableEntry getClassName() const; + + /// @} + + static const char* __category() { return ""; } + static const char* __description() { return ""; } + + /// Subclasses of ConsoleObjects that are datablocks should redefine this static member variable + /// and set it to true. + static const bool __smIsDatablock = false; + + /// @name Object IDs and lookup. + /// For a subclass hierarchy based on ConsoleObject to become functional for use as a console object type, + /// the hierarchy must implement a naming scheme and indexing function for looking up objects by name. + /// @{ + + static ConsoleObject* __findObject( const char* ) { return NULL; } + static const char* __getObjectId( ConsoleObject* ) { return ""; } +}; + +#define addNamedField(fieldName,type,className) addField(#fieldName, type, Offset(fieldName,className)) +#define addNamedFieldV(fieldName,type,className, validator) addFieldV(#fieldName, type, Offset(fieldName,className), validator) + + +//------------------------------------------------------------------------------ +//-------------------------------------- Inlines +// +inline S32 ConsoleObject::getClassId(U32 netClassGroup) const +{ + AssertFatal(getClassRep() != NULL,"Cannot get tag from non-declared dynamic class!"); + return getClassRep()->getClassId(netClassGroup); +} + +inline StringTableEntry ConsoleObject::getClassName() const +{ + AssertFatal(getClassRep() != NULL, + "Cannot get tag from non-declared dynamic class"); + return getClassRep()->getClassName(); +} + +inline const AbstractClassRep::Field * ConsoleObject::findField(StringTableEntry name) const +{ + AssertFatal(getClassRep() != NULL, + avar("Cannot get field '%s' from non-declared dynamic class.", name)); + return getClassRep()->findField(name); +} + +inline bool ConsoleObject::setField(const char *fieldName, const char *value) +{ + //sanity check + if ((! fieldName) || (! fieldName[0]) || (! value)) + return false; + + if (! getClassRep()) + return false; + + const AbstractClassRep::Field *myField = getClassRep()->findField(StringTable->insert(fieldName)); + + if (! myField) + return false; + + Con::setData( + myField->type, + (void *) (((const char *)(this)) + myField->offset), + 0, + 1, + &value, + myField->table, + myField->flag); + + return true; +} + +inline ConsoleObject* ConsoleObject::create(const char* in_pClassName) +{ + return AbstractClassRep::create(in_pClassName); +} + +inline ConsoleObject* ConsoleObject::create(const U32 groupId, const U32 typeId, const U32 in_classId) +{ + return AbstractClassRep::create(groupId, typeId, in_classId); +} + +inline const AbstractClassRep::FieldList& ConsoleObject::getFieldList() const +{ + return getClassRep()->mFieldList; +} + +inline AbstractClassRep::FieldList& ConsoleObject::getModifiableFieldList() +{ + return getClassRep()->mFieldList; +} + +inline bool& ConsoleObject::getDynamicGroupExpand() +{ + return getClassRep()->mDynamicGroupExpand; +} + +/// @name ConsoleObject Macros +/// @{ + +#define DECLARE_CONOBJECT( className ) \ + DECLARE_CLASS( className, Parent ); \ + static S32 _smTypeId; \ + static ConcreteClassRep< className > dynClassRep; \ + static AbstractClassRep* getParentStaticClassRep(); \ + static AbstractClassRep* getStaticClassRep(); \ + static SimObjectRefConsoleBaseType< className > ptrRefType; \ + virtual AbstractClassRep* getClassRep() const + +#define DECLARE_CATEGORY( string ) \ + static const char* __category() { return string; } + +#define DECLARE_DESCRIPTION( string ) \ + static const char* __description() { return string; } + +#define IMPLEMENT_CONOBJECT( className ) \ + IMPLEMENT_CLASS( className, NULL ) \ + END_IMPLEMENT_CLASS; \ + S32 className::_smTypeId; \ + SimObjectRefConsoleBaseType< className > className::ptrRefType( "Type" #className "Ref" ); \ + AbstractClassRep* className::getClassRep() const { return &className::dynClassRep; } \ + AbstractClassRep* className::getStaticClassRep() { return &dynClassRep; } \ + AbstractClassRep* className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ + ConcreteClassRep className::dynClassRep( #className, "Type" #className, &_smTypeId, 0, -1, 0, className::getParentStaticClassRep(), &Parent::__description ) + +#define IMPLEMENT_CO_NETOBJECT_V1( className ) \ + IMPLEMENT_CLASS( className, NULL ) \ + END_IMPLEMENT_CLASS; \ + S32 className::_smTypeId; \ + SimObjectRefConsoleBaseType< className > className::ptrRefType( "Type" #className "Ref" ); \ + AbstractClassRep* className::getClassRep() const { return &className::dynClassRep; } \ + AbstractClassRep* className::getStaticClassRep() { return &dynClassRep; } \ + AbstractClassRep* className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ + ConcreteClassRep className::dynClassRep( #className, "Type" #className, &_smTypeId, NetClassGroupGameMask, NetClassTypeObject, 0, className::getParentStaticClassRep(), &Parent::__description ) + +#define IMPLEMENT_CO_DATABLOCK_V1( className ) \ + IMPLEMENT_CLASS( className, NULL ) \ + END_IMPLEMENT_CLASS; \ + S32 className::_smTypeId; \ + SimObjectRefConsoleBaseType< className > className::ptrRefType( "Type" #className "Ref" ); \ + AbstractClassRep* className::getClassRep() const { return &className::dynClassRep; } \ + AbstractClassRep* className::getStaticClassRep() { return &dynClassRep; } \ + AbstractClassRep* className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ + ConcreteClassRep className::dynClassRep(#className, "Type" #className, &_smTypeId, NetClassGroupGameMask, NetClassTypeDataBlock, 0, className::getParentStaticClassRep(), &Parent::__description ) + +// Support for adding properties to classes CONOBJECT style. +#define PROPERTY_TABLE( className ) \ + namespace { namespace _ ## className { \ + extern EnginePropertyTable _propTable; \ + } } \ + template<> EnginePropertyTable& \ + ConcreteClassRep< className >::smPropertyTable = _ ## className::_propTable; \ + namespace { namespace _ ## className { \ + EnginePropertyTable::Property _props[] = { + +#define END_PROPERTY_TABLE \ + { NULL } \ + }; \ + EnginePropertyTable _propTable( sizeof( _props ) / sizeof( _props[ 0 ] ) - 1, _props ); \ + } } + +/// Add an auto-doc for a class. +#define ConsoleDocClass( className, docString ) \ + CLASSDOC( className, docString ) + +/// @} + +//------------------------------------------------------------------------------ +// Protected field default get/set functions +// +// The reason for these functions is that it will save one branch per console +// data request and script functions will still execute at the same speed as +// before the modifications to allow protected static fields. These will just +// inline and the code should be roughly the same size, and just as fast as +// before the modifications. -pw +inline bool defaultProtectedSetFn( void *object, const char *index, const char *data ) +{ + return true; +} + +inline bool defaultProtectedSetNotEmptyFn( void *object, const char *index, const char *data ) +{ + return data && data[0]; +} + +inline const char *defaultProtectedGetFn( void *obj, const char *data ) +{ + return data; +} + +inline const char *emptyStringProtectedGetFn( void *obj, const char *data ) +{ + return ""; +} + +/// @} + +#endif //_CONSOLEOBJECT_H_ diff --git a/Engine/source/console/consoleParser.cpp b/Engine/source/console/consoleParser.cpp new file mode 100644 index 000000000..d9e4b9308 --- /dev/null +++ b/Engine/source/console/consoleParser.cpp @@ -0,0 +1,94 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/consoleParser.h" + +#include "core/strings/stringFunctions.h" +#include "console/console.h" + + +namespace Compiler +{ + +static ConsoleParser *gParserList = NULL; +static ConsoleParser *gDefaultParser = NULL; + +void freeConsoleParserList(void) +{ + while(gParserList) + { + ConsoleParser * pParser = gParserList; + gParserList = pParser->next; + delete pParser; + } + + gDefaultParser = NULL; +} + +bool addConsoleParser(char *ext, fnGetCurrentFile gcf, fnGetCurrentLine gcl, fnParse p, fnRestart r, fnSetScanBuffer ssb, bool def) +{ + AssertFatal(ext && gcf && gcl && p && r, "AddConsoleParser called with one or more NULL arguments"); + + ConsoleParser * pParser = new ConsoleParser; + if(pParser != NULL) + { + pParser->ext = ext; + pParser->getCurrentFile = gcf; + pParser->getCurrentLine = gcl; + pParser->parse = p; + pParser->restart = r; + pParser->setScanBuffer = ssb; + + if(def) + gDefaultParser = pParser; + + pParser->next = gParserList; + gParserList = pParser; + + return true; + } + return false; +} + +ConsoleParser * getParserForFile(const char *filename) +{ + if(filename == NULL) + return gDefaultParser; + + char *ptr = dStrrchr((char *)filename, '.'); + if(ptr != NULL) + { + ptr++; + + ConsoleParser *p; + for(p = gParserList; p; p = p->next) + { + if(dStricmp(ptr, p->ext) == 0) + return p; + } + } + + return gDefaultParser; +} + +} // end namespace Con diff --git a/Engine/source/console/consoleParser.h b/Engine/source/console/consoleParser.h new file mode 100644 index 000000000..d033f75f4 --- /dev/null +++ b/Engine/source/console/consoleParser.h @@ -0,0 +1,124 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _CONSOLE_PARSER_H_ +#define _CONSOLE_PARSER_H_ + +#include + +/// @ingroup console_system Console System +/// @{ + +namespace Compiler +{ + +//----------------------------------------------------------------------------- +/// \brief Function for GetCurrentFile from the lexer +//----------------------------------------------------------------------------- +typedef const char *(*fnGetCurrentFile)(); +//----------------------------------------------------------------------------- +/// \brief Function for GetCurrentLine from the lexer +//----------------------------------------------------------------------------- +typedef S32 (*fnGetCurrentLine)(); +//----------------------------------------------------------------------------- +/// \brief Function for Parse from the lexer +//----------------------------------------------------------------------------- +typedef S32 (*fnParse)(); +//----------------------------------------------------------------------------- +/// \brief Function for Restart from the lexer +//----------------------------------------------------------------------------- +typedef void (*fnRestart)(FILE *input_file); +//----------------------------------------------------------------------------- +/// \brief Function for SetScanBuffer from the lexer +//----------------------------------------------------------------------------- +typedef void (*fnSetScanBuffer)(const char *sb, const char *fn); + +//----------------------------------------------------------------------------- +/// \brief List of parsers for the compiler +//----------------------------------------------------------------------------- +struct ConsoleParser +{ + struct ConsoleParser *next; //!< Next object in list or NULL + + char *ext; //!< Filename extension handled by this parser + + fnGetCurrentFile getCurrentFile; //!< GetCurrentFile lexer function + fnGetCurrentLine getCurrentLine; //!< GetCurrentLine lexer function + fnParse parse; //!< Parse lexer function + fnRestart restart; //!< Restart lexer function + fnSetScanBuffer setScanBuffer; //!< SetScanBuffer lexer function +}; + +// Macros + +//----------------------------------------------------------------------------- +/// \brief Declare a parser's function prototypes +//----------------------------------------------------------------------------- +#define CON_DECLARE_PARSER(prefix) \ + const char * prefix##GetCurrentFile(); \ + S32 prefix##GetCurrentLine(); \ + void prefix##SetScanBuffer(const char *sb, const char *fn); \ + S32 prefix##parse(); \ + void prefix##restart(FILE *input_file) + +//----------------------------------------------------------------------------- +/// \brief Helper macro to add console parsers +//----------------------------------------------------------------------------- +#define CON_ADD_PARSER(prefix, ext, def) \ + Compiler::addConsoleParser(ext, prefix##GetCurrentFile, prefix##GetCurrentLine, prefix##parse, \ + prefix##restart, prefix##SetScanBuffer, def) + +//----------------------------------------------------------------------------- +/// \brief Free the console parser list +/// +/// \sa AddConsoleParser() +//----------------------------------------------------------------------------- +void freeConsoleParserList(void); + +//----------------------------------------------------------------------------- +/// \brief Add a console parser to the list +/// +/// \param ext Filename extension +/// \param gcf GetCurrentFile function +/// \param gcl GetCurrentLine function +/// \param p Parse function +/// \param r Restart function +/// \param ssb SetScanBuffer function +/// \param def true if this is the default parser (Note: set this only on the .cs parser!) +/// \return true for success, false for failure (out of memory) +/// \sa FreeConsoleParserList(), ConsoleParser +//----------------------------------------------------------------------------- +bool addConsoleParser(char *ext, fnGetCurrentFile gcf, fnGetCurrentLine gcl, fnParse p, fnRestart r, fnSetScanBuffer ssb, bool def = false); + +//----------------------------------------------------------------------------- +/// \brief Get the parser for a particular file based on its extension +/// +/// \param filename Filename of file to obtain parser for +/// \sa ConsoleParser +//----------------------------------------------------------------------------- +ConsoleParser * getParserForFile(const char *filename); + +} // end namespace Con + +/// @} + +#endif // _CONSOLE_PARSER_H_ diff --git a/Engine/source/console/consoleTypes.cpp b/Engine/source/console/consoleTypes.cpp new file mode 100644 index 000000000..a948686a4 --- /dev/null +++ b/Engine/source/console/consoleTypes.cpp @@ -0,0 +1,919 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/console.h" +#include "console/consoleTypes.h" +#include "console/simPersistID.h" +#include "core/stringTable.h" +#include "core/util/str.h" +#include "core/util/uuid.h" +#include "core/color.h" +#include "console/simBase.h" +#include "math/mRect.h" + +//----------------------------------------------------------------------------- +// TypeString +//----------------------------------------------------------------------------- +ConsoleType( string, TypeString, const char* ) +ImplementConsoleTypeCasters( TypeString, const char* ); + +ConsoleGetType( TypeString ) +{ + return *((const char **)(dptr)); +} + +ConsoleSetType( TypeString ) +{ + if(argc == 1) + *((const char **) dptr) = StringTable->insert(argv[0]); + else + Con::printf("(TypeString) Cannot set multiple args to a single string."); +} + +//----------------------------------------------------------------------------- +// TypeCaseString +//----------------------------------------------------------------------------- +ConsoleType( caseString, TypeCaseString, const char* ) + +ConsoleSetType( TypeCaseString ) +{ + if(argc == 1) + *((const char **) dptr) = StringTable->insert(argv[0], true); + else + Con::printf("(TypeCaseString) Cannot set multiple args to a single string."); +} + +ConsoleGetType( TypeCaseString ) +{ + return *((const char **)(dptr)); +} + +//----------------------------------------------------------------------------- +// TypeRealString +//----------------------------------------------------------------------------- +ConsoleType( string, TypeRealString, String ) +ImplementConsoleTypeCasters( TypeRealString, String ) + +ConsoleGetType( TypeRealString ) +{ + const String *theString = static_cast(dptr); + + return theString->c_str(); +} + +ConsoleSetType( TypeRealString ) +{ + String *theString = static_cast(dptr); + + if(argc == 1) + *theString = argv[0]; + else + Con::printf("(TypeRealString) Cannot set multiple args to a single string."); +} + +//----------------------------------------------------------------------------- +// TypeCommand +//----------------------------------------------------------------------------- +ConsoleType( string, TypeCommand, String ) + +ConsoleGetType( TypeCommand ) +{ + const String *theString = static_cast(dptr); + + return theString->c_str(); +} + +ConsoleSetType( TypeCommand ) +{ + String *theString = static_cast(dptr); + + if(argc == 1) + *theString = argv[0]; + else + Con::printf("(TypeCommand) Cannot set multiple args to a single command."); +} + +//----------------------------------------------------------------------------- +// TypeFileName +//----------------------------------------------------------------------------- +ConsolePrepType( filename, TypeFilename, const char * ) + +ConsoleSetType( TypeFilename ) +{ + if(argc == 1) + { + char buffer[1024]; + if(argv[0][0] == '$') + { + dStrncpy(buffer, argv[0], sizeof(buffer) - 1); + buffer[sizeof(buffer)-1] = 0; + } + else if (! Con::expandScriptFilename(buffer, 1024, argv[0])) + { + Con::warnf("(TypeFilename) illegal filename detected: %s", argv[0]); + return; + } + + *((const char **) dptr) = StringTable->insert(buffer); + } + else + Con::printf("(TypeFilename) Cannot set multiple args to a single filename."); +} + +ConsoleGetType( TypeFilename ) +{ + return *((const char **)(dptr)); +} + +ConsoleProcessData( TypeFilename ) +{ + if( Con::expandScriptFilename( buffer, bufferSz, data ) ) + return buffer; + else + { + Con::warnf("(TypeFilename) illegal filename detected: %s", data); + return data; + } +} + +//----------------------------------------------------------------------------- +// TypeStringFilename +//----------------------------------------------------------------------------- +ConsolePrepType( filename, TypeStringFilename, String ) + +ConsoleSetType( TypeStringFilename ) +{ + if(argc == 1) + { + char buffer[1024]; + if(argv[0][0] == '$') + { + dStrncpy(buffer, argv[0], sizeof(buffer) - 1); + buffer[sizeof(buffer)-1] = 0; + } + else if (! Con::expandScriptFilename(buffer, 1024, argv[0])) + { + Con::warnf("(TypeStringFilename) illegal filename detected: %s", argv[0]); + return; + } + + *((String*)dptr) = String(buffer); + } + else + Con::printf("(TypeStringFilename) Cannot set multiple args to a single filename."); +} + +ConsoleGetType( TypeStringFilename ) +{ + return *((String*)dptr); +} + +ConsoleProcessData( TypeStringFilename ) +{ + if( Con::expandScriptFilename( buffer, bufferSz, data ) ) + { + return buffer; + } + else + { + Con::warnf("(TypeFilename) illegal filename detected: %s", data); + return data; + } +} + +//----------------------------------------------------------------------------- +// TypePrefabFilename +//----------------------------------------------------------------------------- +ConsolePrepType( filename, TypePrefabFilename, String ) + +ConsoleSetType( TypePrefabFilename ) +{ + Con::setData(TypeStringFilename, dptr, 0, argc, argv, tbl, flag); +} + +ConsoleGetType( TypePrefabFilename ) +{ + return *((String*)dptr); +} + +ConsoleProcessData( TypePrefabFilename ) +{ + if( Con::expandScriptFilename( buffer, bufferSz, data ) ) + { + return buffer; + } + else + { + Con::warnf("(TypePrefabFilename) illegal filename detected: %s", data); + return data; + } +} + +//----------------------------------------------------------------------------- +// TypeImageFilename +//----------------------------------------------------------------------------- +ConsolePrepType( filename, TypeImageFilename, String ) + +ConsoleSetType( TypeImageFilename ) +{ + Con::setData(TypeStringFilename, dptr, 0, argc, argv, tbl, flag); +} + +ConsoleGetType( TypeImageFilename ) +{ + return *((String*)dptr); +} + +ConsoleProcessData( TypeImageFilename ) +{ + if( Con::expandScriptFilename( buffer, bufferSz, data ) ) + return buffer; + else + { + Con::warnf("(TypeImageFilename) illegal filename detected: %s", data); + return data; + } +} + +//----------------------------------------------------------------------------- +// TypeShapeFilename +//----------------------------------------------------------------------------- +ConsolePrepType( filename, TypeShapeFilename, const char* ) + +ConsoleSetType( TypeShapeFilename ) +{ + Con::setData(TypeFilename, dptr, 0, argc, argv, tbl, flag); +} + +ConsoleGetType( TypeShapeFilename ) +{ + return *((const char **)(dptr)); +} + +ConsoleProcessData( TypeShapeFilename ) +{ + if( Con::expandScriptFilename( buffer, bufferSz, data ) ) + return buffer; + else + { + Con::warnf("(TypeShapeFilename) illegal filename detected: %s", data); + return data; + } +} + +//----------------------------------------------------------------------------- +// TypeS8 +//----------------------------------------------------------------------------- +ConsoleType( char, TypeS8, S8 ) +ImplementConsoleTypeCasters( TypeS8, S8 ) + +ConsoleGetType( TypeS8 ) +{ + char* returnBuffer = Con::getReturnBuffer(256); + dSprintf(returnBuffer, 256, "%d", *((U8 *) dptr) ); + return returnBuffer; +} + +ConsoleSetType( TypeS8 ) +{ + if(argc == 1) + *((U8 *) dptr) = dAtoi(argv[0]); + else + Con::printf("(TypeU8) Cannot set multiple args to a single S8."); +} + +//----------------------------------------------------------------------------- +// TypeS32 +//----------------------------------------------------------------------------- +ConsoleType( int, TypeS32, S32 ) +ImplementConsoleTypeCasters(TypeS32, S32) + +ConsoleGetType( TypeS32 ) +{ + char* returnBuffer = Con::getReturnBuffer(256); + dSprintf(returnBuffer, 256, "%d", *((S32 *) dptr) ); + return returnBuffer; +} + +ConsoleSetType( TypeS32 ) +{ + if(argc == 1) + *((S32 *) dptr) = dAtoi(argv[0]); + else + Con::printf("(TypeS32) Cannot set multiple args to a single S32."); +} + + +//----------------------------------------------------------------------------- +// TypeS32Vector +//----------------------------------------------------------------------------- +ConsoleType( intList, TypeS32Vector, Vector ) +ImplementConsoleTypeCasters( TypeS32Vector, Vector< S32 > ) + +ConsoleGetType( TypeS32Vector ) +{ + Vector *vec = (Vector *)dptr; + S32 buffSize = ( vec->size() * 15 ) + 16 ; + char* returnBuffer = Con::getReturnBuffer( buffSize ); + S32 maxReturn = buffSize; + returnBuffer[0] = '\0'; + S32 returnLeng = 0; + for (Vector::iterator itr = vec->begin(); itr != vec->end(); itr++) + { + // concatenate the next value onto the return string + dSprintf(returnBuffer + returnLeng, maxReturn - returnLeng, "%d ", *itr); + // update the length of the return string (so far) + returnLeng = dStrlen(returnBuffer); + } + // trim off that last extra space + if (returnLeng > 0 && returnBuffer[returnLeng - 1] == ' ') + returnBuffer[returnLeng - 1] = '\0'; + return returnBuffer; +} + +ConsoleSetType( TypeS32Vector ) +{ + Vector *vec = (Vector *)dptr; + // we assume the vector should be cleared first (not just appending) + vec->clear(); + if(argc == 1) + { + const char *values = argv[0]; + const char *endValues = values + dStrlen(values); + S32 value; + // advance through the string, pulling off S32's and advancing the pointer + while (values < endValues && dSscanf(values, "%d", &value) != 0) + { + vec->push_back(value); + const char *nextValues = dStrchr(values, ' '); + if (nextValues != 0 && nextValues < endValues) + values = nextValues + 1; + else + break; + } + } + else if (argc > 1) + { + for (S32 i = 0; i < argc; i++) + vec->push_back(dAtoi(argv[i])); + } + else + Con::printf("Vector must be set as { a, b, c, ... } or \"a b c ...\""); +} + +//----------------------------------------------------------------------------- +// TypeF32 +//----------------------------------------------------------------------------- +ConsoleType( float, TypeF32, F32 ) +ImplementConsoleTypeCasters(TypeF32, F32) + +ConsoleGetType( TypeF32 ) +{ + char* returnBuffer = Con::getReturnBuffer(256); + dSprintf(returnBuffer, 256, "%g", *((F32 *) dptr) ); + return returnBuffer; +} +ConsoleSetType( TypeF32 ) +{ + if(argc == 1) + *((F32 *) dptr) = dAtof(argv[0]); + else + Con::printf("(TypeF32) Cannot set multiple args to a single F32."); +} + +//----------------------------------------------------------------------------- +// TypeF32Vector +//----------------------------------------------------------------------------- +ConsoleType( floatList, TypeF32Vector, Vector ) +ImplementConsoleTypeCasters( TypeF32Vector, Vector< F32 > ) + +ConsoleGetType( TypeF32Vector ) +{ + Vector *vec = (Vector *)dptr; + S32 buffSize = ( vec->size() * 15 ) + 16 ; + char* returnBuffer = Con::getReturnBuffer( buffSize ); + S32 maxReturn = buffSize; + returnBuffer[0] = '\0'; + S32 returnLeng = 0; + for (Vector::iterator itr = vec->begin(); itr != vec->end(); itr++) + { + // concatenate the next value onto the return string + dSprintf(returnBuffer + returnLeng, maxReturn - returnLeng, "%g ", *itr); + // update the length of the return string (so far) + returnLeng = dStrlen(returnBuffer); + } + // trim off that last extra space + if (returnLeng > 0 && returnBuffer[returnLeng - 1] == ' ') + returnBuffer[returnLeng - 1] = '\0'; + return returnBuffer; +} + +ConsoleSetType( TypeF32Vector ) +{ + Vector *vec = (Vector *)dptr; + // we assume the vector should be cleared first (not just appending) + vec->clear(); + if(argc == 1) + { + const char *values = argv[0]; + const char *endValues = values + dStrlen(values); + F32 value; + // advance through the string, pulling off F32's and advancing the pointer + while (values < endValues && dSscanf(values, "%g", &value) != 0) + { + vec->push_back(value); + const char *nextValues = dStrchr(values, ' '); + if (nextValues != 0 && nextValues < endValues) + values = nextValues + 1; + else + break; + } + } + else if (argc > 1) + { + for (S32 i = 0; i < argc; i++) + vec->push_back(dAtof(argv[i])); + } + else + Con::printf("Vector must be set as { a, b, c, ... } or \"a b c ...\""); +} + +//----------------------------------------------------------------------------- +// TypeBool +//----------------------------------------------------------------------------- +ConsoleType( bool, TypeBool, bool ) +ImplementConsoleTypeCasters( TypeBool, bool ) + +ConsoleGetType( TypeBool ) +{ + return *((bool *) dptr) ? "1" : "0"; +} + +ConsoleSetType( TypeBool ) +{ + if(argc == 1) + *((bool *) dptr) = dAtob(argv[0]); + else + Con::printf("(TypeBool) Cannot set multiple args to a single bool."); +} + + +//----------------------------------------------------------------------------- +// TypeBoolVector +//----------------------------------------------------------------------------- +ConsoleType( boolList, TypeBoolVector, Vector ) +ImplementConsoleTypeCasters( TypeBoolVector, Vector< bool > ) + +ConsoleGetType( TypeBoolVector ) +{ + Vector *vec = (Vector*)dptr; + char* returnBuffer = Con::getReturnBuffer(1024); + S32 maxReturn = 1024; + returnBuffer[0] = '\0'; + S32 returnLeng = 0; + for (Vector::iterator itr = vec->begin(); itr < vec->end(); itr++) + { + // concatenate the next value onto the return string + dSprintf(returnBuffer + returnLeng, maxReturn - returnLeng, "%d ", (*itr == true ? 1 : 0)); + returnLeng = dStrlen(returnBuffer); + } + // trim off that last extra space + if (returnLeng > 0 && returnBuffer[returnLeng - 1] == ' ') + returnBuffer[returnLeng - 1] = '\0'; + return(returnBuffer); +} + +ConsoleSetType( TypeBoolVector ) +{ + Vector *vec = (Vector*)dptr; + // we assume the vector should be cleared first (not just appending) + vec->clear(); + if (argc == 1) + { + const char *values = argv[0]; + const char *endValues = values + dStrlen(values); + S32 value; + // advance through the string, pulling off bool's and advancing the pointer + while (values < endValues && dSscanf(values, "%d", &value) != 0) + { + vec->push_back(value == 0 ? false : true); + const char *nextValues = dStrchr(values, ' '); + if (nextValues != 0 && nextValues < endValues) + values = nextValues + 1; + else + break; + } + } + else if (argc > 1) + { + for (S32 i = 0; i < argc; i++) + vec->push_back(dAtob(argv[i])); + } + else + Con::printf("Vector must be set as { a, b, c, ... } or \"a b c ...\""); +} + + +//----------------------------------------------------------------------------- +// TypeFlag +//----------------------------------------------------------------------------- +ConsoleType( flag, TypeFlag, S32 ) + +ConsoleGetType( TypeFlag ) +{ + BitSet32 tempFlags = *(BitSet32 *)dptr; + if (tempFlags.test(flag)) return "true"; + else return "false"; +} + +ConsoleSetType( TypeFlag ) +{ + bool value = true; + if (argc != 1) + { + Con::printf("flag must be true or false"); + } + else + { + value = dAtob(argv[0]); + } + ((BitSet32 *)dptr)->set(flag, value); +} + +//----------------------------------------------------------------------------- +// TypeColorF +//----------------------------------------------------------------------------- +ConsoleType( ColorF, TypeColorF, ColorF ) +ImplementConsoleTypeCasters( TypeColorF, ColorF ) + +ConsoleGetType( TypeColorF ) +{ + ColorF * color = (ColorF*)dptr; + char* returnBuffer = Con::getReturnBuffer(256); + dSprintf(returnBuffer, 256, "%g %g %g %g", color->red, color->green, color->blue, color->alpha); + return(returnBuffer); +} + +ConsoleSetType( TypeColorF ) +{ + ColorF *tmpColor = (ColorF *) dptr; + if(argc == 1) + { + tmpColor->set(0, 0, 0, 1); + F32 r,g,b,a; + S32 args = dSscanf(argv[0], "%g %g %g %g", &r, &g, &b, &a); + tmpColor->red = r; + tmpColor->green = g; + tmpColor->blue = b; + if (args == 4) + tmpColor->alpha = a; + } + else if(argc == 3) + { + tmpColor->red = dAtof(argv[0]); + tmpColor->green = dAtof(argv[1]); + tmpColor->blue = dAtof(argv[2]); + tmpColor->alpha = 1.f; + } + else if(argc == 4) + { + tmpColor->red = dAtof(argv[0]); + tmpColor->green = dAtof(argv[1]); + tmpColor->blue = dAtof(argv[2]); + tmpColor->alpha = dAtof(argv[3]); + } + else + Con::printf("Color must be set as { r, g, b [,a] }"); +} + +//----------------------------------------------------------------------------- +// TypeColorI +//----------------------------------------------------------------------------- +ConsoleType( ColorI, TypeColorI, ColorI ) +ImplementConsoleTypeCasters( TypeColorI, ColorI ) + +ConsoleGetType( TypeColorI ) +{ + ColorI *color = (ColorI *) dptr; + char* returnBuffer = Con::getReturnBuffer(256); + dSprintf(returnBuffer, 256, "%d %d %d %d", color->red, color->green, color->blue, color->alpha); + return returnBuffer; +} + +ConsoleSetType( TypeColorI ) +{ + ColorI *tmpColor = (ColorI *) dptr; + if(argc == 1) + { + tmpColor->set(0, 0, 0, 255); + S32 r,g,b,a; + S32 args = dSscanf(argv[0], "%d %d %d %d", &r, &g, &b, &a); + tmpColor->red = r; + tmpColor->green = g; + tmpColor->blue = b; + if (args == 4) + tmpColor->alpha = a; + } + else if(argc == 3) + { + tmpColor->red = dAtoi(argv[0]); + tmpColor->green = dAtoi(argv[1]); + tmpColor->blue = dAtoi(argv[2]); + tmpColor->alpha = 255; + } + else if(argc == 4) + { + tmpColor->red = dAtoi(argv[0]); + tmpColor->green = dAtoi(argv[1]); + tmpColor->blue = dAtoi(argv[2]); + tmpColor->alpha = dAtoi(argv[3]); + } + else + Con::printf("Color must be set as { r, g, b [,a] }"); +} + +//----------------------------------------------------------------------------- +// TypeSimObjectName +//----------------------------------------------------------------------------- +ConsoleType( SimObject, TypeSimObjectName, SimObject* ) + +ConsoleSetType( TypeSimObjectName ) +{ + if(argc == 1) + { + SimObject **obj = (SimObject **)dptr; + *obj = Sim::findObject(argv[0]); + } + else + Con::printf("(TypeSimObjectName) Cannot set multiple args to a single S32."); +} + +ConsoleGetType( TypeSimObjectName ) +{ + SimObject **obj = (SimObject**)dptr; + char* returnBuffer = Con::getReturnBuffer(128); + dSprintf(returnBuffer, 128, "%s", *obj && (*obj)->getName() ? (*obj)->getName() : ""); + return returnBuffer; +} + +//----------------------------------------------------------------------------- +// TypeName +//----------------------------------------------------------------------------- +ConsoleType( string, TypeName, const char* ) + +ConsoleGetType( TypeName ) +{ + return *((const char **)(dptr)); +} + +ConsoleSetType( TypeName ) +{ + Con::warnf( "ConsoleSetType( TypeName ) should not be called. A ProtectedSetMethod does this work!" ); +} + +//------------------------------------------------------------------------------ +// TypeParticleParameterString +//------------------------------------------------------------------------------ +ConsoleType( string, TypeParticleParameterString, const char* ) + +ConsoleGetType( TypeParticleParameterString ) +{ + return *((const char **)(dptr)); +} + +ConsoleSetType( TypeParticleParameterString ) +{ + if(argc == 1) + *((const char **) dptr) = StringTable->insert(argv[0]); + else + Con::printf("(TypeParticleParameterString) Cannot set multiple args to a single string."); +} + +//----------------------------------------------------------------------------- +// TypeMaterialName +//----------------------------------------------------------------------------- + +ConsoleType( string, TypeMaterialName, String ) + +ConsoleGetType( TypeMaterialName ) +{ + const String *theString = static_cast(dptr); + return theString->c_str(); +} + +ConsoleSetType( TypeMaterialName ) +{ + String *theString = static_cast(dptr); + + if(argc == 1) + *theString = argv[0]; + else + Con::printf("(TypeMaterialName) Cannot set multiple args to a single string."); +} + +//----------------------------------------------------------------------------- +// TypeTerrainMaterialIndex +//----------------------------------------------------------------------------- + +ConsoleType( int, TypeTerrainMaterialIndex, S32 ) + +ConsoleGetType( TypeTerrainMaterialIndex ) +{ + char* returnBuffer = Con::getReturnBuffer(256); + dSprintf(returnBuffer, 256, "%d", *((S32 *) dptr) ); + return returnBuffer; +} + +ConsoleSetType( TypeTerrainMaterialIndex ) +{ + if(argc == 1) + *((S32 *) dptr) = dAtoi(argv[0]); + else + Con::printf("(TypeTerrainMaterialIndex) Cannot set multiple args to a single S32."); +} + +//----------------------------------------------------------------------------- +// TypeTerrainMaterialName +//----------------------------------------------------------------------------- + +ConsoleType( string, TypeTerrainMaterialName, const char* ) + +ConsoleGetType( TypeTerrainMaterialName ) +{ + return *((const char **)(dptr)); +} + +ConsoleSetType( TypeTerrainMaterialName ) +{ + if(argc == 1) + *((const char **) dptr) = StringTable->insert(argv[0]); + else + Con::printf("(TypeTerrainMaterialName) Cannot set multiple args to a single string."); +} + +//----------------------------------------------------------------------------- +// TypeCubemapName +//----------------------------------------------------------------------------- + +ConsoleType( string, TypeCubemapName, String ) + +ConsoleGetType( TypeCubemapName ) +{ + const String *theString = static_cast(dptr); + return theString->c_str(); +} + +ConsoleSetType( TypeCubemapName ) +{ + String *theString = static_cast(dptr); + + if(argc == 1) + *theString = argv[0]; + else + Con::printf("(TypeCubemapName) Cannot set multiple args to a single string."); +} + +//----------------------------------------------------------------------------- +// TypeRectUV +//----------------------------------------------------------------------------- +ConsoleType( RectF, TypeRectUV, RectF ) + +ConsoleGetType( TypeRectUV ) +{ + RectF *rect = (RectF *) dptr; + char* returnBuffer = Con::getReturnBuffer(256); + dSprintf(returnBuffer, 256, "%g %g %g %g", rect->point.x, rect->point.y, + rect->extent.x, rect->extent.y); + return returnBuffer; +} + +ConsoleSetType( TypeRectUV ) +{ + if(argc == 1) + dSscanf(argv[0], "%g %g %g %g", &((RectF *) dptr)->point.x, &((RectF *) dptr)->point.y, + &((RectF *) dptr)->extent.x, &((RectF *) dptr)->extent.y); + else if(argc == 4) + *((RectF *) dptr) = RectF(dAtof(argv[0]), dAtof(argv[1]), dAtof(argv[2]), dAtof(argv[3])); + else + Con::printf("RectUV must be set as { x, y, w, h } or \"x y w h\""); +} + +//----------------------------------------------------------------------------- +// TypeUUID +//----------------------------------------------------------------------------- +ConsoleType( uuid, TypeUUID, Torque::UUID ) +ImplementConsoleTypeCasters( TypeUUID, Torque::UUID ) + +ConsoleGetType( TypeUUID ) +{ + Torque::UUID* uuid = ( Torque::UUID* ) dptr; + return Con::getReturnBuffer( uuid->toString() ); +} + +ConsoleSetType( TypeUUID ) +{ + if( argc == 1 ) + { + Torque::UUID* uuid = ( Torque::UUID* ) dptr; + if( !uuid->fromString( argv[ 0 ] ) ) + Con::errorf( "Error parsing UUID: '%s'", argv[ 0 ] ); + } + else + Con::printf( "(TypeUUID) Cannot set multiple args to a single uuid." ); +} + +//----------------------------------------------------------------------------- +// TypePID +//----------------------------------------------------------------------------- +ConsoleType( pid, TypePID, SimPersistID* ) +ImplementConsoleTypeCasters( TypePID, SimPersistID* ) + +ConsoleGetType( TypePID ) +{ + SimPersistID* pid = *( ( SimPersistID** ) dptr ); + if( !pid ) + return ""; + + return Con::getReturnBuffer( pid->getUUID().toString() ); +} + +ConsoleSetType( TypePID ) +{ + if( argc == 1 ) + { + SimPersistID** pid = ( SimPersistID** ) dptr; + + if( !argv[ 0 ][ 0 ] ) + *pid = NULL; + else + { + Torque::UUID uuid; + if( !uuid.fromString( argv[ 0 ] ) ) + { + Con::errorf( "Error parsing UUID in PID: '%s'", argv[ 0 ] ); + *pid = NULL; + } + else + *pid = SimPersistID::findOrCreate( uuid ); + } + } + else + Con::printf( "(TypePID) Cannot set multiple args to a single pid." ); +} + +//----------------------------------------------------------------------------- +// TypeSimPersistId +//----------------------------------------------------------------------------- +ConsoleType( SimPersistId, TypeSimPersistId, SimPersistID* ) + +ConsoleGetType( TypeSimPersistId ) +{ + SimPersistID* pid = *( ( SimPersistID** ) dptr ); + if( !pid ) + return ""; + + return Con::getReturnBuffer( pid->getUUID().toString() ); +} + +ConsoleSetType( TypeSimPersistId ) +{ + if( argc == 1 ) + { + SimPersistID** pid = ( SimPersistID** ) dptr; + + if( !argv[ 0 ][ 0 ] ) + *pid = NULL; + else + { + Torque::UUID uuid; + if( !uuid.fromString( argv[ 0 ] ) ) + { + Con::errorf( "Error parsing UUID in PID: '%s'", argv[ 0 ] ); + *pid = NULL; + } + else + *pid = SimPersistID::find( uuid ); + } + } + else + Con::printf( "(TypeSimPersistId) Cannot set multiple args to a single SimPersistId." ); +} diff --git a/Engine/source/console/consoleTypes.h b/Engine/source/console/consoleTypes.h new file mode 100644 index 000000000..4e9d180e9 --- /dev/null +++ b/Engine/source/console/consoleTypes.h @@ -0,0 +1,144 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _CONSOLETYPES_H_ +#define _CONSOLETYPES_H_ + +#ifndef _DYNAMIC_CONSOLETYPES_H_ +#include "console/dynamicTypes.h" +#endif + +#ifndef _MATHTYPES_H_ +#include "math/mathTypes.h" +#endif + +#ifndef _ENGINEPRIMITIVES_H_ +#include "console/enginePrimitives.h" +#endif + +#ifndef _ENGINESTRUCTS_H_ +#include "console/engineStructs.h" +#endif + + +/// @file +/// Legacy TS-based console type definitions. + + +/// @ingroup console_system Console System +/// @{ + +#ifndef Offset +#if defined(TORQUE_COMPILER_GCC) && (__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1)) +#define Offset(m,T) ((int)(&((T *)1)->m) - 1) +#else +#define Offset(x, cls) ((dsize_t)((const char *)&(((cls *)0)->x)-(const char *)0)) +#endif +#endif + +class GFXShader; +class GFXCubemap; +class CustomMaterial; +class ProjectileData; +class ParticleEmitterData; +class SimPersistID; + + +// Define Core Console Types +DefineConsoleType( TypeBool, bool ) +DefineConsoleType( TypeBoolVector, Vector) +DefineConsoleType( TypeS8, S8 ) +DefineConsoleType( TypeS32, S32 ) +DefineConsoleType( TypeS32Vector, Vector ) +DefineConsoleType( TypeF32, F32 ) +DefineConsoleType( TypeF32Vector, Vector ) +DefineUnmappedConsoleType( TypeString, const char * ) // plain UTF-8 strings are not supported in new interop +DefineConsoleType( TypeCaseString, const char * ) +DefineConsoleType( TypeRealString, String ) +DefineConsoleType( TypeCommand, String ) +DefineConsoleType( TypeFilename, const char * ) +DefineConsoleType( TypeStringFilename, String ) + +/// A universally unique identifier. +DefineConsoleType( TypeUUID, Torque::UUID ) + +/// A persistent ID that is associated with an object. This type cannot +/// be used to reference PIDs of other objects. +DefineUnmappedConsoleType( TypePID, SimPersistID* ); + +/// TypeImageFilename is equivalent to TypeStringFilename in its usage, +/// it exists for the benefit of GuiInspector, which will provide a custom +/// InspectorField for this type that can display a texture preview. +DefineConsoleType( TypeImageFilename, String ) + +/// TypePrefabFilename is equivalent to TypeStringFilename in its usage, +/// it exists for the benefit of GuiInspector, which will provide a +/// custom InspectorField for this type. +DefineConsoleType( TypePrefabFilename, String ) + +/// TypeShapeFilename is equivalent to TypeStringFilename in its usage, +/// it exists for the benefit of GuiInspector, which will provide a +/// custom InspectorField for this type. +DefineConsoleType( TypeShapeFilename, String ) + +/// TypeMaterialName is equivalent to TypeRealString in its usage, +/// it exists for the benefit of GuiInspector, which will provide a +/// custom InspectorField for this type. +DefineConsoleType( TypeMaterialName, String ) + +/// TypeTerrainMaterialIndex is equivalent to TypeS32 in its usage, +/// it exists for the benefit of GuiInspector, which will provide a +/// custom InspectorField for this type. +DefineConsoleType( TypeTerrainMaterialIndex, S32 ) + +/// TypeTerrainMaterialName is equivalent to TypeString in its usage, +/// it exists for the benefit of GuiInspector, which will provide a +/// custom InspectorField for this type. +DefineConsoleType( TypeTerrainMaterialName, const char * ) + +/// TypeCubemapName is equivalent to TypeRealString in its usage, +/// but the Inspector will provide a drop-down list of CubemapData objects. +DefineConsoleType( TypeCubemapName, String ) + +DefineConsoleType( TypeParticleParameterString, const char * ) + +DefineConsoleType( TypeFlag, S32 ) +DefineConsoleType( TypeColorI, ColorI ) +DefineConsoleType( TypeColorF, ColorF ) +DefineConsoleType( TypeSimObjectName, SimObject* ) +DefineConsoleType( TypeShader, GFXShader * ) + +/// A persistent reference to an object. This reference indirectly goes +/// through the referenced object's persistent ID. +DefineConsoleType( TypeSimPersistId, SimPersistID* ) + +/// Special field type for SimObject::objectName +DefineConsoleType( TypeName, const char* ) + +/// TypeRectUV is equivalent to TypeRectF in its usage, +/// it exists for the benefit of GuiInspector, which will provide a +/// custom InspectorField for this type. +DefineConsoleType( TypeRectUV, RectF ) + +/// @} + +#endif diff --git a/Engine/source/console/consoleXMLExport.cpp b/Engine/source/console/consoleXMLExport.cpp new file mode 100644 index 000000000..f6764ac77 --- /dev/null +++ b/Engine/source/console/consoleXMLExport.cpp @@ -0,0 +1,326 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/consoleInternal.h" +#include "console/consoleObject.h" +#include "console/SimXMLDocument.h" + +#include "console/consoleXMLExport.h" + + +#if 0 + +namespace Con { + + XMLExport::XMLExport() + { + mXML = NULL; + } + + XMLExport::~XMLExport() + { + } + + void XMLExport::exportBaseTypes() + { + mXML->pushNewElement("BaseTypes"); + + ConsoleBaseType *walk = ConsoleBaseType::getListHead(); + while( walk != NULL ) + { + mXML->pushNewElement("BaseType"); + + mXML->setAttribute("name", walk->getTypeName()); + mXML->setAttribute("id", avar("%i",walk->getTypeID())); + mXML->setAttribute("size", avar("%i",walk->getTypeSize())); + mXML->setAttribute("doc", walk->getDocString() ? walk->getDocString() : "" ); + + mXML->popElement(); // Basetype + + walk = walk->getListNext(); + } + + mXML->popElement(); // Basetypes + + } + + void XMLExport::exportEntryTypes() + { + + const char *typeNames [] = { + "ScriptCallbackType", "GroupMarker", "OverloadMarker", "InvalidFunctionType", + "ConsoleFunctionType", "StringCallbackType", "IntCallbackType", "FloatCallbackType", + "VoidCallbackType", "BoolCallbackType" + }; + + S32 typeIds [] = { + Namespace::Entry::ScriptCallbackType, Namespace::Entry::GroupMarker, Namespace::Entry::InvalidFunctionType, + Namespace::Entry::ConsoleFunctionType, Namespace::Entry::StringCallbackType, Namespace::Entry::IntCallbackType, Namespace::Entry::FloatCallbackType, + Namespace::Entry::VoidCallbackType, Namespace::Entry::BoolCallbackType + }; + + mXML->pushNewElement("EntryTypes"); + + S32 numElements = sizeof(typeIds) / sizeof ( S32); + + for (S32 i = 0; i < numElements; i++) + { + mXML->pushNewElement("EntryType"); + mXML->setAttribute("name", typeNames[i]); + mXML->setAttribute("id", avar("%i", typeIds[i])); + mXML->popElement(); + } + + mXML->popElement(); // EntryTypes + } + + void XMLExport::exportNamespaces() + { + + // keep track of which enumTables are in use + Vector < const EnumTable*> enumTables; + + mXML->pushNewElement("Namespaces"); + + for (Namespace *walk = Namespace::mNamespaceList; walk; walk = walk->mNext) + { + + if ( walk->mName && !walk->isClass() ) + continue; + + const char *name = walk->mName ? walk->mName : ""; + + mXML->pushNewElement("Namespace"); + mXML->setAttribute("name", name); + + Namespace *p = walk->mParent; + + mXML->pushNewElement("Parents"); + + while (p) + { + if (p->mName == walk->mName) + { + p = p->mParent; + continue; + } + + const char* pname = p->mName ? p->mName : ""; + + mXML->pushNewElement("Parent"); + mXML->setAttribute("name", pname); + mXML->popElement(); // Parent + + p = p->mParent; + } + + mXML->popElement(); // Parents + + // Entries (Engine/Script Methods/Functions) + + mXML->pushNewElement("Entries"); + + Namespace::Entry *entry; + VectorPtr vec; + + walk->getEntryList(&vec); + + for( NamespaceEntryListIterator compItr = vec.begin(); compItr != vec.end(); compItr++ ) + { + + entry = *compItr; + + if (entry->mNamespace != walk) + continue; + + if (entry->mNamespace->mName != walk->mName) + continue; + + mXML->pushNewElement("Entry"); + + //consistently name functions + char functionName[512]; + dSprintf(functionName, 512, entry->mFunctionName); + functionName[0] = dTolower(functionName[0]); + + S32 minArgs = entry->mMinArgs; + S32 maxArgs = entry->mMaxArgs; + + if (maxArgs < minArgs) + maxArgs = minArgs; + + mXML->setAttribute("name", functionName); + mXML->setAttribute("minArgs", avar("%i", minArgs)); + mXML->setAttribute("maxArgs", avar("%i", maxArgs)); + + const char* usage = ""; + if (entry->mUsage && entry->mUsage[0]) + usage = entry->mUsage; + mXML->setAttribute("usage", usage); + mXML->setAttribute("package", entry->mPackage ? entry->mPackage : ""); + mXML->setAttribute("entryType", avar("%i", entry->mType)); + + mXML->popElement(); // Entry + + } + + mXML->popElement(); // Entries + + // Fields + + mXML->pushNewElement("Fields"); + + AbstractClassRep *rep = walk->mClassRep; + Vector classFields; + + if (rep) + { + AbstractClassRep *parentRep = rep->getParentClass(); + + const AbstractClassRep::FieldList& flist = rep->mFieldList; + + for(U32 i = 0; i < flist.size(); i++) + { + if (parentRep) + { + if (parentRep->findField(flist[i].pFieldname)) + continue; + + } + classFields.push_back(i); + } + + for(U32 i = 0; i < classFields.size(); i++) + { + U32 index = classFields[i]; + + char fieldName[256]; + + dSprintf(fieldName, 256, flist[index].pFieldname); + + //consistently name fields + fieldName[0] = dToupper(fieldName[0]); + + mXML->pushNewElement("Field"); + + mXML->setAttribute("name", fieldName); + mXML->setAttribute("type", avar("%i", flist[index].type)); + +// RD: temporarily deactivated; TypeEnum is no more; need to sync this up +// if (flist[index].type == TypeEnum && flist[index].table && dStrlen(flist[index].table->name)) +// { +// if (!enumTables.contains(flist[index].table)) +// enumTables.push_back(flist[index].table); +// +// mXML->setAttribute("enumTable", flist[index].table->name); +// +// } + + const char* pFieldDocs = ""; + if (flist[index].pFieldDocs && flist[index].pFieldDocs[0]) + pFieldDocs = flist[index].pFieldDocs; + + mXML->setAttribute("docs", pFieldDocs); + mXML->setAttribute("elementCount", avar("%i", flist[index].elementCount)); + + mXML->popElement(); // Field + } + } + + mXML->popElement(); // Fields + mXML->popElement(); // Namespace + } + + mXML->popElement(); // Namespaces + + mXML->pushNewElement("EnumTables"); + + // write out the used EnumTables + for (int i = 0; i < enumTables.size(); i++) + { + mXML->pushNewElement("EnumTable"); + + const EnumTable* table = enumTables[i]; + + mXML->setAttribute("name", table->name); + mXML->setAttribute("firstFlag", avar("%i", table->firstFlag)); + mXML->setAttribute("mask", avar("%i", table->mask)); + + mXML->pushNewElement("Enums"); + + for (int j = 0; j < table->size; j++) + { + mXML->pushNewElement("Enum"); + + mXML->setAttribute("name", table->table[j].label); + mXML->setAttribute("index", avar("%i", table->table[j].index)); + + mXML->popElement(); // Enum + + } + + mXML->popElement(); //Enums + + mXML->popElement(); // EnumTable + } + + mXML->popElement(); // EnumTables + + } + + void XMLExport::exportXML(String& str) + { + mXML = new SimXMLDocument(); + mXML->registerObject(); + + mXML->addHeader(); + + mXML->pushNewElement("ConsoleXML"); + + exportBaseTypes(); + exportEntryTypes(); + exportNamespaces(); + + mXML->popElement(); // TorqueConsole + + mXML->saveToString(str); + + // If you're having trouble with the generated XML, you can dump to a file and inspect + // mXML->saveFile("ConsoleExport.xml"); + + mXML->deleteObject(); + } + +}; // namespace Con + + +ConsoleFunction(consoleExportXML, const char*, 1, 1, "Exports console definition XML representation") +{ + Con::XMLExport xmlExport; + String xml; + xmlExport.exportXML(xml); + char* ret = Con::getReturnBuffer(xml.length() + 1); + dStrcpy(ret, xml.c_str()); + return ret; +} + +#endif diff --git a/Engine/source/console/consoleXMLExport.h b/Engine/source/console/consoleXMLExport.h new file mode 100644 index 000000000..27a0d24f0 --- /dev/null +++ b/Engine/source/console/consoleXMLExport.h @@ -0,0 +1,65 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _CONSOLEXMLEXPORT_H_ +#define _CONSOLEXMLEXPORT_H_ + +#ifndef _TORQUE_STRING_H_ +#include "core/util/str.h" +#endif + +class SimXMLDocument; + +/// @defgroup console_reflection Reflection +/// @ingroup console_system Console System +/// +/// Exports console information to XML representation +/// @{ + +namespace Con { + + class XMLExport + { + + public: + + XMLExport(); + ~XMLExport(); + + // writes console information in XML format to the file specified + void exportXML(String& str); + + private: + + void exportBaseTypes(); + void exportEntryTypes(); + void exportNamespaces(); + + SimXMLDocument *mXML; + + }; + +}; + +/// @} + +#endif _CONSOLEXMLEXPORT_H_ diff --git a/Engine/source/console/debugOutputConsumer.cpp b/Engine/source/console/debugOutputConsumer.cpp new file mode 100644 index 000000000..7d3e37f42 --- /dev/null +++ b/Engine/source/console/debugOutputConsumer.cpp @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/debugOutputConsumer.h" + +namespace DebugOutputConsumer +{ +#ifndef DISABLE_DEBUG_SPEW +bool debugOutputEnabled = true; +#else +bool debugOutputEnabled = false; +#endif + + +void init() +{ + Con::addConsumer( DebugOutputConsumer::logCallback ); +} + +void destroy() +{ + Con::removeConsumer( DebugOutputConsumer::logCallback ); +} + +void logCallback( U32 level, const char *consoleLine ) +{ + TORQUE_UNUSED(level); + if( debugOutputEnabled ) + { + Platform::outputDebugString( "%s", consoleLine ); + } +} + +} diff --git a/Engine/source/console/debugOutputConsumer.h b/Engine/source/console/debugOutputConsumer.h new file mode 100644 index 000000000..383967f88 --- /dev/null +++ b/Engine/source/console/debugOutputConsumer.h @@ -0,0 +1,47 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _DEBUGOUTPUTCONSUMER_H_ +#define _DEBUGOUTPUTCONSUMER_H_ + +#include "platform/platform.h" + +//#define TORQUE_LOCBUILD + +#if !defined(TORQUE_DEBUG) && defined(TORQUE_OS_XENON) && !defined(TORQUE_LOCBUILD) +#define DISABLE_DEBUG_SPEW +#endif + +#include "console/console.h" + +namespace DebugOutputConsumer +{ + extern bool debugOutputEnabled; + + void init(); + void destroy(); + void logCallback( U32 level, const char *consoleLine ); + + void enableDebugOutput( bool enable = true ); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/console/dynamicTypes.cpp b/Engine/source/console/dynamicTypes.cpp new file mode 100644 index 000000000..cdbb23dd5 --- /dev/null +++ b/Engine/source/console/dynamicTypes.cpp @@ -0,0 +1,119 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/dynamicTypes.h" +#include "core/stringTable.h" +#include "core/strings/stringFunctions.h" + + +ConsoleBaseType* ConsoleBaseType::smListHead = NULL; +S32 ConsoleBaseType::smConsoleTypeCount = 0; + +// And, we also privately store the types lookup table. +static VectorPtr< ConsoleBaseType* > gConsoleTypeTable( __FILE__, __LINE__ ); + + +//----------------------------------------------------------------------------- + +ConsoleBaseType::ConsoleBaseType( const S32 size, S32 *idPtr, const char *aTypeName ) + : mInspectorFieldType( NULL ), + mTypeSize( size ), + mTypeInfo( NULL ) +{ + mTypeName = StringTable->insert( aTypeName ); + mTypeID = smConsoleTypeCount++; + *idPtr = mTypeID; + + // Link ourselves into the list. + mListNext = smListHead; + smListHead = this; +} + +//----------------------------------------------------------------------------- + +ConsoleBaseType *ConsoleBaseType::getListHead() +{ + return smListHead; +} + +//----------------------------------------------------------------------------- + +void ConsoleBaseType::initialize() +{ + // Prep and empty the vector. + gConsoleTypeTable.setSize(smConsoleTypeCount+1); + dMemset(gConsoleTypeTable.address(), 0, sizeof(ConsoleBaseType*) * gConsoleTypeTable.size()); + + // Walk the list and register each one with the console system. + ConsoleBaseType *walk = getListHead(); + while(walk) + { + const S32 id = walk->getTypeID(); + AssertFatal(gConsoleTypeTable[id]==NULL, "ConsoleBaseType::initialize - encountered a table slot that contained something!"); + gConsoleTypeTable[id] = walk; + + walk = walk->getListNext(); + } +} + +//----------------------------------------------------------------------------- + +ConsoleBaseType* ConsoleBaseType::getType(const S32 typeID) +{ + if( typeID == -1 || gConsoleTypeTable.size() <= typeID ) + return NULL; + + return gConsoleTypeTable[ typeID ]; +} + +//----------------------------------------------------------------------------- + +ConsoleBaseType* ConsoleBaseType::getTypeByName(const char *typeName) +{ + ConsoleBaseType* walk = getListHead(); + while( walk != NULL ) + { + if( dStrcmp( walk->getTypeName(), typeName ) == 0 ) + return walk; + + walk = walk->getListNext(); + } + + return NULL; +} + +//----------------------------------------------------------------------------- + +ConsoleBaseType * ConsoleBaseType::getTypeByClassName(const char *typeName) +{ + ConsoleBaseType *walk = getListHead(); + while( walk != NULL ) + { + if( dStrcmp( walk->getTypeClassName(), typeName ) == 0 ) + return walk; + + walk = walk->getListNext(); + } + + return NULL; +} diff --git a/Engine/source/console/dynamicTypes.h b/Engine/source/console/dynamicTypes.h new file mode 100644 index 000000000..f03beead2 --- /dev/null +++ b/Engine/source/console/dynamicTypes.h @@ -0,0 +1,375 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _DYNAMIC_CONSOLETYPES_H_ +#define _DYNAMIC_CONSOLETYPES_H_ + +#ifndef _CONSOLE_H_ +#include "console/console.h" +#endif + +#ifndef _STRINGFUNCTIONS_H_ +#include "core/strings/stringFunctions.h" +#endif + +#ifndef _ENGINETYPEINFO_H_ +#include "console/engineTypeInfo.h" +#endif + + +/// @file +/// Support for legacy TorqueScript console types. + + +/// Information about a console type. +class ConsoleBaseType +{ + protected: + + /// Unique numeric type ID. + S32 mTypeID; + + /// Size of a single data value point. + dsize_t mTypeSize; + + /// Name of this console type (TypeXXX). + StringTableEntry mTypeName; + + /// Name of GuiInspectorField subclass to instantiate for field edit controls in + /// inspectors. + const char* mInspectorFieldType; + + /// Next item in the list of all console types. + ConsoleBaseType* mListNext; + + /// Type info object for the engine type corresponding to this console type. + /// Since several console types may be mapped to a single native type, this type info + /// instance may be shared by multiple ConsoleBaseType instances. + /// NULL if the console type is not mapped to an engine API type. + const EngineTypeInfo* mTypeInfo; + + /// Total number of defined console types. This is used to generate unique IDs for each type. + static S32 smConsoleTypeCount; + + /// We maintain a linked list of all console types; this is its head. + static ConsoleBaseType* smListHead; + + /// The constructor is responsible for linking an element into the + /// master list, registering the type ID, etc. + ConsoleBaseType( const S32 size, S32 *idPtr, const char *aTypeName ); + + /// Destructor is private to avoid people mucking up the list. + ~ConsoleBaseType() {} + + public: + + /// @name cbt_list List Interface + /// + /// Interface for accessing/traversing the list of types. + + /// Get the head of the list. + static ConsoleBaseType *getListHead(); + + /// Get the item that follows this item in the list. + ConsoleBaseType *getListNext() const + { + return mListNext; + } + + /// @} + + /// Called once to initialize the console type system. + static void initialize(); + + /// Call me to get a pointer to a type's info. + static ConsoleBaseType *getType( const S32 typeID ); + + /// Call to get a pointer to a type's info + static ConsoleBaseType *getTypeByName( const char *typeName ); + static ConsoleBaseType *getTypeByClassName( const char *typeName ); + + /// Return the unique numeric ID of this type. + S32 getTypeID() const { return mTypeID; } + + /// Return the size of a single value in bytes. + S32 getTypeSize() const { return mTypeSize; } + + /// Return the console type name (TypeXXX). + StringTableEntry getTypeName() const { return mTypeName; } + + /// Return the type info for the engine type corresponding to this console type or NULL if + /// there is no mapping for the console type. + const EngineTypeInfo* getTypeInfo() const { return mTypeInfo; } + + /// Return the documentation string for this type. + const char* getDocString() const { return getTypeInfo() ? getTypeInfo()->getDocString() : ""; } + + /// Return the EnumTable for this type (only for enumeration types). + const EngineEnumTable* getEnumTable() const { return getTypeInfo() ? getTypeInfo()->getEnumTable() : NULL; } + + /// Return the name of the GuiInspectorField subclass that fields of this + /// type should use for editing. + const char* getInspectorFieldType() { return mInspectorFieldType; } + + /// Set the name of the GuiInspectorField subclass that fields of this type + /// should use for editing. + void setInspectorFieldType( const char* type ) { mInspectorFieldType = type; } + + /// @name Value Handling Interface + /// @{ + + virtual void setData( void* dptr, S32 argc, const char** argv, const EnumTable* tbl, BitSet32 flag ) = 0; + virtual const char* getData( void* dptr, const EnumTable* tbl, BitSet32 flag ) = 0; + virtual const char* getTypeClassName() = 0; + + /// Allocate a single value. + virtual void* getNativeVariable() = 0; + + /// Delete a single value allocated with getNativeVariable(). + virtual void deleteNativeVariable(void* var) = 0; + + /// Return true if this is datablock object type. + virtual const bool isDatablock() { return false; }; + + virtual const char* prepData( const char* data, char* buffer, U32 bufferLen ) { return data; }; + + /// @} +}; + + +class EnumConsoleBaseType : public ConsoleBaseType +{ + public: + + typedef ConsoleBaseType Parent; + + protected: + + EnumConsoleBaseType( S32 size, S32* idPtr, const char* typeName ) + : Parent( size, idPtr, typeName ) {} + + public: + + virtual const char* getData(void *dptr, const EnumTable *tbl, BitSet32 flag) + { + S32 dptrVal = *( S32* ) dptr; + if( !tbl ) tbl = getEnumTable(); + const U32 numEnums = tbl->getNumValues(); + for( U32 i = 0; i < numEnums; ++ i ) + if( dptrVal == ( *tbl )[ i ].mInt ) + return ( *tbl )[ i ].mName; + return ""; + } + virtual void setData(void *dptr, S32 argc, const char **argv, const EnumTable *tbl, BitSet32 flag) + { + if( argc != 1 ) return; + if( !tbl ) tbl = getEnumTable(); + S32 val = 0; + const U32 numEnums = tbl->getNumValues(); + for( U32 i = 0; i < numEnums; ++ i ) + if( dStricmp( argv[ 0 ], ( *tbl )[ i ].mName ) == 0 ) + { + val = ( *tbl )[ i ].mInt; + break; + } + *( ( S32* ) dptr ) = val; + } +}; + + +class BitfieldConsoleBaseType : public ConsoleBaseType +{ + public: + + typedef ConsoleBaseType Parent; + + protected: + + BitfieldConsoleBaseType( S32 size, S32* idPtr, const char* typeName ) + : Parent( size, idPtr, typeName ) {} + + public: + + virtual const char* getData( void* dptr, const EnumTable*, BitSet32 ) + { + char* returnBuffer = Con::getReturnBuffer(256); + dSprintf(returnBuffer, 256, "0x%08x", *((S32 *) dptr) ); + return returnBuffer; + } + virtual void setData( void* dptr, S32 argc, const char** argv, const EnumTable*, BitSet32 ) + { + if( argc != 1 ) return; \ + *((S32 *) dptr) = dAtoui(argv[0],0); \ + } +}; + + + +template< typename T > +struct _ConsoleConstType +{ + typedef const T ConstType; +}; + +/// Return the type ID for the primary console type associated with the given native type. +/// +/// There can only be one console type associated with a C++ type. This is referred to as the primary +/// console type. +/// +/// @return The type ID of the primary console type for "T". +template< typename T > +S32 TYPEID() { return T::_smTypeId; } // Default assumes a structured type storing its ID in a static member variable. + + +// Helper to allow to override certain mappings. +template< typename T > +const EngineTypeInfo* _MAPTYPE() { return TYPE< T >(); } + + +/// @name Console Type Macros +/// @{ + +#define DefineConsoleType( type, nativeType ) \ + extern S32 type; \ + extern const char* castConsoleTypeToString( _ConsoleConstType< nativeType >::ConstType &arg ); \ + extern bool castConsoleTypeFromString( nativeType &arg, const char *str ); \ + template<> extern S32 TYPEID< nativeType >(); + +#define DefineUnmappedConsoleType( type, nativeType ) \ + DefineConsoleType( type, nativeType ) \ + template<> inline const EngineTypeInfo* _MAPTYPE< nativeType >() { return NULL; } + +#define ConsoleType( typeName, type, nativeType ) \ + S32 type; \ + class ConsoleType##type : public ConsoleBaseType \ + { \ + public: \ + typedef nativeType T; \ + ConsoleType##type() \ + : ConsoleBaseType( sizeof( nativeType ), &type, #type ) \ + { \ + mTypeInfo = _MAPTYPE< nativeType >(); \ + } \ + virtual void setData(void *dptr, S32 argc, const char **argv, const EnumTable *tbl, BitSet32 flag); \ + virtual const char *getData(void *dptr, const EnumTable *tbl, BitSet32 flag ); \ + virtual const char *getTypeClassName() { return #typeName ; } \ + virtual void *getNativeVariable() { T* var = new T; return (void*)var; } \ + virtual void deleteNativeVariable(void* var) { T* nativeVar = reinterpret_cast(var); delete nativeVar; } \ + }; \ + ConsoleType ## type gConsoleType ## type ## Instance; + +#define ImplementConsoleTypeCasters( type, nativeType ) \ + const char *castConsoleTypeToString( _ConsoleConstType< nativeType >::ConstType &arg ) { return Con::getData(type, const_cast< nativeType* >( &arg ), 0); } \ + bool castConsoleTypeFromString( nativeType &arg, const char *str ) { Con::setData(type, const_cast< nativeType* >( &arg ), 0, 1, &str); return true; } \ + template<> S32 TYPEID< nativeType >() { return type; } + + +#define ConsolePrepType( typeName, type, nativeType ) \ + S32 type; \ + class ConsoleType##type : public ConsoleBaseType \ + { \ + public: \ + typedef nativeType T; \ + ConsoleType##type() \ + : ConsoleBaseType( sizeof( nativeType ), &type, #type ) \ + { \ + mTypeInfo = _MAPTYPE< nativeType >(); \ + } \ + virtual void setData(void *dptr, S32 argc, const char **argv, const EnumTable *tbl, BitSet32 flag); \ + virtual const char *getData(void *dptr, const EnumTable *tbl, BitSet32 flag ); \ + virtual const char *getTypeClassName() { return #typeName ; } \ + virtual void *getNativeVariable() { T* var = new T; return (void*)var; } \ + virtual void deleteNativeVariable(void* var) { T* nativeVar = reinterpret_cast(var); delete nativeVar; } \ + virtual const char *prepData(const char *data, char *buffer, U32 bufferLen); \ + }; \ + ConsoleType ## type gConsoleType ## type ## Instance; + +#define ConsoleSetType( type ) \ + void ConsoleType##type::setData(void *dptr, S32 argc, const char **argv, const EnumTable *tbl, BitSet32 flag) + +#define ConsoleGetType( type ) \ + const char *ConsoleType##type::getData(void *dptr, const EnumTable *tbl, BitSet32 flag) + +#define ConsoleProcessData( type ) \ + const char *ConsoleType##type::prepData(const char *data, char *buffer, U32 bufferSz) + + +#define DefineEnumType( type ) \ + DECLARE_ENUM( type ); \ + DefineConsoleType( Type ## type, type ); + +#define _ConsoleEnumType( typeName, type, nativeType ) \ + S32 type; \ + ImplementConsoleTypeCasters( type, nativeType ) \ + class EnumConsoleType ## type : public EnumConsoleBaseType \ + { \ + public: \ + EnumConsoleType ## type() \ + : EnumConsoleBaseType( sizeof( nativeType ), &type, #type ) \ + { \ + mTypeInfo = _MAPTYPE< nativeType >(); \ + } \ + virtual const char *getTypeClassName() { return #typeName; } \ + virtual void *getNativeVariable() { return new nativeType; } \ + virtual void deleteNativeVariable(void* var) { nativeType* nativeVar = reinterpret_cast< nativeType* >( var ); delete nativeVar; }\ + }; \ + EnumConsoleType ## type gConsoleType ## type ## Instance; + +#define ImplementEnumType( type, doc ) \ + _ConsoleEnumType( type, Type ## type, type ) \ + IMPLEMENT_ENUM( type, type,, doc ) + +#define EndImplementEnumType \ + END_IMPLEMENT_ENUM + + +#define DefineBitfieldType( type ) \ + DECLARE_BITFIELD( type ); \ + DefineConsoleType( Type ## type, type ); + +#define _ConsoleBitfieldType( typeName, type, nativeType ) \ + S32 type; \ + ImplementConsoleTypeCasters( type, nativeType ) \ + class BitfieldConsoleType ## type : public BitfieldConsoleBaseType \ + { \ + public: \ + BitfieldConsoleType ## type() \ + : BitfieldConsoleBaseType( sizeof( nativeType ), &type, #type ) \ + { \ + mTypeInfo = _MAPTYPE< nativeType >(); \ + } \ + virtual const char *getTypeClassName() { return #typeName; } \ + virtual void *getNativeVariable() { return new nativeType; } \ + virtual void deleteNativeVariable(void* var) { nativeType* nativeVar = reinterpret_cast< nativeType* >( var ); delete nativeVar; }\ + }; \ + BitfieldConsoleType ## type gConsoleType ## type ## Instance; + +#define ImplementBitfieldType( type, doc ) \ + _ConsoleBitfieldType( type, Type ## type, type ) \ + IMPLEMENT_BITFIELD( type, type,, doc ) + +#define EndImplementBitfieldType \ + END_IMPLEMENT_BITFIELD + +/// @} + +#endif diff --git a/Engine/source/console/engineAPI.h b/Engine/source/console/engineAPI.h new file mode 100644 index 000000000..7a3886580 --- /dev/null +++ b/Engine/source/console/engineAPI.h @@ -0,0 +1,2794 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _ENGINEAPI_H_ +#define _ENGINEAPI_H_ + +#ifndef _CONSOLETYPES_H_ +#include "console/consoleTypes.h" +#endif + +#ifndef _CONSOLE_H_ +#include "console/console.h" +#endif + +#ifndef _STRINGFUNCTIONS_H_ +#include "core/strings/stringFunctions.h" +#endif + +#ifndef _SIMOBJECT_H_ +#include "console/simObject.h" +#endif + +#ifndef _ENGINEFUNCTIONS_H_ +#include "console/engineFunctions.h" +#endif + +// Whatever types are used in API definitions, their DECLAREs must be visible to the +// macros. We include the basic primitive and struct types here. + +#ifndef _ENGINEPRIMITIVES_H_ + #include "console/enginePrimitives.h" +#endif +#ifndef _ENGINESTRUCTS_H_ + #include "console/engineStructs.h" +#endif + + +/// @file +/// Definitions for exposing engine functionality to the control layer. +/// +/// This file provides a convenience layer around the underlying engine interop system (which at +/// the moment still includes the legacy TorqueScript interop a.k.a. "console system"). The +/// macros exposed here will automatically take care of all marshalling, value type constraints, +/// reflection info instancing, etc. involved in defining engine API call-ins and call-outs. +/// +/// @note At the moment, this file supplies both the legacy TorqueScript console system as well +/// as the new engine export system with the structures and information they need. In the +/// near future, the console-based parts will get purged. This will not result in visible +/// changes to users of the functionality here except for the string-based marshalling +/// functions currently exposed (which will also disappear). + + + +//TODO: Disable warning for extern "C" functions returning UDTs for now; need to take a closer look at this +#pragma warning( disable : 4190 ) + + + +// Disable some VC warnings that are irrelevant to us. +#pragma warning( disable : 4510 ) // default constructor could not be generated; all the Args structures are never constructed by us +#pragma warning( disable : 4610 ) // can never be instantiated; again Args is never constructed by us + + +namespace engineAPI { + + /// Flag for enabling legacy console behavior in the interop system while + /// we still have it around. Will disappear along with console. + extern bool gUseConsoleInterop; + + /// Flag to allow engine functions to detect whether the engine had been + /// initialized or shut down. + extern bool gIsInitialized; +} + + +//FIXME: this allows const char* to be used as a struct field type + +// Temp support for allowing const char* to remain in the API functions as long as we +// still have the console system around. When that is purged, these definitions should +// be deleted and all const char* uses be replaced with String. +template<> struct EngineTypeTraits< const char* > : public EngineTypeTraits< String > {}; +template<> inline const EngineTypeInfo* TYPE< const char* >() { return TYPE< String >(); } + + +/// @name Marshalling +/// +/// Functions for converting to/from string-based data representations. +/// +/// @note This functionality is specific to the console interop. +/// @{ + +/// Marshal a single piece of data from native into client form. +template< typename T > +inline const char* EngineMarshallData( const T& value ) +{ + return castConsoleTypeToString( value ); +} +inline const char* EngineMarshallData( bool value ) +{ + if( value ) + return "1"; + else + return "0"; +} +inline const char* EngineMarshallData( const char* str ) +{ + // The API assumes that if you pass a plain "const char*" through it, then you are referring + // to string storage with non-local lifetime that can be safely passed to the control layer. + return str; +} +template< typename T > +inline const char* EngineMarshallData( T* object ) +{ + return ( object ? object->getIdString() : "0" ); +} +template< typename T > +inline const char* EngineMarshallData( const T* object ) +{ + return ( object ? object->getIdString() : "0" ); +} +inline const char* EngineMarshallData( U32 value ) +{ + return EngineMarshallData( S32( value ) ); +} + +/// Marshal data from native into client form stored directly in +/// client function invocation vector. +template< typename T > +inline void EngineMarshallData( const T& arg, S32& argc, const char** argv ) +{ + argv[ argc ] = Con::getStringArg( castConsoleTypeToString( arg ) ); + argc ++; +} +inline void EngineMarshallData( bool arg, S32& argc, const char** argv ) +{ + if( arg ) + argv[ argc ] = "1"; + else + argv[ argc ] = "0"; + argc ++; +} +inline void EngineMarshallData( S32 arg, S32& argc, const char** argv ) +{ + argv[ argc ] = Con::getIntArg( arg ); + argc ++; +} +inline void EngineMarshallData( U32 arg, S32& argc, const char** argv ) +{ + EngineMarshallData( S32( arg ), argc, argv ); +} +inline void EngineMarshallData( F32 arg, S32& argc, const char** argv ) +{ + argv[ argc ] = Con::getFloatArg( arg ); + argc ++; +} +inline void EngineMarshallData( const char* arg, S32& argc, const char** argv ) +{ + argv[ argc ] = arg; + argc ++; +} +template< typename T > +inline void EngineMarshallData( T* object, S32& argc, const char** argv ) +{ + argv[ argc ] = ( object ? object->getIdString() : "0" ); + argc ++; +} +template< typename T > +inline void EngineMarshallData( const T* object, S32& argc, const char** argv ) +{ + argv[ argc ] = ( object ? object->getIdString() : "0" ); + argc ++; +} + +/// Unmarshal data from client form to engine form. +/// +/// This is wrapped in an a struct as partial specializations on function +/// templates are not allowed in C++. +template< typename T > +struct EngineUnmarshallData +{ + T operator()( const char* str ) const + { + T value; + castConsoleTypeFromString( value, str ); + return value; + } +}; +template<> +struct EngineUnmarshallData< S32 > +{ + S32 operator()( const char* str ) const + { + return dAtoi( str ); + } +}; +template<> +struct EngineUnmarshallData< U32 > +{ + U32 operator()( const char* str ) const + { + return dAtoui( str ); + } +}; +template<> +struct EngineUnmarshallData< F32 > +{ + F32 operator()( const char* str ) const + { + return dAtof( str ); + } +}; +template<> +struct EngineUnmarshallData< const char* > +{ + const char* operator()( const char* str ) const + { + return str; + } +}; +template< typename T > +struct EngineUnmarshallData< T* > +{ + T* operator()( const char* str ) const + { + return dynamic_cast< T* >( Sim::findObject( str ) ); + } +}; +template<> +struct EngineUnmarshallData< void > +{ + void operator()( const char* ) const {} +}; + +/// @} + + +/// @name C to C++ Trampolines +/// +/// The trampolines serve two purposes: +/// +/// For one, they ensure that no matter what argument types are specified by users of the engine API macros, the correct +/// argument value types are enforced on the functions exported by the engine. Let's say, for example, the user writes +/// a function that takes a "Point3F direction" argument, then the template machinery here will automatically expose an +/// API function that takes a "Point3F& direction" argument. +/// +/// Secondly, the templates jump the incoming calls from extern "C" space into C++ space. This is mostly relevant for +/// methods only as they will need an implicit object type argument. +/// +/// @{ + +// Helper type to factor out commonalities between function and method trampolines. +template< typename T > +struct _EngineTrampoline +{ + struct Args {}; +}; +template< typename R, typename A > +struct _EngineTrampoline< R( A ) > +{ + struct Args + { + char data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) ]; + + typename EngineTypeTraits< A >::ValueType a() const + { + return EngineTypeTraits< A >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< A >::ArgumentValueType* >( &data[ 0 ] ) ) ); + } + }; +}; +template< typename R, typename A, typename B > +struct _EngineTrampoline< R( A, B ) > +{ + struct Args + { + char data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) ]; + + typename EngineTypeTraits< A >::ValueType a() const + { + return EngineTypeTraits< A >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< A >::ArgumentValueType* >( &data[ 0 ] ) ) ); + } + typename EngineTypeTraits< B >::ValueType b() const + { + return EngineTypeTraits< B >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< B >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) ] ) ) ); + } + }; +}; +template< typename R, typename A, typename B, typename C > +struct _EngineTrampoline< R( A, B, C ) > +{ + struct Args + { + char data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) ]; + + typename EngineTypeTraits< A >::ValueType a() const + { + return EngineTypeTraits< A >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< A >::ArgumentValueType* >( &data[ 0 ] ) ) ); + } + typename EngineTypeTraits< B >::ValueType b() const + { + return EngineTypeTraits< B >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< B >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< C >::ValueType c() const + { + return EngineTypeTraits< C >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< C >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) ] ) ) ); + } + }; +}; +template< typename R, typename A, typename B, typename C, typename D > +struct _EngineTrampoline< R( A, B, C, D ) > +{ + struct Args + { + char data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) ]; + + typename EngineTypeTraits< A >::ValueType a() const + { + return EngineTypeTraits< A >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< A >::ArgumentValueType* >( &data[ 0 ] ) ) ); + } + typename EngineTypeTraits< B >::ValueType b() const + { + return EngineTypeTraits< B >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< B >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< C >::ValueType c() const + { + return EngineTypeTraits< C >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< C >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< D >::ValueType d() const + { + return EngineTypeTraits< D >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< D >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) ] ) ) ); + } + }; +}; +template< typename R, typename A, typename B, typename C, typename D, typename E > +struct _EngineTrampoline< R( A, B, C, D, E ) > +{ + struct Args + { + char data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) ]; + + typename EngineTypeTraits< A >::ValueType a() const + { + return EngineTypeTraits< A >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< A >::ArgumentValueType* >( &data[ 0 ] ) ) ); + } + typename EngineTypeTraits< B >::ValueType b() const + { + return EngineTypeTraits< B >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< B >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< C >::ValueType c() const + { + return EngineTypeTraits< C >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< C >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< D >::ValueType d() const + { + return EngineTypeTraits< D >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< D >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< E >::ValueType e() const + { + return EngineTypeTraits< E >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< E >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) ] ) ) ); + } + }; +}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F > +struct _EngineTrampoline< R( A, B, C, D, E, F ) > +{ + struct Args + { + char data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< F >::ArgumentValueType ) ]; + + typename EngineTypeTraits< A >::ValueType a() const + { + return EngineTypeTraits< A >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< A >::ArgumentValueType* >( &data[ 0 ] ) ) ); + } + typename EngineTypeTraits< B >::ValueType b() const + { + return EngineTypeTraits< B >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< B >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< C >::ValueType c() const + { + return EngineTypeTraits< C >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< C >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< D >::ValueType d() const + { + return EngineTypeTraits< D >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< D >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< E >::ValueType e() const + { + return EngineTypeTraits< E >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< E >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< F >::ValueType f() const + { + return EngineTypeTraits< F >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< F >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) ] ) ) ); + } + }; +}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G > +struct _EngineTrampoline< R( A, B, C, D, E, F, G ) > +{ + struct Args + { + char data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< F >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< G >::ArgumentValueType ) ]; + + typename EngineTypeTraits< A >::ValueType a() const + { + return EngineTypeTraits< A >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< A >::ArgumentValueType* >( &data[ 0 ] ) ) ); + } + typename EngineTypeTraits< B >::ValueType b() const + { + return EngineTypeTraits< B >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< B >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< C >::ValueType c() const + { + return EngineTypeTraits< C >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< C >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< D >::ValueType d() const + { + return EngineTypeTraits< D >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< D >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< E >::ValueType e() const + { + return EngineTypeTraits< E >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< E >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< F >::ValueType f() const + { + return EngineTypeTraits< F >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< F >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< G >::ValueType g() const + { + return EngineTypeTraits< F >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< G >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< F >::ArgumentValueType ) ] ) ) ); + } + }; +}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H > +struct _EngineTrampoline< R( A, B, C, D, E, F, G, H ) > +{ + struct Args + { + char data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< F >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< G >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< H >::ArgumentValueType ) ]; + + typename EngineTypeTraits< A >::ValueType a() const + { + return EngineTypeTraits< A >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< A >::ArgumentValueType* >( &data[ 0 ] ) ) ); + } + typename EngineTypeTraits< B >::ValueType b() const + { + return EngineTypeTraits< B >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< B >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< C >::ValueType c() const + { + return EngineTypeTraits< C >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< C >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< D >::ValueType d() const + { + return EngineTypeTraits< D >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< D >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< E >::ValueType e() const + { + return EngineTypeTraits< E >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< E >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< F >::ValueType f() const + { + return EngineTypeTraits< F >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< F >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< G >::ValueType g() const + { + return EngineTypeTraits< G >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< G >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< F >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< H >::ValueType h() const + { + return EngineTypeTraits< H >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< H >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< F >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< G >::ArgumentValueType ) ] ) ) ); + } + }; +}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I > +struct _EngineTrampoline< R( A, B, C, D, E, F, G, H, I ) > +{ + struct Args + { + char data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< F >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< G >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< H >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< I >::ArgumentValueType ) ]; + + typename EngineTypeTraits< A >::ValueType a() const + { + return EngineTypeTraits< A >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< A >::ArgumentValueType* >( &data[ 0 ] ) ) ); + } + typename EngineTypeTraits< B >::ValueType b() const + { + return EngineTypeTraits< B >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< B >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< C >::ValueType c() const + { + return EngineTypeTraits< C >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< C >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< D >::ValueType d() const + { + return EngineTypeTraits< D >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< D >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< E >::ValueType e() const + { + return EngineTypeTraits< E >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< E >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< F >::ValueType f() const + { + return EngineTypeTraits< F >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< F >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< G >::ValueType g() const + { + return EngineTypeTraits< G >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< G >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< F >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< H >::ValueType h() const + { + return EngineTypeTraits< H >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< H >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< F >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< G >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< I >::ValueType i() const + { + return EngineTypeTraits< I >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< I >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< F >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< G >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< H >::ArgumentValueType ) ] ) ) ); + } + }; +}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J > +struct _EngineTrampoline< R( A, B, C, D, E, F, G, H, I, J ) > +{ + struct Args + { + char data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< F >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< G >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< H >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< I >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< J >::ArgumentValueType ) ]; + + typename EngineTypeTraits< A >::ValueType a() const + { + return EngineTypeTraits< A >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< A >::ArgumentValueType* >( &data[ 0 ] ) ) ); + } + typename EngineTypeTraits< B >::ValueType b() const + { + return EngineTypeTraits< B >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< B >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< C >::ValueType c() const + { + return EngineTypeTraits< C >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< C >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< D >::ValueType d() const + { + return EngineTypeTraits< D >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< D >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< E >::ValueType e() const + { + return EngineTypeTraits< E >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< E >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< F >::ValueType f() const + { + return EngineTypeTraits< F >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< F >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< G >::ValueType g() const + { + return EngineTypeTraits< G >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< G >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< F >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< H >::ValueType h() const + { + return EngineTypeTraits< H >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< H >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< F >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< G >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< I >::ValueType i() const + { + return EngineTypeTraits< I >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< I >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< F >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< G >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< H >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< J >::ValueType j() const + { + return EngineTypeTraits< J >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< J >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< F >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< G >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< H >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< I >::ArgumentValueType ) ] ) ) ); + } + }; +}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K > +struct _EngineTrampoline< R( A, B, C, D, E, F, G, H, I, J, K ) > +{ + struct Args + { + char data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< F >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< G >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< H >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< I >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< J >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< K >::ArgumentValueType ) ]; + + typename EngineTypeTraits< A >::ValueType a() const + { + return EngineTypeTraits< A >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< A >::ArgumentValueType* >( &data[ 0 ] ) ) ); + } + typename EngineTypeTraits< B >::ValueType b() const + { + return EngineTypeTraits< B >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< B >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< C >::ValueType c() const + { + return EngineTypeTraits< C >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< C >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< D >::ValueType d() const + { + return EngineTypeTraits< D >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< D >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< E >::ValueType e() const + { + return EngineTypeTraits< E >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< E >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< F >::ValueType f() const + { + return EngineTypeTraits< F >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< F >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< G >::ValueType g() const + { + return EngineTypeTraits< G >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< G >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< F >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< H >::ValueType h() const + { + return EngineTypeTraits< H >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< H >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< F >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< G >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< I >::ValueType i() const + { + return EngineTypeTraits< I >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< I >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< F >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< G >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< H >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< J >::ValueType j() const + { + return EngineTypeTraits< J >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< J >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< F >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< G >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< H >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< I >::ArgumentValueType ) ] ) ) ); + } + typename EngineTypeTraits< K >::ValueType k() const + { + return EngineTypeTraits< K >::ArgumentToValue( + *( reinterpret_cast< const typename EngineTypeTraits< K >::ArgumentValueType* > + ( &data[ sizeof( typename EngineTypeTraits< A >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< B >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< C >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< D >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< E >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< F >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< G >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< H >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< I >::ArgumentValueType ) + + sizeof( typename EngineTypeTraits< J >::ArgumentValueType ) ] ) ) ); + } + }; +}; + +template< typename T > +struct _EngineFunctionTrampolineBase : public _EngineTrampoline< T > +{ + typedef T FunctionType; +}; + +// Trampolines for any call-ins that aren't methods. +template< typename T > +struct _EngineFunctionTrampoline {}; + +template< typename R > +struct _EngineFunctionTrampoline< R() > : public _EngineFunctionTrampolineBase< R() > +{ + static R jmp( R ( *fn )(), const typename _EngineFunctionTrampolineBase< R() >::Args& args ) + { + return R( fn() ); + } +}; +template< typename R, typename A > +struct _EngineFunctionTrampoline< R( A ) > : public _EngineFunctionTrampolineBase< R( A ) > +{ + static R jmp( R ( *fn )( A ), const typename _EngineFunctionTrampolineBase< R( A ) >::Args& args ) + { + return R( fn( args.a() ) ); + } +}; +template< typename R, typename A, typename B > +struct _EngineFunctionTrampoline< R( A, B ) > : public _EngineFunctionTrampolineBase< R( A, B ) > +{ + static R jmp( R ( *fn )( A, B ), const typename _EngineFunctionTrampolineBase< R( A, B ) >::Args& args ) + { + return R( fn( args.a(), args.b() ) ); + } +}; +template< typename R, typename A, typename B, typename C > +struct _EngineFunctionTrampoline< R( A, B, C ) > : public _EngineFunctionTrampolineBase< R( A, B, C ) > +{ + static R jmp( R ( *fn )( A, B, C ), const typename _EngineFunctionTrampolineBase< R( A, B, C ) >::Args& args ) + { + return R( fn( args.a(), args.b(), args.c() ) ); + } +}; +template< typename R, typename A, typename B, typename C, typename D > +struct _EngineFunctionTrampoline< R( A, B, C, D ) > : public _EngineFunctionTrampolineBase< R( A, B, C, D ) > +{ + static R jmp( R ( *fn )( A, B, C, D ), const typename _EngineFunctionTrampolineBase< R( A, B, C, D ) >::Args& args ) + { + return R( fn( args.a(), args.b(), args.c(), args.d() ) ); + } +}; +template< typename R, typename A, typename B, typename C, typename D, typename E > +struct _EngineFunctionTrampoline< R( A, B, C, D, E ) > : public _EngineFunctionTrampolineBase< R( A, B, C, D, E ) > +{ + static R jmp( R ( *fn )( A, B, C, D, E ), const typename _EngineFunctionTrampolineBase< R( A, B, C, D, E ) >::Args& args ) + { + return R( fn( args.a(), args.b(), args.c(), args.d(), args.e() ) ); + } +}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F > +struct _EngineFunctionTrampoline< R( A, B, C, D, E, F ) > : public _EngineFunctionTrampolineBase< R( A, B, C, D, E, F ) > +{ + static R jmp( R ( *fn )( A, B, C, D, E, F ), const typename _EngineFunctionTrampolineBase< R( A, B, C, D, E, F ) >::Args& args ) + { + return R( fn( args.a(), args.b(), args.c(), args.d(), args.e(), args.f() ) ); + } +}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G > +struct _EngineFunctionTrampoline< R( A, B, C, D, E, F, G ) > : public _EngineFunctionTrampolineBase< R( A, B, C, D, E, F, G ) > +{ + static R jmp( R ( *fn )( A, B, C, D, E, F, G ), const typename _EngineFunctionTrampolineBase< R( A, B, C, D, E, F, G ) >::Args& args ) + { + return R( fn( args.a(), args.b(), args.c(), args.d(), args.e(), args.f(), args.g() ) ); + } +}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H > +struct _EngineFunctionTrampoline< R( A, B, C, D, E, F, G, H ) > : public _EngineFunctionTrampolineBase< R( A, B, C, D, E, F, G, H ) > +{ + static R jmp( R ( *fn )( A, B, C, D, E, F, G, H ), const typename _EngineFunctionTrampolineBase< R( A, B, C, D, E, F, G, H ) >::Args& args ) + { + return R( fn( args.a, args.b, args.c, args.d, args.e, args.f, args.g, args.h ) ); + } +}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I > +struct _EngineFunctionTrampoline< R( A, B, C, D, E, F, G, H, I ) > : public _EngineFunctionTrampolineBase< R( A, B, C, D, E, F, G, H, I ) > +{ + static R jmp( R ( *fn )( A, B, C, D, E, F, G, H, I ), const typename _EngineFunctionTrampolineBase< R( A, B, C, D, E, F, G, H, I ) >::Args& args ) + { + return R( fn( args.a(), args.b(), args.c(), args.d(), args.e(), args.f(), args.g(), args.h(), args.i() ) ); + } +}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J > +struct _EngineFunctionTrampoline< R( A, B, C, D, E, F, G, H, I, J ) > : public _EngineFunctionTrampolineBase< R( A, B, C, D, E, F, G, H, I, J ) > +{ + static R jmp( R ( *fn )( A, B, C, D, E, F, G, H, I, J ), const typename _EngineFunctionTrampolineBase< R( A, B, C, D, E, F, G, H, I, J ) >::Args& args ) + { + return R( fn( args.a(), args.b(), args.c(), args.d(), args.e(), args.f(), args.g(), args.h(), args.i(), args.j() ) ); + } +}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K > +struct _EngineFunctionTrampoline< R( A, B, C, D, E, F, G, H, I, J, K ) > : public _EngineFunctionTrampolineBase< R( A, B, C, D, E, F, G, H, I, J, K ) > +{ + static R jmp( R ( *fn )( A, B, C, D, E, F, G, H, I, J, K ), const typename _EngineFunctionTrampolineBase< R( A, B, C, D, E, F, G, H, I, J, K ) >::Args& args ) + { + return R( fn( args.a(), args.b(), args.c(), args.d(), args.e(), args.f(), args.g(), args.h(), args.i(), args.j(), args.k() ) ); + } +}; + +template< typename T > +struct _EngineMethodTrampolineBase : public _EngineTrampoline< T > {}; + +template< typename Frame, typename T > +struct _EngineMethodTrampoline {}; + +template< typename Frame, typename R > +struct _EngineMethodTrampoline< Frame, R() > : public _EngineMethodTrampolineBase< R() > +{ + typedef R( FunctionType )( typename Frame::ObjectType* ); + static R jmp( typename Frame::ObjectType* object, const typename _EngineFunctionTrampolineBase< R() >::Args& args ) + { + Frame f; + f.object = object; + return R( f._exec() ); + } +}; +template< typename Frame, typename R, typename A > +struct _EngineMethodTrampoline< Frame, R( A ) > : public _EngineMethodTrampolineBase< R( A ) > +{ + typedef R( FunctionType )( typename Frame::ObjectType*, A ); + static R jmp( typename Frame::ObjectType* object, const typename _EngineFunctionTrampolineBase< R( A ) >::Args& args ) + { + Frame f; + f.object = object; + return R( f._exec( args.a() ) ); + } +}; +template< typename Frame, typename R, typename A, typename B > +struct _EngineMethodTrampoline< Frame, R( A, B ) > : public _EngineMethodTrampolineBase< R( A, B ) > +{ + typedef R( FunctionType )( typename Frame::ObjectType*, A, B ); + static R jmp( typename Frame::ObjectType* object, const typename _EngineFunctionTrampolineBase< R( A, B ) >::Args& args ) + { + Frame f; + f.object = object; + return R( f._exec( args.a(), args.b() ) ); + } +}; +template< typename Frame, typename R, typename A, typename B, typename C > +struct _EngineMethodTrampoline< Frame, R( A, B, C ) > : public _EngineMethodTrampolineBase< R( A, B, C ) > +{ + typedef R( FunctionType )( typename Frame::ObjectType*, A, B, C ); + static R jmp( typename Frame::ObjectType* object, const typename _EngineFunctionTrampolineBase< R( A, B, C ) >::Args& args ) + { + Frame f; + f.object = object; + return R( f._exec( args.a(), args.b(), args.c() ) ); + } +}; +template< typename Frame, typename R, typename A, typename B, typename C, typename D > +struct _EngineMethodTrampoline< Frame, R( A, B, C, D ) > : public _EngineMethodTrampolineBase< R( A, B, C, D ) > +{ + typedef R( FunctionType )( typename Frame::ObjectType*, A, B, C, D ); + static R jmp( typename Frame::ObjectType* object, const typename _EngineFunctionTrampolineBase< R( A, B, C, D ) >::Args& args ) + { + Frame f; + f.object = object; + return R( f._exec( args.a(), args.b(), args.c(), args.d() ) ); + } +}; +template< typename Frame, typename R, typename A, typename B, typename C, typename D, typename E > +struct _EngineMethodTrampoline< Frame, R( A, B, C, D, E ) > : public _EngineMethodTrampolineBase< R( A, B, C, D, E ) > +{ + typedef R( FunctionType )( typename Frame::ObjectType*, A, B, C, D, E ); + static R jmp( typename Frame::ObjectType* object, const typename _EngineFunctionTrampolineBase< R( A, B, C, D, E ) >::Args& args ) + { + Frame f; + f.object = object; + return R( f._exec( args.a(), args.b(), args.c(), args.d(), args.e() ) ); + } +}; +template< typename Frame, typename R, typename A, typename B, typename C, typename D, typename E, typename F > +struct _EngineMethodTrampoline< Frame, R( A, B, C, D, E, F ) > : public _EngineMethodTrampolineBase< R( A, B, C, D, E, F ) > +{ + typedef R( FunctionType )( typename Frame::ObjectType*, A, B, C, D, E, F ); + static R jmp( typename Frame::ObjectType* object, const typename _EngineFunctionTrampolineBase< R( A, B, C, D, E, F ) >::Args& args ) + { + Frame f; + f.object = object; + return R( f._exec( args.a(), args.b(), args.c(), args.d(), args.e(), args.f() ) ); + } +}; +template< typename Frame, typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G > +struct _EngineMethodTrampoline< Frame, R( A, B, C, D, E, F, G ) > : public _EngineMethodTrampolineBase< R( A, B, C, D, E, F, G ) > +{ + typedef R( FunctionType )( typename Frame::ObjectType*, A, B, C, D, E, F, G ); + static R jmp( typename Frame::ObjectType* object, const typename _EngineFunctionTrampolineBase< R( A, B, C, D, E, F, G ) >::Args& args ) + { + Frame f; + f.object = object; + return R( f._exec( args.a(), args.b(), args.c(), args.d(), args.e(), args.f(), args.g() ) ); + } +}; +template< typename Frame, typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H > +struct _EngineMethodTrampoline< Frame, R( A, B, C, D, E, F, G, H ) > : public _EngineMethodTrampolineBase< R( A, B, C, D, E, F, G, H ) > +{ + typedef R( FunctionType )( typename Frame::ObjectType*, A, B, C, D, E, F, G, H ); + static R jmp( typename Frame::ObjectType* object, const typename _EngineFunctionTrampolineBase< R( A, B, C, D, E, F, G, H ) >::Args& args ) + { + Frame f; + f.object = object; + return R( f._exec( args.a(), args.b(), args.c(), args.d(), args.e(), args.f(), args.g(), args.h() ) ); + } +}; +template< typename Frame, typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I > +struct _EngineMethodTrampoline< Frame, R( A, B, C, D, E, F, G, H, I ) > : public _EngineMethodTrampolineBase< R( A, B, C, D, E, F, G, H, I ) > +{ + typedef R( FunctionType )( typename Frame::ObjectType*, A, B, C, D, E, F, G, H, I ); + static R jmp( typename Frame::ObjectType* object, const typename _EngineFunctionTrampolineBase< R( A, B, C, D, E, F, G, H, I ) >::Args& args ) + { + Frame f; + f.object = object; + return R( f._exec( args.a(), args.b(), args.c(), args.d(), args.e(), args.f(), args.g(), args.h(), args.i() ) ); + } +}; +template< typename Frame, typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J > +struct _EngineMethodTrampoline< Frame, R( A, B, C, D, E, F, G, H, I, J ) > : public _EngineMethodTrampolineBase< R( A, B, C, D, E, F, G, H, I, J ) > +{ + typedef R( FunctionType )( typename Frame::ObjectType*, A, B, C, D, E, F, G, H, I, J ); + static R jmp( typename Frame::ObjectType* object, const typename _EngineFunctionTrampolineBase< R( A, B, C, D, E, F, G, H, I, J ) >::Args& args ) + { + Frame f; + f.object = object; + return R( f._exec( args.a(), args.b(), args.c(), args.d(), args.e(), args.f(), args.g(), args.h(), args.i(), args.j() ) ); + } +}; +template< typename Frame, typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K > +struct _EngineMethodTrampoline< Frame, R( A, B, C, D, E, F, G, H, I, J, K ) > : public _EngineMethodTrampolineBase< R( A, B, C, D, E, F, G, H, I, J, K ) > +{ + typedef R( FunctionType )( typename Frame::ObjectType*, A, B, C, D, E, F, G, H, I, J, K ); + static R jmp( typename Frame::ObjectType* object, const typename _EngineFunctionTrampolineBase< R( A, B, C, D, E, F, G, H, I, J, K ) >::Args& args ) + { + Frame f; + f.object = object; + return R( f._exec( args.a(), args.b(), args.c(), args.d(), args.e(), args.f(), args.g(), args.h(), args.i(), args.j(), args.k() ) ); + } +}; + +/// @} + + +/// @name Thunking +/// +/// Internal functionality for thunks placed between TorqueScript calls of engine functions and their native +/// implementations. +/// +/// @note The functionality in this group is specific to the console interop system. +/// @{ + + +// Helper function to return data from a thunk. +template< typename T > +inline const char* _EngineConsoleThunkReturnValue( const T& value ) +{ + return EngineMarshallData( value ); +} +inline bool _EngineConsoleThunkReturnValue( bool value ) +{ + return value; +} +inline S32 _EngineConsoleThunkReturnValue( S32 value ) +{ + return value; +} +inline F32 _EngineConsoleThunkReturnValue( F32 value ) +{ + return value; +} +inline const char* _EngineConsoleThunkReturnValue( const String& str ) +{ + return Con::getReturnBuffer( str ); +} +inline const char* _EngineConsoleThunkReturnValue( const char* value ) +{ + return EngineMarshallData( value ); +} +template< typename T > +inline const char* _EngineConsoleThunkReturnValue( T* value ) +{ + return ( value ? value->getIdString() : "" ); +} +template< typename T > +inline const char* _EngineConsoleThunkReturnValue( const T* value ) +{ + return ( value ? value->getIdString() : "" ); +} + + + +// Helper class to determine the type of callback registered with the console system. +template< typename R > +struct _EngineConsoleThunkType +{ + typedef const char* ReturnType; + typedef StringCallback CallbackType; +}; +template<> +struct _EngineConsoleThunkType< S32 > +{ + typedef S32 ReturnType; + typedef IntCallback CallbackType; +}; +template<> +struct _EngineConsoleThunkType< U32 > +{ + typedef U32 ReturnType; + typedef IntCallback CallbackType; +}; +template<> +struct _EngineConsoleThunkType< F32 > +{ + typedef F32 ReturnType; + typedef FloatCallback CallbackType; +}; +template<> +struct _EngineConsoleThunkType< bool > +{ + typedef bool ReturnType; + typedef BoolCallback CallbackType; +}; +template<> +struct _EngineConsoleThunkType< void > +{ + typedef void ReturnType; + typedef VoidCallback CallbackType; +}; + + +// Helper struct to count the number of parameters in a function list. +// The setup through operator () allows omitting the the argument list entirely. +struct _EngineConsoleThunkCountArgs +{ + template< typename A > + U32 operator ()( A a ) + { + return 1; + } + template< typename A, typename B > + U32 operator ()( A a, B b ) + { + return 2; + } + template< typename A, typename B, typename C > + U32 operator ()( A a, B b, C c ) + { + return 3; + } + template< typename A, typename B, typename C, typename D > + U32 operator ()( A a, B b, C c, D d ) + { + return 4; + } + template< typename A, typename B, typename C, typename D, typename E > + U32 operator ()( A a, B b, C c, D d, E e ) + { + return 5; + } + template< typename A, typename B, typename C, typename D, typename E, typename F > + U32 operator ()( A a, B b, C c, D d, E e, F f ) + { + return 6; + } + template< typename A, typename B, typename C, typename D, typename E, typename F, typename G > + U32 operator ()( A a, B b, C c, D d, E e, F f, G g ) + { + return 7; + } + template< typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H > + U32 operator ()( A a, B b, C c, D d, E e, F f, G g, H h ) + { + return 8; + } + template< typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I > + U32 operator ()( A a, B b, C c, D d, E e, F f, G g, H h, I i ) + { + return 9; + } + template< typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J > + U32 operator ()( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j ) + { + return 10; + } + template< typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K > + U32 operator ()( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k ) + { + return 11; + } + + operator U32() const + { + return 0; + } +}; + + +// Encapsulation of a legacy console function invocation. + +template< int startArgc, typename T > +struct _EngineConsoleThunk {}; + +template< int startArgc, typename R > +struct _EngineConsoleThunk< startArgc, R() > +{ + typedef typename _EngineConsoleThunkType< R >::ReturnType ReturnType; + static const int NUM_ARGS = 0; + static ReturnType thunk( S32 argc, const char** argv, R ( *fn )(), const _EngineFunctionDefaultArguments< void() >& ) + { + return _EngineConsoleThunkReturnValue( fn() ); + } + template< typename Frame > + static ReturnType thunk( S32 argc, const char** argv, R ( Frame::*fn )() const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType* ) >& ) + { + return _EngineConsoleThunkReturnValue( ( frame->*fn )() ); + } +}; +template< int startArgc > +struct _EngineConsoleThunk< startArgc, void() > +{ + typedef void ReturnType; + static const int NUM_ARGS = 0; + static void thunk( S32 argc, const char** argv, void ( *fn )(), const _EngineFunctionDefaultArguments< void() >& ) + { + fn(); + } + template< typename Frame > + static void thunk( S32 argc, const char** argv, void ( Frame::*fn )() const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType* ) >& ) + { + return ( frame->*fn )(); + } +}; + +template< int startArgc, typename R, typename A > +struct _EngineConsoleThunk< startArgc, R( A ) > +{ + typedef typename _EngineConsoleThunkType< R >::ReturnType ReturnType; + static const int NUM_ARGS = 1 + startArgc; + static ReturnType thunk( S32 argc, const char** argv, R ( *fn )( A ), const _EngineFunctionDefaultArguments< void( A ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); + return _EngineConsoleThunkReturnValue( fn( a ) ); + } + template< typename Frame > + static ReturnType thunk( S32 argc, const char** argv, R ( Frame::*fn )( A ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); + return _EngineConsoleThunkReturnValue( ( frame->*fn )( a ) ); + } +}; +template< int startArgc, typename A > +struct _EngineConsoleThunk< startArgc, void( A ) > +{ + typedef void ReturnType; + static const int NUM_ARGS = 1 + startArgc; + static void thunk( S32 argc, const char** argv, void ( *fn )( A ), const _EngineFunctionDefaultArguments< void( A ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); + fn( a ); + } + template< typename Frame > + static void thunk( S32 argc, const char** argv, void ( Frame::*fn )( A ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); + ( frame->*fn )( a ); + } +}; + +template< int startArgc, typename R, typename A, typename B > +struct _EngineConsoleThunk< startArgc, R( A, B ) > +{ + typedef typename _EngineConsoleThunkType< R >::ReturnType ReturnType; + static const int NUM_ARGS = 2 + startArgc; + static ReturnType thunk( S32 argc, const char** argv, R ( *fn )( A, B ), const _EngineFunctionDefaultArguments< void( A, B ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); + return _EngineConsoleThunkReturnValue( fn( a, b ) ); + } + template< typename Frame > + static ReturnType thunk( S32 argc, const char** argv, R ( Frame::*fn )( A, B ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); + return _EngineConsoleThunkReturnValue( ( frame->*fn )( a, b ) ); + } +}; +template< int startArgc, typename A, typename B > +struct _EngineConsoleThunk< startArgc, void( A, B ) > +{ + typedef void ReturnType; + static const int NUM_ARGS = 2 + startArgc; + static void thunk( S32 argc, const char** argv, void ( *fn )( A, B ), const _EngineFunctionDefaultArguments< void( A, B ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); + fn( a, b ); + } + template< typename Frame > + static void thunk( S32 argc, const char** argv, void ( Frame::*fn )( A, B ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); + ( frame->*fn )( a, b ); + } +}; + +template< int startArgc, typename R, typename A, typename B, typename C > +struct _EngineConsoleThunk< startArgc, R( A, B, C ) > +{ + typedef typename _EngineConsoleThunkType< R >::ReturnType ReturnType; + static const int NUM_ARGS = 3 + startArgc; + static ReturnType thunk( S32 argc, const char** argv, R ( *fn )( A, B, C ), const _EngineFunctionDefaultArguments< void( A, B, C ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.c ) ); + return _EngineConsoleThunkReturnValue( fn( a, b, c ) ); + } + template< typename Frame > + static ReturnType thunk( S32 argc, const char** argv, R ( Frame::*fn )( A, B, C ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.d ) ); + return _EngineConsoleThunkReturnValue( ( frame->*fn )( a, b, c ) ); + } +}; +template< int startArgc, typename A, typename B, typename C > +struct _EngineConsoleThunk< startArgc, void( A, B, C ) > +{ + typedef void ReturnType; + static const int NUM_ARGS = 3 + startArgc; + static void thunk( S32 argc, const char** argv, void ( *fn )( A, B, C ), const _EngineFunctionDefaultArguments< void( A, B, C ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.c ) ); + fn( a, b, c ); + } + template< typename Frame > + static void thunk( S32 argc, const char** argv, void ( Frame::*fn )( A, B, C ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.d ) ); + ( frame->*fn )( a, b, c ); + } +}; + +template< int startArgc, typename R, typename A, typename B, typename C, typename D > +struct _EngineConsoleThunk< startArgc, R( A, B, C, D ) > +{ + typedef typename _EngineConsoleThunkType< R >::ReturnType ReturnType; + static const int NUM_ARGS = 4 + startArgc; + static ReturnType thunk( S32 argc, const char** argv, R ( *fn )( A, B, C, D ), const _EngineFunctionDefaultArguments< void( A, B, C, D ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.c ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.d ) ); + return _EngineConsoleThunkReturnValue( fn( a, b, c, d ) ); + } + template< typename Frame > + static ReturnType thunk( S32 argc, const char** argv, R ( Frame::*fn )( A, B, C, D ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.d ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.e ) ); + return _EngineConsoleThunkReturnValue( ( frame->*fn )( a, b, c, d ) ); + } +}; +template< int startArgc, typename A, typename B, typename C, typename D > +struct _EngineConsoleThunk< startArgc, void( A, B, C, D ) > +{ + typedef void ReturnType; + static const int NUM_ARGS = 4 + startArgc; + static void thunk( S32 argc, const char** argv, void ( *fn )( A, B, C, D ), const _EngineFunctionDefaultArguments< void( A, B, C, D ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.c ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.d ) ); + fn( a, b, c, d ); + } + template< typename Frame > + static void thunk( S32 argc, const char** argv, void ( Frame::*fn )( A, B, C, D ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.d ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.e ) ); + ( frame->*fn )( a, b, c, d ); + } +}; + +template< int startArgc, typename R, typename A, typename B, typename C, typename D, typename E > +struct _EngineConsoleThunk< startArgc, R( A, B, C, D, E ) > +{ + typedef typename _EngineConsoleThunkType< R >::ReturnType ReturnType; + static const int NUM_ARGS = 5 + startArgc; + static ReturnType thunk( S32 argc, const char** argv, R ( *fn )( A, B, C, D, E ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.c ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.d ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.e ) ); + return _EngineConsoleThunkReturnValue( fn( a, b, c, d, e ) ); + } + template< typename Frame > + static ReturnType thunk( S32 argc, const char** argv, R ( Frame::*fn )( A, B, C, D, E ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.d ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.e ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.f ) ); + return _EngineConsoleThunkReturnValue( ( frame->*fn )( a, b, c, d, e ) ); + } +}; +template< int startArgc, typename A, typename B, typename C, typename D, typename E > +struct _EngineConsoleThunk< startArgc, void( A, B, C, D, E ) > +{ + typedef void ReturnType; + static const int NUM_ARGS = 5 + startArgc; + static void thunk( S32 argc, const char** argv, void ( *fn )( A, B, C, D, E ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.c ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.d ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.e ) ); + fn( a, b, c, d, e ); + } + template< typename Frame > + static void thunk( S32 argc, const char** argv, void ( Frame::*fn )( A, B, C, D, E ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.d ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.e ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.f ) ); + ( frame->*fn )( a, b, c, d, e ); + } +}; + +template< int startArgc, typename R, typename A, typename B, typename C, typename D, typename E, typename F > +struct _EngineConsoleThunk< startArgc, R( A, B, C, D, E, F ) > +{ + typedef typename _EngineConsoleThunkType< R >::ReturnType ReturnType; + static const int NUM_ARGS = 6 + startArgc; + static ReturnType thunk( S32 argc, const char** argv, R ( *fn )( A, B, C, D, E, F ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.c ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.d ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.e ) ); + F f = ( startArgc + 5 < argc ? EngineUnmarshallData< F >()( argv[ startArgc + 5 ] ) : F( defaultArgs.f ) ); + return _EngineConsoleThunkReturnValue( fn( a, b, c, d, e, f ) ); + } + template< typename Frame > + static ReturnType thunk( S32 argc, const char** argv, R ( Frame::*fn )( A, B, C, D, E, F ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.d ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.e ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.f ) ); + F f = ( startArgc + 5 < argc ? EngineUnmarshallData< F >()( argv[ startArgc + 5 ] ) : F( defaultArgs.g ) ); + return _EngineConsoleThunkReturnValue( ( frame->*fn )( a, b, c, d, e, f ) ); + } +}; +template< int startArgc, typename A, typename B, typename C, typename D, typename E, typename F > +struct _EngineConsoleThunk< startArgc, void( A, B, C, D, E, F ) > +{ + typedef void ReturnType; + static const int NUM_ARGS = 6 + startArgc; + static void thunk( S32 argc, const char** argv, void ( *fn )( A, B, C, D, E, F ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.c ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.d ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.e ) ); + F f = ( startArgc + 5 < argc ? EngineUnmarshallData< F >()( argv[ startArgc + 5 ] ) : F( defaultArgs.f ) ); + fn( a, b, c, d, e, f ); + } + template< typename Frame > + static void thunk( S32 argc, const char** argv, void ( Frame::*fn )( A, B, C, D, E, F ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.d ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.e ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.f ) ); + F f = ( startArgc + 5 < argc ? EngineUnmarshallData< F >()( argv[ startArgc + 5 ] ) : F( defaultArgs.g ) ); + ( frame->*fn )( a, b, c, d, e, f ); + } +}; + +template< int startArgc, typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G > +struct _EngineConsoleThunk< startArgc, R( A, B, C, D, E, F, G ) > +{ + typedef typename _EngineConsoleThunkType< R >::ReturnType ReturnType; + static const int NUM_ARGS = 7 + startArgc; + static ReturnType thunk( S32 argc, const char** argv, R ( *fn )( A, B, C, D, E, F, G ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.c ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.d ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.e ) ); + F f = ( startArgc + 5 < argc ? EngineUnmarshallData< F >()( argv[ startArgc + 5 ] ) : F( defaultArgs.f ) ); + G g = ( startArgc + 6 < argc ? EngineUnmarshallData< G >()( argv[ startArgc + 6 ] ) : G( defaultArgs.g ) ); + return _EngineConsoleThunkReturnValue( fn( a, b, c, d, e, f, g ) ); + } + template< typename Frame > + static ReturnType thunk( S32 argc, const char** argv, R ( Frame::*fn )( A, B, C, D, E, F, G ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.d ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.e ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.f ) ); + F f = ( startArgc + 5 < argc ? EngineUnmarshallData< F >()( argv[ startArgc + 5 ] ) : F( defaultArgs.g ) ); + G g = ( startArgc + 6 < argc ? EngineUnmarshallData< G >()( argv[ startArgc + 6 ] ) : G( defaultArgs.h ) ); + return _EngineConsoleThunkReturnValue( ( frame->*fn )( a, b, c, d, e, f, g ) ); + } +}; +template< int startArgc, typename A, typename B, typename C, typename D, typename E, typename F, typename G > +struct _EngineConsoleThunk< startArgc, void( A, B, C, D, E, F, G ) > +{ + typedef void ReturnType; + static const int NUM_ARGS = 7 + startArgc; + static void thunk( S32 argc, const char** argv, void ( *fn )( A, B, C, D, E, F, G ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.c ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.d ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.e ) ); + F f = ( startArgc + 5 < argc ? EngineUnmarshallData< F >()( argv[ startArgc + 5 ] ) : F( defaultArgs.f ) ); + G g = ( startArgc + 6 < argc ? EngineUnmarshallData< G >()( argv[ startArgc + 6 ] ) : G( defaultArgs.g ) ); + fn( a, b, c, d, e, f, g ); + } + template< typename Frame > + static void thunk( S32 argc, const char** argv, void ( Frame::*fn )( A, B, C, D, E, F, G ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.d ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.e ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.f ) ); + F f = ( startArgc + 5 < argc ? EngineUnmarshallData< F >()( argv[ startArgc + 5 ] ) : F( defaultArgs.g ) ); + G g = ( startArgc + 6 < argc ? EngineUnmarshallData< G >()( argv[ startArgc + 6 ] ) : G( defaultArgs.h ) ); + ( frame->*fn )( a, b, c, d, e, f, g ); + } +}; + +template< int startArgc, typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H > +struct _EngineConsoleThunk< startArgc, R( A, B, C, D, E, F, G, H ) > +{ + typedef typename _EngineConsoleThunkType< R >::ReturnType ReturnType; + static const int NUM_ARGS = 8 + startArgc; + static ReturnType thunk( S32 argc, const char** argv, R ( *fn )( A, B, C, D, E, F, G, H ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.c ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.d ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.e ) ); + F f = ( startArgc + 5 < argc ? EngineUnmarshallData< F >()( argv[ startArgc + 5 ] ) : F( defaultArgs.f ) ); + G g = ( startArgc + 6 < argc ? EngineUnmarshallData< G >()( argv[ startArgc + 6 ] ) : G( defaultArgs.g ) ); + H h = ( startArgc + 7 < argc ? EngineUnmarshallData< H >()( argv[ startArgc + 7 ] ) : H( defaultArgs.h ) ); + return _EngineConsoleThunkReturnValue( fn( a, b, c, d, e, f, g, h ) ); + } + template< typename Frame > + static ReturnType thunk( S32 argc, const char** argv, R ( Frame::*fn )( A, B, C, D, E, F, G, H ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G, H ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.d ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.e ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.f ) ); + F f = ( startArgc + 5 < argc ? EngineUnmarshallData< F >()( argv[ startArgc + 5 ] ) : F( defaultArgs.g ) ); + G g = ( startArgc + 6 < argc ? EngineUnmarshallData< G >()( argv[ startArgc + 6 ] ) : G( defaultArgs.h ) ); + H h = ( startArgc + 7 < argc ? EngineUnmarshallData< H >()( argv[ startArgc + 7 ] ) : H( defaultArgs.i ) ); + return _EngineConsoleThunkReturnValue( ( frame->*fn )( a, b, c, d, e, f, g, h ) ); + } +}; +template< int startArgc, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H > +struct _EngineConsoleThunk< startArgc, void( A, B, C, D, E, F, G, H ) > +{ + typedef void ReturnType; + static const int NUM_ARGS = 8 + startArgc; + static void thunk( S32 argc, const char** argv, void ( *fn )( A, B, C, D, E, F, G, H ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.c ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.d ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.e ) ); + F f = ( startArgc + 5 < argc ? EngineUnmarshallData< F >()( argv[ startArgc + 5 ] ) : F( defaultArgs.f ) ); + G g = ( startArgc + 6 < argc ? EngineUnmarshallData< G >()( argv[ startArgc + 6 ] ) : G( defaultArgs.g ) ); + H h = ( startArgc + 7 < argc ? EngineUnmarshallData< H >()( argv[ startArgc + 7 ] ) : H( defaultArgs.h ) ); + fn( a, b, c, d, e, f, g, h ); + } + template< typename Frame > + static void thunk( S32 argc, const char** argv, void ( Frame::*fn )( A, B, C, D, E, F, G, H ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G, H ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.d ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.e ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.f ) ); + F f = ( startArgc + 5 < argc ? EngineUnmarshallData< F >()( argv[ startArgc + 5 ] ) : F( defaultArgs.g ) ); + G g = ( startArgc + 6 < argc ? EngineUnmarshallData< G >()( argv[ startArgc + 6 ] ) : G( defaultArgs.h ) ); + H h = ( startArgc + 7 < argc ? EngineUnmarshallData< H >()( argv[ startArgc + 7 ] ) : H( defaultArgs.i ) ); + ( frame->*fn )( a, b, c, d, e, f, g, h ); + } +}; + +template< int startArgc, typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I > +struct _EngineConsoleThunk< startArgc, R( A, B, C, D, E, F, G, H, I ) > +{ + typedef typename _EngineConsoleThunkType< R >::ReturnType ReturnType; + static const int NUM_ARGS = 9 + startArgc; + static ReturnType thunk( S32 argc, const char** argv, R ( *fn )( A, B, C, D, E, F, G, H, I ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H, I ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.c ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.d ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.e ) ); + F f = ( startArgc + 5 < argc ? EngineUnmarshallData< F >()( argv[ startArgc + 5 ] ) : F( defaultArgs.f ) ); + G g = ( startArgc + 6 < argc ? EngineUnmarshallData< G >()( argv[ startArgc + 6 ] ) : G( defaultArgs.g ) ); + H h = ( startArgc + 7 < argc ? EngineUnmarshallData< H >()( argv[ startArgc + 7 ] ) : H( defaultArgs.h ) ); + I i = ( startArgc + 8 < argc ? EngineUnmarshallData< I >()( argv[ startArgc + 8 ] ) : I( defaultArgs.i ) ); + return _EngineConsoleThunkReturnValue( fn( a, b, c, d, e, f, g, h, i ) ); + } + template< typename Frame > + static ReturnType thunk( S32 argc, const char** argv, R ( Frame::*fn )( A, B, C, D, E, F, G, H, I ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G, H, I ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.d ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.e ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.f ) ); + F f = ( startArgc + 5 < argc ? EngineUnmarshallData< F >()( argv[ startArgc + 5 ] ) : F( defaultArgs.g ) ); + G g = ( startArgc + 6 < argc ? EngineUnmarshallData< G >()( argv[ startArgc + 6 ] ) : G( defaultArgs.h ) ); + H h = ( startArgc + 7 < argc ? EngineUnmarshallData< H >()( argv[ startArgc + 7 ] ) : H( defaultArgs.i ) ); + I i = ( startArgc + 8 < argc ? EngineUnmarshallData< I >()( argv[ startArgc + 8 ] ) : I( defaultArgs.j ) ); + return _EngineConsoleThunkReturnValue( ( frame->*fn )( a, b, c, d, e, f, g, h, i ) ); + } +}; +template< int startArgc, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I > +struct _EngineConsoleThunk< startArgc, void( A, B, C, D, E, F, G, H, I ) > +{ + typedef void ReturnType; + static const int NUM_ARGS = 9 + startArgc; + static void thunk( S32 argc, const char** argv, void ( *fn )( A, B, C, D, E, F, G, H, I ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H, I ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.c ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.d ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.e ) ); + F f = ( startArgc + 5 < argc ? EngineUnmarshallData< F >()( argv[ startArgc + 5 ] ) : F( defaultArgs.f ) ); + G g = ( startArgc + 6 < argc ? EngineUnmarshallData< G >()( argv[ startArgc + 6 ] ) : G( defaultArgs.g ) ); + H h = ( startArgc + 7 < argc ? EngineUnmarshallData< H >()( argv[ startArgc + 7 ] ) : H( defaultArgs.h ) ); + I i = ( startArgc + 8 < argc ? EngineUnmarshallData< I >()( argv[ startArgc + 8 ] ) : I( defaultArgs.i ) ); + fn( a, b, c, d, e, f, g, h, i ); + } + template< typename Frame > + static void thunk( S32 argc, const char** argv, void ( Frame::*fn )( A, B, C, D, E, F, G, H, I ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G, H, I ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.d ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.e ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.f ) ); + F f = ( startArgc + 5 < argc ? EngineUnmarshallData< F >()( argv[ startArgc + 5 ] ) : F( defaultArgs.g ) ); + G g = ( startArgc + 6 < argc ? EngineUnmarshallData< G >()( argv[ startArgc + 6 ] ) : G( defaultArgs.h ) ); + H h = ( startArgc + 7 < argc ? EngineUnmarshallData< H >()( argv[ startArgc + 7 ] ) : H( defaultArgs.i ) ); + I i = ( startArgc + 8 < argc ? EngineUnmarshallData< I >()( argv[ startArgc + 8 ] ) : I( defaultArgs.j ) ); + ( frame->*fn )( a, b, c, d, e, f, g, h, i ); + } +}; + +template< int startArgc, typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J > +struct _EngineConsoleThunk< startArgc, R( A, B, C, D, E, F, G, H, I, J ) > +{ + typedef typename _EngineConsoleThunkType< R >::ReturnType ReturnType; + static const int NUM_ARGS = 10 + startArgc; + static ReturnType thunk( S32 argc, const char** argv, R ( *fn )( A, B, C, D, E, F, G, H, I, J ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H, I, J ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.c ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.d ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.e ) ); + F f = ( startArgc + 5 < argc ? EngineUnmarshallData< F >()( argv[ startArgc + 5 ] ) : F( defaultArgs.f ) ); + G g = ( startArgc + 6 < argc ? EngineUnmarshallData< G >()( argv[ startArgc + 6 ] ) : G( defaultArgs.g ) ); + H h = ( startArgc + 7 < argc ? EngineUnmarshallData< H >()( argv[ startArgc + 7 ] ) : H( defaultArgs.h ) ); + I i = ( startArgc + 8 < argc ? EngineUnmarshallData< I >()( argv[ startArgc + 8 ] ) : I( defaultArgs.i ) ); + J j = ( startArgc + 9 < argc ? EngineUnmarshallData< J >()( argv[ startArgc + 9 ] ) : J( defaultArgs.j ) ); + return _EngineConsoleThunkReturnValue( fn( a, b, c, d, e, f, g, h, i, j ) ); + } + template< typename Frame > + static ReturnType thunk( S32 argc, const char** argv, R ( Frame::*fn )( A, B, C, D, E, F, G, H, I, J ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G, H, I, J ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.d ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.e ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.f ) ); + F f = ( startArgc + 5 < argc ? EngineUnmarshallData< F >()( argv[ startArgc + 5 ] ) : F( defaultArgs.g ) ); + G g = ( startArgc + 6 < argc ? EngineUnmarshallData< G >()( argv[ startArgc + 6 ] ) : G( defaultArgs.h ) ); + H h = ( startArgc + 7 < argc ? EngineUnmarshallData< H >()( argv[ startArgc + 7 ] ) : H( defaultArgs.i ) ); + I i = ( startArgc + 8 < argc ? EngineUnmarshallData< I >()( argv[ startArgc + 8 ] ) : I( defaultArgs.j ) ); + J j = ( startArgc + 9 < argc ? EngineUnmarshallData< J >()( argv[ startArgc + 9 ] ) : J( defaultArgs.k ) ); + return _EngineConsoleThunkReturnValue( ( frame->*fn )( a, b, c, d, e, f, g, h, i, j ) ); + } +}; +template< int startArgc, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J > +struct _EngineConsoleThunk< startArgc, void( A, B, C, D, E, F, G, H, I, J ) > +{ + typedef void ReturnType; + static const int NUM_ARGS = 10 + startArgc; + static void thunk( S32 argc, const char** argv, void ( *fn )( A, B, C, D, E, F, G, H, I, J ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H, I, J ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.c ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.d ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.e ) ); + F f = ( startArgc + 5 < argc ? EngineUnmarshallData< F >()( argv[ startArgc + 5 ] ) : F( defaultArgs.f ) ); + G g = ( startArgc + 6 < argc ? EngineUnmarshallData< G >()( argv[ startArgc + 6 ] ) : G( defaultArgs.g ) ); + H h = ( startArgc + 7 < argc ? EngineUnmarshallData< H >()( argv[ startArgc + 7 ] ) : H( defaultArgs.h ) ); + I i = ( startArgc + 8 < argc ? EngineUnmarshallData< I >()( argv[ startArgc + 8 ] ) : I( defaultArgs.i ) ); + J j = ( startArgc + 9 < argc ? EngineUnmarshallData< J >()( argv[ startArgc + 9 ] ) : J( defaultArgs.j ) ); + fn( a, b, c, d, e, f, g, h, i, j ); + } + template< typename Frame > + static void thunk( S32 argc, const char** argv, void ( Frame::*fn )( A, B, C, D, E, F, G, H, I, J ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G, H, I, J ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.d ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.e ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.f ) ); + F f = ( startArgc + 5 < argc ? EngineUnmarshallData< F >()( argv[ startArgc + 5 ] ) : F( defaultArgs.g ) ); + G g = ( startArgc + 6 < argc ? EngineUnmarshallData< G >()( argv[ startArgc + 6 ] ) : G( defaultArgs.h ) ); + H h = ( startArgc + 7 < argc ? EngineUnmarshallData< H >()( argv[ startArgc + 7 ] ) : H( defaultArgs.i ) ); + I i = ( startArgc + 8 < argc ? EngineUnmarshallData< I >()( argv[ startArgc + 8 ] ) : I( defaultArgs.j ) ); + J j = ( startArgc + 9 < argc ? EngineUnmarshallData< J >()( argv[ startArgc + 9 ] ) : J( defaultArgs.k ) ); + ( frame->*fn )( a, b, c, d, e, f, g, h, i, j ); + } +}; +template< int startArgc, typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K > +struct _EngineConsoleThunk< startArgc, R( A, B, C, D, E, F, G, H, I, J, K ) > +{ + typedef typename _EngineConsoleThunkType< R >::ReturnType ReturnType; + static const int NUM_ARGS = 11 + startArgc; + static ReturnType thunk( S32 argc, const char** argv, R ( *fn )( A, B, C, D, E, F, G, H, I, J, K ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H, I, J, K ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.c ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.d ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.e ) ); + F f = ( startArgc + 5 < argc ? EngineUnmarshallData< F >()( argv[ startArgc + 5 ] ) : F( defaultArgs.f ) ); + G g = ( startArgc + 6 < argc ? EngineUnmarshallData< G >()( argv[ startArgc + 6 ] ) : G( defaultArgs.g ) ); + H h = ( startArgc + 7 < argc ? EngineUnmarshallData< H >()( argv[ startArgc + 7 ] ) : H( defaultArgs.h ) ); + I i = ( startArgc + 8 < argc ? EngineUnmarshallData< I >()( argv[ startArgc + 8 ] ) : I( defaultArgs.i ) ); + J j = ( startArgc + 9 < argc ? EngineUnmarshallData< J >()( argv[ startArgc + 9 ] ) : J( defaultArgs.j ) ); + K k = ( startArgc + 10 < argc ? EngineUnmarshallData< K >()( argv[ startArgc + 10 ] ) : K( defaultArgs.k ) ); + return _EngineConsoleThunkReturnValue( fn( a, b, c, d, e, f, g, h, i, j, k ) ); + } + template< typename Frame > + static ReturnType thunk( S32 argc, const char** argv, R ( Frame::*fn )( A, B, C, D, E, F, G, H, I, J, K ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G, H, I, J, K ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.d ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.e ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.f ) ); + F f = ( startArgc + 5 < argc ? EngineUnmarshallData< F >()( argv[ startArgc + 5 ] ) : F( defaultArgs.g ) ); + G g = ( startArgc + 6 < argc ? EngineUnmarshallData< G >()( argv[ startArgc + 6 ] ) : G( defaultArgs.h ) ); + H h = ( startArgc + 7 < argc ? EngineUnmarshallData< H >()( argv[ startArgc + 7 ] ) : H( defaultArgs.i ) ); + I i = ( startArgc + 8 < argc ? EngineUnmarshallData< I >()( argv[ startArgc + 8 ] ) : I( defaultArgs.j ) ); + J j = ( startArgc + 9 < argc ? EngineUnmarshallData< J >()( argv[ startArgc + 9 ] ) : J( defaultArgs.k ) ); + K k = ( startArgc + 10 < argc ? EngineUnmarshallData< K >()( argv[ startArgc + 10 ] ) : K( defaultArgs.l ) ); + return _EngineConsoleThunkReturnValue( ( frame->*fn )( a, b, c, d, e, f, g, h, i, j, k ) ); + } +}; +template< int startArgc, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K > +struct _EngineConsoleThunk< startArgc, void( A, B, C, D, E, F, G, H, I, J, K ) > +{ + typedef void ReturnType; + static const int NUM_ARGS = 11 + startArgc; + static void thunk( S32 argc, const char** argv, void ( *fn )( A, B, C, D, E, F, G, H, I, J, K ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H, I, J, K ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.c ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.d ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.e ) ); + F f = ( startArgc + 5 < argc ? EngineUnmarshallData< F >()( argv[ startArgc + 5 ] ) : F( defaultArgs.f ) ); + G g = ( startArgc + 6 < argc ? EngineUnmarshallData< G >()( argv[ startArgc + 6 ] ) : G( defaultArgs.g ) ); + H h = ( startArgc + 7 < argc ? EngineUnmarshallData< H >()( argv[ startArgc + 7 ] ) : H( defaultArgs.h ) ); + I i = ( startArgc + 8 < argc ? EngineUnmarshallData< I >()( argv[ startArgc + 8 ] ) : I( defaultArgs.i ) ); + J j = ( startArgc + 9 < argc ? EngineUnmarshallData< J >()( argv[ startArgc + 9 ] ) : J( defaultArgs.j ) ); + K k = ( startArgc + 10 < argc ? EngineUnmarshallData< K >()( argv[ startArgc + 10 ] ) : K( defaultArgs.k ) ); + fn( a, b, c, d, e, f, g, h, i, j, k ); + } + template< typename Frame > + static void thunk( S32 argc, const char** argv, void ( Frame::*fn )( A, B, C, D, E, F, G, H, I, J, K ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G, H, I, J, K ) >& defaultArgs ) + { + A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); + B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); + C c = ( startArgc + 2 < argc ? EngineUnmarshallData< C >()( argv[ startArgc + 2 ] ) : C( defaultArgs.d ) ); + D d = ( startArgc + 3 < argc ? EngineUnmarshallData< D >()( argv[ startArgc + 3 ] ) : D( defaultArgs.e ) ); + E e = ( startArgc + 4 < argc ? EngineUnmarshallData< E >()( argv[ startArgc + 4 ] ) : E( defaultArgs.f ) ); + F f = ( startArgc + 5 < argc ? EngineUnmarshallData< F >()( argv[ startArgc + 5 ] ) : F( defaultArgs.g ) ); + G g = ( startArgc + 6 < argc ? EngineUnmarshallData< G >()( argv[ startArgc + 6 ] ) : G( defaultArgs.h ) ); + H h = ( startArgc + 7 < argc ? EngineUnmarshallData< H >()( argv[ startArgc + 7 ] ) : H( defaultArgs.i ) ); + I i = ( startArgc + 8 < argc ? EngineUnmarshallData< I >()( argv[ startArgc + 8 ] ) : I( defaultArgs.j ) ); + J j = ( startArgc + 9 < argc ? EngineUnmarshallData< J >()( argv[ startArgc + 9 ] ) : J( defaultArgs.k ) ); + K k = ( startArgc + 10 < argc ? EngineUnmarshallData< K >()( argv[ startArgc + 10 ] ) : K( defaultArgs.l ) ); + ( frame->*fn )( a, b, c, d, e, f, g, h, i, j, k ); + } +}; + + +/// @} + +/// @name API Definition Macros +/// +/// The macros in this group allow to create engine API functions that work both with the +/// legacy console system as well as with the new engine export system. As such, they only +/// support those function features that are available in both systems. This means that for +/// console-style variadic functions, the ConsoleXXX must be used and that for overloaded +/// and/or C-style variadic functions as well as for placing functions in export scopes, +/// DEFINE_CALLIN must be used directly. +/// +/// When the console system is removed, the console thunking functionality will be removed +/// from these macros but otherwise they will remain unchanged and in place. +/// +/// @{ + + +// Helpers to implement initialization checks. Pulled out into separate macros so this can be deactivated easily. +// Especially important for the initialize() function itself. + +#define _CHECK_ENGINE_INITIALIZED_IMPL( fnName, returnType ) \ + if( !engineAPI::gIsInitialized ) \ + { \ + Con::errorf( "EngineAPI: Engine not initialized when calling " #fnName ); \ + return EngineTypeTraits< returnType >::ReturnValue( EngineTypeTraits< returnType >::ReturnValueType() ); \ + } + +#define _CHECK_ENGINE_INITIALIZED( fnName, returnType ) _CHECK_ENGINE_INITIALIZED_IMPL( fnName, returnType ) + + +/// Define a call-in point for calling into the engine. +/// +/// @param name The name of the function as it should be seen by the control layer. +/// @param returnType The value type returned to the control layer. +/// @param args The argument list as it would appear on the function definition +/// @param defaultArgs The list of default argument values. +/// @param usage The usage doc string for the engine API reference. +/// +/// @code +/// DefineEngineFunction( myFunction, int, ( float f, const String& s ), ( "value for s" ), "This is my function." ) +/// { +/// return int( f ) + dAtoi( s ); +/// } +/// @endcode +#define DefineEngineFunction( name, returnType, args, defaultArgs, usage ) \ + static inline returnType _fn ## name ## impl args; \ + TORQUE_API EngineTypeTraits< returnType >::ReturnValueType fn ## name \ + ( _EngineFunctionTrampoline< returnType args >::Args a ) \ + { \ + _CHECK_ENGINE_INITIALIZED( name, returnType ); \ + return EngineTypeTraits< returnType >::ReturnValue( \ + _EngineFunctionTrampoline< returnType args >::jmp( _fn ## name ## impl, a ) \ + ); \ + } \ + static _EngineFunctionDefaultArguments< void args > _fn ## name ## DefaultArgs defaultArgs; \ + static EngineFunctionInfo _fn ## name ## FunctionInfo( \ + #name, \ + &_SCOPE<>()(), \ + usage, \ + #returnType " " #name #args, \ + "fn" #name, \ + TYPE< returnType args >(), \ + &_fn ## name ## DefaultArgs, \ + ( void* ) &fn ## name, \ + 0 \ + ); \ + static _EngineConsoleThunkType< returnType >::ReturnType _ ## name ## caster( SimObject*, S32 argc, const char** argv ) \ + { \ + return _EngineConsoleThunkType< returnType >::ReturnType( _EngineConsoleThunk< 1, returnType args >::thunk( \ + argc, argv, &_fn ## name ## impl, _fn ## name ## DefaultArgs \ + ) ); \ + } \ + static ConsoleFunctionHeader _ ## name ## header \ + ( #returnType, #args, #defaultArgs ); \ + static ConsoleConstructor \ + _ ## name ## obj( NULL, #name, _EngineConsoleThunkType< returnType >::CallbackType( _ ## name ## caster ), usage, \ + _EngineConsoleThunk< 1, returnType args >::NUM_ARGS - _EngineConsoleThunkCountArgs() defaultArgs, \ + _EngineConsoleThunk< 1, returnType args >::NUM_ARGS, \ + false, &_ ## name ## header \ + ); \ + static inline returnType _fn ## name ## impl args + + +// The next thing is a bit tricky. DefineEngineMethod allows to make the 'object' (=this) argument to the function +// implicit which presents quite an obstacle for the macro internals as the engine export system requires the +// name of a DLL symbol that represents an extern "C" function with an explicit first object pointer argument. +// +// Even if we ignored the fact that we don't have a guarantee how the various C++ compilers implement implicit 'this' arguments, +// we could still not just use a C++ method for this as then we would have to get past the C++ compiler's mangling to +// get to the function symbol name (let alone the fact that typing this method correctly would be tricky). +// +// So, the trick employed here is to package all but the implicit 'this' argument in a structure and then define an +// extern "C" function that takes the object pointer as a first argument and the struct type as the second argument. +// This will result in a function with an identical stack call frame layout to the function we want. +// +// Unfortunately, that still requires that function to chain on to the real user-defined function. To do this +// cleanly and portably, _EngineMethodTrampoline is used to unpack and jump the call from extern "C" into C++ space. +// In optimized builds, the compiler should be smart enough to pretty much optimize all our trickery here away. + +#define _DefineMethodTrampoline( className, name, returnType, args ) \ + TORQUE_API EngineTypeTraits< returnType >::ReturnValueType \ + fn ## className ## _ ## name ( className* object, _EngineMethodTrampoline< _ ## className ## name ## frame, returnType args >::Args a ) \ + { \ + _CHECK_ENGINE_INITIALIZED( className::name, returnType ); \ + return EngineTypeTraits< returnType >::ReturnValue( \ + _EngineMethodTrampoline< _ ## className ## name ## frame, returnType args >::jmp( object, a ) \ + ); \ + } + + +/// Define a call-in point for calling a method on an engine object. +/// +/// @param name The name of the C++ class. +/// @param name The name of the method as it should be seen by the control layer. +/// @param returnType The value type returned to the control layer. +/// @param args The argument list as it would appear on the function definition +/// @param defaultArgs The list of default argument values. +/// @param usage The usage doc string for the engine API reference. +/// +/// @code +/// DefineEngineMethod( MyClass, myMethod, int, ( float f, const String& s ), ( "value for s" ), "This is my method." ) +/// { +/// return object->someMethod( f, s ); +/// } +/// @endcode +#define DefineEngineMethod( className, name, returnType, args, defaultArgs, usage ) \ + struct _ ## className ## name ## frame \ + { \ + typedef className ObjectType; \ + className* object; \ + inline returnType _exec args const; \ + }; \ + _DefineMethodTrampoline( className, name, returnType, args ); \ + static _EngineFunctionDefaultArguments< _EngineMethodTrampoline< _ ## className ## name ## frame, void args >::FunctionType > \ + _fn ## className ## name ## DefaultArgs defaultArgs; \ + static EngineFunctionInfo _fn ## className ## name ## FunctionInfo( \ + #name, \ + &_SCOPE< className >()(), \ + usage, \ + "virtual " #returnType " " #name #args, \ + "fn" #className "_" #name, \ + TYPE< _EngineMethodTrampoline< _ ## className ## name ## frame, returnType args >::FunctionType >(), \ + &_fn ## className ## name ## DefaultArgs, \ + ( void* ) &fn ## className ## _ ## name, \ + 0 \ + ); \ + static _EngineConsoleThunkType< returnType >::ReturnType _ ## className ## name ## caster( SimObject* object, S32 argc, const char** argv ) \ + { \ + _ ## className ## name ## frame frame; \ + frame.object = static_cast< className* >( object ); \ + return _EngineConsoleThunkType< returnType >::ReturnType( _EngineConsoleThunk< 2, returnType args >::thunk( \ + argc, argv, &_ ## className ## name ## frame::_exec, &frame, _fn ## className ## name ## DefaultArgs \ + ) ); \ + } \ + static ConsoleFunctionHeader _ ## className ## name ## header \ + ( #returnType, #args, #defaultArgs ); \ + static ConsoleConstructor \ + className ## name ## obj( #className, #name, \ + _EngineConsoleThunkType< returnType >::CallbackType( _ ## className ## name ## caster ), usage, \ + _EngineConsoleThunk< 2, returnType args >::NUM_ARGS - _EngineConsoleThunkCountArgs() defaultArgs, \ + _EngineConsoleThunk< 2, returnType args >::NUM_ARGS, \ + false, &_ ## className ## name ## header \ + ); \ + returnType _ ## className ## name ## frame::_exec args const + + +/// Define a call-in point for calling into the engine. Unlike with DefineEngineFunction, the statically +/// callable function will be confined to the namespace of the given class. +/// +/// @param name The name of the C++ class (or a registered export scope). +/// @param name The name of the method as it should be seen by the control layer. +/// @param returnType The value type returned to the control layer. +/// @param args The argument list as it would appear on the function definition +/// @param defaultArgs The list of default argument values. +/// @param usage The usage doc string for the engine API reference. +/// +/// @code +/// DefineEngineStaticMethod( MyClass, myMethod, int, ( float f, string s ), ( "value for s" ), "This is my method." ) +/// { +/// } +/// @endcode +#define DefineEngineStaticMethod( className, name, returnType, args, defaultArgs, usage ) \ + static inline returnType _fn ## className ## name ## impl args; \ + TORQUE_API EngineTypeTraits< returnType >::ReturnValueType fn ## className ## _ ## name \ + ( _EngineFunctionTrampoline< returnType args >::Args a ) \ + { \ + _CHECK_ENGINE_INITIALIZED( className::name, returnType ); \ + return EngineTypeTraits< returnType >::ReturnValue( \ + _EngineFunctionTrampoline< returnType args >::jmp( _fn ## className ## name ## impl, a ) \ + ); \ + } \ + static _EngineFunctionDefaultArguments< void args > _fn ## className ## name ## DefaultArgs defaultArgs; \ + static EngineFunctionInfo _fn ## name ## FunctionInfo( \ + #name, \ + &_SCOPE< className >()(), \ + usage, \ + #returnType " " #name #args, \ + "fn" #className "_" #name, \ + TYPE< returnType args >(), \ + &_fn ## className ## name ## DefaultArgs, \ + ( void* ) &fn ## className ## _ ## name, \ + 0 \ + ); \ + static _EngineConsoleThunkType< returnType >::ReturnType _ ## className ## name ## caster( SimObject*, S32 argc, const char** argv )\ + { \ + return _EngineConsoleThunkType< returnType >::ReturnType( _EngineConsoleThunk< 1, returnType args >::thunk( \ + argc, argv, &_fn ## className ## name ## impl, _fn ## className ## name ## DefaultArgs \ + ) ); \ + } \ + static ConsoleFunctionHeader _ ## className ## name ## header \ + ( #returnType, #args, #defaultArgs, true ); \ + static ConsoleConstructor \ + _ ## className ## name ## obj( #className, #name, _EngineConsoleThunkType< returnType >::CallbackType( _ ## className ## name ## caster ), usage, \ + _EngineConsoleThunk< 1, returnType args >::NUM_ARGS - _EngineConsoleThunkCountArgs() defaultArgs, \ + _EngineConsoleThunk< 1, returnType args >::NUM_ARGS, \ + false, &_ ## className ## name ## header \ + ); \ + static inline returnType _fn ## className ## name ## impl args + + +// Convenience macros to allow defining functions that use the new marshalling features +// while being only visible in the console interop. When we drop the console system, +// these macros can be removed and all definitions that make use of them can be removed +// as well. +#define DefineConsoleFunction( name, returnType, args, defaultArgs, usage ) \ + static inline returnType _fn ## name ## impl args; \ + static _EngineFunctionDefaultArguments< void args > _fn ## name ## DefaultArgs defaultArgs; \ + static _EngineConsoleThunkType< returnType >::ReturnType _ ## name ## caster( SimObject*, S32 argc, const char** argv ) \ + { \ + return _EngineConsoleThunkType< returnType >::ReturnType( _EngineConsoleThunk< 1, returnType args >::thunk( \ + argc, argv, &_fn ## name ## impl, _fn ## name ## DefaultArgs \ + ) ); \ + } \ + static ConsoleFunctionHeader _ ## name ## header \ + ( #returnType, #args, #defaultArgs ); \ + static ConsoleConstructor \ + _ ## name ## obj( NULL, #name, _EngineConsoleThunkType< returnType >::CallbackType( _ ## name ## caster ), usage, \ + _EngineConsoleThunk< 1, returnType args >::NUM_ARGS - _EngineConsoleThunkCountArgs() defaultArgs, \ + _EngineConsoleThunk< 1, returnType args >::NUM_ARGS, \ + false, &_ ## name ## header \ + ); \ + static inline returnType _fn ## name ## impl args + +#define DefineConsoleMethod( className, name, returnType, args, defaultArgs, usage ) \ + struct _ ## className ## name ## frame \ + { \ + typedef className ObjectType; \ + className* object; \ + inline returnType _exec args const; \ + }; \ + static _EngineFunctionDefaultArguments< _EngineMethodTrampoline< _ ## className ## name ## frame, void args >::FunctionType > \ + _fn ## className ## name ## DefaultArgs defaultArgs; \ + static _EngineConsoleThunkType< returnType >::ReturnType _ ## className ## name ## caster( SimObject* object, S32 argc, const char** argv ) \ + { \ + _ ## className ## name ## frame frame; \ + frame.object = static_cast< className* >( object ); \ + return _EngineConsoleThunkType< returnType >::ReturnType( _EngineConsoleThunk< 2, returnType args >::thunk( \ + argc, argv, &_ ## className ## name ## frame::_exec, &frame, _fn ## className ## name ## DefaultArgs \ + ) ); \ + } \ + static ConsoleFunctionHeader _ ## className ## name ## header \ + ( #returnType, #args, #defaultArgs ); \ + static ConsoleConstructor \ + className ## name ## obj( #className, #name, \ + _EngineConsoleThunkType< returnType >::CallbackType( _ ## className ## name ## caster ), usage, \ + _EngineConsoleThunk< 2, returnType args >::NUM_ARGS - _EngineConsoleThunkCountArgs() defaultArgs, \ + _EngineConsoleThunk< 2, returnType args >::NUM_ARGS, \ + false, &_ ## className ## name ## header \ + ); \ + returnType _ ## className ## name ## frame::_exec args const + +#define DefineConsoleStaticMethod( className, name, returnType, args, defaultArgs, usage ) \ + static inline returnType _fn ## className ## name ## impl args; \ + static _EngineFunctionDefaultArguments< void args > _fn ## className ## name ## DefaultArgs defaultArgs; \ + static _EngineConsoleThunkType< returnType >::ReturnType _ ## className ## name ## caster( SimObject*, S32 argc, const char** argv )\ + { \ + return _EngineConsoleThunkType< returnType >::ReturnType( _EngineConsoleThunk< 1, returnType args >::thunk( \ + argc, argv, &_fn ## className ## name ## impl, _fn ## className ## name ## DefaultArgs \ + ) ); \ + } \ + static ConsoleFunctionHeader _ ## className ## name ## header \ + ( #returnType, #args, #defaultArgs, true ); \ + static ConsoleConstructor \ + _ ## className ## name ## obj( #className, #name, _EngineConsoleThunkType< returnType >::CallbackType( _ ## className ## name ## caster ), usage, \ + _EngineConsoleThunk< 1, returnType args >::NUM_ARGS - _EngineConsoleThunkCountArgs() defaultArgs, \ + _EngineConsoleThunk< 1, returnType args >::NUM_ARGS, \ + false, &_ ## className ## name ## header \ + ); \ + static inline returnType _fn ## className ## name ## impl args + + +// The following three macros are only temporary. They allow to define engineAPI functions using the framework +// here in this file while being visible only in the new API. When the console interop is removed, these macros +// can be removed and all their uses be replaced with their corresponding versions that now still include support +// for the console (e.g. DefineNewEngineFunction should become DefineEngineFunction). +#define DefineNewEngineFunction( name, returnType, args, defaultArgs, usage ) \ + static inline returnType _fn ## name ## impl args; \ + TORQUE_API EngineTypeTraits< returnType >::ReturnValueType fn ## name \ + ( _EngineFunctionTrampoline< returnType args >::Args a ) \ + { \ + _CHECK_ENGINE_INITIALIZED( name, returnType ); \ + return EngineTypeTraits< returnType >::ReturnValue( \ + _EngineFunctionTrampoline< returnType args >::jmp( _fn ## name ## impl, a ) \ + ); \ + } \ + static _EngineFunctionDefaultArguments< void args > _fn ## name ## DefaultArgs defaultArgs; \ + static EngineFunctionInfo _fn ## name ## FunctionInfo( \ + #name, \ + &_SCOPE<>()(), \ + usage, \ + #returnType " " #name #args, \ + "fn" #name, \ + TYPE< returnType args >(), \ + &_fn ## name ## DefaultArgs, \ + ( void* ) &fn ## name, \ + 0 \ + ); \ + static inline returnType _fn ## name ## impl args + +#define DefineNewEngineMethod( className, name, returnType, args, defaultArgs, usage ) \ + struct _ ## className ## name ## frame \ + { \ + typedef className ObjectType; \ + className* object; \ + inline returnType _exec args const; \ + }; \ + _DefineMethodTrampoline( className, name, returnType, args ); \ + static _EngineFunctionDefaultArguments< _EngineMethodTrampoline< _ ## className ## name ## frame, void args >::FunctionType > \ + _fn ## className ## name ## DefaultArgs defaultArgs; \ + static EngineFunctionInfo _fn ## className ## name ## FunctionInfo( \ + #name, \ + &_SCOPE< className >()(), \ + usage, \ + "virtual " #returnType " " #name #args, \ + "fn" #className "_" #name, \ + TYPE< _EngineMethodTrampoline< _ ## className ## name ## frame, returnType args >::FunctionType >(), \ + &_fn ## className ## name ## DefaultArgs, \ + ( void* ) &fn ## className ## _ ## name, \ + 0 \ + ); \ + returnType _ ## className ## name ## frame::_exec args const + +#define DefineNewEngineStaticMethod( className, name, returnType, args, defaultArgs, usage ) \ + static inline returnType _fn ## className ## name ## impl args; \ + TORQUE_API EngineTypeTraits< returnType >::ReturnValueType fn ## className ## _ ## name \ + ( _EngineFunctionTrampoline< returnType args >::Args a ) \ + { \ + _CHECK_ENGINE_INITIALIZED( className::name, returnType ); \ + return EngineTypeTraits< returnType >::ReturnValue( \ + _EngineFunctionTrampoline< returnType args >::jmp( _fn ## className ## name ## impl, a ) \ + ); \ + } \ + static _EngineFunctionDefaultArguments< void args > _fn ## className ## name ## DefaultArgs defaultArgs; \ + static EngineFunctionInfo _fn ## name ## FunctionInfo( \ + #name, \ + &_SCOPE< className >()(), \ + usage, \ + #returnType " " #name #args, \ + "fn" #className "_" #name, \ + TYPE< returnType args >(), \ + &_fn ## className ## name ## DefaultArgs, \ + ( void* ) &fn ## className ## _ ## name, \ + 0 \ + ); \ + static inline returnType _fn ## className ## name ## impl args + +/// @} + + +//============================================================================= +// Callbacks. +//============================================================================= + +/// Matching implement for DECLARE_CALLBACK. +/// +/// +/// @warn With the new interop system, method-style callbacks must not be triggered on object +/// that are being created! This is because the control layer will likely not yet have a fully valid wrapper +/// object in place for the EngineObject under construction. +#define IMPLEMENT_CALLBACK( class, name, returnType, args, argNames, usageString ) \ + struct _ ## class ## name ## frame { typedef class ObjectType; }; \ + TORQUE_API _EngineMethodTrampoline< _ ## class ## name ## frame, returnType args >::FunctionType* cb ## class ## _ ## name; \ + TORQUE_API void set_cb ## class ## _ ## name( \ + _EngineMethodTrampoline< _ ## class ## name ## frame, returnType args >::FunctionType fn ) \ + { cb ## class ## _ ## name = fn; } \ + _EngineMethodTrampoline< _ ## class ## name ## frame, returnType args >::FunctionType* cb ## class ## _ ## name; \ + namespace { \ + ::EngineFunctionInfo _cb ## class ## name( \ + #name, \ + &::_SCOPE< class >()(), \ + usageString, \ + "virtual " #returnType " " #name #args, \ + "cb" #class "_" #name, \ + ::TYPE< _EngineMethodTrampoline< _ ## class ## name ## frame, returnType args >::FunctionType >(), \ + NULL, \ + ( void* ) &cb ## class ## _ ## name, \ + EngineFunctionCallout \ + ); \ + } \ + returnType class::name ## _callback args \ + { \ + if( cb ## class ## _ ## name ) { \ + _EngineCallbackHelper cbh( this, reinterpret_cast< const void* >( cb ## class ## _ ## name ) ); \ + return returnType( cbh.call< returnType > argNames ); \ + } \ + if( engineAPI::gUseConsoleInterop ) \ + { \ + static StringTableEntry sName = StringTable->insert( #name ); \ + _EngineConsoleCallbackHelper cbh( sName, this ); \ + return returnType( cbh.call< returnType > argNames ); \ + } \ + return returnType(); \ + } \ + namespace { \ + ConsoleFunctionHeader _ ## class ## name ## header( \ + #returnType, #args, "" ); \ + ConsoleConstructor _ ## class ## name ## obj( #class, #name, usageString, &_ ## class ## name ## header ); \ + } + + +/// Used to define global callbacks not associated with +/// any particular class or namespace. +#define IMPLEMENT_GLOBAL_CALLBACK( name, returnType, args, argNames, usageString ) \ + DEFINE_CALLOUT( cb ## name, name,, returnType, args, 0, usageString ); \ + returnType name ## _callback args \ + { \ + if( cb ## name ) \ + return returnType( cb ## name argNames ); \ + if( engineAPI::gUseConsoleInterop ) \ + { \ + static StringTableEntry sName = StringTable->insert( #name ); \ + _EngineConsoleCallbackHelper cbh( sName, NULL ); \ + return returnType( cbh.call< returnType > argNames ); \ + } \ + return returnType(); \ + } \ + namespace { \ + ConsoleFunctionHeader _ ## name ## header( \ + #returnType, #args, "" ); \ + ConsoleConstructor _ ## name ## obj( NULL, #name, usageString, &_ ## name ## header ); \ + } + + +// Again, temporary macros to allow splicing the API while we still have the console interop around. + +#define IMPLEMENT_CONSOLE_CALLBACK( class, name, returnType, args, argNames, usageString ) \ + returnType class::name ## _callback args \ + { \ + if( engineAPI::gUseConsoleInterop ) \ + { \ + static StringTableEntry sName = StringTable->insert( #name ); \ + _EngineConsoleCallbackHelper cbh( sName, this ); \ + return returnType( cbh.call< returnType > argNames ); \ + } \ + return returnType(); \ + } \ + namespace { \ + ConsoleFunctionHeader _ ## class ## name ## header( \ + #returnType, #args, "" ); \ + ConsoleConstructor _ ## class ## name ## obj( #class, #name, usageString, &_ ## class ## name ## header ); \ + } + +#define IMPLEMENT_NEW_CALLBACK( class, name, returnType, args, argNames, usageString ) \ + struct _ ## class ## name ## frame { typedef class ObjectType; }; \ + TORQUE_API _EngineMethodTrampoline< _ ## class ## name ## frame, returnType args >::FunctionType* cb ## class ## _ ## name; \ + TORQUE_API void set_cb ## class ## _ ## name( \ + _EngineMethodTrampoline< _ ## class ## name ## frame, returnType args >::FunctionType fn ) \ + { cb ## class ## _ ## name = fn; } \ + _EngineMethodTrampoline< _ ## class ## name ## frame, returnType args >::FunctionType* cb ## class ## _ ## name; \ + namespace { \ + ::EngineFunctionInfo _cb ## class ## name( \ + #name, \ + &::_SCOPE< class >()(), \ + usageString, \ + "virtual " #returnType " " #name #args, \ + "cb" #class "_" #name, \ + ::TYPE< _EngineMethodTrampoline< _ ## class ## name ## frame, returnType args >::FunctionType >(), \ + NULL, \ + &cb ## class ## _ ## name, \ + EngineFunctionCallout \ + ); \ + } \ + returnType class::name ## _callback args \ + { \ + if( cb ## class ## _ ## name ) { \ + _EngineCallbackHelper cbh( this, reinterpret_cast< const void* >( cb ## class ## _ ## name ) ); \ + return returnType( cbh.call< returnType > argNames ); \ + } \ + return returnType(); \ + } + + +// Internal helper class for doing call-outs in the new interop. +struct _EngineCallbackHelper +{ + protected: + + EngineObject* mThis; + const void* mFn; + + public: + + _EngineCallbackHelper( EngineObject* pThis, const void* fn ) + : mThis( pThis ), + mFn( fn ) {} + + template< typename R > + R call() const + { + typedef R( FunctionType )( EngineObject* ); + return R( reinterpret_cast< FunctionType* >( mFn )( mThis ) ); + } + template< typename R, typename A > + R call( A a ) const + { + typedef R( FunctionType )( EngineObject*, A ); + return R( reinterpret_cast< FunctionType* >( mFn )( mThis, a ) ); + } + template< typename R, typename A, typename B > + R call( A a, B b ) const + { + typedef R( FunctionType )( EngineObject*, A, B ); + return R( reinterpret_cast< FunctionType* >( mFn )( mThis, a, b ) ); + } + template< typename R, typename A, typename B, typename C > + R call( A a, B b, C c ) const + { + typedef R( FunctionType )( EngineObject*, A, B, C ); + return R( reinterpret_cast< FunctionType* >( mFn )( mThis, a, b, c ) ); + } + template< typename R, typename A, typename B, typename C, typename D > + R call( A a, B b, C c, D d ) const + { + typedef R( FunctionType )( EngineObject*, A, B, C, D ); + return R( reinterpret_cast< FunctionType* >( mFn )( mThis, a, b, c, d ) ); + } + template< typename R, typename A, typename B, typename C, typename D, typename E > + R call( A a, B b, C c, D d, E e ) const + { + typedef R( FunctionType )( EngineObject*, A, B, C, D, E ); + return R( reinterpret_cast< FunctionType* >( mFn )( mThis, a, b, c, d, e ) ); + } + template< typename R, typename A, typename B, typename C, typename D, typename E, typename F > + R call( A a, B b, C c, D d, E e, F f ) const + { + typedef R( FunctionType )( EngineObject*, A, B, C, D, E, F ); + return R( reinterpret_cast< FunctionType* >( mFn )( mThis, a, b, c, d, e, f ) ); + } + template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G > + R call( A a, B b, C c, D d, E e, F f, G g ) const + { + typedef R( FunctionType )( EngineObject*, A, B, C, D, E, F, G ); + return R( reinterpret_cast< FunctionType* >( mFn )( mThis, a, b, c, d, e, f, g ) ); + } + template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H > + R call( A a, B b, C c, D d, E e, F f, G g, H h ) const + { + typedef R( FunctionType )( EngineObject*, A, B, C, D, E, F, G, H ); + return R( reinterpret_cast< FunctionType* >( mFn )( mThis, a, b, c, d, e, f, g, h ) ); + } + template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I > + R call( A a, B b, C c, D d, E e, F f, G g, H h, I i ) const + { + typedef R( FunctionType )( EngineObject*, A, B, C, D, E, F, G, H, I ); + return R( reinterpret_cast< FunctionType* >( mFn )( mThis, a, b, c, d, e, f, g, h, i ) ); + } + template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J > + R call( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j ) const + { + typedef R( FunctionType )( EngineObject*, A, B, C, D, E, F, G, H, I, J ); + return R( reinterpret_cast< FunctionType* >( mFn )( mThis, a, b, c, d, e, f, g, h, i, j ) ); + } + template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K > + R call( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k ) const + { + typedef R( FunctionType )( EngineObject*, A, B, C, D, E, F, G, H, I, J, K ); + return R( reinterpret_cast< FunctionType* >( mFn )( mThis, a, b, c, d, e, f, g, h, i, j, k ) ); + } + template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K, typename L > + R call( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l ) const + { + typedef R( FunctionType )( EngineObject*, A, B, C, D, E, F, G, H, I, J, K, L l ); + return R( reinterpret_cast< FunctionType* >( mFn )( mThis, a, b, c, d, e, f, g, h, i, j, k, l ) ); + } +}; + +// Internal helper for callback support in legacy console system. +struct _EngineConsoleCallbackHelper +{ + + protected: + + /// Matches up to storeArgs. + static const U32 MAX_ARGUMENTS = 11; + + SimObject* mThis; + S32 mArgc; + const char* mArgv[ MAX_ARGUMENTS + 2 ]; + + const char* _exec() + { + if( mThis ) + { + // Cannot invoke callback until object has been registered + return mThis->isProperlyAdded() ? Con::execute( mThis, mArgc, mArgv ) : ""; + } + else + return Con::execute( mArgc, mArgv ); + } + + public: + + _EngineConsoleCallbackHelper( StringTableEntry callbackName, SimObject* pThis ) + : mThis( pThis ), + mArgc( pThis ? 2 : 1 ) + { + mArgv[ 0 ] = callbackName; + } + + template< typename R > + R call() + { + return R( EngineUnmarshallData< R >()( _exec() ) ); + } + template< typename R, typename A > + R call( A a ) + { + EngineMarshallData( a, mArgc, mArgv ); + return R( EngineUnmarshallData< R >()( _exec() ) ); + } + template< typename R, typename A, typename B > + R call( A a, B b ) + { + EngineMarshallData( a, mArgc, mArgv ); + EngineMarshallData( b, mArgc, mArgv ); + return R( EngineUnmarshallData< R >()( _exec() ) ); + } + template< typename R, typename A, typename B, typename C > + R call( A a, B b, C c ) + { + EngineMarshallData( a, mArgc, mArgv ); + EngineMarshallData( b, mArgc, mArgv ); + EngineMarshallData( c, mArgc, mArgv ); + return R( EngineUnmarshallData< R >()( _exec() ) ); + } + template< typename R, typename A, typename B, typename C, typename D > + R call( A a, B b, C c, D d ) + { + EngineMarshallData( a, mArgc, mArgv ); + EngineMarshallData( b, mArgc, mArgv ); + EngineMarshallData( c, mArgc, mArgv ); + EngineMarshallData( d, mArgc, mArgv ); + return R( EngineUnmarshallData< R >()( _exec() ) ); + } + template< typename R, typename A, typename B, typename C, typename D, typename E > + R call( A a, B b, C c, D d, E e ) + { + EngineMarshallData( a, mArgc, mArgv ); + EngineMarshallData( b, mArgc, mArgv ); + EngineMarshallData( c, mArgc, mArgv ); + EngineMarshallData( d, mArgc, mArgv ); + EngineMarshallData( e, mArgc, mArgv ); + return R( EngineUnmarshallData< R >()( _exec() ) ); + } + template< typename R, typename A, typename B, typename C, typename D, typename E, typename F > + R call( A a, B b, C c, D d, E e, F f ) + { + EngineMarshallData( a, mArgc, mArgv ); + EngineMarshallData( b, mArgc, mArgv ); + EngineMarshallData( c, mArgc, mArgv ); + EngineMarshallData( d, mArgc, mArgv ); + EngineMarshallData( e, mArgc, mArgv ); + EngineMarshallData( f, mArgc, mArgv ); + return R( EngineUnmarshallData< R >()( _exec() ) ); + } + template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G > + R call( A a, B b, C c, D d, E e, F f, G g ) + { + EngineMarshallData( a, mArgc, mArgv ); + EngineMarshallData( b, mArgc, mArgv ); + EngineMarshallData( c, mArgc, mArgv ); + EngineMarshallData( d, mArgc, mArgv ); + EngineMarshallData( e, mArgc, mArgv ); + EngineMarshallData( f, mArgc, mArgv ); + EngineMarshallData( g, mArgc, mArgv ); + return R( EngineUnmarshallData< R >()( _exec() ) ); + } + template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H > + R call( A a, B b, C c, D d, E e, F f, G g, H h ) + { + EngineMarshallData( a, mArgc, mArgv ); + EngineMarshallData( b, mArgc, mArgv ); + EngineMarshallData( c, mArgc, mArgv ); + EngineMarshallData( d, mArgc, mArgv ); + EngineMarshallData( e, mArgc, mArgv ); + EngineMarshallData( f, mArgc, mArgv ); + EngineMarshallData( g, mArgc, mArgv ); + EngineMarshallData( h, mArgc, mArgv ); + return R( EngineUnmarshallData< R >()( _exec() ) ); + } + template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I > + R call( A a, B b, C c, D d, E e, F f, G g, H h, I i ) + { + EngineMarshallData( a, mArgc, mArgv ); + EngineMarshallData( b, mArgc, mArgv ); + EngineMarshallData( c, mArgc, mArgv ); + EngineMarshallData( d, mArgc, mArgv ); + EngineMarshallData( e, mArgc, mArgv ); + EngineMarshallData( f, mArgc, mArgv ); + EngineMarshallData( g, mArgc, mArgv ); + EngineMarshallData( h, mArgc, mArgv ); + EngineMarshallData( i, mArgc, mArgv ); + return R( EngineUnmarshallData< R >()( _exec() ) ); + } + template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J > + R call( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j ) + { + EngineMarshallData( a, mArgc, mArgv ); + EngineMarshallData( b, mArgc, mArgv ); + EngineMarshallData( c, mArgc, mArgv ); + EngineMarshallData( d, mArgc, mArgv ); + EngineMarshallData( e, mArgc, mArgv ); + EngineMarshallData( f, mArgc, mArgv ); + EngineMarshallData( g, mArgc, mArgv ); + EngineMarshallData( h, mArgc, mArgv ); + EngineMarshallData( i, mArgc, mArgv ); + EngineMarshallData( j, mArgc, mArgv ); + return R( EngineUnmarshallData< R >()( _exec() ) ); + } + template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K > + R call( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k ) + { + EngineMarshallData( a, mArgc, mArgv ); + EngineMarshallData( b, mArgc, mArgv ); + EngineMarshallData( c, mArgc, mArgv ); + EngineMarshallData( d, mArgc, mArgv ); + EngineMarshallData( e, mArgc, mArgv ); + EngineMarshallData( f, mArgc, mArgv ); + EngineMarshallData( g, mArgc, mArgv ); + EngineMarshallData( h, mArgc, mArgv ); + EngineMarshallData( i, mArgc, mArgv ); + EngineMarshallData( j, mArgc, mArgv ); + EngineMarshallData( k, mArgc, mArgv ); + return R( EngineUnmarshallData< R >()( _exec() ) ); + } + template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K, typename L > + R call( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l ) + { + EngineMarshallData( a, mArgc, mArgv ); + EngineMarshallData( b, mArgc, mArgv ); + EngineMarshallData( c, mArgc, mArgv ); + EngineMarshallData( d, mArgc, mArgv ); + EngineMarshallData( e, mArgc, mArgv ); + EngineMarshallData( f, mArgc, mArgv ); + EngineMarshallData( g, mArgc, mArgv ); + EngineMarshallData( h, mArgc, mArgv ); + EngineMarshallData( i, mArgc, mArgv ); + EngineMarshallData( j, mArgc, mArgv ); + EngineMarshallData( k, mArgc, mArgv ); + EngineMarshallData( l, mArgc, mArgv ); + return R( EngineUnmarshallData< R >()( _exec() ) ); + } +}; + + +// Re-enable some VC warnings we disabled for this file. +#pragma warning( default : 4510 ) +#pragma warning( default : 4610 ) + +#endif // !_ENGINEAPI_H_ diff --git a/Engine/source/console/engineDoc.cpp b/Engine/source/console/engineDoc.cpp new file mode 100644 index 000000000..f049b6cc3 --- /dev/null +++ b/Engine/source/console/engineDoc.cpp @@ -0,0 +1,723 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/console.h" + +#include "console/engineAPI.h" +#include "core/stream/fileStream.h" +#include "console/consoleInternal.h" +#include "console/compiler.h" + +#define USE_UNDOCUMENTED_GROUP + +/// @file +/// Documentation generator for the current TorqueScript-based engine API. +/// +/// Be aware that this generator is solely for the legacy console system and +/// is and will not be useful to the new engine API system. It will go away +/// when the console interop is removed. + + +/// Used to track unique groups encountered during +/// the dump process. +static HashTable smDocGroups; + + +static void dumpDoc( Stream& stream, const char* text, bool checkUngrouped = true ) +{ + // Extract brief. + + String brief; + + if( text ) + { + const char* briefTag = dStrstr( text, "@brief" ); + if( !briefTag ) + { + const char* newline = dStrchr( text, '\n' ); + if( newline ) + { + brief = String( text, newline - text ); + text = newline + 1; + } + else + { + brief = text; + text = NULL; + } + } + } + + // Write doc comment. + + if( !brief.isEmpty() ) + { + stream.writeText( "@brief " ); + stream.writeText( brief ); + stream.writeText( "\r\n\r\n" ); + } + + if( text ) + stream.writeText( text ); +#ifdef USE_UNDOCUMENTED_GROUP + if( checkUngrouped && ( !text || !dStrstr( text, "@ingroup" ) ) ) + { + smDocGroups.insertUnique( "UNDOCUMENTED", 0 ); + stream.writeText( "\r\n@ingroup UNDOCUMENTED\r\n" ); + } +#endif +} + +static void dumpFragment( Stream& stream, ConsoleDocFragment* fragment ) +{ + if( !fragment->mText || !fragment->mText[ 0 ] ) + return; + + // Emit doc text in comment. + + stream.writeText( "/*!\r\n" ); + stream.writeText( fragment->mText ); + stream.writeText( "*/\r\n\r\n" ); + + // Emit definition, if any. + + if( fragment->mDefinition ) + { + stream.writeText( fragment->mDefinition ); + stream.writeText( "\r\n" ); + } +} + +static void dumpVariable( Stream& stream, + Dictionary::Entry* entry, + const char* inClass = NULL ) +{ + // Skip variables defined in script. + + if( entry->type < 0 ) + return; + + // Skip internals... don't export them. + if ( entry->mUsage && + ( dStrstr( entry->mUsage, "@hide" ) || dStrstr( entry->mUsage, "@internal" ) ) ) + return; + + // Split up qualified name. + + Vector< String > nameComponents; + String( entry->name ).split( "::", nameComponents ); + if( !nameComponents.size() ) // Safety check. + return; + + // Match filter. + + if( inClass ) + { + // Make sure first qualifier in name components is a + // namespace qualifier matching the given class name. + + if( nameComponents.size() <= 1 || dStricmp( nameComponents.first().c_str() + 1, inClass ) != 0 ) // Skip '$'. + return; + } + else + { + // Make sure, this is *not* in a class namespace. + + if( nameComponents.size() > 1 && Con::lookupNamespace( nameComponents.first().c_str() + 1 )->mClassRep ) + return; + } + + // Skip variables for which we can't decipher their type. + + ConsoleBaseType* type = ConsoleBaseType::getType( entry->type ); + if( !type ) + { + Con::errorf( "Can't find type for variable '%s'", entry->name ); + return; + } + + // Write doc comment. + + stream.writeText( "/*!\r\n" ); + + if( !inClass ) + { + stream.writeText( "@var " ); + stream.writeText( type->getTypeClassName() ); + stream.writeText( " " ); + stream.writeText( entry->name ); + stream.writeText( ";\r\n" ); + } + + dumpDoc( stream, entry->mUsage ); + + stream.writeText( "*/\r\n" ); + + // Write definition. + + const U32 numNameComponents = nameComponents.size(); + if( !inClass && numNameComponents > 1 ) + for( U32 i = 0; i < ( numNameComponents - 1 ); ++ i ) + { + stream.writeText( "namespace " ); + stream.writeText( nameComponents[ i ] ); + stream.writeText( " { " ); + } + + if( inClass ) + stream.writeText( "static " ); + + if( entry->mIsConstant ) + stream.writeText( "const " ); + + stream.writeText( type->getTypeClassName() ); + stream.writeText( " " ); + stream.writeText( nameComponents.last() ); + stream.writeText( ";" ); + + if( !inClass && numNameComponents > 1 ) + for( U32 i = 0; i < ( numNameComponents - 1 ); ++ i ) + stream.writeText( " } " ); + + stream.writeText( "\r\n" ); +} + +static void dumpVariables( Stream& stream, const char* inClass = NULL ) +{ + const U32 hashTableSize = gEvalState.globalVars.hashTable->size; + for( U32 i = 0; i < hashTableSize; ++ i ) + for( Dictionary::Entry* entry = gEvalState.globalVars.hashTable->data[ i ]; entry != NULL; entry = entry->nextEntry ) + dumpVariable( stream, entry, inClass ); +} + +static void dumpFunction( Stream &stream, + bool isClassMethod, + Namespace::Entry* entry ) +{ + String doc = entry->getDocString().trim(); + String prototype = entry->getPrototypeString().trim(); + + // If the doc string contains @hide, skip this function. + + if( dStrstr( doc.c_str(), "@hide" ) || dStrstr( doc.c_str(), "@internal" ) ) + return; + + // Make sure we have a valid function prototype. + + if( prototype.isEmpty() ) + { + Con::errorf( "Function '%s::%s' has no prototype!", entry->mNamespace->mName, entry->mFunctionName ); + return; + } + + // See if it's a static method. + + bool isStaticMethod = false; + if( entry->mHeader ) + isStaticMethod = entry->mHeader->mIsStatic; + + // Emit the doc comment. + + if( !doc.isEmpty() ) + { + stream.writeText( "/*!\r\n" ); + + // If there's no @brief, take the first line of the doc text body + // as the description. + + const char* brief = dStrstr( doc, "@brief" ); + if( !brief ) + { + String brief = entry->getBriefDescription( &doc ); + + brief.trim(); + if( !brief.isEmpty() ) + { + stream.writeText( "@brief " ); + stream.writeText( brief ); + stream.writeText( "\r\n\r\n" ); + } + } + + stream.writeText( doc ); + + // Emit @ingroup if it's not a class method. + + if ( !isClassMethod && !isStaticMethod ) // Extra static method check for static classes (which will come out as non-class namespaces). + { + const char *group = dStrstr( doc, "@ingroup" ); + if( group ) + { + char groupName[ 256 ] = { 0 }; + dSscanf( group, "@ingroup %s", groupName ); + smDocGroups.insertUnique( groupName, 0 ); + } +#ifdef USE_UNDOCUMENTED_GROUP + else + { + smDocGroups.insertUnique( "UNDOCUMENTED", 0 ); + stream.writeText( "\r\n@ingroup UNDOCUMENTED\r\n" ); + } +#endif + } + + stream.writeText( "*/\r\n" ); + } +#ifdef USE_UNDOCUMENTED_GROUP + else if( !isClassMethod ) + { + smDocGroups.insertUnique( "UNDOCUMENTED", 0 ); + stream.writeText( "/*! UNDOCUMENTED!\r\n@ingroup UNDOCUMENTED\r\n */\r\n" ); + } +#endif + + if( isStaticMethod ) + stream.writeText( "static " ); + + stream.writeText( prototype ); + stream.writeText( ";\r\n" ); +} + +static void dumpNamespaceEntries( Stream &stream, Namespace *g, bool callbacks = false ) +{ + /// Only print virtual on methods that are members of + /// classes as this allows doxygen to properly do overloads. + const bool isClassMethod = g->mClassRep != NULL; + + // Go through all the entries in the namespace. + for ( Namespace::Entry *ewalk = g->mEntryList; ewalk; ewalk = ewalk->mNext ) + { + int eType = ewalk->mType; + + // We do not dump script defined functions... only engine exports. + if( eType == Namespace::Entry::ConsoleFunctionType + || eType == Namespace::Entry::GroupMarker ) + continue; + + if( eType == Namespace::Entry::ScriptCallbackType ) + { + if( !callbacks ) + continue; + } + else if( callbacks ) + continue; + + dumpFunction( stream, isClassMethod, ewalk ); + } +} + +static void dumpClassHeader( Stream &stream, + const char *usage, + const char *className, + const char *superClassName ) +{ + if ( usage ) + { + stream.writeText( "/*!\r\n" ); + stream.writeText( usage ); + + const char *group = dStrstr( usage, "@ingroup" ); + if ( group ) + { + char groupName[256] = { 0 }; + dSscanf( group, "@ingroup %s", groupName ); + smDocGroups.insertUnique( groupName, 0 ); + } +#ifdef USE_UNDOCUMENTED_GROUP + else + { + smDocGroups.insertUnique( "UNDOCUMENTED", 0 ); + stream.writeText( "\r\n@ingroup UNDOCUMENTED\r\n" ); + } +#endif + + stream.writeText( "\r\n*/\r\n" ); + } + else + { + // No documentation string. Check whether ther is a separate + // class doc fragement. + + bool haveClassDocFragment = false; + if( className ) + { + char buffer[ 1024 ]; + dSprintf( buffer, sizeof( buffer ), "@class %s", className ); + + for( ConsoleDocFragment* fragment = ConsoleDocFragment::smFirst; + fragment != NULL; fragment = fragment->mNext ) + if( !fragment->mClass && dStrstr( fragment->mText, buffer ) != NULL ) + { + haveClassDocFragment = true; + break; + } + } +#ifdef USE_UNDOCUMENTED_GROUP + if( !haveClassDocFragment ) + { + smDocGroups.insertUnique( "UNDOCUMENTED", 0 ); + stream.writeText( "/*! UNDOCUMENTED!\r\n@ingroup UNDOCUMENTED\r\n */\r\n" ); + } +#endif + } + + // Print out appropriate class header + if ( superClassName ) + stream.writeText( String::ToString( "class %s : public %s {\r\npublic:\r\n", className, superClassName ? superClassName : "" ) ); + else if ( className ) + stream.writeText( String::ToString( "class %s {\r\npublic:\r\n", className ) ); + else + stream.writeText( "namespace {\r\n" ); +} + +static void dumpClassMember( Stream &stream, + const AbstractClassRep::Field& field ) +{ + stream.writeText( "/*!\r\n" ); + + if( field.pFieldDocs && field.pFieldDocs[ 0 ] ) + { + stream.writeText( "@brief " ); + + String docs( field.pFieldDocs ); + S32 newline = docs.find( '\n' ); + if( newline == -1 ) + stream.writeText( field.pFieldDocs ); + else + { + String brief = docs.substr( 0, newline ); + String body = docs.substr( newline + 1 ); + + stream.writeText( brief ); + stream.writeText( "\r\n\r\n" ); + stream.writeText( body ); + } + + stream.writeText( "\r\n" ); + } + + const bool isDeprecated = ( field.type == AbstractClassRep::DeprecatedFieldType ); + if( isDeprecated ) + stream.writeText( "@deprecated This member is deprecated and its value is always undefined.\r\n" ); + + stream.writeText( "*/\r\n" ); + + ConsoleBaseType* cbt = ConsoleBaseType::getType( field.type ); + const char* type = ( cbt ? cbt->getTypeClassName() : "" ); + + if( field.elementCount > 1 ) + stream.writeText( String::ToString( "%s %s[ %i ];\r\n", isDeprecated ? "deprecated" : type, field.pFieldname, field.elementCount ) ); + else + stream.writeText( String::ToString( "%s %s;\r\n", isDeprecated ? "deprecated" : type, field.pFieldname ) ); +} + +static void dumpClassFooter( Stream &stream ) +{ + stream.writeText( "};\r\n\r\n" ); +} + +static void dumpGroupStart( Stream &stream, + const char *aName, + const char *aDocs = NULL ) +{ + stream.writeText( String::ToString( "\r\n/*! @name %s\r\n", aName ) ); + + if ( aDocs ) + { + stream.writeText( aDocs ); + stream.writeText( "\r\n" ); + } + + stream.writeText( "@{ */\r\n" ); + + // Add a blank comment in order to make sure groups are parsed properly. + //Con::printf(" /*! */"); +} + +static void dumpGroupEnd( Stream &stream ) +{ + stream.writeText( "/// @}\r\n\r\n" ); +} + +static void dumpClasses( Stream &stream ) +{ + Namespace::trashCache(); + + VectorPtr vec; + vec.reserve( 1024 ); + + // We use mHashSequence to mark if we have traversed... + // so mark all as zero to start. + for ( Namespace *walk = Namespace::mNamespaceList; walk; walk = walk->mNext ) + walk->mHashSequence = 0; + + for(Namespace *walk = Namespace::mNamespaceList; walk; walk = walk->mNext) + { + VectorPtr stack; + stack.reserve( 1024 ); + + // Get all the parents of this namespace... (and mark them as we go) + Namespace *parentWalk = walk; + while(parentWalk) + { + if(parentWalk->mHashSequence != 0) + break; + if(parentWalk->mPackage == 0) + { + parentWalk->mHashSequence = 1; // Mark as traversed. + stack.push_back(parentWalk); + } + parentWalk = parentWalk->mParent; + } + + // Load stack into our results vector. + while(stack.size()) + { + vec.push_back(stack[stack.size() - 1]); + stack.pop_back(); + } + } + + // Go through previously discovered classes + U32 i; + for(i = 0; i < vec.size(); i++) + { + const char *className = vec[i]->mName; + const char *superClassName = vec[i]->mParent ? vec[i]->mParent->mName : NULL; + + // Skip the global namespace, that gets dealt with in dumpFunctions + if(!className) + continue; + + // We're just dumping engine functions, then we don't want to dump + // a class that only contains script functions. So, we iterate over + // all the functions. + bool found = false; + for( Namespace::Entry *ewalk = vec[i]->mEntryList; ewalk; ewalk = ewalk->mNext ) + { + if( ewalk->mType != Namespace::Entry::ConsoleFunctionType ) + { + found = true; + break; + } + } + + // If we don't have engine functions and the namespace name + // doesn't match the class name... then its a script class. + if ( !found && !vec[i]->isClass() ) + continue; + + // If we hit a class with no members and no classRep, do clever filtering. + if(vec[i]->mEntryList == NULL && vec[i]->mClassRep == NULL) + { + // Print out a short stub so we get a proper class hierarchy. + if ( superClassName ) + { + // Filter hack; we don't want non-inheriting classes... + dumpClassHeader( stream, NULL, className, superClassName ); + dumpClassFooter( stream ); + } + continue; + } + + // Skip over hidden or internal classes. + if( vec[i]->mUsage && + ( dStrstr( vec[i]->mUsage, "@hide" ) || dStrstr( vec[i]->mUsage, "@internal" ) ) ) + continue; + + // Print the header for the class.. + dumpClassHeader( stream, vec[i]->mUsage, className, superClassName ); + + // Dump all fragments for this class. + + for( ConsoleDocFragment* fragment = ConsoleDocFragment::smFirst; fragment != NULL; fragment = fragment->mNext ) + if( fragment->mClass && dStricmp( fragment->mClass, className ) == 0 ) + dumpFragment( stream, fragment ); + + // Dump member functions. + dumpNamespaceEntries( stream, vec[ i ], false ); + + // Dump callbacks. + dumpGroupStart( stream, "Callbacks" ); + dumpNamespaceEntries( stream, vec[ i ], true ); + dumpGroupEnd( stream ); + + // Dump static member variables. + dumpVariables( stream, className ); + + // Deal with the classRep (to get members)... + AbstractClassRep *rep = vec[i]->mClassRep; + AbstractClassRep::FieldList emptyList; + AbstractClassRep::FieldList *parentList = &emptyList; + AbstractClassRep::FieldList *fieldList = &emptyList; + if ( rep ) + { + // Get information about the parent's fields... + AbstractClassRep *parentRep = vec[i]->mParent ? vec[i]->mParent->mClassRep : NULL; + if(parentRep) + parentList = &(parentRep->mFieldList); + + // Get information about our fields + fieldList = &(rep->mFieldList); + + // Go through all our fields... + for(U32 j = 0; j < fieldList->size(); j++) + { + const AbstractClassRep::Field &field = (*fieldList)[j]; + + switch( field.type ) + { + case AbstractClassRep::StartArrayFieldType: + case AbstractClassRep::EndArrayFieldType: + break; + case AbstractClassRep::StartGroupFieldType: + dumpGroupStart( stream, field.pGroupname, field.pFieldDocs ); + break; + case AbstractClassRep::EndGroupFieldType: + dumpGroupEnd( stream ); + break; + default: + case AbstractClassRep::DeprecatedFieldType: + // Skip over fields that are already defined in + // our parent class. + if ( parentRep && parentRep->findField( field.pFieldname ) ) + continue; + + dumpClassMember( stream, field ); + break; + } + } + } + + // Close the class/namespace. + dumpClassFooter( stream ); + } +} + +static void dumpEnum( Stream& stream, const EngineTypeInfo* type ) +{ + if( !type->getEnumTable() ) // Sanity check. + return; + + // Skip internals... don't export them. + if ( type->getDocString() && + ( dStrstr( type->getDocString(), "@hide" ) || dStrstr( type->getDocString(), "@internal" ) ) ) + return; + + // Write documentation. + + stream.writeText( "/*!\r\n" ); + dumpDoc( stream, type->getDocString() ); + stream.writeText( "*/\r\n" ); + + // Write definition. + + stream.writeText( "enum " ); + stream.writeText( type->getTypeName() ); + stream.writeText( " {\r\n" ); + + const EngineEnumTable& table = *( type->getEnumTable() ); + const U32 numValues = table.getNumValues(); + + for( U32 i = 0; i < numValues; ++ i ) + { + const EngineEnumTable::Value& value = table[ i ]; + + stream.writeText( "/*!\r\n" ); + dumpDoc( stream, value.getDocString(), false ); + stream.writeText( "*/\r\n" ); + stream.writeText( value.getName() ); + stream.writeText( ",\r\n" ); + } + + stream.writeText( "};\r\n" ); +} + +static void dumpEnums( Stream& stream ) +{ + for( const EngineTypeInfo* type = EngineTypeInfo::getFirstType(); + type != NULL; type = type->getNextType() ) + if( type->isEnum() || type->isBitfield() ) + dumpEnum( stream, type ); +} + +static bool dumpEngineDocs( const char *outputFile ) +{ + // Create the output stream. + FileStream stream; + if ( !stream.open( outputFile, Torque::FS::File::Write ) ) + { + Con::errorf( "dumpEngineDocs - Failed to open output file." ); + return false; + } + + // First dump all global ConsoleDoc fragments. + + for( ConsoleDocFragment* fragment = ConsoleDocFragment::smFirst; fragment != NULL; fragment = fragment->mNext ) + if( !fragment->mClass ) + dumpFragment( stream, fragment ); + + // Clear the doc groups before continuing, + smDocGroups.clear(); + + // Dump enumeration types. + dumpEnums( stream ); + + // Dump all global variables. + dumpVariables( stream ); + + // Now dump the global functions. + Namespace *g = Namespace::find( NULL ); + while( g ) + { + dumpNamespaceEntries( stream, g ); + + // Dump callbacks. + dumpGroupStart( stream, "Callbacks" ); + dumpNamespaceEntries( stream, g, true ); + dumpGroupEnd( stream ); + + g = g->mParent; + } + + // Now dump all the classes. + dumpClasses( stream ); + + // Dump pre-declarations for any groups we encountered + // so that we don't have to explicitly define them. + HashTable::Iterator iter = smDocGroups.begin(); + for ( ; iter != smDocGroups.end(); iter++ ) + stream.writeText( String::ToString( "/*! @addtogroup %s */\r\n\r\n", iter->key.c_str() ) ); + + return true; +} + +DefineEngineFunction( dumpEngineDocs, bool, ( const char* outputFile ),, + "Dumps the engine scripting documentation to the specified file overwriting any existing content.\n" + "@param outputFile The relative or absolute output file path and name.\n" + "@return Returns true if successful.\n" + "@ingroup Console") +{ + return dumpEngineDocs( outputFile ); +} + diff --git a/Engine/source/console/engineExports.cpp b/Engine/source/console/engineExports.cpp new file mode 100644 index 000000000..03530c8e2 --- /dev/null +++ b/Engine/source/console/engineExports.cpp @@ -0,0 +1,84 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/engineExports.h" +#include "console/engineTypeInfo.h" +#include "console/engineAPI.h" + + +IMPLEMENT_SCOPE( ReflectionAPI, Reflection,, + "Metadata for the exported engine API." ); + +IMPLEMENT_NONINSTANTIABLE_CLASS( EngineExport, + "Abstract base class of entities exported through the engine API." ) +END_IMPLEMENT_CLASS; +IMPLEMENT_NONINSTANTIABLE_CLASS( EngineExportScope, + "A scope contained a collection of exported engine API entities." ) +END_IMPLEMENT_CLASS; + +EngineExportScope EngineExportScope::smGlobalScope; + + +//----------------------------------------------------------------------------- + +EngineExport::EngineExport( const char* name, EngineExportKind kind, EngineExportScope* scope, const char* docString ) + : mExportName( name ), + mExportKind( kind ), + mExportScope( scope ), + mNextExport( NULL ), + mDocString( docString ) +{ + AssertFatal( name != NULL, "EngineExport - export without name!" ); + AssertFatal( scope != NULL, avar( "EngineExport - export '%s' is in no scope" ) ); + + // Link to scope's export chain. + + mNextExport = scope->mExports; + scope->mExports = this; +} + +//----------------------------------------------------------------------------- + +String EngineExport::getFullyQualifiedExportName() const +{ + if( getExportScope() ) + { + String parentQualifiedName = getExportScope()->getFullyQualifiedExportName(); + if( parentQualifiedName.isEmpty() ) + return getExportName(); + + return String::ToString( "%s::%s", parentQualifiedName.c_str(), getExportName() ); + } + + return getExportName(); +} + +//----------------------------------------------------------------------------- + +EngineExportScope::EngineExportScope( const char* name, EngineExportScope* scope, const char* docString ) + : SuperType( name, EngineExportKindScope, scope, docString ) +{ + // Do *NOT* initialize mExports here. EngineExportScopes should be + // instantiated globally and by not initializing the field, we allow + // exports to link themselves to their scope without being order dependent + // on our constructor. +} diff --git a/Engine/source/console/engineExports.h b/Engine/source/console/engineExports.h new file mode 100644 index 000000000..7659eeac9 --- /dev/null +++ b/Engine/source/console/engineExports.h @@ -0,0 +1,174 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _ENGINEEXPORTS_H_ +#define _ENGINEEXPORTS_H_ + +#ifndef _ENGINEOBJECT_H_ + #include "console/engineObject.h" +#endif + + +/// @file +/// Foundation for the engine API export system. +/// +/// The engine DLL exposes a well-defined API that the control layer can +/// use to interface with the engine. The structure of this API is accessible +/// through +/// +/// The system is primarily meant to allow mechanical extraction and processing +/// of the API. It is not meant to be used as a direct means to actually interface +/// with the engine. +/// +/// All export classes are themselves EngineObjects so they can be used in the +/// API. They are, however, all declared as non-instantiable classes. + + +class EngineExportScope; + + +DECLARE_SCOPE( ReflectionAPI ); + + +/// Kind of entity being exported. +enum EngineExportKind +{ + EngineExportKindScope, ///< A collection of exports grouped in a separate named scope. + EngineExportKindFunction, ///< A function call across the interop border going either in or out. + EngineExportKindType ///< A data type for data exchange between the engine and its control layer. Note that types are also scopes. +}; + + +/// Information about an entity exported by the engine API. This is an abstract base +/// class. +class EngineExport : public StaticEngineObject +{ + public: + + DECLARE_ABSTRACT_CLASS( EngineExport, StaticEngineObject ); + DECLARE_INSCOPE( ReflectionAPI ); + friend class EngineExportScope; // Default constructor. + + protected: + + /// Name of the export. Never NULL but will be an empty string for anonymous + /// exports such as function types. + const char* mExportName; + + /// Kind of export. + EngineExportKind mExportKind; + + /// The scope in which this export is defined. + EngineExportScope* mExportScope; + + /// Documentation string. + const char* mDocString; + + /// Next export in the link chain of the export's scope. + EngineExport* mNextExport; + + /// Protected constructor as this is an abstract class. + /// + /// @param name Export name. + /// @param kind Export kind. + /// @param scope Scope to export to. + /// @param docString Documentation string. + EngineExport( const char* name, EngineExportKind kind, EngineExportScope* scope, const char* docString ); + + public: + + /// Return the name of the export. + const char* getExportName() const { return mExportName; } + + /// Return the fully qualified name of this export starting from the global export scope. + /// Qualifiers are separated with "::". + String getFullyQualifiedExportName() const; + + /// Return the kind of entity being exported. + EngineExportKind getExportKind() const { return mExportKind; } + + /// Return the scope that contains this export. All exports except the global scope + /// itself are contained in a scope. + EngineExportScope* getExportScope() const { return mExportScope; } + + /// Get the next export in the link chain of the export's associated scope. + EngineExport* getNextExport() const { return mNextExport; } + + /// Return the documentation string for this type. + const char* getDocString() const { return mDocString; } + + private: + + /// Special constructor for the global scope instance. + EngineExport() + : mExportName( "" ), + mExportKind( EngineExportKindScope ), + mExportScope( NULL ), + mNextExport( NULL ) {} +}; + + +/// A group of engine exports. +class EngineExportScope : public EngineExport +{ + public: + + DECLARE_CLASS( EngineExportScope, EngineExport ); + friend class EngineExport; // mExports + friend struct _GLOBALSCOPE; // smGlobalScope + template< typename T > friend T* constructInPlace( T* ); + + protected: + + /// Head of the link chain of exports for this scope. + EngineExport* mExports; + + /// The global export scope singleton. + static EngineExportScope smGlobalScope; + + public: + + /// Construct a new export scope. + /// + /// @param name Name of the scope inside its parent scope. + /// @param scope Parent scope. + /// @param docString Documentation string. + EngineExportScope( const char* name, EngineExportScope* scope, const char* docString ); + + /// Return the global export scope singleton. This is the root of the + /// export hierarchy and thus directly or indirectly contains all + /// entities exported by the engine. + static EngineExportScope* getGlobalScope() { return &smGlobalScope; } + + /// Return the chain of exports associated with this scope. + EngineExport* getExports() const { return mExports; } + + private: + + /// Constructor for the global scope. + EngineExportScope() {} +}; + + +/// @} + +#endif // !_ENGINEEXPORTS_H_ diff --git a/Engine/source/console/engineFunctions.cpp b/Engine/source/console/engineFunctions.cpp new file mode 100644 index 000000000..09b9b4099 --- /dev/null +++ b/Engine/source/console/engineFunctions.cpp @@ -0,0 +1,65 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/engineFunctions.h" + + +IMPLEMENT_NONINSTANTIABLE_CLASS( EngineFunctionInfo, + "Information about an engine function." ) +END_IMPLEMENT_CLASS; + + +EngineFunctionInfo* EngineFunctionInfo::smFirstFunction; + + +//----------------------------------------------------------------------------- + +EngineFunctionInfo::EngineFunctionInfo( const char* name, + EngineExportScope* scope, + const char* docString, + const char* prototypeString, + const char* bindingName, + const EngineTypeInfo* functionType, + const EngineFunctionDefaultArguments* defaultArgs, + void* address, + U32 flags ) + : SuperType( name, EngineExportKindFunction, scope, docString ), + mBindingName( bindingName ), + mFunctionType( functionType ), + mDefaultArgumentValues( defaultArgs ), + mFunctionFlags( flags ), + mPrototypeString( prototypeString ), + mNextFunction( smFirstFunction ), + mAddress( address ) +{ + AssertFatal( functionType, "EngineFunctionInfo - Function cannot have void type!" ); + smFirstFunction = this; +} + +//----------------------------------------------------------------------------- + +void EngineFunctionInfo::resetAllCallouts() +{ + for( EngineFunctionInfo* function = smFirstFunction; function != NULL; function = function->mNextFunction ) + if( function->isCallout() ) + *reinterpret_cast< void** >( function->mAddress ) = NULL; +} diff --git a/Engine/source/console/engineFunctions.h b/Engine/source/console/engineFunctions.h new file mode 100644 index 000000000..8941e75ce --- /dev/null +++ b/Engine/source/console/engineFunctions.h @@ -0,0 +1,985 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _ENGINEFUNCTIONS_H_ +#define _ENGINEFUNCTIONS_H_ + +#ifndef _ENGINEEXPORTS_H_ + #include "console/engineExports.h" +#endif +#ifndef _ENGINETYPEINFO_H_ + #include "console/engineTypeInfo.h" +#endif + + +/// @file +/// Structures for function-type engine export information. + + +#ifdef TORQUE_COMPILER_VISUALC + #define TORQUE_API extern "C" __declspec( dllexport ) +#elif defined( TORQUE_COMPILER_GCC ) + #define TORQUE_API extern "C" __attribute__( ( visibility( "default" ) ) ) +#else + #error Unsupported compiler. +#endif + + +// #pragma pack is bugged in GCC in that the packing in place at the template instantiation +// sites rather than their definition sites is used. Enable workarounds. +#ifdef TORQUE_COMPILER_GCC + #define _PACK_BUG_WORKAROUNDS +#endif + + + +/// Structure storing the default argument values for a function invocation +/// frame. +struct EngineFunctionDefaultArguments +{ + /// Number of default arguments for the function call frame. + /// + /// @warn This is @b NOT the size of the memory block returned by getArgs() and also + /// not the number of elements it contains. + U32 mNumDefaultArgs; + + /// Return a pointer to the variable-sized array of default argument values. + /// + /// @warn The arguments must be stored @b IMMEDIATELY after #mNumDefaultArgs. + /// @warn This is a @b FULL frame and not just the default arguments, i.e. it starts with the + /// first argument that the function takes and ends with the last argument it takes. + /// @warn If the compiler's #pragma pack is buggy, the elements in this structure are allowed + /// to be 4-byte aligned rather than byte-aligned as they should be. + const U8* getArgs() const + { + return ( const U8* ) &( mNumDefaultArgs ) + sizeof( mNumDefaultArgs ); + } +}; + + +// Need byte-aligned packing for the default argument structures. +#pragma pack( push, 1 ) + + +// Structure encapsulating default arguments to an engine API function. +template< typename T > +struct _EngineFunctionDefaultArguments {}; +template<> +struct _EngineFunctionDefaultArguments< void() > : public EngineFunctionDefaultArguments +{ + _EngineFunctionDefaultArguments() + { mNumDefaultArgs = 0; } +}; +template< typename A > +struct _EngineFunctionDefaultArguments< void( A ) > : public EngineFunctionDefaultArguments +{ + typename EngineTypeTraits< A >::DefaultArgumentValueStoreType a; + + _EngineFunctionDefaultArguments() + { mNumDefaultArgs = 0; } + _EngineFunctionDefaultArguments( A a ) + : a( a ) + { mNumDefaultArgs = 1; } +}; +template< typename A, typename B > +struct _EngineFunctionDefaultArguments< void( A, B ) > : public EngineFunctionDefaultArguments +{ + typename EngineTypeTraits< A >::DefaultArgumentValueStoreType a; + typename EngineTypeTraits< B >::DefaultArgumentValueStoreType b; + + _EngineFunctionDefaultArguments() + { mNumDefaultArgs = 0; } + _EngineFunctionDefaultArguments( B b ) + : b( b ) + { mNumDefaultArgs = 1; } + _EngineFunctionDefaultArguments( A a, B b ) + : a( a ), + b( b ) + { mNumDefaultArgs = 2; } +}; +template< typename A, typename B, typename C > +struct _EngineFunctionDefaultArguments< void( A, B, C ) > : public EngineFunctionDefaultArguments +{ + typename EngineTypeTraits< A >::DefaultArgumentValueStoreType a; + typename EngineTypeTraits< B >::DefaultArgumentValueStoreType b; + typename EngineTypeTraits< C >::DefaultArgumentValueStoreType c; + + _EngineFunctionDefaultArguments() + { mNumDefaultArgs = 0; } + _EngineFunctionDefaultArguments( C c ) + : c( c ) + { mNumDefaultArgs = 1; } + _EngineFunctionDefaultArguments( B b, C c ) + : b( b ), + c( c ) + { mNumDefaultArgs = 2; } + _EngineFunctionDefaultArguments( A a, B b, C c ) + : a( a ), + b( b ), + c( c ) + { mNumDefaultArgs = 3; } +}; +template< typename A, typename B, typename C, typename D > +struct _EngineFunctionDefaultArguments< void( A, B, C, D ) > : public EngineFunctionDefaultArguments +{ + typename EngineTypeTraits< A >::DefaultArgumentValueStoreType a; + typename EngineTypeTraits< B >::DefaultArgumentValueStoreType b; + typename EngineTypeTraits< C >::DefaultArgumentValueStoreType c; + typename EngineTypeTraits< D >::DefaultArgumentValueStoreType d; + + _EngineFunctionDefaultArguments() + { mNumDefaultArgs = 0; } + _EngineFunctionDefaultArguments( D d ) + : d( d ) + { mNumDefaultArgs = 1; } + _EngineFunctionDefaultArguments( C c, D d ) + : c( c ), + d( d ) + { mNumDefaultArgs = 2; } + _EngineFunctionDefaultArguments( B b, C c, D d ) + : b( b ), + c( c ), + d( d ) + { mNumDefaultArgs = 3; } + _EngineFunctionDefaultArguments( A a, B b, C c, D d ) + : a( a ), + b( b ), + c( c ), + d( d ) + { mNumDefaultArgs = 4; } +}; +template< typename A, typename B, typename C, typename D, typename E > +struct _EngineFunctionDefaultArguments< void( A, B, C, D, E ) > : public EngineFunctionDefaultArguments +{ + typename EngineTypeTraits< A >::DefaultArgumentValueStoreType a; + typename EngineTypeTraits< B >::DefaultArgumentValueStoreType b; + typename EngineTypeTraits< C >::DefaultArgumentValueStoreType c; + typename EngineTypeTraits< D >::DefaultArgumentValueStoreType d; + typename EngineTypeTraits< E >::DefaultArgumentValueStoreType e; + + _EngineFunctionDefaultArguments() + { mNumDefaultArgs = 0; } + _EngineFunctionDefaultArguments( E e ) + : e( e ) + { mNumDefaultArgs = 1; } + _EngineFunctionDefaultArguments( D d, E e ) + : d( d ), + e( e ) + { mNumDefaultArgs = 2; } + _EngineFunctionDefaultArguments( C c, D d, E e ) + : c( c ), + d( d ), + e( e ) + { mNumDefaultArgs = 3; } + _EngineFunctionDefaultArguments( B b, C c, D d, E e ) + : b( b ), + c( c ), + d( d ), + e( e ) + { mNumDefaultArgs = 4; } + _EngineFunctionDefaultArguments( A a, B b, C c, D d, E e ) + : a( a ), + b( b ), + c( c ), + d( d ), + e( e ) + { mNumDefaultArgs = 5; } +}; +template< typename A, typename B, typename C, typename D, typename E, typename F > +struct _EngineFunctionDefaultArguments< void( A, B, C, D, E, F ) > : public EngineFunctionDefaultArguments +{ + typename EngineTypeTraits< A >::DefaultArgumentValueStoreType a; + typename EngineTypeTraits< B >::DefaultArgumentValueStoreType b; + typename EngineTypeTraits< C >::DefaultArgumentValueStoreType c; + typename EngineTypeTraits< D >::DefaultArgumentValueStoreType d; + typename EngineTypeTraits< E >::DefaultArgumentValueStoreType e; + typename EngineTypeTraits< F >::DefaultArgumentValueStoreType f; + + _EngineFunctionDefaultArguments() + { mNumDefaultArgs = 0; } + _EngineFunctionDefaultArguments( F f ) + : f( f ) + { mNumDefaultArgs = 1; } + _EngineFunctionDefaultArguments( E e, F f ) + : e( e ), + f( f ) + { mNumDefaultArgs = 2; } + _EngineFunctionDefaultArguments( D d, E e, F f ) + : d( d ), + e( e ), + f( f ) + { mNumDefaultArgs = 3; } + _EngineFunctionDefaultArguments( C c, D d, E e, F f ) + : c( c ), + d( d ), + e( e ), + f( f ) + { mNumDefaultArgs = 4; } + _EngineFunctionDefaultArguments( B b, C c, D d, E e, F f ) + : b( b ), + c( c ), + d( d ), + e( e ), + f( f ) + { mNumDefaultArgs = 5; } + _EngineFunctionDefaultArguments( A a, B b, C c, D d, E e, F f ) + : a( a ), + b( b ), + c( c ), + d( d ), + e( e ), + f( f ) + { mNumDefaultArgs = 6; } +}; +template< typename A, typename B, typename C, typename D, typename E, typename F, typename G > +struct _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G ) > : public EngineFunctionDefaultArguments +{ + typename EngineTypeTraits< A >::DefaultArgumentValueStoreType a; + typename EngineTypeTraits< B >::DefaultArgumentValueStoreType b; + typename EngineTypeTraits< C >::DefaultArgumentValueStoreType c; + typename EngineTypeTraits< D >::DefaultArgumentValueStoreType d; + typename EngineTypeTraits< E >::DefaultArgumentValueStoreType e; + typename EngineTypeTraits< F >::DefaultArgumentValueStoreType f; + typename EngineTypeTraits< G >::DefaultArgumentValueStoreType g; + + _EngineFunctionDefaultArguments() + { mNumDefaultArgs = 0; } + _EngineFunctionDefaultArguments( G g ) + : g( g ) + { mNumDefaultArgs = 1; } + _EngineFunctionDefaultArguments( F f, G g ) + : f( f ), + g( g ) + { mNumDefaultArgs = 2; } + _EngineFunctionDefaultArguments( E e, F f, G g ) + : e( e ), + f( f ), + g( g ) + { mNumDefaultArgs = 3; } + _EngineFunctionDefaultArguments( D d, E e, F f, G g ) + : d( d ), + e( e ), + f( f ), + g( g ) + { mNumDefaultArgs = 4; } + _EngineFunctionDefaultArguments( C c, D d, E e, F f, G g ) + : c( c ), + d( d ), + e( e ), + f( f ), + g( g ) + { mNumDefaultArgs = 5; } + _EngineFunctionDefaultArguments( B b, C c, D d, E e, F f, G g ) + : b( b ), + c( c ), + d( d ), + e( e ), + f( f ), + g( g ) + { mNumDefaultArgs = 6; } + _EngineFunctionDefaultArguments( A a, B b, C c, D d, E e, F f, G g ) + : a( a ), + b( b ), + c( c ), + d( d ), + e( e ), + f( f ), + g( g ) + { mNumDefaultArgs = 7; } +}; +template< typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H > +struct _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H ) > : public EngineFunctionDefaultArguments +{ + typename EngineTypeTraits< A >::DefaultArgumentValueStoreType a; + typename EngineTypeTraits< B >::DefaultArgumentValueStoreType b; + typename EngineTypeTraits< C >::DefaultArgumentValueStoreType c; + typename EngineTypeTraits< D >::DefaultArgumentValueStoreType d; + typename EngineTypeTraits< E >::DefaultArgumentValueStoreType e; + typename EngineTypeTraits< F >::DefaultArgumentValueStoreType f; + typename EngineTypeTraits< G >::DefaultArgumentValueStoreType g; + typename EngineTypeTraits< H >::DefaultArgumentValueStoreType h; + + _EngineFunctionDefaultArguments() + { mNumDefaultArgs = 0; } + _EngineFunctionDefaultArguments( H h ) + : h( h ) + { mNumDefaultArgs = 1; } + _EngineFunctionDefaultArguments( G g, H h ) + : g( g ), + h( h ) + { mNumDefaultArgs = 2; } + _EngineFunctionDefaultArguments( F f, G g, H h ) + : f( f ), + g( g ), + h( h ) + { mNumDefaultArgs = 3; } + _EngineFunctionDefaultArguments( E e, F f, G g, H h ) + : e( e ), + f( f ), + g( g ), + h( h ) + { mNumDefaultArgs = 4; } + _EngineFunctionDefaultArguments( D d, E e, F f, G g, H h ) + : d( d ), + e( e ), + f( f ), + g( g ), + h( h ) + { mNumDefaultArgs = 5; } + _EngineFunctionDefaultArguments( C c, D d, E e, F f, G g, H h ) + : c( c ), + d( d ), + e( e ), + f( f ), + g( g ), + h( h ) + { mNumDefaultArgs = 6; } + _EngineFunctionDefaultArguments( B b, C c, D d, E e, F f, G g, H h ) + : b( b ), + c( c ), + d( d ), + e( e ), + f( f ), + g( g ), + h( h ) + { mNumDefaultArgs = 7; } + _EngineFunctionDefaultArguments( A a, B b, C c, D d, E e, F f, G g, H h ) + : a( a ), + b( b ), + c( c ), + d( d ), + e( e ), + f( f ), + g( g ), + h( h ) + { mNumDefaultArgs = 8; } +}; +template< typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I > +struct _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H, I ) > : public EngineFunctionDefaultArguments +{ + typename EngineTypeTraits< A >::DefaultArgumentValueStoreType a; + typename EngineTypeTraits< B >::DefaultArgumentValueStoreType b; + typename EngineTypeTraits< C >::DefaultArgumentValueStoreType c; + typename EngineTypeTraits< D >::DefaultArgumentValueStoreType d; + typename EngineTypeTraits< E >::DefaultArgumentValueStoreType e; + typename EngineTypeTraits< F >::DefaultArgumentValueStoreType f; + typename EngineTypeTraits< G >::DefaultArgumentValueStoreType g; + typename EngineTypeTraits< H >::DefaultArgumentValueStoreType h; + typename EngineTypeTraits< I >::DefaultArgumentValueStoreType i; + + _EngineFunctionDefaultArguments() + { mNumDefaultArgs = 0; } + _EngineFunctionDefaultArguments( I i ) + : i( i ) + { mNumDefaultArgs = 1; } + _EngineFunctionDefaultArguments( H h, I i ) + : h( h ), + i( i ) + { mNumDefaultArgs = 2; } + _EngineFunctionDefaultArguments( G g, H h, I i ) + : g( g ), + h( h ), + i( i ) + { mNumDefaultArgs = 3; } + _EngineFunctionDefaultArguments( F f, G g, H h, I i ) + : f( f ), + g( g ), + h( h ), + i( i ) + { mNumDefaultArgs = 4; } + _EngineFunctionDefaultArguments( E e, F f, G g, H h, I i ) + : e( e ), + f( f ), + g( g ), + h( h ), + i( i ) + { mNumDefaultArgs = 5; } + _EngineFunctionDefaultArguments( D d, E e, F f, G g, H h, I i ) + : d( d ), + e( e ), + f( f ), + g( g ), + h( h ), + i( i ) + { mNumDefaultArgs = 6; } + _EngineFunctionDefaultArguments( C c, D d, E e, F f, G g, H h, I i ) + : c( c ), + d( d ), + e( e ), + f( f ), + g( g ), + h( h ), + i( i ) + { mNumDefaultArgs = 7; } + _EngineFunctionDefaultArguments( B b, C c, D d, E e, F f, G g, H h, I i ) + : b( b ), + c( c ), + d( d ), + e( e ), + f( f ), + g( g ), + h( h ), + i( i ) + { mNumDefaultArgs = 8; } + _EngineFunctionDefaultArguments( A a, B b, C c, D d, E e, F f, G g, H h, I i ) + : a( a ), + b( b ), + c( c ), + d( d ), + e( e ), + f( f ), + g( g ), + h( h ), + i( i ) + { mNumDefaultArgs = 9; } +}; +template< typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J > +struct _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H, I, J ) > : public EngineFunctionDefaultArguments +{ + typename EngineTypeTraits< A >::DefaultArgumentValueStoreType a; + typename EngineTypeTraits< B >::DefaultArgumentValueStoreType b; + typename EngineTypeTraits< C >::DefaultArgumentValueStoreType c; + typename EngineTypeTraits< D >::DefaultArgumentValueStoreType d; + typename EngineTypeTraits< E >::DefaultArgumentValueStoreType e; + typename EngineTypeTraits< F >::DefaultArgumentValueStoreType f; + typename EngineTypeTraits< G >::DefaultArgumentValueStoreType g; + typename EngineTypeTraits< H >::DefaultArgumentValueStoreType h; + typename EngineTypeTraits< I >::DefaultArgumentValueStoreType i; + typename EngineTypeTraits< J >::DefaultArgumentValueStoreType j; + + _EngineFunctionDefaultArguments() + { mNumDefaultArgs = 0; } + _EngineFunctionDefaultArguments( J j ) + : j( j ) + { mNumDefaultArgs = 1; } + _EngineFunctionDefaultArguments( I i, J j ) + : i( i ), + j( j ) + { mNumDefaultArgs = 2; } + _EngineFunctionDefaultArguments( H h, I i, J j ) + : h( h ), + i( i ), + j( j ) + { mNumDefaultArgs = 3; } + _EngineFunctionDefaultArguments( G g, H h, I i, J j ) + : g( g ), + h( h ), + i( i ), + j( j ) + { mNumDefaultArgs = 4; } + _EngineFunctionDefaultArguments( F f, G g, H h, I i, J j ) + : f( f ), + g( g ), + h( h ), + i( i ), + j( j ) + { mNumDefaultArgs = 5; } + _EngineFunctionDefaultArguments( E e, F f, G g, H h, I i, J j ) + : e( e ), + f( f ), + g( g ), + h( h ), + i( i ), + j( j ) + { mNumDefaultArgs = 6; } + _EngineFunctionDefaultArguments( D d, E e, F f, G g, H h, I i, J j ) + : d( d ), + e( e ), + f( f ), + g( g ), + h( h ), + i( i ), + j( j ) + { mNumDefaultArgs = 7; } + _EngineFunctionDefaultArguments( C c, D d, E e, F f, G g, H h, I i, J j ) + : c( c ), + d( d ), + e( e ), + f( f ), + g( g ), + h( h ), + i( i ), + j( j ) + { mNumDefaultArgs = 8; } + _EngineFunctionDefaultArguments( B b, C c, D d, E e, F f, G g, H h, I i, J j ) + : b( b ), + c( c ), + d( d ), + e( e ), + f( f ), + g( g ), + h( h ), + i( i ), + j( j ) + { mNumDefaultArgs = 9; } + _EngineFunctionDefaultArguments( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j ) + : a( a ), + b( b ), + c( c ), + d( d ), + e( e ), + f( f ), + g( g ), + h( h ), + i( i ), + j( j ) + { mNumDefaultArgs = 10; } +}; +template< typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K > +struct _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H, I, J, K ) > : public EngineFunctionDefaultArguments +{ + typename EngineTypeTraits< A >::DefaultArgumentValueStoreType a; + typename EngineTypeTraits< B >::DefaultArgumentValueStoreType b; + typename EngineTypeTraits< C >::DefaultArgumentValueStoreType c; + typename EngineTypeTraits< D >::DefaultArgumentValueStoreType d; + typename EngineTypeTraits< E >::DefaultArgumentValueStoreType e; + typename EngineTypeTraits< F >::DefaultArgumentValueStoreType f; + typename EngineTypeTraits< G >::DefaultArgumentValueStoreType g; + typename EngineTypeTraits< H >::DefaultArgumentValueStoreType h; + typename EngineTypeTraits< I >::DefaultArgumentValueStoreType i; + typename EngineTypeTraits< J >::DefaultArgumentValueStoreType j; + typename EngineTypeTraits< K >::DefaultArgumentValueStoreType k; + + _EngineFunctionDefaultArguments() + { mNumDefaultArgs = 0; } + _EngineFunctionDefaultArguments( K k ) + : k( k ) + { mNumDefaultArgs = 1; } + _EngineFunctionDefaultArguments( J j, K k ) + : j( j ), + k( k ) + { mNumDefaultArgs = 2; } + _EngineFunctionDefaultArguments( I i, J j, K k ) + : i( i ), + j( j ), + k( k ) + { mNumDefaultArgs = 3; } + _EngineFunctionDefaultArguments( H h, I i, J j, K k ) + : h( h ), + i( i ), + j( j ), + k( k ) + { mNumDefaultArgs = 4; } + _EngineFunctionDefaultArguments( G g, H h, I i, J j, K k ) + : g( g ), + h( h ), + i( i ), + j( j ), + k( k ) + { mNumDefaultArgs = 5; } + _EngineFunctionDefaultArguments( F f, G g, H h, I i, J j, K k ) + : f( f ), + g( g ), + h( h ), + i( i ), + j( j ), + k( k ) + { mNumDefaultArgs = 6; } + _EngineFunctionDefaultArguments( E e, F f, G g, H h, I i, J j, K k ) + : e( e ), + f( f ), + g( g ), + h( h ), + i( i ), + j( j ), + k( k ) + { mNumDefaultArgs = 7; } + _EngineFunctionDefaultArguments( D d, E e, F f, G g, H h, I i, J j, K k ) + : d( d ), + e( e ), + f( f ), + g( g ), + h( h ), + i( i ), + j( j ), + k( k ) + { mNumDefaultArgs = 8; } + _EngineFunctionDefaultArguments( C c, D d, E e, F f, G g, H h, I i, J j, K k ) + : c( c ), + d( d ), + e( e ), + f( f ), + g( g ), + h( h ), + i( i ), + j( j ), + k( k ) + { mNumDefaultArgs = 9; } + _EngineFunctionDefaultArguments( B b, C c, D d, E e, F f, G g, H h, I i, J j, K k ) + : b( b ), + c( c ), + d( d ), + e( e ), + f( f ), + g( g ), + h( h ), + i( i ), + j( j ), + k( k ) + { mNumDefaultArgs = 10; } + _EngineFunctionDefaultArguments( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k ) + : a( a ), + b( b ), + c( c ), + d( d ), + e( e ), + f( f ), + g( g ), + h( h ), + i( i ), + j( j ), + k( k ) + { mNumDefaultArgs = 11; } +}; +template< typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K, typename L > +struct _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H, I, J, K, L ) > : public EngineFunctionDefaultArguments +{ + typename EngineTypeTraits< A >::DefaultArgumentValueStoreType a; + typename EngineTypeTraits< B >::DefaultArgumentValueStoreType b; + typename EngineTypeTraits< C >::DefaultArgumentValueStoreType c; + typename EngineTypeTraits< D >::DefaultArgumentValueStoreType d; + typename EngineTypeTraits< E >::DefaultArgumentValueStoreType e; + typename EngineTypeTraits< F >::DefaultArgumentValueStoreType f; + typename EngineTypeTraits< G >::DefaultArgumentValueStoreType g; + typename EngineTypeTraits< H >::DefaultArgumentValueStoreType h; + typename EngineTypeTraits< I >::DefaultArgumentValueStoreType i; + typename EngineTypeTraits< J >::DefaultArgumentValueStoreType j; + typename EngineTypeTraits< K >::DefaultArgumentValueStoreType k; + typename EngineTypeTraits< L >::DefaultArgumentValueStoreType l; + + _EngineFunctionDefaultArguments() + { mNumDefaultArgs = 0; } + _EngineFunctionDefaultArguments( L l ) + : l( l ) + { mNumDefaultArgs = 1; } + _EngineFunctionDefaultArguments( K k, L l ) + : k( k ), + l( l ) + { mNumDefaultArgs = 2; } + _EngineFunctionDefaultArguments( J j, K k, L l ) + : j( j ), + k( k ), + l( l ) + { mNumDefaultArgs = 3; } + _EngineFunctionDefaultArguments( I i, J j, K k, L l ) + : i( i ), + j( j ), + k( k ), + l( l ) + { mNumDefaultArgs = 4; } + _EngineFunctionDefaultArguments( H h, I i, J j, K k, L l ) + : h( h ), + i( i ), + j( j ), + k( k ), + l( l ) + { mNumDefaultArgs = 5; } + _EngineFunctionDefaultArguments( G g, H h, I i, J j, K k, L l ) + : g( g ), + h( h ), + i( i ), + j( j ), + k( k ), + l( l ) + { mNumDefaultArgs = 6; } + _EngineFunctionDefaultArguments( F f, G g, H h, I i, J j, K k, L l ) + : f( f ), + g( g ), + h( h ), + i( i ), + j( j ), + k( k ), + l( l ) + { mNumDefaultArgs = 7; } + _EngineFunctionDefaultArguments( E e, F f, G g, H h, I i, J j, K k, L l ) + : e( e ), + f( f ), + g( g ), + h( h ), + i( i ), + j( j ), + k( k ), + l( l ) + { mNumDefaultArgs = 8; } + _EngineFunctionDefaultArguments( D d, E e, F f, G g, H h, I i, J j, K k, L l ) + : d( d ), + e( e ), + f( f ), + g( g ), + h( h ), + i( i ), + j( j ), + k( k ), + l( l ) + { mNumDefaultArgs = 9; } + _EngineFunctionDefaultArguments( C c, D d, E e, F f, G g, H h, I i, J j, K k, L l ) + : c( c ), + d( d ), + e( e ), + f( f ), + g( g ), + h( h ), + i( i ), + j( j ), + k( k ), + l( l ) + { mNumDefaultArgs = 10; } + _EngineFunctionDefaultArguments( B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l ) + : b( b ), + c( c ), + d( d ), + e( e ), + f( f ), + g( g ), + h( h ), + i( i ), + j( j ), + k( k ), + l( l ) + { mNumDefaultArgs = 11; } + _EngineFunctionDefaultArguments( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l ) + : a( a ), + b( b ), + c( c ), + d( d ), + e( e ), + f( f ), + g( g ), + h( h ), + i( i ), + j( j ), + k( k ), + l( l ) + { mNumDefaultArgs = 12; } +}; + +#pragma pack( pop ) + + +// Helper to allow flags argument to DEFINE_FUNCTION to be empty. +struct _EngineFunctionFlags +{ + U32 val; + _EngineFunctionFlags() + : val( 0 ) {} + _EngineFunctionFlags( U32 val ) + : val( val ) {} + operator U32() const { return val; } +}; + + +/// +enum EngineFunctionFlags +{ + /// Function is a callback into the control layer. If this flag is not set, + /// the function is a call-in. + EngineFunctionCallout = BIT( 0 ), +}; + + +/// A function exported by the engine for interfacing with the control layer. +/// +/// A function can either be a call-in, transfering control flow from the control layer to the engine, or a call-out, +/// transfering control flow from the engine to the control layer. +/// +/// All engine API functions use the native C (@c cdecl) calling convention. +/// +/// Be aware that there a no implicit parameters to functions. This, for example, means that methods will simply +/// list an object type parameter as their first argument but otherwise be indistinguishable from other functions. +/// +/// Variadic functions are supported. +/// +/// @section engineFunction_strings String Arguments and Return Values +/// +/// Strings passed through the API are assumed to be owned by the caller. They must persist for the entire duration +/// of a call. +/// +/// Strings returned by a function are assumed to be in transient storage that will be overwritten by subsequent API +/// calls. If the caller wants to preserve a string, it is responsible to copying strings to its own memory. This will +/// happen with most higher-level control layers anyway. +/// +/// @section engineFunction_defaultargs Default Arguments +/// +/// As the engine API export system is set up to not require hand-written code in generated wrappers per se, the +/// export system seeks to include a maximum possible amount of information in the export structures. +/// To this end, where applicable, information about suggested default values for arguments to the engine API +/// functions is stored in the export structures. It is up to the wrapper generator if and how it makes use of +/// this information. +/// +/// Default arguments are represented by capturing raw stack frame vectors of the arguments to functions. These +/// frames could be used as default images for passing arguments in stack frames, though wrapper generators +/// may actually want to read out individual argument values and include them in function prototypes within +/// the generated code. +/// +/// @section engineFunction_callin Call-ins +/// +/// Call-ins are exposed as native entry points. The control layer must be able to natively +/// marshall arguments and call DLL function exports using C calling conventions. +/// +/// @section engineFunction_callout Call-outs +/// +/// Call-outs are exposed as pointer-sized memory locations into which the control layer needs +/// to install addresses of functions that receive the call from the engine back into the control +/// layer. The function has to follow C calling conventions and +/// +/// A call-out will initially be set to NULL and while being NULL, will simply cause the engine +/// to skip and ignore the call-out. This allows the control layer to only install call-outs +/// it is actually interested in. +/// +class EngineFunctionInfo : public EngineExport +{ + public: + + DECLARE_CLASS( EngineFunctionInfo, EngineExport ); + + protected: + + /// A combination of EngineFunctionFlags. + BitSet32 mFunctionFlags; + + /// The type of the function. + const EngineTypeInfo* mFunctionType; + + /// Default values for the function arguments. + const EngineFunctionDefaultArguments* mDefaultArgumentValues; + + /// Name of the DLL symbol denoting the address of the exported entity. + const char* mBindingName; + + /// Full function prototype string. Useful for quick printing and most importantly, + /// this will be the only place containing information about the argument names. + const char* mPrototypeString; + + /// Address of either the function implementation or the variable taking the address + /// of a call-out. + void* mAddress; + + /// Next function in the global link chain of engine functions. + EngineFunctionInfo* mNextFunction; + + /// First function in the global link chain of engine functions. + static EngineFunctionInfo* smFirstFunction; + + public: + + /// + EngineFunctionInfo( const char* name, + EngineExportScope* scope, + const char* docString, + const char* protoypeString, + const char* bindingName, + const EngineTypeInfo* functionType, + const EngineFunctionDefaultArguments* defaultArgs, + void* address, + U32 flags ); + + /// Return the name of the function. + const char* getFunctionName() const { return getExportName(); } + + /// Return the function's full prototype string including the return type, function name, + /// and argument list. + const char* getPrototypeString() const { return mPrototypeString; } + + /// Return the DLL export symbol name. + const char* getBindingName() const { return mBindingName; } + + /// Test whether this is a callout function. + bool isCallout() const { return mFunctionFlags.test( EngineFunctionCallout ); } + + /// Test whether the function is variadic, i.e. takes a variable number of arguments. + bool isVariadic() const { return mFunctionType->isVariadic(); } + + /// Return the type of this function. + const EngineTypeInfo* getFunctionType() const { return mFunctionType; } + + /// Return the return type of the function. + const EngineTypeInfo* getReturnType() const { return getFunctionType()->getArgumentTypeTable()->getReturnType(); } + + /// Return the number of arguments that this function takes. If the function is variadic, + /// this is the number of fixed arguments. + U32 getNumArguments() const { return getFunctionType()->getArgumentTypeTable()->getNumArguments(); } + + /// + const EngineTypeInfo* getArgumentType( U32 index ) const { return ( *( getFunctionType()->getArgumentTypeTable() ) )[ index ]; } + + /// Return the vector storing the default argument values. + const EngineFunctionDefaultArguments* getDefaultArguments() const { return mDefaultArgumentValues; } + + /// Reset all callout function pointers back to NULL. This deactivates all callbacks. + static void resetAllCallouts(); +}; + + +/// +/// +/// Due to the given argument types and return type being directly used as is, it is not possible +/// to use this macro with engine types that have more complex value passing semantics (like e.g. +/// String). Use engineAPI in this case. +/// +/// @note The method of defining functions exposed by this macro is very low-level. To more +/// conveniently define API functions and methods, use the facilities provided in engineAPI.h. +/// +/// @see engineAPI.h +#define DEFINE_CALLIN( bindingName, exportName, scope, returnType, args, defaultArgs, flags, doc ) \ + TORQUE_API returnType bindingName args; \ + namespace { namespace _ ## bindingName { \ + _EngineFunctionDefaultArguments< void args > sDefaultArgs defaultArgs; \ + EngineFunctionInfo sFunctionInfo( \ + #exportName, \ + &_SCOPE< scope >()(), \ + doc, \ + #returnType " " #exportName #args, \ + #bindingName, \ + TYPE< returnType args >(), \ + &sDefaultArgs, \ + ( void* ) &bindingName, \ + _EngineFunctionFlags( flags ) \ + ); \ + } } \ + TORQUE_API returnType bindingName args + + +/// +/// +/// Not all control layers may be able to access data variables in a DLL so this macro exposes +/// both the variable and a set_XXX function to set the variable programmatically. +#define DEFINE_CALLOUT( bindingName, exportName, scope, returnType, args, flags, doc ) \ + TORQUE_API returnType ( *bindingName ) args; \ + TORQUE_API void set_ ## bindingName( returnType ( *fn ) args ) \ + { bindingName = fn; } \ + returnType ( *bindingName ) args; \ + namespace { \ + ::EngineFunctionInfo _cb ## bindingName( \ + #exportName, \ + &::_SCOPE< scope >()(), \ + doc, \ + #returnType " " #exportName #args, \ + #bindingName, \ + ::TYPE< returnType args >(), \ + NULL, \ + ( void* ) &bindingName, \ + EngineFunctionCallout | EngineFunctionFlags( flags ) \ + ); \ + } + + +#endif // !_ENGINEFUNCTIONS_H_ diff --git a/Engine/source/console/engineObject.cpp b/Engine/source/console/engineObject.cpp new file mode 100644 index 000000000..f82114388 --- /dev/null +++ b/Engine/source/console/engineObject.cpp @@ -0,0 +1,376 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/engineObject.h" +#include "console/engineAPI.h" +#include "platform/tmm_off.h" + + +IMPLEMENT_NONINSTANTIABLE_CLASS( EngineObject, + "Abstract base class for all objects exposed through the engine API." ) +END_IMPLEMENT_CLASS; +IMPLEMENT_NONINSTANTIABLE_CLASS( StaticEngineObject, + "Abstract base class for objects that are statically allocated in the engine." ) +END_IMPLEMENT_CLASS; + + +#ifdef TORQUE_DEBUG +U32 EngineObject::smNumEngineObjects; +EngineObject* EngineObject::smFirstEngineObject; +#endif + + +IEngineObjectPool* IEngineObjectPool::DEFAULT = EngineCRuntimeObjectPool::instance(); +EngineCRuntimeObjectPool EngineCRuntimeObjectPool::smInstance; + +// Helper to get to engine object's user data. Not exposed through get/set methods +// as the rest of the engine has no business accessing this data. It is for the +// exclusive use by the control layer. +void*& _USERDATA( EngineObject* object ) +{ + return object->mEngineObjectUserData; +} + +//----------------------------------------------------------------------------- + +EngineObject::EngineObject() + : mEngineObjectUserData( NULL ) +{ + #ifdef TORQUE_DEBUG + // Add to instance list. + + mNextEngineObject = smFirstEngineObject; + mPrevEngineObject = NULL; + + if( smFirstEngineObject ) + smFirstEngineObject->mPrevEngineObject = this; + smFirstEngineObject = this; + + smNumEngineObjects ++; + #endif +} + +//----------------------------------------------------------------------------- + +EngineObject::~EngineObject() +{ + #ifdef TORQUE_DEBUG + if( mPrevEngineObject ) + mPrevEngineObject->mNextEngineObject = mNextEngineObject; + else + smFirstEngineObject = mNextEngineObject; + + if( mNextEngineObject ) + mNextEngineObject->mPrevEngineObject = mPrevEngineObject; + + smNumEngineObjects --; + #endif +} + +//----------------------------------------------------------------------------- + +void EngineObject::destroySelf() +{ + if( !engineAPI::gUseConsoleInterop ) + AssertFatal( !getRefCount() || TYPEOF( this )->isDisposable(), "EngineObject::destroySelf - object still referenced!" ); + + // Call the internal _destroySelf(). + + _destroySelf(); + + // Destruct the object and release it in the pool. + + IEngineObjectPool* pool = this->mEngineObjectPool; + void* object = this; + + destructInPlace( this ); + if( pool ) + pool->freeObject( object ); +} + +//----------------------------------------------------------------------------- + +String EngineObject::describeSelf() const +{ + String desc = String::ToString( "class: %s", TYPEOF( this )->getFullyQualifiedExportName().c_str() ); + + return desc; +} + +//----------------------------------------------------------------------------- + +#ifndef TORQUE_DISABLE_MEMORY_MANAGER +void* EngineObject::operator new( size_t size ) +{ + AssertFatal( IEngineObjectPool::DEFAULT, "EngineObject::new - No default pool set!" ); + + void* ptr = IEngineObjectPool::DEFAULT->allocateObject( size TORQUE_TMM_LOC ); + if( !ptr ) + { + Platform::AlertOK( "Torque Memory Error", "Out of memory. Shutting down.\n"); + Platform::forceShutdown( -1 ); + } + + EngineObject* object = reinterpret_cast< EngineObject* >( ptr ); + object->mEngineObjectPool = IEngineObjectPool::DEFAULT; + + return ptr; +} +#endif + +//----------------------------------------------------------------------------- + +#ifndef TORQUE_DISABLE_MEMORY_MANAGER +void* EngineObject::operator new( size_t size, IEngineObjectPool* pool ) +{ + AssertFatal( pool, "EngineObject::new - Got a NULL pool pointer!" ); + + void* ptr = pool->allocateObject( size TORQUE_TMM_LOC ); + if( !ptr ) + { + // Fall back to default pool. + + pool = IEngineObjectPool::DEFAULT; + AssertFatal( pool, "EngineObject::new - No default pool set!" ); + ptr = pool->allocateObject( size TORQUE_TMM_LOC ); + + if( !ptr ) + { + Platform::AlertOK( "Torque Memory Error", "Out of memory. Shutting down.\n"); + Platform::forceShutdown( -1 ); + } + } + + EngineObject* object = reinterpret_cast< EngineObject* >( ptr ); + object->mEngineObjectPool = pool; + + return ptr; +} +#endif + +//----------------------------------------------------------------------------- + +void* EngineObject::operator new( size_t size TORQUE_TMM_ARGS_DECL ) +{ + AssertFatal( IEngineObjectPool::DEFAULT, "EngineObject::new - No default pool set!" ); + + void* ptr = IEngineObjectPool::DEFAULT->allocateObject( size TORQUE_TMM_ARGS ); + if( !ptr ) + { + Platform::AlertOK( "Torque Memory Error", "Out of memory. Shutting down.\n"); + Platform::forceShutdown( -1 ); + } + + EngineObject* object = reinterpret_cast< EngineObject* >( ptr ); + object->mEngineObjectPool = IEngineObjectPool::DEFAULT; + + return ptr; +} + +//----------------------------------------------------------------------------- + +void* EngineObject::operator new( size_t size, IEngineObjectPool* pool TORQUE_TMM_ARGS_DECL ) +{ + AssertFatal( pool, "EngineObject::new - Got a NULL pool pointer!" ); + + void* ptr = pool->allocateObject( size TORQUE_TMM_ARGS ); + if( !ptr ) + { + // Fall back to default pool. + + pool = IEngineObjectPool::DEFAULT; + AssertFatal( pool, "EngineObject::new - No default pool set!" ); + ptr = pool->allocateObject( size TORQUE_TMM_ARGS ); + + if( !ptr ) + { + Platform::AlertOK( "Torque Memory Error", "Out of memory. Shutting down.\n"); + Platform::forceShutdown( -1 ); + } + } + + EngineObject* object = reinterpret_cast< EngineObject* >( ptr ); + object->mEngineObjectPool = pool; + + return ptr; +} + +//----------------------------------------------------------------------------- + +void EngineObject::operator delete( void* ptr ) +{ + if( !ptr ) + return; + +// AssertWarn( false, "EngineObject::delete - Directly deleting an EngineObject is disallowed!" ); + + EngineObject* object = reinterpret_cast< EngineObject* >( ptr ); + AssertFatal( !object->getRefCount(), "EngineObject::delete - object still referenced!" ); + if( object->mEngineObjectPool ) + object->mEngineObjectPool->freeObject( object ); +} + +//----------------------------------------------------------------------------- + +#ifdef TORQUE_DEBUG + +void EngineObject::debugDumpInstances() +{ + Con::printf( "----------- Dumping EngineObjects ----------------" ); + for( EngineObject* object = smFirstEngineObject; object != NULL; object = object->mNextEngineObject ) + Con::printf( object->describeSelf() ); + Con::printf( "%i EngineObjects", smNumEngineObjects ); +} + +//----------------------------------------------------------------------------- + +void EngineObject::debugEnumInstances( const std::type_info& type, DebugEnumInstancesCallback callback ) +{ + for( EngineObject* object = smFirstEngineObject; object != NULL; object = object->mNextEngineObject ) + if( typeid( *object ) == type ) + { + // Set breakpoint here to break for each instance. + if( callback ) + callback( object ); + } +} + +//----------------------------------------------------------------------------- + +void EngineObject::debugEnumInstances( const char* className, DebugEnumInstancesCallback callback ) +{ + for( EngineObject* object = smFirstEngineObject; object != NULL; object = object->mNextEngineObject ) + { + for( const EngineTypeInfo* type = TYPEOF( object ); type != NULL; type = type->getSuperType() ) + if( dStricmp( type->getTypeName(), className ) == 0 ) + { + // Set breakpoint here to break for each instance. + if( callback ) + callback( object ); + + break; + } + } +} + +#endif // TORQUE_DEBUG + +//----------------------------------------------------------------------------- + +void* EngineCRuntimeObjectPool::allocateObject( U32 size TORQUE_TMM_ARGS_DECL ) +{ + #ifdef TORQUE_DISABLE_MEMORY_MANAGER + return dMalloc( size ); + #else + return dMalloc_r( size TORQUE_TMM_ARGS ); + #endif +} + +//----------------------------------------------------------------------------- + +void EngineCRuntimeObjectPool::freeObject( void* ptr ) +{ + dFree( ptr ); +} + +//----------------------------------------------------------------------------- + +StaticEngineObject::StaticEngineObject() +{ + mEngineObjectPool = NULL; + + // Add an artificial reference to the object. + incRefCount(); +} + +//----------------------------------------------------------------------------- + +void StaticEngineObject::destroySelf() +{ + AssertFatal( false, "StaticEngineObject::destroySelf - Cannot destroy static object!" ); +} + +//============================================================================= +// API. +//============================================================================= +// MARK: ---- API ---- + +//----------------------------------------------------------------------------- + +DefineNewEngineMethod( EngineObject, getType, const EngineTypeInfo*, (),, + "Return the type descriptor for the type the object is an instance of.\n" + "@return The type descriptor for the object's dynamic type." ) +{ + return TYPEOF( object ); +} + +//----------------------------------------------------------------------------- + +DefineNewEngineMethod( EngineObject, addRef, void, (),, + "Increase the reference count of the given object.\n\n" + "@param object An object." ) +{ + object->incRefCount(); +} + +//----------------------------------------------------------------------------- + +DefineNewEngineMethod( EngineObject, release, void, (),, + "Decrease the reference count of the given object. If the count drops to " + "zero, the object will be deleted.\n\n" + "@param object An object." ) +{ + object->decRefCount(); +} + +//----------------------------------------------------------------------------- + +DefineNewEngineMethod( EngineObject, getUserData, void*, (),, + "Get the opaque user data pointer installed on the object.\n" + "@return The user data pointer previously installed on the object; NULL by default." ) +{ + return _USERDATA( object ); +} + +//----------------------------------------------------------------------------- + +DefineNewEngineMethod( EngineObject, setUserData, void, ( void* ptr ),, + "Install an opaque pointer on the object that the control layer can use to " + "associate data with the object.\n" + "@param ptr A pointer.\n" ) +{ + _USERDATA( object ) = ptr; +} + +//----------------------------------------------------------------------------- + +#ifdef TORQUE_DEBUG + +DefineEngineFunction( debugDumpAllObjects, void, (),, + "@brief Dumps all current EngineObject instances to the console.\n\n" + "@note This function is only available in debug builds.\n\n" + "@ingroup Debugging" ) +{ + EngineObject::debugDumpInstances(); +} + +#endif // TORQUE_DEBUG diff --git a/Engine/source/console/engineObject.h b/Engine/source/console/engineObject.h new file mode 100644 index 000000000..21f77b7ce --- /dev/null +++ b/Engine/source/console/engineObject.h @@ -0,0 +1,676 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _ENGINEOBJECT_H_ +#define _ENGINEOBJECT_H_ + +#ifndef _ENGINETYPES_H_ + #include "console/engineTypes.h" +#endif +#ifndef _REFBASE_H_ + #include "core/util/refBase.h" +#endif + +/// Disable TMM for this file. +#include "platform/tmm_off.h" + + +/// @file +/// This file contains the framework for defining class types for interfacing +/// through engine API. + + +class EngineObject; + + +/// Assign the current class and all its subclasses to the given export scope. +/// May be overridden by subclasses. +/// +/// @code +/// class MyClass : public EngineObject +/// { +/// DECLARE_CLASS( MyClass, EngineObject ); +/// DECLARE_INSCOPE( MyAPI ); +/// }; +/// @endcode +#define DECLARE_INSCOPE( name ) \ + typedef name __DeclScope; + +/// Declare that this class and all its subclasses cannot be instantiated through +/// the API, i.e. objects of these classes can only be created inside the engine. +/// +/// @code +/// class MyClass : public EngineObject +/// { +/// DECLARE_CLASS( MyClass, EngineObject ); +/// DECLARE_NONINSTANTIABLE; +/// }; +/// @endcode +#define DECLARE_NONINSTANTIABLE \ + typedef ::FalseType __IsInstantiableType; + +/// Declare that this class and all its subclasses can be instantiated through the +/// API using their respective create() functions. +/// +/// May be overridden by subclasses. +/// +/// @code +/// class MyClass : public EngineObject +/// { +/// DECLARE_CLASS( MyClass, EngineObject ); +/// DECLARE_INSTANTIABLE; +/// }; +/// @endcode +#define DECLARE_INSTANTIABLE \ + typedef ::TrueType __IsInstantiableType; + +/// Declare a class for which the object's may be forcefully disposed of by the engine. +/// In essense, this means that such objects can only be weak-referenced from within the +/// control layer as their lifetime is ultimately controlled by the engine. This is +/// used for all resource type objects that are owned by their devices. +/// +/// An alternative to disposable types would be to shield the control layer from direct +/// access to these objects and rather expose only intermediary objects. However, at some +/// point this just gets crazy. The control layer wraps around engine objects that wrap +/// around internal objects. The internal object goes away, invalidates the native wrapper +/// which invalidates the managed wrapper. +/// +/// By exposing the control layer directly to in-engine resources using disposable objects, +/// intermediary steps and code are made unnecessary while the control layer is still made +/// aware of the fact that ownership of these objects ultimately and firmly belongs with +/// the engine. +/// +/// One important guarantee for disposable types is that an object will not go +/// away unless the control layer calls into the engine. This means that these +/// objects will not magically disappear while managed code is running. +/// +/// @note This macro automatically redefines destroySelf(). +/// @warn Reference-counting is still used for disposable types!! This means that if the +/// reference-count drops to zero, the object will be deleted as any other object. +/// +/// @see IMPLEMENT_DISPOSABLE +#define DECLARE_DISPOSABLE \ + protected: \ + typedef ::TrueType __IsDisposableType; \ + DECLARE_CALLBACK( void, onDispose, () ); \ + public: \ + virtual void destroySelf() \ + { \ + onDispose_callback(); \ + SuperType::destroySelf(); \ + } + +/// Matching implement for DECLARE_DISPOSABLE. +/// +/// @param type The disposable C++ class type. +#define IMPLEMENT_DISPOSABLE( type ) \ + IMPLEMENT_NEW_CALLBACK( type, onDispose, void, (), (), \ + "Called before the instance is disposed." ); + +/// Declare that the current class (and any of its descendents) can only have a single instance. +/// +/// @code +/// class MySingletonClass : public EngineObject +/// { +/// DECLARE_CLASS( MySingletonClass, EngineObject ); +/// DECLARE_SINGLETON; +/// }; +/// @endcode +/// +/// @note At the moment, DECLARE_SINGLETON disallows the use of custom create methods +/// on the same class. +#define DECLARE_SINGLETON \ + private: \ + static ThisType* _smInstance; \ + struct _clearInstance; \ + friend struct _clearInstance; \ + struct _clearInstance { \ + ~_clearInstance() { _smInstance = NULL; } \ + } _clearInstanceInst; \ + public: \ + static ThisType* createSingleton(); \ + static void destroySingleton(); \ + protected: \ + typedef ::TrueType __IsSingletonType; \ + DEFINE_CREATE_METHOD \ + { \ + return createSingleton(); \ + } \ + public: \ + static ThisType* instance() \ + { \ + return _smInstance; \ + } + +/// Matching implement for DECLARE_SINGLETON. +/// @param type The C++ class type. +#define IMPLEMENT_SINGLETON( type ) \ + type::ThisType* type::_smInstance; \ + type::ThisType* type::createSingleton() \ + { \ + if( _smInstance != NULL ) \ + { \ + Con::errorf( "%s::create - Singleton instance already created", \ + TYPE< ThisType >()->getFullyQualifiedExportName().c_str() ); \ + return NULL; \ + } \ + _smInstance = new ThisType(); \ + _smInstance->incRefCount(); \ + return _smInstance; \ + } \ + void type::destroySingleton() \ + { \ + if( !_smInstance ) \ + return; \ + if( ::IsTrueType< __IsDisposableType >() ) \ + _smInstance->destroySelf(); \ + else \ + _smInstance->decRefCount(); \ + } \ + DefineNewEngineStaticMethod( type, getInstance, type*, (),, \ + "Get the singleton " #type " instance.\n\n" \ + "@return The " #type " singleton." ) \ + { \ + return type::instance(); \ + } + +/// Declare a static class, i.e. a class which only acts as an export scope. Static +/// classes cannot be instantiated, neither through the API nor inside the engine (at +/// least in a form that would be usable within the API). +/// +/// @code +/// class MyFunctions +/// { +/// DECLARE_STATIC_CLASS( MyFunctions ); +/// }; +/// +/// IMPLEMENT_STATIC_CLASS( MyFunctions,, "doc" ); +/// +/// DefineStaticEngineMethod( MyFunctions, doSomething, void, (),, "" ) +/// { +/// // ... +/// } +/// @endcode +/// +/// @param type The C++ class type. +/// +/// @see IMPLEMENT_STATIC_CLASS +#define DECLARE_STATIC_CLASS( type ) \ + private: \ + static EngineExportScope __engineExportScopeInst; \ + static EngineExportScope& __engineExportScope() \ + { return __engineExportScopeInst; } \ + public: \ + template< typename T > friend struct ::_SCOPE; \ + +/// Matching implement for DECLARE_STATIC_CLASS. +/// +/// @param type The C++ type of the static class. Also used as export name. +/// @param scope Export scope to place the static class in. +/// @param doc Documentation string. +#define IMPLEMENT_STATIC_CLASS( type, scope, doc ) \ + EngineExportScope type::__engineExportScopeInst( #type, &_SCOPE< scope >()(), doc ); + +/// Declare a concrete class @a type derived from the class @a super. Whether the +/// class can be instantiated by the control layer depends on its instantiability. +/// +/// @code +/// class MyClass : public EngineObject +/// { +/// DECLARE_CLASS( MyClass, EngineObject ); +/// }; +/// @endcode +/// +/// @param type C++ class type. +/// @param super C++ class type of the superclass. May be void only for EngineObject. +/// +/// @see IMPLEMENT_CLASS +/// @see IMPLEMENT_NONINSTANTIABLE_CLASS +#define DECLARE_CLASS( type, super ) \ + public: \ + typedef type ThisType; \ + typedef super SuperType; \ + template< typename T > friend struct ::_EngineTypeTraits; \ + template< typename T > friend struct ::_SCOPE; \ + template< typename T > friend T* _CREATE(); \ + template< typename T, typename Base > friend class ::EngineClassTypeInfo; \ + private: \ + typedef ::_Private::_ConcreteClassBase< ThisType > _ClassBase; \ + static EngineClassTypeInfo< ThisType, _ClassBase > _smTypeInfo; \ + static EngineExportScope& __engineExportScope(); \ + static EnginePropertyTable& _smPropertyTable; \ + virtual const EngineTypeInfo* __typeinfo() const; \ + public: + +/// Declare an abstract class @a type derived from the class @a super. +/// +/// @code +/// class MyClass : public EngineObject +/// { +/// DECLARE_ABSTRACT_CLASS( MyClass, EngineObject ); +/// }; +/// @endcode +/// +/// @param type C++ class type. +/// @param super C++ class type of the superclass. May be void only for EngineObject. +/// +/// @see IMPLEMENT_NONINSTANTIABLE_CLASS +#define DECLARE_ABSTRACT_CLASS( type, super ) \ + public: \ + typedef type ThisType; \ + typedef super SuperType; \ + template< typename T > friend struct ::_EngineTypeTraits; \ + template< typename T > friend struct ::_SCOPE; \ + template< typename T > friend T* _CREATE(); \ + template< typename T, typename Base > friend class ::EngineClassTypeInfo; \ + private: \ + typedef ::_Private::_AbstractClassBase< ThisType > _ClassBase; \ + static EngineClassTypeInfo< ThisType, _ClassBase > _smTypeInfo; \ + static EngineExportScope& __engineExportScope(); \ + static EnginePropertyTable& _smPropertyTable; \ + virtual const EngineTypeInfo* __typeinfo() const; \ + public: + +/// Matching implement for DECLARE_ABSTRACT_CLASS or DECLARE_CLASS for classes +/// that are not instantiable through the API. +/// +/// @code +/// class MyClass : public EngineObject +/// { +/// DECLARE_CLASS( MyClass, EngineObject ); +/// DECLARE_INSCOPE( MyScope ); +/// DECLARE_NONINSTANTIABLE; +/// }; +/// +/// IMPLEMENT_NONINSTANTIABLE_CLASS( MyClass, "My class." ) +/// PROPERTY( myProperty, 1, "My property.", EnginePropertyTransient ) +/// END_IMPLEMENT_CLASS; +/// @endcode +/// +/// @note If you want the export name to be different to the actual class +/// name, use a typedef. +/// +/// @param type The C++ class type. +/// @doc Documentation string. +#define IMPLEMENT_NONINSTANTIABLE_CLASS( type, doc ) \ + DEFINE_CALLIN( fn ## type ## _staticGetType, staticGetType, type, const EngineTypeInfo*, (),,, \ + "Get the type info object for the " #type " class.\n\n" \ + "@return The type info object for " #type ) \ + { \ + return ::TYPE< type >(); \ + } \ + EngineClassTypeInfo< type::ThisType, type::_ClassBase > \ + type::_smTypeInfo( #type, &_SCOPE< __DeclScope >()(), doc ); \ + EngineExportScope& type::__engineExportScope() { return type::_smTypeInfo; } \ + const EngineTypeInfo* type::__typeinfo() const { return &_smTypeInfo; } \ + namespace { namespace _ ## type { \ + extern EnginePropertyTable _propertyTable; \ + } } \ + EnginePropertyTable& type::_smPropertyTable = _ ## type::_propertyTable; \ + namespace { namespace _ ## type { \ + EnginePropertyTable::Property _properties[] = { + +/// Matching implement to DECLARE_CLASS for classes that are instantiable +/// through the API. +/// +/// @note engineFunctions.h must be included for this macro to work. +/// +/// @param type The C++ class type. +/// @param doc Documentation string. +#define IMPLEMENT_CLASS( type, doc ) \ + DEFINE_CALLIN( fn ## type ## _create, create, type, type*, (),,, \ + "Create a new " #type " instance.\n" \ + "@return A new " #type " instance with a reference count of 1." ) \ + { \ + return ::_CREATE< type >(); \ + } \ + IMPLEMENT_NONINSTANTIABLE_CLASS( type, doc ) + +/// Close an IMPLEMENT_CLASS or IMPLEMENT_NONINSTANTIABLE_CLASS block. +#define END_IMPLEMENT_CLASS \ + { NULL } \ + }; \ + EnginePropertyTable _propertyTable \ + ( sizeof( _properties ) / sizeof( _properties[ 0 ] ) - 1, _properties ); \ + } } + +/// Define a property on the current class. +/// +/// A property named XXX must have a corresponding "getXXX" and/or "setXXX" accessor +/// method defined on the class. If there is only a "setXXX" method, the property is +/// write-only. If there is only a "getXXX", the property is read-only. If both are +/// defined, the property is read-write. +/// +/// If the accessors are static methods, the property is a static property. Otherwise +/// it is an instance property. +/// +/// The type of the property is determined by its accessor methods. +/// +/// A getXXX method must take no arguments and return a value. A setXXX method must take +/// one argument and return void. +/// +/// If the property is indexed (@a numElements != 1), the get and set methods take an +/// additional first argument which is the integer index (type S32). Additionally, the get method +/// must be called "getXXXElement" and the set method must be called "setXXXElement". +/// +/// Indexed properties may be either fixed-size or variable-sized. Fixed-size indexed +/// properties have numElements count > 1. Variable-size indexed properties have a +/// numElements count of 0. Variable-sized indexed properties must have an additional +/// method "getXXXCount" that returns the current count of elements for the given property. +/// +/// @code +/// IMPLEMENT_CLASS( MyClass, "My class." ) +/// PROPERTY( myProperty, 1, "My property.", EnginePropertyTransient ) +/// END_IMPLEMENT_CLASS; +/// @endcode +/// +/// @note Case is ignored when matching get and set methods to property definitions. +/// +/// @param name The name of the property. Must correspond with the get and/or set methods. +/// @param numElements If this is a fixed size array property, this is the number of elements +/// the fixed array has. Otherwise 1. +/// @param doc Documentation string. +/// @param flags A combination of EnginePropertyFlags or simply 0 if not applicable. +/// +/// @see EnginePropertyFlags +#define PROPERTY( name, numElements, doc, flags ) \ + { #name, doc, numElements, flags }, + +/// +#define PROPERTY_GROUP( name, numElements, doc ) \ + { #name, doc, numElements, EnginePropertyGroupBegin }, + +/// +#define END_PROPERTY_GROUP \ + { NULL, NULL, 0, EnginePropertyGroupEnd }, + +/// Define a custom create function for this class and its subclasses. Create +/// functions are used to create new instances of class types. This code will be +/// called by the automatically generated "createXXX" functions exported through +/// the API. +/// +/// The type of class being created is available to the code as the type parameter "T" +/// which is guaranteed to be a subtype of the current class type. +/// +/// @code +/// class MyClass : public EngineObject +/// { +/// DECLARE_CLASS( MyClass, EngineObject ); +/// protected: +/// DEFINE_CREATE_METHOD +/// { +/// T* object = new T(); +/// object->incRefCount(); +/// object->_registerObject(); +/// return object; +/// } +/// virtual void _registerObject(); +/// }; +/// @endcode +/// +/// @note A create method must return an object with a reference count of 1. +#define DEFINE_CREATE_METHOD \ + template< typename T > \ + static T* __create() + +/// Define a method that calls back into the control layer. +/// +/// @see IMPLEMENT_CALLBACK +#define DECLARE_CALLBACK( returnType, name, args ) \ + virtual returnType name ## _callback args + + +// Our backdoor into calling the __create method on any class even if it is +// protected or private. This function is automatically made friends of any +// EngineObject class. Should be used except by internal API code. +template< typename T > +inline T* _CREATE() +{ + return T::template __create< T >(); +} + + +/// Interface for object memory allocation. +class IEngineObjectPool +{ + public: + + /// Allocate a new object memory block of the given size. + /// @return Pointer to a new memory block or NULL on failure. + virtual void* allocateObject( U32 size TORQUE_TMM_ARGS_DECL ) = 0; + + /// Return the member for the object at the given address to the + /// allocator for reuse. + /// @param ptr Pointer to an object memory block previously allocated with allocateObject(). + virtual void freeObject( void* ptr ) = 0; + + /// Instance of the object pool to use by default. + static IEngineObjectPool* DEFAULT; +}; + + +/// Singleton class that uses the C runtime memory routines for allocating objects. +class EngineCRuntimeObjectPool : public IEngineObjectPool +{ + public: + + typedef IEngineObjectPool Parent; + + protected: + + static EngineCRuntimeObjectPool smInstance; + + public: + + /// Return the singleton instance of this pool. + static EngineCRuntimeObjectPool* instance() { return &smInstance; } + + // IEngineObjectPool + virtual void* allocateObject( U32 size TORQUE_TMM_ARGS_DECL ); + virtual void freeObject( void* ptr ); +}; + + +/// Base class for all objects that may be passed to the control layer. +/// +/// A set of rules applies to all EngineObject-derived classes: +/// +/// - Every EngineObject class must have a default constructor. +/// - The default constructor and the destructor of every EngineObject class must be public. +/// - If an EngineObject class inherits from multiple classes, the class leading back to EngineObject +/// must be the @b first class in the list to ensure binary-compatible class layouts. +/// - EngineObjects are cooperatively reference-counted by both the engine as well as the control +/// layer. +class EngineObject : public StrongRefBase +{ + public: + + DECLARE_ABSTRACT_CLASS( EngineObject, void ); + DECLARE_INSCOPE( _GLOBALSCOPE ); + DECLARE_INSTANTIABLE; + + friend const EngineTypeInfo* TYPEOF( const EngineObject* ); // __typeinfo + friend void*& _USERDATA( EngineObject* ); // mEngineObjectUserData + friend class StaticEngineObject; // mEngineObjectPool + + protected: + + typedef ::FalseType __IsDisposableType; + typedef ::FalseType __IsSingletonType; + + DEFINE_CREATE_METHOD + { + T* object = new T; + object->incRefCount(); + return object; + } + + /// Subclasses should overload this method instead of the public destroySelf(). + virtual void _destroySelf() {} + + /// + static void* _allocateObject( size_t size, IEngineObjectPool* pool TORQUE_TMM_ARGS_DECL ); + + public: + + EngineObject(); + virtual ~EngineObject(); + + /// Return a string that describes this instance. Meant primarily for debugging. + virtual String describeSelf() const; + + #ifndef TORQUE_DISABLE_MEMORY_MANAGER + // Make sure no matter what, we get the new/delete calls. + void* operator new( size_t size ); + void* operator new( size_t size, IEngineObjectPool* pool ); + #endif + + /// Allocate a new object in the default object pool. + /// @param size Size of the object in bytes. + /// @return Memory block for new object; never NULL. + void* operator new( size_t size TORQUE_TMM_ARGS_DECL ); + + /// Allocate a new object in the given object pool. + /// + /// If the given pool's allocateObject returns NULL, the method will fall back + /// to the default pool. + /// + /// @param size Size of the object in bytes. + /// @param pool Object pool to allocate the object in. + /// @return Memory block for the new object; never NULL. + void* operator new( size_t size, IEngineObjectPool* pool TORQUE_TMM_ARGS_DECL ); + + /// Placement new. + void* operator new( size_t size, void* ptr ) { return ptr; } + + /// Release the given object's memory in the pool it has been allocated from. + void operator delete( void* ptr ); + + /// Return the pool of EngineObjects to which this object belongs. + IEngineObjectPool* getEngineObjectPool() const { return mEngineObjectPool; } + + // StrongRefBase + virtual void destroySelf(); + +#ifdef TORQUE_DEBUG + + /// @name Instance Tracking (debugging only) + /// + /// In debug builds, all EngineObjects are kept on a global list so that it is easy + /// to enumerate all live objects at any time. + /// + /// @note This is @b NOT thread-safe. + /// @{ + + /// Type of callback function for iterating over EngineObject instances. + typedef void ( *DebugEnumInstancesCallback )( EngineObject* ); + + /// Dump describeSelf()s of all live ConsoleObjects to the console. + static void debugDumpInstances(); + + /// Call the given callback for all instances of the given type. + /// Callback may also be NULL in which case the method just iterates + /// over all instances of the given type. This is useful for setting + /// a breakpoint during debugging. + static void debugEnumInstances( const std::type_info& type, DebugEnumInstancesCallback callback ); + + /// Same as above but uses an export class name and also includes + /// inheritance (i.e. enumerates all instances of the given class and + /// its subclasses). + static void debugEnumInstances( const char* className, DebugEnumInstancesCallback callback ); + + private: + + /// Next object in global link chain of engine objects. + /// @note Debug builds only. + EngineObject* mNextEngineObject; + + /// Previous object in global link chain of engine objects. + /// @note Debug builds only. + EngineObject* mPrevEngineObject; + + /// Total number of engine objects currently instantiated. + /// @note Debug builds only. + static U32 smNumEngineObjects; + + /// First object in the global link chain of engine objects. + /// @note Debug builds only. + static EngineObject* smFirstEngineObject; + + /// @} + +#endif + + private: + + /// Object pool to which this object belongs. If this is NULL, + /// the object will not deallocate itself when it is destructed. + /// This is useful for inline allocation of objects. + IEngineObjectPool* mEngineObjectPool; + + /// Opaque user data pointer that the control layer may install + /// on any engine object. Most importantly, this allows control layers + /// to very easily keep track of EngineObjects that they have already + /// created their own wrapper objects for. + void* mEngineObjectUserData; + + // Disable array new/delete operators. + void* operator new[]( size_t ); + void operator delete[]( void* ); +}; + + +/// A statically allocated engine object. +/// +/// Static objects have an implicit initial reference count of one and will not +/// delete themselves even when asked to do so. +class StaticEngineObject : public EngineObject +{ + public: + + DECLARE_ABSTRACT_CLASS( StaticEngineObject, EngineObject ); + DECLARE_NONINSTANTIABLE; + + StaticEngineObject(); + + // EngineObject. + virtual void destroySelf(); +}; + + +typedef StrongRefPtr< EngineObject > EngineObjectRef; +typedef WeakRefPtr< EngineObject > EngineObjectWeakRef; + + +/// Return the type info object for the dynamic type of the given object. +/// @param object An EngineObject or NULL. +/// @return An EngineTypeInfo instance or NULL if @a object is NULL. +inline const EngineTypeInfo* TYPEOF( const EngineObject* object ) +{ + if( !object ) + return NULL; + return object->__typeinfo(); +} + + +#include "platform/tmm_on.h" +#endif // !_ENGINEOBJECT_H_ diff --git a/Engine/source/console/enginePrimitives.cpp b/Engine/source/console/enginePrimitives.cpp new file mode 100644 index 000000000..de5f1f7b3 --- /dev/null +++ b/Engine/source/console/enginePrimitives.cpp @@ -0,0 +1,35 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/enginePrimitives.h" +#include "console/engineTypeInfo.h" + + +IMPLEMENT_PRIMITIVE( bool, bool,, "Boolean true/false." ); +IMPLEMENT_PRIMITIVE( S8, byte,, "8bit signed integer." ); +IMPLEMENT_PRIMITIVE( U8, ubyte,, "8bit unsigned integer." ); +IMPLEMENT_PRIMITIVE( S32, int,, "32bit signed integer." ); +IMPLEMENT_PRIMITIVE( U32, uint,, "32bit unsigned integer." ); +IMPLEMENT_PRIMITIVE( F32, float,, "32bit single-precision floating-point." ); +IMPLEMENT_PRIMITIVE( F64, double,, "64bit double-precision floating-point." ); +IMPLEMENT_PRIMITIVE( String, string,, "Null-terminated UTF-16 Unicode string." ); +IMPLEMENT_PRIMITIVE( void*, ptr,, "Opaque pointer." ); diff --git a/Engine/source/console/enginePrimitives.h b/Engine/source/console/enginePrimitives.h new file mode 100644 index 000000000..e37fcfa4a --- /dev/null +++ b/Engine/source/console/enginePrimitives.h @@ -0,0 +1,81 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _ENGINEPRIMITIVES_H_ +#define _ENGINEPRIMITIVES_H_ + +#ifndef _ENGINETYPES_H_ + #include "console/engineTypes.h" +#endif + + +/// @file +/// Definitions for the core primitive types used in the +/// exposed engine API. + + + +DECLARE_PRIMITIVE( bool ); +DECLARE_PRIMITIVE( S8 ); +DECLARE_PRIMITIVE( U8 ); +DECLARE_PRIMITIVE( S32 ); +DECLARE_PRIMITIVE( U32 ); +DECLARE_PRIMITIVE( F32 ); +DECLARE_PRIMITIVE( F64 ); +DECLARE_PRIMITIVE( void* ); + + +//FIXME: this allows String to be used as a struct field type + +// String is special in the way its data is exchanged through the API. Through +// calls, strings are passed as plain, null-terminated UTF-16 character strings. +// In addition, strings passed back as return values from engine API functions +// are considered to be owned by the API layer itself. The rule here is that such +// a string is only valid until the next API call is made. Usually, control layers +// will immediately copy and convert strings to their own string type. +_DECLARE_TYPE( String ); +template<> +struct EngineTypeTraits< String > : public _EnginePrimitiveTypeTraits< String > +{ + typedef const UTF16* ArgumentValueType; + typedef const UTF16* ReturnValueType; + + //FIXME: this needs to be sorted out; for now, we store default value literals in ASCII + typedef const char* DefaultArgumentValueStoreType; + + static const UTF16* ReturnValue( const String& str ) + { + static String sTemp; + sTemp = str; + return sTemp.utf16(); + } +}; + + +// For struct fields, String cannot be used directly but "const UTF16*" must be used +// instead. Make sure this works with the template machinery by redirecting the type +// back to String. +template<> struct EngineTypeTraits< const UTF16* > : public EngineTypeTraits< String > {}; +template<> inline const EngineTypeInfo* TYPE< const UTF16* >() { return TYPE< String >(); } +inline const EngineTypeInfo* TYPE( const UTF16*& ) { return TYPE< String >(); } + +#endif // !_ENGINEPRIMITIVES_H_ diff --git a/Engine/source/console/engineStructs.cpp b/Engine/source/console/engineStructs.cpp new file mode 100644 index 000000000..730e599aa --- /dev/null +++ b/Engine/source/console/engineStructs.cpp @@ -0,0 +1,75 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/engineStructs.h" +#include "console/engineAPI.h" +#include "core/util/tVector.h" +#include "core/util/uuid.h" +#include "core/color.h" + + +IMPLEMENT_STRUCT( Vector< bool >, + BoolVector,, + "" ) +END_IMPLEMENT_STRUCT; + + +IMPLEMENT_STRUCT( Vector< S32 >, + IntVector,, + "" ) +END_IMPLEMENT_STRUCT; + + +IMPLEMENT_STRUCT( Vector< F32 >, + FloatVector,, + "" ) +END_IMPLEMENT_STRUCT; + + +IMPLEMENT_STRUCT( Torque::UUID, + UUID,, + "" ) +END_IMPLEMENT_STRUCT; + + +IMPLEMENT_STRUCT( ColorI, + ColorI,, + "RGBA color quadruple in 8bit integer precision per channel." ) + + FIELD( red, red, 1, "Red channel value." ) + FIELD( green, green, 1, "Green channel value." ) + FIELD( blue, blue, 1, "Blue channel value." ) + FIELD( alpha, alpha, 1, "Alpha channel value." ) + +END_IMPLEMENT_STRUCT; + + +IMPLEMENT_STRUCT( ColorF, + ColorF,, + "RGBA color quadruple in 32bit floating-point precision per channel." ) + + FIELD( red, red, 1, "Red channel value." ) + FIELD( green, green, 1, "Green channel value." ) + FIELD( blue, blue, 1, "Blue channel value." ) + FIELD( alpha, alpha, 1, "Alpha channel value." ) + +END_IMPLEMENT_STRUCT; diff --git a/Engine/source/console/engineStructs.h b/Engine/source/console/engineStructs.h new file mode 100644 index 000000000..c77530af1 --- /dev/null +++ b/Engine/source/console/engineStructs.h @@ -0,0 +1,51 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _ENGINESTRUCTS_H_ +#define _ENGINESTRUCTS_H_ + +#ifndef _ENGINETYPES_H_ + #include "console/engineTypes.h" +#endif + + +/// @file +/// Definitions for the core engine structured types. + + +template< typename T > class Vector; +namespace Torque { + class UUID; +} + +class ColorI; +class ColorF; + + +DECLARE_STRUCT( Vector< bool > ); +DECLARE_STRUCT( Vector< S32 > ); +DECLARE_STRUCT( Vector< F32 > ); +DECLARE_STRUCT( Torque::UUID ); +DECLARE_STRUCT( ColorI ); +DECLARE_STRUCT( ColorF ); + +#endif // !_ENGINESTRUCTS_H_ diff --git a/Engine/source/console/engineTypeInfo.cpp b/Engine/source/console/engineTypeInfo.cpp new file mode 100644 index 000000000..a1cdd9d15 --- /dev/null +++ b/Engine/source/console/engineTypeInfo.cpp @@ -0,0 +1,149 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/engineTypeInfo.h" +#include "console/engineAPI.h" +#include "core/strings/stringFunctions.h" + + +IMPLEMENT_ENUM( EngineTypeKind, + EngineTypeKind, ReflectionAPI, + "Kinding for engine types. Engine types are segregated into kinds which " + "are to types what types are to values, i.e. a value is an instance of a type " + "and a type is an instance of a kind.\n" + "@internal\n\n") + { EngineTypeKindPrimitive, "Primitive", "Atomic data." }, + { EngineTypeKindEnum, "Enum", "Enumeration." }, + { EngineTypeKindBitfield, "Bitfield", "Bitfield." }, + { EngineTypeKindFunction, "Function", "Function pointer." }, + { EngineTypeKindStruct, "Struct", "Structured value." }, + { EngineTypeKindClass, "Class", "EngineObject value." }, +END_IMPLEMENT_ENUM; + +IMPLEMENT_NONINSTANTIABLE_CLASS( EngineTypeInfo, + "Information about an engine type." ) + PROPERTY( TypeKind, 1, "Kind of type.", 0 ) + PROPERTY( SuperType, 1, "Type that this type subtypes from.", 0 ) +END_IMPLEMENT_CLASS; + +U32 EngineTypeInfo::smNumTypes; +const EngineTypeInfo* EngineTypeInfo::smFirst; + + +//----------------------------------------------------------------------------- + +EngineTypeInfo::EngineTypeInfo( const char* typeName, EngineExportScope* scope, EngineTypeKind kind, U32 instanceSize, const char* docString ) + : SuperType( typeName, scope, docString ), + mTypeKind( kind ), + mInstanceSize( instanceSize ), + mNext( smFirst ), + mEnumTable( NULL ), + mFieldTable( NULL ), + mPropertyTable( NULL ), + mSuperType( NULL ) +{ + mExportKind = EngineExportKindType; + + smFirst = this; + smNumTypes ++; +} + +//----------------------------------------------------------------------------- + +const EngineTypeInfo* EngineTypeInfo::getTypeInfoByName( const char* typeName ) +{ + for( const EngineTypeInfo* typeInfo = getFirstType(); typeInfo != NULL; typeInfo = typeInfo->getNextType() ) + if( dStricmp( typeInfo->getTypeName(), typeName ) == 0 ) + return typeInfo; + + return NULL; +} + +//----------------------------------------------------------------------------- + +U32 EngineTypeInfo::getValueSize() const +{ + switch( getTypeKind() ) + { + case EngineTypeKindPrimitive: + case EngineTypeKindEnum: + case EngineTypeKindBitfield: + return mInstanceSize; + + case EngineTypeKindStruct: + case EngineTypeKindFunction: + case EngineTypeKindClass: + return sizeof( void* ); + } + + AssertFatal( false, "EngineTypeInfo - unknown type kind!" ); + return U32( -1 ); +} + +//----------------------------------------------------------------------------- + +bool EngineTypeInfo::isSubtypeOf( const EngineTypeInfo* type ) const +{ + for( const EngineTypeInfo* p = this; p != NULL; p = p->getSuperType() ) + if( p == type ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- + +bool EngineTypeInfo::constructInstance( void* ptr ) const +{ + AssertFatal( !isAbstract(), "EngineTypeInfo::constructInstance - Called on abstract type!" ); + return false; +} + +//----------------------------------------------------------------------------- + +void EngineTypeInfo::destructInstance( void* ptr ) const +{ + AssertFatal( !isAbstract(), "EngineTypeInfo::destructInstance - Called on abstract type!" ); +} + +//============================================================================= +// API. +//============================================================================= +// MARK: ---- API ---- + +//----------------------------------------------------------------------------- + +DefineNewEngineMethod( EngineTypeInfo, getTypeKind, EngineTypeKind, (),, + "Get the kind of the type.\n" + "@return The type kind." ) +{ + return object->getTypeKind(); +} + +//----------------------------------------------------------------------------- + +DefineNewEngineMethod( EngineTypeInfo, getSuperType, const EngineTypeInfo*, (),, + "Get the type that this type subtypes from.\n" + "@return The supertype info instance or NULL." ) +{ + return object->getSuperType(); +} diff --git a/Engine/source/console/engineTypeInfo.h b/Engine/source/console/engineTypeInfo.h new file mode 100644 index 000000000..40f38f51f --- /dev/null +++ b/Engine/source/console/engineTypeInfo.h @@ -0,0 +1,1045 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _ENGINETYPEINFO_H_ +#define _ENGINETYPEINFO_H_ + +#ifndef _ENGINEEXPORTS_H_ + #include "console/engineExports.h" +#endif + + +class EngineTypeInfo; + + +/// Kinding for engine types. Engine types are segregated into kinds which +/// are to types what types are to values, i.e. a value is an instance of a type +/// and a type is an instance of a kind. +enum EngineTypeKind +{ + EngineTypeKindPrimitive, ///< Any kind of atomic data. Passed by value. + EngineTypeKindEnum, ///< Enumeration. Passed by value. + EngineTypeKindBitfield, ///< Bitfield. Passed by value. + EngineTypeKindFunction, ///< Function pointer. + EngineTypeKindStruct, ///< Structured value. Passed by reference. + EngineTypeKindClass ///< Pointer to opaque EngineObject. +}; + +DECLARE_ENUM( EngineTypeKind ); + +/// Flags for an EngineTypeInfo. +enum EngineTypeFlags +{ + EngineTypeAbstract = BIT( 0 ), ///< Type is abstract. + EngineTypeInstantiable = BIT( 1 ), ///< Type can be instantiated through API. + EngineTypeDisposable = BIT( 2 ), ///< Instances can be disposed by the engine. + EngineTypeSingleton = BIT( 3 ), ///< Class type with only a single instance. + EngineTypeVariadic = BIT( 4 ), ///< Variadic function type. +}; + + + +/// Table of values for an enumeration or bitfield type. +class EngineEnumTable +{ + public: + + /// A value in an enumeration. + /// + /// The order of the fields in this structure is important as it is meant to be + /// initialized with { ... } in code. + struct Value + { + /// Integer value. If the enumeration is a bit field, + /// this is the bit value. + S32 mInt; + + /// Name of the value. + const char* mName; + + /// Documentation string. + const char* mDocString; + + /// Return the name of this enum value. + const char* getName() const { return mName; } + + /// Return the documentation string of this enum value. + const char* getDocString() const { return mDocString; } + + /// Return the integer value of this enum value. + S32 getInt() const { return mInt; } + + operator S32() const + { + return getInt(); + } + }; + + protected: + + /// Number of values in this enumeration. + U32 mNumValues; + + /// Records for all the enum values. + const Value* mValues; + + public: + + /// + EngineEnumTable( U32 numValues, const Value* values ) + : mNumValues( numValues ), + mValues( values ) {} + + /// Return the number of Values in this enumeration/bitfield. + U32 getNumValues() const { return mNumValues; } + + /// Get the enum value at the given index. + const Value& operator []( U32 index ) const + { + AssertFatal( index < getNumValues(), "" ); + return mValues[ index ]; + } +}; + + +/// Table of fields for a struct type. +class EngineFieldTable +{ + public: + + /// A field descriptor in a field table. + struct Field + { + /// Name of the field or group. + const char* mName; + + /// Documentation string. + const char* mDocString; + + /// Indexed size of this field. Must be >=1. + U32 mNumElements; + + /// Type of the field. + const EngineTypeInfo* mType; + + /// Offset of the field in instances. + U32 mOffset; + + /// + const char* getName() const { return mName; } + + /// + const char* getDocString() const { return mDocString; } + + /// + U32 getNumElements() const { return mNumElements; } + + /// + const EngineTypeInfo* getType() const { return mType; } + + /// + U32 getOffset() const { return mOffset; } + }; + + protected: + + /// Number of fields in this table. + U32 mNumFields; + + /// + const Field* mFields; + + public: + + /// Construct a field table from a NULL-terminated array of Field + /// records. + EngineFieldTable( const Field* fields ) + : mFields( fields ), + mNumFields( 0 ) + { + while( fields[ mNumFields ].getName() ) + mNumFields ++; + } + + /// + EngineFieldTable( U32 numFields, const Field* fields ) + : mNumFields( numFields ), + mFields( fields ) {} + + /// + U32 getNumFields() const { return mNumFields; } + + /// + const Field& operator []( U32 index ) const + { + AssertFatal( index <= getNumFields(), "EngineFieldTable - index out of range" ); + return mFields[ index ]; + } +}; + + +/// Flags for property descriptors. +enum EnginePropertyFlags +{ + EnginePropertyTransient = BIT( 0 ), ///< Exclude from serializations. + EnginePropertyConstant = BIT( 1 ), ///< Property value is constant once object has been constructed. + EnginePropertyHideInInspectors = BIT( 2 ), ///< Don't make the property visible in property sheets in the editor. + EnginePropertyGroupBegin = BIT( 3 ), ///< Special property to mark the beginning of a group; does not define a real property on the object. + EnginePropertyGroupEnd = BIT( 4 ), ///< Special property to mark the end of a group; does not define a real property on the object. +}; + +/// +/// +/// +/// - Read-only properties only have a getXXX and no setXXX method. +/// - Static properties (value shared by all instances) don't take a 'this' parameter. +/// - +class EnginePropertyTable +{ + public: + + struct Property + { + /// Name of the property. + const char* mName; + + /// Doc string using Javadoc markup. + const char* mDocString; + + /// Indexed size of the property. If 0, the property array is variable-sized. If 1, the property + /// is not indexed. If >1, the property is a fixed-size array. + U32 mNumElements; + + /// Combination of EnginePropertyFlags. + U32 mFlags; + + /// Return the name of the property. + const char* getName() const { return mName; } + + /// Return the number of indexed elements of the property. + U32 getNumElements() const { return mNumElements; } + + /// Return the documentation string for this property. + const char* getDocString() const { return mDocString; } + + /// Test whether the property has a constant value. + bool isConstant() const { return ( mFlags & EnginePropertyConstant ); } + + /// Test whether the property value is transient, i.e. should not be serialized. + bool isTransient() const { return ( mFlags & EnginePropertyTransient ); } + + /// Test whether this property begins a group of properties. + bool isGroupBegin() const { return ( mFlags & EnginePropertyGroupBegin ); } + + /// Test whether this property ends a group of properties. + bool isGroupEnd() const { return ( mFlags & EnginePropertyGroupEnd ); } + + /// + bool hideInInspectors() const { return ( mFlags & EnginePropertyHideInInspectors ); } + }; + + protected: + + /// Number of properties in this table. + U32 mNumProperties; + + /// Array of property definitions. + const Property* mProperties; + + public: + + /// + EnginePropertyTable( U32 numProperties, const Property* properties ) + : mNumProperties( numProperties ), + mProperties( properties ) {} + + /// + U32 getNumProperties() const { return mNumProperties; } + + /// + const Property& operator []( U32 index ) const + { + AssertFatal( index <= getNumProperties(), "EnginePropertyTable - index out of range" ); + return mProperties[ index ]; + } +}; + + +/// Information about the return and argument types of a function type. +class EngineArgumentTypeTable +{ + protected: + + /// Return type of the function type. + const EngineTypeInfo* mReturnType; + + /// Number of argument types of the function type. + U32 mNumArguments; + + /// Array of argument types of the function type. + const EngineTypeInfo* const* mArgumentTypes; + + public: + + /// + EngineArgumentTypeTable( const EngineTypeInfo* returnType, + U32 numArguments, + const EngineTypeInfo* const* argumentTypes ) + : mReturnType( returnType ), + mNumArguments( numArguments ), + mArgumentTypes( argumentTypes ) {} + + /// Return the return type of the function type. + const EngineTypeInfo* getReturnType() const { return mReturnType; } + + /// Return the number of argument types of the function type. + U32 getNumArguments() const { return mNumArguments; } + + /// Get the argument type at the given index. + const EngineTypeInfo* operator []( U32 index ) const + { + AssertFatal( index <= getNumArguments(), "EngineArgumentTypeTable - Index out of range!" ); + return mArgumentTypes[ index ]; + } +}; + + +/// Networking related information for an engine API type. +struct EngineTypeNetInfo +{ + S32 mNetGroupMask; + S32 mNetType; + S32 mNetEventDir; + + #ifdef TORQUE_NET_STATS + struct NetStatInstance + { + }; + #endif + + EngineTypeNetInfo() + : mNetGroupMask( 0 ), + mNetType( 0 ), + mNetEventDir( 0 ) {} +}; + + +/// Information about an engine type. +/// +/// This class is used to store run-time type information about engine types. +/// +/// Once created, type info objects must persist for the entire duration the engine +/// is running. +/// +/// All types are implicitly export scopes and may thus contain other exports +/// within them. +class EngineTypeInfo : public EngineExportScope +{ + public: + + DECLARE_CLASS( EngineTypeInfo, EngineExportScope ); + + // While we still have the old ConsoleObject system around, allow + // them to retroactively install property tables. Will be removed + // when the console interop is removed and all classes are migrated + // to the new system. + template< typename T > friend class ConcreteClassRep; + + protected: + + /// Kind of type. + EngineTypeKind mTypeKind; + + /// Size of an instance of this type. + U32 mInstanceSize; + + /// Combination of EngineTypeFlags. + BitSet32 mTypeFlags; + + /// If this is an enumeration or bitfield type, this is the pointer to the enum table. + const EngineEnumTable* mEnumTable; + + /// If this is a struct type, this is the pointer to the field table. + const EngineFieldTable* mFieldTable; + + /// If this is a class type, this is the pointer to the property table. + const EnginePropertyTable* mPropertyTable; + + /// If this is a function type, this is the pointer to the argument type table. + const EngineArgumentTypeTable* mArgumentTypeTable; + + /// Pointer to type info object for engine type that this type subtypes from. NULL if none. + const EngineTypeInfo* mSuperType; + + /// Networking related information for this type. + mutable EngineTypeNetInfo mNetInfo; + + /// Next type in the global link chain. + const EngineTypeInfo* mNext; + + /// Total number of defined types. + static U32 smNumTypes; + + /// First type in the global link chain of type info instances. + static const EngineTypeInfo* smFirst; + + /// + EngineTypeInfo( const char* typeName, EngineExportScope* scope, EngineTypeKind kind, U32 instanceSize, const char* docString ); + + public: + + /// @name List Interface + /// Interface for accessing/traversing the list of types. + /// @{ + + /// Return the first type in the global link chain of types. + static const EngineTypeInfo* getFirstType() { return smFirst; } + + /// Return the next type in the global link chain of types. + const EngineTypeInfo* getNextType() const + { + return mNext; + } + + /// @} + + /// Get the type info instance for the given type. + /// @param typeName Name of a registered engine type. + /// @return Type info instance for @a typeName or NULL if no such type exists. + static const EngineTypeInfo* getTypeInfoByName( const char* typeName ); + + /// Return the name of the type. + /// @return The name of the type or an empty string if this is an anonymous type. + const char* getTypeName() const { return getExportName(); } + + /// Return the kind this type. + EngineTypeKind getTypeKind() const { return mTypeKind; } + + /// Return the type info object of the engine type that this type subtypes from. + const EngineTypeInfo* getSuperType() const { return mSuperType; } + + /// Return the size of a single value in bytes. + /// Be aware that the value size refers to the value as it is passed around. For types using + /// reference or pointer value semantics, this is thus the size of a pointer or reference and + /// not the size of the actual instance. + U32 getValueSize() const; + + /// Return the + U32 getInstanceSize() const { return mInstanceSize; } + + /// Return true if the type is abstract. + /// @note Only class and function types can be abstract. + bool isAbstract() const { return mTypeFlags.test( EngineTypeAbstract ); } + + /// Return true if the type can be instantiated from outside the engine. + bool isInstantiable() const { return mTypeFlags.test( EngineTypeInstantiable ); } + + /// Return true if the objects of this type can be disposed by the engine. + bool isDisposable() const { return mTypeFlags.test( EngineTypeDisposable ); } + + /// Return true if the type can have only a single instance. + bool isSingleton() const { return mTypeFlags.test( EngineTypeSingleton ); } + + /// Return true if the type is a variadic function type. + bool isVariadic() const { return mTypeFlags.test( EngineTypeVariadic ); } + + /// Test whether this type is a primitive type. + bool isPrimitive() const { return ( getTypeKind() == EngineTypeKindPrimitive ); } + + /// Test whether this type is an enumeration type. + bool isEnum() const { return ( getTypeKind() == EngineTypeKindEnum ); } + + /// Test whether this type is a bitfield type. + bool isBitfield() const { return ( getTypeKind() == EngineTypeKindBitfield ); } + + /// Test whether this type is a function type. + bool isFunction() const { return ( getTypeKind() == EngineTypeKindFunction ); } + + /// Test whether this type is a struct type. + bool isStruct() const { return ( getTypeKind() == EngineTypeKindStruct ); } + + /// Test whether this is a class type. + bool isClass() const { return ( getTypeKind() == EngineTypeKindClass ); } + + /// Return the EngineEnumTable for this type (only for enumeration and bitfield types). + const EngineEnumTable* getEnumTable() const { return mEnumTable; } + + /// Return the EngineFieldTable for this type (only for struct types). + const EngineFieldTable* getFieldTable() const { return mFieldTable; } + + /// Return the EnginePropertyTable for this type (only for class types). + const EnginePropertyTable* getPropertyTable() const { return mPropertyTable; } + + /// + const EngineArgumentTypeTable* getArgumentTypeTable() const { return mArgumentTypeTable; } + + /// Return true if this type is a subtype of the given type. + bool isSubtypeOf( const EngineTypeInfo* type ) const; + + /// + EngineTypeNetInfo& getNetInfo() const { return mNetInfo; } + + /// @name Instancing + /// @{ + + /// Create a new instance at the given address. + /// @pre Must not be called for abstract types. + virtual bool constructInstance( void* ptr ) const; + + /// Destroy the instance at the given address. + /// @pre Must not be called for abstract types. + virtual void destructInstance( void* ptr ) const; + + /// @} +}; + +//-------------------------------------------------------------------------- +// Type Info Helper Classes. +//-------------------------------------------------------------------------- + + +/// Template for type infos of primitive, enum, and bitfield types. +template< typename T > +class EngineSimpleTypeInfo : public EngineTypeInfo +{ + public: + + typedef EngineTypeInfo Parent; + + EngineSimpleTypeInfo( const char* name, EngineExportScope* scope, EngineTypeKind kind, const char* docString, EngineEnumTable* enumTable = NULL ) + : Parent( name, scope, kind, sizeof( T ), docString ) + { + mEnumTable = enumTable; + mTypeFlags.set( EngineTypeInstantiable ); + } + + virtual bool constructInstance( void* ptr ) const + { + T* p = reinterpret_cast< T* >( ptr ); + *p = T(); + return true; + } + + virtual void destructInstance( void* ptr ) const + { + // Nothing to do. + } +}; + + +/// Template for struct type infos. +template< typename T > +class EngineStructTypeInfo : public EngineTypeInfo +{ + public: + + typedef EngineTypeInfo Parent; + + EngineStructTypeInfo( const char* name, EngineExportScope* scope, const char* docString, EngineFieldTable* fieldTable ) + : Parent( name, scope, EngineTypeKindStruct, sizeof( T ), docString ) + { + mFieldTable = fieldTable; + mTypeFlags.set( EngineTypeInstantiable ); + } + + virtual bool constructInstance( void* ptr ) const + { + T* p = reinterpret_cast< T* >( ptr ); + *p = T(); + return true; + } + + virtual void destructInstance( void* ptr ) const + { + T* p = reinterpret_cast< T* >( ptr ); + destructInPlace( p ); + } +}; + + +/// Template for class type infos. +template< typename T, typename Base > +class EngineClassTypeInfo : public EngineTypeInfo +{ + public: + + typedef EngineTypeInfo Parent; + + /// The documentation string set by CLASSDOC (if any). + static const char* smDocString; + + EngineClassTypeInfo( const char* name, EngineExportScope* scope, const char* docString = NULL ) + : Parent( name, scope, EngineTypeKindClass, sizeof( T ), docString ? docString : smDocString ) + { + mPropertyTable = &T::_smPropertyTable; + mSuperType = TYPE< typename T::SuperType >(); + if( IsTrueType< typename Base::IsAbstractType >() ) + mTypeFlags.set( EngineTypeAbstract ); + else if( IsTrueType< typename T::__IsInstantiableType >() ) + mTypeFlags.set( EngineTypeInstantiable ); + + if( IsTrueType< typename T::__IsDisposableType >() ) + mTypeFlags.set( EngineTypeDisposable ); + if( IsTrueType< typename T::__IsSingletonType >() ) + mTypeFlags.set( EngineTypeSingleton ); + } + + virtual bool constructInstance( void* ptr ) const + { + return Base::_construct( ptr ); + } + + virtual void destructInstance( void* ptr ) const + { + return Base::_destruct( ptr ); + } +}; + +template< typename T, typename Base > const char* EngineClassTypeInfo< T, Base >::smDocString; + + +/// Template for function type infos. +template< typename T > +class EngineFunctionTypeInfo : public EngineTypeInfo +{ + public: + + typedef EngineTypeInfo Parent; + + static _EngineArgumentTypeTable< T > ARGTYPES; + + EngineFunctionTypeInfo() + : Parent( "", &_SCOPE<>()(), EngineTypeKindFunction, sizeof( T* ), "" ) + { + mArgumentTypeTable = &ARGTYPES; + + if( ARGTYPES.VARIADIC ) + mTypeFlags.set( EngineTypeVariadic ); + + // Function types cannot be instantiated. + mTypeFlags.set( EngineTypeAbstract ); + } +}; + +template< typename T > _EngineArgumentTypeTable< T > EngineFunctionTypeInfo< T >::ARGTYPES; +template< typename T > const EngineFunctionTypeInfo< T > _EngineFunctionTypeTraits< T >::smTYPEINFO; + + +//-------------------------------------------------------------------------- +// Function Argument Type Infos. +//-------------------------------------------------------------------------- + +template< typename R > +struct _EngineArgumentTypeTable< R() > : public EngineArgumentTypeTable +{ + static const U32 NUM_ARGUMENTS = 0; + static const bool VARIADIC = false; + static const EngineTypeInfo* const RETURN; +#ifdef TORQUE_COMPILER_GCC + static const EngineTypeInfo* const ARGS[ 0 ]; +#else + static const EngineTypeInfo* const ARGS[ 1 ]; +#endif + + _EngineArgumentTypeTable() + : EngineArgumentTypeTable( TYPE< typename EngineTypeTraits< R >::Type >(), NUM_ARGUMENTS, ARGS ) {} +}; +template< typename R > const EngineTypeInfo* const _EngineArgumentTypeTable< R() >::RETURN = TYPE< typename EngineTypeTraits< R >::Type >(); +#ifdef TORQUE_COMPILER_GCC +template< typename R > const EngineTypeInfo* const _EngineArgumentTypeTable< R() >::ARGS[ 0 ] = {}; +#else +template< typename R > const EngineTypeInfo* const _EngineArgumentTypeTable< R() >::ARGS[ 1 ] = {}; +#endif +template< typename R > +struct _EngineArgumentTypeTable< R( ... ) > : public _EngineArgumentTypeTable< R() > +{ + static const bool VARIADIC = true; + _EngineArgumentTypeTable() {} +}; + +template< typename R, typename A > +struct _EngineArgumentTypeTable< R( A ) > : public EngineArgumentTypeTable +{ + static const U32 NUM_ARGUMENTS = 1; + static const bool VARIADIC = false; + static const EngineTypeInfo* const RETURN; + static const EngineTypeInfo* const ARGS[ 1 ]; + + _EngineArgumentTypeTable() + : EngineArgumentTypeTable( TYPE< typename EngineTypeTraits< R >::Type >(), NUM_ARGUMENTS, ARGS ) {} +}; +template< typename R, typename A > +const EngineTypeInfo* const _EngineArgumentTypeTable< R( A ) >::RETURN = TYPE< typename EngineTypeTraits< R >::Type >(); +template< typename R, typename A > +const EngineTypeInfo* const _EngineArgumentTypeTable< R( A ) >::ARGS[ 1 ] = +{ + TYPE< typename EngineTypeTraits< A >::Type >() +}; +template< typename R, typename A > +struct _EngineArgumentTypeTable< R( A, ... ) > : public _EngineArgumentTypeTable< R( A ) > +{ + static const bool VARIADIC = true; + _EngineArgumentTypeTable() {} +}; + +template< typename R, typename A, typename B > +struct _EngineArgumentTypeTable< R( A, B ) > : public EngineArgumentTypeTable +{ + static const U32 NUM_ARGUMENTS = 2; + static const bool VARIADIC = false; + static const EngineTypeInfo* const RETURN; + static const EngineTypeInfo* const ARGS[ 2 ]; + + _EngineArgumentTypeTable() + : EngineArgumentTypeTable( TYPE< typename EngineTypeTraits< R >::Type >(), NUM_ARGUMENTS, ARGS ) {} +}; +template< typename R, typename A, typename B > +const EngineTypeInfo* const _EngineArgumentTypeTable< R( A, B ) >::RETURN = TYPE< typename EngineTypeTraits< R >::Type >(); +template< typename R, typename A, typename B > +const EngineTypeInfo* const _EngineArgumentTypeTable< R( A, B ) >::ARGS[ 2 ] = +{ + TYPE< typename EngineTypeTraits< A >::Type >(), + TYPE< typename EngineTypeTraits< B >::Type >() +}; +template< typename R, typename A, typename B > +struct _EngineArgumentTypeTable< R( A, B, ... ) > : public _EngineArgumentTypeTable< R( A, B ) > +{ + static const bool VARIADIC = true; + _EngineArgumentTypeTable() {} +}; + +template< typename R, typename A, typename B, typename C > +struct _EngineArgumentTypeTable< R( A, B, C ) > : public EngineArgumentTypeTable +{ + static const U32 NUM_ARGUMENTS = 3; + static const bool VARIADIC = false; + static const EngineTypeInfo* const RETURN; + static const EngineTypeInfo* const ARGS[ 3 ]; + + _EngineArgumentTypeTable() + : EngineArgumentTypeTable( TYPE< typename EngineTypeTraits< R >::Type >(), NUM_ARGUMENTS, ARGS ) {} +}; +template< typename R, typename A, typename B, typename C > +const EngineTypeInfo* const _EngineArgumentTypeTable< R( A, B, C ) >::RETURN = TYPE< typename EngineTypeTraits< R >::Type >(); +template< typename R, typename A, typename B, typename C > +const EngineTypeInfo* const _EngineArgumentTypeTable< R( A, B, C ) >::ARGS[ 3 ] = +{ + TYPE< typename EngineTypeTraits< A >::Type >(), + TYPE< typename EngineTypeTraits< B >::Type >(), + TYPE< typename EngineTypeTraits< C >::Type >() +}; +template< typename R, typename A, typename B, typename C > +struct _EngineArgumentTypeTable< R( A, B, C, ... ) > : public _EngineArgumentTypeTable< R( A, B, C ) > +{ + static const bool VARIADIC = true; + _EngineArgumentTypeTable() {} +}; + +template< typename R, typename A, typename B, typename C, typename D > +struct _EngineArgumentTypeTable< R( A, B, C, D ) > : public EngineArgumentTypeTable +{ + static const U32 NUM_ARGUMENTS = 4; + static const bool VARIADIC = false; + static const EngineTypeInfo* const RETURN; + static const EngineTypeInfo* const ARGS[ 4 ]; + + _EngineArgumentTypeTable() + : EngineArgumentTypeTable( TYPE< typename EngineTypeTraits< R >::Type >(), NUM_ARGUMENTS, ARGS ) {} +}; +template< typename R, typename A, typename B, typename C, typename D > +const EngineTypeInfo* const _EngineArgumentTypeTable< R( A, B, C, D ) >::RETURN = TYPE< typename EngineTypeTraits< R >::Type >(); +template< typename R, typename A, typename B, typename C, typename D > +const EngineTypeInfo* const _EngineArgumentTypeTable< R( A, B, C, D ) >::ARGS[ 4 ] = +{ + TYPE< typename EngineTypeTraits< A >::Type >(), + TYPE< typename EngineTypeTraits< B >::Type >(), + TYPE< typename EngineTypeTraits< C >::Type >(), + TYPE< typename EngineTypeTraits< D >::Type >() +}; +template< typename R, typename A, typename B, typename C, typename D > +struct _EngineArgumentTypeTable< R( A, B, C, D, ... ) > : public _EngineArgumentTypeTable< R( A, B, C, D ) > +{ + static const bool VARIADIC = true; + _EngineArgumentTypeTable() {} +}; + +template< typename R, typename A, typename B, typename C, typename D, typename E > +struct _EngineArgumentTypeTable< R( A, B, C, D, E ) > : public EngineArgumentTypeTable +{ + static const U32 NUM_ARGUMENTS = 5; + static const bool VARIADIC = false; + static const EngineTypeInfo* const RETURN; + static const EngineTypeInfo* const ARGS[ 5 ]; + + _EngineArgumentTypeTable() + : EngineArgumentTypeTable( TYPE< typename EngineTypeTraits< R >::Type >(), NUM_ARGUMENTS, ARGS ) {} +}; +template< typename R, typename A, typename B, typename C, typename D, typename E > +const EngineTypeInfo* const _EngineArgumentTypeTable< R( A, B, C, D, E ) >::RETURN = TYPE< typename EngineTypeTraits< R >::Type >(); +template< typename R, typename A, typename B, typename C, typename D, typename E > +const EngineTypeInfo* const _EngineArgumentTypeTable< R( A, B, C, D, E ) >::ARGS[ 5 ] = +{ + TYPE< typename EngineTypeTraits< A >::Type >(), + TYPE< typename EngineTypeTraits< B >::Type >(), + TYPE< typename EngineTypeTraits< C >::Type >(), + TYPE< typename EngineTypeTraits< D >::Type >(), + TYPE< typename EngineTypeTraits< E >::Type >() +}; +template< typename R, typename A, typename B, typename C, typename D, typename E > +struct _EngineArgumentTypeTable< R( A, B, C, D, E, ... ) > : public _EngineArgumentTypeTable< R( A, B, C, D, E ) > +{ + static const bool VARIADIC = true; + _EngineArgumentTypeTable() {} +}; + +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F > +struct _EngineArgumentTypeTable< R( A, B, C, D, E, F ) > : public EngineArgumentTypeTable +{ + static const U32 NUM_ARGUMENTS = 6; + static const bool VARIADIC = false; + static const EngineTypeInfo* const RETURN; + static const EngineTypeInfo* const ARGS[ 6 ]; + + _EngineArgumentTypeTable() + : EngineArgumentTypeTable( TYPE< typename EngineTypeTraits< R >::Type >(), NUM_ARGUMENTS, ARGS ) {} +}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F > +const EngineTypeInfo* const _EngineArgumentTypeTable< R( A, B, C, D, E, F ) >::RETURN = TYPE< typename EngineTypeTraits< R >::Type >(); +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F > +const EngineTypeInfo* const _EngineArgumentTypeTable< R( A, B, C, D, E, F ) >::ARGS[ 6 ] = +{ + TYPE< typename EngineTypeTraits< A >::Type >(), + TYPE< typename EngineTypeTraits< B >::Type >(), + TYPE< typename EngineTypeTraits< C >::Type >(), + TYPE< typename EngineTypeTraits< D >::Type >(), + TYPE< typename EngineTypeTraits< E >::Type >(), + TYPE< typename EngineTypeTraits< F >::Type >() +}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F > +struct _EngineArgumentTypeTable< R( A, B, C, D, E, F, ... ) > : public _EngineArgumentTypeTable< R( A, B, C, D, E, F ) > +{ + static const bool VARIADIC = true; + _EngineArgumentTypeTable() {} +}; + +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G > +struct _EngineArgumentTypeTable< R( A, B, C, D, E, F, G ) > : public EngineArgumentTypeTable +{ + static const U32 NUM_ARGUMENTS = 7; + static const bool VARIADIC = false; + static const EngineTypeInfo* const RETURN; + static const EngineTypeInfo* const ARGS[ 7 ]; + + _EngineArgumentTypeTable() + : EngineArgumentTypeTable( TYPE< typename EngineTypeTraits< R >::Type >(), NUM_ARGUMENTS, ARGS ) {} +}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G > +const EngineTypeInfo* const _EngineArgumentTypeTable< R( A, B, C, D, E, F, G ) >::RETURN = TYPE< typename EngineTypeTraits< R >::Type >(); +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G > +const EngineTypeInfo* const _EngineArgumentTypeTable< R( A, B, C, D, E, F, G ) >::ARGS[ 7 ] = +{ + TYPE< typename EngineTypeTraits< A >::Type >(), + TYPE< typename EngineTypeTraits< B >::Type >(), + TYPE< typename EngineTypeTraits< C >::Type >(), + TYPE< typename EngineTypeTraits< D >::Type >(), + TYPE< typename EngineTypeTraits< E >::Type >(), + TYPE< typename EngineTypeTraits< F >::Type >(), + TYPE< typename EngineTypeTraits< G >::Type >() +}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G > +struct _EngineArgumentTypeTable< R( A, B, C, D, E, F, G, ... ) > : public _EngineArgumentTypeTable< R( A, B, C, D, E, F, G ) > +{ + static const bool VARIADIC = true; + _EngineArgumentTypeTable() {} +}; + +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H > +struct _EngineArgumentTypeTable< R( A, B, C, D, E, F, G, H ) > : public EngineArgumentTypeTable +{ + static const U32 NUM_ARGUMENTS = 8; + static const bool VARIADIC = false; + static const EngineTypeInfo* const RETURN; + static const EngineTypeInfo* const ARGS[ 8 ]; + + _EngineArgumentTypeTable() + : EngineArgumentTypeTable( TYPE< typename EngineTypeTraits< R >::Type >(), NUM_ARGUMENTS, ARGS ) {} +}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H > +const EngineTypeInfo* const _EngineArgumentTypeTable< R( A, B, C, D, E, F, G, H ) >::RETURN = TYPE< typename EngineTypeTraits< R >::Type >(); +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H > +const EngineTypeInfo* const _EngineArgumentTypeTable< R( A, B, C, D, E, F, G, H ) >::ARGS[ 8 ] = +{ + TYPE< typename EngineTypeTraits< A >::Type >(), + TYPE< typename EngineTypeTraits< B >::Type >(), + TYPE< typename EngineTypeTraits< C >::Type >(), + TYPE< typename EngineTypeTraits< D >::Type >(), + TYPE< typename EngineTypeTraits< E >::Type >(), + TYPE< typename EngineTypeTraits< F >::Type >(), + TYPE< typename EngineTypeTraits< G >::Type >(), + TYPE< typename EngineTypeTraits< H >::Type >() +}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H > +struct _EngineArgumentTypeTable< R( A, B, C, D, E, F, G, H, ... ) > : public _EngineArgumentTypeTable< R( A, B, C, D, E, F, G, H ) > +{ + static const bool VARIADIC = true; + _EngineArgumentTypeTable() {} +}; + +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I > +struct _EngineArgumentTypeTable< R( A, B, C, D, E, F, G, H, I ) > : public EngineArgumentTypeTable +{ + static const U32 NUM_ARGUMENTS = 9; + static const bool VARIADIC = false; + static const EngineTypeInfo* const RETURN; + static const EngineTypeInfo* const ARGS[ 9 ]; + + _EngineArgumentTypeTable() + : EngineArgumentTypeTable( TYPE< typename EngineTypeTraits< R >::Type >(), NUM_ARGUMENTS, ARGS ) {} +}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I > +const EngineTypeInfo* const _EngineArgumentTypeTable< R( A, B, C, D, E, F, G, H, I ) >::RETURN = TYPE< typename EngineTypeTraits< R >::Type >(); +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I > +const EngineTypeInfo* const _EngineArgumentTypeTable< R( A, B, C, D, E, F, G, H, I ) >::ARGS[ 9 ] = +{ + TYPE< typename EngineTypeTraits< A >::Type >(), + TYPE< typename EngineTypeTraits< B >::Type >(), + TYPE< typename EngineTypeTraits< C >::Type >(), + TYPE< typename EngineTypeTraits< D >::Type >(), + TYPE< typename EngineTypeTraits< E >::Type >(), + TYPE< typename EngineTypeTraits< F >::Type >(), + TYPE< typename EngineTypeTraits< G >::Type >(), + TYPE< typename EngineTypeTraits< H >::Type >(), + TYPE< typename EngineTypeTraits< I >::Type >() +}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I > +struct _EngineArgumentTypeTable< R( A, B, C, D, E, F, G, H, I, ... ) > : public _EngineArgumentTypeTable< R( A, B, C, D, E, F, G, H, I ) > +{ + static const bool VARIADIC = true; + _EngineArgumentTypeTable() {} +}; + +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J > +struct _EngineArgumentTypeTable< R( A, B, C, D, E, F, G, H, I, J ) > : public EngineArgumentTypeTable +{ + static const U32 NUM_ARGUMENTS = 10; + static const bool VARIADIC = false; + static const EngineTypeInfo* const RETURN; + static const EngineTypeInfo* const ARGS[ 10 ]; + + _EngineArgumentTypeTable() + : EngineArgumentTypeTable( TYPE< typename EngineTypeTraits< R >::Type >(), NUM_ARGUMENTS, ARGS ) {} +}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J > +const EngineTypeInfo* const _EngineArgumentTypeTable< R( A, B, C, D, E, F, G, H, I, J ) >::RETURN = TYPE< typename EngineTypeTraits< R >::Type >(); +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J > +const EngineTypeInfo* const _EngineArgumentTypeTable< R( A, B, C, D, E, F, G, H, I, J ) >::ARGS[ 10 ] = +{ + TYPE< typename EngineTypeTraits< A >::Type >(), + TYPE< typename EngineTypeTraits< B >::Type >(), + TYPE< typename EngineTypeTraits< C >::Type >(), + TYPE< typename EngineTypeTraits< D >::Type >(), + TYPE< typename EngineTypeTraits< E >::Type >(), + TYPE< typename EngineTypeTraits< F >::Type >(), + TYPE< typename EngineTypeTraits< G >::Type >(), + TYPE< typename EngineTypeTraits< H >::Type >(), + TYPE< typename EngineTypeTraits< I >::Type >(), + TYPE< typename EngineTypeTraits< J >::Type >() +}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J > +struct _EngineArgumentTypeTable< R( A, B, C, D, E, F, G, H, I, J, ... ) > : public _EngineArgumentTypeTable< R( A, B, C, D, E, F, G, H, I, J ) > +{ + static const bool VARIADIC = true; + _EngineArgumentTypeTable() {} +}; + +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K > +struct _EngineArgumentTypeTable< R( A, B, C, D, E, F, G, H, I, J, K ) > : public EngineArgumentTypeTable +{ + static const U32 NUM_ARGUMENTS = 11; + static const bool VARIADIC = false; + static const EngineTypeInfo* const RETURN; + static const EngineTypeInfo* const ARGS[ 11 ]; + + _EngineArgumentTypeTable() + : EngineArgumentTypeTable( TYPE< typename EngineTypeTraits< R >::Type >(), NUM_ARGUMENTS, ARGS ) {} +}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K > +const EngineTypeInfo* const _EngineArgumentTypeTable< R( A, B, C, D, E, F, G, H, I, J, K ) >::RETURN = TYPE< typename EngineTypeTraits< R >::Type >(); +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K > +const EngineTypeInfo* const _EngineArgumentTypeTable< R( A, B, C, D, E, F, G, H, I, J, K ) >::ARGS[ 11 ] = +{ + TYPE< typename EngineTypeTraits< A >::Type >(), + TYPE< typename EngineTypeTraits< B >::Type >(), + TYPE< typename EngineTypeTraits< C >::Type >(), + TYPE< typename EngineTypeTraits< D >::Type >(), + TYPE< typename EngineTypeTraits< E >::Type >(), + TYPE< typename EngineTypeTraits< F >::Type >(), + TYPE< typename EngineTypeTraits< G >::Type >(), + TYPE< typename EngineTypeTraits< H >::Type >(), + TYPE< typename EngineTypeTraits< I >::Type >(), + TYPE< typename EngineTypeTraits< J >::Type >(), + TYPE< typename EngineTypeTraits< K >::Type >() +}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K > +struct _EngineArgumentTypeTable< R( A, B, C, D, E, F, G, H, I, J, K, ... ) > : public _EngineArgumentTypeTable< R( A, B, C, D, E, F, G, H, I, J, K ) > +{ + static const bool VARIADIC = true; + _EngineArgumentTypeTable() {} +}; + +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K, typename L > +struct _EngineArgumentTypeTable< R( A, B, C, D, E, F, G, H, I, J, K, L ) > : public EngineArgumentTypeTable +{ + static const U32 NUM_ARGUMENTS = 12; + static const bool VARIADIC = false; + static const EngineTypeInfo* const RETURN; + static const EngineTypeInfo* const ARGS[ 12 ]; + + _EngineArgumentTypeTable() + : EngineArgumentTypeTable( TYPE< typename EngineTypeTraits< R >::Type >(), NUM_ARGUMENTS, ARGS ) {} +}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K, typename L > +const EngineTypeInfo* const _EngineArgumentTypeTable< R( A, B, C, D, E, F, G, H, I, J, K, L ) >::RETURN = TYPE< typename EngineTypeTraits< R >::Type >(); +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K, typename L > +const EngineTypeInfo* const _EngineArgumentTypeTable< R( A, B, C, D, E, F, G, H, I, J, K, L ) >::ARGS[ 12 ] = +{ + TYPE< typename EngineTypeTraits< A >::Type >(), + TYPE< typename EngineTypeTraits< B >::Type >(), + TYPE< typename EngineTypeTraits< C >::Type >(), + TYPE< typename EngineTypeTraits< D >::Type >(), + TYPE< typename EngineTypeTraits< E >::Type >(), + TYPE< typename EngineTypeTraits< F >::Type >(), + TYPE< typename EngineTypeTraits< G >::Type >(), + TYPE< typename EngineTypeTraits< H >::Type >(), + TYPE< typename EngineTypeTraits< I >::Type >(), + TYPE< typename EngineTypeTraits< J >::Type >(), + TYPE< typename EngineTypeTraits< K >::Type >(), + TYPE< typename EngineTypeTraits< L >::Type >() +}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K, typename L > +struct _EngineArgumentTypeTable< R( A, B, C, D, E, F, G, H, I, J, K, L, ... ) > : public _EngineArgumentTypeTable< R( A, B, C, D, E, F, G, H, I, J, K, L ) > +{ + static const bool VARIADIC = true; + _EngineArgumentTypeTable() {} +}; + +#endif // !_ENGINETYPEINFO_H_ diff --git a/Engine/source/console/engineTypes.cpp b/Engine/source/console/engineTypes.cpp new file mode 100644 index 000000000..cb5626d3a --- /dev/null +++ b/Engine/source/console/engineTypes.cpp @@ -0,0 +1,30 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/engineTypes.h" +#include "console/engineExports.h" + +const EngineTypeInfo* const _EngineTypeTraits< void >::TYPEINFO = NULL; +EngineExportScope& _GLOBALSCOPE::__engineExportScope() +{ + return EngineExportScope::smGlobalScope; +} diff --git a/Engine/source/console/engineTypes.h b/Engine/source/console/engineTypes.h new file mode 100644 index 000000000..397d67c16 --- /dev/null +++ b/Engine/source/console/engineTypes.h @@ -0,0 +1,602 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _ENGINETYPES_H_ +#define _ENGINETYPES_H_ + +#ifndef _TYPETRAITS_H_ + #include "platform/typetraits.h" +#endif +#ifndef _BITSET_H_ + #include "core/bitSet.h" +#endif + + +//TODO: documentation + + +/// @file +/// This file forms the basis for Torque's engine API type system. +/// +/// The type system is geared towards use in a control layer in a .NET-like environment +/// and is intended to allow for all-native data exchange between the engine and its +/// control layer. + + +/// @defgroup engineAPI_types Engine Type System +/// +/// +/// Akin to C++ RTTI (though more thorough in depth), the engine type system provides a reflection layer +/// that can be used to query type properties at run-time. +/// +/// @section engineAPI_kinds Type Kinds +/// +/// Engine types are segregated into kinds. A kind is to a type what a type is to a value, i.e. a value +/// is an instance of a type whereas a type is an instance of a kind. +/// +/// Just as values share behavior through types, types share behavior through kinds. +/// +/// The following kinds are defined: +/// +///
      +///
      Primitive
      +///
      An atomic piece of data like an int, float, etc. +///
      Enums
      +///
      +///
      Bitfields
      +///
      A bitwise combination of enumeration values.
      +///
      Functions
      +///
      +///
      Structs
      +///
      A structured piece of data like a Point3F or MatrixF. Unlike class instances, structs are directly stored +/// in the memory of their owner. Also, structs don't allow inheritance.
      +///
      Classes
      +///
      +///
      +/// +/// @section engineAPI_subtyping Subtyping of Engine Types +/// +/// +/// At the moment, the type system is not unified such that all types are implicitly subtypes to a single root type. +/// This may change in the future. +/// +/// @section engineAPI_defaultConstruction Default Construction +/// +/// All engine types must be default-constructible, i.e. a parameter-less construction of a value of any type +/// in the engine must be successful. Class and struct types are affected the most by this. Classes and structs +/// @b may provide non-default constructors but they @b must provide a default construction routine that always +/// succeeds. +/// +/// @section engineAPI_networking Engine Type Networking +/// +/// @{ + + +class EngineExportScope; +class EngineTypeInfo; +class EnginePropertyTable; + +template< typename T, typename Base > class EngineClassTypeInfo; +template< typename T > class EngineFunctionTypeInfo; + + +//-------------------------------------------------------------------------- +// Type Traits. +//-------------------------------------------------------------------------- + +/// @name Engine Type Traits +/// +/// Type traits allow to access the static properties of a type at compile-time. This +/// is primarily useful for templating in order to make decisions based on static +/// typing. +/// +/// @{ + + +template< typename T > +struct _EngineTypeTraits +{ + // Default type traits corresponding to the semantics of class types. + + typedef T Type; + typedef T* ValueType; + typedef typename Type::SuperType SuperType; + + typedef T* ArgumentValueType; + typedef T* ReturnValueType; + typedef T* DefaultArgumentValueStoreType; + + typedef ReturnValueType ReturnValue; + static ValueType ArgumentToValue( ArgumentValueType val ) { return val; } + + static const EngineTypeInfo* const TYPEINFO; +}; +template< typename T > const EngineTypeInfo* const _EngineTypeTraits< T >::TYPEINFO = &T::_smTypeInfo; + +template<> +struct _EngineTypeTraits< void > +{ + typedef void Type; + typedef void ValueType; + typedef void ReturnValueType; + typedef ReturnValueType ReturnValue; + + static const EngineTypeInfo* const TYPEINFO; +}; + + +template< typename T > +struct EngineTypeTraits : public _EngineTypeTraits< T > {}; + +// Strip off native type modifiers. Doing it like this allows to query type traits on types +// that are not true engine types or value types thereof (like e.g. a reference to a primitive type) +// but it simplifies matters for us. +template< typename T > +struct EngineTypeTraits< T* > : public EngineTypeTraits< T > {}; +template< typename T > +struct EngineTypeTraits< const T* > : public EngineTypeTraits< T > {}; +template< typename T > +struct EngineTypeTraits< T& > : public EngineTypeTraits< T > {}; +template< typename T > +struct EngineTypeTraits< const T& > : public EngineTypeTraits< T > {}; +template< typename T > +struct EngineTypeTraits< const T > : public EngineTypeTraits< T > {}; + + + +/// Return the type info for the given engine type. +template< typename T > +inline const EngineTypeInfo* TYPE() { return EngineTypeTraits< T >::TYPEINFO; } + + +/// Return the type info for the @b static type of the given variable. +template< typename T > +inline const EngineTypeInfo* TYPE( const T& ) +{ + return TYPE< T >(); +} + + +// Base type trait definitions for different type kinds. + +template< typename T > +struct _EnginePrimitiveTypeTraits +{ + typedef T Type; + typedef T ValueType; + typedef void SuperType; + + typedef ValueType ArgumentValueType; + typedef ValueType ReturnValueType; + typedef ValueType DefaultArgumentValueStoreType; + + typedef ReturnValueType ReturnValue; + static ValueType ArgumentToValue( ArgumentValueType val ) { return val; } + + static const EngineTypeInfo* const TYPEINFO; +}; +template< typename T > const EngineTypeInfo* const _EnginePrimitiveTypeTraits< T >::TYPEINFO = TYPE< T >(); + +template< typename T > +struct _EngineEnumTypeTraits +{ + typedef T Type; + typedef T ValueType; + typedef void SuperType; + + typedef ValueType ArgumentValueType; + typedef ValueType ReturnValueType; + typedef ValueType DefaultArgumentValueStoreType; + + typedef ReturnValueType ReturnValue; + static ValueType ArgumentToValue( ArgumentValueType val ) { return val; } + + static const EngineTypeInfo* const TYPEINFO; +}; +template< typename T > const EngineTypeInfo* const _EngineEnumTypeTraits< T >::TYPEINFO = TYPE< T >(); + +template< typename T > +struct _EngineBitfieldTypeTraits +{ + typedef T Type; + typedef T ValueType; + typedef void SuperType; + + typedef ValueType ArgumentValueType; + typedef ValueType ReturnValueType; + typedef ValueType DefaultArgumentValueStoreType; + + typedef ReturnValueType ReturnValue; + static ValueType ArgumentToValue( ArgumentValueType val ) { return val; } + + static const EngineTypeInfo* const TYPEINFO; +}; +template< typename T > const EngineTypeInfo* const _EngineBitfieldTypeTraits< T >::TYPEINFO = TYPE< T >(); + +template< typename T > +struct _EngineStructTypeTraits +{ + typedef T Type; + typedef const T& ValueType; + typedef void SuperType; + + // Structs get passed in as pointers and passed out as full copies. + typedef T* ArgumentValueType; + typedef T ReturnValueType; + typedef T DefaultArgumentValueStoreType; + + typedef ReturnValueType ReturnValue; + static ValueType ArgumentToValue( ArgumentValueType val ) { return *val; } + + static const EngineTypeInfo* const TYPEINFO; +}; +template< typename T > const EngineTypeInfo* const _EngineStructTypeTraits< T >::TYPEINFO = TYPE< T >(); + + +template< typename T > struct _EngineArgumentTypeTable {}; + +template< typename T > +struct _EngineFunctionTypeTraits +{ + typedef T Type; + typedef T* ValueType; + + typedef T* ArgumentValueType; + typedef T* ReturnValueType; + typedef T* DefaultArgumentValueStoreType; + + static const bool IS_VARIADIC = _EngineArgumentTypeTable< T >::VARIADIC; + static const U32 NUM_ARGUMENTS = _EngineArgumentTypeTable< T >::NUM_ARGUMENTS; + + static const EngineTypeInfo* const TYPEINFO; + + private: + static const EngineFunctionTypeInfo< T > smTYPEINFO; +}; +template< typename T > const EngineTypeInfo* const _EngineFunctionTypeTraits< T >::TYPEINFO = &smTYPEINFO; + + +// Temporary helper. Used to strip the return or argument types supplied in a function type +// down to the value type and thus account for the discrepancy between (argument/return) value +// types and engine types. Don't go for the base type as VC will not allow us to use abstract +// types even in return or argument positions of abstract function types (GCC allows it; not +// sure what the stance of the ANSI C++ standard towards this is). Also, while it may be tempting +// to go for the real return and argument value types here, this would lead to errors as these +// are not guaranteed to be any meaningful value or base types to the engine type system. +#define T( x ) typename EngineTypeTraits< x >::ValueType + +template< typename R > +struct _EngineTypeTraits< R() > : public _EngineFunctionTypeTraits< T( R )() > {}; +template< typename R > +struct _EngineTypeTraits< R( ... ) > : public _EngineFunctionTypeTraits< T( R )( ... ) > {}; +template< typename R, typename A > +struct _EngineTypeTraits< R( A ) > : public _EngineFunctionTypeTraits< T( R )( T( A ) ) > {}; +template< typename R, typename A > +struct _EngineTypeTraits< R( A, ... ) > : public _EngineFunctionTypeTraits< T( R )( T( A ), ... ) > {}; +template< typename R, typename A, typename B > +struct _EngineTypeTraits< R( A, B ) > : public _EngineFunctionTypeTraits< T( R )( T( A ), T( B ) ) > {}; +template< typename R, typename A, typename B > +struct _EngineTypeTraits< R( A, B, ... ) > : public _EngineFunctionTypeTraits< T( R )( T( A ), T( B ), ... ) > {}; +template< typename R, typename A, typename B, typename C > +struct _EngineTypeTraits< R( A, B, C ) > : public _EngineFunctionTypeTraits< T( R )( T( A ), T( B ), T( C ) ) > {}; +template< typename R, typename A, typename B, typename C > +struct _EngineTypeTraits< R( A, B, C, ... ) > : public _EngineFunctionTypeTraits< T( R )( T( A ), T( B ), T( C ), ... ) > {}; +template< typename R, typename A, typename B, typename C, typename D > +struct _EngineTypeTraits< R( A, B, C, D ) > : public _EngineFunctionTypeTraits< T( R )( T( A ), T( B ), T( C ), T( D ) ) > {}; +template< typename R, typename A, typename B, typename C, typename D > +struct _EngineTypeTraits< R( A, B, C, D, ... ) > : public _EngineFunctionTypeTraits< T( R )( T( A ), T( B ), T( C ), T( D ), ... ) > {}; +template< typename R, typename A, typename B, typename C, typename D, typename E > +struct _EngineTypeTraits< R( A, B, C, D, E ) > : public _EngineFunctionTypeTraits< T( R )( T( A ), T( B ), T( C ), T( D ), T( E ) ) > {}; +template< typename R, typename A, typename B, typename C, typename D, typename E > +struct _EngineTypeTraits< R( A, B, C, D, E, ... ) > : public _EngineFunctionTypeTraits< T( R )( T( A ), T( B ), T( C ), T( D ), T( E ), ... ) > {}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F > +struct _EngineTypeTraits< R( A, B, C, D, E, F ) > : public _EngineFunctionTypeTraits< T( R )( T( A ), T( B ), T( C ), T( D ), T( E ), T( F ) ) > {}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F > +struct _EngineTypeTraits< R( A, B, C, D, E, F, ... ) > : public _EngineFunctionTypeTraits< T( R )( T( A ), T( B ), T( C ), T( D ), T( E ), T( F ), ... ) > {}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G > +struct _EngineTypeTraits< R( A, B, C, D, E, F, G ) > : public _EngineFunctionTypeTraits< T( R )( T( A ), T( B ), T( C ), T( D ), T( E ), T( F ), T( G ) ) > {}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G > +struct _EngineTypeTraits< R( A, B, C, D, E, F, G, ... ) > : public _EngineFunctionTypeTraits< T( R )( T( A ), T( B ), T( C ), T( D ), T( E ), T( F ), T( G ), ... ) > {}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H > +struct _EngineTypeTraits< R( A, B, C, D, E, F, G, H ) > : public _EngineFunctionTypeTraits< T( R )( T( A ), T( B ), T( C ), T( D ), T( E ), T( F ), T( G ), T( H ) ) > {}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H > +struct _EngineTypeTraits< R( A, B, C, D, E, F, G, H, ... ) > : public _EngineFunctionTypeTraits< T( R )( T( A ), T( B ), T( C ), T( D ), T( E ), T( F ), T( G ), T( H ), ... ) > {}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I > +struct _EngineTypeTraits< R( A, B, C, D, E, F, G, H, I ) > : public _EngineFunctionTypeTraits< T( R )( T( A ), T( B ), T( C ), T( D ), T( E ), T( F ), T( G ), T( H ), T( I ) ) > {}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I > +struct _EngineTypeTraits< R( A, B, C, D, E, F, G, H, I, ... ) > : public _EngineFunctionTypeTraits< T( R )( T( A ), T( B ), T( C ), T( D ), T( E ), T( F ), T( G ), T( H ), T( I ), ... ) > {}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J > +struct _EngineTypeTraits< R( A, B, C, D, E, F, G, H, I, J ) > : public _EngineFunctionTypeTraits< T( R )( T( A ), T( B ), T( C ), T( D ), T( E ), T( F ), T( G ), T( H ), T( I ), T( J ) ) > {}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J > +struct _EngineTypeTraits< R( A, B, C, D, E, F, G, H, I, J, ... ) > : public _EngineFunctionTypeTraits< T( R )( T( A ), T( B ), T( C ), T( D ), T( E ), T( F ), T( G ), T( H ), T( I ), T( J ), ... ) > {}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K > +struct _EngineTypeTraits< R( A, B, C, D, E, F, G, H, I, J, K ) > : public _EngineFunctionTypeTraits< T( R )( T( A ), T( B ), T( C ), T( D ), T( E ), T( F ), T( G ), T( H ), T( I ), T( J ), T( K ) ) > {}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K > +struct _EngineTypeTraits< R( A, B, C, D, E, F, G, H, I, J, K, ... ) > : public _EngineFunctionTypeTraits< T( R )( T( A ), T( B ), T( C ), T( D ), T( E ), T( F ), T( G ), T( H ), T( I ), T( J ), T( K ), ... ) > {}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K, typename L > +struct _EngineTypeTraits< R( A, B, C, D, E, F, G, H, I, J, K, L ) > : public _EngineFunctionTypeTraits< T( R )( T( A ), T( B ), T( C ), T( D ), T( E ), T( F ), T( G ), T( H ), T( I ), T( J ), T( J ), T( L ) ) > {}; +template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K, typename L > +struct _EngineTypeTraits< R( A, B, C, D, E, F, G, H, I, J, K, L, ... ) > : public _EngineFunctionTypeTraits< T( R )( T( A ), T( B ), T( C ), T( D ), T( E ), T( F ), T( G ), T( H ), T( I ), T( J ), T( K ), T( L ), ... ) > {}; + +#undef T + +/// @} + +//-------------------------------------------------------------------------- +// Helpers. +//-------------------------------------------------------------------------- + +namespace _Private { + template< typename T > + struct _InstantiableConcreteClass + { + static bool _construct( void* ptr ) + { + T* p = reinterpret_cast< T* >( ptr ); + constructInPlace( p ); + return true; + } + + static void _destruct( void* ptr ) + { + T* p = reinterpret_cast< T* >( ptr ); + destructInPlace( p ); + } + }; + template< typename T > + struct _NonInstantiableConcreteClass + { + static bool _construct( void* ptr ) + { + AssertFatal( false, "EngineClassTypeInfo - constructInstance called on non-instantiable class" ); + return false; + } + + static void _destruct( void* ptr ) + { + AssertFatal( false, "EngineClassTypeInfo - destructInstance called on non-instantiable class" ); + } + }; + template< typename T > + struct _ConcreteClassBase : public IfTrueType< typename T::__IsInstantiableType, _InstantiableConcreteClass< T >, _NonInstantiableConcreteClass< T > > + { + typedef ::FalseType IsAbstractType; + }; + template< typename T > + struct _AbstractClassBase + { + typedef ::TrueType IsAbstractType; + static bool _construct( void* ptr ) + { + AssertFatal( false, "EngineClassTypeInfo - constructInstance called on abstract class" ); + return false; + } + + static void _destruct( void* ptr ) + { + AssertFatal( false, "EngineClassTypeInfo - destructInstance called on abstract class" ); + } + }; +} + +//-------------------------------------------------------------------------- +// Macros. +//-------------------------------------------------------------------------- + +/// +/// +#define DECLARE_SCOPE( name ) \ + struct name { \ + static EngineExportScope __engineExportScopeInst; \ + static EngineExportScope& __engineExportScope() { return __engineExportScopeInst; } \ + }; + +/// +#define IMPLEMENT_SCOPE( name, exportName, scope, doc ) \ + EngineExportScope name::__engineExportScopeInst( #exportName, &_SCOPE< scope >()(), doc ); + + +#define _DECLARE_TYPE( type ) \ + template<> extern const EngineTypeInfo* TYPE< type >(); \ + template<> struct _SCOPE< type > { \ + EngineExportScope& operator()() const { \ + return *reinterpret_cast< EngineExportScope* >( \ + const_cast< EngineTypeInfo* >( ( TYPE< type >() ) ) \ + ); \ + } \ + }; + +// Unlike the other types, primitives directly specialize on EngineTypeTraits instead +// of _EngineTypeTraits so as to prevent any stripping from taking place. Otherwise, +// pointers could not be primitive types. +#define _DECLARE_PRIMITIVE( type ) \ + _DECLARE_TYPE( type ) \ + template<> \ + struct EngineTypeTraits< type > : public _EnginePrimitiveTypeTraits< type > {}; + +#define _DECLARE_ENUM( type ) \ + _DECLARE_TYPE( type ) \ + template<> \ + struct _EngineTypeTraits< type > : public _EngineEnumTypeTraits< type > {}; + +#define _DECLARE_BITFIELD( type ) \ + _DECLARE_TYPE( type ) \ + template<> \ + struct _EngineTypeTraits< type > : public _EngineBitfieldTypeTraits< type > {}; + +#define _DECLARE_STRUCT( type ) \ + _DECLARE_TYPE( type ) \ + template<> \ + struct _EngineTypeTraits< type > : public _EngineStructTypeTraits< type > {}; + + +#define _IMPLEMENT_TYPE( type, exportName ) \ + template<> \ + const EngineTypeInfo* TYPE< type >() \ + { \ + return &_ ## exportName::gsTypeInfo; \ + } + + +#define _IMPLEMENT_PRIMITIVE( type, exportName, scope, doc ) \ + namespace { namespace _ ## exportName { \ + static EngineSimpleTypeInfo< type > gsTypeInfo( #exportName, &_SCOPE< scope >()(), EngineTypeKindPrimitive, doc ); \ + } } \ + _IMPLEMENT_TYPE( type, exportName ) + +#define _IMPLEMENT_ENUM( type, exportName, scope, doc ) \ + namespace { namespace _ ## exportName { \ + typedef type EnumType; \ + extern EngineSimpleTypeInfo< EnumType > gsTypeInfo; \ + } } \ + _IMPLEMENT_TYPE( type, exportName ); \ + namespace { namespace _ ## exportName { \ + static const char* const _sEnumName = #exportName; \ + static const char* const _sDoc = doc; \ + static EngineExportScope& _sScope = _SCOPE< scope >()(); \ + static EngineEnumTable::Value _sEnums[] = { + +#define _END_IMPLEMENT_ENUM \ + }; \ + static EngineEnumTable _sEnumTable( sizeof( _sEnums ) / sizeof( _sEnums[ 0 ] ), _sEnums ); \ + EngineSimpleTypeInfo< EnumType > gsTypeInfo( _sEnumName, &_sScope, EngineTypeKindEnum, _sDoc, &_sEnumTable ); \ + } } + +#define _IMPLEMENT_BITFIELD( type, exportName, scope, doc ) \ + namespace { namespace _ ## exportName { \ + extern EngineSimpleTypeInfo< type > gsTypeInfo; \ + } } \ + _IMPLEMENT_TYPE( type, exportName ); \ + namespace { namespace _ ## exportName { \ + typedef type BitfieldType; \ + static const char* const _sBitfieldName = #exportName; \ + static const char* const _sDoc = doc; \ + static EngineExportScope& _sScope = _SCOPE< scope >()(); \ + static EngineEnumTable::Value _sEnums[] = { + +#define _END_IMPLEMENT_BITFIELD \ + }; \ + static EngineEnumTable _sEnumTable( sizeof( _sEnums ) / sizeof( _sEnums[ 0 ] ), _sEnums ); \ + EngineSimpleTypeInfo< BitfieldType > gsTypeInfo( _sBitfieldName, &_sScope, EngineTypeKindBitfield, _sDoc, &_sEnumTable ); \ + } } + +#define _IMPLEMENT_STRUCT( type, exportName, scope, doc ) \ + namespace { namespace _ ## exportName { \ + extern EngineStructTypeInfo< type > gsTypeInfo; \ + } } \ + _IMPLEMENT_TYPE( type, exportName ); \ + namespace { namespace _ ## exportName { \ + typedef type StructType; \ + typedef StructType ThisType; \ + static const char* const _sStructName = #exportName; \ + static const char* const _sDoc = doc; \ + static EngineExportScope& _sScope = _SCOPE< scope >()(); \ + static EngineFieldTable::Field _sFields[] = { + +#define _END_IMPLEMENT_STRUCT \ + { NULL } \ + }; \ + static EngineFieldTable _sFieldTable( sizeof( _sFields ) / sizeof( _sFields[ 0 ] ) - 1, _sFields ); \ + EngineStructTypeInfo< StructType > gsTypeInfo( _sStructName, &_sScope, _sDoc, &_sFieldTable ); \ + } } + + +/// +#define DECLARE_PRIMITIVE( type ) \ + _DECLARE_PRIMITIVE( type ) + +/// +#define IMPLEMENT_PRIMITIVE( type, exportName, scope, doc ) \ + _IMPLEMENT_PRIMITIVE( type, exportName, scope, doc ) + +/// +#define DECLARE_ENUM( type ) \ + _DECLARE_ENUM( type ) + +/// +#define DECLARE_BITFIELD( type ) \ + _DECLARE_BITFIELD( type ) + +/// +#define IMPLEMENT_ENUM( type, exportName, scope, doc ) \ + _IMPLEMENT_ENUM( type, exportName, scope, doc ) + +/// +#define IMPLEMENT_BITFIELD( type, exportName, scope, doc ) \ + _IMPLEMENT_BITFIELD( type, exportName, scope, doc ) + +/// +#define END_IMPLEMENT_ENUM \ + _END_IMPLEMENT_ENUM + +/// +#define END_IMPLEMENT_BITFIELD \ + _END_IMPLEMENT_BITFIELD + +/// +#define DECLARE_STRUCT( type ) \ + _DECLARE_STRUCT( type ) + +/// +#define IMPLEMENT_STRUCT( type, exportName, scope, doc ) \ + _IMPLEMENT_STRUCT( type, exportName, scope, doc ) + +/// +#define END_IMPLEMENT_STRUCT \ + _END_IMPLEMENT_STRUCT + + +/// +#define FIELD( fieldName, exportName, numElements, doc ) \ + { #exportName, doc, numElements, TYPE( ( ( ThisType* ) 16 )->fieldName ), FIELDOFFSET( fieldName ) }, // Artificial offset to avoid compiler warnings. + +/// +#define FIELD_AS( type, fieldName, exportName, numElements, doc ) \ + { #exportName, doc, numElements, TYPE( *( ( type* ) &( ( ThisType* ) 16 )->fieldName ) ), FIELDOFFSET( fieldName ) }, // Artificial offset to avoid compiler warnings. + +/// +#define FIELDOFFSET( fieldName ) \ + U32( ( ( const char* ) &( ( ( ThisType* ) 16 )->fieldName ) ) - 16 ) // Artificial offset to avoid compiler warnings. + +/// +#define CLASSDOC( className, doc ) \ + template<> const char* EngineClassTypeInfo< className, className::_ClassBase >::smDocString = doc; + + +/// @} + + +struct _GLOBALSCOPE +{ + static EngineExportScope& __engineExportScope(); +}; + +// Helper to retrieve a scope instance through a C++ type. The setup here is a little +// contrived to allow the scope parameters to the various macros to be empty. What makes +// this difficult is that T need not necessarily be a structured type. +template< typename T = _GLOBALSCOPE > +struct _SCOPE +{ + EngineExportScope& operator()() const { return T::__engineExportScope(); } +}; + +#endif // !_ENGINETYPES_H_ diff --git a/Engine/source/console/engineXMLExport.cpp b/Engine/source/console/engineXMLExport.cpp new file mode 100644 index 000000000..ce12121e8 --- /dev/null +++ b/Engine/source/console/engineXMLExport.cpp @@ -0,0 +1,558 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/engineExports.h" +#include "console/engineAPI.h" +#include "console/engineTypes.h" +#include "console/engineFunctions.h" +#include "console/SimXMLDocument.h" + + +/// @file +/// A generator that will dump all export structures contained in an engine +/// DLL to an XML file which may then be used by wrapper generators to create a +/// language-specific binding for the engine API. Using XML as an intermediary +/// format allows the generators to use all of the export structures without +/// actually having to access them directly in the DLL as native entities. + + +static void exportScope( const EngineExportScope* scope, SimXMLDocument* xml, bool addNode = false ); + + +static String getTypeName( const EngineTypeInfo* type ) +{ + if( !type ) + { + static String sVoid( "void" ); + return sVoid; + } + + return type->getFullyQualifiedExportName(); +} + +static const char* getDocString( const EngineExport* exportInfo ) +{ + if( !exportInfo->getDocString() ) + return ""; + + return exportInfo->getDocString(); +} + +template< typename T > +inline T getArgValue( const EngineFunctionDefaultArguments* defaultArgs, U32 offset ) +{ + return *reinterpret_cast< const T* >( defaultArgs->getArgs() + offset ); +} + + +// List of exports that we want filtered out. This will only be needed as long +// as the console system is still around. +static const char* sExportFilterList[] = +{ + "Console", // Console namespace +}; + +static bool isExportFiltered( const EngineExport* exportInfo ) +{ + String qualifiedName = exportInfo->getFullyQualifiedExportName(); + + for( U32 i = 0; i < ( sizeof( sExportFilterList ) / sizeof( sExportFilterList[ 0 ] ) ); ++ i ) + if( qualifiedName.compare( sExportFilterList[ i ] ) == 0 ) + return true; + + return false; +} + +//============================================================================= +// Functions. +//============================================================================= +// MARK: ---- Functions ---- + +//----------------------------------------------------------------------------- + +/// Helper to parse argument names out of a prototype string. +static Vector< String > parseFunctionArgumentNames( const EngineFunctionInfo* function ) +{ + Vector< String > argNames; + + const char* prototype = function->getPrototypeString(); + if( !prototype ) + return argNames; + + const U32 prototypeLength = dStrlen( prototype ); + const char* prototypeEnd = &prototype[ prototypeLength ]; + const char* ptr = prototypeEnd - 1; + + // Search for right parenthesis. + while( ptr >= prototype && *ptr != ')' ) + ptr --; + + if( ptr < prototype ) + return argNames; + ptr --; + + while( ptr >= prototype && *ptr != '(' ) + { + // Skip back over spaces. + + while( ptr >= prototype && dIsspace( *ptr ) ) + ptr --; + if( ptr < prototype ) + return argNames; + + // Parse out name. + + const char* end = ptr + 1; + while( ptr > prototype && dIsalnum( *ptr ) ) + ptr --; + const char* start = ptr + 1; + + // Skip back over spaces. + + while( ptr >= prototype && dIsspace( *ptr ) ) + ptr --; + + // If we're sure we don't have just a type name without an + // argument name, copy out the argument name name. + + if( ptr >= prototype && *ptr != ',' && *ptr != '(' && end > start ) + argNames.push_front( String( start, end - start ) ); + else + argNames.push_front( "" ); + + // Skip back to comma or opening parenthesis. + + U32 parenNestingCount = 0; + while( ptr >= prototype ) + { + if( *ptr == ')' ) + parenNestingCount ++; + else if( *ptr == '(' ) + parenNestingCount --; + else if( *ptr == ',' && parenNestingCount == 0 ) + { + ptr --; + break; + } + else if( *ptr == '(' && parenNestingCount == 0 ) + break; + + ptr --; + } + } + + // Add 'this' parameter if this is a method. + + if( dStrncmp( prototype, "virtual ", sizeof( "virtual " ) - 1 ) == 0 ) + argNames.push_front( "this" ); + + return argNames; +} + +//----------------------------------------------------------------------------- + +static String getDefaultArgumentValue( const EngineFunctionInfo* function, const EngineTypeInfo* type, U32 offset ) +{ + String value; + const EngineFunctionDefaultArguments* defaultArgs = function->getDefaultArguments(); + + switch( type->getTypeKind() ) + { + case EngineTypeKindPrimitive: + { + #define PRIMTYPE( tp ) \ + if( TYPE< tp >() == type ) \ + { \ + tp val = getArgValue< tp >( defaultArgs, offset ); \ + value = String::ToString( val ); \ + } + + PRIMTYPE( bool ); + PRIMTYPE( S8 ); + PRIMTYPE( U8 ); + PRIMTYPE( S32 ); + PRIMTYPE( U32 ); + PRIMTYPE( F32 ); + PRIMTYPE( F64 ); + + //TODO: for now we store string literals in ASCII; needs to be sorted out + if( TYPE< const char* >() == type ) + { + const char* val = getArgValue< const char* >( defaultArgs, offset ); + value = val; + } + + #undef PRIMTYPE + break; + } + + case EngineTypeKindEnum: + { + S32 val = getArgValue< S32 >( defaultArgs, offset ); + AssertFatal( type->getEnumTable(), "engineXMLExport - Enum type without table!" ); + + const EngineEnumTable& table = *( type->getEnumTable() ); + const U32 numValues = table.getNumValues(); + + for( U32 i = 0; i < numValues; ++ i ) + if( table[ i ].getInt() == val ) + { + value = table[ i ].getName(); + break; + } + + break; + } + + case EngineTypeKindBitfield: + { + S32 val = getArgValue< S32 >( defaultArgs, offset ); + AssertFatal( type->getEnumTable(), "engineXMLExport - Bitfield type without table!" ); + + const EngineEnumTable& table = *( type->getEnumTable() ); + const U32 numValues = table.getNumValues(); + + bool isFirst = true; + for( U32 i = 0; i < numValues; ++ i ) + if( table[ i ].getInt() & val ) + { + if( !isFirst ) + value += '|'; + + value = table[ i ].getName(); + isFirst = false; + } + + break; + } + + case EngineTypeKindStruct: + { + //TODO: struct type default argument values + break; + } + + case EngineTypeKindClass: + case EngineTypeKindFunction: + { + // For these two kinds, we support "null" as the only valid + // default value. + + const void* ptr = getArgValue< const void* >( defaultArgs, offset ); + if( !ptr ) + value = "null"; + break; + } + + default: + break; + } + + return value; +} + +//----------------------------------------------------------------------------- + +static void exportFunction( const EngineFunctionInfo* function, SimXMLDocument* xml ) +{ + if( isExportFiltered( function ) ) + return; + + xml->pushNewElement( "EngineFunction" ); + + xml->setAttribute( "name", function->getExportName() ); + xml->setAttribute( "returnType", getTypeName( function->getReturnType() ) ); + xml->setAttribute( "symbol", function->getBindingName() ); + xml->setAttribute( "isCallback", function->isCallout() ? "1" : "0" ); + xml->setAttribute( "isVariadic", function->getFunctionType()->isVariadic() ? "1" : "0" ); + xml->setAttribute( "docs", getDocString( function ) ); + + xml->pushNewElement( "arguments" ); + + const U32 numArguments = function->getNumArguments(); + const U32 numDefaultArguments = ( function->getDefaultArguments() ? function->getDefaultArguments()->mNumDefaultArgs : 0 ); + const U32 firstDefaultArg = numArguments - numDefaultArguments; + + Vector< String > argumentNames = parseFunctionArgumentNames( function ); + const U32 numArgumentNames = argumentNames.size(); + + // Accumulated offset in function argument frame vector. + U32 argFrameOffset = 0; + + for( U32 i = 0; i < numArguments; ++ i ) + { + xml->pushNewElement( "EngineFunctionArgument" ); + const EngineTypeInfo* type = function->getArgumentType( i ); + AssertFatal( type != NULL, "exportFunction - Argument cannot have type void!" ); + + String argName; + if( i < numArgumentNames ) + argName = argumentNames[ i ]; + + xml->setAttribute( "name", argName ); + xml->setAttribute( "type", getTypeName( type ) ); + + if( i >= firstDefaultArg ) + { + String defaultValue = getDefaultArgumentValue( function, type, argFrameOffset ); + xml->setAttribute( "defaultValue", defaultValue ); + } + + xml->popElement(); + + if( type->getTypeKind() == EngineTypeKindStruct ) + argFrameOffset += type->getInstanceSize(); + else + argFrameOffset += type->getValueSize(); + + #ifdef _PACK_BUG_WORKAROUNDS + if( argFrameOffset % 4 > 0 ) + argFrameOffset += 4 - ( argFrameOffset % 4 ); + #endif + } + + xml->popElement(); + + xml->popElement(); +} + + +//============================================================================= +// Types. +//============================================================================= +// MARK: ---- Types ---- + +//----------------------------------------------------------------------------- + +static void exportType( const EngineTypeInfo* type, SimXMLDocument* xml ) +{ + // Don't export anonymous types. + if( !type->getTypeName()[ 0 ] ) + return; + + if( isExportFiltered( type ) ) + return; + + const char* nodeName = NULL; + switch( type->getTypeKind() ) + { + case EngineTypeKindPrimitive: + nodeName = "EnginePrimitiveType"; + break; + + case EngineTypeKindEnum: + nodeName = "EngineEnumType"; + break; + + case EngineTypeKindBitfield: + nodeName = "EngineBitfieldType"; + break; + + case EngineTypeKindStruct: + nodeName = "EngineStructType"; + break; + + case EngineTypeKindClass: + nodeName = "EngineClassType"; + break; + + default: + return; + } + + xml->pushNewElement( nodeName ); + + xml->setAttribute( "name", type->getTypeName() ); + xml->setAttribute( "size", String::ToString( type->getInstanceSize() ) ); + xml->setAttribute( "isAbstract", type->isAbstract() ? "1" : "0" ); + xml->setAttribute( "isInstantiable", type->isInstantiable() ? "1" : "0" ); + xml->setAttribute( "isDisposable", type->isDisposable() ? "1" : "0" ); + xml->setAttribute( "isSingleton", type->isSingleton() ? "1" : "0" ); + xml->setAttribute( "docs", getDocString( type ) ); + + if( type->getSuperType() ) + xml->setAttribute( "superType", getTypeName( type->getSuperType() ) ); + + if( type->getEnumTable() ) + { + xml->pushNewElement( "enums" ); + + const EngineEnumTable& table = *( type->getEnumTable() ); + const U32 numValues = table.getNumValues(); + + for( U32 i = 0; i < numValues; ++ i ) + { + xml->pushNewElement( "EngineEnum" ); + + xml->setAttribute( "name", table[ i ].getName() ); + xml->setAttribute( "value", String::ToString( table[ i ].getInt() ) ); + xml->setAttribute( "docs", table[ i ].getDocString() ? table[ i ].getDocString() : "" ); + + xml->popElement(); + } + + xml->popElement(); + } + else if( type->getFieldTable() ) + { + xml->pushNewElement( "fields" ); + + const EngineFieldTable& table = *( type->getFieldTable() ); + const U32 numFields = table.getNumFields(); + + for( U32 i = 0; i < numFields; ++ i ) + { + const EngineFieldTable::Field& field = table[ i ]; + + xml->pushNewElement( "EngineField" ); + + xml->setAttribute( "name", field.getName() ); + xml->setAttribute( "type", getTypeName( field.getType() ) ); + xml->setAttribute( "offset", String::ToString( field.getOffset() ) ); + xml->setAttribute( "indexedSize", String::ToString( field.getNumElements() ) ); + xml->setAttribute( "docs", field.getDocString() ? field.getDocString() : "" ); + + xml->popElement(); + } + + xml->popElement(); + } + else if( type->getPropertyTable() ) + { + xml->pushNewElement( "properties" ); + + const EnginePropertyTable& table = *( type->getPropertyTable() ); + const U32 numProperties = table.getNumProperties(); + U32 groupNestingDepth = 0; + + for( U32 i = 0; i < numProperties; ++ i ) + { + const EnginePropertyTable::Property& property = table[ i ]; + + if( property.isGroupBegin() ) + { + groupNestingDepth ++; + xml->pushNewElement( "EnginePropertyGroup" ); + + xml->setAttribute( "name", property.getName() ); + xml->setAttribute( "indexedSize", String::ToString( property.getNumElements() ) ); + xml->setAttribute( "docs", property.getDocString() ? property.getDocString() : "" ); + + xml->pushNewElement( "properties" ); + } + else if( property.isGroupEnd() ) + { + groupNestingDepth --; + xml->popElement(); + xml->popElement(); + } + else + { + xml->pushNewElement( "EngineProperty" ); + + xml->setAttribute( "name", property.getName() ); + xml->setAttribute( "indexedSize", String::ToString( property.getNumElements() ) ); + xml->setAttribute( "isConstant", property.isConstant() ? "1" : "0" ); + xml->setAttribute( "isTransient", property.isTransient() ? "1" : "0" ); + xml->setAttribute( "isVisible", property.hideInInspectors() ? "0" : "1" ); + xml->setAttribute( "docs", property.getDocString() ? property.getDocString() : "" ); + + xml->popElement(); + } + } + + AssertFatal( !groupNestingDepth, "exportType - Property group nesting mismatch!" ); + xml->popElement(); + } + exportScope( type, xml ); + + xml->popElement(); +} + + +//============================================================================= +// Scopes. +//============================================================================= +// MARK: ---- Scopes ---- + +//----------------------------------------------------------------------------- + +static void exportScope( const EngineExportScope* scope, SimXMLDocument* xml, bool addNode ) +{ + if( addNode ) + { + if( isExportFiltered( scope ) ) + return; + + xml->pushNewElement( "EngineExportScope" ); + + xml->setAttribute( "name", scope->getExportName() ); + xml->setAttribute( "docs", getDocString( scope ) ); + } + + // Dump all contained exports. + + xml->pushNewElement( "exports" ); + + for( const EngineExport* exportInfo = scope->getExports(); exportInfo != NULL; exportInfo = exportInfo->getNextExport() ) + { + switch( exportInfo->getExportKind() ) + { + case EngineExportKindScope: + exportScope( static_cast< const EngineExportScope* >( exportInfo ), xml, true ); + break; + + case EngineExportKindFunction: + exportFunction( static_cast< const EngineFunctionInfo* >( exportInfo ), xml ); + break; + + case EngineExportKindType: + exportType( static_cast< const EngineTypeInfo* >( exportInfo ), xml ); + break; + + default: + break; + } + } + + xml->popElement(); + + if( addNode ) + xml->popElement(); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( exportEngineAPIToXML, SimXMLDocument*, (),, + "Create a XML document containing a dump of the entire exported engine API.\n\n" + "@return A SimXMLDocument containing a dump of the engine's export information or NULL if the operation failed.\n\n" + "@ingroup Console" ) +{ + SimXMLDocument* xml = new SimXMLDocument; + xml->registerObject(); + Sim::getRootGroup()->addObject( xml ); + xml->addHeader(); + + exportScope( EngineExportScope::getGlobalScope(), xml, true ); + + return xml; +} diff --git a/Engine/source/console/fieldBrushObject.cpp b/Engine/source/console/fieldBrushObject.cpp new file mode 100644 index 000000000..0d160ec7c --- /dev/null +++ b/Engine/source/console/fieldBrushObject.cpp @@ -0,0 +1,586 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/strings/stringUnit.h" +#include "console/fieldBrushObject.h" + +// Prefix added to dynamic-fields when they're used to store any copied static-fields when peristing. +#define INTERNAL_FIELD_PREFIX "_fieldBrush_" + +// Size of return buffers used. +const U32 bufferSizes = 1024 * 4; + + +IMPLEMENT_CONOBJECT(FieldBrushObject); + +ConsoleDocClass( FieldBrushObject, + "@brief For static-field copying/pasting, editor use only\n\n" + "@internal" +); + +FieldBrushObject::FieldBrushObject() +{ + // Reset Description. + mDescription = StringTable->insert(""); + mSortName = StringTable->insert(""); +} + + +//----------------------------------------------------------------------------- +// Persist Fields. +//----------------------------------------------------------------------------- +void FieldBrushObject::initPersistFields() +{ + // Add Fields. + addProtectedField("description", TypeCaseString, Offset(mDescription, FieldBrushObject), setDescription, defaultProtectedGetFn, ""); + addProtectedField("sortName", TypeString, Offset(mSortName, FieldBrushObject), setSortName, defaultProtectedGetFn, ""); + + // Call Parent. + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- +// Remove from Sim. +//----------------------------------------------------------------------------- +void FieldBrushObject::onRemove() +{ + // Destroy any fields. + destroyFields(); + + // Call Parent. + Parent::onRemove(); +} + + +//----------------------------------------------------------------------------- +// Destroy any fields. +//----------------------------------------------------------------------------- +void FieldBrushObject::destroyFields() +{ + // Fetch Dynamic-Field Dictionary. + SimFieldDictionary* pFieldDictionary = getFieldDictionary(); + + // Any Field Dictionary? + if ( pFieldDictionary == NULL ) + { + // No, so we're done. + return; + } + + // Iterate fields. + for ( SimFieldDictionaryIterator itr(pFieldDictionary); *itr; ++itr ) + { + // Fetch Field Entry. + SimFieldDictionary::Entry* fieldEntry = *itr; + + // Internal Field? + if ( dStrstr( fieldEntry->slotName, INTERNAL_FIELD_PREFIX ) == fieldEntry->slotName ) + { + // Yes, so remove it. + pFieldDictionary->setFieldValue( fieldEntry->slotName, "" ); + } + } +} + + +//----------------------------------------------------------------------------- +// Suppress Spaces. +//----------------------------------------------------------------------------- +static char replacebuf[1024]; +static char* suppressSpaces(const char* in_pname) +{ + U32 i = 0; + char chr; + do + { + chr = in_pname[i]; + replacebuf[i++] = (chr != 32) ? chr : '_'; + } while(chr); + + return replacebuf; +} + +//----------------------------------------------------------------------------- +// Query Groups. +//----------------------------------------------------------------------------- +ConsoleMethod(FieldBrushObject, queryGroups, const char*, 3, 3, "(simObject) Query available static-field groups for selected object./\n" + "@param simObject Object to query static-field groups on.\n" + "@return Space-seperated static-field group list.") +{ + // Fetch selected object. + SimObject* pSimObject = dynamic_cast( Sim::findObject( argv[2] ) ); + + // Valid object? + if ( pSimObject == NULL ) + { + // No, so warn. + Con::warnf("FieldBrushObject::queryFieldGroups() - Invalid SimObject!"); + return NULL; + } + + // Create Returnable Buffer. + S32 maxBuffer = bufferSizes; + char* pReturnBuffer = Con::getReturnBuffer(bufferSizes); + char* pBuffer = pReturnBuffer; + + // Fetch Field List. + const AbstractClassRep::FieldList& staticFields = pSimObject->getFieldList(); + + // Iterate Fields. + for( U32 fieldIndex = 0; fieldIndex < staticFields.size(); ++fieldIndex ) + { + // Fetch Field. + const AbstractClassRep::Field& staticField = staticFields[fieldIndex]; + + // Start Group? + if ( staticField.type == AbstractClassRep::StartGroupFieldType ) + { + // Yes, so write-out group-name without spaces... + char* pGroupNameNoSpaces = suppressSpaces(staticField.pGroupname); + + // Will the field fit? + // NOTE:- We used "-1" to include the suffix space. + if ( (maxBuffer - (S32)dStrlen(pGroupNameNoSpaces) - 1) >= 0 ) + { + // Yes... + // NOTE:- The group-name does not have the "_begingroup" suffix which should stay hidden. + S32 charsWritten = dSprintf( pBuffer, maxBuffer, "%s ", pGroupNameNoSpaces ); + pBuffer += charsWritten; + maxBuffer -= charsWritten; + } + else + { + // No, so warn. + Con::warnf("FieldBrushObject::queryGroups() - Couldn't fit all groups into return string!"); + break; + } + } + } + + // Strip final space. + if ( pBuffer != pReturnBuffer ) + { + *(pBuffer-1) = 0; + } + + // Return Buffer. + return pReturnBuffer; +} + + +//----------------------------------------------------------------------------- +// Query Fields. +//----------------------------------------------------------------------------- +ConsoleMethod(FieldBrushObject, queryFields, const char*, 3, 4, "(simObject, [groupList]) Query available static-fields for selected object./\n" + "@param simObject Object to query static-fields on.\n" + "@param groupList groups to filter static-fields against.\n" + "@return Space-seperated static-field list.") +{ + // Fetch selected object. + SimObject* pSimObject = dynamic_cast( Sim::findObject( argv[2] ) ); + + // Valid object? + if ( pSimObject == NULL ) + { + // No, so warn. + Con::warnf("FieldBrushObject::queryFields() - Invalid SimObject!"); + return NULL; + } + + // Create Returnable Buffer. + S32 maxBuffer = bufferSizes; + char* pReturnBuffer = Con::getReturnBuffer(bufferSizes); + char* pBuffer = pReturnBuffer; + + // Fetch Field List. + const AbstractClassRep::FieldList& staticFields = pSimObject->getFieldList(); + + // Did we specify a groups list? + if ( argc < 4 ) + { + // No, so return all fields... + + // Iterate fields. + for( U32 fieldIndex = 0; fieldIndex < staticFields.size(); ++fieldIndex ) + { + // Fetch Field. + const AbstractClassRep::Field& staticField = staticFields[fieldIndex]; + + // Standard Field? + if ( staticField.type != AbstractClassRep::StartGroupFieldType && + staticField.type != AbstractClassRep::EndGroupFieldType && + staticField.type != AbstractClassRep::DeprecatedFieldType ) + { + // Yes, so will the field fit? + // NOTE:- We used "-1" to include the suffix space. + if ( (maxBuffer - (S32)dStrlen(staticField.pFieldname) - 1) >= 0 ) + { + // Yes, so write-out field-name. + S32 charsWritten = dSprintf( pBuffer, maxBuffer, "%s ", staticField.pFieldname ); + pBuffer += charsWritten; + maxBuffer -= charsWritten; + } + else + { + // No, so warn. + Con::warnf("FieldBrushObject::queryFields() - Couldn't fit all fields into return string!"); + break; + } + } + } + + // Strip final space. + if ( pBuffer != pReturnBuffer ) + { + *(pBuffer-1) = 0; + } + + // Return field list. + return pReturnBuffer; + } + + // Yes, so filter by groups... + + // Group List. + Vector groups; + // Yes, so fetch group list. + const char* groupList = argv[3]; + // Yes, so calculate group Count. + const U32 groupCount = StringUnit::getUnitCount( groupList, " \t\n" ); + + char tempBuf[256]; + + // Iterate groups... + for ( U32 groupIndex = 0; groupIndex < groupCount; ++groupIndex ) + { + // Copy string element. + dStrcpy( tempBuf, StringUnit::getUnit( groupList, groupIndex, " \t\n" ) ); + // Append internal name. + dStrcat( tempBuf, "_begingroup" ); + // Store Group. + groups.push_back( StringTable->insert( tempBuf ) ); + } + + // Reset Valid Group. + bool validGroup = false; + + // Iterate fields. + for( U32 fieldIndex = 0; fieldIndex < staticFields.size(); ++fieldIndex ) + { + // Fetch Field. + const AbstractClassRep::Field& staticField = staticFields[fieldIndex]; + + // Handle Group Type. + switch( staticField.type ) + { + // Start Group. + case AbstractClassRep::StartGroupFieldType: + { + // Is this group valid? + + // Iterate groups... + for ( U32 groupIndex = 0; groupIndex < groups.size(); ++groupIndex ) + { + // Group selected? + if ( groups[groupIndex] == staticField.pFieldname ) + { + // Yes, so flag as valid. + validGroup = true; + break; + } + } + + } break; + + // End Group. + case AbstractClassRep::EndGroupFieldType: + { + // Reset Valid Group. + validGroup = false; + + } break; + + // Deprecated. + case AbstractClassRep::DeprecatedFieldType: + { + } break; + + // Standard. + default: + { + // Do we have a valid group? + if ( validGroup ) + { + // Yes, so will the field fit? + // NOTE:- We used "-1" to include the suffix space. + if ( (maxBuffer - (S32)dStrlen(staticField.pFieldname) - 1) >= 0 ) + { + // Yes, so write-out field-name. + S32 charsWritten = dSprintf( pBuffer, maxBuffer, "%s ", staticField.pFieldname ); + pBuffer += charsWritten; + maxBuffer -= charsWritten; + } + else + { + // No, so warn. + Con::warnf("FieldBrushObject::queryFields() - Couldn't fit all fields into return string!"); + // HACK: Easy way to finish iterating fields. + fieldIndex = staticFields.size(); + break; + } + } + + } break; + }; + } + + // Strip final space. + if ( pBuffer != pReturnBuffer ) + { + *(pBuffer-1) = 0; + } + + // Return field list. + return pReturnBuffer; +} + +//----------------------------------------------------------------------------- +// Copy Fields. +//----------------------------------------------------------------------------- +ConsoleMethod(FieldBrushObject, copyFields, void, 3, 4, "(simObject, [fieldList]) Copy selected static-fields for selected object./\n" + "@param simObject Object to copy static-fields from.\n" + "@param fieldList fields to filter static-fields against.\n" + "@return No return value.") +{ + // Fetch selected object. + SimObject* pSimObject = dynamic_cast( Sim::findObject( argv[2] ) ); + + // Valid object? + if ( pSimObject == NULL ) + { + // No, so warn. + Con::warnf("FieldBrushObject::copyFields() - Invalid SimObject!"); + return; + } + + // Fetch field list. + const char* pFieldList = (argc > 3 ) ? argv[3] : NULL; + + // Copy Fields. + object->copyFields( pSimObject, pFieldList ); +} +// Copy Fields. +void FieldBrushObject::copyFields( SimObject* pSimObject, const char* fieldList ) +{ + // FieldBrushObject class? + if ( dStrcmp(pSimObject->getClassName(), getClassName()) == 0 ) + { + // Yes, so warn. + Con::warnf("FieldBrushObject::copyFields() - Cannot copy FieldBrushObject objects!"); + return; + } + + char tempBuf[bufferSizes]; + + // Field List. + Vector fields; + + // Fetch valid field-list flag. + bool validFieldList = ( fieldList != NULL ); + + // Did we specify a fields list? + if ( validFieldList ) + { + // Yes, so calculate field Count. + const U32 fieldCount = StringUnit::getUnitCount( fieldList, " \t\n" ); + + // Iterate fields... + for ( U32 fieldIndex = 0; fieldIndex < fieldCount; ++fieldIndex ) + { + // Copy string element. + dStrcpy( tempBuf, StringUnit::getUnit( fieldList, fieldIndex, " \t\n" ) ); + + // Store field. + fields.push_back( StringTable->insert( tempBuf ) ); + } + } + + // Destroy Fields. + destroyFields(); + + // Fetch Field List. + const AbstractClassRep::FieldList& staticFields = pSimObject->getFieldList(); + + // Iterate fields. + for( U32 fieldIndex = 0; fieldIndex < staticFields.size(); ++fieldIndex ) + { + // Fetch Field. + const AbstractClassRep::Field& staticField = staticFields[fieldIndex]; + + // Standard Field? + if ( staticField.type != AbstractClassRep::StartGroupFieldType && + staticField.type != AbstractClassRep::EndGroupFieldType && + staticField.type != AbstractClassRep::DeprecatedFieldType ) + { + // Set field-specified flag. + bool fieldSpecified = !validFieldList; + + // Did we specify a fields list? + if ( validFieldList ) + { + // Yes, so is this field name selected? + + // Iterate fields... + for ( U32 fieldIndex = 0; fieldIndex < fields.size(); ++fieldIndex ) + { + // Field selected? + if ( staticField.pFieldname == fields[fieldIndex] ) + { + // Yes, so flag as such. + fieldSpecified = true; + break; + } + } + } + + // Field specified? + if ( fieldSpecified ) + { + if ( staticField.elementCount <= 1 ) + { + for( U32 fieldElement = 0; S32(fieldElement) < staticField.elementCount; ++fieldElement ) + { + // Fetch Field Value. + const char* fieldValue = (staticField.getDataFn)( pSimObject, Con::getData(staticField.type, (void *) (((const char *)pSimObject) + staticField.offset), fieldElement, staticField.table, staticField.flag) ); + + // Field Value? + if ( fieldValue ) + { + // Yes. + dSprintf( tempBuf, sizeof(tempBuf), INTERNAL_FIELD_PREFIX"%s", staticField.pFieldname ); + + // Fetch Dynamic-Field Dictionary. + SimFieldDictionary* pFieldDictionary = getFieldDictionary(); + + // Set field value. + if ( !pFieldDictionary ) + { + setDataField( StringTable->insert( tempBuf ), NULL, fieldValue ); + } + else + { + pFieldDictionary->setFieldValue( StringTable->insert( tempBuf ), fieldValue ); + } + } + } + } + } + } + } +} + +//----------------------------------------------------------------------------- +// Paste Fields. +//----------------------------------------------------------------------------- +ConsoleMethod(FieldBrushObject, pasteFields, void, 3, 3, "(simObject) Paste copied static-fields to selected object./\n" + "@param simObject Object to paste static-fields to.\n" + "@return No return value.") +{ + // Fetch selected object. + SimObject* pSimObject = dynamic_cast( Sim::findObject( argv[2] ) ); + + // Valid object? + if ( pSimObject == NULL ) + { + // No, so warn. + Con::warnf("FieldBrushObject::pasteFields() - Invalid SimObject!"); + return; + } + + // Paste Fields. + object->pasteFields( pSimObject ); +} +// Paste Fields. +void FieldBrushObject::pasteFields( SimObject* pSimObject ) +{ + // FieldBrushObject class? + if ( dStrcmp(pSimObject->getClassName(), getClassName()) == 0 ) + { + // Yes, so warn. + Con::warnf("FieldBrushObject::pasteFields() - Cannot paste FieldBrushObject objects!"); + return; + } + + // Fetch Dynamic-Field Dictionary. + SimFieldDictionary* pFieldDictionary = getFieldDictionary(); + + // Any Field Dictionary? + if ( pFieldDictionary == NULL ) + { + // No, so we're done. + return; + } + + // Force modification of static-fields on target object! + pSimObject->setModStaticFields( true ); + + // Iterate fields. + for ( SimFieldDictionaryIterator itr(pFieldDictionary); *itr; ++itr ) + { + // Fetch Field Entry. + SimFieldDictionary::Entry* fieldEntry = *itr; + + // Internal Field? + char* pInternalField = dStrstr( fieldEntry->slotName, INTERNAL_FIELD_PREFIX ); + if ( pInternalField == fieldEntry->slotName ) + { + // Yes, so skip the prefix. + pInternalField += dStrlen(INTERNAL_FIELD_PREFIX); + + // Is this a static-field on the target object? + // NOTE:- We're doing this so we don't end-up creating a dynamic-field if it isn't present. + + // Fetch Field List. + const AbstractClassRep::FieldList& staticFields = pSimObject->getFieldList(); + + // Iterate fields. + for( U32 fieldIndex = 0; fieldIndex < staticFields.size(); ++fieldIndex ) + { + // Fetch Field. + const AbstractClassRep::Field& staticField = staticFields[fieldIndex]; + + // Standard Field? + if ( staticField.type != AbstractClassRep::StartGroupFieldType && + staticField.type != AbstractClassRep::EndGroupFieldType && + staticField.type != AbstractClassRep::DeprecatedFieldType ) + { + // Target field? + if ( dStrcmp(staticField.pFieldname, pInternalField) == 0 ) + { + // Yes, so set data. + pSimObject->setDataField( staticField.pFieldname, NULL, fieldEntry->value ); + } + } + } + } + } +} diff --git a/Engine/source/console/fieldBrushObject.h b/Engine/source/console/fieldBrushObject.h new file mode 100644 index 000000000..aeed548cf --- /dev/null +++ b/Engine/source/console/fieldBrushObject.h @@ -0,0 +1,79 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FIELDBRUSHOBJECT_H_ +#define _FIELDBRUSHOBJECT_H_ + +#ifndef _SIM_H_ + #include "console/simObject.h" +#endif +#ifndef _SIMFIELDDICTIONARY_H_ + #include "console/simFieldDictionary.h" +#endif +#ifndef _CONSOLEINTERNAL_H_ + #include "console/consoleInternal.h" +#endif +#ifndef _TDICTIONARY_H_ + #include "core/util/tDictionary.h" +#endif + + +//----------------------------------------------------------------------------- +// Field Brush Object. +//----------------------------------------------------------------------------- + +/// FieldBrushObject for static-field copying/pasting. +/// +class FieldBrushObject : public SimObject +{ +private: + typedef SimObject Parent; + + // Destroy Fields. + void destroyFields( void ); + + StringTableEntry mDescription; ///< Description. + StringTableEntry mSortName; ///< Sort Name. + +public: + FieldBrushObject(); + + void copyFields( SimObject* pSimObject, const char* fieldList ); + void pasteFields( SimObject* pSimObject ); + + static bool setDescription( void *object, const char *index, const char *data ) + { static_cast(object)->setDescription(data); return false; }; + void setDescription( const char* description ) { mDescription = StringTable->insert(description); } + StringTableEntry getDescription(void) const { return mDescription; } + + static bool setSortName( void *object, const char *index, const char *data ) + { static_cast(object)->setSortName(data); return false; }; + void setSortName( const char* sortName ) { mSortName = StringTable->insert(sortName); } + StringTableEntry getSortName(void) const { return mSortName; } + + static void initPersistFields(); ///< Persist Fields. + virtual void onRemove(); ///< Called when the object is removed from the sim. + + DECLARE_CONOBJECT(FieldBrushObject); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/console/fileSystemFunctions.cpp b/Engine/source/console/fileSystemFunctions.cpp new file mode 100644 index 000000000..b4007beb0 --- /dev/null +++ b/Engine/source/console/fileSystemFunctions.cpp @@ -0,0 +1,847 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/console.h" +#include "console/consoleInternal.h" +#include "console/engineAPI.h" +#include "console/ast.h" +#include "core/stream/fileStream.h" +#include "console/compiler.h" +#include "platform/event.h" +#include "platform/platformInput.h" +#include "torqueConfig.h" +#include "core/frameAllocator.h" + +// Buffer for expanding script filenames. +static char sgScriptFilenameBuffer[1024]; + +//-------------------------------------- Helper Functions +static void forwardslash(char *str) +{ + while(*str) + { + if(*str == '\\') + *str = '/'; + str++; + } +} + +//---------------------------------------------------------------- + +static Vector sgFindFilesResults; +static U32 sgFindFilesPos = 0; + +static S32 buildFileList(const char* pattern, bool recurse, bool multiMatch) +{ + static const String sSlash( "/" ); + + sgFindFilesResults.clear(); + + String sPattern(Torque::Path::CleanSeparators(pattern)); + if(sPattern.isEmpty()) + { + Con::errorf("findFirstFile() requires a search pattern"); + return -1; + } + + if(!Con::expandScriptFilename(sgScriptFilenameBuffer, sizeof(sgScriptFilenameBuffer), sPattern.c_str())) + { + Con::errorf("findFirstFile() given initial directory cannot be expanded: '%s'", pattern); + return -1; + } + sPattern = String::ToString(sgScriptFilenameBuffer); + + String::SizeType slashPos = sPattern.find('/', 0, String::Right); +// if(slashPos == String::NPos) +// { +// Con::errorf("findFirstFile() missing search directory or expression: '%s'", sPattern.c_str()); +// return -1; +// } + + // Build the initial search path + Torque::Path givenPath(Torque::Path::CompressPath(sPattern)); + givenPath.setFileName("*"); + givenPath.setExtension("*"); + + if(givenPath.getPath().length() > 0 && givenPath.getPath().find('*', 0, String::Right) == givenPath.getPath().length()-1) + { + // Deal with legacy searches of the form '*/*.*' + String suspectPath = givenPath.getPath(); + String::SizeType newLen = suspectPath.length()-1; + if(newLen > 0 && suspectPath.find('/', 0, String::Right) == suspectPath.length()-2) + { + --newLen; + } + givenPath.setPath(suspectPath.substr(0, newLen)); + } + + Torque::FS::FileSystemRef fs = Torque::FS::GetFileSystem(givenPath); + //Torque::Path path = fs->mapTo(givenPath); + Torque::Path path = givenPath; + + // Make sure that we have a root so the correct file system can be determined when using zips + if(givenPath.isRelative()) + path = Torque::Path::Join(Torque::FS::GetCwd(), '/', givenPath); + + path.setFileName(String::EmptyString); + path.setExtension(String::EmptyString); + if(!Torque::FS::IsDirectory(path)) + { + Con::errorf("findFirstFile() invalid initial search directory: '%s'", path.getFullPath().c_str()); + return -1; + } + + // Build the search expression + const String expression(slashPos != String::NPos ? sPattern.substr(slashPos+1) : sPattern); + if(expression.isEmpty()) + { + Con::errorf("findFirstFile() requires a search expression: '%s'", sPattern.c_str()); + return -1; + } + + S32 results = Torque::FS::FindByPattern(path, expression, recurse, sgFindFilesResults, multiMatch ); + if(givenPath.isRelative() && results > 0) + { + // Strip the CWD out of the returned paths + // MakeRelativePath() returns incorrect results (it adds a leading ..) so doing this the dirty way + const String cwd = Torque::FS::GetCwd().getFullPath(); + for(S32 i = 0;i < sgFindFilesResults.size();++i) + { + String str = sgFindFilesResults[i]; + if(str.compare(cwd, cwd.length(), String::NoCase) == 0) + str = str.substr(cwd.length()); + sgFindFilesResults[i] = str; + } + } + return results; +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( findFirstFile, String, ( const char* pattern, bool recurse ), ( true ), + "@brief Returns the first file in the directory system matching the given pattern.\n\n" + + "Use the corresponding findNextFile() to step through " + "the results. If you're only interested in the number of files returned by the " + "pattern match, use getFileCount().\n\n" + + "This function differs from findFirstFileMultiExpr() in that it supports a single search " + "pattern being passed in.\n\n" + + "@note You cannot run multiple simultaneous file system searches with these functions. Each " + "call to findFirstFile() and findFirstFileMultiExpr() initiates a new search and renders " + "a previous search invalid.\n\n" + + "@param pattern The path and file name pattern to match against.\n" + "@param recurse If true, the search will exhaustively recurse into subdirectories of the given path and match the given filename pattern.\n" + "@return The path of the first file matched by the search or an empty string if no matching file could be found.\n\n" + + "@tsexample\n" + "// Execute all .cs files in a subdirectory and its subdirectories.\n" + "for( %file = findFirstFile( \"subdirectory/*.cs\" ); %file !$= \"\"; %file = findNextFile() )\n" + " exec( %file );\n" + "@endtsexample\n\n" + + "@see findNextFile()" + "@see getFileCount()" + "@see findFirstFileMultiExpr()" + "@ingroup FileSearches" ) +{ + S32 numResults = buildFileList( pattern, recurse, false); + + // For Debugging + //for ( S32 i = 0; i < sgFindFilesResults.size(); i++ ) + // Con::printf( " [%i] [%s]", i, sgFindFilesResults[i].c_str() ); + + sgFindFilesPos = 1; + + if(numResults < 0) + { + Con::errorf("findFirstFile() search directory not found: '%s'", pattern); + return String(); + } + + return numResults ? sgFindFilesResults[0] : String(); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( findNextFile, String, ( const char* pattern ), ( "" ), + "@brief Returns the next file matching a search begun in findFirstFile().\n\n" + + "@param pattern The path and file name pattern to match against. This is optional " + "and may be left out as it is not used by the code. It is here for legacy reasons.\n" + "@return The path of the next filename matched by the search or an empty string if no more files match.\n\n" + + "@tsexample\n" + "// Execute all .cs files in a subdirectory and its subdirectories.\n" + "for( %file = findFirstFile( \"subdirectory/*.cs\" ); %file !$= \"\"; %file = findNextFile() )\n" + " exec( %file );\n" + "@endtsexample\n\n" + + "@see findFirstFile()" + "@ingroup FileSearches" ) +{ + if ( sgFindFilesPos + 1 > sgFindFilesResults.size() ) + return String(); + + return sgFindFilesResults[sgFindFilesPos++]; +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( getFileCount, S32, ( const char* pattern, bool recurse ), ( true ), + "@brief Returns the number of files in the directory tree that match the given patterns\n\n" + + "This function differs from getFileCountMultiExpr() in that it supports a single search " + "pattern being passed in.\n\n" + + "If you're interested in a list of files that match the given pattern and not just " + "the number of files, use findFirstFile() and findNextFile().\n\n" + + "@param pattern The path and file name pattern to match against.\n" + "@param recurse If true, the search will exhaustively recurse into subdirectories of the given path and match the given filename pattern " + "counting files in subdirectories.\n" + "@return Number of files located using the pattern\n\n" + + "@tsexample\n" + "// Count the number of .cs files in a subdirectory and its subdirectories.\n" + "getFileCount( \"subdirectory/*.cs\" );\n" + "@endtsexample\n\n" + + "@see findFirstFile()" + "@see findNextFile()" + "@see getFileCountMultiExpr()" + "@ingroup FileSearches" ) +{ + S32 numResults = buildFileList( pattern, recurse, false ); + + if(numResults < 0) + { + return 0; + } + + return numResults; +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction(findFirstFileMultiExpr, String, ( const char* pattern, bool recurse ), (true), + "@brief Returns the first file in the directory system matching the given patterns.\n\n" + + "Use the corresponding findNextFileMultiExpr() to step through " + "the results. If you're only interested in the number of files returned by the " + "pattern match, use getFileCountMultiExpr().\n\n" + + "This function differs from findFirstFile() in that it supports multiple search patterns " + "to be passed in.\n\n" + + "@note You cannot run multiple simultaneous file system searches with these functions. Each " + "call to findFirstFile() and findFirstFileMultiExpr() initiates a new search and renders " + "a previous search invalid.\n\n" + + "@param pattern The path and file name pattern to match against, such as *.cs. Separate " + "multiple patterns with TABs. For example: \"*.cs\" TAB \"*.dso\"\n" + "@param recurse If true, the search will exhaustively recurse into subdirectories " + "of the given path and match the given filename patterns.\n" + "@return String of the first matching file path, or an empty string if no matching " + "files were found.\n\n" + + "@tsexample\n" + "// Find all DTS or Collada models\n" + "%filePatterns = \"*.dts\" TAB \"*.dae\";\n" + "%fullPath = findFirstFileMultiExpr( %filePatterns );\n" + "while ( %fullPath !$= \"\" )\n" + "{\n" + " echo( %fullPath );\n" + " %fullPath = findNextFileMultiExpr( %filePatterns );\n" + "}\n" + "@endtsexample\n\n" + + "@see findNextFileMultiExpr()" + "@see getFileCountMultiExpr()" + "@see findFirstFile()" + "@ingroup FileSearches") +{ + S32 numResults = buildFileList(pattern, recurse, true); + + // For Debugging + //for ( S32 i = 0; i < sgFindFilesResults.size(); i++ ) + // Con::printf( " [%i] [%s]", i, sgFindFilesResults[i].c_str() ); + + sgFindFilesPos = 1; + + if(numResults < 0) + { + Con::errorf("findFirstFileMultiExpr() search directory not found: '%s'", pattern); + return String(); + } + + return numResults ? sgFindFilesResults[0] : String(); +} + +DefineEngineFunction(findNextFileMultiExpr, String, ( const char* pattern ), (""), + "@brief Returns the next file matching a search begun in findFirstFileMultiExpr().\n\n" + + "@param pattern The path and file name pattern to match against. This is optional " + "and may be left out as it is not used by the code. It is here for legacy reasons.\n" + "@return String of the next matching file path, or an empty string if no matching " + "files were found.\n\n" + + "@tsexample\n" + "// Find all DTS or Collada models\n" + "%filePatterns = \"*.dts\" TAB \"*.dae\";\n" + "%fullPath = findFirstFileMultiExpr( %filePatterns );\n" + "while ( %fullPath !$= \"\" )\n" + "{\n" + " echo( %fullPath );\n" + " %fullPath = findNextFileMultiExpr( %filePatterns );\n" + "}\n" + "@endtsexample\n\n" + + "@see findFirstFileMultiExpr()" + "@ingroup FileSearches") +{ + if ( sgFindFilesPos + 1 > sgFindFilesResults.size() ) + return String(); + + return sgFindFilesResults[sgFindFilesPos++]; +} + +DefineEngineFunction(getFileCountMultiExpr, S32, ( const char* pattern, bool recurse ), (true), + "@brief Returns the number of files in the directory tree that match the given patterns\n\n" + + "If you're interested in a list of files that match the given patterns and not just " + "the number of files, use findFirstFileMultiExpr() and findNextFileMultiExpr().\n\n" + + "@param pattern The path and file name pattern to match against, such as *.cs. Separate " + "multiple patterns with TABs. For example: \"*.cs\" TAB \"*.dso\"\n" + "@param recurse If true, the search will exhaustively recurse into subdirectories " + "of the given path and match the given filename pattern.\n" + "@return Number of files located using the patterns\n\n" + + "@tsexample\n" + "// Count all DTS or Collada models\n" + "%filePatterns = \"*.dts\" TAB \"*.dae\";\n" + "echo( \"Nunmer of shape files:\" SPC getFileCountMultiExpr( %filePatterns ) );\n" + "@endtsexample\n\n" + + "@see findFirstFileMultiExpr()" + "@see findNextFileMultiExpr()" + "@ingroup FileSearches") +{ + S32 numResults = buildFileList(pattern, recurse, true); + + if(numResults < 0) + { + return 0; + } + + return numResults; +} + +DefineEngineFunction(getFileCRC, S32, ( const char* fileName ),, + "@brief Provides the CRC checksum of the given file.\n\n" + + "@param fileName The path to the file.\n" + "@return The calculated CRC checksum of the file, or -1 if the file " + "could not be found.\n" + + "@ingroup FileSystem") +{ + String cleanfilename(Torque::Path::CleanSeparators(fileName)); + Con::expandScriptFilename(sgScriptFilenameBuffer, sizeof(sgScriptFilenameBuffer), cleanfilename.c_str()); + + Torque::Path givenPath(Torque::Path::CompressPath(sgScriptFilenameBuffer)); + Torque::FS::FileNodeRef fileRef = Torque::FS::GetFileNode( givenPath ); + + if ( fileRef == NULL ) + { + Con::errorf("getFileCRC() - could not access file: [%s]", givenPath.getFullPath().c_str() ); + return -1; + } + + return fileRef->getChecksum(); +} + +DefineEngineFunction(isFile, bool, ( const char* fileName ),, + "@brief Determines if the specified file exists or not\n\n" + + "@param fileName The path to the file.\n" + "@return Returns true if the file was found.\n" + + "@ingroup FileSystem") +{ + String cleanfilename(Torque::Path::CleanSeparators(fileName)); + Con::expandScriptFilename(sgScriptFilenameBuffer, sizeof(sgScriptFilenameBuffer), cleanfilename.c_str()); + + Torque::Path givenPath(Torque::Path::CompressPath(sgScriptFilenameBuffer)); + return Torque::FS::IsFile(givenPath); +} + +DefineEngineFunction( IsDirectory, bool, ( const char* directory ),, + "@brief Determines if a specified directory exists or not\n\n" + + "@param directory String containing path in the form of \"foo/bar\"\n" + "@return Returns true if the directory was found.\n" + + "@note Do not include a trailing slash '/'.\n" + + "@ingroup FileSystem") +{ + String dir(Torque::Path::CleanSeparators(directory)); + Con::expandScriptFilename(sgScriptFilenameBuffer, sizeof(sgScriptFilenameBuffer), dir.c_str()); + + Torque::Path givenPath(Torque::Path::CompressPath(sgScriptFilenameBuffer)); + return Torque::FS::IsDirectory( givenPath ); +} + +DefineEngineFunction(isWriteableFileName, bool, ( const char* fileName ),, + "@brief Determines if a file name can be written to using File I/O\n\n" + + "@param fileName Name and path of file to check\n" + "@return Returns true if the file can be written to.\n" + + "@ingroup FileSystem") +{ + String filename(Torque::Path::CleanSeparators(fileName)); + Con::expandScriptFilename(sgScriptFilenameBuffer, sizeof(sgScriptFilenameBuffer), filename.c_str()); + + Torque::Path givenPath(Torque::Path::CompressPath(sgScriptFilenameBuffer)); + Torque::FS::FileSystemRef fs = Torque::FS::GetFileSystem(givenPath); + Torque::Path path = fs->mapTo(givenPath); + + return !Torque::FS::IsReadOnly(path); +} + +DefineEngineFunction(startFileChangeNotifications, void, (),, + "@brief Start watching resources for file changes\n\n" + "Typically this is called during initializeCore().\n\n" + "@see stopFileChangeNotifications()\n" + "@ingroup FileSystem") +{ + Torque::FS::StartFileChangeNotifications(); +} + +DefineEngineFunction(stopFileChangeNotifications, void, (),, + "@brief Stop watching resources for file changes\n\n" + "Typically this is called during shutdownCore().\n\n" + "@see startFileChangeNotifications()\n" + "@ingroup FileSystem") +{ + Torque::FS::StopFileChangeNotifications(); +} + + +DefineEngineFunction(getDirectoryList, String, ( const char* path, S32 depth ), ( 0 ), + "@brief Gathers a list of directories starting at the given path.\n\n" + + "@param path String containing the path of the directory\n" + "@param depth Depth of search, as in how many subdirectories to parse through\n" + "@return Tab delimited string containing list of directories found during search, \"\" if no files were found\n" + + "@ingroup FileSystem") +{ + // Grab the full path. + char fullpath[1024]; + Platform::makeFullPathName(dStrcmp(path, "/") == 0 ? "" : path, fullpath, sizeof(fullpath)); + + //dSprintf(fullpath, 511, "%s/%s", Platform::getWorkingDirectory(), path); + + // Append a trailing backslash if it's not present already. + if (fullpath[dStrlen(fullpath) - 1] != '/') + { + S32 pos = dStrlen(fullpath); + fullpath[pos] = '/'; + fullpath[pos + 1] = '\0'; + } + + // Dump the directories. + Vector directories; + Platform::dumpDirectories(fullpath, directories, depth, true); + + if( directories.empty() ) + return ""; + + // Grab the required buffer length. + S32 length = 0; + + for (S32 i = 0; i < directories.size(); i++) + length += dStrlen(directories[i]) + 1; + + // Get a return buffer. + char* buffer = Con::getReturnBuffer(length); + char* p = buffer; + + // Copy the directory names to the buffer. + for (S32 i = 0; i < directories.size(); i++) + { + dStrcpy(p, directories[i]); + p += dStrlen(directories[i]); + // Tab separated. + p[0] = '\t'; + p++; + } + p--; + p[0] = '\0'; + + return buffer; +} + +DefineEngineFunction(fileSize, S32, ( const char* fileName ),, + "@brief Determines the size of a file on disk\n\n" + + "@param fileName Name and path of the file to check\n" + "@return Returns filesize in KB, or -1 if no file\n" + + "@ingroup FileSystem") +{ + Con::expandScriptFilename(sgScriptFilenameBuffer, sizeof(sgScriptFilenameBuffer), fileName); + return Platform::getFileSize( sgScriptFilenameBuffer ); +} + +DefineEngineFunction( fileModifiedTime, String, ( const char* fileName ),, + "@brief Returns a platform specific formatted string with the last modified time for the file.\n\n" + + "@param fileName Name and path of file to check\n" + "@return Formatted string (OS specific) containing modified time, \"9/3/2010 12:33:47 PM\" for example\n" + "@ingroup FileSystem") +{ + Con::expandScriptFilename(sgScriptFilenameBuffer, sizeof(sgScriptFilenameBuffer), fileName); + + FileTime ft = {0}; + Platform::getFileTimes( sgScriptFilenameBuffer, NULL, &ft ); + + Platform::LocalTime lt = {0}; + Platform::fileToLocalTime( ft, < ); + + String fileStr = Platform::localTimeToString( lt ); + + char *buffer = Con::getReturnBuffer( fileStr.size() ); + dStrcpy( buffer, fileStr ); + + return buffer; +} + +DefineEngineFunction( fileCreatedTime, String, ( const char* fileName ),, + "@brief Returns a platform specific formatted string with the creation time for the file." + + "@param fileName Name and path of file to check\n" + "@return Formatted string (OS specific) containing created time, \"9/3/2010 12:33:47 PM\" for example\n" + "@ingroup FileSystem") +{ + Con::expandScriptFilename( sgScriptFilenameBuffer, sizeof(sgScriptFilenameBuffer), fileName ); + + FileTime ft = {0}; + Platform::getFileTimes( sgScriptFilenameBuffer, &ft, NULL ); + + Platform::LocalTime lt = {0}; + Platform::fileToLocalTime( ft, < ); + + String fileStr = Platform::localTimeToString( lt ); + + char *buffer = Con::getReturnBuffer( fileStr.size() ); + dStrcpy( buffer, fileStr ); + + return buffer; +} + +DefineEngineFunction(fileDelete, bool, ( const char* path ),, + "@brief Delete a file from the hard drive\n\n" + + "@param path Name and path of the file to delete\n" + "@note THERE IS NO RECOVERY FROM THIS. Deleted file is gone for good.\n" + "@return True if file was successfully deleted\n" + "@ingroup FileSystem") +{ + static char fileName[1024]; + static char sandboxFileName[1024]; + + Con::expandScriptFilename( fileName, sizeof( fileName ), path ); + Platform::makeFullPathName(fileName, sandboxFileName, sizeof(sandboxFileName)); + + return dFileDelete(sandboxFileName); +} + + +//---------------------------------------------------------------- + +DefineEngineFunction(fileExt, String, ( const char* fileName ),, + "@brief Get the extension of a file\n\n" + + "@param fileName Name and path of file\n" + "@return String containing the extension, such as \".exe\" or \".cs\"\n" + "@ingroup FileSystem") +{ + const char *ret = dStrrchr(fileName, '.'); + if(ret) + return ret; + return ""; +} + +DefineEngineFunction(fileBase, String, ( const char* fileName ),, + "@brief Get the base of a file name (removes extension)\n\n" + + "@param fileName Name and path of file to check\n" + "@return String containing the file name, minus extension\n" + "@ingroup FileSystem") +{ + + S32 pathLen = dStrlen( fileName ); + FrameTemp szPathCopy( pathLen + 1); + + dStrcpy( szPathCopy, fileName ); + forwardslash( szPathCopy ); + + const char *path = dStrrchr(szPathCopy, '/'); + if(!path) + path = szPathCopy; + else + path++; + char *ret = Con::getReturnBuffer(dStrlen(path) + 1); + dStrcpy(ret, path); + char *ext = dStrrchr(ret, '.'); + if(ext) + *ext = 0; + return ret; +} + +DefineEngineFunction(fileName, String, ( const char* fileName ),, + "@brief Get the file name of a file (removes extension and path)\n\n" + + "@param fileName Name and path of file to check\n" + "@return String containing the file name, minus extension and path\n" + "@ingroup FileSystem") +{ + S32 pathLen = dStrlen( fileName ); + FrameTemp szPathCopy( pathLen + 1); + + dStrcpy( szPathCopy, fileName ); + forwardslash( szPathCopy ); + + const char *name = dStrrchr(szPathCopy, '/'); + if(!name) + name = szPathCopy; + else + name++; + char *ret = Con::getReturnBuffer(dStrlen(name)); + dStrcpy(ret, name); + return ret; +} + +DefineEngineFunction(filePath, String, ( const char* fileName ),, + "@brief Get the path of a file (removes name and extension)\n\n" + + "@param fileName Name and path of file to check\n" + "@return String containing the path, minus name and extension\n" + "@ingroup FileSystem") +{ + S32 pathLen = dStrlen( fileName ); + FrameTemp szPathCopy( pathLen + 1); + + dStrcpy( szPathCopy, fileName ); + forwardslash( szPathCopy ); + + const char *path = dStrrchr(szPathCopy, '/'); + if(!path) + return ""; + U32 len = path - (char*)szPathCopy; + char *ret = Con::getReturnBuffer(len + 1); + dStrncpy(ret, szPathCopy, len); + ret[len] = 0; + return ret; +} + +DefineEngineFunction(getWorkingDirectory, String, (),, + "@brief Reports the current directory\n\n" + + "@return String containing full file path of working directory\n" + "@ingroup FileSystem") +{ + return Platform::getCurrentDirectory(); +} + +//----------------------------------------------------------------------------- + +// [tom, 5/1/2007] I changed these to be ordinary console functions as they +// are just string processing functions. They are needed by the 3D tools which +// are not currently built with TORQUE_TOOLS defined. + +DefineEngineFunction(makeFullPath, String, ( const char* path, const char* cwd ), (""), + "@brief Converts a relative file path to a full path\n\n" + + "For example, \"./console.log\" becomes \"C:/Torque/t3d/examples/FPS Example/game/console.log\"\n" + "@param path Name of file or path to check\n" + "@param cwd Optional current working directory from which to build the full path.\n" + "@return String containing non-relative directory of path\n" + "@ingroup FileSystem") +{ + char *buf = Con::getReturnBuffer(512); + Platform::makeFullPathName(path, buf, 512, dStrlen(cwd) > 1 ? cwd : NULL); + return buf; +} + +DefineEngineFunction(makeRelativePath, String, ( const char* path, const char* to ), (""), + "@brief Turns a full or local path to a relative one\n\n" + + "For example, \"./game/art\" becomes \"game/art\"\n" + "@param path Full path (may include a file) to convert\n" + "@param to Optional base path used for the conversion. If not supplied the current " + "working directory is used.\n" + "@returns String containing relative path\n" + "@ingroup FileSystem") +{ + return Platform::makeRelativePathName( path, dStrlen(to) > 1 ? to : NULL ); +} + +DefineEngineFunction(pathConcat, String, ( const char* path, const char* file),, + "@brief Combines two separate strings containing a file path and file name together into a single string\n\n" + + "@param path String containing file path\n" + "@param file String containing file name\n" + "@return String containing concatenated file name and path\n" + "@ingroup FileSystem") +{ + char *buf = Con::getReturnBuffer(1024); + Platform::makeFullPathName(file, buf, 1024, path); + return buf; +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction(getExecutableName, String, (),, + "@brief Gets the name of the game's executable\n\n" + + "@return String containing this game's executable name\n" + "@ingroup FileSystem") +{ + return Platform::getExecutableName(); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( getMainDotCsDir, String, (),, + "@brief Get the absolute path to the directory that contains the main.cs script from which the engine was started.\n\n" + + "This directory will usually contain all the game assets and, in a user-side game installation, will usually be " + "read-only.\n\n" + "@return The path to the main game assets.\n\n" + "@ingroup FileSystem\n") +{ + return Platform::getMainDotCsDir(); +} + +//----------------------------------------------------------------------------- +// Tools Only Functions +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TOOLS + +//----------------------------------------------------------------------------- + +DefineEngineFunction( openFolder, void, ( const char* path ),, + "@brief Open the given folder in the system's file manager.\n\n" + "@param path full path to a directory.\n\n" + "@note Only present in a Tools build of Torque.\n" + "@ingroup FileSystem\n") +{ + Platform::openFolder( path ); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( openFile, void, ( const char* file ),, + "@brief Open the given @a file through the system. This will usually open the file in its " + "associated application.\n" + "@param file %Path of the file to open.\n\n" + "@note Only present in a Tools build of Torque.\n" + "@ingroup FileSystem\n") +{ + Platform::openFile( file ); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( pathCopy, bool, ( const char* fromFile, const char* toFile, bool noOverwrite ), ( true ), + "@brief Copy a file to a new location.\n" + "@param fromFile %Path of the file to copy.\n" + "@param toFile %Path where to copy @a fromFile to.\n" + "@param noOverwrite If true, then @a fromFile will not overwrite a file that may already exist at @a toFile.\n" + "@return True if the file was successfully copied, false otherwise.\n" + "@note Only present in a Tools build of Torque.\n" + "@ingroup FileSystem") +{ + char qualifiedFromFile[ 2048 ]; + char qualifiedToFile[ 2048 ]; + + Platform::makeFullPathName( fromFile, qualifiedFromFile, sizeof( qualifiedFromFile ) ); + Platform::makeFullPathName( toFile, qualifiedToFile, sizeof( qualifiedToFile ) ); + + return dPathCopy( qualifiedFromFile, qualifiedToFile, noOverwrite ); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( getCurrentDirectory, String, (),, + "@brief Return the current working directory.\n\n" + "@return The absolute path of the current working directory.\n\n" + "@note Only present in a Tools build of Torque.\n" + "@see getWorkingDirectory()" + "@ingroup FileSystem") +{ + return Platform::getCurrentDirectory(); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( setCurrentDirectory, bool, ( const char* path ),, + "@brief Set the current working directory.\n\n" + "@param path The absolute or relative (to the current working directory) path of the directory which should be made the new " + "working directory.\n\n" + "@return True if the working directory was successfully changed to @a path, false otherwise.\n\n" + "@note Only present in a Tools build of Torque.\n" + "@ingroup FileSystem") +{ + return Platform::setCurrentDirectory( StringTable->insert( path ) ); + +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( createPath, bool, ( const char* path ),, + "@brief Create the given directory or the path leading to the given filename.\n\n" + "If @a path ends in a trailing slash, then all components in the given path will be created as directories (if not already in place). If @a path, " + "does @b not end in a trailing slash, then the last component of the path is taken to be a file name and only the directory components " + "of the path will be created.\n\n" + "@param path The path to create.\n\n" + "@note Only present in a Tools build of Torque.\n" + "@ingroup FileSystem" ) +{ + static char pathName[1024]; + + Con::expandScriptFilename( pathName, sizeof( pathName ), path ); + + return Platform::createPath( pathName ); +} + +#endif // TORQUE_TOOLS diff --git a/Engine/source/console/generateCompiler.bat b/Engine/source/console/generateCompiler.bat new file mode 100644 index 000000000..3c797cd8c --- /dev/null +++ b/Engine/source/console/generateCompiler.bat @@ -0,0 +1,3 @@ +@echo off +call bison.bat CMD CMDgram.c CMDgram.y . CMDgram.cpp +..\..\bin\flex\flex -PCMD -oCMDscan.cpp CMDscan.l diff --git a/Engine/source/console/persistenceManager.cpp b/Engine/source/console/persistenceManager.cpp new file mode 100644 index 000000000..c269c44a8 --- /dev/null +++ b/Engine/source/console/persistenceManager.cpp @@ -0,0 +1,2393 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "persistenceManager.h" +#include "console/simSet.h" +#include "console/consoleTypes.h" +#include "core/stream/fileStream.h" +#include "gui/core/guiTypes.h" +#include "materials/customMaterialDefinition.h" +#include "ts/tsShapeConstruct.h" +#include "sim/netStringTable.h" + + +IMPLEMENT_CONOBJECT(PersistenceManager); + +ConsoleDocClass( PersistenceManager, + "@brief this class manages updating SimObjects in the file they were " + "created in non-destructively (mostly aimed at datablocks and materials).\n\n" + + "Basic scripting interface:\n\n" + " - Creation: new PersistenceManager(FooManager);\n" + " - Flag objects as dirty: FooManager.setDirty();\n" + " - Remove objects from dirty list: FooManager.removeDirty();\n" + " - List all currently dirty objects: FooManager.listDirty();\n" + " - Check to see if an object is dirty: FooManager.isDirty();\n" + " - Save dirty objects to their files: FooManager.saveDirty();\n\n" + "@note Dirty objects don't update their files until saveDirty() is " + "called so you can change their properties after you flag them as dirty\n\n" + "@note Currently only used by editors, not intended for actual game development\n\n" + "@ingroup Console\n" + "@ingroup Editors\n" + "@internal"); + +PersistenceManager::PersistenceManager() +{ + mCurrentObject = NULL; + mCurrentFile = NULL; + + VECTOR_SET_ASSOCIATION(mLineBuffer); + + mLineBuffer.reserve(2048); +} + +PersistenceManager::~PersistenceManager() +{ + mDirtyObjects.clear(); +} + +bool PersistenceManager::onAdd() +{ + if (!Parent::onAdd()) + return false; + + return true; +} + +void PersistenceManager::onRemove() +{ + Parent::onRemove(); +} + +void PersistenceManager::clearLineBuffer() +{ + for (U32 i = 0; i < mLineBuffer.size(); i++) + { + dFree( mLineBuffer[ i ] ); + mLineBuffer[ i ] = NULL; + } + + mLineBuffer.clear(); +} + +void PersistenceManager::deleteObject(ParsedObject* object) +{ + if (object) + { + // Clear up used property memory + for (U32 j = 0; j < object->properties.size(); j++) + { + ParsedProperty& prop = object->properties[j]; + + if (prop.value) + { + dFree( prop.value ); + prop.value = NULL; + } + } + + object->properties.clear(); + + // Delete the parsed object + SAFE_DELETE(object); + } +} + +void PersistenceManager::clearObjects() +{ + // Clean up the object buffer + for (U32 i = 0; i < mObjectBuffer.size(); i++) + deleteObject(mObjectBuffer[i]); + + mObjectBuffer.clear(); + + // We shouldn't have anything in the object stack + // but let's clean it up just in case + // Clean up the object buffer + for (U32 i = 0; i < mObjectStack.size(); i++) + deleteObject(mObjectStack[i]); + + mObjectStack.clear(); + + // Finally make sure there isn't a current object + deleteObject(mCurrentObject); +} + +void PersistenceManager::clearFileData() +{ + // Clear the active file name + if (mCurrentFile) + { + dFree( mCurrentFile ); + mCurrentFile = NULL; + } + + // Clear the file objects + clearObjects(); + + // Clear the line buffer + clearLineBuffer(); + + // Clear the tokenizer data + mParser.clear(); +} + +void PersistenceManager::clearAll() +{ + // Clear the file data in case it hasn't cleared yet + clearFileData(); + + // Clear the dirty object list + mDirtyObjects.clear(); + + // Clear the remove field list + mRemoveFields.clear(); +} + +bool PersistenceManager::readFile(const char* fileName) +{ + // Clear our previous file buffers just in + // case saveDirtyFile() didn't catch it + clearFileData(); + + // Handle an object writing out to a new file + if ( !Torque::FS::IsFile( fileName ) ) + { + // Set our current file + mCurrentFile = dStrdup(fileName); + + return true; + } + + // Try to open the file + FileStream stream; + stream.open( fileName, Torque::FS::File::Read ); + + if ( stream.getStatus() != Stream::Ok ) + { + Con::errorf("PersistenceManager::readFile() - Failed to open %s", fileName); + + return false; + } + + // The file is good so read it in + mCurrentFile = dStrdup(fileName); + + while(stream.getStatus() != Stream::EOS) + { + U8* buffer = ( U8* ) dMalloc( 2048 ); + dMemset(buffer, 0, 2048); + + stream.readLine(buffer, 2048); + + mLineBuffer.push_back((const char*)buffer); + } + + // Because of the way that writeLine() works we need to + // make sure we don't have an empty last line or else + // we will get an extra line break + if (mLineBuffer.size() > 0) + { + if (mLineBuffer.last() && mLineBuffer.last()[0] == 0) + { + dFree(mLineBuffer.last()); + + mLineBuffer.pop_back(); + } + } + + stream.close(); + + //Con::printf("Successfully opened and read %s", mCurrentFile); + + return true; +} + +void PersistenceManager::killObject() +{ + // Don't save this object + SAFE_DELETE(mCurrentObject); + + // If there is an object in the stack restore it + if (mObjectStack.size() > 0) + { + mCurrentObject = mObjectStack.last(); + mObjectStack.pop_back(); + } +} + +void PersistenceManager::saveObject() +{ + // Now that we have all of the data attempt to + // find the corresponding SimObject + mCurrentObject->simObject = Sim::findObject(mCurrentFile, mCurrentObject->endLine + 1); + + // Save this object + mObjectBuffer.push_back(mCurrentObject); + + mCurrentObject = NULL; + + // If there is an object in the stack restore it + if (mObjectStack.size() > 0) + { + mCurrentObject = mObjectStack.last(); + mObjectStack.pop_back(); + } +} + +void PersistenceManager::parseObject() +{ + // We *should* already be in position but just in case... + if (!mParser.tokenICmp("new") && + !mParser.tokenICmp("singleton") && + !mParser.tokenICmp("datablock")) + { + Con::errorf("PersistenceManager::parseObject() - handed a position that doesn't point to an object \ + creation keyword (new, singleton, datablock)"); + + return; + } + + // If there is an object already being parsed then + // push it into the stack to finish later + if (mCurrentObject) + mObjectStack.push_back(mCurrentObject); + + mCurrentObject = new ParsedObject; + + //// If this object declaration is being assigned to a variable then + //// consider that the "start" of the declaration (otherwise we could + //// get a script compile error if we delete the object declaration) + mParser.regressToken(true); + + if (mParser.tokenICmp("=")) + { + // Ok, we are at an '='...back up to the beginning of that variable + mParser.regressToken(true); + + // Get the startLine and startPosition + mCurrentObject->startLine = mParser.getCurrentLine(); + mCurrentObject->startPosition = mParser.getTokenLineOffset(); + + // Advance back to the object declaration + mParser.advanceToken(true); + mParser.advanceToken(true); + } + else + { + // Advance back to the object declaration + mParser.advanceToken(true); + + // Get the startLine and startPosition + mCurrentObject->startLine = mParser.getCurrentLine(); + mCurrentObject->startPosition = mParser.getTokenLineOffset(); + } + + if (mObjectStack.size() > 0) + mCurrentObject->parentObject = mObjectStack.last(); + + // The next token should be the className + mCurrentObject->className = StringTable->insert(mParser.getNextToken()); + + // Advance to '(' + mParser.advanceToken(true); + + if (!mParser.tokenICmp("(")) + { + Con::errorf("PersistenceManager::parseObject() - badly formed object \ + declaration on line %d - was expecting a '(' character", mParser.getCurrentLine()+1); + + // Remove this object without saving it + killObject(); + + return; + } + + // The next token should either be the object name or ')' + mParser.advanceToken(true); + + if (mParser.tokenICmp(")")) + { + mCurrentObject->name = StringTable->insert(""); + + mCurrentObject->nameLine = mParser.getCurrentLine(); + mCurrentObject->namePosition = mParser.getTokenLineOffset(); + } + else + { + mCurrentObject->name = StringTable->insert(mParser.getToken()); + + mCurrentObject->nameLine = mParser.getCurrentLine(); + mCurrentObject->namePosition = mParser.getTokenLineOffset(); + + // Advance to either ')' or ':' + mParser.advanceToken(true); + + if (mParser.tokenICmp(":")) + { + // Advance past the object we are copying from + mParser.advanceToken(true); + + // Advance to ')' + mParser.advanceToken(true); + } + + if (!mParser.tokenICmp(")")) + { + Con::errorf("PersistenceManager::parseObject() - badly formed object \ + declaration on line %d - was expecting a ')' character", mParser.getCurrentLine()+1); + + // Remove this object without saving it + killObject(); + + return; + } + } + + // The next token should either be a ';' or a '{' + mParser.advanceToken(true); + + if (mParser.tokenICmp(";")) + { + // Save the end line number + mCurrentObject->endLine = mParser.getCurrentLine(); + + // Save the end position + mCurrentObject->endPosition = mParser.getTokenLineOffset(); + + // Flag this object as not having braces + mCurrentObject->hasBraces = false; + + saveObject(); // Object has no fields + + return; + } + else if (!mParser.tokenICmp("{")) + { + Con::errorf("PersistenceManager::parseObject() - badly formed object \ + declaration on line %d - was expecting a '{' character", mParser.getCurrentLine()+1); + + // Remove this object without saving it + killObject(); + + return; + } + + while (mParser.advanceToken(true)) + { + // Check for a subobject + if (mParser.tokenICmp("new") || + mParser.tokenICmp("singleton") || + mParser.tokenICmp("datablock")) + { + parseObject(); + } + + // Check to see if we have a property + if (mParser.tokenICmp("=")) + { + // Ok, we are at an '='...back up to find out + // what variable is getting assigned + mParser.regressToken(true); + + const char* variable = mParser.getToken(); + + if (variable && dStrlen(variable) > 0) + { + // See if it is a global or a local variable + if (variable[0] == '%' || variable[0] == '$') + { + // We ignore this variable and go + // back to our previous place + mParser.advanceToken(true); + } + // Could also potentially be a . + // assignment which we don't care about either + else if (dStrchr(variable, '.')) + { + // We ignore this variable and go + // back to our previous place + mParser.advanceToken(true); + } + // If we made it to here assume it is a variable + // for the current object + else + { + // Create our new property + mCurrentObject->properties.increment(); + + ParsedProperty& prop = mCurrentObject->properties.last(); + + // Check to see if this is an array variable + if (dStrlen(variable) > 3 && variable[dStrlen(variable) - 1] == ']') + { + // The last character is a ']' which *should* mean + // there is also a corresponding '[' + const char* arrayPosStart = dStrrchr(variable, '['); + + if (!arrayPosStart) + { + Con::errorf("PersistenceManager::parseObject() - error parsing array position - \ + was expecting a '[' character"); + } + else + { + // Parse the array position for the variable name + S32 arrayPos = -1; + + dSscanf(arrayPosStart, "[%d]", &arrayPos); + + // If we got a valid array position then set it + if (arrayPos > -1) + prop.arrayPos = arrayPos; + + // Trim off the [] from the variable name + char* variableName = dStrdup(variable); + variableName[arrayPosStart - variable] = 0; + + // Set the variable name to our new shortened name + variable = StringTable->insert(variableName, true); + + // Cleanup our variableName buffer + dFree( variableName ); + } + } + + + // Set back the variable name + prop.name = StringTable->insert(variable, true); + + // Store the start position for this variable + prop.startLine = mParser.getCurrentLine(); + prop.startPosition = mParser.getTokenLineOffset(); + + // Advance back to the '=' + mParser.advanceToken(true); + + // Sanity check + if (!mParser.tokenICmp("=")) + Con::errorf("PersistenceManager::parseObject() - somehow we aren't \ + pointing at the expected '=' character"); + else + { + // The next token should be the value + // being assigned to the variable + mParser.advanceToken(true); + + // Store the line number for this value + prop.valueLine = mParser.getCurrentLine(); + + // Store the values beginning position + prop.valuePosition = mParser.getTokenLineOffset(); + + // Read tokens up to the semicolon. + // Quoted tokens skip the leading and trailing quote characters. eg. + // "this" becomes: this + // "this" TAB "that" becomes: this" TAB "that + // "this" TAB "that" TAB "other" becomes: this" TAB "that" TAB "other + String value; + bool wasQuoted = false; + while (!mParser.endOfFile() && !mParser.tokenICmp(";")) + { + // Join tokens together (skipped first time through when string is empty) + if (value.length() > 0) + { + if (wasQuoted) + value += "\" "; // quoted followed by non-quoted + else if (mParser.tokenIsQuoted()) + value += " \""; // non-quoted followed by quoted + else + value += " "; // non-quoted followed by non-quoted + } + + value += mParser.getToken(); + wasQuoted = mParser.tokenIsQuoted(); + mParser.advanceToken(true); + } + + // TODO: make sure this doesn't leak + prop.value = dStrdup(value.c_str()); + + if (!mParser.tokenICmp(";")) + Con::errorf("PersistenceManager::parseObject() - badly formed variable " + "assignment on line %d - was expecting a ';' character", mParser.getCurrentLine()+1); + + // Store the end position for this variable + prop.endLine = mParser.getCurrentLine(); + prop.endPosition = mParser.getTokenLineOffset(); + if (wasQuoted) + prop.endPosition -= 1; + + } + } + } + } + + // Check for the end of the object declaration + if (mParser.tokenICmp("}")) + { + // See if the next token is a ';' + mParser.advanceToken(true); + + if (mParser.tokenICmp(";")) + { + // Save the end line number + mCurrentObject->endLine = mParser.getCurrentLine(); + + // Save the end position + mCurrentObject->endPosition = mParser.getTokenLineOffset(); + + saveObject(); + + break; + } + } + } +} + +bool PersistenceManager::parseFile(const char* fileName) +{ + // Read the file into the line buffer + if (!readFile(fileName)) + return false; + + // Load it into our Tokenizer parser + if (!mParser.openFile(fileName)) + { + // Handle an object writing out to a new file + if ( !Torque::FS::IsFile( fileName ) ) + return true; + + return false; + } + + // Set our reserved "single" tokens + mParser.setSingleTokens("(){};=:"); + + // Search object declarations + while (mParser.advanceToken(true)) + { + if (mParser.tokenICmp("new") || + mParser.tokenICmp("singleton") || + mParser.tokenICmp("datablock")) + { + parseObject(); + } + } + + // If we had an object that didn't end properly + // then we could have objects on the stack + while (mCurrentObject) + saveObject(); + + //Con::errorf("Parsed Results:"); + + //for (U32 i = 0; i < mObjectBuffer.size(); i++) + //{ + // ParsedObject* parsedObject = mObjectBuffer[i]; + + // Con::warnf(" mObjectBuffer[%d]:", i); + // Con::warnf(" name = %s", parsedObject->name); + // Con::warnf(" className = %s", parsedObject->className); + // Con::warnf(" startLine = %d", parsedObject->startLine + 1); + // Con::warnf(" endLine = %d", parsedObject->endLine + 1); + + // //if (mObjectBuffer[i]->properties.size() > 0) + // //{ + // // Con::warnf(" properties:"); + // // for (U32 j = 0; j < mObjectBuffer[i]->properties.size(); j++) + // // Con::warnf(" %s = %s;", mObjectBuffer[i]->properties[j].name, + // // mObjectBuffer[i]->properties[j].value); + // //} + + // if (!parsedObject->simObject.isNull()) + // { + // SimObject* simObject = parsedObject->simObject; + + // Con::warnf(" SimObject(%s) %d:", simObject->getName(), simObject->getId()); + // Con::warnf(" declaration line = %d", simObject->getDeclarationLine()); + // } + //} + + return true; +} + +S32 PersistenceManager::getPropertyIndex(ParsedObject* parsedObject, const char* fieldName, U32 arrayPos) +{ + S32 propertyIndex = -1; + + if (!parsedObject) + return propertyIndex; + + for (U32 i = 0; i < parsedObject->properties.size(); i++) + { + if (dStricmp(fieldName, parsedObject->properties[i].name) == 0 && + parsedObject->properties[i].arrayPos == arrayPos) + { + propertyIndex = i; + break; + } + } + + return propertyIndex; +} + +char* PersistenceManager::getObjectIndent(ParsedObject* object) +{ + char* indent = Con::getReturnBuffer(2048); + indent[0] = 0; + + if (!object) + return indent; + + if (object->startLine < 0 || object->startLine >= mLineBuffer.size()) + return indent; + + const char* line = mLineBuffer[object->startLine]; + + if (line) + { + const char* nonSpace = line; + + U32 strLen = dStrlen(line); + + for (U32 i = 0; i < strLen; i++) + { + if (*nonSpace != ' ') + break; + + nonSpace++; + } + + dStrncpy(indent, line, nonSpace - line); + + indent[nonSpace - line] = 0; + } + + return indent; +} + +void PersistenceManager::updatePositions(U32 lineNumber, U32 startPos, S32 diff) +{ + if (diff == 0) + return; + + for (U32 i = 0; i < mObjectBuffer.size(); i++) + { + ParsedObject* object = mObjectBuffer[i]; + + if (object->nameLine == lineNumber && object->namePosition > startPos) + object->namePosition += diff; + + if (object->endLine == lineNumber && object->endPosition > startPos) + object->endPosition += diff; + + if (lineNumber >= object->startLine && lineNumber <= object->endLine) + { + for (U32 j = 0; j < object->properties.size(); j++) + { + ParsedProperty& prop = object->properties[j]; + + S32 propStartPos = prop.startPosition; + S32 endPos = prop.endPosition; + S32 valuePos = prop.valuePosition; + + if (lineNumber == prop.startLine && propStartPos > startPos) + { + propStartPos += diff; + + if (propStartPos < 0) + propStartPos = 0; + + prop.startPosition = valuePos; + } + if (lineNumber == prop.endLine && endPos > startPos) + { + endPos += diff; + + if (endPos < 0) + endPos = 0; + + prop.endPosition = endPos; + } + if (lineNumber == prop.valueLine && valuePos > startPos) + { + valuePos += diff; + + if (valuePos < 0) + valuePos = 0; + + prop.valuePosition = valuePos; + } + } + } + } +} + +void PersistenceManager::updateLineOffsets(U32 startLine, S32 diff, ParsedObject* skipObject) +{ + if (diff == 0) + return; + + if (startLine >= mLineBuffer.size()) + return; + + if (startLine + diff >= mLineBuffer.size()) + return; + + // Make sure we don't double offset a SimObject's + // declaration line + SimObjectList updated; + + if (skipObject && !skipObject->simObject.isNull()) + updated.push_back_unique(skipObject->simObject); + + for (U32 i = 0; i < mObjectBuffer.size(); i++) + { + ParsedObject* object = mObjectBuffer[i]; + + // See if this is the skipObject + if (skipObject && skipObject == object) + continue; + + // We can safely ignore objects that + // came earlier in the file + if (object->endLine < startLine) + continue; + + if (object->startLine >= startLine) + object->startLine += diff; + + if (object->nameLine >= startLine) + object->nameLine += diff; + + for (U32 j = 0; j < object->properties.size(); j++) + { + if (object->properties[j].startLine >= startLine) + object->properties[j].startLine += diff; + if (object->properties[j].endLine >= startLine) + object->properties[j].endLine += diff; + if (object->properties[j].valueLine >= startLine) + object->properties[j].valueLine += diff; + } + + if (object->endLine >= startLine) + object->endLine += diff; + + if (!object->simObject.isNull() && + object->simObject->getDeclarationLine() > startLine) + { + // Check for already updated SimObject's + U32 currSize = updated.size(); + updated.push_back_unique(object->simObject); + + if (updated.size() == currSize) + continue; + + S32 newDeclLine = object->simObject->getDeclarationLine() + diff; + + if (newDeclLine < 0) + newDeclLine = 0; + + object->simObject->setDeclarationLine(newDeclLine); + } + } +} + +PersistenceManager::ParsedObject* PersistenceManager::findParentObject(SimObject* object, ParsedObject* parentObject) +{ + ParsedObject* ret = NULL; + + if (!object) + return ret; + + // First test for the SimGroup it belongs to + ret = findParsedObject(object->getGroup(), parentObject); + + if (ret) + return ret; + + // TODO: Test all of the SimSet's that this object belongs to + + return ret; +} + +PersistenceManager::ParsedObject* PersistenceManager::findParsedObject(SimObject* object, ParsedObject* parentObject) +{ + if (!object) + return NULL; + + // See if our object belongs to a parent + if (!parentObject) + parentObject = findParentObject(object, parentObject); + + // First let's compare the object to the SimObject's that + // we matched to our ParsedObject's when we loaded them + for (U32 i = 0; i < mObjectBuffer.size(); i++) + { + ParsedObject* testObj = mObjectBuffer[i]; + + if (testObj->simObject == object) + { + // Deal with children objects + if (testObj->parentObject != parentObject) + continue; + + return testObj; + } + } + + // Didn't find it in our ParsedObject's SimObject's + // so see if we can find one that corresponds to the + // same name and className + const char *originalName = object->getOriginalName(); + + // Find the corresponding ParsedObject + if (originalName && originalName[0]) + { + for (U32 i = 0; i < mObjectBuffer.size(); i++) + { + ParsedObject* testObj = mObjectBuffer[i]; + + if (testObj->name == originalName) + { + // Deal with children objects + if (testObj->parentObject != parentObject) + continue; + + return testObj; + } + } + } + + //Check internal names + if (object->getInternalName()) + { + for (U32 i = 0; i < mObjectBuffer.size(); i++) + { + ParsedObject* testObj = mObjectBuffer[i]; + for (U32 j = 0; j < testObj->properties.size(); j++) + { + const ParsedProperty &prop = testObj->properties[j]; + + if ( dStrcmp( prop.name, "internalName" ) == 0 && + dStrcmp( prop.value, object->getInternalName() ) == 0 ) + return testObj; + else if ( dStrcmp(prop.name, "internalName") == 0) + break; + } + } + } + + return NULL; +} + +void PersistenceManager::updateToken( const U32 lineNumber, const U32 linePosition, const U32 oldValueLen, const char* newValue, bool addQuotes ) +{ + // Make sure we have a valid lineNumber + if (lineNumber < 0 || linePosition < 0 || + lineNumber >= mLineBuffer.size()) + return; + + // Grab the line that the value is on + const char* line = mLineBuffer[lineNumber]; + + U32 newValueLen = ( newValue ) ? dStrlen(newValue) : 0; + + // Make sure we have a valid linePosition + if (linePosition >= dStrlen(line) || + linePosition + oldValueLen > dStrlen(line)) + return; + + // Get all of the characters up to the value position + U32 preStringLen = linePosition; + + bool needQuotes = false; + if( addQuotes && line[ linePosition - 1 ] != '"' ) + { + preStringLen ++; + needQuotes = true; + } + + char* preString = ( char* ) dMalloc( preStringLen + 1 ); + dMemcpy( preString, line, linePosition ); + + if( needQuotes ) + { + preString[ linePosition ] = '"'; + preString[ linePosition + 1 ] = 0; + } + else + preString[ linePosition ] = 0; + + // Get all of the characters that occur after the value + + const char* postStringSrc = line + linePosition + oldValueLen; + U32 postStringLen = dStrlen( postStringSrc ); + if( needQuotes ) + postStringLen ++; + + char* postString = ( char* ) dMalloc( postStringLen + 1 ); + if( needQuotes ) + postString[ 0 ] = '"'; + dStrcpy( &postString[ needQuotes ? 1 : 0 ], postStringSrc ); + postString[ postStringLen ] = 0; + + // Calculate the length of our new line + U32 newLineLen = 0; + + newLineLen += preStringLen; + newLineLen += newValueLen; + newLineLen += postStringLen; + + // Create a buffer for our new line and + // null terminate it + char* newLine = ( char* ) dMalloc( newLineLen + 1 ); + newLine[0] = 0; + + // Build the new line with the + // preString + newValue + postString + dStrcat(newLine, preString); + if ( newValue ) + dStrcat(newLine, newValue); + dStrcat(newLine, postString); + + // Clear our existing line + if (mLineBuffer[lineNumber]) + { + dFree( mLineBuffer[ lineNumber ] ); + mLineBuffer[ lineNumber ] = NULL; + } + + // Set the new line + mLineBuffer[lineNumber] = newLine; + + // Figure out the size difference of the old value + // and new value in case we need to update any else + // on the line after it + S32 diff = newValueLen - oldValueLen; + + // Update anything that is on the line after this that needs + // to change its offsets to reflect the new line + updatePositions(lineNumber, linePosition, diff); + + // Clean up our buffers + dFree( preString ); + dFree( postString ); +} + +const char* PersistenceManager::getFieldValue(SimObject* object, const char* fieldName, U32 arrayPos) +{ + // Our return string + char* ret = NULL; + + // Buffer to hold the string equivalent of the arrayPos + char arrayPosStr[8]; + dSprintf(arrayPosStr, 8, "%d", arrayPos); + + // Get the object's value + const char *value = object->getDataField(fieldName, arrayPosStr ); + if (value) + ret = dStrdup(value); + + return ret; +} + +const char* PersistenceManager::createNewProperty(const char* name, const char* value, bool isArray, U32 arrayPos) +{ + if (!name || !value) + return NULL; + + AssertFatal( value[0] != StringTagPrefixByte, "Got tagged string!" ); + + char* newProp = ( char* ) dMalloc( 2048 ); + dMemset(newProp, 0, 2048); + + if (value) + { + if (isArray) + dSprintf(newProp, 2048, "%s[%d] = \"%s\";", name, arrayPos, value); + else + dSprintf(newProp, 2048, "%s = \"%s\";", name, value); + } + else + { + if (isArray) + dSprintf(newProp, 2048, "%s[%d] = \"\";", name, arrayPos); + else + dSprintf(newProp, 2048, "%s = \"\";", name); + } + + return newProp; +} + +bool PersistenceManager::isEmptyLine(const char* line) +{ + // Simple test first + if (!line || dStrlen(line) == 0) + return true; + + U32 len = dStrlen(line); + + for (U32 i = 0; i < len; i++) + { + const char& c = line[i]; + + // Skip "empty" characters + if (c == ' ' || + c == '\t' || + c == '\r' || + c == '\n') + { + continue; + } + + // If we have made it to the an end of the line + // comment then consider this an empty line + if (c == '/') + { + if (i < len - 1) + { + if (line[i + 1] == '/') + return true; + } + } + + // If it isn't an "empty" character or a comment then + // we have a valid character on the line and it isn't empty + return false; + } + + return true; +} + +void PersistenceManager::removeLine(U32 lineNumber) +{ + if (lineNumber >= mLineBuffer.size()) + return; + + if (mLineBuffer[lineNumber]) + { + dFree( mLineBuffer[ lineNumber ] ); + mLineBuffer[ lineNumber ] = NULL; + } + + mLineBuffer.erase(lineNumber); + + updateLineOffsets(lineNumber, -1); +} + +void PersistenceManager::removeTextBlock(U32 startLine, U32 endLine, U32 startPos, U32 endPos, bool removeEmptyLines) +{ + // Make sure we have valid lines + if (startLine >= mLineBuffer.size() || endLine >= mLineBuffer.size()) + return; + + // We assume that the startLine is before the endLine + if (startLine > endLine) + return; + + // Grab the lines (they may be the same) + const char* startLineText = mLineBuffer[startLine]; + const char* endLineText = mLineBuffer[endLine]; + + // Make sure we have a valid startPos + if (startPos >= dStrlen(startLineText)) + return; + + // Make sure we have a valid endPos + if (endPos > dStrlen(endLineText)) + return; + + if (startLine == endLine) + { + // Now let updateToken do the heavy lifting on removing it + updateToken(startLine, startPos, endPos - startPos, ""); + + // Handle removing an empty lines if desired + if (removeEmptyLines) + { + const char* line = mLineBuffer[startLine]; + + if (isEmptyLine(line)) + removeLine(startLine); + } + } + else + { + // Start with clearing the startLine from startPos to the end + updateToken(startLine, startPos, dStrlen(startLineText + startPos), ""); + + // Then clear the endLine from beginning to endPos + updateToken(endLine, 0, endPos, ""); + + // Handle removing an empty endLine if desired + if (removeEmptyLines) + { + const char* line = mLineBuffer[endLine]; + + if (isEmptyLine(line)) + removeLine(endLine); + } + + // Handle removing any lines between the startLine and endLine + for (U32 i = startLine + 1; i < endLine; i++) + removeLine(startLine + 1); + + // Handle removing an empty startLine if desired + if (removeEmptyLines) + { + const char* line = mLineBuffer[startLine]; + + if (isEmptyLine(line)) + removeLine(startLine); + } + } +} + +void PersistenceManager::removeParsedObject(ParsedObject* parsedObject) +{ + if (!parsedObject) + return; + + if (parsedObject->startLine < 0 || parsedObject->startLine >= mLineBuffer.size()) + return; + + if (parsedObject->endLine < 0 || parsedObject->startLine >= mLineBuffer.size()) + return; + + removeTextBlock(parsedObject->startLine, parsedObject->endLine, + parsedObject->startPosition, parsedObject->endPosition+1, true); // +1 to remove trailing semicolon as well + + parsedObject->parentObject = NULL; + parsedObject->simObject = NULL; +} + +void PersistenceManager::removeField(const ParsedProperty& prop) +{ + if (prop.startLine < 0 || prop.startLine >= mLineBuffer.size()) + return; + + if (prop.endLine < 0 || prop.endLine >= mLineBuffer.size()) + return; + + S32 endPosition = prop.endPosition+1; // +1 to remove trailing semicolon as well + if ((endPosition < dStrlen(mLineBuffer[prop.endLine])) && + (mLineBuffer[prop.endLine][endPosition] == ';')) // adjust end position for quoted values (otherwise a trailing semicolon will remain) + endPosition++; + + removeTextBlock(prop.startLine, prop.endLine, prop.startPosition, endPosition, true); +} + +U32 PersistenceManager::writeProperties(const Vector& properties, const U32 insertLine, const char* objectIndent) +{ + U32 currInsertLine = insertLine; + + for (U32 i = 0; i < properties.size(); i++) + { + const char* prop = properties[i]; + + if (!prop || dStrlen(prop) == 0) + continue; + + U32 len = dStrlen(objectIndent) + dStrlen(prop) + 4; + + char* newLine = ( char* ) dMalloc( len ); + + dSprintf(newLine, len, "%s %s", objectIndent, prop); + + mLineBuffer.insert(currInsertLine, newLine); + currInsertLine++; + } + + return currInsertLine - insertLine; +} + +PersistenceManager::ParsedObject* PersistenceManager::writeNewObject(SimObject* object, const Vector& properties, const U32 insertLine, ParsedObject* parentObject) +{ + ParsedObject* parsedObject = new ParsedObject; + + parsedObject->name = object->getName(); + parsedObject->className = object->getClassName(); + parsedObject->simObject = object; + + U32 currInsertLine = insertLine; + + // If the parentObject isn't set see if + // we can find it in the file + if (!parentObject) + parentObject = findParentObject(object); + + parsedObject->parentObject = parentObject; + + char* indent = getObjectIndent(parentObject); + + if (parentObject) + dStrcat(indent, " \0"); + + // Write out the beginning of the object declaration + const char* dclToken = "new"; + + if (dynamic_cast(object) || + dynamic_cast(object) || + dynamic_cast(object) || + dynamic_cast(object)) + dclToken = "singleton"; + else if( dynamic_cast< SimDataBlock* >( object ) ) + { + SimDataBlock* db = static_cast(object); + + if( db->isClientOnly() ) + { + if( db->getName() && db->getName()[ 0 ] ) + dclToken = "singleton"; + } + else + dclToken = "datablock"; + } + + char newLine[ 4096 ]; + dMemset(newLine, 0, sizeof( newLine)); + + // New line before an object declaration + dSprintf(newLine, sizeof( newLine ), ""); + + mLineBuffer.insert(currInsertLine, dStrdup(newLine)); + currInsertLine++; + dMemset(newLine, 0, sizeof( newLine )); + + parsedObject->startLine = currInsertLine; + parsedObject->nameLine = currInsertLine; + parsedObject->namePosition = dStrlen(indent) + dStrlen(dclToken) + dStrlen(object->getClassName()) + 2; + + // Objects that had no name were getting saved out as: Object((null)) + if ( object->getName() != NULL ) + { + if( object->getCopySource() ) + dSprintf(newLine, sizeof( newLine ), "%s%s %s(%s : %s)", indent, dclToken, object->getClassName(), object->getName(), + object->getCopySource() ? object->getCopySource()->getName() : "" ); + else + dSprintf(newLine, sizeof( newLine ), "%s%s %s(%s)", indent, dclToken, object->getClassName(), object->getName()); + } + else + dSprintf(newLine, sizeof( newLine ), "%s%s %s()", indent, dclToken, object->getClassName() ); + + mLineBuffer.insert(currInsertLine, dStrdup(newLine)); + currInsertLine++; + dMemset(newLine, 0, sizeof( newLine )); + + dSprintf(newLine, sizeof( newLine ), "%s{", indent); + + mLineBuffer.insert(currInsertLine, dStrdup(newLine)); + currInsertLine++; + dMemset(newLine, 0, sizeof( newLine )); + + currInsertLine += writeProperties(properties, currInsertLine, indent); + + parsedObject->endLine = currInsertLine; + parsedObject->updated = true; + + dSprintf(newLine, sizeof( newLine ), "%s};", indent); + + mLineBuffer.insert(currInsertLine, dStrdup(newLine)); + currInsertLine++; + + updateLineOffsets(insertLine, currInsertLine - insertLine, parsedObject); + + mObjectBuffer.push_back(parsedObject); + + // Update the SimObject to reflect its saved name and declaration line. + // These values should always reflect what is in the file, even if the object + // has not actually been re-created from an execution of that file yet. + object->setOriginalName( object->getName() ); + object->setDeclarationLine( currInsertLine ); + + if (mCurrentFile) + object->setFilename(mCurrentFile); + + return parsedObject; +} + +void PersistenceManager::updateObject(SimObject* object, ParsedObject* parentObject) +{ + // Create a default object of the same type + ConsoleObject *defaultConObject = ConsoleObject::create(object->getClassName()); + SimObject* defaultObject = dynamic_cast(defaultConObject); + + // ***Really*** shouldn't happen + if (!defaultObject) + return; + + Vector newLines; + + ParsedObject* parsedObject = findParsedObject(object, parentObject); + + // If we don't already have an association between the ParsedObject + // and the SimObject then go ahead and create it + if (parsedObject && parsedObject->simObject.isNull()) + parsedObject->simObject = object; + + // Kill all fields on the remove list. + + for( U32 i = 0; i < mRemoveFields.size(); ++ i ) + { + RemoveField& field = mRemoveFields[ i ]; + if( field.object != object ) + continue; + + S32 propertyIndex = getPropertyIndex( parsedObject, field.fieldName, field.arrayPos ); + if( propertyIndex != -1 ) + removeField( parsedObject->properties[ propertyIndex ] ); + } + + // Get our field list + const AbstractClassRep::FieldList &list = object->getFieldList(); + + for(U32 i = 0; i < list.size(); i++) + { + const AbstractClassRep::Field* f = &list[i]; + + // Skip the special field types. + if ( f->type >= AbstractClassRep::ARCFirstCustomField ) + continue; + + for(U32 j = 0; S32(j) < f->elementCount; j++) + { + const char* value = getFieldValue(object, f->pFieldname, j); + + // Make sure we got a value + if (!value) + continue; + + // Let's see if this field is already in the file + S32 propertyIndex = getPropertyIndex(parsedObject, f->pFieldname, j); + + if (propertyIndex > -1) + { + ParsedProperty& prop = parsedObject->properties[propertyIndex]; + + // If this field is on the remove list then remove it and continue + if (findRemoveField(object, f->pFieldname, j) || !object->writeField(f->pFieldname, value)) + { + removeField( parsedObject->properties[ propertyIndex ] ); + dFree( value ); + continue; + } + + // Run the parsed value through the console system conditioners so + // that it will better match the data we got back from the object. + const char* evalue = Con::getFormattedData(f->type, prop.value, f->table, f->flag); + + // If our data doesn't match then we get to update it. + // + // As for copy-sources, we just assume here that if a property setting + // is there in the file, the user does not want it inherited from the copy-source + // even in the case the actual values are identical. + + if( dStricmp(value, evalue) != 0 ) + { + if( value[ 0 ] == '\0' && + dStricmp( getFieldValue( defaultObject, f->pFieldname, j ), value ) == 0 && + ( !object->getCopySource() || dStricmp( getFieldValue( object->getCopySource(), f->pFieldname, j ), value ) == 0 ) ) + { + removeField( prop ); + } + else + { + // TODO: This should be wrapped in a helper method... probably. + // Detect and collapse relative path information + if (f->type == TypeFilename || + f->type == TypeStringFilename || + f->type == TypeImageFilename || + f->type == TypePrefabFilename || + f->type == TypeShapeFilename) + { + char fnBuf[1024]; + Con::collapseScriptFilename(fnBuf, 1024, value); + + updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, fnBuf, true); + } + else + updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, value, true); + } + } + } + else + { + // No need to process a removed field that doesn't exist in the file + if (findRemoveField(object, f->pFieldname, j) || !object->writeField(f->pFieldname, value)) + { + dFree( value ); + continue; + } + + bool mustUpdate = false; + + // If we didn't find the property in the ParsedObject + // then we need to compare against the default value + // for this property and save it out if it is different + + const char* defaultValue = getFieldValue(defaultObject, f->pFieldname, j); + if( !defaultValue || dStricmp( value, defaultValue ) != 0 ) + { + // Value differs. Check whether it also differs from the + // value in the copy source if there is one. + + if( object->getCopySource() ) + { + const char* copySourceValue = getFieldValue( object->getCopySource(), f->pFieldname, j ); + if( !copySourceValue || dStricmp( copySourceValue, value ) != 0 ) + mustUpdate = true; + + if( copySourceValue ) + dFree( copySourceValue ); + } + else + mustUpdate = true; + } + else + { + // Value does not differ. If it differs from the copy source's value, + // though, we still want to write it out as otherwise we'll see the + // copy source's value override us. + + if( object->getCopySource() ) + { + const char* copySourceValue = getFieldValue( object->getCopySource(), f->pFieldname, j ); + if( copySourceValue && dStricmp( copySourceValue, value ) != 0 ) + mustUpdate = true; + + if( copySourceValue ) + dFree( copySourceValue ); + } + } + + // The default value for most string type fields is + // NULL so we can't just continue here or we'd never ever + // write them out... + // + //if (!defaultValue) + // continue; + + // If the object's value is different from the default + // value then add it to the ParsedObject's newLines + if ( mustUpdate ) + { + // TODO: This should be wrapped in a helper method... probably. + // Detect and collapse relative path information + if (f->type == TypeFilename || + f->type == TypeStringFilename || + f->type == TypeImageFilename || + f->type == TypePrefabFilename || + f->type == TypeShapeFilename) + { + char fnBuf[1024]; + Con::collapseScriptFilename(fnBuf, 1024, value); + + newLines.push_back(createNewProperty(f->pFieldname, fnBuf, f->elementCount > 1, j)); + } + else + newLines.push_back(createNewProperty(f->pFieldname, value, f->elementCount > 1, j)); + } + + if (defaultValue) + dFree( defaultValue ); + } + + dFree( value ); + } + } + + // Handle dynamic fields + SimFieldDictionary* fieldDict = object->getFieldDictionary(); + + for(SimFieldDictionaryIterator itr(fieldDict); *itr; ++itr) + { + SimFieldDictionary::Entry * entry = (*itr); + if( !entry->value ) + continue; + + // Let's see if this field is already in the file + S32 propertyIndex = getPropertyIndex(parsedObject, entry->slotName); + + if (propertyIndex > -1) + { + ParsedProperty& prop = parsedObject->properties[propertyIndex]; + + // If this field is on the remove list then remove it and continue + if (findRemoveField(object, entry->slotName) || !object->writeField(entry->slotName, entry->value)) + { + removeField( parsedObject->properties[ propertyIndex ] ); + continue; + } + + if( object->getCopySource() ) + { + const char* copySourceFieldValue = object->getCopySource()->getDataField( entry->slotName, NULL ); + if( dStrcmp( copySourceFieldValue, entry->value ) == 0 ) + { + removeField( prop ); + continue; + } + } + + const char* evalue = prop.value; + + const char *entryVal = entry->value; + if ( entryVal[0] == StringTagPrefixByte ) + entryVal = gNetStringTable->lookupString( dAtoi( entryVal+1 ) ); + else + { + // Run the parsed value through the console system conditioners so + // that it will better match the data we got back from the object. + evalue = Con::getFormattedData(TypeString, evalue); + } + + // If our data doesn't match then we get to update it + if (dStricmp(entryVal, evalue) != 0) + updateToken(prop.valueLine, prop.valuePosition, prop.endPosition - prop.valuePosition, entryVal); + } + else + { + // No need to process a removed field that doesn't exist in the file + if (findRemoveField(object, entry->slotName) || !object->writeField(entry->slotName, entry->value)) + continue; + + if( object->getCopySource() ) + { + const char* copySourceFieldValue = object->getCopySource()->getDataField( entry->slotName, NULL ); + if( dStrcmp( copySourceFieldValue, entry->value ) == 0 ) + continue; + } + + newLines.push_back(createNewProperty(entry->slotName, entry->value)); + } + } + + // If we have a parsedObject and the name changed + // then update the parsedObject to the new name. + // NOTE: an object 'can' have a NULL name which crashes in dStricmp. + if (parsedObject && parsedObject->name != StringTable->insert(object->getName(), true) ) + { + StringTableEntry objectName = StringTable->insert(object->getName(), true); + + if (parsedObject->name != objectName) + { + // Update the name in the file + updateToken(parsedObject->nameLine, parsedObject->namePosition, dStrlen(parsedObject->name), object->getName()); + + // Updated the parsedObject's name + parsedObject->name = objectName; + + // Updated the object's "original" name to the one that is now in the file + object->setOriginalName(objectName); + } + } + + if (parsedObject && newLines.size() > 0) + { + U32 lastPropLine = parsedObject->endLine; + + if (parsedObject->properties.size() > 0) + lastPropLine = parsedObject->properties.last().valueLine + 1; + + U32 currInsertLine = lastPropLine; + + const char* indent = getObjectIndent(parsedObject); + + // This should handle adding the opening { to an object + // that formerly did not have {}; + if (!parsedObject->hasBraces) + { + updateToken(parsedObject->endLine, parsedObject->endPosition, 1, "\r\n{"); + + currInsertLine++; + } + + currInsertLine += writeProperties(newLines, currInsertLine, indent); + + // This should handle adding the opening } to an object + // that formerly did not have {}; + if (!parsedObject->hasBraces) + { + U32 len = dStrlen(indent) + 3; + char* newLine = ( char* ) dMalloc( len ); + + dSprintf(newLine, len, "%s};", indent); + + mLineBuffer.insert(currInsertLine, newLine); + currInsertLine++; + } + + // Update the line offsets to account for the new lines + updateLineOffsets(lastPropLine, currInsertLine - lastPropLine); + } + else if (!parsedObject) + { + U32 insertLine = mLineBuffer.size(); + + if (!parentObject) + parentObject = findParentObject(object, parentObject); + + if (parentObject && parentObject->endLine > -1) + insertLine = parentObject->endLine; + + parsedObject = writeNewObject(object, newLines, insertLine, parentObject); + } + + // Clean up the newLines memory + for (U32 i = 0; i < newLines.size(); i++) + { + if (newLines[i]) + { + dFree(newLines[i]); + newLines[ i ] = NULL; + } + } + + newLines.clear(); + + SimSet* set = dynamic_cast(object); + + if (set) + { + for(SimSet::iterator i = set->begin(); i != set->end(); i++) + { + SimObject* subObject = (SimObject*)(*i); + updateObject(subObject, parsedObject); + } + } + + // Loop through the children of this parsedObject + // If they haven't been updated then assume that they + // don't exist in the file anymore + if (parsedObject) + { + for (S32 i = 0; i < mObjectBuffer.size(); i++) + { + ParsedObject* removeObj = mObjectBuffer[i]; + + if (removeObj->parentObject == parsedObject && !removeObj->updated) + { + removeParsedObject(removeObj); + + mObjectBuffer.erase(i); + i--; + + deleteObject(removeObj); + } + } + } + + // Flag this as an updated object + if (parsedObject) + parsedObject->updated = true; + + // Cleanup our created default object + delete defaultConObject; +} + +bool PersistenceManager::saveDirtyFile() +{ + FileStream stream; + stream.open( mCurrentFile, Torque::FS::File::Write ); + + if ( stream.getStatus() != Stream::Ok ) + { + clearFileData(); + + return false; + } + + for (U32 i = 0; i < mLineBuffer.size(); i++) + stream.writeLine((const U8*)mLineBuffer[i]); + + stream.close(); + + //Con::printf("Successfully opened and wrote %s", mCurrentFile); + + //Con::errorf("Updated Results:"); + + //for (U32 i = 0; i < mObjectBuffer.size(); i++) + //{ + // ParsedObject* parsedObject = mObjectBuffer[i]; + + // Con::warnf(" mObjectBuffer[%d]:", i); + // Con::warnf(" name = %s", parsedObject->name); + // Con::warnf(" className = %s", parsedObject->className); + // Con::warnf(" startLine = %d", parsedObject->startLine + 1); + // Con::warnf(" endLine = %d", parsedObject->endLine + 1); + + // //if (mObjectBuffer[i]->properties.size() > 0) + // //{ + // // Con::warnf(" properties:"); + // // for (U32 j = 0; j < mObjectBuffer[i]->properties.size(); j++) + // // Con::warnf(" %s = %s;", mObjectBuffer[i]->properties[j].name, + // // mObjectBuffer[i]->properties[j].value); + // //} + + // if (!parsedObject->simObject.isNull()) + // { + // SimObject* simObject = parsedObject->simObject; + + // Con::warnf(" SimObject(%s) %d:", simObject->getName(), simObject->getId()); + // Con::warnf(" declaration line = %d", simObject->getDeclarationLine()); + // } + //} + + // Clear our file data + clearFileData(); + + return true; +} + +S32 QSORT_CALLBACK PersistenceManager::compareFiles(const void* a,const void* b) +{ + DirtyObject* objectA = (DirtyObject*)(a); + DirtyObject* objectB = (DirtyObject*)(b); + + if (objectA->isNull()) + return -1; + else if (objectB->isNull()) + return 1; + + if (objectA->fileName == objectB->fileName) + return objectA->getObject()->getDeclarationLine() - objectB->getObject()->getDeclarationLine(); + + return dStricmp(objectA->fileName, objectB->fileName); +} + +bool PersistenceManager::setDirty(SimObject* inObject, const char* inFileName) +{ + // Check if the object is already in the dirty list... + DirtyObject *pDirty = findDirtyObject( inObject ); + + // The filename we will save this object to (later).. + String saveFile; + + // Expand the script filename if we were passed one. + if ( inFileName ) + { + char buffer[4096]; + Con::expandScriptFilename( buffer, 4096, inFileName ); + saveFile = buffer; + } + + // If no filename was passed in, and the object was already dirty, + // we have nothing to do. + if ( saveFile.isEmpty() && pDirty ) + return true; + + // Otherwise default to the simObject's filename. + if ( saveFile.isEmpty() ) + saveFile = inObject->getFilename(); + + // Error if still no filename. + if ( saveFile.isEmpty() ) + { + if (inObject->getName()) + Con::errorf("PersistenceManager::setDirty() - SimObject %s has no file name associated with it - can not save", inObject->getName()); + else + Con::errorf("PersistenceManager::setDirty() - SimObject %d has no file name associated with it - can not save", inObject->getId()); + + return false; + } + + // Update the DirtyObject's fileName if we have it + // else create a new one. + + if ( pDirty ) + pDirty->fileName = StringTable->insert( saveFile ); + else + { + // Add the newly dirty object. + mDirtyObjects.increment(); + mDirtyObjects.last().setObject( inObject ); + mDirtyObjects.last().fileName = StringTable->insert( saveFile ); + } + + return true; +} + +void PersistenceManager::removeDirty(SimObject* object) +{ + for (U32 i = 0; i < mDirtyObjects.size(); i++) + { + const DirtyObject& dirtyObject = mDirtyObjects[i]; + + if (dirtyObject.isNull()) + continue; + + if (dirtyObject.getObject() == object) + { + mDirtyObjects.erase(i); + break; + } + } + + for (U32 i = 0; i < mRemoveFields.size(); i++) + { + const RemoveField& field = mRemoveFields[i]; + + if (field.object != object) + continue; + + mRemoveFields.erase(i); + + if (i > 0) + i--; + } +} + +void PersistenceManager::addRemoveField(SimObject* object, const char* fieldName) +{ + // Check to see if this is an array variable + U32 arrayPos = 0; + const char* name = fieldName; + + if (dStrlen(fieldName) > 3 && fieldName[dStrlen(fieldName) - 1] == ']') + { + // The last character is a ']' which *should* mean + // there is also a corresponding '[' + const char* arrayPosStart = dStrrchr(fieldName, '['); + + if (!arrayPosStart) + { + Con::errorf("PersistenceManager::addRemoveField() - error parsing array position - \ + was expecting a '[' character"); + } + else + { + // Parse the array position for the variable name + dSscanf(arrayPosStart, "[%d]", &arrayPos); + + // Trim off the [] from the variable name + char* variableName = dStrdup(fieldName); + variableName[arrayPosStart - fieldName] = 0; + + // Set the variable name to our new shortened name + name = StringTable->insert(variableName, true); + + // Cleanup our variableName buffer + dFree( variableName ); + } + } + + // Make sure this field isn't already on the list + if (!findRemoveField(object, name, arrayPos)) + { + mRemoveFields.increment(); + + RemoveField& field = mRemoveFields.last(); + + field.object = object; + field.fieldName = StringTable->insert(name); + field.arrayPos = arrayPos; + } +} + +bool PersistenceManager::isDirty(SimObject* object) +{ + return ( findDirtyObject( object ) != NULL ); +} + +PersistenceManager::DirtyObject* PersistenceManager::findDirtyObject(SimObject* object) +{ + for (U32 i = 0; i < mDirtyObjects.size(); i++) + { + const DirtyObject& dirtyObject = mDirtyObjects[i]; + + if (dirtyObject.isNull()) + continue; + + if (dirtyObject.getObject() == object) + return &mDirtyObjects[i]; + } + + return NULL; +} + +bool PersistenceManager::findRemoveField(SimObject* object, const char* fieldName, U32 arrayPos) +{ + for (U32 i = 0; i < mRemoveFields.size(); i++) + { + if (mRemoveFields[i].object == object && + mRemoveFields[i].arrayPos == arrayPos && + dStricmp(mRemoveFields[i].fieldName, fieldName) == 0) + { + return true; + } + } + + return false; +} + +bool PersistenceManager::saveDirty() +{ + // Remove any null SimObject's first + for (S32 i = 0; i < mDirtyObjects.size(); i++) + { + const DirtyObject& dirtyObject = mDirtyObjects[i]; + + if (dirtyObject.isNull()) + { + mDirtyObjects.erase(i); + i--; + } + } + + // Sort by filename and declaration lines + dQsort(mDirtyObjects.address(), mDirtyObjects.size(), sizeof(DirtyList::value_type), compareFiles); + + for (U32 i = 0; i < mDirtyObjects.size(); i++) + { + const DirtyObject& dirtyObject = mDirtyObjects[i]; + + if (dirtyObject.isNull()) + continue; + + SimObject* object = dirtyObject.getObject(); + + if (!mCurrentFile || dStricmp(mCurrentFile, dirtyObject.fileName) != 0) + { + // If mCurrentFile is set then that means we + // changed file names so save our previous one + if (mCurrentFile) + saveDirtyFile(); + + // Open our new file and parse it + bool success = parseFile(dirtyObject.fileName); + + if (!success) + { + const char *name = object->getName(); + if (name) + { + Con::errorf("PersistenceManager::saveDirty(): Unable to open %s to save %s %s (%d)", + dirtyObject.fileName, object->getClassName(), name, object->getId()); + } + else + { + Con::errorf("PersistenceManager::saveDirty(): Unable to open %s to save %s (%d)", + dirtyObject.fileName, object->getClassName(), object->getId()); + } + + continue; + } + } + + // Update this object's properties + // + // An empty script file (with 1 line) gets here with zero + // elements in the linebuffer, so this would prevent us from + // ever writing to it... Or is this test preventing bad things from + // happening if the file didn't exist at all? + // + if (mCurrentFile /*&& mLineBuffer.size() > 0*/) + updateObject(object); + } + + // Save out our last file + if (mCurrentFile) + saveDirtyFile(); + + // Done writing out our dirty objects so reset everything + clearAll(); + + return true; +} + +bool PersistenceManager::saveDirtyObject(SimObject* object) +{ + // find our object passed in + for (U32 i = 0; i < mDirtyObjects.size(); i++) + { + const DirtyObject& dirtyObject = mDirtyObjects[i]; + + if (dirtyObject.isNull()) + continue; + + if (dirtyObject.getObject() == object) + { + // Open our new file and parse it + bool success = parseFile(dirtyObject.fileName); + + if (!success) + { + const char *name = object->getName(); + if (name) + { + Con::errorf("PersistenceManager::saveDirtyObject(): Unable to open %s to save %s %s (%d)", + dirtyObject.fileName, object->getClassName(), name, object->getId()); + } + else + { + Con::errorf("PersistenceManager::saveDirtyObject(): Unable to open %s to save %s (%d)", + dirtyObject.fileName, object->getClassName(), object->getId()); + } + + return false; + } + + // if the file exists then lets update and save + if(mCurrentFile) + { + updateObject(object); + saveDirtyFile(); + } + + break; + } + } + + // remove this object from the dirty list + removeDirty(object); + + return true; +} + +void PersistenceManager::removeObjectFromFile(SimObject* object, const char* fileName) +{ + if (mCurrentFile) + { + Con::errorf("PersistenceManager::removeObjectFromFile(): Can't remove an object from a \ + file while another is currently opened"); + + return; + } + + const char* file = object->getFilename(); + if (fileName) + { + char buffer[1024]; + Con::expandScriptFilename( buffer, 1024, fileName ); + + file = StringTable->insert(buffer); + } + + bool success = false; + + if ( file && file[ 0 ] ) + success = parseFile(file); + + if (!success) + { + const char *name = object->getName(); + + String errorNameStr; + if ( name ) + errorNameStr = String::ToString( "%s %s (%d)", object->getClassName(), name, object->getId() ); + else + errorNameStr = String::ToString( "%s (%d)", object->getClassName(), object->getId() ); + + if ( !file ) + Con::errorf("PersistenceManager::removeObjectFromFile(): File was null trying to save %s", errorNameStr.c_str() ); + else + Con::errorf("PersistenceManager::removeObjectFromFile(): Unable to open %s to save %s", file, errorNameStr.c_str() ); + + // Reset everything + clearAll(); + + return; + } + + ParsedObject* parsedObject = findParsedObject(object); + + if (!parsedObject) + { + const char *name = object->getName(); + if (name) + { + Con::errorf("PersistenceManager::removeObjectFromFile(): Unable to find %s %s (%d) in %s", + object->getClassName(), name, object->getId(), file); + } + else + { + Con::errorf("PersistenceManager::removeObjectFromFile(): Unable to find %s (%d) in %s", + object->getClassName(), object->getId(), file); + } + + // Reset everything + clearAll(); + + return; + } + + removeParsedObject(parsedObject); + + for (U32 i = 0; i < mObjectBuffer.size(); i++) + { + ParsedObject* removeObj = mObjectBuffer[i]; + + if (removeObj == parsedObject) + { + mObjectBuffer.erase(i); + break; + } + } + + deleteObject(parsedObject); + + // Save out the file + if (mCurrentFile) + saveDirtyFile(); + + // Reset everything + clearAll(); +} + +void PersistenceManager::deleteObjectsFromFile(const char* fileName) +{ + if ( mCurrentFile ) + { + Con::errorf( "PersistenceManager::deleteObjectsFromFile(): Cannot process while file while another is currently open." ); + return; + } + + // Expand Script File. + char buffer[1024]; + Con::expandScriptFilename( buffer, 1024, fileName ); + + // Parse File. + if ( !parseFile( StringTable->insert(buffer) ) ) + { + // Invalid. + return; + } + + // Iterate over the objects. + for ( Vector::iterator itr = mObjectBuffer.begin(); itr != mObjectBuffer.end(); itr++ ) + { + SimObject *object; + if ( Sim::findObject( ( *itr )->name, object ) ) + { + // Delete the Object. + object->deleteObject(); + } + } + + // Clear. + clearAll(); +} + +ConsoleMethod( PersistenceManager, deleteObjectsFromFile, void, 3, 3, "( fileName )" + "Delete all of the objects that are created from the given file." ) +{ + // Delete Objects. + object->deleteObjectsFromFile( argv[2] ); +} + +ConsoleMethod( PersistenceManager, setDirty, void, 3, 4, "(SimObject object, [filename])" + "Mark an existing SimObject as dirty (will be written out when saveDirty() is called).") +{ + SimObject *dirtyObject = NULL; + if (argv[2][0]) + { + if (!Sim::findObject(argv[2], dirtyObject)) + { + Con::printf("%s(): Invalid SimObject: %s", argv[0], argv[2]); + return; + } + } + + // Prevent ourselves from shooting us in the foot. + + if( dirtyObject == Sim::getRootGroup() ) + { + Con::errorf( "%s(): Cannot save RootGroup", argv[ 0 ] ); + return; + } + + if (dirtyObject) + { + if (argc == 4 && argv[3][0]) + object->setDirty(dirtyObject, argv[3]); + else + object->setDirty(dirtyObject); + } +} + +ConsoleMethod( PersistenceManager, removeDirty, void, 3, 3, "(SimObject object)" + "Remove a SimObject from the dirty list.") +{ + SimObject *dirtyObject = NULL; + if (argv[2][0]) + { + if (!Sim::findObject(argv[2], dirtyObject)) + { + Con::printf("%s(): Invalid SimObject: %s", argv[0], argv[2]); + return; + } + } + + if (dirtyObject) + object->removeDirty(dirtyObject); +} + +ConsoleMethod( PersistenceManager, isDirty, bool, 3, 3, "(SimObject object)" + "Returns true if the SimObject is on the dirty list.") +{ + SimObject *dirtyObject = NULL; + if (argv[2][0]) + { + if (!Sim::findObject(argv[2], dirtyObject)) + { + Con::printf("%s(): Invalid SimObject: %s", argv[0], argv[2]); + return false; + } + } + + if (dirtyObject) + return object->isDirty(dirtyObject); + + return false; +} + +ConsoleMethod( PersistenceManager, hasDirty, bool, 2, 2, "()" + "Returns true if the manager has dirty objects to save." ) +{ + return object->hasDirty(); +} + +ConsoleMethod( PersistenceManager, getDirtyObjectCount, S32, 2, 2, "()" + "Returns the number of dirty objects." ) +{ + return object->getDirtyList().size(); +} + +ConsoleMethod( PersistenceManager, getDirtyObject, S32, 3, 3, "( index )" + "Returns the ith dirty object." ) +{ + const S32 index = dAtoi( argv[2] ); + if ( index < 0 || index >= object->getDirtyList().size() ) + { + Con::warnf( "PersistenceManager::getDirtyObject() - Index (%s) out of range.", argv[2] ); + return 0; + } + + // Fetch Object. + const PersistenceManager::DirtyObject& dirtyObject = object->getDirtyList()[index]; + + // Return Id. + return ( dirtyObject.getObject() ) ? dirtyObject.getObject()->getId() : 0; +} + +ConsoleMethod( PersistenceManager, listDirty, void, 2, 2, "()" + "Prints the dirty list to the console.") +{ + const PersistenceManager::DirtyList dirtyList = object->getDirtyList(); + + for(U32 i = 0; i < dirtyList.size(); i++) + { + const PersistenceManager::DirtyObject& dirtyObject = dirtyList[i]; + + if (dirtyObject.isNull()) + continue; + + SimObject *obj = dirtyObject.getObject(); + bool isSet = dynamic_cast(obj) != 0; + const char *name = obj->getName(); + if (name) + { + Con::printf(" %d,\"%s\": %s %s %s", obj->getId(), name, + obj->getClassName(), dirtyObject.fileName, isSet ? "(g)":""); + } + else + { + Con::printf(" %d: %s %s, %s", obj->getId(), obj->getClassName(), + dirtyObject.fileName, isSet ? "(g)" : ""); + } + } +} + +ConsoleMethod( PersistenceManager, saveDirty, bool, 2, 2, "()" + "Saves all of the SimObject's on the dirty list to their respective files.") +{ + return object->saveDirty(); +} + +ConsoleMethod( PersistenceManager, saveDirtyObject, bool, 3, 3, "(SimObject object)" + "Save a dirty SimObject to it's file.") +{ + SimObject *dirtyObject = NULL; + if (argv[2][0]) + { + if (!Sim::findObject(argv[2], dirtyObject)) + { + Con::printf("%s(): Invalid SimObject: %s", argv[0], argv[2]); + return false; + } + } + + if (dirtyObject) + return object->saveDirtyObject(dirtyObject); + return false; +} + +ConsoleMethod( PersistenceManager, clearAll, void, 2, 2, "()" + "Clears all the tracked objects without saving them." ) +{ + object->clearAll(); +} + +ConsoleMethod( PersistenceManager, removeObjectFromFile, void, 3, 4, "(SimObject object, [filename])" + "Remove an existing SimObject from a file (can optionally specify a different file than \ + the one it was created in.") +{ + SimObject *dirtyObject = NULL; + if (argv[2][0]) + { + if (!Sim::findObject(argv[2], dirtyObject)) + { + Con::printf("%s(): Invalid SimObject: %s", argv[0], argv[2]); + return; + } + } + + if (dirtyObject) + { + if (argc == 4 && argv[3][0]) + object->removeObjectFromFile(dirtyObject, argv[3]); + else + object->removeObjectFromFile(dirtyObject); + } +} + +ConsoleMethod( PersistenceManager, removeField, void, 4, 4, "(SimObject object, string fieldName)" + "Remove a specific field from an object declaration.") +{ + SimObject *dirtyObject = NULL; + if (argv[2][0]) + { + if (!Sim::findObject(argv[2], dirtyObject)) + { + Con::printf("%s(): Invalid SimObject: %s", argv[0], argv[2]); + return; + } + } + + if (dirtyObject) + { + if (argv[3][0]) + object->addRemoveField(dirtyObject, argv[3]); + } +} \ No newline at end of file diff --git a/Engine/source/console/persistenceManager.h b/Engine/source/console/persistenceManager.h new file mode 100644 index 000000000..afb908960 --- /dev/null +++ b/Engine/source/console/persistenceManager.h @@ -0,0 +1,323 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PERSISTENCEMANAGER_H_ +#define _PERSISTENCEMANAGER_H_ + +#ifndef _SIMOBJECT_H_ +#include "console/simObject.h" +#endif + +#ifndef _SIMOBJECTLIST_H_ +#include "console/simObjectList.h" +#endif + +#ifndef _TOKENIZER_H_ +#include "core/tokenizer.h" +#endif + +class PersistenceManager : public SimObject +{ +public: + struct ParsedProperty + { + StringTableEntry name; + const char* value; + + U32 arrayPos; + + S32 startLine; + S32 endLine; + + S32 startPosition; + S32 endPosition; + + S32 valueLine; + S32 valuePosition; + + ParsedProperty() + { + name = NULL; + value = NULL; + + arrayPos = 0; + + startLine = -1; + endLine = -1; + + startPosition = -1; + endPosition = -1; + + valueLine = -1; + valuePosition = -1; + } + }; + + struct ParsedObject + { + StringTableEntry name; + StringTableEntry className; + + ParsedObject* parentObject; + + SimObjectPtr simObject; + + S32 nameLine; + S32 namePosition; + + S32 startLine; + S32 endLine; + + S32 startPosition; + S32 endPosition; + + bool hasBraces; + + bool updated; + + Vector properties; + + ParsedObject() + { + name = NULL; + className = NULL; + + parentObject = NULL; + + simObject = NULL; + + nameLine = -1; + namePosition = -1; + + startLine = -1; + endLine = -1; + + startPosition = -1; + endPosition = -1; + + hasBraces = true; + + updated = false; + } + }; + + struct DirtyObject + { + SimObjectPtr *object; + StringTableEntry fileName; + + bool isNull() const { return object->isNull(); } + + void setObject( SimObject* newObject ) { *object = newObject; } + + SimObject* getObject() const { return object->getPointer(); } + + DirtyObject() + { + object = new SimObjectPtr(); + fileName = NULL; + } + + ~DirtyObject() + { + SAFE_DELETE( object ); + } + }; + + struct RemoveField + { + SimObjectPtr object; + StringTableEntry fieldName; + U32 arrayPos; + + RemoveField() + { + object = NULL; + fieldName = NULL; + arrayPos = 0; + } + }; + + typedef Vector DirtyList; + +protected: + typedef SimObject Parent; + + // Used to walk through the file and read out + // the SimObject's and their properties + Tokenizer mParser; + + // List of the objects that are flagged as dirty + DirtyList mDirtyObjects; + + // List of fields to be removed from the objects declaration in the file + Vector mRemoveFields; + + // Temp buffers used during file parsing + ParsedObject* mCurrentObject; + Vector mObjectStack; + + // Buffers used on a per-file basis + Vector mLineBuffer; + Vector mObjectBuffer; + + // Name of the currently open file + const char* mCurrentFile; + + // Sort by filename + static S32 QSORT_CALLBACK compareFiles(const void* a, const void* b); + + // Deletes and clears the line buffer + void clearLineBuffer(); + + // Deletes the objects and its properties + void deleteObject(ParsedObject* object); + // Deletes and clears the object buffer, + // the object stack, and the current object + void clearObjects(); + + // Clears all of the data related to the + // currently loaded file + void clearFileData(); + + // Updates the changed values of a dirty object + // Also handles a new object + void updateObject(SimObject* object, ParsedObject* parentObject = NULL); + + // Removes the current object without saving it + void killObject(); + // Saves the current object and restores the last object in the stack (if any) + void saveObject(); + // Parses an object from the current position in the parser + void parseObject(); + + // Reads the file into the line buffer + bool readFile(const char* fileName); + // Parses the ParsedObjects out of the file + bool parseFile(const char* fileName); + + // Writes the line buffer out to the current file + bool saveDirtyFile(); + + // Attempts to look up the property in the ParsedObject + S32 getPropertyIndex(ParsedObject* parsedObject, const char* fieldName, U32 arrayPos = 0); + + // Gets the amount of indent on the ParsedObject. + char* getObjectIndent(ParsedObject* object); + + // Loops through all of the objects and properties that are on the same + // line after the startPos and updates their position offests accordingly + void updatePositions(U32 lineNumber, U32 startPos, S32 diff); + + // Loops thought all of the objects and updates their line offsets + void updateLineOffsets(U32 startLine, S32 diff, ParsedObject* skipObject = NULL); + + // Replaces a token on a given line with a new value + // This also calls updatePositions() to account for size + // differences between the old token and the new token + void updateToken(const U32 lineNumber, const U32 linePosition, const U32 oldValueLen, const char* newValue, bool addQuotes = false); + + // Gets the field value from the SimObject. Note that this does + // allocate memory that needs to be cleaned up elsewhere + const char* getFieldValue(SimObject* object, const char* fieldName, U32 arrayPos); + + // Attempt to find the parent object + ParsedObject* findParentObject(SimObject* object, ParsedObject* parentObject = NULL); + + // Attempt to find the matching ParsedObject in our object buffer + ParsedObject* findParsedObject(SimObject* object, ParsedObject* parentObject = NULL); + + // Attempt to find the matching DirtyObject for a passed SimObject in our DirtyItems list. + DirtyObject* findDirtyObject(SimObject* object); + + // Is this field on the remove list + bool findRemoveField(SimObject* object, const char* fieldName, U32 arrayPos = 0); + + // Helper function that allocates a new string and properly formats the data into it + // Note that this allocates memory that needs to be cleaned up elsewhere + const char* createNewProperty(const char* name, const char* value, bool isArray = false, U32 arrayPos = 0); + + // Test to see if there is anything valid on the line + bool isEmptyLine(const char* line); + + // Removes a line safely from the line buffer + void removeLine(U32 lineNumber); + // Remove a block of text from the line buffer. It returns + // the number of lines removed if removeEmptyLines is set to true. + void removeTextBlock(U32 startLine, U32 endLine, U32 startPos, U32 endPos, bool removeEmptyLines); + // Removes a ParsedObject from the line buffer + // (everything from the startLine to the endLine) + void removeParsedObject(ParsedObject* parsedObject); + // Removes a property from the line buffer + void removeField(const ParsedProperty& prop); + + // Write out properties + // Returns the number of new lines added + U32 writeProperties(const Vector& properties, const U32 insertLine, const char* objectIndent); + // Write out a new object + ParsedObject* writeNewObject(SimObject* object, const Vector& properties, const U32 insertLine, ParsedObject* parentObject = NULL); + +public: + PersistenceManager(); + virtual ~PersistenceManager(); + + bool onAdd(); + void onRemove(); + + // Adds an object to the dirty list + // Optionally changes the object's filename + bool setDirty(SimObject* object, const char* fileName = NULL); + // Removes the object from the dirty list + void removeDirty(SimObject* object); + + // Add a field to the remove list to cut it out of the object's declaration + void addRemoveField(SimObject* object, const char* fieldName); + + // Test to see if an object is on the dirty list + bool isDirty(SimObject* object); + + // Returns whether or not there are dirty objects + bool hasDirty() const { return !mDirtyObjects.empty(); } + + // Saves the dirty objects out to their respective files + // and clears the dirty object data + bool saveDirty(); + + // Saves out a single object, if it's dirty + bool saveDirtyObject(SimObject* object); + + // Clears all of the dirty object data + void clearAll(); + + // Removes the object from the file + void removeObjectFromFile(SimObject* object, const char* fileName = NULL); + + // Deletes all of the objects that were created from this file + void deleteObjectsFromFile(const char* fileName); + + // Returns a list of the dirty objects + const DirtyList& getDirtyList() { return mDirtyObjects; } + + DECLARE_CONOBJECT(PersistenceManager); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/console/propertyParsing.cpp b/Engine/source/console/propertyParsing.cpp new file mode 100644 index 000000000..85cec8dc6 --- /dev/null +++ b/Engine/source/console/propertyParsing.cpp @@ -0,0 +1,587 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#include "core/strings/stringFunctions.h" +#include "core/strings/stringUnit.h" +#include "console/consoleInternal.h" +#include "core/color.h" +#include "console/consoleTypes.h" +#include "math/mPoint2.h" +#include "math/mPoint3.h" +#include "math/mPoint4.h" +#include "math/mRect.h" +#include "math/mBox.h" +#include "math/mQuat.h" +#include "math/mAngAxis.h" +#include "math/mMatrix.h" +// Property system includes: +#include "console/propertyParsing.h" + +extern ExprEvalState gEvalState; + +namespace PropertyInfo +{ + //----------------------------------------------------------------------------- + // Bool + //----------------------------------------------------------------------------- + bool default_scan(const String &data, bool & result) + { + result = dAtob(data.c_str()); + return true; + } + + bool default_print(String & result, bool const & data) + { + if(data) + result = String("1"); + else + result = String("0"); + return true; + } + + //----------------------------------------------------------------------------- + // F32/U32/S32 + //----------------------------------------------------------------------------- + bool default_scan(const String &data, F32 & result) + { + result = dAtof(data.c_str()); + return true; + } + + bool default_print(String & result, F32 const & data) + { + result = String::ToString(data); + return true; + } + + bool default_scan(const String &data, U32 & result) + { + result = dAtoi(data.c_str()); + return true; + } + + bool default_print(String & result, U32 const & data) + { + result = String::ToString(data); + return true; + } + + bool default_scan(const String &data, S32 & result) + { + result = dAtoi(data.c_str()); + return true; + } + + bool default_print(String & result, S32 const & data) + { + result = String::ToString(data); + return true; + } + + //----------------------------------------------------------------------------- + // Basic Vector Types + //----------------------------------------------------------------------------- + template + inline void default_vector_scan(const String &data, Vector & result) + { + result.clear(); + for(S32 i = 0; i < StringUnit::getUnitCount(data, " \t\n"); i++) + result.push_back(dAtof(StringUnit::getUnit(data, i, " \t\n"))); + } + + template + inline void default_vector_print(String & result, Vector const & data) + { + result = String(""); + S32 items = data.size(); + for(S32 i = 0; i < items; i++) + { + result += String::ToString(data[i]); + if(i < items-1) + result += String(" "); + } + } + + bool default_scan(const String &data, Vector & result) + { + default_vector_scan(data,result); + return true; + } + + bool default_print(String & result, Vector const & data) + { + default_vector_print(result,data); + return true; + } + + bool default_scan(const String &data, Vector & result) + { + default_vector_scan(data,result); + return true; + } + + bool default_print(String & result, Vector const & data) + { + default_vector_print(result,data); + return true; + } + + bool default_scan(const String &data, Vector & result) + { + default_vector_scan(data,result); + return true; + } + + bool default_print(String & result, Vector const & data) + { + default_vector_print(result,data); + return true; + } + + //----------------------------------------------------------------------------- + // Math - Points + //----------------------------------------------------------------------------- + bool default_scan(const String &data, Point2F & result) + { + dSscanf(data.c_str(),"%g %g",&result.x,&result.y); + return true; + } + + bool default_print(String & result, Point2F const & data) + { + result = String::ToString("%g %g",data.x,data.y); + return true; + } + + bool default_scan(const String &data, Point2I & result) + { + // Handle passed as floating point from script + if(data.find('.') != String::NPos) + { + Point2F tempResult; + dSscanf(data.c_str(),"%f %f",&tempResult.x,&tempResult.y); + result.x = mFloor(tempResult.x); + result.y = mFloor(tempResult.y); + } + else + dSscanf(data.c_str(),"%d %d",&result.x,&result.y); + return true; + } + + bool default_print(String & result, Point2I const & data) + { + result = String::ToString("%d %d",data.x,data.y); + return true; + } + + bool default_scan(const String &data, Point3F & result) + { + dSscanf(data.c_str(),"%g %g %g",&result.x,&result.y,&result.z); + return true; + } + + bool default_print(String & result, Point3F const & data) + { + result = String::ToString("%g %g %g",data.x,data.y,data.z); + return true; + } + + bool default_scan(const String &data, Point3I & result) + { + // Handle passed as floating point from script + if(data.find('.') != String::NPos) + { + Point3F tempResult; + dSscanf(data.c_str(),"%f %f %f",&tempResult.x,&tempResult.y,&tempResult.z); + result.x = mFloor(tempResult.x); + result.y = mFloor(tempResult.y); + result.z = mFloor(tempResult.z); + } + else + dSscanf(data.c_str(),"%d %d %d",&result.x,&result.y,&result.z); + return true; + } + + bool default_print(String & result, Point3I const & data) + { + result = String::ToString("%d %d %d",data.x,data.y,data.z); + return true; + } + + bool default_scan(const String &data, Point4F & result) + { + dSscanf(data.c_str(),"%g %g %g %g",&result.x,&result.y,&result.z,&result.w); + return true; + } + + bool default_print(String & result, Point4F const & data) + { + result = String::ToString("%g %g %g %g",data.x,data.y,data.z,data.w); + return true; + } + + bool default_scan(const String &data, Point4I & result) + { + // Handle passed as floating point from script + if(data.find('.') != String::NPos) + { + Point4F tempResult; + dSscanf(data.c_str(),"%f %f %f %f",&tempResult.x,&tempResult.y,&tempResult.z,&tempResult.w); + result.x = mFloor(tempResult.x); + result.y = mFloor(tempResult.y); + result.z = mFloor(tempResult.z); + result.w = mFloor(tempResult.w); + } + else + dSscanf(data.c_str(),"%d %d %d %d",&result.x,&result.y,&result.z,&result.w); + return true; + } + + bool default_print(String & result, const Point4I & data) + { + result = String::ToString("%d %d %d %d", data.x, data.y, data.z, data.w); + return true; + } + + //----------------------------------------------------------------------------- + // Math - Rectangles and boxes + //----------------------------------------------------------------------------- + bool default_scan( const String &data, RectI & result ) + { + // Handle passed as floating point from script + if(data.find('.') != String::NPos) + { + RectF tempResult; + dSscanf(data.c_str(),"%f %f %f %f",&tempResult.point.x,&tempResult.point.y,&tempResult.extent.x,&tempResult.extent.y); + result.point.x = mFloor(tempResult.point.x); + result.point.y = mFloor(tempResult.point.y); + result.extent.x = mFloor(tempResult.extent.x); + result.extent.y = mFloor(tempResult.extent.y); + } + else + dSscanf(data.c_str(),"%d %d %d %d",&result.point.x,&result.point.y,&result.extent.x,&result.extent.y); + return true; + } + bool default_print( String & result, const RectI & data ) + { + result = String::ToString("%i %i %i %i",data.point.x,data.point.y,data.extent.x,data.extent.y); + return true; + } + + bool default_scan(const String &data, RectF & result) + { + dSscanf(data.c_str(),"%g %g %g %g",&result.point.x,&result.point.y,&result.extent.x,&result.extent.y); + return true; + } + + bool default_print(String & result, const RectF & data) + { + result = String::ToString("%g %g %g %g",data.point.x,data.point.y,data.extent.x,data.extent.y); + return true; + } + + bool default_scan(const String &data, Box3F & result) + { + dSscanf(data.c_str(),"%g %g %g %g %g %g", + &result.minExtents.x,&result.minExtents.y,&result.minExtents.z, + &result.maxExtents.x,&result.maxExtents.y,&result.maxExtents.z); + return true; + } + + bool default_print(String & result, const Box3F & data) + { + result = String::ToString("%g %g %g %g %g %g", + data.minExtents.x,data.minExtents.y,data.minExtents.z, + data.maxExtents.x,data.maxExtents.y,data.maxExtents.z); + return true; + } + + //----------------------------------------------------------------------------- + + bool default_scan( const String &data, AngAxisF & result ) + { + if(StringUnit::getUnitCount(data," ") < 4) + return false; + + dSscanf(data.c_str(),"%g %g %g %g", &result.axis.x,&result.axis.y,&result.axis.z,&result.angle); + result.angle = mDegToRad(result.angle); + return true; + } + + bool default_print( String & result, const AngAxisF & data ) + { + F32 angle = mRadToDeg(data.angle); + angle = mFmod(angle + 360.0f,360.0f); + result = String::ToString("%g %g %g %g", data.axis.x, data.axis.y, data.axis.z, angle); + return true; + } + + bool default_scan( const String &data, QuatF & result ) + { + if(StringUnit::getUnitCount(data," ") < 4) + return false; + + dSscanf(data.c_str(),"%g %g %g %g", &result.x,&result.y,&result.z,&result.w); + return true; + } + + bool default_print( String & result, const QuatF & data ) + { + result = String::ToString("%g %g %g %g", data.x, data.y, data.z, data.w); + return true; + } + + bool default_scan( const String &data, MatrixF & result ) + { + if(StringUnit::getUnitCount(data," ") < 16) + return false; + + F32* m = result; + dSscanf(data.c_str(),"%g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g", + &m[result.idx(0,0)], &m[result.idx(0,1)], &m[result.idx(0,2)], &m[result.idx(0,3)], + &m[result.idx(1,0)], &m[result.idx(1,1)], &m[result.idx(1,2)], &m[result.idx(1,3)], + &m[result.idx(2,0)], &m[result.idx(2,1)], &m[result.idx(2,2)], &m[result.idx(2,3)], + &m[result.idx(3,0)], &m[result.idx(3,1)], &m[result.idx(3,2)], &m[result.idx(3,3)]); + return true; + } + + bool default_print( String & result, const MatrixF & data ) + { + const F32* m = data; + result = String::ToString("%g %g %g %g %g %g %g %g %g %g %g %g %g %g %g %g", + m[data.idx(0,0)], m[data.idx(0,1)], m[data.idx(0,2)], m[data.idx(0,3)], + m[data.idx(1,0)], m[data.idx(1,1)], m[data.idx(1,2)], m[data.idx(1,3)], + m[data.idx(2,0)], m[data.idx(2,1)], m[data.idx(2,2)], m[data.idx(2,3)], + m[data.idx(3,0)], m[data.idx(3,1)], m[data.idx(3,2)], m[data.idx(3,3)]); + return true; + } + + //----------------------------------------------------------------------------- + // Colors + //----------------------------------------------------------------------------- + bool default_scan(const String &data, ColorF & result) + { + if(StringUnit::getUnitCount(data," ") == 3) + { + dSscanf(data.c_str(),"%g %g %g",&result.red,&result.green,&result.blue); + result.alpha = 1.0f; + } + else + dSscanf(data.c_str(),"%g %g %g %g",&result.red,&result.green,&result.blue,&result.alpha); + return true; + } + + bool default_print(String & result, ColorF const & data) + { + if(data.alpha == 1.0f) + result = String::ToString("%g %g %g",data.red,data.green,data.blue); + else + result = String::ToString("%g %g %g %g",data.red,data.green,data.blue,data.alpha); + return true; + } + + bool default_scan(const String &data, ColorI & result) + { + if(StringUnit::getUnitCount(data," ") == 3) + { + S32 r,g,b; + dSscanf(data.c_str(),"%i %i %i",&r,&g,&b); + result.set(r,g,b); + } + else + { + S32 r,g,b,a; + dSscanf(data.c_str(),"%i %i %i %i",&r,&g,&b,&a); + result.set(r,g,b,a); + } + return true; + } + + bool default_print(String & result, const ColorI & data) + { + if(data.alpha == 255) + result = String::ToString("%d %d %d",data.red,data.green,data.blue); + else + result = String::ToString("%d %d %d %d",data.red,data.green,data.blue,data.alpha); + return true; + } + + //----------------------------------------------------------------------------- + // String + //----------------------------------------------------------------------------- + bool default_scan(const String &data, String & result) + { + result = data; + return true; + } + + bool default_print(String & result, const String & data) + { + result = data; + return true; + } + + //----------------------------------------------------------------------------- + // FileName + //----------------------------------------------------------------------------- + bool default_scan(const String &data, FileName & result) + { + char buffer[1024]; + + if(data.c_str()[0] == '$') + { + dStrncpy(buffer, data.c_str(), sizeof(buffer) - 1); + buffer[sizeof(buffer)-1] = 0; + } + else if (!Con::expandScriptFilename(buffer, sizeof(buffer), data)) + { + Con::warnf("(TypeFilename) illegal filename detected: %s", data.c_str()); + return false; + } + result = String(buffer); + return true; + } + + bool default_print(String & result, const FileName & data) + { + result = data; + return true; + } + + //----------------------------------------------------------------------------- + // SimObject + //----------------------------------------------------------------------------- + bool default_scan(const String &data, SimObject * & result) + { + result = Sim::findObject(data); + return result != NULL; + } + + bool default_print(String & result, SimObject * const & data) + { + if(data) + { + if(String(data->getName()).isEmpty()) + result = data->getIdString(); + else + result = data->getName(); + return true; + } + return false; + } + + //----------------------------------------------------------------------------- + // Print scan ints of various sizes as hex + //----------------------------------------------------------------------------- + + //------- + // 16 bit + //------- + + bool hex_scan(const String & string, U32 & hex) + { + dSscanf(string.c_str(),"%i", &hex); + return true; + } + + bool hex_print(String & string, const U32 & hex) + { + string = String::ToString("0x%X",hex); + return true; + } + + bool hex_scan(const String & string, S32 & hex) + { + dSscanf(string.c_str(),"%i", &hex); + return true; + } + + bool hex_print(String & string, const S32 & hex) + { + string = String::ToString("0x%X",hex); + return true; + } + + //------- + // 16 bit + //------- + + bool hex_scan(const String & string, U16 & hex) + { + U32 tmp; + bool ret = hex_scan(string,tmp); + hex = tmp; + return ret; + } + + bool hex_print(String & string, const U16 & hex) + { + U32 tmp = hex; + return hex_print(string,tmp); + } + + bool hex_scan(const String & string, S16 & hex) + { + S32 tmp; + bool ret = hex_scan(string,tmp); + hex = tmp; + return ret; + } + + bool hex_print(String & string, const S16 & hex) + { + U32 tmp = hex; + return hex_print(string,tmp); + } + + //------- + // 8 bit + //------- + + bool hex_scan(const String & string, U8 & hex) + { + U32 tmp; + bool ret = hex_scan(string,tmp); + hex = tmp; + return ret; + } + + bool hex_print(String & string, const U8 & hex) + { + U32 tmp = hex; + return hex_print(string,tmp); + } + + bool hex_scan(const String & string, S8 & hex) + { + S32 tmp; + bool ret = hex_scan(string,tmp); + hex = tmp; + return ret; + } + + bool hex_print(String & string, const S8 & hex) + { + U32 tmp = hex; + return hex_print(string,tmp); + } + +} diff --git a/Engine/source/console/propertyParsing.h b/Engine/source/console/propertyParsing.h new file mode 100644 index 000000000..5ee60ac90 --- /dev/null +++ b/Engine/source/console/propertyParsing.h @@ -0,0 +1,166 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PROPERTYPARSING_H_ +#define _PROPERTYPARSING_H_ + +class ColorI; +class ColorF; +class Point2I; +class Point2F; +class Point3F; +class Point4F; +class Point3I; +class Point4I; +class RectI; +class RectF; +class Box3I; +class Box3F; +class MatrixF; +class AngAxisF; +class QuatF; +class String; +class FileName; +class SimObject; +class SimObjectType; +template class Vector; + +//----------------------------------------------------------------------------- +// String scan/print methods +// +// Macros in propertyImplementation.h point the ConcretePropertyDefinition scan/print delegates to these functions. +// +namespace PropertyInfo +{ + // String + bool default_scan(const String &data, String & result); + bool default_print(String & result, const String & data); + + // Bool + bool default_scan(const String &data, bool & result); + bool default_print(String & result, const bool & data); + + // S32/F32/U32 + bool default_scan(const String &data, F32 & result); + bool default_print(String & result, const F32 & data); + bool default_scan(const String &data, U32 & result); + bool default_print(String & result, const U32 & data); + bool default_scan(const String &data, S32 & result); + bool default_print(String & result, const S32 & data); + + // Vector + bool default_scan(const String &data, Vector & result); + bool default_print(String & result, const Vector & data); + bool default_scan(const String &data, Vector & result); + bool default_print(String & result, const Vector & data); + bool default_scan(const String &data, Vector & result); + bool default_print(String & result, const Vector & data); + + // Math Points + bool default_scan(const String &data, Point2F & result); + bool default_print(String & result, const Point2F & data); + bool default_scan(const String &data, Point2I & result); + bool default_print(String & result, const Point2I & data); + bool default_scan(const String &data, Point3F & result); + bool default_print(String & result, const Point3F & data); + bool default_scan(const String &data, Point3I & result); + bool default_print(String & result, const Point3I & data); + bool default_scan(const String &data, Point4F & result); + bool default_print(String & result, const Point4F & data); + bool default_scan(const String &data, Point4I & result); + bool default_print(String & result, const Point4I & data); + + // Math Boxs & Rectangles + bool default_scan(const String &data, RectI & result); + bool default_print(String & result, const RectI & data); + bool default_scan(const String &data, RectF & result); + bool default_print(String & result, const RectF & data); + bool default_scan(const String &data, Box3I & result); + bool default_print(String & result, const Box3I & data); + bool default_scan(const String &data, Box3F & result); + bool default_print(String & result, const Box3F & data); + + //----------------------------------------------------------------------------- + bool default_scan( const String &data, AngAxisF & result ); + bool default_print( String & result, const AngAxisF & data ); + + bool default_scan( const String &data, QuatF & result ); + bool default_print( String & result, const QuatF & data ); + + bool default_scan( const String &data, MatrixF & result ); + bool default_print( String & result, const MatrixF & data ); + + // Colors + bool default_scan(const String &data, ColorF & result); + bool default_print(String & result, const ColorF & data); + bool default_scan(const String &data, ColorI & result); + bool default_print(String & result, const ColorI & data); + + // filename handler + bool default_scan(const String &data, FileName & result); + bool default_print(String & result, const FileName & data); + + // SimObjectType + bool default_scan(const String &data, SimObjectType & result); + bool default_print(String & result, SimObjectType const & data); + + // SimObject + bool default_scan(const String &data, SimObject * & result); + bool default_print(String & result, SimObject * const & data); + + template + inline bool typed_simobject_scan(const String &data, T * & result) + { + SimObject * obj; + result = default_scan(data,obj)? dynamic_cast(obj) : NULL; + return result; + } + + template + inline bool typed_simobject_print(String & result, T * const & data) + { + return default_print(result,data); + } + + bool hex_scan(const String & string, U32 & hex); + bool hex_print(String & string, const U32 & hex); + bool hex_scan(const String & string, S32 & hex); + bool hex_print(String & string, const S32 & hex); + + bool hex_scan(const String & string, U16 & hex); + bool hex_print(String & string, const U16 & hex); + bool hex_scan(const String & string, S16 & hex); + bool hex_print(String & string, const S16 & hex); + + bool hex_scan(const String & string, U8 & hex); + bool hex_print(String & string, const U8 & hex); + bool hex_scan(const String & string, S8 & hex); + bool hex_print(String & string, const S8 & hex); + + bool default_print(String & result, SimObjectType * const & data); +} + +// Default Scan/print definition +#define DEFINE_PROPERTY_DEFAULT_PRINT(dataType) bool PropertyInfo::default_print(String & resultString, dataType const & dataTyped) +#define DEFINE_PROPERTY_DEFAULT_SCAN(dataType) bool PropertyInfo::default_scan(const String &dataString, dataType & resultTyped) + +#endif // _PROPERTYPARSING_H_ diff --git a/Engine/source/console/runtimeClassRep.cpp b/Engine/source/console/runtimeClassRep.cpp new file mode 100644 index 000000000..19cbf2762 --- /dev/null +++ b/Engine/source/console/runtimeClassRep.cpp @@ -0,0 +1,26 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/runtimeClassRep.h" + +//----------------------------------------------------------------------------- + diff --git a/Engine/source/console/runtimeClassRep.h b/Engine/source/console/runtimeClassRep.h new file mode 100644 index 000000000..d7fd4a797 --- /dev/null +++ b/Engine/source/console/runtimeClassRep.h @@ -0,0 +1,174 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _RUNTIME_CLASSREP_H_ +#define _RUNTIME_CLASSREP_H_ + +#include "console/consoleObject.h" +#include "console/consoleInternal.h" + +/// This class is to allow new types to be added, at run-time, to Torque. +/// This is primarily for dynamic libraries, plugins etc. +/// +/// The idea is the same, one instance per type which is getting registered +/// however it doesn't get added to the dictionary until it is told to +/// add itself. It can be removed, as well, though no safe-execution +/// assurances are made by this class. If an object type is removed +/// behavior of existing console objects of that type is undefined. +/// (aka bad stuff will probably happen but I don't know exactly what) +template +class RuntimeClassRep : public AbstractClassRep +{ +protected: + static bool smConRegistered; ///< This variable will be set to true if this class-rep is currently registered + +public: + RuntimeClassRep( const char *name, const char* conTypeName, S32* conTypeIdPtr, S32 netClassGroupMask, S32 netClassType, S32 netEventDir, AbstractClassRep *parent ) + : AbstractClassRep( conTypeIdPtr, conTypeName ) + { + // name is a static compiler string so no need to worry about copying or deleting + mClassName = name; + mTypeInfo = _MAPTYPE< T >(); + + // Clean up mClassId + for( U32 i = 0; i < NetClassGroupsCount; i++ ) + mClassId[i] = -1; + + // Set properties for this ACR + mClassType = netClassType; + mClassGroupMask = netClassGroupMask; + mNetEventDir = netEventDir; + parentClass = parent; + }; + + /// Perform class specific initialization tasks. + /// + /// Link namespaces, call initPersistFields() and consoleInit(). + void init() const + { + // Get handle to our parent class, if any, and ourselves (we are our parent's child). + AbstractClassRep *parent = T::getParentStaticClassRep(); + AbstractClassRep *child = T::getStaticClassRep (); + + // If we got reps, then link those namespaces! (To get proper inheritance.) + if( parent && child ) + Con::classLinkNamespaces( parent->getNameSpace(), child->getNameSpace() ); + + // Finally, do any class specific initialization... + T::initPersistFields(); + T::consoleInit(); + } + + /// Wrap constructor. + ConsoleObject *create() const { return new T; } + + //----------------------------------------------------------------------------- + + /// Register this class with the Torque console + void consoleRegister() + { + AssertFatal( !smConRegistered, "Calling consoleRegister, but this type is already linked into the class list" ); + + if( !smConRegistered ) + registerClassRep( this ); + + // Now initialize the namespace + mNamespace = Con::lookupNamespace( StringTable->insert( getClassName() ) ); + mNamespace->mClassRep = this; + + // Perform field initialization + sg_tempFieldList.setSize(0); + + init(); + + // So if we have things in it, copy it over... + if ( sg_tempFieldList.size() != 0 ) + mFieldList = sg_tempFieldList; + + // And of course delete it every round. + sg_tempFieldList.clear(); + + smConRegistered = true; + } + + /// Unregister this class with the Torque console + void consoleUnRegister() + { + AssertFatal( smConRegistered, "Calling consoleUnRegister, but this type is not linked into the class list" ); + if( !smConRegistered ) + return; + + removeClassRep( this ); + smConRegistered = false; + } + + /// Returns true if this class type is registered with the console system + static const bool isRegistered() { return smConRegistered; } + + /// @name Console Type Interface + /// @{ + + virtual void setData( void* dptr, S32 argc, const char** argv, const EnumTable* tbl, BitSet32 flag ) + { + if( argc == 1 ) + { + T** obj = ( T** ) dptr; + *obj = dynamic_cast< T* >( T::__findObject( argv[ 0 ] ) ); + } + else + Con::errorf( "Cannot set multiple args to a single ConsoleObject*."); + } + + virtual const char* getData( void* dptr, const EnumTable* tbl, BitSet32 flag ) + { + T** obj = ( T** ) dptr; + return Con::getReturnBuffer( T::__getObjectId( *obj ) ); + } + + virtual const char* getTypeClassName() { return mClassName; } + virtual const bool isDatablock() { return T::__smIsDatablock; }; + + /// @} +}; + +template bool RuntimeClassRep::smConRegistered = false; + +//----------------------------------------------------------------------------- + +#define DECLARE_RUNTIME_CONOBJECT(className) \ + DECLARE_CLASS( className, Parent ); \ + static S32 _sTypeId; \ + static RuntimeClassRep dynRTClassRep; \ + static AbstractClassRep* getParentStaticClassRep(); \ + static AbstractClassRep* getStaticClassRep(); \ + virtual AbstractClassRep* getClassRep() const + +#define IMPLEMENT_RUNTIME_CONOBJECT(className) \ + IMPLEMENT_CLASS( className, NULL ) \ + END_IMPLEMENT_CLASS; \ + S32 className::_sTypeId; \ + AbstractClassRep* className::getClassRep() const { return &className::dynRTClassRep; } \ + AbstractClassRep* className::getStaticClassRep() { return &dynRTClassRep; } \ + AbstractClassRep* className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ + RuntimeClassRep className::dynRTClassRep(#className, "Type" #className, &_sTypeId, 0, -1, 0, className::getParentStaticClassRep()) + +#endif diff --git a/Engine/source/console/scriptFilename.cpp b/Engine/source/console/scriptFilename.cpp new file mode 100644 index 000000000..35631e60b --- /dev/null +++ b/Engine/source/console/scriptFilename.cpp @@ -0,0 +1,395 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/scriptFilename.h" + +#include "core/frameAllocator.h" +#include "core/tSimpleHashTable.h" +#include "core/strings/stringFunctions.h" +#include "core/stringTable.h" +#include "console/console.h" +#include "console/compiler.h" + + +namespace Con +{ +//----------------------------------------------------------------------------- +// Local Globals +//----------------------------------------------------------------------------- + +struct PathExpando +{ + StringTableEntry mPath; + bool mIsToolsOnly; +}; + +static SimpleHashTable sgPathExpandos(64, false); + +//----------------------------------------------------------------------------- +// Global Functions +//----------------------------------------------------------------------------- + +void setScriptPathExpando(const char *expando, const char *path, bool toolsOnly /*= false*/) +{ + PathExpando *exp = sgPathExpandos.retreive(expando); + if(exp) + { + exp->mPath = StringTable->insert(path); + exp->mIsToolsOnly = toolsOnly; + } + else + { + exp = new PathExpando; + exp->mPath = StringTable->insert(path); + exp->mIsToolsOnly = toolsOnly; + sgPathExpandos.insert(exp, expando); + } +} + +void removeScriptPathExpando(const char *expando) +{ + PathExpando *exp = sgPathExpandos.remove(expando); + if(exp) + delete exp; +} + +bool isScriptPathExpando(const char *expando) +{ + PathExpando *exp = sgPathExpandos.retreive(expando); + return ( exp != NULL); +} + +//----------------------------------------------------------------------------- + +// [tom, 5/18/2006] FIXME: This needs some bounds checking +bool expandToolScriptFilename(char *filename, U32 size, const char *src) +{ + // [tom, 10/16/2006] Note: I am purposefully not early-outing here in the + // same way the old code did as it is now possible that something could + // be expanded if the name or mod is NULL. This was previously not the case. + + const StringTableEntry cbMod = CodeBlock::getCurrentCodeBlockModName(); + const StringTableEntry cbFullPath = CodeBlock::getCurrentCodeBlockFullPath(); + + char varBuf[1024], modBuf[1024]; + const char *ptr = src; + char *retPtr = filename; + char *slash; + + const char *catPath = NULL; + +#ifndef TORQUE_DEBUG + bool isTools = Con::isCurrentScriptToolScript(); +#endif + + // Check leading character + switch(*ptr) + { + case '^': + { + // Variable + const char *varPtr = ptr+1; + char *insertPtr = varBuf; + bool valid = true; + while(*varPtr != '/') + { + if(*varPtr == 0) + { + valid = false; + break; + } + *insertPtr++ = *varPtr++; + } + + if(valid) + { + // Got a valid variable + *insertPtr = 0; + + PathExpando *exp = sgPathExpandos.retreive(varBuf); + + if(exp == NULL) + { + Con::errorf("expandScriptFilename - Ignoring invalid path expando \"%s\"", varBuf); + break; + } + +#ifndef TORQUE_DEBUG + // [tom, 12/13/2006] This stops tools expandos from working in the console, + // which is useful behavior when debugging so I'm ifdefing this out for debug builds. + + if(! isTools && exp->mIsToolsOnly) + { + Con::errorf("expandScriptFilename - attempting to use tools only expando \"%s\" from outside of tools", varBuf); + + *filename = 0; + return false; + } +#endif + + catPath = exp ? exp->mPath : ""; + // swallow the expando and the slash after the expando + ptr += dStrlen(varBuf) + 1; + if(*ptr == '/') + ptr++; + } + } + break; + + case '~': + // Relative to mod + if(cbMod && cbFullPath) + { + Platform::makeFullPathName(cbMod, modBuf, sizeof(modBuf)); + catPath = modBuf; + } + else + { + // Probably not a mod, so we'll use the ^game expando + PathExpando *exp = sgPathExpandos.retreive("game"); + + if(exp == NULL) + { + Con::errorf("expandScriptFilename - ~ expansion failed for mod and ^game when processing '%s'", src); + break; + } + + catPath = exp ? exp->mPath : ""; + } + // swallow ~ and optional slash + switch(ptr[1]) + { + case '/': ptr += 2; break; + default: ptr++; + } + + break; + + case '.': + // Relative to script directory + if(cbFullPath) + { + dStrcpy(varBuf, cbFullPath); + slash = dStrrchr(varBuf, '/'); + if(slash) *slash = 0; + + catPath = varBuf; + + // swallow dot and optional slash, but dont swallow .. relative path token + switch(ptr[1]) + { + case '.': break; + case '/': ptr += 2; break; + default: ptr++; + } + } + break; + } + + // [tom, 11/20/2006] Handing off to makeFullPathName() allows us to process .. correctly. + Platform::makeFullPathName(ptr, retPtr, size, catPath); + + return true; +} + +//----------------------------------------------------------------------------- + +bool expandOldScriptFilename(char *filename, U32 size, const char *src) +{ + const StringTableEntry cbName = CodeBlock::getCurrentCodeBlockName(); + if (!cbName) + { + dStrcpy(filename, src); + return true; + } + + const char *slash; + if (dStrncmp(src, "~/", 2) == 0) + // tilde path means load from current codeblock/mod root + slash = dStrchr(cbName, '/'); + else if (dStrncmp(src, "./", 2) == 0) + // dot path means load from current codeblock/mod path + slash = dStrrchr(cbName, '/'); + else + { + // otherwise path must be fully specified + if (dStrlen(src) > size) + { + Con::errorf("Buffer overflow attempting to expand filename: %s", src); + *filename = 0; + return false; + } + dStrcpy(filename, src); + return true; + } + + if (slash == NULL) + { + Con::errorf("Illegal CodeBlock path detected (no mod directory): %s", cbName); + *filename = 0; + return false; + } + + U32 length = slash-cbName; + if ((length+dStrlen(src)) > size) + { + Con::errorf("Buffer overflow attempting to expand filename: %s", src); + *filename = 0; + return false; + } + + dStrncpy(filename, cbName, length); + dStrcpy(filename+length, src+1); + return true; +} + +//----------------------------------------------------------------------------- + +bool expandScriptFilename(char *filename, U32 size, const char *src) +{ +#ifndef TORQUE2D_TOOLS_FIXME + return expandOldScriptFilename(filename, size, src); +#else + return expandToolScriptFilename(filename, size, src); +#endif +} + +//----------------------------------------------------------------------------- + +static StringTableEntry tryGetBasePath(const char *path, const char *base) +{ + U32 len = dStrlen(base); + if(dStrnicmp(path, base, len) == 0) + { + if(*(path + len) == '/') --len; + return StringTable->insertn(path, len, true); + } + return NULL; +} + +struct CollapseTest +{ + const char *path; + const char *replace; +}; + +bool collapseScriptFilename(char *filename, U32 size, const char *src) +{ + PathExpando *tools = sgPathExpandos.retreive("tools"); + PathExpando *game = sgPathExpandos.retreive("game"); + + CollapseTest test[]= + { + { game ? game->mPath : NULL, "~" }, + { tools ? tools->mPath : NULL, "^tools" }, + { Platform::getCurrentDirectory(), "" }, + { Platform::getMainDotCsDir(), "" }, + { NULL, NULL } + }; + + for(S32 i = 0;!(test[i].path == NULL && test[i].replace == NULL);++i) + { + if(test[i].path == NULL) continue; + + StringTableEntry base = tryGetBasePath(src, test[i].path); + if(base == NULL) + continue; + + StringTableEntry rel = Platform::makeRelativePathName(src, test[i].path); + + *filename = 0; + if(*test[i].replace) + dSprintf(filename, size, "%s/", test[i].replace); + dStrcat(filename, rel); + return true; + } + + dStrncpy(filename, src, size - 1); + filename[size-1] = 0; + return true; +} + +//----------------------------------------------------------------------------- + +} // end namespace Con + +//----------------------------------------------------------------------------- +// Console Functions +//----------------------------------------------------------------------------- + +ConsoleFunction(expandFilename, const char*, 2, 2, "(string filename)" + "@brief Grabs the full path of a specified file\n\n" + "@param filename Name of the local file to locate\n" + "@return String containing the full filepath on disk\n" + "@ingroup FileSystem") +{ + TORQUE_UNUSED(argc); + char* ret = Con::getReturnBuffer( 1024 ); + Con::expandScriptFilename(ret, 1024, argv[1]); + return ret; +} + +ConsoleFunction(expandOldFilename, const char*, 2, 2, "(string filename)" + "@brief Retrofits a filepath that uses old Torque style\n\n" + "@return String containing filepath with new formatting\n" + "@ingroup FileSystem") +{ + TORQUE_UNUSED(argc); + char* ret = Con::getReturnBuffer( 1024 ); + Con::expandOldScriptFilename(ret, 1024, argv[1]); + return ret; +} + +//----------------------------------------------------------------------------- +// Tool Functions +//----------------------------------------------------------------------------- + +ConsoleToolFunction(collapseFilename, const char*, 2, 2, "(string filename)" + "@internal Editor use only") +{ + TORQUE_UNUSED(argc); + char* ret = Con::getReturnBuffer( 1024 ); + Con::collapseScriptFilename(ret, 1024, argv[1]); + return ret; +} + +ConsoleToolFunction(setScriptPathExpando, void, 3, 4, "(string expando, string path[, bool toolsOnly])" + "@internal Editor use only") +{ + if(argc == 4) + Con::setScriptPathExpando(argv[1], argv[2], dAtob(argv[3])); + else + Con::setScriptPathExpando(argv[1], argv[2]); +} + +ConsoleToolFunction(removeScriptPathExpando, void, 2, 2, "(string expando)" + "@internal Editor use only") +{ + Con::removeScriptPathExpando(argv[1]); +} + +ConsoleToolFunction(isScriptPathExpando, bool, 2, 2, "(string expando)" + "@internal Editor use only") +{ + return Con::isScriptPathExpando(argv[1]); +} diff --git a/Engine/source/console/scriptFilename.h b/Engine/source/console/scriptFilename.h new file mode 100644 index 000000000..2dbc33fe1 --- /dev/null +++ b/Engine/source/console/scriptFilename.h @@ -0,0 +1,35 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SCRIPTFILENAME_H_ +#define _SCRIPTFILENAME_H_ + +namespace Con +{ + +extern void setScriptPathExpando(const char *expando, const char *path, bool toolsOnly = false); +extern void removeScriptPathExpando(const char *expando); +extern bool isScriptPathExpando(const char *expando); + +} // end namespace Con + +#endif // _SCRIPTFILENAME_H_ diff --git a/Engine/source/console/scriptObjects.cpp b/Engine/source/console/scriptObjects.cpp new file mode 100644 index 000000000..2f7869976 --- /dev/null +++ b/Engine/source/console/scriptObjects.cpp @@ -0,0 +1,172 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/simBase.h" +#include "console/consoleTypes.h" +#include "console/scriptObjects.h" +#include "console/simBase.h" +#include "console/engineAPI.h" + +IMPLEMENT_CONOBJECT(ScriptObject); + +ConsoleDocClass( ScriptObject, + "@brief A script-level OOP object which allows binding of a class, superClass and arguments along with declaration of methods.\n\n" + + "ScriptObjects are extrodinarily powerful objects that allow defining of any type of data required. They can optionally have\n" + "a class and a superclass defined for added control of multiple ScriptObjects through a simple class definition.\n\n" + + "@tsexample\n" + "new ScriptObject(Game)\n" + "{\n" + " class = \"DeathMatchGame\";\n" + " superClass = GameCore;\n" + " genre = \"Action FPS\"; // Note the new, non-Torque variable\n" + "};\n" + "@endtsexample\n" + "@see SimObject\n" + "@ingroup Console\n" + "@ingroup Scripting" +); + +IMPLEMENT_CALLBACK( ScriptObject, onAdd, void, ( SimObjectId ID ), ( ID ), + "Called when this ScriptObject is added to the system.\n" + "@param ID Unique object ID assigned when created (%this in script).\n" +); + +IMPLEMENT_CALLBACK( ScriptObject, onRemove, void, ( SimObjectId ID ), ( ID ), + "Called when this ScriptObject is removed from the system.\n" + "@param ID Unique object ID assigned when created (%this in script).\n" +); + +ScriptObject::ScriptObject() +{ +} + +bool ScriptObject::onAdd() +{ + if (!Parent::onAdd()) + return false; + + // Call onAdd in script! + onAdd_callback(getId()); + return true; +} + +void ScriptObject::onRemove() +{ + // We call this on this objects namespace so we unlink them after. - jdd + // + // Call onRemove in script! + onRemove_callback(getId()); + + Parent::onRemove(); +} + +//----------------------------------------------------------------------------- +// Script group placeholder +//----------------------------------------------------------------------------- + +class ScriptGroup : public SimGroup +{ + typedef SimGroup Parent; + +public: + ScriptGroup(); + bool onAdd(); + void onRemove(); + + DECLARE_CONOBJECT(ScriptGroup); + + DECLARE_CALLBACK(void, onAdd, (SimObjectId ID) ); + DECLARE_CALLBACK(void, onRemove, (SimObjectId ID)); +}; + +IMPLEMENT_CONOBJECT(ScriptGroup); + +ConsoleDocClass( ScriptGroup, + "@brief Essentially a SimGroup, but with onAdd and onRemove script callbacks.\n\n" + + "@tsexample\n" + "// First container, SimGroup containing a ScriptGroup\n" + "new SimGroup(Scenes)\n" + "{\n" + " // Subcontainer, ScriptGroup containing variables\n" + " // related to a cut scene and a starting WayPoint\n" + " new ScriptGroup(WelcomeScene)\n" + " {\n" + " class = \"Scene\";\n" + " pathName = \"Pathx\";\n" + " description = \"A small orc village set in the Hardesty mountains. This town and its surroundings will be used to illustrate some the Torque Game Engine\'s features.\";\n" + " pathTime = \"0\";\n" + " title = \"Welcome to Orc Town\";\n\n" + " new WayPoint(start)\n" + " {\n" + " position = \"163.873 -103.82 208.354\";\n" + " rotation = \"0.136165 -0.0544916 0.989186 44.0527\";\n" + " scale = \"1 1 1\";\n" + " dataBlock = \"WayPointMarker\";\n" + " team = \"0\";\n" + " };\n" + " };\n" + "};\n" + "@endtsexample\n\n" + + "@see SimGroup\n" + + "@ingroup Console\n" + "@ingroup Scripting" +); + +ScriptGroup::ScriptGroup() +{ +} + +IMPLEMENT_CALLBACK( ScriptGroup, onAdd, void, ( SimObjectId ID ), ( ID ), + "Called when this ScriptGroup is added to the system.\n" + "@param ID Unique object ID assigned when created (%this in script).\n" +); + +IMPLEMENT_CALLBACK( ScriptGroup, onRemove, void, ( SimObjectId ID ), ( ID ), + "Called when this ScriptObject is removed from the system.\n" + "@param ID Unique object ID assigned when created (%this in script).\n" +); + +bool ScriptGroup::onAdd() +{ + if (!Parent::onAdd()) + return false; + + // Call onAdd in script! + //Con::executef(this, "onAdd", Con::getIntArg(getId())); + onAdd_callback(getId()); + return true; +} + +void ScriptGroup::onRemove() +{ + // Call onRemove in script! + //Con::executef(this, "onRemove", Con::getIntArg(getId())); + onRemove_callback(getId()); + + Parent::onRemove(); +} diff --git a/Engine/source/console/scriptObjects.h b/Engine/source/console/scriptObjects.h new file mode 100644 index 000000000..65780b633 --- /dev/null +++ b/Engine/source/console/scriptObjects.h @@ -0,0 +1,49 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SCRIPTOBJECTS_H_ +#define _SCRIPTOBJECTS_H_ + +#ifndef _CONSOLEINTERNAL_H_ +#include "console/consoleInternal.h" +#endif + +//----------------------------------------------------------------------------- +// Script object placeholder +//----------------------------------------------------------------------------- + +class ScriptObject : public SimObject +{ + typedef SimObject Parent; + +public: + ScriptObject(); + bool onAdd(); + void onRemove(); + + DECLARE_CONOBJECT(ScriptObject); + + DECLARE_CALLBACK(void, onAdd, (SimObjectId ID) ); + DECLARE_CALLBACK(void, onRemove, (SimObjectId ID)); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/console/sim.cpp b/Engine/source/console/sim.cpp new file mode 100644 index 000000000..974d2a044 --- /dev/null +++ b/Engine/source/console/sim.cpp @@ -0,0 +1,271 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/console.h" + +#include "console/sim.h" +#include "console/simEvents.h" +#include "console/simObject.h" +#include "console/simSet.h" +#include "core/module.h" + + +MODULE_BEGIN( Sim ) + + // Note: tho the SceneGraphs are created after the Manager, delete them after, rather + // than before to make sure that all the objects are removed from the graph. + MODULE_INIT_AFTER( GFX ) + MODULE_SHUTDOWN_BEFORE( GFX ) + + MODULE_INIT + { + Sim::init(); + } + + MODULE_SHUTDOWN + { + Sim::shutdown(); + } + +MODULE_END; + + + +namespace Sim +{ + // Don't forget to InstantiateNamed* in simManager.cc - DMM + ImplementNamedSet(ActiveActionMapSet) + ImplementNamedSet(GhostAlwaysSet) + ImplementNamedSet(WayPointSet) + ImplementNamedSet(fxReplicatorSet) + ImplementNamedSet(fxFoliageSet) + ImplementNamedSet(BehaviorSet) + ImplementNamedSet(MaterialSet) + ImplementNamedSet(SFXSourceSet) + ImplementNamedSet(SFXDescriptionSet) + ImplementNamedSet(SFXTrackSet) + ImplementNamedSet(SFXEnvironmentSet) + ImplementNamedSet(SFXStateSet) + ImplementNamedSet(SFXAmbienceSet) + ImplementNamedSet(TerrainMaterialSet) + ImplementNamedSet(DataBlockSet); + ImplementNamedGroup(ActionMapGroup) + ImplementNamedGroup(ClientGroup) + ImplementNamedGroup(GuiGroup) + ImplementNamedGroup(GuiDataGroup) + ImplementNamedGroup(TCPGroup) + ImplementNamedGroup(SFXParameterGroup); + + //groups created on the client + ImplementNamedGroup(ClientConnectionGroup) + ImplementNamedSet(sgMissionLightingFilterSet) +} + +//----------------------------------------------------------------------------- +// Console Functions +//----------------------------------------------------------------------------- + +ConsoleFunctionGroupBegin ( SimFunctions, "Functions relating to Sim."); + +ConsoleFunction(nameToID, S32, 2, 2, "nameToID(object)") +{ + TORQUE_UNUSED(argc); + SimObject *obj = Sim::findObject(argv[1]); + if(obj) + return obj->getId(); + else + return -1; +} + +ConsoleFunction(isObject, bool, 2, 2, "isObject(object)") +{ + TORQUE_UNUSED(argc); + if (!dStrcmp(argv[1], "0") || !dStrcmp(argv[1], "")) + return false; + else + return (Sim::findObject(argv[1]) != NULL); +} + +ConsoleDocFragment _spawnObject1( + "@brief Global function used for spawning any type of object.\n\n" + + "Note: This is separate from SpawnSphere::spawnObject(). This function is not called off any " + "other class and uses different parameters than the SpawnSphere's function. In the source, " + "SpawnSphere::spawnObject() actually calls this function and passes its properties " + "(spawnClass, spawnDatablock, etc).\n\n" + + "@param class Mandatory field specifying the object class, such as Player or TSStatic.\n\n" + "@param datablock Field specifying the object's datablock, optional for objects such as TSStatic, mandatory for game objects like Player.\n\n" + "@param name Optional field specifying a name for this instance of the object.\n\n" + "@param properties Optional set of parameters applied to the spawn object during creation.\n\n" + "@param script Optional command(s) to execute when spawning an object.\n\n" + + "@tsexample\n" + "// Set the parameters for the spawn function\n" + "%objectClass = \"Player\";\n" + "%objectDatablock = \"DefaultPlayerData\";\n" + "%objectName = \"PlayerName\";\n" + "%additionalProperties = \"health = \\\"0\\\";\"; // Note the escape sequence \\ in front of quotes\n" + "%spawnScript = \"echo(\\\"Player Spawned\\\");\" // Note the escape sequence \\ in front of quotes\n" + "// Spawn with the engine's Sim::spawnObject() function\n" + "%player = spawnObject(%objectClass, %objectDatablock, %objectName, %additionalProperties, %spawnScript);\n" + "@endtsexample\n\n" + + "@ingroup Game", + NULL, + "bool spawnObject(class [, dataBlock, name, properties, script]);" +); + +ConsoleFunction(spawnObject, S32, 3, 6, "spawnObject(class [, dataBlock, name, properties, script])" + "@hide") +{ + String spawnClass(argv[1]); + String spawnDataBlock; + String spawnName; + String spawnProperties; + String spawnScript; + + if (argc >= 3) + spawnDataBlock = argv[2]; + if (argc >= 4) + spawnName = argv[3]; + if (argc >= 5) + spawnProperties = argv[4]; + if (argc >= 6) + spawnScript = argv[5]; + + SimObject* spawnObject = Sim::spawnObject(spawnClass, spawnDataBlock, spawnName, spawnProperties, spawnScript); + + if (spawnObject) + return spawnObject->getId(); + else + return -1; +} + +ConsoleFunction(cancel,void,2,2,"cancel(eventId)") +{ + Sim::cancelEvent(dAtoi(argv[1])); +} + +ConsoleFunction(cancelAll,void,2,2,"cancelAll(objectId): cancel pending events on the specified object. Events will be automatically cancelled if object is deleted.") +{ + Sim::cancelPendingEvents(Sim::findObject(argv[1])); +} + +ConsoleFunction(isEventPending, bool, 2, 2, "isEventPending(%scheduleId);") +{ + return Sim::isEventPending(dAtoi(argv[1])); +} + +ConsoleFunction(getEventTimeLeft, S32, 2, 2, "getEventTimeLeft(scheduleId) Get the time left in ms until this event will trigger.") +{ + return Sim::getEventTimeLeft(dAtoi(argv[1])); +} + +ConsoleFunction(getScheduleDuration, S32, 2, 2, "getScheduleDuration(%scheduleId);") +{ + TORQUE_UNUSED(argc); S32 ret = Sim::getScheduleDuration(dAtoi(argv[1])); + return ret; +} + +ConsoleFunction(getTimeSinceStart, S32, 2, 2, "getTimeSinceStart(%scheduleId);") +{ + TORQUE_UNUSED(argc); S32 ret = Sim::getTimeSinceStart(dAtoi(argv[1])); + return ret; +} + +ConsoleFunction(schedule, S32, 4, 0, "schedule(time, refobject|0, command, )") +{ + U32 timeDelta = U32(dAtof(argv[1])); + SimObject *refObject = Sim::findObject(argv[2]); + if(!refObject) + { + if(argv[2][0] != '0') + return 0; + + refObject = Sim::getRootGroup(); + } + SimConsoleEvent *evt = new SimConsoleEvent(argc - 3, argv + 3, false); + + S32 ret = Sim::postEvent(refObject, evt, Sim::getCurrentTime() + timeDelta); + // #ifdef DEBUG + // Con::printf("ref %s schedule(%s) = %d", argv[2], argv[3], ret); + // Con::executef( "backtrace"); + // #endif + return ret; +} + +ConsoleFunction(getUniqueName, const char*, 2, 2, + "( String baseName )\n" + "@brief Returns a unique unused SimObject name based on a given base name.\n\n" + "@baseName Name to conver to a unique string if another instance exists\n" + "@note Currently only used by editors\n" + "@ingroup Editors\n" + "@internal") +{ + String outName = Sim::getUniqueName( argv[1] ); + + if ( outName.isEmpty() ) + return NULL; + + char *buffer = Con::getReturnBuffer( outName.size() ); + dStrcpy( buffer, outName ); + + return buffer; +} + +ConsoleFunction(getUniqueInternalName, const char*, 4, 4, + "( String baseName, SimSet set, bool searchChildren )\n" + "@brief Returns a unique unused internal name within the SimSet/Group based on a given base name.\n\n" + "@note Currently only used by editors\n" + "@ingroup Editors\n" + "@internal") +{ + SimSet *set; + if ( !Sim::findObject( argv[2], set ) ) + { + Con::errorf( "getUniqueInternalName() - invalid parameter for SimSet." ); + return NULL; + } + + String outName = Sim::getUniqueInternalName( argv[1], set, dAtob(argv[3]) ); + + if ( outName.isEmpty() ) + return NULL; + + char *buffer = Con::getReturnBuffer( outName.size() ); + dStrcpy( buffer, outName ); + + return buffer; +} + +ConsoleFunction( isValidObjectName, bool, 2, 2, "( string name )" + "@brief Return true if the given name makes for a valid object name.\n\n" + "@param name Name of object\n" + "@return True if name is allowed, false if denied (usually because it starts with a number, _, or invalid character" + "@ingroup Console") +{ + const char* name = argv[ 1 ]; + return Sim::isValidObjectName( name ); +} + +ConsoleFunctionGroupEnd( SimFunctions ); diff --git a/Engine/source/console/sim.h b/Engine/source/console/sim.h new file mode 100644 index 000000000..c7cfa52fb --- /dev/null +++ b/Engine/source/console/sim.h @@ -0,0 +1,197 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SIM_H_ +#define _SIM_H_ + +#ifndef _TORQUE_TYPES_H_ +#include "platform/types.h" +#endif +#ifndef _TORQUE_STRING_H_ +#include "core/util/str.h" +#endif +#ifndef _MODULE_H_ +#include "core/module.h" +#endif + +// Forward Refs +class SimSet; +class SimGroup; +class SimDataBlockGroup; +class SimObject; +class SimEvent; +class Stream; + +// Sim Types +typedef U32 SimTime; +typedef U32 SimObjectId; + +/// Definition of some basic Sim system constants. +/// +/// These constants define the range of ids assigned to datablocks +/// (DataBlockObjectIdFirst - DataBlockObjectIdLast), and the number +/// of bits used to store datablock IDs. +/// +/// Normal Sim objects are given the range of IDs starting at +/// DynamicObjectIdFirst and going to infinity. Sim objects use +/// a SimObjectId to represent their ID; this is currently a U32. +/// +/// The RootGroupId is assigned to gRootGroup, in which most SimObjects +/// are addded as child members. See simManager.cc for details, particularly +/// Sim::initRoot() and following. +enum SimObjectsConstants +{ + DataBlockObjectIdFirst = 3, + DataBlockObjectIdBitSize = 10, + DataBlockObjectIdLast = DataBlockObjectIdFirst + (1 << DataBlockObjectIdBitSize) - 1, + + MessageObjectIdFirst = DataBlockObjectIdLast + 1, + MessageObjectIdBitSize = 6, + MessageObjectIdLast = MessageObjectIdFirst + (1 << MessageObjectIdBitSize) - 1, + + DynamicObjectIdFirst = MessageObjectIdLast + 1, + InvalidEventId = 0, + RootGroupId = 0xFFFFFFFF, +}; + +//--------------------------------------------------------------------------- + +/// @defgroup simbase_helpermacros Helper Macros +/// +/// These are used for named sets and groups in the manager. +/// @{ +#define DeclareNamedSet(set) extern SimSet *g##set;inline SimSet *get##set() { return g##set; } +#define DeclareNamedGroup(set) extern SimGroup *g##set;inline SimGroup *get##set() { return g##set; } +#define ImplementNamedSet(set) SimSet *g##set; +#define ImplementNamedGroup(set) SimGroup *g##set; +/// @} + +//--------------------------------------------------------------------------- + +namespace Sim +{ + DeclareNamedSet(ActiveActionMapSet) + DeclareNamedSet(GhostAlwaysSet) + DeclareNamedSet(WayPointSet) + DeclareNamedSet(fxReplicatorSet) + DeclareNamedSet(fxFoliageSet) + DeclareNamedSet(BehaviorSet) + DeclareNamedSet(MaterialSet) + DeclareNamedSet(SFXSourceSet); + DeclareNamedSet(SFXDescriptionSet); + DeclareNamedSet(SFXTrackSet); + DeclareNamedSet(SFXEnvironmentSet); + DeclareNamedSet(SFXStateSet); + DeclareNamedSet(SFXAmbienceSet); + DeclareNamedSet(TerrainMaterialSet); + DeclareNamedSet(DataBlockSet); + DeclareNamedGroup(ActionMapGroup) + DeclareNamedGroup(ClientGroup) + DeclareNamedGroup(GuiGroup) + DeclareNamedGroup(GuiDataGroup) + DeclareNamedGroup(TCPGroup) + DeclareNamedGroup(ClientConnectionGroup) + DeclareNamedGroup(SFXParameterGroup); + + DeclareNamedSet(sgMissionLightingFilterSet); + + void init(); + void shutdown(); + + bool isShuttingDown(); + + SimDataBlockGroup *getDataBlockGroup(); + SimGroup* getRootGroup(); + + SimObject* findObject(SimObjectId); + SimObject* findObject(const char* name); + SimObject* findObject(const char* fileName, S32 declarationLine); + template inline bool findObject(SimObjectId iD,T*&t) + { + t = dynamic_cast(findObject(iD)); + return t != NULL; + } + template inline bool findObject(const char *objectName,T*&t) + { + t = dynamic_cast(findObject(objectName)); + return t != NULL; + } + + SimObject *spawnObject(String spawnClass, + String spawnDataBlock = String::EmptyString, + String spawnName = String::EmptyString, + String spawnProperties = String::EmptyString, + String spawnScript = String::EmptyString); + + void advanceToTime(SimTime time); + void advanceTime(SimTime delta); + SimTime getCurrentTime(); + SimTime getTargetTime(); + + /// a target time of 0 on an event means current event + U32 postEvent(SimObject*, SimEvent*, U32 targetTime); + + inline U32 postEvent(SimObjectId iD,SimEvent*evt, U32 targetTime) + { + return postEvent(findObject(iD), evt, targetTime); + } + inline U32 postEvent(const char *objectName,SimEvent*evt, U32 targetTime) + { + return postEvent(findObject(objectName), evt, targetTime); + } + inline U32 postCurrentEvent(SimObject*obj, SimEvent*evt) + { + return postEvent(obj,evt,getCurrentTime()); + } + inline U32 postCurrentEvent(SimObjectId obj,SimEvent*evt) + { + return postEvent(obj,evt,getCurrentTime()); + } + inline U32 postCurrentEvent(const char *obj,SimEvent*evt) + { + return postEvent(obj,evt,getCurrentTime()); + } + + void cancelEvent(U32 eventId); + void cancelPendingEvents(SimObject *obj); + bool isEventPending(U32 eventId); + U32 getEventTimeLeft(U32 eventId); + U32 getTimeSinceStart(U32 eventId); + U32 getScheduleDuration(U32 eventId); + + /// Appends numbers to inName until an unused SimObject name is created + String getUniqueName( const char *inName ); + /// Appends numbers to inName until an internal name not taken in the inSet is found. + String getUniqueInternalName( const char *inName, SimSet *inSet, bool searchChildren ); + + /// Return true if the given name string makes for a valid object name. + /// Empty strings and NULL are also treated as valid names (anonymous objects). + bool isValidObjectName( const char* name ); + + bool saveObject(SimObject *obj, Stream *stream); + SimObject *loadObjectStream(Stream *stream); + + bool saveObject(SimObject *obj, const char *filename); + SimObject *loadObjectStream(const char *filename); +} + +#endif // _SIM_H_ diff --git a/Engine/source/console/simBase.h b/Engine/source/console/simBase.h new file mode 100644 index 000000000..e7182c347 --- /dev/null +++ b/Engine/source/console/simBase.h @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SIMBASE_H_ +#define _SIMBASE_H_ + +// This header is now deprecated. New code should include the relevant headers +// for what they need. simBase.h remains solely to avoid having to change a ton +// of existing code. + +#include "console/simObjectList.h" +#include "console/simEvents.h" +#include "console/simFieldDictionary.h" +#include "console/simObject.h" +#include "console/simSet.h" +#include "console/simDatablock.h" + +#endif diff --git a/Engine/source/console/simDatablock.cpp b/Engine/source/console/simDatablock.cpp new file mode 100644 index 000000000..a10ba1761 --- /dev/null +++ b/Engine/source/console/simDatablock.cpp @@ -0,0 +1,212 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/simDatablock.h" + +#include "console/console.h" +#include "console/consoleInternal.h" +#include "console/engineAPI.h" +#include "T3D/gameBase/gameConnectionEvents.h" +#include "T3D/gameBase/gameConnection.h" + + +IMPLEMENT_CO_DATABLOCK_V1(SimDataBlock); +SimObjectId SimDataBlock::sNextObjectId = DataBlockObjectIdFirst; +S32 SimDataBlock::sNextModifiedKey = 0; + +ConsoleDocClass( SimDataBlock, + "@brief \n" + "@ingroup \n" + + "@section Datablock_Networking Datablocks and Networking\n" + + "@section Datablock_ClientSide Client-Side Datablocks\n" +); + + + +//----------------------------------------------------------------------------- + +SimDataBlock::SimDataBlock() +{ + setModDynamicFields(true); + setModStaticFields(true); +} + +//----------------------------------------------------------------------------- + +bool SimDataBlock::onAdd() +{ + Parent::onAdd(); + + // This initialization is done here, and not in the constructor, + // because some jokers like to construct and destruct objects + // (without adding them to the manager) to check what class + // they are. + modifiedKey = ++sNextModifiedKey; + AssertFatal(sNextObjectId <= DataBlockObjectIdLast, + "Exceeded maximum number of data blocks"); + + // add DataBlock to the DataBlockGroup unless it is client side ONLY DataBlock + if ( !isClientOnly() ) + if (SimGroup* grp = Sim::getDataBlockGroup()) + grp->addObject(this); + + Sim::getDataBlockSet()->addObject( this ); + + return true; +} + +//----------------------------------------------------------------------------- + +void SimDataBlock::assignId() +{ + // We don't want the id assigned by the manager, but it may have + // already been assigned a correct data block id. + if ( isClientOnly() ) + setId(sNextObjectId++); +} + +//----------------------------------------------------------------------------- + +void SimDataBlock::onStaticModified(const char* slotName, const char* newValue) +{ + modifiedKey = sNextModifiedKey++; +} + +//----------------------------------------------------------------------------- + +void SimDataBlock::packData(BitStream*) +{ +} + +//----------------------------------------------------------------------------- + +void SimDataBlock::unpackData(BitStream*) +{ +} + +//----------------------------------------------------------------------------- + +bool SimDataBlock::preload(bool, String&) +{ + return true; +} + +//----------------------------------------------------------------------------- + +void SimDataBlock::write(Stream &stream, U32 tabStop, U32 flags) +{ + // Only output selected objects if they want that. + if((flags & SelectedOnly) && !isSelected()) + return; + + stream.writeTabs(tabStop); + char buffer[1024]; + + // Client side datablocks are created with 'new' while + // regular server datablocks use the 'datablock' keyword. + if ( isClientOnly() ) + dSprintf(buffer, sizeof(buffer), "new %s(%s) {\r\n", getClassName(), getName() ? getName() : ""); + else + dSprintf(buffer, sizeof(buffer), "datablock %s(%s) {\r\n", getClassName(), getName() ? getName() : ""); + + stream.write(dStrlen(buffer), buffer); + writeFields(stream, tabStop + 1); + + stream.writeTabs(tabStop); + stream.write(4, "};\r\n"); +} + +//============================================================================= +// API. +//============================================================================= +// MARK: ---- API ---- + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimDataBlock, reloadOnLocalClient, void, (),, + "Reload the datablock. This can only be used with a local client configuration." ) +{ + // Make sure we're running a local client. + + GameConnection* localClient = GameConnection::getLocalClientConnection(); + if( !localClient ) + return; + + // Do an in-place pack/unpack/preload. + + if( !object->preload( true, NetConnection::getErrorBuffer() ) ) + { + Con::errorf( NetConnection::getErrorBuffer() ); + return; + } + + U8 buffer[ 16384 ]; + BitStream stream( buffer, 16384 ); + + object->packData( &stream ); + stream.setPosition(0); + object->unpackData( &stream ); + + if( !object->preload( false, NetConnection::getErrorBuffer() ) ) + { + Con::errorf( NetConnection::getErrorBuffer() ); + return; + } + + // Trigger a post-apply so that change notifications respond. + object->inspectPostApply(); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( preloadClientDataBlocks, void, (),, + "Preload all datablocks in client mode.\n\n" + "(Server parameter is set to false). This will take some time to complete.") +{ + // we go from last to first because we cut 'n pasted the loop from deleteDataBlocks + SimGroup *grp = Sim::getDataBlockGroup(); + String errorStr; + for(S32 i = grp->size() - 1; i >= 0; i--) + { + AssertFatal(dynamic_cast((*grp)[i]), "Doh! non-datablock in datablock group!"); + SimDataBlock *obj = (SimDataBlock*)(*grp)[i]; + if (!obj->preload(false, errorStr)) + Con::errorf("Failed to preload client datablock, %s: %s", obj->getName(), errorStr.c_str()); + } +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( deleteDataBlocks, void, (),, + "Delete all the datablocks we've downloaded.\n\n" + "This is usually done in preparation of downloading a new set of datablocks, " + "such as occurs on a mission change, but it's also good post-mission cleanup." ) +{ + // delete from last to first: + SimGroup *grp = Sim::getDataBlockGroup(); + grp->deleteAllObjects(); + SimDataBlock::sNextObjectId = DataBlockObjectIdFirst; + SimDataBlock::sNextModifiedKey = 0; +} diff --git a/Engine/source/console/simDatablock.h b/Engine/source/console/simDatablock.h new file mode 100644 index 000000000..3d7acc777 --- /dev/null +++ b/Engine/source/console/simDatablock.h @@ -0,0 +1,190 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SIMDATABLOCK_H_ +#define _SIMDATABLOCK_H_ + +#ifndef _SIMBASE_H_ +#include "console/simBase.h" +#endif + +// Forward Refs +class BitStream; + +/// Root DataBlock class. +/// +/// @section SimDataBlock_intro Introduction +/// +/// Another powerful aspect of Torque's networking is the datablock. Datablocks +/// are used to provide relatively static information about entities; for instance, +/// what model a weapon should use to display itself, or how heavy a player class is. +/// +/// This gives significant gains in network efficiency, because it means that all +/// the datablocks on a server can be transferred over the network at client +/// connect time, instead of being intertwined with the update code for NetObjects. +/// +/// This makes the network code much simpler overall, as one-time initialization +/// code is segregated from the standard object update code, as well as providing +/// several powerful features, which we will discuss momentarily. +/// +/// @section SimDataBlock_preload preload() and File Downloading +/// +/// Because datablocks are sent over the wire, using SimDataBlockEvent, before +/// gameplay starts in earnest, we gain in several areas. First, we don't have to +/// try to keep up with the game state while working with incomplete information. +/// Second, we can provide the user with a nice loading screen, instead of the more +/// traditional "Connecting..." message. Finally, and most usefully, we can request +/// missing files from the server as we become aware of them, since we are under +/// no obligation to render anything for the user. +/// +/// The mechanism for this is fairly basic. After a datablock is unpacked, the +/// preload() method is called. If it returns false and sets an error, then the +/// network code checks to see if a file (or files) failed to be located by the +/// ResManager; if so, then it requests those files from the server. If preload +/// returns true, then the datablock is considered loaded. If preload returns +/// false and sets no error, then the connection is aborted. +/// +/// Once the file(s) is downloaded, the datablock's preload() method is called again. +/// If it fails with the same error, the connection is aborted. If a new error is +/// returned, then the download-retry process is repeated until the preload works. +/// +/// @section SimDataBlock_guide Guide To Datablock Code +/// +/// To make a datablock subclass, you need to extend three functions: +/// - preload() +/// - packData() +/// - unpackData() +/// +/// packData() and unpackData() simply read or write data to a network stream. If you +/// add any fields, you need to add appropriate calls to read or write. Make sure that +/// the order of reads and writes is the same in both functions. Make sure to call +/// the Parent's version of these methods in every subclass. +/// +/// preload() is a bit more complex; it is responsible for taking the raw data read by +/// unpackData() and processing it into a form useful by the datablock's owning object. For +/// instance, the Player class' datablock, PlayerData, gets handles to commonly used +/// nodes in the player model, as well as resolving handles to textures and other +/// resources. Any code which loads files or performs other actions beyond simply +/// reading the data from the packet, such as validation, must reside in preload(). +/// +/// To write your own preload() methods, see any of the existing methods in the codebase; for instance, +/// PlayerData::preload() is an excellent example of error-reporting, data validation, and so forth. +/// +/// @note A useful trick, which is used in several places in the engine, is that of temporarily +/// storing SimObjectIds in the variable which will eventually hold the "real" handle. ShapeImage +/// uses this trick in several pllaces; so do the vehicle classes. See GameBaseData for more on +/// using this trick. +/// +/// @see GameBaseData for some more information on the datablocks used throughout +/// most of the engine. +/// @see http://hosted.tribalwar.com/t2faq/datablocks.shtml for an excellent +/// explanation of the basics of datablocks from script. Note that these comments +/// mostly apply to GameBaseData and its children. +/// @nosubgrouping +class SimDataBlock: public SimObject +{ + typedef SimObject Parent; +public: + + SimDataBlock(); + DECLARE_CONOBJECT(SimDataBlock); + + /// @name Datablock Internals + /// @{ + +protected: + S32 modifiedKey; + +public: + static SimObjectId sNextObjectId; + static S32 sNextModifiedKey; + + /// Assign a new modified key. + /// + /// Datablocks are assigned a modified key which is updated every time + /// a static field of the datablock is changed. These are gotten from + /// a global store. + static S32 getNextModifiedKey() { return sNextModifiedKey; } + + /// Returns true if this is a client side only datablock (in + /// other words a datablock allocated with 'new' instead of + /// the 'datablock' keyword). + bool isClientOnly() const { return getId() < DataBlockObjectIdFirst || getId() > DataBlockObjectIdLast; } + + /// Get the modified key for this particular datablock. + S32 getModifiedKey() const { return modifiedKey; } + + bool onAdd(); + virtual void onStaticModified(const char* slotName, const char*newValue = NULL); + //void setLastError(const char*); + void assignId(); + + /// @} + + /// @name Datablock Interface + /// @{ + + /// + virtual void packData(BitStream* stream); + virtual void unpackData(BitStream* stream); + + /// Called to prepare the datablock for use, after it has been unpacked. + /// + /// @param server Set if we're running on the server (and therefore don't need to load + /// things like textures or sounds). + /// @param errorStr If an error occurs in loading, this is set to a short string describing + /// the error. + /// @returns True if all went well; false if something failed. + /// + /// @see @ref SimDataBlock_preload + virtual bool preload(bool server, String &errorStr); + /// @} + + /// Output the TorqueScript to recreate this object. + /// + /// This calls writeFields internally. + /// @param stream Stream to output to. + /// @param tabStop Indentation level for this object. + /// @param flags If SelectedOnly is passed here, then + /// only objects marked as selected (using setSelected) + /// will output themselves. + virtual void write(Stream &stream, U32 tabStop, U32 flags = 0); + + /// Used by the console system to automatically tell datablock classes apart + /// from non-datablock classes. + static const bool __smIsDatablock = true; +}; + +//--------------------------------------------------------------------------- + +class SimDataBlockGroup : public SimGroup +{ +private: + S32 mLastModifiedKey; + +public: + static S32 QSORT_CALLBACK compareModifiedKey(const void* a,const void* b); + void sort(); + SimDataBlockGroup(); +}; + +#endif // _SIMDATABLOCK_H_ diff --git a/Engine/source/console/simDictionary.cpp b/Engine/source/console/simDictionary.cpp new file mode 100644 index 000000000..5065d0a08 --- /dev/null +++ b/Engine/source/console/simDictionary.cpp @@ -0,0 +1,325 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/simDictionary.h" +#include "console/simBase.h" + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- +extern S32 HashPointer(StringTableEntry e); + +SimNameDictionary::SimNameDictionary() +{ + hashTable = NULL; + mutex = Mutex::createMutex(); +} + +SimNameDictionary::~SimNameDictionary() +{ + delete[] hashTable; + Mutex::destroyMutex(mutex); +} + +void SimNameDictionary::insert(SimObject* obj) +{ + if(!obj->objectName) + return; + + SimObject* checkForDup = find(obj->objectName); + + if (checkForDup) + Con::warnf("Warning! You have a duplicate datablock name of %s. This can cause problems. You should rename one of them.", obj->objectName); + + Mutex::lockMutex(mutex); + + if(!hashTable) + { + hashTable = new SimObject *[DefaultTableSize]; + hashTableSize = DefaultTableSize; + hashEntryCount = 0; + + dMemset( hashTable, 0, sizeof( *hashTable ) * DefaultTableSize ); + } + + S32 idx = HashPointer(obj->objectName) % hashTableSize; + obj->nextNameObject = hashTable[idx]; + hashTable[idx] = obj; + hashEntryCount++; + + // Rehash if necessary. + + if( hashEntryCount > hashTableSize ) + { + // Allocate new table. + + U32 newHashTableSize = hashTableSize * 2 + 1; + SimObject** newHashTable = new SimObject *[ newHashTableSize ]; + dMemset( newHashTable, 0, sizeof( newHashTable[ 0 ] ) * newHashTableSize ); + + // Move entries over. + + for( U32 i = 0; i < hashTableSize; ++ i ) + for( SimObject* object = hashTable[ i ]; object != NULL; ) + { + SimObject* next = object->nextNameObject; + + idx = HashPointer( object->objectName ) % newHashTableSize; + object->nextNameObject = newHashTable[ idx ]; + newHashTable[ idx ] = object; + + object = next; + } + + // Switch tables. + + delete [] hashTable; + hashTable = newHashTable; + hashTableSize = newHashTableSize; + } + + Mutex::unlockMutex(mutex); +} + +SimObject* SimNameDictionary::find(StringTableEntry name) +{ + // NULL is a valid lookup - it will always return NULL + if(!hashTable) + return NULL; + + Mutex::lockMutex(mutex); + + S32 idx = HashPointer(name) % hashTableSize; + SimObject *walk = hashTable[idx]; + while(walk) + { + if(walk->objectName == name) + { + Mutex::unlockMutex(mutex); + return walk; + } + walk = walk->nextNameObject; + } + + Mutex::unlockMutex(mutex); + return NULL; +} + +void SimNameDictionary::remove(SimObject* obj) +{ + if(!obj->objectName) + return; + + Mutex::lockMutex(mutex); + + SimObject **walk = &hashTable[HashPointer(obj->objectName) % hashTableSize]; + while(*walk) + { + if(*walk == obj) + { + *walk = obj->nextNameObject; + obj->nextNameObject = (SimObject*)-1; + hashEntryCount--; + + Mutex::unlockMutex(mutex); + return; + } + walk = &((*walk)->nextNameObject); + } + + Mutex::unlockMutex(mutex); +} + +//---------------------------------------------------------------------------- + +SimManagerNameDictionary::SimManagerNameDictionary() +{ + hashTable = new SimObject *[DefaultTableSize]; + hashTableSize = DefaultTableSize; + hashEntryCount = 0; + + dMemset( hashTable, 0, sizeof( hashTable[ 0 ] ) * hashTableSize ); + + mutex = Mutex::createMutex(); +} + +SimManagerNameDictionary::~SimManagerNameDictionary() +{ + delete[] hashTable; + Mutex::destroyMutex(mutex); +} + +void SimManagerNameDictionary::insert(SimObject* obj) +{ + if(!obj->objectName) + return; + + Mutex::lockMutex(mutex); + + S32 idx = HashPointer(obj->objectName) % hashTableSize; + obj->nextManagerNameObject = hashTable[idx]; + hashTable[idx] = obj; + hashEntryCount++; + + // Rehash if necessary. + + if( hashEntryCount > hashTableSize ) + { + // Allocate new table. + + U32 newHashTableSize = hashTableSize * 2 + 1; + SimObject** newHashTable = new SimObject *[ newHashTableSize ]; + dMemset( newHashTable, 0, sizeof( newHashTable[ 0 ] ) * newHashTableSize ); + + // Move entries over. + + for( U32 i = 0; i < hashTableSize; ++ i ) + for( SimObject* object = hashTable[ i ]; object != NULL; ) + { + SimObject* next = object->nextManagerNameObject; + + idx = HashPointer( object->objectName ) % newHashTableSize; + object->nextManagerNameObject = newHashTable[ idx ]; + newHashTable[ idx ] = object; + + object = next; + } + + // Switch tables. + + delete [] hashTable; + hashTable = newHashTable; + hashTableSize = newHashTableSize; + } + + Mutex::unlockMutex(mutex); +} + +SimObject* SimManagerNameDictionary::find(StringTableEntry name) +{ + // NULL is a valid lookup - it will always return NULL + + Mutex::lockMutex(mutex); + + S32 idx = HashPointer(name) % hashTableSize; + SimObject *walk = hashTable[idx]; + while(walk) + { + if(walk->objectName == name) + { + Mutex::unlockMutex(mutex); + return walk; + } + walk = walk->nextManagerNameObject; + } + + Mutex::unlockMutex(mutex); + return NULL; +} + +void SimManagerNameDictionary::remove(SimObject* obj) +{ + if(!obj->objectName) + return; + + Mutex::lockMutex(mutex); + + SimObject **walk = &hashTable[HashPointer(obj->objectName) % hashTableSize]; + while(*walk) + { + if(*walk == obj) + { + *walk = obj->nextManagerNameObject; + obj->nextManagerNameObject = (SimObject*)-1; + hashEntryCount--; + + Mutex::unlockMutex(mutex); + return; + } + walk = &((*walk)->nextManagerNameObject); + } + + Mutex::unlockMutex(mutex); +} + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- + +SimIdDictionary::SimIdDictionary() +{ + dMemset( table, 0, sizeof( table[ 0 ] ) * DefaultTableSize ); + mutex = Mutex::createMutex(); +} + +SimIdDictionary::~SimIdDictionary() +{ + Mutex::destroyMutex(mutex); +} + +void SimIdDictionary::insert(SimObject* obj) +{ + Mutex::lockMutex(mutex); + + S32 idx = obj->getId() & TableBitMask; + obj->nextIdObject = table[idx]; + AssertFatal( obj->nextIdObject != obj, "SimIdDictionary::insert - Creating Infinite Loop linking to self!" ); + table[idx] = obj; + + Mutex::unlockMutex(mutex); +} + +SimObject* SimIdDictionary::find(S32 id) +{ + Mutex::lockMutex(mutex); + + S32 idx = id & TableBitMask; + SimObject *walk = table[idx]; + while(walk) + { + if(walk->getId() == U32(id)) + { + Mutex::unlockMutex(mutex); + return walk; + } + walk = walk->nextIdObject; + } + + Mutex::unlockMutex(mutex); + + return NULL; +} + +void SimIdDictionary::remove(SimObject* obj) +{ + Mutex::lockMutex(mutex); + + SimObject **walk = &table[obj->getId() & TableBitMask]; + while(*walk && *walk != obj) + walk = &((*walk)->nextIdObject); + if(*walk) + *walk = obj->nextIdObject; + + Mutex::unlockMutex(mutex); +} + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- + diff --git a/Engine/source/console/simDictionary.h b/Engine/source/console/simDictionary.h new file mode 100644 index 000000000..75fc075e2 --- /dev/null +++ b/Engine/source/console/simDictionary.h @@ -0,0 +1,112 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SIMDICTIONARY_H_ +#define _SIMDICTIONARY_H_ +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#ifndef _STRINGTABLE_H_ +#include "core/stringTable.h" +#endif + +#ifndef _PLATFORMMUTEX_H_ +#include "platform/threads/mutex.h" +#endif + +class SimObject; + +//---------------------------------------------------------------------------- +/// Map of names to SimObjects +/// +/// Provides fast lookup for name->object and +/// for fast removal of an object given object* +class SimNameDictionary +{ + enum + { + DefaultTableSize = 29 + }; + + SimObject **hashTable; // hash the pointers of the names... + S32 hashTableSize; + S32 hashEntryCount; + + void *mutex; + +public: + void insert(SimObject* obj); + void remove(SimObject* obj); + SimObject* find(StringTableEntry name); + + SimNameDictionary(); + ~SimNameDictionary(); +}; + +class SimManagerNameDictionary +{ + enum + { + DefaultTableSize = 29 + }; + + SimObject **hashTable; // hash the pointers of the names... + S32 hashTableSize; + S32 hashEntryCount; + + void *mutex; + +public: + void insert(SimObject* obj); + void remove(SimObject* obj); + SimObject* find(StringTableEntry name); + + SimManagerNameDictionary(); + ~SimManagerNameDictionary(); +}; + +//---------------------------------------------------------------------------- +/// Map of ID's to SimObjects. +/// +/// Provides fast lookup for ID->object and +/// for fast removal of an object given object* +class SimIdDictionary +{ + enum + { + DefaultTableSize = 4096, + TableBitMask = 4095 + }; + SimObject *table[DefaultTableSize]; + + void *mutex; + +public: + void insert(SimObject* obj); + void remove(SimObject* obj); + SimObject* find(S32 id); + + SimIdDictionary(); + ~SimIdDictionary(); +}; + +#endif //_SIMDICTIONARY_H_ diff --git a/Engine/source/console/simEvents.cpp b/Engine/source/console/simEvents.cpp new file mode 100644 index 000000000..bf73ea7df --- /dev/null +++ b/Engine/source/console/simEvents.cpp @@ -0,0 +1,140 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/console.h" +#include "console/consoleInternal.h" +#include "platform/threads/semaphore.h" +#include "console/simEvents.h" + +// Stupid globals not declared in a header +extern ExprEvalState gEvalState; + +SimConsoleEvent::SimConsoleEvent(S32 argc, const char **argv, bool onObject) +{ + mOnObject = onObject; + mArgc = argc; + U32 totalSize = 0; + S32 i; + for(i = 0; i < argc; i++) + totalSize += dStrlen(argv[i]) + 1; + totalSize += sizeof(char *) * argc; + + mArgv = (char **) dMalloc(totalSize); + char *argBase = (char *) &mArgv[argc]; + + for(i = 0; i < argc; i++) + { + mArgv[i] = argBase; + dStrcpy(mArgv[i], argv[i]); + argBase += dStrlen(argv[i]) + 1; + } +} + +SimConsoleEvent::~SimConsoleEvent() +{ + dFree(mArgv); +} + +void SimConsoleEvent::process(SimObject* object) +{ + // #ifdef DEBUG + // Con::printf("Executing schedule: %d", sequenceCount); + // #endif + if(mOnObject) + Con::execute(object, mArgc, const_cast( mArgv )); + else + { + // Grab the function name. If '::' doesn't exist, then the schedule is + // on a global function. + char* func = dStrstr( mArgv[0], (char*)"::" ); + if( func ) + { + // Set the first colon to NULL, so we can reference the namespace. + // This is okay because events are deleted immediately after + // processing. Maybe a bad idea anyway? + func[0] = '\0'; + + // Move the pointer forward to the function name. + func += 2; + + // Lookup the namespace and function entry. + Namespace* ns = Namespace::find( StringTable->insert( mArgv[0] ) ); + if( ns ) + { + Namespace::Entry* nse = ns->lookup( StringTable->insert( func ) ); + if( nse ) + // Execute. + nse->execute( mArgc, (const char**)mArgv, &gEvalState ); + } + } + + else + Con::execute(mArgc, const_cast( mArgv )); + } +} + +//----------------------------------------------------------------------------- + +SimConsoleThreadExecCallback::SimConsoleThreadExecCallback() : retVal(NULL) +{ + sem = new Semaphore(0); +} + +SimConsoleThreadExecCallback::~SimConsoleThreadExecCallback() +{ + delete sem; +} + +void SimConsoleThreadExecCallback::handleCallback(const char *ret) +{ + retVal = ret; + sem->release(); +} + +const char *SimConsoleThreadExecCallback::waitForResult() +{ + if(sem->acquire(true)) + { + return retVal; + } + + return NULL; +} + +//----------------------------------------------------------------------------- + +SimConsoleThreadExecEvent::SimConsoleThreadExecEvent(S32 argc, const char **argv, bool onObject, SimConsoleThreadExecCallback *callback) : + SimConsoleEvent(argc, argv, onObject), cb(callback) +{ +} + +void SimConsoleThreadExecEvent::process(SimObject* object) +{ + const char *retVal; + if(mOnObject) + retVal = Con::execute(object, mArgc, const_cast( mArgv )); + else + retVal = Con::execute(mArgc, const_cast( mArgv )); + + if(cb) + cb->handleCallback(retVal); +} diff --git a/Engine/source/console/simEvents.h b/Engine/source/console/simEvents.h new file mode 100644 index 000000000..f325591db --- /dev/null +++ b/Engine/source/console/simEvents.h @@ -0,0 +1,155 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SIMEVENTS_H_ +#define _SIMEVENTS_H_ + +#ifndef _SIM_H_ +#include "console/sim.h" +#endif + +#ifndef _UTIL_DELEGATE_H_ +#include "core/util/delegate.h" +#endif + +// Forward Refs +class SimObject; +class Semaphore; + +/// Represents a queued event in the sim. +/// +/// Sim provides an event queue for your convenience, which +/// can be used to schedule events. A few things which use +/// this event queue: +/// +/// - The scene lighting system. In order to keep the game +/// responsive while scene lighting occurs, the lighting +/// process is divided into little chunks. In implementation +/// terms, there is a subclass of SimEvent called +/// SceneLightingProcessEvent. The process method of this +/// subclass calls into the lighting code, telling it to +/// perform the next chunk of lighting calculations. +/// - The schedule() console function uses a subclass of +/// SimEvent called SimConsoleEvent to keep track of +/// scheduled events. +class SimEvent +{ +public: + SimEvent *nextEvent; ///< Linked list details - pointer to next item in the list. + SimTime startTime; ///< When the event was posted. + SimTime time; ///< When the event is scheduled to occur. + U32 sequenceCount; ///< Unique ID. These are assigned sequentially based on order + /// of addition to the list. + SimObject *destObject; ///< Object on which this event will be applied. + + SimEvent() { destObject = NULL; } + virtual ~SimEvent() {} ///< Destructor + /// + /// A dummy virtual destructor is required + /// so that subclasses can be deleted properly + + /// Function called when event occurs. + /// + /// This is where the meat of your event's implementation goes. + /// + /// See any of the subclasses for ideas of what goes in here. + /// + /// The event is deleted immediately after processing. If the + /// object referenced in destObject is deleted, then the event + /// is not called. The even will be executed unconditionally if + /// the object referenced is NULL. + /// + /// @param object Object stored in destObject. + virtual void process(SimObject *object)=0; +}; + +/// Implementation of schedule() function. +/// +/// This allows you to set a console function to be +/// called at some point in the future. +class SimConsoleEvent : public SimEvent +{ +protected: + S32 mArgc; + char **mArgv; + bool mOnObject; +public: + + /// Constructor + /// + /// Pass the arguments of a function call, optionally on an object. + /// + /// The object for the call to be executed on is specified by setting + /// onObject and storing a reference to the object in destObject. If + /// onObject is false, you don't need to store anything into destObject. + /// + /// The parameters here are passed unmodified to Con::execute() at the + /// time of the event. + /// + /// @see Con::execute(S32 argc, const char *argv[]) + /// @see Con::execute(SimObject *object, S32 argc, const char *argv[]) + SimConsoleEvent(S32 argc, const char **argv, bool onObject); + + ~SimConsoleEvent(); + virtual void process(SimObject *object); +}; + +/// Used by Con::threadSafeExecute() +struct SimConsoleThreadExecCallback +{ + Semaphore *sem; + const char *retVal; + + SimConsoleThreadExecCallback(); + ~SimConsoleThreadExecCallback(); + + void handleCallback(const char *ret); + const char *waitForResult(); +}; + +class SimConsoleThreadExecEvent : public SimConsoleEvent +{ + SimConsoleThreadExecCallback *cb; + +public: + SimConsoleThreadExecEvent(S32 argc, const char **argv, bool onObject, SimConsoleThreadExecCallback *callback); + + virtual void process(SimObject *object); +}; + +/// General purpose SimEvent which calls a Delegate callback. +class SimDelegateEvent : public SimEvent +{ +public: + + U32 *mEventId; + Delegate mCallback; + + void process( SimObject* ) + { + // Clear the event id and call the delegate. + *mEventId = InvalidEventId; + mCallback(); + } +}; + +#endif // _SIMEVENTS_H_ diff --git a/Engine/source/console/simFieldDictionary.cpp b/Engine/source/console/simFieldDictionary.cpp new file mode 100644 index 000000000..9615c59d0 --- /dev/null +++ b/Engine/source/console/simFieldDictionary.cpp @@ -0,0 +1,364 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + + +#include "platform/platform.h" +#include "console/simFieldDictionary.h" + +#include "console/console.h" +#include "console/consoleInternal.h" +#include "core/frameAllocator.h" + +SimFieldDictionary::Entry *SimFieldDictionary::smFreeList = NULL; + +static Chunker fieldChunker; + +U32 SimFieldDictionary::getHashValue( StringTableEntry slotName ) +{ + return HashPointer( slotName ) % HashTableSize; +} + +U32 SimFieldDictionary::getHashValue( const String& fieldName ) +{ + return getHashValue( StringTable->insert( fieldName ) ); +} + +SimFieldDictionary::Entry *SimFieldDictionary::addEntry( U32 bucket, StringTableEntry slotName, ConsoleBaseType* type, char* value ) +{ + Entry* ret; + if(smFreeList) + { + ret = smFreeList; + smFreeList = ret->next; + } + else + ret = fieldChunker.alloc(); + + ret->next = mHashTable[ bucket ]; + ret->slotName = slotName; + ret->type = type; + ret->value = value; + + mHashTable[ bucket ] = ret; + mNumFields ++; + mVersion ++; + + return ret; +} + +void SimFieldDictionary::freeEntry(SimFieldDictionary::Entry *ent) +{ + ent->next = smFreeList; + smFreeList = ent; + + mNumFields --; +} + +SimFieldDictionary::SimFieldDictionary() +: mNumFields( 0 ), + mVersion( 0 ) +{ + dMemset( mHashTable, 0, sizeof( mHashTable ) ); +} + +SimFieldDictionary::~SimFieldDictionary() +{ + for(U32 i = 0; i < HashTableSize; i++) + { + for(Entry *walk = mHashTable[i]; walk;) + { + Entry *temp = walk; + walk = temp->next; + + if( temp->value ) + dFree(temp->value); + freeEntry(temp); + } + } + + AssertFatal( mNumFields == 0, "Incorrect count on field dictionary" ); +} + +void SimFieldDictionary::setFieldType(StringTableEntry slotName, const char *typeString) +{ + ConsoleBaseType *cbt = ConsoleBaseType::getTypeByName( typeString ); + setFieldType(slotName, cbt); +} + +void SimFieldDictionary::setFieldType(StringTableEntry slotName, const U32 typeId) +{ + ConsoleBaseType *cbt = ConsoleBaseType::getType(typeId); + setFieldType(slotName, cbt); +} + +void SimFieldDictionary::setFieldType(StringTableEntry slotName, ConsoleBaseType *type) +{ + // If the field exists on the object, set the type + U32 bucket = getHashValue( slotName ); + + for( Entry *walk = mHashTable[bucket]; walk; walk = walk->next ) + { + if( walk->slotName == slotName ) + { + // Found and type assigned, let's bail + walk->type = type; + return; + } + } + + // Otherwise create the field, and set the type. Assign a null value. + addEntry( bucket, slotName, type ); +} + +U32 SimFieldDictionary::getFieldType(StringTableEntry slotName) const +{ + U32 bucket = getHashValue( slotName ); + + for( Entry *walk = mHashTable[bucket]; walk; walk = walk->next ) + if( walk->slotName == slotName ) + return walk->type ? walk->type->getTypeID() : TypeString; + + return TypeString; +} + +SimFieldDictionary::Entry *SimFieldDictionary::findDynamicField(const String &fieldName) const +{ + U32 bucket = getHashValue( fieldName ); + + for( Entry *walk = mHashTable[bucket]; walk; walk = walk->next ) + { + if( fieldName.equal(walk->slotName, String::NoCase) ) + return walk; + } + + return NULL; +} + +SimFieldDictionary::Entry *SimFieldDictionary::findDynamicField( StringTableEntry fieldName) const +{ + U32 bucket = getHashValue( fieldName ); + + for( Entry *walk = mHashTable[bucket]; walk; walk = walk->next ) + { + if( walk->slotName == fieldName ) + { + return walk; + } + } + + return NULL; +} + + +void SimFieldDictionary::setFieldValue(StringTableEntry slotName, const char *value) +{ + U32 bucket = getHashValue(slotName); + Entry **walk = &mHashTable[bucket]; + while(*walk && (*walk)->slotName != slotName) + walk = &((*walk)->next); + + Entry *field = *walk; + if( !value || !*value ) + { + if(field) + { + mVersion++; + + if( field->value ) + dFree(field->value); + + *walk = field->next; + freeEntry(field); + } + } + else + { + if(field) + { + if( field->value ) + dFree(field->value); + + field->value = dStrdup(value); + } + else + addEntry( bucket, slotName, 0, dStrdup( value ) ); + } +} + +const char *SimFieldDictionary::getFieldValue(StringTableEntry slotName) +{ + U32 bucket = getHashValue(slotName); + + for(Entry *walk = mHashTable[bucket];walk;walk = walk->next) + if(walk->slotName == slotName) + return walk->value; + + return NULL; +} + +void SimFieldDictionary::assignFrom(SimFieldDictionary *dict) +{ + mVersion++; + + for(U32 i = 0; i < HashTableSize; i++) + { + for(Entry *walk = dict->mHashTable[i];walk; walk = walk->next) + { + setFieldValue(walk->slotName, walk->value); + setFieldType(walk->slotName, walk->type); + } + } +} + +static S32 QSORT_CALLBACK compareEntries(const void* a,const void* b) +{ + SimFieldDictionary::Entry *fa = *((SimFieldDictionary::Entry **)a); + SimFieldDictionary::Entry *fb = *((SimFieldDictionary::Entry **)b); + return dStricmp(fa->slotName, fb->slotName); +} + +void SimFieldDictionary::writeFields(SimObject *obj, Stream &stream, U32 tabStop) +{ + const AbstractClassRep::FieldList &list = obj->getFieldList(); + Vector flist(__FILE__, __LINE__); + + for(U32 i = 0; i < HashTableSize; i++) + { + for(Entry *walk = mHashTable[i];walk; walk = walk->next) + { + // make sure we haven't written this out yet: + U32 i; + for(i = 0; i < list.size(); i++) + if(list[i].pFieldname == walk->slotName) + break; + + if(i != list.size()) + continue; + + + if (!obj->writeField(walk->slotName, walk->value)) + continue; + + flist.push_back(walk); + } + } + + // Sort Entries to prevent version control conflicts + dQsort(flist.address(),flist.size(),sizeof(Entry *),compareEntries); + + // Save them out + for(Vector::iterator itr = flist.begin(); itr != flist.end(); itr++) + { + U32 nBufferSize = (dStrlen( (*itr)->value ) * 2) + dStrlen( (*itr)->slotName ) + 16; + FrameTemp expandedBuffer( nBufferSize ); + + stream.writeTabs(tabStop+1); + + const char *typeName = (*itr)->type && (*itr)->type->getTypeID() != TypeString ? (*itr)->type->getTypeName() : ""; + dSprintf(expandedBuffer, nBufferSize, "%s%s%s = \"", typeName, *typeName ? " " : "", (*itr)->slotName); + if ( (*itr)->value ) + expandEscape((char*)expandedBuffer + dStrlen(expandedBuffer), (*itr)->value); + dStrcat(expandedBuffer, "\";\r\n"); + + stream.write(dStrlen(expandedBuffer),expandedBuffer); + } + +} +void SimFieldDictionary::printFields(SimObject *obj) +{ + const AbstractClassRep::FieldList &list = obj->getFieldList(); + char expandedBuffer[4096]; + Vector flist(__FILE__, __LINE__); + + for(U32 i = 0; i < HashTableSize; i++) + { + for(Entry *walk = mHashTable[i];walk; walk = walk->next) + { + // make sure we haven't written this out yet: + U32 i; + for(i = 0; i < list.size(); i++) + if(list[i].pFieldname == walk->slotName) + break; + + if(i != list.size()) + continue; + + flist.push_back(walk); + } + } + dQsort(flist.address(),flist.size(),sizeof(Entry *),compareEntries); + + for(Vector::iterator itr = flist.begin(); itr != flist.end(); itr++) + { + const char* type = "string"; + if( ( *itr )->type ) + type = ( *itr )->type->getTypeClassName(); + + dSprintf( expandedBuffer, sizeof( expandedBuffer ), " %s %s = \"", type, ( *itr )->slotName ); + if ( (*itr)->value ) + expandEscape(expandedBuffer + dStrlen(expandedBuffer), (*itr)->value); + Con::printf("%s\"", expandedBuffer); + } +} + +SimFieldDictionary::Entry *SimFieldDictionary::operator[](U32 index) +{ + AssertFatal ( index < mNumFields, "out of range" ); + + if ( index > mNumFields ) + return NULL; + + SimFieldDictionaryIterator itr(this); + + for (S32 i = 0; i < index && *itr; i++) + ++itr; + + return (*itr); +} + +//------------------------------------------------------------------------------ +SimFieldDictionaryIterator::SimFieldDictionaryIterator(SimFieldDictionary * dictionary) +{ + mDictionary = dictionary; + mHashIndex = -1; + mEntry = 0; + operator++(); +} + +SimFieldDictionary::Entry* SimFieldDictionaryIterator::operator++() +{ + if(!mDictionary) + return(mEntry); + + if(mEntry) + mEntry = mEntry->next; + + while(!mEntry && (mHashIndex < (SimFieldDictionary::HashTableSize-1))) + mEntry = mDictionary->mHashTable[++mHashIndex]; + + return(mEntry); +} + +SimFieldDictionary::Entry* SimFieldDictionaryIterator::operator*() +{ + return(mEntry); +} \ No newline at end of file diff --git a/Engine/source/console/simFieldDictionary.h b/Engine/source/console/simFieldDictionary.h new file mode 100644 index 000000000..7bd8cbbd7 --- /dev/null +++ b/Engine/source/console/simFieldDictionary.h @@ -0,0 +1,108 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SIMFIELDDICTIONARY_H_ +#define _SIMFIELDDICTIONARY_H_ + +// Forward Refs +class ConsoleBaseType; +class SimObject; + +#include "core/stringTable.h" +#include "core/stream/stream.h" + +#ifndef _TORQUE_STRING_H_ +#include "core/util/str.h" +#endif + +/// Dictionary to keep track of dynamic fields on SimObject. +class SimFieldDictionary +{ + friend class SimFieldDictionaryIterator; + +public: + struct Entry + { + Entry() : type( NULL ) {}; + + StringTableEntry slotName; + char *value; + Entry *next; + ConsoleBaseType *type; + }; +private: + enum + { + HashTableSize = 19 + }; + Entry *mHashTable[HashTableSize]; + + static Entry *smFreeList; + + void freeEntry(Entry *entry); + Entry* addEntry( U32 bucket, StringTableEntry slotName, ConsoleBaseType* type, char* value = 0 ); + + static U32 getHashValue( StringTableEntry slotName ); + static U32 getHashValue( const String& fieldName ); + + U32 mNumFields; + + /// In order to efficiently detect when a dynamic field has been + /// added or deleted, we increment this every time we add or + /// remove a field. + U32 mVersion; + +public: + const U32 getVersion() const { return mVersion; } + + SimFieldDictionary(); + ~SimFieldDictionary(); + void setFieldType(StringTableEntry slotName, const char *typeString); + void setFieldType(StringTableEntry slotName, const U32 typeId); + void setFieldType(StringTableEntry slotName, ConsoleBaseType *type); + void setFieldValue(StringTableEntry slotName, const char *value); + const char *getFieldValue(StringTableEntry slotName); + U32 getFieldType(StringTableEntry slotName) const; + Entry *findDynamicField(const String &fieldName) const; + Entry *findDynamicField( StringTableEntry fieldName) const; + void writeFields(SimObject *obj, Stream &strem, U32 tabStop); + void printFields(SimObject *obj); + void assignFrom(SimFieldDictionary *dict); + U32 getNumFields() const { return mNumFields; } + + Entry *operator[](U32 index); +}; + +class SimFieldDictionaryIterator +{ + SimFieldDictionary * mDictionary; + S32 mHashIndex; + SimFieldDictionary::Entry * mEntry; + +public: + SimFieldDictionaryIterator(SimFieldDictionary*); + SimFieldDictionary::Entry* operator++(); + SimFieldDictionary::Entry* operator*(); +}; + + +#endif // _SIMFIELDDICTIONARY_H_ diff --git a/Engine/source/console/simManager.cpp b/Engine/source/console/simManager.cpp new file mode 100644 index 000000000..0f00d1e33 --- /dev/null +++ b/Engine/source/console/simManager.cpp @@ -0,0 +1,610 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "platform/threads/mutex.h" +#include "console/simBase.h" +#include "console/simPersistID.h" +#include "core/stringTable.h" +#include "console/console.h" +#include "core/stream/fileStream.h" +#include "core/fileObject.h" +#include "console/consoleInternal.h" +#include "console/engineAPI.h" +#include "core/idGenerator.h" +#include "core/util/safeDelete.h" +#include "platform/platformIntrinsics.h" +#include "platform/profiler.h" +#include "math/mMathFn.h" + +extern ExprEvalState gEvalState; + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- + +// We comment out the implementation of the Con namespace when doxygenizing because +// otherwise Doxygen decides to ignore our docs in console.h +#ifndef DOXYGENIZING + +namespace Sim +{ + + +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- +// event queue variables: + +SimTime gCurrentTime; +SimTime gTargetTime; + +void *gEventQueueMutex; +SimEvent *gEventQueue; +U32 gEventSequence; + +//--------------------------------------------------------------------------- +// event queue init/shutdown + +static void initEventQueue() +{ + gCurrentTime = 0; + gTargetTime = 0; + gEventSequence = 1; + gEventQueue = NULL; + gEventQueueMutex = Mutex::createMutex(); +} + +static void shutdownEventQueue() +{ + // Delete all pending events + Mutex::lockMutex(gEventQueueMutex); + SimEvent *walk = gEventQueue; + while(walk) + { + SimEvent *temp = walk->nextEvent; + delete walk; + walk = temp; + } + Mutex::unlockMutex(gEventQueueMutex); + Mutex::destroyMutex(gEventQueueMutex); +} + +//--------------------------------------------------------------------------- +// event post + +U32 postEvent(SimObject *destObject, SimEvent* event,U32 time) +{ + AssertFatal(time == -1 || time >= getCurrentTime(), + "Sim::postEvent() - Event time must be greater than or equal to the current time." ); + AssertFatal(destObject, "Sim::postEvent() - Destination object for event doesn't exist."); + + Mutex::lockMutex(gEventQueueMutex); + + if( time == -1 ) + time = gCurrentTime; + + event->time = time; + event->startTime = gCurrentTime; + event->destObject = destObject; + + if(!destObject) + { + delete event; + + Mutex::unlockMutex(gEventQueueMutex); + + return InvalidEventId; + } + event->sequenceCount = gEventSequence++; + SimEvent **walk = &gEventQueue; + SimEvent *current; + + while((current = *walk) != NULL && (current->time < event->time)) + walk = &(current->nextEvent); + + // [tom, 6/24/2005] This ensures that SimEvents are dispatched in the same order that they are posted. + // This is needed to ensure Con::threadSafeExecute() executes script code in the correct order. + while((current = *walk) != NULL && (current->time == event->time)) + walk = &(current->nextEvent); + + event->nextEvent = current; + *walk = event; + + U32 seqCount = event->sequenceCount; + + Mutex::unlockMutex(gEventQueueMutex); + + return seqCount; +} + +//--------------------------------------------------------------------------- +// event cancellation + +void cancelEvent(U32 eventSequence) +{ + Mutex::lockMutex(gEventQueueMutex); + + SimEvent **walk = &gEventQueue; + SimEvent *current; + + while((current = *walk) != NULL) + { + if(current->sequenceCount == eventSequence) + { + *walk = current->nextEvent; + delete current; + Mutex::unlockMutex(gEventQueueMutex); + return; + } + else + walk = &(current->nextEvent); + } + + Mutex::unlockMutex(gEventQueueMutex); +} + +void cancelPendingEvents(SimObject *obj) +{ + Mutex::lockMutex(gEventQueueMutex); + + SimEvent **walk = &gEventQueue; + SimEvent *current; + + while((current = *walk) != NULL) + { + if(current->destObject == obj) + { + *walk = current->nextEvent; + delete current; + } + else + walk = &(current->nextEvent); + } + Mutex::unlockMutex(gEventQueueMutex); +} + +//--------------------------------------------------------------------------- +// event pending test + +bool isEventPending(U32 eventSequence) +{ + Mutex::lockMutex(gEventQueueMutex); + + for(SimEvent *walk = gEventQueue; walk; walk = walk->nextEvent) + if(walk->sequenceCount == eventSequence) + { + Mutex::unlockMutex(gEventQueueMutex); + return true; + } + Mutex::unlockMutex(gEventQueueMutex); + return false; +} + +U32 getEventTimeLeft(U32 eventSequence) +{ + Mutex::lockMutex(gEventQueueMutex); + + for(SimEvent *walk = gEventQueue; walk; walk = walk->nextEvent) + if(walk->sequenceCount == eventSequence) + { + SimTime t = walk->time - getCurrentTime(); + Mutex::unlockMutex(gEventQueueMutex); + return t; + } + + Mutex::unlockMutex(gEventQueueMutex); + + return 0; +} + +U32 getScheduleDuration(U32 eventSequence) +{ + for(SimEvent *walk = gEventQueue; walk; walk = walk->nextEvent) + if(walk->sequenceCount == eventSequence) + return (walk->time-walk->startTime); + return 0; +} + +U32 getTimeSinceStart(U32 eventSequence) +{ + for(SimEvent *walk = gEventQueue; walk; walk = walk->nextEvent) + if(walk->sequenceCount == eventSequence) + return (getCurrentTime()-walk->startTime); + return 0; +} + +//--------------------------------------------------------------------------- +// event timing + +void advanceToTime(SimTime targetTime) +{ + AssertFatal(targetTime >= getCurrentTime(), + "Sim::advanceToTime() - Target time is less than the current time." ); + + Mutex::lockMutex(gEventQueueMutex); + + gTargetTime = targetTime; + while(gEventQueue && gEventQueue->time <= targetTime) + { + SimEvent *event = gEventQueue; + gEventQueue = gEventQueue->nextEvent; + AssertFatal(event->time >= gCurrentTime, + "Sim::advanceToTime() - Event time is less than current time."); + gCurrentTime = event->time; + SimObject *obj = event->destObject; + + if(!obj->isDeleted()) + event->process(obj); + delete event; + } + gCurrentTime = targetTime; + + Mutex::unlockMutex(gEventQueueMutex); +} + +void advanceTime(SimTime delta) +{ + advanceToTime(getCurrentTime() + delta); +} + +U32 getCurrentTime() +{ + return dAtomicRead( gCurrentTime); +} + +U32 getTargetTime() +{ + return dAtomicRead( gTargetTime ); +} + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- + +SimGroup *gRootGroup = NULL; +SimManagerNameDictionary *gNameDictionary; +SimIdDictionary *gIdDictionary; +U32 gNextObjectId; + +static void initRoot() +{ + gIdDictionary = new SimIdDictionary; + gNameDictionary = new SimManagerNameDictionary; + + gRootGroup = new SimGroup(); + gRootGroup->incRefCount(); + + gRootGroup->setId(RootGroupId); + gRootGroup->assignName("RootGroup"); + gRootGroup->registerObject(); + + gNextObjectId = DynamicObjectIdFirst; +} + +static void shutdownRoot() +{ + gRootGroup->decRefCount(); + if( engineAPI::gUseConsoleInterop ) + gRootGroup->deleteObject(); + gRootGroup = NULL; + + SAFE_DELETE(gNameDictionary); + SAFE_DELETE(gIdDictionary); +} + +//--------------------------------------------------------------------------- + +SimObject* findObject(const char* fileName, S32 declarationLine) +{ + PROFILE_SCOPE(SimFindObjectByLine); + + if (!fileName) + return NULL; + + if (declarationLine < 0) + return NULL; + + if (!gRootGroup) + return NULL; + + return gRootGroup->findObjectByLineNumber(fileName, declarationLine, true); +} + +SimObject* findObject(const char* name) +{ + PROFILE_SCOPE(SimFindObject); + + // Play nice with bad code - JDD + if( !name ) + return NULL; + + SimObject *obj; + char c = *name; + + if (c == '%') + { + if (gEvalState.getStackDepth()) + { + Dictionary::Entry* ent = gEvalState.getCurrentFrame().lookup(StringTable->insert(name)); + + if (ent) + return Sim::findObject(ent->getIntValue()); + } + } + if(c == '/') + return gRootGroup->findObject(name + 1 ); + if(c >= '0' && c <= '9') + { + // it's an id group + const char* temp = name + 1; + for(;;) + { + c = *temp++; + if(!c) + return findObject(dAtoi(name)); + else if(c == '/') + { + obj = findObject(dAtoi(name)); + if(!obj) + return NULL; + return obj->findObject(temp); + } + } + } + S32 len; + + for(len = 0; name[len] != 0 && name[len] != '/'; len++) + ; + StringTableEntry stName = StringTable->lookupn(name, len); + if(!stName) + return NULL; + obj = gNameDictionary->find(stName); + if(!name[len]) + return obj; + if(!obj) + return NULL; + return obj->findObject(name + len + 1); +} + +SimObject* findObject(SimObjectId id) +{ + return gIdDictionary->find(id); +} + +SimObject *spawnObject(String spawnClass, String spawnDataBlock, String spawnName, + String spawnProperties, String spawnScript) +{ + if (spawnClass.isEmpty()) + { + Con::errorf("Unable to spawn an object without a spawnClass"); + return NULL; + } + + String spawnString; + + spawnString += "$SpawnObject = new " + spawnClass + "(" + spawnName + ") { "; + + if (spawnDataBlock.isNotEmpty() && !spawnDataBlock.equal( "None", String::NoCase ) ) + spawnString += "datablock = " + spawnDataBlock + "; "; + + if (spawnProperties.isNotEmpty()) + spawnString += spawnProperties + " "; + + spawnString += "};"; + + // Evaluate our spawn string + Con::evaluate(spawnString.c_str()); + + // Get our spawnObject id + const char* spawnObjectId = Con::getVariable("$SpawnObject"); + + // Get the actual spawnObject + SimObject* spawnObject = findObject(spawnObjectId); + + // If we have a spawn script go ahead and execute it last + if (spawnScript.isNotEmpty()) + Con::evaluate(spawnScript.c_str(), true); + + return spawnObject; +} + +SimGroup *getRootGroup() +{ + return gRootGroup; +} + +String getUniqueName( const char *inName ) +{ + String outName( inName ); + + if ( outName.isEmpty() ) + return String::EmptyString; + + SimObject *dummy; + + if ( !Sim::findObject( outName, dummy ) ) + return outName; + + S32 suffixNumb = -1; + String nameStr( String::GetTrailingNumber( outName, suffixNumb ) ); + suffixNumb = mAbs( suffixNumb ) + 1; + + #define MAX_TRIES 100 + + for ( U32 i = 0; i < MAX_TRIES; i++ ) + { + outName = String::ToString( "%s%d", nameStr.c_str(), suffixNumb ); + + if ( !Sim::findObject( outName, dummy ) ) + return outName; + + suffixNumb++; + } + + Con::errorf( "Sim::getUniqueName( %s ) - failed after %d attempts", inName, MAX_TRIES ); + return String::EmptyString; +} + +String getUniqueInternalName( const char *inName, SimSet *inSet, bool searchChildren ) +{ + // Since SimSet::findObjectByInternalName operates with StringTableEntry(s) + // we have to muck up the StringTable with our attempts. + // But then again, so does everywhere else. + + StringTableEntry outName = StringTable->insert( inName ); + + if ( !outName || !outName[0] ) + return String::EmptyString; + + if ( !inSet->findObjectByInternalName( outName, searchChildren ) ) + return String(outName); + + S32 suffixNumb = -1; + String nameStr( String::GetTrailingNumber( outName, suffixNumb ) ); + suffixNumb++; + + static char tempStr[512]; + +#define MAX_TRIES 100 + + for ( U32 i = 0; i < MAX_TRIES; i++ ) + { + dSprintf( tempStr, 512, "%s%d", nameStr.c_str(), suffixNumb ); + outName = StringTable->insert( tempStr ); + + if ( !inSet->findObjectByInternalName( outName, searchChildren ) ) + return String(outName); + + suffixNumb++; + } + + Con::errorf( "Sim::getUniqueInternalName( %s ) - failed after %d attempts", inName, MAX_TRIES ); + return String::EmptyString; +} + +bool isValidObjectName( const char* name ) +{ + if( !name || !name[ 0 ] ) + return true; // Anonymous object. + + if( !dIsalpha( name[ 0 ] ) && name[ 0 ] != '_' ) + return false; + + for( U32 i = 1; name[ i ]; ++ i ) + if( !dIsalnum( name[ i ] ) && name[ i ] != '_' ) + return false; + + return true; +} + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- + +#define InstantiateNamedSet(set) g##set = new SimSet; g##set->registerObject(#set); g##set->setNameChangeAllowed(false); gRootGroup->addObject(g##set); SIMSET_SET_ASSOCIATION((*g##set)) +#define InstantiateNamedGroup(set) g##set = new SimGroup; g##set->registerObject(#set); g##set->setNameChangeAllowed(false); gRootGroup->addObject(g##set); SIMSET_SET_ASSOCIATION((*g##set)) + +static bool sgIsShuttingDown; + +SimDataBlockGroup *gDataBlockGroup; +SimDataBlockGroup *getDataBlockGroup() +{ + return gDataBlockGroup; +} + + +void init() +{ + initEventQueue(); + initRoot(); + + InstantiateNamedSet(ActiveActionMapSet); + InstantiateNamedSet(GhostAlwaysSet); + InstantiateNamedSet(WayPointSet); + InstantiateNamedSet(fxReplicatorSet); + InstantiateNamedSet(fxFoliageSet); + InstantiateNamedSet(MaterialSet); + InstantiateNamedSet(SFXSourceSet); + InstantiateNamedSet(SFXDescriptionSet); + InstantiateNamedSet(SFXTrackSet); + InstantiateNamedSet(SFXEnvironmentSet); + InstantiateNamedSet(SFXStateSet); + InstantiateNamedSet(SFXAmbienceSet); + InstantiateNamedSet(TerrainMaterialSet); + InstantiateNamedSet(DataBlockSet); + InstantiateNamedGroup(ActionMapGroup); + InstantiateNamedGroup(ClientGroup); + InstantiateNamedGroup(GuiGroup); + InstantiateNamedGroup(GuiDataGroup); + InstantiateNamedGroup(TCPGroup); + InstantiateNamedGroup(ClientConnectionGroup); + InstantiateNamedGroup(SFXParameterGroup); + InstantiateNamedSet(BehaviorSet); + InstantiateNamedSet(sgMissionLightingFilterSet); + + gDataBlockGroup = new SimDataBlockGroup(); + gDataBlockGroup->registerObject("DataBlockGroup"); + gRootGroup->addObject(gDataBlockGroup); + + SimPersistID::init(); +} + +void shutdown() +{ + sgIsShuttingDown = true; + + shutdownRoot(); + shutdownEventQueue(); + + SimPersistID::shutdown(); +} + +bool isShuttingDown() +{ + return sgIsShuttingDown; +} + +} + + +#endif // DOXYGENIZING. + +SimDataBlockGroup::SimDataBlockGroup() +{ + mLastModifiedKey = 0; +} + +S32 QSORT_CALLBACK SimDataBlockGroup::compareModifiedKey(const void* a,const void* b) +{ + const SimDataBlock* dba = *((const SimDataBlock**)a); + const SimDataBlock* dbb = *((const SimDataBlock**)b); + + return dba->getModifiedKey() - dbb->getModifiedKey(); +} + + +void SimDataBlockGroup::sort() +{ + if(mLastModifiedKey != SimDataBlock::getNextModifiedKey()) + { + mLastModifiedKey = SimDataBlock::getNextModifiedKey(); + dQsort(objectList.address(),objectList.size(),sizeof(SimObject *),compareModifiedKey); + } +} diff --git a/Engine/source/console/simObject.cpp b/Engine/source/console/simObject.cpp new file mode 100644 index 000000000..3385f3a91 --- /dev/null +++ b/Engine/source/console/simObject.cpp @@ -0,0 +1,2790 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "platform/platformMemory.h" +#include "console/simObject.h" +#include "console/console.h" +#include "console/consoleInternal.h" +#include "console/engineAPI.h" +#include "console/simFieldDictionary.h" +#include "console/simPersistID.h" +#include "console/typeValidators.h" +#include "console/arrayObject.h" +#include "console/codeBlock.h" +#include "core/frameAllocator.h" +#include "core/stream/fileStream.h" +#include "core/fileObject.h" + + +IMPLEMENT_CONOBJECT( SimObject ); + +// See full description in the new CHM manual +ConsoleDocClass( SimObject, + "@brief Base class for almost all objects involved in the simulation.\n\n" + + "@ingroup Console\n" +); + +bool SimObject::smForceId = false; +SimObjectId SimObject::smForcedId = 0; + + +namespace Sim +{ + // Defined in simManager.cpp + extern SimGroup *gRootGroup; + extern SimManagerNameDictionary *gNameDictionary; + extern SimIdDictionary *gIdDictionary; + extern U32 gNextObjectId; +} + + +//----------------------------------------------------------------------------- + +SimObject::SimObject() +{ + objectName = NULL; + mOriginalName = NULL; + mInternalName = NULL; + nextNameObject = (SimObject*)-1; + nextManagerNameObject = (SimObject*)-1; + nextIdObject = NULL; + + mFilename = NULL; + mDeclarationLine = -1; + + mId = 0; + mIdString[ 0 ] = '\0'; + mGroup = 0; + mNameSpace = NULL; + mNotifyList = NULL; + mFlags.set( ModStaticFields | ModDynamicFields ); + + mFieldDictionary = NULL; + mCanSaveFieldDictionary = true; + + mClassName = NULL; + mSuperClassName = NULL; + + mCopySource = NULL; + mPersistentId = NULL; +} + +//----------------------------------------------------------------------------- + +SimObject::~SimObject() +{ + if( mFieldDictionary ) + { + delete mFieldDictionary; + mFieldDictionary = NULL; + } + + // Release persistent ID. + if( mPersistentId ) + { + mPersistentId->unresolve(); + mPersistentId->decRefCount(); + mPersistentId = NULL; + } + + if( mCopySource ) + mCopySource->unregisterReference( &mCopySource ); + + AssertFatal(nextNameObject == (SimObject*)-1,avar( + "SimObject::~SimObject: Not removed from dictionary: name %s, id %i", + objectName, mId)); + AssertFatal(nextManagerNameObject == (SimObject*)-1,avar( + "SimObject::~SimObject: Not removed from manager dictionary: name %s, id %i", + objectName,mId)); + AssertFatal(mFlags.test(Added) == 0, "SimObject::object " + "missing call to SimObject::onRemove"); +} + +//----------------------------------------------------------------------------- + +bool SimObject::processArguments(S32 argc, const char**argv) +{ + return argc == 0; +} + +//----------------------------------------------------------------------------- + +void SimObject::initPersistFields() +{ + addGroup( "Ungrouped" ); + + addProtectedField( "name", TypeName, Offset(objectName, SimObject), &setProtectedName, &defaultProtectedGetFn, + "Optional global name of this object." ); + + endGroup( "Ungrouped" ); + + addGroup( "Object" ); + + addField( "internalName", TypeString, Offset(mInternalName, SimObject), + "Optional name that may be used to lookup this object within a SimSet."); + + addProtectedField( "parentGroup", TYPEID< SimObject >(), Offset(mGroup, SimObject), &setProtectedParent, &defaultProtectedGetFn, + "Group hierarchy parent of the object." ); + + addProtectedField( "class", TypeString, Offset(mClassName, SimObject), &setClass, &defaultProtectedGetFn, + "Script class of object." ); + + addProtectedField( "superClass", TypeString, Offset(mSuperClassName, SimObject), &setSuperClass, &defaultProtectedGetFn, + "Script super-class of object." ); + + // For legacy support + addProtectedField( "className", TypeString, Offset(mClassName, SimObject), &setClass, &defaultProtectedGetFn, + "Script class of object.", AbstractClassRep::FIELD_HideInInspectors ); + + endGroup( "Object" ); + + addGroup( "Editing" ); + + addProtectedField( "hidden", TypeBool, NULL, + &_setHidden, &_getHidden, + "Whether the object is visible." ); + addProtectedField( "locked", TypeBool, NULL, + &_setLocked, &_getLocked, + "Whether the object can be edited." ); + + endGroup( "Editing" ); + + addGroup( "Persistence" ); + + addProtectedField( "canSave", TypeBool, Offset( mFlags, SimObject ), + &_setCanSave, &_getCanSave, + "Whether the object can be saved out. If false, the object is purely transient in nature." ); + + addField( "canSaveDynamicFields", TypeBool, Offset(mCanSaveFieldDictionary, SimObject), + "True if dynamic fields (added at runtime) should be saved. Defaults to true." ); + + addProtectedField( "persistentId", TypePID, Offset( mPersistentId, SimObject ), + &_setPersistentID, &defaultProtectedGetFn, + "The universally unique identifier for the object." ); + + endGroup( "Persistence" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +String SimObject::describeSelf() const +{ + String desc = Parent::describeSelf(); + + if( mId != 0 ) + desc = avar( "%s|id: %i", desc.c_str(), mId ); + if( objectName ) + desc = avar( "%s|name: %s", desc.c_str(), objectName ); + if( mInternalName ) + desc = avar( "%s|internal: %s", desc.c_str(), mInternalName ); + if( mNameSpace ) + desc = avar( "%s|nspace: %s", desc.c_str(), mNameSpace->mName ); + if( mGroup ) + desc = avar( "%s|group: %s", desc.c_str(), mGroup->getName() ); + if( mCopySource ) + desc = avar( "%s|copy: %s", desc.c_str(), mCopySource->getName() ); + if( mPersistentId ) + desc = avar( "%s|pid: %s", desc.c_str(), mPersistentId->getUUID().toString().c_str() ); + + return desc; +} + +//============================================================================= +// Persistence. +//============================================================================= +// MARK: ---- Persistence ---- + +//----------------------------------------------------------------------------- + +bool SimObject::writeField(StringTableEntry fieldname, const char* value) +{ + // Don't write empty fields. + if (!value || !*value) + return false; + + // Don't write owner field for components + static StringTableEntry sOwner = StringTable->insert( "owner" ); + if( fieldname == sOwner ) + return false; + + // Don't write ParentGroup + static StringTableEntry sParentGroup = StringTable->insert( "parentGroup" ); + if( fieldname == sParentGroup ) + return false; + + // Don't write name, is within the parenthesis already + static StringTableEntry sName = StringTable->insert( "name" ); + if( fieldname == sName ) + return false; + + // Don't write className, it is read for legacy support but we + // write it out as class. + static StringTableEntry sClassName = StringTable->insert( "className" ); + if( fieldname == sClassName ) + return false; + + // Write persistent ID only if present. + static StringTableEntry sPersistentId = StringTable->insert( "persistentId" ); + if( fieldname == sPersistentId && ( !value || !value[ 0 ] ) ) + return false; + + // Don't write hidden and locked flags if they are at their default value. + + static StringTableEntry sHidden = StringTable->insert( "hidden" ); + static StringTableEntry sLocked = StringTable->insert( "locked" ); + + if( fieldname == sHidden && !dAtob( value ) ) + return false; + if( fieldname == sLocked && !dAtob( value ) ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- + +void SimObject::writeFields(Stream &stream, U32 tabStop) +{ + // Write static fields. + + const AbstractClassRep::FieldList &list = getFieldList(); + + for(U32 i = 0; i < list.size(); i++) + { + const AbstractClassRep::Field* f = &list[i]; + + // Skip the special field types. + if ( f->type >= AbstractClassRep::ARCFirstCustomField ) + continue; + + for(U32 j = 0; S32(j) < f->elementCount; j++) + { + char array[8]; + dSprintf( array, 8, "%d", j ); + const char *val = getDataField(StringTable->insert( f->pFieldname ), array ); + + // Make a copy for the field check. + if (!val) + continue; + + U32 nBufferSize = dStrlen( val ) + 1; + FrameTemp valCopy( nBufferSize ); + dStrcpy( (char *)valCopy, val ); + + if (!writeField(f->pFieldname, valCopy)) + continue; + + val = valCopy; + + U32 expandedBufferSize = ( nBufferSize * 2 ) + dStrlen(f->pFieldname) + 32; + FrameTemp expandedBuffer( expandedBufferSize ); + if(f->elementCount == 1) + dSprintf(expandedBuffer, expandedBufferSize, "%s = \"", f->pFieldname); + else + dSprintf(expandedBuffer, expandedBufferSize, "%s[%d] = \"", f->pFieldname, j); + + // detect and collapse relative path information + char fnBuf[1024]; + if (f->type == TypeFilename || + f->type == TypeStringFilename || + f->type == TypeImageFilename || + f->type == TypePrefabFilename || + f->type == TypeShapeFilename) + { + Con::collapseScriptFilename(fnBuf, 1024, val); + val = fnBuf; + } + + expandEscape((char*)expandedBuffer + dStrlen(expandedBuffer), val); + dStrcat(expandedBuffer, "\";\r\n"); + + stream.writeTabs(tabStop); + stream.write(dStrlen(expandedBuffer),expandedBuffer); + } + } + + // Write dynamic fields, if enabled. + + if(mFieldDictionary && mCanSaveFieldDictionary) + mFieldDictionary->writeFields(this, stream, tabStop); +} + +//----------------------------------------------------------------------------- + +void SimObject::write(Stream &stream, U32 tabStop, U32 flags) +{ + if( !getCanSave() && !( flags & IgnoreCanSave ) ) + return; + + // Only output selected objects if they want that. + if((flags & SelectedOnly) && !isSelected()) + return; + + stream.writeTabs(tabStop); + char buffer[1024]; + dSprintf(buffer, sizeof(buffer), "new %s(%s) {\r\n", getClassName(), getName() && !(flags & NoName) ? getName() : ""); + stream.write(dStrlen(buffer), buffer); + writeFields(stream, tabStop + 1); + + stream.writeTabs(tabStop); + stream.write(4, "};\r\n"); +} + +//----------------------------------------------------------------------------- + +bool SimObject::save(const char *pcFileName, bool bOnlySelected, const char *preappend) +{ + static const char *beginMessage = "//--- OBJECT WRITE BEGIN ---"; + static const char *endMessage = "//--- OBJECT WRITE END ---"; + FileStream *stream; + FileObject f; + f.readMemory(pcFileName); + + // check for flags + U32 writeFlags = 0; + if(bOnlySelected) + writeFlags |= SimObject::SelectedOnly; + + if((stream = FileStream::createAndOpen( pcFileName, Torque::FS::File::Write )) == NULL) + return false; + + char docRoot[256]; + char modRoot[256]; + + dStrcpy(docRoot, pcFileName); + char *p = dStrrchr(docRoot, '/'); + if (p) *++p = '\0'; + else docRoot[0] = '\0'; + + dStrcpy(modRoot, pcFileName); + p = dStrchr(modRoot, '/'); + if (p) *++p = '\0'; + else modRoot[0] = '\0'; + + Con::setVariable("$DocRoot", docRoot); + Con::setVariable("$ModRoot", modRoot); + + const char *buffer; + while(!f.isEOF()) + { + buffer = (const char *) f.readLine(); + if(!dStrcmp(buffer, beginMessage)) + break; + stream->write(dStrlen(buffer), buffer); + stream->write(2, "\r\n"); + } + stream->write(dStrlen(beginMessage), beginMessage); + stream->write(2, "\r\n"); + if ( preappend != NULL ) + stream->write(dStrlen(preappend),preappend); + write(*stream, 0, writeFlags); + stream->write(dStrlen(endMessage), endMessage); + stream->write(2, "\r\n"); + while(!f.isEOF()) + { + buffer = (const char *) f.readLine(); + if(!dStrcmp(buffer, endMessage)) + break; + } + while(!f.isEOF()) + { + buffer = (const char *) f.readLine(); + stream->write(dStrlen(buffer), buffer); + stream->write(2, "\r\n"); + } + + Con::setVariable("$DocRoot", NULL); + Con::setVariable("$ModRoot", NULL); + + delete stream; + + return true; + +} + +//----------------------------------------------------------------------------- + +SimPersistID* SimObject::getOrCreatePersistentId() +{ + if( !mPersistentId ) + { + mPersistentId = SimPersistID::create( this ); + mPersistentId->incRefCount(); + } + return mPersistentId; +} + +//----------------------------------------------------------------------------- + +bool SimObject::_setPersistentID( void* object, const char* index, const char* data ) +{ + SimObject* simObject = reinterpret_cast< SimObject* >( object ); + + // Make sure we don't already have a PID. + if( simObject->getPersistentId() ) + { + Con::errorf( "SimObject::_setPersistentID - cannot set a persistent ID on an object that already has a persistent ID assigned." ); + return false; + } + + SimPersistID* pid; + Con::setData( TypePID, &pid, 0, 1, &data ); + if ( !pid ) + return false; + + // Make sure it's not already bound to an object. + if( pid->getObject() ) + { + AssertWarn( pid->getObject() != simObject, "Sim::_setPersistentID - PID is bound to this object yet not assigned to it!" ); + + SimObject* otherObj = pid->getObject(); + Con::errorf( "SimObject::_setPersistentID - UUID is already used by another object: '%s' -> %i:%s (%s)", + data, otherObj->getId(), otherObj->getClassName(), otherObj->getName() ); + + return false; + } + + pid->resolve( simObject ); + pid->incRefCount(); + simObject->mPersistentId = pid; + + return false; +} + +//----------------------------------------------------------------------------- + +void SimObject::setFilename( const char* file ) +{ + if( file ) + mFilename = StringTable->insert( file ); + else + mFilename = StringTable->EmptyString(); +} + +//----------------------------------------------------------------------------- + +void SimObject::setDeclarationLine(U32 lineNumber) +{ + mDeclarationLine = lineNumber; +} + +//============================================================================= +// Management. +//============================================================================= +// MARK: ---- Management ---- + +//----------------------------------------------------------------------------- + +bool SimObject::registerObject() +{ + AssertFatal( !mFlags.test( Added ), "reigsterObject - Object already registered!"); + mFlags.clear(Deleted | Removed); + + if(smForceId) + { + setId(smForcedId); + smForceId = false; + } + + if( !mId ) + { + mId = Sim::gNextObjectId++; + dSprintf( mIdString, sizeof( mIdString ), "%u", mId ); + } + + AssertFatal(Sim::gIdDictionary && Sim::gNameDictionary, + "SimObject::registerObject - tried to register an object before Sim::init()!"); + + Sim::gIdDictionary->insert(this); + + Sim::gNameDictionary->insert(this); + + // Notify object + bool ret = onAdd(); + + if(!ret) + unregisterObject(); + + AssertFatal(!ret || isProperlyAdded(), "Object did not call SimObject::onAdd()"); + return ret; +} + +//----------------------------------------------------------------------------- + +void SimObject::unregisterObject() +{ + mFlags.set(Removed); + + // Notify object first + onRemove(); + + // Clear out any pending notifications before + // we call our own, just in case they delete + // something that we have referenced. + clearAllNotifications(); + + // Notify all objects that are waiting for delete + // messages + if (getGroup()) + getGroup()->removeObject(this); + + processDeleteNotifies(); + + // Do removals from the Sim. + Sim::gNameDictionary->remove(this); + Sim::gIdDictionary->remove(this); + Sim::cancelPendingEvents(this); +} + +//----------------------------------------------------------------------------- + +void SimObject::deleteObject() +{ + Parent::destroySelf(); +} + +//----------------------------------------------------------------------------- + +void SimObject::_destroySelf() +{ + AssertFatal( !isDeleted(), "SimObject::destroySelf - Object has already been deleted" ); + AssertFatal( !isRemoved(), "SimObject::destroySelf - Object in the process of being removed" ); + + mFlags.set( Deleted ); + + if( mFlags.test( Added ) ) + unregisterObject(); + + Parent::_destroySelf(); +} + +//----------------------------------------------------------------------------- + +void SimObject::destroySelf() +{ + // When using the legacy console interop, we don't delete objects + // when their reference count drops to zero but rather defer their + // deletion until deleteObject() is called. + + if( engineAPI::gUseConsoleInterop ) + return; + + Parent::destroySelf(); +} + +//----------------------------------------------------------------------------- + +class SimObjectDeleteEvent : public SimEvent +{ +public: + void process(SimObject *object) + { + object->deleteObject(); + } +}; + +void SimObject::safeDeleteObject() +{ + Sim::postEvent( this, new SimObjectDeleteEvent, Sim::getCurrentTime() + 1 ); +} + +//----------------------------------------------------------------------------- + +void SimObject::setId(SimObjectId newId) +{ + if(!mFlags.test(Added)) + mId = newId; + else + { + // get this object out of the id dictionary if it's in it + Sim::gIdDictionary->remove(this); + + // Free current Id. + // Assign new one. + mId = newId ? newId : Sim::gNextObjectId++; + Sim::gIdDictionary->insert(this); + } + + dSprintf( mIdString, sizeof( mIdString ), "%u", mId ); +} + +//----------------------------------------------------------------------------- + +void SimObject::assignName(const char *name) +{ + if( objectName && !isNameChangeAllowed() ) + { + Con::errorf( "SimObject::assignName - not allowed to change name of object '%s'", objectName ); + return; + } + + // Added this assert 3/30/2007 because it is dumb to try to name + // a SimObject the same thing as it's class name -patw + //AssertFatal( dStricmp( getClassName(), name ), "Attempted to assign a name to a SimObject which matches it's type name." ); + if( dStricmp( getClassName(), name ) == 0 ) + Con::errorf( "SimObject::assignName - Assigning name '%s' to instance of object with type '%s'." + " This can cause namespace linking issues.", getClassName(), name ); + + StringTableEntry newName = NULL; + if(name[0]) + newName = StringTable->insert(name); + + onNameChange( newName ); + + if( mGroup ) + mGroup->mNameDictionary.remove( this ); + if( isProperlyAdded() ) + { + unlinkNamespaces(); + Sim::gNameDictionary->remove( this ); + } + + objectName = newName; + + if( mGroup ) + mGroup->mNameDictionary.insert( this ); + if( isProperlyAdded() ) + { + Sim::gNameDictionary->insert( this ); + linkNamespaces(); + } +} + +//----------------------------------------------------------------------------- + +bool SimObject::registerObject(U32 id) +{ + setId(id); + return registerObject(); +} + +//----------------------------------------------------------------------------- + +bool SimObject::registerObject(const char *name) +{ + assignName(name); + return registerObject(); +} + +//----------------------------------------------------------------------------- + +bool SimObject::registerObject(const char *name, U32 id) +{ + setId(id); + assignName(name); + return registerObject(); +} + +//============================================================================= +// Introspection. +//============================================================================= +// MARK: ---- Introspection ---- + +//----------------------------------------------------------------------------- + +bool SimObject::isMethod( const char* methodName ) +{ + if( !methodName || !methodName[0] ) + return false; + + StringTableEntry stname = StringTable->insert( methodName ); + + if( getNamespace() ) + return ( getNamespace()->lookup( stname ) != NULL ); + + return false; +} + +//----------------------------------------------------------------------------- + +bool SimObject::isField( const char* fieldName, bool includeStatic, bool includeDynamic ) +{ + const char* strFieldName = StringTable->insert( fieldName ); + + if( includeStatic && getClassRep()->findField( strFieldName ) ) + return true; + + if( includeDynamic && getFieldDictionary() && getFieldDictionary()->findDynamicField( strFieldName ) ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- + +void SimObject::assignDynamicFieldsFrom(SimObject* parent) +{ + if(parent->mFieldDictionary) + { + if( mFieldDictionary == NULL ) + mFieldDictionary = new SimFieldDictionary; + mFieldDictionary->assignFrom(parent->mFieldDictionary); + } +} + +//----------------------------------------------------------------------------- + +void SimObject::assignFieldsFrom(SimObject *parent) +{ + // Only allow field assigns from objects of the same class or + // a superclass. + + if( getClassRep()->isClass( parent->getClassRep() ) ) + { + const AbstractClassRep::FieldList &list = parent->getFieldList(); + + // copy out all the fields: + for(U32 i = 0; i < list.size(); i++) + { + const AbstractClassRep::Field* f = &list[i]; + + // Skip the special field types. + if ( f->type >= AbstractClassRep::ARCFirstCustomField ) + continue; + + // Skip certain fields that we don't want to see copied so we don't + // get error messages from their setters. + + static StringTableEntry sName = StringTable->insert( "name" ); + static StringTableEntry sPersistentId = StringTable->insert( "persistentId" ); + + if( f->pFieldname == sName || f->pFieldname == sPersistentId ) + continue; + + S32 lastField = f->elementCount - 1; + for(S32 j = 0; j <= lastField; j++) + { + const char* fieldVal = (*f->getDataFn)( parent, Con::getData(f->type, (void *) (((const char *)parent) + f->offset), j, f->table, f->flag)); + + // Don't assign the field is the pointer is null or if + // the field is not empty and writing it was disallowed. + if ( !fieldVal || ( fieldVal[0] && !writeField( f->pFieldname, fieldVal ) ) ) + continue; + + // code copied from SimObject::setDataField(). + // TODO: paxorr: abstract this into a better setData / getData that considers prot fields. + FrameTemp buffer(2048); + FrameTemp bufferSecure(2048); // This buffer is used to make a copy of the data + ConsoleBaseType *cbt = ConsoleBaseType::getType( f->type ); + const char* szBuffer = cbt->prepData( fieldVal, buffer, 2048 ); + dMemset( bufferSecure, 0, 2048 ); + dMemcpy( bufferSecure, szBuffer, dStrlen( szBuffer ) ); + + if((*f->setDataFn)( this, NULL, bufferSecure ) ) + Con::setData(f->type, (void *) (((const char *)this) + f->offset), j, 1, &fieldVal, f->table); + } + } + } + else + { + Con::errorf( "SimObject::assignFieldsFrom() - cannot assigned fields from object of type '%s' to object of type '%s'", + parent->getClassName(), getClassName() + ); + } + + assignDynamicFieldsFrom(parent); +} + +//----------------------------------------------------------------------------- + +void SimObject::setDataField(StringTableEntry slotName, const char *array, const char *value) +{ + // first search the static fields if enabled + if(mFlags.test(ModStaticFields)) + { + const AbstractClassRep::Field *fld = findField(slotName); + if(fld) + { + // Skip the special field types as they are not data. + if ( fld->type >= AbstractClassRep::ARCFirstCustomField ) + return; + + S32 array1 = array ? dAtoi(array) : 0; + + if(array1 >= 0 && array1 < fld->elementCount && fld->elementCount >= 1) + { + // If the set data notify callback returns true, then go ahead and + // set the data, otherwise, assume the set notify callback has either + // already set the data, or has deemed that the data should not + // be set at all. + FrameTemp buffer(2048); + FrameTemp bufferSecure(2048); // This buffer is used to make a copy of the data + // so that if the prep functions or any other functions use the string stack, the data + // is not corrupted. + + ConsoleBaseType *cbt = ConsoleBaseType::getType( fld->type ); + AssertFatal( cbt != NULL, "Could not resolve Type Id." ); + + const char* szBuffer = cbt->prepData( value, buffer, 2048 ); + dMemset( bufferSecure, 0, 2048 ); + dMemcpy( bufferSecure, szBuffer, dStrlen( szBuffer ) ); + + if( (*fld->setDataFn)( this, array, bufferSecure ) ) + Con::setData(fld->type, (void *) (((const char *)this) + fld->offset), array1, 1, &value, fld->table); + + if(fld->validator) + fld->validator->validateType(this, (void *) (((const char *)this) + fld->offset)); + + onStaticModified( slotName, value ); + + return; + } + + if(fld->validator) + fld->validator->validateType(this, (void *) (((const char *)this) + fld->offset)); + + onStaticModified( slotName, value ); + return; + } + } + + if(mFlags.test(ModDynamicFields)) + { + if(!mFieldDictionary) + mFieldDictionary = new SimFieldDictionary; + + if(!array) + { + mFieldDictionary->setFieldValue(slotName, value); + onDynamicModified( slotName, value ); + } + else + { + char buf[256]; + dStrcpy(buf, slotName); + dStrcat(buf, array); + StringTableEntry permanentSlotName = StringTable->insert(buf); + mFieldDictionary->setFieldValue(permanentSlotName, value); + onDynamicModified( permanentSlotName, value ); + } + } +} + +//----------------------------------------------------------------------------- + +const char *SimObject::getDataField(StringTableEntry slotName, const char *array) +{ + if(mFlags.test(ModStaticFields)) + { + S32 array1 = array ? dAtoi(array) : -1; + const AbstractClassRep::Field *fld = findField(slotName); + + if(fld) + { + if(array1 == -1 && fld->elementCount == 1) + return (*fld->getDataFn)( this, Con::getData(fld->type, (void *) (((const char *)this) + fld->offset), 0, fld->table, fld->flag) ); + if(array1 >= 0 && array1 < fld->elementCount) + return (*fld->getDataFn)( this, Con::getData(fld->type, (void *) (((const char *)this) + fld->offset), array1, fld->table, fld->flag) );// + typeSizes[fld.type] * array1)); + return ""; + } + } + + if(mFlags.test(ModDynamicFields)) + { + if(!mFieldDictionary) + return ""; + + if(!array) + { + if (const char* val = mFieldDictionary->getFieldValue(slotName)) + return val; + } + else + { + static char buf[256]; + dStrcpy(buf, slotName); + dStrcat(buf, array); + if (const char* val = mFieldDictionary->getFieldValue(StringTable->insert(buf))) + return val; + } + } + + return ""; +} + +//----------------------------------------------------------------------------- + +U32 SimObject::getDataFieldType( StringTableEntry slotName, const char* array ) +{ + const AbstractClassRep::Field* field = findField( slotName ); + if(field) + return field->type; + + // Check dynamic fields + if(!mFieldDictionary) + return 0; + + if(array == NULL || *array == 0) + return mFieldDictionary->getFieldType( slotName ); + else + { + static char buf[256]; + dStrcpy( buf, slotName ); + dStrcat( buf, array ); + + return mFieldDictionary->getFieldType( StringTable->insert( buf ) ); + } +} + +//----------------------------------------------------------------------------- + +void SimObject::setDataFieldType(const U32 fieldTypeId, StringTableEntry slotName, const char *array) +{ + // This only works on dynamic fields, bail if we have no field dictionary + if(!mFieldDictionary) + return; + + if(array == NULL || *array == 0) + { + mFieldDictionary->setFieldType( slotName, fieldTypeId ); + onDynamicModified( slotName, mFieldDictionary->getFieldValue(slotName) ); + } + else + { + static char buf[256]; + dStrcpy( buf, slotName ); + dStrcat( buf, array ); + + mFieldDictionary->setFieldType( StringTable->insert( buf ), fieldTypeId ); + onDynamicModified( slotName, mFieldDictionary->getFieldValue(slotName) ); + } +} + +//----------------------------------------------------------------------------- + +void SimObject::setDataFieldType(const char *typeName, StringTableEntry slotName, const char *array) +{ + // This only works on dynamic fields, bail if we have no field dictionary + if(!mFieldDictionary) + return; + + if(array == NULL || *array == 0) + mFieldDictionary->setFieldType( slotName, typeName ); + else + { + static char buf[256]; + dStrcpy( buf, slotName ); + dStrcat( buf, array ); + StringTableEntry permanentSlotName = StringTable->insert(buf); + + mFieldDictionary->setFieldType( permanentSlotName, typeName ); + onDynamicModified( permanentSlotName, mFieldDictionary->getFieldValue(permanentSlotName) ); + } +} + +//----------------------------------------------------------------------------- + +void SimObject::dumpClassHierarchy() +{ + AbstractClassRep* pRep = getClassRep(); + while(pRep) + { + Con::warnf("%s ->", pRep->getClassName()); + pRep = pRep->getParentClass(); + } +} + +//----------------------------------------------------------------------------- + +SimObject* SimObject::clone() +{ + if( !getClassRep() ) + return NULL; + + ConsoleObject* conObject = getClassRep()->create(); + if( !conObject ) + return NULL; + + SimObject* simObject = dynamic_cast< SimObject* >( conObject ); + if( !simObject ) + { + delete conObject; + return NULL; + } + + simObject->assignFieldsFrom( this ); + + String name = Sim::getUniqueName( getName() ); + if( !simObject->registerObject( name ) ) + { + delete simObject; + return NULL; + } + + if( getGroup() ) + getGroup()->addObject( simObject ); + + return simObject; +} + +//----------------------------------------------------------------------------- + +SimObject* SimObject::deepClone() +{ + return clone(); +} + +//============================================================================= +// Grouping. +//============================================================================= +// MARK: ---- Grouping ---- + +//----------------------------------------------------------------------------- + +SimObject* SimObject::findObject( const char* ) +{ + return NULL; +} + +//----------------------------------------------------------------------------- + +bool SimObject::isChildOfGroup(SimGroup* pGroup) +{ + if(!pGroup) + return false; + + //if we *are* the group in question, + //return true: + if(pGroup == dynamic_cast(this)) + return true; + + SimGroup* temp = mGroup; + while(temp) + { + if(temp == pGroup) + return true; + temp = temp->mGroup; + } + + return false; +} + +//----------------------------------------------------------------------------- + +bool SimObject::addToSet(SimObjectId spid) +{ + if (mFlags.test(Added) == false) + return false; + + SimObject* ptr = Sim::findObject(spid); + if (ptr) + { + SimSet* sp = dynamic_cast(ptr); + AssertFatal(sp != 0, + "SimObject::addToSet: " + "ObjectId does not refer to a set object"); + sp->addObject(this); + return true; + } + return false; +} + +//----------------------------------------------------------------------------- + +bool SimObject::addToSet(const char *ObjectName) +{ + if (mFlags.test(Added) == false) + return false; + + SimObject* ptr = Sim::findObject(ObjectName); + if (ptr) + { + SimSet* sp = dynamic_cast(ptr); + AssertFatal(sp != 0, + "SimObject::addToSet: " + "ObjectName does not refer to a set object"); + sp->addObject(this); + return true; + } + return false; +} + +//----------------------------------------------------------------------------- + +bool SimObject::removeFromSet(SimObjectId sid) +{ + if (mFlags.test(Added) == false) + return false; + + SimSet *set; + if(Sim::findObject(sid, set)) + { + set->removeObject(this); + return true; + } + return false; +} + +//----------------------------------------------------------------------------- + +bool SimObject::removeFromSet(const char *objectName) +{ + if (mFlags.test(Added) == false) + return false; + + SimSet *set; + if(Sim::findObject(objectName, set)) + { + set->removeObject(this); + return true; + } + return false; +} + +//----------------------------------------------------------------------------- + +void SimObject::dumpGroupHierarchy() +{ + String className( getClassName() ); + String objectName( getName() ); + + Con::warnf( "[%i] %s - %s ->", getId(), className.c_str(), objectName.c_str() ); + + if ( mGroup ) + mGroup->dumpGroupHierarchy(); +} + +//============================================================================= +// Events. +//============================================================================= +// MARK: ---- Events ---- + +//----------------------------------------------------------------------------- + +bool SimObject::onAdd() +{ + mFlags.set(Added); + + linkNamespaces(); + + return true; +} + +//----------------------------------------------------------------------------- + +void SimObject::onRemove() +{ + mFlags.clear(Added); + + unlinkNamespaces(); +} + +//----------------------------------------------------------------------------- + +void SimObject::onGroupAdd() +{ +} + +//----------------------------------------------------------------------------- + +void SimObject::onGroupRemove() +{ +} + +//----------------------------------------------------------------------------- + +void SimObject::onDeleteNotify(SimObject*) +{ +} + +//----------------------------------------------------------------------------- + +void SimObject::onNameChange(const char*) +{ +} + +//----------------------------------------------------------------------------- + +void SimObject::onStaticModified(const char* slotName, const char* newValue) +{ +} + +//----------------------------------------------------------------------------- + +void SimObject::onDynamicModified(const char* slotName, const char* newValue) +{ +} + +//============================================================================= +// Notifications. +//============================================================================= +// MARK: ---- Notifications ---- + +static Chunker notifyChunker(128000); +SimObject::Notify *SimObject::mNotifyFreeList = NULL; + +//----------------------------------------------------------------------------- + +SimObject::Notify *SimObject::allocNotify() +{ + if(mNotifyFreeList) + { + SimObject::Notify *ret = mNotifyFreeList; + mNotifyFreeList = ret->next; + return ret; + } + return notifyChunker.alloc(); +} + +//----------------------------------------------------------------------------- + +void SimObject::freeNotify(SimObject::Notify* note) +{ + AssertFatal(note->type != SimObject::Notify::Invalid, "Invalid notify"); + note->type = SimObject::Notify::Invalid; + note->next = mNotifyFreeList; + mNotifyFreeList = note; +} + +//----------------------------------------------------------------------------- + +SimObject::Notify* SimObject::removeNotify(void *ptr, SimObject::Notify::Type type) +{ + Notify **list = &mNotifyList; + while(*list) + { + if((*list)->ptr == ptr && (*list)->type == type) + { + SimObject::Notify *ret = *list; + *list = ret->next; + return ret; + } + list = &((*list)->next); + } + return NULL; +} + +//----------------------------------------------------------------------------- + +void SimObject::deleteNotify(SimObject* obj) +{ + AssertFatal(!obj->isDeleted(), + "SimManager::deleteNotify: Object is being deleted"); + Notify *note = allocNotify(); + note->ptr = (void *) this; + note->next = obj->mNotifyList; + note->type = Notify::DeleteNotify; + obj->mNotifyList = note; + + note = allocNotify(); + note->ptr = (void *) obj; + note->next = mNotifyList; + note->type = Notify::ClearNotify; + mNotifyList = note; + + //obj->deleteNotifyList.pushBack(this); + //clearNotifyList.pushBack(obj); +} + +//----------------------------------------------------------------------------- + +void SimObject::registerReference(SimObject **ptr) +{ + Notify *note = allocNotify(); + note->ptr = (void *) ptr; + note->next = mNotifyList; + note->type = Notify::ObjectRef; + mNotifyList = note; +} + +//----------------------------------------------------------------------------- + +void SimObject::unregisterReference(SimObject **ptr) +{ + Notify *note = removeNotify((void *) ptr, Notify::ObjectRef); + if(note) + { + freeNotify(note); + + if( mFlags.test( AutoDelete ) ) + { + for( Notify* n = mNotifyList; n != NULL; n = n->next ) + if( n->type == Notify::ObjectRef ) + return; + + deleteObject(); + } + } +} + +//----------------------------------------------------------------------------- + +void SimObject::clearNotify(SimObject* obj) +{ + Notify *note = obj->removeNotify((void *) this, Notify::DeleteNotify); + if(note) + freeNotify(note); + + note = removeNotify((void *) obj, Notify::ClearNotify); + if(note) + freeNotify(note); +} + +//----------------------------------------------------------------------------- + +void SimObject::processDeleteNotifies() +{ + // clear out any delete notifies and + // object refs. + + while(mNotifyList) + { + Notify *note = mNotifyList; + mNotifyList = note->next; + + AssertFatal(note->type != Notify::ClearNotify, "Clear notes should be all gone."); + + if(note->type == Notify::DeleteNotify) + { + SimObject *obj = (SimObject *) note->ptr; + Notify *cnote = obj->removeNotify((void *)this, Notify::ClearNotify); + obj->onDeleteNotify(this); + freeNotify(cnote); + } + else + { + // it must be an object ref - a pointer refs this object + *((SimObject **) note->ptr) = NULL; + } + freeNotify(note); + } +} + +//----------------------------------------------------------------------------- + +void SimObject::clearAllNotifications() +{ + for(Notify **cnote = &mNotifyList; *cnote; ) + { + Notify *temp = *cnote; + if(temp->type == Notify::ClearNotify) + { + *cnote = temp->next; + Notify *note = ((SimObject *) temp->ptr)->removeNotify((void *) this, Notify::DeleteNotify); + freeNotify(temp); + if ( note ) + freeNotify(note); + } + else + cnote = &(temp->next); + } +} + +//============================================================================= +// Namespaces. +//============================================================================= +// MARK: ---- Namespaces ---- + +//----------------------------------------------------------------------------- + +void SimObject::linkNamespaces() +{ + // Don't link if we already have a namespace linkage in place. + // If you want to change namespace linking, first call unlinkNamespaces() + // while still having the class namespace fields matching the current + // setup. + + AssertWarn( mNameSpace == NULL, "SimObject::linkNamespaces -- Namespace linkage already in place" ); + if( mNameSpace ) + return; + + // Get the namespace for the C++ class. + + Namespace* cppNamespace = getClassRep()->getNameSpace(); + + // Parent namespace defaults to namespace of C++ class. + + Namespace* parentNamespace = cppNamespace; + + // Perform superclass linking, if requested. + + if( mSuperClassName && mSuperClassName[ 0 ] ) + { + // Look up the superclass namespace. + + Namespace* superClassNamespace = Con::lookupNamespace( mSuperClassName ); + + // If packages are active and adding to the superclass namespace, then we will + // have multiple packages in a parent chain that all have the same name. + // Con::lookupNamespace returns the bottom-most package in the chain to us so + // in order to properly link namespace here without conflicting with the package + // mechanism, we need to properly link child namespaces to the bottom-most namespace + // while linking parent namespaces to the topmost namespace. To find the latter + // one, we walk up the hierarchy here. + + Namespace* superClassNamespacePackageRoot = superClassNamespace->getPackageRoot(); + + // Link the superclass namespace to the C++ class namespace. + + if( superClassNamespacePackageRoot->getParent() == NULL ) + { + // The superclass namespace isn't linked yet so we just + // link it to the C++ class namespace and make that our parent. + // No increasing parent reference counts is needed in this case. + + bool ok = superClassNamespacePackageRoot->classLinkTo( cppNamespace ); + AssertFatal( ok, "SimObject::linkNamespaces - failed to link new namespace to c++ class name" ); + parentNamespace = superClassNamespace; + } + else + { + // In debug builds, make sure the namespace hierarchy that's been + // put into place actually makes sense and leads back to the C++ + // class namespace. + +#ifdef TORQUE_DEBUG + + bool foundClassNameNS = false; + for( Namespace* linkWalk = superClassNamespacePackageRoot->getParent(); linkWalk != NULL; + linkWalk = linkWalk->getParent() ) + { + if( linkWalk == cppNamespace ) + { + foundClassNameNS = true; + break; + } + } + + if( !foundClassNameNS ) + { + // C++ class namespace not in parent link chain. Warn about it. + + Con::errorf( + "SimObject::linkNamespaces - cannot link object to superclass %s because c++ class %s is not in the parent namespace chain. Linking object to c++ class.", + mSuperClassName, + getClassName() + ); + + // Clear out superclass name so we don't come across it during + // unlinking. + + mSuperClassName = NULL; + } + else + +#endif + { + // Super link is ok. + + parentNamespace = superClassNamespace; + + // Now increase the reference count of all namespaces in the parent hierarchy + // (up to the C++ class). + + for( Namespace* linkWalk = parentNamespace; + linkWalk != NULL && linkWalk != cppNamespace && linkWalk->getParent() != NULL; + linkWalk = linkWalk->getParent() ) + { + // Skip namespaces coming from packages. + if( linkWalk->getPackage() != NULL ) + continue; + + linkWalk->incRefCountToParent(); + } + } + } + } + + // If class name is set, link it in as the new parent + // which itself inherits from the current parent. + + if( mClassName && mClassName[ 0 ] ) + { + Namespace* classNamespace = Con::lookupNamespace( mClassName ); + if( classNamespace && classNamespace->classLinkTo( parentNamespace ) ) + { + parentNamespace = classNamespace; + } + else + { + // Clear out class name so we don't perform a bogus unlink + // in unlinkNamespaces(). + mClassName = NULL; + } + } + + // Finally, if we have an object name, link its namespace + // as the child to the current parent namespace and let it + // become the final namespace of this object. + + StringTableEntry objectName = getName(); + if( objectName && objectName[ 0 ] ) + { + Namespace* objectNamespace = Con::lookupNamespace( objectName ); + if( objectNamespace && objectNamespace->classLinkTo( parentNamespace ) ) + { + parentNamespace = objectNamespace; + } + } + + // Store our namespace. + + mNameSpace = parentNamespace; +} + +//----------------------------------------------------------------------------- + +void SimObject::unlinkNamespaces() +{ + if( !mNameSpace ) + return; + + Namespace* cppNamespace = getClassRep()->getNameSpace(); + Namespace* parentNamespace = cppNamespace; + + // Handle superclass. + + if( mSuperClassName && mSuperClassName[ 0 ] ) + { + // Get the superclass namespace. + + Namespace* superClassNamespace = Con::lookupNamespace( mSuperClassName ); + + // Make it the parent namespace. + + parentNamespace = superClassNamespace; + + // Decrease parent refcounts on the superclass hierarchy. + + for( Namespace* linkWalk = superClassNamespace; + linkWalk != NULL && linkWalk != cppNamespace && linkWalk->getParent() != NULL; ) + { + // Store the parent link since it may disappear once we + // decrease the reference count. + Namespace* parent = linkWalk->getParent(); + + // Decrease the refcount. + if( linkWalk->getPackage() == NULL ) // Skip namespaces coming from packages. + linkWalk->decRefCountToParent(); + + // Walk up. + linkWalk = parent; + } + } + + // Handle class. + + if( mClassName && mClassName[ 0 ] ) + { + Namespace* classNamespace = Con::lookupNamespace( mClassName ); + if( classNamespace ) + { + classNamespace->decRefCountToParent(); + parentNamespace = classNamespace; + } + } + + // Handle object name. + + StringTableEntry objectName = getName(); + if( objectName && objectName[ 0 ] ) + mNameSpace->decRefCountToParent(); + + mNameSpace = NULL; +} + +//----------------------------------------------------------------------------- + +void SimObject::setClassNamespace( const char *classNamespace ) +{ + StringTableEntry oldClassNamespace = mClassName; + StringTableEntry newClassNamespace = StringTable->insert( classNamespace ); + + if( oldClassNamespace == newClassNamespace ) + return; + + if( isProperlyAdded() ) + unlinkNamespaces(); + + mClassName = newClassNamespace; + + if( isProperlyAdded() ) + { + linkNamespaces(); + + // Restore old namespace setup if linkage failed. + + if( mClassName != newClassNamespace ) + { + mClassName = oldClassNamespace; + linkNamespaces(); + } + } +} + +//----------------------------------------------------------------------------- + +void SimObject::setSuperClassNamespace( const char *superClassNamespace ) +{ + StringTableEntry oldSuperClassNamespace = mSuperClassName; + StringTableEntry newSuperClassNamespace = StringTable->insert( superClassNamespace ); + + if( oldSuperClassNamespace == newSuperClassNamespace ) + return; + + if( isProperlyAdded() ) + unlinkNamespaces(); + + mSuperClassName = newSuperClassNamespace; + + if( isProperlyAdded() ) + { + linkNamespaces(); + + // Restore old setup if linkage failed. + + if( mSuperClassName != newSuperClassNamespace ) + { + mSuperClassName = oldSuperClassNamespace; + linkNamespaces(); + } + } +} + +//============================================================================= +// Misc. +//============================================================================= +// MARK: ---- Misc ---- + +//----------------------------------------------------------------------------- + +void SimObject::setInternalName( const char* newname ) +{ + if( newname ) + mInternalName = StringTable->insert( newname ); + else + mInternalName = StringTable->EmptyString(); +} + +//----------------------------------------------------------------------------- + +void SimObject::setOriginalName( const char* originalName ) +{ + if( originalName ) + mOriginalName = StringTable->insert( originalName ); + else + mOriginalName = StringTable->EmptyString(); +} + +//----------------------------------------------------------------------------- + +const char *SimObject::tabComplete(const char *prevText, S32 baseLen, bool fForward) +{ + return mNameSpace->tabComplete(prevText, baseLen, fForward); +} + +//----------------------------------------------------------------------------- + +void SimObject::setSelected( bool sel ) +{ + if( mFlags.test( Selected ) == sel ) + return; // No change. + + if( sel ) + { + mFlags.set( Selected ); + _onSelected(); + } + else + { + mFlags.clear( Selected ); + _onUnselected(); + } +} + +//----------------------------------------------------------------------------- + +bool SimObject::isSelectedRecursive() const +{ + const SimObject *walk = this; + while ( walk ) + { + if ( walk->isSelected() ) + return true; + walk = walk->getGroup(); + } + + return false; +} + +//----------------------------------------------------------------------------- + +void SimObject::setLocked( bool b ) +{ + if( b ) + mFlags.set( Locked ); + else + mFlags.clear( Locked ); +} + +//----------------------------------------------------------------------------- + +void SimObject::setHidden( bool b ) +{ + if( b ) + mFlags.set( Hidden ); + else + mFlags.clear( Hidden ); +} + +//----------------------------------------------------------------------------- + +void SimObject::setCopySource( SimObject* object ) +{ + if( mCopySource ) + mCopySource->unregisterReference( &mCopySource ); + mCopySource = object; + if( mCopySource ) + mCopySource->registerReference( &mCopySource ); +} + +//--------------------------------------------------------------------------- + +bool SimObject::_setCanSave( void* object, const char* index, const char* data ) +{ + SimObject* obj = reinterpret_cast< SimObject* >( object ); + obj->setCanSave( dAtob( data ) ); + return false; +} + +//----------------------------------------------------------------------------- + +const char* SimObject::_getCanSave( void* object, const char* data ) +{ + SimObject* obj = reinterpret_cast< SimObject* >( object ); + if( obj->getCanSave() ) + return "1"; + else + return "0"; +} + +//--------------------------------------------------------------------------- + +// Copy SimObject to another SimObject (Originally designed for T2D). +void SimObject::copyTo( SimObject* object ) +{ + object->mClassName = mClassName; + object->mSuperClassName = mSuperClassName; + + linkNamespaces(); +} + +//----------------------------------------------------------------------------- + +bool SimObject::setProtectedParent( void *obj, const char *index, const char *data ) +{ + SimGroup *parent = NULL; + SimObject *object = static_cast(obj); + + if(Sim::findObject(data, parent)) + parent->addObject(object); + + // always return false, because we've set mGroup when we called addObject + return false; +} + +//----------------------------------------------------------------------------- + +bool SimObject::setProtectedName(void *obj, const char *index, const char *data) +{ + SimObject *object = static_cast(obj); + + if ( object->isProperlyAdded() ) + object->assignName( data ); + + // always return false because we assign the name here + return false; +} + +//----------------------------------------------------------------------------- + +void SimObject::inspectPreApply() +{ +} + +//----------------------------------------------------------------------------- + +void SimObject::inspectPostApply() +{ +} + +//----------------------------------------------------------------------------- + +String SimObject::_getLogMessage(const char* fmt, void* args) const +{ + String objClass = "UnknownClass"; + if(getClassRep()) + objClass = getClassRep()->getClassName(); + + String objName = getName(); + if(objName.isEmpty()) + objName = "Unnamed"; + + String formattedMessage = String::VToString(fmt, args); + return String::ToString("%s - %s(%i) - %s", + objClass.c_str(), objName.c_str(), getId(), formattedMessage.c_str()); +} + +//============================================================================= +// API. +//============================================================================= +// MARK: ---- API ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimObject, dumpGroupHierarchy, void, (),, + "Dump the hierarchy of this object up to RootGroup to the console." ) +{ + object->dumpGroupHierarchy(); +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, isMethod, bool, ( const char* methodName ),, + "Test whether the given method is defined on this object.\n" + "@param The name of the method.\n" + "@return True if the object implements the given method." ) +{ + return object->isMethod( methodName ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimObject, isChildOfGroup, bool, ( SimGroup* group ),, + "Test whether the object belongs directly or indirectly to the given group.\n" + "@param group The SimGroup object.\n" + "@return True if the object is a child of the given group or a child of a group that the given group is directly or indirectly a child to." ) +{ + return object->isChildOfGroup( group ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, getClassNamespace, const char*, (),, + "Get the name of the class namespace assigned to this object.\n" + "@return The name of the 'class' namespace." ) +{ + return object->getClassNamespace(); +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, getSuperClassNamespace, const char*, (),, + "Get the name of the superclass namespace assigned to this object.\n" + "@return The name of the 'superClass' namespace." ) +{ + return object->getSuperClassNamespace(); +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, setClassNamespace, void, ( const char* name ),, + "Assign a class namespace to this object.\n" + "@param name The name of the 'class' namespace for this object." ) +{ + object->setClassNamespace( name ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, setSuperClassNamespace, void, ( const char* name ),, + "Assign a superclass namespace to this object.\n" + "@param name The name of the 'superClass' namespace for this object." ) +{ + object->setSuperClassNamespace( name ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimObject, isSelected, bool, (),, + "Get whether the object has been marked as selected. (in editor)\n" + "@return True if the object is currently selected." ) +{ + return object->isSelected(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimObject, setIsSelected, void, ( bool state ), ( true ), + "Set whether the object has been marked as selected. (in editor)\n" + "@param state True if object is to be marked selected; false if not." ) +{ + object->setSelected( state ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, isExpanded, bool, (),, + "Get whether the object has been marked as expanded. (in editor)\n" + "@return True if the object is marked expanded." ) +{ + return object->isExpanded(); +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, setIsExpanded, void, ( bool state ), ( true ), + "Set whether the object has been marked as expanded. (in editor)\n" + "@param state True if the object is to be marked expanded; false if not." ) +{ + object->setExpanded( state ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, getFilename, const char*, (),, + "Returns the filename the object is attached to.\n" + "@return The name of the file the object is associated with; usually the file the object was loaded from." ) +{ + return object->getFilename(); +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, setFilename, void, ( const char* fileName ),, + "Sets the object's file name and path\n" + "@param fileName The name of the file to associate this object with." ) +{ + return object->setFilename( fileName ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, getDeclarationLine, S32, (),, + "Get the line number at which the object is defined in its file.\n\n" + "@return The line number of the object's definition in script.\n" + "@see getFilename()") +{ + return object->getDeclarationLine(); +} + +//----------------------------------------------------------------------------- + +#ifdef TORQUE_DEBUG + +static const char* sEnumCallbackFunction; +static void sEnumCallback( EngineObject* object ) +{ + SimObject* simObject = dynamic_cast< SimObject* >( object ); + if( !simObject ) + return; + + Con::evaluatef( "%s( %i );", sEnumCallbackFunction, simObject->getId() ); +} + +DefineEngineFunction( debugEnumInstances, void, ( const char* className, const char* functionName ),, + "Call the given function for each instance of the given class.\n" + "@param className Name of the class for which to enumerate instances.\n" + "@param functionName Name of function to call and pass each instance of the given class.\n" + "@note This function is only available in debug builds and primarily meant as an aid in debugging." + "@ingroup Console") +{ + sEnumCallbackFunction = functionName; + ConsoleObject::debugEnumInstances( className, sEnumCallback ); +} + +#endif + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, assignFieldsFrom, void, ( SimObject* fromObject ),, + "Copy fields from another object onto this one. The objects must " + "be of same type. Everything from the object will overwrite what's " + "in this object; extra fields in this object will remain. This " + "includes dynamic fields.\n" + "@param fromObject The object from which to copy fields." ) +{ + if( fromObject ) + object->assignFieldsFrom( fromObject ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimObject, assignPersistentId, void, (),, + "Assign a persistent ID to the object if it does not already have one." ) +{ + object->getOrCreatePersistentId(); +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, getCanSave, bool, (),, + "Get whether the object will be included in saves.\n" + "@return True if the object will be saved; false otherwise." ) +{ + return object->getCanSave(); +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, setCanSave, void, ( bool value ), ( true ), + "Set whether the object will be included in saves.\n" + "@param value If true, the object will be included in saves; if false, it will be excluded." ) +{ + object->setCanSave( value ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimObject, isEditorOnly, bool, (),, + "Return true if the object is only used by the editor.\n" + "@return True if this object exists only for the sake of editing." ) +{ + return object->isEditorOnly(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimObject, setEditorOnly, void, ( bool value ), ( true ), + "Set/clear the editor-only flag on this object.\n" + "@param value If true, the object is marked as existing only for the editor." ) +{ + object->setEditorOnly( value ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimObject, isNameChangeAllowed, bool, (),, + "Get whether this object may be renamed.\n" + "@return True if this object can be renamed; false otherwise." ) +{ + return object->isNameChangeAllowed(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimObject, setNameChangeAllowed, void, ( bool value ), ( true ), + "Set whether this object can be renamed from its first name.\n" + "@param value If true, renaming is allowed for this object; if false, trying to change the name of the object will generate a console error." ) +{ + object->setNameChangeAllowed( value ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimObject, clone, SimObject*, (),, + "Create a copy of this object.\n" + "@return An exact duplicate of this object." ) +{ + return object->clone(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimObject, deepClone, SimObject*, (),, + "Create a copy of this object and all its subobjects.\n" + "@return An exact duplicate of this object and all objects it references." ) +{ + return object->deepClone(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimObject, setLocked, void, ( bool value ), ( true ), + "Lock/unlock the object in the editor.\n" + "@param value If true, the object will be locked; if false, the object will be unlocked." ) +{ + object->setLocked( value ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimObject, setHidden, void, ( bool value ), ( true ), + "Hide/unhide the object.\n" + "@param value If true, the object will be hidden; if false, the object will be unhidden." ) +{ + object->setHidden( value ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, dumpMethods, ArrayObject*, (),, + "List the methods defined on this object.\n\n" + "Each description is a newline-separated vector with the following elements:\n" + "- Minimum number of arguments.\n" + "- Maximum number of arguments.\n" + "- Prototype string.\n" + "- Full script file path (if script method).\n" + "- Line number of method definition in script (if script method).\n\n" + "- Documentation string (not including prototype). This takes up the remainder of the vector.\n" + "@return An ArrayObject populated with (name,description) pairs of all methods defined on the object." ) +{ + Namespace *ns = object->getNamespace(); + if( !ns ) + return 0; + + ArrayObject* dictionary = new ArrayObject(); + dictionary->registerObject(); + + VectorPtr vec(__FILE__, __LINE__); + ns->getEntryList(&vec); + + for(Vector< Namespace::Entry* >::iterator j = vec.begin(); j != vec.end(); j++) + { + Namespace::Entry* e = *j; + + if( e->mType < 0 ) + continue; + + StringBuilder str; + + str.append( String::ToString( e->mMinArgs ) ); + str.append( '\n' ); + str.append( String::ToString( e->mMaxArgs ) ); + str.append( '\n' ); + str.append( e->getPrototypeString() ); + + str.append( '\n' ); + if( e->mCode && e->mCode->fullPath ) + str.append( e->mCode->fullPath ); + str.append( '\n' ); + if( e->mCode ) + str.append( String::ToString( e->mFunctionLineNumber ) ); + + str.append( '\n' ); + String docs = e->getDocString(); + if( !docs.isEmpty() ) + str.append( docs ); + + dictionary->push_back( e->mFunctionName, str.end() ); + } + + return dictionary; +} + +//----------------------------------------------------------------------------- + +namespace { + S32 QSORT_CALLBACK compareFields( const void* a,const void* b ) + { + const AbstractClassRep::Field* fa = *((const AbstractClassRep::Field**)a); + const AbstractClassRep::Field* fb = *((const AbstractClassRep::Field**)b); + + return dStricmp(fa->pFieldname, fb->pFieldname); + } + + struct DocString + { + char mPadding[ 8 ]; + String mPrototype; + String mDescription; + const char* mReturnType; + + DocString( Namespace::Entry* entry ) + : mPrototype( entry->getArgumentsString() ), + mDescription( entry->getBriefDescription() ) + { + mReturnType = " "; + mPadding[ 0 ] = 0; + if( entry->mType == -4 ) + { + //TODO: need to have script callbacks set up proper return type info + } + else + { + switch( entry->mType ) + { + case Namespace::Entry::StringCallbackType: + mReturnType = "string"; + mPadding[ 0 ] = ' '; + mPadding[ 1 ] = ' '; + mPadding[ 2 ] = 0; + break; + + case Namespace::Entry::IntCallbackType: + mReturnType = "int"; + mPadding[ 0 ] = ' '; + mPadding[ 1 ] = ' '; + mPadding[ 2 ] = ' '; + mPadding[ 3 ] = ' '; + mPadding[ 4 ] = ' '; + mPadding[ 5 ] = 0; + break; + + case Namespace::Entry::FloatCallbackType: + mReturnType = "float"; + mPadding[ 0 ] = ' '; + mPadding[ 1 ] = ' '; + mPadding[ 2 ] = ' '; + mPadding[ 3 ] = 0; + break; + + case Namespace::Entry::VoidCallbackType: + mReturnType = "void"; + mPadding[ 0 ] = ' '; + mPadding[ 1 ] = ' '; + mPadding[ 2 ] = ' '; + mPadding[ 3 ] = ' '; + mPadding[ 4 ] = 0; + break; + + case Namespace::Entry::BoolCallbackType: + mReturnType = "bool"; + mPadding[ 0 ] = ' '; + mPadding[ 1 ] = ' '; + mPadding[ 2 ] = ' '; + mPadding[ 3 ] = ' '; + mPadding[ 4 ] = 0; + break; + } + } + } + }; +} + +DefineEngineMethod( SimObject, dump, void, ( bool detailed ), ( false ), + "Dump a description of all fields and methods defined on this object to the console.\n" + "@param detailed Whether to print detailed information about members." ) +{ + Con::printf( "Class: %s", object->getClassName() ); + + const AbstractClassRep::FieldList &list = object->getFieldList(); + char expandedBuffer[4096]; + + Con::printf( "Static Fields:" ); + Vector flist(__FILE__, __LINE__); + + for(U32 i = 0; i < list.size(); i++) + flist.push_back(&list[i]); + + dQsort(flist.address(),flist.size(),sizeof(AbstractClassRep::Field *),compareFields); + + for(Vector::iterator itr = flist.begin(); itr != flist.end(); itr++) + { + const AbstractClassRep::Field* f = *itr; + + // The special field types can be skipped. + if ( f->type >= AbstractClassRep::ARCFirstCustomField ) + continue; + + for(U32 j = 0; S32(j) < f->elementCount; j++) + { + // [neo, 07/05/2007 - #3000] + // Some objects use dummy vars and projected fields so make sure we call the get functions + //const char *val = Con::getData(f->type, (void *) (((const char *)object) + f->offset), j, f->table, f->flag); + const char *val = (*f->getDataFn)( object, Con::getData(f->type, (void *) (((const char *)object) + f->offset), j, f->table, f->flag) );// + typeSizes[fld.type] * array1)); + + ConsoleBaseType* conType = ConsoleBaseType::getType( f->type ); + const char* conTypeName = ""; + if( conType ) + conTypeName = conType->getTypeClassName(); + + if( !val /*|| !*val*/ ) + continue; + if( f->elementCount == 1 ) + dSprintf( expandedBuffer, sizeof( expandedBuffer ), " %s %s = \"", conTypeName, f->pFieldname ); + else + dSprintf( expandedBuffer, sizeof( expandedBuffer ), " %s %s[ %d ] = \"", conTypeName, f->pFieldname, j ); + expandEscape( expandedBuffer + dStrlen(expandedBuffer), val); + Con::printf( "%s\"", expandedBuffer ); + + if( detailed && f->pFieldDocs && f->pFieldDocs[ 0 ] ) + Con::printf( " %s", f->pFieldDocs ); + } + } + + Con::printf( "Dynamic Fields:" ); + if(object->getFieldDictionary()) + object->getFieldDictionary()->printFields(object); + + Con::printf( "Methods:" ); + Namespace *ns = object->getNamespace(); + VectorPtr vec(__FILE__, __LINE__); + + if(ns) + ns->getEntryList(&vec); + + bool sawCBs = false; + + for(Vector::iterator j = vec.begin(); j != vec.end(); j++) + { + Namespace::Entry *e = *j; + + if(e->mType == Namespace::Entry::ScriptCallbackType) + sawCBs = true; + + if(e->mType < 0) + continue; + + DocString doc( e ); + Con::printf( " %s%s%s%s", doc.mReturnType, doc.mPadding, e->mFunctionName, doc.mPrototype.c_str() ); + + if( detailed && !doc.mDescription.isEmpty() ) + Con::printf( " %s", doc.mDescription.c_str() ); + } + + if( sawCBs ) + { + Con::printf( "Callbacks:" ); + + for(Vector::iterator j = vec.begin(); j != vec.end(); j++) + { + Namespace::Entry *e = *j; + + if(e->mType != Namespace::Entry::ScriptCallbackType) + continue; + + DocString doc( e ); + Con::printf( " %s%s%s%s", doc.mReturnType, doc.mPadding, e->cb.mCallbackName, doc.mPrototype.c_str() ); + + if( detailed && !doc.mDescription.isEmpty() ) + Con::printf( " %s", doc.mDescription.c_str() ); + } + } +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, save, bool, ( const char* fileName, bool selectedOnly, const char* preAppendString ), ( false, "" ), + "Save out the object to the given file.\n" + "@param fileName The name of the file to save to." + "@param selectedOnly If true, only objects marked as selected will be saved out.\n" + "@param preAppendString Text which will be preprended directly to the object serialization.\n" + "@param True on success, false on failure." ) +{ + return object->save( fileName, selectedOnly, preAppendString ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimObject, setName, void, ( const char* newName ),, + "Set the global name of the object.\n" + "@param newName The new global name to assign to the object.\n" + "@note If name changing is disallowed on the object, the method will fail with a console error." ) +{ + object->assignName( newName ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimObject, getName, const char*, (),, + "Get the global name of the object.\n" + "@return The global name assigned to the object." ) +{ + const char *ret = object->getName(); + return ret ? ret : ""; +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, getClassName, const char*, (),, + "Get the name of the C++ class which the object is an instance of.\n" + "@return The name of the C++ class of the object." ) +{ + const char *ret = object->getClassName(); + return ret ? ret : ""; +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, isField, bool, ( const char* fieldName ),, + "Test whether the given field is defined on this object.\n" + "@param fieldName The name of the field.\n" + "@return True if the object implements the given field." ) +{ + return object->isField( fieldName ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, getFieldValue, const char*, ( const char* fieldName, S32 index ), ( -1 ), + "Return the value of the given field on this object.\n" + "@param fieldName The name of the field. If it includes a field index, the index is parsed out.\n" + "@param index Optional parameter to specify the index of an array field separately.\n" + "@return The value of the given field or \"\" if undefined." ) +{ + char fieldNameBuffer[ 1024 ]; + char arrayIndexBuffer[ 64 ]; + + // Parse out index if the field is given in the form of 'name[index]'. + + const char* arrayIndex = NULL; + const U32 nameLen = dStrlen( fieldName ); + if( fieldName[ nameLen - 1 ] == ']' ) + { + const char* leftBracket = dStrchr( fieldName, '[' ); + const char* rightBracket = &fieldName[ nameLen - 1 ]; + + const U32 fieldNameLen = getMin( U32( leftBracket - fieldName ), sizeof( fieldNameBuffer ) - 1 ); + const U32 arrayIndexLen = getMin( U32( rightBracket - leftBracket - 1 ), sizeof( arrayIndexBuffer ) - 1 ); + + dMemcpy( fieldNameBuffer, fieldName, fieldNameLen ); + dMemcpy( arrayIndexBuffer, leftBracket + 1, arrayIndexLen ); + + fieldNameBuffer[ fieldNameLen ] = '\0'; + arrayIndexBuffer[ arrayIndexLen ] = '\0'; + + fieldName = fieldNameBuffer; + arrayIndex = arrayIndexBuffer; + } + + fieldName = StringTable->insert( fieldName ); + + if( index != -1 ) + { + dSprintf( arrayIndexBuffer, sizeof( arrayIndexBuffer ), "%i", index ); + arrayIndex = arrayIndexBuffer; + } + + return object->getDataField( fieldName, arrayIndex ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, setFieldValue, bool, ( const char* fieldName, const char* value, S32 index ), ( -1 ), + "Set the value of the given field on this object.\n" + "@param fieldName The name of the field to assign to. If it includes an array index, the index will be parsed out.\n" + "@param value The new value to assign to the field.\n" + "@param index Optional argument to specify an index for an array field.\n" + "@return True." ) +{ + char fieldNameBuffer[ 1024 ]; + char arrayIndexBuffer[ 64 ]; + + // Parse out index if the field is given in the form of 'name[index]'. + + const char* arrayIndex = NULL; + const U32 nameLen = dStrlen( fieldName ); + if( fieldName[ nameLen - 1 ] == ']' ) + { + const char* leftBracket = dStrchr( fieldName, '[' ); + const char* rightBracket = &fieldName[ nameLen - 1 ]; + + const U32 fieldNameLen = getMin( U32( leftBracket - fieldName ), sizeof( fieldNameBuffer ) - 1 ); + const U32 arrayIndexLen = getMin( U32( rightBracket - leftBracket - 1 ), sizeof( arrayIndexBuffer ) - 1 ); + + dMemcpy( fieldNameBuffer, fieldName, fieldNameLen ); + dMemcpy( arrayIndexBuffer, leftBracket + 1, arrayIndexLen ); + + fieldNameBuffer[ fieldNameLen ] = '\0'; + arrayIndexBuffer[ arrayIndexLen ] = '\0'; + + fieldName = fieldNameBuffer; + arrayIndex = arrayIndexBuffer; + } + + fieldName = StringTable->insert( fieldName ); + + if( index != -1 ) + { + dSprintf( arrayIndexBuffer, sizeof( arrayIndexBuffer ), "%i", index ); + arrayIndex = arrayIndexBuffer; + } + + object->setDataField( fieldName, arrayIndex, value ); + + return true; +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, getFieldType, const char*, ( const char* fieldName ),, + "Get the console type code of the given field.\n" + "@return The numeric type code for the underlying console type of the given field." ) +{ + U32 typeID = object->getDataFieldType( StringTable->insert( fieldName ), NULL ); + ConsoleBaseType* type = ConsoleBaseType::getType( typeID ); + + if( type ) + return type->getTypeName(); + + return ""; +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, setFieldType, void, ( const char* fieldName, const char* type ),, + "Set the console type code for the given field.\n" + "@param fieldName The name of the dynamic field to change to type for.\n" + "@param type The name of the console type.\n" + "@note This only works for dynamic fields. Types of static fields cannot be changed." ) +{ + object->setDataFieldType( type, StringTable->insert( fieldName ), NULL ); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( SimObject, call, const char*, 3, 0, "( string method, string args... ) Dynamically call a method on an object.\n" + "@param method Name of method to call.\n" + "@param args Zero or more arguments for the method.\n" + "@return The result of the method call." ) +{ + argv[1] = argv[2]; + return Con::execute( object, argc - 1, argv + 1 ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimObject, setInternalName, void, ( const char* newInternalName ),, + "Set the internal name of the object.\n" + "@param newInternalName The new internal name for the object." ) +{ + object->setInternalName( newInternalName ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimObject, getInternalName, const char*, (),, + "Get the internal name of the object.\n" + "@return The internal name of the object." ) +{ + return object->getInternalName(); +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, dumpClassHierarchy, void, (),, + "Dump the native C++ class hierarchy of this object's C++ class to the console." ) +{ + object->dumpClassHierarchy(); +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, isMemberOfClass, bool, ( const char* className ),, + "Test whether this object is a member of the specified class.\n" + "@param className Name of a native C++ class.\n" + "@return True if this object is an instance of the given C++ class or any of its super classes." ) +{ + AbstractClassRep* pRep = object->getClassRep(); + while(pRep) + { + if( !dStricmp(pRep->getClassName(), className ) ) + { + //matches + return true; + } + + pRep = pRep->getParentClass(); + } + + return false; +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, isInNamespaceHierarchy, bool, ( const char* name ),, + "Test whether the namespace of this object is a direct or indirect child to the given namespace.\n" + "@param name The name of a namespace.\n" + "@return True if the given namespace name is within the namespace hierarchy of this object." ) +{ + Namespace* nspace = object->getNamespace(); + + while( nspace && dStricmp( nspace->mName, name ) != 0 ) + nspace = nspace->mParent; + + return ( nspace != NULL ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimObject, getId, S32, (),, + "Get the underlying unique numeric ID of the object.\n" + "@note Object IDs are unique only during single engine runs.\n" + "@return The unique numeric ID of the object." ) +{ + return object->getId(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimObject, getGroup, SimGroup*, (),, + "Get the group that this object is contained in.\n" + "@note If not assigned to particular SimGroup, an object belongs to RootGroup.\n" + "@return The SimGroup object to which the object belongs." ) +{ + return object->getGroup(); +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, delete, void, (),, + "Delete and remove the object." ) +{ + object->deleteObject(); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( SimObject,schedule, S32, 4, 0, "( float time, string method, string args... ) Delay an invocation of a method.\n" + "@param time The number of milliseconds after which to invoke the method. This is a soft limit.\n" + "@param method The method to call.\n" + "@param args The arguments with which to call the method.\n" + "@return The numeric ID of the created schedule. Can be used to cancel the call.\n" ) +{ + U32 timeDelta = U32(dAtof(argv[2])); + argv[2] = argv[3]; + argv[3] = argv[1]; + SimConsoleEvent *evt = new SimConsoleEvent(argc - 2, argv + 2, true); + S32 ret = Sim::postEvent(object, evt, Sim::getCurrentTime() + timeDelta); + // #ifdef DEBUG + // Con::printf("obj %s schedule(%s) = %d", argv[3], argv[2], ret); + // Con::executef( "backtrace"); + // #endif + return ret; +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, getDynamicFieldCount, S32, (),, + "Get the number of dynamic fields defined on the object.\n" + "@return The number of dynamic fields defined on the object." ) +{ + S32 count = 0; + SimFieldDictionary* fieldDictionary = object->getFieldDictionary(); + for (SimFieldDictionaryIterator itr(fieldDictionary); *itr; ++itr) + count++; + + return count; +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, getDynamicField, const char*, ( int index ),, + "Get a value of a dynamic field by index.\n" + "@param index The index of the dynamic field.\n" + "@return The value of the dynamic field at the given index or \"\"." ) +{ + SimFieldDictionary* fieldDictionary = object->getFieldDictionary(); + SimFieldDictionaryIterator itr(fieldDictionary); + for (S32 i = 0; i < index; i++) + { + if (!(*itr)) + { + Con::warnf("Invalid dynamic field index passed to SimObject::getDynamicField!"); + return NULL; + } + ++itr; + } + + char* buffer = Con::getReturnBuffer(256); + if (*itr) + { + SimFieldDictionary::Entry* entry = *itr; + dSprintf(buffer, 256, "%s\t%s", entry->slotName, entry->value); + return buffer; + } + + Con::warnf("Invalid dynamic field index passed to SimObject::getDynamicField!"); + return NULL; +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, getFieldCount, S32, (),, + "Get the number of static fields on the object.\n" + "@return The number of static fields defined on the object." ) +{ + const AbstractClassRep::FieldList &list = object->getFieldList(); + const AbstractClassRep::Field* f; + U32 numDummyEntries = 0; + + for(int i = 0; i < list.size(); i++) + { + f = &list[i]; + + // The special field types do not need to be counted. + if ( f->type >= AbstractClassRep::ARCFirstCustomField ) + numDummyEntries++; + } + + return list.size() - numDummyEntries; +} + +//----------------------------------------------------------------------------- + +DefineConsoleMethod( SimObject, getField, const char*, ( int index ),, + "Retrieve the value of a static field by index.\n" + "@param index The index of the static field.\n" + "@return The value of the static field with the given index or \"\"." ) +{ + const AbstractClassRep::FieldList &list = object->getFieldList(); + if( ( index < 0 ) || ( index >= list.size() ) ) + return ""; + + const AbstractClassRep::Field* f; + S32 currentField = 0; + for ( U32 i = 0; i < list.size() && currentField <= index; i++ ) + { + f = &list[i]; + + // The special field types can be skipped. + if ( f->type >= AbstractClassRep::ARCFirstCustomField ) + continue; + + if(currentField == index) + return f->pFieldname; + + currentField++; + } + + // if we found nada, return nada. + return ""; +} + +//----------------------------------------------------------------------------- + +#ifdef TORQUE_DEBUG + +DefineEngineMethod( SimObject, getDebugInfo, ArrayObject*, (),, + "Return some behind-the-scenes information on the object.\n" + "@return An ArrayObject filled with internal information about the object." ) +{ + ArrayObject* array = new ArrayObject(); + array->registerObject(); + + array->push_back( "C++|Address", String::ToString( "0x%x", object ) ); + array->push_back( "C++|Size", String::ToString( object->getClassRep()->getSizeof() ) ); + array->push_back( "Object|Description", object->describeSelf() ); + array->push_back( "Object|FileName", object->getFilename() ); + array->push_back( "Object|DeclarationLine", String::ToString( object->getDeclarationLine() ) ); + array->push_back( "Object|CopySource", object->getCopySource() ? + String::ToString( "%i:%s (%s)", object->getCopySource()->getId(), object->getCopySource()->getClassName(), object->getCopySource()->getName() ) : "" ); + array->push_back( "Flag|EditorOnly", object->isEditorOnly() ? "true" : "false" ); + array->push_back( "Flag|NameChangeAllowed", object->isNameChangeAllowed() ? "true" : "false" ); + array->push_back( "Flag|AutoDelete", object->isAutoDeleted() ? "true" : "false" ); + array->push_back( "Flag|Selected", object->isSelected() ? "true" : "false" ); + array->push_back( "Flag|Expanded", object->isExpanded() ? "true" : "false" ); + array->push_back( "Flag|ModStaticFields", object->canModStaticFields() ? "true" : "false" ); + array->push_back( "Flag|ModDynamicFields", object->canModDynamicFields() ? "true" : "false" ); + array->push_back( "Flag|CanSave", object->getCanSave() ? "true" : "false" ); + + #ifndef TORQUE_DISABLE_MEMORY_MANAGER + Memory::Info memInfo; + Memory::getMemoryInfo( object, memInfo ); + + array->push_back( "Memory|AllocNumber", String::ToString( memInfo.mAllocNumber ) ); + array->push_back( "Memory|AllocSize", String::ToString( memInfo.mAllocSize ) ); + array->push_back( "Memory|AllocFile", memInfo.mFileName ); + array->push_back( "Memory|AllocLine", String::ToString( memInfo.mLineNumber ) ); + array->push_back( "Memory|IsGlobal", memInfo.mIsGlobal ? "true" : "false" ); + array->push_back( "Memory|IsStatic", memInfo.mIsStatic ? "true" : "false" ); + #endif + + return array; +} + +#endif diff --git a/Engine/source/console/simObject.h b/Engine/source/console/simObject.h new file mode 100644 index 000000000..541c15bbc --- /dev/null +++ b/Engine/source/console/simObject.h @@ -0,0 +1,1006 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SIMOBJECT_H_ +#define _SIMOBJECT_H_ + +#ifndef _SIM_H_ + #include "console/sim.h" +#endif +#ifndef _CONSOLEOBJECT_H_ + #include "console/consoleObject.h" +#endif +#ifndef _BITSET_H_ + #include "core/bitSet.h" +#endif + + +class Stream; +class LightManager; +class SimFieldDictionary; +class SimPersistID; + + +/// Base class for objects involved in the simulation. +/// +/// @section simobject_intro Introduction +/// +/// SimObject is a base class for most of the classes you'll encounter +/// working in Torque. It provides fundamental services allowing "smart" +/// object referencing, creation, destruction, organization, and location. +/// Along with SimEvent, it gives you a flexible event-scheduling system, +/// as well as laying the foundation for the in-game editors, GUI system, +/// and other vital subsystems. +/// +/// @section simobject_subclassing Subclassing +/// +/// You will spend a lot of your time in Torque subclassing, or working +/// with subclasses of, SimObject. SimObject is designed to be easy to +/// subclass. +/// +/// You should not need to override anything in a subclass except: +/// - The constructor/destructor. +/// - processArguments() +/// - onAdd()/onRemove() +/// - onGroupAdd()/onGroupRemove() +/// - onNameChange() +/// - onStaticModified() +/// - onDeleteNotify() +/// - onEditorEnable()/onEditorDisable() +/// - inspectPreApply()/inspectPostApply() +/// - things from ConsoleObject (see ConsoleObject docs for specifics) +/// +/// Of course, if you know what you're doing, go nuts! But in most cases, you +/// shouldn't need to touch things not on that list. +/// +/// When you subclass, you should define a typedef in the class, called Parent, +/// that references the class you're inheriting from. +/// +/// @code +/// class mySubClass : public SimObject { +/// typedef SimObject Parent; +/// ... +/// @endcode +/// +/// Then, when you override a method, put in: +/// +/// @code +/// bool mySubClass::onAdd() +/// { +/// if(!Parent::onAdd()) +/// return false; +/// +/// // ... do other things ... +/// } +/// @endcode +/// +/// Of course, you want to replace onAdd with the appropriate method call. +/// +/// @section simobject_lifecycle A SimObject's Life Cycle +/// +/// SimObjects do not live apart. One of the primary benefits of using a +/// SimObject is that you can uniquely identify it and easily find it (using +/// its ID). Torque does this by keeping a global hierarchy of SimGroups - +/// a tree - containing every registered SimObject. You can then query +/// for a given object using Sim::findObject() (or SimSet::findObject() if +/// you want to search only a specific set). +/// +/// @code +/// // Three examples of registering an object. +/// +/// // Method 1: +/// AIClient *aiPlayer = new AIClient(); +/// aiPlayer->registerObject(); +/// +/// // Method 2: +/// ActionMap* globalMap = new ActionMap; +/// globalMap->registerObject("GlobalActionMap"); +/// +/// // Method 3: +/// bool reg = mObj->registerObject(id); +/// @endcode +/// +/// Registering a SimObject performs these tasks: +/// - Marks the object as not cleared and not removed. +/// - Assigns the object a unique SimObjectID if it does not have one already. +/// - Adds the object to the global name and ID dictionaries so it can be found +/// again. +/// - Calls the object's onAdd() method. Note: SimObject::onAdd() performs +/// some important initialization steps. See @ref simobject_subclassing "here +/// for details" on how to properly subclass SimObject. +/// - If onAdd() fails (returns false), it calls unregisterObject(). +/// - Checks to make sure that the SimObject was properly initialized (and asserts +/// if not). +/// +/// Calling registerObject() and passing an ID or a name will cause the object to be +/// assigned that name and/or ID before it is registered. +/// +/// Congratulations, you have now registered your object! What now? +/// +/// Well, hopefully, the SimObject will have a long, useful life. But eventually, +/// it must die. +/// +/// There are a two ways a SimObject can die. +/// - First, the game can be shut down. This causes the root SimGroup +/// to be unregistered and deleted. When a SimGroup is unregistered, +/// it unregisters all of its member SimObjects; this results in everything +/// that has been registered with Sim being unregistered, as everything +/// registered with Sim is in the root group. +/// - Second, you can manually kill it off, either by calling unregisterObject() +/// or by calling deleteObject(). +/// +/// When you unregister a SimObject, the following tasks are performed: +/// - The object is flagged as removed. +/// - Notifications are cleaned up. +/// - If the object is in a group, then it removes itself from the group. +/// - Delete notifications are sent out. +/// - Finally, the object removes itself from the Sim globals, and tells +/// Sim to get rid of any pending events for it. +/// +/// If you call deleteObject(), all of the above tasks are performed, in addition +/// to some sanity checking to make sure the object was previously added properly, +/// and isn't in the process of being deleted. After the object is unregistered, it +/// deallocates itself. +/// +/// @section simobject_editor Torque Editors +/// +/// SimObjects are one of the building blocks for the in-game editors. They +/// provide a basic interface for the editor to be able to list the fields +/// of the object, update them safely and reliably, and inform the object +/// things have changed. +/// +/// This interface is implemented in the following areas: +/// - onNameChange() is called when the object is renamed. +/// - onStaticModified() is called whenever a static field is modified. +/// - inspectPreApply() is called before the object's fields are updated, +/// when changes are being applied. +/// - inspectPostApply() is called after the object's fields are updated. +/// - onEditorEnable() is called whenever an editor is enabled (for instance, +/// when you hit F11 to bring up the world editor). +/// - onEditorDisable() is called whenever the editor is disabled (for instance, +/// when you hit F11 again to close the world editor). +/// +/// (Note: you can check the variable gEditingMission to see if the mission editor +/// is running; if so, you may want to render special indicators. For instance, the +/// fxFoliageReplicator renders inner and outer radii when the mission editor is +/// runnning.) +/// +/// @section simobject_console The Console +/// +/// SimObject extends ConsoleObject by allowing you to +/// to set arbitrary dynamic fields on the object, as well as +/// statically defined fields. This is done through two methods, +/// setDataField and getDataField, which deal with the complexities of +/// allowing access to two different types of object fields. +/// +/// Static fields take priority over dynamic fields. This is to be +/// expected, as the role of dynamic fields is to allow data to be +/// stored in addition to the predefined fields. +/// +/// The fields in a SimObject are like properties (or fields) in a class. +/// +/// Some fields may be arrays, which is what the array parameter is for; if it's non-null, +/// then it is parsed with dAtoI and used as an index into the array. If you access something +/// as an array which isn't, then you get an empty string. +/// +/// You don't need to read any further than this. Right now, +/// set/getDataField are called a total of 6 times through the entire +/// Torque codebase. Therefore, you probably don't need to be familiar +/// with the details of accessing them. You may want to look at Con::setData +/// instead. Most of the time you will probably be accessing fields directly, +/// or using the scripting language, which in either case means you don't +/// need to do anything special. +/// +/// The functions to get/set these fields are very straightforward: +/// +/// @code +/// setDataField(StringTable->insert("locked", false), NULL, b ? "true" : "false" ); +/// curObject->setDataField(curField, curFieldArray, STR.getStringValue()); +/// setDataField(slotName, array, value); +/// @endcode +/// +/// For advanced users: There are two flags which control the behavior +/// of these functions. The first is ModStaticFields, which controls whether +/// or not the DataField functions look through the static fields (defined +/// with addField; see ConsoleObject for details) of the class. The second +/// is ModDynamicFields, which controls dynamically defined fields. They are +/// set automatically by the console constructor code. +/// +/// @nosubgrouping +class SimObject: public ConsoleObject +{ + public: + + typedef ConsoleObject Parent; + + friend class SimManager; + friend class SimGroup; + friend class SimNameDictionary; + friend class SimManagerNameDictionary; + friend class SimIdDictionary; + + /// @name Notification + /// @{ + + struct Notify + { + enum Type + { + ClearNotify, ///< Notified when the object is cleared. + DeleteNotify, ///< Notified when the object is deleted. + ObjectRef, ///< Cleverness to allow tracking of references. + Invalid ///< Mark this notification as unused (used in freeNotify). + } type; + + void *ptr; ///< Data (typically referencing or interested object). + Notify *next; ///< Next notification in the linked list. + }; + + /// @} + + /// Flags passed to SimObject::write + enum WriteFlags + { + SelectedOnly = BIT( 0 ), ///< Indicates that only objects marked as selected should be outputted. Used in SimSet. + NoName = BIT( 1 ), ///< Indicates that the object name should not be saved. + IgnoreCanSave = BIT( 2 ), ///< Write out even if CannotSave=true. + }; + + private: + + /// Flags for use in mFlags + enum + { + Deleted = BIT( 0 ), ///< This object is marked for deletion. + Removed = BIT( 1 ), ///< This object has been unregistered from the object system. + Added = BIT( 3 ), ///< This object has been registered with the object system. + Selected = BIT( 4 ), ///< This object has been marked as selected. (in editor) + Expanded = BIT( 5 ), ///< This object has been marked as expanded. (in editor) + ModStaticFields = BIT( 6 ), ///< The object allows you to read/modify static fields + ModDynamicFields = BIT( 7 ), ///< The object allows you to read/modify dynamic fields + AutoDelete = BIT( 8 ), ///< Delete this object when the last ObjectRef is gone. + CannotSave = BIT( 9 ), ///< Object should not be saved. + EditorOnly = BIT( 10 ), ///< This object is for use by the editor only. + NoNameChange = BIT( 11 ), ///< Whether changing the name of this object is allowed. + Hidden = BIT( 12 ), ///< Object is hidden in editors. + Locked = BIT( 13 ), ///< Object is locked in editors. + }; + + // dictionary information stored on the object + StringTableEntry objectName; + StringTableEntry mOriginalName; + SimObject* nextNameObject; + SimObject* nextManagerNameObject; + SimObject* nextIdObject; + + /// SimGroup we're contained in, if any. + SimGroup* mGroup; + + /// Flags internal to the object management system. + BitSet32 mFlags; + + /// Object we are copying fields from. + SimObject* mCopySource; + + /// Table of dynamic fields assigned to this object. + SimFieldDictionary *mFieldDictionary; + + /// Buffer to store textual representation of this object's numeric ID in. + char mIdString[ 11 ]; + + /// @name Serialization + /// @{ + + /// Path to file this SimObject was loaded from. + StringTableEntry mFilename; + + /// The line number that the object was declared on if it was loaded from a file. + S32 mDeclarationLine; + + /// @} + + /// @name Notification + /// @{ + + /// List of notifications added to this object. + Notify* mNotifyList; + + static SimObject::Notify *mNotifyFreeList; + static SimObject::Notify *allocNotify(); ///< Get a free Notify structure. + static void freeNotify(SimObject::Notify*); ///< Mark a Notify structure as free. + + /// @} + + static bool _setCanSave( void* object, const char* index, const char* data ); + static const char* _getCanSave( void* object, const char* data ); + + static const char* _getHidden( void* object, const char* data ) + { if( static_cast< SimObject* >( object )->isHidden() ) return "1"; return "0"; } + static const char* _getLocked( void* object, const char* data ) + { if( static_cast< SimObject* >( object )->isLocked() ) return "1"; return "0"; } + static bool _setHidden( void* object, const char* index, const char* data ) + { static_cast< SimObject* >( object )->setHidden( dAtob( data ) ); return false; } + static bool _setLocked( void* object, const char* index, const char* data ) + { static_cast< SimObject* >( object )->setLocked( dAtob( data ) ); return false; } + + // Namespace protected set methods + static bool setClass( void *object, const char *index, const char *data ) + { static_cast(object)->setClassNamespace(data); return false; }; + static bool setSuperClass(void *object, const char *index, const char *data) + { static_cast(object)->setSuperClassNamespace(data); return false; }; + + // Group hierarchy protected set method + static bool setProtectedParent(void *object, const char *index, const char *data); + + // Object name protected set method + static bool setProtectedName(void *object, const char *index, const char *data); + + protected: + + /// Id number for this object. + SimObjectId mId; + + /// Internal name assigned to the object. Not set by default. + StringTableEntry mInternalName; + + static bool smForceId; ///< Force a registered object to use the given Id. Cleared upon use. + static SimObjectId smForcedId; ///< The Id to force upon the object. Poor object. + + /// @name Serialization + /// @{ + + /// Whether dynamic fields should be saved out in serialization. Defaults to true. + bool mCanSaveFieldDictionary; + + /// @} + + /// @name Persistent IDs + /// @{ + + /// Persistent ID assigned to this object. Allows to unambiguously refer to this + /// object in serializations regardless of stream object ordering. + SimPersistID* mPersistentId; + + static bool _setPersistentID( void* object, const char* index, const char* data ); + + /// @} + + /// @name Namespace management + /// @{ + + /// The namespace in which method lookup for this object begins. + Namespace* mNameSpace; + + /// Name of namespace to use as class namespace. + StringTableEntry mClassName; + + /// Name of namespace to use as class super namespace. + StringTableEntry mSuperClassName; + + /// Perform namespace linking on this object. + void linkNamespaces(); + + /// Undo namespace linking on this object. + void unlinkNamespaces(); + + /// @} + + /// Called when the object is selected in the editor. + virtual void _onSelected() {} + + /// Called when the object is unselected in the editor. + virtual void _onUnselected() {} + + /// We can provide more detail, like object name and id. + virtual String _getLogMessage(const char* fmt, void* args) const; + + DEFINE_CREATE_METHOD + { + T* object = new T; + object->incRefCount(); + object->registerObject(); + return object; + } + + + // EngineObject. + virtual void _destroySelf(); + + public: + + /// @name Cloning + /// @{ + + /// Return a shallow copy of this object. + virtual SimObject* clone(); + + /// Return a deep copy of this object. + virtual SimObject* deepClone(); + + /// @} + + /// @name Accessors + /// @{ + + /// Get the value of a field on the object. + /// + /// See @ref simobject_console "here" for a detailed discussion of what this + /// function does. + /// + /// @param slotName Field to access. + /// @param array String containing index into array + /// (if field is an array); if NULL, it is ignored. + const char *getDataField(StringTableEntry slotName, const char *array); + + /// Set the value of a field on the object. + /// + /// See @ref simobject_console "here" for a detailed discussion of what this + /// function does. + /// + /// @param slotName Field to access. + /// @param array String containing index into array; if NULL, it is ignored. + /// @param value Value to store. + void setDataField(StringTableEntry slotName, const char *array, const char *value); + + /// Get the type of a field on the object. + /// + /// @param slotName Field to access. + /// @param array String containing index into array + /// (if field is an array); if NULL, it is ignored. + U32 getDataFieldType(StringTableEntry slotName, const char *array); + + /// Set the type of a *dynamic* field on the object. + /// + /// @param typeName/Id Console base type name/id to assign to a dynamic field. + /// @param slotName Field to access. + /// @param array String containing index into array + /// (if field is an array); if NULL, it is ignored. + void setDataFieldType(const U32 fieldTypeId, StringTableEntry slotName, const char *array); + void setDataFieldType(const char *typeName, StringTableEntry slotName, const char *array); + + /// Get reference to the dictionary containing dynamic fields. + /// + /// See @ref simobject_console "here" for a detailed discussion of what this + /// function does. + /// + /// This dictionary can be iterated over using a SimFieldDictionaryIterator. + SimFieldDictionary * getFieldDictionary() {return(mFieldDictionary);} + + // Component Information + inline virtual StringTableEntry getComponentName() { return StringTable->insert( getClassName() ); }; + + /// These functions support internal naming that is not namespace + /// bound for locating child controls in a generic way. + /// + /// Set the internal name of this control (Not linked to a namespace) + void setInternalName(const char* newname); + + /// Get the internal name of this control + StringTableEntry getInternalName() const { return mInternalName; } + + /// Set the original name of this control + void setOriginalName(const char* originalName); + + /// Get the original name of this control + StringTableEntry getOriginalName() const { return mOriginalName; } + + /// These functions allow you to set and access the filename + /// where this object was created. + /// + /// Set the filename + void setFilename(const char* file); + + /// Get the filename + StringTableEntry getFilename() const { return mFilename; } + + /// These functions are used to track the line number (1-based) + /// on which the object was created if it was loaded from script + /// + /// Set the declaration line number + void setDeclarationLine(U32 lineNumber); + + /// Get the declaration line number + S32 getDeclarationLine() const { return mDeclarationLine; } + + /// Save object as a TorqueScript File. + virtual bool save( const char* pcFilePath, bool bOnlySelected = false, const char *preappend = NULL ); + + /// Check if a method exists in the objects current namespace. + virtual bool isMethod( const char* methodName ); + + /// Return true if the field is defined on the object + virtual bool isField( const char* fieldName, bool includeStatic = true, bool includeDynamic = true ); + + /// @} + + /// @name Initialization + /// @{ + + /// + SimObject(); + + virtual ~SimObject(); + + virtual bool processArguments(S32 argc, const char **argv); ///< Process constructor options. (ie, new SimObject(1,2,3)) + + /// @} + + /// @name Events + /// @{ + + /// Called when the object is added to the sim. + virtual bool onAdd(); + + /// Called when the object is removed from the sim. + virtual void onRemove(); + + /// Called when the object is added to a SimGroup. + virtual void onGroupAdd(); + + /// Called when the object is removed from a SimGroup. + virtual void onGroupRemove(); + + /// Called when the object's name is changed. + virtual void onNameChange(const char *name); + + /// + /// Specifically, these are called by setDataField + /// when a static or dynamic field is modified, see + /// @ref simobject_console "the console details". + virtual void onStaticModified(const char* slotName, const char*newValue = NULL); ///< Called when a static field is modified. + virtual void onDynamicModified(const char* slotName, const char*newValue = NULL); ///< Called when a dynamic field is modified. + + /// Called before any property of the object is changed in the world editor. + /// + /// The calling order here is: + /// - inspectPreApply() + /// - ... + /// - calls to setDataField() + /// - ... + /// - inspectPostApply() + virtual void inspectPreApply(); + + /// Called after any property of the object is changed in the world editor. + /// + /// @see inspectPreApply + virtual void inspectPostApply(); + + /// Called when a SimObject is deleted. + /// + /// When you are on the notification list for another object + /// and it is deleted, this method is called. + virtual void onDeleteNotify(SimObject *object); + + /// Called when the editor is activated. + virtual void onEditorEnable(){}; + + /// Called when the editor is deactivated. + virtual void onEditorDisable(){}; + + /// @} + + /// Find a named sub-object of this object. + /// + /// This is subclassed in the SimGroup and SimSet classes. + /// + /// For a single object, it just returns NULL, as normal objects cannot have children. + virtual SimObject *findObject(const char *name); + + /// @name Notification + /// @{ + + Notify *removeNotify(void *ptr, Notify::Type); ///< Remove a notification from the list. + void deleteNotify(SimObject* obj); ///< Notify an object when we are deleted. + void clearNotify(SimObject* obj); ///< Notify an object when we are cleared. + void clearAllNotifications(); ///< Remove all notifications for this object. + void processDeleteNotifies(); ///< Send out deletion notifications. + + /// Register a reference to this object. + /// + /// You pass a pointer to your reference to this object. + /// + /// When the object is deleted, it will null your + /// pointer, ensuring you don't access old memory. + /// + /// @param obj Pointer to your reference to the object. + void registerReference(SimObject **obj); + + /// Unregister a reference to this object. + /// + /// Remove a reference from the list, so that it won't + /// get nulled inappropriately. + /// + /// Call this when you're done with your reference to + /// the object, especially if you're going to free the + /// memory. Otherwise, you may erroneously get something + /// overwritten. + /// + /// @see registerReference + void unregisterReference(SimObject **obj); + + /// @} + + /// @name Registration + /// + /// SimObjects must be registered with the object system. + /// @{ + + /// Register an object with the object system. + /// + /// This must be called if you want to keep the object around. + /// In the rare case that you will delete the object immediately, or + /// don't want to be able to use Sim::findObject to locate it, then + /// you don't need to register it. + /// + /// registerObject adds the object to the global ID and name dictionaries, + /// after first assigning it a new ID number. It calls onAdd(). If onAdd fails, + /// it unregisters the object and returns false. + /// + /// If a subclass's onAdd doesn't eventually call SimObject::onAdd(), it will + /// cause an assertion. + bool registerObject(); + + /// Register the object, forcing the id. + /// + /// @see registerObject() + /// @param id ID to assign to the object. + bool registerObject(U32 id); + + /// Register the object, assigning the name. + /// + /// @see registerObject() + /// @param name Name to assign to the object. + bool registerObject(const char *name); + + /// Register the object, assigning a name and ID. + /// + /// @see registerObject() + /// @param name Name to assign to the object. + /// @param id ID to assign to the object. + bool registerObject(const char *name, U32 id); + + /// Unregister the object from Sim. + /// + /// This performs several operations: + /// - Sets the removed flag. + /// - Call onRemove() + /// - Clear out notifications. + /// - Remove the object from... + /// - its group, if any. (via getGroup) + /// - Sim::gNameDictionary + /// - Sim::gIDDictionary + /// - Finally, cancel any pending events for this object (as it can't receive them now). + void unregisterObject(); + + /// Unregister, mark as deleted, and free the object. + void deleteObject(); + + /// Performs a safe delayed delete of the object using a sim event. + void safeDeleteObject(); + + /// @} + + /// @name Accessors + /// @{ + + /// Return the unique numeric object ID. + SimObjectId getId() const { return mId; } + + /// Return the object ID as a string. + const char* getIdString() const { return mIdString; } + + /// Return the name of this object. + StringTableEntry getName() const { return objectName; } + + /// Return the SimGroup that this object is contained in. Never NULL except for + /// RootGroup and unregistered objects. + SimGroup* getGroup() const { return mGroup; } + + /// Assign the given name to this object. + void assignName( const char* name ); + + void setId(SimObjectId id); + static void setForcedId(SimObjectId id) { smForceId = true; smForcedId = id; } ///< Force an Id on the next registered object. + bool isChildOfGroup(SimGroup* pGroup); + bool isProperlyAdded() const { return mFlags.test(Added); } + bool isDeleted() const { return mFlags.test(Deleted); } + bool isRemoved() const { return mFlags.test(Deleted | Removed); } + + virtual bool isLocked() const { return mFlags.test( Locked ); } + virtual void setLocked( bool b ); + virtual bool isHidden() const { return mFlags.test( Hidden ); } + virtual void setHidden(bool b); + + /// @} + + /// @name Sets + /// + /// The object must be properly registered before you can add/remove it to/from a set. + /// + /// All these functions accept either a name or ID to identify the set you wish + /// to operate on. Then they call addObject or removeObject on the set, which + /// sets up appropriate notifications. + /// + /// An object may be in multiple sets at a time. + /// @{ + bool addToSet(SimObjectId); + bool addToSet(const char *); + bool removeFromSet(SimObjectId); + bool removeFromSet(const char *); + + /// @} + + /// @name Serialization + /// @{ + + /// Determine whether or not a field should be written. + /// + /// @param fiedname The name of the field being written. + /// @param value The value of the field. + virtual bool writeField(StringTableEntry fieldname, const char* value); + + /// Output the TorqueScript to recreate this object. + /// + /// This calls writeFields internally. + /// @param stream Stream to output to. + /// @param tabStop Indentation level for this object. + /// @param flags If SelectedOnly is passed here, then + /// only objects marked as selected (using setSelected) + /// will output themselves. + virtual void write(Stream &stream, U32 tabStop, U32 flags = 0); + + /// Write the fields of this object in TorqueScript. + /// + /// @param stream Stream for output. + /// @param tabStop Indentation level for the fields. + virtual void writeFields(Stream &stream, U32 tabStop); + + virtual bool writeObject(Stream *stream); + virtual bool readObject(Stream *stream); + + /// Set whether fields created at runtime should be saved. Default is true. + void setCanSaveDynamicFields( bool bCanSave ) { mCanSaveFieldDictionary = bCanSave; } + + /// Get whether fields created at runtime should be saved. Default is true. + bool getCanSaveDynamicFields( bool bCanSave ) { return mCanSaveFieldDictionary;} + + /// Return the object that this object is copying fields from. + SimObject* getCopySource() const { return mCopySource; } + + /// Set the object that this object should be copying fields from. + void setCopySource( SimObject* object ); + + /// Copy fields from another object onto this one. + /// + /// Objects must be of same type. Everything from obj + /// will overwrite what's in this object; extra fields + /// in this object will remain. This includes dynamic + /// fields. + /// + /// @param obj Object to copy from. + void assignFieldsFrom(SimObject *obj); + + /// Copy dynamic fields from another object onto this one. + /// + /// Everything from obj will overwrite what's in this + /// object. + /// + /// @param obj Object to copy from. + void assignDynamicFieldsFrom(SimObject *obj); + + /// @} + + /// Return the object's namespace. + Namespace* getNamespace() { return mNameSpace; } + + /// Get next matching item in namespace. + /// + /// This wraps a call to Namespace::tabComplete; it gets the + /// next thing in the namespace, given a starting value + /// and a base length of the string. See + /// Namespace::tabComplete for details. + const char *tabComplete(const char *prevText, S32 baseLen, bool); + + /// @name Accessors + /// @{ + + bool isSelected() const { return mFlags.test(Selected); } + bool isExpanded() const { return mFlags.test(Expanded); } + bool isEditorOnly() const { return mFlags.test( EditorOnly ); } + bool isNameChangeAllowed() const { return !mFlags.test( NoNameChange ); } + bool isAutoDeleted() const { return mFlags.test( AutoDelete ); } + void setSelected(bool sel); + void setExpanded(bool exp) { if(exp) mFlags.set(Expanded); else mFlags.clear(Expanded); } + void setModDynamicFields(bool dyn) { if(dyn) mFlags.set(ModDynamicFields); else mFlags.clear(ModDynamicFields); } + void setModStaticFields(bool sta) { if(sta) mFlags.set(ModStaticFields); else mFlags.clear(ModStaticFields); } + bool canModDynamicFields() { return mFlags.test(ModDynamicFields); } + bool canModStaticFields() { return mFlags.test(ModStaticFields); } + void setAutoDelete( bool val ) { if( val ) mFlags.set( AutoDelete ); else mFlags.clear( AutoDelete ); } + void setEditorOnly( bool val ) { if( val ) mFlags.set( EditorOnly ); else mFlags.clear( EditorOnly ); } + void setNameChangeAllowed( bool val ) { if( val ) mFlags.clear( NoNameChange ); else mFlags.set( NoNameChange ); } + + /// Returns boolean specifying if the object can be serialized. + bool getCanSave() const { return !mFlags.test( CannotSave ); } + + /// Set serialization flag. + virtual void setCanSave( bool val ) { if( !val ) mFlags.set( CannotSave ); else mFlags.clear( CannotSave ); } + + /// Returns true if this object is selected or any group it is a member of is. + bool isSelectedRecursive() const; + + /// @} + + /// @name Namespace management + /// @{ + + /// Return name of class namespace set on this object. + StringTableEntry getClassNamespace() const { return mClassName; }; + + /// Return name of superclass namespace set on this object. + StringTableEntry getSuperClassNamespace() const { return mSuperClassName; }; + + /// + void setClassNamespace( const char* classNamespace ); + + /// + void setSuperClassNamespace( const char* superClassNamespace ); + + /// @} + + /// @name Persistent IDs + /// @{ + + /// Return the persistent ID assigned to this object or NULL. + SimPersistID* getPersistentId() const { return mPersistentId; } + + /// Return the persistent ID assigned to this object or assign one to it if it has none. + SimPersistID* getOrCreatePersistentId(); + + /// @} + + /// @name Debugging + /// @{ + + /// Return a textual description of the object. + virtual String describeSelf() const; + + /// Dump the contents of this object to the console. Use the Torque Script dump() and dumpF() functions to + /// call this. + void dumpToConsole( bool includeFunctions=true ); + + ///added this so that you can print the entire class hierarchy, including script objects, + //from the console or C++. + + /// Print the AbstractClassRep hierarchy of this object to the console. + virtual void dumpClassHierarchy(); + + /// Print the SimGroup hierarchy of this object to the console. + virtual void dumpGroupHierarchy(); + + /// @} + + static void initPersistFields(); + + /// Copy SimObject to another SimObject (Originally designed for T2D). + virtual void copyTo(SimObject* object); + + // Component Console Overrides + virtual bool handlesConsoleMethod(const char * fname, S32 * routingId) { return false; } + virtual void getConsoleMethodData(const char * fname, S32 routingId, S32 * type, S32 * minArgs, S32 * maxArgs, void ** callback, const char ** usage) {} + + DECLARE_CONOBJECT( SimObject ); + + static SimObject* __findObject( const char* id ) { return Sim::findObject( id ); } + static const char* __getObjectId( ConsoleObject* object ) + { + SimObject* simObject = static_cast< SimObject* >( object ); + if( !simObject ) + return ""; + else if( simObject->getName() ) + return simObject->getName(); + return simObject->getIdString(); + } + + // EngineObject. + virtual void destroySelf(); +}; + + +/// Smart SimObject pointer. +/// +/// This class keeps track of the book-keeping necessary +/// to keep a registered reference to a SimObject or subclass +/// thereof. +/// +/// Normally, if you want the SimObject to be aware that you +/// have a reference to it, you must call SimObject::registerReference() +/// when you create the reference, and SimObject::unregisterReference() when +/// you're done. If you change the reference, you must also register/unregister +/// it. This is a big headache, so this class exists to automatically +/// keep track of things for you. +/// +/// @code +/// // Assign an object to the +/// SimObjectPtr mOrbitObject = Sim::findObject("anObject"); +/// +/// // Use it as a GameBase*. +/// mOrbitObject->getWorldBox().getCenter(&mPosition); +/// +/// // And reassign it - it will automatically update the references. +/// mOrbitObject = Sim::findObject("anotherObject"); +/// @endcode +template< typename T > +class SimObjectPtr : public WeakRefPtr< T > +{ + public: + + typedef WeakRefPtr< T > Parent; + + SimObjectPtr() {} + SimObjectPtr(T *ptr) { this->mReference = NULL; set(ptr); } + SimObjectPtr( const SimObjectPtr& ref ) { this->mReference = NULL; set(ref.mReference); } + + T* getObject() const { return Parent::getPointer(); } + + ~SimObjectPtr() { set((WeakRefBase::WeakReference*)NULL); } + + SimObjectPtr& operator=(const SimObjectPtr ref) + { + set(ref.mReference); + return *this; + } + SimObjectPtr& operator=(T *ptr) + { + set(ptr); + return *this; + } + + protected: + void set(WeakRefBase::WeakReference * ref) + { + if( ref == this->mReference ) + return; + + if( this->mReference ) + { + // Auto-delete + T* obj = this->getPointer(); + if ( this->mReference->getRefCount() == 2 && obj && obj->isAutoDeleted() ) + obj->deleteObject(); + + this->mReference->decRefCount(); + } + this->mReference = NULL; + if( ref ) + { + this->mReference = ref; + this->mReference->incRefCount(); + } + } + + void set(T * obj) + { + set(obj ? obj->getWeakReference() : (WeakRefBase::WeakReference *)NULL); + } +}; + +#endif // _SIMOBJECT_H_ diff --git a/Engine/source/console/simObjectList.cpp b/Engine/source/console/simObjectList.cpp new file mode 100644 index 000000000..4efc055a3 --- /dev/null +++ b/Engine/source/console/simObjectList.cpp @@ -0,0 +1,130 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/simObjectList.h" + +#include "console/console.h" +#include "console/sim.h" +#include "console/simObject.h" + + +String SimObjectList::smSortScriptCallbackFn; + +bool SimObjectList::pushBack(SimObject* obj) +{ + if (find(begin(),end(),obj) == end()) + { + push_back(obj); + return true; + } + + return false; +} + +bool SimObjectList::pushBackForce(SimObject* obj) +{ + iterator itr = find(begin(),end(),obj); + if (itr == end()) + { + push_back(obj); + return true; + } + else + { + // Move to the back... + // + SimObject* pBack = *itr; + removeStable(pBack); + push_back(pBack); + } + + return false; +} + +bool SimObjectList::pushFront(SimObject* obj) +{ + if (find(begin(),end(),obj) == end()) + { + push_front(obj); + return true; + } + + return false; +} + +bool SimObjectList::remove(SimObject* obj) +{ + iterator ptr = find(begin(),end(),obj); + if (ptr != end()) + { + erase(ptr); + return true; + } + + return false; +} + +bool SimObjectList::removeStable(SimObject* obj) +{ + iterator ptr = find(begin(),end(),obj); + if (ptr != end()) + { + erase(ptr); + return true; + } + + return false; +} + +S32 QSORT_CALLBACK SimObjectList::_compareId(const void* a,const void* b) +{ + return (*reinterpret_cast(a))->getId() - + (*reinterpret_cast(b))->getId(); +} + +void SimObjectList::sortId() +{ + dQsort(address(),size(),sizeof(value_type),_compareId); +} + +S32 QSORT_CALLBACK SimObjectList::_callbackSort( const void *a, const void *b ) +{ + const SimObject *objA = *reinterpret_cast( a ); + const SimObject *objB = *reinterpret_cast( b ); + + static char idA[64]; + dSprintf( idA, sizeof( idA ), "%d", objA->getId() ); + static char idB[64]; + dSprintf( idB, sizeof( idB ), "%d", objB->getId() ); + + return dAtoi( Con::executef( smSortScriptCallbackFn, idA, idB ) ); +} + +void SimObjectList::scriptSort( const String &scriptCallback ) +{ + AssertFatal( smSortScriptCallbackFn.isEmpty(), "SimObjectList::scriptSort() - The script sort is not reentrant!" ); + + smSortScriptCallbackFn = scriptCallback; + dQsort( address(), size(), sizeof(value_type), _callbackSort ); + smSortScriptCallbackFn = String::EmptyString; +} diff --git a/Engine/source/console/simObjectList.h b/Engine/source/console/simObjectList.h new file mode 100644 index 000000000..88c72a396 --- /dev/null +++ b/Engine/source/console/simObjectList.h @@ -0,0 +1,77 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SIMOBJECTLIST_H_ +#define _SIMOBJECTLIST_H_ + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _TALGORITHM_H_ +#include "core/tAlgorithm.h" +#endif +#ifndef _TORQUE_STRING_H_ +#include "core/util/str.h" +#endif + +// Forward Refs +class SimObject; + +/// A vector of SimObjects. +/// +/// As this inherits from VectorPtr, it has the full range of vector methods. +class SimObjectList : public VectorPtr +{ + /// The script callback function for the active sort. + /// @see scriptSort + static String smSortScriptCallbackFn; + + /// The script callback comparision callback. + /// @see scriptSort + static S32 QSORT_CALLBACK _callbackSort(const void* a,const void* b); + + /// The SimObjectId comparision sort callback. + /// @see sortId + static S32 QSORT_CALLBACK _compareId( const void *a, const void *b ); + +public: + + bool pushBack(SimObject*); ///< Add the SimObject* to the end of the list, unless it's already in the list. + bool pushBackForce(SimObject*); ///< Add the SimObject* to the end of the list, moving it there if it's already present in the list. + bool pushFront(SimObject*); ///< Add the SimObject* to the start of the list. + bool remove(SimObject*); ///< Remove the SimObject* from the list; may disrupt order of the list. + + SimObject* at(S32 index) const { if(index >= 0 && index < size()) return (*this)[index]; return NULL; } + + /// Remove the SimObject* from the list; guaranteed to preserve list order. + bool removeStable(SimObject* pObject); + + /// Performs a simple sort by SimObjectId. + void sortId(); + + /// Performs a sort of the objects in the set using a script + /// callback function to do the comparision. + /// @see SimSet::scriptSort + void scriptSort( const String &scriptCallback ); +}; + +#endif // _SIMOBJECTLIST_H_ diff --git a/Engine/source/console/simObjectMemento.cpp b/Engine/source/console/simObjectMemento.cpp new file mode 100644 index 000000000..839ae0846 --- /dev/null +++ b/Engine/source/console/simObjectMemento.cpp @@ -0,0 +1,155 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/simObjectMemento.h" + +#include "console/simObject.h" +#include "console/simDatablock.h" +#include "core/stream/memStream.h" + + +SimObjectMemento::SimObjectMemento() + : mState( NULL ), + mIsDatablock( false ) +{ +} + +SimObjectMemento::~SimObjectMemento() +{ + dFree( mState ); +} + +void SimObjectMemento::save( SimObject *object ) +{ + // Cleanup any existing state data. + dFree( mState ); + mObjectName = String::EmptyString; + + // Use a stream to save the state. + MemStream stream( 256 ); + + U32 writeFlags = 0; + SimDataBlock* db = dynamic_cast(object); + if( !db ) + stream.write( sizeof( "return " ) - 1, "return " ); + else + { + mIsDatablock = true; + + // Cull the datablock name from the output so that + // we can easily replace it in case the datablock's name + // is already taken when we call restore(). We can't use the same + // setup as with non-datablock classes as the return semantics + // are not the same. + + writeFlags |= SimObject::NoName; + } + + object->write( stream, 0, writeFlags ); + stream.write( (UTF8)0 ); + + // Steal the data away from the stream. + mState = (UTF8*)stream.takeBuffer(); + mObjectName = object->getName(); +} + +SimObject *SimObjectMemento::restore() const +{ + // Make sure we have data to restore. + if ( !mState ) + return NULL; + + // TODO: We could potentially make this faster by + // caching the CodeBlock generated from the string + + SimObject* object; + if( !mIsDatablock ) + { + // Set the redefine behavior to automatically giving + // the new objects unique names. This will restore the + // old names if they are still available or give reasonable + // approximations if not. + + const char* oldRedefineBehavior = Con::getVariable( "$Con::redefineBehavior" ); + Con::setVariable( "$Con::redefineBehavior", "renameNew" ); + + // Read the object. + + const UTF8* result = Con::evaluate( mState ); + + // Restore the redefine behavior. + + Con::setVariable( "$Con::redefineBehavior", oldRedefineBehavior ); + + if ( !result || !result[ 0 ] ) + return NULL; + + // Look up the object. + + U32 objectId = dAtoi( result ); + object = Sim::findObject( objectId ); + } + else + { + String objectName = mObjectName; + + // For datablocks, it's getting a little complicated. Datablock definitions cannot be used + // as expressions and thus we can't get to the datablock object we create by using the + // Con::evaluate() return value. Instead, we need to rely on the object name. However, if + // the name is already taken and needs to be changed, we need to manually do that. To complicate + // this further, we cannot rely on automatic renaming since then we don't know by what name + // the newly created object actually goes. So what we do is we alter the source text snapshot + // and substitute a name in case the old object name is actually taken now. + + char* tempBuffer; + if( !Sim::findObject( objectName ) ) + tempBuffer = mState; + else + { + String uniqueName = Sim::getUniqueName( objectName ); + U32 uniqueNameLen = uniqueName.length(); + + char* pLeftParen = dStrchr( mState, '(' ); + if( pLeftParen == NULL ) + return NULL; + U32 numCharsToLeftParen = pLeftParen - mState; + + tempBuffer = ( char* ) dMalloc( dStrlen( mState ) + uniqueNameLen + 1 ); + dMemcpy( tempBuffer, mState, numCharsToLeftParen ); + dMemcpy( &tempBuffer[ numCharsToLeftParen ], uniqueName, uniqueNameLen ); + dStrcpy( &tempBuffer[ numCharsToLeftParen + uniqueNameLen ], &mState[ numCharsToLeftParen ] ); + } + + Con::evaluate( tempBuffer ); + + if( tempBuffer != mState ) + dFree( tempBuffer ); + + if( objectName == String::EmptyString ) + return NULL; + + object = Sim::findObject( objectName ); + } + + return object; +} diff --git a/Engine/source/console/simObjectMemento.h b/Engine/source/console/simObjectMemento.h new file mode 100644 index 000000000..8fc029726 --- /dev/null +++ b/Engine/source/console/simObjectMemento.h @@ -0,0 +1,64 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _CONSOLE_SIMOBJECTMEMENTO_H_ +#define _CONSOLE_SIMOBJECTMEMENTO_H_ + +#ifndef _SIM_H_ +#include "console/sim.h" +#endif + + +/// This simple class is used to store an SimObject and +/// its state so it can be recreated at a later time. +/// +/// The success of restoring the object completely depends +/// on the results from SimObject::write(). +class SimObjectMemento +{ +protected: + + /// The captured object state. + UTF8 *mState; + + /// The captured object's name. + String mObjectName; + bool mIsDatablock; + +public: + + SimObjectMemento(); + virtual ~SimObjectMemento(); + + /// Returns true if we have recorded state. + bool hasState() const { return mState; } + + /// + void save( SimObject *object ); + + /// + SimObject *restore() const; + +}; + + +#endif // _CONSOLE_SIMOBJECTMEMENTO_H_ diff --git a/Engine/source/console/simObjectRef.h b/Engine/source/console/simObjectRef.h new file mode 100644 index 000000000..d29c0d6e6 --- /dev/null +++ b/Engine/source/console/simObjectRef.h @@ -0,0 +1,181 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SIMOBJECTREF_H_ +#define _SIMOBJECTREF_H_ + + +#ifndef _DYNAMIC_CONSOLETYPES_H_ +#include "console/dynamicTypes.h" +#endif + +#ifndef _SIM_H_ +#include "console/sim.h" +#endif + + + +// SimObjectRef + +template< class T > +class SimObjectRef +{ +public: + + static S32 _smTypeId; + + SimObjectRef() + : mId( 0 ), + mObject( NULL ), + mName( "" ) + { + } + + T* operator-> () { return _get(); } + + operator T*() { return _get(); } + + operator bool () { return _get() != NULL; } + + SimObjectRef& operator= ( U32 id ) + { + _set( id ); + return *this; + } + + SimObjectRef& operator= ( const char *name ) + { + _set( name ); + return *this; + } + +protected: + + T* _get(); + void _set( U32 id ); + void _set( const char *name ); + +protected: + + StringTableEntry mName; + U32 mId; + T *mObject; +}; + + +// static initialization + +template +S32 SimObjectRef::_smTypeId = 0; + + +// inline methods + +template +void SimObjectRef::_set( const char *name ) +{ + mName = StringTable->insert( name ); + mObject = NULL; + mId = 0; +} + +template +void SimObjectRef::_set( U32 id ) +{ + mId = id; + mObject = NULL; + mName = ""; +} + +template +T* SimObjectRef::_get() +{ + if ( mObject == NULL ) + { + if ( mId != 0 ) + Sim::findObject( mId, mObject ); + else if ( mName != NULL && dStrlen( mName ) != 0 ) + Sim::findObject( mName, mObject ); + } + + return mObject; +} + + +// SimObjectRefConsoleBaseType + +template< class T > +class SimObjectRefConsoleBaseType : public ConsoleBaseType +{ +public: + + typedef ConsoleBaseType Parent; + + SimObjectRefConsoleBaseType( const char *typeName ) + : Parent( sizeof(SimObjectRef), &SimObjectRef::_smTypeId, typeName ) + { + } + +public: + + virtual const char* getData( void *dptr, const EnumTable*, BitSet32 ) + { + SimObjectRef *objRef = static_cast< SimObjectRef* >( dptr ); + T *obj = *objRef; + return T::__getObjectId( obj ); + } + + virtual void setData( void* dptr, S32 argc, const char** argv, const EnumTable*, BitSet32 ) + { + SimObjectRef *objRef = static_cast< SimObjectRef* >( dptr ); + + if ( argc != 1 ) + return; + + *objRef = argv[0]; + } + + virtual const bool isDatablock() + { + return T::__smIsDatablock; + }; + + virtual const char* getTypeClassName() + { + return T::getStaticClassRep()->getClassName(); + } + + virtual void* getNativeVariable() + { + SimObjectRef *var = new SimObjectRef(); + return (void*)var; + } + + virtual void deleteNativeVariable( void *var ) + { + SimObjectRef *nativeVar = reinterpret_cast< SimObjectRef* >( var ); + delete nativeVar; + } +}; + + +#endif // _SIMOBJECTREF_H_ \ No newline at end of file diff --git a/Engine/source/console/simPersistID.cpp b/Engine/source/console/simPersistID.cpp new file mode 100644 index 000000000..0a6a65589 --- /dev/null +++ b/Engine/source/console/simPersistID.cpp @@ -0,0 +1,138 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/simPersistID.h" +#include "console/simObject.h" +#include "core/util/tDictionary.h" +#include "core/util/safeDelete.h" + + +//#define DEBUG_SPEW + + +SimPersistID::LookupTableType* SimPersistID::smLookupTable; + + +//----------------------------------------------------------------------------- + +SimPersistID::SimPersistID( SimObject* object ) + : mObject( object ) +{ + AssertFatal( object, "SimPersistID::SimPersistID - got a NULL object!" ); + AssertFatal( !object->getPersistentId(), "SimPersistID::SimPersistID - object already has a persistent ID!" ); + + mUUID.generate(); + smLookupTable->insertUnique( mUUID, this ); +} + +//----------------------------------------------------------------------------- + +SimPersistID::SimPersistID( const Torque::UUID& uuid ) + : mUUID( uuid ), + mObject( NULL ) +{ + AssertFatal( !uuid.isNull(), "SimPersistID::SimPersistID - invalid UUID!" ); + smLookupTable->insertUnique( mUUID, this ); +} + +//----------------------------------------------------------------------------- + +SimPersistID::~SimPersistID() +{ + smLookupTable->erase( getUUID() ); +} + +//----------------------------------------------------------------------------- + +void SimPersistID::init() +{ + smLookupTable = new LookupTableType; +} + +//----------------------------------------------------------------------------- + +void SimPersistID::shutdown() +{ + SAFE_DELETE( smLookupTable ); +} + +//----------------------------------------------------------------------------- + +SimPersistID* SimPersistID::create( SimObject* object ) +{ + SimPersistID* pid = new SimPersistID( object ); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SimPersistID] Created new pid for object %i:%s (%s) with uuid '%s'", + object->getId(), object->getClassName(), object->getName(), + pid->getUUID().toString().c_str() ); + #endif + + return pid; +} + +//----------------------------------------------------------------------------- + +void SimPersistID::resolve( SimObject* object ) +{ + AssertFatal( !mObject, "SimPersistID::resolve - PID is already resolved!" ); + mObject = object; + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SimPersistID] Resolving pid '%s' to %i:%s (%s)", + getUUID().toString().c_str(), + object->getId(), object->getClassName(), object->getName() ); + #endif +} + +//----------------------------------------------------------------------------- + +SimPersistID* SimPersistID::find( const Torque::UUID& uuid ) +{ + AssertFatal( smLookupTable, "SimPersistID::find - system has not been initialized" ); + + LookupTableType::Iterator iter = smLookupTable->find( uuid ); + if( iter != smLookupTable->end() ) + return iter->value; + + return NULL; +} + +//----------------------------------------------------------------------------- + +SimPersistID* SimPersistID::findOrCreate( const Torque::UUID& uuid ) +{ + AssertFatal( smLookupTable, "SimPersistID::findOrCreate - system has not been initialized" ); + + SimPersistID* pid = find( uuid ); + if( !pid ) + { + pid = new SimPersistID( uuid ); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SimPersistID] Created unresolved pid for UUID '%s'", + uuid.toString().c_str() ); + #endif + } + + return pid; +} diff --git a/Engine/source/console/simPersistID.h b/Engine/source/console/simPersistID.h new file mode 100644 index 000000000..46b1a4559 --- /dev/null +++ b/Engine/source/console/simPersistID.h @@ -0,0 +1,111 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SIMPERSISTID_H_ +#define _SIMPERSISTID_H_ + +#ifndef _TORQUE_UUID_H_ + #include "core/util/uuid.h" +#endif +#ifndef _REFBASE_H_ + #include "core/util/refBase.h" +#endif + + +/// @file +/// Persistent IDs for SimObjects. + + +class SimObject; +template< typename, typename > class HashTable; + + +/// A globally unique persistent ID for a SimObject. +class SimPersistID : public StrongRefBase +{ + public: + + typedef void Parent; + friend class SimObject; + + protected: + + typedef HashTable< Torque::UUID, SimPersistID* > LookupTableType; + + /// Reference to the SimObject. Will be NULL for as long as the + /// persistent ID is not resolved. + SimObject* mObject; + + /// The UUID assigned to the object. Never changes. + Torque::UUID mUUID; + + /// Table of persistent object IDs. + static LookupTableType* smLookupTable; + + /// Construct a new persistent ID for "object" by generating a fresh + /// unique identifier. + SimPersistID( SimObject* object ); + + /// Construct a persistent ID stub for the given unique identifier. + /// The stub remains not bound to any object until it is resolved. + SimPersistID( const Torque::UUID& uuid ); + + /// + ~SimPersistID(); + + /// Bind this unresolved PID to the given object. + void resolve( SimObject* object ); + + /// + void unresolve() { mObject = NULL; } + + /// Create a persistent ID for the given object. + static SimPersistID* create( SimObject* object ); + + public: + + /// Initialize the persistent ID system. + static void init(); + + /// Uninitialize the persistent ID system. + static void shutdown(); + + /// Look up a persistent ID by its UUID. Return NULL if no PID is bound to the given UUID. + static SimPersistID* find( const Torque::UUID& uuid ); + + /// Look up a persistent ID by its UUID. If no PID is bound to the given UUID yet, create a + /// new PID and bind it to the UUID. + static SimPersistID* findOrCreate( const Torque::UUID& uuid ); + + /// Find a SimObject by the UUID assigned to its PID. Return NULL if either no PID is bound + /// to the given UUID or if the PID bound to it is not yet resolved. + static SimObject* findObjectByUUID( const Torque::UUID& uuid ); + + /// Return the object that is bound to this PID. If the PID has not yet been resolved, + /// return NULL. + SimObject* getObject() const { return mObject; } + + /// Return the UUID bound to this PID. + const Torque::UUID& getUUID() const { return mUUID; } +}; + +#endif // !_SIMPERSISTID_H_ diff --git a/Engine/source/console/simPersistSet.cpp b/Engine/source/console/simPersistSet.cpp new file mode 100644 index 000000000..ec3151157 --- /dev/null +++ b/Engine/source/console/simPersistSet.cpp @@ -0,0 +1,192 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/simPersistSet.h" +#include "console/simPersistID.h" +#include "console/consoleTypes.h" + + +IMPLEMENT_CONOBJECT( SimPersistSet ); + +ConsoleDocClass( SimPersistSet, + "@brief A SimSet that can be safely persisted.\n\n" + "Uses SimPersistIDs to reference objects in the set " + "while persisted on disk. This allows the set to resolve " + "its references no matter whether they are loaded before or " + "after the set is created.\n\n" + "Not intended for game development, for editors or internal use only.\n\n " + "@internal"); + +//----------------------------------------------------------------------------- + +SimPersistSet::SimPersistSet() + : mIsResolvingPIDs( false ) +{ + VECTOR_SET_ASSOCIATION( mUnresolvedPIDs ); +} + +//----------------------------------------------------------------------------- + +bool SimPersistSet::processArguments( S32 argc, const char** argv ) +{ + for( U32 i = 0; i < argc; ++ i ) + { + // Parse the UUID. + Torque::UUID uuid; + if( !uuid.fromString( argv[ i ] ) ) + { + Con::errorf( "SimPersistSet::processArguments - could not read UUID at index %i: %s", i, argv[ i ] ); + continue; + } + + // Find or create the respective persistent ID. + SimPersistID* pid = SimPersistID::findOrCreate( uuid ); + if( pid->getObject() ) + { + // There's already an object attached to this PID so just + // add the object to the set. + addObject( pid->getObject() ); + } + else + { + // There not yet an object attached to the PID so push it + // onto the stack to resolve it later. + pid->incRefCount(); + mUnresolvedPIDs.push_back( pid ); + } + } + + return true; +} + +//----------------------------------------------------------------------------- + +void SimPersistSet::write( Stream& stream, U32 tabStop, U32 flags ) +{ + if( ( flags & SelectedOnly ) && !isSelected() ) + return; + + // If the selection is transient, we cannot really save it. + // Just invoke the default SimObject::write and return. + + if( !getCanSave() ) + { + Con::errorf( "SimPersistSet::write - transient set being saved: %d:%s (%s)", + getId(), getClassName(), getName() ); + Parent::write( stream, tabStop, flags ); + return; + } + + // If there are unresolved PIDs, give resolving them one last + // chance before writing out the set. + + if( !mUnresolvedPIDs.empty() ) + resolvePIDs(); + + // Write the set out. + + stream.writeTabs( tabStop ); + + StringBuilder buffer; + buffer.format( "new %s(%s", getClassName(), getName() ? getName() : "" ); + + // Write the persistent IDs of all child objects into the set's + // object constructor so we see them passed back to us through + // processArguments when the object gets read in. + + const U32 numChildren = size(); + for( U32 i = 0; i < numChildren; ++ i ) + { + SimObject* child = at( i ); + + SimPersistID* pid = child->getPersistentId(); + AssertWarn( pid != NULL, "SimPersistSet::write - object without pid in persistent selection!" ); + if( !pid ) + continue; + + buffer.append( ',' ); + buffer.append( '"' ); + buffer.append( pid->getUUID().toString() ); + buffer.append( '"' ); + } + + buffer.append( ") {\r\n" ); + + stream.write( buffer.length(), buffer.data() ); + + // Write our object fields. + + writeFields( stream, tabStop + 1 ); + + // Close our object definition. + + stream.writeTabs( tabStop ); + stream.write( 4, "};\r\n" ); +} + +//----------------------------------------------------------------------------- + +void SimPersistSet::resolvePIDs() +{ + if( mIsResolvingPIDs ) + return; + + lock(); + mIsResolvingPIDs = true; + + for( U32 i = 0; i < mUnresolvedPIDs.size(); ++ i ) + if( mUnresolvedPIDs[ i ]->getObject() != NULL ) + { + addObject( mUnresolvedPIDs[ i ]->getObject() ); + mUnresolvedPIDs[i]->decRefCount(); + mUnresolvedPIDs.erase( i ); + -- i; + } + + mIsResolvingPIDs = false; + unlock(); +} + +//----------------------------------------------------------------------------- + +void SimPersistSet::addObject( SimObject* object ) +{ + // If this set isn't transient, make sure the object has a valid + // persistent ID. + + if( getCanSave() ) + object->getOrCreatePersistentId(); + + Parent::addObject( object ); +} + +//============================================================================= +// Console Methods. +//============================================================================= +// MARK: ---- Console Methods ---- + +//----------------------------------------------------------------------------- + +ConsoleMethod( SimPersistSet, resolvePersistentIds, void, 2, 2, "() - Try to bind unresolved persistent IDs in the set." ) +{ + object->resolvePIDs(); +} diff --git a/Engine/source/console/simPersistSet.h b/Engine/source/console/simPersistSet.h new file mode 100644 index 000000000..6ac534762 --- /dev/null +++ b/Engine/source/console/simPersistSet.h @@ -0,0 +1,68 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SIMPERSISTSET_H_ +#define _SIMPERSISTSET_H_ + +#ifndef _SIMSET_H_ + #include "console/simSet.h" +#endif + + +/// A SimSet that can be safely persisted. Uses SimPersistIDs to reference +/// objects in the set while persisted on disk. This allows the set to resolve +/// its references no matter whether they are loaded before or after the set +/// is created. +/// +class SimPersistSet : public SimSet +{ + public: + + typedef SimSet Parent; + + protected: + + /// List of unresolved persistent IDs. + Vector< SimPersistID* > mUnresolvedPIDs; + + /// If true, the set is currently resolving persistent IDs. + bool mIsResolvingPIDs; + + public: + + /// + SimPersistSet(); + + /// Try to resolve all persistent IDs that as of yet are still unresolved. + void resolvePIDs(); + + // SimSet. + virtual void addObject( SimObject* ); + virtual void write( Stream &stream, U32 tabStop, U32 flags = 0 ); + virtual bool processArguments( S32 argc, const char** argv ); + + DECLARE_CONOBJECT( SimPersistSet ); + DECLARE_CATEGORY( "Console" ); + DECLARE_DESCRIPTION( "A SimSet that can be safely persisted." ); +}; + +#endif // !_SIMPERSISTSET_H_ diff --git a/Engine/source/console/simSerialize.cpp b/Engine/source/console/simSerialize.cpp new file mode 100644 index 000000000..e3f64be4c --- /dev/null +++ b/Engine/source/console/simSerialize.cpp @@ -0,0 +1,230 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "console/console.h" +#include "console/simBase.h" +#include "core/stream/bitStream.h" +#include "core/stream/fileStream.h" +#include "console/engineAPI.h" + + +//----------------------------------------------------------------------------- +// SimObject Methods +//----------------------------------------------------------------------------- + +bool SimObject::writeObject(Stream *stream) +{ + stream->writeString(getName() ? getName() : ""); + + // Static fields + AbstractClassRep *rep = getClassRep(); + AbstractClassRep::FieldList &fieldList = rep->mFieldList; + AbstractClassRep::FieldList::iterator itr; + + U32 savePos = stream->getPosition(); + U32 numFields = fieldList.size(); + stream->write(numFields); + + for(itr = fieldList.begin();itr != fieldList.end();itr++) + { + if( itr->type >= AbstractClassRep::ARCFirstCustomField ) + { + numFields--; + continue; + } + + const char *field = getDataField(itr->pFieldname, NULL); + if(field == NULL) + field = ""; + + stream->writeString(itr->pFieldname); + stream->writeString(field); + } + + // Dynamic Fields + if(mCanSaveFieldDictionary) + { + SimFieldDictionary * fieldDictionary = getFieldDictionary(); + for(SimFieldDictionaryIterator ditr(fieldDictionary); *ditr; ++ditr) + { + SimFieldDictionary::Entry * entry = (*ditr); + + stream->writeString(entry->slotName); + stream->writeString(entry->value); + numFields++; + } + } + + // Overwrite the number of fields with the correct value + U32 savePos2 = stream->getPosition(); + stream->setPosition(savePos); + stream->write(numFields); + stream->setPosition(savePos2); + + return true; +} + +bool SimObject::readObject(Stream *stream) +{ + const char *name = stream->readSTString(true); + if(name && *name) + assignName(name); + + U32 numFields; + stream->read(&numFields); + + for(S32 i = 0;i < numFields;i++) + { + const char *fieldName = stream->readSTString(); + const char *data = stream->readSTString(); + + setDataField(fieldName, NULL, data); + } + return true; +} + +//----------------------------------------------------------------------------- +// SimSet Methods +//----------------------------------------------------------------------------- + +bool SimSet::writeObject( Stream *stream ) +{ + if(! Parent::writeObject(stream)) + return false; + + stream->write(size()); + for(SimSet::iterator i = begin();i < end();++i) + { + if(! Sim::saveObject((*i), stream)) + return false; + } + return true; +} + +bool SimSet::readObject( Stream *stream ) +{ + if(! Parent::readObject(stream)) + return false; + + U32 numObj; + stream->read(&numObj); + + for(U32 i = 0;i < numObj;i++) + { + SimObject *obj = Sim::loadObjectStream(stream); + if(obj == NULL) + return false; + + addObject(obj); + } + + return true; +} + +//----------------------------------------------------------------------------- +// Sim Functions +//----------------------------------------------------------------------------- + +namespace Sim +{ + +bool saveObject(SimObject *obj, const char *filename) +{ + FileStream *stream; + if((stream = FileStream::createAndOpen( filename, Torque::FS::File::Write )) == NULL) + return false; + + bool ret = saveObject(obj, stream); + delete stream; + + return ret; +} + +bool saveObject(SimObject *obj, Stream *stream) +{ + stream->writeString(obj->getClassName()); + return obj->writeObject(stream); +} + +//----------------------------------------------------------------------------- + +SimObject *loadObjectStream(const char *filename) +{ + FileStream * stream; + if((stream = FileStream::createAndOpen( filename, Torque::FS::File::Read )) == NULL) + return NULL; + + SimObject * ret = loadObjectStream(stream); + delete stream; + return ret; +} + +SimObject *loadObjectStream(Stream *stream) +{ + const char *className = stream->readSTString(true); + ConsoleObject *conObj = ConsoleObject::create(className); + if(conObj == NULL) + { + Con::errorf("Sim::restoreObjectStream - Could not create object of class \"%s\"", className); + return NULL; + } + + SimObject *simObj = dynamic_cast(conObj); + if(simObj == NULL) + { + Con::errorf("Sim::restoreObjectStream - Object of class \"%s\" is not a SimObject", className); + delete simObj; + return NULL; + } + + if( simObj->readObject(stream) + && simObj->registerObject() ) + return simObj; + + delete simObj; + return NULL; +} + +} // end namespace Sim + +//----------------------------------------------------------------------------- +// Console Functions +//----------------------------------------------------------------------------- + +DefineEngineFunction(saveObject, bool, ( SimObject *object, const char *filename ),, + "@brief Serialize the object to a file.\n\n" + "@param object The object to serialize.\n" + "@param filename The file name and path.\n" + "@ingroup Console\n") +{ + return object && Sim::saveObject(object, filename); +} + +DefineEngineFunction(loadObject, SimObject*, ( const char *filename ),, + "@brief Loads a serialized object from a file.\n\n" + "@param Name and path to text file containing the object\n" + "@ingroup Console\n") +{ + return Sim::loadObjectStream(filename); +} diff --git a/Engine/source/console/simSet.cpp b/Engine/source/console/simSet.cpp new file mode 100644 index 000000000..441b8d164 --- /dev/null +++ b/Engine/source/console/simSet.cpp @@ -0,0 +1,1138 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/simSet.h" + +#include "core/stringTable.h" +#include "console/console.h" +#include "core/stream/fileStream.h" +#include "sim/actionMap.h" +#include "core/fileObject.h" +#include "console/consoleInternal.h" +#include "console/engineAPI.h" +#include "platform/profiler.h" +#include "console/typeValidators.h" +#include "core/frameAllocator.h" +#include "math/mMathFn.h" + + +IMPLEMENT_CONOBJECT( SimSet ); +IMPLEMENT_CONOBJECT( SimGroup ); + +ConsoleDocClass( SimSet, + "@brief A collection of SimObjects.\n\n" + + "It is often necessary to keep track of an arbitrary set of SimObjects. " + "For instance, Torque's networking code needs to not only keep track of " + "the set of objects which need to be ghosted, but also the set of objects " + "which must always be ghosted. It does this by working with two " + "sets. The first of these is the RootGroup (which is actually a SimGroup) " + "and the second is the GhostAlwaysSet, which contains objects which must " + "always be ghosted to the client.\n\n" + + "Some general notes on SimSets:\n\n" + "- Membership is not exclusive. A SimObject may be a member of multiple " + "SimSets.\n\n" + "- A SimSet does not destroy subobjects when it is destroyed.\n\n" + "- A SimSet may hold an arbitrary number of objects.\n\n" + + "@ingroup Console\n" + "@ingroup Scripting" +); +ConsoleDocClass( SimGroup, + "@brief A collection of SimObjects that are owned by the group.\n\n" + + "A SimGroup is a stricter form of SimSet. SimObjects may only be a member " + "of a single SimGroup at a time. The SimGroup will automatically enforce " + "the single-group-membership rule (ie. adding an object to a SimGroup will " + "cause it to be removed from its current SimGroup, if any).\n\n" + + "Deleting a SimGroup will also delete all SimObjects in the SimGroup.\n\n" + + "@tsexample\n" + "// Create a SimGroup for particle emitters\n" + "new SimGroup(Emitters)\n" + "{\n" + " canSaveDynamicFields = \"1\";\n\n" + " new ParticleEmitterNode(CrystalEmmiter) {\n" + " active = \"1\";\n" + " emitter = \"dustEmitter\";\n" + " velocity = \"1\";\n" + " dataBlock = \"GenericSmokeEmitterNode\";\n" + " position = \"-61.6276 2.1142 4.45027\";\n" + " rotation = \"1 0 0 0\";\n" + " scale = \"1 1 1\";\n" + " canSaveDynamicFields = \"1\";\n" + " };\n\n" + " new ParticleEmitterNode(Steam1) {\n" + " active = \"1\";\n" + " emitter = \"SlowSteamEmitter\";\n" + " velocity = \"1\";\n" + " dataBlock = \"GenericSmokeEmitterNode\";\n" + " position = \"-25.0458 1.55289 2.51308\";\n" + " rotation = \"1 0 0 0\";\n" + " scale = \"1 1 1\";\n" + " canSaveDynamicFields = \"1\";\n" + " };\n" + "};\n\n" + "@endtsexample\n\n" + "@ingroup Console\n" + "@ingroup Scripting" +); + +IMPLEMENT_CALLBACK( SimSet, onObjectAdded, void, ( SimObject* object ), ( object ), + "Called when an object is added to the set.\n" + "@param object The object that was added." ); +IMPLEMENT_CALLBACK( SimSet, onObjectRemoved, void, ( SimObject* object ), ( object ), + "Called when an object is removed from the set.\n" + "@param object The object that was removed." ); + + +//============================================================================= +// SimSet. +//============================================================================= +// MARK: ---- SimSet ---- + +//----------------------------------------------------------------------------- + +SimSet::SimSet() +{ + VECTOR_SET_ASSOCIATION( objectList ); + mMutex = Mutex::createMutex(); +} + +//----------------------------------------------------------------------------- + +SimSet::~SimSet() +{ + Mutex::destroyMutex( mMutex ); +} + +//----------------------------------------------------------------------------- + +void SimSet::addObject( SimObject* obj ) +{ + // Prevent SimSet being added to itself. + if( obj == this ) + return; + + lock(); + + const bool added = objectList.pushBack( obj ); + if( added ) + deleteNotify( obj ); + + unlock(); + + if( added ) + { + getSetModificationSignal().trigger( SetObjectAdded, this, obj ); + if( obj->isProperlyAdded() ) + onObjectAdded_callback( obj ); + } +} + +//----------------------------------------------------------------------------- + +void SimSet::removeObject( SimObject* obj ) +{ + lock(); + + const bool removed = objectList.remove( obj ); + if( removed ) + clearNotify( obj ); + + unlock(); + + if( removed ) + { + getSetModificationSignal().trigger( SetObjectRemoved, this, obj ); + if( obj->isProperlyAdded() ) + onObjectRemoved_callback( obj ); + } +} + +//----------------------------------------------------------------------------- + +void SimSet::pushObject( SimObject* obj ) +{ + if( obj == this ) + return; + + lock(); + + bool added = objectList.pushBackForce( obj ); + if( added ) + deleteNotify( obj ); + + unlock(); + + if( added ) + { + getSetModificationSignal().trigger( SetObjectAdded, this, obj ); + if( obj->isProperlyAdded() ) + onObjectAdded_callback( obj ); + } +} + +//----------------------------------------------------------------------------- + +void SimSet::popObject() +{ + if( objectList.empty() ) + { + AssertWarn(false, "Stack underflow in SimSet::popObject"); + return; + } + + lock(); + SimObject* object = objectList.last(); + objectList.pop_back(); + + clearNotify( object ); + unlock(); + + getSetModificationSignal().trigger( SetObjectRemoved, this, object ); + if( object->isProperlyAdded() ) + onObjectRemoved_callback( object ); +} + +//----------------------------------------------------------------------------- + +void SimSet::scriptSort( const String &scriptCallbackFn ) +{ + lock(); + objectList.scriptSort( scriptCallbackFn ); + unlock(); +} + +//----------------------------------------------------------------------------- + +void SimSet::callOnChildren( const String &method, S32 argc, const char *argv[], bool executeOnChildGroups ) +{ + // Prep the arguments for the console exec... + // Make sure and leave args[1] empty. + const char* args[21]; + args[0] = method.c_str(); + for (S32 i = 0; i < argc; i++) + args[i + 2] = argv[i]; + + for( iterator i = begin(); i != end(); i++ ) + { + SimObject *childObj = static_cast(*i); + + if( childObj->isMethod( method.c_str() ) ) + Con::execute(childObj, argc + 2, args); + + if( executeOnChildGroups ) + { + SimSet* childSet = dynamic_cast(*i); + if ( childSet ) + childSet->callOnChildren( method, argc, argv, executeOnChildGroups ); + } + } +} + +//----------------------------------------------------------------------------- + +U32 SimSet::sizeRecursive() +{ + U32 count = 0; + + for ( iterator i = begin(); i != end(); i++ ) + { + count++; + + SimSet* childSet = dynamic_cast(*i); + if ( childSet ) + count += childSet->sizeRecursive(); + } + + return count; +} + +//----------------------------------------------------------------------------- + +bool SimSet::reOrder( SimObject *obj, SimObject *target ) +{ + MutexHandle handle; + handle.lock(mMutex); + + iterator itrS, itrD; + if ( (itrS = find(begin(),end(),obj)) == end() ) + { + // object must be in list + return false; + } + + if ( obj == target ) + { + // don't reorder same object but don't indicate error + return true; + } + + if ( !target ) + { + // if no target, then put to back of list + + // don't move if already last object + if ( itrS != (end()-1) ) + { + // remove object from its current location and push to back of list + objectList.erase(itrS); + objectList.push_back(obj); + } + } + else + { + // if target, insert object in front of target + if ( (itrD = find(begin(),end(),target)) == end() ) + // target must be in list + return false; + + objectList.erase(itrS); + + // once itrS has been erased, itrD won't be pointing at the + // same place anymore - re-find... + itrD = find(begin(),end(),target); + objectList.insert(itrD, obj); + } + + return true; +} + +//----------------------------------------------------------------------------- + +void SimSet::onDeleteNotify(SimObject *object) +{ + removeObject(object); + Parent::onDeleteNotify(object); +} + +//----------------------------------------------------------------------------- + +void SimSet::onRemove() +{ + MutexHandle handle; + handle.lock( mMutex ); + + if( !objectList.empty() ) + { + objectList.sortId(); + + // This backwards iterator loop doesn't work if the + // list is empty, check the size first. + + for( SimObjectList::iterator ptr = objectList.end() - 1; + ptr >= objectList.begin(); ptr -- ) + clearNotify( *ptr ); + } + + handle.unlock(); + + Parent::onRemove(); +} + +//----------------------------------------------------------------------------- + +void SimSet::write(Stream &stream, U32 tabStop, U32 flags) +{ + MutexHandle handle; + handle.lock(mMutex); + + // export selected only? + if((flags & SelectedOnly) && !isSelected()) + { + for(U32 i = 0; i < size(); i++) + (*this)[i]->write(stream, tabStop, flags); + + return; + + } + + stream.writeTabs( tabStop ); + char buffer[ 2048 ]; + const U32 bufferWriteLen = dSprintf( buffer, sizeof( buffer ), "new %s(%s) {\r\n", getClassName(), getName() && !( flags & NoName ) ? getName() : "" ); + stream.write( bufferWriteLen, buffer ); + writeFields( stream, tabStop + 1 ); + + if(size()) + { + stream.write(2, "\r\n"); + for(U32 i = 0; i < size(); i++) + { + SimObject* child = ( *this )[ i ]; + if( child->getCanSave() ) + child->write(stream, tabStop + 1, flags); + } + } + + stream.writeTabs(tabStop); + stream.write(4, "};\r\n"); +} + +//----------------------------------------------------------------------------- + +void SimSet::clear() +{ + lock(); + + while( !empty() ) + popObject(); + + unlock(); + + getSetModificationSignal().trigger( SetCleared, this, NULL ); +} + +//----------------------------------------------------------------------------- + +//UNSAFE +void SimSet::deleteAllObjects() +{ + lock(); + while( !empty() ) + { + SimObject* object = objectList.last(); + objectList.pop_back(); + + object->deleteObject(); + } + unlock(); +} + +//----------------------------------------------------------------------------- + +SimObject* SimSet::findObject( SimObject* object ) +{ + bool found = false; + lock(); + for( SimSet::iterator iter = begin(); iter != end(); ++ iter ) + if( *iter == object ) + { + found = true; + break; + } + unlock(); + + if( found ) + return object; + + return NULL; +} + +//----------------------------------------------------------------------------- + +SimObject* SimSet::findObject( const char *namePath ) +{ + // find the end of the object name + S32 len; + for(len = 0; namePath[len] != 0 && namePath[len] != '/'; len++) + ; + + StringTableEntry stName = StringTable->lookupn(namePath, len); + if(!stName) + return NULL; + + lock(); + for(SimSet::iterator i = begin(); i != end(); i++) + { + if((*i)->getName() == stName) + { + unlock(); + if(namePath[len] == 0) + return *i; + return (*i)->findObject(namePath + len + 1); + } + } + unlock(); + return NULL; +} + +//----------------------------------------------------------------------------- + +SimObject* SimSet::findObjectByInternalName(StringTableEntry internalName, bool searchChildren) +{ + iterator i; + for (i = begin(); i != end(); i++) + { + SimObject *childObj = static_cast(*i); + if(childObj->getInternalName() == internalName) + return childObj; + else if (searchChildren) + { + SimSet* childSet = dynamic_cast(*i); + if (childSet) + { + SimObject* found = childSet->findObjectByInternalName(internalName, searchChildren); + if (found) return found; + } + } + } + + return NULL; +} + +//----------------------------------------------------------------------------- + +SimObject* SimSet::findObjectByLineNumber(const char* fileName, S32 declarationLine, bool searchChildren) +{ + if (!fileName) + return NULL; + + if (declarationLine < 0) + return NULL; + + StringTableEntry fileEntry = StringTable->insert(fileName); + + for (iterator i = begin(); i != end(); i++) + { + SimObject *childObj = static_cast(*i); + + if(childObj->getFilename() == fileEntry && childObj->getDeclarationLine() == declarationLine) + return childObj; + else if (searchChildren) + { + SimSet* childSet = dynamic_cast(*i); + + if (childSet) + { + SimObject* found = childSet->findObjectByLineNumber(fileName, declarationLine, searchChildren); + if (found) + return found; + } + } + } + + return NULL; +} + +//----------------------------------------------------------------------------- + +SimObject* SimSet::getRandom() +{ + if (size() > 0) + return objectList[mRandI(0, size() - 1)]; + + return NULL; +} + +//----------------------------------------------------------------------------- + +SimSet* SimSet::clone() +{ + // Clone the set object. + + SimObject* object = Parent::clone(); + SimSet* set = dynamic_cast< SimSet* >( object ); + if( !set ) + { + object->deleteObject(); + return NULL; + } + + // Add all object in the set. + + for( iterator iter = begin(); iter != end(); ++ iter ) + set->addObject( *iter ); + + return set; +} + +//----------------------------------------------------------------------------- + +inline void SimSetIterator::Stack::push_back(SimSet* set) +{ + increment(); + last().set = set; + last().itr = set->begin(); +} + +//----------------------------------------------------------------------------- + +SimSetIterator::SimSetIterator(SimSet* set) +{ + VECTOR_SET_ASSOCIATION(stack); + + if (!set->empty()) + stack.push_back(set); +} + +//----------------------------------------------------------------------------- + +SimObject* SimSetIterator::operator++() +{ + SimSet* set; + if ((set = dynamic_cast(*stack.last().itr)) != 0) + { + if (!set->empty()) + { + stack.push_back(set); + return *stack.last().itr; + } + } + + while (++stack.last().itr == stack.last().set->end()) + { + stack.pop_back(); + if (stack.empty()) + return 0; + } + return *stack.last().itr; +} + +//============================================================================= +// SimGroup. +//============================================================================= +// MARK: ---- SimGroup ---- + +//----------------------------------------------------------------------------- + +SimGroup::~SimGroup() +{ + for( iterator itr = begin(); itr != end(); itr ++ ) + mNameDictionary.remove(*itr); +} + +//----------------------------------------------------------------------------- + +void SimGroup::_addObject( SimObject* obj, bool forcePushBack ) +{ + // Make sure we aren't adding ourself. This isn't the most robust check + // but it should be good enough to prevent some self-foot-shooting. + if( obj == this ) + { + Con::errorf( "SimGroup::addObject - (%d) can't add self!", getIdString() ); + return; + } + + if( obj->getGroup() == this ) + return; + + lock(); + + obj->incRefCount(); + + if( obj->getGroup() ) + obj->getGroup()->removeObject( obj ); + + if( forcePushBack ? objectList.pushBack( obj ) : objectList.pushBackForce( obj ) ) + { + mNameDictionary.insert( obj ); + obj->mGroup = this; + + obj->onGroupAdd(); + + getSetModificationSignal().trigger( SetObjectAdded, this, obj ); + if( obj->isProperlyAdded() ) + onObjectAdded_callback( obj ); + } + else + obj->decRefCount(); + + unlock(); + + // SimObjects will automatically remove them from their group + // when deleted so we don't hook up a delete notification. +} + +//----------------------------------------------------------------------------- + +void SimGroup::addObject( SimObject* obj ) +{ + _addObject( obj ); +} + +//----------------------------------------------------------------------------- + +void SimGroup::removeObject( SimObject* obj ) +{ + lock(); + _removeObjectNoLock( obj ); + unlock(); +} + +//----------------------------------------------------------------------------- + +void SimGroup::_removeObjectNoLock( SimObject* obj ) +{ + if( obj->mGroup == this ) + { + obj->onGroupRemove(); + + mNameDictionary.remove( obj ); + objectList.remove( obj ); + obj->mGroup = 0; + + getSetModificationSignal().trigger( SetObjectRemoved, this, obj ); + if( obj->isProperlyAdded() ) + onObjectRemoved_callback( obj ); + obj->decRefCount(); + } +} + +//----------------------------------------------------------------------------- + +void SimGroup::pushObject( SimObject* object ) +{ + _addObject( object, true ); +} + +//----------------------------------------------------------------------------- + +void SimGroup::popObject() +{ + MutexHandle handle; + handle.lock( mMutex ); + + if( objectList.empty() ) + { + AssertWarn( false, "SimGroup::popObject - Stack underflow" ); + return; + } + + SimObject* object = objectList.last(); + objectList.pop_back(); + + object->onGroupRemove(); + object->mGroup = NULL; + + clearNotify( object ); + mNameDictionary.remove( object ); + + getSetModificationSignal().trigger( SetObjectAdded, this, object ); + if( object->isProperlyAdded() ) + onObjectRemoved_callback( object ); + + object->decRefCount(); +} + +//----------------------------------------------------------------------------- + +void SimGroup::onRemove() +{ + lock(); + if( !objectList.empty() ) + { + objectList.sortId(); + clear(); + } + SimObject::onRemove(); + unlock(); +} + +//----------------------------------------------------------------------------- + +void SimGroup::clear() +{ + lock(); + while( size() > 0 ) + { + SimObject* object = objectList.last(); + object->onGroupRemove(); + + objectList.pop_back(); + mNameDictionary.remove( object ); + object->mGroup = 0; + + getSetModificationSignal().trigger( SetObjectRemoved, this, object ); + if( object->isProperlyAdded() ) + onObjectRemoved_callback( object ); + + if( engineAPI::gUseConsoleInterop ) + object->deleteObject(); + else + object->decRefCount(); + } + unlock(); + + getSetModificationSignal().trigger( SetCleared, this, NULL ); +} + +//----------------------------------------------------------------------------- + +SimObject *SimGroup::findObject(const char *namePath) +{ + // find the end of the object name + S32 len; + for(len = 0; namePath[len] != 0 && namePath[len] != '/'; len++) + ; + + StringTableEntry stName = StringTable->lookupn(namePath, len); + if(!stName) + return NULL; + + SimObject *root = mNameDictionary.find( stName ); + if( !root ) + return NULL; + + if(namePath[len] == 0) + return root; + + return root->findObject(namePath + len + 1); +} + +//----------------------------------------------------------------------------- + +SimGroup* SimGroup::clone() +{ + // Skip SimSet::clone since we do not want to steal the child objects + // from this group. + + SimObject* object = SimObject::clone(); + SimGroup* group = dynamic_cast< SimGroup* >( object ); + if( !group ) + { + object->deleteObject(); + return NULL; + } + + return group; +} + +//----------------------------------------------------------------------------- + +SimGroup* SimGroup::deepClone() +{ + // Clone the group object. + + SimObject* object = Parent::deepClone(); + SimGroup* group = dynamic_cast< SimGroup* >( object ); + if( !group ) + { + object->deleteObject(); + return NULL; + } + + // Clone all child objects. + + for( iterator iter = begin(); iter != end(); ++ iter ) + group->addObject( ( *iter )->deepClone() ); + + return group; +} + +//----------------------------------------------------------------------------- + +bool SimGroup::processArguments(S32, const char **) +{ + return true; +} + +//----------------------------------------------------------------------------- + +SimObject* SimGroupIterator::operator++() +{ + SimGroup* set; + if ((set = dynamic_cast(*stack.last().itr)) != 0) + { + if (!set->empty()) + { + stack.push_back(set); + return *stack.last().itr; + } + } + + while (++stack.last().itr == stack.last().set->end()) + { + stack.pop_back(); + if (stack.empty()) + return 0; + } + return *stack.last().itr; +} + +//============================================================================= +// API. +//============================================================================= +// MARK: ---- API ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimSet, listObjects, void, (),, + "Dump a list of all objects contained in the set to the console." ) +{ + object->lock(); + SimSet::iterator itr; + for(itr = object->begin(); itr != object->end(); itr++) + { + SimObject *obj = *itr; + bool isSet = dynamic_cast(obj) != 0; + const char *name = obj->getName(); + if(name) + Con::printf(" %d,\"%s\": %s %s", obj->getId(), name, + obj->getClassName(), isSet ? "(g)":""); + else + Con::printf(" %d: %s %s", obj->getId(), obj->getClassName(), + isSet ? "(g)" : ""); + } + object->unlock(); +} + +//----------------------------------------------------------------------------- + +DEFINE_CALLIN( fnSimSet_add, add, SimSet, void, ( SimSet* set, SimObject* object ),,, + "Add the given object to the set.\n" + "@param object An object." ) +{ + if( object ) + set->addObject( object ); +} + +ConsoleMethod( SimSet, add, void, 3, 0, + "( SimObject objects... ) Add the given objects to the set.\n" + "@param objects The objects to add to the set." ) +{ + for(S32 i = 2; i < argc; i++) + { + SimObject *obj = Sim::findObject( argv[ i ] ); + if(obj) + object->addObject( obj ); + else + Con::printf("Set::add: Object \"%s\" doesn't exist", argv[ i ] ); + } +} + +//----------------------------------------------------------------------------- + +DEFINE_CALLIN( fnSimSet_remove, remove, SimSet, void, ( SimSet* set, SimObject* object ),,, + "Remove the given object from the set.\n" + "@param object An object." ) +{ + if( object ) + set->removeObject( object ); +} + +ConsoleMethod( SimSet, remove, void, 3, 0, + "( SimObject objects... ) Remove the given objects from the set.\n" + "@param objects The objects to remove from the set." ) +{ + for(S32 i = 2; i < argc; i++) + { + SimObject *obj = Sim::findObject(argv[i]); + object->lock(); + if(obj && object->find(object->begin(),object->end(),obj) != object->end()) + object->removeObject(obj); + else + Con::printf("Set::remove: Object \"%s\" does not exist in set", argv[i]); + object->unlock(); + } +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimSet, clear, void, (),, + "Remove all objects from the set." ) +{ + object->clear(); +} + +//----------------------------------------------------------------------------- + +//UNSAFE; don't want this in the new API +ConsoleMethod( SimSet, deleteAllObjects, void, 2, 2, "() Delete all objects in the set." ) +{ + object->deleteAllObjects(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimSet, getRandom, SimObject*, (),, + "Return a random object from the set.\n" + "@return A randomly selected object from the set or -1 if the set is empty." ) +{ + return object->getRandom(); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( SimSet, callOnChildren, void, 3, 0, + "( string method, string args... ) Call a method on all objects contained in the set.\n\n" + "@param method The name of the method to call.\n" + "@param args The arguments to the method.\n\n" + "@note This method recurses into all SimSets that are children to the set.\n\n" + "@see callOnChildrenNoRecurse" ) +{ + object->callOnChildren( argv[2], argc - 3, argv + 3 ); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( SimSet, callOnChildrenNoRecurse, void, 3, 0, + "( string method, string args... ) Call a method on all objects contained in the set.\n\n" + "@param method The name of the method to call.\n" + "@param args The arguments to the method.\n\n" + "@note This method does not recurse into child SimSets.\n\n" + "@see callOnChildren" ) +{ + object->callOnChildren( argv[2], argc - 3, argv + 3, false ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimSet, reorderChild, void, ( SimObject* child1, SimObject* child2 ),, + "Make sure child1 is ordered right before child2 in the set.\n" + "@param child1 The first child. The object must already be contained in the set.\n" + "@param child2 The second child. The object must already be contained in the set." ) +{ + SimObject* pObject = child1; + SimObject* pTarget = child2; + + if(pObject && pTarget) + { + object->reOrder(pObject,pTarget); + } +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimSet, getCount, S32, (),, + "Get the number of objects contained in the set.\n" + "@return The number of objects contained in the set." ) +{ + return object->size(); +} + +//----------------------------------------------------------------------------- + +DEFINE_CALLIN( fnSimSet_getCountRecursive, getCountRecursive, SimSet, U32, ( SimSet* set ),,, + "Get the number of direct and indirect child objects contained in the set.\n" + "@return The number of objects contained in the set as well as in other sets contained directly or indirectly in the set." ) +{ + return set->sizeRecursive(); +} + +ConsoleMethod( SimSet, getFullCount, S32, 2, 2, "() Get the number of direct and indirect child objects contained in the set.\n" + "@return The number of objects contained in the set as well as in other sets contained directly or indirectly in the set." ) +{ + return object->sizeRecursive(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimSet, getObject, SimObject*, ( U32 index ),, + "Get the object at the given index.\n" + "@param index The object index.\n" + "@return The object at the given index or -1 if index is out of range." ) +{ + if( index < 0 || index >= object->size() ) + { + Con::errorf( "Set::getObject - index out of range." ); + return NULL; + } + + return ( *object )[ index ]; +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimSet, getObjectIndex, S32, ( SimObject* obj ),, + "Return the index of the given object in this set.\n" + "@param obj The object for which to return the index. Must be contained in the set.\n" + "@return The index of the object or -1 if the object is not contained in the set." ) +{ + if( !obj ) + return -1; + + object->lock(); + S32 count = 0; + for( SimSet::iterator i = object->begin(); i != object->end(); i++) + { + if( *i == obj ) + { + object->unlock(); + return count; + } + + ++count; + } + object->unlock(); + + return -1; +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimSet, isMember, bool, ( SimObject* obj ),, + "Test whether the given object belongs to the set.\n" + "@param obj The object.\n" + "@return True if the object is contained in the set; false otherwise." ) +{ + if( !obj ) + return false; + + return ( object->find( object->begin(), object->end(), obj ) != object->end() ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimSet, findObjectByInternalName, SimObject*, ( const char* internalName, bool searchChildren ), ( false ), + "Find an object in the set by its internal name.\n" + "@param internalName The internal name of the object to look for.\n" + "@param searchChildren If true, SimSets contained in the set will be recursively searched for the object.\n" + "@return The object with the given internal name or 0 if no match was found.\n" ) +{ + StringTableEntry pcName = StringTable->insert( internalName ); + return object->findObjectByInternalName( pcName, searchChildren ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimSet, bringToFront, void, ( SimObject* obj ),, + "Make the given object the first object in the set.\n" + "@param obj The object to bring to the frontmost position. Must be contained in the set." ) +{ + if( obj ) + object->bringObjectToFront( obj ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimSet, pushToBack, void, ( SimObject* obj ),, + "Make the given object the last object in the set.\n" + "@param obj The object to bring to the last position. Must be contained in the set." ) +{ + if( obj ) + object->pushObjectToBack( obj ); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( SimSet, sort, void, 3, 3, "( string callbackFunction ) Sort the objects in the set using the given comparison function.\n" + "@param callbackFunction Name of a function that takes two object arguments A and B and returns -1 if A is less, 1 if B is less, and 0 if both are equal." ) +{ + object->scriptSort( argv[2] ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SimSet, acceptsAsChild, bool, ( SimObject* obj ),, + "Test whether the given object may be added to the set.\n" + "@param obj The object to test for potential membership.\n" + "@return True if the object may be added to the set, false otherwise." ) +{ + if( !obj ) + return false; + + return object->acceptsAsChild( obj ); +} diff --git a/Engine/source/console/simSet.h b/Engine/source/console/simSet.h new file mode 100644 index 000000000..baf39c787 --- /dev/null +++ b/Engine/source/console/simSet.h @@ -0,0 +1,464 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SIMSET_H_ +#define _SIMSET_H_ + +#ifndef _SIMOBJECT_H_ +#include "console/simObject.h" +#endif + +#ifndef _SIMOBJECTLIST_H_ +#include "console/simObjectList.h" +#endif + +#ifndef _SIMDICTIONARY_H_ +#include "console/simDictionary.h" +#endif + +#ifndef _TSIGNAL_H_ +#include "core/util/tSignal.h" +#endif + + +//--------------------------------------------------------------------------- +/// A set of SimObjects. +/// +/// It is often necessary to keep track of an arbitrary set of SimObjects. +/// For instance, Torque's networking code needs to not only keep track of +/// the set of objects which need to be ghosted, but also the set of objects +/// which must always be ghosted. It does this by working with two +/// sets. The first of these is the RootGroup (which is actually a SimGroup) +/// and the second is the GhostAlwaysSet, which contains objects which must +/// always be ghosted to the client. +/// +/// Some general notes on SimSets: +/// - Membership is not exclusive. A SimObject may be a member of multiple +/// SimSets. +/// - A SimSet does not destroy subobjects when it is destroyed. +/// - A SimSet may hold an arbitrary number of objects. +/// +/// Using SimSets, the code to work with these two sets becomes +/// relatively straightforward: +/// +/// @code +/// // (Example from netObject.cc) +/// // To iterate over all the objects in the Sim: +/// for (SimSetIterator obj(Sim::getRootGroup()); *obj; ++obj) +/// { +/// NetObject* nobj = dynamic_cast(*obj); +/// +/// if (nobj) +/// { +/// // ... do things ... +/// } +/// } +/// +/// // (Example from netGhost.cc) +/// // To iterate over the ghostAlways set. +/// SimSet* ghostAlwaysSet = Sim::getGhostAlwaysSet(); +/// SimSet::iterator i; +/// +/// U32 sz = ghostAlwaysSet->size(); +/// S32 j; +/// +/// for(i = ghostAlwaysSet->begin(); i != ghostAlwaysSet->end(); i++) +/// { +/// NetObject *obj = (NetObject *)(*i); +/// +/// /// ... do things with obj... +/// } +/// @endcode +/// +class SimSet: public SimObject +{ + public: + + typedef SimObject Parent; + + enum SetModification + { + SetCleared, + SetObjectAdded, + SetObjectRemoved + }; + + /// Signal for letting observers know when objects are added to or removed from + /// the set. + /// + /// @param modification In what way the set has been modified. + /// @param set The set that has been modified. + /// @param object If #modification is #SetObjectAdded or #SetObjectRemoved, this is + /// the object that has been added or removed. Otherwise NULL. + typedef Signal< void( SetModification modification, SimSet* set, SimObject* object ) > SetModificationSignal; + + protected: + + SimObjectList objectList; + void *mMutex; + + /// Signal that is triggered when objects are added or removed from the set. + SetModificationSignal mSetModificationSignal; + + /// @name Callbacks + /// @{ + + DECLARE_CALLBACK( void, onObjectAdded, ( SimObject* object ) ); + DECLARE_CALLBACK( void, onObjectRemoved, ( SimObject* object ) ); + + /// @} + + public: + + SimSet(); + ~SimSet(); + + /// Return the signal that is triggered when an object is added to or removed + /// from the set. + const SetModificationSignal& getSetModificationSignal() const { return mSetModificationSignal; } + SetModificationSignal& getSetModificationSignal() { return mSetModificationSignal; } + + /// @name STL Interface + /// @{ + + /// + typedef SimObjectList::iterator iterator; + typedef SimObjectList::value_type value; + SimObject* front() { return objectList.front(); } + SimObject* first() { return objectList.first(); } + SimObject* last() { return objectList.last(); } + bool empty() const { return objectList.empty(); } + S32 size() const { return objectList.size(); } + iterator begin() { return objectList.begin(); } + iterator end() { return objectList.end(); } + value operator[] (S32 index) { return objectList[U32(index)]; } + + iterator find( iterator first, iterator last, SimObject *obj) + { return ::find(first, last, obj); } + + /// Reorder the position of "obj" to either be the last object in the list or, if + /// "target" is given, to come before "target" in the list of children. + virtual bool reOrder( SimObject *obj, SimObject *target=0 ); + + /// Return the object at the given index. + SimObject* at(S32 index) const { return objectList.at(index); } + + /// Remove all objects from this set. + virtual void clear(); + + /// @} + + /// @name Set Management + /// @{ + + /// Add the given object to the set. + /// @param object Object to add to the set. + virtual void addObject( SimObject* object ); + + /// Remove the given object from the set. + /// @param object Object to remove from the set. + virtual void removeObject( SimObject* object ); + + /// Add the given object to the end of the object list of this set. + /// @param object Object to add to the set. + virtual void pushObject( SimObject* object ); + + /// Return true if this set accepts the given object as a child. + /// This method should be overridden for set classes that restrict membership. + virtual bool acceptsAsChild( SimObject* object ) const { return true; } + + /// Deletes all the objects in the set. + void deleteAllObjects(); + + /// Remove an object from the end of the list. + virtual void popObject(); + + void bringObjectToFront(SimObject* obj) { reOrder(obj, front()); } + void pushObjectToBack(SimObject* obj) { reOrder(obj, NULL); } + + /// Performs a sort of the objects in the set using a script + /// callback function to do the comparison. + /// + /// An example script sort callback: + /// + /// @code + /// function sortByName( %object1, %object2 ) + /// { + /// return strcmp( %object1.getName(), %object2.getName() ); + /// } + /// @endcode + /// + /// Note: You should never modify the SimSet itself while in + /// the sort callback function as it can cause a deadlock. + /// + void scriptSort( const String &scriptCallbackFn ); + + /// @} + + void callOnChildren( const String &method, S32 argc, const char *argv[], bool executeOnChildGroups = true ); + + /// Return the number of objects in this set as well as all sets that are contained + /// in this set and its children. + /// + /// @note The child sets themselves count towards the total too. + U32 sizeRecursive(); + + SimObject* findObjectByInternalName(StringTableEntry internalName, bool searchChildren = false); + SimObject* findObjectByLineNumber(const char* fileName, S32 declarationLine, bool searchChildren = false); + + /// Find the given object in this set. Returns NULL if the object + /// is not part of this set. + SimObject* findObject( SimObject* object ); + + /// Add all child objects ( including children of children ) to the foundObjects + /// Vector which are of type T. + + template< class T > + void findObjectByType( Vector &foundObjects ); + + /// Add all child objects ( including children of children ) to the foundObjects + /// Vector which are of type T and for which DecideAddObjectCallback return true; + + template< class T > + void findObjectByCallback( bool ( *fn )( T* ), Vector& foundObjects ); + + SimObject* getRandom(); + + inline void lock() + { + #ifdef TORQUE_MULTITHREAD + Mutex::lockMutex(mMutex); + #endif + } + + void unlock() + { + #ifdef TORQUE_MULTITHREAD + Mutex::unlockMutex(mMutex); + #endif + } + + #ifdef TORQUE_DEBUG_GUARD + inline void _setVectorAssoc( const char *file, const U32 line ) + { + objectList.setFileAssociation( file, line ); + } + #endif + + // SimObject. + DECLARE_CONOBJECT( SimSet ); + + virtual void onRemove(); + virtual void onDeleteNotify(SimObject *object); + + virtual SimObject* findObject( const char* name ); + + virtual void write(Stream &stream, U32 tabStop, U32 flags = 0); + virtual bool writeObject(Stream *stream); + virtual bool readObject(Stream *stream); + + virtual SimSet* clone(); +}; + +#ifdef TORQUE_DEBUG_GUARD +# define SIMSET_SET_ASSOCIATION( x ) x._setVectorAssoc( __FILE__, __LINE__ ) +#else +# define SIMSET_SET_ASSOCIATION( x ) +#endif + +template< class T > +void SimSet::findObjectByType( Vector &foundObjects ) +{ + T *curObj; + SimSet *curSet; + + lock(); + + // Loop through our child objects. + + SimObjectList::iterator itr = objectList.begin(); + + for ( ; itr != objectList.end(); itr++ ) + { + curObj = dynamic_cast( *itr ); + curSet = dynamic_cast( *itr ); + + // If child object is a set, call recursively into it. + if ( curSet ) + curSet->findObjectByType( foundObjects ); + + // Add this child object if appropriate. + if ( curObj ) + foundObjects.push_back( curObj ); + } + + // Add this object if appropriate. + curObj = dynamic_cast(this); + if ( curObj ) + foundObjects.push_back( curObj ); + + unlock(); +} + +template< class T > +void SimSet::findObjectByCallback( bool ( *fn )( T* ), Vector &foundObjects ) +{ + T *curObj; + SimSet *curSet; + + lock(); + + // Loop through our child objects. + + SimObjectList::iterator itr = objectList.begin(); + + for ( ; itr != objectList.end(); itr++ ) + { + curObj = dynamic_cast( *itr ); + curSet = dynamic_cast( *itr ); + + // If child object is a set, call recursively into it. + if ( curSet ) + curSet->findObjectByCallback( fn, foundObjects ); + + // Add this child object if appropriate. + if ( curObj && ( fn == NULL || fn( curObj ) ) ) + foundObjects.push_back( curObj ); + } + + // Add this object if appropriate. + curObj = dynamic_cast(this); + if ( curObj && ( fn == NULL || fn( curObj ) ) ) + foundObjects.push_back( curObj ); + + unlock(); +} + +/// An iterator that recursively and exhaustively traverses the contents +/// of a SimSet. +/// +/// @see SimSet +class SimSetIterator +{ +protected: + struct Entry { + SimSet* set; + SimSet::iterator itr; + }; + class Stack: public Vector { + public: + void push_back(SimSet*); + }; + Stack stack; + +public: + SimSetIterator(SimSet*); + SimObject* operator++(); + SimObject* operator*() { + return stack.empty()? 0: *stack.last().itr; + } +}; + +//--------------------------------------------------------------------------- +/// A group of SimObjects. +/// +/// A SimGroup is a stricter form of SimSet. SimObjects may only be a member +/// of a single SimGroup at a time. +/// +/// The SimGroup will automatically enforce the single-group-membership rule. +/// +/// @code +/// // From engine/sim/simPath.cc - getting a pointer to a SimGroup +/// SimGroup* pMissionGroup = dynamic_cast(Sim::findObject("MissionGroup")); +/// +/// // From game/trigger.cc:46 - iterating over a SimObject's group. +/// SimObject* trigger = ...; +/// SimGroup* pGroup = trigger->getGroup(); +/// for (SimGroup::iterator itr = pGroup->begin(); itr != pGroup->end(); itr++) +/// { +/// // do something with *itr +/// } +/// @endcode +class SimGroup: public SimSet +{ + public: + + typedef SimSet Parent; + + friend class SimManager; + friend class SimObject; + + private: + + SimNameDictionary mNameDictionary; + + void _addObject( SimObject* object, bool forcePushBack = false ); + void _removeObjectNoLock( SimObject* ); + + public: + + ~SimGroup(); + + void addObject( SimObject* object, SimObjectId id); + void addObject( SimObject* object, const char* name ); + + // SimSet. + virtual void addObject( SimObject* object ); + virtual void removeObject( SimObject* object ); + virtual void pushObject( SimObject* object ); + virtual void popObject(); + virtual void clear(); + + virtual SimGroup* clone(); + virtual SimGroup* deepClone(); + + virtual SimObject* findObject(const char* name); + virtual void onRemove(); + + virtual bool processArguments( S32 argc, const char** argv ); + + DECLARE_CONOBJECT( SimGroup ); +}; + +inline void SimGroup::addObject(SimObject* obj, SimObjectId id) +{ + obj->mId = id; + dSprintf( obj->mIdString, sizeof( obj->mIdString ), "%u", obj->mId ); + addObject( obj ); +} + +inline void SimGroup::addObject(SimObject *obj, const char *name) +{ + addObject( obj ); + obj->assignName(name); +} + +/// An iterator that recursively and exhaustively traverses all objects +/// in an SimGroup object tree. +class SimGroupIterator: public SimSetIterator +{ +public: + SimGroupIterator(SimGroup* grp): SimSetIterator(grp) {} + SimObject* operator++(); +}; + +#endif // _SIMSET_H_ diff --git a/Engine/source/console/stringStack.cpp b/Engine/source/console/stringStack.cpp new file mode 100644 index 000000000..6061747d4 --- /dev/null +++ b/Engine/source/console/stringStack.cpp @@ -0,0 +1,41 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/stringStack.h" + +void StringStack::getArgcArgv(StringTableEntry name, U32 *argc, const char ***in_argv, bool popStackFrame /* = false */) +{ + U32 startStack = mFrameOffsets[mNumFrames-1] + 1; + U32 argCount = getMin(mStartStackSize - startStack, (U32)MaxArgs - 1); + + *in_argv = mArgV; + mArgV[0] = name; + + for(U32 i = 0; i < argCount; i++) + mArgV[i+1] = mBuffer + mStartOffsets[startStack + i]; + argCount++; + + *argc = argCount; + + if(popStackFrame) + popFrame(); +} \ No newline at end of file diff --git a/Engine/source/console/stringStack.h b/Engine/source/console/stringStack.h new file mode 100644 index 000000000..a7db69103 --- /dev/null +++ b/Engine/source/console/stringStack.h @@ -0,0 +1,278 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _STRINGSTACK_H_ +#define _STRINGSTACK_H_ + +#ifndef _STRINGFUNCTIONS_H_ +#include "core/strings/stringFunctions.h" +#endif + +#ifndef _STRINGTABLE_H_ +#include "core/stringTable.h" +#endif + +#ifndef _CONSOLE_H_ +#include "console/console.h" +#endif + + +/// Core stack for interpreter operations. +/// +/// This class provides some powerful semantics for working with strings, and is +/// used heavily by the console interpreter. +struct StringStack +{ + enum { + MaxStackDepth = 1024, + MaxArgs = 20, + ReturnBufferSpace = 512 + }; + char *mBuffer; + U32 mBufferSize; + const char *mArgV[MaxArgs]; + U32 mFrameOffsets[MaxStackDepth]; + U32 mStartOffsets[MaxStackDepth]; + + U32 mNumFrames; + U32 mArgc; + + U32 mStart; + U32 mLen; + U32 mStartStackSize; + U32 mFunctionOffset; + + U32 mArgBufferSize; + char *mArgBuffer; + + void validateBufferSize(U32 size) + { + if(size > mBufferSize) + { + mBufferSize = size + 2048; + mBuffer = (char *) dRealloc(mBuffer, mBufferSize); + } + } + void validateArgBufferSize(U32 size) + { + if(size > mArgBufferSize) + { + mArgBufferSize = size + 2048; + mArgBuffer = (char *) dRealloc(mArgBuffer, mArgBufferSize); + } + } + StringStack() + { + mBufferSize = 0; + mBuffer = NULL; + mArgBufferSize = 0; + mArgBuffer = NULL; + mNumFrames = 0; + mStart = 0; + mLen = 0; + mStartStackSize = 0; + mFunctionOffset = 0; + validateBufferSize(8192); + validateArgBufferSize(2048); + } + ~StringStack() + { + if( mBuffer ) + dFree( mBuffer ); + if( mArgBuffer ) + dFree( mArgBuffer ); + } + + /// Set the top of the stack to be an integer value. + void setIntValue(U32 i) + { + validateBufferSize(mStart + 32); + dSprintf(mBuffer + mStart, 32, "%d", i); + mLen = dStrlen(mBuffer + mStart); + } + + /// Set the top of the stack to be a float value. + void setFloatValue(F64 v) + { + validateBufferSize(mStart + 32); + dSprintf(mBuffer + mStart, 32, "%g", v); + mLen = dStrlen(mBuffer + mStart); + } + + /// Return a temporary buffer we can use to return data. + char* getReturnBuffer(U32 size) + { + validateArgBufferSize(size); + return mArgBuffer; + } + + /// Return a buffer we can use for arguments. + /// + /// This updates the function offset. + char *getArgBuffer(U32 size) + { + validateBufferSize(mStart + mFunctionOffset + size); + char *ret = mBuffer + mStart + mFunctionOffset; + mFunctionOffset += size; + return ret; + } + + /// Clear the function offset. + void clearFunctionOffset() + { + mFunctionOffset = 0; + } + + /// Set a string value on the top of the stack. + void setStringValue(const char *s) + { + if(!s) + { + mLen = 0; + mBuffer[mStart] = 0; + return; + } + mLen = dStrlen(s); + + validateBufferSize(mStart + mLen + 2); + dStrcpy(mBuffer + mStart, s); + } + + /// Get the top of the stack, as a StringTableEntry. + /// + /// @note Don't free this memory! + inline StringTableEntry getSTValue() + { + return StringTable->insert(mBuffer + mStart); + } + + /// Get an integer representation of the top of the stack. + inline U32 getIntValue() + { + return dAtoi(mBuffer + mStart); + } + + /// Get a float representation of the top of the stack. + inline F64 getFloatValue() + { + return dAtof(mBuffer + mStart); + } + + /// Get a string representation of the top of the stack. + /// + /// @note This returns a pointer to the actual top of the stack, be careful! + inline const char *getStringValue() + { + return mBuffer + mStart; + } + + /// Advance the start stack, placing a zero length string on the top. + /// + /// @note You should use StringStack::push, not this, if you want to + /// properly push the stack. + void advance() + { + mStartOffsets[mStartStackSize++] = mStart; + mStart += mLen; + mLen = 0; + } + + /// Advance the start stack, placing a single character, null-terminated strong + /// on the top. + /// + /// @note You should use StringStack::push, not this, if you want to + /// properly push the stack. + void advanceChar(char c) + { + mStartOffsets[mStartStackSize++] = mStart; + mStart += mLen; + mBuffer[mStart] = c; + mBuffer[mStart+1] = 0; + mStart += 1; + mLen = 0; + } + + /// Push the stack, placing a zero-length string on the top. + void push() + { + advanceChar(0); + } + + inline void setLen(U32 newlen) + { + mLen = newlen; + } + + /// Pop the start stack. + void rewind() + { + mStart = mStartOffsets[--mStartStackSize]; + mLen = dStrlen(mBuffer + mStart); + } + + // Terminate the current string, and pop the start stack. + void rewindTerminate() + { + mBuffer[mStart] = 0; + mStart = mStartOffsets[--mStartStackSize]; + mLen = dStrlen(mBuffer + mStart); + } + + /// Compare 1st and 2nd items on stack, consuming them in the process, + /// and returning true if they matched, false if they didn't. + U32 compare() + { + // Figure out the 1st and 2nd item offsets. + U32 oldStart = mStart; + mStart = mStartOffsets[--mStartStackSize]; + + // Compare current and previous strings. + U32 ret = !dStricmp(mBuffer + mStart, mBuffer + oldStart); + + // Put an empty string on the top of the stack. + mLen = 0; + mBuffer[mStart] = 0; + + return ret; + } + + + void pushFrame() + { + mFrameOffsets[mNumFrames++] = mStartStackSize; + mStartOffsets[mStartStackSize++] = mStart; + mStart += ReturnBufferSpace; + validateBufferSize(0); + } + + void popFrame() + { + mStartStackSize = mFrameOffsets[--mNumFrames]; + mStart = mStartOffsets[mStartStackSize]; + mLen = 0; + } + + /// Get the arguments for a function call from the stack. + void getArgcArgv(StringTableEntry name, U32 *argc, const char ***in_argv, bool popStackFrame = false); +}; + +#endif diff --git a/Engine/source/console/telnetConsole.cpp b/Engine/source/console/telnetConsole.cpp new file mode 100644 index 000000000..a44bd3166 --- /dev/null +++ b/Engine/source/console/telnetConsole.cpp @@ -0,0 +1,322 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/telnetConsole.h" + +#include "console/engineAPI.h" +#include "core/strings/stringFunctions.h" +#include "platform/event.h" +#include "core/util/journal/process.h" +#include "core/module.h" + + +MODULE_BEGIN( TelnetConsole ) + + MODULE_INIT + { + TelnetConsole::create(); + } + + MODULE_SHUTDOWN + { + TelnetConsole::destroy(); + } + +MODULE_END; + + +TelnetConsole *TelConsole = NULL; + +void TelnetConsole::create() +{ + TelConsole = new TelnetConsole; + Process::notify(TelConsole, &TelnetConsole::process, PROCESS_FIRST_ORDER); +} + +void TelnetConsole::destroy() +{ + Process::remove(TelConsole, &TelnetConsole::process); + delete TelConsole; + TelConsole = NULL; +} + +DefineConsoleFunction( telnetSetParameters, void, ( int port, const char* consolePass, const char* listenPass, bool remoteEcho ), ( false ), + "@brief Initializes and open the telnet console.\n\n" + "@param port Port to listen on for console connections (0 will shut down listening).\n" + "@param consolePass Password for read/write access to console.\n" + "@param listenPass Password for read access to console.\n" + "@param remoteEcho [optional] Enable echoing back to the client, off by default.\n\n" + "@ingroup Debugging") +{ + if (TelConsole) + TelConsole->setTelnetParameters(port, consolePass, listenPass, remoteEcho); +} + +static void telnetCallback(U32 level, const char *consoleLine) +{ + TORQUE_UNUSED(level); + if (TelConsole) + TelConsole->processConsoleLine(consoleLine); +} + +TelnetConsole::TelnetConsole() +{ + Con::addConsumer(telnetCallback); + + mAcceptSocket = InvalidSocket; + mAcceptPort = -1; + mClientList = NULL; + mRemoteEchoEnabled = false; +} + +TelnetConsole::~TelnetConsole() +{ + Con::removeConsumer(telnetCallback); + if(mAcceptSocket != InvalidSocket) + Net::closeSocket(mAcceptSocket); + TelnetClient *walk = mClientList, *temp; + while(walk) + { + temp = walk->nextClient; + if(walk->socket != InvalidSocket) + Net::closeSocket(walk->socket); + delete walk; + walk = temp; + } +} + +void TelnetConsole::setTelnetParameters(S32 port, const char *telnetPassword, const char *listenPassword, bool remoteEcho) +{ + if(port == mAcceptPort) + return; + + mRemoteEchoEnabled = remoteEcho; + + if(mAcceptSocket != InvalidSocket) + { + Net::closeSocket(mAcceptSocket); + mAcceptSocket = InvalidSocket; + } + mAcceptPort = port; + if(mAcceptPort != -1 && mAcceptPort != 0) + { + mAcceptSocket = Net::openSocket(); + Net::bind(mAcceptSocket, mAcceptPort); + Net::listen(mAcceptSocket, 4); + + Net::setBlocking(mAcceptSocket, false); + } + dStrncpy(mTelnetPassword, telnetPassword, PasswordMaxLength); + dStrncpy(mListenPassword, listenPassword, PasswordMaxLength); +} + +void TelnetConsole::processConsoleLine(const char *consoleLine) +{ + if (mClientList==NULL) return; // just escape early. don't even do another step... + + // ok, spew this line out to all our subscribers... + S32 len = dStrlen(consoleLine)+1; + for(TelnetClient *walk = mClientList; walk; walk = walk->nextClient) + { + if(walk->state == FullAccessConnected || walk->state == ReadOnlyConnected) + { + Net::send(walk->socket, (const unsigned char*)consoleLine, len); + Net::send(walk->socket, (const unsigned char*)"\r\n", 2); + } + } +} + +void TelnetConsole::process() +{ + NetAddress address; + + if(mAcceptSocket != InvalidSocket) + { + // ok, see if we have any new connections: + NetSocket newConnection; + newConnection = Net::accept(mAcceptSocket, &address); + + if(newConnection != InvalidSocket) + { + Con::printf ("Telnet connection from %i.%i.%i.%i", + address.netNum[0], address.netNum[1], address.netNum[2], address.netNum[3]); + + TelnetClient *cl = new TelnetClient; + cl->socket = newConnection; + cl->curPos = 0; +#if defined(TORQUE_SHIPPING) && defined(TORQUE_DISABLE_TELNET_CONSOLE_PASSWORD) + // disable the password in a ship build? WTF. lets make an error: + PleaseMakeSureYouKnowWhatYouAreDoingAndCommentOutThisLineIfSo. +#elif !defined(TORQUE_SHIPPING) && defined(TORQUE_DISABLE_TELNET_CONSOLE_PASSWORD) + cl->state = FullAccessConnected; +#else + cl->state = PasswordTryOne; +#endif + + Net::setBlocking(newConnection, false); + + const char *prompt = Con::getVariable("Con::Prompt"); + char connectMessage[1024]; + dSprintf(connectMessage, sizeof(connectMessage), + "Torque Telnet Remote Console\r\n\r\n%s", + cl->state == FullAccessConnected ? prompt : "Enter Password:"); + + Net::send(cl->socket, (const unsigned char*)connectMessage, dStrlen(connectMessage)+1); + cl->nextClient = mClientList; + mClientList = cl; + } + } + + char recvBuf[256]; + char reply[1024]; + + // see if we have any input to process... + + for(TelnetClient *client = mClientList; client; client = client->nextClient) + { + S32 numBytes; + Net::Error err = Net::recv(client->socket, (unsigned char*)recvBuf, sizeof(recvBuf), &numBytes); + + if((err != Net::NoError && err != Net::WouldBlock) || numBytes == 0) + { + Net::closeSocket(client->socket); + client->socket = InvalidSocket; + continue; + } + + S32 replyPos = 0; + for(S32 i = 0; i < numBytes;i++) + { + if(recvBuf[i] == '\r') + continue; + // execute the current command + + if(recvBuf[i] == '\n') + { + reply[replyPos++] = '\r'; + reply[replyPos++] = '\n'; + + client->curLine[client->curPos] = 0; + client->curPos = 0; + + if(client->state == FullAccessConnected) + { + Net::send(client->socket, (const unsigned char*)reply, replyPos); + replyPos = 0; + + // Notify console of line to execute. + RawData rd; + rd.size = dStrlen(client->curLine) + 1; + rd.data = ( S8* ) client->curLine; + Con::smConsoleInput.trigger(rd); + + // note - send prompt next + const char *prompt = Con::getVariable("Con::Prompt"); + Net::send(client->socket, (const unsigned char*)prompt, dStrlen(prompt)); + } + else if(client->state == ReadOnlyConnected) + { + Net::send(client->socket, (const unsigned char*)reply, replyPos); + replyPos = 0; + } + else + { + client->state++; + if(!dStrncmp(client->curLine, mTelnetPassword, PasswordMaxLength)) + { + Net::send(client->socket, (const unsigned char*)reply, replyPos); + replyPos = 0; + + // send prompt + const char *prompt = Con::getVariable("Con::Prompt"); + Net::send(client->socket, (const unsigned char*)prompt, dStrlen(prompt)); + client->state = FullAccessConnected; + } + else if(!dStrncmp(client->curLine, mListenPassword, PasswordMaxLength)) + { + Net::send(client->socket, (const unsigned char*)reply, replyPos); + replyPos = 0; + + // send prompt + const char *listenConnected = "Connected.\r\n"; + Net::send(client->socket, (const unsigned char*)listenConnected, dStrlen(listenConnected)); + client->state = ReadOnlyConnected; + } + else + { + const char *sendStr; + if(client->state == DisconnectThisDude) + sendStr = "Too many tries... cya."; + else + sendStr = "Nope... try agian.\r\nEnter Password:"; + Net::send(client->socket, (const unsigned char*)sendStr, dStrlen(sendStr)); + if(client->state == DisconnectThisDude) + { + Net::closeSocket(client->socket); + client->socket = InvalidSocket; + } + } + } + } + else if(recvBuf[i] == '\b') + { + // pull the old backspace manuever... + if(client->curPos > 0) + { + client->curPos--; + if(client->state == FullAccessConnected) + { + reply[replyPos++] = '\b'; + reply[replyPos++] = ' '; + reply[replyPos++] = '\b'; + } + } + } + else if(client->curPos < Con::MaxLineLength-1) + { + client->curLine[client->curPos++] = recvBuf[i]; + // don't echo password chars... + if(client->state == FullAccessConnected) + reply[replyPos++] = recvBuf[i]; + } + } + + // Echo the character back to the user, unless the remote echo + // is disabled (by default) + if(replyPos && mRemoteEchoEnabled) + Net::send(client->socket, (const unsigned char*)reply, replyPos); + } + + TelnetClient ** walk = &mClientList; + TelnetClient *cl; + while((cl = *walk) != NULL) + { + if(cl->socket == InvalidSocket) + { + *walk = cl->nextClient; + delete cl; + } + else + walk = &cl->nextClient; + } +} diff --git a/Engine/source/console/telnetConsole.h b/Engine/source/console/telnetConsole.h new file mode 100644 index 000000000..431396725 --- /dev/null +++ b/Engine/source/console/telnetConsole.h @@ -0,0 +1,113 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TELNETCONSOLE_H_ +#define _TELNETCONSOLE_H_ + +#ifndef _CONSOLE_H_ +#include "console/console.h" +#endif +#ifndef _EVENT_H_ +#include "platform/event.h" +#endif +#include "platform/platformNet.h" + +/// Telnet admin console. +/// +/// Torque supports remote access to its console. This is most useful when +/// running a dedicated server, as you can remotely administer the game +/// (for instance, kicking people). In the context of a MMORPG, this sort of +/// functionality would be useful for managing a server. +/// +/// There are a number of products for Tribes2 which allow remote administration +/// via a nice GUI. +/// +/// @section telnetconsole_use Using the Telnet Console +/// +/// The TelnetConsole is designed to be used globally, so you don't instantiate +/// it like a normal class. +/// +class TelnetConsole +{ + NetSocket mAcceptSocket; + S32 mAcceptPort; + + enum { + PasswordMaxLength = 32 ///< Maximum length of the telnet and listen passwords. + }; + + bool mRemoteEchoEnabled; + char mTelnetPassword[PasswordMaxLength+1]; + char mListenPassword[PasswordMaxLength+1]; + + /// State of a TelnetClient. + enum State + { + PasswordTryOne, ///< Allow three password attempts. + PasswordTryTwo, + PasswordTryThree, + DisconnectThisDude, ///< If they've failed all three, disconnect them. + FullAccessConnected, ///< They presented the telnetPassword, they get full access. + ReadOnlyConnected ///< They presented the listenPassword, they get read only access. + }; + + /// Represents a connection to the telnet console. + /// + /// This is also a linked list. + struct TelnetClient + { + NetSocket socket; + char curLine[Con::MaxLineLength]; + S32 curPos; + S32 state; ///< State of the client. + /// @see TelnetConsole::State + TelnetClient *nextClient; + }; + TelnetClient *mClientList; + TelnetConsole(); + ~TelnetConsole(); + +public: + static void create(); ///< Initialize the telnet console. + static void destroy(); ///< Shut down the telnet console. + void process(); ///< Called by the main loop to let the console process commands + /// and connections. + + /// Configure the parameter for the telnet console. + /// + /// @param port Port on which to listen for connections. + /// @param telnetPassword Password for full access to the console. + /// @param listenPassword Password for read-only access to the console. + /// @param remoteEcho Enable/disable echoing input back to the client + void setTelnetParameters(S32 port, const char *telnetPassword, const char *listenPassword, bool remoteEcho = false); + + /// Callback to handle a line from the console. + /// + /// @note This is used internally by the class; you + /// shouldn't need to call it. + /// + /// @see Con::addConsumer() + void processConsoleLine(const char *line); +}; + +#endif + diff --git a/Engine/source/console/telnetDebugger.cpp b/Engine/source/console/telnetDebugger.cpp new file mode 100644 index 000000000..25fdca849 --- /dev/null +++ b/Engine/source/console/telnetDebugger.cpp @@ -0,0 +1,923 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/telnetDebugger.h" + +#include "core/frameAllocator.h" +#include "console/console.h" +#include "platform/event.h" +#include "core/stringTable.h" +#include "console/consoleInternal.h" +#include "console/ast.h" +#include "console/compiler.h" +#include "core/util/journal/process.h" +#include "core/module.h" + + +MODULE_BEGIN( TelnetDebugger ) + + MODULE_INIT + { + TelnetDebugger::create(); + } + + MODULE_SHUTDOWN + { + TelnetDebugger::destroy(); + } + +MODULE_END; + +// +// Enhanced TelnetDebugger for Torsion +// http://www.sickheadgames.com/torsion +// +// +// Debugger commands: +// +// CEVAL console line - evaluate the console line +// output: none +// +// BRKVARSET varName passct expr - NOT IMPLEMENTED! +// output: none +// +// BRKVARCLR varName - NOT IMPLEMENTED! +// output: none +// +// BRKSET file line clear passct expr - set a breakpoint on the file,line +// it must pass passct times for it to break and if clear is true, it +// clears when hit +// output: +// +// BRKNEXT - stop execution at the next breakable line. +// output: none +// +// BRKCLR file line - clear a breakpoint on the file,line +// output: none +// +// BRKCLRALL - clear all breakpoints +// output: none +// +// CONTINUE - continue execution +// output: RUNNING +// +// STEPIN - run until next statement +// output: RUNNING +// +// STEPOVER - run until next break <= current frame +// output: RUNNING +// +// STEPOUT - run until next break <= current frame - 1 +// output: RUNNING +// +// EVAL tag frame expr - evaluate the expr in the console, on the frame'th stack frame +// output: EVALOUT tag exprResult +// +// FILELIST - list script files loaded +// output: FILELISTOUT file1 file2 file3 file4 ... +// +// BREAKLIST file - get a list of breakpoint-able lines in the file +// output: BREAKLISTOUT file skipBreakPairs skiplinecount breaklinecount skiplinecount breaklinecount ... +// +// +// Other output: +// +// BREAK file1 line1 function1 file2 line2 function2 ... - Sent when the debugger hits a +// breakpoint. It lists out one file/line/function triplet for each stack level. +// The first one is the top of the stack. +// +// COUT console-output - echo of console output from engine +// +// BRKMOV file line newline - sent when a breakpoint is moved to a breakable line. +// +// BRKCLR file line - sent when a breakpoint cannot be moved to a breakable line on the client. +// + + +ConsoleFunction( dbgSetParameters, void, 3, 4, "(int port, string password, bool waitForClient)" + "Open a debug server port on the specified port, requiring the specified password, " + "and optionally waiting for the debug client to connect.\n" + "@internal Primarily used for Torsion and other debugging tools") +{ + if (TelDebugger) + TelDebugger->setDebugParameters(dAtoi(argv[1]), argv[2], argc > 3 ? dAtob(argv[3]) : false ); +} + +ConsoleFunction( dbgIsConnected, bool, 1, 1, "()" + "Returns true if a script debugging client is connected else return false.\n" + "@internal Primarily used for Torsion and other debugging tools") +{ + return TelDebugger && TelDebugger->isConnected(); +} + +ConsoleFunction( dbgDisconnect, void, 1, 1, "()" + "Forcibly disconnects any attached script debugging client.\n" + "@internal Primarily used for Torsion and other debugging tools") +{ + if (TelDebugger) + TelDebugger->disconnect(); +} + +static void debuggerConsumer(U32 level, const char *line) +{ + TORQUE_UNUSED(level); + if (TelDebugger) + TelDebugger->processConsoleLine(line); +} + +TelnetDebugger::TelnetDebugger() +{ + Con::addConsumer(debuggerConsumer); + + mAcceptPort = -1; + mAcceptSocket = InvalidSocket; + mDebugSocket = InvalidSocket; + + mState = NotConnected; + mCurPos = 0; + + mBreakpoints = NULL; + mBreakOnNextStatement = false; + mStackPopBreakIndex = -1; + mProgramPaused = false; + mWaitForClient = false; + + // Add the version number in a global so that + // scripts can detect the presence of the + // "enhanced" debugger features. + Con::evaluatef( "$dbgVersion = %d;", Version ); +} + +TelnetDebugger::Breakpoint **TelnetDebugger::findBreakpoint(StringTableEntry fileName, S32 lineNumber) +{ + Breakpoint **walk = &mBreakpoints; + Breakpoint *cur; + while((cur = *walk) != NULL) + { + // TODO: This assumes that the OS file names are case + // insensitive... Torque needs a dFilenameCmp() function. + if( dStricmp( cur->fileName, fileName ) == 0 && cur->lineNumber == U32(lineNumber)) + return walk; + walk = &cur->next; + } + return NULL; +} + + +TelnetDebugger::~TelnetDebugger() +{ + Con::removeConsumer(debuggerConsumer); + + if(mAcceptSocket != InvalidSocket) + Net::closeSocket(mAcceptSocket); + if(mDebugSocket != InvalidSocket) + Net::closeSocket(mDebugSocket); +} + +TelnetDebugger *TelDebugger = NULL; + +void TelnetDebugger::create() +{ + TelDebugger = new TelnetDebugger; + Process::notify(TelDebugger, &TelnetDebugger::process, PROCESS_FIRST_ORDER); +} + +void TelnetDebugger::destroy() +{ + Process::remove(TelDebugger, &TelnetDebugger::process); + delete TelDebugger; + TelDebugger = NULL; +} + +void TelnetDebugger::send(const char *str) +{ + Net::send(mDebugSocket, (const unsigned char*)str, dStrlen(str)); +} + +void TelnetDebugger::disconnect() +{ + if ( mDebugSocket != InvalidSocket ) + { + Net::closeSocket(mDebugSocket); + mDebugSocket = InvalidSocket; + } + + removeAllBreakpoints(); + + mState = NotConnected; + mProgramPaused = false; +} + +void TelnetDebugger::setDebugParameters(S32 port, const char *password, bool waitForClient) +{ + // Don't bail if same port... we might just be wanting to change + // the password. +// if(port == mAcceptPort) +// return; + + if(mAcceptSocket != InvalidSocket) + { + Net::closeSocket(mAcceptSocket); + mAcceptSocket = InvalidSocket; + } + mAcceptPort = port; + if(mAcceptPort != -1 && mAcceptPort != 0) + { + mAcceptSocket = Net::openSocket(); + Net::bind(mAcceptSocket, mAcceptPort); + Net::listen(mAcceptSocket, 4); + + Net::setBlocking(mAcceptSocket, false); + } + dStrncpy(mDebuggerPassword, password, PasswordMaxLength); + + mWaitForClient = waitForClient; + if ( !mWaitForClient ) + return; + + // Wait for the client to fully connect. + while ( mState != Connected ) + { + Platform::sleep(10); + process(); + } + +} + +void TelnetDebugger::processConsoleLine(const char *consoleLine) +{ + if(mState != NotConnected) + { + send("COUT "); + send(consoleLine); + send("\r\n"); + } +} + +void TelnetDebugger::process() +{ + NetAddress address; + + if(mAcceptSocket != InvalidSocket) + { + // ok, see if we have any new connections: + NetSocket newConnection; + newConnection = Net::accept(mAcceptSocket, &address); + + if(newConnection != InvalidSocket && mDebugSocket == InvalidSocket) + { + Con::printf ("Debugger connection from %i.%i.%i.%i", + address.netNum[0], address.netNum[1], address.netNum[2], address.netNum[3]); + + mState = PasswordTry; + mDebugSocket = newConnection; + + Net::setBlocking(newConnection, false); + } + else if(newConnection != InvalidSocket) + Net::closeSocket(newConnection); + } + // see if we have any input to process... + + if(mDebugSocket == InvalidSocket) + return; + + checkDebugRecv(); + if(mDebugSocket == InvalidSocket) + removeAllBreakpoints(); +} + +void TelnetDebugger::checkDebugRecv() +{ + for (;;) + { + // Process all the complete commands in the buffer. + while ( mCurPos > 0 ) + { + // Remove leading whitespace. + while ( mCurPos > 0 && ( mLineBuffer[0] == 0 || mLineBuffer[0] == '\r' || mLineBuffer[0] == '\n' ) ) + { + mCurPos--; + dMemmove(mLineBuffer, mLineBuffer + 1, mCurPos); + } + + // Look for a complete command. + bool gotCmd = false; + for(S32 i = 0; i < mCurPos; i++) + { + if( mLineBuffer[i] == 0 ) + mLineBuffer[i] = '_'; + + else if ( mLineBuffer[i] == '\r' || mLineBuffer[i] == '\n' ) + { + // Send this command to be processed. + mLineBuffer[i] = '\n'; + processLineBuffer(i+1); + + // Remove the command from the buffer. + mCurPos -= i + 1; + dMemmove(mLineBuffer, mLineBuffer + i + 1, mCurPos); + + gotCmd = true; + break; + } + } + + // If we didn't find a command in this pass + // then we have an incomplete buffer. + if ( !gotCmd ) + break; + } + + // found no or + if(mCurPos == MaxCommandSize) // this shouldn't happen + { + disconnect(); + return; + } + + S32 numBytes; + Net::Error err = Net::recv(mDebugSocket, (unsigned char*)(mLineBuffer + mCurPos), MaxCommandSize - mCurPos, &numBytes); + + if((err != Net::NoError && err != Net::WouldBlock) || numBytes == 0) + { + disconnect(); + return; + } + if(err == Net::WouldBlock) + return; + + mCurPos += numBytes; + } +} + +void TelnetDebugger::executionStopped(CodeBlock *code, U32 lineNumber) +{ + if(mProgramPaused) + return; + + if(mBreakOnNextStatement) + { + setBreakOnNextStatement( false ); + breakProcess(); + return; + } + + Breakpoint **bp = findBreakpoint(code->name, lineNumber); + if(!bp) + return; + + Breakpoint *brk = *bp; + mProgramPaused = true; + Con::evaluatef("$Debug::result = %s;", brk->testExpression); + if(Con::getBoolVariable("$Debug::result")) + { + brk->curCount++; + if(brk->curCount >= brk->passCount) + { + brk->curCount = 0; + if(brk->clearOnHit) + removeBreakpoint(code->name, lineNumber); + breakProcess(); + } + } + mProgramPaused = false; +} + +void TelnetDebugger::pushStackFrame() +{ + if(mState == NotConnected) + return; + + if(mBreakOnNextStatement && mStackPopBreakIndex > -1 && + gEvalState.getStackDepth() > mStackPopBreakIndex) + setBreakOnNextStatement( false ); +} + +void TelnetDebugger::popStackFrame() +{ + if(mState == NotConnected) + return; + + if(mStackPopBreakIndex > -1 && gEvalState.getStackDepth()-1 <= mStackPopBreakIndex) + setBreakOnNextStatement( true ); +} + +void TelnetDebugger::breakProcess() +{ + // Send out a break with the full stack. + sendBreak(); + + mProgramPaused = true; + while(mProgramPaused) + { + Platform::sleep(10); + checkDebugRecv(); + if(mDebugSocket == InvalidSocket) + { + mProgramPaused = false; + removeAllBreakpoints(); + debugContinue(); + return; + } + } +} + +void TelnetDebugger::sendBreak() +{ + // echo out the break + send("BREAK"); + char buffer[MaxCommandSize]; + char scope[MaxCommandSize]; + + S32 last = 0; + + for(S32 i = (S32) gEvalState.getStackDepth() - 1; i >= last; i--) + { + CodeBlock *code = gEvalState.stack[i]->code; + const char *file = ""; + if (code && code->name && code->name[0]) + file = code->name; + + Namespace *ns = gEvalState.stack[i]->scopeNamespace; + scope[0] = 0; + if ( ns ) { + + if ( ns->mParent && ns->mParent->mPackage && ns->mParent->mPackage[0] ) { + dStrcat( scope, ns->mParent->mPackage ); + dStrcat( scope, "::" ); + } + if ( ns->mName && ns->mName[0] ) { + dStrcat( scope, ns->mName ); + dStrcat( scope, "::" ); + } + } + + const char *function = gEvalState.stack[i]->scopeName; + if ((!function) || (!function[0])) + function = ""; + dStrcat( scope, function ); + + U32 line=0, inst; + U32 ip = gEvalState.stack[i]->ip; + if (code) + code->findBreakLine(ip, line, inst); + dSprintf(buffer, MaxCommandSize, " %s %d %s", file, line, scope); + send(buffer); + } + + send("\r\n"); +} + +void TelnetDebugger::processLineBuffer(S32 cmdLen) +{ + if (mState == PasswordTry) + { + if(dStrncmp(mLineBuffer, mDebuggerPassword, cmdLen-1)) + { + // failed password: + send("PASS WrongPassword.\r\n"); + disconnect(); + } + else + { + send("PASS Connected.\r\n"); + mState = mWaitForClient ? Initialize : Connected; + } + + return; + } + else + { + char evalBuffer[MaxCommandSize]; + char varBuffer[MaxCommandSize]; + char fileBuffer[MaxCommandSize]; + char clear[MaxCommandSize]; + S32 passCount, line, frame; + + if(dSscanf(mLineBuffer, "CEVAL %[^\n]", evalBuffer) == 1) + { + RawData rd; + rd.size = dStrlen(evalBuffer) + 1; + rd.data = ( S8* ) evalBuffer; + Con::smConsoleInput.trigger(rd); + } + else if(dSscanf(mLineBuffer, "BRKVARSET %s %d %[^\n]", varBuffer, &passCount, evalBuffer) == 3) + addVariableBreakpoint(varBuffer, passCount, evalBuffer); + else if(dSscanf(mLineBuffer, "BRKVARCLR %s", varBuffer) == 1) + removeVariableBreakpoint(varBuffer); + else if(dSscanf(mLineBuffer, "BRKSET %s %d %s %d %[^\n]", fileBuffer,&line,&clear,&passCount,evalBuffer) == 5) + addBreakpoint(fileBuffer, line, dAtob(clear), passCount, evalBuffer); + else if(dSscanf(mLineBuffer, "BRKCLR %s %d", fileBuffer, &line) == 2) + removeBreakpoint(fileBuffer, line); + else if(!dStrncmp(mLineBuffer, "BRKCLRALL\n", cmdLen)) + removeAllBreakpoints(); + else if(!dStrncmp(mLineBuffer, "BRKNEXT\n", cmdLen)) + debugBreakNext(); + else if(!dStrncmp(mLineBuffer, "CONTINUE\n", cmdLen)) + debugContinue(); + else if(!dStrncmp(mLineBuffer, "STEPIN\n", cmdLen)) + debugStepIn(); + else if(!dStrncmp(mLineBuffer, "STEPOVER\n", cmdLen)) + debugStepOver(); + else if(!dStrncmp(mLineBuffer, "STEPOUT\n", cmdLen)) + debugStepOut(); + else if(dSscanf(mLineBuffer, "EVAL %s %d %[^\n]", varBuffer, &frame, evalBuffer) == 3) + evaluateExpression(varBuffer, frame, evalBuffer); + else if(!dStrncmp(mLineBuffer, "FILELIST\n", cmdLen)) + dumpFileList(); + else if(dSscanf(mLineBuffer, "BREAKLIST %s", fileBuffer) == 1) + dumpBreakableList(fileBuffer); + else + { + S32 errorLen = dStrlen(mLineBuffer) + 32; // ~25 in error message, plus buffer + FrameTemp errorBuffer(errorLen); + + dSprintf( errorBuffer, errorLen, "DBGERR Invalid command(%s)!\r\n", mLineBuffer ); + // invalid stuff. + send( errorBuffer ); + } + } +} + +void TelnetDebugger::addVariableBreakpoint(const char*, S32, const char*) +{ + send("addVariableBreakpoint\r\n"); +} + +void TelnetDebugger::removeVariableBreakpoint(const char*) +{ + send("removeVariableBreakpoint\r\n"); +} + +void TelnetDebugger::addAllBreakpoints(CodeBlock *code) +{ + if(mState == NotConnected) + return; + + // Find the breakpoints for this code block and attach them. + Breakpoint *cur = mBreakpoints; + while( cur != NULL ) + { + // TODO: This assumes that the OS file names are case + // insensitive... Torque needs a dFilenameCmp() function. + if( dStricmp( cur->fileName, code->name ) == 0 ) + { + cur->code = code; + + // Find the fist breakline starting from and + // including the requested breakline. + S32 newLine = code->findFirstBreakLine(cur->lineNumber); + if (newLine <= 0) + { + char buffer[MaxCommandSize]; + dSprintf(buffer, MaxCommandSize, "BRKCLR %s %d\r\n", cur->fileName, cur->lineNumber); + send(buffer); + + Breakpoint *next = cur->next; + removeBreakpoint(cur->fileName, cur->lineNumber); + cur = next; + + continue; + } + + // If the requested breakline does not match + // the actual break line we need to inform + // the client. + if (newLine != cur->lineNumber) + { + char buffer[MaxCommandSize]; + + // If we already have a line at this breapoint then + // tell the client to clear the breakpoint. + if ( findBreakpoint(cur->fileName, newLine) ) { + + dSprintf(buffer, MaxCommandSize, "BRKCLR %s %d\r\n", cur->fileName, cur->lineNumber); + send(buffer); + + Breakpoint *next = cur->next; + removeBreakpoint(cur->fileName, cur->lineNumber); + cur = next; + + continue; + } + + // We're moving the breakpoint to new line... inform the + // client so it can update it's view. + dSprintf(buffer, MaxCommandSize, "BRKMOV %s %d %d\r\n", cur->fileName, cur->lineNumber, newLine); + send(buffer); + cur->lineNumber = newLine; + } + + code->setBreakpoint(cur->lineNumber); + } + + cur = cur->next; + } + + // Enable all breaks if a break next was set. + if (mBreakOnNextStatement) + code->setAllBreaks(); +} + +void TelnetDebugger::addBreakpoint(const char *fileName, S32 line, bool clear, S32 passCount, const char *evalString) +{ + fileName = StringTable->insert(fileName); + Breakpoint **bp = findBreakpoint(fileName, line); + + if(bp) + { + // trying to add the same breakpoint... + Breakpoint *brk = *bp; + dFree(brk->testExpression); + brk->testExpression = dStrdup(evalString); + brk->passCount = passCount; + brk->clearOnHit = clear; + brk->curCount = 0; + } + else + { + // Note that if the code block is not already + // loaded it is handled by addAllBreakpoints. + CodeBlock* code = CodeBlock::find(fileName); + if (code) + { + // Find the fist breakline starting from and + // including the requested breakline. + S32 newLine = code->findFirstBreakLine(line); + if (newLine <= 0) + { + char buffer[MaxCommandSize]; + dSprintf(buffer, MaxCommandSize, "BRKCLR %s %d\r\n", fileName, line); + send(buffer); + return; + } + + // If the requested breakline does not match + // the actual break line we need to inform + // the client. + if (newLine != line) + { + char buffer[MaxCommandSize]; + + // If we already have a line at this breapoint then + // tell the client to clear the breakpoint. + if ( findBreakpoint(fileName, newLine) ) { + dSprintf(buffer, MaxCommandSize, "BRKCLR %s %d\r\n", fileName, line); + send(buffer); + return; + } + + // We're moving the breakpoint to new line... inform the client. + dSprintf(buffer, MaxCommandSize, "BRKMOV %s %d %d\r\n", fileName, line, newLine); + send(buffer); + line = newLine; + } + + code->setBreakpoint(line); + } + + Breakpoint *brk = new Breakpoint; + brk->code = code; + brk->fileName = fileName; + brk->lineNumber = line; + brk->passCount = passCount; + brk->clearOnHit = clear; + brk->curCount = 0; + brk->testExpression = dStrdup(evalString); + brk->next = mBreakpoints; + mBreakpoints = brk; + } +} + +void TelnetDebugger::removeBreakpointsFromCode(CodeBlock *code) +{ + Breakpoint **walk = &mBreakpoints; + Breakpoint *cur; + while((cur = *walk) != NULL) + { + if(cur->code == code) + { + dFree(cur->testExpression); + *walk = cur->next; + delete walk; + } + else + walk = &cur->next; + } +} + +void TelnetDebugger::removeBreakpoint(const char *fileName, S32 line) +{ + fileName = StringTable->insert(fileName); + Breakpoint **bp = findBreakpoint(fileName, line); + if(bp) + { + Breakpoint *brk = *bp; + *bp = brk->next; + if ( brk->code ) + brk->code->clearBreakpoint(brk->lineNumber); + dFree(brk->testExpression); + delete brk; + } +} + +void TelnetDebugger::removeAllBreakpoints() +{ + Breakpoint *walk = mBreakpoints; + while(walk) + { + Breakpoint *temp = walk->next; + if ( walk->code ) + walk->code->clearBreakpoint(walk->lineNumber); + dFree(walk->testExpression); + delete walk; + walk = temp; + } + mBreakpoints = NULL; +} + +void TelnetDebugger::debugContinue() +{ + if (mState == Initialize) { + mState = Connected; + return; + } + + setBreakOnNextStatement( false ); + mStackPopBreakIndex = -1; + mProgramPaused = false; + send("RUNNING\r\n"); +} + +void TelnetDebugger::setBreakOnNextStatement( bool enabled ) +{ + if ( enabled ) + { + // Apply breaks on all the code blocks. + for(CodeBlock *walk = CodeBlock::getCodeBlockList(); walk; walk = walk->nextFile) + walk->setAllBreaks(); + mBreakOnNextStatement = true; + } + else if ( !enabled ) + { + // Clear all the breaks on the codeblocks + // then go reapply the breakpoints. + for(CodeBlock *walk = CodeBlock::getCodeBlockList(); walk; walk = walk->nextFile) + walk->clearAllBreaks(); + for(Breakpoint *w = mBreakpoints; w; w = w->next) + { + if ( w->code ) + w->code->setBreakpoint(w->lineNumber); + } + mBreakOnNextStatement = false; + } +} + +void TelnetDebugger::debugBreakNext() +{ + if (mState != Connected) + return; + + if ( !mProgramPaused ) + setBreakOnNextStatement( true ); +} + +void TelnetDebugger::debugStepIn() +{ + // Note that step in is allowed during + // the initialize state, so that we can + // break on the first script line executed. + + setBreakOnNextStatement( true ); + mStackPopBreakIndex = -1; + mProgramPaused = false; + + // Don't bother sending this to the client + // if it's in the initialize state. It will + // just be ignored as the client knows it + // is in a running state when it connects. + if (mState != Initialize) + send("RUNNING\r\n"); + else + mState = Connected; +} + +void TelnetDebugger::debugStepOver() +{ + if (mState != Connected) + return; + + setBreakOnNextStatement( true ); + mStackPopBreakIndex = gEvalState.getStackDepth(); + mProgramPaused = false; + send("RUNNING\r\n"); +} + +void TelnetDebugger::debugStepOut() +{ + if (mState != Connected) + return; + + setBreakOnNextStatement( false ); + mStackPopBreakIndex = gEvalState.getStackDepth() - 1; + if ( mStackPopBreakIndex == 0 ) + mStackPopBreakIndex = -1; + mProgramPaused = false; + send("RUNNING\r\n"); +} + +void TelnetDebugger::evaluateExpression(const char *tag, S32 frame, const char *evalBuffer) +{ + // Make sure we're passing a valid frame to the eval. + if ( frame > gEvalState.getStackDepth() ) + frame = gEvalState.getStackDepth() - 1; + if ( frame < 0 ) + frame = 0; + + // Build a buffer just big enough for this eval. + const char* format = "return %s;"; + dsize_t len = dStrlen( format ) + dStrlen( evalBuffer ); + char* buffer = new char[ len ]; + dSprintf( buffer, len, format, evalBuffer ); + + // Execute the eval. + CodeBlock *newCodeBlock = new CodeBlock(); + const char* result = newCodeBlock->compileExec( NULL, buffer, false, frame ); + delete [] buffer; + + // Create a new buffer that fits the result. + format = "EVALOUT %s %s\r\n"; + len = dStrlen( format ) + dStrlen( tag ) + dStrlen( result ); + buffer = new char[ len ]; + dSprintf( buffer, len, format, tag, result[0] ? result : "\"\"" ); + + send( buffer ); + delete [] buffer; +} + +void TelnetDebugger::dumpFileList() +{ + send("FILELISTOUT "); + for(CodeBlock *walk = CodeBlock::getCodeBlockList(); walk; walk = walk->nextFile) + { + send(walk->name); + if(walk->nextFile) + send(" "); + } + send("\r\n"); +} + +void TelnetDebugger::dumpBreakableList(const char *fileName) +{ + fileName = StringTable->insert(fileName); + CodeBlock *file = CodeBlock::find(fileName); + char buffer[MaxCommandSize]; + if(file) + { + dSprintf(buffer, MaxCommandSize, "BREAKLISTOUT %s %d", fileName, file->breakListSize >> 1); + send(buffer); + for(U32 i = 0; i < file->breakListSize; i += 2) + { + dSprintf(buffer, MaxCommandSize, " %d %d", file->breakList[i], file->breakList[i+1]); + send(buffer); + } + send("\r\n"); + } + else + send("DBGERR No such file!\r\n"); +} + + +void TelnetDebugger::clearCodeBlockPointers(CodeBlock *code) +{ + Breakpoint **walk = &mBreakpoints; + Breakpoint *cur; + while((cur = *walk) != NULL) + { + if(cur->code == code) + cur->code = NULL; + + walk = &cur->next; + } +} diff --git a/Engine/source/console/telnetDebugger.h b/Engine/source/console/telnetDebugger.h new file mode 100644 index 000000000..6916ae935 --- /dev/null +++ b/Engine/source/console/telnetDebugger.h @@ -0,0 +1,139 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TELNETDEBUGGER_H_ +#define _TELNETDEBUGGER_H_ + +#ifndef _PLATFORM_PLATFORMNET_H_ +#include "platform/platformNet.h" +#endif + +class CodeBlock; + +/// Telnet debug service implementation. +/// +/// This is the C++ side of the built-in Torque debugger. +/// +/// To use the debugger, use dbgSetParameters(port, password); in the console +/// of the server to enable debugger connections. Then on some other system, +/// start up the app (you don't have to start a game or connect to the +/// server) and exec("common/debugger/debugger.cs"); in the console. Then use +/// the debugger GUI to connect to the server with the right port and password. +/// +/// @see http://www.planettribes.com/tribes2/editing.shtml for more thorough discussion. +class TelnetDebugger +{ + S32 mAcceptPort; + NetSocket mAcceptSocket; + NetSocket mDebugSocket; + + enum { + + // We should only change this is we truely + // break the protocol in a future version. + Version = 2, + + PasswordMaxLength = 32, + MaxCommandSize = 2048 + }; + + char mDebuggerPassword[PasswordMaxLength+1]; + enum State + { + NotConnected, + PasswordTry, + Initialize, + Connected + }; + S32 mState; + char mLineBuffer[MaxCommandSize]; + S32 mCurPos; + bool mWaitForClient; + + TelnetDebugger(); + ~TelnetDebugger(); + + struct Breakpoint + { + StringTableEntry fileName; + CodeBlock *code; + U32 lineNumber; + S32 passCount; + S32 curCount; + char *testExpression; + bool clearOnHit; + Breakpoint *next; + }; + Breakpoint *mBreakpoints; + + Breakpoint **findBreakpoint(StringTableEntry fileName, S32 lineNumber); + + bool mProgramPaused; + bool mBreakOnNextStatement; + S32 mStackPopBreakIndex; + + void addVariableBreakpoint(const char *varName, S32 passCount, const char *evalString); + void removeVariableBreakpoint(const char *varName); + void addBreakpoint(const char *fileName, S32 line, bool clear, S32 passCount, const char *evalString); + void removeBreakpoint(const char *fileName, S32 line); + void removeAllBreakpoints(); + + void debugBreakNext(); + void debugContinue(); + void debugStepIn(); + void debugStepOver(); + void debugStepOut(); + void evaluateExpression(const char *tag, S32 frame, const char *evalBuffer); + void dumpFileList(); + void dumpBreakableList(const char *fileName); + void removeBreakpointsFromCode(CodeBlock *code); + + void checkDebugRecv(); + void processLineBuffer(S32); + void sendBreak(); + void setBreakOnNextStatement( bool enabled ); +public: + static void create(); + static void destroy(); + + void disconnect(); + bool isConnected() const { return mState == Connected; } + + void process(); + void popStackFrame(); + void pushStackFrame(); + void addAllBreakpoints(CodeBlock *code); + + void clearCodeBlockPointers(CodeBlock *code); + + void breakProcess(); + + virtual void executionStopped(CodeBlock *code, U32 lineNumber); + void send(const char *s); + void setDebugParameters(S32 port, const char *password, bool waitForClient); + void processConsoleLine(const char *consoleLine); +}; + +extern TelnetDebugger *TelDebugger; + +#endif + diff --git a/Engine/source/console/typeValidators.cpp b/Engine/source/console/typeValidators.cpp new file mode 100644 index 000000000..0b679a2ec --- /dev/null +++ b/Engine/source/console/typeValidators.cpp @@ -0,0 +1,108 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/console.h" +#include "console/consoleObject.h" +#include "console/typeValidators.h" +#include "console/simBase.h" +#include "math/mPoint3.h" +#include + +void TypeValidator::consoleError(SimObject *object, const char *format, ...) +{ + char buffer[1024]; + va_list argptr; + va_start(argptr, format); + dVsprintf(buffer, sizeof(buffer), format, argptr); + va_end(argptr); + + AbstractClassRep *rep = object->getClassRep(); + AbstractClassRep::Field &fld = rep->mFieldList[fieldIndex]; + const char *objectName = object->getName(); + if(!objectName) + objectName = "unnamed"; + + + Con::warnf("%s - %s(%d) - invalid value for %s: %s", + rep->getClassName(), objectName, object->getId(), fld.pFieldname, buffer); +} + +void FRangeValidator::validateType(SimObject *object, void *typePtr) +{ + F32 *v = (F32 *) typePtr; + if(*v < minV || *v > maxV) + { + consoleError(object, "Must be between %g and %g", minV, maxV); + if(*v < minV) + *v = minV; + else if(*v > maxV) + *v = maxV; + } +} + +void IRangeValidator::validateType(SimObject *object, void *typePtr) +{ + S32 *v = (S32 *) typePtr; + if(*v < minV || *v > maxV) + { + consoleError(object, "Must be between %d and %d", minV, maxV); + if(*v < minV) + *v = minV; + else if(*v > maxV) + *v = maxV; + } +} + +void IRangeValidatorScaled::validateType(SimObject *object, void *typePtr) +{ + S32 *v = (S32 *) typePtr; + *v /= factor; + if(*v < minV || *v > maxV) + { + consoleError(object, "Scaled value must be between %d and %d", minV, maxV); + if(*v < minV) + *v = minV; + else if(*v > maxV) + *v = maxV; + } +} + +void Point3NormalizeValidator::validateType(SimObject *object, void *typePtr) +{ + Point3F *v = (Point3F *) typePtr; + const F32 len = v->len(); + if(!mIsEqual(len, 1.0f)) + { + consoleError(object, "Vector length must be %g", length); + *v *= length / len; + } +} + +namespace CommonValidators +{ + FRangeValidator PositiveFloat(0.0f, F32_MAX); + FRangeValidator PositiveNonZeroFloat(F32( POINT_EPSILON ) , F32_MAX); + FRangeValidator NormalizedFloat(0.0f, 1.0f); + Point3NormalizeValidator NormalizedPoint3(1.0f); +}; + diff --git a/Engine/source/console/typeValidators.h b/Engine/source/console/typeValidators.h new file mode 100644 index 000000000..414721f5d --- /dev/null +++ b/Engine/source/console/typeValidators.h @@ -0,0 +1,109 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TYPEVALIDATORS_H_ +#define _TYPEVALIDATORS_H_ + +class TypeValidator +{ + public: + S32 fieldIndex; + + /// Prints a console error message for the validator. + /// + /// The message is prefaced with with: + /// @code + /// className objectName (objectId) - invalid value for fieldName: msg + /// @endcode + void consoleError(SimObject *object, const char *format, ...); + + /// validateType is called for each assigned value on the field this + /// validator is attached to. + virtual void validateType(SimObject *object, void *typePtr) = 0; +}; + + +/// Floating point min/max range validator +class FRangeValidator : public TypeValidator +{ + F32 minV, maxV; +public: + FRangeValidator(F32 minValue, F32 maxValue) + { + minV = minValue; + maxV = maxValue; + } + void validateType(SimObject *object, void *typePtr); +}; + +/// Signed integer min/max range validator +class IRangeValidator : public TypeValidator +{ + S32 minV, maxV; +public: + IRangeValidator(S32 minValue, S32 maxValue) + { + minV = minValue; + maxV = maxValue; + } + void validateType(SimObject *object, void *typePtr); +}; + +/// Scaled integer field validator +/// +/// @note This should NOT be used on a field that gets exported - +/// the field is only validated once on initial assignment +class IRangeValidatorScaled : public TypeValidator +{ + S32 minV, maxV; + S32 factor; +public: + IRangeValidatorScaled(S32 scaleFactor, S32 minValueScaled, S32 maxValueScaled) + { + minV = minValueScaled; + maxV = maxValueScaled; + factor = scaleFactor; + } + void validateType(SimObject *object, void *typePtr); +}; + +/// Vector normalization validator +class Point3NormalizeValidator : public TypeValidator +{ + F32 length; +public: + Point3NormalizeValidator(F32 normalizeLength = 1.0f) : length(normalizeLength) { } + void validateType(SimObject *object, void *typePtr); +}; + +namespace CommonValidators +{ + // Floats + extern FRangeValidator PositiveFloat; + extern FRangeValidator PositiveNonZeroFloat; + extern FRangeValidator NormalizedFloat; + + // Other Math Types + extern Point3NormalizeValidator NormalizedPoint3; +}; + +#endif diff --git a/Engine/source/core/bitMatrix.h b/Engine/source/core/bitMatrix.h new file mode 100644 index 000000000..0c7582a9b --- /dev/null +++ b/Engine/source/core/bitMatrix.h @@ -0,0 +1,182 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _BITMATRIX_H_ +#define _BITMATRIX_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#ifndef _BITVECTOR_H_ +#include "core/bitVector.h" +#endif + +/// A matrix of bits. +/// +/// This class manages an array of bits. There are no limitations on the +/// size of the bit matrix (beyond available memory). +/// +/// @note This class is currently unused. +class BitMatrix +{ + U32 mWidth; + U32 mHeight; + U32 mRowByteWidth; + + U8* mBits; + U32 mSize; + + BitVector mColFlags; + BitVector mRowFlags; + + public: + + /// Create a new bit matrix. + /// + /// @param width Width of matrix in bits. + /// @param height Height of matrix in bits. + BitMatrix(const U32 width, const U32 height); + ~BitMatrix(); + + /// @name Setters + /// @{ + + /// Set all the bits in the matrix to false. + void clearAllBits(); + + /// Set all the bits in the matrix to true. + void setAllBits(); + + /// Set a bit at a given location in the matrix. + void setBit(const U32 x, const U32 y); + + /// Clear a bit at a given location in the matrix. + void clearBit(const U32 x, const U32 y); + + /// @} + + /// @name Queries + /// @{ + + /// Is the specified bit set? + bool isSet(const U32 x, const U32 y) const; + + /// Is any bit in the given column set? + bool isAnySetCol(const U32 x); + + /// Is any bit in the given row set? + bool isAnySetRow(const U32 y); + + /// @} +}; + +inline BitMatrix::BitMatrix(const U32 width, const U32 height) + : mColFlags(width), + mRowFlags(height) +{ + AssertFatal(width != 0 && height != 0, "Error, w/h must be non-zero"); + + mWidth = width; + mHeight = height; + mRowByteWidth = (width + 7) >> 3; + + mSize = mRowByteWidth * mHeight; + mBits = new U8[mSize]; +} + +inline BitMatrix::~BitMatrix() +{ + mWidth = 0; + mHeight = 0; + mRowByteWidth = 0; + mSize = 0; + + delete [] mBits; + mBits = NULL; +} + +inline void BitMatrix::clearAllBits() +{ + AssertFatal(mBits != NULL, "Error, clearing after deletion"); + + dMemset(mBits, 0x00, mSize); + mColFlags.clear(); + mRowFlags.clear(); +} + +inline void BitMatrix::setAllBits() +{ + AssertFatal(mBits != NULL, "Error, setting after deletion"); + + dMemset(mBits, 0xFF, mSize); + mColFlags.set(); + mRowFlags.set(); +} + +inline void BitMatrix::setBit(const U32 x, const U32 y) +{ + AssertFatal(x < mWidth && y < mHeight, "Error, out of bounds bit!"); + + U8* pRow = &mBits[y * mRowByteWidth]; + + U8* pByte = &pRow[x >> 3]; + *pByte |= 1 << (x & 0x7); + + mColFlags.set(x); + mRowFlags.set(y); +} + +inline void BitMatrix::clearBit(const U32 x, const U32 y) +{ + AssertFatal(x < mWidth && y < mHeight, "Error, out of bounds bit!"); + + U8* pRow = &mBits[y * mRowByteWidth]; + + U8* pByte = &pRow[x >> 3]; + *pByte &= ~(1 << (x & 0x7)); +} + +inline bool BitMatrix::isSet(const U32 x, const U32 y) const +{ + AssertFatal(x < mWidth && y < mHeight, "Error, out of bounds bit!"); + + U8* pRow = &mBits[y * mRowByteWidth]; + + U8* pByte = &pRow[x >> 3]; + return (*pByte & (1 << (x & 0x7))) != 0; +} + +inline bool BitMatrix::isAnySetCol(const U32 x) +{ + AssertFatal(x < mWidth, "Error, out of bounds column!"); + + return mColFlags.test(x); +} + +inline bool BitMatrix::isAnySetRow(const U32 y) +{ + AssertFatal(y < mHeight, "Error, out of bounds row!"); + + return mRowFlags.test(y); +} + +#endif // _H_BITMATRIX_ diff --git a/Engine/source/core/bitRender.cpp b/Engine/source/core/bitRender.cpp new file mode 100644 index 000000000..39bbe127f --- /dev/null +++ b/Engine/source/core/bitRender.cpp @@ -0,0 +1,1004 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/bitRender.h" + +U32 openTable[32] = { 0xFFFFFFFF,0xFFFFFFFE,0xFFFFFFFC,0xFFFFFFF8, + 0xFFFFFFF0,0xFFFFFFE0,0xFFFFFFC0,0xFFFFFF80, + 0xFFFFFF00,0xFFFFFE00,0xFFFFFC00,0xFFFFF800, + 0xFFFFF000,0xFFFFE000,0xFFFFC000,0xFFFF8000, + 0xFFFF0000,0xFFFE0000,0xFFFC0000,0xFFF80000, + 0xFFF00000,0xFFE00000,0xFFC00000,0xFF800000, + 0xFF000000,0xFE000000,0xFC000000,0xF8000000, + 0xF0000000,0xE0000000,0xC0000000,0x80000000 }; + +U32 closeTable[32] = { 0x00000001,0x00000003,0x00000007,0x0000000F, + 0x0000001F,0x0000003F,0x0000007F,0x000000FF, + 0x000001FF,0x000003FF,0x000007FF,0x00000FFF, + 0x00001FFF,0x00003FFF,0x00007FFF,0x0000FFFF, + 0x0001FFFF,0x0003FFFF,0x0007FFFF,0x000FFFFF, + 0x001FFFFF,0x003FFFFF,0x007FFFFF,0x00FFFFFF, + 0x01FFFFFF,0x03FFFFFF,0x07FFFFFF,0x0FFFFFFF, + 0x1FFFFFFF,0x3FFFFFFF,0x7FFFFFFF,0xFFFFFFFF }; + +struct DrawStruct +{ + S16 start; + S16 num; +}; + +void BitRender::render_strips(const U8 * draw, S32 numDraw, S32 szDraw, const U16 * indices, const Point2I * points, S32 dim, U32 * bits) +{ + const U8 * drawEnd = draw + numDraw*szDraw; + + // loop through strips... + for (; drawstart; + const U16 * iend = icurrent + drawStruct->num; + + const Point2I * vv0 = points + *(icurrent++); + const Point2I * vv1; + const Point2I * vv2 = points + *(icurrent++); + const Point2I **nextPt = &vv1; + + while (icurrentx>=0 && v0->xy>=0 && v0->yx>=0 && v1->xy>=0 && v1->yx>=0 && v2->xy>=0 && v2->yx-v1->x)*(v2->y-v1->y) > (v0->y-v1->y)*(v2->x-v1->x)) + continue; + + // rotate so that v0 holds topmost y coord + // Note: The particular inequalities used ( <=, <, then <=) are important. + // They guarantee that if there are two verts on the top row, that + // they will be vert v0 and v1 (also could be three). + if (v1->y <= v2->y) + { + if (v1->y < v0->y) + { + const Point2I * tmp = v0; + v0 = v1; + v1 = v2; + v2 = tmp; + } + } + else + { + if (v2->y <= v0->y) + { + const Point2I * tmp = v0; + v0 = v2; + v2 = v1; + v1 = tmp; + } + } + + // all the control variables we have to set up... + S32 leftDeltaY, xLeftInc, xLeftErrInc, xLeftDir, xLeft, xLeftErr = 0; + S32 rightDeltaY, xRightInc, xRightErrInc, xRightDir, xRight, xRightErr = 0; + S32 * changeThisDelta; + S32 * changeThisInc; + S32 * changeThisErrInc; + S32 * changeThisDir; + S32 toThisDelta; + S32 toThisInc; + S32 toThisErrInc; + S32 toThisDir; + S32 ySwitch; + S32 yBottom; + + // check for special case where top row holds two verts + if (v0->y==v1->y) + { + leftDeltaY = v2->y-v0->y; + rightDeltaY = v2->y-v1->y; + ySwitch = v2->y; + yBottom = v2->y; + + if (v1->y==v2->y) + { + // special-special case where top row holds all three verts + xLeft = getMin(getMin(v0->x,v1->x),v2->x); + xRight = getMax(getMax(v0->x,v1->x),v2->x); + xLeftInc = xRightInc = 0; + xLeftErrInc = xRightErrInc = 0; + xLeftDir = xRightDir = 1; + } + else + { + // standard-special case...v2 on different row from v0 and v1 + xLeft = v0->x; + xLeftInc = (v2->x-v0->x) / leftDeltaY; + xLeftErrInc = (v2->x-v0->x) - leftDeltaY*xLeftInc; + xLeftDir = v2->x-v0->x > 0 ? 1 : -1; + + xRight = v1->x; + xRightInc = (v2->x-v1->x) / rightDeltaY; + xRightErrInc = (v2->x-v1->x) - rightDeltaY*xRightInc; + xRightDir = v2->x-v1->x > 0 ? 1 : -1; + } + + // set these variables to avoid a crash... + changeThisDelta = &toThisDelta; + changeThisInc = &toThisInc; + changeThisErrInc = &toThisErrInc; + changeThisDir = &toThisDir; + } + else + { + leftDeltaY = v2->y-v0->y; + xLeftInc = (v2->x-v0->x) / leftDeltaY; + xLeftErrInc = (v2->x-v0->x) - leftDeltaY*xLeftInc; + xLeftDir = v2->x-v0->x > 0 ? 1 : -1; + + rightDeltaY = v1->y-v0->y; + xRightInc = (v1->x-v0->x) / rightDeltaY; + xRightErrInc = (v1->x-v0->x) - rightDeltaY*xRightInc; + xRightDir = v1->x-v0->x > 0 ? 1 : -1; + + xLeft = xRight = v0->x; + + if (v1->yy) + { + // right edge bends + changeThisDelta = &rightDeltaY; + changeThisInc = &xRightInc; + changeThisErrInc = &xRightErrInc; + changeThisDir = &xRightDir; + toThisDelta = v2->y-v1->y; + toThisInc = (v2->x-v1->x) / toThisDelta; + toThisErrInc = (v2->x-v1->x) - toThisDelta * toThisInc; + toThisDir = v2->x-v1->x > 0 ? 1 : -1; + ySwitch = v1->y-1; + yBottom = v2->y; + } + else + { + // left edge bends + changeThisDelta = &leftDeltaY; + changeThisInc = &xLeftInc; + changeThisErrInc = &xLeftErrInc; + changeThisDir = &xLeftDir; + toThisDelta = v1->y-v2->y; + toThisInc = toThisDelta ? (v1->x-v2->x) / toThisDelta : 0; + toThisErrInc = toThisDelta ? (v1->x-v2->x) - toThisDelta * toThisInc : 0; + toThisDir = v1->x-v2->x > 0 ? 1 : -1; + ySwitch = v2->y-1; + yBottom = v1->y; + } + } + xLeftErrInc *= xLeftDir; + xRightErrInc *= xRightDir; + toThisErrInc *= toThisDir; + + U32 * rowStart = bits + v0->y * (dim>>5); + for (S32 y=v0->y; y<=yBottom;) + { + do + { + AssertFatal(xLeft<=xRight,"BitRender::render"); + + U32 open = openTable[xLeft&31]; + U32 close = closeTable[xRight&31]; + if ( (xLeft^xRight) & ~31) + { + U32 * x = rowStart+(xLeft>>5); + *x |= open; + U32 * xEnd = rowStart+(xRight>>5); + while (++x>5] |= open & close; + + xLeft += xLeftInc; + xLeftErr += xLeftErrInc; + if (xLeftErr >= leftDeltaY) + { + xLeft += xLeftDir; + xLeftErr -= leftDeltaY; + } + + xRight += xRightInc; + xRightErr += xRightErrInc; + if (xRightErr >= rightDeltaY) + { + xRight += xRightDir; + xRightErr -= rightDeltaY; + } + + rowStart += dim>>5; + + } while (y++start; + const U16 * iend = icurrent + drawStruct->num; + + while (icurrentx>=0 && v0->xy>=0 && v0->yx>=0 && v1->xy>=0 && v1->yx>=0 && v2->xy>=0 && v2->yx-v1->x)*(v2->y-v1->y) > (v0->y-v1->y)*(v2->x-v1->x)) + return; + + // rotate so that v0 holds topmost y coord + // Note: The particular inequalities used ( <=, <, then <=) are important. + // They guarantee that if there are two verts on the top row, that + // they will be vert v0 and v1 (also could be three). + if (v1->y <= v2->y) + { + if (v1->y < v0->y) + { + const Point2I * tmp = v0; + v0 = v1; + v1 = v2; + v2 = tmp; + } + } + else + { + if (v2->y <= v0->y) + { + const Point2I * tmp = v0; + v0 = v2; + v2 = v1; + v1 = tmp; + } + } + + // all the control variables we have to set up... + S32 leftDeltaY, xLeftInc = 0, xLeftErrInc, xLeftDir = 0, xLeft, xLeftErr = 0; + S32 rightDeltaY, xRightInc = 0, xRightErrInc, xRightDir = 0, xRight, xRightErr = 0; + S32 * changeThisDelta; + S32 * changeThisInc; + S32 * changeThisErrInc; + S32 * changeThisDir; + S32 toThisDelta; + S32 toThisInc; + S32 toThisErrInc; + S32 toThisDir; + S32 ySwitch; + S32 yBottom; + + // check for special case where top row holds two verts + if (v0->y==v1->y) + { + leftDeltaY = v2->y-v0->y; + rightDeltaY = v2->y-v1->y; + ySwitch = v2->y; + yBottom = v2->y; + + if (v1->y==v2->y) + { + // special-special case where top row holds all three verts + xLeft = getMin(getMin(v0->x,v1->x),v2->x); + xRight = getMax(getMax(v0->x,v1->x),v2->x); + xLeftErrInc = xRightErrInc = 0; + } + else + { + // standard-special case...v2 on different row from v0 and v1 + xLeft = v0->x; + xLeftInc = (v2->x-v0->x) / leftDeltaY; + xLeftErrInc = (v2->x-v0->x) - leftDeltaY*xLeftInc; + xLeftDir = v2->x-v0->x > 0 ? 1 : -1; + + xRight = v1->x; + xRightInc = (v2->x-v1->x) / rightDeltaY; + xRightErrInc = (v2->x-v1->x) - rightDeltaY*xRightInc; + xRightDir = v2->x-v1->x > 0 ? 1 : -1; + } + + // set these variables to avoid a crash... + changeThisDelta = &toThisDelta; + changeThisInc = &toThisInc; + changeThisErrInc = &toThisErrInc; + changeThisDir = &toThisDir; + } + else + { + leftDeltaY = v2->y-v0->y; + xLeftInc = (v2->x-v0->x) / leftDeltaY; + xLeftErrInc = (v2->x-v0->x) - leftDeltaY*xLeftInc; + xLeftDir = v2->x-v0->x > 0 ? 1 : -1; + + rightDeltaY = v1->y-v0->y; + xRightInc = (v1->x-v0->x) / rightDeltaY; + xRightErrInc = (v1->x-v0->x) - rightDeltaY*xRightInc; + xRightDir = v1->x-v0->x > 0 ? 1 : -1; + + xLeft = xRight = v0->x; + + if (v1->yy) + { + // right edge bends + changeThisDelta = &rightDeltaY; + changeThisInc = &xRightInc; + changeThisErrInc = &xRightErrInc; + changeThisDir = &xRightDir; + toThisDelta = v2->y-v1->y; + toThisInc = (v2->x-v1->x) / toThisDelta; + toThisErrInc = (v2->x-v1->x) - toThisDelta * toThisInc; + toThisDir = v2->x-v1->x > 0 ? 1 : -1; + ySwitch = v1->y-1; + yBottom = v2->y; + } + else + { + // left edge bends + changeThisDelta = &leftDeltaY; + changeThisInc = &xLeftInc; + changeThisErrInc = &xLeftErrInc; + changeThisDir = &xLeftDir; + toThisDelta = v1->y-v2->y; + toThisInc = toThisDelta ? (v1->x-v2->x) / toThisDelta : 0; + toThisErrInc = toThisDelta ? (v1->x-v2->x) - toThisDelta * toThisInc : 0; + toThisDir = v1->x-v2->x > 0 ? 1 : -1; + ySwitch = v2->y-1; + yBottom = v1->y; + } + } + xLeftErrInc *= xLeftDir; + xRightErrInc *= xRightDir; + toThisErrInc *= toThisDir; + + U32 * rowStart = bits + v0->y * (dim>>5); + for (S32 y=v0->y; y<=yBottom;) + { + do + { + AssertFatal(xLeft<=xRight,"BitRender::render"); + + U32 open = openTable[xLeft&31]; + U32 close = closeTable[xRight&31]; + if ( (xLeft^xRight) & ~31) + { + U32 * x = rowStart+(xLeft>>5); + *x |= open; + U32 * xEnd = rowStart+(xRight>>5); + while (++x>5] |= open & close; + + xLeft += xLeftInc; + xLeftErr += xLeftErrInc; + if (xLeftErr >= leftDeltaY) + { + xLeft += xLeftDir; + xLeftErr -= leftDeltaY; + } + + xRight += xRightInc; + xRightErr += xRightErrInc; + if (xRightErr >= rightDeltaY) + { + xRight += xRightDir; + xRightErr -= rightDeltaY; + } + + rowStart += dim>>5; + + } while (y++x>=0 && v0->xy>=0 && v0->yx>=0 && v1->xy>=0 && v1->yx>=0 && v2->xy>=0 && v2->yx-v1->x)*(v2->y-v1->y) > (v0->y-v1->y)*(v2->x-v1->x)) + return; + + // rotate so that v0 holds topmost y coord + // Note: The particular inequalities used ( <=, <, then <=) are important. + // They guarantee that if there are two verts on the top row, that + // they will be vert v0 and v1 (also could be three). + if (v1->y <= v2->y) + { + if (v1->y < v0->y) + { + const Point2I * tmp = v0; + v0 = v1; + v1 = v2; + v2 = tmp; + } + } + else + { + if (v2->y <= v0->y) + { + const Point2I * tmp = v0; + v0 = v2; + v2 = v1; + v1 = tmp; + } + } + + // all the control variables we have to set up... + S32 leftDeltaY, xLeftInc = 0, xLeftErrInc, xLeftDir = 0, xLeft, xLeftErr = 0; + S32 rightDeltaY, xRightInc = 0, xRightErrInc, xRightDir = 0, xRight, xRightErr = 0; + S32 * changeThisDelta; + S32 * changeThisInc; + S32 * changeThisErrInc; + S32 * changeThisDir; + S32 toThisDelta; + S32 toThisInc; + S32 toThisErrInc; + S32 toThisDir; + S32 ySwitch; + S32 yBottom; + + // check for special case where top row holds two verts + if (v0->y==v1->y) + { + leftDeltaY = v2->y-v0->y; + rightDeltaY = v2->y-v1->y; + ySwitch = v2->y; + yBottom = v2->y; + + if (v1->y==v2->y) + { + // special-special case where top row holds all three verts + xLeft = getMin(getMin(v0->x,v1->x),v2->x); + xRight = getMax(getMax(v0->x,v1->x),v2->x); + xLeftErrInc = xRightErrInc = 0; + } + else + { + // standard-special case...v2 on different row from v0 and v1 + xLeft = v0->x; + xLeftInc = (v2->x-v0->x) / leftDeltaY; + xLeftErrInc = (v2->x-v0->x) - leftDeltaY*xLeftInc; + xLeftDir = v2->x-v0->x > 0 ? 1 : -1; + + xRight = v1->x; + xRightInc = (v2->x-v1->x) / rightDeltaY; + xRightErrInc = (v2->x-v1->x) - rightDeltaY*xRightInc; + xRightDir = v2->x-v1->x > 0 ? 1 : -1; + } + + // set these variables to avoid a crash... + changeThisDelta = &toThisDelta; + changeThisInc = &toThisInc; + changeThisErrInc = &toThisErrInc; + changeThisDir = &toThisDir; + } + else + { + leftDeltaY = v2->y-v0->y; + xLeftInc = (v2->x-v0->x) / leftDeltaY; + xLeftErrInc = (v2->x-v0->x) - leftDeltaY*xLeftInc; + xLeftDir = v2->x-v0->x > 0 ? 1 : -1; + + rightDeltaY = v1->y-v0->y; + xRightInc = (v1->x-v0->x) / rightDeltaY; + xRightErrInc = (v1->x-v0->x) - rightDeltaY*xRightInc; + xRightDir = v1->x-v0->x > 0 ? 1 : -1; + + xLeft = xRight = v0->x; + + if (v1->yy) + { + // right edge bends + changeThisDelta = &rightDeltaY; + changeThisInc = &xRightInc; + changeThisErrInc = &xRightErrInc; + changeThisDir = &xRightDir; + toThisDelta = v2->y-v1->y; + toThisInc = (v2->x-v1->x) / toThisDelta; + toThisErrInc = (v2->x-v1->x) - toThisDelta * toThisInc; + toThisDir = v2->x-v1->x > 0 ? 1 : -1; + ySwitch = v1->y-1; + yBottom = v2->y; + } + else + { + // left edge bends + changeThisDelta = &leftDeltaY; + changeThisInc = &xLeftInc; + changeThisErrInc = &xLeftErrInc; + changeThisDir = &xLeftDir; + toThisDelta = v1->y-v2->y; + toThisInc = toThisDelta ? (v1->x-v2->x) / toThisDelta : 0; + toThisErrInc = toThisDelta ? (v1->x-v2->x) - toThisDelta * toThisInc : 0; + toThisDir = v1->x-v2->x > 0 ? 1 : -1; + ySwitch = v2->y-1; + yBottom = v1->y; + } + } + xLeftErrInc *= xLeftDir; + xRightErrInc *= xRightDir; + toThisErrInc *= toThisDir; + + U32 * rowStart = bits + v0->y * (dim>>5); + for (S32 y=v0->y; y<=yBottom;) + { + do + { + AssertFatal(xLeft<=xRight,"BitRender::render"); + + U32 open = openTable[xLeft&31]; + U32 close = closeTable[xRight&31]; + if ( (xLeft^xRight) & ~31) + { + U32 * x = rowStart+(xLeft>>5); + *x |= open; + U32 * xEnd = rowStart+(xRight>>5); + while (++x>5] |= open & close; + + xLeft += xLeftInc; + xLeftErr += xLeftErrInc; + if (xLeftErr >= leftDeltaY) + { + xLeft += xLeftDir; + xLeftErr -= leftDeltaY; + } + + xRight += xRightInc; + xRightErr += xRightErrInc; + if (xRightErr >= rightDeltaY) + { + xRight += xRightDir; + xRightErr -= rightDeltaY; + } + + rowStart += dim>>5; + + } while (y++> 2) +#define SF32(a,b,c,d) {SF(a),SF(b),SF(c),SF(d)} + +/* +// endian-ordering version of the SF macro, for the blur methods. +#if PLATFORM_LITTLE_ENDIAN +#define SF32E(a,b,c,d) SF32(a,b,c,d) +#else +#define SF32E(a,b,c,d) SF32(d,c,b,a) +#endif +*/ + + +U8 bitTable[16][4] = +{ + SF32( 0, 0, 0, 0), // 0 + SF32(255, 0, 0, 0), // 1 + SF32( 0,255, 0, 0), // 2 + SF32(255,255, 0, 0), // 3 + SF32( 0, 0,255, 0), // 4 + SF32(255, 0,255, 0), // 5 + SF32( 0,255,255, 0), // 6 + SF32(255,255,255, 0), // 7 + SF32( 0, 0, 0,255), // 8 + SF32(255, 0, 0,255), // 9 + SF32( 0,255, 0,255), // 10 + SF32(255,255, 0,255), // 11 + SF32( 0, 0,255,255), // 12 + SF32(255, 0,255,255), // 13 + SF32( 0,255,255,255), // 14 + SF32(255,255,255,255), // 15 +}; + +void BitRender::bitTo8Bit(U32 * bits, U32 * eightBits, S32 dim) +{ + dim *= dim>>5; + for (S32 i=0; i>= 4; + + *eightBits++ = *(U32*)&bitTable[val&0xF]; + val >>= 4; + + *eightBits++ = *(U32*)&bitTable[val&0xF]; + val >>= 4; + + *eightBits++ = *(U32*)&bitTable[val&0xF]; + val >>= 4; + + *eightBits++ = *(U32*)&bitTable[val&0xF]; + val >>= 4; + + *eightBits++ = *(U32*)&bitTable[val&0xF]; + val >>= 4; + + *eightBits++ = *(U32*)&bitTable[val&0xF]; + val >>= 4; + + *eightBits++ = *(U32*)&bitTable[val&0xF]; + val >>= 4; + } +} + + +U8 bitTableA[16][4] = +{ + SF32( 0, 0, 0, 0), // 0 + SF32( 0, 0, 0, 0), // 1 + SF32( 0, 0, 0, 0), // 2 + SF32( 0, 0, 0, 0), // 3 + SF32( 0, 0, 0, 0), // 4 + SF32( 0, 0, 0, 0), // 5 + SF32( 0, 0, 0, 0), // 6 + SF32( 0, 0, 0, 0), // 7 + SF32( 17, 0, 0, 0), // 8 + SF32( 17, 0, 0, 0), // 9 + SF32( 17, 0, 0, 0), // 10 + SF32( 17, 0, 0, 0), // 11 + SF32( 17, 0, 0, 0), // 12 + SF32( 17, 0, 0, 0), // 13 + SF32( 17, 0, 0, 0), // 14 + SF32( 17, 0, 0, 0), // 15 +}; + +U8 bitTableB[16][4] = +{ + SF32( 0, 0, 0, 0), // 0 + SF32( 34, 17, 0, 0), // 1 + SF32( 17, 34, 17, 0), // 2 + SF32( 51, 51, 17, 0), // 3 + SF32( 0, 17, 34, 17), // 4 + SF32( 34, 34, 34, 17), // 5 + SF32( 17, 51, 51, 17), // 6 + SF32( 51, 68, 51, 17), // 7 + SF32( 0, 0, 17, 34), // 8 + SF32( 34, 17, 17, 34), // 9 + SF32( 17, 34, 34, 34), // 10 + SF32( 51, 51, 34, 34), // 11 + SF32( 0, 17, 51, 51), // 12 + SF32( 34, 34, 51, 51), // 13 + SF32( 17, 51, 68, 51), // 14 + SF32( 51, 68, 68, 51), // 15 +}; + + +U8 bitTableC[16][4] = +{ + SF32( 0, 0, 0, 0), // 0 + SF32( 0, 0, 0, 17), // 1 + SF32( 0, 0, 0, 0), // 2 + SF32( 0, 0, 0, 17), // 3 + SF32( 0, 0, 0, 0), // 4 + SF32( 0, 0, 0, 17), // 5 + SF32( 0, 0, 0, 0), // 6 + SF32( 0, 0, 0, 17), // 7 + SF32( 0, 0, 0, 0), // 8 + SF32( 0, 0, 0, 17), // 9 + SF32( 0, 0, 0, 0), // 10 + SF32( 0, 0, 0, 17), // 11 + SF32( 0, 0, 0, 0), // 12 + SF32( 0, 0, 0, 17), // 13 + SF32( 0, 0, 0, 0), // 14 + SF32( 0, 0, 0, 17), // 15 +}; + +U8 bitTableE[16][4] = +{ + SF32( 0, 0, 0, 0), // 0 + SF32( 51, 34, 0, 0), // 1 + SF32( 34, 51, 34, 0), // 2 + SF32( 85, 85, 34, 0), // 3 + SF32( 0, 34, 51, 34), // 4 + SF32( 51, 68, 51, 34), // 5 + SF32( 34, 85, 85, 34), // 6 + SF32( 85,119, 85, 34), // 7 + SF32( 0, 0, 34, 51), // 8 + SF32( 51, 34, 34, 51), // 9 + SF32( 34, 51, 68, 51), // 10 + SF32( 85, 85, 68, 51), // 11 + SF32( 0, 34, 85, 85), // 12 + SF32( 51, 68, 85, 85), // 13 + SF32( 34, 85,119, 85), // 14 + SF32( 85,119,119, 85), // 15 +}; + +void BitRender::bitTo8Bit_3(U32 * bits, U32 * eightBits, S32 dim) +{ +#if defined(TORQUE_BIG_ENDIAN) +#define MAX_SHADOW_TEXELS (256 + 4) //256 seems big enough, +4 so we can run off end of buffer. + // slow fake gaussian + int i, j, c; + U8 tmpLine[3][MAX_SHADOW_TEXELS]; + U8 *src0, *src1, *src2; + U8 *s0, *s1, *s2; + U32 dimS2 = dim>>2; + U32 *src32; + U32 *currLine = bits; + U32 currVal; + U32 sampleVal; + U8 c00, c01, c02, c10, c11, c12, c20, c21, c22; + int openBuf; + + src0 = tmpLine[0]; + src1 = tmpLine[1]; + src2 = tmpLine[2]; + openBuf = 2; // the one src2 is using right now. + + // pre-process two rows into our tmp buffers. + src32 = (U32*)(src0); + for(i=0; i<(dimS2>>3); i++) // walk 4 bytes at a time, 4 bits at a time. + { + currVal = *currLine++; + *src32++ = *(U32*)&bitTable[0x0F & currVal]; currVal >>= 4; + *src32++ = *(U32*)&bitTable[0x0F & currVal]; currVal >>= 4; + *src32++ = *(U32*)&bitTable[0x0F & currVal]; currVal >>= 4; + *src32++ = *(U32*)&bitTable[0x0F & currVal]; currVal >>= 4; + *src32++ = *(U32*)&bitTable[0x0F & currVal]; currVal >>= 4; + *src32++ = *(U32*)&bitTable[0x0F & currVal]; currVal >>= 4; + *src32++ = *(U32*)&bitTable[0x0F & currVal]; currVal >>= 4; + *src32++ = *(U32*)&bitTable[currVal]; + } + + src32 = (U32*)(src1); + for(i=0; i<(dimS2>>3); i++) + { + currVal = *currLine++; + *src32++ = *(U32*)&bitTable[0x0F & currVal]; currVal >>= 4; + *src32++ = *(U32*)&bitTable[0x0F & currVal]; currVal >>= 4; + *src32++ = *(U32*)&bitTable[0x0F & currVal]; currVal >>= 4; + *src32++ = *(U32*)&bitTable[0x0F & currVal]; currVal >>= 4; + *src32++ = *(U32*)&bitTable[0x0F & currVal]; currVal >>= 4; + *src32++ = *(U32*)&bitTable[0x0F & currVal]; currVal >>= 4; + *src32++ = *(U32*)&bitTable[0x0F & currVal]; currVal >>= 4; + *src32++ = *(U32*)&bitTable[currVal]; + } + + // pre-clear first row of depth buffer. + for (i=0; i0) + { + j--; + // process new line (currLine) into new tmp buffer (src2) + src32 = (U32*)(src2); + for(i=0; i<(dimS2>>3); i++) // 8 at a time. + { + currVal = *currLine++; + *src32++ = *(U32*)&bitTable[0x0F & currVal]; currVal >>= 4; + *src32++ = *(U32*)&bitTable[0x0F & currVal]; currVal >>= 4; + *src32++ = *(U32*)&bitTable[0x0F & currVal]; currVal >>= 4; + *src32++ = *(U32*)&bitTable[0x0F & currVal]; currVal >>= 4; + *src32++ = *(U32*)&bitTable[0x0F & currVal]; currVal >>= 4; + *src32++ = *(U32*)&bitTable[0x0F & currVal]; currVal >>= 4; + *src32++ = *(U32*)&bitTable[0x0F & currVal]; currVal >>= 4; + *src32++ = *(U32*)&bitTable[currVal]; + } + + // blur & copy current src1 to current dest +#if NO_BLUR + // test for basic functionality. + srcTmp = src1; + src32 = (U32*)srcTmp; + for (i=0; i>= 5; // div by 32 subsamples + } + // mix into val store to hold. + currVal |= sampleVal << (8*(3-c)); + c00 = c01; c01 = c02; + c10 = c11; c11 = c12; + c20 = c21; c21 = c22; + //c#2 defd next time round + } + // put samples into dest buffer. + *eightBits++ = currVal; + } +#endif + + // flip around ptrs for next row processing. + openBuf++; + if (openBuf>2) + openBuf = 0; + src0 = src1; + src1 = src2; + src2 = tmpLine[openBuf]; + } + + // clear last dest buffer row + for (i=0; i>2); + do { *eightBits++=0; } while (eightBits>2)*(dim-1); + + U8 * p0 = (U8*)bits; + U8 bitLo10 = 0x0F & *p0; + U8 bitHi10 = (*p0) >> 4; + p0++; + + U8 * p1 = (U8*)bits + (dim>>3); + U8 bitLo11 = 0x0F & *p1; + U8 bitHi11 = (*p1) >> 4; + p1++; + + U8 * p2 = (U8*)bits + (dim>>2); + U8 bitLo12 = 0x0F & *p2; + U8 bitHi12 = (*p2) >> 4; + p2++; + + U8 bitLo20, bitHi20; + U8 bitLo21, bitHi21; + U8 bitLo22, bitHi22; + U8 bitHi00 = 0; + U8 bitHi01 = 0; + U8 bitHi02 = 0; + + // go thru penultimate row (but stop before last entry in that row) + U8 * end = (U8*)bits + dim*(dim>>3) - 1; + do + { + bitLo20 = 0x0F & *p0; + bitHi20 = (*p0) >> 4; + bitLo21 = 0x0F & *p1; + bitHi21 = (*p1) >> 4; + bitLo22 = 0x0F & *p2; + bitHi22 = (*p2) >> 4; + + *eightBits++ = *(U32*)&bitTableA[bitHi00] + *(U32*)&bitTableB[bitLo10] + *(U32*)&bitTableC[bitHi10] + + *(U32*)&bitTableA[bitHi01]*2 + *(U32*)&bitTableE[bitLo11] + *(U32*)&bitTableC[bitHi11]*2 + + *(U32*)&bitTableA[bitHi02] + *(U32*)&bitTableB[bitLo12] + *(U32*)&bitTableC[bitHi12]; + *eightBits++ = *(U32*)&bitTableA[bitLo10] + *(U32*)&bitTableB[bitHi10] + *(U32*)&bitTableC[bitLo20] + + *(U32*)&bitTableA[bitLo11]*2 + *(U32*)&bitTableE[bitHi11] + *(U32*)&bitTableC[bitLo21]*2 + + *(U32*)&bitTableA[bitLo12] + *(U32*)&bitTableB[bitHi12] + *(U32*)&bitTableC[bitLo22]; + + bitHi00 = bitHi10; + bitLo10 = bitLo20; + bitHi10 = bitHi20; + bitHi01 = bitHi11; + bitLo11 = bitLo21; + bitHi11 = bitHi21; + bitHi02 = bitHi12; + bitLo12 = bitLo22; + bitHi12 = bitHi22; + + p0++; + p1++; + p2++; + } + while (p2( mBits ); + const U32* bits2 = reinterpret_cast< const U32* >( vector.mBits ); + + for( U32 i = 0; i < numDWORDS; ++ i ) + { + if( !( bits1[ i ] & bits2[ i ] ) ) + continue; + else if( bits2[ i ] && all ) + return false; + else + return true; + } + + return false; +} + +bool BitVector::testAll() const +{ + const U32 remaider = mSize % 8; + const U32 testBytes = mSize / 8; + + for ( U32 i=0; i < testBytes; i++ ) + if ( mBits[i] != 0xFF ) + return false; + + if ( remaider == 0 ) + return true; + + const U8 mask = (U8)0xFF >> ( 8 - remaider ); + return ( mBits[testBytes] & mask ) == mask; +} + +bool BitVector::testAllClear() const +{ + const U32 remaider = mSize % 8; + const U32 testBytes = mSize / 8; + + for ( U32 i=0; i < testBytes; i++ ) + if ( mBits[i] != 0 ) + return false; + + if ( remaider == 0 ) + return true; + + const U8 mask = (U8)0xFF >> ( 8 - remaider ); + return ( mBits[testBytes] & mask ) == 0; +} diff --git a/Engine/source/core/bitVector.h b/Engine/source/core/bitVector.h new file mode 100644 index 000000000..e1bc6c709 --- /dev/null +++ b/Engine/source/core/bitVector.h @@ -0,0 +1,220 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _BITVECTOR_H_ +#define _BITVECTOR_H_ + + +/// Manage a vector of bits of arbitrary size. +class BitVector +{ + protected: + + /// The array of bytes that stores our bits. + U8* mBits; + + /// The allocated size of the bit array. + U32 mByteSize; + + /// The size of the vector in bits. + U32 mSize; + + /// Returns a size in bytes which is 32bit aligned + /// and can hold all the requested bits. + static U32 calcByteSize( const U32 numBits ); + + /// Internal function which resizes the bit array. + void _resize( U32 sizeInBits, bool copyBits ); + + bool _test( const BitVector& vector, bool all ) const; + + public: + + /// Default constructor which creates an bit + /// vector with a bit size of zero. + BitVector(); + + /// Constructs a bit vector with the desired size. + /// @note The resulting vector is not cleared. + BitVector( U32 sizeInBits ); + + /// Destructor. + ~BitVector(); + + /// @name Size Management + /// @{ + + /// Return true if the bit vector is empty. + bool empty() const { return ( mSize == 0 ); } + + /// Resizes the bit vector. + /// @note The new bits in the vector are not cleared and + /// contain random garbage bits. + void setSize( U32 sizeInBits ); + + /// Returns the size in bits. + U32 getSize() const { return mSize; } + + /// Returns the 32bit aligned size in bytes. + U32 getByteSize() const { return mByteSize; } + + /// Returns the bits. + const U8* getBits() const { return mBits; } + U8* getBits() { return mBits; } + + /// @} + + /// Copy the content of another bit vector. + void copy( const BitVector &from ); + + /// @name Mutators + /// Note that bits are specified by index, unlike BitSet32. + /// @{ + + /// Set the specified bit. + void set(U32 bit); + + /// Set the specified bit on or off. + void set(U32 bit, bool on ); + + /// Set all the bits. + void set(); + + /// Clear the specified bit. + void clear(U32 bit); + + /// Clear all the bits. + void clear(); + + /// Does an OR operation between BitVectors. + void combineOR( const BitVector &other ); + + /// Test that the specified bit is set. + bool test(U32 bit) const; + + /// Test this vector's bits against all the corresponding bits + /// in @a vector and return true if any of the bits that are + /// set in @a vector are also set in this vector. + /// + /// @param vector Bit vector of the same size. + bool testAny( const BitVector& vector ) const { return _test( vector, false ); } + + /// Test this vector's bits against all the corresponding bits + /// in @a vector and return true if all of the bits that are + /// set in @a vector are also set in this vector. + /// + /// @param vector Bit vector of the same size. + bool testAll( const BitVector& vector ) const { return _test( vector, true ); } + + /// Return true if all bits are set. + bool testAll() const; + + /// Return true if all bits are clear. + bool testAllClear() const; + + /// @} +}; + +inline BitVector::BitVector() +{ + mBits = NULL; + mByteSize = 0; + mSize = 0; +} + + +inline BitVector::BitVector( U32 sizeInBits ) +{ + mBits = NULL; + mByteSize = 0; + mSize = 0; + setSize( sizeInBits ); +} + +inline BitVector::~BitVector() +{ + delete [] mBits; + mBits = NULL; + mByteSize = 0; + mSize = 0; +} + +inline U32 BitVector::calcByteSize( U32 numBits ) +{ + // Make sure that we are 32 bit aligned + return (((numBits + 0x7) >> 3) + 0x3) & ~0x3; +} + +inline void BitVector::setSize( const U32 sizeInBits ) +{ + _resize( sizeInBits, true ); +} + +inline void BitVector::clear() +{ + if (mSize != 0) + dMemset( mBits, 0x00, getByteSize() ); +} + +inline void BitVector::copy( const BitVector &from ) +{ + _resize( from.getSize(), false ); + if (mSize != 0) + dMemcpy( mBits, from.getBits(), getByteSize() ); +} + +inline void BitVector::set() +{ + if (mSize != 0) + dMemset(mBits, 0xFF, getByteSize() ); +} + +inline void BitVector::set(U32 bit) +{ + AssertFatal(bit < mSize, "BitVector::set - Error, out of range bit!"); + + mBits[bit >> 3] |= U8(1 << (bit & 0x7)); +} + +inline void BitVector::set(U32 bit, bool on ) +{ + AssertFatal(bit < mSize, "BitVector::set - Error, out of range bit!"); + + if ( on ) + mBits[bit >> 3] |= U8(1 << (bit & 0x7)); + else + mBits[bit >> 3] &= U8(~(1 << (bit & 0x7))); +} + +inline void BitVector::clear(U32 bit) +{ + AssertFatal(bit < mSize, "BitVector::clear - Error, out of range bit!"); + mBits[bit >> 3] &= U8(~(1 << (bit & 0x7))); +} + +inline bool BitVector::test(U32 bit) const +{ + AssertFatal(bit < mSize, "BitVector::test - Error, out of range bit!"); + return (mBits[bit >> 3] & U8(1 << (bit & 0x7))) != 0; +} + +#endif //_BITVECTOR_H_ diff --git a/Engine/source/core/bitVectorW.h b/Engine/source/core/bitVectorW.h new file mode 100644 index 000000000..6a023611a --- /dev/null +++ b/Engine/source/core/bitVectorW.h @@ -0,0 +1,108 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _BITVECTORW_H_ +#define _BITVECTORW_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif + +/// @see BitVector +class BitVectorW +{ + U32 mNumEntries; + U32 mBitWidth; + U32 mBitMask; + U8 * mDataPtr; + + public: + BitVectorW() {mDataPtr=NULL; setDims(0,0);} + ~BitVectorW() {if(mDataPtr) delete [] mDataPtr;} + + U32 bitWidth() const {return mBitWidth;} + U32 numEntries() const {return mNumEntries;} + U8 * dataPtr() const {return mDataPtr;} + + U32 getU17(U32 idx) const; // get and set for bit widths + void setU17(U32 idx, U32 val); // of 17 or less + U32 numBytes() const; + void setDims(U32 sz, U32 w); +}; + +//------------------------------------------------------------------------------------- + +inline U32 BitVectorW::numBytes() const +{ + if (mNumEntries > 0) + return (mBitWidth * mNumEntries) + 32 >> 3; + else + return 0; +} + +// Alloc the data - note it does work for a bit width of zero (lookups return zero) +inline void BitVectorW::setDims(U32 size, U32 width) +{ + if (mDataPtr) + delete [] mDataPtr; + + if (size > 0 && width <= 17) + { + mBitWidth = width; + mNumEntries = size; + mBitMask = (1 << width) - 1; + U32 dataSize = numBytes(); + mDataPtr = new U8 [dataSize]; + dMemset(mDataPtr, 0, dataSize); + } + else + { + mDataPtr = NULL; + mBitWidth = mBitMask = mNumEntries = 0; + } +} + +//------------------------------------------------------------------------------------- +// For coding ease, the get and set methods might read or write an extra byte or two. +// If more or less max bit width is ever needed, add or remove the x[] expressions. + +inline U32 BitVectorW::getU17(U32 i) const +{ + if (mDataPtr) { + register U8 * x = &mDataPtr[(i *= mBitWidth) >> 3]; + return (U32(*x) + (U32(x[1])<<8) + (U32(x[2])<<16) >> (i&7)) & mBitMask; + } + return 0; +} + +inline void BitVectorW::setU17(U32 i, U32 value) +{ + if (mDataPtr) { + register U8 * x = &mDataPtr[(i *= mBitWidth) >> 3]; + register U32 mask = mBitMask << (i &= 7); + x[0] = (x[0] & (~mask >> 0)) | ((value <<= i) & (mask >> 0)); + x[1] = (x[1] & (~mask >> 8)) | ((value >> 8) & (mask >> 8)); + x[2] = (x[2] & (~mask >> 16)) | ((value >> 16) & (mask >> 16)); + } +} + +#endif //_BITVECTORW_H_ diff --git a/Engine/source/core/color.cpp b/Engine/source/core/color.cpp new file mode 100644 index 000000000..17df1cb44 --- /dev/null +++ b/Engine/source/core/color.cpp @@ -0,0 +1,40 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/color.h" + +const ColorF ColorF::ZERO( 0, 0, 0, 0 ); +const ColorF ColorF::ONE( 1, 1, 1, 1 ); +const ColorF ColorF::WHITE( 1, 1, 1 ); +const ColorF ColorF::BLACK( 0, 0, 0 ); +const ColorF ColorF::RED( 1, 0, 0 ); +const ColorF ColorF::GREEN( 0, 1, 0 ); +const ColorF ColorF::BLUE( 0, 0, 1 ); + +const ColorI ColorI::ZERO( 0, 0, 0, 0 ); +const ColorI ColorI::ONE( 255, 255, 255, 255 ); +const ColorI ColorI::WHITE( 255, 255, 255 ); +const ColorI ColorI::BLACK( 0, 0, 0 ); +const ColorI ColorI::RED( 255, 0, 0 ); +const ColorI ColorI::GREEN( 0, 255, 0 ); +const ColorI ColorI::BLUE( 0, 0, 255 ); diff --git a/Engine/source/core/color.h b/Engine/source/core/color.h new file mode 100644 index 000000000..7a9206cd3 --- /dev/null +++ b/Engine/source/core/color.h @@ -0,0 +1,608 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _COLOR_H_ +#define _COLOR_H_ + +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _MPOINT4_H_ +#include "math/mPoint4.h" +#endif + +class ColorI; + + +class ColorF +{ + public: + F32 red; + F32 green; + F32 blue; + F32 alpha; + + public: + ColorF() { } + ColorF(const ColorF& in_rCopy); + ColorF(const F32 in_r, + const F32 in_g, + const F32 in_b, + const F32 in_a = 1.0f); + + void set(const F32 in_r, + const F32 in_g, + const F32 in_b, + const F32 in_a = 1.0f); + + ColorF& operator*=(const ColorF& in_mul); // Can be useful for lighting + ColorF operator*(const ColorF& in_mul) const; + ColorF& operator+=(const ColorF& in_rAdd); + ColorF operator+(const ColorF& in_rAdd) const; + ColorF& operator-=(const ColorF& in_rSub); + ColorF operator-(const ColorF& in_rSub) const; + + ColorF& operator*=(const F32 in_mul); + ColorF operator*(const F32 in_mul) const; + ColorF& operator/=(const F32 in_div); + ColorF operator/(const F32 in_div) const; + + ColorF operator-() const; + + bool operator==(const ColorF&) const; + bool operator!=(const ColorF&) const; + + operator F32*() { return &red; } + operator const F32*() const { return &red; } + + operator Point3F() const { return Point3F( red, green, blue ); } + operator Point4F() const { return Point4F( red, green, blue, alpha ); } + + U32 getARGBPack() const; + U32 getRGBAPack() const; + U32 getABGRPack() const; + + operator ColorI() const; + + void interpolate(const ColorF& in_rC1, + const ColorF& in_rC2, + const F32 in_factor); + + bool isValidColor() const { return (red >= 0.0f && red <= 1.0f) && + (green >= 0.0f && green <= 1.0f) && + (blue >= 0.0f && blue <= 1.0f) && + (alpha >= 0.0f && alpha <= 1.0f); } + void clamp(); + + static const ColorF ZERO; + static const ColorF ONE; + static const ColorF WHITE; + static const ColorF BLACK; + static const ColorF RED; + static const ColorF GREEN; + static const ColorF BLUE; +}; + + +//-------------------------------------- ColorI's are missing some of the operations +// present in ColorF since they cannot recover +// properly from over/underflow. +class ColorI +{ + public: + U8 red; + U8 green; + U8 blue; + U8 alpha; + + public: + ColorI() { } + ColorI(const ColorI& in_rCopy); + ColorI(const U8 in_r, + const U8 in_g, + const U8 in_b, + const U8 in_a = U8(255)); + ColorI(const ColorI& in_rCopy, const U8 in_a); + + void set(const U8 in_r, + const U8 in_g, + const U8 in_b, + const U8 in_a = U8(255)); + + void set(const ColorI& in_rCopy, + const U8 in_a); + + ColorI& operator*=(const F32 in_mul); + ColorI operator*(const F32 in_mul) const; + + ColorI operator+(const ColorI& in_rAdd) const; + ColorI& operator+=(const ColorI& in_rAdd); + + ColorI& operator*=(const S32 in_mul); + ColorI& operator/=(const S32 in_mul); + ColorI operator*(const S32 in_mul) const; + ColorI operator/(const S32 in_mul) const; + + bool operator==(const ColorI&) const; + bool operator!=(const ColorI&) const; + + void interpolate(const ColorI& in_rC1, + const ColorI& in_rC2, + const F32 in_factor); + + U32 getARGBPack() const; + U32 getRGBAPack() const; + U32 getABGRPack() const; + + U32 getBGRPack() const; + U32 getRGBPack() const; + + U32 getRGBEndian() const; + U32 getARGBEndian() const; + + U16 get565() const; + U16 get4444() const; + + operator ColorF() const; + + operator const U8*() const { return &red; } + + static const ColorI ZERO; + static const ColorI ONE; + static const ColorI WHITE; + static const ColorI BLACK; + static const ColorI RED; + static const ColorI GREEN; + static const ColorI BLUE; +}; + +//------------------------------------------------------------------------------ +//-------------------------------------- INLINES (ColorF) +// +inline void ColorF::set(const F32 in_r, + const F32 in_g, + const F32 in_b, + const F32 in_a) +{ + red = in_r; + green = in_g; + blue = in_b; + alpha = in_a; +} + +inline ColorF::ColorF(const ColorF& in_rCopy) +{ + red = in_rCopy.red; + green = in_rCopy.green; + blue = in_rCopy.blue; + alpha = in_rCopy.alpha; +} + +inline ColorF::ColorF(const F32 in_r, + const F32 in_g, + const F32 in_b, + const F32 in_a) +{ + set(in_r, in_g, in_b, in_a); +} + +inline ColorF& ColorF::operator*=(const ColorF& in_mul) +{ + red *= in_mul.red; + green *= in_mul.green; + blue *= in_mul.blue; + alpha *= in_mul.alpha; + + return *this; +} + +inline ColorF ColorF::operator*(const ColorF& in_mul) const +{ + return ColorF(red * in_mul.red, + green * in_mul.green, + blue * in_mul.blue, + alpha * in_mul.alpha); +} + +inline ColorF& ColorF::operator+=(const ColorF& in_rAdd) +{ + red += in_rAdd.red; + green += in_rAdd.green; + blue += in_rAdd.blue; + alpha += in_rAdd.alpha; + + return *this; +} + +inline ColorF ColorF::operator+(const ColorF& in_rAdd) const +{ + return ColorF(red + in_rAdd.red, + green + in_rAdd.green, + blue + in_rAdd.blue, + alpha + in_rAdd.alpha); +} + +inline ColorF& ColorF::operator-=(const ColorF& in_rSub) +{ + red -= in_rSub.red; + green -= in_rSub.green; + blue -= in_rSub.blue; + alpha -= in_rSub.alpha; + + return *this; +} + +inline ColorF ColorF::operator-(const ColorF& in_rSub) const +{ + return ColorF(red - in_rSub.red, + green - in_rSub.green, + blue - in_rSub.blue, + alpha - in_rSub.alpha); +} + +inline ColorF& ColorF::operator*=(const F32 in_mul) +{ + red *= in_mul; + green *= in_mul; + blue *= in_mul; + alpha *= in_mul; + + return *this; +} + +inline ColorF ColorF::operator*(const F32 in_mul) const +{ + return ColorF(red * in_mul, + green * in_mul, + blue * in_mul, + alpha * in_mul); +} + +inline ColorF& ColorF::operator/=(const F32 in_div) +{ + AssertFatal(in_div != 0.0f, "Error, div by zero..."); + F32 inv = 1.0f / in_div; + + red *= inv; + green *= inv; + blue *= inv; + alpha *= inv; + + return *this; +} + +inline ColorF ColorF::operator/(const F32 in_div) const +{ + AssertFatal(in_div != 0.0f, "Error, div by zero..."); + F32 inv = 1.0f / in_div; + + return ColorF(red * inv, + green * inv, + blue * inv, + alpha * inv); +} + +inline ColorF ColorF::operator-() const +{ + return ColorF(-red, -green, -blue, -alpha); +} + +inline bool ColorF::operator==(const ColorF& in_Cmp) const +{ + return (red == in_Cmp.red && green == in_Cmp.green && blue == in_Cmp.blue && alpha == in_Cmp.alpha); +} + +inline bool ColorF::operator!=(const ColorF& in_Cmp) const +{ + return (red != in_Cmp.red || green != in_Cmp.green || blue != in_Cmp.blue || alpha != in_Cmp.alpha); +} + +inline U32 ColorF::getARGBPack() const +{ + return (U32(alpha * 255.0f + 0.5) << 24) | + (U32(red * 255.0f + 0.5) << 16) | + (U32(green * 255.0f + 0.5) << 8) | + (U32(blue * 255.0f + 0.5) << 0); +} + +inline U32 ColorF::getRGBAPack() const +{ + return ( U32( red * 255.0f + 0.5) << 0 ) | + ( U32( green * 255.0f + 0.5) << 8 ) | + ( U32( blue * 255.0f + 0.5) << 16 ) | + ( U32( alpha * 255.0f + 0.5) << 24 ); +} + +inline U32 ColorF::getABGRPack() const +{ + return (U32(alpha * 255.0f + 0.5) << 24) | + (U32(blue * 255.0f + 0.5) << 16) | + (U32(green * 255.0f + 0.5) << 8) | + (U32(red * 255.0f + 0.5) << 0); + +} + +inline void ColorF::interpolate(const ColorF& in_rC1, + const ColorF& in_rC2, + const F32 in_factor) +{ + F32 f2 = 1.0f - in_factor; + red = (in_rC1.red * f2) + (in_rC2.red * in_factor); + green = (in_rC1.green * f2) + (in_rC2.green * in_factor); + blue = (in_rC1.blue * f2) + (in_rC2.blue * in_factor); + alpha = (in_rC1.alpha * f2) + (in_rC2.alpha * in_factor); +} + +inline void ColorF::clamp() +{ + if (red > 1.0f) + red = 1.0f; + else if (red < 0.0f) + red = 0.0f; + + if (green > 1.0f) + green = 1.0f; + else if (green < 0.0f) + green = 0.0f; + + if (blue > 1.0f) + blue = 1.0f; + else if (blue < 0.0f) + blue = 0.0f; + + if (alpha > 1.0f) + alpha = 1.0f; + else if (alpha < 0.0f) + alpha = 0.0f; +} + +//------------------------------------------------------------------------------ +//-------------------------------------- INLINES (ColorI) +// +inline void ColorI::set(const U8 in_r, + const U8 in_g, + const U8 in_b, + const U8 in_a) +{ + red = in_r; + green = in_g; + blue = in_b; + alpha = in_a; +} + +inline void ColorI::set(const ColorI& in_rCopy, + const U8 in_a) +{ + red = in_rCopy.red; + green = in_rCopy.green; + blue = in_rCopy.blue; + alpha = in_a; +} + +inline ColorI::ColorI(const ColorI& in_rCopy) +{ + red = in_rCopy.red; + green = in_rCopy.green; + blue = in_rCopy.blue; + alpha = in_rCopy.alpha; +} + +inline ColorI::ColorI(const U8 in_r, + const U8 in_g, + const U8 in_b, + const U8 in_a) +{ + set(in_r, in_g, in_b, in_a); +} + +inline ColorI::ColorI(const ColorI& in_rCopy, + const U8 in_a) +{ + set(in_rCopy, in_a); +} + +inline ColorI& ColorI::operator*=(const F32 in_mul) +{ + red = U8((F32(red) * in_mul) + 0.5f); + green = U8((F32(green) * in_mul) + 0.5f); + blue = U8((F32(blue) * in_mul) + 0.5f); + alpha = U8((F32(alpha) * in_mul) + 0.5f); + + return *this; +} + +inline ColorI& ColorI::operator*=(const S32 in_mul) +{ + red = red * in_mul; + green = green * in_mul; + blue = blue * in_mul; + alpha = alpha * in_mul; + + return *this; +} + +inline ColorI& ColorI::operator/=(const S32 in_mul) +{ + red = red / in_mul; + green = green / in_mul; + blue = blue / in_mul; + alpha = alpha / in_mul; + + return *this; +} + +inline ColorI ColorI::operator+(const ColorI &in_add) const +{ + ColorI tmp; + + tmp.red = red + in_add.red; + tmp.green = green + in_add.green; + tmp.blue = blue + in_add.blue; + tmp.alpha = alpha + in_add.alpha; + + return tmp; +} + +inline ColorI ColorI::operator*(const F32 in_mul) const +{ + ColorI temp(*this); + temp *= in_mul; + return temp; +} + +inline ColorI ColorI::operator*(const S32 in_mul) const +{ + ColorI temp(*this); + temp *= in_mul; + return temp; +} + +inline ColorI ColorI::operator/(const S32 in_mul) const +{ + ColorI temp(*this); + temp /= in_mul; + return temp; +} + +inline bool ColorI::operator==(const ColorI& in_Cmp) const +{ + return (red == in_Cmp.red && green == in_Cmp.green && blue == in_Cmp.blue && alpha == in_Cmp.alpha); +} + +inline bool ColorI::operator!=(const ColorI& in_Cmp) const +{ + return (red != in_Cmp.red || green != in_Cmp.green || blue != in_Cmp.blue || alpha != in_Cmp.alpha); +} + +inline ColorI& ColorI::operator+=(const ColorI& in_rAdd) +{ + red += in_rAdd.red; + green += in_rAdd.green; + blue += in_rAdd.blue; + alpha += in_rAdd.alpha; + + return *this; +} + +inline void ColorI::interpolate(const ColorI& in_rC1, + const ColorI& in_rC2, + const F32 in_factor) +{ + F32 f2= 1.0f - in_factor; + red = U8(((F32(in_rC1.red) * f2) + (F32(in_rC2.red) * in_factor)) + 0.5f); + green = U8(((F32(in_rC1.green) * f2) + (F32(in_rC2.green) * in_factor)) + 0.5f); + blue = U8(((F32(in_rC1.blue) * f2) + (F32(in_rC2.blue) * in_factor)) + 0.5f); + alpha = U8(((F32(in_rC1.alpha) * f2) + (F32(in_rC2.alpha) * in_factor)) + 0.5f); +} + +inline U32 ColorI::getARGBPack() const +{ + return (U32(alpha) << 24) | + (U32(red) << 16) | + (U32(green) << 8) | + (U32(blue) << 0); +} + +inline U32 ColorI::getRGBAPack() const +{ + return ( U32( red ) << 0 ) | + ( U32( green ) << 8 ) | + ( U32( blue ) << 16 ) | + ( U32( alpha ) << 24 ); +} + +inline U32 ColorI::getABGRPack() const +{ + return (U32(alpha) << 24) | + (U32(blue) << 16) | + (U32(green) << 8) | + (U32(red) << 0); +} + + +inline U32 ColorI::getBGRPack() const +{ + return (U32(blue) << 16) | + (U32(green) << 8) | + (U32(red) << 0); +} + +inline U32 ColorI::getRGBPack() const +{ + return (U32(red) << 16) | + (U32(green) << 8) | + (U32(blue) << 0); +} + +inline U32 ColorI::getRGBEndian() const +{ +#if defined(TORQUE_BIG_ENDIAN) + return(getRGBPack()); +#else + return(getBGRPack()); +#endif +} + +inline U32 ColorI::getARGBEndian() const +{ +#if defined(TORQUE_BIG_ENDIAN) + return(getABGRPack()); +#else + return(getARGBPack()); +#endif +} + +inline U16 ColorI::get565() const +{ + return U16((U16(red >> 3) << 11) | + (U16(green >> 2) << 5) | + (U16(blue >> 3) << 0)); +} + +inline U16 ColorI::get4444() const +{ + return U16(U16(U16(alpha >> 4) << 12) | + U16(U16(red >> 4) << 8) | + U16(U16(green >> 4) << 4) | + U16(U16(blue >> 4) << 0)); +} + +//-------------------------------------- INLINE CONVERSION OPERATORS +inline ColorF::operator ColorI() const +{ + return ColorI(U8(red * 255.0f + 0.5), + U8(green * 255.0f + 0.5), + U8(blue * 255.0f + 0.5), + U8(alpha * 255.0f + 0.5)); +} + +inline ColorI::operator ColorF() const +{ + const F32 inv255 = 1.0f / 255.0f; + + return ColorF(F32(red) * inv255, + F32(green) * inv255, + F32(blue) * inv255, + F32(alpha) * inv255); +} + +#endif //_COLOR_H_ diff --git a/Engine/source/core/crc.cpp b/Engine/source/core/crc.cpp new file mode 100644 index 000000000..66ee4f20e --- /dev/null +++ b/Engine/source/core/crc.cpp @@ -0,0 +1,90 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/crc.h" + +#include "core/stream/stream.h" + +//----------------------------------------------------------------------------- +// simple crc function - generates lookup table on first call + +static U32 crcTable[256]; +static bool crcTableValid; + +static void calculateCRCTable() +{ + U32 val; + + for(S32 i = 0; i < 256; i++) + { + val = i; + for(S32 j = 0; j < 8; j++) + { + if(val & 0x01) + val = 0xedb88320 ^ (val >> 1); + else + val = val >> 1; + } + crcTable[i] = val; + } + + crcTableValid = true; +} + + +//----------------------------------------------------------------------------- + +U32 CRC::calculateCRC(const void * buffer, S32 len, U32 crcVal ) +{ + // check if need to generate the crc table + if(!crcTableValid) + calculateCRCTable(); + + // now calculate the crc + char * buf = (char*)buffer; + for(S32 i = 0; i < len; i++) + crcVal = crcTable[(crcVal ^ buf[i]) & 0xff] ^ (crcVal >> 8); + return(crcVal); +} + +U32 CRC::calculateCRCStream(Stream *stream, U32 crcVal ) +{ + // check if need to generate the crc table + if(!crcTableValid) + calculateCRCTable(); + + // now calculate the crc + stream->setPosition(0); + S32 len = stream->getStreamSize(); + U8 buf[4096]; + + S32 segCount = (len + 4095) / 4096; + + for(S32 j = 0; j < segCount; j++) + { + S32 slen = getMin(4096, len - (j * 4096)); + stream->read(slen, buf); + crcVal = CRC::calculateCRC(buf, slen, crcVal); + } + stream->setPosition(0); + return(crcVal); +} diff --git a/Engine/source/core/crc.h b/Engine/source/core/crc.h new file mode 100644 index 000000000..97280ae19 --- /dev/null +++ b/Engine/source/core/crc.h @@ -0,0 +1,49 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _CRC_H_ +#define _CRC_H_ + +#ifndef _TORQUE_TYPES_H_ +#include "platform/types.h" +#endif + + +class Stream; + +namespace CRC +{ + /// Initial value for CRCs + const U32 INITIAL_CRC_VALUE = 0xFFFFFFFF; + + /// Value XORd with the CRC to post condition it (ones complement) + const U32 CRC_POSTCOND_VALUE = 0xFFFFFFFF; + + /// An invalid CRC + const U32 INVALID_CRC = 0xFFFFFFFF; + + U32 calculateCRC(const void * buffer, S32 len, U32 crcVal = INITIAL_CRC_VALUE); + U32 calculateCRCStream(Stream *stream, U32 crcVal = INITIAL_CRC_VALUE); +} + +#endif + diff --git a/Engine/source/core/dataChunker.cpp b/Engine/source/core/dataChunker.cpp new file mode 100644 index 000000000..5827ae632 --- /dev/null +++ b/Engine/source/core/dataChunker.cpp @@ -0,0 +1,97 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/dataChunker.h" + + +//---------------------------------------------------------------------------- + +DataChunker::DataChunker(S32 size) +{ + mChunkSize = size; + mCurBlock = NULL; +} + +DataChunker::~DataChunker() +{ + freeBlocks(); +} + +void *DataChunker::alloc(S32 size) +{ + if (size > mChunkSize) + { + DataBlock * temp = new DataBlock(size); + if (mCurBlock) + { + temp->next = mCurBlock->next; + mCurBlock->next = temp; + } + else + { + mCurBlock = temp; + temp->curIndex = mChunkSize; + } + return temp->data; + } + + if(!mCurBlock || size + mCurBlock->curIndex > mChunkSize) + { + DataBlock *temp = new DataBlock(mChunkSize); + temp->next = mCurBlock; + temp->curIndex = 0; + mCurBlock = temp; + } + + void *ret = mCurBlock->data + mCurBlock->curIndex; + mCurBlock->curIndex += (size + 3) & ~3; // dword align + return ret; +} + +DataChunker::DataBlock::DataBlock(S32 size) +{ + data = new U8[size]; +} + +DataChunker::DataBlock::~DataBlock() +{ + delete[] data; +} + +void DataChunker::freeBlocks(bool keepOne) +{ + while(mCurBlock && mCurBlock->next) + { + DataBlock *temp = mCurBlock->next; + delete mCurBlock; + mCurBlock = temp; + } + if (!keepOne) + { + delete mCurBlock; + mCurBlock = NULL; + } + else if (mCurBlock) + mCurBlock->curIndex = 0; +} + diff --git a/Engine/source/core/dataChunker.h b/Engine/source/core/dataChunker.h new file mode 100644 index 000000000..165dcca9c --- /dev/null +++ b/Engine/source/core/dataChunker.h @@ -0,0 +1,297 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _DATACHUNKER_H_ +#define _DATACHUNKER_H_ + +#ifndef _PLATFORM_H_ +# include "platform/platform.h" +#endif + +//---------------------------------------------------------------------------- +/// Implements a chunked data allocator. +/// +/// Calling new/malloc all the time is a time consuming operation. Therefore, +/// we provide the DataChunker, which allocates memory in blocks of +/// chunkSize (by default 16k, see ChunkSize, though it can be set in +/// the constructor), then doles it out as requested, in chunks of up to +/// chunkSize in size. +/// +/// It will assert if you try to get more than ChunkSize bytes at a time, +/// and it deals with the logic of allocating new blocks and giving out +/// word-aligned chunks. +/// +/// Note that new/free/realloc WILL NOT WORK on memory gotten from the +/// DataChunker. This also only grows (you can call freeBlocks to deallocate +/// and reset things). +class DataChunker +{ +public: + enum { + ChunkSize = 16376 ///< Default size of each DataBlock page in the DataChunker + }; + + /// Return a pointer to a chunk of memory from a pre-allocated block. + /// + /// This memory goes away when you call freeBlocks. + /// + /// This memory is word-aligned. + /// @param size Size of chunk to return. This must be less than chunkSize or else + /// an assertion will occur. + void *alloc(S32 size); + + /// Free all allocated memory blocks. + /// + /// This invalidates all pointers returned from alloc(). + void freeBlocks(bool keepOne = false); + + /// Initialize using blocks of a given size. + /// + /// One new block is allocated at constructor-time. + /// + /// @param size Size in bytes of the space to allocate for each block. + DataChunker(S32 size=ChunkSize); + ~DataChunker(); + + /// Swaps the memory allocated in one data chunker for another. This can be used to implement + /// packing of memory stored in a DataChunker. + void swap(DataChunker &d) + { + DataBlock *temp = d.mCurBlock; + d.mCurBlock = mCurBlock; + mCurBlock = temp; + } + +private: + /// Block of allocated memory. + /// + /// This has nothing to do with datablocks as used in the rest of Torque. + struct DataBlock + { + DataBlock* prev; + DataBlock* next; ///< linked list pointer to the next DataBlock for this chunker + U8 *data; ///< allocated pointer for the base of this page + S32 curIndex; ///< current allocation point within this DataBlock + DataBlock(S32 size); + ~DataBlock(); + }; + + DataBlock* mFirstBlock; + DataBlock *mCurBlock; ///< current page we're allocating data from. If the + ///< data size request is greater than the memory space currently + ///< available in the current page, a new page will be allocated. + S32 mChunkSize; ///< The size allocated for each page in the DataChunker +}; + +//---------------------------------------------------------------------------- + +template +class Chunker: private DataChunker +{ +public: + Chunker(S32 size = DataChunker::ChunkSize) : DataChunker(size) {}; + T* alloc() { return reinterpret_cast(DataChunker::alloc(S32(sizeof(T)))); } + void clear() { freeBlocks(); } +}; + +//---------------------------------------------------------------------------- +/// This class is similar to the Chunker<> class above. But it allows for multiple +/// types of structs to be stored. +/// CodeReview: This could potentially go into DataChunker directly, but I wasn't sure if +/// CodeReview: That would be polluting it. BTR +class MultiTypedChunker : private DataChunker +{ +public: + MultiTypedChunker(S32 size = DataChunker::ChunkSize) : DataChunker(size) {}; + + /// Use like so: MyType* t = chunker.alloc(); + template + T* alloc() { return reinterpret_cast(DataChunker::alloc(S32(sizeof(T)))); } + void clear() { freeBlocks(true); } +}; + +//---------------------------------------------------------------------------- + +/// Templatized data chunker class with proper construction and destruction of its elements. +/// +/// DataChunker just allocates space. This subclass actually constructs/destructs the +/// elements. This class is appropriate for more complex classes. +template +class ClassChunker: private DataChunker +{ +public: + ClassChunker(S32 size = DataChunker::ChunkSize) : DataChunker(size) + { + mElementSize = getMax(U32(sizeof(T)), U32(sizeof(T *))); + mFreeListHead = NULL; + } + + /// Allocates and properly constructs in place a new element. + T *alloc() + { + if(mFreeListHead == NULL) + return constructInPlace(reinterpret_cast(DataChunker::alloc(mElementSize))); + T* ret = mFreeListHead; + mFreeListHead = *(reinterpret_cast(mFreeListHead)); + return constructInPlace(ret); + } + + /// Properly destructs and frees an element allocated with the alloc method. + void free(T* elem) + { + destructInPlace(elem); + *(reinterpret_cast(elem)) = mFreeListHead; + mFreeListHead = elem; + } + + void freeBlocks( bool keepOne = false ) + { + DataChunker::freeBlocks( keepOne ); + mFreeListHead = NULL; + } + +private: + S32 mElementSize; ///< the size of each element, or the size of a pointer, whichever is greater + T *mFreeListHead; ///< a pointer to a linked list of freed elements for reuse +}; + +//---------------------------------------------------------------------------- + +template +class FreeListChunker +{ +public: + FreeListChunker(DataChunker *inChunker) + : mChunker( inChunker ), + mOwnChunker( false ), + mFreeListHead( NULL ) + { + mElementSize = getMax(U32(sizeof(T)), U32(sizeof(T *))); + } + + FreeListChunker(S32 size = DataChunker::ChunkSize) + : mFreeListHead( NULL ) + { + mChunker = new DataChunker( size ); + mOwnChunker = true; + + mElementSize = getMax(U32(sizeof(T)), U32(sizeof(T *))); + } + + ~FreeListChunker() + { + if ( mOwnChunker ) + delete mChunker; + } + + T *alloc() + { + if(mFreeListHead == NULL) + return reinterpret_cast(mChunker->alloc(mElementSize)); + T* ret = mFreeListHead; + mFreeListHead = *(reinterpret_cast(mFreeListHead)); + return ret; + } + + void free(T* elem) + { + *(reinterpret_cast(elem)) = mFreeListHead; + mFreeListHead = elem; + } + + /// Allow people to free all their memory if they want. + void freeBlocks( bool keepOne = false ) + { + mChunker->freeBlocks( keepOne ); + mFreeListHead = NULL; + } + +private: + DataChunker *mChunker; + bool mOwnChunker; + + S32 mElementSize; + T *mFreeListHead; +}; + + +class FreeListChunkerUntyped +{ +public: + FreeListChunkerUntyped(U32 inElementSize, DataChunker *inChunker) + : mChunker( inChunker ), + mOwnChunker( false ), + mElementSize( inElementSize ), + mFreeListHead( NULL ) + { + } + + FreeListChunkerUntyped(U32 inElementSize, S32 size = DataChunker::ChunkSize) + : mElementSize( inElementSize ), + mFreeListHead( NULL ) + { + mChunker = new DataChunker( size ); + mOwnChunker = true; + } + + ~FreeListChunkerUntyped() + { + if ( mOwnChunker ) + delete mChunker; + } + + void *alloc() + { + if(mFreeListHead == NULL) + return mChunker->alloc(mElementSize); + + void *ret = mFreeListHead; + mFreeListHead = *(reinterpret_cast(mFreeListHead)); + return ret; + } + + void free(void* elem) + { + *(reinterpret_cast(elem)) = mFreeListHead; + mFreeListHead = elem; + } + + // Allow people to free all their memory if they want. + void freeBlocks() + { + mChunker->freeBlocks(); + + // We have to terminate the freelist as well or else we'll run + // into crazy unused memory. + mFreeListHead = NULL; + } + + U32 getElementSize() const { return mElementSize; } + +private: + DataChunker *mChunker; + bool mOwnChunker; + + const U32 mElementSize; + void *mFreeListHead; +}; +#endif diff --git a/Engine/source/core/dnet.cpp b/Engine/source/core/dnet.cpp new file mode 100644 index 000000000..6eda3533e --- /dev/null +++ b/Engine/source/core/dnet.cpp @@ -0,0 +1,288 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/stream/bitStream.h" +#include "core/dnet.h" +#include "core/strings/stringFunctions.h" + +#include "console/consoleTypes.h" + + +bool gLogToConsole = false; + +S32 gNetBitsReceived = 0; + +enum NetPacketType +{ + DataPacket, + PingPacket, + AckPacket, + InvalidPacketType, +}; + +static const char *packetTypeNames[] = +{ + "DataPacket", + "PingPacket", + "AckPacket", +}; + +//----------------------------------------------------------------- +//----------------------------------------------------------------- +//----------------------------------------------------------------- +ConsoleFunction(DNetSetLogging, void, 2, 2, "(bool enabled)" + "@brief Enables logging of the connection protocols\n\n" + "When enabled a lot of network debugging information is sent to the console.\n" + "@param enabled True to enable, false to disable\n" + "@ingroup Networking") +{ + TORQUE_UNUSED(argc); + gLogToConsole = dAtob(argv[1]); +} + +ConnectionProtocol::ConnectionProtocol() +{ + mLastSeqRecvd = 0; + mHighestAckedSeq = 0; + mLastSendSeq = 0; // start sending at 1 + mAckMask = 0; + mLastRecvAckAck = 0; +} +void ConnectionProtocol::buildSendPacketHeader(BitStream *stream, S32 packetType) +{ + S32 ackByteCount = ((mLastSeqRecvd - mLastRecvAckAck + 7) >> 3); + AssertFatal(ackByteCount <= 4, "Too few ack bytes!"); + + // S32 headerSize = 3 + ackByteCount; + + if(packetType == DataPacket) + mLastSendSeq++; + + stream->writeFlag(true); + stream->writeInt(mConnectSequence & 1, 1); + stream->writeInt(mLastSendSeq, 9); + stream->writeInt(mLastSeqRecvd, 9); + stream->writeInt(packetType, 2); + stream->writeInt(ackByteCount, 3); + stream->writeInt(mAckMask, ackByteCount * 8); + + // if we're resending this header, we can't advance the + // sequence recieved (in case this packet drops and the prev one + // goes through) + + if(gLogToConsole) + Con::printf("build hdr %d %d", mLastSendSeq, packetType); + + if(packetType == DataPacket) + mLastSeqRecvdAtSend[mLastSendSeq & 0x1F] = mLastSeqRecvd; +} + +void ConnectionProtocol::sendPingPacket() +{ + U8 buffer[16]; + BitStream bs(buffer, 16); + buildSendPacketHeader(&bs, PingPacket); + if(gLogToConsole) + Con::printf("send ping %d", mLastSendSeq); + + sendPacket(&bs); +} + +void ConnectionProtocol::sendAckPacket() +{ + U8 buffer[16]; + BitStream bs(buffer, 16); + buildSendPacketHeader(&bs, AckPacket); + if(gLogToConsole) + Con::printf("send ack %d", mLastSendSeq); + + sendPacket(&bs); +} + +// packets are read directly into the data portion of +// connection notify packets... makes the events easier to post into +// the system. + +void ConnectionProtocol::processRawPacket(BitStream *pstream) +{ + // read in the packet header: + + // Fixed packet header: 3 bytes + // + // 1 bit game packet flag + // 1 bit connect sequence + // 9 bits packet seq number + // 9 bits ackstart seq number + // 2 bits packet type + // 2 bits ack byte count + // + // type is: + // 00 data packet + // 01 ping packet + // 02 ack packet + + // next 1-4 bytes are ack flags + // + // header len is 4-9 bytes + // average case 4 byte header + + gNetBitsReceived = pstream->getStreamSize(); + + pstream->readFlag(); // get rid of the game info packet bit + U32 pkConnectSeqBit = pstream->readInt(1); + U32 pkSequenceNumber = pstream->readInt(9); + U32 pkHighestAck = pstream->readInt(9); + U32 pkPacketType = pstream->readInt(2); + S32 pkAckByteCount = pstream->readInt(3); + + // check connection sequence bit + if(pkConnectSeqBit != (mConnectSequence & 1)) + return; + + if(pkAckByteCount > 4 || pkPacketType >= InvalidPacketType) + return; + + S32 pkAckMask = pstream->readInt(8 * pkAckByteCount); + + // verify packet ordering and acking and stuff + // check if the 9-bit sequence is within the packet window + // (within 31 packets of the last received sequence number). + + pkSequenceNumber |= (mLastSeqRecvd & 0xFFFFFE00); + // account for wrap around + if(pkSequenceNumber < mLastSeqRecvd) + pkSequenceNumber += 0x200; + + if(pkSequenceNumber > mLastSeqRecvd + 31) + { + // the sequence number is outside the window... must be out of order + // discard. + return; + } + + pkHighestAck |= (mHighestAckedSeq & 0xFFFFFE00); + // account for wrap around + + if(pkHighestAck < mHighestAckedSeq) + pkHighestAck += 0x200; + + if(pkHighestAck > mLastSendSeq) + { + // the ack number is outside the window... must be an out of order + // packet, discard. + return; + } + + if(gLogToConsole) + { + for(U32 i = mLastSeqRecvd+1; i < pkSequenceNumber; i++) + Con::printf("Not recv %d", i); + Con::printf("Recv %d %s", pkSequenceNumber, packetTypeNames[pkPacketType]); + } + + // shift up the ack mask by the packet difference + // this essentially nacks all the packets dropped + + mAckMask <<= pkSequenceNumber - mLastSeqRecvd; + + // if this packet is a data packet (i.e. not a ping packet or an ack packet), ack it + if(pkPacketType == DataPacket) + mAckMask |= 1; + + // do all the notifies... + for(U32 i = mHighestAckedSeq+1; i <= pkHighestAck; i++) + { + bool packetTransmitSuccess = pkAckMask & (1 << (pkHighestAck - i)); + handleNotify(packetTransmitSuccess); + if(gLogToConsole) + Con::printf("Ack %d %d", i, packetTransmitSuccess); + + if(packetTransmitSuccess) + { + mLastRecvAckAck = mLastSeqRecvdAtSend[i & 0x1F]; + if(!mConnectionEstablished) + { + mConnectionEstablished = true; + handleConnectionEstablished(); + } + } + } + + // the other side knows more about its window than we do. + if(pkSequenceNumber - mLastRecvAckAck > 32) + mLastRecvAckAck = pkSequenceNumber - 32; + + mHighestAckedSeq = pkHighestAck; + + // first things first... + // ackback any pings or accept connects + + if(pkPacketType == PingPacket) + { + // send an ack to the other side + // the ack will have the same packet sequence as our last sent packet + // if the last packet we sent was the connection accepted packet + // we must resend that packet + sendAckPacket(); + } + keepAlive(); // notification that the connection is ok + + // note: handlePacket() may delete the connection if an error occurs. + if(mLastSeqRecvd != pkSequenceNumber) + { + mLastSeqRecvd = pkSequenceNumber; + if(pkPacketType == DataPacket) + handlePacket(pstream); + } +} + +bool ConnectionProtocol::windowFull() +{ + return mLastSendSeq - mHighestAckedSeq >= 30; +} + +void ConnectionProtocol::writeDemoStartBlock(ResizeBitStream *stream) +{ + for(U32 i = 0; i < 32; i++) + stream->write(mLastSeqRecvdAtSend[i]); + stream->write(mLastSeqRecvd); + stream->write(mHighestAckedSeq); + stream->write(mLastSendSeq); + stream->write(mAckMask); + stream->write(mConnectSequence); + stream->write(mLastRecvAckAck); + stream->write(mConnectionEstablished); +} + +bool ConnectionProtocol::readDemoStartBlock(BitStream *stream) +{ + for(U32 i = 0; i < 32; i++) + stream->read(&mLastSeqRecvdAtSend[i]); + stream->read(&mLastSeqRecvd); + stream->read(&mHighestAckedSeq); + stream->read(&mLastSendSeq); + stream->read(&mAckMask); + stream->read(&mConnectSequence); + stream->read(&mLastRecvAckAck); + stream->read(&mConnectionEstablished); + return true; +} diff --git a/Engine/source/core/dnet.h b/Engine/source/core/dnet.h new file mode 100644 index 000000000..5f35c100d --- /dev/null +++ b/Engine/source/core/dnet.h @@ -0,0 +1,83 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _DNET_H_ +#define _DNET_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#ifndef _EVENT_H_ +#include "platform/event.h" +#endif + +#include "platform/platformNet.h" + +class BitStream; +class ResizeBitStream; + +/// The base class for Torque's networking protocol. +/// +/// This implements a sliding window connected message stream over an unreliable transport (UDP). It +/// provides a simple notify protocol to allow subclasses to be aware of what packets were sent +/// succesfully and which failed. +/// +/// Basically, a window size of 32 is provided, and each packet contains in the header a bitmask, +/// acknowledging the receipt (or failure to receive) of the last 32 packets. +/// +/// @see NetConnection, @ref NetProtocol +class ConnectionProtocol +{ +protected: + U32 mLastSeqRecvdAtSend[32]; + U32 mLastSeqRecvd; + U32 mHighestAckedSeq; + U32 mLastSendSeq; + U32 mAckMask; + U32 mConnectSequence; + U32 mLastRecvAckAck; + bool mConnectionEstablished; +public: + ConnectionProtocol(); + + void buildSendPacketHeader(BitStream *bstream, S32 packetType = 0); + + void sendPingPacket(); + void sendAckPacket(); + void setConnectionEstablished() { mConnectionEstablished = true; } + + bool windowFull(); + bool connectionEstablished(); + void setConnectSequence(U32 connectSeq) { mConnectSequence = connectSeq; } + + virtual void writeDemoStartBlock(ResizeBitStream *stream); + virtual bool readDemoStartBlock(BitStream *stream); + + virtual void processRawPacket(BitStream *bstream); + virtual Net::Error sendPacket(BitStream *bstream) = 0; + virtual void keepAlive() = 0; + virtual void handleConnectionEstablished() = 0; + virtual void handleNotify(bool recvd) = 0; + virtual void handlePacket(BitStream *bstream) = 0; +}; + +#endif diff --git a/Engine/source/core/fileObject.cpp b/Engine/source/core/fileObject.cpp new file mode 100644 index 000000000..4234d583d --- /dev/null +++ b/Engine/source/core/fileObject.cpp @@ -0,0 +1,502 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/fileObject.h" +#include "console/engineAPI.h" + +IMPLEMENT_CONOBJECT(FileObject); + +ConsoleDocClass( FileObject, + "@brief This class is responsible opening, reading, creating, and saving file contents.\n\n" + + "FileObject acts as the interface with OS level files. You create a new FileObject and pass into it " + "a file's path and name. The FileObject class supports three distinct operations for working with files:\n\n" + + "" + "" + "" + "" + "" + "
      Operation%FileObject MethodDescription
      ReadopenForRead()Open the file for reading
      WriteopenForWrite()Open the file for writing to and replace its contents (if any)
      AppendopenForAppend()Open the file and start writing at its end
      \n\n" + + "Before you may work with a file you need to use one of the three above methods on the FileObject.\n\n" + + "@tsexample\n" + "// Create a file object for writing\n" + "%fileWrite = new FileObject();\n\n" + "// Open a file to write to, if it does not exist it will be created\n" + "%result = %fileWrite.OpenForWrite(\"./test.txt\");\n\n" + "if ( %result )\n" + "{\n" + " // Write a line to the text files\n" + " %fileWrite.writeLine(\"READ. READ CODE. CODE\");\n" + "}\n\n" + "// Close the file when finished\n" + "%fileWrite.close();\n\n" + "// Cleanup the file object\n" + "%fileWrite.delete();\n\n\n" + + "// Create a file object for reading\n" + "%fileRead = new FileObject();\n\n" + "// Open a text file, if it exists\n" + "%result = %fileRead.OpenForRead(\"./test.txt\");\n\n" + "if ( %result )\n" + "{\n" + " // Read in the first line\n" + " %line = %fileRead.readline();\n\n" + " // Print the line we just read\n" + " echo(%line);\n" + "}\n\n" + "// Close the file when finished\n" + "%fileRead.close();\n\n" + "// Cleanup the file object\n" + "%fileRead.delete();\n" + "@endtsexample\n\n" + + "@ingroup FileSystem\n" +); + +bool FileObject::isEOF() +{ + return mCurPos == mBufferSize; +} + +FileObject::FileObject() +{ + mFileBuffer = NULL; + mBufferSize = 0; + mCurPos = 0; + stream = NULL; +} + +FileObject::~FileObject() +{ + SAFE_DELETE_ARRAY(mFileBuffer); + SAFE_DELETE(stream); +} + +void FileObject::close() +{ + SAFE_DELETE(stream); + SAFE_DELETE_ARRAY(mFileBuffer); + mFileBuffer = NULL; + mBufferSize = mCurPos = 0; +} + +bool FileObject::openForWrite(const char *fileName, const bool append) +{ + char buffer[1024]; + Con::expandScriptFilename( buffer, sizeof( buffer ), fileName ); + + close(); + + if( !buffer[ 0 ] ) + return false; + + if((stream = FileStream::createAndOpen( fileName, append ? Torque::FS::File::WriteAppend : Torque::FS::File::Write )) == NULL) + return false; + + stream->setPosition( stream->getStreamSize() ); + return( true ); +} + +bool FileObject::openForRead(const char* /*fileName*/) +{ + AssertFatal(false, "Error, not yet implemented!"); + return false; +} + +bool FileObject::readMemory(const char *fileName) +{ + char buffer[1024]; + Con::expandScriptFilename( buffer, sizeof( buffer ), fileName ); + + close(); + + void *data = NULL; + U32 dataSize = 0; + Torque::FS::ReadFile(buffer, data, dataSize, true); + if(data == NULL) + return false; + + mBufferSize = dataSize; + mFileBuffer = (U8 *)data; + mCurPos = 0; + + return true; +} + +const U8 *FileObject::readLine() +{ + if(!mFileBuffer) + return (U8 *) ""; + + U32 tokPos = mCurPos; + + for(;;) + { + if(mCurPos == mBufferSize) + break; + + if(mFileBuffer[mCurPos] == '\r') + { + mFileBuffer[mCurPos++] = 0; + if(mFileBuffer[mCurPos] == '\n') + mCurPos++; + break; + } + + if(mFileBuffer[mCurPos] == '\n') + { + mFileBuffer[mCurPos++] = 0; + break; + } + + mCurPos++; + } + + return mFileBuffer + tokPos; +} + +void FileObject::peekLine( U8* line, S32 length ) +{ + if(!mFileBuffer) + { + line[0] = '\0'; + return; + } + + // Copy the line into the buffer. We can't do this like readLine because + // we can't modify the file buffer. + S32 i = 0; + U32 tokPos = mCurPos; + while( ( tokPos != mBufferSize ) && ( mFileBuffer[tokPos] != '\r' ) && ( mFileBuffer[tokPos] != '\n' ) && ( i < ( length - 1 ) ) ) + line[i++] = mFileBuffer[tokPos++]; + + line[i++] = '\0'; + + //if( i == length ) + //Con::warnf( "FileObject::peekLine - The line contents could not fit in the buffer (size %d). Truncating.", length ); +} + +void FileObject::writeLine(const U8 *line) +{ + stream->write(dStrlen((const char *) line), line); + stream->write(2, "\r\n"); +} + +void FileObject::writeObject( SimObject* object, const U8* objectPrepend ) +{ + if( objectPrepend == NULL ) + stream->write(2, "\r\n"); + else + stream->write(dStrlen((const char *) objectPrepend), objectPrepend ); + object->write( *stream, 0 ); +} + +DefineEngineMethod( FileObject, openForRead, bool, ( const char* filename ),, + "@brief Open a specified file for reading\n\n" + + "There is no limit as to what kind of file you can read. Any format and data contained within is accessible, not just text\n\n" + + "@param filename Path, name, and extension of file to be read" + + "@tsexample\n" + "// Create a file object for reading\n" + "%fileRead = new FileObject();\n\n" + "// Open a text file, if it exists\n" + "%result = %fileRead.OpenForRead(\"./test.txt\");\n" + "@endtsexample\n\n" + + "@return True if file was successfully opened, false otherwise\n") +{ + return object->readMemory(filename); +} + +DefineEngineMethod( FileObject, openForWrite, bool, ( const char* filename ),, + "@brief Open a specified file for writing\n\n" + + "There is no limit as to what kind of file you can write. Any format and data is allowable, not just text\n\n" + + "@param filename Path, name, and extension of file to write to" + + "@tsexample\n" + "// Create a file object for writing\n" + "%fileWrite = new FileObject();\n\n" + "// Open a file to write to, if it does not exist it will be created\n" + "%result = %fileWrite.OpenForWrite(\"./test.txt\");\n" + "@endtsexample\n\n" + + "@return True if file was successfully opened, false otherwise\n") +{ + return object->openForWrite(filename); +} + +DefineEngineMethod( FileObject, openForAppend, bool, ( const char* filename ),, + "@brief Open a specified file for writing, adding data to the end of the file\n\n" + + "There is no limit as to what kind of file you can write. Any format and data is allowable, not just text. Unlike openForWrite(), " + "which will erase an existing file if it is opened, openForAppend() preserves data in an existing file and adds to it.\n\n" + + "@param filename Path, name, and extension of file to append to" + + "@tsexample\n" + "// Create a file object for writing\n" + "%fileWrite = new FileObject();\n\n" + "// Open a file to write to, if it does not exist it will be created\n" + "// If it does exist, whatever we write will be added to the end\n" + "%result = %fileWrite.OpenForAppend(\"./test.txt\");\n" + "@endtsexample\n\n" + + "@return True if file was successfully opened, false otherwise\n") +{ + return object->openForWrite(filename, true); +} + +DefineEngineMethod( FileObject, isEOF, bool, (),, + "@brief Determines if the parser for this FileObject has reached the end of the file\n\n" + + "@tsexample\n" + "// Create a file object for reading\n" + "%fileRead = new FileObject();\n\n" + "// Open a text file, if it exists\n" + "%fileRead.OpenForRead(\"./test.txt\");\n\n" + "// Keep reading until we reach the end of the file\n" + "while( !%fileRead.isEOF() )\n" + "{\n" + " %line = %fileRead.readline();\n" + " echo(%line);\n" + "}\n\n" + "// Made it to the end\n" + "echo(\"Finished reading file\");\n" + "@endtsexample\n\n" + + "@return True if the parser has reached the end of the file, false otherwise\n") +{ + return object->isEOF(); +} + +DefineEngineMethod( FileObject, readLine, const char*, (),, + "@brief Read a line from file.\n\n" + + "Emphasis on *line*, as in you cannot parse individual characters or chunks of data. " + "There is no limitation as to what kind of data you can read.\n\n" + + "@tsexample\n" + "// Create a file object for reading\n" + "%fileRead = new FileObject();\n\n" + "// Open a text file, if it exists\n" + "%fileRead.OpenForRead(\"./test.txt\");\n\n" + "// Read in the first line\n" + "%line = %fileRead.readline();\n\n" + "// Print the line we just read\n" + "echo(%line);\n" + "@endtsexample\n\n" + + "@return String containing the line of data that was just read\n") +{ + return (const char *) object->readLine(); +} + +DefineEngineMethod( FileObject, peekLine, const char*, (),, + "@brief Read a line from the file without moving the stream position.\n\n" + + "Emphasis on *line*, as in you cannot parse individual characters or chunks of data. " + "There is no limitation as to what kind of data you can read. Unlike readLine, the parser does not move forward after reading.\n\n" + + "@param filename Path, name, and extension of file to be read" + + "@tsexample\n" + "// Create a file object for reading\n" + "%fileRead = new FileObject();\n\n" + "// Open a text file, if it exists\n" + "%fileRead.OpenForRead(\"./test.txt\");\n\n" + "// Peek the first line\n" + "%line = %fileRead.peekLine();\n\n" + "// Print the line we just peeked\n" + "echo(%line);\n" + "// If we peek again...\n" + "%line = %fileRead.peekLine();\n\n" + "// We will get the same output as the first time\n" + "// since the stream did not move forward\n" + "echo(%line);\n" + "@endtsexample\n\n" + + "@return String containing the line of data that was just peeked\n") +{ + char *line = Con::getReturnBuffer( 512 ); + object->peekLine( (U8*)line, 512 ); + return line; +} + +DefineEngineMethod( FileObject, writeLine, void, ( const char* text ),, + "@brief Write a line to the file, if it was opened for writing.\n\n" + + "There is no limit as to what kind of text you can write. Any format and data is allowable, not just text. " + "Be careful of what you write, as whitespace, current values, and literals will be preserved.\n\n" + + "@param text The data we are writing out to file." + + "@tsexample\n" + "// Create a file object for writing\n" + "%fileWrite = new FileObject();\n\n" + "// Open a file to write to, if it does not exist it will be created\n" + "%fileWrite.OpenForWrite(\"./test.txt\");\n\n" + "// Write a line to the text files\n" + "%fileWrite.writeLine(\"READ. READ CODE. CODE\");\n\n" + "@endtsexample\n\n" + + "@return True if file was successfully opened, false otherwise\n") +{ + object->writeLine((const U8 *) text); +} + +DefineEngineMethod( FileObject, close, void, (),, + "@brief Close the file.\n\n" + + "It is EXTREMELY important that you call this function when you are finished reading or writing to a file. " + "Failing to do so is not only a bad programming practice, but could result in bad data or corrupt files. " + "Remember: Open, Read/Write, Close, Delete...in that order!\n\n" + + "@tsexample\n" + "// Create a file object for reading\n" + "%fileRead = new FileObject();\n\n" + "// Open a text file, if it exists\n" + "%fileRead.OpenForRead(\"./test.txt\");\n\n" + "// Peek the first line\n" + "%line = %fileRead.peekLine();\n\n" + "// Print the line we just peeked\n" + "echo(%line);\n" + "// If we peek again...\n" + "%line = %fileRead.peekLine();\n\n" + "// We will get the same output as the first time\n" + "// since the stream did not move forward\n" + "echo(%line);\n\n" + "// Close the file when finished\n" + "%fileWrite.close();\n\n" + "// Cleanup the file object\n" + "%fileWrite.delete();\n" + "@endtsexample\n\n") +{ + object->close(); +} + +static ConsoleDocFragment _FileObjectwriteObject1( + "@brief Write an object to a text file.\n\n" + + "Unlike a simple writeLine using specified strings, this function writes an entire object " + "to file, preserving its type, name, and properties. This is similar to the save() functionality of " + "the SimObject class, but with a bit more control.\n\n" + + "@param object The SimObject being written to file, properties, name, and all.\n" + + "@tsexample\n" + "// Let's assume this SpawnSphere was created and currently\n" + "// exists in the running level\n" + "new SpawnSphere(TestSphere)\n" + "{\n" + " spawnClass = \"Player\";\n" + " spawnDatablock = \"DefaultPlayerData\";\n" + " autoSpawn = \"1\";\n" + " radius = \"5\";\n" + " sphereWeight = \"1\";\n" + " indoorWeight = \"1\";\n" + " outdoorWeight = \"1\";\n" + " dataBlock = \"SpawnSphereMarker\";\n" + " position = \"-42.222 1.4845 4.80334\";\n" + " rotation = \"0 0 -1 108\";\n" + " scale = \"1 1 1\";\n" + " canSaveDynamicFields = \"1\";\n" + "};\n\n" + "// Create a file object for writing\n" + "%fileWrite = new FileObject();\n\n" + "// Open a file to write to, if it does not exist it will be created\n" + "%fileWrite.OpenForWrite(\"./spawnSphers.txt\");\n\n" + "// Write out the TestSphere\n" + "%fileWrite.writeObject(TestSphere);\n\n" + "// Close the text file\n" + "%fileWrite.close();\n\n" + "// Cleanup\n" + "%fileWrite.delete();\n" + "@endtsexample\n\n\n", + "FileObject", + "void writeObject( SimObject* object);"); + +static ConsoleDocFragment _FileObjectwriteObject2( + "@brief Write an object to a text file, with some data added first.\n\n" + + "Unlike a simple writeLine using specified strings, this function writes an entire object " + "to file, preserving its type, name, and properties. This is similar to the save() functionality of " + "the SimObject class, but with a bit more control.\n\n" + + "@param object The SimObject being written to file, properties, name, and all.\n" + "@param prepend Data or text that is written out before the SimObject.\n\n" + + "@tsexample\n" + "// Let's assume this SpawnSphere was created and currently\n" + "// exists in the running level\n" + "new SpawnSphere(TestSphere)\n" + "{\n" + " spawnClass = \"Player\";\n" + " spawnDatablock = \"DefaultPlayerData\";\n" + " autoSpawn = \"1\";\n" + " radius = \"5\";\n" + " sphereWeight = \"1\";\n" + " indoorWeight = \"1\";\n" + " outdoorWeight = \"1\";\n" + " dataBlock = \"SpawnSphereMarker\";\n" + " position = \"-42.222 1.4845 4.80334\";\n" + " rotation = \"0 0 -1 108\";\n" + " scale = \"1 1 1\";\n" + " canSaveDynamicFields = \"1\";\n" + "};\n\n" + "// Create a file object for writing\n" + "%fileWrite = new FileObject();\n\n" + "// Open a file to write to, if it does not exist it will be created\n" + "%fileWrite.OpenForWrite(\"./spawnSphers.txt\");\n\n" + "// Write out the TestSphere, with a prefix\n" + "%fileWrite.writeObject(TestSphere, \"$mySphere = \");\n\n" + "// Close the text file\n" + "%fileWrite.close();\n\n" + "// Cleanup\n" + "%fileWrite.delete();\n" + "@endtsexample\n\n\n", + "FileObject", + "void writeObject( SimObject* object, string prepend);"); + +ConsoleMethod( FileObject, writeObject, void, 3, 4, "FileObject.writeObject(SimObject, object prepend)" + "@hide") +{ + SimObject* obj = Sim::findObject( argv[2] ); + if( !obj ) + { + Con::printf("FileObject::writeObject - Invalid Object!"); + return; + } + + char *objName = NULL; + if( argc == 4 ) + objName = (char*)argv[3]; + + object->writeObject( obj, (const U8*)objName ); +} + diff --git a/Engine/source/core/fileObject.h b/Engine/source/core/fileObject.h new file mode 100644 index 000000000..ca2952a4f --- /dev/null +++ b/Engine/source/core/fileObject.h @@ -0,0 +1,58 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FILEOBJECT_H_ +#define _FILEOBJECT_H_ + +#ifndef _SIMBASE_H_ +#include "console/simBase.h" +#endif +#ifndef _FILESTREAM_H_ +#include "core/stream/fileStream.h" +#endif + +class FileObject : public SimObject +{ + typedef SimObject Parent; + U8 *mFileBuffer; + U32 mBufferSize; + U32 mCurPos; + FileStream *stream; +public: + FileObject(); + ~FileObject(); + + bool openForWrite(const char *fileName, const bool append = false); + bool openForRead(const char *fileName); + bool readMemory(const char *fileName); + const U8 *buffer() { return mFileBuffer; } + const U8 *readLine(); + void peekLine(U8 *line, S32 length); + bool isEOF(); + void writeLine(const U8 *line); + void close(); + void writeObject( SimObject* object, const U8* objectPrepend = NULL ); + + DECLARE_CONOBJECT(FileObject); +}; + +#endif diff --git a/Engine/source/core/fileio.h b/Engine/source/core/fileio.h new file mode 100644 index 000000000..31b367033 --- /dev/null +++ b/Engine/source/core/fileio.h @@ -0,0 +1,140 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FILEIO_H_ +#define _FILEIO_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif + +class File +{ +public: + /// What is the status of our file handle? + enum Status + { + Ok = 0, ///< Ok! + IOError, ///< Read or Write error + EOS, ///< End of Stream reached (mostly for reads) + IllegalCall, ///< An unsupported operation used. Always accompanied by AssertWarn + Closed, ///< Tried to operate on a closed stream (or detached filter) + UnknownError ///< Catchall + }; + + /// How are we accessing the file? + enum AccessMode + { + Read = 0, ///< Open for read only, starting at beginning of file. + Write = 1, ///< Open for write only, starting at beginning of file; will blast old contents of file. + ReadWrite = 2, ///< Open for read-write. + WriteAppend = 3 ///< Write-only, starting at end of file. + }; + + /// Flags used to indicate what we can do to the file. + enum Capability + { + FileRead = BIT(0), + FileWrite = BIT(1) + }; + +private: + void *handle; ///< Pointer to the file handle. + Status currentStatus; ///< Current status of the file (Ok, IOError, etc.). + U32 capability; ///< Keeps track of file capabilities. + + File(const File&); ///< This is here to disable the copy constructor. + File& operator=(const File&); ///< This is here to disable assignment. + +public: + File(); ///< Default constructor + virtual ~File(); ///< Destructor + + /// Opens a file for access using the specified AccessMode + /// + /// @returns The status of the file + Status open(const char *filename, const AccessMode openMode); + + /// Gets the current position in the file + /// + /// This is in bytes from the beginning of the file. + U32 getPosition() const; + + /// Sets the current position in the file. + /// + /// You can set either a relative or absolute position to go to in the file. + /// + /// @code + /// File *foo; + /// + /// ... set up file ... + /// + /// // Go to byte 32 in the file... + /// foo->setPosition(32); + /// + /// // Now skip back 20 bytes... + /// foo->setPosition(-20, false); + /// + /// // And forward 17... + /// foo->setPosition(17, false); + /// @endcode + /// + /// @returns The status of the file + Status setPosition(S32 position, bool absolutePos = true); + + /// Returns the size of the file + U32 getSize() const; + + /// Make sure everything that's supposed to be written to the file gets written. + /// + /// @returns The status of the file. + Status flush(); + + /// Closes the file + /// + /// @returns The status of the file. + Status close(); + + /// Gets the status of the file + Status getStatus() const; + + /// Reads "size" bytes from the file, and dumps data into "dst". + /// The number of actual bytes read is returned in bytesRead + /// @returns The status of the file + Status read(U32 size, char *dst, U32 *bytesRead = NULL); + + /// Writes "size" bytes into the file from the pointer "src". + /// The number of actual bytes written is returned in bytesWritten + /// @returns The status of the file + Status write(U32 size, const char *src, U32 *bytesWritten = NULL); + + /// Returns whether or not this file is capable of the given function. + bool hasCapability(Capability cap) const; + + const void* getHandle() { return handle; } + +protected: + Status setStatus(); ///< Called after error encountered. + Status setStatus(Status status); ///< Setter for the current status. +}; + +#endif // _FILE_IO_H_ diff --git a/Engine/source/core/filterStream.cpp b/Engine/source/core/filterStream.cpp new file mode 100644 index 000000000..bbabb1baa --- /dev/null +++ b/Engine/source/core/filterStream.cpp @@ -0,0 +1,78 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/filterStream.h" + +FilterStream::~FilterStream() +{ + // +} + +bool FilterStream::_read(const U32 in_numBytes, void* out_pBuffer) +{ + AssertFatal(getStream() != NULL, "Error no stream to pass to"); + + bool success = getStream()->read(in_numBytes, out_pBuffer); + + setStatus(getStream()->getStatus()); + return success; +} + + +bool FilterStream::_write(const U32, const void*) +{ + AssertFatal(false, "No writing allowed to filter"); + return false; +} + +bool FilterStream::hasCapability(const Capability in_streamCap) const +{ + // Fool the compiler. We know better... + FilterStream* ncThis = const_cast(this); + AssertFatal(ncThis->getStream() != NULL, "Error no stream to pass to"); + + return ncThis->getStream()->hasCapability(in_streamCap); +} + +U32 FilterStream::getPosition() const +{ + // Fool the compiler. We know better... + FilterStream* ncThis = const_cast(this); + AssertFatal(ncThis->getStream() != NULL, "Error no stream to pass to"); + + return ncThis->getStream()->getPosition(); +} + +bool FilterStream::setPosition(const U32 in_newPosition) +{ + AssertFatal(getStream() != NULL, "Error no stream to pass to"); + + return getStream()->setPosition(in_newPosition); +} + +U32 FilterStream::getStreamSize() +{ + AssertFatal(getStream() != NULL, "Error no stream to pass to"); + + return getStream()->getStreamSize(); +} + diff --git a/Engine/source/core/filterStream.h b/Engine/source/core/filterStream.h new file mode 100644 index 000000000..adb024e16 --- /dev/null +++ b/Engine/source/core/filterStream.h @@ -0,0 +1,56 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FILTERSTREAM_H_ +#define _FILTERSTREAM_H_ + +//Includes +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#ifndef _STREAM_H_ +#include "core/stream/stream.h" +#endif + +class FilterStream : public Stream +{ + public: + virtual ~FilterStream(); + + virtual bool attachStream(Stream* io_pSlaveStream) = 0; + virtual void detachStream() = 0; + virtual Stream* getStream() = 0; + + // Mandatory overrides. By default, these are simply passed to + // whatever is returned from getStream(); + protected: + bool _read(const U32 in_numBytes, void* out_pBuffer); + bool _write(const U32 in_numBytes, const void* in_pBuffer); + public: + bool hasCapability(const Capability) const; + + U32 getPosition() const; + bool setPosition(const U32 in_newPosition); + U32 getStreamSize(); +}; + +#endif //_FILTERSTREAM_H_ diff --git a/Engine/source/core/frameAllocator.cpp b/Engine/source/core/frameAllocator.cpp new file mode 100644 index 000000000..846328290 --- /dev/null +++ b/Engine/source/core/frameAllocator.cpp @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/frameAllocator.h" +#include "console/console.h" + +U8* FrameAllocator::smBuffer = NULL; +U32 FrameAllocator::smWaterMark = 0; +U32 FrameAllocator::smHighWaterMark = 0; + +#ifdef TORQUE_DEBUG +U32 FrameAllocator::smMaxFrameAllocation = 0; + +ConsoleFunction(getMaxFrameAllocation, S32, 1,1, "getMaxFrameAllocation();") +{ + return FrameAllocator::getMaxFrameAllocation(); +} +#endif diff --git a/Engine/source/core/frameAllocator.h b/Engine/source/core/frameAllocator.h new file mode 100644 index 000000000..6abdd13cf --- /dev/null +++ b/Engine/source/core/frameAllocator.h @@ -0,0 +1,320 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FRAMEALLOCATOR_H_ +#define _FRAMEALLOCATOR_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif + +/// This #define is used by the FrameAllocator to align starting addresses to +/// be byte aligned to this value. This is important on the 360 and possibly +/// on other platforms as well. Use this #define anywhere alignment is needed. +/// +/// NOTE: Do not change this value per-platform unless you have a very good +/// reason for doing so. It has the potential to cause inconsistencies in +/// memory which is allocated and expected to be contiguous. +#define FRAMEALLOCATOR_BYTE_ALIGNMENT 4 + +/// Temporary memory pool for per-frame allocations. +/// +/// In the course of rendering a frame, it is often necessary to allocate +/// many small chunks of memory, then free them all in a batch. For instance, +/// say we're allocating storage for some vertex calculations: +/// +/// @code +/// // Get FrameAllocator memory... +/// U32 waterMark = FrameAllocator::getWaterMark(); +/// F32 * ptr = (F32*)FrameAllocator::alloc(sizeof(F32)*2*targetMesh->vertsPerFrame); +/// +/// ... calculations ... +/// +/// // Free frameAllocator memory +/// FrameAllocator::setWaterMark(waterMark); +/// @endcode +class FrameAllocator +{ + static U8* smBuffer; + static U32 smHighWaterMark; + static U32 smWaterMark; + +#ifdef TORQUE_DEBUG + static U32 smMaxFrameAllocation; +#endif + + public: + inline static void init(const U32 frameSize); + inline static void destroy(); + + inline static void* alloc(const U32 allocSize); + + inline static void setWaterMark(const U32); + inline static U32 getWaterMark(); + inline static U32 getHighWaterMark(); + +#ifdef TORQUE_DEBUG + static U32 getMaxFrameAllocation() { return smMaxFrameAllocation; } +#endif +}; + +void FrameAllocator::init(const U32 frameSize) +{ +#ifdef FRAMEALLOCATOR_DEBUG_GUARD + AssertISV( false, "FRAMEALLOCATOR_DEBUG_GUARD has been removed because it allows non-contiguous memory allocation by the FrameAllocator, and this is *not* ok." ); +#endif + + AssertFatal(smBuffer == NULL, "Error, already initialized"); + smBuffer = new U8[frameSize]; + smWaterMark = 0; + smHighWaterMark = frameSize; +} + +void FrameAllocator::destroy() +{ + AssertFatal(smBuffer != NULL, "Error, not initialized"); + + delete [] smBuffer; + smBuffer = NULL; + smWaterMark = 0; + smHighWaterMark = 0; +} + + +void* FrameAllocator::alloc(const U32 allocSize) +{ + U32 _allocSize = allocSize; + + AssertFatal(smBuffer != NULL, "Error, no buffer!"); + AssertFatal(smWaterMark + _allocSize <= smHighWaterMark, "Error alloc too large, increase frame size!"); + smWaterMark = ( smWaterMark + ( FRAMEALLOCATOR_BYTE_ALIGNMENT - 1 ) ) & (~( FRAMEALLOCATOR_BYTE_ALIGNMENT - 1 )); + + // Sanity check. + AssertFatal( !( smWaterMark & ( FRAMEALLOCATOR_BYTE_ALIGNMENT - 1 ) ), "Frame allocation is not on a specified byte boundry." ); + + U8* p = &smBuffer[smWaterMark]; + smWaterMark += _allocSize; + +#ifdef TORQUE_DEBUG + if (smWaterMark > smMaxFrameAllocation) + smMaxFrameAllocation = smWaterMark; +#endif + + return p; +} + + +void FrameAllocator::setWaterMark(const U32 waterMark) +{ + AssertFatal(waterMark < smHighWaterMark, "Error, invalid waterMark"); + smWaterMark = waterMark; +} + +U32 FrameAllocator::getWaterMark() +{ + return smWaterMark; +} + +U32 FrameAllocator::getHighWaterMark() +{ + return smHighWaterMark; +} + +/// Helper class to deal with FrameAllocator usage. +/// +/// The purpose of this class is to make it simpler and more reliable to use the +/// FrameAllocator. Simply use it like this: +/// +/// @code +/// FrameAllocatorMarker mem; +/// +/// char *buff = (char*)mem.alloc(100); +/// @endcode +/// +/// When you leave the scope you defined the FrameAllocatorMarker in, it will +/// automatically restore the watermark on the FrameAllocator. In situations +/// with complex branches, this can be a significant headache remover, as you +/// don't have to remember to reset the FrameAllocator on every posssible branch. +class FrameAllocatorMarker +{ + U32 mMarker; + +public: + FrameAllocatorMarker() + { + mMarker = FrameAllocator::getWaterMark(); + } + + ~FrameAllocatorMarker() + { + FrameAllocator::setWaterMark(mMarker); + } + + void* alloc(const U32 allocSize) const + { + return FrameAllocator::alloc(allocSize); + } + + template + T* alloc(const U32 numElements) const + { + return reinterpret_cast(FrameAllocator::alloc(numElements * sizeof(T))); + } +}; + +/// Class for temporary variables that you want to allocate easily using +/// the FrameAllocator. For example: +/// @code +/// FrameTemp tempStr(32); // NOTE! This parameter is NOT THE SIZE IN BYTES. See constructor docs. +/// dStrcat( tempStr, SomeOtherString ); +/// tempStr[2] = 'l'; +/// Con::printf( tempStr ); +/// Con::printf( "Foo: %s", ~tempStr ); +/// @endcode +/// +/// This will automatically handle getting and restoring the watermark of the +/// FrameAllocator when it goes out of scope. You should notice the strange +/// operator in front of tempStr on the printf call. This is normally a unary +/// operator for ones-complement, but in this class it will simply return the +/// memory of the allocation. It's the same as doing (const char *)tempStr +/// in the above case. The reason why it is necessary for the second printf +/// and not the first is because the second one is taking a variable arg +/// list and so it isn't getting the cast so that it's cast operator can +/// properly return the memory instead of the FrameTemp object itself. +/// +/// @note It is important to note that this object is designed to just be a +/// temporary array of a dynamic size. Some wierdness may occur if you try +/// to perform crazy pointer stuff with it using regular operators on it. +template +class FrameTemp +{ +protected: + U32 mWaterMark; + T *mMemory; + U32 mNumObjectsInMemory; + +public: + /// Constructor will store the FrameAllocator watermark and allocate the memory off + /// of the FrameAllocator. + /// + /// @note It is important to note that, unlike the FrameAllocatorMarker and the + /// FrameAllocator itself, the argument to allocate is NOT the size in bytes, + /// doing: + /// @code + /// FrameTemp f64s(5); + /// @endcode + /// Is the same as + /// @code + /// F64 *f64s = new F64[5]; + /// @endcode + /// + /// @param count The number of objects to allocate + FrameTemp( const U32 count = 1 ) : mNumObjectsInMemory( count ) + { + AssertFatal( count > 0, "Allocating a FrameTemp with less than one instance" ); + mWaterMark = FrameAllocator::getWaterMark(); + mMemory = reinterpret_cast( FrameAllocator::alloc( sizeof( T ) * count ) ); + + for( int i = 0; i < mNumObjectsInMemory; i++ ) + constructInPlace( &mMemory[i] ); + } + + /// Destructor restores the watermark + ~FrameTemp() + { + // Call destructor + for( int i = 0; i < mNumObjectsInMemory; i++ ) + destructInPlace( &mMemory[i] ); + + FrameAllocator::setWaterMark( mWaterMark ); + } + + /// NOTE: This will return the memory, NOT perform a ones-complement + T* operator ~() { return mMemory; }; + /// NOTE: This will return the memory, NOT perform a ones-complement + const T* operator ~() const { return mMemory; }; + + /// NOTE: This will dereference the memory, NOT do standard unary plus behavior + T& operator +() { return *mMemory; }; + /// NOTE: This will dereference the memory, NOT do standard unary plus behavior + const T& operator +() const { return *mMemory; }; + + T& operator *() { return *mMemory; }; + const T& operator *() const { return *mMemory; }; + + T** operator &() { return &mMemory; }; + const T** operator &() const { return &mMemory; }; + + operator T*() { return mMemory; } + operator const T*() const { return mMemory; } + + operator T&() { return *mMemory; } + operator const T&() const { return *mMemory; } + + operator T() { return *mMemory; } + operator const T() const { return *mMemory; } + + T& operator []( U32 i ) { return mMemory[ i ]; } + const T& operator []( U32 i ) const { return mMemory[ i ]; } + + T& operator []( S32 i ) { return mMemory[ i ]; } + const T& operator []( S32 i ) const { return mMemory[ i ]; } + + /// @name Vector-like Interface + /// @{ + T *address() const { return mMemory; } + dsize_t size() const { return mNumObjectsInMemory; } + /// @} +}; + +//----------------------------------------------------------------------------- +// FrameTemp specializations for types with no constructor/destructor +#define FRAME_TEMP_NC_SPEC(type) \ + template<> \ + inline FrameTemp::FrameTemp( const U32 count ) \ + { \ + AssertFatal( count > 0, "Allocating a FrameTemp with less than one instance" ); \ + mWaterMark = FrameAllocator::getWaterMark(); \ + mMemory = reinterpret_cast( FrameAllocator::alloc( sizeof( type ) * count ) ); \ + } \ + template<>\ + inline FrameTemp::~FrameTemp() \ + { \ + FrameAllocator::setWaterMark( mWaterMark ); \ + } \ + +FRAME_TEMP_NC_SPEC(char); +FRAME_TEMP_NC_SPEC(float); +FRAME_TEMP_NC_SPEC(double); +FRAME_TEMP_NC_SPEC(bool); +FRAME_TEMP_NC_SPEC(int); +FRAME_TEMP_NC_SPEC(short); + +FRAME_TEMP_NC_SPEC(unsigned char); +FRAME_TEMP_NC_SPEC(unsigned int); +FRAME_TEMP_NC_SPEC(unsigned short); + +#undef FRAME_TEMP_NC_SPEC + +//----------------------------------------------------------------------------- + +#endif // _H_FRAMEALLOCATOR_ diff --git a/Engine/source/core/iTickable.cpp b/Engine/source/core/iTickable.cpp new file mode 100644 index 000000000..532a67a42 --- /dev/null +++ b/Engine/source/core/iTickable.cpp @@ -0,0 +1,111 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/iTickable.h" + +// The statics +U32 ITickable::smLastTick = 0; +U32 ITickable::smLastTime = 0; +U32 ITickable::smLastDelta = 0; + +U32 ITickable::smTickShift = 5; +U32 ITickable::smTickMs = ( 1 << smTickShift ); +F32 ITickable::smTickSec = ( F32( ITickable::smTickMs ) / 1000.f ); +U32 ITickable::smTickMask = ( smTickMs - 1 ); + +//------------------------------------------------------------------------------ + +ITickable::ITickable() : mProcessTick( true ) +{ + getProcessList().push_back( this ); +} + +//------------------------------------------------------------------------------ + +ITickable::~ITickable() +{ + for( ProcessListIterator i = getProcessList().begin(); i != getProcessList().end(); i++ ) + { + if( (*i) == this ) + { + getProcessList().erase( i ); + return; + } + } +} + +//------------------------------------------------------------------------------ + +void ITickable::init( const U32 tickShift ) +{ + // Sanity! + AssertFatal( tickShift == 0 || tickShift <= 31, "ITickable::init() - Invalid 'tickShift' parameter!" ); + + // Calculate tick constants. + smTickShift = tickShift; + smTickMs = ( 1 << smTickShift ); + smTickSec = ( F32( smTickMs ) / 1000.f ); + smTickMask = ( smTickMs - 1 ); +} + +//------------------------------------------------------------------------------ + +Vector& ITickable::getProcessList() +{ + // This helps to avoid the static initialization order fiasco + static Vector smProcessList( __FILE__, __LINE__ ); ///< List of tick controls + return smProcessList; +} + +//------------------------------------------------------------------------------ + +bool ITickable::advanceTime( U32 timeDelta ) +{ + U32 targetTime = smLastTime + timeDelta; + U32 targetTick = ( targetTime + smTickMask ) & ~smTickMask; + U32 tickCount = ( targetTick - smLastTick ) >> smTickShift; + + // Advance objects + if( tickCount ) + for( ; smLastTick != targetTick; smLastTick += smTickMs ) + for( ProcessListIterator i = getProcessList().begin(); i != getProcessList().end(); i++ ) + if( (*i)->isProcessingTicks() ) + (*i)->processTick(); + + smLastDelta = ( smTickMs - ( targetTime & smTickMask ) ) & smTickMask; + F32 dt = smLastDelta / F32( smTickMs ); + + // Now interpolate objects that want ticks + for( ProcessListIterator i = getProcessList().begin(); i != getProcessList().end(); i++ ) + if( (*i)->isProcessingTicks() ) + (*i)->interpolateTick( dt ); + + + // Inform ALL objects that time was advanced + dt = F32( timeDelta ) / 1000.f; + for( ProcessListIterator i = getProcessList().begin(); i != getProcessList().end(); i++ ) + (*i)->advanceTime( dt ); + + smLastTime = targetTime; + + return tickCount != 0; +} diff --git a/Engine/source/core/iTickable.h b/Engine/source/core/iTickable.h new file mode 100644 index 000000000..1f5123835 --- /dev/null +++ b/Engine/source/core/iTickable.h @@ -0,0 +1,171 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _ITICKABLE_H_ +#define _ITICKABLE_H_ + +#include "core/util/tVector.h" + +/// This interface allows you to let any object be ticked. You use it like so: +/// @code +/// class FooClass : public SimObject, public virtual ITickable +/// { +/// // You still mark SimObject as Parent +/// typdef SimObject Parent; +/// private: +/// ... +/// +/// protected: +/// // These three methods are the interface for ITickable +/// virtual void interpolateTick( F32 delta ); +/// virtual void processTick(); +/// virtual void advanceTime( F32 timeDelta ); +/// +/// public: +/// ... +/// }; +/// @endcode +/// Please note the three methods you must implement to use ITickable, but don't +/// worry. If you forget, the compiler will tell you so. Also note that the +/// typedef for Parent should NOT BE SET to ITickable, the compiler will probably +/// also tell you if you forget that. Last, but assuridly not least is that you note +/// the way that the inheretance is done: public virtual ITickable +/// It is very important that you keep the virtual keyword in there, otherwise +/// proper behavior is not guarenteed. You have been warned. +/// +/// The point of a tickable object is that the object gets ticks at a fixed rate +/// which is one tick every 32ms. This means, also, that if an object doesn't get +/// updated for 64ms, that the next update it will get two-ticks. Basically it +/// comes down to this. You are assured to get one tick per 32ms of time passing +/// provided that isProcessingTicks returns true when ITickable calls it. +/// +/// isProcessingTicks is a virtual method and you can (should you want to) +/// override it and put some extended functionality to decide if you want to +/// recieve tick-notification or not. +/// +/// The other half of this is that you get time-notification from advanceTime. +/// advanceTime lets you know when time passes regardless of the return value +/// of isProcessingTicks. The object WILL get the advanceTime call every single +/// update. The argument passed to advanceTime is the time since the last call +/// to advanceTime. Updates are not based on the 32ms tick time. Updates are +/// dependant on framerate. So you may get 200 advanceTime calls in a second, or you +/// may only get 20. There is no way of assuring consistant calls of advanceTime +/// like there is with processTick. Both are useful for different things, and +/// it is important to understand the differences between them. +/// +/// Interpolation is the last part of the ITickable interface. It is called +/// every update, as long as isProcessingTicks evaluates to true on the object. +/// This is used to interpolate between 32ms ticks. The argument passed to +/// interpolateTick is the time since the last call to processTick. You can see +/// in the code for ITickable::advanceTime that before a tick occurs it calls +/// interpolateTick(0) on every object. This is to tell objects which do interpolate +/// between ticks to reset their interpolation because they are about to get a +/// new tick. +/// +/// This is an extremely powerful interface when used properly. An example of a class +/// that properly uses this interface is GuiTickCtrl. The documentation for that +/// class describes why it was created and why it was important that it use +/// a consistant update frequency for its effects. +/// @see GuiTickCtrl +/// +/// @todo Support processBefore/After and move the GameBase processing over to use ITickable +class ITickable +{ +private: + static U32 smLastTick; ///< Time of the last tick that occurred + static U32 smLastTime; ///< Last time value at which advanceTime was called + static U32 smLastDelta; ///< Last delta value for advanceTime + + static U32 smTickShift; ///< Shift value to control how often Ticks occur + static U32 smTickMs; ///< Number of milliseconds per tick, 32 in this case + static F32 smTickSec; ///< Fraction of a second per tick + static U32 smTickMask; + + // This just makes life easy + typedef Vector::iterator ProcessListIterator; + /// Returns a reference to the list of all ITickable objects. + static Vector& getProcessList(); + + bool mProcessTick; ///< Set to true if this object wants tick processing +protected: + /// This method is called every frame and lets the control interpolate between + /// ticks so you can smooth things as long as isProcessingTicks returns true + /// when it is called on the object + virtual void interpolateTick( F32 delta ) = 0; + + /// This method is called once every 32ms if isProcessingTicks returns true + /// when called on the object + virtual void processTick() = 0; + + /// This method is called once every frame regardless of the return value of + /// isProcessingTicks and informs the object of the passage of time. + /// @param timeDelta Time increment in seconds. + virtual void advanceTime( F32 timeDelta ) = 0; + +public: + + /// Constructor + /// This will add the object to the process list + ITickable(); + + /// Destructor + /// Remove this object from the process list + virtual ~ITickable(); + + /// Is this object wanting to receive tick notifications + /// @returns True if object wants tick notifications + bool isProcessingTicks() const { return mProcessTick; }; + + /// Sets this object as either tick processing or not + /// @param tick True if this object should process ticks + virtual void setProcessTicks( bool tick = true ); + + /// Initialise the ITickable system. + static void init( const U32 tickShift ); + + /// Gets the Tick bit-shift. + static U32 getTickShift() { return smTickShift; } + /// Gets the Tick (ms) + static U32 getTickMs() { return smTickMs; } + /// Gets the Tick (seconds) + static F32 getTickSec() { return smTickSec; } + /// Gets the Tick mask. + static U32 getTickMask() { return smTickMask; } + +//------------------------------------------------------------------------------ + + /// This is called in clientProcess to advance the time for all ITickable + /// objects. + /// @param timeDelta Time increment in milliseconds. + /// @returns True if any ticks were sent + /// @see clientProcess + static bool advanceTime( U32 timeDelta ); +}; + +//------------------------------------------------------------------------------ + +inline void ITickable::setProcessTicks( bool tick /* = true */ ) +{ + mProcessTick = tick; +} + +#endif diff --git a/Engine/source/core/idGenerator.cpp b/Engine/source/core/idGenerator.cpp new file mode 100644 index 000000000..61fe0b9d1 --- /dev/null +++ b/Engine/source/core/idGenerator.cpp @@ -0,0 +1,36 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/idGenerator.h" + +void IdGenerator::reclaim() +{ + // attempt to keep the pool vector as small as possible by reclaiming + // pool entries back into the nextIdBlock variable + + while (!mPool.empty() && (mPool.last() == (mNextId-1)) ) + { + mNextId--; + mPool.pop_back(); + } +} + diff --git a/Engine/source/core/idGenerator.h b/Engine/source/core/idGenerator.h new file mode 100644 index 000000000..a0ddc74ed --- /dev/null +++ b/Engine/source/core/idGenerator.h @@ -0,0 +1,93 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _IDGENERATOR_H_ +#define _IDGENERATOR_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +class IdGenerator +{ +private: + U32 mIdBlockBase; + U32 mIdRangeSize; + Vector mPool; + U32 mNextId; + + void reclaim(); + +public: + IdGenerator(U32 base, U32 numIds) + { + VECTOR_SET_ASSOCIATION(mPool); + + mIdBlockBase = base; + mIdRangeSize = numIds; + mNextId = mIdBlockBase; + } + + void reset() + { + mPool.clear(); + mNextId = mIdBlockBase; + } + + U32 alloc() + { + // fist check the pool: + if(!mPool.empty()) + { + U32 id = mPool.last(); + mPool.pop_back(); + reclaim(); + return id; + } + if(mIdRangeSize && mNextId >= mIdBlockBase + mIdRangeSize) + return 0; + + return mNextId++; + } + + void free(U32 id) + { + AssertFatal(id >= mIdBlockBase, "IdGenerator::alloc: invalid id, id does not belong to this IdGenerator.") + if(id == mNextId - 1) + { + mNextId--; + reclaim(); + } + else + mPool.push_back(id); + } + + U32 numIdsUsed() + { + return mNextId - mIdBlockBase - mPool.size(); + } +}; + +#endif diff --git a/Engine/source/core/memVolume.cpp b/Engine/source/core/memVolume.cpp new file mode 100644 index 000000000..284e753fb --- /dev/null +++ b/Engine/source/core/memVolume.cpp @@ -0,0 +1,511 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/memVolume.h" + +#include "core/crc.h" +#include "core/frameAllocator.h" +#include "core/util/str.h" +#include "core/strings/stringFunctions.h" +#include "platform/platformVolume.h" + +namespace Torque +{ + namespace Mem + { + + // Multiple MemFile's can reference the same path, so this is here to contain + // the actual data at a Path. + struct MemFileData + { + MemFileData(MemFileSystem* fs, const Path& path) + { + mPath = path; + mBufferSize = 1024; + mFileSize = 0; + mBuffer = dMalloc(mBufferSize); + dMemset(mBuffer, 0, mBufferSize); + mModified = Time::getCurrentTime(); + mLastAccess = mModified; + mFileSystem = fs; + } + + ~MemFileData() + { + dFree(mBuffer); + } + + bool getAttributes(FileNode::Attributes* attr) + { + attr->name = mPath; + attr->flags = FileNode::File; + attr->size = mFileSize; + attr->mtime = mModified; + attr->atime = mLastAccess; + return true; + } + + FileNodeRef resolve(const Path& path) + { + // Is it me? + String sThisPath(mPath); + String sTargetPath(path); + if (sThisPath == sTargetPath) + return new MemFile(mFileSystem, this); + // Nope + return NULL; + } + + Path mPath; + void* mBuffer; + U32 mBufferSize; // This is the size of the memory buffer >= mFileSize + U32 mFileSize; // This is the size of the "file" <= mBufferSize + Time mModified; // Last modified + Time mLastAccess; // Last access + MemFileSystem* mFileSystem; + }; + + struct MemDirectoryData + { + Path mPath; + MemFileSystem* mFileSystem; + Vector mFiles; + Vector mDirectories; + + MemDirectoryData(MemFileSystem* fs, const Path& path) + { + mFileSystem = fs; + mPath = path; + } + + ~MemDirectoryData() + { + for (U32 i = 0; i < mFiles.size(); i++) + { + delete mFiles[i]; + } + for (U32 i = 0; i < mDirectories.size(); i++) + { + delete mDirectories[i]; + } + } + + bool getAttributes(FileNode::Attributes* attr) + { + attr->name = mPath; + attr->flags = FileNode::Directory; + return true; + } + + FileNodeRef resolve(const Path& path) + { + // Is it me? + String sThisPath(mPath); + String sTargetPath(path); + if (sThisPath == sTargetPath) + return new MemDirectory(mFileSystem, this); + // Is it one of my children? + if (sTargetPath.find(sThisPath) == 0) + { + FileNodeRef result; + for (U32 i = 0; i < mDirectories.size() && result.isNull(); i++) + result = mDirectories[i]->resolve(path); + for (U32 i = 0; i < mFiles.size() && result.isNull(); i++) + result = mFiles[i]->resolve(path); + return result; + } + // Nope + return NULL; + } + }; + + //----------------------------------------------------------------------------- + MemFileSystem::MemFileSystem(String volume) + { + mVolume = volume; + mRootDir = new MemDirectoryData(this, volume); + } + + MemFileSystem::~MemFileSystem() + { + delete mRootDir; + } + + FileNodeRef MemFileSystem::resolve(const Path& path) + { + return mRootDir->resolve(path); + } + + // + MemDirectory* MemFileSystem::getParentDir(const Path& path, FileNodeRef& parentRef) + { + parentRef = mRootDir->resolve(path.getRoot() + ":" + path.getPath()); + if (parentRef.isNull()) + return NULL; + + MemDirectory* result = dynamic_cast(parentRef.getPointer()); + return result; + } + + FileNodeRef MemFileSystem::create(const Path& path, FileNode::Mode mode) + { + // Already exists + FileNodeRef result = mRootDir->resolve(path); + if (result.isValid()) + return result; + + // Doesn't exist, try to get parent node. + FileNodeRef parentRef; + MemDirectory* mDir = getParentDir(path, parentRef); + if (mDir) + { + MemDirectoryData* mdd = mDir->mDirectoryData; + switch (mode) + { + case FileNode::File : + { + MemFileData* mfd = new MemFileData(this, path); + mdd->mFiles.push_back(mfd); + return new MemFile(this, mfd); + } + break; + case FileNode::Directory : + { + MemDirectoryData* mfd = new MemDirectoryData(this, path); + mdd->mDirectories.push_back(mfd); + return new MemDirectory(this, mfd); + } + break; + default: + // anything else we ignore + break; + } + } + return NULL; + } + + bool MemFileSystem::remove(const Path& path) + { + FileNodeRef parentRef; + MemDirectory* mDir = getParentDir(path, parentRef); + MemDirectoryData* mdd = mDir->mDirectoryData; + for (U32 i = 0; i < mdd->mDirectories.size(); i++) + { + if (mdd->mDirectories[i]->mPath == path) + { + delete mdd->mDirectories[i]; + mdd->mDirectories.erase_fast(i); + return true; + } + } + for (U32 i = 0; i < mdd->mFiles.size(); i++) + { + if (mdd->mFiles[i]->mPath == path) + { + delete mdd->mFiles[i]; + mdd->mFiles.erase_fast(i); + return true; + } + } + return false; + } + + bool MemFileSystem::rename(const Path& from,const Path& to) + { + // Source must exist + FileNodeRef source = mRootDir->resolve(from); + if (source.isNull()) + return false; + + // Destination must not exist + FileNodeRef dest = mRootDir->resolve(to); + if (source.isValid()) + return false; + + // Get source parent + FileNodeRef sourceParentRef; + MemDirectory* sourceDir = getParentDir(from, sourceParentRef); + + // Get dest parent + FileNodeRef destRef; + MemDirectory* mDir = getParentDir(to, destRef); + + // Now move it/rename it + if (dynamic_cast(source.getPointer())) + { + MemDirectoryData* sourcedd; + MemDirectoryData* d = sourceDir->mDirectoryData; + for (U32 i = 0; i < d->mDirectories.size(); i++) + { + if (d->mDirectories[i]->mPath == from) + { + sourcedd = d->mDirectories[i]; + d->mDirectories.erase_fast(i); + sourcedd->mPath = to; + mDir->mDirectoryData->mDirectories.push_back(sourcedd); + return true; + } + } + } else { + MemFileData* sourceFile; + MemDirectoryData* d = sourceDir->mDirectoryData; + for (U32 i = 0; i < d->mFiles.size(); i++) + { + if (d->mFiles[i]->mPath == from) + { + sourceFile = d->mFiles[i]; + d->mFiles.erase_fast(i); + sourceFile->mPath = to; + mDir->mDirectoryData->mFiles.push_back(sourceFile); + return true; + } + } + } + return false; + } + + Path MemFileSystem::mapTo(const Path& path) + { + String file = mVolume; + file = Path::Join(file, '/', path.getPath()); + file = Path::Join(file, '/', path.getFileName()); + file = Path::Join(file, '.', path.getExtension()); + return file; + } + + Path MemFileSystem::mapFrom(const Path& path) + { + const String::SizeType volumePathLen = mVolume.length(); + + String pathStr = path.getFullPath(); + + if ( mVolume.compare( pathStr, volumePathLen, String::NoCase )) + return Path(); + + return pathStr.substr( volumePathLen, pathStr.length() - volumePathLen ); + } + + //----------------------------------------------------------------------------- + + MemFile::MemFile(MemFileSystem* fs, MemFileData* fileData) + { + mFileData = fileData; + mStatus = Closed; + mCurrentPos = U32_MAX; + mFileSystem = fs; + } + + MemFile::~MemFile() + { + } + + Path MemFile::getName() const + { + return mFileData->mPath; + } + + FileNode::Status MemFile::getStatus() const + { + return mStatus; + } + + bool MemFile::getAttributes(Attributes* attr) + { + return mFileData->getAttributes(attr); + } + + U32 MemFile::calculateChecksum() + { + return CRC::calculateCRC(mFileData->mBuffer, mFileData->mFileSize); + } + + bool MemFile::open(AccessMode mode) + { + mStatus = Open; + mCurrentPos = 0; + switch (mode) + { + case Read : + case ReadWrite : + mCurrentPos = 0; + break; + case Write : + mCurrentPos = 0; + mFileData->mFileSize = 0; + break; + case WriteAppend : + mCurrentPos = mFileData->mFileSize; + break; + } + return true; + } + + bool MemFile::close() + { + mStatus = Closed; + return true; + } + + U32 MemFile::getPosition() + { + if (mStatus == Open || mStatus == EndOfFile) + return mCurrentPos; + return 0; + } + + U32 MemFile::setPosition(U32 delta, SeekMode mode) + { + if (mStatus != Open && mStatus != EndOfFile) + return 0; + + switch (mode) + { + case Begin: + mCurrentPos = delta; + break; + case Current: + mCurrentPos += delta; + break; + case End: + mCurrentPos = mFileData->mFileSize - delta; + break; + } + + mStatus = Open; + + return mCurrentPos; + } + + U32 MemFile::read(void* dst, U32 size) + { + if (mStatus != Open && mStatus != EndOfFile) + return 0; + + U32 copyAmount = getMin(size, mFileData->mFileSize - mCurrentPos); + dMemcpy(dst, (U8*) mFileData->mBuffer + mCurrentPos, copyAmount); + mCurrentPos += copyAmount; + mFileData->mLastAccess = Time::getCurrentTime(); + if (mCurrentPos == mFileData->mFileSize) + mStatus = EndOfFile; + return copyAmount; + } + + U32 MemFile::write(const void* src, U32 size) + { + if ((mStatus != Open && mStatus != EndOfFile) || !size) + return 0; + + if (mFileData->mFileSize + size > mFileData->mBufferSize) + { + // Keep doubling our buffer size until we're big enough. + while (mFileData->mFileSize + size > mFileData->mBufferSize) + mFileData->mBufferSize *= 2; + mFileData->mBuffer = dRealloc(mFileData->mBuffer, mFileData->mBufferSize); + if (!mFileData->mBuffer) + { + mStatus = FileSystemFull; + return 0; + } + } + + dMemcpy((U8*)mFileData->mBuffer + mCurrentPos, src, size); + mCurrentPos += size; + mFileData->mFileSize = getMax(mFileData->mFileSize, mCurrentPos); + mFileData->mLastAccess = Time::getCurrentTime(); + mFileData->mModified = mFileData->mLastAccess; + + return size; + } + + //----------------------------------------------------------------------------- + + MemDirectory::MemDirectory(MemFileSystem* fs, MemDirectoryData* dir) + { + mStatus = Closed; + mDirectoryData = dir; + mFileSystem = fs; + } + + MemDirectory::~MemDirectory() + { + } + + Path MemDirectory::getName() const + { + return mDirectoryData->mPath; + } + + bool MemDirectory::open() + { + mSearchIndex = 0; + mStatus = Open; + return true; + } + + bool MemDirectory::close() + { + return true; + } + + bool MemDirectory::read(Attributes* entry) + { + if (mStatus != Open) + return false; + + if (mSearchIndex < mDirectoryData->mDirectories.size()) + { + mDirectoryData->mDirectories[mSearchIndex]->getAttributes(entry); + mSearchIndex++; + return true; + } + + AssertFatal(mSearchIndex > mDirectoryData->mDirectories.size(), "This should not happen!"); + U32 fileIndex = mSearchIndex - mDirectoryData->mDirectories.size(); + if (fileIndex < mDirectoryData->mFiles.size()) + { + mDirectoryData->mFiles[mSearchIndex]->getAttributes(entry); + mSearchIndex++; + return true; + } + + return false; + } + + U32 MemDirectory::calculateChecksum() + { + // Return checksum of current entry + return 0; + } + + bool MemDirectory::getAttributes(Attributes* attr) + { + return mDirectoryData->getAttributes(attr); + } + + FileNode::Status MemDirectory::getStatus() const + { + return mStatus; + } + } // Namespace Mem + +} // Namespace Torque \ No newline at end of file diff --git a/Engine/source/core/memVolume.h b/Engine/source/core/memVolume.h new file mode 100644 index 000000000..18e4641fb --- /dev/null +++ b/Engine/source/core/memVolume.h @@ -0,0 +1,132 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _MEMVOLUME_H_ +#define _MEMVOLUME_H_ + +#ifndef _VOLUME_H_ +#include "core/volume.h" +#endif + +#ifndef _TDICTIONARY_H_ +#include "core/util/tDictionary.h" +#endif + +namespace Torque +{ + using namespace FS; + + namespace Mem + { + + struct MemFileData; + struct MemDirectoryData; + class MemDirectory; + + //----------------------------------------------------------------------------- + class MemFileSystem: public FileSystem + { + public: + MemFileSystem(String volume); + ~MemFileSystem(); + + String getTypeStr() const { return "Mem"; } + + FileNodeRef resolve(const Path& path); + FileNodeRef create(const Path& path,FileNode::Mode); + bool remove(const Path& path); + bool rename(const Path& from,const Path& to); + Path mapTo(const Path& path); + Path mapFrom(const Path& path); + + private: + String mVolume; + MemDirectoryData* mRootDir; + + MemDirectory* getParentDir(const Path& path, FileNodeRef& parentRef); + }; + + //----------------------------------------------------------------------------- + /// Mem stdio file access. + /// This class makes use the fopen, fread and fwrite for buffered io. + class MemFile: public File + { + public: + MemFile(MemFileSystem* fs, MemFileData* fileData); + virtual ~MemFile(); + + Path getName() const; + Status getStatus() const; + bool getAttributes(Attributes*); + + U32 getPosition(); + U32 setPosition(U32,SeekMode); + + bool open(AccessMode); + bool close(); + + U32 read(void* dst, U32 size); + U32 write(const void* src, U32 size); + + private: + U32 calculateChecksum(); + + MemFileSystem* mFileSystem; + MemFileData* mFileData; + Status mStatus; + U32 mCurrentPos; + + bool _updateInfo(); + void _updateStatus(); + }; + + + //----------------------------------------------------------------------------- + + class MemDirectory: public Directory + { + public: + MemDirectory(MemFileSystem* fs, MemDirectoryData* dir); + ~MemDirectory(); + + Path getName() const; + Status getStatus() const; + bool getAttributes(Attributes*); + + bool open(); + bool close(); + bool read(Attributes*); + + private: + friend class MemFileSystem; + MemFileSystem* mFileSystem; + MemDirectoryData* mDirectoryData; + + U32 calculateChecksum(); + + Status mStatus; + U32 mSearchIndex; + }; + + } // Namespace +} // Namespace + +#endif \ No newline at end of file diff --git a/Engine/source/core/module.cpp b/Engine/source/core/module.cpp new file mode 100644 index 000000000..f069cbcb4 --- /dev/null +++ b/Engine/source/core/module.cpp @@ -0,0 +1,385 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/module.h" +#include "core/util/tVector.h" +#include "core/strings/stringFunctions.h" + + +//#define DEBUG_SPEW +#define DEBUG_SPEW_LEVEL 2 + + +Module* Module::smFirst; + + +//----------------------------------------------------------------------------- + +bool Module::_constrainedToComeBefore( Module* module, Mode mode ) +{ + if( module == this ) + return false; + + for( Dependency* dependency = _getDependencies( mode ); + dependency != NULL; dependency = dependency->mNext ) + { + Module* depModule = dependency->mModule; + if( !depModule ) + { + depModule = ModuleManager::findModule( dependency->mModuleName ); + if( !depModule ) + { + // Module does not exist. Only emit a warning here so that modules + // can be omitted from a link without requiring the module definitions + // to be adapted. + + Platform::outputDebugString( "[ModuleManager] Module %s of '%s' depends on module '%s' which does not exist", + mode == Module::ModeInitialize ? "init" : "shutdown", + module->getName(), dependency->mModuleName ); + continue; + } + + dependency->mModule = depModule; + } + + if( dependency->mType == DependencyBefore ) + { + if( depModule == module + || depModule->_constrainedToComeBefore( module, mode ) ) + return true; + } + } + + return false; +} + +//----------------------------------------------------------------------------- + +bool Module::_constrainedToComeAfter( Module* module, Mode mode ) +{ + if( module == this ) + return false; + + for( Dependency* dependency = _getDependencies( mode ); + dependency != NULL; dependency = dependency->mNext ) + { + Module* depModule = dependency->mModule; + if( !depModule ) + { + depModule = ModuleManager::findModule( dependency->mModuleName ); + if( !depModule ) + { + // Module does not exist. Only emit a warning here so that modules + // can be omitted from a link without requiring the module definitions + // to be adapted. + + Platform::outputDebugString( "[ModuleManager] Module %s of '%s' depends on module '%s' which does not exist", + mode == Module::ModeInitialize ? "init" : "shutdown", + module->getName(), dependency->mModuleName ); + continue; + } + + dependency->mModule = depModule; + } + + if( dependency->mType == DependencyAfter ) + { + if( depModule == module + || depModule->_constrainedToComeAfter( module, mode ) ) + return true; + } + } + + return false; +} + +//----------------------------------------------------------------------------- + +String ModuleManager::_moduleListToString( Vector< Module* >& moduleList ) +{ + StringBuilder str; + + const U32 numModules = moduleList.size(); + bool isFirst = true; + for( U32 i = 0; i < numModules; ++ i ) + { + if( !isFirst ) + str.append( " -> " ); + + str.append( moduleList[ i ]->getName() ); + + isFirst = false; + } + + return str.end(); +} + +//----------------------------------------------------------------------------- + +void ModuleManager::_printModuleList( Vector< Module* >& moduleList ) +{ + Platform::outputDebugString( _moduleListToString( moduleList ) ); +} + +//----------------------------------------------------------------------------- + +void ModuleManager::_insertIntoModuleList( Module::Mode mode, Vector< Module* >& moduleList, Module* module ) +{ + // If this module is being overridden, switch over to + // the module overriding it. + + Module* override; + do + { + override = _findOverrideFor( module ); + if( override ) + module = override; + } + while( override != NULL ); + + // If we are already on the list, return. + + if( _getIndexOfModuleInList( moduleList, module ) != -1 ) + return; + + // If we don't have dependencies, just push the module + // to the back of the list. + + if( !module->_getDependencies( mode ) ) + { + #if defined( DEBUG_SPEW ) && DEBUG_SPEW_LEVEL > 1 + Platform::outputDebugString( "[ModuleManager] Appending '%s' to '%s'", + module->getName(), _moduleListToString( moduleList ).c_str() ); + #endif + + moduleList.push_back( module ); + return; + } + + // First make sure that all 'after' dependencies are in the list. + + #if defined( DEBUG_SPEW ) && DEBUG_SPEW_LEVEL > 1 + Platform::outputDebugString( "[ModuleManager] Resolving %s dependencies of '%s'", + mode == Module::ModeInitialize ? "init" : "shutdown", + module->getName() ); + #endif + + for( Module::Dependency* dependency = module->_getDependencies( mode ); + dependency != NULL; dependency = dependency->mNext ) + { + if( dependency->mType != Module::DependencyAfter ) + continue; + + dependency->mModule = findModule( dependency->mModuleName ); + if( !dependency->mModule ) + continue; // Allow modules to not exist. + + if( _getIndexOfModuleInList( moduleList, dependency->mModule ) == -1 ) + _insertIntoModuleList( mode, moduleList, dependency->mModule ); + } + + AssertFatal( _getIndexOfModuleInList( moduleList, module ) == -1, + avar( "ModuleManager::_insertModuleIntoList - Cycle in 'after' %s dependency chain of '%s'", + mode == Module::ModeInitialize ? "init" : "shutdown", + module->getName() ) ); + + // Now add the module itself. + + const U32 numModules = moduleList.size(); + for( U32 i = 0; i < numModules; ++ i ) + { + const bool thisBeforeCurrent = module->_constrainedToComeBefore( moduleList[ i ], mode ); + const bool currentAfterThis = moduleList[ i ]->_constrainedToComeAfter( module, mode ); + + AssertFatal( !( thisBeforeCurrent && currentAfterThis ), + avar( "ModuleManager::_insertModuleIntoList - Ambiguous %s placement of module '%s' relative to '%s'", + mode == Module::ModeInitialize ? "init" : "shutdown", + module->getName(), moduleList[ i ]->getName() ) ); + + // If no contraints relate us to this module, + // push us one more position back in the line. + + if( !thisBeforeCurrent && !currentAfterThis ) + continue; + + // If this module is contrained to come before the + // module at our current position but that module does + // not actually have dependencies of its own, make sure + // that module is at the back of the module list so that + // if we have more dependencies, it will not prevent us + // from correctly positioning us in relation to them. + + if( thisBeforeCurrent && !moduleList[ i ]->_getDependencies( mode ) && i != numModules - 1 ) + { + #if defined( DEBUG_SPEW ) && DEBUG_SPEW_LEVEL > 1 + Platform::outputDebugString( "[ModuleManager] Pushing '%s' to back end of chain for resolving '%s'", + moduleList[ i ]->getName(), module->getName() ); + #endif + + Module* depModule = moduleList[ i ]; + moduleList.erase( i ); + -- i; + + moduleList.push_back( depModule ); + continue; + } + + // Try the reverse constraint with all remaining modules in the list. + // If there is one for which we have one, then the placement of this + // module is ambiguous. + + for( U32 n = i + 1; n < numModules; ++ n ) + AssertFatal( !( moduleList[ n ]->_constrainedToComeBefore( module, mode ) + || module->_constrainedToComeAfter( moduleList[ n ], mode ) ), + avar( "ModuleManager::_insertModuleIntoList - Ambiguous %s constraint on module '%s' to come before '%s' yet after '%s'", + mode == Module::ModeInitialize ? "init" : "shutdown", + module->getName(), + moduleList[ i ]->getName(), + moduleList[ n ]->getName() ) ); + + // Add the module at this position. + + #if defined( DEBUG_SPEW ) && DEBUG_SPEW_LEVEL > 1 + Platform::outputDebugString( "[ModuleManager] Inserting '%s' at index %i into '%s'", + module->getName(), i, _moduleListToString( moduleList ).c_str() ); + #endif + + moduleList.insert( i, module ); + return; + } + + // No constraint-based position. Just append. + + #if defined( DEBUG_SPEW ) && DEBUG_SPEW_LEVEL > 1 + Platform::outputDebugString( "[ModuleManager] Appending '%s' to '%s'", + module->getName(), _moduleListToString( moduleList ).c_str() ); + #endif + + moduleList.push_back( module ); +} + +//----------------------------------------------------------------------------- + +Module* ModuleManager::_findOverrideFor( Module* module ) +{ + const char* name = module->getName(); + + for( Module* ptr = Module::smFirst; ptr != NULL; ptr = ptr->mNext ) + for( Module::Override* override = ptr->mOverrides; override != NULL; override = override->mNext ) + if( dStricmp( override->mModuleName, name ) == 0 ) + return ptr; + + return NULL; +} + +//----------------------------------------------------------------------------- + +S32 ModuleManager::_getIndexOfModuleInList( Vector< Module* >& moduleList, Module* module ) +{ + const U32 numModules = moduleList.size(); + for( U32 i = 0; i < numModules; ++ i ) + if( moduleList[ i ] == module ) + return i; + + return -1; +} + +//----------------------------------------------------------------------------- + +S32 ModuleManager::_getIndexOfModuleInList( Vector< Module* >& moduleList, const char* moduleName ) +{ + const U32 numModules = moduleList.size(); + for( U32 i = 0; i < numModules; ++ i ) + if( dStricmp( moduleList[ i ]->getName(), moduleName ) == 0 ) + return i; + + return -1; +} + +//----------------------------------------------------------------------------- + +void ModuleManager::_createModuleList( Module::Mode mode, Vector< Module* >& moduleList ) +{ + for( Module* module = Module::smFirst; module != NULL; module = module->mNext ) + _insertIntoModuleList( mode, moduleList, module ); +} + +//----------------------------------------------------------------------------- + +void ModuleManager::initializeSystem() +{ + Vector< Module* > modules; + + _createModuleList( Module::ModeInitialize, modules ); + + const U32 numModules = modules.size(); + for( U32 i = 0; i < numModules; ++ i ) + { + Module* module = modules[ i ]; + if( !module->mIsInitialized ) + { + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[ModuleManager] Initializing %s", + module->getName() ); + #endif + + module->initialize(); + module->mIsInitialized = true; + } + } +} + +//----------------------------------------------------------------------------- + +void ModuleManager::shutdownSystem() +{ + Vector< Module* > modules; + + _createModuleList( Module::ModeShutdown, modules ); + + const U32 numModules = modules.size(); + for( U32 i = 0; i < numModules; ++ i ) + { + if( modules[ i ]->mIsInitialized ) + { + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[ModuleManager] Shutting down %s", + modules[ i ]->getName() ); + #endif + + modules[ i ]->shutdown(); + modules[ i ]->mIsInitialized = false; + } + } +} + +//----------------------------------------------------------------------------- + +Module* ModuleManager::findModule( const char* name ) +{ + for( Module* module = Module::smFirst; module != NULL; module = module->mNext ) + if( dStricmp( module->getName(), name ) == 0 ) + return module; + + return NULL; +} diff --git a/Engine/source/core/module.h b/Engine/source/core/module.h new file mode 100644 index 000000000..995ab1d13 --- /dev/null +++ b/Engine/source/core/module.h @@ -0,0 +1,357 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MODULE_H_ +#define _MODULE_H_ + +#ifndef _TSINGLETON_H_ + #include "core/util/tSingleton.h" +#endif +#ifndef _TVECTOR_H_ + #include "core/util/tVector.h" +#endif + + +/// @file +/// A system for keeping initialization and shutdown modular while +/// avoiding non-trivial global constructors/destructors. + + +/// An engine component that requires initialization and/or cleanup. +class Module +{ + public: + + typedef void Parent; + friend struct ModuleManager; + + protected: + + struct Dependency; + friend struct Dependency; + + enum Mode + { + ModeInitialize, + ModeShutdown + }; + + /// Direction of a dependency edge. + enum DependencyType + { + DependencyBefore, + DependencyAfter + }; + + /// Entry in the list of dependencies. + struct Dependency + { + /// Direction of dependence. A "before" dependence goes the reverse direction. + DependencyType mType; + + /// Name of the module that this module depends on. + const char* mModuleName; + + /// Pointer to module. Filled by init code. + Module* mModule; + + /// Next dependency or NULL. + Dependency* mNext; + + Dependency( Mode mode, DependencyType type, Module* parentModule, const char* moduleName ) + : mType( type ), + mNext( mode == ModeInitialize ? parentModule->mInitDependencies : parentModule->mShutdownDependencies ), + mModuleName( moduleName ), + mModule( NULL ) + { + if( mode == ModeInitialize ) + parentModule->mInitDependencies = this; + else + parentModule->mShutdownDependencies = this; + } + }; + + /// Record for module that this module overrides. + struct Override + { + /// Name of module being overridden. + const char* mModuleName; + + /// Next override or NULL. + Override* mNext; + + Override( Module* parentModule, const char* moduleName ) + : mModuleName( moduleName ), + mNext( parentModule->mOverrides ) + { + parentModule->mOverrides = this; + } + }; + + /// Flag to make sure we don't shutdown modules that have not been initialized. + bool mIsInitialized; + + /// Next module in the global module list. + Module* mNext; + + /// List of modules to which the initialization of this module has dependency relations. + Dependency* mInitDependencies; + + /// List of modules to which the shutdown of this module has dependency relations. + Dependency* mShutdownDependencies; + + /// List of modules being overriden by this module. + Override* mOverrides; + + /// Global list of modules. + static Module* smFirst; + + /// Return true if this module is constrained to precede "module" in the given "mode". + bool _constrainedToComeBefore( Module* module, Mode mode ); + + /// Return true if this module is constrained to follow "module" in the given "mode". + bool _constrainedToComeAfter( Module* module, Mode mode ); + + /// + Dependency* _getDependencies( Mode mode ) + { + if( mode == ModeInitialize ) + return mInitDependencies; + else + return mShutdownDependencies; + } + + Module() + : mNext( smFirst ), + mInitDependencies( NULL ), + mShutdownDependencies( NULL ), + mOverrides( NULL ), + mIsInitialized( false ) + { + smFirst = this; + } + + public: + + /// Return the module name. + virtual const char* getName() const = 0; + + /// Initialize the module. This is only called after all modules that this + /// module depends on have been initialized. + virtual void initialize() {} + + /// Shut down the module. This is called before any module that this module + /// depends on have been shut down. + virtual void shutdown() {} +}; + + +/// Begin a module definition. +/// +/// @code +/// MODULE_BEGIN( MyModule ) +/// +/// MODULE_INIT_AFTER( Sim ) +/// MODULE_INIT_BEFORE( 3D ) +/// MODULE_SHUTDOWN_BEFORE( Sim ) +/// +/// MODULE_INIT +/// { +/// // Init code... +/// } +/// +/// MODULE_SHUTDOWN +/// { +/// // Cleanup code... +/// } +/// +/// MODULE_END; +/// @endcode +#define MODULE_BEGIN( name ) \ + namespace { namespace _ ## name { \ + class _ModuleInst : public ::Module { \ + public: \ + typedef ::Module Parent; \ + static _ModuleInst smInstance; \ + virtual const char* getName() const { return #name; } + +/// Make sure this module is initialized before the module called "name". +/// +/// @code +/// MODULE_BEGIN( MyModule ) +/// MODULE_INIT_BEFORE( MyOtherModule ) +/// MODULE_END; +/// @endcode +#define MODULE_INIT_BEFORE( name ) \ + struct _DepInitBefore ## name : public Parent::Dependency \ + { \ + _DepInitBefore ## name() \ + : Parent::Dependency( ModeInitialize, DependencyBefore, &smInstance, #name ) {} \ + } mDepInitBefore ## name; + +/// Make sure this module is initialized after the module called "name". +/// +/// @code +/// MODULE_BEGIN( MyModule ) +/// MODULE_INIT_AFTER( MyOtherModule ) +/// MODULE_END; +/// @endcode +#define MODULE_INIT_AFTER( name ) \ + struct _DepInitAfter ## name : public Parent::Dependency \ + { \ + _DepInitAfter ## name() \ + : Parent::Dependency( ModeInitialize, DependencyAfter, &smInstance, #name ) {} \ + } mDepInitAfter ## name; + +/// Make sure this module is initialized before the module called "name". +/// +/// @code +/// MODULE_BEGIN( MyModule ) +/// MODULE_SHUTDOWN_BEFORE( MyOtherModule ) +/// MODULE_END; +/// @endcode +#define MODULE_SHUTDOWN_BEFORE( name ) \ + struct _DepShutdownBefore ## name : public Parent::Dependency \ + { \ + _DepShutdownBefore ## name() \ + : Parent::Dependency( ModeShutdown, DependencyBefore, &smInstance, #name ) {} \ + } mDepShutdownBefore ## name; + +/// Make sure this module is initialized after the module called "name". +/// +/// @code +/// MODULE_BEGIN( MyModule ) +/// MODULE_SHUTDOWN_AFTER( MyOtherModule ) +/// MODULE_END; +/// @endcode +#define MODULE_SHUTDOWN_AFTER( name ) \ + struct _DepShutdownAfter ## name : public Parent::Dependency \ + { \ + _DepShutdownAfter ## name() \ + : Parent::Dependency( ModeShutdown, DependencyAfter, &smInstance, #name ) {} \ + } mDepShutdownAfter ## name; + +/// Replace the given module in both the init and the shutdown sequence. +/// +/// @code +/// MODULE_BEGIN( MyMoveManager ) +/// MODULE_OVERRIDE( MoveManager ) +/// MODULE_END; +/// @endcode +#define MODULE_OVERRIDE( name ) \ + struct _Override ## name : public Parent::Override \ + { \ + _Override ## name() \ + : Parent::Override( &smInstance, #name ) {} \ + } mOverride ## name; + +/// Define initialization code for the module. +/// +/// @code +/// MODULE_BEGIN( MyModule ) +/// MODULE_INIT +/// { +/// // Init code goes here. +/// } +/// MODULE_END; +/// @endcode +#define MODULE_INIT \ + virtual void initialize() + +/// Define cleanup code for the module. +/// +/// @code +/// MODULE_BEGIN( MyModule ) +/// MODULE_SHUTDOWN +/// { +/// // Cleanup code goes here. +/// } +/// MODULE_END; +/// @endcode +#define MODULE_SHUTDOWN \ + virtual void shutdown() + +/// Terminate a module definition. +/// +/// @code +/// MODULE_BEGIN( MyModule ) +/// MODULE_END; +/// @endcode +#define MODULE_END \ + }; \ + _ModuleInst _ModuleInst::smInstance; \ + } } + + + +/// Used to define a function which will be called right +/// after the named module is initialized. +/// +/// @code +/// AFTER_MODULE_INIT( Sim ) +/// { +/// Con::addVariable( "$myBool", TypeBool, &smMyBool ); +/// } +/// @endcode +/// +#define AFTER_MODULE_INIT( name ) \ + namespace { \ + class _AfterModuleInit : public ::Module { \ + public: \ + typedef ::Module Parent; \ + static _AfterModuleInit smInstance; \ + virtual const char* getName() const { return "AFTER_MODULE_INIT( " #name " ) in " __FILE__; } \ + struct _DepInitAfter : public Parent::Dependency \ + { \ + _DepInitAfter() \ + : Parent::Dependency( ModeInitialize, DependencyAfter, &smInstance, #name ) {} \ + } mDepInitAfter; \ + virtual void initialize(); \ + }; \ + _AfterModuleInit _AfterModuleInit::smInstance; \ + } \ + void _AfterModuleInit::initialize() + + +struct ModuleManager +{ + /// Initialize all modules registered with the system. + static void initializeSystem(); + + /// Shutdown all modules registered with the system. + static void shutdownSystem(); + + /// Return the instance of the module called "name" or NULL if no such module is defined. + static Module* findModule( const char* name ); + + private: + + static Module* _findOverrideFor( Module* module ); + static String _moduleListToString( Vector< Module* >& moduleList ); + static void _printModuleList( Vector< Module* >& moduleList ); + static void _insertIntoModuleList( Module::Mode mode, Vector< Module* >& moduleList, Module* module ); + static S32 _getIndexOfModuleInList( Vector< Module* >& moduleList, Module* module ); + static S32 _getIndexOfModuleInList( Vector< Module* >& moduleList, const char* moduleName ); + static void _createModuleList( Module::Mode mode, Vector< Module* >& moduleList ); +}; + +#endif // !_MODULE_H_ diff --git a/Engine/source/core/ogg/oggInputStream.cpp b/Engine/source/core/ogg/oggInputStream.cpp new file mode 100644 index 000000000..7704f3152 --- /dev/null +++ b/Engine/source/core/ogg/oggInputStream.cpp @@ -0,0 +1,261 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/ogg/oggInputStream.h" +#include "core/stream/stream.h" +#include "core/util/safeDelete.h" + + +//#define DEBUG_SPEW + + +//----------------------------------------------------------------------------- +// OggDecoder implementation. +//----------------------------------------------------------------------------- + +OggDecoder::OggDecoder( const ThreadSafeRef< OggInputStream >& stream ) + : mOggStream( stream ) +{ +} + +OggDecoder::~OggDecoder() +{ + ogg_stream_clear( &mOggStreamState ); +} + +void OggDecoder::_setStartPage( ogg_page* startPage ) +{ + ogg_stream_init( &mOggStreamState, ogg_page_serialno( startPage ) ); + ogg_stream_pagein( &mOggStreamState, startPage ); +} + +bool OggDecoder::_readNextPacket( ogg_packet* packet ) +{ + MutexHandle mutex; + mutex.lock( &mMutex, true ); + + while( 1 ) + { + int result = ogg_stream_packetout( &mOggStreamState, packet ); + if( result == 0 ) + { + if( !mOggStream->_requestData() ) + return false; + } + else if( result < 0 ) + return false; + else + { + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[OggDecoder] read packet %i in %s (bytes: %i, bos: %s, eos: %s)", + ( U32 ) packet->packetno, + getName(), + ( U32 ) packet->bytes, + packet->b_o_s ? "1" : "0", + packet->e_o_s ? "1" : "0" ); + #endif + + return true; + } + } +} + +bool OggDecoder::_nextPacket() +{ + MutexHandle mutex; + mutex.lock( &mMutex, true ); + + ogg_packet packet; + + do + { + if( !_readNextPacket( &packet ) ) + return false; + } + while( !_packetin( &packet ) ); + + return true; +} + +//----------------------------------------------------------------------------- +// OggInputStream implementation. +//----------------------------------------------------------------------------- + +OggInputStream::OggInputStream( Stream* stream ) + : mStream( stream ), + mIsAtEnd( false ) +{ + ogg_sync_init( &mOggSyncState ); + + VECTOR_SET_ASSOCIATION( mConstructors ); + VECTOR_SET_ASSOCIATION( mDecoders ); +} + +OggInputStream::~OggInputStream() +{ + _freeDecoders(); + ogg_sync_clear( &mOggSyncState ); + + if( mStream ) + SAFE_DELETE( mStream ); +} + +OggDecoder* OggInputStream::getDecoder( const String& name ) const +{ + for( U32 i = 0; i < mDecoders.size(); ++ i ) + if( name.equal( mDecoders[ i ]->getName(), String::NoCase ) ) + return mDecoders[ i ]; + + return NULL; +} + +bool OggInputStream::isAtEnd() +{ + MutexHandle mutex; + mutex.lock( &mMutex, true ); + + return mIsAtEnd; +} + +bool OggInputStream::init() +{ + if( !mStream->hasCapability( Stream::StreamPosition ) ) + return false; + + mStream->setPosition( 0 ); + + // Read all beginning-of-stream pages and construct decoders + // for all streams we recognize. + + while( 1 ) + { + // Read next page. + + ogg_page startPage; + _pullNextPage( &startPage ); + + // If not a beginning-of-stream page, push it to the decoders + // and stop reading headers. + + if( !ogg_page_bos( &startPage ) ) + { + _pushNextPage( &startPage ); + break; + } + + // Try the list of constructors for one that consumes + // the page. + + for( U32 i = 0; i < mConstructors.size(); ++ i ) + { + OggDecoder* decoder = mConstructors[ i ]( this ); + if( decoder->_detect( &startPage ) ) + mDecoders.push_back( decoder ); + else + delete decoder; + } + } + + // Initialize decoders and let all them finish up header processing. + + for( U32 i = 0; i < mDecoders.size(); ++ i ) + if( !mDecoders[ i ]->_init() ) + { + delete mDecoders[ i ]; + mDecoders.erase( i ); + -- i; + } + + if( !mDecoders.size() ) + return false; + + return true; +} + +void OggInputStream::_freeDecoders() +{ + for( U32 i = 0; i < mDecoders.size(); ++ i ) + delete mDecoders[ i ]; + mDecoders.clear(); +} + +bool OggInputStream::_pullNextPage( ogg_page* page) +{ + // Read another page. + + while( ogg_sync_pageout( &mOggSyncState, page ) != 1 ) + { + enum { BUFFER_SIZE = 4096 }; + + // Read more data. + + char* buffer = ogg_sync_buffer( &mOggSyncState, BUFFER_SIZE ); + const U32 oldPos = mStream->getPosition(); + mStream->read( BUFFER_SIZE, buffer ); + + const U32 numBytes = mStream->getPosition() - oldPos; + if( numBytes ) + ogg_sync_wrote( &mOggSyncState, numBytes ); + else + return false; + } + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[OggInputStream] pulled next page (header: %i, body: %i)", + page->header_len, page->body_len ); + #endif + + return true; +} + +void OggInputStream::_pushNextPage( ogg_page* page ) +{ + for( U32 i = 0; i < mDecoders.size(); ++ i ) + { + MutexHandle mutex; + mutex.lock( &mDecoders[ i ]->mMutex, true ); + + ogg_stream_pagein( &mDecoders[ i ]->mOggStreamState, page ); + } +} + +bool OggInputStream::_requestData() +{ + // Lock at this level to ensure correct ordering of page writes. + // Technically, the proper place to lock would be _pullNextPage + // but then it could happen that one thread pushes a page before + // another thread gets to push a page that has been read earlier. + + MutexHandle mutex; + mutex.lock( &mMutex, true ); + + ogg_page nextPage; + + if( !_pullNextPage( &nextPage ) ) + { + mIsAtEnd = true; + return false; + } + + _pushNextPage( &nextPage ); + return true; +} diff --git a/Engine/source/core/ogg/oggInputStream.h b/Engine/source/core/ogg/oggInputStream.h new file mode 100644 index 000000000..5c975f9cd --- /dev/null +++ b/Engine/source/core/ogg/oggInputStream.h @@ -0,0 +1,173 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _OGGINPUTSTREAM_H_ +#define _OGGINPUTSTREAM_H_ + +#ifndef _TVECTOR_H_ + #include "core/util/tVector.h" +#endif +#ifndef _TYPETRAITS_H_ + #include "platform/typetraits.h" +#endif +#ifndef _PLATFORM_THREADS_MUTEX_H_ + #include "platform/threads/mutex.h" +#endif +#ifndef _THREADSAFEREFCOUNT_H_ + #include "platform/threads/threadSafeRefCount.h" +#endif +#include "ogg/ogg.h" + + +class Stream; +class OggInputStream; + + +/// Single substream in a multiplexed OGG stream. +class OggDecoder +{ + public: + + typedef void Parent; + friend class OggInputStream; + + protected: + + /// The Ogg container stream. + OggInputStream* mOggStream; + + /// The Ogg bitstream. + ogg_stream_state mOggStreamState; + + /// Lock for synchronizing access to Ogg stream state. + Mutex mMutex; + + /// Read the next packet in the stream. + /// @return false if there is no next packet. + bool _readNextPacket( ogg_packet* packet ); + + /// + bool _nextPacket(); + + /// + virtual bool _detect( ogg_page* startPage ) = 0; + + /// + virtual bool _init() = 0; + + /// + virtual bool _packetin( ogg_packet* packet ) = 0; + + /// + void _setStartPage( ogg_page* startPage ); + + public: + + /// + OggDecoder( const ThreadSafeRef< OggInputStream >& stream ); + + virtual ~OggDecoder(); + + /// Return the serial number of the Ogg bitstream. + U32 getStreamSerialNo() const { return mOggStreamState.serialno; } + + /// + virtual const char* getName() const = 0; +}; + +/// A multiplexed OGG input stream feeding into stream decoders. +class OggInputStream : public ThreadSafeRefCount< OggInputStream > +{ + public: + + typedef void Parent; + friend class OggDecoder; // _requestData + + protected: + + typedef OggDecoder* ( *Constructor )( const ThreadSafeRef< OggInputStream >& stream ); + + template< typename T > + struct _SpellItOutForGCC + { + static OggDecoder* _fn( const ThreadSafeRef< OggInputStream >& stream ) + { + return constructSingle< T* >( stream ); + } + }; + + /// + bool mIsAtEnd; + + /// + Stream* mStream; + + /// + Vector< Constructor > mConstructors; + + /// + Vector< OggDecoder* > mDecoders; + + /// + ogg_sync_state mOggSyncState; + + /// + Mutex mMutex; + + /// Pull the next page from the OGG stream. + bool _pullNextPage( ogg_page* page ); + + /// Push the given page to the attached decoder streams. + void _pushNextPage( ogg_page* page ); + + /// + bool _requestData(); + + /// + void _freeDecoders(); + + public: + + /// + /// @note Ownership of "stream" is transferred to OggInputStream. + OggInputStream( Stream* stream ); + + ~OggInputStream(); + + /// Register a decoder class with the stream. + template< class T > + void addDecoder() + { + mConstructors.push_back( &_SpellItOutForGCC< T >::_fn ); + } + + /// + OggDecoder* getDecoder( const String& name ) const; + + /// + bool init(); + + /// + bool isAtEnd(); +}; + +#endif // !_OGGINPUTSTREAM_H_ diff --git a/Engine/source/core/ogg/oggTheoraDecoder.cpp b/Engine/source/core/ogg/oggTheoraDecoder.cpp new file mode 100644 index 000000000..b0c62ea5f --- /dev/null +++ b/Engine/source/core/ogg/oggTheoraDecoder.cpp @@ -0,0 +1,694 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/ogg/oggTheoraDecoder.h" + +#include "gfx/gfxFormatUtils.h" +#include "math/mMathFn.h" +#include "console/console.h" + + +//#define DEBUG_SPEW + + +//----------------------------------------------------------------------------- + +// Lookup tables for the transcoders. +// +// The Y, Cb, and Cr tables are used by both the SSE2 and the generic transcoder. +// For the SSE2 code, the data must be 16 byte aligned. +// +// The clamping table is only used by the generic transcoder. The SSE2 transcoder +// uses instructions to implicitly clamp out-of-range values. + +dALIGN( static S32 sRGBY[ 256 ][ 4 ] ); +dALIGN( static S32 sRGBCb[ 256 ][ 4 ] ); +dALIGN( static S32 sRGBCr[ 256 ][ 4 ] ); + +static U8 sClampBuff[ 1024 ]; +static U8* sClamp = sClampBuff + 384; + +static void initLookupTables() +{ + static bool sGenerated = false; + if( !sGenerated ) + { + for( S32 i = 0; i < 256; ++ i ) + { + // Y. + + sRGBY[ i ][ 0 ] = ( 298 * ( i - 16 ) ) >> 8; // B + sRGBY[ i ][ 1 ] = ( 298 * ( i - 16 ) ) >> 8; // G + sRGBY[ i ][ 2 ] = ( 298 * ( i - 16 ) ) >> 8; // R + sRGBY[ i ][ 3 ] = 0xff; // A + + // Cb. + + sRGBCb[ i ][ 0 ] = ( 516 * ( i - 128 ) + 128 ) >> 8; // B + sRGBCb[ i ][ 1 ] = - ( ( 100 * ( i - 128 ) + 128 ) >> 8 ); // G + + // Cr. + + sRGBCr[ i ][ 1 ] = - ( ( 208 * ( i - 128 ) + 128 ) >> 8 ); // B + sRGBCr[ i ][ 2 ] = ( 409 * ( i - 128 ) + 128 ) >> 8; // R + } + + // Setup clamping table for generic transcoder. + + for( S32 i = -384; i < 640; ++ i ) + sClamp[ i ] = mClamp( i, 0, 0xFF ); + + sGenerated = true; + } +} + +static inline S32 sampleG( U8* pCb, U8* pCr ) +{ + return sRGBCr[ *pCr ][ 1 ] + sRGBCr[ *pCb ][ 1 ]; +} + +//============================================================================= +// OggTheoraDecoder. +//============================================================================= + +//----------------------------------------------------------------------------- + +OggTheoraDecoder::OggTheoraDecoder( const ThreadSafeRef< OggInputStream >& stream ) + : Parent( stream ), +#ifdef TORQUE_DEBUG + mLock( 0 ), +#endif + mTheoraSetup( NULL ), + mTheoraDecoder( NULL ), + mTranscoder( TRANSCODER_Auto ) +{ + // Initialize. + + th_info_init( &mTheoraInfo ); + th_comment_init( &mTheoraComment ); + + initLookupTables(); +} + +//----------------------------------------------------------------------------- + +OggTheoraDecoder::~OggTheoraDecoder() +{ + // Free packets on the freelist. + + OggTheoraFrame* packet; + while( mFreePackets.tryPopFront( packet ) ) + destructSingle( packet ); + + // Clean up libtheora structures. + + if( mTheoraDecoder ) + th_decode_free( mTheoraDecoder ); + if( mTheoraSetup ) + th_setup_free( mTheoraSetup ); + + th_comment_clear( &mTheoraComment ); + th_info_clear( &mTheoraInfo ); +} + +//----------------------------------------------------------------------------- + +bool OggTheoraDecoder::_detect( ogg_page* startPage ) +{ + _setStartPage( startPage ); + + // Read first header packet. + + ogg_packet nextPacket; + if( !_readNextPacket( &nextPacket ) + || th_decode_headerin( &mTheoraInfo, &mTheoraComment, &mTheoraSetup, &nextPacket ) < 0 ) + { + th_comment_clear( &mTheoraComment ); + th_info_clear( &mTheoraInfo ); + + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- + +bool OggTheoraDecoder::_init() +{ + ogg_packet nextPacket; + + // Read header packets. + + bool haveTheoraHeader = true; + while( 1 ) + { + if( !_readNextPacket( &nextPacket ) ) + { + haveTheoraHeader = false; + break; + } + + int result = th_decode_headerin( &mTheoraInfo, &mTheoraComment, &mTheoraSetup, &nextPacket ); + if( result < 0 ) + { + haveTheoraHeader = false; + break; + } + else if( result == 0 ) + break; + } + + // Fail if we have no valid and complete Theora header. + + if( !haveTheoraHeader ) + { + th_comment_clear( &mTheoraComment ); + th_info_clear( &mTheoraInfo ); + + Con::errorf( "OggTheoraDecoder::_init() - incorrect or corrupt Theora headers" ); + + return false; + } + + // Init the decoder. + + mTheoraDecoder = th_decode_alloc( &mTheoraInfo, mTheoraSetup ); + + // Feed the first video packet to the decoder. + + ogg_int64_t granulePos; + th_decode_packetin( mTheoraDecoder, &nextPacket, &granulePos ); + + mCurrentFrameTime = th_granule_time( mTheoraDecoder, granulePos ); + mCurrentFrameNumber = 0; + mFrameDuration = 1.f / getFramesPerSecond(); + + // Make sure we have a valid pitch. + + if( !mPacketFormat.mPitch ) + mPacketFormat.mPitch = getFrameWidth() * GFXFormatInfo( mPacketFormat.mFormat ).getBytesPerPixel(); + + return true; +} + +//----------------------------------------------------------------------------- + +bool OggTheoraDecoder::_packetin( ogg_packet* packet ) +{ + ogg_int64_t granulePos; + + if( th_decode_packetin( mTheoraDecoder, packet, &granulePos ) != 0 ) + return false; + + // See if we should drop this frame. + //RDTODO: if we have fallen too far behind, start skipping pages + + F32 granuleTime = th_granule_time( mTheoraDecoder, granulePos ); + mCurrentFrameTime = granuleTime; + mCurrentFrameNumber ++; + + bool dropThisFrame = false; + TimeSourceRef timeSource = mTimeSource; + if( timeSource ) + { + F32 currentTick = F32( timeSource->getPosition() ) / 1000.f; + + if( currentTick >= ( mCurrentFrameTime + mFrameDuration ) ) + dropThisFrame = true; + } + +#ifdef DEBUG_SPEW + Platform::outputDebugString( "[OggTheoraDecoder] new frame %i at %f sec%s", + U32( th_granule_frame( mTheoraDecoder, granulePos ) ), + granuleTime, + dropThisFrame ? " !! DROPPED !!" : "" ); +#endif + + return !dropThisFrame; +} + +//----------------------------------------------------------------------------- + +U32 OggTheoraDecoder::read( OggTheoraFrame** buffer, U32 num ) +{ + #ifdef TORQUE_DEBUG + AssertFatal( dCompareAndSwap( mLock, 0, 1 ), "OggTheoraDecoder::read() - simultaneous reads not thread-safe" ); + #endif + + U32 numRead = 0; + + for( U32 i = 0; i < num; ++ i ) + { + // Read and decode a packet. + + if( !_nextPacket() ) + return numRead; // End of stream. + + // Decode the frame to Y'CbCr. + + th_ycbcr_buffer ycbcr; + th_decode_ycbcr_out( mTheoraDecoder, ycbcr ); + + // Allocate a packet. + + const U32 width = getFrameWidth(); + const U32 height = getFrameHeight(); + + OggTheoraFrame* packet; + if( !mFreePackets.tryPopFront( packet ) ) + packet = constructSingle< OggTheoraFrame* >( mPacketFormat.mPitch * height ); + + packet->mFrameNumber = mCurrentFrameNumber; + packet->mFrameTime = mCurrentFrameTime; + packet->mFrameDuration = mFrameDuration; + + // Transcode the packet. + + #if ( defined( TORQUE_COMPILER_GCC ) || defined( TORQUE_COMPILER_VISUALC ) ) && defined( TORQUE_CPU_X86 ) + + if( ( mTranscoder == TRANSCODER_Auto || mTranscoder == TRANSCODER_SSE2420RGBA ) && + getDecoderPixelFormat() == PIXEL_FORMAT_420 && + Platform::SystemInfo.processor.properties & CPU_PROP_SSE2 && + mPacketFormat.mFormat == GFXFormatR8G8B8A8 && + mTheoraInfo.pic_x == 0 && + mTheoraInfo.pic_y == 0 ) + { + _transcode420toRGBA_SSE2( ycbcr, ( U8* ) packet->data, width, height, mPacketFormat.mPitch ); + } + else + + #endif + + { + // Use generic transcoder. + + _transcode( ycbcr, ( U8* ) packet->data, width, height ); + } + + buffer[ i ] = packet; + ++ numRead; + } + + #ifdef TORQUE_DEBUG + AssertFatal( dCompareAndSwap( mLock, 1, 0 ), "" ); + #endif + + return numRead; +} + +//----------------------------------------------------------------------------- + +void OggTheoraDecoder::_transcode( th_ycbcr_buffer ycbcr, U8* buffer, const U32 width, const U32 height ) +{ + #define ycbcrToRGB( rgb, pY, pCb, pCr, G ) \ + { \ + GFXPackPixel( \ + mPacketFormat.mFormat, \ + rgb, \ + sClamp[ sRGBY[ *pY ][ 2 ] + sRGBCr[ *pCr ][ 2 ] ], \ + sClamp[ sRGBY[ *pY ][ 1 ] + G ], \ + sClamp[ sRGBY[ *pY ][ 0 ] + sRGBCb[ *pCb ][ 0 ] ], \ + 255 \ + ); \ + } + + // Determine number of chroma samples per 4-pixel luma block. + + U32 numChromaSamples = 4; + EPixelFormat pixelFormat = getDecoderPixelFormat(); + if( pixelFormat == PIXEL_FORMAT_422 ) + numChromaSamples = 2; + else if( pixelFormat == OggTheoraDecoder::PIXEL_FORMAT_420 ) + numChromaSamples = 1; + + // Convert and copy the pixels. Deal with all three + // possible plane configurations. + + const U32 pictOffsetY = _getPictureOffset( ycbcr, 0 ); + const U32 pictOffsetU = _getPictureOffset( ycbcr, 1 ); + const U32 pictOffsetV = _getPictureOffset( ycbcr, 2 ); + + for( U32 y = 0; y < height; y += 2 ) + { + U8* dst0 = buffer + y * mPacketFormat.mPitch; + U8* dst1 = dst0 + mPacketFormat.mPitch; + + U8* pY0 = _getPixelPtr( ycbcr, 0, pictOffsetY, 0, y ); + U8* pY1 = _getPixelPtr( ycbcr, 0, pictOffsetY, 0, y + 1 ); + U8* pU0 = _getPixelPtr( ycbcr, 1, pictOffsetU, 0, y ); + U8* pU1 = _getPixelPtr( ycbcr, 1, pictOffsetU, 0, y + 1 ); + U8* pV0 = _getPixelPtr( ycbcr, 2, pictOffsetV, 0, y ); + U8* pV1 = _getPixelPtr( ycbcr, 2, pictOffsetV, 0, y + 1 ); + + for( U32 x = 0; x < width; x += 2 ) + { + // Pixel 0x0. + + S32 G = sampleG( pU0, pV0 ); + + ycbcrToRGB( dst0, pY0, pU0, pV0, G ); + + ++ pY0; + + if( numChromaSamples == 4 ) + { + ++ pU0; + ++ pV0; + } + + // Pixel 0x1. + + if( numChromaSamples == 4 ) + G = sampleG( pU0, pV0 ); + + ycbcrToRGB( dst0, pY0, pU0, pV0, G ); + + ++ pY0; + ++ pU0; + ++ pV0; + + // Pixel 1x0. + + if( numChromaSamples != 1 ) + G = sampleG( pU1, pV1 ); + + ycbcrToRGB( dst1, pY1, pU1, pV1, G ); + + ++ pY1; + + if( numChromaSamples == 4 ) + { + ++ pU1; + ++ pV1; + } + + // Pixel 1x1. + + if( numChromaSamples == 4 ) + G = sampleG( pU1, pV1 ); + + ycbcrToRGB( dst1, pY1, pU1, pV1, G ); + + ++ pY1; + ++ pU1; + ++ pV1; + } + } + + #undef ycbcrToRGB +} + +//----------------------------------------------------------------------------- + +void OggTheoraDecoder::_transcode420toRGBA_SSE2( th_ycbcr_buffer ycbcr, U8* buffer, U32 width, U32 height, U32 pitch ) +{ + AssertFatal( width % 2 == 0, "OggTheoraDecoder::_transcode420toRGBA_SSE2() - width must be multiple of 2" ); + AssertFatal( height % 2 == 0, "OggTheoraDecoder::_transcode420toRGBA_SSE2() - height must be multiple of 2" ); + + unsigned char* ydata = ycbcr[ 0 ].data; + unsigned char* udata = ycbcr[ 1 ].data; + unsigned char* vdata = ycbcr[ 2 ].data; + + S32* ycoeff = ( S32* ) sRGBY; + S32* ucoeff = ( S32* ) sRGBCb; + S32* vcoeff = ( S32* ) sRGBCr; + + // At the end of a line loop, we need to jump over the padding resulting from the difference + // between pitch and width plus jump a whole scanline as we always operate two scanlines + // at a time. + const U32 stride = pitch - width * 4 + pitch; + + // Same thing for the Y channel. + const U32 ystrideDelta = ycbcr[ 0 ].stride - width + ycbcr[ 0 ].stride; + const U32 ypitch = ycbcr[ 0 ].stride; + + // U and V only jump a single scanline so we only need to advance by the padding on the + // right. Both planes are half-size. + const U32 ustrideDelta = ycbcr[ 1 ].stride - width / 2; + const U32 vstrideDelta = ycbcr[ 2 ].stride - width / 2; + + #if defined( TORQUE_COMPILER_VISUALC ) && defined( TORQUE_CPU_X86 ) + + __asm + { + mov ecx,height + + hloop: + + push ecx + mov ecx,width + + wloop: + + push ecx + xor eax,eax + + // Load and accumulate coefficients for U and V in XMM0. + + mov esi,udata + mov ebx,ucoeff + mov edx,ydata + mov al,[esi] + xor ecx,ecx + mov edi,vdata + shl eax,4 + movdqa xmm0,[ebx+eax] + + mov ebx,vcoeff + mov cl,[edi] + mov esi,ycoeff + shl ecx,4 + paddd xmm0,[ebx+ecx] + xor eax,eax + xor ebx,ebx + + // Load coefficients for Y of the four pixels into XMM1-XMM4. + + mov ecx,ypitch + mov al,[edx] + mov bl,[edx+1] + shl eax,4 + shl ebx,4 + movdqa xmm1,[esi+eax] + movdqa xmm2,[esi+ebx] + xor eax,eax + xor ebx,ebx + + mov al,[edx+ecx] + mov bl,[edx+ecx+1] + shl eax,4 + shl ebx,4 + movdqa xmm3,[esi+eax] + movdqa xmm4,[esi+ebx] + + mov edi,buffer + mov ecx,pitch + + // Add Cb and Cr on top of Y. + + paddd xmm1,xmm0 + paddd xmm2,xmm0 + paddd xmm3,xmm0 + paddd xmm4,xmm0 + + // Pack pixels together. We need to pack twice per pixel + // to go from 32bits via 16bits to 8bits. + // + // Right now we're simply packing two garbage pixels for the + // second packing operation. An alternative would be to pack the + // four pixels into one XMM register and then do a packed shuffle + // to split out the lower two pixels before the move. + + packssdw xmm1,xmm2 + packssdw xmm3,xmm4 + packuswb xmm1,xmm6 + packuswb xmm3,xmm7 + + // Store pixels. + + movq qword ptr [edi],xmm1 + movq qword ptr [edi+ecx],xmm3 + + // Loop width. + + pop ecx + + add ydata,2 + inc udata + inc vdata + add buffer,8 + + sub ecx,2 + jnz wloop + + // Loop height. + + pop ecx + + mov ebx,stride + mov eax,ystrideDelta + mov edi,ustrideDelta + mov esi,vstrideDelta + + add buffer,ebx + add ydata,eax + add udata,edi + add vdata,esi + + sub ecx,2 + jnz hloop + }; + + #elif defined( TORQUE_COMPILER_GCC ) && defined( TORQUE_CPU_X86 ) + + asm( "pushal\n" // Save all general-purpose registers. + + "movl %0,%%ecx\n" // Load height into ECX. + + ".hloop_sse:\n" + + "pushl %%ecx\n" // Save counter. + "movl %1,%%ecx\n" // Load width into ECX. + + ".wloop_sse:\n" + + "pushl %%ecx\n" // Save counter. + "xorl %%eax,%%eax\n" // Zero out eax for later use. + + // Load and accumulate coefficients for U and V in XMM0. + + "movl %3,%%esi\n" // Load U pointer into ESI. + "movl %8,%%ebx\n" // Load U coefficient table into EBX. + "movl %2,%%edx\n" // Load Y pointer into EDX. + "movb (%%esi),%%al\n" // Load U into AL. + "xorl %%ecx,%%ecx\n" // Clear ECX. + "movl %4,%%edi\n" // Load V pointer into EDI. + "shll $4,%%eax\n" // Multiply EAX by 16 to index into table. + "movdqa (%%ebx,%%eax),%%xmm0\n" // Load Cb coefficient into XMM0. + + "movl %9,%%ebx\n" // Load V coefficients table into EBX. + "movb (%%edi),%%cl\n" // Load V into CL. + "movl %7,%%esi\n" // Load Y coefficients table into ESI. + "shll $4,%%ecx\n" // Multiply ECX by 16 to index into table. + "paddd (%%ebx,%%ecx),%%xmm0\n" // Add Cr coefficient to Cb coefficient. + "xorl %%eax,%%eax\n" // Clear EAX. + "xorl %%ebx,%%ebx\n" // Clear EBX. + + // Load coefficients for Y of the four pixels into XMM1-XMM4. + + "movl %14,%%ecx\n" // Load Y pitch into ECX (needed later for lower two pixels). + "movb (%%edx),%%al\n" // Load upper-left pixel Y into AL. + "movb 1(%%edx),%%bl\n" // Load upper-right pixel Y into BL. + "shll $4,%%eax\n" // Multiply EAX by 16 to index into table. + "shll $4,%%ebx\n" // Multiply EBX by 16 to index into table. + "movdqa (%%esi,%%eax),%%xmm1\n" // Load coefficient for upper-left pixel Y into XMM1. + "movdqa (%%esi,%%ebx),%%xmm2\n" // Load coefficient for upper-right pixel Y into XMM2. + "xorl %%eax,%%eax\n" // Clear EAX. + "xorl %%ebx,%%ebx\n" // Clear EBX. + + "movb (%%edx,%%ecx),%%al\n" // Load lower-left pixel Y into AL. + "movb 1(%%edx,%%ecx),%%bl\n" // Load lower-right pixel Y into AL. + "shll $4,%%eax\n" // Multiply EAX by 16 to index into table. + "shll $4,%%ebx\n" // Multiply EBX by 16 to index into table. + "movdqa (%%esi,%%eax),%%xmm3\n" // Load coefficient for lower-left pixel Y into XMM3. + "movdqa (%%esi,%%ebx),%%xmm4\n" // Load coefficient for lower-right pixel Y into XMM4. + + "movl %5,%%edi\n" // Load buffer pointer into EDI (for later use). + "movl %6,%%ecx\n" // Load pitch into ECX (for later use). + + // Add Cb and Cr on top of Y. + + "paddd %%xmm0,%%xmm1\n" // Add chroma channels to upper-left pixel. + "paddd %%xmm0,%%xmm2\n" // Add chroma channels to upper-right pixel. + "paddd %%xmm0,%%xmm3\n" // Add chroma channels to lower-left pixel. + "paddd %%xmm0,%%xmm4\n" // Add chroma channels to lower-right pixel. + + // Pack pixels together. We need to pack twice per pixel + // to go from 32bits via 16bits to 8bits. + // + // Right now we're simply packing two garbage pixels for the + // second packing operation. An alternative would be to pack the + // four pixels into one XMM register and then do a packed shuffle + // to split out the lower two pixels before the move. + + "packssdw %%xmm2,%%xmm1\n" // Pack 32bit channels together into 16bit channels on upper two pixels. + "packssdw %%xmm4,%%xmm3\n" // Pack 32bit channels together into 16bit channels on lower two pixels. + "packuswb %%xmm6,%%xmm1\n" // Pack 16bit channels together into 8bit channels on upper two pixels (plus two garbage pixels). + "packuswb %%xmm7,%%xmm3\n" // Pack 16bit channels together into 8bit channels on lower two pixels (plus two garbage pixels). + + // Store pixels. + + "movq %%xmm1,(%%edi)\n" // Store upper two pixels. + "movq %%xmm3,(%%edi,%%ecx)\n" // Store lower two pixels. + + // Loop width. + + "popl %%ecx\n" // Restore width counter. + + "addl $2,%2\n" // Bump Y pointer by two pixels (1 bpp). + "incl %3\n" // Bump U pointer by one pixel (1 bpp). + "incl %4\n" // Bump V pointer by one pixel (1 bpp). + "addl $8,%5\n" // Bump buffer pointer by two pixels (4 bpp). + + "subl $2,%%ecx\n" + "jnz .wloop_sse\n" + + // Loop height. + + "popl %%ecx\n" // Restore height counter. + + "movl %10,%%ebx\n" // Load buffer stride into EBX. + "movl %11,%%eax\n" // Load Y stride delta into EAX. + "movl %12,%%edi\n" // Load U stride delta into EDI. + "movl %13,%%esi\n" // Load V stride delta into ESI. + + "addl %%ebx,%5\n" // Bump buffer pointer by stride delta. + "addl %%eax,%2\n" // Bump Y pointer by stride delta. + "addl %%edi,%3\n" // Bump U pointer by stride delta. + "addl %%esi,%4\n" // Bump V pointer by stride delta. + + "subl $2,%%ecx\n" + "jnz .hloop_sse\n" + + "popal\n" + : + : "m" ( height ), // 0 + "m" ( width ), // 1 + "m" ( ydata ), // 2 + "m" ( udata ), // 3 + "m" ( vdata ), // 4 + "m" ( buffer ), // 5 + "m" ( pitch ), // 6 + "m" ( ycoeff ), // 7 + "m" ( ucoeff ), // 8 + "m" ( vcoeff ), // 9 + "m" ( stride ), // 10 + "m" ( ystrideDelta ), // 11 + "m" ( ustrideDelta ), // 12 + "m" ( vstrideDelta ), // 13 + "m" ( ypitch ) // 14 + ); + + #endif +} diff --git a/Engine/source/core/ogg/oggTheoraDecoder.h b/Engine/source/core/ogg/oggTheoraDecoder.h new file mode 100644 index 000000000..5dc8fab26 --- /dev/null +++ b/Engine/source/core/ogg/oggTheoraDecoder.h @@ -0,0 +1,267 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _OGGTHEORADECODER_H_ +#define _OGGTHEORADECODER_H_ + +#ifndef _OGGINPUTSTREAM_H_ + #include "core/ogg/oggInputStream.h" +#endif +#ifndef _TSTREAM_H_ + #include "core/stream/tStream.h" +#endif +#ifndef _RAWDATA_H_ + #include "core/util/rawData.h" +#endif +#ifndef _GFXENUMS_H_ + #include "gfx/gfxEnums.h" +#endif +#ifndef _THREADSAFEDEQUE_H_ + #include "platform/threads/threadSafeDeque.h" +#endif +#include "theora/theoradec.h" + + +/// A single decoded Theora video frame. +class OggTheoraFrame : public RawData +{ + public: + + typedef RawData Parent; + + OggTheoraFrame() {} + OggTheoraFrame( S8* data, U32 size, bool ownMemory = false ) + : Parent( data, size, ownMemory ) {} + + /// Serial number of this frame in the stream. + U32 mFrameNumber; + + /// Playtime in seconds at which to display this frame. + F32 mFrameTime; + + /// Seconds to display this frame. + F32 mFrameDuration; +}; + + +/// Decodes a Theora substream into frame packets. +/// +/// Frame packets contain raw pixel data in the set pixel format (default is R8G8B8). +/// Reading on a thread is safe, but remember to keep a reference to the OggInputStream +/// master from the worker thread. +class OggTheoraDecoder : public OggDecoder, + public IInputStream< OggTheoraFrame* > +{ + public: + + typedef OggDecoder Parent; + + /// Y'CbCr pixel format of the source video stream. + /// For informational purposes only. Packet out is determined + /// by PacketFormat. + enum EPixelFormat + { + PIXEL_FORMAT_444, // Full Y, full Cb, full Cr. + PIXEL_FORMAT_422, // Full Y, half-width Cb, half-width Cr. + PIXEL_FORMAT_420, // Full Y, half-widht+height Cb, half-width+height Cr. + PIXEL_FORMAT_Unknown + }; + + /// Descriptor for surface format that this stream should + /// decode into. This saves an otherwise potentitally necessary + /// swizzling step. + /// + /// @note The output channel ordering will be in device format, i.e. + /// least-significant first. + struct PacketFormat + { + /// Pixel format. + GFXFormat mFormat; + + /// Bytes per scanline. + U32 mPitch; + + /// Default descriptor sets up for RGB. + PacketFormat() + : mFormat( GFXFormatR8G8B8 ), + mPitch( 0 ) {} + + /// + PacketFormat( GFXFormat format, U32 pitch ) + : mFormat( format ), + mPitch( pitch ) {} + }; + + /// + enum ETranscoder + { + TRANSCODER_Auto, ///< Auto-detect from current formats and processor capabilities. + TRANSCODER_Generic, ///< Generic transcoder that handles all source and target formats; 32bit integer + lookup tables. + TRANSCODER_SSE2420RGBA, ///< SSE2 transcoder with fixed 4:2:0 to RGBA conversion; 32bit integer + lookup tables. + }; + + protected: + + typedef IPositionable< U32 >* TimeSourceRef; + + /// @name libtheora Data + /// @{ + + /// + th_comment mTheoraComment; + + /// + th_info mTheoraInfo; + + /// + th_setup_info* mTheoraSetup; + + /// + th_dec_ctx* mTheoraDecoder; + + /// @} + + /// + PacketFormat mPacketFormat; + + /// + F32 mFrameDuration; + + /// + F32 mCurrentFrameTime; + + /// + U32 mCurrentFrameNumber; + + /// If this is set, the decoder will drop frames that are + /// already outdated with respect to the time source. + /// + /// @note Times are in milliseconds and in video time. + TimeSourceRef mTimeSource; + + /// Transcoder to use for color space conversion. If the current + /// setting is invalid, will fall back to generic. + ETranscoder mTranscoder; + + /// + ThreadSafeDeque< OggTheoraFrame* > mFreePackets; + + #ifdef TORQUE_DEBUG + U32 mLock; + #endif + + /// Generic transcoder going from any of the Y'CbCr pixel formats to + /// any RGB format (that is supported by GFXFormatUtils). + void _transcode( th_ycbcr_buffer ycbcr, U8* buffer, U32 width, U32 height ); + + /// Transcoder with fixed 4:2:0 to RGBA conversion using SSE2 assembly. + void _transcode420toRGBA_SSE2( th_ycbcr_buffer ycbcr, U8* buffer, U32 width, U32 height, U32 pitch ); + + // OggDecoder. + virtual bool _detect( ogg_page* startPage ); + virtual bool _init(); + virtual bool _packetin( ogg_packet* packet ); + + /// + U32 _getPixelOffset( th_ycbcr_buffer buffer, U32 plane, U32 x, U32 y ) const + { + switch( getDecoderPixelFormat() ) + { + case PIXEL_FORMAT_444: break; + case PIXEL_FORMAT_422: if( plane != 0 ) x >>= 1; break; + case PIXEL_FORMAT_420: if( plane != 0 ) { x >>= 1; y >>= 1; } break; + + default: + AssertFatal( false, "OggTheoraDecoder::_getPixelOffset() - invalid pixel format" ); + } + + return ( y * buffer[ plane ].stride + x ); + } + + /// + U8* _getPixelPtr( th_ycbcr_buffer buffer, U32 plane, U32 offset, U32 x, U32 y ) const + { + return ( buffer[ plane ].data + offset + _getPixelOffset( buffer, plane, x, y ) ); + } + + /// + U32 _getPictureOffset( th_ycbcr_buffer buffer, U32 plane ) + { + return _getPixelOffset( buffer, plane, mTheoraInfo.pic_x, mTheoraInfo.pic_y ); + } + + public: + + /// + OggTheoraDecoder( const ThreadSafeRef< OggInputStream >& stream ); + + ~OggTheoraDecoder(); + + /// Return the width of video image frames in pixels. + /// @note This returns the actual picture width rather than Theora's internal encoded frame width. + U32 getFrameWidth() const { return mTheoraInfo.pic_width; } + + /// Return the height of video image frames in pixels. + /// @note This returns the actual picture height rather than Theora's internal encoded frame height. + U32 getFrameHeight() const { return mTheoraInfo.pic_height; } + + /// + F32 getFramesPerSecond() const { return ( F32( mTheoraInfo.fps_numerator ) / F32( mTheoraInfo.fps_denominator ) ); } + + /// + EPixelFormat getDecoderPixelFormat() const + { + switch( mTheoraInfo.pixel_fmt ) + { + case TH_PF_444: return PIXEL_FORMAT_444; + case TH_PF_422: return PIXEL_FORMAT_422; + case TH_PF_420: return PIXEL_FORMAT_420; + default: return PIXEL_FORMAT_Unknown; + } + } + + /// + const PacketFormat& getPacketFormat() const { return mPacketFormat; } + + /// + void setPacketFormat( const PacketFormat& format ) { mPacketFormat = format; } + + /// Set the reference time source. Frames will be dropped if the decoder + /// falls behind the time of this source. + /// + /// @note The time source must have at least the same lifetime as the decoder. + void setTimeSource( const TimeSourceRef& timeSource ) { mTimeSource = timeSource; } + + /// Set the Y'CbCr->RGB transcoder to use. + void setTranscoder( ETranscoder transcoder ) { mTranscoder = transcoder; } + + /// + void reusePacket( OggTheoraFrame* packet ) { mFreePackets.pushBack( packet ); } + + // OggDecoder. + virtual const char* getName() const { return "Theora"; } + + // IInputStream. + virtual U32 read( OggTheoraFrame** buffer, U32 num ); +}; + +#endif // !_OGGTHEORADECODER_H_ diff --git a/Engine/source/core/ogg/oggVorbisDecoder.cpp b/Engine/source/core/ogg/oggVorbisDecoder.cpp new file mode 100644 index 000000000..3df624df3 --- /dev/null +++ b/Engine/source/core/ogg/oggVorbisDecoder.cpp @@ -0,0 +1,195 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/ogg/oggVorbisDecoder.h" +#include "console/console.h" + + +//#define DEBUG_SPEW + + +//----------------------------------------------------------------------------- + +OggVorbisDecoder::OggVorbisDecoder( const ThreadSafeRef< OggInputStream >& stream ) + : Parent( stream ) +#ifdef TORQUE_DEBUG + , mLock( 0 ) +#endif +{ + // Initialize. + + vorbis_info_init( &mVorbisInfo ); + vorbis_comment_init( &mVorbisComment ); + dMemset( &mVorbisBlock, 0, sizeof( mVorbisBlock ) ); + dMemset( &mVorbisDspState, 0, sizeof( mVorbisDspState ) ); +} + +//----------------------------------------------------------------------------- + +OggVorbisDecoder::~OggVorbisDecoder() +{ + vorbis_block_clear( &mVorbisBlock ); + vorbis_dsp_clear( &mVorbisDspState ); + vorbis_info_clear( &mVorbisInfo ); + vorbis_comment_clear( &mVorbisComment ); +} + +//----------------------------------------------------------------------------- + +bool OggVorbisDecoder::_detect( ogg_page* startPage ) +{ + _setStartPage( startPage ); + + // Read initial header packet. + + ogg_packet nextPacket; + if( !_readNextPacket( &nextPacket ) + || vorbis_synthesis_headerin( &mVorbisInfo, &mVorbisComment, &nextPacket ) < 0 ) + { + vorbis_info_clear( &mVorbisInfo ); + vorbis_comment_clear( &mVorbisComment ); + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- + +bool OggVorbisDecoder::_init() +{ + // Read header packets. + + bool haveVorbisHeader = true; + for( U32 i = 0; i < 2; ++ i ) + { + ogg_packet nextPacket; + if( !_readNextPacket( &nextPacket ) ) + { + haveVorbisHeader = false; + break; + } + + int result = vorbis_synthesis_headerin( &mVorbisInfo, &mVorbisComment, &nextPacket ); + if( result != 0 ) + { + haveVorbisHeader = false; + break; + } + } + + // Fail if we don't have a complete and valid Vorbis header. + + if( !haveVorbisHeader ) + { + vorbis_info_clear( &mVorbisInfo ); + vorbis_comment_clear( &mVorbisComment ); + + Con::errorf( "OggVorbisDecoder::_init() - Incorrect or corrupt Vorbis headers" ); + + return false; + } + + // Init synthesis. + + vorbis_synthesis_init( &mVorbisDspState, &mVorbisInfo ); + vorbis_block_init( &mVorbisDspState, &mVorbisBlock ); + + return true; +} + +//----------------------------------------------------------------------------- + +bool OggVorbisDecoder::_packetin( ogg_packet* packet ) +{ + return ( vorbis_synthesis( &mVorbisBlock, packet ) == 0 ); +} + +//----------------------------------------------------------------------------- + +U32 OggVorbisDecoder::read( RawData** buffer, U32 num ) +{ + #ifdef TORQUE_DEBUG + AssertFatal( dCompareAndSwap( mLock, 0, 1 ), "OggVorbisDecoder::read() - simultaneous reads not thread-safe" ); + #endif + + U32 numRead = 0; + + for( U32 i = 0; i < num; ++ i ) + { + float** pcmData; + U32 numSamples; + + // Read sample data. + + while( 1 ) + { + numSamples = vorbis_synthesis_pcmout( &mVorbisDspState, &pcmData ); + if( numSamples ) + break; + else + { + if( !_nextPacket() ) + return numRead; // End of stream. + + vorbis_synthesis_blockin( &mVorbisDspState, &mVorbisBlock ); + } + } + vorbis_synthesis_read( &mVorbisDspState, numSamples ); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[OggVorbisDecoder] read %i samples", numSamples ); + #endif + + // Allocate a packet. + + const U32 numChannels = getNumChannels(); + RawData* packet = constructSingle< RawData* >( numSamples * 2 * numChannels ); // Two bytes per channel. + + // Convert and copy the samples. + + S16* samplePtr = ( S16* ) packet->data; + for( U32 n = 0; n < numSamples; ++ n ) + for( U32 c = 0; c < numChannels; ++ c ) + { + S32 val = S32( pcmData[ c ][ n ] * 32767.f ); + if( val > 32767 ) + val = 32767; + else if( val < -34768 ) + val = -32768; + + *samplePtr = val; + ++ samplePtr; + } + + // Success. + + buffer[ i ] = packet; + numRead ++; + } + + #ifdef TORQUE_DEBUG + AssertFatal( dCompareAndSwap( mLock, 1, 0 ), "" ); + #endif + + return numRead; +} diff --git a/Engine/source/core/ogg/oggVorbisDecoder.h b/Engine/source/core/ogg/oggVorbisDecoder.h new file mode 100644 index 000000000..20ca6c65a --- /dev/null +++ b/Engine/source/core/ogg/oggVorbisDecoder.h @@ -0,0 +1,91 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _OGGVORBISDECODER_H_ +#define _OGGVORBISDECODER_H_ + +#ifndef _OGGINPUTSTREAM_H_ + #include "core/ogg/oggInputStream.h" +#endif +#ifndef _TSTREAM_H_ + #include "core/stream/tStream.h" +#endif +#ifndef _RAWDATA_H_ + #include "core/util/rawData.h" +#endif +#include "vorbis/codec.h" + + +/// Decodes a Vorbis substream into sample packets. +/// +/// Vorbis samples are always 16bits. +class OggVorbisDecoder : public OggDecoder, + public IInputStream< RawData* > +{ + public: + + typedef OggDecoder Parent; + + protected: + + /// + vorbis_info mVorbisInfo; + + /// + vorbis_comment mVorbisComment; + + /// + vorbis_dsp_state mVorbisDspState; + + /// + vorbis_block mVorbisBlock; + + #ifdef TORQUE_DEBUG + U32 mLock; + #endif + + // OggDecoder. + virtual bool _detect( ogg_page* startPage ); + virtual bool _init(); + virtual bool _packetin( ogg_packet* packet ); + + public: + + /// + OggVorbisDecoder( const ThreadSafeRef< OggInputStream >& stream ); + + ~OggVorbisDecoder(); + + /// + U32 getNumChannels() const { return mVorbisInfo.channels; } + + /// + U32 getSamplesPerSecond() const { return mVorbisInfo.rate; } + + // OggDecoder. + virtual const char* getName() const { return "Vorbis"; } + + // IInputStream. + virtual U32 read( RawData** buffer, U32 num ); +}; + +#endif // !_OGGVORBISDECODER_H_ diff --git a/Engine/source/core/resizeStream.cpp b/Engine/source/core/resizeStream.cpp new file mode 100644 index 000000000..eccbfda98 --- /dev/null +++ b/Engine/source/core/resizeStream.cpp @@ -0,0 +1,159 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/resizeStream.h" + +ResizeFilterStream::ResizeFilterStream() + : m_pStream(NULL), + m_startOffset(0), + m_streamLen(0), + m_currOffset(0), + m_lastBytesRead(0) +{ + // +} + +ResizeFilterStream::~ResizeFilterStream() +{ + detachStream(); +} + +bool ResizeFilterStream::attachStream(Stream* io_pSlaveStream) +{ + AssertFatal(io_pSlaveStream != NULL, "NULL Slave stream?"); + + m_pStream = io_pSlaveStream; + m_startOffset = 0; + m_streamLen = m_pStream->getStreamSize(); + m_currOffset = 0; + setStatus(EOS); + return true; +} + +void ResizeFilterStream::detachStream() +{ + m_pStream = NULL; + m_startOffset = 0; + m_streamLen = 0; + m_currOffset = 0; + setStatus(Closed); +} + +Stream* ResizeFilterStream::getStream() +{ + return m_pStream; +} + +bool ResizeFilterStream::setStreamOffset(const U32 in_startOffset, const U32 in_streamLen) +{ + AssertFatal(m_pStream != NULL, "stream not attached!"); + if (m_pStream == NULL) + return false; + + U32 start = in_startOffset; + U32 end = in_startOffset + in_streamLen; + U32 actual = m_pStream->getStreamSize(); + + if (start >= actual || end > actual) + return false; + + m_startOffset = start; + m_streamLen = in_streamLen; + m_currOffset = 0; + + if (m_streamLen != 0) + setStatus(Ok); + else + setStatus(EOS); + + return true; +} + +U32 ResizeFilterStream::getPosition() const +{ + AssertFatal(m_pStream != NULL, "Error, stream not attached"); + if (m_pStream == NULL) + return 0; + + return m_currOffset; +} + +bool ResizeFilterStream::setPosition(const U32 in_newPosition) +{ + AssertFatal(m_pStream != NULL, "Error, stream not attached"); + if (m_pStream == NULL) + return false; + + if (in_newPosition < m_streamLen) { + m_currOffset = in_newPosition; + return true; + } else { + m_currOffset = m_streamLen; + return false; + } +} + +U32 ResizeFilterStream::getStreamSize() +{ + AssertFatal(m_pStream != NULL, "Error, stream not attached"); + + return m_streamLen; +} + +bool ResizeFilterStream::_read(const U32 in_numBytes, void* out_pBuffer) +{ + AssertFatal(m_pStream != NULL, "Error, stream not attached"); + m_lastBytesRead = 0; + + if (in_numBytes == 0) + return true; + + AssertFatal(out_pBuffer != NULL, "Invalid output buffer"); + if (getStatus() == Closed) { + AssertFatal(false, "Attempted read from closed stream"); + return false; + } + + U32 savePosition = m_pStream->getPosition(); + if (m_pStream->setPosition(m_startOffset + m_currOffset) == false) + return false; + + U32 actualSize = in_numBytes; + U32 position = m_startOffset + m_currOffset; + if (in_numBytes + position > m_startOffset + m_streamLen) + actualSize = m_streamLen - (position - m_startOffset); + + if (actualSize == 0) { + setStatus(EOS); + return false; + } + + bool success = m_pStream->read(actualSize, out_pBuffer); + m_currOffset += actualSize; + m_lastBytesRead = actualSize; + + setStatus(m_pStream->getStatus()); + + m_pStream->setPosition(savePosition); + return success; +} + diff --git a/Engine/source/core/resizeStream.h b/Engine/source/core/resizeStream.h new file mode 100644 index 000000000..38875eeff --- /dev/null +++ b/Engine/source/core/resizeStream.h @@ -0,0 +1,69 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _RESIZESTREAM_H_ +#define _RESIZESTREAM_H_ + +//Includes +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#ifndef _FILTERSTREAM_H_ +#include "core/filterStream.h" +#endif + +class ResizeFilterStream : public FilterStream, public IStreamByteCount +{ + typedef FilterStream Parent; + + Stream* m_pStream; + U32 m_startOffset; + U32 m_streamLen; + U32 m_currOffset; + U32 m_lastBytesRead; + + public: + ResizeFilterStream(); + ~ResizeFilterStream(); + + bool attachStream(Stream* io_pSlaveStream); + void detachStream(); + Stream* getStream(); + + bool setStreamOffset(const U32 in_startOffset, + const U32 in_streamLen); + + // Mandatory overrides. + protected: + bool _read(const U32 in_numBytes, void* out_pBuffer); + public: + U32 getPosition() const; + bool setPosition(const U32 in_newPosition); + + U32 getStreamSize(); + + // IStreamByteCount + U32 getLastBytesRead() { return m_lastBytesRead; } + U32 getLastBytesWritten() { return 0; } +}; + +#endif //_RESIZESTREAM_H_ diff --git a/Engine/source/core/resource.cpp b/Engine/source/core/resource.cpp new file mode 100644 index 000000000..226908bb1 --- /dev/null +++ b/Engine/source/core/resource.cpp @@ -0,0 +1,110 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/resourceManager.h" +#include "core/volume.h" + +#include "console/console.h" + + +FreeListChunker ResourceHolderBase::smHolderFactory; + +ResourceBase::Header ResourceBase::smBlank; + + +U32 ResourceBase::Header::getChecksum() const +{ + Torque::FS::FileNodeRef fileRef = Torque::FS::GetFileNode( mPath ); + + if ( fileRef == NULL ) + { + Con::errorf("ResourceBase::getChecksum could not access file: [%s]", mPath.getFullPath().c_str() ); + return 0; + } + + return fileRef->getChecksum(); +} + +void ResourceBase::Header::destroySelf() +{ + if (this == &smBlank) + return; + + if( mNotifyUnload ) + mNotifyUnload( getPath(), getResource() ); + + if ( mResource != NULL ) + { + mResource->~ResourceHolderBase(); + ResourceHolderBase::smHolderFactory.free( mResource ); + } + + ResourceManager::get().remove( this ); + delete this; +} + +void ResourceBase::assign(const ResourceBase &inResource, void* resource) +{ + mResourceHeader = inResource.mResourceHeader; + + if ( mResourceHeader == NULL || mResourceHeader.getPointer() == &(ResourceBase::smBlank) ) + return; + + if (mResourceHeader->getSignature()) + { + AssertFatal(inResource.mResourceHeader->getSignature() == getSignature(),"Resource::assign: mis-matching signature"); + } + else + { + mResourceHeader->mSignature = getSignature(); + + const Torque::Path path = mResourceHeader->getPath(); + + if (resource == NULL) + { + if ( !getStaticLoadSignal().trigger(path, &resource) && (resource != NULL) ) + { + mResourceHeader->mResource = createHolder(resource); + mResourceHeader->mNotifyUnload = _getNotifyUnloadFn(); + _triggerPostLoadSignal(); + return; + } + + resource = create(path); + } + + if (resource) + { + mResourceHeader->mResource = createHolder(resource); + mResourceHeader->mNotifyUnload = _getNotifyUnloadFn(); + _triggerPostLoadSignal(); + } + else + { + // Failed to create...delete signature so we can attempt to successfully create resource later + Con::warnf("Failed to create resource: [%s]", path.getFullPath().c_str() ); + + mResourceHeader->mSignature = 0; + } + } +} + diff --git a/Engine/source/core/resource.h b/Engine/source/core/resource.h new file mode 100644 index 000000000..393e3f372 --- /dev/null +++ b/Engine/source/core/resource.h @@ -0,0 +1,323 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef __RESOURCE_H__ +#define __RESOURCE_H__ + +#ifndef _DATACHUNKER_H_ +#include "core/dataChunker.h" +#endif + +#ifndef _PATH_H_ +#include "core/util/path.h" +#endif + +#ifndef _REFBASE_H_ +#include "core/util/refBase.h" +#endif + +#ifndef _TIMECLASS_H_ +#include "core/util/timeClass.h" +#endif + +#ifndef _TSIGNAL_H_ +#include "core/util/tSignal.h" +#endif + +#ifndef _PLATFORMASSERT_H_ +#include "platform/platformAssert.h" +#endif + +class ResourceManager; + +// This is a utility class used by the resource manager. +// The prime responsibility of this class is to delete +// a resource. The base type is never used, but a template +// version is always derived that knows how to delete the +// particular type of resource. Normally, one needs not +// worry about this class. However, if one wants to delete +// a particular resource in a special manner, one may +// override the ResourceHolder::~ResourceHolder method. +class ResourceHolderBase +{ +public: + static FreeListChunker smHolderFactory; + + virtual ~ResourceHolderBase() {} + + // Return void pointer to resource data. + void *getResource() const { return mRes; } + +protected: + // Construct a resource holder pointing at 'p'. + ResourceHolderBase(void *p) : mRes(p) {} + + void *mRes; +}; + +// All resources are derived from this type. The base type +// is only instantiated by the resource manager +// (the resource manager will return a base resource which a +// derived resource, Resource, will construct itself +// with). Base class handles locking and unlocking and +// provides several virtual functions to be defined by +// derived resource. +class ResourceBase +{ + friend class ResourceManager; + +protected: + class Header; + +public: + typedef U32 Signature; + +public: + ResourceBase(Header *header) { mResourceHeader = (header ? header : &smBlank); } + virtual ~ResourceBase() {} + + const Torque::Path &getPath() const + { + AssertFatal(mResourceHeader != NULL,"ResourceBase::getPath called on invalid resource"); + return mResourceHeader->getPath(); + } + + U32 getChecksum() const + { + AssertFatal(mResourceHeader != NULL,"ResourceBase::getChecksum called on invalid resource"); + return mResourceHeader->getChecksum(); + } + +protected: + + typedef void ( *NotifyUnloadFn )( const Torque::Path& path, void* resource ); + + class Header : public StrongRefBase + { + public: + Header() + : mSignature(0), + mResource(NULL), + mNotifyUnload( NULL ) + { + } + + const Torque::Path &getPath() const { return mPath; } + + Signature getSignature() const { return mSignature; } + void *getResource() const { return (mResource ? mResource->getResource() : NULL); } + U32 getChecksum() const; + + virtual void destroySelf(); + + private: + + friend class ResourceBase; + friend class ResourceManager; + + Signature mSignature; + ResourceHolderBase* mResource; + Torque::Path mPath; + NotifyUnloadFn mNotifyUnload; + }; + +protected: + static Header smBlank; + ResourceBase() : mResourceHeader(&smBlank) {} + + StrongRefPtr
      mResourceHeader; + + void assign(const ResourceBase &inResource, void* resource = NULL); + + // The following functions are virtual, but cannot be pure-virtual + // because we need to be able to instantiate this class. + + // To be defined by derived class. Creates a resource + // holder of the desired type. Resource template handles + // this, so one should never need to override. + virtual ResourceHolderBase *createHolder(void *) + { + AssertFatal(0,"ResourceBase::createHolder: should not be called"); + return NULL; + } + + // Create a Resource of desired type using passed path. Derived + // resource class must define this. + virtual void *create(const Torque::Path &path) + { + AssertFatal(0,"ResourceBase::create: should not be called"); + return NULL; + } + + // Return signature for desired type. + virtual Signature getSignature() const + { + return mResourceHeader->getSignature(); + } + + virtual Signal &getStaticLoadSignal() + { + AssertFatal(0,"ResourceBase::getStaticLoadSignal: should not be called"); + static Signal sLoadSignal; + + return sLoadSignal; + } + + virtual void _triggerPostLoadSignal() {} + virtual NotifyUnloadFn _getNotifyUnloadFn() { return ( NotifyUnloadFn ) NULL; } +}; + +// This is a utility class used by resource manager. Classes derived +// from this template pretty much just know how to delete the template's +// type. +template class ResourceHolder : public ResourceHolderBase +{ +public: + ResourceHolder(T *t) : ResourceHolderBase(t) {} + virtual ~ResourceHolder() { delete ((T*)mRes); } +}; + +// Resource template. When dealing with resources, this is the +// class that will be used. One creates resources by opening or +// creating them via the resource manager. The resource manager +// returns ResourceBases, which can be used to construct any +// type of resource (see the constructors for this template). +// When instantiating classes using this template, it is always +// necessary to define the create and getSignature methods. +// The createMethod will be responsible for loading a resource +// from disk using passed path. +template class Resource : public ResourceBase +{ +public: + Resource() {} + Resource(const ResourceBase &base) { assign(base); } + + void operator=(const ResourceBase & base) { assign(base); } + T* operator->() { return getResource(); } + T& operator*() { return *getResource(); } + operator T*() { return getResource(); } + const T* operator->() const { return getResource(); } + const T& operator*() const { return *getResource(); } + operator const T*() const { return getResource(); } + + void setResource(const ResourceBase & base, void* resource) { assign(base, resource); } + + static Signature signature(); + + /// Registering with this signal will give an opportunity to handle resource + /// creation before calling the create() function. This may be used to handle + /// file extensions differently and allow a more plugin-like approach to + /// adding resources. Using this mechanism, one could, for example, override + /// the default methods for loading DTS without touching the main source. + static Signal &getLoadSignal() + { + static Signal sLoadSignal; + return sLoadSignal; + } + + /// Register with this signal to get notified when resources of this type + /// have been loaded. + static Signal< void( Resource< T >& ) >& getPostLoadSignal() + { + static Signal< void( Resource< T >& ) > sPostLoadSignal; + return sPostLoadSignal; + } + + /// Register with this signal to get notified when resources of this type + /// are about to get unloaded. + static Signal< void( const Torque::Path&, T* ) >& getUnloadSignal() + { + static Signal< void( const Torque::Path&, T* ) > sUnloadSignal; + return sUnloadSignal; + } + +private: + T *getResource() { return (T*)mResourceHeader->getResource(); } + const T *getResource() const { return (T*)mResourceHeader->getResource(); } + + Signature getSignature() const { return Resource::signature(); } + + ResourceHolderBase *createHolder(void *); + + Signal &getStaticLoadSignal() { return getLoadSignal(); } + + static void _notifyUnload( const Torque::Path& path, void* resource ) { getUnloadSignal().trigger( path, ( T* ) resource ); } + + virtual void _triggerPostLoadSignal() { getPostLoadSignal().trigger( *this ); } + virtual NotifyUnloadFn _getNotifyUnloadFn() { return ( NotifyUnloadFn ) &_notifyUnload; } + + // These are to be define by instantiated resources + // No generic version is provided...however, since + // base resources are instantiated by resource manager, + // these are not pure virtuals if undefined (but will assert)... + void *create(const Torque::Path &path); +}; + + +template inline ResourceHolderBase *Resource::createHolder(void *ptr) +{ + ResourceHolder *resHolder = (ResourceHolder*)(ResourceHolderBase::smHolderFactory.alloc()); + + resHolder = constructInPlace(resHolder,(T*)ptr); + + return resHolder; +} + +//----------------------------------------------------------------------------- +// Load Signal Hooks. +//----------------------------------------------------------------------------- + +/// This template may be used to register a load signal as follows: +/// static ResourceRegisterLoadSignal sgAuto( staticLoadFunction ); +template +class ResourceRegisterLoadSignal +{ +public: + ResourceRegisterLoadSignal( Delegate func ) + { + Resource::getLoadSignal().notify( func ); + } +}; + +template< class T > +class ResourceRegisterPostLoadSignal +{ + public: + + ResourceRegisterPostLoadSignal( Delegate< void( Resource< T >& ) > func ) + { + Resource< T >::getPostLoadSignal().notify( func ); + } +}; + +template< class T > +class ResourceRegisterUnloadSignal +{ + public: + + ResourceRegisterUnloadSignal( Delegate< void( const Torque::Path&, T* ) > func ) + { + Resource< T >::getUnloadSignal().notify( func ); + } +}; + +#endif // __RESOURCE_H__ diff --git a/Engine/source/core/resourceManager.cpp b/Engine/source/core/resourceManager.cpp new file mode 100644 index 000000000..336d59243 --- /dev/null +++ b/Engine/source/core/resourceManager.cpp @@ -0,0 +1,246 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/resourceManager.h" + +#include "core/volume.h" +#include "console/console.h" +#include "core/util/autoPtr.h" + +#include "console/engineAPI.h" + +static AutoPtr< ResourceManager > smInstance; + +ResourceManager::ResourceManager() +: mIterSigFilter( U32_MAX ) +{ +} + +ResourceManager::~ResourceManager() +{ + // TODO: Dump resources that have not been released? +} + +ResourceManager &ResourceManager::get() +{ + if ( smInstance.isNull() ) + smInstance = new ResourceManager; + return *smInstance; +} + +ResourceBase ResourceManager::load(const Torque::Path &path) +{ +#ifdef TORQUE_DEBUG_RES_MANAGER + Con::printf( "ResourceManager::load : [%s]", path.getFullPath().c_str() ); +#endif + + ResourceHeaderMap::Iterator iter = mResourceHeaderMap.findOrInsert( path.getFullPath() ); + + ResourceHeaderMap::Pair &pair = *iter; + + if ( pair.value == NULL ) + { + pair.value = new ResourceBase::Header; + + // TODO: This can fail if the file doesn't exist + // at all which is possible. + // + // The problem is the templated design in ResourceManager + // keeps me from checking to see if the resource load failed + // before adding a notification. + // + // IMO the resource manager is overly templateized and + // we should refactor it so that its not so. + // + FS::AddChangeNotification( path, this, &ResourceManager::notifiedFileChanged ); + } + + ResourceBase::Header *header = pair.value; + + if (header->getSignature() == 0) + header->mPath = path; + + return ResourceBase( header ); +} + +ResourceBase ResourceManager::find(const Torque::Path &path) +{ +#ifdef TORQUE_DEBUG_RES_MANAGER + Con::printf( "ResourceManager::find : [%s]", path.getFullPath().c_str() ); +#endif + + ResourceHeaderMap::Iterator iter = mResourceHeaderMap.find( path.getFullPath() ); + + if ( iter == mResourceHeaderMap.end() ) + return ResourceBase(); + + ResourceHeaderMap::Pair &pair = *iter; + + ResourceBase::Header *header = pair.value; + + return ResourceBase(header); +} + +#ifdef TORQUE_DEBUG +void ResourceManager::dumpToConsole() +{ + const U32 numResources = mResourceHeaderMap.size(); + + if ( numResources == 0 ) + { + Con::printf( "ResourceManager is not managing any resources" ); + return; + } + + Con::printf( "ResourceManager is managing %d resources:", numResources ); + Con::printf( " [ref count/signature/path]" ); + + ResourceHeaderMap::Iterator iter; + + for( iter = mResourceHeaderMap.begin(); iter != mResourceHeaderMap.end(); ++iter ) + { + ResourceBase::Header *header = (*iter).value; + + char fourCC[ 5 ]; + *( ( U32* ) fourCC ) = header->getSignature(); + fourCC[ 4 ] = 0; + + Con::printf( " %3d %s [%s] ", header->getRefCount(), fourCC, (*iter).key.c_str() ); + } +} +#endif + +bool ResourceManager::remove( ResourceBase::Header* header ) +{ + const Path &path = header->getPath(); + +#ifdef TORQUE_DEBUG_RES_MANAGER + Con::printf( "ResourceManager::remove : [%s]", path.getFullPath().c_str() ); +#endif + + ResourceHeaderMap::Iterator iter = mResourceHeaderMap.find( path.getFullPath() ); + if ( iter != mResourceHeaderMap.end() && iter->value == header ) + { + AssertISV( header && (header->getRefCount() == 0), "ResourceManager error: trying to remove resource which is still in use." ); + mResourceHeaderMap.erase( iter ); + } + else + { + iter = mPrevResourceHeaderMap.find( path.getFullPath() ); + if ( iter == mPrevResourceHeaderMap.end() || iter->value != header ) + { + Con::errorf( "ResourceManager::remove : Trying to remove non-existent resource [%s]", path.getFullPath().c_str() ); + return false; + } + + AssertISV( header && (header->getRefCount() == 0), "ResourceManager error: trying to remove resource which is still in use." ); + mPrevResourceHeaderMap.erase( iter ); + } + + FS::RemoveChangeNotification( path, this, &ResourceManager::notifiedFileChanged ); + + return true; +} + +void ResourceManager::notifiedFileChanged( const Torque::Path &path ) +{ + reloadResource( path, true ); +} + +void ResourceManager::reloadResource( const Torque::Path &path, bool showMessage ) +{ + if ( showMessage ) + Con::warnf( "[ResourceManager::notifiedFileChanged] : File changed [%s]", path.getFullPath().c_str() ); + + ResourceHeaderMap::Iterator iter = mResourceHeaderMap.find( path.getFullPath() ); + if ( iter != mResourceHeaderMap.end() ) + { + ResourceBase::Header *header = (*iter).value; + mResourceHeaderMap.erase( iter ); + + // Move the resource into the previous resource map. + iter = mPrevResourceHeaderMap.findOrInsert( path ); + iter->value = header; + } + + // Now notify users of the resource change so they + // can release and reload. + mChangeSignal.trigger( path ); +} + +ResourceBase ResourceManager::startResourceList( ResourceBase::Signature inSignature ) +{ + mIter = mResourceHeaderMap.begin(); + + mIterSigFilter = inSignature; + + return nextResource(); +} + +ResourceBase ResourceManager::nextResource() +{ + ResourceBase::Header *header = NULL; + + while( mIter != mResourceHeaderMap.end() ) + { + header = (*mIter).value; + + ++mIter; + + if ( mIterSigFilter == U32_MAX ) + return ResourceBase(header); + + if ( header->getSignature() == mIterSigFilter ) + return ResourceBase(header); + } + + return ResourceBase(); +} + +ConsoleFunctionGroupBegin(ResourceManagerFunctions, "Resource management functions."); + +#ifdef TORQUE_DEBUG +ConsoleFunction(resourceDump, void, 1, 1, "()" + "@brief List the currently managed resources\n\n" + "Currently used by editors only, internal\n" + "@ingroup Editors\n" + "@internal") +{ + ResourceManager::get().dumpToConsole(); +} +#endif + +DefineEngineFunction( reloadResource, void, ( const char* path ),, + "Force the resource at specified input path to be reloaded\n" + "@param path Path to the resource to be reloaded\n\n" + "@tsexample\n" + "reloadResource( \"art/shapes/box.dts\" );\n" + "@endtsexample\n\n" + "@note Currently used by editors only\n" + "@ingroup Editors\n" + "@internal") +{ + ResourceManager::get().reloadResource( path ); +} + +ConsoleFunctionGroupEnd( ResourceManagerFunctions ); diff --git a/Engine/source/core/resourceManager.h b/Engine/source/core/resourceManager.h new file mode 100644 index 000000000..ea9aa1dc2 --- /dev/null +++ b/Engine/source/core/resourceManager.h @@ -0,0 +1,91 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _RESOURCEMANAGER_H_ +#define _RESOURCEMANAGER_H_ + +#ifndef __RESOURCE_H__ +#include "core/resource.h" +#endif + +#ifndef _TDICTIONARY_H_ +#include "core/util/tDictionary.h" +#endif + +using namespace Torque; + +class ResourceManager +{ +public: + + static ResourceManager &get(); + + ResourceBase load(const Torque::Path &path); + ResourceBase find(const Torque::Path &path); + + ResourceBase startResourceList( ResourceBase::Signature inSignature = U32_MAX ); + ResourceBase nextResource(); + + void reloadResource( const Torque::Path &path, bool showMessage = false ); + + typedef Signal ChangedSignal; + + /// Registering with this signal will give an opportunity to handle a change to the + /// resource on disk. For example, if a PNG file is edited by the artist and saved + /// the ResourceManager will signal that the file has changed so the TextureManager + /// may act appropriately - which probably means to re-init the materials using that PNG. + /// The signal passes the Resource's signature so the callee may filter these. + ChangedSignal &getChangedSignal() { return mChangeSignal; } + +#ifdef TORQUE_DEBUG + void dumpToConsole(); +#endif + + ~ResourceManager(); + +protected: + + friend class ResourceBase::Header; + + ResourceManager(); + + bool remove( ResourceBase::Header* header ); + + void notifiedFileChanged( const Torque::Path &path ); + + typedef HashTable ResourceHeaderMap; + + /// The map of resources. + ResourceHeaderMap mResourceHeaderMap; + + /// The map of old resources which have been replaced by + /// new resources from a file change notification. + ResourceHeaderMap mPrevResourceHeaderMap; + + ResourceHeaderMap::Iterator mIter; + + U32 mIterSigFilter; + + ChangedSignal mChangeSignal; +}; + +#endif diff --git a/Engine/source/core/stream/bitStream.cpp b/Engine/source/core/stream/bitStream.cpp new file mode 100644 index 000000000..6080eee94 --- /dev/null +++ b/Engine/source/core/stream/bitStream.cpp @@ -0,0 +1,1142 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/stream/bitStream.h" + +#include "core/strings/stringFunctions.h" +#include "math/mathIO.h" +#include "console/consoleObject.h" +#include "platform/platformNet.h" +#include "core/bitVector.h" + + +static BitStream gPacketStream(NULL, 0); +static U8 gPacketBuffer[Net::MaxPacketDataSize]; + +// bitstream utility functions + +void BitStream::clearStringBuffer() +{ + static char stringBuf[256]; + stringBuf[0] = 0; +// setStringBuffer( stringBuf ); +} + +void BitStream::setStringBuffer(char buffer[256]) +{ +// stringBuffer = buffer; +} + +BitStream *BitStream::getPacketStream(U32 writeSize) +{ + if(!writeSize) + writeSize = Net::MaxPacketDataSize; + + gPacketStream.setBuffer(gPacketBuffer, writeSize, Net::MaxPacketDataSize); + gPacketStream.setPosition(0); + + return &gPacketStream; +} + +void BitStream::sendPacketStream(const NetAddress *addr) +{ + Net::sendto(addr, gPacketStream.getBuffer(), gPacketStream.getPosition()); +} + +// CodeReview WTF is this additional IsEqual? - BJG, 3/29/07 + +inline bool IsEqual(F32 a, F32 b) { return a == b; } + +ResizeBitStream::ResizeBitStream(U32 minSpace, U32 initialSize) : BitStream(NULL, 0, 0) +{ + mMinSpace = minSpace; + if(!initialSize) + initialSize = minSpace * 2; + U8 *buf = (U8 *) dMalloc(initialSize); + setBuffer(buf, initialSize, initialSize); +} + +ResizeBitStream::~ResizeBitStream() +{ + dFree(dataPtr); +} + +void ResizeBitStream::validate() +{ + if(getPosition() + mMinSpace > bufSize) + { + bufSize = getPosition() + mMinSpace * 2; + dataPtr = (U8 *) dRealloc(dataPtr, bufSize); + + maxReadBitNum = bufSize << 3; + maxWriteBitNum = bufSize << 3; + } +} + + +class HuffmanProcessor +{ + static const U32 csm_charFreqs[256]; + bool m_tablesBuilt; + + void buildTables(); + + struct HuffNode { + U32 pop; + + S16 index0; + S16 index1; + }; + struct HuffLeaf { + U32 pop; + + U8 numBits; + U8 symbol; + U32 code; // no code should be longer than 32 bits. + }; + // We have to be a bit careful with these, mSince they are pointers... + struct HuffWrap { + HuffNode* pNode; + HuffLeaf* pLeaf; + + public: + HuffWrap() : pNode(NULL), pLeaf(NULL) { } + + void set(HuffLeaf* in_leaf) { pNode = NULL; pLeaf = in_leaf; } + void set(HuffNode* in_node) { pLeaf = NULL; pNode = in_node; } + + U32 getPop() { if (pNode) return pNode->pop; else return pLeaf->pop; } + }; + + Vector m_huffNodes; + Vector m_huffLeaves; + + S16 determineIndex(HuffWrap&); + + void generateCodes(BitStream&, S32, S32); + + public: + HuffmanProcessor() : m_tablesBuilt(false) { } + + static HuffmanProcessor g_huffProcessor; + + bool readHuffBuffer(BitStream* pStream, char* out_pBuffer); + bool writeHuffBuffer(BitStream* pStream, const char* out_pBuffer, S32 maxLen); +}; + +HuffmanProcessor HuffmanProcessor::g_huffProcessor; + +void BitStream::setBuffer(void *bufPtr, S32 size, S32 maxSize) +{ + dataPtr = (U8 *) bufPtr; + bitNum = 0; + bufSize = size; + maxReadBitNum = size << 3; + if(maxSize < 0) + maxSize = size; + maxWriteBitNum = maxSize << 3; + error = false; + clearCompressionPoint(); +} + +U32 BitStream::getPosition() const +{ + return (bitNum + 7) >> 3; +} + + +bool BitStream::setPosition(const U32 pos) +{ + bitNum = pos << 3; + return (true); +} + +U32 BitStream::getStreamSize() +{ + return bufSize; +} + +U8 *BitStream::getBytePtr() +{ + return dataPtr + getPosition(); +} + + +U32 BitStream::getReadByteSize() +{ + return (maxReadBitNum >> 3) - getPosition(); +} + +U32 BitStream::getWriteByteSize() +{ + return (maxWriteBitNum >> 3) - getPosition(); +} + +void BitStream::clear() +{ + dMemset(dataPtr, 0, bufSize); +} + +void BitStream::writeClassId(U32 classId, U32 classType, U32 classGroup) +{ + AssertFatal(classType < NetClassTypesCount, "Out of range class type."); + AssertFatal(classGroup < NetClassGroupsCount, "Out of range class group."); + AssertFatal(classId < AbstractClassRep::NetClassCount[classGroup][classType], "Out of range class id."); + AssertFatal(AbstractClassRep::NetClassCount[classGroup][classType] < (1 << AbstractClassRep::NetClassBitSize[classGroup][classType]), + "NetClassBitSize too small!"); + + writeInt(classId, AbstractClassRep::NetClassBitSize[classGroup][classType]); +} + +S32 BitStream::readClassId(U32 classType, U32 classGroup) +{ + AssertFatal(classType < NetClassTypesCount, "Out of range class type."); + AssertFatal(classGroup < NetClassGroupsCount, "Out of range class group."); + AssertFatal(AbstractClassRep::NetClassCount[classGroup][classType] < (1 << AbstractClassRep::NetClassBitSize[classGroup][classType]), + "NetClassBitSize too small!"); + + S32 ret = readInt(AbstractClassRep::NetClassBitSize[classGroup][classType]); + + AssertFatal(ret < AbstractClassRep::NetClassCount[classGroup][classType], "BitStream::readClassId - unexpected class ID!"); + if(ret >= AbstractClassRep::NetClassCount[classGroup][classType]) + return -1; + return ret; +} + +void BitStream::writeBits(S32 bitCount, const void *bitPtr) +{ + if(!bitCount) + return; + + if(bitCount + bitNum > maxWriteBitNum) + { + error = true; + AssertFatal(false, "Out of range write"); + return; + } + + // [tom, 8/17/2006] This is probably a lot lamer then it needs to be. However, + // at least it doesnt clobber data or overrun the buffer like the old code did. + const U8 *ptr = (U8 *)bitPtr; + + for(S32 srcBitNum = 0;srcBitNum < bitCount;srcBitNum++) + { + if((*(ptr + (srcBitNum >> 3)) & (1 << (srcBitNum & 0x7))) != 0) + *(dataPtr + (bitNum >> 3)) |= (1 << (bitNum & 0x7)); + else + *(dataPtr + (bitNum >> 3)) &= ~(1 << (bitNum & 0x7)); + bitNum++; + } +} + +void BitStream::setBit(S32 bitCount, bool set) +{ + if(set) + *(dataPtr + (bitCount >> 3)) |= (1 << (bitCount & 0x7)); + else + *(dataPtr + (bitCount >> 3)) &= ~(1 << (bitCount & 0x7)); +} + +bool BitStream::testBit(S32 bitCount) +{ + return (*(dataPtr + (bitCount >> 3)) & (1 << (bitCount & 0x7))) != 0; +} + +bool BitStream::writeFlag(bool val) +{ + if(bitNum + 1 > maxWriteBitNum) + { + error = true; + AssertFatal(false, "Out of range write"); + return false; + } + if(val) + *(dataPtr + (bitNum >> 3)) |= (1 << (bitNum & 0x7)); + else + *(dataPtr + (bitNum >> 3)) &= ~(1 << (bitNum & 0x7)); + bitNum++; + return (val); +} + +void BitStream::readBits(S32 bitCount, void *bitPtr) +{ + if(!bitCount) + return; + if(bitCount + bitNum > maxReadBitNum) + { + error = true; + //AssertFatal(false, "Out of range read"); + AssertWarn(false, "Out of range read"); + return; + } + U8 *stPtr = dataPtr + (bitNum >> 3); + S32 byteCount = (bitCount + 7) >> 3; + + U8 *ptr = (U8 *) bitPtr; + + S32 downShift = bitNum & 0x7; + S32 upShift = 8 - downShift; + + U8 curB = *stPtr; + const U8 *stEnd = dataPtr + bufSize; + while(byteCount--) + { + stPtr++; + U8 nextB = stPtr < stEnd ? *stPtr : 0; + *ptr++ = (curB >> downShift) | (nextB << upShift); + curB = nextB; + } + + bitNum += bitCount; +} + +bool BitStream::_read(U32 size, void *dataPtr) +{ + readBits(size << 3, dataPtr); + return true; +} + +bool BitStream::_write(U32 size, const void *dataPtr) +{ + writeBits(size << 3, dataPtr); + return true; +} + +S32 BitStream::readInt(S32 bitCount) +{ + S32 ret = 0; + readBits(bitCount, &ret); + ret = convertLEndianToHost(ret); + if(bitCount == 32) + return ret; + else + ret &= (1 << bitCount) - 1; + return ret; +} + +void BitStream::writeInt(S32 val, S32 bitCount) +{ + val = convertHostToLEndian(val); + writeBits(bitCount, &val); +} + +void BitStream::writeFloat(F32 f, S32 bitCount) +{ + writeInt((S32)(f * ((1 << bitCount) - 1)), bitCount); +} + +F32 BitStream::readFloat(S32 bitCount) +{ + return readInt(bitCount) / F32((1 << bitCount) - 1); +} + +void BitStream::writeSignedFloat(F32 f, S32 bitCount) +{ + writeInt((S32)(((f + 1) * .5) * ((1 << bitCount) - 1)), bitCount); +} + +F32 BitStream::readSignedFloat(S32 bitCount) +{ + return readInt(bitCount) * 2 / F32((1 << bitCount) - 1) - 1.0f; +} + +void BitStream::writeSignedInt(S32 value, S32 bitCount) +{ + if(writeFlag(value < 0)) + writeInt(-value, bitCount - 1); + else + writeInt(value, bitCount - 1); +} + +S32 BitStream::readSignedInt(S32 bitCount) +{ + if(readFlag()) + return -readInt(bitCount - 1); + else + return readInt(bitCount - 1); +} + +void BitStream::writeNormalVector(const Point3F& vec, S32 bitCount) +{ + F32 phi = mAtan2(vec.x, vec.y) / M_PI; + F32 theta = mAtan2(vec.z, mSqrt(vec.x*vec.x + vec.y*vec.y)) / (M_PI/2.0); + + writeSignedFloat(phi, bitCount+1); + writeSignedFloat(theta, bitCount); +} + +void BitStream::readNormalVector(Point3F *vec, S32 bitCount) +{ + F32 phi = readSignedFloat(bitCount+1) * M_PI; + F32 theta = readSignedFloat(bitCount) * (M_PI/2.0); + + vec->x = mSin(phi)*mCos(theta); + vec->y = mCos(phi)*mCos(theta); + vec->z = mSin(theta); +} + +Point3F BitStream::dumbDownNormal(const Point3F& vec, S32 bitCount) +{ + U8 buffer[128]; + BitStream temp(buffer, 128); + + temp.writeNormalVector(vec, bitCount); + temp.setCurPos(0); + + Point3F ret; + temp.readNormalVector(&ret, bitCount); + return ret; +} + +void BitStream::writeVector( Point3F vec, F32 maxMag, S32 magBits, S32 normalBits ) +{ + F32 mag = vec.len(); + + // If its zero length then we're done. + if ( !writeFlag( mag > 0.0f ) ) + return; + + // Write the magnitude compressed unless its greater than the maximum. + if ( writeFlag( mag < maxMag ) ) + writeFloat( mag / maxMag, magBits ); + else + write( mag ); + + // Finally write the normal part. + vec *= 1.0f / mag; + writeNormalVector( vec, normalBits ); +} + +void BitStream::readVector( Point3F *outVec, F32 maxMag, S32 magBits, S32 normalBits ) +{ + // Nothing more to do if we got a zero length vector. + if ( !readFlag() ) + { + outVec->set(0,0,0); + return; + } + + // Read the compressed or uncompressed magnitude. + F32 mag; + if ( readFlag() ) + mag = readFloat( magBits ) * maxMag; + else + read( &mag ); + + // Finally read the normal and reconstruct the vector. + readNormalVector( outVec, normalBits ); + *outVec *= mag; +} + +void BitStream::writeAffineTransform(const MatrixF& matrix) +{ +// AssertFatal(matrix.isAffine() == true, +// "BitStream::writeAffineTransform: Error, must write only affine transforms!"); + + Point3F pos; + matrix.getColumn(3, &pos); + mathWrite(*this, pos); + + QuatF q(matrix); + q.normalize(); + write(q.x); + write(q.y); + write(q.z); + writeFlag(q.w < 0.0); +} + +void BitStream::readAffineTransform(MatrixF* matrix) +{ + Point3F pos; + QuatF q; + + mathRead(*this, &pos); + read(&q.x); + read(&q.y); + read(&q.z); + q.w = mSqrt(1.0 - getMin(F32(((q.x * q.x) + (q.y * q.y) + (q.z * q.z))), 1.f)); + if (readFlag()) + q.w = -q.w; + + q.setMatrix(matrix); + matrix->setColumn(3, pos); +// AssertFatal(matrix->isAffine() == true, +// "BitStream::readAffineTransform: Error, transform should be affine after this function!"); +} + +void BitStream::writeQuat( const QuatF& quat, U32 bitCount ) +{ + writeSignedFloat( quat.x, bitCount ); + writeSignedFloat( quat.y, bitCount ); + writeSignedFloat( quat.z, bitCount ); + writeFlag( quat.w < 0.0f ); +} + +void BitStream::readQuat( QuatF *outQuat, U32 bitCount ) +{ + outQuat->x = readSignedFloat( bitCount ); + outQuat->y = readSignedFloat( bitCount ); + outQuat->z = readSignedFloat( bitCount ); + + outQuat->w = mSqrt( 1.0 - getMin( mSquared( outQuat->x ) + + mSquared( outQuat->y ) + + mSquared( outQuat->z ), + 1.0f ) ); + if ( readFlag() ) + outQuat->w = -outQuat->w; +} + +void BitStream::writeBits( const BitVector &bitvec ) +{ + U32 size = bitvec.getSize(); + if ( writeFlag( size <= 127 ) ) + writeInt( size, 7 ); + else + write( size ); + + writeBits( bitvec.getSize(), bitvec.getBits() ); +} + +void BitStream::readBits( BitVector *bitvec ) +{ + U32 size; + if ( readFlag() ) // size <= 127 + size = readInt( 7 ); + else + read( &size ); + + bitvec->setSize( size ); + readBits( size, bitvec->getBits() ); +} + +//---------------------------------------------------------------------------- + +void BitStream::clearCompressionPoint() +{ + mCompressPoint.set(0,0,0); +} + +void BitStream::setCompressionPoint(const Point3F& p) +{ + mCompressPoint = p; +} + +static U32 gBitCounts[4] = { + 16, 18, 20, 32 +}; + +void BitStream::writeCompressedPoint(const Point3F& p,F32 scale) +{ + // Same # of bits for all axis + Point3F vec; + F32 invScale = 1 / scale; + U32 type; + vec = p - mCompressPoint; + F32 dist = vec.len() * invScale; + if(dist < (1 << 15)) + type = 0; + else if(dist < (1 << 17)) + type = 1; + else if(dist < (1 << 19)) + type = 2; + else + type = 3; + + writeInt(type, 2); + + if (type != 3) + { + type = gBitCounts[type]; + writeSignedInt(S32(vec.x * invScale + 0.5f),type); + writeSignedInt(S32(vec.y * invScale + 0.5f),type); + writeSignedInt(S32(vec.z * invScale + 0.5f),type); + } + else + { + write(p.x); + write(p.y); + write(p.z); + } +} + +void BitStream::readCompressedPoint(Point3F* p,F32 scale) +{ + // Same # of bits for all axis + U32 type = readInt(2); + + if(type == 3) + { + read(&p->x); + read(&p->y); + read(&p->z); + } + else + { + type = gBitCounts[type]; + p->x = (F32)readSignedInt(type); + p->y = (F32)readSignedInt(type); + p->z = (F32)readSignedInt(type); + + p->x = mCompressPoint.x + p->x * scale; + p->y = mCompressPoint.y + p->y * scale; + p->z = mCompressPoint.z + p->z * scale; + } +} + +//------------------------------------------------------------------------------ + +InfiniteBitStream::InfiniteBitStream() +{ + // +} + +InfiniteBitStream::~InfiniteBitStream() +{ + // +} + +void InfiniteBitStream::reset() +{ + // Rewing back to beginning + setPosition(0); +} + +void InfiniteBitStream::validate(U32 upcomingBytes) +{ + if(getPosition() + upcomingBytes + mMinSpace > bufSize) + { + bufSize = getPosition() + upcomingBytes + mMinSpace; + dataPtr = (U8 *) dRealloc(dataPtr, bufSize); + + maxReadBitNum = bufSize << 3; + maxWriteBitNum = bufSize << 3; + } +} + +void InfiniteBitStream::compact() +{ + // Prepare to copy... + U32 oldSize = bufSize; + U8 *tmp = (U8*)dMalloc(bufSize); + + // Copy things... + bufSize = getPosition() + mMinSpace * 2; + dMemcpy(tmp, dataPtr, oldSize); + + // And clean up. + dFree(dataPtr); + dataPtr = tmp; + + maxReadBitNum = bufSize << 3; + maxWriteBitNum = bufSize << 3; +} + +void InfiniteBitStream::writeToStream(Stream &s) +{ + s.write(getPosition(), dataPtr); +} + +//------------------------------------------------------------------------------ + +void BitStream::readString(char buf[256]) +{ + if(stringBuffer) + { + if(readFlag()) + { + S32 offset = readInt(8); + HuffmanProcessor::g_huffProcessor.readHuffBuffer(this, stringBuffer + offset); + dStrcpy(buf, stringBuffer); + return; + } + } + HuffmanProcessor::g_huffProcessor.readHuffBuffer(this, buf); + if(stringBuffer) + dStrcpy(stringBuffer, buf); +} + +void BitStream::writeString(const char *string, S32 maxLen) +{ + if(!string) + string = ""; + if(stringBuffer) + { + S32 j; + for(j = 0; j < maxLen && stringBuffer[j] == string[j] && string[j];j++) + ; + dStrncpy(stringBuffer, string, maxLen); + stringBuffer[maxLen] = 0; + + if(writeFlag(j > 2)) + { + writeInt(j, 8); + HuffmanProcessor::g_huffProcessor.writeHuffBuffer(this, string + j, maxLen - j); + return; + } + } + HuffmanProcessor::g_huffProcessor.writeHuffBuffer(this, string, maxLen); +} + +void HuffmanProcessor::buildTables() +{ + AssertFatal(m_tablesBuilt == false, "Cannot build tables twice!"); + m_tablesBuilt = true; + + S32 i; + + // First, construct the array of wraps... + // + m_huffLeaves.setSize(256); + m_huffNodes.reserve(256); + m_huffNodes.increment(); + for (i = 0; i < 256; i++) { + HuffLeaf& rLeaf = m_huffLeaves[i]; + + rLeaf.pop = csm_charFreqs[i] + 1; + rLeaf.symbol = U8(i); + + dMemset(&rLeaf.code, 0, sizeof(rLeaf.code)); + rLeaf.numBits = 0; + } + + S32 currWraps = 256; + HuffWrap* pWrap = new HuffWrap[256]; + for (i = 0; i < 256; i++) { + pWrap[i].set(&m_huffLeaves[i]); + } + + while (currWraps != 1) { + U32 min1 = 0xfffffffe, min2 = 0xffffffff; + S32 index1 = -1, index2 = -1; + + for (i = 0; i < currWraps; i++) { + if (pWrap[i].getPop() < min1) { + min2 = min1; + index2 = index1; + + min1 = pWrap[i].getPop(); + index1 = i; + } else if (pWrap[i].getPop() < min2) { + min2 = pWrap[i].getPop(); + index2 = i; + } + } + AssertFatal(index1 != -1 && index2 != -1 && index1 != index2, "hrph"); + + // Create a node for this... + m_huffNodes.increment(); + HuffNode& rNode = m_huffNodes.last(); + rNode.pop = pWrap[index1].getPop() + pWrap[index2].getPop(); + rNode.index0 = determineIndex(pWrap[index1]); + rNode.index1 = determineIndex(pWrap[index2]); + + S32 mergeIndex = index1 > index2 ? index2 : index1; + S32 nukeIndex = index1 > index2 ? index1 : index2; + pWrap[mergeIndex].set(&rNode); + + if (index2 != (currWraps - 1)) { + pWrap[nukeIndex] = pWrap[currWraps - 1]; + } + currWraps--; + } + AssertFatal(currWraps == 1, "wrong wraps?"); + AssertFatal(pWrap[0].pNode != NULL && pWrap[0].pLeaf == NULL, "Wrong wrap type!"); + + // Ok, now we have one wrap, which is a node. we need to make sure that this + // is the first node in the node list. + m_huffNodes[0] = *(pWrap[0].pNode); + delete [] pWrap; + + U32 code = 0; + BitStream bs(&code, 4); + + generateCodes(bs, 0, 0); +} + +void HuffmanProcessor::generateCodes(BitStream& rBS, S32 index, S32 depth) +{ + if (index < 0) { + // leaf node, copy the code in, and back out... + HuffLeaf& rLeaf = m_huffLeaves[-(index + 1)]; + + dMemcpy(&rLeaf.code, rBS.dataPtr, sizeof(rLeaf.code)); + rLeaf.numBits = depth; + } else { + HuffNode& rNode = m_huffNodes[index]; + + S32 pos = rBS.getCurPos(); + + rBS.writeFlag(false); + generateCodes(rBS, rNode.index0, depth + 1); + + rBS.setCurPos(pos); + rBS.writeFlag(true); + generateCodes(rBS, rNode.index1, depth + 1); + + rBS.setCurPos(pos); + } +} + +S16 HuffmanProcessor::determineIndex(HuffWrap& rWrap) +{ + if (rWrap.pLeaf != NULL) { + AssertFatal(rWrap.pNode == NULL, "Got a non-NULL pNode in a HuffWrap with a non-NULL leaf."); + + return -((rWrap.pLeaf - m_huffLeaves.address()) + 1); + } else { + AssertFatal(rWrap.pNode != NULL, "Got a NULL pNode in a HuffWrap with a NULL leaf."); + + return rWrap.pNode - m_huffNodes.address(); + } +} + +bool HuffmanProcessor::readHuffBuffer(BitStream* pStream, char* out_pBuffer) +{ + if (m_tablesBuilt == false) + buildTables(); + + if (pStream->readFlag()) { + S32 len = pStream->readInt(8); + for (S32 i = 0; i < len; i++) { + S32 index = 0; + while (true) { + if (index >= 0) { + if (pStream->readFlag() == true) { + index = m_huffNodes[index].index1; + } else { + index = m_huffNodes[index].index0; + } + } else { + out_pBuffer[i] = m_huffLeaves[-(index+1)].symbol; + break; + } + } + } + out_pBuffer[len] = '\0'; + return true; + } else { + // Uncompressed string... + U32 len = pStream->readInt(8); + pStream->read(len, out_pBuffer); + out_pBuffer[len] = '\0'; + return true; + } +} + +bool HuffmanProcessor::writeHuffBuffer(BitStream* pStream, const char* out_pBuffer, S32 maxLen) +{ + if (out_pBuffer == NULL) { + pStream->writeFlag(false); + pStream->writeInt(0, 8); + return true; + } + + if (m_tablesBuilt == false) + buildTables(); + + S32 len = out_pBuffer ? dStrlen(out_pBuffer) : 0; + AssertWarn(len <= 255, "String TOO long for writeString"); + AssertWarn(len <= 255, out_pBuffer); + if (len > maxLen) + len = maxLen; + + S32 numBits = 0; + S32 i; + for (i = 0; i < len; i++) + numBits += m_huffLeaves[(unsigned char)out_pBuffer[i]].numBits; + + if (numBits >= (len * 8)) { + pStream->writeFlag(false); + pStream->writeInt(len, 8); + pStream->write(len, out_pBuffer); + } else { + pStream->writeFlag(true); + pStream->writeInt(len, 8); + for (i = 0; i < len; i++) { + HuffLeaf& rLeaf = m_huffLeaves[((unsigned char)out_pBuffer[i])]; + pStream->writeBits(rLeaf.numBits, &rLeaf.code); + } + } + + return true; +} + +const U32 HuffmanProcessor::csm_charFreqs[256] = { +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +329 , +21 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +2809 , +68 , +0 , +27 , +0 , +58 , +3 , +62 , +4 , +7 , +0 , +0 , +15 , +65 , +554 , +3 , +394 , +404 , +189 , +117 , +30 , +51 , +27 , +15 , +34 , +32 , +80 , +1 , +142 , +3 , +142 , +39 , +0 , +144 , +125 , +44 , +122 , +275 , +70 , +135 , +61 , +127 , +8 , +12 , +113 , +246 , +122 , +36 , +185 , +1 , +149 , +309 , +335 , +12 , +11 , +14 , +54 , +151 , +0 , +0 , +2 , +0 , +0 , +211 , +0 , +2090 , +344 , +736 , +993 , +2872 , +701 , +605 , +646 , +1552 , +328 , +305 , +1240 , +735 , +1533 , +1713 , +562 , +3 , +1775 , +1149 , +1469 , +979 , +407 , +553 , +59 , +279 , +31 , +0 , +0 , +0 , +68 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 , +0 +}; + diff --git a/Engine/source/core/stream/bitStream.h b/Engine/source/core/stream/bitStream.h new file mode 100644 index 000000000..7f6fe82ca --- /dev/null +++ b/Engine/source/core/stream/bitStream.h @@ -0,0 +1,385 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _BITSTREAM_H_ +#define _BITSTREAM_H_ + +#ifndef _STREAM_H_ +#include "core/stream/stream.h" +#endif +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _CRC_H_ +#include "core/crc.h" +#endif + +//-------------------------------------- Some caveats when using this class: +// - Get/setPosition semantics are changed +// to indicate bit position rather than +// byte position. +// + +class Point3F; +class MatrixF; +class HuffmanProcessor; +class BitVector; +class QuatF; + +class BitStream : public Stream +{ +protected: + U8 *dataPtr; + S32 bitNum; + S32 bufSize; + bool error; + S32 maxReadBitNum; + S32 maxWriteBitNum; + char *stringBuffer; + Point3F mCompressPoint; + + friend class HuffmanProcessor; +public: + static BitStream *getPacketStream(U32 writeSize = 0); + static void sendPacketStream(const NetAddress *addr); + + void setBuffer(void *bufPtr, S32 bufSize, S32 maxSize = 0); + U8* getBuffer() { return dataPtr; } + U8* getBytePtr(); + + U32 getReadByteSize(); + U32 getWriteByteSize(); + + S32 getCurPos() const; + void setCurPos(const U32); + + // HACK: We reverted BitStream to this previous version + // because it was crashing the build. + // + // These are just here so that we don't have to revert + // the changes from the rest of the code. + // + // 9/11/2008 - Tom Spilman + // + S32 getBitPosition() const { return getCurPos(); } + void clearStringBuffer(); + + BitStream(void *bufPtr, S32 bufSize, S32 maxWriteSize = -1) { setBuffer(bufPtr, bufSize,maxWriteSize); stringBuffer = NULL; } + void clear(); + + void setStringBuffer(char buffer[256]); + void writeInt(S32 value, S32 bitCount); + S32 readInt(S32 bitCount); + + /// Use this method to write out values in a concise but ass backwards way... + /// Good for values you expect to be frequently zero, often small. Worst case + /// this will bloat values by nearly 20% (5 extra bits!) Best case you'll get + /// one bit (if it's zero). + /// + /// This is not so much for efficiency's sake, as to make life painful for + /// people that want to reverse engineer our network or file formats. + void writeCussedU32(U32 val) + { + // Is it zero? + if(writeFlag(val == 0)) + return; + + if(writeFlag(val <= 0xF)) // 4 bit + writeRangedU32(val, 0, 0xF); + else if(writeFlag(val <= 0xFF)) // 8 bit + writeRangedU32(val, 0, 0xFF); + else if(writeFlag(val <= 0xFFFF)) // 16 bit + writeRangedU32(val, 0, 0xFFFF); + else if(writeFlag(val <= 0xFFFFFF)) // 24 bit + writeRangedU32(val, 0, 0xFFFFFF); + else + writeRangedU32(val, 0, 0xFFFFFFFF); + } + + U32 readCussedU32() + { + if(readFlag()) + return 0; + + if(readFlag()) + return readRangedU32(0, 0xF); + else if(readFlag()) + return readRangedU32(0, 0xFF); + else if(readFlag()) + return readRangedU32(0, 0xFFFF); + else if(readFlag()) + return readRangedU32(0, 0xFFFFFF); + else + return readRangedU32(0, 0xFFFFFFFF); + } + + void writeSignedInt(S32 value, S32 bitCount); + S32 readSignedInt(S32 bitCount); + + void writeRangedU32(U32 value, U32 rangeStart, U32 rangeEnd); + U32 readRangedU32(U32 rangeStart, U32 rangeEnd); + + /// Writes a clamped signed integer to the stream using + /// an optimal amount of bits for the range. + void writeRangedS32( S32 value, S32 min, S32 max ); + + /// Reads a ranged signed integer written with writeRangedS32. + S32 readRangedS32( S32 min, S32 max ); + + // read and write floats... floats are 0 to 1 inclusive, signed floats are -1 to 1 inclusive + + F32 readFloat(S32 bitCount); + F32 readSignedFloat(S32 bitCount); + + void writeFloat(F32 f, S32 bitCount); + void writeSignedFloat(F32 f, S32 bitCount); + + /// Writes a clamped floating point value to the + /// stream with the desired bits of precision. + void writeRangedF32( F32 value, F32 min, F32 max, U32 numBits ); + + /// Reads a ranged floating point value written with writeRangedF32. + F32 readRangedF32( F32 min, F32 max, U32 numBits ); + + void writeClassId(U32 classId, U32 classType, U32 classGroup); + S32 readClassId(U32 classType, U32 classGroup); // returns -1 if the class type is out of range + + // writes a normalized vector + void writeNormalVector(const Point3F& vec, S32 bitCount); + void readNormalVector(Point3F *vec, S32 bitCount); + + void clearCompressionPoint(); + void setCompressionPoint(const Point3F& p); + + // Matching calls to these compression methods must, of course, + // have matching scale values. + void writeCompressedPoint(const Point3F& p,F32 scale = 0.001f); + void readCompressedPoint(Point3F* p,F32 scale = 0.001f); + + // Uses the above method to reduce the precision of a normal vector so the server can + // determine exactly what is on the client. (Pre-dumbing the vector before sending + // to the client can result in precision errors...) + static Point3F dumbDownNormal(const Point3F& vec, S32 bitCount); + + /// Writes a compressed vector as separate magnitude and + /// normal components. The final space used depends on the + /// content of the vector. + /// + /// - 1 bit is used to skip over zero length vectors. + /// - 1 bit is used to mark if the magnitude exceeds max. + /// - The magnitude as: + /// a. magBits if less than maxMag. + /// b. a full 32bit value if greater than maxMag. + /// - The normal as a phi and theta sized normalBits+1 and normalBits. + /// + void writeVector( Point3F vec, F32 maxMag, S32 magBits, S32 normalBits ); + + /// Reads a compressed vector. + /// @see writeVector + void readVector( Point3F *outVec, F32 maxMag, S32 magBits, S32 normalBits ); + + // writes an affine transform (full precision version) + void writeAffineTransform(const MatrixF&); + void readAffineTransform(MatrixF*); + + /// Writes a quaternion in a lossy compressed format that + /// is ( bitCount * 3 ) + 1 bits in size. + /// + /// @param quat The normalized quaternion to write. + /// @param bitCount The the storage space for the xyz component of + /// the quaternion. + /// + void writeQuat( const QuatF& quat, U32 bitCount = 9 ); + + /// Reads a quaternion written with writeQuat. + /// + /// @param quat The normalized quaternion to write. + /// @param bitCount The the storage space for the xyz component of + /// the quaternion. Must match the bitCount at write. + /// @see writeQuat + /// + void readQuat( QuatF *outQuat, U32 bitCount = 9 ); + + virtual void writeBits(S32 bitCount, const void *bitPtr); + virtual void readBits(S32 bitCount, void *bitPtr); + virtual bool writeFlag(bool val); + + inline bool writeFlag(U32 val) + { + return writeFlag(val != 0); + } + + inline bool writeFlag(void *val) + { + return writeFlag(val != 0); + } + + virtual bool readFlag(); + + void writeBits(const BitVector &bitvec); + void readBits(BitVector *bitvec); + + void setBit(S32 bitCount, bool set); + bool testBit(S32 bitCount); + + bool isFull() { return bitNum > (bufSize << 3); } + bool isValid() { return !error; } + + bool _read (const U32 size,void* d); + bool _write(const U32 size,const void* d); + + void readString(char stringBuf[256]); + void writeString(const char *stringBuf, S32 maxLen=255); + + bool hasCapability(const Capability) const { return true; } + U32 getPosition() const; + bool setPosition(const U32 in_newPosition); + U32 getStreamSize(); +}; + +class ResizeBitStream : public BitStream +{ +protected: + U32 mMinSpace; +public: + ResizeBitStream(U32 minSpace = 1500, U32 initialSize = 0); + void validate(); + ~ResizeBitStream(); +}; + +/// This class acts to provide an "infinitely extending" stream. +/// +/// Basically, it does what ResizeBitStream does, but it validates +/// on every write op, so that you never have to worry about overwriting +/// the buffer. +class InfiniteBitStream : public ResizeBitStream +{ +public: + InfiniteBitStream(); + ~InfiniteBitStream(); + + /// Ensure we have space for at least upcomingBytes more bytes in the stream. + void validate(U32 upcomingBytes); + + /// Reset the stream to zero length (but don't clean memory). + void reset(); + + /// Shrink the buffer down to match the actual size of the data. + void compact(); + + /// Write us out to a stream... Results in last byte getting padded! + void writeToStream(Stream &s); + + virtual void writeBits(S32 bitCount, const void *bitPtr) + { + validate((bitCount >> 3) + 1); // Add a little safety. + BitStream::writeBits(bitCount, bitPtr); + } + + virtual bool writeFlag(bool val) + { + validate(1); // One bit will at most grow our buffer by a byte. + return BitStream::writeFlag(val); + } + + const U32 getCRC() + { + // This could be kinda inefficient - BJG + return CRC::calculateCRC(getBuffer(), getStreamSize()); + } +}; + +//------------------------------------------------------------------------------ +//-------------------------------------- INLINES +// +inline S32 BitStream::getCurPos() const +{ + return bitNum; +} + +inline void BitStream::setCurPos(const U32 in_position) +{ + AssertFatal(in_position < (U32)(bufSize << 3), "Out of range bitposition"); + bitNum = S32(in_position); +} + +inline bool BitStream::readFlag() +{ + if(bitNum > maxReadBitNum) + { + error = true; + AssertFatal(false, "Out of range read"); + return false; + } + S32 mask = 1 << (bitNum & 0x7); + bool ret = (*(dataPtr + (bitNum >> 3)) & mask) != 0; + bitNum++; + return ret; +} + +inline void BitStream::writeRangedU32(U32 value, U32 rangeStart, U32 rangeEnd) +{ + AssertFatal(value >= rangeStart && value <= rangeEnd, "Out of bounds value!"); + AssertFatal(rangeEnd >= rangeStart, "error, end of range less than start"); + + U32 rangeSize = rangeEnd - rangeStart + 1; + U32 rangeBits = getBinLog2(getNextPow2(rangeSize)); + + writeInt(S32(value - rangeStart), S32(rangeBits)); +} + +inline U32 BitStream::readRangedU32(U32 rangeStart, U32 rangeEnd) +{ + AssertFatal(rangeEnd >= rangeStart, "error, end of range less than start"); + + U32 rangeSize = rangeEnd - rangeStart + 1; + U32 rangeBits = getBinLog2(getNextPow2(rangeSize)); + + U32 val = U32(readInt(S32(rangeBits))); + return val + rangeStart; +} + +inline void BitStream::writeRangedS32( S32 value, S32 min, S32 max ) +{ + value = mClamp( value, min, max ); + writeRangedU32( ( value - min ), 0, ( max - min ) ); +} + +inline S32 BitStream::readRangedS32( S32 min, S32 max ) +{ + return readRangedU32( 0, ( max - min ) ) + min; +} + +inline void BitStream::writeRangedF32( F32 value, F32 min, F32 max, U32 numBits ) +{ + value = ( mClampF( value, min, max ) - min ) / ( max - min ); + writeInt( (S32)mFloor(value * F32( (1 << numBits) - 1 )), numBits ); +} + +inline F32 BitStream::readRangedF32( F32 min, F32 max, U32 numBits ) +{ + F32 value = (F32)readInt( numBits ); + value /= F32( ( 1 << numBits ) - 1 ); + return min + value * ( max - min ); +} + +#endif //_BITSTREAM_H_ diff --git a/Engine/source/core/stream/fileStream.cpp b/Engine/source/core/stream/fileStream.cpp new file mode 100644 index 000000000..b65221513 --- /dev/null +++ b/Engine/source/core/stream/fileStream.cpp @@ -0,0 +1,579 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/stream/fileStream.h" + + +//----------------------------------------------------------------------------- +// FileStream methods... +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +FileStream::FileStream() +{ + // initialize the file stream + init(); +} + +FileStream *FileStream::createAndOpen(const String &inFileName, Torque::FS::File::AccessMode inMode) +{ + FileStream *newStream = new FileStream; + + if ( newStream ) + { + bool success = newStream->open( inFileName, inMode ); + + if ( !success ) + { + delete newStream; + newStream = NULL; + } + } + + return newStream; +} + +//----------------------------------------------------------------------------- +FileStream::~FileStream() +{ + // make sure the file stream is closed + close(); +} + +//----------------------------------------------------------------------------- +bool FileStream::hasCapability(const Capability i_cap) const +{ + return(0 != (U32(i_cap) & mStreamCaps)); +} + +//----------------------------------------------------------------------------- +U32 FileStream::getPosition() const +{ + AssertFatal(0 != mStreamCaps, "FileStream::getPosition: the stream isn't open"); + //AssertFatal(true == hasCapability(StreamPosition), "FileStream::getPosition(): lacks positioning capability"); + + // return the position inside the buffer if its valid, otherwise return the underlying file position + return((BUFFER_INVALID != mBuffHead) ? mBuffPos : mFile->getPosition()); +} + +//----------------------------------------------------------------------------- +bool FileStream::setPosition(const U32 i_newPosition) +{ + AssertFatal(0 != mStreamCaps, "FileStream::setPosition: the stream isn't open"); + AssertFatal(hasCapability(StreamPosition), "FileStream::setPosition: lacks positioning capability"); + + // if the buffer is valid, test the new position against the bounds of the buffer + if ((BUFFER_INVALID != mBuffHead) && (i_newPosition >= mBuffHead) && (i_newPosition <= mBuffTail)) + { + // set the position and return + mBuffPos = i_newPosition; + + // FIXME [tom, 9/5/2006] This needs to be checked. Basically, when seeking within + // the buffer, if the stream has an EOS status before the seek then if you try to + // read immediately after seeking, you'll incorrectly get an EOS. + // + // I am not 100% sure if this fix is correct, but it seems to be working for the undo system. + if(mBuffPos < mBuffTail) + Stream::setStatus(Ok); + + return(true); + } + // otherwise the new position lies in some block not in memory + else + { + if (mDirty) + flush(); + + clearBuffer(); + + mFile->setPosition(i_newPosition, Torque::FS::File::Begin); + + setStatus(); + + if (mFile->getStatus() == Torque::FS::FileNode::EndOfFile) + mEOF = true; + + return(Ok == getStatus() || EOS == getStatus()); + } +} + +//----------------------------------------------------------------------------- +U32 FileStream::getStreamSize() +{ + AssertWarn(0 != mStreamCaps, "FileStream::getStreamSize: the stream isn't open"); + AssertFatal((BUFFER_INVALID != mBuffHead && true == mDirty) || false == mDirty, "FileStream::getStreamSize: buffer must be valid if its dirty"); + + // the stream size may not match the size on-disk if its been written to... + if (mDirty) + return(getMax((U32)(mFile->getSize()), mBuffTail + 1)); ///<@todo U64 vs U32 issue + // otherwise just get the size on disk... + else + return(mFile->getSize()); +} + +//----------------------------------------------------------------------------- +bool FileStream::open(const String &inFileName, Torque::FS::File::AccessMode inMode) +{ + AssertWarn(0 == mStreamCaps, "FileStream::setPosition: the stream is already open"); + AssertFatal(inFileName.isNotEmpty(), "FileStream::open: empty filename"); + + // make sure the file stream's state is clean + clearBuffer(); + + Torque::Path filePath(inFileName); + + // IF we are writing, make sure the path exists + if( inMode == Torque::FS::File::Write || inMode == Torque::FS::File::WriteAppend || inMode == Torque::FS::File::ReadWrite ) + Torque::FS::CreatePath(filePath); + + mFile = Torque::FS::OpenFile(filePath, inMode); + + if (mFile != NULL) + { + setStatus(); + switch (inMode) + { + case Torque::FS::File::Read: + mStreamCaps = U32(StreamRead) | + U32(StreamPosition); + break; + case Torque::FS::File::Write: + case Torque::FS::File::WriteAppend: + mStreamCaps = U32(StreamWrite) | + U32(StreamPosition); + break; + case Torque::FS::File::ReadWrite: + mStreamCaps = U32(StreamRead) | + U32(StreamWrite) | + U32(StreamPosition); + break; + default: + AssertFatal(false, String::ToString( "FileStream::open: bad access mode on %s", inFileName.c_str() )); + } + } + else + { + Stream::setStatus(IOError); + return(false); + } + + return getStatus() == Ok; +} + +//----------------------------------------------------------------------------- +void FileStream::close() +{ + if (getStatus() == Closed) + return; + + if (mFile != NULL ) + { + // make sure nothing in the buffer differs from what is on disk + if (mDirty) + flush(); + + // and close the file + mFile->close(); + + AssertFatal(mFile->getStatus() == Torque::FS::FileNode::Closed, "FileStream::close: close failed"); + + mFile = NULL; + } + + // clear the file stream's state + init(); +} + +//----------------------------------------------------------------------------- +bool FileStream::flush() +{ + AssertWarn(0 != mStreamCaps, "FileStream::flush: the stream isn't open"); + AssertFatal(false == mDirty || BUFFER_INVALID != mBuffHead, "FileStream::flush: buffer must be valid if its dirty"); + + // if the buffer is dirty + if (mDirty) + { + AssertFatal(hasCapability(StreamWrite), "FileStream::flush: a buffer without write-capability should never be dirty"); + + // align the file pointer to the buffer head + if (mBuffHead != mFile->getPosition()) + { + mFile->setPosition(mBuffHead, Torque::FS::File::Begin); + if (mFile->getStatus() != Torque::FS::FileNode::Open && mFile->getStatus() != Torque::FS::FileNode::EndOfFile) + return(false); + } + + // write contents of the buffer to disk + U32 blockHead; + calcBlockHead(mBuffHead, &blockHead); + mFile->write((char *)mBuffer + (mBuffHead - blockHead), mBuffTail - mBuffHead + 1); + // and update the file stream's state + setStatus(); + if (EOS == getStatus()) + mEOF = true; + + if (Ok == getStatus() || EOS == getStatus()) + // and update the status of the buffer + mDirty = false; + else + return(false); + } + return(true); +} + +//----------------------------------------------------------------------------- +bool FileStream::_read(const U32 i_numBytes, void *o_pBuffer) +{ + AssertFatal(0 != mStreamCaps, "FileStream::_read: the stream isn't open"); + AssertFatal(NULL != o_pBuffer || i_numBytes == 0, "FileStream::_read: NULL destination pointer with non-zero read request"); + + if (!hasCapability(Stream::StreamRead)) + { + AssertFatal(false, "FileStream::_read: file stream lacks capability"); + Stream::setStatus(IllegalCall); + return(false); + } + + // exit on pre-existing errors + if (Ok != getStatus()) + return(false); + + // if a request of non-zero length was made + if (0 != i_numBytes) + { + U8 *pDst = (U8 *)o_pBuffer; + U32 readSize; + U32 remaining = i_numBytes; + U32 bytesRead; + U32 blockHead; + U32 blockTail; + + // check if the buffer has some data in it + if (BUFFER_INVALID != mBuffHead) + { + // copy as much as possible from the buffer into the destination + readSize = ((mBuffTail + 1) >= mBuffPos) ? (mBuffTail + 1 - mBuffPos) : 0; + readSize = getMin(readSize, remaining); + calcBlockHead(mBuffPos, &blockHead); + dMemcpy(pDst, mBuffer + (mBuffPos - blockHead), readSize); + // reduce the remaining amount to read + remaining -= readSize; + // advance the buffer pointers + mBuffPos += readSize; + pDst += readSize; + + if (mBuffPos > mBuffTail && remaining != 0) + { + flush(); + mBuffHead = BUFFER_INVALID; + if (mEOF == true) + Stream::setStatus(EOS); + } + } + + // if the request wasn't satisfied by the buffer and the file has more data + if (false == mEOF && 0 < remaining) + { + // flush the buffer if its dirty, since we now need to go to disk + if (true == mDirty) + flush(); + + // make sure we know the current read location in the underlying file + mBuffPos = mFile->getPosition(); + calcBlockBounds(mBuffPos, &blockHead, &blockTail); + + // check if the data to be read falls within a single block + if ((mBuffPos + remaining) <= blockTail) + { + // fill the buffer from disk + if (true == fillBuffer(mBuffPos)) + { + // copy as much as possible from the buffer to the destination + remaining = getMin(remaining, mBuffTail - mBuffPos + 1); + dMemcpy(pDst, mBuffer + (mBuffPos - blockHead), remaining); + // advance the buffer pointer + mBuffPos += remaining; + } + else + return(false); + } + // otherwise the remaining spans multiple blocks + else + { + clearBuffer(); + // read from disk directly into the destination + bytesRead = mFile->read((char *)pDst, remaining); + setStatus(); + // check to make sure we read as much as expected + if (Ok == getStatus() || EOS == getStatus()) + { + // if not, update the end-of-file status + if (0 != bytesRead && EOS == getStatus()) + { + Stream::setStatus(Ok); + mEOF = true; + } + } + else + return(false); + } + } + } + return(true); +} + +//----------------------------------------------------------------------------- +bool FileStream::_write(const U32 i_numBytes, const void *i_pBuffer) +{ + AssertFatal(0 != mStreamCaps, "FileStream::_write: the stream isn't open"); + AssertFatal(NULL != i_pBuffer || i_numBytes == 0, "FileStream::_write: NULL source buffer pointer on non-zero write request"); + + if (!hasCapability(Stream::StreamWrite)) + { + AssertFatal(false, "FileStream::_write: file stream lacks capability"); + Stream::setStatus(IllegalCall); + return(false); + } + + // exit on pre-existing errors + if (Ok != getStatus() && EOS != getStatus()) + return(false); + + // if a request of non-zero length was made + if (0 != i_numBytes) + { + U8 *pSrc = (U8 *)i_pBuffer; + U32 writeSize; + U32 remaining = i_numBytes; + U32 bytesWrit; + U32 blockHead; + U32 blockTail; + + // check if the buffer is valid + if (BUFFER_INVALID != mBuffHead) + { + // copy as much as possible from the source to the buffer + calcBlockBounds(mBuffHead, &blockHead, &blockTail); + writeSize = (mBuffPos > blockTail) ? 0 : blockTail - mBuffPos + 1; + writeSize = getMin(writeSize, remaining); + + AssertFatal(0 == writeSize || (mBuffPos - blockHead) < BUFFER_SIZE, "FileStream::_write: out of bounds buffer position"); + dMemcpy(mBuffer + (mBuffPos - blockHead), pSrc, writeSize); + // reduce the remaining amount to be written + remaining -= writeSize; + // advance the buffer pointers + mBuffPos += writeSize; + mBuffTail = getMax(mBuffTail, mBuffPos - 1); + pSrc += writeSize; + // mark the buffer dirty + if (0 < writeSize) + mDirty = true; + } + + // if the request wasn't satisfied by the buffer + if (0 < remaining) + { + // flush the buffer if its dirty, since we now need to go to disk + if (mDirty) + flush(); + + // make sure we know the current write location in the underlying file + mBuffPos = mFile->getPosition(); + calcBlockBounds(mBuffPos, &blockHead, &blockTail); + + // check if the data to be written falls within a single block + if ((mBuffPos + remaining) <= blockTail) + { + // write the data to the buffer + dMemcpy(mBuffer + (mBuffPos - blockHead), pSrc, remaining); + // update the buffer pointers + mBuffHead = mBuffPos; + mBuffPos += remaining; + mBuffTail = mBuffPos - 1; + // mark the buffer dirty + mDirty = true; + } + // otherwise the remaining spans multiple blocks + else + { + clearBuffer(); + // write to disk directly from the source + bytesWrit = mFile->write((char *)pSrc, remaining); + setStatus(); + return(Ok == getStatus() || EOS == getStatus()); + } + } + } + return(true); +} + +//----------------------------------------------------------------------------- +void FileStream::init() +{ + mStreamCaps = 0; + Stream::setStatus(Closed); + clearBuffer(); +} + +//----------------------------------------------------------------------------- +bool FileStream::fillBuffer(const U32 i_startPosition) +{ + AssertFatal(0 != mStreamCaps, "FileStream::fillBuffer: the stream isn't open"); + AssertFatal(false == mDirty, "FileStream::fillBuffer: buffer must be clean to fill"); + + // make sure start position and file pointer jive + if (i_startPosition != mFile->getPosition()) + { + mFile->setPosition(i_startPosition, Torque::FS::File::Begin); + if (mFile->getStatus() != Torque::FS::FileNode::Open && mFile->getStatus() != Torque::FS::FileNode::EndOfFile) + { + setStatus(); + return(false); + } + else + // update buffer pointer + mBuffPos = i_startPosition; + } + + // check if file pointer is at end-of-file + if (EOS == getStatus()) + { + // invalidate the buffer + mBuffHead = BUFFER_INVALID; + // set the status to end-of-stream + mEOF = true; + } + // otherwise + else + { + U32 blockHead; + // locate bounds of buffer containing current position + calcBlockHead(mBuffPos, &blockHead); + // read as much as possible from input file + U32 bytesRead = mFile->read((char *)mBuffer + (i_startPosition - blockHead), BUFFER_SIZE - (i_startPosition - blockHead)); + setStatus(); + if (Ok == getStatus() || EOS == getStatus()) + { + // update buffer pointers + mBuffHead = i_startPosition; + mBuffPos = i_startPosition; + mBuffTail = i_startPosition + bytesRead - 1; + // update end-of-file status + if (0 != bytesRead && EOS == getStatus()) + { + Stream::setStatus(Ok); + mEOF = true; + } + } + else + { + mBuffHead = BUFFER_INVALID; + return(false); + } + } + return(true); +} + +//----------------------------------------------------------------------------- +void FileStream::clearBuffer() +{ + mBuffHead = BUFFER_INVALID; + mBuffPos = 0; + mBuffTail = 0; + mDirty = false; + mEOF = false; +} + +//----------------------------------------------------------------------------- +void FileStream::calcBlockHead(const U32 i_position, U32 *o_blockHead) +{ + AssertFatal(NULL != o_blockHead, "FileStream::calcBlockHead: NULL pointer passed for block head"); + + *o_blockHead = i_position/BUFFER_SIZE * BUFFER_SIZE; +} + +//----------------------------------------------------------------------------- +void FileStream::calcBlockBounds(const U32 i_position, U32 *o_blockHead, U32 *o_blockTail) +{ + AssertFatal(NULL != o_blockHead, "FileStream::calcBlockBounds: NULL pointer passed for block head"); + AssertFatal(NULL != o_blockTail, "FileStream::calcBlockBounds: NULL pointer passed for block tail"); + + *o_blockHead = i_position/BUFFER_SIZE * BUFFER_SIZE; + *o_blockTail = *o_blockHead + BUFFER_SIZE - 1; +} + +//----------------------------------------------------------------------------- +void FileStream::setStatus() +{ + switch (mFile->getStatus()) + { + case Torque::FS::FileNode::Open: + Stream::setStatus(Ok); + break; + + case Torque::FS::FileNode::Closed: + Stream::setStatus(Closed); + break; + + case Torque::FS::FileNode::EndOfFile: + Stream::setStatus(EOS); + break; + + case Torque::FS::FileNode::FileSystemFull: + case Torque::FS::FileNode::NoSuchFile: + case Torque::FS::FileNode::AccessDenied: + case Torque::FS::FileNode::NoDisk: + case Torque::FS::FileNode::SharingViolation: + Stream::setStatus(IOError); + break; + + case Torque::FS::FileNode::IllegalCall: + Stream::setStatus(IllegalCall); + break; + + case Torque::FS::FileNode::UnknownError: + Stream::setStatus(UnknownError); + break; + + default: + AssertFatal(false, "FileStream::setStatus: invalid error mode"); + } +} + +FileStream* FileStream::clone() const +{ + Torque::FS::File::AccessMode mode; + if( hasCapability( StreamWrite ) && hasCapability( StreamRead ) ) + mode = Torque::FS::File::ReadWrite; + else if( hasCapability( StreamWrite ) ) + mode = Torque::FS::File::Write; + else + mode = Torque::FS::File::Read; + + FileStream* copy = createAndOpen( mFile->getName(), mode ); + if( copy && copy->setPosition( getPosition() ) ) + return copy; + + delete copy; + return NULL; +} diff --git a/Engine/source/core/stream/fileStream.h b/Engine/source/core/stream/fileStream.h new file mode 100644 index 000000000..826747e08 --- /dev/null +++ b/Engine/source/core/stream/fileStream.h @@ -0,0 +1,93 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FILESTREAM_H_ +#define _FILESTREAM_H_ + +#ifndef _VOLUME_H_ +#include "core/volume.h" +#endif +#ifndef _STREAM_H_ +#include "core/stream/stream.h" +#endif + +class FileStream : public Stream +{ +public: + enum + { + BUFFER_SIZE = 8 * 1024, // this can be changed to anything appropriate [in k] + BUFFER_INVALID = 0xffffffff // file offsets must all be less than this + }; + +public: + FileStream(); // default constructor + virtual ~FileStream(); // destructor + + // This function will allocate a new FileStream and open it. + // If it fails to allocate or fails to open, it will return NULL. + // The caller is responsible for deleting the instance. + static FileStream *createAndOpen(const String &inFileName, Torque::FS::File::AccessMode inMode); + + // mandatory methods from Stream base class... + virtual bool hasCapability(const Capability i_cap) const; + + virtual U32 getPosition() const; + virtual bool setPosition(const U32 i_newPosition); + virtual U32 getStreamSize(); + + // additional methods needed for a file stream... + virtual bool open(const String &inFileName, Torque::FS::File::AccessMode inMode); + virtual void close(); + + bool flush(); + FileStream* clone() const; + +protected: + // more mandatory methods from Stream base class... + virtual bool _read(const U32 i_numBytes, void *o_pBuffer); + virtual bool _write(const U32 i_numBytes, const void* i_pBuffer); + + void init(); + bool fillBuffer(const U32 i_startPosition); + void clearBuffer(); + static void calcBlockHead(const U32 i_position, U32 *o_blockHead); + static void calcBlockBounds(const U32 i_position, U32 *o_blockHead, U32 *o_blockTail); + void setStatus(); + + U32 mStreamCaps; // dependent on access mode + +private: + Torque::FS::FileRef mFile; // file being streamed + + U8 mBuffer[BUFFER_SIZE]; + U32 mBuffHead; // first valid position of buffer (from start-of-file) + U32 mBuffPos; // next read or write will occur here + U32 mBuffTail; // last valid position in buffer (inclusive) + bool mDirty; // whether buffer has been written to + bool mEOF; // whether disk reads have reached the end-of-file + + FileStream(const FileStream &i_fileStrm); // disable copy constructor + FileStream& operator=(const FileStream &i_fileStrm); // disable assignment operator +}; + +#endif // _FILE_STREAM_H diff --git a/Engine/source/core/stream/fileStreamObject.cpp b/Engine/source/core/stream/fileStreamObject.cpp new file mode 100644 index 000000000..68f0ecc6f --- /dev/null +++ b/Engine/source/core/stream/fileStreamObject.cpp @@ -0,0 +1,175 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/stream/fileStreamObject.h" +#include "console/engineAPI.h" + +//----------------------------------------------------------------------------- +// Local Globals +//----------------------------------------------------------------------------- + +static const struct +{ + const char *strMode; + Torque::FS::File::AccessMode mode; +} gModeMap[]= +{ + { "read", Torque::FS::File::Read }, + { "write", Torque::FS::File::Write }, + { "readwrite", Torque::FS::File::ReadWrite }, + { "writeappend", Torque::FS::File::WriteAppend }, + { NULL, (Torque::FS::File::AccessMode)0 } +}; + +//----------------------------------------------------------------------------- +// Constructor/Destructor +//----------------------------------------------------------------------------- + +FileStreamObject::FileStreamObject() +{ +} + +FileStreamObject::~FileStreamObject() +{ + close(); +} + +IMPLEMENT_CONOBJECT(FileStreamObject); + +ConsoleDocClass( FileStreamObject, + "@brief A wrapper around StreamObject for parsing text and data from files.\n\n" + + "FileStreamObject inherits from StreamObject and provides some unique methods for working " + "with strings. If you're looking for general file handling, you may want to use FileObject.\n\n" + + "@tsexample\n" + "// Create a file stream object for reading\n" + "%fsObject = new FileStreamObject();\n\n" + "// Open a file for reading\n" + "%fsObject.open(\"./test.txt\", \"read\");\n\n" + "// Get the status and print it\n" + "%status = %fsObject.getStatus();\n" + "echo(%status);\n\n" + "// Always remember to close a file stream when finished\n" + "%fsObject.close();\n" + "@endtsexample\n\n" + + "@see StreamObject for the list of inherited functions variables\n" + "@see FileObject for general file handling.\n" + + "@ingroup FileSystem\n" +); + +//----------------------------------------------------------------------------- + +bool FileStreamObject::onAdd() +{ + // [tom, 2/12/2007] Skip over StreamObject's onAdd() so that we can + // be instantiated from script. + return SimObject::onAdd(); +} + +//----------------------------------------------------------------------------- +// Public Methods +//----------------------------------------------------------------------------- + +bool FileStreamObject::open(const char *filename, Torque::FS::File::AccessMode mode) +{ + close(); + + if(! mFileStream.open(filename, mode)) + return false; + + mStream = &mFileStream; + return true; +} + +void FileStreamObject::close() +{ + mFileStream.close(); + mStream = NULL; +} + +//----------------------------------------------------------------------------- +// Console Methods +//----------------------------------------------------------------------------- + +DefineEngineMethod( FileStreamObject, open, bool, (const char* filename, const char* openMode),, + "@brief Open a file for reading, writing, reading and writing, or appending\n\n" + + "Using \"Read\" for the open mode allows you to parse the contents of file, but not making modifications. \"Write\" will create a new " + "file if it does not exist, or erase the contents of an existing file when opened. Write also allows you to modify the contents of the file.\n\n" + + "\"ReadWrite\" will provide the ability to parse data (read it in) and manipulate data (write it out) interchangeably. Keep in mind the stream can " + "move during each operation. Finally, \"WriteAppend\" will open a file if it exists, but will not clear the contents. You can write new data starting " + " at the end of the files existing contents.\n\n" + + "@param filename Name of file to open\n" + "@param openMode One of \"Read\", \"Write\", \"ReadWrite\" or \"WriteAppend\"\n\n" + + "@tsexample\n" + "// Create a file stream object for reading\n" + "%fsObject = new FileStreamObject();\n\n" + "// Open a file for reading\n" + "%fsObject.open(\"./test.txt\", \"read\");\n\n" + "// Get the status and print it\n" + "%status = %fsObject.getStatus();\n" + "echo(%status);\n\n" + "// Always remember to close a file stream when finished\n" + "%fsObject.close();\n" + "@endtsexample\n\n" + + "@return True if the file was successfully opened, false if something went wrong" + + "@see close()") +{ + for(S32 i = 0;gModeMap[i].strMode;++i) + { + if(dStricmp(gModeMap[i].strMode, openMode) == 0) + { + Torque::FS::File::AccessMode mode = gModeMap[i].mode; + char buffer[1024]; + Con::expandScriptFilename(buffer, sizeof(buffer), filename); + return object->open(buffer, mode); + } + } + + Con::errorf("FileStreamObject::open - Mode must be one of Read, Write, ReadWrite or WriteAppend."); + return false; +} + +DefineEngineMethod( FileStreamObject, close, void, (),, + "@brief Close the file. You can no longer read or write to it unless you open it again.\n\n" + + "@tsexample\n" + "// Create a file stream object for reading\n" + "%fsObject = new FileStreamObject();\n\n" + "// Open a file for reading\n" + "%fsObject.open(\"./test.txt\", \"read\");\n\n" + "// Always remember to close a file stream when finished\n" + "%fsObject.close();\n" + "@endtsexample\n\n" + + "@see open()") +{ + object->close(); +} diff --git a/Engine/source/core/stream/fileStreamObject.h b/Engine/source/core/stream/fileStreamObject.h new file mode 100644 index 000000000..5063da3e2 --- /dev/null +++ b/Engine/source/core/stream/fileStreamObject.h @@ -0,0 +1,67 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FILESTREAMOBJECT_H_ +#define _FILESTREAMOBJECT_H_ + +#ifndef _STREAMOBJECT_H_ +#include "core/stream/streamObject.h" +#endif + +#ifndef _FILESTREAM_H_ +#include "core/stream/fileStream.h" +#endif + + +class FileStreamObject : public StreamObject +{ + typedef StreamObject Parent; + +protected: + FileStream mFileStream; + +public: + FileStreamObject(); + virtual ~FileStreamObject(); + DECLARE_CONOBJECT(FileStreamObject); + + virtual bool onAdd(); + + //----------------------------------------------------------------------------- + /// @brief Open a file + /// + /// @param filename Name of file to open + /// @param mode One of #Torque::FS::File::Read, #Torque::FS::File::Write, #Torque::FS::File::ReadWrite or #Torque::FS::File::WriteAppend + /// @return true for success, false for failure + /// @see close() + //----------------------------------------------------------------------------- + bool open(const char *filename, Torque::FS::File::AccessMode mode); + + //----------------------------------------------------------------------------- + /// @brief Close the file + /// + /// @see open() + //----------------------------------------------------------------------------- + void close(); +}; + +#endif // _FILESTREAMOBJECT_H_ diff --git a/Engine/source/core/stream/ioHelper.h b/Engine/source/core/stream/ioHelper.h new file mode 100644 index 000000000..1255356d0 --- /dev/null +++ b/Engine/source/core/stream/ioHelper.h @@ -0,0 +1,115 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _UTIL_IOHELPER_H_ +#define _UTIL_IOHELPER_H_ + +#ifndef _CORE_STREAM_H_ +#include "core/stream/stream.h" +#endif + +/// Helper templates to aggregate IO operations - generally used in +/// template expansion. +namespace IOHelper +{ + template + inline bool reads(Stream *s,A& a,B& b,C& c,D& d,E& e,F& f,G& g,H& h,I& i,J& j) + { s->read(&a); s->read(&b); s->read(&c); s->read(&d); s->read(&e); s->read(&f); s->read(&g); s->read(&h); s->read(&i); return s->read(&j); } + + template + inline bool reads(Stream *s,A& a,B& b,C& c,D& d,E& e,F& f,G& g,H& h,I& i) + { s->read(&a); s->read(&b); s->read(&c); s->read(&d); s->read(&e); s->read(&f); s->read(&g); s->read(&h); return s->read(&i); } + + template + inline bool reads(Stream *s,A& a,B& b,C& c,D& d,E& e,F& f,G& g,H& h) + { s->read(&a); s->read(&b); s->read(&c); s->read(&d); s->read(&e); s->read(&f); s->read(&g); return s->read(&h); } + + template + inline bool reads(Stream *s,A& a,B& b,C& c,D& d,E& e,F& f,G& g) + { s->read(&a); s->read(&b); s->read(&c); s->read(&d); s->read(&e); s->read(&f); return s->read(&g); } + + template + inline bool reads(Stream *s,A& a,B& b,C& c,D& d,E& e,F& f) + { s->read(&a); s->read(&b); s->read(&c); s->read(&d); s->read(&e); return s->read(&f); } + + template + inline bool reads(Stream *s,A& a,B& b,C& c,D& d,E& e) + { s->read(&a); s->read(&b); s->read(&c); s->read(&d); return s->read(&e); } + + template + inline bool reads(Stream *s,A& a,B& b,C& c,D& d) + { s->read(&a); s->read(&b); s->read(&c); return s->read(&d); } + + template + inline bool reads(Stream *s,A& a,B& b,C& c) + { s->read(&a); s->read(&b); return s->read(&c); } + + template + inline bool reads(Stream *s,A& a,B& b) + { s->read(&a); return s->read(&b); } + + template + inline bool reads(Stream *s,A& a) + { return s->read(&a); } + + template + inline bool writes(Stream *s,A& a,B& b,C& c,D& d,E& e,F& f,G& g,H& h,I& i,J& j) + { s->write(a); s->write(b); s->write(c); s->write(d); s->write(e); s->write(f); s->write(g); s->write(h); s->write(i); return s->write(j); } + + template + inline bool writes(Stream *s,A& a,B& b,C& c,D& d,E& e,F& f,G& g,H& h,I& i) + { s->write(a); s->write(b); s->write(c); s->write(d); s->write(e); s->write(f); s->write(g); s->write(h); return s->write(i); } + + template + inline bool writes(Stream *s,A& a,B& b,C& c,D& d,E& e,F& f,G& g,H& h) + { s->write(a); s->write(b); s->write(c); s->write(d); s->write(e); s->write(f); s->write(g); return s->write(h); } + + template + inline bool writes(Stream *s,A& a,B& b,C& c,D& d,E& e,F& f,G& g) + { s->write(a); s->write(b); s->write(c); s->write(d); s->write(e); s->write(f); return s->write(g); } + + template + inline bool writes(Stream *s,A& a,B& b,C& c,D& d,E& e,F& f) + { s->write(a); s->write(b); s->write(c); s->write(d); s->write(e); return s->write(f); } + + template + inline bool writes(Stream *s,A& a,B& b,C& c,D& d,E& e) + { s->write(a); s->write(b); s->write(c); s->write(d); return s->write(e); } + + template + inline bool writes(Stream *s,A& a,B& b,C& c,D& d) + { s->write(a); s->write(b); s->write(c); return s->write(d); } + + template + inline bool writes(Stream *s,A& a,B& b,C& c) + { s->write(a); s->write(b); return s->write(c); } + + template + inline bool writes(Stream *s,A& a,B& b) + { s->write(a); return s->write(b); } + + template + inline bool writes(Stream *s,A& a) + { return s->write(a); } +} + +#endif \ No newline at end of file diff --git a/Engine/source/core/stream/memStream.cpp b/Engine/source/core/stream/memStream.cpp new file mode 100644 index 000000000..bcfbc8d89 --- /dev/null +++ b/Engine/source/core/stream/memStream.cpp @@ -0,0 +1,233 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/stream/memStream.h" + + +MemStream::MemStream( U32 growSize, + bool allowRead, + bool allowWrite ) + : mGrowSize( growSize ), + mBufferSize( 0 ), + mStreamSize( 0 ), + mBufferBase( NULL ), + mInstCaps( 0 ), + mCurrentPosition( 0 ) +{ + AssertFatal( allowRead || allowWrite, "Either write or read must be allowed" ); + + if ( allowRead ) + mInstCaps |= Stream::StreamRead; + if ( allowWrite ) + mInstCaps |= Stream::StreamWrite; + + mOwnsMemory = true; + setStatus( mGrowSize > 0 ? Ok : EOS ); +} + +MemStream::MemStream( U32 bufferSize, + void *buffer, + bool allowRead, + bool allowWrite) + : mGrowSize( 0 ), + mBufferSize( bufferSize ), + mStreamSize( bufferSize ), + mBufferBase( buffer ), + mInstCaps( 0 ), + mCurrentPosition( 0 ), + mOwnsMemory( false ) +{ + AssertFatal( bufferSize > 0, "Invalid buffer size"); + AssertFatal( allowRead || allowWrite, "Either write or read must be allowed"); + + if ( allowRead ) + mInstCaps |= Stream::StreamRead; + if ( allowWrite ) + mInstCaps |= Stream::StreamWrite; + + if ( !buffer ) + { + mOwnsMemory = true; + mBufferBase = dMalloc( mBufferSize ); + } + + setStatus( Ok ); +} + +MemStream::~MemStream() +{ + if ( mOwnsMemory ) + dFree( mBufferBase ); + + mBufferBase = NULL; + mCurrentPosition = 0; + + setStatus( Closed ); +} + +U32 MemStream::getStreamSize() +{ + AssertFatal( getStatus() != Closed, "Stream not open, size undefined" ); + + return mStreamSize; +} + +bool MemStream::hasCapability(const Capability in_cap) const +{ + // Closed streams can't do anything + // + if (getStatus() == Closed) + return false; + + U32 totalCaps = U32(Stream::StreamPosition) | mInstCaps; + + return (U32(in_cap) & totalCaps) != 0; +} + +U32 MemStream::getPosition() const +{ + AssertFatal(getStatus() != Closed, "Position of a closed stream is undefined"); + + return mCurrentPosition; +} + +bool MemStream::setPosition(const U32 in_newPosition) +{ + AssertFatal(getStatus() != Closed, "SetPosition of a closed stream is not allowed"); + AssertFatal(in_newPosition <= mStreamSize, "Invalid position"); + + mCurrentPosition = in_newPosition; + if (mCurrentPosition > mStreamSize) { + // Never gets here in debug version, this is for the release builds... + // + setStatus(UnknownError); + return false; + } else if (mCurrentPosition == mStreamSize) { + setStatus(EOS); + } else { + setStatus(Ok); + } + + return true; +} + +bool MemStream::_read(const U32 in_numBytes, void *out_pBuffer) +{ + AssertFatal(getStatus() != Closed, "Attempted read from a closed stream"); + + if (in_numBytes == 0) + return true; + + AssertFatal(out_pBuffer != NULL, "Invalid output buffer"); + + if (hasCapability(StreamRead) == false) { + AssertWarn(false, "Reading is disallowed on this stream"); + setStatus(IllegalCall); + return false; + } + + bool success = true; + U32 actualBytes = in_numBytes; + if ((mCurrentPosition + in_numBytes) > mStreamSize) { + success = false; + actualBytes = mStreamSize - mCurrentPosition; + } + + // Obtain a current pointer, and do the copy + const void* pCurrent = (const void*)((const U8*)mBufferBase + mCurrentPosition); + dMemcpy(out_pBuffer, pCurrent, actualBytes); + + // Advance the stream position + mCurrentPosition += actualBytes; + + if (!success) + setStatus(EOS); + else + setStatus(Ok); + + return success; +} + +bool MemStream::_write(const U32 in_numBytes, const void *in_pBuffer) +{ + AssertFatal(getStatus() != Closed, "Attempted write to a closed stream"); + + if (in_numBytes == 0) + return true; + + if (hasCapability(StreamWrite) == false) + { + AssertWarn(0, "Writing is disallowed on this stream"); + setStatus(IllegalCall); + return false; + } + + bool success = true; + U32 actualBytes = in_numBytes; + if ((mCurrentPosition + in_numBytes) > mBufferSize) + { + if ( mGrowSize > 0 ) + { + U32 growSize = (mCurrentPosition + in_numBytes) - mBufferSize; + mBufferSize += growSize + ( mGrowSize - ( growSize % mGrowSize ) ); + mBufferBase = dRealloc( mBufferBase, mBufferSize ); + } + else + { + success = false; + actualBytes = mBufferSize - mCurrentPosition; + } + } + + AssertFatal(in_pBuffer != NULL, "Invalid input buffer"); + + // Obtain a current pointer, and do the copy + void* pCurrent = (void*)((U8*)mBufferBase + mCurrentPosition); + dMemcpy(pCurrent, in_pBuffer, actualBytes); + + // Advance the stream position + mCurrentPosition += actualBytes; + if (mCurrentPosition > mStreamSize) + mStreamSize = mCurrentPosition; + + if (mCurrentPosition == mStreamSize) + setStatus(EOS); + else + setStatus(Ok); + + return success; +} + +void *MemStream::takeBuffer() +{ + void *buffer = mBufferBase; + + mBufferBase = NULL; + mBufferSize = 0; + mStreamSize = 0; + mCurrentPosition = 0; + + setStatus(EOS); + + return buffer; +} diff --git a/Engine/source/core/stream/memStream.h b/Engine/source/core/stream/memStream.h new file mode 100644 index 000000000..538c8d93e --- /dev/null +++ b/Engine/source/core/stream/memStream.h @@ -0,0 +1,104 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MEMSTREAM_H_ +#define _MEMSTREAM_H_ + +#ifndef _STREAM_H_ +#include "core/stream/stream.h" +#endif + + +/// The MemStream class is used to read and write to a memory buffer. +class MemStream : public Stream +{ + typedef Stream Parent; + + protected: + + /// The amount to grow the buffer when we run out of + /// space writing to the stream. + U32 mGrowSize; + + /// The actual size of the memory buffer. It is always + /// greater than or equal to the mStreamSize. + U32 mBufferSize; + + /// The size of the data in the stream. It is always + /// less than or equal to the mBufferSize. + U32 mStreamSize; + + /// The memory buffer. + void *mBufferBase; + + /// If true the memory is owned by the steam and it + /// will be deleted in the destructor. + bool mOwnsMemory; + + /// + U32 mInstCaps; + + /// Our current read/write position within the buffer. + U32 mCurrentPosition; + + public: + + /// This constructs an empty memory stream that will grow + /// in increments as needed. + MemStream( U32 growSize, + bool allowRead = true, + bool allowWrite = true ); + + /// This constructs the stream with a fixed size memory buffer. If + /// buffer is null then it will be allocated for you. + MemStream( U32 bufferSize, + void *buffer, + bool allowRead = true, + bool allowWrite = true ); + + /// The destructor. + virtual ~MemStream(); + + protected: + + // Stream + bool _read( const U32 in_numBytes, void *out_pBuffer ); + bool _write( const U32 in_numBytes, const void *in_pBuffer ); + + public: + + // Stream + bool hasCapability( const Capability caps ) const; + U32 getPosition() const; + bool setPosition( const U32 in_newPosition ); + U32 getStreamSize(); + + /// Returns the memory buffer. + void *getBuffer() { return mBufferBase; } + const void *getBuffer() const { return mBufferBase; } + + /// Takes the memory buffer reseting the stream. + void *takeBuffer(); + +}; + +#endif //_MEMSTREAM_H_ diff --git a/Engine/source/core/stream/stream.cpp b/Engine/source/core/stream/stream.cpp new file mode 100644 index 000000000..a11d40010 --- /dev/null +++ b/Engine/source/core/stream/stream.cpp @@ -0,0 +1,378 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/color.h" +#include "core/util/rawData.h" +#include "core/frameAllocator.h" +#include "platform/platformNet.h" + +#include "core/stream/stream.h" + +#include "core/stringTable.h" +#include "core/strings/stringFunctions.h" + +#include "core/util/byteBuffer.h" +#include "core/util/endian.h" +#include "core/util/str.h" + + +#define IMPLEMENT_OVERLOADED_READ(type) \ + bool Stream::read(type* out_read) \ + { \ + return read(sizeof(type), out_read); \ + } + +#define IMPLEMENT_OVERLOADED_WRITE(type) \ + bool Stream::write(type in_write) \ + { \ + return write(sizeof(type), &in_write); \ + } + +#define IMPLEMENT_ENDIAN_OVERLOADED_READ(type) \ + bool Stream::read(type* out_read) \ + { \ + type temp; \ + bool success = read(sizeof(type), &temp); \ + *out_read = convertLEndianToHost(temp); \ + return success; \ + } + +#define IMPLEMENT_ENDIAN_OVERLOADED_WRITE(type) \ + bool Stream::write(type in_write) \ + { \ + type temp = convertHostToLEndian(in_write); \ + return write(sizeof(type), &temp); \ + } + +IMPLEMENT_OVERLOADED_WRITE(S8) +IMPLEMENT_OVERLOADED_WRITE(U8) + +IMPLEMENT_ENDIAN_OVERLOADED_WRITE(S16) +IMPLEMENT_ENDIAN_OVERLOADED_WRITE(S32) +IMPLEMENT_ENDIAN_OVERLOADED_WRITE(U16) +IMPLEMENT_ENDIAN_OVERLOADED_WRITE(U32) +IMPLEMENT_ENDIAN_OVERLOADED_WRITE(U64) +IMPLEMENT_ENDIAN_OVERLOADED_WRITE(F32) +IMPLEMENT_ENDIAN_OVERLOADED_WRITE(F64) + +IMPLEMENT_OVERLOADED_READ(S8) +IMPLEMENT_OVERLOADED_READ(U8) + +IMPLEMENT_ENDIAN_OVERLOADED_READ(S16) +IMPLEMENT_ENDIAN_OVERLOADED_READ(S32) +IMPLEMENT_ENDIAN_OVERLOADED_READ(U16) +IMPLEMENT_ENDIAN_OVERLOADED_READ(U32) +IMPLEMENT_ENDIAN_OVERLOADED_READ(U64) +IMPLEMENT_ENDIAN_OVERLOADED_READ(F32) +IMPLEMENT_ENDIAN_OVERLOADED_READ(F64) + + +Stream::Stream() + : m_streamStatus(Closed) +{ +} + +const char* Stream::getStatusString(const Status in_status) +{ + switch (in_status) { + case Ok: + return "StreamOk"; + case IOError: + return "StreamIOError"; + case EOS: + return "StreamEOS"; + case IllegalCall: + return "StreamIllegalCall"; + case Closed: + return "StreamClosed"; + case UnknownError: + return "StreamUnknownError"; + + default: + return "Invalid Stream::Status"; + } +} + +void Stream::writeString(const char *string, S32 maxLen) +{ + S32 len = string ? dStrlen(string) : 0; + if(len > maxLen) + len = maxLen; + + write(U8(len)); + if(len) + write(len, string); +} + +void Stream::readString(char buf[256]) +{ + U8 len; + read(&len); + read(S32(len), buf); + buf[len] = 0; +} + +const char *Stream::readSTString(bool casesens) +{ + char buf[256]; + readString(buf); + return StringTable->insert(buf, casesens); +} + +void Stream::readLongString(U32 maxStringLen, char *stringBuf) +{ + U32 len; + read(&len); + if(len > maxStringLen) + { + m_streamStatus = IOError; + return; + } + read(len, stringBuf); + stringBuf[len] = 0; +} + +void Stream::writeLongString(U32 maxStringLen, const char *string) +{ + U32 len = dStrlen(string); + if(len > maxStringLen) + len = maxStringLen; + write(len); + write(len, string); +} + +void Stream::readLine(U8 *buffer, U32 bufferSize) +{ + bufferSize--; // account for NULL terminator + U8 *buff = buffer; + U8 *buffEnd = buff + bufferSize; + *buff = '\r'; + + // strip off preceding white space + while ( *buff == '\r' ) + { + if ( !read(buff) || *buff == '\n' ) + { + *buff = 0; + return; + } + } + + // read line + while ( buff != buffEnd && read(++buff) && *buff != '\n' ) + { + if ( *buff == '\r' ) + { + +#if defined(TORQUE_OS_MAC) + U32 pushPos = getPosition(); // in case we need to back up. + if (read(buff)) // feeling free to overwrite the \r as the NULL below will overwrite again... + if (*buff != '\n') // then push our position back. + setPosition(pushPos); + break; // we're always done after seeing the CR... +#else + buff--; // 'erases' the CR of a CRLF +#endif + + } + } + *buff = 0; +} + +void Stream::writeText(const char *text) +{ + if (text && text[0]) + write(dStrlen(text), text); +} + +void Stream::writeLine(const U8 *buffer) +{ + write(dStrlen((const char *)buffer), buffer); + write(2, "\r\n"); +} + +void Stream::_write(const String & str) +{ + U32 len = str.length(); + + if (len<255) + write(U8(len)); + else + { + // longer string, write full length + write(U8(255)); + + // fail if longer than 16 bits (will truncate string modulo 2^16) + AssertFatal(len < (1<<16),"String too long"); + + len &= (1<<16)-1; + write(U16(len)); + } + + write(len,str.c_str()); +} + +void Stream::_read(String * str) +{ + U16 len; + + U8 len8; + read(&len8); + if (len8==255) + read(&len); + else + len = len8; + + char * buffer = (char*)FrameAllocator::alloc(len); + read(len, buffer); + *str = String(buffer,len); +} + + +bool Stream::write(const ColorI& rColor) +{ + bool success = write(rColor.red); + success |= write(rColor.green); + success |= write(rColor.blue); + success |= write(rColor.alpha); + + return success; +} + +bool Stream::write(const ColorF& rColor) +{ + ColorI temp = rColor; + return write(temp); +} + +bool Stream::read(ColorI* pColor) +{ + bool success = read(&pColor->red); + success |= read(&pColor->green); + success |= read(&pColor->blue); + success |= read(&pColor->alpha); + + return success; +} + +bool Stream::read(ColorF* pColor) +{ + ColorI temp; + bool success = read(&temp); + + *pColor = temp; + return success; +} + +bool Stream::write(const NetAddress &na) +{ + bool success = write(na.type); + success &= write(na.port); + success &= write(na.netNum[0]); + success &= write(na.netNum[1]); + success &= write(na.netNum[2]); + success &= write(na.netNum[3]); + success &= write(na.nodeNum[0]); + success &= write(na.nodeNum[1]); + success &= write(na.nodeNum[2]); + success &= write(na.nodeNum[3]); + success &= write(na.nodeNum[4]); + success &= write(na.nodeNum[5]); + return success; +} + +bool Stream::read(NetAddress *na) +{ + bool success = read(&na->type); + success &= read(&na->port); + success &= read(&na->netNum[0]); + success &= read(&na->netNum[1]); + success &= read(&na->netNum[2]); + success &= read(&na->netNum[3]); + success &= read(&na->nodeNum[0]); + success &= read(&na->nodeNum[1]); + success &= read(&na->nodeNum[2]); + success &= read(&na->nodeNum[3]); + success &= read(&na->nodeNum[4]); + success &= read(&na->nodeNum[5]); + return success; +} + +bool Stream::write(const RawData &rd) +{ + bool s = write(rd.size); + s &= write(rd.size, rd.data); + return s; +} + +bool Stream::read(RawData *rd) +{ + U32 size = 0; + bool s = read(&size); + + rd->alloc(size); + s &= read(rd->size, rd->data); + + return s; +} + +bool Stream::write(const Torque::ByteBuffer &rd) +{ + bool s = write(rd.getBufferSize()); + s &= write(rd.getBufferSize(), rd.getBuffer()); + return s; +} + +bool Stream::read(Torque::ByteBuffer *rd) +{ + U32 size = 0; + bool s = read(&size); + + rd->resize(size); + s &= read(rd->getBufferSize(), rd->getBuffer()); + + return s; +} + +bool Stream::copyFrom(Stream *other) +{ + U8 buffer[1024]; + U32 numBytes = other->getStreamSize() - other->getPosition(); + while((other->getStatus() != Stream::EOS) && numBytes > 0) + { + U32 numRead = numBytes > sizeof(buffer) ? sizeof(buffer) : numBytes; + if(! other->read(numRead, buffer)) + return false; + + if(! write(numRead, buffer)) + return false; + + numBytes -= numRead; + } + + return true; +} + +Stream* Stream::clone() const +{ + return NULL; +} diff --git a/Engine/source/core/stream/stream.h b/Engine/source/core/stream/stream.h new file mode 100644 index 000000000..c4d17a791 --- /dev/null +++ b/Engine/source/core/stream/stream.h @@ -0,0 +1,242 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _STREAM_H_ +#define _STREAM_H_ + +#ifndef _TORQUE_TYPES_H_ +#include "platform/types.h" +#endif +#ifndef _ENDIAN_H_ +#include "core/util/endian.h" +#endif + + +/// @defgroup stream_overload Primitive Type Stream Operation Overloads +/// These macros declare the read and write functions for all primitive types. +/// @{ +#define DECLARE_OVERLOADED_READ(type) bool read(type* out_read); +#define DECLARE_OVERLOADED_WRITE(type) bool write(type in_write); +/// @} + +class ColorI; +class ColorF; +struct NetAddress; +class RawData; +class String; + +namespace Torque { + class ByteBuffer; +} + +//------------------------------------------------------------------------------ +//-------------------------------------- Base Stream class +// +/// Base stream class for streaming data across a specific media +class Stream +{ + // Public structs and enumerations... +public: + /// Status constants for the stream + enum Status { + Ok = 0, ///< Ok! + IOError, ///< Read or Write error + EOS, ///< End of Stream reached (mostly for reads) + IllegalCall, ///< An unsupported operation used. Always w/ accompanied by AssertWarn + Closed, ///< Tried to operate on a closed stream (or detached filter) + UnknownError ///< Catchall + }; + + enum Capability { + StreamWrite = BIT(0), ///< Can this stream write? + StreamRead = BIT(1), ///< Can this stream read? + StreamPosition = BIT(2) ///< Can this stream position? + }; + + // Accessible only through inline accessors +private: + Status m_streamStatus; + + // Derived accessible data modifiers... +protected: + void setStatus(const Status in_newStatus) { m_streamStatus = in_newStatus; } + +public: + Stream(); + virtual ~Stream() {} + + /// Gets the status of the stream + Stream::Status getStatus() const { return m_streamStatus; } + /// Gets a printable string form of the status + static const char* getStatusString(const Status in_status); + + // Derived classes must override these... +protected: + virtual bool _read(const U32 in_numBytes, void* out_pBuffer) = 0; + virtual bool _write(const U32 in_numBytes, const void* in_pBuffer) = 0; + + virtual void _write(const String & str); + virtual void _read(String * str); + + +public: + /// Checks to see if this stream has the capability of a given function + virtual bool hasCapability(const Capability caps) const = 0; + + /// Gets the position in the stream + virtual U32 getPosition() const = 0; + /// Sets the position of the stream. Returns if the new position is valid or not + virtual bool setPosition(const U32 in_newPosition) = 0; + /// Gets the size of the stream + virtual U32 getStreamSize() = 0; + + /// Reads a line from the stream. + /// @param buffer buffer to be read into + /// @param bufferSize max size of the buffer. Will not read more than the "bufferSize" + void readLine(U8 *buffer, U32 bufferSize); + /// writes a line to the stream + void writeLine(const U8 *buffer); + + /// Reads a string and inserts it into the StringTable + /// @see StringTable + const char *readSTString(bool casesens = false); + /// Reads a string of maximum 255 characters long + virtual void readString(char stringBuf[256]); + /// Reads a string that could potentially be more than 255 characters long. + /// @param maxStringLen Maximum length to read. If the string is longer than maxStringLen, only maxStringLen bytes will be read. + /// @param stringBuf buffer where data is read into + void readLongString(U32 maxStringLen, char *stringBuf); + /// Writes a string to the stream. This function is slightly unstable. + /// Only use this if you have a valid string that is not empty. + /// writeString is safer. + void writeLongString(U32 maxStringLen, const char *string); + + /// Write raw text to the stream + void writeText(const char *text); + + /// Writes a string to the stream. + virtual void writeString(const char *stringBuf, S32 maxLen=255); + + // read/write real strings + void write(const String & str) { _write(str); } + void read(String * str) { _read(str); } + + /// Write an integral color to the stream. + bool write(const ColorI&); + /// Write a floating point color to the stream. + bool write(const ColorF&); + /// Read an integral color from the stream. + bool read(ColorI*); + /// Read a floating point color from the stream. + bool read(ColorF*); + + /// Write a network address to the stream. + bool write(const NetAddress &); + /// Read a network address from the stream. + bool read(NetAddress*); + + /// Write some raw data onto the stream. + bool write(const RawData &); + /// Read some raw data from the stream. + bool read(RawData *); + + /// Write some raw data onto the stream. + bool write(const Torque::ByteBuffer &); + /// Read some raw data from the stream. + bool read(Torque::ByteBuffer *); + + // Overloaded write and read ops.. + public: + bool read(const U32 in_numBytes, void* out_pBuffer) { + return _read(in_numBytes, out_pBuffer); + } + bool write(const U32 in_numBytes, const void* in_pBuffer) { + return _write(in_numBytes, in_pBuffer); + } + + DECLARE_OVERLOADED_WRITE(S8) + DECLARE_OVERLOADED_WRITE(U8) + + DECLARE_OVERLOADED_WRITE(S16) + DECLARE_OVERLOADED_WRITE(S32) + DECLARE_OVERLOADED_WRITE(U16) + DECLARE_OVERLOADED_WRITE(U32) + DECLARE_OVERLOADED_WRITE(U64) + DECLARE_OVERLOADED_WRITE(F32) + DECLARE_OVERLOADED_WRITE(F64) + + DECLARE_OVERLOADED_READ(S8) + DECLARE_OVERLOADED_READ(U8) + + DECLARE_OVERLOADED_READ(S16) + DECLARE_OVERLOADED_READ(S32) + DECLARE_OVERLOADED_READ(U16) + DECLARE_OVERLOADED_READ(U32) + DECLARE_OVERLOADED_READ(U64) + DECLARE_OVERLOADED_READ(F32) + DECLARE_OVERLOADED_READ(F64) + + // We have to do the bool's by hand, since they are different sizes + // on different compilers... + // + bool read(bool* out_pRead) { + U8 translate; + bool success = read(&translate); + if (success == false) + return false; + + *out_pRead = translate != 0; + return true; + } + bool write(const bool& in_rWrite) { + U8 translate = in_rWrite ? U8(1) : U8(0); + return write(translate); + } + + /// Copy the contents of another stream into this one + bool copyFrom(Stream *other); + + /// Write a number of tabs to this stream + void writeTabs(U32 count) + { + char tab[] = " "; + while(count--) + write(3, (void*)tab); + } + + /// Create an exact replica of this stream. + /// Return NULL if the cloning fails. + virtual Stream* clone() const; +}; + +// This interface is used to provide the amount of bytes actually written/read when using the stream as a +// file. The Stream interface does not provide this information. This is a lame workaround. +class IStreamByteCount +{ +public: + virtual ~IStreamByteCount() {} + + virtual U32 getLastBytesRead() = 0; + virtual U32 getLastBytesWritten() = 0; +}; + +#endif //_STREAM_H_ diff --git a/Engine/source/core/stream/streamObject.cpp b/Engine/source/core/stream/streamObject.cpp new file mode 100644 index 000000000..bf684cf08 --- /dev/null +++ b/Engine/source/core/stream/streamObject.cpp @@ -0,0 +1,476 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/stream/streamObject.h" +#include "console/engineAPI.h" + +//----------------------------------------------------------------------------- +// Constructor/Destructor +//----------------------------------------------------------------------------- + +// This entire class has some major issues. Lots of bad characters when reading and +// writing. Stream doesn't seem to jump correctly. All in all this either needs +// to be deprecated, fixed, or someone explain to me what I'm doing wrong. +// Hard to document when it's not working. -Mich 7/16/2010 + +StreamObject::StreamObject() +{ + mStream = NULL; +} + +StreamObject::StreamObject(Stream *stream) +{ + mStream = stream; +} + +StreamObject::~StreamObject() +{ +} + +IMPLEMENT_CONOBJECT(StreamObject); + +ConsoleDocClass( StreamObject, + "@brief Base class for working with streams.\n\n" + + "You do not instantiate a StreamObject directly. Instead, it is used as part of a FileStreamObject and ZipObject to " + "support working with uncompressed and compressed files respectively." + + "@tsexample\n" + "// You cannot actually declare a StreamObject\n" + "// Instead, use the derived class \"FileStreamObject\"\n" + "%fsObject = FileStreamObject();\n" + "@endtsexample\n\n" + + "@see FileStreamObject for the derived class usable in script.\n" + "@see ZipObject where StreamObject is used to read and write to files within a zip archive.\n" + + "@ingroup FileSystem\n\n" +); +//----------------------------------------------------------------------------- + +bool StreamObject::onAdd() +{ + if(mStream == NULL) + { + Con::errorf("StreamObject::onAdd - StreamObject can not be instantiated from script."); + return false; + } + return Parent::onAdd(); +} + +//----------------------------------------------------------------------------- +// Public Methods +//----------------------------------------------------------------------------- + +const char * StreamObject::getStatus() +{ + if(mStream == NULL) + return ""; + + switch(mStream->getStatus()) + { + case Stream::Ok: + return "Ok"; + case Stream::IOError: + return "IOError"; + case Stream::EOS: + return "EOS"; + case Stream::IllegalCall: + return "IllegalCall"; + case Stream::Closed: + return "Closed"; + case Stream::UnknownError: + return "UnknownError"; + + default: + return "Invalid"; + } +} + +//----------------------------------------------------------------------------- + +const char * StreamObject::readLine() +{ + if(mStream == NULL) + return NULL; + + char *buffer = Con::getReturnBuffer(256); + mStream->readLine((U8 *)buffer, 256); + return buffer; +} + +const char * StreamObject::readString() +{ + if(mStream == NULL) + return NULL; + + char *buffer = Con::getReturnBuffer(256); + mStream->readString(buffer); + return buffer; +} + +const char *StreamObject::readLongString(U32 maxStringLen) +{ + if(mStream == NULL) + return NULL; + + char *buffer = Con::getReturnBuffer(maxStringLen + 1); + mStream->readLongString(maxStringLen, buffer); + return buffer; +} + +//----------------------------------------------------------------------------- +// Console Methods +//----------------------------------------------------------------------------- + +DefineEngineMethod( StreamObject, getStatus, const char*, (),, + "@brief Gets a printable string form of the stream's status\n\n" + + "@tsexample\n" + "// Create a file stream object for reading\n" + "%fsObject = new FileStreamObject();\n\n" + "// Open a file for reading\n" + "%fsObject.open(\"./test.txt\", \"read\");\n\n" + "// Get the status and print it\n" + "%status = %fsObject.getStatus();\n" + "echo(%status);\n\n" + "// Always remember to close a file stream when finished\n" + "%fsObject.close();\n" + "@endtsexample\n\n" + + "@return String containing status constant, one of the following:\n\n" + + " OK - Stream is active and no file errors\n\n" + + " IOError - Something went wrong during read or writing the stream\n\n" + + " EOS - End of Stream reached (mostly for reads)\n\n" + + " IllegalCall - An unsupported operation used. Always w/ accompanied by AssertWarn\n\n" + + " Closed - Tried to operate on a closed stream (or detached filter)\n\n" + + " UnknownError - Catch all for an error of some kind\n\n" + + " Invalid - Entire stream is invalid\n\n") +{ + return object->getStatus(); +} + +DefineEngineMethod( StreamObject, isEOS, bool, (),, + "@brief Tests if the stream has reached the end of the file\n\n" + + "This is an alternative name for isEOF. Both functions are interchangeable. This simply exists " + "for those familiar with some C++ file I/O standards.\n\n" + + "@tsexample\n" + "// Create a file stream object for reading\n" + "%fsObject = new FileStreamObject();\n\n" + "// Open a file for reading\n" + "%fsObject.open(\"./test.txt\", \"read\");\n\n" + "// Keep reading until we reach the end of the file\n" + "while( !%fsObject.isEOS() )\n" + "{\n" + " %line = %fsObject.readLine();\n" + " echo(%line);\n" + "}\n" + "// Made it to the end\n" + "echo(\"Finished reading file\");\n\n" + "// Always remember to close a file stream when finished\n" + "%fsObject.close();\n" + "@endtsexample\n\n" + + "@return True if the parser has reached the end of the file, false otherwise\n" + + "@see isEOF()") +{ + return object->isEOS(); +} + +DefineEngineMethod( StreamObject, isEOF, bool, (),, + "@brief Tests if the stream has reached the end of the file\n\n" + + "This is an alternative name for isEOS. Both functions are interchangeable. This simply exists " + "for those familiar with some C++ file I/O standards.\n\n" + + "@tsexample\n" + "// Create a file stream object for reading\n" + "%fsObject = new FileStreamObject();\n\n" + "// Open a file for reading\n" + "%fsObject.open(\"./test.txt\", \"read\");\n\n" + "// Keep reading until we reach the end of the file\n" + "while( !%fsObject.isEOF() )\n" + "{\n" + " %line = %fsObject.readLine();\n" + " echo(%line);\n" + "}\n" + "// Made it to the end\n" + "echo(\"Finished reading file\");\n\n" + "// Always remember to close a file stream when finished\n" + "%fsObject.close();\n" + "@endtsexample\n\n" + + "@return True if the parser has reached the end of the file, false otherwise\n" + + "@see isEOS()") +{ + return object->isEOS(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( StreamObject, getPosition, S32, (),, + "@brief Gets the position in the stream\n\n" + + "The easiest way to visualize this is to think of a cursor in a text file. If you have moved the cursor by " + "five characters, the current position is 5. If you move ahead 10 more characters, the position is now 15. " + "For StreamObject, when you read in the line the position is increased by the number of characters parsed, " + "the null terminator, and a newline.\n\n" + + "@tsexample\n" + "// Create a file stream object for reading\n" + "%fsObject = new FileStreamObject();\n\n" + "// Open a file for reading\n" + "// This file contains two lines of text repeated:\n" + "// Hello World\n" + "// Hello World\n" + "%fsObject.open(\"./test.txt\", \"read\");\n\n" + "// Read in the first line\n" + "%line = %fsObject.readLine();\n\n" + "// Get the position of the stream\n" + "%position = %fsObject.getPosition();\n\n" + "// Print the current position\n" + "// Should be 13, 10 for the words, 1 for the space, 1 for the null terminator, and 1 for the newline\n" + "echo(%position);\n\n" + "// Always remember to close a file stream when finished\n" + "%fsObject.close();\n" + "@endtsexample\n\n" + + "@return Number of bytes which stream has parsed so far, null terminators and newlines are included\n" + + "@see setPosition()") +{ + return object->getPosition(); +} + +DefineEngineMethod( StreamObject, setPosition, bool, (S32 newPosition),, + "@brief Gets the position in the stream\n\n" + + "The easiest way to visualize this is to think of a cursor in a text file. If you have moved the cursor by " + "five characters, the current position is 5. If you move ahead 10 more characters, the position is now 15. " + "For StreamObject, when you read in the line the position is increased by the number of characters parsed, " + "the null terminator, and a newline. Using setPosition allows you to skip to specific points of the file.\n\n" + + "@tsexample\n" + "// Create a file stream object for reading\n" + "%fsObject = new FileStreamObject();\n\n" + "// Open a file for reading\n" + "// This file contains the following two lines:\n" + "// 11111111111\n" + "// Hello World\n" + "%fsObject.open(\"./test.txt\", \"read\");\n\n" + "// Skip ahead by 12, which will bypass the first line entirely\n" + "%fsObject.setPosition(12);\n\n" + "// Read in the next line\n" + "%line = %fsObject.readLine();\n\n" + "// Print the line just read in, should be \"Hello World\"\n" + "echo(%line);\n\n" + "// Always remember to close a file stream when finished\n" + "%fsObject.close();\n" + "@endtsexample\n\n" + + "@return Number of bytes which stream has parsed so far, null terminators and newlines are included\n" + + "@see getPosition()") +{ + return object->setPosition(newPosition); +} + +DefineEngineMethod( StreamObject, getStreamSize, S32, (),, + "@brief Gets the size of the stream\n\n" + + "The size is dependent on the type of stream being used. If it is a file stream, returned value will " + "be the size of the file. If it is a memory stream, it will be the size of the allocated buffer.\n\n" + + "@tsexample\n" + "// Create a file stream object for reading\n" + "%fsObject = new FileStreamObject();\n\n" + "// Open a file for reading\n" + "// This file contains the following two lines:\n" + "// HelloWorld\n" + "// HelloWorld\n" + "%fsObject.open(\"./test.txt\", \"read\");\n\n" + "// Found out how large the file stream is\n" + "// Then print it to the console\n" + "// Should be 22\n" + "%streamSize = %fsObject.getStreamSize();\n" + "echo(%streamSize);\n\n" + "// Always remember to close a file stream when finished\n" + "%fsObject.close();\n" + "@endtsexample\n\n" + + "@return Size of stream, in bytes\n") +{ + return object->getStreamSize(); +} + +//----------------------------------------------------------------------------- +DefineEngineMethod( StreamObject, readLine, const char*, (),, + "@brief Read a line from the stream.\n\n" + + "Emphasis on *line*, as in you cannot parse individual characters or chunks of data. " + "There is no limitation as to what kind of data you can read.\n\n" + + "@tsexample\n" + "// Create a file stream object for reading\n" + "// This file contains the following two lines:\n" + "// HelloWorld\n" + "// HelloWorld\n" + "%fsObject = new FileStreamObject();\n\n" + "%fsObject.open(\"./test.txt\", \"read\");\n\n" + "// Read in the first line\n" + "%line = %fsObject.readLine();\n\n" + "// Print the line we just read\n" + "echo(%line);\n\n" + "// Always remember to close a file stream when finished\n" + "%fsObject.close();\n" + "@endtsexample\n\n" + + "@return String containing the line of data that was just read\n" + + "@see writeLine()") +{ + const char *str = object->readLine(); + return str ? str : ""; +} + +DefineEngineMethod( StreamObject, writeLine, void, ( const char* line ),, + "@brief Write a line to the stream, if it was opened for writing.\n\n" + + "There is no limit as to what kind of data you can write. Any format and data is allowable, not just text. " + "Be careful of what you write, as whitespace, current values, and literals will be preserved.\n\n" + + "@param line The data we are writing out to file." + + "@tsexample\n" + "// Create a file stream\n" + "%fsObject = new FileStreamObject();\n\n" + "// Open the file for writing\n" + "// If it does not exist, it is created. If it does exist, the file is cleared\n" + "%fsObject.open(\"./test.txt\", \"write\");\n\n" + "// Write a line to the file\n" + "%fsObject.writeLine(\"Hello World\");\n\n" + "// Write another line to the file\n" + "%fsObject.writeLine(\"Documentation Rocks!\");\n\n" + "// Always remember to close a file stream when finished\n" + "%fsObject.close();\n" + "@endtsexample\n\n" + + "@see readLine()") +{ + object->writeLine((U8 *)line); +} + +//----------------------------------------------------------------------------- +/* readSTString, readString, and readLongString need to be fixed or deprecated */ +DefineEngineMethod(StreamObject, readSTString, String, ( bool caseSensitive ), ( false ), + "@brief Read in a string and place it on the string table.\n\n" + "@param caseSensitive If false then case will not be taken into account when attempting " + "to match the read in string with what is already in the string table.\n" + "@return The string that was read from the stream.\n" + "@see writeString()" + + "@note When working with these particular string reading and writing methods, the stream " + "begins with the length of the string followed by the string itself, and does not include " + "a NULL terminator.") +{ + const char *str = object->readSTString(caseSensitive); + return str ? str : ""; +} + +DefineEngineMethod(StreamObject, readString, String, (),, + "@brief Read a string up to a maximum of 256 characters" + "@return The string that was read from the stream.\n" + "@see writeString()" + + "@note When working with these particular string reading and writing methods, the stream " + "begins with the length of the string followed by the string itself, and does not include " + "a NULL terminator.") +{ + const char *str = object->readString(); + return str ? str : ""; +} + +DefineEngineMethod(StreamObject, readLongString, String, ( S32 maxLength ),, + "@brief Read in a string up to the given maximum number of characters.\n\n" + "@param maxLength The maximum number of characters to read in.\n" + "@return The string that was read from the stream.\n" + "@see writeLongString()" + + "@note When working with these particular string reading and writing methods, the stream " + "begins with the length of the string followed by the string itself, and does not include " + "a NULL terminator.") +{ + const char *str = object->readLongString(maxLength); + return str ? str : ""; +} + +DefineEngineMethod(StreamObject, writeLongString, void, ( S32 maxLength, const char* string ),, + "@brief Write out a string up to the maximum number of characters.\n\n" + "@param maxLength The maximum number of characters that will be written.\n" + "@param string The string to write out to the stream.\n" + "@see readLongString()" + + "@note When working with these particular string reading and writing methods, the stream " + "begins with the length of the string followed by the string itself, and does not include " + "a NULL terminator.") +{ + object->writeLongString(maxLength, string); +} + +DefineEngineMethod(StreamObject, writeString, void, ( const char* string, S32 maxLength ), ( 256 ), + "@brief Write out a string with a default maximum length of 256 characters.\n\n" + "@param string The string to write out to the stream\n" + "@param maxLength The maximum string length to write out with a default of 256 characters. This " + "value should not be larger than 256 as it is written to the stream as a single byte.\n" + "@see readString()" + + "@note When working with these particular string reading and writing methods, the stream " + "begins with the length of the string followed by the string itself, and does not include " + "a NULL terminator.") +{ + object->writeString(string, maxLength); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod(StreamObject, copyFrom, bool, (SimObject* other),, + "@brief Copy from another StreamObject into this StreamObject\n\n" + "@param other The StreamObject to copy from.\n" + "@return True if the copy was successful.\n") +{ + StreamObject *so = dynamic_cast(other); + if(so == NULL) + return false; + + return object->copyFrom(so); +} diff --git a/Engine/source/core/stream/streamObject.h b/Engine/source/core/stream/streamObject.h new file mode 100644 index 000000000..b70fa236b --- /dev/null +++ b/Engine/source/core/stream/streamObject.h @@ -0,0 +1,138 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _STREAMOBJECT_H_ +#define _STREAMOBJECT_H_ + +#ifndef _SIMOBJECT_H_ +#include "console/simObject.h" +#endif + +/// @addtogroup zip_group +// @{ + +//----------------------------------------------------------------------------- +/// @brief Script wrapper for the Stream class +/// +/// It is not possible to instantiate StreamObject in script. Instead, +/// it is instantiated in C++ code and returned to script. +/// +/// This was mainly intended to allow the \ref zip_group "zip code" to +/// provide the stream interface to script. +//----------------------------------------------------------------------------- +class StreamObject : public SimObject +{ + typedef SimObject Parent; + +protected: + Stream *mStream; + +public: + StreamObject(); + StreamObject(Stream *stream); + virtual ~StreamObject(); + + DECLARE_CONOBJECT(StreamObject); + + virtual bool onAdd(); + + /// Set the stream to allow reuse of the object + void setStream(Stream *stream) { mStream = stream; } + + /// Get the underlying stream. Used with setStream() to support object reuse + Stream *getStream() { return mStream; } + + /// Gets a printable string form of the status + const char* getStatus(); + + bool isEOS() { return mStream ? mStream->getStatus() == Stream::EOS : true; } + + /// Gets the position in the stream + U32 getPosition() const + { + return mStream ? mStream->getPosition() : 0; + } + + /// Sets the position of the stream. Returns if the new position is valid or not + bool setPosition(const U32 in_newPosition) + { + return mStream ? mStream->setPosition(in_newPosition) : false; + } + + /// Gets the size of the stream + U32 getStreamSize() + { + return mStream ? mStream->getStreamSize() : 0; + } + + /// Reads a line from the stream. + const char * readLine(); + + /// Writes a line to the stream + void writeLine(U8 *buffer) + { + if(mStream) + mStream->writeLine(buffer); + } + + /// Reads a string and inserts it into the StringTable + /// @see StringTable + const char *readSTString(bool casesens = false) + { + return mStream ? mStream->readSTString(casesens) : NULL; + } + + /// Reads a string of maximum 255 characters long + const char *readString(); + /// Reads a string that could potentially be more than 255 characters long. + /// @param maxStringLen Maximum length to read. If the string is longer than maxStringLen, only maxStringLen bytes will be read. + /// @param stringBuf buffer where data is read into + const char * readLongString(U32 maxStringLen); + /// Writes a string to the stream. This function is slightly unstable. + /// Only use this if you have a valid string that is not empty. + /// writeString is safer. + void writeLongString(U32 maxStringLen, const char *string) + { + if(mStream) + mStream->writeLongString(maxStringLen, string); + } + + /// Writes a string to the stream. + void writeString(const char *stringBuf, S32 maxLen=255) + { + if(mStream) + mStream->writeString(stringBuf, maxLen); + } + + /// Copy the contents of another stream into this one + bool copyFrom(StreamObject *other) + { + if(mStream) + return mStream->copyFrom(other->getStream()); + + return false; + } +}; + +// @} + +#endif // _STREAMOBJECT_H_ diff --git a/Engine/source/core/stream/tStream.h b/Engine/source/core/stream/tStream.h new file mode 100644 index 000000000..5286116ac --- /dev/null +++ b/Engine/source/core/stream/tStream.h @@ -0,0 +1,373 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TSTREAM_H_ +#define _TSTREAM_H_ + +#ifndef _PLATFORM_H_ + #include "platform/platform.h" +#endif +#ifndef _TYPETRAITS_H_ + #include "platform/typetraits.h" +#endif + + +//TODO: error handling + + +/// @file +/// Definitions for lightweight componentized streaming. +/// +/// This file is an assembly of lightweight classes/interfaces that +/// describe various aspects of streaming classes. The advantage +/// over using Torque's Stream class is that very little requirements +/// are placed on implementations, that specific abilities can be +/// mixed and matched very selectively, and that complex stream processing +/// chains can be hidden behind very simple stream interfaces. + + + +/// Status of an asynchronous I/O operation. +enum EAsyncIOStatus +{ + ASYNC_IO_Pending, ///< I/O is still in queue or being processed. + ASYNC_IO_Complete, ///< I/O has completed successfully. + ASYNC_IO_Error ///< I/O has aborted with an error. +}; + +//----------------------------------------------------------------------------- +// Several component interfaces. +//----------------------------------------------------------------------------- + +/// Interface for streams with an explicit position property. +template< typename P = U32 > +class IPositionable +{ + public: + + typedef void Parent; + + /// The type used to indicate positions. + typedef P PositionType; + + /// @return the current position. + virtual PositionType getPosition() const = 0; + + /// Set the current position to be "pos". + /// @param pos The new position. + virtual void setPosition( PositionType pos ) = 0; +}; + +/// Interface for structures that allow their state to be reset. +class IResettable +{ + public: + + typedef void Parent; + + /// Reset to the initial state. + virtual void reset() = 0; +}; + +/// Interface for structures of finite size. +template< typename T = U32 > +class ISizeable +{ + public: + + typedef void Parent; + + /// The type used to indicate the structure's size. + typedef T SizeType; + + /// @return the size of the structure in number of elements. + virtual SizeType getSize() const = 0; +}; + +/// Interface for structures that represent processes. +class IProcess +{ + public: + + typedef void Parent; + + /// Start the process. + virtual void start() = 0; + + /// Stop the process. + virtual void stop() = 0; + + /// Pause the process. + virtual void pause() = 0; +}; + +/// Interface for objects that need continuous explicit updates from +/// an outside source. +class IPolled +{ + public: + + typedef void Parent; + + /// Update the object state. + virtual bool update() = 0; +}; + + +//----------------------------------------------------------------------------- +// IInputStream. +//----------------------------------------------------------------------------- + +/// An input stream delivers a sequence of elements of type "T". +/// +/// @note This stream has an inherent position property and is thus not +/// safe for concurrent access. +template< typename T > +class IInputStream +{ + public: + + typedef void Parent; + + /// The element type of this input stream. + typedef T ElementType; + + /// Read the next "num" elements into "buffer". + /// + /// @param buffer The buffer into which the elements are stored. + /// @param num Number of elements to read. + /// @return the number of elements actually read; this may be less than + /// "num" or even zero if no elements are available or reading failed. + virtual U32 read( ElementType* buffer, U32 num ) = 0; +}; + +/// An input stream over elements of type "T" that reads from +/// user-specified explicit offsets. +template< typename T, typename Offset = U32 > +class IOffsetInputStream +{ + public: + + typedef void Parent; + typedef Offset OffsetType; + typedef T ElementType; + + /// Read the next "num" elements at "offset" into "buffer". + /// + /// @param offset The offset in the stream from which to read. + /// @param buffer The buffer into which the elements are stored. + /// @param num Number of elements to read. + /// @return the number of elements actually read; this may be less than + /// "num" or even zero if no elements are available or reading failed. + virtual U32 readAt( OffsetType offset, T* buffer, U32 num ) = 0; +}; + +/// An input stream over elements of type "T" that works in +/// the background. +template< typename T, typename Offset = U32 > +class IAsyncInputStream +{ + public: + + typedef void Parent; + typedef Offset OffsetType; + typedef T ElementType; + + /// Queue a read of "num" elements at "offset" into "buffer". + /// + /// @param offset The offset in the stream from which to read. + /// @param buffer The buffer into which the elements are stored. + /// @param num Number of elements to read. + /// @return a handle for the asynchronous read operation or NULL if the + /// operation could not be queued. + virtual void* issueReadAt( OffsetType offset, T* buffer, U32 num ) = 0; + + /// Try moving the given asynchronous read operation to ASYNC_IO_Complete. + /// + /// @note This method invalidates the given handle. + /// + /// @param handle Handle returned by "issueReadAt". + /// @param outNumRead Reference that receives the number of bytes actually read in the + /// operation. + /// @param wait If true, the method waits until the given operation either fails or + /// completes successfully before returning. + /// @return the final operation status. + virtual EAsyncIOStatus tryCompleteReadAt( void* handle, U32& outNumRead, bool wait = false ) = 0; + + /// Cancel the given asynchronous read operation. + /// + /// @note This method invalidates the given handle. + /// + /// @param handle Handle returned by "issueReadAt". + virtual void cancelReadAt( void* handle ) = 0; +}; + +//----------------------------------------------------------------------------- +// IOutputStream. +//----------------------------------------------------------------------------- + +/// An output stream that writes elements of type "T". +/// +/// @note This stream has an inherent position property and is thus not +/// safe for concurrent access. +template< typename T > +class IOutputStream +{ + public: + + typedef void Parent; + + /// The element type of this input stream. + typedef T ElementType; + + /// Write "num" elements from "buffer" to the stream at its + /// current position. + /// + /// @param buffer The buffer from which to read elements. + /// @param num Number of elements to write. + virtual void write( const ElementType* buffer, U32 num ) = 0; +}; + +/// An output stream that writes elements of type "T" to a +/// user-specified explicit offset. +template< typename T, typename Offset = U32 > +class IOffsetOutputStream +{ + public: + + typedef void Parent; + typedef Offset OffsetType; + typedef T ElementType; + + /// Write "num" elements from "buffer" to the stream at "offset". + /// + /// @param offset The offset in the stream at which to write the elements. + /// @param buffer The buffer from which to read elements. + /// @param num Number of elements to write. + virtual void writeAt( OffsetType offset, const ElementType* buffer, U32 num ) = 0; +}; + +/// An output stream that writes elements of type "T" in the background. +template< typename T, typename Offset = U32 > +class IAsyncOutputStream +{ + public: + + typedef void Parent; + typedef Offset OffsetType; + typedef T ElementType; + + /// Queue a write operation of "num" elements from "buffer" to stream position + /// "offset". + /// + /// @param offset The offset in the stream at which to write the elements. + /// @param buffer The buffer from which to read elements. + /// @param num The number of elements to write. + /// @return a handle to the asynchronous write operatior or NULL if the operation + /// could not be queued. + virtual void* issueWriteAt( OffsetType offset, const ElementType* buffer, U32 num ) = 0; + + /// Try moving the given asynchronous write operation to ASYNC_IO_Complete. + /// + /// @note This method invalidates the given handle. + /// + /// @param handle Handle returned by "issueWriteAt". + /// @param wait If true, the method waits until the given operation either fails or + /// completes successfully before returning. + /// @return the final operation status. + virtual EAsyncIOStatus tryCompleteWriteAt( void* handle, bool wait = false ) = 0; + + /// Cancel the given asynchronous write operation. + /// + /// @note This method invalidates the given handle. + /// + /// @param handle Handle return by "issueWriteAt". + virtual void cancelWriteAt( void* handle ) = 0; +}; + +//----------------------------------------------------------------------------- +// IInputStreamFilter. +//----------------------------------------------------------------------------- + +/// An input stream filter takes an input stream "Stream" and processes it +/// into an input stream over type "To". +template< typename To, typename Stream > +class IInputStreamFilter : public IInputStream< To > +{ + public: + + typedef IInputStream< To > Parent; + + /// + typedef typename TypeTraits< Stream >::BaseType SourceStreamType; + + /// The element type of the source stream. + typedef typename SourceStreamType::ElementType SourceElementType; + + /// Construct a filter reading elements from "stream". + IInputStreamFilter( const Stream& stream ) + : mSourceStream( stream ) {} + + /// Return the stream from which this filter is reading its + /// source elements. + const Stream& getSourceStream() const { return mSourceStream; } + Stream& getSourceStream() { return mSourceStream; } + + private: + + Stream mSourceStream; +}; + +//----------------------------------------------------------------------------- +// IOutputStreamFilter. +//----------------------------------------------------------------------------- + +/// An output stream filter takes an output stream "Stream" and processes it +/// into an output stream over type "To". +template< typename To, class Stream > +class IOutputStreamFilter : public IOutputStream< To > +{ + public: + + typedef IOutputStream< To > Parent; + + /// + typedef typename TypeTraits< Stream >::BaseType TargetStreamType; + + /// The element type of the target stream. + typedef typename TargetStreamType::ElementType TargetElementType; + + /// Construct a filter writing elements to "stream". + IOutputStreamFilter( const Stream& stream ) + : mTargetStream( stream ) {} + + /// Return the stream to which this filter is writing its + /// elements. + const Stream& getTargetStream() const { return mTargetStream; } + Stream& getTargetStream() { return mTargetStream; } + + private: + + Stream mTargetStream; +}; + +#endif // _TSTREAM_H_ diff --git a/Engine/source/core/stringBuffer.cpp b/Engine/source/core/stringBuffer.cpp new file mode 100644 index 000000000..be2840153 --- /dev/null +++ b/Engine/source/core/stringBuffer.cpp @@ -0,0 +1,471 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/stringBuffer.h" +#include "core/frameAllocator.h" +#include "core/strings/unicode.h" +#include "core/strings/stringFunctions.h" + + +#if defined(TORQUE_DEBUG) + class StringBufferManager + { + public: + static StringBufferManager& getManager(); + Vector strings; + U64 request8; + U64 request16; + + StringBufferManager() + { + VECTOR_SET_ASSOCIATION( strings ); + } + + void add(StringBuffer* s); + void remove(StringBuffer* s); + void updateStats(); + void dumpStats(); + void dumpAllStrings(); + }; + + ConsoleFunction(sbmDumpStats, void, 1, 1, "") + { + StringBufferManager::getManager().dumpStats(); + } + + ConsoleFunction(sbmDumpStrings, void, 1, 1, "") + { + StringBufferManager::getManager().dumpAllStrings(); + } +#endif // TORQUE_DEBUG + + +#if defined(TORQUE_DEBUG) +#define SBMAddThisStringBuffer() \ + StringBufferManager::getManager().add(this); \ + rc = new RequestCounts; \ + clearRequestCounts() +#define incRequestCount8() rc->requestCount8++ +#define incRequestCount16() rc->requestCount16++ +#define decRequestCount16() rc->requestCount16-- +#else +#define SBMAddThisStringBuffer() +#define incRequestCount8() +#define incRequestCount16() +#define decRequestCount16() +#endif + +StringBuffer::StringBuffer() : mBuffer(), mBuffer8(), mDirty8(true) +{ + VECTOR_SET_ASSOCIATION( mBuffer ); + SBMAddThisStringBuffer(); + mBuffer.push_back(0); +}; + +/// Copy constructor. Very important. +StringBuffer::StringBuffer(const StringBuffer ©) : mBuffer(), mBuffer8() +{ + VECTOR_SET_ASSOCIATION( mBuffer ); + SBMAddThisStringBuffer(); + set(©); +}; + +StringBuffer::StringBuffer(const StringBuffer *in) +: mBuffer(), mBuffer8() +{ + VECTOR_SET_ASSOCIATION( mBuffer ); + SBMAddThisStringBuffer(); + set(in); +} + +StringBuffer::StringBuffer(const UTF8 *in) +: mBuffer(), mBuffer8() +{ + VECTOR_SET_ASSOCIATION( mBuffer ); + SBMAddThisStringBuffer(); + set(in); +} + +StringBuffer::StringBuffer(const UTF16 *in) +: mBuffer(), mBuffer8() +{ + VECTOR_SET_ASSOCIATION( mBuffer ); + SBMAddThisStringBuffer(); + set(in); +} + +//------------------------------------------------------------------------- + +StringBuffer::~StringBuffer() +{ + // Everything will get cleared up nicely cuz it's a vector. Sweet. + #if defined(TORQUE_DEBUG) + StringBufferManager::getManager().remove(this); + delete rc; + #endif + +} + +//------------------------------------------------------------------------- + +void StringBuffer::set(const StringBuffer *in) +{ + // Copy the vector. + mBuffer.setSize(in->mBuffer.size()); + dMemcpy(mBuffer.address(), in->mBuffer.address(), sizeof(UTF16) * mBuffer.size()); + mDirty8 = true; +} + +void StringBuffer::set(const UTF8 *in) +{ + incRequestCount8(); + // Convert and store. Note that a UTF16 version of the string cannot be longer. + FrameTemp tmpBuff(dStrlen(in)+1); + if(!in || in[0] == 0 || !convertUTF8toUTF16(in, tmpBuff, dStrlen(in)+1)) + { + // Easy out, it's a blank string, or a bad string. + mBuffer.clear(); + mBuffer.push_back(0); + AssertFatal(mBuffer.last() == 0, "StringBuffer::set UTF8 - not a null terminated string!"); + mDirty8 = true; + return; + } + + // Otherwise, we've a copy to do. (This might not be strictly necessary.) + mBuffer.setSize(dStrlen(tmpBuff)+1); + dMemcpy(mBuffer.address(), tmpBuff, sizeof(UTF16) * mBuffer.size()); + mBuffer.compact(); + AssertFatal(mBuffer.last() == 0, "StringBuffer::set UTF8 - not a null terminated string!"); + mDirty8 = true; +} + +void StringBuffer::set(const UTF16 *in) +{ + incRequestCount16(); + // Just copy, it's already UTF16. + U32 newsize = dStrlen(in); + mBuffer.setSize(newsize + 1); + dMemcpy(mBuffer.address(), in, sizeof(UTF16) * newsize); + mBuffer[newsize] = '\0'; + mBuffer.compact(); + AssertFatal(mBuffer.last() == 0, "StringBuffer::set UTF16 - not a null terminated string!"); + mDirty8 = true; +} + +//------------------------------------------------------------------------- + +void StringBuffer::append(const StringBuffer &in) +{ + append(in.mBuffer.address(), in.length()); +} + +void StringBuffer::append(const UTF8* in) +{ + incRequestCount8(); + decRequestCount16(); // because we're about to inc it when we go through append(utf16) + + // convert to UTF16, because that's our internal format. + // if the conversion fails, exit. + UTF16* tmp = convertUTF8toUTF16(in); + AssertFatal(tmp, "StringBuffer::append(UTF8) - could not convert UTF8 string!"); + if(!tmp) + return; + + append(tmp); + delete[] tmp; +} + +void StringBuffer::append(const UTF16* in) +{ + AssertFatal(in, "StringBuffer::append(UTF16) - null UTF16 string!"); + append(in, dStrlen(in)); +} + +void StringBuffer::append(const UTF16* in, const U32 len) +{ + incRequestCount16(); + + // Stick in onto the end of us - first make space. + U32 oldSize = length(); + mBuffer.increment(len); + + // Copy it in, ignoring both our terminator and theirs. + dMemcpy(&mBuffer[oldSize], in, sizeof(UTF16) * len); + + // Terminate the string. + mBuffer.last() = 0; + + // mark utf8 buffer dirty + mDirty8 = true; +} + +void StringBuffer::insert(const U32 charOffset, const StringBuffer &in) +{ + insert(charOffset, in.mBuffer.address(), in.length()); +} + +void StringBuffer::insert(const U32 charOffset, const UTF8* in) +{ + incRequestCount8(); + decRequestCount16(); + + // convert to UTF16, because that's our internal format. + // if the conversion fails, exit. + UTF16* tmp = convertUTF8toUTF16(in); + AssertFatal(tmp, "StringBuffer::insert(UTF8) - could not convert UTF8 string!"); + if(!tmp) + return; + + insert(charOffset, tmp); + delete[] tmp; +} + +void StringBuffer::insert(const U32 charOffset, const UTF16* in) +{ + AssertFatal(in, "StringBuffer::insert(UTF16) - null UTF16 string!"); + insert(charOffset, in, dStrlen(in)); +} + +void StringBuffer::insert(const U32 charOffset, const UTF16* in, const U32 len) +{ + incRequestCount16(); + + // Deal with append case. + if(charOffset >= length()) + { + append(in, len); + return; + } + + // Append was easy, now we have to do some work. + + // Copy everything we have that comes after charOffset past where the new + // string data will be. + + // Figure the number of UTF16's to copy, taking into account the possibility + // that we may be inserting a long string into a short string. + + // How many chars come after the insert point? Add 1 to deal with terminator. + const U32 copyCharLength = (S32)(length() - charOffset) + 1; + + // Make some space in our buffer. We only need in.length() more as we + // will be dropping one of the two terminators present in this operation. + mBuffer.increment(len); + + for(S32 i=copyCharLength-1; i>=0; i--) + mBuffer[charOffset+i+len] = mBuffer[charOffset+i]; + + // Can't copy directly: memcpy behavior is undefined if src and dst overlap. + //dMemcpy(&mBuffer[charOffset+len], &mBuffer[charOffset], sizeof(UTF16) * copyCharLength); + + // And finally copy the new data in, not including its terminator. + dMemcpy(&mBuffer[charOffset], in, sizeof(UTF16) * len); + + // All done! + AssertFatal(mBuffer.last() == 0, "StringBuffer::insert - not a null terminated string!"); + + // mark utf8 buffer dirty + mDirty8 = true; +} + +StringBuffer StringBuffer::substring(const U32 start, const U32 len) const +{ + // Deal with bonehead user input. + if(start >= length() || len == 0) + { + // Either they asked beyond the end of the string or we're null. Either + // way, let's give them a null string. + return StringBuffer(""); + } + + AssertFatal(start < length(), "StringBuffer::substring - invalid start!"); + AssertFatal(start+len <= length(), "StringBuffer::substring - invalid len!"); + AssertFatal(len > 0, "StringBuffer::substring - len must be >= 1."); + + StringBuffer tmp; + tmp.mBuffer.clear(); + for(S32 i=0; isubstring(start, len); + return sub.createCopy8(); +} + +void StringBuffer::cut(const U32 start, const U32 len) +{ + AssertFatal(start < length(), "StringBuffer::cut - invalid start!"); + AssertFatal(start+len <= length(), "StringBuffer::cut - invalid len!"); + AssertFatal(len > 0, "StringBuffer::cut - len >= 1."); + + AssertFatal(mBuffer.last() == 0, "StringBuffer::cut - not a null terminated string! (pre)"); + + // Now snip things. + for(S32 i=start; i(this)->updateBuffer8(); + return mBuffer8.address(); +} + +void StringBuffer::updateBuffer8() +{ + U32 slackLen = getUTF8BufferSizeEstimate(); + mBuffer8.setSize(slackLen); + U32 len = convertUTF16toUTF8(mBuffer.address(), mBuffer8.address(), slackLen); + mBuffer8.setSize(len+1); + mBuffer8.compact(); + mDirty8 = false; +} + + +#if defined(TORQUE_DEBUG) +StringBufferManager& StringBufferManager::getManager() +{ + static StringBufferManager _sbm; return _sbm; +} + +void StringBufferManager::add(StringBuffer* s) +{ + strings.push_back(s); +} + +void StringBufferManager::remove(StringBuffer* s) +{ + U32 nstrings = strings.size() - 1; + for(S32 i = nstrings; i >= 0; i--) + if(strings[i] == s) + strings.erase_fast(i); +} + +void StringBufferManager::updateStats() +{ + request8 = 0; + request16 = 0; + U32 nstrings = strings.size(); + for(int i=0; i < nstrings; i++) + { + request8 += strings[i]->rc->requestCount8; + request16 += strings[i]->rc->requestCount16; + } +} + +void StringBufferManager::dumpStats() +{ + updateStats(); + Con::printf("===== String Manager Stats ====="); + Con::printf(" strings: %i", strings.size()); + Con::printf(" utf8 requests: %Lu", request8); + Con::printf(" utf16 requests: %Lu", request16); +} + +void StringBufferManager::dumpAllStrings() +{ + U32 nstrings = strings.size(); + Con::printf("===== String Manager: All Strings ====="); + Con::printf(" utf8 | utf16 | string"); + for(int i=0; i < nstrings; i++) + { + UTF8* tmp = strings[i]->createCopy8(); + strings[i]->rc->requestCount8--; + Con::printf("%5llu %5llu \"%s\"", strings[i]->rc->requestCount8, strings[i]->rc->requestCount16, tmp); + delete[] tmp; + } +} + +#endif diff --git a/Engine/source/core/stringBuffer.h b/Engine/source/core/stringBuffer.h new file mode 100644 index 000000000..003d4a491 --- /dev/null +++ b/Engine/source/core/stringBuffer.h @@ -0,0 +1,134 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _STRINGBUFFER_H_ +#define _STRINGBUFFER_H_ + +//Includes +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +#include "console/console.h" + +/// Utility class to wrap string manipulation in a representation +/// independent way. +/// +/// Length does NOT include the null terminator. +class StringBuffer +{ + Vector mBuffer; + mutable Vector mBuffer8; + mutable bool mDirty8; + +public: + #if defined(TORQUE_DEBUG) + struct RequestCounts + { + U64 requestCount8; + U64 requestCount16; + }; + RequestCounts *rc; + #endif + + StringBuffer(); + StringBuffer(const StringBuffer ©); + StringBuffer(const StringBuffer *in); + StringBuffer(const UTF8 *in); + StringBuffer(const UTF16 *in); + + ~StringBuffer(); + + void append(const StringBuffer &in); + void append(const UTF8* in); + void append(const UTF16* in); + void append(const UTF16* in, U32 len); + + void insert(const U32 charOffset, const StringBuffer &in); + void insert(const U32 charOffset, const UTF8* in); + void insert(const U32 charOffset, const UTF16* in); + void insert(const U32 charOffset, const UTF16* in, const U32 len); + + /// Get a StringBuffer substring of length 'len' starting from 'start'. + /// Returns a new StringBuffer by value; + StringBuffer substring(const U32 start, const U32 len) const; + + /// Get a pointer to a substring of length 'len' starting from 'start'. + /// Returns a raw pointer to a unicode string. + /// You must delete[] the returned string when you are done with it. + /// This follows the "create rule". + UTF8* createSubstring8(const U32 start, const U32 len) const; + UTF16* createSubstring16(const U32 start, const U32 len) const; + + void cut(const U32 start, const U32 len); +// UTF8* cut8(const U32 start, const U32 len); +// UTF16* cut16(const U32 start, const U32 len); + + const UTF16 getChar(const U32 offset) const; + void setChar(const U32 offset, UTF16 c); + + void set(const StringBuffer *in); + void set(const UTF8 *in); + void set(const UTF16 *in); + + inline const U32 length() const + { + return mBuffer.size() - 1; // Don't count the NULL of course. + } + + /// Get an upper bound size estimate for a UTF8 buffer to hold this + /// string. + const U32 getUTF8BufferSizeEstimate() const + { + return length() * 3 + 1; + } + + void getCopy8(UTF8 *buff, const U32 buffSize) const; + void getCopy(UTF16 *buff, const U32 buffSize) const; + + /// Get a copy of the contents of the string buffer. + /// You must delete[] the returned copy when you are done with it. + /// This follows the "create rule". + UTF8* createCopy8() const; + UTF16* createCopy() const; + + /// Get a pointer to the StringBuffer's data store. + /// Use this in situations where you can be sure that the StringBuffer will + /// not be modified out from under you. + /// The win here is, you avoid yet another data copy. Data copy is slow on + /// most modern hardware. + const UTF16* getPtr() const; + const UTF8* getPtr8() const; + +private: + void updateBuffer8(); + #if defined(TORQUE_DEBUG) + void clearRequestCounts() { rc->requestCount16 = 0; rc->requestCount8 = 0; } + #endif + +}; + +#endif \ No newline at end of file diff --git a/Engine/source/core/stringTable.cpp b/Engine/source/core/stringTable.cpp new file mode 100644 index 000000000..3c2ff86f8 --- /dev/null +++ b/Engine/source/core/stringTable.cpp @@ -0,0 +1,233 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/strings/stringFunctions.h" +#include "core/stringTable.h" + +_StringTable *_gStringTable = NULL; +const U32 _StringTable::csm_stInitSize = 29; + +//--------------------------------------------------------------- +// +// StringTable functions +// +//--------------------------------------------------------------- + +namespace { +bool sgInitTable = true; +U8 sgHashTable[256]; + +void initTolowerTable() +{ + for (U32 i = 0; i < 256; i++) { + U8 c = dTolower(i); + sgHashTable[i] = c * c; + } + + sgInitTable = false; +} + +} // namespace {} + +U32 _StringTable::hashString(const char* str) +{ + if (sgInitTable) + initTolowerTable(); + + if(!str) return -1; + + U32 ret = 0; + char c; + while((c = *str++) != 0) { + ret <<= 1; + ret ^= sgHashTable[static_cast(c)]; + } + return ret; +} + +U32 _StringTable::hashStringn(const char* str, S32 len) +{ + if (sgInitTable) + initTolowerTable(); + + U32 ret = 0; + char c; + while((c = *str++) != 0 && len--) { + ret <<= 1; + ret ^= sgHashTable[static_cast(c)]; + } + return ret; +} + +//-------------------------------------- +_StringTable::_StringTable() +{ + buckets = (Node **) dMalloc(csm_stInitSize * sizeof(Node *)); + for(U32 i = 0; i < csm_stInitSize; i++) { + buckets[i] = 0; + } + + numBuckets = csm_stInitSize; + itemCount = 0; +} + +//-------------------------------------- +_StringTable::~_StringTable() +{ + dFree(buckets); +} + + +//-------------------------------------- +void _StringTable::create() +{ + //AssertFatal(_gStringTable == NULL, "StringTable::create: StringTable already exists."); + if(!_gStringTable) + { + _gStringTable = new _StringTable; + _gStringTable->_EmptyString = _gStringTable->insert(""); + } +} + + +//-------------------------------------- +void _StringTable::destroy() +{ + AssertFatal(StringTable != NULL, "StringTable::destroy: StringTable does not exist."); + delete _gStringTable; + _gStringTable = NULL; +} + + +//-------------------------------------- +StringTableEntry _StringTable::insert(const char* _val, const bool caseSens) +{ + // Added 3/29/2007 -- If this is undesirable behavior, let me know -patw + const char *val = _val; + if( val == NULL ) + val = ""; + //- + + Node **walk, *temp; + U32 key = hashString(val); + walk = &buckets[key % numBuckets]; + while((temp = *walk) != NULL) { + if(caseSens && !dStrcmp(temp->val, val)) + return temp->val; + else if(!caseSens && !dStricmp(temp->val, val)) + return temp->val; + walk = &(temp->next); + } + char *ret = 0; + if(!*walk) { + *walk = (Node *) mempool.alloc(sizeof(Node)); + (*walk)->next = 0; + (*walk)->val = (char *) mempool.alloc(dStrlen(val) + 1); + dStrcpy((*walk)->val, val); + ret = (*walk)->val; + itemCount ++; + } + if(itemCount > 2 * numBuckets) { + resize(4 * numBuckets - 1); + } + return ret; +} + +//-------------------------------------- +StringTableEntry _StringTable::insertn(const char* src, S32 len, const bool caseSens) +{ + char val[256]; + AssertFatal(len < 255, "Invalid string to insertn"); + dStrncpy(val, src, len); + val[len] = 0; + return insert(val, caseSens); +} + +//-------------------------------------- +StringTableEntry _StringTable::lookup(const char* val, const bool caseSens) +{ + Node **walk, *temp; + U32 key = hashString(val); + walk = &buckets[key % numBuckets]; + while((temp = *walk) != NULL) { + if(caseSens && !dStrcmp(temp->val, val)) + return temp->val; + else if(!caseSens && !dStricmp(temp->val, val)) + return temp->val; + walk = &(temp->next); + } + return NULL; +} + +//-------------------------------------- +StringTableEntry _StringTable::lookupn(const char* val, S32 len, const bool caseSens) +{ + Node **walk, *temp; + U32 key = hashStringn(val, len); + walk = &buckets[key % numBuckets]; + while((temp = *walk) != NULL) { + if(caseSens && !dStrncmp(temp->val, val, len) && temp->val[len] == 0) + return temp->val; + else if(!caseSens && !dStrnicmp(temp->val, val, len) && temp->val[len] == 0) + return temp->val; + walk = &(temp->next); + } + return NULL; +} + +//-------------------------------------- +void _StringTable::resize(const U32 newSize) +{ + Node *head = NULL, *walk, *temp; + U32 i; + // reverse individual bucket lists + // we do this because new strings are added at the end of bucket + // lists so that case sens strings are always after their + // corresponding case insens strings + + for(i = 0; i < numBuckets; i++) { + walk = buckets[i]; + while(walk) + { + temp = walk->next; + walk->next = head; + head = walk; + walk = temp; + } + } + buckets = (Node **) dRealloc(buckets, newSize * sizeof(Node)); + for(i = 0; i < newSize; i++) { + buckets[i] = 0; + } + numBuckets = newSize; + walk = head; + while(walk) { + U32 key; + Node *temp = walk; + + walk = walk->next; + key = hashString(temp->val); + temp->next = buckets[key % newSize]; + buckets[key % newSize] = temp; + } +} + diff --git a/Engine/source/core/stringTable.h b/Engine/source/core/stringTable.h new file mode 100644 index 000000000..d37be016f --- /dev/null +++ b/Engine/source/core/stringTable.h @@ -0,0 +1,176 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _STRINGTABLE_H_ +#define _STRINGTABLE_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#ifndef _DATACHUNKER_H_ +#include "core/dataChunker.h" +#endif + + +//-------------------------------------- +/// A global table for the hashing and tracking of strings. +/// +/// Only one _StringTable is ever instantiated in Torque. It is accessible via the +/// global variable StringTable. +/// +/// StringTable is used to manage strings in Torque. It performs the following tasks: +/// - Ensures that only one pointer is ever used for a given string (through +/// insert()). +/// - Allows the lookup of a string in the table. +/// +/// @code +/// // Adding a string to the StringTable. +/// StringTableEntry mRoot; +/// mRoot = StringTable->insert(root); +/// +/// // Looking up a string in the StringTable. +/// StringTableEntry stName = StringTable->lookupn(name, len); +/// +/// // Comparing two strings in the StringTable (see below). +/// if(mRoot == stName) Con::printf("These strings are equal!"); +/// @endcode +/// +/// But why is this useful, you ask? Because every string that's run through the +/// StringTable is stored once and only once, every string has one and only one +/// pointer mapped to it. As a pointer is an integer value (usually an unsigned int), +/// so we can do several neat things: +/// - StringTableEntrys can be compared directly for equality, instead of using +/// the time-consuming dStrcmp() or dStricmp() function. +/// - For things like object names, we can avoid storing multiple copies of the +/// string containing the name. The StringTable ensures that we only ever store +/// one copy. +/// - When we're doing lookups by name (for instances, of resources), we can determine +/// if the object is even registered in the system by looking up its name in the +/// StringTable. Then, we can use the pointer as a hash key. +/// +/// The scripting engine and the resource manager are the primary users of the +/// StringTable. +/// +/// @note Be aware that the StringTable NEVER DEALLOCATES memory, so be careful when you +/// add strings to it. If you carelessly add many strings, you will end up wasting +/// space. +class _StringTable +{ +private: + /// @name Implementation details + /// @{ + + /// This is internal to the _StringTable class. + struct Node + { + char *val; + Node *next; + }; + + Node** buckets; + U32 numBuckets; + U32 itemCount; + DataChunker mempool; + + StringTableEntry _EmptyString; + + protected: + static const U32 csm_stInitSize; + + _StringTable(); + ~_StringTable(); + + /// @} + public: + + /// Initialize StringTable. + /// + /// This is called at program start to initialize the StringTable global. + static void create(); + + /// Destroy the StringTable + /// + /// This is called at program end to destroy the StringTable global. + static void destroy(); + + /// Get a pointer from the string table, adding the string to the table + /// if it was not already present. + /// + /// @param string String to check in the table (and add). + /// @param caseSens Determines whether case matters. + StringTableEntry insert(const char *string, bool caseSens = false); + + /// Get a pointer from the string table, adding the string to the table + /// if it was not already present. + /// + /// @param string String to check in the table (and add). + /// @param len Length of the string in bytes. + /// @param caseSens Determines whether case matters. + StringTableEntry insertn(const char *string, S32 len, bool caseSens = false); + + /// Get a pointer from the string table, NOT adding the string to the table + /// if it was not already present. + /// + /// @param string String to check in the table (but not add). + /// @param caseSens Determines whether case matters. + StringTableEntry lookup(const char *string, bool caseSens = false); + + /// Get a pointer from the string table, NOT adding the string to the table + /// if it was not already present. + /// + /// @param string String to check in the table (but not add). + /// @param len Length of string in bytes. + /// @param caseSens Determines whether case matters. + StringTableEntry lookupn(const char *string, S32 len, bool caseSens = false); + + + /// Resize the StringTable to be able to hold newSize items. This + /// is called automatically by the StringTable when the table is + /// full past a certain threshhold. + /// + /// @param newSize Number of new items to allocate space for. + void resize(const U32 newSize); + + /// Hash a string into a U32. + static U32 hashString(const char* in_pString); + + /// Hash a string of given length into a U32. + static U32 hashStringn(const char* in_pString, S32 len); + + /// Represents a zero length string. + StringTableEntry EmptyString() const { return _EmptyString; } +}; + + +extern _StringTable *_gStringTable; + +inline _StringTable* _getStringTable() +{ + if(_gStringTable == NULL) + _StringTable::create(); + return _gStringTable; +} + +#define StringTable _getStringTable() + +#endif //_STRINGTABLE_H_ + diff --git a/Engine/source/core/strings/findMatch.cpp b/Engine/source/core/strings/findMatch.cpp new file mode 100644 index 000000000..c41dd9230 --- /dev/null +++ b/Engine/source/core/strings/findMatch.cpp @@ -0,0 +1,163 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/strings/findMatch.h" +#include "core/strings/stringFunctions.h" + + +//-------------------------------------------------------------------------------- +// NAME +// FindMatch::FindMatch( const char *_expression, S32 maxNumMatches ) +// +// DESCRIPTION +// Class to match regular expressions (file names) +// only works with '*','?', and 'chars' +// +// ARGUMENTS +// _expression - The regular expression you intend to match (*.??abc.bmp) +// _maxMatches - The maximum number of strings you wish to match. +// +// RETURNS +// +// NOTES +// +//-------------------------------------------------------------------------------- + +FindMatch::FindMatch( U32 _maxMatches ) +{ + VECTOR_SET_ASSOCIATION(matchList); + + expression = NULL; + maxMatches = _maxMatches; + matchList.reserve( maxMatches ); +} + +FindMatch::FindMatch( char *_expression, U32 _maxMatches ) +{ + VECTOR_SET_ASSOCIATION(matchList); + + expression = NULL; + setExpression( _expression ); + maxMatches = _maxMatches; + matchList.reserve( maxMatches ); +} + +FindMatch::~FindMatch() +{ + delete [] expression; + matchList.clear(); +} + +void FindMatch::setExpression( const char *_expression ) +{ + delete [] expression; + + expression = new char[dStrlen(_expression) + 1]; + dStrcpy(expression, _expression); + dStrupr(expression); +} + +bool FindMatch::findMatch( const char *str, bool caseSensitive ) +{ + if ( isFull() ) + return false; + + char nstr[512]; + dStrcpy( nstr,str ); + dStrupr(nstr); + if ( isMatch( expression, nstr, caseSensitive ) ) + { + matchList.push_back( (char*)str ); + return true; + } + return false; +} + +inline bool IsCharMatch( char e, char s, bool caseSensitive ) +{ + return ( ( e == '?' ) || ( caseSensitive && e == s ) || ( dToupper(e) == dToupper(s) ) ); +} + +bool FindMatch::isMatch( const char *exp, const char *str, bool caseSensitive ) +{ + while ( *str && ( *exp != '*' ) ) + { + if ( !IsCharMatch( *exp++, *str++, caseSensitive ) ) + return false; + } + + const char* cp = NULL; + const char* mp = NULL; + + while ( *str ) + { + if ( *exp == '*' ) + { + if ( !*++exp ) + return true; + + mp = exp; + cp = str+1; + } + else if ( IsCharMatch( *exp, *str, caseSensitive ) ) + { + exp++; + str++; + } + else + { + exp = mp; + str = cp++; + } + } + + while ( *exp == '*' ) + exp++; + + return !*exp; +} + + +bool FindMatch::isMatchMultipleExprs( const char *exps, const char *str, bool caseSensitive ) +{ + char *tok = 0; + int len = dStrlen(exps); + + char *e = new char[len+1]; + dStrcpy(e,exps); + + // [tom, 12/18/2006] This no longer supports space separated expressions as + // they don't work when the paths have spaces in. + + // search for each expression. return true soon as we see one. + for( tok = dStrtok(e,"\t"); tok != NULL; tok = dStrtok(NULL,"\t")) + { + if( isMatch( tok, str, caseSensitive) ) + { + delete []e; + return true; + } + } + + delete []e; + return false; +} diff --git a/Engine/source/core/strings/findMatch.h b/Engine/source/core/strings/findMatch.h new file mode 100644 index 000000000..ea964ea6e --- /dev/null +++ b/Engine/source/core/strings/findMatch.h @@ -0,0 +1,63 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FINDMATCH_H_ +#define _FINDMATCH_H_ + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +class FindMatch +{ + char* expression; + U32 maxMatches; + + public: + static bool isMatch( const char *exp, const char *string, bool caseSensitive = false ); + static bool isMatchMultipleExprs( const char *exps, const char *str, bool caseSensitive ); + Vector matchList; + + FindMatch( U32 _maxMatches = 256 ); + FindMatch( char *_expression, U32 _maxMatches = 256 ); + ~FindMatch(); + + bool findMatch(const char *string, bool caseSensitive = false); + void setExpression( const char *_expression ); + + S32 numMatches() const + { + return(matchList.size()); + } + + bool isFull() const + { + return (matchList.size() >= S32(maxMatches)); + } + + void clear() + { + matchList.clear(); + } +}; + +#endif // _FINDMATCH_H_ diff --git a/Engine/source/core/strings/stringFunctions.cpp b/Engine/source/core/strings/stringFunctions.cpp new file mode 100644 index 000000000..25c7e22b1 --- /dev/null +++ b/Engine/source/core/strings/stringFunctions.cpp @@ -0,0 +1,534 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include +#include + +#include "core/strings/stringFunctions.h" +#include "platform/platform.h" + + +#if defined(TORQUE_OS_WIN32) || defined(TORQUE_OS_XBOX) || defined(TORQUE_OS_XENON) +// This standard function is not defined when compiling with VC7... +#define vsnprintf _vsnprintf +#endif + + +//----------------------------------------------------------------------------- + +// Original code from: http://sourcefrog.net/projects/natsort/ +// Somewhat altered here. +//TODO: proper UTF8 support; currently only working for single-byte characters + +/* -*- mode: c; c-file-style: "k&r" -*- + + strnatcmp.c -- Perform 'natural order' comparisons of strings in C. + Copyright (C) 2000, 2004 by Martin Pool + + 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. +*/ + + +/* partial change history: + * + * 2004-10-10 mbp: Lift out character type dependencies into macros. + * + * Eric Sosman pointed out that ctype functions take a parameter whose + * value must be that of an unsigned int, even on platforms that have + * negative chars in their default char type. + */ + +typedef char nat_char; + +/* These are defined as macros to make it easier to adapt this code to + * different characters types or comparison functions. */ +static inline int +nat_isdigit( nat_char a ) +{ + return dIsdigit( a ); +} + + +static inline int +nat_isspace( nat_char a ) +{ + return dIsspace( a ); +} + + +static inline nat_char +nat_toupper( nat_char a ) +{ + return dToupper( a ); +} + + + +static int +compare_right(const nat_char* a, const nat_char* b) +{ + int bias = 0; + + /* The longest run of digits wins. That aside, the greatest + value wins, but we can't know that it will until we've scanned + both numbers to know that they have the same magnitude, so we + remember it in BIAS. */ + for (;; a++, b++) { + if (!nat_isdigit(*a) && !nat_isdigit(*b)) + return bias; + else if (!nat_isdigit(*a)) + return -1; + else if (!nat_isdigit(*b)) + return +1; + else if (*a < *b) { + if (!bias) + bias = -1; + } else if (*a > *b) { + if (!bias) + bias = +1; + } else if (!*a && !*b) + return bias; + } + + return 0; +} + + +static int +compare_left(const nat_char* a, const nat_char* b) +{ + /* Compare two left-aligned numbers: the first to have a + different value wins. */ + for (;; a++, b++) { + if (!nat_isdigit(*a) && !nat_isdigit(*b)) + return 0; + else if (!nat_isdigit(*a)) + return -1; + else if (!nat_isdigit(*b)) + return +1; + else if (*a < *b) + return -1; + else if (*a > *b) + return +1; + } + + return 0; +} + + +static int strnatcmp0(const nat_char* a, const nat_char* b, int fold_case) +{ + int ai, bi; + nat_char ca, cb; + int fractional, result; + + ai = bi = 0; + while (1) { + ca = a[ai]; cb = b[bi]; + + /* skip over leading spaces or zeros */ + while (nat_isspace(ca)) + ca = a[++ai]; + + while (nat_isspace(cb)) + cb = b[++bi]; + + /* process run of digits */ + if (nat_isdigit(ca) && nat_isdigit(cb)) { + fractional = (ca == '0' || cb == '0'); + + if (fractional) { + if ((result = compare_left(a+ai, b+bi)) != 0) + return result; + } else { + if ((result = compare_right(a+ai, b+bi)) != 0) + return result; + } + } + + if (!ca && !cb) { + /* The strings compare the same. Perhaps the caller + will want to call strcmp to break the tie. */ + return 0; + } + + if (fold_case) { + ca = nat_toupper(ca); + cb = nat_toupper(cb); + } + + if (ca < cb) + return -1; + else if (ca > cb) + return +1; + + ++ai; ++bi; + } +} + + +int dStrnatcmp(const nat_char* a, const nat_char* b) { + return strnatcmp0(a, b, 0); +} + + +/* Compare, recognizing numeric string and ignoring case. */ +int dStrnatcasecmp(const nat_char* a, const nat_char* b) { + return strnatcmp0(a, b, 1); +} + +//------------------------------------------------------------------------------ +// non-standard string functions + +char *dStrdup_r(const char *src, const char *fileName, dsize_t lineNumber) +{ + char *buffer = (char *) dMalloc_r(dStrlen(src) + 1, fileName, lineNumber); + dStrcpy(buffer, src); + return buffer; +} + +char* dStrichr( char* str, char ch ) +{ + AssertFatal( str != NULL, "dStrichr - NULL string" ); + + if( !ch ) + return dStrchr( str, ch ); + + char c = dToupper( ch ); + while( *str ) + { + if( dToupper( *str ) == c ) + return str; + + ++ str; + } + + return NULL; +} + +const char* dStrichr( const char* str, char ch ) +{ + AssertFatal( str != NULL, "dStrichr - NULL string" ); + + if( !ch ) + return dStrchr( str, ch ); + + char c = dToupper( ch ); + while( *str ) + { + if( dToupper( *str ) == c ) + return str; + + ++ str; + } + + return NULL; +} + +// concatenates a list of src's onto the end of dst +// the list of src's MUST be terminated by a NULL parameter +// dStrcatl(dst, sizeof(dst), src1, src2, NULL); +char* dStrcatl(char *dst, dsize_t dstSize, ...) +{ + const char* src = NULL; + char *p = dst; + + AssertFatal(dstSize > 0, "dStrcatl: destination size is set zero"); + dstSize--; // leave room for string termination + + // find end of dst + while (dstSize && *p++) + dstSize--; + + va_list args; + va_start(args, dstSize); + + // concatenate each src to end of dst + while ( (src = va_arg(args, const char*)) != NULL ) + { + while( dstSize && *src ) + { + *p++ = *src++; + dstSize--; + } + } + + va_end(args); + + // make sure the string is terminated + *p = 0; + + return dst; +} + + +// copy a list of src's into dst +// the list of src's MUST be terminated by a NULL parameter +// dStrccpyl(dst, sizeof(dst), src1, src2, NULL); +char* dStrcpyl(char *dst, dsize_t dstSize, ...) +{ + const char* src = NULL; + char *p = dst; + + AssertFatal(dstSize > 0, "dStrcpyl: destination size is set zero"); + dstSize--; // leave room for string termination + + va_list args; + va_start(args, dstSize); + + // concatenate each src to end of dst + while ( (src = va_arg(args, const char*)) != NULL ) + { + while( dstSize && *src ) + { + *p++ = *src++; + dstSize--; + } + } + + va_end(args); + + // make sure the string is terminated + *p = 0; + + return dst; +} + + +int dStrcmp( const UTF16 *str1, const UTF16 *str2) +{ +#if defined(TORQUE_OS_WIN32) || defined(TORQUE_OS_XBOX) || defined(TORQUE_OS_XENON) + return wcscmp( reinterpret_cast( str1 ), reinterpret_cast( str2 ) ); +#else + int ret; + const UTF16 *a, *b; + a = str1; + b = str2; + + while( ((ret = *a - *b) == 0) && *a && *b ) + a++, b++; + + return ret; +#endif +} + +char* dStrupr(char *str) +{ +#if defined(TORQUE_OS_WIN32) || defined(TORQUE_OS_XBOX) || defined(TORQUE_OS_XENON) + return _strupr(str); +#else + if (str == NULL) + return(NULL); + + char* saveStr = str; + while (*str) + { + *str = toupper(*str); + str++; + } + return saveStr; +#endif +} + +char* dStrlwr(char *str) +{ +#if defined(TORQUE_OS_WIN32) || defined(TORQUE_OS_XBOX) || defined(TORQUE_OS_XENON) + return _strlwr(str); +#else + if (str == NULL) + return(NULL); + + char* saveStr = str; + while (*str) + { + *str = tolower(*str); + str++; + } + return saveStr; +#endif +} + +//------------------------------------------------------------------------------ +// standard I/O functions + +void dPrintf(const char *format, ...) +{ + va_list args; + va_start(args, format); + vprintf(format, args); +} + +S32 dVprintf(const char *format, void *arglist) +{ + return vprintf(format, (char*)arglist); +} + +S32 dSprintf(char *buffer, U32 bufferSize, const char *format, ...) +{ + va_list args; + va_start(args, format); + + S32 len = vsnprintf(buffer, bufferSize, format, args); + + AssertWarn( len < bufferSize, "Buffer too small in call to dSprintf!" ); + + return (len); +} + + +S32 dVsprintf(char *buffer, U32 bufferSize, const char *format, void *arglist) +{ + S32 len = vsnprintf(buffer, bufferSize, format, (char*)arglist); + + AssertWarn( len < bufferSize, "Buffer too small in call to dVsprintf!" ); + + return (len); +} + + +S32 dSscanf(const char *buffer, const char *format, ...) +{ +#if defined(TORQUE_OS_WIN32) || defined(TORQUE_OS_XBOX) || defined(TORQUE_OS_XENON) + va_list args; + va_start(args, format); + + // Boy is this lame. We have to scan through the format string, and find out how many + // arguments there are. We'll store them off as void*, and pass them to the sscanf + // function through specialized calls. We're going to have to put a cap on the number of args that + // can be passed, 8 for the moment. Sigh. + static void* sVarArgs[20]; + U32 numArgs = 0; + + for (const char* search = format; *search != '\0'; search++) { + if (search[0] == '%' && search[1] != '%') + numArgs++; + } + AssertFatal(numArgs <= 20, "Error, too many arguments to lame implementation of dSscanf. Fix implmentation"); + + // Ok, we have the number of arguments... + for (U32 i = 0; i < numArgs; i++) + sVarArgs[i] = va_arg(args, void*); + va_end(args); + + switch (numArgs) { + case 0: return 0; + case 1: return sscanf(buffer, format, sVarArgs[0]); + case 2: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1]); + case 3: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2]); + case 4: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3]); + case 5: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4]); + case 6: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5]); + case 7: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6]); + case 8: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6], sVarArgs[7]); + case 9: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6], sVarArgs[7], sVarArgs[8]); + case 10: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6], sVarArgs[7], sVarArgs[8], sVarArgs[9]); + case 11: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6], sVarArgs[7], sVarArgs[8], sVarArgs[9], sVarArgs[10]); + case 12: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6], sVarArgs[7], sVarArgs[8], sVarArgs[9], sVarArgs[10], sVarArgs[11]); + case 13: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6], sVarArgs[7], sVarArgs[8], sVarArgs[9], sVarArgs[10], sVarArgs[11], sVarArgs[12]); + case 14: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6], sVarArgs[7], sVarArgs[8], sVarArgs[9], sVarArgs[10], sVarArgs[11], sVarArgs[12], sVarArgs[13]); + case 15: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6], sVarArgs[7], sVarArgs[8], sVarArgs[9], sVarArgs[10], sVarArgs[11], sVarArgs[12], sVarArgs[13], sVarArgs[14]); + case 16: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6], sVarArgs[7], sVarArgs[8], sVarArgs[9], sVarArgs[10], sVarArgs[11], sVarArgs[12], sVarArgs[13], sVarArgs[14], sVarArgs[15]); + case 17: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6], sVarArgs[7], sVarArgs[8], sVarArgs[9], sVarArgs[10], sVarArgs[11], sVarArgs[12], sVarArgs[13], sVarArgs[14], sVarArgs[15], sVarArgs[16]); + case 18: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6], sVarArgs[7], sVarArgs[8], sVarArgs[9], sVarArgs[10], sVarArgs[11], sVarArgs[12], sVarArgs[13], sVarArgs[14], sVarArgs[15], sVarArgs[16], sVarArgs[17]); + case 19: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6], sVarArgs[7], sVarArgs[8], sVarArgs[9], sVarArgs[10], sVarArgs[11], sVarArgs[12], sVarArgs[13], sVarArgs[14], sVarArgs[15], sVarArgs[16], sVarArgs[17], sVarArgs[18]); + case 20: return sscanf(buffer, format, sVarArgs[0], sVarArgs[1], sVarArgs[2], sVarArgs[3], sVarArgs[4], sVarArgs[5], sVarArgs[6], sVarArgs[7], sVarArgs[8], sVarArgs[9], sVarArgs[10], sVarArgs[11], sVarArgs[12], sVarArgs[13], sVarArgs[14], sVarArgs[15], sVarArgs[16], sVarArgs[17], sVarArgs[18], sVarArgs[19]); + } + return 0; +#else + va_list args; + va_start(args, format); + return vsscanf(buffer, format, args); +#endif +} + +/// Safe form of dStrcmp: checks both strings for NULL before comparing +bool dStrEqual(const char* str1, const char* str2) +{ + if (!str1 || !str2) + return false; + else + return (dStrcmp(str1, str2) == 0); +} + +/// Check if one string starts with another +bool dStrStartsWith(const char* str1, const char* str2) +{ + return !dStrnicmp(str1, str2, dStrlen(str2)); +} + +/// Check if one string ends with another +bool dStrEndsWith(const char* str1, const char* str2) +{ + const char *p = str1 + dStrlen(str1) - dStrlen(str2); + return ((p >= str1) && !dStricmp(p, str2)); +} + +/// Strip the path from the input filename +char* dStripPath(const char* filename) +{ + const char* itr = filename + dStrlen(filename); + while(--itr != filename) { + if (*itr == '/' || *itr == '\\') { + itr++; + break; + } + } + return dStrdup(itr); +} + +char* dStristr( char* str1, const char* str2 ) +{ + if( !str1 || !str2 ) + return NULL; + + // Slow but at least we have it. + + U32 str2len = strlen( str2 ); + while( *str1 ) + { + if( strncasecmp( str1, str2, str2len ) == 0 ) + return str1; + + ++ str1; + } + + return NULL; +} + +const char* dStristr( const char* str1, const char* str2 ) +{ + return dStristr( const_cast< char* >( str1 ), str2 ); +} diff --git a/Engine/source/core/strings/stringFunctions.h b/Engine/source/core/strings/stringFunctions.h new file mode 100644 index 000000000..34af03b9b --- /dev/null +++ b/Engine/source/core/strings/stringFunctions.h @@ -0,0 +1,227 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _STRINGFUNCTIONS_H_ +#define _STRINGFUNCTIONS_H_ + +#include +#include +#include + +#ifndef _TORQUE_TYPES_H_ +#include "platform/types.h" +#endif + +#if defined(TORQUE_OS_WIN32) || defined(TORQUE_OS_XBOX) || defined(TORQUE_OS_XENON) +// These standard functions are not defined on Win32 and other Microsoft platforms... +#define strcasecmp _stricmp +#define strncasecmp _strnicmp +#define strtof (float)strtod +#endif + + +//------------------------------------------------------------------------------ +// standard string functions [defined in platformString.cpp] + +inline char *dStrcat(char *dst, const char *src) +{ + return strcat(dst,src); +} + +inline char *dStrncat(char *dst, const char *src, dsize_t len) +{ + return strncat(dst,src,len); +} + +inline int dStrcmp(const char *str1, const char *str2) +{ + return strcmp(str1, str2); +} + +inline int dStrncmp(const char *str1, const char *str2, dsize_t len) +{ + return strncmp(str1, str2, len); +} + +inline int dStricmp(const char *str1, const char *str2) +{ + return strcasecmp( str1, str2 ); +} + +inline int dStrnicmp(const char *str1, const char *str2, dsize_t len) +{ + return strncasecmp( str1, str2, len ); +} + +inline char *dStrcpy(char *dst, const char *src) +{ + return strcpy(dst,src); +} + +inline char *dStrncpy(char *dst, const char *src, dsize_t len) +{ + return strncpy(dst,src,len); +} + +inline dsize_t dStrlen(const char *str) +{ + return strlen(str); +} + +inline char *dStrchr(char *str, int c) +{ + return strchr(str,c); +} + +inline const char *dStrchr(const char *str, int c) +{ + return strchr(str,c); +} + +inline char *dStrrchr(char *str, int c) +{ + return strrchr(str,c); +} + +inline const char *dStrrchr(const char *str, int c) +{ + return strrchr(str,c); +} + +inline dsize_t dStrspn(const char *str, const char *set) +{ + return strspn(str, set); +} + +inline dsize_t dStrcspn(const char *str, const char *set) +{ + return strcspn(str, set); +} + +inline char *dStrstr(const char *str1, const char *str2) +{ + return strstr((char *)str1,str2); +} + +const char* dStristr( const char* str1, const char* str2 ); +char* dStristr( char* str1, const char* str2 ); + + +inline char *dStrtok(char *str, const char *sep) +{ + return strtok(str, sep); +} + + +inline S32 dAtoi(const char *str) +{ + return strtol(str, NULL, 10); +} + +inline U32 dAtoui(const char *str, U32 base = 10) +{ + return strtoul(str, NULL, base); +} + +inline F32 dAtof(const char *str) +{ + return strtof(str, NULL); +} + + +inline char dToupper(const char c) +{ + return toupper( c ); +} + +inline char dTolower(const char c) +{ + return tolower( c ); +} + +inline bool dIsalnum(const char c) +{ + return isalnum(c); +} + +inline bool dIsalpha(const char c) +{ + return isalpha(c); +} + +inline bool dIsspace(const char c) +{ + return isspace(c); +} + +inline bool dIsdigit(const char c) +{ + return isdigit(c); +} + +inline bool dIsquote(const char c) +{ + return ( c == '\"' ); +} + +//------------------------------------------------------------------------------ +// non-standard string functions [defined in stringFunctions.cpp] + +#define dStrdup(x) dStrdup_r(x, __FILE__, __LINE__) +extern char *dStrdup_r(const char *src, const char*, dsize_t); + +extern char *dStrcpyl(char *dst, dsize_t dstSize, ...); +extern char *dStrcatl(char *dst, dsize_t dstSize, ...); + +extern char *dStrupr(char *str); +extern char *dStrlwr(char *str); + +extern char* dStrichr( char* str, char ch ); +extern const char* dStrichr( const char* str, char ch ); + +extern int dStrcmp(const UTF16 *str1, const UTF16 *str2); +extern int dStrnatcmp( const char* str1, const char* str2 ); +extern int dStrnatcasecmp( const char* str1, const char* str2 ); + +inline bool dAtob(const char *str) +{ + return !dStricmp(str, "true") || dAtof(str); +} + +bool dStrEqual(const char* str1, const char* str2); + +bool dStrStartsWith(const char* str1, const char* str2); + +bool dStrEndsWith(const char* str1, const char* str2); + +char* dStripPath(const char* filename); + +//------------------------------------------------------------------------------ +// standard I/O functions [defined in platformString.cpp] + +extern void dPrintf(const char *format, ...); +extern int dVprintf(const char *format, void *arglist); +extern int dSprintf(char *buffer, U32 bufferSize, const char *format, ...); +extern int dVsprintf(char *buffer, U32 bufferSize, const char *format, void *arglist); +extern int dSscanf(const char *buffer, const char *format, ...); + +#endif diff --git a/Engine/source/core/strings/stringUnit.cpp b/Engine/source/core/strings/stringUnit.cpp new file mode 100644 index 000000000..dc666f38c --- /dev/null +++ b/Engine/source/core/strings/stringUnit.cpp @@ -0,0 +1,217 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/strings/stringFunctions.h" +#include "core/strings/stringUnit.h" +#include "console/console.h" + +namespace StringUnit +{ + static char _returnBuffer[ 2048 ]; + + const char *getUnit(const char *string, U32 index, const char *set, char* buffer, U32 bufferSize) + { + if( !buffer ) + { + buffer = _returnBuffer; + bufferSize = sizeof( _returnBuffer ); + } + + AssertFatal( bufferSize, "StringUnit::getUnit - bufferSize cannot be zero!" ); + if( !bufferSize ) + return ""; + + buffer[0] = 0; + + U32 sz; + while(index--) + { + if(!*string) + return buffer; + + sz = dStrcspn(string, set); + if (string[sz] == 0) + return buffer; + + string += (sz + 1); + } + sz = dStrcspn(string, set); + if (sz == 0) + return buffer; + + AssertWarn( sz + 1 < bufferSize, "Size of returned string too large for return buffer" ); + sz = getMin( sz, bufferSize - 1 ); + + dStrncpy(buffer, string, sz); + buffer[sz] = '\0'; + return buffer; + } + + const char *getUnits(const char *string, S32 startIndex, S32 endIndex, const char *set) + { + if( startIndex > endIndex ) + return ""; + + S32 sz; + S32 index = startIndex; + while(index--) + { + if(!*string) + return ""; + sz = dStrcspn(string, set); + if (string[sz] == 0) + return ""; + string += (sz + 1); + } + const char *startString = string; + + for( U32 i = startIndex; i <= endIndex && *string; i ++ ) + { + sz = dStrcspn(string, set); + string += sz; + + if( i < endIndex ) + string ++; + } + + S32 totalSize = ( S32 )( string - startString ); + AssertWarn( totalSize + 1 < sizeof( _returnBuffer ), "Size of returned string too large for return buffer" ); + totalSize = getMin( totalSize, S32( sizeof( _returnBuffer ) - 1 ) ); + + if( totalSize > 0 ) + { + char *ret = &_returnBuffer[0]; + dStrncpy( ret, startString, totalSize ); + ret[ totalSize ] = '\0'; + + return ret; + } + + return ""; + } + + U32 getUnitCount(const char *string, const char *set) + { + U32 count = 0; + U8 last = 0; + while(*string) + { + last = *string++; + + for(U32 i =0; set[i]; i++) + { + if(last == set[i]) + { + count++; + last = 0; + break; + } + } + } + if(last) + count++; + return count; + } + + + const char* setUnit(const char *string, U32 index, const char *replace, const char *set) + { + U32 sz; + const char *start = string; + + AssertFatal( dStrlen(string) + dStrlen(replace) + 1 < sizeof( _returnBuffer ), "Size of returned string too large for return buffer" ); + + char *ret = &_returnBuffer[0]; + ret[0] = '\0'; + U32 padCount = 0; + + while(index--) + { + sz = dStrcspn(string, set); + if(string[sz] == 0) + { + string += sz; + padCount = index + 1; + break; + } + else + string += (sz + 1); + } + // copy first chunk + sz = string-start; + dStrncpy(ret, start, sz); + for(U32 i = 0; i < padCount; i++) + ret[sz++] = set[0]; + + // replace this unit + ret[sz] = '\0'; + dStrcat(ret, replace); + + // copy remaining chunks + sz = dStrcspn(string, set); // skip chunk we're replacing + if(!sz && !string[sz]) + return ret; + + string += sz; + dStrcat(ret, string); + return ret; + } + + + const char* removeUnit(const char *string, U32 index, const char *set) + { + U32 sz; + const char *start = string; + AssertFatal( dStrlen(string) + 1 < sizeof( _returnBuffer ), "Size of returned string too large for return buffer" ); + + char *ret = &_returnBuffer[0]; + ret[0] = '\0'; + + while(index--) + { + sz = dStrcspn(string, set); + // if there was no unit out there... return the original string + if(string[sz] == 0) + return start; + else + string += (sz + 1); + } + // copy first chunk + sz = string-start; + dStrncpy(ret, start, sz); + ret[sz] = 0; + + // copy remaining chunks + sz = dStrcspn(string, set); // skip chunk we're removing + + if(string[sz] == 0) { // if that was the last... + if(string != start) { + ret[string - start - 1] = 0; // then kill any trailing delimiter + } + return ret; // and bail + } + + string += sz + 1; // skip the extra field delimiter + dStrcat(ret, string); + return ret; + } +} diff --git a/Engine/source/core/strings/stringUnit.h b/Engine/source/core/strings/stringUnit.h new file mode 100644 index 000000000..e4a410762 --- /dev/null +++ b/Engine/source/core/strings/stringUnit.h @@ -0,0 +1,39 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _STRINGUNIT_H_ +#define _STRINGUNIT_H_ + +#include "platform/types.h" + +/// These functions are used for chunking up strings by delimiter. +/// Especially useful for handling TorqueScript space-delimited fields +namespace StringUnit +{ + const char *getUnit(const char *string, U32 index, const char *set, char* buffer = 0, U32 bufferSize = 0); + const char *getUnits(const char *string, S32 startIndex, S32 endIndex, const char *set); + U32 getUnitCount(const char *string, const char *set); + const char* setUnit(const char *string, U32 index, const char *replace, const char *set); + const char* removeUnit(const char *string, U32 index, const char *set); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/core/strings/unicode.cpp b/Engine/source/core/strings/unicode.cpp new file mode 100644 index 000000000..2edea59e8 --- /dev/null +++ b/Engine/source/core/strings/unicode.cpp @@ -0,0 +1,663 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include + +#include "core/frameAllocator.h" +#include "core/strings/unicode.h" +#include "core/strings/stringFunctions.h" + +#include "platform/profiler.h" +#include "console/console.h" + +#define TORQUE_ENABLE_UTF16_CACHE + +#ifdef TORQUE_ENABLE_UTF16_CACHE +#include "core/util/tDictionary.h" +#include "core/util/hashFunction.h" +#endif + +//----------------------------------------------------------------------------- +/// replacement character. Standard correct value is 0xFFFD. +#define kReplacementChar 0xFFFD + +/// Look up table. Shift a byte >> 1, then look up how many bytes to expect after it. +/// Contains -1's for illegal values. +static const U8 sgFirstByteLUT[128] = +{ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x0F // single byte ascii + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x1F // single byte ascii + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x2F // single byte ascii + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x3F // single byte ascii + + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x4F // trailing utf8 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x5F // trailing utf8 + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x6F // first of 2 + 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 6, 0, // 0x7F // first of 3,4,5,illegal in utf-8 +}; + +/// Look up table. Shift a 16-bit word >> 10, then look up whether it is a surrogate, +/// and which part. 0 means non-surrogate, 1 means 1st in pair, 2 means 2nd in pair. +static const U8 sgSurrogateLUT[64] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x0F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x1F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x2F + 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, // 0x3F +}; + +/// Look up table. Feed value from firstByteLUT in, gives you +/// the mask for the data bits of that UTF-8 code unit. +static const U8 sgByteMask8LUT[] = { 0x3f, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 }; // last 0=6, 1=7, 2=5, 4, 3, 2, 1 bits + +/// Mask for the data bits of a UTF-16 surrogate. +static const U16 sgByteMaskLow10 = 0x03ff; + +//----------------------------------------------------------------------------- + +#ifdef TORQUE_ENABLE_UTF16_CACHE + +/// Cache data for UTF16 strings. This is wrapped in a class so that data is +/// automatically freed when the hash table is deleted. +struct UTF16Cache +{ + UTF16 *mString; + U32 mLength; + + UTF16Cache() + { + mString = NULL; + mLength = 0; + } + + UTF16Cache(UTF16 *str, U32 len) + { + mLength = len; + mString = new UTF16[mLength]; + dMemcpy(mString, str, mLength * sizeof(UTF16)); + } + + UTF16Cache(const UTF16Cache &other) + { + mLength = other.mLength; + mString = new UTF16[mLength]; + dMemcpy(mString, other.mString, mLength * sizeof(UTF16)); + } + + void operator =(const UTF16Cache &other) + { + delete [] mString; + + mLength = other.mLength; + mString = new UTF16[mLength]; + dMemcpy(mString, other.mString, mLength * sizeof(UTF16)); + } + + ~UTF16Cache() + { + delete [] mString; + } + + void copyToBuffer(UTF16 *outBuffer, U32 lenToCopy, bool nullTerminate = true) const + { + U32 copy = getMin(mLength, lenToCopy); + if(mString && copy > 0) + dMemcpy(outBuffer, mString, copy * sizeof(UTF16)); + + if(nullTerminate) + outBuffer[copy] = 0; + } +}; + +/// Cache for UTF16 strings +typedef HashTable UTF16CacheTable; +static UTF16CacheTable sgUTF16Cache; + +#endif // TORQUE_ENABLE_UTF16_CACHE + +//----------------------------------------------------------------------------- +inline bool isSurrogateRange(U32 codepoint) +{ + return ( 0xd800 < codepoint && codepoint < 0xdfff ); +} + +inline bool isAboveBMP(U32 codepoint) +{ + return ( codepoint > 0xFFFF ); +} + +//----------------------------------------------------------------------------- +U32 convertUTF8toUTF16(const UTF8 *unistring, UTF16 *outbuffer, U32 len) +{ + AssertFatal(len >= 1, "Buffer for unicode conversion must be large enough to hold at least the null terminator."); + PROFILE_SCOPE(convertUTF8toUTF16); + +#ifdef TORQUE_ENABLE_UTF16_CACHE + // If we have cached this conversion already, don't do it again + U32 hashKey = Torque::hash((const U8 *)unistring, dStrlen(unistring), 0); + UTF16CacheTable::Iterator cacheItr = sgUTF16Cache.find(hashKey); + if(cacheItr != sgUTF16Cache.end()) + { + const UTF16Cache &cache = (*cacheItr).value; + cache.copyToBuffer(outbuffer, len); + outbuffer[len-1] = '\0'; + return getMin(cache.mLength,len - 1); + } +#endif + + U32 walked, nCodepoints; + UTF32 middleman; + + nCodepoints=0; + while(*unistring != '\0' && nCodepoints < len) + { + walked = 1; + middleman = oneUTF8toUTF32(unistring,&walked); + outbuffer[nCodepoints] = oneUTF32toUTF16(middleman); + unistring+=walked; + nCodepoints++; + } + + nCodepoints = getMin(nCodepoints,len - 1); + outbuffer[nCodepoints] = '\0'; + +#ifdef TORQUE_ENABLE_UTF16_CACHE + // Cache the results. + // FIXME As written, this will result in some unnecessary memory copying due to copy constructor calls. + UTF16Cache cache(outbuffer, nCodepoints); + sgUTF16Cache.insertUnique(hashKey, cache); +#endif + + return nCodepoints; +} + +//----------------------------------------------------------------------------- +U32 convertUTF16toUTF8( const UTF16 *unistring, UTF8 *outbuffer, U32 len) +{ + AssertFatal(len >= 1, "Buffer for unicode conversion must be large enough to hold at least the null terminator."); + PROFILE_START(convertUTF16toUTF8); + U32 walked, nCodeunits, codeunitLen; + UTF32 middleman; + + nCodeunits=0; + while( *unistring != '\0' && nCodeunits + 3 < len ) + { + walked = 1; + middleman = oneUTF16toUTF32(unistring,&walked); + codeunitLen = oneUTF32toUTF8(middleman, &outbuffer[nCodeunits]); + unistring += walked; + nCodeunits += codeunitLen; + } + + nCodeunits = getMin(nCodeunits,len - 1); + outbuffer[nCodeunits] = '\0'; + + PROFILE_END(); + return nCodeunits; +} + +U32 convertUTF16toUTF8DoubleNULL( const UTF16 *unistring, UTF8 *outbuffer, U32 len) +{ + AssertFatal(len >= 1, "Buffer for unicode conversion must be large enough to hold at least the null terminator."); + PROFILE_START(convertUTF16toUTF8DoubleNULL); + U32 walked, nCodeunits, codeunitLen; + UTF32 middleman; + + nCodeunits=0; + while( ! (*unistring == '\0' && *(unistring + 1) == '\0') && nCodeunits + 3 < len ) + { + walked = 1; + middleman = oneUTF16toUTF32(unistring,&walked); + codeunitLen = oneUTF32toUTF8(middleman, &outbuffer[nCodeunits]); + unistring += walked; + nCodeunits += codeunitLen; + } + + nCodeunits = getMin(nCodeunits,len - 1); + outbuffer[nCodeunits] = NULL; + outbuffer[nCodeunits+1] = NULL; + + PROFILE_END(); + return nCodeunits; +} + +//----------------------------------------------------------------------------- +// Functions that convert buffers of unicode code points +//----------------------------------------------------------------------------- +UTF16* convertUTF8toUTF16( const UTF8* unistring) +{ + PROFILE_SCOPE(convertUTF8toUTF16_create); + + // allocate plenty of memory. + U32 nCodepoints, len = dStrlen(unistring) + 1; + FrameTemp buf(len); + + // perform conversion + nCodepoints = convertUTF8toUTF16( unistring, buf, len); + + // add 1 for the NULL terminator the converter promises it included. + nCodepoints++; + + // allocate the return buffer, copy over, and return it. + UTF16 *ret = new UTF16[nCodepoints]; + dMemcpy(ret, buf, nCodepoints * sizeof(UTF16)); + + return ret; +} + +//----------------------------------------------------------------------------- +UTF8* convertUTF16toUTF8( const UTF16* unistring) +{ + PROFILE_SCOPE(convertUTF16toUTF8_create); + + // allocate plenty of memory. + U32 nCodeunits, len = dStrlen(unistring) * 3 + 1; + FrameTemp buf(len); + + // perform conversion + nCodeunits = convertUTF16toUTF8( unistring, buf, len); + + // add 1 for the NULL terminator the converter promises it included. + nCodeunits++; + + // allocate the return buffer, copy over, and return it. + UTF8 *ret = new UTF8[nCodeunits]; + dMemcpy(ret, buf, nCodeunits * sizeof(UTF8)); + + return ret; +} + +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Functions that converts one unicode codepoint at a time +//----------------------------------------------------------------------------- +UTF32 oneUTF8toUTF32( const UTF8* codepoint, U32 *unitsWalked) +{ + PROFILE_SCOPE(oneUTF8toUTF32); + + // codepoints 6 codeunits long are read, but do not convert correctly, + // and are filtered out anyway. + + // early out for ascii + if(!(*codepoint & 0x0080)) + { + if (unitsWalked != NULL) + *unitsWalked = 1; + return (UTF32)*codepoint; + } + + U32 expectedByteCount; + UTF32 ret = 0; + U8 codeunit; + + // check the first byte ( a.k.a. codeunit ) . + unsigned char c = codepoint[0]; + c = c >> 1; + expectedByteCount = sgFirstByteLUT[c]; + if(expectedByteCount > 0) // 0 or negative is illegal to start with + { + // process 1st codeunit + ret |= sgByteMask8LUT[expectedByteCount] & codepoint[0]; // bug? + + // process trailing codeunits + for(U32 i=1;i>1] == 0 ) + { + ret <<= 6; // shift up 6 + ret |= (codeunit & 0x3f); // mask in the low 6 bits of this codeunit byte. + } + else + { + // found a bad codepoint - did not get a medial where we wanted one. + // Dump the replacement, and claim to have parsed only 1 char, + // so that we'll dump a slew of replacements, instead of eating the next char. + ret = kReplacementChar; + expectedByteCount = 1; + break; + } + } + } + else + { + // found a bad codepoint - got a medial or an illegal codeunit. + // Dump the replacement, and claim to have parsed only 1 char, + // so that we'll dump a slew of replacements, instead of eating the next char. + ret = kReplacementChar; + expectedByteCount = 1; + } + + if(unitsWalked != NULL) + *unitsWalked = expectedByteCount; + + // codepoints in the surrogate range are illegal, and should be replaced. + if(isSurrogateRange(ret)) + ret = kReplacementChar; + + // codepoints outside the Basic Multilingual Plane add complexity to our UTF16 string classes, + // we've read them correctly so they won't foul the byte stream, + // but we kill them here to make sure they wont foul anything else + if(isAboveBMP(ret)) + ret = kReplacementChar; + + return ret; +} + +//----------------------------------------------------------------------------- +UTF32 oneUTF16toUTF32(const UTF16* codepoint, U32 *unitsWalked) +{ + PROFILE_START(oneUTF16toUTF32); + U8 expectedType; + U32 unitCount; + UTF32 ret = 0; + UTF16 codeunit1,codeunit2; + + codeunit1 = codepoint[0]; + expectedType = sgSurrogateLUT[codeunit1 >> 10]; + switch(expectedType) + { + case 0: // simple + ret = codeunit1; + unitCount = 1; + break; + case 1: // 2 surrogates + codeunit2 = codepoint[1]; + if( sgSurrogateLUT[codeunit2 >> 10] == 2) + { + ret = ((codeunit1 & sgByteMaskLow10 ) << 10) | (codeunit2 & sgByteMaskLow10); + unitCount = 2; + break; + } + // else, did not find a trailing surrogate where we expected one, + // so fall through to the error + case 2: // error + // found a trailing surrogate where we expected a codepoint or leading surrogate. + // Dump the replacement. + ret = kReplacementChar; + unitCount = 1; + break; + default: + // unexpected return + AssertFatal(false, "oneUTF16toUTF323: unexpected type"); + ret = kReplacementChar; + unitCount = 1; + break; + } + + if(unitsWalked != NULL) + *unitsWalked = unitCount; + + // codepoints in the surrogate range are illegal, and should be replaced. + if(isSurrogateRange(ret)) + ret = kReplacementChar; + + // codepoints outside the Basic Multilingual Plane add complexity to our UTF16 string classes, + // we've read them correctly so they wont foul the byte stream, + // but we kill them here to make sure they wont foul anything else + // NOTE: these are perfectly legal codepoints, we just dont want to deal with them. + if(isAboveBMP(ret)) + ret = kReplacementChar; + + PROFILE_END(); + return ret; +} + +//----------------------------------------------------------------------------- +UTF16 oneUTF32toUTF16(const UTF32 codepoint) +{ + // found a codepoint outside the encodable UTF-16 range! + // or, found an illegal codepoint! + if(codepoint >= 0x10FFFF || isSurrogateRange(codepoint)) + return kReplacementChar; + + // these are legal, we just don't want to deal with them. + if(isAboveBMP(codepoint)) + return kReplacementChar; + + return (UTF16)codepoint; +} + +//----------------------------------------------------------------------------- +U32 oneUTF32toUTF8(const UTF32 codepoint, UTF8 *threeByteCodeunitBuf) +{ + PROFILE_START(oneUTF32toUTF8); + U32 bytecount = 0; + UTF8 *buf; + U32 working = codepoint; + buf = threeByteCodeunitBuf; + + //----------------- + if(isSurrogateRange(working)) // found an illegal codepoint! + working = kReplacementChar; + + if(isAboveBMP(working)) // these are legal, we just dont want to deal with them. + working = kReplacementChar; + + //----------------- + if( working < (1 << 7)) // codeable in 7 bits + bytecount = 1; + else if( working < (1 << 11)) // codeable in 11 bits + bytecount = 2; + else if( working < (1 << 16)) // codeable in 16 bits + bytecount = 3; + + AssertISV( bytecount > 0, "Error converting to UTF-8 in oneUTF32toUTF8(). isAboveBMP() should have caught this!"); + + //----------------- + U8 mask = sgByteMask8LUT[0]; // 0011 1111 + U8 marker = ( ~mask << 1); // 1000 0000 + + // Process the low order bytes, shifting the codepoint down 6 each pass. + for( int i = bytecount-1; i > 0; i--) + { + threeByteCodeunitBuf[i] = marker | (working & mask); + working >>= 6; + } + + // Process the 1st byte. filter based on the # of expected bytes. + mask = sgByteMask8LUT[bytecount]; + marker = ( ~mask << 1 ); + threeByteCodeunitBuf[0] = marker | working & mask; + + PROFILE_END(); + return bytecount; +} + +//----------------------------------------------------------------------------- +U32 dStrlen(const UTF16 *unistring) +{ + if(!unistring) + return 0; + + U32 i = 0; + while(unistring[i] != '\0') + i++; + +// AssertFatal( wcslen(unistring) == i, "Incorrect length" ); + + return i; +} + +//----------------------------------------------------------------------------- +U32 dStrlen(const UTF32 *unistring) +{ + U32 i = 0; + while(unistring[i] != '\0') + i++; + + return i; +} + +//----------------------------------------------------------------------------- +U32 dStrncmp(const UTF16* unistring1, const UTF16* unistring2, U32 len) +{ + UTF16 c1, c2; + for(U32 i = 0; i c2) return 1; + if(!c1) return 0; + } + return 0; +} + +//----------------------------------------------------------------------------- + +const UTF16* dStrrchr(const UTF16* unistring, U32 c) +{ + if(!unistring) return NULL; + + const UTF16* tmp = unistring + dStrlen(unistring); + while( tmp >= unistring) + { + if(*tmp == c) + return tmp; + tmp--; + } + return NULL; +} + +UTF16* dStrrchr(UTF16* unistring, U32 c) +{ + const UTF16* str = unistring; + return const_cast(dStrrchr(str, c)); +} + +const UTF16* dStrchr(const UTF16* unistring, U32 c) +{ + if(!unistring) return NULL; + const UTF16* tmp = unistring; + + while ( *tmp && *tmp != c) + tmp++; + + return (*tmp == c) ? tmp : NULL; +} + +UTF16* dStrchr(UTF16* unistring, U32 c) +{ + const UTF16* str = unistring; + return const_cast(dStrchr(str, c)); +} + +//----------------------------------------------------------------------------- +const UTF8* getNthCodepoint(const UTF8 *unistring, const U32 n) +{ + const UTF8* ret = unistring; + U32 charsseen = 0; + while( *ret && charsseen < n) + { + ret++; + if((*ret & 0xC0) != 0x80) + charsseen++; + } + + return ret; +} + +/* alternate utf-8 decode impl for speed, no error checking, + left here for your amusement: + + U32 codeunit = codepoint + expectedByteCount - 1; + U32 i = 0; + switch(expectedByteCount) + { + case 6: ret |= ( *(codeunit--) & 0x3f ); i++; + case 5: ret |= ( *(codeunit--) & 0x3f ) << (6 * i++); + case 4: ret |= ( *(codeunit--) & 0x3f ) << (6 * i++); + case 3: ret |= ( *(codeunit--) & 0x3f ) << (6 * i++); + case 2: ret |= ( *(codeunit--) & 0x3f ) << (6 * i++); + case 1: ret |= *(codeunit) & byteMask8LUT[expectedByteCount] << (6 * i); + } +*/ + +//------------------------------------------------------------------------------ +// Byte Order Mark functions + +bool chompUTF8BOM( const char *inString, char **outStringPtr ) +{ + *outStringPtr = const_cast( inString ); + + U8 bom[4]; + dMemcpy( bom, inString, 4 ); + + bool valid = isValidUTF8BOM( bom ); + + // This is hackey, but I am not sure the best way to do it at the present. + // The only valid BOM is a UTF8 BOM, which is 3 bytes, even though we read + // 4 bytes because it could possibly be a UTF32 BOM, and we want to provide + // an accurate error message. Perhaps this could be re-worked when more UTF + // formats are supported to have isValidBOM return the size of the BOM, in + // bytes. + if( valid ) + (*outStringPtr) += 3; // SEE ABOVE!! -pw + + return valid; +} + +bool isValidUTF8BOM( U8 bom[4] ) +{ + // Is it a BOM? + if( bom[0] == 0 ) + { + // Could be UTF32BE + if( bom[1] == 0 && bom[2] == 0xFE && bom[3] == 0xFF ) + { + Con::warnf( "Encountered a UTF32 BE BOM in this file; Torque does NOT support this file encoding. Use UTF8!" ); + return false; + } + + return false; + } + else if( bom[0] == 0xFF ) + { + // It's little endian, either UTF16 or UTF32 + if( bom[1] == 0xFE ) + { + if( bom[2] == 0 && bom[3] == 0 ) + Con::warnf( "Encountered a UTF32 LE BOM in this file; Torque does NOT support this file encoding. Use UTF8!" ); + else + Con::warnf( "Encountered a UTF16 LE BOM in this file; Torque does NOT support this file encoding. Use UTF8!" ); + } + + return false; + } + else if( bom[0] == 0xFE && bom[1] == 0xFF ) + { + Con::warnf( "Encountered a UTF16 BE BOM in this file; Torque does NOT support this file encoding. Use UTF8!" ); + return false; + } + else if( bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF ) + { + // Can enable this if you want -pw + //Con::printf("Encountered a UTF8 BOM. Torque supports this."); + return true; + } + + // Don't print out an error message here, because it will try this with + // every script. -pw + return false; +} diff --git a/Engine/source/core/strings/unicode.h b/Engine/source/core/strings/unicode.h new file mode 100644 index 000000000..042415377 --- /dev/null +++ b/Engine/source/core/strings/unicode.h @@ -0,0 +1,131 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _UNICODE_H_ +#define _UNICODE_H_ + +#ifndef _TORQUE_TYPES_H_ +#include "platform/types.h" +#endif + + +/// Unicode conversion utility functions +/// +/// Some definitions first: +/// - Code Point: a single character of Unicode text. Used to disabmiguate from C char type. +/// - UTF-32: a Unicode encoding format where one code point is always 32 bits wide. +/// This format can in theory contain any Unicode code point that will ever be needed, now or in the future. 4billion+ code points should be enough, right? +/// - UTF-16: a variable length Unicode encoding format where one code point can be +/// either one or two 16-bit code units long. +/// - UTF-8: a variable length Unicode endocing format where one code point can be +/// up to four 8-bit code units long. The first bit of a single byte UTF-8 code point is 0. +/// The first few bits of a multi-byte code point determine the length of the code point. +/// @see http://en.wikipedia.org/wiki/UTF-8 +/// - Surrogate Pair: a pair of special UTF-16 code units, that encode a code point +/// that is too large to fit into 16 bits. The surrogate values sit in a special reserved range of Unicode. +/// - Code Unit: a single unit of a variable length Unicode encoded code point. +/// UTF-8 has 8 bit wide code units. UTF-16 has 16 bit wide code units. +/// - BMP: "Basic Multilingual Plane". Unicode values U+0000 - U+FFFF. This range +/// of Unicode contains all the characters for all the languages of the world, that one would +/// usually be interested in. All code points in the BMP are 16 bits wide or less. + +/// The current implementation of these conversion functions deals only with the BMP. +/// Any code points above 0xFFFF, the top of the BMP, are replaced with the +/// standard unicode replacement character: 0xFFFD. +/// Any UTF16 surrogates are read correctly, but replaced. +/// UTF-8 code points up to 6 code units wide will be read, but 5+ is illegal, +/// and 4+ is above the BMP, and will be replaced. +/// This means that UTF-8 output is clamped to 3 code units ( bytes ) per code point. + +//----------------------------------------------------------------------------- +/// Functions that convert buffers of unicode code points, allocating a buffer. +/// - These functions allocate their own return buffers. You are responsible for +/// calling delete[] on these buffers. +/// - Because they allocate memory, do not use these functions in a tight loop. +/// - These are useful when you need a new long term copy of a string. +UTF16* convertUTF8toUTF16( const UTF8 *unistring); + +UTF8* convertUTF16toUTF8( const UTF16 *unistring); + +//----------------------------------------------------------------------------- +/// Functions that convert buffers of unicode code points, into a provided buffer. +/// - These functions are useful for working on existing buffers. +/// - These cannot convert a buffer in place. If unistring is the same memory as +/// outbuffer, the behavior is undefined. +/// - The converter clamps output to the BMP (Basic Multilingual Plane) . +/// - Conversion to UTF-8 requires a buffer of 3 bytes (U8's) per character, + 1. +/// - Conversion to UTF-16 requires a buffer of 1 U16 (2 bytes) per character, + 1. +/// - Conversion to UTF-32 requires a buffer of 1 U32 (4 bytes) per character, + 1. +/// - UTF-8 only requires 3 bytes per character in the worst case. +/// - Output is null terminated. Be sure to provide 1 extra byte, U16 or U32 for +/// the null terminator, or you will see truncated output. +/// - If the provided buffer is too small, the output will be truncated. +U32 convertUTF8toUTF16(const UTF8 *unistring, UTF16 *outbuffer, U32 len); + +U32 convertUTF16toUTF8( const UTF16 *unistring, UTF8 *outbuffer, U32 len); + +//----------------------------------------------------------------------------- +/// Functions that converts one unicode codepoint at a time +/// - Since these functions are designed to be used in tight loops, they do not +/// allocate buffers. +/// - oneUTF8toUTF32() and oneUTF16toUTF32() return the converted Unicode code point +/// in *codepoint, and set *unitsWalked to the \# of code units *codepoint took up. +/// The next Unicode code point should start at *(codepoint + *unitsWalked). +/// - oneUTF32toUTF8() requires a 3 byte buffer, and returns the \# of bytes used. +UTF32 oneUTF8toUTF32( const UTF8 *codepoint, U32 *unitsWalked = NULL); +UTF32 oneUTF16toUTF32(const UTF16 *codepoint, U32 *unitsWalked = NULL); +UTF16 oneUTF32toUTF16(const UTF32 codepoint); +U32 oneUTF32toUTF8( const UTF32 codepoint, UTF8 *threeByteCodeunitBuf); + +//----------------------------------------------------------------------------- +/// Functions that calculate the length of unicode strings. +/// - Since calculating the length of a UTF8 string is nearly as expensive as +/// converting it to another format, a dStrlen for UTF8 is not provided here. +/// - If *unistring does not point to a null terminated string of the correct type, +/// the behavior is undefined. +U32 dStrlen(const UTF16 *unistring); +U32 dStrlen(const UTF32 *unistring); + +//----------------------------------------------------------------------------- +/// Comparing unicode strings +U32 dStrncmp(const UTF16* unistring1, const UTF16* unistring2, U32 len); + +//----------------------------------------------------------------------------- +/// Scanning for characters in unicode strings +UTF16* dStrrchr(UTF16* unistring, U32 c); +const UTF16* dStrrchr(const UTF16* unistring, U32 c); + +UTF16* dStrchr(UTF16* unistring, U32 c); +const UTF16* dStrchr(const UTF16* unistring, U32 c); +//----------------------------------------------------------------------------- +/// Functions that scan for characters in a utf8 string. +/// - this is useful for getting a character-wise offset into a UTF8 string, +/// as opposed to a byte-wise offset into a UTF8 string: foo[i] +const UTF8* getNthCodepoint(const UTF8 *unistring, const U32 n); + +//------------------------------------------------------------------------------ +/// Functions to read and validate UTF BOMs (Byte Order Marker) +/// For reference: http://en.wikipedia.org/wiki/Byte_Order_Mark +bool chompUTF8BOM( const char *inString, char **outStringPtr ); +bool isValidUTF8BOM( U8 bom[4] ); + +#endif // _UNICODE_H_ \ No newline at end of file diff --git a/Engine/source/core/tAlgorithm.h b/Engine/source/core/tAlgorithm.h new file mode 100644 index 000000000..2c5b6d25f --- /dev/null +++ b/Engine/source/core/tAlgorithm.h @@ -0,0 +1,62 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TALGORITHM_H_ +#define _TALGORITHM_H_ + + +/// Finds the first matching value within the container +/// returning the the element or last if its not found. +template +Iterator find(Iterator first, Iterator last, Value value) +{ + while (first != last && *first != value) + ++first; + return first; +} + +/// Exchanges the values of the two elements. +template +inline void swap( T &left, T &right ) +{ + T temp = right; + right = left; + left = temp; +} + +/// Steps thru the elements of an array calling detete for each. +template +void for_each( Iterator first, Iterator last, Functor func ) +{ + for ( ; first != last; first++ ) + func( *first ); +} + +/// Functor for deleting a pointer. +/// @see for_each +struct delete_pointer +{ + template + void operator()(T *ptr){ delete ptr;} +}; + +#endif //_TALGORITHM_H_ diff --git a/Engine/source/core/tSimpleHashTable.h b/Engine/source/core/tSimpleHashTable.h new file mode 100644 index 000000000..9b0e9075b --- /dev/null +++ b/Engine/source/core/tSimpleHashTable.h @@ -0,0 +1,115 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// [tom, 9/19/2006] Simple hash table. Not intended to replace map<>, but it is +// generally good enough for simple things that you don't need to iterate. +// +// Note: If you move this to another project, you need the updated tSparseArray.h +// as well as hashFunction.cc/h + +#include "platform/platform.h" + +#include "core/tSparseArray.h" +#include "core/util/hashFunction.h" +#include "core/strings/stringFunctions.h" + +#ifndef _TSIMPLEHASHTABLE_H +#define _TSIMPLEHASHTABLE_H + +template class SimpleHashTable : public SparseArray +{ + typedef SparseArray Parent; + + bool mCaseSensitive; + + char mCaseConvBuf[1024]; + + // [tom, 9/21/2006] This is incredibly lame and adds a pretty big speed penalty + inline const char *caseConv(const char *str) + { + if(mCaseSensitive) return str; + + S32 len = dStrlen(str); + if(len >= sizeof(mCaseConvBuf)) len = sizeof(mCaseConvBuf) - 1; + + char *dptr = mCaseConvBuf; + const char *sptr = str; + while(*sptr) + { + *dptr = dTolower(*sptr); + ++sptr; + ++dptr; + } + *dptr = 0; + + return mCaseConvBuf; + } + +public: + SimpleHashTable(const U32 modulusSize = 64, bool caseSensitive = true) : Parent(modulusSize), mCaseSensitive(caseSensitive) + { + } + + void insert(T* pObject, U8 *key, U32 keyLen); + T* remove(U8 *key, U32 keyLen); + T* retreive(U8 *key, U32 keyLen); + + void insert(T* pObject, const char *key); + T* remove(const char *key); + T* retreive(const char *key); +}; + +template inline void SimpleHashTable::insert(T* pObject, U8 *key, U32 keyLen) +{ + Parent::insert(pObject, Torque::hash(key, keyLen, 0)); +} + +template inline T* SimpleHashTable::remove(U8 *key, U32 keyLen) +{ + return Parent::remove(Torque::hash(key, keyLen, 0)); +} + +template inline T* SimpleHashTable::retreive(U8 *key, U32 keyLen) +{ + return Parent::retreive(Torque::hash(key, keyLen, 0)); +} + +template inline void SimpleHashTable::insert(T* pObject, const char *key) +{ + key = caseConv(key); + insert(pObject, (U8 *)key, dStrlen(key)); +} + +template T* SimpleHashTable::remove(const char *key) +{ + key = caseConv(key); + return remove((U8 *)key, dStrlen(key)); +} + +template T* SimpleHashTable::retreive(const char *key) +{ + key = caseConv(key); + return retreive((U8 *)key, dStrlen(key)); +} + + +#endif // _TSIMPLEHASHTABLE_H diff --git a/Engine/source/core/tSparseArray.h b/Engine/source/core/tSparseArray.h new file mode 100644 index 000000000..5a15b1f81 --- /dev/null +++ b/Engine/source/core/tSparseArray.h @@ -0,0 +1,152 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TSPARSEARRAY_H_ +#define _TSPARSEARRAY_H_ + +//Includes +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#ifndef _PLATFORMASSERT_H_ +#include "platform/platformAssert.h" +#endif + +template +class SparseArray +{ + protected: + struct Node { + T* pObject; + U32 key; + + Node* next; + }; + + protected: + U32 mModulus; + Node* mSentryTables; + +public: + SparseArray(const U32 modulusSize = 64); + ~SparseArray(); + + void insert(T* pObject, U32 key); + T* remove(U32 key); + T* retreive(U32 key); + + void clearTables(); // Note: _deletes_ the objects! +}; + +template +inline SparseArray::SparseArray(const U32 modulusSize) +{ + AssertFatal(modulusSize > 0, "Error, modulus must be > 0"); + + mModulus = modulusSize; + mSentryTables = new Node[mModulus]; + for (U32 i = 0; i < mModulus; i++) + mSentryTables[i].next = NULL; +} + +template +inline SparseArray::~SparseArray() +{ + clearTables(); +} + +template +inline void SparseArray::clearTables() +{ + for (U32 i = 0; i < mModulus; i++) { + Node* pProbe = mSentryTables[i].next; + while (pProbe != NULL) { + Node* pNext = pProbe->next; + delete pProbe->pObject; + delete pProbe; + pProbe = pNext; + } + } + delete [] mSentryTables; + mSentryTables = NULL; + mModulus = 0; +} + +template +inline void SparseArray::insert(T* pObject, U32 key) +{ + U32 insert = key % mModulus; + Node* pNew = new Node; + pNew->pObject = pObject; + pNew->key = key; + pNew->next = mSentryTables[insert].next; + mSentryTables[insert].next = pNew; + +#ifdef TORQUE_DEBUG + Node* probe = pNew->next; + while (probe != NULL) { + AssertFatal(probe->key != key, "error, duplicate keys in sparse array!"); + probe = probe->next; + } +#endif +} + +template +inline T* SparseArray::remove(U32 key) +{ + U32 remove = key % mModulus; + Node* probe = &mSentryTables[remove]; + while (probe->next != NULL) { + if (probe->next->key == key) { + Node* remove = probe->next; + T* pReturn = remove->pObject; + probe->next = remove->next; + delete remove; + return pReturn; + } + probe = probe->next; + } + + // [tom, 8/19/2006] This assert is also utterly, utterly useless + // AssertFatal(false, "Key didn't exist in the array!"); + return NULL; +} + +template +inline T* SparseArray::retreive(U32 key) +{ + U32 retrieve = key % mModulus; + Node* probe = &mSentryTables[retrieve]; + while (probe->next != NULL) { + if (probe->next->key == key) { + return probe->next->pObject; + } + probe = probe->next; + } + + // [tom, 11/16/2005] This assert is utterly, utterly useless + // AssertFatal(false, "Key didn't exist in the array!"); + return NULL; +} + +#endif //_TSPARSEARRAY_H_ + diff --git a/Engine/source/core/tagDictionary.cpp b/Engine/source/core/tagDictionary.cpp new file mode 100644 index 000000000..e31bb5e29 --- /dev/null +++ b/Engine/source/core/tagDictionary.cpp @@ -0,0 +1,320 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/strings/stringFunctions.h" +#include "core/tagDictionary.h" +#include "core/stream/stream.h" + +namespace { + +const char TAG_ASCII_ID[] = "[TAG]"; +const char TAG_ASCII_END[] = "[END]"; +const char TAG_ASCII_HEADER[] = "// Auto-Generated by TagDictionary class"; + +const S32 sg_tagDictAsciiUser = 1; + +} // namespace + +TagDictionary tagDictionary; + +TagDictionary::TagDictionary() +{ + numBuckets = 29; + defineHashBuckets = (TagEntry **) dMalloc(numBuckets * sizeof(TagEntry *)); + idHashBuckets = (TagEntry **) dMalloc(numBuckets * sizeof(TagEntry *)); + + S32 i; + for(i = 0; i < numBuckets; i++) + { + defineHashBuckets[i] = NULL; + idHashBuckets[i] = NULL; + } + numEntries = 0; + entryChain = NULL; +} + +TagDictionary::~TagDictionary() +{ + dFree(defineHashBuckets); + dFree(idHashBuckets); +} + +//------------------------------------------------------------------------------ + +static inline S32 hashId(S32 id, S32 tsize) +{ + return id % tsize; +} + +static inline S32 hashDefine(StringTableEntry define, S32 tsize) +{ + return ((S32)((dsize_t)define) >> 2) % tsize; +} + +//------------------------------------------------------------------------------ + +bool TagDictionary::addEntry(S32 value, StringTableEntry define, StringTableEntry string) +{ + if(!value) + return false; +//#pragma message "put console prints back" + if(idToDefine(value)) + { + AssertWarn(false, avar("Error: id %d already defined to a tag.", value)); + //Con::printf("Error: id %d already defined to a tag.", value); + return false; + } + S32 tempTag; + if((tempTag = defineToId(define)) != 0) + { + AssertWarn(false, avar("Error: define %s already defined to tag %d.", define, tempTag)); + //Con::printf("Error: define %s already defined to tag %d.", define, tempTag); + return false; + } + TagEntry *newEntry = (TagEntry *) mempool.alloc(sizeof(TagEntry)); + + newEntry->id = value; + newEntry->define = define; + newEntry->string = string; + + numEntries++; + if(numEntries > numBuckets) + { + numBuckets = numBuckets * 2 + 1; + defineHashBuckets = (TagEntry **) dRealloc(defineHashBuckets, numBuckets * sizeof(TagEntry *)); + idHashBuckets = (TagEntry **) dRealloc(idHashBuckets, numBuckets * sizeof(TagEntry *)); + S32 i; + for(i = 0; i < numBuckets; i++) + { + defineHashBuckets[i] = NULL; + idHashBuckets[i] = NULL; + } + TagEntry *walk = entryChain; + + while(walk) + { + S32 index = hashId(walk->id, numBuckets); + walk->idHashLink = idHashBuckets[index]; + idHashBuckets[index] = walk; + + index = hashDefine(walk->define, numBuckets); + walk->defineHashLink = defineHashBuckets[index]; + defineHashBuckets[index] = walk; + + walk = walk->chain; + } + } + newEntry->chain = entryChain; + entryChain = newEntry; + + S32 index = hashId(newEntry->id, numBuckets); + newEntry->idHashLink = idHashBuckets[index]; + idHashBuckets[index] = newEntry; + + index = hashDefine(newEntry->define, numBuckets); + newEntry->defineHashLink = defineHashBuckets[index]; + defineHashBuckets[index] = newEntry; + return true; +} + +//------------------------------------------------------------------------------ + +bool TagDictionary::writeHeader(Stream& io_sio) +{ + char buff[15000]; + Vector v; + + TagEntry *walk = entryChain; + while(walk) + { + v.push_back(walk->id); + walk = walk->chain; + } + + sortIdVector(v); + + io_sio.write( sizeof(TAG_ASCII_HEADER)-1, TAG_ASCII_HEADER); + io_sio.write( 4, "\r\n\r\n"); + + char exclude[256]; + char tempBuf[256]; + dSprintf(exclude, sizeof(exclude), "_TD%10.10u_H_", Platform::getVirtualMilliseconds() / 4); + + dSprintf(tempBuf, sizeof(tempBuf), "#ifndef %s\r\n", exclude); + io_sio.write(dStrlen(tempBuf), tempBuf); + dSprintf(tempBuf, sizeof(tempBuf), "#define %s\r\n\r\n", exclude); + io_sio.write(dStrlen(tempBuf), tempBuf); + + for (U32 i = 0; i < v.size(); i++) + { + dSprintf(buff, sizeof(buff), "#define %s (%d)\r\n", idToDefine(v[i]), v[i]); + io_sio.write(dStrlen(buff), buff); + } + + dSprintf(tempBuf, sizeof(tempBuf), "\r\n#endif // %s\r\n", exclude); + io_sio.write(dStrlen(tempBuf), tempBuf); + + return (io_sio.getStatus() == Stream::Ok); +} + +//------------------------------------------------------------------------------ + +StringTableEntry TagDictionary::defineToString(StringTableEntry tag) +{ + S32 index = hashDefine(tag, numBuckets); + if (index < 0) return NULL; + TagEntry *walk = defineHashBuckets[index]; + while(walk) + { + if(walk->define == tag) + return walk->string; + walk = walk->defineHashLink; + } + return NULL; +} + +S32 TagDictionary::defineToId(StringTableEntry tag) +{ + S32 index = hashDefine(tag, numBuckets); + if (index < 0) return 0; + TagEntry *walk = defineHashBuckets[index]; + while(walk) + { + if(walk->define == tag) + return walk->id; + walk = walk->defineHashLink; + } + return 0; +} + +StringTableEntry TagDictionary::idToString(S32 id) +{ + S32 index = hashId(id, numBuckets); + if (index < 0) return NULL; + TagEntry *walk = idHashBuckets[index]; + while(walk) + { + if(walk->id == id) + return walk->string; + walk = walk->idHashLink; + } + return NULL; +} + +StringTableEntry TagDictionary::idToDefine(S32 id) +{ + S32 index = hashId(id, numBuckets); + if (index < 0) return NULL; + TagEntry *walk = idHashBuckets[index]; + while(walk) + { + if(walk->id == id) + return walk->define; + walk = walk->idHashLink; + } + return NULL; +} + +//------------------------------------------------------------------------------ + +void TagDictionary::findIDs(Vector& out_v, + const S32 in_minID, + const S32 in_maxID ) +{ + //locate all IDs that lie in between minID and maxID + + TagEntry *walk = entryChain; + while(walk) + { + if(walk->id > in_minID && walk->id < in_maxID) + out_v.push_back(walk->id); + walk = walk->chain; + } + sortIdVector(out_v); +} + + +//------------------------------------------------------------------------------ +void TagDictionary::findStrings(Vector& out_v, const char* in_pPattern) +{ + //locate all strings that match the pattern + // + TagEntry *walk = entryChain; + while(walk) + { + if (match(in_pPattern, walk->string)) + out_v.push_back(walk->id); + walk = walk->chain; + } + sortIdVector(out_v); +} + + +//------------------------------------------------------------------------------ +void TagDictionary::findDefines(Vector& out_v, const char* in_pPattern) +{ + //locate all define strings that match the pattern and add their ID + //to the given vector + // + TagEntry *walk = entryChain; + while(walk) + { + if (match(in_pPattern, walk->define)) + out_v.push_back(walk->id); + walk = walk->chain; + } + sortIdVector(out_v); +} + +//------------------------------------------------------------------------------ + +bool TagDictionary::match(const char* pattern, const char* str) +{ + //quick and dirty recursive DOS-style wild-card string matcher + // + switch (*pattern) { + case '\0': + return !*str; + + case '*': + return match(pattern+1, str) || *str && match(pattern, str+1); + + case '?': + return *str && match(pattern+1, str+1); + + default: + return (*pattern == *str) && match(pattern+1, str+1); + } +} + +//------------------------------------------------------------------------------ + +static int QSORT_CALLBACK idCompare(const void *in_p1, const void *in_p2) +{ + return *((S32 *) in_p1) - *((S32 *) in_p2); +} + +void TagDictionary::sortIdVector(Vector& out_v) +{ + dQsort(out_v.address(), out_v.size(), sizeof(S32), idCompare); +} + diff --git a/Engine/source/core/tagDictionary.h b/Engine/source/core/tagDictionary.h new file mode 100644 index 000000000..9d923c3a4 --- /dev/null +++ b/Engine/source/core/tagDictionary.h @@ -0,0 +1,83 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TAGDICTIONARY_H_ +#define _TAGDICTIONARY_H_ + +#ifndef _STRINGTABLE_H_ +#include "core/stringTable.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +class Stream; + +class TagDictionary +{ + struct TagEntry + { + S32 id; + StringTableEntry define; + StringTableEntry string; + TagEntry *chain; // for linear traversal + TagEntry *defineHashLink; + TagEntry *idHashLink; + }; + + TagEntry **defineHashBuckets; + TagEntry **idHashBuckets; + + TagEntry *entryChain; + DataChunker mempool; + S32 numBuckets; + S32 numEntries; + + bool match(const char* pattern, const char* str); + void sortIdVector(Vector& out_v); +public: + TagDictionary(); + ~TagDictionary(); + + //IO functions + // + bool writeHeader(Stream &); + + // String/Define retrieval and search functions... + // + + bool addEntry(S32 value, StringTableEntry define, StringTableEntry string); + + StringTableEntry defineToString(StringTableEntry tag); + StringTableEntry idToString(S32 tag); + StringTableEntry idToDefine(S32 tag); + S32 defineToId(StringTableEntry tag); + + // get IDs such that minID < IDs < maxID + void findIDs( Vector &v, const S32 minID, const S32 maxID ); + void findStrings( Vector &v, const char *pattern); + void findDefines( Vector &v, const char *pattern); +}; + +extern TagDictionary tagDictionary; + +#endif //_TAGDICTIONARY_H_ diff --git a/Engine/source/core/threadStatic.cpp b/Engine/source/core/threadStatic.cpp new file mode 100644 index 000000000..511db4259 --- /dev/null +++ b/Engine/source/core/threadStatic.cpp @@ -0,0 +1,113 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/threadStatic.h" + +//----------------------------------------------------------------------------- +// Statics +U32 _TorqueThreadStatic::mListIndex = 0; + +_TorqueThreadStaticReg *_TorqueThreadStaticReg::smFirst = NULL; +//----------------------------------------------------------------------------- + +inline Vector &_TorqueThreadStaticReg::getThreadStaticListVector() +{ + // This function assures that the static vector of ThreadStatics will get initialized + // before first use. + static Vector sTorqueThreadStaticVec( __FILE__, __LINE__ ); + + return sTorqueThreadStaticVec; +} + +//----------------------------------------------------------------------------- + +// Destructor, size should == 1 otherwise someone didn't clean up, or someone +// did horrible things to list index 0 +_TorqueThreadStaticReg::~_TorqueThreadStaticReg() +{ + AssertFatal( getThreadStaticListVector().size() == 1, "Destruction of static list was not performed on program exit" ); +} + +//----------------------------------------------------------------------------- + +void _TorqueThreadStaticReg::destroyInstances() +{ + // mThreadStaticInstances[0] does *not* need to be deallocated + // because all members of the list are pointers to static memory + while( getThreadStaticListVector().size() > 1 ) + { + // Delete the members of this list + while( getThreadStaticListVector().last().size() ) + { + _TorqueThreadStatic *biscuit = getThreadStaticListVector().last().first(); + + // Erase the vector entry + getThreadStaticListVector().last().pop_front(); + + // And finally the memory + delete biscuit; + } + + // Remove the entry from the list of lists + getThreadStaticListVector().pop_back(); + } +} + +//----------------------------------------------------------------------------- + +void _TorqueThreadStaticReg::destroyInstance( TorqueThreadStaticList *instanceList ) +{ + AssertFatal( instanceList != &getThreadStaticListVector().first(), "Cannot delete static instance list index 0" ); + + while( instanceList->size() ) + { + _TorqueThreadStatic *biscuit = getThreadStaticListVector().last().first(); + + // Erase the vector entry + getThreadStaticListVector().last().pop_front(); + + // And finally the memory + delete biscuit; + } + + getThreadStaticListVector().erase( instanceList ); +} + +//----------------------------------------------------------------------------- + +TorqueThreadStaticListHandle _TorqueThreadStaticReg::spawnThreadStaticsInstance() +{ + AssertFatal( getThreadStaticListVector().size() > 0, "List is not initialized somehow" ); + + // Add a new list of static instances + getThreadStaticListVector().increment(); + + // Copy mThreadStaticInstances[0] (master copy) into new memory, and + // pass it back. + for( int i = 0; i < getThreadStaticListVector()[0].size(); i++ ) + { + getThreadStaticListVector().last().push_back( getThreadStaticListVector()[0][i]->_createInstance() ); + } + + // Return list index of newly allocated static instance list + return &getThreadStaticListVector().last(); +} \ No newline at end of file diff --git a/Engine/source/core/threadStatic.h b/Engine/source/core/threadStatic.h new file mode 100644 index 000000000..716d590b9 --- /dev/null +++ b/Engine/source/core/threadStatic.h @@ -0,0 +1,216 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TORQUETHREADSTATIC_H_ +#define _TORQUETHREADSTATIC_H_ + +#include "core/util/tVector.h" + +//----------------------------------------------------------------------------- +// TorqueThreadStatic Base Class +class _TorqueThreadStatic +{ + friend class _TorqueThreadStaticReg; + +private: + +#ifdef TORQUE_ENABLE_THREAD_STATIC_METRICS + U32 mHitCount; +#endif + +protected: + static U32 mListIndex; + virtual _TorqueThreadStatic *_createInstance() const = 0; + +public: + _TorqueThreadStatic() +#ifdef TORQUE_ENABLE_THREAD_STATIC_METRICS + : mHitCount( 0 ) +#endif + { }; + + static const U32 getListIndex(){ return mListIndex; } + + virtual void *getMemInstPtr() = 0; + virtual const dsize_t getMemInstSize() const = 0; + +#ifdef TORQUE_ENABLE_THREAD_STATIC_METRICS + _TorqueThreadStatic *_chainHit() { mHitCount++; return this; } + const U32 &trackHit() { return ++mHitCount; } + const U32 &getHitCount() const { return mHitCount; } +#endif +}; +// Typedef +typedef VectorPtr<_TorqueThreadStatic *> TorqueThreadStaticList; +typedef TorqueThreadStaticList * TorqueThreadStaticListHandle; + +//----------------------------------------------------------------------------- +// Auto-registration class and manager of the instances +class _TorqueThreadStaticReg +{ + // This will manage all of the thread static registrations + static _TorqueThreadStaticReg *smFirst; + _TorqueThreadStaticReg *mNext; + + // This is a vector of vectors which will store instances of thread static + // variables. mThreadStaticInsances[0] will be the list of the initial values + // of the statics, and then indexing for instanced versions will start at 1 + // + // Note that the list of instances in mThreadStaticInstances[0] does not, and + // must not get 'delete' called on it, because all members of the list are + // pointers to statically allocated memory. All other lists will be contain + // pointers to dynamically allocated memory, and will need to be freed upon + // termination. + // + // So this was originally a static data member, however that caused problems because + // I was relying on static initialization order to make sure the vector got initialized + // *before* any static instance of this class was created via macro. By wrapping the + // static in a function, I can be assured that the static memory will get initialized + // before it is modified. + static Vector &getThreadStaticListVector(); + +public: + /// Constructor + _TorqueThreadStaticReg( _TorqueThreadStatic *ttsInitial ) + { + // Link this entry into the list + mNext = smFirst; + smFirst = this; + + // Create list 0 (initial values) if it doesn't exist + if( getThreadStaticListVector().empty() ) + getThreadStaticListVector().increment(); + + // Set the index of the thread static for lookup + ttsInitial->mListIndex = getThreadStaticListVector()[0].size(); + + // Add the static to the initial value list + getThreadStaticListVector()[0].push_back( ttsInitial ); + } + + virtual ~_TorqueThreadStaticReg(); + + // Accessors + static const TorqueThreadStaticList &getStaticList( const U32 idx = 0 ) + { + AssertFatal( getThreadStaticListVector().size() > idx, "Out of range static list" ); + + return getThreadStaticListVector()[idx]; + } + + static void destroyInstances(); + static void destroyInstance( TorqueThreadStaticList *instanceList ); + + static const _TorqueThreadStaticReg *getFirst() { return smFirst; } + + const _TorqueThreadStaticReg *getNext() const { return mNext; } + + /// Spawn another copy of the ThreadStatics and pass back the id + static TorqueThreadStaticListHandle spawnThreadStaticsInstance(); +}; + +//----------------------------------------------------------------------------- +// Template class that will get used as a base for the thread statics +template +class TorqueThreadStatic : public _TorqueThreadStatic +{ + // The reg object will want access to mInstance + friend class _TorqueThreadStaticReg; + +private: + T mInstance; + +public: + TorqueThreadStatic( T instanceVal ) : mInstance( instanceVal ) {} + virtual void *getMemInstPtr() { return &mInstance; } + + // I am not sure these are needed, and I don't want to create confusing-to-debug code +#if 0 + // Operator overloads + operator T*() { return &mInstance; } + operator T*() const { return &mInstance; } + operator const T*() const { return &mInstance; } + + bool operator ==( const T &l ) const { return mInstance == l; } + bool operator !=( const T &l ) const { return mInstance != l; } + + T &operator =( const T &l ) { mInstance = l; return mInstance; } +#endif // if0 +}; + +//----------------------------------------------------------------------------- +// If ThreadStatic behavior is not enabled, than the macros will resolve +// to regular, static memory +#ifndef TORQUE_ENABLE_THREAD_STATICS + +#define DITTS( type, name, initialvalue ) static type name = initialvalue +#define ATTS( name ) name + +#else // TORQUE_ENABLE_THREAD_STATICS is defined + +//----------------------------------------------------------------------------- +// Declare TorqueThreadStatic, and initialize it's value +// +// This macro would be used in a .cpp file to declare a ThreadStatic +#define DITTS(type, name, initalvalue) \ +class _##name##TorqueThreadStatic : public TorqueThreadStatic \ +{ \ +protected:\ + virtual _TorqueThreadStatic *_createInstance() const { return new _##name##TorqueThreadStatic; } \ +public: \ + _##name##TorqueThreadStatic() : TorqueThreadStatic( initalvalue ) {} \ + virtual const dsize_t getMemInstSize() const { return sizeof( type ); } \ + type &_cast() { return *reinterpret_cast( getMemInstPtr() ); } \ + const type &_const_cast() const { return *reinterpret_cast( getMemInstPtr() ); } \ +}; \ +static _##name##TorqueThreadStatic name##TorqueThreadStatic; \ +static _TorqueThreadStaticReg _##name##TTSReg( reinterpret_cast<_TorqueThreadStatic *>( & name##TorqueThreadStatic ) ) + +//----------------------------------------------------------------------------- +// Access TorqueThreadStatic + +// NOTE: TEMPDEF is there as a temporary place holder for however we want to get the index of the currently running +// thread or whatever. +#define TEMPDEF 0 + +#ifdef TORQUE_ENABLE_THREAD_STATIC_METRICS +// Access counting macro +# define ATTS_(name, idx) \ + (reinterpret_cast< _##name##TorqueThreadStatic *>( _TorqueThreadStaticReg::getStaticList( idx )[ _##name##TorqueThreadStatic::getListIndex() ]->_chainHit() )->_cast() ) +// Const access counting macro +# define CATTS_(name, idx) \ + (reinterpret_cast< _##name##TorqueThreadStatic *>( _TorqueThreadStaticReg::getStaticList( idx )[ _##name##TorqueThreadStatic::getListIndex() ]->_chainHit() )->_const_cast() ) +#else +// Regular access macro +# define ATTS_(name, idx) \ + (reinterpret_cast< _##name##TorqueThreadStatic *>( _TorqueThreadStaticReg::getStaticList( idx )[ _##name##TorqueThreadStatic::getListIndex() ] )->_cast() ) +// Const access macro +# define CATTS_(name, idx) \ + (reinterpret_cast< _##name##TorqueThreadStatic *>( _TorqueThreadStaticReg::getStaticList( idx )[ _##name##TorqueThreadStatic::getListIndex() ] )->_const_cast() ) +#endif // TORQUE_ENABLE_THREAD_STATIC_METRICS + +#define ATTS(name) ATTS_(name, TEMPDEF) +#define CATTS(name) CATTS_(name, TEMPDEF) + +#endif // TORQUE_ENABLE_THREAD_STATICS + +#endif \ No newline at end of file diff --git a/Engine/source/core/tokenizer.cpp b/Engine/source/core/tokenizer.cpp new file mode 100644 index 000000000..adbb14fff --- /dev/null +++ b/Engine/source/core/tokenizer.cpp @@ -0,0 +1,613 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/tokenizer.h" +#include "platform/platform.h" +#include "core/stream/fileStream.h" +#include "core/strings/stringFunctions.h" +#include "core/util/safeDelete.h" + +Tokenizer::Tokenizer() +{ + dMemset(mFileName, 0, sizeof(mFileName)); + + mpBuffer = NULL; + mBufferSize = 0; + + mStartPos = 0; + mCurrPos = 0; + + mTokenIsQuoted = false; + + dMemset(mCurrTokenBuffer, 0, sizeof(mCurrTokenBuffer)); + mTokenIsCurrent = false; + + mSingleTokens = NULL; + + VECTOR_SET_ASSOCIATION(mLinePositions); +} + +Tokenizer::~Tokenizer() +{ + clear(); +} + +bool Tokenizer::openFile(const char* pFileName) +{ + AssertFatal(mFileName[0] == '\0', "Reuse of Tokenizers not allowed!"); + + FileStream* pStream = new FileStream; + if (pStream->open(pFileName, Torque::FS::File::Read) == false) + { + delete pStream; + return false; + } + dStrcpy(mFileName, pFileName); + + mBufferSize = pStream->getStreamSize(); + mpBuffer = new char[mBufferSize]; + pStream->read(mBufferSize, mpBuffer); + pStream->close(); + delete pStream; + + reset(); + + buildLinePositions(); + + return true; +} + +bool Tokenizer::openFile(Stream* pStream) +{ + mBufferSize = pStream->getStreamSize(); + mpBuffer = new char[mBufferSize]; + pStream->read(mBufferSize, mpBuffer); + + reset(); + + buildLinePositions(); + + return true; +} + +void Tokenizer::setBuffer(const char* buffer, U32 bufferSize) +{ + if (mpBuffer) + { + SAFE_DELETE_ARRAY(mpBuffer); + mBufferSize = 0; + } + + mBufferSize = bufferSize; + mpBuffer = new char[mBufferSize + 1]; + dStrcpy(mpBuffer, buffer); + + reset(); + + buildLinePositions(); +} + +void Tokenizer::setSingleTokens(const char* singleTokens) +{ + if (mSingleTokens) + SAFE_DELETE(mSingleTokens); + + if (singleTokens) + mSingleTokens = dStrdup(singleTokens); +} + +bool Tokenizer::reset() +{ + mStartPos = 0; + mCurrPos = 0; + + mTokenIsQuoted = false; + + dMemset(mCurrTokenBuffer, 0, sizeof(mCurrTokenBuffer)); + mTokenIsCurrent = false; + + return true; +} + +bool Tokenizer::clear() +{ + // Delete our buffer + if (mpBuffer) + SAFE_DELETE_ARRAY(mpBuffer); + + // Reset the buffer size + mBufferSize = 0; + + // Reset our active data + reset(); + + // Clear our line positions + mLinePositions.clear(); + + // Reset our file name + dMemset(mFileName, 0, 1024); + + // Wipe the single tokens + setSingleTokens(NULL); + + return true; +} + +bool Tokenizer::setCurrentPos(U32 pos) +{ + mCurrPos = pos; + mTokenIsCurrent = false; + + return advanceToken(true); +} + +void Tokenizer::buildLinePositions() +{ + if (mBufferSize == 0) + return; + + // We can safely assume that the first line is at position 0 + mLinePositions.push_back(0); + + U32 currPos = 0; + while (currPos + 1 < mBufferSize) + { + // Windows line ending + if (mpBuffer[currPos] == '\r' && mpBuffer[currPos + 1] == '\n') + { + currPos += 2; + + mLinePositions.push_back(currPos); + } + // Not sure if this ever happens but just in case + else if (mpBuffer[currPos] == '\n' && mpBuffer[currPos + 1] == '\r') + { + currPos += 2; + + mLinePositions.push_back(currPos); + } + // Unix line endings should only have a single line break character + else if (mpBuffer[currPos] == '\n' || mpBuffer[currPos] == '\r') + { + currPos++; + + mLinePositions.push_back(currPos); + } + else + currPos++; + } +} + +U32 Tokenizer::getLinePosition(const U32 pos, U32 lowIndex, S32 highIndex) +{ + // If we have one or less lines then + // the result is easy + if (mLinePositions.size() <= 1) + return 0; + + // Now that we know we have at least one position + // we can do a quick test against the last line + if (pos >= mLinePositions.last()) + return mLinePositions.size() - 1; + + // If this is the beginning of the search + // set a good starting point (the middle) + if (highIndex < 0) + highIndex = mLinePositions.size() - 1; + + // Just in case bad values got handed in + if (lowIndex > highIndex) + lowIndex = highIndex; + + // Compute our test index (middle) + U32 testIndex = (lowIndex + highIndex) / 2; + + // Make sure that our test indices are valid + if (testIndex >= mLinePositions.size() || + testIndex + 1 >= mLinePositions.size()) + return mLinePositions.size() - 1; + + // See if we are already at the right line + if (pos >= mLinePositions[testIndex] && pos < mLinePositions[testIndex + 1]) + return testIndex; + + if (pos < mLinePositions[testIndex]) + highIndex = testIndex; + else + lowIndex = testIndex; + + return getLinePosition(pos, lowIndex, highIndex); +} + +U32 Tokenizer::getCurrentLine() +{ + // Binary search for the line number whose + // position is equal to or lower than the + // current position + return getLinePosition(mStartPos); +} + +U32 Tokenizer::getTokenLineOffset() +{ + U32 lineNumber = getCurrentLine(); + + if (lineNumber >= mLinePositions.size()) + return 0; + + U32 linePosition = mLinePositions[lineNumber]; + + if (linePosition >= mStartPos) + return 0; + + return mStartPos - linePosition; +} + +bool Tokenizer::advanceToken(const bool crossLine, const bool assertAvail) +{ + if (mTokenIsCurrent == true) + { + AssertFatal(mCurrTokenBuffer[0] != '\0', "No token, but marked as current?"); + mTokenIsCurrent = false; + return true; + } + + U32 currPosition = 0; + mCurrTokenBuffer[0] = '\0'; + + mTokenIsQuoted = false; + + // Store the beginning of the previous advance + // and the beginning of the current advance + mStartPos = mCurrPos; + + while (mCurrPos < mBufferSize) + { + char c = mpBuffer[mCurrPos]; + + bool cont = true; + + if (mSingleTokens && dStrchr(mSingleTokens, c)) + { + if (currPosition == 0) + { + mCurrTokenBuffer[currPosition++] = c; + mCurrPos++; + cont = false; + break; + } + else + { + // End of token + cont = false; + } + } + else + { + switch (c) + { + case ' ': + case '\t': + if (currPosition == 0) + { + // Token hasn't started yet... + mCurrPos++; + } + else + { + // End of token + mCurrPos++; + cont = false; + } + break; + + case '\r': + case '\n': + if (crossLine == true) + { + // Windows line ending + if (mpBuffer[mCurrPos] == '\r' && mpBuffer[mCurrPos + 1] == '\n') + mCurrPos += 2; + // Not sure if this ever happens but just in case + else if (mpBuffer[mCurrPos] == '\n' && mpBuffer[mCurrPos + 1] == '\r') + mCurrPos += 2; + // Unix line endings should only have a single line break character + else + mCurrPos++; + } + else + { + cont = false; + break; + } + break; + + default: + if (c == '\"' || c == '\'') + { + // Quoted token + U32 startLine = getCurrentLine(); + mCurrPos++; + + // Store the beginning of the token + mStartPos = mCurrPos; + + while (mpBuffer[mCurrPos] != c) + { + AssertISV(mCurrPos < mBufferSize, + avar("End of file before quote closed. Quote started: (%s: %d)", + getFileName(), startLine)); + AssertISV((mpBuffer[mCurrPos] != '\n' && mpBuffer[mCurrPos] != '\r'), + avar("End of line reached before end of quote. Quote started: (%s: %d)", + getFileName(), startLine)); + + mCurrTokenBuffer[currPosition++] = mpBuffer[mCurrPos++]; + } + + mTokenIsQuoted = true; + + mCurrPos++; + cont = false; + } + else if (c == '/' && mpBuffer[mCurrPos+1] == '/') + { + // Line quote... + if (currPosition == 0) + { + // continue to end of line, then let crossLine determine on the next pass + while (mCurrPos < mBufferSize && (mpBuffer[mCurrPos] != '\n' && mpBuffer[mCurrPos] != '\r')) + mCurrPos++; + } + else + { + // This is the end of the token. Continue to EOL + while (mCurrPos < mBufferSize && (mpBuffer[mCurrPos] != '\n' && mpBuffer[mCurrPos] != '\r')) + mCurrPos++; + cont = false; + } + } + else + { + // If this is the first non-token character then store the + // beginning of the token + if (currPosition == 0) + mStartPos = mCurrPos; + + mCurrTokenBuffer[currPosition++] = c; + mCurrPos++; + } + break; + } + } + + if (cont == false) + break; + } + + mCurrTokenBuffer[currPosition] = '\0'; + + if (assertAvail == true) + AssertISV(currPosition != 0, avar("Error parsing: %s at or around line: %d", getFileName(), getCurrentLine())); + + if (mCurrPos == mBufferSize) + return false; + + return true; +} + +bool Tokenizer::regressToken(const bool crossLine) +{ + if (mTokenIsCurrent == true) + { + AssertFatal(mCurrTokenBuffer[0] != '\0', "No token, but marked as current?"); + mTokenIsCurrent = false; + return true; + } + + U32 currPosition = 0; + mCurrTokenBuffer[0] = '\0'; + + mTokenIsQuoted = false; + + // Store the beginning of the previous advance + // and the beginning of the current advance + mCurrPos = mStartPos; + + // Back up to the first character of the previous token + mStartPos--; + + while (mStartPos > 0) + { + char c = mpBuffer[mStartPos]; + + bool cont = true; + + if (mSingleTokens && dStrchr(mSingleTokens, c)) + { + if (currPosition == 0) + { + mCurrTokenBuffer[currPosition++] = c; + mStartPos--; + cont = false; + break; + } + else + { + // End of token + cont = false; + } + } + else + { + switch (c) + { + case ' ': + case '\t': + if (currPosition == 0) + { + // Token hasn't started yet... + mStartPos--; + } + else + { + // End of token + mStartPos--; + cont = false; + } + break; + + case '\r': + case '\n': + if (crossLine == true && currPosition == 0) + { + // Windows line ending + if (mStartPos > 0 && mpBuffer[mStartPos] == '\r' && mpBuffer[mStartPos - 1] == '\n') + mStartPos -= 2; + // Not sure if this ever happens but just in case + else if (mStartPos > 0 && mpBuffer[mStartPos] == '\n' && mpBuffer[mStartPos - 1] == '\r') + mStartPos -= 2; + // Unix line endings should only have a single line break character + else + mStartPos--; + } + else + { + cont = false; + break; + } + break; + + default: + if (c == '\"' || c == '\'') + { + // Quoted token + U32 endLine = getCurrentLine(); + mStartPos--; + + while (mpBuffer[mStartPos] != c) + { + AssertISV(mStartPos < 0, + avar("Beginning of file reached before finding begin quote. Quote ended: (%s: %d)", + getFileName(), endLine)); + + mCurrTokenBuffer[currPosition++] = mpBuffer[mStartPos--]; + } + + mTokenIsQuoted = true; + + mStartPos--; + cont = false; + } + else if (c == '/' && mStartPos > 0 && mpBuffer[mStartPos - 1] == '/') + { + // Line quote... + // Clear out anything saved already + currPosition = 0; + + mStartPos -= 2; + } + else + { + mCurrTokenBuffer[currPosition++] = c; + mStartPos--; + } + break; + } + } + + if (cont == false) + break; + } + + mCurrTokenBuffer[currPosition] = '\0'; + + // Reveres the token + for (U32 i = 0; i < currPosition / 2; i++) + { + char c = mCurrTokenBuffer[i]; + mCurrTokenBuffer[i] = mCurrTokenBuffer[currPosition - i - 1]; + mCurrTokenBuffer[currPosition - i - 1] = c; + } + + mStartPos++; + + if (mStartPos == mCurrPos) + return false; + + return true; +} + +bool Tokenizer::tokenAvailable() +{ + // Note: this implies that when advanceToken(false) fails, it must cap the + // token buffer. + // + return mCurrTokenBuffer[0] != '\0'; +} + +const char* Tokenizer::getToken() const +{ + return mCurrTokenBuffer; +} + +const char* Tokenizer::getNextToken() +{ + advanceToken(true); + + return getToken(); +} + +bool Tokenizer::tokenICmp(const char* pCmp) const +{ + return dStricmp(mCurrTokenBuffer, pCmp) == 0; +} + +bool Tokenizer::findToken(U32 start, const char* pCmp) +{ + // Move to the start + setCurrentPos(start); + + // In case the first token is what we are looking for + if (tokenICmp(pCmp)) + return true; + + // Loop through the file and see if the token exists + while (advanceToken(true)) + { + if (tokenICmp(pCmp)) + return true; + } + + return false; +} + +bool Tokenizer::findToken(const char* pCmp) +{ + return findToken(0, pCmp); +} + +bool Tokenizer::endOfFile() +{ + if (mCurrPos < mBufferSize) + return false; + else + return true; +} \ No newline at end of file diff --git a/Engine/source/core/tokenizer.h b/Engine/source/core/tokenizer.h new file mode 100644 index 000000000..4a0bf476d --- /dev/null +++ b/Engine/source/core/tokenizer.h @@ -0,0 +1,109 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TOKENIZER_H_ +#define _TOKENIZER_H_ + +//Includes +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif + +#ifndef _STREAM_H_ +#include "core/stream/stream.h" +#endif + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +class SizedStream; + +class Tokenizer +{ + public: + enum + { + MaxTokenSize = 1023 + }; + + private: + char mFileName[1024]; + + char* mpBuffer; + U32 mBufferSize; + + S32 mCurrPos; + S32 mStartPos; + + bool mTokenIsQuoted; + + char mCurrTokenBuffer[MaxTokenSize + 1]; + bool mTokenIsCurrent; + + char* mSingleTokens; + + Vector mLinePositions; + + public: + Tokenizer(); + ~Tokenizer(); + + bool openFile(const char* pFileName); + bool openFile(Stream* pStream); + void setBuffer(const char* buffer, U32 bufferSize); + + void setSingleTokens(const char* singleTokens); + + void buildLinePositions(); + + bool advanceToken(const bool crossLine, const bool assertAvailable = false); + bool regressToken(const bool crossLine); + bool tokenAvailable(); + + const char* getToken() const; + const char* getNextToken(); + bool tokenICmp(const char* pCmp) const; + bool tokenIsQuoted() const { return mTokenIsQuoted; } + + bool findToken(const char* pCmp); + bool findToken(U32 start, const char* pCmp); + + const char* getFileName() const { return mFileName; } + + U32 getLinePosition(const U32 pos, U32 lowIndex = 0, S32 highIndex = -1); + U32 getCurrentLine(); + U32 getTokenLineOffset(); + U32 getCurrentPos() const { return mCurrPos; } + + bool setCurrentPos(U32 pos); + + // Resets our active data but leaves the buffer intact + bool reset(); + // Clears the buffer and resets the active data + bool clear(); + + bool endOfFile(); +}; + + +#endif //_TOKENIZER_H_ diff --git a/Engine/source/core/util/FastDelegate.h b/Engine/source/core/util/FastDelegate.h new file mode 100644 index 000000000..4ac73c6cb --- /dev/null +++ b/Engine/source/core/util/FastDelegate.h @@ -0,0 +1,2107 @@ +// FastDelegate.h +// Efficient delegates in C++ that generate only two lines of asm code! +// Documentation is found at http://www.codeproject.com/cpp/FastDelegate.asp +// +// - Don Clugston, Mar 2004. +// Major contributions were made by Jody Hagins. +// History: +// 24-Apr-04 1.0 * Submitted to CodeProject. +// 28-Apr-04 1.1 * Prevent most unsafe uses of evil static function hack. +// * Improved syntax for horrible_cast (thanks Paul Bludov). +// * Tested on Metrowerks MWCC and Intel ICL (IA32) +// * Compiled, but not run, on Comeau C++ and Intel Itanium ICL. +// 27-Jun-04 1.2 * Now works on Borland C++ Builder 5.5 +// * Now works on /clr "managed C++" code on VC7, VC7.1 +// * Comeau C++ now compiles without warnings. +// * Prevent the virtual inheritance case from being used on +// VC6 and earlier, which generate incorrect code. +// * Improved warning and error messages. Non-standard hacks +// now have compile-time checks to make them safer. +// * implicit_cast used instead of static_cast in many cases. +// * If calling a const member function, a const class pointer can be used. +// * MakeDelegate() global helper function added to simplify pass-by-value. +// * Added fastdelegate.clear() +// 16-Jul-04 1.2.1* Workaround for gcc bug (const member function pointers in templates) +// 30-Oct-04 1.3 * Support for (non-void) return values. +// * No more workarounds in client code! +// MSVC and Intel now use a clever hack invented by John Dlugosz: +// - The FASTDELEGATEDECLARE workaround is no longer necessary. +// - No more warning messages for VC6 +// * Less use of macros. Error messages should be more comprehensible. +// * Added include guards +// * Added FastDelegate::empty() to test if invocation is safe (Thanks Neville Franks). +// * Now tested on VS 2005 Express Beta, PGI C++ +// 24-Dec-04 1.4 * Added DelegateMemento, to allow collections of disparate delegates. +// * <,>,<=,>= comparison operators to allow storage in ordered containers. +// * Substantial reduction of code size, especially the 'Closure' class. +// * Standardised all the compiler-specific workarounds. +// * MFP conversion now works for CodePlay (but not yet supported in the full code). +// * Now compiles without warnings on _any_ supported compiler, including BCC 5.5.1 +// * New syntax: FastDelegate< int (char *, double) >. +// 14-Feb-05 1.4.1* Now treats =0 as equivalent to .clear(), ==0 as equivalent to .empty(). (Thanks elfric). +// * Now tested on Intel ICL for AMD64, VS2005 Beta for AMD64 and Itanium. +// 30-Mar-05 1.5 * Safebool idiom: "if (dg)" is now equivalent to "if (!dg.empty())" +// * Fully supported by CodePlay VectorC +// * Bugfix for Metrowerks: empty() was buggy because a valid MFP can be 0 on MWCC! +// * More optimal assignment,== and != operators for static function pointers. + +#ifndef FASTDELEGATE_H +#define FASTDELEGATE_H +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + + +//////////////////////////////////////////////////////////////////////////////// +// Configuration options +// +//////////////////////////////////////////////////////////////////////////////// + +// Uncomment the following #define for optimally-sized delegates. +// In this case, the generated asm code is almost identical to the code you'd get +// if the compiler had native support for delegates. +// It will not work on systems where sizeof(dataptr) < sizeof(codeptr). +// Thus, it will not work for DOS compilers using the medium model. +// It will also probably fail on some DSP systems. +#define FASTDELEGATE_USESTATICFUNCTIONHACK + +// Uncomment the next line to allow function declarator syntax. +// It is automatically enabled for those compilers where it is known to work. +#define FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX + +//////////////////////////////////////////////////////////////////////////////// +// Compiler identification for workarounds +// +//////////////////////////////////////////////////////////////////////////////// + +// Compiler identification. It's not easy to identify Visual C++ because +// many vendors fraudulently define Microsoft's identifiers. +#if defined(_MSC_VER) && !defined(__MWERKS__) && !defined(__VECTOR_C) && !defined(__ICL) && !defined(__BORLANDC__) +#define FASTDLGT_ISMSVC + +#if (_MSC_VER <1300) // Many workarounds are required for VC6. +#define FASTDLGT_VC6 +#pragma warning(disable:4786) // disable this ridiculous warning +#endif + +#endif + +// Does the compiler uses Microsoft's member function pointer structure? +// If so, it needs special treatment. +// Metrowerks CodeWarrior, Intel, and CodePlay fraudulently define Microsoft's +// identifier, _MSC_VER. We need to filter Metrowerks out. +#if defined(_MSC_VER) && !defined(__MWERKS__) +#define FASTDLGT_MICROSOFT_MFP + +#if !defined(__VECTOR_C) +// CodePlay doesn't have the __single/multi/virtual_inheritance keywords +#define FASTDLGT_HASINHERITANCE_KEYWORDS +#endif +#endif + +// Does it allow function declarator syntax? The following compilers are known to work: +#if defined(FASTDLGT_ISMSVC) && (_MSC_VER >=1310) // VC 7.1 +#define FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX +#endif + +// Gcc(2.95+), and versions of Digital Mars, Intel and Comeau in common use. +#if defined (__DMC__) || defined(__GNUC__) || defined(__ICL) || defined(__COMO__) +#define FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX +#endif + +// It works on Metrowerks MWCC 3.2.2. From boost.Config it should work on earlier ones too. +#if defined (__MWERKS__) +#define FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX +#endif + +#ifdef __GNUC__ // Workaround GCC bug #8271 + // At present, GCC doesn't recognize constness of MFPs in templates +#define FASTDELEGATE_GCC_BUG_8271 +#endif + + + +//////////////////////////////////////////////////////////////////////////////// +// General tricks used in this code +// +// (a) Error messages are generated by typdefing an array of negative size to +// generate compile-time errors. +// (b) Warning messages on MSVC are generated by declaring unused variables, and +// enabling the "variable XXX is never used" warning. +// (c) Unions are used in a few compiler-specific cases to perform illegal casts. +// (d) For Microsoft and Intel, when adjusting the 'this' pointer, it's cast to +// (char *) first to ensure that the correct number of *bytes* are added. +// +//////////////////////////////////////////////////////////////////////////////// +// Helper templates +// +//////////////////////////////////////////////////////////////////////////////// + + +namespace fastdelegate { +namespace detail { // we'll hide the implementation details in a nested namespace. + +// implicit_cast< > +// I believe this was originally going to be in the C++ standard but +// was left out by accident. It's even milder than static_cast. +// I use it instead of static_cast<> to emphasize that I'm not doing +// anything nasty. +// Usage is identical to static_cast<> +template +inline OutputClass implicit_cast(InputClass input){ + return input; +} + +// horrible_cast< > +// This is truly evil. It completely subverts C++'s type system, allowing you +// to cast from any class to any other class. Technically, using a union +// to perform the cast is undefined behaviour (even in C). But we can see if +// it is OK by checking that the union is the same size as each of its members. +// horrible_cast<> should only be used for compiler-specific workarounds. +// Usage is identical to reinterpret_cast<>. + +// This union is declared outside the horrible_cast because BCC 5.5.1 +// can't inline a function with a nested class, and gives a warning. +template +union horrible_union{ + OutputClass out; + InputClass in; +}; + +template +inline OutputClass horrible_cast(const InputClass input){ + horrible_union u; + // Cause a compile-time error if in, out and u are not the same size. + // If the compile fails here, it means the compiler has peculiar + // unions which would prevent the cast from working. + typedef int ERROR_CantUseHorrible_cast[sizeof(InputClass)==sizeof(u) + && sizeof(InputClass)==sizeof(OutputClass) ? 1 : -1]; + u.in = input; + return u.out; +} + +//////////////////////////////////////////////////////////////////////////////// +// Workarounds +// +//////////////////////////////////////////////////////////////////////////////// + +// Backwards compatibility: This macro used to be necessary in the virtual inheritance +// case for Intel and Microsoft. Now it just forward-declares the class. +#define FASTDELEGATEDECLARE(CLASSNAME) class CLASSNAME; + +// Prevent use of the static function hack with the DOS medium model. +#ifdef __MEDIUM__ +#undef FASTDELEGATE_USESTATICFUNCTIONHACK +#endif + +// DefaultVoid - a workaround for 'void' templates in VC6. +// +// (1) VC6 and earlier do not allow 'void' as a default template argument. +// (2) They also doesn't allow you to return 'void' from a function. +// +// Workaround for (1): Declare a dummy type 'DefaultVoid' which we use +// when we'd like to use 'void'. We convert it into 'void' and back +// using the templates DefaultVoidToVoid<> and VoidToDefaultVoid<>. +// Workaround for (2): On VC6, the code for calling a void function is +// identical to the code for calling a non-void function in which the +// return value is never used, provided the return value is returned +// in the EAX register, rather than on the stack. +// This is true for most fundamental types such as int, enum, void *. +// Const void * is the safest option since it doesn't participate +// in any automatic conversions. But on a 16-bit compiler it might +// cause extra code to be generated, so we disable it for all compilers +// except for VC6 (and VC5). +#ifdef FASTDLGT_VC6 +// VC6 workaround +typedef const void * DefaultVoid; +#else +// On any other compiler, just use a normal void. +typedef void DefaultVoid; +#endif + +// Translate from 'DefaultVoid' to 'void'. +// Everything else is unchanged +template +struct DefaultVoidToVoid { typedef T type; }; + +template <> +struct DefaultVoidToVoid { typedef void type; }; + +// Translate from 'void' into 'DefaultVoid' +// Everything else is unchanged +template +struct VoidToDefaultVoid { typedef T type; }; + +template <> +struct VoidToDefaultVoid { typedef DefaultVoid type; }; + + + +//////////////////////////////////////////////////////////////////////////////// +// Fast Delegates, part 1: +// +// Conversion of member function pointer to a standard form +// +//////////////////////////////////////////////////////////////////////////////// + +// GenericClass is a fake class, ONLY used to provide a type. +// It is vitally important that it is never defined, so that the compiler doesn't +// think it can optimize the invocation. For example, Borland generates simpler +// code if it knows the class only uses single inheritance. + +// Compilers using Microsoft's structure need to be treated as a special case. +#ifdef FASTDLGT_MICROSOFT_MFP + +#ifdef FASTDLGT_HASINHERITANCE_KEYWORDS + // For Microsoft and Intel, we want to ensure that it's the most efficient type of MFP + // (4 bytes), even when the /vmg option is used. Declaring an empty class + // would give 16 byte pointers in this case.... + class __single_inheritance GenericClass; +#endif + // ...but for Codeplay, an empty class *always* gives 4 byte pointers. + // If compiled with the /clr option ("managed C++"), the JIT compiler thinks + // it needs to load GenericClass before it can call any of its functions, + // (compiles OK but crashes at runtime!), so we need to declare an + // empty class to make it happy. + // Codeplay and VC4 can't cope with the unknown_inheritance case either. + class GenericClass {}; +#else + class GenericClass; +#endif + +// The size of a single inheritance member function pointer. +const int SINGLE_MEMFUNCPTR_SIZE = sizeof(void (GenericClass::*)()); + +// SimplifyMemFunc< >::Convert() +// +// A template function that converts an arbitrary member function pointer into the +// simplest possible form of member function pointer, using a supplied 'this' pointer. +// According to the standard, this can be done legally with reinterpret_cast<>. +// For (non-standard) compilers which use member function pointers which vary in size +// depending on the class, we need to use knowledge of the internal structure of a +// member function pointer, as used by the compiler. Template specialization is used +// to distinguish between the sizes. Because some compilers don't support partial +// template specialisation, I use full specialisation of a wrapper struct. + +// general case -- don't know how to convert it. Force a compile failure +template +struct SimplifyMemFunc { + template + inline static GenericClass *Convert(X *pthis, XFuncType function_to_bind, + GenericMemFuncType &bound_func) { + // Unsupported member function type -- force a compile failure. + // (it's illegal to have a array with negative size). + typedef char ERROR_Unsupported_member_function_pointer_on_this_compiler[N-100]; + return 0; + } +}; + +// For compilers where all member func ptrs are the same size, everything goes here. +// For non-standard compilers, only single_inheritance classes go here. +template <> +struct SimplifyMemFunc { + template + inline static GenericClass *Convert(X *pthis, XFuncType function_to_bind, + GenericMemFuncType &bound_func) { +#if defined __DMC__ + // Digital Mars doesn't allow you to cast between abitrary PMF's, + // even though the standard says you can. The 32-bit compiler lets you + // static_cast through an int, but the DOS compiler doesn't. + bound_func = horrible_cast(function_to_bind); +#else + bound_func = reinterpret_cast(function_to_bind); +#endif + return reinterpret_cast(pthis); + } +}; + +//////////////////////////////////////////////////////////////////////////////// +// Fast Delegates, part 1b: +// +// Workarounds for Microsoft and Intel +// +//////////////////////////////////////////////////////////////////////////////// + + +// Compilers with member function pointers which violate the standard (MSVC, Intel, Codeplay), +// need to be treated as a special case. +#ifdef FASTDLGT_MICROSOFT_MFP + +// We use unions to perform horrible_casts. I would like to use #pragma pack(push, 1) +// at the start of each function for extra safety, but VC6 seems to ICE +// intermittently if you do this inside a template. + +// __multiple_inheritance classes go here +// Nasty hack for Microsoft and Intel (IA32 and Itanium) +template<> +struct SimplifyMemFunc< SINGLE_MEMFUNCPTR_SIZE + sizeof(int) > { + template + inline static GenericClass *Convert(X *pthis, XFuncType function_to_bind, + GenericMemFuncType &bound_func) { + // We need to use a horrible_cast to do this conversion. + // In MSVC, a multiple inheritance member pointer is internally defined as: + union { + XFuncType func; + struct { + GenericMemFuncType funcaddress; // points to the actual member function + int delta; // #BYTES to be added to the 'this' pointer + }s; + } u; + // Check that the horrible_cast will work + typedef int ERROR_CantUsehorrible_cast[sizeof(function_to_bind)==sizeof(u.s)? 1 : -1]; + u.func = function_to_bind; + bound_func = u.s.funcaddress; + return reinterpret_cast(reinterpret_cast(pthis) + u.s.delta); + } +}; + +// virtual inheritance is a real nuisance. It's inefficient and complicated. +// On MSVC and Intel, there isn't enough information in the pointer itself to +// enable conversion to a closure pointer. Earlier versions of this code didn't +// work for all cases, and generated a compile-time error instead. +// But a very clever hack invented by John M. Dlugosz solves this problem. +// My code is somewhat different to his: I have no asm code, and I make no +// assumptions about the calling convention that is used. + +// In VC++ and ICL, a virtual_inheritance member pointer +// is internally defined as: +struct MicrosoftVirtualMFP { + void (GenericClass::*codeptr)(); // points to the actual member function + int delta; // #bytes to be added to the 'this' pointer + int vtable_index; // or 0 if no virtual inheritance +}; +// The CRUCIAL feature of Microsoft/Intel MFPs which we exploit is that the +// m_codeptr member is *always* called, regardless of the values of the other +// members. (This is *not* true for other compilers, eg GCC, which obtain the +// function address from the vtable if a virtual function is being called). +// Dlugosz's trick is to make the codeptr point to a probe function which +// returns the 'this' pointer that was used. + +// Define a generic class that uses virtual inheritance. +// It has a trival member function that returns the value of the 'this' pointer. +struct GenericVirtualClass : virtual public GenericClass +{ + typedef GenericVirtualClass * (GenericVirtualClass::*ProbePtrType)(); + GenericVirtualClass * GetThis() { return this; } +}; + +// __virtual_inheritance classes go here +template <> +struct SimplifyMemFunc +{ + + template + inline static GenericClass *Convert(X *pthis, XFuncType function_to_bind, + GenericMemFuncType &bound_func) { + union { + XFuncType func; + GenericClass* (X::*ProbeFunc)(); + MicrosoftVirtualMFP s; + } u; + u.func = function_to_bind; + bound_func = reinterpret_cast(u.s.codeptr); + union { + GenericVirtualClass::ProbePtrType virtfunc; + MicrosoftVirtualMFP s; + } u2; + // Check that the horrible_cast<>s will work + typedef int ERROR_CantUsehorrible_cast[sizeof(function_to_bind)==sizeof(u.s) + && sizeof(function_to_bind)==sizeof(u.ProbeFunc) + && sizeof(u2.virtfunc)==sizeof(u2.s) ? 1 : -1]; + // Unfortunately, taking the address of a MF prevents it from being inlined, so + // this next line can't be completely optimised away by the compiler. + u2.virtfunc = &GenericVirtualClass::GetThis; + u.s.codeptr = u2.s.codeptr; + return (pthis->*u.ProbeFunc)(); + } +}; + +#if (_MSC_VER <1300) + +// Nasty hack for Microsoft Visual C++ 6.0 +// unknown_inheritance classes go here +// There is a compiler bug in MSVC6 which generates incorrect code in this case!! +template <> +struct SimplifyMemFunc +{ + template + inline static GenericClass *Convert(X *pthis, XFuncType function_to_bind, + GenericMemFuncType &bound_func) { + // There is an apalling but obscure compiler bug in MSVC6 and earlier: + // vtable_index and 'vtordisp' are always set to 0 in the + // unknown_inheritance case! + // This means that an incorrect function could be called!!! + // Compiling with the /vmg option leads to potentially incorrect code. + // This is probably the reason that the IDE has a user interface for specifying + // the /vmg option, but it is disabled - you can only specify /vmg on + // the command line. In VC1.5 and earlier, the compiler would ICE if it ever + // encountered this situation. + // It is OK to use the /vmg option if /vmm or /vms is specified. + + // Fortunately, the wrong function is only called in very obscure cases. + // It only occurs when a derived class overrides a virtual function declared + // in a virtual base class, and the member function + // points to the *Derived* version of that function. The problem can be + // completely averted in 100% of cases by using the *Base class* for the + // member fpointer. Ie, if you use the base class as an interface, you'll + // stay out of trouble. + // Occasionally, you might want to point directly to a derived class function + // that isn't an override of a base class. In this case, both vtable_index + // and 'vtordisp' are zero, but a virtual_inheritance pointer will be generated. + // We can generate correct code in this case. To prevent an incorrect call from + // ever being made, on MSVC6 we generate a warning, and call a function to + // make the program crash instantly. + typedef char ERROR_VC6CompilerBug[-100]; + return 0; + } +}; + + +#else + +// Nasty hack for Microsoft and Intel (IA32 and Itanium) +// unknown_inheritance classes go here +// This is probably the ugliest bit of code I've ever written. Look at the casts! +// There is a compiler bug in MSVC6 which prevents it from using this code. +template <> +struct SimplifyMemFunc +{ + template + inline static GenericClass *Convert(X *pthis, XFuncType function_to_bind, + GenericMemFuncType &bound_func) { + // The member function pointer is 16 bytes long. We can't use a normal cast, but + // we can use a union to do the conversion. + union { + XFuncType func; + // In VC++ and ICL, an unknown_inheritance member pointer + // is internally defined as: + struct { + GenericMemFuncType m_funcaddress; // points to the actual member function + int delta; // #bytes to be added to the 'this' pointer + int vtordisp; // #bytes to add to 'this' to find the vtable + int vtable_index; // or 0 if no virtual inheritance + } s; + } u; + // Check that the horrible_cast will work + typedef int ERROR_CantUsehorrible_cast[sizeof(XFuncType)==sizeof(u.s)? 1 : -1]; + u.func = function_to_bind; + bound_func = u.s.funcaddress; + int virtual_delta = 0; + if (u.s.vtable_index) { // Virtual inheritance is used + // First, get to the vtable. + // It is 'vtordisp' bytes from the start of the class. + const int * vtable = *reinterpret_cast( + reinterpret_cast(pthis) + u.s.vtordisp ); + + // 'vtable_index' tells us where in the table we should be looking. + virtual_delta = u.s.vtordisp + *reinterpret_cast( + reinterpret_cast(vtable) + u.s.vtable_index); + } + // The int at 'virtual_delta' gives us the amount to add to 'this'. + // Finally we can add the three components together. Phew! + return reinterpret_cast( + reinterpret_cast(pthis) + u.s.delta + virtual_delta); + }; +}; +#endif // MSVC 7 and greater + +#endif // MS/Intel hacks + +} // namespace detail + +//////////////////////////////////////////////////////////////////////////////// +// Fast Delegates, part 2: +// +// Define the delegate storage, and cope with static functions +// +//////////////////////////////////////////////////////////////////////////////// + +// DelegateMemento -- an opaque structure which can hold an arbitary delegate. +// It knows nothing about the calling convention or number of arguments used by +// the function pointed to. +// It supplies comparison operators so that it can be stored in STL collections. +// It cannot be set to anything other than null, nor invoked directly: +// it must be converted to a specific delegate. + +// Implementation: +// There are two possible implementations: the Safe method and the Evil method. +// DelegateMemento - Safe version +// +// This implementation is standard-compliant, but a bit tricky. +// A static function pointer is stored inside the class. +// Here are the valid values: +// +-- Static pointer --+--pThis --+-- pMemFunc-+-- Meaning------+ +// | 0 | 0 | 0 | Empty | +// | !=0 |(dontcare)| Invoker | Static function| +// | 0 | !=0 | !=0* | Method call | +// +--------------------+----------+------------+----------------+ +// * For Metrowerks, this can be 0. (first virtual function in a +// single_inheritance class). +// When stored stored inside a specific delegate, the 'dontcare' entries are replaced +// with a reference to the delegate itself. This complicates the = and == operators +// for the delegate class. + +// DelegateMemento - Evil version +// +// For compilers where data pointers are at least as big as code pointers, it is +// possible to store the function pointer in the this pointer, using another +// horrible_cast. In this case the DelegateMemento implementation is simple: +// +--pThis --+-- pMemFunc-+-- Meaning---------------------+ +// | 0 | 0 | Empty | +// | !=0 | !=0* | Static function or method call| +// +----------+------------+-------------------------------+ +// * For Metrowerks, this can be 0. (first virtual function in a +// single_inheritance class). +// Note that the Sun C++ and MSVC documentation explicitly state that they +// support static_cast between void * and function pointers. + +class DelegateMemento { +protected: + // the data is protected, not private, because many + // compilers have problems with template friends. + typedef void (detail::GenericClass::*GenericMemFuncType)(); // arbitrary MFP. + detail::GenericClass *m_pthis; + GenericMemFuncType m_pFunction; + +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + typedef void (*GenericFuncPtr)(); // arbitrary code pointer + GenericFuncPtr m_pStaticFunction; +#endif + +public: +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + DelegateMemento() : m_pthis(0), m_pFunction(0), m_pStaticFunction(0) {}; + void clear() { + m_pthis=0; m_pFunction=0; m_pStaticFunction=0; + } +#else + DelegateMemento() : m_pthis(0), m_pFunction(0) {}; + void clear() { m_pthis=0; m_pFunction=0; } +#endif +public: +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + inline bool IsEqual (const DelegateMemento &x) const{ + // We have to cope with the static function pointers as a special case + if (m_pFunction!=x.m_pFunction) return false; + // the static function ptrs must either both be equal, or both be 0. + if (m_pStaticFunction!=x.m_pStaticFunction) return false; + if (m_pStaticFunction!=0) return m_pthis==x.m_pthis; + else return true; + } +#else // Evil Method + inline bool IsEqual (const DelegateMemento &x) const{ + return m_pthis==x.m_pthis && m_pFunction==x.m_pFunction; + } +#endif + // Provide a strict weak ordering for DelegateMementos. + inline bool IsLess(const DelegateMemento &right) const { + // deal with static function pointers first +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + if (m_pStaticFunction !=0 || right.m_pStaticFunction!=0) + return m_pStaticFunction < right.m_pStaticFunction; +#endif + if (m_pthis !=right.m_pthis) return m_pthis < right.m_pthis; + // There are no ordering operators for member function pointers, + // but we can fake one by comparing each byte. The resulting ordering is + // arbitrary (and compiler-dependent), but it permits storage in ordered STL containers. + return dMemcmp(&m_pFunction, &right.m_pFunction, sizeof(m_pFunction)) < 0; + + } + // BUGFIX (Mar 2005): + // We can't just compare m_pFunction because on Metrowerks, + // m_pFunction can be zero even if the delegate is not empty! + inline bool operator ! () const // Is it bound to anything? + { return m_pthis==0 && m_pFunction==0; } + inline bool empty() const // Is it bound to anything? + { return m_pthis==0 && m_pFunction==0; } +public: + DelegateMemento & operator = (const DelegateMemento &right) { + SetMementoFrom(right); + return *this; + } + inline bool operator <(const DelegateMemento &right) { + return IsLess(right); + } + inline bool operator >(const DelegateMemento &right) { + return right.IsLess(*this); + } + DelegateMemento (const DelegateMemento &right) : + m_pthis(right.m_pthis), m_pFunction(right.m_pFunction) +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + , m_pStaticFunction (right.m_pStaticFunction) +#endif + {} +protected: + void SetMementoFrom(const DelegateMemento &right) { + m_pFunction = right.m_pFunction; + m_pthis = right.m_pthis; +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + m_pStaticFunction = right.m_pStaticFunction; +#endif + } +}; + + +// ClosurePtr<> +// +// A private wrapper class that adds function signatures to DelegateMemento. +// It's the class that does most of the actual work. +// The signatures are specified by: +// GenericMemFunc: must be a type of GenericClass member function pointer. +// StaticFuncPtr: must be a type of function pointer with the same signature +// as GenericMemFunc. +// UnvoidStaticFuncPtr: is the same as StaticFuncPtr, except on VC6 +// where it never returns void (returns DefaultVoid instead). + +// An outer class, FastDelegateN<>, handles the invoking and creates the +// necessary typedefs. +// This class does everything else. + +namespace detail { + +template < class GenericMemFunc, class StaticFuncPtr, class UnvoidStaticFuncPtr> +class ClosurePtr : public DelegateMemento { +public: + // These functions are for setting the delegate to a member function. + + // Here's the clever bit: we convert an arbitrary member function into a + // standard form. XMemFunc should be a member function of class X, but I can't + // enforce that here. It needs to be enforced by the wrapper class. + template < class X, class XMemFunc > + inline void bindmemfunc(X *pthis, XMemFunc function_to_bind ) { + m_pthis = SimplifyMemFunc< sizeof(function_to_bind) > + ::Convert(pthis, function_to_bind, m_pFunction); +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + m_pStaticFunction = 0; +#endif + } + // For const member functions, we only need a const class pointer. + // Since we know that the member function is const, it's safe to + // remove the const qualifier from the 'this' pointer with a const_cast. + // VC6 has problems if we just overload 'bindmemfunc', so we give it a different name. + template < class X, class XMemFunc> + inline void bindconstmemfunc(const X *pthis, XMemFunc function_to_bind) { + m_pthis= SimplifyMemFunc< sizeof(function_to_bind) > + ::Convert(const_cast(pthis), function_to_bind, m_pFunction); +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + m_pStaticFunction = 0; +#endif + } +#ifdef FASTDELEGATE_GCC_BUG_8271 // At present, GCC doesn't recognize constness of MFPs in templates + template < class X, class XMemFunc> + inline void bindmemfunc(const X *pthis, XMemFunc function_to_bind) { + bindconstmemfunc(pthis, function_to_bind); +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + m_pStaticFunction = 0; +#endif + } +#endif + // These functions are required for invoking the stored function + inline GenericClass *GetClosureThis() const { return m_pthis; } + inline GenericMemFunc GetClosureMemPtr() const { return reinterpret_cast(m_pFunction); } + +// There are a few ways of dealing with static function pointers. +// There's a standard-compliant, but tricky method. +// There's also a straightforward hack, that won't work on DOS compilers using the +// medium memory model. It's so evil that I can't recommend it, but I've +// implemented it anyway because it produces very nice asm code. + +#if !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + +// ClosurePtr<> - Safe version +// +// This implementation is standard-compliant, but a bit tricky. +// I store the function pointer inside the class, and the delegate then +// points to itself. Whenever the delegate is copied, these self-references +// must be transformed, and this complicates the = and == operators. +public: + // The next two functions are for operator ==, =, and the copy constructor. + // We may need to convert the m_pthis pointers, so that + // they remain as self-references. + template< class DerivedClass > + inline void CopyFrom (DerivedClass *pParent, const DelegateMemento &x) { + SetMementoFrom(x); + if (m_pStaticFunction!=0) { + // transform self references... + m_pthis=reinterpret_cast(pParent); + } + } + // For static functions, the 'static_function_invoker' class in the parent + // will be called. The parent then needs to call GetStaticFunction() to find out + // the actual function to invoke. + template < class DerivedClass, class ParentInvokerSig > + inline void bindstaticfunc(DerivedClass *pParent, ParentInvokerSig static_function_invoker, + StaticFuncPtr function_to_bind ) { + if (function_to_bind==0) { // cope with assignment to 0 + m_pFunction=0; + } else { + bindmemfunc(pParent, static_function_invoker); + } + m_pStaticFunction=reinterpret_cast(function_to_bind); + } + inline UnvoidStaticFuncPtr GetStaticFunction() const { + return reinterpret_cast(m_pStaticFunction); + } +#else + +// ClosurePtr<> - Evil version +// +// For compilers where data pointers are at least as big as code pointers, it is +// possible to store the function pointer in the this pointer, using another +// horrible_cast. Invocation isn't any faster, but it saves 4 bytes, and +// speeds up comparison and assignment. If C++ provided direct language support +// for delegates, they would produce asm code that was almost identical to this. +// Note that the Sun C++ and MSVC documentation explicitly state that they +// support static_cast between void * and function pointers. + + template< class DerivedClass > + inline void CopyFrom (DerivedClass *pParent, const DelegateMemento &right) { + SetMementoFrom(right); + } + // For static functions, the 'static_function_invoker' class in the parent + // will be called. The parent then needs to call GetStaticFunction() to find out + // the actual function to invoke. + // ******** EVIL, EVIL CODE! ******* + template < class DerivedClass, class ParentInvokerSig> + inline void bindstaticfunc(DerivedClass *pParent, ParentInvokerSig static_function_invoker, + StaticFuncPtr function_to_bind) { + if (function_to_bind==0) { // cope with assignment to 0 + m_pFunction=0; + } else { + // We'll be ignoring the 'this' pointer, but we need to make sure we pass + // a valid value to bindmemfunc(). + bindmemfunc(pParent, static_function_invoker); + } + + // WARNING! Evil hack. We store the function in the 'this' pointer! + // Ensure that there's a compilation failure if function pointers + // and data pointers have different sizes. + // If you get this error, you need to #undef FASTDELEGATE_USESTATICFUNCTIONHACK. + typedef int ERROR_CantUseEvilMethod[sizeof(GenericClass *)==sizeof(function_to_bind) ? 1 : -1]; + m_pthis = horrible_cast(function_to_bind); + // MSVC, SunC++ and DMC accept the following (non-standard) code: +// m_pthis = static_cast(static_cast(function_to_bind)); + // BCC32, Comeau and DMC accept this method. MSVC7.1 needs __int64 instead of long +// m_pthis = reinterpret_cast(reinterpret_cast(function_to_bind)); + } + // ******** EVIL, EVIL CODE! ******* + // This function will be called with an invalid 'this' pointer!! + // We're just returning the 'this' pointer, converted into + // a function pointer! + inline UnvoidStaticFuncPtr GetStaticFunction() const { + // Ensure that there's a compilation failure if function pointers + // and data pointers have different sizes. + // If you get this error, you need to #undef FASTDELEGATE_USESTATICFUNCTIONHACK. + typedef int ERROR_CantUseEvilMethod[sizeof(UnvoidStaticFuncPtr)==sizeof(this) ? 1 : -1]; + return horrible_cast(this); + } +#endif // !defined(FASTDELEGATE_USESTATICFUNCTIONHACK) + + // Does the closure contain this static function? + inline bool IsEqualToStaticFuncPtr(StaticFuncPtr funcptr){ + if (funcptr==0) return empty(); + // For the Evil method, if it doesn't actually contain a static function, this will return an arbitrary + // value that is not equal to any valid function pointer. + else return funcptr==reinterpret_cast(GetStaticFunction()); + } +}; + + +} // namespace detail + +//////////////////////////////////////////////////////////////////////////////// +// Fast Delegates, part 3: +// +// Wrapper classes to ensure type safety +// +//////////////////////////////////////////////////////////////////////////////// + + +// Once we have the member function conversion templates, it's easy to make the +// wrapper classes. So that they will work with as many compilers as possible, +// the classes are of the form +// FastDelegate3 +// They can cope with any combination of parameters. The max number of parameters +// allowed is 8, but it is trivial to increase this limit. +// Note that we need to treat const member functions seperately. +// All this class does is to enforce type safety, and invoke the delegate with +// the correct list of parameters. + +// Because of the weird rule about the class of derived member function pointers, +// you sometimes need to apply a downcast to the 'this' pointer. +// This is the reason for the use of "implicit_cast(pthis)" in the code below. +// If CDerivedClass is derived from CBaseClass, but doesn't override SimpleVirtualFunction, +// without this trick you'd need to write: +// MyDelegate(static_cast(&d), &CDerivedClass::SimpleVirtualFunction); +// but with the trick you can write +// MyDelegate(&d, &CDerivedClass::SimpleVirtualFunction); + +// RetType is the type the compiler uses in compiling the template. For VC6, +// it cannot be void. DesiredRetType is the real type which is returned from +// all of the functions. It can be void. + +// Implicit conversion to "bool" is achieved using the safe_bool idiom, +// using member data pointers (MDP). This allows "if (dg)..." syntax +// Because some compilers (eg codeplay) don't have a unique value for a zero +// MDP, an extra padding member is added to the SafeBool struct. +// Some compilers (eg VC6) won't implicitly convert from 0 to an MDP, so +// in that case the static function constructor is not made explicit; this +// allows "if (dg==0) ..." to compile. + +//N=0 +template +class FastDelegate0 { +private: + typedef typename detail::DefaultVoidToVoid::type DesiredRetType; + typedef DesiredRetType (*StaticFunctionPtr)(); + typedef RetType (*UnvoidStaticFunctionPtr)(); + typedef RetType (detail::GenericClass::*GenericMemFn)(); + typedef detail::ClosurePtr ClosureType; + ClosureType m_Closure; +public: + // Typedefs to aid generic programming + typedef FastDelegate0 type; + + // Construction and comparison functions + FastDelegate0() { clear(); } + FastDelegate0(const FastDelegate0 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + void operator = (const FastDelegate0 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + bool operator ==(const FastDelegate0 &x) const { + return m_Closure.IsEqual(x.m_Closure); } + bool operator !=(const FastDelegate0 &x) const { + return !m_Closure.IsEqual(x.m_Closure); } + bool operator <(const FastDelegate0 &x) const { + return m_Closure.IsLess(x.m_Closure); } + bool operator >(const FastDelegate0 &x) const { + return x.m_Closure.IsLess(m_Closure); } + // Binding to non-const member functions + template < class X, class Y > + FastDelegate0(Y *pthis, DesiredRetType (X::* function_to_bind)() ) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(Y *pthis, DesiredRetType (X::* function_to_bind)()) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Binding to const member functions. + template < class X, class Y > + FastDelegate0(const Y *pthis, DesiredRetType (X::* function_to_bind)() const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(const Y *pthis, DesiredRetType (X::* function_to_bind)() const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Static functions. We convert them into a member function call. + // This constructor also provides implicit conversion + FastDelegate0(DesiredRetType (*function_to_bind)() ) { + bind(function_to_bind); } + // for efficiency, prevent creation of a temporary + void operator = (DesiredRetType (*function_to_bind)() ) { + bind(function_to_bind); } + inline void bind(DesiredRetType (*function_to_bind)()) { + m_Closure.bindstaticfunc(this, &FastDelegate0::InvokeStaticFunction, + function_to_bind); } + // Invoke the delegate + RetType operator() () const { + return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(); } + // Implicit conversion to "bool" using the safe_bool idiom +private: + typedef struct SafeBoolStruct { + int a_data_pointer_to_this_is_0_on_buggy_compilers; + StaticFunctionPtr m_nonzero; + } UselessTypedef; + typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type; +public: + operator unspecified_bool_type() const { + return empty()? 0: &SafeBoolStruct::m_nonzero; + } + // necessary to allow ==0 to work despite the safe_bool idiom + inline bool operator==(StaticFunctionPtr funcptr) { + return m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator!=(StaticFunctionPtr funcptr) { + return !m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator ! () const { // Is it bound to anything? + return !m_Closure; } + inline bool empty() const { + return !m_Closure; } + void clear() { m_Closure.clear();} + // Conversion to and from the DelegateMemento storage class + const DelegateMemento & GetMemento() { return m_Closure; } + void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); } + +private: // Invoker for static functions + RetType InvokeStaticFunction() const { + return (*(m_Closure.GetStaticFunction()))(); } +}; + +//N=1 +template +class FastDelegate1 { +private: + typedef typename detail::DefaultVoidToVoid::type DesiredRetType; + typedef DesiredRetType (*StaticFunctionPtr)(Param1 p1); + typedef RetType (*UnvoidStaticFunctionPtr)(Param1 p1); + typedef RetType (detail::GenericClass::*GenericMemFn)(Param1 p1); + typedef detail::ClosurePtr ClosureType; + ClosureType m_Closure; +public: + // Typedefs to aid generic programming + typedef FastDelegate1 type; + + // Construction and comparison functions + FastDelegate1() { clear(); } + FastDelegate1(const FastDelegate1 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + void operator = (const FastDelegate1 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + bool operator ==(const FastDelegate1 &x) const { + return m_Closure.IsEqual(x.m_Closure); } + bool operator !=(const FastDelegate1 &x) const { + return !m_Closure.IsEqual(x.m_Closure); } + bool operator <(const FastDelegate1 &x) const { + return m_Closure.IsLess(x.m_Closure); } + bool operator >(const FastDelegate1 &x) const { + return x.m_Closure.IsLess(m_Closure); } + // Binding to non-const member functions + template < class X, class Y > + FastDelegate1(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1) ) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1)) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Binding to const member functions. + template < class X, class Y > + FastDelegate1(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Static functions. We convert them into a member function call. + // This constructor also provides implicit conversion + FastDelegate1(DesiredRetType (*function_to_bind)(Param1 p1) ) { + bind(function_to_bind); } + // for efficiency, prevent creation of a temporary + void operator = (DesiredRetType (*function_to_bind)(Param1 p1) ) { + bind(function_to_bind); } + inline void bind(DesiredRetType (*function_to_bind)(Param1 p1)) { + m_Closure.bindstaticfunc(this, &FastDelegate1::InvokeStaticFunction, + function_to_bind); } + // Invoke the delegate + RetType operator() (Param1 p1) const { + return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(p1); } + // Implicit conversion to "bool" using the safe_bool idiom +private: + typedef struct SafeBoolStruct { + int a_data_pointer_to_this_is_0_on_buggy_compilers; + StaticFunctionPtr m_nonzero; + } UselessTypedef; + typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type; +public: + operator unspecified_bool_type() const { + return empty()? 0: &SafeBoolStruct::m_nonzero; + } + // necessary to allow ==0 to work despite the safe_bool idiom + inline bool operator==(StaticFunctionPtr funcptr) { + return m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator!=(StaticFunctionPtr funcptr) { + return !m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator ! () const { // Is it bound to anything? + return !m_Closure; } + inline bool empty() const { + return !m_Closure; } + void clear() { m_Closure.clear();} + // Conversion to and from the DelegateMemento storage class + const DelegateMemento & GetMemento() { return m_Closure; } + void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); } + +private: // Invoker for static functions + RetType InvokeStaticFunction(Param1 p1) const { + return (*(m_Closure.GetStaticFunction()))(p1); } +}; + +//N=2 +template +class FastDelegate2 { +private: + typedef typename detail::DefaultVoidToVoid::type DesiredRetType; + typedef DesiredRetType (*StaticFunctionPtr)(Param1 p1, Param2 p2); + typedef RetType (*UnvoidStaticFunctionPtr)(Param1 p1, Param2 p2); + typedef RetType (detail::GenericClass::*GenericMemFn)(Param1 p1, Param2 p2); + typedef detail::ClosurePtr ClosureType; + ClosureType m_Closure; +public: + // Typedefs to aid generic programming + typedef FastDelegate2 type; + + // Construction and comparison functions + FastDelegate2() { clear(); } + FastDelegate2(const FastDelegate2 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + void operator = (const FastDelegate2 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + bool operator ==(const FastDelegate2 &x) const { + return m_Closure.IsEqual(x.m_Closure); } + bool operator !=(const FastDelegate2 &x) const { + return !m_Closure.IsEqual(x.m_Closure); } + bool operator <(const FastDelegate2 &x) const { + return m_Closure.IsLess(x.m_Closure); } + bool operator >(const FastDelegate2 &x) const { + return x.m_Closure.IsLess(m_Closure); } + // Binding to non-const member functions + template < class X, class Y > + FastDelegate2(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2) ) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2)) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Binding to const member functions. + template < class X, class Y > + FastDelegate2(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Static functions. We convert them into a member function call. + // This constructor also provides implicit conversion + FastDelegate2(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2) ) { + bind(function_to_bind); } + // for efficiency, prevent creation of a temporary + void operator = (DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2) ) { + bind(function_to_bind); } + inline void bind(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2)) { + m_Closure.bindstaticfunc(this, &FastDelegate2::InvokeStaticFunction, + function_to_bind); } + // Invoke the delegate + RetType operator() (Param1 p1, Param2 p2) const { + return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(p1, p2); } + // Implicit conversion to "bool" using the safe_bool idiom +private: + typedef struct SafeBoolStruct { + int a_data_pointer_to_this_is_0_on_buggy_compilers; + StaticFunctionPtr m_nonzero; + } UselessTypedef; + typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type; +public: + operator unspecified_bool_type() const { + return empty()? 0: &SafeBoolStruct::m_nonzero; + } + // necessary to allow ==0 to work despite the safe_bool idiom + inline bool operator==(StaticFunctionPtr funcptr) { + return m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator!=(StaticFunctionPtr funcptr) { + return !m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator ! () const { // Is it bound to anything? + return !m_Closure; } + inline bool empty() const { + return !m_Closure; } + void clear() { m_Closure.clear();} + // Conversion to and from the DelegateMemento storage class + const DelegateMemento & GetMemento() { return m_Closure; } + void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); } + +private: // Invoker for static functions + RetType InvokeStaticFunction(Param1 p1, Param2 p2) const { + return (*(m_Closure.GetStaticFunction()))(p1, p2); } +}; + +//N=3 +template +class FastDelegate3 { +private: + typedef typename detail::DefaultVoidToVoid::type DesiredRetType; + typedef DesiredRetType (*StaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3); + typedef RetType (*UnvoidStaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3); + typedef RetType (detail::GenericClass::*GenericMemFn)(Param1 p1, Param2 p2, Param3 p3); + typedef detail::ClosurePtr ClosureType; + ClosureType m_Closure; +public: + // Typedefs to aid generic programming + typedef FastDelegate3 type; + + // Construction and comparison functions + FastDelegate3() { clear(); } + FastDelegate3(const FastDelegate3 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + void operator = (const FastDelegate3 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + bool operator ==(const FastDelegate3 &x) const { + return m_Closure.IsEqual(x.m_Closure); } + bool operator !=(const FastDelegate3 &x) const { + return !m_Closure.IsEqual(x.m_Closure); } + bool operator <(const FastDelegate3 &x) const { + return m_Closure.IsLess(x.m_Closure); } + bool operator >(const FastDelegate3 &x) const { + return x.m_Closure.IsLess(m_Closure); } + // Binding to non-const member functions + template < class X, class Y > + FastDelegate3(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3) ) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3)) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Binding to const member functions. + template < class X, class Y > + FastDelegate3(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Static functions. We convert them into a member function call. + // This constructor also provides implicit conversion + FastDelegate3(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3) ) { + bind(function_to_bind); } + // for efficiency, prevent creation of a temporary + void operator = (DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3) ) { + bind(function_to_bind); } + inline void bind(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3)) { + m_Closure.bindstaticfunc(this, &FastDelegate3::InvokeStaticFunction, + function_to_bind); } + // Invoke the delegate + RetType operator() (Param1 p1, Param2 p2, Param3 p3) const { + return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(p1, p2, p3); } + // Implicit conversion to "bool" using the safe_bool idiom +private: + typedef struct SafeBoolStruct { + int a_data_pointer_to_this_is_0_on_buggy_compilers; + StaticFunctionPtr m_nonzero; + } UselessTypedef; + typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type; +public: + operator unspecified_bool_type() const { + return empty()? 0: &SafeBoolStruct::m_nonzero; + } + // necessary to allow ==0 to work despite the safe_bool idiom + inline bool operator==(StaticFunctionPtr funcptr) { + return m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator!=(StaticFunctionPtr funcptr) { + return !m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator ! () const { // Is it bound to anything? + return !m_Closure; } + inline bool empty() const { + return !m_Closure; } + void clear() { m_Closure.clear();} + // Conversion to and from the DelegateMemento storage class + const DelegateMemento & GetMemento() { return m_Closure; } + void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); } + +private: // Invoker for static functions + RetType InvokeStaticFunction(Param1 p1, Param2 p2, Param3 p3) const { + return (*(m_Closure.GetStaticFunction()))(p1, p2, p3); } +}; + +//N=4 +template +class FastDelegate4 { +private: + typedef typename detail::DefaultVoidToVoid::type DesiredRetType; + typedef DesiredRetType (*StaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4); + typedef RetType (*UnvoidStaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4); + typedef RetType (detail::GenericClass::*GenericMemFn)(Param1 p1, Param2 p2, Param3 p3, Param4 p4); + typedef detail::ClosurePtr ClosureType; + ClosureType m_Closure; +public: + // Typedefs to aid generic programming + typedef FastDelegate4 type; + + // Construction and comparison functions + FastDelegate4() { clear(); } + FastDelegate4(const FastDelegate4 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + void operator = (const FastDelegate4 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + bool operator ==(const FastDelegate4 &x) const { + return m_Closure.IsEqual(x.m_Closure); } + bool operator !=(const FastDelegate4 &x) const { + return !m_Closure.IsEqual(x.m_Closure); } + bool operator <(const FastDelegate4 &x) const { + return m_Closure.IsLess(x.m_Closure); } + bool operator >(const FastDelegate4 &x) const { + return x.m_Closure.IsLess(m_Closure); } + // Binding to non-const member functions + template < class X, class Y > + FastDelegate4(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4) ) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4)) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Binding to const member functions. + template < class X, class Y > + FastDelegate4(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Static functions. We convert them into a member function call. + // This constructor also provides implicit conversion + FastDelegate4(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4) ) { + bind(function_to_bind); } + // for efficiency, prevent creation of a temporary + void operator = (DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4) ) { + bind(function_to_bind); } + inline void bind(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4)) { + m_Closure.bindstaticfunc(this, &FastDelegate4::InvokeStaticFunction, + function_to_bind); } + // Invoke the delegate + RetType operator() (Param1 p1, Param2 p2, Param3 p3, Param4 p4) const { + return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(p1, p2, p3, p4); } + // Implicit conversion to "bool" using the safe_bool idiom +private: + typedef struct SafeBoolStruct { + int a_data_pointer_to_this_is_0_on_buggy_compilers; + StaticFunctionPtr m_nonzero; + } UselessTypedef; + typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type; +public: + operator unspecified_bool_type() const { + return empty()? 0: &SafeBoolStruct::m_nonzero; + } + // necessary to allow ==0 to work despite the safe_bool idiom + inline bool operator==(StaticFunctionPtr funcptr) { + return m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator!=(StaticFunctionPtr funcptr) { + return !m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator ! () const { // Is it bound to anything? + return !m_Closure; } + inline bool empty() const { + return !m_Closure; } + void clear() { m_Closure.clear();} + // Conversion to and from the DelegateMemento storage class + const DelegateMemento & GetMemento() { return m_Closure; } + void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); } + +private: // Invoker for static functions + RetType InvokeStaticFunction(Param1 p1, Param2 p2, Param3 p3, Param4 p4) const { + return (*(m_Closure.GetStaticFunction()))(p1, p2, p3, p4); } +}; + +//N=5 +template +class FastDelegate5 { +private: + typedef typename detail::DefaultVoidToVoid::type DesiredRetType; + typedef DesiredRetType (*StaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5); + typedef RetType (*UnvoidStaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5); + typedef RetType (detail::GenericClass::*GenericMemFn)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5); + typedef detail::ClosurePtr ClosureType; + ClosureType m_Closure; +public: + // Typedefs to aid generic programming + typedef FastDelegate5 type; + + // Construction and comparison functions + FastDelegate5() { clear(); } + FastDelegate5(const FastDelegate5 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + void operator = (const FastDelegate5 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + bool operator ==(const FastDelegate5 &x) const { + return m_Closure.IsEqual(x.m_Closure); } + bool operator !=(const FastDelegate5 &x) const { + return !m_Closure.IsEqual(x.m_Closure); } + bool operator <(const FastDelegate5 &x) const { + return m_Closure.IsLess(x.m_Closure); } + bool operator >(const FastDelegate5 &x) const { + return x.m_Closure.IsLess(m_Closure); } + // Binding to non-const member functions + template < class X, class Y > + FastDelegate5(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5) ) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5)) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Binding to const member functions. + template < class X, class Y > + FastDelegate5(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Static functions. We convert them into a member function call. + // This constructor also provides implicit conversion + FastDelegate5(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5) ) { + bind(function_to_bind); } + // for efficiency, prevent creation of a temporary + void operator = (DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5) ) { + bind(function_to_bind); } + inline void bind(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5)) { + m_Closure.bindstaticfunc(this, &FastDelegate5::InvokeStaticFunction, + function_to_bind); } + // Invoke the delegate + RetType operator() (Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5) const { + return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(p1, p2, p3, p4, p5); } + // Implicit conversion to "bool" using the safe_bool idiom +private: + typedef struct SafeBoolStruct { + int a_data_pointer_to_this_is_0_on_buggy_compilers; + StaticFunctionPtr m_nonzero; + } UselessTypedef; + typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type; +public: + operator unspecified_bool_type() const { + return empty()? 0: &SafeBoolStruct::m_nonzero; + } + // necessary to allow ==0 to work despite the safe_bool idiom + inline bool operator==(StaticFunctionPtr funcptr) { + return m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator!=(StaticFunctionPtr funcptr) { + return !m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator ! () const { // Is it bound to anything? + return !m_Closure; } + inline bool empty() const { + return !m_Closure; } + void clear() { m_Closure.clear();} + // Conversion to and from the DelegateMemento storage class + const DelegateMemento & GetMemento() { return m_Closure; } + void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); } + +private: // Invoker for static functions + RetType InvokeStaticFunction(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5) const { + return (*(m_Closure.GetStaticFunction()))(p1, p2, p3, p4, p5); } +}; + +//N=6 +template +class FastDelegate6 { +private: + typedef typename detail::DefaultVoidToVoid::type DesiredRetType; + typedef DesiredRetType (*StaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6); + typedef RetType (*UnvoidStaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6); + typedef RetType (detail::GenericClass::*GenericMemFn)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6); + typedef detail::ClosurePtr ClosureType; + ClosureType m_Closure; +public: + // Typedefs to aid generic programming + typedef FastDelegate6 type; + + // Construction and comparison functions + FastDelegate6() { clear(); } + FastDelegate6(const FastDelegate6 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + void operator = (const FastDelegate6 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + bool operator ==(const FastDelegate6 &x) const { + return m_Closure.IsEqual(x.m_Closure); } + bool operator !=(const FastDelegate6 &x) const { + return !m_Closure.IsEqual(x.m_Closure); } + bool operator <(const FastDelegate6 &x) const { + return m_Closure.IsLess(x.m_Closure); } + bool operator >(const FastDelegate6 &x) const { + return x.m_Closure.IsLess(m_Closure); } + // Binding to non-const member functions + template < class X, class Y > + FastDelegate6(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6) ) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6)) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Binding to const member functions. + template < class X, class Y > + FastDelegate6(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Static functions. We convert them into a member function call. + // This constructor also provides implicit conversion + FastDelegate6(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6) ) { + bind(function_to_bind); } + // for efficiency, prevent creation of a temporary + void operator = (DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6) ) { + bind(function_to_bind); } + inline void bind(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6)) { + m_Closure.bindstaticfunc(this, &FastDelegate6::InvokeStaticFunction, + function_to_bind); } + // Invoke the delegate + RetType operator() (Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6) const { + return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(p1, p2, p3, p4, p5, p6); } + // Implicit conversion to "bool" using the safe_bool idiom +private: + typedef struct SafeBoolStruct { + int a_data_pointer_to_this_is_0_on_buggy_compilers; + StaticFunctionPtr m_nonzero; + } UselessTypedef; + typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type; +public: + operator unspecified_bool_type() const { + return empty()? 0: &SafeBoolStruct::m_nonzero; + } + // necessary to allow ==0 to work despite the safe_bool idiom + inline bool operator==(StaticFunctionPtr funcptr) { + return m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator!=(StaticFunctionPtr funcptr) { + return !m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator ! () const { // Is it bound to anything? + return !m_Closure; } + inline bool empty() const { + return !m_Closure; } + void clear() { m_Closure.clear();} + // Conversion to and from the DelegateMemento storage class + const DelegateMemento & GetMemento() { return m_Closure; } + void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); } + +private: // Invoker for static functions + RetType InvokeStaticFunction(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6) const { + return (*(m_Closure.GetStaticFunction()))(p1, p2, p3, p4, p5, p6); } +}; + +//N=7 +template +class FastDelegate7 { +private: + typedef typename detail::DefaultVoidToVoid::type DesiredRetType; + typedef DesiredRetType (*StaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7); + typedef RetType (*UnvoidStaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7); + typedef RetType (detail::GenericClass::*GenericMemFn)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7); + typedef detail::ClosurePtr ClosureType; + ClosureType m_Closure; +public: + // Typedefs to aid generic programming + typedef FastDelegate7 type; + + // Construction and comparison functions + FastDelegate7() { clear(); } + FastDelegate7(const FastDelegate7 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + void operator = (const FastDelegate7 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + bool operator ==(const FastDelegate7 &x) const { + return m_Closure.IsEqual(x.m_Closure); } + bool operator !=(const FastDelegate7 &x) const { + return !m_Closure.IsEqual(x.m_Closure); } + bool operator <(const FastDelegate7 &x) const { + return m_Closure.IsLess(x.m_Closure); } + bool operator >(const FastDelegate7 &x) const { + return x.m_Closure.IsLess(m_Closure); } + // Binding to non-const member functions + template < class X, class Y > + FastDelegate7(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7) ) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7)) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Binding to const member functions. + template < class X, class Y > + FastDelegate7(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Static functions. We convert them into a member function call. + // This constructor also provides implicit conversion + FastDelegate7(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7) ) { + bind(function_to_bind); } + // for efficiency, prevent creation of a temporary + void operator = (DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7) ) { + bind(function_to_bind); } + inline void bind(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7)) { + m_Closure.bindstaticfunc(this, &FastDelegate7::InvokeStaticFunction, + function_to_bind); } + // Invoke the delegate + RetType operator() (Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7) const { + return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(p1, p2, p3, p4, p5, p6, p7); } + // Implicit conversion to "bool" using the safe_bool idiom +private: + typedef struct SafeBoolStruct { + int a_data_pointer_to_this_is_0_on_buggy_compilers; + StaticFunctionPtr m_nonzero; + } UselessTypedef; + typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type; +public: + operator unspecified_bool_type() const { + return empty()? 0: &SafeBoolStruct::m_nonzero; + } + // necessary to allow ==0 to work despite the safe_bool idiom + inline bool operator==(StaticFunctionPtr funcptr) { + return m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator!=(StaticFunctionPtr funcptr) { + return !m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator ! () const { // Is it bound to anything? + return !m_Closure; } + inline bool empty() const { + return !m_Closure; } + void clear() { m_Closure.clear();} + // Conversion to and from the DelegateMemento storage class + const DelegateMemento & GetMemento() { return m_Closure; } + void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); } + +private: // Invoker for static functions + RetType InvokeStaticFunction(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7) const { + return (*(m_Closure.GetStaticFunction()))(p1, p2, p3, p4, p5, p6, p7); } +}; + +//N=8 +template +class FastDelegate8 { +private: + typedef typename detail::DefaultVoidToVoid::type DesiredRetType; + typedef DesiredRetType (*StaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8); + typedef RetType (*UnvoidStaticFunctionPtr)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8); + typedef RetType (detail::GenericClass::*GenericMemFn)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8); + typedef detail::ClosurePtr ClosureType; + ClosureType m_Closure; +public: + // Typedefs to aid generic programming + typedef FastDelegate8 type; + + // Construction and comparison functions + FastDelegate8() { clear(); } + FastDelegate8(const FastDelegate8 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + void operator = (const FastDelegate8 &x) { + m_Closure.CopyFrom(this, x.m_Closure); } + bool operator ==(const FastDelegate8 &x) const { + return m_Closure.IsEqual(x.m_Closure); } + bool operator !=(const FastDelegate8 &x) const { + return !m_Closure.IsEqual(x.m_Closure); } + bool operator <(const FastDelegate8 &x) const { + return m_Closure.IsLess(x.m_Closure); } + bool operator >(const FastDelegate8 &x) const { + return x.m_Closure.IsLess(m_Closure); } + // Binding to non-const member functions + template < class X, class Y > + FastDelegate8(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8) ) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8)) { + m_Closure.bindmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Binding to const member functions. + template < class X, class Y > + FastDelegate8(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + template < class X, class Y > + inline void bind(const Y *pthis, DesiredRetType (X::* function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8) const) { + m_Closure.bindconstmemfunc(detail::implicit_cast(pthis), function_to_bind); } + // Static functions. We convert them into a member function call. + // This constructor also provides implicit conversion + FastDelegate8(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8) ) { + bind(function_to_bind); } + // for efficiency, prevent creation of a temporary + void operator = (DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8) ) { + bind(function_to_bind); } + inline void bind(DesiredRetType (*function_to_bind)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8)) { + m_Closure.bindstaticfunc(this, &FastDelegate8::InvokeStaticFunction, + function_to_bind); } + // Invoke the delegate + RetType operator() (Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8) const { + return (m_Closure.GetClosureThis()->*(m_Closure.GetClosureMemPtr()))(p1, p2, p3, p4, p5, p6, p7, p8); } + // Implicit conversion to "bool" using the safe_bool idiom +private: + typedef struct SafeBoolStruct { + int a_data_pointer_to_this_is_0_on_buggy_compilers; + StaticFunctionPtr m_nonzero; + } UselessTypedef; + typedef StaticFunctionPtr SafeBoolStruct::*unspecified_bool_type; +public: + operator unspecified_bool_type() const { + return empty()? 0: &SafeBoolStruct::m_nonzero; + } + // necessary to allow ==0 to work despite the safe_bool idiom + inline bool operator==(StaticFunctionPtr funcptr) { + return m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator!=(StaticFunctionPtr funcptr) { + return !m_Closure.IsEqualToStaticFuncPtr(funcptr); } + inline bool operator ! () const { // Is it bound to anything? + return !m_Closure; } + inline bool empty() const { + return !m_Closure; } + void clear() { m_Closure.clear();} + // Conversion to and from the DelegateMemento storage class + const DelegateMemento & GetMemento() { return m_Closure; } + void SetMemento(const DelegateMemento &any) { m_Closure.CopyFrom(this, any); } + +private: // Invoker for static functions + RetType InvokeStaticFunction(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8) const { + return (*(m_Closure.GetStaticFunction()))(p1, p2, p3, p4, p5, p6, p7, p8); } +}; + + +//////////////////////////////////////////////////////////////////////////////// +// Fast Delegates, part 4: +// +// FastDelegate<> class (Original author: Jody Hagins) +// Allows boost::function style syntax like: +// FastDelegate< double (int, long) > +// instead of: +// FastDelegate2< int, long, double > +// +//////////////////////////////////////////////////////////////////////////////// + +#ifdef FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX + +// Declare FastDelegate as a class template. It will be specialized +// later for all number of arguments. +template +class FastDelegate; + +//N=0 +// Specialization to allow use of +// FastDelegate< R ( ) > +// instead of +// FastDelegate0 < R > +template +class FastDelegate< R ( ) > + // Inherit from FastDelegate0 so that it can be treated just like a FastDelegate0 + : public FastDelegate0 < R > +{ +public: + // Make using the base type a bit easier via typedef. + typedef FastDelegate0 < R > BaseType; + + // Allow users access to the specific type of this delegate. + typedef FastDelegate SelfType; + + // Mimic the base class constructors. + FastDelegate() : BaseType() { } + + template < class X, class Y > + FastDelegate(Y * pthis, + R (X::* function_to_bind)( )) + : BaseType(pthis, function_to_bind) { } + + template < class X, class Y > + FastDelegate(const Y *pthis, + R (X::* function_to_bind)( ) const) + : BaseType(pthis, function_to_bind) + { } + + FastDelegate(R (*function_to_bind)( )) + : BaseType(function_to_bind) { } + void operator = (const BaseType &x) { + *static_cast(this) = x; } +}; + +//N=1 +// Specialization to allow use of +// FastDelegate< R ( Param1 ) > +// instead of +// FastDelegate1 < Param1, R > +template +class FastDelegate< R ( Param1 ) > + // Inherit from FastDelegate1 so that it can be treated just like a FastDelegate1 + : public FastDelegate1 < Param1, R > +{ +public: + // Make using the base type a bit easier via typedef. + typedef FastDelegate1 < Param1, R > BaseType; + + // Allow users access to the specific type of this delegate. + typedef FastDelegate SelfType; + + // Mimic the base class constructors. + FastDelegate() : BaseType() { } + + template < class X, class Y > + FastDelegate(Y * pthis, + R (X::* function_to_bind)( Param1 p1 )) + : BaseType(pthis, function_to_bind) { } + + template < class X, class Y > + FastDelegate(const Y *pthis, + R (X::* function_to_bind)( Param1 p1 ) const) + : BaseType(pthis, function_to_bind) + { } + + FastDelegate(R (*function_to_bind)( Param1 p1 )) + : BaseType(function_to_bind) { } + void operator = (const BaseType &x) { + *static_cast(this) = x; } +}; + +//N=2 +// Specialization to allow use of +// FastDelegate< R ( Param1, Param2 ) > +// instead of +// FastDelegate2 < Param1, Param2, R > +template +class FastDelegate< R ( Param1, Param2 ) > + // Inherit from FastDelegate2 so that it can be treated just like a FastDelegate2 + : public FastDelegate2 < Param1, Param2, R > +{ +public: + // Make using the base type a bit easier via typedef. + typedef FastDelegate2 < Param1, Param2, R > BaseType; + + // Allow users access to the specific type of this delegate. + typedef FastDelegate SelfType; + + // Mimic the base class constructors. + FastDelegate() : BaseType() { } + + template < class X, class Y > + FastDelegate(Y * pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2 )) + : BaseType(pthis, function_to_bind) { } + + template < class X, class Y > + FastDelegate(const Y *pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2 ) const) + : BaseType(pthis, function_to_bind) + { } + + FastDelegate(R (*function_to_bind)( Param1 p1, Param2 p2 )) + : BaseType(function_to_bind) { } + void operator = (const BaseType &x) { + *static_cast(this) = x; } +}; + +//N=3 +// Specialization to allow use of +// FastDelegate< R ( Param1, Param2, Param3 ) > +// instead of +// FastDelegate3 < Param1, Param2, Param3, R > +template +class FastDelegate< R ( Param1, Param2, Param3 ) > + // Inherit from FastDelegate3 so that it can be treated just like a FastDelegate3 + : public FastDelegate3 < Param1, Param2, Param3, R > +{ +public: + // Make using the base type a bit easier via typedef. + typedef FastDelegate3 < Param1, Param2, Param3, R > BaseType; + + // Allow users access to the specific type of this delegate. + typedef FastDelegate SelfType; + + // Mimic the base class constructors. + FastDelegate() : BaseType() { } + + template < class X, class Y > + FastDelegate(Y * pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3 )) + : BaseType(pthis, function_to_bind) { } + + template < class X, class Y > + FastDelegate(const Y *pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3 ) const) + : BaseType(pthis, function_to_bind) + { } + + FastDelegate(R (*function_to_bind)( Param1 p1, Param2 p2, Param3 p3 )) + : BaseType(function_to_bind) { } + void operator = (const BaseType &x) { + *static_cast(this) = x; } +}; + +//N=4 +// Specialization to allow use of +// FastDelegate< R ( Param1, Param2, Param3, Param4 ) > +// instead of +// FastDelegate4 < Param1, Param2, Param3, Param4, R > +template +class FastDelegate< R ( Param1, Param2, Param3, Param4 ) > + // Inherit from FastDelegate4 so that it can be treated just like a FastDelegate4 + : public FastDelegate4 < Param1, Param2, Param3, Param4, R > +{ +public: + // Make using the base type a bit easier via typedef. + typedef FastDelegate4 < Param1, Param2, Param3, Param4, R > BaseType; + + // Allow users access to the specific type of this delegate. + typedef FastDelegate SelfType; + + // Mimic the base class constructors. + FastDelegate() : BaseType() { } + + template < class X, class Y > + FastDelegate(Y * pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4 )) + : BaseType(pthis, function_to_bind) { } + + template < class X, class Y > + FastDelegate(const Y *pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4 ) const) + : BaseType(pthis, function_to_bind) + { } + + FastDelegate(R (*function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4 )) + : BaseType(function_to_bind) { } + void operator = (const BaseType &x) { + *static_cast(this) = x; } +}; + +//N=5 +// Specialization to allow use of +// FastDelegate< R ( Param1, Param2, Param3, Param4, Param5 ) > +// instead of +// FastDelegate5 < Param1, Param2, Param3, Param4, Param5, R > +template +class FastDelegate< R ( Param1, Param2, Param3, Param4, Param5 ) > + // Inherit from FastDelegate5 so that it can be treated just like a FastDelegate5 + : public FastDelegate5 < Param1, Param2, Param3, Param4, Param5, R > +{ +public: + // Make using the base type a bit easier via typedef. + typedef FastDelegate5 < Param1, Param2, Param3, Param4, Param5, R > BaseType; + + // Allow users access to the specific type of this delegate. + typedef FastDelegate SelfType; + + // Mimic the base class constructors. + FastDelegate() : BaseType() { } + + template < class X, class Y > + FastDelegate(Y * pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5 )) + : BaseType(pthis, function_to_bind) { } + + template < class X, class Y > + FastDelegate(const Y *pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5 ) const) + : BaseType(pthis, function_to_bind) + { } + + FastDelegate(R (*function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5 )) + : BaseType(function_to_bind) { } + void operator = (const BaseType &x) { + *static_cast(this) = x; } +}; + +//N=6 +// Specialization to allow use of +// FastDelegate< R ( Param1, Param2, Param3, Param4, Param5, Param6 ) > +// instead of +// FastDelegate6 < Param1, Param2, Param3, Param4, Param5, Param6, R > +template +class FastDelegate< R ( Param1, Param2, Param3, Param4, Param5, Param6 ) > + // Inherit from FastDelegate6 so that it can be treated just like a FastDelegate6 + : public FastDelegate6 < Param1, Param2, Param3, Param4, Param5, Param6, R > +{ +public: + // Make using the base type a bit easier via typedef. + typedef FastDelegate6 < Param1, Param2, Param3, Param4, Param5, Param6, R > BaseType; + + // Allow users access to the specific type of this delegate. + typedef FastDelegate SelfType; + + // Mimic the base class constructors. + FastDelegate() : BaseType() { } + + template < class X, class Y > + FastDelegate(Y * pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6 )) + : BaseType(pthis, function_to_bind) { } + + template < class X, class Y > + FastDelegate(const Y *pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6 ) const) + : BaseType(pthis, function_to_bind) + { } + + FastDelegate(R (*function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6 )) + : BaseType(function_to_bind) { } + void operator = (const BaseType &x) { + *static_cast(this) = x; } +}; + +//N=7 +// Specialization to allow use of +// FastDelegate< R ( Param1, Param2, Param3, Param4, Param5, Param6, Param7 ) > +// instead of +// FastDelegate7 < Param1, Param2, Param3, Param4, Param5, Param6, Param7, R > +template +class FastDelegate< R ( Param1, Param2, Param3, Param4, Param5, Param6, Param7 ) > + // Inherit from FastDelegate7 so that it can be treated just like a FastDelegate7 + : public FastDelegate7 < Param1, Param2, Param3, Param4, Param5, Param6, Param7, R > +{ +public: + // Make using the base type a bit easier via typedef. + typedef FastDelegate7 < Param1, Param2, Param3, Param4, Param5, Param6, Param7, R > BaseType; + + // Allow users access to the specific type of this delegate. + typedef FastDelegate SelfType; + + // Mimic the base class constructors. + FastDelegate() : BaseType() { } + + template < class X, class Y > + FastDelegate(Y * pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7 )) + : BaseType(pthis, function_to_bind) { } + + template < class X, class Y > + FastDelegate(const Y *pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7 ) const) + : BaseType(pthis, function_to_bind) + { } + + FastDelegate(R (*function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7 )) + : BaseType(function_to_bind) { } + void operator = (const BaseType &x) { + *static_cast(this) = x; } +}; + +//N=8 +// Specialization to allow use of +// FastDelegate< R ( Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8 ) > +// instead of +// FastDelegate8 < Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, R > +template +class FastDelegate< R ( Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8 ) > + // Inherit from FastDelegate8 so that it can be treated just like a FastDelegate8 + : public FastDelegate8 < Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, R > +{ +public: + // Make using the base type a bit easier via typedef. + typedef FastDelegate8 < Param1, Param2, Param3, Param4, Param5, Param6, Param7, Param8, R > BaseType; + + // Allow users access to the specific type of this delegate. + typedef FastDelegate SelfType; + + // Mimic the base class constructors. + FastDelegate() : BaseType() { } + + template < class X, class Y > + FastDelegate(Y * pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8 )) + : BaseType(pthis, function_to_bind) { } + + template < class X, class Y > + FastDelegate(const Y *pthis, + R (X::* function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8 ) const) + : BaseType(pthis, function_to_bind) + { } + + FastDelegate(R (*function_to_bind)( Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8 )) + : BaseType(function_to_bind) { } + void operator = (const BaseType &x) { + *static_cast(this) = x; } +}; + + +#endif //FASTDELEGATE_ALLOW_FUNCTION_TYPE_SYNTAX + +//////////////////////////////////////////////////////////////////////////////// +// Fast Delegates, part 5: +// +// MakeDelegate() helper function +// +// MakeDelegate(&x, &X::func) returns a fastdelegate of the type +// necessary for calling x.func() with the correct number of arguments. +// This makes it possible to eliminate many typedefs from user code. +// +//////////////////////////////////////////////////////////////////////////////// + +// Also declare overloads of a MakeDelegate() global function to +// reduce the need for typedefs. +// We need seperate overloads for const and non-const member functions. +// Also, because of the weird rule about the class of derived member function pointers, +// implicit downcasts may need to be applied later to the 'this' pointer. +// That's why two classes (X and Y) appear in the definitions. Y must be implicitly +// castable to X. + +// Workaround for VC6. VC6 needs void return types converted into DefaultVoid. +// GCC 3.2 and later won't compile this unless it's preceded by 'typename', +// but VC6 doesn't allow 'typename' in this context. +// So, I have to use a macro. + +#ifdef FASTDLGT_VC6 +#define FASTDLGT_RETTYPE detail::VoidToDefaultVoid::type +#else +#define FASTDLGT_RETTYPE RetType +#endif + +//N=0 +template +FastDelegate0 MakeDelegate(Y* x, RetType (X::*func)()) { + return FastDelegate0(x, func); +} + +template +FastDelegate0 MakeDelegate(Y* x, RetType (X::*func)() const) { + return FastDelegate0(x, func); +} + +//N=1 +template +FastDelegate1 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1)) { + return FastDelegate1(x, func); +} + +template +FastDelegate1 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1) const) { + return FastDelegate1(x, func); +} + +//N=2 +template +FastDelegate2 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2)) { + return FastDelegate2(x, func); +} + +template +FastDelegate2 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2) const) { + return FastDelegate2(x, func); +} + +//N=3 +template +FastDelegate3 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3)) { + return FastDelegate3(x, func); +} + +template +FastDelegate3 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3) const) { + return FastDelegate3(x, func); +} + +//N=4 +template +FastDelegate4 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4)) { + return FastDelegate4(x, func); +} + +template +FastDelegate4 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4) const) { + return FastDelegate4(x, func); +} + +//N=5 +template +FastDelegate5 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5)) { + return FastDelegate5(x, func); +} + +template +FastDelegate5 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5) const) { + return FastDelegate5(x, func); +} + +//N=6 +template +FastDelegate6 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6)) { + return FastDelegate6(x, func); +} + +template +FastDelegate6 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6) const) { + return FastDelegate6(x, func); +} + +//N=7 +template +FastDelegate7 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7)) { + return FastDelegate7(x, func); +} + +template +FastDelegate7 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7) const) { + return FastDelegate7(x, func); +} + +//N=8 +template +FastDelegate8 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8)) { + return FastDelegate8(x, func); +} + +template +FastDelegate8 MakeDelegate(Y* x, RetType (X::*func)(Param1 p1, Param2 p2, Param3 p3, Param4 p4, Param5 p5, Param6 p6, Param7 p7, Param8 p8) const) { + return FastDelegate8(x, func); +} + + + // clean up after ourselves... +#undef FASTDLGT_RETTYPE + +} // namespace fastdelegate + +#endif // !defined(FASTDELEGATE_H) + diff --git a/Engine/source/core/util/autoPtr.h b/Engine/source/core/util/autoPtr.h new file mode 100644 index 000000000..446934e9a --- /dev/null +++ b/Engine/source/core/util/autoPtr.h @@ -0,0 +1,131 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _AUTOPTR_H_ +#define _AUTOPTR_H_ + +#ifndef _TYPETRAITS_H_ +# include "platform/typetraits.h" +#endif + + +template +struct AutoPtrRef +{ + T* _ptr; + AutoPtrRef(T *ptr) + : _ptr(ptr) + {} +}; + +/// A simple smart pointer. +/// An extended version of std::auto_ptr which supports a deletion policy. +/// The delete policy indicates how the ptr is to be deleted. DeleteSingle, +/// the default, is used to delete individual objects. DeleteArray can +/// can be used to delete arrays. +/// +/// AutoPtr ptr(new Object); +/// AutoPtr ptr(new Object); +/// AutoPtr ptr(new Object[10]); +/// +/// AutoPtrs do not perform reference counting and assume total ownership +/// of any object assigned to them. Assigning an AutoPtr to another transfers +/// that ownership and resets the source AutoPtr to 0. +template +class AutoPtr +{ +public: + typedef T ValueType; + + explicit AutoPtr(T *ptr = 0): _ptr(ptr) {} + ~AutoPtr() + { + P::destroy(_ptr); + } + + // Copy constructors + AutoPtr(AutoPtr &rhs): _ptr(rhs.release()) {} + + template + AutoPtr(AutoPtr &rhs): _ptr(rhs.release()) { } + + /// Transfer ownership, any object currently be referenced is deleted and + /// rhs is set to 0. + AutoPtr& operator= (AutoPtr &rhs) + { + reset(rhs.release()); + return *this; + } + + template + AutoPtr& operator= (AutoPtr &rhs) + { + reset(rhs.release()); + return *this; + } + + // Access + T* ptr() const { return _ptr; } + T& operator*() const { return *_ptr; } + T* operator->() const { return _ptr; } + T& operator[](size_t index) { return (_ptr)[index]; } + + /// Release ownership of the object without deleting it. + T* release() + { + T* tmp(_ptr); + _ptr = 0; + return tmp; + } + + /// Equivalent to *this = (T*)ptr, except that operator=(T*) isn't provided for. + void reset(T* ptr = 0) + { + if (_ptr != ptr) + { + P::destroy(_ptr); + _ptr = ptr; + } + } + + // Conversion to/from ref type + AutoPtr(AutoPtrRef ref): _ptr(ref._ptr) {} + AutoPtr& operator= (AutoPtrRef ref) + { + reset(ref._ptr); + return *this; + } + + bool isNull() const { return _ptr == NULL; } + bool isValid() const { return !isNull(); } + + template + operator AutoPtrRef() { return AutoPtrRef(release()); } + + template + operator AutoPtr() { return AutoPtr(release()); } + +private: + T *_ptr; +}; + +#endif diff --git a/Engine/source/core/util/byteBuffer.cpp b/Engine/source/core/util/byteBuffer.cpp new file mode 100644 index 000000000..333f34172 --- /dev/null +++ b/Engine/source/core/util/byteBuffer.cpp @@ -0,0 +1,158 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/util/byteBuffer.h" + + +namespace Torque +{ + +class PrivateBBData +{ +public: + PrivateBBData() + : refCount( 1 ), + dataSize( 0 ), + data( NULL ) + { + } + + U32 refCount; ///< Reference count + U32 dataSize; ///< Length of buffer + U8 *data; ///< Our data buffer +}; + +//-------------------------------------- + +ByteBuffer::ByteBuffer() +{ + _data = new PrivateBBData; + _data->dataSize = 0; + _data->data = NULL; +} + +ByteBuffer::ByteBuffer(U8 *dataPtr, U32 bufferSize) +{ + _data = new PrivateBBData; + _data->dataSize = bufferSize; + _data->data = new U8[bufferSize]; + + dMemcpy( _data->data, dataPtr, bufferSize ); +} + +ByteBuffer::ByteBuffer(U32 bufferSize) +{ + _data = new PrivateBBData; + _data->dataSize = bufferSize; + _data->data = new U8[bufferSize]; +} + +ByteBuffer::ByteBuffer(const ByteBuffer &theBuffer) +{ + _data = theBuffer._data; + _data->refCount++; +} + +ByteBuffer &ByteBuffer::operator=(const ByteBuffer &theBuffer) +{ + _data = theBuffer._data; + _data->refCount++; + + return *this; +} + +ByteBuffer::~ByteBuffer() +{ + if (!--_data->refCount) + { + delete [] _data->data; + delete _data; + + _data = NULL; + } +} + +void ByteBuffer::setBuffer(U8 *dataPtr, U32 bufferSize, bool copyData) +{ + U8 *newData = dataPtr; + + if ( copyData ) + { + newData = new U8[bufferSize]; + + dMemcpy( newData, dataPtr, bufferSize ); + } + + delete [] _data->data; + + _data->data = newData; + _data->dataSize = bufferSize; +} + +void ByteBuffer::resize(U32 newBufferSize) +{ + U8 *newData = new U8[newBufferSize]; + + U32 copyLen = getMin( newBufferSize, _data->dataSize ); + + dMemcpy( newData, _data->data, copyLen ); + + delete [] _data->data; + + _data->data = newData; + _data->dataSize = newBufferSize; +} + +void ByteBuffer::appendBuffer(const U8 *dataBuffer, U32 bufferSize) +{ + U32 start = _data->dataSize; + resize(start + bufferSize); + dMemcpy(_data->data + start, dataBuffer, bufferSize); +} + +U32 ByteBuffer::getBufferSize() const +{ + return _data->dataSize; +} + +U8 *ByteBuffer::getBuffer() +{ + return _data->data; +} + +const U8 *ByteBuffer::getBuffer() const +{ + return _data->data; +} + +ByteBuffer ByteBuffer::getCopy() const +{ + return ByteBuffer( _data->data, _data->dataSize ); +} + +void ByteBuffer::clear() +{ + dMemset(_data->data, 0, _data->dataSize); +} + + +} diff --git a/Engine/source/core/util/byteBuffer.h b/Engine/source/core/util/byteBuffer.h new file mode 100644 index 000000000..79751aad0 --- /dev/null +++ b/Engine/source/core/util/byteBuffer.h @@ -0,0 +1,85 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _BYTEBUFFER_H_ +#define _BYTEBUFFER_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif + +namespace Torque +{ + +class PrivateBBData; + +class ByteBuffer +{ +public: + ByteBuffer(); + + /// Create a ByteBuffer from a chunk of memory. + ByteBuffer(U8 *dataPtr, U32 bufferSize); + + /// Create a ByteBuffer of the specified size. + ByteBuffer(U32 bufferSize); + + /// Copy constructor + ByteBuffer(const ByteBuffer &theBuffer); + + ByteBuffer &operator=(const ByteBuffer &theBuffer); + + ~ByteBuffer(); + + /// Set the ByteBuffer to point to a new chunk of memory. + void setBuffer(U8 *dataPtr, U32 bufferSize, bool copyData); + + /// Resize the buffer. + void resize(U32 newBufferSize); + + /// Appends the specified buffer to the end of the byte buffer. + void appendBuffer(const U8 *dataBuffer, U32 bufferSize); + + /// Appends the specified ByteBuffer to the end of this byte buffer. + void appendBuffer(const ByteBuffer &theBuffer) + { + appendBuffer(theBuffer.getBuffer(), theBuffer.getBufferSize()); + } + + U32 getBufferSize() const; + + U8 *getBuffer(); + const U8 *getBuffer() const; + + /// Copy the data in the buffer. + ByteBuffer getCopy() const; + + /// Clear the buffer. + void clear(); + +private: + PrivateBBData *_data; +}; + +} + +#endif diff --git a/Engine/source/core/util/byteswap.h b/Engine/source/core/util/byteswap.h new file mode 100644 index 000000000..c5d0814b3 --- /dev/null +++ b/Engine/source/core/util/byteswap.h @@ -0,0 +1,25 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef BYTESWAP +#define BYTESWAP(x, y) x = x ^ y; y = x ^ y; x = x ^y; +#endif //defined(BYTESWAP) \ No newline at end of file diff --git a/Engine/source/core/util/commonSwizzles.cpp b/Engine/source/core/util/commonSwizzles.cpp new file mode 100644 index 000000000..b71b6b4d5 --- /dev/null +++ b/Engine/source/core/util/commonSwizzles.cpp @@ -0,0 +1,42 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/util/swizzle.h" + +namespace Swizzles +{ + dsize_t _bgra[] = { 2, 1, 0, 3 }; + dsize_t _bgr[] = { 2, 1, 0 }; + dsize_t _rgb[] = { 0, 1, 2 }; + dsize_t _argb[] = { 3, 0, 1, 2 }; + dsize_t _rgba[] = { 0, 1, 2, 3 }; + dsize_t _abgr[] = { 3, 2, 1, 0 }; + + Swizzle bgra( _bgra ); + Swizzle bgr( _bgr ); + Swizzle rgb( _rgb ); + Swizzle argb( _argb ); + Swizzle rgba( _rgba ); + Swizzle abgr( _abgr ); + + NullSwizzle null; +} \ No newline at end of file diff --git a/Engine/source/core/util/delegate.h b/Engine/source/core/util/delegate.h new file mode 100644 index 000000000..6dbf1f668 --- /dev/null +++ b/Engine/source/core/util/delegate.h @@ -0,0 +1,61 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _UTIL_DELEGATE_H_ +#define _UTIL_DELEGATE_H_ + +#include "core/util/FastDelegate.h" + +/// @def Delegate +/// The macro which abstracts the details of the delegate implementation. +#define Delegate fastdelegate::FastDelegate + +/// @typedef DelegateMemento +/// An opaque structure which can hold an arbitary delegate. +/// @see Delegate +typedef fastdelegate::DelegateMemento DelegateMemento; + + +template +class DelegateRemapper : public DelegateMemento +{ +public: + DelegateRemapper() : mOffset(0) {} + + void set(T * t, const DelegateMemento & memento) + { + SetMementoFrom(memento); + if (m_pthis) + mOffset = ((int)m_pthis) - ((int)t); + } + + void rethis(T * t) + { + if (m_pthis) + m_pthis = (fastdelegate::detail::GenericClass *)(mOffset + (int)t); + } + +protected: + int mOffset; +}; + +#endif // _UTIL_DELEGATE_H_ \ No newline at end of file diff --git a/Engine/source/core/util/dxt5nmSwizzle.h b/Engine/source/core/util/dxt5nmSwizzle.h new file mode 100644 index 000000000..555a21eee --- /dev/null +++ b/Engine/source/core/util/dxt5nmSwizzle.h @@ -0,0 +1,108 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _DXT5nm_SWIZZLE_H_ +#define _DXT5nm_SWIZZLE_H_ + +#include "core/util/swizzle.h" +#include "core/util/byteswap.h" + +class DXT5nmSwizzle : public Swizzle +{ +public: + DXT5nmSwizzle() : Swizzle( NULL ) {}; + + virtual void InPlace( void *memory, const dsize_t size ) const + { + AssertFatal( size % 4 == 0, "Bad buffer size for DXT5nm Swizzle" ); + + volatile U8 *u8Mem = reinterpret_cast( memory ); + + for( int i = 0; i < size >> 2; i++ ) + { + // g = garbage byte + // Input: [X|Y|Z|g] (rgba) + // Output: [g|Y|0xFF|X] (bgra) + BYTESWAP( u8Mem[0], u8Mem[3] ); // Store X in Alpha + *u8Mem ^= *u8Mem; // 0 the garbage bit + u8Mem[2] |= 0xFF; // Set Red to 1.0 + + u8Mem += 4; + } + } + + virtual void ToBuffer( void *destination, const void *source, const dsize_t size ) const + { + AssertFatal( size % 4 == 0, "Bad buffer size for DXT5nm Swizzle" ); + + volatile const U8 *srcU8 = reinterpret_cast( source ); + volatile U8 *dstU8 = reinterpret_cast( destination ); + + for( int i = 0; i < size >> 2; i++ ) + { + // g = garbage byte + // Input: [X|Y|Z|g] (rgba) + // Output: [g|Y|0xFF|X] (bgra) + *dstU8++ = 0; // 0 garbage bit + *dstU8++ = srcU8[1]; // Copy Y into G + *dstU8++ |= 0xFF; // Set Red to 1.0 + *dstU8++ = srcU8[0]; // Copy X into Alpha + + srcU8 += 4; + } + } +}; + +class DXT5nmSwizzleUp24t32 : public Swizzle +{ +public: + DXT5nmSwizzleUp24t32() : Swizzle( NULL ) {}; + + virtual void InPlace( void *memory, const dsize_t size ) const + { + AssertISV( false, "Cannot swizzle in place a 24->32 bit swizzle." ); + } + + virtual void ToBuffer( void *destination, const void *source, const dsize_t size ) const + { + AssertFatal( size % 3 == 0, "Bad buffer size for DXT5nm Swizzle" ); + const int pixels = size / 3; + + volatile const U8 *srcU8 = reinterpret_cast( source ); + volatile U8 *dstU8 = reinterpret_cast( destination ); + + // destination better damn well be the right size + for( int i = 0; i < pixels; i++ ) + { + // g = garbage byte + // Input: [X|Y|Z|g] (rgba) + // Output: [g|Y|0xFF|X] (bgra) + *dstU8++ = 0; // 0 garbage bit + *dstU8++ = srcU8[1]; // Copy Y into G + *dstU8++ |= 0xFF; // Set Red to 1.0 + *dstU8++ = srcU8[0]; // Copy X into Alpha + + srcU8 += 3; + } + } +}; +#endif diff --git a/Engine/source/core/util/endian.h b/Engine/source/core/util/endian.h new file mode 100644 index 000000000..566f8be54 --- /dev/null +++ b/Engine/source/core/util/endian.h @@ -0,0 +1,141 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _ENDIAN_H_ +#define _ENDIAN_H_ + +#ifndef _TORQUE_TYPES_H_ +#include "platform/types.h" +#endif + +//------------------------------------------------------------------------------ +// Endian conversions + +inline U8 endianSwap(const U8 in_swap) +{ + return in_swap; +} + +inline S8 endianSwap(const S8 in_swap) +{ + return in_swap; +} + +/** +Convert the byte ordering on the U16 to and from big/little endian format. +@param in_swap Any U16 +@returns swapped U16. +*/ + +inline U16 endianSwap(const U16 in_swap) +{ + return U16(((in_swap >> 8) & 0x00ff) | + ((in_swap << 8) & 0xff00)); +} + +inline S16 endianSwap(const S16 in_swap) +{ + return S16(endianSwap(U16(in_swap))); +} + +/** +Convert the byte ordering on the U32 to and from big/little endian format. +@param in_swap Any U32 +@returns swapped U32. +*/ +inline U32 endianSwap(const U32 in_swap) +{ + return U32(((in_swap >> 24) & 0x000000ff) | + ((in_swap >> 8) & 0x0000ff00) | + ((in_swap << 8) & 0x00ff0000) | + ((in_swap << 24) & 0xff000000)); +} + +inline S32 endianSwap(const S32 in_swap) +{ + return S32(endianSwap(U32(in_swap))); +} + +inline U64 endianSwap(const U64 in_swap) +{ + U32 *inp = (U32 *) &in_swap; + U64 ret; + U32 *outp = (U32 *) &ret; + outp[0] = endianSwap(inp[1]); + outp[1] = endianSwap(inp[0]); + return ret; +} + +inline S64 endianSwap(const S64 in_swap) +{ + return S64(endianSwap(U64(in_swap))); +} + +inline F32 endianSwap(const F32 in_swap) +{ + U32 result = endianSwap(* ((U32 *) &in_swap) ); + return * ((F32 *) &result); +} + +inline F64 endianSwap(const F64 in_swap) +{ + U64 result = endianSwap(* ((U64 *) &in_swap) ); + return * ((F64 *) &result); +} + +//------------------------------------------------------------------------------ +// Endian conversions + +#ifdef TORQUE_LITTLE_ENDIAN + +#define TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(type) \ + inline type convertHostToLEndian(type i) { return i; } \ + inline type convertLEndianToHost(type i) { return i; } \ + inline type convertHostToBEndian(type i) { return endianSwap(i); } \ + inline type convertBEndianToHost(type i) { return endianSwap(i); } + +#elif defined(TORQUE_BIG_ENDIAN) + +#define TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(type) \ + inline type convertHostToLEndian(type i) { return endianSwap(i); } \ + inline type convertLEndianToHost(type i) { return endianSwap(i); } \ + inline type convertHostToBEndian(type i) { return i; } \ + inline type convertBEndianToHost(type i) { return i; } + +#else +#error "Endian define not set!" +#endif + + +TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(U8) +TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(S8) +TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(U16) +TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(S16) +TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(U32) +TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(S32) +TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(U64) +TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(S64) +TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(F32) +TORQUE_DECLARE_TEMPLATIZED_ENDIAN_CONV(F64) + +#endif + diff --git a/Engine/source/core/util/fourcc.h b/Engine/source/core/util/fourcc.h new file mode 100644 index 000000000..c49a6f406 --- /dev/null +++ b/Engine/source/core/util/fourcc.h @@ -0,0 +1,27 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef MakeFourCC +#define MakeFourCC(ch0, ch1, ch2, ch3) \ + ((U32)(U8)(ch0) | ((U32)(U8)(ch1) << 8) | \ + ((U32)(U8)(ch2) << 16) | ((U32)(U8)(ch3) << 24 )) +#endif //defined(MakeFourCC) diff --git a/Engine/source/core/util/hashFunction.cpp b/Engine/source/core/util/hashFunction.cpp new file mode 100644 index 000000000..c9ecd24ff --- /dev/null +++ b/Engine/source/core/util/hashFunction.cpp @@ -0,0 +1,271 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// Borrowed from: http://burtleburtle.net/bob/hash/doobs.html +// +// Original code by: +// +// By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use this +// code any way you wish, private, educational, or commercial. It's free. + +#include "platform/platform.h" + +#include "core/util/hashFunction.h" + +namespace Torque +{ + +#define hashsize(n) ((U32)1<<(n)) +#define hashmask(n) (hashsize(n)-1) + +/* +-------------------------------------------------------------------- +mix -- mix 3 32-bit values reversibly. +For every delta with one or two bits set, and the deltas of all three +high bits or all three low bits, whether the original value of a,b,c +is almost all zero or is uniformly distributed, +* If mix() is run forward or backward, at least 32 bits in a,b,c +have at least 1/4 probability of changing. +* If mix() is run forward, every bit of c will change between 1/3 and +2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.) +mix() was built out of 36 single-cycle latency instructions in a +structure that could supported 2x parallelism, like so: +a -= b; +a -= c; x = (c>>13); +b -= c; a ^= x; +b -= a; x = (a<<8); +c -= a; b ^= x; +c -= b; x = (b>>13); +... +Unfortunately, superscalar Pentiums and Sparcs can't take advantage +of that parallelism. They've also turned some of those single-cycle +latency instructions into multi-cycle latency instructions. Still, +this is the fastest good hash I could find. There were about 2^^68 +to choose from. I only looked at a billion or so. +-------------------------------------------------------------------- +*/ +#define mix(a,b,c) \ +{ \ + a -= b; a -= c; a ^= (c>>13); \ + b -= c; b -= a; b ^= (a<<8); \ + c -= a; c -= b; c ^= (b>>13); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<16); \ + c -= a; c -= b; c ^= (b>>5); \ + a -= b; a -= c; a ^= (c>>3); \ + b -= c; b -= a; b ^= (a<<10); \ + c -= a; c -= b; c ^= (b>>15); \ +} + +/* +-------------------------------------------------------------------- +hash() -- hash a variable-length key into a 32-bit value +k : the key (the unaligned variable-length array of bytes) +len : the length of the key, counting by bytes +initval : can be any 4-byte value +Returns a 32-bit value. Every bit of the key affects every bit of +the return value. Every 1-bit and 2-bit delta achieves avalanche. +About 6*len+35 instructions. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 32 bits, +use a bitmask. For example, if you need only 10 bits, do +h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (U8 **)k, do it like this: +for (i=0, h=0; i= 12) + { + a += (k[0] +((U32)k[1]<<8) +((U32)k[2]<<16) +((U32)k[3]<<24)); + b += (k[4] +((U32)k[5]<<8) +((U32)k[6]<<16) +((U32)k[7]<<24)); + c += (k[8] +((U32)k[9]<<8) +((U32)k[10]<<16)+((U32)k[11]<<24)); + mix(a,b,c); + k += 12; len -= 12; + } + + /*------------------------------------- handle the last 11 bytes */ + c += length; + switch(len) /* all the case statements fall through */ + { + case 11: c+=((U32)k[10]<<24); + case 10: c+=((U32)k[9]<<16); + case 9 : c+=((U32)k[8]<<8); + /* the first byte of c is reserved for the length */ + case 8 : b+=((U32)k[7]<<24); + case 7 : b+=((U32)k[6]<<16); + case 6 : b+=((U32)k[5]<<8); + case 5 : b+=k[4]; + case 4 : a+=((U32)k[3]<<24); + case 3 : a+=((U32)k[2]<<16); + case 2 : a+=((U32)k[1]<<8); + case 1 : a+=k[0]; + /* case 0: nothing left to add */ + } + mix(a,b,c); + /*-------------------------------------------- report the result */ + return c; +} + + +/* +-------------------------------------------------------------------- +mix -- mix 3 64-bit values reversibly. +mix() takes 48 machine instructions, but only 24 cycles on a superscalar + machine (like Intel's new MMX architecture). It requires 4 64-bit + registers for 4::2 parallelism. +All 1-bit deltas, all 2-bit deltas, all deltas composed of top bits of + (a,b,c), and all deltas of bottom bits were tested. All deltas were + tested both on random keys and on keys that were nearly all zero. + These deltas all cause every bit of c to change between 1/3 and 2/3 + of the time (well, only 113/400 to 287/400 of the time for some + 2-bit delta). These deltas all cause at least 80 bits to change + among (a,b,c) when the mix is run either forward or backward (yes it + is reversible). +This implies that a hash using mix64 has no funnels. There may be + characteristics with 3-bit deltas or bigger, I didn't test for + those. +-------------------------------------------------------------------- +*/ +#define mix64(a,b,c) \ +{ \ + a -= b; a -= c; a ^= (c>>43); \ + b -= c; b -= a; b ^= (a<<9); \ + c -= a; c -= b; c ^= (b>>8); \ + a -= b; a -= c; a ^= (c>>38); \ + b -= c; b -= a; b ^= (a<<23); \ + c -= a; c -= b; c ^= (b>>5); \ + a -= b; a -= c; a ^= (c>>35); \ + b -= c; b -= a; b ^= (a<<49); \ + c -= a; c -= b; c ^= (b>>11); \ + a -= b; a -= c; a ^= (c>>12); \ + b -= c; b -= a; b ^= (a<<18); \ + c -= a; c -= b; c ^= (b>>22); \ +} + +/* +-------------------------------------------------------------------- +hash64() -- hash a variable-length key into a 64-bit value + k : the key (the unaligned variable-length array of bytes) + len : the length of the key, counting by bytes + level : can be any 8-byte value +Returns a 64-bit value. Every bit of the key affects every bit of +the return value. No funnels. Every 1-bit and 2-bit delta achieves +avalanche. About 41+5len instructions. + +The best hash table sizes are powers of 2. There is no need to do +mod a prime (mod is sooo slow!). If you need less than 64 bits, +use a bitmask. For example, if you need only 10 bits, do + h = (h & hashmask(10)); +In which case, the hash table should have hashsize(10) elements. + +If you are hashing n strings (ub1 **)k, do it like this: + for (i=0, h=0; i= 24) + { + a += (k[0] +((U64)k[ 1]<< 8)+((U64)k[ 2]<<16)+((U64)k[ 3]<<24) + +((U64)k[4 ]<<32)+((U64)k[ 5]<<40)+((U64)k[ 6]<<48)+((U64)k[ 7]<<56)); + b += (k[8] +((U64)k[ 9]<< 8)+((U64)k[10]<<16)+((U64)k[11]<<24) + +((U64)k[12]<<32)+((U64)k[13]<<40)+((U64)k[14]<<48)+((U64)k[15]<<56)); + c += (k[16] +((U64)k[17]<< 8)+((U64)k[18]<<16)+((U64)k[19]<<24) + +((U64)k[20]<<32)+((U64)k[21]<<40)+((U64)k[22]<<48)+((U64)k[23]<<56)); + mix64(a,b,c); + k += 24; len -= 24; + } + + /*------------------------------------- handle the last 23 bytes */ + c += length; + switch(len) /* all the case statements fall through */ + { + case 23: c+=((U64)k[22]<<56); + case 22: c+=((U64)k[21]<<48); + case 21: c+=((U64)k[20]<<40); + case 20: c+=((U64)k[19]<<32); + case 19: c+=((U64)k[18]<<24); + case 18: c+=((U64)k[17]<<16); + case 17: c+=((U64)k[16]<<8); + /* the first byte of c is reserved for the length */ + case 16: b+=((U64)k[15]<<56); + case 15: b+=((U64)k[14]<<48); + case 14: b+=((U64)k[13]<<40); + case 13: b+=((U64)k[12]<<32); + case 12: b+=((U64)k[11]<<24); + case 11: b+=((U64)k[10]<<16); + case 10: b+=((U64)k[ 9]<<8); + case 9: b+=((U64)k[ 8]); + case 8: a+=((U64)k[ 7]<<56); + case 7: a+=((U64)k[ 6]<<48); + case 6: a+=((U64)k[ 5]<<40); + case 5: a+=((U64)k[ 4]<<32); + case 4: a+=((U64)k[ 3]<<24); + case 3: a+=((U64)k[ 2]<<16); + case 2: a+=((U64)k[ 1]<<8); + case 1: a+=((U64)k[ 0]); + /* case 0: nothing left to add */ + } + mix64(a,b,c); + /*-------------------------------------------- report the result */ + return c; +} + +} // namespace diff --git a/Engine/source/core/util/hashFunction.h b/Engine/source/core/util/hashFunction.h new file mode 100644 index 000000000..d2e1d5b4a --- /dev/null +++ b/Engine/source/core/util/hashFunction.h @@ -0,0 +1,39 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _HASHFUNCTION_H_ +#define _HASHFUNCTION_H_ + +#ifndef _TORQUE_TYPES_H_ +#include "platform/types.h" +#endif + +namespace Torque +{ + +extern U32 hash(register const U8 *k, register U32 length, register U32 initval); + +extern U64 hash64(register const U8 *k, register U32 length, register U64 initval); + +} + +#endif // _HASHFUNCTION_H_ diff --git a/Engine/source/core/util/journal/journal.cpp b/Engine/source/core/util/journal/journal.cpp new file mode 100644 index 000000000..d67bceb47 --- /dev/null +++ b/Engine/source/core/util/journal/journal.cpp @@ -0,0 +1,202 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/util/journal/journal.h" + +#include "core/stream/fileStream.h" +#include "core/util/safeDelete.h" +#include "console/console.h" +#include + +//----------------------------------------------------------------------------- + +Journal::FuncDecl* Journal::_FunctionList; +Stream *Journal::mFile; +Journal::Mode Journal::_State = Journal::StopState; +U32 Journal::_Count; +bool Journal::_Dispatching = false; + +Journal Journal::smInstance; + +//----------------------------------------------------------------------------- +Journal::~Journal() +{ + if( mFile ) + Stop(); +} + +//----------------------------------------------------------------------------- + +Journal::Functor* Journal::_create(Id id) +{ + for (FuncDecl* ptr = _FunctionList; ptr; ptr = ptr->next) + if (ptr->id == id) + return ptr->create(); + return 0; +} + +Journal::Id Journal::_getFunctionId(VoidPtr ptr,VoidMethod method) +{ + for (FuncDecl* itr = _FunctionList; itr; itr = itr->next) + if (itr->match(ptr,method)) + return itr->id; + return 0; +} + +void Journal::_removeFunctionId(VoidPtr ptr,VoidMethod method) +{ + FuncDecl ** itr = &_FunctionList; + + do + { + if((*itr)->match(ptr, method)) + { + // Unlink and break. + FuncDecl* decl = *itr; + idPool().free( decl->id ); + *itr = (*itr)->next; + delete decl; + return; + } + + // Advance to next... + itr = &((*itr)->next); + } + while(*itr); +} + +void Journal::_start() +{ +} + +void Journal::_finish() +{ + if (_State == PlayState) + --_Count; + else { + U32 pos = mFile->getPosition(); + mFile->setPosition(0); + mFile->write(++_Count); + mFile->setPosition(pos); + } +} + +void Journal::Record(const char * file) +{ + if (_State == DisabledState) + { + Con::errorf("//---------------------------------------------//"); + Con::errorf("Journal::Record() - Cannot record a journal after GuiCanvas or NetConnection creation!"); + Con::errorf("To record before canvas/netConnection creation, run %s with the following arguments: -jSave %s", + Platform::getExecutableName(), file); + Con::errorf("//---------------------------------------------//"); + return; + } + + if (_State == StopState) + { + _Count = 0; + mFile = new FileStream(); + + if( ((FileStream*)mFile)->open(file, Torque::FS::File::Write) ) + { + mFile->write(_Count); + _State = RecordState; + } + else + { + AssertWarn(false,"Journal: Could not create journal file"); + Con::errorf("Journal: Could not create journal file '%s'", file); + } + } +} + +void Journal::Play(const char * file) +{ + if (_State == DisabledState) + { + Con::errorf("//---------------------------------------------//"); + Con::errorf("Journal::Play() - Cannot playback a journal after GuiCanvas or NetConnection creation!"); + Con::errorf("To playback before canvas/netConnection creation, run %s with the following arguments: -jPlay %s", + Platform::getExecutableName(), file); + Con::errorf("//---------------------------------------------//"); + return; + } + + if (_State == StopState) + { + SAFE_DELETE(mFile); + mFile = new FileStream(); + if( ((FileStream*)mFile)->open(file, Torque::FS::File::Read) ) + { + mFile->read(&_Count); + _State = PlayState; + } + else + { + AssertWarn(false,"Journal: Could not open journal file"); + Con::errorf("Journal: Could not open journal file '%s'", file); + } + } +} + +void Journal::Stop() +{ + AssertFatal(mFile, "Journal::Stop - no file stream open!"); + + SAFE_DELETE( mFile ); + _State = StopState; +} + +bool Journal::PlayNext() +{ + if (_State == PlayState) { + _start(); + Id id; + + mFile->read(&id); + + Functor* jrn = _create(id); + AssertFatal(jrn,"Journal: Undefined function found in journal"); + jrn->read(mFile); + _finish(); + + _Dispatching = true; + jrn->dispatch(); + _Dispatching = false; + + delete jrn; + if (_Count) + return true; + Stop(); + + //debugBreak(); + } + return false; +} + +void Journal::Disable() +{ + if (_State == StopState) + _State = DisabledState; +} \ No newline at end of file diff --git a/Engine/source/core/util/journal/journal.h b/Engine/source/core/util/journal/journal.h new file mode 100644 index 000000000..aefebcd23 --- /dev/null +++ b/Engine/source/core/util/journal/journal.h @@ -0,0 +1,635 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _UTIL_JOURNAL_JOURNAL_H_ +#define _UTIL_JOURNAL_JOURNAL_H_ + +#include "platform/platform.h" +#include "core/stream/stream.h" +#include "util/returnType.h" +#include "core/stream/ioHelper.h" +#include "core/idGenerator.h" + +/// Journaling System. +/// +/// The journaling system is used to record external events and +/// non-deterministic values from an execution run in order that the +/// run may be re-played for debugging purposes. The journaling +/// system is integrated into the platform library, though not all +/// platform calls are journaled. +/// +/// File system calls are not journaled, so any modified files must +/// be reset to their original state before playback. Only a single +/// journal can be recored or played back at a time. +/// +/// For the journals to play back correctly, journal events cannot +/// be triggered during the processing of another event. +class Journal +{ + Journal() {} + ~Journal(); + + static Journal smInstance; + + typedef U32 Id; + typedef void* VoidPtr; + typedef void (Journal::*VoidMethod)(); + + /// Functor base classes + struct Functor + { + Functor() {} + virtual ~Functor() {} + virtual void read(Stream *s) = 0; + virtual void dispatch() = 0; + }; + + /// Multiple argument function functor specialization + template + struct FunctorDecl: public Functor { + typedef void(*FuncPtr)(); + FuncPtr ptr; + FunctorDecl(FuncPtr p): ptr(p) {} + void read(Stream *file) {} + void dispatch() { (*ptr)(); } + }; + + template + struct FunctorDecl< void(*)(A,B,C,D,E,F,G,H,I,J) >: public Functor { + typedef void(*FuncPtr)(A,B,C,D,E,F,G,H,I,J); + FuncPtr ptr; A a; B b; C c; D d; E e; F f; G g; H h; I i; J j; + FunctorDecl(FuncPtr p): ptr(p) {} + void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f,g,h,i,j); } + void dispatch() { (*ptr)(a,b,c,d,e,f,g,h,i,j); } + }; + + template + struct FunctorDecl< void(*)(A,B,C,D,E,F,G,H,I) >: public Functor { + typedef void(*FuncPtr)(A,B,C,D,E,F,G,H,I); + FuncPtr ptr; A a; B b; C c; D d; E e; F f; G g; H h; I i; + FunctorDecl(FuncPtr p): ptr(p) {} + void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f,g,h,i); } + void dispatch() { (*ptr)(a,b,c,d,e,f,g,h,i); } + }; + + template + struct FunctorDecl< void(*)(A,B,C,D,E,F,G,H) >: public Functor { + typedef void(*FuncPtr)(A,B,C,D,E,F,G,H); + FuncPtr ptr; A a; B b; C c; D d; E e; F f; G g; H h; + FunctorDecl(FuncPtr p): ptr(p) {} + void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f,g,h); } + void dispatch() { (*ptr)(a,b,c,d,e,f,g,h); } + }; + + template + struct FunctorDecl< void(*)(A,B,C,D,E,F,G) >: public Functor { + typedef void(*FuncPtr)(A,B,C,D,E,F,G); + FuncPtr ptr; A a; B b; C c; D d; E e; F f; G g; + FunctorDecl(FuncPtr p): ptr(p) {} + void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f,g); } + void dispatch() { (*ptr)(a,b,c,d,e,f,g); } + }; + + template + struct FunctorDecl< void(*)(A,B,C,D,E,F) >: public Functor { + typedef void(*FuncPtr)(A,B,C,D,E,F); + FuncPtr ptr; A a; B b; C c; D d; E e; F f; + FunctorDecl(FuncPtr p): ptr(p) {} + void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f); } + void dispatch() { (*ptr)(a,b,c,d,e,f); } + }; + + template + struct FunctorDecl< void(*)(A,B,C,D,E) >: public Functor { + typedef void(*FuncPtr)(A,B,C,D,E); + FuncPtr ptr; A a; B b; C c; D d; E e; + FunctorDecl(FuncPtr p): ptr(p) {} + void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e); } + void dispatch() { (*ptr)(a,b,c,d,e); } + }; + + template + struct FunctorDecl< void(*)(A,B,C,D) >: public Functor { + typedef void(*FuncPtr)(A,B,C,D); + FuncPtr ptr; A a; B b; C c; D d; + FunctorDecl(FuncPtr p): ptr(p) {} + void read(Stream *file) { IOHelper::reads(file,a,b,c,d); } + void dispatch() { (*ptr)(a,b,c,d); } + }; + + template + struct FunctorDecl< void(*)(A,B,C) >: public Functor { + typedef void(*FuncPtr)(A,B,C); + FuncPtr ptr; A a; B b; C c; + FunctorDecl(FuncPtr p): ptr(p) {} + void read(Stream *file) { IOHelper::reads(file,a,b,c); } + void dispatch() { (*ptr)(a,b,c); } + }; + + template + struct FunctorDecl< void(*)(A,B) >: public Functor { + typedef void(*FuncPtr)(A,B); + FuncPtr ptr; A a; B b; + FunctorDecl(FuncPtr p): ptr(p) {} + void read(Stream *file) { IOHelper::reads(file,a,b); } + void dispatch() { (*ptr)(a,b); } + }; + + template + struct FunctorDecl< void(*)(A) >: public Functor { + typedef void(*FuncPtr)(A); + FuncPtr ptr; A a; + FunctorDecl(FuncPtr p): ptr(p) {} + void read(Stream *file) { IOHelper::reads(file,a); } + void dispatch() { (*ptr)(a); } + }; + + // Multiple argument object member function functor specialization + template + struct MethodDecl: public Functor { + typedef T ObjPtr; + typedef U MethodPtr; + ObjPtr obj; MethodPtr method; + MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {} + void read(Stream *file) {} + void dispatch() { (obj->*method)(); } + }; + + template + struct MethodDecl: public Functor { + typedef T* ObjPtr; + typedef void(T::*MethodPtr)(A,B,C,D,E,F,G,H,I,J); + ObjPtr obj; MethodPtr method; A a; B b; C c; D d; E e; F f; G g; H h; I i; J j; + MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {} + void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f,g,h,i,j); } + void dispatch() { (obj->*method)(a,b,c,d,e,f,g,h,i,j); } + }; + + template + struct MethodDecl: public Functor { + typedef T* ObjPtr; + typedef void(T::*MethodPtr)(A,B,C,D,E,F,G,H,I); + ObjPtr obj; MethodPtr method; A a; B b; C c; D d; E e; F f; G g; H h; I i; + MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {} + void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f,g,h,i); } + void dispatch() { (obj->*method)(a,b,c,d,e,f,g,h,i); } + }; + + template + struct MethodDecl: public Functor { + typedef T* ObjPtr; + typedef void(T::*MethodPtr)(A,B,C,D,E,F,G,H); + ObjPtr obj; MethodPtr method; A a; B b; C c; D d; E e; F f; G g; H h; + MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {} + void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f,g,h); } + void dispatch() { (obj->*method)(a,b,c,d,e,f,g,h); } + }; + + template + struct MethodDecl: public Functor { + typedef T* ObjPtr; + typedef void(T::*MethodPtr)(A,B,C,D,E,F,G); + ObjPtr obj; MethodPtr method; A a; B b; C c; D d; E e; F f; G g; + MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {} + void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f,g); } + void dispatch() { (obj->*method)(a,b,c,d,e,f,g); } + }; + + template + struct MethodDecl: public Functor { + typedef T* ObjPtr; + typedef void(T::*MethodPtr)(A,B,C,D,E,F); + ObjPtr obj; MethodPtr method; A a; B b; C c; D d; E e; F f; + MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {} + void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e,f); } + void dispatch() { (obj->*method)(a,b,c,d,e,f); } + }; + + template + struct MethodDecl: public Functor { + typedef T* ObjPtr; + typedef void(T::*MethodPtr)(A,B,C,D,E); + ObjPtr obj; MethodPtr method; A a; B b; C c; D d; E e; + MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {} + void read(Stream *file) { IOHelper::reads(file,a,b,c,d,e); } + void dispatch() { (obj->*method)(a,b,c,d,e); } + }; + + template + struct MethodDecl: public Functor { + typedef T* ObjPtr; + typedef void(T::*MethodPtr)(A,B,C,D); + ObjPtr obj; MethodPtr method; A a; B b; C c; D d; + MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {} + void read(Stream *file) { IOHelper::reads(file,a,b,c,d); } + void dispatch() { (obj->*method)(a,b,c,d); } + }; + + template + struct MethodDecl: public Functor { + typedef T* ObjPtr; + typedef void(T::*MethodPtr)(A,B,C); + ObjPtr obj; MethodPtr method; A a; B b; C c; + MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {} + void read(Stream *file) { IOHelper::reads(file,a,b,c); } + void dispatch() { (obj->*method)(a,b,c); } + }; + + template + struct MethodDecl: public Functor { + typedef T* ObjPtr; + typedef void(T::*MethodPtr)(A,B); + ObjPtr obj; MethodPtr method; A a; B b; + MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {} + void read(Stream *file) { IOHelper::reads(file,a,b); } + void dispatch() { (obj->*method)(a,b); } + }; + + template + struct MethodDecl: public Functor { + typedef T* ObjPtr; + typedef void(T::*MethodPtr)(A); + ObjPtr obj; MethodPtr method; A a; + MethodDecl(ObjPtr o,MethodPtr p): obj(o), method(p) {} + void read(Stream *file) { IOHelper::reads(file,a); } + void dispatch() { (obj->*method)(a); } + }; + + // Function declarations + struct FuncDecl { + FuncDecl* next; + Id id; + virtual ~FuncDecl() {} + virtual bool match(VoidPtr,VoidMethod) const = 0; + virtual Functor* create() const = 0; + }; + + template + struct FuncRep: public FuncDecl { + typename T::FuncPtr function; + virtual bool match(VoidPtr ptr,VoidMethod) const { + return function == (typename T::FuncPtr)ptr; + } + T* create() const { return new T(function); }; + }; + + template + struct MethodRep: public FuncDecl { + typename T::ObjPtr obj; + typename T::MethodPtr method; + virtual bool match(VoidPtr ptr,VoidMethod func) const { + return obj == (typename T::ObjPtr)ptr && method == (typename T::MethodPtr)func; + } + T* create() const { return new T(obj,method); }; + }; + + static FuncDecl* _FunctionList; + + static inline IdGenerator &idPool() + { + static IdGenerator _IdPool(1, 65535); + return _IdPool; + } + + static U32 _Count; + static Stream *mFile; + static enum Mode { + StopState, PlayState, RecordState, DisabledState + } _State; + static bool _Dispatching; + + static Functor* _create(Id id); + static void _start(); + static void _finish(); + static Id _getFunctionId(VoidPtr ptr,VoidMethod method); + static void _removeFunctionId(VoidPtr ptr,VoidMethod method); + +public: + static void Record(const char * file); + static void Play(const char * file); + static bool PlayNext(); + static void Stop(); + static void Disable(); + + /// Returns true if in recording mode. + static inline bool IsRecording() { + return _State == RecordState; + } + + /// Returns true if in play mode. + static inline bool IsPlaying() { + return _State == PlayState; + } + + /// Returns true if a function is being dispatched + static inline bool IsDispatching() { + return _Dispatching; + } + + template + static void Read(T *v) + { + AssertFatal(IsPlaying(), "Journal::Read - not playing right now."); + bool r = mFile->read(v); + AssertFatal(r, "Journal::Read - failed to read!"); + } + + static bool Read(U32 size, void *buffer) + { + AssertFatal(IsPlaying(), "Journal::Read - not playing right now."); + bool r = mFile->read(size, buffer); + AssertFatal(r, "Journal::Read - failed to read!"); + return r; + } + + static void ReadString(char str[256]) + { + AssertFatal(IsPlaying(), "Journal::ReadString - not playing right now."); + mFile->readString(str); + } + + template + static void Write(const T &v) + { + AssertFatal(IsRecording(), "Journal::Write - not recording right now."); + bool r = mFile->write(v); + AssertFatal(r, "Journal::Write - failed to write!"); + } + + static bool Write(U32 size, void *buffer) + { + AssertFatal(IsRecording(), "Journal::Write - not recording right now."); + bool r = mFile->write(size, buffer); + AssertFatal(r, "Journal::Write - failed to write!"); + return r; + } + + static void WriteString(const char str[256]) + { + AssertFatal(IsRecording(), "Journal::WriteString - not recording right now."); + mFile->writeString(str); + } + + /// Register a function with the journalling system. + template + static void DeclareFunction(T func) { + if (!_getFunctionId((VoidPtr)func,0)) { + FuncRep >* decl = new FuncRep >; + decl->function = func; + decl->id = idPool().alloc(); + decl->next = _FunctionList; + _FunctionList = decl; + } + } + + template + static void DeclareFunction(T obj, U method) + { + if (!_getFunctionId((VoidPtr)obj,(VoidMethod)method)) { + MethodRep >* decl = new MethodRep >; + decl->obj = obj; + decl->method = method; + decl->id = idPool().alloc(); + decl->next = _FunctionList; + _FunctionList = decl; + } + } + + template + static void RemoveFunction(T obj, U method) + { + _removeFunctionId((VoidPtr)obj,(VoidMethod)method); + } + + /// Journal a function's return value. The return value of the + /// function is stored into the journal and retrieved during + /// playback. During playback the function is not executed. + #define Method(Func,Arg1,Arg2) \ + static typename ReturnType::ValueType Result Arg1 { \ + typename ReturnType::ValueType value; \ + if (_Dispatching) \ + return; \ + if (_State == PlayState) { \ + _start(); \ + IOHelper::reads(mFile,value); \ + _finish(); \ + return value; \ + } \ + _Dispatching = true; \ + value = (*func) Arg2; \ + _Dispatching = false; \ + if (_State == RecordState) { \ + _start(); \ + IOHelper::writes(mFile,value); \ + _finish(); \ + } \ + return value; \ + } + + template + Method(Func,(Func func,A a,B b,C c,D d,E e,F f, G g, H h, I i, J j),(a,b,c,d,e,f,g,h,i,j)) + template + Method(Func,(Func func,A a,B b,C c,D d,E e,F f, G g, H h, I i),(a,b,c,d,e,f,g,h,i)) + template + Method(Func,(Func func,A a,B b,C c,D d,E e,F f, G g, H h),(a,b,c,d,e,f,g,h)) + template + Method(Func,(Func func,A a,B b,C c,D d,E e,F f, G g),(a,b,c,d,e,f,g)) + template + Method(Func,(Func func,A a,B b,C c,D d,E e,F f),(a,b,c,d,e,f)) + template + Method(Func,(Func func,A a,B b,C c,D d,E e),(a,b,c,d,e)) + template + Method(Func,(Func func,A a,B b,C c,D d),(a,b,c,d)) + template + Method(Func,(Func func,A a,B b,C c),(a,b,c)) + template + Method(Func,(Func func,A a,B b),(a,b)) + template + Method(Func,(Func func,A a),(a)) + template + Method(Func,(Func func),()) + #undef Method + + /// Journal a function call. Store the function id and all the + /// function's arguments into the journal. On journal playback the + /// function is executed with the retrieved arguments. The function + /// must have been previously declared using the declareFunction() + /// method. + #define Method(Arg1,Arg2,Arg3) \ + static void Call Arg1 { \ + if (_Dispatching) \ + return; \ + if (_State == PlayState) \ + return; \ + if (_State == RecordState) { \ + Id id = _getFunctionId((VoidPtr)func,0); \ + AssertFatal(id,"Journal: Function must be be declared before being called"); \ + _start(); \ + IOHelper::writes Arg2; \ + _finish(); \ + } \ + _Dispatching = true; \ + (*func) Arg3; \ + _Dispatching = false; \ + return; \ + } + + template + Method((Func func,A a,B b,C c,D d,E e,F f,G g,H h,I i,J j),(mFile,id,a,b,c,d,e,f,g,h,i,j),(a,b,c,d,e,f,g,h,i,j)) + template + Method((Func func,A a,B b,C c,D d,E e,F f,G g,H h,I i),(mFile,id,a,b,c,d,e,f,g,h,i),(a,b,c,d,e,f,g,h,i)) + template + Method((Func func,A a,B b,C c,D d,E e,F f,G g,H h),(mFile,id,a,b,c,d,e,f,g,h),(a,b,c,d,e,f,g,h)) + template + Method((Func func,A a,B b,C c,D d,E e,F f,G g),(mFile,id,a,b,c,d,e,f,g),(a,b,c,d,e,f,g)) + template + Method((Func func,A a,B b,C c,D d,E e,F f),(mFile,id,a,b,c,d,e,f),(a,b,c,d,e,f)) + template + Method((Func func,A a,B b,C c,D d,E e),(mFile,id,a,b,c,d,e),(a,b,c,d,e)) + template + Method((Func func,A a,B b,C c,D d),(mFile,id,a,b,c,d),(a,b,c,d)) + template + Method((Func func,A a,B b,C c),(mFile,id,a,b,c),(a,b,c)) + template + Method((Func func,A a,B b),(mFile,id,a,b),(a,b)) + template + Method((Func func,A a),(mFile,id,a),(a)) + template + Method((Func func),(mFile,id),()) + #undef Method + + #define Method(Arg1,Arg2,Arg3) \ + static void Call Arg1 { \ + if (_Dispatching) \ + return; \ + if (_State == PlayState) \ + return; \ + if (_State == RecordState) { \ + Id id = _getFunctionId((VoidPtr)obj,(VoidMethod)method); \ + AssertFatal(id != 0,"Journal: Function must be be declared before being called"); \ + _start(); \ + IOHelper::writes Arg2; \ + _finish(); \ + } \ + _Dispatching = true; \ + (obj->*method) Arg3; \ + _Dispatching = false; \ + return; \ + } + + + template + Method((Obj* obj,void (Obj::*method)(A,B,C,D,E,F,G,H,I,J),A a,B b,C c,D d,E e,F f,G g,H h,I i,J j),(mFile,id,a,b,c,d,e,f,g,h,i,j),(a,b,c,d,e,f,g,h,i,j)) + template + Method((Obj* obj,void (Obj::*method)(A,B,C,D,E,F,G,H,I),A a,B b,C c,D d,E e,F f,G g,H h,I i),(mFile,id,a,b,c,d,e,f,g,h,i),(a,b,c,d,e,f,g,h,i)) + template + Method((Obj* obj,void (Obj::*method)(A,B,C,D,E,F,G,H),A a,B b,C c,D d,E e,F f,G g,H h),(mFile,id,a,b,c,d,e,f,g,h),(a,b,c,d,e,f,g,h)) + template + Method((Obj* obj,void (Obj::*method)(A,B,C,D,E,F,G),A a,B b,C c,D d,E e,F f,G g),(mFile,id,a,b,c,d,e,f,g),(a,b,c,d,e,f,g)) + template + Method((Obj* obj,void (Obj::*method)(A,B,C,D,E,F),A a,B b,C c,D d,E e,F f),(mFile,id,a,b,c,d,e,f),(a,b,c,d,e,f)) + template + Method((Obj* obj,void (Obj::*method)(A,B,C,D,E),A a,B b,C c,D d,E e),(mFile,id,a,b,c,d,e),(a,b,c,d,e)) + template + Method((Obj* obj,void (Obj::*method)(A,B,C,D),A a,B b,C c,D d),(mFile,id,a,b,c,d),(a,b,c,d)) + template + Method((Obj* obj,void (Obj::*method)(A,B,C),A a,B b,C c),(mFile,id,a,b,c),(a,b,c)) + template + Method((Obj* obj,void (Obj::*method)(A,B),A a,B b),(mFile,id,a,b),(a,b)) + template + Method((Obj* obj,void (Obj::*method)(A),A a),(mFile,id,a),(a)) + template + Method((Obj* obj,void (Obj::*method)()),(mFile,id),()) + + #undef Method + + /// Write data into the journal. Non-deterministic data can be stored + /// into the journal for reading during playback. The function + /// returns true if the journal is record mode. + #define Method(Arg1,Arg2) \ + static inline bool Writes Arg1 { \ + if (_State == RecordState) { \ + _start(); IOHelper::writes Arg2; _finish(); \ + return true; \ + } \ + return false; \ + } + + template + Method((A& a,B& b,C& c,D& d,E& e, F& f, G& g, H& h, I& i,J& j),(mFile,a,b,c,d,e,f,g,h,i,j)); + template + Method((A& a,B& b,C& c,D& d,E& e, F& f, G& g, H& h, I& i),(mFile,a,b,c,d,e,f,g,h,i)); + template + Method((A& a,B& b,C& c,D& d,E& e, F& f, G& g, H& h),(mFile,a,b,c,d,e,f,g,h)); + template + Method((A& a,B& b,C& c,D& d,E& e, F& f, G& g),(mFile,a,b,c,d,e,f,g)); + template + Method((A& a,B& b,C& c,D& d,E& e, F& f),(mFile,a,b,c,d,e,f)); + template + Method((A& a,B& b,C& c,D& d,E& e),(mFile,a,b,c,d,e)); + template + Method((A& a,B& b,C& c,D& d),(mFile,a,b,c,d)); + template + Method((A& a,B& b,C& c),(mFile,a,b,c)); + template + Method((A& a,B& b),(mFile,a,b)); + template + Method((A& a),(mFile,a)); + #undef Method + + /// Read data from the journal. Read non-deterministic data stored + /// during the recording phase. The function returns true if the + /// journal is play mode. + #define Method(Arg1,Arg2) \ + static inline bool Reads Arg1 { \ + if (_State == PlayState) { \ + _start(); IOHelper::reads Arg2; _finish(); \ + return true; \ + } \ + return false; \ + } + + template + Method((A& a,B& b,C& c,D& d,E& e,F& f,G& g, H& h, I& i, J& j),(mFile,a,b,c,d,e,f,g,h,i,j)); + template + Method((A& a,B& b,C& c,D& d,E& e,F& f,G& g, H& h, I& i),(mFile,a,b,c,d,e,f,g,h,i)); + template + Method((A& a,B& b,C& c,D& d,E& e,F& f,G& g, H& h),(mFile,a,b,c,d,e,f,g,h)); + template + Method((A& a,B& b,C& c,D& d,E& e,F& f,G& g),(mFile,a,b,c,d,e,f,g)); + template + Method((A& a,B& b,C& c,D& d,E& e,F& f),(mFile,a,b,c,d,e,f)); + template + Method((A& a,B& b,C& c,D& d,E& e),(mFile,a,b,c,d,e)); + template + Method((A& a,B& b,C& c,D& d),(mFile,a,b,c,d)); + template + Method((A& a,B& b,C& c),(mFile,a,b,c)); + template + Method((A& a,B& b),(mFile,a,b)); + template + Method((A& a),(mFile,a)); + + #undef Method +}; + +#endif diff --git a/Engine/source/core/util/journal/journaledSignal.h b/Engine/source/core/util/journal/journaledSignal.h new file mode 100644 index 000000000..99843fcf5 --- /dev/null +++ b/Engine/source/core/util/journal/journaledSignal.h @@ -0,0 +1,334 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _JOURNALEDSIGNAL_H_ +#define _JOURNALEDSIGNAL_H_ + +#ifndef _UTIL_JOURNAL_JOURNAL_H_ +#include "core/util/journal/journal.h" +#endif +#ifndef _TSIGNAL_H_ +#include "core/util/tSignal.h" +#endif + +template class JournaledSignal; + + +/// A specialized signal object for journaling input. +/// @see Journal +template<> +class JournaledSignal : public Signal +{ + typedef Signal Parent; + +public: + JournaledSignal() + { + Journal::DeclareFunction((Parent*)this, &Parent::trigger); + } + + ~JournaledSignal() + { + Journal::RemoveFunction((Parent*)this, &Parent::trigger); + } + + /// Fires off the bound delegates. + /// @see Journal::Call + void trigger() + { + Journal::Call((Parent*)this, &Parent::trigger); + } +}; + +/// @copydoc JournaledSignal +template +class JournaledSignal : public Signal +{ + typedef Signal Parent; + +public: + JournaledSignal() + { + Journal::DeclareFunction((Parent*)this, &Parent::trigger); + } + + ~JournaledSignal() + { + Journal::RemoveFunction((Parent*)this, &Parent::trigger); + } + + void trigger(A a) + { + Journal::Call((Parent*)this, &Parent::trigger, a); + } +}; + +/// @copydoc JournaledSignal +template +class JournaledSignal : public Signal +{ + typedef Signal Parent; + +public: + JournaledSignal() + { + Journal::DeclareFunction((Parent*)this, &Parent::trigger); + } + + ~JournaledSignal() + { + Journal::RemoveFunction((Parent*)this, &Parent::trigger); + } + + void trigger(A a, B b) + { + Journal::Call((Parent*)this, &Parent::trigger, a, b); + } +}; + +/// @copydoc JournaledSignal +template +class JournaledSignal : public Signal +{ + typedef Signal Parent; + +public: + JournaledSignal() + { + Journal::DeclareFunction((Parent*)this, &Parent::trigger); + } + + ~JournaledSignal() + { + Journal::RemoveFunction((Parent*)this, &Parent::trigger); + } + + void trigger(A a, B b, C c) + { + Journal::Call((Parent*)this, &Parent::trigger, a, b, c); + } +}; + +/// @copydoc JournaledSignal +template +class JournaledSignal : public Signal +{ + typedef Signal Parent; + +public: + JournaledSignal() + { + Journal::DeclareFunction((Parent*)this, &Parent::trigger); + } + + ~JournaledSignal() + { + Journal::RemoveFunction((Parent*)this, &Parent::trigger); + } + + void trigger(A a, B b, C c, D d) + { + Journal::Call((Parent*)this, &Parent::trigger, a, b, c, d); + } +}; + +/// @copydoc JournaledSignal +template +class JournaledSignal : public Signal +{ + typedef Signal Parent; + +public: + JournaledSignal() + { + Journal::DeclareFunction((Parent*)this, &Parent::trigger); + } + + ~JournaledSignal() + { + Journal::RemoveFunction((Parent*)this, &Parent::trigger); + } + + void trigger(A a, B b, C c, D d, E e) + { + Journal::Call((Parent*)this, &Parent::trigger, a, b, c, d, e); + } +}; + +/// @copydoc JournaledSignal +template +class JournaledSignal : public Signal +{ + typedef Signal Parent; + +public: + JournaledSignal() + { + Journal::DeclareFunction((Parent*)this, &Parent::trigger); + } + + ~JournaledSignal() + { + Journal::RemoveFunction((Parent*)this, &Parent::trigger); + } + + void trigger(A a, B b, C c, D d, E e, F f) + { + Journal::Call((Parent*)this, &Parent::trigger, a, b, c, d, e, f); + } +}; + +/// @copydoc JournaledSignal +template +class JournaledSignal : public Signal +{ + typedef Signal Parent; + +public: + JournaledSignal() + { + Journal::DeclareFunction((Parent*)this, &Parent::trigger); + } + + ~JournaledSignal() + { + Journal::RemoveFunction((Parent*)this, &Parent::trigger); + } + + void trigger(A a, B b, C c, D d, E e, F f, G g) + { + Journal::Call((Parent*)this, &Parent::trigger, a, b, c, d, e, f, g); + } +}; + +/// @copydoc JournaledSignal +template +class JournaledSignal : public Signal +{ + typedef Signal Parent; + +public: + JournaledSignal() + { + Journal::DeclareFunction((Parent*)this, &Parent::trigger); + } + + ~JournaledSignal() + { + Journal::RemoveFunction((Parent*)this, &Parent::trigger); + } + + void trigger(A a, B b, C c, D d, E e, F f, G g, H h) + { + Journal::Call((Parent*)this, &Parent::trigger, a, b, c, d, e, f, g, h); + } +}; + + +//----------------------------------------------------------------------------- +// Common event callbacks definitions +enum InputModifier { + IM_LALT = (1 << 1), + IM_RALT = (1 << 2), + IM_LSHIFT = (1 << 3), + IM_RSHIFT = (1 << 4), + IM_LCTRL = (1 << 5), + IM_RCTRL = (1 << 6), + IM_LOPT = (1 << 7), + IM_ROPT = (1 << 8), + IM_ALT = IM_LALT | IM_RALT, + IM_SHIFT = IM_LSHIFT | IM_RSHIFT, + IM_CTRL = IM_LCTRL | IM_RCTRL, + IM_OPT = IM_LOPT | IM_ROPT, +}; + +enum InputAction { + IA_MAKE = (1 << 0), + IA_BREAK = (1 << 1), + IA_REPEAT = (1 << 2), + IA_MOVE = (1 << 3), + IA_DELTA = (1 << 4), + IA_BUTTON = (1 << 5), +}; + +enum ApplicationMessage { + Quit, + WindowOpen, ///< Window opened + WindowClose, ///< Window closed. + WindowShown, ///< Window has been shown on screen + WindowHidden, ///< Window has become hidden + WindowDestroy, ///< Window was destroyed. + GainCapture, ///< Window will capture all input + LoseCapture, ///< Window will no longer capture all input + GainFocus, ///< Application gains focus + LoseFocus, ///< Application loses focus + DisplayChange, ///< Desktop Display mode has changed + GainScreen, ///< Window will acquire lock on the full screen + LoseScreen, ///< Window has released lock on the full screen + Timer, +}; + +typedef U32 WindowId; + +/// void event() +typedef JournaledSignal IdleEvent; + +/// void event(WindowId,U32 modifier,S32 x,S32 y, bool isRelative) +typedef JournaledSignal MouseEvent; + +/// void event(WindowId,U32 modifier,S32 wheelDeltaX, S32 wheelDeltaY) +typedef JournaledSignal MouseWheelEvent; + +/// void event(WindowId,U32 modifier,U32 action,U16 key) +typedef JournaledSignal KeyEvent; + +/// void event(WindowId,U32 modifier,U16 key) +typedef JournaledSignal CharEvent; + +/// void event(WindowId,U32 modifier,U32 action,U16 button) +typedef JournaledSignal ButtonEvent; + +/// void event(WindowId,U32 modifier,U32 action,U32 axis,F32 value) +typedef JournaledSignal LinearEvent; + +/// void event(WindowId,U32 modifier,F32 value) +typedef JournaledSignal PovEvent; + +/// void event(WindowId,InputAppMessage) +typedef JournaledSignal AppEvent; + +/// void event(WindowId) +typedef JournaledSignal DisplayEvent; + +/// void event(WindowId, S32 width, S32 height) +typedef JournaledSignal ResizeEvent; + +/// void event(S32 timeDelta) +typedef JournaledSignal TimeManagerEvent; + +// void event(U32 deviceInst,F32 fValue, U16 deviceType, U16 objType, U16 ascii, U16 objInst, U8 action, U8 modifier) +typedef JournaledSignal InputEvent; + +/// void event(U32 popupGUID, U32 commandID, bool& returnValue) +typedef JournaledSignal PopupMenuEvent; + +#endif diff --git a/Engine/source/core/util/journal/process.cpp b/Engine/source/core/util/journal/process.cpp new file mode 100644 index 000000000..a2520337d --- /dev/null +++ b/Engine/source/core/util/journal/process.cpp @@ -0,0 +1,110 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/util/journal/process.h" +#include "core/util/journal/journal.h" +#include "core/module.h" + + +MODULE_BEGIN( Process ) + + MODULE_INIT + { + Process::init(); + } + + MODULE_SHUTDOWN + { + Process::shutdown(); + } + +MODULE_END; + +static Process* _theOneProcess = NULL; ///< the one instance of the Process class + +//----------------------------------------------------------------------------- + +void Process::requestShutdown() +{ + Process::get()._RequestShutdown = true; +} + +//----------------------------------------------------------------------------- + +Process::Process() +: _RequestShutdown( false ) +{ +} + +Process &Process::get() +{ + struct Cleanup + { + ~Cleanup() + { + if( _theOneProcess ) + delete _theOneProcess; + } + }; + static Cleanup cleanup; + + // NOTE that this function is not thread-safe + // To make it thread safe, use the double-checked locking mechanism for singleton objects + + if ( !_theOneProcess ) + _theOneProcess = new Process; + + return *_theOneProcess; +} + +bool Process::init() +{ + return Process::get()._signalInit.trigger(); +} + +void Process::handleCommandLine(S32 argc, const char **argv) +{ + Process::get()._signalCommandLine.trigger(argc, argv); +} + +bool Process::processEvents() +{ + // Process all the devices. We need to call these even during journal + // playback to ensure that the OS event queues are serviced. + Process::get()._signalProcess.trigger(); + + if (!Process::get()._RequestShutdown) + { + if (Journal::IsPlaying()) + return Journal::PlayNext(); + return true; + } + + // Reset the Quit flag so the function can be called again. + Process::get()._RequestShutdown = false; + return false; +} + +bool Process::shutdown() +{ + return Process::get()._signalShutdown.trigger(); +} diff --git a/Engine/source/core/util/journal/process.h b/Engine/source/core/util/journal/process.h new file mode 100644 index 000000000..19040e747 --- /dev/null +++ b/Engine/source/core/util/journal/process.h @@ -0,0 +1,193 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _UTIL_JOURNAL_PROCESS_H_ +#define _UTIL_JOURNAL_PROCESS_H_ + +#include "platform/platform.h" +#include "core/util/tVector.h" +#include "core/util/delegate.h" +#include "core/util/tSignal.h" + + +#define PROCESS_FIRST_ORDER 0.0f +#define PROCESS_NET_ORDER 0.35f +#define PROCESS_INPUT_ORDER 0.4f +#define PROCESS_DEFAULT_ORDER 0.5f +#define PROCESS_TIME_ORDER 0.75f +#define PROCESS_RENDER_ORDER 0.8f +#define PROCESS_LAST_ORDER 1.0f + +class StandardMainLoop; + + +/// Event generation signal. +/// +/// Objects that generate events need to register a callback with +/// this signal and should only generate events from within the callback. +/// +/// This signal is triggered from the ProcessEvents() method. +class Process +{ +public: + /// Trigger the ProcessSignal and replay journal events. + /// + /// The ProcessSignal is triggered during which all events are generated, + /// journaled, and delivered using the EventSignal classes. Event producers should + /// only generate events from within the function they register with ProcessSignal. + /// ProcessSignal is also triggered during event playback, though all new events are + /// thrown away so as not to interfere with journal playback. + /// This function returns false if Process::requestShutdown() has been called, otherwise it + /// returns true. + /// + /// NOTE: This should only be called from main loops - it should really be private, + /// but we need to sort out how to handle the unit test cases + static bool processEvents(); + + /// Ask the processEvents() function to shutdown. + static void requestShutdown(); + + + static void notifyInit(Delegate del, F32 order = PROCESS_DEFAULT_ORDER) + { + get()._signalInit.notify(del,order); + } + + template + static void notifyInit(T func, F32 order = PROCESS_DEFAULT_ORDER) + { + get()._signalInit.notify(func,order); + } + + + static void notifyCommandLine(Delegate del, F32 order = PROCESS_DEFAULT_ORDER) + { + get()._signalCommandLine.notify(del,order); + } + + template + static void notifyCommandLine(T func, F32 order = PROCESS_DEFAULT_ORDER) + { + get()._signalCommandLine.notify(func,order); + } + + + static void notify(Delegate del, F32 order = PROCESS_DEFAULT_ORDER) + { + get()._signalProcess.notify(del,order); + } + + template + static void notify(T func, F32 order = PROCESS_DEFAULT_ORDER) + { + get()._signalProcess.notify(func,order); + } + + template + static void notify(T obj,U func, F32 order = PROCESS_DEFAULT_ORDER) + { + get()._signalProcess.notify(obj,func,order); + } + + + static void remove(Delegate del) + { + get()._signalProcess.remove(del); + } + + template + static void remove(T func) + { + get()._signalProcess.remove(func); + } + + template + static void remove(T obj,U func) + { + get()._signalProcess.remove(obj,func); + } + + + static void notifyShutdown(Delegate del, F32 order = PROCESS_DEFAULT_ORDER) + { + get()._signalShutdown.notify(del,order); + } + + template + static void notifyShutdown(T func, F32 order = PROCESS_DEFAULT_ORDER) + { + get()._signalShutdown.notify(func,order); + } + + /// Trigger the registered init functions + static bool init(); + + /// Trigger the registered shutdown functions + static bool shutdown(); + +private: + friend class StandardMainLoop; + + /// Trigger the registered command line handling functions + static void handleCommandLine(S32 argc, const char **argv); + + /// Private constructor + Process(); + + /// Access method will construct the singleton as necessary + static Process &get(); + + Signal _signalInit; + Signal _signalCommandLine; + Signal _signalProcess; + Signal _signalShutdown; + + bool _RequestShutdown; +}; + +/// Register a command line handling function. +/// +/// To use this, put it as a member variable into your module definition. +class ProcessRegisterCommandLine +{ +public: + template + ProcessRegisterCommandLine( T func, F32 order = PROCESS_DEFAULT_ORDER ) + { + Process::notifyCommandLine( func, order ); + } +}; + +/// Register a processing function +/// +/// To use this, put it as a member variable into your module definition. +class ProcessRegisterProcessing +{ +public: + template + ProcessRegisterProcessing( T func, F32 order = PROCESS_DEFAULT_ORDER ) + { + Process::notify( func, order ); + } +}; + +#endif diff --git a/Engine/source/core/util/journal/test/testJournal.cpp b/Engine/source/core/util/journal/test/testJournal.cpp new file mode 100644 index 000000000..404f7637e --- /dev/null +++ b/Engine/source/core/util/journal/test/testJournal.cpp @@ -0,0 +1,173 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "unit/test.h" +#include "core/util/journal/journaledSignal.h" +#include "core/util/safeDelete.h" + +using namespace UnitTesting; + +CreateUnitTest(TestsJournalRecordAndPlayback, "Journal/Basic") +{ + U32 _lastTriggerValue; + + void triggerReceiver(U16 msg) + { + _lastTriggerValue = msg; + } + + void run() + { + // Reset the last trigger value just in case... + _lastTriggerValue = 0; + + // Set up a journaled signal to test with. + JournaledSignal testEvent; + + testEvent.notify(this, &TestsJournalRecordAndPlayback::triggerReceiver); + + // Initialize journal recording and fire off some events... + Journal::Record("test.jrn"); + + testEvent.trigger(16); + testEvent.trigger(17); + testEvent.trigger(18); + + test(_lastTriggerValue == 18, "Should encounter last triggered value (18)."); + + Journal::Stop(); + + // Clear it... + _lastTriggerValue = 0; + + // and play back - should get same thing. + Journal::Play("test.jrn"); + + // Since we fired 3 events, it should take three loops. + test(Journal::PlayNext(), "Should be two more events."); + test(Journal::PlayNext(), "Should be one more event."); + test(!Journal::PlayNext(), "Should be no more events."); + + test(_lastTriggerValue == 18, "Should encounter last journaled value (18)."); + } +}; + +CreateUnitTest(TestsJournalDynamicSignals, "Journal/DynamicSignals") +{ + typedef JournaledSignal EventA; + typedef JournaledSignal EventB; + typedef JournaledSignal EventC; + + EventA *dynamicA; + EventB *dynamicB; + EventC *dynamicC; + + // Root, non-dynamic signal receiver. + void receiverRoot(U8 msg) + { + if(msg==1) + { + dynamicA = new EventA(); + dynamicA->notify(this, &TestsJournalDynamicSignals::receiverA); + } + + if(msg==2) + { + dynamicB = new EventB(); + dynamicB->notify(this, &TestsJournalDynamicSignals::receiverB); + } + + if(msg==3) + { + dynamicC = new EventC(); + dynamicC->notify(this, &TestsJournalDynamicSignals::receiverC); + } + } + + U32 recvA, recvB, recvC; + + void receiverA(U32, U16 d) + { + recvA += d; + } + + void receiverB(U8, S8 d) + { + recvB += d; + } + + void receiverC(U32, S32 d) + { + recvC += d; + } + + void run() + { + // Reset our state values. + recvA = recvB = recvC = 0; + + // Set up a signal to start with. + JournaledSignal testEvent; + testEvent.notify(this, &TestsJournalDynamicSignals::receiverRoot); + + // Initialize journal recording and fire off some events... + Journal::Record("test.jrn"); + + testEvent.trigger(1); + dynamicA->trigger(8, 100); + testEvent.trigger(2); + dynamicA->trigger(8, 8); + dynamicB->trigger(9, 'a'); + testEvent.trigger(3); + SAFE_DELETE(dynamicB); // Test a deletion. + dynamicC->trigger(8, 1); + dynamicC->trigger(8, 1); + + // Did we end up with expected values? Check before clearing. + test(recvA == 108, "recvA wasn't 108 - something broken in signals?"); + test(recvB == 'a', "recvB wasn't 'a' - something broken in signals?"); + test(recvC == 2, "recvC wasn't 2 - something broken in signals?"); + + // Reset our state values. + recvA = recvB = recvC = 0; + + // And kill the journal... + Journal::Stop(); + + // Also kill our remaining dynamic signals. + SAFE_DELETE(dynamicA); + SAFE_DELETE(dynamicB); + SAFE_DELETE(dynamicC); + + // Play back - should get same thing. + Journal::Play("test.jrn"); + + // Since we fired 8 events, it should take 7+1=8 loops. + for(S32 i=0; i<7; i++) + test(Journal::PlayNext(), "Should be more events."); + test(!Journal::PlayNext(), "Should be no more events."); + + test(recvA == 108, "recvA wasn't 108 - something broken in journal?"); + test(recvB == 'a', "recvB wasn't 'a' - something broken in journal?"); + test(recvC == 2, "recvC wasn't 2 - something broken in journal?"); + } +}; diff --git a/Engine/source/core/util/journal/test/testProcess.cpp b/Engine/source/core/util/journal/test/testProcess.cpp new file mode 100644 index 000000000..1dc847b34 --- /dev/null +++ b/Engine/source/core/util/journal/test/testProcess.cpp @@ -0,0 +1,56 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "unit/test.h" +#include "core/util/journal/process.h" +#include "core/util/safeDelete.h" + +using namespace UnitTesting; + +CreateUnitTest(TestingProcess, "Journal/Process") +{ + // How many ticks remaining? + U32 _remainingTicks; + + // Callback for process list. + void process() + { + if(_remainingTicks==0) + Process::requestShutdown(); + + _remainingTicks--; + } + + void run() + { + // We'll run 30 ticks, then quit. + _remainingTicks = 30; + + // Register with the process list. + Process::notify(this, &TestingProcess::process); + + // And do 30 notifies, making sure we end on the 30th. + for(S32 i=0; i<30; i++) + test(Process::processEvents(), "Should quit after 30 ProcessEvents() calls - not before!"); + test(!Process::processEvents(), "Should quit after the 30th ProcessEvent() call!"); + } +}; \ No newline at end of file diff --git a/Engine/source/core/util/md5.cpp b/Engine/source/core/util/md5.cpp new file mode 100644 index 000000000..8358d7655 --- /dev/null +++ b/Engine/source/core/util/md5.cpp @@ -0,0 +1,259 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +/* Brutally hacked by John Walker back from ANSI C to K&R (no + prototypes) to maintain the tradition that Netfone will compile + with Sun's original "cc". */ + +#include /* for memcpy() */ +#include "md5.h" + +#ifdef sgi +#define HIGHFIRST +#endif + +#ifdef sun +#define HIGHFIRST +#endif + +#ifndef HIGHFIRST +#define byteReverse(buf, len) /* Nothing */ +#else +/* + * Note: this code is harmless on little-endian machines. + */ +void byteReverse(buf, longs) + unsigned char *buf; unsigned longs; +{ + int t; + do { + t = (int) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(int *) buf = t; + buf += 4; + } while (--longs); +} +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Init( MD5Context* ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Update( MD5Context* ctx, unsigned char* buf, unsigned int len) +{ + int t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((int) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (int *) ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (int *) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Final( unsigned char digest[16], MD5Context* ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (int *) ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((int *) ctx->in)[14] = ctx->bits[0]; + ((int *) ctx->in)[15] = ctx->bits[1]; + + MD5Transform(ctx->buf, (int *) ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void MD5Transform( int buf[4], int in[16]) +{ + register int a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} diff --git a/Engine/source/core/util/md5.h b/Engine/source/core/util/md5.h new file mode 100644 index 000000000..0f57833c5 --- /dev/null +++ b/Engine/source/core/util/md5.h @@ -0,0 +1,18 @@ +#ifndef MD5_H +#define MD5_H + + +struct MD5Context { + int buf[4]; + int bits[2]; + unsigned char in[64]; +}; + +extern void MD5Init(struct MD5Context *ctx); +extern void MD5Update(struct MD5Context *ctx, unsigned char *buf, unsigned len); +extern void MD5Final(unsigned char digest[16], struct MD5Context *ctx); +extern void MD5Transform(int but[4], int in[16]); + +typedef MD5Context MD5_CTX; + +#endif /* !MD5_H */ diff --git a/Engine/source/core/util/namedSingleton.h b/Engine/source/core/util/namedSingleton.h new file mode 100644 index 000000000..2c14812be --- /dev/null +++ b/Engine/source/core/util/namedSingleton.h @@ -0,0 +1,145 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TORQUE_CORE_UTIL_NAMEDSINGLETON_H_ +#define _TORQUE_CORE_UTIL_NAMEDSINGLETON_H_ + +#include "platform/platform.h" +#include "core/util/safeCast.h" +#include "console/console.h" +#include "core/stringTable.h" + +//-------------------------------------------------------------------------- +// StaticNamedSingleton. +//-------------------------------------------------------------------------- + +/// Collection of statically registered named singletons. +/// +/// This class is useful as a mix-in for classes that are supposed to +/// represent a range of named singleton instances from which a specific +/// instance is then selected at run-time. +/// +/// @param T Arbitrary type parameter; identical types will share +/// static data. + +template< class T > +struct StaticNamedSingleton +{ + typedef StaticNamedSingleton This; + + StaticNamedSingleton( const char* name ); + virtual ~StaticNamedSingleton() {} + + const char* getName(); + T* getNext(); + + static T* staticGetFirst(); + static T* staticFindSingleton( const char* name ); + static EnumTable* staticCreateEnumTable(); + static U32 staticGetNumSingletons(); + +private: + const char* mName; + This* mNext; + + static This* smSingletons; +}; + +template< class T > +StaticNamedSingleton< T >* StaticNamedSingleton< T >::smSingletons; + +template< class T > +StaticNamedSingleton< T >::StaticNamedSingleton( const char* name ) + : mName( name ) +{ + mNext = smSingletons; + smSingletons = this; +} + +template< class T > +inline const char* StaticNamedSingleton< T >::getName() +{ + return mName; +} + +template< class T > +inline T* StaticNamedSingleton< T >::getNext() +{ + return static_cast< T* >( mNext ); +} + +template< class T > +T* StaticNamedSingleton< T >::staticGetFirst() +{ + return static_cast< T* >( smSingletons ); +} + +/// Find the instance with the given name. Returns NULL if no such +/// instance exists. + +template< class T > +T* StaticNamedSingleton< T >::staticFindSingleton( const char* name ) +{ + for( This* ptr = smSingletons; ptr != 0; ptr = ptr->mNext ) + if( dStricmp( name, ptr->mName ) == 0 ) + return static_cast< T* >( ptr ); + + return 0; +} + +/// Create a TorqueScript EnumTable that contains all registered +/// instance names. + +template< class T > +EnumTable* StaticNamedSingleton< T >::staticCreateEnumTable() +{ + U32 numSingletons = staticGetNumSingletons(); + + // Create the enums. + + EnumTable::Enums* enums = new EnumTable::Enums[ numSingletons ]; + This* ptr = smSingletons; + for( U32 i = 0; i < numSingletons; ++ i ) + { + enums[ i ].index = i; + enums[ i ].label = StringTable->insert( ptr->getName() ); + + ptr = ptr->mNext; + } + + // Create the table. + + return new EnumTable( numSingletons, enums ); +} + +/// Return the number of registered named singletons. + +template< class T > +U32 StaticNamedSingleton< T >::staticGetNumSingletons() +{ + U32 numSingletons = 0; + for( This* ptr = smSingletons; ptr != 0; ptr = ptr->mNext ) + numSingletons ++; + return numSingletons; +} + +#endif // _TORQUE_CORE_UTIL_NAMEDSINGLETON_H_ diff --git a/Engine/source/core/util/noncopyable.h b/Engine/source/core/util/noncopyable.h new file mode 100644 index 000000000..6518d598a --- /dev/null +++ b/Engine/source/core/util/noncopyable.h @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _CORE_NONCOPYABLE_H_ +#define _CORE_NONCOPYABLE_H_ + +class Noncopyable +{ +protected: + Noncopyable() {} + ~Noncopyable() {} + +private: + Noncopyable(const Noncopyable&); + const Noncopyable& operator=(const Noncopyable&); +}; + +#endif diff --git a/Engine/source/core/util/path.cpp b/Engine/source/core/util/path.cpp new file mode 100644 index 000000000..5bcea08fb --- /dev/null +++ b/Engine/source/core/util/path.cpp @@ -0,0 +1,436 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/util/path.h" + + +namespace Torque +{ + +//----------------------------------------------------------------------------- + +void Path::_split(String name) +{ + S32 pos = 0; + S32 idx = 0; + + // Make sure we have platform separators + name = PathToPlatform(name); + + // root: + idx = name.find(':'); + if (idx >= 0) + { + mRoot = name.substr(0,idx); + pos = idx + 1; + } + else if( name[ 0 ] == '/' ) + { + mRoot = "/"; + } + else + { + mRoot = ""; + } + + // Extract path and strip trailing '/' + idx = name.find('/', 0, String::Right); + if (idx >= pos) + { + int len = idx - pos; + mPath = name.substr(pos,len? len: 1); + mPath = Path::CleanSeparators(mPath); + pos = idx + 1; + } + else + { + mPath = ""; + } + + // file.ext + idx = name.find('.', 0, String::Right); + if (idx >= pos) + { + mFile = name.substr(pos,idx - pos); + mExt = name.substr(idx + 1,name.length() - idx - 1); + } + else + { + mFile = name.substr(pos,name.length() - pos); + mExt = ""; + } +} + +String Path::_join() const +{ + String name; + if( mRoot != '/' ) + name = Path::Join(mRoot, ':', mPath); + else + name = mPath; + name = Path::Join(name, '/', mFile); + name = Path::Join(name, '.', mExt); + return name; +} + +String Path::CleanSeparators(String path) +{ + return path.replace( '\\', '/' ); +} + +String Path::CompressPath(String path) +{ + // Remove "./" and "../" references. A ".." will also result in the + // removal of the proceeding directory. + // Also cleans separators as in CleanSeparators(). + // Assumes there are no trailing "/" + + // Start by cleaning separators + path = Path::CleanSeparators(path); + + U32 src = 0; + U32 dst = 0; + + while (path[src]) + { + if (path[src] == '/' && path[src + 1] == '/') + { + src += 1; + continue; + } + else if (path[src] == '.') + { + if (path[src + 1] == 0) + { + if (dst && path[dst - 1] == '/') + dst--; + src++; + break; + } + else if (path[src + 1] == '/') + { + src += 2; + continue; + } + else if (path[src + 1] == '.') + { + if (path[src + 2] == 0) + { + if (dst && path[dst - 1] == '/') + dst = path.find('/', dst - 1, String::Right); + src += 2; + break; + } + if (dst && path[dst - 1] == '/') + dst = path.find('/', dst - 1, String::Right) + 1; + else + dst += 3; + + src += 3; + continue; + } + } + + if (dst != src) + { + String end = path.substr(src, path.length() - src); + if (dst > 0 && end[(String::SizeType)0] == '/' && path[dst-1] == '/') + end = end.substr(1, end.length() - 1); + path.replace(dst, path.length() - dst, end); + src = dst; + } + else + { + src++; + dst++; + } + } + + if (src - dst) + path.erase(dst, src - dst); + + return path; +} + +Torque::Path Path::MakeRelativePath( const Path &makeRelative, const Path &relativeTo, U32 mode ) +{ + // We need to find the part of makeRelative that starts to diverge from + // relativeTo. We only need to check up to the end of makeRelative or realtiveTo + // (whichever comes first). + U32 minDirCount = getMin(makeRelative.getDirectoryCount(), relativeTo.getDirectoryCount()); + + // Store the index of the directory where we diverge. If we never diverge this + // will end up being the same as the number of directories in makeRelative + U32 divergeDirIdx = 0; + + for (divergeDirIdx = 0; divergeDirIdx < minDirCount; divergeDirIdx++) + { + // If our directories don't match then this is the diverge directory + if (!makeRelative.getDirectory(divergeDirIdx).equal(relativeTo.getDirectory(divergeDirIdx), mode)) + break; + } + + // Get the part of makeRelative's path after it diverged from relativeTo's path + String uniquePath; + + // If we never diverged then divergeDirIdx will be equal to the number of + // directories in makeRelative and this loop will immediately exit + for (U32 i = divergeDirIdx; i < makeRelative.getDirectoryCount(); i++) + uniquePath += makeRelative.getDirectory(i) + "/"; + + // Go ahead and add the full file name + uniquePath += makeRelative.getFullFileName(); + + // Now calculate the relative offset + String offsetPath; + + U32 numOffset = relativeTo.getDirectoryCount() - divergeDirIdx; + + // Push back an "up" for each directory we are offset + for (U32 i = 0; i < numOffset; i++) + offsetPath += "../"; + + return offsetPath + uniquePath; +} + +String Path::Join(const String& a,String::ValueType s,const String& b) +{ + switch (s) + { + case '/': + { + if (b.isEmpty() || (b.length() == 1 && (b.c_str()[0] == '/'))) + return a; + + if (a.isEmpty()) + return b; + + String::ValueType c = a[a.length()-1]; + + if (c == ':' || ((c == '/') ^ (b.c_str()[0] == '/'))) + return a + b; + + if (c == '/' && b.c_str()[0] == '/') + return a.substr(0,a.length() - 1) + b; + break; + } + + case ':': + { + if (a.isEmpty()) + return b; + + if (b.isEmpty()) + return a + ':'; + break; + } + + case '.': + { + if (b.isEmpty()) + return a; + + if (a.isEmpty()) + return '.' + b; + break; + } + + default: + break; + } + + return a + s + b; +} + +bool Path::appendPath( const Path &p ) +{ + mPath = CompressPath(Join(mPath,'/',p.getPath())); + mIsDirtyPath = true; + return true; +} + +const String &Path::getFullFileName() const +{ + if ( mIsDirtyFileName ) + { + mFullFileName = mFile; + + if (mExt.isNotEmpty()) + mFullFileName += '.' + mExt; + mIsDirtyFileName = false; + } + + return mFullFileName; + +} + +const String& Path::getFullPath() const +{ + if ( mIsDirtyPath ) + { + mFullPath = _join(); + mIsDirtyPath = false; + } + + return mFullPath; +} + +String Path::getFullPathWithoutRoot() const +{ + return Torque::Path::Join(getPath(), '/', getFullFileName()); +} + +String Path::getRootAndPath() const +{ + if( mRoot != '/' ) + return Path::Join(mRoot, ':', mPath); + else + return mPath; +} + +const String& Path::setRoot(const String &s) +{ + if ( mRoot != s ) + { + mIsDirtyPath = true; + mRoot = s; + } + + return mRoot; +} + +const String& Path::setPath(const String &s) +{ + String clean = CleanSeparators(s); + + if ( mPath != clean ) + { + mIsDirtyPath = true; + mPath = clean; + } + + return mPath; +} + +const String& Path::setFileName(const String &s) +{ + if ( mFile != s ) + { + mIsDirtyPath = true; + mIsDirtyFileName = true; + mFile = s; + } + + return mFile; +} + +const String& Path::setExtension(const String &s) +{ + if ( mExt != s ) + { + mIsDirtyPath = true; + mIsDirtyFileName = true; + mExt = s; + } + + return mExt; +} + +bool Path::isDirectory() const +{ + return mFile.isEmpty() && mExt.isEmpty(); +} + +bool Path::isRelative() const +{ + return (mPath.isEmpty() || mPath.c_str()[0] != '/'); +} + +bool Path::isAbsolute() const +{ + return (!mPath.isEmpty() && mPath.c_str()[0] == '/'); +} + +U32 Path::getDirectoryCount() const +{ + if (mPath.isEmpty()) + return 0; + + U32 count = 0; + U32 offset = 0; + + if (mPath.c_str()[0] == '/') + offset++; + + while (offset < mPath.length()) + { + if (mPath[offset++] == '/') + count++; + } + + return count + 1; +} + +String Path::getDirectory(U32 count) const +{ + if (mPath.isEmpty()) + return String(); + + U32 offset = 0; + + if (mPath.c_str()[0] == '/') + offset++; + + while (count && offset < mPath.length()) + { + if (mPath[offset++] == '/') + count--; + } + + U32 end = offset; + + while (mPath[end] != '/' && end < mPath.length()) + end++; + + return mPath.substr(offset,end - offset); +} + +//----------------------------------------------------------------------------- + +String PathToPlatform(String file) +{ + if (Path::OsSeparator != '/') + file.replace( Path::OsSeparator, '/' ); + + return file; +} + +String PathToOS(String file) +{ + if (Path::OsSeparator != '/') + file.replace( '/', Path::OsSeparator ); + + return file; +} + +} // Namespace + diff --git a/Engine/source/core/util/path.h b/Engine/source/core/util/path.h new file mode 100644 index 000000000..604b48f76 --- /dev/null +++ b/Engine/source/core/util/path.h @@ -0,0 +1,147 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PATH_H_ +#define _PATH_H_ + +#ifndef _TORQUE_STRING_H_ +#include "core/util/str.h" +#endif + +namespace Torque +{ + +//----------------------------------------------------------------------------- + +/// FileSystem filename representation. +/// Filenames has the following form: "root:path/file.ext" +/// @ingroup UtilString +class Path +{ +public: + enum Separator + { +#if defined(TORQUE_OS_WIN32) || defined(TORQUE_OS_XENON) + OsSeparator = '\\' +#else + OsSeparator = '/' +#endif + }; + + Path() + : mIsDirtyFileName( true ), + mIsDirtyPath( true ) + { + } + + Path( const char *file ) + : mIsDirtyFileName( true ), + mIsDirtyPath( true ) + { + _split(file); + } + + Path( const String &file ) + : mIsDirtyFileName( true ), + mIsDirtyPath( true ) + { + _split(file); + } + + Path& operator = ( const String &file ) { _split(file); mIsDirtyPath = mIsDirtyFileName = true; return *this; } + operator String() const { return getFullPath(); } + + bool operator == (const Path& path) const { return getFullPath().equal(path.getFullPath()); } + bool operator != (const Path& path) const { return !(*this == path); } + + bool isEmpty() const { return getFullPath().isEmpty(); } + + /// Join two path or file name components together. + static String Join(const String&,String::ValueType,const String&); + + /// Replace all '\' with '/' + static String CleanSeparators( String path ); + + /// Remove "." and ".." relative paths. + static String CompressPath( String path ); + + /// Take two paths and return the relative path between them. + static Path MakeRelativePath( const Path &makeRelative, const Path &relativeTo, U32 mode = String::NoCase ); + + const String& getRoot() const { return mRoot; } + const String& getPath() const { return mPath; } + const String& getFileName() const { return mFile; } + const String& getExtension() const { return mExt; } + + const String& getFullFileName() const; + const String& getFullPath() const; + + /// Returns the full file path without the volume root. + String getFullPathWithoutRoot() const; + + /// Returns the root and path. + String getRootAndPath() const; + + const String& setRoot(const String &s); + const String& setPath(const String &s); + const String& setFileName(const String &s); + const String& setExtension(const String &s); + + U32 getDirectoryCount() const; + String getDirectory(U32) const; + + bool isDirectory() const; + bool isRelative() const; + bool isAbsolute() const; + + /// Appends the argument's path component to the object's + /// path component. The object's root, filename and + /// extension are unaffected. + bool appendPath(const Path &path); + +private: + String mRoot; + String mPath; + String mFile; + String mExt; + + mutable String mFullFileName; + mutable String mFullPath; + + mutable bool mIsDirtyFileName; + mutable bool mIsDirtyPath; + + void _split(String name); + String _join() const; +}; + +/// Convert file/path name to use platform standard path separator +///@ingroup VolumeSystem +String PathToPlatform(String file); + +/// Convert file/path name to use OS standard path separator +///@ingroup VolumeSystem +String PathToOS(String file); + +} // Namespace +#endif + diff --git a/Engine/source/core/util/preprocessorHelpers.h b/Engine/source/core/util/preprocessorHelpers.h new file mode 100644 index 000000000..a6bafbb6c --- /dev/null +++ b/Engine/source/core/util/preprocessorHelpers.h @@ -0,0 +1,38 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef TORQUE_CORE_UTIL_PREPROCESSORHELPERS_H_ +#define TORQUE_CORE_UTIL_PREPROCESSORHELPERS_H_ + +/// @defgroup preprocess_helpers Preprocessor Helpers +/// These are some handy preprocessor macros to simplify certain tasks, like +/// preprocessor concatenation that works properly with __LINE__. + +#define _TORQUE_CONCAT(x, y) x ## y + +/// @ingroup preprocess_helpers +/// This command concatenates two tokens in a way that will work with things such +/// as __LINE__. +/// @hideinitializer +#define TORQUE_CONCAT(x, y) _TORQUE_CONCAT(x, y) + +#endif diff --git a/Engine/source/core/util/rawData.h b/Engine/source/core/util/rawData.h new file mode 100644 index 000000000..e23d79a57 --- /dev/null +++ b/Engine/source/core/util/rawData.h @@ -0,0 +1,157 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _RAWDATA_H_ +#define _RAWDATA_H_ + +#ifndef _PLATFORM_H_ +# include "platform/platform.h" +#endif +#ifndef _TYPETRAITS_H_ +# include "platform/typetraits.h" +#endif + + +template< typename T > +class RawDataT +{ + public: + + typedef void Parent; + typedef RawDataT< T > ThisType; + + /// Type of elements in the buffer. + typedef T ValueType; + + /// If true, the structure own the data buffer and will + /// delete[] it on destruction. + bool ownMemory; + + /// The data buffer. + T *data; + + /// Number of elements in the buffer. + U32 size; + + RawDataT() + : ownMemory(false), data(NULL), size(0) + { + } + + RawDataT( T* data, U32 size, bool ownMemory = false ) + : data( data ), size( size ), ownMemory( ownMemory ) {} + + RawDataT(const ThisType& rd) + { + data = rd.data; + size = rd.size; + ownMemory = false; + } + + ~RawDataT() + { + reset(); + } + + void reset() + { + if (ownMemory) + delete [] data; + + data = NULL; + ownMemory = false; + size = 0; + } + + void alloc(const U32 newSize) + { + reset(); + + ownMemory = true; + size = newSize; + data = new ValueType[newSize]; + } + + void operator =(const ThisType& rd) + { + data = rd.data; + size = rd.size; + ownMemory = false; + } + + /// Allocate a RawDataT instance inline with its data elements. + /// + /// @param Self RawDataT instance type; this is a type parameter so this + /// can work with types derived from RawDataT. + template< class Self > + static Self* allocInline( U32 numElements TORQUE_TMM_ARGS_DECL ) + { + const char* file = __FILE__; + U32 line = __LINE__; +#ifndef TORQUE_DISABLE_MEMORY_MANAGER + file = fileName; + line = lineNum; +#endif + Self* inst = ( Self* ) dMalloc_r( sizeof( Self ) + numElements * sizeof( ValueType ), file, line ); + ValueType* data = ( ValueType* ) ( inst + 1 ); + constructArray< ValueType >( data, numElements ); + return constructInPlace< Self >( inst, data, numElements ); + } +}; + +template< typename T > +struct TypeTraits< RawDataT< T >* > : public TypeTraits< typename RawDataT< T >::Parent* > +{ + struct Construct + { + template< typename R > + static R* single( U32 size ) + { + typedef typename TypeTraits< R >::BaseType Type; + return Type::template allocInline< Type >( size TORQUE_TMM_LOC ); + } + }; + struct Destruct + { + template< typename R > + static void single( R* ptr ) + { + destructInPlace( ptr ); + dFree( ptr ); + } + }; +}; + +/// Raw byte buffer. +/// This isn't a typedef to allow forward declarations. +class RawData : public RawDataT< S8 > +{ + public: + + typedef RawDataT< S8 > Parent; + + RawData() {} + RawData( S8* data, U32 size, bool ownMemory = false ) + : Parent( data, size, ownMemory ) {} +}; + +#endif // _RAWDATA_H_ diff --git a/Engine/source/core/util/refBase.h b/Engine/source/core/util/refBase.h new file mode 100644 index 000000000..6ef6d9775 --- /dev/null +++ b/Engine/source/core/util/refBase.h @@ -0,0 +1,473 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _REFBASE_H_ +#define _REFBASE_H_ + +#ifndef _PLATFORMASSERT_H_ +# include "platform/platformAssert.h" +#endif +#ifndef _TYPETRAITS_H_ +# include "platform/typetraits.h" +#endif + + +/// Base class for objects which can be weakly referenced +/// (i.e., reference goes away when object is destroyed). +class WeakRefBase +{ +public: + + /// Weak reference to WeakRefBase. + class WeakReference + { + public: + + WeakRefBase * get() { return mObject; } + void incRefCount() { mRefCount++; } + void decRefCount() + { + AssertFatal( mRefCount > 0, "WeakReference - decrementing count of zero!" ); + if (--mRefCount==0) + delete this; + } + U32 getRefCount() { return mRefCount; } + + private: + + friend class WeakRefBase; + WeakReference(WeakRefBase *object) { mObject = object; mRefCount = 0; } + ~WeakReference() { AssertFatal(mObject==NULL, "Deleting weak reference which still points at an object."); } + + // Object we reference + WeakRefBase *mObject; + + // reference count for this structure (not WeakObjectRef itself) + U32 mRefCount; + }; + +public: + WeakRefBase() { mReference = NULL; } + virtual ~WeakRefBase() { clearWeakReferences(); } + + WeakReference * getWeakReference(); + +protected: + void clearWeakReferences(); + +private: + WeakReference * mReference; +}; + +template< typename T > class SimObjectPtr; + +/// Weak reference pointer class. +/// Instances of this template class can be used as pointers to +/// instances of WeakRefBase and its subclasses. +/// When the object referenced by a WeakRefPtr instance is deleted, +/// the pointer to the object is set to NULL in the WeakRefPtr instance. +template class WeakRefPtr +{ +public: + WeakRefPtr() { mReference = NULL; } + WeakRefPtr(T *ptr) { mReference = NULL; set(ptr); } + WeakRefPtr(const WeakRefPtr & ref) { mReference = NULL; set(ref.mReference); } + + ~WeakRefPtr() { set((WeakRefBase::WeakReference*)NULL); } + + WeakRefPtr& operator=(const WeakRefPtr& ref) + { + set(ref.mReference); + return *this; + } + WeakRefPtr& operator=(T *ptr) + { + set(ptr); + return *this; + } + + /// Returns true if the pointer is not set. + bool isNull() const { return mReference == NULL || mReference->get() == NULL; } + + /// Returns true if the pointer is set. + bool isValid() const { return mReference && mReference->get(); } + + T* operator->() const { return getPointer(); } + T& operator*() const { return *getPointer(); } + operator T*() const { return getPointer(); } + + /// Returns the pointer. + T* getPointer() const { return mReference ? ( T* ) mReference->get() : NULL; } + +protected: + void set(WeakRefBase::WeakReference * ref) + { + if (mReference) + mReference->decRefCount(); + mReference = NULL; + if (ref) + { + mReference = ref; + mReference->incRefCount(); + } + } + + void set(T * obj) { set(obj ? obj->getWeakReference() : (WeakRefBase::WeakReference *)NULL); } +private: + template< typename > friend class SimObjectPtr; + WeakRefBase::WeakReference * mReference; +}; + +/// Union of an arbitrary type with a WeakRefBase. The exposed type will +/// remain accessible so long as the WeakRefBase is not cleared. Once +/// it is cleared, accessing the exposed type will result in a NULL pointer. +template +class WeakRefUnion +{ + typedef WeakRefUnion Union; + +public: + WeakRefUnion() : mPtr(NULL) {} + WeakRefUnion(const WeakRefPtr & ref, ExposedType * ptr) : mPtr(NULL) { set(ref, ptr); } + WeakRefUnion(const Union & lock) : mPtr(NULL) { *this = lock; } + WeakRefUnion(WeakRefBase * ref) : mPtr(NULL) { set(ref, dynamic_cast(ref)); } + ~WeakRefUnion() { mWeakReference = NULL; } + + Union & operator=(const Union & ptr) + { + set(ptr.mWeakReference, ptr.getPointer()); + return *this; + } + + void set(const WeakRefPtr & ref, ExposedType * ptr) + { + mWeakReference = ref; + mPtr = ptr; + } + + bool operator == (const ExposedType * t ) const { return getPointer() == t; } + bool operator != (const ExposedType * t ) const { return getPointer() != t; } + bool operator == (const Union &t ) const { return getPointer() == t.getPointer(); } + bool operator != (const Union &t ) const { return getPointer() != t.getPointer(); } + bool isNull() const { return mWeakReference.isNull() || !mPtr; } + + ExposedType* getPointer() const { return !mWeakReference.isNull() ? mPtr : NULL; } + ExposedType* operator->() const { return getPointer(); } + ExposedType& operator*() const { return *getPointer(); } + operator ExposedType*() const { return getPointer(); } + + WeakRefPtr getRef() const { return mWeakReference; } + +private: + WeakRefPtr mWeakReference; + ExposedType * mPtr; +}; + +/// Base class for objects which can be strongly referenced +/// (i.e., as long as reference exists, object will exist, +/// when all strong references go away, object is destroyed). +class StrongRefBase : public WeakRefBase +{ + friend class StrongObjectRef; + +public: + StrongRefBase() { mRefCount = 0; } + + U32 getRefCount() const { return mRefCount; } + + /// object destroy self call (from StrongRefPtr). Override if this class has specially allocated memory. + virtual void destroySelf() { delete this; } + + /// Increments the reference count. + void incRefCount() + { + mRefCount++; + } + + /// Decrements the reference count. + void decRefCount() + { + AssertFatal(mRefCount, "Decrementing a reference with refcount 0!"); + if(!--mRefCount) + destroySelf(); + } + +protected: + U32 mRefCount; ///< reference counter for StrongRefPtr objects +}; + +/// Base class for StrongRefBase strong reference pointers. +class StrongObjectRef +{ + +public: + /// Constructor, assigns from the object and increments its reference count if it's not NULL + StrongObjectRef(StrongRefBase *object = NULL) : mObject( object ) + { + mObject = object; + incRef(); + } + + /// Destructor, dereferences the object, if there is one + ~StrongObjectRef() { decRef(); } + + /// Assigns this reference object from an existing StrongRefBase instance + void set(StrongRefBase *object) + { + if( mObject != object ) + { + decRef(); + mObject = object; + incRef(); + } + } + +protected: + StrongRefBase *mObject; ///< the object this RefObjectRef references + + /// increments the reference count on the referenced object + void incRef() + { + if(mObject) + mObject->incRefCount(); + } + + /// decrements the reference count on the referenced object + void decRef() + { + if(mObject) + mObject->decRefCount(); + } +}; + +/// Reference counted object template pointer class +/// Instances of this template class can be used as pointers to +/// instances of StrongRefBase and its subclasses. The object will not +/// be deleted until all of the StrongRefPtr instances pointing to it +/// have been destructed. +template class StrongRefPtr : protected StrongObjectRef +{ +public: + StrongRefPtr() : StrongObjectRef() {} + StrongRefPtr(T *ptr) : StrongObjectRef(ptr) {} + StrongRefPtr(const StrongRefPtr& ref) : StrongObjectRef(ref.mObject) {} + ~StrongRefPtr() {} + + StrongRefPtr& operator=(const StrongRefPtr& ref) + { + set(ref.mObject); + return *this; + } + StrongRefPtr& operator=(T *ptr) + { + set(ptr); + return *this; + } + + bool isNull() const { return mObject == 0; } + bool isValid() const { return mObject != 0; } + T* operator->() const { return getPointer(); } + T& operator*() const { return *getPointer(); } + operator T*() const { return getPointer(); } + T* getPointer() const { return const_cast(static_cast(mObject)); } +}; + +/// Union of an arbitrary type with a StrongRefBase. StrongRefBase will remain locked +/// until the union is destructed. Handy for when the exposed class will +/// become invalid whenever the reference becomes invalid and you want to make sure that doesn't +/// happen. +template +class StrongRefUnion +{ + typedef StrongRefUnion Union; + +public: + StrongRefUnion() : mPtr(NULL) {} + + StrongRefUnion(const StrongRefPtr & ref, ExposedType * ptr) : mPtr(NULL) { set(ref, ptr); } + StrongRefUnion(const Union & lock) : mPtr(NULL) { *this = lock; } + StrongRefUnion(StrongRefBase * ref) : mPtr(NULL) { set(ref, dynamic_cast(ref)); } + + ~StrongRefUnion() { mReference = NULL; } + + Union & operator=(const Union & ptr) + { + set(ptr.mReference, ptr.mPtr); + return *this; + } + + void set(const StrongRefPtr & ref, ExposedType * ptr) + { + mReference = ref; + mPtr = ptr; + } + + bool operator == (const ExposedType * t ) const { return mPtr == t; } + bool operator != (const ExposedType * t ) const { return mPtr != t; } + bool operator == (const Union &t ) const { return mPtr == t.mPtr; } + bool operator != (const Union &t ) const { return mPtr != t.mPtr; } + bool isNull() const { return mReference.isNull() || !mPtr; } + bool isValid() const { return mReference.isValid() && mPtr; } + + ExposedType* operator->() const { return mPtr; } + ExposedType& operator*() const { return *mPtr; } + operator ExposedType*() const { return mPtr; } + ExposedType* getPointer() const { return mPtr; } + + StrongRefPtr getRef() const { return mReference; } + +private: + StrongRefPtr mReference; + ExposedType * mPtr; +}; + +/// This oxymoron is a pointer that reference-counts the referenced +/// object but also NULLs out if the object goes away. +/// +/// This is useful for situations where an object's lifetime is ultimately +/// governed by a superior entity but where individual objects may also die +/// independently of the superior entity. All client code should use +/// StrongWeakRefs that keep object live as long as the superior entity doesn't +/// step in and kill them (in which case, the client code sees the reference +/// disappear). +template< class T > +class StrongWeakRefPtr +{ +public: + StrongWeakRefPtr() : mReference( NULL ) {} + StrongWeakRefPtr( T* ptr ) : mReference( NULL ) { _set( ptr ); } + ~StrongWeakRefPtr() + { + if( mReference ) + { + T* ptr = _get(); + if( ptr ) + ptr->decRefCount(); + + mReference->decRefCount(); + } + } + + bool isNull() const { return ( _get() == NULL ); } + bool operator ==( T* ptr ) const { return ( _get() == ptr ); } + bool operator !=( T* ptr ) const { return ( _get() != ptr ); } + bool operator !() const { return isNull(); } + T* operator ->() const { return _get(); } + T& operator *() const { return *( _get() ); } + operator T*() const { return _get(); } + T* getPointer() const { return _get(); } + + StrongWeakRefPtr& operator =( T* ptr ) + { + _set( ptr ); + return *this; + } + +private: + WeakRefBase::WeakReference* mReference; + + T* _get() const + { + if( mReference ) + return static_cast< T* >( mReference->get() ); + else + return NULL; + } + void _set( T* ptr ) + { + if( mReference ) + { + T* old = _get(); + if( old ) + old->decRefCount(); + + mReference->decRefCount(); + } + + if( ptr ) + { + ptr->incRefCount(); + mReference = ptr->getWeakReference(); + mReference->incRefCount(); + } + else + mReference = NULL; + } +}; + +//--------------------------------------------------------------- + +inline void WeakRefBase::clearWeakReferences() +{ + if (mReference) + { + mReference->mObject = NULL; + mReference->decRefCount(); + mReference = NULL; + } +} + +inline WeakRefBase::WeakReference * WeakRefBase::getWeakReference() +{ + if (!mReference) + { + mReference = new WeakReference(this); + mReference->incRefCount(); + } + return mReference; +} + +//--------------------------------------------------------------- + +template< typename T > +struct TypeTraits< WeakRefPtr< T > > : public _TypeTraits< WeakRefPtr< T > > +{ + typedef typename TypeTraits< T >::BaseType BaseType; +}; +template< typename T > +struct TypeTraits< StrongRefPtr< T > > : public _TypeTraits< StrongRefPtr< T > > +{ + typedef typename TypeTraits< T >::BaseType BaseType; +}; +template< typename T > +struct TypeTraits< StrongWeakRefPtr< T > > : public _TypeTraits< StrongWeakRefPtr< T > > +{ + typedef typename TypeTraits< T >::BaseType BaseType; +}; + +template< typename T > +inline T& Deref( WeakRefPtr< T >& ref ) +{ + return *ref; +} +template< typename T > +inline T& Deref( StrongRefPtr< T >& ref ) +{ + return *ref; +} +template< typename T > +inline T& Deref( StrongWeakRefPtr< T >& ref ) +{ + return *ref; +} + +#endif \ No newline at end of file diff --git a/Engine/source/core/util/rgb2luv.cpp b/Engine/source/core/util/rgb2luv.cpp new file mode 100644 index 000000000..71225f729 --- /dev/null +++ b/Engine/source/core/util/rgb2luv.cpp @@ -0,0 +1,58 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/util/rgb2luv.h" + +#include "core/util/rgb2xyz.h" +#include "math/mPoint3.h" +#include "math/mPoint2.h" + + +namespace ConvertRGB +{ + +ColorF toLUV( const ColorF &rgbColor ) +{ + static const Point3F scXYZLUVDot( 1.0f, 15.0f, 3.0f ); + static const Point2F sc49( 4.0f, 9.0f ); + + ColorF xyzColor = ConvertRGB::toXYZ( rgbColor ); + + const Point2F &xyz_xy = *((Point2F *)&xyzColor); + + Point2F uvColor = sc49; + uvColor.convolve( xyz_xy ); + uvColor /= mDot( *(Point3F *)&xyzColor, scXYZLUVDot ); + + return ColorF( uvColor.x, uvColor.y, xyzColor.green, rgbColor.alpha ); +} + +ColorF toLUVScaled( const ColorF &rgbColor ) +{ + ColorF luvColor = toLUV( rgbColor ); + luvColor.red /= 0.62f; + luvColor.green /= 0.62f; + return luvColor; +} + +} diff --git a/Engine/source/core/util/rgb2luv.h b/Engine/source/core/util/rgb2luv.h new file mode 100644 index 000000000..9bb20bd65 --- /dev/null +++ b/Engine/source/core/util/rgb2luv.h @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _RGB2LUV_H_ +#define _RGB2LUV_H_ + +#ifndef _COLOR_H_ +#include "core/color.h" +#endif + +namespace ConvertRGB +{ + ColorF toLUV( const ColorF &rgbColor ); + ColorF toLUVScaled( const ColorF &rgbColor ); + ColorF fromLUV( const ColorF &luvColor ); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/core/util/rgb2xyz.cpp b/Engine/source/core/util/rgb2xyz.cpp new file mode 100644 index 000000000..e402971d0 --- /dev/null +++ b/Engine/source/core/util/rgb2xyz.cpp @@ -0,0 +1,67 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/util/rgb2xyz.h" + +#include "math/mMatrix.h" + + +namespace ConvertRGB +{ + +// http://www.w3.org/Graphics/Color/sRGB +static const F32 scRGB2XYZ[] = +{ + 0.4124f, 0.3576f, 0.1805f, 0.0f, + 0.2126f, 0.7152f, 0.0722f, 0.0f, + 0.0193f, 0.1192f, 0.9505f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, +}; + +static const F32 scXYZ2RGB[] = +{ + 3.2410f, -1.5374f, -0.4986f, 0.0f, + -0.9692f, 1.8760f, 0.0416f, 0.0f, + 0.0556f, -0.2040f, 1.0570f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, +}; + +ColorF toXYZ( const ColorF &rgbColor ) +{ + const MatrixF &rgb2xyz = *((MatrixF *)scRGB2XYZ); + + ColorF retColor = rgbColor; + rgb2xyz.mul( *(Point4F *)&retColor ); + return retColor; +} + +ColorF fromXYZ( const ColorF &xyzColor ) +{ + const MatrixF &xyz2rgb = *((MatrixF *)scXYZ2RGB); + + ColorF retColor = xyzColor; + xyz2rgb.mul( *(Point4F *)&retColor ); + return retColor; +} + +} diff --git a/Engine/source/core/util/rgb2xyz.h b/Engine/source/core/util/rgb2xyz.h new file mode 100644 index 000000000..f5e9e9c50 --- /dev/null +++ b/Engine/source/core/util/rgb2xyz.h @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _RGB2XYZ_H_ +#define _RGB2XYZ_H_ + +#include "core/color.h" + +namespace ConvertRGB +{ + ColorF toXYZ( const ColorF &rgbColor ); + ColorF fromXYZ( const ColorF &xyzColor ); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/core/util/safeCast.h b/Engine/source/core/util/safeCast.h new file mode 100644 index 000000000..6a82488a7 --- /dev/null +++ b/Engine/source/core/util/safeCast.h @@ -0,0 +1,60 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TORQUE_CORE_UTIL_SAFECAST_H_ +#define _TORQUE_CORE_UTIL_SAFECAST_H_ + +#include "platform/platform.h" + +template< class T, typename I > +inline T* safeCast( I* inPtr ) +{ + if( !inPtr ) + return 0; + else + { + T* outPtr = dynamic_cast< T* >( inPtr ); + AssertFatal( outPtr != 0, "safeCast failed" ); + return outPtr; + } +} + +template<> +inline void* safeCast< void >( void* inPtr ) +{ + return inPtr; +} + +template< class T, typename I > +inline T* safeCastISV( I* inPtr ) +{ + if( !inPtr ) + return 0; + else + { + T* outPtr = dynamic_cast< T* >( inPtr ); + AssertISV( outPtr != 0, "safeCast failed" ); + return outPtr; + } +} + +#endif // _TORQUE_CORE_UTIL_SAFECAST_H_ diff --git a/Engine/source/core/util/safeDelete.h b/Engine/source/core/util/safeDelete.h new file mode 100644 index 000000000..5b5410416 --- /dev/null +++ b/Engine/source/core/util/safeDelete.h @@ -0,0 +1,94 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TORQUE_SAFEDELETE_H_ +#define _TORQUE_SAFEDELETE_H_ + +/// @addtogroup utility_macros Utility Macros +// @{ + +#undef SAFE_DELETE + +//----------------------------------------------------------------------------- +/// @brief Safely delete an object and set the pointer to NULL +/// +/// @param a Object to delete +/// @see #SAFE_DELETE_ARRAY(), #SAFE_DELETE_OBJECT(), #SAFE_FREE(), #SAFE_FREE_REFERENCE() +//----------------------------------------------------------------------------- +#define SAFE_DELETE(a) {delete (a); (a) = NULL; } + +#undef SAFE_DELETE_ARRAY + +//----------------------------------------------------------------------------- +/// @brief Safely delete an array and set the pointer to NULL +/// +/// @param a Array to delete +/// @see #SAFE_DELETE(), #SAFE_DELETE_OBJECT(), #SAFE_FREE(), #SAFE_FREE_REFERENCE() +//----------------------------------------------------------------------------- +#define SAFE_DELETE_ARRAY(a) { delete [] (a); (a) = NULL; } + +#undef SAFE_DELETE_OBJECT + +//----------------------------------------------------------------------------- +/// @brief Safely delete a SimObject and set the pointer to NULL +/// +/// @param a Object to delete +/// @see #SAFE_DELETE_ARRAY(), #SAFE_DELETE(), #SAFE_FREE(), #SAFE_FREE_REFERENCE() +//----------------------------------------------------------------------------- +#define SAFE_DELETE_OBJECT(a) { if( (a) != NULL ) (a)->deleteObject(); (a) = NULL; } + +#undef SAFE_FREE + +//----------------------------------------------------------------------------- +/// @brief Safely free memory and set the pointer to NULL +/// +/// @param a Pointer to memory to free +/// @see #SAFE_DELETE_ARRAY(), #SAFE_DELETE_OBJECT(), #SAFE_DELETE(), #SAFE_FREE_REFERENCE() +//----------------------------------------------------------------------------- +#define SAFE_FREE(a) { if( (a) != NULL ) dFree ((void *)a); (a) = NULL; } + +// CodeReview: Is the NULL conditional needed? [5/14/2007 Pat] + +#undef SAFE_FREE_REFERENCE + +//----------------------------------------------------------------------------- +/// @brief Safely free a reference to a Message and set the pointer to NULL +/// +/// @param a Pointer to message to free +/// @see #SAFE_DELETE_ARRAY(), #SAFE_DELETE_OBJECT(), #SAFE_FREE(), #SAFE_DELETE() +//----------------------------------------------------------------------------- +#define SAFE_FREE_REFERENCE(a) { if((a) != NULL) (a)->freeReference(); (a) = NULL; } + +#undef SAFE_DELETE_MESSAGE + +//----------------------------------------------------------------------------- +/// @brief Synonym for SAFE_FREE_REFERENCE() +/// +/// @param a Object to delete +/// @see #SAFE_DELETE(), #SAFE_DELETE_ARRAY(), #SAFE_DELETE_OBJECT(), #SAFE_FREE(), #SAFE_FREE_REFERENCE() +//----------------------------------------------------------------------------- +#define SAFE_DELETE_MESSAGE SAFE_FREE_REFERENCE + +// @} + +#endif // _TORQUE_SAFEDELETE_H_ + diff --git a/Engine/source/core/util/safeRelease.h b/Engine/source/core/util/safeRelease.h new file mode 100644 index 000000000..88dd96a6f --- /dev/null +++ b/Engine/source/core/util/safeRelease.h @@ -0,0 +1,25 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef SAFE_RELEASE +# define SAFE_RELEASE(x) if( x != NULL ) { x->Release(); x = NULL; } +#endif \ No newline at end of file diff --git a/Engine/source/core/util/str.cpp b/Engine/source/core/util/str.cpp new file mode 100644 index 000000000..9a89ef2a1 --- /dev/null +++ b/Engine/source/core/util/str.cpp @@ -0,0 +1,1622 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include +#include + +#include "platform/platform.h" + +// Sigh... guess what compiler needs this... +namespace DictHash { U32 hash( String::StringData* ); } +namespace KeyCmp +{ + template< typename Key > bool equals( const Key&, const Key& ); + template<> bool equals<>( String::StringData* const&, String::StringData* const& ); +} + +#include "core/util/str.h" +#include "core/util/tDictionary.h" +#include "core/strings/stringFunctions.h" +#include "core/strings/unicode.h" +#include "core/util/hashFunction.h" +#include "core/util/autoPtr.h" +#include "core/util/tVector.h" +#include "core/dataChunker.h" +#include "console/console.h" + +#include "math/mMathFn.h" + +#include "platform/platform.h" +#include "platform/profiler.h" +#include "platform/platformIntrinsics.h" +#include "platform/threads/mutex.h" + +#ifndef TORQUE_DISABLE_MEMORY_MANAGER +# undef new +#else +# define _new new +#endif + +const String::SizeType String::NPos = U32(~0); +const String String::EmptyString; + +/// A delete policy for the AutoPtr class +struct DeleteString +{ + template + static void destroy(T *ptr) { dFree(ptr); } +}; + + +//----------------------------------------------------------------------------- + +/// Search for a character. +/// Search for the position of the needle in the haystack. +/// Default mode is StrCase | StrLeft, mode also accepts StrNoCase and StrRight. +/// If pos is non-zero, then in mode StrLeft the search starts at (hay + pos) and +/// in mode StrRight the search starts at (hay + pos - 1) +/// @return Returns a pointer to the location of the character in the haystack or 0 +static const char* StrFind(const char* hay, char needle, S32 pos, U32 mode) +{ + if (mode & String::Right) + { + // Go to the end first, then search backwards + const char *he = hay; + + if (pos) + { + he += pos - 1; + } + else + { + while (*he) + he++; + } + + if (mode & String::NoCase) + { + needle = dTolower(needle); + + for (; he >= hay; he--) + { + if (dTolower(*he) == needle) + return he; + } + } + else + { + for (; he >= hay; he--) + { + if (*he == needle) + return he; + } + } + return 0; + } + else + { + if (mode & String::NoCase) + { + needle = dTolower(needle); + for (hay += pos; *hay && dTolower(*hay) != needle;) + hay++; + } + else + { + for (hay += pos; *hay && *hay != needle;) + hay++; + } + + return *hay ? hay : 0; + } +} + +/// Search for a StringData. +/// Search for the position of the needle in the haystack. +/// Default mode is StrCase | StrLeft, mode also accepts StrNoCase and StrRight. +/// If pos is non-zero, then in mode StrLeft the search starts at (hay + pos) and +/// in mode StrRight the search starts at (hay + pos - 1) +/// @return Returns a pointer to the StringData in the haystack or 0 +static const char* StrFind(const char* hay, const char* needle, S32 pos, U32 mode) +{ + if (mode & String::Right) + { + const char *he = hay; + + if (pos) + { + he += pos - 1; + } + else + { + while (*he) + he++; + } + + if (mode & String::NoCase) + { + AutoPtr ln(dStrlwr(dStrdup(needle))); + for (; he >= hay; he--) + { + if (dTolower(*he) == *ln) + { + U32 i = 0; + while (ln[i] && ln[i] == dTolower(he[i])) + i++; + if (!ln[i]) + return he; + if (!hay[i]) + return 0; + } + } + } + else + { + for (; he >= hay; he--) + { + if (*he == *needle) + { + U32 i = 0; + while (needle[i] && needle[i] == he[i]) + i++; + if (!needle[i]) + return he; + if (!hay[i]) + return 0; + } + } + } + return 0; + } + else + { + if (mode & String::NoCase) + { + AutoPtr ln(dStrlwr(dStrdup(needle))); + for (hay += pos; *hay; hay++) + { + if (dTolower(*hay) == *ln) + { + U32 i = 0; + while (ln[i] && ln[i] == dTolower(hay[i])) + i++; + if (!ln[i]) + return hay; + if (!hay[i]) + return 0; + } + } + } + else + { + for (hay += pos; *hay; hay++) + { + if (*hay == *needle) + { + U32 i = 0; + while (needle[i] && needle[i] == hay[i]) + i++; + if (!needle[i]) + return hay; + if (!hay[i]) + return 0; + } + } + } + } + + return 0; +} + +//----------------------------------------------------------------------------- + +/// Struct with String::StringData's field so we can initialize +/// this without a constructor. +struct StringDataImpl +{ +#ifdef TORQUE_DEBUG + StringChar* mString; ///< so we can inspect data in a debugger +#endif + + U32 mRefCount; ///< String reference count; string is not refcounted if this is U32_MAX (necessary for thread-safety of interned strings and the empty string). + U32 mLength; ///< String length in bytes excluding null. + mutable U32 mNumChars; ///< Character count; varies from byte count for strings with multi-bytes characters. + mutable U32 mHashCase; ///< case-sensitive hash + mutable U32 mHashNoCase; ///< case-insensitive hash + mutable UTF16* mUTF16; + bool mIsInterned; ///< If true, this string is interned in the string table. + StringChar mData[1]; ///< Start of string data +}; + +/// +class String::StringData : protected StringDataImpl +{ + public: + + /// + StringData( const StringChar* data, bool interned = false ) + { + mRefCount = 1; + mNumChars = U32_MAX; + mHashCase = U32_MAX; + mHashNoCase = U32_MAX; + mUTF16 = NULL; + mIsInterned = interned; + + // mLength is initialized by operator new() + + if( data ) + { + dMemcpy( mData, data, sizeof( StringChar ) * mLength ); + mData[ mLength ] = '\0'; + } + +#ifdef TORQUE_DEBUG + mString = &mData[0]; +#endif + if( mIsInterned ) + mRefCount = U32_MAX; + } + + ~StringData() + { + if( mUTF16 ) + delete [] mUTF16; + } + + void* operator new(size_t size, U32 len); + void* operator new( size_t size, U32 len, DataChunker& chunker ); + void operator delete(void *); + + bool isShared() const + { + return ( mRefCount > 1 ); + } + + void addRef() + { + if( mRefCount != U32_MAX ) + mRefCount ++; + } + + void release() + { + if( mRefCount != U32_MAX ) + { + -- mRefCount; + if( !mRefCount ) + delete this; + } + } + + U32 getLength() const + { + return mLength; + } + + U32 getDataSize() const + { + return ( mLength + 1 ); + } + + U32 getDataSizeUTF16() const + { + return ( mLength * sizeof( UTF16 ) ); + } + + UTF8 operator []( U32 index ) const + { + AssertFatal( index < mLength, "String::StringData::operator []() - index out of range" ); + return mData[ index ]; + } + + UTF8* utf8() + { + return mData; + } + + const UTF8* utf8() const + { + return mData; + } + + UTF16* utf16() const + { + if( !mUTF16 ) + { + // Do this atomically to protect interned strings. + + UTF16* utf16 = convertUTF8toUTF16( mData ); + if( !dCompareAndSwap( mUTF16,( UTF16* ) NULL, utf16 ) ) + delete [] utf16; + } + return mUTF16; + } + + U32 getHashCase() const + { + return mHashCase; + } + + U32 getOrCreateHashCase() const + { + if( mHashCase == U32_MAX ) + { + PROFILE_SCOPE(StringData_getOrCreateHashCase); + mHashCase = Torque::hash((const U8 *)(mData), mLength, 0); + } + return mHashCase; + } + + U32 getHashNoCase() const + { + return mHashNoCase; + } + + U32 getOrCreateHashNoCase() const + { + if( mHashNoCase == U32_MAX) + { + PROFILE_SCOPE(StringData_getOrCreateHashNoCase); + UTF8 *lower = new UTF8[ mLength + 1 ]; + dStrncpy( lower, utf8(), mLength ); + lower[ mLength ] = 0; + dStrlwr( lower ); + mHashNoCase = Torque::hash( (const U8*)lower, mLength, 0 ); + delete [] lower; + } + + return mHashNoCase; + } + + U32 getNumChars() const + { + if( mNumChars == U32_MAX ) + mNumChars = dStrlen( utf16() ); + + return mNumChars; + } + + bool isInterned() const + { + return mIsInterned; + } + + static StringData* Empty() + { + static UTF16 emptyUTF16[ 1 ] = { 0 }; + static StringDataImpl empty = + { + #ifdef TORQUE_DEBUG + "", // mString + #endif + + U32_MAX, // mRefCount + 0, // mLength + 0, // mNumChars + 0, // mHashCase + 0, // mHashNoCase + emptyUTF16, // mUTF16 + true, // mIsInterned + { 0 } // mData + }; + + return ( StringData* ) ∅ + } +}; + +//----------------------------------------------------------------------------- + +namespace DictHash +{ + inline U32 hash( String::StringData* data ) + { + return data->getOrCreateHashCase(); + } +} +namespace KeyCmp +{ + template<> + inline bool equals<>( String::StringData* const& d1, String::StringData* const& d2 ) + { + return ( dStrcmp( d1->utf8(), d2->utf8() ) == 0 ); + } +} + +/// Type for the intern string table. We don't want String instances directly +/// on the table so that destructors don't run when the table is destroyed. This +/// is because we really shouldn't depend on dtor ordering within this file and thus +/// we can't tell whether the intern string memory is freed before or after the +/// table is destroyed. +struct StringInternTable : public HashTable< String::StringData*, String::StringData* > +{ + Mutex mMutex; + DataChunker mChunker; +}; + +static StringInternTable* sInternTable; + +struct KillInternTable +{ + ~KillInternTable() + { + if( sInternTable ) + delete sInternTable; + } +}; +static KillInternTable sKillInternTable; + +//----------------------------------------------------------------------------- + +#ifdef TORQUE_DEBUG + +/// Tracks the number of bytes allocated for strings. +/// @bug This currently does not include UTF16 allocations. +static U32 sgStringMemBytes; + +/// Tracks the number of Strings which are currently instantiated. +static U32 sgStringInstances; + +ConsoleFunction( dumpStringMemStats, void, 1, 1, "()" + "@brief Dumps information about String memory usage\n\n" + "@ingroup Debugging\n" + "@ingroup Strings\n") +{ + Con::printf( "String Data: %i instances, %i bytes", sgStringInstances, sgStringMemBytes ); +} + +#endif + +//----------------------------------------------------------------------------- + +void* String::StringData::operator new( size_t size, U32 len ) +{ + AssertFatal( len != 0, "String::StringData::operator new() - string must not be empty" ); + StringData *str = reinterpret_cast( dMalloc( size + len * sizeof(StringChar) ) ); + + str->mLength = len; + +#ifdef TORQUE_DEBUG + dFetchAndAdd( sgStringMemBytes, size + len * sizeof(StringChar) ); + dFetchAndAdd( sgStringInstances, 1 ); +#endif + + return str; +} + +void String::StringData::operator delete(void *ptr) +{ + StringData* sub = static_cast(ptr); + AssertFatal( sub->mRefCount == 0, "StringData::delete() - invalid refcount" ); + +#ifdef TORQUE_DEBUG + dFetchAndAdd( sgStringMemBytes, U32( -( S32( sizeof( StringData ) + sub->mLength * sizeof(StringChar) ) ) ) ); + dFetchAndAdd( sgStringInstances, U32( -1 ) ); +#endif + + dFree( ptr ); +} + +void* String::StringData::operator new( size_t size, U32 len, DataChunker& chunker ) +{ + AssertFatal( len != 0, "String::StringData::operator new() - string must not be empty" ); + StringData *str = reinterpret_cast( chunker.alloc( size + len * sizeof(StringChar) ) ); + + str->mLength = len; + +#ifdef TORQUE_DEBUG + dFetchAndAdd( sgStringMemBytes, size + len * sizeof(StringChar) ); + dFetchAndAdd( sgStringInstances, 1 ); +#endif + + return str; +} + +//----------------------------------------------------------------------------- + +String::String() +{ + PROFILE_SCOPE(String_default_constructor); + _string = StringData::Empty(); +} + +String::String(const String &str) +{ + PROFILE_SCOPE(String_String_constructor); + _string = str._string; + _string->addRef(); +} + +String::String(const StringChar *str) +{ + PROFILE_SCOPE(String_char_constructor); + if( str && *str ) + { + U32 len = dStrlen(str); + _string = new ( len ) StringData( str ); + } + else + _string = StringData::Empty(); +} + +String::String(const StringChar *str, SizeType len) +{ + PROFILE_SCOPE(String_char_len_constructor); + if (str && *str && len!=0) + { + AssertFatal(len<=dStrlen(str), "String::String: string too short"); + _string = new ( len ) StringData( str ); + } + else + _string = StringData::Empty(); +} + +String::String(const UTF16 *str) +{ + PROFILE_SCOPE(String_UTF16_constructor); + + if( str && str[ 0 ] ) + { + UTF8* utf8 = convertUTF16toUTF8( str ); + U32 len = dStrlen( utf8 ); + _string = new ( len ) StringData( utf8 ); + delete [] utf8; + } + else + _string = StringData::Empty(); +} + +String::~String() +{ + _string->release(); +} + +//----------------------------------------------------------------------------- + +String String::intern() const +{ + if( isInterned() ) + return *this; + + // Create the intern table, if we haven't already. + + if( !sInternTable ) + sInternTable = new StringInternTable; + + // Lock the string table. + + MutexHandle mutex; + mutex.lock( &sInternTable->mMutex ); + + // Lookup. + + StringInternTable::Iterator iter = sInternTable->find( _string ); + if( iter != sInternTable->end() ) + return ( *iter ).value; + + // Create new. + + StringData* data = new ( length(), sInternTable->mChunker ) StringData( c_str(), true ); + iter = sInternTable->insertUnique( data, data ); + + return ( *iter ).value; +} + +//----------------------------------------------------------------------------- + +const StringChar* String::c_str() const +{ + return _string->utf8(); +} + +const UTF16 *String::utf16() const +{ + return _string->utf16(); +} + +String::SizeType String::length() const +{ + return _string->getLength(); +} + +String::SizeType String::size() const +{ + return _string->getDataSize(); +} + +String::SizeType String::numChars() const +{ + return _string->getNumChars(); +} + +bool String::isEmpty() const +{ + return ( _string == StringData::Empty() ); +} + +bool String::isShared() const +{ + return _string->isShared(); +} + +bool String::isSame( const String& str ) const +{ + return ( _string == str._string ); +} + +bool String::isInterned() const +{ + return ( _string->isInterned() ); +} + +U32 String::getHashCaseSensitive() const +{ + return _string->getOrCreateHashCase(); +} + +U32 String::getHashCaseInsensitive() const +{ + return _string->getOrCreateHashNoCase(); +} + +//----------------------------------------------------------------------------- + +String::SizeType String::find(const String &str, SizeType pos, U32 mode) const +{ + return find(str._string->utf8(), pos, mode); +} + +String& String::insert(SizeType pos, const String &str) +{ + return insert(pos, str._string->utf8()); +} + +String& String::replace(SizeType pos, SizeType len, const String &str) +{ + return replace(pos, len, str._string->utf8()); +} + +//----------------------------------------------------------------------------- + +String& String::operator=(StringChar c) +{ + _string->release(); + + _string = new ( 2 ) StringData( 0 ); + _string->utf8()[ 0 ] = c; + _string->utf8()[ 1 ] = '\0'; + + return *this; +} + +String& String::operator+=(StringChar c) +{ + // Append the given string into a new string + U32 len = _string->getLength(); + StringData* sub = new ( len + 1 ) StringData( NULL ); + + copy( sub->utf8(), _string->utf8(), len ); + sub->utf8()[len] = c; + sub->utf8()[len+1] = 0; + + _string->release(); + _string = sub; + + return *this; +} + +//----------------------------------------------------------------------------- + +String& String::operator=(const StringChar *str) +{ + // Protect against self assignment which is not only a + // waste of time, but can also lead to the string being + // freed before it can be reassigned. + if ( _string->utf8() == str ) + return *this; + + _string->release(); + + if (str && *str) + { + U32 len = dStrlen(str); + _string = new ( len ) StringData( str ); + } + else + _string = StringData::Empty(); + + return *this; +} + +String& String::operator=(const String &src) +{ + // Inc src first to avoid assignment to self problems. + src._string->addRef(); + + _string->release(); + _string = src._string; + + return *this; +} + +String& String::operator+=(const StringChar *src) +{ + if( src == NULL && !*src ) + return *this; + + // Append the given string into a new string + U32 lena = _string->getLength(); + U32 lenb = dStrlen(src); + U32 newlen = lena + lenb; + + StringData* sub; + if( !newlen ) + sub = StringData::Empty(); + else + { + sub = new ( newlen ) StringData( NULL ); + + copy(sub->utf8(),_string->utf8(),lena); + copy(sub->utf8() + lena,src,lenb + 1); + } + + _string->release(); + _string = sub; + + return *this; +} + +String& String::operator+=(const String &src) +{ + if( src.isEmpty() ) + return *this; + + // Append the given string into a new string + U32 lena = _string->getLength(); + U32 lenb = src._string->getLength(); + U32 newlen = lena + lenb; + + StringData* sub; + if( !newlen ) + sub = StringData::Empty(); + else + { + sub = new ( newlen ) StringData( NULL ); + + copy(sub->utf8(),_string->utf8(),lena); + copy(sub->utf8() + lena,src._string->utf8(),lenb + 1); + } + + _string->release(); + _string = sub; + + return *this; +} + +//----------------------------------------------------------------------------- + +String operator+(const String &a, const String &b) +{ + PROFILE_SCOPE( String_String_plus_String ); + + if( a.isEmpty() ) + return b; + else if( b.isEmpty() ) + return a; + + U32 lena = a.length(); + U32 lenb = b.length(); + + String::StringData *sub = new ( lena + lenb ) String::StringData( NULL ); + + String::copy(sub->utf8(),a._string->utf8(),lena); + String::copy(sub->utf8() + lena,b._string->utf8(),lenb + 1); + + return String(sub); +} + +String operator+(const String &a, StringChar c) +{ + //PROFILE_SCOPE( String_String_plus_Char ); + + U32 lena = a.length(); + String::StringData *sub = new ( lena + 1 ) String::StringData( NULL ); + + String::copy(sub->utf8(),a._string->utf8(),lena); + + sub->utf8()[lena] = c; + sub->utf8()[lena+1] = 0; + + return String(sub); +} + +String operator+(StringChar c, const String &a) +{ + //PROFILE_SCOPE( String_Char_plus_String ); + + U32 lena = a.length(); + String::StringData *sub = new ( lena + 1 ) String::StringData( NULL ); + + String::copy(sub->utf8() + 1,a._string->utf8(),lena + 1); + sub->utf8()[0] = c; + + return String(sub); +} + +String operator+(const String &a, const StringChar *b) +{ + //PROFILE_SCOPE( String_String_plus_CString ); + + AssertFatal(b,"String:: Invalid null ptr argument"); + + if( a.isEmpty() ) + return String( b ); + + U32 lena = a.length(); + U32 lenb = dStrlen(b); + + if( !lenb ) + return a; + + String::StringData *sub = new ( lena + lenb ) String::StringData( NULL ); + + String::copy(sub->utf8(),a._string->utf8(),lena); + String::copy(sub->utf8() + lena,b,lenb + 1); + + return String(sub); +} + +String operator+(const StringChar *a, const String &b) +{ + //PROFILE_SCOPE( String_CString_plus_String ); + AssertFatal(a,"String:: Invalid null ptr argument"); + + if( b.isEmpty() ) + return String( a ); + + U32 lena = dStrlen(a); + if( !lena ) + return b; + + U32 lenb = b.length(); + + String::StringData* sub = new ( lena + lenb ) String::StringData( NULL ); + + String::copy(sub->utf8(),a,lena); + String::copy(sub->utf8() + lena,b._string->utf8(),lenb + 1); + + return String(sub); +} + +bool String::operator==(const String &str) const +{ + //PROFILE_SCOPE( String_op_equal ); + + if( str._string == _string ) + return true; + else if( str._string->isInterned() && _string->isInterned() ) + return false; + else if( str.length() != length() ) + return false; + else if( str._string->getHashCase() != U32_MAX + && _string->getHashCase() != U32_MAX + && str._string->getHashCase() != _string->getHashCase() ) + return false; + else + return ( dMemcmp( str._string->utf8(), _string->utf8(), _string->getLength() ) == 0 ); +} + +bool String::operator==( StringChar c ) const +{ + if( !_string || _string->getLength() != 1 ) + return false; + else + return ( _string->utf8()[ 0 ] == c ); +} + +bool String::operator<(const String &str) const +{ + return ( dStrnatcmp( _string->utf8(), str._string->utf8() ) < 0 ); +} + +bool String::operator>(const String &str) const +{ + return ( dStrnatcmp( _string->utf8(), str._string->utf8() ) > 0 ); +} + +bool String::operator<=(const String &str) const +{ + return ( dStrnatcmp( _string->utf8(), str._string->utf8() ) <= 0 ); +} + +bool String::operator>=(const String &str) const +{ + return ( dStrnatcmp( _string->utf8(), str._string->utf8() ) >= 0 ); +} + +//----------------------------------------------------------------------------- +// Base functions for string comparison + +S32 String::compare(const StringChar *str, SizeType len, U32 mode) const +{ + PROFILE_SCOPE( String_compare ); + + AssertFatal(str,"String:: Invalid null ptr argument"); + + const StringChar *p1 = _string->utf8(); + const StringChar *p2 = str; + + if (p1 == p2) + return 0; + + if( mode & String::Right ) + { + U32 n = len; + if( n > length() ) + n = length(); + + p1 += length() - n; + p2 += dStrlen( str ) - n; + } + + if (mode & String::NoCase) + { + if (len) + { + for (;--len; p1++,p2++) + { + if (dTolower(*p1) != dTolower(*p2) || !*p1) + break; + } + } + else + { + while (dTolower(*p1) == dTolower(*p2) && *p1) + { + p1++; + p2++; + } + } + + return dTolower(*p1) - dTolower(*p2); + } + + if (len) + return dMemcmp(p1,p2,len); + + while (*p1 == *p2 && *p1) + { + p1++; + p2++; + } + + return *p1 - *p2; +} + +S32 String::compare(const String &str, SizeType len, U32 mode) const +{ + if ( str._string == _string ) + return 0; + + return compare( str.c_str(), len, mode ); +} + +bool String::equal(const String &str, U32 mode) const +{ + if( !mode ) + return ( *this == str ); + else + { + if( _string == str._string ) + return true; + else if( _string->isInterned() && str._string->isInterned() ) + return false; + else if( length() != str.length() ) + return false; + else if( _string->getHashNoCase() != U32_MAX + && str._string->getHashNoCase() != U32_MAX + && _string->getHashNoCase() != str._string->getHashNoCase() ) + return false; + else + return ( compare( str.c_str(), length(), mode ) == 0 ); + } +} + +//----------------------------------------------------------------------------- + +String::SizeType String::find(StringChar c, SizeType pos, U32 mode) const +{ + const StringChar* ptr = StrFind(_string->utf8(),c,pos,mode); + + return ptr? SizeType(ptr - _string->utf8()): NPos; +} + +String::SizeType String::find(const StringChar *str, SizeType pos, U32 mode) const +{ + AssertFatal(str,"String:: Invalid null ptr argument"); + + const StringChar* ptr = StrFind(_string->utf8(),str,pos,mode); + + return ptr? SizeType(ptr - _string->utf8()): NPos; +} + + +//----------------------------------------------------------------------------- + +String& String::insert(SizeType pos, const StringChar *str) +{ + AssertFatal(str,"String:: Invalid null ptr argument"); + + return insert(pos,str,dStrlen(str)); +} + +///@todo review for error checking +String& String::insert(SizeType pos, const StringChar *str, SizeType len) +{ + if( !len ) + return *this; + + AssertFatal( str, "String:: Invalid null ptr argument" ); + + SizeType lena = length(); + AssertFatal((pos <= lena),"Calling String::insert with position greater than length"); + U32 newlen = lena + len; + + StringData *sub; + if( !newlen ) + sub = StringData::Empty(); + else + { + sub = new ( newlen ) StringData( NULL ); + + String::copy(sub->utf8(),_string->utf8(),pos); + String::copy(sub->utf8() + pos,str,len); + String::copy(sub->utf8() + pos + len,_string->utf8() + pos,lena - pos + 1); + } + + _string->release(); + _string = sub; + + return *this; +} + +String& String::erase(SizeType pos, SizeType len) +{ + AssertFatal( len != 0, "String::erase() - Calling String::erase with 0 length" ); + AssertFatal( ( pos + len ) <= length(), "String::erase() - Invalid string region" ); + + if( !len ) + return *this; + + SizeType slen = length(); + U32 newlen = slen - len; + + StringData *sub; + if( !newlen ) + sub = StringData::Empty(); + else + { + sub = new ( newlen ) StringData( NULL ); + + if (pos > 0) + String::copy(sub->utf8(),_string->utf8(),pos); + + String::copy(sub->utf8() + pos, _string->utf8() + pos + len, slen - (pos + len) + 1); + } + + _string->release(); + _string = sub; + + return *this; +} + +///@todo review for error checking +String& String::replace(SizeType pos, SizeType len, const StringChar *str) +{ + AssertFatal( str, "String::replace() - Invalid null ptr argument" ); + AssertFatal( len != 0, "String::replace() - Zero length" ); + AssertFatal( ( pos + len ) <= length(), "String::replace() - Invalid string region" ); + + SizeType slen = length(); + SizeType rlen = dStrlen(str); + + U32 newlen = slen - len + rlen; + StringData *sub; + if( !newlen ) + sub = StringData::Empty(); + else + { + sub = new ( newlen ) StringData( NULL ); + + String::copy(sub->utf8(),_string->utf8(), pos); + String::copy(sub->utf8() + pos,str,rlen); + String::copy(sub->utf8() + pos + rlen,_string->utf8() + pos + len,slen - pos - len + 1); + } + + _string->release(); + _string = sub; + + return *this; +} + +String& String::replace( StringChar c1, StringChar c2 ) +{ + if( isEmpty() ) + return *this; + + // Create the new string lazily so that we don't needlessly + // dup strings when there is nothing to replace. + + StringData* sub = NULL; + bool foundReplacement = false; + + StringChar* c = _string->utf8(); + while( *c ) + { + if( *c == c1 ) + { + if( !foundReplacement ) + { + sub = new ( length() ) StringData( _string->utf8() ); + c = &sub->utf8()[ c - _string->utf8() ]; + foundReplacement = true; + } + + *c = c2; + } + + c++; + } + + if( foundReplacement ) + { + _string->release(); + _string = sub; + } + + return *this; +} + +String &String::replace(const String &s1, const String &s2) +{ + // Find number of occurrences of s1 and + // Calculate length of the new string... + + const U32 &s1len = s1.length(); + const U32 &s2len = s2.length(); + + U32 pos = 0; + Vector indices; + StringChar *walk = _string->utf8(); + + while ( walk ) + { + // Casting away the const... was there a better way? + walk = (StringChar*)StrFind( _string->utf8(), s1.c_str(), pos, Case|Left ); + if ( walk ) + { + pos = SizeType(walk - _string->utf8()); + indices.push_back( pos ); + pos += s1len; + } + } + + // Early-out, no StringDatas found. + if ( indices.size() == 0 ) + return *this; + + U32 newSize = size() - ( indices.size() * s1len ) + ( indices.size() * s2len ); + StringData *sub; + if( newSize == 1 ) + sub = StringData::Empty(); + else + { + sub = new (newSize - 1 ) StringData( NULL ); + + // Now assemble the new string from the pieces of the old... + + // Index into the old string + pos = 0; + // Index into the new string + U32 newPos = 0; + // Used to store a character count to be memcpy'd + U32 copyCharCount = 0; + + for ( U32 i = 0; i < indices.size(); i++ ) + { + const U32 &index = indices[i]; + + // Number of chars (if any) before the next indexed StringData + copyCharCount = index - pos; + + // Copy chars before the StringData if we have any. + if ( copyCharCount > 0 ) + { + dMemcpy( sub->utf8() + newPos, _string->utf8() + pos, copyCharCount * sizeof(StringChar) ); + newPos += copyCharCount; + } + + // Copy over the replacement string. + if ( s2len > 0 ) + dMemcpy( sub->utf8() + newPos, s2._string->utf8(), s2len * sizeof(StringChar) ); + + newPos += s2len; + pos = index + s1len; + } + + // There could be characters left in the original string after the last + // StringData occurrence, which we need to copy now - outside the loop. + copyCharCount = length() - indices.last() - s1len; + if ( copyCharCount != 0 ) + dMemcpy( sub->utf8() + newPos, _string->utf8() + pos, copyCharCount * sizeof(StringChar) ); + + // Null terminate it! + sub->utf8()[newSize-1] = 0; + } + + _string->release(); + _string = sub; + + return *this; +} + +//----------------------------------------------------------------------------- + +String String::substr(SizeType pos, SizeType len) const +{ + //PROFILE_SCOPE( String_substr ); + + AssertFatal( pos <= length(), "String::substr - Invalid position!" ); + + if ( len == -1 ) + len = length() - pos; + + AssertFatal( len + pos <= length(), "String::substr - Invalid length!" ); + + StringData* sub; + if( !len ) + sub = StringData::Empty(); + else + sub = new ( len ) StringData( _string->utf8() + pos ); + + return sub; +} + +//----------------------------------------------------------------------------- + +String String::trim() const +{ + if( isEmpty() ) + return *this; + + const StringChar* start = _string->utf8(); + while( *start && dIsspace( *start ) ) + start ++; + + const StringChar* end = _string->utf8() + length() - 1; + while( end > start && dIsspace( *end ) ) + end --; + end ++; + + const U32 len = end - start; + if( len == length() ) + return *this; + + StringData* sub; + if( !len ) + sub = StringData::Empty(); + else + sub = new ( len ) StringData( start ); + + return sub; +} + +//----------------------------------------------------------------------------- + +String String::expandEscapes() const +{ + char* tmp = ( char* ) dMalloc( length() * 2 + 1 ); // worst-case situation. + expandEscape( tmp, c_str() ); + String str( tmp ); + dFree( tmp ); + return str; +} + +//----------------------------------------------------------------------------- + +String String::collapseEscapes() const +{ + char* tmp = dStrdup( c_str() ); + collapseEscape( tmp ); + String str( tmp ); + dFree( tmp ); + return str; +} + +//----------------------------------------------------------------------------- + +void String::split( const char* delimiter, Vector< String >& outElements ) const +{ + const char* ptr = _string->utf8(); + + const char* start = ptr; + while( *ptr ) + { + // Search for start of delimiter. + + if( *ptr != delimiter[ 0 ] ) + ptr ++; + else + { + // Skip delimiter. + + const char* end = ptr; + const char* del = delimiter; + while( *del && *del == *ptr ) + { + ptr ++; + del ++; + } + + // If we didn't match all of delimiter, + // continue with search. + + if( *del != '\0' ) + continue; + + // Extract component. + + outElements.push_back( String( start, end - start ) ); + start = ptr; + } + } + + // Add rest of string if there is any. + + if( start != ptr ) + outElements.push_back( start ); +} + +//----------------------------------------------------------------------------- + +bool String::startsWith( const char* text ) const +{ + return dStrStartsWith( _string->utf8(), text ); +} + +//----------------------------------------------------------------------------- + +bool String::endsWith( const char* text ) const +{ + return dStrEndsWith( _string->utf8(), text ); +} + +//----------------------------------------------------------------------------- + +void String::copy(StringChar* dst, const StringChar *src, U32 len) +{ + dMemcpy(dst, src, len * sizeof(StringChar)); +} + +//----------------------------------------------------------------------------- + +#if defined(TORQUE_OS_WIN32) || defined(TORQUE_OS_XBOX) || defined(TORQUE_OS_XENON) +// This standard function is not defined when compiling with VC7... +#define vsnprintf _vsnprintf +#endif + +String::StrFormat::~StrFormat() +{ + if( _dynamicBuffer ) + dFree( _dynamicBuffer ); +} + +S32 String::StrFormat::format( const char *format, void *args ) +{ + _len=0; + return formatAppend(format,args); +} + +S32 String::StrFormat::formatAppend( const char *format, void *args ) +{ + // Format into the fixed buffer first. + S32 startLen = _len; + if (_dynamicBuffer == NULL) + { + _len += vsnprintf(_fixedBuffer + _len, sizeof(_fixedBuffer) - _len, format, *(va_list*)args); + if (_len >= 0 && _len < sizeof(_fixedBuffer)) + return _len; + + // Start off the dynamic buffer at twice fixed buffer size + _len = startLen; + _dynamicSize = sizeof(_fixedBuffer) * 2; + _dynamicBuffer = (char*)dMalloc(_dynamicSize); + dMemcpy(_dynamicBuffer, _fixedBuffer, _len + 1); + } + + // Format into the dynamic buffer, if the buffer is not large enough, then + // keep doubling it's size until it is. The buffer is not reallocated + // using reallocate() to avoid unnecessary buffer copying. + _len += vsnprintf(_dynamicBuffer + _len, _dynamicSize - _len, format, *(va_list*)args); + while (_len < 0 || _len >= _dynamicSize) + { + _len = startLen; + _dynamicBuffer = (char*)dRealloc(_dynamicBuffer, _dynamicSize *= 2); + _len += vsnprintf(_dynamicBuffer + _len, _dynamicSize - _len, format, *(va_list*)args); + } + + return _len; +} + +S32 String::StrFormat::append(const char * str, S32 len) +{ + if (_dynamicBuffer == NULL) + { + if (_len+len >= 0 && _len+len < sizeof(_fixedBuffer)) + { + dMemcpy(_fixedBuffer + _len, str, len); + _len += len; + _fixedBuffer[_len] = '\0'; + return _len; + } + + _dynamicSize = sizeof(_fixedBuffer) * 2; + _dynamicBuffer = (char*)dMalloc(_dynamicSize); + dMemcpy(_dynamicBuffer, _fixedBuffer, _len + 1); + } + + S32 newSize = _dynamicSize; + while (newSize < _len+len) + newSize *= 2; + if (newSize != _dynamicSize) + _dynamicBuffer = (char*) dRealloc(_dynamicBuffer, newSize); + _dynamicSize = newSize; + dMemcpy(_dynamicBuffer + _len, str, len); + _len += len; + _dynamicBuffer[_len] = '\0'; + return _len; +} + +S32 String::StrFormat::append(const char * str) +{ + return append(str, dStrlen(str)); +} + +char* String::StrFormat::copy( char *buffer ) const +{ + dMemcpy(buffer, _dynamicBuffer? _dynamicBuffer: _fixedBuffer, _len+1); + return buffer; +} + +//----------------------------------------------------------------------------- + +String String::ToString( bool value ) +{ + static String sTrue = "true"; + static String sFalse = "false"; + + if( value ) + return sTrue; + return sFalse; +} + +String String::ToString(const char *str, ...) +{ + AssertFatal(str,"String:: Invalid null ptr argument"); + + // Use the format object + va_list args; + va_start(args, str); + String ret = VToString(str, args); + va_end(args); + return ret; +} + +String String::VToString(const char* str, void* args) +{ + StrFormat format(str,&args); + + // Copy it into a string + U32 len = format.length(); + StringData* sub; + if( !len ) + sub = StringData::Empty(); + else + { + sub = new ( len ) StringData( NULL ); + + format.copy( sub->utf8() ); + sub->utf8()[ len ] = 0; + } + + return sub; +} + +String String::SpanToString(const char *start, const char *end) +{ + if ( end == start ) + return String(); + + AssertFatal( end > start, "Invalid arguments to String::SpanToString - end is before start" ); + + U32 len = U32(end - start); + StringData* sub = new ( len ) StringData( start ); + + return sub; +} + +String String::ToLower(const String &string) +{ + if ( string.isEmpty() ) + return String(); + + StringData* sub = new ( string.length() ) StringData( string ); + dStrlwr( sub->utf8() ); + + return sub; +} + +String String::ToUpper(const String &string) +{ + if ( string.isEmpty() ) + return String(); + + StringData* sub = new ( string.length() ) StringData( string ); + dStrupr( sub->utf8() ); + + return sub; +} + +String String::GetTrailingNumber(const char* str, S32& number) +{ + // Check for trivial strings + if (!str || !str[0]) + return String::EmptyString; + + // Find the number at the end of the string + String base(str); + const char* p = base.c_str() + base.length() - 1; + + // Ignore trailing whitespace + while ((p != base.c_str()) && dIsspace(*p)) + p--; + + // Need at least one digit! + if (!isdigit(*p)) + return base; + + // Back up to the first non-digit character + while ((p != base.c_str()) && isdigit(*p)) + p--; + + // Convert number => allow negative numbers, treat '_' as '-' for Maya + if ((*p == '-') || (*p == '_')) + number = -dAtoi(p + 1); + else + number = ((p == base.c_str()) ? dAtoi(p) : dAtoi(++p)); + + // Remove space between the name and the number + while ((p > base.c_str()) && dIsspace(*(p-1))) + p--; + + return base.substr(0, p - base.c_str()); +} diff --git a/Engine/source/core/util/str.h b/Engine/source/core/util/str.h new file mode 100644 index 000000000..6b1c443f5 --- /dev/null +++ b/Engine/source/core/util/str.h @@ -0,0 +1,399 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TORQUE_STRING_H_ +#define _TORQUE_STRING_H_ + +#include + +#ifndef _TORQUE_TYPES_H_ +#include "platform/types.h" +#endif + + +template< class T > class Vector; + + +typedef UTF8 StringChar; + + +/// The String class represents a 0-terminated array of characters. +class String +{ +public: + class StringData; + + /// Default mode is case sensitive starting from the left + enum Mode + { + Case = 0, ///< Case sensitive + NoCase = 1, ///< Case insensitive + Left = 0, ///< Start at left end of string + Right = 2, ///< Start at right end of string + }; + + typedef U32 SizeType; + typedef StringChar ValueType; + + static const SizeType NPos; ///< Indicates 'not found' when using find() functions + + /// A predefined empty string. + static const String EmptyString; + + String(); + String(const String &str); + String(const StringChar *str); + String(const StringChar *str, SizeType size); + String(const UTF16 *str); + ~String(); + + const UTF8 *c_str() const; ///< Return the string as a native type + const UTF16 *utf16() const; + const UTF8* utf8() const { return c_str(); } + + SizeType length() const; ///< Returns the length of the string in bytes. + SizeType size() const; ///< Returns the length of the string in bytes including the NULL terminator. + SizeType numChars() const; ///< Returns the length of the string in characters. + bool isEmpty() const; ///< Is this an empty string [""]? + bool isNotEmpty() const { return !isEmpty(); } ///< Is this not an empty string [""]? + + /// Erases all characters in a string. + void clear() { *this = EmptyString; } + + bool isShared() const; ///< Is this string's reference count greater than 1? + bool isSame( const String& str ) const; ///< Return true if both strings refer to the same shared data. + + U32 getHashCaseSensitive() const; ///< Get the case-sensitive hash of the string [only calculates the hash as necessary] + U32 getHashCaseInsensitive() const; ///< Get the case-insensitive hash of the string [only calculates the hash as necessary] + + String& operator=(StringChar); + String& operator+=(StringChar); + String& operator=(const StringChar*); + String& operator+=(const StringChar*); + String& operator=(const String&); + String& operator+=(const String&); + + /** + Compare this string with another. + @param str The string to compare against. + @param len If len is non-zero, then at most len characters are compared. + @param mode Comparison mode. + @return Difference between the first two characters that don't match. + */ + S32 compare(const StringChar *str, SizeType len = 0, U32 mode = Case|Left) const; + S32 compare(const String &str, SizeType len = 0, U32 mode = Case|Left) const; ///< @see compare(const StringChar *, SizeType, U32) const + + /** + Compare two strings for equality. + It will use the string hashes to determine inequality. + @param str The string to compare against. + @param mode Comparison mode - case sensitive or not. + */ + bool equal(const String &str, U32 mode = Case) const; + + SizeType find(StringChar c, SizeType pos = 0, U32 mode = Case|Left) const; + SizeType find(const StringChar *str, SizeType pos = 0, U32 mode = Case|Left) const; + SizeType find(const String &str, SizeType pos = 0, U32 mode = Case|Left) const; + + String &insert(SizeType pos, const StringChar c) { return insert(pos,&c,1); } + String &insert(SizeType pos, const StringChar *str); + String &insert(SizeType pos, const String &str); + String &insert(SizeType pos, const StringChar *str, SizeType len); + + String &erase(SizeType pos, SizeType len); + + String &replace(SizeType pos, SizeType len, const StringChar *str); + String &replace(SizeType pos, SizeType len, const String &str); + + /// Replace all occurrences of character 'c1' with 'c2' + String &replace( StringChar c1, StringChar c2 ); + + /// Replace all occurrences of StringData 's1' with StringData 's2' + String &replace(const String &s1, const String &s2); + + String substr( SizeType pos, SizeType len = -1 ) const; + + /// Remove leading and trailing whitespace. + String trim() const; + + /// Replace all characters that need to be escaped for the string to be a valid string literal with their + /// respective escape sequences. + String expandEscapes() const; + + /// Replace all escape sequences in with their respective character codes. + String collapseEscapes() const; + + /// Split the string into its components separated by the given delimiter. + void split( const char* delimiter, Vector< String >& outElements ) const; + + /// Return true if the string starts with the given text. + bool startsWith( const char* text ) const; + + /// Return true if the string ends with the given text. + bool endsWith( const char* text ) const; + + operator const StringChar*() const { return c_str(); } + + StringChar operator []( U32 i ) const { return c_str()[i]; } + StringChar operator []( S32 i ) const { return c_str()[i]; } + + bool operator==(const String &str) const; + bool operator!=(const String &str) const { return !(*this == str); } + bool operator==( StringChar c ) const; + bool operator!=( StringChar c ) const { return !(*this == c); } + bool operator<(const String &str) const; + bool operator>(const String &str) const; + bool operator<=(const String &str) const; + bool operator>=(const String &str) const; + + friend String operator+(const String &a, StringChar c); + friend String operator+(StringChar c, const String &a); + friend String operator+(const String &a, const StringChar *b); + friend String operator+(const String &a, const String &b); + friend String operator+(const StringChar *a, const String &b); + +public: + /// @name String Utility routines + /// @{ + + static String ToString(const char *format, ...); + static String VToString(const char* format, void* args); + + static String ToString( bool v ); + static inline String ToString( U32 v ) { return ToString( "%u", v ); } + static inline String ToString( S32 v ) { return ToString( "%d", v ); } + static inline String ToString( F32 v ) { return ToString( "%g", v ); } + static inline String ToString( F64 v ) { return ToString( "%Lg", v ); } + + static String SpanToString(const char* start, const char* end); + + static String ToLower(const String &string); + static String ToUpper(const String &string); + + static String GetTrailingNumber(const char* str, S32& number); + + /// @} + + /// @name Interning + /// + /// Interning maps identical strings to unique instances so that equality + /// amounts to simple pointer comparisons. + /// + /// Note that using interned strings within global destructors is not safe + /// as table destruction runs within this phase as well. Uses o interned + /// strings in global destructors is thus dependent on object file ordering. + /// + /// Also, interned strings are not reference-counted. Once interned, a + /// string will persist until shutdown. This is to avoid costly concurrent + /// reference counting that would otherwise be necessary. + /// + /// @{ + + /// Return the interned version of the string. + /// @note Interning is case-sensitive. + String intern() const; + + /// Return true if this string is interned. + bool isInterned() const; + + /// @} + + /** An internal support class for ToString(). + StrFormat manages the formatting of arbitrary length strings. + The class starts with a default internal fixed size buffer and + moves to dynamic allocation from the heap when that is exceeded. + Constructing the class on the stack will result in its most + efficient use. This class is meant to be used as a helper class, + and not for the permanent storage of string data. + @code + char* indexString(U32 index) + { + StrFormat format("Index: %d",index); + char* str = new char[format.size()]; + format.copy(str); + return str; + } + @endcode + */ + class StrFormat + { + public: + StrFormat() + : _dynamicBuffer( NULL ), + _dynamicSize( 0 ), + _len( 0 ) + { + _fixedBuffer[0] = '\0'; + } + + StrFormat(const char *formatStr, void *args) + : _dynamicBuffer( NULL ), + _dynamicSize( 0 ), + _len( 0 ) + { + format(formatStr, args); + } + + ~StrFormat(); + + S32 format( const char *format, void *args ); + S32 formatAppend( const char *format, void *args ); + S32 append(const char * str, S32 len); + S32 append(const char * str); + + String getString() { return String(c_str(),_len); } + + const char * c_str() const { return _dynamicBuffer ? _dynamicBuffer : _fixedBuffer; } + + void reset() + { + _len = 0; + _fixedBuffer[0] = '\0'; + } + + /// Copy the formatted string into the output buffer which must be at least size() characters. + char *copy(char* buffer) const; + + /// Return the length of the formated string (does not include the terminating 0) + U32 length() const { return _len; }; + + public: + char _fixedBuffer[2048]; //< Fixed size buffer + char *_dynamicBuffer; //< Temporary format buffer + U32 _dynamicSize; //< Dynamic buffer size + U32 _len; //< Len of the formatted string + }; + +private: + String(StringData *str) + : _string( str ) {} + + // Generate compile error if operator bool is used. Without this we use + // operator const char *, which is always true...including operator bool + // causes an ambiguous cast compile error. Making it private is simply + // more insurance that it isn't used on different compilers. + // NOTE: disable on GCC since it causes hyper casting to U32 on gcc. +#ifndef TORQUE_COMPILER_GCC + operator const bool() const { return false; } +#endif + + + static void copy(StringChar *dst, const StringChar *src, U32 size); + + StringData *_string; +}; + +// Utility class for formatting strings. +class StringBuilder +{ + protected: + + /// + String::StrFormat mFormat; + + public: + + StringBuilder() {} + + U32 length() const + { + return mFormat.length(); + } + + void copy( char* buffer ) const + { + mFormat.copy( buffer ); + } + + const char* data() const + { + return mFormat.c_str(); + } + + String end() + { + return mFormat.getString(); + } + + S32 append( char ch ) + { + char str[2]; + str[0]=ch; + str[1]='\0'; + return mFormat.append(str); + } + S32 append( const char* str ) + { + return mFormat.append(str); + } + S32 append( const String& str ) + { + return mFormat.append( str.c_str(), str.length() ); + } + S32 append( const char* str, U32 length ) + { + return mFormat.append(str,length); + } + S32 format( const char* fmt, ... ) + { + va_list args; + va_start(args, fmt); + return mFormat.formatAppend(fmt, &args); + } +}; + +// For use in hash tables and the like for explicitly requesting case sensitive hashing. +// Meant to only appear in hash table definition (casting will take care of the rest). +class StringCase : public String +{ +public: + StringCase() : String() {} + StringCase(const String & s) : String(s) {} +}; + +// For use in hash tables and the like for explicitly requesting case insensitive hashing. +// Meant to only appear in hash table definition (casting will take care of the rest). +class StringNoCase : public String +{ +public: + StringNoCase() : String() {} + StringNoCase(const String & s) : String(s) {} +}; + +class FileName : public String +{ +public: + FileName() : String() {} + FileName(const String & s) : String(s) {} + FileName & operator=(const String & s) { String::operator=(s); return *this; } +}; + +//----------------------------------------------------------------------------- + +extern String operator+(const String &a, StringChar c); +extern String operator+(StringChar c, const String &a); +extern String operator+(const String &a, const StringChar *b); +extern String operator+(const String &a, const String &b); +extern String operator+(const StringChar *a, const String &b); + +#endif + diff --git a/Engine/source/core/util/swizzle.h b/Engine/source/core/util/swizzle.h new file mode 100644 index 000000000..4cf929fce --- /dev/null +++ b/Engine/source/core/util/swizzle.h @@ -0,0 +1,164 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SWIZZLE_H_ +#define _SWIZZLE_H_ + +#include "platform/platform.h" +#include "core/frameAllocator.h" + +/// This class will swizzle 'sizeof( T )' length chunks of memory into different +/// patterns which are user described. The pattern is described by an instance +/// of Swizzle and this swizzle can then be executed on buffers. The following +/// must be true of the buffer size: +/// size % ( sizeof( T ) * mapLength ) == 0 +template +class Swizzle +{ +private: + /// This is an array from 0..n. Each entry in the array is a dsize_t with values + /// in the range 0..n. Each value in the range 0..n can occur any number of times. + /// + /// For example: + /// This is our data set, an array of characters with 4 elements + /// { 'a', 'b', 'c', 'd' } + /// + /// If the map { 3, 2, 1, 0 } was applied to this set, the result would be: + /// { 'd', 'c', 'b', 'a' } + /// + /// If the map { 3, 0, 2, 2 } was applied to the set, the result would be: + /// { 'd', 'a', 'c', 'c' } + dsize_t mMap[mapLength]; + +public: + /// Construct a swizzle + /// @see Swizzle::mMap + Swizzle( const dsize_t *map ); + + virtual ~Swizzle(){} + + /// This method will, in the general case, use the ToBuffer method to swizzle + /// the memory specified into a temporary buffer, allocated by FrameTemp, and + /// then copy the temporary memory into the source memory. + /// + /// @param memory Pointer to the start of the buffer to swizzle + /// @param size Size of the buffer + virtual void InPlace( void *memory, const dsize_t size ) const; + + /// This method copies the data from source to destination while applying the + /// re-ordering. This method is, in the non-specalized case, O(N^2) where N + /// is sizeof( T ) / size; the number of instances of type 'T' in the buffer + /// + /// @param destination The destination of the swizzled data + /// @param source The source data to be swizzled + /// @param size Size of the source and destination buffers. + virtual void ToBuffer( void *destination, const void *source, const dsize_t size ) const; +}; + +// Null swizzle +template +class NullSwizzle : public Swizzle +{ +public: + NullSwizzle( const dsize_t *map = NULL ) : Swizzle( map ) {}; + + virtual void InPlace( void *memory, const dsize_t size ) const {} + + virtual void ToBuffer( void *destination, const void *source, const dsize_t size ) const + { + dMemcpy( destination, source, size ); + } +}; + +//------------------------------------------------------------------------------ +// Common Swizzles +namespace Swizzles +{ + extern Swizzle bgra; + extern Swizzle argb; + extern Swizzle rgba; + extern Swizzle abgr; + + extern Swizzle bgr; + extern Swizzle rgb; + + extern NullSwizzle null; +}; + +//------------------------------------------------------------------------------ + +template +Swizzle::Swizzle( const dsize_t *map ) +{ + if( map != NULL ) + dMemcpy( mMap, map, sizeof( dsize_t ) * mapLength ); +} + +//------------------------------------------------------------------------------ + +template +inline void Swizzle::ToBuffer( void *destination, const void *source, const dsize_t size ) const +{ + // TODO: OpenMP? + AssertFatal( size % ( sizeof( T ) * mapLength ) == 0, "Bad buffer size for swizzle, see docs." ); + AssertFatal( destination != NULL, "Swizzle::ToBuffer - got a NULL destination pointer!" ); + AssertFatal( source != NULL, "Swizzle::ToBuffer - got a NULL source pointer!" ); + + T *dest = reinterpret_cast( destination ); + const T *src = reinterpret_cast( source ); + + for( int i = 0; i < size / ( mapLength * sizeof( T ) ); i++ ) + { + dMemcpy( dest, src, mapLength * sizeof( T ) ); + + for( int j = 0; j < mapLength; j++ ) + *dest++ = src[mMap[j]]; + + src += mapLength; + } +} + +//------------------------------------------------------------------------------ + +template +inline void Swizzle::InPlace( void *memory, const dsize_t size ) const +{ + // Just in case the inliner messes up the FrameTemp scoping (not sure if it would) -patw + { + // FrameTemp should work because the PNG loading code uses the FrameAllocator, so + // it should only get used on an image w/ that size as max -patw + FrameTemp buffer( size ); + dMemcpy( ~buffer, memory, size ); + ToBuffer( memory, ~buffer, size ); + } +} + +//------------------------------------------------------------------------------ + +// Template specializations for certain swizzles +//#include "core/util/swizzleSpec.h" + +#ifdef TORQUE_OS_XENON +# include "platformXbox/altivecSwizzle.h" +#endif + +#endif \ No newline at end of file diff --git a/Engine/source/core/util/swizzleSpec.h b/Engine/source/core/util/swizzleSpec.h new file mode 100644 index 000000000..d45273498 --- /dev/null +++ b/Engine/source/core/util/swizzleSpec.h @@ -0,0 +1,112 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SWIZZLESPEC_H_ +#define _SWIZZLESPEC_H_ + +//------------------------------------------------------------------------------ +// (most common) Specialization +//------------------------------------------------------------------------------ +#include "core/util/byteswap.h" + +template<> +inline void Swizzle::InPlace( void *memory, const dsize_t size ) const +{ + AssertFatal( size % 4 == 0, "Bad buffer size for swizzle, see docs." ); + + U8 *dest = reinterpret_cast( memory ); + U8 *src = reinterpret_cast( memory ); + + // Fast divide by 4 since we are assured a proper size + for( int i = 0; i < size >> 2; i++ ) + { + BYTESWAP( *dest++, src[mMap[0]] ); + BYTESWAP( *dest++, src[mMap[1]] ); + BYTESWAP( *dest++, src[mMap[2]] ); + BYTESWAP( *dest++, src[mMap[3]] ); + + src += 4; + } +} + +template<> +inline void Swizzle::ToBuffer( void *destination, const void *source, const dsize_t size ) const +{ + AssertFatal( size % 4 == 0, "Bad buffer size for swizzle, see docs." ); + + U8 *dest = reinterpret_cast( destination ); + const U8 *src = reinterpret_cast( source ); + + // Fast divide by 4 since we are assured a proper size + for( int i = 0; i < size >> 2; i++ ) + { + *dest++ = src[mMap[0]]; + *dest++ = src[mMap[1]]; + *dest++ = src[mMap[2]]; + *dest++ = src[mMap[3]]; + + src += 4; + } +} + +//------------------------------------------------------------------------------ +// Specialization +//------------------------------------------------------------------------------ + +template<> +inline void Swizzle::InPlace( void *memory, const dsize_t size ) const +{ + AssertFatal( size % 3 == 0, "Bad buffer size for swizzle, see docs." ); + + U8 *dest = reinterpret_cast( memory ); + U8 *src = reinterpret_cast( memory ); + + for( int i = 0; i < size /3; i++ ) + { + BYTESWAP( *dest++, src[mMap[0]] ); + BYTESWAP( *dest++, src[mMap[1]] ); + BYTESWAP( *dest++, src[mMap[2]] ); + + src += 3; + } +} + +template<> +inline void Swizzle::ToBuffer( void *destination, const void *source, const dsize_t size ) const +{ + AssertFatal( size % 3 == 0, "Bad buffer size for swizzle, see docs." ); + + U8 *dest = reinterpret_cast( destination ); + const U8 *src = reinterpret_cast( source ); + + for( int i = 0; i < size / 3; i++ ) + { + *dest++ = src[mMap[0]]; + *dest++ = src[mMap[1]]; + *dest++ = src[mMap[2]]; + + src += 3; + } +} + + +#endif \ No newline at end of file diff --git a/Engine/source/core/util/tAlignedArray.h b/Engine/source/core/util/tAlignedArray.h new file mode 100644 index 000000000..b3fa785c3 --- /dev/null +++ b/Engine/source/core/util/tAlignedArray.h @@ -0,0 +1,203 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _ALIGNEDARRAY_H_ +#define _ALIGNEDARRAY_H_ + +/// This is a fixed size class that will align its elements on configurable boundaries. +template +class AlignedArray +{ +public: + AlignedArray(); + /// Create an AlignedArray + /// @param arraySize How many items + /// @param elementSize Size of each element (including padding) + AlignedArray(const U32 arraySize, const U32 elementSize); + /// Create an AlignedArray + /// @param arraySize How many items + /// @param elementSize Size of each element (including padding) + /// @param buffer Preallocated buffer (with data and aligned on elementSize boundaries) + /// @param takeOwn If true, this class will take ownership of the buffer and free on destruct + AlignedArray(const U32 arraySize, const U32 elementSize, U8* buffer, bool takeOwn); + ~AlignedArray(); + + void setCapacity(const U32 arraySize, const U32 elementSize); + void setCapacity(const U32 arraySize, const U32 elementSize, U8* buffer, bool takeOwn); + + /// Size of the array + U32 size() const; + + /// Set a new array size (up to initial size created returned by capacity) + void setSize(U32 newsize); + + /// Capacity of the array (you can setCapacity the size this high) + U32 capacity() const; + + /// Returns the size of an element (useful for asserting, etc) + U32 getElementSize() const; + + /// Returns a pointer to the raw buffer data. + const void* getBuffer() const; + void* getBuffer(); + + // Returns the buffer size in bytes. + U32 getBufferSize() const { return mElementSize * mElementCount; } + + // Operators + T& operator[](const U32); + const T& operator[](const U32) const; +protected: + // How big an element is, this includes padding need to align + U32 mElementSize; + // How many elements do we have + U32 mElementCount; + // How many elements can we have + U32 mCapacity; + // Storage, we use placement new and reinterpret casts to deal with + // alignment + U8* mBuffer; + // Do we own this buffer? Or are we just wrapping it? + bool mOwnBuffer; +}; + +template +inline AlignedArray::AlignedArray() +{ + mElementSize = 0; + mElementCount = 0; + mCapacity = 0; + mBuffer = NULL; + mOwnBuffer = true; +} + +template +inline AlignedArray::AlignedArray(const U32 arraySize, const U32 elementSize) +{ + mElementCount = 0; // avoid debug assert + setCapacity(arraySize, elementSize); +} + +template +inline AlignedArray::AlignedArray(const U32 arraySize, const U32 elementSize, U8* buffer, bool takeOwn) +{ + mElementCount = 0; // avoid debug assert + setCapacity(arraySize, elementSize, buffer, takeOwn); +} + +template +inline AlignedArray::~AlignedArray() +{ + if (mOwnBuffer) + delete[] mBuffer; +} +template +inline void AlignedArray::setCapacity(const U32 arraySize, const U32 elementSize) +{ + AssertFatal(mElementCount == 0, "Unable to set array properties after they are init'ed"); + AssertFatal(elementSize >= sizeof(T), "Element size is too small!"); + AssertFatal(arraySize > 0, "0 length AlignedArrays are not allowed!"); + + mElementSize = elementSize; + mElementCount = arraySize; + mCapacity = arraySize; + mBuffer = new U8[mElementSize * mElementCount]; + dMemset(mBuffer, 0xFF, mElementSize * mElementCount); + U32 bufIndex = 0; + for (U32 i = 0; i < mElementCount; i++) + { + T* item = reinterpret_cast(&mBuffer[bufIndex]); + constructInPlace(item); + bufIndex += mElementSize; + } + mOwnBuffer = true; +} + +template +inline void AlignedArray::setCapacity(const U32 arraySize, const U32 elementSize, U8* buffer, bool takeOwn) +{ + AssertFatal(mElementCount == 0, "Unable to set array properties after they are init'ed"); + AssertFatal(elementSize >= sizeof(T), "Element size is too small!"); + AssertFatal(arraySize > 0, "0 length AlignedArrays are not allowed!"); + AssertFatal(buffer, "NULL buffer!"); + + mElementSize = elementSize; + mElementCount = arraySize; + mCapacity = arraySize; + mBuffer = buffer; + mOwnBuffer = takeOwn; +} + +/// Set a new array size (up to initial size created returned by capacity) +template +inline void AlignedArray::setSize(U32 newsize) +{ + AssertFatal(newsize <= mCapacity, "Unable to grow this type of array!"); + mElementCount = newsize; +} + +template +inline U32 AlignedArray::size() const +{ + return mElementCount; +} + +template +inline U32 AlignedArray::capacity() const +{ + return mCapacity; +} + +/// Returns the size of an element (useful for asserting, etc) +template +U32 AlignedArray::getElementSize() const +{ + return mElementSize; +} + +template +inline T& AlignedArray::operator[](const U32 index) +{ + AssertFatal(index < mElementCount, "AlignedArray::operator[] - out of bounds array access!"); + return reinterpret_cast(mBuffer[index*mElementSize]); +} + +template +inline const T& AlignedArray::operator[](const U32 index) const +{ + AssertFatal(index < mElementCount, "AlignedArray::operator[] - out of bounds array access!"); + return reinterpret_cast(mBuffer[index*mElementSize]); +} + +template +const void* AlignedArray::getBuffer() const +{ + return reinterpret_cast(mBuffer); +} + +template +void* AlignedArray::getBuffer() +{ + return reinterpret_cast(mBuffer); +} + +#endif // _ALIGNEDARRAY_H_ diff --git a/Engine/source/core/util/tDictionary.cpp b/Engine/source/core/util/tDictionary.cpp new file mode 100644 index 000000000..a23e6b751 --- /dev/null +++ b/Engine/source/core/util/tDictionary.cpp @@ -0,0 +1,47 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/util/tDictionary.h" + +namespace DictHash +{ + +static U32 Primes[] = { + 53ul, 97ul, 193ul, 389ul, 769ul, + 1543ul, 3079ul, 6151ul, 12289ul, 24593ul, + 49157ul, 98317ul, 196613ul, 393241ul, 786433ul, + 1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul, + 50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul, + 1610612741ul, 3221225473ul, 4294967291ul +}; + +U32 nextPrime(U32 size) +{ + U32 len = sizeof(Primes) / sizeof(U32); + for(U32 i=0; i size) + return Primes[i]; + + return Primes[len-1]; +} + +}; diff --git a/Engine/source/core/util/tDictionary.h b/Engine/source/core/util/tDictionary.h new file mode 100644 index 000000000..804d3eabd --- /dev/null +++ b/Engine/source/core/util/tDictionary.h @@ -0,0 +1,867 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TDICTIONARY_H_ +#define _TDICTIONARY_H_ + +#ifndef _STRINGFUNCTIONS_H_ +#include "core/strings/stringFunctions.h" +#endif +#ifndef _HASHFUNCTION_H_ +#include "core/util/hashFunction.h" +#endif +#ifndef _TORQUE_STRING_H_ +#include "core/util/str.h" +#endif + +#ifndef _DATACHUNKER_H_ +#include "core/dataChunker.h" +#endif + + +// TODO: Maybe move these into a more general Tuple class? + +template +struct CompoundKey +{ + A key1; + B key2; + + CompoundKey() {}; + CompoundKey(const A & a, const B & b) { key1 = a; key2 = b; }; + + bool operator==(const CompoundKey & compound) const { return key1==compound.key1 && key2==compound.key2; } +}; + +template +struct CompoundKey3 +{ + A key1; + B key2; + C key3; + + CompoundKey3() {}; + CompoundKey3(const A & a, const B & b, const C & c) { key1 = a; key2 = b; key3 = c;}; + + bool operator==(const CompoundKey3 & compound) const { return key1==compound.key1 && key2==compound.key2 && key3==compound.key3; } +}; + + +namespace DictHash +{ + inline U32 hash(U32 data) + { + return data; + } + + inline U32 hash(const StringCase &data) + { + return data.getHashCaseSensitive(); + } + + inline U32 hash(const StringNoCase &data) + { + return data.getHashCaseInsensitive(); + } + + inline U32 hash(const String &data) + { + return data.getHashCaseInsensitive(); + } + + inline U32 hash(const char *data) + { + return Torque::hash( (const U8 *)data, dStrlen( data ), 0 ); + } + + inline U32 hash(const void *data) + { + return (U32)data; + } + + template + inline U32 hash(const CompoundKey & compound) + { + return hash(compound.key1) + hash(compound.key2); + } + + template + inline U32 hash(const CompoundKey3 & compound) + { + return hash(compound.key1) + hash(compound.key2) + hash(compound.key3); + } + + U32 nextPrime(U32); +}; + +namespace KeyCmp +{ + template + inline bool equals( const Key &keya, const Key &keyb ) + { + return ( keya == keyb ); + } + + template<> + inline bool equals<>( const StringCase &keya, const StringCase &keyb ) + { + return ( keya.equal( keyb, String::Case ) ); + } + + template<> + inline bool equals<>( const StringNoCase &keya, const StringNoCase &keyb ) + { + return ( keya.equal( keyb, String::NoCase ) ); + } + + template<> + inline bool equals<>( const String &keya, const String &keyb ) + { + return ( keya.equal( keyb, String::NoCase ) ); + } + + template<> + inline bool equals<>( const char * const &keya, const char * const &keyb ) + { + return ( dStrcmp( keya, keyb ) == 0 ); + } +}; + +/// A HashTable template class. +/// +/// The hash table class maps between a key and an associated value. Access +/// using the key is performed using a hash table. The class provides +/// methods for both unique and equal keys. The global ::hash(Type) function +/// is used for hashing, see util/hash.h +/// @ingroup UtilContainers +template +class HashTable +{ +public: + struct Pair + { + Key key; + Value value; + Pair() {} + Pair(Key k,Value v) + : key(k), + value(v) + {} + }; + +private: + struct Node + { + Node* mNext; + Pair mPair; + Node(): mNext(0) {} + Node(Pair p,Node* n) + : mNext(n), + mPair(p) + {} + }; + + Node** mTable; ///< Hash table + S32 mTableSize; ///< Hash table size + U32 mSize; ///< Number of keys in the table + ClassChunker mNodeAllocator; + + U32 _hash(const Key& key) const; + U32 _index(const Key& key) const; + Node* _next(U32 index) const; + void _resize(U32 size); + void _destroy(); + +public: + // Iterator support + template + class _Iterator { + friend class HashTable; + E* mLink; + M* mHashTable; + operator E*(); + public: + typedef U ValueType; + typedef U* Pointer; + typedef U& Reference; + + _Iterator() + { + mHashTable = 0; + mLink = 0; + } + + _Iterator(M* table,E* ptr) + { + mHashTable = table; + mLink = ptr; + } + + _Iterator& operator++() + { + mLink = mLink->mNext? mLink->mNext : + mHashTable->_next(mHashTable->_index(mLink->mPair.key) + 1); + return *this; + } + + _Iterator operator++(int) + { + _Iterator itr(*this); + ++(*this); + return itr; + } + + bool operator==(const _Iterator& b) const + { + return mHashTable == b.mHashTable && mLink == b.mLink; + } + + bool operator!=(const _Iterator& b) const + { + return !(*this == b); + } + + U* operator->() const + { + return &mLink->mPair; + } + + U& operator*() const + { + return mLink->mPair; + } + }; + + // Types + typedef Pair ValueType; + typedef Pair& Reference; + typedef const Pair& ConstReference; + + typedef _Iterator Iterator; + typedef _Iterator ConstIterator; + typedef S32 DifferenceType; + typedef U32 SizeType; + + // Initialization + HashTable(); + ~HashTable(); + HashTable(const HashTable& p); + + // Management + U32 size() const; ///< Return the number of elements + U32 tableSize() const; ///< Return the size of the hash bucket table + void clear(); ///< Empty the HashTable + void resize(U32 size); + bool isEmpty() const; ///< Returns true if the table is empty + F32 collisions() const; ///< Returns the average number of nodes per bucket + + // Insert & erase elements + Iterator insertEqual(const Key& key, const Value&); + Iterator insertUnique(const Key& key, const Value&); + void erase(Iterator); ///< Erase the given entry + void erase(const Key& key); ///< Erase all matching keys from the table + void erase(const Key & key, const Value & value); ///< Erase entry for this key-value pair + + // HashTable lookup + Iterator findOrInsert(const Key& key); + Iterator find(const Key&); ///< Find the first entry for the given key + ConstIterator find(const Key&) const; ///< Find the first entry for the given key + bool find(const Key & key, Value & value); ///< Find the first entry for the given key + S32 count(const Key&) const; ///< Count the number of matching keys in the table + + // Forward Iterator access + Iterator begin(); ///< Iterator to first element + ConstIterator begin() const; ///< Iterator to first element + Iterator end(); ///< Iterator to last element + 1 + ConstIterator end() const; ///< Iterator to last element + 1 + + void operator=(const HashTable& p); +}; + +template HashTable::HashTable() : mNodeAllocator(512) +{ + mTableSize = 0; + mTable = 0; + mSize = 0; +} + +template HashTable::HashTable(const HashTable& p) : mNodeAllocator(512) +{ + mSize = 0; + mTableSize = 0; + mTable = 0; + *this = p; +} + +template HashTable::~HashTable() +{ + _destroy(); +} + +//----------------------------------------------------------------------------- + +template +inline U32 HashTable::_hash(const Key& key) const +{ + return DictHash::hash(key); +} + +template +inline U32 HashTable::_index(const Key& key) const +{ + return _hash(key) % mTableSize; +} + +template +typename HashTable::Node* HashTable::_next(U32 index) const +{ + for (; index < mTableSize; index++) + if (Node* node = mTable[index]) + return node; + return 0; +} + +template +void HashTable::_resize(U32 size) +{ + S32 currentSize = mTableSize; + mTableSize = DictHash::nextPrime(size); + Node** table = new Node*[mTableSize]; + dMemset(table,0,mTableSize * sizeof(Node*)); + + for (S32 i = 0; i < currentSize; i++) + for (Node* node = mTable[i]; node; ) + { + // Get groups of matching keys + Node* last = node; + while (last->mNext && last->mNext->mPair.key == node->mPair.key) + last = last->mNext; + + // Move the chain to the new table + Node** link = &table[_index(node->mPair.key)]; + Node* tmp = last->mNext; + last->mNext = *link; + *link = node; + node = tmp; + } + + delete[] mTable; + mTable = table; +} + +template +void HashTable::_destroy() +{ + // Call destructors. + for (S32 i = 0; i < mTableSize; i++) + for (Node* ptr = mTable[i]; ptr; ) + { + Node *tmp = ptr; + ptr = ptr->mNext; + destructInPlace( tmp ); + } + + mNodeAllocator.freeBlocks(); + delete[] mTable; + mTable = NULL; +} + + +//----------------------------------------------------------------------------- +// management + +template +inline U32 HashTable::size() const +{ + return mSize; +} + +template +inline U32 HashTable::tableSize() const +{ + return mTableSize; +} + +template +inline void HashTable::clear() +{ + _destroy(); + mTableSize = 0; + mSize = 0; +} + +/// Resize the bucket table for an estimated number of elements. +/// This method will optimize the hash bucket table size to hold the given +/// number of elements. The size argument is a hint, and will not be the +/// exact size of the table. If the table is sized down below it's optimal +/// size, the next insert will cause it to be resized again. Normally this +/// function is used to avoid resizes when the number of elements that will +/// be inserted is known in advance. +template +inline void HashTable::resize(U32 size) +{ + // Attempt to resize the datachunker as well. + mNodeAllocator.setChunkSize(sizeof(Node) * size); + _resize(size); +} + +template +inline bool HashTable::isEmpty() const +{ + return mSize == 0; +} + +template +inline F32 HashTable::collisions() const +{ + S32 chains = 0; + for (S32 i = 0; i < mTableSize; i++) + if (mTable[i]) + chains++; + return F32(mSize) / chains; +} + + +//----------------------------------------------------------------------------- +// add & remove elements + +/// Insert the key value pair but don't insert duplicates. +/// This insert method does not insert duplicate keys. If the key already exists in +/// the table the function will fail and end() is returned. +template +typename HashTable::Iterator HashTable::insertUnique(const Key& key, const Value& x) +{ + if (mSize >= mTableSize) + _resize(mSize + 1); + Node** table = &mTable[_index(key)]; + for (Node* itr = *table; itr; itr = itr->mNext) + if ( KeyCmp::equals( itr->mPair.key, key) ) + return end(); + + mSize++; + Node* newNode = mNodeAllocator.alloc(); + newNode->mPair = Pair(key, x); + newNode->mNext = *table; + *table = newNode; + return Iterator(this,*table); +} + +/// Insert the key value pair and allow duplicates. +/// This insert method allows duplicate keys. Keys are grouped together but +/// are not sorted. +template +typename HashTable::Iterator HashTable::insertEqual(const Key& key, const Value& x) +{ + if (mSize >= mTableSize) + _resize(mSize + 1); + // The new key is inserted at the head of any group of matching keys. + Node** prev = &mTable[_index(key)]; + for (Node* itr = *prev; itr; prev = &itr->mNext, itr = itr->mNext) + if ( KeyCmp::equals( itr->mPair.key, key ) ) + break; + mSize++; + Node* newNode = mNodeAllocator.alloc(); + newNode->mPair = Pair(key, x); + newNode->mNext = *prev; + *prev = newNode; + return Iterator(this,*prev); +} + +template +void HashTable::erase(const Key& key) +{ + if (mTable==NULL) + return; + Node** prev = &mTable[_index(key)]; + for (Node* itr = *prev; itr; prev = &itr->mNext, itr = itr->mNext) + if ( KeyCmp::equals( itr->mPair.key, key ) ) { + // Delete matching keys, which should be grouped together. + do { + Node* tmp = itr; + itr = itr->mNext; + mNodeAllocator.free(tmp); + mSize--; + } while (itr && KeyCmp::equals( itr->mPair.key, key ) ); + *prev = itr; + return; + } +} + +template +void HashTable::erase(Iterator node) +{ + if (mTable==NULL) + return; + Node** prev = &mTable[_index(node->key)]; + for (Node* itr = *prev; itr; prev = &itr->mNext, itr = itr->mNext) + { + if (itr == node.mLink) + { + *prev = itr->mNext; + mNodeAllocator.free(itr); + mSize--; + return; + } + } +} + +template +void HashTable::erase(const Key & key, const Value & value) +{ + if (mTable==NULL) + return; + Node** prev = &mTable[_index(key)]; + for (Node* itr = *prev; itr; prev = &itr->mNext, itr = itr->mNext) + { + if ( KeyCmp::equals( itr->mPair.key, key ) && itr->mPair.value == value) + { + *prev = itr->mNext; + mNodeAllocator.free(itr); + mSize--; + return; + } + } +} + +//----------------------------------------------------------------------------- + +/// Find the key, or insert a one if it doesn't exist. +/// Returns the first key in the table that matches, or inserts one if there +/// are none. +template +typename HashTable::Iterator HashTable::findOrInsert(const Key& key) +{ + if (mSize >= mTableSize) + _resize(mSize + 1); + Node** table = &mTable[_index(key)]; + for (Node* itr = *table; itr; itr = itr->mNext) + if ( KeyCmp::equals( itr->mPair.key, key ) ) + return Iterator(this,itr); + mSize++; + Node* newNode = mNodeAllocator.alloc(); + newNode->mPair = Pair(key, Value()); + newNode->mNext = *table; + *table = newNode; + return Iterator(this,*table); +} + +template +typename HashTable::Iterator HashTable::find(const Key& key) +{ + if (mTableSize) + for (Node* itr = mTable[_index(key)]; itr; itr = itr->mNext) + if ( KeyCmp::equals( itr->mPair.key, key ) ) + return Iterator(this,itr); + return Iterator(this,0); +} + +template +typename HashTable::ConstIterator HashTable::find(const Key& key) const +{ + if (mTableSize) + { + for (Node* itr = mTable[_index(key)]; itr; itr = itr->mNext) + { + if ( KeyCmp::equals( itr->mPair.key, key ) ) + return ConstIterator(this,itr); + } + } + return ConstIterator(this,0); +} + +template +bool HashTable::find(const Key & key, Value & value) +{ + if (mTableSize) + { + for (Node* itr = mTable[_index(key)]; itr; itr = itr->mNext) + if ( KeyCmp::equals( itr->mPair.key, key ) ) + { + value = itr->mPair.value; + return true; + } + } + return false; +} + +template +S32 HashTable::count(const Key& key) const +{ + S32 count = 0; + if (mTableSize) + for (Node* itr = mTable[_index(key)]; itr; itr = itr->mNext) + if ( KeyCmp::equals( itr->mPair.key, key ) ) { + // Matching keys should be grouped together. + do { + count++; + itr = itr->mNext; + } while (itr && KeyCmp::equals( itr->mPair.key, key ) ); + break; + } + return count; +} + + +//----------------------------------------------------------------------------- +// Iterator access + +template +inline typename HashTable::Iterator HashTable::begin() +{ + return Iterator(this,_next(0)); +} + +template +inline typename HashTable::ConstIterator HashTable::begin() const +{ + return ConstIterator(this,_next(0)); +} + +template +inline typename HashTable::Iterator HashTable::end() +{ + return Iterator(this,0); +} + +template +inline typename HashTable::ConstIterator HashTable::end() const +{ + return ConstIterator(this,0); +} + + +//----------------------------------------------------------------------------- +// operators + +template +void HashTable::operator=(const HashTable& p) +{ + _destroy(); + mTableSize = p.mTableSize; + mTable = new Node*[mTableSize]; + mSize = p.mSize; + for (S32 i = 0; i < mTableSize; i++) + if (Node* itr = p.mTable[i]) + { + Node** head = &mTable[i]; + do + { + *head = new Node(itr->mPair,0); + head = &(*head)->mNext; + itr = itr->mNext; + } while (itr); + } + else + mTable[i] = 0; +} + +//----------------------------------------------------------------------------- +// Iterator class + +/// A Map template class. +/// The map class maps between a key and an associated value. Keys +/// are unique. +/// The hash table class is used as the default implementation so the +/// the key must be hashable, see util/hash.h for details. +/// @ingroup UtilContainers +template > +class Map +{ +public: + // types + typedef typename Sequence::Pair Pair; + typedef Pair ValueType; + typedef Pair& Reference; + typedef const Pair& ConstReference; + + typedef typename Sequence::Iterator Iterator; + typedef typename Sequence::ConstIterator ConstIterator; + typedef S32 DifferenceType; + typedef U32 SizeType; + + // initialization + Map() {} + ~Map() {} + Map(const Map& p); + + // management + U32 size() const; ///< Return the number of elements + void resize(U32 size); + void clear(); ///< Empty the Map + bool isEmpty() const; ///< Returns true if the map is empty + + // insert & erase elements + Iterator insert(const Key& key, const Value&); // Documented below... + void erase(Iterator); ///< Erase the given entry + void erase(const Key& key); ///< Erase the key from the map + + // Map lookup + Iterator find(const Key&); ///< Find entry for the given key + ConstIterator find(const Key&) const; ///< Find entry for the given key + bool contains(const Key&a) const + { + return mMap.count(a) > 0; + } + /// Try to get the value of the specified key. Returns true and fills in the value + /// if the key exists. Returns false otherwise. + /// Unlike operator [], this function does not create the key/value if the key + /// is not found. + bool tryGetValue(const Key& key, Value& val) const + { + ConstIterator iter = find(key); + if (iter != end()) + { + val = (*iter).value; + return true; + } + return false; + } + + // forward Iterator access + Iterator begin(); ///< Iterator to first element + ConstIterator begin() const; ///< Iterator to first element + Iterator end(); ///< IIterator to last element + 1 + ConstIterator end() const; ///< Iterator to last element + 1 + + // operators + /// Index using the given key. If the key is not currently in the map it is added. + /// If you just want to try to get the value without the side effect of creating the + /// key, use tryGetValue() instead. + Value& operator[](const Key&); + +private: + Sequence mMap; +}; + +template Map::Map(const Map& p) +{ + *this = p; +} + + +//----------------------------------------------------------------------------- +// management + +template +inline U32 Map::size() const +{ + return mMap.size(); +} + +template +inline void Map::resize(U32 size) +{ + return mMap.resize(size); +} + +template +inline void Map::clear() +{ + mMap.clear(); +} + +template +inline bool Map::isEmpty() const +{ + return mMap.isEmpty(); +} + + +//----------------------------------------------------------------------------- +// add & remove elements + +/// Insert the key value pair but don't allow duplicates. +/// The map class does not allow duplicates keys. If the key already exists in +/// the map the function will fail and return end(). +template +typename Map::Iterator Map::insert(const Key& key, const Value& x) +{ + return mMap.insertUnique(key,x); +} + +template +void Map::erase(const Key& key) +{ + mMap.erase(key); +} + +template +void Map::erase(Iterator node) +{ + mMap.erase(node); +} + + +//----------------------------------------------------------------------------- +// Searching + +template +typename Map::Iterator Map::find(const Key& key) +{ + return mMap.find(key); +} + +template +typename Map::ConstIterator Map::find(const Key& key) const +{ + return mMap.find(key); +} + +//----------------------------------------------------------------------------- +// Iterator access + +template +inline typename Map::Iterator Map::begin() +{ + return mMap.begin(); +} + +template +inline typename Map::ConstIterator Map::begin() const +{ + return mMap.begin(); +} + +template +inline typename Map::Iterator Map::end() +{ + return mMap.end(); +} + +template +inline typename Map::ConstIterator Map::end() const +{ + return mMap.end(); +} + + +//----------------------------------------------------------------------------- +// operators + +template +inline Value& Map::operator[](const Key& key) +{ + return mMap.findOrInsert(key)->value; +} + +#endif + diff --git a/Engine/source/core/util/tFixedSizeDeque.h b/Engine/source/core/util/tFixedSizeDeque.h new file mode 100644 index 000000000..2d6c65ff9 --- /dev/null +++ b/Engine/source/core/util/tFixedSizeDeque.h @@ -0,0 +1,185 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TFIXEDSIZEDEQUE_H_ +#define _TFIXEDSIZEDEQUE_H_ + +#ifndef _PLATFORM_H_ +# include "platform/platform.h" +#endif + + +/// A double-ended queue with a fixed number of entries set at +/// construction time. Implemented as an array. +/// +/// @param T Element type of the queue. +template< typename T > +class FixedSizeDeque +{ + public: + + /// Type for elements stored in the deque. + typedef T ValueType; + + protected: + + /// + U32 mBufferSize; + + /// + U32 mSize; + + /// Index + U32 mStart; + + /// + U32 mEnd; + + /// + ValueType* mBuffer; + + public: + + /// + FixedSizeDeque( U32 bufferSize ) + : mBufferSize( bufferSize ), mSize( 0 ), mStart( 0 ), mEnd( 0 ) + { + // Don't use new() so we don't get a buffer full of instances. + mBuffer = ( ValueType* ) dMalloc( sizeof( ValueType ) * bufferSize ); + } + + ~FixedSizeDeque() + { + // Destruct remaining instances. + + while( mSize ) + { + destructInPlace( &mBuffer[ mStart ] ); + mStart ++; + if( mStart == mBufferSize ) + mStart = 0; + mSize --; + } + + // Free buffer. + dFree( mBuffer ); + } + + /// + bool isEmpty() const { return !size(); } + + /// + U32 size() const + { + return mSize; + } + + /// + U32 capacity() const + { + return ( mBufferSize - size() ); + } + + /// + void clear() + { + while( size() ) + popFront(); + } + + /// Return the leftmost value in the queue. + ValueType front() const + { + AssertFatal( !isEmpty(), "FixedSizeDeque::front() - queue is empty" ); + return mBuffer[ mStart ]; + } + + /// Return the rightmost value in the queue. + ValueType back() const + { + AssertFatal( !isEmpty(), "FixedSizeDeque::back() - queue is empty" ); + if( !mEnd ) + return mBuffer[ mBufferSize - 1 ]; + else + return mBuffer[ mEnd - 1 ]; + } + + /// Prepend "value" to the left end of the queue. + void pushFront( const ValueType& value ) + { + AssertFatal( capacity() != 0, "FixedSizeDeque::pushFront() - queue is full" ); + if( mStart == 0 ) + mStart = mBufferSize - 1; + else + mStart --; + mBuffer[ mStart ] = value; + mSize ++; + } + + /// Append "value" to the right end of the queue. + void pushBack( const ValueType& value ) + { + AssertFatal( capacity() != 0, "FixedSizeDeque::pushBack() - queue is full" ); + mBuffer[ mEnd ] = value; + mEnd ++; + if( mEnd == mBufferSize ) + mEnd = 0; + mSize ++; + } + + /// Remove and return the leftmost value in the queue. + ValueType popFront() + { + AssertFatal( !isEmpty(), "FixedSizeDeque::popFront() - queue is empty" ); + ValueType value = mBuffer[ mStart ]; + destructInPlace( &mBuffer[ mStart ] ); + + mStart ++; + if( mStart == mBufferSize ) + mStart = 0; + + mSize --; + return value; + } + + /// Remove and return the rightmost value in the queue. + ValueType popBack() + { + AssertFatal( !isEmpty(), "FixedSizeDeque::popBack() - queue is empty" ); + + U32 idx; + if( !mEnd ) + idx = mBufferSize - 1; + else + idx = mEnd - 1; + + ValueType value = mBuffer[ idx ]; + destructInPlace( &mBuffer[ idx ] ); + + mEnd = idx; + + mSize --; + return value; + } +}; + +#endif // _TFIXEDSIZEDEQUE_H_ diff --git a/Engine/source/core/util/tFixedSizeVector.h b/Engine/source/core/util/tFixedSizeVector.h new file mode 100644 index 000000000..cab8a2186 --- /dev/null +++ b/Engine/source/core/util/tFixedSizeVector.h @@ -0,0 +1,106 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TFIXEDSIZEVECTOR_H_ +#define _TFIXEDSIZEVECTOR_H_ + + +/// A vector with a compile-time constant size. +template< typename T, int SIZE > +class FixedSizeVector +{ + protected: + + T mArray[ SIZE ]; + + public: + + typedef T* iterator; + typedef const T* const_iterator; + + FixedSizeVector() {} + FixedSizeVector( const T& a ) { mArray[ 0 ] = a; } + FixedSizeVector( const T& a, const T& b ) { mArray[ 0 ] = a; mArray[ 1 ] = b; } + FixedSizeVector( const T& a, const T& b, const T& c ) { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; } + FixedSizeVector( const T& a, const T& b, const T& c, const T& d ) { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; } + FixedSizeVector( const T& a, const T& b, const T& c, const T& d, const T& e ) { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; } + FixedSizeVector( const T& a, const T& b, const T& c, const T& d, const T& e, const T& f ) + { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; } + FixedSizeVector( const T& a, const T& b, const T& c, const T& d, const T& e, const T& f, const T& g ) + { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ]; } + FixedSizeVector( const T& a, const T& b, const T& c, const T& d, const T& e, const T& f, const T& g, const T& h ) + { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ]; mArray[ 7 ] = h; } + FixedSizeVector( const T& a, const T& b, const T& c, const T& d, const T& e, const T& f, const T& g, const T& h, const T& i ) + { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ]; mArray[ 7 ] = h; mArray[ 8 ] = i; } + FixedSizeVector( const T& a, const T& b, const T& c, const T& d, const T& e, const T& f, const T& g, const T& h, const T& i, const T& j ) + { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ]; mArray[ 7 ] = h; mArray[ 8 ] = i; mArray[ 9 ] = j; } + FixedSizeVector( const T& a, const T& b, const T& c, const T& d, const T& e, const T& f, const T& g, const T& h, const T& i, const T& j, const T& k ) + { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ]; mArray[ 7 ] = h; mArray[ 8 ] = i; mArray[ 9 ] = j; mArray[ 10 ] = k; } + FixedSizeVector( const T& a, const T& b, const T& c, const T& d, const T& e, const T& f, const T& g, const T& h, const T& i, const T& j, const T& k, const T& l ) + { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ]; mArray[ 7 ] = h; mArray[ 8 ] = i; mArray[ 9 ] = j; mArray[ 10 ] = k; mArray[ 11 ] = l; } + FixedSizeVector( const T& a, const T& b, const T& c, const T& d, const T& e, const T& f, const T& g, const T& h, const T& i, const T& j, const T& k, const T& l, const T& m ) + { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ]; mArray[ 7 ] = h; mArray[ 8 ] = i; mArray[ 9 ] = j; mArray[ 10 ] = k; mArray[ 11 ] = l; mArray[ 12 ] =m; } + + U32 size() const { return SIZE; } + bool empty() const { return ( SIZE == 0 ); } + const T* address() const { return mArray; } + T* address() { return mArray; } + + iterator begin() { return &mArray[ 0 ]; } + iterator end() { return &mArray[ SIZE ]; } + const_iterator begin() const { return &mArray[ 0 ]; } + const_iterator end() const { return &mArray[ SIZE ]; } + + const T& first() const + { + AssertFatal( !empty(), "FixedSizeVector::first - Vector is empty" ); + return mArray[ 0 ]; + } + T& first() + { + AssertFatal( !empty(), "FixedSizeVector::first - Vector is empty" ); + return mArray[ 0 ]; + } + const T& last() const + { + AssertFatal( !empty(), "FixedSizeVector::last - Vector is empty" ); + return mArray[ size() - 1 ]; + } + T& last() + { + AssertFatal( !empty(), "FixedSizeVector::last - Vector is empty" ); + return mArray[ size() - 1 ]; + } + + const T& operator []( U32 index ) const + { + AssertFatal( index <= size(), "FixedSizeVector::operator[] - Index out of range" ); + return mArray[ index ]; + } + T& operator []( U32 index ) + { + AssertFatal( index <= size(), "FixedSizeVector::operator[] - Index out of range" ); + return mArray[ index ]; + } +}; + +#endif // !_TFIXEDSIZEVECTOR_H_ diff --git a/Engine/source/core/util/tList.h b/Engine/source/core/util/tList.h new file mode 100644 index 000000000..4a2bcfd34 --- /dev/null +++ b/Engine/source/core/util/tList.h @@ -0,0 +1,490 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TORQUE_LIST_ +#define _TORQUE_LIST_ + +#ifndef _TORQUE_TYPES_H_ +#include "platform/types.h" +#endif + +namespace Torque +{ + +/// A list template class. +/// A classic list class similar to the STL list class. The list +/// class supports fast insert and erase operations, but slow dynamic +/// access. The list is circular and the iterator supports iterating +/// past the end() or rend() in order to loop around. +/// @ingroup UtilContainers +template +class List +{ + struct Link; + struct PrivatePersist {}; ///< Used instead of a (void *) for type-safety of persistent iterators + +public: + /// A PersistentIter is used for the special cases where you need to store an iterator for + /// efficiency reasons. The _Iterator class handles the conversion to/from PersistentIter. + /// @note The data in the node this points to could go away, so only use these in cases where + /// you control the allocation/deallocation of the nodes in the list. + typedef PrivatePersist *PersistentIter; + + // Iterator support + template + class _Iterator + { + public: + typedef U ValueType; + typedef U* Pointer; + typedef U& Reference; + + _Iterator(); + _Iterator(PersistentIter &iter) { _link = (Link *)iter; } + + operator PersistentIter() const { return (PrivatePersist *)_link; } + + _Iterator& operator++(); + _Iterator operator++(int); + _Iterator& operator--(); + _Iterator operator--(int); + bool operator==(const _Iterator&) const; + bool operator!=(const _Iterator&) const; + U* operator->() const; + U& operator*() const; + + private: + friend class List; + + E* _link; + _Iterator(E*); + }; + + // types + typedef Type ValueType; + typedef Type& Reference; + typedef const Type& ConstReference; + + typedef _Iterator Iterator; + typedef _Iterator ConstIterator; + + typedef S32 DifferenceType; + typedef U32 SizeType; + + // initialization + List(); + ~List(); + List(const List& p); + + // management + U32 getSize() const; ///< Return the number of elements + void resize(U32); ///< Set the list size + void clear(); ///< Empty the List + bool isEmpty() const; ///< Node count == 0? + + // insert elements + Iterator insert(U32 index, const Type& = Type()); ///< Insert new element at the given index + Iterator insert(Iterator, const Type& = Type()); ///< Insert at the given iter + Iterator pushFront(const Type& = Type()); ///< Insert at first element + Iterator pushBack(const Type& = Type()); ///< Insert after the last element + + // erase elements + void erase(U32); ///< Preserves element order + void erase(Iterator); ///< Preserves element order + void popFront(); ///< Remove the first element + void popBack(); ///< Remove the last element + + // forward Iterator access + Iterator begin(); ///< _Iterator to first element + ConstIterator begin() const; ///< _Iterator to first element + Iterator end(); ///< _Iterator to last element + 1 + ConstIterator end() const; ///< _Iterator to last element + 1 + + // reverse Iterator access + Iterator rbegin(); ///< _Iterator to first element - 1 + ConstIterator rbegin() const; ///< _Iterator to first element - 1 + Iterator rend(); ///< _Iterator to last element + ConstIterator rend() const; ///< _Iterator to last element + + // type access + Type& first(); ///< First element + const Type& first() const; ///< First element + Type& last(); ///< Last element + const Type& last() const; ///< Last element + + // operators + void operator=(const List& p); + Type& operator[](U32); + const Type& operator[](U32) const; + +private: + struct Link + { + Link* next; + Link* prev; + Link() {} + Link(Link* p,Link* n): next(n),prev(p) {} + }; + + struct Node: Link + { + Type value; + Node() {} + Node(Link* p,Link* n,const Type& x): Link(p,n),value(x) {} + }; + + Link _head; + U32 _size; + + Link* _node(U32 index) const; + void _destroy(); +}; + +template List::List() +{ + _size = 0; + _head.next = &_head; + _head.prev = &_head; +} + +template List::List(const List& p) +{ + _size = 0; + _head.next = &_head; + _head.prev = &_head; + *this = p; +} + +template List::~List() +{ + _destroy(); +} + + +//----------------------------------------------------------------------------- + +template typename List::Link* List::_node(U32 index) const +{ + Iterator itr(_head.next); + while (index--) + itr++; + return itr._link; +} + +template void List::_destroy() +{ + for (Iterator itr(begin()); itr != end(); ) + { + Node* n = static_cast((itr++)._link); + delete n; + } +} + + +//----------------------------------------------------------------------------- +// management + +template inline U32 List::getSize() const +{ + return _size; +} + +template void List::resize(U32 size) +{ + if (size > _size) + { + while (_size != size) + pushBack(Type()); + } + else + { + while (_size != size) + popBack(); + } +} + +template void List::clear() +{ + _destroy(); + _head.next = &_head; + _head.prev = &_head; + _size = 0; +} + +template inline bool List::isEmpty() const +{ + return _size == 0; +} + + +//----------------------------------------------------------------------------- +// add Nodes + +template typename List::Iterator List::insert(Iterator itr, const Type& x) +{ + Link* node = itr._link; + Node* n = new Node(node->prev,node,x); + node->prev->next = n; + node->prev = n; + _size++; + return n; +} + +template inline typename List::Iterator List::insert(U32 index, const Type& x) +{ + return insert(_node(index),x); +} + +template inline typename List::Iterator List::pushFront(const Type& x) +{ + return insert(_head.next,x); +} + +template inline typename List::Iterator List::pushBack(const Type& x) +{ + return insert(&_head,x); +} + + +//----------------------------------------------------------------------------- +// remove elements + +template void List::erase(Iterator itr) +{ + Node* node = static_cast(itr._link); + node->prev->next = node->next; + node->next->prev = node->prev; + delete node; + _size--; +} + +template inline void List::erase(U32 index) +{ + erase(_node(index)); +} + +template inline void List::popFront() +{ + erase(_head.next); +} + +template inline void List::popBack() +{ + erase(_head.prev); +} + + +//----------------------------------------------------------------------------- +// Iterator access + +template inline typename List::Iterator List::begin() +{ + return _head.next; +} + +template inline typename List::ConstIterator List::begin() const +{ + return _head.next; +} + +template inline typename List::Iterator List::end() +{ + return &_head; +} + +template inline typename List::ConstIterator List::end() const +{ + return &_head; +} + +template inline typename List::Iterator List::rbegin() +{ + return _head.prev; +} + +template inline typename List::ConstIterator List::rbegin() const +{ + return _head.prev; +} + +template inline typename List::Iterator List::rend() +{ + return &_head; +} + +template inline typename List::ConstIterator List::rend() const +{ + return &_head; +} + + +//----------------------------------------------------------------------------- +// type access + +template inline Type& List::first() +{ + return static_cast(_head.next)->value; +} + +template inline const Type& List::first() const +{ + return static_cast(_head.next)->value; +} + +template inline Type& List::last() +{ + return static_cast(_head.prev)->value; +} + +template inline const Type& List::last() const +{ + return static_cast(_head.prev)->value; +} + + +//----------------------------------------------------------------------------- +// operators + +template void List::operator=(const List& p) +{ + if (_size >= p._size) + { + // Destroy the elements we're not going to use and copy the rest + while (_size != p._size) + popBack(); + } + + U32 count = _size; + ConstIterator src = p.begin(); + Iterator dst = begin(); + while (count--) + *dst++ = *src++; + while (src != p.end()) + pushBack(*src++); +} + +template inline Type& List::operator[](U32 index) +{ + return static_cast(_node(index))->value; +} + +template inline const Type& List::operator[](U32 index) const +{ + return static_cast(_node(index))->value; +} + +//----------------------------------------------------------------------------- +// _Iterator class + +template template +inline List::_Iterator::_Iterator() +{ + _link = 0; +} + +template template +inline List::_Iterator::_Iterator(E* ptr) +{ + _link = ptr; +} + +// The checking for _MSC_VER on the ++/-- operators is due to a bug with the VS2008 compiler +// recheck this and remove if fixed with VS2008 SP1 + +template template +#ifdef _MSC_VER +inline typename List:: _Iterator& List::_Iterator::operator++() +#else +inline typename List::template _Iterator& List::_Iterator::operator++() +#endif +{ + _link = _link->next; + return *this; +} + +template template +#ifdef _MSC_VER +inline typename List:: _Iterator List::_Iterator::operator++(int) +#else +inline typename List::template _Iterator List::_Iterator::operator++(int) +#endif +{ + _Iterator itr(*this); + _link = _link->next; + return itr; +} + +template template +#ifdef _MSC_VER +inline typename List:: _Iterator& List::_Iterator::operator--() +#else +inline typename List::template _Iterator& List::_Iterator::operator--() +#endif +{ + _link = _link->prev; + return *this; +} + +template template +#ifdef _MSC_VER +inline typename List:: _Iterator List::_Iterator::operator--(int) +#else +inline typename List::template _Iterator List::_Iterator::operator--(int) +#endif +{ + _Iterator itr(*this); + _link = _link->prev; + return itr; +} + +template template +inline bool List::_Iterator::operator==(const _Iterator& b) const +{ + return _link == b._link; +} + +template template +inline bool List::_Iterator::operator!=(const _Iterator& b) const +{ + return !(_link == b._link); +} + +template template +inline U* List::_Iterator::operator->() const +{ + // Can't use static_cast because of const / non-const versions of Iterator. + // Could use reflection functions, but C-style cast is easier in this case. + return &((Node*)_link)->value; +} + +template template +inline U& List::_Iterator::operator*() const +{ + // Can't use static_cast because of const / non-const versions of Iterator. + // Could use reflection functions, but C-style cast is easier in this case. + return ((Node*)_link)->value; +} + +} // Namespace + +#endif // _TORQUE_LIST_ + diff --git a/Engine/source/core/util/tNamedFactory.h b/Engine/source/core/util/tNamedFactory.h new file mode 100644 index 000000000..5391626b5 --- /dev/null +++ b/Engine/source/core/util/tNamedFactory.h @@ -0,0 +1,83 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TFACTORY_H_ +#define _TFACTORY_H_ + +#ifndef _TDICTIONARY_H_ +#include "core/util/tDictionary.h" +#endif +#ifndef _TORQUE_STRING_H_ +#include "core/util/str.h" +#endif + + +/// A helper template class for registering creation +/// methods to name strings. +template +class NamedFactory +{ +protected: + + typedef Type*(*FactorySig)(); + + typedef Map FactoryMap; + + /// + static FactoryMap& getFactoryMap() + { + static FactoryMap theMap; + return theMap; + } + +public: + + /// Add a new creation method to the factory. + static void add( const char *name, FactorySig func ) + { + getFactoryMap().insert( name, func ); + } + + /// Create an object instance by name. + static Type* create( const char *name ) + { + typename FactoryMap::Iterator iter = getFactoryMap().find( name ); + if ( iter == getFactoryMap().end() ) + return NULL; + + return iter->value(); + } + + /// Create an object instance of the first entry in the factory. + static Type* create() + { + typename FactoryMap::Iterator iter = getFactoryMap().begin(); + if ( iter == getFactoryMap().end() ) + return NULL; + + return iter->value(); + } +}; + + +#endif //_TFACTORY_H_ + diff --git a/Engine/source/core/util/tSignal.cpp b/Engine/source/core/util/tSignal.cpp new file mode 100644 index 000000000..8bc5e8164 --- /dev/null +++ b/Engine/source/core/util/tSignal.cpp @@ -0,0 +1,72 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/util/tSignal.h" + + +void SignalBase::DelegateLink::insert(DelegateLink* node, float order) +{ + // Note: can only legitimately be called on list head + DelegateLink * walk = next; + while (order >= walk->order && walk->next != this) + walk = walk->next; + if (order >= walk->order) + { + // insert after walk + node->prev = walk; + node->next = walk->next; + walk->next->prev = node; + walk->next = node; + } + else + { + // insert before walk + node->prev = walk->prev; + node->next = walk; + walk->prev->next = node; + walk->prev = node; + } + node->order = order; +} + +void SignalBase::DelegateLink::unlink() +{ + // Unlink this node + next->prev = prev; + prev->next = next; +} + +SignalBase::~SignalBase() +{ + removeAll(); +} + +void SignalBase::removeAll() +{ + while (mList.next != &mList) + { + DelegateLink* ptr = mList.next; + ptr->unlink(); + delete ptr; + } +} diff --git a/Engine/source/core/util/tSignal.h b/Engine/source/core/util/tSignal.h new file mode 100644 index 000000000..1fbbb0758 --- /dev/null +++ b/Engine/source/core/util/tSignal.h @@ -0,0 +1,666 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TSIGNAL_H_ +#define _TSIGNAL_H_ + +#ifndef _UTIL_DELEGATE_H_ +#include "core/util/delegate.h" +#endif + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +/// Signals (Multi-cast Delegates) +/// +/// Signals are used throughout this engine to allow subscribers to listen +/// for generated events for various things. +/// +/// Signals are called according to their order parameter (lower +/// numbers first). +/// +/// Signal functions can return bool or void. If bool then returning false +/// from a signal function will cause entries in the ordered signal list after +/// that one to not be called. +/// +/// This allows handlers of a given event to say to the signal generator, "I handled this message, and +/// it is no longer appropriate for other listeners to handle it" + +class SignalBase +{ +public: + + SignalBase() + { + mList.next = mList.prev = &mList; + mList.order = 0.5f; + mTriggerNext = NULL; + } + + ~SignalBase(); + + /// Removes all the delegates from the signal. + void removeAll(); + + /// Returns true if the delegate list is empty. + bool isEmpty() const + { + return mList.next == &mList; + } + +protected: + + struct DelegateLink + { + DelegateLink *next,*prev; + F32 order; + + void insert(DelegateLink* node, F32 order); + void unlink(); + }; + + DelegateLink mList; + + /// We need to protect the delegate list against removal of the currently + /// triggering node as well removal of the next node in the list. When not + /// handling these two cases correctly, removing delegates from a signal + /// while it is triggering will lead to crashes. + /// + /// So this field stores the next node of each active traversal so that when + /// we unlink a node, we can check it against this field and move the traversal + /// along if needed. + Vector mTriggerNext; +}; + +template class SignalBaseT : public SignalBase +{ +public: + + /// The delegate signature for this signal. + typedef Delegate DelegateSig; + + SignalBaseT() {} + + SignalBaseT( const SignalBaseT &base ) + { + mList.next = mList.prev = &mList; + merge( base ); + } + + void operator =( const SignalBaseT &base ) + { + removeAll(); + merge( base ); + } + + void merge( const SignalBaseT &base ) + { + for ( DelegateLink *ptr = base.mList.next; ptr != &base.mList; ptr = ptr->next ) + { + DelegateLinkImpl *del = static_cast( ptr ); + notify( del->mDelegate, del->order ); + } + } + + void notify( const DelegateSig &dlg, F32 order = 0.5f) + { + mList.insert(new DelegateLinkImpl(dlg), order); + } + + void remove( DelegateSig dlg ) + { + for( DelegateLink* ptr = mList.next;ptr != &mList; ptr = ptr->next ) + { + if( DelegateLinkImpl* del = static_cast< DelegateLinkImpl* >( ptr ) ) + { + if( del->mDelegate == dlg ) + { + for ( int i = 0; i < mTriggerNext.size(); i++ ) + { + if( mTriggerNext[i] == ptr ) + mTriggerNext[i] = ptr->next; + } + + del->unlink(); + delete del; + return; + } + } + } + } + + template + void notify(T obj,U func, F32 order = 0.5f) + { + DelegateSig dlg(obj, func); + notify(dlg, order); + } + + template + void notify(T func, F32 order = 0.5f) + { + DelegateSig dlg(func); + notify(dlg, order); + } + + template + void remove(T obj,U func) + { + DelegateSig compDelegate(obj, func); + remove(compDelegate); + } + + template + void remove(T func) + { + DelegateSig compDelegate(func); + remove(compDelegate); + } + + /// Returns true if the signal already contains this delegate. + bool contains( const DelegateSig &dlg ) const + { + for ( DelegateLink *ptr = mList.next; ptr != &mList; ptr = ptr->next ) + { + DelegateLinkImpl *del = static_cast( ptr ); + if ( del->mDelegate == dlg ) + return true; + } + + return false; + } + +protected: + + struct DelegateLinkImpl : public SignalBase::DelegateLink + { + DelegateSig mDelegate; + DelegateLinkImpl(DelegateSig dlg) : mDelegate(dlg) {} + }; + + DelegateSig & getDelegate(SignalBase::DelegateLink * link) + { + return ((DelegateLinkImpl*)link)->mDelegate; + } +}; + +//----------------------------------------------------------------------------- + +template class Signal; + +// Short-circuit signal implementations + +template<> +class Signal : public SignalBaseT +{ + public: + + bool trigger() + { + this->mTriggerNext.push_back(NULL); + for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) + { + this->mTriggerNext.last() = ptr->next; + if( !this->getDelegate( ptr )() ) + { + this->mTriggerNext.pop_back(); + return false; + } + ptr = this->mTriggerNext.last(); + } + this->mTriggerNext.pop_back(); + return true; + } +}; + +template +class Signal : public SignalBaseT +{ + public: + + bool trigger( A a ) + { + this->mTriggerNext.push_back(NULL); + for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) + { + this->mTriggerNext.last() = ptr->next; + if( !this->getDelegate( ptr )( a ) ) + { + this->mTriggerNext.pop_back(); + return false; + } + ptr = this->mTriggerNext.last(); + } + this->mTriggerNext.pop_back(); + return true; + } +}; + +template +class Signal : public SignalBaseT +{ + public: + + bool trigger( A a, B b ) + { + this->mTriggerNext.push_back(NULL); + for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) + { + this->mTriggerNext.last() = ptr->next; + if( !this->getDelegate( ptr )( a, b ) ) + { + this->mTriggerNext.pop_back(); + return false; + } + ptr = this->mTriggerNext.last(); + } + this->mTriggerNext.pop_back(); + return true; + } +}; + +template +class Signal : public SignalBaseT +{ + public: + + bool trigger( A a, B b, C c ) + { + this->mTriggerNext.push_back(NULL); + for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) + { + this->mTriggerNext.last() = ptr->next; + if( !this->getDelegate( ptr )( a, b, c ) ) + { + this->mTriggerNext.pop_back(); + return false; + } + ptr = this->mTriggerNext.last(); + } + this->mTriggerNext.pop_back(); + return true; + } +}; + +template +class Signal : public SignalBaseT +{ + public: + + bool trigger( A a, B b, C c, D d ) + { + this->mTriggerNext.push_back(NULL); + for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) + { + this->mTriggerNext.last() = ptr->next; + if( !this->getDelegate( ptr )( a, b, c, d ) ) + { + this->mTriggerNext.pop_back(); + return false; + } + ptr = this->mTriggerNext.last(); + } + this->mTriggerNext.pop_back(); + return true; + } +}; + +template +class Signal : public SignalBaseT +{ + public: + + bool trigger( A a, B b, C c, D d, E e ) + { + this->mTriggerNext.push_back(NULL); + for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) + { + this->mTriggerNext.last() = ptr->next; + if( !this->getDelegate( ptr )( a, b, c, d, e ) ) + { + this->mTriggerNext.pop_back(); + return false; + } + ptr = this->mTriggerNext.last(); + } + this->mTriggerNext.pop_back(); + return true; + } +}; + +template +class Signal : public SignalBaseT +{ + public: + + bool trigger( A a, B b, C c, D d, E e, F f ) + { + this->mTriggerNext.push_back(NULL); + for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) + { + this->mTriggerNext.last() = ptr->next; + if( !this->getDelegate( ptr )( a, b, c, d, e, f ) ) + { + this->mTriggerNext.pop_back(); + return false; + } + ptr = this->mTriggerNext.last(); + } + this->mTriggerNext.pop_back(); + return true; + } +}; + +template +class Signal : public SignalBaseT +{ + public: + + bool trigger( A a, B b, C c, D d, E e, F f, G g ) + { + this->mTriggerNext.push_back(NULL); + for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) + { + this->mTriggerNext.last() = ptr->next; + if( !this->getDelegate( ptr )( a, b, c, d, e, f, g ) ) + { + this->mTriggerNext.pop_back(); + return false; + } + ptr = this->mTriggerNext.last(); + } + this->mTriggerNext.pop_back(); + return true; + } +}; + +template +class Signal : public SignalBaseT +{ + public: + + bool trigger( A a, B b, C c, D d, E e, F f, G g, H h ) + { + this->mTriggerNext.push_back(NULL); + for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) + { + this->mTriggerNext.last() = ptr->next; + if( !this->getDelegate( ptr )( a, b, c, d, e, f, g, h ) ) + { + this->mTriggerNext.pop_back(); + return false; + } + ptr = this->mTriggerNext.last(); + } + this->mTriggerNext.pop_back(); + return true; + } +}; + +template +class Signal : public SignalBaseT +{ + public: + + bool trigger( A a, B b, C c, D d, E e, F f, G g, H h, I i ) + { + this->mTriggerNext.push_back(NULL); + for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) + { + this->mTriggerNext.last() = ptr->next; + if( !this->getDelegate( ptr )( a, b, c, d, e, f, g, h, i ) ) + { + this->mTriggerNext.pop_back(); + return false; + } + ptr = this->mTriggerNext.last(); + } + this->mTriggerNext.pop_back(); + return true; + } +}; + +template +class Signal : public SignalBaseT +{ + public: + + bool trigger( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j ) + { + this->mTriggerNext.push_back(NULL); + for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) + { + this->mTriggerNext.last() = ptr->next; + if( !this->getDelegate( ptr )( a, b, c, d, e, f, g, h, i, j ) ) + { + this->mTriggerNext.pop_back(); + return false; + } + ptr = this->mTriggerNext.last(); + } + this->mTriggerNext.pop_back(); + return true; + } +}; + +// Non short-circuit signal implementations + +template<> +class Signal : public SignalBaseT +{ + public: + + void trigger() + { + this->mTriggerNext.push_back(NULL); + for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) + { + this->mTriggerNext.last() = ptr->next; + this->getDelegate( ptr )(); + ptr = this->mTriggerNext.last(); + } + this->mTriggerNext.pop_back(); + } +}; + +template +class Signal : public SignalBaseT +{ + public: + + void trigger( A a ) + { + this->mTriggerNext.push_back(NULL); + for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) + { + this->mTriggerNext.last() = ptr->next; + this->getDelegate( ptr )( a ); + ptr = this->mTriggerNext.last(); + } + this->mTriggerNext.pop_back(); + } +}; + +template +class Signal : public SignalBaseT +{ + public: + + void trigger( A a, B b ) + { + this->mTriggerNext.push_back(NULL); + for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) + { + this->mTriggerNext.last() = ptr->next; + this->getDelegate( ptr )( a, b ); + ptr = this->mTriggerNext.last(); + } + this->mTriggerNext.pop_back(); + } +}; + +template +class Signal : public SignalBaseT +{ + public: + + void trigger( A a, B b, C c ) + { + this->mTriggerNext.push_back(NULL); + for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) + { + this->mTriggerNext.last() = ptr->next; + this->getDelegate( ptr )( a, b, c ); + ptr = this->mTriggerNext.last(); + } + this->mTriggerNext.pop_back(); + } +}; + +template +class Signal : public SignalBaseT +{ + public: + + void trigger( A a, B b, C c, D d ) + { + this->mTriggerNext.push_back(NULL); + for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) + { + this->mTriggerNext.last() = ptr->next; + this->getDelegate( ptr )( a, b, c, d ); + ptr = this->mTriggerNext.last(); + } + this->mTriggerNext.pop_back(); + } +}; + +template +class Signal : public SignalBaseT +{ + public: + + void trigger( A a, B b, C c, D d, E e ) + { + this->mTriggerNext.push_back(NULL); + for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) + { + this->mTriggerNext.last() = ptr->next; + this->getDelegate( ptr )( a, b, c, d, e ); + ptr = this->mTriggerNext.last(); + } + this->mTriggerNext.pop_back(); + } +}; + +template +class Signal : public SignalBaseT +{ + public: + + void trigger( A a, B b, C c, D d, E e, F f ) + { + this->mTriggerNext.push_back(NULL); + for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) + { + this->mTriggerNext.last() = ptr->next; + this->getDelegate( ptr )( a, b, c, d, e, f ); + ptr = this->mTriggerNext.last(); + } + this->mTriggerNext.pop_back(); + } +}; + +template +class Signal : public SignalBaseT +{ + public: + + void trigger( A a, B b, C c, D d, E e, F f, G g ) + { + this->mTriggerNext.push_back(NULL); + for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) + { + this->mTriggerNext.last() = ptr->next; + this->getDelegate( ptr )( a, b, c, d, e, f, g ); + ptr = this->mTriggerNext.last(); + } + this->mTriggerNext.pop_back(); + } +}; + +template +class Signal : public SignalBaseT +{ + public: + + void trigger( A a, B b, C c, D d, E e, F f, G g, H h ) + { + this->mTriggerNext.push_back(NULL); + for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) + { + this->mTriggerNext.last() = ptr->next; + this->getDelegate( ptr )( a, b, c, d, e, f, g, h ); + ptr = this->mTriggerNext.last(); + } + this->mTriggerNext.pop_back(); + } +}; + +template +class Signal : public SignalBaseT +{ + public: + + void trigger( A a, B b, C c, D d, E e, F f, G g, H h, I i ) + { + this->mTriggerNext.push_back(NULL); + for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) + { + this->mTriggerNext.last() = ptr->next; + this->getDelegate( ptr )( a, b, c, d, e, f, g, h, i ); + ptr = this->mTriggerNext.last(); + } + this->mTriggerNext.pop_back(); + } +}; + +template +class Signal : public SignalBaseT +{ + public: + + void trigger( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j ) + { + this->mTriggerNext.push_back(NULL); + for( SignalBase::DelegateLink* ptr = this->mList.next; ptr != &this->mList; ) + { + this->mTriggerNext.last() = ptr->next; + this->getDelegate( ptr )( a, b, c, d, e, f, g, h, i, j ); + ptr = this->mTriggerNext.last(); + } + this->mTriggerNext.pop_back(); + } +}; + +#endif // _TSIGNAL_H_ diff --git a/Engine/source/core/util/tSingleton.h b/Engine/source/core/util/tSingleton.h new file mode 100644 index 000000000..14790d3c2 --- /dev/null +++ b/Engine/source/core/util/tSingleton.h @@ -0,0 +1,151 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TSINGLETON_H_ +#define _TSINGLETON_H_ + +#ifndef _PLATFORMASSERT_H_ +#include "platform/platformAssert.h" +#endif + +/// This is a simple thread safe singleton class based on the +/// design of boost::singleton_default (see http://www.boost.org/). +/// +/// This singleton is guaranteed to be constructed before main() is called +/// and destroyed after main() exits. It will also be created on demand +/// if Singleton::instance() is called before main() begins. +/// +/// This thread safety only works within the execution context of main(). +/// Access to the singleton from multiple threads outside of main() is +/// is not guaranteed to be thread safe. +/// +/// To create a singleton you only need to access it once in your code: +/// +/// Singleton::instance()->myFunction(); +/// +/// You do not need to derive from this class. +/// +/// @note Generally stay away from this class (!!) except if your class T +/// has no meaningful constructor. Otherwise, it is very easy to make +/// execution of global ctors ordering dependent. +template +class Singleton +{ +private: + + // This is the creator object which ensures that the + // singleton is created before main() begins. + struct SingletonCreator + { + SingletonCreator() { Singleton::instance(); } + + // This dummy function is used below to force + // singleton creation at startup. + inline void forceSafeCreate() const {} + }; + + // The creator object instance. + static SingletonCreator smSingletonCreator; + + /// This is private on purpose. + Singleton(); + +public: + + /// Returns the singleton instance. + static T* instance() + { + // The singleton. + static T theSingleton; + + // This is here to force the compiler to create + // the singleton before main() is called. + smSingletonCreator.forceSafeCreate(); + + return &theSingleton; + } + +}; + +template +typename Singleton::SingletonCreator Singleton::smSingletonCreator; + + +/// This is a managed singleton class with explict creation +/// and destruction functions which must be called at startup +/// and shutdown of the engine. +/// +/// Your class to be managed must implement the following +/// function: +/// +/// static const char* getSingletonName() { return "YourClassName"; } +/// +/// This allow us to provide better asserts. +/// +template +class ManagedSingleton +{ +private: + + static T *smSingleton; + +public: + + /// Create the singleton instance. + /// @note Asserts when the singleton instance has already been constructed. + static void createSingleton() + { + AssertFatal( smSingleton == NULL, String::ToString( "%s::createSingleton() - The singleton is already created!", T::getSingletonName() ) ); + smSingleton = new T(); + } + + /// Destroy the singleton instance. + /// @note Asserts when no singleton has been constructed. + static void deleteSingleton() + { + AssertFatal( smSingleton, String::ToString( "%s::deleteSingleton() - The singleton doest not exist!", T::getSingletonName() ) ); + delete smSingleton; + smSingleton = NULL; + } + + /// Return the singleton instance. + /// @note Asserts when called before createSingleton(). + static T* instance() + { + AssertFatal( smSingleton, String::ToString( "%s::instance() - The singleton has not been created!", T::getSingletonName() ) ); + return smSingleton; + } + + /// Return the singleton instance or NULL if it has been deleted or not yet constructed. + static T* instanceOrNull() + { + return smSingleton; + } +}; + +/// +template +T* ManagedSingleton::smSingleton = NULL; + + +#endif //_TSINGLETON_H_ + diff --git a/Engine/source/core/util/tUnmanagedVector.h b/Engine/source/core/util/tUnmanagedVector.h new file mode 100644 index 000000000..bc989403f --- /dev/null +++ b/Engine/source/core/util/tUnmanagedVector.h @@ -0,0 +1,95 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TUNMANAGEDVECTOR_H_ +#define _TUNMANAGEDVECTOR_H_ + + +/// An array that does not manage the memory it gets passed. Conversely, the +/// array cannot be enlarged. +/// +/// @note As the array does not manage the instances it uses, it will also not +/// destruct them when it is itself destructed. +template< typename T > +class UnmanagedVector +{ + protected: + + U32 mCount; + T* mArray; + + public: + + typedef T* iterator; + typedef const T* const_iterator; + + UnmanagedVector() + : mCount( 0 ), mArray( NULL ) {} + UnmanagedVector( T* array, U32 count ) + : mArray( array ), mCount( count ) {} + + U32 size() const { return mCount; } + bool empty() const { return ( mCount == 0 ); } + const T* address() const { return mArray; } + T* address() { return mArray; } + + void clear() { mCount = 0; mArray = NULL; } + + iterator begin() { return &mArray[ 0 ]; } + iterator end() { return &mArray[ mCount ]; } + const_iterator begin() const { return &mArray[ 0 ]; } + const_iterator end() const { return &mArray[ mCount ]; } + + const T& first() const + { + AssertFatal( !empty(), "UnmanagedVector::first - Vector is empty" ); + return mArray[ 0 ]; + } + T& first() + { + AssertFatal( !empty(), "UnmanagedVector::first - Vector is empty" ); + return mArray[ 0 ]; + } + const T& last() const + { + AssertFatal( !empty(), "UnmanagedVector::last - Vector is empty" ); + return mArray[ mCount - 1 ]; + } + T& last() + { + AssertFatal( !empty(), "UnmanagedVector::last - Vector is empty" ); + return mArray[ mCount - 1 ]; + } + + const T& operator []( U32 index ) const + { + AssertFatal( index <= size(), "UnmanagedVector::operator[] - Index out of range" ); + return mArray[ index ]; + } + T& operator []( U32 index ) + { + AssertFatal( index <= size(), "UnmanagedVector::operator[] - Index out of range" ); + return mArray[ index ]; + } +}; + +#endif // !_TUNMANAGEDVECTOR_H_ diff --git a/Engine/source/core/util/tVector.cpp b/Engine/source/core/util/tVector.cpp new file mode 100644 index 000000000..42ef4b21a --- /dev/null +++ b/Engine/source/core/util/tVector.cpp @@ -0,0 +1,101 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/util/tVector.h" + +#include "platform/profiler.h" + + +#ifdef TORQUE_DEBUG_GUARD + +bool VectorResize(U32 *aSize, U32 *aCount, void **arrayPtr, U32 newCount, U32 elemSize, + const char* fileName, + const U32 lineNum) +{ + PROFILE_SCOPE( VectorResize ); + + if (newCount > 0) + { + U32 blocks = newCount / VectorBlockSize; + if (newCount % VectorBlockSize) + blocks++; + S32 mem_size = blocks * VectorBlockSize * elemSize; + + const char* pUseFileName = fileName != NULL ? fileName : __FILE__; + U32 useLineNum = fileName != NULL ? lineNum : __LINE__; + + if (*arrayPtr != NULL) + *arrayPtr = dRealloc_r(*arrayPtr, mem_size, pUseFileName, useLineNum); + else + *arrayPtr = dMalloc_r(mem_size, pUseFileName, useLineNum); + + AssertFatal( *arrayPtr, "VectorResize - Allocation failed." ); + + *aCount = newCount; + *aSize = blocks * VectorBlockSize; + return true; + } + + if (*arrayPtr) + { + dFree(*arrayPtr); + *arrayPtr = 0; + } + + *aSize = 0; + *aCount = 0; + return true; +} + +#else + +bool VectorResize(U32 *aSize, U32 *aCount, void **arrayPtr, U32 newCount, U32 elemSize) +{ + PROFILE_SCOPE( VectorResize ); + + if (newCount > 0) + { + U32 blocks = newCount / VectorBlockSize; + if (newCount % VectorBlockSize) + blocks++; + S32 mem_size = blocks * VectorBlockSize * elemSize; + *arrayPtr = *arrayPtr ? dRealloc(*arrayPtr,mem_size) : + dMalloc(mem_size); + + *aCount = newCount; + *aSize = blocks * VectorBlockSize; + return true; + } + + if (*arrayPtr) + { + dFree(*arrayPtr); + *arrayPtr = 0; + } + + *aSize = 0; + *aCount = 0; + return true; +} + +#endif diff --git a/Engine/source/core/util/tVector.h b/Engine/source/core/util/tVector.h new file mode 100644 index 000000000..457ac5245 --- /dev/null +++ b/Engine/source/core/util/tVector.h @@ -0,0 +1,964 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TVECTOR_H_ +#define _TVECTOR_H_ + +// TODO: This shouldn't be included in headers... it should +// be included by the source file before all other includes. +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif + +//----------------------------------------------------------------------------- +// Helper definitions for the vector class. + +/// Size of memory blocks to allocate at a time for vectors. +const static S32 VectorBlockSize = 16; +#ifdef TORQUE_DEBUG_GUARD +extern bool VectorResize(U32 *aSize, U32 *aCount, void **arrayPtr, U32 newCount, U32 elemSize, + const char* fileName, + const U32 lineNum); +#else +extern bool VectorResize(U32 *aSize, U32 *aCount, void **arrayPtr, U32 newCount, U32 elemSize); +#endif + +/// Use the following macro to bind a vector to a particular line +/// of the owning class for memory tracking purposes +#ifdef TORQUE_DEBUG_GUARD +#define VECTOR_SET_ASSOCIATION(x) x.setFileAssociation(__FILE__, __LINE__) +#else +#define VECTOR_SET_ASSOCIATION(x) +#endif + +// ============================================================================= +/// A dynamic array template class. +/// +/// The vector grows as you insert or append +/// elements. Insertion is fastest at the end of the array. Resizing +/// of the array can be avoided by pre-allocating space using the +/// reserve() method. +/// +/// @nosubgrouping +template +class Vector +{ + protected: + U32 mElementCount; ///< Number of elements currently in the Vector. + U32 mArraySize; ///< Number of elements allocated for the Vector. + T* mArray; ///< Pointer to the Vector elements. + +#ifdef TORQUE_DEBUG_GUARD + const char* mFileAssociation; + U32 mLineAssociation; +#endif + + bool resize(U32); // resizes, but does no construction/destruction + void destroy(U32 start, U32 end); ///< Destructs elements from start to end-1 + void construct(U32 start, U32 end); ///< Constructs elements from start to end-1 + void construct(U32 start, U32 end, const T* array); + public: + Vector(const U32 initialSize = 0); + Vector(const U32 initialSize, const char* fileName, const U32 lineNum); + Vector(const char* fileName, const U32 lineNum); + Vector(const Vector&); + ~Vector(); + +#ifdef TORQUE_DEBUG_GUARD + void setFileAssociation(const char* file, const U32 line); +#endif + + /// @name STL interface + /// @{ + + typedef T value_type; + typedef T& reference; + typedef const T& const_reference; + + typedef T* iterator; + typedef const T* const_iterator; + typedef S32 difference_type; + typedef U32 size_type; + + typedef difference_type (QSORT_CALLBACK *compare_func)(const T *a, const T *b); + + Vector& operator=(const Vector& p); + + iterator begin(); + const_iterator begin() const; + iterator end(); + const_iterator end() const; + + S32 size() const; + bool empty() const; + bool contains(const T&) const; + + void insert(iterator, const T&); + void erase(iterator); + + T& front(); + const T& front() const; + T& back(); + const T& back() const; + + void push_front(const T&); + void push_back(const T&); + U32 push_front_unique(const T&); + U32 push_back_unique(const T&); + S32 find_next( const T&, U32 start = 0 ) const; + void pop_front(); + void pop_back(); + + T& operator[](U32); + const T& operator[](U32) const; + + T& operator[](S32 i) { return operator[](U32(i)); } + const T& operator[](S32 i ) const { return operator[](U32(i)); } + + void reserve(U32); + U32 capacity() const; + + /// @} + + /// @name Extended interface + /// @{ + + U32 memSize() const; + T* address() const; + U32 setSize(U32); + void increment(); + void decrement(); + void increment(U32); + void decrement(U32); + void insert(U32); + void insert(U32, const T&); + void erase(U32); + void erase_fast(U32); + void erase(U32 index, U32 count); + void erase_fast(iterator); + void clear(); + void compact(); + void sort(compare_func f); + void fill( const T& value ); + + /// Finds the first matching element and erases it. + /// @return Returns true if a match is found. + bool remove( const T& ); + + T& first(); + T& last(); + const T& first() const; + const T& last() const; + + void set(void * addr, U32 sz); + + /// Appends the content of the vector to this one. + void merge( const Vector &p ); + + /// Appends the content of the array to the vector. + /// + /// @param addr A pointer to the first item of the array to merge. + /// @param count The number of elements in the array to merge. + /// + void merge( const T *addr, U32 count ); + + /// @} +}; + +template inline Vector::~Vector() +{ + clear(); + dFree(mArray); +} + +template inline Vector::Vector(const U32 initialSize) +{ +#ifdef TORQUE_DEBUG_GUARD + mFileAssociation = NULL; + mLineAssociation = 0; +#endif + + mArray = 0; + mElementCount = 0; + mArraySize = 0; + if(initialSize) + reserve(initialSize); +} + +template inline Vector::Vector(const U32 initialSize, + const char* fileName, + const U32 lineNum) +{ +#ifdef TORQUE_DEBUG_GUARD + mFileAssociation = fileName; + mLineAssociation = lineNum; +#else +// TORQUE_UNUSED(fileName); +// TORQUE_UNUSED(lineNum); +#endif + + mArray = 0; + mElementCount = 0; + mArraySize = 0; + if(initialSize) + reserve(initialSize); +} + +template inline Vector::Vector(const char* fileName, + const U32 lineNum) +{ +#ifdef TORQUE_DEBUG_GUARD + mFileAssociation = fileName; + mLineAssociation = lineNum; +#else +// TORQUE_UNUSED(fileName); +// TORQUE_UNUSED(lineNum); +#endif + + mArray = 0; + mElementCount = 0; + mArraySize = 0; +} + +template inline Vector::Vector(const Vector& p) +{ +#ifdef TORQUE_DEBUG_GUARD + mFileAssociation = p.mFileAssociation; + mLineAssociation = p.mLineAssociation; +#endif + + mArray = 0; + resize(p.mElementCount); + construct(0, p.mElementCount, p.mArray); +} + + +#ifdef TORQUE_DEBUG_GUARD +template inline void Vector::setFileAssociation(const char* file, + const U32 line) +{ + mFileAssociation = file; + mLineAssociation = line; +} +#endif + +template inline void Vector::destroy(U32 start, U32 end) // destroys from start to end-1 +{ + // This check is a little generous as we can legitimately get (0,0) as + // our parameters... so it won't detect every invalid case but it does + // remain simple. + AssertFatal(start <= mElementCount && end <= mElementCount, "Vector::destroy - out of bounds start/end."); + + // destroys from start to end-1 + while(start < end) + destructInPlace(&mArray[start++]); +} + +template inline void Vector::construct(U32 start, U32 end) // destroys from start to end-1 +{ + AssertFatal(start <= mElementCount && end <= mElementCount, "Vector::construct - out of bounds start/end."); + while(start < end) + constructInPlace(&mArray[start++]); +} + +template inline void Vector::construct(U32 start, U32 end, const T* array) // destroys from start to end-1 +{ + AssertFatal(start <= mElementCount && end <= mElementCount, "Vector::construct - out of bounds start/end."); + while(start < end) + { + constructInPlace(&mArray[start], &array[start]); + start++; + } +} + +template inline U32 Vector::memSize() const +{ + return capacity() * sizeof(T); +} + +template inline T* Vector::address() const +{ + return mArray; +} + +template inline U32 Vector::setSize(U32 size) +{ + const U32 oldSize = mElementCount; + + if(size > mElementCount) + { + if (size > mArraySize) + resize(size); + + // Set count first so we are in a valid state for construct. + mElementCount = size; + construct(oldSize, size); + } + else if(size < mElementCount) + { + destroy(size, oldSize); + mElementCount = size; + } + + return mElementCount; +} + +template inline void Vector::increment() +{ + if(mElementCount == mArraySize) + resize(mElementCount + 1); + else + mElementCount++; + constructInPlace(&mArray[mElementCount - 1]); +} + +template inline void Vector::decrement() +{ + AssertFatal(mElementCount != 0, "Vector::decrement - cannot decrement zero-length vector."); + mElementCount--; + destructInPlace(&mArray[mElementCount]); +} + +template inline void Vector::increment(U32 delta) +{ + U32 count = mElementCount; + if ((mElementCount += delta) > mArraySize) + resize(mElementCount); + construct(count, mElementCount); +} + +template inline void Vector::decrement(U32 delta) +{ + AssertFatal(mElementCount != 0, "Vector::decrement - cannot decrement zero-length vector."); + + const U32 count = mElementCount; + + // Determine new count after decrement... + U32 newCount = mElementCount; + if (mElementCount > delta) + newCount -= delta; + else + newCount = 0; + + // Destruct removed items... + destroy(newCount, count); + + // Note new element count. + mElementCount = newCount; +} + +template inline void Vector::insert(U32 index) +{ + AssertFatal(index <= mElementCount, "Vector::insert - out of bounds index."); + + if(mElementCount == mArraySize) + resize(mElementCount + 1); + else + mElementCount++; + + dMemmove(&mArray[index + 1], + &mArray[index], + (mElementCount - index - 1) * sizeof(value_type)); + + constructInPlace(&mArray[index]); +} + +template inline void Vector::insert(U32 index,const T& x) +{ + insert(index); + mArray[index] = x; +} + +template inline void Vector::erase(U32 index) +{ + AssertFatal(index < mElementCount, "Vector::erase - out of bounds index!"); + + destructInPlace(&mArray[index]); + + if (index < (mElementCount - 1)) + { + dMemmove(&mArray[index], + &mArray[index + 1], + (mElementCount - index - 1) * sizeof(value_type)); + } + + mElementCount--; +} + +template inline bool Vector::remove( const T& x ) +{ + iterator i = begin(); + while (i != end()) + { + if (*i == x) + { + erase( i ); + return true; + } + + i++; + } + + return false; +} + +template inline void Vector::erase(U32 index, U32 count) +{ + AssertFatal(index < mElementCount, "Vector::erase - out of bounds index!"); + AssertFatal(count > 0, "Vector::erase - count must be greater than zero!"); + AssertFatal(index+count <= mElementCount, "Vector::erase - out of bounds count!"); + + destroy( index, index+count ); + + dMemmove( &mArray[index], + &mArray[index + count], + (mElementCount - index - count) * sizeof(value_type)); + + mElementCount -= count; +} + +template inline void Vector::erase_fast(U32 index) +{ + AssertFatal(index < mElementCount, "Vector::erase_fast - out of bounds index."); + + // CAUTION: this operator does NOT maintain list order + // Copy the last element into the deleted 'hole' and decrement the + // size of the vector. + destructInPlace(&mArray[index]); + if (index < (mElementCount - 1)) + dMemmove(&mArray[index], &mArray[mElementCount - 1], sizeof(value_type)); + mElementCount--; +} + +template inline bool Vector::contains(const T& x) const +{ + const_iterator i = begin(); + while (i != end()) + { + if (*i == x) + return true; + + i++; + } + + return false; +} + +template< class T > inline void Vector< T >::fill( const T& value ) +{ + for( U32 i = 0; i < size(); ++ i ) + mArray[ i ] = value; +} + +template inline T& Vector::first() +{ + AssertFatal(mElementCount != 0, "Vector::first - Error, no first element of a zero sized array!"); + return mArray[0]; +} + +template inline const T& Vector::first() const +{ + AssertFatal(mElementCount != 0, "Vector::first - Error, no first element of a zero sized array! (const)"); + return mArray[0]; +} + +template inline T& Vector::last() +{ + AssertFatal(mElementCount != 0, "Vector::last - Error, no last element of a zero sized array!"); + return mArray[mElementCount - 1]; +} + +template inline const T& Vector::last() const +{ + AssertFatal(mElementCount != 0, "Vector::last - Error, no last element of a zero sized array! (const)"); + return mArray[mElementCount - 1]; +} + +template inline void Vector::clear() +{ + destroy(0, mElementCount); + mElementCount = 0; +} + +template inline void Vector::compact() +{ + resize(mElementCount); +} + +typedef int (QSORT_CALLBACK *qsort_compare_func)(const void *, const void *); + +template inline void Vector::sort(compare_func f) +{ + qsort(address(), size(), sizeof(T), (qsort_compare_func) f); +} + +//----------------------------------------------------------------------------- + +template inline Vector& Vector::operator=(const Vector& p) +{ + if(mElementCount > p.mElementCount) + { + destroy(p.mElementCount, mElementCount); + } + + U32 count = getMin( mElementCount, p.mElementCount ); + U32 i; + for( i=0; i < count; i++ ) + { + mArray[i] = p.mArray[i]; + } + + resize( p.mElementCount ); + + if( i < p.mElementCount ) + { + construct(i, p.mElementCount, p.mArray); + } + return *this; +} + +template inline typename Vector::iterator Vector::begin() +{ + return mArray; +} + +template inline typename Vector::const_iterator Vector::begin() const +{ + return mArray; +} + +template inline typename Vector::iterator Vector::end() +{ + return mArray + mElementCount; +} + +template inline typename Vector::const_iterator Vector::end() const +{ + return mArray +mElementCount; +} + +template inline S32 Vector::size() const +{ + return (S32)mElementCount; +} + +template inline bool Vector::empty() const +{ + return (mElementCount == 0); +} + +template inline void Vector::insert(iterator p,const T& x) +{ + U32 index = (U32) (p - mArray); + insert(index); + mArray[index] = x; +} + +template inline void Vector::erase(iterator q) +{ + erase(U32(q - mArray)); +} + +template inline void Vector::erase_fast(iterator q) +{ + erase_fast(U32(q - mArray)); +} + +template inline T& Vector::front() +{ + return *begin(); +} + +template inline const T& Vector::front() const +{ + return *begin(); +} + +template inline T& Vector::back() +{ + AssertFatal(mElementCount != 0, "Vector::back - cannot access last element of zero-length vector."); + return *(end()-1); +} + +template inline const T& Vector::back() const +{ + AssertFatal(mElementCount != 0, "Vector::back - cannot access last element of zero-length vector."); + return *(end()-1); +} + +template inline void Vector::push_front(const T& x) +{ + insert(0); + mArray[0] = x; +} + +template inline void Vector::push_back(const T& x) +{ + increment(); + mArray[mElementCount - 1] = x; +} + +template inline U32 Vector::push_front_unique(const T& x) +{ + S32 index = find_next(x); + + if (index == -1) + { + index = 0; + + insert(index); + mArray[index] = x; + } + + return index; +} + +template inline U32 Vector::push_back_unique(const T& x) +{ + S32 index = find_next(x); + + if (index == -1) + { + increment(); + + index = mElementCount - 1; + mArray[index] = x; + } + + return index; +} + +template inline S32 Vector::find_next( const T& x, U32 start ) const +{ + S32 index = -1; + + if (start < mElementCount) + { + for (U32 i = start; i < mElementCount; i++) + { + if (mArray[i] == x) + { + index = i; + break; + } + } + } + + return index; +} + +template inline void Vector::pop_front() +{ + AssertFatal(mElementCount != 0, "Vector::pop_front - cannot pop the front of a zero-length vector."); + erase(U32(0)); +} + +template inline void Vector::pop_back() +{ + AssertFatal(mElementCount != 0, "Vector::pop_back - cannot pop the back of a zero-length vector."); + decrement(); +} + +template inline T& Vector::operator[](U32 index) +{ + AssertFatal(index < mElementCount, "Vector::operator[] - out of bounds array access!"); + return mArray[index]; +} + +template inline const T& Vector::operator[](U32 index) const +{ + AssertFatal(index < mElementCount, "Vector::operator[] - out of bounds array access!"); + return mArray[index]; +} + +template inline void Vector::reserve(U32 size) +{ + if (size <= mArraySize) + return; + + const U32 ec = mElementCount; + if (resize(size)) + mElementCount = ec; +} + +template inline U32 Vector::capacity() const +{ + return mArraySize; +} + +template inline void Vector::set(void * addr, U32 sz) +{ + if ( !addr ) + sz = 0; + + setSize( sz ); + + if ( addr && sz > 0 ) + dMemcpy(address(),addr,sz*sizeof(T)); +} + +//----------------------------------------------------------------------------- + +template inline bool Vector::resize(U32 ecount) +{ +#ifdef TORQUE_DEBUG_GUARD + return VectorResize(&mArraySize, &mElementCount, (void**) &mArray, ecount, sizeof(T), + mFileAssociation, mLineAssociation); +#else + return VectorResize(&mArraySize, &mElementCount, (void**) &mArray, ecount, sizeof(T)); +#endif +} + +template inline void Vector::merge( const Vector &p ) +{ + if ( !p.size() ) + return; + + const U32 oldSize = mElementCount; + const U32 newSize = oldSize + p.size(); + if ( newSize > mArraySize ) + resize( newSize ); + + T *dest = mArray + oldSize; + const T *src = p.mArray; + while ( dest < mArray + newSize ) + constructInPlace( dest++, src++ ); + + mElementCount = newSize; +} + +template inline void Vector::merge( const T *addr, U32 count ) +{ + const U32 oldSize = mElementCount; + const U32 newSize = oldSize + count; + if ( newSize > mArraySize ) + resize( newSize ); + + T *dest = mArray + oldSize; + while ( dest < mArray + newSize ) + constructInPlace( dest++, addr++ ); + + mElementCount = newSize; +} + +//----------------------------------------------------------------------------- +/// Template for vectors of pointers. +template +class VectorPtr : public Vector +{ + /// @deprecated Disallowed. + VectorPtr(const VectorPtr&); // Disallowed + + public: + VectorPtr(); + VectorPtr(const char* fileName, const U32 lineNum); + + /// @name STL interface + /// @{ + + typedef T value_type; + typedef T& reference; + typedef const T& const_reference; + + typedef T* iterator; + typedef const T* const_iterator; + typedef U32 difference_type; + typedef U32 size_type; + + iterator begin(); + const_iterator begin() const; + iterator end(); + const_iterator end() const; + + void insert(iterator,const T&); + void insert(int idx) { Parent::insert(idx); } + void erase(iterator); + + T& front(); + const T& front() const; + T& back(); + const T& back() const; + void push_front(const T&); + void push_back(const T&); + + T& operator[](U32); + const T& operator[](U32) const; + + /// @} + + /// @name Extended interface + /// @{ + + typedef Vector Parent; + T& first(); + T& last(); + const T& first() const; + const T& last() const; + void erase_fast(U32); + void erase_fast(iterator); + + /// @} +}; + + +//----------------------------------------------------------------------------- +template inline VectorPtr::VectorPtr() +{ + // +} + +template inline VectorPtr::VectorPtr(const char* fileName, + const U32 lineNum) + : Vector(fileName, lineNum) +{ + // +} + +template inline T& VectorPtr::first() +{ + return (T&)Parent::first(); +} + +template inline const T& VectorPtr::first() const +{ + return (const T)Parent::first(); +} + +template inline T& VectorPtr::last() +{ + return (T&)Parent::last(); +} + +template inline const T& VectorPtr::last() const +{ + return (const T&)Parent::last(); +} + +template inline typename VectorPtr::iterator VectorPtr::begin() +{ + return (iterator)Parent::begin(); +} + +template inline typename VectorPtr::const_iterator VectorPtr::begin() const +{ + return (const_iterator)Parent::begin(); +} + +template inline typename VectorPtr::iterator VectorPtr::end() +{ + return (iterator)Parent::end(); +} + +template inline typename VectorPtr::const_iterator VectorPtr::end() const +{ + return (const_iterator)Parent::end(); +} + +template inline void VectorPtr::insert(iterator i,const T& x) +{ + Parent::insert( (Parent::iterator)i, (Parent::reference)x ); +} + +template inline void VectorPtr::erase(iterator i) +{ + Parent::erase( (Parent::iterator)i ); +} + +template inline void VectorPtr::erase_fast(U32 index) +{ + AssertFatal(index < mElementCount, "VectorPtr::erase_fast - out of bounds index." ); + + // CAUTION: this operator does not maintain list order + // Copy the last element into the deleted 'hole' and decrement the + // size of the vector. + // Assert: index >= 0 && index < mElementCount + if (index < (mElementCount - 1)) + mArray[index] = mArray[mElementCount - 1]; + decrement(); +} + +template inline void VectorPtr::erase_fast(iterator i) +{ + erase_fast(U32(i - iterator(mArray))); +} + +template inline T& VectorPtr::front() +{ + return *begin(); +} + +template inline const T& VectorPtr::front() const +{ + return *begin(); +} + +template inline T& VectorPtr::back() +{ + AssertFatal(mElementCount != 0, "Vector::back - cannot access last element of zero-length vector."); + return *(end()-1); +} + +template inline const T& VectorPtr::back() const +{ + AssertFatal(mElementCount != 0, "Vector::back - cannot access last element of zero-length vector."); + return *(end()-1); +} + +template inline void VectorPtr::push_front(const T& x) +{ + Parent::push_front((Parent::const_reference)x); +} + +template inline void VectorPtr::push_back(const T& x) +{ + Parent::push_back((Parent::const_reference)x); +} + +template inline T& VectorPtr::operator[](U32 index) +{ + return (T&)Parent::operator[](index); +} + +template inline const T& VectorPtr::operator[](U32 index) const +{ + return (const T&)Parent::operator[](index); +} + +//------------------------------------------------------------------------------ + +template class VectorSet : public Vector +{ +public: + void insert(T dat) + { + if(find(this->begin(), this->end(), dat) == this->end()) + push_back(dat); + } +}; + +// Include vector specializations +#ifndef _VECTORSPEC_H_ +#include "core/util/tVectorSpecializations.h" +#endif + +#endif //_TVECTOR_H_ + diff --git a/Engine/source/core/util/tVectorSpecializations.h b/Engine/source/core/util/tVectorSpecializations.h new file mode 100644 index 000000000..8f9970fc6 --- /dev/null +++ b/Engine/source/core/util/tVectorSpecializations.h @@ -0,0 +1,46 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TVECTORSPEC_H_ +#define _TVECTORSPEC_H_ + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +// Not exactly a specialization, just a vector to use when speed is a concern +template +class FastVector : public Vector +{ +public: + // This method was re-written to prevent load-hit-stores during the simple-case. + void push_back_noresize(const T &x) + { +#ifdef TORQUE_DEBUG + AssertFatal(Vector::mElementCount < Vector::mArraySize, "use of push_back_noresize requires that you reserve enough space in the FastVector"); +#endif + Vector::mArray[Vector::mElementCount++] = x; + } +}; + +#endif //_TVECTORSPEC_H_ + diff --git a/Engine/source/core/util/test/testFixedSizeDeque.cpp b/Engine/source/core/util/test/testFixedSizeDeque.cpp new file mode 100644 index 000000000..e9b3f76f5 --- /dev/null +++ b/Engine/source/core/util/test/testFixedSizeDeque.cpp @@ -0,0 +1,54 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "unit/test.h" +#include "core/util/tFixedSizeDeque.h" + + +using namespace UnitTesting; + +#define TEST( x ) test( ( x ), "FAIL: " #x ) + +CreateUnitTest( TestFixedSizeDeque, "Util/FixedSizeDeque" ) +{ + void run() + { + enum { DEQUE_SIZE = 3 }; + FixedSizeDeque< U32 > deque( DEQUE_SIZE ); + + TEST( deque.capacity() == DEQUE_SIZE ); + TEST( deque.size() == 0 ); + + deque.pushFront( 1 ); + TEST( deque.capacity() == ( DEQUE_SIZE - 1 ) ); + TEST( deque.size() == 1 ); + TEST( !deque.isEmpty() ); + + deque.pushBack( 2 ); + TEST( deque.capacity() == ( DEQUE_SIZE - 2 ) ); + TEST( deque.size() == 2 ); + + TEST( deque.popFront() == 1 ); + TEST( deque.popFront() == 2 ); + TEST( deque.isEmpty() ); + } +}; diff --git a/Engine/source/core/util/test/testPath.cpp b/Engine/source/core/util/test/testPath.cpp new file mode 100644 index 000000000..94593185b --- /dev/null +++ b/Engine/source/core/util/test/testPath.cpp @@ -0,0 +1,44 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "unit/test.h" +#include "core/util/path.h" + +using namespace UnitTesting; + +#define TEST( x ) test( ( x ), "FAIL: " #x ) + +CreateUnitTest(TestPathMakeRelativePath, "Core/Util/Path/MakeRelativePath") +{ + void run() + { + TEST(Torque::Path::MakeRelativePath("art/interiors/burg/file.png", "art/interiors/") == "burg/file.png"); + TEST(Torque::Path::MakeRelativePath("art/interiors/file.png", "art/interiors/burg/") == "../file.png"); + TEST(Torque::Path::MakeRelativePath("art/file.png", "art/interiors/burg/") == "../../file.png"); + TEST(Torque::Path::MakeRelativePath("file.png", "art/interiors/burg/") == "../../../file.png"); + TEST(Torque::Path::MakeRelativePath("art/interiors/burg/file.png", "art/interiors/burg/") == "file.png"); + TEST(Torque::Path::MakeRelativePath("art/interiors/camp/file.png", "art/interiors/burg/") == "../camp/file.png"); + TEST(Torque::Path::MakeRelativePath("art/interiors/burg/file.png", "art/shapes/") == "../interiors/burg/file.png"); + TEST(Torque::Path::MakeRelativePath("levels/den/file.png", "art/interiors/burg/") == "../../../levels/den/file.png"); + TEST(Torque::Path::MakeRelativePath("art/interiors/burg/file.png", "art/dts/burg/") == "../../interiors/burg/file.png"); + } +}; diff --git a/Engine/source/core/util/test/testString.cpp b/Engine/source/core/util/test/testString.cpp new file mode 100644 index 000000000..33c6ea6d4 --- /dev/null +++ b/Engine/source/core/util/test/testString.cpp @@ -0,0 +1,358 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "unit/test.h" +#include "core/util/str.h" +#include "core/util/tVector.h" +#include "core/strings/stringFunctions.h" +#include "core/strings/unicode.h" + + +#ifndef TORQUE_SHIPPING + +using namespace UnitTesting; + +#define TEST( x ) test( ( x ), "FAIL: " #x ) +#define XTEST( t, x ) t->test( ( x ), "FAIL: " #x ) + +CreateUnitTest( TestString, "Util/String" ) +{ + struct StrTest + { + const UTF8* mData; + const UTF16* mUTF16; + U32 mLength; + + StrTest() : mData( 0 ), mUTF16( 0 ) {} + StrTest( const char* str ) + : mData( str ), mLength( str ? dStrlen( str ) : 0 ), mUTF16( NULL ) + { + if( str ) + mUTF16 = convertUTF8toUTF16( mData ); + } + ~StrTest() + { + if( mUTF16 ) + delete [] mUTF16; + } + }; + + Vector< StrTest* > mStrings; + + template< class T > + void runTestOnStrings() + { + for( U32 i = 0; i < mStrings.size(); ++ i ) + T::run( this, *mStrings[ i ] ); + } + template< class T > + void runPairwiseTestOnStrings() + { + for( U32 i = 0; i < mStrings.size(); ++ i ) + for( U32 j = 0; j < mStrings.size(); ++ j ) + T::run( this, *mStrings[ i ], *mStrings[ j ] ); + } + + struct Test1 + { + static void run( TestString* test, StrTest& data ) + { + String str( data.mData ); + String str16( data.mUTF16 ); + + XTEST( test, str.length() == data.mLength ); + XTEST( test, str.size() == data.mLength + 1 ); + XTEST( test, str.isEmpty() || str.length() > 0 ); + XTEST( test, str.length() == str16.length() ); + XTEST( test, str.size() == str16.size() ); + + XTEST( test, dMemcmp( str.utf16(), str16.utf16(), str.length() * sizeof( UTF16 ) ) == 0 ); + XTEST( test, !data.mData || dMemcmp( str.utf16(), data.mUTF16, str.length() * sizeof( UTF16 ) ) == 0 ); + XTEST( test, !data.mData || dMemcmp( str16.utf8(), data.mData, str.length() ) == 0 ); + + XTEST( test, !data.mData || dStrcmp( str.utf8(), data.mData ) == 0 ); + XTEST( test, !data.mData || dStrcmp( str.utf16(), data.mUTF16 ) == 0 ); + } + }; + + struct Test2 + { + static void run( TestString* test, StrTest& data ) + { + String str( data.mData ); + + XTEST( test, str == str ); + XTEST( test, !( str != str ) ); + XTEST( test, !( str < str ) ); + XTEST( test, !( str > str ) ); + XTEST( test, str.equal( str ) ); + XTEST( test, str.equal( str, String::NoCase ) ); + } + }; + + struct Test3 + { + static void run( TestString* test, StrTest& d1, StrTest& d2 ) + { + if( &d1 != &d2 ) + XTEST( test, String( d1.mData ) != String( d2.mData ) + || ( String( d1.mData ).isEmpty() && String( d2.mData ).isEmpty() ) ); + else + XTEST( test, String( d1.mData ) == String( d2.mData ) ); + } + }; + + void testEmpty() + { + TEST( String().length() == 0 ); + TEST( String( "" ).length() == 0 ); + TEST( String().size() == 1 ); + TEST( String( "" ).size() == 1 ); + TEST( String().isEmpty() ); + TEST( String( "" ).isEmpty() ); + } + + void testTrim() + { + TEST( String( " Foobar Barfoo \n\t " ).trim() == String( "Foobar Barfoo" ) ); + TEST( String( "Foobar" ).trim() == String( "Foobar" ) ); + TEST( String( " " ).trim().isEmpty() ); + } + + void testCompare() + { + String str( "Foobar" ); + + TEST( str.compare( "Foo", 3 ) == 0 ); + TEST( str.compare( "bar", 3, String::NoCase | String::Right ) == 0 ); + TEST( str.compare( "foo", 3, String::NoCase ) == 0 ); + TEST( str.compare( "BAR", 3, String::NoCase | String::Right ) == 0 ); + TEST( str.compare( "Foobar" ) == 0 ); + TEST( str.compare( "Foo" ) != 0 ); + TEST( str.compare( "foobar", 0, String::NoCase ) == 0 ); + TEST( str.compare( "FOOBAR", 0, String::NoCase ) == 0 ); + TEST( str.compare( "Foobar", 0, String::Right ) == 0 ); + TEST( str.compare( "foobar", 0, String::NoCase | String::Right ) == 0 ); + } + + void testOrder() + { + Vector< String > strs; + + strs.push_back( "a" ); + strs.push_back( "a0" ); + strs.push_back( "a1" ); + strs.push_back( "a1a" ); + strs.push_back( "a1b" ); + strs.push_back( "a2" ); + strs.push_back( "a10" ); + strs.push_back( "a20" ); + + for( U32 i = 0; i < strs.size(); ++ i ) + { + for( U32 j = 0; j < i; ++ j ) + { + TEST( strs[ j ] < strs[ i ] ); + TEST( strs[ i ] > strs[ j ] ); + + TEST( !( strs[ j ] > strs[ i ] ) ); + TEST( !( strs[ i ] < strs[ i ] ) ); + + TEST( strs[ j ] <= strs[ i ] ); + TEST( strs[ i ] >= strs[ j ] ); + } + + TEST( !( strs[ i ] < strs[ i ] ) ); + TEST( !( strs[ i ] > strs[ i ] ) ); + TEST( strs[ i ] <= strs[ i ] ); + TEST( strs[ i ] >= strs[ i ] ); + + for( U32 j = i + 1; j < strs.size(); ++ j ) + { + TEST( strs[ j ] > strs[ i ] ); + TEST( strs[ i ] < strs[ j ] ); + + TEST( !( strs[ j ] < strs[ i ] ) ); + TEST( !( strs[ i ] > strs[ j ] ) ); + + TEST( strs[ j ] >= strs[ i ] ); + TEST( strs[ i ] <= strs[ j ] ); + } + } + } + + void testFind() + { + //TODO + } + + void testInsert() + { + // String.insert( Pos, Char ) + TEST( String( "aa" ).insert( 1, 'c' ) == String( "aca" ) ); + + // String.insert( Pos, String ) + TEST( String( "aa" ).insert( 1, "cc" ) == String( "acca" ) ); + TEST( String( "aa" ).insert( 1, String( "cc" ) ) == String( "acca" ) ); + + // String.insert( Pos, String, Len ) + TEST( String( "aa" ).insert( 1, "ccdddd", 2 ) == String( "acca" ) ); + } + + void testErase() + { + TEST( String( "abba" ).erase( 1, 2 ) == String( "aa" ) ); + TEST( String( "abba" ).erase( 0, 4 ).isEmpty() ); + } + + void testReplace() + { + // String.replace( Pos, Len, String ) + TEST( String( "abba" ).replace( 1, 2, "ccc" ) == String( "accca" ) ); + TEST( String( "abba" ).replace( 1, 2, String( "ccc" ) ) == String( "accca" ) ); + TEST( String( "abba" ).replace( 0, 4, "" ).isEmpty() ); + TEST( String( "abba" ).replace( 2, 2, "c" ) == String( "abc" ) ); + + // String.replace( Char, Char ) + TEST( String().replace( 'a', 'b' ).isEmpty() ); + TEST( String( "ababc" ).replace( 'a', 'b' ) == String( "bbbbc" ) ); + TEST( String( "ababc" ).replace( 'd', 'e' ) == String( "ababc" ) ); + + // String.replace( String, String ) + TEST( String().replace( "foo", "bar" ).isEmpty() ); + TEST( String( "foobarfoo" ).replace( "foo", "bar" ) == String( "barbarbar" ) ); + TEST( String( "foobar" ).replace( "xx", "yy" ) == String( "foobar" ) ); + TEST( String( "foofoofoo" ).replace( "foo", "" ).isEmpty() ); + } + + void testSubstr() + { + TEST( String( "foobar" ).substr( 0, 3 ) == String( "foo" ) ); + TEST( String( "foobar" ).substr( 3 ) == String( "bar" ) ); + TEST( String( "foobar" ).substr( 2, 2 ) == String( "ob" ) ); + TEST( String( "foobar" ).substr( 2, 0 ).isEmpty() ); + TEST( String( "foobar" ).substr( 0, 6 ) == String( "foobar" ) ); + } + + void testToString() + { + TEST( String::ToString( U32( 1 ) ) == String( "1" ) ); + TEST( String::ToString( S32( -1 ) ) == String( "-1" ) ); + TEST( String::ToString( F32( 1.01 ) ) == String( "1.01" ) ); + TEST( String::ToString( "%s%i", "foo", 1 ) == String( "foo1" ) ); + } + + void testCaseConversion() + { + TEST( String::ToLower( "foobar123." ) == String( "foobar123." ) ); + TEST( String::ToLower( "FOOBAR123." ) == String( "foobar123." ) ); + TEST( String::ToUpper( "barfoo123." ) == String( "BARFOO123." ) ); + TEST( String::ToUpper( "BARFOO123." ) == String( "BARFOO123." ) ); + } + + void testConcat() + { + TEST( String( "foo" ) + String( "bar" ) == String( "foobar" ) ); + TEST( String() + String( "bar" ) == String( "bar" ) ); + TEST( String( "foo" ) + String() == String( "foo" ) ); + TEST( String() + String() == String() ); + TEST( String( "fo" ) + 'o' == String( "foo" ) ); + TEST( 'f' + String( "oo" ) == String( "foo" ) ); + TEST( String( "foo" ) + "bar" == String( "foobar" ) ); + TEST( "foo" + String( "bar" ) == String( "foobar" ) ); + } + + void testHash() + { + TEST( String( "foo" ).getHashCaseSensitive() == String( "foo" ).getHashCaseSensitive() ); + TEST( String( "foo" ).getHashCaseSensitive() != String( "bar" ).getHashCaseSensitive() ); + TEST( String( "foo" ).getHashCaseInsensitive() == String( "FOO" ).getHashCaseInsensitive() ); + } + + void testIntern() + { + TEST( String( "foo" ).intern().isSame( String( "foo" ).intern() ) ); + TEST( !String( "foo" ).intern().isSame( String( "bar" ).intern() ) ); + TEST( !String( "foo" ).intern().isSame( String( "Foo" ).intern() ) ); + TEST( String( "foo" ).intern() == String( "foo" ).intern() ); + TEST( String( "foo" ).intern() != String( "bar" ).intern() ); + TEST( String( "foo" ).intern().isInterned() ); + } + + void run() + { + mStrings.push_back( new StrTest( NULL ) ); + mStrings.push_back( new StrTest( "" ) ); + mStrings.push_back( new StrTest( "Torque" ) ); + mStrings.push_back( new StrTest( "TGEA" ) ); + mStrings.push_back( new StrTest( "GarageGames" ) ); + mStrings.push_back( new StrTest( "TGB" ) ); + mStrings.push_back( new StrTest( "games" ) ); + mStrings.push_back( new StrTest( "engine" ) ); + mStrings.push_back( new StrTest( "rocks" ) ); + mStrings.push_back( new StrTest( "technology" ) ); + mStrings.push_back( new StrTest( "Torque 3D" ) ); + mStrings.push_back( new StrTest( "Torque 2D" ) ); + + runTestOnStrings< Test1 >(); + runTestOnStrings< Test2 >(); + + runPairwiseTestOnStrings< Test3 >(); + + testEmpty(); + testTrim(); + testCompare(); + testOrder(); + testFind(); + testInsert(); + testReplace(); + testErase(); + testSubstr(); + testToString(); + testCaseConversion(); + testConcat(); + testHash(); + testIntern(); + + for( U32 i = 0; i < mStrings.size(); ++ i ) + delete mStrings[ i ]; + mStrings.clear(); + } +}; + +CreateUnitTest( TestStringBuilder, "Util/StringBuilder" ) +{ + void run() + { + StringBuilder str; + + str.append( 'f' ); + str.append( "oo" ); + str.append( String( "ba" ) ); + str.append( "rfajskfdj", 1 ); + str.format( "%s", "barfoo" ); + + TEST( str.end() == String( "foobarbarfoo" ) ); + } +}; + +#endif // !TORQUE_SHIPPING diff --git a/Engine/source/core/util/test/testVector2.cpp b/Engine/source/core/util/test/testVector2.cpp new file mode 100644 index 000000000..7c0cf2189 --- /dev/null +++ b/Engine/source/core/util/test/testVector2.cpp @@ -0,0 +1,138 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "unit/test.h" +#include "console/console.h" +#include "core/util/tVector.h" + + +#ifndef TORQUE_SHIPPING + +using namespace UnitTesting; + +#define TEST( x ) test( ( x ), "FAIL: " #x ) +#define XTEST( t, x ) t->test( ( x ), "FAIL: " #x ) + +CreateUnitTest( TestVector, "Util/Vector" ) +{ + bool dtorVals[ 10 ]; + struct Dtor + { + bool* ptr; + Dtor() {} + Dtor( bool* ptr ) + : ptr( ptr ) { *ptr = false; } + ~Dtor() + { + *ptr = true; + } + }; + void testDestruction() + { + Vector< Dtor > v; + + for( U32 i = 0; i < 9; ++ i ) + v.push_back( Dtor( &dtorVals[ i ] ) ); + + v.decrement(); + v.decrement( 2 ); + v.pop_back(); + v.increment(); + v.last() = Dtor( &dtorVals[ 9 ] ); + v.clear(); + + TEST( dtorVals[ 0 ] ); + TEST( dtorVals[ 1 ] ); + TEST( dtorVals[ 2 ] ); + TEST( dtorVals[ 3 ] ); + TEST( dtorVals[ 4 ] ); + TEST( dtorVals[ 5 ] ); + TEST( dtorVals[ 6 ] ); + TEST( dtorVals[ 7 ] ); + TEST( dtorVals[ 8 ] ); + TEST( dtorVals[ 9 ] ); + } + + static S32 QSORT_CALLBACK sortInts( const int* a, const int* b ) + { + int av = *a; + int bv = *b; + + if( av < bv ) + return -1; + else if( av > bv ) + return 1; + else + return 0; + } + + void testSort() + { + Vector< int > v; + + v.push_back( 0 ); + v.push_back( 10 ); + v.push_back( 2 ); + v.push_back( 3 ); + v.push_back( 14 ); + v.push_back( 4 ); + v.push_back( 12 ); + v.push_back( 6 ); + v.push_back( 16 ); + v.push_back( 7 ); + v.push_back( 8 ); + v.push_back( 1 ); + v.push_back( 11 ); + v.push_back( 5 ); + v.push_back( 13 ); + v.push_back( 9 ); + v.push_back( 15 ); + + v.sort( sortInts ); + + TEST( v[ 0 ] == 0 ); + TEST( v[ 1 ] == 1 ); + TEST( v[ 2 ] == 2 ); + TEST( v[ 3 ] == 3 ); + TEST( v[ 4 ] == 4 ); + TEST( v[ 5 ] == 5 ); + TEST( v[ 6 ] == 6 ); + TEST( v[ 7 ] == 7 ); + TEST( v[ 8 ] == 8 ); + TEST( v[ 9 ] == 9 ); + TEST( v[ 10 ] == 10 ); + TEST( v[ 11 ] == 11 ); + TEST( v[ 12 ] == 12 ); + TEST( v[ 13 ] == 13 ); + TEST( v[ 14 ] == 14 ); + TEST( v[ 15 ] == 15 ); + TEST( v[ 16 ] == 16 ); + } + + void run() + { + testSort(); + testDestruction(); + } +}; + +#endif diff --git a/Engine/source/core/util/timeClass.cpp b/Engine/source/core/util/timeClass.cpp new file mode 100644 index 000000000..98c60e882 --- /dev/null +++ b/Engine/source/core/util/timeClass.cpp @@ -0,0 +1,198 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include + +#include "core/util/timeClass.h" + +namespace Torque +{ + +//Micro 0.000001 10-6 +//Milli 0.001 10-3 + +static S8 _DaysInMonth[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; +static S8 _DaysInMonthLeap[13]= {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; +static S32 _DayNumber[13] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}; +static S32 _DayNumberLeap[13] = {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}; + + +/**---------------------------------------------------------------------------- +* PRIVATE Test to see if a year is a leap year. +* +* @param year Year to test for leap year +* @return true if year is a leap year +*/ +inline bool Time::_isLeapYear(S32 year) const +{ + return ((year & 3) == 0) && ( ((year % 100) != 0) || ((year % 400) == 0) ); +} + + +/**---------------------------------------------------------------------------- +* PRIVATE Determine the number of days in any given month/year +* +* @param month Month to be tested +* @param year Year to be tested +* @return Number of days in month/year +*/ +S32 Time::_daysInMonth(S32 month, S32 year) const +{ + if (_isLeapYear(year)) + return _DaysInMonthLeap[month]; + else + return _DaysInMonth[month]; +} + + +//----------------------------------------------------------------------------- +void Time::getCurrentDateTime(DateTime &dateTime) +{ + time_t long_time; + + time( &long_time ); + + struct tm *systime = localtime( &long_time ); + + dateTime.year = systime->tm_year; + dateTime.month = systime->tm_mon; + dateTime.day = systime->tm_mday; + dateTime.hour = systime->tm_hour; + dateTime.minute = systime->tm_min; + dateTime.second = systime->tm_sec; + dateTime.microsecond = 0; +} + +Time Time::getCurrentTime() +{ + return Torque::UnixTimeToTime( time( NULL ) ); +} + +bool Time::set(S32 year, S32 month, S32 day, S32 hour, S32 minute, S32 second, S32 microsecond) +{ + second += microsecond / 100000; + microsecond %= 100000; + minute += second / 60; + second %= 60; + hour += minute / 60; + minute %= 60; + S32 carryDays = hour / 24; + hour %= 24; + + bool leapYear = _isLeapYear(year); + + year -= 1; // all the next operations need (year-1) so do it ahead of time + S32 gregorian = 365 * year // number of days since the epoch + + (year/4) // add Julian leap year days + - (year/100) // subtract century leap years + + (year/400) // add gregorian 400 year leap adjustment + + ((367*month-362)/12) // days in prior months + + day // add days + + carryDays; // add days from time overflow/underflow + + // make days in this year adjustment if leap year + if (leapYear) + { + if (month > 2) + gregorian -= 1; + } + else + { + if (month > 2) + gregorian -= 2; + } + + _time = S64(gregorian) * OneDay; + _time += S64((hour * OneHour) + + (minute * OneMinute) + + (second * OneSecond) + + microsecond); + + return true; +} + + +//----------------------------------------------------------------------------- +void Time::get(S32 *pyear, S32 *pmonth, S32 *pday, S32 *phour, S32 *pminute, S32 *psecond, S32 *pmicrosecond) const +{ + // extract date if requested + if (pyear || pmonth || pday) + { + S32 gregorian = (S32)(_time / OneDay); + + S32 prior = gregorian - 1; // prior days + S32 years400 = prior / 146097L; // number of 400 year cycles + S32 days400 = prior % 146097L; // days NOT in years400 + S32 years100 = days400 / 36524L; // number 100 year cycles not checked + S32 days100 = days400 % 36524L; // days NOT already included + S32 years4 = days100 / 1461L; // number 4 year cycles not checked + S32 days4 = days100 % 1461L; // days NOT already included + S32 year1 = days4 / 365L; // number years not already checked + S32 day1 = days4 % 365L; // days NOT already included + S32 day; + S32 year = (400 * years400) + (100 * years100) + (4 * years4) + year1; + + // December 31 of leap year + if (years100 == 4 || year1 == 4) + { + day = 366; + } + else + { + year += 1; + day = day1 + 1; + } + + const S32 *dayNumber = _isLeapYear(year) ? _DayNumberLeap : _DayNumber; + + // find month and day in month given computed year and day number, + S32 month = 1; + while(day >= dayNumber[month]) + month++; + + day -= dayNumber[month-1]; + + if(pyear) + *pyear = year; + if(pmonth) + *pmonth = month; + if(pday) + *pday = day; + } + + // extract time + if (phour) + *phour = (S32)((_time % OneDay) / OneHour); + + S32 time = (S32)(_time % OneHour); + + if (pminute) + *pminute = time / (S32)OneMinute; + time %= OneMinute; + + if (psecond) + *psecond = time / (S32)OneSecond; + if (pmicrosecond) + *pmicrosecond = time % OneSecond; +} + +} // Namespace diff --git a/Engine/source/core/util/timeClass.h b/Engine/source/core/util/timeClass.h new file mode 100644 index 000000000..af9fe09fa --- /dev/null +++ b/Engine/source/core/util/timeClass.h @@ -0,0 +1,278 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TIMECLASS_H_ +#define _TIMECLASS_H_ + +#ifndef _TORQUE_TYPES_H_ +#include "platform/types.h" +#endif + + +#if defined(TORQUE_COMPILER_VISUALC) + #define TORQUE_CONSTANT_S64(a) (a##I64) + #define TORQUE_CONSTANT_U64(a) (a##UI64) +#else + #define TORQUE_CONSTANT_S64(a) (a##LL) ///< Used to declare signed 64 bit constants @hideinitializer + #define TORQUE_CONSTANT_U64(a) (a##ULL) ///< Used to declare unsigned 64 bit constants @hideinitializer +#endif + +namespace Torque +{ + +//----------------------------------------------------------------------------- +/// 64 bit time representation with ten microsecond resolution. +class Time +{ + class Tester; + +public: + struct DateTime + { + S32 year; + S32 month; + S32 day; + S32 hour; + S32 minute; + S32 second; + S32 microsecond; + }; + + static void getCurrentDateTime(DateTime &dateTime); + static Time getCurrentTime(); + + static const S64 OneDay = TORQUE_CONSTANT_S64(8640000000); + static const S64 OneHour = TORQUE_CONSTANT_S64( 360000000); + static const S64 OneMinute = TORQUE_CONSTANT_S64( 6000000); + static const S64 OneSecond = TORQUE_CONSTANT_S64( 100000); + static const S64 OneMillisecond = TORQUE_CONSTANT_S64( 100); + + Time(); + explicit Time(S64 time); + Time(S32 year, S32 month, S32 day, S32 hour, S32 minute, S32 second, S32 microsecond); + Time(const DateTime &dateTime); + + bool set(S32 year, S32 month, S32 day, S32 hour, S32 minute, S32 second, S32 microsecond); + void get(S32 *year, S32 *month, S32 *day, S32 *hour, S32 *minute, S32 *second, S32 *microsecond) const; + + Time operator+() const; + Time operator-() const; + Time operator+(const Time &time) const; + Time operator-(const Time &time) const; + S64 operator/(const Time &time) const; + const Time& operator+=(const Time time); + const Time& operator-=(const Time time); + + template Time operator*(T scaler) const { return Time(_time * scaler); } + template Time operator/(T scaler) const { return Time(_time / scaler); } + template friend Time operator*(T scaler,Time t) { return t * scaler; } + + bool operator==(const Time &time) const; + bool operator!=(const Time &time) const; + bool operator<(const Time &time) const; + bool operator>(const Time &time) const; + bool operator<=(const Time &time) const; + bool operator>=(const Time &time) const; + + operator Tester*() const + { + static Tester test; + return (_time == 0)? 0: &test; + } + bool operator!() const; + + S64 getSeconds() const; + S64 getMilliseconds() const; + S64 getMicroseconds() const; + S64 getInternalRepresentation() const; + +private: + class Tester + { + void operator delete(void*) {} + }; + + S64 _time; + + bool _isLeapYear(S32 year) const; + S32 _daysInMonth(S32 month, S32 year) const; +}; + +namespace TimeConstant +{ + const Time OneDay (Time::OneDay); + const Time OneHour (Time::OneHour); + const Time OneMinute (Time::OneMinute); + const Time OneSecond (Time::OneSecond); + const Time OneMillisecond (Time::OneMillisecond); +} + +//----------------------------------------------------------------------------- + +inline Time::Time() +{ + _time = 0; +} + +inline Time::Time(S64 time) +{ + _time = time; +} + +inline Time::Time(S32 year, S32 month, S32 day, S32 hour, S32 minute, S32 second, S32 microsecond) +{ + set(year, month, day, hour, minute, second, microsecond); +} + +inline Time::Time(const Time::DateTime &dateTime) +{ + set(dateTime.year, dateTime.month, dateTime.day, dateTime.hour, dateTime.minute, dateTime.second, dateTime.microsecond); +} + +inline Time Time::operator+() const +{ + return Time(_time); +} + +inline Time Time::operator-() const +{ + return Time(-_time); +} + +inline Time Time::operator+(const Time &time) const +{ + return Time(_time + time._time); +} + +inline Time Time::operator-(const Time &time) const +{ + return Time(_time - time._time); +} + +inline S64 Time::operator/(const Time &time) const +{ + return S64(_time / time._time); +} + +inline const Time& Time::operator+=(const Time time) +{ + _time += time._time; + return *this; +} + +inline const Time& Time::operator-=(const Time time) +{ + _time -= time._time; + return *this; +} + +inline bool Time::operator==(const Time &time) const +{ + return (_time == time._time); +} + +inline bool Time::operator!=(const Time &time) const +{ + return (_time != time._time); +} + +inline bool Time::operator<(const Time &time) const +{ + return (_time < time._time); +} + +inline bool Time::operator>(const Time &time) const +{ + return (_time > time._time); +} + +inline bool Time::operator<=(const Time &time) const +{ + return (_time <= time._time); +} + +inline bool Time::operator>=(const Time &time) const +{ + return (_time >= time._time); +} + +inline bool Time::operator !() const +{ + return _time == 0; +} + +inline S64 Time::getSeconds() const +{ + return _time / TimeConstant::OneSecond._time; +} + +inline S64 Time::getMilliseconds() const +{ + return _time / TimeConstant::OneMillisecond._time; +} + +inline S64 Time::getMicroseconds() const +{ + return _time * 10; +} + +inline S64 Time::getInternalRepresentation() const +{ + return _time; +} + +//----------------------------------------------------------------------------- +// time i/o time functions + +template inline bool read(S &stream, Time *theTime) +{ + S64 time; + bool ret = read(stream, &time); + *theTime = Time(time); + return ret; +} + +template inline bool write(S &stream, const Time &theTime) +{ + S64 time = theTime.getInternalRepresentation(); + return write(stream, time); +} + +//----------------------------------------------------------------------------- + +inline Time UnixTimeToTime(U32 t) +{ + // Converts "unix" time, seconds since 00:00:00 UTC, January 1, 1970 + return Time(((S64)(t)) * 100000 + TORQUE_CONSTANT_S64(6213568320000000)); +} + +inline Time Win32FileTimeToTime(U32 low,U32 high) +{ + // Converts Win32 "file" time, 100 nanosecond intervals since 00:00:00 UTC, January 1, 1601 + S64 t = (((S64)high) << 32) + low; + return Time(t / 100 + TORQUE_CONSTANT_S64(5049120960000000)); +} + + +} // Namespace + +#endif diff --git a/Engine/source/core/util/timeSource.h b/Engine/source/core/util/timeSource.h new file mode 100644 index 000000000..9c1a3ca89 --- /dev/null +++ b/Engine/source/core/util/timeSource.h @@ -0,0 +1,168 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TIMESOURCE_H_ +#define _TIMESOURCE_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif + +#ifndef _TSTREAM_H_ +#include "core/stream/tStream.h" +#endif + +#ifndef _SIM_H_ +#include "console/sim.h" +#endif + + +/// Timer that queries the real-time ticker. +struct RealMSTimer +{ + typedef U32 TickType; + static TickType getTick() + { + return Platform::getRealMilliseconds(); + } +}; + +/// Timer that queries the simulation-time ticker. +struct VirtualMSTimer +{ + typedef U32 TickType; + static TickType getTick() + { + return Platform::getVirtualMilliseconds(); + } +}; + +/// Timer that queries Sim::getCurrentTime(). +struct SimMSTimer +{ + typedef U32 TickType; + static TickType getTick() + { + return Sim::getCurrentTime(); + } +}; + + +/// +template< class Timer = RealMSTimer, typename Tick = typename Timer::TickType > +class GenericTimeSource : public IPositionable< Tick >, + public IProcess, + public IResettable +{ + public: + + typedef IPositionable< Tick > Parent; + typedef Tick TickType; + + protected: + + /// + TickType mStartTime; + + /// + TickType mPauseTime; + + /// + Timer mTimer; + + public: + + GenericTimeSource() + : mStartTime( TypeTraits< TickType >::MAX ), + mPauseTime( TypeTraits< TickType >::MAX ) {} + + bool isStarted() const + { + return ( mStartTime != TypeTraits< TickType >::MAX ); + } + bool isPaused() const + { + return ( mPauseTime != TypeTraits< TickType >::MAX ); + } + + /// Return the number of ticks since the time source + /// has been started. + TickType getPosition() const + { + if( !isStarted() ) + return TypeTraits< TickType >::ZERO; + else if( isPaused() ) + return ( mPauseTime - mStartTime ); + else + return ( mTimer.getTick() - mStartTime ); + } + + /// + void setPosition( TickType pos ) + { + if( !isStarted() ) + mStartTime = pos; + else + { + TickType savedStartTime = mStartTime; + + mStartTime = ( mTimer.getTick() - pos ); + if( isPaused() ) + mPauseTime = ( mStartTime + ( mPauseTime - savedStartTime ) ); + } + } + + // IResettable. + virtual void reset() + { + mStartTime = TypeTraits< TickType >::MAX; + mPauseTime = TypeTraits< TickType >::MAX; + } + + // IProcess. + virtual void start() + { + if( !isStarted() ) + { + TickType now = mTimer.getTick(); + + if( isPaused() ) + { + mStartTime += now - mPauseTime; + mPauseTime = TypeTraits< TickType >::MAX; + } + else + mStartTime = now; + } + } + virtual void stop() + { + reset(); + } + virtual void pause() + { + if( !isPaused() ) + mPauseTime = mTimer.getTick(); + } +}; + +#endif // _TIMESOURCE_H_ diff --git a/Engine/source/core/util/uuid.cpp b/Engine/source/core/util/uuid.cpp new file mode 100644 index 000000000..37017c709 --- /dev/null +++ b/Engine/source/core/util/uuid.cpp @@ -0,0 +1,453 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +// Original code: +/* +** Copyright (C) 1998-1999 Greg Stein. All Rights Reserved. +** +** By using this file, you agree to the terms and conditions set forth in +** the LICENSE.html file which can be found at the top level of the mod_dav +** distribution or at http://www.webdav.org/mod_dav/license-1.html. +** +** Contact information: +** Greg Stein, PO Box 3151, Redmond, WA, 98073 +** gstein@lyra.org, http://www.webdav.org/mod_dav/ +*/ + +/* +** DAV opaquelocktoken scheme implementation +** +** Written 5/99 by Keith Wannamaker, wannamak@us.ibm.com +** Adapted from ISO/DCE RPC spec and a former Internet Draft +** by Leach and Salz: +** http://www.ics.uci.edu/pub/ietf/webdav/uuid-guid/draft-leach-uuids-guids-01 +** +** Portions of the code are covered by the following license: +** +** Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +** Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & +** Digital Equipment Corporation, Maynard, Mass. +** Copyright (c) 1998 Microsoft. +** To anyone who acknowledges that this file is provided "AS IS" +** without any express or implied warranty: permission to use, copy, +** modify, and distribute this file for any purpose is hereby +** granted without fee, provided that the above copyright notices and +** this notice appears in all source code copies, and that none of +** the names of Open Software Foundation, Inc., Hewlett-Packard +** Company, or Digital Equipment Corporation be used in advertising +** or publicity pertaining to distribution of the software without +** specific, written prior permission. Neither Open Software +** Foundation, Inc., Hewlett-Packard Company, Microsoft, nor Digital Equipment +** Corporation makes any representations about the suitability of +** this software for any purpose. +*/ + +#include +#include +#include +#include +#include + +#include "core/util/md5.h" + +typedef unsigned long unsigned32; +typedef unsigned short unsigned16; +typedef unsigned char unsigned8; + +typedef struct { + char nodeID[6]; +} uuid_node_t; + +#undef xuuid_t + +typedef struct _uuid_t +{ + unsigned32 time_low; + unsigned16 time_mid; + unsigned16 time_hi_and_version; + unsigned8 clock_seq_hi_and_reserved; + unsigned8 clock_seq_low; + unsigned8 node[6]; +} xuuid_t; + +/* data type for UUID generator persistent state */ + +typedef struct { + uuid_node_t node; /* saved node ID */ + unsigned16 cs; /* saved clock sequence */ +} uuid_state; + +#if defined(_XBOX) +#include +#elif defined(_WIN32) +#include +#else +#include +#include +#include +#ifdef XP_BEOS +#include +#endif +#endif + +/* set the following to the number of 100ns ticks of the actual resolution of + your system's clock */ +#define UUIDS_PER_TICK 1024 + +/* Set this to what your compiler uses for 64 bit data type */ +#ifdef _WIN32 +#define unsigned64_t unsigned __int64 +#define I64(C) C +#else +#define unsigned64_t unsigned long long +#define I64(C) C##LL +#endif + +typedef unsigned64_t uuid_time_t; + +static void format_uuid_v1(xuuid_t * uuid, unsigned16 clockseq, uuid_time_t timestamp, uuid_node_t node); +static void get_current_time(uuid_time_t * timestamp); +static unsigned16 true_random(void); +static void get_pseudo_node_identifier(uuid_node_t *node); +static void get_system_time(uuid_time_t *uuid_time); +static void get_random_info(unsigned char seed[16]); + + +/* dav_create_opaquelocktoken - generates a UUID version 1 token. + * Clock_sequence and node_address set to pseudo-random + * numbers during init. + * + * Should postpend pid to account for non-seralized creation? + */ +static int create_token(uuid_state *st, xuuid_t *u) +{ + uuid_time_t timestamp; + + get_current_time(×tamp); + format_uuid_v1(u, st->cs, timestamp, st->node); + + return 1; +} + +/* + * dav_create_uuid_state - seed UUID state with pseudorandom data + */ +static void create_uuid_state(uuid_state *st) +{ + st->cs = true_random(); + get_pseudo_node_identifier(&st->node); +} + +/* + * dav_format_opaquelocktoken - generates a text representation + * of an opaquelocktoken + */ +static void format_token(char *target, const xuuid_t *u) +{ + sprintf(target, "%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", + u->time_low, u->time_mid, u->time_hi_and_version, + u->clock_seq_hi_and_reserved, u->clock_seq_low, + u->node[0], u->node[1], u->node[2], + u->node[3], u->node[4], u->node[5]); +} + +/* convert a pair of hex digits to an integer value [0,255] */ +static int dav_parse_hexpair(const char *s) +{ + int result; + int temp; + + result = s[0] - '0'; + if (result > 48) + result = (result - 39) << 4; + else if (result > 16) + result = (result - 7) << 4; + else + result = result << 4; + + temp = s[1] - '0'; + if (temp > 48) + result |= temp - 39; + else if (temp > 16) + result |= temp - 7; + else + result |= temp; + + return result; +} + +/* dav_parse_locktoken: Parses string produced from + * dav_format_opaquelocktoken back into a xuuid_t + * structure. On failure, return DAV_IF_ERROR_PARSE, + * else DAV_IF_ERROR_NONE. + */ +static int parse_token(const char *char_token, xuuid_t *bin_token) +{ + int i; + + for (i = 0; i < 36; ++i) { + char c = char_token[i]; + if (!isxdigit(c) && + !(c == '-' && (i == 8 || i == 13 || i == 18 || i == 23))) + return -1; + } + if (char_token[36] != '\0') + return -1; + + bin_token->time_low = + (dav_parse_hexpair(&char_token[0]) << 24) | + (dav_parse_hexpair(&char_token[2]) << 16) | + (dav_parse_hexpair(&char_token[4]) << 8) | + dav_parse_hexpair(&char_token[6]); + + bin_token->time_mid = + (dav_parse_hexpair(&char_token[9]) << 8) | + dav_parse_hexpair(&char_token[11]); + + bin_token->time_hi_and_version = + (dav_parse_hexpair(&char_token[14]) << 8) | + dav_parse_hexpair(&char_token[16]); + + bin_token->clock_seq_hi_and_reserved = dav_parse_hexpair(&char_token[19]); + bin_token->clock_seq_low = dav_parse_hexpair(&char_token[21]); + + for (i = 6; i--;) + bin_token->node[i] = dav_parse_hexpair(&char_token[i*2+24]); + + return 0; +} + +/* format_uuid_v1 -- make a UUID from the timestamp, clockseq, and node ID */ +static void format_uuid_v1(xuuid_t * uuid, unsigned16 clock_seq, + uuid_time_t timestamp, uuid_node_t node) +{ + /* Construct a version 1 uuid with the information we've gathered + * plus a few constants. */ + uuid->time_low = (unsigned long)(timestamp & 0xFFFFFFFF); + uuid->time_mid = (unsigned short)((timestamp >> 32) & 0xFFFF); + uuid->time_hi_and_version = (unsigned short)((timestamp >> 48) & 0x0FFF); + uuid->time_hi_and_version |= (1 << 12); + uuid->clock_seq_low = clock_seq & 0xFF; + uuid->clock_seq_hi_and_reserved = (clock_seq & 0x3F00) >> 8; + uuid->clock_seq_hi_and_reserved |= 0x80; + memcpy(&uuid->node, &node, sizeof uuid->node); +} + +/* get-current_time -- get time as 60 bit 100ns ticks since whenever. + Compensate for the fact that real clock resolution is less than 100ns. */ +static void get_current_time(uuid_time_t * timestamp) +{ + uuid_time_t time_now; + static uuid_time_t time_last; + static unsigned16 uuids_this_tick; + static int inited = 0; + + if (!inited) { + get_system_time(&time_now); + uuids_this_tick = UUIDS_PER_TICK; + inited = 1; + }; + + while (1) { + get_system_time(&time_now); + + /* if clock reading changed since last UUID generated... */ + if (time_last != time_now) { + /* reset count of uuids gen'd with this clock reading */ + uuids_this_tick = 0; + break; + }; + if (uuids_this_tick < UUIDS_PER_TICK) { + uuids_this_tick++; + break; + }; /* going too fast for our clock; spin */ + }; /* add the count of uuids to low order bits of the clock reading */ + + *timestamp = time_now + uuids_this_tick; + time_last = time_now; +} + +/* true_random -- generate a crypto-quality random number. + This sample doesn't do that. */ +static unsigned16 true_random(void) +{ + uuid_time_t time_now; + + get_system_time(&time_now); + time_now = time_now/UUIDS_PER_TICK; + srand((unsigned int)(((time_now >> 32) ^ time_now)&0xffffffff)); + + return rand(); +} + +/* This sample implementation generates a random node ID * + * in lieu of a system dependent call to get IEEE node ID. */ +static void get_pseudo_node_identifier(uuid_node_t *node) +{ + unsigned char seed[16]; + + get_random_info(seed); + seed[0] |= 0x80; + memcpy(node, seed, sizeof(uuid_node_t)); +} + +/* system dependent call to get the current system time. + Returned as 100ns ticks since Oct 15, 1582, but resolution may be + less than 100ns. */ + +#ifdef _WIN32 + +static void get_system_time(uuid_time_t *uuid_time) +{ + ULARGE_INTEGER time; + + GetSystemTimeAsFileTime((FILETIME *)&time); + + /* NT keeps time in FILETIME format which is 100ns ticks since + Jan 1, 1601. UUIDs use time in 100ns ticks since Oct 15, 1582. + The difference is 17 Days in Oct + 30 (Nov) + 31 (Dec) + + 18 years and 5 leap days. */ + + time.QuadPart += + (unsigned __int64) (1000*1000*10) + * (unsigned __int64) (60 * 60 * 24) + * (unsigned __int64) (17+30+31+365*18+5); + *uuid_time = time.QuadPart; +} + +#if defined(_XBOX) +#include "platform/platformAssert.h" +#endif + +static void get_random_info(unsigned char seed[16]) +{ +#if defined(_XBOX) + AssertFatal(false, "get_random_info not implemented on Xbox360"); +#else + MD5_CTX c; + struct { + MEMORYSTATUS m; + SYSTEM_INFO s; + FILETIME t; + LARGE_INTEGER pc; + DWORD tc; + DWORD l; + TCHAR hostname[MAX_COMPUTERNAME_LENGTH + 1]; + + } r; + + MD5Init(&c); /* memory usage stats */ + GlobalMemoryStatus(&r.m); /* random system stats */ + GetSystemInfo(&r.s); /* 100ns resolution (nominally) time of day */ + GetSystemTimeAsFileTime(&r.t); /* high resolution performance counter */ + QueryPerformanceCounter(&r.pc); /* milliseconds since last boot */ + r.tc = GetTickCount(); + r.l = MAX_COMPUTERNAME_LENGTH + 1; + + GetComputerName(r.hostname, &r.l ); + MD5Update(&c, (unsigned char *) &r, sizeof(r)); + MD5Final(seed, &c); +#endif +} + +#else /* WIN32 */ + +static void get_system_time(uuid_time_t *uuid_time) +{ + struct timeval tp; + + gettimeofday(&tp, (struct timezone *)0); + + /* Offset between UUID formatted times and Unix formatted times. + UUID UTC base time is October 15, 1582. + Unix base time is January 1, 1970. */ + *uuid_time = (tp.tv_sec * 10000000) + (tp.tv_usec * 10) + + I64(0x01B21DD213814000); +} + +static void get_random_info(unsigned char seed[16]) +{ + MD5_CTX c; + /* Leech & Salz use Linux-specific struct sysinfo; + * replace with pid/tid for portability (in the spirit of mod_unique_id) */ + struct { + /* Add thread id here, if applicable, when we get to pthread or apr */ + pid_t pid; + struct timeval t; + char hostname[257]; + + } r; + + MD5Init(&c); + r.pid = getpid(); + gettimeofday(&r.t, (struct timezone *)0); + gethostname(r.hostname, 256); + MD5Update(&c, (unsigned char *)&r, sizeof(r)); + MD5Final(seed, &c); +} + +#endif /* WIN32 */ + +#include "core/util/uuid.h" + +namespace { + bool gUUIDStateInitialized; + uuid_state gUUIDState; +} + +namespace Torque +{ + UUID UUID::smNull; + + void UUID::generate() + { + if( !gUUIDStateInitialized ) + { + create_uuid_state( &gUUIDState ); + gUUIDStateInitialized = true; + } + + create_token( &gUUIDState, ( xuuid_t* ) this ); + } + + String UUID::toString() const + { + char buffer[ 1024 ]; + format_token( buffer, ( xuuid_t* ) this ); + return buffer; + } + + bool UUID::fromString( const char* str ) + { + if( parse_token( str, ( xuuid_t* ) this ) != 0 ) + { + dMemset( this, 0, sizeof( UUID ) ); + return false; + } + + return true; + } + + U32 UUID::getHash() const + { + return ( a + b + c + d + e + f[ 0 ] + f[ 1 ] + f[ 2 ] + f[ 3 ] + f[ 4 ] + f[ 5 ] ); + } +} diff --git a/Engine/source/core/util/uuid.h b/Engine/source/core/util/uuid.h new file mode 100644 index 000000000..e4ed4153a --- /dev/null +++ b/Engine/source/core/util/uuid.h @@ -0,0 +1,94 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TORQUE_UUID_H_ +#define _TORQUE_UUID_H_ + +#ifndef _PLATFORM_H_ + #include "platform/platform.h" +#endif + + +namespace Torque +{ + /// A universally unique identifier. + class UUID + { + public: + + typedef void Parent; + + protected: + + U32 a; + U16 b; + U16 c; + U8 d; + U8 e; + U8 f[ 6 ]; + + static UUID smNull; + + public: + + UUID() + { + dMemset( this, 0, sizeof( UUID ) ); + } + + /// + bool isNull() const { return ( *this == smNull ); } + + /// Generate a new universally unique identifier (UUID). + void generate(); + + /// Convert the given universally unique identifier to a printed string + /// representation. + String toString() const; + + /// Parse a text string generated by UUIDToString back into a + /// universally unique identifier (UUID). + bool fromString( const char* str ); + + /// Return a hash value for this UUID. + U32 getHash() const; + + bool operator ==( const UUID& uuid ) const + { + return ( dMemcmp( this, &uuid, sizeof( UUID ) ) == 0 ); + } + bool operator !=( const UUID& uuid ) const + { + return !( *this == uuid ); + } + }; +} + +namespace DictHash +{ + inline U32 hash( const Torque::UUID& uuid ) + { + return uuid.getHash(); + } +} + +#endif // !_TORQUE_UUID_H_ diff --git a/Engine/source/core/util/zip/centralDir.cpp b/Engine/source/core/util/zip/centralDir.cpp new file mode 100644 index 000000000..e107df2e8 --- /dev/null +++ b/Engine/source/core/util/zip/centralDir.cpp @@ -0,0 +1,320 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/stream/stream.h" +#include "core/strings/stringFunctions.h" + +#include "core/util/zip/centralDir.h" +#include "core/util/zip/compressor.h" + +#include "core/util/safeDelete.h" + +namespace Zip +{ + +//----------------------------------------------------------------------------- +// CentralDir Class +//----------------------------------------------------------------------------- + +CentralDir::CentralDir() +{ + mHeaderSig = mCentralDirSignature; + + mDiskNumStart = 0; + + mInternalFileAttr = 0; + mExternalFileAttr = 0; + + mLocalHeadOffset = 0; + + mVersionMadeBy = 0; + + mFileComment = NULL; + + mInternalFlags = 0; +} + +CentralDir::CentralDir(FileHeader &fh) : FileHeader(fh) +{ + mHeaderSig = mCentralDirSignature; + + mDiskNumStart = 0; + + mInternalFileAttr = 0; + mExternalFileAttr = 0; + + mLocalHeadOffset = 0; + + mVersionMadeBy = 0; + + mFileComment = NULL; +} + +CentralDir::~CentralDir() +{ + SAFE_DELETE_ARRAY(mFileComment); +} + +//----------------------------------------------------------------------------- + +bool CentralDir::read(Stream *stream) +{ + stream->read(&mHeaderSig); + if(mHeaderSig != mCentralDirSignature) + return false; + + stream->read(&mVersionMadeBy); + stream->read(&mExtractVer); + stream->read(&mFlags); + stream->read(&mCompressMethod); + stream->read(&mModTime); + stream->read(&mModDate); + stream->read(&mCRC32); + stream->read(&mCompressedSize); + stream->read(&mUncompressedSize); + + U16 fnLen, efLen, fcLen; + stream->read(&fnLen); + stream->read(&efLen); + stream->read(&fcLen); + + stream->read(&mDiskNumStart); + + stream->read(&mInternalFileAttr); + stream->read(&mExternalFileAttr); + + stream->read(&mLocalHeadOffset); + + char *fn = new char[fnLen + 1]; + stream->read(fnLen, fn); + fn[fnLen] = 0; + mFilename = String(fn); + SAFE_DELETE_ARRAY(fn); + + + // [tom, 10/28/2006] We currently only need the extra fields when we want to + // open the file, so we won't bother reading them here. This avoids keeping + // them in memory twice. + + //readExtraFields(stream, efLen); + stream->setPosition(stream->getPosition() + efLen); + + fn = new char[fcLen + 1]; + stream->read(fcLen, fn); + fn[fcLen] = 0; + + SAFE_DELETE_ARRAY(mFileComment); + mFileComment = fn; + + // Sanity checks to make life easier elsewhere + if(mCompressMethod != Stored && mUncompressedSize == 0 && mCompressedSize == 0) + mCompressMethod = Stored; + + return true; +} + +bool CentralDir::write(Stream *stream) +{ + mHeaderSig = mCentralDirSignature; + stream->write(mHeaderSig); + + stream->write(mVersionMadeBy); + stream->write(mExtractVer); + stream->write(mFlags); + stream->write(mCompressMethod); + stream->write(mModTime); + stream->write(mModDate); + stream->write(mCRC32); + stream->write(mCompressedSize); + stream->write(mUncompressedSize); + + U16 fnLen = mFilename.length(), + efLen = 0, + fcLen = mFileComment ? (U16)dStrlen(mFileComment) : 0; + stream->write(fnLen); + stream->write(efLen); + stream->write(fcLen); + + stream->write(mDiskNumStart); + + stream->write(mInternalFileAttr); + stream->write(mExternalFileAttr); + + stream->write(mLocalHeadOffset); + + if(fnLen) + stream->write(fnLen, mFilename); + + // FIXME [tom, 10/29/2006] Write extra fields here + + if(fcLen) + stream->write(fcLen, mFileComment); + + return true; +} + +//----------------------------------------------------------------------------- + +void CentralDir::setFileComment(const char *comment) +{ + SAFE_DELETE_ARRAY(mFileComment); + mFileComment = new char [dStrlen(comment)+1]; + dStrcpy(mFileComment, comment); +} + +//----------------------------------------------------------------------------- +// EndOfCentralDir Class +//----------------------------------------------------------------------------- + +EndOfCentralDir::EndOfCentralDir() +{ + mHeaderSig = mEOCDSignature; + + mDiskNum = 0; + mStartCDDiskNum = 0; + mNumEntriesInThisCD = 0; + mTotalEntriesInCD = 0; + mCDSize = 0; + mCDOffset = 0; + mCommentSize = 0; + mZipComment = NULL; +} + +EndOfCentralDir::~EndOfCentralDir() +{ + SAFE_DELETE_ARRAY(mZipComment); +} + +//----------------------------------------------------------------------------- + +bool EndOfCentralDir::read(Stream *stream) +{ + stream->read(&mHeaderSig); + if(mHeaderSig != mEOCDSignature) + return false; + + stream->read(&mDiskNum); + stream->read(&mStartCDDiskNum); + stream->read(&mNumEntriesInThisCD); + stream->read(&mTotalEntriesInCD); + stream->read(&mCDSize); + stream->read(&mCDOffset); + + stream->read(&mCommentSize); + + char *comment = new char[mCommentSize + 1]; + stream->read(mCommentSize, comment); + comment[mCommentSize] = 0; + + SAFE_DELETE_ARRAY(mZipComment); + mZipComment = comment; + + return true; +} + +bool EndOfCentralDir::write(Stream *stream) +{ + stream->write(mHeaderSig); + + stream->write(mDiskNum); + stream->write(mStartCDDiskNum); + stream->write(mNumEntriesInThisCD); + stream->write(mTotalEntriesInCD); + stream->write(mCDSize); + stream->write(mCDOffset); + + stream->write(mCommentSize); + if(mZipComment && mCommentSize) + stream->write(mCommentSize, mZipComment); + + return true; +} + +//----------------------------------------------------------------------------- + +// [tom, 10/19/2006] I know, i know ... this'll get rewritten. +// [tom, 1/23/2007] Maybe. + +bool EndOfCentralDir::findInStream(Stream *stream) +{ + U32 initialPos = stream->getPosition(); + U32 size = stream->getStreamSize(); + U32 pos; + if(size == 0) + return false; + + if(! stream->setPosition(size - mRecordSize)) + goto hell; + + U32 sig; + stream->read(&sig); + + if(sig == mEOCDSignature) + { + stream->setPosition(size - mRecordSize); + return true; + } + + // OK, so we couldn't find the EOCD where we expected it. The zip file + // either has comments or isn't a zip file. We need to search the last + // 64Kb of the file for the EOCD. + + pos = size > mEOCDSearchSize ? size - mEOCDSearchSize : 0; + if(! stream->setPosition(pos)) + goto hell; + + while(pos < (size - 4)) + { + stream->read(&sig); + + if(sig == mEOCDSignature) + { + stream->setPosition(pos); + return true; + } + + pos++; + if(! stream->setPosition(pos)) + goto hell; + } + +hell: + stream->setPosition(initialPos); + return false; +} + +//----------------------------------------------------------------------------- + +void EndOfCentralDir::setZipComment(U16 commentSize, const char *zipComment) +{ + SAFE_DELETE_ARRAY(mZipComment); + mZipComment = new char [commentSize]; + dMemcpy((void *)mZipComment, zipComment, commentSize); + mCommentSize = commentSize; +} + +void EndOfCentralDir::setZipComment(const char *zipComment) +{ + setZipComment(dStrlen(zipComment), zipComment); +} + +} // end namespace Zip diff --git a/Engine/source/core/util/zip/centralDir.h b/Engine/source/core/util/zip/centralDir.h new file mode 100644 index 000000000..4f582b206 --- /dev/null +++ b/Engine/source/core/util/zip/centralDir.h @@ -0,0 +1,112 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/util/zip/fileHeader.h" + +#ifndef _CENTRALDIR_H_ +#define _CENTRALDIR_H_ + +namespace Zip +{ + +/// @addtogroup zipint_group +/// @ingroup zip_group +// @{ + +/// Internal flags used by the zip code when writing zips +enum CDIntFlags +{ + CDFileDirty = BIT(0), + CDFileAdded = BIT(1), + CDFileDeleted = BIT(2), + CDFileOpen = BIT(3) +}; + +class CentralDir : public FileHeader +{ + typedef FileHeader Parent; + + static const U32 mCentralDirSignature = 0x02014b50; + + char *mFileComment; + +public: + U16 mDiskNumStart; + + U16 mInternalFileAttr; + U32 mExternalFileAttr; + + U32 mLocalHeadOffset; + + U16 mVersionMadeBy; + + U32 mInternalFlags; + + CentralDir(); + CentralDir(FileHeader &fh); + virtual ~CentralDir(); + + virtual bool read(Stream *stream); + virtual bool write(Stream *stream); + + void setFileComment(const char *comment); +}; + +class EndOfCentralDir +{ + static const U32 mEOCDSignature = 0x06054b50; + /// The size of the EndOfCentralDir record in the zip file, used to locate it + /// at the end of the file. + static const U32 mRecordSize = 20; + /// The number of bytes from the end of the file to start searching for the EOCD + static const U32 mEOCDSearchSize = 64 * 1024; + +public: + U32 mHeaderSig; + + U16 mDiskNum; + U16 mStartCDDiskNum; + U16 mNumEntriesInThisCD; + U16 mTotalEntriesInCD; + U32 mCDSize; + U32 mCDOffset; + U16 mCommentSize; + + const char *mZipComment; + + EndOfCentralDir(); + virtual ~EndOfCentralDir(); + + virtual bool findInStream(Stream *stream); + + virtual bool read(Stream *stream); + virtual bool write(Stream *stream); + + virtual void setZipComment(const char *zipComment); + virtual void setZipComment(U16 commentSize, const char *zipComment); +}; + +// @} + +} // end namespace Zip + +#endif // _CENTRALDIR_H_ diff --git a/Engine/source/core/util/zip/compressor.cpp b/Engine/source/core/util/zip/compressor.cpp new file mode 100644 index 000000000..0db1a4ca8 --- /dev/null +++ b/Engine/source/core/util/zip/compressor.cpp @@ -0,0 +1,70 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/strings/stringFunctions.h" +#include "core/util/zip/compressor.h" + +namespace Zip +{ + +static Compressor *gCompressorInitList = NULL; + +//----------------------------------------------------------------------------- +// Constructor/Destructor +//----------------------------------------------------------------------------- + +Compressor::Compressor(S32 method, const char *name) +{ + mName = name; + mMethod = method; + + mNext = gCompressorInitList; + gCompressorInitList = this; +} + +//----------------------------------------------------------------------------- +// Static Methods +//----------------------------------------------------------------------------- + +Compressor *Compressor::findCompressor(S32 method) +{ + for(Compressor *walk = gCompressorInitList;walk;walk = walk->mNext) + { + if(walk->getMethod() == method) + return walk; + } + + return NULL; +} + +Compressor *Compressor::findCompressor(const char *name) +{ + for(Compressor *walk = gCompressorInitList;walk;walk = walk->mNext) + { + if(dStricmp(walk->getName(), name) == 0) + return walk; + } + + return NULL; +} + +} // end namespace Zip diff --git a/Engine/source/core/util/zip/compressor.h b/Engine/source/core/util/zip/compressor.h new file mode 100644 index 000000000..0def7e4f9 --- /dev/null +++ b/Engine/source/core/util/zip/compressor.h @@ -0,0 +1,103 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/util/zip/centralDir.h" + +#ifndef _COMPRESSOR_H_ +#define _COMPRESSOR_H_ + +// Forward refs +class Stream; + +namespace Zip +{ + +/// @addtogroup zipint_group +/// @ingroup zip_group +// @{ + +// [tom, 10/26/2006] Standard Zip compression methods +enum CompressionMethod +{ + Stored = 0, + Shrunk = 1, + ReducedL1 = 2, + ReducedL2 = 3, + ReducedL3 = 4, + ReducedL4 = 5, + Imploded = 6, + ReservedTokenized = 7, + Deflated = 8, + EnhDefalted = 9, + DateCompression = 10, + ReservedPKWARE = 11, + BZip2 = 12, + PPMd = 98, + AESEncrypted = 99, // WinZip's AES Encrypted zips use compression method 99 + // to indicate AES encryption. The actual compression + // method is specified in the AES extra field. +}; + +class Compressor +{ + Compressor *mNext; + +protected: + const char *mName; //!< The name of the compression method + S32 mMethod; //!< The compression method as in the Zip header + +public: + Compressor(S32 method, const char *name); + virtual ~Compressor() {} + + inline const char * getName() { return mName; } + inline S32 getMethod() { return mMethod; } + + virtual Stream *createReadStream(const CentralDir *cdir, Stream *zipStream) = 0; + virtual Stream *createWriteStream(const CentralDir *cdir, Stream *zipStream) = 0; + + // Run time lookup methods + static Compressor *findCompressor(const char *name); + static Compressor *findCompressor(S32 method); +}; + +#define ImplementCompressor(name, method) \ + class Compressor##name : public Compressor \ + { \ + public: \ + Compressor##name(S32 m, const char *n) : Compressor(m, n) {} \ + virtual Stream *createReadStream(const CentralDir *cdir, Stream *zipStream); \ + virtual Stream *createWriteStream(const CentralDir *cdir, Stream *zipStream); \ + }; \ + Compressor##name gCompressor##name##instance(method, #name); + +#define CompressorCreateReadStream(name) \ + Stream * Compressor##name::createReadStream(const CentralDir *cdir, Stream *zipStream) + +#define CompressorCreateWriteStream(name) \ + Stream * Compressor##name::createWriteStream(const CentralDir *cdir, Stream *zipStream) + +// @} + +} // end namespace Zip + +#endif // _COMPRESSOR_H_ diff --git a/Engine/source/core/util/zip/compressors/deflate.cpp b/Engine/source/core/util/zip/compressors/deflate.cpp new file mode 100644 index 000000000..d11265926 --- /dev/null +++ b/Engine/source/core/util/zip/compressors/deflate.cpp @@ -0,0 +1,50 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/util/zip/compressor.h" + +#include "core/util/zip/zipSubStream.h" + +namespace Zip +{ + +ImplementCompressor(Deflate, Deflated); + +CompressorCreateReadStream(Deflate) +{ + ZipSubRStream *stream = new ZipSubRStream; + stream->attachStream(zipStream); + stream->setUncompressedSize(cdir->mUncompressedSize); + + return stream; +} + +CompressorCreateWriteStream(Deflate) +{ + ZipSubWStream *stream = new ZipSubWStream; + stream->attachStream(zipStream); + + return stream; +} + +} // end namespace Zip diff --git a/Engine/source/core/util/zip/compressors/stored.cpp b/Engine/source/core/util/zip/compressors/stored.cpp new file mode 100644 index 000000000..ffda185db --- /dev/null +++ b/Engine/source/core/util/zip/compressors/stored.cpp @@ -0,0 +1,47 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/util/zip/compressor.h" + +#include "core/resizeStream.h" + +namespace Zip +{ + +ImplementCompressor(Stored, Stored); + +CompressorCreateReadStream(Stored) +{ + ResizeFilterStream *resStream = new ResizeFilterStream; + resStream->attachStream(zipStream); + resStream->setStreamOffset(zipStream->getPosition(), cdir->mCompressedSize); + + return resStream; +} + +CompressorCreateWriteStream(Stored) +{ + return zipStream; +} + +} // end namespace Zip diff --git a/Engine/source/core/util/zip/crctab.h b/Engine/source/core/util/zip/crctab.h new file mode 100644 index 000000000..3c8f4818a --- /dev/null +++ b/Engine/source/core/util/zip/crctab.h @@ -0,0 +1,84 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _CRCTAB_H_ +#define _CRCTAB_H_ + +static U32 crc_32_tab[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +#define ZC_CRC32(c, b) (crc_32_tab[((int)(c) ^ (b)) & 0xff] ^ ((c) >> 8)) + +#endif // _CRCTAB_H_ + diff --git a/Engine/source/core/util/zip/extraField.cpp b/Engine/source/core/util/zip/extraField.cpp new file mode 100644 index 000000000..127223c36 --- /dev/null +++ b/Engine/source/core/util/zip/extraField.cpp @@ -0,0 +1,59 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/util/zip/extraField.h" + +namespace Zip +{ + +static ExtraField *gExtraFieldInitList = NULL; + +//----------------------------------------------------------------------------- +// Constructor/Destructor +//----------------------------------------------------------------------------- + +ExtraField::ExtraField(U16 id, ExtraFieldCreateFn fnCreate) +{ + mID = id; + mCreateFn = fnCreate; + + mNext = gExtraFieldInitList; + gExtraFieldInitList = this; +} + +//----------------------------------------------------------------------------- +// Static Methods +//----------------------------------------------------------------------------- + +ExtraField * ExtraField::create(U16 id) +{ + for(ExtraField *walk = gExtraFieldInitList;walk;walk = walk->mNext) + { + if(walk->getID() == id) + return walk->mCreateFn(); + } + + return NULL; +} + +} // end namespace Zip diff --git a/Engine/source/core/util/zip/extraField.h b/Engine/source/core/util/zip/extraField.h new file mode 100644 index 000000000..b9958f46e --- /dev/null +++ b/Engine/source/core/util/zip/extraField.h @@ -0,0 +1,84 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _EXTRAFIELD_H_ +#define _EXTRAFIELD_H_ + +class Stream; + +namespace Zip +{ + +/// @addtogroup zipint_group +/// @ingroup zip_group +// @{ + + +// Forward Refs +class ExtraField; + +// Creation Helpers +typedef ExtraField *(*ExtraFieldCreateFn)(); + +template ExtraField * createExtraField() +{ + return new T; +} + +// ExtraField base class +class ExtraField +{ + ExtraField *mNext; + +protected: + U16 mID; + ExtraFieldCreateFn mCreateFn; + +public: + ExtraField() + { + mID = 0; + mCreateFn = NULL; + } + ExtraField(U16 id, ExtraFieldCreateFn fnCreate); + + virtual ~ExtraField() {} + + inline U16 getID() { return mID; } + + virtual bool read(Stream *stream) = 0; + + // Run time creation methods + static ExtraField *create(U16 id); +}; + +#define DeclareExtraField(name) \ + name(U16 id, ExtraFieldCreateFn fnCreate) : Parent(id, fnCreate) {} + +#define ImplementExtraField(name, id) \ + name gExtraField##name##instance(id, &createExtraField); + +// @} + +} // end namespace Zip + +#endif // _EXTRAFIELD_H_ diff --git a/Engine/source/core/util/zip/fileHeader.cpp b/Engine/source/core/util/zip/fileHeader.cpp new file mode 100644 index 000000000..f5f0ef5c6 --- /dev/null +++ b/Engine/source/core/util/zip/fileHeader.cpp @@ -0,0 +1,191 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/frameAllocator.h" +#include "core/stream/stream.h" + +#include "core/util/zip/fileHeader.h" +#include "core/util/zip/compressor.h" + +#include "core/util/safeDelete.h" + +#include "core/resizeStream.h" +#include "core/strings/stringFunctions.h" +#include "core/frameAllocator.h" + +namespace Zip +{ + +//----------------------------------------------------------------------------- +// Constructor/Destructor +//----------------------------------------------------------------------------- + +FileHeader::FileHeader() +{ + mHeaderSig = mFileHeaderSignature; + + mExtractVer = 20; + mFlags = 0; + mCompressMethod = Stored; + + mModTime = 0; + mModDate = 0; + + mCRC32 = 0; + + mCompressedSize = 0; + mUncompressedSize = 0; + + mFilename = ""; +} + +FileHeader::~FileHeader() +{ + for(S32 i = 0;i < mExtraFields.size();i++) + { + SAFE_DELETE(mExtraFields[i]); + } +} + +//----------------------------------------------------------------------------- +// Protected Methods +//----------------------------------------------------------------------------- + +bool FileHeader::readExtraFields(Stream *stream, U16 efLen) +{ + bool ret = true; + + U32 pos = stream->getPosition(); + U32 end = pos + efLen; + + while(stream->getPosition() < end) + { + U16 fieldSig, fieldSize; + + ret = false; + + ret |= stream->read(&fieldSig); + ret |= stream->read(&fieldSize); + if(! ret) + break; + + pos = stream->getPosition(); + + ExtraField *ef = ExtraField::create(fieldSig); + if(ef) + { + ret |= ef->read(stream); + + if(! ret) + delete ef; + else + mExtraFields.push_back(ef); + } + + stream->setPosition(pos + fieldSize); + } + + return ret; +} + +//----------------------------------------------------------------------------- +// Public Methods +//----------------------------------------------------------------------------- + +bool FileHeader::read(Stream *stream) +{ + stream->read(&mHeaderSig); + if(mHeaderSig != mFileHeaderSignature) + return false; + + stream->read(&mExtractVer); + stream->read(&mFlags); + stream->read(&mCompressMethod); + stream->read(&mModTime); + stream->read(&mModDate); + stream->read(&mCRC32); + stream->read(&mCompressedSize); + stream->read(&mUncompressedSize); + + U16 fnLen, efLen; + stream->read(&fnLen); + stream->read(&efLen); + + char *fn = new char[fnLen + 1]; + stream->read(fnLen, fn); + fn[fnLen] = 0; + mFilename = fn; + SAFE_DELETE_ARRAY(fn); + + + return readExtraFields(stream, efLen); +} + +bool FileHeader::write(Stream *stream) +{ + mHeaderSig = mFileHeaderSignature; + + stream->write(mHeaderSig); + + stream->write(mExtractVer); + stream->write(mFlags); + stream->write(mCompressMethod); + stream->write(mModTime); + stream->write(mModDate); + stream->write(mCRC32); + stream->write(mCompressedSize); + stream->write(mUncompressedSize); + + U16 fnLen = mFilename.length(), + efLen = 0; + stream->write(fnLen); + stream->write(efLen); + + if(fnLen) + stream->write(fnLen, mFilename); + + // FIXME [tom, 1/23/2007] Write extra fields here + + return true; +} + +//----------------------------------------------------------------------------- + +ExtraField *FileHeader::findExtraField(U16 id) +{ + for(S32 i = 0;i < mExtraFields.size();++i) + { + if(mExtraFields[i]->getID() == id) + return mExtraFields[i]; + } + + return NULL; +} + +//----------------------------------------------------------------------------- + +void FileHeader::setFilename(String filename) +{ + mFilename = filename; +} + +} // end namespace Zip diff --git a/Engine/source/core/util/zip/fileHeader.h b/Engine/source/core/util/zip/fileHeader.h new file mode 100644 index 000000000..0a7bf03be --- /dev/null +++ b/Engine/source/core/util/zip/fileHeader.h @@ -0,0 +1,112 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/util/zip/extraField.h" +#include "core/util/tVector.h" + +#ifndef _FILEHEADER_H_ +#define _FILEHEADER_H_ + +// Forward Refs +class Stream; + +namespace Zip +{ + +/// @addtogroup zipint_group +/// @ingroup zip_group +// @{ + +enum FileFlags +{ + Encrypted = BIT(0), + + // For implode compression + Implode8KDictionary = BIT(1), + Implode3ShannonFanoTrees = BIT(2), + + // For deflate compression + DeflateTypeMask = BIT(1) | BIT(2), + + FileInfoInDirectory = BIT(3), + + // Note that much of the following flag bits are unsupported for various reasons + ReservedEnhDeflate = BIT(4), + PatchData = BIT(5), + StrongEncryption = BIT(6), + + UnusedReserved1 = BIT(7), + UnusedReserved2 = BIT(8), + UnusedReserved3 = BIT(9), + UnusedReserved4 = BIT(10), + UnusedReserved5 = BIT(11), + + ReservedPKWARE1 = BIT(12), + + EncryptedDirectory = BIT(13), + + ReservedPKWARE2 = BIT(14), + ReservedPKWARE3 = BIT(15), +}; + +class FileHeader +{ + static const U32 mFileHeaderSignature = 0x04034b50; + +protected: + bool readExtraFields(Stream *stream, U16 efLen); + +public: + U32 mHeaderSig; + + U16 mExtractVer; + U16 mFlags; + U16 mCompressMethod; + + U16 mModTime; + U16 mModDate; + + U32 mCRC32; + + U32 mCompressedSize; + U32 mUncompressedSize; + + String mFilename; + + Vector mExtraFields; + + FileHeader(); + virtual ~FileHeader(); + + virtual bool read(Stream *stream); + virtual bool write(Stream *stream); + + ExtraField *findExtraField(U16 id); + + void setFilename(String filename); +}; + +// @} + +} // end namespace Zip + +#endif diff --git a/Engine/source/core/util/zip/unitTests/zipTest.h b/Engine/source/core/util/zip/unitTests/zipTest.h new file mode 100644 index 000000000..db11175e4 --- /dev/null +++ b/Engine/source/core/util/zip/unitTests/zipTest.h @@ -0,0 +1,107 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/stringTable.h" + +#ifndef _ZIPTEST_H_ +#define _ZIPTEST_H_ + +namespace Zip +{ + +//----------------------------------------------------------------------------- +// +// Things that need to be tested: +// +// Writing Zips: +// Write to file that doesn't already exist +// Write to file that we've already written to +// Write to file that already exists, but we haven't written to +// Attempt to open a file for write that's already open for write (should fail) +// ** Attempt to open a file that's open for read (should fail) +// Writing >= 5000 files to a zip +// Writing >= 5000 files to a zip in a number of folders +// +// Reading Zips: +// Open for read from file that doesn't exist (should fail) +// Read from file that already existed in the zip +// Read from file that we have written to the zip but not yet rebuilt +// ** Read from file that is already open for read (should fail) +// Read from file that is already open for write (should fail) +// +// Miscellaneous: +// Opening a file in the zip as ReadWrite (should fail) +// Enumerating files +// Deleting files +// ** Adding files +// Opening Zips with zip comments +// Opening Zips/Files with file comments +// Opening large zips that require searching for the EOCD +// +// All tests should be run on zip files that are opened for read, write and readwrite +// All tests need to be run for both forms of openArchive() +// +// All tests that require an existing zip file should be run on a zip created with +// a standard zip application in addition to zip files created by this code. +// +// Tests involving ReadWrite mode need to be done both with and without the zip +// file existing before the test. +// +// Tests marked ** are not possible to test yet as they are not supported by the +// zip code. +// +// [tom, 2/2/2007] I'm using WinZip 11 to create the baseline zips. It is probably +// worthwhile to include zips created in additional applications. +// +//----------------------------------------------------------------------------- + +// Directory relative to executable directory to use as a working directory +#define ZIPTEST_WORKING_DIR "unitTests/zip" + +// The filename of the zip file that we create for writing to +#define ZIPTEST_WRITE_FILENAME "zipUnitTest.zip" + +// The filename of the zip file created in a standard zip application +#define ZIPTEST_BASELINE_FILENAME "zipUnitTestBaseline.zip" + +// The filename of our working copy of the above so that we don't destroy the svn copy +#define ZIPTEST_WORKING_FILENAME "zipUnitTestWorking.zip" + +//----------------------------------------------------------------------------- +// Helper Functions +//----------------------------------------------------------------------------- + +inline StringTableEntry makeTestPath(const char *path) +{ + char buffer[1024], dir[1024]; + + Platform::makeFullPathName(ZIPTEST_WORKING_DIR, dir, sizeof(dir), Platform::getMainDotCsDir()); + Platform::makeFullPathName(path, buffer, sizeof(buffer), dir); + + return StringTable->insert(buffer, true); +} + +//----------------------------------------------------------------------------- + +} // end namespace Zip + +#endif // _ZIPTEST_H_ diff --git a/Engine/source/core/util/zip/unitTests/zipTestMisc.cpp b/Engine/source/core/util/zip/unitTests/zipTestMisc.cpp new file mode 100644 index 000000000..be5392c30 --- /dev/null +++ b/Engine/source/core/util/zip/unitTests/zipTestMisc.cpp @@ -0,0 +1,196 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/crc.h" +#include "core/strings/stringFunctions.h" +#include "core/util/zip/zipArchive.h" +#include "core/util/zip/unitTests/zipTest.h" + +#include "unit/test.h" +#include "unit/memoryTester.h" + + +using namespace UnitTesting; +using namespace Zip; + +CreateUnitTest(ZipTestMisc, "Zip/Misc") +{ +private: + StringTableEntry mWriteFilename; + StringTableEntry mBaselineFilename; + StringTableEntry mWorkingFilename; + +public: + ZipTestMisc() + { + mWriteFilename = makeTestPath(ZIPTEST_WRITE_FILENAME); + mBaselineFilename = makeTestPath(ZIPTEST_BASELINE_FILENAME); + mWorkingFilename = makeTestPath(ZIPTEST_WORKING_FILENAME); + } + + void run() + { + MemoryTester m; + m.mark(); + + // Clean up from a previous run + cleanup(); + + test(makeTestZip(), "Failed to create test zip"); + + miscTest(mWorkingFilename); + + // [tom, 2/7/2007] extractFile() uses the resource manager so it will + // create a resource object that will be detected as a "leak" since it + // won't be freed until much later. + + test(m.check(), "Zip misc test leaked memory! (Unless it says it's from the Resource Manager above, see comments in core/zip/unitTests/zipTestMisc.cc)"); + } + +private: + + //----------------------------------------------------------------------------- + + void cleanup() + { + if(Platform::isFile(mWriteFilename)) + dFileDelete(mWriteFilename); + if(Platform::isFile(mWorkingFilename)) + dFileDelete(mWorkingFilename); + } + + bool makeTestZip() + { + FileStream source, dest; + + if(! source.open(mBaselineFilename, Torque::FS::File::Read)) + { + fail("Failed to open baseline zip for read"); + return false; + } + + // [tom, 2/7/2007] FileStream's d'tor calls close() so we don't really have to do it here + + if(! dest.open(mWorkingFilename, Torque::FS::File::Write)) + { + fail("Failed to open working zip for write"); + return false; + } + + if(! dest.copyFrom(&source)) + { + fail("Failed to copy baseline zip"); + return false; + } + + dest.close(); + source.close(); + return true; + } + + //----------------------------------------------------------------------------- + + bool miscTest(const char *zipfile) + { + ZipArchive *zip = new ZipArchive; + bool ret = true; + + if(! zip->openArchive(zipfile, ZipArchive::ReadWrite)) + { + delete zip; + fail("Unable to open zip file"); + + return false; + } + + // Opening a file in the zip as ReadWrite (should fail) + Stream *stream; + if((stream = zip->openFile("readWriteTest.txt", ZipArchive::ReadWrite)) != NULL) + { + zip->closeFile(stream); + + fail("File opened successfully as read/write"); + ret = false; + } + + // Enumerating, Extracting and Deleting files + U32 curSize = zip->mDiskStream->getStreamSize(); + + if(zip->numEntries() > 0) + { + // Find the biggest file in the zip + const CentralDir *del = NULL; + for(S32 i = 0;i < zip->numEntries();++i) + { + const CentralDir &cd = (*zip)[i]; + + if(del == NULL || (del && cd.mUncompressedSize > del->mUncompressedSize)) + del = &cd; + } + + if(del) + { + // Extract the file + const char *ptr = dStrrchr(del->mFilename, '/'); + if(ptr) + ++ptr; + else + ptr = del->mFilename; + StringTableEntry fn = makeTestPath(ptr); + + ret = test(zip->extractFile(del->mFilename, fn), "Failed to extract file."); + + // Then delete it + ret = test(zip->deleteFile(del->mFilename), "ZipArchive::deleteFile() failed?!"); + + // Close and reopen the zip to force it to rebuild + zip->closeArchive(); + + if(! zip->openArchive(zipfile, ZipArchive::ReadWrite)) + { + delete zip; + fail("Unable to re-open zip file?!"); + + return false; + } + } + else + { + fail("Couldn't find a file to delete?!"); + ret = false; + } + } + else + { + fail("Baseline zip has no files."); + ret = false; + } + + U32 newSize = zip->mDiskStream->getStreamSize(); + test(newSize != curSize, "Zip file size didn't change when deleting a file ?!"); + + zip->closeArchive(); + delete zip; + + return ret; + } +}; diff --git a/Engine/source/core/util/zip/unitTests/zipTestRead.cpp b/Engine/source/core/util/zip/unitTests/zipTestRead.cpp new file mode 100644 index 000000000..45be733d4 --- /dev/null +++ b/Engine/source/core/util/zip/unitTests/zipTestRead.cpp @@ -0,0 +1,252 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "unit/test.h" +#include "unit/memoryTester.h" + +#include "core/util/zip/zipArchive.h" +#include "core/util/zip/unitTests/zipTest.h" + +#include "core/stringTable.h" +#include "core/crc.h" + +using namespace UnitTesting; +using namespace Zip; + +CreateUnitTest(ZipTestRead, "Zip/Read") +{ +private: + StringTableEntry mWriteFilename; + StringTableEntry mBaselineFilename; + StringTableEntry mWorkingFilename; + +public: + ZipTestRead() + { + mWriteFilename = makeTestPath(ZIPTEST_WRITE_FILENAME); + mBaselineFilename = makeTestPath(ZIPTEST_BASELINE_FILENAME); + mWorkingFilename = makeTestPath(ZIPTEST_WORKING_FILENAME); + } + + void run() + { + MemoryTester m; + m.mark(); + + // Clean up from a previous run + cleanup(); + + // Read mode tests with the baseline zip + readTest(mBaselineFilename); + + // Read mode tests with a zip created by us + test(makeTestZip(), "Failed to make test zip"); + readTest(mWorkingFilename); + + // Do read/write mode tests + readWriteTest(mWriteFilename); + + test(m.check(), "Zip read test leaked memory!"); + } + +private: + + //----------------------------------------------------------------------------- + + void cleanup() + { + if(Platform::isFile(mWriteFilename)) + dFileDelete(mWriteFilename); + if(Platform::isFile(mWorkingFilename)) + dFileDelete(mWorkingFilename); + } + + //----------------------------------------------------------------------------- + + bool writeFile(ZipArchive *zip, const char *filename) + { + Stream * stream = zip->openFile(filename, ZipArchive::Write); + if(stream != NULL) + { + stream->writeLine((U8 *)"This is a test file for reading from a zip created with the zip code.\r\nIt is pointless by itself, but needs to be long enough that it gets compressed.\r\n"); + zip->closeFile(stream); + return true; + } + + return false; + } + + bool makeTestZip() + { + ZipArchive *zip = new ZipArchive; + + if(!zip->openArchive(mWorkingFilename, ZipArchive::Write)) + { + delete zip; + fail("Unable to open zip file"); + + return false; + } + + test(writeFile(zip, "test.txt"), "Failed to write file into test zip"); + test(writeFile(zip, "console.log"), "Failed to write file into test zip"); + test(writeFile(zip, "folder/OpenAL32.dll"), "Failed to write file into test zip"); + + zip->closeArchive(); + delete zip; + + return true; + } + + //----------------------------------------------------------------------------- + + bool testReadFile(ZipArchive *zip, const char *filename) + { + // [tom, 2/5/2007] Try using openFile() first for fairest real-world test + Stream *stream; + if((stream = zip->openFile(filename, ZipArchive::Read)) == NULL) + return false; + + const CentralDir *cd = zip->findFileInfo(filename); + if(cd == NULL) + { + // [tom, 2/5/2007] This shouldn't happen + zip->closeFile(stream); + + fail("testReadFile - File opened successfully, but couldn't find central directory. This is bad."); + + return false; + } + + U32 crc = CRC::INITIAL_CRC_VALUE; + + bool ret = true; + U8 buffer[1024]; + U32 numBytes = stream->getStreamSize() - stream->getPosition(); + while((stream->getStatus() != Stream::EOS) && numBytes > 0) + { + U32 numRead = numBytes > sizeof(buffer) ? sizeof(buffer) : numBytes; + if(! stream->read(numRead, buffer)) + { + ret = false; + fail("testReadFile - Couldn't read from stream"); + break; + } + + crc = CRC::calculateCRC(buffer, numRead, crc); + + numBytes -= numRead; + } + + if(ret) + { + // CRC Check + crc ^= CRC::CRC_POSTCOND_VALUE; + if(crc != cd->mCRC32) + { + fail("testReadFile - File failed CRC check"); + ret = false; + } + } + + zip->closeFile(stream); + return ret; + } + + //----------------------------------------------------------------------------- + + bool readTest(const char *zipfile) + { + ZipArchive *zip = new ZipArchive; + + if(!zip->openArchive(zipfile, ZipArchive::Read)) + { + delete zip; + fail("Unable to open zip file"); + + return false; + } + + // Try reading files that exist in various formats + testReadFile(zip, "test.txt"); + testReadFile(zip, "console.log"); + testReadFile(zip, "folder/OpenAL32.dll"); + + // Try reading a file that doesn't exist + if(testReadFile(zip, "hopefullyDoesntExist.bla.bla.foo")) + fail("Opening a file the didn't exist succeeded"); + + zip->closeArchive(); + delete zip; + + return true; + } + + bool readWriteTest(const char *zipfile) + { + ZipArchive *zip = new ZipArchive; + bool ret = true; + + if(!zip->openArchive(zipfile, ZipArchive::ReadWrite)) + { + delete zip; + fail("Unable to open zip file"); + + return false; + } + + // Test writing a file and then reading it back before the zip is rebuilt + test(writeFile(zip, "test.txt"), "Failed to write file to test zip"); + test(testReadFile(zip, "test.txt"), "Failed to read file back from test zip"); + + // Read from file that is already open for write (should fail) + Stream *wrStream = zip->openFile("writeTest.txt", ZipArchive::Write); + if(wrStream != NULL) + { + wrStream->writeLine((U8 *)"Test text to make a valid file"); + + // This next open should fail + Stream * rdStream = zip->openFile("writeTest.txt", ZipArchive::Read); + if(rdStream != NULL) + { + fail("Succeeded in opening a file for read that's already open for write"); + ret = false; + + zip->closeFile(rdStream); + } + + zip->closeFile(wrStream); + } + else + { + fail("Could not open file for write"); + ret = false; + } + + zip->closeArchive(); + delete zip; + + return ret; + } +}; diff --git a/Engine/source/core/util/zip/unitTests/zipTestWrite.cpp b/Engine/source/core/util/zip/unitTests/zipTestWrite.cpp new file mode 100644 index 000000000..cdfaa8778 --- /dev/null +++ b/Engine/source/core/util/zip/unitTests/zipTestWrite.cpp @@ -0,0 +1,244 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/strings/stringFunctions.h" +#include "core/util/zip/zipArchive.h" +#include "core/util/zip/unitTests/zipTest.h" + +#include "unit/test.h" +#include "unit/memoryTester.h" + + +using namespace UnitTesting; +using namespace Zip; + +CreateUnitTest(ZipTestWrite, "Zip/Write") +{ +private: + StringTableEntry mWriteFilename; + StringTableEntry mBaselineFilename; + StringTableEntry mWorkingFilename; + +public: + /// Number of files to write for testing large numbers of files in a zip + static const U32 mTons = 5000; + + ZipTestWrite() + { + mWriteFilename = makeTestPath(ZIPTEST_WRITE_FILENAME); + mBaselineFilename = makeTestPath(ZIPTEST_BASELINE_FILENAME); + mWorkingFilename = makeTestPath(ZIPTEST_WORKING_FILENAME); + } + + void run() + { + MemoryTester m; + m.mark(); + + // Clean up from a previous run + cleanup(); + + // Test writing to zip files without the zip file existing + testWriting(mWriteFilename, ZipArchive::Read); + testWriting(mWriteFilename, ZipArchive::ReadWrite); + + // Cleanup to try write without existing file + cleanup(); + testWriting(mWriteFilename, ZipArchive::Write); + + // Now use previous file to test everything again with an existing file + testWriting(mWriteFilename, ZipArchive::ReadWrite); + testWriting(mWriteFilename, ZipArchive::Write); + testWriting(mWriteFilename, ZipArchive::Read); + + testWritingTons(makeTestPath("WriteTons.zip")); + + test(m.check(), "Zip write test leaked memory!"); + } + +private: + + //----------------------------------------------------------------------------- + + void cleanup() + { + if(Platform::isFile(mWriteFilename)) + dFileDelete(mWriteFilename); + if(Platform::isFile(mWorkingFilename)) + dFileDelete(mWorkingFilename); + } + + //----------------------------------------------------------------------------- + + bool writeFile(ZipArchive *zip, const char *filename, char *fileBuf, U32 bufSize, bool mustNotExist = false, const char *contents = NULL) + { + if(mustNotExist && fileBuf && bufSize > 0) + { + // Find a unique filename + U32 count = 1; + dStrcpy(fileBuf, filename); + + while(zip->findFileInfo(fileBuf)) + { + dSprintf(fileBuf, bufSize, "%s.%04x", filename, count++); + + if(count == 0) + { + fail("writeFile - got stuck in an infinite loop looking for a unique filename"); + return false; + } + } + } + else if(fileBuf && bufSize > 0) + dStrcpy(fileBuf, filename); + + // Try and write to the file + Stream * stream = zip->openFile(fileBuf ? fileBuf : filename, ZipArchive::Write); + if(stream != NULL) + { + stream->writeLine(contents ? (U8 *)contents : (U8 *)"This is a test of writing to a file."); + zip->closeFile(stream); + + return true; + } + + return false; + } + + //----------------------------------------------------------------------------- + + bool testWriting(const char *zipfile, ZipArchive::AccessMode mode) + { + ZipArchive *zip = new ZipArchive; + + if(! zip->openArchive(zipfile, mode)) + { + delete zip; + + // This is only an error if we're not trying to open as read + + if(mode != ZipArchive::Read) + fail("Unable to open zip file"); + + return mode == ZipArchive::Read; + } + + char fileBuf[1024]; + + // Write to file that doesn't already exist + if(!writeFile(zip, "testWriteNew.txt", fileBuf, sizeof(fileBuf), true)) + { + fail("Couldn't write to a file that didn't already exist"); + goto bail; + } + + // Write to file that we've already written to + if(!writeFile(zip, fileBuf, NULL, 0, false, "This is a test of overwriting the file.")) + { + if(mode != ZipArchive::Read) + fail("Couldn't write to a file that we've already written to"); + goto bail; + } + + // Write to file that already exists, but we haven't written to + // To do this, we need to close and re-open the zipfile + + zip->closeArchive(); + delete zip; + + zip = new ZipArchive; + if(! zip->openArchive(zipfile, mode)) + { + delete zip; + fail("Unable to re-open zip file. Strange!"); + return false; + } + + // Use the file we already overwrote since we are sure of it's filename + if(!writeFile(zip, fileBuf, NULL, 0, false, "This is a test of overwriting the file again.")) + { + if(mode != ZipArchive::Read) + fail("Couldn't write to a file that already existed"); + goto bail; + } + + { + // Move this into its own scope to deal with goto 'crosses initialization' errors + // Attempt to open a file for write that's already open for write (should fail) + Stream * stream1 = zip->openFile("writeLockTest.txt", ZipArchive::Write); + if(stream1 != NULL) + { + stream1->writeLine((U8 *)"Test text to make a valid file"); + + // This next open should fail + Stream * stream2 = zip->openFile("writeLockTest.txt", ZipArchive::Write); + if(stream2 != NULL) + { + if(mode != ZipArchive::Read) + fail("Opening a file for write multiple times should have failed"); + zip->closeFile(stream2); + } + zip->closeFile(stream1); + } + } + +bail: + zip->closeArchive(); + delete zip; + + return true; + } + + //----------------------------------------------------------------------------- + + bool testWritingTons(const char *zipfile) + { + ZipArchive zip; + + if(! zip.openArchive(zipfile, ZipArchive::Write)) + { + fail("Unable to open zip file"); + return false; + } + + bool ret = true; + + for(U32 i = 0;i < mTons;++i) + { + char fname[256]; + dSprintf(fname, sizeof(fname), "file%04x.txt", i); + + if(! writeFile(&zip, fname, NULL, 0)) + { + fail("Failed to write file"); + fail(fname); + + ret = false; + + break; + } + } + + zip.closeArchive(); + return ret; + } +}; diff --git a/Engine/source/core/util/zip/zipArchive.cpp b/Engine/source/core/util/zip/zipArchive.cpp new file mode 100644 index 000000000..437d0c3f6 --- /dev/null +++ b/Engine/source/core/util/zip/zipArchive.cpp @@ -0,0 +1,926 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/util/zip/zipArchive.h" + +#include "core/stream/stream.h" +#include "core/stream/fileStream.h" +#include "core/filterStream.h" +#include "core/util/zip/zipCryptStream.h" +#include "core/crc.h" +//#include "core/resManager.h" + +#include "console/console.h" + +#include "core/util/zip/compressor.h" +#include "core/util/zip/zipTempStream.h" +#include "core/util/zip/zipStatFilter.h" + +#ifdef TORQUE_ZIP_AES +#include "core/zipAESCryptStream.h" +#include "core/zip/crypto/aes.h" +#endif + +#include "core/util/safeDelete.h" + +#include "app/version.h" + +namespace Zip +{ + +//----------------------------------------------------------------------------- +// Constructor/Destructor +//----------------------------------------------------------------------------- + +ZipArchive::ZipArchive() : + mStream(NULL), + mDiskStream(NULL), + mMode(Read), + mRoot(NULL), + mFilename(NULL) +{ +} + +ZipArchive::~ZipArchive() +{ + closeArchive(); +} + +//----------------------------------------------------------------------------- +// Protected Methods +//----------------------------------------------------------------------------- + +bool ZipArchive::readCentralDirectory() +{ + mEntries.clear(); + SAFE_DELETE(mRoot); + mRoot = new ZipEntry; + mRoot->mName = ""; + mRoot->mIsDirectory = true; + mRoot->mCD.setFilename(""); + + if(! mEOCD.findInStream(mStream)) + return false; + + if(! mEOCD.read(mStream)) + return false; + + if(mEOCD.mDiskNum != mEOCD.mStartCDDiskNum || + mEOCD.mNumEntriesInThisCD != mEOCD.mTotalEntriesInCD) + { + if(isVerbose()) + Con::errorf("ZipArchive::readCentralDirectory - %s: Zips that span multiple disks are not supported.", mFilename ? mFilename : ""); + return false; + } + + if(! mStream->setPosition(mEOCD.mCDOffset)) + return false; + + for(S32 i = 0;i < mEOCD.mNumEntriesInThisCD;++i) + { + ZipEntry *ze = new ZipEntry; + if(! ze->mCD.read(mStream)) + { + delete ze; + + if(isVerbose()) + Con::errorf("ZipArchive::readCentralDirectory - %s: Error reading central directory.", mFilename ? mFilename : ""); + return false; + } + + insertEntry(ze); + } + + return true; +} + +void ZipArchive::dumpCentralDirectory(ZipEntry* entry, String* indent) +{ + // if entry is null, use root + if (entry == NULL) + entry = mRoot; + + if (!entry) + return; + + String emptyIndent; + if (indent == NULL) + indent = &emptyIndent; + + Con::printf("%s%s%s", indent->c_str(), entry->mIsDirectory ? "/" : "", entry->mName.c_str()); + for (Map::Iterator iter = entry->mChildren.begin(); + iter != entry->mChildren.end(); + ++iter) + { + String newIdent = *indent + " "; + dumpCentralDirectory((*iter).value, &(newIdent)); + } +} + +//----------------------------------------------------------------------------- + +void ZipArchive::insertEntry(ZipEntry *ze) +{ + char path[1024]; + dStrncpy(path, ze->mCD.mFilename.c_str(), sizeof(path)); + path[sizeof(path) - 1] = 0; + + for(S32 i = 0;i < dStrlen(path);++i) + { + if(path[i] == '\\') + path[i] = '/'; + } + + ZipEntry *root = mRoot; + + char *ptr = path, *slash = NULL; + do + { + slash = dStrchr(ptr, '/'); + if(slash) + { + // Add the directory + *slash = 0; + + // try to get root, create if not found + ZipEntry *newEntry = NULL; + if (!root->mChildren.tryGetValue(ptr, newEntry)) + { + newEntry = new ZipEntry; + newEntry->mParent = root; + newEntry->mName = String(ptr); + newEntry->mIsDirectory = true; + newEntry->mCD.setFilename(path); + + root->mChildren[ptr] = newEntry; + } + + root = newEntry; + + *slash = '/'; + ptr = slash + 1; + } + else + { + // Add the file. + if(*ptr) + { + ze->mIsDirectory = false; + ze->mName = ptr; + ze->mParent = root; + root->mChildren[ptr] = ze; + mEntries.push_back(ze); + } + else + { + // [tom, 2/6/2007] If ptr is empty, this was a directory entry. Since + // we created a new entry for it above, we need to delete the old + // pointer otherwise it will leak as it won't have got inserted. + + delete ze; + } + } + } while(slash); +} + +void ZipArchive::removeEntry(ZipEntry *ze) +{ + if(ze == mRoot) + { + // [tom, 2/1/2007] We don't want to remove the root as it should always + // be removed through closeArchive() + AssertFatal(0, "ZipArchive::removeEntry - Attempting to remove the root"); + return; + } + + // Can't iterate the hash table, so we can't do this safely + AssertFatal(!ze->mIsDirectory, "ZipArchive::removeEntry - Cannot remove a directory"); + + // See if we have a temporary file for this entry + Vector::iterator i; + for(i = mTempFiles.begin();i != mTempFiles.end();++i) + { + if((*i)->getCentralDir() == &ze->mCD) + { + SAFE_DELETE(*i); + mTempFiles.erase(i); + + break; + } + } + + // Remove from the tree + Vector::iterator j; + for(j = mEntries.begin();j != mEntries.end();++j) + { + if(*j == ze) + { + mEntries.erase(j); + break; + } + } + + // [tom, 2/2/2007] This must be last, as ze is no longer valid once it's + // removed from the parent. + ZipEntry *z = ze->mParent->mChildren[ze->mName]; + ze->mParent->mChildren.erase(ze->mName); + delete z; +} + +//----------------------------------------------------------------------------- + +CentralDir *ZipArchive::findFileInfo(const char *filename) +{ + ZipEntry *ze = findZipEntry(filename); + return ze ? &ze->mCD : NULL; +} + +ZipArchive::ZipEntry *ZipArchive::findZipEntry(const char *filename) +{ + char path[1024]; + dStrncpy(path, filename, sizeof(path)); + path[sizeof(path) - 1] = 0; + + for(S32 i = 0;i < dStrlen(path);++i) + { + if(path[i] == '\\') + path[i] = '/'; + } + + ZipEntry *root = mRoot; + + char *ptr = path, *slash = NULL; + do + { + slash = dStrchr(ptr, '/'); + if(slash) + { + // Find the directory + *slash = 0; + + // check child dict for new root + ZipEntry *newRoot = NULL; + if (!root->mChildren.tryGetValue(ptr, newRoot)) + return NULL; + + root = newRoot; + + ptr = slash + 1; + } + else + { + // Find the file + ZipEntry* entry = NULL; + if (root->mChildren.tryGetValue(ptr, entry)) + return entry; + } + } while(slash); + + return NULL; +} + +//----------------------------------------------------------------------------- + +Stream *ZipArchive::createNewFile(const char *filename, Compressor *method) +{ + ZipEntry *ze = new ZipEntry; + ze->mIsDirectory = false; + ze->mCD.setFilename(filename); + insertEntry(ze); + + ZipTempStream *stream = new ZipTempStream(&ze->mCD); + if(stream->open()) + { + Stream *retStream = method->createWriteStream(&ze->mCD, stream); + if(retStream == NULL) + { + delete stream; + return NULL; + } + + ZipStatFilter *filter = new ZipStatFilter(&ze->mCD); + if(! filter->attachStream(retStream)) + { + delete filter; + delete retStream; + delete stream; + return NULL; + } + + ze->mCD.mCompressMethod = method->getMethod(); + ze->mCD.mInternalFlags |= CDFileOpen; + + return filter; + } + + return NULL; +} + +void ZipArchive::updateFile(ZipTempStream *stream) +{ + CentralDir *cd = stream->getCentralDir(); + + // [tom, 1/23/2007] Uncompressed size and CRC32 are updated by ZipStatFilter + cd->mCompressedSize = stream->getStreamSize(); + cd->mInternalFlags |= CDFileDirty; + cd->mInternalFlags &= ~CDFileOpen; + + // Upper byte should be zero, lower is version as major * 10 + minor + cd->mVersionMadeBy = (getVersionNumber() / 100) & 0xff; + cd->mExtractVer = 20; + + U32 dosTime = currentTimeToDOSTime(); + cd->mModTime = dosTime & 0x0000ffff; + cd->mModDate = (dosTime & 0xffff0000) >> 16; + + mTempFiles.push_back(stream); +} + +//----------------------------------------------------------------------------- + +U32 ZipArchive::localTimeToDOSTime(const Torque::Time::DateTime &dt) +{ + // DOS time format + // http://msdn.microsoft.com/en-us/library/ms724274(VS.85).aspx + return TimeToDOSTime(Torque::Time(dt)); +} + +U32 ZipArchive::TimeToDOSTime(const Torque::Time& t) +{ + S32 year,month,day,hour,minute,second,microsecond; + t.get(&year, &month, &day, &hour, &minute, &second, µsecond); + + if(year > 1980) // De Do Do Do, De Da Da Da + year -= 1980; + + return (((day) + (32 * (month)) + (512 * year)) << 16) | ((second/2) + (32* minute) + (2048 * (U32)hour)); +} + +Torque::Time ZipArchive::DOSTimeToTime(U16 time, U16 date) +{ + Torque::Time::DateTime dt; + dt.microsecond = 0; + dt.hour = (time & 0xF800) >> 11; + dt.minute = (time & 0x07E0) >> 5; + dt.second = (time & 0x001F)*2; + + dt.year = ((date & 0xFE00) >> 9) + 1980; + dt.month = (date & 0x01E0) >> 5; + dt.day = (date & 0x001F); + + return Torque::Time(dt); +} + +Torque::Time ZipArchive::DOSTimeToTime(U32 dosTime) +{ + U16 time = dosTime & 0x0000ffff; + U16 date = (dosTime & 0xffff0000) >> 16; + + return ZipArchive::DOSTimeToTime(time, date); +} + +U32 ZipArchive::currentTimeToDOSTime() +{ + Torque::Time::DateTime dt; + Torque::Time::getCurrentDateTime(dt); + + return localTimeToDOSTime(dt); +} + +//----------------------------------------------------------------------------- + +// [tom, 1/24/2007] The general idea here is we want to create a new file, +// copy any data from the old zip file and add the new stuff. Once the new +// zip is created, delete the old one and rename the new one. + +bool ZipArchive::rebuildZip() +{ + String newZipName; + FileStream tempFile; + Stream *zipFile = mStream; + + // FIXME [tom, 1/24/2007] Temporary for expediting testing + if(mFilename == NULL) + return false; + + if(mMode == ReadWrite) + { + newZipName = String(mFilename) + ".new"; + + if(! tempFile.open(newZipName, mMode == Write ? Torque::FS::File::Write : Torque::FS::File::ReadWrite)) + return false; + + zipFile = &tempFile; + } + + // Copy any unmodified files + for(S32 i = 0;i < mEntries.size();++i) + { + ZipEntry *entry = mEntries[i]; + + // Directories are internal only for lookup purposes + if(entry->mIsDirectory || (entry->mCD.mInternalFlags & (CDFileDirty | CDFileDeleted))) + continue; + + copyFileToNewZip(&entry->mCD, zipFile); + } + + // Copy any dirty files + for(S32 i = 0;i < mTempFiles.size();++i) + { + ZipTempStream *zts = mTempFiles[i]; + + writeDirtyFileToNewZip(zts, zipFile); + zts->close(); + delete zts; + mTempFiles[i] = NULL; + } + mTempFiles.clear(); + + // Write central directory + mEOCD.mCDOffset = zipFile->getPosition(); + mEOCD.mNumEntriesInThisCD = 0; + + for(S32 i = 0;i < mEntries.size();++i) + { + ZipEntry *entry = mEntries[i]; + + // [tom, 1/24/2007] Directories are internal only for lookup purposes + if(entry->mIsDirectory || (entry->mCD.mInternalFlags & CDFileDeleted) != 0) + continue; + + ++mEOCD.mNumEntriesInThisCD; + if(! entry->mCD.write(zipFile)) + break; + } + + mEOCD.mCDSize = zipFile->getPosition() - mEOCD.mCDOffset; + mEOCD.mTotalEntriesInCD = mEOCD.mNumEntriesInThisCD; + + mEOCD.mDiskNum = 0; + mEOCD.mStartCDDiskNum = 0; + + mEOCD.write(zipFile); + + if(mMode == ReadWrite) + { + // Close file, replace old zip with it + tempFile.close(); + + // [tom, 2/1/2007] The disk stream must be closed else we can't rename + // the file. Since rebuildZip() is only called from closeArchive() this + // should be safe. + if(mDiskStream) + { + mDiskStream->close(); + + delete mDiskStream; + mDiskStream = NULL; + } + + String oldRename; + oldRename = String(mFilename) + ".old"; + + if(! Torque::FS::Rename(mFilename, oldRename)) + return false; + + if(! Torque::FS::Rename(newZipName, mFilename)) + return false; + + Torque::FS::Remove(oldRename); + } + return true; +} + +bool ZipArchive::writeDirtyFileToNewZip(ZipTempStream *fileStream, Stream *zipStream) +{ + CentralDir *cdir = fileStream->getCentralDir(); + FileHeader fh(*cdir); + fh.setFilename(cdir->mFilename); + + cdir->mLocalHeadOffset = zipStream->getPosition(); + + // Write header and file + if(! fh.write(zipStream)) + return false; + + if(! fileStream->rewind()) + return false; + + return zipStream->copyFrom(fileStream); +} + +bool ZipArchive::copyFileToNewZip(CentralDir *cdir, Stream *newZipStream) +{ + // [tom, 1/24/2007] Using the stored compressor allows us to copy the raw + // data regardless of compression method without having to re-compress it. + Compressor *comp = Compressor::findCompressor(Stored); + if(comp == NULL) + return false; + + if(! mStream->setPosition(cdir->mLocalHeadOffset)) + return false; + + // Copy file header + // FIXME [tom, 1/24/2007] This will currently not copy the extra fields + FileHeader fh; + if(! fh.read(mStream)) + return false; + + cdir->mLocalHeadOffset = newZipStream->getPosition(); + + if(! fh.write(newZipStream)) + return false; + + // Copy file data + Stream *readS = comp->createReadStream(cdir, mStream); + if(readS == NULL) + return false; + + bool ret = newZipStream->copyFrom(readS); + + // [tom, 1/24/2007] closeFile() just frees the relevant filters and + // thus it is safe to call from here. + closeFile(readS); + + return ret; +} + +//----------------------------------------------------------------------------- +// Public Methods +//----------------------------------------------------------------------------- + +void ZipArchive::setFilename(const char *filename) +{ + SAFE_FREE(mFilename); + if(filename) + mFilename = dStrdup(filename); +} + +//----------------------------------------------------------------------------- + +bool ZipArchive::openArchive(const char *filename, AccessMode mode /* = Read */) +{ + if(mode != Read && mode != Write && mode != ReadWrite) + return false; + + closeArchive(); + + mDiskStream = new FileStream; + if(mDiskStream->open(filename, (Torque::FS::File::AccessMode)mode)) + { + setFilename(filename); + + if(openArchive(mDiskStream, mode)) + return true; + } + + // Cleanup just in case openArchive() failed + closeArchive(); + + return false; +} + +bool ZipArchive::openArchive(Stream *stream, AccessMode mode /* = Read */) +{ + if(mode != Read && mode != Write && mode != ReadWrite) + return false; + + mStream = stream; + mMode = mode; + + if(mode == Read || mode == ReadWrite) + { + bool ret = readCentralDirectory(); + if(mode == Read) + return ret; + + return true; + } + else + { + mEntries.clear(); + SAFE_DELETE(mRoot); + mRoot = new ZipEntry; + mRoot->mName = ""; + mRoot->mIsDirectory = true; + mRoot->mCD.setFilename(""); + } + + return true; +} + +void ZipArchive::closeArchive() +{ + if(mMode == Write || mMode == ReadWrite) + rebuildZip(); + + // Free any remaining temporary files + for(S32 i = 0;i < mTempFiles.size();++i) + { + SAFE_DELETE(mTempFiles[i]); + } + mTempFiles.clear(); + + // Close the zip file stream and clean up + if(mDiskStream) + { + mDiskStream->close(); + + delete mDiskStream; + mDiskStream = NULL; + } + + mStream = NULL; + + SAFE_FREE(mFilename); + SAFE_DELETE(mRoot); + mEntries.clear(); +} + +//----------------------------------------------------------------------------- +Stream * ZipArchive::openFile(const char *filename, AccessMode mode /* = Read */) +{ + ZipEntry* ze = findZipEntry(filename); + return openFile(filename, ze, mode); +} + +Stream * ZipArchive::openFile(const char *filename, ZipEntry* ze, AccessMode mode /* = Read */) +{ + if(mode == Read) + { + if(ze == NULL) + return NULL; + + return openFileForRead(&ze->mCD); + } + + if(mode == Write) + { + if(ze) + { + if(ze->mCD.mInternalFlags & CDFileOpen) + { + if(isVerbose()) + Con::errorf("ZipArchive::openFile - File %s is already open", filename); + return NULL; + } + + // Remove the old entry so we can create a new one + removeEntry(ze); + ze = NULL; + } + + return createNewFile(filename, Compressor::findCompressor(Deflated)); + } + + if(isVerbose()) + Con::errorf("ZipArchive::openFile - Files within zips can only be opened as read or write, but not both at the same time."); + + return NULL; +} + +void ZipArchive::closeFile(Stream *stream) +{ + FilterStream *currentStream, *nextStream; + + nextStream = dynamic_cast(stream); + while (nextStream) + { + currentStream = nextStream; + stream = currentStream->getStream(); + + currentStream->detachStream(); + + nextStream = dynamic_cast(stream); + + delete currentStream; + } + + ZipTempStream *tempStream = dynamic_cast(stream); + if(tempStream && (tempStream->getCentralDir()->mInternalFlags & CDFileOpen)) + { + // [tom, 1/23/2007] This is a temporary file we are writing to + // so we need to update the relevant information in the header. + updateFile(tempStream); + } +} + +//----------------------------------------------------------------------------- + +Stream *ZipArchive::openFileForRead(const CentralDir *fileCD) +{ + if(mMode != Read && mMode != ReadWrite) + return NULL; + + if((fileCD->mInternalFlags & (CDFileDeleted | CDFileOpen)) != 0) + return NULL; + + Stream *stream = mStream; + + if(fileCD->mInternalFlags & CDFileDirty) + { + // File is dirty, we need to read from the temporary file + for(S32 i = 0;i < mTempFiles.size();++i) + { + if(mTempFiles[i]->getCentralDir() == fileCD) + { + // Found the temporary file + if(! mTempFiles[i]->rewind()) + { + if(isVerbose()) + Con::errorf("ZipArchive::openFile - %s: %s is dirty, but could not rewind temporary file?", mFilename ? mFilename : "", fileCD->mFilename.c_str()); + return NULL; + } + + stream = mTempFiles[i]; + break; + } + } + + if(stream == mStream) + { + if(isVerbose()) + Con::errorf("ZipArchive::openFile - %s: %s is dirty, but no temporary file found?", mFilename ? mFilename : "", fileCD->mFilename.c_str()); + return NULL; + } + } + else + { + // Read from the zip file directly + if(! mStream->setPosition(fileCD->mLocalHeadOffset)) + { + if(isVerbose()) + Con::errorf("ZipArchive::openFile - %s: Could not locate local header for file %s", mFilename ? mFilename : "", fileCD->mFilename.c_str()); + return NULL; + } + + FileHeader fh; + if(! fh.read(mStream)) + { + if(isVerbose()) + Con::errorf("ZipArchive::openFile - %s: Could not read local header for file %s", mFilename ? mFilename : "", fileCD->mFilename.c_str()); + return NULL; + } + } + + Stream *attachTo = stream; + U16 compMethod = fileCD->mCompressMethod; + + if(fileCD->mFlags & Encrypted) + { + if(fileCD->mCompressMethod == AESEncrypted) + { + // [tom, 1/19/2007] Whilst AES support does exist, I'm not including it in TGB + // to avoid having to deal with crypto export legal issues. + Con::errorf("ZipArchive::openFile - %s: File %s is AES encrypted, but AES is not supported in this version.", mFilename ? mFilename : "", fileCD->mFilename.c_str()); + } + else + { + ZipCryptRStream *cryptStream = new ZipCryptRStream; + cryptStream->setPassword(DEFAULT_ZIP_PASSWORD); + cryptStream->setFileEndPos(stream->getPosition() + fileCD->mCompressedSize); + if(! cryptStream->attachStream(stream)) + { + delete cryptStream; + return NULL; + } + + attachTo = cryptStream; + } + } + + Compressor *comp = Compressor::findCompressor(compMethod); + if(comp == NULL) + { + if(isVerbose()) + Con::errorf("ZipArchive::openFile - %s: Unsupported compression method (%d) for file %s", mFilename ? mFilename : "", fileCD->mCompressMethod, fileCD->mFilename.c_str()); + return NULL; + } + + return comp->createReadStream(fileCD, attachTo); +} + +//----------------------------------------------------------------------------- + +bool ZipArchive::addFile(const char *filename, const char *pathInZip, bool replace /* = true */) +{ + FileStream f; + if (!f.open(filename, Torque::FS::File::Read)) + return false; + + const CentralDir *cd = findFileInfo(pathInZip); + if(! replace && cd && (cd->mInternalFlags & CDFileDeleted) == 0) + return false; + + Stream *dest = openFile(pathInZip, Write); + if(dest == NULL) + { + f.close(); + return false; + } + + bool ret = dest->copyFrom(&f); + + closeFile(dest); + f.close(); + + return ret; +} + +bool ZipArchive::extractFile(const char *pathInZip, const char *filename, bool *crcFail /* = NULL */) +{ + if(crcFail) + *crcFail = false; + + const CentralDir *realCD = findFileInfo(pathInZip); + if(realCD == NULL) + return false; + + FileStream dest; + if(! dest.open(filename, Torque::FS::File::Write)) + return false; + + Stream *source = openFile(pathInZip, Read); + if(source == NULL) + { + dest.close(); + return false; + } + + // [tom, 2/7/2007] CRC checking the lazy man's way + // ZipStatFilter only fails if it doesn't have a central directory, so this is safe + CentralDir fakeCD; + ZipStatFilter zsf(&fakeCD); + zsf.attachStream(source); + + bool ret = dest.copyFrom(&zsf); + + zsf.detachStream(); + + if(ret && fakeCD.mCRC32 != realCD->mCRC32) + { + if(crcFail) + *crcFail = true; + + if(isVerbose()) + Con::errorf("ZipArchive::extractFile - CRC failure extracting file %s", pathInZip); + ret = false; + } + + closeFile(source); + dest.close(); + + return ret; +} + +bool ZipArchive::deleteFile(const char *filename) +{ + if(mMode != Write && mMode != ReadWrite) + return false; + + CentralDir *cd = findFileInfo(filename); + if(cd == NULL) + return false; + + cd->mInternalFlags |= CDFileDeleted; + + // CodeReview [tom, 2/9/2007] If this is a file we have a temporary file for, + // we should probably delete it here rather then waiting til the archive is closed. + + return true; +} + +//----------------------------------------------------------------------------- + +bool ZipArchive::isVerbose() +{ + return Con::getBoolVariable("$pref::Zip::Verbose"); +} + +void ZipArchive::setVerbose(bool verbose) +{ + Con::setBoolVariable("$pref::Zip::Verbose", verbose); +} + +} // end namespace Zip diff --git a/Engine/source/core/util/zip/zipArchive.h b/Engine/source/core/util/zip/zipArchive.h new file mode 100644 index 000000000..5644bf9de --- /dev/null +++ b/Engine/source/core/util/zip/zipArchive.h @@ -0,0 +1,590 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/util/zip/fileHeader.h" +#include "core/util/zip/centralDir.h" +#include "core/util/zip/compressor.h" + +#include "core/stream/fileStream.h" + +#include "core/util/tVector.h" +#include "core/util/tDictionary.h" +#include "core/util/timeClass.h" + +#ifndef _ZIPARCHIVE_H_ +#define _ZIPARCHIVE_H_ + +/// @addtogroup zip_group Zip Code +// @{ + +/// Password to use when opening encrypted zip files. Change this to whatever the password is for your zips. +#define DEFAULT_ZIP_PASSWORD "changeme" + +class ZipTestWrite; +class ZipTestRead; +class ZipTestMisc; + +namespace Zip +{ + +/// @addtogroup zip_group Zip Code +// @{ + +// Forward Refs +class ZipTempStream; + +// [tom, 10/18/2006] This will be split up into a separate interface for allowing +// the resource manager to handle any kind of archive relatively easily. + +// [tom, 2/9/2007] At least, it was designed so that it could be. It may not +// actually happen for a very long time, if ever ;-) + +//----------------------------------------------------------------------------- +/// @brief Class for accessing Zip files +/// +/// ZipArchive provides two interfaces for reading or writing zip files. +/// The first is a stream based interface that should be familiar to anyone +/// who's ever written code, and the other is an archiver interface that +/// should be familiar to anyone who's ever used a standard Zip application. +/// +/// The two interfaces are not mutually exclusive so you can use both if +/// you wish. For example, you may want to use the stream interface to add +/// a configuration file to a zip without having to create a temporary file +/// and then add a number of existing files using the addFile() method. +/// +/// Both interfaces have their advantages and disadvantages, which will +/// be discussed below. +/// +///

      Accessing a Zip file

      +/// +/// Before you can access any files in the zip, you first need to open the +/// archive. This is the same regardless of which interface you use, and there +/// are two ways to accomplish it. +/// +/// Opening from a file on the file system +/// +/// The simplest method of opening a zip file is to use openArchive(const char *, AccessMode) +/// to open a file that is on the disk. +/// +/// When opening a zip file on the file system, the filename is automatically set. +/// +/// Opening a file from a stream +/// +/// A more advanced way to open the zip file is from an arbitrary stream. The +/// only requirements are that the stream supports seeking and was opened with +/// the correct access mode. Use the openArchive(Stream *, AccessMode) method to +/// do this. +/// +/// Opening zip files from arbitrary streams is a very powerful feature and +/// opens many interesting doors. For example, combined with some small changes +/// to the resource manager and startup code, it was possible to implement a +/// VFS that allows the entire game to run from a single executable with no +/// external files. +/// +/// Note that the filename is not automatically set when you open the zip file +/// from a stream. The filename is used in error reporting and by the resource +/// manager, so you may wish to set it to something meaningful. +/// +/// Regardless of which method you use to open the file, the #AccessMode controls +/// what you can do with it. If you open the archive as #ReadWrite, you can both +/// write to and read from files in the zip. However, it is not possible to open +/// files in the zip as #ReadWrite. +/// +/// Closing the zip file +/// +/// When you are done with the zip file, call closeArchive() to free any resources +/// and rebuild the zip file if it was open for #Write. +/// +/// Example +/// +/// @code +/// Zip::ZipArchive za; +/// if(za.openArchive("filename.zip", ZipArchive::Read)) +/// { +/// // ... do stuff ... +/// za.closeArchive(); +/// } +/// @endcode +/// +///

      Archiver Interface

      +/// +/// The archiver style interface allows you to add, extract and delete files in +/// the zip in a way similar to that of an standard archiver application. +/// +/// While the archiver interface is simple to use, it is blocking and thus +/// difficult to use asynchronously. If you require zip file support and +/// responsive UI then you should consider using the stream interface instead. +/// +/// See the following method documentation for more information: +/// +///
        +///
      • addFile() +///
      • extractFile() +///
      • deleteFile() +///
      +/// +/// Example +/// +/// @code +/// Zip::ZipArchive za; +/// if(za.openArchive("filename.zip", ZipArchive::ReadWrite)) +/// { +/// // Extract a file +/// za.extractFile("test.txt", "test.txt"); +/// // Add a file +/// za.addFile("test.txt", "test.txt"); +/// // Delete a file +/// za.deleteFile("test.txt"); +/// +/// za.closeArchive(); +/// } +/// @endcode +/// +///

      Stream Interface

      +/// +/// The stream based interface allows you to access files within the zip +/// in a similar way to accessing the file system through the ResourceManager. +/// +/// There are a few small caveats to the stream interface: +///
        +///
      • When writing files, the whole file must be written sequentially. You +/// cannot seek in the stream. +///
      • It may or may not be possible to seek in streams opened for read. +/// Files that were not compressed in the zip file support seeking with +/// no penalty. In all cases where the file is compressed, if seeking is +/// supported by the decompression and/or decryption filter then it +/// carries with it an extreme performance penalty and should be avoided. +/// All currently available decompression filters (Deflate and BZip2) and +/// decryption filters (Zip 2.0 and AES) support seeking, but have to reset +/// their state and re-decompress/decrypt the entire file up to the point +/// you are seeking to. An extreme example would be that if you had a +/// 20MB file and were currently at the end of the file, seeking back 1 byte +/// of the file would cause the entire file to be decompressed again. This +/// would be a blocking operation that would lock Torque up for an appreciable +/// chunk of time. +///
      • Files can only be open as #Read or #Write, but not #ReadWrite +///
      • Only one file can be open for read at a time, but multiple files can +/// be open for write at a time. - [tom, 2/9/2007] Check this +///
      +/// +/// See the following method documentation for more information: +/// +///
        +///
      • openFile() +///
      • closeFile() +///
      +/// +/// CRC Checking +/// +/// Unlike the archiver interface, there is no automatic CRC checking when +/// reading from files using the stream interface. If you will only be +/// reading files sequentially, see the documentation for ZipStatFilter +/// for a useful trick to get easy CRC checking. +/// +/// Example +/// +/// @code +/// Zip::ZipArchive za; +/// if(za.openArchive("filename.zip", ZipArchive::Write)) +/// { +/// // Write to the file +/// Stream *stream; +/// if(stream = za.openFile("test.txt", ZipArchive::Write)) +/// { +/// stream->writeLine((U8 *)"Hello, Zipped World!"); +/// za.closeFile(stream); +/// } +/// +/// za.closeArchive(); +/// } +/// @endcode +/// +///

      Compressed Files

      +/// +/// The zip code included with stock Torque supports "stored" (uncompressed) files +/// and deflate compressed files. The code is easily extensible to support any +/// compression format that the Zip file format supports. +/// +/// In addition to the deflate and stored formats, BZip2 is supported but not +/// included with stock Torque. BZip2 support will be released as a resource in +/// the future. +/// +///

      Encrypted Files

      +/// +/// Preliminary support for Encrypted/Passworded files is included in TGB Pro only. +/// Currently, only Zip 2.0 encryption is supported by the stock code. AES support +/// exists and may be released as a resource in the future. +/// +/// To set the password used for zips, you need to modify the #DEFAULT_ZIP_PASSWORD +/// define in core/zip/zipArchive.h. This password will be used for all zips that +/// require a password. The default password is changeme. This may be used by +/// TGB Binary users to test encrypted zips with their game. Shipping with the +/// default password is not recommended for obvious reasons. +/// +/// The intended use of encrypted zips is for preventing casual copying of your +/// game's assets. Zip 2.0 encryption has known weaknesses that allow an attacker +/// to decrypt the contents of the zip. AES encryption is significantly more secure, +/// but as the password must be stored in the executable it will not stop a +/// determined attacker. +/// +/// A script accessible mechanism for setting the password does not currently exist. +/// To use encrypted mod zips, if the password was in script then the password +/// would be clearly visible to anyone that cared to poke around in your scripts. +/// +/// Encrypted zip support will be improved in a future version. For now, a more +/// secure method of storing the password is left as an exercise for the reader. +/// +///

      Accessing Zip files from script

      +/// +/// ZipArchive is a C++ class and thus cannot be used from script. However, +/// a wrapper is provided to allow script access to zips. See the documentation +/// on ZipObject for more information. +/// +///

      More Examples

      +/// +/// More in depth example code than that featured here can be found in the +/// unit tests for the zip code (in the core/zip/unitTests directory) +/// and the script code for the packaging utility. +/// +//----------------------------------------------------------------------------- +class ZipArchive : public StrongRefBase +{ + friend class ::ZipTestWrite; + friend class ::ZipTestRead; + friend class ::ZipTestMisc; + +public: + /// Access modes for zip files and files within the zip + enum AccessMode + { + Read = Torque::FS::File::Read, //!< Open a zip or file in a zip for reading + Write = Torque::FS::File::Write, //!< Open a zip or file in a zip for writing + ReadWrite = Torque::FS::File::ReadWrite //!< Open a zip file for reading and writing. Note: Not valid for files in zips. + }; + + struct ZipEntry + { + ZipEntry *mParent; + + String mName; + + bool mIsDirectory; + CentralDir mCD; + + Map mChildren; + + ZipEntry() + { + mName = ""; + mIsDirectory = false; + mParent = NULL; + } + }; +protected: + + Stream *mStream; + FileStream *mDiskStream; + AccessMode mMode; + + EndOfCentralDir mEOCD; + + // mRoot forms a tree of entries for fast queries given a file path + // mEntries allows easy iteration of the entire file list + ZipEntry *mRoot; + Vector mEntries; + + const char *mFilename; + + Vector mTempFiles; + + bool readCentralDirectory(); + + void insertEntry(ZipEntry *ze); + void removeEntry(ZipEntry *ze); + + Stream *createNewFile(const char *filename, Compressor *method); + Stream *createNewFile(const char *filename, const char *method) + { + return createNewFile(filename, Compressor::findCompressor(method)); + } + Stream *createNewFile(const char *filename, S32 method) + { + return createNewFile(filename, Compressor::findCompressor(method)); + } + + void updateFile(ZipTempStream *stream); + bool rebuildZip(); + bool copyFileToNewZip(CentralDir *cdir, Stream *newZipStream); + bool writeDirtyFileToNewZip(ZipTempStream *fileStream, Stream *zipStream); + +public: + ZipEntry* getRoot() { return mRoot; } + ZipEntry* findZipEntry(const char *filename); + static U32 localTimeToDOSTime(const Torque::Time::DateTime &dt); + static Torque::Time DOSTimeToTime(U16 time, U16 date); + static Torque::Time DOSTimeToTime(U32 datetime); + static U32 TimeToDOSTime(const Torque::Time& t); + static U32 currentTimeToDOSTime(); + void dumpCentralDirectory(ZipEntry* entry = NULL, String* indent = NULL); + +public: + ZipArchive(); + virtual ~ZipArchive(); + + /// @name Miscellaneous Methods + // @{ + + //----------------------------------------------------------------------------- + /// @brief Set the filename of the zip file. + /// + /// The zip filename is used by the resource manager and for error reporting. + /// + /// Note: The filename is set automatically when you open the file. + /// + /// @param filename Filename of the zip file + //----------------------------------------------------------------------------- + void setFilename(const char *filename); + + /// Set the disk stream pointer. The ZipArchive is then responsible for + /// deleting the stream when appropriate and the caller should not do the same. + /// This function should only be called after openArchive(Stream*) has been + /// successfully executed. + void setDiskStream(FileStream* stream) { mDiskStream = stream; } + + //----------------------------------------------------------------------------- + /// @brief Get the filename of the zip file. + /// + /// @returns Filename of the zip file, or NULL if none set + //----------------------------------------------------------------------------- + const char *getFilename() { return mFilename; } + + //----------------------------------------------------------------------------- + /// @brief Determine if the Zip code is in verbose mode + /// + /// Verbose mode causes the Zip code to provide diagnostic error messages + /// when things go wrong. It can be enabled or disabled through script by + /// setting the $pref::Zip::Verbose variable to true to enable it or false + /// to disable it. + /// + /// Verbose mode is mostly useful when tracking down issues with opening + /// a zip file without having to resort to using a debugger. + /// + /// @returns The value of $pref::Zip::Verbose + /// @see ZipArchive::setVerbose() + //----------------------------------------------------------------------------- + bool isVerbose(); + + //----------------------------------------------------------------------------- + /// @brief Turn verbose mode on or off. + /// + /// This sets the $pref::Zip::Verbose variable. + /// + /// See isVerbose() for a discussion on verbose mode. + /// + /// @param verbose True to enable verbose mode, false to disable + /// @see ZipArchive::isVerbose() + //----------------------------------------------------------------------------- + void setVerbose(bool verbose); + // @} + + /// @name Archive Access Methods + // @{ + + //----------------------------------------------------------------------------- + /// @brief Open a zip archive from a file + /// + /// The archive must be closed with closeArchive() when you are done with it. + /// + /// @param filename Filename of zip file to open + /// @param mode Access mode. May be Read, Write or ReadWrite + /// @return true for success, false for failure + /// @see ZipArchive::openArchive(Stream *, AccessMode), ZipArchive::closeArchive() + //----------------------------------------------------------------------------- + virtual bool openArchive(const char *filename, AccessMode mode = Read); + + //----------------------------------------------------------------------------- + /// @brief Open a zip archive from a stream + /// + /// The stream must support seeking and must support the specified access + /// mode. For example, if the stream is opened for Read you cannot specify + /// Write to openArchive(). However, if the stream is open for ReadWrite + /// then you can specify any one of Read, Write or ReadWrite as the mode + /// argument to openArchive(). + /// + /// The archive must be closed with closeArchive() when you are done with it. + /// + /// @param stream Pointer to stream to open the zip archive from + /// @param mode Access mode. May be Read, Write or ReadWrite + /// @return true for success, false for failure + /// @see ZipArchive::openArchive(const char *, AccessMode), ZipArchive::closeArchive() + //----------------------------------------------------------------------------- + + // CodeReview [tom, 2/9/2007] I just thought, if we open a stream directly + // for write then rebuilding the zip file probably won't work. This needs to + // be checked and either fixed or worked around. + + virtual bool openArchive(Stream *stream, AccessMode mode = Read); + + //----------------------------------------------------------------------------- + /// @brief Close the zip archive and free any resources + /// + /// @see ZipArchive::openArchive(Stream *, AccessMode), ZipArchive::openArchive(const char *, AccessMode) + //----------------------------------------------------------------------------- + virtual void closeArchive(); + // @} + + /// @name Stream Based File Access Methods + // @{ + + //----------------------------------------------------------------------------- + /// @brief Open a file within the zip file + /// + /// The access mode can only be Read or Write. It is not possible to open + /// a file within the zip as ReadWrite. + /// + /// The returned stream must be freed with closeFile(). Do not delete it + /// directly. + /// + /// In verbose mode, openFile() will display additional error information + /// in the console when it fails. + /// + /// @param filename Filename of the file in the zip + /// @param mode Access mode. May be Read or Write + /// @param ze Output zip entry. May be unspecified. + /// @return Pointer to stream or NULL for failure + /// @see ZipArchive::closeFile(), ZipArchive::isVerbose() + //----------------------------------------------------------------------------- + virtual Stream *openFile(const char *filename, AccessMode mode = Read); + virtual Stream *openFile(const char *filename, ZipEntry* ze, AccessMode = Read); + + //----------------------------------------------------------------------------- + /// @brief Close a file opened through openFile() + /// + /// @param stream Stream to close + /// @see ZipArchive::openFile(const char *, AccessMode) + //----------------------------------------------------------------------------- + virtual void closeFile(Stream *stream); + + //----------------------------------------------------------------------------- + /// @brief Open a file within the zip file for read + /// + /// This method exists mainly for the integration with the resource manager. + /// Unless there is good reason to use this method, it is better to use the + /// openFile() method instead. + /// + /// @param fileCD Pointer to central directory of the file to open + /// @return Pointer to stream or NULL for failure + /// @see ZipArchive::openFile(const char *, AccessMode), ZipArchive::closeFile() + //----------------------------------------------------------------------------- + Stream *openFileForRead(const CentralDir *fileCD); + // @} + + /// @name Archiver Style File Access Methods + // @{ + + //----------------------------------------------------------------------------- + /// @brief Add a file to the zip + /// + /// If replace is false and the file already exists in the zip, this function + /// will fail and return false. If replace is true, the existing file will be + /// overwritten. + /// + /// @param filename Filename on the local file system to add + /// @param pathInZip The path and filename in the zip file to give this file + /// @param replace true to replace existing files, false otherwise + /// @return true for success, false for failure + /// @see ZipArchive::extractFile(), ZipArchive::deleteFile(), ZipArchive::isVerbose() + //----------------------------------------------------------------------------- + bool addFile(const char *filename, const char *pathInZip, bool replace = true); + + //----------------------------------------------------------------------------- + /// @brief Extract a file from the zip + /// + /// The file will be created through the resource manager and so must be + /// in a location that is writable. + /// + /// The file will be CRC checked during extraction and extractFile() will + /// return false if the CRC check failed. The CRC check is just an advisory, + /// the output file will still exist if the CRC check failed. It is up to + /// the caller to decide what to do in the event of a CRC failure. + /// + /// You can optionally pass a pointer to a bool to determine if a CRC check + /// failed. If extractFile() returns false and crcFail is false then the failure + /// was not CRC related. If crcFail is true and extractFile() returns false, + /// then the CRC check failed but the file otherwise extracted OK. You can + /// take your chances as to whether the data is valid or not, if you wish. + /// + /// In verbose mode, extractFile() will display an error in the console when + /// a file fails the CRC check. + /// + /// @param pathInZip The path and filename in the zip file to extract + /// @param filename Filename on the local file system to extract to + /// @param crcFail Pointer to a boolean that receives the result of the CRC check + /// @return true for success, false for failure + /// @see ZipArchive::addFile(), ZipArchive::deleteFile(), ZipArchive::isVerbose() + //----------------------------------------------------------------------------- + bool extractFile(const char *pathInZip, const char *filename, bool *crcFail = NULL); + + //----------------------------------------------------------------------------- + /// @brief Delete a file from the zip + /// + /// Flags a file as deleted so it is removed when the zip file is rebuilt. + /// + /// @param filename Filename in the zip to delete + /// @return true for success, false for failure + /// @see ZipArchive::addFile(), ZipArchive::extractFile(), ZipArchive::isVerbose() + //----------------------------------------------------------------------------- + bool deleteFile(const char *filename); + // @} + + /// @name Enumeration Methods + // @{ + + //----------------------------------------------------------------------------- + /// @brief Get number of entries in the central directory + /// + /// @see ZipArchive::findFileInfo(const char *) + //----------------------------------------------------------------------------- + U32 numEntries() const { return mEntries.size(); } + + //----------------------------------------------------------------------------- + /// Get a central directory entry + //----------------------------------------------------------------------------- + const CentralDir & operator[](const U32 idx) const { return mEntries[idx]->mCD; } + + //----------------------------------------------------------------------------- + /// @brief Find a file in the zip + /// + /// @param filename Path and filename to find + /// @return Pointer to the central directory entry + //----------------------------------------------------------------------------- + CentralDir *findFileInfo(const char *filename); + // @} +}; + +// @} + +} // end namespace Zip + +// @} + +#endif // _ZIPARCHIVE_H_ diff --git a/Engine/source/core/util/zip/zipCryptStream.cpp b/Engine/source/core/util/zip/zipCryptStream.cpp new file mode 100644 index 000000000..ba58349b6 --- /dev/null +++ b/Engine/source/core/util/zip/zipCryptStream.cpp @@ -0,0 +1,204 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/util/zip/zipCryptStream.h" +#include "core/util/zip/crctab.h" + +#include "console/console.h" + +//----------------------------------------------------------------------------- +// Constructor/Destructor +//----------------------------------------------------------------------------- + +ZipCryptRStream::ZipCryptRStream() : mStream(NULL), mFileEndPos(0), mPassword(NULL) +{ +} + +ZipCryptRStream::~ZipCryptRStream() +{ +} + +//----------------------------------------------------------------------------- +// Private Methods +//----------------------------------------------------------------------------- + +U32 ZipCryptRStream::fillBuffer(const U32 in_attemptSize, void *pBuffer) +{ + AssertFatal(mStream != NULL, "No stream to fill from?"); + AssertFatal(mStream->getStatus() != Stream::Closed, + "Fill from a closed stream?"); + + U32 currPos = mStream->getPosition(); + + U32 actualReadSize; + if (in_attemptSize + currPos > mFileEndPos) { + actualReadSize = mFileEndPos - currPos; + } else { + actualReadSize = in_attemptSize; + } + + if (mStream->read(actualReadSize, pBuffer) == true) { + return actualReadSize; + } else { + AssertWarn(false, "Read failed while trying to fill buffer"); + return 0; + } +} + +//----------------------------------------------------------------------------- +// Public Methods +//----------------------------------------------------------------------------- + +void ZipCryptRStream::setPassword(const char *password) +{ + mKeys[0] = 305419896; + mKeys[1] = 591751049; + mKeys[2] = 878082192; + + mPassword = password; + const char *pPtr = password; + while(*pPtr) + { + updateKeys(*pPtr); + pPtr++; + } +} + +bool ZipCryptRStream::attachStream(Stream* io_pSlaveStream) +{ + mStream = io_pSlaveStream; + mStreamStartPos = mStream->getPosition(); + + // [tom, 12/20/2005] Encrypted zip files have an extra 12 bytes + // of entropy before the file data. + + U8 buffer[12]; + if(mStream->read(sizeof(buffer), &buffer)) + { + // Initialize keys + for(S32 i = 0;i < sizeof(buffer);i++) + { + updateKeys(buffer[i] ^= decryptByte()); + } + + // if(buffer[11] !) + mFileStartPos = mStream->getPosition(); + + setStatus(Ok); + return true; + } + return false; +} + +void ZipCryptRStream::detachStream() +{ + mStream = NULL; + + // Clear keys, just in case + dMemset(&mKeys, 0, sizeof(mKeys)); + + setStatus(Closed); +} + +U32 ZipCryptRStream::getPosition() const +{ + return mStream->getPosition(); +} + +bool ZipCryptRStream::setPosition(const U32 in_newPosition) +{ + if(in_newPosition > mFileEndPos) + return false; + + U32 curPos = getPosition(); + U32 readSize = in_newPosition - mFileStartPos; + bool ret = true; + + if(in_newPosition < curPos) + { + // Reposition to start of stream + Stream *stream = getStream(); + U32 startPos = mStreamStartPos; + const char *password = mPassword; + detachStream(); + setPassword(password); + stream->setPosition(startPos); + ret = attachStream(stream); + + if(in_newPosition == mFileStartPos) + return ret; + } + + // Read until we reach the new position + U8 *buffer = new U8 [1024]; + while(readSize >= 1024) + { + readSize -= 1024; + ret = _read(1024, buffer); + if(! ret) + break; + } + + if(readSize > 0 && ret) + { + ret = _read(readSize, buffer); + } + delete [] buffer; + + return ret; +} + +//----------------------------------------------------------------------------- +// Protected Methods +//----------------------------------------------------------------------------- + +void ZipCryptRStream::updateKeys(const U8 c) +{ + mKeys[0] = ZC_CRC32(mKeys[0], c); + mKeys[1] += mKeys[0] & 0x000000ff; + mKeys[1] = mKeys[1] * 134775813 + 1; + U32 k = mKeys[1] >> 24; + mKeys[2] = ZC_CRC32(mKeys[2], k); +} + +U8 ZipCryptRStream::decryptByte() +{ + U16 temp; + temp = (mKeys[2] & 0xffff) | 2; + return (temp * (temp ^ 1)) >> 8; +} + +bool ZipCryptRStream::_read(const U32 in_numBytes, void* out_pBuffer) +{ + U32 numRead = fillBuffer(in_numBytes, out_pBuffer); + if(numRead > 0) + { + // Decrypt + U8 *pBytes = (U8 *)out_pBuffer; + for(S32 i = 0;i < numRead;i++) + { + updateKeys(pBytes[i] ^= decryptByte()); + } + return true; + } + return false; +} diff --git a/Engine/source/core/util/zip/zipCryptStream.h b/Engine/source/core/util/zip/zipCryptStream.h new file mode 100644 index 000000000..dcdae35fa --- /dev/null +++ b/Engine/source/core/util/zip/zipCryptStream.h @@ -0,0 +1,68 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _ZIPCRYPTSTREAM_H_ +#define _ZIPCRYPTSTREAM_H_ + +#ifndef _FILTERSTREAM_H_ +#include "core/filterStream.h" +#endif + +class ZipCryptRStream : public FilterStream +{ + typedef FilterStream Parent; + + Stream *mStream; + + S32 mStreamStartPos; + S32 mFileStartPos; + S32 mFileEndPos; + + U32 mKeys[3]; // mKeys and it's usage is very unclear and has a ton of magic numbers -patw + + const char *mPassword; + + U32 fillBuffer(const U32 in_attemptSize, void *pBuffer); + +public: + ZipCryptRStream(); + virtual ~ZipCryptRStream(); + + void setPassword(const char *password); + inline void setFileEndPos(S32 pos) { mFileEndPos = pos; } + + // Overrides of FilterStream + bool attachStream(Stream* io_pSlaveStream); + void detachStream(); + Stream *getStream() { return mStream; } + + U32 getPosition() const; + bool setPosition(const U32 in_newPosition); + +protected: + bool _read(const U32 in_numBytes, void* out_pBuffer); + + void updateKeys(const U8 c); + U8 decryptByte(); +}; + +#endif // _ZIPCRYPTSTREAM_H_ diff --git a/Engine/source/core/util/zip/zipObject.cpp b/Engine/source/core/util/zip/zipObject.cpp new file mode 100644 index 000000000..60c05c183 --- /dev/null +++ b/Engine/source/core/util/zip/zipObject.cpp @@ -0,0 +1,423 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/util/zip/zipObject.h" +#include "core/util/safeDelete.h" +#include "console/engineAPI.h" + +//----------------------------------------------------------------------------- +// Constructor/Destructor +//----------------------------------------------------------------------------- + +ZipObject::ZipObject() +{ + mZipArchive = NULL; +} + +ZipObject::~ZipObject() +{ + closeArchive(); +} + +IMPLEMENT_CONOBJECT(ZipObject); + +ConsoleDocClass( ZipObject, + "@brief Provides access to a zip file.\n\n" + + "A ZipObject add, delete and extract files that are within a zip archive. You may also " + "read and write directly to the files within the archive by obtaining a StreamObject " + "for the file." + + "@tsexample\n" + "// Open a zip archive, creating it if it doesn't exist\n" + "%archive = new ZipObject();\n" + "%archive.openArchive(\"testArchive.zip\", Write);\n\n" + "// Add a file to the archive with the given name\n" + "%archive.addFile(\"./water.png\", \"water.png\");\n\n" + "// Close the archive to save the changes\n" + "%archive.closeArchive();\n" + "@endtsexample\n\n" + + "@note Behind the scenes all of the work is being done with the ZipArchive and StreamObject classes.\n" + + "@see StreamObject when using methods such as openFileForRead() and openFileForWrite()\n\n" + + "@ingroup FileSystem\n" +); + + +//----------------------------------------------------------------------------- +// Protected Methods +//----------------------------------------------------------------------------- + +StreamObject *ZipObject::createStreamObject(Stream *stream) +{ + for(S32 i = 0;i < mStreamPool.size();++i) + { + StreamObject *so = mStreamPool[i]; + + if(so == NULL) + { + // Reuse any free locations in the pool + so = new StreamObject(stream); + so->registerObject(); + mStreamPool[i] = so; + return so; + } + + if(so->getStream() == NULL) + { + // Existing unused stream, update it and return it + so->setStream(stream); + return so; + } + } + + // No free object found, create a new one + StreamObject *so = new StreamObject(stream); + so->registerObject(); + mStreamPool.push_back(so); + + return so; +} + +//----------------------------------------------------------------------------- +// Public Methods +//----------------------------------------------------------------------------- + +bool ZipObject::openArchive(const char *filename, Zip::ZipArchive::AccessMode mode /* = Read */) +{ + closeArchive(); + + mZipArchive = new Zip::ZipArchive; + if(mZipArchive->openArchive(filename, mode)) + return true; + + SAFE_DELETE(mZipArchive); + return false; +} + +void ZipObject::closeArchive() +{ + if(mZipArchive == NULL) + return; + + for(S32 i = 0;i < mStreamPool.size();++i) + { + StreamObject *so = mStreamPool[i]; + if(so && so->getStream() != NULL) + closeFile(so); + + SAFE_DELETE_OBJECT(mStreamPool[i]); + } + mStreamPool.clear(); + + mZipArchive->closeArchive(); + SAFE_DELETE(mZipArchive); +} + +//----------------------------------------------------------------------------- + +StreamObject * ZipObject::openFileForRead(const char *filename) +{ + if(mZipArchive == NULL) + return NULL; + + Stream * stream = mZipArchive->openFile(filename, Zip::ZipArchive::Read); + if(stream != NULL) + return createStreamObject(stream); + + return NULL; +} + +StreamObject * ZipObject::openFileForWrite(const char *filename) +{ + if(mZipArchive == NULL) + return NULL; + + Stream * stream = mZipArchive->openFile(filename, Zip::ZipArchive::Write); + if(stream != NULL) + return createStreamObject(stream); + + return NULL; +} + +void ZipObject::closeFile(StreamObject *stream) +{ + if(mZipArchive == NULL) + return; + +#ifdef TORQUE_DEBUG + bool found = false; + for(S32 i = 0;i < mStreamPool.size();++i) + { + StreamObject *so = mStreamPool[i]; + if(so && so == stream) + { + found = true; + break; + } + } + + AssertFatal(found, "ZipObject::closeFile() - Attempting to close stream not opened by this ZipObject"); +#endif + + mZipArchive->closeFile(stream->getStream()); + stream->setStream(NULL); +} + +//----------------------------------------------------------------------------- + +bool ZipObject::addFile(const char *filename, const char *pathInZip, bool replace /* = true */) +{ + return mZipArchive->addFile(filename, pathInZip, replace); +} + +bool ZipObject::extractFile(const char *pathInZip, const char *filename) +{ + return mZipArchive->extractFile(pathInZip, filename); +} + +bool ZipObject::deleteFile(const char *filename) +{ + return mZipArchive->deleteFile(filename); +} + +//----------------------------------------------------------------------------- + +S32 ZipObject::getFileEntryCount() +{ + return mZipArchive ? mZipArchive->numEntries() : 0; +} + +String ZipObject::getFileEntry(S32 idx) +{ + if(mZipArchive == NULL) + return ""; + + const Zip::CentralDir &dir = (*mZipArchive)[idx]; + char buffer[1024]; + int chars = dSprintf(buffer, sizeof(buffer), "%s\t%d\t%d\t%d\t%08x", + dir.mFilename.c_str(), dir.mUncompressedSize, dir.mCompressedSize, + dir.mCompressMethod, dir.mCRC32); + if (chars < sizeof(buffer)) + buffer[chars] = 0; + + return String(buffer); +} + +//----------------------------------------------------------------------------- +// Console Methods +//----------------------------------------------------------------------------- + +static const struct +{ + const char *strMode; + Zip::ZipArchive::AccessMode mode; +} gModeMap[]= +{ + { "read", Zip::ZipArchive::Read }, + { "write", Zip::ZipArchive::Write }, + { "readwrite", Zip::ZipArchive::ReadWrite }, + { NULL, (Zip::ZipArchive::AccessMode)0 } +}; + +//----------------------------------------------------------------------------- + +DefineEngineMethod(ZipObject, openArchive, bool, ( const char* filename, const char* accessMode ), ( "read" ), + "@brief Open a zip archive for manipulation.\n\n" + + "Once a zip archive is opened use the various ZipObject methods for " + "working with the files within the archive. Be sure to close the archive when " + "you are done with it.\n\n" + + "@param filename The path and file name of the zip archive to open.\n" + "@param accessMode One of read, write or readwrite\n" + + "@return True is the archive was successfully opened.\n" + + "@note If you wish to make any changes to the archive, be sure to open it " + "with a write or readwrite access mode.\n" + + "@see closeArchive()") +{ + Zip::ZipArchive::AccessMode mode = Zip::ZipArchive::Read; + + for(S32 i = 0;gModeMap[i].strMode;++i) + { + if(dStricmp(gModeMap[i].strMode, accessMode) == 0) + { + mode = gModeMap[i].mode; + break; + } + } + + char buf[512]; + Con::expandScriptFilename(buf, sizeof(buf), filename); + + return object->openArchive(buf, mode); +} + +DefineEngineMethod(ZipObject, closeArchive, void, (),, + "@brief Close an already opened zip archive.\n\n" + "@see openArchive()") +{ + object->closeArchive(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod(ZipObject, openFileForRead, SimObject*, ( const char* filename ),, + "@brief Open a file within the zip archive for reading.\n\n" + + "Be sure to close the file when you are done with it.\n" + + "@param filename The path and name of the file to open within the zip archive.\n" + + "@return A standard StreamObject is returned for working with the file.\n" + "@note You must first open the zip archive before working with files within it.\n" + + "@see closeFile()\n" + "@see openArchive()") +{ + StreamObject *stream = object->openFileForRead(filename); + return stream; +} + +DefineEngineMethod(ZipObject, openFileForWrite, SimObject*, ( const char* filename ),, + "@brief Open a file within the zip archive for writing to.\n\n" + + "Be sure to close the file when you are done with it.\n" + + "@param filename The path and name of the file to open within the zip archive.\n" + + "@return A standard StreamObject is returned for working with the file.\n" + "@note You must first open the zip archive before working with files within it.\n" + + "@see closeFile()\n" + "@see openArchive()") +{ + StreamObject *stream = object->openFileForWrite(filename); + return stream; +} + +DefineEngineMethod(ZipObject, closeFile, void, ( SimObject* stream ),, + "@brief Close a previously opened file within the zip archive.\n\n" + "@param stream The StreamObject of a previously opened file within the zip archive.\n" + "@see openFileForRead()\n" + "@see openFileForWrite()") +{ + StreamObject *so = dynamic_cast(stream); + if(so == NULL) + { + Con::errorf("ZipObject::closeFile - Invalid stream specified"); + return; + } + + object->closeFile(so); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod(ZipObject, addFile, bool, ( const char* filename, const char* pathInZip, bool replace ), ( true ), + "@brief Add a file to the zip archive\n\n" + + "@param filename The path and name of the file to add to the zip archive.\n" + "@param pathInZip The path and name to be given to the file within the zip archive.\n" + "@param replace If a file already exists within the zip archive at the same location as this " + "new file, this parameter indicates if it should be replaced. By default, it will be replaced.\n" + "@return True if the file was successfully added to the zip archive.") +{ + // Left this line commented out as it was useful when i had a problem + // with the zip's i was creating. [2/21/2007 justind] + // [tom, 2/21/2007] To is now a warnf() for better visual separation in the console + // Con::errorf("zipAdd: %s", argv[2]); + //Con::warnf(" To: %s", argv[3]); + + return object->addFile(filename, pathInZip, replace); +} + +DefineEngineMethod(ZipObject, extractFile, bool, ( const char* pathInZip, const char* filename ),, + "@brief Extact a file from the zip archive and save it to the requested location.\n\n" + "@param pathInZip The path and name of the file to be extracted within the zip archive.\n" + "@param filename The path and name to give the extracted file.\n\n" + "@return True if the file was successfully extracted.") +{ + return object->extractFile(pathInZip, filename); +} + +DefineEngineMethod(ZipObject, deleteFile, bool, ( const char* pathInZip ),, + "@brief Deleted the given file from the zip archive\n\n" + "@param pathInZip The path and name of the file to be deleted from the zip archive.\n" + "@return True of the file was successfully deleted.\n" + + "@note Files that have been deleted from the archive will still show up with a " + "getFileEntryCount() until you close the archive. If you need to have the file " + "count up to date with only valid files within the archive, you could close and then " + "open the archive again.\n" + + "@see getFileEntryCount()\n" + "@see closeArchive()\n" + "@see openArchive()") +{ + return object->deleteFile(pathInZip); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod(ZipObject, getFileEntryCount, S32, (),, + "@brief Get the number of files within the zip archive.\n\n" + + "Use getFileEntry() to retrive information on each file within the archive.\n\n" + + "@return The number of files within the zip archive.\n" + + "@note The returned count will include any files that have been deleted from " + "the archive using deleteFile(). To clear out all deleted files, you could " + "close and then open the archive again.\n" + + "@see getFileEntry()\n" + "@see closeArchive()\n" + "@see openArchive()") +{ + return object->getFileEntryCount(); +} + +DefineEngineMethod(ZipObject, getFileEntry, String, ( S32 index ),, + "@brief Get information on the requested file within the zip archive.\n\n" + + "This methods provides five different pieces of information for the requested file:\n" + "
      • filename - The path and name of the file within the zip archive
      • " + "
      • uncompressed size
      • " + "
      • compressed size
      • " + "
      • compression method
      • " + "
      • CRC32
      \n" + + "Use getFileEntryCount() to obtain the total number of files within the archive.\n" + + "@param index The index of the file within the zip archive. Use getFileEntryCount() to determine the number of files.\n" + "@return A tab delimited list of information on the requested file, or an empty string if the file could not be found.\n" + + "@see getFileEntryCount()") +{ + return object->getFileEntry(index); +} diff --git a/Engine/source/core/util/zip/zipObject.h b/Engine/source/core/util/zip/zipObject.h new file mode 100644 index 000000000..cc99b14dc --- /dev/null +++ b/Engine/source/core/util/zip/zipObject.h @@ -0,0 +1,85 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/simBase.h" +#include "core/util/zip/zipArchive.h" +#include "core/util/tVector.h" +#include "core/stream/streamObject.h" +#include "core/util/str.h" + +#ifndef _ZIPOBJECT_H_ +#define _ZIPOBJECT_H_ + +/// @addtogroup zip_group +// @{ + +//----------------------------------------------------------------------------- +/// @brief Script wrapper for Zip::ZipArchive. +//----------------------------------------------------------------------------- +class ZipObject : public SimObject +{ + typedef SimObject Parent; + +protected: + Zip::ZipArchive *mZipArchive; + + // StreamObjects are pooled and reused to avoid creating tons of SimObjects + Vector mStreamPool; + + StreamObject *createStreamObject(Stream *stream); + +public: + ZipObject(); + virtual ~ZipObject(); + DECLARE_CONOBJECT(ZipObject); + + // Methods for accessing the archive + /// @see Zip::ZipArchive::openArchive() + bool openArchive(const char *filename, Zip::ZipArchive::AccessMode mode = Zip::ZipArchive::Read); + /// @see Zip::ZipArchive::closeArchive() + void closeArchive(); + + // Stream based file system style interface + /// @see Zip::ZipArchive::openFile() + StreamObject *openFileForRead(const char *filename); + /// @see Zip::ZipArchive::openFile() + StreamObject *openFileForWrite(const char *filename); + /// @see Zip::ZipArchive::closeFile() + void closeFile(StreamObject *stream); + + // Alternative archiver style interface + /// @see Zip::ZipArchive::addFile() + bool addFile(const char *filename, const char *pathInZip, bool replace = true); + /// @see Zip::ZipArchive::extractFile() + bool extractFile(const char *pathInZip, const char *filename); + /// @see Zip::ZipArchive::deleteFile() + bool deleteFile(const char *filename); + + + // Methods for access the list of files + S32 getFileEntryCount(); + String getFileEntry(S32 idx); +}; + +// @} + +#endif // _ZIPOBJECT_H_ diff --git a/Engine/source/core/util/zip/zipStatFilter.h b/Engine/source/core/util/zip/zipStatFilter.h new file mode 100644 index 000000000..17d377c61 --- /dev/null +++ b/Engine/source/core/util/zip/zipStatFilter.h @@ -0,0 +1,173 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/filterStream.h" +#include "core/crc.h" +#include "core/util/zip/centralDir.h" + +// [tom, 1/29/2007] ZipStatFilter allows us to track CRC and uncompressed size +// on the fly. This is necessary when dealing with compressed files as the +// CRC must be of the uncompressed data. +// +// The alternative would be to compress the files when updating the zip file, +// but this could potentially cause ZipArchive::closeArchive() to take a long +// time to complete. With compression done on the fly the time consuming code +// is pushed out to when writing to files, which is significantly easier to +// do asynchronously. + +#ifndef _ZIPSTATFILTER_H_ +#define _ZIPSTATFILTER_H_ + +//----------------------------------------------------------------------------- +/// \brief Namespace for the zip code. +/// +/// @see Zip::ZipArchive, \ref zip_group "Zip Code Module" +//----------------------------------------------------------------------------- +namespace Zip +{ + +/// @addtogroup zipint_group Zip Code Internals +/// +/// The zip code internals are mostly undocumented, but should be fairly +/// obvious to anyone who is familiar with the zip file format. + +/// @ingroup zip_group +// @{ + +//----------------------------------------------------------------------------- +/// \brief Helper class for tracking CRC and uncompressed size +/// +/// ZipStatFilter allows us to track CRC and uncompressed size +/// on the fly. This is necessary when dealing with compressed files as the +/// CRC must be of the uncompressed data. +/// +/// ZipStatFilter is mostly intended for internal use by the zip code. +/// However, it can be useful when reading zips sequentially using the +/// stream interface to provide CRC checking. +/// +/// Example +/// +/// @code +/// // It's assumed that you would use proper error checking and that +/// // zip is a valid pointer to a ZipArchive and otherStream is a pointer +/// // to a valid stream. +/// Zip::ZipArchive *zip; +/// Stream *otherStream; +/// +/// // We need the real central directory to compare the CRC32 +/// Zip::CentralDir *realCD = zip->findFileInfo("file.txt"); +/// Stream *stream = zip->openFile("file.txt", ZipArchive::Read); +/// +/// Zip::CentralDir fakeCD; +/// Zip::ZipStatFilter zsf(&fakeCD); +/// +/// zsf.attachStream(stream); +/// +/// // ... read entire file sequentially using zsf instead of stream +/// otherStream->copyFrom(&zsf); +/// +/// zsf.detachStream(); +/// +/// // fakeCD.mCRC32 now contains the CRC32 of the stream +/// if(fakeCD.mCRC32 != realCD->mCRC32) +/// { +/// // ... handle CRC failure ... +/// } +/// +/// zip->closeFile(stream); +/// @endcode +/// +/// A more complete example of this may be found in the code for the +/// ZipArchive::extractFile() method in zipArchive.cc +/// +//----------------------------------------------------------------------------- +class ZipStatFilter : public FilterStream +{ + typedef FilterStream Parent; + +protected: + Stream *mStream; + + CentralDir *mCD; + + virtual bool _write(const U32 numBytes, const void *buffer) + { + if(! mStream->write(numBytes, buffer)) + return false; + + mCD->mUncompressedSize += numBytes; + mCD->mCRC32 = CRC::calculateCRC(buffer, numBytes, mCD->mCRC32); + + return true; + } + + virtual bool _read(const U32 numBytes, void *buffer) + { + if(! mStream->read(numBytes, buffer)) + return false; + + mCD->mUncompressedSize += numBytes; + mCD->mCRC32 = CRC::calculateCRC(buffer, numBytes, mCD->mCRC32); + + return true; + } + +public: + ZipStatFilter() : mStream(NULL), mCD(NULL) {} + ZipStatFilter(CentralDir *cd) : mStream(NULL), mCD(cd) {} + virtual ~ZipStatFilter() + { + detachStream(); + } + + virtual bool attachStream(Stream *stream) + { + if(mCD == NULL) + return false; + + mStream = stream; + mCD->mUncompressedSize = 0; + mCD->mCRC32 = CRC::INITIAL_CRC_VALUE; + return true; + } + + virtual void detachStream() + { + if(mStream == NULL) + return; + + // Post condition the CRC + mCD->mCRC32 ^= CRC::CRC_POSTCOND_VALUE; + mStream = NULL; + } + + virtual Stream *getStream() { return mStream; } + + void setCentralDir(CentralDir *cd) { mCD = cd; } + CentralDir *getCentralDir() { return mCD; } +}; + +// @} + +} // end namespace Zip + +#endif // _ZIPSTATFILTER_H_ diff --git a/Engine/source/core/util/zip/zipSubStream.cpp b/Engine/source/core/util/zip/zipSubStream.cpp new file mode 100644 index 000000000..4a38a955a --- /dev/null +++ b/Engine/source/core/util/zip/zipSubStream.cpp @@ -0,0 +1,479 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "zlib.h" +#include "core/util/zip/zipSubStream.h" + + +const U32 ZipSubRStream::csm_streamCaps = U32(Stream::StreamRead) | U32(Stream::StreamPosition); +const U32 ZipSubRStream::csm_inputBufferSize = 4096; + +const U32 ZipSubWStream::csm_streamCaps = U32(Stream::StreamWrite); +const U32 ZipSubWStream::csm_bufferSize = (2048 * 1024); + +//-------------------------------------------------------------------------- +//-------------------------------------- +// +ZipSubRStream::ZipSubRStream() + : m_pStream(NULL), + m_uncompressedSize(0), + m_currentPosition(0), + m_EOS(false), + m_pZipStream(NULL), + m_pInputBuffer(NULL), + m_originalSlavePosition(0), + m_lastBytesRead(0) +{ + // +} + +//-------------------------------------- +ZipSubRStream::~ZipSubRStream() +{ + detachStream(); +} + +//-------------------------------------- +bool ZipSubRStream::attachStream(Stream* io_pSlaveStream) +{ + AssertFatal(io_pSlaveStream != NULL, "NULL Slave stream?"); + AssertFatal(m_pStream == NULL, "Already attached!"); + + m_pStream = io_pSlaveStream; + m_originalSlavePosition = io_pSlaveStream->getPosition(); + m_uncompressedSize = 0; + m_currentPosition = 0; + m_EOS = false; + + // Initialize zipStream state... + m_pZipStream = new z_stream_s; + m_pInputBuffer = new U8[csm_inputBufferSize]; + + m_pZipStream->zalloc = Z_NULL; + m_pZipStream->zfree = Z_NULL; + m_pZipStream->opaque = Z_NULL; + + U32 buffSize = fillBuffer(csm_inputBufferSize); + + m_pZipStream->next_in = m_pInputBuffer; + m_pZipStream->avail_in = buffSize; + m_pZipStream->total_in = 0; + inflateInit2(m_pZipStream, -MAX_WBITS); + + setStatus(Ok); + return true; +} + +//-------------------------------------- +void ZipSubRStream::detachStream() +{ + if (m_pZipStream != NULL) + { + // close out zip stream... + inflateEnd(m_pZipStream); + + delete [] m_pInputBuffer; + m_pInputBuffer = NULL; + delete m_pZipStream; + m_pZipStream = NULL; + } + + m_pStream = NULL; + m_originalSlavePosition = 0; + m_uncompressedSize = 0; + m_currentPosition = 0; + m_EOS = false; + setStatus(Closed); +} + +//-------------------------------------- +Stream* ZipSubRStream::getStream() +{ + return m_pStream; +} + +//-------------------------------------- +void ZipSubRStream::setUncompressedSize(const U32 in_uncSize) +{ + AssertFatal(m_pStream != NULL, "error, no stream to set unc size for"); + + m_uncompressedSize = in_uncSize; +} + +//-------------------------------------- +bool ZipSubRStream::_read(const U32 in_numBytes, void *out_pBuffer) +{ + m_lastBytesRead = 0; + if (in_numBytes == 0) + return true; + + AssertFatal(out_pBuffer != NULL, "NULL output buffer"); + if (getStatus() == Closed) { + AssertFatal(false, "Attempted read from closed stream"); + return false; + } + + + if (Ok != getStatus()) + return false; + + if (m_EOS) + { + setStatus(EOS); + return true; + }; + + // Ok, we need to call inflate() until the output buffer is full. + // first, set up the output portion of the z_stream + // + m_pZipStream->next_out = (Bytef*)out_pBuffer; + m_pZipStream->avail_out = in_numBytes; + m_pZipStream->total_out = 0; + + while (m_pZipStream->avail_out != 0) + { + S32 retVal = Z_OK; + + if(m_pZipStream->avail_in == 0) + { + // check if there is more output pending + inflate(m_pZipStream, Z_SYNC_FLUSH); + + if(m_pZipStream->total_out != in_numBytes) + { + // Need to provide more input bytes for the stream to read... + U32 buffSize = fillBuffer(csm_inputBufferSize); + //AssertFatal(buffSize != 0, "Must find a more graceful way to handle this"); + + m_pZipStream->next_in = m_pInputBuffer; + m_pZipStream->avail_in = buffSize; + m_pZipStream->total_in = 0; + } + } + + // need to get more? + if(m_pZipStream->total_out != in_numBytes) + retVal = inflate(m_pZipStream, Z_SYNC_FLUSH); + + AssertFatal(retVal != Z_BUF_ERROR, "Should never run into a buffer error"); + AssertFatal(retVal == Z_OK || retVal == Z_STREAM_END, "error in the stream"); + + m_lastBytesRead = m_pZipStream->total_out; + + if (retVal == Z_STREAM_END) + { + if (m_pZipStream->avail_out != 0) + m_EOS = true; + + setStatus(EOS); + m_currentPosition += m_pZipStream->total_out; + return true; + } + } + AssertFatal(m_pZipStream->total_out == in_numBytes, + "Error, didn't finish the decompression!"); + + // If we're here, everything went peachy... + setStatus(Ok); + m_currentPosition += m_pZipStream->total_out; + + return true; +} + +//-------------------------------------- +bool ZipSubRStream::hasCapability(const Capability in_cap) const +{ + return (csm_streamCaps & U32(in_cap)) != 0; +} + +//-------------------------------------- +U32 ZipSubRStream::getPosition() const +{ + AssertFatal(m_pStream != NULL, "Error, not attached"); + + return m_currentPosition; +} + +//-------------------------------------- +bool ZipSubRStream::setPosition(const U32 in_newPosition) +{ + AssertFatal(m_pStream != NULL, "Error, not attached"); + + if (in_newPosition == 0) + { + Stream* pStream = getStream(); + U32 resetPosition = m_originalSlavePosition; + U32 uncompressedSize = m_uncompressedSize; + detachStream(); + pStream->setPosition(resetPosition); + attachStream(pStream); + setUncompressedSize(uncompressedSize); + return true; + } + else + { + if (in_newPosition > m_uncompressedSize) + return false; + + U32 newPosition = in_newPosition; + if (newPosition < m_currentPosition) + { + Stream* pStream = getStream(); + U32 resetPosition = m_originalSlavePosition; + U32 uncompressedSize = m_uncompressedSize; + detachStream(); + pStream->setPosition(resetPosition); + attachStream(pStream); + setUncompressedSize(uncompressedSize); + } + else + { + newPosition -= m_currentPosition; + } + + bool bRet = true; + char *buffer = new char[2048]; + while (newPosition >= 2048) + { + newPosition -= 2048; + if (!_read(2048,buffer)) + { + bRet = false; + break; + } + }; + if (bRet && newPosition > 0) + { + if (!_read(newPosition,buffer)) + { + bRet = false; + }; + }; + + delete [] buffer; + + return bRet; + + } +} + +//-------------------------------------- +U32 ZipSubRStream::getStreamSize() +{ + AssertFatal(m_pStream != NULL, "No stream to size()"); + AssertFatal(m_uncompressedSize != 0, "No data? Properties probably not set..."); + + return m_uncompressedSize; +} + +//-------------------------------------- +U32 ZipSubRStream::fillBuffer(const U32 in_attemptSize) +{ + AssertFatal(m_pStream != NULL, "No stream to fill from?"); + AssertFatal(m_pStream->getStatus() != Stream::Closed, + "Fill from a closed stream?"); + + U32 streamSize = m_pStream->getStreamSize(); + U32 currPos = m_pStream->getPosition(); + + U32 actualReadSize; + if (in_attemptSize + currPos > streamSize) { + actualReadSize = streamSize - currPos; + } else { + actualReadSize = in_attemptSize; + } + + if (m_pStream->read(actualReadSize, m_pInputBuffer) == true) { + return actualReadSize; + } else { + AssertWarn(false, "Read failed while trying to fill buffer"); + return 0; + } +} + + +//-------------------------------------------------------------------------- +ZipSubWStream::ZipSubWStream() + : m_pStream(NULL), + m_pZipStream(NULL), + m_currPosition(0), + m_pOutputBuffer(NULL), + m_pInputBuffer(NULL), + m_lastBytesRead(0), + m_lastBytesWritten(0) +{ + // +} + +//-------------------------------------- +ZipSubWStream::~ZipSubWStream() +{ + detachStream(); +} + +//-------------------------------------- +bool ZipSubWStream::attachStream(Stream* io_pSlaveStream) +{ + AssertFatal(io_pSlaveStream != NULL, "NULL Slave stream?"); + AssertFatal(m_pStream == NULL, "Already attached!"); + + m_pStream = io_pSlaveStream; + m_currPosition = 0; + + m_pOutputBuffer = new U8[csm_bufferSize]; + m_pInputBuffer = new U8[csm_bufferSize]; + + // Initialize zipStream state... + m_pZipStream = new z_stream_s; + + m_pZipStream->zalloc = Z_NULL; + m_pZipStream->zfree = Z_NULL; + m_pZipStream->opaque = Z_NULL; + + m_pZipStream->next_in = m_pInputBuffer; + m_pZipStream->avail_in = csm_bufferSize; + m_pZipStream->total_in = 0; + m_pZipStream->next_out = m_pOutputBuffer; + m_pZipStream->avail_out = csm_bufferSize; + m_pZipStream->total_out = 0; + + deflateInit2(m_pZipStream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); + + setStatus(Ok); + return true; +} + +//-------------------------------------- +void ZipSubWStream::detachStream() +{ + // Must finish... + if (m_pZipStream != NULL) + { + m_pZipStream->avail_in = 0; + deflate(m_pZipStream, Z_FINISH); + + // write the remainder + m_pStream->write(csm_bufferSize - m_pZipStream->avail_out, m_pOutputBuffer); + + // close out zip stream... + deflateEnd(m_pZipStream); + + delete m_pZipStream; + m_pZipStream = NULL; + + delete [] m_pInputBuffer; + delete [] m_pOutputBuffer; + m_pInputBuffer = NULL; + m_pOutputBuffer = NULL; + } + + m_pStream = NULL; + m_currPosition = 0; + setStatus(Closed); +} + +//-------------------------------------- +Stream* ZipSubWStream::getStream() +{ + return m_pStream; +} + +//-------------------------------------- +bool ZipSubWStream::_read(const U32, void*) +{ + AssertFatal(false, "Cannot read from a ZipSubWStream"); + + setStatus(IllegalCall); + return false; +} + +//-------------------------------------- +bool ZipSubWStream::_write(const U32 numBytes, const void *pBuffer) +{ + m_lastBytesWritten = 0; + if (numBytes == 0) + return true; + + AssertFatal(pBuffer != NULL, "NULL input buffer"); + if (getStatus() == Closed) + { + AssertFatal(false, "Attempted write to a closed stream"); + return false; + } + + m_pZipStream->next_in = (U8*)pBuffer; + m_pZipStream->avail_in = numBytes; + + // write as many bufferSize chunks as possible + while(m_pZipStream->avail_in != 0) + { + if(m_pZipStream->avail_out == 0) + { + if(!m_pStream->write(csm_bufferSize, m_pOutputBuffer)) + return(false); + + m_pZipStream->next_out = m_pOutputBuffer; + m_pZipStream->avail_out = csm_bufferSize; + } + + S32 retVal = deflate(m_pZipStream, Z_NO_FLUSH); + AssertFatal(retVal != Z_BUF_ERROR, "ZipSubWStream::_write: invalid buffer"); + } + + setStatus(Ok); + m_currPosition += m_pZipStream->total_out; + + m_lastBytesWritten = m_pZipStream->total_out; + + return true; +} + +//-------------------------------------- +bool ZipSubWStream::hasCapability(const Capability in_cap) const +{ + return (csm_streamCaps & U32(in_cap)) != 0; +} + +//-------------------------------------- +U32 ZipSubWStream::getPosition() const +{ + AssertFatal(m_pStream != NULL, "Error, not attached"); + + return m_currPosition; +} + +//-------------------------------------- +bool ZipSubWStream::setPosition(const U32 /*in_newPosition*/) +{ + AssertFatal(m_pStream != NULL, "Error, not attached"); + AssertFatal(false, "Not implemented!"); + + // Erk. How do we do this. + return false; +} + +U32 ZipSubWStream::getStreamSize() +{ + AssertFatal(false, "Undecided how to implement this!"); + return 0; +} + diff --git a/Engine/source/core/util/zip/zipSubStream.h b/Engine/source/core/util/zip/zipSubStream.h new file mode 100644 index 000000000..db672b1dd --- /dev/null +++ b/Engine/source/core/util/zip/zipSubStream.h @@ -0,0 +1,121 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _ZIPSUBSTREAM_H_ +#define _ZIPSUBSTREAM_H_ + +//Includes +#ifndef _FILTERSTREAM_H_ +#include "core/filterStream.h" +#endif + +struct z_stream_s; + +class ZipSubRStream : public FilterStream, public IStreamByteCount +{ + typedef FilterStream Parent; + static const U32 csm_streamCaps; + static const U32 csm_inputBufferSize; + + Stream* m_pStream; + U32 m_uncompressedSize; + U32 m_currentPosition; + bool m_EOS; + z_stream_s* m_pZipStream; + U8* m_pInputBuffer; + U32 m_originalSlavePosition; + U32 m_lastBytesRead; + + U32 fillBuffer(const U32 in_attemptSize); +public: + virtual U32 getLastBytesRead() { return m_lastBytesRead; } + virtual U32 getLastBytesWritten() { return 0; } + + + public: + ZipSubRStream(); + virtual ~ZipSubRStream(); + + // Overrides of NFilterStream + public: + bool attachStream(Stream* io_pSlaveStream); + void detachStream(); + Stream* getStream(); + + void setUncompressedSize(const U32); + + // Mandatory overrides. By default, these are simply passed to + // whatever is returned from getStream(); + protected: + bool _read(const U32 in_numBytes, void* out_pBuffer); + public: + bool hasCapability(const Capability) const; + + U32 getPosition() const; + bool setPosition(const U32 in_newPosition); + + U32 getStreamSize(); +}; + +class ZipSubWStream : public FilterStream, public IStreamByteCount +{ + typedef FilterStream Parent; + static const U32 csm_streamCaps; + static const U32 csm_bufferSize; + + Stream* m_pStream; + z_stream_s* m_pZipStream; + U32 m_currPosition; // Indicates number of _uncompressed_ bytes written + U8* m_pOutputBuffer; + U8* m_pInputBuffer; + U32 m_lastBytesRead; + U32 m_lastBytesWritten; + +public: + virtual U32 getLastBytesRead() { return m_lastBytesRead; } + virtual U32 getLastBytesWritten() { return m_lastBytesWritten; } + + public: + ZipSubWStream(); + virtual ~ZipSubWStream(); + + // Overrides of NFilterStream + public: + bool attachStream(Stream* io_pSlaveStream); + void detachStream(); + Stream* getStream(); + + // Mandatory overrides. By default, these are simply passed to + // whatever is returned from getStream(); + protected: + bool _read(const U32 in_numBytes, void* out_pBuffer); + bool _write(const U32 in_numBytes, const void* in_pBuffer); + public: + bool hasCapability(const Capability) const; + + U32 getPosition() const; + bool setPosition(const U32 in_newPosition); + + U32 getStreamSize(); +}; + +#endif //_ZIPSUBSTREAM_H_ diff --git a/Engine/source/core/util/zip/zipTempStream.cpp b/Engine/source/core/util/zip/zipTempStream.cpp new file mode 100644 index 000000000..b122a2768 --- /dev/null +++ b/Engine/source/core/util/zip/zipTempStream.cpp @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/console.h" +#include "core/util/zip/zipTempStream.h" +#include "core/crc.h" + +namespace Zip +{ + +//----------------------------------------------------------------------------- +// Public Methods +//----------------------------------------------------------------------------- +static S32 tempNum = 0; + +bool ZipTempStream::open(String filename, Torque::FS::File::AccessMode mode) +{ + if(filename.isEmpty()) + { + filename = String("_ZipTempStream_tempfile") + String::ToString(tempNum++); + //filename = Platform::getTemporaryFileName(); + mDeleteOnClose = true; + } + + mFilename = filename; + + if(! Parent::open(filename, mode)) + return false; + + mStreamCaps &= ~U32(StreamPosition); + + return true; +} + +} // end namespace Zip diff --git a/Engine/source/core/util/zip/zipTempStream.h b/Engine/source/core/util/zip/zipTempStream.h new file mode 100644 index 000000000..7efcb141b --- /dev/null +++ b/Engine/source/core/util/zip/zipTempStream.h @@ -0,0 +1,92 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/util/zip/zipArchive.h" +#include "core/util/str.h" + +#ifndef _ZIPTEMPSTREAM_H_ +#define _ZIPTEMPSTREAM_H_ + +namespace Zip +{ + +/// @addtogroup zipint_group +/// @ingroup zip_group +// @{ + +class ZipTempStream : public FileStream +{ + typedef FileStream Parent; + +protected: + CentralDir *mCD; + bool mDeleteOnClose; + String mFilename; + +public: + ZipTempStream() : mCD(NULL), mDeleteOnClose(false) {} + ZipTempStream(CentralDir *cd) : mCD(cd), mDeleteOnClose(false) {} + virtual ~ZipTempStream() { close(); } + + void setCentralDir(CentralDir *cd) { mCD = cd; } + CentralDir *getCentralDir() { return mCD; } + + void setDeleteOnClose(bool del) { mDeleteOnClose = del; } + + virtual bool open(String filename, Torque::FS::File::AccessMode mode); + + /// Open a temporary file in ReadWrite mode. The file will be deleted when the stream is closed. + virtual bool open() + { + return open(String(), Torque::FS::File::ReadWrite); + } + + virtual void close() + { + Parent::close(); + + if(mDeleteOnClose) + Torque::FS::Remove(mFilename); + + } + + /// Disallow setPosition() + virtual bool setPosition(const U32 i_newPosition) { return false; } + + /// Seek back to the start of the file. + /// This is used internally by the zip code and should never be called whilst + /// filters are attached (e.g. when reading or writing in a zip file) + bool rewind() + { + mStreamCaps |= U32(StreamPosition); + bool ret = Parent::setPosition(0); + mStreamCaps &= ~U32(StreamPosition); + + return ret; + } +}; + +// @} + +} // end namespace Zip + +#endif // _ZIPTEMPSTREAM_H_ diff --git a/Engine/source/core/util/zip/zipVolume.cpp b/Engine/source/core/util/zip/zipVolume.cpp new file mode 100644 index 000000000..16bad16dd --- /dev/null +++ b/Engine/source/core/util/zip/zipVolume.cpp @@ -0,0 +1,475 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/util/zip/zipVolume.h" + +#include "core/util/zip/zipSubStream.h" +#include "core/util/noncopyable.h" +#include "console/console.h" + +namespace Torque +{ + using namespace FS; + using namespace Zip; + +class ZipFileNode : public Torque::FS::File, public Noncopyable +{ + typedef FileNode Parent; + + //-------------------------------------------------------------------------- + // ZipFileNode class (Internal) + //-------------------------------------------------------------------------- +public: + ZipFileNode(StrongRefPtr& archive, String zipFilename, Stream* zipStream, ZipArchive::ZipEntry* ze) + { + mZipStream = zipStream; + mArchive = archive; + mZipFilename = zipFilename; + mByteCount = dynamic_cast(mZipStream); + AssertFatal(mByteCount, "error, zip stream interface does not implement IStreamByteCount"); + mZipEntry = ze; + } + virtual ~ZipFileNode() + { + close(); + } + + virtual Path getName() const { return mZipFilename; } + virtual Status getStatus() const + { + if (mZipStream) + { + // great, Stream Status is different from FileNode Status... + switch (mZipStream->getStatus()) + { + case Stream::Ok: + return FileNode::Open; + case Stream::EOS: + return FileNode::EndOfFile; + case Stream::IOError: + return FileNode::UnknownError; + default: + return FileNode::UnknownError; + } + } + else + return FileNode::Closed; + } + + virtual bool getAttributes(Attributes* attr) + { + if (!attr) + return false; + + attr->flags = FileNode::File | FileNode::Compressed | FileNode::ReadOnly; + attr->name = mZipFilename; + // use the mod time for both mod and access time, since we only have mod time in the CD + attr->mtime = ZipArchive::DOSTimeToTime(mZipEntry->mCD.mModTime, mZipEntry->mCD.mModDate); + attr->atime = ZipArchive::DOSTimeToTime(mZipEntry->mCD.mModTime, mZipEntry->mCD.mModDate); + attr->size = mZipEntry->mCD.mUncompressedSize; + + return true; + } + + virtual U32 getPosition() + { + if (mZipStream) + return mZipStream->getPosition(); + else + return 0; + } + + virtual U32 setPosition(U32 pos, SeekMode mode) + { + if (!mZipStream || mode != Begin) + return 0; + else + return mZipStream->setPosition(pos); + } + + virtual bool open(AccessMode mode) + { + // stream is already open so just check to make sure that they are using a valid mode + if (mode == Read) + return mZipStream != NULL; + else + { + Con::errorf("ZipFileSystem: Write access denied for file %s", mZipFilename.c_str()); + return false; + } + } + + virtual bool close() + { + if (mZipStream != NULL && mArchive != NULL) + { + mArchive->closeFile(mZipStream); + mZipStream = NULL; + mByteCount = NULL; + } + return true; + } + + virtual U32 read(void* dst, U32 size) + { + if (mZipStream && mZipStream->read(size, dst) && mByteCount) + return mByteCount->getLastBytesRead(); + else + return 0; + } + + virtual U32 write(const void* src, U32 size) + { + if (mZipStream && mZipStream->write(size, src) && mByteCount) + return mByteCount->getLastBytesWritten(); + else + return 0; + } + + protected: + virtual U32 calculateChecksum() + { + // JMQ: implement + return 0; + }; + + Stream* mZipStream; + StrongRefPtr mArchive; + ZipArchive::ZipEntry* mZipEntry; + String mZipFilename; + IStreamByteCount* mByteCount; +}; + +//-------------------------------------------------------------------------- +// ZipDirectoryNode class (Internal) +//-------------------------------------------------------------------------- + +class ZipDirectoryNode : public Torque::FS::Directory, public Noncopyable +{ +public: + ZipDirectoryNode(StrongRefPtr& archive, const Torque::Path& path, ZipArchive::ZipEntry* ze) + { + mPath = path; + mArchive = archive; + mZipEntry = ze; + if (mZipEntry) + mChildIter = mZipEntry->mChildren.end(); + } + ~ZipDirectoryNode() + { + } + + Torque::Path getName() const { return mPath; } + + // getStatus() doesn't appear to be used for directories + Status getStatus() const + { + return FileNode::Open; + } + + bool getAttributes(Attributes* attr) + { + if (!attr) + return false; + + attr->flags = FileNode::Directory | FileNode::Compressed | FileNode::ReadOnly; + attr->name = mPath.getFullPath(); + // use the mod time for both mod and access time, since we only have mod time in the CD + attr->mtime = ZipArchive::DOSTimeToTime(mZipEntry->mCD.mModTime, mZipEntry->mCD.mModDate); + attr->atime = ZipArchive::DOSTimeToTime(mZipEntry->mCD.mModTime, mZipEntry->mCD.mModDate); + attr->size = mZipEntry->mCD.mUncompressedSize; + + return true; + } + + bool open() + { + // reset iterator + if (mZipEntry) + mChildIter = mZipEntry->mChildren.begin(); + return (mZipEntry != NULL && mArchive.getPointer() != NULL); + } + bool close() + { + if (mZipEntry) + mChildIter = mZipEntry->mChildren.end(); + return true; + } + bool read(Attributes* attr) + { + if (!attr) + return false; + + if (mChildIter == mZipEntry->mChildren.end()) + return false; + + ZipArchive::ZipEntry* ze = (*mChildIter).value; + + attr->flags = FileNode::Compressed; + if (ze->mIsDirectory) + attr->flags |= FileNode::Directory; + else + attr->flags |= FileNode::File; + + attr->name = ze->mName; + attr->mtime = ZipArchive::DOSTimeToTime(ze->mCD.mModTime, ze->mCD.mModDate); + attr->atime = ZipArchive::DOSTimeToTime(ze->mCD.mModTime, ze->mCD.mModDate); + attr->size = 0; // we don't know the size until we open a stream, so we'll have to use size 0 + + mChildIter++; + + return true; + } + +private: + U32 calculateChecksum() + { + return 0; + } + + Torque::Path mPath; + Map::Iterator mChildIter; + StrongRefPtr mArchive; + ZipArchive::ZipEntry* mZipEntry; +}; + +//-------------------------------------------------------------------------- +// ZipFakeRootNode class (Internal) +//-------------------------------------------------------------------------- + +class ZipFakeRootNode : public Torque::FS::Directory, public Noncopyable +{ +public: + ZipFakeRootNode(StrongRefPtr& archive, const Torque::Path& path, const String &fakeRoot) + { + mPath = path; + mArchive = archive; + mRead = false; + mFakeRoot = fakeRoot; + } + ~ZipFakeRootNode() + { + } + + Torque::Path getName() const { return mPath; } + + // getStatus() doesn't appear to be used for directories + Status getStatus() const + { + return FileNode::Open; + } + + bool getAttributes(Attributes* attr) + { + if (!attr) + return false; + + attr->flags = FileNode::Directory | FileNode::Compressed | FileNode::ReadOnly; + attr->name = mPath.getFullPath(); + // use the mod time for both mod and access time, since we only have mod time in the CD + + ZipArchive::ZipEntry* zipEntry = mArchive->getRoot(); + attr->mtime = ZipArchive::DOSTimeToTime(zipEntry->mCD.mModTime, zipEntry->mCD.mModDate); + attr->atime = ZipArchive::DOSTimeToTime(zipEntry->mCD.mModTime, zipEntry->mCD.mModDate); + attr->size = zipEntry->mCD.mUncompressedSize; + + return true; + } + + bool open() + { + mRead = false; + return (mArchive.getPointer() != NULL); + } + bool close() + { + mRead = false; + return true; + } + bool read(Attributes* attr) + { + if (!attr) + return false; + + if (mRead) + return false; + + ZipArchive::ZipEntry* ze = mArchive->getRoot(); + + attr->flags = FileNode::Compressed; + if (ze->mIsDirectory) + attr->flags |= FileNode::Directory; + else + attr->flags |= FileNode::File; + + attr->name = mFakeRoot; + attr->mtime = ZipArchive::DOSTimeToTime(ze->mCD.mModTime, ze->mCD.mModDate); + attr->atime = ZipArchive::DOSTimeToTime(ze->mCD.mModTime, ze->mCD.mModDate); + attr->size = 0; // we don't know the size until we open a stream, so we'll have to use size 0 + + mRead = true; + + return true; + } + +private: + U32 calculateChecksum() + { + return 0; + } + + Torque::Path mPath; + Map::Iterator mChildIter; + StrongRefPtr mArchive; + bool mRead; + String mFakeRoot; +}; + +//-------------------------------------------------------------------------- +// ZipFileSystem +//-------------------------------------------------------------------------- + +ZipFileSystem::ZipFileSystem(String& zipFilename, bool zipNameIsDir /* = false */) +{ + mZipFilename = zipFilename; + mInitted = false; + mZipNameIsDir = zipNameIsDir; + if(mZipNameIsDir) + { + Path path(zipFilename); + mFakeRoot = Path::Join(path.getPath(), '/', path.getFileName()); + } + + // open the file now but don't read it yet, since we want construction to be lightweight + // we open the file now so that whatever filesystems are mounted right now (which may be temporary) + // can be umounted without affecting this file system. + mZipArchiveStream = new FileStream(); + mZipArchiveStream->open(mZipFilename, Torque::FS::File::Read); + + // As far as the mount system is concerned, ZFSes are read only write now (even though + // ZipArchive technically support read-write, we don't expose this to the mount system because we + // don't want to support that as a standard run case right now.) + mReadOnly = true; +} + +ZipFileSystem::~ZipFileSystem() +{ + if (mZipArchiveStream) + { + mZipArchiveStream->close(); + delete mZipArchiveStream; + } + mZipArchive = NULL; +} + +FileNodeRef ZipFileSystem::resolve(const Path& path) +{ + if (!mInitted) + _init(); + + if (mZipArchive.isNull()) + return NULL; + + // eat leading "/" + String name = path.getFullPathWithoutRoot(); + if (name.find("/") == 0) + name = name.substr(1, name.length() - 1); + + if(name.isEmpty() && mZipNameIsDir) + return new ZipFakeRootNode(mZipArchive, path, mFakeRoot); + + if(mZipNameIsDir) + { + // Remove the fake root from the name so things can be found + if(name.find(mFakeRoot) == 0) + name = name.substr(mFakeRoot.length()); + +#ifdef TORQUE_DISABLE_FIND_ROOT_WITHIN_ZIP + else + // If a zip file's name isn't the root of the path we're looking for + // then do not continue. Otherwise, we'll continue to look for the + // path's root within the zip file itself. i.e. we're looking for the + // path "scripts/test.cs". If the zip file itself isn't called scripts.zip + // then we won't look within the archive for a "scripts" directory. + return NULL; +#endif + + if (name.find("/") == 0) + name = name.substr(1, name.length() - 1); + } + + // first check to see if input path is a directory + // check for request of root directory + if (name.isEmpty()) + { + ZipDirectoryNode* zdn = new ZipDirectoryNode(mZipArchive, path, mZipArchive->getRoot()); + return zdn; + } + + ZipArchive::ZipEntry* ze = mZipArchive->findZipEntry(name); + if (ze == NULL) + return NULL; + + if (ze->mIsDirectory) + { + ZipDirectoryNode* zdn = new ZipDirectoryNode(mZipArchive, path, ze); + return zdn; + } + + // pass in the zip entry so that openFile() doesn't need to look it up again. + Stream* stream = mZipArchive->openFile(name, ze, ZipArchive::Read); + if (stream == NULL) + return NULL; + + ZipFileNode* zfn = new ZipFileNode(mZipArchive, name, stream, ze); + return zfn; +} + +void ZipFileSystem::_init() +{ + if (mInitted) + return; + mInitted = true; + + if (!mZipArchive.isNull()) + return; + if (mZipArchiveStream->getStatus() != Stream::Ok) + return; + + mZipArchive = new ZipArchive(); + if (!mZipArchive->openArchive(mZipArchiveStream, ZipArchive::Read)) + { + Con::errorf("ZipFileSystem: failed to open zip archive %s", mZipFilename.c_str()); + return; + } + + // tell the archive that it owns the zipStream now + mZipArchive->setDiskStream(mZipArchiveStream); + // and null it out because we don't own it anymore + mZipArchiveStream = NULL; + + // for debugging + //mZipArchive->dumpCentralDirectory(); +} + +}; \ No newline at end of file diff --git a/Engine/source/core/util/zip/zipVolume.h b/Engine/source/core/util/zip/zipVolume.h new file mode 100644 index 000000000..75838579c --- /dev/null +++ b/Engine/source/core/util/zip/zipVolume.h @@ -0,0 +1,71 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _CORE_ZIP_VOLUME_H_ +#define _CORE_ZIP_VOLUME_H_ + +#include "core/volume.h" +#include "core/util/str.h" +#include "core/util/zip/zipArchive.h" +#include "core/util/autoPtr.h" + +namespace Torque +{ + using namespace FS; + using namespace Zip; + +class ZipFileSystem: public FileSystem +{ +public: + ZipFileSystem(String& zipFilename, bool zipNameIsDir = false); + virtual ~ZipFileSystem(); + + String getTypeStr() const { return "Zip"; } + + FileNodeRef resolve(const Path& path); + + // these are unsupported, ZipFileSystem is currently read only access + FileNodeRef create(const Path& path,FileNode::Mode) { return 0; } + bool remove(const Path& path) { return 0; } + bool rename(const Path& a,const Path& b) { return 0; } + + // these are unsupported + Path mapTo(const Path& path) { return path; } + Path mapFrom(const Path& path) { return path; } + +public: + /// Private interface for use by unit test only. + StrongRefPtr getArchive() { return mZipArchive; } + +private: + void _init(); + + bool mInitted; + bool mZipNameIsDir; + String mZipFilename; + String mFakeRoot; + FileStream* mZipArchiveStream; + StrongRefPtr mZipArchive; +}; + +} + +#endif \ No newline at end of file diff --git a/Engine/source/core/virtualMountSystem.cpp b/Engine/source/core/virtualMountSystem.cpp new file mode 100644 index 000000000..42bc27b5a --- /dev/null +++ b/Engine/source/core/virtualMountSystem.cpp @@ -0,0 +1,310 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/virtualMountSystem.h" + +#include "console/console.h" +#include "core/tAlgorithm.h" + +namespace Torque +{ +namespace FS +{ + +bool gVMSVerboseLog = false; + +bool VirtualMountSystem::mount(String root, FileSystemRef fs) +{ + bool ok = Parent::mount(root,fs); + if (!ok) + return false; + + root = String::ToLower(root); + + mRootMap[root].push_back(fs); + +// PathFSMap* rootDict = NULL; +// if (!mMountMap.tryGetValue(root, rootDict)) +// { +// rootDict = new PathFSMap(); +// mMountMap[root] = rootDict; +// } +// +// U32 start = Platform::getRealMilliseconds(); +// +// // get the paths from the fs and add them to the rootDict +// Vector paths; +// +// // we'll use the mount system's findByPattern function to build the path list. +// // but, we want to override its default behavior so that it searches only the desired fs. +// _setFindByPatternOverrideFS(fs); +// +// Torque::Path basePath; +// // we use an empty root so that the resulting filenames don't have the root filename in them. +// // we don't want to include the root in the dict has entries. we can omit the root because we have +// // specified an override FS; the search would fail otherwise. +// //basePath.setRoot(root); +// basePath.setRoot(""); +// basePath.setPath("/"); +// mUseParentFind = true; +// if (findByPattern(basePath, "*.*", true, paths, true) == -1) +// { +// // this is probably a problem +// _log("Unable to get paths from filesystem for virtual mount"); +// _setFindByPatternOverrideFS(NULL); +// mUseParentFind = false; +// return false; +// } +// +// _setFindByPatternOverrideFS(NULL); +// mUseParentFind = false; +// +// for (S32 i = 0; i < paths.size(); ++i) +// { +// String path = String::ToLower(paths[i]); +// +// // is it a directory? if so remove dir prefix +// String dirPrefix = "DIR:"; +// String::SizeType dIdx = path.find(dirPrefix, 0, String::NoCase); +// if (dIdx == 0) +// path = path.substr(dirPrefix.length()); +// // omit leading / +// if (path[(String::SizeType)0] == '/') +// path = path.substr(1); +// +// // warn about duplicate files (not directories) +// // JMQ: disabled this, it false alarms at startup because the mount doc always mounts the +// // root before other processing mounts. still, would be useful, maybe change the mount doc to +// // not mount root? +// if (dIdx != 0 && (*rootDict)[path].size() > 0) +// _log(String::ToString("Duplicate file path detected, first volume containing file will be used: %s", path.c_str())); +// +// (*rootDict)[path].push_back(fs); +// } +// +// if (gVMSVerboseLog) +// _log(String::ToString("Indexed virtual file system in %ums", Platform::getRealMilliseconds() - start)); + + return true; +} + +bool VirtualMountSystem::mount(String root, const Path &path) +{ + //AssertFatal(false, "This function not supported in virtual mount system"); + return Parent::mount(root, path); +} + +FileSystemRef VirtualMountSystem::unmount(String root) +{ + FileSystemRef ret = Parent::unmount(root); + + mRootMap.erase(root); + + // clear all filesystem lists for root. +// PathFSMap* rootDict = NULL; +// root = String::ToLower(root); +// if (!mMountMap.tryGetValue(root, rootDict)) +// return ret; +// +// // buh bye +// mMountMap.erase(root); +// delete rootDict; + + return ret; +} + +bool VirtualMountSystem::unmount(FileSystemRef fs) +{ + bool unmounted = Parent::unmount(fs); + if (!unmounted) + return false; + + for(PathFSMap::Iterator ritr = mRootMap.begin();ritr != mRootMap.end();++ritr) + { + RootToFSVec &vec = (*ritr).value; + for (S32 i = vec.size() - 1;i >= 0;i--) + { + if (vec[i].getPointer() == fs.getPointer()) + vec.erase(i); + } + } + + // this is a linear time operation, because we have to search every path in all roots + // to remove references to the fs. + // contant time operation can be achieved be using the unmount(string) version, which unmounts all + // filesystems for a given root and so doesn't need to do any searching. +// U32 start = Platform::getRealMilliseconds(); +// for (RootToPathFSMap::Iterator riter = mMountMap.begin(); +// riter != mMountMap.end(); +// ++riter) +// { +// PathFSMap* rootDict = (*riter).value; +// for (PathFSMap::Iterator piter = rootDict->begin(); +// piter != rootDict->end(); +// ++piter) +// { +// Vector& plist = (*piter).value; +// for (S32 i = plist.size() - 1; +// i >= 0; +// i--) +// { +// if (plist[i].getPointer() == fs.getPointer()) +// plist.erase(i); +// } +// } +// } +// +// if (gVMSVerboseLog) +// _log(String::ToString("Unmounted virtual file system in %ums", Platform::getRealMilliseconds() - start)); + + return true; +} + +S32 VirtualMountSystem::findByPattern( const Path &inBasePath, const String &inFilePattern, bool inRecursive, Vector &outList, bool includeDirs/* =false */, bool multiMatch /* = true */ ) +{ + if (mUseParentFind) + // use parent version + return Parent::findByPattern(inBasePath, inFilePattern, inRecursive, outList, includeDirs, multiMatch); + + // don't want to re-enter this version + mUseParentFind = true; + + // kind of cheesy, just call find by pattern on each File system mounted on the root + for (Vector::const_iterator itr = mMountList.begin(); itr != mMountList.end(); itr++) + { + if (itr->root.equal( inBasePath.getRoot(), String::NoCase ) ) + { + FileSystemRef fsref = itr->fileSystem; + _setFindByPatternOverrideFS(fsref); + Parent::findByPattern(inBasePath, inFilePattern, inRecursive, outList, includeDirs, multiMatch); + _setFindByPatternOverrideFS(NULL); + } + } + + mUseParentFind = false; + + return outList.size(); +} + +bool VirtualMountSystem::createPath(const Path& path) +{ + bool ret = Parent::createPath(path); + +// if (ret) +// { +// // make sure the filesystem that owns the path has the path elements +// // in its search table (so that we can open the file, if it is new) +// String root = String::ToLower(path.getRoot()); +// FileSystemRef fsRef = getFileSystem(path); +// +// PathFSMap* rootDict = mMountMap[root]; +// if (rootDict) +// { +// // add all directories in the path +// // add the filename +// +// // Start from the top and work our way down +// Path sub,dir; +// dir.setPath(""); +// for (U32 i = 0; i < path.getDirectoryCount(); i++) +// { +// sub.setPath(path.getDirectory(i)); +// dir.appendPath(sub); +// +// Vector& fsList = (*rootDict)[String::ToLower(dir.getPath())]; +// Vector::iterator iter = ::find(fsList.begin(), fsList.end(), fsRef); +// +// if (iter == fsList.end()) +// fsList.push_back(fsRef); +// } +// +// // add full file path +// Vector& fsList = (*rootDict)[String::ToLower(path.getFullPath(false))]; +// Vector::iterator iter = ::find(fsList.begin(), fsList.end(), fsRef); +// +// if (iter == fsList.end()) +// fsList.push_back(fsRef); +// } +// } + + return ret; +} + +void VirtualMountSystem::_log(const String& msg) +{ + String newMsg = "VirtualMountSystem: " + msg; + Con::warnf("%s", newMsg.c_str()); +} + +FileSystemRef VirtualMountSystem::_removeMountFromList(String root) +{ + return Parent::_removeMountFromList(root); +} + +FileSystemRef VirtualMountSystem::_getFileSystemFromList(const Path& fullpath) const +{ + String root = String::ToLower(fullpath.getRoot()); + String path = fullpath.getFullPathWithoutRoot(); + // eat leading slash + if (path[(String::SizeType)0] == '/') + path = path.substr(1); + // lowercase it + path = String::ToLower(path); + + // find the dictionary for root +// PathFSMap* rootDict = NULL; +// if (!mMountMap.tryGetValue(root, rootDict)) +// return NULL; +// +// // see if we have a FS list for this path +// Vector& fsList = (*rootDict)[path]; + + RootToFSVec fsList; + if(! mRootMap.tryGetValue(root, fsList)) + return NULL; + + if (fsList.size() == 0) + { + // no exact match for path, defer to parent + return Parent::_getFileSystemFromList(fullpath); + } + else + { + // find the right file system + if(fsList.size() == 1) + return fsList[0]; + + // Go in reverse order to pick up the last matching virtual path + for(S32 i = fsList.size()-1; i >= 0 ; --i) + { + FileNodeRef fn = fsList[i]->resolve(path); + if(fn != NULL) + return fsList[i]; + } + + return fsList[0]; + } +} + +} //namespace FS +} //namespace Torque \ No newline at end of file diff --git a/Engine/source/core/virtualMountSystem.h b/Engine/source/core/virtualMountSystem.h new file mode 100644 index 000000000..e7e3ca181 --- /dev/null +++ b/Engine/source/core/virtualMountSystem.h @@ -0,0 +1,87 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _CORE_VMS_H_ +#define _CORE_VMS_H_ + +#include "core/volume.h" +#include "core/util/tDictionary.h" + +namespace Torque +{ +namespace FS +{ + + /// The VirtualMountSystem extend the mount system by allowing you to mount and access multiple filesystems + /// on a single root. This allows you to mount multiple volume files on a single root without needing to + /// change source paths that reference those volumes to use new roots. For instance, you could mount + /// "missions1.zip" and "missions2.zip", on the same root "", and you could access files from either one. + /// + /// If you need to mount a writeable filesystem on a VMS, you should mount it as the first filesystem on a root. + /// File writes are handled as follows: + /// 1) If you try to open a file for write, and it exists on one of the virtual mounts, + /// that is the mount that will be used for writing. But if the filesystem is read-only then the writes + /// will fail (and you will get appropriate error codes from your FileStream or whatever). + /// 2) If you try to open a file for write that doesn't exist, the VMS falls back to the default MountSystem + /// behavior, which is to use the first filesystem mounted on the root. If this filesystem happens to be + /// writable, then you will be able to write to the file. + /// 3) Nothing special is done for the WriteAppend case; that is, it follows the same logic as if you were + /// just trying to Write the file. + /// + /// Because of rule 1) above, you should take care that any files you need to write (such as prefs.cs), are not + /// included in read-only zip archive files. + class VirtualMountSystem : public MountSystem + { + typedef MountSystem Parent; + public: + VirtualMountSystem() : mUseParentFind(false) {} + + virtual ~VirtualMountSystem() { } + virtual bool mount(String root, FileSystemRef fs); + virtual bool mount(String root, const Path &path); + virtual FileSystemRef unmount(String root); + virtual bool unmount(FileSystemRef fs); + virtual S32 findByPattern( const Path &inBasePath, const String &inFilePattern, bool inRecursive, Vector &outList, bool includeDirs=false, bool multiMatch = true ); + virtual bool createPath(const Path& path); + + protected: + virtual void _log(const String& msg); + virtual FileSystemRef _removeMountFromList(String root); + virtual FileSystemRef _getFileSystemFromList(const Path& path) const ; + + // Vector of file system refs + typedef Vector RootToFSVec; + // map of path to list of file systems containing path + typedef Map > PathFSMap; + // map of root to PathFSMap for that root + //typedef Map RootToPathFSMap; + + //RootToPathFSMap mMountMap; + PathFSMap mRootMap; + + bool mUseParentFind; // this is needed because findByParent calls itself recursively and in some cases we don't want it to re-enter our version + }; + +} +} + +#endif + diff --git a/Engine/source/core/volume.cpp b/Engine/source/core/volume.cpp new file mode 100644 index 000000000..1e0b6cda7 --- /dev/null +++ b/Engine/source/core/volume.cpp @@ -0,0 +1,1137 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/volume.h" + +#include "core/virtualMountSystem.h" +#include "core/strings/findMatch.h" +#include "core/util/journal/process.h" +#include "core/util/safeDelete.h" +#include "console/console.h" + + +namespace Torque +{ +using namespace FS; + +//----------------------------------------------------------------------------- + +bool FileSystemChangeNotifier::addNotification( const Path &path, ChangeDelegate callback ) +{ + // Notifications are for files... if the path is empty + // then there is nothing to do. + if ( path.isEmpty() ) + return false; + + // strip the filename and extension - we notify on dirs + Path dir(cleanPath(path)); + if (dir.isEmpty()) + return false; + + dir.setFileName( String() ); + dir.setExtension( String () ); + + // first lookup the dir to see if we already have an entry for it... + DirMap::Iterator itr = mDirMap.find( dir ); + + FileInfoList *fileList = NULL; + bool addedDir = false; + + // Note that GetFileAttributes can fail here if the file doesn't + // exist, but thats ok and expected. You can have notifications + // on files that don't exist yet. + FileNode::Attributes attr; + GetFileAttributes(path, &attr); + + if ( itr != mDirMap.end() ) + { + fileList = &(itr->value); + + // look for the file and return if we find it + for ( U32 i = 0; i < fileList->getSize(); i++ ) + { + FileInfo &fInfo = (*fileList)[i]; + if ( fInfo.filePath == path ) + { + // NOTE: This is bad... we should store the mod + // time for each callback seperately in the future. + // + fInfo.savedLastModTime = attr.mtime; + fInfo.signal.notify(callback); + return true; + } + } + } + else + { + // otherwise we need to add the dir to our map and let the inherited class add it + itr = mDirMap.insert( dir, FileInfoList() ); + + fileList = &(itr->value); + + addedDir = true; + } + + FileInfo newInfo; + newInfo.signal.notify(callback); + newInfo.filePath = path; + newInfo.savedLastModTime = attr.mtime; + + fileList->pushBack( newInfo ); + + return addedDir ? internalAddNotification( dir ) : true; +} + +bool FileSystemChangeNotifier::removeNotification( const Path &path, ChangeDelegate callback ) +{ + if (path.isEmpty()) + return false; + + // strip the filename and extension - we notify on dirs + Path dir(cleanPath(path)); + if (dir.isEmpty()) + return false; + + dir.setFileName( String() ); + dir.setExtension( String () ); + + DirMap::Iterator itr = mDirMap.find( dir ); + + if ( itr == mDirMap.end() ) + return false; + + FileInfoList &fileList = itr->value; + + // look for the file and return if we find it + for ( U32 i = 0; i < fileList.getSize(); i++ ) + { + FileInfo &fInfo = fileList[i]; + if ( fInfo.filePath == path ) + { + fInfo.signal.remove(callback); + if (fInfo.signal.isEmpty()) + fileList.erase( i ); + break; + } + } + + // IF we removed the last file + // THEN get rid of the dir from our map and notify inherited classes + if ( fileList.getSize() == 0 ) + { + mDirMap.erase( dir ); + + return internalRemoveNotification( dir ); + } + + return true; +} + +void FileSystemChangeNotifier::startNotifier() +{ + // update the timestamps of all the files we are managing + + DirMap::Iterator itr = mDirMap.begin(); + + for ( ; itr != mDirMap.end(); ++itr ) + { + FileInfoList &fileList = itr->value; + + for ( U32 i = 0; i < fileList.getSize(); i++ ) + { + FileInfo &fInfo = fileList[i]; + + // This may fail if the file doesn't exist... thats ok. + FileNode::Attributes attr; + GetFileAttributes(fInfo.filePath, &attr); + + fInfo.savedLastModTime = attr.mtime; + } + } + + mNotifying = true; + + Process::notify( this, &FileSystemChangeNotifier::process, PROCESS_LAST_ORDER ); +} + +void FileSystemChangeNotifier::process() +{ + internalProcessOnce(); +} + +void FileSystemChangeNotifier::stopNotifier() +{ + mNotifying = false; + + Process::remove( this, &FileSystemChangeNotifier::process ); +} + +/// Makes sure paths going in and out of the notifier will have the same format +String FileSystemChangeNotifier::cleanPath(const Path& dir) +{ + // This "cleans up" the path, if we don't do this we can get mismatches on the path + // coming from the notifier + FileSystemRef fs = Torque::FS::GetFileSystem(dir); + if (!fs) + return String::EmptyString; + return fs->mapFrom(fs->mapTo(dir)); +} + +void FileSystemChangeNotifier::internalNotifyDirChanged( const Path &dir ) +{ + DirMap::Iterator itr = mDirMap.find( dir ); + if ( itr == mDirMap.end() ) + return; + + // Gather the changed file info. + FileInfoList changedList; + FileInfoList &fileList = itr->value; + for ( U32 i = 0; i < fileList.getSize(); i++ ) + { + FileInfo &fInfo = fileList[i]; + + FileNode::Attributes attr; + bool success = GetFileAttributes(fInfo.filePath, &attr); + + // Ignore the file if we couldn't get the attributes (it must have + // been deleted) or the last modification time isn't newer. + if ( !success || attr.mtime <= fInfo.savedLastModTime ) + continue; + + // Store the new mod time. + fInfo.savedLastModTime = attr.mtime; + + // We're taking a copy of the FileInfo struct here so that the + // callback can safely remove the notification without crashing. + changedList.pushBack( fInfo ); + } + + // Now signal all the changed files. + for ( U32 i = 0; i < changedList.getSize(); i++ ) + { + FileInfo &fInfo = changedList[i]; + + Con::warnf( " : file changed [%s]", fInfo.filePath.getFullPath().c_str() ); + fInfo.signal.trigger( fInfo.filePath ); + } +} + +//----------------------------------------------------------------------------- + +FileSystem::FileSystem() + : mChangeNotifier( NULL ), + mReadOnly(false) +{ +} + +FileSystem::~FileSystem() +{ + delete mChangeNotifier; + mChangeNotifier = NULL; +} + +File::File() {} +File::~File() {} +Directory::Directory() {} +Directory::~Directory() {} + + +FileNode::FileNode() +: mChecksum(0) +{ +} + +Time FileNode::getModifiedTime() +{ + Attributes attrs; + + bool success = getAttributes( &attrs ); + + if ( !success ) + return Time(); + + return attrs.mtime; +} + +U64 FileNode::getSize() +{ + Attributes attrs; + + bool success = getAttributes( &attrs ); + + if ( !success ) + return 0; + + return attrs.size; +} + +U32 FileNode::getChecksum() +{ + bool calculateCRC = (mLastChecksum == Torque::Time()); + + if ( !calculateCRC ) + { + Torque::Time modTime = getModifiedTime(); + + calculateCRC = (modTime > mLastChecksum); + } + + if ( calculateCRC ) + mChecksum = calculateChecksum(); + + if ( mChecksum ) + mLastChecksum = Time::getCurrentTime(); + + return mChecksum; + +} + +//----------------------------------------------------------------------------- + +class FileSystemRedirect: public FileSystem +{ + friend class FileSystemRedirectChangeNotifier; +public: + FileSystemRedirect(MountSystem* mfs,const Path& path); + + String getTypeStr() const { return "Redirect"; } + + FileNodeRef resolve(const Path& path); + FileNodeRef create(const Path& path,FileNode::Mode); + bool remove(const Path& path); + bool rename(const Path& a,const Path& b); + Path mapTo(const Path& path); + Path mapFrom(const Path& path); + +private: + Path _merge(const Path& path); + + Path mPath; + MountSystem *mMFS; +}; + +class FileSystemRedirectChangeNotifier : public FileSystemChangeNotifier +{ +public: + + FileSystemRedirectChangeNotifier( FileSystem *fs ); + + bool addNotification( const Path &path, ChangeDelegate callback ); + bool removeNotification( const Path &path, ChangeDelegate callback ); + +protected: + + virtual void internalProcessOnce() {} + virtual bool internalAddNotification( const Path &dir ) { return false; } + virtual bool internalRemoveNotification( const Path &dir ) { return false; } +}; + +FileSystemRedirectChangeNotifier::FileSystemRedirectChangeNotifier( FileSystem *fs ) +: FileSystemChangeNotifier( fs ) +{ + +} + +bool FileSystemRedirectChangeNotifier::addNotification( const Path &path, ChangeDelegate callback ) +{ + FileSystemRedirect *rfs = (FileSystemRedirect*)mFS; + Path redirectPath = rfs->_merge( path ); + + FileSystemRef fs = rfs->mMFS->getFileSystem( redirectPath ); + if ( !fs || !fs->getChangeNotifier() ) + return false; + + return fs->getChangeNotifier()->addNotification( redirectPath, callback ); +} + +bool FileSystemRedirectChangeNotifier::removeNotification( const Path &path, ChangeDelegate callback ) +{ + FileSystemRedirect *rfs = (FileSystemRedirect*)mFS; + Path redirectPath = rfs->_merge( path ); + + FileSystemRef fs = rfs->mMFS->getFileSystem( redirectPath ); + if ( !fs || !fs->getChangeNotifier() ) + return false; + + return fs->getChangeNotifier()->removeNotification( redirectPath, callback ); +} + +FileSystemRedirect::FileSystemRedirect(MountSystem* mfs,const Path& path) +{ + mMFS = mfs; + mPath.setRoot(path.getRoot()); + mPath.setPath(path.getPath()); + mChangeNotifier = new FileSystemRedirectChangeNotifier( this ); +} + +Path FileSystemRedirect::_merge(const Path& path) +{ + Path p = mPath; + p.setPath(Path::Join(p.getPath(),'/',Path::CompressPath(path.getPath()))); + p.setFileName(path.getFileName()); + p.setExtension(path.getExtension()); + return p; +} + +FileNodeRef FileSystemRedirect::resolve(const Path& path) +{ + Path p = _merge(path); + FileSystemRef fs = mMFS->getFileSystem(p); + if (fs != NULL) + return fs->resolve(p); + return NULL; +} + +FileNodeRef FileSystemRedirect::create(const Path& path,FileNode::Mode mode) +{ + Path p = _merge(path); + FileSystemRef fs = mMFS->getFileSystem(p); + if (fs != NULL) + return fs->create(p,mode); + return NULL; +} + +bool FileSystemRedirect::remove(const Path& path) +{ + Path p = _merge(path); + FileSystemRef fs = mMFS->getFileSystem(p); + if (fs != NULL) + return fs->remove(p); + return false; +} + +bool FileSystemRedirect::rename(const Path& a,const Path& b) +{ + Path na = _merge(a); + Path nb = _merge(b); + FileSystemRef fsa = mMFS->getFileSystem(na); + FileSystemRef fsb = mMFS->getFileSystem(nb); + if (fsa.getPointer() == fsb.getPointer()) + return fsa->rename(na,nb); + return false; +} + +Path FileSystemRedirect::mapTo(const Path& path) +{ + Path p = _merge(path); + FileSystemRef fs = mMFS->getFileSystem(p); + if (fs != NULL) + return fs->mapTo(p); + return NULL; +} + +Path FileSystemRedirect::mapFrom(const Path& path) +{ + Path p = _merge(path); + FileSystemRef fs = mMFS->getFileSystem(p); + if (fs != NULL) + return fs->mapFrom(p); + return NULL; +} + +//----------------------------------------------------------------------------- + +void MountSystem::_log(const String& msg) +{ + String newMsg = "MountSystem: " + msg; + Con::warnf("%s", newMsg.c_str()); +} + +FileSystemRef MountSystem::_removeMountFromList(String root) +{ + for (Vector::iterator itr = mMountList.begin(); itr != mMountList.end(); itr++) + { + if (root.equal( itr->root, String::NoCase )) + { + FileSystemRef fs = itr->fileSystem; + mMountList.erase(itr); + return fs; + } + } + return NULL; +} + +FileSystemRef MountSystem::_getFileSystemFromList(const Path& path) const +{ + for (Vector::const_iterator itr = mMountList.begin(); itr != mMountList.end(); itr++) + { + if (itr->root.equal( path.getRoot(), String::NoCase )) + return itr->fileSystem; + } + + return NULL; +} + + +Path MountSystem::_normalize(const Path& path) +{ + Path po = path; + + // Assign to cwd root if none is specified. + if( po.getRoot().isEmpty() ) + po.setRoot( mCWD.getRoot() ); + + // Merge in current working directory if the path is relative to + // the current cwd. + if( po.getRoot().equal( mCWD.getRoot(), String::NoCase ) && po.isRelative() ) + { + po.setPath( Path::CompressPath( Path::Join( mCWD.getPath(),'/',po.getPath() ) ) ); + } + return po; +} + +FileRef MountSystem::createFile(const Path& path) +{ + Path np = _normalize(path); + FileSystemRef fs = _getFileSystemFromList(np); + + if (fs && fs->isReadOnly()) + { + _log(String::ToString("Cannot create file %s, filesystem is read-only", path.getFullPath().c_str())); + return NULL; + } + + if (fs != NULL) + return static_cast(fs->create(np,FileNode::File).getPointer()); + return NULL; +} + +DirectoryRef MountSystem::createDirectory(const Path& path, FileSystemRef fs) +{ + Path np = _normalize(path); + if (fs.isNull()) + fs = _getFileSystemFromList(np); + + if (fs && fs->isReadOnly()) + { + _log(String::ToString("Cannot create directory %s, filesystem is read-only", path.getFullPath().c_str())); + return NULL; + } + + if (fs != NULL) + return static_cast(fs->create(np,FileNode::Directory).getPointer()); + return NULL; +} + +FileRef MountSystem::openFile(const Path& path,File::AccessMode mode) +{ + FileNodeRef node = getFileNode(path); + if (node != NULL) + { + FileRef file = dynamic_cast(node.getPointer()); + if (file != NULL) + { + if (file->open(mode)) + return file; + else + return NULL; + } + } + else + { + if (mode != File::Read) + { + FileRef file = createFile(path); + + if (file != NULL) + { + file->open(mode); + return file; + } + } + } + return NULL; +} + +DirectoryRef MountSystem::openDirectory(const Path& path) +{ + FileNodeRef node = getFileNode(path); + + if (node != NULL) + { + DirectoryRef dir = dynamic_cast(node.getPointer()); + if (dir != NULL) + { + dir->open(); + return dir; + } + } + return NULL; +} + +bool MountSystem::remove(const Path& path) +{ + Path np = _normalize(path); + FileSystemRef fs = _getFileSystemFromList(np); + if (fs && fs->isReadOnly()) + { + _log(String::ToString("Cannot remove path %s, filesystem is read-only", path.getFullPath().c_str())); + return false; + } + if (fs != NULL) + return fs->remove(np); + return false; +} + +bool MountSystem::rename(const Path& from,const Path& to) +{ + // Will only rename files on the same filesystem + Path pa = _normalize(from); + Path pb = _normalize(to); + FileSystemRef fsa = _getFileSystemFromList(pa); + FileSystemRef fsb = _getFileSystemFromList(pb); + if (!fsa || !fsb) + return false; + if (fsa.getPointer() != fsb.getPointer()) + { + _log(String::ToString("Cannot rename path %s to a different filesystem", from.getFullPath().c_str())); + return false; + } + if (fsa->isReadOnly() || fsb->isReadOnly()) + { + _log(String::ToString("Cannot rename path %s; source or target filesystem is read-only", from.getFullPath().c_str())); + return false; + } + + return fsa->rename(pa,pb); +} + +bool MountSystem::mount(String root,FileSystemRef fs) +{ + MountFS mount; + mount.root = root; + mount.path = "/"; + mount.fileSystem = fs; + mMountList.push_back(mount); + return true; +} + +bool MountSystem::mount(String root,const Path &path) +{ + return mount(root,new FileSystemRedirect(this,_normalize(path))); +} + +FileSystemRef MountSystem::unmount(String root) +{ + FileSystemRef first = _removeMountFromList(root); + + // remove remaining FSes on this root + while (!_removeMountFromList(root).isNull()) + ; + + return first; +} + +bool MountSystem::unmount(FileSystemRef fs) +{ + if (fs.isNull()) + return false; + + // iterate back to front in case FS is in list multiple times. + // also check that fs is not null each time since its a strong ref + // so it could be nulled during removal. + bool unmounted = false; + for (int i = mMountList.size() - 1; !fs.isNull() && i >= 0; --i) + { + if (mMountList[i].fileSystem.getPointer() == fs.getPointer()) + { + mMountList.erase(i); + unmounted = true; + } + } + return unmounted; +} + +bool MountSystem::setCwd(const Path& file) +{ + if (file.getPath().isEmpty()) + return false; + mCWD.setRoot(file.getRoot()); + mCWD.setPath(file.getPath()); + return true; +} + +const Path& MountSystem::getCwd() const +{ + return mCWD; +} + +FileSystemRef MountSystem::getFileSystem(const Path& path) +{ + return _getFileSystemFromList(_normalize(path)); +} + +bool MountSystem::getFileAttributes(const Path& path,FileNode::Attributes* attr) +{ + FileNodeRef file = getFileNode(path); + + if (file != NULL) + { + bool result = file->getAttributes(attr); + return result; + } + + return false; +} + +FileNodeRef MountSystem::getFileNode(const Path& path) +{ + Path np = _normalize(path); + FileSystemRef fs = _getFileSystemFromList(np); + if (fs != NULL) + return fs->resolve(np); + return NULL; +} + +bool MountSystem::mapFSPath( const String &inRoot, const Path &inPath, Path &outPath ) +{ + FileSystemRef fs = _getFileSystemFromList(inRoot); + + if ( fs == NULL ) + { + outPath = Path(); + return false; + } + + outPath = fs->mapFrom( inPath ); + + return outPath.getFullPath() != String(); +} + +S32 MountSystem::findByPattern( const Path &inBasePath, const String &inFilePattern, bool inRecursive, Vector &outList, bool includeDirs/* =false */, bool multiMatch /* = true */ ) +{ + if (mFindByPatternOverrideFS.isNull() && !inBasePath.isDirectory() ) + return -1; + + DirectoryRef dir = NULL; + if (mFindByPatternOverrideFS.isNull()) + // open directory using standard mount system search + dir = openDirectory( inBasePath ); + else + { + // use specified filesystem to open directory + FileNodeRef fNode = mFindByPatternOverrideFS->resolve(inBasePath); + if (fNode && (dir = dynamic_cast(fNode.getPointer())) != NULL) + dir->open(); + } + + if ( dir == NULL ) + return -1; + + if (includeDirs) + { + // prepend cheesy "DIR:" annotation for directories + outList.push_back(String("DIR:") + inBasePath.getPath()); + } + + FileNode::Attributes attrs; + + Vector recurseDirs; + + while ( dir->read( &attrs ) ) + { + // skip hidden files + if ( attrs.name.c_str()[0] == '.' ) + continue; + + String name( attrs.name ); + + if ( (attrs.flags & FileNode::Directory) && inRecursive ) + { + name += '/'; + String path = Path::Join( inBasePath, '/', name ); + recurseDirs.push_back( path ); + } + + if ( !multiMatch && FindMatch::isMatch( inFilePattern, attrs.name, false ) ) + { + String path = Path::Join( inBasePath, '/', name ); + outList.push_back( path ); + } + + if ( multiMatch && FindMatch::isMatchMultipleExprs( inFilePattern, attrs.name, false ) ) + { + String path = Path::Join( inBasePath, '/', name ); + outList.push_back( path ); + } + } + + dir->close(); + + for ( S32 i = 0; i < recurseDirs.size(); i++ ) + findByPattern( recurseDirs[i], inFilePattern, true, outList, includeDirs, multiMatch ); + + return outList.size(); +} + +bool MountSystem::isFile(const Path& path) +{ + FileNode::Attributes attr; + if (getFileAttributes(path,&attr)) + return attr.flags & FileNode::File; + return false; +} + +bool MountSystem::isDirectory(const Path& path, FileSystemRef fsRef) +{ + FileNode::Attributes attr; + + if (fsRef.isNull()) + { + if (getFileAttributes(path,&attr)) + return attr.flags & FileNode::Directory; + return false; + } + else + { + FileNodeRef fnRef = fsRef->resolve(path); + if (fnRef.isNull()) + return false; + + FileNode::Attributes attr; + if (fnRef->getAttributes(&attr)) + return attr.flags & FileNode::Directory; + return false; + } +} + +bool MountSystem::isReadOnly(const Path& path) +{ + // first check to see if filesystem is read only + FileSystemRef fs = getFileSystem(path); + if ( fs.isNull() ) + // no filesystem owns this file...oh well, return false + return false; + if (fs->isReadOnly()) + return true; + + // check the file attributes, note that if the file does not exist, + // this function returns false. that should be ok since we know + // the file system is writable at this point. + FileNode::Attributes attr; + if (getFileAttributes(path,&attr)) + return attr.flags & FileNode::ReadOnly; + return false; +} + +void MountSystem::startFileChangeNotifications() +{ + for ( U32 i = 0; i < mMountList.size(); i++ ) + { + FileSystemChangeNotifier *notifier = mMountList[i].fileSystem->getChangeNotifier(); + + if ( notifier != NULL && !notifier->isNotifying() ) + notifier->startNotifier(); + } +} + +void MountSystem::stopFileChangeNotifications() +{ + for ( U32 i = 0; i < mMountList.size(); i++ ) + { + FileSystemChangeNotifier *notifier = mMountList[i].fileSystem->getChangeNotifier(); + + if ( notifier != NULL && notifier->isNotifying() ) + notifier->stopNotifier(); + } +} + +bool MountSystem::createPath(const Path& path) +{ + if (path.getPath().isEmpty()) + return true; + + // See if the pathectory exists + Path dir; + dir.setRoot(path.getRoot()); + dir.setPath(path.getPath()); + + // in a virtual mount system, isDirectory may return true if the directory exists in a read only FS, + // but the directory may not exist on a writeable filesystem that is also mounted. + // So get the target filesystem that will + // be used for the full writable path and and make sure the directory exists on it. + FileSystemRef fsRef = getFileSystem(path); + + if (isDirectory(dir,fsRef)) + return true; + + // Start from the top and work our way down + Path sub; + dir.setPath(path.isAbsolute()? String("/"): String()); + for (U32 i = 0; i < path.getDirectoryCount(); i++) + { + sub.setPath(path.getDirectory(i)); + dir.appendPath(sub); + if (!isDirectory(dir,fsRef)) + { + if (!createDirectory(dir,fsRef)) + return false; + } + } + return true; +} + + +//----------------------------------------------------------------------------- + +// Default global mount system +#ifndef TORQUE_DISABLE_VIRTUAL_MOUNT_SYSTEM +// Note that the Platform::FS::MountZips() must be called in platformVolume.cpp for zip support to work. +static VirtualMountSystem sgMountSystem; +#else +static MountSystem sgMountSystem; +#endif + +namespace FS +{ + +FileRef CreateFile(const Path &path) +{ + return sgMountSystem.createFile(path); +} + +DirectoryRef CreateDirectory(const Path &path) +{ + return sgMountSystem.createDirectory(path); +} + +FileRef OpenFile(const Path &path, File::AccessMode mode) +{ + return sgMountSystem.openFile(path,mode); +} + +bool ReadFile(const Path &inPath, void *&outData, U32 &outSize, bool inNullTerminate ) +{ + FileRef fileR = OpenFile( inPath, File::Read ); + + outData = NULL; + outSize = 0; + + // We'll get a NULL file reference if + // the file failed to open. + if ( fileR == NULL ) + return false; + + outSize = fileR->getSize(); + + // Its not a failure to read an empty + // file... but we can exit early. + if ( outSize == 0 ) + return true; + + U32 sizeRead = 0; + + if ( inNullTerminate ) + { + outData = new char [outSize+1]; + sizeRead = fileR->read(outData, outSize); + static_cast(outData)[outSize] = '\0'; + } + else + { + outData = new char [outSize]; + sizeRead = fileR->read(outData, outSize); + } + + if ( sizeRead != outSize ) + { + delete static_cast(outData); + outData = NULL; + outSize = 0; + return false; + } + + return true; +} + +DirectoryRef OpenDirectory(const Path &path) +{ + return sgMountSystem.openDirectory(path); +} + +bool Remove(const Path &path) +{ + return sgMountSystem.remove(path); +} + +bool Rename(const Path &from, const Path &to) +{ + return sgMountSystem.rename(from,to); +} + +bool Mount(String root, FileSystemRef fs) +{ + return sgMountSystem.mount(root,fs); +} + +bool Mount(String root, const Path &path) +{ + return sgMountSystem.mount(root,path); +} + +FileSystemRef Unmount(String root) +{ + return sgMountSystem.unmount(root); +} + +bool Unmount(FileSystemRef fs) +{ + return sgMountSystem.unmount(fs); +} + +bool SetCwd(const Path &file) +{ + return sgMountSystem.setCwd(file); +} + +const Path& GetCwd() +{ + return sgMountSystem.getCwd(); +} + +FileSystemRef GetFileSystem(const Path &path) +{ + return sgMountSystem.getFileSystem(path); +} + +bool GetFileAttributes(const Path &path, FileNode::Attributes* attr) +{ + return sgMountSystem.getFileAttributes(path,attr); +} + +S32 CompareModifiedTimes(const Path& p1, const Path& p2) +{ + FileNode::Attributes a1, a2; + if (!Torque::FS::GetFileAttributes(p1, &a1)) + return -1; + if (!Torque::FS::GetFileAttributes(p2, &a2)) + return -1; + if (a1.mtime < a2.mtime) + return -1; + if (a1.mtime == a2.mtime) + return 0; + return 1; +} + +FileNodeRef GetFileNode(const Path &path) +{ + return sgMountSystem.getFileNode(path); +} + +bool MapFSPath( const String &inRoot, const Path &inPath, Path &outPath ) +{ + return sgMountSystem.mapFSPath( inRoot, inPath, outPath ); +} + +bool GetFSPath( const Path &inPath, Path &outPath ) +{ + FileSystemRef sys = GetFileSystem( inPath ); + if ( sys ) + { + outPath = sys->mapTo( inPath ); + return true; + } + + return false; +} + +S32 FindByPattern( const Path &inBasePath, const String &inFilePattern, bool inRecursive, Vector &outList, bool multiMatch ) +{ + return sgMountSystem.findByPattern(inBasePath, inFilePattern, inRecursive, outList, false, multiMatch); +} + +bool IsFile(const Path &path) +{ + return sgMountSystem.isFile(path); +} + +bool IsDirectory(const Path &path) +{ + return sgMountSystem.isDirectory(path); +} + +bool IsReadOnly(const Path &path) +{ + return sgMountSystem.isReadOnly(path); +} + +String MakeUniquePath( const char *path, const char *fileName, const char *ext ) +{ + Path filePath; + + filePath.setPath( path ); + filePath.setFileName( fileName ); + filePath.setExtension( ext ); + + // First get an upper bound on the range of filenames to search. This lets us + // quickly skip past a large number of existing filenames. + // Note: upper limit of 2^31 added to handle the degenerate case of a folder + // with files named using the powers of 2, but plenty of space in between! + U32 high = 1; + while ( IsFile( filePath ) && ( high < 0x80000000 ) ) + { + high = high * 2; + filePath.setFileName( String::ToString( "%s%d", fileName, high ) ); + } + + // Now perform binary search for first filename in the range that doesn't exist + // Note that the returned name will not be strictly numerically *first* if the + // existing filenames are non-sequential (eg. 4,6,7), but it will still be unique. + if ( high > 1 ) + { + U32 low = high / 2; + while ( high - low > 1 ) + { + U32 probe = low + ( high - low ) / 2; + filePath.setFileName( String::ToString( "%s%d", fileName, probe ) ); + if ( IsFile( filePath ) ) + low = probe; + else + high = probe; + } + + // The 'high' index is guaranteed not to exist + filePath.setFileName( String::ToString( "%s%d", fileName, high ) ); + } + + return filePath.getFullPath(); +} + +void StartFileChangeNotifications() { sgMountSystem.startFileChangeNotifications(); } +void StopFileChangeNotifications() { sgMountSystem.stopFileChangeNotifications(); } + +S32 GetNumMounts() { return sgMountSystem.getNumMounts(); } +String GetMountRoot( S32 index ) { return sgMountSystem.getMountRoot(index); } +String GetMountPath( S32 index ) { return sgMountSystem.getMountPath(index); } +String GetMountType( S32 index ) { return sgMountSystem.getMountType(index); } + +bool CreatePath(const Path& path) +{ + return sgMountSystem.createPath(path); +} + +} // Namespace FS + +} // Namespace Torque + + diff --git a/Engine/source/core/volume.h b/Engine/source/core/volume.h new file mode 100644 index 000000000..db0d0e5e8 --- /dev/null +++ b/Engine/source/core/volume.h @@ -0,0 +1,577 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _VOLUME_H_ +#define _VOLUME_H_ + +#ifndef _TSIGNAL_H_ +#include "core/util/tSignal.h" +#endif + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +#ifndef _REFBASE_H_ +#include "core/util/refBase.h" +#endif + +#ifndef _TDICTIONARY_H_ +#include "core/util/tDictionary.h" +#endif + +#ifndef _TORQUE_LIST_ +#include "core/util/tList.h" +#endif + +#ifndef _PATH_H_ +#include "core/util/path.h" +#endif + +#ifndef _TIMECLASS_H_ +#include "core/util/timeClass.h" +#endif + +namespace Torque +{ +namespace FS +{ + +///@defgroup VolumeSystem Volume System +/// Volume access. + +//----------------------------------------------------------------------------- + +/// Base class for all FileIO objects. +/// @ingroup VolumeSystem +class FileBase : public StrongRefBase +{ +public: + virtual ~FileBase() {} +}; + + +//----------------------------------------------------------------------------- + +/// Base class for objects in a FileSystem. +/// This class provides the functionality required by all file system +/// files, which is basically name and attributes. +/// @ingroup VolumeSystem +class FileNode : public FileBase +{ +public: + enum Status + { + Open, ///< In an open state + Closed, ///< In a closed state + EndOfFile, ///< End of file reached + UnknownError, ///< An undetermined error has occurred + FileSystemFull, ///< File system full + NoSuchFile, ///< File or path does not exist + AccessDenied, ///< File access denied + IllegalCall, ///< An unsupported operation was used. + SharingViolation, ///< File being used by another process + NoDisk, ///< No disk or dvd in drive + DriveOpen, ///< Disk or DVD drive open + WrongDisk, ///< Disk or DVD has been swapped + }; + + enum Mode + { + File = 1 << 0, ///< Normal file + Directory = 1 << 1, ///< Directory + System = 1 << 2, ///< OS specific system file + Hidden = 1 << 3, ///< Hidden file or directory + ReadOnly = 1 << 4, ///< Read only + Compressed = 1 << 5, ///< Part of a compressed archive? + Encrypted = 1 << 6, ///< Part of an encrypted archive? + Library = 1 << 7, ///< Dynamic Library + Executable = 1 << 8, ///< Executable file + }; + + struct Attributes + { + U32 flags; ///< File/Directory modes + String name; ///< File/Directory name + Time mtime; ///< Last modified time + Time atime; ///< Last access time + U64 size; + }; + +public: + FileNode(); + + // Properties + virtual Path getName() const = 0; + virtual Status getStatus() const = 0; + + virtual bool getAttributes(Attributes*) = 0; + + // Convenience routines - may be overridden for optimal access + virtual Time getModifiedTime(); ///< @note This will return Time() on failure + virtual U64 getSize(); ///< @note This will return 0 on failure + virtual U32 getChecksum(); ///< @note This will return 0 on failure + +protected: + virtual U32 calculateChecksum() = 0; ///< return 0 on failure + +private: + U32 mChecksum; + Torque::Time mLastChecksum; +}; + +typedef WeakRefPtr FileNodePtr; +typedef StrongRefPtr FileNodeRef; + + +//----------------------------------------------------------------------------- + +/// File object in a FileSystem. +/// File object in a FileSystem. When a file is initially obtained from a +/// FileSystem it is in a closed state. +/// @ingroup VolumeSystem +class File : public FileNode +{ +public: + enum AccessMode + { + Read = 0, ///< Open for read only. + Write = 1, ///< Open for write only. + ReadWrite = 2, ///< Open for read-write. + WriteAppend = 3 ///< Write-only, starting at end of file. + }; + + enum SeekMode + { + Begin, ///< Relative to the start of the file + Current, ///< Relative to the current position + End, ///< Relative to the end of the file + }; + + File(); + virtual ~File(); + + // Properties + virtual U32 getPosition() = 0; + virtual U32 setPosition(U32 pos, SeekMode mode) = 0; + + // Functions + virtual bool open(AccessMode mode) = 0; + virtual bool close() = 0; + + virtual U32 read(void* dst, U32 size) = 0; + virtual U32 write(const void* src, U32 size) = 0; +}; + +typedef WeakRefPtr FilePtr; +typedef StrongRefPtr FileRef; + + +//----------------------------------------------------------------------------- + +/// Directory in a FileSystem. +/// Directory object in a FileSystem. When a directory is initially obtained from a +/// FileSystem it is in a closed state. +/// @ingroup VolumeSystem +class Directory : public FileNode +{ +public: + Directory(); + virtual ~Directory(); + + // Functions + virtual bool open() = 0; + virtual bool close() = 0; + virtual bool read(Attributes*) = 0; +}; + +typedef WeakRefPtr DirectoryPtr; +typedef StrongRefPtr DirectoryRef; + + +//----------------------------------------------------------------------------- + +class FileSystem; + +class FileSystemChangeNotifier +{ +public: + typedef Delegate ChangeDelegate; + typedef Signal ChangeSignal; + +public: + FileSystemChangeNotifier( FileSystem *fs ) + : mFS( fs ), + mNotifying( false ) + { + } + + virtual ~FileSystemChangeNotifier() {} + + /// Adds a file change notification. + /// @see FS::AddChangeNotification + virtual bool addNotification( const Path &path, ChangeDelegate callback ); + + /// Removes an existing file change notification. + /// @see FS::RemoveChangeNotification + virtual bool removeNotification( const Path &path, ChangeDelegate callback ); + + void startNotifier(); + void stopNotifier(); + + /// Returns true if the notifier is enabled and file + /// change notifications will be sent. + bool isNotifying() const { return mNotifying; } + +protected: + struct FileInfo + { + /// The full path to the file. + Path filePath; + + /// The last known modification time. + Time savedLastModTime; + + /// The notifications and reference count. + ChangeSignal signal; + }; + + typedef List FileInfoList; + typedef Map DirMap; ///< map a directory to a list of files and their mod times + + void process(); + + virtual void internalProcessOnce() = 0; + + /// This is called so the inherited class can do its own bookkeeping on addNotification() + /// @note We pass the directory here, not the file + virtual bool internalAddNotification( const Path &dir ) = 0; + + /// This is called so the inherited class can do its own bookkeeping on removeNotification() + /// @note We pass the directory here, not the file + virtual bool internalRemoveNotification( const Path &dir ) = 0; + + /// Called by the inherited class to let us know a directory has changed + /// so we can find the file which changed and notify on it + void internalNotifyDirChanged( const Path &dir ); + + /// Makes sure paths going in and out of the notifier will have the same format + String cleanPath(const Path& dir); + + FileSystem *mFS; + + DirMap mDirMap; + + bool mNotifying; +}; + +//----------------------------------------------------------------------------- + +/// Collection of FileNode objects. +/// File systems represent collections of FileNode objects. Functions are +/// provided for manipulating FileNode objects but the internal organization +/// and representation is opaque. +/// Path names must be fully specified relative to the file system root and +/// names cannot contain relative path information. +/// @ingroup VolumeSystem +class FileSystem : public FileBase +{ +public: + FileSystem(); + virtual ~FileSystem(); + + virtual String getTypeStr() const = 0; ///< Used for describing the file system type + + virtual FileNodeRef resolve(const Path& path) = 0; + virtual FileNodeRef create(const Path& path,FileNode::Mode) = 0; + virtual bool remove(const Path& path) = 0; + virtual bool rename(const Path& a,const Path& b) = 0; + virtual Path mapTo(const Path& path) = 0; + virtual Path mapFrom(const Path& path) = 0; + + /// Returns the file change notifier. + /// @see FS::AddChangeNotification + /// @see FS::RemoveChangeNotification + FileSystemChangeNotifier *getChangeNotifier() { return mChangeNotifier; } + + bool isReadOnly() { return mReadOnly; } + +protected: + FileSystemChangeNotifier *mChangeNotifier; + bool mReadOnly; +}; + +typedef WeakRefPtr FileSystemPtr; +typedef StrongRefPtr FileSystemRef; + + +//----------------------------------------------------------------------------- +///@name File System Access +/// A collection of file systems. +/// @ingroup VolumeSystem +class MountSystem +{ +public: + virtual ~MountSystem() {} + + FileRef createFile(const Path& path); + DirectoryRef createDirectory(const Path& path, FileSystemRef fs = NULL); + virtual bool createPath(const Path& path); + + FileRef openFile(const Path& path,File::AccessMode mode); + DirectoryRef openDirectory(const Path& path); + + bool remove(const Path& path); + + bool rename(const Path& from,const Path& to); + + virtual bool mount(String root, FileSystemRef fs); + virtual bool mount(String root, const Path &path); + virtual FileSystemRef unmount(String root); + virtual bool unmount(FileSystemRef fs); + + bool setCwd(const Path& file); + const Path &getCwd() const; + + FileSystemRef getFileSystem(const Path& path); + bool getFileAttributes(const Path& path,FileNode::Attributes* attr); + FileNodeRef getFileNode(const Path& path); + + bool mapFSPath( const String &inRoot, const Path &inPath, Path &outPath ); + + virtual S32 findByPattern( const Path &inBasePath, const String &inFilePattern, bool inRecursive, Vector &outList, bool includeDirs=false, bool multiMatch = true ); + + bool isFile(const Path &path); + bool isDirectory(const Path &path, FileSystemRef fsRef = NULL); + bool isReadOnly(const Path &path); + + S32 getNumMounts() const { return mMountList.size(); } + String getMountRoot( S32 index ) const { return mMountList[index].root; } + String getMountPath( S32 index ) const { return mMountList[index].fileSystem->mapTo(mMountList[index].path); } + String getMountType( S32 index ) const { return mMountList[index].fileSystem->getTypeStr(); } + + // File system notifications + void startFileChangeNotifications(); + void stopFileChangeNotifications(); + +protected: + virtual void _log(const String& msg); + +protected: + struct MountFS + { + String root; // Root for file system + String path; // File system path + FileSystemRef fileSystem; + }; + + virtual FileSystemRef _removeMountFromList(String root); + virtual FileSystemRef _getFileSystemFromList(const Path& path) const ; + void _setFindByPatternOverrideFS(FileSystemRef fs) { mFindByPatternOverrideFS = fs; } + + Path _normalize(const Path& path); + + Vector mMountList; + Path mCWD; + FileSystemRef mFindByPatternOverrideFS; +}; + +///@name File System Access +/// Functions for mounting file systems and dealing with files and directories. +/// The kernel provides FileSystem mounting, the concept of a current working +/// directory as well as relative paths. +///@{ + +/// Mount file system +///@ingroup VolumeSystem +bool Mount(String root, FileSystemRef fs); + +/// Mount file system redirect +///@ingroup VolumeSystem +bool Mount(String root, const Path &path); + +/// Remove mounted file system. +/// The file system object associated with the given root is unmounted. +/// Open files associated with this file system are unaffected. +///@return The unmounted file system. +///@ingroup VolumeSystem +FileSystemRef Unmount(String root); + +/// Remove mounted file system. +/// Open files associated with this file system are unaffected. +///@return true if the filesystem was successfully unmounted, false otherwise (most likely, the FS was not mounted) +bool Unmount(FileSystemRef fs); + +/// Find the the file system which owns the given file. +///@ingroup VolumeSystem +FileSystemRef GetFileSystem(const Path &file); + +/// Find the file system node for the given file. +///@return Null if the file doesn't exist +///@ingroup VolumeSystem +FileNodeRef GetFileNode(const Path &path); + +/// Adds a file change notification callback. +///@ingroup VolumeSystem +template +inline bool AddChangeNotification( const Path &path, T obj, U func ) +{ + FileSystemRef fs = GetFileSystem( path ); + if ( !fs || !fs->getChangeNotifier() ) + return false; + + FileSystemChangeNotifier::ChangeDelegate dlg( obj, func ); + return fs->getChangeNotifier()->addNotification( path, dlg ); +} + +/// Removes an existing file change notification callback. +///@ingroup VolumeSystem +template +inline bool RemoveChangeNotification( const Path &path, T obj, U func ) +{ + FileSystemRef fs = GetFileSystem( path ); + if ( !fs || !fs->getChangeNotifier() ) + return false; + + FileSystemChangeNotifier::ChangeDelegate dlg( obj, func ); + return fs->getChangeNotifier()->removeNotification( path, dlg ); +} + +/// Map a real file system path to a virtual one based on a root. +/// This is useful when we get a real path back from an OS file dialog for example. +/// e.g. If we have a root "gumby" which points at "C:/foo/bar", +/// MapFSPath("gumby", "C:/foo/bar/blat/picture.png", path ); +/// will map "C:/foo/bar/blat/picture.png" to "gumby:/blat/picture.png" +///@param inRoot The root to check +///@param inPath The real file system path +///@param outPath The resulting volume system path +///@return Success or failure +bool MapFSPath( const String &inRoot, const Path &inPath, Path &outPath ); + +/// Returns a true file system path without virtual mounts. +/// +///@param inPath The path to convert. +///@param outPath The resulting real file system path. +/// +bool GetFSPath( const Path &inPath, Path &outPath ); + +/// Find files matching a pattern starting in a given dir. +///@param inBasePath path to start in +///@param inFilePattern the file pattern [it uses the FindMatch class] +///@param inRecursive do we search recursively? +///@param outList the list of files as Strings [Paths are more expensive to compute, so these may be converted on demand] +///@param multiMatch match against multiple file patterns given in inFilePattern? +///@return number of files which matched +///@ingroup VolumeSystem +S32 FindByPattern( const Path &inBasePath, const String &inFilePattern, bool inRecursive, Vector &outList, bool multiMatch = false ); + +/// Set current working directory. +///@ingroup VolumeSystem +bool SetCwd(const Path &file); + +/// Get the current working directory. +///@ingroup VolumeSystem +const Path& GetCwd(); + +/// Remove (or delete) a file from the file system. +///@ingroup VolumeSystem +bool Remove(const Path &file); + +/// Rename a file or directory. +///@ingroup VolumeSystem +bool Rename(const Path &from, const Path &to); + +/// Get the file attributes. +/// @return success +///@ingroup VolumeSystem +bool GetFileAttributes(const Path &path, FileNode::Attributes *attr); + +/// Compare modified times of p1 & p2 +/// @return -1 if p1 < p2, 0 if p1 == p2, 1 if p1 > p2 +S32 CompareModifiedTimes(const Path& p1, const Path& p2); + +/// Open a file. +/// If the file exists a file object will be returned even if the +/// open operation fails. +///@return Null if the file does not exist +///@ingroup VolumeSystem +FileRef OpenFile(const Path &file, File::AccessMode mode); + +/// Read in an entire file +/// @note Caller is responsible for freeing memory +///@param inPath the file +///@param outData the pointer to return the data +///@param outSize the size of the data returned +///@param inNullTerminate add an extra '\0' byte to the return buffer +///@return successful read? If not, outData will be NULL and outSize will be 0 +bool ReadFile(const Path &inPath, void *&outData, U32 &outSize, bool inNullTerminate = false ); + +/// Open a directory. +/// If the directory exists a directory object will be returned even if the +/// open operation fails. +///@return Null if the file does not exist +///@ingroup VolumeSystem +DirectoryRef OpenDirectory(const Path &file); + +/// Create a file. +/// The file object is returned in a closed state. +///@ingroup VolumeSystem +FileRef CreateFile(const Path &file); + +/// Create a directory. +/// The directory object is returned in a closed state. +///@ingroup VolumeSystem +DirectoryRef CreateDirectory(const Path &file); + +/// Create all the directories in the path if they don't already exist +///@ingroup VolumeSystem +bool CreatePath(const Path &path); + +bool IsReadOnly(const Path &path); +bool IsDirectory(const Path &path); +bool IsFile(const Path &path); +bool VerifyWriteAccess(const Path &path); + +/// This returns a unique file path from the components +/// by appending numbers to the end of the file name if +/// a file with the same name already exists. +/// +/// @param path The root and directory for the file. +/// @param fileName The file name without extension. +/// @param ext The dot-less extension. +String MakeUniquePath( const char *path, const char *fileName, const char *ext ); + +void StartFileChangeNotifications(); +void StopFileChangeNotifications(); + +S32 GetNumMounts(); +String GetMountRoot( S32 index ); +String GetMountPath( S32 index ); +String GetMountType( S32 index ); + +///@} + +} // Namespace FS +} // Namespace Torque + +#endif + diff --git a/Engine/source/environment/basicClouds.cpp b/Engine/source/environment/basicClouds.cpp new file mode 100644 index 000000000..ac2f459a7 --- /dev/null +++ b/Engine/source/environment/basicClouds.cpp @@ -0,0 +1,409 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "platform/profiler.h" +#include "console/consoleTypes.h" +#include "basicClouds.h" + +#include "gfx/gfxTransformSaver.h" +#include "core/stream/fileStream.h" +#include "core/stream/bitStream.h" +#include "scene/sceneRenderState.h" +#include "renderInstance/renderPassManager.h" +#include "materials/shaderData.h" +#include "math/mathIO.h" + + +ConsoleDocClass( BasicClouds, + "@brief Renders up to three layers of scrolling cloud-cover textures overhead.\n\n" + + "%BasicClouds always renders overhead, following the camera. It is intended " + "as part of the background of your level, rendering in front of Sky/Sun " + "type objects and behind everything else.\n\n" + + "The parameters controlling the rendering of each texture are refered to " + "and grouped as 'layers'. They are rendered in sequential order, so, layer 1 " + "obscures layer 0, and so on.\n\n" + + "BasicClouds is not affected by scene lighting and is therefore not appropriate " + "for scenes in which lighting radically changes, such as day/night.\n\n" + + "@ingroup Atmosphere" +); + + +U32 BasicClouds::smVertStride = 50; +U32 BasicClouds::smStrideMinusOne = 49; +U32 BasicClouds::smVertCount = 50 * 50; +U32 BasicClouds::smTriangleCount = smStrideMinusOne * smStrideMinusOne * 2; + +BasicClouds::BasicClouds() +{ + mTypeMask |= EnvironmentObjectType | StaticObjectType; + mNetFlags.set(Ghostable | ScopeAlways); + + mLayerEnabled[0] = true; + mLayerEnabled[1] = true; + mLayerEnabled[2] = true; + + // Default textures are assigned by the ObjectBuilderGui. + //mTexName[0] = "art/skies/clouds/cloud1"; + //mTexName[1] = "art/skies/clouds/cloud2"; + //mTexName[2] = "art/skies/clouds/cloud3"; + + mHeight[0] = 4.0f; + mHeight[1] = 3.0f; + mHeight[2] = 2.0f; + + mTexSpeed[0] = 0.0005f; + mTexSpeed[1] = 0.001f; + mTexSpeed[2] = 0.0003f; + + mTexScale[0] = 1.0; + mTexScale[1] = 1.0; + mTexScale[2] = 1.0; + + mTexDirection[0].set( 1.0f, 0.0f ); + mTexDirection[1].set( 1.0f, 0.0f ); + mTexDirection[2].set( 1.0f, 0.0f ); + + mTexOffset[0].set( 0.5f, 0.5f ); + mTexOffset[1].set( 0.5f, 0.5f ); + mTexOffset[2].set( 0.5f, 0.5f ); +} + +IMPLEMENT_CO_NETOBJECT_V1( BasicClouds ); + +// ConsoleObject... + + +bool BasicClouds::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + setGlobalBounds(); + resetWorldBox(); + + addToScene(); + + if ( isClientObject() ) + { + _initTexture(); + _initBuffers(); + + // Find ShaderData + ShaderData *shaderData; + mShader = Sim::findObject( "BasicCloudsShader", shaderData ) ? shaderData->getShader() : NULL; + if ( !mShader ) + { + Con::errorf( "BasicClouds::onAdd - could not find BasicCloudsShader" ); + return false; + } + + // Create ShaderConstBuffer and Handles + mShaderConsts = mShader->allocConstBuffer(); + mModelViewProjSC = mShader->getShaderConstHandle( "$modelView" ); + mTimeSC = mShader->getShaderConstHandle( "$accumTime" ); + mTexScaleSC = mShader->getShaderConstHandle( "$texScale" ); + mTexDirectionSC = mShader->getShaderConstHandle( "$texDirection" ); + mTexOffsetSC = mShader->getShaderConstHandle( "$texOffset" ); + + // Create StateBlocks + GFXStateBlockDesc desc; + desc.setCullMode( GFXCullNone ); + desc.setBlend( true ); + desc.setZReadWrite( false, false ); + desc.samplersDefined = true; + desc.samplers[0].addressModeU = GFXAddressWrap; + desc.samplers[0].addressModeV = GFXAddressWrap; + desc.samplers[0].addressModeW = GFXAddressWrap; + desc.samplers[0].magFilter = GFXTextureFilterLinear; + desc.samplers[0].minFilter = GFXTextureFilterLinear; + desc.samplers[0].mipFilter = GFXTextureFilterLinear; + desc.samplers[0].textureColorOp = GFXTOPModulate; + + mStateblock = GFX->createStateBlock( desc ); + } + + return true; +} + +void BasicClouds::onRemove() +{ + removeFromScene(); + + Parent::onRemove(); +} + +void BasicClouds::initPersistFields() +{ + addGroup( "BasicClouds" ); + + addArray( "Layers", TEX_COUNT ); + + addField( "layerEnabled", TypeBool, Offset( mLayerEnabled, BasicClouds ), TEX_COUNT, + "Enable or disable rendering of this layer." ); + + addField( "texture", TypeImageFilename, Offset( mTexName, BasicClouds ), TEX_COUNT, + "Texture for this layer." ); + + addField( "texScale", TypeF32, Offset( mTexScale, BasicClouds ), TEX_COUNT, + "Texture repeat for this layer." ); + + addField( "texDirection", TypePoint2F, Offset( mTexDirection, BasicClouds ), TEX_COUNT, + "Texture scroll direction for this layer, relative to the world axis." ); + + addField( "texSpeed", TypeF32, Offset( mTexSpeed, BasicClouds ), TEX_COUNT, + "Texture scroll speed for this layer." ); + + addField( "texOffset", TypePoint2F, Offset( mTexOffset, BasicClouds ), TEX_COUNT, + "UV offset for this layer." ); + + addField( "height", TypeF32, Offset( mHeight, BasicClouds ), TEX_COUNT, + "Abstract number which controls the curvature and height of the dome mesh" ); + + endArray( "Layers" ); + + endGroup( "BasicClouds" ); + + Parent::initPersistFields(); +} + +void BasicClouds::inspectPostApply() +{ + Parent::inspectPostApply(); + setMaskBits( BasicCloudsMask ); +} + + +// NetObject... + + +U32 BasicClouds::packUpdate( NetConnection *conn, U32 mask, BitStream *stream ) +{ + U32 retMask = Parent::packUpdate( conn, mask, stream ); + + for ( U32 i = 0; i < TEX_COUNT; i++ ) + { + stream->writeFlag( mLayerEnabled[i] ); + + stream->write( mTexName[i] ); + + stream->write( mTexScale[i] ); + mathWrite( *stream, mTexDirection[i] ); + stream->write( mTexSpeed[i] ); + mathWrite( *stream, mTexOffset[i] ); + + stream->write( mHeight[i] ); + } + + return retMask; +} + +void BasicClouds::unpackUpdate( NetConnection *conn, BitStream *stream ) +{ + Parent::unpackUpdate( conn, stream ); + + for ( U32 i = 0; i < TEX_COUNT; i++ ) + { + mLayerEnabled[i] = stream->readFlag(); + + stream->read( &mTexName[i] ); + + stream->read( &mTexScale[i] ); + mathRead( *stream, &mTexDirection[i] ); + stream->read( &mTexSpeed[i] ); + mathRead( *stream, &mTexOffset[i] ); + + stream->read( &mHeight[i] ); + } + + if ( isProperlyAdded() ) + { + // We could check if the height or texture have actually changed. + _initBuffers(); + _initTexture(); + } +} + + +// SceneObject... + + +void BasicClouds::prepRenderImage( SceneRenderState *state ) +{ + PROFILE_SCOPE( BasicClouds_prepRenderImage ); + + bool isEnabled = false; + for ( U32 i = 0; i < TEX_COUNT; i++ ) + { + if ( mLayerEnabled[i] ) + { + isEnabled = true; + break; + } + } + + if ( !isEnabled ) + return; + + // This should be sufficient for most objects that don't manage zones, and + // don't need to return a specialized RenderImage... + ObjectRenderInst *ri = state->getRenderPass()->allocInst< ObjectRenderInst >(); + ri->renderDelegate.bind( this, &BasicClouds::renderObject ); + ri->type = RenderPassManager::RIT_Sky; + ri->defaultKey = 0; + ri->defaultKey2 = 0; + state->getRenderPass()->addInst( ri ); +} + +void BasicClouds::renderObject( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *mi ) +{ + GFXTransformSaver saver; + + Point3F camPos = state->getCameraPosition(); + MatrixF xfm(true); + xfm.setPosition(camPos); + GFX->multWorld(xfm); + + if ( state->isReflectPass() ) + GFX->setProjectionMatrix( state->getSceneManager()->getNonClipProjection() ); + + GFX->setShader( mShader ); + GFX->setShaderConstBuffer( mShaderConsts ); + GFX->setStateBlock( mStateblock ); + + MatrixF xform(GFX->getProjectionMatrix()); + xform *= GFX->getViewMatrix(); + xform *= GFX->getWorldMatrix(); + + mShaderConsts->setSafe( mModelViewProjSC, xform ); + mShaderConsts->setSafe( mTimeSC, (F32)Sim::getCurrentTime() / 1000.0f ); + GFX->setPrimitiveBuffer( mPB ); + + for ( U32 i = 0; i < TEX_COUNT; i++ ) + { + if ( !mLayerEnabled[i] ) + continue; + + mShaderConsts->setSafe( mTexScaleSC, mTexScale[i] ); + mShaderConsts->setSafe( mTexDirectionSC, mTexDirection[i] * mTexSpeed[i] ); + mShaderConsts->setSafe( mTexOffsetSC, mTexOffset[i] ); + + GFX->setTexture( 0, mTexture[i] ); + GFX->setVertexBuffer( mVB[i] ); + + GFX->drawIndexedPrimitive( GFXTriangleList, 0, 0, smVertCount, 0, smTriangleCount ); + } +} + + +// BasicClouds Internal Methods.... + + +void BasicClouds::_initTexture() +{ + for ( U32 i = 0; i < TEX_COUNT; i++ ) + { + if ( !mLayerEnabled[i] ) + { + mTexture[i] = NULL; + continue; + } + + if ( mTexName[i].isNotEmpty() ) + mTexture[i].set( mTexName[i], &GFXDefaultStaticDiffuseProfile, "BasicClouds" ); + + if ( mTexture[i].isNull() ) + mTexture[i].set( "core/art/warnmat", &GFXDefaultStaticDiffuseProfile, "BasicClouds" ); + } +} + +void BasicClouds::_initBuffers() +{ + // Primitive Buffer... Is shared for all Layers. + + mPB.set( GFX, smTriangleCount * 3, smTriangleCount, GFXBufferTypeStatic ); + + U16 *pIdx = NULL; + mPB.lock(&pIdx); + U32 curIdx = 0; + + for ( U32 y = 0; y < smStrideMinusOne; y++ ) + { + for ( U32 x = 0; x < smStrideMinusOne; x++ ) + { + U32 offset = x + y * smVertStride; + + pIdx[curIdx] = offset; + curIdx++; + pIdx[curIdx] = offset + 1; + curIdx++; + pIdx[curIdx] = offset + smVertStride + 1; + curIdx++; + + pIdx[curIdx] = offset; + curIdx++; + pIdx[curIdx] = offset + smVertStride + 1; + curIdx++; + pIdx[curIdx] = offset + smVertStride; + curIdx++; + } + } + + mPB.unlock(); + + // Vertex Buffer... + // Each layer has their own so they can be at different heights. + + for ( U32 i = 0; i < TEX_COUNT; i++ ) + { + Point3F vertScale( 16.0f, 16.0f, mHeight[i] ); + F32 zOffset = -( mCos( mSqrt( 1.0f ) ) + 0.01f ); + + mVB[i].set( GFX, smVertCount, GFXBufferTypeStatic ); + GFXVertexPT *pVert = mVB[i].lock(); + + for ( U32 y = 0; y < smVertStride; y++ ) + { + F32 v = ( (F32)y / (F32)smStrideMinusOne - 0.5f ) * 2.0f; + + for ( U32 x = 0; x < smVertStride; x++ ) + { + F32 u = ( (F32)x / (F32)smStrideMinusOne - 0.5f ) * 2.0f; + + F32 sx = u; + F32 sy = v; + F32 sz = mCos( mSqrt( sx*sx + sy*sy ) ) + zOffset; + + pVert->point.set( sx, sy, sz ); + pVert->point *= vertScale; + pVert->texCoord.set( u, v ); + pVert++; + } + } + + mVB[i].unlock(); + } +} \ No newline at end of file diff --git a/Engine/source/environment/basicClouds.h b/Engine/source/environment/basicClouds.h new file mode 100644 index 000000000..997ef57b8 --- /dev/null +++ b/Engine/source/environment/basicClouds.h @@ -0,0 +1,122 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _BASICCLOUDS_H_ +#define _BASICCLOUDS_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _GFXTEXTUREHANDLE_H_ +#include "gfx/gfxTextureHandle.h" +#endif +#ifndef _GFXVERTEXBUFFER_H_ +#include "gfx/gfxVertexBuffer.h" +#endif +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif +#ifndef _GFXSTATEBLOCK_H_ +#include "gfx/gfxStateBlock.h" +#endif +#ifndef _GFXSHADER_H_ +#include "gfx/gfxShader.h" +#endif + +class BaseMatInstance; + + +class BasicClouds : public SceneObject +{ + typedef SceneObject Parent; + + enum + { + BasicCloudsMask = Parent::NextFreeMask, + NextFreeMask = Parent::NextFreeMask << 1, + }; + + #define TEX_COUNT 3 + +public: + + BasicClouds(); + virtual ~BasicClouds() {} + + DECLARE_CONOBJECT( BasicClouds ); + + // ConsoleObject + virtual bool onAdd(); + virtual void onRemove(); + static void initPersistFields(); + virtual void inspectPostApply(); + + // NetObject + virtual U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream ); + virtual void unpackUpdate( NetConnection *conn, BitStream *stream ); + + // SceneObject + virtual void prepRenderImage( SceneRenderState *state ); + void renderObject( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *mi ); + +protected: + + void _initTexture(); + void _initBuffers(); + void _initBuffer( F32 height, GFXVertexBufferHandle *vb, GFXPrimitiveBufferHandle *pb ); + +protected: + + static U32 smVertStride; + static U32 smStrideMinusOne; + static U32 smVertCount; + static U32 smTriangleCount; + + GFXTexHandle mTexture[TEX_COUNT]; + + GFXStateBlockRef mStateblock; + + GFXShaderRef mShader; + + GFXShaderConstBufferRef mShaderConsts; + GFXShaderConstHandle *mTimeSC; + GFXShaderConstHandle *mModelViewProjSC; + GFXShaderConstHandle *mTexScaleSC; + GFXShaderConstHandle *mTexDirectionSC; + GFXShaderConstHandle *mTexOffsetSC; + + GFXVertexBufferHandle mVB[TEX_COUNT]; + GFXPrimitiveBufferHandle mPB; + + // Fields... + + bool mLayerEnabled[TEX_COUNT]; + String mTexName[TEX_COUNT]; + F32 mTexScale[TEX_COUNT]; + Point2F mTexDirection[TEX_COUNT]; + F32 mTexSpeed[TEX_COUNT]; + Point2F mTexOffset[TEX_COUNT]; + F32 mHeight[TEX_COUNT]; +}; + + +#endif // _BASICCLOUDS_H_ \ No newline at end of file diff --git a/Engine/source/environment/cloudLayer.cpp b/Engine/source/environment/cloudLayer.cpp new file mode 100644 index 000000000..7e424bf81 --- /dev/null +++ b/Engine/source/environment/cloudLayer.cpp @@ -0,0 +1,489 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "platform/profiler.h" +#include "console/consoleTypes.h" +#include "cloudLayer.h" + +#include "gfx/gfxTransformSaver.h" +#include "core/stream/fileStream.h" +#include "core/stream/bitStream.h" +#include "scene/sceneRenderState.h" +#include "renderInstance/renderPassManager.h" +#include "gfx/primBuilder.h" +#include "materials/materialManager.h" +#include "materials/customMaterialDefinition.h" +#include "materials/shaderData.h" +#include "lighting/lightInfo.h" +#include "math/mathIO.h" + +ConsoleDocClass( CloudLayer, + "@brief A layer of clouds which change shape over time and are affected by scene lighting.\n\n" + + "%CloudLayer always renders overhead, following the camera. It is intended " + "as part of the background of your level, rendering in front of Sky/Sun " + "type objects and behind everything else.\n\n" + + "The illusion of clouds forming and changing over time is controlled by the " + "normal/opacity texture and the three sets of texture animation parameters. " + "The texture is sampled three times. The first sample defines overall cloud " + "density, where clouds are likely to form and their general size and shape. " + "The second two samples control how it changes over time; they are " + "combined and used as modifiers to the first sample.\n\n" + + "%CloudLayer is affected by scene lighting and is designed to be used in " + "scenes with dynamic lighting or time of day changes.\n\n" + + "@ingroup Atmosphere" +); + +GFXImplementVertexFormat( GFXCloudVertex ) +{ + addElement( "POSITION", GFXDeclType_Float3 ); + addElement( "NORMAL", GFXDeclType_Float3 ); + addElement( "BINORMAL", GFXDeclType_Float3 ); + addElement( "TANGENT", GFXDeclType_Float3 ); + addElement( "TEXCOORD", GFXDeclType_Float2, 0 ); +} + +U32 CloudLayer::smVertStride = 50; +U32 CloudLayer::smStrideMinusOne = smVertStride - 1; +U32 CloudLayer::smVertCount = smVertStride * smVertStride; +U32 CloudLayer::smTriangleCount = smStrideMinusOne * smStrideMinusOne * 2; + +CloudLayer::CloudLayer() +: mBaseColor( 0.9f, 0.9f, 0.9f, 1.0f ), + mCoverage( 0.5f ), + mExposure( 1.0f ), + mWindSpeed( 1.0f ), + mLastTime( 0 ) +{ + mTypeMask |= EnvironmentObjectType | StaticObjectType; + mNetFlags.set(Ghostable | ScopeAlways); + + mTexScale[0] = 1.0; + mTexScale[1] = 1.0; + mTexScale[2] = 1.0; + + mTexDirection[0].set( 1.0f, 0.0f ); + mTexDirection[1].set( 0.0f, 1.0f ); + mTexDirection[2].set( 0.5f, 0.0f ); + + mTexSpeed[0] = 0.005f; + mTexSpeed[1] = 0.005f; + mTexSpeed[2] = 0.005f; + + mTexOffset[0] = mTexOffset[1] = mTexOffset[2] = Point2F::Zero; + + mHeight = 4.0f; +} + +IMPLEMENT_CO_NETOBJECT_V1( CloudLayer ); + +// ConsoleObject... + + +bool CloudLayer::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + setGlobalBounds(); + resetWorldBox(); + + addToScene(); + + if ( isClientObject() ) + { + _initTexture(); + _initBuffers(); + + // Find ShaderData + ShaderData *shaderData; + mShader = Sim::findObject( "CloudLayerShader", shaderData ) ? + shaderData->getShader() : NULL; + if ( !mShader ) + { + Con::errorf( "CloudLayer::onAdd - could not find CloudLayerShader" ); + return false; + } + + // Create ShaderConstBuffer and Handles + mShaderConsts = mShader->allocConstBuffer(); + mModelViewProjSC = mShader->getShaderConstHandle( "$modelView" ); + mEyePosWorldSC = mShader->getShaderConstHandle( "$eyePosWorld" ); + mSunVecSC = mShader->getShaderConstHandle( "$sunVec" ); + mTexOffsetSC[0] = mShader->getShaderConstHandle( "$texOffset0" ); + mTexOffsetSC[1] = mShader->getShaderConstHandle( "$texOffset1" ); + mTexOffsetSC[2] = mShader->getShaderConstHandle( "$texOffset2" ); + mTexScaleSC = mShader->getShaderConstHandle( "$texScale" ); + mAmbientColorSC = mShader->getShaderConstHandle( "$ambientColor" ); + mSunColorSC = mShader->getShaderConstHandle( "$sunColor" ); + mCoverageSC = mShader->getShaderConstHandle( "$cloudCoverage" ); + mExposureSC = mShader->getShaderConstHandle( "$cloudExposure" ); + mBaseColorSC = mShader->getShaderConstHandle( "$cloudBaseColor" ); + + // Create StateBlocks + GFXStateBlockDesc desc; + desc.setCullMode( GFXCullNone ); + desc.setBlend( true ); + desc.setZReadWrite( false, false ); + desc.samplersDefined = true; + desc.samplers[0].addressModeU = GFXAddressWrap; + desc.samplers[0].addressModeV = GFXAddressWrap; + desc.samplers[0].addressModeW = GFXAddressWrap; + desc.samplers[0].magFilter = GFXTextureFilterLinear; + desc.samplers[0].minFilter = GFXTextureFilterLinear; + desc.samplers[0].mipFilter = GFXTextureFilterLinear; + desc.samplers[0].textureColorOp = GFXTOPModulate; + + mStateblock = GFX->createStateBlock( desc ); + } + + return true; +} + +void CloudLayer::onRemove() +{ + removeFromScene(); + + Parent::onRemove(); +} + +void CloudLayer::initPersistFields() +{ + addGroup( "CloudLayer" ); + + addField( "texture", TypeImageFilename, Offset( mTextureName, CloudLayer ), + "An RGBA texture which should contain normals and opacity (density)." ); + + addArray( "Textures", TEX_COUNT ); + + addField( "texScale", TypeF32, Offset( mTexScale, CloudLayer ), TEX_COUNT, + "Controls the texture repeat of this slot." ); + + addField( "texDirection", TypePoint2F, Offset( mTexDirection, CloudLayer ), TEX_COUNT, + "Controls the direction this slot scrolls." ); + + addField( "texSpeed", TypeF32, Offset( mTexSpeed, CloudLayer ), TEX_COUNT, + "Controls the speed this slot scrolls." ); + + endArray( "Textures" ); + + addField( "baseColor", TypeColorF, Offset( mBaseColor, CloudLayer ), + "Base cloud color before lighting." ); + + addField( "exposure", TypeF32, Offset( mExposure, CloudLayer ), + "Brightness scale so CloudLayer can be overblown if desired." ); + + addField( "coverage", TypeF32, Offset( mCoverage, CloudLayer ), + "Fraction of sky covered by clouds 0-1." ); + + addField( "windSpeed", TypeF32, Offset( mWindSpeed, CloudLayer ), + "Overall scalar to texture scroll speed." ); + + addField( "height", TypeF32, Offset( mHeight, CloudLayer ), + "Abstract number which controls the curvature and height of the dome mesh." ); + + endGroup( "CloudLayer" ); + + Parent::initPersistFields(); +} + +void CloudLayer::inspectPostApply() +{ + Parent::inspectPostApply(); + setMaskBits( CloudLayerMask ); +} + + +// NetObject... + + +U32 CloudLayer::packUpdate( NetConnection *conn, U32 mask, BitStream *stream ) +{ + U32 retMask = Parent::packUpdate( conn, mask, stream ); + + stream->write( mTextureName ); + + for ( U32 i = 0; i < TEX_COUNT; i++ ) + { + stream->write( mTexScale[i] ); + stream->write( mTexSpeed[i] ); + mathWrite( *stream, mTexDirection[i] ); + } + + stream->write( mBaseColor ); + stream->write( mCoverage ); + stream->write( mExposure ); + stream->write( mWindSpeed ); + stream->write( mHeight ); + + return retMask; +} + +void CloudLayer::unpackUpdate( NetConnection *conn, BitStream *stream ) +{ + Parent::unpackUpdate( conn, stream ); + + String oldTextureName = mTextureName; + stream->read( &mTextureName ); + + for ( U32 i = 0; i < TEX_COUNT; i++ ) + { + stream->read( &mTexScale[i] ); + stream->read( &mTexSpeed[i] ); + mathRead( *stream, &mTexDirection[i] ); + } + + stream->read( &mBaseColor ); + + F32 oldCoverage = mCoverage; + stream->read( &mCoverage ); + stream->read( &mExposure ); + + stream->read( &mWindSpeed ); + + F32 oldHeight = mHeight; + stream->read( &mHeight ); + + if ( isProperlyAdded() ) + { + if ( ( oldTextureName != mTextureName ) || ( ( oldCoverage == 0.0f ) != ( mCoverage == 0.0f ) ) ) + _initTexture(); + if ( oldHeight != mHeight ) + _initBuffers(); + } +} + + +// SceneObject... + + +void CloudLayer::prepRenderImage( SceneRenderState *state ) +{ + PROFILE_SCOPE( CloudLayer_prepRenderImage ); + + if ( mCoverage <= 0.0f ) + return; + + if ( state->isDiffusePass() ) + { + // Scroll textures... + + U32 time = Sim::getCurrentTime(); + F32 delta = (F32)( time - mLastTime ) / 1000.0f; + mLastTime = time; + + for ( U32 i = 0; i < 3; i++ ) + { + mTexOffset[i] += mTexDirection[i] * mTexSpeed[i] * delta * mWindSpeed; + } + } + + // This should be sufficient for most objects that don't manage zones, and + // don't need to return a specialized RenderImage... + + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &CloudLayer::renderObject ); + ri->type = RenderPassManager::RIT_Sky; + ri->defaultKey = 0; + ri->defaultKey2 = 0; + state->getRenderPass()->addInst( ri ); +} + +void CloudLayer::renderObject( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *mi ) +{ + GFXTransformSaver saver; + + const Point3F &camPos = state->getCameraPosition(); + MatrixF xfm(true); + xfm.setPosition(camPos); + GFX->multWorld(xfm); + + if ( state->isReflectPass() ) + GFX->setProjectionMatrix( state->getSceneManager()->getNonClipProjection() ); + + GFX->setShader( mShader ); + GFX->setShaderConstBuffer( mShaderConsts ); + GFX->setStateBlock( mStateblock ); + + // Set all the shader consts... + + MatrixF xform(GFX->getProjectionMatrix()); + xform *= GFX->getViewMatrix(); + xform *= GFX->getWorldMatrix(); + + mShaderConsts->setSafe( mModelViewProjSC, xform ); + + mShaderConsts->setSafe( mEyePosWorldSC, camPos ); + + LightInfo *lightinfo = LIGHTMGR->getSpecialLight(LightManager::slSunLightType); + const ColorF &sunlight = state->getAmbientLightColor(); + + Point3F ambientColor( sunlight.red, sunlight.green, sunlight.blue ); + mShaderConsts->setSafe( mAmbientColorSC, ambientColor ); + + const ColorF &sunColor = lightinfo->getColor(); + Point3F data( sunColor.red, sunColor.green, sunColor.blue ); + mShaderConsts->setSafe( mSunColorSC, data ); + + mShaderConsts->setSafe( mSunVecSC, lightinfo->getDirection() ); + + for ( U32 i = 0; i < TEX_COUNT; i++ ) + mShaderConsts->setSafe( mTexOffsetSC[i], mTexOffset[i] ); + + Point3F scale( mTexScale[0], mTexScale[1], mTexScale[2] ); + mShaderConsts->setSafe( mTexScaleSC, scale ); + + Point3F color; + color.set( mBaseColor.red, mBaseColor.green, mBaseColor.blue ); + mShaderConsts->setSafe( mBaseColorSC, color ); + + mShaderConsts->setSafe( mCoverageSC, mCoverage ); + + mShaderConsts->setSafe( mExposureSC, mExposure ); + + GFX->setTexture( 0, mTexture ); + GFX->setVertexBuffer( mVB ); + GFX->setPrimitiveBuffer( mPB ); + + GFX->drawIndexedPrimitive( GFXTriangleList, 0, 0, smVertCount, 0, smTriangleCount ); +} + + +// CloudLayer Internal Methods.... + + +void CloudLayer::_initTexture() +{ + if ( mCoverage <= 0.0f ) + { + mTexture = NULL; + return; + } + + if ( mTextureName.isNotEmpty() ) + mTexture.set( mTextureName, &GFXDefaultStaticDiffuseProfile, "CloudLayer" ); + + if ( mTexture.isNull() ) + mTexture.set( "core/art/warnmat", &GFXDefaultStaticDiffuseProfile, "CloudLayer" ); +} + +void CloudLayer::_initBuffers() +{ + // Vertex Buffer... + + Point3F vertScale( 16.0f, 16.0f, mHeight ); + F32 zOffset = -( mCos( mSqrt( 1.0f ) ) + 0.01f ); + + mVB.set( GFX, smVertCount, GFXBufferTypeStatic ); + GFXCloudVertex *pVert = mVB.lock(); + + for ( U32 y = 0; y < smVertStride; y++ ) + { + F32 v = ( (F32)y / (F32)smStrideMinusOne - 0.5f ) * 2.0f; + + for ( U32 x = 0; x < smVertStride; x++ ) + { + F32 u = ( (F32)x / (F32)smStrideMinusOne - 0.5f ) * 2.0f; + + F32 sx = u; + F32 sy = v; + F32 sz = mCos( mSqrt( sx*sx + sy*sy ) ) + zOffset; + //F32 sz = 1.0f; + pVert->point.set( sx, sy, sz ); + pVert->point *= vertScale; + + // The vert to our right. + Point3F rpnt; + + F32 ru = ( (F32)( x + 1 ) / (F32)smStrideMinusOne - 0.5f ) * 2.0f; + F32 rv = v; + + rpnt.x = ru; + rpnt.y = rv; + rpnt.z = mCos( mSqrt( rpnt.x*rpnt.x + rpnt.y*rpnt.y ) ) + zOffset; + rpnt *= vertScale; + + // The vert to our front. + Point3F fpnt; + + F32 fu = u; + F32 fv = ( (F32)( y + 1 ) / (F32)smStrideMinusOne - 0.5f ) * 2.0f; + + fpnt.x = fu; + fpnt.y = fv; + fpnt.z = mCos( mSqrt( fpnt.x*fpnt.x + fpnt.y*fpnt.y ) ) + zOffset; + fpnt *= vertScale; + + Point3F fvec = fpnt - pVert->point; + fvec.normalize(); + + Point3F rvec = rpnt - pVert->point; + rvec.normalize(); + + pVert->normal = mCross( fvec, rvec ); + pVert->normal.normalize(); + pVert->binormal = fvec; + pVert->tangent = rvec; + pVert->texCoord.set( u, v ); + pVert++; + } + } + + mVB.unlock(); + + + // Primitive Buffer... + + mPB.set( GFX, smTriangleCount * 3, smTriangleCount, GFXBufferTypeStatic ); + + U16 *pIdx = NULL; + mPB.lock(&pIdx); + U32 curIdx = 0; + + for ( U32 y = 0; y < smStrideMinusOne; y++ ) + { + for ( U32 x = 0; x < smStrideMinusOne; x++ ) + { + U32 offset = x + y * smVertStride; + + pIdx[curIdx] = offset; + curIdx++; + pIdx[curIdx] = offset + 1; + curIdx++; + pIdx[curIdx] = offset + smVertStride + 1; + curIdx++; + + pIdx[curIdx] = offset; + curIdx++; + pIdx[curIdx] = offset + smVertStride + 1; + curIdx++; + pIdx[curIdx] = offset + smVertStride; + curIdx++; + } + } + + mPB.unlock(); +} \ No newline at end of file diff --git a/Engine/source/environment/cloudLayer.h b/Engine/source/environment/cloudLayer.h new file mode 100644 index 000000000..10e9f20af --- /dev/null +++ b/Engine/source/environment/cloudLayer.h @@ -0,0 +1,135 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _CLOUDLAYER_H_ +#define _CLOUDLAYER_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _GFXTEXTUREHANDLE_H_ +#include "gfx/gfxTextureHandle.h" +#endif +#ifndef _GFXVERTEXBUFFER_H_ +#include "gfx/gfxVertexBuffer.h" +#endif +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif +#ifndef _MATINSTANCE_H_ +#include "materials/matInstance.h" +#endif + +GFXDeclareVertexFormat( GFXCloudVertex ) +{ + Point3F point; + Point3F normal; + Point3F binormal; + Point3F tangent; + Point2F texCoord; +}; + +class CloudLayer : public SceneObject +{ + typedef SceneObject Parent; + + enum + { + CloudLayerMask = Parent::NextFreeMask, + NextFreeMask = Parent::NextFreeMask << 1, + }; + + #define TEX_COUNT 3 + +public: + + CloudLayer(); + virtual ~CloudLayer() {} + + DECLARE_CONOBJECT( CloudLayer ); + + // ConsoleObject + virtual bool onAdd(); + virtual void onRemove(); + static void initPersistFields(); + virtual void inspectPostApply(); + + // NetObject + virtual U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream ); + virtual void unpackUpdate( NetConnection *conn, BitStream *stream ); + + // SceneObject + void prepRenderImage( SceneRenderState *state ); + void renderObject( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *mi ); + +protected: + + void _initTexture(); + void _initBuffers(); + +protected: + + static U32 smVertStride; + static U32 smStrideMinusOne; + static U32 smVertCount; + static U32 smTriangleCount; + + GFXTexHandle mTexture; + + GFXShaderRef mShader; + + GFXStateBlockRef mStateblock; + + GFXShaderConstBufferRef mShaderConsts; + GFXShaderConstHandle *mModelViewProjSC; + GFXShaderConstHandle *mAmbientColorSC; + GFXShaderConstHandle *mSunColorSC; + GFXShaderConstHandle *mSunVecSC; + GFXShaderConstHandle *mTexOffsetSC[3]; + GFXShaderConstHandle *mTexScaleSC; + GFXShaderConstHandle *mBaseColorSC; + GFXShaderConstHandle *mCoverageSC; + GFXShaderConstHandle *mExposureSC; + GFXShaderConstHandle *mEyePosWorldSC; + + GFXVertexBufferHandle mVB; + GFXPrimitiveBufferHandle mPB; + + Point2F mTexOffset[3]; + U32 mLastTime; + + // Fields... + + String mTextureName; + F32 mTexScale[TEX_COUNT]; + Point2F mTexDirection[TEX_COUNT]; + F32 mTexSpeed[TEX_COUNT]; + + ColorF mBaseColor; + F32 mExposure; + F32 mCoverage; + F32 mWindSpeed; + F32 mHeight; +}; + + +#endif // _CLOUDLAYER_H_ \ No newline at end of file diff --git a/Engine/source/environment/decalRoad.cpp b/Engine/source/environment/decalRoad.cpp new file mode 100644 index 000000000..822d13ef2 --- /dev/null +++ b/Engine/source/environment/decalRoad.cpp @@ -0,0 +1,1728 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "environment/decalRoad.h" + +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "util/catmullRom.h" +#include "math/util/quadTransforms.h" +#include "scene/sceneRenderState.h" +#include "scene/sceneManager.h" +#include "core/stream/bitStream.h" +#include "gfx/gfxDrawUtil.h" +#include "gfx/gfxTransformSaver.h" +#include "math/mathIO.h" +#include "math/mathUtils.h" +#include "terrain/terrData.h" +#include "materials/materialDefinition.h" +#include "materials/materialManager.h" +#include "materials/baseMatInstance.h" +#include "environment/nodeListManager.h" +#include "lighting/lightQuery.h" + + +extern F32 gDecalBias; +extern bool gEditingMission; + + +//----------------------------------------------------------------------------- +// DecalRoadNodeList Struct +//----------------------------------------------------------------------------- + +struct DecalRoadNodeList : public NodeListManager::NodeList +{ + Vector mPositions; + Vector mWidths; + + DecalRoadNodeList() { } + virtual ~DecalRoadNodeList() { } +}; + +//----------------------------------------------------------------------------- +// DecalRoadNodeEvent Class +//----------------------------------------------------------------------------- + +class DecalRoadNodeEvent : public NodeListEvent +{ + typedef NodeListEvent Parent; + +public: + Vector mPositions; + Vector mWidths; + +public: + DecalRoadNodeEvent() { mNodeList = NULL; } + virtual ~DecalRoadNodeEvent() { } + + virtual void pack(NetConnection*, BitStream*); + virtual void unpack(NetConnection*, BitStream*); + + virtual void copyIntoList(NodeListManager::NodeList* copyInto); + virtual void padListToSize(); + + DECLARE_CONOBJECT(DecalRoadNodeEvent); +}; + +void DecalRoadNodeEvent::pack(NetConnection* conn, BitStream* stream) +{ + Parent::pack( conn, stream ); + + stream->writeInt( mPositions.size(), 16 ); + + for (U32 i=0; iwrite( mWidths[i] ); + } +} + +void DecalRoadNodeEvent::unpack(NetConnection* conn, BitStream* stream) +{ + mNodeList = new DecalRoadNodeList(); + + Parent::unpack( conn, stream ); + + U32 count = stream->readInt( 16 ); + + Point3F pos; + F32 width; + + DecalRoadNodeList* list = static_cast(mNodeList); + + for (U32 i=0; iread( &width ); + + list->mPositions.push_back( pos ); + list->mWidths.push_back( width ); + } + + list->mTotalValidNodes = count; + + // Do we have a complete list? + if (list->mPositions.size() >= mTotalNodes) + list->mListComplete = true; +} + +void DecalRoadNodeEvent::copyIntoList(NodeListManager::NodeList* copyInto) +{ + DecalRoadNodeList* prevList = static_cast(copyInto); + DecalRoadNodeList* list = static_cast(mNodeList); + + // Merge our list with the old list. + for (U32 i=mLocalListStart, index=0; imPositions.size(); ++i, ++index) + { + prevList->mPositions[i] = list->mPositions[index]; + prevList->mWidths[i] = list->mWidths[index]; + } +} + +void DecalRoadNodeEvent::padListToSize() +{ + DecalRoadNodeList* list = static_cast(mNodeList); + + U32 totalValidNodes = list->mTotalValidNodes; + + // Pad our list front? + if (mLocalListStart) + { + DecalRoadNodeList* newlist = new DecalRoadNodeList(); + newlist->mPositions.increment(mLocalListStart); + newlist->mWidths.increment(mLocalListStart); + + newlist->mPositions.merge(list->mPositions); + newlist->mWidths.merge(list->mWidths); + + mNodeList = newlist; + delete list; + } + + // Pad our list end? + if (list->mPositions.size() < mTotalNodes) + { + U32 delta = mTotalNodes - list->mPositions.size(); + list->mPositions.increment(delta); + list->mWidths.increment(delta); + } + + list->mTotalValidNodes = totalValidNodes; +} + +IMPLEMENT_CO_NETEVENT_V1(DecalRoadNodeEvent); + +ConsoleDocClass( DecalRoadNodeEvent, + "@brief Sends messages to the Decal Road Editor\n\n" + "Editor use only.\n\n" + "@internal" +); +//----------------------------------------------------------------------------- +// DecalRoadNodeListNotify Class +//----------------------------------------------------------------------------- + +class DecalRoadNodeListNotify : public NodeListNotify +{ + typedef NodeListNotify Parent; + +protected: + SimObjectPtr mRoad; + +public: + DecalRoadNodeListNotify( DecalRoad* road, U32 listId ) { mRoad = road; mListId = listId; } + virtual ~DecalRoadNodeListNotify() { mRoad = NULL; } + + virtual void sendNotification( NodeListManager::NodeList* list ); +}; + +void DecalRoadNodeListNotify::sendNotification( NodeListManager::NodeList* list ) +{ + if (mRoad.isValid()) + { + // Build the road's nodes + DecalRoadNodeList* roadList = dynamic_cast( list ); + if (roadList) + mRoad->buildNodesFromList( roadList ); + } +} + +//----------------------------------------------------------------------------- +// DecalRoadUpdateEvent Class +//----------------------------------------------------------------------------- + +void DecalRoadUpdateEvent::process( SimObject *object ) +{ + DecalRoad *road = dynamic_cast( object ); + AssertFatal( road, "DecalRoadRegenEvent::process - wasn't a DecalRoad" ); + + // Inform clients to perform the update. + road->setMaskBits( mMask ); + + if ( !road->isProperlyAdded() ) + return; + + // Perform the server side update. + if ( mMask & DecalRoad::TerrainChangedMask ) + { + road->_generateEdges(); + } + if ( mMask & DecalRoad::GenEdgesMask ) + { + // Server has already done this. + //road->_generateEdges(); + } + if ( mMask & DecalRoad::ReClipMask ) + { + // Server does not need to capture verts. + road->_captureVerts(); + } +} + + +//------------------------------------------------------------------------------ +// Class: DecalRoad +//------------------------------------------------------------------------------ + +ConsoleDocClass( DecalRoad, + "@brief A strip shaped decal defined by spine nodes which clips against Terrain objects.\n\n" + + "DecalRoad is for representing a road or path ( or other inventive things ) across " + "a TerrainBlock. It renders as a decal and is therefore only for features that do " + "not need geometric depth.\n\n" + + "The Material assigned to DecalRoad should tile vertically.\n\n" + + "@ingroup Terrain" +); + +// Init Statics + +// Static ConsoleVars for toggling debug rendering +bool DecalRoad::smEditorOpen = false; +bool DecalRoad::smWireframe = true; +bool DecalRoad::smShowBatches = false; +bool DecalRoad::smDiscardAll = false; +bool DecalRoad::smShowSpline = true; +bool DecalRoad::smShowRoad = true; +S32 DecalRoad::smUpdateDelay = 500; + +SimObjectPtr DecalRoad::smServerDecalRoadSet = NULL; + + +// Constructors + +DecalRoad::DecalRoad() + : mLoadRenderData( true ), + mBreakAngle( 3.0f ), + mSegmentsPerBatch( 10 ), + mTextureLength( 5.0f ), + mRenderPriority( 10 ), + mMaterial( NULL ), + mMatInst( NULL ), + mUpdateEventId( -1 ), + mTerrainUpdateRect( Box3F::Invalid ) +{ + // Setup NetObject. + mTypeMask |= StaticObjectType | StaticShapeObjectType; + mNetFlags.set(Ghostable); +} + +DecalRoad::~DecalRoad() +{ +} + +IMPLEMENT_CO_NETOBJECT_V1(DecalRoad); + + +// ConsoleObject + +void DecalRoad::initPersistFields() +{ + addGroup( "DecalRoad" ); + + addField( "material", TypeMaterialName, Offset( mMaterialName, DecalRoad ), "Material used for rendering." ); + + addProtectedField( "textureLength", TypeF32, Offset( mTextureLength, DecalRoad ), &DecalRoad::ptSetTextureLength, &defaultProtectedGetFn, + "The length in meters of textures mapped to the DecalRoad" ); + + addProtectedField( "breakAngle", TypeF32, Offset( mBreakAngle, DecalRoad ), &DecalRoad::ptSetBreakAngle, &defaultProtectedGetFn, + "Angle in degrees - DecalRoad will subdivided the spline if its curve is greater than this threshold." ); + + addField( "renderPriority", TypeS32, Offset( mRenderPriority, DecalRoad ), + "DecalRoad(s) are rendered in descending renderPriority order." ); + + endGroup( "DecalRoad" ); + + addGroup( "Internal" ); + + addProtectedField( "node", TypeString, NULL, &addNodeFromField, &emptyStringProtectedGetFn, + "Do not modify, for internal use." ); + + endGroup( "Internal" ); + + Parent::initPersistFields(); +} + +void DecalRoad::consoleInit() +{ + Parent::consoleInit(); + + // Vars for debug rendering while the RoadEditor is open, only used if smEditorOpen is true. + Con::addVariable( "$DecalRoad::EditorOpen", TypeBool, &DecalRoad::smEditorOpen, "For use by the Decal Editor.\n\n" + "@ingroup Editors\n" ); + Con::addVariable( "$DecalRoad::wireframe", TypeBool, &DecalRoad::smWireframe, "For use by the Decal Editor.\n\n" + "@ingroup Editors\n" ); + Con::addVariable( "$DecalRoad::showBatches", TypeBool, &DecalRoad::smShowBatches, "For use by the Decal Editor.\n\n" + "@ingroup Editors\n" ); + Con::addVariable( "$DecalRoad::discardAll", TypeBool, &DecalRoad::smDiscardAll, "For use by the Decal Editor.\n\n" + "@ingroup Editors\n"); + Con::addVariable( "$DecalRoad::showSpline", TypeBool, &DecalRoad::smShowSpline, "For use by the Decal Editor.\n\n" + "@ingroup Editors\n" ); + Con::addVariable( "$DecalRoad::showRoad", TypeBool, &DecalRoad::smShowRoad, "For use by the Decal Editor.\n\n" + "@ingroup Editors\n" ); + Con::addVariable( "$DecalRoad::updateDelay", TypeS32, &DecalRoad::smUpdateDelay, "For use by the Decal Editor.\n\n" + "@ingroup Editors\n" ); +} + + +// SimObject + +bool DecalRoad::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + // DecalRoad is at position zero when created, + // it sets its own position to the first node inside + // _generateEdges but until it has at least one node + // it will be at 0,0,0. + + MatrixF mat(true); + Parent::setTransform( mat ); + + // The client side calculates bounds based on clipped geometry. It would + // be wasteful for the server to do this so the server uses global bounds. + if ( isServerObject() ) + { + setGlobalBounds(); + resetWorldBox(); + } + + // Set the Render Transform. + setRenderTransform(mObjToWorld); + + // Add to Scene. + addToScene(); + + if ( isServerObject() ) + getServerSet()->addObject( this ); + + // + TerrainBlock::smUpdateSignal.notify( this, &DecalRoad::_onTerrainChanged ); + + // + if ( isClientObject() ) + _initMaterial(); + + _generateEdges(); + _captureVerts(); + + return true; +} + +void DecalRoad::onRemove() +{ + SAFE_DELETE( mMatInst ); + + TerrainBlock::smUpdateSignal.remove( this, &DecalRoad::_onTerrainChanged ); + + removeFromScene(); + + Parent::onRemove(); +} + +void DecalRoad::inspectPostApply() +{ + Parent::inspectPostApply(); + + setMaskBits( DecalRoadMask ); +} + +void DecalRoad::onStaticModified( const char* slotName, const char*newValue ) +{ + Parent::onStaticModified( slotName, newValue ); + + /* + if ( isProperlyAdded() && + dStricmp( slotName, "material" ) == 0 ) + { + setMaskBits( DecalRoadMask ); + } + */ + + if ( dStricmp( slotName, "renderPriority" ) == 0 ) + { + mRenderPriority = getMax( dAtoi(newValue), (S32)1 ); + } +} + +SimSet* DecalRoad::getServerSet() +{ + if ( !smServerDecalRoadSet ) + { + smServerDecalRoadSet = new SimSet(); + smServerDecalRoadSet->registerObject( "ServerDecalRoadSet" ); + Sim::getRootGroup()->addObject( smServerDecalRoadSet ); + } + + return smServerDecalRoadSet; +} + +void DecalRoad::writeFields( Stream &stream, U32 tabStop ) +{ + Parent::writeFields( stream, tabStop ); + + // Now write all nodes + + stream.write(2, "\r\n"); + + for ( U32 i = 0; i < mNodes.size(); i++ ) + { + const RoadNode &node = mNodes[i]; + + stream.writeTabs(tabStop); + + char buffer[1024]; + dMemset( buffer, 0, 1024 ); + dSprintf( buffer, 1024, "Node = \"%f %f %f %f\";", node.point.x, node.point.y, node.point.z, node.width ); + stream.writeLine( (const U8*)buffer ); + } +} + +bool DecalRoad::writeField( StringTableEntry fieldname, const char *value ) +{ + if ( fieldname == StringTable->insert("node") ) + return false; + + return Parent::writeField( fieldname, value ); +} + +void DecalRoad::onEditorEnable() +{ +} + +void DecalRoad::onEditorDisable() +{ +} + + +// NetObject + +U32 DecalRoad::packUpdate(NetConnection * con, U32 mask, BitStream * stream) +{ + // Pack Parent. + U32 retMask = Parent::packUpdate(con, mask, stream); + + if ( stream->writeFlag( mask & DecalRoadMask ) ) + { + // Write Texture Name. + stream->write( mMaterialName ); + + stream->write( mBreakAngle ); + + stream->write( mSegmentsPerBatch ); + + stream->write( mTextureLength ); + + stream->write( mRenderPriority ); + } + + if ( stream->writeFlag( mask & NodeMask ) ) + { + //stream->writeInt( mNodes.size(), 16 ); + + //for ( U32 i = 0; i < mNodes.size(); i++ ) + //{ + // mathWrite( *stream, mNodes[i].point ); + // stream->write( mNodes[i].width ); + //} + + const U32 nodeByteSize = 16; // Based on sending all of a node's parameters + + // Test if we can fit all of our nodes within the current stream. + // We make sure we leave 100 bytes still free in the stream for whatever + // may follow us. + S32 allowedBytes = stream->getWriteByteSize() - 100; + if ( stream->writeFlag( (nodeByteSize * mNodes.size()) < allowedBytes ) ) + { + // All nodes should fit, so send them out now. + stream->writeInt( mNodes.size(), 16 ); + + for ( U32 i = 0; i < mNodes.size(); i++ ) + { + mathWrite( *stream, mNodes[i].point ); + stream->write( mNodes[i].width ); + } + } + else + { + // There isn't enough space left in the stream for all of the + // nodes. Batch them up into NetEvents. + U32 id = gServerNodeListManager->nextListId(); + U32 count = 0; + U32 index = 0; + while (count < mNodes.size()) + { + count += NodeListManager::smMaximumNodesPerEvent; + if (count > mNodes.size()) + { + count = mNodes.size(); + } + + DecalRoadNodeEvent* event = new DecalRoadNodeEvent(); + event->mId = id; + event->mTotalNodes = mNodes.size(); + event->mLocalListStart = index; + + for (; indexmPositions.push_back( mNodes[index].point ); + event->mWidths.push_back( mNodes[index].width ); + } + + con->postNetEvent( event ); + } + + stream->write( id ); + } + } + + stream->writeFlag( mask & GenEdgesMask ); + + stream->writeFlag( mask & ReClipMask ); + + stream->writeFlag( mask & TerrainChangedMask ); + + // Were done ... + return retMask; +} + +void DecalRoad::unpackUpdate( NetConnection *con, BitStream *stream ) +{ + // Unpack Parent. + Parent::unpackUpdate( con, stream ); + + // DecalRoadMask + if ( stream->readFlag() ) + { + String matName; + stream->read( &matName ); + + if ( matName != mMaterialName ) + { + mMaterialName = matName; + Material *pMat = NULL; + if ( !Sim::findObject( mMaterialName, pMat ) ) + { + Con::printf( "DecalRoad::unpackUpdate, failed to find Material of name %s!", mMaterialName.c_str() ); + } + else + { + mMaterial = pMat; + if ( isProperlyAdded() ) + _initMaterial(); + } + } + + stream->read( &mBreakAngle ); + + stream->read( &mSegmentsPerBatch ); + + stream->read( &mTextureLength ); + + stream->read( &mRenderPriority ); + } + + // NodeMask + if ( stream->readFlag() ) + { + //U32 count = stream->readInt( 16 ); + + //mNodes.clear(); + + //Point3F pos; + //F32 width; + //for ( U32 i = 0; i < count; i++ ) + //{ + // mathRead( *stream, &pos ); + // stream->read( &width ); + // _addNode( pos, width ); + //} + + if (stream->readFlag()) + { + // Nodes have been passed in this update + U32 count = stream->readInt( 16 ); + + mNodes.clear(); + + Point3F pos; + F32 width; + for ( U32 i = 0; i < count; i++ ) + { + mathRead( *stream, &pos ); + stream->read( &width ); + _addNode( pos, width ); + } + } + else + { + // Nodes will arrive as events + U32 id; + stream->read( &id ); + + // Check if the road's nodes made it here before we did. + NodeListManager::NodeList* list = NULL; + if ( gClientNodeListManager->findListById( id, &list, true) ) + { + // Work with the completed list + DecalRoadNodeList* roadList = dynamic_cast( list ); + if (roadList) + buildNodesFromList( roadList ); + + delete list; + } + else + { + // Nodes have not yet arrived, so register our interest in the list + DecalRoadNodeListNotify* notify = new DecalRoadNodeListNotify( this, id ); + gClientNodeListManager->registerNotification( notify ); + } + } + } + + // GenEdgesMask + if ( stream->readFlag() && isProperlyAdded() ) + _generateEdges(); + + // ReClipMask + if ( stream->readFlag() && isProperlyAdded() ) + _captureVerts(); + + // TerrainChangedMask + if ( stream->readFlag() ) + { + if ( isProperlyAdded() ) + { + if ( mTerrainUpdateRect.isOverlapped( getWorldBox() ) ) + { + _generateEdges(); + _captureVerts(); + // Clear out the mTerrainUpdateRect since we have updated its + // region and we now need to store future terrain changes + // in it. + mTerrainUpdateRect = Box3F::Invalid; + } + } + } +} + +void DecalRoad::prepRenderImage( SceneRenderState* state ) +{ + PROFILE_SCOPE( DecalRoad_prepRenderImage ); + + if ( mNodes.size() <= 1 || + mBatches.size() == 0 || + !mMatInst || + state->isShadowPass() ) + return; + + // If we don't have a material instance after the override then + // we can skip rendering all together. + BaseMatInstance *matInst = state->getOverrideMaterial( mMatInst ); + if ( !matInst ) + return; + + RenderPassManager *renderPass = state->getRenderPass(); + + // Debug RenderInstance + // Only when editor is open. + if ( smEditorOpen ) + { + ObjectRenderInst *ri = renderPass->allocInst(); + ri->type = RenderPassManager::RIT_Editor; + ri->renderDelegate.bind( this, &DecalRoad::_debugRender ); + state->getRenderPass()->addInst( ri ); + } + + // Normal Road RenderInstance + // Always rendered when the editor is not open + // otherwise obey the smShowRoad flag + if ( !smShowRoad && smEditorOpen ) + return; + + const Frustum &frustum = state->getFrustum(); + + MeshRenderInst coreRI; + coreRI.clear(); + coreRI.objectToWorld = &MatrixF::Identity; + coreRI.worldToCamera = renderPass->allocSharedXform(RenderPassManager::View); + + MatrixF *tempMat = renderPass->allocUniqueXform( MatrixF( true ) ); + MathUtils::getZBiasProjectionMatrix( gDecalBias, frustum, tempMat ); + coreRI.projection = tempMat; + + coreRI.type = RenderPassManager::RIT_Decal; + coreRI.vertBuff = &mVB; + coreRI.primBuff = &mPB; + coreRI.matInst = matInst; + + // Make it the sort distance the max distance so that + // it renders after all the other opaque geometry in + // the prepass bin. + coreRI.sortDistSq = F32_MAX; + + // If we need lights then set them up. + if ( matInst->isForwardLit() ) + { + LightQuery query; + query.init( getWorldSphere() ); + query.getLights( coreRI.lights, 8 ); + } + + U32 startBatchIdx = -1; + U32 endBatchIdx = 0; + + for ( U32 i = 0; i < mBatches.size(); i++ ) + { + const RoadBatch &batch = mBatches[i]; + const bool isVisible = !frustum.isCulled( batch.bounds ); + if ( isVisible ) + { + // If this is the start of a set of batches. + if ( startBatchIdx == -1 ) + endBatchIdx = startBatchIdx = i; + + // Else we're extending the end batch index. + else + ++endBatchIdx; + + // If this isn't the last batch then continue. + if ( i < mBatches.size()-1 ) + continue; + } + + // We we still don't have a start batch, so skip. + if ( startBatchIdx == -1 ) + continue; + + // Render this set of batches. + const RoadBatch &startBatch = mBatches[startBatchIdx]; + const RoadBatch &endBatch = mBatches[endBatchIdx]; + + U32 startVert = startBatch.startVert; + U32 startIdx = startBatch.startIndex; + U32 vertCount = endBatch.endVert - startVert; + U32 idxCount = ( endBatch.endIndex - startIdx ) + 1; + U32 triangleCount = idxCount / 3; + + AssertFatal( startVert + vertCount <= mVertCount, "DecalRoad, bad draw call!" ); + AssertFatal( startIdx + triangleCount < mTriangleCount * 3, "DecalRoad, bad draw call!" ); + + MeshRenderInst *ri = renderPass->allocInst(); + + *ri = coreRI; + + ri->prim = renderPass->allocPrim(); + ri->prim->type = GFXTriangleList; + ri->prim->minIndex = 0; + ri->prim->startIndex = startIdx; + ri->prim->numPrimitives = triangleCount; + ri->prim->startVertex = 0; + ri->prim->numVertices = endBatch.endVert + 1; + + // For sorting we first sort by render priority + // and then by objectId. + // + // Since a road can submit more than one render instance, we want all + // draw calls for a single road to occur consecutively, since they + // could use the same vertex buffer. + ri->defaultKey = mRenderPriority << 0 | mId << 16; + ri->defaultKey2 = 0; + + renderPass->addInst( ri ); + + // Reset the batching. + startBatchIdx = -1; + } +} + +void DecalRoad::setTransform( const MatrixF &mat ) +{ + // We ignore transform requests from the editor + // right now. +} + +void DecalRoad::setScale( const VectorF &scale ) +{ + // We ignore scale requests from the editor + // right now. +} + + +// DecalRoad Public Methods + +bool DecalRoad::getClosestNode( const Point3F &pos, U32 &idx ) +{ + F32 closestDist = F32_MAX; + + for ( U32 i = 0; i < mNodes.size(); i++ ) + { + F32 dist = ( mNodes[i].point - pos ).len(); + if ( dist < closestDist ) + { + closestDist = dist; + idx = i; + } + } + + return closestDist != F32_MAX; +} + +bool DecalRoad::containsPoint( const Point3F &worldPos, U32 *nodeIdx ) const +{ + // This is just for making selections in the editor, we use the + // client-side road because it has the proper edge's. + if ( isServerObject() && getClientObject() ) + return ((DecalRoad*)getClientObject())->containsPoint( worldPos, nodeIdx ); + + // If point isn't in the world box, + // it's definitely not in the road. + //if ( !getWorldBox().isContained( worldPos ) ) + // return false; + + if ( mEdges.size() < 2 ) + return false; + + Point2F testPt( worldPos.x, + worldPos.y ); + Point2F poly[4]; + + // Look through all edges, does the polygon + // formed from adjacent edge's contain the worldPos? + for ( U32 i = 0; i < mEdges.size() - 1; i++ ) + { + const RoadEdge &edge0 = mEdges[i]; + const RoadEdge &edge1 = mEdges[i+1]; + + poly[0].set( edge0.p0.x, edge0.p0.y ); + poly[1].set( edge0.p2.x, edge0.p2.y ); + poly[2].set( edge1.p2.x, edge1.p2.y ); + poly[3].set( edge1.p0.x, edge1.p0.y ); + + if ( MathUtils::pointInPolygon( poly, 4, testPt ) ) + { + if ( nodeIdx ) + *nodeIdx = edge0.parentNodeIdx; + + return true; + } + + } + + return false; +} + +bool DecalRoad::castray( const Point3F &start, const Point3F &end ) const +{ + // We just cast against the object box for the editor. + return mWorldBox.collideLine( start, end ); +} + +Point3F DecalRoad::getNodePosition( U32 idx ) +{ + if ( mNodes.size() - 1 < idx ) + return Point3F(); + + return mNodes[idx].point; +} + +void DecalRoad::setNodePosition( U32 idx, const Point3F &pos ) +{ + if ( mNodes.size() - 1 < idx ) + return; + + mNodes[idx].point = pos; + + _generateEdges(); + scheduleUpdate( GenEdgesMask | ReClipMask | NodeMask ); +} + +U32 DecalRoad::addNode( const Point3F &pos, F32 width ) +{ + U32 idx = _addNode( pos, width ); + + _generateEdges(); + scheduleUpdate( GenEdgesMask | ReClipMask | NodeMask ); + + return idx; +} + +U32 DecalRoad::insertNode(const Point3F &pos, const F32 &width, const U32 &idx) +{ + U32 ret = _insertNode( pos, width, idx ); + + _generateEdges(); + scheduleUpdate( GenEdgesMask | ReClipMask | NodeMask ); + + return ret; +} + +void DecalRoad::setNodeWidth( U32 idx, F32 width ) +{ + if ( mNodes.size() - 1 < idx ) + return; + + mNodes[idx].width = width; + + _generateEdges(); + scheduleUpdate( GenEdgesMask | ReClipMask | NodeMask ); +} + +F32 DecalRoad::getNodeWidth( U32 idx ) +{ + if ( mNodes.size() - 1 < idx ) + return -1.0f; + + return mNodes[idx].width; +} + +void DecalRoad::deleteNode( U32 idx ) +{ + if ( mNodes.size() - 1 < idx ) + return; + + mNodes.erase(idx); + + _generateEdges(); + scheduleUpdate( GenEdgesMask | ReClipMask | NodeMask ); +} + +void DecalRoad::buildNodesFromList( DecalRoadNodeList* list ) +{ + mNodes.clear(); + + for (U32 i=0; imPositions.size(); ++i) + { + _addNode( list->mPositions[i], list->mWidths[i] ); + } + + _generateEdges(); + _captureVerts(); +} + +void DecalRoad::setTextureLength( F32 meters ) +{ + meters = getMax( meters, 0.1f ); + if ( mTextureLength == meters ) + return; + + mTextureLength = meters; + + _generateEdges(); + scheduleUpdate( DecalRoadMask | ReClipMask ); +} + +void DecalRoad::setBreakAngle( F32 degrees ) +{ + //meters = getMax( meters, MIN_METERS_PER_SEGMENT ); + //if ( mBreakAngle == meters ) + // return; + + mBreakAngle = degrees; + + _generateEdges(); + scheduleUpdate( DecalRoadMask | GenEdgesMask | ReClipMask ); +} + +void DecalRoad::scheduleUpdate( U32 updateMask ) +{ + scheduleUpdate( updateMask, smUpdateDelay, true ); +} + +void DecalRoad::scheduleUpdate( U32 updateMask, U32 delayMs, bool restartTimer ) +{ + if ( Sim::isEventPending( mUpdateEventId ) ) + { + if ( !restartTimer ) + { + mLastEvent->mMask |= updateMask; + return; + } + else + { + Sim::cancelEvent( mUpdateEventId ); + } + } + + mLastEvent = new DecalRoadUpdateEvent( updateMask, delayMs ); + mUpdateEventId = Sim::postEvent( this, mLastEvent, Sim::getCurrentTime() + delayMs ); +} + +void DecalRoad::regenerate() +{ + _generateEdges(); + _captureVerts(); + setMaskBits( NodeMask | GenEdgesMask | ReClipMask ); +} + +bool DecalRoad::addNodeFromField( void *object, const char *index, const char *data ) +{ + DecalRoad *pObj = static_cast(object); + + F32 x,y,z,width; + U32 result = dSscanf( data, "%f %f %f %f", &x, &y, &z, &width ); + if ( result == 4 ) + pObj->_addNode( Point3F(x,y,z), width ); + + return false; +} + + +// Internal Helper Methods + +void DecalRoad::_initMaterial() +{ + SAFE_DELETE( mMatInst ); + + if ( mMaterial ) + mMatInst = mMaterial->createMatInstance(); + else + mMatInst = MATMGR->createMatInstance( "WarningMaterial" ); + + GFXStateBlockDesc desc; + desc.setZReadWrite( true, false ); + mMatInst->addStateBlockDesc( desc ); + + mMatInst->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat() ); +} + +void DecalRoad::_debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* ) +{ + //if ( mStateBlock.isNull() ) + // return; + + GFX->enterDebugEvent( ColorI( 255, 0, 0 ), "DecalRoad_debugRender" ); + GFXTransformSaver saver; + + //GFX->setStateBlock( mStateBlock ); + + Point3F size(1,1,1); + ColorI color( 255, 0, 0, 255 ); + + GFXStateBlockDesc desc; + desc.setZReadWrite( true, false ); + desc.setBlend( true ); + desc.fillMode = GFXFillWireframe; + + if ( smShowBatches ) + { + for ( U32 i = 0; i < mBatches.size(); i++ ) + { + const Box3F &box = mBatches[i].bounds; + GFX->getDrawUtil()->drawCube( desc, box, ColorI(255,100,100,255) ); + } + } + + //GFX->leaveDebugEvent(); +} + +void DecalRoad::_generateEdges() +{ + PROFILE_SCOPE( DecalRoad_generateEdges ); + + //Con::warnf( "%s - generateEdges", isServerObject() ? "server" : "client" ); + + if ( mNodes.size() > 0 ) + { + // Set our object position to the first node. + const Point3F &nodePt = mNodes.first().point; + MatrixF mat( true ); + mat.setPosition( nodePt ); + Parent::setTransform( mat ); + + // The server object has global bounds, which Parent::setTransform + // messes up so we must reset it. + if ( isServerObject() ) + { + mObjBox.minExtents.set(-1e10, -1e10, -1e10); + mObjBox.maxExtents.set( 1e10, 1e10, 1e10); + } + } + + + if ( mNodes.size() < 2 ) + return; + + // Ensure nodes are above the terrain height at their xy position + for ( U32 i = 0; i < mNodes.size(); i++ ) + { + _getTerrainHeight( mNodes[i].point ); + } + + // Now start generating edges... + + U32 nodeCount = mNodes.size(); + Point3F *positions = new Point3F[nodeCount]; + + for ( U32 i = 0; i < nodeCount; i++ ) + { + const RoadNode &node = mNodes[i]; + positions[i].set( node.point.x, node.point.y, node.width ); + } + + CatmullRom spline; + spline.initialize( nodeCount, positions ); + delete [] positions; + + mEdges.clear(); + + Point3F lastBreakVector(0,0,0); + RoadEdge slice; + Point3F lastBreakNode; + lastBreakNode = spline.evaluate(0.0f); + + for ( U32 i = 1; i < mNodes.size(); i++ ) + { + F32 t1 = spline.getTime(i); + F32 t0 = spline.getTime(i-1); + + F32 segLength = spline.arcLength( t0, t1 ); + + U32 numSegments = mCeil( segLength / MIN_METERS_PER_SEGMENT ); + numSegments = getMax( numSegments, (U32)1 ); + F32 tstep = ( t1 - t0 ) / numSegments; + + U32 startIdx = 0; + U32 endIdx = ( i == nodeCount - 1 ) ? numSegments + 1 : numSegments; + + for ( U32 j = startIdx; j < endIdx; j++ ) + { + F32 t = t0 + tstep * j; + Point3F splineNode = spline.evaluate(t); + F32 width = splineNode.z; + _getTerrainHeight( splineNode ); + + Point3F toNodeVec = splineNode - lastBreakNode; + toNodeVec.normalizeSafe(); + + if ( lastBreakVector.isZero() ) + lastBreakVector = toNodeVec; + + F32 angle = mRadToDeg( mAcos( mDot( toNodeVec, lastBreakVector ) ) ); + + if ( j == startIdx || + ( j == endIdx - 1 && i == mNodes.size() - 1 ) || + angle > mBreakAngle ) + { + // Push back a spline node + //slice.p1.set( splineNode.x, splineNode.y, 0.0f ); + //_getTerrainHeight( slice.p1 ); + slice.p1 = splineNode; + slice.uvec.set(0,0,1); + slice.width = width; + slice.parentNodeIdx = i-1; + mEdges.push_back( slice ); + + lastBreakVector = splineNode - lastBreakNode; + lastBreakVector.normalizeSafe(); + + lastBreakNode = splineNode; + } + } + } + + /* + for ( U32 i = 1; i < nodeCount; i++ ) + { + F32 t0 = spline.getTime( i-1 ); + F32 t1 = spline.getTime( i ); + + F32 segLength = spline.arcLength( t0, t1 ); + + U32 numSegments = mCeil( segLength / mBreakAngle ); + numSegments = getMax( numSegments, (U32)1 ); + F32 tstep = ( t1 - t0 ) / numSegments; + + AssertFatal( numSegments > 0, "DecalRoad::_generateEdges, got zero segments!" ); + + U32 startIdx = 0; + U32 endIdx = ( i == nodeCount - 1 ) ? numSegments + 1 : numSegments; + + for ( U32 j = startIdx; j < endIdx; j++ ) + { + F32 t = t0 + tstep * j; + Point3F val = spline.evaluate(t); + + RoadEdge edge; + edge.p1.set( val.x, val.y, 0.0f ); + _getTerrainHeight( val.x, val.y, edge.p1.z ); + edge.uvec.set(0,0,1); + edge.width = val.z; + edge.parentNodeIdx = i-1; + mEdges.push_back( edge ); + } + } + */ + + // + // Calculate fvec and rvec for all edges + // + RoadEdge *edge = NULL; + RoadEdge *nextEdge = NULL; + + for ( U32 i = 0; i < mEdges.size() - 1; i++ ) + { + edge = &mEdges[i]; + nextEdge = &mEdges[i+1]; + + edge->fvec = nextEdge->p1 - edge->p1; + edge->fvec.normalize(); + + edge->rvec = mCross( edge->fvec, edge->uvec ); + edge->rvec.normalize(); + } + + // Must do the last edge outside the loop + RoadEdge *lastEdge = &mEdges[mEdges.size()-1]; + RoadEdge *prevEdge = &mEdges[mEdges.size()-2]; + lastEdge->fvec = prevEdge->fvec; + lastEdge->rvec = prevEdge->rvec; + + + // + // Calculate p0/p2 for all edges + // + for ( U32 i = 0; i < mEdges.size(); i++ ) + { + RoadEdge *edge = &mEdges[i]; + edge->p0 = edge->p1 - edge->rvec * edge->width * 0.5f; + edge->p2 = edge->p1 + edge->rvec * edge->width * 0.5f; + _getTerrainHeight( edge->p0 ); + _getTerrainHeight( edge->p2 ); + } +} + +void DecalRoad::_captureVerts() +{ + PROFILE_SCOPE( DecalRoad_captureVerts ); + + //Con::warnf( "%s - captureVerts", isServerObject() ? "server" : "client" ); + + if ( isServerObject() ) + { + //Con::errorf( "DecalRoad::_captureVerts - called on the server side!" ); + return; + } + + if ( mEdges.size() == 0 ) + return; + + // + // Construct ClippedPolyList objects for each pair + // of roadEdges. + // Use them to capture Terrain verts. + // + SphereF sphere; + RoadEdge *edge = NULL; + RoadEdge *nextEdge = NULL; + + mTriangleCount = 0; + mVertCount = 0; + + Vector clipperList; + + for ( U32 i = 0; i < mEdges.size() - 1; i++ ) + { + Box3F box; + edge = &mEdges[i]; + nextEdge = &mEdges[i+1]; + + box.minExtents = edge->p1; + box.maxExtents = edge->p1; + box.extend( edge->p0 ); + box.extend( edge->p2 ); + box.extend( nextEdge->p0 ); + box.extend( nextEdge->p1 ); + box.extend( nextEdge->p2 ); + box.minExtents.z -= 5.0f; + box.maxExtents.z += 5.0f; + + sphere.center = ( nextEdge->p1 + edge->p1 ) * 0.5f; + sphere.radius = 100.0f; // NOTE: no idea how to calculate this + + ClippedPolyList clipper; + clipper.mNormal.set(0.0f, 0.0f, 0.0f); + VectorF n; + PlaneF plane0, plane1; + + // Construct Back Plane + n = edge->p2 - edge->p0; + n.normalize(); + n = mCross( n, edge->uvec ); + plane0.set( edge->p0, n ); + clipper.mPlaneList.push_back( plane0 ); + + // Construct Front Plane + n = nextEdge->p2 - nextEdge->p0; + n.normalize(); + n = -mCross( edge->uvec, n ); + plane1.set( nextEdge->p0, -n ); + //clipper.mPlaneList.push_back( plane1 ); + + // Test if / where the planes intersect. + bool discardLeft = false; + bool discardRight = false; + Point3F iPos; + VectorF iDir; + + if ( plane0.intersect( plane1, iPos, iDir ) ) + { + Point2F iPos2F( iPos.x, iPos.y ); + Point2F cPos2F( edge->p1.x, edge->p1.y ); + Point2F rVec2F( edge->rvec.x, edge->rvec.y ); + + Point2F iVec2F = iPos2F - cPos2F; + F32 iLen = iVec2F.len(); + iVec2F.normalize(); + + if ( iLen < edge->width * 0.5f ) + { + F32 dot = mDot( rVec2F, iVec2F ); + + // The clipping planes intersected on the right side, + // discard the right side clipping plane. + if ( dot > 0.0f ) + discardRight = true; + // The clipping planes intersected on the left side, + // discard the left side clipping plane. + else + discardLeft = true; + } + } + + // Left Plane + if ( !discardLeft ) + { + n = ( nextEdge->p0 - edge->p0 ); + n.normalize(); + n = mCross( edge->uvec, n ); + clipper.mPlaneList.push_back( PlaneF(edge->p0, n) ); + } + else + { + nextEdge->p0 = edge->p0; + } + + // Right Plane + if ( !discardRight ) + { + n = ( nextEdge->p2 - edge->p2 ); + n.normalize(); + n = -mCross( n, edge->uvec ); + clipper.mPlaneList.push_back( PlaneF(edge->p2, -n) ); + } + else + { + nextEdge->p2 = edge->p2; + } + + n = nextEdge->p2 - nextEdge->p0; + n.normalize(); + n = -mCross( edge->uvec, n ); + plane1.set( nextEdge->p0, -n ); + clipper.mPlaneList.push_back( plane1 ); + + // We have constructed the clipping planes, + // now grab/clip the terrain geometry + getContainer()->buildPolyList( PLC_Decal, box, TerrainObjectType, &clipper ); + clipper.cullUnusedVerts(); + clipper.triangulate(); + clipper.generateNormals(); + + // If we got something, add it to the ClippedPolyList Vector + if ( !clipper.isEmpty() && !( smDiscardAll && ( discardRight || discardLeft ) ) ) + { + clipperList.push_back( clipper ); + + mVertCount += clipper.mVertexList.size(); + mTriangleCount += clipper.mPolyList.size(); + } + } + + // + // Set the roadEdge height to be flush with terrain + // This is not really necessary but makes the debug spline rendering better. + // + for ( U32 i = 0; i < mEdges.size() - 1; i++ ) + { + edge = &mEdges[i]; + + _getTerrainHeight( edge->p0.x, edge->p0.y, edge->p0.z ); + + _getTerrainHeight( edge->p2.x, edge->p2.y, edge->p2.z ); + } + + // + // Allocate the RoadBatch(s) + // + + // If we captured no verts, then we can return here without + // allocating any RoadBatches or the Vert/Index Buffers. + // PreprenderImage will not allocate a render instance while + // mBatches.size() is zero. + U32 numClippers = clipperList.size(); + if ( numClippers == 0 ) + return; + + mBatches.clear(); + + // Allocate the VertexBuffer and PrimitiveBuffer + mVB.set( GFX, mVertCount, GFXBufferTypeStatic ); + mPB.set( GFX, mTriangleCount * 3, 0, GFXBufferTypeStatic ); + + // Lock the VertexBuffer + GFXVertexPNTBT *vertPtr = mVB.lock(); + U32 vertIdx = 0; + + // + // Fill the VertexBuffer and vertex data for the RoadBatches + // Loop through the ClippedPolyList Vector + // + RoadBatch *batch = NULL; + F32 texStart = 0.0f; + F32 texEnd; + + for ( U32 i = 0; i < clipperList.size(); i++ ) + { + ClippedPolyList *clipper = &clipperList[i]; + RoadEdge &edge = mEdges[i]; + RoadEdge &nextEdge = mEdges[i+1]; + + VectorF segFvec = nextEdge.p1 - edge.p1; + F32 segLen = segFvec.len(); + segFvec.normalize(); + + F32 texLen = segLen / mTextureLength; + texEnd = texStart + texLen; + + BiQuadToSqr quadToSquare( Point2F( edge.p0.x, edge.p0.y ), + Point2F( edge.p2.x, edge.p2.y ), + Point2F( nextEdge.p2.x, nextEdge.p2.y ), + Point2F( nextEdge.p0.x, nextEdge.p0.y ) ); + + // + if ( i % mSegmentsPerBatch == 0 ) + { + mBatches.increment(); + batch = &mBatches.last(); + + batch->bounds.minExtents = clipper->mVertexList[0].point; + batch->bounds.maxExtents = clipper->mVertexList[0].point; + batch->startVert = vertIdx; + } + + // Loop through each ClippedPolyList + for ( U32 j = 0; j < clipper->mVertexList.size(); j++ ) + { + // Add each vert to the VertexBuffer + Point3F pos = clipper->mVertexList[j].point; + vertPtr[vertIdx].point = pos; + vertPtr[vertIdx].normal = clipper->mNormalList[j]; + + Point2F uv = quadToSquare.transform( Point2F(pos.x,pos.y) ); + vertPtr[vertIdx].texCoord.x = uv.x; + vertPtr[vertIdx].texCoord.y = -(( texEnd - texStart ) * uv.y + texStart); + + vertPtr[vertIdx].tangent = mCross( segFvec, clipper->mNormalList[j] ); + vertPtr[vertIdx].binormal = segFvec; + + vertIdx++; + + // Expand the RoadBatch bounds to contain this vertex + batch->bounds.extend( pos ); + } + + batch->endVert = vertIdx - 1; + + texStart = texEnd; + } + + // Unlock the VertexBuffer, we are done filling it. + mVB.unlock(); + + // Lock the PrimitiveBuffer + U16 *idxBuff; + mPB.lock(&idxBuff); + U32 curIdx = 0; + U16 vertOffset = 0; + batch = NULL; + S32 batchIdx = -1; + + // Fill the PrimitiveBuffer + // Loop through each ClippedPolyList in the Vector + for ( U32 i = 0; i < clipperList.size(); i++ ) + { + ClippedPolyList *clipper = &clipperList[i]; + + if ( i % mSegmentsPerBatch == 0 ) + { + batchIdx++; + batch = &mBatches[batchIdx]; + batch->startIndex = curIdx; + } + + for ( U32 j = 0; j < clipper->mPolyList.size(); j++ ) + { + // Write indices for each Poly + ClippedPolyList::Poly *poly = &clipper->mPolyList[j]; + + AssertFatal( poly->vertexCount == 3, "Got non-triangle poly!" ); + + idxBuff[curIdx] = clipper->mIndexList[poly->vertexStart] + vertOffset; + curIdx++; + idxBuff[curIdx] = clipper->mIndexList[poly->vertexStart + 1] + vertOffset; + curIdx++; + idxBuff[curIdx] = clipper->mIndexList[poly->vertexStart + 2] + vertOffset; + curIdx++; + } + + batch->endIndex = curIdx - 1; + + vertOffset += clipper->mVertexList.size(); + } + + // Unlock the PrimitiveBuffer, we are done filling it. + mPB.unlock(); + + // Generate the object/world bounds + // Is the union of all batch bounding boxes. + + Box3F box; + for ( U32 i = 0; i < mBatches.size(); i++ ) + { + const RoadBatch &batch = mBatches[i]; + + if ( i == 0 ) + box = batch.bounds; + else + box.intersect( batch.bounds ); + } + + Point3F pos = getPosition(); + + mWorldBox = box; + resetObjectBox(); + + // Make sure we are in the correct bins given our world box. + if( getSceneManager() != NULL ) + getSceneManager()->notifyObjectDirty( this ); +} + +U32 DecalRoad::_addNode( const Point3F &pos, F32 width ) +{ + mNodes.increment(); + RoadNode &node = mNodes.last(); + + node.point = pos; + node.width = width; + + return mNodes.size() - 1; +} + +U32 DecalRoad::_insertNode( const Point3F &pos, const F32 &width, const U32 &idx ) +{ + U32 ret; + RoadNode *node; + + if ( idx == U32_MAX ) + { + mNodes.increment(); + node = &mNodes.last(); + ret = mNodes.size() - 1; + } + else + { + mNodes.insert( idx ); + node = &mNodes[idx]; + ret = idx; + } + + node->point = pos; + //node->t = -1.0f; + //node->rot.identity(); + node->width = width; + + return ret; +} + +bool DecalRoad::_getTerrainHeight( Point3F &pos ) +{ + return _getTerrainHeight( pos.x, pos.y, pos.z ); +} + +bool DecalRoad::_getTerrainHeight( const Point2F &pos, F32 &height ) +{ + return _getTerrainHeight( pos.x, pos.y, height ); +} + +bool DecalRoad::_getTerrainHeight( const F32 &x, const F32 &y, F32 &height ) +{ + Point3F startPnt( x, y, 10000.0f ); + Point3F endPnt( x, y, -10000.0f ); + + RayInfo ri; + bool hit; + + hit = getContainer()->castRay(startPnt, endPnt, TerrainObjectType, &ri); + + if ( hit ) + height = ri.point.z; + + return hit; +} + +void DecalRoad::_onTerrainChanged( U32 type, TerrainBlock* tblock, const Point2I &min, const Point2I &max ) +{ + // The client side object just stores the area that has changed + // and waits for the (delayed) update event from the server + // to actually perform the update. + if ( isClientObject() && tblock->isClientObject() ) + { + // Convert the min and max into world space. + const F32 size = tblock->getSquareSize(); + const Point3F pos = tblock->getPosition(); + + // TODO: I don't think this works right with tiling! + Box3F dirty( F32( min.x * size ) + pos.x, F32( min.y * size ) + pos.y, -F32_MAX, + F32( max.x * size ) + pos.x, F32( max.y * size ) + pos.y, F32_MAX ); + + if ( !mTerrainUpdateRect.isValidBox() ) + mTerrainUpdateRect = dirty; + else + mTerrainUpdateRect.intersect( dirty ); + } + // The server object only updates edges (doesn't clip to geometry) + // and schedules an update to be sent to the client. + else if ( isServerObject() && tblock->isServerObject() ) + { + //_generateEdges(); + scheduleUpdate( TerrainChangedMask ); + } +} + + +// Static protected field set methods + +bool DecalRoad::ptSetBreakAngle( void *object, const char *index, const char *data ) +{ + DecalRoad *road = static_cast( object ); + F32 val = dAtof( data ); + + road->setBreakAngle( val ); + + // we already set the field + return false; +} + +bool DecalRoad::ptSetTextureLength( void *object, const char *index, const char *data ) +{ + DecalRoad *road = static_cast( object ); + F32 val = dAtof( data ); + + road->setTextureLength( val ); + + // we already set the field + return false; +} + + +// ConsoleMethods + +DefineEngineMethod( DecalRoad, regenerate, void, (),, + "Intended as a helper to developers and editor scripts.\n" + "Force DecalRoad to update it's spline and reclip geometry." + ) +{ + object->regenerate(); +} + +DefineEngineMethod( DecalRoad, postApply, void, (),, + "Intended as a helper to developers and editor scripts.\n" + "Force trigger an inspectPostApply. This will transmit " + "the material and other fields ( not including nodes ) " + "to client objects." + ) +{ + object->inspectPostApply(); +} \ No newline at end of file diff --git a/Engine/source/environment/decalRoad.h b/Engine/source/environment/decalRoad.h new file mode 100644 index 000000000..7b0987d27 --- /dev/null +++ b/Engine/source/environment/decalRoad.h @@ -0,0 +1,278 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _DECALROAD_H_ +#define _DECALROAD_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _GFXVERTEXBUFFER_H_ +#include "gfx/gfxVertexBuffer.h" +#endif +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif +#ifndef _CLIPPEDPOLYLIST_H_ +#include "collision/clippedPolyList.h" +#endif + +class Path; +class TerrainBlock; +struct ObjectRenderInst; +class Material; +struct DecalRoadNodeList; + + +class DecalRoadUpdateEvent : public SimEvent +{ + typedef SimEvent Parent; +public: + + DecalRoadUpdateEvent( U32 mask, U32 ms ) { mMask = mask; mMs = ms; } + virtual void process( SimObject *object ); + + U32 mMask; + U32 mMs; +}; + + +struct RoadNode +{ + /// The 3D position of the node. + Point3F point; + + /// The width of the road at this node. + F32 width; + + /// Alpha of the road at this node. + //F32 alpha; +}; +typedef Vector RoadNodeVector; + +struct RoadEdge +{ + RoadEdge() + { + p0.zero(); + p1.zero(); + p2.zero(); + + uvec.zero(); + fvec.zero(); + rvec.zero(); + + width = 0.0f; + + parentNodeIdx = -1; + }; + + Point3F p0; + Point3F p1; + Point3F p2; + + VectorF uvec; + VectorF fvec; + VectorF rvec; + + F32 width; + + U32 parentNodeIdx; +}; +typedef Vector RoadEdgeVector; + +struct RoadBatch +{ + U32 startVert; + U32 endVert; + + U32 startIndex; + U32 endIndex; + + Box3F bounds; +}; +typedef Vector RoadBatchVector; + + +//------------------------------------------------------------------------------ +// DecalRoad Class +//------------------------------------------------------------------------------ +class DecalRoad : public SceneObject +{ +private: + + friend class DecalRoadUpdateEvent; + friend class GuiRoadEditorCtrl; + friend class GuiRoadEditorUndoAction; + typedef SceneObject Parent; + +protected: + // Internal defines, enums, structs, classes + + struct Triangle + { + GFXVertexPT v0, v1, v2; + }; + + enum + { + DecalRoadMask = Parent::NextFreeMask, + NodeMask = Parent::NextFreeMask << 1, + GenEdgesMask = Parent::NextFreeMask << 2, + ReClipMask = Parent::NextFreeMask << 3, + TerrainChangedMask = Parent::NextFreeMask << 4, + NextFreeMask = Parent::NextFreeMask << 5, + }; + + #define StepSize_Normal 10.0f + #define MIN_METERS_PER_SEGMENT 1.0f + +public: + + DecalRoad(); + ~DecalRoad(); + + DECLARE_CONOBJECT(DecalRoad); + + // ConsoleObject + static void initPersistFields(); + static void consoleInit(); + + // SimObject + bool onAdd(); + void onRemove(); + void onEditorEnable(); + void onEditorDisable(); + void inspectPostApply(); + void onStaticModified(const char* slotName, const char*newValue = NULL); + void writeFields(Stream &stream, U32 tabStop); + bool writeField( StringTableEntry fieldname, const char *value ); + + // NetObject + U32 packUpdate(NetConnection *, U32, BitStream *); + void unpackUpdate(NetConnection *, BitStream *); + + // SceneObject + virtual void prepRenderImage( SceneRenderState* state ); + virtual void setTransform( const MatrixF &mat ); + virtual void setScale( const VectorF &scale ); + virtual bool containsPoint( const Point3F& point ) const { return containsPoint( point, NULL ); } + + // fxRoad Public Methods + void scheduleUpdate( U32 updateMask ); + void scheduleUpdate( U32 updateMask, U32 delayMs, bool restartTimer ); + void regenerate(); + void setTextureLength( F32 meters ); + void setBreakAngle( F32 degrees ); + + /// Insert a node anywhere in the road. + /// Pass idx zero to add to the front and idx U32_MAX to add to the end + U32 insertNode( const Point3F &pos, const F32 &width, const U32 &idx ); + + U32 addNode( const Point3F &pos, F32 width = 10.0f ); + void deleteNode( U32 idx ); + + void buildNodesFromList( DecalRoadNodeList* list ); + + bool getClosestNode( const Point3F &pos, U32 &idx ); + + bool containsPoint( const Point3F &worldPos, U32 *nodeIdx ) const; + bool castray( const Point3F &start, const Point3F &end ) const; + + Point3F getNodePosition( U32 idx ); + void setNodePosition( U32 idx, const Point3F &pos ); + + F32 getNodeWidth( U32 idx ); + void setNodeWidth( U32 idx, F32 width ); + + /// Protected 'Node' Field setter that will add a node to the list. + static bool addNodeFromField( void *object, const char *index, const char *data ); + + static SimSet* getServerSet(); + +protected: + + // Internal Helper Methods + + void _initMaterial(); + void _debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *matInst ); + + U32 _insertNode( const Point3F &pos, const F32 &width, const U32 &idx ); + U32 _addNode( const Point3F &pos, F32 width ); + void _generateEdges(); + void _captureVerts(); + + bool _getTerrainHeight( Point3F &pos ); + bool _getTerrainHeight( const Point2F &pos, F32 &height ); + bool _getTerrainHeight( const F32 &x, const F32 &y, F32 &height ); + + void _onTerrainChanged( U32 type, TerrainBlock* tblock, const Point2I &min, const Point2I &max ); + + // static protected field set methods + static bool ptSetBreakAngle( void *object, const char *index, const char *data ); + static bool ptSetTextureLength( void *object, const char *index, const char *data ); + +protected: + + // Field Vars + F32 mBreakAngle; + U32 mSegmentsPerBatch; + F32 mTextureLength; + String mMaterialName; + U32 mRenderPriority; + + // Static ConsoleVars for editor + static bool smEditorOpen; + static bool smWireframe; + static bool smShowBatches; + static bool smDiscardAll; + static bool smShowSpline; + static bool smShowRoad; + static S32 smUpdateDelay; + + static SimObjectPtr smServerDecalRoadSet; + + // Other Internal Vars + + RoadEdgeVector mEdges; + RoadNodeVector mNodes; + RoadBatchVector mBatches; + + bool mLoadRenderData; + + SimObjectPtr mMaterial; + BaseMatInstance *mMatInst; + + GFXVertexBufferHandle mVB; + GFXPrimitiveBufferHandle mPB; + + U32 mTriangleCount; + U32 mVertCount; + + S32 mUpdateEventId; + DecalRoadUpdateEvent *mLastEvent; + + Box3F mTerrainUpdateRect; +}; + + +#endif // _DECALROAD_H_ diff --git a/Engine/source/environment/editors/guiMeshRoadEditorCtrl.cpp b/Engine/source/environment/editors/guiMeshRoadEditorCtrl.cpp new file mode 100644 index 000000000..ac4e4edc5 --- /dev/null +++ b/Engine/source/environment/editors/guiMeshRoadEditorCtrl.cpp @@ -0,0 +1,1307 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "environment/editors/guiMeshRoadEditorCtrl.h" + +#include "console/consoleTypes.h" +#include "environment/meshRoad.h" +#include "renderInstance/renderPassManager.h" +#include "collision/collision.h" +#include "math/util/frustum.h" +#include "math/mathUtils.h" +#include "gfx/gfxPrimitiveBuffer.h" +#include "gfx/gfxTextureHandle.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxDrawUtil.h" +#include "scene/sceneRenderState.h" +#include "scene/sceneManager.h" +#include "gui/core/guiCanvas.h" +#include "gui/buttons/guiButtonCtrl.h" +#include "gui/worldEditor/undoActions.h" +#include "T3D/gameBase/gameConnection.h" +#include "gfx/sim/debugDraw.h" +#include "materials/materialDefinition.h" +#include "T3D/prefab.h" + +IMPLEMENT_CONOBJECT(GuiMeshRoadEditorCtrl); + +ConsoleDocClass( GuiMeshRoadEditorCtrl, + "@brief GUI tool that makes up the Mesh Road Editor\n\n" + "Editor use only.\n\n" + "@internal" +); + +GuiMeshRoadEditorCtrl::GuiMeshRoadEditorCtrl() + : + // Each of the mode names directly correlates with the Mesh Road Editor's + // tool palette + mSelectMeshRoadMode("MeshRoadEditorSelectMode"), + mAddMeshRoadMode("MeshRoadEditorAddRoadMode"), + mMovePointMode("MeshRoadEditorMoveMode"), + mRotatePointMode("MeshRoadEditorRotateMode"), + mScalePointMode("MeshRoadEditorScaleMode"), + mAddNodeMode("MeshRoadEditorAddNodeMode"), + mInsertPointMode("MeshRoadEditorInsertPointMode"), + mRemovePointMode("MeshRoadEditorRemovePointMode"), + mMode(mSelectMeshRoadMode), + + mHasCopied( false ), + mIsDirty( false ), + mRoadSet( NULL ), + mSelNode( -1 ), + mSelRoad( NULL ), + mHoverRoad( NULL ), + mHoverNode( -1 ), + mDefaultWidth( 10.0f ), + mDefaultDepth( 5.0f ), + mDefaultNormal( 0,0,1 ), + mAddNodeIdx( 0 ), + mNodeHalfSize( 4,4 ), + mHoverSplineColor( 255,0,0,255 ), + mSelectedSplineColor( 0,255,0,255 ), + mHoverNodeColor( 255,255,255,255 ) +{ + mMaterialName[Top] = StringTable->insert("DefaultRoadMaterialTop"); + mMaterialName[Bottom] = StringTable->insert("DefaultRoadMaterialOther"); + mMaterialName[Side] = StringTable->insert("DefaultRoadMaterialOther"); +} + +GuiMeshRoadEditorCtrl::~GuiMeshRoadEditorCtrl() +{ + // nothing to do +} + +void GuiMeshRoadEditorUndoAction::undo() +{ + MeshRoad *object = NULL; + if ( !Sim::findObject( mObjId, object ) ) + return; + + // Temporarily save the Roads current data. + //F32 metersPerSeg = object->mMetersPerSegment; + Vector nodes; + nodes.merge( object->mNodes ); + + // Restore the River properties saved in the UndoAction + //object->mMetersPerSegment = mMetersPerSegment; + + // Restore the Nodes saved in the UndoAction + object->mNodes.clear(); + for ( U32 i = 0; i < mNodes.size(); i++ ) + { + object->_addNode( mNodes[i].point, mNodes[i].width, mNodes[i].depth, mNodes[i].normal ); + } + + // Regenerate the Road + object->regenerate(); + + // If applicable set the selected Road and node + mEditor->mSelRoad = object; + mEditor->mSelNode = -1; + + // Now save the previous Road data in this UndoAction + // since an undo action must become a redo action and vice-versa + //mMetersPerSegment = metersPerSeg; + mNodes.clear(); + mNodes.merge( nodes ); +} + +bool GuiMeshRoadEditorCtrl::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + mRoadSet = MeshRoad::getServerSet(); + + GFXStateBlockDesc desc; + desc.fillMode = GFXFillSolid; + desc.blendDefined = true; + desc.blendEnable = false; + desc.zDefined = true; + desc.zEnable = false; + desc.cullDefined = true; + desc.cullMode = GFXCullNone; + + mZDisableSB = GFX->createStateBlock(desc); + + desc.zEnable = true; + mZEnableSB = GFX->createStateBlock(desc); + + return true; +} + +void GuiMeshRoadEditorCtrl::initPersistFields() +{ + addField( "DefaultWidth", TypeF32, Offset( mDefaultWidth, GuiMeshRoadEditorCtrl ) ); + addField( "DefaultDepth", TypeF32, Offset( mDefaultDepth, GuiMeshRoadEditorCtrl ) ); + addField( "DefaultNormal", TypePoint3F,Offset( mDefaultNormal, GuiMeshRoadEditorCtrl ) ); + addField( "HoverSplineColor", TypeColorI, Offset( mHoverSplineColor, GuiMeshRoadEditorCtrl ) ); + addField( "SelectedSplineColor", TypeColorI, Offset( mSelectedSplineColor, GuiMeshRoadEditorCtrl ) ); + addField( "HoverNodeColor", TypeColorI, Offset( mHoverNodeColor, GuiMeshRoadEditorCtrl ) ); + addField( "isDirty", TypeBool, Offset( mIsDirty, GuiMeshRoadEditorCtrl ) ); + addField( "topMaterialName", TypeString, Offset( mMaterialName[Top], GuiMeshRoadEditorCtrl ), + "Default Material used by the Mesh Road Editor on upper surface road creation." ); + addField( "bottomMaterialName", TypeString, Offset( mMaterialName[Bottom], GuiMeshRoadEditorCtrl ), + "Default Material used by the Mesh Road Editor on bottom surface road creation." ); + addField( "sideMaterialName", TypeString, Offset( mMaterialName[Side], GuiMeshRoadEditorCtrl ), + "Default Material used by the Mesh Road Editor on side surface road creation." ); + //addField( "MoveNodeCursor", TYPEID< SimObject >(), Offset( mMoveNodeCursor, GuiMeshRoadEditorCtrl) ); + //addField( "AddNodeCursor", TYPEID< SimObject >(), Offset( mAddNodeCursor, GuiMeshRoadEditorCtrl) ); + //addField( "InsertNodeCursor", TYPEID< SimObject >(), Offset( mInsertNodeCursor, GuiMeshRoadEditorCtrl) ); + //addField( "ResizeNodeCursor", TYPEID< SimObject >(), Offset( mResizeNodeCursor, GuiMeshRoadEditorCtrl) ); + + Parent::initPersistFields(); +} + +void GuiMeshRoadEditorCtrl::onSleep() +{ + Parent::onSleep(); + + mMode = mSelectMeshRoadMode; + mHoverNode = -1; + mHoverRoad = NULL; + setSelectedNode(-1); +} + +void GuiMeshRoadEditorCtrl::get3DCursor( GuiCursor *&cursor, + bool &visible, + const Gui3DMouseEvent &event_ ) +{ + //cursor = mAddNodeCursor; + //visible = false; + + cursor = NULL; + visible = false; + + GuiCanvas *root = getRoot(); + if ( !root ) + return; + + S32 currCursor = PlatformCursorController::curArrow; + + if ( root->mCursorChanged == currCursor ) + return; + + PlatformWindow *window = root->getPlatformWindow(); + PlatformCursorController *controller = window->getCursorController(); + + // We've already changed the cursor, + // so set it back before we change it again. + if( root->mCursorChanged != -1) + controller->popCursor(); + + // Now change the cursor shape + controller->pushCursor(currCursor); + root->mCursorChanged = currCursor; +} + +void GuiMeshRoadEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event) +{ + mHasCopied = false; + + mGizmo->on3DMouseDown( event ); + + if ( !isFirstResponder() ) + setFirstResponder(); + + // Get the raycast collision position + Point3F tPos; + if ( !getStaticPos( event, tPos ) ) + return; + + mouseLock(); + + // Construct a LineSegment from the camera position to 1000 meters away in + // the direction clicked. + // If that segment hits the terrain, truncate the ray to only be that length. + + // We will use a LineSegment/Sphere intersection test to determine if a MeshRoadNode + // was clicked. + + MeshRoad *pRoad = NULL; + MeshRoad *pClickedRoad = NULL; + U32 insertNodeIdx = -1; + Point3F collisionPnt; + + Point3F startPnt = event.pos; + Point3F endPnt = event.pos + event.vec * 2000.0f; + RayInfo ri; + + if ( gServerContainer.castRay(startPnt, endPnt, StaticShapeObjectType, &ri) ) + endPnt = ri.point; + + DebugDrawer *ddraw = DebugDrawer::get(); + if ( false && ddraw ) + { + ddraw->drawLine( startPnt, endPnt, ColorI(255,0,0,255) ); + ddraw->setLastTTL(DebugDrawer::DD_INFINITE); + } + + // Check for collision with nodes of the currently selected or highlighted MeshRoad + + // Did we click on a MeshRoad? check currently selected road first + if ( mSelRoad != NULL && mSelRoad->collideRay( event.pos, event.vec, &insertNodeIdx, &collisionPnt ) ) + { + pClickedRoad = mSelRoad; + } + else + { + for ( SimSetIterator iter(mRoadSet); *iter; ++iter ) + { + pRoad = static_cast( *iter ); + + // Do not select or edit a MeshRoad within a Prefab. + if ( Prefab::getPrefabByChild(pRoad) ) + continue; + + if ( pRoad->collideRay( event.pos, event.vec, &insertNodeIdx, &collisionPnt ) ) + { + pClickedRoad = pRoad; + break; + } + } + } + + /* + else if ( gServerContainer.castRay(startPnt, endPnt, StaticShapeObjectType, &ri) ) + { + MeshRoad *pRoad = NULL; + pRoad = dynamic_cast(ri.object); + + if ( pRoad && pRoad->collideRay( event.pos, event.vec, &insertNodeIdx, &collisionPnt ) ) + pClickedRoad = pRoad; + } + */ + + // Did we click on a node? + bool nodeClicked = false; + S32 clickedNodeIdx = -1; + + // If we clicked on the currently selected road, only scan its nodes + if ( mSelRoad != NULL && pClickedRoad == mSelRoad ) + { + clickedNodeIdx = _getNodeAtScreenPos( mSelRoad, event.mousePoint ); + nodeClicked = clickedNodeIdx != -1; + } + else + { + for ( SimSetIterator iter(mRoadSet); *iter; ++iter ) + { + pRoad = static_cast( *iter ); + + // Do not select or edit a MeshRoad within a Prefab. + if ( Prefab::getPrefabByChild(pRoad) ) + continue; + + clickedNodeIdx = _getNodeAtScreenPos( pRoad, event.mousePoint ); + + if ( clickedNodeIdx != -1 ) + { + nodeClicked = true; + pClickedRoad = pRoad; + break; + } + } + } + + // shortcuts + bool dblClick = ( event.mouseClickCount > 1 ); + if( dblClick ) + { + if( mMode == mSelectMeshRoadMode ) + { + setMode( mAddMeshRoadMode, true ); + return; + } + if( mMode == mAddNodeMode ) + { + // Delete the node attached to the cursor. + deleteSelectedNode(); + mMode = mAddMeshRoadMode; + return; + } + } + + //this check is here in order to bounce back from deleting a whole road with ctrl+z + //this check places the editor back into addrivermode + if ( mMode == mAddNodeMode ) + { + if ( !mSelRoad ) + mMode = mAddMeshRoadMode; + } + + if ( mMode == mSelectMeshRoadMode ) + { + // Did not click on a MeshRoad or a node. + if ( !pClickedRoad ) + { + setSelectedRoad( NULL ); + setSelectedNode( -1 ); + + return; + } + + // Clicked on a MeshRoad that wasn't the currently selected River. + if ( pClickedRoad != mSelRoad ) + { + setSelectedRoad( pClickedRoad ); + setSelectedNode( clickedNodeIdx ); + return; + } + + // Clicked on a node in the currently selected River that wasn't + // the currently selected node. + if ( nodeClicked ) + { + setSelectedNode( clickedNodeIdx ); + return; + } + } + else if ( mMode == mAddMeshRoadMode ) + { + if ( nodeClicked ) + { + // A double-click on a node in Normal mode means set AddNode mode. + if ( clickedNodeIdx == 0 ) + { + setSelectedRoad( pClickedRoad ); + setSelectedNode( clickedNodeIdx ); + + mAddNodeIdx = clickedNodeIdx; + mMode = mAddNodeMode; + + mSelNode = mSelRoad->insertNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal, mAddNodeIdx ); + mIsDirty = true; + + return; + } + else if ( clickedNodeIdx == pClickedRoad->mNodes.size() - 1 ) + { + setSelectedRoad( pClickedRoad ); + setSelectedNode( clickedNodeIdx ); + + mAddNodeIdx = U32_MAX; + mMode = mAddNodeMode; + + mSelNode = mSelRoad->addNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal); + mIsDirty = true; + setSelectedNode( mSelNode ); + + return; + } + } + + MeshRoad *newRoad = new MeshRoad; + + newRoad->mMaterialName[Top] = mMaterialName[Top]; + newRoad->mMaterialName[Bottom] = mMaterialName[Bottom]; + newRoad->mMaterialName[Side] = mMaterialName[Side]; + + newRoad->registerObject(); + + // Add to MissionGroup + SimGroup *missionGroup; + if ( !Sim::findObject( "MissionGroup", missionGroup ) ) + Con::errorf( "GuiMeshRoadEditorCtrl - could not find MissionGroup to add new MeshRoad" ); + else + missionGroup->addObject( newRoad ); + + Point3F pos( endPnt ); + pos.z += mDefaultDepth * 0.5f; + + newRoad->insertNode( pos, mDefaultWidth, mDefaultDepth, mDefaultNormal, 0 ); + U32 newNode = newRoad->insertNode( pos, mDefaultWidth, mDefaultDepth, mDefaultNormal, 1 ); + + // Always add to the end of the road, the first node is the start. + mAddNodeIdx = U32_MAX; + + setSelectedRoad( newRoad ); + setSelectedNode( newNode ); + + mMode = mAddNodeMode; + + // Disable the hover node while in addNodeMode, we + // don't want some random node enlarged. + mHoverNode = -1; + + // Grab the mission editor undo manager. + UndoManager *undoMan = NULL; + if ( !Sim::findObject( "EUndoManager", undoMan ) ) + { + Con::errorf( "GuiMeshRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" ); + return; + } + + // Create the UndoAction. + MECreateUndoAction *action = new MECreateUndoAction("Create MeshRoad"); + action->addObject( newRoad ); + + // Submit it. + undoMan->addAction( action ); + + return; + } + else if ( mMode == mAddNodeMode ) + { + // Oops the road got deleted, maybe from an undo action? + // Back to NormalMode. + if ( mSelRoad ) + { + // A double-click on a node in Normal mode means set AddNode mode. + if ( clickedNodeIdx == 0 ) + { + submitUndo( "Add Node" ); + mAddNodeIdx = clickedNodeIdx; + mMode = mAddNodeMode; + mSelNode = mSelRoad->insertNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal, mAddNodeIdx ); + mIsDirty = true; + setSelectedNode( mSelNode ); + + return; + } + else + { + if( pClickedRoad && clickedNodeIdx == pClickedRoad->mNodes.size() - 1 ) + { + submitUndo( "Add Node" ); + mAddNodeIdx = U32_MAX; + mMode = mAddNodeMode; + U32 newNode = mSelRoad->addNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal); + mIsDirty = true; + setSelectedNode( newNode ); + return; + } + else + { + submitUndo( "Insert Node" ); + // A single-click on empty space while in + // AddNode mode means insert / add a node. + //submitUndo( "Add Node" ); + U32 newNode = mSelRoad->insertNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal, mAddNodeIdx); + mIsDirty = true; + setSelectedNode( newNode ); + return; + } + } + } + + } + else if ( mMode == mInsertPointMode && mSelRoad != NULL ) + { + if ( pClickedRoad == mSelRoad && insertNodeIdx != -1 ) + { + // NOTE: I guess we have to determine the if the clicked ray intersects a road but not a specific node... + // in order to handle inserting nodes in the same way as for fxRoad + + U32 prevNodeIdx = insertNodeIdx; + U32 nextNodeIdx = ( prevNodeIdx + 1 > mSelRoad->mNodes.size() - 1 ) ? prevNodeIdx : prevNodeIdx + 1; + + const MeshRoadNode &prevNode = mSelRoad->mNodes[prevNodeIdx]; + const MeshRoadNode &nextNode = mSelRoad->mNodes[nextNodeIdx]; + + F32 width = ( prevNode.width + nextNode.width ) * 0.5f; + F32 depth = ( prevNode.depth + nextNode.depth ) * 0.5f; + Point3F normal = ( prevNode.normal + nextNode.normal ) * 0.5f; + normal.normalize(); + + submitUndo( "Insert Node" ); + U32 newNode = mSelRoad->insertNode( collisionPnt, width, depth, normal, insertNodeIdx + 1 ); + mIsDirty = true; + setSelectedNode( newNode ); + return; + } + } + else if ( mMode == mRemovePointMode && mSelRoad != NULL ) + { + if ( nodeClicked && pClickedRoad == mSelRoad ) + { + setSelectedNode( clickedNodeIdx ); + deleteSelectedNode(); + return; + } + } + else if ( mMode == mMovePointMode ) + { + if ( nodeClicked && pClickedRoad == mSelRoad ) + { + setSelectedNode( clickedNodeIdx ); + return; + } + } + else if ( mMode == mScalePointMode ) + { + if ( nodeClicked && pClickedRoad == mSelRoad ) + { + setSelectedNode( clickedNodeIdx ); + return; + } + } + else if ( mMode == mRotatePointMode ) + { + if ( nodeClicked && pClickedRoad == mSelRoad ) + { + setSelectedNode( clickedNodeIdx ); + return; + } + } +} + +void GuiMeshRoadEditorCtrl::on3DRightMouseDown(const Gui3DMouseEvent & event) +{ + //mIsPanning = true; +} + +void GuiMeshRoadEditorCtrl::on3DRightMouseUp(const Gui3DMouseEvent & event) +{ + //mIsPanning = false; +} + +void GuiMeshRoadEditorCtrl::on3DMouseUp(const Gui3DMouseEvent & event) +{ + mGizmo->on3DMouseUp(event); + + mSavedDrag = false; + + mouseUnlock(); +} + +void GuiMeshRoadEditorCtrl::on3DMouseMove(const Gui3DMouseEvent & event) +{ + if ( mSelRoad != NULL && mMode == mAddNodeMode ) + { + Point3F pos; + + mSelRoad->disableCollision(); + if ( getStaticPos( event, pos ) ) + { + pos.z += mSelRoad->getNodeDepth( mSelNode ) * 0.5f; + mSelRoad->setNodePosition( mSelNode, pos ); + mIsDirty = true; + } + mSelRoad->enableCollision(); + + return; + } + + if ( mSelRoad != NULL && mSelNode != -1 ) + { + mGizmo->on3DMouseMove( event ); + //mGizmo.collideAxisGizmo( event ); + //Con::printf( "SelectedAxis: %i", mGizmo.getSelectedAxis() ); + } + + // Is cursor hovering over a river? + if ( mMode == mSelectMeshRoadMode ) + { + mHoverRoad = NULL; + + Point3F startPnt = event.pos; + Point3F endPnt = event.pos + event.vec * 1000.0f; + + RayInfo ri; + + if ( gServerContainer.castRay(startPnt, endPnt, StaticShapeObjectType, &ri) ) + { + MeshRoad *pRoad = NULL; + pRoad = dynamic_cast(ri.object); + + // Do not select or edit a MeshRoad within a Prefab. + if ( pRoad && !Prefab::getPrefabByChild(pRoad) ) + mHoverRoad = pRoad; + } + } + + // Is cursor over a node? + if ( mHoverRoad ) + { + MeshRoad *pRoad = NULL; + S32 nodeIdx = -1; + for ( SimSetIterator iter(mRoadSet); *iter; ++iter ) + { + pRoad = static_cast( *iter ); + + nodeIdx = _getNodeAtScreenPos( pRoad, event.mousePoint ); + + if ( nodeIdx != -1 ) + { + mHoverRoad = pRoad; + break; + } + } + + mHoverNode = nodeIdx; + } +} + +void GuiMeshRoadEditorCtrl::on3DMouseDragged(const Gui3DMouseEvent & event) +{ + // Drags are only used to transform nodes + if ( !mSelRoad || mSelNode == -1 || + ( mMode != mMovePointMode && mMode != mScalePointMode && mMode != mRotatePointMode ) ) + return; + + // If we haven't already saved, + // save an undo action to get back to this state, + // before we make any modifications to the selected node. + if ( !mSavedDrag ) + { + submitUndo( "Modify Node" ); + mSavedDrag = true; + } + + // If shift is held and we haven't already copied the node, + // make a copy of the selected node and select it. + if ( event.modifier & SI_SHIFT && !mHasCopied && mSelRoad->isEndNode( mSelNode ) ) + { + const MeshRoadNode &data = mSelRoad->getNode( mSelNode ); + + U32 insertIdx = ( mSelNode == 0 ) ? 0 : U32_MAX; + U32 newNodeIdx = mSelRoad->insertNode( data.point, data.width, data.depth, data.normal, insertIdx ); + mIsDirty = true; + + mSelNode = -1; + setSelectedNode( newNodeIdx ); + + mHasCopied = true; + } + + // Let the Gizmo handle the drag, eg, modify its transforms + mGizmo->on3DMouseDragged( event ); + if ( mGizmo->isDirty() ) + { + Point3F pos = mGizmo->getPosition(); + Point3F scale = mGizmo->getScale(); + const MatrixF &mat = mGizmo->getTransform(); + VectorF normal; + mat.getColumn( 2, &normal ); + + mSelRoad->setNode( pos, scale.x, scale.z, normal, mSelNode ); + mIsDirty = true; + mGizmo->markClean(); + } + + Con::executef( this, "onNodeModified", Con::getIntArg(mSelNode) ); +} + +void GuiMeshRoadEditorCtrl::on3DMouseEnter(const Gui3DMouseEvent & event) +{ + // nothing to do +} + +void GuiMeshRoadEditorCtrl::on3DMouseLeave(const Gui3DMouseEvent & event) +{ + // nothing to do +} + +bool GuiMeshRoadEditorCtrl::onKeyDown(const GuiEvent& event) +{ + if( event.keyCode == KEY_RETURN && mMode == mAddNodeMode ) + { + // Delete the node attached to the cursor. + deleteSelectedNode(); + mMode = mAddMeshRoadMode; + return true; + } + + return false; +} + +void GuiMeshRoadEditorCtrl::updateGuiInfo() +{ + // nothing to do +} + +void GuiMeshRoadEditorCtrl::renderScene(const RectI & updateRect) +{ + //GFXDrawUtil *drawer = GFX->getDrawUtil(); + + GFX->setStateBlock( mZDisableSB ); + + // get the projected size... + GameConnection* connection = GameConnection::getConnectionToServer(); + if(!connection) + return; + + // Grab the camera's transform + MatrixF mat; + connection->getControlCameraTransform(0, &mat); + + // Get the camera position + Point3F camPos; + mat.getColumn(3,&camPos); + + if ( mHoverRoad && mHoverRoad != mSelRoad ) + { + _drawSpline( mHoverRoad, mHoverSplineColor ); + } + + if ( mSelRoad ) + { + _drawSpline( mSelRoad, mSelectedSplineColor ); + + // Render Gizmo for selected node if were in either of the three transform modes + if ( mSelNode != -1 && ( mMode == mMovePointMode || mMode == mScalePointMode || mMode == mRotatePointMode ) ) + { + if( mMode == mMovePointMode ) + { + mGizmo->getProfile()->mode = MoveMode; + } + else if( mMode == mScalePointMode ) + { + mGizmo->getProfile()->mode = ScaleMode; + } + else if( mMode == mRotatePointMode ) + { + mGizmo->getProfile()->mode = RotateMode; + } + + const MeshRoadNode &node = mSelRoad->mNodes[mSelNode]; + + MatrixF objMat = mSelRoad->getNodeTransform(mSelNode); + Point3F objScale( node.width, 1.0f, node.depth ); + Point3F worldPos = node.point; + + mGizmo->set( objMat, worldPos, objScale ); + + mGizmo->renderGizmo( mLastCameraQuery.cameraMatrix, mLastCameraQuery.fov ); + + // Render Gizmo text + mGizmo->renderText( mSaveViewport, mSaveModelview, mSaveProjection ); + } + } + + DebugDrawer::get()->render(); + + // Now draw all the 2d stuff! + GFX->setClipRect(updateRect); + + // Draw Control nodes for selecting and highlighted rivers + if ( mHoverRoad ) + _drawControlNodes( mHoverRoad, mHoverSplineColor ); + if ( mSelRoad ) + _drawControlNodes( mSelRoad, mSelectedSplineColor ); +} + +S32 GuiMeshRoadEditorCtrl::_getNodeAtScreenPos( const MeshRoad *pRoad, const Point2I &posi ) +{ + for ( U32 i = 0; i < pRoad->mNodes.size(); i++ ) + { + const Point3F &nodePos = pRoad->mNodes[i].point; + + Point3F screenPos; + project( nodePos, &screenPos ); + + if ( screenPos.z < 0.0f ) + continue; + + Point2I screenPosI( (S32)screenPos.x, (S32)screenPos.y ); + + RectI nodeScreenRect( screenPosI - mNodeHalfSize, mNodeHalfSize * 2 ); + + if ( nodeScreenRect.pointInRect(posi) ) + { + // we found a hit! + return i; + } + } + + return -1; +} + +void GuiMeshRoadEditorCtrl::_drawSpline( MeshRoad *river, const ColorI &color ) +{ + if ( river->mSlices.size() <= 1 ) + return; + + if ( MeshRoad::smShowSpline ) + { + // Render the River center-line + PrimBuild::color( color ); + PrimBuild::begin( GFXLineStrip, river->mSlices.size() ); + for ( U32 i = 0; i < river->mSlices.size(); i++ ) + { + PrimBuild::vertex3fv( river->mSlices[i].p1 ); + } + PrimBuild::end(); + } + + if ( MeshRoad::smWireframe ) + { + // Left-side line + PrimBuild::color3i( 100, 100, 100 ); + PrimBuild::begin( GFXLineStrip, river->mSlices.size() ); + for ( U32 i = 0; i < river->mSlices.size(); i++ ) + { + PrimBuild::vertex3fv( river->mSlices[i].p0 ); + } + PrimBuild::end(); + + // Right-side line + PrimBuild::begin( GFXLineStrip, river->mSlices.size() ); + for ( U32 i = 0; i < river->mSlices.size(); i++ ) + { + PrimBuild::vertex3fv( river->mSlices[i].p2 ); + } + PrimBuild::end(); + + // Cross-sections + PrimBuild::begin( GFXLineList, river->mSlices.size() * 2 ); + for ( U32 i = 0; i < river->mSlices.size(); i++ ) + { + PrimBuild::vertex3fv( river->mSlices[i].p0 ); + PrimBuild::vertex3fv( river->mSlices[i].p2 ); + } + PrimBuild::end(); + } + + // Segment +} + +void GuiMeshRoadEditorCtrl::_drawControlNodes( MeshRoad *river, const ColorI &color ) +{ + if ( !MeshRoad::smShowSpline ) + return; + + RectI bounds = getBounds(); + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + + bool isSelected = ( river == mSelRoad ); + bool isHighlighted = ( river == mHoverRoad ); + + for ( U32 i = 0; i < river->mNodes.size(); i++ ) + { + if ( false && isSelected && mSelNode == i ) + continue; + + const Point3F &wpos = river->mNodes[i].point; + + Point3F spos; + project( wpos, &spos ); + + if ( spos.z > 1.0f ) + continue; + + Point2I posi; + posi.x = spos.x; + posi.y = spos.y; + + if ( !bounds.pointInRect( posi ) ) + continue; + + ColorI theColor = color; + Point2I nodeHalfSize = mNodeHalfSize; + + if ( isHighlighted && mHoverNode == i ) + { + //theColor = mHoverNodeColor; + nodeHalfSize += Point2I(2,2); + } + + if ( isSelected ) + { + if ( mSelNode == i ) + { + theColor.set(0,0,255); + } + else if ( i == 0 ) + { + theColor.set(0,255,0); + } + else if ( i == river->mNodes.size() - 1 ) + { + theColor.set(255,0,0); + } + } + + drawer->drawRectFill( posi - nodeHalfSize, posi + nodeHalfSize, theColor ); + } +} + +bool GuiMeshRoadEditorCtrl::getStaticPos( const Gui3DMouseEvent & event, Point3F &tpos ) +{ + // Find clicked point on the terrain + + Point3F startPnt = event.pos; + Point3F endPnt = event.pos + event.vec * 1000.0f; + + RayInfo ri; + bool hit; + + hit = gServerContainer.castRay(startPnt, endPnt, StaticShapeObjectType, &ri); + tpos = ri.point; + + return hit; +} + +void GuiMeshRoadEditorCtrl::deleteSelectedNode() +{ + if ( !mSelRoad || mSelNode == -1 ) + return; + + // If the Road has only two nodes remaining, + // delete the whole Road. + if ( mSelRoad->mNodes.size() <= 2 ) + { + deleteSelectedRoad( mMode != mAddNodeMode ); + } + else + { + if ( mMode != mAddNodeMode ) + submitUndo( "Delete Node" ); + + // Delete the SelectedNode of the SelectedRoad + mSelRoad->deleteNode( mSelNode ); + + // We deleted the Node but not the Road (it has nodes left) + // so decrement the currently selected node. + if ( mSelRoad->mNodes.size() <= mSelNode ) + setSelectedNode( mSelNode - 1 ); + else + { + // force gizmo to update to the selected nodes position + // the index didn't change but the node it refers to did. + U32 i = mSelNode; + mSelNode = -1; + setSelectedNode( i ); + } + } + + // If you were in addNodeMode, + // deleting a node should ends it. + //mMode = smNormalMode; +} + +void GuiMeshRoadEditorCtrl::deleteSelectedRoad( bool undoAble ) +{ + AssertFatal( mSelRoad != NULL, "GuiMeshRoadEditorCtrl::deleteSelectedRoad() - No Road is selected" ); + + // Not undoAble? Just delete it. + if ( !undoAble ) + { + mSelRoad->deleteObject(); + mIsDirty = true; + Con::executef( this, "onRoadSelected" ); + mSelNode = -1; + + return; + } + + // Grab the mission editor undo manager. + UndoManager *undoMan = NULL; + if ( !Sim::findObject( "EUndoManager", undoMan ) ) + { + // Couldn't find it? Well just delete the Road. + Con::errorf( "GuiMeshRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" ); + return; + } + else + { + // Create the UndoAction. + MEDeleteUndoAction *action = new MEDeleteUndoAction("Deleted Road"); + action->deleteObject( mSelRoad ); + mIsDirty = true; + + // Submit it. + undoMan->addAction( action ); + } + + // ScriptCallback with 'NULL' parameter for no Road currently selected. + Con::executef( this, "onRoadSelected" ); + + // Clear the SelectedNode (it has been deleted along with the River). + setSelectedNode( -1 ); + mSelNode = -1; +} + +void GuiMeshRoadEditorCtrl::setMode( String mode, bool sourceShortcut = false ) +{ + mMode = mode; + + if( sourceShortcut ) + Con::executef( this, "paletteSync", mode ); +} + +void GuiMeshRoadEditorCtrl::setSelectedRoad( MeshRoad *road ) +{ + mSelRoad = road; + + if ( mSelRoad != NULL ) + Con::executef( this, "onRoadSelected", road->getIdString() ); + else + Con::executef( this, "onRoadSelected" ); + + if ( mSelRoad != road ) + setSelectedNode(-1); +} + +void GuiMeshRoadEditorCtrl::setNodeWidth( F32 width ) +{ + if ( mSelRoad && mSelNode != -1 ) + { + mSelRoad->setNodeWidth( mSelNode, width ); + mIsDirty = true; + } +} + +F32 GuiMeshRoadEditorCtrl::getNodeWidth() +{ + if ( mSelRoad && mSelNode != -1 ) + return mSelRoad->getNodeWidth( mSelNode ); + + return 0.0f; +} + +void GuiMeshRoadEditorCtrl::setNodeDepth(F32 depth) +{ + if ( mSelRoad && mSelNode != -1 ) + { + mSelRoad->setNodeDepth( mSelNode, depth ); + mIsDirty = true; + } +} + +F32 GuiMeshRoadEditorCtrl::getNodeDepth() +{ + if ( mSelRoad && mSelNode != -1 ) + return mSelRoad->getNodeDepth( mSelNode ); + + return 0.0f; +} + +void GuiMeshRoadEditorCtrl::setNodePosition( Point3F pos ) +{ + if ( mSelRoad && mSelNode != -1 ) + { + mSelRoad->setNodePosition( mSelNode, pos ); + mIsDirty = true; + } +} + +Point3F GuiMeshRoadEditorCtrl::getNodePosition() +{ + if ( mSelRoad && mSelNode != -1 ) + return mSelRoad->getNodePosition( mSelNode ); + + return Point3F( 0, 0, 0 ); +} + +void GuiMeshRoadEditorCtrl::setNodeNormal( const VectorF &normal ) +{ + if ( mSelRoad && mSelNode != -1 ) + { + mSelRoad->setNodeNormal( mSelNode, normal ); + mIsDirty = true; + } +} + +VectorF GuiMeshRoadEditorCtrl::getNodeNormal() +{ + if ( mSelRoad && mSelNode != -1 ) + return mSelRoad->getNodeNormal( mSelNode ); + + return VectorF::Zero; +} + +void GuiMeshRoadEditorCtrl::setSelectedNode( S32 node ) +{ + if ( mSelNode == node ) + return; + + mSelNode = node; + if ( mSelNode != -1 ) + { + const MeshRoadNode &node = mSelRoad->mNodes[mSelNode]; + + MatrixF objMat = mSelRoad->getNodeTransform(mSelNode); + Point3F objScale( node.width, 1.0f, node.depth ); + Point3F worldPos = node.point; + + mGizmo->set( objMat, worldPos, objScale ); + } + + if ( mSelNode != -1 ) + Con::executef( this, "onNodeSelected", Con::getIntArg(mSelNode) ); + else + Con::executef( this, "onNodeSelected", Con::getIntArg(-1) ); +} + +void GuiMeshRoadEditorCtrl::submitUndo( const UTF8 *name ) +{ + // Grab the mission editor undo manager. + UndoManager *undoMan = NULL; + if ( !Sim::findObject( "EUndoManager", undoMan ) ) + { + Con::errorf( "GuiMeshRoadEditorCtrl::submitUndo() - EUndoManager not found!" ); + return; + } + + // Setup the action. + GuiMeshRoadEditorUndoAction *action = new GuiMeshRoadEditorUndoAction( name ); + + action->mObjId = mSelRoad->getId(); + //action->mMetersPerSegment = mSelRoad->mMetersPerSegment; + action->mEditor = this; + + for( U32 i = 0; i < mSelRoad->mNodes.size(); i++ ) + { + action->mNodes.push_back( mSelRoad->mNodes[i] ); + } + + undoMan->addAction( action ); +} + +void GuiMeshRoadEditorCtrl::matchTerrainToRoad() +{ + if ( !mSelRoad ) + return; + + // Not implemented, but a potentially useful idea. + // Move manipulate the terrain so that the MeshRoad appears to be sitting + // on top of it. + + // The opposite could also be useful, manipulate the MeshRoad to line up + // with the terrain underneath it. +} + +ConsoleMethod( GuiMeshRoadEditorCtrl, deleteNode, void, 2, 2, "deleteNode()" ) +{ + object->deleteSelectedNode(); +} + +ConsoleMethod( GuiMeshRoadEditorCtrl, getMode, const char*, 2, 2, "" ) +{ + return object->getMode(); +} + +ConsoleMethod( GuiMeshRoadEditorCtrl, setMode, void, 3, 3, "setMode( String mode )" ) +{ + String newMode = ( argv[2] ); + object->setMode( newMode ); +} + +ConsoleMethod( GuiMeshRoadEditorCtrl, getNodeWidth, F32, 2, 2, "" ) +{ + return object->getNodeWidth(); +} + +ConsoleMethod( GuiMeshRoadEditorCtrl, setNodeWidth, void, 3, 3, "" ) +{ + object->setNodeWidth( dAtof(argv[2]) ); +} + +ConsoleMethod( GuiMeshRoadEditorCtrl, getNodeDepth, F32, 2, 2, "" ) +{ + return object->getNodeDepth(); +} + +ConsoleMethod( GuiMeshRoadEditorCtrl, setNodeDepth, void, 3, 3, "" ) +{ + object->setNodeDepth( dAtof(argv[2]) ); +} + +ConsoleMethod( GuiMeshRoadEditorCtrl, getNodePosition, const char*, 2, 2, "" ) +{ + char* returnBuffer = Con::getReturnBuffer(256); + + dSprintf(returnBuffer, 256, "%f %f %f", + object->getNodePosition().x, object->getNodePosition().y, object->getNodePosition().z); + + return returnBuffer; +} + +ConsoleMethod( GuiMeshRoadEditorCtrl, setNodePosition, void, 3, 3, "" ) +{ + Point3F pos; + + S32 count = dSscanf( argv[2], "%f %f %f", + &pos.x, &pos.y, &pos.z); + + if ( (count != 3) ) + { + Con::printf("Failed to parse node information \"px py pz\" from '%s'", argv[3]); + return; + } + + object->setNodePosition( pos ); +} + +ConsoleMethod( GuiMeshRoadEditorCtrl, getNodeNormal, const char*, 2, 2, "" ) +{ + char* returnBuffer = Con::getReturnBuffer(256); + + dSprintf(returnBuffer, 256, "%f %f %f", + object->getNodeNormal().x, object->getNodeNormal().y, object->getNodeNormal().z); + + return returnBuffer; +} + +ConsoleMethod( GuiMeshRoadEditorCtrl, setNodeNormal, void, 3, 3, "" ) +{ + VectorF normal; + + S32 count = dSscanf( argv[2], "%f %f %f", + &normal.x, &normal.y, &normal.z); + + if ( (count != 3) ) + { + Con::printf("Failed to parse node information \"px py pz\" from '%s'", argv[3]); + return; + } + + object->setNodeNormal( normal ); +} + +ConsoleMethod( GuiMeshRoadEditorCtrl, setSelectedRoad, void, 2, 3, "" ) +{ + if ( argc == 2 ) + object->setSelectedRoad(NULL); + else + { + MeshRoad *road = NULL; + if ( Sim::findObject( argv[2], road ) ) + object->setSelectedRoad(road); + } +} + +ConsoleMethod( GuiMeshRoadEditorCtrl, getSelectedRoad, const char*, 2, 2, "" ) +{ + MeshRoad *road = object->getSelectedRoad(); + if ( !road ) + return NULL; + + return road->getIdString(); +} + +ConsoleMethod( GuiMeshRoadEditorCtrl, regenerate, void, 2, 2, "" ) +{ + MeshRoad *road = object->getSelectedRoad(); + if ( road ) + road->regenerate(); +} + +ConsoleMethod( GuiMeshRoadEditorCtrl, matchTerrainToRoad, void, 2, 2, "" ) +{ + object->matchTerrainToRoad(); +} \ No newline at end of file diff --git a/Engine/source/environment/editors/guiMeshRoadEditorCtrl.h b/Engine/source/environment/editors/guiMeshRoadEditorCtrl.h new file mode 100644 index 000000000..9e810982a --- /dev/null +++ b/Engine/source/environment/editors/guiMeshRoadEditorCtrl.h @@ -0,0 +1,178 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIMESHROADEDITORCTRL_H_ +#define _GUIMESHROADEDITORCTRL_H_ + +#ifndef _EDITTSCTRL_H_ +#include "gui/worldEditor/editTSCtrl.h" +#endif +#ifndef _UNDO_H_ +#include "util/undo.h" +#endif +#ifndef _GIZMO_H_ +#include "gui/worldEditor/gizmo.h" +#endif +#ifndef _MESHROAD_H_ +#include "environment/meshRoad.h" +#endif + +class GameBase; + +class GuiMeshRoadEditorCtrl : public EditTSCtrl +{ + typedef EditTSCtrl Parent; + + public: + + friend class GuiMeshRoadEditorUndoAction; + + String mSelectMeshRoadMode; + String mAddMeshRoadMode; + String mAddNodeMode; + String mInsertPointMode; + String mRemovePointMode; + String mMovePointMode; + String mScalePointMode; + String mRotatePointMode; + + GuiMeshRoadEditorCtrl(); + ~GuiMeshRoadEditorCtrl(); + + DECLARE_CONOBJECT(GuiMeshRoadEditorCtrl); + + // SimObject + bool onAdd(); + static void initPersistFields(); + + // GuiControl + virtual void onSleep(); + + // EditTSCtrl + bool onKeyDown(const GuiEvent& event); + void get3DCursor( GuiCursor *&cursor, bool &visible, const Gui3DMouseEvent &event_ ); + void on3DMouseDown(const Gui3DMouseEvent & event); + void on3DMouseUp(const Gui3DMouseEvent & event); + void on3DMouseMove(const Gui3DMouseEvent & event); + void on3DMouseDragged(const Gui3DMouseEvent & event); + void on3DMouseEnter(const Gui3DMouseEvent & event); + void on3DMouseLeave(const Gui3DMouseEvent & event); + void on3DRightMouseDown(const Gui3DMouseEvent & event); + void on3DRightMouseUp(const Gui3DMouseEvent & event); + void updateGuiInfo(); + void renderScene(const RectI & updateRect); + + // GuiRiverEditorCtrl + bool getStaticPos( const Gui3DMouseEvent & event, Point3F &tpos ); + void deleteSelectedNode(); + void deleteSelectedRoad( bool undoAble = true ); + + void setMode( String mode, bool sourceShortcut ); + String getMode() { return mMode; } + + void setSelectedRoad( MeshRoad *road ); + MeshRoad* getSelectedRoad() { return mSelRoad; }; + void setSelectedNode( S32 node ); + + F32 getNodeWidth(); + void setNodeWidth( F32 width ); + + F32 getNodeDepth(); + void setNodeDepth( F32 depth ); + + Point3F getNodePosition(); + void setNodePosition( Point3F pos ); + + VectorF getNodeNormal(); + void setNodeNormal( const VectorF &normal ); + + void matchTerrainToRoad(); + + protected: + + enum { + Top = 0, + Bottom = 1, + Side = 2, + SurfaceCount = 3 + }; + + S32 _getNodeAtScreenPos( const MeshRoad *pRoad, const Point2I &posi ); + void _drawSpline( MeshRoad *road, const ColorI &color ); + void _drawControlNodes( MeshRoad *road, const ColorI &color ); + + void submitUndo( const UTF8 *name = "Action" ); + + GFXStateBlockRef mZDisableSB; + GFXStateBlockRef mZEnableSB; + + bool mSavedDrag; + bool mIsDirty; + + SimSet *mRoadSet; + S32 mSelNode; + S32 mHoverNode; + U32 mAddNodeIdx; + SimObjectPtr mSelRoad; + SimObjectPtr mHoverRoad; + + String mMode; + + F32 mDefaultWidth; + F32 mDefaultDepth; + VectorF mDefaultNormal; + + Point2I mNodeHalfSize; + + ColorI mHoverSplineColor; + ColorI mSelectedSplineColor; + ColorI mHoverNodeColor; + + bool mHasCopied; + public: + + StringTableEntry mMaterialName[SurfaceCount]; +}; + +class GuiMeshRoadEditorUndoAction : public UndoAction +{ + public: + + GuiMeshRoadEditorUndoAction( const UTF8* actionName ) : UndoAction( actionName ) + { + } + + GuiMeshRoadEditorCtrl *mEditor; + + Vector mNodes; + + SimObjectId mObjId; + F32 mMetersPerSegment; + + virtual void undo(); + virtual void redo() { undo(); } +}; + +#endif // _GUIMESHROADEDITORCTRL_H_ + + + diff --git a/Engine/source/environment/editors/guiRiverEditorCtrl.cpp b/Engine/source/environment/editors/guiRiverEditorCtrl.cpp new file mode 100644 index 000000000..527c53b3a --- /dev/null +++ b/Engine/source/environment/editors/guiRiverEditorCtrl.cpp @@ -0,0 +1,1506 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "environment/editors/guiRiverEditorCtrl.h" + +#include "console/consoleTypes.h" +#include "environment/river.h" +#include "renderInstance/renderPassManager.h" +#include "collision/collision.h" +#include "math/util/frustum.h" +#include "math/mathUtils.h" +#include "gfx/gfxPrimitiveBuffer.h" +#include "gfx/gfxTextureHandle.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxDrawUtil.h" +#include "scene/sceneRenderState.h" +#include "scene/sceneManager.h" +#include "gui/core/guiCanvas.h" +#include "gui/buttons/guiButtonCtrl.h" +#include "gui/worldEditor/undoActions.h" +#include "T3D/gameBase/gameConnection.h" +#include "T3D/prefab.h" + +IMPLEMENT_CONOBJECT(GuiRiverEditorCtrl); + +ConsoleDocClass( GuiRiverEditorCtrl, + "@brief GUI tool that makes up the River Editor\n\n" + "Editor use only.\n\n" + "@internal" +); + +GuiRiverEditorCtrl::GuiRiverEditorCtrl() + : mDefaultNormal( 0, 0, 1 ), + mDefaultWidth( 10.0f ), + mDefaultDepth( 5.0f ) +{ + // Each of the mode names directly correlates with the River Editor's + // tool palette + mSelectRiverMode = "RiverEditorSelectMode"; + mAddRiverMode = "RiverEditorAddRiverMode"; + mMovePointMode = "RiverEditorMoveMode"; + mRotatePointMode = "RiverEditorRotateMode"; + mScalePointMode = "RiverEditorScaleMode"; + mAddNodeMode = "RiverEditorAddNodeMode"; + mInsertPointMode = "RiverEditorInsertPointMode"; + mRemovePointMode = "RiverEditorRemovePointMode"; + + mMode = mSelectRiverMode; + + mRiverSet = NULL; + mSelNode = -1; + mSelRiver = NULL; + mHoverRiver = NULL; + mAddNodeIdx = 0; + mHoverNode = -1; + + mInsertIdx = -1; + + mStartWidth = -1.0f; + mStartHeight = -1.0f; + mStartX = 0; + + mIsDirty = false; + + mNodeHalfSize.set(4,4); + + mNodeSphereRadius = 15.0f; + mNodeSphereFillColor.set( 15,15,100,145 ); + mNodeSphereLineColor.set( 25,25,25,0 ); + mHoverSplineColor.set( 255,0,0,255 ); + mSelectedSplineColor.set( 0,255,0,255 ); + mHoverNodeColor.set( 255,255,255,255 ); + + mStartDragMousePoint = InvalidMousePoint; + //mMoveNodeCursor = NULL; + //mAddNodeCursor = NULL; + //mInsertNodeCursor = NULL; + //mResizeNodeCursor = NULL; +} + +GuiRiverEditorCtrl::~GuiRiverEditorCtrl() +{ + // nothing to do +} + +void GuiRiverEditorUndoAction::undo() +{ + River *river = NULL; + if ( !Sim::findObject( mObjId, river ) ) + return; + + // Temporarily save the Rivers current data. + F32 metersPerSeg = river->mMetersPerSegment; + Vector nodes; + nodes.merge( river->mNodes ); + + // Restore the River properties saved in the UndoAction + river->mMetersPerSegment = mMetersPerSegment; + + // Restore the Nodes saved in the UndoAction + river->mNodes.clear(); + for ( U32 i = 0; i < mNodes.size(); i++ ) + { + river->_addNode( mNodes[i].point, mNodes[i].width, mNodes[i].depth, mNodes[i].normal ); + } + + // Regenerate the River + river->regenerate(); + + // If applicable set the selected River and node + mRiverEditor->mSelRiver = river; + mRiverEditor->mSelNode = -1; + + // Now save the previous River data in this UndoAction + // since an undo action must become a redo action and vice-versa + mMetersPerSegment = metersPerSeg; + mNodes.clear(); + mNodes.merge( nodes ); +} + +bool GuiRiverEditorCtrl::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + mRiverSet = River::getServerSet(); + + GFXStateBlockDesc desc; + desc.fillMode = GFXFillSolid; + desc.setBlend( false ); + desc.setZReadWrite( false, false ); + desc.setCullMode( GFXCullNone ); + + mZDisableSB = GFX->createStateBlock(desc); + + desc.setZReadWrite( true, true ); + mZEnableSB = GFX->createStateBlock(desc); + + SceneManager::getPreRenderSignal().notify( this, &GuiRiverEditorCtrl::_prepRenderImage ); + + return true; +} + +void GuiRiverEditorCtrl::initPersistFields() +{ + addField( "DefaultWidth", TypeF32, Offset( mDefaultWidth, GuiRiverEditorCtrl ) ); + addField( "DefaultDepth", TypeF32, Offset( mDefaultDepth, GuiRiverEditorCtrl ) ); + addField( "DefaultNormal", TypePoint3F,Offset( mDefaultNormal, GuiRiverEditorCtrl ) ); + addField( "HoverSplineColor", TypeColorI, Offset( mHoverSplineColor, GuiRiverEditorCtrl ) ); + addField( "SelectedSplineColor", TypeColorI, Offset( mSelectedSplineColor, GuiRiverEditorCtrl ) ); + addField( "HoverNodeColor", TypeColorI, Offset( mHoverNodeColor, GuiRiverEditorCtrl ) ); + addField( "isDirty", TypeBool, Offset( mIsDirty, GuiRiverEditorCtrl ) ); + //addField( "MoveNodeCursor", TYPEID< SimObject >(), Offset( mMoveNodeCursor, GuiRiverEditorCtrl) ); + //addField( "AddNodeCursor", TYPEID< SimObject >(), Offset( mAddNodeCursor, GuiRiverEditorCtrl) ); + //addField( "InsertNodeCursor", TYPEID< SimObject >(), Offset( mInsertNodeCursor, GuiRiverEditorCtrl) ); + //addField( "ResizeNodeCursor", TYPEID< SimObject >(), Offset( mResizeNodeCursor, GuiRiverEditorCtrl) ); + + Parent::initPersistFields(); +} + +void GuiRiverEditorCtrl::onSleep() +{ + Parent::onSleep(); + + mMode = mSelectRiverMode; + mHoverNode = -1; + mHoverRiver = NULL; + setSelectedNode(-1); + //mSelRiver = NULL; + //mSelNode = -1; +} + +void GuiRiverEditorCtrl::get3DCursor( GuiCursor *&cursor, + bool &visible, + const Gui3DMouseEvent &event_ ) +{ + //cursor = mAddNodeCursor; + //visible = false; + + cursor = NULL; + visible = false; + + GuiCanvas *root = getRoot(); + if ( !root ) + return; + + S32 currCursor = PlatformCursorController::curArrow; + + if ( root->mCursorChanged == currCursor ) + return; + + PlatformWindow *window = root->getPlatformWindow(); + PlatformCursorController *controller = window->getCursorController(); + + // We've already changed the cursor, + // so set it back before we change it again. + if( root->mCursorChanged != -1) + controller->popCursor(); + + // Now change the cursor shape + controller->pushCursor(currCursor); + root->mCursorChanged = currCursor; +} + +void GuiRiverEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event) +{ + + + mGizmo->on3DMouseDown( event ); + + if ( !isFirstResponder() ) + setFirstResponder(); + + // Get the raycast collision position + Point3F tPos; + if ( !getStaticPos( event, tPos ) ) + return; + + mouseLock(); + + // Construct a LineSegment from the camera position to 1000 meters away in + // the direction clicked. + // If that segment hits the terrain, truncate the ray to only be that length. + + // We will use a LineSegment/Sphere intersection test to determine if a RiverNode + // was clicked. + + Point3F startPnt = event.pos; + Point3F endPnt = event.pos + event.vec * 1000.0f; + + RayInfo ri; + + if ( gServerContainer.castRay(startPnt, endPnt, StaticShapeObjectType, &ri) ) + endPnt = ri.point; + + River *riverPtr = NULL; + River *clickedRiverPtr = NULL; + + // Did we click on a river? check current selection first + U32 insertNodeIdx = -1; + Point3F collisionPnt; + if ( mSelRiver != NULL && mSelRiver->collideRay( event.pos, event.vec, &insertNodeIdx, &collisionPnt ) ) + { + clickedRiverPtr = mSelRiver; + } + else + { + for ( SimSetIterator iter(mRiverSet); *iter; ++iter ) + { + riverPtr = static_cast( *iter ); + + // Do not select or edit a River within a Prefab. + if ( Prefab::getPrefabByChild(riverPtr) ) + continue; + + if ( riverPtr->collideRay( event.pos, event.vec, &insertNodeIdx, &collisionPnt ) ) + { + clickedRiverPtr = riverPtr; + break; + } + } + } + + // Did we click on a riverNode? + bool nodeClicked = false; + S32 clickedNodeIdx = -1; + F32 clickedNodeDist = mNodeSphereRadius; + + // If we clicked on the currently selected river, only scan its nodes + if ( mSelRiver != NULL && clickedRiverPtr == mSelRiver ) + { + for ( U32 i = 0; i < mSelRiver->mNodes.size(); i++ ) + { + const Point3F &nodePos = mSelRiver->mNodes[i].point; + + Point3F screenPos; + project( nodePos, &screenPos ); + + F32 dist = ( event.mousePoint - Point2I(screenPos.x, screenPos.y) ).len(); + if ( dist < clickedNodeDist ) + { + clickedNodeDist = dist; + clickedNodeIdx = i; + insertNodeIdx = i; + nodeClicked = true; + } + } + } + else + { + for ( SimSetIterator iter(mRiverSet); *iter; ++iter ) + { + riverPtr = static_cast( *iter ); + + // Do not select or edit a River within a Prefab. + if ( Prefab::getPrefabByChild(riverPtr) ) + continue; + + for ( U32 i = 0; i < riverPtr->mNodes.size(); i++ ) + { + const Point3F &nodePos = riverPtr->mNodes[i].point; + + Point3F screenPos; + project( nodePos, &screenPos ); + + F32 dist = ( event.mousePoint - Point2I(screenPos.x, screenPos.y) ).len(); + if ( dist < clickedNodeDist ) + { + // we found a hit! + clickedNodeDist = dist; + clickedNodeIdx = i; + insertNodeIdx = i; + nodeClicked = true; + clickedRiverPtr = riverPtr; + } + } + } + } + + // shortcuts + bool dblClick = ( event.mouseClickCount > 1 ); + if( dblClick ) + { + if( mMode == mSelectRiverMode ) + { + setMode( mAddRiverMode, true ); + return; + } + if( mMode == mAddNodeMode ) + { + // Delete the node attached to the cursor. + deleteSelectedNode(); + mMode = mAddRiverMode; + return; + } + } + + //this check is here in order to bounce back from deleting a whole road with ctrl+z + //this check places the editor back into addrivermode + if ( mMode == mAddNodeMode ) + { + if ( !mSelRiver ) + mMode = mAddRiverMode; + } + + if ( mMode == mSelectRiverMode ) + { + // Did not click on a River or a node. + if ( !clickedRiverPtr ) + { + setSelectedRiver( NULL ); + setSelectedNode( -1 ); + + return; + } + + // Clicked on a River that wasn't the currently selected River. + if ( clickedRiverPtr != mSelRiver ) + { + setSelectedRiver( clickedRiverPtr ); + setSelectedNode( clickedNodeIdx ); + return; + } + + // Clicked on a node in the currently selected River that wasn't + // the currently selected node. + if ( nodeClicked ) + { + setSelectedNode( clickedNodeIdx ); + return; + } + } + else if ( mMode == mAddRiverMode ) + { + if ( nodeClicked ) + { + // A double-click on a node in Normal mode means set AddNode mode. + if ( clickedNodeIdx == 0 ) + { + setSelectedRiver( clickedRiverPtr ); + setSelectedNode( clickedNodeIdx ); + + mAddNodeIdx = clickedNodeIdx; + mMode = mAddNodeMode; + + mSelNode = mSelRiver->insertNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal, mAddNodeIdx ); + mIsDirty = true; + + return; + } + else if ( clickedNodeIdx == clickedRiverPtr->mNodes.size() - 1 ) + { + setSelectedRiver( clickedRiverPtr ); + setSelectedNode( clickedNodeIdx ); + + mAddNodeIdx = U32_MAX; + mMode = mAddNodeMode; + + mSelNode = mSelRiver->addNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal); + mIsDirty = true; + setSelectedNode( mSelNode ); + + return; + } + } + + if ( !isMethod( "createRiver" ) ) + { + Con::errorf( "GuiRiverEditorCtrl::on3DMouseDown - createRiver method does not exist." ); + return; + } + + const char *res = Con::executef( this, "createRiver" ); + + River *newRiver; + if ( !Sim::findObject( res, newRiver ) ) + { + Con::errorf( "GuiRiverEditorCtrl::on3DMouseDown - createRiver method did not return a river object." ); + return; + } + + // Add to MissionGroup + SimGroup *missionGroup; + if ( !Sim::findObject( "MissionGroup", missionGroup ) ) + Con::errorf( "GuiRiverEditorCtrl - could not find MissionGroup to add new River" ); + else + missionGroup->addObject( newRiver ); + + Point3F pos( endPnt ); + pos.z += mDefaultDepth * 0.5f; + + newRiver->insertNode( pos, mDefaultWidth, mDefaultDepth, mDefaultNormal, 0 ); + U32 newNode = newRiver->insertNode( pos, mDefaultWidth, mDefaultDepth, mDefaultNormal, 1 ); + + // Always add to the end of the road, the first node is the start. + mAddNodeIdx = U32_MAX; + + setSelectedRiver( newRiver ); + setSelectedNode( newNode ); + + mMode = mAddNodeMode; + + // Disable the hover node while in addNodeMode, we + // don't want some random node enlarged. + mHoverNode = -1; + + // Grab the mission editor undo manager. + UndoManager *undoMan = NULL; + if ( !Sim::findObject( "EUndoManager", undoMan ) ) + { + Con::errorf( "GuiMeshRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" ); + return; + } + + // Create the UndoAction. + MECreateUndoAction *action = new MECreateUndoAction("Create MeshRoad"); + action->addObject( newRiver ); + + // Submit it. + undoMan->addAction( action ); + + return; + } + else if ( mMode == mAddNodeMode ) + { + // Oops the road got deleted, maybe from an undo action? + // Back to NormalMode. + if ( mSelRiver ) + { + // A double-click on a node in Normal mode means set AddNode mode. + if ( clickedNodeIdx == 0 ) + { + submitUndo( "Add Node" ); + mAddNodeIdx = clickedNodeIdx; + mMode = mAddNodeMode; + mSelNode = mSelRiver->insertNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal, mAddNodeIdx ); + mIsDirty = true; + setSelectedNode( mSelNode ); + + return; + } + else + { + if( clickedRiverPtr && clickedNodeIdx == clickedRiverPtr->mNodes.size() - 1 ) + { + submitUndo( "Add Node" ); + mAddNodeIdx = U32_MAX; + mMode = mAddNodeMode; + U32 newNode = mSelRiver->addNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal); + mIsDirty = true; + setSelectedNode( newNode ); + + return; + } + else + { + submitUndo( "Insert Node" ); + // A single-click on empty space while in + // AddNode mode means insert / add a node. + //submitUndo( "Add Node" ); + //F32 width = mSelRiver->getNodeWidth( mSelNode ); + U32 newNode = mSelRiver->insertNode( tPos, mDefaultWidth, mDefaultDepth, mDefaultNormal, mAddNodeIdx); + mIsDirty = true; + setSelectedNode( newNode ); + + return; + } + } + } + } + else if ( mMode == mInsertPointMode && mSelRiver != NULL ) + { + if ( clickedRiverPtr == mSelRiver ) + { + // NOTE: I guess we have to determine the if the clicked ray intersects a road but not a specific node... + // in order to handle inserting nodes in the same way as for DecalRoad + + U32 prevNodeIdx = insertNodeIdx; + U32 nextNodeIdx = ( prevNodeIdx + 1 > mSelRiver->mNodes.size() - 1 ) ? prevNodeIdx : prevNodeIdx + 1; + + const RiverNode &prevNode = mSelRiver->mNodes[prevNodeIdx]; + const RiverNode &nextNode = mSelRiver->mNodes[nextNodeIdx]; + + F32 width = ( prevNode.width + nextNode.width ) * 0.5f; + F32 depth = ( prevNode.depth + nextNode.depth ) * 0.5f; + Point3F normal = ( prevNode.normal + nextNode.normal ) * 0.5f; + normal.normalize(); + + submitUndo( "Insert Node" ); + U32 newNode = mSelRiver->insertNode( collisionPnt, width, depth, normal, insertNodeIdx + 1 ); + mIsDirty = true; + setSelectedNode( newNode ); + + return; + } + } + else if ( mMode == mRemovePointMode && mSelRiver != NULL ) + { + if ( nodeClicked && clickedRiverPtr == mSelRiver ) + { + setSelectedNode( clickedNodeIdx ); + deleteSelectedNode(); + return; + } + } + else if ( mMode == mMovePointMode ) + { + if ( nodeClicked && clickedRiverPtr == mSelRiver ) + { + setSelectedNode( clickedNodeIdx ); + return; + } + } + else if ( mMode == mScalePointMode ) + { + if ( nodeClicked && clickedRiverPtr == mSelRiver ) + { + setSelectedNode( clickedNodeIdx ); + return; + } + } + else if ( mMode == mRotatePointMode ) + { + if ( nodeClicked && clickedRiverPtr == mSelRiver ) + { + setSelectedNode( clickedNodeIdx ); + return; + } + } +} + +void GuiRiverEditorCtrl::on3DRightMouseDown(const Gui3DMouseEvent & event) +{ + //mIsPanning = true; +} + +void GuiRiverEditorCtrl::on3DRightMouseUp(const Gui3DMouseEvent & event) +{ + //mIsPanning = false; +} + +void GuiRiverEditorCtrl::on3DMouseUp(const Gui3DMouseEvent & event) +{ + // Keep the Gizmo up to date. + mGizmo->on3DMouseUp( event ); + + mStartWidth = -1.0f; + mStartHeight = -1.0f; + mSavedDrag = false; + + mouseUnlock(); +} + +void GuiRiverEditorCtrl::on3DMouseMove(const Gui3DMouseEvent & event) +{ + if ( mSelRiver != NULL && mMode == mAddNodeMode ) + { + Point3F pos; + if ( getStaticPos( event, pos ) ) + { + pos.z += mSelRiver->getNodeDepth(mSelNode) * 0.5f; + mSelRiver->setNodePosition( mSelNode, pos ); + mIsDirty = true; + } + + return; + } + + if ( mSelRiver != NULL && mSelNode != -1 ) + mGizmo->on3DMouseMove( event ); + + // Is cursor hovering over a river? + if ( mMode == mSelectRiverMode ) + { + mHoverRiver = NULL; + + Point3F startPnt = event.pos; + Point3F endPnt = event.pos + event.vec * 1000.0f; + + RayInfo ri; + + if ( gServerContainer.castRay(startPnt, endPnt, StaticShapeObjectType, &ri) ) + endPnt = ri.point; + + River *pRiver = NULL; + + for ( SimSetIterator iter(mRiverSet); *iter; ++iter ) + { + pRiver = static_cast( *iter ); + + // Do not select or edit a River within a Prefab. + if ( Prefab::getPrefabByChild(pRiver) ) + continue; + + if ( pRiver->collideRay( event.pos, event.vec ) ) + { + mHoverRiver = pRiver; + break; + } + } + } + + // Is cursor hovering over a RiverNode? + if ( mHoverRiver ) + { + River *pRiver = mHoverRiver; + + S32 hoverNodeIdx = -1; + F32 hoverNodeDist = mNodeSphereRadius; + + //for ( SimSetIterator iter(mRiverSet); *iter; ++iter ) + //{ + // River *pRiver = static_cast( *iter ); + + for ( U32 i = 0; i < pRiver->mNodes.size(); i++ ) + { + const Point3F &nodePos = pRiver->mNodes[i].point; + + Point3F screenPos; + project( nodePos, &screenPos ); + + F32 dist = ( event.mousePoint - Point2I(screenPos.x, screenPos.y) ).len(); + if ( dist < hoverNodeDist ) + { + // we found a hit! + hoverNodeDist = dist; + hoverNodeIdx = i; + } + } + //} + + mHoverNode = hoverNodeIdx; + } +} + +void GuiRiverEditorCtrl::on3DMouseDragged(const Gui3DMouseEvent & event) +{ + // Drags are only used to transform nodes + if ( !mSelRiver || mSelNode == -1 || + ( mMode != mMovePointMode && mMode != mScalePointMode && mMode != mRotatePointMode ) ) + return; + + // If we haven't already saved, + // save an undo action to get back to this state, + // before we make any modifications to the selected node. + if ( !mSavedDrag ) + { + submitUndo( "Modify Node" ); + mSavedDrag = true; + } + + // Let the gizmo handle the drag, eg, modify its transforms + mGizmo->on3DMouseDragged( event ); + if ( mGizmo->isDirty() ) + { + Point3F pos = mGizmo->getPosition(); + Point3F scale = mGizmo->getScale(); + const MatrixF &mat = mGizmo->getTransform(); + VectorF normal; + mat.getColumn( 2, &normal ); + + mSelRiver->setNode( pos, scale.x, scale.z, normal, mSelNode ); + mIsDirty = true; + } + Con::executef( this, "onNodeModified", Con::getIntArg(mSelNode) ); + /* + // If we are just starting a new drag, + // we need to save the starting screen position of the mouse, + // and the starting position of the selected node. + if ( mStartDragMousePoint == InvalidMousePoint ) + { + mStartDragMousePoint = event.mousePoint; + mStartDragNodePos = mSelRiver->getNodePosition( mSelNode ); + } + + MathUtils::Line clickLine; + clickLine.p = event.pos; + clickLine.d = event.vec; + + MathUtils::Line axisLine; + axisLine.p = mStartDragNodePos; + axisLine.d = mGizmo.selectionToAxisVector( mGizmoSelection ); + + MathUtils::LineSegment segment; + + MathUtils::mShortestSegmentBetweenLines( clickLine, axisLine, segment ); + + // Segment.p1 is the closest point on the axis line, + // We want to put the selected gizmo handle at that point, + // So calculate the offset from the handle to the centerPoint to + // determine the gizmo's position. + mSelRiver->setNodePosition( mSelNode, segment.p1 ); + */ + + /* + // Convert the delta (dragged mouse distance) from screen space + // into world space. + Point2I deltaScreen = event.mousePoint - mStartDragMousePoint; + + F32 worldDist = ( event.pos - mStartDragNodePos ).len(); + + Point2F deltaWorld; + deltaWorld.x = GFX->unprojectRadius( worldDist, deltaScreen.x ); + deltaWorld.y = GFX->unprojectRadius( worldDist, deltaScreen.y ); + + // Now modify the selected node depending on the kind of operation we are doing. + if ( mGizmoSelection == Gizmo::Axis_X ) + { + Point3F newPos = mStartDragNodePos; + newPos.x += deltaWorld.x; + mSelRiver->setNodePosition( mSelNode, newPos ); + } + else if ( mGizmoSelection == Gizmo::Axis_Y ) + { + Point3F newPos = mStartDragNodePos; + newPos.y += deltaWorld.x; + mSelRiver->setNodePosition( mSelNode, newPos ); + } + else if ( mGizmoSelection == Gizmo::Axis_Z ) + { + Point3F newPos = mStartDragNodePos; + newPos.z += deltaWorld.y; + mSelRiver->setNodePosition( mSelNode, newPos ); + } + */ + + /* + F32 height = mStartHeight + deltaWorldX; + Con::printf( "height = %g", height ); + + mSelRiver->setNodeHeight( mSelNode, height ); + + Con::executef( this, "onNodeHeightModified", Con::getFloatArg(height) ); + + + if ( event.modifier & SI_PRIMARY_CTRL ) + { + //Point3F tPos; + //if ( !getStaticPos( event, tPos ) ) + // return; + + if ( mStartHeight == -1.0f ) + { + mStartHeight = mSelRiver->mNodes[mSelNode].point.z; + + mStartX = event.mousePoint.x; + mStartWorld = mSelRiver->mNodes[mSelNode].point; + } + + S32 deltaScreenX = event.mousePoint.x - mStartX; + + F32 worldDist = ( event.pos - mStartWorld ).len(); + + F32 deltaWorldX = GFX->unprojectRadius( worldDist, deltaScreenX ); + + F32 height = mStartHeight + deltaWorldX; + Con::printf( "height = %g", height ); + + mSelRiver->setNodeHeight( mSelNode, height ); + + Con::executef( this, "onNodeHeightModified", Con::getFloatArg(height) ); + } + else if ( event.modifier & SI_SHIFT ) + { + Point3F tPos; + if ( !getStaticPos( event, tPos ) ) + return; + + if ( mStartWidth == -1.0f ) + { + mStartWidth = mSelRiver->mNodes[mSelNode].width; + + mStartX = event.mousePoint.x; + mStartWorld = tPos; + } + + S32 deltaScreenX = event.mousePoint.x - mStartX; + + F32 worldDist = ( event.pos - mStartWorld ).len(); + + F32 deltaWorldX = GFX->unprojectRadius( worldDist, deltaScreenX ); + + F32 width = mStartWidth + deltaWorldX; + + mSelRiver->setNodeWidth( mSelNode, width ); + + Con::executef( this, "onNodeWidthModified", Con::getFloatArg(width) ); + } + else + { + Point3F tPos; + if ( !getStaticPos( event, tPos ) ) + return; + else if ( mGizmoSelection == Gizmo::Axis_Y ) + { + Point3F newPos = mStartDragNodePos; + newPos.y += deltaWorld.x; + mSelRiver->setNodePosition( mSelNode, newPos ); + } + mSelRiver->setNodePosition( mSelNode, tPos ); + } + */ +} + +void GuiRiverEditorCtrl::on3DMouseEnter(const Gui3DMouseEvent & event) +{ + // nothing to do +} + +void GuiRiverEditorCtrl::on3DMouseLeave(const Gui3DMouseEvent & event) +{ + // nothing to do +} + +bool GuiRiverEditorCtrl::onKeyDown(const GuiEvent& event) +{ + if( event.keyCode == KEY_RETURN && mMode == mAddNodeMode ) + { + // Delete the node attached to the cursor. + deleteSelectedNode(); + mMode = mAddRiverMode; + return true; + } + + return false; +} + +void GuiRiverEditorCtrl::updateGuiInfo() +{ + // nothing to do +} + +void GuiRiverEditorCtrl::onRender( Point2I offset, const RectI &updateRect ) +{ + PROFILE_SCOPE( GuiRiverEditorCtrl_OnRender ); + + Parent::onRender( offset, updateRect ); + return; +} + +void GuiRiverEditorCtrl::renderScene(const RectI & updateRect) +{ + //GFXDrawUtil *drawer = GFX->getDrawUtil(); + + GFX->setStateBlock( mZDisableSB ); + + // get the projected size... + GameConnection* connection = GameConnection::getConnectionToServer(); + if(!connection) + return; + + // Grab the camera's transform + MatrixF mat; + connection->getControlCameraTransform(0, &mat); + + // Get the camera position + Point3F camPos; + mat.getColumn(3,&camPos); + + if ( mHoverRiver && mHoverRiver != mSelRiver ) + { + _drawRiverSpline( mHoverRiver, mHoverSplineColor ); + } + + if ( mSelRiver ) + { + _drawRiverSpline( mSelRiver, mSelectedSplineColor ); + + // Render Gizmo for selected node if were in either of the three transform modes + if ( mSelNode != -1 && ( mMode == mMovePointMode || mMode == mScalePointMode || mMode == mRotatePointMode ) ) + { + if( mMode == mMovePointMode ) + { + mGizmo->getProfile()->mode = MoveMode; + } + else if( mMode == mScalePointMode ) + { + mGizmo->getProfile()->mode = ScaleMode; + } + else if( mMode == mRotatePointMode ) + { + mGizmo->getProfile()->mode = RotateMode; + } + + const RiverNode &node = mSelRiver->mNodes[mSelNode]; + + MatrixF objMat = mSelRiver->getNodeTransform(mSelNode); + Point3F objScale( node.width, 1.0f, node.depth ); + Point3F worldPos = node.point; + + mGizmo->set( objMat, worldPos, objScale ); + + mGizmo->renderGizmo( mLastCameraQuery.cameraMatrix, mLastCameraQuery.fov ); + + // Render Gizmo text + //mGizmo->renderText( mSaveViewport, mSaveModelview, mSaveProjection ); + } + } + + // Now draw all the 2d stuff! + GFX->setClipRect(updateRect); + + // Draw Control nodes for selecting and highlighted rivers + if ( mHoverRiver ) + _drawRiverControlNodes( mHoverRiver, mHoverSplineColor ); + if ( mSelRiver ) + _drawRiverControlNodes( mSelRiver, mSelectedSplineColor ); +} + +void GuiRiverEditorCtrl::_drawRiverSpline( River *river, const ColorI &color ) +{ + if ( river->mSlices.size() <= 1 ) + return; + + if ( River::smShowSpline ) + { + // Render the River center-line + PrimBuild::color( color ); + PrimBuild::begin( GFXLineStrip, river->mSlices.size() ); + for ( U32 i = 0; i < river->mSlices.size(); i++ ) + { + PrimBuild::vertex3fv( river->mSlices[i].p1 ); + } + PrimBuild::end(); + } + + if ( River::smWireframe ) + { + // Left-side line + PrimBuild::color3i( 100, 100, 100 ); + PrimBuild::begin( GFXLineStrip, river->mSlices.size() ); + for ( U32 i = 0; i < river->mSlices.size(); i++ ) + { + PrimBuild::vertex3fv( river->mSlices[i].p0 ); + } + PrimBuild::end(); + + // Right-side line + PrimBuild::begin( GFXLineStrip, river->mSlices.size() ); + for ( U32 i = 0; i < river->mSlices.size(); i++ ) + { + PrimBuild::vertex3fv( river->mSlices[i].p2 ); + } + PrimBuild::end(); + + // Cross-sections + PrimBuild::begin( GFXLineList, river->mSlices.size() * 2 ); + for ( U32 i = 0; i < river->mSlices.size(); i++ ) + { + PrimBuild::vertex3fv( river->mSlices[i].p0 ); + PrimBuild::vertex3fv( river->mSlices[i].p2 ); + } + PrimBuild::end(); + } + // Segment +} + +void GuiRiverEditorCtrl::_drawRiverControlNodes( River *river, const ColorI &color ) +{ + if ( !River::smShowSpline ) + return; + + RectI bounds = getBounds(); + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + + bool isSelected = ( river == mSelRiver ); + bool isHighlighted = ( river == mHoverRiver ); + + for ( U32 i = 0; i < river->mNodes.size(); i++ ) + { + if ( false && isSelected && mSelNode == i ) + continue; + + const Point3F &wpos = river->mNodes[i].point; + + Point3F spos; + project( wpos, &spos ); + + if ( spos.z > 1.0f ) + continue; + + Point2I posi; + posi.x = spos.x; + posi.y = spos.y; + + if ( !bounds.pointInRect( posi ) ) + continue; + + ColorI theColor = color; + Point2I nodeHalfSize = mNodeHalfSize; + + if ( isHighlighted && mHoverNode == i ) + { + //theColor = mHoverNodeColor; + nodeHalfSize += Point2I(2,2); + } + + if ( isSelected ) + { + if ( mSelNode == i ) + { + theColor.set(0,0,255); + } + else if ( i == 0 ) + { + theColor.set(0,255,0); + } + else if ( i == river->mNodes.size() - 1 ) + { + theColor.set(255,0,0); + } + } + + drawer->drawRectFill( posi - nodeHalfSize, posi + nodeHalfSize, theColor ); + } +} + +bool GuiRiverEditorCtrl::getStaticPos( const Gui3DMouseEvent & event, Point3F &tpos ) +{ + // Find clicked point on the terrain + + Point3F startPnt = event.pos; + Point3F endPnt = event.pos + event.vec * 1000.0f; + + RayInfo ri; + bool hit; + + hit = gServerContainer.castRay(startPnt, endPnt, StaticShapeObjectType, &ri); + tpos = ri.point; + + return hit; +} + +void GuiRiverEditorCtrl::deleteSelectedNode() +{ + if ( !mSelRiver || mSelNode == -1 ) + return; + + // If the River has only two nodes remaining, + // delete the whole River. + if ( mSelRiver->mNodes.size() <= 2 ) + { + deleteSelectedRiver( mMode != mAddNodeMode ); + } + else + { + if ( mMode != mAddNodeMode ) + submitUndo( "Delete Node" ); + + // Delete the SelectedNode of the SelectedRiver + mSelRiver->deleteNode(mSelNode); + mIsDirty = true; + + // We deleted the Node but not the River (it has nodes left) + // so decrement the currently selected node. + if ( mSelRiver->mNodes.size() <= mSelNode ) + setSelectedNode( mSelNode - 1 ); + else + { + // force gizmo to update to the selected nodes position + // the index didn't change but the node it refers to did. + U32 i = mSelNode; + mSelNode = -1; + setSelectedNode( i ); + } + } + + // If you were in addNodeMode, + // deleting a node should ends it. + //mMode = smNormalMode; +} + +void GuiRiverEditorCtrl::deleteSelectedRiver( bool undoAble ) +{ + AssertFatal( mSelRiver != NULL, "GuiRiverEditorCtrl::deleteSelectedRiver() - No River IS selected" ); + + // Not undoAble? Just delete it. + if ( !undoAble ) + { + mSelRiver->deleteObject(); + mIsDirty = true; + Con::executef( this, "onRiverSelected" ); + mSelNode = -1; + + return; + } + + // Grab the mission editor undo manager. + UndoManager *undoMan = NULL; + if ( !Sim::findObject( "EUndoManager", undoMan ) ) + { + // Couldn't find it? Well just delete the River. + Con::errorf( "GuiRiverEditorCtrl::on3DMouseDown() - EUndoManager not found!" ); + return; + } + else + { + // Create the UndoAction. + MEDeleteUndoAction *action = new MEDeleteUndoAction("Deleted River"); + action->deleteObject( mSelRiver ); + mIsDirty = true; + + // Submit it. + undoMan->addAction( action ); + } + + // ScriptCallback with 'NULL' parameter for no River currently selected. + Con::executef( this, "onRiverSelected" ); + + // Clear the SelectedNode (it has been deleted along with the River). + setSelectedNode( -1 ); + mSelNode = -1; + + // SelectedRiver is a SimObjectPtr and will be NULL automatically. +} + +void GuiRiverEditorCtrl::setMode( String mode, bool sourceShortcut = false ) +{ + mMode = mode; + + if( sourceShortcut ) + Con::executef( this, "paletteSync", mode ); +} + +void GuiRiverEditorCtrl::setSelectedRiver( River *river ) +{ + mSelRiver = river; + + if ( mSelRiver != NULL ) + Con::executef( this, "onRiverSelected", river->getIdString() ); + else + Con::executef( this, "onRiverSelected" ); + + if ( mSelRiver != river ) + setSelectedNode(-1); +} + +void GuiRiverEditorCtrl::setNodeWidth( F32 width ) +{ + if ( mSelRiver && mSelNode != -1 ) + { + mSelRiver->setNodeWidth( mSelNode, width ); + mIsDirty = true; + } +} + +F32 GuiRiverEditorCtrl::getNodeWidth() +{ + if ( mSelRiver && mSelNode != -1 ) + return mSelRiver->getNodeWidth( mSelNode ); + + return 0.0f; +} + +void GuiRiverEditorCtrl::setNodeDepth(F32 depth) +{ + if ( mSelRiver && mSelNode != -1 ) + { + mSelRiver->setNodeDepth( mSelNode, depth ); + mIsDirty = true; + } +} + +F32 GuiRiverEditorCtrl::getNodeDepth() +{ + if ( mSelRiver && mSelNode != -1 ) + return mSelRiver->getNodeDepth( mSelNode ); + + return 0.0f; +} + +void GuiRiverEditorCtrl::setNodePosition( Point3F pos ) +{ + if ( mSelRiver && mSelNode != -1 ) + { + mSelRiver->setNodePosition( mSelNode, pos ); + mIsDirty = true; + } +} + +Point3F GuiRiverEditorCtrl::getNodePosition() +{ + if ( mSelRiver && mSelNode != -1 ) + return mSelRiver->getNodePosition( mSelNode ); + + return Point3F( 0, 0, 0 ); +} + +void GuiRiverEditorCtrl::setNodeNormal( const VectorF &normal ) +{ + if ( mSelRiver && mSelNode != -1 ) + { + mSelRiver->setNodeNormal( mSelNode, normal ); + mIsDirty = true; + } +} + +VectorF GuiRiverEditorCtrl::getNodeNormal() +{ + if ( mSelRiver && mSelNode != -1 ) + return mSelRiver->getNodeNormal( mSelNode ); + + return VectorF::Zero; +} + +void GuiRiverEditorCtrl::setSelectedNode( S32 node ) +{ + //if ( mSelNode == node ) + // return; + + mSelNode = node; + if ( mSelNode != -1 ) + { + const RiverNode &node = mSelRiver->mNodes[mSelNode]; + + MatrixF objMat = mSelRiver->getNodeTransform(mSelNode); + Point3F objScale( node.width, 1.0f, node.depth ); + Point3F worldPos = node.point; + + mGizmo->set( objMat, worldPos, objScale ); + } + + if ( mSelNode != -1 ) + Con::executef( this, "onNodeSelected", Con::getIntArg(mSelNode) ); + else + Con::executef( this, "onNodeSelected", Con::getIntArg(-1) ); +} + +void GuiRiverEditorCtrl::submitUndo( const UTF8 *name ) +{ + // Grab the mission editor undo manager. + UndoManager *undoMan = NULL; + if ( !Sim::findObject( "EUndoManager", undoMan ) ) + { + Con::errorf( "GuiRiverEditorCtrl::submitUndo() - EUndoManager not found!" ); + return; + } + + // Setup the action. + GuiRiverEditorUndoAction *action = new GuiRiverEditorUndoAction( name ); + + action->mObjId = mSelRiver->getId(); + action->mMetersPerSegment = mSelRiver->mMetersPerSegment; + action->mSegmentsPerBatch = mSelRiver->mSegmentsPerBatch; + action->mRiverEditor = this; + + for( U32 i = 0; i < mSelRiver->mNodes.size(); i++ ) + { + action->mNodes.push_back( mSelRiver->mNodes[i] ); + } + + undoMan->addAction( action ); +} + +void GuiRiverEditorCtrl::_prepRenderImage( SceneManager* sceneGraph, const SceneRenderState* state ) +{ + if ( isAwake() && River::smEditorOpen && mSelRiver ) + { + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->type = RenderPassManager::RIT_Editor; + ri->renderDelegate.bind( this, &GuiRiverEditorCtrl::_renderSelectedRiver ); + ri->defaultKey = 100; + state->getRenderPass()->addInst( ri ); + } +} + +void GuiRiverEditorCtrl::_renderSelectedRiver( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *matInst ) +{ + if ( !mSelRiver || !River::smEditorOpen) + return; + + GFXTransformSaver saver; + + GFX->setStateBlock( mZEnableSB ); + + if ( River::smShowWalls && mSelRiver->mSlices.size() > 1 ) + { + Point3F offset(0,0,1); + + // Render the River volume + PrimBuild::begin( GFXTriangleList, 18 * mSelRiver->mSlices.size() - 1 ); + + for ( U32 i = 0; i < mSelRiver->mSlices.size() - 1; i++ ) + { + const RiverSlice &slice = mSelRiver->mSlices[i]; + const RiverSlice &nextSlice = mSelRiver->mSlices[i+1]; + + // Top face + //drawer->drawQuad( slice.p0, nextSlice.p0, nextSlice.p2, slice.p2, colorRed, true ); + //PrimBuild::color3i( 0, 0, 255 ); + //PrimBuild::vertex3fv( slice.p0 ); + //PrimBuild::vertex3fv( nextSlice.p0 ); + //PrimBuild::vertex3fv( nextSlice.p2 ); + //PrimBuild::vertex3fv( slice.p0 ); + //PrimBuild::vertex3fv( nextSlice.p2 ); + //PrimBuild::vertex3fv( slice.p2 ); + + // Bottom face + PrimBuild::color3i( 0, 255, 0 ); + PrimBuild::vertex3fv( slice.pb0 ); + PrimBuild::vertex3fv( nextSlice.pb0 ); + PrimBuild::vertex3fv( nextSlice.pb2 ); + PrimBuild::vertex3fv( slice.pb0 ); + PrimBuild::vertex3fv( nextSlice.pb2 ); + PrimBuild::vertex3fv( slice.pb2 ); + + // Left face + PrimBuild::color3i( 255, 0, 0 ); + PrimBuild::vertex3fv( slice.pb0 ); + PrimBuild::vertex3fv( nextSlice.pb0 ); + PrimBuild::vertex3fv( nextSlice.p0 ); + PrimBuild::vertex3fv( slice.pb0 ); + PrimBuild::vertex3fv( nextSlice.p0 ); + PrimBuild::vertex3fv( slice.p0 ); + + // Right face + PrimBuild::color3i( 255, 0, 0 ); + PrimBuild::vertex3fv( slice.p2 ); + PrimBuild::vertex3fv( nextSlice.p2 ); + PrimBuild::vertex3fv( nextSlice.pb2 ); + PrimBuild::vertex3fv( slice.p2 ); + PrimBuild::vertex3fv( nextSlice.pb2 ); + PrimBuild::vertex3fv( slice.pb2 ); + } + + PrimBuild::end(); + } +} + +ConsoleMethod( GuiRiverEditorCtrl, deleteNode, void, 2, 2, "deleteNode()" ) +{ + object->deleteSelectedNode(); +} + +ConsoleMethod( GuiRiverEditorCtrl, getMode, const char*, 2, 2, "" ) +{ + return object->getMode(); +} + +ConsoleMethod( GuiRiverEditorCtrl, setMode, void, 3, 3, "setMode( String mode )" ) +{ + String newMode = ( argv[2] ); + object->setMode( newMode ); +} + +ConsoleMethod( GuiRiverEditorCtrl, getNodeWidth, F32, 2, 2, "" ) +{ + return object->getNodeWidth(); +} + +ConsoleMethod( GuiRiverEditorCtrl, setNodeWidth, void, 3, 3, "" ) +{ + object->setNodeWidth( dAtof(argv[2]) ); +} + +ConsoleMethod( GuiRiverEditorCtrl, getNodeDepth, F32, 2, 2, "" ) +{ + return object->getNodeDepth(); +} + +ConsoleMethod( GuiRiverEditorCtrl, setNodeDepth, void, 3, 3, "" ) +{ + object->setNodeDepth( dAtof(argv[2]) ); +} + +ConsoleMethod( GuiRiverEditorCtrl, getNodePosition, const char*, 2, 2, "" ) +{ + char* returnBuffer = Con::getReturnBuffer(256); + + dSprintf(returnBuffer, 256, "%f %f %f", + object->getNodePosition().x, object->getNodePosition().y, object->getNodePosition().z); + + return returnBuffer; +} + +ConsoleMethod( GuiRiverEditorCtrl, setNodePosition, void, 3, 3, "" ) +{ + Point3F pos; + + S32 count = dSscanf( argv[2], "%f %f %f", + &pos.x, &pos.y, &pos.z); + + if ( (count != 3) ) + { + Con::printf("Failed to parse node information \"px py pz\" from '%s'", argv[3]); + return; + } + + object->setNodePosition( pos ); +} + +ConsoleMethod( GuiRiverEditorCtrl, getNodeNormal, const char*, 2, 2, "" ) +{ + char* returnBuffer = Con::getReturnBuffer(256); + + dSprintf(returnBuffer, 256, "%f %f %f", + object->getNodeNormal().x, object->getNodeNormal().y, object->getNodeNormal().z); + + return returnBuffer; +} + +ConsoleMethod( GuiRiverEditorCtrl, setNodeNormal, void, 3, 3, "" ) +{ + VectorF normal; + + S32 count = dSscanf( argv[2], "%f %f %f", + &normal.x, &normal.y, &normal.z); + + if ( (count != 3) ) + { + Con::printf("Failed to parse node information \"px py pz\" from '%s'", argv[3]); + return; + } + + object->setNodeNormal( normal ); +} + +ConsoleMethod( GuiRiverEditorCtrl, setSelectedRiver, void, 2, 3, "" ) +{ + if ( argc == 2 ) + object->setSelectedRiver(NULL); + else + { + River *river = NULL; + if ( Sim::findObject( argv[2], river ) ) + object->setSelectedRiver(river); + } +} + +ConsoleMethod( GuiRiverEditorCtrl, getSelectedRiver, const char*, 2, 2, "" ) +{ + River *river = object->getSelectedRiver(); + if ( !river ) + return NULL; + + return river->getIdString(); +} + +ConsoleMethod( GuiRiverEditorCtrl, regenerate, void, 2, 2, "" ) +{ + River *river = object->getSelectedRiver(); + if ( river ) + river->regenerate(); +} \ No newline at end of file diff --git a/Engine/source/environment/editors/guiRiverEditorCtrl.h b/Engine/source/environment/editors/guiRiverEditorCtrl.h new file mode 100644 index 000000000..a8320eb16 --- /dev/null +++ b/Engine/source/environment/editors/guiRiverEditorCtrl.h @@ -0,0 +1,200 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIRIVEREDITORCTRL_H_ +#define _GUIRIVEREDITORCTRL_H_ + +#ifndef _EDITTSCTRL_H_ +#include "gui/worldEditor/editTSCtrl.h" +#endif +#ifndef _UNDO_H_ +#include "util/undo.h" +#endif +#ifndef _RIVER_H_ +#include "environment/river.h" +#endif +#ifndef _GIZMO_H_ +#include "gui/worldEditor/gizmo.h" +#endif + +struct ObjectRenderInst; +class SceneManager; +class SceneRenderState; +class BaseMatInstance; + + +class GuiRiverEditorCtrl : public EditTSCtrl +{ + typedef EditTSCtrl Parent; + + public: + + friend class GuiRiverEditorUndoAction; + + //static StringTableEntry smNormalMode; + //static StringTableEntry smAddNodeMode; + + String mSelectRiverMode; + String mAddRiverMode; + String mAddNodeMode; + String mInsertPointMode; + String mRemovePointMode; + String mMovePointMode; + String mScalePointMode; + String mRotatePointMode; + + GuiRiverEditorCtrl(); + ~GuiRiverEditorCtrl(); + + DECLARE_CONOBJECT(GuiRiverEditorCtrl); + + // SimObject + bool onAdd(); + static void initPersistFields(); + + // GuiControl + virtual void onSleep(); + virtual void onRender(Point2I offset, const RectI &updateRect); + + // EditTSCtrl + bool onKeyDown(const GuiEvent& event); + void get3DCursor( GuiCursor *&cursor, bool &visible, const Gui3DMouseEvent &event_ ); + void on3DMouseDown(const Gui3DMouseEvent & event); + void on3DMouseUp(const Gui3DMouseEvent & event); + void on3DMouseMove(const Gui3DMouseEvent & event); + void on3DMouseDragged(const Gui3DMouseEvent & event); + void on3DMouseEnter(const Gui3DMouseEvent & event); + void on3DMouseLeave(const Gui3DMouseEvent & event); + void on3DRightMouseDown(const Gui3DMouseEvent & event); + void on3DRightMouseUp(const Gui3DMouseEvent & event); + void updateGuiInfo(); + void renderScene(const RectI & updateRect); + + // GuiRiverEditorCtrl + bool getStaticPos( const Gui3DMouseEvent & event, Point3F &tpos ); + void deleteSelectedNode(); + void deleteSelectedRiver( bool undoAble = true ); + + void setMode( String mode, bool sourceShortcut ); + String getMode() { return mMode; } + + //void setGizmoMode( Gizmo::Mode mode ) { mGizmo->setMode( mode ); } + + void setSelectedRiver( River *river ); + River* getSelectedRiver() { return mSelRiver; }; + void setSelectedNode( S32 node ); + + F32 getNodeWidth(); + void setNodeWidth( F32 width ); + + F32 getNodeDepth(); + void setNodeDepth( F32 depth ); + + Point3F getNodePosition(); + void setNodePosition( Point3F pos ); + + VectorF getNodeNormal(); + void setNodeNormal( const VectorF &normal ); + + protected: + + void _renderSelectedRiver( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *matInst ); + void _prepRenderImage( SceneManager* sceneGraph, const SceneRenderState* sceneState ); + void _drawRiverSpline( River *river, const ColorI &color ); + void _drawRiverControlNodes( River *river, const ColorI &color ); + + void submitUndo( const UTF8 *name = "Action" ); + + GFXStateBlockRef mZDisableSB; + GFXStateBlockRef mZEnableSB; + + bool mSavedDrag; + bool mIsDirty; + + SimSet *mRiverSet; + S32 mSelNode; + S32 mHoverNode; + U32 mAddNodeIdx; + SimObjectPtr mSelRiver; + SimObjectPtr mHoverRiver; + + String mMode; + + F32 mDefaultWidth; + F32 mDefaultDepth; + VectorF mDefaultNormal; + S32 mInsertIdx; + + F32 mStartHeight; + F32 mStartWidth; + S32 mStartX; + Point3F mStartWorld; + + Point2I mNodeHalfSize; + + //Gizmo mGizmo; + + Gui3DMouseEvent mLastMouseEvent; + + F32 mNodeSphereRadius; + ColorI mNodeSphereFillColor; + ColorI mNodeSphereLineColor; + + ColorI mHoverSplineColor; + ColorI mSelectedSplineColor; + ColorI mHoverNodeColor; + + #define InvalidMousePoint Point2I(-100,-100) + Point2I mStartDragMousePoint; + + Point3F mStartDragNodePos; + + //GuiCursor *mMoveNodeCursor; + //GuiCursor *mAddNodeCursor; + //GuiCursor *mInsertNodeCursor; + //GuiCursor *mResizeNodeCursor; +}; + +class GuiRiverEditorUndoAction : public UndoAction +{ + public: + + GuiRiverEditorUndoAction( const UTF8* actionName ) : UndoAction( actionName ) + { + } + + GuiRiverEditorCtrl *mRiverEditor; + + Vector mNodes; + + SimObjectId mObjId; + F32 mMetersPerSegment; + U32 mSegmentsPerBatch; + + virtual void undo(); + virtual void redo() { undo(); } +}; + +#endif + + + diff --git a/Engine/source/environment/editors/guiRoadEditorCtrl.cpp b/Engine/source/environment/editors/guiRoadEditorCtrl.cpp new file mode 100644 index 000000000..a890c6ce2 --- /dev/null +++ b/Engine/source/environment/editors/guiRoadEditorCtrl.cpp @@ -0,0 +1,1120 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "environment/editors/guiRoadEditorCtrl.h" + +#include "console/consoleTypes.h" +#include "scene/sceneManager.h" +#include "collision/collision.h" +#include "math/util/frustum.h" +#include "gfx/gfxPrimitiveBuffer.h" +#include "gfx/gfxTextureHandle.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/gfxDrawUtil.h" +#include "gfx/primBuilder.h" +#include "T3D/gameBase/gameConnection.h" +#include "gui/core/guiCanvas.h" +#include "gui/buttons/guiButtonCtrl.h" +#include "gui/worldEditor/undoActions.h" +#include "materials/materialDefinition.h" + +IMPLEMENT_CONOBJECT(GuiRoadEditorCtrl); + +ConsoleDocClass( GuiRoadEditorCtrl, + "@brief GUI tool that makes up the Decal Road Editor\n\n" + "Editor use only.\n\n" + "@internal" +); + +GuiRoadEditorCtrl::GuiRoadEditorCtrl() +{ + // Each of the mode names directly correlates with the River Editor's + // tool palette + mSelectRoadMode = "RoadEditorSelectMode"; + mAddRoadMode = "RoadEditorAddRoadMode"; + mMovePointMode = "RoadEditorMoveMode"; + mScalePointMode = "RoadEditorScaleMode"; + mAddNodeMode = "RoadEditorAddNodeMode"; + mInsertPointMode = "RoadEditorInsertPointMode"; + mRemovePointMode = "RoadEditorRemovePointMode"; + + mMode = mSelectRoadMode; + + mRoadSet = NULL; + mSelNode = -1; + mHoverNode = -1; + mSelRoad = NULL; + mHoverRoad = NULL; + mAddNodeIdx = 0; + + mDefaultWidth = 10.0f; + mInsertIdx = -1; + + mStartWidth = -1.0f; + mStartX = 0; + + mNodeHalfSize.set(4,4); + + mHoverSplineColor.set( 255,0,0,255 ); + mSelectedSplineColor.set( 0,255,0,255 ); + mHoverNodeColor.set( 255,255,255,255 ); + + mIsDirty = false; + + mMaterialName = StringTable->insert("DefaultDecalRoadMaterial"); +} + +GuiRoadEditorCtrl::~GuiRoadEditorCtrl() +{ + // nothing to do +} + +void GuiRoadEditorUndoAction::undo() +{ + DecalRoad *road = NULL; + if ( !Sim::findObject( mObjId, road ) ) + return; + + // Temporarily save the roads current data. + String materialName = road->mMaterialName; + F32 textureLength = road->mTextureLength; + F32 breakAngle = road->mBreakAngle; + F32 segmentsPerBatch = road->mSegmentsPerBatch; + Vector nodes; + nodes.merge( road->mNodes ); + + // Restore the Road properties saved in the UndoAction + road->mMaterialName = materialName; + road->mBreakAngle = breakAngle; + road->mSegmentsPerBatch = segmentsPerBatch; + road->mTextureLength = textureLength; + road->inspectPostApply(); + + // Restore the Nodes saved in the UndoAction + road->mNodes.clear(); + for ( U32 i = 0; i < mNodes.size(); i++ ) + { + road->_addNode( mNodes[i].point, mNodes[i].width ); + } + + // Regenerate the road + road->regenerate(); + + // If applicable set the selected road and node + mRoadEditor->mSelRoad = road; + mRoadEditor->mSelNode = -1; + + // Now save the previous Road data in this UndoAction + // since an undo action must become a redo action and vice-versa + mMaterialName = materialName; + mBreakAngle = breakAngle; + mSegmentsPerBatch = segmentsPerBatch; + mTextureLength = textureLength; + + mNodes.clear(); + mNodes.merge( nodes ); +} + +bool GuiRoadEditorCtrl::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + mRoadSet = DecalRoad::getServerSet(); + + GFXStateBlockDesc desc; + desc.setCullMode( GFXCullNone ); + desc.setBlend(false); + desc.setZReadWrite( false, false ); + + mZDisableSB = GFX->createStateBlock(desc); + + return true; +} + +void GuiRoadEditorCtrl::initPersistFields() +{ + addField( "DefaultWidth", TypeF32, Offset( mDefaultWidth, GuiRoadEditorCtrl ) ); + addField( "HoverSplineColor", TypeColorI, Offset( mHoverSplineColor, GuiRoadEditorCtrl ) ); + addField( "SelectedSplineColor", TypeColorI, Offset( mSelectedSplineColor, GuiRoadEditorCtrl ) ); + addField( "HoverNodeColor", TypeColorI, Offset( mHoverNodeColor, GuiRoadEditorCtrl ) ); + addField( "isDirty", TypeBool, Offset( mIsDirty, GuiRoadEditorCtrl ) ); + addField( "materialName", TypeString, Offset( mMaterialName, GuiRoadEditorCtrl ), + "Default Material used by the Road Editor on road creation." ); + //addField( "MoveNodeCursor", TYPEID< SimObject >(), Offset( mMoveNodeCursor, GuiRoadEditorCtrl) ); + //addField( "AddNodeCursor", TYPEID< SimObject >(), Offset( mAddNodeCursor, GuiRoadEditorCtrl) ); + //addField( "InsertNodeCursor", TYPEID< SimObject >(), Offset( mInsertNodeCursor, GuiRoadEditorCtrl) ); + //addField( "ResizeNodeCursor", TYPEID< SimObject >(), Offset( mResizeNodeCursor, GuiRoadEditorCtrl) ); + + Parent::initPersistFields(); +} + +void GuiRoadEditorCtrl::onSleep() +{ + Parent::onSleep(); + + mMode = mSelectRoadMode; + mHoverNode = -1; + mHoverRoad = NULL; + setSelectedNode(-1); +} + +void GuiRoadEditorCtrl::get3DCursor( GuiCursor *&cursor, + bool &visible, + const Gui3DMouseEvent &event_ ) +{ + //cursor = mAddNodeCursor; + //visible = false; + + cursor = NULL; + visible = false; + + GuiCanvas *root = getRoot(); + if ( !root ) + return; + + S32 currCursor = PlatformCursorController::curArrow; + + if ( root->mCursorChanged == currCursor ) + return; + + PlatformWindow *window = root->getPlatformWindow(); + PlatformCursorController *controller = window->getCursorController(); + + // We've already changed the cursor, + // so set it back before we change it again. + if( root->mCursorChanged != -1) + controller->popCursor(); + + // Now change the cursor shape + controller->pushCursor(currCursor); + root->mCursorChanged = currCursor; +} + +void GuiRoadEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event) +{ + if ( !isFirstResponder() ) + setFirstResponder(); + + // Get the clicked terrain position. + Point3F tPos; + if ( !getTerrainPos( event, tPos ) ) + return; + + mouseLock(); + + // Find any road / node at the clicked position. + // TODO: handle overlapping roads/nodes somehow, cycle through them. + + DecalRoad *roadPtr = NULL; + S32 closestNodeIdx = -1; + F32 closestDist = F32_MAX; + DecalRoad *closestNodeRoad = NULL; + + // First, find the closest node in any road to the clicked position. + for ( SimSetIterator iter(mRoadSet); *iter; ++iter ) + { + roadPtr = static_cast( *iter ); + U32 idx; + if ( roadPtr->getClosestNode( tPos, idx ) ) + { + Point3F nodePos = roadPtr->getNodePosition(idx); + F32 dist = ( nodePos - tPos ).len(); + if ( dist < closestDist ) + { + closestNodeIdx = idx; + closestDist = dist; + closestNodeRoad = roadPtr; + } + } + } + + // + // Second, determine if the screen-space node rectangle + // contains the clicked position. + + bool nodeClicked = false; + S32 clickedNodeIdx = -1; + + if ( closestNodeIdx != -1 ) + { + Point3F nodePos = closestNodeRoad->getNodePosition( closestNodeIdx ); + + Point3F temp; + project( nodePos, &temp ); + Point2I screenPos( temp.x, temp.y ); + + RectI nodeRect( screenPos - mNodeHalfSize, mNodeHalfSize * 2 ); + + nodeClicked = nodeRect.pointInRect( event.mousePoint ); + if ( nodeClicked ) + clickedNodeIdx = closestNodeIdx; + } + + // + // Determine the clickedRoad + // + DecalRoad *clickedRoadPtr = NULL; + U32 insertNodeIdx = 0; + + if ( nodeClicked && (mSelRoad == NULL || closestNodeRoad == mSelRoad) ) + { + // If a node was clicked, the owning road is always + // considered the clicked road. + clickedRoadPtr = closestNodeRoad; + } + else + { + // check the selected road first + if ( mSelRoad != NULL && mSelRoad->containsPoint( tPos, &insertNodeIdx ) ) + { + clickedRoadPtr = mSelRoad; + nodeClicked = false; + clickedNodeIdx = -1; + } + else + { + // Otherwise, we must ask each road if it contains + // the clicked pos. + for ( SimSetIterator iter(mRoadSet); *iter; ++iter ) + { + roadPtr = static_cast( *iter ); + if ( roadPtr->containsPoint( tPos, &insertNodeIdx ) ) + { + clickedRoadPtr = roadPtr; + break; + } + } + } + } + + // shortcuts + bool dblClick = ( event.mouseClickCount > 1 ); + if( dblClick ) + { + if( mMode == mSelectRoadMode ) + { + setMode( mAddRoadMode, true ); + return; + } + if( mMode == mAddNodeMode ) + { + // Delete the node attached to the cursor. + deleteSelectedNode(); + mMode = mAddRoadMode; + return; + } + } + + //this check is here in order to bounce back from deleting a whole road with ctrl+z + //this check places the editor back into addroadmode + if ( mMode == mAddNodeMode ) + { + if ( !mSelRoad ) + mMode = mAddRoadMode; + } + + if ( mMode == mSelectRoadMode ) + { + // Did not click on a road or a node. + if ( !clickedRoadPtr ) + { + setSelectedRoad( NULL ); + setSelectedNode( -1 ); + + return; + } + + // Clicked on a road that wasn't the currently selected road. + if ( clickedRoadPtr != mSelRoad ) + { + setSelectedRoad( clickedRoadPtr ); + setSelectedNode( -1 ); + return; + } + + // Clicked on a node in the currently selected road that wasn't + // the currently selected node. + if ( nodeClicked ) + { + setSelectedNode( clickedNodeIdx ); + return; + } + + + // Clicked a position on the currently selected road + // that did not contain a node. + //U32 newNode = clickedRoadPtr->insertNode( tPos, mDefaultWidth, insertNodeIdx ); + //setSelectedNode( newNode ); + } + else if ( mMode == mAddRoadMode ) + { + if ( nodeClicked && clickedRoadPtr ) + { + // A double-click on a node in Normal mode means set AddNode mode. + if ( clickedNodeIdx == 0 ) + { + setSelectedRoad( clickedRoadPtr ); + setSelectedNode( clickedNodeIdx ); + + mAddNodeIdx = clickedNodeIdx; + mMode = mAddNodeMode; + mSelNode = mSelRoad->insertNode( tPos, mDefaultWidth, mAddNodeIdx ); + mIsDirty = true; + + return; + } + else if ( clickedNodeIdx == clickedRoadPtr->mNodes.size() - 1 ) + { + setSelectedRoad( clickedRoadPtr ); + setSelectedNode( clickedNodeIdx ); + + mAddNodeIdx = U32_MAX; + mMode = mAddNodeMode; + mSelNode = mSelRoad->addNode( tPos, mDefaultWidth ); + mIsDirty = true; + setSelectedNode( mSelNode ); + + return; + } + } + + DecalRoad *newRoad = new DecalRoad; + + + newRoad->mMaterialName = mMaterialName; + + newRoad->registerObject(); + + // Add to MissionGroup + SimGroup *missionGroup; + if ( !Sim::findObject( "MissionGroup", missionGroup ) ) + Con::errorf( "GuiDecalRoadEditorCtrl - could not find MissionGroup to add new DecalRoad" ); + else + missionGroup->addObject( newRoad ); + + newRoad->insertNode( tPos, mDefaultWidth, 0 ); + U32 newNode = newRoad->insertNode( tPos, mDefaultWidth, 1 ); + + // Always add to the end of the road, the first node is the start. + mAddNodeIdx = U32_MAX; + + setSelectedRoad( newRoad ); + setSelectedNode( newNode ); + + mMode = mAddNodeMode; + + // Disable the hover node while in addNodeMode, we + // don't want some random node enlarged. + mHoverNode = -1; + + // Grab the mission editor undo manager. + UndoManager *undoMan = NULL; + if ( !Sim::findObject( "EUndoManager", undoMan ) ) + { + Con::errorf( "GuiRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" ); + return; + } + + // Create the UndoAction. + MECreateUndoAction *action = new MECreateUndoAction("Create Road"); + action->addObject( newRoad ); + + // Submit it. + undoMan->addAction( action ); + + //send a callback to script after were done here if one exists + if ( isMethod( "onRoadCreation" ) ) + Con::executef( this, "onRoadCreation" ); + + return; + } + else if ( mMode == mAddNodeMode ) + { + // Oops the road got deleted, maybe from an undo action? + // Back to NormalMode. + if ( mSelRoad ) + { + // A double-click on a node in Normal mode means set AddNode mode. + if ( clickedNodeIdx == 0 ) + { + submitUndo( "Add Node" ); + mAddNodeIdx = clickedNodeIdx; + mMode = mAddNodeMode; + mSelNode = mSelRoad->insertNode( tPos, mDefaultWidth, mAddNodeIdx ); + mIsDirty = true; + setSelectedNode( mSelNode ); + + return; + } + else + { + if( clickedRoadPtr && clickedNodeIdx == clickedRoadPtr->mNodes.size() - 1 ) + { + submitUndo( "Add Node" ); + mAddNodeIdx = U32_MAX; + mMode = mAddNodeMode; + mSelNode = mSelRoad->addNode( tPos, mDefaultWidth ); + mIsDirty = true; + setSelectedNode( mSelNode ); + + return; + } + else + { + submitUndo( "Insert Node" ); + // A single-click on empty space while in + // AddNode mode means insert / add a node. + //submitUndo( "Add Node" ); + //F32 width = mSelRoad->getNodeWidth( mSelNode ); + U32 newNode = mSelRoad->insertNode( tPos, mDefaultWidth, mAddNodeIdx); + mIsDirty = true; + setSelectedNode( newNode ); + + return; + } + } + } + } + else if ( mMode == mInsertPointMode && mSelRoad != NULL) + { + if ( clickedRoadPtr == mSelRoad ) + { + F32 w0 = mSelRoad->getNodeWidth( insertNodeIdx ); + F32 w1 = mSelRoad->getNodeWidth( insertNodeIdx + 1 ); + F32 width = ( w0 + w1 ) * 0.5f; + + submitUndo( "Insert Node" ); + U32 newNode = mSelRoad->insertNode( tPos, width, insertNodeIdx + 1); + mIsDirty = true; + setSelectedNode( newNode ); + + return; + } + } + else if ( mMode == mRemovePointMode && mSelRoad != NULL) + { + if ( nodeClicked && clickedRoadPtr == mSelRoad ) + { + setSelectedNode( clickedNodeIdx ); + deleteSelectedNode(); + return; + } + } + else if ( mMode == mMovePointMode ) + { + if ( nodeClicked && clickedRoadPtr == mSelRoad ) + { + setSelectedNode( clickedNodeIdx ); + return; + } + } + else if ( mMode == mScalePointMode ) + { + if ( nodeClicked && clickedRoadPtr == mSelRoad ) + { + setSelectedNode( clickedNodeIdx ); + return; + } + } +} + +void GuiRoadEditorCtrl::on3DRightMouseDown(const Gui3DMouseEvent & event) +{ + //mIsPanning = true; +} + +void GuiRoadEditorCtrl::on3DRightMouseUp(const Gui3DMouseEvent & event) +{ + //mIsPanning = false; +} + +void GuiRoadEditorCtrl::on3DMouseUp(const Gui3DMouseEvent & event) +{ + mStartWidth = -1.0f; + mSavedDrag = false; + mouseUnlock(); +} + +void GuiRoadEditorCtrl::on3DMouseMove(const Gui3DMouseEvent & event) +{ + if ( mSelRoad != NULL && mMode == mAddNodeMode ) + { + Point3F startPnt = event.pos; + Point3F endPnt = event.pos + event.vec * 1000.0f; + RayInfo ri; + if ( gServerContainer.castRay(startPnt, endPnt, TerrainObjectType, &ri) ) + { + mSelRoad->setNodePosition( mSelNode, ri.point ); + mIsDirty = true; + } + + return; + } + + // Is cursor hovering over a road? + if ( mMode == mSelectRoadMode ) + { + mHoverRoad = NULL; + + Point3F startPnt = event.pos; + Point3F endPnt = event.pos + event.vec * 1000.0f; + + RayInfo ri; + + if ( gServerContainer.castRay(startPnt, endPnt, TerrainObjectType, &ri) ) + { + DecalRoad *pRoad = NULL; + + for ( SimSetIterator iter(mRoadSet); *iter; ++iter ) + { + pRoad = static_cast( *iter ); + + if ( pRoad->containsPoint( ri.point ) ) + { + mHoverRoad = pRoad; + break; + } + } + } + } + + // Is cursor hovering over a RoadNode? + if ( mHoverRoad ) + { + DecalRoad *pRoad = mHoverRoad; + + S32 hoverNodeIdx = -1; + F32 hoverNodeDist = F32_MAX; + + for ( U32 i = 0; i < pRoad->mNodes.size(); i++ ) + { + const Point3F &nodePos = pRoad->mNodes[i].point; + + Point3F screenPos; + project( nodePos, &screenPos ); + + RectI rect( Point2I((S32)screenPos.x,(S32)screenPos.y) - mNodeHalfSize, mNodeHalfSize * 2 ); + + if ( rect.pointInRect( event.mousePoint ) && screenPos.z < hoverNodeDist ) + { + hoverNodeDist = screenPos.z; + hoverNodeIdx = i; + } + } + + mHoverNode = hoverNodeIdx; + } +} + +void GuiRoadEditorCtrl::on3DMouseDragged(const Gui3DMouseEvent & event) +{ + // Drags are only used to transform nodes + if ( !mSelRoad || mSelNode == -1 || + ( mMode != mMovePointMode && mMode != mScalePointMode ) ) + return; + + if ( !mSavedDrag ) + { + submitUndo( "Modify Node" ); + mSavedDrag = true; + } + + if ( mMode == mScalePointMode ) + { + Point3F tPos; + if ( !getTerrainPos( event, tPos ) ) + return; + + if ( mStartWidth == -1.0f ) + { + mStartWidth = mSelRoad->mNodes[mSelNode].width; + + mStartX = event.mousePoint.x; + mStartWorld = tPos; + } + + S32 deltaScreenX = event.mousePoint.x - mStartX; + + F32 worldDist = ( event.pos - mStartWorld ).len(); + + F32 deltaWorldX = ( deltaScreenX * worldDist ) / getWorldToScreenScale().y; + + F32 width = mStartWidth + deltaWorldX; + + mSelRoad->setNodeWidth( mSelNode, width ); + mIsDirty = true; + } + else if( mMode == mMovePointMode ) + { + Point3F tPos; + if ( !getTerrainPos( event, tPos ) ) + return; + + mSelRoad->setNodePosition( mSelNode, tPos ); + mIsDirty = true; + } + + Con::executef( this, "onNodeModified", Con::getIntArg(mSelNode) ); +} + +void GuiRoadEditorCtrl::on3DMouseEnter(const Gui3DMouseEvent & event) +{ + // nothing to do +} + +void GuiRoadEditorCtrl::on3DMouseLeave(const Gui3DMouseEvent & event) +{ + // nothing to do +} + +bool GuiRoadEditorCtrl::onKeyDown(const GuiEvent& event) +{ + if( event.keyCode == KEY_RETURN && mMode == mAddNodeMode ) + { + // Delete the node attached to the cursor. + deleteSelectedNode(); + mMode = mAddRoadMode; + return true; + } + + return false; +} + +void GuiRoadEditorCtrl::updateGuiInfo() +{ + // nothing to do +} + +void GuiRoadEditorCtrl::onRender( Point2I offset, const RectI &updateRect ) +{ + PROFILE_SCOPE( GuiRoadEditorCtrl_OnRender ); + + Parent::onRender( offset, updateRect ); + return; +} + +void GuiRoadEditorCtrl::renderScene(const RectI & updateRect) +{ + GFX->setStateBlock( mZDisableSB ); + + // Draw the spline based from the client-side road + // because the serverside spline is not actually reliable... + // Can be incorrect if the DecalRoad is before the TerrainBlock + // in the MissionGroup. + + if ( mHoverRoad && mHoverRoad != mSelRoad ) + { + DecalRoad *pRoad = (DecalRoad*)mHoverRoad->getClientObject(); + if ( pRoad ) + _drawRoadSpline( pRoad, mHoverSplineColor ); + } + + if ( mSelRoad ) + { + DecalRoad *pRoad = (DecalRoad*)mSelRoad->getClientObject(); + if ( pRoad ) + _drawRoadSpline( pRoad, mSelectedSplineColor ); + } +} + +void GuiRoadEditorCtrl::renderGui( Point2I offset, const RectI &updateRect ) +{ + // Draw Control nodes for selected and highlighted roads + if ( mHoverRoad ) + _drawRoadControlNodes( mHoverRoad, mHoverSplineColor ); + if ( mSelRoad ) + _drawRoadControlNodes( mSelRoad, mSelectedSplineColor ); + + Parent::renderGui(offset, updateRect); +} + +void GuiRoadEditorCtrl::_drawRoadSpline( DecalRoad *road, const ColorI &color ) +{ + if ( road->mEdges.size() <= 1 ) + return; + + GFXTransformSaver saver; + + if ( DecalRoad::smShowSpline ) + { + // Render the center-line + PrimBuild::color( color ); + PrimBuild::begin( GFXLineStrip, road->mEdges.size() ); + for ( U32 i = 0; i < road->mEdges.size(); i++ ) + { + PrimBuild::vertex3fv( road->mEdges[i].p1 ); + } + PrimBuild::end(); + } + + if ( DecalRoad::smWireframe ) + { + // Left-side line + PrimBuild::color3i( 100, 100, 100 ); + PrimBuild::begin( GFXLineStrip, road->mEdges.size() ); + for ( U32 i = 0; i < road->mEdges.size(); i++ ) + { + PrimBuild::vertex3fv( road->mEdges[i].p0 ); + } + PrimBuild::end(); + + // Right-side line + PrimBuild::begin( GFXLineStrip, road->mEdges.size() ); + for ( U32 i = 0; i < road->mEdges.size(); i++ ) + { + PrimBuild::vertex3fv( road->mEdges[i].p2 ); + } + PrimBuild::end(); + + // Cross-sections + PrimBuild::begin( GFXLineList, road->mEdges.size() * 2 ); + for ( U32 i = 0; i < road->mEdges.size(); i++ ) + { + PrimBuild::vertex3fv( road->mEdges[i].p0 ); + PrimBuild::vertex3fv( road->mEdges[i].p2 ); + } + PrimBuild::end(); + } +} + +void GuiRoadEditorCtrl::_drawRoadControlNodes( DecalRoad *road, const ColorI &color ) +{ + if ( !DecalRoad::smShowSpline ) + return; + + RectI bounds = getBounds(); + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + + bool isSelected = ( road == mSelRoad ); + bool isHighlighted = ( road == mHoverRoad ); + + for ( U32 i = 0; i < road->mNodes.size(); i++ ) + { + if ( false && isSelected && mSelNode == i ) + continue; + + const Point3F &wpos = road->mNodes[i].point; + + Point3F spos; + project( wpos, &spos ); + + if ( spos.z > 1.0f ) + continue; + + Point2I posi; + posi.x = spos.x; + posi.y = spos.y; + + if ( !bounds.pointInRect( posi ) ) + continue; + + ColorI theColor = color; + Point2I nodeHalfSize = mNodeHalfSize; + + if ( isHighlighted && mHoverNode == i ) + nodeHalfSize += Point2I(2,2); + + if ( isSelected ) + { + if ( mSelNode == i ) + { + theColor.set(0,0,255); + } + else if ( i == 0 ) + { + theColor.set(0,255,0); + } + else if ( i == road->mNodes.size() - 1 ) + { + theColor.set(255,0,0); + } + } + + drawer->drawRectFill( posi - nodeHalfSize, posi + nodeHalfSize, theColor ); + } +} + +bool GuiRoadEditorCtrl::getTerrainPos( const Gui3DMouseEvent & event, Point3F &tpos ) +{ + // Find clicked point on the terrain + + Point3F startPnt = event.pos; + Point3F endPnt = event.pos + event.vec * 10000.0f; + + RayInfo ri; + bool hit; + + hit = gServerContainer.castRay(startPnt, endPnt, TerrainObjectType, &ri); + tpos = ri.point; + + return hit; +} + +void GuiRoadEditorCtrl::deleteSelectedNode() +{ + if ( !mSelRoad || mSelNode == -1 ) + return; + + // If the road has only two nodes remaining, + // delete the whole road. + if ( mSelRoad->mNodes.size() <= 2 ) + { + deleteSelectedRoad(); + } + else + { + // Only submit undo if we weren't in AddMode + if ( mMode != mAddNodeMode ) + submitUndo( "Delete Node" ); + + // Delete the SelectedNode of the SelectedRoad + mSelRoad->deleteNode(mSelNode); + mIsDirty = true; + + // We deleted the Node but not the Road (it has nodes left) + // so decrement the currently selected node. + if ( mSelRoad->mNodes.size() <= mSelNode ) + mSelNode--; + } +} + +void GuiRoadEditorCtrl::deleteSelectedRoad( bool undoAble ) +{ + AssertFatal( mSelRoad != NULL, "GuiRoadEditorCtrl::deleteSelectedRoad() - No road IS selected" ); + + // Not undo-able? Just delete it. + if ( !undoAble ) + { + DecalRoad *lastRoad = mSelRoad; + + setSelectedRoad(NULL); + + lastRoad->deleteObject(); + mIsDirty = true; + + return; + } + + // Grab the mission editor undo manager. + UndoManager *undoMan = NULL; + if ( !Sim::findObject( "EUndoManager", undoMan ) ) + { + // Couldn't find it? Well just delete the road. + Con::errorf( "GuiRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" ); + return; + } + else + { + DecalRoad *lastRoad = mSelRoad; + setSelectedRoad(NULL); + + // Create the UndoAction. + MEDeleteUndoAction *action = new MEDeleteUndoAction("Deleted Road"); + action->deleteObject( lastRoad ); + mIsDirty = true; + + // Submit it. + undoMan->addAction( action ); + } +} + +void GuiRoadEditorCtrl::setMode( String mode, bool sourceShortcut = false ) +{ + mMode = mode; + + if( sourceShortcut ) + Con::executef( this, "paletteSync", mode ); +} + +void GuiRoadEditorCtrl::setSelectedRoad( DecalRoad *road ) +{ + mSelRoad = road; + + if ( road != NULL ) + Con::executef( this, "onRoadSelected", road->getIdString() ); + else + Con::executef( this, "onRoadSelected" ); + + if ( mSelRoad != road ) + setSelectedNode(-1); +} + +void GuiRoadEditorCtrl::setNodeWidth( F32 width ) +{ + if ( mSelRoad && mSelNode != -1 ) + { + mSelRoad->setNodeWidth( mSelNode, width ); + mIsDirty = true; + } +} + +F32 GuiRoadEditorCtrl::getNodeWidth() +{ + if ( mSelRoad && mSelNode != -1 ) + return mSelRoad->getNodeWidth( mSelNode ); + + return 0.0f; +} + +void GuiRoadEditorCtrl::setNodePosition( Point3F pos ) +{ + if ( mSelRoad && mSelNode != -1 ) + { + mSelRoad->setNodePosition( mSelNode, pos ); + mIsDirty = true; + } +} + +Point3F GuiRoadEditorCtrl::getNodePosition() +{ + if ( mSelRoad && mSelNode != -1 ) + return mSelRoad->getNodePosition( mSelNode ); + + return Point3F( 0, 0, 0 ); +} + +void GuiRoadEditorCtrl::setSelectedNode( S32 node ) +{ + //if ( mSelNode == node ) + // return; + + mSelNode = node; + + if ( mSelNode != -1 && mSelRoad != NULL ) + Con::executef( this, "onNodeSelected", Con::getIntArg(mSelNode), Con::getFloatArg(mSelRoad->mNodes[mSelNode].width) ); + else + Con::executef( this, "onNodeSelected", Con::getIntArg(-1) ); +} + +void GuiRoadEditorCtrl::submitUndo( const UTF8 *name ) +{ + // Grab the mission editor undo manager. + UndoManager *undoMan = NULL; + if ( !Sim::findObject( "EUndoManager", undoMan ) ) + { + Con::errorf( "GuiRoadEditorCtrl::submitUndo() - EUndoManager not found!" ); + return; + } + + // Setup the action. + GuiRoadEditorUndoAction *action = new GuiRoadEditorUndoAction( name ); + + action->mObjId = mSelRoad->getId(); + action->mBreakAngle = mSelRoad->mBreakAngle; + action->mMaterialName = mSelRoad->mMaterialName; + action->mSegmentsPerBatch = mSelRoad->mSegmentsPerBatch; + action->mTextureLength = mSelRoad->mTextureLength; + action->mRoadEditor = this; + + for( U32 i = 0; i < mSelRoad->mNodes.size(); i++ ) + { + action->mNodes.push_back( mSelRoad->mNodes[i] ); + } + + undoMan->addAction( action ); +} + +ConsoleMethod( GuiRoadEditorCtrl, deleteNode, void, 2, 2, "deleteNode()" ) +{ + object->deleteSelectedNode(); +} + +ConsoleMethod( GuiRoadEditorCtrl, getMode, const char*, 2, 2, "" ) +{ + return object->getMode(); +} + +ConsoleMethod( GuiRoadEditorCtrl, setMode, void, 3, 3, "setMode( String mode )" ) +{ + String newMode = ( argv[2] ); + object->setMode( newMode ); +} + +ConsoleMethod( GuiRoadEditorCtrl, getNodeWidth, F32, 2, 2, "" ) +{ + return object->getNodeWidth(); +} + +ConsoleMethod( GuiRoadEditorCtrl, setNodeWidth, void, 3, 3, "" ) +{ + object->setNodeWidth( dAtof(argv[2]) ); +} + +ConsoleMethod( GuiRoadEditorCtrl, getNodePosition, const char*, 2, 2, "" ) +{ + char* returnBuffer = Con::getReturnBuffer(256); + + dSprintf(returnBuffer, 256, "%f %f %f", + object->getNodePosition().x, object->getNodePosition().y, object->getNodePosition().z); + + return returnBuffer; +} + +ConsoleMethod( GuiRoadEditorCtrl, setNodePosition, void, 3, 3, "" ) +{ + Point3F pos; + + S32 count = dSscanf( argv[2], "%f %f %f", + &pos.x, &pos.y, &pos.z); + + if ( (count != 3) ) + { + Con::printf("Failed to parse node information \"px py pz\" from '%s'", argv[3]); + return; + } + + object->setNodePosition( pos ); +} + +ConsoleMethod( GuiRoadEditorCtrl, setSelectedRoad, void, 2, 3, "" ) +{ + if ( argc == 2 ) + object->setSelectedRoad(NULL); + else + { + DecalRoad *road = NULL; + if ( Sim::findObject( argv[2], road ) ) + object->setSelectedRoad(road); + } +} + +ConsoleMethod( GuiRoadEditorCtrl, getSelectedRoad, const char*, 2, 2, "" ) +{ + DecalRoad *road = object->getSelectedRoad(); + if ( road ) + return road->getIdString(); + + return NULL; +} + +ConsoleMethod( GuiRoadEditorCtrl, getSelectedNode, S32, 2, 2, "" ) +{ + return object->getSelectedNode(); +} + +ConsoleMethod( GuiRoadEditorCtrl, deleteRoad, void, 2, 2, "" ) +{ + object->deleteSelectedRoad(); +} diff --git a/Engine/source/environment/editors/guiRoadEditorCtrl.h b/Engine/source/environment/editors/guiRoadEditorCtrl.h new file mode 100644 index 000000000..c87a0fd53 --- /dev/null +++ b/Engine/source/environment/editors/guiRoadEditorCtrl.h @@ -0,0 +1,168 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIROADEDITORCTRL_H_ +#define _GUIROADEDITORCTRL_H_ + +#ifndef _EDITTSCTRL_H_ +#include "gui/worldEditor/editTSCtrl.h" +#endif +#ifndef _UNDO_H_ +#include "util/undo.h" +#endif +#ifndef _DECALROAD_H_ +#include "environment/decalRoad.h" +#endif + +class GameBase; +class DecalRoad; + + +class GuiRoadEditorCtrl : public EditTSCtrl +{ + typedef EditTSCtrl Parent; + + public: + + friend class GuiRoadEditorUndoAction; + + String mSelectRoadMode; + String mAddRoadMode; + String mAddNodeMode; + String mInsertPointMode; + String mRemovePointMode; + String mMovePointMode; + String mScalePointMode; + + GuiRoadEditorCtrl(); + ~GuiRoadEditorCtrl(); + + DECLARE_CONOBJECT(GuiRoadEditorCtrl); + + // SimObject + bool onAdd(); + static void initPersistFields(); + + // GuiControl + virtual void onSleep(); + virtual void onRender(Point2I offset, const RectI &updateRect); + + // EditTSCtrl + bool onKeyDown(const GuiEvent& event); + void get3DCursor( GuiCursor *&cursor, bool &visible, const Gui3DMouseEvent &event_ ); + void on3DMouseDown(const Gui3DMouseEvent & event); + void on3DMouseUp(const Gui3DMouseEvent & event); + void on3DMouseMove(const Gui3DMouseEvent & event); + void on3DMouseDragged(const Gui3DMouseEvent & event); + void on3DMouseEnter(const Gui3DMouseEvent & event); + void on3DMouseLeave(const Gui3DMouseEvent & event); + void on3DRightMouseDown(const Gui3DMouseEvent & event); + void on3DRightMouseUp(const Gui3DMouseEvent & event); + void updateGuiInfo(); + void renderScene(const RectI & updateRect); + void renderGui(Point2I offset, const RectI &updateRect); + + bool getTerrainPos( const Gui3DMouseEvent & event, Point3F &tpos ); + void deleteSelectedNode(); + void deleteSelectedRoad( bool undoAble = true ); + + void setMode( String mode, bool sourceShortcut ); + String getMode() { return mMode; } + + void setSelectedRoad( DecalRoad *road ); + DecalRoad* getSelectedRoad() { return mSelRoad; }; + void setSelectedNode( S32 node ); + S32 getSelectedNode() { return mSelNode; }; + + F32 getNodeWidth(); + void setNodeWidth( F32 width ); + + Point3F getNodePosition(); + void setNodePosition( Point3F pos ); + + void setTextureFile( StringTableEntry file ); + + public: + + StringTableEntry mMaterialName; + protected: + + void _drawRoadSpline( DecalRoad *road, const ColorI &color ); + void _drawRoadControlNodes( DecalRoad *road, const ColorI &color ); + + void submitUndo( const UTF8 *name ); + + bool mSavedDrag; + bool mIsDirty; + + SimSet *mRoadSet; + S32 mSelNode; + S32 mHoverNode; + U32 mAddNodeIdx; + SimObjectPtr mSelRoad; + SimObjectPtr mHoverRoad; + + ColorI mHoverSplineColor; + ColorI mSelectedSplineColor; + ColorI mHoverNodeColor; + + String mMode; + + F32 mDefaultWidth; + S32 mInsertIdx; + + F32 mStartWidth; + S32 mStartX; + Point3F mStartWorld; + + Point2I mNodeHalfSize; + + // StateBlock for rendering road splines. + GFXStateBlockRef mZDisableSB; +}; + +class GuiRoadEditorUndoAction : public UndoAction +{ + public: + + GuiRoadEditorUndoAction( const UTF8* actionName ) : UndoAction( actionName ) + { + } + + GuiRoadEditorCtrl *mRoadEditor; + + Vector mNodes; + + SimObjectId mObjId; + String mMaterialName; + F32 mBreakAngle; + U32 mSegmentsPerBatch; + F32 mTextureLength; + + virtual void undo(); + virtual void redo() { undo(); } +}; + +#endif + + + diff --git a/Engine/source/environment/meshRoad.cpp b/Engine/source/environment/meshRoad.cpp new file mode 100644 index 000000000..fac154f72 --- /dev/null +++ b/Engine/source/environment/meshRoad.cpp @@ -0,0 +1,2441 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "environment/meshRoad.h" + +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "util/catmullRom.h" +#include "math/util/quadTransforms.h" +#include "scene/simPath.h" +#include "scene/sceneRenderState.h" +#include "scene/sceneManager.h" +#include "scene/sgUtil.h" +#include "renderInstance/renderPassManager.h" +#include "T3D/gameBase/gameConnection.h" +#include "core/stream/bitStream.h" +#include "gfx/gfxDrawUtil.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxDebugEvent.h" +#include "materials/materialManager.h" +#include "math/mathIO.h" +#include "math/mathUtils.h" +#include "math/util/frustum.h" +#include "gui/3d/guiTSControl.h" +#include "materials/shaderData.h" +#include "gfx/sim/gfxStateBlockData.h" +#include "gfx/sim/debugDraw.h" +#include "collision/concretePolyList.h" +#include "T3D/physics/physicsPlugin.h" +#include "T3D/physics/physicsBody.h" +#include "environment/nodeListManager.h" + +#define MIN_METERS_PER_SEGMENT 1.0f +#define MIN_NODE_DEPTH 0.25f +#define MAX_NODE_DEPTH 50.0f +#define MIN_NODE_WIDTH 0.25f +#define MAX_NODE_WIDTH 50.0f + + +static U32 gIdxArray[6][2][3] = { + { { 0, 4, 5 }, { 0, 5, 1 }, }, // Top Face + { { 2, 6, 4 }, { 2, 4, 0 }, }, // Left Face + { { 1, 5, 7 }, { 1, 7, 3 }, }, // Right Face + { { 2, 3, 7 }, { 2, 7, 6 }, }, // Bottom Face + { { 0, 1, 3 }, { 0, 3, 2 }, }, // Front Face + { { 4, 6, 7 }, { 4, 7, 5 }, }, // Back Face +}; + +static S32 QSORT_CALLBACK compareHitSegments(const void* a,const void* b) +{ + const MeshRoadHitSegment *fa = (MeshRoadHitSegment*)a; + const MeshRoadHitSegment *fb = (MeshRoadHitSegment*)b; + + return mSign(fb->t - fa->t); +} + + +//----------------------------------------------------------------------------- +// MeshRoadNodeList Struct +//----------------------------------------------------------------------------- + +struct MeshRoadNodeList : public NodeListManager::NodeList +{ + Vector mPositions; + Vector mWidths; + Vector mDepths; + Vector mNormals; + + MeshRoadNodeList() { } + virtual ~MeshRoadNodeList() { } +}; + +//----------------------------------------------------------------------------- +// MeshRoadNodeEvent Class +//----------------------------------------------------------------------------- + +class MeshRoadNodeEvent : public NodeListEvent +{ + typedef NodeListEvent Parent; + +public: + Vector mPositions; + Vector mWidths; + Vector mDepths; + Vector mNormals; + +public: + MeshRoadNodeEvent() { mNodeList = NULL; } + virtual ~MeshRoadNodeEvent() { } + + virtual void pack(NetConnection*, BitStream*); + virtual void unpack(NetConnection*, BitStream*); + + virtual void copyIntoList(NodeListManager::NodeList* copyInto); + virtual void padListToSize(); + + DECLARE_CONOBJECT(MeshRoadNodeEvent); +}; + +void MeshRoadNodeEvent::pack(NetConnection* conn, BitStream* stream) +{ + Parent::pack( conn, stream ); + + stream->writeInt( mPositions.size(), 16 ); + + for (U32 i=0; iwrite( mWidths[i] ); + stream->write( mDepths[i] ); + mathWrite( *stream, mNormals[i] ); + } +} + +void MeshRoadNodeEvent::unpack(NetConnection* conn, BitStream* stream) +{ + mNodeList = new MeshRoadNodeList(); + + Parent::unpack( conn, stream ); + + U32 count = stream->readInt( 16 ); + + Point3F pos; + F32 width, depth; + VectorF normal; + + MeshRoadNodeList* list = static_cast(mNodeList); + + for (U32 i=0; iread( &width ); + stream->read( &depth ); + mathRead( *stream, &normal ); + + list->mPositions.push_back( pos ); + list->mWidths.push_back( width ); + list->mDepths.push_back( depth ); + list->mNormals.push_back( normal ); + } + + list->mTotalValidNodes = count; + + // Do we have a complete list? + if (list->mPositions.size() >= mTotalNodes) + list->mListComplete = true; +} + +void MeshRoadNodeEvent::copyIntoList(NodeListManager::NodeList* copyInto) +{ + MeshRoadNodeList* prevList = dynamic_cast(copyInto); + MeshRoadNodeList* list = static_cast(mNodeList); + + // Merge our list with the old list. + for (U32 i=mLocalListStart, index=0; imPositions.size(); ++i, ++index) + { + prevList->mPositions[i] = list->mPositions[index]; + prevList->mWidths[i] = list->mWidths[index]; + prevList->mDepths[i] = list->mDepths[index]; + prevList->mNormals[i] = list->mNormals[index]; + } +} + +void MeshRoadNodeEvent::padListToSize() +{ + MeshRoadNodeList* list = static_cast(mNodeList); + + U32 totalValidNodes = list->mTotalValidNodes; + + // Pad our list front? + if (mLocalListStart) + { + MeshRoadNodeList* newlist = new MeshRoadNodeList(); + newlist->mPositions.increment(mLocalListStart); + newlist->mWidths.increment(mLocalListStart); + newlist->mDepths.increment(mLocalListStart); + newlist->mNormals.increment(mLocalListStart); + + newlist->mPositions.merge(list->mPositions); + newlist->mWidths.merge(list->mWidths); + newlist->mDepths.merge(list->mDepths); + newlist->mNormals.merge(list->mNormals); + + mNodeList = newlist; + delete list; + } + + // Pad our list end? + if (list->mPositions.size() < mTotalNodes) + { + U32 delta = mTotalNodes - list->mPositions.size(); + list->mPositions.increment(delta); + list->mWidths.increment(delta); + list->mDepths.increment(delta); + list->mNormals.increment(delta); + } + + list->mTotalValidNodes = totalValidNodes; +} + +IMPLEMENT_CO_NETEVENT_V1(MeshRoadNodeEvent); + +ConsoleDocClass( MeshRoadNodeEvent, + "@brief Sends messages to the Mesh Road Editor\n\n" + "Editor use only.\n\n" + "@internal" +); + +//----------------------------------------------------------------------------- +// MeshRoadNodeListNotify Class +//----------------------------------------------------------------------------- + +class MeshRoadNodeListNotify : public NodeListNotify +{ + typedef NodeListNotify Parent; + +protected: + SimObjectPtr mRoad; + +public: + MeshRoadNodeListNotify( MeshRoad* road, U32 listId ) { mRoad = road; mListId = listId; } + virtual ~MeshRoadNodeListNotify() { mRoad = NULL; } + + virtual void sendNotification( NodeListManager::NodeList* list ); +}; + +void MeshRoadNodeListNotify::sendNotification( NodeListManager::NodeList* list ) +{ + if (mRoad.isValid()) + { + // Build the road's nodes + MeshRoadNodeList* roadList = dynamic_cast( list ); + if (roadList) + mRoad->buildNodesFromList( roadList ); + } +} + +//------------------------------------------------------------------------------ +// MeshRoadConvex Class +//------------------------------------------------------------------------------ + +const MatrixF& MeshRoadConvex::getTransform() const +{ + return MatrixF::Identity; //mObject->getTransform(); +} + +Box3F MeshRoadConvex::getBoundingBox() const +{ + return box; +} + +Box3F MeshRoadConvex::getBoundingBox(const MatrixF& mat, const Point3F& scale) const +{ + Box3F newBox = box; + newBox.minExtents.convolve(scale); + newBox.maxExtents.convolve(scale); + mat.mul(newBox); + return newBox; +} + +Point3F MeshRoadConvex::support(const VectorF& vec) const +{ + F32 bestDot = mDot( verts[0], vec ); + + const Point3F *bestP = &verts[0]; + for(S32 i=1; i<4; i++) + { + F32 newD = mDot(verts[i], vec); + if(newD > bestDot) + { + bestDot = newD; + bestP = &verts[i]; + } + } + + return *bestP; +} + +void MeshRoadConvex::getFeatures(const MatrixF& mat, const VectorF& n, ConvexFeature* cf) +{ + cf->material = 0; + cf->object = mObject; + + // For a tetrahedron this is pretty easy... first + // convert everything into world space. + Point3F tverts[4]; + mat.mulP(verts[0], &tverts[0]); + mat.mulP(verts[1], &tverts[1]); + mat.mulP(verts[2], &tverts[2]); + mat.mulP(verts[3], &tverts[3]); + + // Points... + S32 firstVert = cf->mVertexList.size(); + cf->mVertexList.increment(); cf->mVertexList.last() = tverts[0]; + cf->mVertexList.increment(); cf->mVertexList.last() = tverts[1]; + cf->mVertexList.increment(); cf->mVertexList.last() = tverts[2]; + cf->mVertexList.increment(); cf->mVertexList.last() = tverts[3]; + + // Edges... + cf->mEdgeList.increment(); + cf->mEdgeList.last().vertex[0] = firstVert+0; + cf->mEdgeList.last().vertex[1] = firstVert+1; + + cf->mEdgeList.increment(); + cf->mEdgeList.last().vertex[0] = firstVert+1; + cf->mEdgeList.last().vertex[1] = firstVert+2; + + cf->mEdgeList.increment(); + cf->mEdgeList.last().vertex[0] = firstVert+2; + cf->mEdgeList.last().vertex[1] = firstVert+0; + + cf->mEdgeList.increment(); + cf->mEdgeList.last().vertex[0] = firstVert+3; + cf->mEdgeList.last().vertex[1] = firstVert+0; + + cf->mEdgeList.increment(); + cf->mEdgeList.last().vertex[0] = firstVert+3; + cf->mEdgeList.last().vertex[1] = firstVert+1; + + cf->mEdgeList.increment(); + cf->mEdgeList.last().vertex[0] = firstVert+3; + cf->mEdgeList.last().vertex[1] = firstVert+2; + + // Triangles... + cf->mFaceList.increment(); + cf->mFaceList.last().normal = PlaneF(tverts[2], tverts[1], tverts[0]); + cf->mFaceList.last().vertex[0] = firstVert+2; + cf->mFaceList.last().vertex[1] = firstVert+1; + cf->mFaceList.last().vertex[2] = firstVert+0; + + cf->mFaceList.increment(); + cf->mFaceList.last().normal = PlaneF(tverts[1], tverts[0], tverts[3]); + cf->mFaceList.last().vertex[0] = firstVert+1; + cf->mFaceList.last().vertex[1] = firstVert+0; + cf->mFaceList.last().vertex[2] = firstVert+3; + + cf->mFaceList.increment(); + cf->mFaceList.last().normal = PlaneF(tverts[2], tverts[1], tverts[3]); + cf->mFaceList.last().vertex[0] = firstVert+2; + cf->mFaceList.last().vertex[1] = firstVert+1; + cf->mFaceList.last().vertex[2] = firstVert+3; + + cf->mFaceList.increment(); + cf->mFaceList.last().normal = PlaneF(tverts[0], tverts[2], tverts[3]); + cf->mFaceList.last().vertex[0] = firstVert+0; + cf->mFaceList.last().vertex[1] = firstVert+2; + cf->mFaceList.last().vertex[2] = firstVert+3; +} + + +void MeshRoadConvex::getPolyList( AbstractPolyList* list ) +{ + // Transform the list into object space and set the pointer to the object + //MatrixF i( mObject->getTransform() ); + //Point3F iS( mObject->getScale() ); + //list->setTransform(&i, iS); + + list->setTransform( &MatrixF::Identity, Point3F::One ); + list->setObject(mObject); + + // Points... + S32 base = list->addPoint(verts[1]); + list->addPoint(verts[2]); + list->addPoint(verts[0]); + list->addPoint(verts[3]); + + // Planes... + list->begin(0,0); + list->vertex(base + 2); + list->vertex(base + 1); + list->vertex(base + 0); + list->plane(base + 2, base + 1, base + 0); + list->end(); + list->begin(0,0); + list->vertex(base + 2); + list->vertex(base + 1); + list->vertex(base + 3); + list->plane(base + 2, base + 1, base + 3); + list->end(); + list->begin(0,0); + list->vertex(base + 3); + list->vertex(base + 1); + list->vertex(base + 0); + list->plane(base + 3, base + 1, base + 0); + list->end(); + list->begin(0,0); + list->vertex(base + 2); + list->vertex(base + 3); + list->vertex(base + 0); + list->plane(base + 2, base + 3, base + 0); + list->end(); +} + + +//------------------------------------------------------------------------------ +// MeshRoadSegment Class +//------------------------------------------------------------------------------ + +MeshRoadSegment::MeshRoadSegment() +{ + mPlaneCount = 0; + columns = 0; + rows = 0; + numVerts = 0; + numTriangles = 0; + + startVert = 0; + endVert = 0; + startIndex = 0; + endIndex = 0; + + slice0 = NULL; + slice1 = NULL; +} + +MeshRoadSegment::MeshRoadSegment( MeshRoadSlice *rs0, MeshRoadSlice *rs1, const MatrixF &roadMat ) +{ + columns = 0; + rows = 0; + numVerts = 0; + numTriangles = 0; + + startVert = 0; + endVert = 0; + startIndex = 0; + endIndex = 0; + + slice0 = rs0; + slice1 = rs1; + + // Calculate the bounding box(s) + worldbounds.minExtents = worldbounds.maxExtents = rs0->p0; + worldbounds.extend( rs0->p2 ); + worldbounds.extend( rs0->pb0 ); + worldbounds.extend( rs0->pb2 ); + worldbounds.extend( rs1->p0 ); + worldbounds.extend( rs1->p2 ); + worldbounds.extend( rs1->pb0 ); + worldbounds.extend( rs1->pb2 ); + + objectbounds = worldbounds; + roadMat.mul( objectbounds ); + + // Calculate the planes for this segment + // Will be used for intersection/buoyancy tests + + mPlaneCount = 6; + mPlanes[0].set( slice0->pb0, slice0->p0, slice1->p0 ); // left + mPlanes[1].set( slice1->pb2, slice1->p2, slice0->p2 ); // right + mPlanes[2].set( slice0->pb2, slice0->p2, slice0->p0 ); // near + mPlanes[3].set( slice1->p0, slice1->p2, slice1->pb2 ); // far + mPlanes[4].set( slice1->p2, slice1->p0, slice0->p0 ); // top + mPlanes[5].set( slice0->pb0, slice1->pb0, slice1->pb2 ); // bottom +} + +void MeshRoadSegment::set( MeshRoadSlice *rs0, MeshRoadSlice *rs1 ) +{ + columns = 0; + rows = 0; + numVerts = 0; + numTriangles = 0; + + startVert = 0; + endVert = 0; + startIndex = 0; + endIndex = 0; + + slice0 = rs0; + slice1 = rs1; +} + +bool MeshRoadSegment::intersectBox( const Box3F &bounds ) const +{ + // This code copied from Frustum class. + + Point3F maxPoint; + F32 maxDot; + + // Note the planes are ordered left, right, near, + // far, top, bottom for getting early rejections + // from the typical horizontal scene. + for ( S32 i = 0; i < mPlaneCount; i++ ) + { + // This is pretty much as optimal as you can + // get for a plane vs AABB test... + // + // 4 comparisons + // 3 multiplies + // 2 adds + // 1 negation + // + // It will early out as soon as it detects the + // bounds is outside one of the planes. + + if ( mPlanes[i].x > 0 ) + maxPoint.x = bounds.maxExtents.x; + else + maxPoint.x = bounds.minExtents.x; + + if ( mPlanes[i].y > 0 ) + maxPoint.y = bounds.maxExtents.y; + else + maxPoint.y = bounds.minExtents.y; + + if ( mPlanes[i].z > 0 ) + maxPoint.z = bounds.maxExtents.z; + else + maxPoint.z = bounds.minExtents.z; + + maxDot = mDot( maxPoint, mPlanes[ i ] ); + + if ( maxDot <= -mPlanes[ i ].d ) + return false; + } + + return true; +} + +bool MeshRoadSegment::containsPoint( const Point3F &pnt ) const +{ + // This code from Frustum class. + + F32 maxDot; + + // Note the planes are ordered left, right, near, + // far, top, bottom for getting early rejections + // from the typical horizontal scene. + for ( S32 i = 0; i < mPlaneCount; i++ ) + { + const PlaneF &plane = mPlanes[ i ]; + + // This is pretty much as optimal as you can + // get for a plane vs point test... + // + // 1 comparison + // 2 multiplies + // 1 adds + // + // It will early out as soon as it detects the + // point is outside one of the planes. + + maxDot = mDot( pnt, plane ) + plane.d; + if ( maxDot < 0.0f ) + return false; + } + + return true; +} + +F32 MeshRoadSegment::distanceToSurface(const Point3F &pnt) const +{ + return mPlanes[4].distToPlane( pnt ); +} + +//------------------------------------------------------------------------------ +// MeshRoad Class +//------------------------------------------------------------------------------ + +ConsoleDocClass( MeshRoad, + "@brief A strip of rectangular mesh segments defined by a 3D spline " + "for prototyping road-shaped objects in your scene.\n\n" + + "User may control width and depth per node, overall spline shape in three " + "dimensions, and seperate Materials for rendering the top, bottom, and side surfaces.\n\n" + + "MeshRoad is not capable of handling intersections, branches, curbs, or other " + "desirable features in a final 'road' asset and is therefore intended for " + "prototyping and experimentation.\n\n" + + "Materials assigned to MeshRoad should tile vertically.\n\n" + + "@ingroup Terrain" +); + +bool MeshRoad::smEditorOpen = false; +bool MeshRoad::smShowBatches = false; +bool MeshRoad::smShowSpline = true; +bool MeshRoad::smShowRoad = true; +bool MeshRoad::smWireframe = true; +SimObjectPtr MeshRoad::smServerMeshRoadSet = NULL; + +GFXStateBlockRef MeshRoad::smWireframeSB; + +IMPLEMENT_CO_NETOBJECT_V1(MeshRoad); + +MeshRoad::MeshRoad() +: mTextureLength( 5.0f ), + mBreakAngle( 3.0f ), + mPhysicsRep( NULL ), + mWidthSubdivisions( 0 ) +{ + mConvexList = new Convex; + + // Setup NetObject. + mTypeMask |= StaticObjectType | StaticShapeObjectType; + mNetFlags.set(Ghostable); + + mMatInst[Top] = NULL; + mMatInst[Bottom] = NULL; + mMatInst[Side] = NULL; +} + +MeshRoad::~MeshRoad() +{ + delete mConvexList; + mConvexList = NULL; +} + +void MeshRoad::initPersistFields() +{ + addGroup( "MeshRoad" ); + + addField( "topMaterial", TypeMaterialName, Offset( mMaterialName[Top], MeshRoad ), + "Material for the upper surface of the road." ); + + addField( "bottomMaterial", TypeMaterialName, Offset( mMaterialName[Bottom], MeshRoad ), + "Material for the bottom surface of the road." ); + + addField( "sideMaterial", TypeMaterialName, Offset( mMaterialName[Side], MeshRoad ), + "Material for the left, right, front, and back surfaces of the road." ); + + addField( "textureLength", TypeF32, Offset( mTextureLength, MeshRoad ), + "The length in meters of textures mapped to the MeshRoad." ); + + addField( "breakAngle", TypeF32, Offset( mBreakAngle, MeshRoad ), + "Angle in degrees - MeshRoad will subdivide the spline if its curve is greater than this threshold." ); + + addField( "widthSubdivisions", TypeS32, Offset( mWidthSubdivisions, MeshRoad ), + "Subdivide segments widthwise this many times when generating vertices." ); + + endGroup( "MeshRoad" ); + + addGroup( "Internal" ); + + addProtectedField( "Node", TypeString, NULL, &addNodeFromField, &emptyStringProtectedGetFn, + "Do not modify, for internal use." ); + + endGroup( "Internal" ); + + Parent::initPersistFields(); +} + +void MeshRoad::consoleInit() +{ + Parent::consoleInit(); + + Con::addVariable( "$MeshRoad::EditorOpen", TypeBool, &MeshRoad::smEditorOpen, "True if the MeshRoad editor is open, otherwise false.\n" + "@ingroup Editors\n"); + Con::addVariable( "$MeshRoad::wireframe", TypeBool, &MeshRoad::smWireframe, "If true, will render the wireframe of the road.\n" + "@ingroup Editors\n"); + Con::addVariable( "$MeshRoad::showBatches", TypeBool, &MeshRoad::smShowBatches, "Determines if the debug rendering of the batches cubes is displayed or not.\n" + "@ingroup Editors\n"); + Con::addVariable( "$MeshRoad::showSpline", TypeBool, &MeshRoad::smShowSpline, "If true, the spline on which the curvature of this road is based will be rendered.\n" + "@ingroup Editors\n"); + Con::addVariable( "$MeshRoad::showRoad", TypeBool, &MeshRoad::smShowRoad, "If true, the road will be rendered. When in the editor, roads are always rendered regardless of this flag.\n" + "@ingroup Editors\n"); +} + +bool MeshRoad::addNodeFromField( void *object, const char *index, const char *data ) +{ + MeshRoad *pObj = static_cast(object); + + //if ( !pObj->isProperlyAdded() ) + //{ + F32 width, depth; + Point3F pos, normal; + U32 result = dSscanf( data, "%g %g %g %g %g %g %g %g", &pos.x, &pos.y, &pos.z, &width, &depth, &normal.x, &normal.y, &normal.z ); + if ( result == 8 ) + pObj->_addNode( pos, width, depth, normal ); + //} + + return false; +} + +bool MeshRoad::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + // Reset the World Box. + //setGlobalBounds(); + resetWorldBox(); + + // Set the Render Transform. + setRenderTransform(mObjToWorld); + + // Add to ServerMeshRoadSet + if ( isServerObject() ) + { + getServerSet()->addObject( this ); + } + + if ( isClientObject() ) + _initMaterial(); + + // Generate the Vert/Index buffers and everything else. + _regenerate(); + + // Add to Scene. + addToScene(); + + return true; +} + +void MeshRoad::onRemove() +{ + SAFE_DELETE( mPhysicsRep ); + + mConvexList->nukeList(); + + for ( U32 i = 0; i < SurfaceCount; i++ ) + { + SAFE_DELETE( mMatInst[i] ); + } + + removeFromScene(); + Parent::onRemove(); +} + +void MeshRoad::inspectPostApply() +{ + // Set Parent. + Parent::inspectPostApply(); + + //if ( mMetersPerSegment < MIN_METERS_PER_SEGMENT ) + // mMetersPerSegment = MIN_METERS_PER_SEGMENT; + + setMaskBits(MeshRoadMask); +} + +void MeshRoad::onStaticModified( const char* slotName, const char*newValue ) +{ + Parent::onStaticModified( slotName, newValue ); + + if ( dStricmp( slotName, "breakAngle" ) == 0 ) + { + setMaskBits( RegenMask ); + } +} + +void MeshRoad::writeFields( Stream &stream, U32 tabStop ) +{ + Parent::writeFields( stream, tabStop ); + + // Now write all nodes + + stream.write(2, "\r\n"); + + for ( U32 i = 0; i < mNodes.size(); i++ ) + { + const MeshRoadNode &node = mNodes[i]; + + stream.writeTabs(tabStop); + + char buffer[1024]; + dMemset( buffer, 0, 1024 ); + dSprintf( buffer, 1024, "Node = \"%g %g %g %g %g %g %g %g\";", node.point.x, node.point.y, node.point.z, node.width, node.depth, node.normal.x, node.normal.y, node.normal.z ); + stream.writeLine( (const U8*)buffer ); + } +} + +bool MeshRoad::writeField( StringTableEntry fieldname, const char *value ) +{ + if ( fieldname == StringTable->insert("Node") ) + return false; + + return Parent::writeField( fieldname, value ); +} + +void MeshRoad::onEditorEnable() +{ +} + +void MeshRoad::onEditorDisable() +{ +} + +SimSet* MeshRoad::getServerSet() +{ + if ( !smServerMeshRoadSet ) + { + smServerMeshRoadSet = new SimSet(); + smServerMeshRoadSet->registerObject( "ServerMeshRoadSet" ); + Sim::getRootGroup()->addObject( smServerMeshRoadSet ); + } + + return smServerMeshRoadSet; +} + +void MeshRoad::prepRenderImage( SceneRenderState* state ) +{ + if ( mNodes.size() <= 1 ) + return; + + RenderPassManager *renderPass = state->getRenderPass(); + + // Normal Road RenderInstance + // Always rendered when the editor is not open + // otherwise obey the smShowRoad flag + if ( smShowRoad || !smEditorOpen ) + { + MeshRenderInst coreRI; + coreRI.clear(); + coreRI.objectToWorld = &MatrixF::Identity; + coreRI.worldToCamera = renderPass->allocSharedXform(RenderPassManager::View); + coreRI.projection = renderPass->allocSharedXform(RenderPassManager::Projection); + coreRI.type = RenderPassManager::RIT_Mesh; + + BaseMatInstance *matInst; + for ( U32 i = 0; i < SurfaceCount; i++ ) + { + matInst = state->getOverrideMaterial( mMatInst[i] ); + if ( !matInst ) + continue; + + // Get the lights if we haven't already. + if ( matInst->isForwardLit() && !coreRI.lights[0] ) + { + LightQuery query; + query.init( getWorldSphere() ); + query.getLights( coreRI.lights, 8 ); + } + + MeshRenderInst *ri = renderPass->allocInst(); + *ri = coreRI; + + // Currently rendering whole road, fix to cull and batch + // per segment. + + // Set the correct material for rendering. + ri->matInst = matInst; + ri->vertBuff = &mVB[i]; + ri->primBuff = &mPB[i]; + + ri->prim = renderPass->allocPrim(); + ri->prim->type = GFXTriangleList; + ri->prim->minIndex = 0; + ri->prim->startIndex = 0; + ri->prim->numPrimitives = mTriangleCount[i]; + ri->prim->startVertex = 0; + ri->prim->numVertices = mVertCount[i]; + + // We sort by the material then vertex buffer. + ri->defaultKey = matInst->getStateHint(); + ri->defaultKey2 = (U32)ri->vertBuff; // Not 64bit safe! + + renderPass->addInst( ri ); + } + } + + // Debug RenderInstance + // Only when editor is open. + if ( smEditorOpen ) + { + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &MeshRoad::_debugRender ); + ri->type = RenderPassManager::RIT_Editor; + state->getRenderPass()->addInst( ri ); + } +} + +void MeshRoad::_initMaterial() +{ + for ( U32 i = 0; i < SurfaceCount; i++ ) + { + if ( mMatInst[i] ) + SAFE_DELETE( mMatInst[i] ); + + if ( mMaterial[i] ) + mMatInst[i] = mMaterial[i]->createMatInstance(); + else + mMatInst[i] = MATMGR->createMatInstance( "WarningMaterial" ); + + mMatInst[i]->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat() ); + } +} + +void MeshRoad::_debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* ) +{ + //MeshRoadConvex convex; + //buildConvex( Box3F(true), convex ); + //convex.render(); + //GFXDrawUtil *drawer = GFX->getDrawUtil(); + + //GFX->setStateBlock( smStateBlock ); + return; + /* + U32 convexCount = mDebugConvex.size(); + + PrimBuild::begin( GFXTriangleList, convexCount * 12 ); + PrimBuild::color4i( 0, 0, 255, 155 ); + + for ( U32 i = 0; i < convexCount; i++ ) + { + MeshRoadConvex *convex = mDebugConvex[i]; + + Point3F a = convex->verts[0]; + Point3F b = convex->verts[1]; + Point3F c = convex->verts[2]; + Point3F p = convex->verts[3]; + + //mObjToWorld.mulP(a); + //mObjToWorld.mulP(b); + //mObjToWorld.mulP(c); + //mObjToWorld.mulP(p); + + PrimBuild::vertex3fv( c ); + PrimBuild::vertex3fv( b ); + PrimBuild::vertex3fv( a ); + + PrimBuild::vertex3fv( b ); + PrimBuild::vertex3fv( a ); + PrimBuild::vertex3fv( p ); + + PrimBuild::vertex3fv( c ); + PrimBuild::vertex3fv( b ); + PrimBuild::vertex3fv( p ); + + PrimBuild::vertex3fv( a ); + PrimBuild::vertex3fv( c ); + PrimBuild::vertex3fv( p ); + } + + PrimBuild::end(); + + for ( U32 i = 0; i < mSegments.size(); i++ ) + { + ///GFX->getDrawUtil()->drawWireBox( mSegments[i].worldbounds, ColorI(255,0,0,255) ); + } + + GFX->enterDebugEvent( ColorI( 255, 0, 0 ), "DecalRoad_debugRender" ); + GFXTransformSaver saver; + + GFX->setStateBlock( smStateBlock ); + + Point3F size(1,1,1); + ColorI color( 255, 0, 0, 255 ); + + if ( smShowBatches ) + { + for ( U32 i = 0; i < mBatches.size(); i++ ) + { + const Box3F &box = mBatches[i].bounds; + Point3F center; + box.getCenter( ¢er ); + + GFX->getDrawUtil()->drawWireCube( ( box.maxExtents - box.minExtents ) * 0.5f, center, ColorI(255,100,100,255) ); + } + } + + GFX->leaveDebugEvent(); + */ +} + +U32 MeshRoad::packUpdate(NetConnection * con, U32 mask, BitStream * stream) +{ + U32 retMask = Parent::packUpdate(con, mask, stream); + + if ( stream->writeFlag( mask & MeshRoadMask ) ) + { + // Write Object Transform. + stream->writeAffineTransform( mObjToWorld ); + + // Write Materials + stream->write( mMaterialName[0] ); + stream->write( mMaterialName[1] ); + stream->write( mMaterialName[2] ); + + stream->write( mTextureLength ); + stream->write( mBreakAngle ); + stream->write( mWidthSubdivisions ); + } + + if ( stream->writeFlag( mask & NodeMask ) ) + { + const U32 nodeByteSize = 32; // Based on sending all of a node's parameters + + // Test if we can fit all of our nodes within the current stream. + // We make sure we leave 100 bytes still free in the stream for whatever + // may follow us. + S32 allowedBytes = stream->getWriteByteSize() - 100; + if ( stream->writeFlag( (nodeByteSize * mNodes.size()) < allowedBytes ) ) + { + // All nodes should fit, so send them out now. + stream->writeInt( mNodes.size(), 16 ); + + for ( U32 i = 0; i < mNodes.size(); i++ ) + { + mathWrite( *stream, mNodes[i].point ); + stream->write( mNodes[i].width ); + stream->write( mNodes[i].depth ); + mathWrite( *stream, mNodes[i].normal ); + } + } + else + { + // There isn't enough space left in the stream for all of the + // nodes. Batch them up into NetEvents. + U32 id = gServerNodeListManager->nextListId(); + U32 count = 0; + U32 index = 0; + while (count < mNodes.size()) + { + count += NodeListManager::smMaximumNodesPerEvent; + if (count > mNodes.size()) + { + count = mNodes.size(); + } + + MeshRoadNodeEvent* event = new MeshRoadNodeEvent(); + event->mId = id; + event->mTotalNodes = mNodes.size(); + event->mLocalListStart = index; + + for (; indexmPositions.push_back( mNodes[index].point ); + event->mWidths.push_back( mNodes[index].width ); + event->mDepths.push_back( mNodes[index].depth ); + event->mNormals.push_back( mNodes[index].normal ); + } + + con->postNetEvent( event ); + } + + stream->write( id ); + } + } + + stream->writeFlag( mask & RegenMask ); + + // Were done ... + return retMask; +} + +void MeshRoad::unpackUpdate(NetConnection * con, BitStream * stream) +{ + // Unpack Parent. + Parent::unpackUpdate(con, stream); + + // MeshRoadMask + if(stream->readFlag()) + { + MatrixF ObjectMatrix; + stream->readAffineTransform(&ObjectMatrix); + Parent::setTransform(ObjectMatrix); + + // Read Materials... + Material *pMat = NULL; + + for ( U32 i = 0; i < SurfaceCount; i++ ) + { + stream->read( &mMaterialName[i] ); + + if ( !Sim::findObject( mMaterialName[i], pMat ) ) + Con::printf( "DecalRoad::unpackUpdate, failed to find Material of name &s!", mMaterialName[i].c_str() ); + else + mMaterial[i] = pMat; + } + + if ( isProperlyAdded() ) + _initMaterial(); + + stream->read( &mTextureLength ); + + stream->read( &mBreakAngle ); + + stream->read( &mWidthSubdivisions ); + } + + // NodeMask + if ( stream->readFlag() ) + { + if (stream->readFlag()) + { + // Nodes have been passed in this update + U32 count = stream->readInt( 16 ); + + mNodes.clear(); + + Point3F pos, normal; + F32 width, depth; + for ( U32 i = 0; i < count; i++ ) + { + mathRead( *stream, &pos ); + stream->read( &width ); + stream->read( &depth ); + mathRead( *stream, &normal ); + _addNode( pos, width, depth, normal ); + } + } + else + { + // Nodes will arrive as events + U32 id; + stream->read( &id ); + + // Check if the road's nodes made it here before we did. + NodeListManager::NodeList* list = NULL; + if ( gClientNodeListManager->findListById( id, &list, true) ) + { + // Work with the completed list + MeshRoadNodeList* roadList = dynamic_cast( list ); + if (roadList) + buildNodesFromList( roadList ); + + delete list; + } + else + { + // Nodes have not yet arrived, so register our interest in the list + MeshRoadNodeListNotify* notify = new MeshRoadNodeListNotify( this, id ); + gClientNodeListManager->registerNotification( notify ); + } + } + } + + if ( stream->readFlag() && isProperlyAdded() ) + _regenerate(); +} + +void MeshRoad::setTransform( const MatrixF &mat ) +{ + for ( U32 i = 0; i < mNodes.size(); i++ ) + { + mWorldToObj.mulP( mNodes[i].point ); + mat.mulP( mNodes[i].point ); + } + + Parent::setTransform( mat ); + + if ( mPhysicsRep ) + mPhysicsRep->setTransform( mat ); + + // Regenerate and update the client + _regenerate(); + setMaskBits( NodeMask | RegenMask ); +} + +void MeshRoad::setScale( const VectorF &scale ) +{ + // We ignore scale requests from the editor + // right now. + + //Parent::setScale( scale ); +} + +void MeshRoad::buildConvex(const Box3F& box, Convex* convex) +{ + if ( mSlices.size() < 2 ) + return; + + mConvexList->collectGarbage(); + mDebugConvex.clear(); + + Box3F realBox = box; + mWorldToObj.mul(realBox); + realBox.minExtents.convolveInverse(mObjScale); + realBox.maxExtents.convolveInverse(mObjScale); + + if (realBox.isOverlapped(getObjBox()) == false) + return; + + U32 segmentCount = mSegments.size(); + + // Create convex(s) for each segment + for ( U32 i = 0; i < segmentCount; i++ ) + { + const MeshRoadSegment &segment = mSegments[i]; + + // Is this segment overlapped? + if ( !segment.getWorldBounds().isOverlapped( box ) ) + continue; + + // Each segment has 6 faces + for ( U32 j = 0; j < 6; j++ ) + { + // Only first segment has front face + if ( j == 4 && i != 0 ) + continue; + // Only last segment has back face + if ( j == 5 && i != segmentCount-1 ) + continue; + + // Each face has 2 convex(s) + for ( U32 k = 0; k < 2; k++ ) + { + // See if this convex exists in the working set already... + Convex* cc = 0; + CollisionWorkingList& wl = convex->getWorkingList(); + for ( CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext ) + { + if ( itr->mConvex->getType() == MeshRoadConvexType ) + { + MeshRoadConvex *pConvex = static_cast(itr->mConvex); + + if ( pConvex->pRoad == this && + pConvex->segmentId == i && + pConvex->faceId == j && + pConvex->triangleId == k ) + { + cc = itr->mConvex; + break; + } + } + } + if (cc) + continue; + + // Get the triangle... + U32 idx0 = gIdxArray[j][k][0]; + U32 idx1 = gIdxArray[j][k][1]; + U32 idx2 = gIdxArray[j][k][2]; + + Point3F a = segment[idx0]; + Point3F b = segment[idx1]; + Point3F c = segment[idx2]; + + // Transform the result into object space! + //mWorldToObj.mulP( a ); + //mWorldToObj.mulP( b ); + //mWorldToObj.mulP( c ); + + PlaneF p( c, b, a ); + Point3F peak = ((a + b + c) / 3.0f) + (p * 0.15f); + + // Set up the convex... + MeshRoadConvex *cp = new MeshRoadConvex(); + + mConvexList->registerObject( cp ); + convex->addToWorkingList( cp ); + + cp->mObject = this; + cp->pRoad = this; + cp->segmentId = i; + cp->faceId = j; + cp->triangleId = k; + + cp->normal = p; + cp->verts[0] = c; + cp->verts[1] = b; + cp->verts[2] = a; + cp->verts[3] = peak; + + // Update the bounding box. + Box3F &bounds = cp->box; + bounds.minExtents.set( F32_MAX, F32_MAX, F32_MAX ); + bounds.maxExtents.set( -F32_MAX, -F32_MAX, -F32_MAX ); + + bounds.minExtents.setMin( a ); + bounds.minExtents.setMin( b ); + bounds.minExtents.setMin( c ); + bounds.minExtents.setMin( peak ); + + bounds.maxExtents.setMax( a ); + bounds.maxExtents.setMax( b ); + bounds.maxExtents.setMax( c ); + bounds.maxExtents.setMax( peak ); + + mDebugConvex.push_back(cp); + } + } + } +} + +bool MeshRoad::buildPolyList( PolyListContext, AbstractPolyList* polyList, const Box3F &box, const SphereF & ) +{ + if ( mSlices.size() < 2 ) + return false; + + polyList->setTransform( &MatrixF::Identity, Point3F::One ); + polyList->setObject(this); + + // JCF: optimize this to not always add everything. + + return buildSegmentPolyList( polyList, 0, mSegments.size() - 1, true, true ); +} + +bool MeshRoad::buildSegmentPolyList( AbstractPolyList* polyList, U32 startSegIdx, U32 endSegIdx, bool capFront, bool capEnd ) +{ + if ( mSlices.size() < 2 ) + return false; + + // Add verts + for ( U32 i = startSegIdx; i <= endSegIdx; i++ ) + { + const MeshRoadSegment &seg = mSegments[i]; + + if ( i == startSegIdx ) + { + polyList->addPoint( seg.slice0->p0 ); + polyList->addPoint( seg.slice0->p2 ); + polyList->addPoint( seg.slice0->pb0 ); + polyList->addPoint( seg.slice0->pb2 ); + } + + polyList->addPoint( seg.slice1->p0 ); + polyList->addPoint( seg.slice1->p2 ); + polyList->addPoint( seg.slice1->pb0 ); + polyList->addPoint( seg.slice1->pb2 ); + } + + // Temporaries to hold indices for the corner points of a quad. + S32 p00, p01, p11, p10; + S32 pb00, pb01, pb11, pb10; + U32 offset = 0; + + DebugDrawer *ddraw = NULL;//DebugDrawer::get(); + ClippedPolyList *cpolyList = dynamic_cast(polyList); + MatrixF mat; + Point3F scale; + if ( cpolyList ) + cpolyList->getTransform( &mat, &scale ); + + + for ( U32 i = startSegIdx; i <= endSegIdx; i++ ) + { + p00 = offset; + p10 = offset + 1; + pb00 = offset + 2; + pb10 = offset + 3; + p01 = offset + 4; + p11 = offset + 5; + pb01 = offset + 6; + pb11 = offset + 7; + + // Top Face + + polyList->begin( 0,0 ); + polyList->vertex( p00 ); + polyList->vertex( p01 ); + polyList->vertex( p11 ); + polyList->plane( p00, p01, p11 ); + polyList->end(); + if ( ddraw && cpolyList ) + { + Point3F v0 = cpolyList->mVertexList[p00].point; + mat.mulP( v0 ); + Point3F v1 = cpolyList->mVertexList[p01].point; + mat.mulP( v1 ); + Point3F v2 = cpolyList->mVertexList[p11].point; + mat.mulP( v2 ); + ddraw->drawTri( v0, v1, v2 ); + ddraw->setLastZTest( false ); + ddraw->setLastTTL( 0 ); + } + + polyList->begin( 0,0 ); + polyList->vertex( p00 ); + polyList->vertex( p11 ); + polyList->vertex( p10 ); + polyList->plane( p00, p11, p10 ); + polyList->end(); + if ( ddraw && cpolyList ) + { + ddraw->drawTri( cpolyList->mVertexList[p00].point, cpolyList->mVertexList[p11].point, cpolyList->mVertexList[p10].point ); + ddraw->setLastTTL( 0 ); + } + + // Left Face + + polyList->begin( 0,0 ); + polyList->vertex( pb00 ); + polyList->vertex( pb01 ); + polyList->vertex( p01 ); + polyList->plane( pb00, pb01, p01 ); + polyList->end(); + + polyList->begin( 0,0 ); + polyList->vertex( pb00 ); + polyList->vertex( p01 ); + polyList->vertex( p00 ); + polyList->plane( pb00, p01, p00 ); + polyList->end(); + + // Right Face + + polyList->begin( 0,0 ); + polyList->vertex( p10 ); + polyList->vertex( p11 ); + polyList->vertex( pb11 ); + polyList->plane( p10, p11, pb11 ); + polyList->end(); + + polyList->begin( 0,0 ); + polyList->vertex( p10 ); + polyList->vertex( pb11 ); + polyList->vertex( pb10 ); + polyList->plane( p10, pb11, pb10 ); + polyList->end(); + + // Bottom Face + + polyList->begin( 0,0 ); + polyList->vertex( pb00 ); + polyList->vertex( pb10 ); + polyList->vertex( pb11 ); + polyList->plane( pb00, pb10, pb11 ); + polyList->end(); + + polyList->begin( 0,0 ); + polyList->vertex( pb00 ); + polyList->vertex( pb11 ); + polyList->vertex( pb01 ); + polyList->plane( pb00, pb11, pb01 ); + polyList->end(); + + // Front Face + + if ( i == startSegIdx && capFront ) + { + polyList->begin( 0,0 ); + polyList->vertex( p00 ); + polyList->vertex( p10 ); + polyList->vertex( pb10 ); + polyList->plane( p00, p10, pb10 ); + polyList->end(); + + polyList->begin( 0,0 ); + polyList->vertex( p00 ); + polyList->vertex( pb10 ); + polyList->vertex( pb00 ); + polyList->plane( p00, pb10, pb00 ); + polyList->end(); + } + + // Back Face + if ( i == endSegIdx && capEnd ) + { + polyList->begin( 0,0 ); + polyList->vertex( p01 ); + polyList->vertex( pb01 ); + polyList->vertex( pb11 ); + polyList->plane( p01, pb01, pb11 ); + polyList->end(); + + polyList->begin( 0,0 ); + polyList->vertex( p01 ); + polyList->vertex( pb11 ); + polyList->vertex( p11 ); + polyList->plane( p01, pb11, p11 ); + polyList->end(); + } + + offset += 4; + } + + return true; +} + +bool MeshRoad::castRay( const Point3F &s, const Point3F &e, RayInfo *info ) +{ + Point3F start = s; + Point3F end = e; + mObjToWorld.mulP(start); + mObjToWorld.mulP(end); + + F32 out = 1.0f; // The output fraction/percentage along the line defined by s and e + VectorF norm(0.0f, 0.0f, 0.0f); // The normal of the face intersected + + Vector hitSegments; + + for ( U32 i = 0; i < mSegments.size(); i++ ) + { + const MeshRoadSegment &segment = mSegments[i]; + + F32 t; + VectorF n; + + if ( segment.getWorldBounds().collideLine( start, end, &t, &n ) ) + { + hitSegments.increment(); + hitSegments.last().t = t; + hitSegments.last().idx = i; + } + } + + dQsort( hitSegments.address(), hitSegments.size(), sizeof(MeshRoadHitSegment), compareHitSegments ); + + U32 idx0, idx1, idx2; + F32 t; + + for ( U32 i = 0; i < hitSegments.size(); i++ ) + { + U32 segIdx = hitSegments[i].idx; + const MeshRoadSegment &segment = mSegments[segIdx]; + + // Each segment has 6 faces + for ( U32 j = 0; j < 6; j++ ) + { + if ( j == 4 && segIdx != 0 ) + continue; + + if ( j == 5 && segIdx != mSegments.size() - 1 ) + continue; + + // Each face has 2 triangles + for ( U32 k = 0; k < 2; k++ ) + { + idx0 = gIdxArray[j][k][0]; + idx1 = gIdxArray[j][k][1]; + idx2 = gIdxArray[j][k][2]; + + const Point3F &v0 = segment[idx0]; + const Point3F &v1 = segment[idx1]; + const Point3F &v2 = segment[idx2]; + + if ( !MathUtils::mLineTriangleCollide( start, end, + v2, v1, v0, + NULL, + &t ) ) + continue; + + if ( t >= 0.0f && t < 1.0f && t < out ) + { + out = t; + norm = PlaneF( v0, v1, v2 ); + } + } + } + + if (out >= 0.0f && out < 1.0f) + break; + } + + if (out >= 0.0f && out < 1.0f) + { + info->t = out; + info->normal = norm; + info->point.interpolate(start, end, out); + info->face = -1; + info->object = this; + + return true; + } + + return false; +} + +bool MeshRoad::collideBox(const Point3F &start, const Point3F &end, RayInfo* info) +{ + Con::warnf( "MeshRoad::collideBox() - not yet implemented!" ); + return Parent::collideBox( start, end, info ); +} + +void MeshRoad::_regenerate() +{ + if ( mNodes.size() == 0 ) + return; + + const Point3F &nodePt = mNodes.first().point; + + MatrixF mat( true ); + mat.setPosition( nodePt ); + Parent::setTransform( mat ); + + _generateSlices(); + + // Make sure we are in the correct bins given our world box. + if( getSceneManager() != NULL ) + getSceneManager()->notifyObjectDirty( this ); +} + +void MeshRoad::_generateSlices() +{ + if ( mNodes.size() < 2 ) + return; + + // Create the spline, initialized with the MeshRoadNode(s) + U32 nodeCount = mNodes.size(); + MeshRoadSplineNode *splineNodes = new MeshRoadSplineNode[nodeCount]; + + for ( U32 i = 0; i < nodeCount; i++ ) + { + MeshRoadSplineNode &splineNode = splineNodes[i]; + const MeshRoadNode &node = mNodes[i]; + + splineNode.x = node.point.x; + splineNode.y = node.point.y; + splineNode.z = node.point.z; + splineNode.width = node.width; + splineNode.depth = node.depth; + splineNode.normal = node.normal; + } + + CatmullRom spline; + spline.initialize( nodeCount, splineNodes ); + delete [] splineNodes; + + mSlices.clear(); + + VectorF lastBreakVector(0,0,0); + MeshRoadSlice slice; + MeshRoadSplineNode lastBreakNode; + lastBreakNode = spline.evaluate(0.0f); + + for ( U32 i = 1; i < mNodes.size(); i++ ) + { + F32 t1 = spline.getTime(i); + F32 t0 = spline.getTime(i-1); + + F32 segLength = spline.arcLength( t0, t1 ); + + U32 numSegments = mCeil( segLength / MIN_METERS_PER_SEGMENT ); + numSegments = getMax( numSegments, (U32)1 ); + F32 tstep = ( t1 - t0 ) / numSegments; + + U32 startIdx = 0; + U32 endIdx = ( i == nodeCount - 1 ) ? numSegments + 1 : numSegments; + + for ( U32 j = startIdx; j < endIdx; j++ ) + { + F32 t = t0 + tstep * j; + MeshRoadSplineNode splineNode = spline.evaluate(t); + + VectorF toNodeVec = splineNode.getPosition() - lastBreakNode.getPosition(); + toNodeVec.normalizeSafe(); + + if ( lastBreakVector.isZero() ) + lastBreakVector = toNodeVec; + + F32 angle = mRadToDeg( mAcos( mDot( toNodeVec, lastBreakVector ) ) ); + + if ( j == startIdx || + ( j == endIdx - 1 && i == mNodes.size() - 1 ) || + angle > mBreakAngle ) + { + // Push back a spline node + slice.p1.set( splineNode.x, splineNode.y, splineNode.z ); + slice.width = splineNode.width; + slice.depth = splineNode.depth; + slice.normal = splineNode.normal; + slice.normal.normalize(); + slice.parentNodeIdx = i-1; + slice.t = t; + mSlices.push_back( slice ); + + lastBreakVector = splineNode.getPosition() - lastBreakNode.getPosition(); + lastBreakVector.normalizeSafe(); + + lastBreakNode = splineNode; + } + } + } + + // + // Calculate uvec, fvec, and rvec for all slices + // + + MatrixF mat(true); + + for ( U32 i = 0; i < mSlices.size(); i++ ) + { + calcSliceTransform( i, mat ); + mat.getColumn( 0, &mSlices[i].rvec ); + mat.getColumn( 1, &mSlices[i].fvec ); + mat.getColumn( 2, &mSlices[i].uvec ); + } + + // + // Calculate p0/p2/pb0/pb2 for all slices + // + for ( U32 i = 0; i < mSlices.size(); i++ ) + { + MeshRoadSlice *slice = &mSlices[i]; + slice->p0 = slice->p1 - slice->rvec * slice->width * 0.5f; + slice->p2 = slice->p1 + slice->rvec * slice->width * 0.5f; + slice->pb0 = slice->p0 - slice->uvec * slice->depth; + slice->pb2 = slice->p2 - slice->uvec * slice->depth; + } + + // Generate the object/world bounds + Box3F box; + for ( U32 i = 0; i < mSlices.size(); i++ ) + { + const MeshRoadSlice &slice = mSlices[i]; + + if ( i == 0 ) + { + box.minExtents = slice.p0; + box.maxExtents = slice.p2; + box.extend( slice.pb0 ); + box.extend( slice.pb2 ); + } + else + { + box.extend( slice.p0 ); + box.extend( slice.p2 ); + box.extend( slice.pb0 ); + box.extend( slice.pb2 ); + } + } + + Point3F pos = getPosition(); + + mWorldBox = box; + resetObjectBox(); + + _generateSegments(); +} + +void MeshRoad::_generateSegments() +{ + mSegments.clear(); + + for ( U32 i = 0; i < mSlices.size() - 1; i++ ) + { + MeshRoadSegment seg( &mSlices[i], &mSlices[i+1], getWorldTransform() ); + + mSegments.push_back( seg ); + } + + if ( isClientObject() ) + _generateVerts(); + + if ( PHYSICSMGR ) + { + SAFE_DELETE( mPhysicsRep ); + //mPhysicsRep = PHYSICSMGR->createBody(); + } +} + +void MeshRoad::_generateVerts() +{ + const U32 widthDivisions = getMax( 0, mWidthSubdivisions ); + const F32 divisionStep = 1.0f / (F32)( widthDivisions + 1 ); + const U32 sliceCount = mSlices.size(); + const U32 segmentCount = mSegments.size(); + + mVertCount[Top] = ( 2 + widthDivisions ) * sliceCount; + mTriangleCount[Top] = segmentCount * 2 * ( widthDivisions + 1 ); + + mVertCount[Bottom] = sliceCount * 2; + mTriangleCount[Bottom] = segmentCount * 2; + + mVertCount[Side] = sliceCount * 4; + mTriangleCount[Side] = segmentCount * 4 + 4; + + // Calculate TexCoords for Slices + + F32 texCoordV = 0.0f; + mSlices[0].texCoordV = 0.0f; + + for ( U32 i = 1; i < sliceCount; i++ ) + { + MeshRoadSlice &slice = mSlices[i]; + MeshRoadSlice &prevSlice = mSlices[i-1]; + + // Increment the textCoordV for the next slice. + F32 len = ( slice.p1 - prevSlice.p1 ).len(); + texCoordV += len / mTextureLength; + + slice.texCoordV = texCoordV; + } + + // Make Vertex Buffers + GFXVertexPNTT *pVert = NULL; + U32 vertCounter = 0; + + // Top Buffers... + + mVB[Top].set( GFX, mVertCount[Top], GFXBufferTypeStatic ); + pVert = mVB[Top].lock(); + vertCounter = 0; + + for ( U32 i = 0; i < sliceCount; i++ ) + { + MeshRoadSlice &slice = mSlices[i]; + + pVert->point = slice.p0; + pVert->normal = slice.uvec; + pVert->tangent = slice.fvec; + pVert->texCoord.set(1,slice.texCoordV); + pVert++; + vertCounter++; + + for ( U32 j = 0; j < widthDivisions; j++ ) + { + const F32 t = divisionStep * (F32)( j + 1 ); + + pVert->point.interpolate( slice.p0, slice.p2, t ); + pVert->normal = slice.uvec; + pVert->tangent = slice.fvec; + pVert->texCoord.set( 1.0f - t, slice.texCoordV ); + pVert++; + vertCounter++; + } + + pVert->point = slice.p2; + pVert->normal = slice.uvec; + pVert->tangent = slice.fvec; + pVert->texCoord.set( 0, slice.texCoordV ); + pVert++; + vertCounter++; + } + + AssertFatal( vertCounter == mVertCount[Top], "MeshRoad, wrote incorrect number of verts in mVB[Top]!" ); + + mVB[Top].unlock(); + + // Bottom Buffer... + + mVB[Bottom].set( GFX, mVertCount[Bottom], GFXBufferTypeStatic ); + pVert = mVB[Bottom].lock(); + vertCounter = 0; + + for ( U32 i = 0; i < sliceCount; i++ ) + { + MeshRoadSlice &slice = mSlices[i]; + + pVert->point = slice.pb2; + pVert->normal = -slice.uvec; + pVert->tangent = slice.fvec; + pVert->texCoord.set(0,slice.texCoordV); + pVert++; + vertCounter++; + + pVert->point = slice.pb0; + pVert->normal = -slice.uvec; + pVert->tangent = slice.fvec; + pVert->texCoord.set(1,slice.texCoordV); + pVert++; + vertCounter++; + } + + AssertFatal( vertCounter == mVertCount[Bottom], "MeshRoad, wrote incorrect number of verts in mVB[Bottom]!" ); + + mVB[Bottom].unlock(); + + // Side Buffers... + + mVB[Side].set( GFX, mVertCount[Side], GFXBufferTypeStatic ); + pVert = mVB[Side].lock(); + vertCounter = 0; + + for ( U32 i = 0; i < sliceCount; i++ ) + { + MeshRoadSlice &slice = mSlices[i]; + + pVert->point = slice.p0; + pVert->normal = -slice.rvec; + pVert->tangent = slice.fvec; + pVert->texCoord.set(1,slice.texCoordV); + pVert++; + vertCounter++; + + pVert->point = slice.p2; + pVert->normal = slice.rvec; + pVert->tangent = slice.fvec; + pVert->texCoord.set(1,slice.texCoordV); + pVert++; + vertCounter++; + + pVert->point = slice.pb0; + pVert->normal = -slice.rvec; + pVert->tangent = slice.fvec; + pVert->texCoord.set(0,slice.texCoordV); + pVert++; + vertCounter++; + + pVert->point = slice.pb2; + pVert->normal = slice.rvec; + pVert->tangent = slice.fvec; + pVert->texCoord.set(0,slice.texCoordV); + pVert++; + vertCounter++; + } + + AssertFatal( vertCounter == mVertCount[Side], "MeshRoad, wrote incorrect number of verts in mVB[Side]!" ); + + mVB[Side].unlock(); + + // Make Primitive Buffers + U32 p00, p01, p11, p10; + U32 pb00, pb01, pb11, pb10; + U32 offset = 0; + U16 *pIdx = NULL; + U32 curIdx = 0; + + // Top Primitive Buffer + + mPB[Top].set( GFX, mTriangleCount[Top] * 3, mTriangleCount[Top], GFXBufferTypeStatic ); + + mPB[Top].lock(&pIdx); + curIdx = 0; + offset = 0; + + const U32 rowStride = 2 + widthDivisions; + + for ( U32 i = 0; i < mSegments.size(); i++ ) + { + for ( U32 j = 0; j < widthDivisions + 1; j++ ) + { + p00 = offset; + p10 = offset + 1; + p01 = offset + rowStride; + p11 = offset + rowStride + 1; + + pIdx[curIdx] = p00; + curIdx++; + pIdx[curIdx] = p01; + curIdx++; + pIdx[curIdx] = p11; + curIdx++; + + pIdx[curIdx] = p00; + curIdx++; + pIdx[curIdx] = p11; + curIdx++; + pIdx[curIdx] = p10; + curIdx++; + + offset += 1; + } + + offset += 1; + } + + + AssertFatal( curIdx == mTriangleCount[Top] * 3, "MeshRoad, wrote incorrect number of indices in mPB[Top]!" ); + + mPB[Top].unlock(); + + // Bottom Primitive Buffer + + mPB[Bottom].set( GFX, mTriangleCount[Bottom] * 3, mTriangleCount[Bottom], GFXBufferTypeStatic ); + + mPB[Bottom].lock(&pIdx); + curIdx = 0; + offset = 0; + + for ( U32 i = 0; i < mSegments.size(); i++ ) + { + p00 = offset; + p10 = offset + 1; + p01 = offset + 2; + p11 = offset + 3; + + pIdx[curIdx] = p00; + curIdx++; + pIdx[curIdx] = p01; + curIdx++; + pIdx[curIdx] = p11; + curIdx++; + + pIdx[curIdx] = p00; + curIdx++; + pIdx[curIdx] = p11; + curIdx++; + pIdx[curIdx] = p10; + curIdx++; + + offset += 2; + } + + AssertFatal( curIdx == mTriangleCount[Bottom] * 3, "MeshRoad, wrote incorrect number of indices in mPB[Bottom]!" ); + + mPB[Bottom].unlock(); + + // Side Primitive Buffer + + mPB[Side].set( GFX, mTriangleCount[Side] * 3, mTriangleCount[Side], GFXBufferTypeStatic ); + + mPB[Side].lock(&pIdx); + curIdx = 0; + offset = 0; + + for ( U32 i = 0; i < mSegments.size(); i++ ) + { + p00 = offset; + p10 = offset + 1; + pb00 = offset + 2; + pb10 = offset + 3; + p01 = offset + 4; + p11 = offset + 5; + pb01 = offset + 6; + pb11 = offset + 7; + + // Left Side + + pIdx[curIdx] = pb00; + curIdx++; + pIdx[curIdx] = pb01; + curIdx++; + pIdx[curIdx] = p01; + curIdx++; + + pIdx[curIdx] = pb00; + curIdx++; + pIdx[curIdx] = p01; + curIdx++; + pIdx[curIdx] = p00; + curIdx++; + + // Right Side + + pIdx[curIdx] = p10; + curIdx++; + pIdx[curIdx] = p11; + curIdx++; + pIdx[curIdx] = pb11; + curIdx++; + + pIdx[curIdx] = p10; + curIdx++; + pIdx[curIdx] = pb11; + curIdx++; + pIdx[curIdx] = pb10; + curIdx++; + + offset += 4; + } + + // Cap the front and back ends + pIdx[curIdx++] = 0; + pIdx[curIdx++] = 1; + pIdx[curIdx++] = 3; + pIdx[curIdx++] = 0; + pIdx[curIdx++] = 3; + pIdx[curIdx++] = 2; + + pIdx[curIdx++] = offset + 0; + pIdx[curIdx++] = offset + 3; + pIdx[curIdx++] = offset + 1; + pIdx[curIdx++] = offset + 0; + pIdx[curIdx++] = offset + 2; + pIdx[curIdx++] = offset + 3; + + + AssertFatal( curIdx == mTriangleCount[Side] * 3, "MeshRoad, wrote incorrect number of indices in mPB[Side]!" ); + + mPB[Side].unlock(); +} + +const MeshRoadNode& MeshRoad::getNode( U32 idx ) +{ + return mNodes[idx]; +} + +VectorF MeshRoad::getNodeNormal( U32 idx ) +{ + if ( mNodes.size() - 1 < idx ) + return VectorF::Zero; + + return mNodes[idx].normal; +} + +void MeshRoad::setNodeNormal( U32 idx, const VectorF &normal ) +{ + if ( mNodes.size() - 1 < idx ) + return; + + mNodes[idx].normal = normal; + + regenerate(); + + setMaskBits( NodeMask | RegenMask ); +} + +Point3F MeshRoad::getNodePosition( U32 idx ) +{ + if ( mNodes.size() - 1 < idx ) + return Point3F::Zero; + + return mNodes[idx].point; +} + +void MeshRoad::setNodePosition( U32 idx, const Point3F &pos ) +{ + if ( mNodes.size() - 1 < idx ) + return; + + mNodes[idx].point = pos; + + regenerate(); + + setMaskBits( NodeMask | RegenMask ); +} + +U32 MeshRoad::addNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal ) +{ + U32 idx = _addNode( pos, width, depth, normal ); + + regenerate(); + + setMaskBits( NodeMask | RegenMask ); + + return idx; +} + +void MeshRoad::buildNodesFromList( MeshRoadNodeList* list ) +{ + mNodes.clear(); + + for (U32 i=0; imPositions.size(); ++i) + { + _addNode( list->mPositions[i], list->mWidths[i], list->mDepths[i], list->mNormals[i] ); + } + + _regenerate(); +} + +U32 MeshRoad::insertNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal, const U32 &idx ) +{ + U32 ret = _insertNode( pos, width, depth, normal, idx ); + + regenerate(); + + setMaskBits( NodeMask | RegenMask ); + + return ret; +} + +void MeshRoad::setNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal, const U32 &idx ) +{ + if ( mNodes.size() - 1 < idx ) + return; + + MeshRoadNode &node = mNodes[idx]; + + node.point = pos; + node.width = width; + node.depth = depth; + node.normal = normal; + + regenerate(); + + setMaskBits( NodeMask | RegenMask ); +} + +void MeshRoad::setNodeWidth( U32 idx, F32 meters ) +{ + meters = mClampF( meters, MIN_NODE_WIDTH, MAX_NODE_WIDTH ); + + if ( mNodes.size() - 1 < idx ) + return; + + mNodes[idx].width = meters; + _regenerate(); + + setMaskBits( RegenMask | NodeMask ); +} + +F32 MeshRoad::getNodeWidth( U32 idx ) +{ + if ( mNodes.size() - 1 < idx ) + return -1.0f; + + return mNodes[idx].width; +} + +void MeshRoad::setNodeDepth( U32 idx, F32 meters ) +{ + meters = mClampF( meters, MIN_NODE_DEPTH, MAX_NODE_DEPTH ); + + if ( mNodes.size() - 1 < idx ) + return; + + mNodes[idx].depth = meters; + + _regenerate(); + + setMaskBits( MeshRoadMask | RegenMask | NodeMask ); +} + +F32 MeshRoad::getNodeDepth( U32 idx ) +{ + if ( mNodes.size() - 1 < idx ) + return -1.0f; + + return mNodes[idx].depth; +} + +MatrixF MeshRoad::getNodeTransform( U32 idx ) +{ + MatrixF mat(true); + + if ( mNodes.size() - 1 < idx ) + return mat; + + bool hasNext = idx + 1 < mNodes.size(); + bool hasPrev = (S32)idx - 1 > 0; + + const MeshRoadNode &node = mNodes[idx]; + + VectorF fvec( 0, 1, 0 ); + + if ( hasNext ) + { + fvec = mNodes[idx+1].point - node.point; + fvec.normalizeSafe(); + } + else if ( hasPrev ) + { + fvec = node.point - mNodes[idx-1].point; + fvec.normalizeSafe(); + } + else + fvec = mPerp( node.normal ); + + if ( fvec.isZero() ) + fvec = mPerp( node.normal ); + + F32 dot = mDot( fvec, node.normal ); + if ( dot < -0.9f || dot > 0.9f ) + fvec = mPerp( node.normal ); + + VectorF rvec = mCross( fvec, node.normal ); + if ( rvec.isZero() ) + rvec = mPerp( fvec ); + rvec.normalize(); + + fvec = mCross( node.normal, rvec ); + fvec.normalize(); + + mat.setColumn( 0, rvec ); + mat.setColumn( 1, fvec ); + mat.setColumn( 2, node.normal ); + mat.setColumn( 3, node.point ); + + AssertFatal( m_matF_determinant( mat ) != 0.0f, "no inverse!"); + + return mat; +} + +void MeshRoad::calcSliceTransform( U32 idx, MatrixF &mat ) +{ + if ( mSlices.size() - 1 < idx ) + return; + + bool hasNext = idx + 1 < mSlices.size(); + bool hasPrev = (S32)idx - 1 >= 0; + + const MeshRoadSlice &slice = mSlices[idx]; + + VectorF fvec( 0, 1, 0 ); + + if ( hasNext ) + { + fvec = mSlices[idx+1].p1 - slice.p1; + fvec.normalizeSafe(); + } + else if ( hasPrev ) + { + fvec = slice.p1 - mSlices[idx-1].p1; + fvec.normalizeSafe(); + } + else + fvec = mPerp( slice.normal ); + + if ( fvec.isZero() ) + fvec = mPerp( slice.normal ); + + F32 dot = mDot( fvec, slice.normal ); + if ( dot < -0.9f || dot > 0.9f ) + fvec = mPerp( slice.normal ); + + VectorF rvec = mCross( fvec, slice.normal ); + if ( rvec.isZero() ) + rvec = mPerp( fvec ); + rvec.normalize(); + + fvec = mCross( slice.normal, rvec ); + fvec.normalize(); + + mat.setColumn( 0, rvec ); + mat.setColumn( 1, fvec ); + mat.setColumn( 2, slice.normal ); + mat.setColumn( 3, slice.p1 ); + + AssertFatal( m_matF_determinant( mat ) != 0.0f, "no inverse!"); +} + +F32 MeshRoad::getRoadLength() const +{ + F32 length = 0.0f; + + for ( U32 i = 0; i < mSegments.size(); i++ ) + { + length += mSegments[i].length(); + } + + return length; +} + +void MeshRoad::deleteNode( U32 idx ) +{ + if ( mNodes.size() - 1 < idx ) + return; + + mNodes.erase(idx); + _regenerate(); + + setMaskBits( RegenMask | NodeMask ); +} + +U32 MeshRoad::_addNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal ) +{ + mNodes.increment(); + MeshRoadNode &node = mNodes.last(); + + node.point = pos; + node.width = width; + node.depth = depth; + node.normal = normal; + + setMaskBits( NodeMask | RegenMask ); + + return mNodes.size() - 1; +} + +U32 MeshRoad::_insertNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal, const U32 &idx ) +{ + U32 ret; + MeshRoadNode *node; + + if ( idx == U32_MAX ) + { + mNodes.increment(); + node = &mNodes.last(); + ret = mNodes.size() - 1; + } + else + { + mNodes.insert( idx ); + node = &mNodes[idx]; + ret = idx; + } + + node->point = pos; + node->depth = depth; + node->width = width; + node->normal = normal; + + return ret; +} + +bool MeshRoad::collideRay( const Point3F &origin, const Point3F &direction, U32 *nodeIdx, Point3F *collisionPnt ) +{ + Point3F p0 = origin; + Point3F p1 = origin + direction * 2000.0f; + + // If the line segment does not collide with the MeshRoad's world box, + // it definitely does not collide with any part of the river. + if ( !getWorldBox().collideLine( p0, p1 ) ) + return false; + + if ( mSlices.size() < 2 ) + return false; + + MathUtils::Quad quad; + MathUtils::Ray ray; + F32 t; + + // Check each road segment (formed by a pair of slices) for collision + // with the line segment. + for ( U32 i = 0; i < mSlices.size() - 1; i++ ) + { + const MeshRoadSlice &slice0 = mSlices[i]; + const MeshRoadSlice &slice1 = mSlices[i+1]; + + // For simplicities sake we will only test for collision between the + // line segment and the Top face of the river segment. + + // Clockwise starting with the leftmost/closest point. + quad.p00 = slice0.p0; + quad.p01 = slice1.p0; + quad.p11 = slice1.p2; + quad.p10 = slice0.p2; + + ray.origin = origin; + ray.direction = direction; + + if ( MathUtils::mRayQuadCollide( quad, ray, NULL, &t ) ) + { + if ( nodeIdx ) + *nodeIdx = slice0.parentNodeIdx; + if ( collisionPnt ) + *collisionPnt = ray.origin + ray.direction * t; + return true; + } + } + + return false; +} + +void MeshRoad::regenerate() +{ + _regenerate(); + setMaskBits( RegenMask ); +} + +//------------------------------------------------------------------------- +// Console Methods +//------------------------------------------------------------------------- + +DefineEngineMethod( MeshRoad, setNodeDepth, void, ( S32 idx, F32 meters ),, + "Intended as a helper to developers and editor scripts.\n" + "Sets the depth in meters of a particular node." + ) +{ + object->setNodeDepth( idx, meters ); +} + +DefineEngineMethod( MeshRoad, regenerate, void, (),, + "Intended as a helper to developers and editor scripts.\n" + "Force MeshRoad to recreate its geometry." + ) +{ + object->regenerate(); +} + +DefineEngineMethod( MeshRoad, postApply, void, (),, + "Intended as a helper to developers and editor scripts.\n" + "Force trigger an inspectPostApply. This will transmit " + "material and other fields ( not including nodes ) to client objects." + ) +{ + object->inspectPostApply(); +} \ No newline at end of file diff --git a/Engine/source/environment/meshRoad.h b/Engine/source/environment/meshRoad.h new file mode 100644 index 000000000..ed47984b1 --- /dev/null +++ b/Engine/source/environment/meshRoad.h @@ -0,0 +1,566 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MESHROAD_H_ +#define _MESHROAD_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _GFXTEXTUREHANDLE_H_ +#include "gfx/gfxTextureHandle.h" +#endif +#ifndef _GFXVERTEXBUFFER_H_ +#include "gfx/gfxVertexBuffer.h" +#endif +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif +#ifndef _CLIPPEDPOLYLIST_H_ +#include "collision/clippedPolyList.h" +#endif +#ifndef _MATINSTANCE_H_ +#include "materials/matInstance.h" +#endif +#ifndef _CONVEX_H_ +#include "collision/convex.h" +#endif + + +//extern U32 gIdxArray[6][2][3]; + +struct MeshRoadHitSegment +{ + U32 idx; + F32 t; +}; + +class MeshRoad; + +//------------------------------------------------------------------------- +// MeshRoadConvex Class +//------------------------------------------------------------------------- + +class MeshRoadConvex : public Convex +{ + typedef Convex Parent; + friend class MeshRoad; + +protected: + + MeshRoad *pRoad; + +public: + + U32 faceId; + U32 triangleId; + U32 segmentId; + Point3F verts[4]; + PlaneF normal; + Box3F box; + +public: + + MeshRoadConvex() { mType = MeshRoadConvexType; } + + MeshRoadConvex( const MeshRoadConvex& cv ) { + mType = MeshRoadConvexType; + mObject = cv.mObject; + pRoad = cv.pRoad; + faceId = cv.faceId; + triangleId = cv.triangleId; + segmentId = cv.segmentId; + verts[0] = cv.verts[0]; + verts[1] = cv.verts[1]; + verts[2] = cv.verts[2]; + verts[3] = cv.verts[3]; + normal = cv.normal; + box = cv.box; + } + + const MatrixF& getTransform() const; + Box3F getBoundingBox() const; + Box3F getBoundingBox(const MatrixF& mat, const Point3F& scale) const; + Point3F support(const VectorF& vec) const; + void getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf); + void getPolyList(AbstractPolyList* list); +}; + + +//------------------------------------------------------------------------- +// MeshRoadSplineNode Class +//------------------------------------------------------------------------- + +class Path; +class TerrainBlock; +struct ObjectRenderInst; + +class MeshRoadSplineNode +{ +public: + MeshRoadSplineNode() {} + + F32 x; + F32 y; + F32 z; + F32 width; + F32 depth; + VectorF normal; + + MeshRoadSplineNode& operator=(const MeshRoadSplineNode&); + MeshRoadSplineNode operator+(const MeshRoadSplineNode&) const; + MeshRoadSplineNode operator-(const MeshRoadSplineNode&) const; + MeshRoadSplineNode operator*(const F32) const; + + F32 len(); + Point3F getPosition() const { return Point3F(x,y,z); }; +}; + +inline F32 MeshRoadSplineNode::len() +{ + return mSqrt(F32(x*x + y*y + z*z)); +} + +inline MeshRoadSplineNode& MeshRoadSplineNode::operator=(const MeshRoadSplineNode &_node) +{ + x = _node.x; + y = _node.y; + z = _node.z; + width = _node.width; + depth = _node.depth; + normal = _node.normal; + + return *this; +} + +inline MeshRoadSplineNode MeshRoadSplineNode::operator+(const MeshRoadSplineNode& _add) const +{ + MeshRoadSplineNode result; + result.x = x + _add.x; + result.y = y + _add.y; + result.z = z + _add.z; + result.width = width + _add.width; + result.depth = depth + _add.depth; + result.normal = normal + _add.normal; + + return result; +} + + +inline MeshRoadSplineNode MeshRoadSplineNode::operator-(const MeshRoadSplineNode& _rSub) const +{ + MeshRoadSplineNode result; + result.x = x - _rSub.x; + result.y = y - _rSub.y; + result.z = z - _rSub.z; + result.width = width - _rSub.width; + result.depth = depth - _rSub.depth; + result.normal = normal - _rSub.normal; + + return result; +} + +inline MeshRoadSplineNode operator*(const F32 mul, const MeshRoadSplineNode& multiplicand) +{ + return multiplicand * mul; +} + +inline MeshRoadSplineNode MeshRoadSplineNode::operator*(const F32 _mul) const +{ + MeshRoadSplineNode result; + result.x = x * _mul; + result.y = y * _mul; + result.z = z * _mul; + result.width = width * _mul; + result.depth = depth * _mul; + result.normal = normal * _mul; + + return result; +} + +//------------------------------------------------------------------------- +// Structures +//------------------------------------------------------------------------- + +struct MeshRoadRenderBatch +{ + U32 startSegmentIdx; + U32 endSegmentIdx; + U32 startVert; + U32 endVert; + U32 startIndex; + U32 endIndex; + U32 totalRows; + U32 indexCount; + + U32 vertCount; + U32 triangleCount; +}; + +typedef Vector MeshRoadBatchVector; + +struct MeshRoadNode +{ + // The 3D position of the node + Point3F point; + + // The width of the River at this node (meters) + F32 width; + + // The depth of the River at this node (meters) + F32 depth; + + VectorF normal; +}; + +typedef Vector MeshRoadNodeVector; + +struct MeshRoadSlice +{ + MeshRoadSlice() + { + p0.zero(); + p1.zero(); + p2.zero(); + pb0.zero(); + pb2.zero(); + + uvec.zero(); + fvec.zero(); + rvec.zero(); + + width = 0.0f; + depth = 0.0f; + normal.set(0,0,1); + texCoordV = 0.0f; + + parentNodeIdx = -1; + }; + + Point3F p0; // upper left + Point3F p1; // upper center + Point3F p2; // upper right + + Point3F pb0; // bottom left + Point3F pb2; // bottom right + + VectorF uvec; + VectorF fvec; + VectorF rvec; + + F32 width; + F32 depth; + Point3F normal; + + F32 t; + + F32 texCoordV; + + U32 parentNodeIdx; +}; +typedef Vector MeshRoadSliceVector; + + +//------------------------------------------------------------------------- +// MeshRoadSegment Class +//------------------------------------------------------------------------- + +class MeshRoadSegment +{ +public: + + MeshRoadSegment(); + MeshRoadSegment( MeshRoadSlice *rs0, MeshRoadSlice *rs1, const MatrixF &roadMat ); + + void set( MeshRoadSlice *rs0, MeshRoadSlice *rs1 ); + + F32 TexCoordStart() const { return slice0->texCoordV; } + F32 TexCoordEnd() const { return slice1->texCoordV; } + + const Point3F& getP00() const { return slice0->p0; } + const Point3F& getP01() const { return slice1->p0; } + const Point3F& getP11() const { return slice1->p2; } + const Point3F& getP10() const { return slice0->p2; } + + Point3F getSurfaceCenter() const { return ( slice0->p1 + slice1->p1 ) / 2.0f; } + Point3F getSurfaceNormal() const { return -mPlanes[4].getNormal(); } + + bool intersectBox( const Box3F &bounds ) const; + bool containsPoint( const Point3F &pnt ) const; + F32 distanceToSurface( const Point3F &pnt ) const; + F32 length() const { return ( slice1->p1 - slice0->p1 ).len(); } + + // Quick access to the segment's points + Point3F& operator[](U32); + const Point3F& operator[](U32) const; + Point3F& operator[](S32 i) { return operator[](U32(i)); } + const Point3F& operator[](S32 i ) const { return operator[](U32(i)); } + + const Box3F& getWorldBounds() const { return worldbounds; } + + MeshRoadSlice *slice0; + MeshRoadSlice *slice1; + +protected: + + PlaneF mPlanes[6]; + U32 mPlaneCount; + + U32 columns; + U32 rows; + + U32 startVert; + U32 endVert; + U32 startIndex; + U32 endIndex; + + U32 numVerts; + U32 numTriangles; + + Box3F objectbounds; + Box3F worldbounds; +}; + +typedef Vector MeshRoadSegmentVector; + + +inline Point3F& MeshRoadSegment::operator[](U32 index) +{ + AssertFatal(index < 8, "MeshRoadSegment::operator[] - out of bounds array access!"); + + MeshRoadSlice *slice = NULL; + if ( index > 3 ) + { + slice = slice1; + index -= 4; + } + else + { + slice = slice0; + } + + if ( index == 0 ) + return slice->p0; + + if ( index == 1 ) + return slice->p2; + + if ( index == 2 ) + return slice->pb0; + + else //( index == 3 ) + return slice->pb2; +} + +inline const Point3F& MeshRoadSegment::operator[](U32 index) const +{ + AssertFatal(index < 8, "MeshRoadSegment::operator[] - out of bounds array access!"); + + MeshRoadSlice *slice = NULL; + if ( index > 3 ) + { + slice = slice1; + index -= 4; + } + else + { + slice = slice0; + } + + if ( index == 0 ) + return slice->p0; + + if ( index == 1 ) + return slice->p2; + + if ( index == 2 ) + return slice->pb0; + + else// ( index == 3 ) + return slice->pb2; +} + + +//------------------------------------------------------------------------------ +// MeshRoad Class +//------------------------------------------------------------------------------ + +class PhysicsBody; +struct MeshRoadNodeList; + +class MeshRoad : public SceneObject +{ +private: + + friend class GuiMeshRoadEditorCtrl; + friend class GuiMeshRoadEditorUndoAction; + friend class MeshRoadConvex; + + typedef SceneObject Parent; + + enum + { + MeshRoadMask = Parent::NextFreeMask, + NodeMask = Parent::NextFreeMask << 1, + RegenMask = Parent::NextFreeMask << 2, + InitialUpdateMask = Parent::NextFreeMask << 3, + SelectedMask = Parent::NextFreeMask << 4, + MaterialMask = Parent::NextFreeMask << 5, + NextFreeMask = Parent::NextFreeMask << 6, + }; + +public: + + MeshRoad(); + ~MeshRoad(); + + DECLARE_CONOBJECT(MeshRoad); + + // ConObject. + static void initPersistFields(); + static void consoleInit(); + + // SimObject + bool onAdd(); + void onRemove(); + void onEditorEnable(); + void onEditorDisable(); + void inspectPostApply(); + void onStaticModified(const char* slotName, const char*newValue = NULL); + void writeFields(Stream &stream, U32 tabStop); + bool writeField( StringTableEntry fieldname, const char *value ); + + // NetObject + U32 packUpdate(NetConnection *, U32, BitStream *); + void unpackUpdate(NetConnection *, BitStream *); + + // SceneObject + virtual void prepRenderImage( SceneRenderState* sceneState ); + virtual void setTransform( const MatrixF &mat ); + virtual void setScale( const VectorF &scale ); + + // SceneObject - Collision + virtual void buildConvex(const Box3F& box,Convex* convex); + virtual bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere); + virtual bool castRay(const Point3F &start, const Point3F &end, RayInfo* info); + virtual bool collideBox(const Point3F &start, const Point3F &end, RayInfo* info); + + // MeshRoad + void regenerate(); + void setBatchSize( U32 level ); + void setTextureFile( StringTableEntry file ); + void setTextureRepeat( F32 meters ); + + bool collideRay( const Point3F &origin, const Point3F &direction, U32 *nodeIdx = NULL, Point3F *collisionPnt = NULL ); + bool buildSegmentPolyList( AbstractPolyList* polyList, U32 startSegIdx, U32 endSegIdx, bool capFront, bool capEnd ); + + void buildNodesFromList( MeshRoadNodeList* list ); + + U32 insertNode( const Point3F &pos, const F32 &width, const F32 &depth, const Point3F &normal, const U32 &idx ); + U32 addNode( const Point3F &pos, const F32 &width, const F32 &depth, const Point3F &normal ); + void deleteNode( U32 idx ); + + void setNode( const Point3F &pos, const F32 &width, const F32 &depth, const Point3F &normal, const U32 &idx ); + + const MeshRoadNode& getNode( U32 idx ); + VectorF getNodeNormal( U32 idx ); + void setNodeNormal( U32 idx, const VectorF &normal ); + Point3F getNodePosition( U32 idx ); + void setNodePosition( U32 idx, const Point3F &pos ); + F32 getNodeWidth( U32 idx ); + void setNodeWidth( U32 idx, F32 width ); + F32 getNodeDepth( U32 idx ); + void setNodeDepth( U32 idx, F32 depth ); + MatrixF getNodeTransform( U32 idx ); + void calcSliceTransform( U32 idx, MatrixF &mat ); + bool isEndNode( U32 idx ) { return ( mNodes.size() > 0 && ( idx == 0 || idx == mNodes.size() - 1 ) ); } + + U32 getSegmentCount() { return mSegments.size(); } + const MeshRoadSegment& getSegment( U32 idx ) { return mSegments[idx]; } + + F32 getRoadLength() const; + + static SimSet* getServerSet(); + + /// Protected 'Component' Field setter that will add a component to the list. + static bool addNodeFromField( void *object, const char *index, const char *data ); + + static bool smEditorOpen; + static bool smWireframe; + static bool smShowBatches; + static bool smShowSpline; + static bool smShowRoad; + static SimObjectPtr smServerMeshRoadSet; + +protected: + + void _initMaterial(); + + void _debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* ); + + U32 _insertNode( const Point3F &pos, const F32 &width, const F32 &depth, const Point3F &normal, const U32 &idx ); + U32 _addNode( const Point3F &pos, const F32 &width, const F32 &depth, const Point3F &normal ); + + void _regenerate(); + void _generateSlices(); + void _generateSegments(); + void _generateVerts(); + +protected: + + MeshRoadSliceVector mSlices; + MeshRoadNodeVector mNodes; + MeshRoadSegmentVector mSegments; + MeshRoadBatchVector mBatches; + + static GFXStateBlockRef smWireframeSB; + + enum { + Top = 0, + Bottom = 1, + Side = 2, + SurfaceCount = 3 + }; + + GFXVertexBufferHandle mVB[SurfaceCount]; + GFXPrimitiveBufferHandle mPB[SurfaceCount]; + + String mMaterialName[SurfaceCount]; + SimObjectPtr mMaterial[SurfaceCount]; + BaseMatInstance *mMatInst[SurfaceCount]; + + U32 mVertCount[SurfaceCount]; + U32 mTriangleCount[SurfaceCount]; + + // Fields. + F32 mTextureLength; + F32 mBreakAngle; + S32 mWidthSubdivisions; + + // Collision and Physics. + Convex* mConvexList; + Vector mDebugConvex; + PhysicsBody *mPhysicsRep; +}; + + +#endif // _MESHROAD_H_ \ No newline at end of file diff --git a/Engine/source/environment/nodeListManager.cpp b/Engine/source/environment/nodeListManager.cpp new file mode 100644 index 000000000..0a67d6b39 --- /dev/null +++ b/Engine/source/environment/nodeListManager.cpp @@ -0,0 +1,275 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "environment/nodeListManager.h" +#include "core/module.h" +#include "core/stream/bitStream.h" + +MODULE_BEGIN( NodeListManager ) + + MODULE_INIT + { + AssertFatal(gClientNodeListManager == NULL && gServerNodeListManager == NULL, "Error, already initialized the node list manager!"); + + gClientNodeListManager = new NodeListManager(false); + gServerNodeListManager = new NodeListManager(true); + } + + MODULE_SHUTDOWN + { + AssertFatal(gClientNodeListManager != NULL && gServerNodeListManager != NULL, "Error, node list manager not initialized!"); + + delete gClientNodeListManager; + gClientNodeListManager = NULL; + delete gServerNodeListManager; + gServerNodeListManager = NULL; + } + +MODULE_END; + +//----------------------------------------------------------------------------- +// NodeListEvent Class +//----------------------------------------------------------------------------- + +NodeListEvent::~NodeListEvent() +{ + if (mNodeList) + { + // This means the node list wasn't processed + delete mNodeList; + } +} + +void NodeListEvent::pack(NetConnection* conn, BitStream* stream) +{ + stream->write(mId); + stream->write(mTotalNodes); + stream->write(mLocalListStart); + + // NOTE: Child class needs to transmit the nodes +} + +void NodeListEvent::write(NetConnection* conn, BitStream *stream) +{ + pack(conn, stream); +} + +void NodeListEvent::unpack(NetConnection* conn, BitStream* stream) +{ + stream->read(&mId); + stream->read(&mTotalNodes); + stream->read(&mLocalListStart); + + mNodeList->mId = mId; + + // NOTE: Child class needs to populate the local node list +} + +void NodeListEvent::process(NetConnection* conn) +{ + if (mNodeList) + { + NodeListManager::NodeList* oldList = NULL; + + gClientNodeListManager->findListById(mNodeList->mId, &oldList, false); + mergeLists(oldList); + + gClientNodeListManager->addNodeList(mNodeList); + mNodeList = NULL; + } +} + +void NodeListEvent::mergeLists(NodeListManager::NodeList* oldList) +{ + if (oldList) + { + if ( !mNodeList->mListComplete) + { + copyIntoList( oldList ); + + // Is the node list now complete? + oldList->mTotalValidNodes += mNodeList->mTotalValidNodes; + if (oldList->mTotalValidNodes >= mTotalNodes) + { + oldList->mListComplete = true; + } + + delete mNodeList; + mNodeList = oldList; + } + } + else + { + padListToSize(); + } +} + +IMPLEMENT_CO_NETEVENT_V1(NodeListEvent); + +ConsoleDocClass( NodeListEvent, + "@brief Base class for events used by node editors, like River\n\n" + "Editor use only.\n\n" + "@internal" +); +//----------------------------------------------------------------------------- +// NodeListManager Class +//----------------------------------------------------------------------------- + +NodeListManager* gClientNodeListManager = NULL; +NodeListManager* gServerNodeListManager = NULL; + +U32 NodeListManager::smMaximumNodesPerEvent = 20; + +//----------------------------------------------------------------------------- + +NodeListManager::NodeListManager(const bool isServer) +{ + mIsServer = isServer; + mNextListId = 0; +} + +NodeListManager::~NodeListManager() +{ + clearAllNotifications(); + clearNodeLists(); +} + +void NodeListManager::clearNodeLists() +{ + for ( U32 i=0; imListComplete) + { + if (dispatchNotification( list )) + { + delete list; + return; + } + } + + // Nothing is registered or the list is not complete, so store the list for later + mNodeLists.push_back( list ); +} + +bool NodeListManager::findListById(U32 id, NodeList** list, bool completeOnly) +{ + *list = NULL; + + for (U32 i=0; imId == id) + { + if (completeOnly && !mNodeLists[i]->mListComplete) + { + // Found the list, but it is not complete. + return false; + } + + // Return the node list (complete or not) and remove + // it from our list of lists + *list = mNodeLists[i]; + mNodeLists.erase(i); + return true; + } + } + + return false; +} + +void NodeListManager::clearNotification( U32 listId ) +{ + for (U32 i=0; igetListId() == listId) + { + delete mNotifyList[i]; + mNotifyList.erase(i); + return; + } + } +} + +void NodeListManager::clearAllNotifications() +{ + for (U32 i=0; imId == listId) + { + list = mNodeLists[i]; + break; + } + } + + if (list) + return dispatchNotification( list ); + + return false; +} + +bool NodeListManager::dispatchNotification( NodeList* list ) +{ + for (U32 i=0; igetListId() == list->mId) + { + mNotifyList[i]->sendNotification( list ); + delete mNotifyList[i]; + mNotifyList.erase(i); + + return true; + } + } + + return false; +} diff --git a/Engine/source/environment/nodeListManager.h b/Engine/source/environment/nodeListManager.h new file mode 100644 index 000000000..c4f42d3f2 --- /dev/null +++ b/Engine/source/environment/nodeListManager.h @@ -0,0 +1,133 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _NODELISTMANAGER_H_ +#define _NODELISTMANAGER_H_ + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _MQUAT_H_ +#include "math/mQuat.h" +#endif +#ifndef _NETCONNECTION_H_ +#include "sim/netConnection.h" +#endif + +//----------------------------------------------------------------------------- + +class NodeListNotify; + +class NodeListManager +{ +public: + + struct NodeList + { + U32 mId; + U32 mTotalValidNodes; + bool mListComplete; + + NodeList() { mTotalValidNodes=0; mListComplete=false; } + virtual ~NodeList() { } + }; + + static U32 smMaximumNodesPerEvent; + +protected: + bool mIsServer; + U32 mNextListId; + + Vector mNodeLists; + Vector mNotifyList; + +public: + NodeListManager( const bool isServer ); + ~NodeListManager(); + + void clearNodeLists(); + + U32 nextListId(); + + void addNodeList( NodeList* list ); + + bool findListById( U32 id, NodeList** list, bool completeOnly=true ); + + void clearNotification( U32 listId ); + void clearAllNotifications(); + void registerNotification( NodeListNotify* notify ); + bool dispatchNotification( U32 listId ); + bool dispatchNotification( NodeList* list ); +}; + +extern NodeListManager* gClientNodeListManager; +extern NodeListManager* gServerNodeListManager; + +//----------------------------------------------------------------------------- + +class NodeListNotify +{ +protected: + U32 mListId; + +public: + NodeListNotify() { } + virtual ~NodeListNotify() { } + + U32 getListId() { return mListId; } + + virtual void sendNotification( NodeListManager::NodeList* list ) { } +}; + +//----------------------------------------------------------------------------- + +class NodeListEvent : public NetEvent +{ + typedef NetEvent Parent; + +public: + U32 mId; + U32 mTotalNodes; + U32 mLocalListStart; + + NodeListManager::NodeList* mNodeList; + +public: + NodeListEvent() { mNodeList=NULL; mTotalNodes = mLocalListStart = 0; } + virtual ~NodeListEvent(); + + virtual void pack(NetConnection*, BitStream*); + virtual void write(NetConnection*, BitStream*); + virtual void unpack(NetConnection*, BitStream*); + virtual void process(NetConnection*); + virtual void mergeLists(NodeListManager::NodeList* oldList); + + virtual void copyIntoList(NodeListManager::NodeList* copyInto) { } + virtual void padListToSize() { } + + DECLARE_CONOBJECT(NodeListEvent); +}; + +#endif // _NODELISTMANAGER_H_ diff --git a/Engine/source/environment/river.cpp b/Engine/source/environment/river.cpp new file mode 100644 index 000000000..59a16230f --- /dev/null +++ b/Engine/source/environment/river.cpp @@ -0,0 +1,2492 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "environment/river.h" + +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "util/catmullRom.h" +#include "math/util/quadTransforms.h" +#include "scene/simPath.h" +#include "scene/sceneRenderState.h" +#include "scene/sceneManager.h" +#include "materials/sceneData.h" +#include "materials/baseMatInstance.h" +#include "scene/sgUtil.h" +#include "T3D/gameBase/gameConnection.h" +#include "core/stream/bitStream.h" +#include "gfx/gfxDrawUtil.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxDebugEvent.h" +#include "gfx/gfxOcclusionQuery.h" +#include "math/mathIO.h" +#include "math/mathUtils.h" +#include "math/util/frustum.h" +#include "math/util/quadTransforms.h" +#include "gui/3d/guiTSControl.h" +#include "gfx/sim/debugDraw.h" +#include "T3D/fx/particleEmitter.h" +#include "scene/reflectionManager.h" +#include "ts/tsShapeInstance.h" +#include "postFx/postEffect.h" +#include "math/util/matrixSet.h" +#include "environment/nodeListManager.h" + +ConsoleDocClass( River, + "@brief A water volume defined by a 3D spline.\n\n" + + "User may control width and depth per node and overall spline shape in three " + "dimensions.\n\n" + + "%River supports dynamic planar reflections (fullReflect) like all WaterObject " + "classes, but keep in mind it is not necessarily a planar surface. For best " + "visual quality a %River should be less reflective the more it twists and " + "bends. This caution only applies to %Rivers with fullReflect on.\n\n" + + "@see WaterObject for inherited functionality.\n\n" + + "@ingroup Water" +); + +#define MIN_METERS_PER_SEGMENT 1.0f +#define MIN_NODE_DEPTH 0.25f +#define MAX_NODE_DEPTH 500.0f +#define MIN_NODE_WIDTH 0.25f +#define MAX_NODE_WIDTH 1000.0f +#define NODE_RADIUS 15.0f + +static U32 gIdxArray[6][2][3] = { + { { 0, 4, 5 }, { 0, 5, 1 }, }, // Top Face + { { 2, 6, 4 }, { 2, 4, 0 }, }, // Left Face + { { 1, 5, 7 }, { 1, 7, 3 }, }, // Right Face + { { 2, 3, 7 }, { 2, 7, 6 }, }, // Bottom Face + { { 0, 1, 3 }, { 0, 3, 2 }, }, // Front Face + { { 4, 6, 7 }, { 4, 7, 5 }, }, // Back Face +}; + +struct RiverHitSegment +{ + U32 idx; + F32 t; +}; + +static S32 QSORT_CALLBACK compareHitSegments(const void* a,const void* b) +{ + const RiverHitSegment *fa = (RiverHitSegment*)a; + const RiverHitSegment *fb = (RiverHitSegment*)b; + + return mSign(fb->t - fa->t); +} + +static Point3F sSegmentPointComparePoints[4]; + +//----------------------------------------------------------------------------- +// DecalRoadNodeList Struct +//----------------------------------------------------------------------------- + +struct RiverNodeList : public NodeListManager::NodeList +{ + Vector mPositions; + Vector mWidths; + Vector mDepths; + Vector mNormals; + + RiverNodeList() { } + virtual ~RiverNodeList() { } +}; + +//----------------------------------------------------------------------------- +// RiverNodeEvent Class +//----------------------------------------------------------------------------- + +class RiverNodeEvent : public NodeListEvent +{ + typedef NodeListEvent Parent; + +public: + Vector mPositions; + Vector mWidths; + Vector mDepths; + Vector mNormals; + +public: + RiverNodeEvent() { mNodeList = NULL; } + virtual ~RiverNodeEvent() { } + + virtual void pack(NetConnection*, BitStream*); + virtual void unpack(NetConnection*, BitStream*); + + virtual void copyIntoList(NodeListManager::NodeList* copyInto); + virtual void padListToSize(); + + DECLARE_CONOBJECT(RiverNodeEvent); +}; + +void RiverNodeEvent::pack(NetConnection* conn, BitStream* stream) +{ + Parent::pack( conn, stream ); + + stream->writeInt( mPositions.size(), 16 ); + + for (U32 i=0; iwrite( mWidths[i] ); + stream->write( mDepths[i] ); + mathWrite( *stream, mNormals[i] ); + } +} + +void RiverNodeEvent::unpack(NetConnection* conn, BitStream* stream) +{ + mNodeList = new RiverNodeList(); + + Parent::unpack( conn, stream ); + + U32 count = stream->readInt( 16 ); + + Point3F pos; + F32 width, depth; + VectorF normal; + + RiverNodeList* list = static_cast(mNodeList); + + for (U32 i=0; iread( &width ); + stream->read( &depth ); + mathRead( *stream, &normal ); + + list->mPositions.push_back( pos ); + list->mWidths.push_back( width ); + list->mDepths.push_back( depth ); + list->mNormals.push_back( normal ); + } + + list->mTotalValidNodes = count; + + // Do we have a complete list? + if (list->mPositions.size() >= mTotalNodes) + list->mListComplete = true; +} + +void RiverNodeEvent::copyIntoList(NodeListManager::NodeList* copyInto) +{ + RiverNodeList* prevList = dynamic_cast(copyInto); + RiverNodeList* list = static_cast(mNodeList); + + // Merge our list with the old list. + for (U32 i=mLocalListStart, index=0; imPositions.size(); ++i, ++index) + { + prevList->mPositions[i] = list->mPositions[index]; + prevList->mWidths[i] = list->mWidths[index]; + prevList->mDepths[i] = list->mDepths[index]; + prevList->mNormals[i] = list->mNormals[index]; + } +} + +void RiverNodeEvent::padListToSize() +{ + RiverNodeList* list = static_cast(mNodeList); + + U32 totalValidNodes = list->mTotalValidNodes; + + // Pad our list front? + if (mLocalListStart) + { + RiverNodeList* newlist = new RiverNodeList(); + newlist->mPositions.increment(mLocalListStart); + newlist->mWidths.increment(mLocalListStart); + newlist->mDepths.increment(mLocalListStart); + newlist->mNormals.increment(mLocalListStart); + + newlist->mPositions.merge(list->mPositions); + newlist->mWidths.merge(list->mWidths); + newlist->mDepths.merge(list->mDepths); + newlist->mNormals.merge(list->mNormals); + + mNodeList = newlist; + delete list; + } + + // Pad our list end? + if (list->mPositions.size() < mTotalNodes) + { + U32 delta = mTotalNodes - list->mPositions.size(); + list->mPositions.increment(delta); + list->mWidths.increment(delta); + list->mDepths.increment(delta); + list->mNormals.increment(delta); + } + + list->mTotalValidNodes = totalValidNodes; +} + +IMPLEMENT_CO_NETEVENT_V1(RiverNodeEvent); + +ConsoleDocClass( RiverNodeEvent, + "@brief Sends messages to the River Editor\n\n" + "Editor use only.\n\n" + "@internal" +); +//----------------------------------------------------------------------------- +// RiverNodeListNotify Class +//----------------------------------------------------------------------------- + +class RiverNodeListNotify : public NodeListNotify +{ + typedef NodeListNotify Parent; + +protected: + SimObjectPtr mRiver; + +public: + RiverNodeListNotify( River* river, U32 listId ) { mRiver = river; mListId = listId; } + virtual ~RiverNodeListNotify() { mRiver = NULL; } + + virtual void sendNotification( NodeListManager::NodeList* list ); +}; + +void RiverNodeListNotify::sendNotification( NodeListManager::NodeList* list ) +{ + if (mRiver.isValid()) + { + // Build the road's nodes + RiverNodeList* riverList = dynamic_cast( list ); + if (riverList) + mRiver->buildNodesFromList( riverList ); + } +} + +//------------------------------------------------------------------------------ +// Class: RiverSegment +//------------------------------------------------------------------------------ + +RiverSegment::RiverSegment() +{ + mPlaneCount = 0; + columns = 0; + rows = 0; + numVerts = 0; + numTriangles = 0; + + startVert = 0; + endVert = 0; + startIndex = 0; + endIndex = 0; + + slice0 = NULL; + slice1 = NULL; +} + +RiverSegment::RiverSegment( RiverSlice *rs0, RiverSlice *rs1 ) +{ + columns = 0; + rows = 0; + numVerts = 0; + numTriangles = 0; + + startVert = 0; + endVert = 0; + startIndex = 0; + endIndex = 0; + + slice0 = rs0; + slice1 = rs1; + + // Calculate the planes for this segment + // Will be used for intersection/buoyancy tests + VectorF normal; + mPlaneCount = 6; + + sSegmentPointCompareReference = getFaceCenter(6); + + // left + mPlanes[0] = _getBestPlane( &slice1->p0, &slice1->pb0, &slice0->pb0, &slice0->p0 ); + + // right + mPlanes[1] = _getBestPlane( &slice0->pb2, &slice1->pb2, &slice1->p2, &slice0->p2 ); + + // near + mPlanes[2] = _getBestPlane( &slice0->pb0, &slice0->pb2, &slice0->p2, &slice0->p0 ); + + // far + mPlanes[3] = _getBestPlane( &slice1->pb2, &slice1->pb0, &slice1->p0, &slice1->p2 ); + + // top + mPlanes[4] = _getBestPlane( &slice0->p2, &slice1->p2, &slice1->p0, &slice0->p0 ); + + // bottom + mPlanes[5] = _getBestPlane( &slice0->pb2, &slice0->pb0, &slice1->pb0, &slice1->pb2 ); + + // Calculate the bounding box(s) + worldbounds.minExtents = worldbounds.maxExtents = rs0->p0; + worldbounds.extend( rs0->p2 ); + worldbounds.extend( rs0->pb0 ); + worldbounds.extend( rs0->pb2 ); + worldbounds.extend( rs1->p0 ); + worldbounds.extend( rs1->p2 ); + worldbounds.extend( rs1->pb0 ); + worldbounds.extend( rs1->pb2 ); + + /* + // Calculate tetrahedrons (for collision and buoyancy testing) + // This is 0 in the diagram. + mCubePoints[0] = cornerPoint; + mCubePoints[1] = cornerPoint + (VectorF( 1.0f, 0.0f, 0.0f ) * size ); + mCubePoints[2] = cornerPoint + (VectorF( 0.0f, 1.0f, 0.0f ) * size ); + mCubePoints[3] = cornerPoint + (VectorF( 1.0f, 1.0f, 0.0f ) * size ); + + mCubePoints[4] = cornerPoint + (VectorF( 0.0f, 0.0f, 1.0f ); + mCubePoints[5] = cornerPoint + (VectorF( 1.0f, 0.0f, 1.0f ); + mCubePoints[6] = cornerPoint + (VectorF( 0.0f, 1.0f, 1.0f ); + mCubePoints[7] = cornerPoint + (VectorF( 1.0f, 1.0f, 1.0f ); + + // Center tetra. + mTetras[0].p0 = &mCubePoints[1]; + mTetras[0].p1 = &mCubePoints[2]; + mTetras[0].p2 = &mCubePoints[4]; + mTetras[0].p3 = &mCubePoints[7]; + + + + mTetras[1].p0 = &mCubePoints[0]; // this is the tip + mTetras[1].p1 = &mCubePoints[1]; + mTetras[1].p2 = &mCubePoints[2]; + mTetras[1].p3 = &mCubePoints[4]; + + mTetras[2].p0 = &mCubePoints[3]; // tip + mTetras[2].p1 = &mCubePoints[2]; + mTetras[2].p2 = &mCubePoints[1]; + mTetras[2].p3 = &mCubePoints[7]; + + mTetras[3].p0 = &mCubePoints[6]; // tip + mTetras[3].p1 = &mCubePoints[7]; + mTetras[3].p2 = &mCubePoints[4]; + mTetras[3].p3 = &mCubePoints[2]; + + mTetras[4].p0 = &mCubePoints[5]; // tip + mTetras[4].p1 = &mCubePoints[7]; + mTetras[4].p2 = &mCubePoints[4]; + mTetras[4].p3 = &mCubePoints[3];*/ + +} + +void RiverSegment::set( RiverSlice *rs0, RiverSlice *rs1 ) +{ + columns = 0; + rows = 0; + numVerts = 0; + numTriangles = 0; + + startVert = 0; + endVert = 0; + startIndex = 0; + endIndex = 0; + + slice0 = rs0; + slice1 = rs1; +} + +static S32 QSORT_CALLBACK SegmentPointCompare(const void *aptr, const void *bptr) +{ + const U32 a = *(const U32*)aptr; + const U32 b = *(const U32*)bptr; + + F32 lenA = ( sSegmentPointCompareReference - sSegmentPointComparePoints[a] ).lenSquared(); + F32 lenB = ( sSegmentPointCompareReference - sSegmentPointComparePoints[b] ).lenSquared(); + return ( lenB - lenA ); +} + +PlaneF RiverSegment::_getBestPlane( const Point3F *p0, const Point3F *p1, const Point3F *p2, const Point3F *p3 ) +{ + sSegmentPointComparePoints[0] = *p0; + sSegmentPointComparePoints[1] = *p1; + sSegmentPointComparePoints[2] = *p2; + sSegmentPointComparePoints[3] = *p3; + + Point3F points[4] = { + *p0, *p1, *p2, *p3 + }; + + U32 indices[4] = { + 0,1,2,3 + }; + + dQsort(indices, 4, sizeof(U32), SegmentPointCompare); + + // Collect the best three points (in correct winding order) + // To generate the plane's normal + Vector normalPnts; + + for ( U32 i = 0; i < 4; i++ ) + { + if ( i == indices[3] ) + continue; + + normalPnts.push_back(points[i]); + } + + PlaneF plane( normalPnts[0], normalPnts[1], normalPnts[2] ); + return plane; +} + +Point3F RiverSegment::getFaceCenter( U32 faceIdx ) const +{ + Point3F center(0,0,0); + + switch ( faceIdx ) + { + case 0: // left + center = slice1->p0 + slice0->p0 + slice0->pb0 + slice1->pb0; + center *= 0.25f; + break; + + case 1: // right + center = slice0->p2 + slice1->p2 + slice1->pb2 + slice0->pb2; + center *= 0.25f; + break; + + case 2: // near + center = slice0->p0 + slice0->p2 + slice0->pb2 + slice0->pb0; + center *= 0.25f; + break; + + case 3: // far + center = slice1->pb0 + slice1->p0 + slice1->pb0 + slice1->pb2; + center *= 0.25f; + break; + + case 4: // top + center = slice0->p0 + slice1->p0 + slice1->p2 + slice0->p2; + center *= 0.25f; + break; + + case 5: // bottom + center = slice1->pb2 + slice1->pb0 + slice0->pb0 + slice0->pb2; + center *= 0.25f; + break; + + case 6: // segment center + center = slice0->p0 + slice0->p2 + slice1->p0 + slice1->p2 + slice0->pb0 + slice0->pb2 + slice1->pb0 + slice1->pb2; + center /= 8; + break; + } + + return center; +} + +bool RiverSegment::intersectBox( const Box3F &bounds ) const +{ + // This code copied from Frustum class. + + Point3F maxPoint; + F32 maxDot; + + // Note the planes are ordered left, right, near, + // far, top, bottom for getting early rejections + // from the typical horizontal scene. + for ( S32 i = 0; i < mPlaneCount; i++ ) + { + // This is pretty much as optimal as you can + // get for a plane vs AABB test... + // + // 4 comparisons + // 3 multiplies + // 2 adds + // 1 negation + // + // It will early out as soon as it detects the + // bounds is outside one of the planes. + + if ( mPlanes[i].x > 0 ) + maxPoint.x = bounds.maxExtents.x; + else + maxPoint.x = bounds.minExtents.x; + + if ( mPlanes[i].y > 0 ) + maxPoint.y = bounds.maxExtents.y; + else + maxPoint.y = bounds.minExtents.y; + + if ( mPlanes[i].z > 0 ) + maxPoint.z = bounds.maxExtents.z; + else + maxPoint.z = bounds.minExtents.z; + + maxDot = mDot( maxPoint, mPlanes[ i ] ); + + if ( maxDot <= -mPlanes[ i ].d ) + return false; + } + + return true; +} + +bool RiverSegment::containsPoint( const Point3F &pnt ) const +{ + // NOTE: this code from Frustum class. + + F32 maxDot; + + // Note the planes are ordered left, right, near, + // far, top, bottom for getting early rejections + // from the typical horizontal scene. + for ( S32 i = 0; i < mPlaneCount; i++ ) + { + const PlaneF &plane = mPlanes[ i ]; + + // This is pretty much as optimal as you can + // get for a plane vs point test... + // + // 1 comparison + // 2 multiplies + // 1 adds + // + // It will early out as soon as it detects the + // point is outside one of the planes. + + maxDot = mDot( pnt, plane ) + plane.d; + if ( maxDot < -0.1f ) + return false; + } + + return true; +} + +F32 RiverSegment::distanceToSurface(const Point3F &pnt) const +{ + return mPlanes[4].distToPlane( pnt ); +} + +bool River::smEditorOpen = false; +bool River::smWireframe = false; +bool River::smShowWalls = false; +bool River::smShowNodes = false; +bool River::smShowSpline = true; +bool River::smShowRiver = true; +SimObjectPtr River::smServerRiverSet = NULL; + +IMPLEMENT_CO_NETOBJECT_V1(River); + + +River::River() + : mMetersPerSegment(10.0f), + mSegmentsPerBatch(10), + mDepthScale(1.0f), + mMaxDivisionSize(2.5f), + mMinDivisionSize(0.25f), + mColumnCount(5), + mFlowMagnitude(1.0f), + mLodDistance( 50.0f ) +{ + mNetFlags.set( Ghostable | ScopeAlways ); + + mObjScale.set( 1, 1, 1 ); + + mObjBox.minExtents.set( -0.5, -0.5, -0.5 ); + mObjBox.maxExtents.set( 0.5, 0.5, 0.5 ); + + mReflectNormalUp = false; + + // We use the shader const miscParams.w to signify + // that this object is a River. + mMiscParamW = 1.0f; +} + +River::~River() +{ +} + +void River::initPersistFields() +{ + addGroup( "River" ); + + addField( "SegmentLength", TypeF32, Offset( mMetersPerSegment, River ), + "Divide the River lengthwise into segments of this length in meters. " + "These geometric volumes are used for spacial queries like determining containment." ); + + addField( "SubdivideLength", TypeF32, Offset( mMaxDivisionSize, River ), + "For purposes of generating the renderable geometry River segments are further subdivided " + "such that no quad is of greater width or length than this distance in meters." ); + + addField( "FlowMagnitude", TypeF32, Offset( mFlowMagnitude, River ), + "Magnitude of the force vector applied to dynamic objects within the River." ); + + addField( "LowLODDistance", TypeF32, Offset( mLodDistance, River ), + "Segments of the river at this distance in meters or greater will " + "render as a single unsubdivided without undulation effects." ); + + endGroup( "River" ); + + addGroup( "Internal" ); + + addProtectedField( "Node", TypeString, NULL, &addNodeFromField, &emptyStringProtectedGetFn, "For internal use, do not modify." ); + + endGroup( "Internal" ); + + Parent::initPersistFields(); +} + +void River::consoleInit() +{ + Parent::consoleInit(); + + Con::addVariable( "$River::EditorOpen", TypeBool, &River::smEditorOpen, "For editor use.\n" + "@ingroup Editors\n" ); + Con::addVariable( "$River::showWalls", TypeBool, &River::smShowWalls, "For editor use.\n" + "@ingroup Editors\n" ); + Con::addVariable( "$River::showNodes", TypeBool, &River::smShowNodes, "For editor use.\n" + "@ingroup Editors\n"); + Con::addVariable( "$River::showSpline", TypeBool, &River::smShowSpline, "For editor use.\n" + "@ingroup Editors\n" ); + Con::addVariable( "$River::showRiver", TypeBool, &River::smShowRiver, "For editor use.\n" + "@ingroup Editors\n" ); + Con::addVariable( "$River::showWireframe", TypeBool, &River::smWireframe, "For editor use.\n" + "@ingroup Editors\n"); +} + +bool River::addNodeFromField( void *object, const char *index, const char *data ) +{ + River *pObj = static_cast(object); + + //if ( !pObj->isProperlyAdded() ) + //{ + F32 x,y,z,width,depth; + VectorF normal; + U32 result = dSscanf( data, "%f %f %f %f %f %f %f %f", &x, &y, &z, &width, &depth, &normal.x, &normal.y, &normal.z ); + if ( result == 8 ) + pObj->_addNode( Point3F(x,y,z), width, depth, normal ); + //} + + return false; +} + +bool River::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + // Reset the World Box. + //setGlobalBounds(); + resetWorldBox(); + + // Set the Render Transform. + setRenderTransform(mObjToWorld); + + // Add to Scene. + addToScene(); + + if ( isServerObject() ) + getServerSet()->addObject( this ); + + _regenerate(); + + return true; +} + +void River::onRemove() +{ + removeFromScene(); + + Parent::onRemove(); +} + +void River::inspectPostApply() +{ + // Set Parent. + Parent::inspectPostApply(); + + if ( mMetersPerSegment < MIN_METERS_PER_SEGMENT ) + mMetersPerSegment = MIN_METERS_PER_SEGMENT; + + mMaxDivisionSize = getMax( mMaxDivisionSize, mMinDivisionSize ); + + // Set fxPortal Mask. + setMaskBits(RiverMask|RegenMask); +} + +void River::onStaticModified( const char* slotName, const char*newValue ) +{ + Parent::onStaticModified( slotName, newValue ); + + if ( dStricmp( slotName, "surfMaterial" ) == 0 ) + setMaskBits( MaterialMask ); +} + +SimSet* River::getServerSet() +{ + if ( !smServerRiverSet ) + { + smServerRiverSet = new SimSet(); + smServerRiverSet->registerObject( "ServerRiverSet" ); + Sim::getRootGroup()->addObject( smServerRiverSet ); + } + + return smServerRiverSet; +} + +void River::writeFields( Stream &stream, U32 tabStop ) +{ + Parent::writeFields( stream, tabStop ); + + // Now write all nodes + + stream.write(2, "\r\n"); + + for ( U32 i = 0; i < mNodes.size(); i++ ) + { + const RiverNode &node = mNodes[i]; + + stream.writeTabs(tabStop); + + char buffer[1024]; + dMemset( buffer, 0, 1024 ); + dSprintf( buffer, 1024, "Node = \"%f %f %f %f %f %f %f %f\";", node.point.x, node.point.y, node.point.z, + node.width, + node.depth, + node.normal.x, node.normal.y, node.normal.z ); + stream.writeLine( (const U8*)buffer ); + } +} + +bool River::writeField( StringTableEntry fieldname, const char *value ) +{ + if ( fieldname == StringTable->insert("node") ) + return false; + + return Parent::writeField( fieldname, value ); +} + +void River::innerRender( SceneRenderState *state ) +{ + GFXDEBUGEVENT_SCOPE( River_innerRender, ColorI( 255, 0, 0 ) ); + + PROFILE_SCOPE( River_innerRender ); + + // Setup SceneData + SceneData sgData; + sgData.init( state ); + sgData.lights[0] = LIGHTMGR->getSpecialLight( LightManager::slSunLightType ); + sgData.backBuffTex = REFLECTMGR->getRefractTex(); + sgData.reflectTex = mPlaneReflector.reflectTex; + sgData.wireframe |= smWireframe; + + const Point3F &camPosition = state->getCameraPosition(); + + // set the material + + S32 matIdx = getMaterialIndex( camPosition ); + + if ( !initMaterial( matIdx ) ) + return; + + BaseMatInstance *mat = mMatInstances[matIdx]; + WaterMatParams matParams = mMatParamHandles[matIdx]; + + if ( !mat ) + return; + + // setup proj/world transform + GFXTransformSaver saver; + + setShaderParams( state, mat, matParams ); + + _makeRenderBatches( camPosition ); + + if ( !River::smShowRiver ) + return; + + // If no material... we're done. + if ( mLowLODBatches.empty() && mHighLODBatches.empty() ) + return; + + if ( !mHighLODBatches.empty() ) + _makeHighLODBuffers(); + + mMatrixSet->restoreSceneViewProjection(); + mMatrixSet->setWorld( MatrixF::Identity ); + + while( mat->setupPass( state, sgData ) ) + { + mat->setSceneInfo(state, sgData); + mat->setTransforms(*mMatrixSet, state); + + setCustomTextures( matIdx, mat->getCurPass(), matParams ); + + GFX->setVertexBuffer( mVB_low ); + GFX->setPrimitiveBuffer( mPB_low ); + + for ( U32 i = 0; i < mLowLODBatches.size(); i++ ) + { + const RiverRenderBatch &batch = mLowLODBatches[i]; + + U32 startVert = batch.startSegmentIdx * 2; + U32 endVert = ( batch.endSegmentIdx + 1 ) * 2 + 1; + U32 startIdx = batch.startSegmentIdx * 6; + U32 endIdx = batch.endSegmentIdx * 6 + 5; + + U32 vertCount = ( endVert - startVert ) + 1; + U32 idxCount = ( endIdx - startIdx ) + 1; + U32 triangleCount = idxCount / 3; + + AssertFatal( startVert < mLowVertCount, "River, bad draw call!" ); + AssertFatal( startVert + vertCount <= mLowVertCount, "River, bad draw call!" ); + AssertFatal( triangleCount <= mLowTriangleCount, "River, bad draw call!" ); + + GFX->drawIndexedPrimitive( GFXTriangleList, 0, startVert, vertCount, startIdx, triangleCount ); + } + + // Render all high detail batches. + // + // It is possible that the buffers could not be allocated because + // the max number of verts/indices was exceeded. We don't want to + // crash because that would be unhelpful for working in the editor. + if ( mVB_high.isValid() && mPB_high.isValid() ) + { + GFX->setVertexBuffer( mVB_high ); + GFX->setPrimitiveBuffer( mPB_high ); + + for ( U32 i = 0; i < mHighLODBatches.size(); i++ ) + { + const RiverRenderBatch &batch = mHighLODBatches[i]; + + AssertFatal( batch.startVert < mHighVertCount, "River, bad draw call!" ); + AssertFatal( batch.startVert + batch.vertCount <= mHighVertCount, "River, bad draw call!" ); + AssertFatal( batch.triangleCount <= mHighTriangleCount, "River, bad draw call!" ); + AssertFatal( batch.startIndex < mHighTriangleCount * 3, "River, bad draw call!" ); + AssertFatal( batch.startIndex + batch.triangleCount * 3 <= mHighTriangleCount * 3, "River, bad draw call!" ); + + GFX->drawIndexedPrimitive( GFXTriangleList, + 0, + 0, + batch.vertCount, + batch.startIndex, + batch.triangleCount ); + } + } + + } // while( mat->setupPass( sgData ) ) +} + +void River::updateUnderwaterEffect( SceneRenderState *state ) +{ + // Calculate mWaterPlane before calling updateUnderwaterEffect. + Point3F dummy; + _getWaterPlane( state->getCameraPosition(), mWaterFogData.plane, dummy ); + + Parent::updateUnderwaterEffect( state ); +} + +void River::setShaderParams( SceneRenderState *state, BaseMatInstance* mat, const WaterMatParams& paramHandles ) +{ + // Set variables that will be assigned to shader consts within WaterCommon + // before calling Parent::setShaderParams + + mUndulateMaxDist = mLodDistance; + + Parent::setShaderParams( state, mat, paramHandles ); + + // Now set the rest of the shader consts that are either unique to this + // class or that WaterObject leaves to us to handle... + + MaterialParameters* matParams = mat->getMaterialParameters(); + + // set vertex shader constants + //----------------------------------- + + matParams->setSafe(paramHandles.mGridElementSizeSC, 1.0f); + if ( paramHandles.mModelMatSC->isValid() ) + matParams->set(paramHandles.mModelMatSC, MatrixF::Identity, GFXSCT_Float4x4); + + // set pixel shader constants + //----------------------------------- + + ColorF c( mWaterFogData.color ); + matParams->setSafe(paramHandles.mBaseColorSC, c); + + // By default we need to show a true reflection is fullReflect is enabled and + // we are above water. + F32 reflect = mPlaneReflector.isEnabled() && !isUnderwater( state->getCameraPosition() ); + + // If we were occluded the last frame a query was fetched ( not necessarily last frame ) + // and we weren't updated last frame... we don't have a valid texture to show + // so use the cubemap / fake reflection color this frame. + if ( mPlaneReflector.lastUpdateMs != REFLECTMGR->getLastUpdateMs() && mPlaneReflector.isOccluded() ) + reflect = false; + + Point4F reflectParams( mWaterPos.z, 0.0f, 1000.0f, !reflect ); + matParams->setSafe(paramHandles.mReflectParamsSC, reflectParams ); + + matParams->setSafe(paramHandles.mReflectNormalSC, mPlaneReflector.refplane ); +} + +bool River::isUnderwater( const Point3F &pnt ) const +{ + return containsPoint( pnt, NULL ); +} + +U32 River::packUpdate(NetConnection * con, U32 mask, BitStream * stream) +{ + // Pack Parent. + U32 retMask = Parent::packUpdate(con, mask, stream); + + if ( stream->writeFlag( mask & RiverMask ) ) + { + // Write Object Transform. + stream->writeAffineTransform(mObjToWorld); + + stream->write( mMetersPerSegment ); + stream->write( mSegmentsPerBatch ); + stream->write( mDepthScale ); + stream->write( mMaxDivisionSize ); + stream->write( mColumnCount ); + + stream->write( mFlowMagnitude ); + stream->write( mLodDistance ); + } + + if ( stream->writeFlag( mask & NodeMask ) ) + { + const U32 nodeByteSize = 32; // Based on sending all of a node's parameters + + // Test if we can fit all of our nodes within the current stream. + // We make sure we leave 100 bytes still free in the stream for whatever + // may follow us. + S32 allowedBytes = stream->getWriteByteSize() - 100; + if ( stream->writeFlag( (nodeByteSize * mNodes.size()) < allowedBytes ) ) + { + // All nodes should fit, so send them out now. + stream->writeInt( mNodes.size(), 16 ); + + for ( U32 i = 0; i < mNodes.size(); i++ ) + { + mathWrite( *stream, mNodes[i].point ); + stream->write( mNodes[i].width ); + stream->write( mNodes[i].depth ); + mathWrite( *stream, mNodes[i].normal ); + } + } + else + { + // There isn't enough space left in the stream for all of the + // nodes. Batch them up into NetEvents. + U32 id = gServerNodeListManager->nextListId(); + U32 count = 0; + U32 index = 0; + while (count < mNodes.size()) + { + count += NodeListManager::smMaximumNodesPerEvent; + if (count > mNodes.size()) + { + count = mNodes.size(); + } + + RiverNodeEvent* event = new RiverNodeEvent(); + event->mId = id; + event->mTotalNodes = mNodes.size(); + event->mLocalListStart = index; + + for (; indexmPositions.push_back( mNodes[index].point ); + event->mWidths.push_back( mNodes[index].width ); + event->mDepths.push_back( mNodes[index].depth ); + event->mNormals.push_back( mNodes[index].normal ); + } + + con->postNetEvent( event ); + } + + stream->write( id ); + } + } + + if( stream->writeFlag( mask & ( RiverMask | InitialUpdateMask ) ) ) + { + // This is set to allow the user to modify the size of the water dynamically + // in the editor + mathWrite( *stream, mObjScale ); + stream->writeAffineTransform( mObjToWorld ); + } + + stream->writeFlag( mask & RegenMask ); + + return retMask; +} + +void River::unpackUpdate(NetConnection * con, BitStream * stream) +{ + // Unpack Parent. + Parent::unpackUpdate(con, stream); + + // RiverMask + if(stream->readFlag()) + { + MatrixF ObjectMatrix; + stream->readAffineTransform(&ObjectMatrix); + Parent::setTransform(ObjectMatrix); + + stream->read( &mMetersPerSegment ); + stream->read( &mSegmentsPerBatch ); + stream->read( &mDepthScale ); + stream->read( &mMaxDivisionSize ); + stream->read( &mColumnCount ); + + stream->read( &mFlowMagnitude ); + stream->read( &mLodDistance ); + } + + // NodeMask + if ( stream->readFlag() ) + { + if (stream->readFlag()) + { + // Nodes have been passed in this update + U32 count = stream->readInt( 16 ); + + mNodes.clear(); + + Point3F pos; + VectorF normal; + F32 width,depth; + + for ( U32 i = 0; i < count; i++ ) + { + mathRead( *stream, &pos ); + stream->read( &width ); + stream->read( &depth ); + mathRead( *stream, &normal ); + _addNode( pos, width, depth, normal ); + } + } + else + { + // Nodes will arrive as events + U32 id; + stream->read( &id ); + + // Check if the road's nodes made it here before we did. + NodeListManager::NodeList* list = NULL; + if ( gClientNodeListManager->findListById( id, &list, true) ) + { + // Work with the completed list + RiverNodeList* riverList = dynamic_cast( list ); + if (riverList) + buildNodesFromList( riverList ); + + delete list; + } + else + { + // Nodes have not yet arrived, so register our interest in the list + RiverNodeListNotify* notify = new RiverNodeListNotify( this, id ); + gClientNodeListManager->registerNotification( notify ); + } + } + } + + // RiverMask | InitialUpdateMask + if( stream->readFlag() ) + { + mathRead( *stream, &mObjScale ); + stream->readAffineTransform( &mObjToWorld ); + } + + // RegenMask + if ( stream->readFlag() && isProperlyAdded() ) + regenerate(); +} + +void River::_getWaterPlane( const Point3F &camPos, PlaneF &outPlane, Point3F &outPos ) +{ + // Find the RiverSegment closest to the camera. + F32 closestDist = F32_MAX; + S32 closestSegment = 0; + Point3F projPnt(0.0f, 0.0f, 0.0f); + + VectorF normal(0,0,0); + + for ( U32 i = 0; i < mSegments.size(); i++ ) + { + const RiverSegment &segment = mSegments[i]; + + const Point3F pos = MathUtils::mClosestPointOnSegment( segment.slice0->p1, segment.slice1->p1, camPos ); + + F32 dist = ( camPos - pos ).len(); + + if ( dist < closestDist ) + { + closestDist = dist; + closestSegment = i; + projPnt = pos; + } + + normal += segment.getSurfaceNormal(); + } + + if ( mReflectNormalUp ) + normal.set(0,0,1); + else + normal.normalizeSafe(); + + outPos = projPnt; + outPlane.set( projPnt, normal ); +} + +void River::setTransform( const MatrixF &mat ) +{ + + for ( U32 i = 0; i < mNodes.size(); i++ ) + { + mWorldToObj.mulP( mNodes[i].point ); + mat.mulP( mNodes[i].point ); + } + + /* + // Get the amount of change in position. + MatrixF oldMat = getTransform(); + Point3F oldPos = oldMat.getPosition(); + Point3F newPos = mat.getPosition(); + Point3F delta = newPos - oldPos; + + // Offset all nodes by that amount + for ( U32 i = 0; i < mNodes.size(); i++ ) + { + mNodes[i].point += delta; + } + + // Assign the new position ( we ignore rotation ) + MatrixF newMat( oldMat ); + newMat.setPosition( newPos ); + */ + + Parent::setTransform( mat ); + + // Regenerate and update the client + _regenerate(); + setMaskBits( NodeMask | RegenMask ); +} + +void River::setScale( const VectorF &scale ) +{ + // We ignore scale requests from the editor + // right now. +} + +bool River::castRay(const Point3F &s, const Point3F &e, RayInfo* info) +{ + Point3F start = s; + Point3F end = e; + mObjToWorld.mulP(start); + mObjToWorld.mulP(end); + + F32 out = 1.0f; // The output fraction/percentage along the line defined by s and e + VectorF norm(0.0f, 0.0f, 0.0f); // The normal of the face intersected + + Vector hitSegments; + + for ( U32 i = 0; i < mSegments.size(); i++ ) + { + const RiverSegment &segment = mSegments[i]; + + F32 t; + VectorF n; + + if ( segment.worldbounds.collideLine( start, end, &t, &n ) ) + { + hitSegments.increment(); + hitSegments.last().t = t; + hitSegments.last().idx = i; + } + } + + dQsort( hitSegments.address(), hitSegments.size(), sizeof(RiverHitSegment), compareHitSegments ); + + U32 idx0, idx1, idx2; + F32 t; + + for ( U32 i = 0; i < hitSegments.size(); i++ ) + { + U32 segIdx = hitSegments[i].idx; + const RiverSegment &segment = mSegments[segIdx]; + + // Each segment has 6 faces + for ( U32 j = 0; j < 6; j++ ) + { + if ( j == 4 && segIdx != 0 ) + continue; + + if ( j == 5 && segIdx != mSegments.size() - 1 ) + continue; + + // Each face has 2 triangles + for ( U32 k = 0; k < 2; k++ ) + { + idx0 = gIdxArray[j][k][0]; + idx1 = gIdxArray[j][k][1]; + idx2 = gIdxArray[j][k][2]; + + const Point3F &v0 = segment[idx0]; + const Point3F &v1 = segment[idx1]; + const Point3F &v2 = segment[idx2]; + + if ( !MathUtils::mLineTriangleCollide( start, end, + v2, v1, v0, + NULL, + &t ) ) + continue; + + if ( t >= 0.0f && t < 1.0f && t < out ) + { + out = t; + + // optimize this, can be calculated easily within + // the collision test + norm = PlaneF( v0, v1, v2 ); + } + } + } + + if (out >= 0.0f && out < 1.0f) + break; + } + + if (out >= 0.0f && out < 1.0f) + { + info->t = out; + info->normal = norm; + info->point.interpolate(start, end, out); + info->face = -1; + info->object = this; + + return true; + } + + return false; +} + +bool River::collideBox(const Point3F &start, const Point3F &end, RayInfo* info) +{ + return false; +} + +F32 River::getWaterCoverage( const Box3F &worldBox ) const +{ + PROFILE_SCOPE( River_GetWaterCoverage ); + + if ( !mWorldBox.isOverlapped(worldBox) ) + return 0.0f; + + Point3F bottomPnt = worldBox.getCenter(); + bottomPnt.z = worldBox.minExtents.z; + + F32 farthest = 0.0f; + + for ( U32 i = 0; i < mSegments.size(); i++ ) + { + const RiverSegment &segment = mSegments[i]; + + if ( !segment.worldbounds.isOverlapped(worldBox) ) + continue; + + if ( !segment.intersectBox( worldBox ) ) + continue; + + F32 distance = segment.distanceToSurface( bottomPnt ); + + if ( distance > farthest ) + farthest = distance; + } + + F32 height = worldBox.maxExtents.z - worldBox.minExtents.z; + F32 distance = mClampF( farthest, 0.0f, height ); + F32 coverage = distance / height; + + return coverage; +} + +F32 River::getSurfaceHeight( const Point2F &pos ) const +{ + PROFILE_SCOPE( River_GetSurfaceHeight ); + + Point3F origin( pos.x, pos.y, mWorldBox.maxExtents.z ); + Point3F direction(0,0,-1); + U32 nodeIdx; + Point3F collisionPnt; + + if ( !collideRay( origin, direction, &nodeIdx, &collisionPnt ) ) + return -1.0f; + + return collisionPnt.z; +} + +VectorF River::getFlow( const Point3F &pos ) const +{ + PROFILE_SCOPE( River_GetFlow ); + + for ( U32 i = 0; i < mSegments.size(); i++ ) + { + const RiverSegment &segment = mSegments[i]; + + if ( !segment.containsPoint(pos) ) + continue; + + VectorF flow = segment.slice0->p1 - segment.slice1->p1; + flow.normalize(); + flow *= mFlowMagnitude; + + return flow; + } + + return VectorF::Zero; +} + +void River::onReflectionInfoChanged() +{ + /* + if ( isClientObject() && GFX->getPixelShaderVersion() >= 1.4 ) + { + if ( mFullReflect ) + REFLECTMGR->registerObject( this, ReflectDelegate( this, &River::updateReflection ), mReflectPriority, mReflectMaxRateMs, mReflectMaxDist ); + else + { + REFLECTMGR->unregisterObject( this ); + mReflectTex = NULL; + } + } + */ +} + +void River::_regenerate() +{ + if ( mNodes.size() == 0 ) + return; + + const Point3F &nodePt = mNodes.first().point; + + MatrixF mat( true ); + mat.setPosition( nodePt ); + Parent::setTransform( mat ); + + _generateSlices(); +} + +void River::_generateSlices() +{ + if ( mNodes.size() < 2 ) + return; + + U32 nodeCount = mNodes.size(); + RiverSplineNode *splineNodes = new RiverSplineNode[nodeCount]; + + for ( U32 i = 0; i < nodeCount; i++ ) + { + const RiverNode &node = mNodes[i]; + splineNodes[i].x = node.point.x; + splineNodes[i].y = node.point.y; + splineNodes[i].z = node.point.z; + splineNodes[i].width = node.width; + splineNodes[i].depth = node.depth; + splineNodes[i].normal = node.normal; + } + + CatmullRom spline; + spline.initialize( nodeCount, splineNodes ); + delete [] splineNodes; + + mSlices.clear(); + + for ( U32 i = 1; i < nodeCount; i++ ) + { + F32 t0 = spline.getTime( i-1 ); + F32 t1 = spline.getTime( i ); + + F32 segLength = spline.arcLength( t0, t1 ); + + U32 numSegments = mCeil( segLength / mMetersPerSegment ); + numSegments = getMax( numSegments, (U32)1 ); + F32 tstep = ( t1 - t0 ) / numSegments; + + //AssertFatal( numSegments > 0, "River::_generateSlices, got zero segments!" ); + + U32 startIdx = 0; + U32 endIdx = ( i == nodeCount - 1 ) ? numSegments + 1 : numSegments; + + for ( U32 j = startIdx; j < endIdx; j++ ) + { + F32 t = t0 + tstep * j; //spline.findParameterByDistance( 0.0f, i * segLen ); + RiverSplineNode val = spline.evaluate(t); + + RiverSlice slice; + slice.p1.set( val.x, val.y, val.z ); + slice.uvec.set( 0,0,1 ); + slice.width = val.width; + slice.depth = val.depth; + slice.parentNodeIdx = i-1; + slice.normal = val.normal; + slice.normal.normalize(); + mSlices.push_back( slice ); + } + } + + // + // Calculate fvec and rvec for all slices + // + RiverSlice *pSlice = NULL; + RiverSlice *pNextSlice = NULL; + + // Must do the first slice outside the loop + { + pSlice = &mSlices[0]; + pNextSlice = &mSlices[1]; + pSlice->fvec = pNextSlice->p1 - pSlice->p1; + pSlice->fvec.normalize(); + pSlice->rvec = mCross( pSlice->fvec, pSlice->normal ); + pSlice->rvec.normalize(); + pSlice->uvec = mCross( pSlice->rvec, pSlice->fvec ); + pSlice->uvec.normalize(); + pSlice->rvec = mCross( pSlice->fvec, pSlice->uvec ); + pSlice->rvec.normalize(); + } + + for ( U32 i = 1; i < mSlices.size() - 1; i++ ) + { + pSlice = &mSlices[i]; + pNextSlice = &mSlices[i+1]; + + pSlice->fvec = pNextSlice->p1 - pSlice->p1; + pSlice->fvec.normalize(); + + pSlice->rvec = mCross( pSlice->fvec, pSlice->normal ); + pSlice->rvec.normalize(); + + pSlice->uvec = mCross( pSlice->rvec, pSlice->fvec ); + pSlice->uvec.normalize(); + + pSlice->rvec = mCross( pSlice->fvec, pSlice->uvec ); + pSlice->rvec.normalize(); + } + + // Must do the last slice outside the loop + { + RiverSlice *lastSlice = &mSlices[mSlices.size()-1]; + RiverSlice *prevSlice = &mSlices[mSlices.size()-2]; + + lastSlice->fvec = prevSlice->fvec; + + lastSlice->rvec = mCross( lastSlice->fvec, lastSlice->normal ); + lastSlice->rvec.normalize(); + + lastSlice->uvec = mCross( lastSlice->rvec, lastSlice->fvec ); + lastSlice->uvec.normalize(); + + lastSlice->rvec = mCross( lastSlice->fvec, lastSlice->uvec ); + lastSlice->rvec.normalize(); + } + + + // + // Calculate p0/p2/pb0/pb2 for all slices + // + for ( U32 i = 0; i < mSlices.size(); i++ ) + { + RiverSlice *slice = &mSlices[i]; + slice->p0 = slice->p1 - slice->rvec * slice->width * 0.5f; + slice->p2 = slice->p1 + slice->rvec * slice->width * 0.5f; + slice->pb0 = slice->p0 - slice->uvec * slice->depth; + slice->pb2 = slice->p2 - slice->uvec * slice->depth; + } + + // Generate the object/world bounds + Box3F box; + for ( U32 i = 0; i < mSlices.size(); i++ ) + { + const RiverSlice &slice = mSlices[i]; + + if ( i == 0 ) + { + box.minExtents = slice.p0; + box.maxExtents = slice.p2; + box.extend( slice.pb0 ); + box.extend( slice.pb2 ); + } + else + { + box.extend( slice.p0 ); + box.extend( slice.p2 ); + box.extend( slice.pb0 ); + box.extend( slice.pb2 ); + } + } + + Point3F pos = getPosition(); + + mWorldBox = box; + //mObjBox.minExtents -= pos; + //mObjBox.maxExtents -= pos; + resetObjectBox(); + + // Make sure we are in the correct bins given our world box. + if( getSceneManager() != NULL ) + getSceneManager()->notifyObjectDirty( this ); + + _generateSegments(); +} + +void River::_generateSegments() +{ + mSegments.clear(); + + for ( U32 i = 0; i < mSlices.size() - 1; i++ ) + { + RiverSegment seg( &mSlices[i], &mSlices[i+1] ); + + mSegments.push_back( seg ); + } + + /* + #ifdef TORQUE_DEBUG + + for ( U32 i = 0; i < mSegments.size(); i++ ) + { + const RiverSegment &segment = mSegments[i]; + PlaneF normal0 = MathUtils::mTriangleNormal( segment.slice0->p0, segment.slice1->p0, segment.slice1->p2 ); + PlaneF normal1 = MathUtils::mTriangleNormal( segment.slice0->p0, segment.slice1->p2, segment.slice0->p2 ); + AssertFatal( true || normal0 != normal1, "River::generateSegments, segment is not coplanar!" ); + } + + #endif // TORQUE_DEBUG + */ + + // We have to go back and generate normals for each slice + // to be used in calculation of the reflect plane. + // The slice-normal we calculate are relative to the surface normal + // of the segments adjacent to the slice. + /* + if ( mSlices.size() >= 2 ) + { + mSlices[0].normal = mSegments[0].getSurfaceNormal(); + for ( U32 i = 1; i < mSlices.size() - 1; i++ ) + { + mSlices[i].normal = ( mSegments[i-1].getSurfaceNormal() + mSegments[i].getSurfaceNormal() ) / 2; + } + mSlices.last().normal = mSegments.last().getSurfaceNormal(); + } + */ + + _generateVerts(); +} + +void River::_generateVerts() +{ + if ( isServerObject() ) + return; + + // These will depend on the level of subdivision per segment + // calculated below. + mHighVertCount = 0; + mHighTriangleCount = 0; + + // Calculate the number of row/column subdivisions per each + // RiverSegment. + + F32 greatestWidth = 0.1f; + for ( U32 i = 0; i < mNodes.size(); i++ ) + { + RiverNode &node = mNodes[i]; + if ( node.width > greatestWidth ) + greatestWidth = node.width; + } + + mColumnCount = mCeil( greatestWidth / mMaxDivisionSize ); + + for ( U32 i = 0; i < mSegments.size(); i++ ) + { + RiverSegment &segment = mSegments[i]; + const RiverSlice *slice = segment.slice0; + const RiverSlice *nextSlice = segment.slice1; + + // Calculate the size of divisions in the forward direction ( p00 -> p01 ) + F32 segLength = (nextSlice->p1 - slice->p1).len(); + + // A division count of one is actually NO subdivision, + // the segment corners are the only verts in this segment. + U32 numRows = 1; + + if ( segLength > 0.0f ) + numRows = mCeil( segLength / mMaxDivisionSize ); + + // The problem with calculating num columns per segment is + // two adjacent - high lod segments of different width can have + // verts that don't line up! So even though RiverSegment HAS a + // column data member we initialize all segments in the river to + // the same (River::mColumnCount) + + // Calculate the size of divisions in the right direction ( p00 -> p10 ) + // F32 segWidth = ( ( p11 - p01 ).len() + ( p10 - p00 ).len() ) * 0.5f; + + // U32 numColumns = 5; + //F32 columnSize = segWidth / numColumns; + + //while ( columnSize > mMaxDivisionSize ) + //{ + // numColumns++; + // columnSize = segWidth / numColumns; + //} + + // Save the calculated numb of columns / rows for this segment. + segment.columns = mColumnCount; + segment.rows = numRows; + + // Save the corresponding number of verts/prims + segment.numVerts = ( 1 + mColumnCount ) * ( 1 + numRows ); + segment.numTriangles = mColumnCount * numRows * 2; + + mHighVertCount += segment.numVerts; + mHighTriangleCount += segment.numTriangles; + } + + // Number of low detail verts/prims. + mLowVertCount = mSlices.size() * 2; + mLowTriangleCount = mSegments.size() * 2; + + // Allocate the low detail VertexBuffer, + // this will stay in memory and will never need to change. + mVB_low.set( GFX, mLowVertCount, GFXBufferTypeStatic ); + + GFXWaterVertex *lowVertPtr = mVB_low.lock(); + U32 vertCounter = 0; + + // The texCoord.y value start/end for a segment + // as we loop through them. + F32 textCoordV = 0; + + // + // Fill the low-detail VertexBuffer + // + for ( U32 i = 0; i < mSlices.size(); i++ ) + { + RiverSlice &slice = mSlices[i]; + + lowVertPtr->point = slice.p0; + lowVertPtr->normal = slice.normal; + lowVertPtr->undulateData.set( -slice.width*0.5f, textCoordV ); + lowVertPtr->horizonFactor.set( 0, 0, 0, 0 ); + lowVertPtr++; + vertCounter++; + + lowVertPtr->point = slice.p2; + lowVertPtr->normal = slice.normal; + lowVertPtr->undulateData.set( slice.width*0.5f, textCoordV ); + lowVertPtr->horizonFactor.set( 0, 0, 0, 0 ); + lowVertPtr++; + vertCounter++; + + // Save this so we can get it later. + slice.texCoordV = textCoordV; + + if ( i < mSlices.size() - 1 ) + { + // Increment the textCoordV for the next slice. + F32 segLen = ( mSlices[i+1].p1 - slice.p1 ).len(); + textCoordV += segLen; + } + } + + AssertFatal( vertCounter == mLowVertCount, "River, wrote incorrect number of verts in mBV_low!" ); + + // Unlock the low-detail VertexBuffer, we are done filling it. + mVB_low.unlock(); + + // + // Create the low-detail prim buffer(s) + // + mPB_low.set( GFX, mLowTriangleCount * 3, mLowTriangleCount, GFXBufferTypeStatic ); + + U16 *lowIdxBuff; + mPB_low.lock(&lowIdxBuff); + U32 curLowIdx = 0; + + // Temporaries to hold indices for the corner points of a quad. + U32 p00, p01, p11, p10; + + U32 offset = 0; + + // Fill the low-detail PrimitiveBuffer + for ( U32 i = 0; i < mSegments.size(); i++ ) + { + //const RiverSegment &segment = mSegments[i]; + + // Two triangles formed by the corner points of this segment + // into the the low detail primitive buffer. + p00 = offset; + p01 = p00 + 2; + p11 = p01 + 1; + p10 = p00 + 1; + + // Upper-Left triangle + lowIdxBuff[curLowIdx] = p00; + curLowIdx++; + lowIdxBuff[curLowIdx] = p01; + curLowIdx++; + lowIdxBuff[curLowIdx] = p11; + curLowIdx++; + + // Lower-Right Triangle + lowIdxBuff[curLowIdx] = p00; + curLowIdx++; + lowIdxBuff[curLowIdx] = p11; + curLowIdx++; + lowIdxBuff[curLowIdx] = p10; + curLowIdx++; + + offset += 2; + } + + AssertFatal( curLowIdx == mLowTriangleCount * 3, "River, wrote incorrect number of indices in mPB_low!" ); + + // Unlock the low-detail PrimitiveBuffer, we are done filling it. + mPB_low.unlock(); +} + +bool River::getClosestNode( const Point3F &pos, U32 &idx ) const +{ + F32 closestDist = F32_MAX; + + for ( U32 i = 0; i < mNodes.size(); i++ ) + { + F32 dist = ( mNodes[i].point - pos ).len(); + if ( dist < closestDist ) + { + closestDist = dist; + idx = i; + } + } + + return closestDist != F32_MAX; +} + +bool River::containsPoint( const Point3F &worldPos, U32 *nodeIdx ) const +{ + // If point isn't in the world box, + // it's definitely not in the River. + //if ( !getWorldBox().isContained( worldPos ) ) + // return false; + + // Look through all edges, does the polygon + // formed from adjacent edge's contain the worldPos? + for ( U32 i = 0; i < mSegments.size(); i++ ) + { + const RiverSegment &segment = mSegments[i]; + + if ( segment.containsPoint( worldPos ) ) + { + if ( nodeIdx ) + *nodeIdx = i; + return true; + } + } + + return false; +} + +F32 River::distanceToSurface( const Point3F &pnt, U32 segmentIdx ) +{ + return mSegments[segmentIdx].distanceToSurface( pnt ); +} + +bool River::collideRay( const Point3F &origin, const Point3F &direction, U32 *nodeIdx, Point3F *collisionPnt ) const +{ + Point3F p0 = origin; + Point3F p1 = origin + direction * 2000.0f; + + // If the line segment does not collide with the river's world box, + // it definitely does not collide with any part of the river. + if ( !getWorldBox().collideLine( p0, p1 ) ) + return false; + + if ( mSlices.size() < 2 ) + return false; + + MathUtils::Quad quad; + MathUtils::Ray ray; + F32 t; + + // Check each river segment (formed by a pair of slices) for collision + // with the line segment. + for ( U32 i = 0; i < mSlices.size() - 1; i++ ) + { + const RiverSlice &slice0 = mSlices[i]; + const RiverSlice &slice1 = mSlices[i+1]; + + // For simplicities sake we will only test for collision between the + // line segment and the Top face of the river segment. + + // Clockwise starting with the leftmost/closest point. + quad.p00 = slice0.p0; + quad.p01 = slice1.p0; + quad.p11 = slice1.p2; + quad.p10 = slice0.p2; + + ray.origin = origin; + ray.direction = direction; + + // NOTE: + // mRayQuadCollide is designed for a "real" quad in which all four points + // are coplanar which is actually not the case here. The more twist + // and turn in-between two neighboring river slices the more incorrect + // this calculation will be. + + if ( MathUtils::mRayQuadCollide( quad, ray, NULL, &t ) ) + { + if ( nodeIdx ) + *nodeIdx = slice0.parentNodeIdx; + if ( collisionPnt ) + *collisionPnt = ray.origin + ray.direction * t; + return true; + } + } + + return false; +} + +Point3F River::getNodePosition( U32 idx ) const +{ + if ( mNodes.size() - 1 < idx ) + return Point3F(); + + return mNodes[idx].point; +} + +void River::setNodePosition( U32 idx, const Point3F &pos ) +{ + if ( mNodes.size() - 1 < idx ) + return; + + mNodes[idx].point = pos; + + regenerate(); + + setMaskBits( NodeMask | RegenMask ); +} + +U32 River::addNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal ) +{ + U32 idx = _addNode( pos, width, depth, normal ); + + regenerate(); + + setMaskBits( NodeMask | RegenMask ); + + return idx; +} + +U32 River::insertNode(const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal, const U32 &idx) +{ + U32 ret = _insertNode( pos, width, depth, normal, idx ); + + regenerate(); + + setMaskBits( NodeMask | RegenMask ); + + return ret; +} + +void River::setNode(const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal, const U32 &idx) +{ + if ( mNodes.size() - 1 < idx ) + return; + + RiverNode &node = mNodes[idx]; + node.point = pos; + node.width = width; + node.depth = depth; + node.normal = normal; + + regenerate(); + + setMaskBits( NodeMask | RegenMask ); +} + +void River::setNodeWidth( U32 idx, F32 meters ) +{ + meters = mClampF( meters, MIN_NODE_WIDTH, MAX_NODE_WIDTH ); + + if ( mNodes.size() - 1 < idx ) + return; + + mNodes[idx].width = meters; + _regenerate(); + + setMaskBits( RegenMask | NodeMask ); +} + +void River::setNodeHeight( U32 idx, F32 height ) +{ + if ( mNodes.size() - 1 < idx ) + return; + + mNodes[idx].point.z = height; + _regenerate(); + + setMaskBits( RegenMask | NodeMask ); +} + +F32 River::getNodeWidth( U32 idx ) const +{ + if ( mNodes.size() - 1 < idx ) + return -1.0f; + + return mNodes[idx].width; +} + +void River::setNodeDepth( U32 idx, F32 meters ) +{ + meters = mClampF( meters, MIN_NODE_DEPTH, MAX_NODE_DEPTH ); + + if ( mNodes.size() - 1 < idx ) + return; + + mNodes[idx].depth = meters; + _regenerate(); + setMaskBits( RiverMask | RegenMask | NodeMask ); +} + +void River::setNodeNormal( U32 idx, const VectorF &normal ) +{ + if ( mNodes.size() - 1 < idx ) + return; + + mNodes[idx].normal = normal; + + regenerate(); + + setMaskBits( NodeMask | RegenMask ); +} + +F32 River::getNodeDepth( U32 idx ) const +{ + if ( mNodes.size() - 1 < idx ) + return -1.0f; + + return mNodes[idx].depth; +} + +VectorF River::getNodeNormal( U32 idx ) const +{ + if ( mNodes.size() - 1 < idx ) + return VectorF::Zero; + + return mNodes[idx].normal; +} + +MatrixF River::getNodeTransform( U32 idx ) const +{ + MatrixF mat(true); + + if ( mNodes.size() - 1 < idx ) + return mat; + + bool hasNext = idx + 1 < mNodes.size(); + bool hasPrev = (S32)idx - 1 >= 0; + + const RiverNode &node = mNodes[idx]; + + VectorF fvec( 0, 1, 0 ); + + if ( hasNext ) + { + fvec = mNodes[idx+1].point - node.point; + fvec.normalizeSafe(); + } + else if ( hasPrev ) + { + fvec = node.point - mNodes[idx-1].point; + fvec.normalizeSafe(); + } + else + fvec = mPerp( node.normal ); + + if ( fvec.isZero() ) + fvec = mPerp( node.normal ); + + F32 dot = mDot( fvec, node.normal ); + if ( dot < -0.9f || dot > 0.9f ) + fvec = mPerp( node.normal ); + + VectorF rvec = mCross( fvec, node.normal ); + if ( rvec.isZero() ) + rvec = mPerp( fvec ); + rvec.normalize(); + + fvec = mCross( node.normal, rvec ); + fvec.normalize(); + + mat.setColumn( 0, rvec ); + mat.setColumn( 1, fvec ); + mat.setColumn( 2, node.normal ); + mat.setColumn( 3, node.point ); + + AssertFatal( m_matF_determinant( mat ) != 0.0f, "no inverse!"); + + return mat; +} + +void River::deleteNode( U32 idx ) +{ + if ( mNodes.size() - 1 < idx ) + return; + + mNodes.erase(idx); + _regenerate(); + + setMaskBits( RegenMask | NodeMask ); +} + +void River::buildNodesFromList( RiverNodeList* list ) +{ + mNodes.clear(); + + for (U32 i=0; imPositions.size(); ++i) + { + _addNode( list->mPositions[i], list->mWidths[i], list->mDepths[i], list->mNormals[i] ); + } + + _regenerate(); +} + +void River::_makeRenderBatches( const Point3F &cameraPos ) +{ + // Loop through each segment to determine if it is either 1 [not visible], 2 [high LOD], 3 [low LOD] + + mHighLODBatches.clear(); + mLowLODBatches.clear(); + + // Keeps track of what we batch type we are currently collecting. + // -1 is uninitialized, 0 is low detail, 1 is high detail + S32 lastDetail = -1; + bool highDetail; + + U32 startSegmentIdx = -1; + U32 endSegmentIdx = 0; + + F32 lodDistSquared = mLodDistance * mLodDistance; + + for ( U32 i = 0; i < mSegments.size(); i++ ) + { + const RiverSegment &segment = mSegments[i]; + const RiverSlice *slice = segment.slice0; + const RiverSlice *nextSlice = segment.slice1; + + // TODO: add bounds BoxF to RiverSegment + const bool isVisible = true; //frustum.intersects( segment.bounds ); + if ( isVisible ) + { + F32 dist0 = MathUtils::mTriangleDistance( slice->p0, nextSlice->p0, nextSlice->p2, cameraPos ); + F32 dist1 = MathUtils::mTriangleDistance( slice->p0, nextSlice->p2, slice->p2, cameraPos ); + + F32 dist = getMin( dist0, dist1 ); + highDetail = ( dist < lodDistSquared ); + if ( highDetail && lastDetail == 0 || + !highDetail && lastDetail == 1 ) + { + // We hit a segment with a different lod than the previous. + // Save what we have so far... + + RiverRenderBatch batch; + + batch.startSegmentIdx = startSegmentIdx; + batch.endSegmentIdx = endSegmentIdx; + + if ( lastDetail == 0 ) + { + mLowLODBatches.push_back( batch ); + } + else + { + mHighLODBatches.push_back( batch ); + } + + // Reset the batching + startSegmentIdx = -1; + lastDetail = -1; + i--; + + continue; + } + + // If this is the start of a set of batches. + if ( startSegmentIdx == -1 ) + { + endSegmentIdx = startSegmentIdx = i; + lastDetail = ( highDetail ) ? 1 : 0; + } + + // Else we're extending the end batch index. + else + ++endSegmentIdx; + + // If this isn't the last batch then continue. + if ( i < mSegments.size()-1 ) + continue; + } + + // If we still don't have a start batch skip. + if ( startSegmentIdx == -1 ) + continue; + + // Save what we have so far... + + RiverRenderBatch batch; + + batch.startSegmentIdx = startSegmentIdx; + batch.endSegmentIdx = endSegmentIdx; + + if ( lastDetail == 0 ) + { + mLowLODBatches.push_back( batch ); + } + else + { + mHighLODBatches.push_back( batch ); + } + + // Reset the batching. + startSegmentIdx = -1; + lastDetail = -1; + } +} + +void River::_makeHighLODBuffers() +{ + PROFILE_SCOPE( River_makeHighLODBuffers ); + + // This is the number of verts/triangles for ALL high lod batches combined. + // eg. the size for the buffers. + U32 numVerts = 0; + U32 numTriangles = 0; + + for ( U32 i = 0; i < mHighLODBatches.size(); i++ ) + { + RiverRenderBatch &batch = mHighLODBatches[i]; + + for ( U32 j = batch.startSegmentIdx; j <= batch.endSegmentIdx; j++ ) + { + const RiverSegment &segment = mSegments[j]; + + numTriangles += segment.numTriangles; + numVerts += segment.numVerts; + } + } + + if ( numVerts > MAX_DYNAMIC_VERTS || numTriangles * 3 > MAX_DYNAMIC_INDICES ) + { + mVB_high = NULL; + mPB_high = NULL; + return; + } + + mHighTriangleCount = numTriangles; + mHighVertCount = numVerts; + + mVB_high.set( GFX, numVerts, GFXBufferTypeVolatile ); + GFXWaterVertex *vertPtr = mVB_high.lock(); + U32 vertCounter = 0; + + // NOTE: this will break if different segments have different number + // of columns, but that will also cause T-junction triangles so just don't + // do that. + + // For each batch, loop through the segments contained by + // that batch, and add their verts to the buffer. + for ( U32 i = 0; i < mHighLODBatches.size(); i++ ) + { + RiverRenderBatch &batch = mHighLODBatches[i]; + + batch.startVert = vertCounter; + batch.vertCount = 0; + + VectorF lastNormal(0,0,1); + + for ( U32 j = batch.startSegmentIdx; j <= batch.endSegmentIdx; j++ ) + { + // Add the verts for this segment to the buffer. + RiverSegment &segment = mSegments[j]; + + BiSqrToQuad3D squareToQuad( segment.getP00(), + segment.getP10(), + segment.getP11(), + segment.getP01() ); + + // We are duplicating the last row of verts in a segment on + // the first row of the next segment. This could be optimized but + // shouldn't cause any problems. + + VectorF normal = segment.getSurfaceNormal(); + + for ( U32 k = 0; k <= segment.rows; k++ ) + { + VectorF vertNormal = ( k == 0 && j != batch.startSegmentIdx ) ? lastNormal : normal; + + F32 rowLen = mLerp( segment.slice0->width, segment.slice1->width, (F32)k / (F32)segment.rows ); + + for ( U32 l = 0; l <= segment.columns; l++ ) + { + // We are generating a "row" of verts along the forwardDivision + // Each l iteration is a step to the right along with row. + + Point2F uv( (F32)l / (F32)segment.columns, (F32)k / (F32)segment.rows ); + + Point3F pnt = squareToQuad.transform( uv ); + + // Assign the Vert + vertPtr->point = pnt; + vertPtr->normal = vertNormal; + vertPtr->undulateData.x = ( uv.x - 0.5f ) * rowLen; + vertPtr->undulateData.y = ( segment.TexCoordEnd() - segment.TexCoordStart() ) * uv.y + segment.TexCoordStart(); + vertPtr->horizonFactor.set( 0, 0, 0, 0 ); + + vertPtr++; + vertCounter++; + batch.vertCount++; + } + } + + lastNormal = normal; + } + } + + AssertFatal( vertCounter == mHighVertCount, "River, wrote incorrect number of verts in mVB_high" ); + + mVB_high.unlock(); + + // + // Do the high lod primitive buffer. + // + + mPB_high.set( GFX, numTriangles * 3, numTriangles, GFXBufferTypeVolatile ); + U16 *idxBuff; + mPB_high.lock(&idxBuff); + U32 curIdx = 0; + + U32 batchOffset = 0; + + // For each high lod batch, we must add indices to the buffer + // for each segment it contains ( and the count will depend on + // the division level columns/rows for each segment ). + + // Temporaries for holding the indices of a quad + U32 p00, p01, p11, p10; + + for ( U32 i = 0; i < mHighLODBatches.size(); i++ ) + { + RiverRenderBatch &batch = mHighLODBatches[i]; + + batch.indexCount = 0; + batch.triangleCount = 0; + batch.startIndex = curIdx; + + U32 temp = 0; + U32 segmentOffset = 0; + + for ( U32 j = batch.startSegmentIdx; j <= batch.endSegmentIdx; j++ ) + { + const RiverSegment &segment = mSegments[j]; + + // Loop through all divisions adding the indices to the + // high detail primitive buffer. + for ( U32 k = 0; k < segment.rows; k++ ) + { + for ( U32 l = 0; l < segment.columns; l++ ) + { + // The indices for this quad. + p00 = batchOffset + segmentOffset + l + k * ( segment.columns + 1 ); + p01 = p00 + segment.columns + 1; + p11 = p01 + 1; + p10 = p00 + 1; + + AssertFatal( p00 <= mHighTriangleCount * 3, "River, bad draw call!" ); + AssertFatal( p01 <= mHighTriangleCount * 3, "River, bad draw call!" ); + AssertFatal( p11 <= mHighTriangleCount * 3, "River, bad draw call!" ); + AssertFatal( p10 <= mHighTriangleCount * 3, "River, bad draw call!" ); + + // Upper-Left triangle + idxBuff[curIdx] = p00; + curIdx++; + idxBuff[curIdx] = p01; + curIdx++; + idxBuff[curIdx] = p11; + curIdx++; + + // Lower-Right Triangle + idxBuff[curIdx] = p00; + curIdx++; + idxBuff[curIdx] = p11; + curIdx++; + idxBuff[curIdx] = p10; + curIdx++; + + batch.indexCount += 6; + batch.triangleCount += 2; + } + } + + // Increment the sliceOffset by the number of verts + // used by this segment. So the next segment will index + // into new verts. + segmentOffset += ( segment.columns + 1 ) * ( segment.rows + 1 ); + temp += ( segment.columns + 1 ) * ( segment.rows + 1 ); + } + + batchOffset += temp; + } + + // Unlock the PrimitiveBuffer, we are done filling it. + mPB_high.unlock(); +} + +U32 River::_addNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal ) +{ + mNodes.increment(); + RiverNode &node = mNodes.last(); + + node.point = pos; + node.width = width; + node.depth = depth; + node.normal = normal; + + setMaskBits( NodeMask | RegenMask ); + + return mNodes.size() - 1; +} + +U32 River::_insertNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal, const U32 &idx ) +{ + U32 ret; + RiverNode *node; + + if ( idx == U32_MAX ) + { + mNodes.increment(); + node = &mNodes.last(); + ret = mNodes.size() - 1; + } + else + { + mNodes.insert( idx ); + node = &mNodes[idx]; + ret = idx; + } + + node->point = pos; + node->depth = depth; + node->width = width; + node->normal = normal; + + return ret; +} + +void River::setMetersPerSegment( F32 meters ) +{ + if ( meters < MIN_METERS_PER_SEGMENT ) + { + Con::warnf( "River::setMetersPerSegment, specified meters (%g) is below the min meters (%g), NOT SET!", meters, MIN_METERS_PER_SEGMENT ); + return; + } + + mMetersPerSegment = meters; + _regenerate(); + setMaskBits( RiverMask | RegenMask ); +} + +void River::setBatchSize( U32 size ) +{ + // Not functional + //mSegmentsPerBatch = size; + //_regenerate(); + //setMaskBits( RiverMask | RegenMask ); +} + +void River::regenerate() +{ + _regenerate(); + setMaskBits( RegenMask ); +} + +void River::setMaxDivisionSize( F32 meters ) +{ + if ( meters < mMinDivisionSize ) + mMaxDivisionSize = mMinDivisionSize; + else + mMaxDivisionSize = meters; + + _regenerate(); + setMaskBits( RiverMask | RegenMask ); +} + +//------------------------------------------------------------------------- +// Console Methods +//------------------------------------------------------------------------- + +DefineEngineMethod( River, regenerate, void, (),, + "Intended as a helper to developers and editor scripts.\n" + "Force River to recreate its geometry." + ) +{ + object->regenerate(); +} + +DefineEngineMethod( River, setMetersPerSegment, void, ( F32 meters ),, + "Intended as a helper to developers and editor scripts.\n" + "@see SegmentLength field." + ) +{ + object->setMetersPerSegment( meters ); +} + +DefineEngineMethod( River, setBatchSize, void, ( F32 meters ),, + "Intended as a helper to developers and editor scripts.\n" + "BatchSize is not currently used." + ) +{ + object->setBatchSize( meters ); +} + +DefineEngineMethod( River, setNodeDepth, void, ( S32 idx, F32 meters ),, + "Intended as a helper to developers and editor scripts.\n" + "Sets the depth in meters of a particular node." + ) +{ + object->setNodeDepth( idx, meters ); +} + +DefineEngineMethod( River, setMaxDivisionSize, void, ( F32 meters ),, + "Intended as a helper to developers and editor scripts.\n" + "@see SubdivideLength field." + ) +{ + object->setMaxDivisionSize( meters ); +} diff --git a/Engine/source/environment/river.h b/Engine/source/environment/river.h new file mode 100644 index 000000000..cd6c044a2 --- /dev/null +++ b/Engine/source/environment/river.h @@ -0,0 +1,531 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _RIVER_H_ +#define _RIVER_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _GFXTEXTUREHANDLE_H_ +#include "gfx/gfxTextureHandle.h" +#endif +#ifndef _GFXVERTEXBUFFER_H_ +#include "gfx/gfxVertexBuffer.h" +#endif +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif +#ifndef _CLIPPEDPOLYLIST_H_ +#include "collision/clippedPolyList.h" +#endif +#ifndef _GFXSTATEBLOCK_H_ +#include "gfx/gfxStateBlock.h" +#endif +#ifndef _WATEROBJECT_H_ +#include "waterObject.h" +#endif + + + +//------------------------------------------------------------------------- +// RiverSplineNode Class +//------------------------------------------------------------------------- + +class Path; +class TerrainBlock; +struct ObjectRenderInst; + +class RiverSplineNode +{ +public: + RiverSplineNode() {} + + F32 x; + F32 y; + F32 z; + F32 width; + F32 depth; + VectorF normal; + + RiverSplineNode& operator=(const RiverSplineNode&); + RiverSplineNode operator+(const RiverSplineNode&) const; + RiverSplineNode operator-(const RiverSplineNode&) const; + RiverSplineNode operator*(const F32) const; + + F32 len(); +}; + +inline F32 RiverSplineNode::len() +{ + return mSqrt(F32(x*x + y*y + z*z)); +} + +inline RiverSplineNode& RiverSplineNode::operator=(const RiverSplineNode &_node) +{ + x = _node.x; + y = _node.y; + z = _node.z; + width = _node.width; + depth = _node.depth; + normal = _node.normal; + + return *this; +} + +inline RiverSplineNode RiverSplineNode::operator+(const RiverSplineNode& _add) const +{ + RiverSplineNode result; + result.x = x + _add.x; + result.y = y + _add.y; + result.z = z + _add.z; + result.width = width + _add.width; + result.depth = depth + _add.depth; + result.normal = normal + _add.normal; + + return result; +} + + +inline RiverSplineNode RiverSplineNode::operator-(const RiverSplineNode& _rSub) const +{ + RiverSplineNode result; + result.x = x - _rSub.x; + result.y = y - _rSub.y; + result.z = z - _rSub.z; + result.width = width - _rSub.width; + result.depth = depth - _rSub.depth; + result.normal = normal - _rSub.normal; + + return result; +} + +inline RiverSplineNode operator*(const F32 mul, const RiverSplineNode& multiplicand) +{ + return multiplicand * mul; +} + +inline RiverSplineNode RiverSplineNode::operator*(const F32 _mul) const +{ + RiverSplineNode result; + result.x = x * _mul; + result.y = y * _mul; + result.z = z * _mul; + result.width = width * _mul; + result.depth = depth * _mul; + result.normal = normal * _mul; + + return result; +} + +//------------------------------------------------------------------------- +// Structures +//------------------------------------------------------------------------- + +struct RiverRenderBatch +{ + U32 startSegmentIdx; + U32 endSegmentIdx; + U32 startVert; + U32 endVert; + U32 startIndex; + U32 endIndex; + U32 totalRows; + U32 indexCount; + + U32 vertCount; + U32 triangleCount; +}; + +typedef Vector RiverBatchVector; + +struct RiverNode +{ + // The 3D position of the node + Point3F point; + + // The width of the River at this node (meters) + F32 width; + + // The depth of the River at this node (meters) + F32 depth; + + VectorF normal; +}; + +typedef Vector RiverNodeVector; + +struct RiverSlice +{ + RiverSlice() + { + p0.zero(); + p1.zero(); + p2.zero(); + pb0.zero(); + pb2.zero(); + + uvec.zero(); + fvec.zero(); + rvec.zero(); + + normal.zero(); + + width = 0.0f; + depth = 0.0f; + texCoordV = 0.0f; + + parentNodeIdx = -1; + }; + + Point3F p0; // upper left + Point3F p1; // upper center + Point3F p2; // upper right + + Point3F pb0; // bottom left + Point3F pb2; // bottom right + + VectorF uvec; + VectorF fvec; + VectorF rvec; + + VectorF normal; + + F32 width; + F32 depth; + + F32 texCoordV; + + U32 parentNodeIdx; +}; +typedef Vector RiverSliceVector; + +//------------------------------------------------------------------------- +// RiverSegment Class +//------------------------------------------------------------------------- + +static Point3F sSegmentPointCompareReference; + +class RiverSegment +{ +public: + + RiverSegment(); + RiverSegment( RiverSlice *rs0, RiverSlice *rs1 ); + + void set( RiverSlice *rs0, RiverSlice *rs1 ); + + F32 TexCoordStart() { return slice0->texCoordV; } + F32 TexCoordEnd() { return slice1->texCoordV; } + + Point3F getP00() const { return slice0->p0; } + Point3F getP01() const { return slice1->p0; } + Point3F getP11() const { return slice1->p2; } + Point3F getP10() const { return slice0->p2; } + + // NOTE: + // For purposes of collision testing against a RiverSegment we represent + // it as a set of 6 planes (one for each face) much like a frustum. + // This is actually a flawed representation that will be more incorrect + // the more twist/bend exists between the two RiverSlices making up + // the segment. Basically we treat the four points that make up a "face" + // as if they were coplanar. + + Point3F getSurfaceCenter() const { return (slice0->p1 + slice1->p1) / 2; } + Point3F getSurfaceNormal() const { return ( -mPlanes[4].getNormal() ); } + Point3F getFaceCenter( U32 faceIdx ) const; + + bool intersectBox( const Box3F &bounds ) const; + bool containsPoint( const Point3F &pnt ) const; + F32 distanceToSurface( const Point3F &pnt ) const; + + // Quick access to the segment's points + Point3F& operator[](U32); + const Point3F& operator[](U32) const; + Point3F& operator[](S32 i) { return operator[](U32(i)); } + const Point3F& operator[](S32 i ) const { return operator[](U32(i)); } + + RiverSlice *slice0; + RiverSlice *slice1; + + PlaneF mPlanes[6]; + U32 mPlaneCount; + + U32 columns; + U32 rows; + + U32 startVert; + U32 endVert; + U32 startIndex; + U32 endIndex; + + U32 numVerts; + U32 numTriangles; + + Box3F worldbounds; + +protected: + + PlaneF _getBestPlane( const Point3F *p0, const Point3F *p1, const Point3F *p2, const Point3F *p3 ); +}; +typedef Vector RiverSegmentVector; + +inline Point3F& RiverSegment::operator[](U32 index) +{ + AssertFatal(index < 8, "RiverSegment::operator[] - out of bounds array access!"); + + RiverSlice *slice = NULL; + if ( index > 3 ) + { + slice = slice1; + index -= 4; + } + else + { + slice = slice0; + } + + if ( index == 0 ) + return slice->p0; + + if ( index == 1 ) + return slice->p2; + + if ( index == 2 ) + return slice->pb0; + + else //( index == 3 ) + return slice->pb2; +} + +inline const Point3F& RiverSegment::operator[](U32 index) const +{ + AssertFatal(index < 8, "RiverSegment::operator[] - out of bounds array access!"); + + RiverSlice *slice = NULL; + if ( index > 3 ) + { + slice = slice1; + index -= 4; + } + else + { + slice = slice0; + } + + if ( index == 0 ) + return slice->p0; + + if ( index == 1 ) + return slice->p2; + + if ( index == 2 ) + return slice->pb0; + + else// ( index == 3 ) + return slice->pb2; +} + +//------------------------------------------------------------------------------ +// River Class +//------------------------------------------------------------------------------ + +class ParticleEmitter; +class ParticleEmitterData; +struct RiverNodeList; + +class River : public WaterObject +{ +private: + + friend class GuiRiverEditorCtrl; + friend class GuiRiverEditorUndoAction; + + typedef WaterObject Parent; + +protected: + + enum + { + RiverMask = Parent::NextFreeMask, + NodeMask = Parent::NextFreeMask << 1, + RegenMask = Parent::NextFreeMask << 2, + InitialUpdateMask = Parent::NextFreeMask << 3, + SelectedMask = Parent::NextFreeMask << 4, + MaterialMask = Parent::NextFreeMask << 5, + NextFreeMask = Parent::NextFreeMask << 6, + }; + +public: + + River(); + ~River(); + + DECLARE_CONOBJECT(River); + + // ConObject. + static void initPersistFields(); + static void consoleInit(); + + // SimObject + bool onAdd(); + void onRemove(); + void inspectPostApply(); + void onStaticModified(const char* slotName, const char*newValue = NULL); + void writeFields(Stream &stream, U32 tabStop); + bool writeField( StringTableEntry fieldname, const char *value ); + + // NetObject + U32 packUpdate(NetConnection *, U32, BitStream *); + void unpackUpdate(NetConnection *, BitStream *); + + // SceneObject + virtual void setTransform( const MatrixF &mat ); + virtual void setScale( const VectorF &scale ); + virtual bool castRay(const Point3F &start, const Point3F &end, RayInfo* info); + virtual bool collideBox(const Point3F &start, const Point3F &end, RayInfo* info); + virtual bool containsPoint( const Point3F& point ) const { return containsPoint( point, NULL ); } + + // WaterObject + virtual F32 getWaterCoverage( const Box3F &worldBox ) const; + virtual F32 getSurfaceHeight( const Point2F &pos ) const; + virtual VectorF getFlow( const Point3F &pos ) const; + virtual void onReflectionInfoChanged(); + virtual void updateUnderwaterEffect( SceneRenderState *state ); + + virtual bool isUnderwater( const Point3F &pnt ) const; + F32 distanceToSurface( const Point3F &pnt, U32 segmentIdx ); + bool containsPoint( const Point3F &worldPos, U32 *nodeIdx ) const; + + // Cast a ray against the river -- THE TOP SURFACE ONLY + bool collideRay( const Point3F &origin, const Point3F &direction, U32 *nodeIdx = NULL, Point3F *collisionPnt = NULL ) const; + + void regenerate(); + void setMetersPerSegment( F32 meters ); + void setBatchSize( U32 level ); + void setShowNodes( bool enabled ); + void setMaxDivisionSize( F32 meters ); + void setColumnCount( S32 count ); + + U32 insertNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal, const U32 &idx ); + U32 addNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal ); + void setNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal, const U32 &idx ); + void deleteNode( U32 idx ); + + void buildNodesFromList( RiverNodeList* list ); + + bool getClosestNode( const Point3F &pos, U32 &idx ) const; + + Point3F getNodePosition( U32 idx ) const; + void setNodePosition( U32 idx, const Point3F &pos ); + + MatrixF getNodeTransform( U32 idx ) const; + + F32 getNodeWidth( U32 idx ) const; + void setNodeWidth( U32 idx, F32 width ); + + F32 getNodeDepth( U32 idx ) const; + void setNodeDepth( U32 idx, F32 depth ); + + void setNodeHeight( U32 idx, F32 height ); + + void setNodeNormal( U32 idx, const VectorF &normal ); + VectorF getNodeNormal( U32 idx ) const; + + /// Protected 'Component' Field setter that will add a component to the list. + static bool addNodeFromField( void *object, const char *index, const char *data ); + + static SimSet* getServerSet(); + + static bool smEditorOpen; + static bool smWireframe; + static bool smShowWalls; + static bool smShowNodes; + static bool smShowSpline; + static bool smShowRiver; + static SimObjectPtr smServerRiverSet; + +protected: + + void _loadRenderData(); + bool _setRenderData(); + + void _render( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *matInst ); + + void _makeRenderBatches( const Point3F &cameraPos ); + void _makeHighLODBuffers(); + + U32 _insertNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal, const U32 &idx ); + U32 _addNode( const Point3F &pos, const F32 &width, const F32 &depth, const VectorF &normal ); + + void _regenerate(); + void _generateSlices(); + void _generateSegments(); + void _generateVerts(); + + bool _getTerrainHeight( const Point2F &pos, F32 &height ); + bool _getTerrainHeight( F32 x, F32 y, F32 &height ); + + // WaterObject + virtual void setShaderParams( SceneRenderState *state, BaseMatInstance *mat, const WaterMatParams ¶mHandles ); + virtual void innerRender( SceneRenderState *state ); + virtual void _getWaterPlane( const Point3F &camPos, PlaneF &outPlane, Point3F &outPos ); + +protected: + + RiverSliceVector mSlices; + + RiverNodeVector mNodes; + + RiverSegmentVector mSegments; + + GFXVertexBufferHandle mVB_high; // the high detail vertex buffer + GFXVertexBufferHandle mVB_low; // the low detail vertex buffer + + GFXPrimitiveBufferHandle mPB_high; // the high detail prim buffer + GFXPrimitiveBufferHandle mPB_low; // the low detail prim buffer + + RiverBatchVector mHighLODBatches; + RiverBatchVector mLowLODBatches; + + U32 mLowVertCount; + U32 mHighVertCount; + + U32 mLowTriangleCount; + U32 mHighTriangleCount; + + // Fields. + U32 mSegmentsPerBatch; + F32 mMetersPerSegment; + F32 mDepthScale; + F32 mFlowMagnitude; + F32 mLodDistance; + + // Divide segments that are greater than this length + // into sections of this size, or as close as possible + // while maintaining an equal division size. + F32 mMaxDivisionSize; + F32 mMinDivisionSize; + U32 mColumnCount; +}; + +#endif // _RIVER_H_ \ No newline at end of file diff --git a/Engine/source/environment/scatterSky.cpp b/Engine/source/environment/scatterSky.cpp new file mode 100644 index 000000000..7babcf0d3 --- /dev/null +++ b/Engine/source/environment/scatterSky.cpp @@ -0,0 +1,1407 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "scatterSky.h" + +#include "core/stream/bitStream.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "sim/netConnection.h" +#include "math/util/sphereMesh.h" +#include "math/mathUtils.h" +#include "math/util/matrixSet.h" +#include "scene/sceneRenderState.h" +#include "lighting/lightInfo.h" +#include "gfx/sim/gfxStateBlockData.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/gfxDrawUtil.h" +#include "gfx/sim/cubemapData.h" +#include "materials/shaderData.h" +#include "materials/materialManager.h" +#include "materials/baseMatInstance.h" +#include "materials/sceneData.h" +#include "environment/timeOfDay.h" + + +ConsoleDocClass( ScatterSky, + "@brief Represents both the sun and sky for scenes with a dynamic time of day.\n\n" + + "%ScatterSky renders as a dome shaped mesh which is camera relative and always overhead. " + "It is intended to be part of the background of your scene and renders before all " + "other objects types.\n\n" + + "%ScatterSky is designed for outdoor scenes which need to transition fluidly " + "between radically different times of day. It will respond to time changes " + "originating from a TimeOfDay object or the elevation field can be directly " + "adjusted.\n\n" + + "During day, %ScatterSky uses atmosphereic sunlight scattering " + "aproximations to generate a sky gradient and sun corona. It also calculates " + "the fog color, ambient color, and sun color, which are used for scene " + "lighting. This is user controlled by fields within the ScatterSky group.\n\n" + + "During night, %ScatterSky supports can transition to a night sky cubemap and " + "moon sprite. The user can control this and night time colors used for scene " + "lighting with fields within the Night group.\n\n" + + "A scene with a ScatterSky should not have any other sky or sun objects " + "as it already fulfills both roles.\n\n" + + "%ScatterSky is intended to be used with CloudLayer and TimeOfDay as part of " + "a scene with dynamic lighting. Having a %ScatterSky without a changing " + "time of day would unnecessarily give up artistic control compared and fillrate " + "compared to a SkyBox + Sun setup.\n\n" + + "@ingroup Atmosphere" +); + + +IMPLEMENT_CO_NETOBJECT_V1(ScatterSky); + +const F32 ScatterSky::smEarthRadius = (6378.0f * 1000.0f); +const F32 ScatterSky::smAtmosphereRadius = 200000.0f; +const F32 ScatterSky::smViewerHeight = 1.0f; + +GFXImplementVertexFormat( ScatterSkyVertex ) +{ + addElement( "POSITION", GFXDeclType_Float3 ); + addElement( "NORMAL", GFXDeclType_Float3 ); + addElement( "COLOR", GFXDeclType_Color ); +} + +ScatterSky::ScatterSky() +{ + mPrimCount = 0; + mVertCount = 0; + + + // Rayleigh scattering constant. + mRayleighScattering = 0.0035f; + mRayleighScattering4PI = mRayleighScattering * 4.0f * M_PI_F; + + // Mie scattering constant. + mMieScattering = 0.0045f; + mMieScattering4PI = mMieScattering * 4.0f * M_PI_F; + + // Overall scatter scalar. + mSkyBrightness = 25.0f; + + // The Mie phase asymmetry factor. + mMiePhaseAssymetry = -0.75f; + + mSphereInnerRadius = 1.0f; + mSphereOuterRadius = 1.0f * 1.025f; + mScale = 1.0f / (mSphereOuterRadius - mSphereInnerRadius); + + // 650 nm for red + // 570 nm for green + // 475 nm for blue + mWavelength.set( 0.650f, 0.570f, 0.475f, 0 ); + + mWavelength4[0] = mPow(mWavelength[0], 4.0f); + mWavelength4[1] = mPow(mWavelength[1], 4.0f); + mWavelength4[2] = mPow(mWavelength[2], 4.0f); + + mRayleighScaleDepth = 0.25f; + mMieScaleDepth = 0.1f; + + mAmbientColor.set( 0, 0, 0, 1.0f ); + mAmbientScale.set( 1.0f, 1.0f, 1.0f, 1.0f ); + + mSunColor.set( 0, 0, 0, 1.0f ); + mSunScale = ColorF::WHITE; + + mFogColor.set( 0, 0, 0, 1.0f ); + mFogScale = ColorF::WHITE; + + mExposure = 1.0f; + mNightInterpolant = 0; + + mShader = NULL; + + mTimeOfDay = 0; + + mSunAzimuth = 0.0f; + mSunElevation = 35.0f; + + mMoonAzimuth = 0.0f; + mMoonElevation = 45.0f; + + mBrightness = 1.0f; + + mCastShadows = true; + mDirty = true; + + mLight = LightManager::createLightInfo(); + mLight->setType( LightInfo::Vector ); + + mFlareData = NULL; + mFlareState.clear(); + mFlareScale = 1.0f; + + mMoonEnabled = true; + mMoonScale = 0.2f; + mMoonTint.set( 0.192157f, 0.192157f, 0.192157f, 1.0f ); + MathUtils::getVectorFromAngles( mMoonLightDir, 0.0f, 45.0f ); + mMoonLightDir.normalize(); + mMoonLightDir = -mMoonLightDir; + mNightCubemap = NULL; + mNightColor.set( 0.0196078f, 0.0117647f, 0.109804f, 1.0f ); + mNightFogColor = mNightColor; + mUseNightCubemap = false; + + mMoonMatInst = NULL; + + mNetFlags.set( Ghostable | ScopeAlways ); + mTypeMask |= EnvironmentObjectType | LightObjectType | StaticObjectType; + + _generateSkyPoints(); + + mMatrixSet = reinterpret_cast(dMalloc_aligned(sizeof(MatrixSet), 16)); + constructInPlace(mMatrixSet); +} + +ScatterSky::~ScatterSky() +{ + SAFE_DELETE( mLight ); + SAFE_DELETE( mMoonMatInst ); + + dFree_aligned(mMatrixSet); +} + +bool ScatterSky::onAdd() +{ + PROFILE_SCOPE(ScatterSky_onAdd); + + // onNewDatablock for the server is called here + // for the client it is called in unpackUpdate + if ( !Parent::onAdd() ) + return false; + + if ( isClientObject() ) + TimeOfDay::getTimeOfDayUpdateSignal().notify( this, &ScatterSky::_updateTimeOfDay ); + + setGlobalBounds(); + resetWorldBox(); + + addToScene(); + + if ( isClientObject() ) + { + _initMoon(); + Sim::findObject( mNightCubemapName, mNightCubemap ); + } + + return true; +} + +void ScatterSky::onRemove() +{ + removeFromScene(); + + if ( isClientObject() ) + TimeOfDay::getTimeOfDayUpdateSignal().remove( this, &ScatterSky::_updateTimeOfDay ); + + Parent::onRemove(); +} + +void ScatterSky::_conformLights() +{ + _initCurves(); + + F32 val = mCurves[0].getVal( mTimeOfDay ); + mNightInterpolant = 1.0f - val; + + VectorF lightDirection; + F32 brightness; + + // Build the light direction from the azimuth and elevation. + F32 yaw = mDegToRad(mClampF(mSunAzimuth,0,359)); + F32 pitch = mDegToRad(mClampF(mSunElevation,-360,+360)); + MathUtils::getVectorFromAngles(lightDirection, yaw, pitch); + lightDirection.normalize(); + mSunDir = -lightDirection; + + yaw = mDegToRad(mClampF(mMoonAzimuth,0,359)); + pitch = mDegToRad(mClampF(mMoonElevation,-360,+360)); + MathUtils::getVectorFromAngles( mMoonLightDir, yaw, pitch ); + mMoonLightDir.normalize(); + mMoonLightDir = -mMoonLightDir; + + brightness = mCurves[2].getVal( mTimeOfDay ); + + if ( mNightInterpolant >= 1.0f ) + lightDirection = -mMoonLightDir; + + mLight->setDirection( -lightDirection ); + mLight->setBrightness( brightness * mBrightness ); + mLightDir = lightDirection; + + // Have to do interpolation + // after the light direction is set + // otherwise the sun color will be invalid. + _interpolateColors(); + + mLight->setAmbient( mAmbientColor ); + mLight->setColor( mSunColor ); + mLight->setCastShadows( mCastShadows ); + + FogData fog = getSceneManager()->getFogData(); + fog.color = mFogColor; + getSceneManager()->setFogData( fog ); +} + +void ScatterSky::submitLights( LightManager *lm, bool staticLighting ) +{ + if ( mDirty ) + { + _conformLights(); + mDirty = false; + } + + // The sun is a special light and needs special registration. + lm->setSpecialLight( LightManager::slSunLightType, mLight ); +} + +void ScatterSky::setAzimuth( F32 azimuth ) +{ + mSunAzimuth = azimuth; + mDirty = true; + setMaskBits( TimeMask ); +} + +void ScatterSky::setElevation( F32 elevation ) +{ + mSunElevation = elevation; + + while( elevation < 0 ) + elevation += 360.0f; + + while( elevation >= 360.0f ) + elevation -= 360.0f; + + mTimeOfDay = elevation / 180.0f; + mDirty = true; + setMaskBits( TimeMask ); +} + +void ScatterSky::inspectPostApply() +{ + mDirty = true; + setMaskBits( 0xFFFFFFFF ); +} + +void ScatterSky::initPersistFields() +{ + addGroup( "ScatterSky", + "Only azimuth and elevation are networked fields. To trigger a full update of all other fields use the applyChanges ConsoleMethod." ); + + addField( "skyBrightness", TypeF32, Offset( mSkyBrightness, ScatterSky ), + "Global brightness and intensity applied to the sky and objects in the level." ); + + addField( "mieScattering", TypeF32, Offset( mMieScattering, ScatterSky ), + "Affects the size and intensity of light scattering around the sun." ); + + addField( "rayleighScattering", TypeF32, Offset( mRayleighScattering, ScatterSky ), + "Controls how blue the atmosphere is during the day." ); + + addField( "sunScale", TypeColorF, Offset( mSunScale, ScatterSky ), + "Modulates the directional color of sunlight." ); + + addField( "ambientScale", TypeColorF, Offset( mAmbientScale, ScatterSky ), + "Modulates the ambient color of sunlight." ); + + addField( "fogScale", TypeColorF, Offset( mFogScale, ScatterSky ), + "Modulates the fog color. Note that this overrides the LevelInfo.fogColor " + "property, so you should not use LevelInfo.fogColor if the level contains " + "a ScatterSky object." ); + + addField( "exposure", TypeF32, Offset( mExposure, ScatterSky ), + "Controls the contrast of the sky and sun during daytime." ); + + endGroup( "ScatterSky" ); + + addGroup( "Orbit" ); + + addProtectedField( "azimuth", TypeF32, Offset( mSunAzimuth, ScatterSky ), &ScatterSky::ptSetAzimuth, &defaultProtectedGetFn, + "The horizontal angle of the sun measured clockwise from the positive Y world axis. This field is networked." ); + + addProtectedField( "elevation", TypeF32, Offset( mSunElevation, ScatterSky ), &ScatterSky::ptSetElevation, &defaultProtectedGetFn, + "The elevation angle of the sun above or below the horizon. This field is networked." ); + + addField( "moonAzimuth", TypeF32, Offset( mMoonAzimuth, ScatterSky ), + "The horizontal angle of the moon measured clockwise from the positive Y world axis. This is not animated by time or networked." ); + + addField( "moonElevation", TypeF32, Offset( mMoonElevation, ScatterSky ), + "The elevation angle of the moon above or below the horizon. This is not animated by time or networked." ); + + endGroup( "Orbit" ); + + // We only add the basic lighting options that all lighting + // systems would use... the specific lighting system options + // are injected at runtime by the lighting system itself. + + addGroup( "Lighting" ); + + addField( "castShadows", TypeBool, Offset( mCastShadows, ScatterSky ), + "Enables/disables shadows cast by objects due to ScatterSky light." ); + + addField( "brightness", TypeF32, Offset( mBrightness, ScatterSky ), + "The brightness of the ScatterSky's light object." ); + + endGroup( "Lighting" ); + + addGroup( "Misc" ); + + addField( "flareType", TYPEID< LightFlareData >(), Offset( mFlareData, ScatterSky ), + "Datablock for the flare produced by the ScatterSky." ); + + addField( "flareScale", TypeF32, Offset( mFlareScale, ScatterSky ), + "Changes the size and intensity of the flare." ); + + endGroup( "Misc" ); + + addGroup( "Night" ); + + addField( "nightColor", TypeColorF, Offset( mNightColor, ScatterSky ), + "The ambient color during night. Also used for the sky color if useNightCubemap is false." ); + + addField( "nightFogColor", TypeColorF, Offset( mNightFogColor, ScatterSky ), + "The fog color during night." ); + + addField( "moonEnabled", TypeBool, Offset( mMoonEnabled, ScatterSky ), + "Enable or disable rendering of the moon sprite during night." ); + + addField( "moonMat", TypeMaterialName, Offset( mMoonMatName, ScatterSky ), + "Material for the moon sprite." ); + + addField( "moonScale", TypeF32, Offset( mMoonScale, ScatterSky ), + "Controls size the moon sprite renders, specified as a fractional amount of the screen height." ); + + addField( "moonLightColor", TypeColorF, Offset( mMoonTint, ScatterSky ), + "Color of light cast by the directional light during night." ); + + addField( "useNightCubemap", TypeBool, Offset( mUseNightCubemap, ScatterSky ), + "Transition to the nightCubemap during night. If false we use nightColor." ); + + addField( "nightCubemap", TypeCubemapName, Offset( mNightCubemapName, ScatterSky ), + "Cubemap visible during night." ); + + endGroup( "Night" ); + + // Now inject any light manager specific fields. + LightManager::initLightFields(); + + Parent::initPersistFields(); +} + +U32 ScatterSky::packUpdate(NetConnection *con, U32 mask, BitStream *stream) +{ + U32 retMask = Parent::packUpdate(con, mask, stream); + + if ( stream->writeFlag( mask & TimeMask ) ) + { + stream->write( mSunAzimuth ); + stream->write( mSunElevation ); + } + + if ( stream->writeFlag( mask & UpdateMask ) ) + { + stream->write( mRayleighScattering ); + mRayleighScattering4PI = mRayleighScattering * 4.0f * M_PI_F; + + stream->write( mRayleighScattering4PI ); + + stream->write( mMieScattering ); + mMieScattering4PI = mMieScattering * 4.0f * M_PI_F; + + stream->write( mMieScattering4PI ); + + stream->write( mSkyBrightness ); + + stream->write( mMiePhaseAssymetry ); + + stream->write( mSphereInnerRadius ); + stream->write( mSphereOuterRadius ); + + stream->write( mScale ); + + stream->write( mWavelength ); + + stream->write( mWavelength4[0] ); + stream->write( mWavelength4[1] ); + stream->write( mWavelength4[2] ); + + stream->write( mRayleighScaleDepth ); + stream->write( mMieScaleDepth ); + + stream->write( mNightColor ); + stream->write( mNightFogColor ); + stream->write( mAmbientScale ); + stream->write( mSunScale ); + stream->write( mFogScale ); + + stream->write( mExposure ); + + stream->write( mBrightness ); + + stream->writeFlag( mCastShadows ); + + stream->write( mFlareScale ); + + if ( stream->writeFlag( mFlareData ) ) + { + stream->writeRangedU32( mFlareData->getId(), + DataBlockObjectIdFirst, + DataBlockObjectIdLast ); + } + + stream->writeFlag( mMoonEnabled ); + stream->write( mMoonMatName ); + stream->write( mMoonScale ); + stream->write( mMoonTint ); + stream->writeFlag( mUseNightCubemap ); + stream->write( mNightCubemapName ); + + stream->write( mMoonAzimuth ); + stream->write( mMoonElevation ); + + mLight->packExtended( stream ); + } + + return retMask; +} + +void ScatterSky::unpackUpdate(NetConnection *con, BitStream *stream) +{ + Parent::unpackUpdate(con, stream); + + if ( stream->readFlag() ) // TimeMask + { + F32 temp = 0; + stream->read( &temp ); + setAzimuth( temp ); + + stream->read( &temp ); + setElevation( temp ); + } + + if ( stream->readFlag() ) // UpdateMask + { + stream->read( &mRayleighScattering ); + stream->read( &mRayleighScattering4PI ); + + stream->read( &mMieScattering ); + stream->read( &mMieScattering4PI ); + + stream->read( &mSkyBrightness ); + + stream->read( &mMiePhaseAssymetry ); + + stream->read( &mSphereInnerRadius ); + stream->read( &mSphereOuterRadius ); + + stream->read( &mScale ); + + ColorF tmpColor( 0, 0, 0 ); + + stream->read( &tmpColor ); + + stream->read( &mWavelength4[0] ); + stream->read( &mWavelength4[1] ); + stream->read( &mWavelength4[2] ); + + stream->read( &mRayleighScaleDepth ); + stream->read( &mMieScaleDepth ); + + stream->read( &mNightColor ); + stream->read( &mNightFogColor ); + stream->read( &mAmbientScale ); + stream->read( &mSunScale ); + stream->read( &mFogScale ); + + if ( tmpColor != mWavelength ) + { + mWavelength = tmpColor; + mWavelength4[0] = mPow(mWavelength[0], 4.0f); + mWavelength4[1] = mPow(mWavelength[1], 4.0f); + mWavelength4[2] = mPow(mWavelength[2], 4.0f); + } + + stream->read( &mExposure ); + + stream->read( &mBrightness ); + + mCastShadows = stream->readFlag(); + + stream->read( &mFlareScale ); + + if ( stream->readFlag() ) + { + SimObjectId id = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); + LightFlareData *datablock = NULL; + + if ( Sim::findObject( id, datablock ) ) + mFlareData = datablock; + else + { + con->setLastError( "ScatterSky::unpackUpdate() - invalid LightFlareData!" ); + mFlareData = NULL; + } + } + else + mFlareData = NULL; + + mMoonEnabled = stream->readFlag(); + stream->read( &mMoonMatName ); + stream->read( &mMoonScale ); + stream->read( &mMoonTint ); + mUseNightCubemap = stream->readFlag(); + stream->read( &mNightCubemapName ); + + stream->read( &mMoonAzimuth ); + stream->read( &mMoonElevation ); + + mLight->unpackExtended( stream ); + + if ( isProperlyAdded() ) + { + mDirty = true; + _initMoon(); + Sim::findObject( mNightCubemapName, mNightCubemap ); + } + } +} + +void ScatterSky::prepRenderImage( SceneRenderState *state ) +{ + // Only render into diffuse and reflect passes. + + if( !state->isDiffusePass() && + !state->isReflectPass() ) + return; + + // Regular sky render instance. + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &ScatterSky::_render ); + ri->type = RenderPassManager::RIT_Sky; + ri->defaultKey = 10; + ri->defaultKey2 = 0; + state->getRenderPass()->addInst( ri ); + + // Debug render instance. + /* + if ( Con::getBoolVariable( "$ScatterSky::debug", false ) ) + { + ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &ScatterSky::_debugRender ); + ri->type = RenderPassManager::RIT_Editor; + state->getRenderPass()->addInst( ri ); + } + */ + + // Light flare effect render instance. + if ( mFlareData && mNightInterpolant != 1.0f ) + { + mFlareState.fullBrightness = mBrightness; + mFlareState.scale = mFlareScale; + mFlareState.lightInfo = mLight; + + Point3F lightPos = state->getCameraPosition() - state->getFarPlane() * mLight->getDirection() * 0.9f; + mFlareState.lightMat.identity(); + mFlareState.lightMat.setPosition( lightPos ); + + mFlareData->prepRender( state, &mFlareState ); + } + + // Render instances for Night effects. + if ( mNightInterpolant <= 0.0f ) + return; + + // Render instance for Moon sprite. + if ( mMoonEnabled && mMoonMatInst ) + { + mMatrixSet->setSceneView(GFX->getWorldMatrix()); + mMatrixSet->setSceneProjection(GFX->getProjectionMatrix()); + mMatrixSet->setWorld(GFX->getWorldMatrix()); + + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &ScatterSky::_renderMoon ); + ri->type = RenderPassManager::RIT_Sky; + // Render after sky objects and before CloudLayer! + ri->defaultKey = 5; + ri->defaultKey2 = 0; + state->getRenderPass()->addInst( ri ); + } +} + +bool ScatterSky::_initShader() +{ + ShaderData *shaderData; + if ( !Sim::findObject( "ScatterSkyShaderData", shaderData ) ) + { + Con::warnf( "ScatterSky::_initShader - failed to locate shader ScatterSkyShaderData!" ); + return false; + } + + mShader = shaderData->getShader(); + if ( !mShader ) + return false; + + if ( mStateBlock.isNull() ) + { + GFXStateBlockData *data = NULL; + if ( !Sim::findObject( "ScatterSkySBData", data ) ) + Con::warnf( "ScatterSky::_initShader - failed to locate ScatterSkySBData!" ); + else + mStateBlock = GFX->createStateBlock( data->getState() ); + } + + if ( !mStateBlock ) + return false; + + mShaderConsts = mShader->allocConstBuffer(); + mModelViewProjSC = mShader->getShaderConstHandle( "$modelView" ); + + // Camera height, cam height squared, scale and scale over depth. + mMiscSC = mShader->getShaderConstHandle( "$misc" ); + + // Inner and out radius, and inner and outer radius squared. + mSphereRadiiSC = mShader->getShaderConstHandle( "$sphereRadii" ); + + // Rayleigh sun brightness, mie sun brightness and 4 * PI * coefficients. + mScatteringCoefficientsSC = mShader->getShaderConstHandle( "$scatteringCoeffs" ); + mCamPosSC = mShader->getShaderConstHandle( "$camPos" ); + mLightDirSC = mShader->getShaderConstHandle( "$lightDir" ); + mSunDirSC = mShader->getShaderConstHandle( "$sunDir" ); + mNightColorSC = mShader->getShaderConstHandle( "$nightColor" ); + mInverseWavelengthSC = mShader->getShaderConstHandle( "$invWaveLength" ); + mNightInterpolantAndExposureSC = mShader->getShaderConstHandle( "$nightInterpAndExposure" ); + mUseCubemapSC = mShader->getShaderConstHandle( "$useCubemap" ); + + return true; +} + +void ScatterSky::_initVBIB() +{ + // Vertex Buffer... + U32 vertStride = 50; + U32 strideMinusOne = vertStride - 1; + mVertCount = vertStride * vertStride; + mPrimCount = strideMinusOne * strideMinusOne * 2; + + Point3F vertScale( 16.0f, 16.0f, 4.0f ); + + F32 zOffset = -( mCos( mSqrt( 1.0f ) ) + 0.01f ); + + mVB.set( GFX, mVertCount, GFXBufferTypeStatic ); + ScatterSkyVertex *pVert = mVB.lock(); + + for ( U32 y = 0; y < vertStride; y++ ) + { + F32 v = ( (F32)y / (F32)strideMinusOne - 0.5f ) * 2.0f; + + for ( U32 x = 0; x < vertStride; x++ ) + { + F32 u = ( (F32)x / (F32)strideMinusOne - 0.5f ) * 2.0f; + + F32 sx = u; + F32 sy = v; + F32 sz = (mCos( mSqrt( sx*sx + sy*sy ) ) * 1.0f) + zOffset; + //F32 sz = 1.0f; + pVert->point.set( sx, sy, sz ); + pVert->point *= vertScale; + + pVert->point.normalize(); + pVert->point *= 200000.0f; + + pVert++; + } + } + + mVB.unlock(); + + // Primitive Buffer... + mPrimBuffer.set( GFX, mPrimCount * 3, mPrimCount, GFXBufferTypeStatic ); + + U16 *pIdx = NULL; + mPrimBuffer.lock(&pIdx); + U32 curIdx = 0; + + for ( U32 y = 0; y < strideMinusOne; y++ ) + { + for ( U32 x = 0; x < strideMinusOne; x++ ) + { + U32 offset = x + y * vertStride; + + pIdx[curIdx] = offset; + curIdx++; + pIdx[curIdx] = offset + 1; + curIdx++; + pIdx[curIdx] = offset + vertStride + 1; + curIdx++; + + pIdx[curIdx] = offset; + curIdx++; + pIdx[curIdx] = offset + vertStride + 1; + curIdx++; + pIdx[curIdx] = offset + vertStride; + curIdx++; + } + } + + mPrimBuffer.unlock(); +} + +void ScatterSky::_initMoon() +{ + if ( isServerObject() ) + return; + + if ( mMoonMatInst ) + SAFE_DELETE( mMoonMatInst ); + + if ( mMoonMatName.isNotEmpty() ) + mMoonMatInst = MATMGR->createMatInstance( mMoonMatName, MATMGR->getDefaultFeatures(), getGFXVertexFormat() ); +} + +void ScatterSky::_initCurves() +{ + if ( mCurves->getSampleCount() > 0 ) + return; + + // Takes time of day (0-2) and returns + // the night interpolant (0-1) day/night factor. + mCurves[0].clear(); + mCurves[0].addPoint( 0.0f, 0.5f ); + mCurves[0].addPoint( 0.1f, 1.0f ); + mCurves[0].addPoint( 0.9f, 1.0f ); + mCurves[0].addPoint( 1.0f, 0.5f ); + mCurves[0].addPoint( 1.1f, 0.0f ); + mCurves[0].addPoint( 1.9f, 0.0f ); + mCurves[0].addPoint( 2.0f, 0.5f ); + //mCurves[0].addPoint( 0.0f, 0.25f ); + //mCurves[0].addPoint( 0.05f, 0.5f ); + //mCurves[0].addPoint( 0.1f, 1.0f ); + //mCurves[0].addPoint( 0.6f, 1.0f ); + //mCurves[0].addPoint( 0.98f, 0.895f ); + //mCurves[0].addPoint( 1.0f, 0.15f ); + //mCurves[0].addPoint( 1.15f, 0.0f ); + //mCurves[0].addPoint( 1.9f, 0.0f ); + //mCurves[0].addPoint( 2.0f, 0.15f ); + + + // Takes time of day (0-2) and returns + // the moon light brightness. + mCurves[1].clear(); + mCurves[1].addPoint( 0.0f, 0.0f ); + mCurves[1].addPoint( 1.0f, 0.0f ); + mCurves[1].addPoint( 1.1f, 0.0f ); + mCurves[1].addPoint( 1.2f, 0.5f ); + mCurves[1].addPoint( 1.3f, 1.0f ); + mCurves[1].addPoint( 1.8f, 0.5f ); + mCurves[1].addPoint( 1.9f, 0.0f ); + mCurves[1].addPoint( 2.0f, 0.0f ); + + // Takes time of day and returns brightness + mCurves[2].clear(); + mCurves[2].addPoint( 0.0f, 0.4f ); + mCurves[2].addPoint( 0.25f, 1.0f ); + mCurves[2].addPoint( 0.5f, 1.0f ); + mCurves[2].addPoint( 0.75f, 0.9f ); + mCurves[2].addPoint( 1.0f, 0.3f ); + mCurves[2].addPoint( 1.02877f, 0.0f ); + mCurves[2].addPoint( 1.05f, 0.0f ); + mCurves[2].addPoint( 1.15f, 0.0f ); + mCurves[2].addPoint( 1.2f, 0.0f ); + mCurves[2].addPoint( 1.3f, 0.3f ); + mCurves[2].addPoint( 1.85f, 0.4f ); + mCurves[2].addPoint( 1.9f, 0.0f ); + mCurves[2].addPoint( 2.0f, 0.0f ); + + + mCurves[3].clear(); + mCurves[3].addPoint( 0.0f, 0.01f ); + mCurves[3].addPoint( 0.05f, 0.0f ); + mCurves[3].addPoint( 0.1f, 0.0f ); + mCurves[3].addPoint( 0.6f, 0.0f ); + mCurves[3].addPoint( 0.98f, 0.75f ); + mCurves[3].addPoint( 1.0f, 1.0f ); + mCurves[3].addPoint( 1.02877f, 1.0f ); + mCurves[3].addPoint( 1.05f, 1.0f ); + mCurves[3].addPoint( 1.2f, 1.0f ); + mCurves[3].addPoint( 1.3f, 1.0f ); + mCurves[3].addPoint( 1.85f, 1.0f ); + mCurves[3].addPoint( 1.9f, 1.0f ); + mCurves[3].addPoint( 2.0f, 1.0f ); +} + +void ScatterSky::_updateTimeOfDay( TimeOfDay *timeOfDay, F32 time ) +{ + setElevation( timeOfDay->getElevationDegrees() ); + setAzimuth( timeOfDay->getAzimuthDegrees() ); +} + +void ScatterSky::_render( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ) +{ + if ( overrideMat || (!mShader && !_initShader()) ) + return; + + GFXTransformSaver saver; + + //mLightDir = -mLight->getDirection(); + //mLightDir.normalize(); + + if ( mVB.isNull() || mPrimBuffer.isNull() ) + _initVBIB(); + + GFX->setShader( mShader ); + GFX->setShaderConstBuffer( mShaderConsts ); + + Point4F sphereRadii( mSphereOuterRadius, mSphereOuterRadius * mSphereOuterRadius, + mSphereInnerRadius, mSphereInnerRadius * mSphereInnerRadius ); + + Point4F scatteringCoeffs( mRayleighScattering * mSkyBrightness, mRayleighScattering4PI, + mMieScattering * mSkyBrightness, mMieScattering4PI ); + + Point4F invWavelength( 1.0f / mWavelength4[0], + 1.0f / mWavelength4[1], + 1.0f / mWavelength4[2], 1.0f ); + + Point3F camPos( 0, 0, smViewerHeight ); + Point4F miscParams( camPos.z, camPos.z * camPos.z, mScale, mScale / mRayleighScaleDepth ); + + Frustum frust = state->getFrustum(); + frust.setFarDist( smEarthRadius + smAtmosphereRadius ); + MatrixF proj( true ); + frust.getProjectionMatrix( &proj ); + + //MatrixF camMat = state->getCameraTransform(); + //camMat.inverse(); + //MatrixF tmp( true ); + //tmp = camMat; + //tmp.setPosition( Point3F( 0, 0, 0 ) ); + + //proj.mul( tmp ); + + Point3F camPos2 = state->getCameraPosition(); + MatrixF xfm(true); + xfm.setPosition(camPos2);//-Point3F( 0, 0, 200000.0f)); + GFX->multWorld(xfm); + MatrixF xform(proj);//GFX->getProjectionMatrix()); + xform *= GFX->getViewMatrix(); + xform *= GFX->getWorldMatrix(); + + mShaderConsts->setSafe( mModelViewProjSC, xform ); + mShaderConsts->setSafe( mMiscSC, miscParams ); + mShaderConsts->setSafe( mSphereRadiiSC, sphereRadii ); + mShaderConsts->setSafe( mScatteringCoefficientsSC, scatteringCoeffs ); + mShaderConsts->setSafe( mCamPosSC, camPos ); + mShaderConsts->setSafe( mLightDirSC, mLightDir ); + mShaderConsts->setSafe( mSunDirSC, mSunDir ); + mShaderConsts->setSafe( mNightColorSC, mNightColor ); + mShaderConsts->setSafe( mInverseWavelengthSC, invWavelength ); + mShaderConsts->setSafe( mNightInterpolantAndExposureSC, Point2F( mExposure, mNightInterpolant ) ); + + if ( GFXDevice::getWireframe() ) + { + GFXStateBlockDesc desc( mStateBlock->getDesc() ); + desc.setFillModeWireframe(); + GFX->setStateBlockByDesc( desc ); + } + else + GFX->setStateBlock( mStateBlock ); + + if ( mUseNightCubemap && mNightCubemap ) + { + mShaderConsts->setSafe( mUseCubemapSC, 1.0f ); + + if ( !mNightCubemap->mCubemap ) + mNightCubemap->createMap(); + + GFX->setCubeTexture( 0, mNightCubemap->mCubemap ); + } + else + { + GFX->setCubeTexture( 0, NULL ); + mShaderConsts->setSafe( mUseCubemapSC, 0.0f ); + } + + GFX->setPrimitiveBuffer( mPrimBuffer ); + GFX->setVertexBuffer( mVB ); + + GFX->drawIndexedPrimitive( GFXTriangleList, 0, 0, mVertCount, 0, mPrimCount ); +} + +void ScatterSky::_debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ) +{ + GFXStateBlockDesc desc; + desc.fillMode = GFXFillSolid; + desc.setBlend( false, GFXBlendOne, GFXBlendZero ); + desc.setZReadWrite( false, false ); + GFXStateBlockRef sb = GFX->GFX->createStateBlock( desc ); + + GFX->setStateBlock( sb ); + + PrimBuild::begin( GFXLineStrip, mSkyPoints.size() ); + PrimBuild::color3i( 255, 0, 255 ); + + for ( U32 i = 0; i < mSkyPoints.size(); i++ ) + { + Point3F pnt = mSkyPoints[i]; + pnt.normalize(); + pnt *= 500; + pnt += state->getCameraPosition(); + PrimBuild::vertex3fv( pnt ); + } + + PrimBuild::end(); +} + +void ScatterSky::_renderMoon( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ) +{ + if ( !mMoonMatInst ) + return; + + Point3F moonlightPosition = state->getCameraPosition() - /*mLight->getDirection()*/ mMoonLightDir * state->getFarPlane() * 0.9f; + F32 dist = (moonlightPosition - state->getCameraPosition()).len(); + + // worldRadius = screenRadius * dist / worldToScreen + // screenRadius = worldRadius / dist * worldToScreen + + // + F32 screenRadius = GFX->getViewport().extent.y * mMoonScale * 0.5f; + F32 worldRadius = screenRadius * dist / state->getWorldToScreenScale().y; + + // Calculate Billboard Radius (in world units) to be constant, independent of distance. + // Takes into account distance, viewport size, and specified size in editor + + F32 BBRadius = worldRadius; + + + mMatrixSet->restoreSceneViewProjection(); + + if ( state->isReflectPass() ) + mMatrixSet->setProjection( state->getSceneManager()->getNonClipProjection() ); + + mMatrixSet->setWorld( MatrixF::Identity ); + + // Initialize points with basic info + Point3F points[4]; + points[0] = Point3F(-BBRadius, 0.0, -BBRadius); + points[1] = Point3F( -BBRadius, 0.0, BBRadius); + points[2] = Point3F( BBRadius, 0.0, BBRadius); + points[3] = Point3F( BBRadius, 0.0, -BBRadius); + + static const Point2F sCoords[4] = + { + Point2F( 0.0f, 0.0f ), + Point2F( 0.0f, 1.0f ), + Point2F( 1.0f, 1.0f ), + Point2F( 1.0f, 0.0f ) + }; + + // Get info we need to adjust points + const MatrixF &camView = state->getCameraTransform(); + + // Finalize points + for(int i = 0; i < 4; i++) + { + // align with camera + camView.mulV(points[i]); + // offset + points[i] += moonlightPosition; + } + + // Vertex color. + ColorF moonVertColor( 1.0f, 1.0f, 1.0f, mNightInterpolant ); + + // Copy points to buffer. + + GFXVertexBufferHandle< GFXVertexPCT > vb; + vb.set( GFX, 4, GFXBufferTypeVolatile ); + GFXVertexPCT *pVert = vb.lock(); + + for ( S32 i = 0; i < 4; i++ ) + { + pVert->color.set( moonVertColor ); + pVert->point.set( points[i] ); + pVert->texCoord.set( sCoords[i].x, sCoords[i].y ); + pVert++; + } + + vb.unlock(); + + // Setup SceneData struct. + + SceneData sgData; + sgData.wireframe = GFXDevice::getWireframe(); + sgData.visibility = 1.0f; + + // Draw it + + while ( mMoonMatInst->setupPass( state, sgData ) ) + { + mMoonMatInst->setTransforms( *mMatrixSet, state ); + mMoonMatInst->setSceneInfo( state, sgData ); + + GFX->setVertexBuffer( vb ); + GFX->drawPrimitive( GFXTriangleFan, 0, 2 ); + } +} + +void ScatterSky::_generateSkyPoints() +{ + U32 rings=60, segments=20;//rings=160, segments=20; + + Point3F tmpPoint( 0, 0, 0 ); + + // Establish constants used in sphere generation. + F32 deltaRingAngle = ( M_PI_F / (F32)(rings * 2) ); + F32 deltaSegAngle = ( 2.0f * M_PI_F / (F32)segments ); + + // Generate the group of rings for the sphere. + for( int ring = 0; ring < 2; ring++ ) + { + F32 r0 = mSin( ring * deltaRingAngle ); + F32 y0 = mCos( ring * deltaRingAngle ); + + // Generate the group of segments for the current ring. + for( int seg = 0; seg < segments + 1 ; seg++ ) + { + F32 x0 = r0 * sinf( seg * deltaSegAngle ); + F32 z0 = r0 * cosf( seg * deltaSegAngle ); + + tmpPoint.set( x0, z0, y0 ); + tmpPoint.normalizeSafe(); + + tmpPoint.x *= smEarthRadius + smAtmosphereRadius; + tmpPoint.y *= smEarthRadius + smAtmosphereRadius; + tmpPoint.z *= smEarthRadius + smAtmosphereRadius; + tmpPoint.z -= smEarthRadius; + + if ( ring == 1 ) + mSkyPoints.push_back( tmpPoint ); + } + } +} + +void ScatterSky::_interpolateColors() +{ + mFogColor.set( 0, 0, 0, 0 ); + mAmbientColor.set( 0, 0, 0, 0 ); + mSunColor.set( 0, 0, 0, 0 ); + + _getFogColor( &mFogColor ); + _getAmbientColor( &mAmbientColor ); + _getSunColor( &mSunColor ); + + mAmbientColor *= mAmbientScale; + mSunColor *= mSunScale; + mFogColor *= mFogScale; + + mFogColor.interpolate( mFogColor, mNightFogColor, mCurves[3].getVal( mTimeOfDay ) );//mNightInterpolant ); + mFogColor.alpha = 1.0f; + + mAmbientColor.interpolate( mAmbientColor, mNightColor, mCurves[3].getVal( mTimeOfDay ) );//mNightInterpolant ); + mSunColor.interpolate( mSunColor, mMoonTint, mCurves[3].getVal( mTimeOfDay ) );//mNightInterpolant ); +} + +void ScatterSky::_getSunColor( ColorF *outColor ) +{ + PROFILE_SCOPE( ScatterSky_GetSunColor ); + + U32 count = 0; + ColorF tmpColor( 0, 0, 0 ); + VectorF tmpVec( 0, 0, 0 ); + + tmpVec = mLightDir; + tmpVec.x *= smEarthRadius + smAtmosphereRadius; + tmpVec.y *= smEarthRadius + smAtmosphereRadius; + tmpVec.z *= smEarthRadius + smAtmosphereRadius; + tmpVec.z -= smAtmosphereRadius; + + for ( U32 i = 0; i < 10; i++ ) + { + _getColor( tmpVec, &tmpColor ); + (*outColor) += tmpColor; + tmpVec.x += (smEarthRadius * 0.5f) + (smAtmosphereRadius * 0.5f); + count++; + } + + if ( count > 0 ) + (*outColor) /= count; +} + +void ScatterSky::_getAmbientColor( ColorF *outColor ) +{ + PROFILE_SCOPE( ScatterSky_GetAmbientColor ); + + ColorF tmpColor( 0, 0, 0, 0 ); + U32 count = 0; + + // Disable mieScattering for purposes of calculating the ambient color. + F32 oldMieScattering = mMieScattering; + mMieScattering = 0.0f; + + for ( U32 i = 0; i < mSkyPoints.size(); i++ ) + { + Point3F pnt( mSkyPoints[i] ); + + _getColor( pnt, &tmpColor ); + (*outColor) += tmpColor; + count++; + } + + if ( count > 0 ) + (*outColor) /= count; + //Point3F pColor( outColor->red, outColor->green, outColor->blue ); + //F32 len = pColor.len(); + //if ( len > 0 ) + // (*outColor) /= len; + + mMieScattering = oldMieScattering; +} + +void ScatterSky::_getFogColor( ColorF *outColor ) +{ + PROFILE_SCOPE( ScatterSky_GetFogColor ); + + VectorF scatterPos( 0, 0, 0 ); + + F32 sunBrightness = mSkyBrightness; + mSkyBrightness *= 0.25f; + + F32 yaw = 0, pitch = 0, originalYaw = 0; + VectorF fwd( 0, 1.0f, 0 ); + MathUtils::getAnglesFromVector( fwd, yaw, pitch ); + originalYaw = yaw; + pitch = mDegToRad( 10.0f ); + + ColorF tmpColor( 0, 0, 0 ); + + U32 i = 0; + for ( i = 0; i < 10; i++ ) + { + MathUtils::getVectorFromAngles( scatterPos, yaw, pitch ); + + scatterPos.x *= smEarthRadius + smAtmosphereRadius; + scatterPos.y *= smEarthRadius + smAtmosphereRadius; + scatterPos.z *= smEarthRadius + smAtmosphereRadius; + scatterPos.y -= smEarthRadius; + + _getColor( scatterPos, &tmpColor ); + (*outColor) += tmpColor; + + if ( i <= 5 ) + yaw += mDegToRad( 5.0f ); + else + { + originalYaw += mDegToRad( -5.0f ); + yaw = originalYaw; + } + + yaw = mFmod( yaw, M_2PI_F ); + } + + if ( i > 0 ) + (*outColor) /= i; + + mSkyBrightness = sunBrightness; +} + +F32 ScatterSky::_vernierScale( F32 fCos ) +{ + /* + F32 x5 = x * 5.25; + F32 x5p6 = (-6.80 + x5); + F32 xnew = (3.83 + x * x5p6); + F32 xfinal = (0.459 + x * xnew); + F32 xfinal2 = -0.00287 + x * xfinal; + F32 outx = mExp( xfinal2 ); + return 0.25 * outx;*/ + F32 x = 1.0 - fCos; + return 0.25f * exp( -0.00287f + x * (0.459f + x * (3.83f + x * ((-6.80f + (x * 5.25f))))) ); +} + +F32 ScatterSky::_getMiePhase( F32 fCos, F32 fCos2, F32 g, F32 g2) +{ + return 1.5f * ((1.0f - g2) / (2.0f + g2)) * (1.0f + fCos2) / mPow(mFabs(1.0f + g2 - 2.0f*g*fCos), 1.5f); +} + +F32 ScatterSky::_getRayleighPhase( F32 fCos2 ) +{ + return 0.75 + 0.75 * fCos2; +} + +void ScatterSky::_getColor( const Point3F &pos, ColorF *outColor ) +{ + PROFILE_SCOPE( ScatterSky_GetColor ); + + F32 scaleOverScaleDepth = mScale / mRayleighScaleDepth; + F32 rayleighBrightness = mRayleighScattering * mSkyBrightness; + F32 mieBrightness = mMieScattering * mSkyBrightness; + + Point3F invWaveLength( 1.0f / mWavelength4[0], + 1.0f / mWavelength4[1], + 1.0f / mWavelength4[2] ); + + Point3F v3Pos = pos / 6378000.0f; + v3Pos.z += mSphereInnerRadius; + + Point3F newCamPos( 0, 0, smViewerHeight ); + + VectorF v3Ray = v3Pos - newCamPos; + F32 fFar = v3Ray.len(); + v3Ray / fFar; + v3Ray.normalizeSafe(); + + Point3F v3Start = newCamPos; + F32 fDepth = mExp( scaleOverScaleDepth * (mSphereInnerRadius - smViewerHeight ) ); + F32 fStartAngle = mDot( v3Ray, v3Start ); + + F32 fStartOffset = fDepth * _vernierScale( fStartAngle ); + + F32 fSampleLength = fFar / 2.0f; + F32 fScaledLength = fSampleLength * mScale; + VectorF v3SampleRay = v3Ray * fSampleLength; + Point3F v3SamplePoint = v3Start + v3SampleRay * 0.5f; + + Point3F v3FrontColor( 0, 0, 0 ); + for ( U32 i = 0; i < 2; i++ ) + { + F32 fHeight = v3SamplePoint.len(); + F32 fDepth = mExp( scaleOverScaleDepth * (mSphereInnerRadius - smViewerHeight) ); + F32 fLightAngle = mDot( mLightDir, v3SamplePoint ) / fHeight; + F32 fCameraAngle = mDot( v3Ray, v3SamplePoint ) / fHeight; + + F32 fScatter = (fStartOffset + fDepth * ( _vernierScale( fLightAngle ) - _vernierScale( fCameraAngle ) )); + Point3F v3Attenuate( 0, 0, 0 ); + + F32 tmp = mExp( -fScatter * (invWaveLength[0] * mRayleighScattering4PI + mMieScattering4PI) ); + v3Attenuate.x = tmp; + + tmp = mExp( -fScatter * (invWaveLength[1] * mRayleighScattering4PI + mMieScattering4PI) ); + v3Attenuate.y = tmp; + + tmp = mExp( -fScatter * (invWaveLength[2] * mRayleighScattering4PI + mMieScattering4PI) ); + v3Attenuate.z = tmp; + + v3FrontColor += v3Attenuate * (fDepth * fScaledLength); + v3SamplePoint += v3SampleRay; + } + + Point3F mieColor = v3FrontColor * mieBrightness; + Point3F rayleighColor = v3FrontColor * (invWaveLength * rayleighBrightness); + Point3F v3Direction = newCamPos - v3Pos; + v3Direction.normalize(); + + F32 fCos = mDot( mLightDir, v3Direction ) / v3Direction.len(); + F32 fCos2 = fCos * fCos; + + F32 g = -0.991f; + F32 g2 = g * g; + F32 miePhase = _getMiePhase( fCos, fCos2, g, g2 ); + //F32 rayleighPhase = _getRayleighPhase( fCos2 ); + + Point3F color = rayleighColor + (miePhase * mieColor); + ColorF tmp( color.x, color.y, color.z, color.y ); + + //if ( !tmp.isValidColor() ) + //{ + // F32 len = color.len(); + // if ( len > 0 ) + // color /= len; + //} + + Point3F expColor( 0, 0, 0 ); + expColor.x = 1.0f - exp(-mExposure * color.x); + expColor.y = 1.0f - exp(-mExposure * color.y); + expColor.z = 1.0f - exp(-mExposure * color.z); + + tmp.set( expColor.x, expColor.y, expColor.z, 1.0f ); + + if ( !tmp.isValidColor() ) + { + F32 len = expColor.len(); + if ( len > 0 ) + expColor /= len; + } + + outColor->set( expColor.x, expColor.y, expColor.z, 1.0f ); +} + +// Static protected field set methods + +bool ScatterSky::ptSetElevation( void *object, const char *index, const char *data ) +{ + ScatterSky *sky = static_cast( object ); + F32 val = dAtof( data ); + + sky->setElevation( val ); + + // we already set the field + return false; +} + +bool ScatterSky::ptSetAzimuth( void *object, const char *index, const char *data ) +{ + ScatterSky *sky = static_cast( object ); + F32 val = dAtof( data ); + + sky->setAzimuth( val ); + + // we already set the field + return false; +} + +void ScatterSky::_onSelected() +{ +#ifdef TORQUE_DEBUG + // Enable debug rendering on the light. + if( isClientObject() ) + mLight->enableDebugRendering( true ); +#endif + + Parent::_onSelected(); +} + +void ScatterSky::_onUnselected() +{ +#ifdef TORQUE_DEBUG + // Disable debug rendering on the light. + if( isClientObject() ) + mLight->enableDebugRendering( false ); +#endif + + Parent::_onUnselected(); +} + +// ConsoleMethods + +DefineEngineMethod( ScatterSky, applyChanges, void, (),, + "Apply a full network update of all fields to all clients." + ) +{ + object->inspectPostApply(); +} \ No newline at end of file diff --git a/Engine/source/environment/scatterSky.h b/Engine/source/environment/scatterSky.h new file mode 100644 index 000000000..c84448449 --- /dev/null +++ b/Engine/source/environment/scatterSky.h @@ -0,0 +1,251 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SCATTERSKY_H_ +#define _SCATTERSKY_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif +#ifndef _GFXVERTEXBUFFER_H_ +#include "gfx/gfxVertexBuffer.h" +#endif +#ifndef _GFXSTATEBLOCK_H_ +#include "gfx/gfxStateBlock.h" +#endif +#ifndef _RENDERPASSMANAGER_H_ +#include "renderInstance/renderPassManager.h" +#endif +#ifndef _PRIMBUILDER_H_ +#include "gfx/primBuilder.h" +#endif +#ifndef _LIGHTINFO_H_ +#include "lighting/lightInfo.h" +#endif +#ifndef _LIGHTFLAREDATA_H_ +#include "T3D/lightFlareData.h" +#endif +#ifndef _TRESPONSECURVE_H_ +#include "math/util/tResponseCurve.h" +#endif + +class LightInfo; +class SphereMesh; +class TimeOfDay; +class CubemapData; +class MatrixSet; + + +GFXDeclareVertexFormat( ScatterSkyVertex ) +{ + // .xyz = coords + Point3F point; + VectorF normal; + ColorF color; +}; + +class ScatterSky : public SceneObject, public ISceneLight +{ + typedef SceneObject Parent; + +public: + + enum MaskBits + { + UpdateMask = Parent::NextFreeMask, + TimeMask = Parent::NextFreeMask << 1, + NextFreeMask = Parent::NextFreeMask << 2 + }; + + ScatterSky(); + ~ScatterSky(); + + // SimObject + bool onAdd(); + void onRemove(); + + // ISceneLight + virtual void submitLights( LightManager *lm, bool staticLighting ); + virtual LightInfo* getLight() { return mLight; } + + // ConsoleObject + DECLARE_CONOBJECT(ScatterSky); + void inspectPostApply(); + static void initPersistFields(); + + // Network + U32 packUpdate ( NetConnection *conn, U32 mask, BitStream *stream ); + void unpackUpdate( NetConnection *conn, BitStream *stream ); + + void prepRenderImage( SceneRenderState* state ); + + /// + void setAzimuth( F32 azimuth ); + /// + void setElevation( F32 elevation ); + + /// + F32 getAzimuth() const { return mSunAzimuth; } + /// + F32 getElevation() const { return mSunElevation; } + +protected: + + void _render( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); + void _debugRender( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); + void _renderMoon( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); + + void _initVBIB(); + bool _initShader(); + void _initMoon(); + void _initCurves(); + + F32 _getRayleighPhase( F32 fCos2 ); + F32 _getMiePhase( F32 fCos, F32 fCos2, F32 g, F32 g2 ); + F32 _vernierScale( F32 fCos ); + + void _generateSkyPoints(); + + void _getColor( const Point3F &pos, ColorF *outColor ); + void _getFogColor( ColorF *outColor ); + void _getAmbientColor( ColorF *outColor ); + void _getSunColor( ColorF *outColor ); + void _interpolateColors(); + + void _conformLights(); + + void _updateTimeOfDay( TimeOfDay *timeofDay, F32 time ); + + // static protected field set methods + static bool ptSetElevation( void *object, const char *index, const char *data ); + static bool ptSetAzimuth( void *object, const char *index, const char *data ); + + // SimObject. + virtual void _onSelected(); + virtual void _onUnselected(); + +protected: + + static const F32 smEarthRadius; + static const F32 smAtmosphereRadius; + static const F32 smViewerHeight; + +#define CURVE_COUNT 4 + + FloatCurve mCurves[CURVE_COUNT]; + + U32 mVertCount; + U32 mPrimCount; + + F32 mRayleighScattering; + F32 mRayleighScattering4PI; + + F32 mMieScattering; + F32 mMieScattering4PI; + + F32 mSkyBrightness; + F32 mMiePhaseAssymetry; + + F32 mOuterRadius; + F32 mScale; + ColorF mWavelength; + F32 mWavelength4[3]; + F32 mRayleighScaleDepth; + F32 mMieScaleDepth; + + F32 mSphereInnerRadius; + F32 mSphereOuterRadius; + + F32 mExposure; + F32 mNightInterpolant; + + VectorF mLightDir; + VectorF mSunDir; + + F32 mSunAzimuth; + F32 mSunElevation; + F32 mMoonAzimuth; + F32 mMoonElevation; + + F32 mTimeOfDay; + + F32 mBrightness; + + ColorF mNightColor; + ColorF mNightFogColor; + + ColorF mAmbientColor; ///< Not a field + ColorF mSunColor; ///< Not a field + ColorF mFogColor; ///< Not a field + + ColorF mAmbientScale; + ColorF mSunScale; + ColorF mFogScale; + + LightInfo *mLight; + + bool mCastShadows; + bool mDirty; + + LightFlareData *mFlareData; + LightFlareState mFlareState; + F32 mFlareScale; + + bool mMoonEnabled; + String mMoonMatName; + BaseMatInstance *mMoonMatInst; + F32 mMoonScale; + ColorF mMoonTint; + VectorF mMoonLightDir; + CubemapData *mNightCubemap; + String mNightCubemapName; + bool mUseNightCubemap; + MatrixSet *mMatrixSet; + + Vector mSkyPoints; + + // Prim buffer, vertex buffer and shader for rendering. + GFXPrimitiveBufferHandle mPrimBuffer; + GFXVertexBufferHandle mVB; + GFXShaderRef mShader; + + GFXStateBlockRef mStateBlock; + + // Shared shader constant blocks + GFXShaderConstBufferRef mShaderConsts; + GFXShaderConstHandle *mModelViewProjSC; + GFXShaderConstHandle *mMiscSC; // Camera height, cam height squared, scale and scale over depth. + GFXShaderConstHandle *mSphereRadiiSC; // Inner and out radius, and inner and outer radius squared. + GFXShaderConstHandle *mScatteringCoefficientsSC; // Rayleigh sun brightness, mie sun brightness and 4 * PI * coefficients. + GFXShaderConstHandle *mCamPosSC; + GFXShaderConstHandle *mLightDirSC; + GFXShaderConstHandle *mSunDirSC; + GFXShaderConstHandle *mNightColorSC; + GFXShaderConstHandle *mInverseWavelengthSC; + GFXShaderConstHandle *mNightInterpolantAndExposureSC; + GFXShaderConstHandle *mUseCubemapSC; +}; + +#endif // _SCATTERSKY_H_ diff --git a/Engine/source/environment/skyBox.cpp b/Engine/source/environment/skyBox.cpp new file mode 100644 index 000000000..f2154b395 --- /dev/null +++ b/Engine/source/environment/skyBox.cpp @@ -0,0 +1,637 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "environment/skyBox.h" + +#include "console/consoleTypes.h" +#include "scene/sceneRenderState.h" +#include "renderInstance/renderPassManager.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxTransformSaver.h" +#include "core/stream/fileStream.h" +#include "core/stream/bitStream.h" +#include "materials/materialManager.h" +#include "materials/materialFeatureTypes.h" +#include "materials/sceneData.h" +#include "T3D/gameFunctions.h" +#include "renderInstance/renderBinManager.h" +#include "materials/processedMaterial.h" +#include "gfx/gfxDebugEvent.h" +#include "math/util/matrixSet.h" + + +IMPLEMENT_CO_NETOBJECT_V1( SkyBox ); + +ConsoleDocClass( SkyBox, + "@brief Represents the sky with an artist-created cubemap.\n\n" + + "SkyBox is not a directional light and should be used in conjunction with a Sun object.\n\n" + + "@ingroup Atmosphere" +); + +SkyBox::SkyBox() +{ + mTypeMask |= EnvironmentObjectType | StaticObjectType; + mNetFlags.set(Ghostable | ScopeAlways); + + mMatName = ""; + mMatInstance = NULL; + + mIsVBDirty = false; + mDrawBottom = true; + mPrimCount = 0; + mFogBandHeight = 0; + + mMatrixSet = reinterpret_cast(dMalloc_aligned(sizeof(MatrixSet), 16)); + constructInPlace(mMatrixSet); + + mFogBandMat = NULL; + mFogBandMatInst = NULL; +} + +SkyBox::~SkyBox() +{ + dFree_aligned(mMatrixSet); + + if( mMatInstance ) + SAFE_DELETE( mMatInstance ); + + SAFE_DELETE( mFogBandMatInst ); + + if ( mFogBandMat ) + { + mFogBandMat->deleteObject(); + mFogBandMat = NULL; + } +} + +bool SkyBox::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + setGlobalBounds(); + resetWorldBox(); + + addToScene(); + + if ( isClientObject() ) + { + _initRender(); + _updateMaterial(); + } + + return true; +} + +void SkyBox::onRemove() +{ + removeFromScene(); + Parent::onRemove(); +} + +void SkyBox::initPersistFields() +{ + addGroup( "Sky Box" ); + + addField( "material", TypeMaterialName, Offset( mMatName, SkyBox ), + "The name of a cubemap material for the sky box." ); + + addField( "drawBottom", TypeBool, Offset( mDrawBottom, SkyBox ), + "If false the bottom of the skybox is not rendered." ); + + addField( "fogBandHeight", TypeF32, Offset( mFogBandHeight, SkyBox ), + "The height (0-1) of the fog band from the horizon to the top of the SkyBox." ); + + endGroup( "Sky Box" ); + + Parent::initPersistFields(); +} + +void SkyBox::inspectPostApply() +{ + Parent::inspectPostApply(); + _updateMaterial(); +} + +U32 SkyBox::packUpdate( NetConnection *conn, U32 mask, BitStream *stream ) +{ + U32 retMask = Parent::packUpdate( conn, mask, stream ); + + stream->write( mMatName ); + stream->writeFlag( mDrawBottom ); + stream->write( mFogBandHeight ); + + return retMask; +} + +void SkyBox::unpackUpdate( NetConnection *conn, BitStream *stream ) +{ + Parent::unpackUpdate( conn, stream ); + + String tmpString( "" ); + stream->read( &tmpString ); + if ( !tmpString.equal( mMatName, String::NoCase ) ) + { + mMatName = tmpString; + _updateMaterial(); + } + + bool drawBottom = stream->readFlag(); + F32 bandHeight = 0; + stream->read( &bandHeight ); + + // If this flag has changed + // we need to update the vertex buffer. + if ( drawBottom != mDrawBottom || + bandHeight != mFogBandHeight ) + { + mDrawBottom = drawBottom; + mFogBandHeight = bandHeight; + mIsVBDirty = true; + _initRender(); + } +} + +void SkyBox::prepRenderImage( SceneRenderState *state ) +{ + PROFILE_SCOPE( SkyBox_prepRenderImage ); + + if ( state->isShadowPass() || + mVB.isNull() || + mFogBandVB.isNull() || + !mMatInstance ) + return; + + mMatrixSet->setSceneView(GFX->getWorldMatrix()); + mMatrixSet->setSceneProjection(GFX->getProjectionMatrix()); + + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &SkyBox::_renderObject ); + ri->type = RenderPassManager::RIT_Sky; + ri->defaultKey = 10; + ri->defaultKey2 = 0; + state->getRenderPass()->addInst( ri ); +} + +void SkyBox::_renderObject( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *mi ) +{ + GFXDEBUGEVENT_SCOPE( SkyBox_RenderObject, ColorF::WHITE ); + + GFXTransformSaver saver; + GFX->setVertexBuffer( mVB ); + + MatrixF worldMat = MatrixF::Identity; + worldMat.setPosition( state->getCameraPosition() ); + + SceneData sgData; + sgData.init( state ); + sgData.objTrans = &worldMat; + + mMatrixSet->restoreSceneViewProjection(); + mMatrixSet->setWorld( worldMat ); + if ( state->isReflectPass() ) + mMatrixSet->setProjection( state->getSceneManager()->getNonClipProjection() ); + + while ( mMatInstance->setupPass( state, sgData ) ) + { + mMatInstance->setTransforms( *mMatrixSet, state ); + mMatInstance->setSceneInfo( state, sgData ); + + GFX->drawPrimitive( GFXTriangleList, 0, mPrimCount ); + } + + // Draw render band. + if ( mFogBandHeight > 0 && mFogBandMatInst ) + { + const FogData &fog = state->getSceneManager()->getFogData(); + if ( mLastFogColor != fog.color ) + { + mLastFogColor = fog.color; + _initRender(); + } + + // Just need it to follow the camera... no rotation. + MatrixF camPosMat( MatrixF::Identity ); + camPosMat.setPosition( worldMat.getPosition() ); + sgData.objTrans = &camPosMat; + mMatrixSet->setWorld( *sgData.objTrans ); + + while ( mFogBandMatInst->setupPass( state, sgData ) ) + { + mFogBandMatInst->setTransforms( *mMatrixSet, state ); + mFogBandMatInst->setSceneInfo( state, sgData ); + + GFX->setVertexBuffer( mFogBandVB ); + GFX->drawPrimitive( GFXTriangleList, 0, 16 ); + } + } +} + +void SkyBox::_initRender() +{ + GFXVertexPNTT *tmpVerts = NULL; + + U32 vertCount = 36; + + if ( !mDrawBottom ) + vertCount = 30; + + mPrimCount = vertCount / 3; + + // Create temp vertex pointer + // so we can read from it + // for generating the normals below. + tmpVerts = new GFXVertexPNTT[vertCount]; + + // We don't bother sharing + // vertices here, in order to + // avoid using a primitive buffer. + tmpVerts[0].point.set( -1, -1, 1 ); + tmpVerts[1].point.set( 1, -1, 1 ); + tmpVerts[2].point.set( 1, -1, -1 ); + + tmpVerts[0].texCoord.set( 0, 0 ); + tmpVerts[1].texCoord.set( 1.0f, 0 ); + tmpVerts[2].texCoord.set( 1.0f, 1.0f ); + + tmpVerts[3].point.set( -1, -1, 1 ); + tmpVerts[4].point.set( 1, -1, -1 ); + tmpVerts[5].point.set( -1, -1, -1 ); + + tmpVerts[3].texCoord.set( 0, 0 ); + tmpVerts[4].texCoord.set( 1.0f, 1.0f ); + tmpVerts[5].texCoord.set( 0, 1.0f ); + + tmpVerts[6].point.set( 1, -1, 1 ); + tmpVerts[7].point.set( 1, 1, 1 ); + tmpVerts[8].point.set( 1, 1, -1 ); + + tmpVerts[6].texCoord.set( 0, 0 ); + tmpVerts[7].texCoord.set( 1.0f, 0 ); + tmpVerts[8].texCoord.set( 1.0f, 1.0f ); + + tmpVerts[9].point.set( 1, -1, 1 ); + tmpVerts[10].point.set( 1, 1, -1 ); + tmpVerts[11].point.set( 1, -1, -1 ); + + tmpVerts[9].texCoord.set( 0, 0 ); + tmpVerts[10].texCoord.set( 1.0f, 1.0f ); + tmpVerts[11].texCoord.set( 0, 1.0f ); + + tmpVerts[12].point.set( -1, 1, 1 ); + tmpVerts[13].point.set( -1, -1, 1 ); + tmpVerts[14].point.set( -1, -1, -1 ); + + tmpVerts[12].texCoord.set( 0, 0 ); + tmpVerts[13].texCoord.set( 1.0f, 0 ); + tmpVerts[14].texCoord.set( 1.0f, 1.0f ); + + tmpVerts[15].point.set( -1, 1, 1 ); + tmpVerts[16].point.set( -1, -1, -1 ); + tmpVerts[17].point.set( -1, 1, -1 ); + + tmpVerts[15].texCoord.set( 0, 0 ); + tmpVerts[16].texCoord.set( 1.0f, 1.0f ); + tmpVerts[17].texCoord.set( 1.0f, 0 ); + + tmpVerts[18].point.set( 1, 1, 1 ); + tmpVerts[19].point.set( -1, 1, 1 ); + tmpVerts[20].point.set( -1, 1, -1 ); + + tmpVerts[18].texCoord.set( 0, 0 ); + tmpVerts[19].texCoord.set( 1.0f, 0 ); + tmpVerts[20].texCoord.set( 1.0f, 1.0f ); + + tmpVerts[21].point.set( 1, 1, 1 ); + tmpVerts[22].point.set( -1, 1, -1 ); + tmpVerts[23].point.set( 1, 1, -1 ); + + tmpVerts[21].texCoord.set( 0, 0 ); + tmpVerts[22].texCoord.set( 1.0f, 1.0f ); + tmpVerts[23].texCoord.set( 0, 1.0f ); + + tmpVerts[24].point.set( -1, -1, 1 ); + tmpVerts[25].point.set( -1, 1, 1 ); + tmpVerts[26].point.set( 1, 1, 1 ); + + tmpVerts[24].texCoord.set( 0, 0 ); + tmpVerts[25].texCoord.set( 1.0f, 0 ); + tmpVerts[26].texCoord.set( 1.0f, 1.0f ); + + tmpVerts[27].point.set( -1, -1, 1 ); + tmpVerts[28].point.set( 1, 1, 1 ); + tmpVerts[29].point.set( 1, -1, 1 ); + + tmpVerts[27].texCoord.set( 0, 0 ); + tmpVerts[28].texCoord.set( 1.0f, 1.0f ); + tmpVerts[29].texCoord.set( 0, 1.0f ); + + // Only set up these + // vertices if the SkyBox + // is set to render the bottom face. + if ( mDrawBottom ) + { + tmpVerts[30].point.set( 1, 1, -1 ); + tmpVerts[31].point.set( -1, 1, -1 ); + tmpVerts[32].point.set( -1, -1, -1 ); + + tmpVerts[30].texCoord.set( 1.0f, 1.0f ); + tmpVerts[31].texCoord.set( 1.0f, 0 ); + tmpVerts[32].texCoord.set( 0, 0 ); + + tmpVerts[33].point.set( 1, -1, -1 ); + tmpVerts[34].point.set( 1, 1, -1 ); + tmpVerts[35].point.set( -1, -1, -1 ); + + tmpVerts[33].texCoord.set( 0, 1.0f ); + tmpVerts[34].texCoord.set( 1.0f, 1.0f ); + tmpVerts[35].texCoord.set( 0, 0 ); + } + + VectorF tmp( 0, 0, 0 ); + + for ( U32 i = 0; i < vertCount; i++ ) + { + //tmp = tmpVerts[i].point; + //tmp.normalize(); + //tmpVerts[i].normal.set( tmp ); + + // Note: SkyBox renders with a regular material, which uses the "Reflect Cube" + // feature. + // + // This feature is really designed a cubemap representing a reflection + // on an objects surface and therefore looks up into the cubemap with the + // cubemap-space view vector reflected by the vert normal. + // + // Since we are actually viewing the skybox from "inside" not from + // "outside" this reflection ends up making the cubemap appear upsidown. + // Therefore we set the vert-normals to "zero" so that the reflection + // operation returns the input, unreflected, vector. + + tmpVerts[i].normal.set( Point3F::Zero ); + } + + if ( mVB.isNull() || mIsVBDirty ) + { + mVB.set( GFX, vertCount, GFXBufferTypeStatic ); + mIsVBDirty = false; + } + + GFXVertexPNTT *vertPtr = mVB.lock(); + + dMemcpy( vertPtr, tmpVerts, sizeof ( GFXVertexPNTT ) * vertCount ); + + mVB.unlock(); + + // Clean up temp verts. + delete [] tmpVerts; + + if ( mFogBandVB.isNull() ) + mFogBandVB.set( GFX, 48, GFXBufferTypeStatic ); + + GFXVertexPC *bandVertPtr = mFogBandVB.lock(); + + // Grab the fog color. + ColorI fogColor( mLastFogColor.red * 255, mLastFogColor.green * 255, mLastFogColor.blue * 255 ); + ColorI fogColorAlpha( mLastFogColor.red * 255, mLastFogColor.green * 255, mLastFogColor.blue * 255, 0 ); + + // Upper portion of band geometry. + { + bandVertPtr[0].point.set( -1, -1, mFogBandHeight ); + bandVertPtr[1].point.set( 1, -1, mFogBandHeight ); + bandVertPtr[2].point.set( 1, -1, 0 ); + + bandVertPtr[0].color.set( fogColorAlpha ); + bandVertPtr[1].color.set( fogColorAlpha ); + bandVertPtr[2].color.set( fogColor ); + + bandVertPtr[3].point.set( -1, -1, mFogBandHeight ); + bandVertPtr[4].point.set( 1, -1, 0 ); + bandVertPtr[5].point.set( -1, -1, 0 ); + + bandVertPtr[3].color.set( fogColorAlpha ); + bandVertPtr[4].color.set( fogColor ); + bandVertPtr[5].color.set( fogColor ); + + bandVertPtr[6].point.set( 1, -1, mFogBandHeight ); + bandVertPtr[7].point.set( 1, 1, mFogBandHeight ); + bandVertPtr[8].point.set( 1, 1, 0 ); + + bandVertPtr[6].color.set( fogColorAlpha ); + bandVertPtr[7].color.set( fogColorAlpha ); + bandVertPtr[8].color.set( fogColor ); + + bandVertPtr[9].point.set( 1, -1, mFogBandHeight ); + bandVertPtr[10].point.set( 1, 1, 0 ); + bandVertPtr[11].point.set( 1, -1, 0 ); + + bandVertPtr[9].color.set( fogColorAlpha ); + bandVertPtr[10].color.set( fogColor ); + bandVertPtr[11].color.set( fogColor ); + + bandVertPtr[12].point.set( -1, 1, mFogBandHeight ); + bandVertPtr[13].point.set( -1, -1, mFogBandHeight ); + bandVertPtr[14].point.set( -1, -1, 0 ); + + bandVertPtr[12].color.set( fogColorAlpha ); + bandVertPtr[13].color.set( fogColorAlpha ); + bandVertPtr[14].color.set( fogColor ); + + bandVertPtr[15].point.set( -1, 1, mFogBandHeight ); + bandVertPtr[16].point.set( -1, -1, 0 ); + bandVertPtr[17].point.set( -1, 1, 0 ); + + bandVertPtr[15].color.set( fogColorAlpha ); + bandVertPtr[16].color.set( fogColor ); + bandVertPtr[17].color.set( fogColor ); + + bandVertPtr[18].point.set( 1, 1, mFogBandHeight ); + bandVertPtr[19].point.set( -1, 1, mFogBandHeight ); + bandVertPtr[20].point.set( -1, 1, 0 ); + + bandVertPtr[18].color.set( fogColorAlpha ); + bandVertPtr[19].color.set( fogColorAlpha ); + bandVertPtr[20].color.set( fogColor ); + + bandVertPtr[21].point.set( 1, 1, mFogBandHeight ); + bandVertPtr[22].point.set( -1, 1, 0 ); + bandVertPtr[23].point.set( 1, 1, 0 ); + + bandVertPtr[21].color.set( fogColorAlpha ); + bandVertPtr[22].color.set( fogColor ); + bandVertPtr[23].color.set( fogColor ); + } + + // Lower portion of band geometry. + { + bandVertPtr[24].point.set( -1, -1, 0 ); + bandVertPtr[25].point.set( 1, -1, 0 ); + bandVertPtr[26].point.set( 1, -1, -1 ); + + bandVertPtr[24].color.set( fogColor ); + bandVertPtr[25].color.set( fogColor ); + bandVertPtr[26].color.set( fogColor ); + + bandVertPtr[27].point.set( -1, -1, 0 ); + bandVertPtr[28].point.set( 1, -1, -1 ); + bandVertPtr[29].point.set( -1, -1, -1 ); + + bandVertPtr[27].color.set( fogColor ); + bandVertPtr[28].color.set( fogColor ); + bandVertPtr[29].color.set( fogColor ); + + bandVertPtr[30].point.set( 1, -1, 0 ); + bandVertPtr[31].point.set( 1, 1, 0 ); + bandVertPtr[32].point.set( 1, 1, -1 ); + + bandVertPtr[30].color.set( fogColor ); + bandVertPtr[31].color.set( fogColor ); + bandVertPtr[32].color.set( fogColor ); + + bandVertPtr[33].point.set( 1, -1, 0 ); + bandVertPtr[34].point.set( 1, 1, -1 ); + bandVertPtr[35].point.set( 1, -1, -1 ); + + bandVertPtr[33].color.set( fogColor ); + bandVertPtr[34].color.set( fogColor ); + bandVertPtr[35].color.set( fogColor ); + + bandVertPtr[36].point.set( -1, 1, 0 ); + bandVertPtr[37].point.set( -1, -1, 0 ); + bandVertPtr[38].point.set( -1, -1, -1 ); + + bandVertPtr[36].color.set( fogColor ); + bandVertPtr[37].color.set( fogColor ); + bandVertPtr[38].color.set( fogColor ); + + bandVertPtr[39].point.set( -1, 1, 0 ); + bandVertPtr[40].point.set( -1, -1, -1 ); + bandVertPtr[41].point.set( -1, 1, -1 ); + + bandVertPtr[39].color.set( fogColor ); + bandVertPtr[40].color.set( fogColor ); + bandVertPtr[41].color.set( fogColor ); + + bandVertPtr[42].point.set( 1, 1, 0 ); + bandVertPtr[43].point.set( -1, 1, 0 ); + bandVertPtr[44].point.set( -1, 1, -1 ); + + bandVertPtr[42].color.set( fogColor ); + bandVertPtr[43].color.set( fogColor ); + bandVertPtr[44].color.set( fogColor ); + + bandVertPtr[45].point.set( 1, 1, 0 ); + bandVertPtr[46].point.set( -1, 1, -1 ); + bandVertPtr[47].point.set( 1, 1, -1 ); + + bandVertPtr[45].color.set( fogColor ); + bandVertPtr[46].color.set( fogColor ); + bandVertPtr[47].color.set( fogColor ); + } + + mFogBandVB.unlock(); + + SAFE_DELETE( mFogBandMatInst ); + if ( mFogBandMat ) + { + mFogBandMat->deleteObject(); + mFogBandMat = NULL; + } + + // Setup the material for this imposter. + mFogBandMat = MATMGR->allocateAndRegister( String::EmptyString ); + mFogBandMat->mAutoGenerated = true; + mFogBandMat->mTranslucent = true; + mFogBandMat->mVertColor[0] = true; + mFogBandMat->mDoubleSided = true; + mFogBandMat->mEmissive[0] = true; + + mFogBandMatInst = mFogBandMat->createMatInstance(); + mFogBandMatInst->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat() ); +} + +void SkyBox::onStaticModified( const char *slotName, const char *newValue ) +{ + Parent::onStaticModified( slotName, newValue ); + + if ( dStricmp( slotName, "material" ) == 0 ) + setMaskBits( 0xFFFFFFFF ); +} + +void SkyBox::_initMaterial() +{ + if ( mMatInstance ) + SAFE_DELETE( mMatInstance ); + + if ( mMaterial ) + mMatInstance = mMaterial->createMatInstance(); + else + mMatInstance = MATMGR->createMatInstance( "WarningMaterial" ); + + // We want to disable culling and z write. + GFXStateBlockDesc desc; + desc.setCullMode( GFXCullCW ); + desc.setZReadWrite( true, false ); + mMatInstance->addStateBlockDesc( desc ); + + // Also disable lighting on the skybox material by default. + FeatureSet features = MATMGR->getDefaultFeatures(); + features.removeFeature( MFT_RTLighting ); + features.removeFeature( MFT_Visibility ); + + // Now initialize the material. + mMatInstance->init( features, getGFXVertexFormat() ); +} + +void SkyBox::_updateMaterial() +{ + if ( mMatName.isEmpty() ) + return; + + Material *pMat = NULL; + if ( !Sim::findObject( mMatName, pMat ) ) + Con::printf( "SkyBox::_updateMaterial, failed to find Material of name %s!", mMatName.c_str() ); + else if ( isProperlyAdded() ) + { + mMaterial = pMat; + _initMaterial(); + } +} + +BaseMatInstance* SkyBox::_getMaterialInstance() +{ + if ( !mMaterial || !mMatInstance || mMatInstance->getMaterial() != mMaterial ) + _initMaterial(); + + if ( !mMatInstance ) + return NULL; + + return mMatInstance; +} + +ConsoleMethod( SkyBox, postApply, void, 2, 2, "") +{ + object->inspectPostApply(); +} \ No newline at end of file diff --git a/Engine/source/environment/skyBox.h b/Engine/source/environment/skyBox.h new file mode 100644 index 000000000..997088eb9 --- /dev/null +++ b/Engine/source/environment/skyBox.h @@ -0,0 +1,130 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SKYBOX_H_ +#define _SKYBOX_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif + +#ifndef _GFXDEVICE_H_ +#include "gfx/gfxDevice.h" +#endif + +#ifndef _CUBEMAPDATA_H_ +#include "gfx/sim/cubemapData.h" +#endif + +#ifndef _MATERIALLIST_H_ +#include "materials/materialList.h" +#endif + +#ifndef _GFXVERTEXBUFFER_H_ +#include "gfx/gfxVertexBuffer.h" +#endif + +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif + + +GFXDeclareVertexFormat( GFXSkyVertex ) +{ + Point3F point; + Point3F normal; + GFXVertexColor color; +}; + + +struct SkyMatParams +{ + void init( BaseMatInstance *matInst ) {}; +}; + +class MatrixSet; + +class SkyBox : public SceneObject +{ + typedef SceneObject Parent; + +public: + + SkyBox(); + virtual ~SkyBox(); + + DECLARE_CONOBJECT( SkyBox ); + + // SimObject + void onStaticModified( const char *slotName, const char *newValue ); + + // ConsoleObject + virtual bool onAdd(); + virtual void onRemove(); + static void initPersistFields(); + virtual void inspectPostApply(); + + // NetObject + virtual U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream ); + virtual void unpackUpdate( NetConnection *conn, BitStream *stream ); + + // SceneObject + void prepRenderImage( SceneRenderState* state ); + + /// Our render delegate. + void _renderObject( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *mi ); + + /// Prepares rendering structures and geometry. + void _initRender(); + +protected: + + // Material + String mMatName; + BaseMatInstance *mMatInstance; + SkyMatParams mMatParamHandle; + + SimObjectPtr mMaterial; + + GFXVertexBufferHandle mVB; + + GFXVertexBufferHandle mFogBandVB; + Material *mFogBandMat; + BaseMatInstance *mFogBandMatInst; + + ColorF mLastFogColor; + + bool mDrawBottom; + bool mIsVBDirty; + U32 mPrimCount; + + MatrixSet *mMatrixSet; + + F32 mFogBandHeight; + + void _updateMaterial(); + void _initMaterial(); + + BaseMatInstance* _getMaterialInstance(); +}; + +#endif // _SKYBOX_H_ \ No newline at end of file diff --git a/Engine/source/environment/sun.cpp b/Engine/source/environment/sun.cpp new file mode 100644 index 000000000..00b6a0337 --- /dev/null +++ b/Engine/source/environment/sun.cpp @@ -0,0 +1,563 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "environment/sun.h" + +#include "gfx/bitmap/gBitmap.h" +#include "math/mathIO.h" +#include "core/stream/bitStream.h" +#include "console/consoleTypes.h" +#include "scene/sceneManager.h" +#include "math/mathUtils.h" +#include "lighting/lightInfo.h" +#include "lighting/lightManager.h" +#include "scene/sceneRenderState.h" +#include "renderInstance/renderPassManager.h" +#include "sim/netConnection.h" +#include "environment/timeOfDay.h" +#include "gfx/gfxTransformSaver.h" +#include "materials/materialManager.h" +#include "materials/baseMatInstance.h" +#include "materials/sceneData.h" +#include "math/util/matrixSet.h" + + +IMPLEMENT_CO_NETOBJECT_V1(Sun); + +ConsoleDocClass( Sun, + "@brief A global light affecting your entire scene and optionally renders a corona effect.\n\n" + + "Sun is both the directional and ambient light for your entire scene.\n\n" + + "@ingroup Atmosphere" +); + +//----------------------------------------------------------------------------- + +Sun::Sun() +{ + mNetFlags.set(Ghostable | ScopeAlways); + mTypeMask = EnvironmentObjectType | LightObjectType | StaticObjectType; + + mLightColor.set(0.7f, 0.7f, 0.7f); + mLightAmbient.set(0.3f, 0.3f, 0.3f); + mBrightness = 1.0f; + mSunAzimuth = 0.0f; + mSunElevation = 35.0f; + mCastShadows = true; + + mAnimateSun = false; + mTotalTime = 0.0f; + mCurrTime = 0.0f; + mStartAzimuth = 0.0f; + mEndAzimuth = 0.0f; + mStartElevation = 0.0f; + mEndElevation = 0.0f; + + mLight = LightManager::createLightInfo(); + mLight->setType( LightInfo::Vector ); + + mFlareData = NULL; + mFlareState.clear(); + mFlareScale = 1.0f; + + mCoronaEnabled = true; + mCoronaScale = 0.5f; + mCoronaTint.set( 1.0f, 1.0f, 1.0f, 1.0f ); + mCoronaUseLightColor = true; + mCoronaMatInst = NULL; + + mMatrixSet = reinterpret_cast(dMalloc_aligned(sizeof(MatrixSet), 16)); + constructInPlace(mMatrixSet); + + mCoronaWorldRadius = 0.0f; + mLightWorldPos = Point3F::Zero; +} + +Sun::~Sun() +{ + SAFE_DELETE( mLight ); + SAFE_DELETE( mCoronaMatInst ); + dFree_aligned(mMatrixSet); +} + +bool Sun::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + // Register as listener to TimeOfDay update events + TimeOfDay::getTimeOfDayUpdateSignal().notify( this, &Sun::_updateTimeOfDay ); + + // Make this thing have a global bounds so that its + // always returned from spatial light queries. + setGlobalBounds(); + resetWorldBox(); + setRenderTransform( mObjToWorld ); + addToScene(); + + _initCorona(); + + // Update the light parameters. + _conformLights(); + + setProcessTick( true ); + + return true; +} + +void Sun::onRemove() +{ + TimeOfDay::getTimeOfDayUpdateSignal().remove( this, &Sun::_updateTimeOfDay ); + + removeFromScene(); + Parent::onRemove(); +} + +void Sun::initPersistFields() +{ + addGroup( "Orbit" ); + + addField( "azimuth", TypeF32, Offset( mSunAzimuth, Sun ), + "The horizontal angle of the sun measured clockwise from the positive Y world axis." ); + + addField( "elevation", TypeF32, Offset( mSunElevation, Sun ), + "The elevation angle of the sun above or below the horizon." ); + + endGroup( "Orbit" ); + + // We only add the basic lighting options that all lighting + // systems would use... the specific lighting system options + // are injected at runtime by the lighting system itself. + + addGroup( "Lighting" ); + + addField( "color", TypeColorF, Offset( mLightColor, Sun ), + "Color shading applied to surfaces in direct contact with light source."); + + addField( "ambient", TypeColorF, Offset( mLightAmbient, Sun ), "Color shading applied to surfaces not " + "in direct contact with light source, such as in the shadows or interiors."); + + addField( "brightness", TypeF32, Offset( mBrightness, Sun ), + "Adjust the Sun's global contrast/intensity"); + + addField( "castShadows", TypeBool, Offset( mCastShadows, Sun ), + "Enables/disables shadows cast by objects due to Sun light"); + + endGroup( "Lighting" ); + + addGroup( "Corona" ); + + addField( "coronaEnabled", TypeBool, Offset( mCoronaEnabled, Sun ), + "Enable or disable rendering of the corona sprite." ); + + addField( "coronaMaterial", TypeMaterialName, Offset( mCoronaMatName, Sun ), + "Texture for the corona sprite." ); + + addField( "coronaScale", TypeF32, Offset( mCoronaScale, Sun ), + "Controls size the corona sprite renders, specified as a fractional amount of the screen height." ); + + addField( "coronaTint", TypeColorF, Offset( mCoronaTint, Sun ), + "Modulates the corona sprite color ( if coronaUseLightColor is false )." ); + + addField( "coronaUseLightColor", TypeBool, Offset( mCoronaUseLightColor, Sun ), + "Modulate the corona sprite color by the color of the light ( overrides coronaTint )." ); + + endGroup( "Corona" ); + + + addGroup( "Misc" ); + + addField( "flareType", TYPEID< LightFlareData >(), Offset( mFlareData, Sun ), + "Datablock for the flare produced by the Sun" ); + + addField( "flareScale", TypeF32, Offset( mFlareScale, Sun ), + "Changes the size and intensity of the flare." ); + + endGroup( "Misc" ); + + // Now inject any light manager specific fields. + LightManager::initLightFields(); + + Parent::initPersistFields(); +} + +void Sun::inspectPostApply() +{ + _conformLights(); + setMaskBits(UpdateMask); +} + +U32 Sun::packUpdate(NetConnection *conn, U32 mask, BitStream *stream ) +{ + U32 retMask = Parent::packUpdate( conn, mask, stream ); + + if ( stream->writeFlag( mask & UpdateMask ) ) + { + stream->write( mSunAzimuth ); + stream->write( mSunElevation ); + stream->write( mLightColor ); + stream->write( mLightAmbient ); + stream->write( mBrightness ); + stream->writeFlag( mCastShadows ); + stream->write( mFlareScale ); + + if ( stream->writeFlag( mFlareData ) ) + { + stream->writeRangedU32( mFlareData->getId(), + DataBlockObjectIdFirst, + DataBlockObjectIdLast ); + } + + stream->writeFlag( mCoronaEnabled ); + stream->write( mCoronaMatName ); + stream->write( mCoronaScale ); + stream->write( mCoronaTint ); + stream->writeFlag( mCoronaUseLightColor ); + + mLight->packExtended( stream ); + } + + return retMask; +} + +void Sun::unpackUpdate( NetConnection *conn, BitStream *stream ) +{ + Parent::unpackUpdate( conn, stream ); + + if ( stream->readFlag() ) // UpdateMask + { + stream->read( &mSunAzimuth ); + stream->read( &mSunElevation ); + stream->read( &mLightColor ); + stream->read( &mLightAmbient ); + stream->read( &mBrightness ); + mCastShadows = stream->readFlag(); + stream->read( &mFlareScale ); + + if ( stream->readFlag() ) + { + SimObjectId id = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); + LightFlareData *datablock = NULL; + + if ( Sim::findObject( id, datablock ) ) + mFlareData = datablock; + else + { + conn->setLastError( "Sun::unpackUpdate() - invalid LightFlareData!" ); + mFlareData = NULL; + } + } + else + mFlareData = NULL; + + mCoronaEnabled = stream->readFlag(); + stream->read( &mCoronaMatName ); + stream->read( &mCoronaScale ); + stream->read( &mCoronaTint ); + mCoronaUseLightColor = stream->readFlag(); + + mLight->unpackExtended( stream ); + } + + if ( isProperlyAdded() ) + { + _initCorona(); + _conformLights(); + } +} + +void Sun::submitLights( LightManager *lm, bool staticLighting ) +{ + // The sun is a special light and needs special registration. + lm->setSpecialLight( LightManager::slSunLightType, mLight ); +} + + +void Sun::advanceTime( F32 timeDelta ) +{ + if (mAnimateSun) + { + if (mCurrTime >= mTotalTime) + { + mAnimateSun = false; + mCurrTime = 0.0f; + } + else + { + mCurrTime += timeDelta; + + F32 fract = mCurrTime / mTotalTime; + F32 inverse = 1.0f - fract; + + F32 newAzimuth = mStartAzimuth * inverse + mEndAzimuth * fract; + F32 newElevation = mStartElevation * inverse + mEndElevation * fract; + + if (newAzimuth > 360.0f) + newAzimuth -= 360.0f; + if (newElevation > 360.0f) + newElevation -= 360.0f; + + setAzimuth(newAzimuth); + setElevation(newElevation); + } + } +} + + +void Sun::prepRenderImage( SceneRenderState *state ) +{ + // Only render into diffuse and reflect passes. + + if( !state->isDiffusePass() && + !state->isReflectPass() ) + return; + + mLightWorldPos = state->getCameraPosition() - state->getFarPlane() * mLight->getDirection() * 0.9f; + F32 dist = ( mLightWorldPos - state->getCameraPosition() ).len(); + + F32 screenRadius = GFX->getViewport().extent.y * mCoronaScale * 0.5f; + mCoronaWorldRadius = screenRadius * dist / state->getWorldToScreenScale().y; + + // Render instance for Corona effect. + if ( mCoronaEnabled && mCoronaMatInst ) + { + mMatrixSet->setSceneProjection( GFX->getProjectionMatrix() ); + mMatrixSet->setSceneView( GFX->getViewMatrix() ); + mMatrixSet->setWorld( GFX->getWorldMatrix() ); + + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &Sun::_renderCorona ); + ri->type = RenderPassManager::RIT_Sky; + // Render after sky objects and before CloudLayer! + ri->defaultKey = 5; + ri->defaultKey2 = 0; + state->getRenderPass()->addInst( ri ); + } + + // LightFlareData handles rendering flare effects. + if ( mFlareData ) + { + mFlareState.fullBrightness = mBrightness; + mFlareState.scale = mFlareScale; + mFlareState.lightInfo = mLight; + mFlareState.worldRadius = mCoronaWorldRadius; + + mFlareState.lightMat.identity(); + mFlareState.lightMat.setPosition( mLightWorldPos ); + + mFlareData->prepRender( state, &mFlareState ); + } +} + +void Sun::setAzimuth( F32 azimuth ) +{ + mSunAzimuth = azimuth; + _conformLights(); + setMaskBits( UpdateMask ); // TODO: Break out the masks to save bandwidth! +} + +void Sun::setElevation( F32 elevation ) +{ + mSunElevation = elevation; + _conformLights(); + setMaskBits( UpdateMask ); // TODO: Break out the masks to save some space! +} + +void Sun::setColor( const ColorF &color ) +{ + mLightColor = color; + _conformLights(); + setMaskBits( UpdateMask ); // TODO: Break out the masks to save some space! +} + +void Sun::animate( F32 duration, F32 startAzimuth, F32 endAzimuth, F32 startElevation, F32 endElevation ) +{ + mAnimateSun = true; + mCurrTime = 0.0f; + + mTotalTime = duration; + + mStartAzimuth = startAzimuth; + mEndAzimuth = endAzimuth; + mStartElevation = startElevation; + mEndElevation = endElevation; +} + +void Sun::_conformLights() +{ + // Build the light direction from the azimuth and elevation. + F32 yaw = mDegToRad(mClampF(mSunAzimuth,0,359)); + F32 pitch = mDegToRad(mClampF(mSunElevation,-360,+360)); + VectorF lightDirection; + MathUtils::getVectorFromAngles(lightDirection, yaw, pitch); + lightDirection.normalize(); + mLight->setDirection( -lightDirection ); + mLight->setBrightness( mBrightness ); + + // Now make sure the colors are within range. + mLightColor.clamp(); + mLight->setColor( mLightColor ); + mLightAmbient.clamp(); + mLight->setAmbient( mLightAmbient ); + + // Optimization... disable shadows if the ambient and + // directional color are the same. + bool castShadows = mLightColor != mLightAmbient && mCastShadows; + mLight->setCastShadows( castShadows ); +} + +void Sun::_initCorona() +{ + if ( isServerObject() ) + return; + + SAFE_DELETE( mCoronaMatInst ); + + if ( mCoronaMatName.isNotEmpty() ) + mCoronaMatInst = MATMGR->createMatInstance( mCoronaMatName, MATMGR->getDefaultFeatures(), getGFXVertexFormat() ); +} + +void Sun::_renderCorona( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ) +{ + // Calculate Billboard Radius (in world units) to be constant, independent of distance. + // Takes into account distance, viewport size, and specified size in editor + F32 BBRadius = mCoronaWorldRadius; + + mMatrixSet->restoreSceneViewProjection(); + + if ( state->isReflectPass() ) + mMatrixSet->setProjection( state->getSceneManager()->getNonClipProjection() ); + + //mMatrixSet->setWorld( MatrixF::Identity ); + + // Initialize points with basic info + Point3F points[4]; + points[0] = Point3F(-BBRadius, 0.0, -BBRadius); + points[1] = Point3F( -BBRadius, 0.0, BBRadius); + points[2] = Point3F( BBRadius, 0.0, BBRadius); + points[3] = Point3F( BBRadius, 0.0, -BBRadius); + + static const Point2F sCoords[4] = + { + Point2F( 0.0f, 0.0f ), + Point2F( 0.0f, 1.0f ), + Point2F( 1.0f, 1.0f ), + Point2F( 1.0f, 0.0f ) + }; + + // Get info we need to adjust points + const MatrixF &camView = state->getCameraTransform(); + + // Finalize points + for(int i = 0; i < 4; i++) + { + // align with camera + camView.mulV(points[i]); + // offset + points[i] += mLightWorldPos; + } + + ColorF vertColor; + if ( mCoronaUseLightColor ) + vertColor = mLightColor; + else + vertColor = mCoronaTint; + + GFXVertexBufferHandle< GFXVertexPCT > vb; + vb.set( GFX, 4, GFXBufferTypeVolatile ); + GFXVertexPCT *pVert = vb.lock(); + + for ( S32 i = 0; i < 4; i++ ) + { + pVert->color.set( vertColor ); + pVert->point.set( points[i] ); + pVert->texCoord.set( sCoords[i].x, sCoords[i].y ); + pVert++; + } + + vb.unlock(); + + // Setup SceneData struct. + + SceneData sgData; + sgData.wireframe = GFXDevice::getWireframe(); + sgData.visibility = 1.0f; + + // Draw it + + while ( mCoronaMatInst->setupPass( state, sgData ) ) + { + mCoronaMatInst->setTransforms( *mMatrixSet, state ); + mCoronaMatInst->setSceneInfo( state, sgData ); + + GFX->setVertexBuffer( vb ); + GFX->drawPrimitive( GFXTriangleFan, 0, 2 ); + } +} + +void Sun::_updateTimeOfDay( TimeOfDay *timeOfDay, F32 time ) +{ + setElevation( timeOfDay->getElevationDegrees() ); + setAzimuth( timeOfDay->getAzimuthDegrees() ); +} + +void Sun::_onSelected() +{ +#ifdef TORQUE_DEBUG + // Enable debug rendering on the light. + if( isClientObject() ) + mLight->enableDebugRendering( true ); +#endif + + + Parent::_onSelected(); +} + +void Sun::_onUnselected() +{ +#ifdef TORQUE_DEBUG + // Disable debug rendering on the light. + if( isClientObject() ) + mLight->enableDebugRendering( false ); +#endif + + Parent::_onUnselected(); +} + +ConsoleMethod(Sun, apply, void, 2, 2, "") +{ + object->inspectPostApply(); +} + +ConsoleMethod(Sun, animate, void, 7, 7, "animate( F32 duration, F32 startAzimuth, F32 endAzimuth, F32 startElevation, F32 endElevation )") +{ + F32 duration = dAtof(argv[2]); + F32 startAzimuth = dAtof(argv[3]); + F32 endAzimuth = dAtof(argv[4]); + F32 startElevation = dAtof(argv[5]); + F32 endElevation = dAtof(argv[6]); + + object->animate(duration, startAzimuth, endAzimuth, startElevation, endElevation); +} + diff --git a/Engine/source/environment/sun.h b/Engine/source/environment/sun.h new file mode 100644 index 000000000..d82c45768 --- /dev/null +++ b/Engine/source/environment/sun.h @@ -0,0 +1,143 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SUN_H_ +#define _SUN_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _COLOR_H_ +#include "core/color.h" +#endif +#ifndef _LIGHTINFO_H_ +#include "lighting/lightInfo.h" +#endif +#ifndef _LIGHTFLAREDATA_H_ +#include "T3D/lightFlareData.h" +#endif + +class TimeOfDay; +class MatrixSet; + +/// +class Sun : public SceneObject, public ISceneLight +{ + typedef SceneObject Parent; + +protected: + + F32 mSunAzimuth; + + F32 mSunElevation; + + ColorF mLightColor; + + ColorF mLightAmbient; + + F32 mBrightness; + + bool mAnimateSun; + F32 mTotalTime; + F32 mCurrTime; + F32 mStartAzimuth; + F32 mEndAzimuth; + F32 mStartElevation; + F32 mEndElevation; + + bool mCastShadows; + + LightInfo *mLight; + + LightFlareData *mFlareData; + LightFlareState mFlareState; + F32 mFlareScale; + + bool mCoronaEnabled; + String mCoronaMatName; + BaseMatInstance *mCoronaMatInst; + MatrixSet *mMatrixSet; + F32 mCoronaScale; + ColorF mCoronaTint; + bool mCoronaUseLightColor; + + // These are not user specified. + // These hold data calculated once used across several methods. + F32 mCoronaWorldRadius; + Point3F mLightWorldPos; + + void _conformLights(); + void _initCorona(); + void _renderCorona( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); + void _updateTimeOfDay( TimeOfDay *timeOfDay, F32 time ); + + // SimObject. + virtual void _onSelected(); + virtual void _onUnselected(); + + enum NetMaskBits + { + UpdateMask = BIT(0) + }; + +public: + + Sun(); + virtual ~Sun(); + + // SimObject + virtual bool onAdd(); + virtual void onRemove(); + + // ConsoleObject + DECLARE_CONOBJECT(Sun); + static void initPersistFields(); + void inspectPostApply(); + + // NetObject + U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream ); + void unpackUpdate( NetConnection *conn, BitStream *stream ); + + // ISceneLight + virtual void submitLights( LightManager *lm, bool staticLighting ); + virtual LightInfo* getLight() { return mLight; } + + // SceneObject + virtual void prepRenderImage( SceneRenderState* state ); + + // ProcessObject + virtual void advanceTime( F32 dt ); + + /// + void setAzimuth( F32 azimuth ); + + /// + void setElevation( F32 elevation ); + + /// + void setColor( const ColorF &color ); + + /// + void animate( F32 duration, F32 startAzimuth, F32 endAzimuth, F32 startElevation, F32 endElevation ); +}; + +#endif // _SUN_H_ diff --git a/Engine/source/environment/timeOfDay.cpp b/Engine/source/environment/timeOfDay.cpp new file mode 100644 index 000000000..49b069b64 --- /dev/null +++ b/Engine/source/environment/timeOfDay.cpp @@ -0,0 +1,745 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "environment/timeOfDay.h" + +#include "console/consoleTypes.h" +#include "core/stream/bitStream.h" +#include "T3D/gameBase/gameConnection.h" +#include "environment/sun.h" +#include "console/engineAPI.h" + + +TimeOfDayUpdateSignal TimeOfDay::smTimeOfDayUpdateSignal; + +IMPLEMENT_CO_NETOBJECT_V1(TimeOfDay); + +ConsoleDocClass( TimeOfDay, + "@brief Environmental object that triggers a day/night cycle in level.\n\n" + + "@note TimeOfDay only works in Advanced Lighting with a Sub object or ScatterSky\n\n" + + "@tsexample\n" + "new TimeOfDay(tod)\n" + "{\n" + " axisTilt = \"23.44\";\n" + " dayLength = \"120\";\n" + " startTime = \"0.15\";\n" + " time = \"0.15\";\n" + " play = \"0\";\n" + " azimuthOverride = \"572.958\";\n" + " dayScale = \"1\";\n" + " nightScale = \"1.5\";\n" + " position = \"598.399 550.652 196.297\";\n" + " rotation = \"1 0 0 0\";\n" + " scale = \"1 1 1\";\n" + " canSave = \"1\";\n" + " canSaveDynamicFields = \"1\";\n" + "};\n" + "@endtsexample\n\n" + "@ingroup enviroMisc" +); + +TimeOfDay::TimeOfDay() + : mElevation( 0.0f ), + mAzimuth( 0.0f ), + mAxisTilt( 23.44f ), // 35 degree tilt + mDayLen( 120.0f ), // 2 minutes + mStartTimeOfDay( 0.5f ), // High noon + mTimeOfDay( 0.0f ), // initialized to StartTimeOfDay in onAdd + mPlay( true ), + mDayScale( 1.0f ), + mNightScale( 1.5f ), + mAnimateTime( 0.0f ), + mAnimateSpeed( 0.0f ), + mAnimate( false ) +{ + mNetFlags.set( Ghostable | ScopeAlways ); + mTypeMask = EnvironmentObjectType; + + // Sets the sun vector directly overhead for lightmap generation + // The value of mSunVector is grabbed by the terrain lighting stuff. + /* + F32 ele, azi; + ele = azi = TORADIANS(90); + MathUtils::getVectorFromAngles(mSunVector, azi, ele); + */ + mPrevElevation = 0; + mNextElevation = 0; + mAzimuthOverride = 1.0f; + + _initColors(); +} + +TimeOfDay::~TimeOfDay() +{ +} + +bool TimeOfDay::setTimeOfDay( void *object, const char *index, const char *data ) +{ + TimeOfDay *tod = static_cast(object); + tod->setTimeOfDay( dAtof( data ) ); + + return false; +} + +bool TimeOfDay::setPlay( void *object, const char *index, const char *data ) +{ + TimeOfDay *tod = static_cast(object); + tod->setPlay( dAtob( data ) ); + + return false; +} + +bool TimeOfDay::setDayLength( void *object, const char *index, const char *data ) +{ + TimeOfDay *tod = static_cast(object); + F32 length = dAtof( data ); + if( length != 0 ) + tod->setDayLength( length ); + + return false; + +} + +void TimeOfDay::initPersistFields() +{ + addGroup( "TimeOfDay" ); + + addField( "axisTilt", TypeF32, Offset( mAxisTilt, TimeOfDay ), + "The angle in degrees between global equator and tropic." ); + + addProtectedField( "dayLength", TypeF32, Offset( mDayLen, TimeOfDay ), &setDayLength, &defaultProtectedGetFn, + "The length of a virtual day in real world seconds." ); + + addField( "startTime", TypeF32, Offset( mStartTimeOfDay, TimeOfDay ), + "" ); + + addProtectedField( "time", TypeF32, Offset( mTimeOfDay, TimeOfDay ), &setTimeOfDay, &defaultProtectedGetFn, "Current time of day." ); + + addProtectedField( "play", TypeBool, Offset( mPlay, TimeOfDay ), &setPlay, &defaultProtectedGetFn, "True when the TimeOfDay object is operating." ); + + addField( "azimuthOverride", TypeF32, Offset( mAzimuthOverride, TimeOfDay ), "" ); + + addField( "dayScale", TypeF32, Offset( mDayScale, TimeOfDay ), "Scalar applied to time that elapses while the sun is up." ); + + addField( "nightScale", TypeF32, Offset( mNightScale, TimeOfDay ), "Scalar applied to time that elapses while the sun is down." ); + + endGroup( "TimeOfDay" ); + + Parent::initPersistFields(); +} + +void TimeOfDay::consoleInit() +{ + Parent::consoleInit(); + + //addVariable( "$TimeOfDay::currentTime", &TimeOfDay::smCurrentTime ); + //addVariable( "$TimeOfDay::timeScale", TypeF32, &TimeOfDay::smTimeScale ); +} + +void TimeOfDay::inspectPostApply() +{ + _updatePosition(); + setMaskBits( OrbitMask ); +} + +void TimeOfDay::_onGhostAlwaysDone() +{ + _updatePosition(); +} + +bool TimeOfDay::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + // The server initializes to the specified starting values. + // The client initializes itself to the server time from + // unpackUpdate. + if ( isServerObject() ) + { + mTimeOfDay = mStartTimeOfDay; + _updatePosition(); + } + + // We don't use a bounds. + setGlobalBounds(); + resetWorldBox(); + addToScene(); + + // Lets receive ghost events so we can resolve + // the sun object. + if ( isClientObject() ) + NetConnection::smGhostAlwaysDone.notify( this, &TimeOfDay::_onGhostAlwaysDone ); + + if ( isServerObject() ) + Con::executef( this, "onAdd" ); + + setProcessTick( true ); + + return true; +} + +void TimeOfDay::onRemove() +{ + if ( isClientObject() ) + NetConnection::smGhostAlwaysDone.remove( this, &TimeOfDay::_onGhostAlwaysDone ); + + removeFromScene(); + Parent::onRemove(); +} + +U32 TimeOfDay::packUpdate(NetConnection *conn, U32 mask, BitStream *stream ) +{ + U32 retMask = Parent::packUpdate( conn, mask, stream ); + + if ( stream->writeFlag( mask & OrbitMask ) ) + { + stream->write( mStartTimeOfDay ); + stream->write( mDayLen ); + stream->write( mTimeOfDay ); + stream->write( mAxisTilt ); + stream->write( mAzimuthOverride ); + + stream->write( mDayScale ); + stream->write( mNightScale ); + + stream->writeFlag( mPlay ); + } + + if ( stream->writeFlag( mask & AnimateMask ) ) + { + stream->write( mAnimateTime ); + stream->write( mAnimateSpeed ); + } + + return retMask; +} + +void TimeOfDay::unpackUpdate( NetConnection *conn, BitStream *stream ) +{ + Parent::unpackUpdate( conn, stream ); + + if ( stream->readFlag() ) // OrbitMask + { + stream->read( &mStartTimeOfDay ); + stream->read( &mDayLen ); + stream->read( &mTimeOfDay ); + stream->read( &mAxisTilt ); + stream->read( &mAzimuthOverride ); + + stream->read( &mDayScale ); + stream->read( &mNightScale ); + + mPlay = stream->readFlag(); + + _updatePosition(); + } + + if ( stream->readFlag() ) // AnimateMask + { + F32 time, speed; + stream->read( &time ); + stream->read( &speed ); + + if( isProperlyAdded() ) + animate( time, speed ); + } +} + +void TimeOfDay::processTick( const Move *move ) +{ + if ( mAnimate ) + { + F32 current = mTimeOfDay * 360.0f; + F32 next = current + (mAnimateSpeed * TickSec); + + // Protect for wrap around. + while ( next > 360.0f ) + next -= 360.0f; + + // Clamp to make sure we don't pass the target time. + if ( next >= mAnimateTime ) + { + next = mAnimateTime; + mAnimate = false; + } + + // Set the new time of day. + mTimeOfDay = next / 360.0f; + + _updatePosition(); + _updateTimeEvents(); + + if ( !mAnimate && isServerObject() ) + Con::executef( this, "onAnimateDone" ); + } + else if ( mPlay ) + { + F32 dt = TickSec; + F32 current = mRadToDeg( mNextElevation ); + + if ( current > 350.0f || ( 0.0f <= current && current < 190.0f ) ) + dt *= mDayScale; + else + dt *= mNightScale; + + mTimeOfDay += dt / mDayLen; + + // It could be possible for more than a full day to + // pass in a single advance time, so I put this inside a loop + // but timeEvents will not actually be called for the + // skipped day. + while ( mTimeOfDay > 1.0f ) + mTimeOfDay -= 1.0f; + + _updatePosition(); + _updateTimeEvents(); + } + else + _updatePosition(); +} + +void TimeOfDay::_updatePosition() +{ + mPrevElevation = mNextElevation; + + if ( mFabs( mAzimuthOverride ) ) + { + mElevation = mDegToRad( mTimeOfDay * 360.0f ); + mAzimuth = mAzimuthOverride; + + mNextElevation = mElevation; // already normalized + } + else + { + //// Full azimuth/elevation calculation. + //// calculate sun decline and meridian angle (in radians) + //F32 sunDecline = mSin( M_2PI * mTimeOfYear ) * mDegToRad( mAxisTilt ); + //F32 meridianAngle = mTimeOfDay * M_2PI - mDegToRad( mLongitude ); + + //// calculate the elevation and azimuth (in radians) + //mElevation = _calcElevation( mDegToRad( mLatitude ), sunDecline, meridianAngle ); + //mAzimuth = _calcAzimuth( mDegToRad( mLatitude ), sunDecline, meridianAngle ); + + // Simplified azimuth/elevation calculation. + // calculate sun decline and meridian angle (in radians) + F32 sunDecline = mDegToRad( mAxisTilt ); + F32 meridianAngle = mTimeOfDay * M_2PI; + + // calculate the elevation and azimuth (in radians) + mElevation = _calcElevation( 0.0f, sunDecline, meridianAngle ); + mAzimuth = _calcAzimuth( 0.0f, sunDecline, meridianAngle ); + + // calculate 'normalized' elevation (0=sunrise, PI/2=zenith, PI=sunset, 3PI/4=nadir) + F32 normElevation = M_PI_F * mElevation / ( 2 * _calcElevation( 0.0f, sunDecline, 0.0f ) ); + if ( mAzimuth > M_PI_F ) + normElevation = M_PI_F - normElevation; + else if ( mElevation < 0 ) + normElevation = M_2PI_F + normElevation; + + mNextElevation = normElevation; + } + + // Only the client updates the sun position! + if ( isClientObject() ) + smTimeOfDayUpdateSignal.trigger( this, mTimeOfDay ); +} + +F32 TimeOfDay::_calcElevation( F32 lat, F32 dec, F32 mer ) +{ + return mAsin( mSin(lat) * mSin(dec) + mCos(lat) * mCos(dec) * mCos(mer) ); +} + +F32 TimeOfDay::_calcAzimuth( F32 lat, F32 dec, F32 mer ) +{ + // Add PI to normalize this from the range of -PI/2 to PI/2 to 0 to 2 * PI; + return mAtan2( mSin(mer), mCos(mer) * mSin(lat) - mTan(dec) * mCos(lat) ) + M_PI_F; +} + +void TimeOfDay::_getSunColor( ColorF *outColor ) const +{ + const COLOR_TARGET *ct = NULL; + + F32 ele = mClampF( M_2PI_F - mNextElevation, 0.0f, M_PI_F ); + F32 phase = -1.0f; + F32 div; + + if (!mColorTargets.size()) + { + outColor->set(1.0f,1.0f,1.0f); + return; + } + + if (mColorTargets.size() == 1) + { + ct = &mColorTargets[0]; + outColor->set(ct->color.red, ct->color.green, ct->color.blue); + return; + } + + //simple check + if ( mColorTargets[0].elevation != 0.0f ) + { + AssertFatal(0, "TimeOfDay::GetColor() - First elevation must be 0.0 radians") + outColor->set(1.0f, 1.0f, 1.0f); + //mBandMod = 1.0f; + //mCurrentBandColor = color; + return; + } + + if ( mColorTargets[mColorTargets.size()-1].elevation != M_PI_F ) + { + AssertFatal(0, "Celestails::GetColor() - Last elevation must be PI") + outColor->set(1.0f, 1.0f, 1.0f); + //mBandMod = 1.0f; + //mCurrentBandColor = color; + return; + } + + //we need to find the phase and interp... also loop back around + U32 count=0; + for (;count < mColorTargets.size() - 1; count++) + { + const COLOR_TARGET *one = &mColorTargets[count]; + const COLOR_TARGET *two = &mColorTargets[count+1]; + + if (ele >= one->elevation && ele <= two->elevation) + { + div = two->elevation - one->elevation; + + //catch bad input divide by zero + if ( mFabs( div ) < 0.01f ) + div = 0.01f; + + phase = (ele - one->elevation) / div; + outColor->interpolate( one->color, two->color, phase ); + + //mCurrentBandColor.interpolate(one->bandColor, two->bandColor, phase); + //mBandMod = one->bandMod * (1.0f - phase) + two->bandMod * phase; + + return; + } + } + + AssertFatal(0,"This isn't supposed to happen"); +} + +void TimeOfDay::_initColors() +{ + // NOTE: The elevation targets represent distances + // from PI/2 radians (strait up). + + ColorF c; + ColorF bc; + + // e is for elevation + F32 e = M_PI_F / 13.0f; // (semicircle in radians)/(number of color target entries); + + // Day + c.set(1.0f,1.0f,1.0f); + _addColorTarget(0, c, 1.0f, c); // High noon at equanox + c.set(.9f,.9f,.9f); + _addColorTarget(e * 1.0f, c, 1.0f, c); + c.set(.9f,.9f,.9f); + _addColorTarget(e * 2.0f, c, 1.0f, c); + c.set(.8f,.75f,.75f); + _addColorTarget(e * 3.0f, c, 1.0f, c); + c.set(.7f,.65f,.65f); + _addColorTarget(e * 4.0f, c, 1.0f, c); + + //Dawn and Dusk (3 entries) + c.set(.7f,.65f,.65f); + bc.set(.8f,.6f,.3f); + _addColorTarget(e * 5.0f, c, 3.0f, bc); + c.set(.65f,.54f,.4f); + bc.set(.75f,.5f,.4f); + _addColorTarget(e * 6.0f, c, 2.75f, bc); + c.set(.55f,.45f,.25f); + bc.set(.65f,.3f,.3f); + _addColorTarget(e * 7.0f, c, 2.5f, bc); + + //NIGHT + c.set(.3f,.3f,.3f); + bc.set(.7f,.4f,.2f); + _addColorTarget(e * 8.0f, c, 1.25f, bc); + c.set(.25f,.25f,.3f); + bc.set(.8f,.3f,.2f); + _addColorTarget(e * 9.0f, c, 1.00f, bc); + c.set(.25f,.25f,.4f); + _addColorTarget(e * 10.0f, c, 1.0f, c); + c.set(.2f,.2f,.35f); + _addColorTarget(e * 11.0f, c, 1.0f, c); + c.set(.15f,.15f,.2f); + _addColorTarget(M_PI_F, c, 1.0f, c); // Midnight at equanox. +} + +void TimeOfDay::_addColorTarget( F32 ele, const ColorF &color, F32 bandMod, const ColorF &bandColor ) +{ + COLOR_TARGET newTarget; + + newTarget.elevation = ele; + newTarget.color = color; + newTarget.bandMod = bandMod; + newTarget.bandColor = bandColor; + + mColorTargets.push_back(newTarget); +} + +void TimeOfDay::_updateTimeEvents() +{ + if ( mTimeEvents.empty() ) + return; + + // Get the prev, next elevation in degrees since TimeOfDayEvent is specified + // in degrees. + F32 prevElevation = mRadToDeg( mPrevElevation ); + F32 nextElevation = mRadToDeg( mNextElevation ); + + // If prevElevation is less than nextElevation then its the next day. + // Unroll it so we can just loop forward in time and simplify our loop. + if ( nextElevation < prevElevation ) + nextElevation += 360.0f; + + const U32 evtCount = mTimeEvents.size(); + + // Find where in the event list we need to start... + // The first timeEvent with elevation greater than our previous elevation. + + U32 start = 0; + for ( ; start < evtCount; start++ ) + { + if ( mTimeEvents[start].triggerElevation > prevElevation ) + break; + } + + bool onNextDay = false; + + // Nothing between prevElevation and the end of the day... + // Check between start of the day and nextElevation... + if ( start == evtCount ) + { + start = 0; + for ( ; start < evtCount; start++ ) + { + if ( mTimeEvents[start].triggerElevation <= nextElevation ) + { + onNextDay = true; + break; + } + } + } + + // No events were hit... + if ( start == evtCount ) + return; + + U32 itr = start; + while ( true ) + { + TimeOfDayEvent &timeEvent = mTimeEvents[itr]; + + F32 elev = timeEvent.triggerElevation; + if ( onNextDay ) + elev += 360.0f; + + // Hit an event that happens later after nextElevation so we + // have checked everything within the range and are done. + if ( elev > nextElevation ) + break; + + // If its not greater than the nextElevation it must be less, and if + // we are here we already know its greater than prevElevation. + + AssertFatal( elev >= prevElevation && elev <= nextElevation, "TimeOfDay::_updateTimeEvents - Logical error in here!" ); + AssertFatal( !timeEvent.deleteMe, "TimeOfDay::_updateTimeEvents - tried to fire the same event twice!" ); + + _onTimeEvent( timeEvent.identifier ); + + if ( timeEvent.oneShot ) + timeEvent.deleteMe = true; + + // On to the next time event... + itr++; + + // We hit the end of the day? + if ( itr == evtCount ) + { + // We are already on the next day so we have checked everything. + if ( onNextDay ) + break; + // Check events for the next day + else + { + itr = 0; + onNextDay = true; + } + } + } + + // Cleanup one-shot events that fired... + + for ( S32 i = 0; i < mTimeEvents.size(); i++ ) + { + if ( mTimeEvents[i].deleteMe ) + { + // Don't use erase_fast, there are ordered. + mTimeEvents.erase( i ); + i--; + } + } +} + +void TimeOfDay::addTimeEvent( F32 triggerElevation, const UTF8 *identifier ) +{ + // Insert in ascending order of elevation. + // Note that having more than one TimeEvent with the same triggerElevation + // may cause undefined behavior. + + TimeOfDayEvent *pEvent = NULL; + + if ( mTimeEvents.empty() || mTimeEvents.last().triggerElevation <= triggerElevation ) + { + mTimeEvents.increment(); + pEvent = &mTimeEvents.last(); + } + else + { + for ( S32 i = 0; i < mTimeEvents.size(); i++ ) + { + if ( mTimeEvents[i].triggerElevation > triggerElevation ) + { + mTimeEvents.insert( i ); + pEvent = &mTimeEvents[i]; + break; + } + } + } + + AssertFatal( pEvent, "TimeOfDay::addTimeEvent - could not find place to insert event." ); + + pEvent->triggerElevation = triggerElevation; + pEvent->identifier = identifier; + pEvent->oneShot = false; + + pEvent->deleteMe = false; +} + +void TimeOfDay::setTimeOfDay( F32 time ) +{ + mTimeOfDay = time; + + while ( mTimeOfDay > 1.0f ) + mTimeOfDay -= 1.0f; + while ( mTimeOfDay < 0.0f ) + mTimeOfDay += 1.0f; + + _updatePosition(); + + //if ( isServerObject() ) + _updateTimeEvents(); + + setMaskBits( OrbitMask ); +} + +void TimeOfDay::_onTimeEvent( const String &identifier ) +{ + // Client doesn't do onTimeEvent callbacks. + if ( isClientObject() ) + return; + + String strCurrentTime = String::ToString( "%g", mTimeOfDay ); + + F32 elevation = mRadToDeg( mNextElevation ); + while( elevation < 0 ) + elevation += 360.0f; + while( elevation > 360.0f ) + elevation -= 360.0f; + + String strCurrentElevation = String::ToString( "%g", elevation ); + + Con::executef( this, "onTimeEvent", identifier.c_str(), strCurrentTime.c_str(), strCurrentElevation.c_str() ); +} + +void TimeOfDay::animate( F32 time, F32 speed ) +{ + // Stop any existing animation... this one + // becomes the new one. + mAnimate = false; + + // Set the target time to hit. + mAnimateTime = mClamp(time, 0.0f, 360.0f); + + F32 current = mTimeOfDay * 360.0f; + F32 target = mAnimateTime; + if ( target < current ) + target += 360.0f; + + // If we're already at the current time then + // we have nothing more to do... the animation is here. + F32 dif = target - current; + if ( mIsZero( dif ) ) + return; + + // Start playback. + mAnimateSpeed = speed; + mAnimate = true; + + if ( isServerObject() ) + { + Con::executef( this, "onAnimateStart" ); + setMaskBits( AnimateMask ); + } +} + +DefineEngineMethod( TimeOfDay, addTimeOfDayEvent, void, (F32 elevation, const char *identifier ),, + "" ) +{ + object->addTimeEvent( elevation, identifier ); +} + +DefineEngineMethod( TimeOfDay, setTimeOfDay, void, ( F32 time ),, + "" ) +{ + object->setTimeOfDay( time ); +} + +DefineEngineMethod( TimeOfDay, setPlay, void, ( bool enabled ),, + "") +{ + object->setPlay( enabled ); +} + +DefineEngineMethod( TimeOfDay, setDayLength, void, ( F32 seconds ),, + "" ) +{ + if ( seconds > 0.0f ) + object->setDayLength( seconds ); +} + +DefineEngineMethod( TimeOfDay, animate, void, ( F32 elevation, F32 degreesPerSecond ),, + "") +{ + object->animate( elevation, degreesPerSecond ); +} diff --git a/Engine/source/environment/timeOfDay.h b/Engine/source/environment/timeOfDay.h new file mode 100644 index 000000000..25beddc12 --- /dev/null +++ b/Engine/source/environment/timeOfDay.h @@ -0,0 +1,212 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + + +#ifndef _TIMEOFDAY_H_ +#define _TIMEOFDAY_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif + +class Sun; +class TimeOfDay; + +struct COLOR_TARGET +{ + F32 elevation; // maximum target elevation + ColorF color; //normalized 0 = 1.0 ... + F32 bandMod; //6 is max + ColorF bandColor; +}; + +typedef Vector COLOR_TARGETS; + +typedef Signal TimeOfDayUpdateSignal; + + +struct TimeOfDayEvent +{ + // The elevation at which + // this event will fire. + F32 triggerElevation; + + // User identifier for the event. + String identifier; + + // Remove this event when it fires. + bool oneShot; + + // For internal use. + bool deleteMe; +}; + +class TimeOfDay : public SceneObject +{ + typedef SceneObject Parent; + +public: + + static S32 smCurrentTime; + static F32 smTimeScale; // To pause or resume time flow from outside this object, like in the editor. + + TimeOfDay(); + virtual ~TimeOfDay(); + + // ConsoleObject + static void initPersistFields(); + static void consoleInit(); + DECLARE_CONOBJECT( TimeOfDay ); + void inspectPostApply(); + + // SimObject + virtual bool onAdd(); + virtual void onRemove(); + + // NetObject + U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream ); + void unpackUpdate( NetConnection *conn, BitStream *stream ); + + // ProcessObject + virtual void processTick( const Move *move ); + + F32 getAzimuthRads() { return mAzimuth; } + F32 getElevationRads() { return mElevation; } + F32 getAzimuthDegrees() { return mRadToDeg(mAzimuth); } + F32 getElevationDegrees() { return mRadToDeg(mElevation); } + + /* + // Sun position stuff + void UpdateSunPosition(void); + // void UpdateSunPosition(fxSunLight *sunLight); + + + // Scene lighting (Adapted from Joshua Ritter's Day/Night cycle code) + // I changed references to pointers on the basis of principle. ;-) + void EnableLighting(F32 emissiveScale = 1.0); + void DisableLighting(); + F32 GetIntensity() + { return (mCurrentColor.blue + mCurrentColor.green + mCurrentColor.red) / 3; } + */ + static TimeOfDayUpdateSignal& getTimeOfDayUpdateSignal() { return smTimeOfDayUpdateSignal; } + void getSunColor( ColorF *outColor ) const { _getSunColor( outColor ); } + + void addTimeEvent( F32 triggerElevation, const UTF8 *identifier ); + + void setTimeOfDay( F32 time ); + void setPlay( bool play ) { mPlay = play; setMaskBits( OrbitMask ); } + void setDayLength( F32 length ) { mDayLen = length; setMaskBits( OrbitMask ); } + + void animate( F32 time, F32 speed ); + +protected: + + Vector mTimeEvents; + + void _updateTimeEvents(); + void _onTimeEvent( const String &identifier ); + + static TimeOfDayUpdateSignal smTimeOfDayUpdateSignal; + + enum NetMaskBits + { + OrbitMask = Parent::NextFreeMask << 0, + AnimateMask = Parent::NextFreeMask << 1 + }; + + void _updatePosition(); + + void _onGhostAlwaysDone(); + + F32 _calcElevation( F32 lat, F32 dec, F32 mer ); + + F32 _calcAzimuth( F32 lat, F32 dec, F32 mer ); + + /// Adds all of our target colors to our COLOR_TARGETS. + void _initColors(); + + /// Adds a color target to our set of targets. + /// + /// @param ele [in] target sun elevation. + /// @param color [in] target color. + /// @param bandMod [in] + /// @param bandColor [in] + void _addColorTarget( F32 ele, const ColorF &color, F32 bandMod, const ColorF &bandColor ); + + // Grab our sun and sky colors based upon sun elevation. + void _getSunColor( ColorF *outColor ) const; + + static bool setTimeOfDay( void *object, const char *index, const char *data ); + static bool setPlay( void *object, const char *index, const char *data ); + static bool setDayLength( void *object, const char *index, const char *data ); + + /* + // Get a pointer to the sun's light object + Sun* GetSunObject(); + // return number between 0 and 1 representing color variance + F32 getColorVariance(); + */ + + // Date tracking stuff + F32 mStartTimeOfDay; ///< The time of day this object begins at. + F32 mDayLen; ///< length of day in real world seconds. + F32 mPrevElevation; ///< The 0-360 normalized elevation for the previous update. + F32 mNextElevation; ///< The 0-360 normalized elevation for the next update. + F32 mTimeOfDay; ///< The zero to one time of day where zero is the start of a day and one is the end. + + F32 mAzimuthOverride; ///< Used to specify an azimuth that will stay constant throughout the day cycle. + + // Global positioning stuff + F32 mAxisTilt; // angle between global equator and tropic + + F32 mAzimuth; // Angle from true north of celestial object in radians + F32 mElevation; // Angle from horizon of celestial object in radians + + VectorF mZenithDirection; // The direction of celestial object at the zenith of its orbit. + + // Scalar applied to time that elapses while the sun is up. + F32 mDayScale; + // Scalar applied to time that elapses while the sun is down. + F32 mNightScale; + + // color management + COLOR_TARGETS mColorTargets; + + F32 mAnimateTime; + F32 mAnimateSpeed; + bool mAnimate; + + /* + ColorF mCurrentColor; + F32 mBandMod; + ColorF mCurrentBandColor; + + // PersistFields preparation + bool mConvertedToRads; + */ + + // Debugging stuff that probably needs to be removed eventaully + bool mPlay; +}; + + +#endif // _TIMEOFDAY_H_ \ No newline at end of file diff --git a/Engine/source/environment/waterBlock.cpp b/Engine/source/environment/waterBlock.cpp new file mode 100644 index 000000000..78438eec2 --- /dev/null +++ b/Engine/source/environment/waterBlock.cpp @@ -0,0 +1,700 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "environment/waterBlock.h" + +#include "core/util/safeDelete.h" +#include "scene/sceneRenderState.h" +#include "scene/sceneManager.h" +#include "lighting/lightInfo.h" +#include "core/stream/bitStream.h" +#include "math/mathIO.h" +#include "console/consoleTypes.h" +#include "gui/3d/guiTSControl.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/gfxDebugEvent.h" +#include "gfx/gfxOcclusionQuery.h" +#include "renderInstance/renderPassManager.h" +#include "sim/netConnection.h" +#include "scene/reflectionManager.h" +#include "ts/tsShapeInstance.h" +#include "postFx/postEffect.h" +#include "math/util/matrixSet.h" + +IMPLEMENT_CO_NETOBJECT_V1(WaterBlock); + +ConsoleDocClass( WaterBlock, + "@brief A block shaped water volume defined by a 3D scale and orientation.\n\n" + + "@see WaterObject for inherited functionality.\n\n" + + "@ingroup Water" +); + +WaterBlock::WaterBlock() +{ + mGridElementSize = 5.0f; + mObjScale.set( 100.0f, 100.0f, 10.0f ); + + mNetFlags.set(Ghostable | ScopeAlways); + + mObjBox.minExtents.set( -0.5f, -0.5f, -0.5f ); + mObjBox.maxExtents.set( 0.5f, 0.5f, 0.5f ); + + mElapsedTime = 0.0f; + mGenerateVB = true; +} + +WaterBlock::~WaterBlock() +{ +} + +bool WaterBlock::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + resetWorldBox(); + addToScene(); + + return true; +} + +void WaterBlock::onRemove() +{ + clearVertBuffers(); + + removeFromScene(); + + Parent::onRemove(); +} + +//----------------------------------------------------------------------------- +// packUpdate +//----------------------------------------------------------------------------- +U32 WaterBlock::packUpdate(NetConnection* con, U32 mask, BitStream* stream) +{ + U32 retMask = Parent::packUpdate(con, mask, stream); + + stream->write( mGridElementSize ); + + if ( stream->writeFlag( mask & UpdateMask ) ) + { + // This is set to allow the user to modify the size of the water dynamically + // in the editor + mathWrite( *stream, mObjScale ); + stream->writeAffineTransform( mObjToWorld ); + } + + return retMask; +} + +//----------------------------------------------------------------------------- +// unpackUpdate +//----------------------------------------------------------------------------- +void WaterBlock::unpackUpdate(NetConnection* con, BitStream* stream) +{ + Parent::unpackUpdate(con, stream); + + F32 gridSize = mGridElementSize; + stream->read( &mGridElementSize ); + if ( gridSize != mGridElementSize ) + mGenerateVB = true; + + if( stream->readFlag() ) // UpdateMask + { + Point3F scale; + mathRead( *stream, &scale ); + + setScale( scale ); + + MatrixF objToWorld; + stream->readAffineTransform( &objToWorld ); + + setTransform( objToWorld ); + } +} + +//----------------------------------------------------------------------------- +// Setup vertex and index buffers +//----------------------------------------------------------------------------- +void WaterBlock::setupVBIB() +{ + clearVertBuffers(); + + const U32 maxIndexedVerts = 65536; // max number of indexed verts with U16 size indices + + if( mObjScale.x < mGridElementSize || + mObjScale.y < mGridElementSize ) + { + F32 oldGridSize = mGridElementSize; + mGridElementSize = getMin(mObjScale.x, mObjScale.y); + logWarning("gridElementSize %g is larger than scale (%g, %g), clamping gridElementSize to %g", + oldGridSize, mObjScale.x, mObjScale.y, mGridElementSize); + } + + Point3F div = getScale() / mGridElementSize; + // Add one to width and height for the edge. + mWidth = (U32)mCeil(div.x) + 1; + mHeight = (U32)mCeil(div.y) + 1; + + if( mWidth > maxIndexedVerts / 2 ) + mWidth = maxIndexedVerts / 2; + + // figure out how many blocks are needed and their size + U32 maxBlockRows = maxIndexedVerts / mWidth; + U32 rowOffset = 0; + + while( (rowOffset+1) < mHeight ) + { + U32 numRows = mHeight - rowOffset; + if( numRows == 1 ) numRows++; + if( numRows > maxBlockRows ) + { + numRows = maxBlockRows; + } + + setupVertexBlock( mWidth, numRows, rowOffset ); + setupPrimitiveBlock( mWidth, numRows ); + + rowOffset += numRows - 1; + } + +} + +//----------------------------------------------------------------------------- +// Set up a block of vertices - the width is always the width of the entire +// waterBlock, so this is a block of full rows. +//----------------------------------------------------------------------------- +void WaterBlock::setupVertexBlock( U32 width, U32 height, U32 rowOffset ) +{ + Point3F pos = getPosition(); + RayInfo rInfo; + VectorF sunVector(-0.61f, 0.354f, 0.707f); + + if ( LIGHTMGR ) + { + LightInfo* linfo = LIGHTMGR->getSpecialLight( LightManager::slSunLightType ); + if ( linfo ) + sunVector = linfo->getDirection(); + } + + sunVector.normalize(); + + U32 numVerts = width * height; + + GFXWaterVertex *verts = new GFXWaterVertex[ numVerts ]; + ColorI waterColor(31, 56, 64, 127); + GFXVertexColor vertCol(waterColor); + + U32 index = 0; + for( U32 i=0; ipoint.x = vertX; + vert->point.y = vertY; + vert->point.z = 0.0; + vert->color = vertCol; + vert->normal.set(0,0,1); + vert->undulateData.set( vertX, vertY ); + vert->horizonFactor.set( 0, 0, 0, 0 ); + + // Calculate the water depth + + /* + vert->depthData.set( 0.0f, 0.0f ); + + Point3F start, end; + Point3F worldPoint = vert->point + pos; + + start.x = end.x = worldPoint.x; + start.y = end.y = worldPoint.y; + start.z = -2000; // Really high, might be over kill + end.z = 2000; // really low, might be overkill + + // Cast a ray to see how deep the water is. We are + // currently just testing for terrain and atlas + // objects, but potentially any object that responds + // to a ray cast could detected. + if(gClientContainer.castRay(start, end, + //StaticObjectType | + InteriorObjectType | + //ShapeBaseObjectType | + //StaticShapeObjectType | + //ItemObjectType | + //StaticTSObjectType | + TerrainObjectType + , &rInfo)) + { + F32 depth = -(rInfo.point.z - pos.z); + + if(depth <= 0.0f) + { + depth = 1.0f; + } + else + { + depth = depth / mVisibilityDepth; + if(depth > 1.0f) + { + depth = 1.0f; + } + + depth = 1.0f - depth; + } + + vert->depthData.x = depth; + } + else + { + vert->depthData.x = 0.0f; + } + + // Cast a ray to do some AO-style shadowing. + F32 &shadow = vert->depthData.y; + + if(gClientContainer.castRay(worldPoint, worldPoint + sunVector * 9000.f, + //StaticObjectType | + InteriorObjectType | + //ShapeBaseObjectType | + //StaticShapeObjectType | + //ItemObjectType | + //StaticTSObjectType | + TerrainObjectType + , &rInfo)) + { + shadow = 0.f; + } + else + { + shadow = 1.f; + } + */ + } + } + + // copy to vertex buffer + GFXVertexBufferHandle * vertBuff = new GFXVertexBufferHandle ; + + vertBuff->set( GFX, numVerts, GFXBufferTypeStatic ); + GFXWaterVertex *vbVerts = vertBuff->lock(); + dMemcpy( vbVerts, verts, sizeof(GFXWaterVertex) * numVerts ); + vertBuff->unlock(); + mVertBuffList.push_back( vertBuff ); + + + delete [] verts; + +} + +//----------------------------------------------------------------------------- +// Set up a block of indices to match the block of vertices. The width is +// always the width of the entire waterBlock, so this is a block of full rows. +//----------------------------------------------------------------------------- +void WaterBlock::setupPrimitiveBlock( U32 width, U32 height ) +{ + AssertFatal( height > 1, "WaterBlock::setupPrimitiveBlock() - invalid height" ); + + // setup vertex / primitive buffers + U32 numIndices = (width-1) * (height-1) * 6; + U16 *indices = new U16[ numIndices ]; + U32 numVerts = width * height; + + // This uses indexed triangle lists instead of strips, but it shouldn't be + // significantly slower if the indices cache well. + + // Rough diagram of the index order + // 0----2----+ ... + // | / | | + // |/ | | + // 1----3----+ ... + // | | | + // | | | + // +----+----+ ... + + U32 index = 0; + for( U32 i=0; i<(height-1); i++ ) + { + for( U32 j=0; j<(width-1); j++, index+=6 ) + { + // Process one quad at a time. Note it will re-use the same indices from + // previous quad, thus optimizing vert cache. Cache will run out at + // end of each row with this implementation however. + indices[index+0] = (i) * mWidth + j; // 0 + indices[index+1] = (i+1) * mWidth + j; // 1 + indices[index+2] = i * mWidth + j+1; // 2 + indices[index+3] = (i+1) * mWidth + j; // 1 + indices[index+4] = (i+1) * mWidth + j+1; // 3 + indices[index+5] = i * mWidth + j+1; // 2 + } + + } + + GFXPrimitiveBufferHandle *indexBuff = new GFXPrimitiveBufferHandle; + + GFXPrimitive pInfo; + pInfo.type = GFXTriangleList; + pInfo.numPrimitives = numIndices / 3; + pInfo.startIndex = 0; + pInfo.minIndex = 0; + pInfo.numVertices = numVerts; + + U16 *ibIndices; + GFXPrimitive *piInput; + indexBuff->set( GFX, numIndices, 1, GFXBufferTypeStatic ); + indexBuff->lock( &ibIndices, &piInput ); + dMemcpy( ibIndices, indices, numIndices * sizeof(U16) ); + dMemcpy( piInput, &pInfo, sizeof(GFXPrimitive) ); + indexBuff->unlock(); + mPrimBuffList.push_back( indexBuff ); + + + delete [] indices; +} + +//------------------------------------------------------------------------------ +// Setup scenegraph data structure for materials +//------------------------------------------------------------------------------ +SceneData WaterBlock::setupSceneGraphInfo( SceneRenderState *state ) +{ + SceneData sgData; + + sgData.lights[0] = LIGHTMGR->getSpecialLight( LightManager::slSunLightType ); + + // fill in water's transform + sgData.objTrans = &getRenderTransform(); + + // fog + sgData.setFogParams( state->getSceneManager()->getFogData() ); + + // misc + sgData.backBuffTex = REFLECTMGR->getRefractTex(); + sgData.reflectTex = mPlaneReflector.reflectTex; + sgData.wireframe = GFXDevice::getWireframe() || smWireframe; + + return sgData; +} + +//----------------------------------------------------------------------------- +// set shader parameters +//----------------------------------------------------------------------------- +void WaterBlock::setShaderParams( SceneRenderState *state, BaseMatInstance *mat, const WaterMatParams ¶mHandles) +{ + // Set variables that will be assigned to shader consts within WaterCommon + // before calling Parent::setShaderParams + + mUndulateMaxDist = F32_MAX; + + Parent::setShaderParams( state, mat, paramHandles ); + + // Now set the rest of the shader consts that are either unique to this + // class or that WaterObject leaves to us to handle... + + MaterialParameters* matParams = mat->getMaterialParameters(); + + // set vertex shader constants + //----------------------------------- + + MatrixF modelMat( getRenderTransform() ); + if ( paramHandles.mModelMatSC->isValid() ) + matParams->set(paramHandles.mModelMatSC, modelMat, GFXSCT_Float4x4); + matParams->setSafe(paramHandles.mGridElementSizeSC, (F32)mGridElementSize); + + // set pixel shader constants + //----------------------------------- + + ColorF c( mWaterFogData.color ); + matParams->setSafe( paramHandles.mBaseColorSC, c ); + + // By default we need to show a true reflection is fullReflect is enabled and + // we are above water. + F32 reflect = mPlaneReflector.isEnabled() && !isUnderwater( state->getCameraPosition() ); + + // If we were occluded the last frame a query was fetched ( not necessarily last frame ) + // and we weren't updated last frame... we don't have a valid texture to show + // so use the cubemap / fake reflection color this frame. + if ( mPlaneReflector.lastUpdateMs != REFLECTMGR->getLastUpdateMs() && mPlaneReflector.isOccluded() ) + reflect = false; + + Point4F reflectParams( mWaterPos.z, 0.0f, 1000.0f, !reflect ); + matParams->setSafe( paramHandles.mReflectParamsSC, reflectParams ); + + VectorF reflectNorm = mReflectNormalUp ? VectorF(0,0,1) : static_cast(mPlaneReflector.refplane); + matParams->setSafe(paramHandles.mReflectNormalSC, reflectNorm ); +} + +void WaterBlock::innerRender( SceneRenderState *state ) +{ + GFXDEBUGEVENT_SCOPE( WaterBlock_innerRender, ColorI( 255, 0, 0 ) ); + + if ( mGenerateVB ) + { + setupVBIB(); + mGenerateVB = false; + } + + // Setup SceneData + SceneData sgData = setupSceneGraphInfo( state ); + const Point3F &camPosition = state->getCameraPosition(); + + // set the material + + S32 matIdx = getMaterialIndex( camPosition ); + + if ( !initMaterial( matIdx ) ) + return; + + BaseMatInstance *mat = mMatInstances[matIdx]; + WaterMatParams matParams = mMatParamHandles[matIdx]; + + // render the geometry + if ( mat ) + { + // setup proj/world transform + mMatrixSet->setWorld(getRenderTransform()); + mMatrixSet->restoreSceneViewProjection(); + + setShaderParams( state, mat, matParams ); + + while ( mat->setupPass( state, sgData ) ) + { + mat->setSceneInfo(state, sgData); + mat->setTransforms(*mMatrixSet, state); + setCustomTextures( matIdx, mat->getCurPass(), matParams ); + + for ( U32 i = 0; i < mVertBuffList.size(); i++ ) + { + GFX->setVertexBuffer( *mVertBuffList[i] ); + GFXPrimitiveBuffer *primBuff = *mPrimBuffList[i]; + GFX->setPrimitiveBuffer( primBuff ); + GFX->drawPrimitives(); + } + } + } +} + +bool WaterBlock::setGridSizeProperty( void *obj, const char *index, const char *data ) +{ + WaterBlock* object = static_cast(obj); + F32 gridSize = dAtof(data); + + Point3F scale = object->getScale(); + + if(gridSize < 0.001f) + { + object->logWarning("gridSize cannot be <= 0, clamping to scale"); + gridSize = getMin(scale.x, scale.y); + } + + if(gridSize > scale.x || gridSize > scale.y) + { + object->logWarning("gridSize cannot be > scale. Your scale is (%g, %g) and your gridsize is %g", + scale.x, scale.y, gridSize); + gridSize = getMin(scale.x, scale.y); + } + + object->mGridElementSize = gridSize; + + // This is a hack so the console system doesn't go in and set our variable + // again, after we've already set it (possibly with a different value...) + return false; +} + +//----------------------------------------------------------------------------- +// initPersistFields +//----------------------------------------------------------------------------- +void WaterBlock::initPersistFields() +{ + addGroup( "WaterBlock" ); + addProtectedField( "gridElementSize", TypeF32, Offset( mGridElementSize, WaterBlock ), + &setGridSizeProperty, &defaultProtectedGetFn, "Spacing between vertices in the WaterBlock mesh" ); + addProtectedField( "gridSize", TypeF32, Offset( mGridElementSize, WaterBlock ), + &setGridSizeProperty, &defaultProtectedGetFn, "Duplicate of gridElementSize for backwards compatility" ); + endGroup( "WaterBlock" ); + + Parent::initPersistFields(); +} + +bool WaterBlock::isUnderwater( const Point3F &pnt ) const +{ + // Transform point into object space so we can test if it is within + // the WaterBlock's object box, include rotation/scale. + + Point3F objPnt = pnt; + mWorldToObj.mulP( pnt, &objPnt ); + + objPnt.z -= 0.1f; + + Box3F testBox = mObjBox; + testBox.scale( mObjScale ); + + // We already tested if below the surface plane, + // so clamping the z height of the box is not really necessary. + testBox.maxExtents.z = testBox.getCenter().z; + + if ( testBox.isContained( objPnt ) ) + return true; + + return false; +} + +void WaterBlock::clearVertBuffers() +{ + for( U32 i=0; i 1.0f ) + return false; + + info->t = hit; + info->object = this; + info->point = start + ( ( end - start ) * hit ); + info->normal = norm; + info->material = mMatInstances[WaterMat]; + + return mObjBox.isContained(info->point); +} + +F32 WaterBlock::getWaterCoverage( const Box3F &testBox ) const +{ + Box3F wbox = getWorldBox(); + wbox.maxExtents.z = wbox.getCenter().z; + + F32 coverage = 0.0f; + + if ( wbox.isOverlapped(testBox) ) + { + if (wbox.maxExtents.z < testBox.maxExtents.z) + coverage = (wbox.maxExtents.z - testBox.minExtents.z) / (testBox.maxExtents.z - testBox.minExtents.z); + else + coverage = 1.0f; + } + + return coverage; +} + +F32 WaterBlock::getSurfaceHeight( const Point2F &pos ) const +{ + if ( !mWorldBox.isContained( pos ) ) + return -1.0f; + + return getPosition().z; +} + +void WaterBlock::_getWaterPlane( const Point3F &camPos, PlaneF &outPlane, Point3F &outPos ) +{ + outPos = getPosition(); + if ( mReflectNormalUp ) + outPlane.set( outPos, Point3F(0,0,1) ); + else + { + Point3F normal; + getRenderTransform().getColumn( 2, &normal ); + outPlane.set( outPos, normal ); + } +} + +F32 WaterBlock::distanceTo( const Point3F& point ) const +{ + Box3F waterBox = getWorldBox(); + waterBox.maxExtents.z = getPosition().z; + + return waterBox.getDistanceToPoint( point ); +} diff --git a/Engine/source/environment/waterBlock.h b/Engine/source/environment/waterBlock.h new file mode 100644 index 000000000..fbbb04246 --- /dev/null +++ b/Engine/source/environment/waterBlock.h @@ -0,0 +1,152 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _WATERBLOCK_H_ +#define _WATERBLOCK_H_ + +#ifndef _GAMEBASE_H_ +#include "T3D/gameBase/gameBase.h" +#endif +#ifndef _GFXDEVICE_H_ +#include "gfx/gfxDevice.h" +#endif +#ifndef _SCENEDATA_H_ +#include "materials/sceneData.h" +#endif +#ifndef _MATINSTANCE_H_ +#include "materials/matInstance.h" +#endif +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif +#ifndef _RENDERPASSMANAGER_H_ +#include "renderInstance/renderPassManager.h" +#endif +#ifndef _WATEROBJECT_H_ +#include "waterObject.h" +#endif + + +//***************************************************************************** +// WaterBlock +//***************************************************************************** +class WaterBlock : public WaterObject +{ + typedef WaterObject Parent; + +public: + + // LEGACY support + enum EWaterType + { + eWater = 0, + eOceanWater = 1, + eRiverWater = 2, + eStagnantWater = 3, + eLava = 4, + eHotLava = 5, + eCrustyLava = 6, + eQuicksand = 7, + }; + +private: + + enum MaskBits { + UpdateMask = Parent::NextFreeMask, + NextFreeMask = Parent::NextFreeMask << 1 + }; + + // vertex / index buffers + Vector< GFXVertexBufferHandle* > mVertBuffList; + Vector mPrimBuffList; + GFXVertexBufferHandle mRadialVertBuff; + GFXPrimitiveBufferHandle mRadialPrimBuff; + + // misc + F32 mGridElementSize; + U32 mWidth; + U32 mHeight; + F32 mElapsedTime; + GFXTexHandle mBumpTex; + bool mGenerateVB; + + // reflect plane + //ReflectPlane mReflectPlane; + //Point3F mReflectPlaneWorldPos; + + GFXTexHandle mReflectTex; + + // Stateblocks + GFXStateBlockRef mUnderwaterSB; + + void setupVertexBlock( U32 width, U32 height, U32 rowOffset ); + void setupPrimitiveBlock( U32 width, U32 height ); + void setMultiPassProjection(); + void clearVertBuffers(); + + static bool setGridSizeProperty( void *object, const char *index, const char *data ); +protected: + + //------------------------------------------------------- + // Standard engine functions + //------------------------------------------------------- + bool onAdd(); + void onRemove(); + U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream); + void unpackUpdate(NetConnection *conn, BitStream *stream); + + bool castRay(const Point3F &start, const Point3F &end, RayInfo* info); + +public: + WaterBlock(); + virtual ~WaterBlock(); + + DECLARE_CONOBJECT(WaterBlock); + + static void initPersistFields(); + void onStaticModified( const char* slotName, const char*newValue = NULL ); + virtual void inspectPostApply(); + virtual void setTransform( const MatrixF & mat ); + virtual void setScale( const Point3F &scale ); + + // WaterObject + virtual F32 getWaterCoverage( const Box3F &worldBox ) const; + virtual F32 getSurfaceHeight( const Point2F &pos ) const; + virtual bool isUnderwater( const Point3F &pnt ) const; + + // WaterBlock + bool isPointSubmerged ( const Point3F &pos, bool worldSpace = true ) const{ return true; } + + // SceneObject. + virtual F32 distanceTo( const Point3F& pos ) const; + +protected: + + // WaterObject + virtual void setShaderParams( SceneRenderState *state, BaseMatInstance *mat, const WaterMatParams ¶mHandles ); + virtual SceneData setupSceneGraphInfo( SceneRenderState *state ); + virtual void setupVBIB(); + virtual void innerRender( SceneRenderState *state ); + virtual void _getWaterPlane( const Point3F &camPos, PlaneF &outPlane, Point3F &outPos ); +}; + +#endif // _WATERBLOCK_H_ diff --git a/Engine/source/environment/waterObject.cpp b/Engine/source/environment/waterObject.cpp new file mode 100644 index 000000000..470d27b8a --- /dev/null +++ b/Engine/source/environment/waterObject.cpp @@ -0,0 +1,1205 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "environment/waterObject.h" + +#include "console/consoleTypes.h" +#include "materials/materialParameters.h" +#include "materials/baseMatInstance.h" +#include "materials/materialManager.h" +#include "materials/customMaterialDefinition.h" +#include "materials/sceneData.h" +#include "core/stream/bitStream.h" +#include "scene/reflectionManager.h" +#include "scene/sceneRenderState.h" +#include "lighting/lightInfo.h" +#include "math/mathIO.h" +#include "postFx/postEffect.h" +#include "T3D/gameBase/gameConnection.h" +#include "T3D/shapeBase.h" +#include "gfx/gfxOcclusionQuery.h" +#include "gfx/sim/cubemapData.h" +#include "math/util/matrixSet.h" +#include "sfx/sfxAmbience.h" +#include "T3D/sfx/sfx3DWorld.h" +#include "sfx/sfxTypes.h" + + +GFXImplementVertexFormat( GFXWaterVertex ) +{ + addElement( "POSITION", GFXDeclType_Float3 ); + addElement( "NORMAL", GFXDeclType_Float3 ); + addElement( "COLOR", GFXDeclType_Color ); + addElement( "TEXCOORD", GFXDeclType_Float2, 0 ); + addElement( "TEXCOORD", GFXDeclType_Float4, 1 ); +} + +void WaterMatParams::clear() +{ + mRippleMatSC = NULL; + mRippleDirSC = NULL; + mRippleTexScaleSC = NULL; + mRippleSpeedSC = NULL; + mRippleMagnitudeSC = NULL; + mFoamDirSC = NULL; + mFoamTexScaleSC = NULL; + mFoamSpeedSC = NULL; + mFoamOpacitySC = NULL; + mWaveDirSC = NULL; + mWaveDataSC = NULL; + mReflectTexSizeSC = NULL; + mBaseColorSC = NULL; + mMiscParamsSC = NULL; + mReflectParamsSC = NULL; + mReflectNormalSC = NULL; + mHorizonPositionSC = NULL; + mFogParamsSC = NULL; + mMoreFogParamsSC = NULL; + mFarPlaneDistSC = NULL; + mWetnessParamsSC = NULL; + mDistortionParamsSC = NULL; + mUndulateMaxDistSC = NULL; + mAmbientColorSC = NULL; + mLightDirSC = NULL; + mFoamParamsSC = NULL; + mGridElementSizeSC = NULL; + mElapsedTimeSC = NULL; + mFoamSamplerSC = NULL; + mRippleSamplerSC = NULL; + mCubemapSamplerSC = NULL; + mSpecularParamsSC = NULL; + mDepthGradMaxSC = NULL; + mReflectivitySC = NULL; +} + +void WaterMatParams::init( BaseMatInstance* matInst ) +{ + clear(); + + mRippleMatSC = matInst->getMaterialParameterHandle( "$rippleMat" ); + mRippleDirSC = matInst->getMaterialParameterHandle( "$rippleDir" ); + mRippleTexScaleSC = matInst->getMaterialParameterHandle( "$rippleTexScale" ); + mRippleSpeedSC = matInst->getMaterialParameterHandle( "$rippleSpeed" ); + mRippleMagnitudeSC = matInst->getMaterialParameterHandle( "$rippleMagnitude" ); + mFoamDirSC = matInst->getMaterialParameterHandle( "$foamDir" ); + mFoamTexScaleSC = matInst->getMaterialParameterHandle( "$foamTexScale" ); + mFoamSpeedSC = matInst->getMaterialParameterHandle( "$foamSpeed" ); + mFoamOpacitySC = matInst->getMaterialParameterHandle( "$foamOpacity" ); + mWaveDirSC = matInst->getMaterialParameterHandle( "$waveDir" ); + mWaveDataSC = matInst->getMaterialParameterHandle( "$waveData" ); + mReflectTexSizeSC = matInst->getMaterialParameterHandle( "$reflectTexSize" ); + mBaseColorSC = matInst->getMaterialParameterHandle( "$baseColor" ); + mMiscParamsSC = matInst->getMaterialParameterHandle( "$miscParams" ); + mReflectParamsSC = matInst->getMaterialParameterHandle( "$reflectParams" ); + mReflectNormalSC = matInst->getMaterialParameterHandle( "$reflectNormal" ); + mHorizonPositionSC = matInst->getMaterialParameterHandle( "$horizonPos" ); + mFogParamsSC = matInst->getMaterialParameterHandle( "$fogParams" ); + mMoreFogParamsSC = matInst->getMaterialParameterHandle( "$moreFogParams" ); + mFarPlaneDistSC = matInst->getMaterialParameterHandle( "$farPlaneDist" ); + mWetnessParamsSC = matInst->getMaterialParameterHandle( "$wetnessParams" ); + mDistortionParamsSC = matInst->getMaterialParameterHandle( "$distortionParams" ); + mUndulateMaxDistSC = matInst->getMaterialParameterHandle( "$undulateMaxDist" ); + mAmbientColorSC = matInst->getMaterialParameterHandle( "$ambientColor" ); + mLightDirSC = matInst->getMaterialParameterHandle( "$inLightVec" ); + mFoamParamsSC = matInst->getMaterialParameterHandle( "$foamParams" ); + mGridElementSizeSC = matInst->getMaterialParameterHandle( "$gridElementSize" ); + mElapsedTimeSC = matInst->getMaterialParameterHandle( "$elapsedTime" ); + mModelMatSC = matInst->getMaterialParameterHandle( "$modelMat" ); + mFoamSamplerSC = matInst->getMaterialParameterHandle( "$foamMap" ); + mRippleSamplerSC = matInst->getMaterialParameterHandle( "$bumpMap" ); + mCubemapSamplerSC = matInst->getMaterialParameterHandle( "$skyMap" ); + mSpecularParamsSC = matInst->getMaterialParameterHandle( "$specularParams" ); + mDepthGradMaxSC = matInst->getMaterialParameterHandle( "$depthGradMax" ); + mReflectivitySC = matInst->getMaterialParameterHandle( "$reflectivity" ); +} + + +bool WaterObject::smWireframe = false; +bool WaterObject::smDisableTrueReflections = false; + +//------------------------------------------------------------------------- +// WaterObject Class +//------------------------------------------------------------------------- + +IMPLEMENT_CONOBJECT( WaterObject ); + +ConsoleDocClass( WaterObject, + "@brief Abstract base class for representing a body of water.\n\n" + + "%WaterObject is abstract and may not be created. It defines functionality " + "shared by its derived classes.\n\n" + + "%WaterObject exposes many fields for controlling it visual quality.\n\n" + + "%WaterObject surface rendering has the following general features:\n" + "\t- Waves represented by vertex undulation and user paramaters.\n" + "\t- Ripples represented by a normal map and user parameters.\n" + "\t- Refraction of underwater objects.\n" + "\t- Dynamic planar reflection or static cubemap reflection.\n" + "\t- Paramable water fog and color shift.\n\n" + + "It will, however, look significantly different depending on the LightingManager " + "that is active. With Basic Lighting, we do not have a prepass texture to " + "lookup per-pixel depth and therefore cannot use our rendering techniques that depend on it.\n\n" + + "In particular, the following field groups are not used under Basic Lighting:\n" + "\t- Underwater Fogging \n" + "\t- Misc \n" + "\t- Distortion \n" + "\t- And foam related fields under the %WaterObject group.\n\n" + + "%WaterObject also defines several fields for gameplay use and objects " + "that support buoyancy.\n\n" + + "@ingroup Water" +); + +WaterObject::WaterObject() + : mViscosity( 1.0f ), + mDensity( 1.0f ), + mReflectivity( 0.5f ), + mReflectNormalUp( true ), + mDistortStartDist( 0.1f ), + mDistortEndDist( 20.0f ), + mDistortFullDepth( 3.5f ), + mUndulateMaxDist(50.0f), + mOverallFoamOpacity( 1.0f ), + mFoamMaxDepth( 2.0f ), + mFoamAmbientLerp( 0.5f ), + mFoamRippleInfluence( 0.05f ), + mUnderwaterPostFx( NULL ), + mLiquidType( "Water" ), + mFresnelBias( 0.3f ), + mFresnelPower( 6.0f ), + mClarity( 0.5f ), + mBasicLighting( false ), + mMiscParamW( 0.0f ), + mOverallWaveMagnitude( 1.0f ), + mOverallRippleMagnitude( 0.1f ), + mCubemap( NULL ), + mSoundAmbience( NULL ), + mSpecularPower( 48.0f ), + mSpecularColor( 1.0f, 1.0f, 1.0f, 1.0f ), + mDepthGradientMax( 50.0f ), + mEmissive( false ), + mUnderwaterColor(9, 6, 5, 240) +{ + mTypeMask = WaterObjectType | StaticObjectType; + + for( U32 i=0; i < MAX_WAVES; i++ ) + { + mRippleDir[i].set( 0.0f, 0.0f ); + mRippleSpeed[i] = 0.0f; + mRippleTexScale[i].set( 0.0f, 0.0f ); + + mWaveDir[i].set( 0.0f, 0.0f ); + mWaveSpeed[i] = 0.0f; + mWaveMagnitude[i] = 0.0f; + } + + for ( U32 i = 0; i < MAX_FOAM; i++ ) + { + mFoamDir[i].set( 0.0f, 0.0f ); + mFoamSpeed[i] = 0.0f; + mFoamTexScale[i].set( 0.0f, 0.0f ); + mFoamOpacity[i] = 0.0f; + } + + mFoamDir[0].set( 1, 0 ); + mFoamDir[1].set( 0, 1 ); + mFoamTexScale[0].set( 1, 1 ); + mFoamTexScale[1].set( 3, 3 ); + + mRippleMagnitude[0] = 1.0f; + mRippleMagnitude[1] = 1.0f; + mRippleMagnitude[2] = 0.3f; + + mWaterFogData.density = 0.1f; + mWaterFogData.densityOffset = 1.0f; + mWaterFogData.wetDepth = 1.5f; + mWaterFogData.wetDarkening = 0.2f; + mWaterFogData.color = ColorI::BLUE; + + mSurfMatName[WaterMat] = "WaterMat"; + mSurfMatName[UnderWaterMat] = "UnderWaterMat"; + mSurfMatName[BasicWaterMat] = "WaterBasicMat"; + mSurfMatName[BasicUnderWaterMat] = "UnderWaterBasicMat"; + + dMemset( mMatInstances, 0, sizeof(mMatInstances) ); + + mWaterPos.set( 0,0,0 ); + mWaterPlane.set( mWaterPos, Point3F(0,0,1) ); + + mGenerateVB = true; + + mMatrixSet = reinterpret_cast(dMalloc_aligned(sizeof(MatrixSet), 16)); + constructInPlace(mMatrixSet); +} + +WaterObject::~WaterObject() +{ + dFree_aligned(mMatrixSet); +} + + +void WaterObject::initPersistFields() +{ + addGroup( "WaterObject" ); + + addProtectedField( "density", TypeF32, Offset( mDensity, WaterObject ), &WaterObject::_checkDensity, &defaultProtectedGetFn, "Affects buoyancy of an object, thus affecting the Z velocity of a player (jumping, falling, etc."); + addField( "viscosity", TypeF32, Offset( mViscosity, WaterObject ), "Affects drag force applied to an object submerged in this container." ); + addField( "liquidType", TypeRealString, Offset( mLiquidType, WaterObject ), "Liquid type of WaterBlock, such as water, ocean, lava" + " Currently only Water is defined and used."); + addField( "baseColor", TypeColorI, Offset( mWaterFogData.color, WaterObject ), "Changes color of water fog." ); + addField( "fresnelBias", TypeF32, Offset( mFresnelBias, WaterObject ), "Extent of fresnel affecting reflection fogging." ); + addField( "fresnelPower", TypeF32, Offset( mFresnelPower, WaterObject ), "Measures intensity of affect on reflection based on fogging." ); + addField( "specularPower", TypeF32, Offset( mSpecularPower, WaterObject ), "Power used for specularity on the water surface ( sun only )." ); + addField( "specularColor", TypeColorF, Offset( mSpecularColor, WaterObject ), "Color used for specularity on the water surface ( sun only )." ); + addField( "emissive", TypeBool, Offset( mEmissive, WaterObject ), "When true the water colors don't react to changes to environment lighting." ); + + addArray( "Waves (vertex undulation)", MAX_WAVES ); + + addField( "waveDir", TypePoint2F, Offset( mWaveDir, WaterObject ), MAX_WAVES, "Direction waves flow toward shores." ); + addField( "waveSpeed", TypeF32, Offset( mWaveSpeed, WaterObject ), MAX_WAVES, "Speed of water undulation." ); + addField( "waveMagnitude", TypeF32, Offset( mWaveMagnitude, WaterObject ), MAX_WAVES, "Height of water undulation." ); + + endArray( "Waves (vertex undulation)" ); + + addField( "overallWaveMagnitude", TypeF32, Offset( mOverallWaveMagnitude, WaterObject ), "Master variable affecting entire body" + " of water's undulation" ); + + addField( "rippleTex", TypeImageFilename, Offset( mRippleTexName, WaterObject ), "Normal map used to simulate small surface ripples" ); + + addArray( "Ripples (texture animation)", MAX_WAVES ); + + addField( "rippleDir", TypePoint2F, Offset( mRippleDir, WaterObject ), MAX_WAVES, "Modifies the direction of ripples on the surface." ); + addField( "rippleSpeed", TypeF32, Offset( mRippleSpeed, WaterObject ), MAX_WAVES, "Modifies speed of surface ripples."); + addField( "rippleTexScale", TypePoint2F, Offset( mRippleTexScale, WaterObject ), MAX_WAVES, "Intensifies the affect of the normal map " + "applied to the surface."); + addField( "rippleMagnitude", TypeF32, Offset( mRippleMagnitude, WaterObject ), MAX_WAVES, "Intensifies the vertext modification of the surface." ); + + endArray( "Ripples (texture animation)" ); + + addField( "overallRippleMagnitude", TypeF32, Offset( mOverallRippleMagnitude, WaterObject ), "Master variable affecting entire surface"); + + addField( "foamTex", TypeImageFilename, Offset( mFoamTexName, WaterObject ), "Diffuse texture for foam in shallow water (advanced lighting only)" ); + + addArray( "Foam", MAX_FOAM ); + + addField( "foamDir", TypePoint2F, Offset( mFoamDir, WaterObject ), MAX_FOAM, "" ); + addField( "foamSpeed", TypeF32, Offset( mFoamSpeed, WaterObject ), MAX_FOAM, ""); + addField( "foamTexScale", TypePoint2F, Offset( mFoamTexScale, WaterObject ), MAX_FOAM, "" + "applied to the surface."); + addField( "foamOpacity", TypeF32, Offset( mFoamOpacity, WaterObject ), MAX_FOAM, "" ); + + endArray( "Foam" ); + + addField( "overallFoamOpacity", TypeF32, Offset( mOverallFoamOpacity, WaterObject ), "" ); + addField( "foamMaxDepth", TypeF32, Offset( mFoamMaxDepth, WaterObject ), "" ); + addField( "foamAmbientLerp", TypeF32, Offset( mFoamAmbientLerp, WaterObject ), "" ); + addField( "foamRippleInfluence", TypeF32, Offset( mFoamRippleInfluence, WaterObject ), "" ); + + endGroup( "WaterObject" ); + + addGroup( "Reflect" ); + + addField( "cubemap", TypeCubemapName, Offset( mCubemapName, WaterObject ), "Cubemap used instead of reflection texture if fullReflect is off." ); + + addProtectedField( "fullReflect", TypeBool, Offset( mFullReflect, WaterObject ), + &WaterObject::_setFullReflect, + &defaultProtectedGetFn, + "Enables dynamic reflection rendering." ); + + addField( "reflectivity", TypeF32, Offset( mReflectivity, WaterObject ), "Overall scalar to the reflectivity of the water surface." ); + addField( "reflectPriority", TypeF32, Offset( mReflectorDesc.priority, WaterObject ), "Affects the sort order of reflected objects." ); + addField( "reflectMaxRateMs", TypeS32, Offset( mReflectorDesc.maxRateMs, WaterObject ), "Affects the sort time of reflected objects." ); + //addField( "reflectMaxDist", TypeF32, Offset( mReflectMaxDist, WaterObject ), "vert distance at which only cubemap color is used" ); + //addField( "reflectMinDist", TypeF32, Offset( mReflectMinDist, WaterObject ), "vert distance at which only reflection color is used" ); + addField( "reflectDetailAdjust", TypeF32, Offset( mReflectorDesc.detailAdjust, WaterObject ), "scale up or down the detail level for objects rendered in a reflection" ); + addField( "reflectNormalUp", TypeBool, Offset( mReflectNormalUp, WaterObject ), "always use z up as the reflection normal" ); + addField( "useOcclusionQuery", TypeBool, Offset( mReflectorDesc.useOcclusionQuery, WaterObject ), "turn off reflection rendering when occluded (delayed)." ); + addField( "reflectTexSize", TypeS32, Offset( mReflectorDesc.texSize, WaterObject ), "The texture size used for reflections (square)" ); + + endGroup( "Reflect" ); + + addGroup( "Underwater Fogging" ); + + addField( "waterFogDensity", TypeF32, Offset( mWaterFogData.density, WaterObject ), "Intensity of underwater fogging." ); + addField( "waterFogDensityOffset", TypeF32, Offset( mWaterFogData.densityOffset, WaterObject ), "Delta, or limit, applied to waterFogDensity." ); + addField( "wetDepth", TypeF32, Offset( mWaterFogData.wetDepth, WaterObject ), "The depth in world units at which full darkening will be received," + " giving a wet look to objects underwater." ); + addField( "wetDarkening", TypeF32, Offset( mWaterFogData.wetDarkening, WaterObject ), "The refract color intensity scaled at wetDepth." ); + + endGroup( "Underwater Fogging" ); + + addGroup( "Misc" ); + + addField( "depthGradientTex", TypeImageFilename, Offset( mDepthGradientTexName, WaterObject ), "1D texture defining the base water color by depth" ); + addField( "depthGradientMax", TypeF32, Offset( mDepthGradientMax, WaterObject ), "Depth in world units, the max range of the color gradient texture." ); + + endGroup( "Misc" ); + + addGroup( "Distortion" ); + + addField( "distortStartDist", TypeF32, Offset( mDistortStartDist, WaterObject ), "Determines start of distortion effect where water" + " surface intersects the camera near plane."); + addField( "distortEndDist", TypeF32, Offset( mDistortEndDist, WaterObject ), "Max distance that distortion algorithm is performed. " + "The lower, the more distorted the effect."); + addField( "distortFullDepth", TypeF32, Offset( mDistortFullDepth, WaterObject ), "Determines the scaling down of distortion " + "in shallow water."); + + endGroup( "Distortion" ); + + addGroup( "Basic Lighting" ); + + addField( "clarity", TypeF32, Offset( mClarity, WaterObject ), "Relative opacity or transparency of the water surface." ); + addField( "underwaterColor", TypeColorI, Offset( mUnderwaterColor, WaterObject ), "Changes the color shading of objects beneath" + " the water surface."); + + endGroup( "Basic Lighting" ); + + addGroup( "Sound" ); + + addField( "soundAmbience", TypeSFXAmbienceName, Offset( mSoundAmbience, WaterObject ), "Ambient sound environment when listener is submerged." ); + + endGroup( "Sound" ); + + Parent::initPersistFields(); + + Con::addVariable( "$WaterObject::wireframe", TypeBool, &smWireframe, "If true, will render the wireframe of the WaterObject.\n" + "@ingroup Water\n"); +} + +void WaterObject::consoleInit() +{ + Parent::consoleInit(); + + Con::addVariable( "$pref::Water::disableTrueReflections", TypeBool, &WaterObject::smDisableTrueReflections, + "Force all water objects to use static cubemap reflections.\n" + "@ingroup Water"); +} + +void WaterObject::inspectPostApply() +{ + Parent::inspectPostApply(); + + setMaskBits( UpdateMask | WaveMask | TextureMask | SoundMask ); +} + +bool WaterObject::processArguments( S32 argc, const char **argv ) +{ + if( typeid( *this ) == typeid( WaterObject ) ) + { + Con::errorf( ConsoleLogEntry::Script, "WaterObject is an abstract class, only its child classes may be allocated." ); + return false; + } + else + return Parent::processArguments( argc, argv ); +} + +bool WaterObject::_setFullReflect( void *object, const char *index, const char *data ) +{ + WaterObject *water = static_cast( object ); + water->mFullReflect = dAtob( data ); + + if ( water->isProperlyAdded() && water->isClientObject() ) + { + bool isEnabled = water->mPlaneReflector.isEnabled(); + + bool enable = water->mFullReflect && !smDisableTrueReflections; + + if ( enable && !isEnabled ) + water->mPlaneReflector.registerReflector( water, &water->mReflectorDesc ); + else if ( !enable && isEnabled ) + water->mPlaneReflector.unregisterReflector(); + } + + return false; +} + +bool WaterObject::_checkDensity( void *object, const char *index, const char *data ) +{ + //Water densities above 1000 shoot the player high and fast into the air. + //value clamped to prevent errors. + WaterObject *water = static_cast( object ); + water->mDensity = mClampF(dAtof( data ), 0.0f, 1000.0f); + + return false; +} + +U32 WaterObject::packUpdate( NetConnection * conn, U32 mask, BitStream *stream ) +{ + U32 retMask = Parent::packUpdate( conn, mask, stream ); + + if ( stream->writeFlag( mask & UpdateMask ) ) + { + stream->write( mDensity ); + stream->write( mViscosity ); + stream->write( mLiquidType ); + + if ( stream->writeFlag( mFullReflect ) ) + { + stream->write( mReflectorDesc.priority ); + stream->writeInt( mReflectorDesc.maxRateMs, 32 ); + //stream->write( mReflectMaxDist ); + //stream->write( mReflectMinDist ); + stream->write( mReflectorDesc.detailAdjust ); + stream->writeFlag( mReflectNormalUp ); + stream->writeFlag( mReflectorDesc.useOcclusionQuery ); + stream->writeInt( mReflectorDesc.texSize, 32 ); + } + + stream->write( mReflectivity ); + + stream->write( mWaterFogData.density ); + stream->write( mWaterFogData.densityOffset ); + stream->write( mWaterFogData.wetDepth ); + stream->write( mWaterFogData.wetDarkening ); + + stream->write( mDistortStartDist ); + stream->write( mDistortEndDist ); + stream->write( mDistortFullDepth ); + + stream->write( mDepthGradientMax ); + stream->writeFlag( mEmissive ); + + stream->write( mFoamMaxDepth ); + stream->write( mFoamAmbientLerp ); + stream->write( mFoamRippleInfluence ); + + stream->write( mWaterFogData.color ); + + stream->write( mFresnelBias ); + stream->write( mFresnelPower ); + + Point4F specularData( mSpecularColor.red, mSpecularColor.green, mSpecularColor.blue, mSpecularPower ); + mathWrite( *stream, specularData ); + + stream->write( mClarity ); + stream->write( mUnderwaterColor ); + + stream->write( mOverallRippleMagnitude ); + stream->write( mOverallWaveMagnitude ); + stream->write( mOverallFoamOpacity ); + } + + if ( stream->writeFlag( mask & WaveMask ) ) + { + for( U32 i=0; iwrite( mRippleSpeed[i] ); + mathWrite( *stream, mRippleDir[i] ); + mathWrite( *stream, mRippleTexScale[i] ); + stream->write( mRippleMagnitude[i] ); + + stream->write( mWaveSpeed[i] ); + mathWrite( *stream, mWaveDir[i] ); + stream->write( mWaveMagnitude[i] ); + } + + for ( U32 i = 0; i < MAX_FOAM; i++ ) + { + stream->write( mFoamSpeed[i] ); + mathWrite( *stream, mFoamDir[i] ); + mathWrite( *stream, mFoamTexScale[i] ); + stream->write( mFoamOpacity[i] ); + } + } + + if ( stream->writeFlag( mask & MaterialMask ) ) + { + for ( U32 i = 0; i < NumMatTypes; i++ ) + stream->write( mSurfMatName[i] ); + } + + if ( stream->writeFlag( mask & TextureMask ) ) + { + stream->write( mRippleTexName ); + stream->write( mDepthGradientTexName ); + stream->write( mFoamTexName ); + stream->write( mCubemapName ); + } + + if( stream->writeFlag( mask & SoundMask ) ) + sfxWrite( stream, mSoundAmbience ); + + return retMask; +} + +void WaterObject::unpackUpdate( NetConnection * conn, BitStream *stream ) +{ + Parent::unpackUpdate( conn, stream ); + + // UpdateMask + if ( stream->readFlag() ) + { + stream->read( &mDensity ); + stream->read( &mViscosity ); + stream->read( &mLiquidType ); + + if ( stream->readFlag() ) + { + mFullReflect = true; + stream->read( &mReflectorDesc.priority ); + mReflectorDesc.maxRateMs = stream->readInt( 32 ); + //stream->read( &mReflectMaxDist ); + //stream->read( &mReflectMinDist ); + stream->read( &mReflectorDesc.detailAdjust ); + mReflectNormalUp = stream->readFlag(); + mReflectorDesc.useOcclusionQuery = stream->readFlag(); + mReflectorDesc.texSize = stream->readInt( 32 ); + + if ( isProperlyAdded() && !mPlaneReflector.isEnabled() && !smDisableTrueReflections ) + mPlaneReflector.registerReflector( this, &mReflectorDesc ); + } + else + { + mFullReflect = false; + if ( isProperlyAdded() && mPlaneReflector.isEnabled() ) + mPlaneReflector.unregisterReflector(); + } + + stream->read( &mReflectivity ); + + stream->read( &mWaterFogData.density ); + stream->read( &mWaterFogData.densityOffset ); + stream->read( &mWaterFogData.wetDepth ); + stream->read( &mWaterFogData.wetDarkening ); + + stream->read( &mDistortStartDist ); + stream->read( &mDistortEndDist ); + stream->read( &mDistortFullDepth ); + + stream->read( &mDepthGradientMax ); + mEmissive = stream->readFlag(); + + stream->read( &mFoamMaxDepth ); + stream->read( &mFoamAmbientLerp ); + stream->read( &mFoamRippleInfluence ); + + stream->read( &mWaterFogData.color ); + + stream->read( &mFresnelBias ); + stream->read( &mFresnelPower ); + + Point4F specularData; + mathRead( *stream, &specularData ); + mSpecularColor.set( specularData.x, specularData.y, specularData.z, 1.0f ); + mSpecularPower = specularData.w; + + stream->read( &mClarity ); + stream->read( &mUnderwaterColor ); + + stream->read( &mOverallRippleMagnitude ); + stream->read( &mOverallWaveMagnitude ); + stream->read( &mOverallFoamOpacity ); + } + + // WaveMask + if ( stream->readFlag() ) + { + for( U32 i=0; iread( &mRippleSpeed[i] ); + mathRead( *stream, &mRippleDir[i] ); + mathRead( *stream, &mRippleTexScale[i] ); + stream->read( &mRippleMagnitude[i] ); + + stream->read( &mWaveSpeed[i] ); + mathRead( *stream, &mWaveDir[i] ); + stream->read( &mWaveMagnitude[i] ); + } + + for ( U32 i = 0; i < MAX_FOAM; i++ ) + { + stream->read( &mFoamSpeed[i] ); + mathRead( *stream, &mFoamDir[i] ); + mathRead( *stream, &mFoamTexScale[i] ); + stream->read( &mFoamOpacity[i] ); + } + } + + // MaterialMask + if ( stream->readFlag() ) + { + for ( U32 i = 0; i < NumMatTypes; i++ ) + stream->read( &mSurfMatName[i] ); + + if ( isProperlyAdded() ) + { + // So they will be reloaded on next use. + cleanupMaterials(); + } + } + + // TextureMask + if ( stream->readFlag() ) + { + stream->read( &mRippleTexName ); + stream->read( &mDepthGradientTexName ); + stream->read( &mFoamTexName ); + stream->read( &mCubemapName ); + + if ( isProperlyAdded() ) + initTextures(); + } + + // Sound environment. + if( stream->readFlag() ) + { + String errorStr; + if( !sfxReadAndResolve( stream, &mSoundAmbience, errorStr ) ) + Con::errorf( "WaterObject::unpackUpdate - pad packet: %s", errorStr.c_str() ); + + if( isProperlyAdded() && gSFX3DWorld ) + gSFX3DWorld->notifyChanged( this ); + } +} + +void WaterObject::prepRenderImage( SceneRenderState *state ) +{ + PROFILE_SCOPE(WaterObject_prepRenderImage); + + // Are we in Basic Lighting? + mBasicLighting = dStricmp( LIGHTMGR->getId(), "BLM" ) == 0; + mUnderwater = isUnderwater( state->getCameraPosition() ); + + // We only render during the normal diffuse render pass. + if( !state->isDiffusePass() ) + return; + + // Setup scene transforms + mMatrixSet->setSceneView(GFX->getWorldMatrix()); + mMatrixSet->setSceneProjection(GFX->getProjectionMatrix()); + + _getWaterPlane( state->getCameraPosition(), mWaterPlane, mWaterPos ); + mWaterFogData.plane = mWaterPlane; + mPlaneReflector.refplane = mWaterPlane; + + updateUnderwaterEffect( state ); + + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &WaterObject::renderObject ); + ri->type = RenderPassManager::RIT_Water; + ri->defaultKey = 1; + state->getRenderPass()->addInst( ri ); + + //mRenderUpdateCount++; +} + +void WaterObject::renderObject( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ) +{ + if ( overrideMat ) + return; + + // TODO: Revive projection z-bias at some point. + // The current issue with this method of fixing z-fighting + // in the WaterBlock is that a constant bias does not alleviate + // the issue at the extreme end of the view range. + //GFXTransformSaver saver; + + //MatrixF projMat( true ); + //const Frustum &frustum = ri->state->getFrustum(); + // + //F32 bias = Con::getFloatVariable( "$waterBlockBias", 0.0002418f ); + + //MathUtils::getZBiasProjectionMatrix( bias, frustum, &projMat ); + //GFX->setProjectionMatrix( projMat ); + + + GFXOcclusionQuery *query = mPlaneReflector.getOcclusionQuery(); + + bool doQuery = ( !mPlaneReflector.mQueryPending && query && mReflectorDesc.useOcclusionQuery ); + + if ( doQuery ) + query->begin(); + + // Real render call, done by derived class. + innerRender( state ); + + if ( doQuery ) + query->end(); + + if ( mUnderwater && mBasicLighting ) + drawUnderwaterFilter( state ); +} + +void WaterObject::setCustomTextures( S32 matIdx, U32 pass, const WaterMatParams ¶mHandles ) +{ + // TODO: Retrieve sampler numbers from parameter handles, see r22631. + + // Always use the ripple texture. + GFX->setTexture( 0, mRippleTex ); + + // Only above-water in advanced-lighting uses the foam texture. + if ( matIdx == WaterMat ) + { + GFX->setTexture( 5, mFoamTex ); + GFX->setTexture( 6, mDepthGradientTex ); + } + + if ( ( matIdx == WaterMat || matIdx == BasicWaterMat ) && mCubemap ) + GFX->setCubeTexture( 4, mCubemap->mCubemap ); + else + GFX->setCubeTexture( 4, NULL ); +} + +void WaterObject::drawUnderwaterFilter( SceneRenderState *state ) +{ + // set up camera transforms + MatrixF proj = GFX->getProjectionMatrix(); + MatrixF newMat(true); + GFX->setProjectionMatrix( newMat ); + GFX->pushWorldMatrix(); + GFX->setWorldMatrix( newMat ); + + // set up render states + GFX->disableShaders(); + GFX->setStateBlock( mUnderwaterSB ); + + /* + const Frustum &frustum = state->getFrustum(); + const MatrixF &camXfm = state->getCameraTransform(); + F32 nearDist = frustum.getNearDist(); + F32 nearLeft = frustum.getNearLeft(); + F32 nearRight = frustum.getNearRight(); + F32 nearTop = frustum.getNearTop(); + F32 nearBottom = frustum.getNearBottom(); + Point3F centerPnt; + frustum.getCenterPoint( ¢erPnt ); + + MatrixF.mul + Point3F linePnt, lineDir; + if ( mIntersect( nearPlane, mWaterPlane, &linePnt, &lineDir ) ) + { + Point3F leftPnt( centerPnt ); + leftPnt.x = near + } + */ + + Point2I resolution = GFX->getActiveRenderTarget()->getSize(); + F32 copyOffsetX = 1.0 / resolution.x; + F32 copyOffsetY = 1.0 / resolution.y; + + /* + ClippedPolyList polylist; + polylist.addPoint( Point3F( -1.0f - copyOffsetX, -1.0f + copyOffsetY, 0.0f ) ); + polylist.addPoint( Point3F( -1.0f - copyOffsetX, 1.0f + copyOffsetY, 0.0f ) ); + polylist.addPoint( Point3F( 1.0f - copyOffsetX, 1.0f + copyOffsetY, 0.0f ) ); + polylist.addPoint( Point3F( 1.0f - copyOffsetX, -1.0f + copyOffsetY, 0.0f ) ); + polylist.addPlane( clipPlane ); + + polylist.begin( NULL, 0 ); + polylist.vertex( 0 ); + polylist.vertex( 1 ); + polylist.vertex( 2 ); + polylist.vertex( 0 ); + polylist.vertex( 2 ); + polylist.vertex( 3 ); + */ + + // draw quad + + + GFXVertexBufferHandle verts( GFX, 4, GFXBufferTypeVolatile ); + verts.lock(); + + verts[0].point.set( -1.0 - copyOffsetX, -1.0 + copyOffsetY, 0.0 ); + verts[0].color = mUnderwaterColor; + + verts[1].point.set( -1.0 - copyOffsetX, 1.0 + copyOffsetY, 0.0 ); + verts[1].color = mUnderwaterColor; + + verts[2].point.set( 1.0 - copyOffsetX, 1.0 + copyOffsetY, 0.0 ); + verts[2].color = mUnderwaterColor; + + verts[3].point.set( 1.0 - copyOffsetX, -1.0 + copyOffsetY, 0.0 ); + verts[3].color = mUnderwaterColor; + + verts.unlock(); + + GFX->setVertexBuffer( verts ); + GFX->drawPrimitive( GFXTriangleFan, 0, 2 ); + + // reset states / transforms + GFX->setProjectionMatrix( proj ); + GFX->popWorldMatrix(); +} + +bool WaterObject::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + Con::NotifyDelegate clbk( this, &WaterObject::_onDisableTrueRelfections ); + Con::addVariableNotify( "$pref::Water::disableTrueReflections", clbk ); + + if ( isClientObject() ) + { + GFXStateBlockDesc desc; + desc.blendDefined = true; + desc.blendEnable = true; + desc.blendSrc = GFXBlendSrcAlpha; + desc.blendDest = GFXBlendInvSrcAlpha; + desc.zDefined = true; + desc.zEnable = false; + desc.cullDefined = true; + desc.cullMode = GFXCullNone; + mUnderwaterSB = GFX->createStateBlock( desc ); + + initTextures(); + + if ( mFullReflect && !smDisableTrueReflections ) + mPlaneReflector.registerReflector( this, &mReflectorDesc ); + } + + return true; +} + +void WaterObject::onRemove() +{ + Con::NotifyDelegate clbk( this, &WaterObject::_onDisableTrueRelfections ); + Con::removeVariableNotify( "$pref::Water::disableTrueReflections", clbk ); + + if ( isClientObject() ) + { + mPlaneReflector.unregisterReflector(); + cleanupMaterials(); + } + + Parent::onRemove(); +} + +void WaterObject::_onDisableTrueRelfections() +{ + // Same code as _setFullReflect + if ( isProperlyAdded() && isClientObject() ) + { + bool isEnabled = mPlaneReflector.isEnabled(); + + bool enable = mFullReflect && !smDisableTrueReflections; + + if ( enable && !isEnabled ) + mPlaneReflector.registerReflector( this, &mReflectorDesc ); + else if ( !enable && isEnabled ) + mPlaneReflector.unregisterReflector(); + } +} + +void WaterObject::setShaderParams( SceneRenderState *state, BaseMatInstance *mat, const WaterMatParams ¶mHandles ) +{ + MaterialParameters* matParams = mat->getMaterialParameters(); + + matParams->setSafe( paramHandles.mElapsedTimeSC, (F32)Sim::getCurrentTime() / 1000.0f ); + + // set vertex shader constants + //----------------------------------- + + Point2F reflectTexSize( mPlaneReflector.reflectTex.getWidth(), mPlaneReflector.reflectTex.getHeight() ); + matParams->setSafe( paramHandles.mReflectTexSizeSC, reflectTexSize ); + + static AlignedArray mConstArray( MAX_WAVES, sizeof( Point4F ) ); + + // Ripples... + + for ( U32 i = 0; i < MAX_WAVES; i++ ) + mConstArray[i].set( -mRippleDir[i].x, -mRippleDir[i].y ); + matParams->setSafe( paramHandles.mRippleDirSC, mConstArray ); + + Point3F rippleSpeed( mRippleSpeed[0], mRippleSpeed[1], mRippleSpeed[2] ); + matParams->setSafe( paramHandles.mRippleSpeedSC, rippleSpeed ); + + Point4F rippleMagnitude( mRippleMagnitude[0], + mRippleMagnitude[1], + mRippleMagnitude[2], + mOverallRippleMagnitude ); + matParams->setSafe( paramHandles.mRippleMagnitudeSC, rippleMagnitude ); + + for ( U32 i = 0; i < MAX_WAVES; i++ ) + { + Point2F texScale = mRippleTexScale[i]; + if ( texScale.x > 0.0 ) + texScale.x = 1.0 / texScale.x; + if ( texScale.y > 0.0 ) + texScale.y = 1.0 / texScale.y; + + mConstArray[i].set( texScale.x, texScale.y ); + } + matParams->setSafe(paramHandles.mRippleTexScaleSC, mConstArray); + + static AlignedArray mConstArray4F( 3, sizeof( Point4F ) ); + + F32 angle, cosine, sine; + + for ( U32 i = 0; i < MAX_WAVES; i++ ) + { + angle = mAtan2( mRippleDir[i].x, -mRippleDir[i].y ); + cosine = mCos( angle ); + sine = mSin( angle ); + + mConstArray4F[i].set( cosine, sine, -sine, cosine ); + matParams->setSafe( paramHandles.mRippleMatSC, mConstArray4F ); + } + + // Waves... + + for ( U32 i = 0; i < MAX_WAVES; i++ ) + mConstArray[i].set( -mWaveDir[i].x, -mWaveDir[i].y ); + matParams->setSafe( paramHandles.mWaveDirSC, mConstArray ); + + for ( U32 i = 0; i < MAX_WAVES; i++ ) + mConstArray[i].set( mWaveSpeed[i], mWaveMagnitude[i] * mOverallWaveMagnitude ); + matParams->setSafe( paramHandles.mWaveDataSC, mConstArray ); + + // Foam... + + Point4F foamDir( mFoamDir[0].x, mFoamDir[0].y, mFoamDir[1].x, mFoamDir[1].y ); + matParams->setSafe( paramHandles.mFoamDirSC, foamDir ); + + Point2F foamSpeed( mFoamSpeed[0], mFoamSpeed[1] ); + matParams->setSafe( paramHandles.mFoamSpeedSC, foamSpeed ); + + //Point3F rippleMagnitude( mRippleMagnitude[0] * mOverallRippleMagnitude, + // mRippleMagnitude[1] * mOverallRippleMagnitude, + // mRippleMagnitude[2] * mOverallRippleMagnitude ); + //matParams->setSafe( paramHandles.mRippleMagnitudeSC, rippleMagnitude ); + + Point4F foamTexScale( mFoamTexScale[0].x, mFoamTexScale[0].y, mFoamTexScale[1].x, mFoamTexScale[1].y ); + + for ( U32 i = 0; i < 4; i++ ) + { + if ( foamTexScale[i] > 0.0f ) + foamTexScale[i] = 1.0 / foamTexScale[i]; + } + + matParams->setSafe(paramHandles.mFoamTexScaleSC, foamTexScale); + + // Other vert params... + + matParams->setSafe( paramHandles.mUndulateMaxDistSC, mUndulateMaxDist ); + + // set pixel shader constants + //----------------------------------- + + Point2F fogParams( mWaterFogData.density, mWaterFogData.densityOffset ); + matParams->setSafe(paramHandles.mFogParamsSC, fogParams ); + + matParams->setSafe(paramHandles.mFarPlaneDistSC, (F32)state->getFarPlane() ); + + Point2F wetnessParams( mWaterFogData.wetDepth, mWaterFogData.wetDarkening ); + matParams->setSafe(paramHandles.mWetnessParamsSC, wetnessParams ); + + Point3F distortionParams( mDistortStartDist, mDistortEndDist, mDistortFullDepth ); + matParams->setSafe(paramHandles.mDistortionParamsSC, distortionParams ); + + LightInfo *sun = LIGHTMGR->getSpecialLight(LightManager::slSunLightType); + const ColorF &sunlight = state->getAmbientLightColor(); + Point3F ambientColor = mEmissive ? Point3F::One : sunlight; + matParams->setSafe(paramHandles.mAmbientColorSC, ambientColor ); + matParams->setSafe(paramHandles.mLightDirSC, sun->getDirection() ); + + Point4F foamParams( mOverallFoamOpacity, mFoamMaxDepth, mFoamAmbientLerp, mFoamRippleInfluence ); + matParams->setSafe(paramHandles.mFoamParamsSC, foamParams ); + + Point4F miscParams( mFresnelBias, mFresnelPower, mClarity, mMiscParamW ); + matParams->setSafe( paramHandles.mMiscParamsSC, miscParams ); + + Point4F specularParams( mSpecularColor.red, mSpecularColor.green, mSpecularColor.blue, mSpecularPower ); + if ( !mEmissive ) + { + const ColorF &sunColor = sun->getColor(); + F32 brightness = sun->getBrightness(); + specularParams.x *= sunColor.red * brightness; + specularParams.y *= sunColor.green * brightness; + specularParams.z *= sunColor.blue * brightness; + } + matParams->setSafe( paramHandles.mSpecularParamsSC, specularParams ); + + matParams->setSafe( paramHandles.mDepthGradMaxSC, mDepthGradientMax ); + + matParams->setSafe( paramHandles.mReflectivitySC, mReflectivity ); +} + +PostEffect* WaterObject::getUnderwaterEffect() +{ + if ( mUnderwaterPostFx.isValid() ) + return mUnderwaterPostFx; + + PostEffect *effect; + if ( Sim::findObject( "UnderwaterFogPostFx", effect ) ) + mUnderwaterPostFx = effect; + + return mUnderwaterPostFx; +} + +void WaterObject::updateUnderwaterEffect( SceneRenderState *state ) +{ + AssertFatal( isClientObject(), "uWaterObject::updateUnderwaterEffect() called on the server" ); + + PostEffect *effect = getUnderwaterEffect(); + if ( !effect ) + return; + + // Never use underwater postFx with Basic Lighting, we don't have depth. + if ( mBasicLighting ) + { + effect->disable(); + return; + } + + GameConnection *conn = GameConnection::getConnectionToServer(); + if ( !conn ) + return; + + GameBase *control = conn->getControlObject(); + if ( !control ) + return; + + WaterObject *water = control->getCurrentWaterObject(); + if ( water == NULL ) + effect->disable(); + + else if ( water == this ) + { + MatrixF mat; + conn->getControlCameraTransform( 0, &mat ); + + if ( mUnderwater ) + { + effect->enable(); + effect->setOnThisFrame( true ); + + mWaterFogData.depthGradMax = mDepthGradientMax; + state->getSceneManager()->setWaterFogData( mWaterFogData ); + + // Register our depthGradient texture with a name so it can + // be fetched by the effect when it renders. + if ( !mNamedDepthGradTex.isRegistered() ) + mNamedDepthGradTex.registerWithName( "waterDepthGradMap" ); + mNamedDepthGradTex.setTexture( mDepthGradientTex ); + } + else + effect->disable(); + } +} + +bool WaterObject::initMaterial( S32 idx ) +{ + // We must return false for any case which it is NOT safe for the caller + // to use the indexed material. + + if ( idx < 0 || idx > NumMatTypes ) + return false; + + BaseMatInstance *mat = mMatInstances[idx]; + WaterMatParams &matParams = mMatParamHandles[idx]; + + // Is it already initialized? + + if ( mat && mat->isValid() ) + return true; + + // Do we need to allocate anything? + + if ( mSurfMatName[idx].isNotEmpty() ) + { + if ( mat ) + SAFE_DELETE( mat ); + + CustomMaterial *custMat; + if ( Sim::findObject( mSurfMatName[idx], custMat ) && custMat->mShaderData ) + mat = custMat->createMatInstance(); + else + mat = MATMGR->createMatInstance( mSurfMatName[idx] ); + + const GFXVertexFormat *flags = getGFXVertexFormat(); + + if ( mat && mat->init( MATMGR->getDefaultFeatures(), flags ) ) + { + mMatInstances[idx] = mat; + matParams.init( mat ); + return true; + } + + SAFE_DELETE( mat ); + } + + return false; +} + +void WaterObject::initTextures() +{ + if ( mRippleTexName.isNotEmpty() ) + mRippleTex.set( mRippleTexName, &GFXDefaultStaticDiffuseProfile, "WaterObject::mRippleTex" ); + if ( mRippleTex.isNull() ) + mRippleTex.set( "core/art/warnmat", &GFXDefaultStaticDiffuseProfile, "WaterObject::mRippleTex" ); + + if ( mDepthGradientTexName.isNotEmpty() ) + mDepthGradientTex.set( mDepthGradientTexName, &GFXDefaultStaticDiffuseProfile, "WaterObject::mDepthGradientTex" ); + if ( mDepthGradientTex.isNull() ) + mDepthGradientTex.set( "core/art/warnmat", &GFXDefaultStaticDiffuseProfile, "WaterObject::mDepthGradientTex" ); + + if ( mNamedDepthGradTex.isRegistered() ) + mNamedDepthGradTex.setTexture( mDepthGradientTex ); + + if ( mFoamTexName.isNotEmpty() ) + mFoamTex.set( mFoamTexName, &GFXDefaultStaticDiffuseProfile, "WaterObject::mFoamTex" ); + if ( mFoamTex.isNull() ) + mFoamTex.set( "core/art/warnmat", &GFXDefaultStaticDiffuseProfile, "WaterObject::mFoamTex" ); + + if ( mCubemapName.isNotEmpty() ) + Sim::findObject( mCubemapName, mCubemap ); + if ( mCubemap ) + mCubemap->createMap(); +} + +void WaterObject::cleanupMaterials() +{ + for (U32 i = 0; i < NumMatTypes; i++) + SAFE_DELETE(mMatInstances[i]); +} + +S32 WaterObject::getMaterialIndex( const Point3F &camPos ) +{ + bool underwater = isUnderwater( camPos ); + bool basicLighting = dStricmp( LIGHTMGR->getId(), "BLM" ) == 0; + + // set the material + S32 matIdx = -1; + if ( underwater ) + { + if ( basicLighting ) + matIdx = BasicUnderWaterMat; + else + matIdx = UnderWaterMat; + } + else + { + if ( basicLighting ) + matIdx = BasicWaterMat; + else + matIdx = WaterMat; + } + + return matIdx; +} \ No newline at end of file diff --git a/Engine/source/environment/waterObject.h b/Engine/source/environment/waterObject.h new file mode 100644 index 000000000..06c5e7dd0 --- /dev/null +++ b/Engine/source/environment/waterObject.h @@ -0,0 +1,320 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _WATEROBJECT_H_ +#define _WATEROBJECT_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _GFXSTRUCTS_H_ +#include "gfx/gfxStructs.h" +#endif +#ifndef _FOGSTRUCTS_H_ +#include "scene/fogStructs.h" +#endif +#ifndef _GFXSTATEBLOCK_H_ +#include "gfx/gfxStateBlock.h" +#endif +#ifndef _REFLECTOR_H_ +#include "scene/reflector.h" +#endif +#ifndef _ALIGNEDARRAY_H_ +#include "core/util/tAlignedArray.h" +#endif +#ifndef _MATTEXTURETARGET_H_ +#include "materials/matTextureTarget.h" +#endif + +GFXDeclareVertexFormat( GFXWaterVertex ) +{ + Point3F point; + Point3F normal; + GFXVertexColor color; + Point2F undulateData; + Point4F horizonFactor; +}; + + +class MaterialParameterHandle; +class BaseMatInstance; +class ShaderData; +class MatrixSet; + +struct WaterMatParams +{ + MaterialParameterHandle* mRippleMatSC; + MaterialParameterHandle* mRippleDirSC; + MaterialParameterHandle* mRippleTexScaleSC; + MaterialParameterHandle* mRippleSpeedSC; + MaterialParameterHandle* mRippleMagnitudeSC; + MaterialParameterHandle* mFoamDirSC; + MaterialParameterHandle* mFoamTexScaleSC; + MaterialParameterHandle* mFoamOpacitySC; + MaterialParameterHandle* mFoamSpeedSC; + MaterialParameterHandle* mWaveDirSC; + MaterialParameterHandle* mWaveDataSC; + MaterialParameterHandle* mReflectTexSizeSC; + MaterialParameterHandle* mBaseColorSC; + MaterialParameterHandle* mMiscParamsSC; + MaterialParameterHandle* mReflectParamsSC; + MaterialParameterHandle* mReflectNormalSC; + MaterialParameterHandle* mHorizonPositionSC; + MaterialParameterHandle* mFogParamsSC; + MaterialParameterHandle* mMoreFogParamsSC; + MaterialParameterHandle* mFarPlaneDistSC; + MaterialParameterHandle* mWetnessParamsSC; + MaterialParameterHandle* mDistortionParamsSC; + MaterialParameterHandle* mUndulateMaxDistSC; + MaterialParameterHandle* mAmbientColorSC; + MaterialParameterHandle* mLightDirSC; + MaterialParameterHandle* mFoamParamsSC; + MaterialParameterHandle* mGridElementSizeSC; + MaterialParameterHandle* mElapsedTimeSC; + MaterialParameterHandle* mModelMatSC; + MaterialParameterHandle* mFoamSamplerSC; + MaterialParameterHandle* mRippleSamplerSC; + MaterialParameterHandle* mCubemapSamplerSC; + MaterialParameterHandle* mSpecularParamsSC; + MaterialParameterHandle* mDepthGradMaxSC; + MaterialParameterHandle* mReflectivitySC; + + void clear(); + void init(BaseMatInstance* matInst); +}; + + +class GFXOcclusionQuery; +class PostEffect; +class CubemapData; + +//------------------------------------------------------------------------- +// WaterObject Class +//------------------------------------------------------------------------- + +class WaterObject : public SceneObject +{ + typedef SceneObject Parent; + +protected: + + enum MaskBits { + UpdateMask = Parent::NextFreeMask << 0, + WaveMask = Parent::NextFreeMask << 1, + MaterialMask = Parent::NextFreeMask << 2, + TextureMask = Parent::NextFreeMask << 3, + SoundMask = Parent::NextFreeMask << 4, + NextFreeMask = Parent::NextFreeMask << 5 + }; + + enum consts { + MAX_WAVES = 3, + MAX_FOAM = 2, + NUM_ANIM_FRAMES = 32, + }; + + enum MaterialType + { + WaterMat = 0, + UnderWaterMat, + BasicWaterMat, + BasicUnderWaterMat, + NumMatTypes + }; + +public: + + WaterObject(); + virtual ~WaterObject(); + + DECLARE_CONOBJECT( WaterObject ); + + // ConsoleObject + static void consoleInit(); + static void initPersistFields(); + + // SimObject + virtual bool onAdd(); + virtual void onRemove(); + virtual void inspectPostApply(); + virtual bool processArguments(S32 argc, const char **argv); + + // NetObject + virtual U32 packUpdate( NetConnection * conn, U32 mask, BitStream *stream ); + virtual void unpackUpdate( NetConnection * conn, BitStream *stream ); + + // SceneObject + virtual void prepRenderImage( SceneRenderState *state ); + virtual void renderObject( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); + virtual SFXAmbience* getSoundAmbience() const { return mSoundAmbience; } + virtual bool containsPoint( const Point3F& point ) { return isUnderwater( point ); } + + // WaterObject + virtual F32 getViscosity() const { return mViscosity; } + virtual F32 getDensity() const { return mDensity; } + virtual F32 getSurfaceHeight( const Point2F &pos ) const { return 0.0f; }; + virtual const char* getLiquidType() const { return mLiquidType; } + virtual F32 getWaterCoverage( const Box3F &worldBox ) const { return 0.0f; } + virtual VectorF getFlow( const Point3F &pos ) const { return Point3F::Zero; } + virtual void updateUnderwaterEffect( SceneRenderState *state ); + virtual bool isUnderwater( const Point3F &pnt ) const { return false; } + +protected: + + virtual void setShaderXForms( BaseMatInstance *mat ) {}; + virtual void setupVBIB() {}; + virtual void innerRender( SceneRenderState *state ) {}; + virtual void setCustomTextures( S32 matIdx, U32 pass, const WaterMatParams ¶mHandles ); + void drawUnderwaterFilter( SceneRenderState *state ); + + virtual void setShaderParams( SceneRenderState *state, BaseMatInstance *mat, const WaterMatParams ¶mHandles ); + PostEffect* getUnderwaterEffect(); + + bool initMaterial( S32 idx ); + void cleanupMaterials(); + S32 getMaterialIndex( const Point3F &camPos ); + + void initTextures(); + + virtual void _getWaterPlane( const Point3F &camPos, PlaneF &outPlane, Point3F &outPos ) {} + + /// Callback used internally when smDisableTrueReflections changes. + void _onDisableTrueRelfections(); + +protected: + + static bool _setFullReflect( void *object, const char *index, const char *data ); + static bool _checkDensity(void *object, const char *index, const char *data); + + // WaterObject + F32 mViscosity; + F32 mDensity; + String mLiquidType; + F32 mFresnelBias; + F32 mFresnelPower; + F32 mSpecularPower; + ColorF mSpecularColor; + bool mEmissive; + + // Reflection + bool mFullReflect; + PlaneReflector mPlaneReflector; + ReflectorDesc mReflectorDesc; + PlaneF mWaterPlane; + Point3F mWaterPos; + bool mReflectNormalUp; + F32 mReflectivity; + + // Water Fogging + WaterFogData mWaterFogData; + + // Distortion + F32 mDistortStartDist; + F32 mDistortEndDist; + F32 mDistortFullDepth; + + // Ripples + Point2F mRippleDir[ MAX_WAVES ]; + F32 mRippleSpeed[ MAX_WAVES ]; + Point2F mRippleTexScale[ MAX_WAVES ]; + F32 mRippleMagnitude[ MAX_WAVES ]; + + F32 mOverallRippleMagnitude; + + // Waves + Point2F mWaveDir[ MAX_WAVES ]; + F32 mWaveSpeed[ MAX_WAVES ]; + F32 mWaveMagnitude[ MAX_WAVES ]; + + F32 mOverallWaveMagnitude; + + // Foam + Point2F mFoamDir[ MAX_WAVES ]; + F32 mFoamSpeed[ MAX_WAVES ]; + Point2F mFoamTexScale[ MAX_WAVES ]; + F32 mFoamOpacity[ MAX_WAVES ]; + + F32 mOverallFoamOpacity; + F32 mFoamMaxDepth; + F32 mFoamAmbientLerp; + F32 mFoamRippleInfluence; + + // Basic Lighting + F32 mClarity; + ColorI mUnderwaterColor; + + // Misc + F32 mDepthGradientMax; + + // Other textures + String mRippleTexName; + String mFoamTexName; + String mCubemapName; + String mDepthGradientTexName; + + // Sound + SFXAmbience* mSoundAmbience; + + // Not fields... + + /// Defined here and sent to the shader in WaterCommon::setShaderParams + /// but needs to be initialized in child classes prior to that call. + F32 mUndulateMaxDist; + + /// Defined in WaterCommon but set and used by child classes. + /// If true will refuse to render a reflection even if called from + /// the ReflectionManager, is set true if occlusion query is enabled and + /// it determines it is occluded. + //bool mSkipReflectUpdate; + + /// Derived classes can set this value prior to calling Parent::setShaderConst + /// to pass it into the shader miscParam.w + F32 mMiscParamW; + + SimObjectPtr mUnderwaterPostFx; + + /// A global for enabling wireframe rendering + /// on all water objects. + static bool smWireframe; + + /// Force all water objects to use static cubemap reflections + static bool smDisableTrueReflections; + + // Rendering + bool mBasicLighting; + //U32 mRenderUpdateCount; + //U32 mReflectUpdateCount; + bool mGenerateVB; + String mSurfMatName[NumMatTypes]; + BaseMatInstance* mMatInstances[NumMatTypes]; + WaterMatParams mMatParamHandles[NumMatTypes]; + bool mUnderwater; + GFXStateBlockRef mUnderwaterSB; + GFXTexHandle mRippleTex; + GFXTexHandle mDepthGradientTex; + GFXTexHandle mFoamTex; + CubemapData *mCubemap; + MatrixSet *mMatrixSet; + NamedTexTarget mNamedDepthGradTex; +}; + +#endif // _WATEROBJECT_H_ diff --git a/Engine/source/environment/waterPlane.cpp b/Engine/source/environment/waterPlane.cpp new file mode 100644 index 000000000..c0158a61f --- /dev/null +++ b/Engine/source/environment/waterPlane.cpp @@ -0,0 +1,973 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "environment/waterPlane.h" + +#include "core/util/safeDelete.h" +#include "scene/sceneRenderState.h" +#include "scene/sceneManager.h" +#include "lighting/lightInfo.h" +#include "core/stream/bitStream.h" +#include "math/mathIO.h" +#include "math/mathUtils.h" +#include "console/consoleTypes.h" +#include "gui/3d/guiTSControl.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/gfxDebugEvent.h" +#include "gfx/gfxOcclusionQuery.h" +#include "renderInstance/renderPassManager.h" +#include "sim/netConnection.h" +#include "scene/reflectionManager.h" +#include "ts/tsShapeInstance.h" +#include "T3D/gameFunctions.h" +#include "postFx/postEffect.h" +#include "math/util/matrixSet.h" + +extern ColorI gCanvasClearColor; + +#define BLEND_TEX_SIZE 256 +#define V_SHADER_PARAM_OFFSET 50 + +IMPLEMENT_CO_NETOBJECT_V1(WaterPlane); + +ConsoleDocClass( WaterPlane, + "@brief Represents a large body of water stretching to the horizon in all directions.\n\n" + + "WaterPlane's position is defined only height, the z element of position, " + "it is infinite in xy and depth. %WaterPlane is designed to represent the " + "ocean on an island scene and viewed from ground level; other uses may not " + "be appropriate and a WaterBlock may be used.\n\n" + + "@see WaterObject for inherited functionality.\n\n" + + "Limitations:\n\n" + + "Because %WaterPlane cannot be projected exactly to the far-clip distance, " + "other objects nearing this distance can have noticible artifacts as they " + "clip through first the %WaterPlane and then the far plane.\n\n" + + "To avoid this large objects should be positioned such that they will not line up with " + "the far-clip from vantage points the player is expected to be. In particular, " + "your TerrainBlock should be completely contained by the far-clip distance.\n\n" + + "Viewing %WaterPlane from a high altitude with a tight far-clip distance " + "will accentuate this limitation. %WaterPlane is primarily designed to " + "be viewed from ground level.\n\n" + + "@ingroup Water" +); + +WaterPlane::WaterPlane() +{ + mGridElementSize = 1.0f; + mGridSize = 101; + mGridSizeMinusOne = mGridSize - 1; + + mNetFlags.set(Ghostable | ScopeAlways); + + mVertCount = 0; + mIndxCount = 0; + mPrimCount = 0; +} + +WaterPlane::~WaterPlane() +{ +} + +bool WaterPlane::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + setGlobalBounds(); + resetWorldBox(); + addToScene(); + + mWaterFogData.plane.set( 0, 0, 1, -getPosition().z ); + + return true; +} + +void WaterPlane::onRemove() +{ + removeFromScene(); + + Parent::onRemove(); +} + +void WaterPlane::initPersistFields() +{ + addGroup( "WaterPlane" ); + + addProtectedField( "gridSize", TypeS32, Offset( mGridSize, WaterPlane ), &protectedSetGridSize, &defaultProtectedGetFn, + "Spacing between vertices in the WaterBlock mesh" ); + + addProtectedField( "gridElementSize", TypeF32, Offset( mGridElementSize, WaterPlane ), &protectedSetGridElementSize, &defaultProtectedGetFn, + "Duplicate of gridElementSize for backwards compatility"); + + endGroup( "WaterPlane" ); + + Parent::initPersistFields(); + + removeField( "rotation" ); + removeField( "scale" ); +} + +U32 WaterPlane::packUpdate(NetConnection* con, U32 mask, BitStream* stream) +{ + U32 retMask = Parent::packUpdate(con, mask, stream); + + stream->write( mGridSize ); + stream->write( mGridElementSize ); + + if ( stream->writeFlag( mask & UpdateMask ) ) + { + stream->write( getPosition().z ); + } + + return retMask; +} + +void WaterPlane::unpackUpdate(NetConnection* con, BitStream* stream) +{ + Parent::unpackUpdate(con, stream); + + U32 inGridSize; + stream->read( &inGridSize ); + setGridSize( inGridSize ); + + F32 inGridElementSize; + stream->read( &inGridElementSize ); + setGridElementSize( inGridElementSize ); + + if( stream->readFlag() ) // UpdateMask + { + float posZ; + stream->read( &posZ ); + Point3F newPos = getPosition(); + newPos.z = posZ; + setPosition( newPos ); + } +} + +void WaterPlane::setupVBIB( SceneRenderState *state ) +{ + const Frustum &frustum = state->getFrustum(); + + // Water base-color, assigned as color for all verts. + const GFXVertexColor vertCol(mWaterFogData.color); + + // World-Up vector, assigned as normal for all verts. + const Point3F worldUp( 0.0f, 0.0f, 1.0f ); + + // World-unit size of a grid cell. + const F32 squareSize = mGridElementSize; + + // Column/Row count. + // So we don't neet to access class-specific member variables + // in the code below. + const U32 gridSize = mGridSize; + + // Number of verts in one column / row + const U32 gridStride = gridSize + 1; + + // Grid is filled in this order... + + // Ex. Grid with gridSize of 2. + // + // Letters are cells. + // Numbers are verts, enumerated in their order within the vert buffer. + // + // 6 7 8 + // (c) (d) + // 3 4 5 + // (a) (b) + // 0 1 2 + // + // Note... + // Camera would be positioned at vert 4 ( in this particular grid not a constant ). + // Positive Y points UP the diagram ( verts 0, 3, 6 ). + // Positive X points RIGHT across the diagram ( verts 0, 1, 2 ). + + // Length of a grid row/column divided by two. + F32 gridSideHalfLen = squareSize * gridSize * 0.5f; + + // Position of the first vertex in the grid. + // Relative to the camera this is the "Back Left" corner vert. + const Point3F cornerPosition( -gridSideHalfLen, -gridSideHalfLen, 0.0f ); + + // Number of verts in the grid centered on the camera. + const U32 gridVertCount = gridStride * gridStride; + + // Number of verts surrounding the grid, projected by the frustum. + const U32 borderVertCount = gridSize * 4; + + // Number of verts in the front-most row which are raised to the horizon. + const U32 horizonVertCount = gridStride; + + // Total number of verts. Calculation explained above. + mVertCount = gridVertCount + borderVertCount + horizonVertCount; + + + // Fill the vertex buffer... + + mVertBuff.set( GFX, mVertCount, GFXBufferTypeStatic ); + + GFXWaterVertex *vertPtr = mVertBuff.lock(); + + // Fill verts in the camera centered grid... + + // Temorary storage for calculation of vert position. + F32 xVal, yVal; + + for ( U32 i = 0; i < gridStride; i++ ) + { + yVal = cornerPosition.y + (F32)( i * squareSize ); + + for ( U32 j = 0; j < gridStride; j++ ) + { + xVal = cornerPosition.x + (F32)( j * squareSize ); + + vertPtr->point.set( xVal, yVal, 0.0f ); + vertPtr->color = vertCol; + vertPtr->normal = worldUp; + vertPtr->undulateData.set( xVal, yVal ); + vertPtr->horizonFactor.set( 0, 0, 0, 0 ); + vertPtr++; + } + } + + // Fill in 'border' verts, surrounding the grid, projected by the frustum. + + // Ex. Grid with gridSize of 2. + // + // Letters in parenthesis are cells. + // x's are grid-verts ( we have already filled ). + // Numbers are border verts, enumerated in their order within the vert buffer. + // + // Lines connecting verts explained in the code below. + // + // Front + // + // L 0------1 2 R + // e x x x | i + // f (c) (d) | g + // t 7 x x x 3 h + // | (a) (b) t + // | x x x + // 6 5------4 + // + // Back + // + // As in previous diagram... + // Camera would be positioned at vert 4 ( in this particular grid not a constant ). + // Positive Y points UP the diagram ( verts 6, 7, 0 ). + // Positive X points RIGHT across the diagram ( verts 0, 1, 2 ). + + + // Iterator i is looping through the 4 'sides' of the grid. + // Inner loop ( using iterator j ) will fill in a number of verts + // where that count is 'gridSize'. + // + // + // Ex. Given the grid with gridSize of 2 diagramed above, + // Outer loop iterates through: Front, Right, Back, Left + // Inner loop fills 2 verts per iteration of the outer loop: { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 } + + // Grid-space vectors indexed by 'side'. + // Each vector describes the direction we iterate when + // filling in verts ( mathematically the tangent ). + const Point2F sBorderTangentVec [4] = { + Point2F( 1, 0 ), // Front ( 0 ) + Point2F( 0, -1 ), // Right ( 1 ) + Point2F( -1, 0 ), // Back ( 2 ) + Point2F( 0, 1 ) // Left ( 3 ) + }; + + + // Normalized positions indexed by 'side' + // Defines the 'start' position of each side, eg. the position of the first vert. + // See Diagram below. + const Point2F sBorderStartPos [4] = { + Point2F( -1, 1 ), // Front ( 0 ) + Point2F( 1, 1 ), // Right ( 1 ) + Point2F( 1, -1 ), // Back ( 2 ) + Point2F( -1, -1 ) // Left ( 3 ) + }; + + // Diagram of Start vert position per Side. + // + // Labeling convention for verts is 'As' where A is the first letter of + // that side's descriptive name and lower-case s indicates 'start'. + // + // + // + // Front + // (-1,1) + // Fs------o-----Rs(1,1)R + // | | i + // | | g + // o (0,0) o h + // | | t + // | | + // L(-1,-1)Ls------o-----Bs + // e (1,-1) + // f Back + // t + + // Calculate the world-space dimension of the border-vert-ring... + + // Diagram shows overall layout of the WaterPlane with a gridSize of 1, + // with all 'quads' enumerated. + // center-grid ( 1 ), border ( 2, 3, 4, 5 ), and horizon ( 6 ). + // + // + // x------------x + // \ 6 \ <- horizon quad is really 'above' the front border + // x --------- x not in front of it + // | \ 2 / | + // | x---- x | + // | | | | + // | 5| 1 |3 | + // | x --- x | + // | / 4 \| + // x------------x + + + // WaterPlane renders relative to the camera rotation around z and xy position. + // + // That is, it rotates around the z-up axis with the camera such that the + // camera is always facing towards the front border unless looking straight + // down or up. + // + // Also note that the horizon verts are pulled straight up from the front + // border verts. + // + // Therefore... + // + // The front border must be as close to the farclip plane as possible + // so distant objects clip through the horizon and farplane at the same time. + // + // The left and right borders must be pulled outward a distance such + // that water extends horizontally across the entire viewable area while + // looking straight forward +y or straight down -z. + // + + // + const F32 farDistScale = 0.99f; + + // + F32 farDist = frustum.getFarDist() * farDistScale; + + // + F32 farWidth = (F32)state->getViewport().extent.x * farDist / state->getWorldToScreenScale().x; + + Point2F borderExtents( farWidth * 2.0f, farDist * 2.0f ); + Point2F borderHalfExtents( farWidth, farDist ); + + Point2F borderDir; + Point2F borderStart; + + for ( U32 i = 0; i < 4; i++ ) + { + borderDir = sBorderTangentVec[i]; + borderStart = sBorderStartPos[i]; + + for ( U32 j = 0; j < gridSize; j++ ) + { + F32 frac = (F32)j / (F32)gridSize; + + Point2F pos( borderStart * borderHalfExtents ); + pos += borderDir * borderExtents * frac; + + vertPtr->point.set( pos.x, pos.y, 0.0f ); + vertPtr->undulateData.set( pos.x, pos.y ); + vertPtr->horizonFactor.set( 0, 0, 0, 0 ); + vertPtr->color = vertCol; + vertPtr->normal = worldUp; + vertPtr++; + } + } + + // Fill in row of horizion verts. + // Verts are positioned identical to the front border, but will be + // manipulated in z within the shader. + // + // Z position of 50.0f is unimportant unless you want to disable + // shader manipulation and render in wireframe for debugging. + + for ( U32 i = 0; i < gridStride; i++ ) + { + F32 frac = (F32)i / (F32)gridSize; + + Point2F pos( sBorderStartPos[0] * borderHalfExtents ); + pos += sBorderTangentVec[0] * borderExtents * frac; + + vertPtr->point.set( pos.x, pos.y, 50.0f ); + vertPtr->undulateData.set( pos.x, pos.y ); + vertPtr->horizonFactor.set( 1, 0, 0, 0 ); + vertPtr->color = vertCol; + vertPtr->normal = worldUp; + vertPtr++; + } + + mVertBuff.unlock(); + + + // Fill in the PrimitiveBuffer... + + // 2 triangles per cell/quad + const U32 gridTriCount = gridSize * gridSize * 2; + + // 4 sides, mGridSize quads per side, 2 triangles per quad + const U32 borderTriCount = 4 * gridSize * 2; + + // 1 quad per gridSize, 2 triangles per quad + // i.e. an extra row of 'cells' leading the front side of the grid + const U32 horizonTriCount = gridSize * 2; + + mPrimCount = gridTriCount + borderTriCount + horizonTriCount; + + // 3 indices per triangle. + mIndxCount = mPrimCount * 3; + + mPrimBuff.set( GFX, mIndxCount, mPrimCount, GFXBufferTypeStatic ); + U16 *idxPtr; + mPrimBuff.lock(&idxPtr); + + // Temporaries to hold indices for the corner points of a quad. + U32 p00, p01, p11, p10; + U32 offset = 0; + + // Given a single cell of the grid diagramed below, + // quad indice variables are in this orientation. + // + // p01 --- p11 + // | | + // | | + // p00 --- p10 + // + // Positive Y points UP the diagram ( p00, p01 ). + // Positive X points RIGHT across the diagram ( p00, p10 ) + // + + // i iterates bottom to top "column-wise" + for ( U32 i = 0; i < mGridSize; i++ ) + { + // j iterates left to right "row-wise" + for ( U32 j = 0; j < mGridSize; j++ ) + { + // where (j,i) is a particular cell. + p00 = offset; + p10 = offset + 1; + p01 = offset + gridStride; + p11 = offset + 1 + gridStride; + + // Top Left Triangle + + *idxPtr = p00; + idxPtr++; + *idxPtr = p01; + idxPtr++; + *idxPtr = p11; + idxPtr++; + + // Bottom Right Triangle + + *idxPtr = p00; + idxPtr++; + *idxPtr = p11; + idxPtr++; + *idxPtr = p10; + idxPtr++; + + offset += 1; + } + + offset += 1; + } + + // Fill border indices... + + // Given a grid size of 1, + // the grid / border verts are in the vertex buffer in this order. + // + // + // 4 5 + // 2 --- 3 + // | | + // | | + // 0 --- 1 + // 7 6 + // + // Positive Y points UP the diagram ( p00, p01 ). + // Positive X points RIGHT across the diagram ( p00, p10 ) + // + // Note we duplicate the first border vert ( 4 ) since it is also the last + // and this makes our loop easier. + + const U32 sBorderStartVert [4] = { + gridStride * gridSize, // Index to the Top-Left grid vert. + gridStride * gridSize + gridSize, // Index to the Top-Right grid vert. + gridSize, // Index to the Bottom-Right grid vert. + 0, // Index to the Bottom-Left grid vert. + }; + + const S32 sBorderStepSize [4] = { + // Step size to the next grid vert along the specified side.... + 1, // Top + -(S32)gridStride, // Right + -1, // Bottom + gridStride, // Left + }; + + const U32 firstBorderVert = gridStride * gridSize + gridStride; + const U32 lastBorderVert = firstBorderVert + ( borderVertCount - 1 ); + U32 startBorderVert = firstBorderVert; + U32 startGridVert; + U32 curStepSize; + + + for ( U32 i = 0; i < 4; i++ ) + { + startGridVert = sBorderStartVert[i]; + curStepSize = sBorderStepSize[i]; + + for ( U32 j = 0; j < gridSize; j++ ) + { + // Each border cell is 1 quad, 2 triangles. + + p00 = startGridVert; + p10 = startGridVert + curStepSize; + p01 = startBorderVert; + p11 = startBorderVert + 1; + + if ( p11 > lastBorderVert ) + p11 = firstBorderVert; + + // Top Left Triangle + + *idxPtr = p00; + idxPtr++; + *idxPtr = p01; + idxPtr++; + *idxPtr = p11; + idxPtr++; + + // Bottom Right Triangle + + *idxPtr = p00; + idxPtr++; + *idxPtr = p11; + idxPtr++; + *idxPtr = p10; + idxPtr++; + + startBorderVert++; + startGridVert += curStepSize; + } + } + + // Fill in 'horizon' triangles. + + U32 curHorizonVert = lastBorderVert + 1; + U32 curBorderVert = firstBorderVert; + + for ( U32 i = 0; i < gridSize; i++ ) + { + p00 = curBorderVert; + p10 = curBorderVert + 1; + p01 = curHorizonVert; + p11 = curHorizonVert + 1; + + // Top Left Triangle + + *idxPtr = p00; + idxPtr++; + *idxPtr = p01; + idxPtr++; + *idxPtr = p11; + idxPtr++; + + // Bottom Right Triangle + + *idxPtr = p00; + idxPtr++; + *idxPtr = p11; + idxPtr++; + *idxPtr = p10; + idxPtr++; + + curBorderVert++; + curHorizonVert++; + } + + mPrimBuff.unlock(); +} + +SceneData WaterPlane::setupSceneGraphInfo( SceneRenderState *state ) +{ + SceneData sgData; + + sgData.lights[0] = LIGHTMGR->getSpecialLight( LightManager::slSunLightType ); + + // fill in water's transform + sgData.objTrans = &getRenderTransform(); + + // fog + sgData.setFogParams( state->getSceneManager()->getFogData() ); + + // misc + sgData.backBuffTex = REFLECTMGR->getRefractTex(); + sgData.reflectTex = mPlaneReflector.reflectTex; + sgData.wireframe = GFXDevice::getWireframe() || smWireframe; + + return sgData; +} + +void WaterPlane::setShaderParams( SceneRenderState *state, BaseMatInstance* mat, const WaterMatParams& paramHandles) +{ + // Set variables that will be assigned to shader consts within WaterCommon + // before calling Parent::setShaderParams + + mUndulateMaxDist = mGridElementSize * mGridSizeMinusOne * 0.5f; + + Parent::setShaderParams( state, mat, paramHandles ); + + // Now set the rest of the shader consts that are either unique to this + // class or that WaterObject leaves to us to handle... + + MaterialParameters* matParams = mat->getMaterialParameters(); + + // set vertex shader constants + //----------------------------------- + matParams->setSafe(paramHandles.mGridElementSizeSC, (F32)mGridElementSize); + //matParams->setSafe( paramHandles.mReflectTexSizeSC, mReflectTexSize ); + if ( paramHandles.mModelMatSC->isValid() ) + matParams->set(paramHandles.mModelMatSC, getRenderTransform(), GFXSCT_Float4x4); + + // set pixel shader constants + //----------------------------------- + + ColorF c( mWaterFogData.color ); + matParams->setSafe( paramHandles.mBaseColorSC, c ); + + // By default we need to show a true reflection is fullReflect is enabled and + // we are above water. + F32 reflect = mPlaneReflector.isEnabled() && !isUnderwater( state->getCameraPosition() ); + + // If we were occluded the last frame a query was fetched ( not necessarily last frame ) + // and we weren't updated last frame... we don't have a valid texture to show + // so use the cubemap / fake reflection color this frame. + if ( mPlaneReflector.lastUpdateMs != REFLECTMGR->getLastUpdateMs() && mPlaneReflector.isOccluded() ) + reflect = false; + + //Point4F reflectParams( getRenderPosition().z, mReflectMinDist, mReflectMaxDist, reflect ); + Point4F reflectParams( getRenderPosition().z, 0.0f, 1000.0f, !reflect ); + + // TODO: This is a hack... why is this broken... check after + // we merge advanced lighting with trunk! + // + reflectParams.z = 0.0f; + matParams->setSafe( paramHandles.mReflectParamsSC, reflectParams ); + + VectorF reflectNorm( 0, 0, 1 ); + matParams->setSafe(paramHandles.mReflectNormalSC, reflectNorm ); +} + +void WaterPlane::prepRenderImage( SceneRenderState *state ) +{ + PROFILE_SCOPE(WaterPlane_prepRenderImage); + + if( !state->isDiffusePass() ) + return; + + mBasicLighting = dStricmp( LIGHTMGR->getId(), "BLM" ) == 0; + mUnderwater = isUnderwater( state->getCameraPosition() ); + + mMatrixSet->setSceneView(GFX->getWorldMatrix()); + + const Frustum &frustum = state->getFrustum(); + + if ( mPrimBuff.isNull() || + mGenerateVB || + frustum != mFrustum ) + { + mFrustum = frustum; + setupVBIB( state ); + mGenerateVB = false; + + MatrixF proj( true ); + MathUtils::getZBiasProjectionMatrix( 0.0001f, mFrustum, &proj ); + mMatrixSet->setSceneProjection(proj); + } + + _getWaterPlane( state->getCameraPosition(), mWaterPlane, mWaterPos ); + mWaterFogData.plane = mWaterPlane; + mPlaneReflector.refplane = mWaterPlane; + updateUnderwaterEffect( state ); + + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &WaterObject::renderObject ); + ri->type = RenderPassManager::RIT_Water; + state->getRenderPass()->addInst( ri ); + + //mRenderUpdateCount++; +} + +void WaterPlane::innerRender( SceneRenderState *state ) +{ + GFXDEBUGEVENT_SCOPE( WaterPlane_innerRender, ColorI( 255, 0, 0 ) ); + + const Point3F &camPosition = state->getCameraPosition(); + + Point3F rvec, fvec, uvec, pos; + + const MatrixF &objMat = getTransform(); //getRenderTransform(); + const MatrixF &camMat = state->getCameraTransform(); + + MatrixF renderMat( true ); + + camMat.getColumn( 1, &fvec ); + uvec.set( 0, 0, 1 ); + rvec = mCross( fvec, uvec ); + rvec.normalize(); + fvec = mCross( uvec, rvec ); + pos = camPosition; + pos.z = objMat.getPosition().z; + + renderMat.setColumn( 0, rvec ); + renderMat.setColumn( 1, fvec ); + renderMat.setColumn( 2, uvec ); + renderMat.setColumn( 3, pos ); + + setRenderTransform( renderMat ); + + // Setup SceneData + SceneData sgData = setupSceneGraphInfo( state ); + + // set the material + S32 matIdx = getMaterialIndex( camPosition ); + + if ( !initMaterial( matIdx ) ) + return; + + BaseMatInstance *mat = mMatInstances[matIdx]; + WaterMatParams matParams = mMatParamHandles[matIdx]; + + // render the geometry + if ( mat ) + { + // setup proj/world transform + mMatrixSet->restoreSceneViewProjection(); + mMatrixSet->setWorld(getRenderTransform()); + + setShaderParams( state, mat, matParams ); + + while( mat->setupPass( state, sgData ) ) + { + mat->setSceneInfo(state, sgData); + mat->setTransforms(*mMatrixSet, state); + setCustomTextures( matIdx, mat->getCurPass(), matParams ); + + // set vert/prim buffer + GFX->setVertexBuffer( mVertBuff ); + GFX->setPrimitiveBuffer( mPrimBuff ); + GFX->drawIndexedPrimitive( GFXTriangleList, 0, 0, mVertCount, 0, mPrimCount ); + } + } +} + +bool WaterPlane::isUnderwater( const Point3F &pnt ) const +{ + F32 height = getPosition().z; + + F32 diff = pnt.z - height; + + return ( diff < 0.1 ); +} + +F32 WaterPlane::distanceTo( const Point3F& point ) const +{ + if( isUnderwater( point ) ) + return 0.f; + else + return ( point.z - getPosition().z ); +} + +void WaterPlane::inspectPostApply() +{ + Parent::inspectPostApply(); + + setMaskBits( UpdateMask ); +} + +void WaterPlane::setTransform( const MatrixF &mat ) +{ + // We only accept the z value from the new transform. + + MatrixF newMat( true ); + + Point3F newPos = getPosition(); + newPos.z = mat.getPosition().z; + newMat.setPosition( newPos ); + + Parent::setTransform( newMat ); + + // Parent::setTransforms ends up setting our worldBox to something other than + // global, so we have to set it back... but we can't actually call setGlobalBounds + // again because it does extra work adding and removing us from the container. + + mGlobalBounds = true; + mObjBox.minExtents.set(-1e10, -1e10, -1e10); + mObjBox.maxExtents.set( 1e10, 1e10, 1e10); + + // Keep mWaterPlane up to date. + mWaterFogData.plane.set( 0, 0, 1, -getPosition().z ); +} + +void WaterPlane::onStaticModified( const char* slotName, const char*newValue ) +{ + Parent::onStaticModified( slotName, newValue ); + + if ( dStricmp( slotName, "surfMaterial" ) == 0 ) + setMaskBits( MaterialMask ); +} + +bool WaterPlane::castRay(const Point3F& start, const Point3F& end, RayInfo* info ) +{ + // Simply look for the hit on the water plane + // and ignore any future issues with waves, etc. + const Point3F norm(0,0,1); + PlaneF plane( Point3F::Zero, norm ); + + F32 hit = plane.intersect( start, end ); + if ( hit < 0.0f || hit > 1.0f ) + return false; + + info->t = hit; + info->object = this; + info->point = start + ( ( end - start ) * hit ); + info->normal = norm; + info->material = mMatInstances[ WaterMat ]; + + return true; +} + +F32 WaterPlane::getWaterCoverage( const Box3F &testBox ) const +{ + F32 posZ = getPosition().z; + + F32 coverage = 0.0f; + + if ( posZ > testBox.minExtents.z ) + { + if ( posZ < testBox.maxExtents.z ) + coverage = (posZ - testBox.minExtents.z) / (testBox.maxExtents.z - testBox.minExtents.z); + else + coverage = 1.0f; + } + + return coverage; +} + +F32 WaterPlane::getSurfaceHeight( const Point2F &pos ) const +{ + return getPosition().z; +} + +void WaterPlane::onReflectionInfoChanged() +{ + /* + if ( isClientObject() && GFX->getPixelShaderVersion() >= 1.4 ) + { + if ( mFullReflect ) + REFLECTMGR->registerObject( this, ReflectDelegate( this, &WaterPlane::updateReflection ), mReflectPriority, mReflectMaxRateMs, mReflectMaxDist ); + else + { + REFLECTMGR->unregisterObject( this ); + mReflectTex = NULL; + } + } + */ +} + +void WaterPlane::setGridSize( U32 inSize ) +{ + if ( inSize == mGridSize ) + return; + + // GridSize must be an odd number. + //if ( inSize % 2 == 0 ) + // inSize++; + + // GridSize must be at least 1 + inSize = getMax( inSize, (U32)1 ); + + mGridSize = inSize; + mGridSizeMinusOne = mGridSize - 1; + mGenerateVB = true; + setMaskBits( UpdateMask ); +} + +void WaterPlane::setGridElementSize( F32 inSize ) +{ + if ( inSize == mGridElementSize ) + return; + + // GridElementSize must be greater than 0 + inSize = getMax( inSize, 0.0001f ); + + mGridElementSize = inSize; + mGenerateVB = true; + setMaskBits( UpdateMask ); +} + +bool WaterPlane::protectedSetGridSize( void *obj, const char *index, const char *data ) +{ + WaterPlane *object = static_cast(obj); + S32 size = dAtoi( data ); + + object->setGridSize( size ); + + // We already set the field. + return false; +} + +bool WaterPlane::protectedSetGridElementSize( void *obj, const char *index, const char *data ) +{ + WaterPlane *object = static_cast(obj); + F32 size = dAtof( data ); + + object->setGridElementSize( size ); + + // We already set the field. + return false; +} + +void WaterPlane::_getWaterPlane( const Point3F &camPos, PlaneF &outPlane, Point3F &outPos ) +{ + outPos = getPosition(); + outPlane.set( outPos, Point3F(0,0,1) ); +} \ No newline at end of file diff --git a/Engine/source/environment/waterPlane.h b/Engine/source/environment/waterPlane.h new file mode 100644 index 000000000..8902d2a64 --- /dev/null +++ b/Engine/source/environment/waterPlane.h @@ -0,0 +1,147 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _WATERPLANE_H_ +#define _WATERPLANE_H_ + +#ifndef _GAMEBASE_H_ +#include "T3D/gameBase/gameBase.h" +#endif +#ifndef _GFXDEVICE_H_ +#include "gfx/gfxDevice.h" +#endif +#ifndef _SCENEDATA_H_ +#include "materials/sceneData.h" +#endif +#ifndef _MATINSTANCE_H_ +#include "materials/matInstance.h" +#endif +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif +#ifndef _RENDERPASSMANAGER_H_ +#include "renderInstance/renderPassManager.h" +#endif +#ifndef _MATHUTIL_FRUSTUM_H_ +#include "math/util/frustum.h" +#endif +#ifndef _WATEROBJECT_H_ +#include "environment/waterObject.h" +#endif + + +//***************************************************************************** +// WaterPlane +//***************************************************************************** +class WaterPlane : public WaterObject +{ + typedef WaterObject Parent; + +public: + + // LEGACY support + enum EWaterType + { + eWater = 0, + eOceanWater = 1, + eRiverWater = 2, + eStagnantWater = 3, + eLava = 4, + eHotLava = 5, + eCrustyLava = 6, + eQuicksand = 7, + }; + +private: + + enum MaskBits { + UpdateMask = Parent::NextFreeMask, + NextFreeMask = Parent::NextFreeMask << 1 + }; + + // vertex / index buffers + GFXVertexBufferHandle mVertBuff; + GFXPrimitiveBufferHandle mPrimBuff; + + // misc + U32 mGridSize; + U32 mGridSizeMinusOne; + F32 mGridElementSize; + U32 mVertCount; + U32 mIndxCount; + U32 mPrimCount; + Frustum mFrustum; + + SceneData setupSceneGraphInfo( SceneRenderState *state ); + void setShaderParams( SceneRenderState *state, BaseMatInstance* mat, const WaterMatParams& paramHandles ); + void setupVBIB( SceneRenderState *state ); + virtual void prepRenderImage( SceneRenderState *state ); + virtual void innerRender( SceneRenderState *state ); + void setMultiPassProjection(); + +protected: + + //------------------------------------------------------- + // Standard engine functions + //------------------------------------------------------- + bool onAdd(); + void onRemove(); + U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream); + void unpackUpdate(NetConnection *conn, BitStream *stream); + bool castRay(const Point3F &start, const Point3F &end, RayInfo* info); + +public: + WaterPlane(); + virtual ~WaterPlane(); + + DECLARE_CONOBJECT(WaterPlane); + + static void initPersistFields(); + void onStaticModified( const char* slotName, const char*newValue = NULL ); + virtual void inspectPostApply(); + virtual void setTransform( const MatrixF & mat ); + virtual F32 distanceTo( const Point3F& point ) const; + + // WaterObject + virtual F32 getWaterCoverage( const Box3F &worldBox ) const; + virtual F32 getSurfaceHeight( const Point2F &pos ) const; + virtual void onReflectionInfoChanged(); + virtual bool isUnderwater( const Point3F &pnt ) const; + + // WaterBlock + bool isPointSubmerged ( const Point3F &pos, bool worldSpace = true ) const{ return true; } + + // WaterPlane + void setGridSize( U32 inSize ); + void setGridElementSize( F32 inSize ); + + // Protected Set'ers + static bool protectedSetGridSize( void *object, const char *index, const char *data ); + static bool protectedSetGridElementSize( void *object, const char *index, const char *data ); + +protected: + + // WaterObject + virtual void _getWaterPlane( const Point3F &camPos, PlaneF &outPlane, Point3F &outPos ); +}; + +#endif // _WATERPLANE_H_ diff --git a/Engine/source/forest/editor/forestBrushElement.cpp b/Engine/source/forest/editor/forestBrushElement.cpp new file mode 100644 index 000000000..3b870e227 --- /dev/null +++ b/Engine/source/forest/editor/forestBrushElement.cpp @@ -0,0 +1,200 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "forest/editor/forestBrushElement.h" + +#include "console/consoleTypes.h" +#include "forest/forestItem.h" + + +//------------------------------------------------------------------------- +// ForestBrushElement +//------------------------------------------------------------------------- + +IMPLEMENT_CONOBJECT( ForestBrushElement ); + +ConsoleDocClass( ForestBrushElement, + "@brief Represents a type of ForestItem and parameters for how it is placed" + " when painting with a ForestBrush that contains it.\n\n" + "@ingroup Forest" +); + +ForestBrushElement::ForestBrushElement() +: mData( NULL ), + mProbability( 1 ), + mRotationRange( 360 ), + mScaleMin( 1 ), + mScaleMax( 1 ), + mScaleExponent( 1 ), + mSinkMin( 0.0f ), + mSinkMax( 0.0f ), + mSinkRadius( 1 ), + mSlopeMax( 90.0f ), + mSlopeMin( 0.0f ), + mElevationMin( -10000.0f ), + mElevationMax( 10000.0f ) +{ +} + + +void ForestBrushElement::initPersistFields() +{ + Parent::initPersistFields(); + + addGroup( "ForestBrushElement" ); + + addField( "forestItemData", TYPEID< ForestItemData >(), Offset( mData, ForestBrushElement ), + "The type of ForestItem this element holds placement parameters for." ); + + addField( "probability", TypeF32, Offset( mProbability, ForestBrushElement ), + "The probability that this element will be created during an editor brush stroke " + "is the sum of all element probabilities in the brush divided by the probability " + "of this element." ); + + addField( "rotationRange", TypeF32, Offset( mRotationRange, ForestBrushElement ), + "The max rotation in degrees that items will be placed." ); + + addField( "scaleMin", TypeF32, Offset( mScaleMin, ForestBrushElement ), + "The minimum random size for each item." ); + + addField( "scaleMax", TypeF32, Offset( mScaleMax, ForestBrushElement ), + "The maximum random size of each item." ); + + addField( "scaleExponent", TypeF32, Offset( mScaleExponent, ForestBrushElement ), + "An exponent used to bias between the minimum and maximum random sizes." ); + + addField( "sinkMin", TypeF32, Offset( mSinkMin, ForestBrushElement ), + "Min variation in the sink radius." ); + + addField( "sinkMax", TypeF32, Offset( mSinkMax, ForestBrushElement ), + "Max variation in the sink radius." ); + + addField( "sinkRadius", TypeF32, Offset( mSinkRadius, ForestBrushElement ), + "This is the radius used to calculate how much to sink the trunk at " + "its base and is used to sink the tree into the ground when its on a slope." ); + + addField( "slopeMin", TypeF32, Offset( mSlopeMin, ForestBrushElement ), + "The min surface slope in degrees this item will be placed on." ); + + addField( "slopeMax", TypeF32, Offset( mSlopeMax, ForestBrushElement ), + "The max surface slope in degrees this item will be placed on." ); + + addField( "elevationMin", TypeF32, Offset( mElevationMin, ForestBrushElement ), + "The min world space elevation this item will be placed." ); + + addField( "elevationMax", TypeF32, Offset( mElevationMax, ForestBrushElement ), + "The max world space elevation this item will be placed." ); + + endGroup( "ForestBrushElement" ); +} + + +//------------------------------------------------------------------------- +// ForestBrushElementSet +//------------------------------------------------------------------------- + +SimObjectPtr ForestBrush::smGroup = NULL; + +IMPLEMENT_CONOBJECT( ForestBrush ); + +ConsoleDocClass( ForestBrush, + "@brief Container class for ForestBrushElements\n\n" + "Editor use only.\n\n" + "@internal" +); + +ForestBrush::ForestBrush() +{ + +} + +bool ForestBrush::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + getGroup()->addObject( this ); + + return true; +} + +void ForestBrush::addObject( SimObject *inObj ) +{ + ForestBrushElement *ele = dynamic_cast( inObj ); + if ( !ele ) + return; + + //if ( containsItemData( ele->mData ) ) + // return; + + Parent::addObject( inObj ); +} + +SimGroup* ForestBrush::getGroup() +{ + if ( !smGroup ) + { + SimGroup *dummy; + if ( Sim::findObject( "ForestBrushGroup", dummy ) ) + { + smGroup = dummy; + return smGroup; + } + + smGroup = new SimGroup; + smGroup->assignName( "ForestBrushGroup" ); + smGroup->registerObject(); + Sim::getRootGroup()->addObject( smGroup ); + } + + return smGroup; +} + +bool ForestBrush::containsItemData( const ForestItemData *inData ) +{ + SimObjectList::iterator iter = objectList.begin(); + for ( ; iter != objectList.end(); iter++ ) + { + ForestBrushElement *pElement = dynamic_cast(*iter); + + if ( !pElement ) + continue; + + if ( pElement->mData == inData ) + return true; + } + + return false; +} + +ConsoleMethod( ForestBrush, containsItemData, bool, 3, 3, "( ForestItemData obj )" ) +{ + ForestItemData *data = NULL; + if ( !Sim::findObject( argv[2], data ) ) + { + Con::warnf( "ForestBrush::containsItemData - invalid object passed" ); + return false; + } + + return object->containsItemData( data ); +} \ No newline at end of file diff --git a/Engine/source/forest/editor/forestBrushElement.h b/Engine/source/forest/editor/forestBrushElement.h new file mode 100644 index 000000000..be6a37cc0 --- /dev/null +++ b/Engine/source/forest/editor/forestBrushElement.h @@ -0,0 +1,125 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FOREST_EDITOR_BRUSHELEMENT_H_ +#define _FOREST_EDITOR_BRUSHELEMENT_H_ + +//#ifndef _SIMOBJECT_H_ +//#include "console/simObject.h" +//#endif +#ifndef _SIMSET_H_ +#include "console/simSet.h" +#endif + + +//------------------------------------------------------------------------- +// ForestBrushElement +//------------------------------------------------------------------------- + +class ForestItemData; + +class ForestBrushElement : public SimObject +{ + typedef SimObject Parent; + +public: + + ForestBrushElement(); + + DECLARE_CONOBJECT( ForestBrushElement ); + + static void initPersistFields(); + +public: + + /// The type of ForestItem this element holds placement parameters for. + ForestItemData *mData; + + /// The probability that this element will be + /// created during an editor brush stroke. + F32 mProbability; + + /// The max rotation in degrees that items will be placed. + F32 mRotationRange; + + /// The minimum random size for each item. + F32 mScaleMin; + + /// The maximum random size of each item. + F32 mScaleMax; + + /// An exponent used to bias between the minimum + /// and maximum random sizes. + F32 mScaleExponent; + + /// Min variation in the sink radius. + F32 mSinkMin; + + /// Max variation in the sink radius. + F32 mSinkMax; + + /// This is the radius used to calculate how much to + /// sink the trunk at its base and is used to sink + /// the tree into the ground when its on a slope. + F32 mSinkRadius; + + /// The min surface slope in degrees this item will be placed on. + F32 mSlopeMin; + + /// The max surface slope in degrees this item will be placed on. + F32 mSlopeMax; + + /// The min world space elevation this item will be placed. + F32 mElevationMin; + + /// The max world space elevation this item will be placed. + F32 mElevationMax; +}; + +//------------------------------------------------------------------------- +// ForestBrush +//------------------------------------------------------------------------- + +class ForestBrush : public SimGroup +{ + typedef SimGroup Parent; + +public: + + ForestBrush(); + + DECLARE_CONOBJECT( ForestBrush ); + + virtual bool onAdd(); + + virtual void addObject(SimObject*); + + static SimGroup* getGroup(); + + bool containsItemData( const ForestItemData *inData ); +protected: + + static SimObjectPtr smGroup; +}; + + +#endif // _FOREST_EDITOR_BRUSHELEMENT_H_ \ No newline at end of file diff --git a/Engine/source/forest/editor/forestBrushTool.cpp b/Engine/source/forest/editor/forestBrushTool.cpp new file mode 100644 index 000000000..a32e28ed7 --- /dev/null +++ b/Engine/source/forest/editor/forestBrushTool.cpp @@ -0,0 +1,687 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "forest/editor/forestBrushTool.h" + +#include "forest/forest.h" +#include "forest/editor/forestUndo.h" +#include "forest/editor/forestBrushElement.h" +#include "forest/editor/forestEditorCtrl.h" + +#include "gui/worldEditor/editTSCtrl.h" +#include "console/consoleTypes.h" +#include "core/util/tVector.h" +#include "gfx/gfxDrawUtil.h" +#include "gui/core/guiCanvas.h" +#include "gfx/primBuilder.h" +#include "gui/controls/guiTreeViewCtrl.h" +#include "core/strings/stringUnit.h" +#include "math/mRandomDeck.h" +#include "math/mRandomSet.h" + + +bool ForestBrushTool::protectedSetSize( void *object, const char *index, const char *data ) +{ + ForestBrushTool *tool = static_cast( object ); + F32 val = dAtof(data); + tool->setSize( val ); + + return false; +} + +bool ForestBrushTool::protectedSetPressure( void *object, const char *index, const char *data ) +{ + ForestBrushTool *tool = static_cast( object ); + F32 val = dAtof(data); + tool->setPressure( val ); + + return false; +} + +bool ForestBrushTool::protectedSetHardness( void *object, const char *index, const char *data ) +{ + ForestBrushTool *tool = static_cast( object ); + F32 val = dAtof(data); + tool->setHardness( val ); + + return false; +} + +ImplementEnumType( ForestBrushMode, + "Active brush mode type.\n" + "@internal\n\n") + { ForestBrushTool::Paint, "Paint", "Creates Items based on the Elements you have selected.\n" }, + { ForestBrushTool::Erase, "Erase", "Erases Items of any Mesh type.\n" }, + { ForestBrushTool::EraseSelected, "EraseSelected", "Erases items of a specific type.\n" }, +EndImplementEnumType; + + +ForestBrushTool::ForestBrushTool() + : mSize( 5.0f ), + mPressure( 0.1f ), + mHardness( 1.0f ), + mDrawBrush( false ), + mCurrAction( NULL ), + mStrokeEvent( 0 ), + mBrushDown( false ), + mColor( ColorI::WHITE ), + mMode( Paint ), + mRandom( Platform::getRealMilliseconds() + 1 ) +{ +} + +ForestBrushTool::~ForestBrushTool() +{ +} + +IMPLEMENT_CONOBJECT( ForestBrushTool ); + +ConsoleDocClass( ForestBrushTool, + "@brief Defines the brush properties when painting trees in Forest Editor\n\n" + "Editor use only.\n\n" + "@internal" +); + +void ForestBrushTool::initPersistFields() +{ + addGroup( "ForestBrushTool" ); + + addField( "mode", TYPEID< BrushMode >(), Offset( mMode, ForestBrushTool) ); + + addProtectedField( "size", TypeF32, Offset( mSize, ForestBrushTool ), + &protectedSetSize, &defaultProtectedGetFn, "Brush Size" ); + + addProtectedField( "pressure", TypeF32, Offset( mPressure, ForestBrushTool ), + &protectedSetPressure, &defaultProtectedGetFn, "Brush Pressure" ); + + addProtectedField( "hardness", TypeF32, Offset( mHardness, ForestBrushTool ), + &protectedSetHardness, &defaultProtectedGetFn, "Brush Hardness" ); + + endGroup( "ForestBrushTool" ); + + Parent::initPersistFields(); +} + +bool ForestBrushTool::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + return true; +} + +void ForestBrushTool::onRemove() +{ + Parent::onRemove(); +} + +void ForestBrushTool::on3DMouseDown( const Gui3DMouseEvent &evt ) +{ + Con::executef( this, "onMouseDown" ); + + if ( !_updateBrushPoint( evt ) || !mForest ) + return; + + mBrushDown = true; + + mEditor->getRoot()->showCursor( false ); + + _collectElements(); + _onStroke(); + + return; +} + +void ForestBrushTool::on3DMouseUp( const Gui3DMouseEvent &evt ) +{ + _updateBrushPoint( evt ); + Sim::cancelEvent( mStrokeEvent ); + mBrushDown = false; + + mEditor->getRoot()->showCursor( true ); + + if ( mCurrAction ) + { + _submitUndo( mCurrAction ); + mCurrAction = NULL; + } + + //mElements.clear(); +} + +void ForestBrushTool::on3DMouseMove( const Gui3DMouseEvent &evt ) +{ + _updateBrushPoint( evt ); +} + +void ForestBrushTool::on3DMouseDragged( const Gui3DMouseEvent &evt ) +{ + _updateBrushPoint( evt ); + + if ( mBrushDown && !Sim::isEventPending( mStrokeEvent ) ) + mStrokeEvent = Sim::postEvent( this, new ForestBrushToolEvent(), Sim::getCurrentTime() + 250 ); +} + +bool ForestBrushTool::onMouseWheel( const GuiEvent &evt ) +{ + if ( evt.modifier & SI_PRIMARY_CTRL ) + setPressure( mPressure + evt.fval * ( 0.05f / 120.0f ) ); + else if ( evt.modifier & SI_SHIFT ) + setHardness( mHardness + evt.fval * ( 0.05f / 120.0f ) ); + else + setSize( mSize + evt.fval * ( 1.0f / 120.0f ) ); + + return true; +} + +void ForestBrushTool::onRender3D( ) +{ +} + +void ForestBrushTool::onRender2D() +{ + if ( !mDrawBrush ) + return; + + if ( !mEditor->getRoot()->isCursorON() && !mBrushDown ) + return; + + RayInfo ri; + Point3F start( 0, 0, mLastBrushPoint.z + mSize ); + Point3F end( 0, 0, mLastBrushPoint.z - mSize ); + + Vector pointList; + + const U32 steps = 32; + + if ( mForest ) + mForest->disableCollision(); + + for ( S32 i = 0; i < steps; i++ ) + { + F32 radians = (F32)i / (F32)(steps-1) * M_2PI_F; + VectorF vec(0,1,0); + MathUtils::vectorRotateZAxis( vec, radians ); + + end.x = start.x = mLastBrushPoint.x + vec.x * mSize; + end.y = start.y = mLastBrushPoint.y + vec.y * mSize; + + bool hit = gServerContainer.castRay( start, end, TerrainObjectType | StaticShapeObjectType, &ri ); + + if ( hit ) + pointList.push_back( ri.point ); + } + + if ( mForest ) + mForest->enableCollision(); + + if ( pointList.empty() ) + return; + + ColorI brushColor( ColorI::WHITE ); + + if ( mMode == Paint ) + brushColor = ColorI::BLUE; + else if ( mMode == Erase ) + brushColor = ColorI::RED; + else if ( mMode == EraseSelected ) + brushColor.set( 150, 0, 0 ); + + if ( mMode == Paint || mMode == EraseSelected ) + { + if ( mElements.empty() ) + { + brushColor.set( 140, 140, 140 ); + } + } + + mEditor->drawLineList( pointList, brushColor, 1.5f ); +} + +void ForestBrushTool::onActivated( const Gui3DMouseEvent &lastEvent ) +{ + _updateBrushPoint( lastEvent ); + + Con::executef( this, "onActivated" ); +} + +void ForestBrushTool::onDeactivated() +{ + Con::executef( this, "onDeactivated" ); +} + +bool ForestBrushTool::updateGuiInfo() +{ + GuiTextCtrl *statusbar; + Sim::findObject( "EWorldEditorStatusBarInfo", statusbar ); + + GuiTextCtrl *selectionBar; + Sim::findObject( "EWorldEditorStatusBarSelection", selectionBar ); + + String text; + + if ( mMode == Paint ) + text = "Forest Editor ( Paint Tool ) - This brush creates Items based on the Elements you have selected."; + else if ( mMode == Erase ) + text = "Forest Editor ( Erase Tool ) - This brush erases Items of any Mesh type."; + else if ( mMode == EraseSelected ) + text = "Forest Editor ( Erase Selected ) - This brush erases Items based on the Elements you have selected."; + + if ( statusbar ) + statusbar->setText( text ); + + if ( mMode == Paint || mMode == EraseSelected ) + text = String::ToString( "%i elements selected", mElements.size() ); + else + text = ""; + + if ( selectionBar ) + selectionBar->setText( text ); + + return true; +} + +void ForestBrushTool::setSize( F32 val ) +{ + mSize = mClampF( val, 0.0f, 150.0f ); + Con::executef( this, "syncBrushToolbar" ); +} + +void ForestBrushTool::setPressure( F32 val ) +{ + mPressure = mClampF( val, 0.0f, 1.0f ); + Con::executef( this, "syncBrushToolbar" ); +} + +void ForestBrushTool::setHardness( F32 val ) +{ + mHardness = mClampF( val, 0.0f, 1.0f ); + Con::executef( this, "syncBrushToolbar" ); +} + +void ForestBrushTool::_onStroke() +{ + if ( !mForest || !mBrushDown ) + return; + + _action( mLastBrushPoint ); +} + +void ForestBrushTool::_action( const Point3F &point ) +{ + if ( mMode == Paint ) + _paint( point ); + else if ( mMode == Erase || mMode == EraseSelected ) + _erase( point ); +} + +inline F32 mCircleArea( F32 radius ) +{ + return radius * radius * M_PI_F; +} + +void ForestBrushTool::_paint( const Point3F &point ) +{ + AssertFatal( mForest, "ForestBrushTool::_paint() - Can't paint without a Forest!" ); + + if ( mElements.empty() ) + return; + + // Iterators, pointers, and temporaries. + ForestBrushElement *pElement; + ForestItemData *pData; + F32 radius, area; + + // How much area do we have to fill with trees ( within the brush ). + F32 fillArea = mCircleArea( mSize ); + + // Scale that down by pressure, this is how much area we want to fill. + fillArea *= mPressure; + + // Create an MRandomSet we can get items we are painting out of with + // the desired distribution. + // Also grab the smallest and largest radius elements while we are looping. + + ForestBrushElement *smallestElement, *largestElement; + smallestElement = largestElement = mElements[0]; + + MRandomSet randElementSet(&mRandom); + + for ( S32 i = 0; i < mElements.size(); i++ ) + { + pElement = mElements[i]; + + if ( pElement->mData->mRadius > largestElement->mData->mRadius ) + largestElement = pElement; + if ( pElement->mData->mRadius < smallestElement->mData->mRadius ) + smallestElement = pElement; + + randElementSet.add( pElement, pElement->mProbability ); + } + + // Pull elements from the random set until we would theoretically fill + // the desired area. + + F32 areaLeft = fillArea; + F32 scaleFactor, sink, randRot, worldCoordZ, slope; + Point2F worldCoords; + Point3F normalVector, right; + Point2F temp; + + const S32 MaxTries = 5; + + + while ( areaLeft > 0.0f ) + { + pElement = randElementSet.get(); + pData = pElement->mData; + + scaleFactor = mLerp( pElement->mScaleMin, pElement->mScaleMax, mClampF( mPow( mRandom.randF(), pElement->mScaleExponent ), 0.0f, 1.0f ) ); + radius = getMax( pData->mRadius * scaleFactor, 0.1f ); + area = mCircleArea( radius ); + + areaLeft -= area * 5.0f; // fudge value + + // No room left we are done. + //if ( areaLeft < 0.0f ) + // break; + + // We have area left to fill... + + const F32 rotRange = mDegToRad( pElement->mRotationRange ); + + // Get a random sink value. + sink = mRandom.randF( pElement->mSinkMin, pElement->mSinkMax ) * scaleFactor; + + // Get a random rotation. + randRot = mRandom.randF( 0.0f, rotRange ); + + // Look for a place within the brush area to place this item. + // We may have to try several times or give and go onto the next. + + S32 i = 0; + for ( ; i < MaxTries; i++ ) + { + // Pick some randoms for placement. + worldCoords = MathUtils::randomPointInCircle( mSize ) + point.asPoint2F(); + + // Look for the ground at this position. + if ( !getGroundAt( Point3F( worldCoords.x, worldCoords.y , point.z ), + &worldCoordZ, + &normalVector ) ) + { + continue; + } + + // Does this pass our slope and elevation limits. + right.set( normalVector.x, normalVector.y, 0 ); + right.normalizeSafe(); + slope = mRadToDeg( mDot( right, normalVector ) ); + + if ( worldCoordZ < pElement->mElevationMin || + worldCoordZ > pElement->mElevationMax || + slope < pElement->mSlopeMin || + slope > pElement->mSlopeMax ) + { + continue; + } + + // Are we up against another tree? + if ( mForest->getData()->getItems( worldCoords, radius, NULL ) > 0 ) + continue; + + // If the trunk radius is set then we need to sink + // the tree into the ground a bit to hide the bottom. + if ( pElement->mSinkRadius > 0.0f ) + { + // Items that are not aligned to the ground surface + // get sunken down to hide the bottom of their trunks. + + // sunk down a bit to hide their bottoms on slopes. + normalVector.z = 0; + normalVector.normalizeSafe(); + normalVector *= sink; + temp = worldCoords + normalVector.asPoint2F(); + getGroundAt( Point3F( temp.x, temp.y, point.z ), + &worldCoordZ, + NULL ); + } + + worldCoordZ -= sink; + + // Create a new undo action if this is a new stroke. + ForestCreateUndoAction *action = dynamic_cast( mCurrAction ); + if ( !action ) + { + action = new ForestCreateUndoAction( mForest->getData(), mEditor ); + mCurrAction = action; + } + + //Con::printf( "worldCoords = %g, %g, %g", worldCoords.x, worldCoords.y, worldCoordZ ); + + // Let the action manage adding it to the forest. + action->addItem( pData, + Point3F( worldCoords.x, worldCoords.y, worldCoordZ ), + randRot, + scaleFactor ); + break; + } + } +} + +void ForestBrushTool::_erase( const Point3F &point ) +{ + AssertFatal( mForest, "ForestBrushTool::_erase() - Can't erase without a Forest!" ); + + // First grab all the forest items around the point. + ForestItemVector trees; + if ( mForest->getData()->getItems( point.asPoint2F(), mSize, &trees ) == 0 ) + return; + + if ( mMode == EraseSelected ) + { + for ( U32 i = 0; i < trees.size(); i++ ) + { + const ForestItem &tree = trees[i]; + + if ( !mDatablocks.contains( tree.getData() ) ) + { + trees.erase_fast( i ); + i--; + } + } + } + + if ( trees.empty() ) + return; + + // Number of trees to erase depending on pressure. + S32 eraseCount = getMax( (S32)mCeil( (F32)trees.size() * mPressure ), 0 ); + + // Initialize an MRandomDeck with trees under the brush. + MRandomDeck deck(&mRandom); + deck.addToPile( trees ); + deck.shuffle(); + + ForestItem currentTree; + + // Draw eraseCount number of trees from MRandomDeck, adding them to our erase action. + for ( U32 i = 0; i < eraseCount; i++ ) + { + deck.draw(¤tTree); + + // Create a new undo action if this is a new stroke. + ForestDeleteUndoAction *action = dynamic_cast( mCurrAction ); + if ( !action ) + { + action = new ForestDeleteUndoAction( mForest->getData(), mEditor ); + mCurrAction = action; + } + + action->removeItem( currentTree ); + } +} + +bool ForestBrushTool::_updateBrushPoint( const Gui3DMouseEvent &event_ ) +{ + // Do a raycast for terrain... thats the placement center. + const U32 mask = TerrainObjectType | StaticShapeObjectType; // TODO: Make an option! + + Point3F start( event_.pos ); + Point3F end( event_.pos + ( event_.vec * 10000.0f ) ); + + if ( mForest ) + mForest->disableCollision(); + + RayInfo rinfo; + mDrawBrush = gServerContainer.castRay( start, end, mask, &rinfo ); + + if ( mForest ) + mForest->enableCollision(); + + if ( mDrawBrush ) + { + mLastBrushPoint = rinfo.point; + mLastBrushNormal = rinfo.normal; + } + + return mDrawBrush; +} + +bool findSelectedElements( ForestBrushElement *obj ) +{ + if ( obj->isSelectedRecursive() ) + return true; + + return false; +} + +void ForestBrushTool::_collectElements() +{ + mElements.clear(); + + // Get the selected objects from the tree view. + // These can be a combination of ForestBrush(s) and ForestBrushElement(s). + + GuiTreeViewCtrl *brushTree; + if ( !Sim::findObject( "ForestEditBrushTree", brushTree ) ) + return; + + const char* objectIdList = Con::executef( brushTree, "getSelectedObjectList" ); + + // Collect those objects in a vector and mark them as selected. + + Vector objectList; + SimObject *simobj; + + S32 wordCount = StringUnit::getUnitCount( objectIdList, " " ); + + for ( S32 i = 0; i < wordCount; i++ ) + { + const char* word = StringUnit::getUnit( objectIdList, i, " " ); + + if ( Sim::findObject( word, simobj ) ) + { + objectList.push_back( simobj ); + simobj->setSelected(true); + } + } + + // Find all ForestBrushElements that are directly or indirectly selected. + + SimGroup *brushGroup = ForestBrush::getGroup(); + brushGroup->findObjectByCallback( findSelectedElements, mElements ); + + // We just needed to flag these objects as selected for the benefit of our + // findSelectedElements callback, we can now mark them un-selected again. + + for ( S32 i = 0; i < objectList.size(); i++ ) + objectList[i]->setSelected(false); + + // If we are in Paint or EraseSelected mode we filter out elements with + // a non-positive probability. + + if ( mMode == Paint || mMode == EraseSelected ) + { + for ( S32 i = 0; i < mElements.size(); i++ ) + { + if ( mElements[i]->mProbability <= 0.0f ) + { + mElements.erase_fast(i); + i--; + } + } + } + + // Filter out elements with NULL datablocks and collect all unique datablocks + // in a vector. + + mDatablocks.clear(); + for ( S32 i = 0; i < mElements.size(); i++ ) + { + if ( mElements[i]->mData == NULL ) + { + mElements.erase_fast(i); + i--; + continue; + } + + mDatablocks.push_back_unique( mElements[i]->mData ); + } +} + +bool ForestBrushTool::getGroundAt( const Point3F &worldPt, float *zValueOut, VectorF *normalOut ) +{ + const U32 mask = TerrainObjectType | StaticShapeObjectType; + + Point3F start( worldPt.x, worldPt.y, worldPt.z + mSize ); + Point3F end( worldPt.x, worldPt.y, worldPt.z - mSize ); + + if ( mForest ) + mForest->disableCollision(); + + // Do a cast ray at this point from the top to + // the bottom of our brush radius. + + RayInfo rinfo; + bool hit = gServerContainer.castRay( start, end, mask, &rinfo ); + + if ( mForest ) + mForest->enableCollision(); + + if ( !hit ) + return false; + + if (zValueOut) + *zValueOut = rinfo.point.z; + + if (normalOut) + *normalOut = rinfo.normal; + + return true; +} + +ConsoleMethod( ForestBrushTool, collectElements, void, 2, 2, "" ) +{ + object->collectElements(); +} \ No newline at end of file diff --git a/Engine/source/forest/editor/forestBrushTool.h b/Engine/source/forest/editor/forestBrushTool.h new file mode 100644 index 000000000..510146ff4 --- /dev/null +++ b/Engine/source/forest/editor/forestBrushTool.h @@ -0,0 +1,152 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FOREST_EDITOR_BRUSH_H_ +#define _FOREST_EDITOR_BRUSH_H_ + +#ifndef _FOREST_EDITOR_TOOL_H_ +#include "forest/editor/forestTool.h" +#endif +#ifndef _FORESTITEM_H_ +#include "forest/forestItem.h" +#endif +#ifndef _FOREST_EDITOR_BRUSHELEMENT_H_ +#include "forest/editor/forestBrushElement.h" +#endif +#ifndef _COLOR_H_ +#include "core/color.h" +#endif + + + +class Forest; +class ForestUndoAction; + +class ForestBrushTool : public ForestTool +{ + typedef ForestTool Parent; + + friend class ForestBrushToolEvent; + +public: + + enum BrushMode + { + Paint = 0, + Erase, + EraseSelected + }; + + ForestBrushTool(); + virtual ~ForestBrushTool(); + + // SimObject + DECLARE_CONOBJECT( ForestBrushTool ); + static void initPersistFields(); + virtual bool onAdd(); + virtual void onRemove(); + + // ForestTool + virtual void on3DMouseDown( const Gui3DMouseEvent &evt ); + virtual void on3DMouseUp( const Gui3DMouseEvent &evt ); + virtual void on3DMouseMove( const Gui3DMouseEvent &evt ); + virtual void on3DMouseDragged( const Gui3DMouseEvent &evt ); + virtual bool onMouseWheel(const GuiEvent &evt ); + virtual void onRender3D(); + virtual void onRender2D(); + virtual void onActivated( const Gui3DMouseEvent &lastEvent ); + virtual void onDeactivated(); + virtual bool updateGuiInfo(); + + // ForestBrushTool + void setSize( F32 val ); + void setPressure( F32 val ); + void setHardness( F32 val ); + void collectElements() { _collectElements(); } + bool getGroundAt( const Point3F &worldPt, float *zValueOut, VectorF *normalOut ); + +protected: + + void _onStroke(); + virtual void _action( const Point3F &point ); + virtual void _paint( const Point3F &point ); + virtual void _erase( const Point3F &point ); + + bool _updateBrushPoint( const Gui3DMouseEvent &event_ ); + virtual void _collectElements(); + + static bool protectedSetSize( void *object, const char *index, const char *data ); + static bool protectedSetPressure( void *object, const char *index, const char *data ); + static bool protectedSetHardness( void *object, const char *index, const char *data ); + +protected: + + MRandom mRandom; + + /// We treat this as a radius. + F32 mSize; + F32 mPressure; + F32 mHardness; + + U32 mMode; + + ColorI mColor; + + Vector mElements; + Vector mDatablocks; + + /// + bool mBrushDown; + + /// + bool mDrawBrush; + + /// + U32 mStrokeEvent; + + /// + Point3F mLastBrushPoint; + Point3F mLastBrushNormal; + + /// The creation action we're actively filling. + ForestUndoAction *mCurrAction; +}; + +typedef ForestBrushTool::BrushMode ForestBrushMode; +DefineEnumType( ForestBrushMode ); + + +class ForestBrushToolEvent : public SimEvent +{ +public: + + void process( SimObject *object ) + { + ((ForestBrushTool*)object)->_onStroke(); + } +}; + + +#endif // _FOREST_EDITOR_BRUSH_H_ + + + diff --git a/Engine/source/forest/editor/forestEditorCtrl.cpp b/Engine/source/forest/editor/forestEditorCtrl.cpp new file mode 100644 index 000000000..923e1ee56 --- /dev/null +++ b/Engine/source/forest/editor/forestEditorCtrl.cpp @@ -0,0 +1,402 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "forest/editor/forestEditorCtrl.h" + +#include "forest/editor/forestBrushTool.h" +#include "console/consoleTypes.h" +#include "gui/core/guiCanvas.h" +#include "windowManager/platformCursorController.h" +#include "forest/editor/forestUndo.h" +#include "gui/worldEditor/undoActions.h" + + +IMPLEMENT_CONOBJECT( ForestEditorCtrl ); + +ConsoleDocClass( ForestEditorCtrl, + "@brief The actual Forest Editor control\n\n" + "Editor use only, should not be modified.\n\n" + "@internal" +); + +ForestEditorCtrl::ForestEditorCtrl() +{ + dMemset( &mLastEvent, 0, sizeof(Gui3DMouseEvent) ); +} + +ForestEditorCtrl::~ForestEditorCtrl() +{ +} + +bool ForestEditorCtrl::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + return true; +} + +void ForestEditorCtrl::initPersistFields() +{ + Parent::initPersistFields(); +} + +bool ForestEditorCtrl::onWake() +{ + if ( !Parent::onWake() ) + return false; + + // Push our default cursor on here once. + GuiCanvas *root = getRoot(); + if ( root ) + { + S32 currCursor = PlatformCursorController::curArrow; + + PlatformWindow *window = root->getPlatformWindow(); + PlatformCursorController *controller = window->getCursorController(); + controller->pushCursor( currCursor ); + } + + return true; +} + +void ForestEditorCtrl::onSleep() +{ + // Pop our default cursor off. + GuiCanvas *root = getRoot(); + if ( root ) + { + PlatformWindow *window = root->getPlatformWindow(); + PlatformCursorController *controller = window->getCursorController(); + controller->popCursor(); + } + + Parent::onSleep(); +} + +bool ForestEditorCtrl::updateActiveForest( bool createNew ) +{ + mForest = dynamic_cast( Sim::findObject( "theForest" ) ); + Con::executef( this, "onActiveForestUpdated", mForest ? mForest->getIdString() : "", createNew ? "1" : "0" ); + + if ( mTool ) + mTool->setActiveForest( mForest ); + + return mForest; +} + +void ForestEditorCtrl::setActiveTool( ForestTool *tool ) +{ + if ( mTool ) + { + mTool->onDeactivated(); + } + + mTool = tool; + + if ( mTool ) + { + mTool->setActiveForest( mForest ); + mTool->setParentEditor( this ); + mTool->onActivated( mLastEvent ); + } +} + +void ForestEditorCtrl::onMouseUp( const GuiEvent &event_ ) +{ + Parent::onMouseUp( event_ ); +} + +void ForestEditorCtrl::get3DCursor( GuiCursor *&cursor, + bool &visible, + const Gui3DMouseEvent &event_ ) +{ + cursor = NULL; + visible = false; + + GuiCanvas *root = getRoot(); + if ( !root ) + return; + + S32 currCursor = PlatformCursorController::curArrow; + + if ( root->mCursorChanged == currCursor ) + return; + + PlatformWindow *window = root->getPlatformWindow(); + PlatformCursorController *controller = window->getCursorController(); + + // We've already changed the cursor, + // so set it back before we change it again. + if( root->mCursorChanged != -1) + controller->popCursor(); + + // Now change the cursor shape + controller->pushCursor(currCursor); + root->mCursorChanged = currCursor; +} + +void ForestEditorCtrl::on3DMouseDown( const Gui3DMouseEvent &evt ) +{ + if ( !mForest && !updateActiveForest( true ) ) + return; + + if ( mTool ) + mTool->on3DMouseDown( evt ); + + mouseLock(); +} + +void ForestEditorCtrl::on3DMouseUp( const Gui3DMouseEvent &evt ) +{ + if ( !isMouseLocked() ) + return; + + if ( mTool ) + mTool->on3DMouseUp( evt ); + + mouseUnlock(); +} + +void ForestEditorCtrl::on3DMouseMove( const Gui3DMouseEvent &evt ) +{ + if ( !mForest ) + updateActiveForest( false ); + + if ( mTool ) + mTool->on3DMouseMove( evt ); +} + +void ForestEditorCtrl::on3DMouseDragged( const Gui3DMouseEvent &evt ) +{ + if ( mTool ) + mTool->on3DMouseDragged( evt ); +} + +void ForestEditorCtrl::on3DMouseEnter( const Gui3DMouseEvent &evt ) +{ + if ( mTool ) + mTool->on3DMouseEnter( evt ); +} + +void ForestEditorCtrl::on3DMouseLeave( const Gui3DMouseEvent &evt ) +{ + if ( mTool ) + mTool->on3DMouseLeave( evt ); +} + +void ForestEditorCtrl::on3DRightMouseDown( const Gui3DMouseEvent &evt ) +{ +} + +void ForestEditorCtrl::on3DRightMouseUp( const Gui3DMouseEvent &evt ) +{ +} + +bool ForestEditorCtrl::onMouseWheelUp(const GuiEvent &event_) +{ + if ( mTool ) + return mTool->onMouseWheel( event_ ); + + return Parent::onMouseWheelUp( event_ ); +} + +bool ForestEditorCtrl::onMouseWheelDown(const GuiEvent &event_) +{ + if ( mTool ) + return mTool->onMouseWheel( event_ ); + + return Parent::onMouseWheelDown( event_ ); +} + +void ForestEditorCtrl::updateGuiInfo() +{ + // Note: This is intended to be used for updating + // GuiControls with info before they are rendered. + + SimObject *statusbar; + Sim::findObject( "EditorGuiStatusBar", statusbar ); + + SimObject *selectionBar; + Sim::findObject( "EWorldEditorStatusBarSelection", selectionBar ); + + String text; + + if ( !mForest ) + { + if ( statusbar ) + Con::executef( statusbar, "setInfo", "Forest Editor. You have no Forest in your level; click anywhere in the scene to create one." ); + if ( selectionBar ) + Con::executef( selectionBar, "setInfo", "" ); + return; + } + + if ( mTool ) + { + if ( mTool->updateGuiInfo() ) + return; + } + + // Tool did not handle the update so we will. + + if ( statusbar ) + Con::executef( statusbar, "setInfo", "Forest Editor." ); + if ( selectionBar ) + Con::executef( selectionBar, "setInfo", "" ); +} + +void ForestEditorCtrl::renderScene( const RectI &updateRect ) +{ + if ( mTool ) + mTool->onRender3D(); +} + +void ForestEditorCtrl::updateGizmo() +{ + if ( mTool ) + mTool->updateGizmo(); +} + +void ForestEditorCtrl::renderGui( Point2I offset, const RectI &updateRect ) +{ + if ( mTool ) + mTool->onRender2D(); +} + +static ForestItemData* sKey = NULL; + +bool findMeshReferences( SimObject *obj ) +{ + ForestBrushElement *element = dynamic_cast(obj); + + if ( element && element->mData == sKey ) + return true; + + return false; +} + +void ForestEditorCtrl::onUndoAction() +{ + if ( mTool ) + mTool->onUndoAction(); + + updateCollision(); +} + +void ForestEditorCtrl::deleteMeshSafe( ForestItemData *mesh ) +{ + UndoManager *undoMan = NULL; + if ( !Sim::findObject( "EUndoManager", undoMan ) ) + { + Con::errorf( "ForestEditorCtrl::deleteMeshSafe() - EUndoManager not found." ); + return; + } + + // CompoundUndoAction which will delete the ForestItemData, ForestItem(s), and ForestBrushElement(s). + CompoundUndoAction *compoundAction = new CompoundUndoAction( "Delete Forest Mesh" ); + + // Find ForestItem(s) referencing this datablock and add their deletion + // to the undo action. + if ( mForest ) + { + Vector foundItems; + mForest->getData()->getItems( mesh, &foundItems ); + + ForestDeleteUndoAction *itemAction = new ForestDeleteUndoAction( mForest->getData(), this ); + itemAction->removeItem( foundItems ); + compoundAction->addAction( itemAction ); + } + + // Find ForestBrushElement(s) referencing this datablock. + SimGroup *brushGroup = ForestBrush::getGroup(); + sKey = mesh; + Vector foundElements; + brushGroup->findObjectByCallback( &findMeshReferences, foundElements ); + + // Add UndoAction to delete the ForestBrushElement(s) and the ForestItemData. + MEDeleteUndoAction *elementAction = new MEDeleteUndoAction(); + elementAction->deleteObject( foundElements ); + elementAction->deleteObject( mesh ); + + // Add compound action to the UndoManager. Done. + undoMan->addAction( compoundAction ); + + updateCollision(); +} + +void ForestEditorCtrl::updateCollision() +{ + if ( mForest ) + { + mForest->updateCollision(); + + if ( mForest->getClientObject() ) + ((Forest*)(mForest->getClientObject()))->updateCollision(); + } +} + +void FindDirtyForests( SceneObject *obj, void *key ) +{ + Forest *forest = dynamic_cast(obj); + if ( forest && forest->getData()->isDirty() ) + *((bool*)(key)) = true; +} + +bool ForestEditorCtrl::isDirty() +{ + bool foundDirty = false; + gServerContainer.findObjects( EnvironmentObjectType, FindDirtyForests, (void*)&foundDirty ); + + return foundDirty; +} + +ConsoleMethod( ForestEditorCtrl, updateActiveForest, void, 2, 2, "()" ) +{ + object->updateActiveForest( true ); +} + +ConsoleMethod( ForestEditorCtrl, setActiveTool, void, 3, 3, "( ForestTool tool )" ) +{ + ForestTool *tool = dynamic_cast( Sim::findObject( argv[2] ) ); + object->setActiveTool( tool ); +} + +ConsoleMethod( ForestEditorCtrl, getActiveTool, S32, 2, 2, "()" ) +{ + ForestTool *tool = object->getActiveTool(); + return tool ? tool->getId() : 0; +} + +ConsoleMethod( ForestEditorCtrl, deleteMeshSafe, void, 3, 3, "( ForestItemData obj )" ) +{ + ForestItemData *db; + if ( !Sim::findObject( argv[2], db ) ) + return; + + object->deleteMeshSafe( db ); +} + +ConsoleMethod( ForestEditorCtrl, isDirty, bool, 2, 2, "" ) +{ + return object->isDirty(); +} \ No newline at end of file diff --git a/Engine/source/forest/editor/forestEditorCtrl.h b/Engine/source/forest/editor/forestEditorCtrl.h new file mode 100644 index 000000000..d210040e9 --- /dev/null +++ b/Engine/source/forest/editor/forestEditorCtrl.h @@ -0,0 +1,107 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FOREST_EDITOR_FORESTEDITORCTRL_H_ +#define _FOREST_EDITOR_FORESTEDITORCTRL_H_ + +#ifndef _EDITTSCTRL_H_ +#include "gui/worldEditor/editTSCtrl.h" +#endif + +#ifndef _H_FOREST_ +#include "forest/forest.h" +#endif + +#ifndef _FOREST_EDITOR_TOOL_H_ +#include "forest/editor/forestTool.h" +#endif + + +class ForestEditorCtrl : public EditTSCtrl +{ + typedef EditTSCtrl Parent; + + friend class ForestPaintEvent; + + protected: + + /// The server Forest we're editing. + SimObjectPtr mForest; + + /// The active tool in used by the editor. + SimObjectPtr mTool; + + public: + + ForestEditorCtrl(); + virtual ~ForestEditorCtrl(); + + DECLARE_CONOBJECT( ForestEditorCtrl ); + + // SimObject + bool onAdd(); + static void initPersistFields(); + + // GuiControl + virtual bool onWake(); + virtual void onSleep(); + virtual void onMouseUp( const GuiEvent &event_ ); + + // EditTSCtrl + void get3DCursor( GuiCursor *&cursor, bool &visible, const Gui3DMouseEvent &event_ ); + void on3DMouseDown( const Gui3DMouseEvent &event_ ); + void on3DMouseUp( const Gui3DMouseEvent &event_ ); + void on3DMouseMove( const Gui3DMouseEvent &event_ ); + void on3DMouseDragged( const Gui3DMouseEvent &event_ ); + void on3DMouseEnter( const Gui3DMouseEvent &event_ ); + void on3DMouseLeave( const Gui3DMouseEvent &event_ ); + void on3DRightMouseDown( const Gui3DMouseEvent &event_ ); + void on3DRightMouseUp( const Gui3DMouseEvent &event_ ); + bool onMouseWheelUp(const GuiEvent &event_); + bool onMouseWheelDown(const GuiEvent &event_); + void updateGuiInfo(); + void updateGizmo(); + void renderScene( const RectI &updateRect ); + void renderGui( Point2I offset, const RectI &updateRect ); + + /// Causes the editor to reselect the active forest. + bool updateActiveForest( bool createNew ); + + /// Returns the active Forest. + Forest *getActiveForest() const { return mForest; } + + void setActiveTool( ForestTool *tool ); + ForestTool* getActiveTool() { return mTool; } + + void onUndoAction(); + + void deleteMeshSafe( ForestItemData *itemData ); + + void updateCollision(); + + bool isDirty(); +}; + +#endif // _FOREST_EDITOR_FORESTEDITORCTRL_H_ + + + diff --git a/Engine/source/forest/editor/forestSelectionTool.cpp b/Engine/source/forest/editor/forestSelectionTool.cpp new file mode 100644 index 000000000..c2032343c --- /dev/null +++ b/Engine/source/forest/editor/forestSelectionTool.cpp @@ -0,0 +1,590 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "forest/editor/forestSelectionTool.h" + +#include "forest/forest.h" +#include "forest/editor/forestUndo.h" +#include "forest/editor/forestEditorCtrl.h" + +#include "gui/worldEditor/editTSCtrl.h" +#include "gui/worldEditor/gizmo.h" +#include "console/consoleTypes.h" +#include "core/util/tVector.h" +#include "core/util/safeDelete.h" +#include "gfx/gfxDrawUtil.h" +#include "gui/worldEditor/worldEditor.h" +#include "math/mMatrix.h" + +template <> +MatrixF Selection::getOrientation() +{ + if ( size() == 1 ) + return first().getTransform(); + + return MatrixF::Identity; +} + +template <> +Point3F Selection::getOrigin() +{ + Point3F centroid( Point3F::Zero ); + + if ( empty() ) + return centroid; + + Selection::iterator itr = begin(); + + for ( ; itr != end(); itr++ ) + { + const MatrixF &mat = itr->getTransform(); + Point3F wPos; + mat.getColumn( 3, &wPos ); + + centroid += wPos; + } + + centroid /= (F32)size(); + + return centroid; +} + +template <> +Point3F Selection::getScale() +{ + if ( size() == 1 ) + return Point3F( first().getScale() ); + + return Point3F::One; +} + +void ForestItemSelection::offsetObject( ForestItem &object, const Point3F &delta ) +{ + if ( !mData ) + return; + + MatrixF newMat( object.getTransform() ); + newMat.displace( delta ); + + object = mData->updateItem( object.getKey(), object.getPosition(), object.getData(), newMat, object.getScale() ); +} + +void ForestItemSelection::rotateObject( ForestItem &object, const EulerF &delta, const Point3F &origin ) +{ + if ( !mData ) + return; + + MatrixF mat = object.getTransform(); + + Point3F pos; + mat.getColumn( 3, &pos ); + + MatrixF wMat( mat ); + wMat.inverse(); + + // get offset in obj space + Point3F offset = pos - origin; + + if ( size() == 1 ) + { + wMat.mulV(offset); + + MatrixF transform(EulerF::Zero, -offset); + transform.mul(MatrixF(delta)); + transform.mul(MatrixF(EulerF::Zero, offset)); + mat.mul(transform); + } + else + { + MatrixF transform( delta ); + Point3F wOffset; + transform.mulV( offset, &wOffset ); + + wMat.mulV( offset ); + + transform.set( EulerF::Zero, -offset ); + + mat.setColumn( 3, Point3F::Zero ); + wMat.setColumn( 3, Point3F::Zero ); + + transform.mul( wMat ); + transform.mul( MatrixF(delta) ); + transform.mul( mat ); + mat.mul( transform ); + + mat.normalize(); + mat.setColumn( 3, wOffset + origin ); + } + + object = mData->updateItem( object.getKey(), object.getPosition(), object.getData(), mat, object.getScale() ); +} + +void ForestItemSelection::scaleObject( ForestItem &object, const Point3F &delta ) +{ + if ( !mData ) + return; + + object = mData->updateItem( object.getKey(), object.getPosition(), object.getData(), object.getTransform(), delta.z ); +} + + +IMPLEMENT_CONOBJECT( ForestSelectionTool ); + +ConsoleDocClass( ForestSelectionTool, + "@brief Specialized selection tool for picking individual trees in a forest.\n\n" + "Editor use only.\n\n" + "@internal" +); + +ForestSelectionTool::ForestSelectionTool() + : Parent(), + mCurrAction( NULL ), + mGizmo( NULL ), + mGizmoProfile( NULL ) +{ + mBounds = Box3F::Invalid; + + mDragRectColor.set(255,255,0); + mMouseDown = false; + mDragSelect = false; +} + +ForestSelectionTool::~ForestSelectionTool() +{ + SAFE_DELETE( mCurrAction ); +} + +void ForestSelectionTool::setParentEditor( ForestEditorCtrl *editor ) +{ + mEditor = editor; + mGizmo = editor->getGizmo(); + mGizmoProfile = mGizmo->getProfile(); +} + +void ForestSelectionTool::_selectItem( const ForestItem &item ) +{ + // Make sure its not already selected. + for ( U32 i=0; i < mSelection.size(); i++ ) + { + if ( mSelection[i].getKey() == item.getKey() ) + return; + } + + mSelection.push_back( item ); + mBounds.intersect( item.getWorldBox() ); +} + +void ForestSelectionTool::deleteSelection() +{ + ForestDeleteUndoAction *action = new ForestDeleteUndoAction( mForest->getData(), mEditor ); + + for ( U32 i=0; i < mSelection.size(); i++ ) + { + const ForestItem &item = mSelection[i]; + action->removeItem( item ); + } + + clearSelection(); + _submitUndo( action ); +} + +void ForestSelectionTool::clearSelection() +{ + mSelection.clear(); + mBounds = Box3F::Invalid; +} + +void ForestSelectionTool::cutSelection() +{ + +} + +void ForestSelectionTool::copySelection() +{ + +} + +void ForestSelectionTool::pasteSelection() +{ + +} + +void ForestSelectionTool::setActiveForest( Forest *forest ) +{ + mForest = forest; + + if ( forest ) + mSelection.setForestData( forest->getData() ); + else + mSelection.setForestData( NULL ); +} + +void ForestSelectionTool::on3DMouseDown( const Gui3DMouseEvent &evt ) +{ + mMouseDown = true; + mMouseDragged = false; + + mUsingGizmo = !mSelection.empty() && mGizmo->getSelection() != Gizmo::None; + + if ( mUsingGizmo ) + { + mGizmo->on3DMouseDown( evt ); + return; + } + + mDragSelection.clear(); + mDragRect.set( Point2I(evt.mousePoint), Point2I(0,0) ); + mDragStart = evt.mousePoint; + + const bool multiSel = evt.modifier & SI_CTRL; + + if ( !multiSel ) + clearSelection(); + + if ( mHoverItem.isValid() ) + _selectItem( mHoverItem ); + + // This should never happen... it should have been + // submitted and nulled in on3DMouseUp()! + // + // Yeah, unless you had a breakpoint there and on3DMouseUp never fired, + // in which case this is really annoying. + // + //AssertFatal( !mCurrAction, "ForestSelectionTool::on3DMouseDown() - Dangling undo action!" ); +} + +void ForestSelectionTool::on3DMouseMove( const Gui3DMouseEvent &evt ) +{ + // Invalidate the hover item first... we're gonna find a new one. + mHoverItem.makeInvalid(); + + if ( !mForest ) + return; + + if ( !mSelection.empty() ) + mGizmo->on3DMouseMove( evt ); + + RayInfo ri; + ri.userData = new ForestItem; + Point3F startPnt = evt.pos; + Point3F endPnt = evt.pos + evt.vec * 1000.0f; + + if ( mForest->castRayRendered( startPnt, endPnt, &ri ) ) + mHoverItem = (*(ForestItem*)ri.userData); + + delete static_cast(ri.userData); +} + +void ForestSelectionTool::on3DMouseDragged( const Gui3DMouseEvent &evt ) +{ + mHoverItem.makeInvalid(); + + if ( mUsingGizmo ) + { + mGizmo->on3DMouseDragged( evt ); + + const Point3F &deltaRot = mGizmo->getDeltaRot(); + const Point3F &deltaPos = mGizmo->getOffset(); + const Point3F &deltaScale = mGizmo->getDeltaScale(); + + if ( deltaRot.isZero() && deltaPos.isZero() && deltaScale.isZero() ) + return; + + // Store the current item states! + if ( !mCurrAction ) + { + mCurrAction = new ForestUpdateAction( mForest->getData(), mEditor ); + for ( U32 i=0; i < mSelection.size(); i++ ) + { + const ForestItem &item = mSelection[i]; + mCurrAction->saveItem( item ); + } + } + + switch ( mGizmo->getMode() ) + { + case MoveMode: + mSelection.offset( deltaPos ); break; + case RotateMode: + mSelection.rotate( deltaRot ); break; + case ScaleMode: + mSelection.scale( mGizmo->getScale() ); break; + default: ; + } + + return; + } + + mDragSelect = true; + mHoverItem.makeInvalid(); + + // Doing a drag selection. + + if ( mDragSelect ) + { + // build the drag selection on the renderScene method - make sure no neg extent! + mDragRect.point.x = (evt.mousePoint.x < mDragStart.x) ? evt.mousePoint.x : mDragStart.x; + mDragRect.extent.x = (evt.mousePoint.x > mDragStart.x) ? evt.mousePoint.x - mDragStart.x : mDragStart.x - evt.mousePoint.x; + mDragRect.point.y = (evt.mousePoint.y < mDragStart.y) ? evt.mousePoint.y : mDragStart.y; + mDragRect.extent.y = (evt.mousePoint.y > mDragStart.y) ? evt.mousePoint.y - mDragStart.y : mDragStart.y - evt.mousePoint.y; + return; + } +} + +void ForestSelectionTool::on3DMouseUp( const Gui3DMouseEvent &evt ) +{ + mGizmo->on3DMouseUp( evt ); + + mMouseDown = false; + + // If we have an undo action then we must have + // moved, rotated, or scaled something. + if ( mCurrAction ) + { + _submitUndo( mCurrAction ); + mCurrAction = NULL; + return; + } + + // If we were performing a drag select, finalize it now. + if ( mDragSelect ) + { + mDragSelect = false; + + clearSelection(); + + for ( S32 i = 0; i < mDragSelection.size(); i++ ) + _selectItem( mDragSelection[i] ); + + mDragSelection.clear(); + + return; + } +} + +void ForestSelectionTool::onRender3D() +{ + GFXDrawUtil *drawUtil = GFX->getDrawUtil(); + ColorI color( 255, 255, 255, 255 ); + MatrixF treeMat; + + GFXStateBlockDesc desc; + desc.setBlend( true ); + desc.setZReadWrite( true, false ); + + if ( mHoverItem.isValid() ) + { + treeMat = mHoverItem.getTransform(); + drawUtil->drawObjectBox( desc, mHoverItem.getSize(), mHoverItem.getWorldBox().getCenter(), treeMat, color ); + } + + if ( !mSelection.empty() ) + { + for ( U32 i = 0; i < mSelection.size(); i++ ) + { + const ForestItem &item = mSelection[i]; + treeMat = item.getTransform(); + drawUtil->drawObjectBox( desc, item.getSize(), item.getWorldBox().getCenter(), treeMat, color ); + } + + mGizmo->set( mSelection.getOrientation(), mSelection.getOrigin(), mSelection.getScale() ); + + mGizmo->renderGizmo( mEditor->getLastCameraQuery().cameraMatrix, mEditor->getLastCameraQuery().fov ); + } +} + +static Frustum gDragFrustum; + +void ForestSelectionTool::onRender2D() +{ + // Draw drag selection rect. + if ( mDragSelect && mDragRect.extent.x > 1 && mDragRect.extent.y > 1 ) + GFX->getDrawUtil()->drawRect( mDragRect, mDragRectColor ); + + // update what is in the selection + if ( mDragSelect ) + mDragSelection.clear(); + + // Determine selected objects based on the drag box touching + // a mesh if a drag operation has begun. + if ( mDragSelect && mDragRect.extent.x > 1 && mDragRect.extent.y > 1 ) + { + // Build the drag frustum based on the rect + F32 wwidth; + F32 wheight; + F32 aspectRatio = F32(mEditor->getWidth()) / F32(mEditor->getHeight()); + + const CameraQuery &lastCameraQuery = mEditor->getLastCameraQuery(); + if(!lastCameraQuery.ortho) + { + wheight = lastCameraQuery.nearPlane * mTan(lastCameraQuery.fov / 2); + wwidth = aspectRatio * wheight; + } + else + { + wheight = lastCameraQuery.fov; + wwidth = aspectRatio * wheight; + } + + F32 hscale = wwidth * 2 / F32(mEditor->getWidth()); + F32 vscale = wheight * 2 / F32(mEditor->getHeight()); + + F32 left = (mDragRect.point.x - mEditor->getPosition().x) * hscale - wwidth; + F32 right = (mDragRect.point.x - mEditor->getPosition().x + mDragRect.extent.x) * hscale - wwidth; + F32 top = wheight - vscale * (mDragRect.point.y - mEditor->getPosition().y); + F32 bottom = wheight - vscale * (mDragRect.point.y - mEditor->getPosition().y + mDragRect.extent.y); + gDragFrustum.set(lastCameraQuery.ortho, left, right, top, bottom, lastCameraQuery.nearPlane, lastCameraQuery.farPlane, lastCameraQuery.cameraMatrix ); + + mForest->getData()->getItems( gDragFrustum, &mDragSelection ); + } +} + +bool ForestSelectionTool::updateGuiInfo() +{ + SimObject *statusbar; + if ( !Sim::findObject( "EditorGuiStatusBar", statusbar ) ) + return false; + + String text( "Forest Editor." ); + GizmoMode mode = mGizmoProfile->mode; + + if ( mMouseDown && mGizmo->getSelection() != Gizmo::None ) + { + Point3F delta; + String qualifier; + + if ( mode == RotateMode ) + delta = mGizmo->getDeltaTotalRot(); + else if ( mode == MoveMode ) + delta = mGizmo->getTotalOffset(); + else if ( mode == ScaleMode ) + delta = mGizmo->getDeltaTotalScale(); + + if ( mGizmo->getAlignment() == Object && mode != ScaleMode ) + { + mSelection.getOrientation().mulV( delta ); + } + + if ( mIsZero( delta.x, 0.0001f ) ) + delta.x = 0.0f; + if ( mIsZero( delta.y, 0.0001f ) ) + delta.y = 0.0f; + if ( mIsZero( delta.z, 0.0001f ) ) + delta.z = 0.0f; + + if ( mode == RotateMode ) + { + delta.x = mRadToDeg( delta.x ); + delta.y = mRadToDeg( delta.y ); + delta.z = mRadToDeg( delta.z ); + text = String::ToString( "Delta angle ( x: %4.2f, y: %4.2f, z: %4.2f ).", delta.x, delta.y, delta.z ); + } + else if ( mode == MoveMode ) + text = String::ToString( "Delta position ( x: %4.2f, y: %4.2f, z: %4.2f ).", delta.x, delta.y, delta.z ); + else if ( mode == ScaleMode ) + text = String::ToString( "Delta scale ( x: %4.2f, y: %4.2f, z: %4.2f ).", delta.x, delta.y, delta.z ); + } + else + { + if ( mode == MoveMode ) + text = "Move selection. SHIFT while dragging duplicates objects."; + else if ( mode == RotateMode ) + text = "Rotate selection."; + else if ( mode == ScaleMode ) + text = "Scale selection."; + } + + Con::executef( statusbar, "setInfo", text.c_str() ); + + Con::executef( statusbar, "setSelectionObjectsByCount", Con::getIntArg( mSelection.size() ) ); + + return true; +} + +void ForestSelectionTool::updateGizmo() +{ + mGizmoProfile->restoreDefaultState(); + + const GizmoMode &mode = mGizmoProfile->mode; + + if ( mode == ScaleMode ) + { + mGizmoProfile->flags &= ~GizmoProfile::PlanarHandlesOn; + mGizmoProfile->allAxesScaleUniform = true; + } +} + +void ForestSelectionTool::onUndoAction() +{ + ForestData *data = mForest->getData(); + + // Remove items from our selection that no longer exist. + for ( S32 i = 0; i < mSelection.size(); i++ ) + { + const ForestItem &item = data->findItem( mSelection[i].getKey(), mSelection[i].getPosition() ); + + if ( item == ForestItem::Invalid ) + { + mSelection.erase_fast( i ); + i--; + } + else + mSelection[i] = item; + } + + // Recalculate our selection bounds. + mBounds = Box3F::Invalid; + for ( S32 i = 0; i < mSelection.size(); i++ ) + mBounds.intersect( mSelection[i].getWorldBox() ); +} + +ConsoleMethod( ForestSelectionTool, getSelectionCount, S32, 2, 2, "" ) +{ + return object->getSelectionCount(); +} + +ConsoleMethod( ForestSelectionTool, deleteSelection, void, 2, 2, "" ) +{ + object->deleteSelection(); +} + +ConsoleMethod( ForestSelectionTool, clearSelection, void, 2, 2, "" ) +{ + object->clearSelection(); +} + +ConsoleMethod( ForestSelectionTool, cutSelection, void, 2, 2, "" ) +{ + object->cutSelection(); +} + +ConsoleMethod( ForestSelectionTool, copySelection, void, 2, 2, "" ) +{ + object->copySelection(); +} + +ConsoleMethod( ForestSelectionTool, pasteSelection, void, 2, 2, "" ) +{ + object->pasteSelection(); +} + diff --git a/Engine/source/forest/editor/forestSelectionTool.h b/Engine/source/forest/editor/forestSelectionTool.h new file mode 100644 index 000000000..c7185e89d --- /dev/null +++ b/Engine/source/forest/editor/forestSelectionTool.h @@ -0,0 +1,121 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FOREST_EDITOR_SELECTIONTOOL_H_ +#define _FOREST_EDITOR_SELECTIONTOOL_H_ + +#ifndef _FOREST_EDITOR_TOOL_H_ +#include "forest/editor/forestTool.h" +#endif +#ifndef _FORESTITEM_H_ +#include "forest/forestItem.h" +#endif +#ifndef _TSELECTION_H_ +#include "gui/worldEditor/tSelection.h" +#endif + + +class Forest; +class Gizmo; +class GizmoProfile; +class ForestUpdateAction; + +class ForestItemSelection : public Selection +{ +public: + + void setForestData( ForestData* data ) { mData = data; } + +protected: + + void offsetObject( ForestItem &object, const Point3F &delta ); + void rotateObject( ForestItem &object, const EulerF &delta, const Point3F &origin ); + void scaleObject( ForestItem &object, const Point3F &delta ); + +protected: + + ForestData *mData; +}; + + +class ForestSelectionTool : public ForestTool +{ + typedef ForestTool Parent; + + protected: + + Gizmo *mGizmo; + GizmoProfile *mGizmoProfile; + + ForestItem mHoverItem; + + ForestItemSelection mSelection; + + ForestItemSelection mDragSelection; + bool mDragSelect; + RectI mDragRect; + Point2I mDragStart; + bool mMouseDown; + bool mMouseDragged; + ColorI mDragRectColor; + bool mUsingGizmo; + + Box3F mBounds; + + ForestUpdateAction *mCurrAction; + + void _selectItem( const ForestItem &item ); + + public: + + ForestSelectionTool(); + virtual ~ForestSelectionTool(); + + DECLARE_CONOBJECT( ForestSelectionTool ); + + // ForestTool + virtual void setParentEditor( ForestEditorCtrl *editor ); + virtual void setActiveForest( Forest *forest ); + virtual void on3DMouseDown( const Gui3DMouseEvent &evt ); + virtual void on3DMouseUp( const Gui3DMouseEvent &evt ); + virtual void on3DMouseMove( const Gui3DMouseEvent &evt ); + virtual void on3DMouseDragged( const Gui3DMouseEvent &evt ); + virtual void onRender3D(); + virtual void onRender2D(); + virtual bool updateGuiInfo(); + virtual void updateGizmo(); + virtual void onUndoAction(); + + S32 getSelectionCount() const { return mSelection.size(); } + + void deleteSelection(); + void clearSelection(); + void cutSelection(); + void copySelection(); + void pasteSelection(); +}; + + +#endif // _FOREST_EDITOR_SELECTIONTOOL_H_ + + + diff --git a/Engine/source/forest/editor/forestTool.cpp b/Engine/source/forest/editor/forestTool.cpp new file mode 100644 index 000000000..062fadace --- /dev/null +++ b/Engine/source/forest/editor/forestTool.cpp @@ -0,0 +1,68 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "forest/editor/forestTool.h" +#include "forest/editor/forestEditorCtrl.h" + +#include "forest/forestDataFile.h" +#include "forest/forestCell.h" + +#include "util/undo.h" +#include "math/mMath.h" +#include "math/mathUtils.h" + + +IMPLEMENT_CONOBJECT( ForestTool ); + +ConsoleDocClass( ForestTool, + "@brief Base class for Forest Editor specific tools\n\n" + "Editor use only.\n\n" + "@internal" +); + +ForestTool::ForestTool() + : mForest( NULL ), + mEditor( NULL ) +{ +} + +ForestTool::~ForestTool() +{ +} + +void ForestTool::_submitUndo( UndoAction *action ) +{ + AssertFatal( action, "ForestTool::_submitUndo() - No undo action!" ); + + // Grab the mission editor undo manager. + UndoManager *undoMan = NULL; + if ( !Sim::findObject( "EUndoManager", undoMan ) ) + { + Con::errorf( "ForestTool::_submitUndo() - EUndoManager not found!" ); + return; + } + + undoMan->addAction( action ); + + mEditor->updateCollision(); +} diff --git a/Engine/source/forest/editor/forestTool.h b/Engine/source/forest/editor/forestTool.h new file mode 100644 index 000000000..be94d206e --- /dev/null +++ b/Engine/source/forest/editor/forestTool.h @@ -0,0 +1,87 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FOREST_EDITOR_TOOL_H_ +#define _FOREST_EDITOR_TOOL_H_ + +#ifndef _FORESTITEM_H_ +#include "forest/forestItem.h" +#endif +#ifndef _SIMBASE_H_ +#include "console/simBase.h" +#endif +#ifndef _GUITYPES_H_ +#include "gui/core/guiTypes.h" +#endif + +class Forest; +class ForestEditorCtrl; +class ForestData; +class UndoAction; +class GuiTSCtrl; +class Point3F; +struct Gui3DMouseEvent; + + +class ForestTool : public SimObject +{ + typedef SimObject Parent; + + protected: + + SimObjectPtr mForest; + ForestEditorCtrl *mEditor; + + void _submitUndo( UndoAction *action ); + + public: + + ForestTool(); + virtual ~ForestTool(); + + DECLARE_CONOBJECT( ForestTool ); + + virtual void setActiveForest( Forest *forest ) { mForest = forest; } + virtual void setParentEditor( ForestEditorCtrl *editor ) { mEditor = editor; } + + virtual void onActivated( const Gui3DMouseEvent &lastEvent ) {} + virtual void onDeactivated() {} + + virtual void on3DMouseDown( const Gui3DMouseEvent &evt ) {} + virtual void on3DMouseUp( const Gui3DMouseEvent &evt ) {} + virtual void on3DMouseMove( const Gui3DMouseEvent &evt ) {} + virtual void on3DMouseDragged( const Gui3DMouseEvent &evt ) {} + virtual void on3DMouseEnter( const Gui3DMouseEvent &evt ) {} + virtual void on3DMouseLeave( const Gui3DMouseEvent &evt ) {} + virtual bool onMouseWheel( const GuiEvent &evt ) { return false; } + virtual void onRender3D() {} + virtual void onRender2D() {} + virtual void updateGizmo() {} + virtual bool updateGuiInfo() { return false; } + virtual void onUndoAction() {} +}; + + +#endif // _FOREST_EDITOR_TOOL_H_ + + + diff --git a/Engine/source/forest/editor/forestUndo.cpp b/Engine/source/forest/editor/forestUndo.cpp new file mode 100644 index 000000000..5889bc26b --- /dev/null +++ b/Engine/source/forest/editor/forestUndo.cpp @@ -0,0 +1,223 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "forest/editor/forestUndo.h" + +#include "forest/forestDataFile.h" +#include "forest/editor/forestEditorCtrl.h" + + +ForestUndoAction::ForestUndoAction( const Resource &data, + ForestEditorCtrl *editor, + const char *description ) + : UndoAction( description ), + mData( data ), + mEditor( editor ) +{ +} + + + +ForestCreateUndoAction::ForestCreateUndoAction( const Resource &data, + ForestEditorCtrl *editor ) + : ForestUndoAction( data, editor, "Create Forest Items" ) +{ +} + +void ForestCreateUndoAction::addItem( ForestItemData *data, + const Point3F &position, + F32 rotation, + F32 scale ) +{ + const ForestItem &item = mData->addItem( data, position, rotation, scale ); + mItems.push_back( item ); + + // We store the datablock ID rather than the actual pointer + // since the pointer could go bad. + SimObjectId dataId = item.getData()->getId(); + mItems.last().setData( (ForestItemData*)dataId ); +} + +void ForestCreateUndoAction::redo() +{ + for ( U32 i = 0; i < mItems.size(); i++ ) + { + const ForestItem &item = mItems[i]; + + // Not 64bit safe! + // We store the datablock ID rather than the actual pointer + // since the pointer could go bad. + ForestItemData *data; + if ( !Sim::findObject( (SimObjectId)(item.getData()), data ) ) + { + Con::errorf( "ForestCreateUndoAction::redo() - ForestItemData for item to restore does not seem to exist. Undo stack may be hosed." ); + continue; + } + + mData->addItem( item.getKey(), + data, + item.getTransform(), + item.getScale() ); + } + + mEditor->onUndoAction(); +} + +void ForestCreateUndoAction::undo() +{ + for ( S32 i = mItems.size()-1; i >= 0; i-- ) + { + const ForestItem &item = mItems[i]; + mData->removeItem( item.getKey(), item.getPosition() ); + } + + mEditor->onUndoAction(); +} + + + +ForestDeleteUndoAction::ForestDeleteUndoAction( const Resource &data, + ForestEditorCtrl *editor ) + : ForestUndoAction( data, editor, "Delete Forest Items" ) +{ +} + +void ForestDeleteUndoAction::removeItem( const ForestItem &item ) +{ + // Not 64bit safe! + // We store the datablock ID rather than the actual pointer + // since the pointer could go bad. + SimObjectId dataId = item.getData()->getId(); + + mItems.push_back( item ); + mItems.last().setData( (ForestItemData*)dataId ); + mData->removeItem( item.getKey(), item.getPosition() ); +} + +void ForestDeleteUndoAction::removeItem( const Vector &itemList ) +{ + for ( S32 i = 0; i < itemList.size(); i++ ) + removeItem( itemList[i] ); +} + +void ForestDeleteUndoAction::redo() +{ + for ( U32 i = 0; i < mItems.size(); i++ ) + { + const ForestItem &item = mItems[i]; + mData->removeItem( item.getKey(), item.getPosition() ); + } + + mEditor->onUndoAction(); +} + +void ForestDeleteUndoAction::undo() +{ + for ( S32 i = mItems.size()-1; i >= 0; i-- ) + { + const ForestItem &item = mItems[i]; + + // We store the datablock ID rather than the actual pointer + // since the pointer could go bad. + ForestItemData *data; + if ( !Sim::findObject( (SimObjectId)(item.getData()), data ) ) + { + Con::errorf( "ForestDeleteUndoAction::undo() - ForestItemData for item to restore does not seem to exist. Undo stack may be hosed." ); + continue; + } + + mData->addItem( item.getKey(), + data, + item.getTransform(), + item.getScale() ); + } + + mEditor->onUndoAction(); +} + + + +ForestUpdateAction::ForestUpdateAction( const Resource &data, + ForestEditorCtrl *editor ) + : ForestUndoAction( data, editor, "Update Forest Items" ) +{ +} + +void ForestUpdateAction::saveItem( const ForestItem &item ) +{ + // We just store the current state... we undo it later. + mItems.push_back( item ); + + // We store the datablock ID rather than the actual pointer + // since the pointer could go bad. + SimObjectId dataId = item.getData()->getId(); + mItems.last().setData( (ForestItemData*)dataId ); +} + +void ForestUpdateAction::_swapState() +{ + Vector prevItems = mItems; + mItems.clear(); + + for ( U32 i=0; i < prevItems.size(); i++ ) + { + const ForestItem &item = prevItems[i]; + + // Save the current state so we can reverse this swap. + // + // Note that we do 'not' want the const ref returned by findItem + // because when we call updateItem below we would lose our copy + // of the items state before the call. + // + ForestItem newItem = mData->findItem( item.getKey() ); + + if ( !newItem.isValid() ) + { + Con::errorf( "ForestUpdateAction::_swapState() - saved item no longer exists. Undo stack may be hosed." ); + continue; + } + + // Not 64bit safe! + // We store the datablock ID rather than the actual pointer + // since the pointer could go bad. + ForestItemData *data; + if ( !Sim::findObject( (SimObjectId)(item.getData()), data ) ) + { + Con::errorf( "ForestUpdateAction::_swapState() - ForestItemData for item to restore does not seem to exist. Undo stack may be hosed." ); + continue; + } + + // Now revert to the old state. + mData->updateItem( item.getKey(), + item.getPosition(), + data, + item.getTransform(), + item.getScale() ); + + // Save the state before this swap for the next swap. + newItem.setData( (ForestItemData*)data->getId() ); + mItems.push_back( newItem ); + } + + mEditor->onUndoAction(); +} diff --git a/Engine/source/forest/editor/forestUndo.h b/Engine/source/forest/editor/forestUndo.h new file mode 100644 index 000000000..1d63c85e9 --- /dev/null +++ b/Engine/source/forest/editor/forestUndo.h @@ -0,0 +1,121 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FOREST_EDITOR_UNDO_H_ +#define _FOREST_EDITOR_UNDO_H_ + +#ifndef _UNDO_H_ +#include "util/undo.h" +#endif +#ifndef _FORESTITEM_H_ +#include "forest/forestItem.h" +#endif +#ifndef __RESOURCE_H__ +#include "core/resource.h" +#endif + +class ForestData; +class ForestEditorCtrl; + +class ForestUndoAction : public UndoAction +{ + typedef UndoAction Parent; + +public: + + ForestUndoAction( const Resource &data, ForestEditorCtrl *editor, const char *description ); + + // UndoAction + virtual void undo() {} + virtual void redo() {} + +protected: + + ForestEditorCtrl *mEditor; + Vector mItems; + Resource mData; +}; + +class ForestCreateUndoAction : public ForestUndoAction +{ + typedef ForestUndoAction Parent; + +public: + + ForestCreateUndoAction( const Resource &data, + ForestEditorCtrl *editor ); + + /// Adds the item to the Forest and stores + /// its info for undo later. + void addItem( ForestItemData *data, + const Point3F &position, + F32 rotation, + F32 scale ); + + // UndoAction + virtual void undo(); + virtual void redo(); +}; + + +class ForestDeleteUndoAction : public ForestUndoAction +{ + typedef ForestUndoAction Parent; + +public: + + ForestDeleteUndoAction( const Resource &data, + ForestEditorCtrl *editor ); + + /// + void removeItem( const ForestItem &item ); + void removeItem( const Vector &itemList ); + + // UndoAction + virtual void undo(); + virtual void redo(); +}; + + +class ForestUpdateAction : public ForestUndoAction +{ + typedef ForestUndoAction Parent; + +public: + + ForestUpdateAction( const Resource &data, + ForestEditorCtrl *editor ); + + void saveItem( const ForestItem &item ); + + virtual void undo() { _swapState(); } + virtual void redo() { _swapState(); } + +protected: + + void _swapState(); +}; + +#endif // _FOREST_EDITOR_UNDO_H_ + + + diff --git a/Engine/source/forest/forest.cpp b/Engine/source/forest/forest.cpp new file mode 100644 index 000000000..41da38500 --- /dev/null +++ b/Engine/source/forest/forest.cpp @@ -0,0 +1,376 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "forest/forest.h" + +#include "forest/forestCell.h" +#include "forest/forestCollision.h" +#include "forest/forestDataFile.h" +#include "forest/forestWindMgr.h" +#include "forest/forestWindAccumulator.h" + +#include "core/resourceManager.h" +#include "core/volume.h" +#include "T3D/gameBase/gameConnection.h" +#include "console/consoleInternal.h" +#include "core/stream/bitStream.h" +#include "math/mathIO.h" +#include "environment/sun.h" +#include "scene/sceneManager.h" +#include "math/mathUtils.h" +#include "T3D/physics/physicsBody.h" +#include "forest/editor/forestBrushElement.h" + +/// For frame signal +#include "gui/core/guiCanvas.h" + + +extern bool gEditingMission; + +ForestCreatedSignal Forest::smCreatedSignal; +ForestCreatedSignal Forest::smDestroyedSignal; + +bool Forest::smForceImposters = false; +bool Forest::smDisableImposters = false; +bool Forest::smDrawCells = false; +bool Forest::smDrawBounds = false; + + +IMPLEMENT_CO_NETOBJECT_V1(Forest); + +ConsoleDocClass( Forest, + + "@brief %Forest is a global-bounds scene object provides collision and rendering for a " + "(.forest) data file.\n\n" + + "%Forest is designed to efficiently render a large number of static meshes: trees, rocks " + "plants, etc. These cannot be moved at game-time or play animations but do support wind " + "effects using vertex shader transformations guided by vertex color in the asset and " + "user placed wind emitters ( or weapon explosions ).\n\n" + + "Script level manipulation of forest data is not possible through %Forest, it is only " + "the rendering/collision. All editing is done through the world editor.\n\n" + + "@see TSForestItemData Defines a tree type.\n" + "@see GuiForestEditorCtrl Used by the world editor to provide manipulation of forest data.\n" + "@ingroup Forest" +); + + +Forest::Forest() + : mDataFileName( NULL ), + mReflectionLodScalar( 2.0f ), + mConvexList( new Convex() ), + mZoningDirty( false ) +{ + mTypeMask |= EnvironmentObjectType | StaticShapeObjectType | StaticObjectType; + mNetFlags.set(Ghostable | ScopeAlways); +} + +Forest::~Forest() +{ + delete mConvexList; + mConvexList = NULL; +} + + +void Forest::initPersistFields() +{ + Parent::initPersistFields(); + + addField( "dataFile", TypeFilename, Offset( mDataFileName, Forest ), + "The source forest data file." ); + + addGroup( "Lod" ); + + addField( "lodReflectScalar", TypeF32, Offset( mReflectionLodScalar, Forest ), + "Scalar applied to the farclip distance when Forest renders into a reflection." ); + + endGroup( "Lod" ); +} + +void Forest::consoleInit() +{ + // Some stats exposed to the console. + Con::addVariable("$Forest::totalCells", TypeS32, &Forest::smTotalCells, "@internal" ); + Con::addVariable("$Forest::cellsRendered", TypeS32, &Forest::smCellsRendered, "@internal" ); + Con::addVariable("$Forest::cellItemsRendered", TypeS32, &Forest::smCellItemsRendered, "@internal" ); + Con::addVariable("$Forest::cellsBatched", TypeS32, &Forest::smCellsBatched, "@internal" ); + Con::addVariable("$Forest::cellItemsBatched", TypeS32, &Forest::smCellItemsBatched, "@internal" ); + Con::addVariable("$Forest::averageCellItems", TypeF32, &Forest::smAverageItemsPerCell, "@internal" ); + + // Some debug flags. + Con::addVariable("$Forest::forceImposters", TypeBool, &Forest::smForceImposters, + "A debugging aid which will force all forest items to be rendered as imposters.\n" + "@ingroup Forest\n" ); + Con::addVariable("$Forest::disableImposters", TypeBool, &Forest::smDisableImposters, + "A debugging aid which will disable rendering of all imposters in the forest.\n" + "@ingroup Forest\n" ); + Con::addVariable("$Forest::drawCells", TypeBool, &Forest::smDrawCells, + "A debugging aid which renders the forest cell bounds.\n" + "@ingroup Forest\n" ); + Con::addVariable("$Forest::drawBounds", TypeBool, &Forest::smDrawBounds, + "A debugging aid which renders the forest bounds.\n" + "@ingroup Forest\n" ); + + // The canvas signal lets us know to clear the rendering stats. + GuiCanvas::getGuiCanvasFrameSignal().notify( &Forest::_clearStats ); +} + +bool Forest::onAdd() +{ + if (!Parent::onAdd()) + return false; + + const char *name = getName(); + if(name && name[0] && getClassRep()) + { + Namespace *parent = getClassRep()->getNameSpace(); + Con::linkNamespaces(parent->mName, name); + mNameSpace = Con::lookupNamespace(name); + } + + setGlobalBounds(); + resetWorldBox(); + + // TODO: Make sure this calls the script "onAdd" which will + // populate the object with forest entries before creation. + addToScene(); + + // If we don't have a file name and the editor is + // enabled then create an empty forest data file. + if ( isServerObject() && ( !mDataFileName || !mDataFileName[0] ) ) + createNewFile(); + else + { + // Try to load the forest file. + mData = ResourceManager::get().load( mDataFileName ); + if ( !mData ) + { + if ( isClientObject() ) + NetConnection::setLastError( "You are missing a file needed to play this mission: %s", mDataFileName ); + + return false; + } + } + + updateCollision(); + + smCreatedSignal.trigger( this ); + + if ( isClientObject() ) + { + mZoningDirty = true; + SceneZoneSpaceManager::getZoningChangedSignal().notify( this, &Forest::_onZoningChanged ); + + ForestWindMgr::getAdvanceSignal().notify( this, &Forest::getLocalWindTrees ); + } + + return true; +} + +void Forest::onRemove() +{ + Parent::onRemove(); + + smDestroyedSignal.trigger( this ); + + if ( mData ) + mData->clearPhysicsRep( this ); + + mData = NULL; + + if ( isClientObject() ) + { + SceneZoneSpaceManager::getZoningChangedSignal().remove( this, &Forest::_onZoningChanged ); + ForestWindMgr::getAdvanceSignal().remove( this, &Forest::getLocalWindTrees ); + } + + mConvexList->nukeList(); + + removeFromScene(); +} + +U32 Forest::packUpdate( NetConnection *connection, U32 mask, BitStream *stream ) +{ + U32 retMask = Parent::packUpdate( connection, mask, stream ); + + if ( stream->writeFlag( mask & MediaMask ) ) + stream->writeString( mDataFileName ); + + if ( stream->writeFlag( mask & LodMask ) ) + { + stream->write( mReflectionLodScalar ); + } + + return retMask; +} + +void Forest::unpackUpdate(NetConnection *connection, BitStream *stream) +{ + Parent::unpackUpdate(connection,stream); + + if ( stream->readFlag() ) + { + mDataFileName = stream->readSTString(); + } + + if ( stream->readFlag() ) // LodMask + { + stream->read( &mReflectionLodScalar ); + } +} + +void Forest::inspectPostApply() +{ + Parent::inspectPostApply(); + + // Update the client... note that this + // doesn't cause a regen of the forest. + setMaskBits( LodMask ); +} + +void Forest::setTransform( const MatrixF &mat ) +{ + // Note: We do not use the position of the forest at all. + Parent::setTransform( mat ); +} + +void Forest::_onZoningChanged( SceneZoneSpaceManager *zoneManager ) +{ + if ( mData == NULL || zoneManager != getSceneManager()->getZoneManager() ) + return; + + mZoningDirty = true; +} + +void Forest::getLocalWindTrees( const Point3F &camPos, F32 radius, Vector *placementInfo ) +{ + PROFILE_SCOPE( Forest_getLocalWindTrees ); + + Vector items; + items.reserve( placementInfo->capacity() ); + mData->getItems( camPos, radius, &items ); + + TreePlacementInfo treeInfo; + dMemset( &treeInfo, 0, sizeof ( TreePlacementInfo ) ); + + // Reserve some space in the output. + placementInfo->reserve( items.size() ); + + // Build an info struct for each returned item. + Vector::const_iterator iter = items.begin(); + for ( ; iter != items.end(); iter++ ) + { + // Skip over any zero wind elements here and + // just keep them out of the final list. + treeInfo.dataBlock = iter->getData(); + if ( treeInfo.dataBlock->mWindScale < 0.001f ) + continue; + + treeInfo.pos = iter->getPosition(); + treeInfo.scale = iter->getScale(); + treeInfo.itemKey = iter->getKey(); + placementInfo->push_back( treeInfo ); + } +} + +void Forest::applyRadialImpulse( const Point3F &origin, F32 radius, F32 magnitude ) +{ + if ( isServerObject() ) + return; + + // Find all the trees in the radius + // then get their accumulators and + // push our impulse into them. + VectorF impulse( 0, 0, 0 ); + ForestWindAccumulator *accumulator = NULL; + + Vector trees; + getLocalWindTrees( origin, radius, &trees ); + for ( U32 i = 0; i < trees.size(); i++ ) + { + const TreePlacementInfo &treeInfo = trees[i]; + accumulator = WINDMGR->getLocalWind( treeInfo.itemKey ); + if ( !accumulator ) + continue; + + impulse = treeInfo.pos - origin; + impulse.normalize(); + impulse *= magnitude; + + accumulator->applyImpulse( impulse ); + } +} + +void Forest::createNewFile() +{ + // Release the current file if we have one. + mData = NULL; + + // We need to construct a default file name + String missionName( Con::getVariable( "$Client::MissionFile" ) ); + missionName.replace( "tools/levels", "levels" ); + missionName = Platform::makeRelativePathName(missionName, Platform::getMainDotCsDir()); + + Torque::Path basePath( missionName ); + String fileName = Torque::FS::MakeUniquePath( basePath.getPath(), basePath.getFileName(), "forest" ); + mDataFileName = StringTable->insert( fileName ); + + ForestData *file = new ForestData; + file->write( mDataFileName ); + delete file; + + mData = ResourceManager::get().load( mDataFileName ); + mZoningDirty = true; +} + +void Forest::saveDataFile( const char *path ) +{ + if ( path ) + mDataFileName = StringTable->insert( path ); + + if ( mData ) + mData->write( mDataFileName ); +} + +ConsoleMethod( Forest, saveDataFile, bool, 2, 3, "saveDataFile( [path] )" ) +{ + object->saveDataFile( argc == 3 ? argv[2] : NULL ); + return true; +} + +ConsoleMethod(Forest, isDirty, bool, 2, 2, "()") +{ + return object->getData() && object->getData()->isDirty(); +} + +ConsoleMethod(Forest, regenCells, void, 2, 2, "()") +{ + object->getData()->regenCells(); +} + +ConsoleMethod(Forest, clear, void, 2, 2, "()" ) +{ + object->clear(); +} \ No newline at end of file diff --git a/Engine/source/forest/forest.h b/Engine/source/forest/forest.h new file mode 100644 index 000000000..b44764b70 --- /dev/null +++ b/Engine/source/forest/forest.h @@ -0,0 +1,214 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _H_FOREST_ +#define _H_FOREST_ + +#ifndef _FORESTITEM_H_ + #include "forest/forestItem.h" +#endif +#ifndef _FORESTDATAFILE_H_ + #include "forest/forestDataFile.h" +#endif +#ifndef _MATHUTIL_FRUSTUM_H_ + #include "math/util/frustum.h" +#endif +#ifndef _GFXTEXTUREHANDLE_H_ + #include "gfx/gfxTextureHandle.h" +#endif +#ifndef _COLLISION_H_ + #include "collision/collision.h" +#endif +#ifndef _SCENEOBJECT_H_ + #include "scene/sceneObject.h" +#endif +#ifndef _SIGNAL_H_ + #include "core/util/tSignal.h" +#endif +#ifndef __RESOURCE_H__ + #include "core/resource.h" +#endif +#ifndef _CONVEX_H_ + #include "collision/convex.h" +#endif + + +class TSShapeInstance; +class Forest; +class ForestItemData; +class ForestData; +class GBitmap; +class IForestCollision; +class IForestMask; +class PhysicsBody; +struct ObjectRenderInst; +struct TreePlacementInfo; +class ForestRayInfo; +class SceneZoneSpaceManager; + + +struct TreeInfo +{ + U32 treeId; + SimObjectId treeTypeId; + F32 distance; + SimObjectId forestId; + Point3F position; +}; + +typedef Signal ForestCreatedSignal; + + +/// +class Forest : public SceneObject +{ + friend class CreateForestEvent; + friend class ForestConvex; + +protected: + + typedef SceneObject Parent; + + /// Collision and Physics + /// @{ + + Convex* mConvexList; + + /// @} + + /// The name of the planting data file. + StringTableEntry mDataFileName; + + /// The forest data file which defines planting. + Resource mData; + + /// Used to scale the tree LODs when rendering into + /// reflections. It should be greater or equal to 1. + F32 mReflectionLodScalar; + + /// Set when rezoning of forest cells is required. + bool mZoningDirty; + + /// Debug helpers. + static bool smForceImposters; + static bool smDisableImposters; + static bool smDrawCells; + static bool smDrawBounds; + + /// + bool mRegen; + + enum MaskBits + { + MediaMask = Parent::NextFreeMask << 1, + LodMask = Parent::NextFreeMask << 2, + NextFreeMask = Parent::NextFreeMask << 3 + }; + + + static U32 smTotalCells; + static U32 smCellsRendered; + static U32 smCellItemsRendered; + static U32 smCellsBatched; + static U32 smCellItemsBatched; + static F32 smAverageItemsPerCell; + + static void _clearStats(bool); + + void _renderCellBounds( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); + + void _onZoningChanged( SceneZoneSpaceManager *zoneManager ); + + static ForestCreatedSignal smCreatedSignal; + static ForestCreatedSignal smDestroyedSignal; + +public: + + static ForestCreatedSignal& getCreatedSignal() { return smCreatedSignal; } + static ForestCreatedSignal& getDestroyedSignal() { return smDestroyedSignal; } + + Forest(); + virtual ~Forest(); + + DECLARE_CONOBJECT(Forest); + static void consoleInit(); + static void initPersistFields(); + + // SimObject + bool onAdd(); + void onRemove(); + + /// Overloaded from SceneObject to properly update + /// the client side forest when changes occur within + /// the mission editor. + void inspectPostApply(); + + /// Overloaded from SceneObject for updating the + /// client side position of the forest. + void setTransform( const MatrixF &mat ); + + void prepRenderImage( SceneRenderState *state ); + + bool isTreeInRange( const Point2F& point, F32 radius ) const; + + // Network + U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream ); + void unpackUpdate( NetConnection *conn, BitStream *stream ); + + //IForestCollision *getCollision() const { return mCollision; } + + // SceneObject - Collision + virtual void buildConvex( const Box3F& box, Convex* convex ); + virtual bool buildPolyList( PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere ); + virtual bool castRay( const Point3F &start, const Point3F &end, RayInfo *outInfo ); + virtual bool castRayRendered( const Point3F &start, const Point3F &end, RayInfo *outInfo ); + virtual bool collideBox( const Point3F &start, const Point3F &end, RayInfo *outInfo ); + + // SceneObject - Other + virtual void applyRadialImpulse( const Point3F &origin, F32 radius, F32 magnitude ); + + bool castRayBase( const Point3F &start, const Point3F &end, RayInfo *outInfo, bool rendered ); + + const Resource& getData() const { return mData; } + + Resource& getData() { return mData; } + + //bool buildPolyList(AbstractPolyList* polyList, const Box3F &box, const SphereF& sphere); + //void buildConvex(const Box3F& box, Convex* convex); + + void getLocalWindTrees( const Point3F &camPos, F32 radius, Vector *placementInfo ); + + /// Called to create a new empty planting data file and + /// assign it to this forest. + void createNewFile(); + + /// + void saveDataFile( const char *path = NULL ); + + /// + void clear() { mData->clear(); } + + /// Called to rebuild the collision state. + void updateCollision(); +}; + +#endif // _H_FOREST_ \ No newline at end of file diff --git a/Engine/source/forest/forestCell.cpp b/Engine/source/forest/forestCell.cpp new file mode 100644 index 000000000..a2a4f36e6 --- /dev/null +++ b/Engine/source/forest/forestCell.cpp @@ -0,0 +1,505 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "forest/forestCell.h" + +#include "forest/forest.h" +#include "forest/forestCellBatch.h" +#include "forest/forestCollision.h" +#include "T3D/physics/physicsPlugin.h" +#include "T3D/physics/physicsBody.h" +#include "T3D/physics/physicsCollision.h" +#include "collision/concretePolyList.h" + +#include "gfx/gfxDrawUtil.h" +#include "math/util/frustum.h" + + +ForestCell::ForestCell( const RectF &rect ) : + mRect( rect ), + mBounds( Box3F::Invalid ), + mLargestItem( ForestItem::Invalid ), + mIsDirty( false ), + mIsInteriorOnly( false ) +{ + dMemset( mSubCells, 0, sizeof( mSubCells ) ); + dMemset( mPhysicsRep, 0, sizeof( mPhysicsRep ) ); +} + +ForestCell::~ForestCell() +{ + mItems.clear(); + + for ( U32 i=0; i < 4; i++ ) + SAFE_DELETE( mSubCells[i] ); + + freeBatches(); + + SAFE_DELETE( mPhysicsRep[0] ); + SAFE_DELETE( mPhysicsRep[1] ); +} + +void ForestCell::freeBatches() +{ + for ( U32 i=0; i < mBatches.size(); i++ ) + SAFE_DELETE( mBatches[i] ); + + mBatches.clear(); +} + +void ForestCell::buildBatches() +{ + // Gather items for batches. + Vector items; + getItems( &items ); + + // Ask the item to batch itself. + Vector::const_iterator item = items.begin(); + bool batched = false; + for ( ; item != items.end(); item++ ) + { + // Loop thru the batches till someone + // takes this guy off our hands. + batched = false; + for ( S32 i=0; i < mBatches.size(); i++ ) + { + if ( mBatches[i]->add( *item ) ) + { + batched = true; + break; + } + } + + if ( batched ) + continue; + + // Gotta create a new batch. + ForestCellBatch *batch = item->getData()->allocateBatch(); + if ( batch ) + { + batch->add( *item ); + mBatches.push_back( batch ); + } + } +} + +S32 ForestCell::renderBatches( SceneRenderState *state, Frustum *culler ) +{ + PROFILE_SCOPE( ForestCell_renderBatches ); + + if ( !hasBatches() ) + return 0; + + S32 renderedItems = 0; + + for ( S32 i=0; i < mBatches.size(); i++ ) + { + // Is this batch entirely culled? + if ( culler && culler->isCulled( mBatches[i]->getWorldBox() ) ) + continue; + + mBatches[i]->render( state ); + renderedItems += mBatches[i]->getItemCount(); + } + + return renderedItems; +} + +S32 ForestCell::render( TSRenderState *rdata, const Frustum *culler ) +{ + PROFILE_SCOPE( ForestCell_render ); + + AssertFatal( isLeaf(), "ForestCell::render() - This shouldn't be called on non-leaf cells!" ); + + U32 itemsRendered = 0; + + // TODO: Items are generated in order of type, + // so we can maybe save some overhead by preparing + // the item for rendering once. + + Vector::iterator item = mItems.begin(); + for ( ; item != mItems.end(); item++ ) + { + // Do we need to cull individual items? + if ( culler && culler->isCulled( item->getWorldBox() ) ) + continue; + + if ( item->getData()->render( rdata, *item ) ) + ++itemsRendered; + } + + return itemsRendered; +} + +void ForestCell::_updateBounds() +{ + mIsDirty = false; + mBounds = Box3F::Invalid; + mLargestItem = ForestItem::Invalid; + + F32 radius; + + if ( isBranch() ) + { + for ( U32 i=0; i < 4; i++ ) + { + mBounds.intersect( mSubCells[i]->getBounds() ); + + radius = mSubCells[i]->mLargestItem.getRadius(); + if ( radius > mLargestItem.getRadius() ) + mLargestItem = mSubCells[i]->mLargestItem; + } + + return; + } + + // Loop thru all the items in this cell. + Vector::const_iterator item = mItems.begin(); + for ( ; item != mItems.end(); item++ ) + { + mBounds.intersect( (*item).getWorldBox() ); + + radius = (*item).getRadius(); + if ( radius > mLargestItem.getRadius() ) + mLargestItem = (*item); + } +} + +void ForestCell::_updateZoning( const SceneZoneSpaceManager *zoneManager ) +{ + PROFILE_SCOPE( ForestCell_UpdateZoning ); + + mZoneOverlap.setSize( zoneManager->getNumZones() ); + mZoneOverlap.clear(); + mIsInteriorOnly = true; + + if ( isLeaf() ) + { + // Skip empty cells... they don't have valid bounds. + if ( mItems.empty() ) + return; + + Vector zones; + zoneManager->findZones( getBounds(), zones ); + + for ( U32 i=0; i < zones.size(); i++ ) + { + // Set overlap bit for zone except it's the outdoor zone. + if( zones[ i ] != SceneZoneSpaceManager::RootZoneId ) + mZoneOverlap.set( zones[i] ); + else + mIsInteriorOnly = false; + } + + return; + } + + for ( U32 i = 0; i < 4; i++ ) + { + ForestCell *cell = mSubCells[i]; + cell->_updateZoning( zoneManager ); + mZoneOverlap.combineOR( cell->getZoneOverlap() ); + mIsInteriorOnly &= cell->mIsInteriorOnly; + } +} + +bool ForestCell::findIndexByKey( ForestItemKey key, U32 *outIndex ) const +{ + // Do a simple binary search. + + U32 i = 0, + lo = 0, + hi = mItems.size(); + + const ForestItem *items = mItems.address(); + + while ( lo < hi ) + { + i = (lo + hi) / 2; + + if ( key < items[i].getKey() ) + hi = i; + else if ( key > items[i].getKey() ) + lo = i + 1; + else + { + *outIndex = i; + return true; + } + } + + *outIndex = lo; + return false; +} + +const ForestItem& ForestCell::insertItem( ForestItemKey key, + ForestItemData *data, + const MatrixF &xfm, + F32 scale ) +{ + AssertFatal( key != 0, "ForestCell::insertItem() - Got null key!" ); + AssertFatal( data != NULL, "ForestCell::insertItem() - Got null datablock!" ); + + // Make sure we update the bounds later. + mIsDirty = true; + + // PhysicsBody is now invalid and must be rebuilt later. + SAFE_DELETE( mPhysicsRep[0] ); + SAFE_DELETE( mPhysicsRep[1] ); + + // Destroy batches so we recreate it on + // the next next render. + freeBatches(); + + // Ok... do we need to split this cell? + if ( isLeaf() && mItems.size() > MaxItems ) + { + // Add the children. + for ( U32 i=0; i < 4; i++ ) + mSubCells[i] = new ForestCell( _makeChildRect( i ) ); + + // Now push all our current children down. + Vector::iterator item = mItems.begin(); + for ( ; item != mItems.end(); item++ ) + { + U32 index = _getSubCell( item->getPosition().x, item->getPosition().y ); + + mSubCells[index]->insertItem( item->getKey(), + item->getData(), + item->getTransform(), + item->getScale() ); + } + + // Clean up. + mItems.clear(); + mItems.compact(); + } + + // Do we have children? + if ( isBranch() ) + { + // Ok... kick this item down then. + U32 index = _getSubCell( xfm.getPosition().x, xfm.getPosition().y ); + const ForestItem &result = mSubCells[index]->insertItem( key, data, xfm, scale ); + + AssertFatal( index == _getSubCell( result.getPosition().x, result.getPosition().y ), "ForestCell::insertItem() - binning is hosed." ); + + return result; + } + + // Do the datablock preload here to insure it happens + // before an item is used in the scene. + data->preload(); + + // First see if we can find it. This is nifty so + // I'll explain it a bit more. + // + // The find function does a binary search thru the + // sorted item list. + // + // If found the index is the position of the item. + // + // If not found the index is the correct insertion + // position for adding the new item. + // + // So not only do we have a fast find which is worst + // case O(log n)... but we also have the proper insert + // position to maintain a sorted item list. + // + U32 index; + bool found = findIndexByKey( key, &index ); + + // If we didn't find one then insert it. + if ( !found ) + mItems.insert( index ); + + // Update the item settings. + ForestItem &item = mItems[ index ]; + item.setData( data ); + item.setTransform( xfm, scale ); + + if ( !found ) + item.setKey( key ); + + return item; +} + +bool ForestCell::removeItem( ForestItemKey key, const Point3F &keyPos, bool deleteIfEmpty ) +{ + PROFILE_SCOPE( ForestCell_removeItem ); + + AssertFatal( key != 0, "ForestCell::removeItem() - Got null key!" ); + + // If this cell has no items then check the children. + if ( mItems.empty() ) + { + // Let the child deal with it. + U32 index = _getSubCell( keyPos.x, keyPos.y ); + if ( !mSubCells[index]->removeItem( key, keyPos, deleteIfEmpty ) ) + { + // For debugging lets make sure we didn't pick the wrong subCell... + + return false; + } + + // If requested by the caller delete our empty subcells. + // Note that by deleting SubCell[0] we have become a leaf with no items + // and will return true to our parent's isEmpty test. + if ( deleteIfEmpty && + mSubCells[0]->isEmpty() && + mSubCells[1]->isEmpty() && + mSubCells[2]->isEmpty() && + mSubCells[3]->isEmpty() ) + { + SAFE_DELETE( mSubCells[0] ); + SAFE_DELETE( mSubCells[1] ); + SAFE_DELETE( mSubCells[2] ); + SAFE_DELETE( mSubCells[3] ); + } + } + else + { + // First see if we can find it. + U32 index; + if ( !findIndexByKey( key, &index ) ) + return false; + + // Erase it. + mItems.erase( index ); + } + + // Do a full bounds update on the next request. + mIsDirty = true; + + // PhysicsBody is now invalid and must be rebuilt later. + SAFE_DELETE( mPhysicsRep[0] ); + SAFE_DELETE( mPhysicsRep[1] ); + + // Destroy batches so we recreate it on + // the next next render. + freeBatches(); + + return true; +} + +void ForestCell::getItems( Vector *outItems ) const +{ + Vector stack; + stack.push_back( this ); + + // Now loop till we run out of cells. + while ( !stack.empty() ) + { + // Pop off the next cell. + const ForestCell *cell = stack.last(); + stack.pop_back(); + + // Recurse thru non-leaf cells. + if ( !cell->isLeaf() ) + { + stack.merge( cell->mSubCells, 4 ); + continue; + } + + // Get the items. + outItems->merge( cell->getItems() ); + } +} + +void ForestCell::buildPhysicsRep( Forest *forest ) +{ + AssertFatal( isLeaf(), "ForestCell::buildPhysicsRep() - This shouldn't be called on non-leaf cells!" ); + + bool isServer = forest->isServerObject(); + + // Already has a PhysicsBody, if it needed to be rebuilt it would + // already be null. + if ( mPhysicsRep[ isServer ] ) + return; + + if ( !PHYSICSMGR ) + return; + + PhysicsCollision *colShape = NULL; + + // If we can steal the collision shape from the server-side cell + // then do so as it saves us alot of cpu time and memory. + if ( mPhysicsRep[ 1 ] ) + { + colShape = mPhysicsRep[ 1 ]->getColShape(); + } + else + { + // We must pass a sphere to buildPolyList but it is not used. + const static SphereF dummySphere( Point3F::Zero, 0 ); + + // Step thru them and build collision data. + ForestItemVector::iterator itemItr = mItems.begin(); + ConcretePolyList polyList; + for ( ; itemItr != mItems.end(); itemItr++ ) + { + const ForestItem &item = *itemItr; + const ForestItemData *itemData = item.getData(); + + // If not collidable don't need to build anything. + if ( !itemData->mCollidable ) + continue; + + // TODO: When we add breakable tree support this is where + // we would need to store their collision data seperately. + + item.buildPolyList( &polyList, item.getWorldBox(), dummySphere ); + + // TODO: Need to support multiple collision shapes + // for really big forests at some point in the future. + } + + if ( !polyList.isEmpty() ) + { + colShape = PHYSICSMGR->createCollision(); + if ( !colShape->addTriangleMesh( polyList.mVertexList.address(), + polyList.mVertexList.size(), + polyList.mIndexList.address(), + polyList.mIndexList.size() / 3, + MatrixF::Identity ) ) + { + SAFE_DELETE( colShape ); + } + } + } + + // We might not have any trees. + if ( !colShape ) + return; + + PhysicsWorld *world = PHYSICSMGR->getWorld( isServer ? "server" : "client" ); + mPhysicsRep[ isServer ] = PHYSICSMGR->createBody(); + mPhysicsRep[ isServer ]->init( colShape, 0, 0, forest, world ); +} + +void ForestCell::clearPhysicsRep( Forest *forest ) +{ + bool isServer = forest->isServerObject(); + + SAFE_DELETE( mPhysicsRep[ isServer ] ); +} \ No newline at end of file diff --git a/Engine/source/forest/forestCell.h b/Engine/source/forest/forestCell.h new file mode 100644 index 000000000..4133e06cd --- /dev/null +++ b/Engine/source/forest/forestCell.h @@ -0,0 +1,225 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FORESTCELL_H_ +#define _FORESTCELL_H_ + +#ifndef _FORESTITEM_H_ +#include "forest/forestItem.h" +#endif +#ifndef _H_FOREST_ +#include "forest/forest.h" +#endif +#ifndef _BITVECTOR_H_ +#include "core/bitVector.h" +#endif + +class ForestCellBatch; +class SceneRenderState; +class Frustum; +class IForestCellCollision; +class PhysicsBody; +//class ForestRayInfo; + + +/// +class ForestCell +{ + friend class Forest; + +protected: + + /// The area which this cell represents in the world. + RectF mRect; + + /// If items have been added or removed the dirty flag + /// is set so that the bounds can be rebuilt on the next + /// request. + bool mIsDirty; + + /// The combined bounding box of all the items + /// in or children of this cell. + Box3F mBounds; + + /// All the items in this cell. + Vector mItems; + + /// A vector of the current batches + /// associated with this cell. + Vector mBatches; + + /// The largest item in this cell. + ForestItem mLargestItem; + + /// PhysicsBody for client and server for all trees in this ForestCell, + /// if it is a leaf cell. + PhysicsBody *mPhysicsRep[2]; + + /// The quad tree cells below this one which + /// may contain sub elements. + ForestCell* mSubCells[4]; + + /// The zone overlap for this cell. + /// @note The bit for the outdoor zone is never set. + BitVector mZoneOverlap; + + /// Whether this cell is fully contained inside interior zones. + bool mIsInteriorOnly; + + /// + inline U32 _getSubCell( F32 x, F32 y ) const + { + bool left = x < ( mRect.point.x + ( mRect.extent.x / 2.0f ) ); + bool top = y < ( mRect.point.y + ( mRect.extent.y / 2.0f ) ); + + if ( left && top ) return 0; + if ( left && !top ) return 1; + if ( !left && top ) return 2; + + return 3; + } + + /// + inline RectF _makeChildRect( U32 index ) const + { + RectF rect( mRect.point, mRect.extent / 2.0f ); + + if ( index == 1 || index == 3 ) + rect.point.y += rect.extent.y; + + if ( index > 1 ) + rect.point.x += rect.extent.x; + + return rect; + } + + void _updateBounds(); + + /// + void _updateZoning( const SceneZoneSpaceManager *zoneManager ); + +public: + + /// The maximum amount of objects allowed in a + /// cell before we repartition it. + static const U32 MaxItems = 200; + + ForestCell( const RectF &rect ); + virtual ~ForestCell(); + + /// Returns the 2D rectangle of quad tree bounds for this + /// cell. It is fixed and does not change during runtime. + const RectF& getRect() const { return mRect; } + + /// The bounds of the cell generated by combining the + /// bounds of all the contained items or child cells. + const Box3F& getBounds() const; + + /// Set flag so this cells bounds will be recalculated the next call to getBounds. + void invalidateBounds() { mIsDirty = true; } + + /// Returns true if this cell has no items and no child cells. + bool isEmpty() const { return isLeaf() && mItems.empty(); } + + /// Returns true if this cell does not have child cells. + /// It should directly contain items. + bool isLeaf() const { return !mSubCells[0]; } + + /// Returns true if this cell has child cells. + /// This is the same as !isLeaf() but exists for clarity. + bool isBranch() const { return mSubCells[0] != NULL; } + + /// Returns a bit vector of what zones overlap this cell. + const BitVector& getZoneOverlap() const { return mZoneOverlap; } + + /// + bool castRay( const Point3F &start, const Point3F &end, RayInfo *outInfo, bool rendered ) const; + + bool hasBatches() const { return !mBatches.empty(); } + + void buildBatches(); + + void freeBatches(); + + S32 renderBatches( SceneRenderState *state, Frustum *culler ); + + + S32 render( TSRenderState *rdata, const Frustum *culler ); + + + /// The find function does a binary search thru the sorted + /// item list. If the key is found then the index is the + /// position of the item. If the key is not found the index + /// is the correct insertion position for adding the new item. + /// + /// @param key The item key to search for. + /// + /// @param outIndex The item index or insertion index if + /// the item was not found. + /// + /// @return Returns true if the item index is found. + /// + bool findIndexByKey( ForestItemKey key, U32 *outIndex ) const; + + const ForestItem& getLargestItem() const { return mLargestItem; } + + const ForestItem& insertItem( ForestItemKey key, + ForestItemData *data, + const MatrixF &xfm, + F32 scale ); + + bool removeItem( ForestItemKey key, const Point3F &keyPos, bool deleteIfEmpty = false ); + + /// Returns the child cell at the position. The position is + /// assumed to be within this cell. + ForestCell* getChildAt( const Point3F &pos ) const; + + /// Returns the child cells. + void getChildren( Vector *outCells ) const { outCells->merge( mSubCells, 4 ); } + void getChildren( Vector *outCells ) const { outCells->merge( mSubCells, 4 ); } + + /// Returns the items from this one cell. + const Vector& getItems() const { return mItems; } + + /// Returns the items from this cell and all its sub-cells. + void getItems( Vector *outItems ) const; + + void clearPhysicsRep( Forest *forest ); + void buildPhysicsRep( Forest *forest ); +}; + + +inline const Box3F& ForestCell::getBounds() const +{ + if ( mIsDirty ) + const_cast( this )->_updateBounds(); + + return mBounds; +} + +inline ForestCell* ForestCell::getChildAt( const Point3F &pos ) const +{ + U32 index = _getSubCell( pos.x, pos.y ); + return mSubCells[index]; +} + +#endif // _FORESTCELL_H_ diff --git a/Engine/source/forest/forestCellBatch.cpp b/Engine/source/forest/forestCellBatch.cpp new file mode 100644 index 000000000..5a23f2bcb --- /dev/null +++ b/Engine/source/forest/forestCellBatch.cpp @@ -0,0 +1,71 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "forest/forestCellBatch.h" + +#include "forest/forestItem.h" + + +ForestCellBatch::ForestCellBatch() + : mDirty( false ), + mBounds( Box3F::Invalid ) +{ +} + +ForestCellBatch::~ForestCellBatch() +{ +} + +bool ForestCellBatch::add( const ForestItem &item ) +{ + // A little hacky, but don't allow more than 65K / 6 items + // in a cell... this is generally the VB size limit on hardware. + const U32 maxItems = 10000; + if ( mItems.size() > maxItems ) + return false; + + // Do the pre batching tests... if it fails + // then we cannot batch this type! + if ( !_prepBatch( item ) ) + return false; + + // Add it to our list and we'll populate the VB at render time. + mItems.push_back( item ); + mDirty = true; + + // Expand out bounds. + const Box3F &box = item.getWorldBox(); + mBounds.intersect( box ); + return true; +} + +void ForestCellBatch::render( SceneRenderState *state ) +{ + if ( mDirty ) + { + _rebuildBatch(); + mDirty = false; + } + + _render( state ); +} diff --git a/Engine/source/forest/forestCellBatch.h b/Engine/source/forest/forestCellBatch.h new file mode 100644 index 000000000..8b021778a --- /dev/null +++ b/Engine/source/forest/forestCellBatch.h @@ -0,0 +1,62 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FORESTCELLBATCH_H_ +#define _FORESTCELLBATCH_H_ + +#include "scene/sceneRenderState.h" + +class ForestItem; + + +class ForestCellBatch +{ +protected: + + /// Used to detect when the batch rendering + /// objects need to be repacked. + bool mDirty; + + /// The items in the batch. + Vector mItems; + + /// The world space bounding box of this batch. + Box3F mBounds; + + virtual bool _prepBatch( const ForestItem &item ) = 0; + virtual void _rebuildBatch() = 0; + virtual void _render( const SceneRenderState *state ) = 0; + +public: + + ForestCellBatch(); + virtual ~ForestCellBatch(); + + bool add( const ForestItem &item ); + S32 getItemCount() const { return mItems.size(); } + + void render( SceneRenderState *state ); + const Box3F& getWorldBox() const { return mBounds; } +}; + + +#endif // _FORESTCELLBATCH_H_ diff --git a/Engine/source/forest/forestCollision.cpp b/Engine/source/forest/forestCollision.cpp new file mode 100644 index 000000000..8658f2474 --- /dev/null +++ b/Engine/source/forest/forestCollision.cpp @@ -0,0 +1,477 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "forest/forestCollision.h" + +#include "forest/forest.h" +#include "forest/forestCell.h" +#include "forest/ts/tsForestItemData.h" +#include "ts/tsShapeInstance.h" +#include "T3D/physics/physicsPlugin.h" +#include "T3D/physics/physicsBody.h" +#include "T3D/physics/physicsCollision.h" +#include "collision/concretePolyList.h" +#include "platform/profiler.h" + + +/* +bool Forest::castRay( const Point3F &start, const Point3F &end, RayInfo* info ) +{ + //if ( !mData ) + //return false; + //return mData->castRay( start, end, outInfo ); + + return false; +} + +bool Forest::castRayI( const Point3F &start, const Point3F &end, ForestRayInfo *outInfo ) +{ + if ( !mData ) + return false; + + return mData->castRay( start, end, outInfo ); +} + +ScriptMethod( Forest, forestRayCast, const char*, 4, 4, "( Point3F start, Point3F end )" + "Cast a ray from start to end, checking for collision against trees in this forest.\n\n" + "@returns A string containing either null, if nothing was struck, or these fields:\n" + " - The ID of the tree type datablock.\n" + " - The tree ID (not a normal object id).\n" + " - t, The time during the raycast at which the collision occured." ) +{ + + Point3F start, end; + dSscanf(argv[2], "%g %g %g", &start.x, &start.y, &start.z); + dSscanf(argv[3], "%g %g %g", &end.x, &end.y, &end.z); + + char *returnBuffer = Con::getReturnBuffer(256); + returnBuffer[0] = '0'; + returnBuffer[1] = '\0'; + + ForestRayInfo rinfo; + if ( object->castRayI( start, end, &rinfo ) ) + dSprintf( returnBuffer, 256, "%d %d %g", rinfo.item->getData()->getId(), rinfo.key, rinfo.t ); + + return returnBuffer; +} +*/ + +void ForestConvex::calculateTransform( const MatrixF &worldXfrm ) +{ + mTransform = worldXfrm; + return; +} + +Box3F ForestConvex::getBoundingBox() const +{ + // This is probably a bad idea? -- BJG + return getBoundingBox( mTransform, Point3F(mScale,mScale,mScale) ); +} + +Box3F ForestConvex::getBoundingBox(const MatrixF& mat, const Point3F& scale) const +{ + Box3F newBox = box; + newBox.minExtents.convolve(scale); + newBox.maxExtents.convolve(scale); + mat.mul(newBox); + return newBox; +} + +Point3F ForestConvex::support(const VectorF& v) const +{ + TSShapeInstance *si = mData->getShapeInstance(); + + TSShape::ConvexHullAccelerator* pAccel = + si->getShape()->getAccelerator(mData->getCollisionDetails()[hullId]); + AssertFatal(pAccel != NULL, "Error, no accel!"); + + F32 currMaxDP = mDot(pAccel->vertexList[0], v); + U32 index = 0; + for (U32 i = 1; i < pAccel->numVerts; i++) + { + F32 dp = mDot(pAccel->vertexList[i], v); + if (dp > currMaxDP) + { + currMaxDP = dp; + index = i; + } + } + + return pAccel->vertexList[index]; +} + +void ForestConvex::getFeatures( const MatrixF &mat, const VectorF &n, ConvexFeature *cf ) +{ + cf->material = 0; + cf->object = mObject; + + TSShapeInstance *si = mData->getShapeInstance(); + + TSShape::ConvexHullAccelerator* pAccel = + si->getShape()->getAccelerator(mData->getCollisionDetails()[hullId]); + AssertFatal(pAccel != NULL, "Error, no accel!"); + + F32 currMaxDP = mDot(pAccel->vertexList[0], n); + U32 index = 0; + U32 i; + for (i = 1; i < pAccel->numVerts; i++) + { + F32 dp = mDot(pAccel->vertexList[i], n); + if (dp > currMaxDP) + { + currMaxDP = dp; + index = i; + } + } + + const U8* emitString = pAccel->emitStrings[index]; + U32 currPos = 0; + U32 numVerts = emitString[currPos++]; + for (i = 0; i < numVerts; i++) + { + cf->mVertexList.increment(); + U32 index = emitString[currPos++]; + mat.mulP(pAccel->vertexList[index], &cf->mVertexList.last()); + } + + U32 numEdges = emitString[currPos++]; + for (i = 0; i < numEdges; i++) + { + U32 ev0 = emitString[currPos++]; + U32 ev1 = emitString[currPos++]; + cf->mEdgeList.increment(); + cf->mEdgeList.last().vertex[0] = ev0; + cf->mEdgeList.last().vertex[1] = ev1; + } + + U32 numFaces = emitString[currPos++]; + for (i = 0; i < numFaces; i++) + { + cf->mFaceList.increment(); + U32 plane = emitString[currPos++]; + mat.mulV(pAccel->normalList[plane], &cf->mFaceList.last().normal); + for (U32 j = 0; j < 3; j++) + cf->mFaceList.last().vertex[j] = emitString[currPos++]; + } +} + +void ForestConvex::getPolyList(AbstractPolyList* list) +{ + list->setTransform( &mTransform, Point3F(mScale,mScale,mScale)); + list->setObject(mObject); + + TSShapeInstance *si = mData->getShapeInstance(); + + S32 detail = mData->getCollisionDetails()[hullId]; + si->animate(detail); + si->buildPolyList(list, detail); +} + + +void Forest::buildConvex( const Box3F &box, Convex *convex ) +{ + mConvexList->collectGarbage(); + + // Get all ForestItem(s) within the box. + Vector trees; + mData->getItems( box, &trees ); + if ( trees.empty() ) + return; + + for ( U32 i = 0; i < trees.size(); i++ ) + { + const ForestItem &forestItem = trees[i]; + + Box3F realBox = box; + mWorldToObj.mul( realBox ); + realBox.minExtents.convolveInverse( mObjScale ); + realBox.maxExtents.convolveInverse( mObjScale ); + + // JCF: is this really necessary if we already got this ForestItem + // as a result from getItems? + if ( realBox.isOverlapped( getObjBox() ) == false ) + continue; + + TSForestItemData *data = (TSForestItemData*)forestItem.getData(); + + // Find CollisionDetail(s) that are defined... + const Vector &details = data->getCollisionDetails(); + for ( U32 j = 0; j < details.size(); j++ ) + { + // JCFHACK: need to fix this if we want this to work with speedtree + // or other cases in which we don't have a TSForestItemData. + // Most likely via preventing this method and other torque collision + // specific stuff from ever getting called. + if ( details[j] == -1 ) + continue; + + // See if this convex exists in the working set already... + Convex* cc = 0; + CollisionWorkingList& wl = convex->getWorkingList(); + for ( CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext ) + { + if ( itr->mConvex->getType() == ForestConvexType ) + { + ForestConvex *pConvex = static_cast(itr->mConvex); + + if ( pConvex->mObject == this && + pConvex->mForestItemKey == forestItem.getKey() && + pConvex->hullId == j ) + { + cc = itr->mConvex; + break; + } + } + } + if (cc) + continue; + + // Then we need to make one. + ForestConvex *cp = new ForestConvex; + mConvexList->registerObject(cp); + convex->addToWorkingList(cp); + cp->mObject = this; + cp->mForestItemKey = forestItem.getKey(); + cp->mData = data; + cp->mScale = forestItem.getScale(); + cp->hullId = j; + cp->box = forestItem.getObjBox(); + cp->calculateTransform( forestItem.getTransform() ); + } + } +} + +bool Forest::buildPolyList( PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere ) +{ + if ( context == PLC_Decal ) + return false; + + // Get all ForestItem(s) within the box. + Vector trees; + if ( mData->getItems( box, &trees ) == 0 ) + return false; + + polyList->setObject(this); + + bool gotPoly = false; + + for ( U32 i = 0; i < trees.size(); i++ ) + gotPoly |= trees[i].buildPolyList( polyList, box, sphere ); + + return gotPoly; +} + +bool ForestItem::buildPolyList( AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere ) const +{ + TSForestItemData *data = (TSForestItemData*)mDataBlock; + + bool ret = false; + + MatrixF xfm = getTransform(); + polyList->setTransform( &xfm, Point3F(mScale,mScale,mScale) ); + + TSShapeInstance *si = data->getShapeInstance(); + + const Vector &details = data->getCollisionDetails(); + S32 detail; + for (U32 i = 0; i < details.size(); i++ ) + { + detail = details[i]; + if (detail != -1) + ret |= si->buildPolyList( polyList, detail ); + } + + return ret; +} + +bool Forest::collideBox( const Point3F &start, const Point3F &end, RayInfo *outInfo ) +{ + //Con::warnf( "Forest::collideBox() - not yet implemented!" ); + return Parent::collideBox( start, end, outInfo ); +} + +bool Forest::castRay( const Point3F &start, const Point3F &end, RayInfo *outInfo ) +{ + return castRayBase( start, end, outInfo, false ); +} + +bool Forest::castRayRendered( const Point3F &start, const Point3F &end, RayInfo *outInfo ) +{ + return castRayBase( start, end, outInfo, true ); +} + +bool Forest::castRayBase( const Point3F &start, const Point3F &end, RayInfo *outInfo, bool rendered ) +{ + if ( !getObjBox().collideLine( start, end ) ) + return false; + + if ( mData->castRay( start, end, outInfo, rendered ) ) + { + outInfo->object = this; + return true; + } + + return false; +} + +void Forest::updateCollision() +{ + if ( !mData ) + return; + + mData->buildPhysicsRep( this ); + + // Make the assumption that if collision needs + // to be updated that the zoning probably changed too. + if ( isClientObject() ) + mZoningDirty = true; +} + +bool ForestData::castRay( const Point3F &start, const Point3F &end, RayInfo *outInfo, bool rendered ) const +{ + RayInfo shortest; + shortest.userData = outInfo->userData; + shortest.t = F32_MAX; + + BucketTable::ConstIterator iter = mBuckets.begin(); + for ( ; iter != mBuckets.end(); iter++ ) + { + if ( iter->value->castRay( start, end, outInfo, rendered ) ) + { + if ( outInfo->t < shortest.t ) + shortest = *outInfo; + } + } + + *outInfo = shortest; + + return ( outInfo->t < F32_MAX ); +} + +bool ForestCell::castRay( const Point3F &start, const Point3F &end, RayInfo *outInfo, bool rendered ) const +{ + // JCF: start/end are in object space of the Forest but mBounds + // is in world space... Luckily Forest is global bounds and always at the origin + // with no scale. + if ( !mBounds.collideLine( start, end ) ) + return false; + + RayInfo shortest; + shortest.userData = outInfo->userData; + shortest.t = F32_MAX; + + if ( !isLeaf() ) + { + for ( U32 i=0; i < 4; i++ ) + { + if ( mSubCells[i]->castRay( start, end, outInfo, rendered ) ) + { + if ( outInfo->t < shortest.t ) + shortest = *outInfo; + } + } + } + else + { + for ( U32 i = 0; i < mItems.size(); i++ ) + { + if ( mItems[i].castRay( start, end, outInfo, rendered ) ) + { + if ( outInfo->t < shortest.t ) + shortest = *outInfo; + } + } + } + + *outInfo = shortest; + + return ( outInfo->t < F32_MAX ); +} + +bool ForestItem::castRay( const Point3F &start, const Point3F &end, RayInfo *outInfo, bool rendered ) const +{ + if ( !mWorldBox.collideLine( start, end ) ) + return false; + + Point3F s, e; + MatrixF mat = getTransform(); + mat.scale( Point3F(mScale) ); + mat.inverse(); + + mat.mulP( start, &s ); + mat.mulP( end, &e ); + + TSForestItemData *data = (TSForestItemData*)mDataBlock; + TSShapeInstance *si = data->getShapeInstance(); + + if ( !si ) + return false; + + if ( rendered ) + { + // Always raycast against detail level zero + // because it makes lots of things easier. + if ( !si->castRayRendered( s, e, outInfo, 0 ) ) + return false; + + if ( outInfo->userData != NULL ) + *(ForestItem*)(outInfo->userData) = *this; + + return true; + } + + RayInfo shortest; + shortest.t = 1e8; + bool gotMatch = false; + S32 detail; + + const Vector &details = data->getLOSDetails(); + for (U32 i = 0; i < details.size(); i++) + { + detail = details[i]; + if (detail != -1) + { + //si->animate(data->mLOSDetails[i]); + + if ( si->castRayOpcode( detail, s, e, outInfo ) ) + { + if (outInfo->t < shortest.t) + { + gotMatch = true; + shortest = *outInfo; + } + } + } + } + + if ( !gotMatch ) + return false; + + // Copy out the shortest time... + *outInfo = shortest; + + if ( outInfo->userData != NULL ) + *(ForestItem*)(outInfo->userData) = *this; + + return true; +} \ No newline at end of file diff --git a/Engine/source/forest/forestCollision.h b/Engine/source/forest/forestCollision.h new file mode 100644 index 000000000..f750fc85b --- /dev/null +++ b/Engine/source/forest/forestCollision.h @@ -0,0 +1,88 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FORESTCOLLISION_H_ +#define _FORESTCOLLISION_H_ + +#ifndef _CONVEX_H_ +#include "collision/convex.h" +#endif +#ifndef _COLLISION_H_ +#include "collision/collision.h" +#endif + + +class Forest; +class ForestItem; +class TSForestItemData; + + +class ForestConvex : public Convex +{ + typedef Convex Parent; + friend class Forest; + +public: + + ForestConvex() + { + mType = ForestConvexType; + mTransform.identity(); + } + + ForestConvex( const ForestConvex &cv ) + { + mType = ForestConvexType; + mObject = cv.mObject; + mForestItemKey = cv.mForestItemKey; + mTransform = cv.mTransform; + mData = cv.mData; + mScale = cv.mScale; + hullId = cv.hullId; + box = box; + } + + void calculateTransform( const MatrixF &worldXfrm ); + const MatrixF& getTransform() const { return mTransform; } + Box3F getBoundingBox() const; + Box3F getBoundingBox( const MatrixF &mat, const Point3F &scale) const; + Point3F support( const VectorF &v ) const; + void getFeatures( const MatrixF &mat, const VectorF &n, ConvexFeature *cf ); + void getPolyList( AbstractPolyList *list); + +public: + + U32 hullId; + Box3F box; + +protected: + + // JCF: ForestItemKey is a U32, didn't want to include forest.h here + // fix me if ForestItemKey is changed to a class. + U32 mForestItemKey; + MatrixF mTransform; + TSForestItemData *mData; + F32 mScale; +}; + + +#endif // _FORESTCOLLISION_H_ \ No newline at end of file diff --git a/Engine/source/forest/forestDataFile.cpp b/Engine/source/forest/forestDataFile.cpp new file mode 100644 index 000000000..a6c6d9062 --- /dev/null +++ b/Engine/source/forest/forestDataFile.cpp @@ -0,0 +1,834 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "forest/forestDataFile.h" + +#include "forest/forest.h" +#include "forest/forestCell.h" +#include "T3D/physics/physicsBody.h" +#include "core/stream/fileStream.h" +#include "core/resource.h" +#include "math/mathIO.h" +#include "math/mPoint2.h" +#include "platform/profiler.h" + + +template<> ResourceBase::Signature Resource::signature() +{ + return MakeFourCC('f','k','d','f'); +} + +template<> +void* Resource::create( const Torque::Path &path ) +{ + FileStream stream; + stream.open( path.getFullPath(), Torque::FS::File::Read ); + if ( stream.getStatus() != Stream::Ok ) + return NULL; + + ForestData *file = new ForestData(); + if ( !file->read( stream ) ) + { + delete file; + return NULL; + } + + return file; +} + + +U32 ForestData::smNextItemId = 1; + +ForestData::ForestData() + : mIsDirty( false ) +{ + ForestItemData::getReloadSignal().notify( this, &ForestData::_onItemReload ); +} + +ForestData::~ForestData() +{ + ForestItemData::getReloadSignal().remove( this, &ForestData::_onItemReload ); + clear(); +} + +void ForestData::clear() +{ + // We only have to delete the top level cells and ForestCell will + // clean up its sub-cells in its destructor. + + BucketTable::Iterator iter = mBuckets.begin(); + for ( ; iter != mBuckets.end(); iter++ ) delete iter->value; + mBuckets.clear(); + + mIsDirty = true; +} + +bool ForestData::read( Stream &stream ) +{ + // Read our identifier... so we know we're + // not reading in pure garbage. + char id[4] = { 0 }; + stream.read( 4, id ); + if ( dMemcmp( id, "FKDF", 4 ) != 0 ) + { + Con::errorf( "ForestDataFile::read() - This is not a Forest planting file!" ); + return false; + } + + // Empty ourselves before we really begin reading. + clear(); + + // Now the version number. + U8 version; + stream.read( &version ); + if ( version > (U8)FILE_VERSION ) + { + Con::errorf( "ForestDataFile::read() - This file was created with an newer version of Forest!" ); + return false; + } + + // Read in the names of the ForestItemData datablocks + // and recover the datablock. + Vector allDatablocks; + U32 count; + stream.read( &count ); + allDatablocks.setSize( count ); + for ( U32 i=0; i < count; i++ ) + { + StringTableEntry name = stream.readSTString(); + ForestItemData* data = ForestItemData::find( name ); + + // TODO: Change this to instead create a dummy forest data + // for each so that the user can swap it with the right one. + if ( data == NULL ) + { + Con::warnf( "ForestData::read - ForestItemData named %s was not found.", name ); + Con::warnf( "Note this can occur if you have deleted or renamed datablocks prior to loading this forest and is not an 'error' in this scenario." ); + } + + allDatablocks[ i ] = data; + } + + U8 dataIndex; + Point3F pos; + QuatF rot; + F32 scale; + ForestItemData* data; + MatrixF xfm; + + U32 skippedItems = 0; + + // Read in the items. + stream.read( &count ); + for ( U32 i=0; i < count; i++ ) + { + stream.read( &dataIndex ); + mathRead( stream, &pos ); + mathRead( stream, &rot ); + stream.read( &scale ); + + data = allDatablocks[ dataIndex ]; + if ( data ) + { + rot.setMatrix( &xfm ); + xfm.setPosition( pos ); + + addItem( smNextItemId++, data, xfm, scale ); + } + else + { + skippedItems++; + } + } + + if ( skippedItems > 0 ) + Con::warnf( "ForestData::read - %i items were skipped because their datablocks were not found.", skippedItems ); + + // Clear the dirty flag. + mIsDirty = false; + + return true; +} + +bool ForestData::write( const char *path ) +{ + // Open the stream. + FileStream stream; + if ( !stream.open( path, Torque::FS::File::Write ) ) + { + Con::errorf( "ForestDataFile::write() - Failed opening stream!" ); + return false; + } + + // Write our identifier... so we have a better + // idea if we're reading pure garbage. + stream.write( 4, "FKDF" ); + + // Now the version number. + stream.write( (U8)FILE_VERSION ); + + // First gather all the ForestItemData datablocks + // used by the items in the forest. + Vector allDatablocks; + getDatablocks( &allDatablocks ); + + // Write out the datablock list. + U32 count = allDatablocks.size(); + stream.write( count ); + for ( U32 i=0; i < count; i++ ) + { + StringTableEntry localName = allDatablocks[i]->getInternalName(); + AssertFatal( localName != NULL && localName[0] != '\0', "ForestData::write - ForestItemData had no internal name set!" ); + stream.writeString( allDatablocks[i]->getInternalName() ); + } + + // Get a copy of all the items. + Vector items; + getItems( &items ); + + // Save the item count. + stream.write( (U32)items.size() ); + + // Save the items. + Vector::const_iterator iter = items.begin(); + for ( ; iter != items.end(); iter++ ) + { + U8 dataIndex = find( allDatablocks.begin(), allDatablocks.end(), iter->getData() ) - allDatablocks.begin(); + + stream.write( dataIndex ); + + mathWrite( stream, iter->getPosition() ); + + QuatF quat; + quat.set( iter->getTransform() ); + mathWrite( stream, quat ); + + stream.write( iter->getScale() ); + } + + // Clear the dirty flag. + mIsDirty = false; + + return true; +} + +void ForestData::regenCells() +{ + Vector items; + getItems( &items ); + + clear(); + + for ( U32 i=0; i < items.size(); i++ ) + { + const ForestItem &item = items[i]; + addItem( item.getKey(), item.getData(), item.getTransform(), item.getScale() ); + } + + mIsDirty = true; +} + +ForestCell* ForestData::_findBucket( const Point2I &key ) const +{ + BucketTable::ConstIterator iter = mBuckets.find( key ); + + if ( iter != mBuckets.end() ) + return iter->value; + else + return NULL; +} + +ForestCell* ForestData::_findOrCreateBucket( const Point3F &pos ) +{ + // Look it up. + const Point2I key = _getBucketKey( pos ); + BucketTable::Iterator iter = mBuckets.find( key ); + + ForestCell *bucket = NULL; + if ( iter != mBuckets.end() ) + bucket = iter->value; + else + { + bucket = new ForestCell( RectF( key.x, key.y, BUCKET_DIM, BUCKET_DIM ) ); + mBuckets.insertUnique( key, bucket ); + mIsDirty = true; + } + + return bucket; +} + +void ForestData::_onItemReload() +{ + // Invalidate cell batches and bounds so they + // will be regenerated next render. + + Vector stack; + getCells( &stack ); + + ForestCell *pCell; + + while ( !stack.empty() ) + { + pCell = stack.last(); + stack.pop_back(); + + if ( !pCell ) + continue; + + pCell->freeBatches(); + pCell->invalidateBounds(); + + pCell->getChildren( &stack ); + } +} + +const ForestItem& ForestData::addItem( ForestItemData *data, + const Point3F &position, + F32 rotation, + F32 scale ) +{ + MatrixF xfm; + xfm.set( EulerF( 0, 0, rotation ), position ); + + return addItem( smNextItemId++, + data, + xfm, + scale ); +} + +const ForestItem& ForestData::addItem( ForestItemKey key, + ForestItemData *data, + const MatrixF &xfm, + F32 scale ) +{ + ForestCell *bucket = _findOrCreateBucket( xfm.getPosition() ); + + mIsDirty = true; + + return bucket->insertItem( key, data, xfm, scale ); +} + +const ForestItem& ForestData::updateItem( ForestItemKey key, + const Point3F &keyPosition, + ForestItemData *newData, + const MatrixF &newXfm, + F32 newScale ) +{ + Point2I bucketKey = _getBucketKey( keyPosition ); + + ForestCell *bucket = _findBucket( bucketKey ); + + if ( !bucket || !bucket->removeItem( key, keyPosition, true ) ) + return ForestItem::Invalid; + + if ( bucket->isEmpty() ) + { + delete bucket; + mBuckets.erase( bucketKey ); + } + + return addItem( key, newData, newXfm, newScale ); +} + +const ForestItem& ForestData::updateItem( ForestItem &item ) +{ + return updateItem( item.getKey(), + item.getPosition(), + item.getData(), + item.getTransform(), + item.getScale() ); +} + +bool ForestData::removeItem( ForestItemKey key, const Point3F &keyPosition ) +{ + Point2I bucketkey = _getBucketKey( keyPosition ); + + ForestCell *bucket = _findBucket( keyPosition ); + + if ( !bucket || !bucket->removeItem( key, keyPosition, true ) ) + return false; + + if ( bucket->isEmpty() ) + { + delete bucket; + mBuckets.erase( bucketkey ); + } + + mIsDirty = true; + + return true; +} + +const ForestItem& ForestData::findItem( ForestItemKey key, const Point3F &keyPos ) const +{ + PROFILE_SCOPE( ForestData_findItem ); + + AssertFatal( key != 0, "ForestCell::findItem() - Got null key!" ); + + ForestCell *cell = _findBucket( keyPos ); + + while ( cell && !cell->isLeaf() ) + cell = cell->getChildAt( keyPos ); + + U32 index; + if ( cell && cell->findIndexByKey( key, &index ) ) + return cell->getItems()[ index ]; + + return ForestItem::Invalid; +} + +const ForestItem& ForestData::findItem( ForestItemKey key ) const +{ + PROFILE_SCOPE( ForestData_findItem_Slow ); + + AssertFatal( key != 0, "ForestData::findItem() - Got null key!" ); + + // Do an exhaustive search thru all the cells... this + // is really crappy... we shouldn't do this regularly. + + Vector stack; + BucketTable::ConstIterator iter = mBuckets.begin(); + for ( ; iter != mBuckets.end(); iter++ ) + stack.push_back( iter->value ); + + // Now loop till we run out of cells. + while ( !stack.empty() ) + { + // Pop off the next cell. + const ForestCell *cell = stack.last(); + stack.pop_back(); + + // Recurse thru non-leaf cells. + if ( !cell->isLeaf() ) + { + cell->getChildren( &stack ); + continue; + } + + // Finally search for the item. + U32 index; + if ( cell->findIndexByKey( key, &index ) ) + return cell->getItems()[ index ]; + } + + return ForestItem::Invalid; +} + +U32 ForestData::getItems( Vector *outItems ) const +{ + AssertFatal( outItems, "ForestData::getItems() - The output vector was NULL!" ); + + PROFILE_SCOPE( ForestData_getItems ); + + Vector stack; + U32 count = 0; + + BucketTable::ConstIterator iter = mBuckets.begin(); + for ( ; iter != mBuckets.end(); iter++ ) + stack.push_back( iter->value ); + + // Now loop till we run out of cells. + while ( !stack.empty() ) + { + // Pop off the next cell. + const ForestCell *cell = stack.last(); + stack.pop_back(); + + // Recurse thru non-leaf cells. + if ( !cell->isLeaf() ) + { + cell->getChildren( &stack ); + continue; + } + + // Get the items. + count += cell->getItems().size(); + outItems->merge( cell->getItems() ); + } + + return count; +} + +U32 ForestData::getItems( const Frustum &culler, Vector *outItems ) const +{ + AssertFatal( outItems, "ForestData::getItems() - The output vector was NULL!" ); + + PROFILE_SCOPE( ForestData_getItems_ByFrustum ); + + Vector stack; + getCells( &stack ); + Vector::const_iterator iter; + U32 count = 0; + + // Now loop till we run out of cells. + while ( !stack.empty() ) + { + // Pop off the next cell. + const ForestCell *cell = stack.last(); + stack.pop_back(); + + if ( culler.isCulled( cell->getBounds() ) ) + continue; + + // Recurse thru non-leaf cells. + if ( cell->isBranch() ) + { + cell->getChildren( &stack ); + continue; + } + + // Get the items. + iter = cell->getItems().begin(); + for ( ; iter != cell->getItems().end(); iter++ ) + { + if ( !culler.isCulled( iter->getWorldBox() ) ) + { + outItems->merge( cell->getItems() ); + count++; + } + } + } + + return count; +} + +U32 ForestData::getItems( const Box3F &box, Vector *outItems ) const +{ + PROFILE_SCOPE( ForestData_getItems_ByBox ); + + Vector stack; + U32 count = 0; + + BucketTable::ConstIterator iter = mBuckets.begin(); + for ( ; iter != mBuckets.end(); iter++ ) + stack.push_back( iter->value ); + + // Now loop till we run out of cells. + while ( !stack.empty() ) + { + // Pop off the next cell. + const ForestCell *cell = stack.last(); + stack.pop_back(); + + // If the cell is empty or doesn't overlap the box... skip it. + if ( cell->isEmpty() || + !cell->getBounds().isOverlapped( box ) ) + continue; + + // Recurse thru non-leaf cells. + if ( !cell->isLeaf() ) + { + cell->getChildren( &stack ); + continue; + } + + // Finally look thru the items. + const Vector &items = cell->getItems(); + Vector::const_iterator item = items.begin(); + for ( ; item != items.end(); item++ ) + { + if ( item->getWorldBox().isOverlapped( box ) ) + { + // If we don't have an output vector then the user just + // wanted to know if any object existed... so early out. + if ( !outItems ) + return 1; + + ++count; + outItems->push_back( *item ); + } + } + } + + return count; +} + +U32 ForestData::getItems( const Point3F &point, F32 radius, Vector *outItems ) const +{ + PROFILE_SCOPE( ForestData_getItems_BySphere ); + + Vector stack; + U32 count = 0; + + BucketTable::ConstIterator iter = mBuckets.begin(); + for ( ; iter != mBuckets.end(); iter++ ) + stack.push_back( iter->value ); + + const F32 radiusSq = radius * radius; + + // Now loop till we run out of cells. + while ( !stack.empty() ) + { + // Pop off the next cell. + const ForestCell *cell = stack.last(); + stack.pop_back(); + + // TODO: If we could know here that the cell is fully within + // the sphere... we could do a fast gather of all its elements + // without any further testing of it or its children. + + // If the cell is empty or doesn't overlap the sphere... skip it. + if ( cell->isEmpty() || + cell->getBounds().getSqDistanceToPoint( point ) > radiusSq ) + continue; + + // Recurse thru non-leaf cells. + if ( !cell->isLeaf() ) + { + cell->getChildren( &stack ); + continue; + } + + // Finally look thru the items. + const Vector &items = cell->getItems(); + Vector::const_iterator item = items.begin(); + for ( ; item != items.end(); item++ ) + { + if ( item->getWorldBox().getSqDistanceToPoint( point ) < radiusSq ) + { + // If we don't have an output vector then the user just + // wanted to know if any object existed... so early out. + if ( !outItems ) + return 1; + + ++count; + outItems->push_back( *item ); + } + } + } + + return count; +} + + +U32 ForestData::getItems( const Point2F &point, F32 radius, Vector *outItems ) const +{ + PROFILE_SCOPE( ForestData_getItems_ByCircle ); + + Vector stack; + U32 count = 0; + + BucketTable::ConstIterator iter = mBuckets.begin(); + for ( ; iter != mBuckets.end(); iter++ ) + stack.push_back( iter->value ); + + const F32 radiusSq = radius * radius; + + // Now loop till we run out of cells. + while ( !stack.empty() ) + { + // Pop off the next cell. + const ForestCell *cell = stack.last(); + stack.pop_back(); + + // If the cell is empty or doesn't overlap the sphere... skip it. + if ( cell->isEmpty() || + cell->getRect().getSqDistanceToPoint( point ) > radiusSq ) + continue; + + // Recurse thru non-leaf cells. + if ( !cell->isLeaf() ) + { + cell->getChildren( &stack ); + continue; + } + + // Finally look thru the items. + const Vector &items = cell->getItems(); + Vector::const_iterator item = items.begin(); + F32 compareDist; + for ( ; item != items.end(); item++ ) + { + compareDist = mSquared( radius + item->getData()->mRadius ); + if ( item->getSqDistanceToPoint( point ) < compareDist ) + { + // If we don't have an output vector then the user just + // wanted to know if any object existed... so early out. + if ( !outItems ) + return 1; + + ++count; + outItems->push_back( *item ); + } + } + } + + return count; +} + +U32 ForestData::getItems( const ForestItemData *data, Vector *outItems ) const +{ + AssertFatal( outItems, "ForestData::getItems() - The output vector was NULL!" ); + + PROFILE_SCOPE( ForestData_getItems_ByDatablock ); + + Vector stack; + U32 count = 0; + + BucketTable::ConstIterator iter = mBuckets.begin(); + for ( ; iter != mBuckets.end(); iter++ ) + stack.push_back( iter->value ); + + // Now loop till we run out of cells. + while ( !stack.empty() ) + { + // Pop off the next cell. + const ForestCell *cell = stack.last(); + stack.pop_back(); + + // Recurse thru non-leaf cells. + if ( !cell->isLeaf() ) + { + cell->getChildren( &stack ); + continue; + } + + // Get the items. + const Vector &items = cell->getItems(); + Vector::const_iterator item = items.begin(); + for ( ; item != items.end(); item++ ) + { + if ( item->getData() == data ) + { + ++count; + outItems->push_back( *item ); + } + } + } + + return count; +} + +void ForestData::getCells( const Frustum &frustum, Vector *outCells ) const +{ + PROFILE_SCOPE( ForestData_getCells_frustum ); + + BucketTable::ConstIterator iter = mBuckets.begin(); + for ( ; iter != mBuckets.end(); iter++ ) + { + if ( !frustum.isCulled( iter->value->getBounds() ) ) + outCells->push_back( iter->value ); + } +} + +void ForestData::getCells( Vector *outCells ) const +{ + PROFILE_SCOPE( ForestData_getCells_nofrustum ); + + BucketTable::ConstIterator iter = mBuckets.begin(); + for ( ; iter != mBuckets.end(); iter++ ) + outCells->push_back( iter->value ); +} + +U32 ForestData::getDatablocks( Vector *outVector ) const +{ + Vector stack; + U32 count = 0; + + BucketTable::ConstIterator iter = mBuckets.begin(); + for ( ; iter != mBuckets.end(); iter++ ) + stack.push_back( iter->value ); + + // Now loop till we run out of cells. + while ( !stack.empty() ) + { + // Pop off the next cell. + const ForestCell *cell = stack.last(); + stack.pop_back(); + + // Recurse thru non-leaf cells. + if ( !cell->isLeaf() ) + { + cell->getChildren( &stack ); + continue; + } + + // Go thru the items. + const Vector &items = cell->getItems(); + Vector::const_iterator item = items.begin(); + for ( ; item != items.end(); item++ ) + { + ForestItemData *data = item->getData(); + + if ( find( outVector->begin(), outVector->end(), data ) != outVector->end() ) + continue; + + count++; + outVector->push_back( data ); + } + } + + return count; +} + +void ForestData::clearPhysicsRep( Forest *forest ) +{ + Vector stack; + + BucketTable::Iterator iter = mBuckets.begin(); + for ( ; iter != mBuckets.end(); iter++ ) + stack.push_back( iter->value ); + + // Now loop till we run out of cells. + while ( !stack.empty() ) + { + // Pop off the next cell. + ForestCell *cell = stack.last(); + stack.pop_back(); + + // Recurse thru non-leaf cells. + if ( !cell->isLeaf() ) + { + cell->getChildren( &stack ); + continue; + } + + cell->clearPhysicsRep( forest ); + } +} + +void ForestData::buildPhysicsRep( Forest *forest ) +{ + Vector stack; + + BucketTable::Iterator iter = mBuckets.begin(); + for ( ; iter != mBuckets.end(); iter++ ) + stack.push_back( iter->value ); + + // Now loop till we run out of cells. + while ( !stack.empty() ) + { + // Pop off the next cell. + ForestCell *cell = stack.last(); + stack.pop_back(); + + // Recurse thru non-leaf cells. + if ( !cell->isLeaf() ) + { + cell->getChildren( &stack ); + continue; + } + + cell->buildPhysicsRep( forest ); + } +} \ No newline at end of file diff --git a/Engine/source/forest/forestDataFile.h b/Engine/source/forest/forestDataFile.h new file mode 100644 index 000000000..0104a4d9e --- /dev/null +++ b/Engine/source/forest/forestDataFile.h @@ -0,0 +1,212 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FORESTDATAFILE_H_ +#define _FORESTDATAFILE_H_ + +#ifndef _FORESTITEM_H_ +#include "forest/forestItem.h" +#endif +#ifndef _TDICTIONARY_H_ +#include "core/util/tDictionary.h" +#endif + +class ForestCell; +class Forest; +class Frustum; + + +/// This is the data file for Forests. +class ForestData +{ + protected: + + enum { FILE_VERSION = 1 }; + + /// Set the bucket dimensions to 2km x 2km. + static const U32 BUCKET_DIM = 2000; + + /// Set to true if the file is dirty and + /// needs to be saved before being destroyed. + bool mIsDirty; + + /// + typedef HashTable BucketTable; + + /// The top level cell buckets which allows us + /// to have virtually unbounded range. + BucketTable mBuckets; + + /// The next free item id. + static U32 smNextItemId; + + /// Converts a ForestItem's Point3F 'KeyPosition' to a Point2I + /// key we index into BucketTable with. + static Point2I _getBucketKey( const Point3F &pos ); + + /// Finds the bucket with the given Point2I key or returns NULL. + ForestCell* _findBucket( const Point2I &key ) const; + + /// Finds the best top level bucket for the ForestItem 'key' position + /// or returns NULL. + ForestCell* _findBucket( const Point3F &pos ) const; + + /// Find the best top level bucket for the given position + /// or returns a new one. + ForestCell* _findOrCreateBucket( const Point3F &pos ); + + void _onItemReload(); + + + public: + + ForestData(); + virtual ~ForestData(); + + bool isDirty() const { return mIsDirty; } + + /// Deletes all the data and resets the + /// file to an empty state. + void clear(); + + /// Helper for debugging cell generation. + void regenCells(); + + /// + bool read( Stream &stream ); + + /// + bool write( const char *path ); + + const ForestItem& addItem( ForestItemData *data, + const Point3F &position, + F32 rotation, + F32 scale ); + + const ForestItem& addItem( ForestItemKey key, + ForestItemData *data, + const MatrixF &xfm, + F32 scale ); + + const ForestItem& updateItem( ForestItemKey key, + const Point3F &keyPosition, + ForestItemData *newData, + const MatrixF &newXfm, + F32 newscale ); + + const ForestItem& updateItem( ForestItem &item ); + + bool removeItem( ForestItemKey key, const Point3F &keyPosition ); + + /// Performs a search using the position to limit tested cells. + const ForestItem& findItem( ForestItemKey key, const Point3F &keyPosition ) const; + + /// Does an exhaustive search thru all cells looking for + /// the item. This method is slow and should be avoided. + const ForestItem& findItem( ForestItemKey key ) const; + + /// Fills a vector with a copy of all the items in the data set. + /// + /// @param outItems The output vector of items. + /// @return The count of items found. + /// + U32 getItems( Vector *outItems ) const; + + /// Fills a vector with a copy of all items in the Frustum. + /// Note that this IS expensive and this is not how Forest internally + /// collects items for rendering. This is here for ForestSelectionTool. + /// + /// @param The Frustum to cull with. + /// @param outItems The output vector of items. + /// @return The count of items found. + /// + U32 getItems( const Frustum &culler, Vector *outItems ) const; + + /// Returns a copy of all the items that intersect the box. If + /// the output vector is NULL then it will early out on the first + /// found item returning 1. + /// + /// @param box The search box. + /// @param outItems The output vector of items or NULL. + /// @return The count of items found. + /// + U32 getItems( const Box3F &box, Vector *outItems ) const; + + /// Returns a copy of all the items that intersect the sphere. If + /// the output vector is NULL then it will early out on the first + /// found item returning 1. + /// + /// @param point The center of the search sphere. + /// @param radius The radius of the search sphere. + /// @param outItems The output vector of items or NULL. + /// @return The count of items found. + /// + U32 getItems( const Point3F &point, F32 radius, Vector *outItems ) const; + + /// Returns a copy of all the items that intersect the 2D circle ignoring + /// the z component. If the output vector is NULL then it will early out + /// on the first found item returning 1. + /// + /// @param point The center point of the search circle. + /// @param radius The radius of the search circle. + /// @param outItems The output vector of items or NULL. + /// @return The count of items found. + /// + U32 getItems( const Point2F &point, F32 radius, Vector *outItems ) const; + + /// Returns a copy of all the items that share the input item datablock. + /// + /// @param data The datablock to search for. + /// @param outItems The output vector of items. + /// @return The count of items found. + /// + U32 getItems( const ForestItemData *data, Vector *outItems ) const; + + /// Returns all the top level cells which intersect the frustum. + void getCells( const Frustum &frustum, Vector *outCells ) const; + + /// Returns all top level cells. + void getCells( Vector *outCells ) const; + + /// Gathers all the datablocks used and returns the count. + U32 getDatablocks( Vector *outVector ) const; + + /// + bool castRay( const Point3F &start, const Point3F &end, RayInfo *outInfo, bool rendered ) const; + + /// + void clearPhysicsRep( Forest *forest ); + void buildPhysicsRep( Forest *forest ); +}; + +inline Point2I ForestData::_getBucketKey( const Point3F &pos ) +{ + return Point2I ( (S32)mFloor(pos.x / BUCKET_DIM) * BUCKET_DIM, + (S32)mFloor(pos.y / BUCKET_DIM) * BUCKET_DIM ); +} + +inline ForestCell* ForestData::_findBucket( const Point3F &pos ) const +{ + return _findBucket( _getBucketKey( pos ) ); +} + +#endif // _FORESTDATAFILE_H_ diff --git a/Engine/source/forest/forestItem.cpp b/Engine/source/forest/forestItem.cpp new file mode 100644 index 000000000..a38194b42 --- /dev/null +++ b/Engine/source/forest/forestItem.cpp @@ -0,0 +1,249 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "forest/forestItem.h" + +#include "forest/forestCollision.h" + +#include "core/stream/bitStream.h" +#include "console/consoleTypes.h" + +IMPLEMENT_CO_DATABLOCK_V1(ForestItemData); + +ConsoleDocClass( ForestItemData, + "@brief Base class for defining a type of ForestItem. It does not implement " + "loading or rendering of the shapeFile.\n\n" + "@ingroup Forest" +); + +SimSet* ForestItemData::smSet = NULL; + + +ForestItemData::ForestItemData() + : mNeedPreload( true ), + mShapeFile( NULL ), + mCollidable( true ), + mRadius( 1 ), + mWindScale( 0.0f ), + mTrunkBendScale( 0.0f ), + mWindBranchAmp( 0.0f ), + mWindDetailAmp( 0.0f ), + mWindDetailFreq( 0.0f ), + mMass( 5.0f ), + mRigidity( 10.0f ), + mTightnessCoefficient( 0.4f ), + mDampingCoefficient( 0.7f ) +{ +} + +void ForestItemData::initPersistFields() +{ + Parent::initPersistFields(); + + addGroup( "Media" ); + + addField( "shapeFile", TypeShapeFilename, Offset( mShapeFile, ForestItemData ), + "Shape file for this item type" ); + + addField( "collidable", TypeBool, Offset( mCollidable, ForestItemData ), + "Can other objects or spacial queries hit items of this type." ); + + addField( "radius", TypeF32, Offset( mRadius, ForestItemData ), + "Radius used during placement to ensure items are not crowded." ); + + endGroup( "Media" ); + + addGroup( "Wind" ); + + addField( "mass", TypeF32, Offset( mMass, ForestItemData ), + "Mass used in calculating spring forces on the trunk. Generally how " + "springy a plant is." ); + + addField( "rigidity", TypeF32, Offset( mRigidity, ForestItemData ), + "Rigidity used in calculating spring forces on the trunk. How much the plant resists the wind force" ); + + addField( "tightnessCoefficient", TypeF32, Offset( mTightnessCoefficient, ForestItemData ), + "Coefficient used in calculating spring forces on the trunk. " + "How much the plant resists bending." ); + + addField( "dampingCoefficient", TypeF32, Offset( mDampingCoefficient, ForestItemData ), + "Coefficient used in calculating spring forces on the trunk. " + "Causes oscillation and forces to decay faster over time." ); + + addField( "windScale", TypeF32, Offset( mWindScale, ForestItemData ), + "Overall scale to the effect of wind." ); + + addField( "trunkBendScale", TypeF32, Offset( mTrunkBendScale, ForestItemData ), + "Overall bend amount of the tree trunk by wind and impacts." ); + + addField( "branchAmp", TypeF32, Offset( mWindBranchAmp, ForestItemData ), + "Amplitude of the effect on larger branches." ); + + addField( "detailAmp", TypeF32, Offset( mWindDetailAmp, ForestItemData ), + "Amplitude of the winds effect on leafs/fronds." ); + + addField( "detailFreq", TypeF32, Offset( mWindDetailFreq, ForestItemData ), + "Frequency (speed) of the effect on leafs/fronds." ); + + endGroup( "Wind" ); +} + +void ForestItemData::consoleInit() +{ +} + +SimSet* ForestItemData::getSet() +{ + if ( !smSet ) + { + if ( Sim::findObject( "ForestItemDataSet", smSet ) ) + return smSet; + + smSet = new SimSet; + smSet->assignName( "ForestItemDataSet" ); + smSet->registerObject(); + Sim::getRootGroup()->addObject( smSet ); + } + + return smSet; +} + +ForestItemData* ForestItemData::find( const char *name ) +{ + ForestItemData *result = dynamic_cast( getSet()->findObjectByInternalName( name ) ); + if ( !result ) + Sim::findObject( name, result ); + + return result; +} + +void ForestItemData::onNameChange( const char *name ) +{ + setInternalName( name ); +} + +bool ForestItemData::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + getSet()->addObject( this ); + + return true; +} + +void ForestItemData::packData(BitStream* stream) +{ + Parent::packData(stream); + + String localName = getInternalName(); + if ( localName.isEmpty() ) + localName = getName(); + + stream->write( localName ); + + stream->writeString(mShapeFile); + + stream->writeFlag( mCollidable ); + + stream->write( mRadius ); + + stream->write( mMass ); + stream->write( mRigidity ); + stream->write( mTightnessCoefficient ); + stream->write( mDampingCoefficient ); + + stream->write( mWindScale ); + stream->write( mTrunkBendScale ); + stream->write( mWindBranchAmp ); + stream->write( mWindDetailAmp ); + stream->write( mWindDetailFreq ); +} + +void ForestItemData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + + String localName; + stream->read( &localName ); + setInternalName( localName ); + + char readBuffer[1024]; + + stream->readString(readBuffer); + mShapeFile = StringTable->insert(readBuffer); + + mCollidable = stream->readFlag(); + + stream->read( &mRadius ); + + stream->read( &mMass ); + stream->read( &mRigidity ); + stream->read( &mTightnessCoefficient ); + stream->read( &mDampingCoefficient ); + + stream->read( &mWindScale ); + stream->read( &mTrunkBendScale ); + stream->read( &mWindBranchAmp ); + stream->read( &mWindDetailAmp ); + stream->read( &mWindDetailFreq ); +} + +const ForestItem ForestItem::Invalid; + +ForestItem::ForestItem() + : mDataBlock( NULL ), + mTransform( true ), + mScale( 0.0f ), + mWorldBox( 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f ), + mRadius( 0.0f ), + mKey( 0 ) +{ +} + +ForestItem::~ForestItem() +{ +} + +void ForestItem::setTransform( const MatrixF &xfm, F32 scale ) +{ + mTransform = xfm; + mScale = scale; + + // Cache the world box to improve culling performance. + VectorF objScale( mScale, mScale, mScale ); + mWorldBox = getObjBox(); + mWorldBox.minExtents.convolve( objScale ); + mWorldBox.maxExtents.convolve( objScale ); + mTransform.mul( mWorldBox ); + + // Generate a radius that encompasses the entire box. + mRadius = ( mWorldBox.maxExtents - mWorldBox.minExtents ).len() / 2.0f; +} + +void ForestItem::setData( ForestItemData *data ) +{ + mDataBlock = data; +} + + diff --git a/Engine/source/forest/forestItem.h b/Engine/source/forest/forestItem.h new file mode 100644 index 000000000..c5a0fe374 --- /dev/null +++ b/Engine/source/forest/forestItem.h @@ -0,0 +1,274 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FORESTITEM_H_ +#define _FORESTITEM_H_ + +#ifndef _MMATH_H_ +#include "math/mMath.h" +#endif +#ifndef _SIMBASE_H_ +#include "console/simBase.h" +#endif +#ifndef _DYNAMIC_CONSOLETYPES_H_ +#include "console/dynamicTypes.h" +#endif + + +class ForestItem; +class ForestCellBatch; +class ForestConvex; +class ForestWindAccumulator; + +class Box3F; +class Point3F; +class TSRenderState; +class SceneRenderState; +struct RayInfo; +class AbstractPolyList; + + +class ForestItemData : public SimDataBlock +{ +protected: + + typedef SimDataBlock Parent; + + static SimSet* smSet; + + bool mNeedPreload; + + virtual void _preload() {} + +public: + + /// Shape file for this item type. + StringTableEntry mShapeFile; + + /// This is the radius used during placement to ensure + /// the element isn't crowded up against other trees. + F32 mRadius; + + /// Overall scale to the effect of wind. + F32 mWindScale; + + /// This is used to control the overall bend amount of the tree by wind and impacts. + F32 mTrunkBendScale; + + /// Amplitude of the effect on larger branches. + F32 mWindBranchAmp; + + /// Amplitude of the winds effect on leafs/fronds. + F32 mWindDetailAmp; + + /// Frequency (speed) of the effect on leafs/fronds. + F32 mWindDetailFreq; + + /// Mass used in calculating spring forces. + F32 mMass; + + // The rigidity of the tree's trunk. + F32 mRigidity; + + // The tightness coefficient of the spring for this tree type's ForestWindAccumulator. + F32 mTightnessCoefficient; + + // The damping coefficient. + F32 mDampingCoefficient; + + /// Can other objects or spacial queries hit items of this type. + bool mCollidable; + + + static SimSet* getSet(); + static ForestItemData* find( const char *name ); + + ForestItemData(); + virtual ~ForestItemData() {} + + DECLARE_CONOBJECT( ForestItemData ); + + static void consoleInit(); + static void initPersistFields(); + + virtual void onNameChange(const char *name); + virtual bool onAdd(); + virtual void packData(BitStream* stream); + virtual void unpackData(BitStream* stream); + + /// Called from Forest the first time a datablock is used + /// in order to lazy load content. + void preload() + { + if ( !mNeedPreload ) + return; + + _preload(); + mNeedPreload = false; + } + + virtual const Box3F& getObjBox() const { return Box3F::Invalid; } + + virtual bool render( TSRenderState *rdata, const ForestItem &item ) const { return false; } + + virtual bool canBillboard( const SceneRenderState *state, const ForestItem &item, F32 distToCamera ) const { return false; } + + virtual ForestCellBatch* allocateBatch() const { return NULL; } + + typedef Signal ReloadSignal; + + static ReloadSignal& getReloadSignal() + { + static ReloadSignal theSignal; + return theSignal; + } +}; + +typedef Vector ForestItemDataVector; + + + + +typedef U32 ForestItemKey; + + +class ForestItem +{ +protected: + + ForestItemData *mDataBlock; + + // The unique identifier used when querying forest items. + ForestItemKey mKey; + + MatrixF mTransform; + + F32 mScale; + + F32 mRadius; + + Box3F mWorldBox; + + // JCFHACK: change this to an abstract physics-rep. + //NxActor *mActor; + + /// If we're currently being effected by one or + /// more wind emitters then we hold the results + /// in this class. + //ForestWindAccumulator *mWind; + +public: + + // Constructs an invalid item. + ForestItem(); + + // Note: We keep this non-virtual to save vtable space. + ~ForestItem(); + + static const ForestItem Invalid; + + // Comparison operators with other ForestItems. + // Note that this compares only the ForestItemKey, we are not validating + // that any other data like the position actually matches. + bool operator==(const ForestItem&) const; + bool operator!=(const ForestItem&) const; + + /// Returns true if this is a valid item. + bool isValid() const { return mKey != 0; } + + /// Invalidates the item. + void makeInvalid() { mKey = 0; } + + const ForestItemKey& getKey() const { return mKey; }; + + void setKey( const ForestItemKey &key ) { mKey = key; } + + Point3F getPosition() const { return mTransform.getPosition(); } + + const MatrixF& getTransform() const { return mTransform; } + + F32 getScale() const { return mScale; } + + F32 getRadius() const { return mRadius; } + + Point3F getCenterPoint() const { return mWorldBox.getCenter(); } + + void setTransform( const MatrixF &xfm, F32 scale ); + + F32 getSqDistanceToPoint( const Point2F &point ) const; + + void setData( ForestItemData *data ); + + const Box3F& getObjBox() const { return mDataBlock->getObjBox(); } + + const Box3F& getWorldBox() const { return mWorldBox; } + + Point3F getSize() const + { + if ( !mDataBlock ) + return Point3F::One; + + Box3F size = mDataBlock->getObjBox(); + size.scale( mScale ); + return size.getExtents(); + } + + ForestItemData* getData() const { return mDataBlock; }; + + inline bool canBillboard( const SceneRenderState *state, F32 distToCamera ) const + { + return mDataBlock && mDataBlock->canBillboard( state, *this, distToCamera ); + } + + /// Collision + /// @{ + + bool castRay( const Point3F &start, const Point3F &end, RayInfo *outInfo, bool rendered ) const; + + bool buildPolyList( AbstractPolyList *polyList, const Box3F &box, const SphereF &sphere ) const; + + //ForestConvex* buildConvex( const Box3F &box, Convex *convex ) const; + + /// @} + +}; + +typedef Vector ForestItemVector; + + +inline F32 ForestItem::getSqDistanceToPoint( const Point2F &point ) const +{ + return ( getPosition().asPoint2F() - point ).lenSquared(); +} + +inline bool ForestItem::operator==(const ForestItem& _test) const +{ + return mKey == _test.mKey; +} + +inline bool ForestItem::operator!=(const ForestItem& _test) const +{ + return mKey != _test.mKey; +} + + +#endif // _FORESTITEM_H_ diff --git a/Engine/source/forest/forestRender.cpp b/Engine/source/forest/forestRender.cpp new file mode 100644 index 000000000..0c7a3fb91 --- /dev/null +++ b/Engine/source/forest/forestRender.cpp @@ -0,0 +1,319 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "forest/forest.h" +#include "forest/forestCell.h" +#include "forest/forestDataFile.h" + +#include "gfx/gfxTransformSaver.h" +#include "renderInstance/renderPassManager.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "lighting/lightManager.h" +#include "ts/tsMesh.h" +#include "ts/tsRenderState.h" +#include "ts/tsShapeInstance.h" + +#include "gfx/primBuilder.h" +#include "gfx/gfxDrawUtil.h" +#include "math/mathUtils.h" + + +U32 Forest::smTotalCells = 0; +U32 Forest::smCellsRendered = 0; +U32 Forest::smCellItemsRendered = 0; +U32 Forest::smCellsBatched = 0; +U32 Forest::smCellItemsBatched = 0; +F32 Forest::smAverageItemsPerCell = 0.0f; + +void Forest::_clearStats(bool beginFrame) +{ + // Reset the rendering stats! + if (beginFrame) + { + smTotalCells = 0; + smCellsRendered = 0; + smCellItemsRendered = 0; + smCellsBatched = 0; + smCellItemsBatched = 0; + smAverageItemsPerCell = 0.0f; + } +} + +void Forest::prepRenderImage( SceneRenderState *state ) +{ + PROFILE_SCOPE(Forest_RenderCells); + + // TODO: Fix stats. + /* + ForestCellVector &theCells = mData->getCells(); + smTotalCells += theCells.size(); + + // Don't render if we don't have a grid! + if ( theCells.empty() ) + return false; + */ + + // Prepare to render. + GFXTransformSaver saver; + + // Figure out the grid range in the viewing area. + const bool isReflectPass = state->isReflectPass(); + + const F32 cullScale = isReflectPass ? mReflectionLodScalar : 1.0f; + + // If we need to update our cached + // zone state then do it now. + if ( mZoningDirty ) + { + mZoningDirty = false; + + Vector cells; + mData->getCells( &cells ); + for ( U32 i=0; i < cells.size(); i++ ) + cells[i]->_updateZoning( getSceneManager()->getZoneManager() ); + } + + // TODO: Move these into the TSForestItemData as something we + // setup once and don't do per-instance. + + // Set up the TS render state. + TSRenderState rdata; + rdata.setSceneState( state ); + + // Use origin sort on all forest elements as + // its alot cheaper than the bounds sort. + rdata.setOriginSort( true ); + + // We may have some forward lit materials in + // the forest, so pass down a LightQuery for it. + LightQuery lightQuery; + rdata.setLightQuery( &lightQuery ); + Frustum culler = state->getFrustum(); + + // Adjust the far distance if the cull scale has changed. + if ( !mIsEqual( cullScale, 1.0f ) ) + { + const F32 visFarDist = culler.getFarDist() * cullScale; + culler.setFarDist( visFarDist ); + } + + Box3F worldBox; + + // Used for debug drawing. + GFXDrawUtil* drawer = GFX->getDrawUtil(); + drawer->clearBitmapModulation(); + + // Go thru the visible cells. + const Box3F &cullerBounds = culler.getBounds(); + const Point3F &camPos = state->getDiffuseCameraPosition(); + + U32 clipMask; + smAverageItemsPerCell = 0.0f; + U32 cellsProcessed = 0; + ForestCell *cell; + + // First get all the top level cells which + // intersect the frustum. + Vector cellStack; + mData->getCells( culler, &cellStack ); + + // Get the culling zone state. + const BitVector &zoneState = state->getCullingState().getZoneVisibilityFlags(); + + // Now loop till we run out of cells. + while ( !cellStack.empty() ) + { + // Pop off the next cell. + cell = cellStack.last(); + cellStack.pop_back(); + + const Box3F &cellBounds = cell->getBounds(); + + // If the cell is empty or its bounds is outside the frustum + // bounds then we have nothing nothing more to do. + if ( cell->isEmpty() || !cullerBounds.isOverlapped( cellBounds ) ) + continue; + + // Can we cull this cell entirely? + clipMask = culler.testPlanes( cellBounds, Frustum::PlaneMaskAll ); + if ( clipMask == -1 ) + continue; + + // Test cell visibility for interior zones. + const bool visibleInside = !cell->getZoneOverlap().empty() ? zoneState.testAny( cell->getZoneOverlap() ) : false; + + // Test cell visibility for outdoor zone, but only + // if we need to. + bool visibleOutside = false; + if( !cell->mIsInteriorOnly && !visibleInside ) + { + U32 outdoorZone = SceneZoneSpaceManager::RootZoneId; + visibleOutside = !state->getCullingState().isCulled( cellBounds, &outdoorZone, 1 ); + } + + // Skip cell if neither visible indoors nor outdoors. + if( !visibleInside && !visibleOutside ) + continue; + + // Update the stats. + smAverageItemsPerCell += cell->getItems().size(); + ++cellsProcessed; + //if ( cell->isLeaf() ) + //++leafCellsProcessed; + + // Get the distance from the camera to the cell bounds. + F32 dist = cellBounds.getDistanceToPoint( camPos ); + + // If the largest item in the cell can be billboarded + // at the cell distance to the camera... then the whole + // cell can be billboarded. + // + if ( smForceImposters || + ( dist > 0.0f && cell->getLargestItem().canBillboard( state, dist ) ) ) + { + // If imposters are disabled then skip out. + if ( smDisableImposters ) + continue; + + PROFILE_SCOPE(Forest_RenderBatches); + + // Keep track of how many cells were batched. + ++smCellsBatched; + + // Ok... everything in this cell should be batched. First + // create the batches if we don't have any. + if ( !cell->hasBatches() ) + cell->buildBatches(); + + //if ( drawCells ) + //mCellRenderFlag[ cellIter - theCells.begin() ] = 1; + + // TODO: Light queries for batches? + + // Now render the batches... we pass the culler if the + // cell wasn't fully visible so that each batch can be culled. + smCellItemsBatched += cell->renderBatches( state, clipMask != 0 ? &culler : NULL ); + continue; + } + + // If this isn't a leaf then recurse. + if ( !cell->isLeaf() ) + { + cell->getChildren( &cellStack ); + continue; + } + + // This cell has mixed billboards and mesh based items. + ++smCellsRendered; + + PROFILE_SCOPE(Forest_RenderItems); + + //if ( drawCells ) + //mCellRenderFlag[ cellIter - theCells.begin() ] = 2; + + // Use the cell bounds as the light query volume. + // + // This means all forward lit items in this cell will + // get the same lights, but it performs much better. + lightQuery.init( cellBounds ); + + // This cell is visible... have it render its items. + smCellItemsRendered += cell->render( &rdata, clipMask != 0 ? &culler : NULL ); + } + + // Keep track of the average items per cell. + if ( cellsProcessed > 0 ) + smAverageItemsPerCell /= (F32)cellsProcessed; + + // Got debug drawing to do? + if ( smDrawCells && state->isDiffusePass() ) + { + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &Forest::_renderCellBounds ); + ri->type = RenderPassManager::RIT_Editor; + state->getRenderPass()->addInst( ri ); + } +} + + +void Forest::_renderCellBounds( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ) +{ + PROFILE_SCOPE( Forest_RenderCellBounds ); + + if ( overrideMat ) + return; + + GFXTransformSaver saver; + + MatrixF projBias(true); + const Frustum frustum = GFX->getFrustum(); + MathUtils::getZBiasProjectionMatrix( 0.001f, frustum, &projBias ); + GFX->setProjectionMatrix( projBias ); + + VectorF extents; + Point3F pos; + + // Get top level cells + Vector cellStack; + mData->getCells( &cellStack ); + + // Holds child cells we need to render as we encounter them. + Vector frontier; + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + + GFXStateBlockDesc desc; + desc.setZReadWrite( true, false ); + desc.setBlend( true ); + desc.setFillModeWireframe(); + + while ( !cellStack.empty() ) + { + while ( !cellStack.empty() ) + { + const ForestCell *cell = cellStack.last(); + cellStack.pop_back(); + + Box3F box = cell->getBounds(); + + drawer->drawCube( desc, box, ColorI( 0, 255, 0 ) ); + + RectF rect = cell->getRect(); + + box.minExtents.set( rect.point.x, rect.point.y, box.minExtents.z ); + box.maxExtents.set( rect.point.x + rect.extent.x, rect.point.y + rect.extent.y, box.minExtents.z ); + + drawer->drawCube( desc, box, ColorI::RED ); + + // If this cell has children, add them to the frontier. + if ( !cell->isLeaf() ) + cell->getChildren( &frontier ); + } + + // Now the frontier becomes the cellStack and we empty the frontier. + cellStack = frontier; + frontier.clear(); + } +} diff --git a/Engine/source/forest/forestWindAccumulator.cpp b/Engine/source/forest/forestWindAccumulator.cpp new file mode 100644 index 000000000..fac6d477b --- /dev/null +++ b/Engine/source/forest/forestWindAccumulator.cpp @@ -0,0 +1,132 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "forest/forestWindAccumulator.h" + +#include "forest/forestWindMgr.h" +#include "forest/forestItem.h" +#include "platform/profiler.h" + + +ForestWindAccumulator::ForestWindAccumulator( const TreePlacementInfo &info ) +: mCurrentStrength( 0.0f ) +{ + mCurrentDir.set( 0, 0 ); + mPosition.set( info.pos ); + mScale = info.scale; + + mDataBlock = info.dataBlock; + + dMemset( &mParticles[0], 0, sizeof( VerletParticle ) ); + dMemset( &mParticles[1], 0, sizeof( VerletParticle ) ); +} + +ForestWindAccumulator::~ForestWindAccumulator() +{ +} + +void ForestWindAccumulator::presimulate( const VectorF &windVector, U32 ticks ) +{ + PROFILE_SCOPE( ForestWindAccumulator_Presimulate ); + + for ( U32 i = 0; i < ticks; i++ ) + updateWind( windVector, TickSec ); +} + +void ForestWindAccumulator::updateWind( const VectorF &windForce, F32 timeDelta ) +{ + PROFILE_SCOPE( ForestWindAccumulator_UpdateWind ); + + // Update values from datablock... this way we can + // change settings live and see instant results. + const F32 tightnessCoefficient = mDataBlock->mTightnessCoefficient; + const F32 dampingCoefficient = mDataBlock->mDampingCoefficient; + const F32 mass = mDataBlock->mMass * mScale; + const F32 rigidity = mDataBlock->mRigidity * mScale; + + // This will be the accumulated + // target strength for flutter. + //F32 targetStrength = windForce.len(); + + // This will be the accumulated + // target displacement vector. + Point2F target( windForce.x, windForce.y ); + + // This particle is the spring target. + // It has a mass of 0, which we count as + // an infinite mass. + mParticles[0].position = target; + + Point2F relVel = target * timeDelta; + + Point2F diff( 0, 0 ); + Point2F springForce( 0, 0 ); + + // Spring length is the target + // particle's position minus the + // current displacement/direction vector. + diff = mParticles[0].position - mCurrentDir; + + // F = diff * tightness - v * -damping + diff *= tightnessCoefficient; + springForce = diff - ( (mParticles[1].position - mParticles[1].lastPosition) * -dampingCoefficient ); + + Point2F accel( 0, 0 ); + accel = springForce * (rigidity * 0.001f) / (mass * 0.001f); + + _updateParticle( &mParticles[1], accel, timeDelta ); + + mCurrentDir *= 0.989f; + mCurrentDir += mParticles[1].position; + + mCurrentStrength += windForce.len() * timeDelta; + mCurrentStrength *= 0.98f; +} + +void ForestWindAccumulator::_updateParticle( VerletParticle *particle, const Point2F &accel, F32 timeDelta ) +{ + // Verlet integration: + // x' = 2x - x* + a * dt^2 + // x' is the new position. + // x is the current position. + // x* is the last position. + // a is the acceleration for this frame. + // dt is the delta time. + + particle->position = ((particle->position * 2.0f) - particle->lastPosition) + accel * (timeDelta * timeDelta); + particle->lastPosition = particle->position; +} + +void ForestWindAccumulator::applyImpulse( const VectorF &impulse ) +{ + // First build the current force. + VectorF force( mCurrentDir.x, mCurrentDir.y, 0 ); + + // Add in our mass corrected force. + const F32 mass = mDataBlock->mMass * mScale; + force += impulse / mass; + + // Set the new direction and force. + mCurrentDir.set( force.x, force.y ); + mCurrentStrength += impulse.len() * TickSec; +} diff --git a/Engine/source/forest/forestWindAccumulator.h b/Engine/source/forest/forestWindAccumulator.h new file mode 100644 index 000000000..7322f9cbc --- /dev/null +++ b/Engine/source/forest/forestWindAccumulator.h @@ -0,0 +1,79 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FORESTWINDACCUMULATOR_H_ +#define _FORESTWINDACCUMULATOR_H_ + +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif + + +struct TreePlacementInfo; +class ForestItemData; + + +/// This simple class holds the state of the accumulated +/// wind effect for a single tree. +class ForestWindAccumulator +{ +protected: + + struct VerletParticle + { + Point2F position; + Point2F lastPosition; + }; + + F32 mCurrentStrength; + Point2F mCurrentDir; + + Point3F mPosition; + F32 mScale; + ForestItemData *mDataBlock; + + VerletParticle mParticles[2]; + + void _updateParticle( VerletParticle *particle, const Point2F &force, F32 timeDelta ); + +public: + + ForestWindAccumulator( const TreePlacementInfo &info ); + ~ForestWindAccumulator(); + + void presimulate( const VectorF &windVector, U32 ticks ); + + void updateWind( const VectorF &windVector, F32 timeDelta ); + + void setDirection( const VectorF &dir ) { mCurrentDir.set( dir.x, dir.y ); } + VectorF getDirection() const { return VectorF( mCurrentDir.x, mCurrentDir.y, 0 ); } + + void setStrength( F32 strength ) { mCurrentStrength = strength; } + F32 getStrength() const { return mCurrentStrength; } + + void setPosition( const Point3F &pos ) { mPosition = pos; } + Point3F getPosition() const { return mPosition; } + + void applyImpulse( const VectorF &impulse ); +}; + +#endif // _FORESTWINDACCUMULATOR_H_ \ No newline at end of file diff --git a/Engine/source/forest/forestWindEmitter.cpp b/Engine/source/forest/forestWindEmitter.cpp new file mode 100644 index 000000000..f997dfa94 --- /dev/null +++ b/Engine/source/forest/forestWindEmitter.cpp @@ -0,0 +1,578 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "forest/forestWindEmitter.h" + +#include "forest/forestWindMgr.h" +#include "console/consoleInternal.h" +#include "core/stream/bitStream.h" +#include "core/util/safeDelete.h" +#include "platform/profiler.h" +#include "math/mathIO.h" +#include "math/mRandom.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "renderInstance/renderPassManager.h" +#include "gfx/gfxDrawUtil.h" +#include "gfx/gfxTransformSaver.h" +#include "sim/netConnection.h" +#include "T3D/gameBase/processList.h" +#include "console/engineAPI.h" + +ConsoleDocClass( ForestWindEmitter, + "@brief Object responsible for simulating wind in a level.\n\n" + + "When placed in the level, a ForestWindEmitter will cause tree branches to bend and sway, leaves " + "to flutter, and create vertical bending on the tree's trunk.\n\n" + + "@tsexample\n" + "// The following is a full declaration of a wind emitter\n" + "new ForestWindEmitter()\n" + "{\n" + " position = \"497.739 765.821 102.395\";\n" + " windEnabled = \"1\";\n" + " radialEmitter = \"1\";\n" + " strength = \"1\";\n" + " radius = \"3\";\n" + " gustStrength = \"0.5\";\n" + " gustFrequency = \"1\";\n" + " gustYawAngle = \"10\";\n" + " gustYawFrequency = \"4\";\n" + " gustWobbleStrength = \"2\";\n" + " turbulenceStrength = \"1\";\n" + " turbulenceFrequency = \"2\";\n" + " hasMount = \"0\";\n" + " scale = \"3 3 3\";\n" + " canSave = \"1\";\n" + " canSaveDynamicFields = \"1\";\n" + " rotation = \"1 0 0 0\";\n" + "};\n" + "@endtsexample\n\n" + + "@ingroup FX\n" + "@ingroup Forest\n" + "@ingroup Atmosphere\n" +); + +// We need to know when the mission editor is enabled. +extern bool gEditingMission; + +ForestWind::ForestWind( ForestWindEmitter *emitter ) + : mStrength( 0.0f ), + mDirection( 1.0f, 0, 0 ), + mLastGustTime( 0 ), + mLastYawTime( 0 ), + mCurrentTarget( 0, 0 ), + mCurrentInterp( 0 ), + mTargetYawAngle( 0 ), + mParent( emitter ), + mIsDirty( false ), + mRandom( Platform::getRealMilliseconds() + 1 ) +{ +} + +ForestWind::~ForestWind() +{ +} + +void ForestWind::processTick() +{ + PROFILE_SCOPE( ForestWind_ProcessTick ); + + const F32 deltaTime = 0.032f; + const U32 simTime = Sim::getCurrentTime(); + + Point2F finalVec( 0, 0 ); + Point2F windDir( mParent->mWindDirection.x, mParent->mWindDirection.y ); + + if ( mLastGustTime < simTime ) + { + Point2F turbVec( 0, 0 ); + if ( mLastGustTime < simTime + (mParent->mWindTurbulenceFrequency * 1000.0f) ) + turbVec = (mRandom.randF() * mParent->mWindTurbulenceStrength) * windDir; + + mLastGustTime = simTime + (mParent->mWindGustFrequency * 1000.0f); + Point2F gustVec = (mRandom.randF() * mParent->mWindGustStrength + mRandom.randF() * mParent->mWindGustWobbleStrength) * windDir; + + finalVec += gustVec + turbVec; + //finalVec.normalizeSafe(); + } + + //bool rotationChange = false; + + if ( mLastYawTime < simTime ) + { + mLastYawTime = simTime + (mParent->mWindGustYawFrequency * 1000.0f); + F32 rotateAmt = mRandom.randF() * mParent->mWindGustYawAngle + mRandom.randF() * mParent->mWindGustWobbleStrength; + + if ( mRandom.randF() <= 0.5f ) + rotateAmt = -rotateAmt; + + rotateAmt = mDegToRad( rotateAmt ); + + if ( rotateAmt > M_2PI_F ) + rotateAmt -= M_2PI_F; + else if ( rotateAmt < -M_2PI_F ) + rotateAmt += M_2PI_F; + + mTargetYawAngle = rotateAmt; + + //finalVec.rotate( rotateAmt ); + mCurrentTarget.rotate( rotateAmt ); + } + + //mCurrentTarget.normalizeSafe(); + + if ( mCurrentTarget.isZero() || mCurrentInterp >= 1.0f ) + { + mCurrentInterp = 0; + mCurrentTarget.set( 0, 0 ); + + Point2F windDir( mDirection.x, mDirection.y ); + windDir.normalizeSafe(); + + mCurrentTarget = finalVec + windDir; + } + else + { + mCurrentInterp += deltaTime; + mCurrentInterp = mClampF( mCurrentInterp, 0.0f, 1.0f ); + mDirection.interpolate( mDirection, Point3F( mCurrentTarget.x, mCurrentTarget.y, 0 ), mCurrentInterp ); + //F32 rotateAmt = mLerp( 0, mTargetYawAngle, mCurrentInterp ); + + //mTargetYawAngle -= rotateAmt; + + //Point2F dir( mDirection.x, mDirection.y ); + //if ( mTargetYawAngle > 0.0f ) + // dir.rotate( rotateAmt ); + + //mDirection.set( dir.x, dir.y, 0 ); + } +} + +void ForestWind::setStrengthAndDirection( F32 strength, const VectorF &direction ) +{ + if ( mStrength != strength || + mDirection != direction ) + { + mStrength = strength; + mDirection = direction; + mIsDirty = true; + } +} + +void ForestWind::setStrength( F32 strength ) +{ + if ( mStrength != strength ) + { + mStrength = strength; + mIsDirty = true; + } +} + +void ForestWind::setDirection( const VectorF &direction ) +{ + if ( mDirection != direction ) + { + mDirection = direction; + mIsDirty = true; + } +} + +IMPLEMENT_CO_NETOBJECT_V1(ForestWindEmitter); + +ForestWindEmitter::ForestWindEmitter( bool makeClientObject ) + : mEnabled( true ), + mAddedToScene( false ), + mWind( NULL ), + mWindStrength( 1 ), + mWindDirection( 1, 0, 0 ), + mWindGustFrequency( 3.0f ), + mWindGustStrength( 0.25f ), + mWindGustYawAngle( 10.0f ), + mWindGustYawFrequency( 4.0f ), + mWindGustWobbleStrength( 2.0f ), + mWindTurbulenceFrequency( 2.0f ), + mWindTurbulenceStrength( 0.25f ), + mWindRadius( 0 ), + mRadialEmitter( false ), + mHasMount( false ), + mIsMounted( false ), + mMountObject( NULL ) +{ + mTypeMask |= StaticObjectType | EnvironmentObjectType; + + if ( makeClientObject ) + mNetFlags.set( IsGhost ); + else + mNetFlags.set( Ghostable | ScopeAlways ); +} + +ForestWindEmitter::~ForestWindEmitter() +{ +} + +void ForestWindEmitter::initPersistFields() +{ + // Initialise parents' persistent fields. + Parent::initPersistFields(); + + addGroup( "ForestWind" ); + addField( "windEnabled", TypeBool, Offset( mEnabled, ForestWindEmitter ), "Determines if the emitter will be counted in wind calculations." ); + addField( "radialEmitter", TypeBool, Offset( mRadialEmitter, ForestWindEmitter ), "Determines if the emitter is a global direction or local radial emitter." ); + addField( "strength", TypeF32, Offset( mWindStrength, ForestWindEmitter ), "The strength of the wind force." ); + addField( "radius", TypeF32, Offset( mWindRadius, ForestWindEmitter ), "The radius of the emitter for local radial emitters." ); + addField( "gustStrength", TypeF32, Offset( mWindGustStrength, ForestWindEmitter ), "The maximum strength of a gust." ); + addField( "gustFrequency", TypeF32, Offset( mWindGustFrequency, ForestWindEmitter ), "The frequency of gusting in seconds." ); + addField( "gustYawAngle", TypeF32, Offset( mWindGustYawAngle, ForestWindEmitter ), "The amount of degrees the wind direction can drift (both positive and negative)." ); + addField( "gustYawFrequency", TypeF32, Offset( mWindGustYawFrequency, ForestWindEmitter ), "The frequency of wind yaw drift, in seconds." ); + addField( "gustWobbleStrength", TypeF32, Offset( mWindGustWobbleStrength, ForestWindEmitter ), "The amount of random wobble added to gust and turbulence vectors." ); + addField( "turbulenceStrength", TypeF32, Offset( mWindTurbulenceStrength, ForestWindEmitter ), "The strength of gust turbulence." ); + addField( "turbulenceFrequency", TypeF32, Offset( mWindTurbulenceFrequency, ForestWindEmitter ), "The frequency of gust turbulence, in seconds." ); + addField( "hasMount", TypeBool, Offset( mHasMount, ForestWindEmitter ), "Determines if the emitter is mounted to another object." ); + endGroup( "ForestWind" ); +} + +U32 ForestWindEmitter::packUpdate(NetConnection * con, U32 mask, BitStream * stream) +{ + U32 retMask = Parent::packUpdate( con, mask, stream ); + + mathWrite( *stream, mObjToWorld ); + + if ( stream->writeFlag( mask & EnabledMask ) ) + stream->writeFlag( mEnabled ); + + if ( stream->writeFlag( mask & WindMask ) ) + { + stream->write( mWindStrength ); + + stream->write( mWindRadius ); + stream->writeFlag( mRadialEmitter ); + + stream->write( mWindGustStrength ); + stream->write( mWindGustFrequency ); + + stream->write( mWindGustYawAngle ); + stream->write( mWindGustYawFrequency ); + stream->write( mWindGustWobbleStrength ); + + stream->write( mWindTurbulenceStrength ); + stream->write( mWindTurbulenceFrequency ); + + // The wind direction should be normalized! + if ( mWindDirection.isZero() ) + { + VectorF forwardVec( 0, 0, 0 ); + mWorldToObj.getColumn( 1, &mWindDirection ); + } + else + mWindDirection.normalize(); + + stream->writeNormalVector( mWindDirection, 8 ); + + stream->writeFlag( mHasMount ); + } + + return retMask; +} + +void ForestWindEmitter::unpackUpdate(NetConnection * con, BitStream * stream) +{ + // Unpack Parent. + Parent::unpackUpdate( con, stream ); + + MatrixF xfm; + mathRead( *stream, &xfm ); + Parent::setTransform( xfm ); + + U32 windMask = 0; + + if ( stream->readFlag() ) // EnabledMask + mEnabled = stream->readFlag(); + + if ( stream->readFlag() ) // WindMask + { + stream->read( &mWindStrength ); + stream->read( &mWindRadius ); + + mRadialEmitter = stream->readFlag(); + + stream->read( &mWindGustStrength ); + stream->read( &mWindGustFrequency ); + + stream->read( &mWindGustYawAngle ); + stream->read( &mWindGustYawFrequency ); + stream->read( &mWindGustWobbleStrength ); + + stream->read( &mWindTurbulenceStrength ); + stream->read( &mWindTurbulenceFrequency ); + + stream->readNormalVector( &mWindDirection, 8 ); + windMask |= WindMask; + + mHasMount = stream->readFlag(); + } + + // This does nothing if the masks are not set! + if ( windMask != 0 && isProperlyAdded() ) + { + Point3F boxRad( 0, 0, 0 ); + + if ( !isRadialEmitter() ) + boxRad.set( 10000.0f, 10000.0f, 10000.0f ); + else + boxRad.set( mWindRadius, mWindRadius, mWindRadius ); + + mObjBox.set( -boxRad, boxRad ); + resetWorldBox(); + + _initWind( windMask ); + } +} + +bool ForestWindEmitter::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + // Only the client side actually does wind. + if ( isClientObject() ) + { + // TODO: wasn't this a big hack we already fixed better? + //Projectile::getGhostReceivedSignal().notify( this, &ForestWindEmitter::_onMountObjectGhostReceived ); + + _initWind(); + WINDMGR->addEmitter( this ); + } + + Point3F boxRad( 0, 0, 0 ); + + if ( !isRadialEmitter() ) + boxRad.set( 10000.0f, 10000.0f, 10000.0f ); + else + boxRad.set( mWindRadius, mWindRadius, mWindRadius ); + + mObjBox.set( -boxRad, boxRad ); + resetWorldBox(); + + enableCollision(); + + // If we are we editing the mission then + // be sure to add us to the scene. + if ( gEditingMission || mHasMount ) + { + addToScene(); + mAddedToScene = true; + } + + return true; +} + +void ForestWindEmitter::onRemove() +{ + // Only the client side actually does wind. + if ( isClientObject() ) + { + //Projectile::getGhostReceivedSignal().remove( this, &ForestWindEmitter::_onMountObjectGhostReceived ); + + WINDMGR->removeEmitter( this ); + SAFE_DELETE( mWind ); + } + + // If we are editing the mission then remove + // us from the scene graph. + if ( gEditingMission || mHasMount ) + { + removeFromScene(); + mAddedToScene = false; + } + + // Do Parent. + Parent::onRemove(); +} + +void ForestWindEmitter::_onMountObjectGhostReceived( SceneObject *object ) +{ + if ( !object ) + return; + + attachToObject( object ); +} + +void ForestWindEmitter::inspectPostApply() +{ + // Force the client update! + setMaskBits(0xffffffff); +} + +void ForestWindEmitter::onEditorEnable() +{ + if ( !mAddedToScene ) + { + addToScene(); + mAddedToScene = true; + } +} + +void ForestWindEmitter::onEditorDisable() +{ + // Remove us from the scene. + if ( mAddedToScene ) + { + removeFromScene(); + mAddedToScene = false; + } +} + +void ForestWindEmitter::_initWind( U32 mask ) +{ + AssertFatal( !isServerObject(), "SpeedWind is never updated on the server!" ); + + // If we don't have a wind + // object create one now. + if ( !mWind ) + mWind = new ForestWind( this ); + + // Do we need to apply a new direction and strength? + if ( mask & WindMask ) + { + mWorldToObj.getColumn( 1, &mWindDirection ); + mWind->setStrengthAndDirection( mWindStrength, mWindDirection ); + } +} + +void ForestWindEmitter::setTransform( const MatrixF &mat ) +{ + Parent::setTransform( mat ); + + // Force the client update! + setMaskBits(0xffffffff); + + if ( isClientObject() ) + _initWind(); +} + +void ForestWindEmitter::prepRenderImage( SceneRenderState* state ) +{ + PROFILE_SCOPE( ForestWindEmitter_PrepRenderImage ); + + // Only render the radius and + // direction if we're in the editor. + // Don't render them if this is a reflect pass. + if ( !state->isDiffusePass() || !gEditingMission ) + return; + + // This should be sufficient for most objects that don't manage zones, and + // don't need to return a specialized RenderImage... + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &ForestWindEmitter::_renderEmitterInfo ); + ri->type = RenderPassManager::RIT_Editor; + state->getRenderPass()->addInst( ri ); +} + +void ForestWindEmitter::_renderEmitterInfo( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ) +{ + if ( overrideMat ) + return; + + GFXTransformSaver saver; + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + + AssertFatal( drawer, "Got NULL GFXDrawUtil!" ); + + const Point3F &pos = getPosition(); + const VectorF &windVec = mWind->getDirection(); + + GFXStateBlockDesc desc; + desc.setBlend( true ); + desc.setZReadWrite( true, false ); + + // Draw an arrow pointing + // in the wind direction. + drawer->drawArrow( desc, pos, pos + (windVec * mWindStrength), ColorI( 0, 0, 255, 255 ) );//Point3F( -235.214, 219.589, 34.0991 ), Point3F( -218.814, 244.731, 37.5587 ), ColorI( 255, 255, 0, 255 ) );// + drawer->drawArrow( desc, pos, pos + (mWind->getTarget() * mWindStrength ), ColorI( 255, 0, 0, 85 ) ); + + // Draw a 2D circle for the wind radius. + if ( isRadialEmitter() ) + drawer->drawSphere( desc, mWindRadius, pos, ColorI( 255, 0, 0, 80 ) ); +} + +F32 ForestWindEmitter::getStrength() const +{ + return mWind->getStrength(); +} + +void ForestWindEmitter::setStrength( F32 strength ) +{ + mWindStrength = strength; + mWind->setStrength( mWindStrength ); +} + +void ForestWindEmitter::attachToObject( SceneObject *obj ) +{ + if ( !obj ) + return; + + mMountObject = obj; + mIsMounted = true; + + if ( isServerObject() ) + deleteNotify( mMountObject ); +} + +void ForestWindEmitter::updateMountPosition() +{ + AssertFatal( isClientObject(), "ForestWindEmitter::updateMountPosition - This should only happen on the client!" ); + + if ( !mHasMount || !mMountObject ) + return; + + MatrixF mat( true ); + mat.setPosition( mMountObject->getPosition() ); + Parent::setTransform( mat ); +} + +void ForestWindEmitter::onDeleteNotify(SimObject *object) +{ + AssertFatal( isServerObject(), "ForestWindEmitter::onDeleteNotify - This should never happen on the client!" ); + safeDeleteObject(); +} + +DefineEngineMethod( ForestWindEmitter, attachToObject, void, ( U32 objectID ),, + "@brief Mounts the wind emitter to another scene object\n\n" + + "@param objectID Unique ID of the object wind emitter should attach to" + + "@tsexample\n" + "// Wind emitter previously created and named %windEmitter\n" + "// Going to attach it to the player, making him a walking wind storm\n" + "%windEmitter.attachToObject(%player);\n" + "@endtsexample\n\n") +{ + SceneObject *obj = dynamic_cast( Sim::findObject( objectID ) ); + if ( !obj ) + Con::warnf( "ForestWindEmitter::attachToObject - failed to find object with ID: %d", objectID ); + + object->attachToObject( obj ); +} \ No newline at end of file diff --git a/Engine/source/forest/forestWindEmitter.h b/Engine/source/forest/forestWindEmitter.h new file mode 100644 index 000000000..baf30d07f --- /dev/null +++ b/Engine/source/forest/forestWindEmitter.h @@ -0,0 +1,219 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FORESTWINDEMITTER_H_ +#define _FORESTWINDEMITTER_H_ + + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _MSPHERE_H_ +#include "math/mSphere.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + + +class ForestWindEmitter; +class ForestWindAccumulator; +class BaseMatInstance; + + +class ForestWind +{ + +protected: + + F32 mStrength; + + VectorF mDirection; + + F32 mLastGustTime; + F32 mLastYawTime; + + F32 mTargetYawAngle; + + F32 mCurrentInterp; + Point2F mCurrentTarget; + + ForestWindEmitter *mParent; + + MRandom mRandom; + + bool mIsDirty; + +public: + + ForestWind( ForestWindEmitter *emitter ); + virtual ~ForestWind(); + + void processTick(); + + void setStrengthAndDirection( F32 strength, const VectorF &direction ); + + void setStrength( F32 strength ); + + void setDirection( const VectorF &direction ); + + void setIsDirty( bool isDirty ) { mIsDirty = isDirty; } + + F32 getStrength() const { return mStrength; } + + const VectorF& getDirection() const { return mDirection; } + VectorF getTarget() const { return VectorF( mCurrentTarget.x, mCurrentTarget.y, 0 ); } +}; + + +/// A vector of WindEmitter pointers. +typedef Vector ForestWindEmitterList; + +class ForestWindEmitter : public SceneObject +{ + typedef SceneObject Parent; + friend class ForestWind; + friend class ForestWindAccumulator; + +protected: + + enum + { + EnabledMask = Parent::NextFreeMask << 0, + WindMask = Parent::NextFreeMask << 1, + NextFreeMask = Parent::NextFreeMask << 2 + }; + + bool mAddedToScene; + + bool mEnabled; + + ForestWind *mWind; + + F32 mWindStrength; + + VectorF mWindDirection; + + /// Controls how often the wind gust peaks per second. + F32 mWindGustFrequency; + + /// The maximum distance in meters that the peak wind + /// gust will displace an element. + F32 mWindGustStrength; + + /// The range that the wind direction + /// will yaw between (-val to +val). + F32 mWindGustYawAngle; + + /// The frequency, in seconds, between + /// animations in the gust yaw angle. + F32 mWindGustYawFrequency; + + /// The maximum amount of random wobble + /// that will be applied to the gusting + /// as well as the yaw rotation. + F32 mWindGustWobbleStrength; + + /// Controls the overally rapidity of the wind turbulence. + F32 mWindTurbulenceFrequency; + + /// The maximum distance in meters that the turbulence can + /// displace a ground cover element. + F32 mWindTurbulenceStrength; + + /// If the radius is greater than zero then this is + /// a localized radial wind emitter. + F32 mWindRadius; + + /// Explicitly denotes whether this is a + /// global directional wind emitter or a + /// localized radial wind emitter. + bool mRadialEmitter; + + bool mHasMount; + bool mIsMounted; + + /// An object that the emitter can + /// get position updates from. + SimObjectPtr mMountObject; + + static ForestWindEmitterList smEmitters; + + void _initWind( U32 mask = 0xFFFFFFFF ); + + void _onMountObjectGhostReceived( SceneObject *object ); + +public: + + ForestWindEmitter( bool makeClientObject = false ); + + virtual ~ForestWindEmitter(); + + bool isEnabled() const { return mEnabled; } + + ForestWind* getWind() { return mWind; } + + bool isLocalWind() const { return mWindRadius > 0.0f; } + bool isRadialEmitter() const { return mRadialEmitter; } + + F32 getWindRadius() const { return mWindRadius; } + + F32 getWindRadiusSquared() const { return mWindRadius * mWindRadius; } + + void setWindRadius( F32 radius ) { mWindRadius = radius; } + + F32 getStrength() const; + void setStrength( F32 strength ); + + void _renderEmitterInfo( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); + + void attachToObject( SceneObject *obj ); + void updateMountPosition(); + + // SceneObject + virtual void setTransform( const MatrixF &mat ); + void prepRenderImage( SceneRenderState *state ); + + // SimObject + bool onAdd(); + void onRemove(); + void inspectPostApply(); + void onEditorEnable(); + void onEditorDisable(); + void onDeleteNotify(SimObject *object); + + // NetObject + U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream ); + void unpackUpdate( NetConnection *conn, BitStream *stream ); + + // ConObject. + static void initPersistFields(); + DECLARE_CONOBJECT(ForestWindEmitter); +}; + +#endif // _FORESTWINDEMITTER_H_ \ No newline at end of file diff --git a/Engine/source/forest/forestWindMgr.cpp b/Engine/source/forest/forestWindMgr.cpp new file mode 100644 index 000000000..33c0830f0 --- /dev/null +++ b/Engine/source/forest/forestWindMgr.cpp @@ -0,0 +1,316 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "forest/forestWindMgr.h" + +#include "platform/profiler.h" +#include "core/tAlgorithm.h" +#include "core/module.h" +#include "console/consoleTypes.h" +#include "math/mathUtils.h" +#include "T3D/gameBase/gameConnection.h" +#include "forest/forest.h" +#include "forest/forestWindAccumulator.h" +#include "T3D/fx/particleEmitter.h" + + +MODULE_BEGIN( ForestWindMgr ) + + MODULE_INIT + { + ManagedSingleton< ForestWindMgr >::createSingleton(); + ForestWindMgr::initConsole(); + } + + MODULE_SHUTDOWN + { + ManagedSingleton< ForestWindMgr >::deleteSingleton(); + } + +MODULE_END; + + +ForestWindMgr::WindAdvanceSignal ForestWindMgr::smAdvanceSignal; + +F32 ForestWindMgr::smWindEffectRadius = 25.0f; + + +ForestWindMgr::ForestWindMgr() +{ + setProcessTicks( true ); + mSources = new IdToWindMap(); + mPrevSources = new IdToWindMap(); +} + +ForestWindMgr::~ForestWindMgr() +{ + IdToWindMap::Iterator sourceIter = mSources->begin(); + for( ; sourceIter != mSources->end(); sourceIter++ ) + delete (*sourceIter).value; + + delete mSources; + delete mPrevSources; +} + +void ForestWindMgr::initConsole() +{ + Con::addVariable( "$pref::windEffectRadius", TypeF32, &smWindEffectRadius, "Radius to affect the wind.\n" + "@ingroup Rendering\n"); +} + +void ForestWindMgr::addEmitter( ForestWindEmitter *emitter ) +{ + mEmitters.push_back( emitter ); +} + +void ForestWindMgr::removeEmitter( ForestWindEmitter *emitter ) +{ + ForestWindEmitterList::iterator iter = find( mEmitters.begin(), + mEmitters.end(), + emitter ); + + AssertFatal( iter != mEmitters.end(), + "SpeedTreeWindMgr::removeEmitter() - Bad emitter!" ); + + mEmitters.erase( iter ); +} + +void ForestWindMgr::processTick() +{ + const F32 timeDelta = 0.032f; + + if ( mEmitters.empty() ) + return; + + PROFILE_SCOPE(ForestWindMgr_AdvanceTime); + + // Advance all ForestWinds. + { + PROFILE_SCOPE(ForestWindMgr_AdvanceTime_ForestWind_ProcessTick); + + ForestWindEmitterList::iterator iter = mEmitters.begin(); + for ( ; iter != mEmitters.end(); iter++ ) + { + if ( (*iter)->getWind() && + (*iter)->isEnabled() ) + { + (*iter)->updateMountPosition(); + + ForestWind *wind = (*iter)->getWind(); + if ( wind ) + wind->processTick(); + } + } + } + + // Assign the new global wind value used by the particle system. + { + ForestWindEmitter *pWindEmitter = getGlobalWind(); + if ( pWindEmitter == NULL ) + ParticleEmitter::setWindVelocity( Point3F::Zero ); + else + { + ForestWind *pWind = pWindEmitter->getWind(); + ParticleEmitter::setWindVelocity( pWind->getDirection() * pWind->getStrength() ); + } + } + + // Get the game connection and camera object + // in order to retrieve the camera position. + GameConnection *conn = GameConnection::getConnectionToServer(); + if ( !conn ) + return; + + GameBase *cam = conn->getCameraObject(); + if ( !cam ) + return; + + const Point3F &camPos = cam->getPosition(); + + + + // Gather TreePlacementInfo for trees near the camera. + { + PROFILE_SCOPE( ForestWindMgr_AdvanceTime_GatherTreePlacementInfo ); + + smAdvanceSignal.trigger( camPos, smWindEffectRadius, &mPlacementInfo ); + } + + // Prepare to build a new local source map. + { + PROFILE_SCOPE( ForestWindMgr_AdvanceTime_SwapSources ); + AssertFatal( mPrevSources->isEmpty(), "prev sources not empty!" ); + + swap( mSources, mPrevSources ); + + AssertFatal( mSources->isEmpty(), "swap failed!" ); + } + + // Update wind for each TreePlacementInfo + { + PROFILE_SCOPE( ForestWindMgr_AdvanceTime_UpdateWind ); + + for( S32 i = 0; i < mPlacementInfo.size(); i++ ) + { + const TreePlacementInfo &info = mPlacementInfo[i]; + updateWind( camPos, info, timeDelta ); + } + + mPlacementInfo.clear(); + } + + // Clean up any accumulators in the + // previous local source map. + { + PROFILE_SCOPE( ForestWindMgr_AdvanceTime_Cleanup ); + + IdToWindMap::Iterator sourceIter = mPrevSources->begin(); + for( ; sourceIter != mPrevSources->end(); sourceIter++ ) + { + ForestWindAccumulator *accum = (*sourceIter).value; + + AssertFatal( accum, "Got null accumulator!" ); + delete accum; + } + + mPrevSources->clear(); + } +} + +ForestWindAccumulator* ForestWindMgr::getLocalWind( ForestItemKey key ) +{ + PROFILE_SCOPE( ForestWindMgr_getLocalWind ); + + ForestWindAccumulator *accumulator = NULL; + IdToWindMap::Iterator iter = mSources->find( key ); + if ( iter != mSources->end() ) + accumulator = iter->value; + + if ( accumulator ) + return accumulator; + else + { + /* + ForestWindEmitterList::iterator iter = mEmitters.begin(); + for ( ; iter != mEmitters.end(); iter++ ) + { + if ( (*iter)->getWind() && + (*iter)->isEnabled() && + !(*iter)->isLocalWind() ) + { + return (*iter)->getWind(); + } + } + */ + return NULL; + } +} + +ForestWindEmitter* ForestWindMgr::getGlobalWind() +{ + ForestWindEmitterList::iterator itr = mEmitters.begin(); + for ( ; itr != mEmitters.end(); itr++ ) + { + if ( !(*itr)->isRadialEmitter() ) + return *itr; + } + + return NULL; +} + +void ForestWindMgr::updateWind( const Point3F &camPos, + const TreePlacementInfo &info, + F32 timeDelta ) +{ + PROFILE_SCOPE(ForestWindMgr_updateWind); + + // See if we have the blended source available. + ForestWindAccumulator *blendDest = NULL; + { + IdToWindMap::Iterator iter = mPrevSources->find( info.itemKey ); + if ( iter != mPrevSources->end() ) + { + blendDest = iter->value; + mPrevSources->erase( iter ); + } + } + + // Get some stuff we'll need for finding the emitters. + F32 treeHeight = info.scale * info.dataBlock->getObjBox().len_z(); + Point3F top = info.pos; + top.z += treeHeight; + if ( blendDest ) + top += ( 1.0f / info.scale ) * blendDest->getDirection(); + + // Go thru the emitters to accumulate the total wind force. + VectorF windForce( 0, 0, 0 ); + + F32 time = Sim::getCurrentTime() / 1000.0f; + + ForestWindEmitterList::iterator iter = mEmitters.begin(); + for ( ; iter != mEmitters.end(); iter++ ) + { + ForestWindEmitter *emitter = (*iter); + + // If disabled or no wind object... skip it. + if ( !emitter->isEnabled() || !emitter->getWind() ) + continue; + + ForestWind *wind = emitter->getWind(); + + F32 strength = wind->getStrength(); + + if ( emitter->isRadialEmitter() ) + { + Point3F closest = MathUtils::mClosestPointOnSegment( info.pos, top, emitter->getPosition() ); + Point3F dir = closest - emitter->getPosition(); + F32 lenSquared = dir.lenSquared(); + if ( lenSquared > emitter->getWindRadiusSquared() ) + continue; + + dir *= 1.0f / mSqrt( lenSquared ); + + F32 att = lenSquared / emitter->getWindRadiusSquared(); + strength *= 1.0f - att; + windForce += dir * strength; + } + else + { + F32 d = mDot( info.pos, Point3F::One ); //PlaneF( Point3F::Zero, wind->getDirection() ).distToPlane( Point3F( info.pos.x, info.pos.y, 0 ) ); + //F32 d = PlaneF( Point3F::Zero, wind->getDirection() ).distToPlane( Point3F( info.pos.x, info.pos.y, 0 ) ); + F32 scale = 1.0f + ( mSin( d + ( time / 10.0 ) ) * 0.5f ); + windForce += wind->getDirection() * strength * scale; + } + } + + // If we need a accumulator then we also need to presimulate. + if ( !blendDest ) + { + blendDest = new ForestWindAccumulator( info ); + blendDest->presimulate( windForce, 4.0f / TickSec ); + } + else + blendDest->updateWind( windForce, timeDelta ); + + mSources->insertUnique( info.itemKey, blendDest ); +} \ No newline at end of file diff --git a/Engine/source/forest/forestWindMgr.h b/Engine/source/forest/forestWindMgr.h new file mode 100644 index 000000000..32fdab67a --- /dev/null +++ b/Engine/source/forest/forestWindMgr.h @@ -0,0 +1,101 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FORESTWINDMGR_H_ +#define _FORESTWINDMGR_H_ + +#ifndef _FORESTWINDEMITTER_H_ +#include "forest/forestWindEmitter.h" +#endif +#ifndef _TDICTIONARY_H_ +#include "core/util/tDictionary.h" +#endif +#ifndef _ITICKABLE_H_ +#include "core/iTickable.h" +#endif +#ifndef _FORESTITEM_H_ +#include "forest/forestItem.h" +#endif +#ifndef _TSINGLETON_H_ +#include "core/util/tSingleton.h" +#endif + +struct TreePlacementInfo +{ + F32 scale; + Point3F pos; + ForestItemKey itemKey; + ForestItemData *dataBlock; +}; + +class ForestWindMgr : public virtual ITickable +{ +protected: + + ForestWindEmitterList mEmitters; + + typedef HashTable IdToWindMap; + typedef Signal *placementInfo )> WindAdvanceSignal; + + IdToWindMap *mSources; + IdToWindMap *mPrevSources; + Vector mPlacementInfo; + + static WindAdvanceSignal smAdvanceSignal; + + virtual void interpolateTick( F32 delta ) {}; + virtual void processTick(); + virtual void advanceTime( F32 timeDelta ) {}; + +public: + + ForestWindMgr(); + virtual ~ForestWindMgr(); + + void addEmitter( ForestWindEmitter *emitter ); + + void removeEmitter( ForestWindEmitter *emitter ); + + void updateWind( const Point3F &camPos, + const TreePlacementInfo &info, + F32 timeDelta ); + + ForestWindAccumulator* getLocalWind( ForestItemKey key ); + + // Returns the first non-radial emitter in the list. + ForestWindEmitter* getGlobalWind(); + + + static WindAdvanceSignal& getAdvanceSignal() { return smAdvanceSignal; } + + static F32 smWindEffectRadius; + + static void initConsole(); + + // For ManagedSingleton. + static const char* getSingletonName() { return "ForestWindMgr"; } +}; + +/// Returns the ReflectionManager singleton. +#define WINDMGR ManagedSingleton::instance() + +#endif // _FORESTWINDMGR_H_ \ No newline at end of file diff --git a/Engine/source/forest/glsl/windDeformationGLSL.cpp b/Engine/source/forest/glsl/windDeformationGLSL.cpp new file mode 100644 index 000000000..00502caa9 --- /dev/null +++ b/Engine/source/forest/glsl/windDeformationGLSL.cpp @@ -0,0 +1,210 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "forest/glsl/windDeformationGLSL.h" +#include "forest/windDeformation.h" + +#include "forest/forestItem.h" +#include "forest/forestWindMgr.h" +#include "materials/sceneData.h" +#include "scene/sceneRenderState.h" + +#include "shaderGen/shaderGen.h" +#include "shaderGen/featureMgr.h" +#include "shaderGen/langElement.h" +#include "shaderGen/shaderOp.h" +#include "shaderGen/shaderGenVars.h" +#include "gfx/gfxStructs.h" +#include "core/module.h" + + +static void _onRegisterFeatures( GFXAdapterType type ) +{ + FEATUREMGR->registerFeature( MFT_WindEffect, new WindDeformationGLSL ); +} + + +MODULE_BEGIN( WindDeformationGLSL ) + + MODULE_INIT_AFTER( ShaderGen ) + + MODULE_INIT + { + SHADERGEN->getFeatureInitSignal().notify( _onRegisterFeatures ); + } + +MODULE_END; + + +WindDeformationGLSL::WindDeformationGLSL() + : mDep( "shaders/common/gl/wind.glsl" ) +{ + addDependency( &mDep ); +} + +void WindDeformationGLSL::determineFeature( Material *material, + const GFXVertexFormat *vertexFormat, + U32 stageNum, + const FeatureType &type, + const FeatureSet &features, + MaterialFeatureData *outFeatureData ) +{ + bool enabled = vertexFormat->hasColor() && features.hasFeature( MFT_WindEffect ); + outFeatureData->features.setFeature( type, enabled ); +} + +void WindDeformationGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + output = meta; + + // We combined all the tree parameters into one float4 to + // save constant space and reduce the memory copied to the + // card. + // + // This in particular helps when we're instancing. + // + // .x = bend scale + // .y = branch amplitude + // .z = detail amplitude + // .w = detail frequency + // + Var *windParams; + if ( fd.features[MFT_UseInstancing] ) + { + ShaderConnector *vertStruct = dynamic_cast( componentList[C_VERT_STRUCT] ); + windParams = vertStruct->getElement( RT_TEXCOORD ); + windParams->setName( "inst_windParams" ); + windParams->setType( "vec4" ); + + mInstancingFormat->addElement( "windParams", GFXDeclType_Float4, windParams->constNum ); + } + else + { + windParams = new Var( "windParams", "vec4" ); + windParams->uniform = true; + windParams->constSortPos = cspPotentialPrimitive; + } + + // If we're instancing then we need to instance the wind direction + // and speed as its unique for each tree instance. + Var *windDirAndSpeed; + if ( fd.features[MFT_UseInstancing] ) + { + ShaderConnector *vertStruct = dynamic_cast( componentList[C_VERT_STRUCT] ); + windDirAndSpeed = vertStruct->getElement( RT_TEXCOORD ); + windDirAndSpeed->setName( "inst_windDirAndSpeed" ); + windDirAndSpeed->setType( "vec3" ); + + mInstancingFormat->addElement( "windDirAndSpeed", GFXDeclType_Float3, windDirAndSpeed->constNum ); + } + else + { + windDirAndSpeed = new Var( "windDirAndSpeed", "vec3" ); + windDirAndSpeed->uniform = true; + windDirAndSpeed->constSortPos = cspPrimitive; + } + + Var *accumTime = (Var*)LangElement::find( "accumTime" ); + if ( !accumTime ) + { + accumTime = new Var( "accumTime", "float" ); + accumTime->uniform = true; + accumTime->constSortPos = cspPass; + } + + // Get the transform to world space. + Var *objTrans = getObjTrans( componentList, fd.features[MFT_UseInstancing], meta ); + + // First check for an input position from a previous feature + // then look for the default vertex position. + Var *inPosition = (Var*)LangElement::find( "inPosition" ); + if ( !inPosition ) + inPosition = (Var*)LangElement::find( "position" ); + + // Get the incoming color data + Var *inColor = (Var*)LangElement::find( "diffuse" ); + + // Do the branch and detail bending first so that + // it can work in pure object space of the tree. + LangElement *effect = + new GenOp( "windBranchBending( " + + "@.xyz, " // vPos + "normalize( normal ), " // vNormal + + "@, " // fTime + "@.z, " // fWindSpeed + + "@.g, " // fBranchPhase + "@.y, " // fBranchAmp + "@.r, " // fBranchAtten + + "dot( @[3], vec4( 1.0 ) ), " // fDetailPhase + "@.z, " // fDetailAmp + "@.w, " // fDetailFreq + + "@.b )", // fEdgeAtten + + inPosition, // vPos + // vNormal + + accumTime, // fTime + windDirAndSpeed, // fWindSpeed + + inColor, // fBranchPhase + windParams, // fBranchAmp + inColor, // fBranchAtten + + objTrans, // fDetailPhase + windParams, // fDetailAmp + windParams, // fDetailFreq + + inColor ); // fEdgeAtten + + Var *outPosition = (Var*)LangElement::find( "inPosition" ); + if ( outPosition ) + meta->addStatement( new GenOp( " @.xyz = @;\r\n", outPosition, effect, inPosition ) ); + else + { + outPosition = new Var; + outPosition->setType( "vec3" ); + outPosition->setName( "inPosition" ); + meta->addStatement( new GenOp(" vec3 inPosition = @;\r\n", effect, inPosition ) ); + } + + // Now do the trunk bending. + effect = new GenOp( "windTrunkBending( @, @.xy, @.z * @.x )", + outPosition, windDirAndSpeed, outPosition, windParams ); + + meta->addStatement( new GenOp(" @ = @;\r\n", outPosition, effect ) ); +} + +ShaderFeatureConstHandles* WindDeformationGLSL::createConstHandles( GFXShader *shader, SimObject *userObject ) +{ + WindDeformationConstHandles *handles = new WindDeformationConstHandles(); + handles->init( shader ); + + return handles; +} diff --git a/Engine/source/forest/glsl/windDeformationGLSL.h b/Engine/source/forest/glsl/windDeformationGLSL.h new file mode 100644 index 000000000..cce0fe51c --- /dev/null +++ b/Engine/source/forest/glsl/windDeformationGLSL.h @@ -0,0 +1,66 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FOREST_SHADERGEN_WINDDEFORMATIONGLSL_H_ +#define _FOREST_SHADERGEN_WINDDEFORMATIONGLSL_H_ + +#ifndef _SHADERGEN_GLSL_SHADERFEATUREGLSL_H_ +#include "shaderGen/GLSL/shaderFeatureGLSL.h" +#endif +#ifndef _FEATURETYPE_H_ +#include "materials/materialFeatureTypes.h" +#endif + +class GFXShaderConstHandle; + + +class WindDeformationGLSL : public ShaderFeatureGLSL +{ +protected: + + ShaderIncludeDependency mDep; + +public: + + WindDeformationGLSL(); + + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual String getName() + { + return "Wind Effect"; + } + + virtual void determineFeature( Material *material, + const GFXVertexFormat *vertexFormat, + U32 stageNum, + const FeatureType &type, + const FeatureSet &features, + MaterialFeatureData *outFeatureData ); + + virtual ShaderFeatureConstHandles* createConstHandles( GFXShader *shader, SimObject *userObject ); +}; + +DeclareFeatureType( MFT_WindEffect ); + +#endif // _FOREST_SHADERGEN_WINDDEFORMATIONGLSL_H_ diff --git a/Engine/source/forest/hlsl/windDeformationHLSL.cpp b/Engine/source/forest/hlsl/windDeformationHLSL.cpp new file mode 100644 index 000000000..24acf769a --- /dev/null +++ b/Engine/source/forest/hlsl/windDeformationHLSL.cpp @@ -0,0 +1,207 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "forest/hlsl/windDeformationHLSL.h" +#include "forest/windDeformation.h" + +#include "forest/forestItem.h" +#include "forest/forestWindMgr.h" +#include "materials/sceneData.h" +#include "scene/sceneRenderState.h" + +#include "shaderGen/shaderGen.h" +#include "shaderGen/featureMgr.h" +#include "shaderGen/langElement.h" +#include "shaderGen/shaderOp.h" +#include "shaderGen/shaderGenVars.h" +#include "gfx/gfxStructs.h" +#include "core/module.h" + + +static void _onRegisterFeatures( GFXAdapterType type ) +{ + if ( type == OpenGL ) + return; + + FEATUREMGR->registerFeature( MFT_WindEffect, new WindDeformationHLSL ); +} + + +MODULE_BEGIN( WindDeformationHLSL ) + + MODULE_INIT_AFTER( ShaderGen ) + + MODULE_INIT + { + SHADERGEN->getFeatureInitSignal().notify( _onRegisterFeatures ); + } + +MODULE_END; + + +WindDeformationHLSL::WindDeformationHLSL() + : mDep( "shaders/common/wind.hlsl" ) +{ + addDependency( &mDep ); +} + +void WindDeformationHLSL::determineFeature( Material *material, + const GFXVertexFormat *vertexFormat, + U32 stageNum, + const FeatureType &type, + const FeatureSet &features, + MaterialFeatureData *outFeatureData ) +{ + bool enabled = vertexFormat->hasColor() && features.hasFeature( MFT_WindEffect ); + outFeatureData->features.setFeature( type, enabled ); +} + +void WindDeformationHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + output = meta; + + // We combined all the tree parameters into one float4 to + // save constant space and reduce the memory copied to the + // card. + // + // .x = bend scale + // .y = branch amplitude + // .z = detail amplitude + // .w = detail frequency + // + Var *windParams = new Var( "windParams", "float4" ); + windParams->uniform = true; + windParams->constSortPos = cspPotentialPrimitive; + + // If we're instancing then we need to instance the wind direction + // and speed as its unique for each tree instance. + Var *windDirAndSpeed; + if ( fd.features[MFT_UseInstancing] ) + { + ShaderConnector *vertStruct = dynamic_cast( componentList[C_VERT_STRUCT] ); + windDirAndSpeed = vertStruct->getElement( RT_TEXCOORD ); + windDirAndSpeed->setStructName( "IN" ); + windDirAndSpeed->setName( "inst_windDirAndSpeed" ); + windDirAndSpeed->setType( "float3" ); + + mInstancingFormat->addElement( "windDirAndSpeed", GFXDeclType_Float3, windDirAndSpeed->constNum ); + } + else + { + windDirAndSpeed = new Var( "windDirAndSpeed", "float3" ); + windDirAndSpeed->uniform = true; + windDirAndSpeed->constSortPos = cspPrimitive; + } + + Var *accumTime = (Var*)LangElement::find( "accumTime" ); + if ( !accumTime ) + { + accumTime = new Var( "accumTime", "float" ); + accumTime->uniform = true; + accumTime->constSortPos = cspPass; + } + + // Get the transform to world space. + Var *objTrans = getObjTrans( componentList, fd.features[MFT_UseInstancing], meta ); + + // First check for an input position from a previous feature + // then look for the default vertex position. + Var *inPosition = (Var*)LangElement::find( "inPosition" ); + if ( !inPosition ) + inPosition = (Var*)LangElement::find( "position" ); + + // Copy the input position to the output first as + // the wind effects are conditional. + Var *outPosition = (Var*)LangElement::find( "inPosition" ); + if ( !outPosition ) + { + outPosition = new Var; + outPosition->setType( "float3" ); + outPosition->setName( "inPosition" ); + meta->addStatement( new GenOp(" @ = @.xyz;\r\n", new DecOp( outPosition ), inPosition ) ); + } + + // Get the incoming color data + Var *inColor = (Var*)LangElement::find( "diffuse" ); + + // Do a dynamic branch based on wind force. + if ( GFX->getPixelShaderVersion() >= 3.0f ) + meta->addStatement( new GenOp(" [branch] if ( any( @ ) ) {\r\n", windDirAndSpeed ) ); + + // Do the branch and detail bending first so that + // it can work in pure object space of the tree. + LangElement *effect = + new GenOp( "windBranchBending( " + + "@, " // vPos + "normalize( IN.normal ), " // vNormal + + "@, " // fTime + "@.z, " // fWindSpeed + + "@.g, " // fBranchPhase + "@.y, " // fBranchAmp + "@.r, " // fBranchAtten + + "dot( @[3], 1 ), " // fDetailPhase + "@.z, " // fDetailAmp + "@.w, " // fDetailFreq + + "@.b )", // fEdgeAtten + + outPosition, // vPos + // vNormal + + accumTime, // fTime + windDirAndSpeed, // fWindSpeed + + inColor, // fBranchPhase + windParams, // fBranchAmp + inColor, // fBranchAtten + + objTrans, // fDetailPhase + windParams, // fDetailAmp + windParams, // fDetailFreq + + inColor ); // fEdgeAtten + + meta->addStatement( new GenOp( " @ = @;\r\n", outPosition, effect ) ); + + // Now do the trunk bending. + meta->addStatement( new GenOp(" @ = windTrunkBending( @, @.xy, @.z * @.x );\r\n", + outPosition, outPosition, windDirAndSpeed, outPosition, windParams ) ); + + // End the dynamic branch. + if ( GFX->getPixelShaderVersion() >= 3.0f ) + meta->addStatement( new GenOp(" } // [branch]\r\n" ) ); +} + +ShaderFeatureConstHandles* WindDeformationHLSL::createConstHandles( GFXShader *shader, SimObject *userObject ) +{ + WindDeformationConstHandles *handles = new WindDeformationConstHandles(); + handles->init( shader ); + + return handles; +} diff --git a/Engine/source/forest/hlsl/windDeformationHLSL.h b/Engine/source/forest/hlsl/windDeformationHLSL.h new file mode 100644 index 000000000..8c2c182d8 --- /dev/null +++ b/Engine/source/forest/hlsl/windDeformationHLSL.h @@ -0,0 +1,66 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FOREST_SHADERGEN_WINDDEFORMATIONHLSL_H_ +#define _FOREST_SHADERGEN_WINDDEFORMATIONHLSL_H_ + +#ifndef _SHADERGEN_HLSL_SHADERFEATUREHLSL_H_ +#include "shaderGen/HLSL/shaderFeatureHLSL.h" +#endif +#ifndef _FEATURETYPE_H_ +#include "materials/materialFeatureTypes.h" +#endif + +class GFXShaderConstHandle; + + +class WindDeformationHLSL : public ShaderFeatureHLSL +{ +protected: + + ShaderIncludeDependency mDep; + +public: + + WindDeformationHLSL(); + + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual String getName() + { + return "Wind Effect"; + } + + virtual void determineFeature( Material *material, + const GFXVertexFormat *vertexFormat, + U32 stageNum, + const FeatureType &type, + const FeatureSet &features, + MaterialFeatureData *outFeatureData ); + + virtual ShaderFeatureConstHandles* createConstHandles( GFXShader *shader, SimObject *userObject ); +}; + +DeclareFeatureType( MFT_WindEffect ); + +#endif // _FOREST_SHADERGEN_WINDDEFORMATION_H_ diff --git a/Engine/source/forest/ts/tsForestCellBatch.cpp b/Engine/source/forest/ts/tsForestCellBatch.cpp new file mode 100644 index 000000000..9d176a914 --- /dev/null +++ b/Engine/source/forest/ts/tsForestCellBatch.cpp @@ -0,0 +1,149 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "forest/ts/tsForestCellBatch.h" + +#include "forest/ts/tsForestItemData.h" +#include "scene/sceneManager.h" +#include "ts/tsLastDetail.h" + + +TSForestCellBatch::TSForestCellBatch( TSLastDetail *detail ) + : mDetail( detail ) +{ +} + +TSForestCellBatch::~TSForestCellBatch() +{ +} + +bool TSForestCellBatch::_prepBatch( const ForestItem &item ) +{ + // Make sure it's our item type! + TSForestItemData* data = dynamic_cast( item.getData() ); + if ( !data ) + return false; + + // TODO: Eventually we should atlas multiple details into + // a single combined texture map. Till then we have to + // do one batch per-detail type. + + // If the detail type doesn't match then + // we need to start a new batch. + if ( data->getLastDetail() != mDetail ) + return false; + + return true; +} + +void TSForestCellBatch::_rebuildBatch() +{ + // Clean up first. + mVB = NULL; + if ( mItems.empty() ) + return; + + // How big do we need to make this? + U32 verts = mItems.size() * 6; + mVB.set( GFX, verts, GFXBufferTypeStatic ); + if ( !mVB.isValid() ) + { + // If we failed it is probably because we requested + // a size bigger than a VB can be. Warn the user. + AssertWarn( false, "TSForestCellBatch::_rebuildBatch: Batch too big... try reducing the forest cell size!" ); + return; + } + + // Fill this puppy! + ImposterState *vertPtr = mVB.lock(); + Vector::const_iterator item = mItems.begin(); + + const F32 radius = mDetail->getRadius(); + ImposterState state; + + for ( ; item != mItems.end(); item++ ) + { + item->getWorldBox().getCenter( &state.center ); + state.halfSize = radius * item->getScale(); + state.alpha = 1.0f; + item->getTransform().getColumn( 2, &state.upVec ); + item->getTransform().getColumn( 0, &state.rightVec ); + + *vertPtr = state; + vertPtr->corner = 0; + ++vertPtr; + + *vertPtr = state; + vertPtr->corner = 1; + ++vertPtr; + + *vertPtr = state; + vertPtr->corner = 2; + ++vertPtr; + + *vertPtr = state; + vertPtr->corner = 2; + ++vertPtr; + + *vertPtr = state; + vertPtr->corner = 3; + ++vertPtr; + + *vertPtr = state; + vertPtr->corner = 0; + ++vertPtr; + } + + mVB.unlock(); +} + +void TSForestCellBatch::_render( const SceneRenderState *state ) +{ + if ( !mVB.isValid() || + ( state->isShadowPass() && !TSLastDetail::smCanShadow ) ) + return; + + // Make sure we have a material to render with. + BaseMatInstance *mat = state->getOverrideMaterial( mDetail->getMatInstance() ); + if ( mat == NULL ) + return; + + // We don't really render here... we submit it to + // the render manager which collects all the batches + // in the scene, sorts them by texture, sets up the + // shader, and then renders them all at once. + + ImposterBatchRenderInst *inst = state->getRenderPass()->allocInst(); + inst->mat = mat; + inst->vertBuff = &mVB; + + // We sort by the imposter type first so that RIT_Imposter and + // RIT_ImposterBatches do not get mixed together. + // + // We then sort by material. + // + inst->defaultKey = 0; + inst->defaultKey2 = mat->getStateHint(); + + state->getRenderPass()->addInst( inst ); +} diff --git a/Engine/source/forest/ts/tsForestCellBatch.h b/Engine/source/forest/ts/tsForestCellBatch.h new file mode 100644 index 000000000..a4c79d098 --- /dev/null +++ b/Engine/source/forest/ts/tsForestCellBatch.h @@ -0,0 +1,68 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TSFORESTCELLBATCH_H_ +#define _TSFORESTCELLBATCH_H_ + +#ifndef _FORESTCELLBATCH_H_ +#include "renderInstance/renderImposterMgr.h" +#endif +#ifndef _FORESTCELLBATCH_H_ +#include "forest/forestCellBatch.h" +#endif +#ifndef _GFXTEXTUREHANDLE_H_ +#include "gfx/gfxTextureHandle.h" +#endif +#ifndef _GFXVERTEXBUFFER_H_ +#include "gfx/gfxVertexBuffer.h" +#endif +#ifndef _GFXSTRUCTS_H_ +#include "gfx/gfxStructs.h" +#endif + +class GFXShader; + + +class TSForestCellBatch : public ForestCellBatch +{ +protected: + + /// We use the same shader and vertex format as TSLastDetail. + GFXVertexBufferHandle mVB; + + TSLastDetail *mDetail; + + // ForestCellBatch + virtual bool _prepBatch( const ForestItem &item ); + virtual void _rebuildBatch(); + virtual void _render( const SceneRenderState *state ); + +public: + + TSForestCellBatch( TSLastDetail *detail ); + + virtual ~TSForestCellBatch(); + +}; + + +#endif // _TSFORESTCELLBATCH_H_ diff --git a/Engine/source/forest/ts/tsForestItemData.cpp b/Engine/source/forest/ts/tsForestItemData.cpp new file mode 100644 index 000000000..7a1bebadf --- /dev/null +++ b/Engine/source/forest/ts/tsForestItemData.cpp @@ -0,0 +1,252 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "forest/ts/tsForestItemData.h" + +#include "forest/ts/tsForestCellBatch.h" +#include "core/resourceManager.h" +#include "ts/tsShapeInstance.h" +#include "ts/tsLastDetail.h" +#include "sim/netConnection.h" +#include "materials/materialManager.h" +#include "forest/windDeformation.h" + + +IMPLEMENT_CO_DATABLOCK_V1(TSForestItemData); + +ConsoleDocClass( TSForestItemData, + "@brief Concrete implementation of ForestItemData which loads and renders " + "dts format shapeFiles.\n\n" + "@ingroup Forest" +); + +TSForestItemData::TSForestItemData() + : mShapeInstance( NULL ), + mIsClientObject( false ) +{ +} + +TSForestItemData::~TSForestItemData() +{ +} + +bool TSForestItemData::preload( bool server, String &errorBuffer ) +{ + mIsClientObject = !server; + + if ( !SimDataBlock::preload( server, errorBuffer ) ) + return false; + + return true; +} + +void TSForestItemData::_updateCollisionDetails() +{ + mCollisionDetails.clear(); + mLOSDetails.clear(); + mShape->findColDetails( false, &mCollisionDetails, &mLOSDetails ); +} + +bool TSForestItemData::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + // Register for the resource change signal. + ResourceManager::get().getChangedSignal().notify( this, &TSForestItemData::_onResourceChanged ); + + return true; +} + +void TSForestItemData::onRemove() +{ + // Remove the resource change signal. + ResourceManager::get().getChangedSignal().remove( this, &TSForestItemData::_onResourceChanged ); + + SAFE_DELETE( mShapeInstance ); + + Parent::onRemove(); +} + +void TSForestItemData::inspectPostApply() +{ + Parent::inspectPostApply(); + + SAFE_DELETE( mShapeInstance ); + _loadShape(); +} + +void TSForestItemData::_onResourceChanged( const Torque::Path &path ) +{ + if ( path != Path( mShapeFile ) ) + return; + + SAFE_DELETE( mShapeInstance ); + _loadShape(); + + getReloadSignal().trigger(); +} + +void TSForestItemData::_loadShape() +{ + mShape = ResourceManager::get().load(mShapeFile); + if ( !(bool)mShape ) + return; + + if ( mIsClientObject && + !mShape->preloadMaterialList( mShapeFile ) ) + return; + + // Lets add an autobillboard detail if don't have one. + //_checkLastDetail(); + + _updateCollisionDetails(); +} + +TSShapeInstance* TSForestItemData::_getShapeInstance() const +{ + // Create the shape instance if we haven't already. + if ( !mShapeInstance && mShape ) + { + // Create the instance. + mShapeInstance = new TSShapeInstance( mShape, true ); + + // So we can make OpCode collision calls. + mShapeInstance->prepCollision(); + + // Get the material features adding the wind effect if + // we have a positive wind scale and have vertex color + // data which is used for the weighting. + FeatureSet features = MATMGR->getDefaultFeatures(); + if ( mWindScale > 0.0f && mShape->getVertexFormat()->hasColor() ) + { + // We create our own cloned material list to + // enable the wind effects. + features.addFeature( MFT_WindEffect ); + mShapeInstance->cloneMaterialList( &features ); + } + } + + return mShapeInstance; +} + +void TSForestItemData::_checkLastDetail() +{ + const S32 dl = mShape->mSmallestVisibleDL; + const TSDetail *detail = &mShape->details[dl]; + + // TODO: Expose some real parameters to the datablock maybe? + if ( detail->subShapeNum != -1 ) + { + mShape->addImposter( mShapeFile, 10, 4, 0, 0, 256, 0, 0 ); + + // HACK: If i don't do this it crashes! + while ( mShape->detailCollisionAccelerators.size() < mShape->details.size() ) + mShape->detailCollisionAccelerators.push_back( NULL ); + } +} + +TSLastDetail* TSForestItemData::getLastDetail() const +{ + // Gotta call this first of the last detail isn't created! + if (!_getShapeInstance()) + return NULL; + + const S32 dl = mShape->mSmallestVisibleDL; + const TSDetail* detail = &mShape->details[dl]; + if ( detail->subShapeNum >= 0 || + mShape->billboardDetails.size() <= dl ) + return NULL; + + return mShape->billboardDetails[dl]; +} + +ForestCellBatch* TSForestItemData::allocateBatch() const +{ + TSLastDetail* lastDetail = getLastDetail(); + if ( !lastDetail ) + return NULL; + + return new TSForestCellBatch( lastDetail ); +} + +bool TSForestItemData::canBillboard( const SceneRenderState *state, const ForestItem &item, F32 distToCamera ) const +{ + PROFILE_SCOPE( TSForestItemData_canBillboard ); + + if ( !mShape ) + return false; + + // Use the shape instance to do the work it normally does. + TSShapeInstance *shapeInstance = _getShapeInstance(); + const S32 dl = shapeInstance->setDetailFromDistance( state, distToCamera / item.getScale() ); + + // This item has a null LOD... lets consider + // that as being billboarded. + if ( dl < 0 ) + return true; + + const TSDetail *detail = &mShape->details[dl]; + if ( detail->subShapeNum < 0 && dl < mShape->billboardDetails.size() ) + return true; + + return false; +} + +bool TSForestItemData::render( TSRenderState *rdata, const ForestItem &item ) const +{ + PROFILE_SCOPE( TSForestItemData_render ); + + // This shouldn't happen normally at runtime, but during + // development a file change notification on a bad file + // can cause us to get here without a shape. + TSShapeInstance *shapeInst = _getShapeInstance(); + if ( !shapeInst ) + return false; + + const F32 scale = item.getScale(); + + // Figure out the distance of this item to the camera. + const SceneRenderState *state = rdata->getSceneState(); + F32 dist = ( item.getPosition() - state->getDiffuseCameraPosition() ).len(); + + // TODO: Selecting the lod seems more expensive than + // it should be... we should look to optimize this. + if ( shapeInst->setDetailFromDistance( state, dist / scale ) < 0 ) + return false; + + // TSShapeInstance::render() uses the + // world matrix for the RenderInst. + MatrixF worldMat = item.getTransform(); + worldMat.scale( scale ); + GFX->setWorldMatrix( worldMat ); + rdata->setMaterialHint( (void*)&item ); + + // This isn't documented well, but these calls + // don't really render... rather they batch the + // shape to be rendered by the render instance + // manager later on. + shapeInst->animate(); + shapeInst->render( *rdata ); + return true; +} \ No newline at end of file diff --git a/Engine/source/forest/ts/tsForestItemData.h b/Engine/source/forest/ts/tsForestItemData.h new file mode 100644 index 000000000..197069c9f --- /dev/null +++ b/Engine/source/forest/ts/tsForestItemData.h @@ -0,0 +1,98 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TSFORESTITEMDATA_H_ +#define _TSFORESTITEMDATA_H_ + +#ifndef _FORESTITEM_H_ +#include "forest/forestItem.h" +#endif +#ifndef _TSSHAPE_H_ +#include "ts/tsShape.h" +#endif +#ifndef _RESOURCEMANAGER_H_ +#include "core/resourceManager.h" +#endif + + +class TSShapeInstance; +class TSLastDetail; + + +class TSForestItemData : public ForestItemData +{ +protected: + + typedef ForestItemData Parent; + + bool mIsClientObject; + + // This is setup during forest creation. + mutable TSShapeInstance *mShapeInstance; + + Resource mShape; + + Vector mCollisionDetails; + Vector mLOSDetails; + + TSShapeInstance* _getShapeInstance() const; + + void _loadShape(); + + void _onResourceChanged( const Torque::Path &path ); + + void _checkLastDetail(); + + void _updateCollisionDetails(); + + // ForestItemData + void _preload() { _loadShape(); } + +public: + + DECLARE_CONOBJECT(TSForestItemData); + TSForestItemData(); + virtual ~TSForestItemData(); + + bool preload( bool server, String &errorBuffer ); + void onRemove(); + bool onAdd(); + + virtual void inspectPostApply(); + + TSLastDetail* getLastDetail() const; + + // JCF: need access to this to build convex(s) in ForestConvex + TSShapeInstance* getShapeInstance() const { return _getShapeInstance(); } + + const Vector& getCollisionDetails() const { return mCollisionDetails; } + const Vector& getLOSDetails() const { return mLOSDetails; } + + // ForestItemData + const Box3F& getObjBox() const { return mShape ? mShape->bounds : Box3F::Invalid; } + bool render( TSRenderState *rdata, const ForestItem& item ) const; + ForestCellBatch* allocateBatch() const; + bool canBillboard( const SceneRenderState *state, const ForestItem &item, F32 distToCamera ) const; + bool buildPolyList( const ForestItem& item, AbstractPolyList *polyList, const Box3F *box ) const { return false; } +}; + +#endif // _TSFORESTITEMDATA_H_ diff --git a/Engine/source/forest/windDeformation.cpp b/Engine/source/forest/windDeformation.cpp new file mode 100644 index 000000000..e258981a2 --- /dev/null +++ b/Engine/source/forest/windDeformation.cpp @@ -0,0 +1,134 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "forest/windDeformation.h" + +#include "forest/forestItem.h" +#include "forest/forestWindMgr.h" +#include "forest/forestWindAccumulator.h" +#include "materials/sceneData.h" +#include "scene/sceneRenderState.h" +#include "gfx/gfxShader.h" +#include "gfx/gfxStructs.h" + + +ImplementFeatureType( MFT_WindEffect, MFG_PreTransform, 0, false ); + + +void WindDeformationConstHandles::init( GFXShader *shader ) +{ + mWindDirAndSpeed = shader->getShaderConstHandle( "$windDirAndSpeed" ); + mWindParams = shader->getShaderConstHandle( "$windParams" ); +} + +void WindDeformationConstHandles::setConsts( SceneRenderState *state, + const SceneData &sgData, + GFXShaderConstBuffer *buffer ) +{ + PROFILE_SCOPE( WindDeformationConstHandles_setConsts ); + + Point3F windDir( 0, 0, 0 ); + F32 windSpeed = 0; + F32 bendScale = 0; + F32 branchAmp = 0; + F32 detailAmp = 0; + F32 detailFreq = 0; + + const ForestItem *item = (const ForestItem*)sgData.materialHint; + + ForestWindAccumulator *wind = NULL; + if ( item ) + { + // First setup the per-datablock shader constants. + // + // These cannot be skipped as they modifiy the rest + // state of a tree even without wind. + // + const ForestItemData *itemData = item->getData(); + const F32 windScale = itemData->mWindScale; + bendScale = itemData->mTrunkBendScale * windScale; + branchAmp = itemData->mWindBranchAmp * windScale; + detailAmp = itemData->mWindDetailAmp * windScale; + detailFreq = itemData->mWindDetailFreq * windScale; + + // Look for wind state. + wind = WINDMGR->getLocalWind( item->getKey() ); + } + + if ( wind ) + { + // Calculate distance to camera fade. + F32 toCamLen = ( state->getDiffuseCameraPosition() - wind->getPosition()).len(); + F32 toCamInterp = 1.0f - (toCamLen / ForestWindMgr::smWindEffectRadius); + toCamInterp = mClampF( toCamInterp, 0.0f, 1.0f ); + + windDir.set( wind->getDirection().x, wind->getDirection().y, 0.0f ); + windSpeed = wind->getStrength(); + + // Since the effect is just a displacement + // of geometry i need to scale it up for smaller + // trees so it looks like its affecting it more. + // + // TODO: This is possibly a side effect of not + // properly doing the displacement physics in the + // first place. + // + const F32 strengthScale = 1.0f / item->getScale(); + windDir *= strengthScale; + windSpeed *= strengthScale; + + // Add in fade. + windDir *= toCamInterp; + windSpeed *= toCamInterp; + + MatrixF invXfm( item->getTransform() ); + invXfm.inverse(); + invXfm.mulV( windDir ); + } + else + { + /* + // HACK: This should go away... we need to work out a strategy + // for allocating accumulators that accounts for random objects + // and not just forest elements. + // + // Maybe add a generic 'id' value to SceneData that is set + // by the RenderInst that can be any unique identifier for that + // object that we can used down here to fetch an item id. + + // TODO: This is horrible... we should avoid access to the script + // system while rendering as its really slow! + if ( Con::getBoolVariable( "$tsStaticWindHack", false ) ) + { + VectorF windVec( mSin( Sim::getCurrentTime() / 1000.0f ) * 0.25f, 0.0f, 0.0f ); + //sgData.objTrans.mulV( windVec ); + windDir.x = windVec.x; + windDir.y = windVec.y; + windSpeed = 0.75f; + } + */ + } + + buffer->setSafe( mWindDirAndSpeed, Point3F( windDir.x, windDir.y, windSpeed ) ); + buffer->setSafe( mWindParams, Point4F( bendScale, branchAmp, detailAmp, detailFreq ) ); +} diff --git a/Engine/source/forest/windDeformation.h b/Engine/source/forest/windDeformation.h new file mode 100644 index 000000000..46c2f7b8f --- /dev/null +++ b/Engine/source/forest/windDeformation.h @@ -0,0 +1,52 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FOREST_SHADERGEN_WINDDEFORMATION_H_ +#define _FOREST_SHADERGEN_WINDDEFORMATION_H_ + +#ifndef _SHADERGEN_H_ +#include "shaderGen/shaderGen.h" +#endif +#ifndef _FEATURETYPE_H_ +#include "materials/materialFeatureTypes.h" +#endif + +class GFXShaderConstHandle; + +class WindDeformationConstHandles : public ShaderFeatureConstHandles +{ +public: + + GFXShaderConstHandle *mWindDirAndSpeed; + GFXShaderConstHandle *mWindParams; + + // ShaderFeatureConstHandles + virtual void init( GFXShader *shader ); + virtual void setConsts( SceneRenderState *state, + const SceneData &sgData, + GFXShaderConstBuffer *buffer ); +}; + + +DeclareFeatureType( MFT_WindEffect ); + +#endif // _FOREST_SHADERGEN_WINDDEFORMATION_H_ diff --git a/Engine/source/gfx/D3D9/d3dx9Functions.h b/Engine/source/gfx/D3D9/d3dx9Functions.h new file mode 100644 index 000000000..633faf935 --- /dev/null +++ b/Engine/source/gfx/D3D9/d3dx9Functions.h @@ -0,0 +1,148 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +D3DX_FUNCTION( D3DXCreateBuffer, HRESULT, + (DWORD NumBytes, + LPD3DXBUFFER * ppBuffer) ) + +D3DX_FUNCTION( D3DXSaveSurfaceToFileA, HRESULT, + (LPCSTR pDestFile, + D3DXIMAGE_FILEFORMAT DestFormat, + LPDIRECT3DSURFACE9 pSrcSurface, + CONST PALETTEENTRY* pSrcPalette, + CONST RECT* pSrcRect) ) + +D3DX_FUNCTION( D3DXSaveSurfaceToFileW, HRESULT, + (LPCWSTR pDestFile, + D3DXIMAGE_FILEFORMAT DestFormat, + LPDIRECT3DSURFACE9 pSrcSurface, + CONST PALETTEENTRY* pSrcPalette, + CONST RECT* pSrcRect) ) + +D3DX_FUNCTION( D3DXCompileShader, HRESULT, + (LPCSTR pSrcData, + UINT srcDataLen, + CONST D3DXMACRO* pDefines, + LPD3DXINCLUDE pInclude, + LPCSTR pFunctionName, + LPCSTR pProfile, + DWORD Flags, + LPD3DXBUFFER* ppShader, + LPD3DXBUFFER* ppErrorMsgs, + LPD3DXCONSTANTTABLE * ppConstantTable) ) + +D3DX_FUNCTION( D3DXGetShaderConstantTable, HRESULT, + (CONST DWORD* pFunction, + LPD3DXCONSTANTTABLE* ppConstantTable) ) + + +D3DX_FUNCTION( D3DXLoadSurfaceFromSurface, HRESULT, + (LPDIRECT3DSURFACE9 pDestSurface, + CONST PALETTEENTRY* pDestPalette, + CONST RECT* pDestRect, + LPDIRECT3DSURFACE9 pSrcSurface, + CONST PALETTEENTRY* pSrcPalette, + CONST RECT* pSrcRect, + DWORD Filter, + D3DCOLOR ColorKey) ) + +D3DX_FUNCTION( D3DXCreateVolumeTexture, HRESULT, + (LPDIRECT3DDEVICE9 pDevice, + UINT Width, + UINT Height, + UINT Depth, + UINT MipLevels, + DWORD Usage, + D3DFORMAT Format, + D3DPOOL Pool, + LPDIRECT3DVOLUMETEXTURE9* ppVolumeTexture) ) + +D3DX_FUNCTION( D3DXCreateTexture, HRESULT, + (LPDIRECT3DDEVICE9 pDevice, + UINT Width, + UINT Height, + UINT MipLevels, + DWORD Usage, + D3DFORMAT Format, + D3DPOOL Pool, + LPDIRECT3DTEXTURE9* ppTexture) ) + +#ifdef TORQUE_OS_XENON +D3DX_FUNCTION( D3DXLoadVolumeFromMemory, HRESULT, + (LPDIRECT3DVOLUME9 pDestVolume, + CONST PALETTEENTRY* pDestPalette, + CONST D3DBOX* pDestBox, + LPCVOID pSrcMemory, + D3DFORMAT SrcFormat, + UINT SrcRowPitch, + UINT SrcSlicePitch, + CONST PALETTEENTRY* pSrcPalette, + CONST D3DBOX* pSrcBox, + BOOL SrcParentPacked, + UINT SrcParentWidth, + UINT SrcParentHeight, + UINT SrcParentDepth, + DWORD Filter, + D3DCOLOR ColorKey) ) +#else +D3DX_FUNCTION( D3DXLoadVolumeFromMemory, HRESULT, + (LPDIRECT3DVOLUME9 pDestVolume, + CONST PALETTEENTRY* pDestPalette, + CONST D3DBOX* pDestBox, + LPCVOID pSrcMemory, + D3DFORMAT SrcFormat, + UINT SrcRowPitch, + UINT SrcSlicePitch, + CONST PALETTEENTRY* pSrcPalette, + CONST D3DBOX* pSrcBox, + DWORD Filter, + D3DCOLOR ColorKey) ) +#endif + +D3DX_FUNCTION( D3DXSaveTextureToFileInMemory, HRESULT, + (LPD3DXBUFFER *ppDestBuf, + D3DXIMAGE_FILEFORMAT DestFormat, + LPDIRECT3DBASETEXTURE9 pSrcTexture, + const PALETTEENTRY *pSrcPalette) ) + +D3DX_FUNCTION( D3DXGetImageInfoFromFileInMemory, HRESULT, + (LPCVOID pSrcData, + UINT SrcDataSize, + D3DXIMAGE_INFO* pSrcInfo) ) + +D3DX_FUNCTION( D3DXLoadSurfaceFromFileInMemory, HRESULT, + (LPDIRECT3DSURFACE9 pDestSurface, + CONST PALETTEENTRY * pDestPalette, + CONST RECT * pDestRect, + LPCVOID pSrcData, + UINT SrcData, + CONST RECT * pSrcRect, + DWORD Filter, + D3DCOLOR ColorKey, + D3DXIMAGE_INFO * pSrcInfo) ) + +D3DX_FUNCTION( D3DXSaveSurfaceToFileInMemory, HRESULT, + (LPD3DXBUFFER* ppDestBuf, + D3DXIMAGE_FILEFORMAT DestFormat, + LPDIRECT3DSURFACE9 pSrcSurface, + CONST PALETTEENTRY* pSrcPalette, + CONST RECT* pSrcRect) ) diff --git a/Engine/source/gfx/D3D9/gfxD3D9CardProfiler.cpp b/Engine/source/gfx/D3D9/gfxD3D9CardProfiler.cpp new file mode 100644 index 000000000..f198b8007 --- /dev/null +++ b/Engine/source/gfx/D3D9/gfxD3D9CardProfiler.cpp @@ -0,0 +1,161 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/D3D9/gfxD3D9Device.h" +#include "console/console.h" +#include "gfx/primBuilder.h" +#include "gfx/D3D9/gfxD3D9CardProfiler.h" +#include "gfx/D3D9/gfxD3D9EnumTranslate.h" +#ifdef TORQUE_OS_WIN32 +#include "platformWin32/videoInfo/wmiVideoInfo.h" +#endif + + +GFXD3D9CardProfiler::GFXD3D9CardProfiler() : GFXCardProfiler() +{ + +} + +GFXD3D9CardProfiler::~GFXD3D9CardProfiler() +{ + +} + +void GFXD3D9CardProfiler::init() +{ + mD3DDevice = dynamic_cast(GFX)->getDevice(); + AssertISV( mD3DDevice, "GFXD3D9CardProfiler::init() - No D3D9 Device found!"); + + // Grab the caps so we can get our adapter ordinal and look up our name. + D3DCAPS9 caps; + D3D9Assert(mD3DDevice->GetDeviceCaps(&caps), "GFXD3D9CardProfiler::init - failed to get device caps!"); + +#ifdef TORQUE_OS_XENON + mCardDescription = "Xbox360 GPU"; + mChipSet = "ATI"; + mVersionString = String::ToString(_XDK_VER); + mVideoMemory = 512 * 1024 * 1024; +#else + WMIVideoInfo wmiVidInfo; + if( wmiVidInfo.profileAdapters() ) + { + const PlatformVideoInfo::PVIAdapter &adapter = wmiVidInfo.getAdapterInformation( caps.AdapterOrdinal ); + + mCardDescription = adapter.description; + mChipSet = adapter.chipSet; + mVersionString = adapter.driverVersion; + mVideoMemory = adapter.vram; + } +#endif + + Parent::init(); +} + +void GFXD3D9CardProfiler::setupCardCapabilities() +{ + // Get the D3D device caps + D3DCAPS9 caps; + mD3DDevice->GetDeviceCaps(&caps); + + setCapability( "autoMipMapLevel", ( caps.Caps2 & D3DCAPS2_CANAUTOGENMIPMAP ? 1 : 0 ) ); + + setCapability( "maxTextureWidth", caps.MaxTextureWidth ); + setCapability( "maxTextureHeight", caps.MaxTextureHeight ); + setCapability( "maxTextureSize", getMin( (U32)caps.MaxTextureWidth, (U32)caps.MaxTextureHeight) ); + + bool canDoLERPDetailBlend = ( caps.TextureOpCaps & D3DTEXOPCAPS_LERP ) && ( caps.MaxTextureBlendStages > 1 ); + + bool canDoFourStageDetailBlend = ( caps.TextureOpCaps & D3DTEXOPCAPS_SUBTRACT ) && + ( caps.PrimitiveMiscCaps & D3DPMISCCAPS_TSSARGTEMP ) && + ( caps.MaxTextureBlendStages > 3 ); + + setCapability( "lerpDetailBlend", canDoLERPDetailBlend ); + setCapability( "fourStageDetailBlend", canDoFourStageDetailBlend ); +} + +bool GFXD3D9CardProfiler::_queryCardCap(const String &query, U32 &foundResult) +{ + return 0; +} + +bool GFXD3D9CardProfiler::_queryFormat( const GFXFormat fmt, const GFXTextureProfile *profile, bool &inOutAutogenMips ) +{ + LPDIRECT3D9 pD3D = static_cast(GFX)->getD3D(); + D3DDISPLAYMODE displayMode = static_cast(GFX)->getDisplayMode(); + + DWORD usage = 0; + D3DRESOURCETYPE rType = D3DRTYPE_TEXTURE; + D3DFORMAT adapterFormat = displayMode.Format; + + D3DFORMAT texFormat = GFXD3D9TextureFormat[fmt]; + +#if defined(TORQUE_OS_XENON) + inOutAutogenMips = false; + adapterFormat = D3DFMT_A8B8G8R8; + + if(profile->isRenderTarget()) + { + texFormat = (D3DFORMAT)MAKELEFMT(texFormat); + } +#else + if( profile->isRenderTarget() ) + usage |= D3DUSAGE_RENDERTARGET; + else if( profile->isZTarget() ) + { + usage |= D3DUSAGE_DEPTHSTENCIL; + rType = D3DRTYPE_SURFACE; + } + + if( inOutAutogenMips ) + usage |= D3DUSAGE_AUTOGENMIPMAP; +#endif + + // Early-check to see if the enum translation table has an unsupported value + if(texFormat == (_D3DFORMAT)GFX_UNSUPPORTED_VAL) + return false; + + HRESULT hr = pD3D->CheckDeviceFormat( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, + adapterFormat, usage, rType, texFormat ); + + bool retVal = SUCCEEDED( hr ); + +#if !defined(TORQUE_OS_XENON) + // If check device format failed, and auto gen mips were requested, try again + // without autogen mips. + if( !retVal && inOutAutogenMips ) + { + usage ^= D3DUSAGE_AUTOGENMIPMAP; + + hr = pD3D->CheckDeviceFormat( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, + adapterFormat, usage, D3DRTYPE_TEXTURE, GFXD3D9TextureFormat[fmt] ); + + retVal = SUCCEEDED( hr ); + + // If this one passed, auto gen mips are not supported with this format, + // so set the variable properly + if( retVal ) + inOutAutogenMips = false; + } +#endif + + return retVal; +} \ No newline at end of file diff --git a/Engine/source/gfx/D3D9/gfxD3D9CardProfiler.h b/Engine/source/gfx/D3D9/gfxD3D9CardProfiler.h new file mode 100644 index 000000000..f97f5d228 --- /dev/null +++ b/Engine/source/gfx/D3D9/gfxD3D9CardProfiler.h @@ -0,0 +1,51 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXD3D9CARDPROFILER_H_ +#define _GFXD3D9CARDPROFILER_H_ + + +#include "gfx/gfxCardProfile.h" + +struct IDirect3DDevice9; +class GFXD3D9CardProfiler : public GFXCardProfiler +{ +private: + typedef GFXCardProfiler Parent; + + IDirect3DDevice9 *mD3DDevice; + UINT mAdapterOrdinal; + +public: + GFXD3D9CardProfiler(); + ~GFXD3D9CardProfiler(); + void init(); + +protected: + const String &getRendererString() const { static String sRS("D3D9"); return sRS; } + + void setupCardCapabilities(); + bool _queryCardCap(const String &query, U32 &foundResult); + bool _queryFormat(const GFXFormat fmt, const GFXTextureProfile *profile, bool &inOutAutogenMips); +}; + +#endif diff --git a/Engine/source/gfx/D3D9/gfxD3D9Cubemap.cpp b/Engine/source/gfx/D3D9/gfxD3D9Cubemap.cpp new file mode 100644 index 000000000..ff46a3d22 --- /dev/null +++ b/Engine/source/gfx/D3D9/gfxD3D9Cubemap.cpp @@ -0,0 +1,239 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/D3D9/gfxD3D9Cubemap.h" +#include "gfx/gfxTextureManager.h" +#include "gfx/D3D9/gfxD3D9EnumTranslate.h" + +_D3DCUBEMAP_FACES GFXD3D9Cubemap::faceList[6] = +{ + D3DCUBEMAP_FACE_POSITIVE_X, D3DCUBEMAP_FACE_NEGATIVE_X, + D3DCUBEMAP_FACE_POSITIVE_Y, D3DCUBEMAP_FACE_NEGATIVE_Y, + D3DCUBEMAP_FACE_POSITIVE_Z, D3DCUBEMAP_FACE_NEGATIVE_Z +}; + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +GFXD3D9Cubemap::GFXD3D9Cubemap() +{ + mCubeTex = NULL; + mDynamic = false; + mFaceFormat = GFXFormatR8G8B8A8; +} + +//----------------------------------------------------------------------------- +// Destructor +//----------------------------------------------------------------------------- +GFXD3D9Cubemap::~GFXD3D9Cubemap() +{ + releaseSurfaces(); + + if ( mDynamic ) + GFXTextureManager::removeEventDelegate( this, &GFXD3D9Cubemap::_onTextureEvent ); +} + +//----------------------------------------------------------------------------- +// Release D3D surfaces +//----------------------------------------------------------------------------- +void GFXD3D9Cubemap::releaseSurfaces() +{ + if ( !mCubeTex ) + return; + + mCubeTex->Release(); + mCubeTex = NULL; +} + +void GFXD3D9Cubemap::_onTextureEvent( GFXTexCallbackCode code ) +{ + // Can this happen? + if ( !mDynamic ) + return; + + if ( code == GFXZombify ) + releaseSurfaces(); + else if ( code == GFXResurrect ) + initDynamic( mTexSize ); +} + +//----------------------------------------------------------------------------- +// Init Static +//----------------------------------------------------------------------------- +void GFXD3D9Cubemap::initStatic( GFXTexHandle *faces ) +{ + //if( mCubeTex ) + // return; + + if( faces ) + { + AssertFatal( faces[0], "empty texture passed to CubeMap::create" ); + + GFXD3D9Device *dev = static_cast(GFX); + + D3DPOOL pool = D3DPOOL_MANAGED; + + if (dev->isD3D9Ex()) + pool = D3DPOOL_DEFAULT; + + LPDIRECT3DDEVICE9 D3D9Device = dev->getDevice(); + + // NOTE - check tex sizes on all faces - they MUST be all same size + mTexSize = faces[0].getWidth(); + mFaceFormat = faces[0].getFormat(); + + D3D9Assert( D3D9Device->CreateCubeTexture( mTexSize, 1, 0, GFXD3D9TextureFormat[mFaceFormat], + pool, &mCubeTex, NULL ), NULL ); + + fillCubeTextures( faces, D3D9Device ); +// mCubeTex->GenerateMipSubLevels(); + } +} + +void GFXD3D9Cubemap::initStatic( DDSFile *dds ) +{ + AssertFatal( dds, "GFXD3D9Cubemap::initStatic - Got null DDS file!" ); + AssertFatal( dds->isCubemap(), "GFXD3D9Cubemap::initStatic - Got non-cubemap DDS file!" ); + AssertFatal( dds->mSurfaces.size() == 6, "GFXD3D9Cubemap::initStatic - DDS has less than 6 surfaces!" ); + + GFXD3D9Device *dev = static_cast(GFX); + + D3DPOOL pool = D3DPOOL_MANAGED; + + if (dev->isD3D9Ex()) + pool = D3DPOOL_DEFAULT; + + LPDIRECT3DDEVICE9 D3D9Device = dev->getDevice(); + + // NOTE - check tex sizes on all faces - they MUST be all same size + mTexSize = dds->getWidth(); + mFaceFormat = dds->getFormat(); + U32 levels = dds->getMipLevels(); + + D3D9Assert( D3D9Device->CreateCubeTexture( mTexSize, levels, 0, GFXD3D9TextureFormat[mFaceFormat], + pool, &mCubeTex, NULL ), NULL ); + + for( U32 i=0; i < 6; i++ ) + { + if ( !dds->mSurfaces[i] ) + { + // TODO: The DDS can skip surfaces, but i'm unsure what i should + // do here when creating the cubemap. Ignore it for now. + continue; + } + + // Now loop thru the mip levels! + for( U32 l = 0; l < levels; l++ ) + { + IDirect3DSurface9 *cubeSurf = NULL; + D3D9Assert( mCubeTex->GetCubeMapSurface( faceList[i], l, &cubeSurf ), + "GFXD3D9Cubemap::initStatic - Get cubemap surface failed!" ); + + // Lock the dest surface. + D3DLOCKED_RECT lockedRect; + D3D9Assert( cubeSurf->LockRect( &lockedRect, NULL, 0 ), + "GFXD3D9Cubemap::initStatic - Failed to lock surface level for load!" ); + + dMemcpy( lockedRect.pBits, dds->mSurfaces[i]->mMips[l], dds->getSurfaceSize(l) ); + + cubeSurf->UnlockRect(); + cubeSurf->Release(); + } + } +} + +//----------------------------------------------------------------------------- +// Init Dynamic +//----------------------------------------------------------------------------- +void GFXD3D9Cubemap::initDynamic( U32 texSize, GFXFormat faceFormat ) +{ + if ( mCubeTex ) + return; + + if ( !mDynamic ) + GFXTextureManager::addEventDelegate( this, &GFXD3D9Cubemap::_onTextureEvent ); + + mDynamic = true; + mTexSize = texSize; + mFaceFormat = faceFormat; + + LPDIRECT3DDEVICE9 D3D9Device = reinterpret_cast(GFX)->getDevice(); + + // might want to try this as a 16 bit texture... + D3D9Assert( D3D9Device->CreateCubeTexture( texSize, + 1, +#ifdef TORQUE_OS_XENON + 0, +#else + D3DUSAGE_RENDERTARGET, +#endif + GFXD3D9TextureFormat[faceFormat], + D3DPOOL_DEFAULT, + &mCubeTex, + NULL ), NULL ); +} + +//----------------------------------------------------------------------------- +// Fills in face textures of cube map from existing textures +//----------------------------------------------------------------------------- +void GFXD3D9Cubemap::fillCubeTextures( GFXTexHandle *faces, LPDIRECT3DDEVICE9 D3DDevice ) +{ + for( U32 i=0; i<6; i++ ) + { + // get cube face surface + IDirect3DSurface9 *cubeSurf = NULL; + D3D9Assert( mCubeTex->GetCubeMapSurface( faceList[i], 0, &cubeSurf ), NULL ); + + // get incoming texture surface + GFXD3D9TextureObject *texObj = dynamic_cast( (GFXTextureObject*)faces[i] ); + IDirect3DSurface9 *inSurf; + D3D9Assert( texObj->get2DTex()->GetSurfaceLevel( 0, &inSurf ), NULL ); + + // copy incoming texture into cube face + D3D9Assert( GFXD3DX.D3DXLoadSurfaceFromSurface( cubeSurf, NULL, NULL, inSurf, NULL, + NULL, D3DX_FILTER_NONE, 0 ), NULL ); + cubeSurf->Release(); + inSurf->Release(); + } +} + +//----------------------------------------------------------------------------- +// Set the cubemap to the specified texture unit num +//----------------------------------------------------------------------------- +void GFXD3D9Cubemap::setToTexUnit( U32 tuNum ) +{ + static_cast(GFX)->getDevice()->SetTexture( tuNum, mCubeTex ); +} + +void GFXD3D9Cubemap::zombify() +{ + // Static cubemaps are handled by D3D + if( mDynamic ) + releaseSurfaces(); +} + +void GFXD3D9Cubemap::resurrect() +{ + // Static cubemaps are handled by D3D + if( mDynamic ) + initDynamic( mTexSize, mFaceFormat ); +} diff --git a/Engine/source/gfx/D3D9/gfxD3D9Cubemap.h b/Engine/source/gfx/D3D9/gfxD3D9Cubemap.h new file mode 100644 index 000000000..cf95bd0be --- /dev/null +++ b/Engine/source/gfx/D3D9/gfxD3D9Cubemap.h @@ -0,0 +1,72 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXD3D9CUBEMAP_H_ +#define _GFXD3D9CUBEMAP_H_ + +#include "gfx/D3D9/gfxD3D9Device.h" +#include "gfx/gfxCubemap.h" +#include "gfx/gfxResource.h" +#include "gfx/gfxTarget.h" + +//************************************************************************** +// Cube map +//************************************************************************** +class GFXD3D9Cubemap : public GFXCubemap +{ +public: + virtual void initStatic( GFXTexHandle *faces ); + virtual void initStatic( DDSFile *dds ); + virtual void initDynamic( U32 texSize, GFXFormat faceFormat = GFXFormatR8G8B8A8 ); + virtual void setToTexUnit( U32 tuNum ); + virtual U32 getSize() const { return mTexSize; } + virtual GFXFormat getFormat() const { return mFaceFormat; } + + GFXD3D9Cubemap(); + virtual ~GFXD3D9Cubemap(); + + // GFXResource interface + virtual void zombify(); + virtual void resurrect(); + +protected: + + friend class GFXPCD3D9TextureTarget; + friend class GFX360TextureTarget; + friend class GFXD3D9Device; + + LPDIRECT3DCUBETEXTURE9 mCubeTex; + + static _D3DCUBEMAP_FACES faceList[6]; + bool mDynamic; + U32 mTexSize; + GFXFormat mFaceFormat; + + void fillCubeTextures( GFXTexHandle *faces, LPDIRECT3DDEVICE9 D3DDevice ); + void releaseSurfaces(); + + /// The callback used to get texture events. + /// @see GFXTextureManager::addEventDelegate + void _onTextureEvent( GFXTexCallbackCode code ); +}; + +#endif // GFXD3D9CUBEMAP diff --git a/Engine/source/gfx/D3D9/gfxD3D9Device.cpp b/Engine/source/gfx/D3D9/gfxD3D9Device.cpp new file mode 100644 index 000000000..4888deec0 --- /dev/null +++ b/Engine/source/gfx/D3D9/gfxD3D9Device.cpp @@ -0,0 +1,1145 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/D3D9/gfxD3D9Device.h" + +#include "console/console.h" +#include "core/stream/fileStream.h" +#include "core/strings/unicode.h" +#include "gfx/D3D9/gfxD3D9CardProfiler.h" +#include "gfx/D3D9/gfxD3D9VertexBuffer.h" +#include "gfx/D3D9/screenShotD3D9.h" +#include "gfx/D3D9/gfxD3D9EnumTranslate.h" +#include "gfx/D3D9/gfxD3D9QueryFence.h" +#include "gfx/D3D9/gfxD3D9OcclusionQuery.h" +#include "gfx/D3D9/gfxD3D9Shader.h" +#include "windowManager/platformWindow.h" +#ifndef TORQUE_OS_XENON +# include "windowManager/win32/win32Window.h" +#endif + +D3DXFNTable GFXD3D9Device::smD3DX; + +GFXD3D9Device::GFXD3D9Device( LPDIRECT3D9 d3d, U32 index ) +{ + mDeviceSwizzle32 = &Swizzles::bgra; + GFXVertexColor::setSwizzle( mDeviceSwizzle32 ); + + mDeviceSwizzle24 = &Swizzles::bgr; + + mD3D = d3d; + mAdapterIndex = index; + mD3DDevice = NULL; + mVolatileVB = NULL; + + mCurrentPB = NULL; + mDynamicPB = NULL; + + mLastVertShader = NULL; + mLastPixShader = NULL; + + mCanCurrentlyRender = false; + mTextureManager = NULL; + mCurrentStateBlock = NULL; + mResourceListHead = NULL; + +#ifdef TORQUE_DEBUG + mVBListHead = NULL; + mNumAllocatedVertexBuffers = 0; +#endif + + mPixVersion = 0.0; + mNumSamplers = 0; + mNumRenderTargets = 0; + + mCardProfiler = NULL; + + mDeviceDepthStencil = NULL; + mDeviceBackbuffer = NULL; + mDeviceColor = NULL; + + mCreateFenceType = -1; // Unknown, test on first allocate + + mCurrentConstBuffer = NULL; + + mOcclusionQuerySupported = false; + + // Set up the Enum translation tables + GFXD3D9EnumTranslate::init(); + +#if !defined(TORQUE_OS_XENON) + mD3DEx = NULL; + mD3DDeviceEx = NULL; +#endif +} + +//----------------------------------------------------------------------------- + +GFXD3D9Device::~GFXD3D9Device() +{ + // Release our refcount on the current stateblock object + mCurrentStateBlock = NULL; + + releaseDefaultPoolResources(); + + // Free the vertex declarations. + VertexDeclMap::Iterator iter = mVertexDecls.begin(); + for ( ; iter != mVertexDecls.end(); iter++ ) + delete iter->value; + + // Check up on things + Con::printf("Cur. D3DDevice ref count=%d", mD3DDevice->AddRef() - 1); + mD3DDevice->Release(); + + // Forcibly clean up the pools + mVolatileVBList.setSize(0); + mDynamicPB = NULL; + + // And release our D3D resources. + SAFE_RELEASE( mDeviceDepthStencil ); + SAFE_RELEASE( mDeviceBackbuffer ) + SAFE_RELEASE( mDeviceColor ); + SAFE_RELEASE( mD3D ); + SAFE_RELEASE( mD3DDevice ); + +#ifdef TORQUE_DEBUG + logVertexBuffers(); +#endif + + if( mCardProfiler ) + { + delete mCardProfiler; + mCardProfiler = NULL; + } + + if( gScreenShot ) + { + delete gScreenShot; + gScreenShot = NULL; + } +} + +//------------------------------------------------------------------------------ +// setupGenericShaders - This function is totally not needed on PC because there +// is fixed-function support in D3D9 +//------------------------------------------------------------------------------ +inline void GFXD3D9Device::setupGenericShaders( GenericShaderType type /* = GSColor */ ) +{ +#ifdef WANT_TO_SIMULATE_UI_ON_360 + if( mGenericShader[GSColor] == NULL ) + { + mGenericShader[GSColor] = createShader( "shaders/common/genericColorV.hlsl", + "shaders/common/genericColorP.hlsl", + 2.f ); + + mGenericShader[GSModColorTexture] = createShader( "shaders/common/genericModColorTextureV.hlsl", + "shaders/common/genericModColorTextureP.hlsl", + 2.f ); + + mGenericShader[GSAddColorTexture] = createShader( "shaders/common/genericAddColorTextureV.hlsl", + "shaders/common/genericAddColorTextureP.hlsl", + 2.f ); + } + + mGenericShader[type]->process(); + + MatrixF world, view, proj; + mWorldMatrix[mWorldStackSize].transposeTo( world ); + mViewMatrix.transposeTo( view ); + mProjectionMatrix.transposeTo( proj ); + + mTempMatrix = world * view * proj; + + setVertexShaderConstF( VC_WORLD_PROJ, (F32 *)&mTempMatrix, 4 ); +#else + disableShaders(); +#endif +} + +//----------------------------------------------------------------------------- +/// Creates a state block object based on the desc passed in. This object +/// represents an immutable state. +GFXStateBlockRef GFXD3D9Device::createStateBlockInternal(const GFXStateBlockDesc& desc) +{ + return GFXStateBlockRef(new GFXD3D9StateBlock(desc, mD3DDevice)); +} + +/// Activates a stateblock +void GFXD3D9Device::setStateBlockInternal(GFXStateBlock* block, bool force) +{ + AssertFatal(dynamic_cast(block), "Incorrect stateblock type for this device!"); + GFXD3D9StateBlock* d3dBlock = static_cast(block); + GFXD3D9StateBlock* d3dCurrent = static_cast(mCurrentStateBlock.getPointer()); + if (force) + d3dCurrent = NULL; + d3dBlock->activate(d3dCurrent); +} + +/// Called by base GFXDevice to actually set a const buffer +void GFXD3D9Device::setShaderConstBufferInternal(GFXShaderConstBuffer* buffer) +{ + if (buffer) + { + PROFILE_SCOPE(GFXD3D9Device_setShaderConstBufferInternal); + AssertFatal(dynamic_cast(buffer), "Incorrect shader const buffer type for this device!"); + GFXD3D9ShaderConstBuffer* d3dBuffer = static_cast(buffer); + + d3dBuffer->activate(mCurrentConstBuffer); + mCurrentConstBuffer = d3dBuffer; + } else { + mCurrentConstBuffer = NULL; + } +} + +//----------------------------------------------------------------------------- + +void GFXD3D9Device::clear( U32 flags, ColorI color, F32 z, U32 stencil ) +{ + // Make sure we have flushed our render target state. + _updateRenderTargets(); + + // Kind of a bummer we have to do this, there should be a better way made + DWORD realflags = 0; + + if( flags & GFXClearTarget ) + realflags |= D3DCLEAR_TARGET; + + if( flags & GFXClearZBuffer ) + realflags |= D3DCLEAR_ZBUFFER; + + if( flags & GFXClearStencil ) + realflags |= D3DCLEAR_STENCIL; + + mD3DDevice->Clear( 0, NULL, realflags, + D3DCOLOR_ARGB( color.alpha, color.red, color.green, color.blue ), + z, stencil ); +} + +//----------------------------------------------------------------------------- + +bool GFXD3D9Device::beginSceneInternal() +{ + HRESULT hr = mD3DDevice->BeginScene(); + D3D9Assert(hr, "GFXD3D9Device::beginSceneInternal - failed to BeginScene"); + mCanCurrentlyRender = SUCCEEDED(hr); + return mCanCurrentlyRender; +} + +//----------------------------------------------------------------------------- + +void GFXD3D9Device::endSceneInternal() +{ + mD3DDevice->EndScene(); + mCanCurrentlyRender = false; +} + +void GFXD3D9Device::_updateRenderTargets() +{ + if ( mRTDirty || ( mCurrentRT && mCurrentRT->isPendingState() ) ) + { + if ( mRTDeactivate ) + { + mRTDeactivate->deactivate(); + mRTDeactivate = NULL; + } + + // NOTE: The render target changes are not really accurate + // as the GFXTextureTarget supports MRT internally. So when + // we activate a GFXTarget it could result in multiple calls + // to SetRenderTarget on the actual device. + mDeviceStatistics.mRenderTargetChanges++; + + mCurrentRT->activate(); + + mRTDirty = false; + } + + if ( mViewportDirty ) + { + D3DVIEWPORT9 viewport; + viewport.X = mViewport.point.x; + viewport.Y = mViewport.point.y; + viewport.Width = mViewport.extent.x; + viewport.Height = mViewport.extent.y; + viewport.MinZ = 0.0; + viewport.MaxZ = 1.0; + + D3D9Assert( mD3DDevice->SetViewport( &viewport ), + "GFXD3D9Device::_updateRenderTargets() - Error setting viewport!" ); + + mViewportDirty = false; + } +} + + +#ifdef TORQUE_DEBUG + +void GFXD3D9Device::logVertexBuffers() +{ + + // NOTE: This function should be called on the destructor of this class and ONLY then + // otherwise it'll produce the wrong output + if( mNumAllocatedVertexBuffers == 0 ) + return; + + FileStream fs; + + fs.open( "vertexbuffer.log", Torque::FS::File::Write ); + + char buff[256]; + + fs.writeLine( (U8 *)avar("-- Vertex buffer memory leak report -- time = %d", Platform::getRealMilliseconds()) ); + dSprintf( (char *)&buff, sizeof( buff ), "%d un-freed vertex buffers", mNumAllocatedVertexBuffers ); + fs.writeLine( (U8 *)buff ); + + GFXD3D9VertexBuffer *walk = mVBListHead; + + while( walk != NULL ) + { + dSprintf( (char *)&buff, sizeof( buff ), "[Name: %s] Size: %d", walk->name, walk->mNumVerts ); + fs.writeLine( (U8 *)buff ); + + walk = walk->next; + } + + fs.writeLine( (U8 *)"-- End report --" ); + + fs.close(); +} + +//----------------------------------------------------------------------------- + +void GFXD3D9Device::addVertexBuffer( GFXD3D9VertexBuffer *buffer ) +{ + mNumAllocatedVertexBuffers++; + + if( mVBListHead == NULL ) + { + mVBListHead = buffer; + } + else + { + GFXD3D9VertexBuffer *walk = mVBListHead; + + while( walk->next != NULL ) + { + walk = walk->next; + } + + walk->next = buffer; + } + + buffer->next = NULL; +} + +//----------------------------------------------------------------------------- + +void GFXD3D9Device::removeVertexBuffer( GFXD3D9VertexBuffer *buffer ) +{ + mNumAllocatedVertexBuffers--; + + // Quick check to see if this is head of list + if( mVBListHead == buffer ) + { + mVBListHead = mVBListHead->next; + return; + } + + GFXD3D9VertexBuffer *walk = mVBListHead; + + while( walk->next != NULL ) + { + if( walk->next == buffer ) + { + walk->next = walk->next->next; + return; + } + + walk = walk->next; + } + + AssertFatal( false, "Vertex buffer not found in list." ); +} + +#endif + +//----------------------------------------------------------------------------- + +void GFXD3D9Device::releaseDefaultPoolResources() +{ + // Release all the dynamic vertex buffer arrays + // Forcibly clean up the pools + for( U32 i=0; ivb->AddRef() - 1, mVolatileVBList[i]->mRefCount); + // mVolatileVBList[i]->vb->Release(); + + mVolatileVBList[i]->vb->Release(); + mVolatileVBList[i]->vb = NULL; + mVolatileVBList[i] = NULL; + } + mVolatileVBList.setSize(0); + + // We gotta clear the current const buffer else the next + // activate may erroneously think the device is still holding + // this state and fail to set it. + mCurrentConstBuffer = NULL; + + // Set current VB to NULL and set state dirty + for ( U32 i=0; i < VERTEX_STREAM_COUNT; i++ ) + { + mCurrentVertexBuffer[i] = NULL; + mVertexBufferDirty[i] = true; + mVertexBufferFrequency[i] = 0; + mVertexBufferFrequencyDirty[i] = true; + } + + // Release dynamic index buffer + if( mDynamicPB != NULL ) + { + SAFE_RELEASE( mDynamicPB->ib ); + } + + // Set current PB/IB to NULL and set state dirty + mCurrentPrimitiveBuffer = NULL; + mCurrentPB = NULL; + mPrimitiveBufferDirty = true; + + // Zombify texture manager (for D3D this only modifies default pool textures) + if( mTextureManager ) + mTextureManager->zombify(); + + // Kill off other potentially dangling references... + SAFE_RELEASE( mDeviceDepthStencil ); + SAFE_RELEASE( mDeviceBackbuffer ); + mD3DDevice->SetDepthStencilSurface(NULL); + + // Set global dirty state so the IB/PB and VB get reset + mStateDirty = true; + + // Walk the resource list and zombify everything. + GFXResource *walk = mResourceListHead; + while(walk) + { + walk->zombify(); + walk = walk->getNextResource(); + } +} + +//----------------------------------------------------------------------------- + +void GFXD3D9Device::reacquireDefaultPoolResources() +{ + // Now do the dynamic index buffers + if( mDynamicPB == NULL ) + mDynamicPB = new GFXD3D9PrimitiveBuffer(this, 0, 0, GFXBufferTypeDynamic); + + D3D9Assert( mD3DDevice->CreateIndexBuffer( sizeof( U16 ) * MAX_DYNAMIC_INDICES, +#ifdef TORQUE_OS_XENON + D3DUSAGE_WRITEONLY, +#else + D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, +#endif + GFXD3D9IndexFormat[GFXIndexFormat16], D3DPOOL_DEFAULT, &mDynamicPB->ib, NULL ), "Failed to allocate dynamic IB" ); + + // Grab the depth-stencil... + SAFE_RELEASE(mDeviceDepthStencil); + D3D9Assert(mD3DDevice->GetDepthStencilSurface(&mDeviceDepthStencil), + "GFXD3D9Device::reacquireDefaultPoolResources - couldn't grab reference to device's depth-stencil surface."); + + SAFE_RELEASE(mDeviceBackbuffer); + mD3DDevice->GetBackBuffer( 0, 0, D3DBACKBUFFER_TYPE_MONO, &mDeviceBackbuffer ); + + // Store for query later. + mD3DDevice->GetDisplayMode( 0, &mDisplayMode ); + + // Walk the resource list and zombify everything. + GFXResource *walk = mResourceListHead; + while(walk) + { + walk->resurrect(); + walk = walk->getNextResource(); + } + + if(mTextureManager) + mTextureManager->resurrect(); +} + +GFXD3D9VertexBuffer* GFXD3D9Device::findVBPool( const GFXVertexFormat *vertexFormat, U32 vertsNeeded ) +{ + PROFILE_SCOPE( GFXD3D9Device_findVBPool ); + + // Verts needed is ignored on the base device, 360 is different + for( U32 i=0; imVertexFormat.isEqual( *vertexFormat ) ) + return mVolatileVBList[i]; + + return NULL; +} + +GFXD3D9VertexBuffer * GFXD3D9Device::createVBPool( const GFXVertexFormat *vertexFormat, U32 vertSize ) +{ + PROFILE_SCOPE( GFXD3D9Device_createVBPool ); + + // this is a bit funky, but it will avoid problems with (lack of) copy constructors + // with a push_back() situation + mVolatileVBList.increment(); + StrongRefPtr newBuff; + mVolatileVBList.last() = new GFXD3D9VertexBuffer(); + newBuff = mVolatileVBList.last(); + + newBuff->mNumVerts = 0; + newBuff->mBufferType = GFXBufferTypeVolatile; + newBuff->mVertexFormat.copy( *vertexFormat ); + newBuff->mVertexSize = vertSize; + newBuff->mDevice = this; + + // Requesting it will allocate it. + vertexFormat->getDecl(); + + // Con::printf("Created buff with type %x", vertFlags); + + D3D9Assert( mD3DDevice->CreateVertexBuffer( vertSize * MAX_DYNAMIC_VERTS, +#ifdef TORQUE_OS_XENON + D3DUSAGE_WRITEONLY, +#else + D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, +#endif + 0, + D3DPOOL_DEFAULT, + &newBuff->vb, + NULL ), + "Failed to allocate dynamic VB" ); + return newBuff; +} + +//----------------------------------------------------------------------------- + +void GFXD3D9Device::setClipRect( const RectI &inRect ) +{ + // We transform the incoming rect by the view + // matrix first, so that it can be used to pan + // and scale the clip rect. + // + // This is currently used to take tiled screenshots. + Point3F pos( inRect.point.x, inRect.point.y, 0.0f ); + Point3F extent( inRect.extent.x, inRect.extent.y, 0.0f ); + getViewMatrix().mulP( pos ); + getViewMatrix().mulV( extent ); + RectI rect( pos.x, pos.y, extent.x, extent.y ); + + // Clip the rect against the renderable size. + Point2I size = mCurrentRT->getSize(); + + RectI maxRect(Point2I(0,0), size); + rect.intersect(maxRect); + + mClipRect = rect; + + F32 l = F32( mClipRect.point.x ); + F32 r = F32( mClipRect.point.x + mClipRect.extent.x ); + F32 b = F32( mClipRect.point.y + mClipRect.extent.y ); + F32 t = F32( mClipRect.point.y ); + + // Set up projection matrix, + static Point4F pt; + pt.set(2.0f / (r - l), 0.0f, 0.0f, 0.0f); + mTempMatrix.setColumn(0, pt); + + pt.set(0.0f, 2.0f/(t - b), 0.0f, 0.0f); + mTempMatrix.setColumn(1, pt); + + pt.set(0.0f, 0.0f, 1.0f, 0.0f); + mTempMatrix.setColumn(2, pt); + + pt.set((l+r)/(l-r), (t+b)/(b-t), 1.0f, 1.0f); + mTempMatrix.setColumn(3, pt); + + setProjectionMatrix( mTempMatrix ); + + // Set up world/view matrix + mTempMatrix.identity(); + setWorldMatrix( mTempMatrix ); + + setViewport( mClipRect ); +} + +void GFXD3D9Device::setVertexStream( U32 stream, GFXVertexBuffer *buffer ) +{ + GFXD3D9VertexBuffer *d3dBuffer = static_cast( buffer ); + + if ( stream == 0 ) + { + // Set the volatile buffer which is used to + // offset the start index when doing draw calls. + if ( d3dBuffer && d3dBuffer->mVolatileStart > 0 ) + mVolatileVB = d3dBuffer; + else + mVolatileVB = NULL; + } + + // NOTE: We do not use the stream offset here for stream 0 + // as that feature is *supposedly* not as well supported as + // using the start index in drawPrimitive. + // + // If we can verify that this is not the case then we should + // start using this method exclusively for all streams. + + D3D9Assert( mD3DDevice->SetStreamSource( stream, + d3dBuffer ? d3dBuffer->vb : NULL, + d3dBuffer && stream != 0 ? d3dBuffer->mVolatileStart * d3dBuffer->mVertexSize : 0, + d3dBuffer ? d3dBuffer->mVertexSize : 0 ), + "GFXD3D9Device::setVertexStream - Failed to set stream source." ); +} + +void GFXD3D9Device::setVertexStreamFrequency( U32 stream, U32 frequency ) +{ + if ( frequency == 0 ) + frequency = 1; + else + { + if ( stream == 0 ) + frequency = D3DSTREAMSOURCE_INDEXEDDATA | frequency; + else + frequency = D3DSTREAMSOURCE_INSTANCEDATA | frequency; + } + + D3D9Assert( mD3DDevice->SetStreamSourceFreq( stream, frequency ), + "GFXD3D9Device::setVertexStreamFrequency - Failed to set stream frequency." ); +} + +void GFXD3D9Device::_setPrimitiveBuffer( GFXPrimitiveBuffer *buffer ) +{ + mCurrentPB = static_cast( buffer ); + + D3D9Assert( mD3DDevice->SetIndices( mCurrentPB->ib ), "Failed to set indices" ); +} + +void GFXD3D9Device::drawPrimitive( GFXPrimitiveType primType, U32 vertexStart, U32 primitiveCount ) +{ + // This is done to avoid the function call overhead if possible + if( mStateDirty ) + updateStates(); + if (mCurrentShaderConstBuffer) + setShaderConstBufferInternal(mCurrentShaderConstBuffer); + + if ( mVolatileVB ) + vertexStart += mVolatileVB->mVolatileStart; + + D3D9Assert( mD3DDevice->DrawPrimitive( GFXD3D9PrimType[primType], vertexStart, primitiveCount ), "Failed to draw primitives" ); + + mDeviceStatistics.mDrawCalls++; + if ( mVertexBufferFrequency[0] > 1 ) + mDeviceStatistics.mPolyCount += primitiveCount * mVertexBufferFrequency[0]; + else + mDeviceStatistics.mPolyCount += primitiveCount; +} + +//----------------------------------------------------------------------------- + +void GFXD3D9Device::drawIndexedPrimitive( GFXPrimitiveType primType, + U32 startVertex, + U32 minIndex, + U32 numVerts, + U32 startIndex, + U32 primitiveCount ) +{ + // This is done to avoid the function call overhead if possible + if( mStateDirty ) + updateStates(); + if (mCurrentShaderConstBuffer) + setShaderConstBufferInternal(mCurrentShaderConstBuffer); + + AssertFatal( mCurrentPB != NULL, "Trying to call drawIndexedPrimitive with no current index buffer, call setIndexBuffer()" ); + + if ( mVolatileVB ) + startVertex += mVolatileVB->mVolatileStart; + + D3D9Assert( mD3DDevice->DrawIndexedPrimitive( GFXD3D9PrimType[primType], + startVertex, + /* mCurrentPB->mVolatileStart + */ minIndex, + numVerts, + mCurrentPB->mVolatileStart + startIndex, + primitiveCount ), "Failed to draw indexed primitive" ); + + mDeviceStatistics.mDrawCalls++; + if ( mVertexBufferFrequency[0] > 1 ) + mDeviceStatistics.mPolyCount += primitiveCount * mVertexBufferFrequency[0]; + else + mDeviceStatistics.mPolyCount += primitiveCount; +} + +GFXShader* GFXD3D9Device::createShader() +{ + GFXD3D9Shader* shader = new GFXD3D9Shader(); + shader->registerResourceWithDevice( this ); + return shader; +} + +void GFXD3D9Device::disableShaders() +{ + setShader( NULL ); + setShaderConstBuffer( NULL ); +} + +//----------------------------------------------------------------------------- +// Set shader - this function exists to make sure this is done in one place, +// and to make sure redundant shader states are not being +// sent to the card. +//----------------------------------------------------------------------------- +void GFXD3D9Device::setShader( GFXShader *shader ) +{ + GFXD3D9Shader *d3dShader = static_cast( shader ); + + IDirect3DPixelShader9 *pixShader = ( d3dShader != NULL ? d3dShader->mPixShader : NULL ); + IDirect3DVertexShader9 *vertShader = ( d3dShader ? d3dShader->mVertShader : NULL ); + + if( pixShader != mLastPixShader ) + { + mD3DDevice->SetPixelShader( pixShader ); + mLastPixShader = pixShader; + } + + if( vertShader != mLastVertShader ) + { + mD3DDevice->SetVertexShader( vertShader ); + mLastVertShader = vertShader; + } + +} + +//----------------------------------------------------------------------------- +// allocPrimitiveBuffer +//----------------------------------------------------------------------------- +GFXPrimitiveBuffer * GFXD3D9Device::allocPrimitiveBuffer( U32 numIndices, + U32 numPrimitives, + GFXBufferType bufferType ) +{ + // Allocate a buffer to return + GFXD3D9PrimitiveBuffer * res = new GFXD3D9PrimitiveBuffer(this, numIndices, numPrimitives, bufferType); + + // Determine usage flags + U32 usage = 0; + D3DPOOL pool = D3DPOOL_DEFAULT; + + // Assumptions: + // - static buffers are write once, use many + // - dynamic buffers are write many, use many + // - volatile buffers are write once, use once + // You may never read from a buffer. + switch(bufferType) + { + case GFXBufferTypeStatic: + pool = isD3D9Ex() ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED; + break; + + case GFXBufferTypeDynamic: + case GFXBufferTypeVolatile: +#ifndef TORQUE_OS_XENON + usage |= D3DUSAGE_DYNAMIC; +#endif + break; + } + + // Register resource + res->registerResourceWithDevice(this); + + // We never allow reading from a primitive buffer. + usage |= D3DUSAGE_WRITEONLY; + + // Create d3d index buffer + if(bufferType == GFXBufferTypeVolatile) + { + // Get it from the pool if it's a volatile... + AssertFatal( numIndices < MAX_DYNAMIC_INDICES, "Cannot allocate that many indices in a volatile buffer, increase MAX_DYNAMIC_INDICES." ); + + res->ib = mDynamicPB->ib; + // mDynamicPB->ib->AddRef(); + res->mVolatileBuffer = mDynamicPB; + } + else + { + // Otherwise, get it as a seperate buffer... + D3D9Assert(mD3DDevice->CreateIndexBuffer( sizeof(U16) * numIndices , usage, GFXD3D9IndexFormat[GFXIndexFormat16], pool, &res->ib, 0), + "Failed to allocate an index buffer."); + } + + return res; +} + +//----------------------------------------------------------------------------- +// allocVertexBuffer +//----------------------------------------------------------------------------- +GFXVertexBuffer * GFXD3D9Device::allocVertexBuffer( U32 numVerts, + const GFXVertexFormat *vertexFormat, + U32 vertSize, + GFXBufferType bufferType ) +{ + PROFILE_SCOPE( GFXD3D9Device_allocVertexBuffer ); + + GFXD3D9VertexBuffer *res = new GFXD3D9VertexBuffer( this, + numVerts, + vertexFormat, + vertSize, + bufferType ); + + // Determine usage flags + U32 usage = 0; + D3DPOOL pool = D3DPOOL_DEFAULT; + + res->mNumVerts = 0; + + // Assumptions: + // - static buffers are write once, use many + // - dynamic buffers are write many, use many + // - volatile buffers are write once, use once + // You may never read from a buffer. + + switch(bufferType) + { + case GFXBufferTypeStatic: + pool = isD3D9Ex() ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED; + break; + + case GFXBufferTypeDynamic: + case GFXBufferTypeVolatile: + pool = D3DPOOL_DEFAULT; + usage |= D3DUSAGE_WRITEONLY; +#ifndef TORQUE_OS_XENON + usage |= D3DUSAGE_DYNAMIC; +#endif + break; + } + + res->registerResourceWithDevice(this); + + // Create vertex buffer + if( bufferType == GFXBufferTypeVolatile ) + { + // NOTE: Volatile VBs are pooled and will be allocated at lock time. + + AssertFatal( numVerts <= MAX_DYNAMIC_VERTS, + "GFXD3D9Device::allocVertexBuffer - Volatile vertex buffer is too big... see MAX_DYNAMIC_VERTS!" ); + } + else + { + // Requesting it will allocate it. + vertexFormat->getDecl(); + + // Get a new buffer... + D3D9Assert( mD3DDevice->CreateVertexBuffer( vertSize * numVerts, usage, 0, pool, &res->vb, NULL ), + "Failed to allocate VB" ); + } + + res->mNumVerts = numVerts; + return res; +} + +//----------------------------------------------------------------------------- +// deallocate vertex buffer +//----------------------------------------------------------------------------- +void GFXD3D9Device::deallocVertexBuffer( GFXD3D9VertexBuffer *vertBuff ) +{ + SAFE_RELEASE(vertBuff->vb); +} + +GFXVertexDecl* GFXD3D9Device::allocVertexDecl( const GFXVertexFormat *vertexFormat ) +{ + PROFILE_SCOPE( GFXD3D9Device_allocVertexDecl ); + + // First check the map... you shouldn't allocate VBs very often + // if you want performance. The map lookup should never become + // a performance bottleneck. + D3D9VertexDecl *decl = mVertexDecls[vertexFormat->getDescription()]; + if ( decl ) + return decl; + + // Setup the declaration struct. + U32 elemCount = vertexFormat->getElementCount(); + U32 offsets[4] = { 0 }; + U32 stream; + D3DVERTEXELEMENT9 *vd = new D3DVERTEXELEMENT9[ elemCount + 1 ]; + for ( U32 i=0; i < elemCount; i++ ) + { + const GFXVertexElement &element = vertexFormat->getElement( i ); + + stream = element.getStreamIndex(); + + vd[i].Stream = stream; + vd[i].Offset = offsets[stream]; + vd[i].Type = GFXD3D9DeclType[element.getType()]; + vd[i].Method = D3DDECLMETHOD_DEFAULT; + + // We force the usage index of 0 for everything but + // texture coords for now... this may change later. + vd[i].UsageIndex = 0; + + if ( element.isSemantic( GFXSemantic::POSITION ) ) + vd[i].Usage = D3DDECLUSAGE_POSITION; + else if ( element.isSemantic( GFXSemantic::NORMAL ) ) + vd[i].Usage = D3DDECLUSAGE_NORMAL; + else if ( element.isSemantic( GFXSemantic::COLOR ) ) + vd[i].Usage = D3DDECLUSAGE_COLOR; + else if ( element.isSemantic( GFXSemantic::TANGENT ) ) + vd[i].Usage = D3DDECLUSAGE_TANGENT; + else if ( element.isSemantic( GFXSemantic::BINORMAL ) ) + vd[i].Usage = D3DDECLUSAGE_BINORMAL; + else + { + // Anything that falls thru to here will be a texture coord. + vd[i].Usage = D3DDECLUSAGE_TEXCOORD; + vd[i].UsageIndex = element.getSemanticIndex(); + } + + offsets[stream] += element.getSizeInBytes(); + } + + D3DVERTEXELEMENT9 declEnd = D3DDECL_END(); + vd[elemCount] = declEnd; + + decl = new D3D9VertexDecl(); + D3D9Assert( mD3DDevice->CreateVertexDeclaration( vd, &decl->decl ), + "GFXD3D9Device::allocVertexDecl - Failed to create vertex declaration!" ); + + delete [] vd; + + // Store it in the cache. + mVertexDecls[vertexFormat->getDescription()] = decl; + + return decl; +} + +void GFXD3D9Device::setVertexDecl( const GFXVertexDecl *decl ) +{ + IDirect3DVertexDeclaration9 *dx9Decl = NULL; + if ( decl ) + dx9Decl = static_cast( decl )->decl; + D3D9Assert( mD3DDevice->SetVertexDeclaration( dx9Decl ), "GFXD3D9Device::setVertexDecl - Failed to set vertex declaration." ); +} + +//----------------------------------------------------------------------------- +// This function should ONLY be called from GFXDevice::updateStates() !!! +//----------------------------------------------------------------------------- +void GFXD3D9Device::setTextureInternal( U32 textureUnit, const GFXTextureObject *texture) +{ + if( texture == NULL ) + { + D3D9Assert(mD3DDevice->SetTexture( textureUnit, NULL ), "Failed to set texture to null!"); + return; + } + + GFXD3D9TextureObject *tex = (GFXD3D9TextureObject *) texture; + D3D9Assert(mD3DDevice->SetTexture( textureUnit, tex->getTex()), "Failed to set texture to valid value!"); +} + +//----------------------------------------------------------------------------- +// This function should ONLY be called from GFXDevice::updateStates() !!! +//----------------------------------------------------------------------------- +void GFXD3D9Device::setLightInternal(U32 lightStage, const GFXLightInfo light, bool lightEnable) +{ +#ifndef TORQUE_OS_XENON + if(!lightEnable) + { + mD3DDevice->LightEnable(lightStage, false); + return; + } + D3DLIGHT9 d3dLight; + switch (light.mType) + { + case GFXLightInfo::Ambient: + AssertFatal(false, "Instead of setting an ambient light you should set the global ambient color."); + return; + case GFXLightInfo::Vector: + d3dLight.Type = D3DLIGHT_DIRECTIONAL; + break; + + case GFXLightInfo::Point: + d3dLight.Type = D3DLIGHT_POINT; + break; + + case GFXLightInfo::Spot: + d3dLight.Type = D3DLIGHT_SPOT; + break; + + default : + AssertFatal(false, "Unknown light type!"); + }; + + dMemcpy(&d3dLight.Diffuse, &light.mColor, sizeof(light.mColor)); + dMemcpy(&d3dLight.Ambient, &light.mAmbient, sizeof(light.mAmbient)); + dMemcpy(&d3dLight.Specular, &light.mColor, sizeof(light.mColor)); + dMemcpy(&d3dLight.Position, &light.mPos, sizeof(light.mPos)); + dMemcpy(&d3dLight.Direction, &light.mDirection, sizeof(light.mDirection)); + + d3dLight.Range = light.mRadius; + + d3dLight.Falloff = 1.0; + + d3dLight.Attenuation0 = 1.0f; + d3dLight.Attenuation1 = 0.1f; + d3dLight.Attenuation2 = 0.0f; + + d3dLight.Theta = light.mInnerConeAngle; + d3dLight.Phi = light.mOuterConeAngle; + + mD3DDevice->SetLight(lightStage, &d3dLight); + mD3DDevice->LightEnable(lightStage, true); +#endif +} + +void GFXD3D9Device::setLightMaterialInternal(const GFXLightMaterial mat) +{ +#ifndef TORQUE_OS_XENON + D3DMATERIAL9 d3dmat; + dMemset(&d3dmat, 0, sizeof(D3DMATERIAL9)); + D3DCOLORVALUE col; + + col.r = mat.ambient.red; + col.g = mat.ambient.green; + col.b = mat.ambient.blue; + col.a = mat.ambient.alpha; + d3dmat.Ambient = col; + + col.r = mat.diffuse.red; + col.g = mat.diffuse.green; + col.b = mat.diffuse.blue; + col.a = mat.diffuse.alpha; + d3dmat.Diffuse = col; + + col.r = mat.specular.red; + col.g = mat.specular.green; + col.b = mat.specular.blue; + col.a = mat.specular.alpha; + d3dmat.Specular = col; + + col.r = mat.emissive.red; + col.g = mat.emissive.green; + col.b = mat.emissive.blue; + col.a = mat.emissive.alpha; + d3dmat.Emissive = col; + + d3dmat.Power = mat.shininess; + mD3DDevice->SetMaterial(&d3dmat); +#endif +} + +void GFXD3D9Device::setGlobalAmbientInternal(ColorF color) +{ +#ifndef TORQUE_OS_XENON + mD3DDevice->SetRenderState(D3DRS_AMBIENT, + D3DCOLOR_COLORVALUE(color.red, color.green, color.blue, color.alpha)); +#endif +} + +//------------------------------------------------------------------------------ +// Check for texture mis-match between GFX internal state and what is on the card +// This function is expensive because of the readbacks from DX, and additionally +// won't work unless it's a non-pure device. +// +// This function can crash or give false positives when the game +// is shutting down or returning to the main menu as some of the textures +// present in the mCurrentTexture array will have been freed. +// +// This function is best used as a quick check for mismatched state when it is +// suspected. +//------------------------------------------------------------------------------ +void GFXD3D9Device::doParanoidStateCheck() +{ +#ifdef TORQUE_DEBUG + // Read back all states and make sure they match what we think they should be. + + // For now just do texture binds. + for(U32 i = 0; i < getNumSamplers(); i++) + { + IDirect3DBaseTexture9 *b=NULL; + getDevice()->GetTexture(i, &b); + if ((mCurrentTexture[i].isNull()) && (mCurrentCubemap[i].isNull())) + { + AssertFatal(b == NULL, "GFXD3D9Device::doParanoidStateCheck - got non-null texture in expected NULL slot!"); + getDevice()->SetTexture(i, NULL); + } + else + { + AssertFatal(mCurrentTexture[i] || mCurrentCubemap[i], "GFXD3D9Device::doParanoidStateCheck - got null texture in expected non-null slot!"); + if (mCurrentCubemap[i]) + { + IDirect3DCubeTexture9 *cur= static_cast(mCurrentCubemap[i].getPointer())->mCubeTex; + AssertFatal(cur == b, "GFXD3D9Device::doParanoidStateCheck - mismatched cubemap!"); + } + else + { + IDirect3DBaseTexture9 *cur= static_cast(mCurrentTexture[i].getPointer())->getTex(); + AssertFatal(cur == b, "GFXD3D9Device::doParanoidStateCheck - mismatched 2d texture!"); + } + } + + SAFE_RELEASE(b); + } +#endif +} + +GFXFence *GFXD3D9Device::createFence() +{ + // Figure out what fence type we should be making if we don't know + if( mCreateFenceType == -1 ) + { + IDirect3DQuery9 *testQuery = NULL; + mCreateFenceType = ( mD3DDevice->CreateQuery( D3DQUERYTYPE_EVENT, &testQuery ) == D3DERR_NOTAVAILABLE ); + SAFE_RELEASE( testQuery ); + } + + // Cool, use queries + if( !mCreateFenceType ) + { + GFXFence* fence = new GFXD3D9QueryFence( this ); + fence->registerResourceWithDevice(this); + return fence; + } + + // CodeReview: At some point I would like a specialized D3D9 implementation of + // the method used by the general fence, only without the overhead incurred + // by using the GFX constructs. Primarily the lock() method on texture handles + // will do a data copy, and this method doesn't require a copy, just a lock + // [5/10/2007 Pat] + GFXFence* fence = new GFXGeneralFence( this ); + fence->registerResourceWithDevice(this); + return fence; +} + +GFXOcclusionQuery* GFXD3D9Device::createOcclusionQuery() +{ + GFXOcclusionQuery *query; + if (mOcclusionQuerySupported) + query = new GFXD3D9OcclusionQuery( this ); + else + return NULL; + + query->registerResourceWithDevice(this); + return query; +} + +GFXCubemap * GFXD3D9Device::createCubemap() +{ + GFXD3D9Cubemap* cube = new GFXD3D9Cubemap(); + cube->registerResourceWithDevice(this); + return cube; +} diff --git a/Engine/source/gfx/D3D9/gfxD3D9Device.h b/Engine/source/gfx/D3D9/gfxD3D9Device.h new file mode 100644 index 000000000..12405add2 --- /dev/null +++ b/Engine/source/gfx/D3D9/gfxD3D9Device.h @@ -0,0 +1,357 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXD3D9DEVICE_H_ +#define _GFXD3D9DEVICE_H_ + +#include "platform/tmm_off.h" + +#ifdef TORQUE_OS_XENON +# include "platformXbox/platformXbox.h" +#else +# include +# include "platformWin32/platformWin32.h" +#endif +#ifndef _GFXD3D9STATEBLOCK_H_ +#include "gfx/D3D9/gfxD3D9StateBlock.h" +#endif +#ifndef _GFXD3DTEXTUREMANAGER_H_ +#include "gfx/D3D9/gfxD3D9TextureManager.h" +#endif +#ifndef _GFXD3D9CUBEMAP_H_ +#include "gfx/D3D9/gfxD3D9Cubemap.h" +#endif +#ifndef _GFXD3D9PRIMITIVEBUFFER_H_ +#include "gfx/D3D9/gfxD3D9PrimitiveBuffer.h" +#endif +#ifndef _GFXINIT_H_ +#include "gfx/gfxInit.h" +#endif +#ifndef _PLATFORMDLIBRARY_H +#include "platform/platformDlibrary.h" +#endif + +#ifndef TORQUE_OS_XENON +#include +#else +#include +#define DXGetErrorStringA DXGetErrorString9A +#define DXGetErrorDescriptionA DXGetErrorDescription9A +#endif + +#include "platform/tmm_on.h" + +inline void D3D9Assert( HRESULT hr, const char *info ) +{ +#if defined( TORQUE_DEBUG ) + if( FAILED( hr ) ) + { + char buf[256]; + dSprintf( buf, 256, "%s\n%s\n%s", DXGetErrorStringA( hr ), DXGetErrorDescriptionA( hr ), info ); + AssertFatal( false, buf ); + // DXTrace( __FILE__, __LINE__, hr, info, true ); + } +#endif +} + + +// Typedefs +#define D3DX_FUNCTION(fn_name, fn_return, fn_args) \ + typedef fn_return (WINAPI *D3DXFNPTR##fn_name##)##fn_args##; +#include "gfx/D3D9/d3dx9Functions.h" +#undef D3DX_FUNCTION + +// Function table +struct D3DXFNTable +{ + D3DXFNTable() : isLoaded( false ){}; + bool isLoaded; + DLibraryRef dllRef; + DLibraryRef compilerDllRef; +#define D3DX_FUNCTION(fn_name, fn_return, fn_args) \ + D3DXFNPTR##fn_name fn_name; +#include "gfx/D3D9/d3dx9Functions.h" +#undef D3DX_FUNCTION +}; + +#define GFXD3DX static_cast(GFX)->smD3DX + +class GFXResource; +class GFXD3D9ShaderConstBuffer; + +//------------------------------------------------------------------------------ + +class GFXD3D9Device : public GFXDevice +{ + friend class GFXResource; + friend class GFXD3D9PrimitiveBuffer; + friend class GFXD3D9VertexBuffer; + friend class GFXD3D9TextureObject; + friend class GFXPCD3D9TextureTarget; + friend class GFXPCD3D9WindowTarget; + + typedef GFXDevice Parent; + +protected: + + MatrixF mTempMatrix; ///< Temporary matrix, no assurances on value at all + RectI mClipRect; + + typedef StrongRefPtr RPGDVB; + Vector mVolatileVBList; + + class D3D9VertexDecl : public GFXVertexDecl + { + public: + virtual ~D3D9VertexDecl() + { + SAFE_RELEASE( decl ); + } + + IDirect3DVertexDeclaration9 *decl; + }; + + /// Used to lookup a vertex declaration for the vertex format. + /// @see allocVertexDecl + typedef Map VertexDeclMap; + VertexDeclMap mVertexDecls; + + IDirect3DSurface9 *mDeviceBackbuffer; + IDirect3DSurface9 *mDeviceDepthStencil; + IDirect3DSurface9 *mDeviceColor; + + /// The stream 0 vertex buffer used for volatile VB offseting. + GFXD3D9VertexBuffer *mVolatileVB; + + static void initD3DXFnTable(); + //----------------------------------------------------------------------- + StrongRefPtr mDynamicPB; ///< Dynamic index buffer + GFXD3D9PrimitiveBuffer *mCurrentPB; + + IDirect3DVertexShader9 *mLastVertShader; + IDirect3DPixelShader9 *mLastPixShader; + + S32 mCreateFenceType; + + LPDIRECT3D9 mD3D; ///< D3D Handle + LPDIRECT3DDEVICE9 mD3DDevice; ///< Handle for D3DDevice + +#if !defined(TORQUE_OS_XENON) + LPDIRECT3D9EX mD3DEx; ///< D3D9Ex Handle + LPDIRECT3DDEVICE9EX mD3DDeviceEx; ///< Handle for D3DDevice9Ex +#endif + + U32 mAdapterIndex; ///< Adapter index because D3D supports multiple adapters + + F32 mPixVersion; + U32 mNumSamplers; ///< Profiled (via caps) + U32 mNumRenderTargets; ///< Profiled (via caps) + + D3DMULTISAMPLE_TYPE mMultisampleType; + DWORD mMultisampleLevel; + + bool mOcclusionQuerySupported; + + /// The current adapter display mode. + D3DDISPLAYMODE mDisplayMode; + + /// To manage creating and re-creating of these when device is aquired + void reacquireDefaultPoolResources(); + + /// To release all resources we control from D3DPOOL_DEFAULT + void releaseDefaultPoolResources(); + + /// This you will probably never, ever use, but it is used to generate the code for + /// the initStates() function + void regenStates(); + + virtual GFXD3D9VertexBuffer* findVBPool( const GFXVertexFormat *vertexFormat, U32 numVertsNeeded ); + virtual GFXD3D9VertexBuffer* createVBPool( const GFXVertexFormat *vertexFormat, U32 vertSize ); + +#ifdef TORQUE_DEBUG + /// @name Debug Vertex Buffer information/management + /// @{ + + /// + U32 mNumAllocatedVertexBuffers; ///< To keep track of how many are allocated and freed + GFXD3D9VertexBuffer *mVBListHead; + void addVertexBuffer( GFXD3D9VertexBuffer *buffer ); + void removeVertexBuffer( GFXD3D9VertexBuffer *buffer ); + void logVertexBuffers(); + /// @} +#endif + + // State overrides + // { + + /// + virtual void setTextureInternal(U32 textureUnit, const GFXTextureObject* texture); + + /// Called by GFXDevice to create a device specific stateblock + virtual GFXStateBlockRef createStateBlockInternal(const GFXStateBlockDesc& desc); + /// Called by GFXDevice to actually set a stateblock. + virtual void setStateBlockInternal(GFXStateBlock* block, bool force); + + /// Track the last const buffer we've used. Used to notify new constant buffers that + /// they should send all of their constants up + StrongRefPtr mCurrentConstBuffer; + /// Called by base GFXDevice to actually set a const buffer + virtual void setShaderConstBufferInternal(GFXShaderConstBuffer* buffer); + + // CodeReview - How exactly do we want to deal with this on the Xenon? + // Right now it's just in an #ifndef in gfxD3D9Device.cpp - AlexS 4/11/07 + virtual void setLightInternal(U32 lightStage, const GFXLightInfo light, bool lightEnable); + virtual void setLightMaterialInternal(const GFXLightMaterial mat); + virtual void setGlobalAmbientInternal(ColorF color); + + virtual void initStates()=0; + // } + + // Index buffer management + // { + virtual void _setPrimitiveBuffer( GFXPrimitiveBuffer *buffer ); + virtual void drawIndexedPrimitive( GFXPrimitiveType primType, + U32 startVertex, + U32 minIndex, + U32 numVerts, + U32 startIndex, + U32 primitiveCount ); + // } + + virtual GFXShader* createShader(); + + /// Device helper function + virtual D3DPRESENT_PARAMETERS setupPresentParams( const GFXVideoMode &mode, const HWND &hwnd ) const = 0; + +public: + static D3DXFNTable smD3DX; + + static GFXDevice *createInstance( U32 adapterIndex ); + + GFXTextureObject* createRenderSurface( U32 width, U32 height, GFXFormat format, U32 mipLevel ); + + const D3DDISPLAYMODE& getDisplayMode() const { return mDisplayMode; } + + /// Constructor + /// @param d3d Direct3D object to instantiate this device with + /// @param index Adapter index since D3D can use multiple graphics adapters + GFXD3D9Device( LPDIRECT3D9 d3d, U32 index ); + virtual ~GFXD3D9Device(); + + // Activate/deactivate + // { + virtual void init( const GFXVideoMode &mode, PlatformWindow *window = NULL ) = 0; + + virtual void preDestroy() { Parent::preDestroy(); if(mTextureManager) mTextureManager->kill(); } + + GFXAdapterType getAdapterType(){ return Direct3D9; } + + virtual GFXCubemap *createCubemap(); + + virtual F32 getPixelShaderVersion() const { return mPixVersion; } + virtual void setPixelShaderVersion( F32 version ){ mPixVersion = version; } + virtual void disableShaders(); + virtual void setShader( GFXShader *shader ); + virtual U32 getNumSamplers() const { return mNumSamplers; } + virtual U32 getNumRenderTargets() const { return mNumRenderTargets; } + // } + + // Misc rendering control + // { + virtual void clear( U32 flags, ColorI color, F32 z, U32 stencil ); + virtual bool beginSceneInternal(); + virtual void endSceneInternal(); + + virtual void setClipRect( const RectI &rect ); + virtual const RectI& getClipRect() const { return mClipRect; } + + // } + + /// @name Render Targets + /// @{ + virtual void _updateRenderTargets(); + /// @} + + // Vertex/Index buffer management + // { + virtual GFXVertexBuffer* allocVertexBuffer( U32 numVerts, + const GFXVertexFormat *vertexFormat, + U32 vertSize, + GFXBufferType bufferType ); + virtual GFXPrimitiveBuffer *allocPrimitiveBuffer( U32 numIndices, + U32 numPrimitives, + GFXBufferType bufferType ); + virtual void deallocVertexBuffer( GFXD3D9VertexBuffer *vertBuff ); + virtual GFXVertexDecl* allocVertexDecl( const GFXVertexFormat *vertexFormat ); + virtual void setVertexDecl( const GFXVertexDecl *decl ); + + virtual void setVertexStream( U32 stream, GFXVertexBuffer *buffer ); + virtual void setVertexStreamFrequency( U32 stream, U32 frequency ); + // } + + virtual U32 getMaxDynamicVerts() { return MAX_DYNAMIC_VERTS; } + virtual U32 getMaxDynamicIndices() { return MAX_DYNAMIC_INDICES; } + + // Rendering + // { + virtual void drawPrimitive( GFXPrimitiveType primType, U32 vertexStart, U32 primitiveCount ); + // } + + virtual LPDIRECT3DDEVICE9 getDevice(){ return mD3DDevice; } + virtual LPDIRECT3D9 getD3D() { return mD3D; } + + /// Reset + virtual void reset( D3DPRESENT_PARAMETERS &d3dpp ) = 0; + + GFXShaderRef mGenericShader[GS_COUNT]; + + virtual void setupGenericShaders( GenericShaderType type = GSColor ); + + // Function only really used on the, however a centralized function for + // destroying resources is probably a good thing -patw + virtual void destroyD3DResource( IDirect3DResource9 *d3dResource ) { SAFE_RELEASE( d3dResource ); }; + + inline virtual F32 getFillConventionOffset() const { return 0.5f; } + virtual void doParanoidStateCheck(); + + GFXFence *createFence(); + + GFXOcclusionQuery* createOcclusionQuery(); + + // Default multisample parameters + D3DMULTISAMPLE_TYPE getMultisampleType() const { return mMultisampleType; } + DWORD getMultisampleLevel() const { return mMultisampleLevel; } + + // Whether or not the Direct3D device was created with Direct3D9Ex support +#if !defined(TORQUE_OS_XENON) + virtual bool isD3D9Ex() { return mD3DEx != NULL; } +#else + virtual bool isD3D9Ex() { return false; } +#endif + + // Get the backbuffer, currently only access for WPF support + virtual IDirect3DSurface9* getBackBuffer() { return mDeviceBackbuffer; } + +}; + + +#endif // _GFXD3D9DEVICE_H_ diff --git a/Engine/source/gfx/D3D9/gfxD3D9Device.regen-states.cpp b/Engine/source/gfx/D3D9/gfxD3D9Device.regen-states.cpp new file mode 100644 index 000000000..8160cc688 --- /dev/null +++ b/Engine/source/gfx/D3D9/gfxD3D9Device.regen-states.cpp @@ -0,0 +1,92 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/D3D9/gfxD3D9Device.h" +#include "console/console.h" +#include "gfx/D3D9/gfxD3D9EnumTranslate.h" + +// Cut and paste from console.log into GFXD3D9Device::initStates() +void GFXD3D9Device::regenStates() +{ + DWORD temp; + Con::printf( " //-------------------------------------" ); + Con::printf( " // Auto-generated default states, see regenStates() for details" ); + Con::printf( " //" ); + Con::printf( "" ); + Con::printf( " // Render states" ); + + for( U32 state = GFXRenderState_FIRST; state < GFXRenderState_COUNT; state++ ) + { + if( GFXD3D9RenderState[state] == GFX_UNSUPPORTED_VAL ) + continue; + + temp = 0; + mD3DDevice->GetRenderState( GFXD3D9RenderState[state], &temp ); + Con::printf( " mD3DDevice->SetRenderState( GFXD3D9RenderState[%d], %d );", state, temp ); + } + +#ifndef TORQUE_OS_XENON + Con::printf( "" ); + Con::printf( " // Texture Stage states" ); + + for( U32 stage = 0; stage < TEXTURE_STAGE_COUNT; stage++ ) + { + if( stage >= GFX->getNumSamplers() ) + { + Con::errorf( "Sampler %d out of range for this device, ignoring.", stage ); + break; + } + + for( U32 state = GFXTSS_FIRST; state < GFXTSS_COUNT; state++ ) + { + if( GFXD3D9TextureStageState[state] == GFX_UNSUPPORTED_VAL ) + continue; + + temp = 0; + mD3DDevice->GetTextureStageState( stage, GFXD3D9TextureStageState[state], &temp ); + Con::printf( " mD3DDevice->SetTextureStageState( %d, GFXD3D9TextureStageState[%d], %d );", stage, state, temp ); + } + } +#endif + + Con::printf( "" ); + Con::printf( " // Sampler states" ); + for( U32 stage = 0; stage < TEXTURE_STAGE_COUNT; stage++ ) + { + if( stage >= GFX->getNumSamplers() ) + { + Con::errorf( "Sampler %d out of range for this device, ignoring.", stage ); + break; + } + + for( U32 state = GFXSAMP_FIRST; state < GFXSAMP_COUNT; state++ ) + { + if( GFXD3D9SamplerState[state] == GFX_UNSUPPORTED_VAL ) + continue; + + temp = 0; + + mD3DDevice->GetSamplerState( stage, GFXD3D9SamplerState[state], &temp ); + Con::printf( " mD3DDevice->SetSamplerState( %d, GFXD3D9SamplerState[%d], %d );", stage, state, temp ); + } + } +} diff --git a/Engine/source/gfx/D3D9/gfxD3D9EnumTranslate.h b/Engine/source/gfx/D3D9/gfxD3D9EnumTranslate.h new file mode 100644 index 000000000..00448bad0 --- /dev/null +++ b/Engine/source/gfx/D3D9/gfxD3D9EnumTranslate.h @@ -0,0 +1,63 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/D3D9/gfxD3D9Shader.h" + +#include "gfx/gfxEnums.h" + +//------------------------------------------------------------------------------ + +namespace GFXD3D9EnumTranslate +{ + void init(); +}; + +//------------------------------------------------------------------------------ + +extern _D3DFORMAT GFXD3D9IndexFormat[GFXIndexFormat_COUNT]; +extern _D3DSAMPLERSTATETYPE GFXD3D9SamplerState[GFXSAMP_COUNT]; +extern _D3DFORMAT GFXD3D9TextureFormat[GFXFormat_COUNT]; +#ifdef TORQUE_OS_XENON +extern _D3DFORMAT GFXD3D9RenderTargetFormat[GFXFormat_COUNT]; +#endif +extern _D3DRENDERSTATETYPE GFXD3D9RenderState[GFXRenderState_COUNT]; +extern _D3DTEXTUREFILTERTYPE GFXD3D9TextureFilter[GFXTextureFilter_COUNT]; +extern _D3DBLEND GFXD3D9Blend[GFXBlend_COUNT]; +extern _D3DBLENDOP GFXD3D9BlendOp[GFXBlendOp_COUNT]; +extern _D3DSTENCILOP GFXD3D9StencilOp[GFXStencilOp_COUNT]; +extern _D3DCMPFUNC GFXD3D9CmpFunc[GFXCmp_COUNT]; +extern _D3DCULL GFXD3D9CullMode[GFXCull_COUNT]; +extern _D3DFILLMODE GFXD3D9FillMode[GFXFill_COUNT]; +extern _D3DPRIMITIVETYPE GFXD3D9PrimType[GFXPT_COUNT]; +extern _D3DTEXTURESTAGESTATETYPE GFXD3D9TextureStageState[GFXTSS_COUNT]; +extern _D3DTEXTUREADDRESS GFXD3D9TextureAddress[GFXAddress_COUNT]; +extern _D3DTEXTUREOP GFXD3D9TextureOp[GFXTOP_COUNT]; +extern _D3DDECLTYPE GFXD3D9DeclType[GFXDeclType_COUNT]; + +#define GFXREVERSE_LOOKUP( tablearray, enumprefix, val ) \ + for( int i = enumprefix##_FIRST; i < enumprefix##_COUNT; i++ ) \ + if( (int)tablearray##[i] == val ) \ + { \ + val = i; \ + break; \ + } \ + diff --git a/Engine/source/gfx/D3D9/gfxD3D9OcclusionQuery.cpp b/Engine/source/gfx/D3D9/gfxD3D9OcclusionQuery.cpp new file mode 100644 index 000000000..af61bf997 --- /dev/null +++ b/Engine/source/gfx/D3D9/gfxD3D9OcclusionQuery.cpp @@ -0,0 +1,163 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/D3D9/gfxD3D9Device.h" +#include "gfx/D3D9/gfxD3D9OcclusionQuery.h" + +#include "gui/3d/guiTSControl.h" + +#ifdef TORQUE_GATHER_METRICS +// For TickMs define +#include "T3D/gameBase/processList.h" +#endif + +GFXD3D9OcclusionQuery::GFXD3D9OcclusionQuery( GFXDevice *device ) + : GFXOcclusionQuery( device ), + mQuery( NULL ) +{ +#ifdef TORQUE_GATHER_METRICS + mTimer = PlatformTimer::create(); + mTimer->getElapsedMs(); + + mTimeSinceEnd = 0; + mBeginFrame = 0; +#endif +} + +GFXD3D9OcclusionQuery::~GFXD3D9OcclusionQuery() +{ + SAFE_RELEASE( mQuery ); + +#ifdef TORQUE_GATHER_METRICS + SAFE_DELETE( mTimer ); +#endif +} + +bool GFXD3D9OcclusionQuery::begin() +{ + if ( GFXDevice::getDisableOcclusionQuery() ) + return true; + + if ( mQuery == NULL ) + { +#ifdef TORQUE_OS_XENON + HRESULT hRes = static_cast( mDevice )->getDevice()->CreateQueryTiled( D3DQUERYTYPE_OCCLUSION, 2, &mQuery ); +#else + HRESULT hRes = static_cast( mDevice )->getDevice()->CreateQuery( D3DQUERYTYPE_OCCLUSION, &mQuery ); +#endif + + AssertFatal( hRes != D3DERR_NOTAVAILABLE, "GFXD3D9OcclusionQuery::begin - Hardware does not support D3D9 Occlusion-Queries, this should be caught before this type is created" ); + AssertISV( hRes != E_OUTOFMEMORY, "GFXD3D9OcclusionQuery::begin - Out of memory" ); + } + + // Add a begin marker to the command buffer queue. + mQuery->Issue( D3DISSUE_BEGIN ); + +#ifdef TORQUE_GATHER_METRICS + mBeginFrame = GuiTSCtrl::getFrameCount(); +#endif + + return true; +} + +void GFXD3D9OcclusionQuery::end() +{ + if ( GFXDevice::getDisableOcclusionQuery() ) + return; + + // Add an end marker to the command buffer queue. + mQuery->Issue( D3DISSUE_END ); + +#ifdef TORQUE_GATHER_METRICS + AssertFatal( mBeginFrame == GuiTSCtrl::getFrameCount(), "GFXD3D9OcclusionQuery::end - ended query on different frame than begin!" ); + mTimer->getElapsedMs(); + mTimer->reset(); +#endif +} + +GFXD3D9OcclusionQuery::OcclusionQueryStatus GFXD3D9OcclusionQuery::getStatus( bool block, U32 *data ) +{ + // If this ever shows up near the top of a profile then your system is + // GPU bound or you are calling getStatus too soon after submitting it. + // + // To test if you are GPU bound resize your window very small and see if + // this profile no longer appears at the top. + // + // To test if you are calling getStatus to soon after submitting it, + // check the value of mTimeSinceEnd in a debug build. If it is < half the length + // of time to render an individual frame you could have problems. + PROFILE_SCOPE(GFXD3D9OcclusionQuery_getStatus); + + if ( GFXDevice::getDisableOcclusionQuery() ) + return NotOccluded; + + if ( mQuery == NULL ) + return Unset; + +#ifdef TORQUE_GATHER_METRICS + AssertFatal( mBeginFrame < GuiTSCtrl::getFrameCount(), "GFXD3D9OcclusionQuery::getStatus - called on the same frame as begin!" ); + + //U32 mTimeSinceEnd = mTimer->getElapsedMs(); + //AssertFatal( mTimeSinceEnd >= 5, "GFXD3DOcculsionQuery::getStatus - less than TickMs since called ::end!" ); +#endif + + HRESULT hRes; + DWORD dwOccluded = 0; + + if ( block ) + { + while( ( hRes = mQuery->GetData( &dwOccluded, sizeof(DWORD), D3DGETDATA_FLUSH ) ) == S_FALSE ) + ; + } + else + { + hRes = mQuery->GetData( &dwOccluded, sizeof(DWORD), 0 ); + } + + if ( hRes == S_OK ) + { + if ( data != NULL ) + *data = dwOccluded; + + return dwOccluded > 0 ? NotOccluded : Occluded; + } + + if ( hRes == S_FALSE ) + return Waiting; + + return Error; +} + +void GFXD3D9OcclusionQuery::zombify() +{ + SAFE_RELEASE( mQuery ); +} + +void GFXD3D9OcclusionQuery::resurrect() +{ +} + +const String GFXD3D9OcclusionQuery::describeSelf() const +{ + // We've got nothing + return String(); +} \ No newline at end of file diff --git a/Engine/source/gfx/D3D9/gfxD3D9OcclusionQuery.h b/Engine/source/gfx/D3D9/gfxD3D9OcclusionQuery.h new file mode 100644 index 000000000..062653b40 --- /dev/null +++ b/Engine/source/gfx/D3D9/gfxD3D9OcclusionQuery.h @@ -0,0 +1,64 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFX_D3D9_OCCLUSIONQUERY_H_ +#define _GFX_D3D9_OCCLUSIONQUERY_H_ + +#ifndef _GFXOCCLUSIONQUERY_H_ +#include "gfx/gfxOcclusionQuery.h" +#endif + +#ifdef TORQUE_GATHER_METRICS + #ifndef _PLATFORM_PLATFORMTIMER_H_ + #include "platform/platformTimer.h" + #endif +#endif + +struct IDirect3DQuery9; + + +class GFXD3D9OcclusionQuery : public GFXOcclusionQuery +{ +private: + mutable IDirect3DQuery9 *mQuery; + +#ifdef TORQUE_GATHER_METRICS + U32 mBeginFrame; + U32 mTimeSinceEnd; + PlatformTimer *mTimer; +#endif + +public: + GFXD3D9OcclusionQuery( GFXDevice *device ); + virtual ~GFXD3D9OcclusionQuery(); + + virtual bool begin(); + virtual void end(); + virtual OcclusionQueryStatus getStatus( bool block, U32 *data = NULL ); + + // GFXResource + virtual void zombify(); + virtual void resurrect(); + virtual const String describeSelf() const; +}; + +#endif // _GFX_D3D9_OCCLUSIONQUERY_H_ \ No newline at end of file diff --git a/Engine/source/gfx/D3D9/gfxD3D9PrimitiveBuffer.cpp b/Engine/source/gfx/D3D9/gfxD3D9PrimitiveBuffer.cpp new file mode 100644 index 000000000..9588f798d --- /dev/null +++ b/Engine/source/gfx/D3D9/gfxD3D9PrimitiveBuffer.cpp @@ -0,0 +1,114 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/D3D9/gfxD3D9Device.h" +#include "gfx/D3D9/gfxD3D9EnumTranslate.h" +#include "gfx/D3D9/gfxD3D9PrimitiveBuffer.h" +#include "core/util/safeRelease.h" + +void GFXD3D9PrimitiveBuffer::prepare() +{ + static_cast( mDevice )->_setPrimitiveBuffer(this); +} + +void GFXD3D9PrimitiveBuffer::unlock() +{ + #ifdef TORQUE_DEBUG + + if ( mDebugGuardBuffer ) + { + const U32 guardSize = sizeof( _PBGuardString ); + + // First check the guard areas for overwrites. + AssertFatal( dMemcmp( mDebugGuardBuffer, _PBGuardString, guardSize ) == 0, + "GFXD3D9PrimitiveBuffer::unlock - Caught lock memory underrun!" ); + AssertFatal( dMemcmp( mDebugGuardBuffer + mLockedSize + guardSize, _PBGuardString, guardSize ) == 0, + "GFXD3D9PrimitiveBuffer::unlock - Caught lock memory overrun!" ); + + // Copy the debug content down to the real PB. + dMemcpy( mLockedBuffer, mDebugGuardBuffer + guardSize, mLockedSize ); + + // Cleanup. + delete [] mDebugGuardBuffer; + mDebugGuardBuffer = NULL; + mLockedBuffer = NULL; + mLockedSize = 0; + } + + #endif // TORQUE_DEBUG + + ib->Unlock(); + mLocked = false; + mIsFirstLock = false; + mVolatileBuffer = NULL; +} + +GFXD3D9PrimitiveBuffer::~GFXD3D9PrimitiveBuffer() +{ + if( mBufferType != GFXBufferTypeVolatile ) + { +#if defined(TORQUE_OS_XENON) + if(ib->IsSet(reinterpret_cast(mDevice)->mD3DDevice)) + { + reinterpret_cast(mDevice)->mD3DDevice->SetIndices(NULL); + } +#endif + SAFE_RELEASE( ib ); + } +} + +void GFXD3D9PrimitiveBuffer::zombify() +{ + if(mBufferType == GFXBufferTypeStatic) + return; + + AssertFatal(!mLocked, "GFXD3D9PrimitiveBuffer::zombify - Cannot zombify a locked buffer!"); + + if (mBufferType == GFXBufferTypeVolatile) + { + // We must null the volatile buffer else we're holding + // a dead pointer which can be set on the device. + ib = NULL; + return; + } + + // Dynamic buffers get released. + SAFE_RELEASE(ib); +} + +void GFXD3D9PrimitiveBuffer::resurrect() +{ + if ( mBufferType != GFXBufferTypeDynamic ) + return; + + U32 usage = D3DUSAGE_WRITEONLY; + +#ifndef TORQUE_OS_XENON + usage |= D3DUSAGE_DYNAMIC; +#endif + + D3DPOOL pool = D3DPOOL_DEFAULT; + + D3D9Assert(static_cast(mDevice)->mD3DDevice->CreateIndexBuffer( sizeof(U16) * mIndexCount , + usage , GFXD3D9IndexFormat[GFXIndexFormat16], pool, &ib, 0), + "GFXD3D9PrimitiveBuffer::resurrect - Failed to allocate an index buffer."); +} diff --git a/Engine/source/gfx/D3D9/gfxD3D9PrimitiveBuffer.h b/Engine/source/gfx/D3D9/gfxD3D9PrimitiveBuffer.h new file mode 100644 index 000000000..6dba3741c --- /dev/null +++ b/Engine/source/gfx/D3D9/gfxD3D9PrimitiveBuffer.h @@ -0,0 +1,88 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXD3D9PRIMITIVEBUFFER_H_ +#define _GFXD3D9PRIMITIVEBUFFER_H_ + +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif + + +struct IDirect3DIndexBuffer9; + +class GFXD3D9PrimitiveBuffer : public GFXPrimitiveBuffer +{ + public: + IDirect3DIndexBuffer9 *ib; + StrongRefPtr mVolatileBuffer; + U32 mVolatileStart; + +#ifdef TORQUE_DEBUG + #define _PBGuardString "GFX_PRIMTIVE_BUFFER_GUARD_STRING" + U8 *mDebugGuardBuffer; + void *mLockedBuffer; + U32 mLockedSize; +#endif TORQUE_DEBUG + + bool mLocked; + bool mIsFirstLock; + + GFXD3D9PrimitiveBuffer( GFXDevice *device, + U32 indexCount, + U32 primitiveCount, + GFXBufferType bufferType ); + + virtual ~GFXD3D9PrimitiveBuffer(); + + virtual void lock(U32 indexStart, U32 indexEnd, void **indexPtr); + virtual void unlock(); + + virtual void prepare(); + +#ifdef TORQUE_DEBUG + //GFXD3D9PrimitiveBuffer *next; +#endif + + // GFXResource interface + virtual void zombify(); + virtual void resurrect(); +}; + +inline GFXD3D9PrimitiveBuffer::GFXD3D9PrimitiveBuffer( GFXDevice *device, + U32 indexCount, + U32 primitiveCount, + GFXBufferType bufferType ) + : GFXPrimitiveBuffer( device, indexCount, primitiveCount, bufferType ) +{ + mVolatileStart = 0; + ib = NULL; + mIsFirstLock = true; + mLocked = false; +#ifdef TORQUE_DEBUG + mDebugGuardBuffer = NULL; + mLockedBuffer = NULL; + mLockedSize = 0; +#endif +} + +#endif diff --git a/Engine/source/gfx/D3D9/gfxD3D9QueryFence.cpp b/Engine/source/gfx/D3D9/gfxD3D9QueryFence.cpp new file mode 100644 index 000000000..7056e00b2 --- /dev/null +++ b/Engine/source/gfx/D3D9/gfxD3D9QueryFence.cpp @@ -0,0 +1,106 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/D3D9/gfxD3D9Device.h" +#include "gfx/D3D9/gfxD3D9QueryFence.h" + +GFXD3D9QueryFence::~GFXD3D9QueryFence() +{ + SAFE_RELEASE( mQuery ); +} + +//------------------------------------------------------------------------------ + +void GFXD3D9QueryFence::issue() +{ + PROFILE_START( GFXD3D9QueryFence_issue ); + + // Create the query if we need to + if( mQuery == NULL ) + { + HRESULT hRes = static_cast( mDevice )->getDevice()->CreateQuery( D3DQUERYTYPE_EVENT, &mQuery ); + + AssertFatal( hRes != D3DERR_NOTAVAILABLE, "Hardware does not support D3D9 Queries, this should be caught before this fence type is created" ); + AssertISV( hRes != E_OUTOFMEMORY, "Out of memory" ); + } + + // Issue the query + mQuery->Issue( D3DISSUE_END ); + + PROFILE_END(); +} + +//------------------------------------------------------------------------------ + +GFXFence::FenceStatus GFXD3D9QueryFence::getStatus() const +{ + if( mQuery == NULL ) + return GFXFence::Unset; + + HRESULT hRes = mQuery->GetData( NULL, 0, 0 ); + + return ( hRes == S_OK ? GFXFence::Processed : GFXFence::Pending ); +} + +//------------------------------------------------------------------------------ + +void GFXD3D9QueryFence::block() +{ + PROFILE_SCOPE(GFXD3D9QueryFence_block); + + // Calling block() before issue() is valid, catch this case + if( mQuery == NULL ) + return; + + HRESULT hRes; + while( ( hRes = mQuery->GetData( NULL, 0, D3DGETDATA_FLUSH ) ) == S_FALSE ) + ; + + // Check for D3DERR_DEVICELOST, if we lost the device, the fence will get + // re-created next issue() + if( hRes == D3DERR_DEVICELOST ) + SAFE_RELEASE( mQuery ); +} + +void GFXD3D9QueryFence::zombify() +{ + // Release our query + SAFE_RELEASE( mQuery ); +} + +void GFXD3D9QueryFence::resurrect() +{ + // Recreate the query + if( mQuery == NULL ) + { + HRESULT hRes = static_cast( mDevice )->getDevice()->CreateQuery( D3DQUERYTYPE_EVENT, &mQuery ); + + AssertFatal( hRes != D3DERR_NOTAVAILABLE, "GFXD3D9QueryFence::resurrect - Hardware does not support D3D9 Queries, this should be caught before this fence type is created" ); + AssertISV( hRes != E_OUTOFMEMORY, "GFXD3D9QueryFence::resurrect - Out of memory" ); + } +} + +const String GFXD3D9QueryFence::describeSelf() const +{ + // We've got nothing + return String(); +} \ No newline at end of file diff --git a/Engine/source/gfx/D3D9/gfxD3D9QueryFence.h b/Engine/source/gfx/D3D9/gfxD3D9QueryFence.h new file mode 100644 index 000000000..9318283ac --- /dev/null +++ b/Engine/source/gfx/D3D9/gfxD3D9QueryFence.h @@ -0,0 +1,52 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFX_D3D9_QUERYFENCE_H_ +#define _GFX_D3D9_QUERYFENCE_H_ + +#include "gfx/gfxFence.h" +#include "gfx/gfxResource.h" + +struct IDirect3DQuery9; + +class GFXD3D9QueryFence : public GFXFence +{ +private: + mutable IDirect3DQuery9 *mQuery; + +public: + GFXD3D9QueryFence( GFXDevice *device ) : GFXFence( device ), mQuery( NULL ) {}; + virtual ~GFXD3D9QueryFence(); + + virtual void issue(); + virtual FenceStatus getStatus() const; + virtual void block(); + + + + // GFXResource interface + virtual void zombify(); + virtual void resurrect(); + virtual const String describeSelf() const; +}; + +#endif \ No newline at end of file diff --git a/Engine/source/gfx/D3D9/gfxD3D9Shader.cpp b/Engine/source/gfx/D3D9/gfxD3D9Shader.cpp new file mode 100644 index 000000000..87db8fe75 --- /dev/null +++ b/Engine/source/gfx/D3D9/gfxD3D9Shader.cpp @@ -0,0 +1,1441 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#if defined(TORQUE_OS_XENON) +# include +#else +# include +#endif + +#include "gfx/D3D9/gfxD3D9Shader.h" +#include "gfx/D3D9/gfxD3D9Device.h" + +#include "core/frameAllocator.h" +#include "core/stream/fileStream.h" +#include "core/util/safeDelete.h" +#include "console/console.h" + +extern bool gDisassembleAllShaders; + +/// D3DXInclude plugin +class _gfxD3DXInclude : public ID3DXInclude, public StrongRefBase +{ +private: + + Vector mLastPath; + +public: + + void setPath( const String &path ) + { + mLastPath.clear(); + mLastPath.push_back( path ); + } + + _gfxD3DXInclude() {} + virtual ~_gfxD3DXInclude() {} + + STDMETHOD(Close)(THIS_ LPCVOID pData); + + // 360 + STDMETHOD(Open)(THIS_ D3DXINCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID *ppData, UINT *pBytes, /* OUT */ LPSTR pFullPath, DWORD cbFullPath); + + // PC + STDMETHOD(Open)(THIS_ D3DXINCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID *ppData, UINT *pBytes) + { + return Open( IncludeType, pFileName, pParentData, ppData, pBytes, NULL, 0 ); + } +}; + +_gfxD3DXIncludeRef GFXD3D9Shader::smD3DXInclude = NULL; + +HRESULT _gfxD3DXInclude::Open(THIS_ D3DXINCLUDE_TYPE IncludeType, LPCSTR pFileName, + LPCVOID pParentData, LPCVOID *ppData, UINT *pBytes, + LPSTR pFullPath, DWORD cbFullPath) +{ + // First try making the path relative to the parent. + Torque::Path path = Torque::Path::Join( mLastPath.last(), '/', pFileName ); + path = Torque::Path::CompressPath( path ); + + if ( !Torque::FS::ReadFile( path, (void *&)*ppData, *pBytes, true ) ) + { + // Ok... now try using the path as is. + path = String( pFileName ); + path = Torque::Path::CompressPath( path ); + + if ( !Torque::FS::ReadFile( path, (void *&)*ppData, *pBytes, true ) ) + { + AssertISV(false, avar( "Failed to open include '%s'.", pFileName)); + return E_FAIL; + } + } + + // If the data was of zero size then we cannot recurse + // into this file and DX won't call Close() below. + // + // So in this case don't push on the path. + if ( *pBytes > 0 ) + mLastPath.push_back( path.getRootAndPath() ); + + return S_OK; +} + +HRESULT _gfxD3DXInclude::Close( THIS_ LPCVOID pData ) +{ + // Free the data file and pop its path off the stack. + delete [] (U8*)pData; + mLastPath.pop_back(); + + return S_OK; +} + +GFXD3D9ShaderConstHandle::GFXD3D9ShaderConstHandle() +{ + clear(); +} + +const String& GFXD3D9ShaderConstHandle::getName() const +{ + if ( mVertexConstant ) + return mVertexHandle.name; + else + return mPixelHandle.name; +} + +GFXShaderConstType GFXD3D9ShaderConstHandle::getType() const +{ + if ( mVertexConstant ) + return mVertexHandle.constType; + else + return mPixelHandle.constType; +} + +U32 GFXD3D9ShaderConstHandle::getArraySize() const +{ + if ( mVertexConstant ) + return mVertexHandle.arraySize; + else + return mPixelHandle.arraySize; +} + +S32 GFXD3D9ShaderConstHandle::getSamplerRegister() const +{ + if ( !mValid || !isSampler() ) + return -1; + + // We always store sampler type and register index in the pixelHandle, + // sampler registers are shared between vertex and pixel shaders anyway. + + return mPixelHandle.offset; +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + + +bool GFXD3D9ShaderBufferLayout::setMatrix(const ParamDesc& pd, const GFXShaderConstType constType, const U32 size, const void* data, U8* basePointer) +{ + PROFILE_SCOPE(GFXD3D9ShaderBufferLayout_setMatrix); + + if (pd.constType == GFXSCT_Float4x4) + { + // Special case, we can just blast this guy. + AssertFatal(pd.size >= size, "Not enough room in the buffer for this data!"); + if (dMemcmp(basePointer+pd.offset, data, size) != 0) + { + dMemcpy(basePointer+pd.offset, data, size); + return true; + } + + return false; + } + else + { + PROFILE_SCOPE(GFXD3D9ShaderBufferLayout_setMatrix_not4x4); + + // Figure out how big of a chunk we are copying. We're going to copy 4 columns by N rows of data + U32 csize; + switch (pd.constType) + { + case GFXSCT_Float2x2 : + csize = 32; + break; + case GFXSCT_Float3x3 : + csize = 48; + break; + default: + AssertFatal(false, "Unhandled case!"); + return false; + break; + } + + // Loop through and copy + bool ret = false; + U8* currDestPointer = basePointer+pd.offset; + const U8* currSourcePointer = static_cast(data); + const U8* endData = currSourcePointer + size; + while (currSourcePointer < endData) + { + if (dMemcmp(currDestPointer, currSourcePointer, csize) != 0) + { + dMemcpy(currDestPointer, currSourcePointer, csize); + ret = true; + } + + currDestPointer += csize; + currSourcePointer += sizeof(MatrixF); + } + + return ret; + } +} + +//------------------------------------------------------------------------------ +GFXD3D9ShaderConstBuffer::GFXD3D9ShaderConstBuffer( GFXD3D9Shader* shader, + GFXD3D9ShaderBufferLayout* vertexLayoutF, + GFXD3D9ShaderBufferLayout* vertexLayoutI, + GFXD3D9ShaderBufferLayout* pixelLayoutF, + GFXD3D9ShaderBufferLayout* pixelLayoutI ) +{ + AssertFatal( shader, "GFXD3D9ShaderConstBuffer() - Got a null shader!" ); + + // We hold on to this so we don't have to call + // this virtual method during activation. + mDevice = static_cast( GFX )->getDevice(); + + mShader = shader; + + // TODO: Remove buffers and layouts that don't exist for performance? + + mVertexConstBufferLayoutF = vertexLayoutF; + mVertexConstBufferF = new GenericConstBuffer(vertexLayoutF); + mVertexConstBufferLayoutI = vertexLayoutI; + mVertexConstBufferI = new GenericConstBuffer(vertexLayoutI); + mPixelConstBufferLayoutF = pixelLayoutF; + mPixelConstBufferF = new GenericConstBuffer(pixelLayoutF); + mPixelConstBufferLayoutI = pixelLayoutI; + mPixelConstBufferI = new GenericConstBuffer(pixelLayoutI); +} + +GFXD3D9ShaderConstBuffer::~GFXD3D9ShaderConstBuffer() +{ + SAFE_DELETE(mVertexConstBufferF); + SAFE_DELETE(mPixelConstBufferF); + SAFE_DELETE(mVertexConstBufferI); + SAFE_DELETE(mPixelConstBufferI); + + if ( mShader ) + mShader->_unlinkBuffer( this ); +} + +GFXShader* GFXD3D9ShaderConstBuffer::getShader() +{ + return mShader; +} + +// This is kind of cheesy, but I don't think templates would work well here because +// these functions potentially need to be handled differently by other derived types +template +inline void GFXD3D9ShaderConstBuffer::SET_CONSTANT( GFXShaderConstHandle* handle, const T& fv, GenericConstBuffer *vBuffer, GenericConstBuffer *pBuffer ) +{ + AssertFatal(dynamic_cast(handle), "Incorrect const buffer type!"); + const GFXD3D9ShaderConstHandle* h = static_cast(handle); + AssertFatal(h, "Handle is NULL!" ); + AssertFatal(h->isValid(), "Handle is not valid!" ); + AssertFatal(!h->isSampler(), "Handle is sampler constant!" ); + AssertFatal(!mShader.isNull(), "Buffer's shader is null!" ); + AssertFatal(!h->mShader.isNull(), "Handle's shader is null!" ); + AssertFatal(h->mShader.getPointer() == mShader.getPointer(), "Mismatched shaders!"); + if ( h->mInstancingConstant ) + { + dMemcpy( mInstPtr+h->mPixelHandle.offset, &fv, sizeof( fv ) ); + return; + } + if (h->mVertexConstant) + vBuffer->set(h->mVertexHandle, fv); + if (h->mPixelConstant) + pBuffer->set(h->mPixelHandle, fv); +} + +void GFXD3D9ShaderConstBuffer::set(GFXShaderConstHandle* handle, const F32 fv) +{ + SET_CONSTANT(handle, fv, mVertexConstBufferF, mPixelConstBufferF); +} + +void GFXD3D9ShaderConstBuffer::set(GFXShaderConstHandle* handle, const Point2F& fv) +{ + SET_CONSTANT(handle, fv, mVertexConstBufferF, mPixelConstBufferF); +} + +void GFXD3D9ShaderConstBuffer::set(GFXShaderConstHandle* handle, const Point3F& fv) +{ + SET_CONSTANT(handle, fv, mVertexConstBufferF, mPixelConstBufferF); +} + +void GFXD3D9ShaderConstBuffer::set(GFXShaderConstHandle* handle, const Point4F& fv) +{ + SET_CONSTANT(handle, fv, mVertexConstBufferF, mPixelConstBufferF); +} + +void GFXD3D9ShaderConstBuffer::set(GFXShaderConstHandle* handle, const PlaneF& fv) +{ + SET_CONSTANT(handle, fv, mVertexConstBufferF, mPixelConstBufferF); +} + +void GFXD3D9ShaderConstBuffer::set(GFXShaderConstHandle* handle, const ColorF& fv) +{ + SET_CONSTANT(handle, fv, mVertexConstBufferF, mPixelConstBufferF); +} + +void GFXD3D9ShaderConstBuffer::set(GFXShaderConstHandle* handle, const S32 f) +{ + // This is the only type that is allowed to be used + // with a sampler shader constant type, but it is only + // allowed to be set from GLSL. + // + // So we ignore it here... all other cases will assert. + // + if ( ((GFXD3D9ShaderConstHandle*)handle)->isSampler() ) + return; + + SET_CONSTANT(handle, f, mVertexConstBufferI, mPixelConstBufferI); +} + +void GFXD3D9ShaderConstBuffer::set(GFXShaderConstHandle* handle, const Point2I& fv) +{ + SET_CONSTANT(handle, fv, mVertexConstBufferI, mPixelConstBufferI); +} + +void GFXD3D9ShaderConstBuffer::set(GFXShaderConstHandle* handle, const Point3I& fv) +{ + SET_CONSTANT(handle, fv, mVertexConstBufferI, mPixelConstBufferI); +} + +void GFXD3D9ShaderConstBuffer::set(GFXShaderConstHandle* handle, const Point4I& fv) +{ + SET_CONSTANT(handle, fv, mVertexConstBufferI, mPixelConstBufferI); +} + +void GFXD3D9ShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray& fv) +{ + SET_CONSTANT(handle, fv, mVertexConstBufferF, mPixelConstBufferF); +} + +void GFXD3D9ShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray& fv) +{ + SET_CONSTANT(handle, fv, mVertexConstBufferF, mPixelConstBufferF); +} + +void GFXD3D9ShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray& fv) +{ + SET_CONSTANT(handle, fv, mVertexConstBufferF, mPixelConstBufferF); +} + +void GFXD3D9ShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray& fv) +{ + SET_CONSTANT(handle, fv, mVertexConstBufferF, mPixelConstBufferF); +} + +void GFXD3D9ShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray& fv) +{ + SET_CONSTANT(handle, fv, mVertexConstBufferI, mPixelConstBufferI); +} + +void GFXD3D9ShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray& fv) +{ + SET_CONSTANT(handle, fv, mVertexConstBufferI, mPixelConstBufferI); +} + +void GFXD3D9ShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray& fv) +{ + SET_CONSTANT(handle, fv, mVertexConstBufferI, mPixelConstBufferI); +} + +void GFXD3D9ShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray& fv) +{ + SET_CONSTANT(handle, fv, mVertexConstBufferI, mPixelConstBufferI); +} +#undef SET_CONSTANT + +void GFXD3D9ShaderConstBuffer::set(GFXShaderConstHandle* handle, const MatrixF& mat, const GFXShaderConstType matrixType) +{ + AssertFatal(handle, "Handle is NULL!" ); + AssertFatal(handle->isValid(), "Handle is not valid!" ); + + AssertFatal(dynamic_cast(handle), "Incorrect const buffer type!"); + const GFXD3D9ShaderConstHandle* h = static_cast(handle); + AssertFatal(!h->isSampler(), "Handle is sampler constant!" ); + AssertFatal(h->mShader == mShader, "Mismatched shaders!"); + + MatrixF transposed; + mat.transposeTo(transposed); + + if (h->mInstancingConstant) + { + if ( matrixType == GFXSCT_Float4x4 ) + dMemcpy( mInstPtr+h->mPixelHandle.offset, mat, sizeof( mat ) ); + + // TODO: Support 3x3 and 2x2 matricies? + return; + } + + if (h->mVertexConstant) + mVertexConstBufferF->set(h->mVertexHandle, transposed, matrixType); + if (h->mPixelConstant) + mPixelConstBufferF->set(h->mPixelHandle, transposed, matrixType); +} + +void GFXD3D9ShaderConstBuffer::set(GFXShaderConstHandle* handle, const MatrixF* mat, const U32 arraySize, const GFXShaderConstType matrixType) +{ + AssertFatal(handle, "Handle is NULL!" ); + AssertFatal(handle->isValid(), "Handle is not valid!" ); + + AssertFatal(dynamic_cast(handle), "Incorrect const buffer type!"); + const GFXD3D9ShaderConstHandle* h = static_cast(handle); + AssertFatal(!h->isSampler(), "Handle is sampler constant!" ); + AssertFatal(h->mShader == mShader, "Mismatched shaders!"); + + static Vector transposed; + if (arraySize > transposed.size()) + transposed.setSize(arraySize); + for (U32 i = 0; i < arraySize; i++) + mat[i].transposeTo(transposed[i]); + + // TODO: Maybe support this in the future? + if (h->mInstancingConstant) + return; + + if (h->mVertexConstant) + mVertexConstBufferF->set(h->mVertexHandle, transposed.begin(), arraySize, matrixType); + if (h->mPixelConstant) + mPixelConstBufferF->set(h->mPixelHandle, transposed.begin(), arraySize, matrixType); +} + +const String GFXD3D9ShaderConstBuffer::describeSelf() const +{ + String ret; + ret = String(" GFXD3D9ShaderConstBuffer\n"); + + for (U32 i = 0; i < mVertexConstBufferLayoutF->getParameterCount(); i++) + { + GenericConstBufferLayout::ParamDesc pd; + mVertexConstBufferLayoutF->getDesc(i, pd); + + ret += String::ToString(" Constant name: %s", pd.name); + } + + return ret; +} + +void GFXD3D9ShaderConstBuffer::zombify() +{ +} + +void GFXD3D9ShaderConstBuffer::resurrect() +{ +} + +bool GFXD3D9ShaderConstBuffer::isDirty() +{ + bool ret = mVertexConstBufferF->isDirty(); + ret |= mVertexConstBufferI->isDirty(); + ret |= mPixelConstBufferF->isDirty(); + ret |= mPixelConstBufferI->isDirty(); + return ret; +} + +void GFXD3D9ShaderConstBuffer::activate( GFXD3D9ShaderConstBuffer *prevShaderBuffer ) +{ + PROFILE_SCOPE(GFXD3D9ShaderConstBuffer_activate); + + // NOTE: This is a really critical function as it gets + // called between every draw call to update the constants. + // + // Alot of the calls here are inlined... be careful + // what you change. + + // If the buffer has changed we need to compare it + // with the new buffer to see if we can skip copying + // equal buffer content. + // + // If the buffer hasn't changed then we only will + // be copying the changes that have occured since + // the last activate call. + // + if ( prevShaderBuffer != this ) + { + // If the previous buffer is dirty, than we can't compare + // against it, because it hasn't sent its contents to the + // card yet and must be copied. + if ( prevShaderBuffer && !prevShaderBuffer->isDirty() ) + { + PROFILE_SCOPE(GFXD3D9ShaderConstBuffer_activate_dirty_check_1); + + // If the buffer content is equal then we set the dirty + // flag to false knowing the current state of the card matches + // the new buffer. + // + // If the content is not equal we set the dirty flag to + // true which causes the full content of the buffer to be + // copied to the card. + // + mVertexConstBufferF->setDirty( !prevShaderBuffer->mVertexConstBufferF->isEqual( mVertexConstBufferF ) ); + mPixelConstBufferF->setDirty( !prevShaderBuffer->mPixelConstBufferF->isEqual( mPixelConstBufferF ) ); + mVertexConstBufferI->setDirty( !prevShaderBuffer->mVertexConstBufferF->isEqual( mVertexConstBufferI ) ); + mPixelConstBufferI->setDirty( !prevShaderBuffer->mPixelConstBufferF->isEqual( mPixelConstBufferI ) ); + } + else + { + // This happens rarely... but it can happen. + // + // We copy the entire dirty state to the card. + + PROFILE_SCOPE(GFXD3D9ShaderConstBuffer_activate_dirty_check_2); + + mVertexConstBufferF->setDirty( true ); + mPixelConstBufferF->setDirty( true ); + mVertexConstBufferI->setDirty( true ); + mPixelConstBufferI->setDirty( true ); + } + } + + const U32 bytesToFloat4 = 16; + const U32 bytesToInt4 = 16; + U32 start, bufferSize; + const U8* buf; + + if ( mVertexConstBufferF->isDirty() ) + { + buf = mVertexConstBufferF->getDirtyBuffer( &start, &bufferSize ); + mDevice->SetVertexShaderConstantF( start / bytesToFloat4, (float*)buf, bufferSize / bytesToFloat4 ); + } + + if ( mPixelConstBufferF->isDirty() ) + { + buf = mPixelConstBufferF->getDirtyBuffer( &start, &bufferSize ); + mDevice->SetPixelShaderConstantF( start / bytesToFloat4, (float*)buf, bufferSize / bytesToFloat4 ); + } + + if ( mVertexConstBufferI->isDirty() ) + { + buf = mVertexConstBufferI->getDirtyBuffer( &start, &bufferSize ); + mDevice->SetVertexShaderConstantI( start / bytesToInt4, (int*)buf, bufferSize / bytesToInt4 ); + } + + if ( mPixelConstBufferI->isDirty() ) + { + buf = mPixelConstBufferI->getDirtyBuffer( &start, &bufferSize ); + mDevice->SetPixelShaderConstantI( start / bytesToInt4, (int*)buf, bufferSize / bytesToInt4 ); + } + + #ifdef TORQUE_DEBUG + + // Make sure all the constants for this buffer were assigned. + if ( mWasLost ) + { + mVertexConstBufferF->assertUnassignedConstants( mShader->getVertexShaderFile().c_str() ); + mVertexConstBufferI->assertUnassignedConstants( mShader->getVertexShaderFile().c_str() ); + mPixelConstBufferF->assertUnassignedConstants( mShader->getPixelShaderFile().c_str() ); + mPixelConstBufferI->assertUnassignedConstants( mShader->getPixelShaderFile().c_str() ); + } + + #endif + + // Clear the lost state. + mWasLost = false; +} + +void GFXD3D9ShaderConstBuffer::onShaderReload( GFXD3D9Shader *shader ) +{ + AssertFatal( shader == mShader, "GFXD3D9ShaderConstBuffer::onShaderReload is hosed!" ); + + SAFE_DELETE( mVertexConstBufferF ); + SAFE_DELETE( mPixelConstBufferF ); + SAFE_DELETE( mVertexConstBufferI ); + SAFE_DELETE( mPixelConstBufferI ); + + AssertFatal( mVertexConstBufferLayoutF == shader->mVertexConstBufferLayoutF, "GFXD3D9ShaderConstBuffer::onShaderReload is hosed!" ); + AssertFatal( mPixelConstBufferLayoutF == shader->mPixelConstBufferLayoutF, "GFXD3D9ShaderConstBuffer::onShaderReload is hosed!" ); + AssertFatal( mVertexConstBufferLayoutI == shader->mVertexConstBufferLayoutI, "GFXD3D9ShaderConstBuffer::onShaderReload is hosed!" ); + AssertFatal( mPixelConstBufferLayoutI == shader->mPixelConstBufferLayoutI, "GFXD3D9ShaderConstBuffer::onShaderReload is hosed!" ); + + mVertexConstBufferF = new GenericConstBuffer( mVertexConstBufferLayoutF ); + mVertexConstBufferI = new GenericConstBuffer( mVertexConstBufferLayoutI ); + mPixelConstBufferF = new GenericConstBuffer( mPixelConstBufferLayoutF ); + mPixelConstBufferI = new GenericConstBuffer( mPixelConstBufferLayoutI ); + + // Set the lost state. + mWasLost = true; +} + +//------------------------------------------------------------------------------ + +GFXD3D9Shader::GFXD3D9Shader() +{ + VECTOR_SET_ASSOCIATION( mShaderConsts ); + + mD3D9Device = dynamic_cast(GFX)->getDevice(); + AssertFatal(mD3D9Device, "Invalid device for shader."); + mVertShader = NULL; + mPixShader = NULL; + mVertexConstBufferLayoutF = NULL; + mPixelConstBufferLayoutF = NULL; + mVertexConstBufferLayoutI = NULL; + mPixelConstBufferLayoutI = NULL; + + if( smD3DXInclude == NULL ) + smD3DXInclude = new _gfxD3DXInclude; +} + +//------------------------------------------------------------------------------ + +GFXD3D9Shader::~GFXD3D9Shader() +{ + for (HandleMap::Iterator i = mHandles.begin(); i != mHandles.end(); i++) + delete i->value; + SAFE_DELETE(mVertexConstBufferLayoutF); + SAFE_DELETE(mPixelConstBufferLayoutF); + SAFE_DELETE(mVertexConstBufferLayoutI); + SAFE_DELETE(mPixelConstBufferLayoutI); + SAFE_RELEASE(mVertShader); + SAFE_RELEASE(mPixShader); +} + +bool GFXD3D9Shader::_init() +{ + PROFILE_SCOPE( GFXD3D9Shader_Init ); + + if ( mPixVersion > GFX->getPixelShaderVersion() ) + { + if ( smLogErrors ) + Con::errorf( "GFXD3D9Shader::init - Bad pixel shader version!" ); + + return false; + } + + if ( mPixVersion < 1.0f && mPixelFile.getFileName().isNotEmpty() ) + { + if ( smLogErrors ) + Con::errorf( "GFXD3D9Shader::init - Pixel shaders not supported on SM %.1f!", mPixVersion ); + + return false; + } + + SAFE_RELEASE(mVertShader); + SAFE_RELEASE(mPixShader); + + U32 mjVer = (U32)mFloor( mPixVersion ); + U32 mnVer = (U32)( ( mPixVersion - F32( mjVer ) ) * 10.01f ); // 10.01 instead of 10.0 because of floating point issues + + String vertTarget = String::ToString("vs_%d_%d", mjVer, mnVer); + String pixTarget = String::ToString("ps_%d_%d", mjVer, mnVer); + + // Adjust version for vertex shaders + if (mjVer == 2 && mnVer == 1) + { + pixTarget = "ps_2_a"; + vertTarget = "vs_2_0"; + } + else if ( mjVer == 2 && mnVer == 2 ) + { + pixTarget = "ps_2_b"; + vertTarget = "vs_2_0"; + } + else if ( ( mPixVersion < 2.0f ) && ( mPixVersion > 1.101f ) ) + vertTarget = "vs_1_1"; + + // Create the macro array including the system wide macros. + const U32 macroCount = smGlobalMacros.size() + mMacros.size() + 2; + FrameTemp d3dXMacros( macroCount ); + for ( U32 i=0; i < smGlobalMacros.size(); i++ ) + { + d3dXMacros[i].Name = smGlobalMacros[i].name.c_str(); + d3dXMacros[i].Definition = smGlobalMacros[i].value.c_str(); + } + for ( U32 i=0; i < mMacros.size(); i++ ) + { + d3dXMacros[i+smGlobalMacros.size()].Name = mMacros[i].name.c_str(); + d3dXMacros[i+smGlobalMacros.size()].Definition = mMacros[i].value.c_str(); + } + String smVersion = String::ToString( mjVer * 10 + mnVer ); + d3dXMacros[macroCount - 2].Name = "TORQUE_SM"; + d3dXMacros[macroCount - 2].Definition = smVersion.c_str(); + d3dXMacros[macroCount - 1].Name = NULL; + d3dXMacros[macroCount - 1].Definition = NULL; + + if ( !mVertexConstBufferLayoutF ) + mVertexConstBufferLayoutF = new GFXD3D9ShaderBufferLayout(); + else + mVertexConstBufferLayoutF->clear(); + + if ( !mVertexConstBufferLayoutI ) + mVertexConstBufferLayoutI = new GFXD3D9ShaderBufferLayout(); + else + mVertexConstBufferLayoutI->clear(); + + if ( !mPixelConstBufferLayoutF ) + mPixelConstBufferLayoutF = new GFXD3D9ShaderBufferLayout(); + else + mPixelConstBufferLayoutF->clear(); + + if ( !mPixelConstBufferLayoutI ) + mPixelConstBufferLayoutI = new GFXD3D9ShaderBufferLayout(); + else + mPixelConstBufferLayoutI->clear(); + + mSamplerDescriptions.clear(); + mShaderConsts.clear(); + + if ( GFXD3DX.isLoaded && !Con::getBoolVariable( "$shaders::forceLoadCSF", false ) ) + { + if ( !mVertexFile.isEmpty() && + !_compileShader( mVertexFile, vertTarget, d3dXMacros, mVertexConstBufferLayoutF, mVertexConstBufferLayoutI, mSamplerDescriptions ) ) + return false; + + if ( !mPixelFile.isEmpty() && + !_compileShader( mPixelFile, pixTarget, d3dXMacros, mPixelConstBufferLayoutF, mPixelConstBufferLayoutI, mSamplerDescriptions ) ) + return false; + } + else + { + if ( !_loadCompiledOutput( mVertexFile, vertTarget, mVertexConstBufferLayoutF, mVertexConstBufferLayoutI, mSamplerDescriptions ) ) + { + if ( smLogErrors ) + Con::errorf( "GFXD3D9Shader::init - Unable to load precompiled vertex shader for '%s'.", + mVertexFile.getFullPath().c_str() ); + + return false; + } + + if ( !_loadCompiledOutput( mPixelFile, pixTarget, mPixelConstBufferLayoutF, mPixelConstBufferLayoutI, mSamplerDescriptions ) ) + { + if ( smLogErrors ) + Con::errorf( "GFXD3D9Shader::init - Unable to load precompiled pixel shader for '%s'.", + mPixelFile.getFullPath().c_str() ); + + return false; + } + } + + // Existing handles are resored to an uninitialized state. + // Those that are found when parsing the layout parameters + // will then be re-initialized. + HandleMap::Iterator iter = mHandles.begin(); + for ( ; iter != mHandles.end(); iter++ ) + (iter->value)->clear(); + + _buildShaderConstantHandles(mVertexConstBufferLayoutF, true); + _buildShaderConstantHandles(mVertexConstBufferLayoutI, true); + _buildShaderConstantHandles(mPixelConstBufferLayoutF, false); + _buildShaderConstantHandles(mPixelConstBufferLayoutI, false); + _buildSamplerShaderConstantHandles( mSamplerDescriptions ); + _buildInstancingShaderConstantHandles(); + + // Notify any existing buffers that the buffer + // layouts have changed and they need to update. + Vector::iterator biter = mActiveBuffers.begin(); + for ( ; biter != mActiveBuffers.end(); biter++ ) + ((GFXD3D9ShaderConstBuffer*)(*biter))->onShaderReload( this ); + + return true; +} + +bool GFXD3D9Shader::_compileShader( const Torque::Path &filePath, + const String& target, + const D3DXMACRO *defines, + GenericConstBufferLayout* bufferLayoutF, + GenericConstBufferLayout* bufferLayoutI, + Vector &samplerDescriptions ) +{ + PROFILE_SCOPE( GFXD3D9Shader_CompileShader ); + + HRESULT res = D3DERR_INVALIDCALL; + LPD3DXBUFFER code = NULL; + LPD3DXBUFFER errorBuff = NULL; + +#ifdef TORQUE_DEBUG + U32 flags = D3DXSHADER_DEBUG; +#else + U32 flags = 0; +#endif + +#ifdef TORQUE_OS_XENON + flags |= D3DXSHADER_PREFER_FLOW_CONTROL; +#endif + +#ifdef D3DXSHADER_USE_LEGACY_D3DX9_31_DLL + if( D3DX_SDK_VERSION >= 32 ) + { + // will need to use old compiler for 1_1 shaders - check for pixel + // or vertex shader with appropriate version. + if ((target.compare("vs1", 3) == 0) || (target.compare("vs_1", 4) == 0)) + flags |= D3DXSHADER_USE_LEGACY_D3DX9_31_DLL; + + if ((target.compare("ps1", 3) == 0) || (target.compare("ps_1", 4) == 0)) + flags |= D3DXSHADER_USE_LEGACY_D3DX9_31_DLL; + } +#endif + +#if !defined(TORQUE_OS_XENON) && (D3DX_SDK_VERSION <= 40) +#error This version of the DirectX SDK is too old. Please install a newer version of the DirectX SDK: http://msdn.microsoft.com/en-us/directx/default.aspx +#endif + + ID3DXConstantTable* table = NULL; + + static String sHLSLStr( "hlsl" ); + static String sOBJStr( "obj" ); + + // Is it an HLSL shader? + if ( filePath.getExtension().equal(sHLSLStr, String::NoCase) ) + { + FrameAllocatorMarker fam; + char *buffer = NULL; + + // Set this so that the D3DXInclude::Open will have this + // information for relative paths. + smD3DXInclude->setPath( filePath.getRootAndPath() ); + + FileStream s; + if ( !s.open( filePath, Torque::FS::File::Read ) ) + { + AssertISV(false, avar("GFXD3D9Shader::initShader - failed to open shader '%s'.", filePath.getFullPath().c_str())); + + if ( smLogErrors ) + Con::errorf( "GFXD3D9Shader::_compileShader - Failed to open shader file '%s'.", + filePath.getFullPath().c_str() ); + + return false; + } + + // Convert the path which might have virtualized + // mount paths to a real file system path. + Torque::Path realPath; + if ( !FS::GetFSPath( filePath, realPath ) ) + realPath = filePath; + + // Add a #line pragma so that error and warning messages + // returned by the HLSL compiler report the right file. + String linePragma = String::ToString( "#line 1 \"%s\"\r\n", realPath.getFullPath().c_str() ); + U32 linePragmaLen = linePragma.length(); + + U32 bufSize = s.getStreamSize(); + buffer = (char *)fam.alloc( bufSize + linePragmaLen + 1 ); + dStrncpy( buffer, linePragma.c_str(), linePragmaLen ); + s.read( bufSize, buffer + linePragmaLen ); + buffer[bufSize+linePragmaLen] = 0; + + res = GFXD3DX.D3DXCompileShader( buffer, bufSize + linePragmaLen, defines, smD3DXInclude, "main", + target, flags, &code, &errorBuff, &table ); + } + + // Is it a precompiled obj shader? + else if ( filePath.getExtension().equal( sOBJStr, String::NoCase ) ) + { + FileStream s; + if(!s.open(filePath, Torque::FS::File::Read)) + { + AssertISV(false, avar("GFXD3D9Shader::initShader - failed to open shader '%s'.", filePath.getFullPath().c_str())); + + if ( smLogErrors ) + Con::errorf( "GFXD3D9Shader::_compileShader - Failed to open shader file '%s'.", + filePath.getFullPath().c_str() ); + + return false; + } + + res = GFXD3DX.D3DXCreateBuffer(s.getStreamSize(), &code); + AssertISV(res == D3D_OK, "Unable to create buffer!"); + s.read(s.getStreamSize(), code->GetBufferPointer()); + + if (res == D3D_OK) + { + DWORD* data = (DWORD*) code->GetBufferPointer(); + res = GFXD3DX.D3DXGetShaderConstantTable(data, &table); + } + } + else + { + if ( smLogErrors ) + Con::errorf( "GFXD3D9Shader::_compileShader - Unsupported shader file type '%s'.", + filePath.getFullPath().c_str() ); + + return false; + } + + if ( res != D3D_OK && smLogErrors ) + Con::errorf( "GFXD3D9Shader::_compileShader - Error compiling shader: %s: %s (%x)", + DXGetErrorStringA(res), DXGetErrorDescriptionA(res), res ); + + if ( errorBuff ) + { + // remove \n at end of buffer + U8 *buffPtr = (U8*) errorBuff->GetBufferPointer(); + U32 len = dStrlen( (const char*) buffPtr ); + buffPtr[len-1] = '\0'; + + if( res != D3D_OK ) + { + if ( smLogErrors ) + Con::errorf( " %s", (const char*) errorBuff->GetBufferPointer() ); + } + else + { + if ( smLogWarnings ) + Con::warnf( "%s", (const char*) errorBuff->GetBufferPointer() ); + } + } + else if ( code == NULL && smLogErrors ) + Con::errorf( "GFXD3D9Shader::_compileShader - no compiled code produced; possibly missing file '%s'.", + filePath.getFullPath().c_str() ); + + // Create the proper shader if we have code + if( code != NULL ) + { + #ifndef TORQUE_SHIPPING + + LPD3DXBUFFER disassem = NULL; + D3DXDisassembleShader( (DWORD*)code->GetBufferPointer(), false, NULL, &disassem ); + mDissasembly = (const char*)disassem->GetBufferPointer(); + SAFE_RELEASE( disassem ); + + if ( gDisassembleAllShaders ) + { + String filename = filePath.getFullPath(); + filename.replace( ".hlsl", "_dis.txt" ); + + FileStream *fstream = FileStream::createAndOpen( filename, Torque::FS::File::Write ); + if ( fstream ) + { + fstream->write( mDissasembly ); + fstream->close(); + delete fstream; + } + } + + #endif + + if (target.compare("ps_", 3) == 0) + res = mD3D9Device->CreatePixelShader( (DWORD*)code->GetBufferPointer(), &mPixShader ); + else + res = mD3D9Device->CreateVertexShader( (DWORD*)code->GetBufferPointer(), &mVertShader ); + + if (res == S_OK) + _getShaderConstants(table, bufferLayoutF, bufferLayoutI, samplerDescriptions); + +#ifdef TORQUE_ENABLE_CSF_GENERATION + + // Ok, we've got a valid shader and constants, let's write them all out. + if ( !_saveCompiledOutput(filePath, code, bufferLayoutF, bufferLayoutI) && smLogErrors ) + Con::errorf( "GFXD3D9Shader::_compileShader - Unable to save shader compile output for: %s", + filePath.getFullPath().c_str() ); + +#endif + + SAFE_RELEASE(table); + + if ( res != S_OK && smLogErrors ) + Con::errorf( "GFXD3D9Shader::_compileShader - Unable to create shader for '%s'.", + filePath.getFullPath().c_str() ); + } + + bool result = code != NULL && res == S_OK; + + SAFE_RELEASE( code ); + SAFE_RELEASE( errorBuff ); + + return result; +} + +void GFXD3D9Shader::_getShaderConstants( ID3DXConstantTable *table, + GenericConstBufferLayout *bufferLayoutF, + GenericConstBufferLayout* bufferLayoutI, + Vector &samplerDescriptions ) +{ + PROFILE_SCOPE( GFXD3D9Shader_GetShaderConstants ); + + AssertFatal(table, "NULL constant table not allowed, is this an assembly shader?"); + + D3DXCONSTANTTABLE_DESC tableDesc; + D3D9Assert(table->GetDesc(&tableDesc), "Unable to get constant table info."); + + for (U32 i = 0; i < tableDesc.Constants; i++) + { + D3DXHANDLE handle = table->GetConstant(0, i); + const U32 descSize=16; + D3DXCONSTANT_DESC constantDescArray[descSize]; + U32 size = descSize; + if (table->GetConstantDesc(handle, constantDescArray, &size) == S_OK) + { + D3DXCONSTANT_DESC& constantDesc = constantDescArray[0]; + GFXShaderConstDesc desc; + + desc.name = String(constantDesc.Name); + // Prepend a "$" if it doesn't exist. Just to make things consistent. + if (desc.name.find("$") != 0) + desc.name = String::ToString("$%s", desc.name.c_str()); + //Con::printf("name %s: , offset: %d, size: %d, constantDesc.Elements: %d", desc.name.c_str(), constantDesc.RegisterIndex, constantDesc.Bytes, constantDesc.Elements); + desc.arraySize = constantDesc.Elements; + + GenericConstBufferLayout* bufferLayout = NULL; + switch (constantDesc.RegisterSet) + { + case D3DXRS_INT4 : + { + bufferLayout = bufferLayoutI; + switch (constantDesc.Class) + { + case D3DXPC_SCALAR : + desc.constType = GFXSCT_Int; + break; + case D3DXPC_VECTOR : + { + switch (constantDesc.Columns) + { + case 1 : + desc.constType = GFXSCT_Int; + break; + case 2 : + desc.constType = GFXSCT_Int2; + break; + case 3 : + desc.constType = GFXSCT_Int3; + break; + case 4 : + desc.constType = GFXSCT_Int4; + break; + default: + AssertFatal(false, "Unknown int vector type!"); + break; + } + } + break; + } + desc.constType = GFXSCT_Int4; + break; + } + case D3DXRS_FLOAT4 : + { + bufferLayout = bufferLayoutF; + switch (constantDesc.Class) + { + case D3DXPC_SCALAR: + desc.constType = GFXSCT_Float; + break; + case D3DXPC_VECTOR : + { + switch (constantDesc.Columns) + { + case 1 : + desc.constType = GFXSCT_Float; + break; + case 2 : + desc.constType = GFXSCT_Float2; + break; + case 3 : + desc.constType = GFXSCT_Float3; + break; + case 4 : + desc.constType = GFXSCT_Float4; + break; + default: + AssertFatal(false, "Unknown float vector type!"); + break; + } + } + break; + case D3DXPC_MATRIX_ROWS : + case D3DXPC_MATRIX_COLUMNS : + { + switch (constantDesc.RegisterCount) + { + case 3 : + desc.constType = GFXSCT_Float3x3; + break; + case 4 : + desc.constType = GFXSCT_Float4x4; + break; + } + } + break; + case D3DXPC_OBJECT : + case D3DXPC_STRUCT : + bufferLayout = NULL; + break; + } + } + break; + case D3DXRS_SAMPLER : + { + AssertFatal( constantDesc.Elements == 1, "Sampler Arrays not yet supported!" ); + + switch (constantDesc.Type) + { + case D3DXPT_SAMPLER : + case D3DXPT_SAMPLER1D : + case D3DXPT_SAMPLER2D : + case D3DXPT_SAMPLER3D : + // Hi-jack the desc's arraySize to store the registerIndex. + desc.constType = GFXSCT_Sampler; + desc.arraySize = constantDesc.RegisterIndex; + samplerDescriptions.push_back( desc ); + break; + case D3DXPT_SAMPLERCUBE : + desc.constType = GFXSCT_SamplerCube; + desc.arraySize = constantDesc.RegisterIndex; + samplerDescriptions.push_back( desc ); + break; + } + } + break; + default: + AssertFatal(false, "Unknown shader constant class enum"); + break; + } + + if (bufferLayout) + { + mShaderConsts.push_back(desc); + + U32 alignBytes = getAlignmentValue(desc.constType); + U32 paramSize = alignBytes * desc.arraySize; + bufferLayout->addParameter( desc.name, + desc.constType, + constantDesc.RegisterIndex * sizeof(Point4F), + paramSize, + desc.arraySize, + alignBytes ); + } + } + else + AssertFatal(false, "Unable to get shader constant description! (may need more elements of constantDesc"); + } +} + +const U32 GFXD3D9Shader::smCompiledShaderTag = MakeFourCC('t','c','s','f'); + +bool GFXD3D9Shader::_saveCompiledOutput( const Torque::Path &filePath, + LPD3DXBUFFER buffer, + GenericConstBufferLayout *bufferLayoutF, + GenericConstBufferLayout *bufferLayoutI, + Vector &samplerDescriptions ) +{ + Torque::Path outputPath(filePath); + outputPath.setExtension("csf"); // "C"ompiled "S"hader "F"ile (fancy!) + + FileStream f; + if (!f.open(outputPath, Torque::FS::File::Write)) + return false; + if (!f.write(smCompiledShaderTag)) + return false; + // We could reverse engineer the structure in the compiled output, but this + // is a bit easier because we can just read it into the struct that we want. + if (!bufferLayoutF->write(&f)) + return false; + if (!bufferLayoutI->write(&f)) + return false; + U32 bufferSize = buffer->GetBufferSize(); + if (!f.write(bufferSize)) + return false; + if (!f.write(bufferSize, buffer->GetBufferPointer())) + return false; + + // Write out sampler descriptions. + + f.write( samplerDescriptions.size() ); + + for ( U32 i = 0; i < samplerDescriptions.size(); i++ ) + { + f.write( samplerDescriptions[i].name ); + f.write( (U32)(samplerDescriptions[i].constType) ); + f.write( samplerDescriptions[i].arraySize ); + } + + f.close(); + + return true; +} + +bool GFXD3D9Shader::_loadCompiledOutput( const Torque::Path &filePath, + const String &target, + GenericConstBufferLayout *bufferLayoutF, + GenericConstBufferLayout *bufferLayoutI, + Vector &samplerDescriptions ) +{ + Torque::Path outputPath(filePath); + outputPath.setExtension("csf"); // "C"ompiled "S"hader "F"ile (fancy!) + + FileStream f; + if (!f.open(outputPath, Torque::FS::File::Read)) + return false; + U32 fileTag; + if (!f.read(&fileTag)) + return false; + if (fileTag != smCompiledShaderTag) + return false; + if (!bufferLayoutF->read(&f)) + return false; + if (!bufferLayoutI->read(&f)) + return false; + U32 bufferSize; + if (!f.read(&bufferSize)) + return false; + U32 waterMark = FrameAllocator::getWaterMark(); + DWORD* buffer = static_cast(FrameAllocator::alloc(bufferSize)); + if (!f.read(bufferSize, buffer)) + return false; + + // Read sampler descriptions. + + U32 samplerCount; + f.read( &samplerCount ); + + for ( U32 i = 0; i < samplerCount; i++ ) + { + GFXShaderConstDesc samplerDesc; + f.read( &(samplerDesc.name) ); + f.read( (U32*)&(samplerDesc.constType) ); + f.read( &(samplerDesc.arraySize) ); + + samplerDescriptions.push_back( samplerDesc ); + } + + f.close(); + + HRESULT res; + if (target.compare("ps_", 3) == 0) + res = mD3D9Device->CreatePixelShader(buffer, &mPixShader ); + else + res = mD3D9Device->CreateVertexShader(buffer, &mVertShader ); + AssertFatal(SUCCEEDED(res), "Unable to load shader!"); + + FrameAllocator::setWaterMark(waterMark); + return SUCCEEDED(res); +} + +void GFXD3D9Shader::_buildShaderConstantHandles(GenericConstBufferLayout* layout, bool vertexConst) +{ + for (U32 i = 0; i < layout->getParameterCount(); i++) + { + GenericConstBufferLayout::ParamDesc pd; + layout->getDesc(i, pd); + + GFXD3D9ShaderConstHandle* handle; + HandleMap::Iterator j = mHandles.find(pd.name); + + if (j != mHandles.end()) + { + handle = j->value; + handle->mShader = this; + handle->setValid( true ); + } + else + { + handle = new GFXD3D9ShaderConstHandle(); + handle->mShader = this; + mHandles[pd.name] = handle; + handle->setValid( true ); + } + + if ( vertexConst ) + { + handle->mVertexConstant = true; + handle->mVertexHandle = pd; + } + else + { + handle->mPixelConstant = true; + handle->mPixelHandle = pd; + } + } +} + +void GFXD3D9Shader::_buildSamplerShaderConstantHandles( Vector &samplerDescriptions ) +{ + Vector::iterator iter = samplerDescriptions.begin(); + for ( ; iter != samplerDescriptions.end(); iter++ ) + { + const GFXShaderConstDesc &desc = *iter; + + AssertFatal( desc.constType == GFXSCT_Sampler || + desc.constType == GFXSCT_SamplerCube, + "GFXD3D9Shader::_buildSamplerShaderConstantHandles - Invalid samplerDescription type!" ); + + GFXD3D9ShaderConstHandle *handle; + HandleMap::Iterator j = mHandles.find(desc.name); + + if ( j != mHandles.end() ) + handle = j->value; + else + { + handle = new GFXD3D9ShaderConstHandle(); + mHandles[desc.name] = handle; + } + + handle->mShader = this; + handle->setValid( true ); + handle->mPixelConstant = true; + handle->mPixelHandle.name = desc.name; + handle->mPixelHandle.constType = desc.constType; + handle->mPixelHandle.offset = desc.arraySize; + } +} + +void GFXD3D9Shader::_buildInstancingShaderConstantHandles() +{ + U32 offset = 0; + for ( U32 i=0; i < mInstancingFormat.getElementCount(); i++ ) + { + const GFXVertexElement &element = mInstancingFormat.getElement( i ); + + String constName = String::ToString( "$%s", element.getSemantic().c_str() ); + + GFXD3D9ShaderConstHandle *handle; + HandleMap::Iterator j = mHandles.find( constName ); + + if ( j != mHandles.end() ) + handle = j->value; + else + { + handle = new GFXD3D9ShaderConstHandle(); + mHandles[ constName ] = handle; + } + + handle->mShader = this; + handle->setValid( true ); + handle->mInstancingConstant = true; + + // We shouldn't have an instancing constant that is also + // a vertex or pixel constant! This means the shader features + // are confused as to what is instanced. + // + AssertFatal( !handle->mVertexConstant && + !handle->mPixelConstant, + "GFXD3D9Shader::_buildInstancingShaderConstantHandles - Bad instanced constant!" ); + + // HACK: The GFXD3D9ShaderConstHandle will check mVertexConstant then + // fall back to reading the mPixelHandle values. We depend on this here + // and store the data we need in the mPixelHandle constant although its + // not a pixel shader constant. + // + handle->mPixelHandle.name = constName; + handle->mPixelHandle.offset = offset; + + // If this is a matrix we will have 2 or 3 more of these + // semantics with the same name after it. + for ( ; i < mInstancingFormat.getElementCount(); i++ ) + { + const GFXVertexElement &nextElement = mInstancingFormat.getElement( i ); + if ( nextElement.getSemantic() != element.getSemantic() ) + { + i--; + break; + } + offset += nextElement.getSizeInBytes(); + } + } +} + +GFXShaderConstBufferRef GFXD3D9Shader::allocConstBuffer() +{ + if (mVertexConstBufferLayoutF && mPixelConstBufferLayoutF) + { + GFXD3D9ShaderConstBuffer* buffer = new GFXD3D9ShaderConstBuffer(this, mVertexConstBufferLayoutF, mVertexConstBufferLayoutI, mPixelConstBufferLayoutF, mPixelConstBufferLayoutI); + mActiveBuffers.push_back( buffer ); + buffer->registerResourceWithDevice(getOwningDevice()); + return buffer; + } else { + return NULL; + } +} + +/// Returns a shader constant handle for name, if the variable doesn't exist NULL is returned. +GFXShaderConstHandle* GFXD3D9Shader::getShaderConstHandle(const String& name) +{ + HandleMap::Iterator i = mHandles.find(name); + if ( i != mHandles.end() ) + { + return i->value; + } + else + { + GFXD3D9ShaderConstHandle *handle = new GFXD3D9ShaderConstHandle(); + handle->setValid( false ); + handle->mShader = this; + mHandles[name] = handle; + + return handle; + } +} + +const Vector& GFXD3D9Shader::getShaderConstDesc() const +{ + return mShaderConsts; +} + +U32 GFXD3D9Shader::getAlignmentValue(const GFXShaderConstType constType) const +{ + const U32 mRowSizeF = 16; + const U32 mRowSizeI = 16; + + switch (constType) + { + case GFXSCT_Float : + case GFXSCT_Float2 : + case GFXSCT_Float3 : + case GFXSCT_Float4 : + return mRowSizeF; + break; + // Matrices + case GFXSCT_Float2x2 : + return mRowSizeF * 2; + break; + case GFXSCT_Float3x3 : + return mRowSizeF * 3; + break; + case GFXSCT_Float4x4 : + return mRowSizeF * 4; + break; + //// Scalar + case GFXSCT_Int : + case GFXSCT_Int2 : + case GFXSCT_Int3 : + case GFXSCT_Int4 : + return mRowSizeI; + break; + default: + AssertFatal(false, "Unsupported type!"); + return 0; + break; + } +} + +void GFXD3D9Shader::zombify() +{ + // Shaders don't need zombification +} + +void GFXD3D9Shader::resurrect() +{ + // Shaders are never zombies, and therefore don't have to be brought back. +} diff --git a/Engine/source/gfx/D3D9/gfxD3D9Shader.h b/Engine/source/gfx/D3D9/gfxD3D9Shader.h new file mode 100644 index 000000000..1db493cf7 --- /dev/null +++ b/Engine/source/gfx/D3D9/gfxD3D9Shader.h @@ -0,0 +1,287 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXD3D9SHADER_H_ +#define _GFXD3D9SHADER_H_ + +#ifndef _PATH_H_ +#include "core/util/path.h" +#endif +#ifndef _TDICTIONARY_H_ +#include "core/util/tDictionary.h" +#endif +#ifndef _GFXSHADER_H_ +#include "gfx/gfxShader.h" +#endif +#ifndef _GFXRESOURCE_H_ +#include "gfx/gfxResource.h" +#endif +#ifndef _GENERICCONSTBUFFER_H_ +#include "gfx/genericConstBuffer.h" +#endif + + +class GFXD3D9Shader; +struct IDirect3DVertexShader9; +struct IDirect3DPixelShader9; +struct IDirect3DDevice9; +struct ID3DXConstantTable; +struct ID3DXBuffer; +struct _D3DXMACRO; + + +class GFXD3D9ShaderBufferLayout : public GenericConstBufferLayout +{ +protected: + /// Set a matrix, given a base pointer + virtual bool setMatrix(const ParamDesc& pd, const GFXShaderConstType constType, const U32 size, const void* data, U8* basePointer); +}; + + +/// The D3D9 implementation of a shader constant handle. +class GFXD3D9ShaderConstHandle : public GFXShaderConstHandle +{ +public: + + // GFXShaderConstHandle + const String& getName() const; + GFXShaderConstType getType() const; + U32 getArraySize() const; + + WeakRefPtr mShader; + + bool mVertexConstant; + GenericConstBufferLayout::ParamDesc mVertexHandle; + bool mPixelConstant; + GenericConstBufferLayout::ParamDesc mPixelHandle; + + /// Is true if this constant is for hardware mesh instancing. + /// + /// Note: We currently store its settings in mPixelHandle. + /// + bool mInstancingConstant; + + void setValid( bool valid ) { mValid = valid; } + S32 getSamplerRegister() const; + + // Returns true if this is a handle to a sampler register. + bool isSampler() const + { + return ( mPixelConstant && mPixelHandle.constType >= GFXSCT_Sampler ) || + ( mVertexConstant && mVertexHandle.constType >= GFXSCT_Sampler ); + } + + /// Restore to uninitialized state. + void clear() + { + mShader = NULL; + mVertexConstant = false; + mPixelConstant = false; + mInstancingConstant = false; + mVertexHandle.clear(); + mPixelHandle.clear(); + mValid = false; + } + + GFXD3D9ShaderConstHandle(); +}; + + +/// The D3D9 implementation of a shader constant buffer. +class GFXD3D9ShaderConstBuffer : public GFXShaderConstBuffer +{ + friend class GFXD3D9Shader; + +public: + + GFXD3D9ShaderConstBuffer( GFXD3D9Shader* shader, + GFXD3D9ShaderBufferLayout* vertexLayoutF, + GFXD3D9ShaderBufferLayout* vertexLayoutI, + GFXD3D9ShaderBufferLayout* pixelLayoutF, + GFXD3D9ShaderBufferLayout* pixelLayoutI ); + virtual ~GFXD3D9ShaderConstBuffer(); + + /// Called by GFXD3D9Device to activate this buffer. + /// @param mPrevShaderBuffer The previously active buffer + void activate( GFXD3D9ShaderConstBuffer *prevShaderBuffer ); + + /// Used internally by GXD3D9ShaderConstBuffer to determine if it's dirty. + bool isDirty(); + + /// Called from GFXD3D9Shader when constants have changed and need + /// to be the shader this buffer references is reloaded. + void onShaderReload( GFXD3D9Shader *shader ); + + // GFXShaderConstBuffer + virtual GFXShader* getShader(); + virtual void set(GFXShaderConstHandle* handle, const F32 fv); + virtual void set(GFXShaderConstHandle* handle, const Point2F& fv); + virtual void set(GFXShaderConstHandle* handle, const Point3F& fv); + virtual void set(GFXShaderConstHandle* handle, const Point4F& fv); + virtual void set(GFXShaderConstHandle* handle, const PlaneF& fv); + virtual void set(GFXShaderConstHandle* handle, const ColorF& fv); + virtual void set(GFXShaderConstHandle* handle, const S32 f); + virtual void set(GFXShaderConstHandle* handle, const Point2I& fv); + virtual void set(GFXShaderConstHandle* handle, const Point3I& fv); + virtual void set(GFXShaderConstHandle* handle, const Point4I& fv); + virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv); + virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv); + virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv); + virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv); + virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv); + virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv); + virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv); + virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv); + virtual void set(GFXShaderConstHandle* handle, const MatrixF& mat, const GFXShaderConstType matType = GFXSCT_Float4x4); + virtual void set(GFXShaderConstHandle* handle, const MatrixF* mat, const U32 arraySize, const GFXShaderConstType matrixType = GFXSCT_Float4x4); + + // GFXResource + virtual const String describeSelf() const; + virtual void zombify(); + virtual void resurrect(); + +protected: + + template + inline void SET_CONSTANT( GFXShaderConstHandle* handle, + const T& fv, + GenericConstBuffer *vBuffer, + GenericConstBuffer *pBuffer ); + + /// A cached direct pointer to the device. + IDirect3DDevice9 *mDevice; + + /// We keep a weak reference to the shader + /// because it will often be deleted. + WeakRefPtr mShader; + + GFXD3D9ShaderBufferLayout* mVertexConstBufferLayoutF; + GenericConstBuffer* mVertexConstBufferF; + GFXD3D9ShaderBufferLayout* mPixelConstBufferLayoutF; + GenericConstBuffer* mPixelConstBufferF; + GFXD3D9ShaderBufferLayout* mVertexConstBufferLayoutI; + GenericConstBuffer* mVertexConstBufferI; + GFXD3D9ShaderBufferLayout* mPixelConstBufferLayoutI; + GenericConstBuffer* mPixelConstBufferI; +}; + + +class _gfxD3DXInclude; +typedef StrongRefPtr<_gfxD3DXInclude> _gfxD3DXIncludeRef; + +/// The D3D9 implementation of a shader. +class GFXD3D9Shader : public GFXShader +{ + friend class GFXD3D9Device; + friend class GFX360Device; + friend class GFXD3D9ShaderConstBuffer; + friend class GFX360ShaderConstBuffer; +public: + typedef Map HandleMap; + + GFXD3D9Shader(); + virtual ~GFXD3D9Shader(); + + // GFXShader + virtual GFXShaderConstBufferRef allocConstBuffer(); + virtual const Vector& getShaderConstDesc() const; + virtual GFXShaderConstHandle* getShaderConstHandle(const String& name); + virtual U32 getAlignmentValue(const GFXShaderConstType constType) const; + virtual bool getDisassembly( String &outStr ) const; + + // GFXResource + virtual void zombify(); + virtual void resurrect(); + +protected: + + virtual bool _init(); + + static const U32 smCompiledShaderTag; + + IDirect3DDevice9 *mD3D9Device; + + IDirect3DVertexShader9 *mVertShader; + IDirect3DPixelShader9 *mPixShader; + + GFXD3D9ShaderBufferLayout* mVertexConstBufferLayoutF; + GFXD3D9ShaderBufferLayout* mPixelConstBufferLayoutF; + GFXD3D9ShaderBufferLayout* mVertexConstBufferLayoutI; + GFXD3D9ShaderBufferLayout* mPixelConstBufferLayoutI; + + static _gfxD3DXIncludeRef smD3DXInclude; + + HandleMap mHandles; + + /// The shader disassembly from DX when this shader is compiled. + /// We only store this data in non-release builds. + String mDissasembly; + + /// Vector of sampler type descriptions consolidated from _compileShader. + Vector mSamplerDescriptions; + + /// Vector of descriptions (consolidated for the getShaderConstDesc call) + Vector mShaderConsts; + + // These two functions are used when compiling shaders from hlsl + virtual bool _compileShader( const Torque::Path &filePath, + const String &target, + const _D3DXMACRO *defines, + GenericConstBufferLayout *bufferLayoutF, + GenericConstBufferLayout *bufferLayoutI, + Vector &samplerDescriptions ); + + void _getShaderConstants( ID3DXConstantTable* table, + GenericConstBufferLayout *bufferLayoutF, + GenericConstBufferLayout *bufferLayoutI, + Vector &samplerDescriptions ); + + bool _saveCompiledOutput( const Torque::Path &filePath, + ID3DXBuffer *buffer, + GenericConstBufferLayout *bufferLayoutF, + GenericConstBufferLayout *bufferLayoutI, + Vector &samplerDescriptions ); + + // Loads precompiled shaders + bool _loadCompiledOutput( const Torque::Path &filePath, + const String &target, + GenericConstBufferLayout *bufferLayoutF, + GenericConstBufferLayout *bufferLayoutI, + Vector &samplerDescriptions ); + + // This is used in both cases + virtual void _buildShaderConstantHandles( GenericConstBufferLayout *layout, bool vertexConst ); + + virtual void _buildSamplerShaderConstantHandles( Vector &samplerDescriptions ); + + /// Used to build the instancing shader constants from + /// the instancing vertex format. + void _buildInstancingShaderConstantHandles(); +}; + +inline bool GFXD3D9Shader::getDisassembly( String &outStr ) const +{ + outStr = mDissasembly; + return ( outStr.isNotEmpty() ); +} + +#endif // _GFXD3D9SHADER_H_ \ No newline at end of file diff --git a/Engine/source/gfx/D3D9/gfxD3D9StateBlock.cpp b/Engine/source/gfx/D3D9/gfxD3D9StateBlock.cpp new file mode 100644 index 000000000..b24a2c852 --- /dev/null +++ b/Engine/source/gfx/D3D9/gfxD3D9StateBlock.cpp @@ -0,0 +1,203 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/gfxDevice.h" + +#if defined(TORQUE_OS_XENON) +# include +#else +# include +#endif + +#include "gfx/D3D9/gfxD3D9StateBlock.h" +#include "gfx/D3D9/gfxD3D9EnumTranslate.h" + +GFXD3D9StateBlock::GFXD3D9StateBlock(const GFXStateBlockDesc& desc, LPDIRECT3DDEVICE9 d3dDevice) +{ + AssertFatal(d3dDevice, "Invalid mD3DDevice!"); + + mDesc = desc; + mCachedHashValue = desc.getHashValue(); + mD3DDevice = d3dDevice; + + // Color writes + mColorMask = 0; + mColorMask |= ( mDesc.colorWriteRed ? GFXCOLORWRITEENABLE_RED : 0 ); + mColorMask |= ( mDesc.colorWriteGreen ? GFXCOLORWRITEENABLE_GREEN : 0 ); + mColorMask |= ( mDesc.colorWriteBlue ? GFXCOLORWRITEENABLE_BLUE : 0 ); + mColorMask |= ( mDesc.colorWriteAlpha ? GFXCOLORWRITEENABLE_ALPHA : 0 ); + + // Z*bias + mZBias = *((U32*)&mDesc.zBias); + mZSlopeBias = *((U32*)&mDesc.zSlopeBias); +} + +GFXD3D9StateBlock::~GFXD3D9StateBlock() +{ + +} + +/// Returns the hash value of the desc that created this block +U32 GFXD3D9StateBlock::getHashValue() const +{ + return mCachedHashValue; +} + +/// Returns a GFXStateBlockDesc that this block represents +const GFXStateBlockDesc& GFXD3D9StateBlock::getDesc() const +{ + return mDesc; +} + +/// Called by D3D9 device to active this state block. +/// @param oldState The current state, used to make sure we don't set redundant states on the device. Pass NULL to reset all states. +void GFXD3D9StateBlock::activate(GFXD3D9StateBlock* oldState) +{ + PROFILE_SCOPE( GFXD3D9StateBlock_Activate ); + + // Little macro to save some typing, SD = state diff, checks for null source state block, then + // checks to see if the states differ +#if defined(TORQUE_OS_XENON) + #define SD(x, y) if (!oldState || oldState->mDesc.x != mDesc.x) \ + mD3DDevice->SetRenderState_Inline(y, mDesc.x) + + // Same as above, but allows you to set the data + #define SDD(x, y, z) if (!oldState || oldState->mDesc.x != mDesc.x) \ + mD3DDevice->SetRenderState_Inline(y, z) +#else + #define SD(x, y) if (!oldState || oldState->mDesc.x != mDesc.x) \ + mD3DDevice->SetRenderState(y, mDesc.x) + + // Same as above, but allows you to set the data + #define SDD(x, y, z) if (!oldState || oldState->mDesc.x != mDesc.x) \ + mD3DDevice->SetRenderState(y, z) +#endif + + // Blending + SD(blendEnable, D3DRS_ALPHABLENDENABLE); + SDD(blendSrc, D3DRS_SRCBLEND, GFXD3D9Blend[mDesc.blendSrc]); + SDD(blendDest, D3DRS_DESTBLEND, GFXD3D9Blend[mDesc.blendDest]); + SDD(blendOp, D3DRS_BLENDOP, GFXD3D9BlendOp[mDesc.blendOp]); + + // Separate alpha blending + SD(separateAlphaBlendEnable, D3DRS_SEPARATEALPHABLENDENABLE); + SDD(separateAlphaBlendSrc, D3DRS_SRCBLENDALPHA, GFXD3D9Blend[mDesc.separateAlphaBlendSrc]); + SDD(separateAlphaBlendDest, D3DRS_DESTBLENDALPHA, GFXD3D9Blend[mDesc.separateAlphaBlendDest]); + SDD(separateAlphaBlendOp, D3DRS_BLENDOPALPHA, GFXD3D9BlendOp[mDesc.separateAlphaBlendOp]); + + // Alpha test + SD(alphaTestEnable, D3DRS_ALPHATESTENABLE); + SDD(alphaTestFunc, D3DRS_ALPHAFUNC, GFXD3D9CmpFunc[mDesc.alphaTestFunc]); + SD(alphaTestRef, D3DRS_ALPHAREF); + + // Color writes + if ((oldState == NULL) || (mColorMask != oldState->mColorMask)) + mD3DDevice->SetRenderState(D3DRS_COLORWRITEENABLE, mColorMask); + + // Culling + SDD(cullMode, D3DRS_CULLMODE, GFXD3D9CullMode[mDesc.cullMode]); + + // Depth + SD(zEnable, D3DRS_ZENABLE); + SD(zWriteEnable, D3DRS_ZWRITEENABLE); + SDD(zFunc, D3DRS_ZFUNC, GFXD3D9CmpFunc[mDesc.zFunc]); + if ((!oldState) || (mZBias != oldState->mZBias)) + mD3DDevice->SetRenderState(D3DRS_DEPTHBIAS, mZBias); + if ((!oldState) || (mZSlopeBias != oldState->mZSlopeBias)) + mD3DDevice->SetRenderState(D3DRS_SLOPESCALEDEPTHBIAS, mZSlopeBias); + + // Stencil + SD(stencilEnable, D3DRS_STENCILENABLE); + SDD(stencilFailOp, D3DRS_STENCILFAIL, GFXD3D9StencilOp[mDesc.stencilFailOp]); + SDD(stencilZFailOp, D3DRS_STENCILZFAIL, GFXD3D9StencilOp[mDesc.stencilZFailOp]); + SDD(stencilPassOp, D3DRS_STENCILPASS, GFXD3D9StencilOp[mDesc.stencilPassOp]); + SDD(stencilFunc, D3DRS_STENCILFUNC, GFXD3D9CmpFunc[mDesc.stencilFunc]); + SD(stencilRef, D3DRS_STENCILREF); + SD(stencilMask, D3DRS_STENCILMASK); + SD(stencilWriteMask, D3DRS_STENCILWRITEMASK); + SDD(fillMode, D3DRS_FILLMODE, GFXD3D9FillMode[mDesc.fillMode]); +#if !defined(TORQUE_OS_XENON) + SD(ffLighting, D3DRS_LIGHTING); + SD(vertexColorEnable, D3DRS_COLORVERTEX); + + static DWORD swzTemp; + getOwningDevice()->getDeviceSwizzle32()->ToBuffer( &swzTemp, &mDesc.textureFactor, sizeof(ColorI) ); + SDD(textureFactor, D3DRS_TEXTUREFACTOR, swzTemp); +#endif +#undef SD +#undef SDD + + + // NOTE: Samplers and Stages are different things. + // + // The Stages were for fixed function blending. When using shaders + // calling SetTextureStageState() is a complete waste of time. In + // fact if this function rises to the top of profiles we should + // refactor stateblocks to seperate the two. + // + // Samplers are used by both fixed function and shaders, but the + // number of samplers is limited by shader model. +#if !defined(TORQUE_OS_XENON) + + #define TSS(x, y, z) if (!oldState || oldState->mDesc.samplers[i].x != mDesc.samplers[i].x) \ + mD3DDevice->SetTextureStageState(i, y, z) + for ( U32 i = 0; i < 8; i++ ) + { + TSS(textureColorOp, D3DTSS_COLOROP, GFXD3D9TextureOp[mDesc.samplers[i].textureColorOp]); + TSS(colorArg1, D3DTSS_COLORARG1, mDesc.samplers[i].colorArg1); + TSS(colorArg2, D3DTSS_COLORARG2, mDesc.samplers[i].colorArg2); + TSS(colorArg3, D3DTSS_COLORARG0, mDesc.samplers[i].colorArg3); + TSS(alphaOp, D3DTSS_ALPHAOP, GFXD3D9TextureOp[mDesc.samplers[i].alphaOp]); + TSS(alphaArg1, D3DTSS_ALPHAARG1, mDesc.samplers[i].alphaArg1); + TSS(alphaArg2, D3DTSS_ALPHAARG2, mDesc.samplers[i].alphaArg2); + TSS(alphaArg3, D3DTSS_ALPHAARG0, mDesc.samplers[i].alphaArg3); + TSS(textureTransform, D3DTSS_TEXTURETRANSFORMFLAGS, mDesc.samplers[i].textureTransform); + TSS(resultArg, D3DTSS_RESULTARG, mDesc.samplers[i].resultArg); + } + #undef TSS +#endif + +#if defined(TORQUE_OS_XENON) + #define SS(x, y, z) if (!oldState || oldState->mDesc.samplers[i].x != mDesc.samplers[i].x) \ + mD3DDevice->SetSamplerState_Inline(i, y, z) +#else + #define SS(x, y, z) if (!oldState || oldState->mDesc.samplers[i].x != mDesc.samplers[i].x) \ + mD3DDevice->SetSamplerState(i, y, z) +#endif + for ( U32 i = 0; i < getOwningDevice()->getNumSamplers(); i++ ) + { + SS(minFilter, D3DSAMP_MINFILTER, GFXD3D9TextureFilter[mDesc.samplers[i].minFilter]); + SS(magFilter, D3DSAMP_MAGFILTER, GFXD3D9TextureFilter[mDesc.samplers[i].magFilter]); + SS(mipFilter, D3DSAMP_MIPFILTER, GFXD3D9TextureFilter[mDesc.samplers[i].mipFilter]); + + F32 bias = mDesc.samplers[i].mipLODBias; + DWORD dwBias = *( (LPDWORD)(&bias) ); + SS(mipLODBias, D3DSAMP_MIPMAPLODBIAS, dwBias); + + SS(maxAnisotropy, D3DSAMP_MAXANISOTROPY, mDesc.samplers[i].maxAnisotropy); + + SS(addressModeU, D3DSAMP_ADDRESSU, GFXD3D9TextureAddress[mDesc.samplers[i].addressModeU]); + SS(addressModeV, D3DSAMP_ADDRESSV, GFXD3D9TextureAddress[mDesc.samplers[i].addressModeV]); + SS(addressModeW, D3DSAMP_ADDRESSW, GFXD3D9TextureAddress[mDesc.samplers[i].addressModeW]); + } + #undef SS +} diff --git a/Engine/source/gfx/D3D9/gfxD3D9StateBlock.h b/Engine/source/gfx/D3D9/gfxD3D9StateBlock.h new file mode 100644 index 000000000..ea9044f88 --- /dev/null +++ b/Engine/source/gfx/D3D9/gfxD3D9StateBlock.h @@ -0,0 +1,73 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _GFXD3D9STATEBLOCK_H_ +#define _GFXD3D9STATEBLOCK_H_ + +#ifndef _GFXSTATEBLOCK_H_ +#include "gfx/gfxStateBlock.h" +#endif + +struct IDirect3DDevice9; +class GFXD3D9StateBlock : public GFXStateBlock +{ +public: + // + // GFXD3D9StateBlock interface + // + + GFXD3D9StateBlock(const GFXStateBlockDesc& desc, IDirect3DDevice9 *d3dDevice); + virtual ~GFXD3D9StateBlock(); + + /// Called by D3D9 device to active this state block. + /// @param oldState The current state, used to make sure we don't set redundant states on the device. Pass NULL to reset all states. + void activate(GFXD3D9StateBlock* oldState); + + + // + // GFXStateBlock interface + // + + /// Returns the hash value of the desc that created this block + virtual U32 getHashValue() const; + + /// Returns a GFXStateBlockDesc that this block represents + virtual const GFXStateBlockDesc& getDesc() const; + + // + // GFXResource + // + virtual void zombify() { } + /// When called the resource should restore all device sensitive information destroyed by zombify() + virtual void resurrect() { } +private: + GFXStateBlockDesc mDesc; + U32 mCachedHashValue; + IDirect3DDevice9 *mD3DDevice; ///< Handle for D3DDevice + // Cached D3D specific things, these are "calculated" from GFXStateBlock + U32 mColorMask; + U32 mZBias; + U32 mZSlopeBias; +}; + +typedef StrongRefPtr GFXD3D9StateBlockRef; + +#endif \ No newline at end of file diff --git a/Engine/source/gfx/D3D9/gfxD3D9TextureManager.cpp b/Engine/source/gfx/D3D9/gfxD3D9TextureManager.cpp new file mode 100644 index 000000000..8eac8c03e --- /dev/null +++ b/Engine/source/gfx/D3D9/gfxD3D9TextureManager.cpp @@ -0,0 +1,638 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef _MSC_VER +#pragma warning(disable: 4996) +#endif + +#include "gfx/D3D9/gfxD3D9Device.h" +#include "gfx/D3D9/gfxD3D9EnumTranslate.h" +#include "gfx/bitmap/bitmapUtils.h" +#include "gfx/gfxCardProfile.h" +#include "core/strings/unicode.h" +#include "core/util/swizzle.h" +#include "core/util/safeDelete.h" +#include "console/console.h" +#include "core/resourceManager.h" + +//----------------------------------------------------------------------------- +// Utility function, valid only in this file +#ifdef D3D_TEXTURE_SPEW +U32 GFXD3D9TextureObject::mTexCount = 0; +#endif + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +GFXD3D9TextureManager::GFXD3D9TextureManager( LPDIRECT3DDEVICE9 d3ddevice ) +{ + mD3DDevice = d3ddevice; + dMemset( mCurTexSet, 0, sizeof( mCurTexSet ) ); + mD3DDevice->GetDeviceCaps(&mDeviceCaps); +} + +//----------------------------------------------------------------------------- +// Destructor +//----------------------------------------------------------------------------- +GFXD3D9TextureManager::~GFXD3D9TextureManager() +{ + // Destroy texture table now so just in case some texture objects + // are still left, we don't crash on a pure virtual method call. + SAFE_DELETE_ARRAY( mHashTable ); +} + +//----------------------------------------------------------------------------- +// _innerCreateTexture +//----------------------------------------------------------------------------- +void GFXD3D9TextureManager::_innerCreateTexture( GFXD3D9TextureObject *retTex, + U32 height, + U32 width, + U32 depth, + GFXFormat format, + GFXTextureProfile *profile, + U32 numMipLevels, + bool forceMips, + S32 antialiasLevel) +{ + GFXD3D9Device* d3d = static_cast(GFX); + + // Some relevant helper information... + bool supportsAutoMips = GFX->getCardProfiler()->queryProfile("autoMipMapLevel", true); + + DWORD usage = 0; // 0, D3DUSAGE_RENDERTARGET, or D3DUSAGE_DYNAMIC + D3DPOOL pool = D3DPOOL_DEFAULT; + + retTex->mProfile = profile; + + D3DFORMAT d3dTextureFormat = GFXD3D9TextureFormat[format]; + +#ifndef TORQUE_OS_XENON + if( retTex->mProfile->isDynamic() ) + { + usage = D3DUSAGE_DYNAMIC; + } + else + { + usage = 0; + pool = d3d->isD3D9Ex() ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED; + } + + if( retTex->mProfile->isRenderTarget() ) + { + pool = D3DPOOL_DEFAULT; + usage |= D3DUSAGE_RENDERTARGET; + } + + if(retTex->mProfile->isZTarget()) + { + usage |= D3DUSAGE_DEPTHSTENCIL; + pool = D3DPOOL_DEFAULT; + } + + if( retTex->mProfile->isSystemMemory() ) + { + pool = D3DPOOL_SYSTEMMEM; + } + + if( supportsAutoMips && + !forceMips && + !retTex->mProfile->isSystemMemory() && + numMipLevels == 0 && + !(depth > 0) ) + { + usage |= D3DUSAGE_AUTOGENMIPMAP; + } +#else + if(retTex->mProfile->isRenderTarget()) + { + d3dTextureFormat = (D3DFORMAT)MAKELEFMT(d3dTextureFormat); + } +#endif + + // Set the managed flag... + retTex->isManaged = (pool == D3DPOOL_MANAGED) || d3d->isD3D9Ex(); + + if( depth > 0 ) + { +#ifdef TORQUE_OS_XENON + D3D9Assert( mD3DDevice->CreateVolumeTexture( width, height, depth, numMipLevels, 0 /* usage ignored on the 360 */, + d3dTextureFormat, pool, retTex->get3DTexPtr(), NULL), "Failed to create volume texture" ); +#else + D3D9Assert( + GFXD3DX.D3DXCreateVolumeTexture( + mD3DDevice, + width, + height, + depth, + numMipLevels, + usage, + d3dTextureFormat, + pool, + retTex->get3DTexPtr() + ), "GFXD3D9TextureManager::_createTexture - failed to create volume texture!" + ); +#endif + + retTex->mTextureSize.set( width, height, depth ); + retTex->mMipLevels = retTex->get3DTex()->GetLevelCount(); + // required for 3D texture support - John Kabus + retTex->mFormat = format; + } + else + { +#ifdef TORQUE_OS_XENON + D3D9Assert( mD3DDevice->CreateTexture(width, height, numMipLevels, usage, d3dTextureFormat, pool, retTex->get2DTexPtr(), NULL), "Failed to create texture" ); + retTex->mMipLevels = retTex->get2DTex()->GetLevelCount(); +#else + // Figure out AA settings for depth and render targets + D3DMULTISAMPLE_TYPE mstype; + DWORD mslevel; + + switch (antialiasLevel) + { + case 0 : + mstype = D3DMULTISAMPLE_NONE; + mslevel = 0; + break; + case AA_MATCH_BACKBUFFER : + mstype = d3d->getMultisampleType(); + mslevel = d3d->getMultisampleLevel(); + break; + default : + { + mstype = D3DMULTISAMPLE_NONMASKABLE; + mslevel = antialiasLevel; +#ifdef TORQUE_DEBUG + DWORD MaxSampleQualities; + d3d->getD3D()->CheckDeviceMultiSampleType(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, d3dTextureFormat, FALSE, D3DMULTISAMPLE_NONMASKABLE, &MaxSampleQualities); + AssertFatal(mslevel < MaxSampleQualities, "Invalid AA level!"); +#endif + } + break; + } + + bool fastCreate = true; + // Check for power of 2 textures - this is a problem with FX 5xxx cards + // with current drivers - 3/2/05 + if( !isPow2(width) || !isPow2(height) ) + { + fastCreate = false; + } + + if(retTex->mProfile->isZTarget()) + { + D3D9Assert(mD3DDevice->CreateDepthStencilSurface(width, height, d3dTextureFormat, + mstype, mslevel, retTex->mProfile->canDiscard(), retTex->getSurfacePtr(), NULL), "Failed to create Z surface" ); + + retTex->mFormat = format; // Assigning format like this should be fine. + } + else + { + // Try to create the texture directly - should gain us a bit in high + // performance cases where we know we're creating good stuff and we + // don't want to bother with D3DX - slow function. + HRESULT res = D3DERR_INVALIDCALL; + if( fastCreate ) + { + res = mD3DDevice->CreateTexture(width, height, numMipLevels, usage, d3dTextureFormat, pool, retTex->get2DTexPtr(), NULL); + } + + if( !fastCreate || (res != D3D_OK) ) + { + D3D9Assert( + GFXD3DX.D3DXCreateTexture( + mD3DDevice, + width, + height, + numMipLevels, + usage, + d3dTextureFormat, + pool, + retTex->get2DTexPtr() + ), "GFXD3D9TextureManager::_createTexture - failed to create texture!" + ); + } + + // If this is a render target, and it wants AA or wants to match the backbuffer (for example, to share the z) + // Check the caps though, if we can't stretchrect between textures, use the old RT method. (Which hopefully means + // that they can't force AA on us as well.) + if (retTex->mProfile->isRenderTarget() && mslevel != 0 && (mDeviceCaps.Caps2 && D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES)) + { + D3D9Assert(mD3DDevice->CreateRenderTarget(width, height, d3dTextureFormat, + mstype, mslevel, false, retTex->getSurfacePtr(), NULL), + "GFXD3D9TextureManager::_createTexture - unable to create render target"); + } + + // All done! + retTex->mMipLevels = retTex->get2DTex()->GetLevelCount(); + } +#endif + + // Get the actual size of the texture... + D3DSURFACE_DESC probeDesc; + ZeroMemory(&probeDesc, sizeof probeDesc); + + if( retTex->get2DTex() != NULL ) + D3D9Assert( retTex->get2DTex()->GetLevelDesc( 0, &probeDesc ), "Failed to get surface description"); + else if( retTex->getSurface() != NULL ) + D3D9Assert( retTex->getSurface()->GetDesc( &probeDesc ), "Failed to get surface description"); + + retTex->mTextureSize.set(probeDesc.Width, probeDesc.Height, 0); + + int fmt = probeDesc.Format; + +#if !defined(TORQUE_OS_XENON) + GFXREVERSE_LOOKUP( GFXD3D9TextureFormat, GFXFormat, fmt ); + retTex->mFormat = (GFXFormat)fmt; +#else + retTex->mFormat = format; +#endif + } +} + +//----------------------------------------------------------------------------- +// createTexture +//----------------------------------------------------------------------------- +GFXTextureObject *GFXD3D9TextureManager::_createTextureObject( U32 height, + U32 width, + U32 depth, + GFXFormat format, + GFXTextureProfile *profile, + U32 numMipLevels, + bool forceMips, + S32 antialiasLevel, + GFXTextureObject *inTex ) +{ + GFXD3D9TextureObject *retTex; + if ( inTex ) + { + AssertFatal( dynamic_cast( inTex ), "GFXD3D9TextureManager::_createTexture() - Bad inTex type!" ); + retTex = static_cast( inTex ); + retTex->release(); + } + else + { + retTex = new GFXD3D9TextureObject( GFX, profile ); + retTex->registerResourceWithDevice( GFX ); + } + + _innerCreateTexture(retTex, height, width, depth, format, profile, numMipLevels, forceMips, antialiasLevel); + + return retTex; +} + +//----------------------------------------------------------------------------- +// loadTexture - GBitmap +//----------------------------------------------------------------------------- +bool GFXD3D9TextureManager::_loadTexture(GFXTextureObject *aTexture, GBitmap *pDL) +{ + PROFILE_SCOPE(GFXD3D9TextureManager_loadTexture); + + GFXD3D9TextureObject *texture = static_cast(aTexture); + +#ifdef TORQUE_OS_XENON + // If the texture is currently bound, it needs to be unbound before modifying it + if( texture->getTex() && texture->getTex()->IsSet( mD3DDevice ) ) + { + mD3DDevice->SetTexture( 0, NULL ); + mD3DDevice->SetTexture( 1, NULL ); + mD3DDevice->SetTexture( 2, NULL ); + mD3DDevice->SetTexture( 3, NULL ); + mD3DDevice->SetTexture( 4, NULL ); + mD3DDevice->SetTexture( 5, NULL ); + mD3DDevice->SetTexture( 6, NULL ); + mD3DDevice->SetTexture( 7, NULL ); + } +#endif + + // Check with profiler to see if we can do automatic mipmap generation. + const bool supportsAutoMips = GFX->getCardProfiler()->queryProfile("autoMipMapLevel", true); + + // Helper bool + const bool isCompressedTexFmt = aTexture->mFormat >= GFXFormatDXT1 && aTexture->mFormat <= GFXFormatDXT5; + + GFXD3D9Device* dev = static_cast(GFX); + + // Settings for mipmap generation + U32 maxDownloadMip = pDL->getNumMipLevels(); + U32 nbMipMapLevel = pDL->getNumMipLevels(); + + if( supportsAutoMips && !isCompressedTexFmt ) + { + maxDownloadMip = 1; + nbMipMapLevel = aTexture->mMipLevels; + } + + // Fill the texture... + for( int i = 0; i < maxDownloadMip; i++ ) + { + LPDIRECT3DSURFACE9 surf = NULL; + D3D9Assert(texture->get2DTex()->GetSurfaceLevel( i, &surf ), "Failed to get surface"); + + D3DLOCKED_RECT lockedRect; + +#ifdef TORQUE_OS_XENON + // On the 360, doing a LockRect doesn't work like it does with untiled memory + // so instead swizzle into some temporary memory, and then later use D3DX + // to do the upload properly. + FrameTemp swizzleMem(pDL->getWidth(i) * pDL->getHeight(i) * pDL->getBytesPerPixel()); + lockedRect.pBits = (void*)~swizzleMem; +#else + U32 waterMark = 0; + if (!dev->isD3D9Ex()) + surf->LockRect( &lockedRect, NULL, 0 ); + else + { + waterMark = FrameAllocator::getWaterMark(); + lockedRect.pBits = static_cast(FrameAllocator::alloc(pDL->getWidth(i) * pDL->getHeight(i) * pDL->getBytesPerPixel())); + } +#endif + + switch( texture->mFormat ) + { + case GFXFormatR8G8B8: + { + PROFILE_SCOPE(Swizzle24_Upload); + AssertFatal( pDL->getFormat() == GFXFormatR8G8B8, "Assumption failed" ); + GFX->getDeviceSwizzle24()->ToBuffer( lockedRect.pBits, pDL->getBits(i), + pDL->getWidth(i) * pDL->getHeight(i) * pDL->getBytesPerPixel() ); + } + break; + + case GFXFormatR8G8B8A8: + case GFXFormatR8G8B8X8: + { + PROFILE_SCOPE(Swizzle32_Upload); + GFX->getDeviceSwizzle32()->ToBuffer( lockedRect.pBits, pDL->getBits(i), + pDL->getWidth(i) * pDL->getHeight(i) * pDL->getBytesPerPixel() ); + } + break; + + default: + { + // Just copy the bits in no swizzle or padding + PROFILE_SCOPE(SwizzleNull_Upload); + AssertFatal( pDL->getFormat() == texture->mFormat, "Format mismatch" ); + dMemcpy( lockedRect.pBits, pDL->getBits(i), + pDL->getWidth(i) * pDL->getHeight(i) * pDL->getBytesPerPixel() ); + } + } + +#ifdef TORQUE_OS_XENON + RECT srcRect; + srcRect.bottom = pDL->getHeight(i); + srcRect.top = 0; + srcRect.left = 0; + srcRect.right = pDL->getWidth(i); + + D3DXLoadSurfaceFromMemory(surf, NULL, NULL, ~swizzleMem, (D3DFORMAT)MAKELINFMT(GFXD3D9TextureFormat[pDL->getFormat()]), + pDL->getWidth(i) * pDL->getBytesPerPixel(), NULL, &srcRect, false, 0, 0, D3DX_FILTER_NONE, 0); +#else + if (!dev->isD3D9Ex()) + surf->UnlockRect(); + else + { + RECT srcRect; + srcRect.top = 0; + srcRect.left = 0; + srcRect.right = pDL->getWidth(i); + srcRect.bottom = pDL->getHeight(i); + D3DXLoadSurfaceFromMemory(surf, NULL, NULL, lockedRect.pBits, GFXD3D9TextureFormat[pDL->getFormat()], pDL->getBytesPerPixel() * pDL->getWidth(i), NULL, &srcRect, D3DX_DEFAULT, 0); + FrameAllocator::setWaterMark(waterMark); + } +#endif + + surf->Release(); + } + + return true; +} + +//----------------------------------------------------------------------------- +// loadTexture - raw +//----------------------------------------------------------------------------- +bool GFXD3D9TextureManager::_loadTexture( GFXTextureObject *inTex, void *raw ) +{ + PROFILE_SCOPE(GFXD3D9TextureManager_loadTextureRaw); + + GFXD3D9TextureObject *texture = (GFXD3D9TextureObject *) inTex; + + // currently only for volume textures... + if( texture->getDepth() < 1 ) return false; + + + U32 bytesPerPix = 1; + + switch( texture->mFormat ) + { + case GFXFormatR8G8B8: + bytesPerPix = 3; + break; + case GFXFormatR8G8B8A8: + case GFXFormatR8G8B8X8: + bytesPerPix = 4; + break; + } + + U32 rowPitch = texture->getWidth() * bytesPerPix; + U32 slicePitch = texture->getWidth() * texture->getHeight() * bytesPerPix; + + D3DBOX box; + box.Left = 0; + box.Right = texture->getWidth(); + box.Front = 0; + box.Back = texture->getDepth(); + box.Top = 0; + box.Bottom = texture->getHeight(); + + + LPDIRECT3DVOLUME9 volume = NULL; + D3D9Assert( texture->get3DTex()->GetVolumeLevel( 0, &volume ), "Failed to load volume" ); + +#ifdef TORQUE_OS_XENON + D3DLOCKED_BOX lockedBox; + volume->LockBox( &lockedBox, &box, 0 ); + + dMemcpy( lockedBox.pBits, raw, slicePitch * texture->getDepth() ); + + volume->UnlockBox(); +#else + D3D9Assert( + GFXD3DX.D3DXLoadVolumeFromMemory( + volume, + NULL, + NULL, + raw, + GFXD3D9TextureFormat[texture->mFormat], + rowPitch, + slicePitch, + NULL, + &box, +#ifdef TORQUE_OS_XENON + false, 0, 0, 0, // Unique to Xenon -pw +#endif + D3DX_FILTER_NONE, + 0 + ), + "Failed to load volume texture" + ); +#endif + + volume->Release(); + + + return true; +} + +//----------------------------------------------------------------------------- +// refreshTexture +//----------------------------------------------------------------------------- +bool GFXD3D9TextureManager::_refreshTexture(GFXTextureObject *texture) +{ + U32 usedStrategies = 0; + GFXD3D9TextureObject *realTex = static_cast( texture ); + + if(texture->mProfile->doStoreBitmap()) + { +// SAFE_RELEASE(realTex->mD3DTexture); +// _innerCreateTexture(realTex, texture->mTextureSize.x, texture->mTextureSize.y, texture->mFormat, texture->mProfile, texture->mMipLevels); + + if(texture->mBitmap) + _loadTexture(texture, texture->mBitmap); + + if(texture->mDDS) + _loadTexture(texture, texture->mDDS); + + usedStrategies++; + } + + if(texture->mProfile->isRenderTarget() || texture->mProfile->isDynamic() || + texture->mProfile->isZTarget()) + { + realTex->release(); + _innerCreateTexture(realTex, texture->getHeight(), texture->getWidth(), texture->getDepth(), texture->mFormat, + + texture->mProfile, texture->mMipLevels, false, texture->mAntialiasLevel); + usedStrategies++; + } + + AssertFatal(usedStrategies < 2, "GFXD3D9TextureManager::_refreshTexture - Inconsistent profile flags!"); + + return true; +} + + +//----------------------------------------------------------------------------- +// freeTexture +//----------------------------------------------------------------------------- +bool GFXD3D9TextureManager::_freeTexture(GFXTextureObject *texture, bool zombify) +{ + AssertFatal(dynamic_cast(texture),"Not an actual d3d texture object!"); + GFXD3D9TextureObject *tex = static_cast( texture ); + + // If it's a managed texture and we're zombifying, don't blast it, D3D allows + // us to keep it. + if(zombify && tex->isManaged) + return true; + + tex->release(); + + return true; +} + +/// Load a texture from a proper DDSFile instance. +bool GFXD3D9TextureManager::_loadTexture(GFXTextureObject *aTexture, DDSFile *dds) +{ + PROFILE_SCOPE(GFXD3D9TextureManager_loadTextureDDS); + + GFXD3D9TextureObject *texture = static_cast(aTexture); + + // Fill the texture... + for( int i = 0; i < aTexture->mMipLevels; i++ ) + { + PROFILE_SCOPE(GFXD3DTexMan_loadSurface); + + LPDIRECT3DSURFACE9 surf = NULL; + D3D9Assert(texture->get2DTex()->GetSurfaceLevel( i, &surf ), "Failed to get surface"); + +#if defined(TORQUE_OS_XENON) + XGTEXTURE_DESC surfDesc; + dMemset(&surfDesc, 0, sizeof(XGTEXTURE_DESC)); + XGGetSurfaceDesc(surf, &surfDesc); + + RECT srcRect; + srcRect.top = srcRect.left = 0; + srcRect.bottom = dds->getHeight(i); + srcRect.right = dds->getWidth(i); + + D3DXLoadSurfaceFromMemory(surf, NULL, NULL, dds->mSurfaces[0]->mMips[i], + (D3DFORMAT)MAKELINFMT(GFXD3D9TextureFormat[dds->mFormat]), dds->getSurfacePitch(i), + NULL, &srcRect, false, 0, 0, D3DX_FILTER_NONE, 0); +#else + + GFXD3D9Device* dev = static_cast(GFX); + + if (dev->isD3D9Ex()) + { + RECT r; + r.top = r.left = 0; + r.bottom = dds->getHeight(i); + r.right = dds->getWidth(i); + D3DXLoadSurfaceFromMemory(surf, NULL, NULL, dds->mSurfaces[0]->mMips[i], GFXD3D9TextureFormat[dds->mFormat], dds->getSurfacePitch(i), NULL, &r, D3DX_DEFAULT, 0); + } + else + { + D3DLOCKED_RECT lockedRect; + D3D9Assert( surf->LockRect( &lockedRect, NULL, 0 ), "Failed to lock surface level for load" ); + + AssertFatal( dds->mSurfaces.size() > 0, "Assumption failed. DDSFile has no surfaces." ); + + if ( dds->getSurfacePitch( i ) != lockedRect.Pitch ) + { + // Do a row-by-row copy. + U32 srcPitch = dds->getSurfacePitch( i ); + U32 srcHeight = dds->getHeight(); + U8* srcBytes = dds->mSurfaces[0]->mMips[i]; + U8* dstBytes = (U8*)lockedRect.pBits; + for (U32 i = 0; iUnlockRect(); + surf->Release(); + + return true; + } + + dMemcpy( lockedRect.pBits, dds->mSurfaces[0]->mMips[i], dds->getSurfaceSize(i) ); + + surf->UnlockRect(); + } +#endif + + surf->Release(); + } + + return true; +} \ No newline at end of file diff --git a/Engine/source/gfx/D3D9/gfxD3D9TextureManager.h b/Engine/source/gfx/D3D9/gfxD3D9TextureManager.h new file mode 100644 index 000000000..ab7dcacbd --- /dev/null +++ b/Engine/source/gfx/D3D9/gfxD3D9TextureManager.h @@ -0,0 +1,72 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXD3DTEXTUREMANAGER_H_ +#define _GFXD3DTEXTUREMANAGER_H_ + +#include "gfx/D3D9/gfxD3D9TextureObject.h" +#include "core/util/safeRelease.h" + +// #define D3D_TEXTURE_SPEW + + +//***************************************************************************** +// GFX D3D Texture Manager +//***************************************************************************** +class GFXD3D9TextureManager : public GFXTextureManager +{ + friend class GFXD3D9TextureObject; + +public: + GFXD3D9TextureManager( LPDIRECT3DDEVICE9 d3ddevice ); + virtual ~GFXD3D9TextureManager(); + +protected: + + // GFXTextureManager + GFXTextureObject *_createTextureObject( U32 height, + U32 width, + U32 depth, + GFXFormat format, + GFXTextureProfile *profile, + U32 numMipLevels, + bool forceMips = false, + S32 antialiasLevel = 0, + GFXTextureObject *inTex = NULL ); + bool _loadTexture(GFXTextureObject *texture, DDSFile *dds); + bool _loadTexture(GFXTextureObject *texture, GBitmap *bmp); + bool _loadTexture(GFXTextureObject *texture, void *raw); + bool _refreshTexture(GFXTextureObject *texture); + bool _freeTexture(GFXTextureObject *texture, bool zombify = false); + +private: + U32 mCurTexSet[TEXTURE_STAGE_COUNT]; + + LPDIRECT3DDEVICE9 mD3DDevice; + D3DCAPS9 mDeviceCaps; + + void _innerCreateTexture(GFXD3D9TextureObject *obj, U32 height, U32 width, + U32 depth, GFXFormat format, GFXTextureProfile *profile, U32 numMipLevels, + bool forceMips = false, S32 antialiasLevel = 0); +}; + +#endif diff --git a/Engine/source/gfx/D3D9/gfxD3D9TextureObject.cpp b/Engine/source/gfx/D3D9/gfxD3D9TextureObject.cpp new file mode 100644 index 000000000..be859b857 --- /dev/null +++ b/Engine/source/gfx/D3D9/gfxD3D9TextureObject.cpp @@ -0,0 +1,277 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/D3D9/gfxD3D9Device.h" +#include "gfx/D3D9/gfxD3D9TextureObject.h" +#include "platform/profiler.h" + +#ifdef TORQUE_OS_XENON +#include "gfx/D3D9/360/gfx360Device.h" +#include "gfx/D3D9/360/gfx360Target.h" +#include "gfx/D3D9/gfxD3D9EnumTranslate.h" +#endif + +U32 GFXD3D9TextureObject::mTexCount = 0; + +//***************************************************************************** +// GFX D3D Texture Object +//***************************************************************************** +GFXD3D9TextureObject::GFXD3D9TextureObject( GFXDevice * d, GFXTextureProfile *profile) + : GFXTextureObject( d, profile ) +{ +#ifdef D3D_TEXTURE_SPEW + mTexCount++; + Con::printf("+ texMake %d %x", mTexCount, this); +#endif + + isManaged = false; + mD3DTexture = NULL; + mLocked = false; + + mD3DSurface = NULL; +} + +//----------------------------------------------------------------------------- +// Destructor +//----------------------------------------------------------------------------- +GFXD3D9TextureObject::~GFXD3D9TextureObject() +{ + kill(); +#ifdef D3D_TEXTURE_SPEW + mTexCount--; + Con::printf("+ texkill %d %x", mTexCount, this); +#endif +} + +//----------------------------------------------------------------------------- +// lock +//----------------------------------------------------------------------------- +GFXLockedRect *GFXD3D9TextureObject::lock(U32 mipLevel /*= 0*/, RectI *inRect /*= NULL*/) +{ + AssertFatal( !mLocked, "GFXD3D9TextureObject::lock - The texture is already locked!" ); + + if( mProfile->isRenderTarget() ) + { + if( !mLockTex || + mLockTex->getWidth() != getWidth() || + mLockTex->getHeight() != getHeight() ) + { + mLockTex.set( getWidth(), getHeight(), mFormat, &GFXSystemMemProfile, avar("%s() - mLockTex (line %d)", __FUNCTION__, __LINE__) ); + } + + PROFILE_START(GFXD3D9TextureObject_lockRT); + + IDirect3DSurface9 *source; + D3D9Assert( get2DTex()->GetSurfaceLevel( 0, &source ), "GFXD3D9TextureObject::lock - failed to get our own texture's surface." ); + + IDirect3DSurface9 *dest; + GFXD3D9TextureObject *to = (GFXD3D9TextureObject *) &(*mLockTex); + D3D9Assert( to->get2DTex()->GetSurfaceLevel( 0, &dest ), "GFXD3D9TextureObject::lock - failed to get dest texture's surface." ); + +#ifndef TORQUE_OS_XENON + LPDIRECT3DDEVICE9 D3DDevice = dynamic_cast(GFX)->getDevice(); + HRESULT rtLockRes = D3DDevice->GetRenderTargetData( source, dest ); +#else + AssertFatal(false, "Use different functionality on the Xbox 360 to perform this task."); + HRESULT rtLockRes = E_FAIL; +#endif + source->Release(); + + if(!SUCCEEDED(rtLockRes)) + { + // This case generally occurs if the device is lost. The lock failed + // so clean up and return NULL. + dest->Release(); + PROFILE_END(); + return NULL; + } + + D3D9Assert( dest->LockRect( &mLockRect, NULL, D3DLOCK_READONLY ), NULL ); + dest->Release(); + mLocked = true; + + PROFILE_END(); + } + else + { + RECT r; + + if(inRect) + { + r.top = inRect->point.y; + r.left = inRect->point.x; + r.bottom = inRect->point.y + inRect->extent.y; + r.right = inRect->point.x + inRect->extent.x; + } + + D3D9Assert( get2DTex()->LockRect(mipLevel, &mLockRect, inRect ? &r : NULL, 0), + "GFXD3D9TextureObject::lock - could not lock non-RT texture!" ); + mLocked = true; + + } + + // GFXLockedRect is set up to correspond to D3DLOCKED_RECT, so this is ok. + return (GFXLockedRect*)&mLockRect; +} + +//----------------------------------------------------------------------------- +// unLock +//----------------------------------------------------------------------------- +void GFXD3D9TextureObject::unlock(U32 mipLevel) +{ + AssertFatal( mLocked, "GFXD3D9TextureObject::unlock - Attempting to unlock a surface that has not been locked" ); + +#ifndef TORQUE_OS_XENON + if( mProfile->isRenderTarget() ) + { + IDirect3DSurface9 *dest; + GFXD3D9TextureObject *to = (GFXD3D9TextureObject *) &(*mLockTex); + D3D9Assert( to->get2DTex()->GetSurfaceLevel( 0, &dest ), NULL ); + + dest->UnlockRect(); + dest->Release(); + + mLocked = false; + } + else +#endif + { + D3D9Assert( get2DTex()->UnlockRect(mipLevel), + "GFXD3D9TextureObject::unlock - could not unlock non-RT texture." ); + + mLocked = false; + } +} + +//------------------------------------------------------------------------------ + +void GFXD3D9TextureObject::release() +{ + static_cast( GFX )->destroyD3DResource( mD3DTexture ); + static_cast( GFX )->destroyD3DResource( mD3DSurface ); + mD3DTexture = NULL; + mD3DSurface = NULL; +} + +void GFXD3D9TextureObject::zombify() +{ + // Managed textures are managed by D3D + AssertFatal(!mLocked, "GFXD3D9TextureObject::zombify - Cannot zombify a locked texture!"); + if(isManaged) + return; + + release(); +} + +void GFXD3D9TextureObject::resurrect() +{ + // Managed textures are managed by D3D + if(isManaged) + return; + + static_cast(TEXMGR)->refreshTexture(this); +} + +//------------------------------------------------------------------------------ + +bool GFXD3D9TextureObject::copyToBmp(GBitmap* bmp) +{ +#ifdef TORQUE_OS_XENON + // TODO: Implement Xenon version -patw + return false; +#else + if (!bmp) + return false; + + // check format limitations + // at the moment we only support RGBA for the source (other 4 byte formats should + // be easy to add though) + AssertFatal(mFormat == GFXFormatR8G8B8A8, "copyToBmp: invalid format"); + if (mFormat != GFXFormatR8G8B8A8) + return false; + + PROFILE_START(GFXD3D9TextureObject_copyToBmp); + + AssertFatal(bmp->getWidth() == getWidth(), "doh"); + AssertFatal(bmp->getHeight() == getHeight(), "doh"); + U32 width = getWidth(); + U32 height = getHeight(); + + bmp->setHasTransparency(mHasTransparency); + + // set some constants + const U32 sourceBytesPerPixel = 4; + U32 destBytesPerPixel = 0; + if (bmp->getFormat() == GFXFormatR8G8B8A8) + destBytesPerPixel = 4; + else if (bmp->getFormat() == GFXFormatR8G8B8) + destBytesPerPixel = 3; + else + // unsupported + AssertFatal(false, "unsupported bitmap format"); + + // lock the texture + D3DLOCKED_RECT* lockRect = (D3DLOCKED_RECT*) lock(); + + // set pointers + U8* srcPtr = (U8*)lockRect->pBits; + U8* destPtr = bmp->getWritableBits(); + + // we will want to skip over any D3D cache data in the source texture + const S32 sourceCacheSize = lockRect->Pitch - width * sourceBytesPerPixel; + AssertFatal(sourceCacheSize >= 0, "copyToBmp: cache size is less than zero?"); + + PROFILE_START(GFXD3D9TextureObject_copyToBmp_pixCopy); + // copy data into bitmap + for (int row = 0; row < height; ++row) + { + for (int col = 0; col < width; ++col) + { + destPtr[0] = srcPtr[2]; // red + destPtr[1] = srcPtr[1]; // green + destPtr[2] = srcPtr[0]; // blue + if (destBytesPerPixel == 4) + destPtr[3] = srcPtr[3]; // alpha + + // go to next pixel in src + srcPtr += sourceBytesPerPixel; + + // go to next pixel in dest + destPtr += destBytesPerPixel; + } + // skip past the cache data for this row (if any) + srcPtr += sourceCacheSize; + } + PROFILE_END(); + + // assert if we stomped or underran memory + AssertFatal(U32(destPtr - bmp->getWritableBits()) == width * height * destBytesPerPixel, "copyToBmp: doh, memory error"); + AssertFatal(U32(srcPtr - (U8*)lockRect->pBits) == height * lockRect->Pitch, "copyToBmp: doh, memory error"); + + // unlock + unlock(); + + PROFILE_END(); + + return true; +#endif +} diff --git a/Engine/source/gfx/D3D9/gfxD3D9TextureObject.h b/Engine/source/gfx/D3D9/gfxD3D9TextureObject.h new file mode 100644 index 000000000..34005194d --- /dev/null +++ b/Engine/source/gfx/D3D9/gfxD3D9TextureObject.h @@ -0,0 +1,80 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXD3D9TEXTUREOBJECT_H_ +#define _GFXD3D9TEXTUREOBJECT_H_ + +#ifndef _GFXTEXTUREHANDLE_H_ +#include "gfx/gfxTextureHandle.h" +#endif + +#ifndef _GFXTEXTUREMANAGER_H_ +#include "gfx/gfxTextureManager.h" +#endif + + +class GFXD3D9TextureObject : public GFXTextureObject +{ +protected: + static U32 mTexCount; + GFXTexHandle mLockTex; + D3DLOCKED_RECT mLockRect; + bool mLocked; + + IDirect3DBaseTexture9 *mD3DTexture; + + // used for z buffers... + IDirect3DSurface9 *mD3DSurface; + +public: + + GFXD3D9TextureObject( GFXDevice * d, GFXTextureProfile *profile); + ~GFXD3D9TextureObject(); + + IDirect3DBaseTexture9 * getTex(){ return mD3DTexture; } + IDirect3DTexture9 * get2DTex(){ return (LPDIRECT3DTEXTURE9) mD3DTexture; } + IDirect3DTexture9 ** get2DTexPtr(){ return (LPDIRECT3DTEXTURE9*) &mD3DTexture; } + IDirect3DVolumeTexture9 * get3DTex(){ return (LPDIRECT3DVOLUMETEXTURE9) mD3DTexture; } + IDirect3DVolumeTexture9 ** get3DTexPtr(){ return (LPDIRECT3DVOLUMETEXTURE9*) &mD3DTexture; } + + void release(); + + bool isManaged; + + virtual GFXLockedRect * lock(U32 mipLevel = 0, RectI *inRect = NULL); + virtual void unlock(U32 mipLevel = 0 ); + + virtual bool copyToBmp(GBitmap* bmp); + IDirect3DSurface9 *getSurface() {return mD3DSurface;} + IDirect3DSurface9 **getSurfacePtr() {return &mD3DSurface;} + + // GFXResource + void zombify(); + void resurrect(); + +#ifdef TORQUE_DEBUG + virtual void pureVirtualCrash() {}; +#endif +}; + + +#endif diff --git a/Engine/source/gfx/D3D9/gfxD3D9VertexBuffer.cpp b/Engine/source/gfx/D3D9/gfxD3D9VertexBuffer.cpp new file mode 100644 index 000000000..d31e93870 --- /dev/null +++ b/Engine/source/gfx/D3D9/gfxD3D9VertexBuffer.cpp @@ -0,0 +1,230 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/D3D9/gfxD3D9VertexBuffer.h" + +GFXD3D9VertexBuffer::~GFXD3D9VertexBuffer() +{ +#ifdef TORQUE_DEBUG + SAFE_DELETE( name ); +#endif + + if (getOwningDevice() != NULL) + { + if (mBufferType == GFXBufferTypeDynamic) + static_cast(getOwningDevice())->deallocVertexBuffer( this ); + else if (mBufferType != GFXBufferTypeVolatile) + { + static_cast(getOwningDevice())->destroyD3DResource( vb ); + } + } +} + +//----------------------------------------------------------------------------- +// Lock +//----------------------------------------------------------------------------- +void GFXD3D9VertexBuffer::lock(U32 vertexStart, U32 vertexEnd, void **vertexPtr) +{ + PROFILE_SCOPE(GFXD3D9VertexBuffer_lock); + + AssertFatal(lockedVertexStart == 0 && lockedVertexEnd == 0, "Cannot lock a buffer more than once!"); + + U32 flags = 0; + + GFXD3D9Device *d = static_cast( mDevice ); + + switch( mBufferType ) + { + case GFXBufferTypeStatic: + break; + + case GFXBufferTypeDynamic: +#ifndef TORQUE_OS_XENON + flags |= D3DLOCK_DISCARD; +#endif + break; + + case GFXBufferTypeVolatile: + + // Get or create the volatile buffer... + mVolatileBuffer = d->findVBPool( &mVertexFormat, vertexEnd ); + + if( !mVolatileBuffer ) + mVolatileBuffer = d->createVBPool( &mVertexFormat, mVertexSize ); + + vb = mVolatileBuffer->vb; + + // Get our range now... + AssertFatal(vertexStart == 0, "Cannot get a subrange on a volatile buffer."); + AssertFatal(vertexEnd <= MAX_DYNAMIC_VERTS, "Cannot get more than MAX_DYNAMIC_VERTS in a volatile buffer. Up the constant!"); + AssertFatal(mVolatileBuffer->lockedVertexStart == 0 && mVolatileBuffer->lockedVertexEnd == 0, "Got more than one lock on the volatile pool."); + + // We created the pool when we requested this volatile buffer, so assume it exists... + if( mVolatileBuffer->mNumVerts + vertexEnd > MAX_DYNAMIC_VERTS ) + { +#ifdef TORQUE_OS_XENON + AssertFatal( false, "This should never, ever happen. findVBPool should have returned NULL" ); +#else + flags |= D3DLOCK_DISCARD; +#endif + mVolatileStart = vertexStart = 0; + vertexEnd = vertexEnd; + } + else + { + flags |= D3DLOCK_NOOVERWRITE; + mVolatileStart = vertexStart = mVolatileBuffer->mNumVerts; + vertexEnd += mVolatileBuffer->mNumVerts; + } + + mVolatileBuffer->mNumVerts = vertexEnd+1; + + mVolatileBuffer->lockedVertexStart = vertexStart; + mVolatileBuffer->lockedVertexEnd = vertexEnd; + break; + } + + lockedVertexStart = vertexStart; + lockedVertexEnd = vertexEnd; + + // Con::printf("%x: Locking %s range (%d, %d)", this, (mBufferType == GFXBufferTypeVolatile ? "volatile" : "static"), lockedVertexStart, lockedVertexEnd); + +#ifdef TORQUE_OS_XENON + // If the vertex buffer which we are trying to lock is held by the D3D device + // on Xenon it will bomb. So if that is the case, then SetStreamSource to NULL + // and also call setVertexBuffer because otherwise the state-cache will be hosed + if( d->mCurrentVB != NULL && d->mCurrentVB->vb == vb ) + { + d->setVertexBuffer( NULL ); + d->mD3DDevice->SetStreamSource( 0, NULL, 0, 0 ); + } + + // As of October 2006 XDK, range locking is no longer supported. Lock the whole buffer + // and then manually offset the pointer to simulate the subrange. -patw + D3D9Assert( vb->Lock( 0, 0, vertexPtr, flags), + "Unable to lock vertex buffer."); + + U8 *tmp = (U8 *)(*vertexPtr); + tmp += ( vertexStart * mVertexSize ); + *vertexPtr = tmp; +#else + + U32 sizeToLock = (vertexEnd - vertexStart) * mVertexSize; + D3D9Assert( vb->Lock(vertexStart * mVertexSize, sizeToLock, vertexPtr, flags), + "Unable to lock vertex buffer."); + + #ifdef TORQUE_DEBUG + + // Allocate a debug buffer large enough for the lock + // plus space for over and under run guard strings. + const U32 guardSize = sizeof( _VBGuardString ); + mDebugGuardBuffer = new U8[sizeToLock+(guardSize*2)]; + + // Setup the guard strings. + dMemcpy( mDebugGuardBuffer, _VBGuardString, guardSize ); + dMemcpy( mDebugGuardBuffer + sizeToLock + guardSize, _VBGuardString, guardSize ); + + // Store the real lock pointer and return our debug pointer. + mLockedBuffer = *vertexPtr; + *vertexPtr = mDebugGuardBuffer + guardSize; + + #endif // TORQUE_DEBUG + +#endif +} + +void GFXD3D9VertexBuffer::unlock() +{ + PROFILE_SCOPE(GFXD3D9VertexBuffer_unlock); + + #ifdef TORQUE_DEBUG + + if ( mDebugGuardBuffer ) + { + const U32 guardSize = sizeof( _VBGuardString ); + const U32 sizeLocked = (lockedVertexEnd - lockedVertexStart) * mVertexSize; + + // First check the guard areas for overwrites. + AssertFatal( dMemcmp( mDebugGuardBuffer, _VBGuardString, guardSize ) == 0, + "GFXD3D9VertexBuffer::unlock - Caught lock memory underrun!" ); + AssertFatal( dMemcmp( mDebugGuardBuffer + sizeLocked + guardSize, _VBGuardString, guardSize ) == 0, + "GFXD3D9VertexBuffer::unlock - Caught lock memory overrun!" ); + + // Copy the debug content down to the real VB. + dMemcpy( mLockedBuffer, mDebugGuardBuffer + guardSize, sizeLocked ); + + // Cleanup. + delete [] mDebugGuardBuffer; + mDebugGuardBuffer = NULL; + mLockedBuffer = NULL; + } + + #endif // TORQUE_DEBUG + + D3D9Assert( vb->Unlock(), + "Unable to unlock vertex buffer."); + mIsFirstLock = false; + + // Con::printf("%x: Unlocking %s range (%d, %d)", this, (mBufferType == GFXBufferTypeVolatile ? "volatile" : "static"), lockedVertexStart, lockedVertexEnd); + + lockedVertexEnd = lockedVertexStart = 0; + + if(mVolatileBuffer.isValid()) + { + mVolatileBuffer->lockedVertexStart = 0; + mVolatileBuffer->lockedVertexEnd = 0; + mVolatileBuffer = NULL; + //vb->Release(); + //vb = NULL; + } +} + +void GFXD3D9VertexBuffer::zombify() +{ + AssertFatal(lockedVertexStart == 0 && lockedVertexEnd == 0, "GFXD3D9VertexBuffer::zombify - Cannot zombify a locked buffer!"); + // Static buffers are managed by D3D9 so we don't deal with them. + if(mBufferType == GFXBufferTypeDynamic) + { + SAFE_RELEASE(vb); + } +} + +void GFXD3D9VertexBuffer::resurrect() +{ + // Static buffers are managed by D3D9 so we don't deal with them. + if(mBufferType == GFXBufferTypeDynamic) + { + D3D9Assert(static_cast(mDevice)->mD3DDevice->CreateVertexBuffer( mVertexSize * mNumVerts, +#ifdef TORQUE_OS_XENON + D3DUSAGE_WRITEONLY, +#else + D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, +#endif + 0, + D3DPOOL_DEFAULT, + &vb, + NULL ), + "GFXD3D9VertexBuffer::resurrect - Failed to allocate VB" ); + } +} + diff --git a/Engine/source/gfx/D3D9/gfxD3D9VertexBuffer.h b/Engine/source/gfx/D3D9/gfxD3D9VertexBuffer.h new file mode 100644 index 000000000..3be0e24b3 --- /dev/null +++ b/Engine/source/gfx/D3D9/gfxD3D9VertexBuffer.h @@ -0,0 +1,136 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXD3D_VERTEXBUFFER_H_ +#define _GFXD3D_VERTEXBUFFER_H_ + +#ifndef _GFXD3D9DEVICE_H_ +#include "gfx/D3D9/gfxD3D9Device.h" +#endif +#ifndef _SAFEDELETE_H_ +#include "core/util/safeDelete.h" +#endif + +//***************************************************************************** +// GFXD3D9VertexBuffer +//***************************************************************************** +class GFXD3D9VertexBuffer : public GFXVertexBuffer +{ +public: + IDirect3DVertexBuffer9 *vb; + StrongRefPtr mVolatileBuffer; + +#ifdef TORQUE_DEBUG + #define _VBGuardString "GFX_VERTEX_BUFFER_GUARD_STRING" + U8 *mDebugGuardBuffer; + void *mLockedBuffer; +#endif TORQUE_DEBUG + + bool mIsFirstLock; + bool mClearAtFrameEnd; + + GFXD3D9VertexBuffer(); + GFXD3D9VertexBuffer( GFXDevice *device, + U32 numVerts, + const GFXVertexFormat *vertexFormat, + U32 vertexSize, + GFXBufferType bufferType ); + virtual ~GFXD3D9VertexBuffer(); + + void lock(U32 vertexStart, U32 vertexEnd, void **vertexPtr); + void unlock(); + void prepare() {} + +#ifdef TORQUE_DEBUG + char *name; + + /// In debug compile, the verts will be chained together and the device + /// will examine the chain when it's destructor is called, this will + /// allow developers to see which vertex buffers are not destroyed + GFXD3D9VertexBuffer *next; +#endif + void setName( const char *n ); + + // GFXResource interface + virtual void zombify(); + virtual void resurrect(); +}; + +//----------------------------------------------------------------------------- +// This is for debugging vertex buffers and trying to track down which vbs +// aren't getting free'd + +inline GFXD3D9VertexBuffer::GFXD3D9VertexBuffer() +: GFXVertexBuffer(0,0,0,0,(GFXBufferType)0) +{ +#ifdef TORQUE_DEBUG + name = NULL; +#endif + vb = NULL; + mIsFirstLock = true; + lockedVertexEnd = lockedVertexStart = 0; + mClearAtFrameEnd = false; + +#ifdef TORQUE_DEBUG + mDebugGuardBuffer = NULL; + mLockedBuffer = NULL; +#endif +} + +inline GFXD3D9VertexBuffer::GFXD3D9VertexBuffer( GFXDevice *device, + U32 numVerts, + const GFXVertexFormat *vertexFormat, + U32 vertexSize, + GFXBufferType bufferType ) + : GFXVertexBuffer( device, numVerts, vertexFormat, vertexSize, bufferType ) +{ +#ifdef TORQUE_DEBUG + name = NULL; +#endif + vb = NULL; + mIsFirstLock = true; + mClearAtFrameEnd = false; + lockedVertexEnd = lockedVertexStart = 0; + +#ifdef TORQUE_DEBUG + mDebugGuardBuffer = NULL; + mLockedBuffer = NULL; +#endif +} + +#ifdef TORQUE_DEBUG + +inline void GFXD3D9VertexBuffer::setName( const char *n ) +{ + SAFE_DELETE( name ); + name = new char[dStrlen( n )]; + dStrcpy( name, n ); +} + +#else + +inline void GFXD3D9VertexBuffer::setName( const char *n ) { } + +#endif // !TORQUE_DEBUG + +#endif // _GFXD3D_VERTEXBUFFER_H_ + diff --git a/Engine/source/gfx/D3D9/pc/gfxD3D9Device.pc.cpp b/Engine/source/gfx/D3D9/pc/gfxD3D9Device.pc.cpp new file mode 100644 index 000000000..618a32117 --- /dev/null +++ b/Engine/source/gfx/D3D9/pc/gfxD3D9Device.pc.cpp @@ -0,0 +1,87 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/D3D9/pc/gfxPCD3D9Device.h" +#include "gfx/D3D9/gfxD3D9CardProfiler.h" +#include "gfx/D3D9/gfxD3D9Shader.h" +#include "gfx/D3D9/gfxD3D9VertexBuffer.h" +#include "gfx/D3D9/gfxD3D9EnumTranslate.h" +#include "core/strings/unicode.h" +#include "console/console.h" + +//------------------------------------------------------------------------------ +// D3DX Function binding +//------------------------------------------------------------------------------ + +bool d3dxBindFunction( DLibrary *dll, void *&fnAddress, const char *name ) +{ + fnAddress = dll->bind( name ); + + if (!fnAddress) + Con::warnf( "D3DX Loader: DLL bind failed for %s", name ); + + return fnAddress != 0; +} + +void GFXD3D9Device::initD3DXFnTable() +{ + if ( smD3DX.isLoaded ) + return; + + // We only load the d3dx version that we compiled + // and linked against which should keep unexpected + // problems from newer or older SDKs to a minimum. + String d3dxVersion = String::ToString( "d3dx9_%d.dll", (S32)D3DX_SDK_VERSION ); + smD3DX.dllRef = OsLoadLibrary( d3dxVersion ); + + // If the d3dx version we requested didn't load then we have + // a corrupt or old install of DirectX.... prompt them to update. + if ( !smD3DX.dllRef ) + { + Con::errorf( "Unsupported DirectX version!" ); + Platform::messageBox( Con::getVariable( "$appName" ), + "DirectX could not be started!\r\n" + "Please be sure you have the latest version of DirectX installed.", + MBOk, MIStop ); + Platform::forceShutdown( -1 ); + } + + smD3DX.isLoaded = true; + + #define D3DX_FUNCTION(fn_name, fn_return, fn_args) \ + smD3DX.isLoaded &= d3dxBindFunction(smD3DX.dllRef, *(void**)&smD3DX.fn_name, #fn_name); + # include "gfx/D3D9/d3dx9Functions.h" + #undef D3DX_FUNCTION + + AssertISV( smD3DX.isLoaded, "D3DX Failed to load all functions." ); + + // HACK: For some reason in the latest versions of + // the D3D SDK on the PC the shader compiler will load + // and unload the compiler DLL over and over with each + // shader compiled. + // + // By loading the DLL once ourselves we keep it from + // ever unloading it which makes shader compiling faster. + // + String compilerVersion = String::ToString( "D3DCompiler_%d.dll", (S32)D3DX_SDK_VERSION ); + smD3DX.compilerDllRef = OsLoadLibrary( compilerVersion ); +} diff --git a/Engine/source/gfx/D3D9/pc/gfxD3D9EnumTranslate.pc.cpp b/Engine/source/gfx/D3D9/pc/gfxD3D9EnumTranslate.pc.cpp new file mode 100644 index 000000000..ae143cfc3 --- /dev/null +++ b/Engine/source/gfx/D3D9/pc/gfxD3D9EnumTranslate.pc.cpp @@ -0,0 +1,376 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include +#include "gfx/D3D9/gfxD3D9EnumTranslate.h" +#include "console/console.h" + +//------------------------------------------------------------------------------ + +_D3DFORMAT GFXD3D9IndexFormat[GFXIndexFormat_COUNT]; +_D3DSAMPLERSTATETYPE GFXD3D9SamplerState[GFXSAMP_COUNT]; +_D3DFORMAT GFXD3D9TextureFormat[GFXFormat_COUNT]; +_D3DRENDERSTATETYPE GFXD3D9RenderState[GFXRenderState_COUNT]; +_D3DTEXTUREFILTERTYPE GFXD3D9TextureFilter[GFXTextureFilter_COUNT]; +_D3DBLEND GFXD3D9Blend[GFXBlend_COUNT]; +_D3DBLENDOP GFXD3D9BlendOp[GFXBlendOp_COUNT]; +_D3DSTENCILOP GFXD3D9StencilOp[GFXStencilOp_COUNT]; +_D3DCMPFUNC GFXD3D9CmpFunc[GFXCmp_COUNT]; +_D3DCULL GFXD3D9CullMode[GFXCull_COUNT]; +_D3DFILLMODE GFXD3D9FillMode[GFXFill_COUNT]; +_D3DPRIMITIVETYPE GFXD3D9PrimType[GFXPT_COUNT]; +_D3DTEXTURESTAGESTATETYPE GFXD3D9TextureStageState[GFXTSS_COUNT]; +_D3DTEXTUREADDRESS GFXD3D9TextureAddress[GFXAddress_COUNT]; +_D3DTEXTUREOP GFXD3D9TextureOp[GFXTOP_COUNT]; +_D3DDECLTYPE GFXD3D9DeclType[GFXDeclType_COUNT]; + +//------------------------------------------------------------------------------ + +#define INIT_LOOKUPTABLE( tablearray, enumprefix, type ) \ + for( int i = enumprefix##_FIRST; i < enumprefix##_COUNT; i++ ) \ + tablearray##[i] = (##type##)GFX_UNINIT_VAL; + +#define VALIDATE_LOOKUPTABLE( tablearray, enumprefix ) \ + for( int i = enumprefix##_FIRST; i < enumprefix##_COUNT; i++ ) \ + if( (int)tablearray##[i] == GFX_UNINIT_VAL ) \ + Con::warnf( "GFXD3D9EnumTranslate: Unassigned value in " #tablearray ": %i", i ); \ + else if( (int)tablearray##[i] == GFX_UNSUPPORTED_VAL ) \ + Con::warnf( "GFXD3D9EnumTranslate: Unsupported value in " #tablearray ": %i", i ); + +//------------------------------------------------------------------------------ + +void GFXD3D9EnumTranslate::init() +{ + INIT_LOOKUPTABLE( GFXD3D9IndexFormat, GFXIndexFormat, _D3DFORMAT ); + GFXD3D9IndexFormat[GFXIndexFormat16] = D3DFMT_INDEX16; + GFXD3D9IndexFormat[GFXIndexFormat32] = D3DFMT_INDEX32; + VALIDATE_LOOKUPTABLE( GFXD3D9IndexFormat, GFXIndexFormat ); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE( GFXD3D9SamplerState, GFXSAMP, _D3DSAMPLERSTATETYPE ); + GFXD3D9SamplerState[GFXSAMPAddressU] = D3DSAMP_ADDRESSU; + GFXD3D9SamplerState[GFXSAMPAddressV] = D3DSAMP_ADDRESSV; + GFXD3D9SamplerState[GFXSAMPAddressW] = D3DSAMP_ADDRESSW; + GFXD3D9SamplerState[GFXSAMPBorderColor] = D3DSAMP_BORDERCOLOR; + GFXD3D9SamplerState[GFXSAMPMagFilter] = D3DSAMP_MAGFILTER; + GFXD3D9SamplerState[GFXSAMPMinFilter] = D3DSAMP_MINFILTER; + GFXD3D9SamplerState[GFXSAMPMipFilter] = D3DSAMP_MIPFILTER; + GFXD3D9SamplerState[GFXSAMPMipMapLODBias] = D3DSAMP_MIPMAPLODBIAS; + GFXD3D9SamplerState[GFXSAMPMaxMipLevel] = D3DSAMP_MAXMIPLEVEL; + GFXD3D9SamplerState[GFXSAMPMaxAnisotropy] = D3DSAMP_MAXANISOTROPY; + GFXD3D9SamplerState[GFXSAMPSRGBTexture] = D3DSAMP_SRGBTEXTURE; + GFXD3D9SamplerState[GFXSAMPElementIndex] = D3DSAMP_ELEMENTINDEX; + GFXD3D9SamplerState[GFXSAMPDMapOffset] = D3DSAMP_DMAPOFFSET; + VALIDATE_LOOKUPTABLE( GFXD3D9SamplerState, GFXSAMP ); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE( GFXD3D9TextureFormat, GFXFormat, _D3DFORMAT ); + GFXD3D9TextureFormat[GFXFormatR8G8B8] = D3DFMT_R8G8B8; + GFXD3D9TextureFormat[GFXFormatR8G8B8A8] = D3DFMT_A8R8G8B8; + GFXD3D9TextureFormat[GFXFormatR8G8B8X8] = D3DFMT_X8R8G8B8; + GFXD3D9TextureFormat[GFXFormatR5G6B5] = D3DFMT_R5G6B5; + GFXD3D9TextureFormat[GFXFormatR5G5B5A1] = D3DFMT_A1R5G5B5; + GFXD3D9TextureFormat[GFXFormatR5G5B5X1] = D3DFMT_X1R5G5B5; + GFXD3D9TextureFormat[GFXFormatR32F] = D3DFMT_R32F; + GFXD3D9TextureFormat[GFXFormatA4L4] = D3DFMT_A4L4; + GFXD3D9TextureFormat[GFXFormatA8L8] = D3DFMT_A8L8; + GFXD3D9TextureFormat[GFXFormatA8] = D3DFMT_A8; + GFXD3D9TextureFormat[GFXFormatL8] = D3DFMT_L8; + GFXD3D9TextureFormat[GFXFormatDXT1] = D3DFMT_DXT1; + GFXD3D9TextureFormat[GFXFormatDXT2] = D3DFMT_DXT2; + GFXD3D9TextureFormat[GFXFormatDXT3] = D3DFMT_DXT3; + GFXD3D9TextureFormat[GFXFormatDXT4] = D3DFMT_DXT4; + GFXD3D9TextureFormat[GFXFormatDXT5] = D3DFMT_DXT5; + GFXD3D9TextureFormat[GFXFormatR32G32B32A32F] = D3DFMT_A32B32G32R32F; + GFXD3D9TextureFormat[GFXFormatR16G16B16A16F] = D3DFMT_A16B16G16R16F; + GFXD3D9TextureFormat[GFXFormatL16] = D3DFMT_L16; + GFXD3D9TextureFormat[GFXFormatR16G16B16A16] = D3DFMT_A16B16G16R16; + GFXD3D9TextureFormat[GFXFormatR16G16] = D3DFMT_G16R16; + GFXD3D9TextureFormat[GFXFormatR16F] = D3DFMT_R16F; + GFXD3D9TextureFormat[GFXFormatR16G16F] = D3DFMT_G16R16F; + GFXD3D9TextureFormat[GFXFormatR10G10B10A2] = D3DFMT_A2R10G10B10; + GFXD3D9TextureFormat[GFXFormatD32] = D3DFMT_D32; + GFXD3D9TextureFormat[GFXFormatD24X8] = D3DFMT_D24X8; + GFXD3D9TextureFormat[GFXFormatD24S8] = D3DFMT_D24S8; + GFXD3D9TextureFormat[GFXFormatD24FS8] = D3DFMT_D24FS8; + GFXD3D9TextureFormat[GFXFormatD16] = D3DFMT_D16; + VALIDATE_LOOKUPTABLE( GFXD3D9TextureFormat, GFXFormat); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE( GFXD3D9RenderState, GFXRenderState, _D3DRENDERSTATETYPE ); + GFXD3D9RenderState[GFXRSZEnable] = D3DRS_ZENABLE; + GFXD3D9RenderState[GFXRSFillMode] = D3DRS_FILLMODE; + GFXD3D9RenderState[GFXRSZWriteEnable] = D3DRS_ZWRITEENABLE; + GFXD3D9RenderState[GFXRSAlphaTestEnable] = D3DRS_ALPHATESTENABLE; + GFXD3D9RenderState[GFXRSSrcBlend] = D3DRS_SRCBLEND; + GFXD3D9RenderState[GFXRSDestBlend] = D3DRS_DESTBLEND; + GFXD3D9RenderState[GFXRSCullMode] = D3DRS_CULLMODE; + GFXD3D9RenderState[GFXRSZFunc] = D3DRS_ZFUNC; + GFXD3D9RenderState[GFXRSAlphaRef] = D3DRS_ALPHAREF; + GFXD3D9RenderState[GFXRSAlphaFunc] = D3DRS_ALPHAFUNC; + GFXD3D9RenderState[GFXRSAlphaBlendEnable] = D3DRS_ALPHABLENDENABLE; + GFXD3D9RenderState[GFXRSStencilEnable] = D3DRS_STENCILENABLE; + GFXD3D9RenderState[GFXRSStencilFail] = D3DRS_STENCILFAIL; + GFXD3D9RenderState[GFXRSStencilZFail] = D3DRS_STENCILZFAIL; + GFXD3D9RenderState[GFXRSStencilPass] = D3DRS_STENCILPASS; + GFXD3D9RenderState[GFXRSStencilFunc] = D3DRS_STENCILFUNC; + GFXD3D9RenderState[GFXRSStencilRef] = D3DRS_STENCILREF; + GFXD3D9RenderState[GFXRSStencilMask] = D3DRS_STENCILMASK; + GFXD3D9RenderState[GFXRSStencilWriteMask] = D3DRS_STENCILWRITEMASK; + GFXD3D9RenderState[GFXRSWrap0] = D3DRS_WRAP0; + GFXD3D9RenderState[GFXRSWrap1] = D3DRS_WRAP1; + GFXD3D9RenderState[GFXRSWrap2] = D3DRS_WRAP2; + GFXD3D9RenderState[GFXRSWrap3] = D3DRS_WRAP3; + GFXD3D9RenderState[GFXRSWrap4] = D3DRS_WRAP4; + GFXD3D9RenderState[GFXRSWrap5] = D3DRS_WRAP5; + GFXD3D9RenderState[GFXRSWrap6] = D3DRS_WRAP6; + GFXD3D9RenderState[GFXRSWrap7] = D3DRS_WRAP7; + GFXD3D9RenderState[GFXRSClipPlaneEnable] = D3DRS_CLIPPLANEENABLE; + GFXD3D9RenderState[GFXRSPointSize] = D3DRS_POINTSIZE; + GFXD3D9RenderState[GFXRSPointSizeMin] = D3DRS_POINTSIZE_MIN; + GFXD3D9RenderState[GFXRSPointSize_Max] = D3DRS_POINTSIZE_MAX; + GFXD3D9RenderState[GFXRSPointSpriteEnable] = D3DRS_POINTSPRITEENABLE; + GFXD3D9RenderState[GFXRSMultiSampleantiAlias] = D3DRS_MULTISAMPLEANTIALIAS; + GFXD3D9RenderState[GFXRSMultiSampleMask] = D3DRS_MULTISAMPLEMASK; + GFXD3D9RenderState[GFXRSShadeMode] = D3DRS_SHADEMODE; + GFXD3D9RenderState[GFXRSLastPixel] = D3DRS_LASTPIXEL; + GFXD3D9RenderState[GFXRSClipping] = D3DRS_CLIPPING; + GFXD3D9RenderState[GFXRSPointScaleEnable] = D3DRS_POINTSCALEENABLE; + GFXD3D9RenderState[GFXRSPointScale_A] = D3DRS_POINTSCALE_A; + GFXD3D9RenderState[GFXRSPointScale_B] = D3DRS_POINTSCALE_B; + GFXD3D9RenderState[GFXRSPointScale_C] = D3DRS_POINTSCALE_C; + GFXD3D9RenderState[GFXRSLighting] = D3DRS_LIGHTING; + GFXD3D9RenderState[GFXRSAmbient] = D3DRS_AMBIENT; + GFXD3D9RenderState[GFXRSFogVertexMode] = D3DRS_FOGVERTEXMODE; + GFXD3D9RenderState[GFXRSColorVertex] = D3DRS_COLORVERTEX; + GFXD3D9RenderState[GFXRSLocalViewer] = D3DRS_LOCALVIEWER; + GFXD3D9RenderState[GFXRSNormalizeNormals] = D3DRS_NORMALIZENORMALS; + GFXD3D9RenderState[GFXRSDiffuseMaterialSource] = D3DRS_DIFFUSEMATERIALSOURCE; + GFXD3D9RenderState[GFXRSSpecularMaterialSource] = D3DRS_SPECULARMATERIALSOURCE; + GFXD3D9RenderState[GFXRSAmbientMaterialSource] = D3DRS_AMBIENTMATERIALSOURCE; + GFXD3D9RenderState[GFXRSEmissiveMaterialSource] = D3DRS_EMISSIVEMATERIALSOURCE; + GFXD3D9RenderState[GFXRSVertexBlend] = D3DRS_VERTEXBLEND; + GFXD3D9RenderState[GFXRSFogEnable] = D3DRS_FOGENABLE; + GFXD3D9RenderState[GFXRSSpecularEnable] = D3DRS_SPECULARENABLE; + GFXD3D9RenderState[GFXRSFogColor] = D3DRS_FOGCOLOR; + GFXD3D9RenderState[GFXRSFogTableMode] = D3DRS_FOGTABLEMODE; + GFXD3D9RenderState[GFXRSFogStart] = D3DRS_FOGSTART; + GFXD3D9RenderState[GFXRSFogEnd] = D3DRS_FOGEND; + GFXD3D9RenderState[GFXRSFogDensity] = D3DRS_FOGDENSITY; + GFXD3D9RenderState[GFXRSRangeFogEnable] = D3DRS_RANGEFOGENABLE; + GFXD3D9RenderState[GFXRSDebugMonitorToken] = D3DRS_DEBUGMONITORTOKEN; + GFXD3D9RenderState[GFXRSIndexedVertexBlendEnable] = D3DRS_INDEXEDVERTEXBLENDENABLE; + GFXD3D9RenderState[GFXRSTweenFactor] = D3DRS_TWEENFACTOR; + GFXD3D9RenderState[GFXRSTextureFactor] = D3DRS_TEXTUREFACTOR; + GFXD3D9RenderState[GFXRSPatchEdgeStyle] = D3DRS_PATCHEDGESTYLE; + GFXD3D9RenderState[GFXRSPositionDegree] = D3DRS_POSITIONDEGREE; + GFXD3D9RenderState[GFXRSNormalDegree] = D3DRS_NORMALDEGREE; + GFXD3D9RenderState[GFXRSAntiAliasedLineEnable] = D3DRS_ANTIALIASEDLINEENABLE; + GFXD3D9RenderState[GFXRSAdaptiveTess_X] = D3DRS_ADAPTIVETESS_X; + GFXD3D9RenderState[GFXRSAdaptiveTess_Y] = D3DRS_ADAPTIVETESS_Y; + GFXD3D9RenderState[GFXRSdaptiveTess_Z] = D3DRS_ADAPTIVETESS_Z; + GFXD3D9RenderState[GFXRSAdaptiveTess_W] = D3DRS_ADAPTIVETESS_W; + GFXD3D9RenderState[GFXRSEnableAdaptiveTesselation] = D3DRS_ENABLEADAPTIVETESSELLATION; + GFXD3D9RenderState[GFXRSDitherEnable] = D3DRS_DITHERENABLE; + GFXD3D9RenderState[GFXRSColorWriteEnable] = D3DRS_COLORWRITEENABLE; + GFXD3D9RenderState[GFXRSBlendOp] = D3DRS_BLENDOP; + GFXD3D9RenderState[GFXRSScissorTestEnable] = D3DRS_SCISSORTESTENABLE; + GFXD3D9RenderState[GFXRSSlopeScaleDepthBias] = D3DRS_SLOPESCALEDEPTHBIAS; + GFXD3D9RenderState[GFXRSMinTessellationLevel] = D3DRS_MINTESSELLATIONLEVEL; + GFXD3D9RenderState[GFXRSMaxTessellationLevel] = D3DRS_MAXTESSELLATIONLEVEL; + GFXD3D9RenderState[GFXRSTwoSidedStencilMode] = D3DRS_TWOSIDEDSTENCILMODE; + GFXD3D9RenderState[GFXRSCCWStencilFail] = D3DRS_CCW_STENCILFAIL; + GFXD3D9RenderState[GFXRSCCWStencilZFail] = D3DRS_CCW_STENCILZFAIL; + GFXD3D9RenderState[GFXRSCCWStencilPass] = D3DRS_CCW_STENCILPASS; + GFXD3D9RenderState[GFXRSCCWStencilFunc] = D3DRS_CCW_STENCILFUNC; + GFXD3D9RenderState[GFXRSColorWriteEnable1] = D3DRS_COLORWRITEENABLE1; + GFXD3D9RenderState[GFXRSColorWriteEnable2] = D3DRS_COLORWRITEENABLE2; + GFXD3D9RenderState[GFXRSolorWriteEnable3] = D3DRS_COLORWRITEENABLE3; + GFXD3D9RenderState[GFXRSBlendFactor] = D3DRS_BLENDFACTOR; + GFXD3D9RenderState[GFXRSSRGBWriteEnable] = D3DRS_SRGBWRITEENABLE; + GFXD3D9RenderState[GFXRSDepthBias] = D3DRS_DEPTHBIAS; + GFXD3D9RenderState[GFXRSWrap8] = D3DRS_WRAP8; + GFXD3D9RenderState[GFXRSWrap9] = D3DRS_WRAP9; + GFXD3D9RenderState[GFXRSWrap10] = D3DRS_WRAP10; + GFXD3D9RenderState[GFXRSWrap11] = D3DRS_WRAP11; + GFXD3D9RenderState[GFXRSWrap12] = D3DRS_WRAP12; + GFXD3D9RenderState[GFXRSWrap13] = D3DRS_WRAP13; + GFXD3D9RenderState[GFXRSWrap14] = D3DRS_WRAP14; + GFXD3D9RenderState[GFXRSWrap15] = D3DRS_WRAP15; + GFXD3D9RenderState[GFXRSSeparateAlphaBlendEnable] = D3DRS_SEPARATEALPHABLENDENABLE; + GFXD3D9RenderState[GFXRSSrcBlendAlpha] = D3DRS_SRCBLENDALPHA; + GFXD3D9RenderState[GFXRSDestBlendAlpha] = D3DRS_DESTBLENDALPHA; + GFXD3D9RenderState[GFXRSBlendOpAlpha] = D3DRS_BLENDOPALPHA; + VALIDATE_LOOKUPTABLE( GFXD3D9RenderState, GFXRenderState ); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE( GFXD3D9TextureFilter, GFXTextureFilter, _D3DTEXTUREFILTERTYPE ); + GFXD3D9TextureFilter[GFXTextureFilterNone] = D3DTEXF_NONE; + GFXD3D9TextureFilter[GFXTextureFilterPoint] = D3DTEXF_POINT; + GFXD3D9TextureFilter[GFXTextureFilterLinear] = D3DTEXF_LINEAR; + GFXD3D9TextureFilter[GFXTextureFilterAnisotropic] = D3DTEXF_ANISOTROPIC; + GFXD3D9TextureFilter[GFXTextureFilterPyramidalQuad] = D3DTEXF_PYRAMIDALQUAD; + GFXD3D9TextureFilter[GFXTextureFilterGaussianQuad] = D3DTEXF_GAUSSIANQUAD; + VALIDATE_LOOKUPTABLE( GFXD3D9TextureFilter, GFXTextureFilter ); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE( GFXD3D9Blend, GFXBlend, _D3DBLEND ); + GFXD3D9Blend[GFXBlendZero] = D3DBLEND_ZERO; + GFXD3D9Blend[GFXBlendOne] = D3DBLEND_ONE; + GFXD3D9Blend[GFXBlendSrcColor] = D3DBLEND_SRCCOLOR; + GFXD3D9Blend[GFXBlendInvSrcColor] = D3DBLEND_INVSRCCOLOR; + GFXD3D9Blend[GFXBlendSrcAlpha] = D3DBLEND_SRCALPHA; + GFXD3D9Blend[GFXBlendInvSrcAlpha] = D3DBLEND_INVSRCALPHA; + GFXD3D9Blend[GFXBlendDestAlpha] = D3DBLEND_DESTALPHA; + GFXD3D9Blend[GFXBlendInvDestAlpha] = D3DBLEND_INVDESTALPHA; + GFXD3D9Blend[GFXBlendDestColor] = D3DBLEND_DESTCOLOR; + GFXD3D9Blend[GFXBlendInvDestColor] = D3DBLEND_INVDESTCOLOR; + GFXD3D9Blend[GFXBlendSrcAlphaSat] = D3DBLEND_SRCALPHASAT; + VALIDATE_LOOKUPTABLE( GFXD3D9Blend, GFXBlend ); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE( GFXD3D9BlendOp, GFXBlendOp, _D3DBLENDOP ); + GFXD3D9BlendOp[GFXBlendOpAdd] = D3DBLENDOP_ADD; + GFXD3D9BlendOp[GFXBlendOpSubtract] = D3DBLENDOP_SUBTRACT; + GFXD3D9BlendOp[GFXBlendOpRevSubtract] = D3DBLENDOP_REVSUBTRACT; + GFXD3D9BlendOp[GFXBlendOpMin] = D3DBLENDOP_MIN; + GFXD3D9BlendOp[GFXBlendOpMax] = D3DBLENDOP_MAX; + VALIDATE_LOOKUPTABLE( GFXD3D9BlendOp, GFXBlendOp ); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE( GFXD3D9StencilOp, GFXStencilOp, _D3DSTENCILOP ); + GFXD3D9StencilOp[GFXStencilOpKeep] = D3DSTENCILOP_KEEP; + GFXD3D9StencilOp[GFXStencilOpZero] = D3DSTENCILOP_ZERO; + GFXD3D9StencilOp[GFXStencilOpReplace] = D3DSTENCILOP_REPLACE; + GFXD3D9StencilOp[GFXStencilOpIncrSat] = D3DSTENCILOP_INCRSAT; + GFXD3D9StencilOp[GFXStencilOpDecrSat] = D3DSTENCILOP_DECRSAT; + GFXD3D9StencilOp[GFXStencilOpInvert] = D3DSTENCILOP_INVERT; + GFXD3D9StencilOp[GFXStencilOpIncr] = D3DSTENCILOP_INCR; + GFXD3D9StencilOp[GFXStencilOpDecr] = D3DSTENCILOP_DECR; + VALIDATE_LOOKUPTABLE( GFXD3D9StencilOp, GFXStencilOp ); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE( GFXD3D9CmpFunc, GFXCmp, _D3DCMPFUNC ); + GFXD3D9CmpFunc[GFXCmpNever] = D3DCMP_NEVER; + GFXD3D9CmpFunc[GFXCmpLess] = D3DCMP_LESS; + GFXD3D9CmpFunc[GFXCmpEqual] = D3DCMP_EQUAL; + GFXD3D9CmpFunc[GFXCmpLessEqual] = D3DCMP_LESSEQUAL; + GFXD3D9CmpFunc[GFXCmpGreater] = D3DCMP_GREATER; + GFXD3D9CmpFunc[GFXCmpNotEqual] = D3DCMP_NOTEQUAL; + GFXD3D9CmpFunc[GFXCmpGreaterEqual] = D3DCMP_GREATEREQUAL; + GFXD3D9CmpFunc[GFXCmpAlways] = D3DCMP_ALWAYS; + VALIDATE_LOOKUPTABLE( GFXD3D9CmpFunc, GFXCmp ); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE( GFXD3D9CullMode, GFXCull, _D3DCULL ); + GFXD3D9CullMode[GFXCullNone] = D3DCULL_NONE; + GFXD3D9CullMode[GFXCullCW] = D3DCULL_CW; + GFXD3D9CullMode[GFXCullCCW] = D3DCULL_CCW; + VALIDATE_LOOKUPTABLE( GFXD3D9CullMode, GFXCull ); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE( GFXD3D9FillMode, GFXFill, _D3DFILLMODE ); + GFXD3D9FillMode[GFXFillPoint] = D3DFILL_POINT; + GFXD3D9FillMode[GFXFillWireframe] = D3DFILL_WIREFRAME; + GFXD3D9FillMode[GFXFillSolid] = D3DFILL_SOLID; + VALIDATE_LOOKUPTABLE( GFXD3D9FillMode, GFXFill ); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE( GFXD3D9PrimType, GFXPT, _D3DPRIMITIVETYPE ); + GFXD3D9PrimType[GFXPointList] = D3DPT_POINTLIST; + GFXD3D9PrimType[GFXLineList] = D3DPT_LINELIST; + GFXD3D9PrimType[GFXLineStrip] = D3DPT_LINESTRIP; + GFXD3D9PrimType[GFXTriangleList] = D3DPT_TRIANGLELIST; + GFXD3D9PrimType[GFXTriangleStrip] = D3DPT_TRIANGLESTRIP; + GFXD3D9PrimType[GFXTriangleFan] = D3DPT_TRIANGLEFAN; + VALIDATE_LOOKUPTABLE( GFXD3D9PrimType, GFXPT ); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE( GFXD3D9TextureStageState, GFXTSS, _D3DTEXTURESTAGESTATETYPE ); + GFXD3D9TextureStageState[GFXTSSColorOp] = D3DTSS_COLOROP; + GFXD3D9TextureStageState[GFXTSSColorArg1] = D3DTSS_COLORARG1; + GFXD3D9TextureStageState[GFXTSSColorArg2] = D3DTSS_COLORARG2; + GFXD3D9TextureStageState[GFXTSSAlphaOp] = D3DTSS_ALPHAOP; + GFXD3D9TextureStageState[GFXTSSAlphaArg1] = D3DTSS_ALPHAARG1; + GFXD3D9TextureStageState[GFXTSSAlphaArg2] = D3DTSS_ALPHAARG2; + GFXD3D9TextureStageState[GFXTSSBumpEnvMat00] = D3DTSS_BUMPENVMAT00; + GFXD3D9TextureStageState[GFXTSSBumpEnvMat01] = D3DTSS_BUMPENVMAT01; + GFXD3D9TextureStageState[GFXTSSBumpEnvMat10] = D3DTSS_BUMPENVMAT10; + GFXD3D9TextureStageState[GFXTSSBumpEnvMat11] = D3DTSS_BUMPENVMAT11; + GFXD3D9TextureStageState[GFXTSSTexCoordIndex] = D3DTSS_TEXCOORDINDEX; + GFXD3D9TextureStageState[GFXTSSBumpEnvlScale] = D3DTSS_BUMPENVLSCALE; + GFXD3D9TextureStageState[GFXTSSBumpEnvlOffset] = D3DTSS_BUMPENVLOFFSET; + GFXD3D9TextureStageState[GFXTSSTextureTransformFlags] = D3DTSS_TEXTURETRANSFORMFLAGS; + GFXD3D9TextureStageState[GFXTSSColorArg0] = D3DTSS_COLORARG0; + GFXD3D9TextureStageState[GFXTSSAlphaArg0] = D3DTSS_ALPHAARG0; + GFXD3D9TextureStageState[GFXTSSResultArg] = D3DTSS_RESULTARG; + GFXD3D9TextureStageState[GFXTSSConstant] = D3DTSS_CONSTANT; + VALIDATE_LOOKUPTABLE( GFXD3D9TextureStageState, GFXTSS ); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE( GFXD3D9TextureAddress, GFXAddress, _D3DTEXTUREADDRESS ); + GFXD3D9TextureAddress[GFXAddressWrap] = D3DTADDRESS_WRAP ; + GFXD3D9TextureAddress[GFXAddressMirror] = D3DTADDRESS_MIRROR; + GFXD3D9TextureAddress[GFXAddressClamp] = D3DTADDRESS_CLAMP; + GFXD3D9TextureAddress[GFXAddressBorder] = D3DTADDRESS_BORDER; + GFXD3D9TextureAddress[GFXAddressMirrorOnce] = D3DTADDRESS_MIRRORONCE; + VALIDATE_LOOKUPTABLE(GFXD3D9TextureAddress, GFXAddress ); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE( GFXD3D9TextureOp, GFXTOP, _D3DTEXTUREOP ); + GFXD3D9TextureOp[GFXTOPDisable] = D3DTOP_DISABLE; + GFXD3D9TextureOp[GFXTOPSelectARG1] = D3DTOP_SELECTARG1; + GFXD3D9TextureOp[GFXTOPSelectARG2] = D3DTOP_SELECTARG2; + GFXD3D9TextureOp[GFXTOPModulate] = D3DTOP_MODULATE; + GFXD3D9TextureOp[GFXTOPModulate2X] = D3DTOP_MODULATE2X; + GFXD3D9TextureOp[GFXTOPModulate4X] = D3DTOP_MODULATE4X; + GFXD3D9TextureOp[GFXTOPAdd] = D3DTOP_ADD; + GFXD3D9TextureOp[GFXTOPAddSigned] = D3DTOP_ADDSIGNED; + GFXD3D9TextureOp[GFXTOPAddSigned2X] = D3DTOP_ADDSIGNED2X; + GFXD3D9TextureOp[GFXTOPSubtract] = D3DTOP_SUBTRACT; + GFXD3D9TextureOp[GFXTOPAddSmooth] = D3DTOP_ADDSMOOTH; + GFXD3D9TextureOp[GFXTOPBlendDiffuseAlpha] = D3DTOP_BLENDDIFFUSEALPHA; + GFXD3D9TextureOp[GFXTOPBlendTextureAlpha] = D3DTOP_BLENDTEXTUREALPHA; + GFXD3D9TextureOp[GFXTOPBlendFactorAlpha] = D3DTOP_BLENDFACTORALPHA; + GFXD3D9TextureOp[GFXTOPBlendTextureAlphaPM] = D3DTOP_BLENDTEXTUREALPHAPM; + GFXD3D9TextureOp[GFXTOPBlendCURRENTALPHA] = D3DTOP_BLENDCURRENTALPHA; + GFXD3D9TextureOp[GFXTOPPreModulate] = D3DTOP_PREMODULATE; + GFXD3D9TextureOp[GFXTOPModulateAlphaAddColor] = D3DTOP_MODULATEALPHA_ADDCOLOR; + GFXD3D9TextureOp[GFXTOPModulateColorAddAlpha] = D3DTOP_MODULATECOLOR_ADDALPHA; + GFXD3D9TextureOp[GFXTOPModulateInvAlphaAddColor] = D3DTOP_MODULATEINVALPHA_ADDCOLOR; + GFXD3D9TextureOp[GFXTOPModulateInvColorAddAlpha] = D3DTOP_MODULATEINVCOLOR_ADDALPHA; + GFXD3D9TextureOp[GFXTOPBumpEnvMap] = D3DTOP_BUMPENVMAP; + GFXD3D9TextureOp[GFXTOPBumpEnvMapLuminance] = D3DTOP_BUMPENVMAPLUMINANCE; + GFXD3D9TextureOp[GFXTOPDotProduct3] = D3DTOP_DOTPRODUCT3; + GFXD3D9TextureOp[GFXTOPLERP] = D3DTOP_LERP; + VALIDATE_LOOKUPTABLE( GFXD3D9TextureOp, GFXTOP ); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE( GFXD3D9DeclType, GFXDeclType, _D3DDECLTYPE ); + GFXD3D9DeclType[GFXDeclType_Float] = D3DDECLTYPE_FLOAT1; + GFXD3D9DeclType[GFXDeclType_Float2] = D3DDECLTYPE_FLOAT2; + GFXD3D9DeclType[GFXDeclType_Float3] = D3DDECLTYPE_FLOAT3; + GFXD3D9DeclType[GFXDeclType_Float4] = D3DDECLTYPE_FLOAT4; + GFXD3D9DeclType[GFXDeclType_Color] = D3DDECLTYPE_D3DCOLOR; + VALIDATE_LOOKUPTABLE( GFXD3D9DeclType, GFXDeclType ); +} + diff --git a/Engine/source/gfx/D3D9/pc/gfxD3D9PrimitiveBuffer.pc.cpp b/Engine/source/gfx/D3D9/pc/gfxD3D9PrimitiveBuffer.pc.cpp new file mode 100644 index 000000000..21bb586bb --- /dev/null +++ b/Engine/source/gfx/D3D9/pc/gfxD3D9PrimitiveBuffer.pc.cpp @@ -0,0 +1,94 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/D3D9/gfxD3D9Device.h" +#include "gfx/D3D9/gfxD3D9PrimitiveBuffer.h" +#include "core/util/safeRelease.h" + +void GFXD3D9PrimitiveBuffer::lock(U32 indexStart, U32 indexEnd, void **indexPtr) +{ + AssertFatal(!mLocked, "GFXD3D9PrimitiveBuffer::lock - Can't lock a primitive buffer more than once!"); + mLocked = true; + U32 flags=0; + switch(mBufferType) + { + case GFXBufferTypeStatic: + // flags |= D3DLOCK_DISCARD; + break; + + case GFXBufferTypeDynamic: + // Always discard the content within a locked region. + flags |= D3DLOCK_DISCARD; + break; + + case GFXBufferTypeVolatile: + // Get our range now... + AssertFatal(indexStart == 0, "Cannot get a subrange on a volatile buffer."); + AssertFatal(indexEnd < MAX_DYNAMIC_INDICES, "Cannot get more than MAX_DYNAMIC_INDICES in a volatile buffer. Up the constant!"); + + // Get the primtive buffer + mVolatileBuffer = ((GFXD3D9Device*)mDevice)->mDynamicPB; + + AssertFatal( mVolatileBuffer, "GFXD3D9PrimitiveBuffer::lock - No dynamic primitive buffer was available!"); + + // We created the pool when we requested this volatile buffer, so assume it exists... + if( mVolatileBuffer->mIndexCount + indexEnd > MAX_DYNAMIC_INDICES ) + { + flags |= D3DLOCK_DISCARD; + mVolatileStart = indexStart = 0; + indexEnd = indexEnd; + } + else + { + flags |= D3DLOCK_NOOVERWRITE; + mVolatileStart = indexStart = mVolatileBuffer->mIndexCount; + indexEnd += mVolatileBuffer->mIndexCount; + } + + mVolatileBuffer->mIndexCount = indexEnd + 1; + ib = mVolatileBuffer->ib; + + break; + } + + D3D9Assert( ib->Lock(indexStart * sizeof(U16), (indexEnd - indexStart) * sizeof(U16), indexPtr, flags), + "GFXD3D9PrimitiveBuffer::lock - Could not lock primitive buffer."); + + #ifdef TORQUE_DEBUG + + // Allocate a debug buffer large enough for the lock + // plus space for over and under run guard strings. + mLockedSize = (indexEnd - indexStart) * sizeof(U16); + const U32 guardSize = sizeof( _PBGuardString ); + mDebugGuardBuffer = new U8[mLockedSize+(guardSize*2)]; + + // Setup the guard strings. + dMemcpy( mDebugGuardBuffer, _PBGuardString, guardSize ); + dMemcpy( mDebugGuardBuffer + mLockedSize + guardSize, _PBGuardString, guardSize ); + + // Store the real lock pointer and return our debug pointer. + mLockedBuffer = *indexPtr; + *indexPtr = (U16*)( mDebugGuardBuffer + guardSize ); + + #endif // TORQUE_DEBUG +} + diff --git a/Engine/source/gfx/D3D9/pc/gfxPCD3D9Device.cpp b/Engine/source/gfx/D3D9/pc/gfxPCD3D9Device.cpp new file mode 100644 index 000000000..3d7f332b8 --- /dev/null +++ b/Engine/source/gfx/D3D9/pc/gfxPCD3D9Device.cpp @@ -0,0 +1,1176 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/D3D9/pc/gfxPCD3D9Device.h" +#include "gfx/D3D9/pc/gfxPCD3D9Target.h" +#include "gfx/D3D9/gfxD3D9CardProfiler.h" +#include "gfx/D3D9/gfxD3D9EnumTranslate.h" +#include "platformWin32/platformWin32.h" +#include "windowManager/win32/win32Window.h" +#include "gfx/screenshot.h" +#include "gfx/D3D9/screenshotD3D9.h" +#include "gfx/D3D9/videoCaptureD3D9.h" +#include "core/util/journal/process.h" + + +bool GFXPCD3D9Device::mEnableNVPerfHUD = false; + +GFXAdapter::CreateDeviceInstanceDelegate GFXPCD3D9Device::mCreateDeviceInstance(GFXPCD3D9Device::createInstance); + +GFXPCD3D9Device::~GFXPCD3D9Device() +{ + if( mVideoFrameGrabber ) + { + if( ManagedSingleton< VideoCapture >::instanceOrNull() ) + VIDCAP->setFrameGrabber( NULL ); + delete mVideoFrameGrabber; + } +} + +void GFXPCD3D9Device::createDirect3D9(LPDIRECT3D9 &d3d9, LPDIRECT3D9EX &d3d9ex) +{ + d3d9 = NULL; + d3d9ex = NULL; + + if ( !Con::getBoolVariable( "$pref::Video::preferDirect3D9Ex", false ) ) + { + d3d9 = Direct3DCreate9(D3D_SDK_VERSION); + return; + } + + HMODULE hD3D = LoadLibrary(TEXT("d3d9.dll")); + + if (hD3D) + { + + HRESULT (WINAPI *pfnCreate9Ex)(UINT SDKVersion, IDirect3D9Ex**) = (HRESULT ( WINAPI *)(UINT SDKVersion, IDirect3D9Ex**)) GetProcAddress(hD3D, "Direct3DCreate9Ex"); + + if (pfnCreate9Ex) + { + if (!FAILED(pfnCreate9Ex(D3D_SDK_VERSION, &d3d9ex)) && d3d9ex) + d3d9ex->QueryInterface(__uuidof(IDirect3D9), reinterpret_cast(&d3d9)); + } + + if (!pfnCreate9Ex || !d3d9) + { + if (d3d9ex) + { + SAFE_RELEASE(d3d9ex) + d3d9ex = NULL; + } + + d3d9 = Direct3DCreate9(D3D_SDK_VERSION); + } + + FreeLibrary(hD3D); + } +} + +GFXDevice *GFXPCD3D9Device::createInstance( U32 adapterIndex ) +{ + LPDIRECT3D9 d3d9; + LPDIRECT3D9EX d3d9ex; + + createDirect3D9(d3d9, d3d9ex); + + GFXPCD3D9Device* dev = new GFXPCD3D9Device( d3d9, adapterIndex ); + dev->mD3DEx = d3d9ex; + return dev; +} + +//----------------------------------------------------------------------------- + +GFXFormat GFXPCD3D9Device::selectSupportedFormat(GFXTextureProfile *profile, + const Vector &formats, bool texture, bool mustblend, bool mustfilter) +{ + DWORD usage = 0; + + if(profile->isDynamic()) + usage |= D3DUSAGE_DYNAMIC; + + if(profile->isRenderTarget()) + usage |= D3DUSAGE_RENDERTARGET; + + if(profile->isZTarget()) + usage |= D3DUSAGE_DEPTHSTENCIL; + + if(mustblend) + usage |= D3DUSAGE_QUERY_POSTPIXELSHADER_BLENDING; + + if(mustfilter) + usage |= D3DUSAGE_QUERY_FILTER; + + D3DDISPLAYMODE mode; + D3D9Assert(mD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &mode), "Unable to get adapter mode."); + + D3DRESOURCETYPE type; + if(texture) + type = D3DRTYPE_TEXTURE; + else + type = D3DRTYPE_SURFACE; + + for(U32 i=0; iCheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, mode.Format, + usage, type, GFXD3D9TextureFormat[formats[i]]) == D3D_OK) + return formats[i]; + } + + return GFXFormatR8G8B8A8; +} + +HRESULT GFXPCD3D9Device::createDevice(U32 adapter, D3DDEVTYPE deviceType, HWND hFocusWindow, DWORD behaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters) +{ + HRESULT hres = E_FAIL; + + if (mD3DEx) + { + hres = mD3DEx->CreateDeviceEx( adapter, deviceType, + hFocusWindow, + behaviorFlags, + pPresentationParameters, NULL, &mD3DDeviceEx ); + + if (!FAILED(hres) && mD3DDeviceEx) + hres = mD3DDeviceEx->QueryInterface(__uuidof(IDirect3DDevice9), reinterpret_cast(&mD3DDevice)); + } + else + { + hres = mD3D->CreateDevice( adapter, deviceType, + hFocusWindow, + behaviorFlags, + pPresentationParameters, &mD3DDevice ); + } + + return hres; + +} + +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Setup D3D present parameters - init helper function +//----------------------------------------------------------------------------- +D3DPRESENT_PARAMETERS GFXPCD3D9Device::setupPresentParams( const GFXVideoMode &mode, const HWND &hwnd ) const +{ + // Create D3D Presentation params + D3DPRESENT_PARAMETERS d3dpp; + dMemset( &d3dpp, 0, sizeof( d3dpp ) ); + + D3DFORMAT fmt = D3DFMT_X8R8G8B8; // 32 bit + + if( mode.bitDepth == 16 ) + fmt = D3DFMT_R5G6B5; + + D3DMULTISAMPLE_TYPE aatype; + DWORD aalevel; + + // Setup the AA flags... If we've been ask to + // disable hardware AA then do that now. + if ( mode.antialiasLevel == 0 || Con::getBoolVariable( "$pref::Video::disableHardwareAA", false ) ) + { + aatype = D3DMULTISAMPLE_NONE; + aalevel = 0; + } + else + { + aatype = D3DMULTISAMPLE_NONMASKABLE; + aalevel = mode.antialiasLevel-1; + } + + _validateMultisampleParams(fmt, aatype, aalevel); + + d3dpp.BackBufferWidth = mode.resolution.x; + d3dpp.BackBufferHeight = mode.resolution.y; + d3dpp.BackBufferFormat = fmt; + d3dpp.BackBufferCount = 1; + d3dpp.MultiSampleType = aatype; + d3dpp.MultiSampleQuality = aalevel; + d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; + d3dpp.hDeviceWindow = hwnd; + d3dpp.Windowed = !mode.fullScreen; + d3dpp.EnableAutoDepthStencil = TRUE; + d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8; + d3dpp.Flags = 0; + d3dpp.FullScreen_RefreshRateInHz = (mode.refreshRate == 0 || !mode.fullScreen) ? + D3DPRESENT_RATE_DEFAULT : mode.refreshRate; + d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE; + + if ( smDisableVSync ) + d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; // This does NOT wait for vsync + + return d3dpp; +} + +//----------------------------------------------------------------------------- +// Enumerate D3D adapters +//----------------------------------------------------------------------------- +void GFXPCD3D9Device::enumerateAdapters( Vector &adapterList ) +{ + // Grab a D3D9 handle here to first get the D3D9 devices + LPDIRECT3D9 d3d9; + LPDIRECT3D9EX d3d9ex; + createDirect3D9( d3d9, d3d9ex); + + // If we could not create the d3d9 object then either the system + // is corrupt or they need to update the directx runtime. + if ( !d3d9 ) + { + Con::errorf( "Unsupported DirectX version!" ); + Platform::messageBox( Con::getVariable( "$appName" ), + "DirectX could not be started!\r\n" + "Please be sure you have the latest version of DirectX installed.", + MBOk, MIStop ); + Platform::forceShutdown( -1 ); + } + + for( U32 adapterIndex = 0; adapterIndex < d3d9->GetAdapterCount(); adapterIndex++ ) + { + GFXAdapter *toAdd = new GFXAdapter; + toAdd->mType = Direct3D9; + toAdd->mIndex = adapterIndex; + toAdd->mCreateDeviceInstanceDelegate = mCreateDeviceInstance; + + // Grab the shader model. + D3DCAPS9 caps; + d3d9->GetDeviceCaps(adapterIndex, D3DDEVTYPE_HAL, &caps); + U8 *pxPtr = (U8*) &caps.PixelShaderVersion; + toAdd->mShaderModel = pxPtr[1] + pxPtr[0] * 0.1; + + // Get the device description string. + D3DADAPTER_IDENTIFIER9 temp; + d3d9->GetAdapterIdentifier( adapterIndex, NULL, &temp ); // The NULL is the flags which deal with WHQL + + dStrcpy( toAdd->mName, temp.Description ); + dStrncat(toAdd->mName, " (D3D9)", GFXAdapter::MaxAdapterNameLen); + + // Video mode enumeration. + Vector formats( __FILE__, __LINE__ ); + formats.push_back( D3DFMT_R5G6B5 ); // D3DFMT_R5G6B5 - 16bit format + formats.push_back( D3DFMT_X8R8G8B8 ); // D3DFMT_X8R8G8B8 - 32bit format + + for( S32 i = 0; i < formats.size(); i++ ) + { + DWORD MaxSampleQualities; + d3d9->CheckDeviceMultiSampleType(adapterIndex, D3DDEVTYPE_HAL, formats[i], FALSE, D3DMULTISAMPLE_NONMASKABLE, &MaxSampleQualities); + + for( U32 j = 0; j < d3d9->GetAdapterModeCount( adapterIndex, formats[i] ); j++ ) + { + D3DDISPLAYMODE mode; + d3d9->EnumAdapterModes( adapterIndex, formats[i], j, &mode ); + + GFXVideoMode vmAdd; + + vmAdd.bitDepth = ( i == 0 ? 16 : 32 ); // This will need to be changed later + vmAdd.fullScreen = true; + vmAdd.refreshRate = mode.RefreshRate; + vmAdd.resolution = Point2I( mode.Width, mode.Height ); + vmAdd.antialiasLevel = MaxSampleQualities; + + toAdd->mAvailableModes.push_back( vmAdd ); + } + } + + adapterList.push_back( toAdd ); + } + + d3d9->Release(); +} + +void GFXPCD3D9Device::enumerateVideoModes() +{ + Vector formats( __FILE__, __LINE__ ); + formats.push_back( D3DFMT_R5G6B5 ); // D3DFMT_R5G6B5 - 16bit format + formats.push_back( D3DFMT_X8R8G8B8 ); // D3DFMT_X8R8G8B8 - 32bit format + + for( S32 i = 0; i < formats.size(); i++ ) + { + for( U32 j = 0; j < mD3D->GetAdapterModeCount( D3DADAPTER_DEFAULT, formats[i] ); j++ ) + { + D3DDISPLAYMODE mode; + mD3D->EnumAdapterModes( D3DADAPTER_DEFAULT, formats[i], j, &mode ); + + GFXVideoMode toAdd; + + toAdd.bitDepth = ( i == 0 ? 16 : 32 ); // This will need to be changed later + toAdd.fullScreen = false; + toAdd.refreshRate = mode.RefreshRate; + toAdd.resolution = Point2I( mode.Width, mode.Height ); + + mVideoModes.push_back( toAdd ); + } + } +} + +//----------------------------------------------------------------------------- +// Initialize - create window, device, etc +//----------------------------------------------------------------------------- +void GFXPCD3D9Device::init( const GFXVideoMode &mode, PlatformWindow *window /* = NULL */ ) +{ + AssertFatal(window, "GFXPCD3D9Device::init - must specify a window!"); + + initD3DXFnTable(); + + Win32Window *win = dynamic_cast( window ); + AssertISV( win, "GFXD3D9Device::init - got a non Win32Window window passed in! Did DX go crossplatform?" ); + + HWND winHwnd = win->getHWND(); + + // Create D3D Presentation params + D3DPRESENT_PARAMETERS d3dpp = setupPresentParams( mode, winHwnd ); + mMultisampleType = d3dpp.MultiSampleType; + mMultisampleLevel = d3dpp.MultiSampleQuality; + +#ifndef TORQUE_SHIPPING + bool usePerfHud = GFXPCD3D9Device::mEnableNVPerfHUD || Con::getBoolVariable("$Video::useNVPerfHud", false); +#else + bool usePerfHud = false; +#endif + + HRESULT hres = E_FAIL; + if ( usePerfHud ) + { + hres = createDevice( mD3D->GetAdapterCount() - 1, D3DDEVTYPE_REF, winHwnd, D3DCREATE_MIXED_VERTEXPROCESSING, &d3dpp); + } + else + { + // Vertex processing was changed from MIXED to HARDWARE because of the switch to a pure D3D device. + + // Set up device flags from our compile flags. + U32 deviceFlags = 0; + deviceFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING; + + // Currently, offscreen rendering is only used by WPF apps and we need to create with D3DCREATE_MULTITHREAD for it + // In all other cases, you can do better by locking/creating resources in the primary thread + // and passing them to worker threads. + if (window->getOffscreenRender()) + { + deviceFlags |= D3DCREATE_MULTITHREADED; + d3dpp.Windowed = TRUE; + d3dpp.BackBufferHeight = 1; + d3dpp.BackBufferWidth = 1; + d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; + d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8; + d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; + } + + // DirectX will switch the floating poing control word to single precision + // and disable exceptions by default. There are a few issues with this... + // + // 1. It can cause rendering problems when running in WPF. + // 2. Firefox embedding issues. + // 3. Physics engines depend on the higher precision. + // + // Interestingly enough... DirectX 10 and 11 do not modifiy the floating point + // settings and are always in full precision. + // + // The down side is we supposedly loose some performance, but so far i've not + // seen a measurable impact. + // + deviceFlags |= D3DCREATE_FPU_PRESERVE; + + // Try to do pure, unless we're doing debug (and thus doing more paranoid checking). +#ifndef TORQUE_DEBUG_RENDER + deviceFlags |= D3DCREATE_PUREDEVICE; +#endif + + hres = createDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, winHwnd, deviceFlags, &d3dpp); + + if (FAILED(hres) && hres != D3DERR_OUTOFVIDEOMEMORY) + { + Con::errorf(" Failed to create hardware device, trying mixed device"); + // turn off pure + deviceFlags &= (~D3DCREATE_PUREDEVICE); + + // try mixed mode + deviceFlags &= (~D3DCREATE_HARDWARE_VERTEXPROCESSING); + deviceFlags |= D3DCREATE_MIXED_VERTEXPROCESSING; + hres = createDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, + winHwnd, deviceFlags, + &d3dpp); + + // try software + if (FAILED(hres) && hres != D3DERR_OUTOFVIDEOMEMORY) + { + Con::errorf(" Failed to create mixed mode device, trying software device"); + deviceFlags &= (~D3DCREATE_MIXED_VERTEXPROCESSING); + deviceFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING; + hres = createDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, + winHwnd, deviceFlags, + &d3dpp); + + if (FAILED(hres) && hres != D3DERR_OUTOFVIDEOMEMORY) + Con::errorf(" Failed to create software device, giving up"); + D3D9Assert(hres, "GFXPCD3D9Device::init - CreateDevice failed!"); + } + } + } + + // Gracefully die if they can't give us a device. + if(!mD3DDevice) + { + if (hres == D3DERR_OUTOFVIDEOMEMORY) + { + char errorMsg[4096]; + dSprintf(errorMsg, sizeof(errorMsg), + "Out of video memory. Close other windows, reboot, and/or upgrade your video card drivers. Your video card is: %s", getAdapter().getName()); + Platform::AlertOK("DirectX Error", errorMsg); + } + else + { + Platform::AlertOK("DirectX Error!", "Failed to initialize Direct3D! Make sure you have DirectX 9 installed, and " + "are running a graphics card that supports Pixel Shader 1.1."); + } + Platform::forceShutdown(1); + } + + // Check up on things + Con::printf(" Cur. D3DDevice ref count=%d", mD3DDevice->AddRef() - 1); + mD3DDevice->Release(); + + mTextureManager = new GFXD3D9TextureManager( mD3DDevice ); + + // Now reacquire all the resources we trashed earlier + reacquireDefaultPoolResources(); + + // Setup default states + initStates(); + + //-------- Output init info --------- + D3DCAPS9 caps; + mD3DDevice->GetDeviceCaps( &caps ); + + U8 *pxPtr = (U8*) &caps.PixelShaderVersion; + mPixVersion = pxPtr[1] + pxPtr[0] * 0.1; + if (mPixVersion >= 2.0f && mPixVersion < 3.0f && caps.PS20Caps.NumTemps >= 32) + mPixVersion += 0.2f; + else if (mPixVersion >= 2.0f && mPixVersion < 3.0f && caps.PS20Caps.NumTemps >= 22) + mPixVersion += 0.1f; + Con::printf( " Pix version detected: %f", mPixVersion ); + + if ( smForcedPixVersion >= 0.0f && smForcedPixVersion < mPixVersion ) + { + mPixVersion = smForcedPixVersion; + Con::errorf( " Forced pix version: %f", mPixVersion ); + } + + U8 *vertPtr = (U8*) &caps.VertexShaderVersion; + F32 vertVersion = vertPtr[1] + vertPtr[0] * 0.1; + Con::printf( " Vert version detected: %f", vertVersion ); + + // The sampler count is based on the shader model and + // not found in the caps. + // + // MaxSimultaneousTextures is only valid for fixed + // function rendering. + // + if ( mPixVersion >= 2.0f ) + mNumSamplers = 16; + else if ( mPixVersion >= 1.4f ) + mNumSamplers = 6; + else if ( mPixVersion > 0.0f ) + mNumSamplers = 4; + else + mNumSamplers = caps.MaxSimultaneousTextures; + + // This shouldn't happen until SM5 or some other + // radical change in GPU hardware occurs. + AssertFatal( mNumSamplers <= TEXTURE_STAGE_COUNT, + "GFXPCD3D9Device::init - Sampler count greater than TEXTURE_STAGE_COUNT!" ); + + Con::printf( " Maximum number of simultaneous samplers: %d", mNumSamplers ); + + // detect max number of simultaneous render targets + mNumRenderTargets = caps.NumSimultaneousRTs; + Con::printf( " Number of simultaneous render targets: %d", mNumRenderTargets ); + + // detect occlusion query support + if (SUCCEEDED(mD3DDevice->CreateQuery( D3DQUERYTYPE_OCCLUSION, NULL ))) + mOcclusionQuerySupported = true; + + Con::printf( " Hardware occlusion query detected: %s", mOcclusionQuerySupported ? "Yes" : "No" ); + + Con::printf( " Using Direct3D9Ex: %s", isD3D9Ex() ? "Yes" : "No" ); + + mCardProfiler = new GFXD3D9CardProfiler(); + mCardProfiler->init(); + + gScreenShot = new ScreenShotD3D; + + // Set the video capture frame grabber. + mVideoFrameGrabber = new VideoFrameGrabberD3D9(); + VIDCAP->setFrameGrabber( mVideoFrameGrabber ); + + // Grab the depth-stencil... + SAFE_RELEASE(mDeviceDepthStencil); + D3D9Assert(mD3DDevice->GetDepthStencilSurface(&mDeviceDepthStencil), "GFXD3D9Device::init - couldn't grab reference to device's depth-stencil surface."); + + mInitialized = true; + + deviceInited(); + + // Uncomment to dump out code needed in initStates, you may also need to enable the reference device (get rid of code in initStates first as well) + // regenStates(); +} + +//------------------------------------------------------------------------------ +void GFXPCD3D9Device::enterDebugEvent(ColorI color, const char *name) +{ + // BJGFIX + WCHAR eventName[260]; + MultiByteToWideChar( CP_ACP, 0, name, -1, eventName, 260 ); + + D3DPERF_BeginEvent(D3DCOLOR_ARGB(color.alpha, color.red, color.green, color.blue), + (LPCWSTR)&eventName); +} + +//------------------------------------------------------------------------------ +void GFXPCD3D9Device::leaveDebugEvent() +{ + D3DPERF_EndEvent(); +} + +//------------------------------------------------------------------------------ +void GFXPCD3D9Device::setDebugMarker(ColorI color, const char *name) +{ + // BJGFIX + WCHAR eventName[260]; + MultiByteToWideChar( CP_ACP, 0, name, -1, eventName, 260 ); + + D3DPERF_SetMarker(D3DCOLOR_ARGB(color.alpha, color.red, color.green, color.blue), + (LPCWSTR)&eventName); +} + +//----------------------------------------------------------------------------- + +void GFXPCD3D9Device::setMatrix( GFXMatrixType mtype, const MatrixF &mat ) +{ + mat.transposeTo( mTempMatrix ); + + mD3DDevice->SetTransform( (_D3DTRANSFORMSTATETYPE)mtype, (D3DMATRIX *)&mTempMatrix ); +} + +//----------------------------------------------------------------------------- + +void GFXPCD3D9Device::_setTextureStageState( U32 stage, U32 state, U32 value ) +{ + switch( state ) + { + case GFXTSSColorOp: + case GFXTSSAlphaOp: + mD3DDevice->SetTextureStageState( stage, GFXD3D9TextureStageState[state], GFXD3D9TextureOp[value] ); + break; + + default: + mD3DDevice->SetTextureStageState( stage, GFXD3D9TextureStageState[state], value ); + break; + } +} + +//------------------------------------------------------------------------------ + +void GFXPCD3D9Device::initStates() +{ + //------------------------------------- + // Auto-generated default states, see regenStates() for details + // + + // Render states + mD3DDevice->SetRenderState( GFXD3D9RenderState[0], 1 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[1], 3 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[2], 2 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[3], 1 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[4], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[5], 1 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[6], 2 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[7], 1 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[8], 3 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[9], 4 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[10], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[11], 8 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[12], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[13], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[14], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[15], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[16], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[17], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[18], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[19], 1065353216 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[20], 1065353216 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[21], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[22], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[23], 1 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[24], 1 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[25], 1 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[26], 8 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[27], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[28], -1 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[29], -1 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[30], -1 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[31], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[32], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[33], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[34], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[35], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[36], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[37], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[38], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[39], 1 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[40], 1 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[41], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[42], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[43], 1 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[44], 1 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[45], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[46], 1 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[47], 2 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[48], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[49], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[50], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[51], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[52], 1065353216 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[53], 1065353216 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[54], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[55], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[56], 1065353216 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[57], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[58], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[59], 1 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[60], -1 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[61], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[62], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[63], 1115684864 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[64], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[65], 15 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[66], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[67], 1 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[68], 3 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[69], 1 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[70], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[71], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[72], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[73], 1065353216 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[74], 1065353216 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[75], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[76], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[77], 1065353216 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[78], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[79], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[80], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[81], 1 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[82], 1 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[83], 1 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[84], 8 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[85], 15 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[86], 15 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[87], 15 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[88], -1 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[89], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[90], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[91], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[92], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[93], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[94], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[95], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[96], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[97], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[98], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[99], 0 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[100], 2 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[101], 1 ); + mD3DDevice->SetRenderState( GFXD3D9RenderState[102], 1 ); + + // Texture Stage states + mD3DDevice->SetTextureStageState( 0, GFXD3D9TextureStageState[0], 4 ); + mD3DDevice->SetTextureStageState( 0, GFXD3D9TextureStageState[1], 2 ); + mD3DDevice->SetTextureStageState( 0, GFXD3D9TextureStageState[2], 1 ); + mD3DDevice->SetTextureStageState( 0, GFXD3D9TextureStageState[3], 2 ); + mD3DDevice->SetTextureStageState( 0, GFXD3D9TextureStageState[4], 2 ); + mD3DDevice->SetTextureStageState( 0, GFXD3D9TextureStageState[5], 1 ); + mD3DDevice->SetTextureStageState( 0, GFXD3D9TextureStageState[6], 0 ); + mD3DDevice->SetTextureStageState( 0, GFXD3D9TextureStageState[7], 0 ); + mD3DDevice->SetTextureStageState( 0, GFXD3D9TextureStageState[8], 0 ); + mD3DDevice->SetTextureStageState( 0, GFXD3D9TextureStageState[9], 0 ); + mD3DDevice->SetTextureStageState( 0, GFXD3D9TextureStageState[10], 0 ); + mD3DDevice->SetTextureStageState( 0, GFXD3D9TextureStageState[11], 0 ); + mD3DDevice->SetTextureStageState( 0, GFXD3D9TextureStageState[12], 0 ); + mD3DDevice->SetTextureStageState( 0, GFXD3D9TextureStageState[13], 0 ); + mD3DDevice->SetTextureStageState( 0, GFXD3D9TextureStageState[14], 1 ); + mD3DDevice->SetTextureStageState( 0, GFXD3D9TextureStageState[15], 1 ); + mD3DDevice->SetTextureStageState( 0, GFXD3D9TextureStageState[16], 1 ); + mD3DDevice->SetTextureStageState( 0, GFXD3D9TextureStageState[17], 0 ); + mD3DDevice->SetTextureStageState( 1, GFXD3D9TextureStageState[0], 1 ); + mD3DDevice->SetTextureStageState( 1, GFXD3D9TextureStageState[1], 2 ); + mD3DDevice->SetTextureStageState( 1, GFXD3D9TextureStageState[2], 1 ); + mD3DDevice->SetTextureStageState( 1, GFXD3D9TextureStageState[3], 1 ); + mD3DDevice->SetTextureStageState( 1, GFXD3D9TextureStageState[4], 2 ); + mD3DDevice->SetTextureStageState( 1, GFXD3D9TextureStageState[5], 1 ); + mD3DDevice->SetTextureStageState( 1, GFXD3D9TextureStageState[6], 0 ); + mD3DDevice->SetTextureStageState( 1, GFXD3D9TextureStageState[7], 0 ); + mD3DDevice->SetTextureStageState( 1, GFXD3D9TextureStageState[8], 0 ); + mD3DDevice->SetTextureStageState( 1, GFXD3D9TextureStageState[9], 0 ); + mD3DDevice->SetTextureStageState( 1, GFXD3D9TextureStageState[10], 1 ); + mD3DDevice->SetTextureStageState( 1, GFXD3D9TextureStageState[11], 0 ); + mD3DDevice->SetTextureStageState( 1, GFXD3D9TextureStageState[12], 0 ); + mD3DDevice->SetTextureStageState( 1, GFXD3D9TextureStageState[13], 0 ); + mD3DDevice->SetTextureStageState( 1, GFXD3D9TextureStageState[14], 1 ); + mD3DDevice->SetTextureStageState( 1, GFXD3D9TextureStageState[15], 1 ); + mD3DDevice->SetTextureStageState( 1, GFXD3D9TextureStageState[16], 1 ); + mD3DDevice->SetTextureStageState( 1, GFXD3D9TextureStageState[17], 0 ); + mD3DDevice->SetTextureStageState( 2, GFXD3D9TextureStageState[0], 1 ); + mD3DDevice->SetTextureStageState( 2, GFXD3D9TextureStageState[1], 2 ); + mD3DDevice->SetTextureStageState( 2, GFXD3D9TextureStageState[2], 1 ); + mD3DDevice->SetTextureStageState( 2, GFXD3D9TextureStageState[3], 1 ); + mD3DDevice->SetTextureStageState( 2, GFXD3D9TextureStageState[4], 2 ); + mD3DDevice->SetTextureStageState( 2, GFXD3D9TextureStageState[5], 1 ); + mD3DDevice->SetTextureStageState( 2, GFXD3D9TextureStageState[6], 0 ); + mD3DDevice->SetTextureStageState( 2, GFXD3D9TextureStageState[7], 0 ); + mD3DDevice->SetTextureStageState( 2, GFXD3D9TextureStageState[8], 0 ); + mD3DDevice->SetTextureStageState( 2, GFXD3D9TextureStageState[9], 0 ); + mD3DDevice->SetTextureStageState( 2, GFXD3D9TextureStageState[10], 2 ); + mD3DDevice->SetTextureStageState( 2, GFXD3D9TextureStageState[11], 0 ); + mD3DDevice->SetTextureStageState( 2, GFXD3D9TextureStageState[12], 0 ); + mD3DDevice->SetTextureStageState( 2, GFXD3D9TextureStageState[13], 0 ); + mD3DDevice->SetTextureStageState( 2, GFXD3D9TextureStageState[14], 1 ); + mD3DDevice->SetTextureStageState( 2, GFXD3D9TextureStageState[15], 1 ); + mD3DDevice->SetTextureStageState( 2, GFXD3D9TextureStageState[16], 1 ); + mD3DDevice->SetTextureStageState( 2, GFXD3D9TextureStageState[17], 0 ); + mD3DDevice->SetTextureStageState( 3, GFXD3D9TextureStageState[0], 1 ); + mD3DDevice->SetTextureStageState( 3, GFXD3D9TextureStageState[1], 2 ); + mD3DDevice->SetTextureStageState( 3, GFXD3D9TextureStageState[2], 1 ); + mD3DDevice->SetTextureStageState( 3, GFXD3D9TextureStageState[3], 1 ); + mD3DDevice->SetTextureStageState( 3, GFXD3D9TextureStageState[4], 2 ); + mD3DDevice->SetTextureStageState( 3, GFXD3D9TextureStageState[5], 1 ); + mD3DDevice->SetTextureStageState( 3, GFXD3D9TextureStageState[6], 0 ); + mD3DDevice->SetTextureStageState( 3, GFXD3D9TextureStageState[7], 0 ); + mD3DDevice->SetTextureStageState( 3, GFXD3D9TextureStageState[8], 0 ); + mD3DDevice->SetTextureStageState( 3, GFXD3D9TextureStageState[9], 0 ); + mD3DDevice->SetTextureStageState( 3, GFXD3D9TextureStageState[10], 3 ); + mD3DDevice->SetTextureStageState( 3, GFXD3D9TextureStageState[11], 0 ); + mD3DDevice->SetTextureStageState( 3, GFXD3D9TextureStageState[12], 0 ); + mD3DDevice->SetTextureStageState( 3, GFXD3D9TextureStageState[13], 0 ); + mD3DDevice->SetTextureStageState( 3, GFXD3D9TextureStageState[14], 1 ); + mD3DDevice->SetTextureStageState( 3, GFXD3D9TextureStageState[15], 1 ); + mD3DDevice->SetTextureStageState( 3, GFXD3D9TextureStageState[16], 1 ); + mD3DDevice->SetTextureStageState( 3, GFXD3D9TextureStageState[17], 0 ); + mD3DDevice->SetTextureStageState( 4, GFXD3D9TextureStageState[0], 1 ); + mD3DDevice->SetTextureStageState( 4, GFXD3D9TextureStageState[1], 2 ); + mD3DDevice->SetTextureStageState( 4, GFXD3D9TextureStageState[2], 1 ); + mD3DDevice->SetTextureStageState( 4, GFXD3D9TextureStageState[3], 1 ); + mD3DDevice->SetTextureStageState( 4, GFXD3D9TextureStageState[4], 2 ); + mD3DDevice->SetTextureStageState( 4, GFXD3D9TextureStageState[5], 1 ); + mD3DDevice->SetTextureStageState( 4, GFXD3D9TextureStageState[6], 0 ); + mD3DDevice->SetTextureStageState( 4, GFXD3D9TextureStageState[7], 0 ); + mD3DDevice->SetTextureStageState( 4, GFXD3D9TextureStageState[8], 0 ); + mD3DDevice->SetTextureStageState( 4, GFXD3D9TextureStageState[9], 0 ); + mD3DDevice->SetTextureStageState( 4, GFXD3D9TextureStageState[10], 4 ); + mD3DDevice->SetTextureStageState( 4, GFXD3D9TextureStageState[11], 0 ); + mD3DDevice->SetTextureStageState( 4, GFXD3D9TextureStageState[12], 0 ); + mD3DDevice->SetTextureStageState( 4, GFXD3D9TextureStageState[13], 0 ); + mD3DDevice->SetTextureStageState( 4, GFXD3D9TextureStageState[14], 1 ); + mD3DDevice->SetTextureStageState( 4, GFXD3D9TextureStageState[15], 1 ); + mD3DDevice->SetTextureStageState( 4, GFXD3D9TextureStageState[16], 1 ); + mD3DDevice->SetTextureStageState( 4, GFXD3D9TextureStageState[17], 0 ); + mD3DDevice->SetTextureStageState( 5, GFXD3D9TextureStageState[0], 1 ); + mD3DDevice->SetTextureStageState( 5, GFXD3D9TextureStageState[1], 2 ); + mD3DDevice->SetTextureStageState( 5, GFXD3D9TextureStageState[2], 1 ); + mD3DDevice->SetTextureStageState( 5, GFXD3D9TextureStageState[3], 1 ); + mD3DDevice->SetTextureStageState( 5, GFXD3D9TextureStageState[4], 2 ); + mD3DDevice->SetTextureStageState( 5, GFXD3D9TextureStageState[5], 1 ); + mD3DDevice->SetTextureStageState( 5, GFXD3D9TextureStageState[6], 0 ); + mD3DDevice->SetTextureStageState( 5, GFXD3D9TextureStageState[7], 0 ); + mD3DDevice->SetTextureStageState( 5, GFXD3D9TextureStageState[8], 0 ); + mD3DDevice->SetTextureStageState( 5, GFXD3D9TextureStageState[9], 0 ); + mD3DDevice->SetTextureStageState( 5, GFXD3D9TextureStageState[10], 5 ); + mD3DDevice->SetTextureStageState( 5, GFXD3D9TextureStageState[11], 0 ); + mD3DDevice->SetTextureStageState( 5, GFXD3D9TextureStageState[12], 0 ); + mD3DDevice->SetTextureStageState( 5, GFXD3D9TextureStageState[13], 0 ); + mD3DDevice->SetTextureStageState( 5, GFXD3D9TextureStageState[14], 1 ); + mD3DDevice->SetTextureStageState( 5, GFXD3D9TextureStageState[15], 1 ); + mD3DDevice->SetTextureStageState( 5, GFXD3D9TextureStageState[16], 1 ); + mD3DDevice->SetTextureStageState( 5, GFXD3D9TextureStageState[17], 0 ); + mD3DDevice->SetTextureStageState( 6, GFXD3D9TextureStageState[0], 1 ); + mD3DDevice->SetTextureStageState( 6, GFXD3D9TextureStageState[1], 2 ); + mD3DDevice->SetTextureStageState( 6, GFXD3D9TextureStageState[2], 1 ); + mD3DDevice->SetTextureStageState( 6, GFXD3D9TextureStageState[3], 1 ); + mD3DDevice->SetTextureStageState( 6, GFXD3D9TextureStageState[4], 2 ); + mD3DDevice->SetTextureStageState( 6, GFXD3D9TextureStageState[5], 1 ); + mD3DDevice->SetTextureStageState( 6, GFXD3D9TextureStageState[6], 0 ); + mD3DDevice->SetTextureStageState( 6, GFXD3D9TextureStageState[7], 0 ); + mD3DDevice->SetTextureStageState( 6, GFXD3D9TextureStageState[8], 0 ); + mD3DDevice->SetTextureStageState( 6, GFXD3D9TextureStageState[9], 0 ); + mD3DDevice->SetTextureStageState( 6, GFXD3D9TextureStageState[10], 6 ); + mD3DDevice->SetTextureStageState( 6, GFXD3D9TextureStageState[11], 0 ); + mD3DDevice->SetTextureStageState( 6, GFXD3D9TextureStageState[12], 0 ); + mD3DDevice->SetTextureStageState( 6, GFXD3D9TextureStageState[13], 0 ); + mD3DDevice->SetTextureStageState( 6, GFXD3D9TextureStageState[14], 1 ); + mD3DDevice->SetTextureStageState( 6, GFXD3D9TextureStageState[15], 1 ); + mD3DDevice->SetTextureStageState( 6, GFXD3D9TextureStageState[16], 1 ); + mD3DDevice->SetTextureStageState( 6, GFXD3D9TextureStageState[17], 0 ); + mD3DDevice->SetTextureStageState( 7, GFXD3D9TextureStageState[0], 1 ); + mD3DDevice->SetTextureStageState( 7, GFXD3D9TextureStageState[1], 2 ); + mD3DDevice->SetTextureStageState( 7, GFXD3D9TextureStageState[2], 1 ); + mD3DDevice->SetTextureStageState( 7, GFXD3D9TextureStageState[3], 1 ); + mD3DDevice->SetTextureStageState( 7, GFXD3D9TextureStageState[4], 2 ); + mD3DDevice->SetTextureStageState( 7, GFXD3D9TextureStageState[5], 1 ); + mD3DDevice->SetTextureStageState( 7, GFXD3D9TextureStageState[6], 0 ); + mD3DDevice->SetTextureStageState( 7, GFXD3D9TextureStageState[7], 0 ); + mD3DDevice->SetTextureStageState( 7, GFXD3D9TextureStageState[8], 0 ); + mD3DDevice->SetTextureStageState( 7, GFXD3D9TextureStageState[9], 0 ); + mD3DDevice->SetTextureStageState( 7, GFXD3D9TextureStageState[10], 7 ); + mD3DDevice->SetTextureStageState( 7, GFXD3D9TextureStageState[11], 0 ); + mD3DDevice->SetTextureStageState( 7, GFXD3D9TextureStageState[12], 0 ); + mD3DDevice->SetTextureStageState( 7, GFXD3D9TextureStageState[13], 0 ); + mD3DDevice->SetTextureStageState( 7, GFXD3D9TextureStageState[14], 1 ); + mD3DDevice->SetTextureStageState( 7, GFXD3D9TextureStageState[15], 1 ); + mD3DDevice->SetTextureStageState( 7, GFXD3D9TextureStageState[16], 1 ); + mD3DDevice->SetTextureStageState( 7, GFXD3D9TextureStageState[17], 0 ); + + // Sampler states + mD3DDevice->SetSamplerState( 0, GFXD3D9SamplerState[0], 1 ); + mD3DDevice->SetSamplerState( 0, GFXD3D9SamplerState[1], 1 ); + mD3DDevice->SetSamplerState( 0, GFXD3D9SamplerState[2], 1 ); + mD3DDevice->SetSamplerState( 0, GFXD3D9SamplerState[3], 0 ); + mD3DDevice->SetSamplerState( 0, GFXD3D9SamplerState[4], 1 ); + mD3DDevice->SetSamplerState( 0, GFXD3D9SamplerState[5], 1 ); + mD3DDevice->SetSamplerState( 0, GFXD3D9SamplerState[6], 0 ); + mD3DDevice->SetSamplerState( 0, GFXD3D9SamplerState[7], 0 ); + mD3DDevice->SetSamplerState( 0, GFXD3D9SamplerState[8], 0 ); + mD3DDevice->SetSamplerState( 0, GFXD3D9SamplerState[9], 1 ); + mD3DDevice->SetSamplerState( 0, GFXD3D9SamplerState[10], 0 ); + mD3DDevice->SetSamplerState( 0, GFXD3D9SamplerState[11], 0 ); + mD3DDevice->SetSamplerState( 0, GFXD3D9SamplerState[12], 0 ); + mD3DDevice->SetSamplerState( 1, GFXD3D9SamplerState[0], 1 ); + mD3DDevice->SetSamplerState( 1, GFXD3D9SamplerState[1], 1 ); + mD3DDevice->SetSamplerState( 1, GFXD3D9SamplerState[2], 1 ); + mD3DDevice->SetSamplerState( 1, GFXD3D9SamplerState[3], 0 ); + mD3DDevice->SetSamplerState( 1, GFXD3D9SamplerState[4], 1 ); + mD3DDevice->SetSamplerState( 1, GFXD3D9SamplerState[5], 1 ); + mD3DDevice->SetSamplerState( 1, GFXD3D9SamplerState[6], 0 ); + mD3DDevice->SetSamplerState( 1, GFXD3D9SamplerState[7], 0 ); + mD3DDevice->SetSamplerState( 1, GFXD3D9SamplerState[8], 0 ); + mD3DDevice->SetSamplerState( 1, GFXD3D9SamplerState[9], 1 ); + mD3DDevice->SetSamplerState( 1, GFXD3D9SamplerState[10], 0 ); + mD3DDevice->SetSamplerState( 1, GFXD3D9SamplerState[11], 0 ); + mD3DDevice->SetSamplerState( 1, GFXD3D9SamplerState[12], 0 ); + mD3DDevice->SetSamplerState( 2, GFXD3D9SamplerState[0], 1 ); + mD3DDevice->SetSamplerState( 2, GFXD3D9SamplerState[1], 1 ); + mD3DDevice->SetSamplerState( 2, GFXD3D9SamplerState[2], 1 ); + mD3DDevice->SetSamplerState( 2, GFXD3D9SamplerState[3], 0 ); + mD3DDevice->SetSamplerState( 2, GFXD3D9SamplerState[4], 1 ); + mD3DDevice->SetSamplerState( 2, GFXD3D9SamplerState[5], 1 ); + mD3DDevice->SetSamplerState( 2, GFXD3D9SamplerState[6], 0 ); + mD3DDevice->SetSamplerState( 2, GFXD3D9SamplerState[7], 0 ); + mD3DDevice->SetSamplerState( 2, GFXD3D9SamplerState[8], 0 ); + mD3DDevice->SetSamplerState( 2, GFXD3D9SamplerState[9], 1 ); + mD3DDevice->SetSamplerState( 2, GFXD3D9SamplerState[10], 0 ); + mD3DDevice->SetSamplerState( 2, GFXD3D9SamplerState[11], 0 ); + mD3DDevice->SetSamplerState( 2, GFXD3D9SamplerState[12], 0 ); + mD3DDevice->SetSamplerState( 3, GFXD3D9SamplerState[0], 1 ); + mD3DDevice->SetSamplerState( 3, GFXD3D9SamplerState[1], 1 ); + mD3DDevice->SetSamplerState( 3, GFXD3D9SamplerState[2], 1 ); + mD3DDevice->SetSamplerState( 3, GFXD3D9SamplerState[3], 0 ); + mD3DDevice->SetSamplerState( 3, GFXD3D9SamplerState[4], 1 ); + mD3DDevice->SetSamplerState( 3, GFXD3D9SamplerState[5], 1 ); + mD3DDevice->SetSamplerState( 3, GFXD3D9SamplerState[6], 0 ); + mD3DDevice->SetSamplerState( 3, GFXD3D9SamplerState[7], 0 ); + mD3DDevice->SetSamplerState( 3, GFXD3D9SamplerState[8], 0 ); + mD3DDevice->SetSamplerState( 3, GFXD3D9SamplerState[9], 1 ); + mD3DDevice->SetSamplerState( 3, GFXD3D9SamplerState[10], 0 ); + mD3DDevice->SetSamplerState( 3, GFXD3D9SamplerState[11], 0 ); + mD3DDevice->SetSamplerState( 3, GFXD3D9SamplerState[12], 0 ); + mD3DDevice->SetSamplerState( 4, GFXD3D9SamplerState[0], 1 ); + mD3DDevice->SetSamplerState( 4, GFXD3D9SamplerState[1], 1 ); + mD3DDevice->SetSamplerState( 4, GFXD3D9SamplerState[2], 1 ); + mD3DDevice->SetSamplerState( 4, GFXD3D9SamplerState[3], 0 ); + mD3DDevice->SetSamplerState( 4, GFXD3D9SamplerState[4], 1 ); + mD3DDevice->SetSamplerState( 4, GFXD3D9SamplerState[5], 1 ); + mD3DDevice->SetSamplerState( 4, GFXD3D9SamplerState[6], 0 ); + mD3DDevice->SetSamplerState( 4, GFXD3D9SamplerState[7], 0 ); + mD3DDevice->SetSamplerState( 4, GFXD3D9SamplerState[8], 0 ); + mD3DDevice->SetSamplerState( 4, GFXD3D9SamplerState[9], 1 ); + mD3DDevice->SetSamplerState( 4, GFXD3D9SamplerState[10], 0 ); + mD3DDevice->SetSamplerState( 4, GFXD3D9SamplerState[11], 0 ); + mD3DDevice->SetSamplerState( 4, GFXD3D9SamplerState[12], 0 ); + mD3DDevice->SetSamplerState( 5, GFXD3D9SamplerState[0], 1 ); + mD3DDevice->SetSamplerState( 5, GFXD3D9SamplerState[1], 1 ); + mD3DDevice->SetSamplerState( 5, GFXD3D9SamplerState[2], 1 ); + mD3DDevice->SetSamplerState( 5, GFXD3D9SamplerState[3], 0 ); + mD3DDevice->SetSamplerState( 5, GFXD3D9SamplerState[4], 1 ); + mD3DDevice->SetSamplerState( 5, GFXD3D9SamplerState[5], 1 ); + mD3DDevice->SetSamplerState( 5, GFXD3D9SamplerState[6], 0 ); + mD3DDevice->SetSamplerState( 5, GFXD3D9SamplerState[7], 0 ); + mD3DDevice->SetSamplerState( 5, GFXD3D9SamplerState[8], 0 ); + mD3DDevice->SetSamplerState( 5, GFXD3D9SamplerState[9], 1 ); + mD3DDevice->SetSamplerState( 5, GFXD3D9SamplerState[10], 0 ); + mD3DDevice->SetSamplerState( 5, GFXD3D9SamplerState[11], 0 ); + mD3DDevice->SetSamplerState( 5, GFXD3D9SamplerState[12], 0 ); + mD3DDevice->SetSamplerState( 6, GFXD3D9SamplerState[0], 1 ); + mD3DDevice->SetSamplerState( 6, GFXD3D9SamplerState[1], 1 ); + mD3DDevice->SetSamplerState( 6, GFXD3D9SamplerState[2], 1 ); + mD3DDevice->SetSamplerState( 6, GFXD3D9SamplerState[3], 0 ); + mD3DDevice->SetSamplerState( 6, GFXD3D9SamplerState[4], 1 ); + mD3DDevice->SetSamplerState( 6, GFXD3D9SamplerState[5], 1 ); + mD3DDevice->SetSamplerState( 6, GFXD3D9SamplerState[6], 0 ); + mD3DDevice->SetSamplerState( 6, GFXD3D9SamplerState[7], 0 ); + mD3DDevice->SetSamplerState( 6, GFXD3D9SamplerState[8], 0 ); + mD3DDevice->SetSamplerState( 6, GFXD3D9SamplerState[9], 1 ); + mD3DDevice->SetSamplerState( 6, GFXD3D9SamplerState[10], 0 ); + mD3DDevice->SetSamplerState( 6, GFXD3D9SamplerState[11], 0 ); + mD3DDevice->SetSamplerState( 6, GFXD3D9SamplerState[12], 0 ); + mD3DDevice->SetSamplerState( 7, GFXD3D9SamplerState[0], 1 ); + mD3DDevice->SetSamplerState( 7, GFXD3D9SamplerState[1], 1 ); + mD3DDevice->SetSamplerState( 7, GFXD3D9SamplerState[2], 1 ); + mD3DDevice->SetSamplerState( 7, GFXD3D9SamplerState[3], 0 ); + mD3DDevice->SetSamplerState( 7, GFXD3D9SamplerState[4], 1 ); + mD3DDevice->SetSamplerState( 7, GFXD3D9SamplerState[5], 1 ); + mD3DDevice->SetSamplerState( 7, GFXD3D9SamplerState[6], 0 ); + mD3DDevice->SetSamplerState( 7, GFXD3D9SamplerState[7], 0 ); + mD3DDevice->SetSamplerState( 7, GFXD3D9SamplerState[8], 0 ); + mD3DDevice->SetSamplerState( 7, GFXD3D9SamplerState[9], 1 ); + mD3DDevice->SetSamplerState( 7, GFXD3D9SamplerState[10], 0 ); + mD3DDevice->SetSamplerState( 7, GFXD3D9SamplerState[11], 0 ); + mD3DDevice->SetSamplerState( 7, GFXD3D9SamplerState[12], 0 ); +} + +void GFXPCD3D9Device::_validateMultisampleParams(D3DFORMAT format, D3DMULTISAMPLE_TYPE & aatype, DWORD & aalevel) const +{ + if (aatype != D3DMULTISAMPLE_NONE) + { + DWORD MaxSampleQualities; + mD3D->CheckDeviceMultiSampleType(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, format, FALSE, D3DMULTISAMPLE_NONMASKABLE, &MaxSampleQualities); + aatype = D3DMULTISAMPLE_NONMASKABLE; + aalevel = getMin((U32)aalevel, (U32)MaxSampleQualities-1); + } +} + +bool GFXPCD3D9Device::beginSceneInternal() +{ + // Make sure we have a device + HRESULT res = mD3DDevice->TestCooperativeLevel(); + + S32 attempts = 0; + const S32 MaxAttempts = 40; + const S32 SleepMsPerAttempt = 50; + while(res == D3DERR_DEVICELOST && attempts < MaxAttempts) + { + // Lost device! Just keep querying + res = mD3DDevice->TestCooperativeLevel(); + + Con::warnf("GFXPCD3D9Device::beginScene - Device needs to be reset, waiting on device..."); + + Sleep(SleepMsPerAttempt); + attempts++; + } + + if (attempts >= MaxAttempts && res == D3DERR_DEVICELOST) + { + Con::errorf("GFXPCD3D9Device::beginScene - Device lost and reset wait time exceeded, skipping reset (will retry later)"); + mCanCurrentlyRender = false; + return false; + } + + // Trigger a reset if we can't get a good result from TestCooperativeLevel. + if(res == D3DERR_DEVICENOTRESET) + { + Con::warnf("GFXPCD3D9Device::beginScene - Device needs to be reset, resetting device..."); + + // Reset the device! + GFXResource *walk = mResourceListHead; + while(walk) + { + // Find the window target with implicit flag set and reset the device with its presentation params. + if(GFXPCD3D9WindowTarget *gdwt = dynamic_cast(walk)) + { + if(gdwt->mImplicit) + { + reset(gdwt->mPresentationParams); + break; + } + } + + walk = walk->getNextResource(); + } + } + + // Call parent + return Parent::beginSceneInternal(); +} + +GFXWindowTarget * GFXPCD3D9Device::allocWindowTarget( PlatformWindow *window ) +{ + AssertFatal(window,"GFXD3D9Device::allocWindowTarget - no window provided!"); +#ifndef TORQUE_OS_XENON + AssertFatal(dynamic_cast(window), + "GFXD3D9Device::allocWindowTarget - only works with Win32Windows!"); +#endif + + // Set up a new window target... + GFXPCD3D9WindowTarget *gdwt = new GFXPCD3D9WindowTarget(); + gdwt->mWindow = window; + gdwt->mSize = window->getClientExtent(); + gdwt->mDevice = this; + gdwt->initPresentationParams(); + + // Now, we have to init & bind our device... we have basically two scenarios + // of which the first is: + if(mD3DDevice == NULL) + { + // Allocate the device. + init(window->getVideoMode(), window); + + // Cool, we have the device, grab back the depthstencil buffer as well + // as the swap chain. + gdwt->mImplicit = true; + gdwt->setImplicitSwapChain(); + } + else + { + // And the second case: + // Initialized device, create an additional swap chain. + gdwt->mImplicit = false; + gdwt->createAdditionalSwapChain(); + } + + gdwt->registerResourceWithDevice(this); + + return gdwt; +} + +GFXTextureTarget * GFXPCD3D9Device::allocRenderToTextureTarget() +{ + GFXPCD3D9TextureTarget *targ = new GFXPCD3D9TextureTarget(); + targ->mDevice = this; + + targ->registerResourceWithDevice(this); + + return targ; +} + +//----------------------------------------------------------------------------- +// Reset D3D device +//----------------------------------------------------------------------------- +void GFXPCD3D9Device::reset( D3DPRESENT_PARAMETERS &d3dpp ) +{ + if(!mD3DDevice) + return; + + mInitialized = false; + + mMultisampleType = d3dpp.MultiSampleType; + mMultisampleLevel = d3dpp.MultiSampleQuality; + _validateMultisampleParams(d3dpp.BackBufferFormat, mMultisampleType, mMultisampleLevel); + + // Clean up some commonly dangling state. This helps prevents issues with + // items that are destroyed by the texture manager callbacks and recreated + // later, but still left bound. + setVertexBuffer(NULL); + setPrimitiveBuffer(NULL); + for(S32 i=0; iAddRef()-1); + mDeviceDepthStencil->Release(); + } + + // First release all the stuff we allocated from D3DPOOL_DEFAULT + releaseDefaultPoolResources(); + + // reset device + Con::printf( "--- Resetting D3D Device ---" ); + HRESULT hres = S_OK; + hres = mD3DDevice->Reset( &d3dpp ); + + if( FAILED( hres ) ) + { + while( mD3DDevice->TestCooperativeLevel() == D3DERR_DEVICELOST ) + { + Sleep( 100 ); + } + + hres = mD3DDevice->Reset( &d3dpp ); + } + + D3D9Assert( hres, "GFXD3D9Device::reset - Failed to create D3D Device!" ); + mInitialized = true; + + // Setup default states + initStates(); + + // Now re aquire all the resources we trashed earlier + reacquireDefaultPoolResources(); + + // Mark everything dirty and flush to card, for sanity. + updateStates(true); +} + +// +// Register this device with GFXInit +// +class GFXPCD3D9RegisterDevice +{ +public: + GFXPCD3D9RegisterDevice() + { + GFXInit::getRegisterDeviceSignal().notify(&GFXPCD3D9Device::enumerateAdapters); + } +}; + +static GFXPCD3D9RegisterDevice pPCD3D9RegisterDevice; + +//----------------------------------------------------------------------------- +/// Parse command line arguments for window creation +//----------------------------------------------------------------------------- +static void sgPCD3D9DeviceHandleCommandLine( S32 argc, const char **argv ) +{ + for (U32 i = 1; i < argc; i++) + { + String s(argv[i]); + if (s.equal("-nvperfhud", String::NoCase)) + { + GFXPCD3D9Device::mEnableNVPerfHUD = true; + break; + } + } +} + +// Register the command line parsing hook +static ProcessRegisterCommandLine sgCommandLine( sgPCD3D9DeviceHandleCommandLine ); + +extern "C" HRESULT WINAPI D3D_GetBackBufferNoRef(IDirect3DSurface9 **ppSurface) +{ + HRESULT hr = S_OK; + + GFXD3D9Device *dev = static_cast(GFX); + + if (!dev) + { + *ppSurface = NULL; + return S_OK; + } + + *ppSurface = dev->getBackBuffer(); + + return hr; +} \ No newline at end of file diff --git a/Engine/source/gfx/D3D9/pc/gfxPCD3D9Device.h b/Engine/source/gfx/D3D9/pc/gfxPCD3D9Device.h new file mode 100644 index 000000000..e8270298f --- /dev/null +++ b/Engine/source/gfx/D3D9/pc/gfxPCD3D9Device.h @@ -0,0 +1,81 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFX_PC_D3D9DEVICE_H_ +#define _GFX_PC_D3D9DEVICE_H_ + +#include "gfx/D3D9/gfxD3D9Device.h" + +class PlatformWindow; +class VideoFrameGrabberD3D9; + + +class GFXPCD3D9Device : public GFXD3D9Device +{ + typedef GFXD3D9Device Parent; + +public: + // Set to true to force nvperfhud device creation + static bool mEnableNVPerfHUD; + + GFXPCD3D9Device( LPDIRECT3D9 d3d, U32 index ) + : GFXD3D9Device( d3d, index ), + mVideoFrameGrabber( NULL ) {}; + ~GFXPCD3D9Device(); + + static GFXDevice *createInstance( U32 adapterIndex ); + + virtual GFXFormat selectSupportedFormat(GFXTextureProfile *profile, + const Vector &formats, bool texture, bool mustblend, bool mustfilter); + + static void enumerateAdapters( Vector &adapterList ); + + virtual void enumerateVideoModes(); + + virtual GFXWindowTarget *allocWindowTarget(PlatformWindow *window); + virtual GFXTextureTarget *allocRenderToTextureTarget(); + virtual bool beginSceneInternal(); + + virtual void init( const GFXVideoMode &mode, PlatformWindow *window = NULL ); + + virtual void enterDebugEvent(ColorI color, const char *name); + virtual void leaveDebugEvent(); + virtual void setDebugMarker(ColorI color, const char *name); + + virtual void setMatrix( GFXMatrixType mtype, const MatrixF &mat ); + + virtual void initStates(); + virtual void reset( D3DPRESENT_PARAMETERS &d3dpp ); + virtual D3DPRESENT_PARAMETERS setupPresentParams( const GFXVideoMode &mode, const HWND &hwnd ) const; +protected: + + VideoFrameGrabberD3D9* mVideoFrameGrabber; + + static GFXAdapter::CreateDeviceInstanceDelegate mCreateDeviceInstance; + static void createDirect3D9(LPDIRECT3D9 &d3d9, LPDIRECT3D9EX &d3d9ex); + HRESULT createDevice(U32 adapter, D3DDEVTYPE deviceType, HWND hFocusWindow, DWORD behaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters); + + virtual void _setTextureStageState( U32 stage, U32 state, U32 value ); + void _validateMultisampleParams(D3DFORMAT format, D3DMULTISAMPLE_TYPE & aatype, DWORD & aalevel) const; +}; + +#endif \ No newline at end of file diff --git a/Engine/source/gfx/D3D9/pc/gfxPCD3D9Target.cpp b/Engine/source/gfx/D3D9/pc/gfxPCD3D9Target.cpp new file mode 100644 index 000000000..aa1f1fb3d --- /dev/null +++ b/Engine/source/gfx/D3D9/pc/gfxPCD3D9Target.cpp @@ -0,0 +1,587 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/D3D9/pc/gfxPCD3D9Target.h" + +#include "gfx/D3D9/gfxD3D9Device.h" +#include "gfx/D3D9/gfxD3D9TextureObject.h" +#include "gfx/D3D9/gfxD3D9Cubemap.h" +#include "gfx/D3D9/gfxD3D9EnumTranslate.h" +#include "gfx/D3D9/pc/gfxPCD3D9Device.h" +#include "gfx/gfxDebugEvent.h" +#include "windowManager/win32/win32Window.h" + + +GFXPCD3D9TextureTarget::GFXPCD3D9TextureTarget() + : mTargetSize( Point2I::Zero ), + mTargetFormat( GFXFormatR8G8B8A8 ) +{ + for(S32 i=0; idestroyD3DResource( mTargets[i] ); // SAFE_RELEASE + mTargets[i] = NULL; + } + else + SAFE_RELEASE( mTargets[i] ); + } + + zombify(); +} + +void GFXPCD3D9TextureTarget::attachTexture( RenderSlot slot, GFXTextureObject *tex, U32 mipLevel/*=0*/, U32 zOffset /*= 0*/ ) +{ + GFXDEBUGEVENT_SCOPE( GFXPCD3D9TextureTarget_attachTexture, ColorI::RED ); + + AssertFatal(slot < MaxRenderSlotId, "GFXPCD3D9TextureTarget::attachTexture - out of range slot."); + + // TODO: The way this is implemented... you can attach a texture + // object multiple times and it will release and reset it. + // + // We should rework this to detect when no change has occured + // and skip out early. + + // Mark state as dirty so device can know to update. + invalidateState(); + + // Release what we had, it's definitely going to change. + mDevice->destroyD3DResource( mTargets[slot] ); // SAFE_RELEASE + mTargets[slot] = NULL; + mResolveTargets[slot] = NULL; + + if(slot == Color0) + { + mTargetSize = Point2I::Zero; + mTargetFormat = GFXFormatR8G8B8A8; + } + + // Are we clearing? + if(!tex) + { + // Yup - just exit, it'll stay NULL. + return; + } + + + // Take care of default targets + if( tex == GFXTextureTarget::sDefaultDepthStencil ) + { + mTargets[slot] = mDevice->mDeviceDepthStencil; + mTargets[slot]->AddRef(); + } + else + { + // Cast the texture object to D3D... + AssertFatal(dynamic_cast(tex), + "GFXPCD3D9TextureTarget::attachTexture - invalid texture object."); + + GFXD3D9TextureObject *d3dto = static_cast(tex); + + // Grab the surface level. + if( slot == DepthStencil ) + { + mTargets[slot] = d3dto->getSurface(); + if ( mTargets[slot] ) + mTargets[slot]->AddRef(); + } + else + { + // getSurface will almost always return NULL. It will only return non-NULL + // if the surface that it needs to render to is different than the mip level + // in the actual texture. This will happen with MSAA. + if( d3dto->getSurface() == NULL ) + { + D3D9Assert(d3dto->get2DTex()->GetSurfaceLevel(mipLevel, &mTargets[slot]), + "GFXPCD3D9TextureTarget::attachTexture - could not get surface level for the passed texture!"); + } + else + { + mTargets[slot] = d3dto->getSurface(); + mTargets[slot]->AddRef(); + + // Only assign resolve target if d3dto has a surface to give us. + // + // That usually means there is an MSAA target involved, which is why + // the resolve is needed to get the data out of the target. + mResolveTargets[slot] = d3dto; + + if ( tex && slot == Color0 ) + { + mTargetSize.set( tex->getSize().x, tex->getSize().y ); + mTargetFormat = tex->getFormat(); + } + } + } + + // Update surface size + if(slot == Color0) + { + IDirect3DSurface9 *surface = mTargets[Color0]; + if ( surface ) + { + D3DSURFACE_DESC sd; + surface->GetDesc(&sd); + mTargetSize = Point2I(sd.Width, sd.Height); + + S32 format = sd.Format; + GFXREVERSE_LOOKUP( GFXD3D9TextureFormat, GFXFormat, format ); + mTargetFormat = (GFXFormat)format; + } + } + } +} + +void GFXPCD3D9TextureTarget::attachTexture( RenderSlot slot, GFXCubemap *tex, U32 face, U32 mipLevel/*=0*/ ) +{ + GFXDEBUGEVENT_SCOPE( GFXPCD3D9TextureTarget_attachTexture_Cubemap, ColorI::RED ); + + AssertFatal(slot < MaxRenderSlotId, "GFXPCD3D9TextureTarget::attachTexture - out of range slot."); + + // Mark state as dirty so device can know to update. + invalidateState(); + + // Release what we had, it's definitely going to change. + mDevice->destroyD3DResource( mTargets[slot] ); // SAFE_RELEASE + mTargets[slot] = NULL; + mResolveTargets[slot] = NULL; + + // Cast the texture object to D3D... + AssertFatal(!tex || dynamic_cast(tex), + "GFXD3DTextureTarget::attachTexture - invalid cubemap object."); + + GFXD3D9Cubemap *cube = static_cast(tex); + + if(slot == Color0) + { + mTargetSize = Point2I::Zero; + mTargetFormat = GFXFormatR8G8B8A8; + } + + // Are we clearing? + if(!tex) + { + // Yup - just exit, it'll stay NULL. + return; + } + + D3D9Assert(cube->mCubeTex->GetCubeMapSurface( (D3DCUBEMAP_FACES)face, mipLevel, &mTargets[slot] ), + "GFXD3DTextureTarget::attachTexture - could not get surface level for the passed texture!"); + + // Update surface size + if(slot == Color0) + { + IDirect3DSurface9 *surface = mTargets[Color0]; + if ( surface ) + { + D3DSURFACE_DESC sd; + surface->GetDesc(&sd); + mTargetSize = Point2I(sd.Width, sd.Height); + + S32 format = sd.Format; + GFXREVERSE_LOOKUP( GFXD3D9TextureFormat, GFXFormat, format ); + mTargetFormat = (GFXFormat)format; + } + } +} + +void GFXPCD3D9TextureTarget::activate() +{ + GFXDEBUGEVENT_SCOPE( GFXPCD3D9TextureTarget_activate, ColorI::RED ); + + AssertFatal( mTargets[GFXTextureTarget::Color0], + "GFXPCD3D9TextureTarget::activate() - You can never have a NULL primary render target!" ); + + const U32 NumRenderTargets = getMin( mDevice->getNumRenderTargets(), (U32)Color4 - Color0 ); + + LPDIRECT3DDEVICE9 d3dDevice = mDevice->getDevice(); + + // Clear the state indicator. + stateApplied(); + + IDirect3DSurface9 *depth = mTargets[GFXTextureTarget::DepthStencil]; + + // In debug lets do a complete test to be sure we don't + // have a bad depth format for this display mode. + #ifdef TORQUE_DEBUG + + if ( depth && mTargets[GFXTextureTarget::Color0] ) + { + D3DSURFACE_DESC desc; + D3D9Assert( mTargets[GFXTextureTarget::Color0]->GetDesc( &desc ), + "GFXPCD3D9TextureTarget::activate() - Failed to get surface description!"); + D3DFORMAT renderFormat = desc.Format; + + D3D9Assert( depth->GetDesc( &desc ), + "GFXPCD3D9TextureTarget::activate() - Failed to get surface description!"); + D3DFORMAT depthFormat = desc.Format; + + HRESULT hr = mDevice->getD3D()->CheckDepthStencilMatch( D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + mDevice->mDisplayMode.Format, + renderFormat, + depthFormat ); + + D3D9Assert( hr, "GFXPCD3D9TextureTarget::activate() - Bad depth format for this target!" ); + } + + #endif + + // First clear the non-primary targets to make the debug DX runtime happy. + for(U32 i = 1; i < NumRenderTargets; i++) + D3D9Assert(d3dDevice->SetRenderTarget( i, NULL ), + avar("GFXPCD3D9TextureTarget::activate() - failed to clear texture target %d!", i) ); + + // Now set all the new surfaces into the appropriate slots. + for(U32 i = 0; i < NumRenderTargets; i++) + { + IDirect3DSurface9 *target = mTargets[GFXTextureTarget::Color0 + i]; + if ( target ) + { + D3D9Assert(d3dDevice->SetRenderTarget(i, target), + avar("GFXPCD3D9TextureTarget::activate() - failed to set slot %d for texture target!", i) ); + } + } + + // TODO: This is often the same shared depth buffer used by most + // render targets. Are we getting performance hit from setting it + // multiple times... aside from the function call? + + D3D9Assert(d3dDevice->SetDepthStencilSurface( depth ), + "GFXPCD3D9TextureTarget::activate() - failed to set depthstencil target!" ); +} + +void GFXPCD3D9TextureTarget::deactivate() +{ + // Nothing to do... the next activate() call will + // set all the targets correctly. +} + +void GFXPCD3D9TextureTarget::resolve() +{ + GFXDEBUGEVENT_SCOPE( GFXPCD3D9TextureTarget_resolve, ColorI::RED ); + + for (U32 i = 0; i < MaxRenderSlotId; i++) + { + // We use existance @ mResolveTargets as a flag that we need to copy + // data from the rendertarget into the texture. + if (mResolveTargets[i]) + { + IDirect3DSurface9 *surf; + D3D9Assert( mResolveTargets[i]->get2DTex()->GetSurfaceLevel( 0, &surf ), + "GFXPCD3D9TextureTarget::resolve() - GetSurfaceLevel failed!" ); + + D3D9Assert( mDevice->getDevice()->StretchRect( mTargets[i], NULL, surf, NULL, D3DTEXF_NONE ), + "GFXPCD3D9TextureTarget::resolve() - StretchRect failed!" ); + + surf->Release(); + } + } +} + +void GFXPCD3D9TextureTarget::resolveTo( GFXTextureObject *tex ) +{ + GFXDEBUGEVENT_SCOPE( GFXPCD3D9TextureTarget_resolveTo, ColorI::RED ); + + if ( mTargets[Color0] == NULL ) + return; + + IDirect3DSurface9 *surf; + D3D9Assert( ((GFXD3D9TextureObject*)(tex))->get2DTex()->GetSurfaceLevel( 0, &surf ), + "GFXPCD3D9TextureTarget::resolveTo() - GetSurfaceLevel failed!" ); + + D3D9Assert( mDevice->getDevice()->StretchRect( mTargets[Color0], NULL, surf, NULL, D3DTEXF_NONE ), + "GFXPCD3D9TextureTarget::resolveTo() - StretchRect failed!" ); + + surf->Release(); +} + +void GFXPCD3D9TextureTarget::zombify() +{ + for(int i = 0; i < MaxRenderSlotId; i++) + attachTexture(RenderSlot(i), NULL); +} + +void GFXPCD3D9TextureTarget::resurrect() +{ + +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + + +GFXPCD3D9WindowTarget::GFXPCD3D9WindowTarget() +{ + mSwapChain = NULL; + mDepthStencil = NULL; + mWindow = NULL; + mDevice = NULL; + mBackbuffer = NULL; + mImplicit = true; +} + +GFXPCD3D9WindowTarget::~GFXPCD3D9WindowTarget() +{ + SAFE_RELEASE(mSwapChain); + SAFE_RELEASE(mDepthStencil); + SAFE_RELEASE(mBackbuffer); +} + +void GFXPCD3D9WindowTarget::initPresentationParams() +{ + // Get some video mode related info. + GFXVideoMode vm = mWindow->getVideoMode(); + + // Do some validation... + if(vm.fullScreen == true && mImplicit == false) + { + AssertISV(false, + "GFXPCD3D9WindowTarget::initPresentationParams - Cannot go fullscreen with secondary window!"); + } + + Win32Window *win = dynamic_cast(mWindow); + AssertISV(win, "GFXPCD3D9WindowTarget::initPresentationParams() - got a non Win32Window window passed in! Did DX go crossplatform?"); + + HWND hwnd = win->getHWND(); + + // At some point, this will become GFXPCD3D9WindowTarget like trunk has, + // so this cast isn't as bad as it looks. ;) BTR + GFXPCD3D9Device* pcdevice = dynamic_cast(mDevice); + mPresentationParams = pcdevice->setupPresentParams(vm, hwnd); + + if (mImplicit) + { + pcdevice->mMultisampleType = mPresentationParams.MultiSampleType; + pcdevice->mMultisampleLevel = mPresentationParams.MultiSampleQuality; + } +} + +const Point2I GFXPCD3D9WindowTarget::getSize() +{ + return mWindow->getVideoMode().resolution; +} + +GFXFormat GFXPCD3D9WindowTarget::getFormat() +{ + S32 format = mPresentationParams.BackBufferFormat; + GFXREVERSE_LOOKUP( GFXD3D9TextureFormat, GFXFormat, format ); + return (GFXFormat)format; +} + +bool GFXPCD3D9WindowTarget::present() +{ + AssertFatal(mSwapChain, "GFXPCD3D9WindowTarget::present - no swap chain present to present!"); + HRESULT res = mSwapChain->Present(NULL, NULL, NULL, NULL, NULL); + + return (res == S_OK); +} + +void GFXPCD3D9WindowTarget::setImplicitSwapChain() +{ + AssertFatal(mImplicit, "Invalid swap chain type! Additional swap chains are created as needed"); + // Reacquire our swapchain & DS + if(!mSwapChain) + mDevice->getDevice()->GetSwapChain(0, &mSwapChain); + if(!mDepthStencil) + mDevice->getDevice()->GetDepthStencilSurface(&mDepthStencil); + if (!mBackbuffer) + mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackbuffer); +} + +void GFXPCD3D9WindowTarget::createAdditionalSwapChain() +{ + AssertFatal(!mImplicit, "Invalid swap chain type! Implicit swap chains use the device"); + + // Since we're not going to do a device reset for an additional swap + // chain, we can just release our resources and regrab them. + SAFE_RELEASE(mSwapChain); + SAFE_RELEASE(mDepthStencil); + SAFE_RELEASE(mBackbuffer); + + // If there's a fullscreen window active, don't try to create these additional swap chains. + // CodeReview, we need to store the window target with the implicit swap chain better, this line below + // could fail if the current render target isn't what we expect. + GFXPCD3D9WindowTarget* currTarget = dynamic_cast(mDevice->getActiveRenderTarget()); + if (currTarget && currTarget->getWindow()->getVideoMode().fullScreen) + return; + + // Setup our presentation params. + initPresentationParams(); + + // Create our resources! + D3D9Assert(mDevice->getDevice()->CreateAdditionalSwapChain(&mPresentationParams, &mSwapChain), + "GFXPCD3D9WindowTarget::createAdditionalSwapChain - couldn't reallocate additional swap chain!"); + D3D9Assert(mDevice->getDevice()->CreateDepthStencilSurface(mPresentationParams.BackBufferWidth, mPresentationParams.BackBufferHeight, + D3DFMT_D24S8, mPresentationParams.MultiSampleType, mPresentationParams.MultiSampleQuality, false, &mDepthStencil, NULL), + "GFXPCD3D9WindowTarget::createAdditionalSwapChain: Unable to create stencil/depth surface"); + D3D9Assert(mSwapChain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &mBackbuffer), + "GFXPCD3D9WindowTarget::createAdditionalSwapChain: Unable to get backbuffer!"); +} + +void GFXPCD3D9WindowTarget::resetMode() +{ + mWindow->setSuppressReset(true); + + if (mSwapChain) + { + // The current video settings. + D3DPRESENT_PARAMETERS pp; + mSwapChain->GetPresentParameters(&pp); + bool ppFullscreen = !pp.Windowed; + Point2I backbufferSize(pp.BackBufferWidth, pp.BackBufferHeight); + + // The settings we are now applying. + const GFXVideoMode &mode = mWindow->getVideoMode(); + + // Convert the current multisample parameters into something + // we can compare with our GFXVideoMode.antialiasLevel value. + U32 ppAntiAliaseLevel = 0; + if ( pp.MultiSampleType != D3DMULTISAMPLE_NONE ) + ppAntiAliaseLevel = pp.MultiSampleQuality + 1; + + // Early out if none of the settings which require a device reset + // have changed. + if ( backbufferSize == getSize() && + ppFullscreen == mode.fullScreen && + ppAntiAliaseLevel == mode.antialiasLevel ) + return; + } + + // So, the video mode has changed - if we're an additional swap chain + // just kill the swapchain and reallocate to match new vid mode. + if(mImplicit == false) + { + createAdditionalSwapChain(); + } + else + { + // Setup our presentation params. + initPresentationParams(); + + // Otherwise, we have to reset the device, if we're the implicit swapchain. + mDevice->reset(mPresentationParams); + } + + // Update our size, too. + mSize = Point2I(mPresentationParams.BackBufferWidth, mPresentationParams.BackBufferHeight); + + mWindow->setSuppressReset(false); +} + +void GFXPCD3D9WindowTarget::zombify() +{ + // Release our resources + SAFE_RELEASE(mSwapChain); + SAFE_RELEASE(mDepthStencil); + SAFE_RELEASE(mBackbuffer); +} + +void GFXPCD3D9WindowTarget::resurrect() +{ + if(mImplicit) + { + setImplicitSwapChain(); + } + else if(!mSwapChain) + { + createAdditionalSwapChain(); + } +} + +void GFXPCD3D9WindowTarget::activate() +{ + GFXDEBUGEVENT_SCOPE( GFXPCD3D9WindowTarget_activate, ColorI::RED ); + + LPDIRECT3DDEVICE9 d3dDevice = mDevice->getDevice(); + + // In debug lets do a complete test to be sure we don't + // have a bad depth format for this display mode. + #ifdef TORQUE_DEBUG + if ( mDepthStencil && mBackbuffer ) + { + + D3DSURFACE_DESC desc; + D3D9Assert( mBackbuffer->GetDesc( &desc ), + "GFXPCD3D9TextureTarget::activate() - Failed to get surface description!"); + D3DFORMAT renderFormat = desc.Format; + + D3D9Assert( mDepthStencil->GetDesc( &desc ), + "GFXPCD3D9TextureTarget::activate() - Failed to get surface description!"); + D3DFORMAT depthFormat = desc.Format; + + HRESULT hr = mDevice->getD3D()->CheckDepthStencilMatch( D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + mDevice->mDisplayMode.Format, + renderFormat, + depthFormat ); + + D3D9Assert( hr, "GFXPCD3D9WindowTarget::activate() - Bad depth format for this back buffer!" ); + } + #endif + + D3D9Assert( d3dDevice->SetRenderTarget( 0, mBackbuffer ), + "GFXPCD3D9WindowTarget::activate() - Failed to set backbuffer target!" ); + + D3D9Assert( d3dDevice->SetDepthStencilSurface( mDepthStencil ), + "GFXPCD3D9WindowTarget::activate() - Failed to set depthstencil target!" ); + + D3DPRESENT_PARAMETERS pp; + + mSwapChain->GetPresentParameters(&pp); + + // Update our video mode here, too. + GFXVideoMode vm; + vm = mWindow->getVideoMode(); + vm.resolution.x = pp.BackBufferWidth; + vm.resolution.y = pp.BackBufferHeight; + vm.fullScreen = !pp.Windowed; + + mSize = vm.resolution; +} + +void GFXPCD3D9WindowTarget::resolveTo( GFXTextureObject *tex ) +{ + GFXDEBUGEVENT_SCOPE( GFXPCD3D9WindowTarget_resolveTo, ColorI::RED ); + + IDirect3DSurface9 *surf; + D3D9Assert( ((GFXD3D9TextureObject*)(tex))->get2DTex()->GetSurfaceLevel( 0, &surf ), + "GFXPCD3D9WindowTarget::resolveTo() - GetSurfaceLevel failed!" ); + + D3D9Assert( mDevice->getDevice()->StretchRect( mBackbuffer, NULL, surf, NULL, D3DTEXF_NONE ), + "GFXPCD3D9WindowTarget::resolveTo() - StretchRect failed!" ); + + surf->Release(); +} \ No newline at end of file diff --git a/Engine/source/gfx/D3D9/pc/gfxPCD3D9Target.h b/Engine/source/gfx/D3D9/pc/gfxPCD3D9Target.h new file mode 100644 index 000000000..b766ec8ff --- /dev/null +++ b/Engine/source/gfx/D3D9/pc/gfxPCD3D9Target.h @@ -0,0 +1,130 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _GFX_D3D_GFXD3D9TARGET_H_ +#define _GFX_D3D_GFXD3D9TARGET_H_ + +#ifndef _GFXTARGET_H_ +#include "gfx/gfxTarget.h" +#endif +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _MPOINT2_H_ +#include "math/mPoint2.h" +#endif +#include + +struct IDirect3DSurface9; +struct IDirect3DSwapChain9; +class GFXD3D9TextureObject; + + +class GFXPCD3D9TextureTarget : public GFXTextureTarget +{ + friend class GFXPCD3D9Device; + + // Array of target surfaces, this is given to us by attachTexture + IDirect3DSurface9 * mTargets[MaxRenderSlotId]; + + // Array of texture objects which correspond to the target surfaces above, + // needed for copy from RenderTarget to texture situations. Current only valid in those situations + GFXD3D9TextureObject* mResolveTargets[MaxRenderSlotId]; + + /// Owning d3d device. + GFXD3D9Device *mDevice; + + Point2I mTargetSize; + + GFXFormat mTargetFormat; + +public: + + GFXPCD3D9TextureTarget(); + ~GFXPCD3D9TextureTarget(); + + // Public interface. + virtual const Point2I getSize() { return mTargetSize; } + virtual GFXFormat getFormat() { return mTargetFormat; } + virtual void attachTexture(RenderSlot slot, GFXTextureObject *tex, U32 mipLevel=0, U32 zOffset = 0); + virtual void attachTexture(RenderSlot slot, GFXCubemap *tex, U32 face, U32 mipLevel=0); + virtual void resolve(); + + /// Note we always copy the Color0 RenderSlot. + virtual void resolveTo( GFXTextureObject *tex ); + + virtual void activate(); + virtual void deactivate(); + + void zombify(); + void resurrect(); +}; + +class GFXPCD3D9WindowTarget : public GFXWindowTarget +{ + friend class GFXPCD3D9Device; + + /// Our depth stencil buffer, if any. + IDirect3DSurface9 *mDepthStencil; + + /// Our backbuffer + IDirect3DSurface9 *mBackbuffer; + + /// Maximum size we can render to. + Point2I mSize; + + /// Our swap chain, potentially the implicit device swap chain. + IDirect3DSwapChain9 *mSwapChain; + + /// D3D presentation info. + D3DPRESENT_PARAMETERS mPresentationParams; + + /// Owning d3d device. + GFXD3D9Device *mDevice; + + /// Is this the implicit swap chain? + bool mImplicit; + + /// Internal interface that notifies us we need to reset our video mode. + void resetMode(); + +public: + + GFXPCD3D9WindowTarget(); + ~GFXPCD3D9WindowTarget(); + + virtual const Point2I getSize(); + virtual GFXFormat getFormat(); + virtual bool present(); + + void initPresentationParams(); + void setImplicitSwapChain(); + void createAdditionalSwapChain(); + + virtual void activate(); + + void zombify(); + void resurrect(); + + virtual void resolveTo( GFXTextureObject *tex ); +}; + +#endif // _GFX_D3D_GFXD3D9TARGET_H_ diff --git a/Engine/source/gfx/D3D9/screenshotD3D9.cpp b/Engine/source/gfx/D3D9/screenshotD3D9.cpp new file mode 100644 index 000000000..b6b5e0500 --- /dev/null +++ b/Engine/source/gfx/D3D9/screenshotD3D9.cpp @@ -0,0 +1,110 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/D3D9/screenshotD3D9.h" + +#include "gfx/D3D9/gfxD3D9Device.h" + +#include +#include +#include + + +GBitmap* ScreenShotD3D::_captureBackBuffer() +{ +#ifdef TORQUE_OS_XENON + return NULL; +#else + LPDIRECT3DDEVICE9 D3DDevice = dynamic_cast(GFX)->getDevice(); + + IDirect3DSurface9 * backBuffer; + D3DDevice->GetBackBuffer( 0, 0, D3DBACKBUFFER_TYPE_MONO, &backBuffer ); + + // Figure the size we're snagging. + D3DSURFACE_DESC desc; + backBuffer->GetDesc(&desc); + + Point2I size; + size.x = desc.Width; + size.y = desc.Height; + + // set up the 2 copy surfaces + GFXTexHandle tex[2]; + IDirect3DSurface9 *surface[2]; + + tex[0].set( size.x, size.y, GFXFormatR8G8B8X8, &GFXDefaultRenderTargetProfile, avar("%s() - tex[0] (line %d)", __FUNCTION__, __LINE__) ); + tex[1].set( size.x, size.y, GFXFormatR8G8B8X8, &GFXSystemMemProfile, avar("%s() - tex[1] (line %d)", __FUNCTION__, __LINE__) ); + + // grab the top level surface of tex 0 + GFXD3D9TextureObject *to = (GFXD3D9TextureObject *) &(*tex[0]); + D3D9Assert( to->get2DTex()->GetSurfaceLevel( 0, &surface[0] ), NULL ); + + // use StretchRect because it allows a copy from a multisample surface + // to a normal rendertarget surface + D3DDevice->StretchRect( backBuffer, NULL, surface[0], NULL, D3DTEXF_NONE ); + + // grab the top level surface of tex 1 + to = (GFXD3D9TextureObject *) &(*tex[1]); + D3D9Assert( to->get2DTex()->GetSurfaceLevel( 0, &surface[1] ), NULL ); + + // copy the data from the render target to the system memory texture + D3DDevice->GetRenderTargetData( surface[0], surface[1] ); + + // Allocate a GBitmap and copy into it. + GBitmap *gb = new GBitmap(size.x, size.y); + + D3DLOCKED_RECT r; + D3DSURFACE_DESC d; + surface[1]->GetDesc(&d); + surface[1]->LockRect( &r, NULL, D3DLOCK_READONLY); + + // We've got the X8 in there so we have to manually copy stuff. + ColorI c; + for(S32 i=0; isetColor(j, i, c); + } + } + + surface[1]->UnlockRect(); + + // Also save it out with D3DX + //D3DXSaveSurfaceToFile( dT( "testScreen.png" ), D3DXIFF_PNG, surface[1], NULL, NULL ); + + // release the COM pointers + surface[0]->Release(); + surface[1]->Release(); + backBuffer->Release(); + + return gb; +#endif +} + diff --git a/Engine/source/gfx/D3D9/screenshotD3D9.h b/Engine/source/gfx/D3D9/screenshotD3D9.h new file mode 100644 index 000000000..d017591d6 --- /dev/null +++ b/Engine/source/gfx/D3D9/screenshotD3D9.h @@ -0,0 +1,39 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _SCREENSHOTD3D_H_ +#define _SCREENSHOTD3D_H_ + +#include "gfx/screenshot.h" + +//************************************************************************** +// D3D implementation of screenshot +//************************************************************************** +class ScreenShotD3D : public ScreenShot +{ +protected: + + GBitmap* _captureBackBuffer(); + +}; + + +#endif // _SCREENSHOTD3D_H_ diff --git a/Engine/source/gfx/D3D9/videoCaptureD3D9.cpp b/Engine/source/gfx/D3D9/videoCaptureD3D9.cpp new file mode 100644 index 000000000..39786a472 --- /dev/null +++ b/Engine/source/gfx/D3D9/videoCaptureD3D9.cpp @@ -0,0 +1,210 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "videoCaptureD3D9.h" +#include "gfx/D3D9/gfxD3D9Device.h" + +#include "platform/tmm_off.h" + +#include +#include +#include + +VideoFrameGrabberD3D9::VideoFrameGrabberD3D9() +{ + GFXDevice::getDeviceEventSignal().notify( this, &VideoFrameGrabberD3D9::_handleGFXEvent ); + mCurrentCapture = 0; +} + +VideoFrameGrabberD3D9::~VideoFrameGrabberD3D9() +{ + GFXDevice::getDeviceEventSignal().remove( this, &VideoFrameGrabberD3D9::_handleGFXEvent ); +} + + +void VideoFrameGrabberD3D9::captureBackBuffer() +{ + AssertFatal( mCapture[mCurrentCapture].stage != eInSystemMemory, "Error! Trying to override a capture resource in \"SystemMemory\" stage!" ); + +#ifndef TORQUE_OS_XENON + LPDIRECT3DDEVICE9 D3DDevice = dynamic_cast(GFX)->getDevice(); + + IDirect3DSurface9 * backBuffer; + D3DDevice->GetBackBuffer( 0, 0, D3DBACKBUFFER_TYPE_MONO, &backBuffer ); + + GFXTexHandle &vidMemTex = mCapture[mCurrentCapture].vidMemTex; + + // Re-init video memory texture if needed + if (vidMemTex.isNull() || vidMemTex.getWidthHeight() != mResolution) + vidMemTex.set(mResolution.x, mResolution.y,GFXFormatR8G8B8X8, &GFXDefaultRenderTargetProfile, avar("%s() - mVidMemTex (line %d)", __FUNCTION__, __LINE__) ); + + // set up the copy surface + IDirect3DSurface9 *surface; + + // grab the top level surface of tex 0 + GFXD3D9TextureObject *to = (GFXD3D9TextureObject *) &(*vidMemTex); + D3D9Assert( to->get2DTex()->GetSurfaceLevel( 0, &surface ), NULL ); + + // use StretchRect because it allows a copy from a multisample surface + // to a normal rendertarget surface + D3DDevice->StretchRect( backBuffer, NULL, surface, NULL, D3DTEXF_LINEAR ); + + // Reelase surfaces + backBuffer->Release(); + surface->Release(); + + // Update the stage + mCapture[mCurrentCapture].stage = eInVideoMemory; +#endif +} + +void VideoFrameGrabberD3D9::makeBitmap() +{ + // Advance the stages for all resources, except the one used for the last capture + for (U32 i=0; iget2DTex()->GetSurfaceLevel( 0, &surface[0] ), NULL ); + + // grab the top level surface of tex 1 + to = (GFXD3D9TextureObject *) &(*sysMemTex); + D3D9Assert( to->get2DTex()->GetSurfaceLevel( 0, &surface[1] ), NULL ); + + // copy the data from the render target to the system memory texture + LPDIRECT3DDEVICE9 D3DDevice = dynamic_cast(GFX)->getDevice(); + D3DDevice->GetRenderTargetData( surface[0], surface[1] ); + + // celease surfaces + surface[0]->Release(); + surface[1]->Release(); + + // Change the resource state + capture.stage = eInSystemMemory; +#endif +} + +void VideoFrameGrabberD3D9::copyToBitmap(CaptureResource &capture) +{ + AssertFatal( capture.stage == eInSystemMemory, "Error! copyToBitmap() can only work in resources in 'eInSystemMemory' stage!" ); + + GFXTexHandle &sysMemTex = capture.sysMemTex; + Point2I size = sysMemTex.getWidthHeight(); + + // Setup a surface + IDirect3DSurface9 *surface; + + GFXD3D9TextureObject *to = (GFXD3D9TextureObject *) &(*sysMemTex); + D3D9Assert( to->get2DTex()->GetSurfaceLevel( 0, &surface ), NULL ); + + // Lock the system memory surface + D3DLOCKED_RECT r; + D3DSURFACE_DESC d; + surface->GetDesc(&d); + surface->LockRect( &r, NULL, D3DLOCK_READONLY); + + // Allocate a GBitmap and copy into it. + GBitmap *gb = new GBitmap(size.x, size.y); + + // We've got the X8 in there so we have to manually copy stuff. + const U32* src = (const U32*)r.pBits; + U8* dst = gb->getWritableBits(); + S32 pixels = size.x*size.y; + for(S32 i=0; i> 16; + *dst++ = px >> 8; + *dst++ = px; + } + surface->UnlockRect(); + + // celease surfaces + surface->Release(); + + // Push this new bitmap + pushNewBitmap(gb); + + // Change the resource state + capture.stage = eReadyToCapture; +} + +bool VideoFrameGrabberD3D9::_handleGFXEvent( GFXDevice::GFXDeviceEventType event_ ) +{ + switch ( event_ ) + { + case GFXDevice::deDestroy : + releaseTextures(); + break; + + default: + break; + } + + return true; +} diff --git a/Engine/source/gfx/D3D9/videoCaptureD3D9.h b/Engine/source/gfx/D3D9/videoCaptureD3D9.h new file mode 100644 index 000000000..f9c5e9f1d --- /dev/null +++ b/Engine/source/gfx/D3D9/videoCaptureD3D9.h @@ -0,0 +1,82 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _VIDEOCAPTURE_H_ +#include "gfx/video/videoCapture.h" +#endif + +#ifndef _GFXDEVICE_H_ +#include "gfx/gfxDevice.h" +#endif + + +class VideoFrameGrabberD3D9 : public VideoFrameGrabber +{ +protected: + enum CaptureStage { + eReadyToCapture, + eInVideoMemory, + eInSystemMemory, + eNumStages + }; + + // Contains all elements involved in single frame capture and + // is used to spread the multiple "stages" needed to capture a bitmap + // over various frames to keep GPU resources from locking the CPU. + struct CaptureResource { + GFXTexHandle vidMemTex; //Video memory texture + GFXTexHandle sysMemTex; //System memory texture + CaptureStage stage; //This resource's capture stage + + CaptureResource() : stage(eReadyToCapture) {}; + ~CaptureResource() + { + vidMemTex.free(); + sysMemTex.free(); + } + }; + + // Capture resource array. One item for each capture pipeline stage + CaptureResource mCapture[eNumStages]; + + // Current capture index + S32 mCurrentCapture; + + // Copies a capture's video memory content to system memory + void copyToSystemMemory(CaptureResource &capture); + + // Copies a capture's syste memory content to a new bitmap + void copyToBitmap(CaptureResource &capture); + + bool _handleGFXEvent(GFXDevice::GFXDeviceEventType event); + + //------------------------------------------------ + // Overloaded from VideoFrameGrabber + //------------------------------------------------ + void captureBackBuffer(); + void makeBitmap(); + void releaseTextures(); + +public: + VideoFrameGrabberD3D9(); + ~VideoFrameGrabberD3D9(); +}; \ No newline at end of file diff --git a/Engine/source/gfx/Null/gfxNullDevice.cpp b/Engine/source/gfx/Null/gfxNullDevice.cpp new file mode 100644 index 000000000..315336c24 --- /dev/null +++ b/Engine/source/gfx/Null/gfxNullDevice.cpp @@ -0,0 +1,343 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/Null/gfxNullDevice.h" + +#include "core/strings/stringFunctions.h" +#include "gfx/gfxCubemap.h" +#include "gfx/screenshot.h" +#include "gfx/gfxPrimitiveBuffer.h" +#include "gfx/gfxCardProfile.h" +#include "gfx/gfxTextureManager.h" +#include "gfx/bitmap/gBitmap.h" +#include "core/util/safeDelete.h" + + +GFXAdapter::CreateDeviceInstanceDelegate GFXNullDevice::mCreateDeviceInstance(GFXNullDevice::createInstance); + +class GFXNullCardProfiler: public GFXCardProfiler +{ +private: + typedef GFXCardProfiler Parent; +public: + + /// + virtual const String &getRendererString() const { static String sRS("GFX Null Device Renderer"); return sRS; } + +protected: + + virtual void setupCardCapabilities() { }; + + virtual bool _queryCardCap(const String &query, U32 &foundResult){ return false; } + virtual bool _queryFormat(const GFXFormat fmt, const GFXTextureProfile *profile, bool &inOutAutogenMips) { inOutAutogenMips = false; return false; } + +public: + virtual void init() + { + mCardDescription = "GFX Null Device Card"; + mChipSet = "NULL Device"; + mVersionString = "0"; + + Parent::init(); // other code notes that not calling this is "BAD". + }; +}; + +class GFXNullTextureObject : public GFXTextureObject +{ +public: + GFXNullTextureObject(GFXDevice * aDevice, GFXTextureProfile *profile); + ~GFXNullTextureObject() { kill(); }; + + virtual void pureVirtualCrash() { }; + + virtual GFXLockedRect * lock( U32 mipLevel = 0, RectI *inRect = NULL ) { return NULL; }; + virtual void unlock( U32 mipLevel = 0) {}; + virtual bool copyToBmp(GBitmap *) { return false; }; + + virtual void zombify() {} + virtual void resurrect() {} +}; + +GFXNullTextureObject::GFXNullTextureObject(GFXDevice * aDevice, GFXTextureProfile *profile) : + GFXTextureObject(aDevice, profile) +{ + mProfile = profile; + mTextureSize.set( 0, 0, 0 ); +} + +class GFXNullTextureManager : public GFXTextureManager +{ +protected: + virtual GFXTextureObject *_createTextureObject( U32 height, + U32 width, + U32 depth, + GFXFormat format, + GFXTextureProfile *profile, + U32 numMipLevels, + bool forceMips = false, + S32 antialiasLevel = 0, + GFXTextureObject *inTex = NULL ) + { + GFXNullTextureObject *retTex; + if ( inTex ) + { + AssertFatal( dynamic_cast( inTex ), "GFXNullTextureManager::_createTexture() - Bad inTex type!" ); + retTex = static_cast( inTex ); + } + else + { + retTex = new GFXNullTextureObject( GFX, profile ); + retTex->registerResourceWithDevice( GFX ); + } + + SAFE_DELETE( retTex->mBitmap ); + retTex->mBitmap = new GBitmap(width, height); + return retTex; + }; + + /// Load a texture from a proper DDSFile instance. + virtual bool _loadTexture(GFXTextureObject *texture, DDSFile *dds){ return true; }; + + /// Load data into a texture from a GBitmap using the internal API. + virtual bool _loadTexture(GFXTextureObject *texture, GBitmap *bmp){ return true; }; + + /// Load data into a texture from a raw buffer using the internal API. + /// + /// Note that the size of the buffer is assumed from the parameters used + /// for this GFXTextureObject's _createTexture call. + virtual bool _loadTexture(GFXTextureObject *texture, void *raw){ return true; }; + + /// Refresh a texture using the internal API. + virtual bool _refreshTexture(GFXTextureObject *texture){ return true; }; + + /// Free a texture (but do not delete the GFXTextureObject) using the internal + /// API. + /// + /// This is only called during zombification for textures which need it, so you + /// don't need to do any internal safety checks. + virtual bool _freeTexture(GFXTextureObject *texture, bool zombify=false) { return true; }; + + virtual U32 _getTotalVideoMemory() { return 0; }; + virtual U32 _getFreeVideoMemory() { return 0; }; +}; + +class GFXNullCubemap : public GFXCubemap +{ + friend class GFXDevice; +private: + // should only be called by GFXDevice + virtual void setToTexUnit( U32 tuNum ) { }; + +public: + virtual void initStatic( GFXTexHandle *faces ) { }; + virtual void initStatic( DDSFile *dds ) { }; + virtual void initDynamic( U32 texSize, GFXFormat faceFormat = GFXFormatR8G8B8A8 ) { }; + virtual U32 getSize() const { return 0; } + virtual GFXFormat getFormat() const { return GFXFormatR8G8B8A8; } + + virtual ~GFXNullCubemap(){}; + + virtual void zombify() {} + virtual void resurrect() {} +}; + +class GFXNullVertexBuffer : public GFXVertexBuffer +{ + unsigned char* tempBuf; +public: + GFXNullVertexBuffer( GFXDevice *device, + U32 numVerts, + const GFXVertexFormat *vertexFormat, + U32 vertexSize, + GFXBufferType bufferType ) : + GFXVertexBuffer(device, numVerts, vertexFormat, vertexSize, bufferType) { }; + virtual void lock(U32 vertexStart, U32 vertexEnd, void **vertexPtr); + virtual void unlock(); + virtual void prepare(); + + virtual void zombify() {} + virtual void resurrect() {} +}; + +void GFXNullVertexBuffer::lock(U32 vertexStart, U32 vertexEnd, void **vertexPtr) +{ + tempBuf = new unsigned char[(vertexEnd - vertexStart) * mVertexSize]; + *vertexPtr = (void*) tempBuf; + lockedVertexStart = vertexStart; + lockedVertexEnd = vertexEnd; +} + +void GFXNullVertexBuffer::unlock() +{ + delete[] tempBuf; + tempBuf = NULL; +} + +void GFXNullVertexBuffer::prepare() +{ +} + +class GFXNullPrimitiveBuffer : public GFXPrimitiveBuffer +{ +private: + U16* temp; +public: + GFXNullPrimitiveBuffer( GFXDevice *device, + U32 indexCount, + U32 primitiveCount, + GFXBufferType bufferType ) : + GFXPrimitiveBuffer(device, indexCount, primitiveCount, bufferType), temp( NULL ) {}; + + virtual void lock(U32 indexStart, U32 indexEnd, void **indexPtr); ///< locks this primitive buffer for writing into + virtual void unlock(); ///< unlocks this primitive buffer. + virtual void prepare() { }; ///< prepares this primitive buffer for use on the device it was allocated on + + virtual void zombify() {} + virtual void resurrect() {} +}; + +void GFXNullPrimitiveBuffer::lock(U32 indexStart, U32 indexEnd, void **indexPtr) +{ + temp = new U16[indexEnd - indexStart]; + *indexPtr = temp; +} + +void GFXNullPrimitiveBuffer::unlock() +{ + delete[] temp; + temp = NULL; +} + +// +// GFXNullStateBlock +// +class GFXNullStateBlock : public GFXStateBlock +{ +public: + /// Returns the hash value of the desc that created this block + virtual U32 getHashValue() const { return 0; }; + + /// Returns a GFXStateBlockDesc that this block represents + virtual const GFXStateBlockDesc& getDesc() const { return mDefaultDesc; } + + // + // GFXResource + // + virtual void zombify() { } + /// When called the resource should restore all device sensitive information destroyed by zombify() + virtual void resurrect() { } +private: + GFXStateBlockDesc mDefaultDesc; +}; + +// +// GFXNullDevice +// + +GFXDevice *GFXNullDevice::createInstance( U32 adapterIndex ) +{ + return new GFXNullDevice(); +} + +GFXNullDevice::GFXNullDevice() +{ + clip.set(0, 0, 800, 800); + + mTextureManager = new GFXNullTextureManager(); + gScreenShot = new ScreenShot(); + mCardProfiler = new GFXNullCardProfiler(); + mCardProfiler->init(); +} + +GFXNullDevice::~GFXNullDevice() +{ +} + +GFXVertexBuffer *GFXNullDevice::allocVertexBuffer( U32 numVerts, + const GFXVertexFormat *vertexFormat, + U32 vertSize, + GFXBufferType bufferType ) +{ + return new GFXNullVertexBuffer(GFX, numVerts, vertexFormat, vertSize, bufferType); +} + +GFXPrimitiveBuffer *GFXNullDevice::allocPrimitiveBuffer( U32 numIndices, + U32 numPrimitives, + GFXBufferType bufferType) +{ + return new GFXNullPrimitiveBuffer(GFX, numIndices, numPrimitives, bufferType); +} + +GFXCubemap* GFXNullDevice::createCubemap() +{ + return new GFXNullCubemap(); +}; + +void GFXNullDevice::enumerateAdapters( Vector &adapterList ) +{ + // Add the NULL renderer + GFXAdapter *toAdd = new GFXAdapter(); + + toAdd->mIndex = 0; + toAdd->mType = NullDevice; + toAdd->mCreateDeviceInstanceDelegate = mCreateDeviceInstance; + + GFXVideoMode vm; + vm.bitDepth = 32; + vm.resolution.set(800,600); + toAdd->mAvailableModes.push_back(vm); + + dStrcpy(toAdd->mName, "GFX Null Device"); + + adapterList.push_back(toAdd); +} + +void GFXNullDevice::setLightInternal(U32 lightStage, const GFXLightInfo light, bool lightEnable) +{ + +} + +void GFXNullDevice::init( const GFXVideoMode &mode, PlatformWindow *window ) +{ + mCardProfiler = new GFXNullCardProfiler(); + mCardProfiler->init(); +} + +GFXStateBlockRef GFXNullDevice::createStateBlockInternal(const GFXStateBlockDesc& desc) +{ + return new GFXNullStateBlock(); +} + +// +// Register this device with GFXInit +// +class GFXNullRegisterDevice +{ +public: + GFXNullRegisterDevice() + { + GFXInit::getRegisterDeviceSignal().notify(&GFXNullDevice::enumerateAdapters); + } +}; + +static GFXNullRegisterDevice pNullRegisterDevice; \ No newline at end of file diff --git a/Engine/source/gfx/Null/gfxNullDevice.h b/Engine/source/gfx/Null/gfxNullDevice.h new file mode 100644 index 000000000..5e137cf01 --- /dev/null +++ b/Engine/source/gfx/Null/gfxNullDevice.h @@ -0,0 +1,185 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXNullDevice_H_ +#define _GFXNullDevice_H_ + +#include "platform/platform.h" + +//----------------------------------------------------------------------------- + +#include "gfx/gfxDevice.h" +#include "gfx/gfxInit.h" +#include "gfx/gfxFence.h" + +class GFXNullWindowTarget : public GFXWindowTarget +{ +public: + virtual bool present() + { + return true; + } + + virtual const Point2I getSize() + { + // Return something stupid. + return Point2I(1,1); + } + + virtual GFXFormat getFormat() { return GFXFormatR8G8B8A8; } + + virtual void resetMode() + { + + } + + virtual void zombify() {}; + virtual void resurrect() {}; + +}; + +class GFXNullDevice : public GFXDevice +{ +public: + GFXNullDevice(); + virtual ~GFXNullDevice(); + + static GFXDevice *createInstance( U32 adapterIndex ); + + static void enumerateAdapters( Vector &adapterList ); + + void init( const GFXVideoMode &mode, PlatformWindow *window = NULL ); + + virtual void activate() { }; + virtual void deactivate() { }; + virtual GFXAdapterType getAdapterType() { return NullDevice; }; + + /// @name Debug Methods + /// @{ + virtual void enterDebugEvent(ColorI color, const char *name) { }; + virtual void leaveDebugEvent() { }; + virtual void setDebugMarker(ColorI color, const char *name) { }; + /// @} + + /// Enumerates the supported video modes of the device + virtual void enumerateVideoModes() { }; + + /// Sets the video mode for the device + virtual void setVideoMode( const GFXVideoMode &mode ) { }; +protected: + static GFXAdapter::CreateDeviceInstanceDelegate mCreateDeviceInstance; + + /// Called by GFXDevice to create a device specific stateblock + virtual GFXStateBlockRef createStateBlockInternal(const GFXStateBlockDesc& desc); + /// Called by GFXDevice to actually set a stateblock. + virtual void setStateBlockInternal(GFXStateBlock* block, bool force) { }; + /// @} + + /// Called by base GFXDevice to actually set a const buffer + virtual void setShaderConstBufferInternal(GFXShaderConstBuffer* buffer) { }; + + virtual void setTextureInternal(U32 textureUnit, const GFXTextureObject*texture) { }; + + virtual void setLightInternal(U32 lightStage, const GFXLightInfo light, bool lightEnable); + virtual void setLightMaterialInternal(const GFXLightMaterial mat) { }; + virtual void setGlobalAmbientInternal(ColorF color) { }; + + /// @name State Initalization. + /// @{ + + /// State initalization. This MUST BE CALLED in setVideoMode after the device + /// is created. + virtual void initStates() { }; + + virtual void setMatrix( GFXMatrixType mtype, const MatrixF &mat ) { }; + + virtual GFXVertexBuffer *allocVertexBuffer( U32 numVerts, + const GFXVertexFormat *vertexFormat, + U32 vertSize, + GFXBufferType bufferType ); + virtual GFXPrimitiveBuffer *allocPrimitiveBuffer( U32 numIndices, + U32 numPrimitives, + GFXBufferType bufferType ); + + virtual GFXVertexDecl* allocVertexDecl( const GFXVertexFormat *vertexFormat ) { return NULL; } + virtual void setVertexDecl( const GFXVertexDecl *decl ) { } + virtual void setVertexStream( U32 stream, GFXVertexBuffer *buffer ) { } + virtual void setVertexStreamFrequency( U32 stream, U32 frequency ) { } + +public: + virtual GFXCubemap * createCubemap(); + + virtual F32 getFillConventionOffset() const { return 0.0f; }; + + ///@} + + virtual GFXTextureTarget *allocRenderToTextureTarget(){return NULL;}; + virtual GFXWindowTarget *allocWindowTarget(PlatformWindow *window) + { + return new GFXNullWindowTarget(); + }; + + virtual void _updateRenderTargets(){}; + + virtual F32 getPixelShaderVersion() const { return 0.0f; }; + virtual void setPixelShaderVersion( F32 version ) { }; + virtual U32 getNumSamplers() const { return 0; }; + virtual U32 getNumRenderTargets() const { return 0; }; + + virtual GFXShader* createShader() { return NULL; }; + + + virtual void clear( U32 flags, ColorI color, F32 z, U32 stencil ) { }; + virtual bool beginSceneInternal() { return true; }; + virtual void endSceneInternal() { }; + + virtual void drawPrimitive( GFXPrimitiveType primType, U32 vertexStart, U32 primitiveCount ) { }; + virtual void drawIndexedPrimitive( GFXPrimitiveType primType, + U32 startVertex, + U32 minIndex, + U32 numVerts, + U32 startIndex, + U32 primitiveCount ) { }; + + virtual void setClipRect( const RectI &rect ) { }; + virtual const RectI &getClipRect() const { return clip; }; + + virtual void preDestroy() { Parent::preDestroy(); }; + + virtual U32 getMaxDynamicVerts() { return 16384; }; + virtual U32 getMaxDynamicIndices() { return 16384; }; + + virtual GFXFormat selectSupportedFormat( GFXTextureProfile *profile, + const Vector &formats, + bool texture, + bool mustblend, + bool mustfilter ) { return GFXFormatR8G8B8A8; }; + + GFXFence *createFence() { return new GFXGeneralFence( this ); } + GFXOcclusionQuery* createOcclusionQuery() { return NULL; } + +private: + typedef GFXDevice Parent; + RectI clip; +}; + +#endif diff --git a/Engine/source/gfx/bitmap/bitmapUtils.cpp b/Engine/source/gfx/bitmap/bitmapUtils.cpp new file mode 100644 index 000000000..f1ac8060f --- /dev/null +++ b/Engine/source/gfx/bitmap/bitmapUtils.cpp @@ -0,0 +1,284 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/bitmap/bitmapUtils.h" + +#include "platform/platform.h" + + +void bitmapExtrude5551_c(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth) +{ + const U16 *src = (const U16 *) srcMip; + U16 *dst = (U16 *) mip; + U32 stride = srcHeight != 1 ? srcWidth : 0; + + U32 width = srcWidth >> 1; + U32 height = srcHeight >> 1; + if (width == 0) width = 1; + if (height == 0) height = 1; + + if (srcWidth != 1) + { + for(U32 y = 0; y < height; y++) + { + for(U32 x = 0; x < width; x++) + { + U32 a = src[0]; + U32 b = src[1]; + U32 c = src[stride]; + U32 d = src[stride+1]; +#if defined(TORQUE_BIG_ENDIAN) + dst[x] = ((( (a >> 10) + (b >> 10) + (c >> 10) + (d >> 10)) >> 2) << 10) | + ((( ((a >> 5) & 0x1F) + ((b >> 5) & 0x1F) + ((c >> 5) & 0x1F) + ((d >> 5) & 0x1F)) >> 2) << 5) | + ((( ((a >> 0) & 0x1F) + ((b >> 0) & 0x1F) + ((c >> 0) & 0x1F) + ((d >> 0) & 0x1F)) >> 2) << 0); +#else + dst[x] = ((( (a >> 11) + (b >> 11) + (c >> 11) + (d >> 11)) >> 2) << 11) | + ((( ((a >> 6) & 0x1F) + ((b >> 6) & 0x1F) + ((c >> 6) & 0x1F) + ((d >> 6) & 0x1F)) >> 2) << 6) | + ((( ((a >> 1) & 0x1F) + ((b >> 1) & 0x1F) + ((c >> 1) & 0x1F) + ((d >> 1) & 0x1F)) >> 2) << 1); +#endif + src += 2; + } + src += stride; + dst += width; + } + } + else + { + for(U32 y = 0; y < height; y++) + { + U32 a = src[0]; + U32 c = src[stride]; +#if defined(TORQUE_OS_MAC) + dst[y] = ((( (a >> 10) + (c >> 10)) >> 1) << 10) | + ((( ((a >> 5) & 0x1F) + ((c >> 5) & 0x1f)) >> 1) << 5) | + ((( ((a >> 0) & 0x1F) + ((c >> 0) & 0x1f)) >> 1) << 0); +#else + dst[y] = ((( (a >> 11) + (c >> 11)) >> 1) << 11) | + ((( ((a >> 6) & 0x1f) + ((c >> 6) & 0x1f)) >> 1) << 6) | + ((( ((a >> 1) & 0x1F) + ((c >> 1) & 0x1f)) >> 1) << 1); +#endif + src += 1 + stride; + } + } +} + + +//-------------------------------------------------------------------------- +void bitmapExtrudeRGB_c(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth) +{ + const U8 *src = (const U8 *) srcMip; + U8 *dst = (U8 *) mip; + U32 stride = srcHeight != 1 ? (srcWidth) * 3 : 0; + + U32 width = srcWidth >> 1; + U32 height = srcHeight >> 1; + if (width == 0) width = 1; + if (height == 0) height = 1; + + if (srcWidth != 1) + { + for(U32 y = 0; y < height; y++) + { + for(U32 x = 0; x < width; x++) + { + *dst++ = (U32(*src) + U32(src[3]) + U32(src[stride]) + U32(src[stride+3]) + 2) >> 2; + src++; + *dst++ = (U32(*src) + U32(src[3]) + U32(src[stride]) + U32(src[stride+3]) + 2) >> 2; + src++; + *dst++ = (U32(*src) + U32(src[3]) + U32(src[stride]) + U32(src[stride+3]) + 2) >> 2; + src += 4; + } + src += stride; // skip + } + } + else + { + for(U32 y = 0; y < height; y++) + { + *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1; + src++; + *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1; + src++; + *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1; + src += 4; + + src += stride; // skip + } + } +} + +//-------------------------------------------------------------------------- +void bitmapExtrudeRGBA_c(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth) +{ + const U8 *src = (const U8 *) srcMip; + U8 *dst = (U8 *) mip; + U32 stride = srcHeight != 1 ? (srcWidth) * 4 : 0; + + U32 width = srcWidth >> 1; + U32 height = srcHeight >> 1; + if (width == 0) width = 1; + if (height == 0) height = 1; + + if (srcWidth != 1) + { + for(U32 y = 0; y < height; y++) + { + for(U32 x = 0; x < width; x++) + { + *dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride+4]) + 2) >> 2; + src++; + *dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride+4]) + 2) >> 2; + src++; + *dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride+4]) + 2) >> 2; + src++; + *dst++ = (U32(*src) + U32(src[4]) + U32(src[stride]) + U32(src[stride+4]) + 2) >> 2; + src += 5; + } + src += stride; // skip + } + } + else + { + for(U32 y = 0; y < height; y++) + { + *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1; + src++; + *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1; + src++; + *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1; + src++; + *dst++ = (U32(*src) + U32(src[stride]) + 1) >> 1; + src += 5; + + src += stride; // skip + } + } +} + +void (*bitmapExtrude5551)(const void *srcMip, void *mip, U32 height, U32 width) = bitmapExtrude5551_c; +void (*bitmapExtrudeRGB)(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth) = bitmapExtrudeRGB_c; +void (*bitmapExtrudeRGBA)(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth) = bitmapExtrudeRGBA_c; + + +//-------------------------------------------------------------------------- + +void bitmapConvertRGB_to_1555_c(U8 *src, U32 pixels) +{ + U16 *dst = (U16 *)src; + for(U32 j = 0; j < pixels; j++) + { + U32 r = src[0] >> 3; + U32 g = src[1] >> 3; + U32 b = src[2] >> 3; + +#if defined(TORQUE_OS_MAC) + *dst++ = 0x8000 | (b << 10) | (g << 5) | (r << 0); +#else + *dst++ = b | (g << 5) | (r << 10) | 0x8000; +#endif + src += 3; + } +} + +void (*bitmapConvertRGB_to_1555)(U8 *src, U32 pixels) = bitmapConvertRGB_to_1555_c; + +//------------------------------------------------------------------------------ + +void bitmapConvertRGB_to_5551_c(U8 *src, U32 pixels) +{ + U16 *dst = (U16 *)src; + for(U32 j = 0; j < pixels; j++) + { + U32 r = src[0] >> 3; + U32 g = src[1] >> 3; + U32 b = src[2] >> 3; + +#if defined(TORQUE_OS_MAC) + *dst++ = (1 << 15) | (b << 10) | (g << 5) | (r << 0); +#else + *dst++ = (b << 1) | (g << 6) | (r << 11) | 1; +#endif + src += 3; + } +} + + + +void (*bitmapConvertRGB_to_5551)(U8 *src, U32 pixels) = bitmapConvertRGB_to_5551_c; + +//------------------------------------------------------------------------------ + +void bitmapConvertRGB_to_RGBX_c( U8 **src, U32 pixels ) +{ + const U8 *oldBits = *src; + U8 *newBits = new U8[pixels * 4]; + dMemset( newBits, 0xFF, pixels * 4 ); // This is done to set alpha values -patw + + // Copy the bits over to the new memory + for( U32 i = 0; i < pixels; i++ ) + dMemcpy( &newBits[i * 4], &oldBits[i * 3], sizeof(U8) * 3 ); + + // Now hose the old bits + delete [] *src; + *src = newBits; +} + +void (*bitmapConvertRGB_to_RGBX)( U8 **src, U32 pixels ) = bitmapConvertRGB_to_RGBX_c; + +//------------------------------------------------------------------------------ + +void bitmapConvertRGBX_to_RGB_c( U8 **src, U32 pixels ) +{ + const U8 *oldBits = *src; + U8 *newBits = new U8[pixels * 3]; + + // Copy the bits over to the new memory + for( U32 i = 0; i < pixels; i++ ) + dMemcpy( &newBits[i * 3], &oldBits[i * 4], sizeof(U8) * 3 ); + + // Now hose the old bits + delete [] *src; + *src = newBits; +} + +void (*bitmapConvertRGBX_to_RGB)( U8 **src, U32 pixels ) = bitmapConvertRGBX_to_RGB_c; + +//------------------------------------------------------------------------------ + +void bitmapConvertA8_to_RGBA_c( U8 **src, U32 pixels ) +{ + const U8 *oldBits = *src; + U8 *newBits = new U8[pixels * 4]; + + // Zero new bits + dMemset( newBits, 0, pixels * 4 ); + + // Copy Alpha values + for( U32 i = 0; i < pixels; i++ ) + newBits[i * 4 + 3] = oldBits[i]; + + // Now hose the old bits + delete [] *src; + *src = newBits; +} + +void (*bitmapConvertA8_to_RGBA)( U8 **src, U32 pixels ) = bitmapConvertA8_to_RGBA_c; diff --git a/Engine/source/gfx/bitmap/bitmapUtils.h b/Engine/source/gfx/bitmap/bitmapUtils.h new file mode 100644 index 000000000..4efa9031e --- /dev/null +++ b/Engine/source/gfx/bitmap/bitmapUtils.h @@ -0,0 +1,41 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _BITMAPUTILS_H_ +#define _BITMAPUTILS_H_ + +#ifndef _TORQUE_TYPES_H_ +#include "platform/types.h" +#endif + +extern void (*bitmapExtrude5551)(const void *srcMip, void *mip, U32 height, U32 width); +extern void (*bitmapExtrudeRGB)(const void *srcMip, void *mip, U32 height, U32 width); +extern void (*bitmapExtrudeRGBA)(const void *srcMip, void *mip, U32 height, U32 width); +extern void (*bitmapConvertRGB_to_5551)(U8 *src, U32 pixels); +extern void (*bitmapConvertRGB_to_1555)(U8 *src, U32 pixels); +extern void (*bitmapConvertRGB_to_RGBX)( U8 **src, U32 pixels ); +extern void (*bitmapConvertRGBX_to_RGB)( U8 **src, U32 pixels ); +extern void (*bitmapConvertA8_to_RGBA)( U8 **src, U32 pixels ); + +void bitmapExtrudeRGB_c(const void *srcMip, void *mip, U32 height, U32 width); + +#endif //_BITMAPUTILS_H_ diff --git a/Engine/source/gfx/bitmap/ddsFile.h b/Engine/source/gfx/bitmap/ddsFile.h new file mode 100644 index 000000000..3a530e610 --- /dev/null +++ b/Engine/source/gfx/bitmap/ddsFile.h @@ -0,0 +1,205 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _DDSFILE_H_ +#define _DDSFILE_H_ + +#ifndef _GFXSTRUCTS_H_ +#include "gfx/gfxStructs.h" +#endif +#ifndef _BITSET_H_ +#include "core/bitSet.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef __RESOURCE_H__ +#include "core/resource.h" +#endif + +class Stream; +class GBitmap; + + +struct DDSFile +{ + enum DDSFlags + { + ComplexFlag = BIT(0), ///< Indicates this includes a mipchain, cubemap, or + /// volume texture, ie, isn't a plain old bitmap. + MipMapsFlag = BIT(1), ///< Indicates we have a mipmap chain in the file. + CubeMapFlag = BIT(2), ///< Indicates we are a cubemap. Requires all six faces. + VolumeFlag = BIT(3), ///< Indicates we are a volume texture. + + PitchSizeFlag = BIT(4), ///< Cue as to how to interpret our pitchlinear value. + LinearSizeFlag = BIT(5), ///< Cue as to how to interpret our pitchlinear value. + + RGBData = BIT(6), ///< Indicates that this is straight out RGBA data. + CompressedData = BIT(7), ///< Indicates that this is compressed or otherwise + /// exotic data. + + /// These are the flags for which cubemap + /// surfaces are included in the file. + CubeMap_PosX_Flag = BIT(8), + CubeMap_NegX_Flag = BIT(9), + CubeMap_PosY_Flag = BIT(10), + CubeMap_NegY_Flag = BIT(11), + CubeMap_PosZ_Flag = BIT(12), + CubeMap_NegZ_Flag = BIT(13), + }; + + /// The index into mSurfaces for each + /// cubemap face. + enum + { + Cubemap_Surface_PosX, + Cubemap_Surface_NegX, + Cubemap_Surface_PosY, + Cubemap_Surface_NegY, + Cubemap_Surface_PosZ, + Cubemap_Surface_NegZ, + Cubemap_Surface_Count, + }; + + BitSet32 mFlags; + U32 mHeight; + U32 mWidth; + U32 mDepth; + U32 mPitchOrLinearSize; + U32 mMipMapCount; + + GFXFormat mFormat; + U32 mBytesPerPixel; ///< Ignored if we're a compressed texture. + U32 mFourCC; + String mCacheString; + Torque::Path mSourcePath; + + bool mHasTransparency; + + // This is ugly... but it allows us to pass the number of + // mips to drop into the ResourceManager loading process. + static U32 smDropMipCount; + + struct SurfaceData + { + SurfaceData() + { + VECTOR_SET_ASSOCIATION( mMips ); + } + + ~SurfaceData() + { + // Free our mips! + for(S32 i=0; i mMips; + + // Helper function to read in a mipchain. + bool readMipChain(); + + void dumpImage(DDSFile *dds, U32 mip, const char *file); + + /// Helper for reading a mip level. + void readNextMip(DDSFile *dds, Stream &s, U32 height, U32 width, U32 mipLevel, bool skip); + + /// Helper for writing a mip level. + void writeNextMip(DDSFile *dds, Stream &s, U32 height, U32 width, U32 mipLevel); + }; + + Vector mSurfaces; + + /// Clear all our information; used before reading. + void clear(); + + /// Reads a DDS file from the stream. + bool read(Stream &s, U32 dropMipCount); + + /// Called from read() to read in the DDS header. + bool readHeader(Stream &s); + + /// Writes this DDS file to the stream. + bool write(Stream &s); + + /// Called from write() to write the DDS header. + bool writeHeader(Stream &s); + + /// For our current format etc., what is the size of a surface with the + /// given dimensions? + U32 getSurfaceSize( U32 mipLevel = 0 ) const { return getSurfaceSize( mHeight, mWidth, mipLevel ); } + U32 getSurfaceSize( U32 height, U32 width, U32 mipLevel = 0 ) const; + + // Helper for getting the size in bytes of a compressed DDS texture. + static U32 getSizeInBytes( GFXFormat format, U32 height, U32 width, U32 mipLevels ); + + /// Returns the total video memory size of the texture + /// including all mipmaps and compression settings. + U32 getSizeInBytes() const; + + U32 getWidth( U32 mipLevel = 0 ) const { return getMax( U32(1), mWidth >> mipLevel ); } + U32 getHeight( U32 mipLevel = 0 ) const { return getMax(U32(1), mHeight >> mipLevel); } + U32 getDepth( U32 mipLevel = 0 ) const { return getMax(U32(1), mDepth >> mipLevel); } + + U32 getMipLevels() const { return mMipMapCount; } + + bool getHasTransparency() const { return mHasTransparency; } + + bool isCubemap() const { return mFlags.test( CubeMapFlag ); } + + GFXFormat getFormat() const { return mFormat; } + + U32 getSurfacePitch( U32 mipLevel = 0 ) const; + + const Torque::Path &getSourcePath() const { return mSourcePath; } + const String &getTextureCacheString() const { return mCacheString; } + + static Resource load( const Torque::Path &path, U32 dropMipCount ); + + // For debugging fun! + static S32 smActiveCopies; + + DDSFile() + { + VECTOR_SET_ASSOCIATION( mSurfaces ); + smActiveCopies++; + + mHasTransparency = false; + } + + DDSFile( const DDSFile &dds ); + + ~DDSFile() + { + smActiveCopies--; + + // Free our surfaces! + for(S32 i=0; imMips.size(); m++ ) + { + U32 size = dds.getSurfaceSize( m ); + surface->mMips.push_back(new U8[size]); + dMemcpy( surface->mMips.last(), dds.mSurfaces[i]->mMips[m], size ); + } + } +} + +void DDSFile::clear() +{ + mFlags = 0; + mHeight = mWidth = mDepth = mPitchOrLinearSize = mMipMapCount = 0; + mFormat = GFXFormatR8G8B8; +} + +U32 DDSFile::getSurfacePitch( U32 mipLevel ) const +{ + if(mFlags.test(CompressedData)) + { + U32 sizeMultiple = 0; + + switch(mFormat) + { + case GFXFormatDXT1: + sizeMultiple = 8; + break; + case GFXFormatDXT2: + case GFXFormatDXT3: + case GFXFormatDXT4: + case GFXFormatDXT5: + sizeMultiple = 16; + break; + default: + AssertISV(false, "DDSFile::getPitch - invalid compressed texture format, we only support DXT1-5 right now."); + break; + } + + // Maybe need to be DWORD aligned? + U32 align = getMax(U32(1), getWidth(mipLevel)/4) * sizeMultiple; + align += 3; align >>=2; align <<=2; + return align; + + } + else + return getWidth(mipLevel) * mBytesPerPixel; +} + +U32 DDSFile::getSurfaceSize( U32 height, U32 width, U32 mipLevel ) const +{ + // Bump by the mip level. + height = getMax(U32(1), height >> mipLevel); + width = getMax(U32(1), width >> mipLevel); + + if(mFlags.test(CompressedData)) + { + // From the directX docs: + // max(1, width ÷ 4) x max(1, height ÷ 4) x 8(DXT1) or 16(DXT2-5) + + U32 sizeMultiple = 0; + + switch(mFormat) + { + case GFXFormatDXT1: + sizeMultiple = 8; + break; + case GFXFormatDXT2: + case GFXFormatDXT3: + case GFXFormatDXT4: + case GFXFormatDXT5: + sizeMultiple = 16; + break; + default: + AssertISV(false, "DDSFile::getSurfaceSize - invalid compressed texture format, we only support DXT1-5 right now."); + break; + } + + return getMax(U32(1), width/4) * getMax(U32(1), height/4) * sizeMultiple; + } + else + { + return height * width* mBytesPerPixel; + } +} + +U32 DDSFile::getSizeInBytes() const +{ + // TODO: This doesn't take mDepth into account, so + // it doesn't work right for volume or cubemap textures! + + U32 bytes = 0; + for ( U32 i=0; i < mMipMapCount; i++ ) + bytes += getSurfaceSize( mHeight, mWidth, i ); + + return bytes; +} + +U32 DDSFile::getSizeInBytes( GFXFormat format, U32 height, U32 width, U32 mipLevels ) +{ + AssertFatal( format >= GFXFormatDXT1 && format <= GFXFormatDXT5, + "DDSFile::getSizeInBytes - Must be a DXT format!" ); + + // From the directX docs: + // max(1, width ÷ 4) x max(1, height ÷ 4) x 8(DXT1) or 16(DXT2-5) + + U32 sizeMultiple = 0; + if ( format == GFXFormatDXT1 ) + sizeMultiple = 8; + else + sizeMultiple = 16; + + U32 mipHeight, mipWidth; + U32 bytes = 0; + for ( U32 m=0; m < mipLevels; m++ ) + { + mipHeight = getMax( U32(1), height >> m ); + mipWidth = getMax( U32(1), width >> m ); + + bytes += getMax( U32(1), mipWidth / 4 ) * + getMax( U32(1), mipHeight / 4 ) * sizeMultiple; + } + + return bytes; +} + +bool DDSFile::readHeader(Stream &s) +{ + U32 tmp; + + // Read the FOURCC + s.read(&tmp); + + if(tmp != MakeFourCC('D', 'D', 'S', ' ')) + { + Con::errorf("DDSFile::readHeader - unexpected magic number, wanted 'DDS '!"); + return false; + } + + // Read the size of the header. + s.read(&tmp); + + if(tmp != 124) + { + Con::errorf("DDSFile::readHeader - incorrect header size. Expected 124 bytes."); + return false; + } + + // Read some flags... + U32 ddsdFlags; + s.read(&ddsdFlags); + + // "Always include DDSD_CAPS, DDSD_PIXELFORMAT, DDSD_WIDTH, DDSD_HEIGHT." + if(!(ddsdFlags & (DDSDCaps | DDSDPixelFormat | DDSDWidth | DDSDHeight))) + { + Con::errorf("DDSFile::readHeader - incorrect surface description flags."); + return false; + } + + // Read height and width (always present) + s.read(&mHeight); + s.read(&mWidth); + + // Read pitch or linear size. + + // First make sure we have valid flags (either linear size or pitch). + if((ddsdFlags & (DDSDLinearSize | DDSDPitch)) == (DDSDLinearSize | DDSDPitch)) + { + // Both are invalid! + Con::errorf("DDSFile::readHeader - encountered both DDSD_LINEARSIZE and DDSD_PITCH!"); + return false; + } + + // Ok, some flags are set, so let's do some reading. + s.read(&mPitchOrLinearSize); + + if(ddsdFlags & DDSDLinearSize) + { + mFlags.set(LinearSizeFlag); // ( mHeight / 4 ) * ( mWidth / 4 ) * DDSSIZE + } + else if (ddsdFlags & DDSDPitch) + { + mFlags.set(PitchSizeFlag); // ( mWidth / 4 ) * DDSSIZE ??? + } + else + { + // Neither set! This appears to be depressingly common. + // Con::warnf("DDSFile::readHeader - encountered neither DDSD_LINEARSIZE nor DDSD_PITCH!"); + } + + // Do we need to read depth? If so, we are a volume texture! + s.read(&mDepth); + + if(ddsdFlags & DDSDDepth) + { + mFlags.set(VolumeFlag); + } + else + { + // Wipe it if the flag wasn't set! + mDepth = 0; + } + + // Deal with mips! + s.read(&mMipMapCount); + + if(ddsdFlags & DDSDMipMapCount) + { + mFlags.set(MipMapsFlag); + } + else + { + // Wipe it if the flag wasn't set! + mMipMapCount = 1; + } + + // Deal with 11 DWORDS of reserved space (this reserved space brought to + // you by DirectDraw and the letters F and U). + for(U32 i=0; i<11; i++) + s.read(&tmp); + + // Now we're onto the pixel format! + s.read(&tmp); + + if(tmp != 32) + { + Con::errorf("DDSFile::readHeader - pixel format chunk has unexpected size!"); + return false; + } + + U32 ddpfFlags; + + s.read(&ddpfFlags); + + // Read the next few values so we can deal with them all in one go. + U32 pfFourCC, pfBitCount, pfRMask, pfGMask, pfBMask, pfAlphaMask; + + s.read(&pfFourCC); + s.read(&pfBitCount); + s.read(&pfRMask); + s.read(&pfGMask); + s.read(&pfBMask); + s.read(&pfAlphaMask); + + // Sanity check flags... + if(!(ddpfFlags & (DDPFRGB | DDPFFourCC | DDPFLUMINANCE))) + { + Con::errorf("DDSFile::readHeader - incoherent pixel flags, neither RGB, FourCC, or Luminance!"); + return false; + } + + // For now let's just dump the header info. + if(ddpfFlags & DDPFLUMINANCE) + { + mFlags.set(RGBData); + + mBytesPerPixel = pfBitCount / 8; + + bool hasAlpha = ddpfFlags & DDPFAlphaPixels; + + mHasTransparency = hasAlpha; + + // Try to match a format. + if(hasAlpha) + { + // If it has alpha it is one of... + // GFXFormatA8L8 + // GFXFormatA4L4 + + if(pfBitCount == 16) + mFormat = GFXFormatA8L8; + else if(pfBitCount == 8) + mFormat = GFXFormatA4L4; + else + { + Con::errorf("DDSFile::readHeader - unable to match alpha Luminance format!"); + return false; + } + } + else + { + // Otherwise it is one of... + // GFXFormatL16 + // GFXFormatL8 + + if(pfBitCount == 16) + mFormat = GFXFormatL16; + else if(pfBitCount == 8) + mFormat = GFXFormatL8; + else + { + Con::errorf("DDSFile::readHeader - unable to match non-alpha Luminance format!"); + return false; + } + } + } + else if(ddpfFlags & DDPFRGB) + { + mFlags.set(RGBData); + + //Con::printf("RGB Pixel format of DDS:"); + //Con::printf(" bitcount = %d (16, 24, 32)", pfBitCount); + mBytesPerPixel = pfBitCount / 8; + //Con::printf(" red mask = %x", pfRMask); + //Con::printf(" green mask = %x", pfGMask); + //Con::printf(" blue mask = %x", pfBMask); + + bool hasAlpha = false; + + if(ddpfFlags & DDPFAlphaPixels) + { + hasAlpha = true; + //Con::printf(" alpha mask = %x", pfAlphaMask); + } + else + { + //Con::printf(" no alpha."); + } + + mHasTransparency = hasAlpha; + + // Try to match a format. + if(hasAlpha) + { + // If it has alpha it is one of... + // GFXFormatR8G8B8A8 + // GFXFormatR5G5B5A1 + // GFXFormatA8 + + if(pfBitCount == 32) + mFormat = GFXFormatR8G8B8A8; + else if(pfBitCount == 16) + mFormat = GFXFormatR5G5B5A1; + else if(pfBitCount == 8) + mFormat = GFXFormatA8; + else + { + Con::errorf("DDSFile::readHeader - unable to match alpha RGB format!"); + return false; + } + } + else + { + // Otherwise it is one of... + // GFXFormatR8G8B8 + // GFXFormatR8G8B8X8 + // GFXFormatR5G6B5 + // GFXFormatL8 + + if(pfBitCount == 24) + mFormat = GFXFormatR8G8B8; + else if(pfBitCount == 32) + mFormat = GFXFormatR8G8B8X8; + else if(pfBitCount == 16) + mFormat = GFXFormatR5G6B5; + else if(pfBitCount == 8) + { + // luminance + mFormat = GFXFormatL8; + } + else + { + Con::errorf("DDSFile::readHeader - unable to match non-alpha RGB format!"); + return false; + } + } + + + // Sweet, all done. + } + else if (ddpfFlags & DDPFFourCC) + { + mHasTransparency = (ddpfFlags & DDPFAlphaPixels); + mFlags.set(CompressedData); + +/* Con::printf("FourCC Pixel format of DDS:"); + Con::printf(" fourcc = '%c%c%c%c'", ((U8*)&pfFourCC)[0], ((U8*)&pfFourCC)[1], ((U8*)&pfFourCC)[2], ((U8*)&pfFourCC)[3]); */ + + // Ok, make a format determination. + switch(pfFourCC) + { + case FOURCC_DXT1: + mFormat = GFXFormatDXT1; + break; + case FOURCC_DXT2: + mFormat = GFXFormatDXT2; + break; + case FOURCC_DXT3: + mFormat = GFXFormatDXT3; + break; + case FOURCC_DXT4: + mFormat = GFXFormatDXT4; + break; + case FOURCC_DXT5: + mFormat = GFXFormatDXT5; + break; + default: + Con::errorf("DDSFile::readHeader - unknown fourcc = '%c%c%c%c'", ((U8*)&pfFourCC)[0], ((U8*)&pfFourCC)[1], ((U8*)&pfFourCC)[2], ((U8*)&pfFourCC)[3]); + break; + } + + } + + // Deal with final caps bits... Is this really necessary? + + U32 caps1, caps2; + s.read(&caps1); + s.read(&caps2); + s.read(&tmp); + s.read(&tmp); // More icky reserved space. + + // Screw caps1. + // if(!(caps1 & DDSCAPS_TEXTURE))) + // { + // } + + // Caps2 has cubemap/volume info. Care about that. + if(caps2 & DDSCAPS2Cubemap) + { + mFlags.set(CubeMapFlag); + + // Store the face flags too. + if ( caps2 & DDSCAPS2Cubemap_POSITIVEX ) mFlags.set( CubeMap_PosX_Flag ); + if ( caps2 & DDSCAPS2Cubemap_NEGATIVEX ) mFlags.set( CubeMap_NegX_Flag ); + if ( caps2 & DDSCAPS2Cubemap_POSITIVEY ) mFlags.set( CubeMap_PosY_Flag ); + if ( caps2 & DDSCAPS2Cubemap_NEGATIVEY ) mFlags.set( CubeMap_NegY_Flag ); + if ( caps2 & DDSCAPS2Cubemap_POSITIVEZ ) mFlags.set( CubeMap_PosZ_Flag ); + if ( caps2 & DDSCAPS2Cubemap_NEGATIVEZ ) mFlags.set( CubeMap_NegZ_Flag ); + } + + // MS has ANOTHER reserved word here. This one particularly sucks. + s.read(&tmp); + + return true; +} + +bool DDSFile::read(Stream &s, U32 dropMipCount) +{ + if( !readHeader(s) || mMipMapCount == 0 ) + { + Con::errorf("DDSFile::read - error reading header!"); + return false; + } + + // If we're droping mips then make sure we have enough. + dropMipCount = getMin( dropMipCount, mMipMapCount - 1 ); + + // At this point we know what sort of image we contain. So we should + // allocate some buffers, and read it in. + + // How many surfaces are we talking about? + if(mFlags.test(CubeMapFlag)) + { + mSurfaces.setSize( Cubemap_Surface_Count ); + + for ( U32 i=0; i < Cubemap_Surface_Count; i++ ) + { + // Does the cubemap contain this surface? + if ( mFlags.test( CubeMap_PosX_Flag + ( i << 1 ) ) ) + mSurfaces[i] = new SurfaceData(); + else + { + mSurfaces[i] = NULL; + continue; + } + + // Load all the mips. + for(S32 l=0; lreadNextMip(this, s, mHeight, mWidth, l, l < dropMipCount ); + } + + } + else if (mFlags.test(VolumeFlag)) + { + // Do something with volume + } + else + { + // It's a plain old texture. + + // First allocate a SurfaceData to stick this in. + mSurfaces.push_back(new SurfaceData()); + + // Load however many mips there are. + for(S32 i=0; ireadNextMip(this, s, mHeight, mWidth, i, i < dropMipCount); + + // Ok, we're done. + } + + // If we're dropping mips then fix up the stats. + if ( dropMipCount > 0 ) + { + // Fix up the pitch and/or linear size. + if( mFlags.test( LinearSizeFlag ) ) + mPitchOrLinearSize = getSurfaceSize( dropMipCount ); + else if ( mFlags.test( PitchSizeFlag ) ) + mPitchOrLinearSize = getSurfacePitch( dropMipCount ); + else + mPitchOrLinearSize = mPitchOrLinearSize; // Do nothing? + + // Now fix up the rest of the + mMipMapCount = getMax( (U32)1, mMipMapCount - dropMipCount ); + mHeight = getHeight( dropMipCount ); + mWidth = getWidth( dropMipCount ); + } + + return true; +} + +bool DDSFile::writeHeader( Stream &s ) +{ + // Read the FOURCC + s.write( 4, "DDS " ); + + U32 tmp = 0; + + // Read the size of the header. + s.write( 124 ); + + // Read some flags... + U32 ddsdFlags = DDSDCaps | DDSDPixelFormat | DDSDWidth | DDSDHeight; + + if ( mFlags.test( CompressedData ) ) + ddsdFlags |= DDSDLinearSize; + else + ddsdFlags |= DDSDPitch; + + if ( mMipMapCount > 0 ) + ddsdFlags |= DDSDMipMapCount; + + s.write( ddsdFlags ); + + // Read height and width (always present) + s.write( mHeight ); + s.write( mWidth ); + + // Ok, some flags are set, so let's do some reading. + s.write( mPitchOrLinearSize ); + + // Do we need to read depth? If so, we are a volume texture! + s.write( mDepth ); + + // Deal with mips! + s.write( mMipMapCount ); + + // Deal with 11 DWORDS of reserved space (this reserved space brought to + // you by DirectDraw and the letters F and U). + for(U32 i=0; i<11; i++) + s.write( tmp ); // is this right? + + // Now we're onto the pixel format! + + // This is the size, in bits, + // of the pixel format data. + tmp = 32; + s.write( tmp ); + + U32 ddpfFlags; + + U32 fourCC = 0; + + if ( mFlags.test( CompressedData ) ) + { + ddpfFlags = DDPFFourCC; + if (mFormat == GFXFormatDXT1) + fourCC = FOURCC_DXT1; + if (mFormat == GFXFormatDXT3) + fourCC = FOURCC_DXT3; + if (mFormat == GFXFormatDXT5) + fourCC = FOURCC_DXT5; + } + else + ddpfFlags = mBytesPerPixel == 4 ? DDPFRGB | DDPFAlphaPixels : DDPFRGB; + + s.write( ddpfFlags ); + + // Read the next few values so we can deal with them all in one go. + //U32 pfFourCC, pfBitCount, pfRMask, pfGMask, pfBMask, pfAlphaMask; + + s.write( fourCC ); + s.write( mBytesPerPixel * 8 ); + s.write( 0x000000FF ); + s.write( 0x00FF0000 ); + s.write( 0x0000FF00 ); + s.write( 0xFF000000 ); + + // Deal with final caps bits... Is this really necessary? + + U32 caps1 = DDSCAPSTexture; + if ( mMipMapCount > 0 ) + caps1 |= DDSCAPSComplex | DDSCAPSMipMap; + + tmp = 0; + + s.write( caps1 ); + s.write( tmp ); + s.write( tmp ); + s.write( tmp );// More icky reserved space. + + // MS has ANOTHER reserved word here. This one particularly sucks. + s.write( tmp ); + + return true; +} + +bool DDSFile::write( Stream &s ) +{ + if(!writeHeader(s)) + { + Con::errorf("DDSFile::write - error writing header!"); + return false; + } + + // At this point we know what sort of image we contain. So we should + // allocate some buffers, and read it in. + + // How many surfaces are we talking about? + if(mFlags.test(CubeMapFlag)) + { + // Do something with cubemaps. + } + else if (mFlags.test(VolumeFlag)) + { + // Do something with volume + } + else + { + // It's a plain old texture. + + // Load however many mips there are. + for ( S32 i = 0; i < mMipMapCount; i++ ) + mSurfaces.last()->writeNextMip(this, s, mHeight, mWidth, i); + + // Ok, we're done. + } + + return true; +} + +void DDSFile::SurfaceData::dumpImage(DDSFile *dds, U32 mip, const char *file) +{ + GBitmap *foo = new GBitmap(dds->mWidth >> mip, dds->mHeight >> mip, false, dds->mFormat); + + // Copy our data in. + dMemcpy(foo->getWritableBits(), mMips[mip], dds->getSurfaceSize(dds->mHeight, dds->mWidth, mip) ); + + FileStream stream; + + stream.open( file, Torque::FS::File::Write ); + + if ( stream.getStatus() == Stream::Ok ) + { + // Write it out. + foo->writeBitmap("png", stream); + } + + // Clean up. + delete foo; +} + +void DDSFile::SurfaceData::readNextMip(DDSFile *dds, Stream &s, U32 height, U32 width, U32 mipLevel, bool skip) +{ + U32 size = dds->getSurfaceSize(height, width, mipLevel); + + // If we're skipping this mip then seek forward. + if ( skip ) + s.setPosition( s.getPosition() + size ); + else + { + mMips.push_back(new U8[size]); + if(!s.read(size, mMips.last())) + Con::errorf("DDSFile::SurfaceData::addNextMip - failed to read mip!"); + } +} + +void DDSFile::SurfaceData::writeNextMip(DDSFile *dds, Stream &s, U32 height, U32 width, U32 mipLevel) +{ + U32 size = dds->getSurfaceSize(height, width, mipLevel); + if(!s.write(size, mMips[mipLevel])) + Con::errorf("DDSFile::SurfaceData::writeNextMip - failed to write mip!"); +} + +//------------------------------------------------------------------------------ + +template<> void *Resource::create( const Torque::Path &path ) +{ +#ifdef TORQUE_DEBUG_RES_MANAGER + Con::printf( "Resource::create - [%s]", path.getFullPath().c_str() ); +#endif + + FileStream stream; + + stream.open( path.getFullPath(), Torque::FS::File::Read ); + + if ( stream.getStatus() != Stream::Ok ) + return NULL; + + DDSFile *retDDS = new DDSFile; + + if( !retDDS->read( stream, DDSFile::smDropMipCount ) ) + { + delete retDDS; + return NULL; + } + else + { + // Set source file name + retDDS->mSourcePath = path; + retDDS->mCacheString = Torque::Path::Join( path.getRoot(), ':', path.getPath() ); + retDDS->mCacheString = Torque::Path::Join( retDDS->mCacheString, '/', path.getFileName() ); + } + + return retDDS; +} + +template<> ResourceBase::Signature Resource::signature() +{ + return MakeFourCC('D','D','S',' '); // Direct Draw Surface +} + +Resource DDSFile::load( const Torque::Path &path, U32 dropMipCount ) +{ + PROFILE_SCOPE( DDSFile_load ); + + // HACK: It sucks that we cannot pass parameters into + // the resource manager loading system. + DDSFile::smDropMipCount = dropMipCount; + Resource ret = ResourceManager::get().load( path ); + DDSFile::smDropMipCount = 0; + + // Any kind of error checking or path stepping can happen here + + return ret; +} + +//------------------------------------------------------------------------------ + +DDSFile *DDSFile::createDDSFileFromGBitmap( const GBitmap *gbmp ) +{ + if( gbmp == NULL ) + return NULL; + + DDSFile *ret = new DDSFile; + + // Set up the DDSFile properties that matter. Since this is a GBitmap, there + // are assumptions that can be made + ret->mHeight = gbmp->getHeight(); + ret->mWidth = gbmp->getWidth(); + ret->mDepth = 0; + ret->mFormat = gbmp->getFormat(); + ret->mFlags.set(RGBData); + ret->mBytesPerPixel = gbmp->getBytesPerPixel(); + ret->mMipMapCount = gbmp->getNumMipLevels(); + ret->mHasTransparency = gbmp->getHasTransparency(); + + // ASSUMPTION!!! + // This _most likely_ does not belong here, but it is safe to assume that if + // a GBitmap is 24-bit, and it's being converted to a DDS, it is most likely + // going to be either: + // a) Uploaded as a 32-bit texture, and just needs to be padded to RGBX + // b) Uploaded as a compressed format, and needs to be padded to 32-bits anyway + if( ret->mFormat == GFXFormatR8G8B8 ) + { + ret->mFormat = GFXFormatR8G8B8X8; + ret->mBytesPerPixel = 4; + } + + if( ret->mMipMapCount > 1 ) + ret->mFlags.set(MipMapsFlag); + + // One surface per GBitmap + ret->mSurfaces.push_back( new SurfaceData() ); + + // Load the mips + for( int i = 0; i < ret->mMipMapCount; i++ ) + { + const U32 mipSz = ret->getSurfaceSize(i); + ret->mSurfaces.last()->mMips.push_back( new U8[mipSz] ); + + U8 *mipMem = ret->mSurfaces.last()->mMips.last(); + + // If this is a straight copy, just do it, otherwise (ugh) + if( ret->mFormat == gbmp->getFormat() ) + dMemcpy( mipMem, gbmp->getBits(i), mipSz ); + else + { + // Assumption: + AssertFatal( gbmp->getBytesPerPixel() + 1 == ret->mBytesPerPixel, "Assumption failed, not 24->32 bit straight convert." ); + + for( int pxl = 0; pxl < gbmp->getWidth(i) * gbmp->getHeight(i); pxl++ ) + { + U8 *dst = &mipMem[pxl * ret->mBytesPerPixel]; + const U8 *src = &gbmp->getBits(i)[pxl * gbmp->getBytesPerPixel()]; + dMemcpy( dst, src, gbmp->getBytesPerPixel() * sizeof(U8) ); + dst[ret->mBytesPerPixel - 1] = 255; + } + } + + // Uncomment to debug-dump each mip level + //ret->mSurfaces.last()->dumpImage( ret, i, avar( "%d_Gbmp_xmip%d", ret, i ) ); + } + + return ret; +} + +DefineEngineFunction( getActiveDDSFiles, S32, (),, + "Returns the count of active DDSs files in memory.\n" + "@ingroup Rendering\n" ) +{ + return DDSFile::smActiveCopies; +} \ No newline at end of file diff --git a/Engine/source/gfx/bitmap/ddsUtils.cpp b/Engine/source/gfx/bitmap/ddsUtils.cpp new file mode 100644 index 000000000..e190efcac --- /dev/null +++ b/Engine/source/gfx/bitmap/ddsUtils.cpp @@ -0,0 +1,116 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "squish/squish.h" +#include "gfx/bitmap/ddsFile.h" +#include "gfx/bitmap/ddsUtils.h" + +//------------------------------------------------------------------------------ + +// If false is returned, from this method, the source DDS is not modified +bool DDSUtil::squishDDS( DDSFile *srcDDS, const GFXFormat dxtFormat ) +{ + // Sanity check + if( srcDDS->mBytesPerPixel != 4 ) + { + AssertFatal( false, "Squish wants 32-bit source data" ); + return false; + } + + // Build flags, start with fast compress + U32 squishFlags = squish::kColourRangeFit; + + // Flag which format we are using + switch( dxtFormat ) + { + case GFXFormatDXT1: + squishFlags |= squish::kDxt1; + break; + + case GFXFormatDXT2: + case GFXFormatDXT3: + squishFlags |= squish::kDxt3; + break; + + case GFXFormatDXT4: + case GFXFormatDXT5: + squishFlags |= squish::kDxt5; + break; + + default: + AssertFatal( false, "Assumption failed" ); + return false; + break; + } + + // We got this far, so assume we can finish (gosh I hope so) + srcDDS->mFormat = dxtFormat; + srcDDS->mFlags.set( DDSFile::CompressedData ); + + // If this has alpha, set the flag + if( srcDDS->mFormat == GFXFormatR8G8B8A8 ) + squishFlags |= squish::kWeightColourByAlpha; + + // The source surface is the original surface of the file + DDSFile::SurfaceData *srcSurface = srcDDS->mSurfaces.last(); + + // Create a new surface, this will be the DXT compressed surface. Once we + // are done, we can discard the old surface, and replace it with this one. + DDSFile::SurfaceData *newSurface = new DDSFile::SurfaceData(); + + for( int i = 0; i < srcDDS->mMipMapCount; i++ ) + { + const U8 *srcBits = srcSurface->mMips[i]; + + const U32 mipSz = srcDDS->getSurfaceSize(i); + U8 *dstBits = new U8[mipSz]; + newSurface->mMips.push_back( dstBits ); + + PROFILE_START(SQUISH_DXT_COMPRESS); + + // Compress with Squish + // + // squish::CompressImageOMP will call squish::CompressImage if OpenMP is + // not enabled. + squish::CompressImageOMP( srcBits, srcDDS->getWidth(i), srcDDS->getHeight(i), + dstBits, squishFlags ); + + PROFILE_END(); + } + + // Now delete the source surface, and return. + srcDDS->mSurfaces.pop_back(); + delete srcSurface; + srcDDS->mSurfaces.push_back( newSurface ); + + return true; +} + +//------------------------------------------------------------------------------ + +void DDSUtil::swizzleDDS( DDSFile *srcDDS, const Swizzle &swizzle ) +{ + for( int i = 0; i < srcDDS->mMipMapCount; i++ ) + { + swizzle.InPlace( srcDDS->mSurfaces.last()->mMips[i], srcDDS->getSurfaceSize( i ) ); + } +} \ No newline at end of file diff --git a/Engine/source/gfx/bitmap/ddsUtils.h b/Engine/source/gfx/bitmap/ddsUtils.h new file mode 100644 index 000000000..0db66c8a4 --- /dev/null +++ b/Engine/source/gfx/bitmap/ddsUtils.h @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _DDS_UTILS_H_ +#define _DDS_UTILS_H_ + +struct DDSFile; + +namespace DDSUtil +{ + bool squishDDS( DDSFile *srcDDS, const GFXFormat dxtFormat ); + void swizzleDDS( DDSFile *srcDDS, const Swizzle &swizzle ); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/gfx/bitmap/gBitmap.cpp b/Engine/source/gfx/bitmap/gBitmap.cpp new file mode 100644 index 000000000..a2a64cf5f --- /dev/null +++ b/Engine/source/gfx/bitmap/gBitmap.cpp @@ -0,0 +1,1184 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/bitmap/gBitmap.h" + +#include "core/resourceManager.h" +#include "core/stream/fileStream.h" +#include "core/strings/stringFunctions.h" +#include "core/color.h" +#include "gfx/bitmap/bitmapUtils.h" +#include "math/mRect.h" +#include "console/console.h" +#include "platform/profiler.h" +#include "console/engineAPI.h" + + +const U32 GBitmap::csFileVersion = 3; + +Vector GBitmap::sRegistrations( __FILE__, __LINE__ ); + + +GBitmap::GBitmap() + : mInternalFormat(GFXFormatR8G8B8), + mBits(NULL), + mByteSize(0), + mWidth(0), + mHeight(0), + mBytesPerPixel(0), + mNumMipLevels(0), + mHasTransparency(false) +{ + for (U32 i = 0; i < c_maxMipLevels; i++) + mMipLevelOffsets[i] = 0xffffffff; +} + +GBitmap::GBitmap(const GBitmap& rCopy) +{ + mInternalFormat = rCopy.mInternalFormat; + + mByteSize = rCopy.mByteSize; + mBits = new U8[mByteSize]; + dMemcpy(mBits, rCopy.mBits, mByteSize); + + mWidth = rCopy.mWidth; + mHeight = rCopy.mHeight; + mBytesPerPixel = rCopy.mBytesPerPixel; + mNumMipLevels = rCopy.mNumMipLevels; + dMemcpy(mMipLevelOffsets, rCopy.mMipLevelOffsets, sizeof(mMipLevelOffsets)); + + mHasTransparency = rCopy.mHasTransparency; +} + + +GBitmap::GBitmap(const U32 in_width, + const U32 in_height, + const bool in_extrudeMipLevels, + const GFXFormat in_format) + : mBits(NULL), + mByteSize(0) +{ + for (U32 i = 0; i < c_maxMipLevels; i++) + mMipLevelOffsets[i] = 0xffffffff; + + allocateBitmap(in_width, in_height, in_extrudeMipLevels, in_format); + + mHasTransparency = false; +} + +GBitmap::GBitmap(const U32 in_width, + const U32 in_height, + const U8* data ) + : mBits(NULL), + mByteSize(0) +{ + allocateBitmap(in_width, in_height, false, GFXFormatR8G8B8A8); + + mHasTransparency = false; + + for (U32 x = 0; x < in_width; x++) + { + for (U32 y = 0; y < in_height; y++) + { + U32 offset = (x + y * in_width) * 4; + + ColorI color(data[offset], + data[offset + 1], + data[offset + 2], + data[offset + 3]); + + if (color.alpha < 255) + mHasTransparency = true; + + setColor(x, y, color); + } + } +} + + +//-------------------------------------------------------------------------- + +GBitmap::~GBitmap() +{ + deleteImage(); +} + +//-------------------------------------------------------------------------- + +void GBitmap::sRegisterFormat( const GBitmap::Registration ® ) +{ + U32 insert = sRegistrations.size(); + for ( U32 i = 0; i < sRegistrations.size(); i++ ) + { + if ( sRegistrations[i].priority <= reg.priority ) + { + insert = i; + break; + } + } + + sRegistrations.insert( insert, reg ); +} + +const GBitmap::Registration *GBitmap::sFindRegInfo( const String &extension ) +{ + for ( U32 i = 0; i < GBitmap::sRegistrations.size(); i++ ) + { + const GBitmap::Registration ® = GBitmap::sRegistrations[i]; + const Vector &extensions = reg.extensions; + + for ( U32 j = 0; j < extensions.size(); ++j ) + { + if ( extensions[j].equal( extension, String::NoCase ) ) + return ® + } + } + + return NULL; +} + +bool GBitmap::sFindFile( const Path &path, Path *outPath ) +{ + PROFILE_SCOPE( GBitmap_sFindFile ); + + const String origExt( String::ToLower( path.getExtension() ) ); + + Path tryPath( path ); + + for ( U32 i = 0; i < sRegistrations.size(); i++ ) + { + const Registration ® = sRegistrations[i]; + const Vector &extensions = reg.extensions; + + for ( U32 j = 0; j < extensions.size(); ++j ) + { + // We've already tried this one. + if ( extensions[j] == origExt ) + continue; + + tryPath.setExtension( extensions[j] ); + if ( !Torque::FS::IsFile( tryPath ) ) + continue; + + if ( outPath ) + *outPath = tryPath; + return true; + } + } + + return false; +} + +bool GBitmap::sFindFiles( const Path &path, Vector *outFoundPaths ) +{ + PROFILE_SCOPE( GBitmap_sFindFiles ); + + Path tryPath( path ); + + for ( U32 i = 0; i < GBitmap::sRegistrations.size(); i++ ) + { + const GBitmap::Registration ® = GBitmap::sRegistrations[i]; + const Vector &extensions = reg.extensions; + + for ( U32 j = 0; j < extensions.size(); ++j ) + { + tryPath.setExtension( extensions[j] ); + + if ( Torque::FS::IsFile( tryPath ) ) + { + if ( outFoundPaths ) + outFoundPaths->push_back( tryPath ); + else + return true; + } + } + } + + return outFoundPaths ? outFoundPaths->size() > 0 : false; +} + +String GBitmap::sGetExtensionList() +{ + String list; + + for ( U32 i = 0; i < sRegistrations.size(); i++ ) + { + const Registration ® = sRegistrations[i]; + for ( U32 j = 0; j < reg.extensions.size(); j++ ) + { + list += reg.extensions[j]; + list += " "; + } + } + + return list; +} + +//-------------------------------------------------------------------------- +void GBitmap::deleteImage() +{ + delete [] mBits; + mBits = NULL; + mByteSize = 0; + + mWidth = 0; + mHeight = 0; + mNumMipLevels = 0; +} + + +//-------------------------------------------------------------------------- + +void GBitmap::copyRect(const GBitmap *src, const RectI &srcRect, const Point2I &dstPt, const U32 srcMipLevel, const U32 dstMipLevel) +{ + if(src->getFormat() != getFormat()) + return; + if(srcRect.extent.x + srcRect.point.x > src->getWidth(srcMipLevel) || srcRect.extent.y + srcRect.point.y > src->getHeight(srcMipLevel)) + return; + if(srcRect.extent.x + dstPt.x > getWidth(dstMipLevel) || srcRect.extent.y + dstPt.y > getHeight(dstMipLevel)) + return; + + for(U32 i = 0; i < srcRect.extent.y; i++) + { + dMemcpy(getAddress(dstPt.x, dstPt.y + i, dstMipLevel), + src->getAddress(srcRect.point.x, srcRect.point.y + i, srcMipLevel), + mBytesPerPixel * srcRect.extent.x); + } +} + +//-------------------------------------------------------------------------- +void GBitmap::allocateBitmap(const U32 in_width, const U32 in_height, const bool in_extrudeMipLevels, const GFXFormat in_format ) +{ + //-------------------------------------- Some debug checks... + U32 svByteSize = mByteSize; + U8 *svBits = mBits; + + AssertFatal(in_width != 0 && in_height != 0, "GBitmap::allocateBitmap: width or height is 0"); + + if (in_extrudeMipLevels == true) + { + AssertFatal(isPow2(in_width) == true && isPow2(in_height) == true, "GBitmap::GBitmap: in order to extrude mip levels, bitmap w/h must be pow2"); + } + + mInternalFormat = in_format; + mWidth = in_width; + mHeight = in_height; + + mBytesPerPixel = 1; + switch (mInternalFormat) + { + case GFXFormatA8: + case GFXFormatL8: mBytesPerPixel = 1; + break; + case GFXFormatR8G8B8: mBytesPerPixel = 3; + break; + case GFXFormatR8G8B8X8: + case GFXFormatR8G8B8A8: mBytesPerPixel = 4; + break; + case GFXFormatR5G6B5: + case GFXFormatR5G5B5A1: mBytesPerPixel = 2; + break; + default: + AssertFatal(false, "GBitmap::GBitmap: misunderstood format specifier"); + break; + } + + // Set up the mip levels, if necessary... + mNumMipLevels = 1; + U32 allocPixels = in_width * in_height * mBytesPerPixel; + mMipLevelOffsets[0] = 0; + + + if (in_extrudeMipLevels == true) + { + U32 currWidth = in_width; + U32 currHeight = in_height; + + do + { + mMipLevelOffsets[mNumMipLevels] = mMipLevelOffsets[mNumMipLevels - 1] + + (currWidth * currHeight * mBytesPerPixel); + currWidth >>= 1; + currHeight >>= 1; + if (currWidth == 0) currWidth = 1; + if (currHeight == 0) currHeight = 1; + + mNumMipLevels++; + allocPixels += currWidth * currHeight * mBytesPerPixel; + } while (currWidth != 1 || currHeight != 1); + } + AssertFatal(mNumMipLevels <= c_maxMipLevels, "GBitmap::allocateBitmap: too many miplevels"); + + // Set up the memory... + mByteSize = allocPixels; + mBits = new U8[mByteSize]; + + dMemset(mBits, 0xFF, mByteSize); + + if(svBits != NULL) + { + dMemcpy(mBits, svBits, getMin(mByteSize, svByteSize)); + delete[] svBits; + } +} + +//-------------------------------------------------------------------------- +void GBitmap::extrudeMipLevels(bool clearBorders) +{ + if(mNumMipLevels == 1) + allocateBitmap(getWidth(), getHeight(), true, getFormat()); + + switch (getFormat()) + { + case GFXFormatR5G5B5A1: + { + for(U32 i = 1; i < mNumMipLevels; i++) + bitmapExtrude5551(getBits(i - 1), getWritableBits(i), getHeight(i), getWidth(i)); + break; + } + + case GFXFormatR8G8B8: + { + for(U32 i = 1; i < mNumMipLevels; i++) + bitmapExtrudeRGB(getBits(i - 1), getWritableBits(i), getHeight(i-1), getWidth(i-1)); + break; + } + + case GFXFormatR8G8B8A8: + case GFXFormatR8G8B8X8: + { + for(U32 i = 1; i < mNumMipLevels; i++) + bitmapExtrudeRGBA(getBits(i - 1), getWritableBits(i), getHeight(i-1), getWidth(i-1)); + break; + } + + default: + break; + } + if (clearBorders) + { + for (U32 i = 1; i> shift; + AssertFatal(newVal <= 255, "Error, oob"); + pMipBits[j] = U8(newVal); + } + } + AssertFatal(getWidth(mNumMipLevels - 1) == 1 && getHeight(mNumMipLevels - 1) == 1, + "Error, last miplevel should be 1x1!"); + ((U8*)getWritableBits(mNumMipLevels - 1))[0] = 0x80; + ((U8*)getWritableBits(mNumMipLevels - 1))[1] = 0x80; + ((U8*)getWritableBits(mNumMipLevels - 1))[2] = 0x80; +} + +//-------------------------------------------------------------------------- +bool GBitmap::setFormat(GFXFormat fmt) +{ + if (getFormat() == fmt) + return true; + + PROFILE_SCOPE(GBitmap_setFormat); + + // this is a nasty pointer math hack + // is there a quick way to calc pixels of a fully mipped bitmap? + U32 pixels = 0; + for (U32 i=0; i < mNumMipLevels; i++) + pixels += getHeight(i) * getWidth(i); + + switch( getFormat() ) + { + case GFXFormatR8G8B8: + switch ( fmt ) + { + case GFXFormatR5G5B5A1: +#ifdef _XBOX + bitmapConvertRGB_to_1555(mBits, pixels); +#else + bitmapConvertRGB_to_5551(mBits, pixels); +#endif + mInternalFormat = GFXFormatR5G5B5A1; + mBytesPerPixel = 2; + break; + + case GFXFormatR8G8B8A8: + case GFXFormatR8G8B8X8: + // Took this out, it may crash -patw + //AssertFatal( mNumMipLevels == 1, "Do the mip-mapping in hardware." ); + + bitmapConvertRGB_to_RGBX( &mBits, pixels ); + mInternalFormat = fmt; + mBytesPerPixel = 4; + mByteSize = pixels * 4; + break; + + default: + AssertWarn(0, "GBitmap::setFormat: unable to convert bitmap to requested format."); + return false; + } + break; + + case GFXFormatR8G8B8X8: + switch( fmt ) + { + // No change needed for this + case GFXFormatR8G8B8A8: + mInternalFormat = GFXFormatR8G8B8A8; + break; + + case GFXFormatR8G8B8: + bitmapConvertRGBX_to_RGB( &mBits, pixels ); + mInternalFormat = GFXFormatR8G8B8; + mBytesPerPixel = 3; + mByteSize = pixels * 3; + break; + + default: + AssertWarn(0, "GBitmap::setFormat: unable to convert bitmap to requested format."); + return false; + } + break; + + case GFXFormatR8G8B8A8: + switch( fmt ) + { + // No change needed for this + case GFXFormatR8G8B8X8: + mInternalFormat = GFXFormatR8G8B8X8; + break; + + case GFXFormatR8G8B8: + bitmapConvertRGBX_to_RGB( &mBits, pixels ); + mInternalFormat = GFXFormatR8G8B8; + mBytesPerPixel = 3; + mByteSize = pixels * 3; + break; + + default: + AssertWarn(0, "GBitmap::setFormat: unable to convert bitmap to requested format."); + return false; + } + break; + + case GFXFormatA8: + switch( fmt ) + { + case GFXFormatR8G8B8A8: + mInternalFormat = GFXFormatR8G8B8A8; + bitmapConvertA8_to_RGBA( &mBits, pixels ); + mBytesPerPixel = 4; + mByteSize = pixels * 4; + break; + + default: + AssertWarn(0, "GBitmap::setFormat: unable to convert bitmap to requested format."); + return false; + } + break; + + default: + AssertWarn(0, "GBitmap::setFormat: unable to convert bitmap to requested format."); + return false; + } + + U32 offset = 0; + for (U32 j=0; j < mNumMipLevels; j++) + { + mMipLevelOffsets[j] = offset; + offset += getHeight(j) * getWidth(j) * mBytesPerPixel; + } + + return true; +} + +//------------------------------------------------------------------------------ + +bool GBitmap::checkForTransparency() +{ + mHasTransparency = false; + + ColorI pixel(255, 255, 255, 255); + + switch (mInternalFormat) + { + // Non-transparent formats + case GFXFormatL8: + case GFXFormatR8G8B8: + case GFXFormatR5G6B5: + break; + // Transparent formats + case GFXFormatA8: + case GFXFormatR8G8B8A8: + case GFXFormatR5G5B5A1: + // Let getColor() do the heavy lifting + for (U32 x = 0; x < mWidth; x++) + { + for (U32 y = 0; y < mHeight; y++) + { + if (getColor(x, y, pixel)) + { + if (pixel.alpha < 255) + { + mHasTransparency = true; + break; + } + } + } + } + + break; + default: + AssertFatal(false, "GBitmap::checkForTransparency: misunderstood format specifier"); + break; + } + + return mHasTransparency; +} + +//------------------------------------------------------------------------------ +ColorF GBitmap::sampleTexel(F32 u, F32 v) const +{ + ColorF col(0.5f, 0.5f, 0.5f); + // normally sampling wraps all the way around at 1.0, + // but locking doesn't support this, and we seem to calc + // the uv based on a clamped 0 - 1... + Point2F max((F32)(getWidth()-1), (F32)(getHeight()-1)); + Point2F posf; + posf.x = mClampF(((u) * max.x), 0.0f, max.x); + posf.y = mClampF(((v) * max.y), 0.0f, max.y); + Point2I posi((S32)posf.x, (S32)posf.y); + + const U8 *buffer = getBits(); + U32 lexelindex = ((posi.y * getWidth()) + posi.x) * mBytesPerPixel; + + if(mBytesPerPixel == 2) + { + //U16 *buffer = (U16 *)lockrect->pBits; + } + else if(mBytesPerPixel > 2) + { + col.red = F32(buffer[lexelindex + 0]) / 255.0f; + col.green = F32(buffer[lexelindex + 1]) / 255.0f; + col.blue = F32(buffer[lexelindex + 2]) / 255.0f; + } + + return col; +} + +//-------------------------------------------------------------------------- +bool GBitmap::getColor(const U32 x, const U32 y, ColorI& rColor) const +{ + if (x >= mWidth || y >= mHeight) + return false; + + const U8* pLoc = getAddress(x, y); + + switch (mInternalFormat) { + case GFXFormatA8: + case GFXFormatL8: + rColor.set( *pLoc, *pLoc, *pLoc, *pLoc ); + break; + + case GFXFormatR8G8B8: + case GFXFormatR8G8B8X8: + rColor.set( pLoc[0], pLoc[1], pLoc[2], 255 ); + break; + + case GFXFormatR8G8B8A8: + rColor.set( pLoc[0], pLoc[1], pLoc[2], pLoc[3] ); + break; + + case GFXFormatR5G5B5A1: +#if defined(TORQUE_OS_MAC) + rColor.set( (*((U16*)pLoc) >> 0) & 0x1F, + (*((U16*)pLoc) >> 5) & 0x1F, + (*((U16*)pLoc) >> 10) & 0x1F, + ((*((U16*)pLoc) >> 15) & 0x01) ? 255 : 0 ); +#else + rColor.set( *((U16*)pLoc) >> 11, + (*((U16*)pLoc) >> 6) & 0x1f, + (*((U16*)pLoc) >> 1) & 0x1f, + (*((U16*)pLoc) & 1) ? 255 : 0 ); +#endif + break; + + default: + AssertFatal(false, "Bad internal format"); + return false; + } + + return true; +} + + +//-------------------------------------------------------------------------- + + +bool GBitmap::setColor(const U32 x, const U32 y, const ColorI& rColor) +{ + if (x >= mWidth || y >= mHeight) + return false; + + U8* pLoc = getAddress(x, y); + + switch (mInternalFormat) { + case GFXFormatA8: + case GFXFormatL8: + *pLoc = rColor.alpha; + break; + + case GFXFormatR8G8B8: + dMemcpy( pLoc, &rColor, 3 * sizeof( U8 ) ); + break; + + case GFXFormatR8G8B8A8: + case GFXFormatR8G8B8X8: + dMemcpy( pLoc, &rColor, 4 * sizeof( U8 ) ); + break; + + case GFXFormatR5G6B5: + #ifdef TORQUE_OS_MAC + *((U16*)pLoc) = (rColor.red << 11) | (rColor.green << 5) | (rColor.blue << 0) ; + #else + *((U16*)pLoc) = (rColor.blue << 0) | (rColor.green << 5) | (rColor.red << 11); + #endif + break; + + case GFXFormatR5G5B5A1: + #ifdef TORQUE_OS_MAC + *((U16*)pLoc) = (((rColor.alpha>0) ? 1 : 0)<<15) | (rColor.blue << 10) | (rColor.green << 5) | (rColor.red << 0); + #else + *((U16*)pLoc) = (rColor.blue << 1) | (rColor.green << 6) | (rColor.red << 11) | ((rColor.alpha>0) ? 1 : 0); + #endif + break; + + default: + AssertFatal(false, "Bad internal format"); + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- + +bool GBitmap::combine( const GBitmap *bitmapA, const GBitmap *bitmapB, const GFXTextureOp combineOp ) +{ + // Check valid texture ops + switch( combineOp ) + { + case GFXTOPAdd: + case GFXTOPSubtract: + break; + + default: + Con::errorf( "GBitmap::combine - Invalid op type" ); + return false; + } + + // Check bitmapA format + switch( bitmapA->getFormat() ) + { + case GFXFormatR8G8B8: + case GFXFormatR8G8B8X8: + case GFXFormatR8G8B8A8: + break; + + default: + Con::errorf( "GBitmap::combine - invalid format for bitmapA" ); + return false; + } + + // Check bitmapB format + switch( bitmapB->getFormat() ) + { + case GFXFormatR8G8B8: + case GFXFormatR8G8B8X8: + case GFXFormatR8G8B8A8: + break; + + default: + Con::errorf( "GBitmap::combine - invalid format for bitmapB" ); + return false; + } + + // Determine format of result texture + // CodeReview: This is dependent on the order of the GFXFormat enum. [5/11/2007 Pat] + GFXFormat resFmt = static_cast( getMax( bitmapA->getFormat(), bitmapB->getFormat() ) ); + U32 resWidth = getMax( bitmapA->getWidth(), bitmapB->getWidth() ); + U32 resHeight = getMax( bitmapA->getHeight(), bitmapB->getHeight() ); + + // Adjust size OF bitmap based on the biggest one + if( bitmapA->getWidth() != bitmapB->getWidth() || + bitmapA->getHeight() != bitmapB->getHeight() ) + { + // Delete old bitmap + deleteImage(); + + // Allocate new one + allocateBitmap( resWidth, resHeight, false, resFmt ); + } + + // Adjust format of result bitmap (if resFmt == getFormat() it will not perform the format convert) + setFormat( resFmt ); + + // Perform combine + U8 *destBits = getWritableBits(); + const U8 *aBits = bitmapA->getBits(); + const U8 *bBits = bitmapB->getBits(); + + for( int y = 0; y < getHeight(); y++ ) + { + for( int x = 0; x < getWidth(); x++ ) + { + for( int _byte = 0; _byte < mBytesPerPixel; _byte++ ) + { + U8 pxA = 0; + U8 pxB = 0; + + // Get contributions from A and B + if( y < bitmapA->getHeight() && + x < bitmapA->getWidth() && + _byte < bitmapA->mBytesPerPixel ) + pxA = *aBits++; + + if( y < bitmapB->getHeight() && + x < bitmapB->getWidth() && + _byte < bitmapB->mBytesPerPixel ) + pxB = *bBits++; + + // Combine them (clamp values 0-U8_MAX) + switch( combineOp ) + { + case GFXTOPAdd: + *destBits++ = getMin( U8( pxA + pxB ), U8_MAX ); + break; + + case GFXTOPSubtract: + *destBits++ = getMax( U8( pxA - pxB ), U8( 0 ) ); + break; + default: + AssertFatal(false, "GBitmap::combine - Invalid combineOp"); + break; + } + } + } + } + + return true; +} + +void GBitmap::fill( const ColorI &rColor ) +{ + // Set the first pixel using the slow + // but proper method. + setColor( 0, 0, rColor ); + mHasTransparency = rColor.alpha < 255; + + // Now fill the first row of the bitmap by + // copying the first pixel across the row. + const U32 stride = getWidth() * mBytesPerPixel; + const U8 *src = getBits(); + U8 *dest = getWritableBits() + mBytesPerPixel; + const U8 *end = src + stride; + for ( ; dest != end; dest += mBytesPerPixel ) + dMemcpy( dest, src, mBytesPerPixel ); + + // Now copy the first row to all the others. + // + // TODO: This could adaptively size the copy + // amount to copy more rows from the source + // and reduce the total number of memcpy calls. + // + dest = getWritableBits() + stride; + end = src + ( stride * getHeight() ); + for ( ; dest != end; dest += stride ) + dMemcpy( dest, src, stride ); +} + +void GBitmap::fillWhite() +{ + dMemset( getWritableBits(), 255, mByteSize ); + mHasTransparency = false; +} + +GBitmap* GBitmap::createPaddedBitmap() const +{ + if (isPow2(getWidth()) && isPow2(getHeight())) + return NULL; + + AssertFatal(getNumMipLevels() == 1, + "Cannot have non-pow2 bitmap with miplevels"); + + U32 width = getWidth(); + U32 height = getHeight(); + + U32 newWidth = getNextPow2(getWidth()); + U32 newHeight = getNextPow2(getHeight()); + + GBitmap* pReturn = new GBitmap(newWidth, newHeight, false, getFormat()); + + for (U32 i = 0; i < height; i++) + { + U8* pDest = (U8*)pReturn->getAddress(0, i); + const U8* pSrc = (const U8*)getAddress(0, i); + + dMemcpy(pDest, pSrc, width * mBytesPerPixel); + + pDest += width * mBytesPerPixel; + // set the src pixel to the last pixel in the row + const U8 *pSrcPixel = pDest - mBytesPerPixel; + + for(U32 j = width; j < newWidth; j++) + for(U32 k = 0; k < mBytesPerPixel; k++) + *pDest++ = pSrcPixel[k]; + } + + for(U32 i = height; i < newHeight; i++) + { + U8* pDest = (U8*)pReturn->getAddress(0, i); + U8* pSrc = (U8*)pReturn->getAddress(0, height-1); + dMemcpy(pDest, pSrc, newWidth * mBytesPerPixel); + } + + return pReturn; +} + +GBitmap* GBitmap::createPow2Bitmap() const +{ + if (isPow2(getWidth()) && isPow2(getHeight())) + return NULL; + + AssertFatal(getNumMipLevels() == 1, + "Cannot have non-pow2 bitmap with miplevels"); + + U32 width = getWidth(); + U32 height = getHeight(); + + U32 newWidth = getNextPow2(getWidth()); + U32 newHeight = getNextPow2(getHeight()); + + GBitmap* pReturn = new GBitmap(newWidth, newHeight, false, getFormat()); + + U8* pDest = (U8*)pReturn->getAddress(0, 0); + const U8* pSrc = (const U8*)getAddress(0, 0); + + F32 yCoeff = (F32) height / (F32) newHeight; + F32 xCoeff = (F32) width / (F32) newWidth; + + F32 currY = 0.0f; + for (U32 y = 0; y < newHeight; y++) + { + F32 currX = 0.0f; + //U32 yDestOffset = (pReturn->mWidth * pReturn->mBytesPerPixel) * y; + //U32 xDestOffset = 0; + //U32 ySourceOffset = (U32)((mWidth * mBytesPerPixel) * currY); + //F32 xSourceOffset = 0.0f; + for (U32 x = 0; x < newWidth; x++) + { + pDest = (U8*) pReturn->getAddress(x, y); + pSrc = (U8*) getAddress((S32)currX, (S32)currY); + for (U32 p = 0; p < pReturn->mBytesPerPixel; p++) + { + pDest[p] = pSrc[p]; + } + currX += xCoeff; + } + currY += yCoeff; + } + + return pReturn; +} + +void GBitmap::copyChannel( U32 index, GBitmap *outBitmap ) const +{ + AssertFatal( index < mBytesPerPixel, "GBitmap::copyChannel() - Bad channel offset!" ); + AssertFatal( outBitmap, "GBitmap::copyChannel() - Null output bitmap!" ); + AssertFatal( outBitmap->getWidth() == getWidth(), "GBitmap::copyChannel() - Width mismatch!" ); + AssertFatal( outBitmap->getHeight() == getHeight(), "GBitmap::copyChannel() - Height mismatch!" ); + + U8 *outBits = outBitmap->getWritableBits(); + const U32 outBytesPerPixel = outBitmap->getBytesPerPixel(); + const U8 *srcBits = getBits() + index; + const U8 *endBits = getBits() + mByteSize; + + for ( ; srcBits < endBits; ) + { + *outBits = *srcBits; + outBits += outBytesPerPixel; + srcBits += mBytesPerPixel; + } +} + +//------------------------------------------------------------------------------ + +bool GBitmap::read(Stream& io_rStream) +{ + // Handle versioning + U32 version; + io_rStream.read(&version); + AssertFatal(version == csFileVersion, "Bitmap::read: incorrect file version"); + + //-------------------------------------- Read the object + U32 fmt; + io_rStream.read(&fmt); + mInternalFormat = GFXFormat(fmt); + mBytesPerPixel = 1; + switch (mInternalFormat) { + case GFXFormatA8: + case GFXFormatL8: mBytesPerPixel = 1; + break; + case GFXFormatR8G8B8: mBytesPerPixel = 3; + break; + case GFXFormatR8G8B8A8: mBytesPerPixel = 4; + break; + case GFXFormatR5G6B5: + case GFXFormatR5G5B5A1: mBytesPerPixel = 2; + break; + default: + AssertFatal(false, "GBitmap::read: misunderstood format specifier"); + break; + } + + io_rStream.read(&mByteSize); + + mBits = new U8[mByteSize]; + io_rStream.read(mByteSize, mBits); + + io_rStream.read(&mWidth); + io_rStream.read(&mHeight); + + io_rStream.read(&mNumMipLevels); + for (U32 i = 0; i < c_maxMipLevels; i++) + io_rStream.read(&mMipLevelOffsets[i]); + + checkForTransparency(); + + return (io_rStream.getStatus() == Stream::Ok); +} + +bool GBitmap::write(Stream& io_rStream) const +{ + // Handle versioning + io_rStream.write(csFileVersion); + + //-------------------------------------- Write the object + io_rStream.write(U32(mInternalFormat)); + + io_rStream.write(mByteSize); + io_rStream.write(mByteSize, mBits); + + io_rStream.write(mWidth); + io_rStream.write(mHeight); + + io_rStream.write(mNumMipLevels); + for (U32 i = 0; i < c_maxMipLevels; i++) + io_rStream.write(mMipLevelOffsets[i]); + + return (io_rStream.getStatus() == Stream::Ok); +} + +//------------------------------------------------------------------------------ +//-------------------------------------- Persistent I/O +// + +bool GBitmap::readBitmap( const String &bmType, Stream &ioStream ) +{ + const GBitmap::Registration *regInfo = GBitmap::sFindRegInfo( bmType ); + + if ( regInfo == NULL ) + { + Con::errorf( "[GBitmap::readBitmap] unable to find registration for extension [%s]", bmType.c_str() ); + return NULL; + } + + return regInfo->readFunc( ioStream, this ); +} + +bool GBitmap::writeBitmap( const String &bmType, Stream &ioStream, U32 compressionLevel ) +{ + const GBitmap::Registration *regInfo = GBitmap::sFindRegInfo( bmType ); + + if ( regInfo == NULL ) + { + Con::errorf( "[GBitmap::writeBitmap] unable to find registration for extension [%s]", bmType.c_str() ); + return NULL; + } + + return regInfo->writeFunc( this, ioStream, (compressionLevel == U32_MAX) ? regInfo->defaultCompression : compressionLevel ); +} + +template<> void *Resource::create(const Torque::Path &path) +{ + PROFILE_SCOPE( ResourceGBitmap_create ); + +#ifdef TORQUE_DEBUG_RES_MANAGER + Con::printf( "Resource::create - [%s]", path.getFullPath().c_str() ); +#endif + + FileStream stream; + + stream.open( path.getFullPath(), Torque::FS::File::Read ); + + if ( stream.getStatus() != Stream::Ok ) + { + Con::errorf( "Resource::create - failed to open '%s'", path.getFullPath().c_str() ); + return NULL; + } + + GBitmap *bmp = new GBitmap; + const String extension = path.getExtension(); + if( !bmp->readBitmap( extension, stream ) ) + { + Con::errorf( "Resource::create - error reading '%s'", path.getFullPath().c_str() ); + delete bmp; + bmp = NULL; + } + + return bmp; +} + +template<> ResourceBase::Signature Resource::signature() +{ + return MakeFourCC('b','i','t','m'); +} + +Resource GBitmap::load(const Torque::Path &path) +{ + Resource ret = _load( path ); + if ( ret != NULL ) + return ret; + + // Do a recursive search. + return _search( path ); +} + +Resource GBitmap::_load(const Torque::Path &path) +{ + PROFILE_SCOPE( GBitmap_load ); + + if ( Torque::FS::IsFile( path ) ) + return ResourceManager::get().load( path ); + + Path foundPath; + if ( GBitmap::sFindFile( path, &foundPath ) ) + { + Resource ret = ResourceManager::get().load( foundPath ); + if ( ret != NULL ) + return ret; + } + + return Resource< GBitmap >( NULL ); +} + +Resource GBitmap::_search(const Torque::Path &path) +{ + PROFILE_SCOPE( GBitmap_search ); + + // If unable to load texture in current directory + // look in the parent directory. But never look in the root. + Path newPath( path ); + while ( true ) + { + String filePath = newPath.getPath(); + String::SizeType slash = filePath.find( '/', filePath.length(), String::Right ); + + if ( slash == String::NPos ) + break; + + slash = filePath.find( '/', filePath.length(), String::Right ); + if ( slash == String::NPos ) + break; + + String truncPath = filePath.substr( 0, slash ); + newPath.setPath( truncPath ); + + Resource ret = _load( newPath ); + if ( ret != NULL ) + return ret; + } + + return Resource< GBitmap >( NULL ); +} + +DefineEngineFunction( getBitmapInfo, String, ( const char *filename ),, + "Returns image info in the following format: width TAB height TAB bytesPerPixel. " + "It will return an empty string if the file is not found.\n" + "@ingroup Rendering\n" ) +{ + Resource image = GBitmap::load( filename ); + if ( !image ) + return String::EmptyString; + + return String::ToString( "%d\t%d\t%d", image->getWidth(), + image->getHeight(), + image->getBytesPerPixel() ); +} diff --git a/Engine/source/gfx/bitmap/gBitmap.h b/Engine/source/gfx/bitmap/gBitmap.h new file mode 100644 index 000000000..419b982f1 --- /dev/null +++ b/Engine/source/gfx/bitmap/gBitmap.h @@ -0,0 +1,327 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GBITMAP_H_ +#define _GBITMAP_H_ + +#ifndef __RESOURCE_H__ +#include "core/resource.h" +#endif + +#ifndef _SWIZZLE_H_ +#include "core/util/swizzle.h" +#endif + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +#ifndef _GFXENUMS_H_ +#include "gfx/gfxEnums.h" // For the format +#endif + +//-------------------------------------- Forward decls. +class Stream; +class RectI; +class Point2I; +class ColorI; +class ColorF; + +//------------------------------------------------------------------------------ +//-------------------------------------- GBitmap + +class GBitmap +{ +public: + enum Constants + { + /// The maximum mipmap levels we support. The current + /// value lets us support up to 4096 x 4096 images. + c_maxMipLevels = 13 + }; + + struct Registration + { + /// The read function prototype. + typedef bool(*ReadFunc)(Stream &stream, GBitmap *bitmap); + + /// The write function prototype. Compression levels are image-specific - see their registration declaration for details. + typedef bool(*WriteFunc)(GBitmap *bitmap, Stream &stream, U32 compressionLevel); + + /// Used to sort the registrations so that + /// lookups occur in a fixed order. + U32 priority; + + Vector extensions; ///< the list of file extensions for this bitmap type [these should be lower case] + + ReadFunc readFunc; ///< the read function to call for this bitmap type + WriteFunc writeFunc; ///< the write function to call for this bitmap type + U32 defaultCompression; ///< the default compression level [levels are image-specific - see their registration declaration for details] + + Registration() + { + priority = 0; + VECTOR_SET_ASSOCIATION( extensions ); + } + }; + + /// Load the given bitmap file. It will try known file + /// extensions if one is not specified. If all else fails + /// it will look up the folder hierarchy for a match. + /// + /// Important: Don't do something like this... + /// + /// @code + /// GBitmap* bitmap; // WRONG TYPE! + /// bitmap = GBitmap::load( filename ); + /// @endcode + /// + /// Resources are reference-counted and the smart pointer conversion will + /// release the bitmap and thus render the resulting bitmap pointer invalid! + /// The right way is like this: + /// + /// @code + /// Resource bitmap; // Correct! + /// bitmap = GBitmap::load( filename ); + /// @endcode + /// + static Resource load(const Torque::Path &path); + +protected: + + static Resource _load(const Torque::Path &path); + static Resource _search(const Torque::Path &path); + +public: + GBitmap(); + GBitmap(const GBitmap&); + + GBitmap(const U32 in_width, + const U32 in_height, + const bool in_extrudeMipLevels = false, + const GFXFormat in_format = GFXFormatR8G8B8 ); + + // This builds a GBitmap with the R8G8B8A8 format using the passed in + // data (assumes that there is width * height * 4 U8's in data) + GBitmap(const U32 in_width, + const U32 in_height, + const U8* data ); + + virtual ~GBitmap(); + + + static void sRegisterFormat( const Registration ® ); + static const Registration* sFindRegInfo( const String &extension ); + + /// Find the first file matching the registered extensions + /// skipping the original. + static bool sFindFile( const Torque::Path &path, Torque::Path *outPath ); + + /// Given a path to a file, try all known extensions. If the file exists on disk, fill in path + /// with the correct extension and return true. Otherwise, return false. + static bool sFindFiles( const Torque::Path &path, Vector *outFoundPaths ); + + /// Returns a space separated string of all registered extensions. + static String sGetExtensionList(); + + void allocateBitmap(const U32 in_width, + const U32 in_height, + const bool in_extrudeMipLevels = false, + const GFXFormat in_format = GFXFormatR8G8B8 ); + + void extrudeMipLevels(bool clearBorders = false); + void extrudeMipLevelsDetail(); + + U32 getNumMipLevels() const { return mNumMipLevels; } + + GBitmap *createPaddedBitmap() const; + GBitmap *createPow2Bitmap() const; + + /// Copies a color channel by index into the first channel + /// of the output bitmap. The output bitmap must be the same + /// dimensions as the source. + void copyChannel( U32 index, GBitmap *outBitmap ) const; + + void copyRect(const GBitmap *in, const RectI &srcRect, const Point2I &dstPoint, const U32 srcMipLevel = 0, const U32 dstMipLevel = 0); + + GFXFormat getFormat() const { return mInternalFormat; } + bool setFormat(GFXFormat fmt); + + U32 getWidth(const U32 in_mipLevel = 0) const; + U32 getHeight(const U32 in_mipLevel = 0) const; + U32 getDepth(const U32 in_mipLevel = 0) const; + + U8* getAddress(const S32 in_x, const S32 in_y, const U32 mipLevel = 0); + const U8* getAddress(const S32 in_x, const S32 in_y, const U32 mipLevel = 0) const; + + const U8* getBits(const U32 in_mipLevel = 0) const; + U8* getWritableBits(const U32 in_mipLevel = 0); + + U32 getByteSize() const { return mByteSize; } + U32 getBytesPerPixel() const { return mBytesPerPixel; } + + /// Use these functions to set and get the mHasTransparency value + /// This is used to indicate that this bitmap has pixels that have + /// an alpha value less than 255 (used by the auto-Material mapper) + bool getHasTransparency() const { return mHasTransparency; } + void setHasTransparency(bool hasTransparency) { mHasTransparency = hasTransparency; } + + /// In general you will want to use this function if there is not a + /// good spot in the bitmap loader(s) to check the alpha value of + /// the pixels. This function uses the texture format to loop over + /// the bitmap bits and to check for alpha values less than 255 + bool checkForTransparency(); + + ColorF sampleTexel(F32 u, F32 v) const; + bool getColor(const U32 x, const U32 y, ColorI& rColor) const; + bool setColor(const U32 x, const U32 y, const ColorI& rColor); + + /// This method will combine bitmapA and bitmapB using the operation specified + /// by combineOp. The result will be stored in the bitmap that this method is + /// called on. The size of the resulting bitmap will be the larger of A and B. + /// The format of the resulting bitmap will be the format of A or B, whichever + /// has a larger byte size. + /// + /// @note There are some restrictions on ops and formats that will probably change + /// based on how we use this function. + bool combine( const GBitmap *bitmapA, const GBitmap *bitmapB, const GFXTextureOp combineOp ); + + /// Fills the first mip level of the bitmap with the specified color. + void fill( const ColorI &rColor ); + + /// An optimized version of fill(). + void fillWhite(); + + //-------------------------------------- Internal data/operators + + void deleteImage(); + + //-------------------------------------- Input/Output interface + + /// Read a bitmap from a stream + /// @param bmType This is a file extension to describe the type of the data [i.e. "png" for PNG file, etc] + /// @param ioStream The stream to read from + bool readBitmap( const String &bmType, Stream &ioStream ); + + /// Write a bitmap to a stream + /// @param bmType This is a file extension to describe the type of the data [i.e. "png" for PNG file, etc] + /// @param ioStream The stream to read from + /// @param compressionLevel Image format-specific compression level. If set to U32_MAX, we use the default compression defined when the format was registered. + bool writeBitmap( const String &bmType, Stream &ioStream, U32 compressionLevel = U32_MAX ); + + bool readMNG(Stream& io_rStream); // located in bitmapMng.cc + bool writeMNG(Stream& io_rStream) const; + + bool read(Stream& io_rStream); + bool write(Stream& io_rStream) const; + + template + void swizzle(const Swizzle *s); + + static Vector sRegistrations; + +private: + GFXFormat mInternalFormat; + + U8* mBits; // Master bytes + U32 mByteSize; + U32 mWidth; + U32 mHeight; + U32 mBytesPerPixel; + + U32 mNumMipLevels; + U32 mMipLevelOffsets[c_maxMipLevels]; + + bool mHasTransparency; + + static const U32 csFileVersion; +}; + +//------------------------------------------------------------------------------ +//-------------------------------------- Inlines +// + +inline U32 GBitmap::getWidth(const U32 in_mipLevel) const +{ + AssertFatal(in_mipLevel < mNumMipLevels, + avar("GBitmap::getWidth: mip level out of range: (%d, %d)", + in_mipLevel, mNumMipLevels)); + + U32 retVal = mWidth >> in_mipLevel; + + return (retVal != 0) ? retVal : 1; +} + +inline U32 GBitmap::getHeight(const U32 in_mipLevel) const +{ + AssertFatal(in_mipLevel < mNumMipLevels, + avar("Bitmap::getHeight: mip level out of range: (%d, %d)", + in_mipLevel, mNumMipLevels)); + + U32 retVal = mHeight >> in_mipLevel; + + return (retVal != 0) ? retVal : 1; +} + +inline const U8* GBitmap::getBits(const U32 in_mipLevel) const +{ + AssertFatal(in_mipLevel < mNumMipLevels, + avar("GBitmap::getBits: mip level out of range: (%d, %d)", + in_mipLevel, mNumMipLevels)); + + return &mBits[mMipLevelOffsets[in_mipLevel]]; +} + +inline U8* GBitmap::getWritableBits(const U32 in_mipLevel) +{ + AssertFatal(in_mipLevel < mNumMipLevels, + avar("GBitmap::getWritableBits: mip level out of range: (%d, %d)", + in_mipLevel, mNumMipLevels)); + + return &mBits[mMipLevelOffsets[in_mipLevel]]; +} + +inline U8* GBitmap::getAddress(const S32 in_x, const S32 in_y, const U32 mipLevel) +{ + return (getWritableBits(mipLevel) + ((in_y * getWidth(mipLevel)) + in_x) * mBytesPerPixel); +} + +inline const U8* GBitmap::getAddress(const S32 in_x, const S32 in_y, const U32 mipLevel) const +{ + return (getBits(mipLevel) + ((in_y * getWidth(mipLevel)) + in_x) * mBytesPerPixel); +} + +template +void GBitmap::swizzle(const Swizzle *s ) +{ + const U32 memSize = getWidth() * getHeight() * mBytesPerPixel; + + void *b = dMalloc(memSize); + + s->ToBuffer(b, getWritableBits(), memSize); + + dMemcpy(getWritableBits(), b, memSize); + + dFree(b); +} + +#endif //_GBITMAP_H_ diff --git a/Engine/source/gfx/bitmap/loaders/bitmapBmp.cpp b/Engine/source/gfx/bitmap/loaders/bitmapBmp.cpp new file mode 100644 index 000000000..925d676dd --- /dev/null +++ b/Engine/source/gfx/bitmap/loaders/bitmapBmp.cpp @@ -0,0 +1,245 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/stream/stream.h" + +#include "gfx/bitmap/gBitmap.h" + + +static bool sReadBMP(Stream &stream, GBitmap *bitmap); +static bool sWriteBMP(GBitmap *bitmap, Stream &stream, U32 compressionLevel); + +static struct _privateRegisterBMP +{ + _privateRegisterBMP() + { + GBitmap::Registration reg; + + reg.extensions.push_back( "bmp" ); + + reg.readFunc = sReadBMP; + reg.writeFunc = sWriteBMP; + + GBitmap::sRegisterFormat( reg ); + } +} sStaticRegisterBMP; + + +// structures mirror those defined by the win32 API + +struct RGBQUAD +{ + U8 rgbBlue; + U8 rgbGreen; + U8 rgbRed; + U8 rgbReserved; +}; + +struct BITMAPFILEHEADER +{ + U16 bfType; + U32 bfSize; + U16 bfReserved1; + U16 bfReserved2; + U32 bfOffBits; +}; + +struct BITMAPINFOHEADER +{ + U32 biSize; + S32 biWidth; + S32 biHeight; + U16 biPlanes; + U16 biBitCount; + U32 biCompression; + U32 biSizeImage; + S32 biXPelsPerMeter; + S32 biYPelsPerMeter; + U32 biClrUsed; + U32 biClrImportant; +}; + +// constants for the biCompression field +#define BI_RGB 0L +#define BI_RLE8 1L +#define BI_RLE4 2L +#define BI_BITFIELDS 3L + + +//------------------------------------------------------------------------------ +//-------------------------------------- Supplementary I/O (Partially located in +// bitmapPng.cc) +// + +static bool sReadBMP(Stream &stream, GBitmap *bitmap) +{ + BITMAPINFOHEADER bi; + BITMAPFILEHEADER bf; + RGBQUAD rgb[256]; + + stream.read(&bf.bfType); + stream.read(&bf.bfSize); + stream.read(&bf.bfReserved1); + stream.read(&bf.bfReserved2); + stream.read(&bf.bfOffBits); + + stream.read(&bi.biSize); + stream.read(&bi.biWidth); + stream.read(&bi.biHeight); + stream.read(&bi.biPlanes); + stream.read(&bi.biBitCount); + stream.read(&bi.biCompression); + stream.read(&bi.biSizeImage); + stream.read(&bi.biXPelsPerMeter); + stream.read(&bi.biYPelsPerMeter); + stream.read(&bi.biClrUsed); + stream.read(&bi.biClrImportant); + + GFXFormat fmt = GFXFormatR8G8B8; + if(bi.biBitCount == 8) + { + // read in texture palette + if(!bi.biClrUsed) + bi.biClrUsed = 256; + stream.read(sizeof(RGBQUAD) * bi.biClrUsed, rgb); + } + bitmap->allocateBitmap(bi.biWidth, bi.biHeight, false, fmt); + U32 width = bitmap->getWidth(); + U32 height = bitmap->getHeight(); + U32 bytesPerPixel = bitmap->getBytesPerPixel(); + + for(U32 i = 0; i < bi.biHeight; i++) + { + U8 *rowDest = bitmap->getAddress(0, height - i - 1); + if (bi.biBitCount == 8) + { + // use palette...don't worry about being slow + for (S32 j=0; jgetAddress(0,0); + for(int i = 0; i < width * height; i++) + { + U8 tmp = ptr[0]; + ptr[0] = ptr[2]; + ptr[2] = tmp; + ptr += 3; + } + } + + // We know BMP's don't have any transparency + bitmap->setHasTransparency(false); + + return true; +} + +static bool sWriteBMP(GBitmap *bitmap, Stream &stream, U32 compressionLevel) +{ + TORQUE_UNUSED( compressionLevel ); // BMP does not use compression + + BITMAPINFOHEADER bi; + BITMAPFILEHEADER bf; + + bi.biSize = sizeof(BITMAPINFOHEADER); + bi.biWidth = bitmap->getWidth(); + bi.biHeight = bitmap->getHeight(); //our data is top-down + bi.biPlanes = 1; + + if(bitmap->getFormat() == GFXFormatR8G8B8) + { + bi.biBitCount = 24; + bi.biCompression = BI_RGB; + bi.biClrUsed = 0; + } + else + { + bi.biBitCount = 0; + bi.biCompression = BI_RGB; // Removes warning C4701 on line + AssertISV(false, "GBitmap::writeMSBmp - only support R8G8B8 formats!"); + } + + U32 width = bitmap->getWidth(); + U32 height = bitmap->getHeight(); + + U32 bytesPP = bi.biBitCount >> 3; + bi.biSizeImage = width * height * bytesPP; + bi.biXPelsPerMeter = 0; + bi.biYPelsPerMeter = 0; + bi.biClrUsed = 0; + bi.biClrImportant = 0; + + bf.bfType = makeFourCCTag('B','M',0,0); //Type of file 'BM' + bf.bfOffBits= sizeof(BITMAPINFOHEADER) + + sizeof(BITMAPFILEHEADER) + + (sizeof(RGBQUAD)*bi.biClrUsed); + bf.bfSize = bf.bfOffBits + bi.biSizeImage; + bf.bfReserved1 = 0; + bf.bfReserved2 = 0; + + stream.write(bf.bfType); + stream.write(bf.bfSize); + stream.write(bf.bfReserved1); + stream.write(bf.bfReserved2); + stream.write(bf.bfOffBits); + + stream.write(bi.biSize); + stream.write(bi.biWidth); + stream.write(bi.biHeight); + stream.write(bi.biPlanes); + stream.write(bi.biBitCount); + stream.write(bi.biCompression); + stream.write(bi.biSizeImage); + stream.write(bi.biXPelsPerMeter); + stream.write(bi.biYPelsPerMeter); + stream.write(bi.biClrUsed); + stream.write(bi.biClrImportant); + + //write the bitmap bits + U8* pMSUpsideDownBits = new U8[bi.biSizeImage]; + for (U32 i = 0; i < height; i++) + { + const U8* pSrc = bitmap->getAddress(0, i); + U8* pDst = pMSUpsideDownBits + (height - i - 1) * width * bytesPP; + + dMemcpy(pDst, pSrc, width * bytesPP); + } + + stream.write(bi.biSizeImage, pMSUpsideDownBits); + delete [] pMSUpsideDownBits; + + return stream.getStatus() == Stream::Ok; +} diff --git a/Engine/source/gfx/bitmap/loaders/bitmapGif.cpp b/Engine/source/gfx/bitmap/loaders/bitmapGif.cpp new file mode 100644 index 000000000..ef1bb783b --- /dev/null +++ b/Engine/source/gfx/bitmap/loaders/bitmapGif.cpp @@ -0,0 +1,231 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "core/stream/fileStream.h" +#include "core/util/path.h" + +#include "gfx/bitmap/gBitmap.h" + +// This must come after our headers due to a conflicting definition of VoidPtr +#include "lungif/gif_lib.h" + + +using namespace Torque; + +static bool sReadGIF(Stream &stream, GBitmap *bitmap); +static bool sWriteGIF(GBitmap *bitmap, Stream &stream, U32 compressionLevel); + +static struct _privateRegisterGIF +{ + _privateRegisterGIF() + { + GBitmap::Registration reg; + + reg.extensions.push_back( "gif" ); + + reg.readFunc = sReadGIF; + reg.writeFunc = sWriteGIF; + + GBitmap::sRegisterFormat( reg ); + } +} sStaticRegisterGIF; + +//-------------------------------------- Replacement I/O for standard LIBjpeg +// functions. we don't wanna use +// FILE*'s... +static int gifReadDataFn(GifFileType *gifinfo, GifByteType *data, int length) +{ + Stream *stream = (Stream*)gifinfo->UserData; + AssertFatal(stream != NULL, "gifReadDataFn::No stream."); + int pos = stream->getPosition(); + if (stream->read(length, data)) + return length; + + if (stream->getStatus() == Stream::EOS) + return (stream->getPosition()-pos); + else + return 0; +} + + +//-------------------------------------- +#if 0 +// CodeReview - until we can write these, get rid of warning by disabling method. +static int gifWriteDataFn(GifFileType *gifinfo, GifByteType *data, int length) +{ + Stream *stream = (Stream*)gifinfo->UserData; + AssertFatal(stream != NULL, "gifWriteDataFn::No stream."); + if (stream->write(length, data)) + return length; + else + return 0; +} +#endif + +//-------------------------------------- +static bool sReadGIF( Stream &stream, GBitmap *bitmap ) +{ + GifFileType *gifinfo = DGifOpen( (void*)&stream, gifReadDataFn); + if (!gifinfo) + return false; + + GifRecordType recordType; + do + { + if (DGifGetRecordType(gifinfo, &recordType) == GIF_ERROR) + break; + + if (recordType == IMAGE_DESC_RECORD_TYPE) + { + if (DGifGetImageDesc(gifinfo) == GIF_ERROR) + break; + + GFXFormat format = (gifinfo->SBackGroundColor == 0 ) ? GFXFormatR8G8B8 : GFXFormatR8G8B8A8; + bitmap->allocateBitmap(gifinfo->SWidth, gifinfo->SHeight, false, format); + + // Assume no transparency until proven otherwise + bitmap->setHasTransparency(false); + + U32 gwidth = gifinfo->Image.Width ? gifinfo->Image.Width : bitmap->getWidth(); + U32 gheight= gifinfo->Image.Height ? gifinfo->Image.Height : bitmap->getHeight(); + U32 gifSize = gwidth * gheight; + U8 *data = new U8[gifSize]; + + if (DGifGetLine(gifinfo, data, gifSize) != GIF_ERROR) + { + // use the global or local color table ? + GifColorType *color = NULL; + if (gifinfo->Image.ColorMap) + color = gifinfo->Image.ColorMap->Colors; + else if (gifinfo->SColorMap) + color = gifinfo->SColorMap->Colors; + + if (color) + { + U8 *dst = bitmap->getAddress(gifinfo->Image.Left, gifinfo->Image.Top); + U8 *src = data; + U32 right = gifinfo->Image.Left + gwidth; + U32 bottom = gifinfo->Image.Top + gheight; + U32 next = (bitmap->getWidth() - gwidth) * bitmap->getBytesPerPixel(); + + if (format == GFXFormatR8G8B8A8) + { + for (U32 y=gifinfo->Image.Top; yImage.Left; xSBackGroundColor) + { + // this is a transparent pixel + dst[0] = 0; // red + dst[1] = 0; // green + dst[2] = 0; // blue + dst[3] = 0; // alpha + + bitmap->setHasTransparency(true); + } + else + { + dst[0] = color[*src].Red; + dst[1] = color[*src].Green; + dst[2] = color[*src].Blue; + dst[3] = 0; // alpha + } + dst += bitmap->getBytesPerPixel(); + } + dst += next; + } + } + else + { + for (U32 y=gifinfo->Image.Top; yImage.Left; xgetBytesPerPixel(); + } + dst += next; + } + } + delete [] data; + DGifCloseFile(gifinfo); + return true; + } + } + // failure + delete [] data; + break; + } + else if (recordType == EXTENSION_RECORD_TYPE) + { + GifByteType *extension; + S32 extCode; + + // Skip any extension blocks in file + if (DGifGetExtension(gifinfo, &extCode, &extension) != GIF_ERROR) + { + while (extension != NULL) + { + if (DGifGetExtensionNext(gifinfo, &extension) == GIF_ERROR) + { + return false; + } + } + } + else + { + return false; + } + } + + // There used to be a break right here. This caused the while condition to + // never get processed, and so it never looped through all the records in + // the GIF. I took a quick peek back at TGB and TGE histories and I am not + // sure where this change got made, but I can't figure out why the loading + // worked at all, ever, with that break in there. The only case I can think + // of is if the first record in the GIF was the bitmap data. + // [6/6/2007 Pat] + + }while (recordType != TERMINATE_RECORD_TYPE); + + + DGifCloseFile(gifinfo); + return true; +} + + +//-------------------------------------------------------------------------- +static bool sWriteGIF(GBitmap *bitmap, Stream &stream, U32 compressionLevel) +{ + TORQUE_UNUSED( bitmap ); + TORQUE_UNUSED( stream ); + TORQUE_UNUSED( compressionLevel ); + + return false; +} + + diff --git a/Engine/source/gfx/bitmap/loaders/bitmapJpeg.cpp b/Engine/source/gfx/bitmap/loaders/bitmapJpeg.cpp new file mode 100644 index 000000000..790369f32 --- /dev/null +++ b/Engine/source/gfx/bitmap/loaders/bitmapJpeg.cpp @@ -0,0 +1,250 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "ljpeg/jpeglib.h" + +#include "core/stream/stream.h" + +#include "gfx/bitmap/gBitmap.h" + + +static bool sReadJPG(Stream &stream, GBitmap *bitmap); +static bool sWriteJPG(GBitmap *bitmap, Stream &stream, U32 compressionLevel); + +static struct _privateRegisterJPG +{ + _privateRegisterJPG() + { + GBitmap::Registration reg; + + reg.priority = 50; + reg.extensions.push_back( "jpeg" ); + reg.extensions.push_back( "jpg" ); + + reg.readFunc = sReadJPG; + reg.writeFunc = sWriteJPG; + + GBitmap::sRegisterFormat( reg ); + } +} sStaticRegisterJPG; + +//-------------------------------------- Replacement I/O for standard LIBjpeg +// functions. we don't wanna use +// FILE*'s... +static int jpegReadDataFn(void *client_data, unsigned char *data, int length) +{ + Stream *stream = (Stream*)client_data; + AssertFatal(stream != NULL, "jpegReadDataFn::No stream."); + int pos = stream->getPosition(); + if (stream->read(length, data)) + return length; + + if (stream->getStatus() == Stream::EOS) + return (stream->getPosition()-pos); + else + return 0; +} + + +//-------------------------------------- +static int jpegWriteDataFn(void *client_data, unsigned char *data, int length) +{ + Stream *stream = (Stream*)client_data; + AssertFatal(stream != NULL, "jpegWriteDataFn::No stream."); + if (stream->write(length, data)) + return length; + else + return 0; +} + + +//-------------------------------------- +static int jpegFlushDataFn(void *) +{ + // do nothing since we can't flush the stream object + return 0; +} + + +//-------------------------------------- +static int jpegErrorFn(void *client_data) +{ + Stream *stream = (Stream*)client_data; + AssertFatal(stream != NULL, "jpegErrorFn::No stream."); + return (stream->getStatus() != Stream::Ok); +} + + +//-------------------------------------- +static bool sReadJPG(Stream &stream, GBitmap *bitmap) +{ + JFREAD = jpegReadDataFn; + JFERROR = jpegErrorFn; + + jpeg_decompress_struct cinfo; + jpeg_error_mgr jerr; + + // We set up the normal JPEG error routines, then override error_exit. + //cinfo.err = jpeg_std_error(&jerr.pub); + //jerr.pub.error_exit = my_error_exit; + + // if (setjmp(jerr.setjmp_buffer)) + // { + // // If we get here, the JPEG code has signaled an error. + // // We need to clean up the JPEG object, close the input file, and return. + // jpeg_destroy_decompress(&cinfo); + // return false; + // } + + + cinfo.err = jpeg_std_error(&jerr); // set up the normal JPEG error routines. + cinfo.client_data = (void*)&stream; // set the stream into the client_data + + // Now we can initialize the JPEG decompression object. + jpeg_create_decompress(&cinfo); + + jpeg_stdio_src(&cinfo); + + // Read file header, set default decompression parameters + jpeg_read_header(&cinfo, true); + + GFXFormat format; + switch (cinfo.out_color_space) + { + case JCS_GRAYSCALE: format = GFXFormatA8; break; + case JCS_RGB: format = GFXFormatR8G8B8; break; + default: + jpeg_destroy_decompress(&cinfo); + return false; + } + + // Start decompressor + jpeg_start_decompress(&cinfo); + + // allocate the bitmap space and init internal variables... + bitmap->allocateBitmap(cinfo.output_width, cinfo.output_height, false, format); + + // Set up the row pointers... + U32 rowBytes = cinfo.output_width * cinfo.output_components; + + U8* pBase = (U8*)bitmap->getBits(); + for (U32 i = 0; i < bitmap->getHeight(); i++) + { + JSAMPROW rowPointer = pBase + (i * rowBytes); + jpeg_read_scanlines(&cinfo, &rowPointer, 1); + } + + // Finish decompression + jpeg_finish_decompress(&cinfo); + + // Release JPEG decompression object + // This is an important step since it will release a good deal of memory. + jpeg_destroy_decompress(&cinfo); + + // We know JPEG's don't have any transparency + bitmap->setHasTransparency(false); + + return true; +} + + +//-------------------------------------------------------------------------- +static bool sWriteJPG(GBitmap *bitmap, Stream &stream, U32 compressionLevel) +{ + TORQUE_UNUSED(compressionLevel); // compression level not currently hooked up + + GFXFormat format = bitmap->getFormat(); + + // JPEG format does not support transparency so any image + // in Alpha format should be saved as a grayscale which coincides + // with how the readJPEG function will read-in a JPEG. So the + // only formats supported are RGB and Alpha, not RGBA. + AssertFatal(format == GFXFormatR8G8B8 || format == GFXFormatA8, + "GBitmap::writeJPEG: ONLY RGB bitmap writing supported at this time."); + if (format != GFXFormatR8G8B8 && format != GFXFormatA8) + return false; + + // maximum image size allowed + #define MAX_HEIGHT 4096 + if (bitmap->getHeight() > MAX_HEIGHT) + return false; + + // Bind our own stream writing, error, and memory flush functions + // to the jpeg library interface + JFWRITE = jpegWriteDataFn; + JFFLUSH = jpegFlushDataFn; + JFERROR = jpegErrorFn; + + // Allocate and initialize our jpeg compression structure and error manager + jpeg_compress_struct cinfo; + jpeg_error_mgr jerr; + + cinfo.err = jpeg_std_error(&jerr); // set up the normal JPEG error routines. + cinfo.client_data = (void*)&stream; // set the stream into the client_data + jpeg_create_compress(&cinfo); // allocates a small amount of memory + + // specify the destination for the compressed data(our stream) + jpeg_stdio_dest(&cinfo); + + // set the image properties + cinfo.image_width = bitmap->getWidth(); // image width + cinfo.image_height = bitmap->getHeight(); // image height + cinfo.input_components = bitmap->getBytesPerPixel(); // samples per pixel(RGB:3, Alpha:1) + + switch (format) + { + case GFXFormatA8: // no alpha support in JPEG format, so turn it into a grayscale + cinfo.in_color_space = JCS_GRAYSCALE; + break; + case GFXFormatR8G8B8: // otherwise we are writing in RGB format + cinfo.in_color_space = JCS_RGB; + break; + default: + AssertFatal( false, "Format not handled in GBitmap::writeJPEG() switch" ); + break; + } + // use default compression params(75% compression) + jpeg_set_defaults(&cinfo); + + // begin JPEG compression cycle + jpeg_start_compress(&cinfo, true); + + // Set up the row pointers... + U32 rowBytes = cinfo.image_width * cinfo.input_components; + + U8* pBase = (U8*)bitmap->getBits(); + for (U32 i = 0; i < bitmap->getHeight(); i++) + { + // write the image data + JSAMPROW rowPointer = pBase + (i * rowBytes); + jpeg_write_scanlines(&cinfo, &rowPointer, 1); + } + + // complete the compression cycle + jpeg_finish_compress(&cinfo); + + // release the JPEG compression object + jpeg_destroy_compress(&cinfo); + + // return success + return true; +} diff --git a/Engine/source/gfx/bitmap/loaders/bitmapMng.cpp b/Engine/source/gfx/bitmap/loaders/bitmapMng.cpp new file mode 100644 index 000000000..7483dc0cb --- /dev/null +++ b/Engine/source/gfx/bitmap/loaders/bitmapMng.cpp @@ -0,0 +1,421 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/stream/stream.h" + +#include "gfx/bitmap/gBitmap.h" + +#include "core/color.h" + +#define MNG_NO_CMS +#define MNG_SUPPORT_READ +#define MNG_SUPPORT_WRITE +#define MNG_SUPPORT_DISPLAY +#define MNG_STORE_CHUNKS +#define MNG_ACCESS_CHUNKS + +#include "lmng/libmng.h" + + + +static bool sReadMNG(Stream &stream, GBitmap *bitmap); +static bool sWriteMNG(GBitmap *bitmap, Stream &stream, U32 compressionLevel); + +static struct _privateRegisterMNG +{ + _privateRegisterMNG() + { + GBitmap::Registration reg; + + reg.extensions.push_back( "jng" ); + reg.extensions.push_back( "mng" ); + + reg.readFunc = sReadMNG; + reg.writeFunc = sWriteMNG; + + GBitmap::sRegisterFormat( reg ); + } +} sStaticRegisterMNG; + + +typedef struct +{ + GBitmap* image; + Stream* stream; +} mngstuff; + +static mng_ptr mngMallocFn(mng_size_t size) +{ + mng_ptr data = dMalloc(size); + return dMemset(data, 0, size); +} + +static void mngFreeFn(mng_ptr p, mng_size_t size) +{ + dFree(p); +} + +static mng_bool mngOpenDataFn(mng_handle mng) +{ + return MNG_TRUE; +} + +static mng_bool mngCloseDataFn(mng_handle mng) +{ + return MNG_TRUE; +} + +static mng_bool mngReadDataFn(mng_handle mng, mng_ptr data, mng_uint32 length, mng_uint32 *bytesread) +{ + mngstuff *mymng = (mngstuff *)mng_get_userdata(mng); + AssertFatal(mymng->stream != NULL, "No stream?"); + + bool success = mymng->stream->read(length, data); + *bytesread = length; // stupid hack + + AssertFatal(success, "MNG read catastrophic error!"); + if(success) + return MNG_TRUE; + else + return MNG_FALSE; +} + +#if 0 +// CodeReview - until we can write these, get rid of warning by disabling method. +static mng_bool mngWriteDataFn(mng_handle mng, mng_ptr data, mng_uint32 length, mng_uint32 *iWritten) +{ + mngstuff *mymng = (mngstuff *)mng_get_userdata(mng); + AssertFatal(mymng->stream != NULL, "No stream?"); + + bool success = mymng->stream->write(length, data); + *iWritten = length; // stupid hack + + AssertFatal(success, "MNG write catastrophic error!"); + if(success) + return MNG_TRUE; + else + return MNG_FALSE; +} +#endif + +static mng_bool mngProcessHeaderFn(mng_handle mng, mng_uint32 width, mng_uint32 height) +{ + mngstuff *mymng = (mngstuff *)mng_get_userdata(mng); + + GFXFormat format; + mng_uint8 colorType = mng_get_colortype(mng); + mng_uint8 alphaDepth = mng_get_alphadepth(mng); + switch(colorType) + { + case MNG_COLORTYPE_GRAY: + case MNG_COLORTYPE_JPEGGRAY: + format = GFXFormatR8G8B8; + mng_set_canvasstyle(mng, MNG_CANVAS_RGB8); + break; + + case MNG_COLORTYPE_INDEXED: + if(alphaDepth >= 1) + { + format = GFXFormatR8G8B8A8; + mng_set_canvasstyle(mng, MNG_CANVAS_RGBA8); + } + else + { + format = GFXFormatR8G8B8; + mng_set_canvasstyle(mng, MNG_CANVAS_RGB8); + } + + case MNG_COLORTYPE_RGB: + case MNG_COLORTYPE_JPEGCOLOR: + if(alphaDepth >= 1) + { + format = GFXFormatR8G8B8A8; + mng_set_canvasstyle(mng, MNG_CANVAS_RGBA8); + } + else + { + format = GFXFormatR8G8B8; + mng_set_canvasstyle(mng, MNG_CANVAS_RGB8); + } + break; + + case MNG_COLORTYPE_RGBA: + case MNG_COLORTYPE_JPEGCOLORA: + format = GFXFormatR8G8B8A8; + mng_set_canvasstyle(mng, MNG_CANVAS_RGBA8); + break; + + default: + // This case should never get hit, however it resolves a compiler + // warning + format = GFXFormat_FIRST; + AssertISV( false, "Unknown color format in bitmap MNG Loading" ); + } + + mymng->image->allocateBitmap(width, height, false, format); + return MNG_TRUE; +} + +static mng_ptr mngCanvasLineFn(mng_handle mng, mng_uint32 line) +{ + mngstuff *mymng = (mngstuff *)mng_get_userdata(mng); + return (mng_ptr) mymng->image->getAddress(0, line); +} + +static mng_bool mngRefreshFn(mng_handle mng, mng_uint32 x, mng_uint32 y, mng_uint32 w, mng_uint32 h) +{ + return MNG_TRUE; +} + +static mng_uint32 mngGetTicksFn(mng_handle mng) +{ + return 0; +} + +static mng_bool mngSetTimerFn(mng_handle mng, mng_uint32 msecs) +{ + return MNG_TRUE; +} + +static mng_bool mngFatalErrorFn(mng_handle mng, mng_int32 code, mng_int8 severity, mng_chunkid chunktype, mng_uint32 chunkseq, mng_int32 extra1, mng_int32 extra2, mng_pchar text) +{ + mng_cleanup(&mng); + + AssertISV(false, avar("Error reading MNG file:\n %s", (const char*)text)); + return MNG_FALSE; +} + +static bool sReadMNG(Stream &stream, GBitmap *bitmap) +{ + mngstuff mnginfo; + dMemset(&mnginfo, 0, sizeof(mngstuff)); + + mng_handle mng = mng_initialize(&mnginfo, mngMallocFn, mngFreeFn, MNG_NULL); + if(mng == NULL) + return false; + + // setup the callbacks + mng_setcb_errorproc(mng, mngFatalErrorFn); + mng_setcb_openstream(mng, mngOpenDataFn); + mng_setcb_closestream(mng, mngCloseDataFn); + mng_setcb_readdata(mng, mngReadDataFn); + mng_setcb_processheader(mng, mngProcessHeaderFn); + mng_setcb_getcanvasline(mng, mngCanvasLineFn); + mng_setcb_refresh(mng, mngRefreshFn); + mng_setcb_gettickcount(mng, mngGetTicksFn); + mng_setcb_settimer(mng, mngSetTimerFn); + + mnginfo.image = bitmap; + mnginfo.stream = &stream; + + mng_read(mng); + mng_display(mng); + + // hacks :( + // libmng doesn't support returning data in gray/gray alpha format, + // so we grab as RGB/RGBA and just cut off the g and b + mng_uint8 colorType = mng_get_colortype(mng); + switch(colorType) + { + case MNG_COLORTYPE_GRAY: + case MNG_COLORTYPE_JPEGGRAY: + { + GBitmap temp(*bitmap); + bitmap->deleteImage(); + bitmap->allocateBitmap(temp.getWidth(), temp.getHeight(), false, GFXFormatA8); + + // force getColor to read in in the same color value for each channel + // since the gray colortype has the real alpha in the first channel + temp.setFormat( GFXFormatA8 ); + + ColorI color; + for(U32 row = 0; row < bitmap->getHeight(); row++) + { + for(U32 col = 0; col < bitmap->getWidth(); col++) + { + temp.getColor(col, row, color); + bitmap->setColor(col, row, color); + } + } + } + + break; + } + + mng_cleanup(&mng); + + // Check this bitmap for transparency + bitmap->checkForTransparency(); + + return true; +} + +static bool sWriteMNG(GBitmap *bitmap, Stream &stream, U32 compressionLevel) +{ + TORQUE_UNUSED( bitmap ); + TORQUE_UNUSED( stream ); + TORQUE_UNUSED( compressionLevel ); + + return false; +#if 0 + // ONLY RGB bitmap writing supported at this time! + AssertFatal(getFormat() == GFXFormatR8G8B8 || getFormat() == GFXFormatR8G8B8A8 || getFormat() == GFXFormatA8, "GBitmap::writeMNG: ONLY RGB bitmap writing supported at this time."); + if(getFormat() != GFXFormatR8G8B8 && getFormat() != GFXFormatR8G8B8A8 && getFormat() != GFXFormatA8) + return (false); + + // maximum image size allowed + #define MAX_HEIGHT 4096 + if(getHeight() >= MAX_HEIGHT) + return false; + + mngstuff mnginfo; + dMemset(&mnginfo, 0, sizeof(mngstuff)); + mng_handle mng = mng_initialize(&mnginfo, mngMallocFn, mngFreeFn, MNG_NULL); + if(mng == NULL) { + return false; + } + + // setup the callbacks + mng_setcb_openstream(mng, mngOpenDataFn); + mng_setcb_closestream(mng, mngCloseDataFn); + mng_setcb_writedata(mng, mngWriteDataFn); + + // create the file in memory + mng_create(mng); + + mng_putchunk_defi(mng, 0, 0, 0, MNG_FALSE, 0, 0, MNG_FALSE, 0, getWidth(), 0, getHeight()); + + mnginfo.image = (GBitmap*)this; + mnginfo.stream = &stream; + + switch(getFormat()) { + case GFXFormatA8: + mng_putchunk_ihdr(mng, getWidth(), getHeight(), + MNG_BITDEPTH_8, + MNG_COLORTYPE_GRAY, + MNG_COMPRESSION_DEFLATE, + MNG_FILTER_ADAPTIVE, + MNG_INTERLACE_NONE); + + // not implemented in lib yet + //mng_putimgdata_ihdr(mng, getWidth(), getHeight(), + // MNG_COLORTYPE_GRAY, + // MNG_BITDEPTH_8, + // MNG_COMPRESSION_DEFLATE, + // MNG_FILTER_ADAPTIVE, + // MNG_INTERLACE_NONE, + // MNG_CANVAS_GRAY8, mngCanvasLineFn); + break; + case GFXFormatR8G8B8: + mng_putchunk_ihdr(mng, getWidth(), getHeight(), + MNG_BITDEPTH_8, + MNG_COLORTYPE_RGB, + MNG_COMPRESSION_DEFLATE, + MNG_FILTER_ADAPTIVE, + MNG_INTERLACE_NONE); + + // not implemented in lib yet + //mng_putimgdata_ihdr(mng, getWidth(), getHeight(), + // MNG_COLORTYPE_RGB, + // MNG_BITDEPTH_8, + // MNG_COMPRESSION_DEFLATE, + // MNG_FILTER_ADAPTIVE, + // MNG_INTERLACE_NONE, + // MNG_CANVAS_RGB8, mngCanvasLineFn); + break; + case GFXFormatR8G8B8A8: + mng_putchunk_ihdr(mng, getWidth(), getHeight(), + MNG_BITDEPTH_8, + MNG_COLORTYPE_RGBA, + MNG_COMPRESSION_DEFLATE, + MNG_FILTER_ADAPTIVE, + MNG_INTERLACE_NONE); + + // not implemented in lib yet + //mng_putimgdata_ihdr(mng, getWidth(), getHeight(), + // MNG_COLORTYPE_RGBA, + // MNG_BITDEPTH_8, + // MNG_COMPRESSION_DEFLATE, + // MNG_FILTER_ADAPTIVE, + // MNG_INTERLACE_NONE, + // MNG_CANVAS_RGBA8, mngCanvasLineFn); + break; + } + + + // below is a hack until libmng is mature enough to handle this itself + //----------------------------------------------------------------------------- + + + U8 *tmpbuffer = new U8[this->byteSize + getHeight()]; + if(tmpbuffer == 0) + { + mng_cleanup(&mng); + return false; + } + + // transfer data, add filterbyte + U32 effwdt = getWidth() * this->bytesPerPixel; + for(U32 Row = 0; Row < getHeight(); Row++) + { + // first Byte in each scanline is filterbyte: currently 0 -> no filter + tmpbuffer[Row * (effwdt + 1)] = 0; + + // copy the scanline + dMemcpy(tmpbuffer + Row * (effwdt + 1) + 1, getAddress(0, Row), effwdt); + } + + // compress data with zlib + U8 *dstbuffer = new U8[this->byteSize + getHeight()]; + if(dstbuffer == 0) + { + delete [] tmpbuffer; + mng_cleanup(&mng); + return false; + } + + U32 dstbufferSize = this->byteSize + getHeight(); + if(Z_OK != compress2((Bytef*)dstbuffer,(uLongf*)&dstbufferSize, (const Bytef*)tmpbuffer, dstbufferSize, 9)) + { + delete [] tmpbuffer; + delete [] dstbuffer; + mng_cleanup(&mng); + return false; + } + + mng_putchunk_idat(mng, dstbufferSize, (mng_ptr*)dstbuffer); + + + //----------------------------------------------------------------------------- + + + mng_putchunk_iend(mng); + + delete [] tmpbuffer; + delete [] dstbuffer; + + mng_write(mng); + mng_cleanup(&mng); + + return true; +#endif +} diff --git a/Engine/source/gfx/bitmap/loaders/bitmapPng.cpp b/Engine/source/gfx/bitmap/loaders/bitmapPng.cpp new file mode 100644 index 000000000..ddf817526 --- /dev/null +++ b/Engine/source/gfx/bitmap/loaders/bitmapPng.cpp @@ -0,0 +1,647 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "core/stream/fileStream.h" +#include "core/stream/memStream.h" +#include "core/strings/stringFunctions.h" + +#include "gfx/bitmap/gBitmap.h" +#include "gfx/bitmap/pngUtils.h" + +#define PNG_INTERNAL 1 +#include +#include "lpng/png.h" +#include "zlib/zlib.h" + +#ifdef NULL +#undef NULL +#define NULL 0 +#endif + + +static bool sReadPNG(Stream &stream, GBitmap *bitmap); + +/// Compression levels for PNGs range from 0-9. +/// A value outside that range will cause the write routine to look for the best compression for a given PNG. This can be slow. +static bool sWritePNG(GBitmap *bitmap, Stream &stream, U32 compressionLevel); +static bool _writePNG(GBitmap *bitmap, Stream &stream, U32 compressionLevel, U32 strategy, U32 filter); + +static struct _privateRegisterPNG +{ + _privateRegisterPNG() + { + GBitmap::Registration reg; + + reg.priority = 100; + reg.extensions.push_back( "png" ); + + reg.readFunc = sReadPNG; + reg.writeFunc = sWritePNG; + reg.defaultCompression = 6; + + GBitmap::sRegisterFormat( reg ); + } +} sStaticRegisterPNG; + + +//-------------------------------------- Replacement I/O for standard LIBPng +// functions. we don't wanna use +// FILE*'s... +static void pngReadDataFn(png_structp png_ptr, + png_bytep data, + png_size_t length) +{ + AssertFatal(png_ptr->io_ptr != NULL, "No stream?"); + + Stream *strm = (Stream*)png_ptr->io_ptr; + bool success = strm->read(length, data); + AssertFatal(success, "pngReadDataFn - failed to read from stream!"); +} + + +//-------------------------------------- +static void pngWriteDataFn(png_structp png_ptr, + png_bytep data, + png_size_t length) +{ + AssertFatal(png_ptr->io_ptr != NULL, "No stream?"); + + Stream *strm = (Stream*)png_ptr->io_ptr; + bool success = strm->write(length, data); + AssertFatal(success, "pngWriteDataFn - failed to write to stream!"); +} + + +//-------------------------------------- +static void pngFlushDataFn(png_structp /*png_ptr*/) +{ + // +} + +static png_voidp pngMallocFn(png_structp /*png_ptr*/, png_size_t size) +{ + return FrameAllocator::alloc(size); +} + +static void pngFreeFn(png_structp /*png_ptr*/, png_voidp /*mem*/) +{ +} + +static png_voidp pngRealMallocFn(png_structp /*png_ptr*/, png_size_t size) +{ + return (png_voidp)dMalloc(size); +} + +static void pngRealFreeFn(png_structp /*png_ptr*/, png_voidp mem) +{ + dFree(mem); +} + +//-------------------------------------- +static void pngFatalErrorFn(png_structp /*png_ptr*/, + png_const_charp pMessage) +{ + AssertISV(false, avar("Error reading PNG file:\n %s", pMessage)); +} + + +//-------------------------------------- +static void pngWarningFn(png_structp, png_const_charp /*pMessage*/) +{ + // AssertWarn(false, avar("Warning reading PNG file:\n %s", pMessage)); +} + + +//-------------------------------------- +static bool sReadPNG(Stream &stream, GBitmap *bitmap) +{ + static const U32 cs_headerBytesChecked = 8; + + U8 header[cs_headerBytesChecked]; + stream.read(cs_headerBytesChecked, header); + + bool isPng = png_check_sig(header, cs_headerBytesChecked) != 0; + if (isPng == false) + { + AssertWarn(false, "GBitmap::readPNG: stream doesn't contain a PNG"); + return false; + } + + U32 prevWaterMark = FrameAllocator::getWaterMark(); + png_structp png_ptr = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, + NULL, + pngFatalErrorFn, + pngWarningFn, + NULL, + pngRealMallocFn, + pngRealFreeFn); + + if (png_ptr == NULL) + { + FrameAllocator::setWaterMark(prevWaterMark); + return false; + } + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) + { + png_destroy_read_struct(&png_ptr, + (png_infopp)NULL, + (png_infopp)NULL); + + FrameAllocator::setWaterMark(prevWaterMark); + return false; + } + + png_infop end_info = png_create_info_struct(png_ptr); + if (end_info == NULL) + { + png_destroy_read_struct(&png_ptr, + &info_ptr, + (png_infopp)NULL); + + FrameAllocator::setWaterMark(prevWaterMark); + return false; + } + + png_set_read_fn(png_ptr, &stream, pngReadDataFn); + + // Read off the info on the image. + png_set_sig_bytes(png_ptr, cs_headerBytesChecked); + png_read_info(png_ptr, info_ptr); + + // OK, at this point, if we have reached it ok, then we can reset the + // image to accept the new data... + // + bitmap->deleteImage(); + + png_uint_32 width; + png_uint_32 height; + S32 bit_depth; + S32 color_type; + + png_get_IHDR(png_ptr, info_ptr, + &width, &height, // obv. + &bit_depth, &color_type, // obv. + NULL, // interlace + NULL, // compression_type + NULL); // filter_type + + // First, handle the color transformations. We need this to read in the + // data as RGB or RGBA, _always_, with a maximal channel width of 8 bits. + // + bool transAlpha = false; + GFXFormat format = GFXFormatR8G8B8; + + // Strip off any 16 bit info + // + if (bit_depth == 16 && color_type != PNG_COLOR_TYPE_GRAY) + { + png_set_strip_16(png_ptr); + } + + // Expand a transparency channel into a full alpha channel... + // + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + { + png_set_expand(png_ptr); + transAlpha = true; + } + + if (color_type == PNG_COLOR_TYPE_PALETTE) + { + png_set_expand(png_ptr); + format = transAlpha ? GFXFormatR8G8B8A8 : GFXFormatR8G8B8; + } + else if (color_type == PNG_COLOR_TYPE_GRAY) + { + png_set_expand(png_ptr); + + if (bit_depth == 16) + format = GFXFormatR5G6B5; + else + format = GFXFormatA8; + } + else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + png_set_expand(png_ptr); + png_set_gray_to_rgb(png_ptr); + format = GFXFormatR8G8B8A8; + } + else if (color_type == PNG_COLOR_TYPE_RGB) + { + format = transAlpha ? GFXFormatR8G8B8A8 : GFXFormatR8G8B8; + png_set_expand(png_ptr); + } + else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) + { + png_set_expand(png_ptr); + format = GFXFormatR8G8B8A8; + } + + // Update the info pointer with the result of the transformations + // above... + png_read_update_info(png_ptr, info_ptr); + + png_uint_32 rowBytes = png_get_rowbytes(png_ptr, info_ptr); + if (format == GFXFormatR8G8B8) + { + AssertFatal(rowBytes == width * 3, + "Error, our rowbytes are incorrect for this transform... (3)"); + } + else if (format == GFXFormatR8G8B8A8) + { + AssertFatal(rowBytes == width * 4, + "Error, our rowbytes are incorrect for this transform... (4)"); + } + else if (format == GFXFormatR5G6B5) + { + AssertFatal(rowBytes == width * 2, + "Error, our rowbytes are incorrect for this transform... (2)"); + } + + // actually allocate the bitmap space... + bitmap->allocateBitmap(width, height, + false, // don't extrude miplevels... + format); // use determined format... + + // Set up the row pointers... + png_bytep* rowPointers = new png_bytep[ height ]; + U8* pBase = (U8*)bitmap->getBits(); + + for (U32 i = 0; i < height; i++) + rowPointers[i] = pBase + (i * rowBytes); + + // And actually read the image! + png_read_image(png_ptr, rowPointers); + + // We're outta here, destroy the png structs, and release the lock + // as quickly as possible... + //png_read_end(png_ptr, end_info); + delete [] rowPointers; + png_read_end(png_ptr, NULL); + png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); + + // Ok, the image is read in, now we need to finish up the initialization, + // which means: setting up the detailing members, init'ing the palette + // key, etc... + // + // actually, all of that was handled by allocateBitmap, so we're outta here + // + + // Check this bitmap for transparency + bitmap->checkForTransparency(); + + FrameAllocator::setWaterMark(prevWaterMark); + + return true; +} + +//-------------------------------------------------------------------------- +static bool _writePNG(GBitmap *bitmap, Stream &stream, U32 compressionLevel, U32 strategy, U32 filter) +{ + GFXFormat format = bitmap->getFormat(); + + // ONLY RGB bitmap writing supported at this time! + AssertFatal( format == GFXFormatR8G8B8 || + format == GFXFormatR8G8B8A8 || + format == GFXFormatR8G8B8X8 || + format == GFXFormatA8 || + format == GFXFormatR5G6B5, "_writePNG: ONLY RGB bitmap writing supported at this time."); + + if ( format != GFXFormatR8G8B8 && + format != GFXFormatR8G8B8A8 && + format != GFXFormatR8G8B8X8 && + format != GFXFormatA8 && + format != GFXFormatR5G6B5 ) + return false; + + png_structp png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, + NULL, + pngFatalErrorFn, + pngWarningFn, + NULL, + pngMallocFn, + pngFreeFn); + if (png_ptr == NULL) + return (false); + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) + { + png_destroy_write_struct(&png_ptr, (png_infopp)NULL); + return false; + } + + png_set_write_fn(png_ptr, &stream, pngWriteDataFn, pngFlushDataFn); + + // Set the compression level, image filters, and compression strategy... + png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY; + png_ptr->zlib_strategy = strategy; + png_set_compression_window_bits(png_ptr, 15); + png_set_compression_level(png_ptr, compressionLevel); + png_set_filter(png_ptr, 0, filter); + + // Set the image information here. Width and height are up to 2^31, + // bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on + // the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, + // PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, + // or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or + // PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST + // currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED + + U32 width = bitmap->getWidth(); + U32 height = bitmap->getHeight(); + + if (format == GFXFormatR8G8B8) + { + png_set_IHDR(png_ptr, info_ptr, + width, height, // the width & height + 8, PNG_COLOR_TYPE_RGB, // bit_depth, color_type, + NULL, // no interlace + NULL, // compression type + NULL); // filter type + } + else if (format == GFXFormatR8G8B8A8 || format == GFXFormatR8G8B8X8) + { + png_set_IHDR(png_ptr, info_ptr, + width, height, // the width & height + 8, PNG_COLOR_TYPE_RGB_ALPHA, // bit_depth, color_type, + NULL, // no interlace + NULL, // compression type + NULL); // filter type + } + else if (format == GFXFormatA8) + { + png_set_IHDR(png_ptr, info_ptr, + width, height, // the width & height + 8, PNG_COLOR_TYPE_GRAY, // bit_depth, color_type, + NULL, // no interlace + NULL, // compression type + NULL); // filter type + } + else if (format == GFXFormatR5G6B5) + { + png_set_IHDR(png_ptr, info_ptr, + width, height, // the width & height + 16, PNG_COLOR_TYPE_GRAY, // bit_depth, color_type, + PNG_INTERLACE_NONE, // no interlace + PNG_COMPRESSION_TYPE_DEFAULT, // compression type + PNG_FILTER_TYPE_DEFAULT); // filter type + + png_color_8_struct sigBit = { 0 }; + sigBit.gray = 16; + png_set_sBIT(png_ptr, info_ptr, &sigBit ); + + png_set_swap( png_ptr ); + } + + png_write_info(png_ptr, info_ptr); + FrameAllocatorMarker marker; + png_bytep* row_pointers = (png_bytep*)marker.alloc( height * sizeof( png_bytep ) ); + for (U32 i=0; i(bitmap->getAddress(0, i)); + + png_write_image(png_ptr, row_pointers); + + // Write S3TC data if present... + // Write FXT1 data if present... + + png_write_end(png_ptr, info_ptr); + png_destroy_write_struct(&png_ptr, (png_infopp)NULL); + + return true; +} + + +//-------------------------------------------------------------------------- +static bool sWritePNG(GBitmap *bitmap, Stream &stream, U32 compressionLevel) +{ + U32 waterMark = FrameAllocator::getWaterMark(); + + if ( compressionLevel < 10 ) + { + bool retVal = _writePNG(bitmap, stream, compressionLevel, 0, PNG_ALL_FILTERS); + FrameAllocator::setWaterMark(waterMark); + return retVal; + } + + // check all our methods of compression to find the best one and use it + U8* buffer = new U8[1 << 22]; // 4 Megs. Should be enough... + MemStream* pMemStream = new MemStream(1 << 22, buffer, false, true); + + const U32 zStrategies[] = { Z_DEFAULT_STRATEGY, + Z_FILTERED }; + const U32 pngFilters[] = { PNG_FILTER_NONE, + PNG_FILTER_SUB, + PNG_FILTER_UP, + PNG_FILTER_AVG, + PNG_FILTER_PAETH, + PNG_ALL_FILTERS }; + + U32 minSize = 0xFFFFFFFF; + U32 bestStrategy = 0xFFFFFFFF; + U32 bestFilter = 0xFFFFFFFF; + U32 bestCLevel = 0xFFFFFFFF; + + for (U32 cl = 0; cl <=9; cl++) + { + for (U32 zs = 0; zs < 2; zs++) + { + for (U32 pf = 0; pf < 6; pf++) + { + pMemStream->setPosition(0); + + U32 waterMarkInner = FrameAllocator::getWaterMark(); + + if (_writePNG(bitmap, *pMemStream, cl, zStrategies[zs], pngFilters[pf]) == false) + AssertFatal(false, "Handle this error!"); + + FrameAllocator::setWaterMark(waterMarkInner); + + if (pMemStream->getPosition() < minSize) + { + minSize = pMemStream->getPosition(); + bestStrategy = zs; + bestFilter = pf; + bestCLevel = cl; + } + } + } + } + AssertFatal(minSize != 0xFFFFFFFF, "Error, no best found?"); + + delete pMemStream; + delete [] buffer; + + + bool retVal = _writePNG(bitmap, stream, + bestCLevel, + zStrategies[bestStrategy], + pngFilters[bestFilter]); + FrameAllocator::setWaterMark(waterMark); + + return retVal; +} + +//-------------------------------------------------------------------------- +// Stores PNG stream data +struct DeferredPNGWriterData { + png_structp png_ptr; + png_infop info_ptr; + U32 width; + U32 height; +}; +DeferredPNGWriter::DeferredPNGWriter() : + mData( NULL ), + mActive(false) +{ + mData = new DeferredPNGWriterData(); +} +DeferredPNGWriter::~DeferredPNGWriter() +{ + delete mData; +} + +bool DeferredPNGWriter::begin( GFXFormat format, S32 width, S32 height, Stream &stream, U32 compressionLevel ) +{ + // ONLY RGB bitmap writing supported at this time! + AssertFatal( format == GFXFormatR8G8B8 || + format == GFXFormatR8G8B8A8 || + format == GFXFormatR8G8B8X8 || + format == GFXFormatA8 || + format == GFXFormatR5G6B5, "_writePNG: ONLY RGB bitmap writing supported at this time."); + + if ( format != GFXFormatR8G8B8 && + format != GFXFormatR8G8B8A8 && + format != GFXFormatR8G8B8X8 && + format != GFXFormatA8 && + format != GFXFormatR5G6B5 ) + return false; + + mData->png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, + NULL, + pngFatalErrorFn, + pngWarningFn, + NULL, + pngRealMallocFn, + pngRealFreeFn); + if (mData->png_ptr == NULL) + return (false); + + mData->info_ptr = png_create_info_struct(mData->png_ptr); + if (mData->info_ptr == NULL) + { + png_destroy_write_struct(&mData->png_ptr, (png_infopp)NULL); + return false; + } + + png_set_write_fn(mData->png_ptr, &stream, pngWriteDataFn, pngFlushDataFn); + + // Set the compression level, image filters, and compression strategy... + mData->png_ptr->flags |= PNG_FLAG_ZLIB_CUSTOM_STRATEGY; + mData->png_ptr->zlib_strategy = 0; + png_set_compression_window_bits(mData->png_ptr, 15); + png_set_compression_level(mData->png_ptr, compressionLevel); + png_set_filter(mData->png_ptr, 0, PNG_ALL_FILTERS); + + // Set the image information here. Width and height are up to 2^31, + // bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on + // the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, + // PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, + // or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or + // PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST + // currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED + + if (format == GFXFormatR8G8B8) + { + png_set_IHDR(mData->png_ptr, mData->info_ptr, + width, height, // the width & height + 8, PNG_COLOR_TYPE_RGB, // bit_depth, color_type, + NULL, // no interlace + NULL, // compression type + NULL); // filter type + } + else if (format == GFXFormatR8G8B8A8 || format == GFXFormatR8G8B8X8) + { + png_set_IHDR(mData->png_ptr, mData->info_ptr, + width, height, // the width & height + 8, PNG_COLOR_TYPE_RGB_ALPHA, // bit_depth, color_type, + NULL, // no interlace + NULL, // compression type + NULL); // filter type + } + else if (format == GFXFormatA8) + { + png_set_IHDR(mData->png_ptr, mData->info_ptr, + width, height, // the width & height + 8, PNG_COLOR_TYPE_GRAY, // bit_depth, color_type, + NULL, // no interlace + NULL, // compression type + NULL); // filter type + } + else if (format == GFXFormatR5G6B5) + { + png_set_IHDR(mData->png_ptr, mData->info_ptr, + width, height, // the width & height + 16, PNG_COLOR_TYPE_GRAY, // bit_depth, color_type, + PNG_INTERLACE_NONE, // no interlace + PNG_COMPRESSION_TYPE_DEFAULT, // compression type + PNG_FILTER_TYPE_DEFAULT); // filter type + + png_color_8_struct sigBit = { 0 }; + sigBit.gray = 16; + png_set_sBIT(mData->png_ptr, mData->info_ptr, &sigBit ); + + png_set_swap( mData->png_ptr ); + } + + png_write_info(mData->png_ptr, mData->info_ptr); + + mActive = true; + + return true; +} +void DeferredPNGWriter::append( GBitmap* bitmap, U32 rows) +{ + AssertFatal(mActive, "Cannot append to an inactive DeferredPNGWriter!"); + + U32 height = getMin( bitmap->getHeight(), rows); + + FrameAllocatorMarker marker; + png_bytep* row_pointers = (png_bytep*)marker.alloc( height * sizeof( png_bytep ) ); + for (U32 i=0; i(bitmap->getAddress(0, i)); + + png_write_rows(mData->png_ptr, row_pointers, height); +} +void DeferredPNGWriter::end() +{ + AssertFatal(mActive, "Cannot end an inactive DeferredPNGWriter!"); + + png_write_end(mData->png_ptr, mData->info_ptr); + png_destroy_write_struct(&mData->png_ptr, (png_infopp)NULL); + + mActive = false; +} \ No newline at end of file diff --git a/Engine/source/gfx/bitmap/loaders/bitmapTga.cpp b/Engine/source/gfx/bitmap/loaders/bitmapTga.cpp new file mode 100644 index 000000000..15812ff9b --- /dev/null +++ b/Engine/source/gfx/bitmap/loaders/bitmapTga.cpp @@ -0,0 +1,489 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/stream/stream.h" + +#include "gfx/bitmap/gBitmap.h" + + +static bool sReadTGA(Stream &stream, GBitmap *bitmap); +static bool sWriteTGA(GBitmap *bitmap, Stream &stream, U32 compressionLevel); + +static struct _privateRegisterTGA +{ + _privateRegisterTGA() + { + GBitmap::Registration reg; + + reg.extensions.push_back( "tga" ); + + reg.readFunc = sReadTGA; + reg.writeFunc = sWriteTGA; + + GBitmap::sRegisterFormat( reg ); + } +} sStaticRegisterTGA; + + +//------------------------------------------------------------------------------ +//-------------------------------------- Supplementary I/O +// + +enum eImageType +{ + TypeNoData = 0, + TypeUncPaletted = 1, + TypeUncTruecolor = 2, + TypeUncGrayscale = 3, + TypeRlePaletted = 9, + TypeRleTruecolor = 10, + TypeRleGrayscale = 11 +}; + +enum ePixelMap +{ + MapLowerLeft = 0, + MapLowerRight = 1, + MapUpperLeft = 2, + MapUpperRight = 3, +}; + +static void tga_write_pixel_to_mem( U8 * dat, U8 img_spec, U32 number, + U32 w, U32 h, U32 pixel, U32 bppOut ) +{ + // write the pixel to the data regarding how the + // header says the data is ordered. + + U32 x, y; + + switch( (img_spec & 0x30) >> 4 ) + { + case MapLowerRight: + x = w - 1 - (number % w); + y = h - 1 - (number / w); + break; + + case MapUpperLeft: + x = number % w; + y = number / w; + break; + + case MapUpperRight: + x = w - 1 - (number % w); + y = number / w; + break; + + case MapLowerLeft: + default: + x = number % w; + y = h - 1 - (number / w); + break; + + } + + U32 addy = (y * w + x) * bppOut; + for ( U32 j = 0; j < bppOut; j++ ) + dat[addy + j] = (U8)((pixel >> (j * 8)) & 0xFF); +} + +static U32 tga_get_pixel( Stream& stream, U8 bppIn, + U8 * colormap, U8 cmapBytesEntry ) +{ + /* get the image data value out */ + + U32 tmp_int32 = 0; + + for ( U32 j = 0; j < bppIn; j++ ) + { + U8 tmp_byte; + if ( !stream.read( &tmp_byte ) ) + tmp_int32 = 0; + else + tmp_int32 += tmp_byte << (j * 8); + } + + /* byte-order correct the thing */ + switch( bppIn ) + { + case 2: + tmp_int32 = convertLEndianToHost( (U16)tmp_int32 ); + break; + + case 3: /* intentional fall-thru */ + case 4: + tmp_int32 = convertLEndianToHost( tmp_int32 ); + break; + } + + U32 tmp_col; + + if ( colormap ) + { + /* need to look up value to get real color */ + tmp_col = 0; + for ( U32 j = 0; j < cmapBytesEntry; j++ ) + tmp_col += colormap[cmapBytesEntry * tmp_int32 + j] << (8 * j); + } + else + { + tmp_col = tmp_int32; + } + + return tmp_col; +} + +static U32 tga_convert_color( U32 pixel, U32 bppIn, U8 alphabits, U32 bppOut ) +{ + // this is not only responsible for converting from different depths + // to other depths, it also switches BGR to RGB. + + // this thing will also premultiply alpha, on a pixel by pixel basis. + + U8 r, g, b, a; + + switch( bppIn ) + { + case 32: + if ( alphabits == 0 ) + goto is_24_bit_in_disguise; + // 32-bit to 32-bit -- nop. + break; + + case 24: +is_24_bit_in_disguise: + // 24-bit to 32-bit; (only force alpha to full) + pixel |= 0xFF000000; + break; + + case 15: +is_15_bit_in_disguise: + r = (U8)(((F32)((pixel & 0x7C00) >> 10)) * 8.2258f); + g = (U8)(((F32)((pixel & 0x03E0) >> 5 )) * 8.2258f); + b = (U8)(((F32)(pixel & 0x001F)) * 8.2258f); + // 15-bit to 32-bit; (force alpha to full) + pixel = 0xFF000000 + (r << 16) + (g << 8) + b; + break; + + case 16: + if ( alphabits == 1 ) + goto is_15_bit_in_disguise; + + // 16-bit to 32-bit; (force alpha to full) + r = (U8)(((F32)((pixel & 0xF800) >> 11)) * 8.2258f); + g = (U8)(((F32)((pixel & 0x07E0) >> 5 )) * 4.0476f); + b = (U8)(((F32)(pixel & 0x001F)) * 8.2258f); + pixel = 0xFF000000 + (r << 16) + (g << 8) + b; + break; + } + + // convert the 32-bit pixel from BGR to RGB. + pixel = (pixel & 0xFF00FF00) + ((pixel & 0xFF) << 16) + ((pixel & 0xFF0000) >> 16); + + r = pixel & 0x000000FF; + g = (pixel & 0x0000FF00) >> 8; + b = (pixel & 0x00FF0000) >> 16; + a = (pixel & 0xFF000000) >> 24; + + // not premultiplied alpha -- multiply. + r = (U8)(((F32)r / 255.0f) * ((F32)a / 255.0f) * 255.0f); + g = (U8)(((F32)g / 255.0f) * ((F32)a / 255.0f) * 255.0f); + b = (U8)(((F32)b / 255.0f) * ((F32)a / 255.0f) * 255.0f); + + pixel = r + (g << 8) + (b << 16) + (a << 24); + + /* now convert from 32-bit to whatever they want. */ + switch( bppOut ) + { + case 4: + // 32 to 32 -- nop. + break; + + case 3: + // 32 to 24 -- discard alpha. + pixel &= 0x00FFFFFF; + break; + } + + return pixel; +} + +static bool sReadTGA(Stream &stream, GBitmap *bitmap) +{ + struct Header + { + U8 idLength; // length of the image_id string below. + U8 cmapType; // paletted image <=> cmapType + U8 imageType; // can be any of the IMG_TYPE constants above. + U16 cmapFirst; // + U16 cmapLength; // how long the colormap is + U8 cmapEntrySize; // how big a palette entry is. + U16 xOrigin; // the x origin of the image in the image data. + U16 yOrigin; // the y origin of the image in the image data. + U16 width; // the width of the image. + U16 height; // the height of the image. + U8 pixelDepth; // the depth of a pixel in the image. + U8 imageDesc; // the image descriptor. + }; + + // Read header + Header header; + stream.read( &header.idLength ); + stream.read( &header.cmapType ); + stream.read( &header.imageType ); + stream.read( &header.cmapFirst ); + stream.read( &header.cmapLength ); + stream.read( &header.cmapEntrySize ); + stream.read( &header.xOrigin ); + stream.read( &header.yOrigin ); + stream.read( &header.width ); + stream.read( &header.height ); + stream.read( &header.pixelDepth ); + stream.read( &header.imageDesc ); + + U32 numPixels = header.width * header.height; + if ( numPixels == 0 ) + { + //Con::errorf( "Texture has width and/or height set to 0" ); + return false; + } + + U8 alphabits = header.imageDesc & 0x0F; + + /* seek past the image id, if there is one */ + if ( header.idLength ) + { + if ( !stream.setPosition( stream.getPosition() + header.idLength ) ) + { + //Con::errorf( "Unexpected end of stream encountered" ); + return false; + } + } + + /* if this is a 'nodata' image, just jump out. */ + if ( header.imageType == TypeNoData ) + { + //Con::errorf( "Texture contains no data" ); + return false; + } + + /* deal with the colormap, if there is one. */ + U8* colormap = NULL; + U32 cmapBytes = 0; + U8 cmapBytesEntry = 0; + + if ( header.cmapType ) + { + switch( header.imageType ) + { + case TypeUncPaletted: + case TypeRlePaletted: + break; + + case TypeUncTruecolor: + case TypeRleTruecolor: + // this should really be an error, but some really old + // crusty targas might actually be like this (created by TrueVision, no less!) + // so, we'll hack our way through it. + break; + + case TypeUncGrayscale: + case TypeRleGrayscale: + //Con::errorf( "Found colormap for a grayscale image" ); + return false; + } + + /* ensure colormap entry size is something we support */ + if ( !(header.cmapEntrySize == 15 || + header.cmapEntrySize == 16 || + header.cmapEntrySize == 24 || + header.cmapEntrySize == 32) ) + { + //Con::errorf( "Unsupported colormap entry size" ); + return false; + } + + /* allocate memory for a colormap */ + if ( header.cmapEntrySize & 0x07 ) + cmapBytesEntry = (((8 - (header.cmapEntrySize & 0x07)) + header.cmapEntrySize) >> 3); + else + cmapBytesEntry = (header.cmapEntrySize >> 3); + + cmapBytes = cmapBytesEntry * header.cmapLength; + colormap = new U8[ cmapBytes ]; + + for ( U32 i = 0; i < header.cmapLength; i++ ) + { + /* seek ahead to first entry used */ + if ( header.cmapFirst != 0 ) + stream.setPosition( stream.getPosition() + header.cmapFirst * cmapBytesEntry ); + + U32 tmp_int32 = 0; + for ( U32 j = 0; j < cmapBytesEntry; j++ ) + { + U8 tmp_byte; + if ( !stream.read( &tmp_byte ) ) + { + delete [] colormap; + //Con::errorf( "Bad colormap" ); + return false; + } + tmp_int32 += tmp_byte << (j * 8); + } + + // byte order correct. + tmp_int32 = convertLEndianToHost( tmp_int32 ); + + for ( U32 j = 0; j < cmapBytesEntry; j++ ) + colormap[i * cmapBytesEntry + j] = (tmp_int32 >> (8 * j)) & 0xFF; + } + } + + // compute number of bytes in an image data unit (either index or BGR triple) + U8 inBytesPerPixel = 0; + if ( header.pixelDepth & 0x07 ) + inBytesPerPixel = (((8 - (header.pixelDepth & 0x07)) + header.pixelDepth) >> 3); + else + inBytesPerPixel = (header.pixelDepth >> 3); + + /* assume that there's one byte per pixel */ + if ( inBytesPerPixel == 0 ) + inBytesPerPixel = 1; + + GFXFormat gfxFmt; + U32 outBytesPerPixel; + switch ( header.pixelDepth ) + { + case 32: + gfxFmt = GFXFormatR8G8B8A8; + outBytesPerPixel = 4; + break; + + case 24: + default: + gfxFmt = GFXFormatR8G8B8; + outBytesPerPixel = 3; + break; + } + + bitmap->allocateBitmap( header.width, header.height, false, gfxFmt ); + + // compute the true number of bits per pixel + U8 trueBitsPerPixel = header.cmapType ? header.cmapEntrySize : header.pixelDepth; + + // Override the number of alpha bits if necessary + // Some apps generate transparent TGAs with alphabits set to 0 in the image descriptor + if ( ( trueBitsPerPixel == 32 ) && ( alphabits == 0 ) ) + alphabits = 8; + + switch( header.imageType ) + { + case TypeUncTruecolor: + case TypeUncGrayscale: + case TypeUncPaletted: + + /* FIXME: support grayscale */ + + for ( U32 i = 0; i < numPixels; i++ ) + { + // get the color value. + U32 tmp_col = tga_get_pixel( stream, inBytesPerPixel, colormap, cmapBytesEntry ); + tmp_col = tga_convert_color( tmp_col, trueBitsPerPixel, alphabits, outBytesPerPixel ); + + // now write the data out. + tga_write_pixel_to_mem( bitmap->getAddress( 0, 0 ), header.imageDesc, + i, header.width, header.height, tmp_col, outBytesPerPixel ); + } + break; + + case TypeRleTruecolor: + case TypeRleGrayscale: + case TypeRlePaletted: + + // FIXME: handle grayscale.. + + for ( U32 i = 0; i < numPixels; ) + { + /* a bit of work to do to read the data.. */ + U8 packet_header; + if ( !stream.read( 1, &packet_header ) ) + { + // well, just let them fill the rest with null pixels then... + packet_header = 1; + } + + if ( packet_header & 0x80 ) + { + /* run length packet */ + U32 tmp_col = tga_get_pixel( stream, inBytesPerPixel, colormap, cmapBytesEntry ); + tmp_col = tga_convert_color( tmp_col, trueBitsPerPixel, alphabits, outBytesPerPixel ); + + U8 repcount = (packet_header & 0x7F) + 1; + + /* write all the data out */ + for ( U32 j = 0; j < repcount; j++ ) + { + tga_write_pixel_to_mem( bitmap->getAddress( 0, 0 ), header.imageDesc, + i + j, header.width, header.height, tmp_col, outBytesPerPixel ); + } + + i += repcount; + + } + else + { + /* raw packet */ + /* get pixel from file */ + U8 repcount = (packet_header & 0x7F) + 1; + + for ( U32 j = 0; j < repcount; j++ ) + { + U32 tmp_col = tga_get_pixel( stream, inBytesPerPixel, colormap, cmapBytesEntry ); + tmp_col = tga_convert_color( tmp_col, trueBitsPerPixel, alphabits, outBytesPerPixel ); + + tga_write_pixel_to_mem( bitmap->getAddress( 0, 0 ), header.imageDesc, + i + j, header.width, header.height, tmp_col, outBytesPerPixel ); + } + + i += repcount; + } + } + break; + + default: + //Con::errorf( "Unknown image type" ); + return false; + } + + delete [] colormap; + + // 32-bit tgas have an alpha channel + bitmap->setHasTransparency( header.pixelDepth == 32 ); + + return true; +} + +static bool sWriteTGA(GBitmap *bitmap, Stream &stream, U32 compressionLevel) +{ + AssertISV(false, "GBitmap::writeTGA - doesn't support writing tga files!") + + return false; +} diff --git a/Engine/source/gfx/bitmap/pngUtils.h b/Engine/source/gfx/bitmap/pngUtils.h new file mode 100644 index 000000000..787f5272b --- /dev/null +++ b/Engine/source/gfx/bitmap/pngUtils.h @@ -0,0 +1,49 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PNG_UTILS_H_ +#define _PNG_UTILS_H_ + +#ifndef _GFXENUMS_H_ +#include "gfx/gfxEnums.h" +#endif + +struct DeferredPNGWriterData; // This is used to avoid including png.h in this header +class GBitmap; +class Stream; + +/// This class is used to write PNGs in row batches +class DeferredPNGWriter { +protected: + DeferredPNGWriterData *mData; + bool mActive; + +public: + DeferredPNGWriter(); + ~DeferredPNGWriter(); + + bool begin( GFXFormat format, S32 width, S32 height, Stream &stream, U32 compressionLevel ); + void append( GBitmap* bitmap, U32 rows ); + void end(); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/gfx/gFont.cpp b/Engine/source/gfx/gFont.cpp new file mode 100644 index 000000000..ec8ce4205 --- /dev/null +++ b/Engine/source/gfx/gFont.cpp @@ -0,0 +1,1289 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/gFont.h" + +#include "core/resourceManager.h" +#include "core/stream/fileStream.h" +#include "core/strings/unicode.h" +#include "core/strings/findMatch.h" +#include "core/strings/stringFunctions.h" +#include "core/util/endian.h" +#include "core/util/safeDelete.h" +#include "console/console.h" +#include "console/engineAPI.h" +#include "platform/threads/mutex.h" +#include "zlib/zlib.h" + + +GFX_ImplementTextureProfile(GFXFontTextureProfile, + GFXTextureProfile::DiffuseMap, + GFXTextureProfile::PreserveSize | + GFXTextureProfile::Static | + GFXTextureProfile::KeepBitmap | + GFXTextureProfile::NoMipmap, + GFXTextureProfile::None); + +template<> void *Resource::create(const Torque::Path &path) +{ +#ifdef TORQUE_DEBUG_RES_MANAGER + Con::printf( "Resource::create - [%s]", path.getFullPath().c_str() ); +#endif + + return GFont::load( path ); +} + +template<> ResourceBase::Signature Resource::signature() +{ + return MakeFourCC('f','o','n','t'); +} + +/// Used for repacking in GFont::importStrip. +struct GlyphMap +{ + U32 charId; + GBitmap *bitmap; +}; + +static S32 QSORT_CALLBACK GlyphMapCompare(const void *a, const void *b) +{ + S32 ha = ((GlyphMap *) a)->bitmap->getHeight(); + S32 hb = ((GlyphMap *) b)->bitmap->getHeight(); + + return hb - ha; +} + + +const U32 GFont::csm_fileVersion = 3; + +String GFont::getFontCacheFilename(const String &faceName, U32 size) +{ + return String::ToString("%s/%s %d (%s).uft", + Con::getVariable("$GUI::fontCacheDirectory"), faceName.c_str(), size, getCharSetName(0)); +} + +GFont* GFont::load( const Torque::Path& path ) +{ + FileStream stream; + + stream.open( path.getFullPath(), Torque::FS::File::Read ); + if ( stream.getStatus() != Stream::Ok ) + return NULL; + + GFont *ret = new GFont; + ret->mGFTFile = path; + + if(!ret->read(stream)) + { + Con::errorf( "GFont::load - error reading '%s'", path.getFullPath().c_str() ); + SAFE_DELETE(ret); + } + else + { +#ifndef TORQUE_OS_XENON + PlatformFont *platFont = createPlatformFont(ret->getFontFaceName(), ret->getFontSize(), ret->getFontCharSet()); + + if ( platFont == NULL ) + { + Con::errorf( "GFont::load - error creating platform font for '%s'", path.getFullPath().c_str() ); + SAFE_DELETE(ret); + } + else + ret->setPlatformFont(platFont); +#endif + } + + return ret; +} + +Resource GFont::create(const String &faceName, U32 size, const char *cacheDirectory, U32 charset /* = TGE_ANSI_CHARSET */) +{ + if( !cacheDirectory ) + cacheDirectory = Con::getVariable( "$GUI::fontCacheDirectory" ); + + const Torque::Path path( String::ToString("%s/%s %d (%s).uft", + cacheDirectory, faceName.c_str(), size, getCharSetName(charset)) ); + + Resource ret; + + // If the file already exists attempt to load it + if (Platform::isFile(path.getFullPath().c_str())) + { + ret = ResourceManager::get().load(path); + + if (ret != NULL) + { + ret->mGFTFile = path; + return ret; + } + } + + // Otherwise attempt to have the platform generate a new font + PlatformFont *platFont = createPlatformFont(faceName, size, charset); + + if (platFont == NULL) + { + String fontName; + +#ifdef _XBOX + //AssertFatal( false, "Font creation is not supported on the Xbox platform. Create the font files (*.uft) using the Windows/MacOS build of the project." ); + if(!faceName.equal("arial", String::NoCase) || size != 14) + { + return create("Arial", 14, cacheDirectory, charset); + } + return ret; +#endif + + // Couldn't load the requested font. This probably will be common + // since many unix boxes don't have arial or lucida console installed. + // Attempt to map the font name into a font we're pretty sure exist + // Lucida Console is a common code & console font on windows, and + // Monaco is the recommended code & console font on mac. + if (faceName.equal("arial", String::NoCase)) + fontName = "Helvetica"; + else if (faceName.equal("lucida console", String::NoCase)) + fontName = "Monaco"; + else if (faceName.equal("monaco", String::NoCase)) + fontName = "Courier"; + else + return ret; + + return create(fontName, size, cacheDirectory, charset); + } + + // Create the actual GFont and set some initial properties + GFont *font = new GFont; + font->mPlatformFont = platFont; + font->mGFTFile = path; + font->mFaceName = faceName; + font->mSize = size; + font->mCharSet = charset; + + font->mHeight = platFont->getFontHeight(); + font->mBaseline = platFont->getFontBaseLine(); + font->mAscent = platFont->getFontBaseLine(); + font->mDescent = platFont->getFontHeight() - platFont->getFontBaseLine(); + + // Flag it to save when we exit + font->mNeedSave = true; + + // Load the newly created font into the ResourceManager + ret.setResource(ResourceManager::get().load(path), font); + + return ret; +} + +//------------------------------------------------------------------------- + +GFont::GFont() +{ + VECTOR_SET_ASSOCIATION(mCharInfoList); + VECTOR_SET_ASSOCIATION(mTextureSheets); + + for (U32 i = 0; i < (sizeof(mRemapTable) / sizeof(S32)); i++) + mRemapTable[i] = -1; + + mCurX = mCurY = mCurSheet = -1; + + mPlatformFont = NULL; + mSize = 0; + mCharSet = 0; + mNeedSave = false; + + mMutex = Mutex::createMutex(); +} + +GFont::~GFont() +{ + if(mNeedSave) + { + AssertFatal( mGFTFile.getFullPath().isNotEmpty(), "GFont::~GFont - path not set" ); + + FileStream stream; + stream.open(mGFTFile, Torque::FS::File::Write); + + if(stream.getStatus() == Stream::Ok) + write(stream); + + stream.close(); + } + + S32 i; + + for(i = 0;i < mCharInfoList.size();i++) + { + SAFE_DELETE_ARRAY(mCharInfoList[i].bitmapData); + } + + for(i=0; imapEnd) mapEnd = i; + } + } + + + // Let's write out all the info we can on this font. + Con::printf(" '%s' %dpt", mFaceName.c_str(), mSize); + Con::printf(" - %d texture sheets, %d mapped characters.", mTextureSheets.size(), mapCount); + + if(mapCount) + Con::printf(" - Codepoints range from 0x%x to 0x%x.", mapBegin, mapEnd); + else + Con::printf(" - No mapped codepoints.", mapBegin, mapEnd); + Con::printf(" - Platform font is %s.", (mPlatformFont ? "present" : "not present") ); +} + +//----------------------------------------------------------------------------- + +bool GFont::loadCharInfo(const UTF16 ch) +{ + PROFILE_SCOPE(GFont_loadCharInfo); + + if(mRemapTable[ch] != -1) + return true; // Not really an error + + if(mPlatformFont && mPlatformFont->isValidChar(ch)) + { + Mutex::lockMutex(mMutex); // the CharInfo returned by mPlatformFont is static data, must protect from changes. + PlatformFont::CharInfo &ci = mPlatformFont->getCharInfo(ch); + if(ci.bitmapData) + addBitmap(ci); + + mCharInfoList.push_back(ci); + mRemapTable[ch] = mCharInfoList.size() - 1; + + mNeedSave = true; + + Mutex::unlockMutex(mMutex); + return true; + } + + return false; +} + +void GFont::addBitmap(PlatformFont::CharInfo &charInfo) +{ + U32 nextCurX = U32(mCurX + charInfo.width ); /*7) & ~0x3;*/ + U32 nextCurY = U32(mCurY + mPlatformFont->getFontHeight()); // + 7) & ~0x3; + + // These are here for postmortem debugging. + bool routeA = false, routeB = false; + + if(mCurSheet == -1 || nextCurY >= TextureSheetSize) + { + routeA = true; + addSheet(); + + // Recalc our nexts. + nextCurX = U32(mCurX + charInfo.width); // + 7) & ~0x3; + nextCurY = U32(mCurY + mPlatformFont->getFontHeight()); // + 7) & ~0x3; + } + + if( nextCurX >= TextureSheetSize) + { + routeB = true; + mCurX = 0; + mCurY = nextCurY; + + // Recalc our nexts. + nextCurX = U32(mCurX + charInfo.width); // + 7) & ~0x3; + nextCurY = U32(mCurY + mPlatformFont->getFontHeight()); // + 7) & ~0x3; + } + + // Check the Y once more - sometimes we advance to a new row and run off + // the end. + if(nextCurY >= TextureSheetSize) + { + routeA = true; + addSheet(); + + // Recalc our nexts. + nextCurX = U32(mCurX + charInfo.width); // + 7) & ~0x3; + nextCurY = U32(mCurY + mPlatformFont->getFontHeight()); // + 7) & ~0x3; + } + + charInfo.bitmapIndex = mCurSheet; + charInfo.xOffset = mCurX; + charInfo.yOffset = mCurY; + + mCurX = nextCurX; + + S32 x, y; + GBitmap *bmp = mTextureSheets[mCurSheet].getBitmap(); + + AssertFatal(bmp->getFormat() == GFXFormatA8, "GFont::addBitmap - cannot added characters to non-greyscale textures!"); + + for(y = 0;y < charInfo.height;y++) + for(x = 0;x < charInfo.width;x++) + *bmp->getAddress(x + charInfo.xOffset, y + charInfo.yOffset) = charInfo.bitmapData[y * charInfo.width + x]; + + mTextureSheets[mCurSheet].refresh(); +} + +void GFont::addSheet() +{ + GBitmap *bitmap = new GBitmap(TextureSheetSize, TextureSheetSize, false, GFXFormatA8 ); + + // Set everything to transparent. + U8 *bits = bitmap->getWritableBits(); + dMemset(bits, 0, sizeof(U8) *TextureSheetSize*TextureSheetSize); + + GFXTexHandle handle = GFXTexHandle( bitmap, &GFXFontTextureProfile, true, avar("%s() - (line %d)", __FUNCTION__, __LINE__) ); + mTextureSheets.increment(); + mTextureSheets.last() = handle; + + mCurX = 0; + mCurY = 0; + mCurSheet = mTextureSheets.size() - 1; +} + +//----------------------------------------------------------------------------- + +const PlatformFont::CharInfo &GFont::getCharInfo(const UTF16 in_charIndex) +{ + PROFILE_SCOPE(GFont_getCharInfo); + + AssertFatal(in_charIndex, "GFont::getCharInfo - can't get info for char 0!"); + + if(mRemapTable[in_charIndex] == -1) + loadCharInfo(in_charIndex); + + AssertFatal(mRemapTable[in_charIndex] != -1, "No remap info for this character"); + + return mCharInfoList[mRemapTable[in_charIndex]]; +} + +const PlatformFont::CharInfo &GFont::getDefaultCharInfo() +{ + static PlatformFont::CharInfo c; + // c is initialized by the CharInfo default constructor. + return c; +} + +//----------------------------------------------------------------------------- + +U32 GFont::getStrWidth(const UTF8* in_pString) +{ + AssertFatal(in_pString != NULL, "GFont::getStrWidth: String is NULL, height is undefined"); + // If we ain't running debug... + if (in_pString == NULL || *in_pString == '\0') + return 0; + + return getStrNWidth(in_pString, dStrlen(in_pString)); +} + +U32 GFont::getStrWidthPrecise(const UTF8* in_pString) +{ + AssertFatal(in_pString != NULL, "GFont::getStrWidth: String is NULL, height is undefined"); + // If we ain't running debug... + if (in_pString == NULL) + return 0; + + return getStrNWidthPrecise(in_pString, dStrlen(in_pString)); +} + +//----------------------------------------------------------------------------- +U32 GFont::getStrNWidth(const UTF8 *str, U32 n) +{ + // UTF8 conversion is expensive. Avoid converting in a tight loop. + FrameTemp str16(n + 1); + convertUTF8toUTF16(str, str16, n + 1); + return getStrNWidth(str16, dStrlen(str16)); +} + +U32 GFont::getStrNWidth(const UTF16 *str, U32 n) +{ + AssertFatal(str != NULL, "GFont::getStrNWidth: String is NULL"); + + if (str == NULL || str[0] == '\0' || n == 0) + return 0; + + U32 totWidth = 0; + UTF16 curChar; + U32 charCount; + + for(charCount = 0; charCount < n; charCount++) + { + curChar = str[charCount]; + if(curChar == '\0') + break; + + if(isValidChar(curChar)) + { + const PlatformFont::CharInfo& rChar = getCharInfo(curChar); + totWidth += rChar.xIncrement; + } + else if (curChar == dT('\t')) + { + const PlatformFont::CharInfo& rChar = getCharInfo(dT(' ')); + totWidth += rChar.xIncrement * TabWidthInSpaces; + } + } + + return(totWidth); +} + +U32 GFont::getStrNWidthPrecise(const UTF8 *str, U32 n) +{ + FrameTemp str16(n + 1); + convertUTF8toUTF16(str, str16, n + 1); + return getStrNWidthPrecise(str16, dStrlen(str16)); +} + +U32 GFont::getStrNWidthPrecise(const UTF16 *str, U32 n) +{ + AssertFatal(str != NULL, "GFont::getStrNWidth: String is NULL"); + + if (str == NULL || str[0] == '\0' || n == 0) + return(0); + + U32 totWidth = 0; + UTF16 curChar; + U32 charCount = 0; + + for(charCount = 0; charCount < n; charCount++) + { + curChar = str[charCount]; + if(curChar == '\0') + break; + + if(isValidChar(curChar)) + { + const PlatformFont::CharInfo& rChar = getCharInfo(curChar); + totWidth += rChar.xIncrement; + } + else if (curChar == dT('\t')) + { + const PlatformFont::CharInfo& rChar = getCharInfo(dT(' ')); + totWidth += rChar.xIncrement * TabWidthInSpaces; + } + } + + UTF16 endChar = str[getMin(charCount,n-1)]; + + if (isValidChar(endChar)) + { + const PlatformFont::CharInfo& rChar = getCharInfo(endChar); + if (rChar.width != rChar.xIncrement) + totWidth += (rChar.width - rChar.xIncrement); + } + + return(totWidth); +} + +U32 GFont::getBreakPos(const UTF16 *str16, U32 slen, U32 width, bool breakOnWhitespace) +{ + // Some early out cases. + if(slen==0) + return 0; + + U32 ret = 0; + U32 lastws = 0; + UTF16 c; + U32 charCount = 0; + + for( charCount=0; charCount < slen; charCount++) + { + c = str16[charCount]; + if(c == '\0') + break; + + if(c == dT('\t')) + c = dT(' '); + + if(!isValidChar(c)) + { + ret++; + continue; + } + + if(c == dT(' ')) + lastws = ret+1; + + const PlatformFont::CharInfo& rChar = getCharInfo(c); + if(rChar.width > width || rChar.xIncrement > width) + { + if(lastws && breakOnWhitespace) + return lastws; + return ret; + } + + width -= rChar.xIncrement; + + ret++; + } + return ret; +} + +void GFont::wrapString(const UTF8 *txt, U32 lineWidth, Vector &startLineOffset, Vector &lineLen) +{ + // TODO: Is this error still true? + //Con::errorf("GFont::wrapString(): Not yet converted to be UTF-8 safe"); + + startLineOffset.clear(); + lineLen.clear(); + + if (!txt || !txt[0] || lineWidth < getCharWidth('W')) //make sure the line width is greater then a single character + return; + + U32 len = dStrlen(txt); + + U32 startLine; + + for (U32 i = 0; i < len;) + { + startLine = i; + startLineOffset.push_back(startLine); + + // loop until the string is too large + bool needsNewLine = false; + U32 lineStrWidth = 0; + for (; i < len; i++) + { + if( txt[ i ] == '\n' ) + { + needsNewLine = true; + break; + } + else if(isValidChar(txt[i])) + { + lineStrWidth += getCharInfo(txt[i]).xIncrement; + if( lineStrWidth > lineWidth ) + { + needsNewLine = true; + break; + } + } + } + + if (!needsNewLine) + { + // we are done! + lineLen.push_back(i - startLine); + return; + } + + S32 j; + + // Did we hit a hardwrap (newline character) in the string. + bool hardwrap = ( txt[i] == '\n' ); + + if ( hardwrap ) + { + j = i; + } + // determine where to put the newline + // we need to backtrack until we find a space character + // we don't do this for hardwrap(s) + else + { + for (j = i - 1; j >= startLine; j--) + { + if ( dIsspace(txt[j]) || txt[i] == '\n' ) + break; + } + + if (j < startLine) + { + // the line consists of a single word! + // So, just break up the word + + j = i - 1; + } + } + + lineLen.push_back(j - startLine); + i = j; + + // Now we need to increment through any space characters at the + // beginning of the next line. + // We don't skip spaces after a hardwrap because they were obviously intended. + for (i++; i < len; i++) + { + if ( txt[i] == '\n' ) + continue; + + if ( dIsspace( txt[i] ) && !hardwrap ) + continue; + + break; + } + } +} + +//----------------------------------------------------------------------------- + +bool GFont::read(Stream& io_rStream) +{ + // Handle versioning + U32 version; + io_rStream.read(&version); + if(version != csm_fileVersion) + return false; + + char buf[256]; + io_rStream.readString(buf); + mFaceName = buf; + + io_rStream.read(&mSize); + io_rStream.read(&mCharSet); + + io_rStream.read(&mHeight); + io_rStream.read(&mBaseline); + io_rStream.read(&mAscent); + io_rStream.read(&mDescent); + + U32 size = 0; + io_rStream.read(&size); + mCharInfoList.setSize(size); + U32 i; + for(i = 0; i < size; i++) + { + PlatformFont::CharInfo *ci = &mCharInfoList[i]; + io_rStream.read(&ci->bitmapIndex); + io_rStream.read(&ci->xOffset); + io_rStream.read(&ci->yOffset); + io_rStream.read(&ci->width); + io_rStream.read(&ci->height); + io_rStream.read(&ci->xOrigin); + io_rStream.read(&ci->yOrigin); + io_rStream.read(&ci->xIncrement); + ci->bitmapData = NULL; + } + + U32 numSheets = 0; + io_rStream.read(&numSheets); + + for(i = 0; i < numSheets; i++) + { + GBitmap *bmp = new GBitmap; + if(!bmp->readBitmap("png", io_rStream)) + { + delete bmp; + return false; + } + GFXTexHandle handle = GFXTexHandle(bmp, &GFXFontTextureProfile, true, avar("%s() - Read Font Sheet for %s %d (line %d)", __FUNCTION__, mFaceName.c_str(), mSize, __LINE__)); + //handle.setFilterNearest(); + mTextureSheets.push_back(handle); + } + + // Read last position info + io_rStream.read(&mCurX); + io_rStream.read(&mCurY); + io_rStream.read(&mCurSheet); + + // Read the remap table. + U32 minGlyph, maxGlyph; + io_rStream.read(&minGlyph); + io_rStream.read(&maxGlyph); + + if(maxGlyph >= minGlyph) + { + // Length of buffer.. + U32 buffLen; + io_rStream.read(&buffLen); + + // Read the buffer. + FrameTemp inBuff(buffLen); + io_rStream.read(buffLen, inBuff); + + // Decompress. + uLongf destLen = (maxGlyph-minGlyph+1)*sizeof(S32); + uncompress((Bytef*)&mRemapTable[minGlyph], &destLen, (Bytef*)(S32*)inBuff, buffLen); + + AssertISV(destLen == (maxGlyph-minGlyph+1)*sizeof(S32), "GFont::read - invalid remap table data!"); + + // Make sure we've got the right endianness. + for(i = minGlyph; i <= maxGlyph; i++) + mRemapTable[i] = convertBEndianToHost(mRemapTable[i]); + } + + return (io_rStream.getStatus() == Stream::Ok); +} + +bool GFont::write(Stream& stream) +{ + // Handle versioning + stream.write(csm_fileVersion); + + // Write font info + stream.write(mFaceName); + stream.write(mSize); + stream.write(mCharSet); + + stream.write(mHeight); + stream.write(mBaseline); + stream.write(mAscent); + stream.write(mDescent); + + // Write char info list + stream.write(U32(mCharInfoList.size())); + U32 i; + for(i = 0; i < mCharInfoList.size(); i++) + { + const PlatformFont::CharInfo *ci = &mCharInfoList[i]; + stream.write(ci->bitmapIndex); + stream.write(ci->xOffset); + stream.write(ci->yOffset); + stream.write(ci->width); + stream.write(ci->height); + stream.write(ci->xOrigin); + stream.write(ci->yOrigin); + stream.write(ci->xIncrement); + } + + stream.write(mTextureSheets.size()); + for(i = 0; i < mTextureSheets.size(); i++) + mTextureSheets[i].getBitmap()->writeBitmap("png", stream); + + stream.write(mCurX); + stream.write(mCurY); + stream.write(mCurSheet); + + // Get the min/max we have values for, and only write that range out. + S32 minGlyph = S32_MAX, maxGlyph = 0; + + for(i = 0; i < 65536; i++) + { + if(mRemapTable[i] != -1) + { + if(i>maxGlyph) maxGlyph = i; + if(i= minGlyph) + { + // Put everything big endian, to be consistent. Do this inplace. + for(i = minGlyph; i <= maxGlyph; i++) + mRemapTable[i] = convertHostToBEndian(mRemapTable[i]); + + { + // Compress. + const U32 buffSize = 128 * 1024; + FrameTemp outBuff(buffSize); + uLongf destLen = buffSize * sizeof(S32); + compress2((Bytef*)(S32*)outBuff, &destLen, (Bytef*)(S32*)&mRemapTable[minGlyph], (maxGlyph-minGlyph+1)*sizeof(S32), 9); + + // Write out. + stream.write((U32)destLen); + stream.write(destLen, outBuff); + } + + // Put us back to normal. + for(i = minGlyph; i <= maxGlyph; i++) + mRemapTable[i] = convertBEndianToHost(mRemapTable[i]); + } + + return (stream.getStatus() == Stream::Ok); +} + +void GFont::exportStrip(const char *fileName, U32 padding, U32 kerning) +{ + // Figure dimensions of our strip by iterating over all the char infos. + U32 totalHeight = 0; + U32 totalWidth = 0; + + S32 heightMin=0, heightMax=0; + + for(S32 i=0; igetFormat()); + + dMemset(gb.getWritableBits(), 0, sizeof(U8) * totalHeight * totalWidth ); + + // Ok, copy some rects, taking into account padding, kerning, offset. + U32 curWidth = kerning + padding; + + for(S32 i=0; i strip = GBitmap::load(fileName); + + if(!strip) + { + Con::errorf("GFont::importStrip - could not load file '%s'!", fileName); + return; + } + + // And get parsing and copying - load up all the characters as separate + // GBitmaps, sort, then pack. Not terribly efficient but this is basically + // on offline task anyway. + + // Ok, snag some glyphs. + Vector glyphList; + glyphList.reserve(mCharInfoList.size()); + + U32 curWidth = 0; + for(S32 i=0; igetFormat()); + glyphList.last().charId = i; + + // Copy the rect. + RectI ri(curWidth, getBaseline() - mCharInfoList[i].yOrigin, glyphList.last().bitmap->getWidth(), glyphList.last().bitmap->getHeight()); + Point2I outRi(0,0); + glyphList.last().bitmap->copyRect(strip, ri, outRi); + + // Update glyph attributes. + mCharInfoList[i].width = glyphList.last().bitmap->getWidth(); + mCharInfoList[i].height = glyphList.last().bitmap->getHeight(); + mCharInfoList[i].xOffset -= kerning + padding; + mCharInfoList[i].xIncrement += kerning; + mCharInfoList[i].yOffset -= padding; + + // Advance. + curWidth += ri.extent.x; + } + + // Ok, we have a big list of glyphmaps now. So let's sort them, then pack them. + dQsort(glyphList.address(), glyphList.size(), sizeof(GlyphMap), GlyphMapCompare); + + // They're sorted by height, so now we can do some sort of awesome packing. + Point2I curSheetSize(256, 256); + Vector sheetSizes; + + S32 curY = 0; + S32 curX = 0; + S32 curLnHeight = 0; + S32 maxHeight = 0; + for(U32 i = 0; i < glyphList.size(); i++) + { + PlatformFont::CharInfo *ci = &mCharInfoList[glyphList[i].charId]; + + if(ci->height > maxHeight) + maxHeight = ci->height; + + if(curX + ci->width > curSheetSize.x) + { + curY += curLnHeight; + curX = 0; + curLnHeight = 0; + } + + if(curY + ci->height > curSheetSize.y) + { + sheetSizes.push_back(curSheetSize.y); + curX = 0; + curY = 0; + curLnHeight = 0; + } + + if(ci->height > curLnHeight) + curLnHeight = ci->height; + + ci->bitmapIndex = sheetSizes.size(); + ci->xOffset = curX; + ci->yOffset = curY; + curX += ci->width; + } + + // Terminate the packing loop calculations. + curY += curLnHeight; + + if(curY < 64) + curSheetSize.y = 64; + else if(curY < 128) + curSheetSize.y = 128; + + sheetSizes.push_back(curSheetSize.y); + + if(getHeight() + padding * 2 > maxHeight) + maxHeight = getHeight() + padding * 2; + + // Allocate texture pages. + for(S32 i=0; igetFormat()); + + // Set everything to transparent. + U8 *bits = bitmap->getWritableBits(); + dMemset(bits, 0, sizeof(U8) *TextureSheetSize*TextureSheetSize * strip->getBytesPerPixel()); + + GFXTexHandle handle = GFXTexHandle( bitmap, &GFXFontTextureProfile, true, avar("%s() - Font Sheet for %s (line %d)", __FUNCTION__, fileName, __LINE__) ); + mTextureSheets.increment(); + mTextureSheets.last() = handle; + } + + // Alright, we're ready to copy bits! + for(S32 i=0; ibitmapIndex; + mTextureSheets[bi]->getBitmap()->copyRect(glyphList[i].bitmap, RectI(0,0, glyphList[i].bitmap->getWidth(),glyphList[i].bitmap->getHeight()), Point2I(ci->xOffset, ci->yOffset)); + } + + // Ok, all done! Just refresh some textures and we're set. + for(S32 i=0; i f = GFont::create(faceName, fontSize, Con::getVariable("$GUI::fontCacheDirectory")); + + if(f == NULL) + { + Con::errorf("populateFontCacheString - could not load font '%s %d'", faceName, fontSize); + return; + } + + if(!f->hasPlatformFont()) + { + Con::errorf("populateFontCacheString - font '%s %d' has no platform font. Cannot generate more characters.", faceName, fontSize); + return; + } + + // This has the side effect of generating character info, including the bitmaps. + f->getStrWidthPrecise( string ); +} + +DefineEngineFunction( populateFontCacheRange, void, ( const char *faceName, S32 fontSize, U32 rangeStart, U32 rangeEnd ),, + "Populate the font cache for the specified font with Unicode code points in the specified range.\n" + "@param faceName The name of the font face.\n" + "@param fontSize The size of the font in pixels.\n" + "@param rangeStart The start Unicode point.\n" + "@param rangeEnd The end Unicode point.\n" + "@note We only support BMP-0, so code points range from 0 to 65535.\n" + "@ingroup Font\n" ) +{ + Resource f = GFont::create(faceName, fontSize, Con::getVariable("$GUI::fontCacheDirectory")); + + if(f == NULL) + { + Con::errorf("populateFontCacheRange - could not load font '%s %d'", faceName, fontSize); + return; + } + + if(rangeStart > rangeEnd) + { + Con::errorf("populateFontCacheRange - range start is after end"); + return; + } + + if(!f->hasPlatformFont()) + { + Con::errorf("populateFontCacheRange - font '%s %d' has no platform font Cannot generate more characters.", faceName, fontSize); + return; + } + + // This has the side effect of generating character info, including the bitmaps. + for(U32 i=rangeStart; iisValidChar(i)) + f->getCharWidth(i); + else + Con::warnf("populateFontCacheRange - skipping invalid char 0x%x", i); + } + + // All done! +} + +DefineEngineFunction( dumpFontCacheStatus, void, (),, + "Dumps to the console a full description of all cached fonts, along with " + "info on the codepoints each contains.\n" + "@ingroup Font\n" ) +{ + Resource theFont = ResourceManager::get().startResourceList( Resource::signature() ); + + Con::printf("--------------------------------------------------------------------------"); + Con::printf(" Font Cache Usage Report"); + + while( theFont != NULL ) + { + theFont->dumpInfo(); + + theFont = ResourceManager::get().nextResource(); + } +} + +DefineEngineFunction( writeFontCache, void, (),, + "Force all cached fonts to serialize themselves to the cache.\n" + "@ingroup Font\n" ) +{ + Resource theFont = ResourceManager::get().startResourceList( Resource::signature() ); + + Con::printf("--------------------------------------------------------------------------"); + Con::printf(" Writing font cache to disk"); + + while( theFont != NULL ) + { + const String fileName( theFont.getPath() ); + + FileStream stream; + stream.open(fileName, Torque::FS::File::Write); + + if(stream.getStatus() == Stream::Ok) + { + Con::printf(" o Writing '%s' to disk...", fileName.c_str()); + theFont->write(stream); + } + else + { + Con::errorf(" o Could not open '%s' for write", fileName.c_str()); + } + + theFont = ResourceManager::get().nextResource(); + } +} + +DefineEngineFunction( populateAllFontCacheString, void, ( const char *string ),, + "Populate the font cache for all fonts with characters from the specified string.\n" + "@ingroup Font\n" ) +{ + Resource theFont = ResourceManager::get().startResourceList( Resource::signature() ); + + Con::printf("Populating font cache with string '%s'", string); + + while( theFont != NULL ) + { + if(theFont->hasPlatformFont()) + { + // This has the side effect of generating character info, including the bitmaps. + theFont->getStrWidthPrecise( string ); + } + else + { + const String fileName( theFont.getPath() ); + Con::errorf("populateAllFontCacheString - font '%s' has no platform font. Cannot generate more characters.", fileName.c_str()); + } + + theFont = ResourceManager::get().nextResource(); + } +} + +DefineEngineFunction( populateAllFontCacheRange, void, ( U32 rangeStart, U32 rangeEnd ),, + "Populate the font cache for all fonts with Unicode code points in the specified range.\n" + "@param rangeStart The start Unicode point.\n" + "@param rangeEnd The end Unicode point.\n" + "@note We only support BMP-0, so code points range from 0 to 65535.\n" + "@ingroup Font\n" ) +{ + if(rangeStart > rangeEnd) + { + Con::errorf("populateAllFontCacheRange - range start is after end!"); + return; + } + + Resource theFont = ResourceManager::get().startResourceList( Resource::signature() ); + + Con::printf("Populating font cache with range 0x%x to 0x%x", rangeStart, rangeEnd); + + while( theFont != NULL ) + { + const String fileName( theFont.getPath() ); + + if(theFont->hasPlatformFont()) + { + // This has the side effect of generating character info, including the bitmaps. + Con::printf(" o Populating font '%s'", fileName.c_str()); + for(U32 i=rangeStart; iisValidChar(i)) + theFont->getCharWidth(i); + else + Con::warnf("populateAllFontCacheRange - skipping invalid char 0x%x", i); + } + } + else + { + Con::errorf("populateAllFontCacheRange - font '%s' has no platform font. Cannot generate more characters.", fileName.c_str()); + } + + theFont = ResourceManager::get().nextResource(); + } +} + +DefineEngineFunction( exportCachedFont, void, + ( const char *faceName, S32 fontSize, const char *fileName, S32 padding, S32 kerning ),, + "Export specified font to the specified filename as a PNG. The " + "image can then be processed in Photoshop or another tool and " + "reimported using importCachedFont. Characters in the font are " + "exported as one long strip.\n" + "@param faceName The name of the font face.\n" + "@param fontSize The size of the font in pixels.\n" + "@param fileName The file name and path for the output PNG.\n" + "@param padding The padding between characters.\n" + "@param kerning The kerning between characters.\n" + "@ingroup Font\n" ) +{ + // Tell the font to export itself. + Resource f = GFont::create(faceName, fontSize, Con::getVariable("$GUI::fontCacheDirectory")); + + if(f == NULL) + { + Con::errorf("exportCachedFont - could not load font '%s %d'", faceName, fontSize); + return; + } + + f->exportStrip(fileName, padding, kerning); +} + +DefineEngineFunction( importCachedFont, void, + ( const char *faceName, S32 fontSize, const char *fileName, S32 padding, S32 kerning ),, + "Import an image strip from exportCachedFont. Call with the " + "same parameters you called exportCachedFont.\n" + "@param faceName The name of the font face.\n" + "@param fontSize The size of the font in pixels.\n" + "@param fileName The file name and path for the input PNG.\n" + "@param padding The padding between characters.\n" + "@param kerning The kerning between characters.\n" + "@ingroup Font\n" ) +{ + // Tell the font to import itself. + Resource f = GFont::create(faceName, fontSize, Con::getVariable("$GUI::fontCacheDirectory")); + + if(f == NULL) + { + Con::errorf("importCachedFont - could not load font '%s %d'", faceName, fontSize); + return; + } + + f->importStrip(fileName, padding, kerning); +} + +DefineEngineFunction( duplicateCachedFont, void, + ( const char *oldFontName, S32 oldFontSize, const char *newFontName ),, + "Copy the specified old font to a new name. The new copy will not have a " + "platform font backing it, and so will never have characters added to it. " + "But this is useful for making copies of fonts to add postprocessing effects " + "to via exportCachedFont.\n" + "@param oldFontName The name of the font face to copy.\n" + "@param oldFontSize The size of the font to copy.\n" + "@param newFontName The name of the new font face.\n" + "@ingroup Font\n" ) +{ + String newFontFile = GFont::getFontCacheFilename(newFontName, oldFontSize); + + // Load the original font. + Resource font = GFont::create(oldFontName, oldFontSize, Con::getVariable("$GUI::fontCacheDirectory")); + + // Deal with inexplicably missing or failed to load fonts. + if (font == NULL) + { + Con::errorf(" o Couldn't find font : %s", oldFontName); + return; + } + + // Ok, dump info! + FileStream stream; + stream.open( newFontFile, Torque::FS::File::Write ); + if(stream.getStatus() == Stream::Ok) + { + Con::printf( " o Writing duplicate font '%s' to disk...", newFontFile.c_str() ); + font->write(stream); + stream.close(); + } + else + { + Con::errorf( " o Could not open '%s' for write", newFontFile.c_str() ); + } +} + diff --git a/Engine/source/gfx/gFont.h b/Engine/source/gfx/gFont.h new file mode 100644 index 000000000..b67d7e72f --- /dev/null +++ b/Engine/source/gfx/gFont.h @@ -0,0 +1,194 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFONT_H_ +#define _GFONT_H_ + +//Includes +#ifndef _RESOURCE_H_ +#include "core/resource.h" +#endif +#ifndef _PLATFORMFONT_H_ +#include "platform/platformFont.h" +#endif +#ifndef _GBITMAP_H_ +#include "gfx/bitmap/gBitmap.h" +#endif +#ifndef _GFXDEVICE_H_ +#include "gfx/gfxDevice.h" +#endif +#ifndef _GFXTEXTUREHANDLE_H_ +#include "gfx/gfxTextureHandle.h" +#endif + + +GFX_DeclareTextureProfile(GFXFontTextureProfile); + +class GFont +{ +public: + enum Constants + { + TabWidthInSpaces = 3, + TextureSheetSize = 256, + }; + +public: + GFont(); + virtual ~GFont(); + + static Resource create(const String &faceName, U32 size, const char *cacheDirectory = 0, U32 charset = TGE_ANSI_CHARSET); + + GFXTexHandle getTextureHandle(S32 index) const { return mTextureSheets[index]; } + + const PlatformFont::CharInfo& getCharInfo(const UTF16 in_charIndex); + static const PlatformFont::CharInfo& getDefaultCharInfo(); + + U32 getCharHeight(const UTF16 in_charIndex); + U32 getCharWidth(const UTF16 in_charIndex); + U32 getCharXIncrement(const UTF16 in_charIndex); + + bool isValidChar(const UTF16 in_charIndex) const; + + const U32 getHeight() const { return mHeight; } + const U32 getBaseline() const { return mBaseline; } + const U32 getAscent() const { return mAscent; } + const U32 getDescent() const { return mDescent; } + + U32 getBreakPos(const UTF16 *string, U32 strlen, U32 width, bool breakOnWhitespace); + + /// These are the preferred width functions. + U32 getStrNWidth(const UTF16*, U32 n); + U32 getStrNWidthPrecise(const UTF16*, U32 n); + + /// These UTF8 versions of the width functions will be deprecated, please avoid them. + U32 getStrWidth(const UTF8*); // Note: ignores c/r + U32 getStrNWidth(const UTF8*, U32 n); + U32 getStrWidthPrecise(const UTF8*); // Note: ignores c/r + U32 getStrNWidthPrecise(const UTF8*, U32 n); + void wrapString(const UTF8 *string, U32 width, Vector &startLineOffset, Vector &lineLen); + + /// Dump information about this font to the console. + void dumpInfo() const; + + /// Export to an image strip for image processing. + void exportStrip(const char *fileName, U32 padding, U32 kerning); + + /// Import an image strip generated with exportStrip, make sure parameters match! + void importStrip(const char *fileName, U32 padding, U32 kerning); + + void setPlatformFont(PlatformFont *inPlatformFont); + + /// Query as to presence of platform font. If absent, we cannot generate more + /// chars! + const bool hasPlatformFont() const + { + return mPlatformFont != NULL; + } + + /// Query to determine if we should use add or modulate (as A8 textures + /// are treated as having 0 for RGB). + bool isAlphaOnly() const + { + return mTextureSheets[0]->getBitmap()->getFormat() == GFXFormatA8; + } + + /// Get the filename for a cached font. + static String getFontCacheFilename(const String &faceName, U32 faceSize); + + /// Get the face name of the font. + String getFontFaceName() const { return mFaceName; }; + U32 getFontSize() const { return mSize; } + U32 getFontCharSet() const { return mCharSet; } + + bool read(Stream& io_rStream); + bool write(Stream& io_rStream); + + static GFont* load( const Torque::Path& path ); + +protected: + bool loadCharInfo(const UTF16 ch); + void addBitmap(PlatformFont::CharInfo &charInfo); + void addSheet(void); + void assignSheet(S32 sheetNum, GBitmap *bmp); + + void *mMutex; + +private: + static const U32 csm_fileVersion; + + PlatformFont *mPlatformFont; + VectormTextureSheets; + + S32 mCurX; + S32 mCurY; + S32 mCurSheet; + + bool mNeedSave; + Torque::Path mGFTFile; + String mFaceName; + U32 mSize; + U32 mCharSet; + + U32 mHeight; + U32 mBaseline; + U32 mAscent; + U32 mDescent; + + /// List of character info structures, must be accessed through the + /// getCharInfo(U32) function to account for remapping. + Vector mCharInfoList; + + /// Index remapping + S32 mRemapTable[65536]; +}; + +inline U32 GFont::getCharXIncrement(const UTF16 in_charIndex) +{ + const PlatformFont::CharInfo& rChar = getCharInfo(in_charIndex); + return rChar.xIncrement; +} + +inline U32 GFont::getCharWidth(const UTF16 in_charIndex) +{ + const PlatformFont::CharInfo& rChar = getCharInfo(in_charIndex); + return rChar.width; +} + +inline U32 GFont::getCharHeight(const UTF16 in_charIndex) +{ + const PlatformFont::CharInfo& rChar = getCharInfo(in_charIndex); + return rChar.height; +} + +inline bool GFont::isValidChar(const UTF16 in_charIndex) const +{ + if(mRemapTable[in_charIndex] != -1) + return true; + + if(mPlatformFont) + return mPlatformFont->isValidChar(in_charIndex); + + return false; +} + +#endif //_GFONT_H_ diff --git a/Engine/source/gfx/genericConstBuffer.cpp b/Engine/source/gfx/genericConstBuffer.cpp new file mode 100644 index 000000000..011881ece --- /dev/null +++ b/Engine/source/gfx/genericConstBuffer.cpp @@ -0,0 +1,284 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/genericConstBuffer.h" + +#include "platform/profiler.h" +#include "core/stream/stream.h" + + +GenericConstBufferLayout::GenericConstBufferLayout() +{ + VECTOR_SET_ASSOCIATION( mParams ); + + mBufferSize = 0; + mCurrentIndex = 0; + mTimesCleared = 0; +} + +void GenericConstBufferLayout::addParameter(const String& name, const GFXShaderConstType constType, const U32 offset, const U32 size, const U32 arraySize, const U32 alignValue) +{ +#ifdef TORQUE_DEBUG + // Make sure we don't have overlapping parameters + S32 start = offset; + S32 end = offset + size; + for (Params::iterator i = mParams.begin(); i != mParams.end(); i++) + { + const ParamDesc& dp = *i; + S32 pstart = dp.offset; + S32 pend = pstart + dp.size; + pstart -= start; + pend -= end; + // This is like a minkowski sum for two line segments, if the newly formed line contains + // the origin, then they intersect + bool intersect = ((pstart >= 0 && 0 >= pend) || ((pend >= 0 && 0 >= pstart))); + AssertFatal(!intersect, "Overlapping shader parameter!"); + } +#endif + ParamDesc desc; + desc.name = name; + desc.constType = constType; + desc.offset = offset; + desc.size = size; + desc.arraySize = arraySize; + desc.alignValue = alignValue; + desc.index = mCurrentIndex++; + mParams.push_back(desc); + mBufferSize = getMax(desc.offset + desc.size, mBufferSize); + AssertFatal(mBufferSize, "Empty constant buffer!"); +} + +bool GenericConstBufferLayout::set(const ParamDesc& pd, const GFXShaderConstType constType, const U32 size, const void* data, U8* basePointer) +{ + PROFILE_SCOPE(GenericConstBufferLayout_set); + + // Shader compilers like to optimize float4x4 uniforms into float3x3s. + // So long as the real paramater is a matrix of-some-type and the data + // passed in is a MatrixF ( which is will be ), we DO NOT have a + // mismatched const type. + AssertFatal( pd.constType == constType || + ( + ( pd.constType == GFXSCT_Float2x2 || + pd.constType == GFXSCT_Float3x3 || + pd.constType == GFXSCT_Float4x4 ) && + ( constType == GFXSCT_Float2x2 || + constType == GFXSCT_Float3x3 || + constType == GFXSCT_Float4x4 ) + ), "Mismatched const type!" ); + + // This "cute" bit of code allows us to support 2x3 and 3x3 matrices in shader constants but use our MatrixF class. Yes, a hack. -BTR + switch (pd.constType) + { + case GFXSCT_Float2x2 : + case GFXSCT_Float3x3 : + case GFXSCT_Float4x4 : + return setMatrix(pd, constType, size, data, basePointer); + break; + default : + break; + } + + AssertFatal(pd.size >= size, "Not enough room in the buffer for this data!"); + + // Ok, we only set data if it's different than the data we already have, this maybe more expensive than just setting the data, but + // we'll have to do some timings to see. For example, the lighting shader constants rarely change, but we can't assume that at the + // renderInstMgr level, but we can check down here. -BTR + if (dMemcmp(basePointer+pd.offset, data, size) != 0) + { + dMemcpy(basePointer+pd.offset, data, size); + return true; + } + return false; +} + +bool GenericConstBufferLayout::setMatrix(const ParamDesc& pd, const GFXShaderConstType constType, const U32 size, const void* data, U8* basePointer) +{ + PROFILE_SCOPE(GenericConstBufferLayout_setMatrix); + + // We're generic, so just copy the full MatrixF in + AssertFatal(pd.size >= size, "Not enough room in the buffer for this data!"); + + // Matrices are an annoying case because of the alignment issues. There are alignment issues in the matrix itself, and then potential inter matrices alignment issues. + // So GL and DX will need to derive their own GenericConstBufferLayout classes and override this method to deal with that stuff. For GenericConstBuffer, copy the whole + // 4x4 matrix regardless of the target case. + + if (dMemcmp(basePointer+pd.offset, data, size) != 0) + { + dMemcpy(basePointer+pd.offset, data, size); + return true; + } + + return false; +} + +bool GenericConstBufferLayout::getDesc(const String& name, ParamDesc& param) const +{ + for (U32 i = 0; i < mParams.size(); i++) + { + if (mParams[i].name.equal(name)) + { + param = mParams[i]; + return true; + } + } + return false; +} + +bool GenericConstBufferLayout::getDesc(const U32 index, ParamDesc& param) const +{ + if ( index < mParams.size() ) + { + param = mParams[index]; + return true; + } + + return false; +} + +bool GenericConstBufferLayout::write(Stream* s) +{ + // Write out the size of the ParamDesc structure as a sanity check. + if (!s->write((U32) sizeof(ParamDesc))) + return false; + // Next, write out the number of elements we've got. + if (!s->write(mParams.size())) + return false; + for (U32 i = 0; i < mParams.size(); i++) + { + s->write(mParams[i].name); + + if (!s->write(mParams[i].offset)) + return false; + if (!s->write(mParams[i].size)) + return false; + U32 t = (U32) mParams[i].constType; + if (!s->write(t)) + return false; + if (!s->write(mParams[i].arraySize)) + return false; + if (!s->write(mParams[i].alignValue)) + return false; + if (!s->write(mParams[i].index)) + return false; + } + return true; +} + +/// Load this layout from a stream +bool GenericConstBufferLayout::read(Stream* s) +{ + U32 structSize; + if (!s->read(&structSize)) + return false; + if (structSize != sizeof(ParamDesc)) + { + AssertFatal(false, "Invalid shader layout structure size!"); + return false; + } + U32 numParams; + if (!s->read(&numParams)) + return false; + mParams.setSize(numParams); + mBufferSize = 0; + mCurrentIndex = 0; + for (U32 i = 0; i < mParams.size(); i++) + { + s->read(&mParams[i].name); + if (!s->read(&mParams[i].offset)) + return false; + if (!s->read(&mParams[i].size)) + return false; + U32 t; + if (!s->read(&t)) + return false; + mParams[i].constType = (GFXShaderConstType) t; + if (!s->read(&mParams[i].arraySize)) + return false; + if (!s->read(&mParams[i].alignValue)) + return false; + if (!s->read(&mParams[i].index)) + return false; + mBufferSize = getMax(mParams[i].offset + mParams[i].size, mBufferSize); + mCurrentIndex = getMax(mParams[i].index, mCurrentIndex); + } + mCurrentIndex++; + return true; +} + +void GenericConstBufferLayout::clear() +{ + mParams.clear(); + mBufferSize = 0; + mCurrentIndex = 0; + mTimesCleared++; +} + + +GenericConstBuffer::GenericConstBuffer(GenericConstBufferLayout* layout) + : mBuffer( NULL ), + mLayout( layout ), + mDirtyStart( U32_MAX ), + mDirtyEnd( 0 ) +{ + if ( layout && layout->getBufferSize() > 0 ) + { + mBuffer = new U8[mLayout->getBufferSize()]; + + // Always set a default value, that way our isEqual checks + // will work in release as well. + dMemset( mBuffer, 0xFFFF, mLayout->getBufferSize() ); + + #ifdef TORQUE_DEBUG + + // Clear the debug assignment tracking. + mWasAssigned.setSize( layout->getParameterCount() ); + dMemset( mWasAssigned.address(), 0, mWasAssigned.memSize() ); + + #endif + } +} + +GenericConstBuffer::~GenericConstBuffer() +{ + delete [] mBuffer; +} + +#ifdef TORQUE_DEBUG + +void GenericConstBuffer::assertUnassignedConstants( const char *shaderName ) +{ + for ( U32 i=0; i < mWasAssigned.size(); i++ ) + { + if ( mWasAssigned[i] ) + continue; + + GenericConstBufferLayout::ParamDesc pd; + mLayout->getDesc( i, pd ); + + // Assert on the unassigned constant. + AssertFatal( false, avar( "The '%s' shader constant in shader '%s' was unassigned!", + pd.name.c_str(), shaderName ) ); + } +} + +#endif diff --git a/Engine/source/gfx/genericConstBuffer.h b/Engine/source/gfx/genericConstBuffer.h new file mode 100644 index 000000000..b8a012159 --- /dev/null +++ b/Engine/source/gfx/genericConstBuffer.h @@ -0,0 +1,360 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GENERICCONSTBUFFER_H_ +#define _GENERICCONSTBUFFER_H_ + +#ifndef _TORQUE_STRING_H_ +#include "core/util/str.h" +#endif +#ifndef _TDICTIONARY_H_ +#include "core/util/tDictionary.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _ALIGNEDARRAY_H_ +#include "core/util/tAlignedArray.h" +#endif +#ifndef _COLOR_H_ +#include "core/color.h" +#endif +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif +#ifndef _MPOINT2_H_ +#include "math/mPoint2.h" +#endif +#ifndef _GFXENUMS_H_ +#include "gfx/gfxEnums.h" +#endif + +class Stream; + + +/// This class defines the memory layout for a GenericConstBuffer. +class GenericConstBufferLayout +{ +public: + /// Describes the parameters we contain + struct ParamDesc + { + ParamDesc() + : name(), + offset( 0 ), + size( 0 ), + constType( GFXSCT_Float ), + arraySize( 0 ), + alignValue( 0 ), + index( 0 ) + { + } + + void clear() + { + name = String::EmptyString; + offset = 0; + size = 0; + constType = GFXSCT_Float; + arraySize = 0; + alignValue = 0; + index = 0; + } + + /// Parameter name + String name; + + /// Offset into the memory block + U32 offset; + + /// Size of the block + U32 size; + + /// Type of data + GFXShaderConstType constType; + + // For arrays, how many elements + U32 arraySize; + + // Array element alignment value + U32 alignValue; + + /// 0 based index of this param, in order of addParameter calls. + U32 index; + }; + + GenericConstBufferLayout(); + virtual ~GenericConstBufferLayout() {} + + /// Add a parameter to the buffer + void addParameter(const String& name, const GFXShaderConstType constType, const U32 offset, const U32 size, const U32 arraySize, const U32 alignValue); + + /// Get the size of the buffer + inline U32 getBufferSize() const { return mBufferSize; } + + /// Get the number of parameters + inline U32 getParameterCount() const { return mParams.size(); } + + /// Returns the ParamDesc of a parameter + bool getDesc(const String& name, ParamDesc& param) const; + + /// Returns the ParamDesc of a parameter + bool getDesc(const U32 index, ParamDesc& param) const; + + /// Set a parameter, given a base pointer + virtual bool set(const ParamDesc& pd, const GFXShaderConstType constType, const U32 size, const void* data, U8* basePointer); + + /// Save this layout to a stream + bool write(Stream* s); + + /// Load this layout from a stream + bool read(Stream* s); + + /// Restore to initial state. + void clear(); + +protected: + + /// Set a matrix, given a base pointer. + virtual bool setMatrix(const ParamDesc& pd, const GFXShaderConstType constType, const U32 size, const void* data, U8* basePointer); + + /// Vector of parameter descriptions. + typedef Vector Params; + + /// Vector of parameter descriptions. + Params mParams; + U32 mBufferSize; + U32 mCurrentIndex; + + // This if for debugging shader reloading and can be removed later. + U32 mTimesCleared; +}; + + +/// This class manages shader constant data in a system memory buffer. It is +/// used by device specific classes for batching together many constant changes +/// which are then copied to the device thru a single API call. +/// +/// @see GenericConstBufferLayout +/// +class GenericConstBuffer +{ +public: + GenericConstBuffer(GenericConstBufferLayout* layout); + ~GenericConstBuffer(); + + /// @name Set shader constant values + /// @{ + /// Actually set shader constant values + /// @param name Name of the constant, this should be a name contained in the array returned in getShaderConstDesc, + /// if an invalid name is used, its ignored, but it's not an error. + inline void set(const GenericConstBufferLayout::ParamDesc& pd, const F32 f) { internalSet(pd, GFXSCT_Float, sizeof(F32), &f); } + inline void set(const GenericConstBufferLayout::ParamDesc& pd, const Point2F& fv) { internalSet(pd, GFXSCT_Float2, sizeof(Point2F), &fv); } + inline void set(const GenericConstBufferLayout::ParamDesc& pd, const Point3F& fv) { internalSet(pd, GFXSCT_Float3, sizeof(Point3F), &fv); } + inline void set(const GenericConstBufferLayout::ParamDesc& pd, const Point4F& fv) { internalSet(pd, GFXSCT_Float4, sizeof(Point4F), &fv); } + inline void set(const GenericConstBufferLayout::ParamDesc& pd, const PlaneF& fv) { internalSet(pd, GFXSCT_Float4, sizeof(PlaneF), &fv); } + inline void set(const GenericConstBufferLayout::ParamDesc& pd, const ColorF& fv) { internalSet(pd, GFXSCT_Float4, sizeof(Point4F), &fv); } + inline void set(const GenericConstBufferLayout::ParamDesc& pd, const S32 f) { internalSet(pd, GFXSCT_Int, sizeof(S32), &f); } + inline void set(const GenericConstBufferLayout::ParamDesc& pd, const Point2I& fv) { internalSet(pd, GFXSCT_Int2, sizeof(Point2I), &fv); } + inline void set(const GenericConstBufferLayout::ParamDesc& pd, const Point3I& fv) { internalSet(pd, GFXSCT_Int3, sizeof(Point3I), &fv); } + inline void set(const GenericConstBufferLayout::ParamDesc& pd, const Point4I& fv) { internalSet(pd, GFXSCT_Int4, sizeof(Point4I), &fv); } + inline void set(const GenericConstBufferLayout::ParamDesc& pd, const AlignedArray& fv) { internalSet(pd, GFXSCT_Float, fv.getElementSize() * fv.size(), fv.getBuffer()); } + inline void set(const GenericConstBufferLayout::ParamDesc& pd, const AlignedArray& fv) { internalSet(pd, GFXSCT_Float2, fv.getElementSize() * fv.size(), fv.getBuffer()); } + inline void set(const GenericConstBufferLayout::ParamDesc& pd, const AlignedArray& fv) { internalSet(pd, GFXSCT_Float3, fv.getElementSize() * fv.size(), fv.getBuffer()); } + inline void set(const GenericConstBufferLayout::ParamDesc& pd, const AlignedArray& fv) { internalSet(pd, GFXSCT_Float4, fv.getElementSize() * fv.size(), fv.getBuffer()); } + inline void set(const GenericConstBufferLayout::ParamDesc& pd, const AlignedArray& fv) { internalSet(pd, GFXSCT_Int, fv.getElementSize() * fv.size(), fv.getBuffer()); } + inline void set(const GenericConstBufferLayout::ParamDesc& pd, const AlignedArray& fv) { internalSet(pd, GFXSCT_Int2, fv.getElementSize() * fv.size(), fv.getBuffer()); } + inline void set(const GenericConstBufferLayout::ParamDesc& pd, const AlignedArray& fv) { internalSet(pd, GFXSCT_Int3, fv.getElementSize() * fv.size(), fv.getBuffer()); } + inline void set(const GenericConstBufferLayout::ParamDesc& pd, const AlignedArray& fv) { internalSet(pd, GFXSCT_Int4, fv.getElementSize() * fv.size(), fv.getBuffer()); } + + inline void set( const GenericConstBufferLayout::ParamDesc& pd, const MatrixF& mat, const GFXShaderConstType matrixType ) + { + AssertFatal( matrixType == GFXSCT_Float2x2 || + matrixType == GFXSCT_Float3x3 || + matrixType == GFXSCT_Float4x4, + "GenericConstBuffer::set() - Invalid matrix type!" ); + + internalSet( pd, matrixType, sizeof(MatrixF), &mat ); + } + + inline void set( const GenericConstBufferLayout::ParamDesc& pd, const MatrixF* mat, const U32 arraySize, const GFXShaderConstType matrixType ) + { + AssertFatal( matrixType == GFXSCT_Float2x2 || + matrixType == GFXSCT_Float3x3 || + matrixType == GFXSCT_Float4x4, + "GenericConstBuffer::set() - Invalid matrix type!" ); + + internalSet( pd, matrixType, sizeof(MatrixF)*arraySize, mat ); + } + + /// Gets the dirty buffer range and clears the dirty + /// state at the same time. + inline const U8* getDirtyBuffer( U32 *start, U32 *size ); + + /// Sets the entire buffer as dirty or clears the dirty state. + inline void setDirty( bool dirty ); + + /// Returns true if the buffer has been modified since the + /// last call to getDirtyBuffer or setDirty. The buffer is + /// not dirty on initial creation. + /// + /// @see getDirtyBuffer + /// @see setDirty + inline bool isDirty() const { return mDirtyEnd != 0; } + + /// Returns true if have the same layout and hold the same + /// data as the input buffer. + inline bool isEqual( const GenericConstBuffer *buffer ) const; + + /// Returns our layout object. + inline GenericConstBufferLayout* getLayout() const { return mLayout; } + + #ifdef TORQUE_DEBUG + + /// Helper function used to assert on unset constants. + void assertUnassignedConstants( const char *shaderName ); + + #endif + +protected: + + /// Returns a pointer to the raw buffer + inline const U8* getBuffer() const { return mBuffer; } + + /// Called by the inlined set functions above to do the + /// real dirty work of copying the data to the right location + /// within the buffer. + inline void internalSet( const GenericConstBufferLayout::ParamDesc &pd, + const GFXShaderConstType constType, + const U32 size, + const void *data ); + + /// The buffer layout. + GenericConstBufferLayout *mLayout; + + /// The pointer to the contant store or + /// NULL if the layout is empty. + U8 *mBuffer; + + /// The byte offset to the start of the dirty + /// range within the buffer or U32_MAX if the + /// buffer is not dirty. + U32 mDirtyStart; + + /// The btye offset to the end of the dirty + /// range within the buffer or 0 if the buffer + /// is not dirty. + U32 mDirtyEnd; + + + #ifdef TORQUE_DEBUG + + /// A vector used to keep track if a constant + /// has beed assigned a value or not. + /// + /// @see assertUnassignedConstants + Vector mWasAssigned; + + #endif +}; + + +// NOTE: These inlines below are here to get the very best possible +// performance when setting the device shader constants and can be +// called 4000-8000 times per frame or more. +// +// You need a very good reason to consider changing them. + +inline void GenericConstBuffer::internalSet( const GenericConstBufferLayout::ParamDesc &pd, + const GFXShaderConstType constType, + const U32 size, + const void *data ) +{ + // NOTE: We should have never gotten here if the buffer + // was null as no valid shader constant could have been + // assigned. + // + // If this happens its a bug in another part of the code. + // + AssertFatal( mBuffer, "GenericConstBuffer::internalSet - The buffer is NULL!" ); + + if ( mLayout->set( pd, constType, size, data, mBuffer ) ) + { + #ifdef TORQUE_DEBUG + + // Update the debug assignment tracking. + mWasAssigned[ pd.index ] = true; + + #endif + + // Keep track of the dirty range so it can be queried + // later in GenericConstBuffer::getDirtyBuffer. + mDirtyStart = getMin( pd.offset, mDirtyStart ); + mDirtyEnd = getMax( pd.offset + pd.size, mDirtyEnd ); + } +} + +inline void GenericConstBuffer::setDirty( bool dirty ) +{ + if ( !mBuffer ) + return; + + if ( dirty ) + { + mDirtyStart = 0; + mDirtyEnd = mLayout->getBufferSize(); + } + else if ( !dirty ) + { + mDirtyStart = U32_MAX; + mDirtyEnd = 0; + } +} + +inline const U8* GenericConstBuffer::getDirtyBuffer( U32 *start, U32 *size ) +{ + AssertFatal( isDirty(), "GenericConstBuffer::getDirtyBuffer() - Buffer is not dirty!" ); + AssertFatal( mDirtyEnd > mDirtyStart, "GenericConstBuffer::getDirtyBuffer() - Dirty range is invalid!" ); + AssertFatal( mBuffer, "GenericConstBuffer::getDirtyBuffer() - Buffer is empty!" ); + + // Use the area we calculated during internalSet. + *size = mDirtyEnd - mDirtyStart; + *start = mDirtyStart; + const U8 *buffer = mBuffer + mDirtyStart; + + // Clear the dirty state while we're here. + mDirtyStart = U32_MAX; + mDirtyEnd = 0; + + return buffer; +} + +inline bool GenericConstBuffer::isEqual( const GenericConstBuffer *buffer ) const +{ + U32 bsize = mLayout->getBufferSize(); + if ( bsize != buffer->mLayout->getBufferSize() ) + return false; + + return dMemcmp( mBuffer, buffer->getBuffer(), bsize ) == 0; +} + +#endif // _GENERICCONSTBUFFER_H_ diff --git a/Engine/source/gfx/gfxAPI.cpp b/Engine/source/gfx/gfxAPI.cpp new file mode 100644 index 000000000..91a9974df --- /dev/null +++ b/Engine/source/gfx/gfxAPI.cpp @@ -0,0 +1,247 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/gfxAPI.h" + + +IMPLEMENT_SCOPE( GFXAPI, GFX,, + "Graphics subystem." ); + +IMPLEMENT_STRUCT( GFXVideoMode, + GFXVideoMode, GFXAPI, + "Descriptor for a specific video mode." ) + FIELD( resolution, resolution, 1, "Width and height of the mode's resolution in pixels." ) + FIELD( bitDepth, bitDepth, 1, "Bits per pixel." ) + FIELD( refreshRate, refreshRate, 1, "Frequency at which the screen is refreshed (in Hertz)." ) + FIELD( fullScreen, fullScreen, 1, "Whether this is a fullscreen or windowed mode." ) + FIELD( wideScreen, wideScreen, 1, "Whether this is a widescreen display mode." ) + FIELD( antialiasLevel, antialiasLevel, 1, "Maximum or desired antialiasing level." ) +END_IMPLEMENT_STRUCT; + +ImplementEnumType( GFXAdapterType, + "Back-end graphics API used by the GFX subsystem.\n\n" + "@ingroup GFX" ) + { OpenGL, "OpenGL", "OpenGL." }, + { Direct3D8, "D3D8", "Direct3D 8." }, + { Direct3D9, "D3D9", "Direct3D 9." }, + { NullDevice, "NullDevice", "Null device for dedicated servers." }, + { Direct3D9_360, "Xenon", "Direct3D 9 on Xbox 360." } +EndImplementEnumType; + +ImplementEnumType( GFXBlend, + "The supported blend modes.\n" + "@ingroup GFX" ) + + { GFXBlendZero, "GFXBlendZero", "(0, 0, 0, 0)" }, + { GFXBlendOne, "GFXBlendOne", "(1, 1, 1, 1)" }, + { GFXBlendSrcColor, "GFXBlendSrcColor", "(Rs, Gs, Bs, As)" }, + { GFXBlendInvSrcColor, "GFXBlendInvSrcColor", "(1 - Rs, 1 - Gs, 1 - Bs, 1 - As)" }, + { GFXBlendSrcAlpha, "GFXBlendSrcAlpha", "(As, As, As, As)" }, + { GFXBlendInvSrcAlpha, "GFXBlendInvSrcAlpha", "( 1 - As, 1 - As, 1 - As, 1 - As)" }, + { GFXBlendDestAlpha, "GFXBlendDestAlpha", "(Ad Ad Ad Ad)" }, + { GFXBlendInvDestAlpha, "GFXBlendInvDestAlpha", "(1 - Ad 1 - Ad 1 - Ad 1 - Ad)" }, + { GFXBlendDestColor, "GFXBlendDestColor", "(Rd, Gd, Bd, Ad)" }, + { GFXBlendInvDestColor, "GFXBlendInvDestColor", "(1 - Rd, 1 - Gd, 1 - Bd, 1 - Ad)" }, + { GFXBlendSrcAlphaSat, "GFXBlendSrcAlphaSat", "(f, f, f, 1) where f = min(As, 1 - Ad)" } + +EndImplementEnumType; + +ImplementEnumType( GFXCmpFunc, + "The supported comparison functions.\n" + "@ingroup GFX" ) + + { GFXCmpNever, "GFXCmpNever" }, + { GFXCmpLess, "GFXCmpLess" }, + { GFXCmpEqual, "GFXCmpEqual" }, + { GFXCmpLessEqual, "GFXCmpLessEqual" }, + { GFXCmpGreater, "GFXCmpGreater" }, + { GFXCmpNotEqual, "GFXCmpNotEqual" }, + { GFXCmpGreaterEqual, "GFXCmpGreaterEqual" }, + { GFXCmpAlways, "GFXCmpAlways" }, + +EndImplementEnumType; + +ImplementEnumType( GFXTextureAddressMode, + "The texture address modes.\n" + "@ingroup GFX" ) + + { GFXAddressWrap, "GFXAddressWrap" }, + { GFXAddressMirror, "GFXAddressMirror" }, + { GFXAddressClamp, "GFXAddressClamp" }, + { GFXAddressBorder, "GFXAddressBorder" }, + { GFXAddressMirrorOnce, "GFXAddressMirrorOnce" } + +EndImplementEnumType; + +ImplementEnumType( GFXTextureFilterType, + "The texture filter types.\n" + "@ingroup GFX" ) + + { GFXTextureFilterNone, "GFXTextureFilterNone" }, + { GFXTextureFilterPoint, "GFXTextureFilterPoint" }, + { GFXTextureFilterLinear, "GFXTextureFilterLinear" }, + { GFXTextureFilterAnisotropic, "GFXTextureFilterAnisotropic" }, + { GFXTextureFilterPyramidalQuad, "GFXTextureFilterPyramidalQuad" }, + { GFXTextureFilterGaussianQuad, "GFXTextureFilterGaussianQuad" } + +EndImplementEnumType; + +ImplementEnumType( GFXTextureOp, + "The texture operators.\n" + "@ingroup GFX" ) + + { GFXTOPDisable, "GFXTOPDisable" }, + { GFXTOPSelectARG1, "GFXTOPSelectARG1" }, + { GFXTOPSelectARG2, "GFXTOPSelectARG2" }, + { GFXTOPModulate, "GFXTOPModulate" }, + { GFXTOPModulate2X, "GFXTOPModulate2X" }, + { GFXTOPModulate4X, "GFXTOPModulate4X" }, + { GFXTOPAdd, "GFXTOPAdd" }, + { GFXTOPAddSigned, "GFXTOPAddSigned" }, + { GFXTOPAddSigned2X, "GFXTOPAddSigned2X" }, + { GFXTOPSubtract, "GFXTOPSubtract" }, + { GFXTOPAddSmooth, "GFXTOPAddSmooth" }, + { GFXTOPBlendDiffuseAlpha, "GFXTOPBlendDiffuseAlpha" }, + { GFXTOPBlendTextureAlpha, "GFXTOPBlendTextureAlpha" }, + { GFXTOPBlendFactorAlpha, "GFXTOPBlendFactorAlpha" }, + { GFXTOPBlendTextureAlphaPM, "GFXTOPBlendTextureAlphaPM" }, + { GFXTOPBlendCURRENTALPHA, "GFXTOPBlendCURRENTALPHA" }, + { GFXTOPPreModulate, "GFXTOPPreModulate" }, + { GFXTOPModulateAlphaAddColor, "GFXTOPModulateAlphaAddColor" }, + { GFXTOPModulateColorAddAlpha, "GFXTOPModulateColorAddAlpha" }, + { GFXTOPModulateInvAlphaAddColor, "GFXTOPModulateInvAlphaAddColor" }, + { GFXTOPModulateInvColorAddAlpha, "GFXTOPModulateInvColorAddAlpha" }, + { GFXTOPBumpEnvMap, "GFXTOPBumpEnvMap" }, + { GFXTOPBumpEnvMapLuminance, "GFXTOPBumpEnvMapLuminance" }, + { GFXTOPDotProduct3, "GFXTOPDotProduct3" }, + { GFXTOPLERP, "GFXTOPLERP" } + +EndImplementEnumType; + +ImplementEnumType( GFXTextureArgument, + "The texture arguments.\n" + "@ingroup GFX" ) + + { GFXTADiffuse, "GFXTADiffuse" }, + { GFXTACurrent, "GFXTACurrent" }, + { GFXTATexture, "GFXTATexture" }, + { GFXTATFactor, "GFXTATFactor" }, + { GFXTASpecular, "GFXTASpecular" }, + { GFXTATemp, "GFXTATemp" }, + { GFXTAConstant, "GFXTAConstant" }, + + { GFXTAComplement, "OneMinus" }, + { GFXTAAlphaReplicate, "AlphaReplicate" } + +EndImplementEnumType; + +ImplementEnumType( GFXTextureTransformFlags, + "The texture transform state flags.\n" + "@ingroup GFX" ) + + { GFXTTFFDisable, "GFXTTFDisable" }, + { GFXTTFFCoord1D, "GFXTTFFCoord1D" }, + { GFXTTFFCoord2D, "GFXTTFFCoord2D" }, + { GFXTTFFCoord3D, "GFXTTFFCoord3D" }, + { GFXTTFFCoord4D, "GFXTTFFCoord4D" }, + { GFXTTFFProjected, "GFXTTFProjected" } + +EndImplementEnumType; + + +ImplementEnumType( GFXFormat, + "The texture formats.\n" + "@note Not all formats are supported on all platforms.\n" + "@ingroup GFX" ) + + { GFXFormatR8G8B8, "GFXFormatR8G8B8" }, + { GFXFormatR8G8B8A8, "GFXFormatR8G8B8A8" }, + { GFXFormatR8G8B8X8, "GFXFormatR8G8B8X8" }, + { GFXFormatR32F, "GFXFormatR32F" }, + { GFXFormatR5G6B5, "GFXFormatR5G6B5" }, + { GFXFormatR5G5B5A1, "GFXFormatR5G5B5A1" }, + { GFXFormatR5G5B5X1, "GFXFormatR5G5B5X1" }, + { GFXFormatA4L4, "GFXFormatA4L4" }, + { GFXFormatA8L8, "GFXFormatA8L8" }, + { GFXFormatA8, "GFXFormatA8" }, + { GFXFormatL8, "GFXFormatL8" }, + { GFXFormatDXT1, "GFXFormatDXT1" }, + { GFXFormatDXT2, "GFXFormatDXT2" }, + { GFXFormatDXT3, "GFXFormatDXT3" }, + { GFXFormatDXT4, "GFXFormatDXT4" }, + { GFXFormatDXT5, "GFXFormatDXT5" }, + { GFXFormatD32, "GFXFormatD32" }, + { GFXFormatD24X8, "GFXFormatD24X8" }, + { GFXFormatD24S8, "GFXFormatD24S8" }, + { GFXFormatD24FS8, "GFXFormatD24FS8" }, + { GFXFormatD16, "GFXFormatD16" }, + + { GFXFormatR32G32B32A32F, "GFXFormatR32G32B32A32F" }, + { GFXFormatR16G16B16A16F, "GFXFormatR16G16B16A16F" }, + { GFXFormatL16, "GFXFormatL16" }, + { GFXFormatR16G16B16A16, "GFXFormatR16G16B16A16" }, + { GFXFormatR16G16, "GFXFormatR16G16" }, + { GFXFormatR16F, "GFXFormatR16F" }, + { GFXFormatR16G16F, "GFXFormatR16G16F" }, + { GFXFormatR10G10B10A2, "GFXFormatR10G10B10A2" }, + +EndImplementEnumType; + + +ImplementEnumType( GFXCullMode, + "The render cull modes.\n" + "@ingroup GFX" ) + + { GFXCullNone, "GFXCullNone" }, + { GFXCullCW, "GFXCullCW" }, + { GFXCullCCW, "GFXCullCCW" } + +EndImplementEnumType; + + +ImplementEnumType( GFXStencilOp, + "The stencil operators.\n" + "@ingroup GFX" ) + + { GFXStencilOpKeep, "GFXStencilOpKeep" }, + { GFXStencilOpZero, "GFXStencilOpZero" }, + { GFXStencilOpReplace, "GFXStencilOpReplace" }, + { GFXStencilOpIncrSat, "GFXStencilOpIncrSat" }, + { GFXStencilOpDecrSat, "GFXStencilOpDecrSat" }, + { GFXStencilOpInvert, "GFXStencilOpInvert" }, + { GFXStencilOpIncr, "GFXStencilOpIncr" }, + { GFXStencilOpDecr, "GFXStencilOpDecr" }, + +EndImplementEnumType; + + +ImplementEnumType( GFXBlendOp, + "The blend operators.\n" + "@ingroup GFX" ) + + { GFXBlendOpAdd, "GFXBlendOpAdd" }, + { GFXBlendOpSubtract, "GFXBlendOpSubtract" }, + { GFXBlendOpRevSubtract, "GFXBlendOpRevSubtract" }, + { GFXBlendOpMin, "GFXBlendOpMin" }, + { GFXBlendOpMax, "GFXBlendOpMax" } + +EndImplementEnumType; diff --git a/Engine/source/gfx/gfxAPI.h b/Engine/source/gfx/gfxAPI.h new file mode 100644 index 000000000..1faedff2a --- /dev/null +++ b/Engine/source/gfx/gfxAPI.h @@ -0,0 +1,67 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXAPI_H_ +#define _GFXAPI_H_ + +#ifndef _ENGINEAPI_H_ + #include "console/engineAPI.h" +#endif +#ifndef _GFXSTRUCTS_H_ + #include "gfx/gfxStructs.h" +#endif +#ifndef _GFXENUMS_H_ + #include "gfx/gfxEnums.h" +#endif + + +DECLARE_SCOPE( GFXAPI ); + +DefineEnumType( GFXBlend ); +DefineEnumType( GFXCmpFunc ); +DefineEnumType( GFXTextureAddressMode ); +DefineEnumType( GFXFormat ); +DefineEnumType( GFXTextureTransformFlags ); +DefineEnumType( GFXTextureFilterType ); +DefineEnumType( GFXTextureOp ); +DefineEnumType( GFXTextureArgument ); +DefineEnumType( GFXCullMode ); +DefineEnumType( GFXStencilOp ); +DefineEnumType( GFXBlendOp ); +DefineEnumType( GFXAdapterType ); + +DECLARE_STRUCT( GFXVideoMode ); + +DefineConsoleType( TypeGFXAdapterType, GFXAdapterType ); +DefineConsoleType( TypeGFXBlend, GFXBlend ); +DefineConsoleType( TypeGFXCmpFunc, GFXCmpFunc ); +DefineConsoleType( TypeGFXTextureAddressMode, GFXTextureAddressMode ); +DefineConsoleType( TypeGFXFormat, GFXFormat ); +DefineConsoleType( TypeGFXTextureTransformFlags, GFXTextureTransformFlags ); +DefineConsoleType( TypeGFXTextureFilterType, GFXTextureFilterType ); +DefineConsoleType( TypeGFXTextureOp, GFXTextureOp ); +DefineConsoleType( TypeGFXTextureArgument, GFXTextureArgument ); +DefineConsoleType( TypeGFXCullMode, GFXCullMode ); +DefineConsoleType( TypeGFXStencilOp, GFXStencilOp ); +DefineConsoleType( TypeGFXBlendOp, GFXBlendOp ); + +#endif // !_GFXAPI_H_ diff --git a/Engine/source/gfx/gfxAdapter.h b/Engine/source/gfx/gfxAdapter.h new file mode 100644 index 000000000..834b84dbd --- /dev/null +++ b/Engine/source/gfx/gfxAdapter.h @@ -0,0 +1,80 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXADAPTER_H_ +#define _GFXADAPTER_H_ + +#ifndef _GFXSTRUCTS_H_ +#include "gfx/gfxStructs.h" +#endif + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +#ifndef _UTIL_DELEGATE_H_ +#include "core/util/delegate.h" +#endif + +struct GFXAdapter +{ +public: + typedef Delegate CreateDeviceInstanceDelegate; + + enum + { + MaxAdapterNameLen = 512, + }; + + char mName[MaxAdapterNameLen]; + + /// List of available full-screen modes. Windows can be any size, + /// so we do not enumerate them here. + Vector mAvailableModes; + + /// Supported shader model. 0.f means none supported. + F32 mShaderModel; + + const char * getName() const { return mName; } + GFXAdapterType mType; + U32 mIndex; + CreateDeviceInstanceDelegate mCreateDeviceInstanceDelegate; + + GFXAdapter() + { + VECTOR_SET_ASSOCIATION( mAvailableModes ); + + mName[0] = 0; + mShaderModel = 0.f; + mIndex = 0; + } + + ~GFXAdapter() + { + mAvailableModes.clear(); + } +private: + // Disallow copying to prevent mucking with our data above. + GFXAdapter(const GFXAdapter&); +}; + +#endif // _GFXADAPTER_H_ diff --git a/Engine/source/gfx/gfxCardProfile.cpp b/Engine/source/gfx/gfxCardProfile.cpp new file mode 100644 index 000000000..cdfdefc0b --- /dev/null +++ b/Engine/source/gfx/gfxCardProfile.cpp @@ -0,0 +1,233 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/gfxCardProfile.h" + +#include "console/console.h" +#include "console/engineAPI.h" +#include "core/volume.h" + +// NOTE: The script class docs are in +// Documentation\scriptDocs\docs\classGFXCardProfiler.txt + + +void GFXCardProfiler::loadProfileScript(const char* aScriptName) +{ + const String profilePath = Con::getVariable( "$pref::Video::profilePath" ); + String scriptName = !profilePath.isEmpty() ? profilePath.c_str() : "profile"; + scriptName += "/"; + scriptName += aScriptName; + + void *data = NULL; + U32 dataSize = 0; + + Torque::FS::ReadFile( scriptName.c_str(), data, dataSize, true ); + + if(data == NULL) + { + Con::warnf(" - No card profile %s exists", scriptName.c_str()); + return; + } + + const char *script = static_cast(data); + + Con::printf(" - Loaded card profile %s", scriptName.c_str()); + + Con::executef("eval", script); + delete[] script; +} + +void GFXCardProfiler::loadProfileScripts(const String& render, const String& vendor, const String& card, const String& version) +{ + String script = render + ".cs"; + loadProfileScript(script); + + script = render + "." + vendor + ".cs"; + loadProfileScript(script); + + script = render + "." + vendor + "." + card + ".cs"; + loadProfileScript(script); + + script = render + "." + vendor + "." + card + "." + version + ".cs"; + loadProfileScript(script); +} + +GFXCardProfiler::GFXCardProfiler() : mVideoMemory( 0 ) +{ +} + +GFXCardProfiler::~GFXCardProfiler() +{ + mCapDictionary.clear(); +} + +String GFXCardProfiler::strippedString(const char *string) +{ + String res = ""; + + // And fill it with the stripped string... + const char *a=string; + while(*a) + { + if(isalnum(*a)) + { + res += *a; + } + a++; + } + + return res; +} + +void GFXCardProfiler::init() +{ + // Spew a bit... + Con::printf("Initializing GFXCardProfiler (%s)", getRendererString().c_str()); + Con::printf(" o Chipset : '%s'", getChipString().c_str()); + Con::printf(" o Card : '%s'", getCardString().c_str()); + Con::printf(" o Version : '%s'", getVersionString().c_str()); + + // Do card-specific setup... + Con::printf(" - Scanning card capabilities..."); + + setupCardCapabilities(); + + // And finally, load stuff up... + String render = strippedString(getRendererString()); + String chipset = strippedString(getChipString()); + String card = strippedString(getCardString()); + String version = strippedString(getVersionString()); + + Con::printf(" - Loading card profiles..."); + loadProfileScripts(render, chipset, card, version); +} + +U32 GFXCardProfiler::queryProfile(const String &cap) +{ + U32 res; + if( _queryCardCap( cap, res ) ) + return res; + + if(mCapDictionary.contains(cap)) + return mCapDictionary[cap]; + + Con::errorf( "GFXCardProfiler (%s) - Unknown capability '%s'.", getRendererString().c_str(), cap.c_str() ); + return 0; +} + +U32 GFXCardProfiler::queryProfile(const String &cap, U32 defaultValue) +{ + PROFILE_SCOPE( GFXCardProfiler_queryProfile ); + + U32 res; + if( _queryCardCap( cap, res ) ) + return res; + + if( mCapDictionary.contains( cap ) ) + return mCapDictionary[cap]; + else + return defaultValue; +} + +void GFXCardProfiler::setCapability(const String &cap, U32 value) +{ + // Check for dups. + if( mCapDictionary.contains( cap ) ) + { + Con::warnf( "GFXCardProfiler (%s) - Setting capability '%s' multiple times.", getRendererString().c_str(), cap.c_str() ); + mCapDictionary[cap] = value; + return; + } + + // Insert value as necessary. + Con::printf( "GFXCardProfiler (%s) - Setting capability '%s' to %d.", getRendererString().c_str(), cap.c_str(), value ); + mCapDictionary.insert( cap, value ); +} + +bool GFXCardProfiler::checkFormat( const GFXFormat fmt, const GFXTextureProfile *profile, bool &inOutAutogenMips ) +{ + return _queryFormat( fmt, profile, inOutAutogenMips ); +} + +DECLARE_SCOPE( GFXCardProfilerAPI ); +IMPLEMENT_SCOPE( GFXCardProfilerAPI, GFXCardProfiler,, ""); +ConsoleDoc( + "@class GFXCardProfilerAPI\n" + "@brief This class is the interface between TorqueScript and GFXCardProfiler.\n\n" + "You will not actually declare GFXCardProfilerAPI in TorqueScript. It exists solely " + "to give access to the GFXCardProfiler's querying functions, such as GFXCardProfiler::getRenderer.\n\n" + "@tsexample\n" + "// Example of accessing GFXCardProfiler function from script\n" + "// Notice you are not using the API version\n" + "%videoMem = GFXCardProfiler::getVideoMemoryMB();\n" + "@endtsexample\n\n" + "@see GFXCardProfiler for more information\n\n" + "@ingroup GFX\n" +); + +DefineEngineStaticMethod( GFXCardProfilerAPI, getVersion, String, (),, + "Returns the driver version string." ) +{ + return GFX->getCardProfiler()->getVersionString(); +} + +DefineEngineStaticMethod( GFXCardProfilerAPI, getCard, String, (),, + "Returns the card name." ) +{ + return GFX->getCardProfiler()->getCardString(); +} + +DefineEngineStaticMethod( GFXCardProfilerAPI, getVendor, String, (),, + "Returns the card vendor name." ) +{ + // TODO: Fix all of this vendor crap, it's not consistent + return GFX->getCardProfiler()->getChipString(); +} + +DefineEngineStaticMethod( GFXCardProfilerAPI, getRenderer, String, (),, + "Returns the renderer name. For example D3D9 or OpenGL." ) +{ + return GFX->getCardProfiler()->getRendererString(); +} + +DefineEngineStaticMethod( GFXCardProfilerAPI, getVideoMemoryMB, S32, (),, + "Returns the amount of video memory in megabytes." ) +{ + return GFX->getCardProfiler()->getVideoMemoryInMB(); +} + +DefineEngineStaticMethod( GFXCardProfilerAPI, setCapability, void, ( const char *name, S32 value ),, + "Used to set the value for a specific card capability.\n" + "@param name The name of the capability being set.\n" + "@param value The value to set for that capability." ) +{ + GFX->getCardProfiler()->setCapability( name, (U32)value ); +} + +DefineEngineStaticMethod( GFXCardProfilerAPI, queryProfile, S32, ( const char *name, S32 defaultValue ),, + "Used to query the value of a specific card capability.\n" + "@param name The name of the capability being queried.\n" + "@param defaultValue The value to return if the capability is not defined." ) +{ + return (S32)GFX->getCardProfiler()->queryProfile( name, (U32)defaultValue ); +} diff --git a/Engine/source/gfx/gfxCardProfile.h b/Engine/source/gfx/gfxCardProfile.h new file mode 100644 index 000000000..183b27532 --- /dev/null +++ b/Engine/source/gfx/gfxCardProfile.h @@ -0,0 +1,153 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXCARDPROFILE_H_ +#define _GFXCARDPROFILE_H_ + +#ifndef _TDICTIONARY_H_ +#include "core/util/tDictionary.h" +#endif +#ifndef _GFXDEVICE_H_ +#include "gfx/gfxDevice.h" +#endif +#ifndef _TORQUE_STRING_H_ +#include "core/util/str.h" +#endif + + +/// GFXCardProfiler provides a device independent wrapper around both the +/// capabilities reported by the card/drivers and the exceptions recorded +/// in various scripts. +/// +/// See the Torque Scripting Manual for more details. +/// +class GFXCardProfiler +{ + /// @name icpi Internal Card Profile Interface + /// + /// This is the interface implemented by subclasses of this class in order + /// to provide implementation-specific information about the current + /// card/drivers. + /// + /// Basically, the implementation needs to provide some unique strings: + /// - mVersionString indicating the current driver version of the + /// card in question. (For instance, "53.36") + /// - mCardDescription indicating the name of the card ("Radeon 8500") + /// - getRendererString() indicating the name of the renderer ("DX9", "GL1.2"). + /// Each card profiler subclass must return a unique constant so we can keep + /// data separate. Bear in mind that punctuation is stripped from filenames. + /// + /// The profiler also needs to implement setupCardCapabilities(), which is responsible + /// for querying the active device and setting defaults based on the reported capabilities, + /// and _queryCardCap, which is responsible for recognizing and responding to + /// device-specific capability queries. + /// + /// @{ + +public: + + /// + const String &getVersionString() const { return mVersionString; } + const String &getCardString() const { return mCardDescription; } + const String &getChipString() const { return mChipSet; } + U32 getVideoMemoryInMB() const { return mVideoMemory; } + + virtual const String &getRendererString() const = 0; + +protected: + + String mVersionString; + String mCardDescription; + String mChipSet; + U32 mVideoMemory; + + virtual void setupCardCapabilities()=0; + + /// Implementation specific query code. + /// + /// This function is meant to be overridden by the specific implementation class. + /// + /// Some query strings are handled by the external implementation while others must + /// be done by the specific implementation. This is given first chance to return + /// a result, then the generic rules are applied. + /// + /// @param query Capability being queried. + /// @param foundResult Result to return to the caller. If the function returns true + /// then this value is returned as the result of the query. + virtual bool _queryCardCap(const String &query, U32 &foundResult)=0; + + virtual bool _queryFormat( const GFXFormat fmt, const GFXTextureProfile *profile, bool &inOutAutogenMips ) = 0; + /// @} + + /// @name helpergroup Helper Functions + /// + /// Various helper functions. + + /// Load a specified script file from the profiles directory, if it exists. + void loadProfileScript(const char* scriptName); + + /// Load the script files in order for the specified card profile tuple. + void loadProfileScripts(const String& render, const String& vendor, const String& card, const String& version); + + String strippedString(const char*string); + + /// @} + + /// Capability dictionary. + Map mCapDictionary; + +public: + + /// @name ecpi External Card Profile Interface + /// + /// @{ + + /// Called for a profile for a given device. + GFXCardProfiler(); + virtual ~GFXCardProfiler(); + + /// Set load script files and generally initialize things. + virtual void init()=0; + + /// Called to query a capability. Given a query string it returns a + /// bool indicating whether or not the capability holds. If you call + /// this and cap isn't recognized then it returns false and prints + /// a console error. + U32 queryProfile(const String &cap); + + /// Same as queryProfile(), but a default can be specified to indicate + /// what value should be returned if the profiler doesn't know anything + /// about it. If cap is not recognized, defaultValue is returned and + /// no error is reported. + U32 queryProfile(const String &cap, U32 defaultValue); + + /// Set the specified capability to the specified value. + void setCapability(const String &cap, U32 value); + + /// Queries support for the specified texture format, and texture profile + bool checkFormat( const GFXFormat fmt, const GFXTextureProfile *profile, bool &inOutAutogenMips ); + + /// @} +}; + +#endif + diff --git a/Engine/source/gfx/gfxCubemap.cpp b/Engine/source/gfx/gfxCubemap.cpp new file mode 100644 index 000000000..69d55f511 --- /dev/null +++ b/Engine/source/gfx/gfxCubemap.cpp @@ -0,0 +1,109 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/gfxCubemap.h" +#include "gfx/gfxDevice.h" +#include "gfx/bitmap/gBitmap.h" +#include "gfx/gfxTextureManager.h" + + +GFXCubemap::~GFXCubemap() +{ + // If we're not dynamic and we were loaded from a + // file then give the texture manager a chance to + // remove us from the cache. + if ( mPath.isNotEmpty() ) + TEXMGR->releaseCubemap( this ); +} + +void GFXCubemap::initNormalize( U32 size ) +{ + Point3F axis[6] = + {Point3F(1.0, 0.0, 0.0), Point3F(-1.0, 0.0, 0.0), + Point3F(0.0, 1.0, 0.0), Point3F( 0.0, -1.0, 0.0), + Point3F(0.0, 0.0, 1.0), Point3F( 0.0, 0.0, -1.0),}; + Point3F s[6] = + {Point3F(0.0, 0.0, -1.0), Point3F( 0.0, 0.0, 1.0), + Point3F(1.0, 0.0, 0.0), Point3F( 1.0, 0.0, 0.0), + Point3F(1.0, 0.0, 0.0), Point3F(-1.0, 0.0, 0.0),}; + Point3F t[6] = + {Point3F(0.0, -1.0, 0.0), Point3F(0.0, -1.0, 0.0), + Point3F(0.0, 0.0, 1.0), Point3F(0.0, 0.0, -1.0), + Point3F(0.0, -1.0, 0.0), Point3F(0.0, -1.0, 0.0),}; + + F32 span = 2.0; + F32 start = -1.0; + + F32 stride = span / F32(size - 1); + GFXTexHandle faces[6]; + + for(U32 i=0; i<6; i++) + { + GFXTexHandle &tex = faces[i]; + GBitmap *bitmap = new GBitmap(size, size); + + // fill in... + for(U32 v=0; vgetAddress(u, v); + bits[0] = U8(vector.x); + bits[1] = U8(vector.y); + bits[2] = U8(vector.z); + } + } + + tex.set(bitmap, &GFXDefaultStaticDiffuseProfile, true, "Cubemap"); + } + + initStatic(faces); +} + +const String GFXCubemap::describeSelf() const +{ + // We've got nothing + return String(); +} + + +bool GFXCubemapHandle::set( const String &cubemapDDS ) +{ + /// Free the previous handle to give us + /// back any texture memory when it can. + free(); + + // Let the texture manager find this for us. + StrongRefPtr::set( TEXMGR->createCubemap( cubemapDDS ) ); + + return isValid(); +} diff --git a/Engine/source/gfx/gfxCubemap.h b/Engine/source/gfx/gfxCubemap.h new file mode 100644 index 000000000..79a2bbd91 --- /dev/null +++ b/Engine/source/gfx/gfxCubemap.h @@ -0,0 +1,96 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXCUBEMAP_H_ +#define _GFXCUBEMAP_H_ + +#ifndef _GFXTEXTUREHANDLE_H_ +#include "gfx/gfxTextureHandle.h" +#endif + +class GFXDevice; +struct DDSFile; + +/// +class GFXCubemap : public StrongRefBase, public GFXResource +{ + friend class GFXDevice; + friend class GFXTextureManager; + +protected: + + // should only be called by GFXDevice + virtual void setToTexUnit( U32 tuNum ) = 0; + + /// The path to the cubemap file. + String mPath; + + /// Sets the cubemap file path. + void _setPath( const String &path ) { mPath = path; } + +public: + + /// Create a static cubemap from a list of 6 face textures. + virtual void initStatic( GFXTexHandle *faces ) = 0; + + /// Create a static cubemap from a DDS cubemap file. + virtual void initStatic( DDSFile *dds ) = 0; + + /// + virtual void initDynamic( U32 texSize, GFXFormat faceFormat = GFXFormatR8G8B8A8 ) = 0; + + void initNormalize(U32 size); + + virtual ~GFXCubemap(); + + /// Returns the size of the faces. + virtual U32 getSize() const = 0; + + /// Returns the face texture format. + virtual GFXFormat getFormat() const = 0; + + /// Returns the cubemap file path set at creation. + const String& getPath() const { return mPath; } + + // GFXResource interface + /// The resource should put a description of itself (number of vertices, size/width of texture, etc.) in buffer + virtual const String describeSelf() const; +}; + + +/// A reference counted handle to a cubemap resource. +class GFXCubemapHandle : public StrongRefPtr +{ +public: + GFXCubemapHandle() {} + GFXCubemapHandle( GFXCubemap *cubemap ) { StrongRefPtr::set( cubemap ); } + GFXCubemapHandle( const String &cubemapDDS ) { set( cubemapDDS ); } + + /// Set a cubemap from a DDS cubemap texture file. + bool set( const String &cubemapDDS ); + + /// Releases the texture handle. + void free() { StrongObjectRef::set( NULL ); } +}; + + +#endif // GFXCUBEMAP diff --git a/Engine/source/gfx/gfxDebugEvent.h b/Engine/source/gfx/gfxDebugEvent.h new file mode 100644 index 000000000..a6763f800 --- /dev/null +++ b/Engine/source/gfx/gfxDebugEvent.h @@ -0,0 +1,83 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXDEBUGEVENT_H_ +#define _GFXDEBUGEVENT_H_ + +#ifndef _GFXDEVICE_H_ +#include "gfx/gfxDevice.h" +#endif + +/// See TorqueConfig.h to enable this. +#ifdef TORQUE_ENABLE_GFXDEBUGEVENTS + + +/// You shouldn't use this class directly... use the +/// following macros: +/// +/// GFXDEBUGEVENT_START / GFXDEBUGEVENT_END +/// GFXDEBUGEVENT_SCOPE +/// +class GFXDebugEventScope +{ +public: + GFXDebugEventScope( const char* name, const ColorI &color ) + { + GFX->enterDebugEvent( color, name ); + } + + ~GFXDebugEventScope() + { + GFX->leaveDebugEvent(); + } +}; + +#define GFXDEBUGEVENT_START( name, color ) GFX->enterDebugEvent( color, #name ) + +#define GFXDEBUGEVENT_END() GFX->leaveDebugEvent() + +#define GFXDEBUGEVENT_MARKER( name, color ) GFX->setDebugMarker( color, #name ) + + +/// +/// Will add start/end GFX debug events around the +/// current scope. +/// +/// @param name The unquoted name for the event. +/// @param color A ColorI to associate with the event. +/// +#define GFXDEBUGEVENT_SCOPE( name, color ) GFXDebugEventScope GFXDebugEventScope##name##Obj( #name, color ) +#define GFXDEBUGEVENT_SCOPE_EX( name, color, desc ) GFXDebugEventScope GFXDebugEventScope##name##Obj( desc, color ) + +#else // !TORQUE_ENABLE_GFXDEBUGEVENTS + + /// These are disabled in shipping builds or maybe you + /// forgot to include "platform/platform.h" first? + #define GFXDEBUGEVENT_START(n,c) + #define GFXDEBUGEVENT_END() + #define GFXDEBUGEVENT_MARKER(n,c) + #define GFXDEBUGEVENT_SCOPE(n,c) + #define GFXDEBUGEVENT_SCOPE_EX(n,c,d) + +#endif + +#endif // _GFXDEBUGEVENT_H_ diff --git a/Engine/source/gfx/gfxDevice.cpp b/Engine/source/gfx/gfxDevice.cpp new file mode 100644 index 000000000..4f5cae5d7 --- /dev/null +++ b/Engine/source/gfx/gfxDevice.cpp @@ -0,0 +1,1304 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/gfxDevice.h" + +#include "gfx/gfxInit.h" +#include "gfx/gfxCubemap.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxDrawUtil.h" +#include "gfx/gfxFence.h" +#include "gfx/gfxFontRenderBatcher.h" +#include "gfx/gfxPrimitiveBuffer.h" +#include "gfx/gfxShader.h" +#include "gfx/gfxStateBlock.h" +#include "gfx/screenshot.h" +#include "gfx/gfxStringEnumTranslate.h" +#include "gfx/gfxTextureManager.h" + +#include "core/frameAllocator.h" +#include "core/stream/fileStream.h" +#include "core/strings/unicode.h" +#include "core/util/journal/process.h" +#include "core/util/safeDelete.h" +#include "math/util/frustum.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" + +GFXDevice * GFXDevice::smGFXDevice = NULL; +bool GFXDevice::smWireframe = false; +bool GFXDevice::smDisableVSync = true; +F32 GFXDevice::smForcedPixVersion = -1.0f; +bool GFXDevice::smDisableOcclusionQuery = false; +bool gDisassembleAllShaders = false; + + +void GFXDevice::initConsole() +{ + GFXStringEnumTranslate::init(); + + Con::addVariable( "$gfx::wireframe", TypeBool, &smWireframe, + "Used to toggle wireframe rendering at runtime.\n" + "@ingroup GFX\n" ); + + Con::addVariable( "$gfx::disassembleAllShaders", TypeBool, &gDisassembleAllShaders, + "On supported devices this will dump shader disassembly to the " + "procedural shader folder.\n" + "@ingroup GFX\n" ); + + Con::addVariable( "$gfx::disableOcclusionQuery", TypeBool, &smDisableOcclusionQuery, + "Debug helper that disables all hardware occlusion queries causing " + "them to return only the visibile state.\n" + "@ingroup GFX\n" ); + + Con::addVariable( "$pref::Video::disableVerticalSync", TypeBool, &smDisableVSync, + "Disables vertical sync on the active device.\n" + "@note The video mode must be reset for the change to take affect.\n" + "@ingroup GFX\n" ); + + Con::addVariable( "$pref::Video::forcedPixVersion", TypeF32, &smForcedPixVersion, + "Will force the shader model if the value is positive and less than the " + "shader model supported by the active device. Use 0 for fixed function.\n" + "@note The graphics device must be reset for the change to take affect.\n" + "@ingroup GFX\n" ); +} + +GFXDevice::DeviceEventSignal& GFXDevice::getDeviceEventSignal() +{ + static DeviceEventSignal theSignal; + return theSignal; +} + +GFXDevice::GFXDevice() +{ + VECTOR_SET_ASSOCIATION( mVideoModes ); + VECTOR_SET_ASSOCIATION( mRTStack ); + + mWorldMatrixDirty = false; + mWorldStackSize = 0; + mProjectionMatrixDirty = false; + mViewMatrixDirty = false; + mTextureMatrixCheckDirty = false; + + mViewMatrix.identity(); + mProjectionMatrix.identity(); + + for( int i = 0; i < WORLD_STACK_MAX; i++ ) + mWorldMatrix[i].identity(); + + AssertFatal(smGFXDevice == NULL, "Already a GFXDevice created! Bad!"); + smGFXDevice = this; + + // Vertex buffer cache + mCurrVertexDecl = NULL; + mVertexDeclDirty = false; + for ( U32 i=0; i < VERTEX_STREAM_COUNT; i++ ) + { + mVertexBufferDirty[i] = false; + mVertexBufferFrequency[i] = 0; + mVertexBufferFrequencyDirty[i] = false; + } + + // Primitive buffer cache + mPrimitiveBufferDirty = false; + mTexturesDirty = false; + + // Use of TEXTURE_STAGE_COUNT in initialization is okay [7/2/2007 Pat] + for(U32 i = 0; i < TEXTURE_STAGE_COUNT; i++) + { + mTextureDirty[i] = false; + mCurrentTexture[i] = NULL; + mNewTexture[i] = NULL; + mCurrentCubemap[i] = NULL; + mNewCubemap[i] = NULL; + mTexType[i] = GFXTDT_Normal; + + mTextureMatrix[i].identity(); + mTextureMatrixDirty[i] = false; + } + + mLightsDirty = false; + for(U32 i = 0; i < LIGHT_STAGE_COUNT; i++) + { + mLightDirty[i] = false; + mCurrentLightEnable[i] = false; + } + + mGlobalAmbientColorDirty = false; + mGlobalAmbientColor = ColorF(0.0f, 0.0f, 0.0f, 1.0f); + + mLightMaterialDirty = false; + dMemset(&mCurrentLightMaterial, NULL, sizeof(GFXLightMaterial)); + + // State block + mStateBlockDirty = false; + mCurrentStateBlock = NULL; + mNewStateBlock = NULL; + + mCurrentShaderConstBuffer = NULL; + + // misc + mAllowRender = true; + mCanCurrentlyRender = false; + mInitialized = false; + + mRTDirty = false; + mViewport = RectI::Zero; + mViewportDirty = false; + + mCurrentFrontBufferIdx = 0; + + mDeviceSwizzle32 = NULL; + mDeviceSwizzle24 = NULL; + + mResourceListHead = NULL; + + mCardProfiler = NULL; + + // Initialize our drawing utility. + mDrawer = NULL; + + // Add a few system wide shader macros. + GFXShader::addGlobalMacro( "TORQUE", "1" ); + GFXShader::addGlobalMacro( "TORQUE_VERSION", String::ToString(getVersionNumber()) ); + #if defined TORQUE_OS_WIN32 + GFXShader::addGlobalMacro( "TORQUE_OS_WIN32" ); + #elif defined TORQUE_OS_MAC + GFXShader::addGlobalMacro( "TORQUE_OS_MAC" ); + #elif defined TORQUE_OS_LINUX + GFXShader::addGlobalMacro( "TORQUE_OS_LINUX" ); + #elif defined TORQUE_OS_XENON + GFXShader::addGlobalMacro( "TORQUE_OS_XENON" ); + #elif defined TORQUE_OS_XBOX + GFXShader::addGlobalMacro( "TORQUE_OS_XBOX" ); + #elif defined TORQUE_OS_PS3 + GFXShader::addGlobalMacro( "TORQUE_OS_PS3" ); + #endif +} + +GFXDrawUtil* GFXDevice::getDrawUtil() +{ + if (!mDrawer) + { + mDrawer = new GFXDrawUtil(this); + } + return mDrawer; +} + +void GFXDevice::deviceInited() +{ + getDeviceEventSignal().trigger(deInit); + mDeviceStatistics.setPrefix("$GFXDeviceStatistics::"); + + // Initialize the static helper textures. + GBitmap temp( 2, 2, false, GFXFormatR8G8B8A8 ); + temp.fill( ColorI::ONE ); + GFXTexHandle::ONE.set( &temp, &GFXDefaultStaticDiffuseProfile, false, "GFXTexHandle::ONE" ); + temp.fill( ColorI::ZERO ); + GFXTexHandle::ZERO.set( &temp, &GFXDefaultStaticDiffuseProfile, false, "GFXTexHandle::ZERO" ); + temp.fill( ColorI( 128, 128, 255 ) ); + GFXTexHandle::ZUP.set( &temp, &GFXDefaultStaticNormalMapProfile, false, "GFXTexHandle::ZUP" ); +} + +bool GFXDevice::destroy() +{ + // Cleanup the static helper textures. + GFXTexHandle::ONE.free(); + GFXTexHandle::ZERO.free(); + GFXTexHandle::ZUP.free(); + + // Make this release its buffer. + PrimBuild::shutdown(); + + // Let people know we are shutting down + getDeviceEventSignal().trigger(deDestroy); + + if(smGFXDevice) + smGFXDevice->preDestroy(); + SAFE_DELETE(smGFXDevice); + + return true; +} + +void GFXDevice::preDestroy() +{ + // Delete draw util + SAFE_DELETE( mDrawer ); +} + +GFXDevice::~GFXDevice() +{ + smGFXDevice = NULL; + + // Clean up our current buffers. + mCurrentPrimitiveBuffer = NULL; + for ( U32 i=0; i < VERTEX_STREAM_COUNT; i++ ) + mCurrentVertexBuffer[i] = NULL; + + // Clear out our current texture references + for (U32 i = 0; i < TEXTURE_STAGE_COUNT; i++) + { + mCurrentTexture[i] = NULL; + mNewTexture[i] = NULL; + mCurrentCubemap[i] = NULL; + mNewCubemap[i] = NULL; + } + + // Release all the unreferenced textures in the cache. + mTextureManager->cleanupCache(); + + // Check for resource leaks +#ifdef TORQUE_DEBUG + AssertFatal( GFXTextureObject::dumpActiveTOs() == 0, "There is a texture object leak, check the log for more details." ); + GFXPrimitiveBuffer::dumpActivePBs(); +#endif + + SAFE_DELETE( mTextureManager ); + + // Clear out our state block references + mCurrentStateBlocks.clear(); + mNewStateBlock = NULL; + mCurrentStateBlock = NULL; + + mCurrentShaderConstBuffer = NULL; + /// End Block above BTR + + // -- Clear out resource list + // Note: our derived class destructor will have already released resources. + // Clearing this list saves us from having our resources (which are not deleted + // just released) turn around and try to remove themselves from this list. + while (mResourceListHead) + { + GFXResource * head = mResourceListHead; + mResourceListHead = head->mNextResource; + + head->mPrevResource = NULL; + head->mNextResource = NULL; + head->mOwningDevice = NULL; + } +} + +GFXStateBlockRef GFXDevice::createStateBlock(const GFXStateBlockDesc& desc) +{ + PROFILE_SCOPE( GFXDevice_CreateStateBlock ); + + U32 hashValue = desc.getHashValue(); + if (mCurrentStateBlocks[hashValue]) + return mCurrentStateBlocks[hashValue]; + + GFXStateBlockRef result = createStateBlockInternal(desc); + result->registerResourceWithDevice(this); + mCurrentStateBlocks[hashValue] = result; + return result; +} + +void GFXDevice::setStateBlock(GFXStateBlock* block) +{ + AssertFatal(block, "NULL state block!"); + AssertFatal(block->getOwningDevice() == this, "This state doesn't apply to this device!"); + + if (block != mCurrentStateBlock) + { + mStateDirty = true; + mStateBlockDirty = true; + mNewStateBlock = block; + } else { + mStateBlockDirty = false; + mNewStateBlock = mCurrentStateBlock; + } +} + +void GFXDevice::setStateBlockByDesc( const GFXStateBlockDesc &desc ) +{ + PROFILE_SCOPE( GFXDevice_SetStateBlockByDesc ); + GFXStateBlock *block = createStateBlock( desc ); + setStateBlock( block ); +} + +void GFXDevice::setShaderConstBuffer(GFXShaderConstBuffer* buffer) +{ + mCurrentShaderConstBuffer = buffer; +} + +void GFXDevice::updateStates(bool forceSetAll /*=false*/) +{ + PROFILE_SCOPE(GFXDevice_updateStates); + + if(forceSetAll) + { + bool rememberToEndScene = false; + if(!canCurrentlyRender()) + { + if (!beginScene()) + { + AssertFatal(false, "GFXDevice::updateStates: Unable to beginScene!"); + } + rememberToEndScene = true; + } + + setMatrix( GFXMatrixProjection, mProjectionMatrix ); + setMatrix( GFXMatrixWorld, mWorldMatrix[mWorldStackSize] ); + setMatrix( GFXMatrixView, mViewMatrix ); + + setVertexDecl( mCurrVertexDecl ); + + for ( U32 i=0; i < VERTEX_STREAM_COUNT; i++ ) + { + setVertexStream( i, mCurrentVertexBuffer[i] ); + setVertexStreamFrequency( i, mVertexBufferFrequency[i] ); + } + + if( mCurrentPrimitiveBuffer.isValid() ) // This could be NULL when the device is initalizing + mCurrentPrimitiveBuffer->prepare(); + + /// Stateblocks + if ( mNewStateBlock ) + setStateBlockInternal(mNewStateBlock, true); + mCurrentStateBlock = mNewStateBlock; + + for(U32 i = 0; i < getNumSamplers(); i++) + { + switch (mTexType[i]) + { + case GFXTDT_Normal : + { + mCurrentTexture[i] = mNewTexture[i]; + setTextureInternal(i, mCurrentTexture[i]); + } + break; + case GFXTDT_Cube : + { + mCurrentCubemap[i] = mNewCubemap[i]; + if (mCurrentCubemap[i]) + mCurrentCubemap[i]->setToTexUnit(i); + else + setTextureInternal(i, NULL); + } + break; + default: + AssertFatal(false, "Unknown texture type!"); + break; + } + } + + // Set our material + setLightMaterialInternal(mCurrentLightMaterial); + + // Set our lights + for(U32 i = 0; i < LIGHT_STAGE_COUNT; i++) + { + setLightInternal(i, mCurrentLight[i], mCurrentLightEnable[i]); + } + + _updateRenderTargets(); + + if(rememberToEndScene) + endScene(); + + return; + } + + if (!mStateDirty) + return; + + // Normal update logic begins here. + mStateDirty = false; + + // Update Projection Matrix + if( mProjectionMatrixDirty ) + { + setMatrix( GFXMatrixProjection, mProjectionMatrix ); + mProjectionMatrixDirty = false; + } + + // Update World Matrix + if( mWorldMatrixDirty ) + { + setMatrix( GFXMatrixWorld, mWorldMatrix[mWorldStackSize] ); + mWorldMatrixDirty = false; + } + + // Update View Matrix + if( mViewMatrixDirty ) + { + setMatrix( GFXMatrixView, mViewMatrix ); + mViewMatrixDirty = false; + } + + + if( mTextureMatrixCheckDirty ) + { + for( int i = 0; i < getNumSamplers(); i++ ) + { + if( mTextureMatrixDirty[i] ) + { + mTextureMatrixDirty[i] = false; + setMatrix( (GFXMatrixType)(GFXMatrixTexture + i), mTextureMatrix[i] ); + } + } + + mTextureMatrixCheckDirty = false; + } + + // Update the vertex declaration. + if ( mVertexDeclDirty ) + { + setVertexDecl( mCurrVertexDecl ); + mVertexDeclDirty = false; + } + + // Update the vertex buffers. + for ( U32 i=0; i < VERTEX_STREAM_COUNT; i++ ) + { + if ( mVertexBufferDirty[i] ) + { + setVertexStream( i, mCurrentVertexBuffer[i] ); + mVertexBufferDirty[i] = false; + } + + if ( mVertexBufferFrequencyDirty[i] ) + { + setVertexStreamFrequency( i, mVertexBufferFrequency[i] ); + mVertexBufferFrequencyDirty[i] = false; + } + } + + // Update primitive buffer + // + // NOTE: It is very important to set the primitive buffer AFTER the vertex buffer + // because in order to draw indexed primitives in DX8, the call to SetIndicies + // needs to include the base vertex offset, and the DX8 GFXDevice relies on + // having mCurrentVB properly assigned before the call to setIndices -patw + if( mPrimitiveBufferDirty ) + { + if( mCurrentPrimitiveBuffer.isValid() ) // This could be NULL when the device is initalizing + mCurrentPrimitiveBuffer->prepare(); + mPrimitiveBufferDirty = false; + } + + // NOTE: With state blocks, it's now important to update state before setting textures + // some devices (e.g. OpenGL) set states on the texture and we need that information before + // the texture is activated. + if (mStateBlockDirty) + { + setStateBlockInternal(mNewStateBlock, false); + mCurrentStateBlock = mNewStateBlock; + mStateBlockDirty = false; + } + + if( mTexturesDirty ) + { + mTexturesDirty = false; + for(U32 i = 0; i < getNumSamplers(); i++) + { + if(!mTextureDirty[i]) + continue; + mTextureDirty[i] = false; + + switch (mTexType[i]) + { + case GFXTDT_Normal : + { + mCurrentTexture[i] = mNewTexture[i]; + setTextureInternal(i, mCurrentTexture[i]); + } + break; + case GFXTDT_Cube : + { + mCurrentCubemap[i] = mNewCubemap[i]; + if (mCurrentCubemap[i]) + mCurrentCubemap[i]->setToTexUnit(i); + else + setTextureInternal(i, NULL); + } + break; + default: + AssertFatal(false, "Unknown texture type!"); + break; + } + } + } + + // Set light material + if(mLightMaterialDirty) + { + setLightMaterialInternal(mCurrentLightMaterial); + mLightMaterialDirty = false; + } + + // Set our lights + if(mLightsDirty) + { + mLightsDirty = false; + for(U32 i = 0; i < LIGHT_STAGE_COUNT; i++) + { + if(!mLightDirty[i]) + continue; + + mLightDirty[i] = false; + setLightInternal(i, mCurrentLight[i], mCurrentLightEnable[i]); + } + } + + _updateRenderTargets(); + +#ifdef TORQUE_DEBUG_RENDER + doParanoidStateCheck(); +#endif +} + +void GFXDevice::setPrimitiveBuffer( GFXPrimitiveBuffer *buffer ) +{ + if( buffer == mCurrentPrimitiveBuffer ) + return; + + mCurrentPrimitiveBuffer = buffer; + mPrimitiveBufferDirty = true; + mStateDirty = true; +} + +void GFXDevice::drawPrimitive( U32 primitiveIndex ) +{ + AssertFatal( mCurrentPrimitiveBuffer.isValid(), "Trying to call drawPrimitive with no current primitive buffer, call setPrimitiveBuffer()" ); + AssertFatal( primitiveIndex < mCurrentPrimitiveBuffer->mPrimitiveCount, "Out of range primitive index."); + drawPrimitive( mCurrentPrimitiveBuffer->mPrimitiveArray[primitiveIndex] ); +} + +void GFXDevice::drawPrimitive( const GFXPrimitive &prim ) +{ + // Do NOT add index buffer offset to this call, it will be added by drawIndexedPrimitive + drawIndexedPrimitive( prim.type, + prim.startVertex, + prim.minIndex, + prim.numVertices, + prim.startIndex, + prim.numPrimitives ); +} + +void GFXDevice::drawPrimitives() +{ + AssertFatal( mCurrentPrimitiveBuffer.isValid(), "Trying to call drawPrimitive with no current primitive buffer, call setPrimitiveBuffer()" ); + + GFXPrimitive *info = NULL; + + for( U32 i = 0; i < mCurrentPrimitiveBuffer->mPrimitiveCount; i++ ) { + info = &mCurrentPrimitiveBuffer->mPrimitiveArray[i]; + + // Do NOT add index buffer offset to this call, it will be added by drawIndexedPrimitive + drawIndexedPrimitive( info->type, + info->startVertex, + info->minIndex, + info->numVertices, + info->startIndex, + info->numPrimitives ); + } +} + +DefineEngineFunction( getDisplayDeviceList, String, (),, + "Returns a tab-seperated string of the detected devices across all adapters.\n" + "@ingroup GFX\n" ) +{ + Vector adapters; + GFXInit::getAdapters(&adapters); + + StringBuilder str; + for (S32 i=0; imName); + } + + return str.end(); +} + +void GFXDevice::setFrustum( F32 left, + F32 right, + F32 bottom, + F32 top, + F32 nearPlane, + F32 farPlane, + bool bRotate ) +{ + // store values + mFrustum.set(false, left, right, top, bottom, nearPlane, farPlane); + + // compute matrix + MatrixF projection; + mFrustum.getProjectionMatrix(&projection, bRotate); + setProjectionMatrix( projection ); +} + +void GFXDevice::setFrustum( const Frustum& frust, bool bRotate ) +{ + // store values + mFrustum = frust; + + // compute matrix + MatrixF projection; + mFrustum.getProjectionMatrix(&projection, bRotate); + setProjectionMatrix( projection ); +} + + +void GFXDevice::getFrustum( F32 *left, F32 *right, F32 *bottom, F32 *top, F32 *nearPlane, F32 *farPlane, bool *isOrtho ) const +{ + if ( left ) *left = mFrustum.getNearLeft(); + if ( right ) *right = mFrustum.getNearRight(); + if ( bottom ) *bottom = mFrustum.getNearBottom(); + if ( top ) *top = mFrustum.getNearTop(); + if ( nearPlane ) *nearPlane = mFrustum.getNearDist(); + if ( farPlane ) *farPlane = mFrustum.getFarDist(); + if ( isOrtho ) *isOrtho = mFrustum.isOrtho(); +} + +void GFXDevice::setOrtho( F32 left, + F32 right, + F32 bottom, + F32 top, + F32 nearPlane, + F32 farPlane, + bool doRotate ) +{ + // store values + mFrustum.set(true, left, right, top, bottom, nearPlane, farPlane); + + // compute matrix + MatrixF projection; + mFrustum.getProjectionMatrix(&projection, doRotate); + + setProjectionMatrix( projection ); +} + +Point2F GFXDevice::getWorldToScreenScale() const +{ + Point2F scale; + + const RectI &viewport = getViewport(); + + if ( mFrustum.isOrtho() ) + scale.set( viewport.extent.x / mFrustum.getWidth(), + viewport.extent.y / mFrustum.getHeight() ); + else + scale.set( ( mFrustum.getNearDist() * viewport.extent.x ) / mFrustum.getWidth(), + ( mFrustum.getNearDist() * viewport.extent.y ) / mFrustum.getHeight() ); + + return scale; +} + +//----------------------------------------------------------------------------- +// Set Light +//----------------------------------------------------------------------------- +void GFXDevice::setLight(U32 stage, GFXLightInfo* light) +{ + AssertFatal(stage < LIGHT_STAGE_COUNT, "GFXDevice::setLight - out of range stage!"); + + if(!mLightDirty[stage]) + { + mStateDirty = true; + mLightsDirty = true; + mLightDirty[stage] = true; + } + mCurrentLightEnable[stage] = (light != NULL); + if(mCurrentLightEnable[stage]) + mCurrentLight[stage] = *light; +} + +//----------------------------------------------------------------------------- +// Set Light Material +//----------------------------------------------------------------------------- +void GFXDevice::setLightMaterial(GFXLightMaterial mat) +{ + mCurrentLightMaterial = mat; + mLightMaterialDirty = true; + mStateDirty = true; +} + +void GFXDevice::setGlobalAmbientColor(ColorF color) +{ + if(mGlobalAmbientColor != color) + { + mGlobalAmbientColor = color; + mGlobalAmbientColorDirty = true; + } +} + +//----------------------------------------------------------------------------- +// Set texture +//----------------------------------------------------------------------------- +void GFXDevice::setTexture( U32 stage, GFXTextureObject *texture ) +{ + AssertFatal(stage < getNumSamplers(), "GFXDevice::setTexture - out of range stage!"); + + if ( mTexType[stage] == GFXTDT_Normal && + ( ( mTextureDirty[stage] && mNewTexture[stage].getPointer() == texture ) || + ( !mTextureDirty[stage] && mCurrentTexture[stage].getPointer() == texture ) ) ) + return; + + mStateDirty = true; + mTexturesDirty = true; + mTextureDirty[stage] = true; + + mNewTexture[stage] = texture; + mTexType[stage] = GFXTDT_Normal; + + // Clear out the cubemaps + mNewCubemap[stage] = NULL; + mCurrentCubemap[stage] = NULL; +} + +//----------------------------------------------------------------------------- +// Set cube texture +//----------------------------------------------------------------------------- +void GFXDevice::setCubeTexture( U32 stage, GFXCubemap *texture ) +{ + AssertFatal(stage < getNumSamplers(), "GFXDevice::setTexture - out of range stage!"); + + if ( mTexType[stage] == GFXTDT_Cube && + ( ( mTextureDirty[stage] && mNewCubemap[stage].getPointer() == texture ) || + ( !mTextureDirty[stage] && mCurrentCubemap[stage].getPointer() == texture ) ) ) + return; + + mStateDirty = true; + mTexturesDirty = true; + mTextureDirty[stage] = true; + + mNewCubemap[stage] = texture; + mTexType[stage] = GFXTDT_Cube; + + // Clear out the normal textures + mNewTexture[stage] = NULL; + mCurrentTexture[stage] = NULL; +} + +inline bool GFXDevice::beginScene() +{ + AssertFatal( mCanCurrentlyRender == false, "GFXDevice::beginScene() - The scene has already begun!" ); + + mDeviceStatistics.clear(); + + // Send the start of frame signal. + getDeviceEventSignal().trigger( GFXDevice::deStartOfFrame ); + + return beginSceneInternal(); +} + +//------------------------------------------------------------------------------ + +inline void GFXDevice::endScene() +{ + AssertFatal( mCanCurrentlyRender == true, "GFXDevice::endScene() - The scene has already ended!" ); + + // End frame signal + getDeviceEventSignal().trigger( GFXDevice::deEndOfFrame ); + + endSceneInternal(); + mDeviceStatistics.exportToConsole(); +} + +void GFXDevice::setViewport( const RectI &inRect ) +{ + // Clip the rect against the renderable size. + Point2I size = mCurrentRT->getSize(); + RectI maxRect(Point2I(0,0), size); + RectI rect = inRect; + rect.intersect(maxRect); + + if ( mViewport != rect ) + { + mViewport = rect; + mViewportDirty = true; + } +} + +void GFXDevice::pushActiveRenderTarget() +{ + // Push the current target on to the stack. + mRTStack.push_back( mCurrentRT ); +} + +void GFXDevice::popActiveRenderTarget() +{ + AssertFatal( mRTStack.size() > 0, "GFXDevice::popActiveRenderTarget() - stack is empty!" ); + + // Restore the last item on the stack and pop. + setActiveRenderTarget( mRTStack.last() ); + mRTStack.pop_back(); +} + +void GFXDevice::setActiveRenderTarget( GFXTarget *target ) +{ + AssertFatal( target, + "GFXDevice::setActiveRenderTarget - must specify a render target!" ); + + if ( target == mCurrentRT ) + return; + + // If we're not dirty then store the + // current RT for deactivation later. + if ( !mRTDirty ) + { + // Deactivate the target queued for deactivation + if(mRTDeactivate) + mRTDeactivate->deactivate(); + + mRTDeactivate = mCurrentRT; + } + + mRTDirty = true; + mCurrentRT = target; + + // When a target changes we also change the viewport + // to match it. This causes problems when the viewport + // has been modified for clipping to a GUI bounds. + // + // We should consider removing this and making it the + // responsibility of the caller to set a proper viewport + // when the target is changed. + setViewport( RectI( Point2I::Zero, mCurrentRT->getSize() ) ); +} + +/// Helper class for GFXDevice::describeResources. +class DescriptionOutputter +{ + /// Are we writing to a file? + bool mWriteToFile; + + /// File if we are writing to a file + FileStream mFile; +public: + DescriptionOutputter(const char* file) + { + mWriteToFile = false; + // If we've been given what could be a valid file path, open it. + if(file && file[0] != '\0') + { + mWriteToFile = mFile.open(file, Torque::FS::File::Write); + + // Note that it is safe to retry. If this is hit, we'll just write to the console instead of to the file. + AssertFatal(mWriteToFile, avar("DescriptionOutputter::DescriptionOutputter - could not open file %s", file)); + } + } + + ~DescriptionOutputter() + { + // Close the file + if(mWriteToFile) + mFile.close(); + } + + /// Writes line to the file or to the console, depending on what we want. + void write(const char* line) + { + if(mWriteToFile) + mFile.writeLine((const U8*)line); + else + Con::printf(line); + } +}; + +#ifndef TORQUE_SHIPPING +void GFXDevice::dumpStates( const char *fileName ) const +{ + DescriptionOutputter output(fileName); + + output.write("Current state"); + if (!mCurrentStateBlock.isNull()) + output.write(mCurrentStateBlock->getDesc().describeSelf().c_str()); + else + output.write("No state!"); + + output.write("\nAll states:\n"); + GFXResource* walk = mResourceListHead; + while(walk) + { + const GFXStateBlock* sb = dynamic_cast(walk); + if (sb) + { + output.write(sb->getDesc().describeSelf().c_str()); + } + walk = walk->getNextResource(); + } +} +#endif + +void GFXDevice::listResources(bool unflaggedOnly) +{ + U32 numTextures = 0, numShaders = 0, numRenderToTextureTargs = 0, numWindowTargs = 0; + U32 numCubemaps = 0, numVertexBuffers = 0, numPrimitiveBuffers = 0, numFences = 0; + U32 numStateBlocks = 0; + + GFXResource* walk = mResourceListHead; + while(walk) + { + if(unflaggedOnly && walk->isFlagged()) + { + walk = walk->getNextResource(); + continue; + } + + if(dynamic_cast(walk)) + numTextures++; + else if(dynamic_cast(walk)) + numShaders++; + else if(dynamic_cast(walk)) + numRenderToTextureTargs++; + else if(dynamic_cast(walk)) + numWindowTargs++; + else if(dynamic_cast(walk)) + numCubemaps++; + else if(dynamic_cast(walk)) + numVertexBuffers++; + else if(dynamic_cast(walk)) + numPrimitiveBuffers++; + else if(dynamic_cast(walk)) + numFences++; + else if (dynamic_cast(walk)) + numStateBlocks++; + else + Con::warnf("Unknown resource: %x", walk); + + walk = walk->getNextResource(); + } + const char* flag = unflaggedOnly ? "unflagged" : "allocated"; + + Con::printf("GFX currently has:"); + Con::printf(" %i %s textures", numTextures, flag); + Con::printf(" %i %s shaders", numShaders, flag); + Con::printf(" %i %s texture targets", numRenderToTextureTargs, flag); + Con::printf(" %i %s window targets", numWindowTargs, flag); + Con::printf(" %i %s cubemaps", numCubemaps, flag); + Con::printf(" %i %s vertex buffers", numVertexBuffers, flag); + Con::printf(" %i %s primitive buffers", numPrimitiveBuffers, flag); + Con::printf(" %i %s fences", numFences, flag); + Con::printf(" %i %s state blocks", numStateBlocks, flag); +} + +void GFXDevice::fillResourceVectors(const char* resNames, bool unflaggedOnly, Vector &textureObjects, + Vector &textureTargets, Vector &windowTargets, Vector &vertexBuffers, + Vector &primitiveBuffers, Vector &fences, Vector &cubemaps, + Vector &shaders, Vector &stateblocks) +{ + bool describeTexture = true, describeTextureTarget = true, describeWindowTarget = true, describeVertexBuffer = true, + describePrimitiveBuffer = true, describeFence = true, describeCubemap = true, describeShader = true, + describeStateBlock = true; + + // If we didn't specify a string of names, we'll print all of them + if(resNames && resNames[0] != '\0') + { + // If we did specify a string of names, determine which names + describeTexture = (dStrstr(resNames, "GFXTextureObject") != NULL); + describeTextureTarget = (dStrstr(resNames, "GFXTextureTarget") != NULL); + describeWindowTarget = (dStrstr(resNames, "GFXWindowTarget") != NULL); + describeVertexBuffer = (dStrstr(resNames, "GFXVertexBuffer") != NULL); + describePrimitiveBuffer = (dStrstr(resNames, "GFXPrimitiveBuffer") != NULL); + describeFence = (dStrstr(resNames, "GFXFence") != NULL); + describeCubemap = (dStrstr(resNames, "GFXCubemap") != NULL); + describeShader = (dStrstr(resNames, "GFXShader") != NULL); + describeStateBlock = (dStrstr(resNames, "GFXStateBlock") != NULL); + } + + // Start going through the list + GFXResource* walk = mResourceListHead; + while(walk) + { + // If we only want unflagged resources, skip all flagged resources + if(unflaggedOnly && walk->isFlagged()) + { + walk = walk->getNextResource(); + continue; + } + + // All of the following checks go through the same logic. + // if(describingThisResource) + // { + // ResourceType* type = dynamic_cast(walk) + // if(type) + // { + // typeVector.push_back(type); + // walk = walk->getNextResource(); + // continue; + // } + // } + + if(describeTexture) + { + GFXTextureObject* tex = dynamic_cast(walk); + { + if(tex) + { + textureObjects.push_back(tex); + walk = walk->getNextResource(); + continue; + } + } + } + if(describeShader) + { + GFXShader* shd = dynamic_cast(walk); + if(shd) + { + shaders.push_back(shd); + walk = walk->getNextResource(); + continue; + } + } + if(describeVertexBuffer) + { + GFXVertexBuffer* buf = dynamic_cast(walk); + if(buf) + { + vertexBuffers.push_back(buf); + walk = walk->getNextResource(); + continue; + } + } + if(describePrimitiveBuffer) + { + GFXPrimitiveBuffer* buf = dynamic_cast(walk); + if(buf) + { + primitiveBuffers.push_back(buf); + walk = walk->getNextResource(); + continue; + } + } + if(describeTextureTarget) + { + GFXTextureTarget* targ = dynamic_cast(walk); + if(targ) + { + textureTargets.push_back(targ); + walk = walk->getNextResource(); + continue; + } + } + if(describeWindowTarget) + { + GFXWindowTarget* targ = dynamic_cast(walk); + if(targ) + { + windowTargets.push_back(targ); + walk = walk->getNextResource(); + continue; + } + } + if(describeCubemap) + { + GFXCubemap* cube = dynamic_cast(walk); + if(cube) + { + cubemaps.push_back(cube); + walk = walk->getNextResource(); + continue; + } + } + if(describeFence) + { + GFXFence* fence = dynamic_cast(walk); + if(fence) + { + fences.push_back(fence); + walk = walk->getNextResource(); + continue; + } + } + if (describeStateBlock) + { + GFXStateBlock* sb = dynamic_cast(walk); + if (sb) + { + stateblocks.push_back(sb); + walk = walk->getNextResource(); + continue; + } + } + // Wasn't something we were looking for + walk = walk->getNextResource(); + } +} + +void GFXDevice::describeResources(const char* resNames, const char* filePath, bool unflaggedOnly) +{ + const U32 numResourceTypes = 9; + Vector resVectors[numResourceTypes]; + const char* reslabels[numResourceTypes] = { "texture", "texture target", "window target", "vertex buffers", "primitive buffers", "fences", "cubemaps", "shaders", "stateblocks" }; + + // Fill the vectors with the right resources + fillResourceVectors(resNames, unflaggedOnly, resVectors[0], resVectors[1], resVectors[2], resVectors[3], + resVectors[4], resVectors[5], resVectors[6], resVectors[7], resVectors[8]); + + // Helper object + DescriptionOutputter output(filePath); + + // Print the info to the file + // Note that we check if we have any objects of that type. + for (U32 i = 0; i < numResourceTypes; i++) + { + if (resVectors[i].size()) + { + // Header + String header = String::ToString("--------Dumping GFX %s descriptions...----------", reslabels[i]); + output.write(header); + // Data + for (U32 j = 0; j < resVectors[i].size(); j++) + { + GFXResource* resource = resVectors[i][j]; + String dataline = String::ToString("Addr: %x %s", resource, resource->describeSelf().c_str()); + output.write(dataline.c_str()); + } + // Footer + output.write("--------------------Done---------------------"); + output.write(""); + } + } +} + +void GFXDevice::flagCurrentResources() +{ + GFXResource* walk = mResourceListHead; + while(walk) + { + walk->setFlag(); + walk = walk->getNextResource(); + } +} + +void GFXDevice::clearResourceFlags() +{ + GFXResource* walk = mResourceListHead; + while(walk) + { + walk->clearFlag(); + walk = walk->getNextResource(); + } +} + +DefineEngineFunction( listGFXResources, void, ( bool unflaggedOnly ), ( false ), + "Returns a list of the unflagged GFX resources. See flagCurrentGFXResources for usage details.\n" + "@ingroup GFX\n" + "@see flagCurrentGFXResources, clearGFXResourceFlags, describeGFXResources" ) +{ + GFX->listResources(unflaggedOnly); +} + +DefineEngineFunction( flagCurrentGFXResources, void, (),, + "@brief Flags all currently allocated GFX resources.\n" + "Used for resource allocation and leak tracking by flagging " + "current resources then dumping a list of unflagged resources " + "at some later point in execution.\n" + "@ingroup GFX\n" + "@see listGFXResources, clearGFXResourceFlags, describeGFXResources" ) +{ + GFX->flagCurrentResources(); +} + +DefineEngineFunction( clearGFXResourceFlags, void, (),, + "Clears the flagged state on all allocated GFX resources. " + "See flagCurrentGFXResources for usage details.\n" + "@ingroup GFX\n" + "@see flagCurrentGFXResources, listGFXResources, describeGFXResources" ) +{ + GFX->clearResourceFlags(); +} + +DefineEngineFunction( describeGFXResources, void, ( const char *resourceTypes, const char *filePath, bool unflaggedOnly ), ( false ), + "@brief Dumps a description of GFX resources to a file or the console.\n" + "@param resourceTypes A space seperated list of resource types or an empty string for all resources.\n" + "@param filePath A file to dump the list to or an empty string to write to the console.\n" + "@param unflaggedOnly If true only unflagged resources are dumped. See flagCurrentGFXResources.\n" + "@note The resource types can be one or more of the following:\n\n" + " - texture\n" + " - texture target\n" + " - window target\n" + " - vertex buffers\n" + " - primitive buffers\n" + " - fences\n" + " - cubemaps\n" + " - shaders\n" + " - stateblocks\n\n" + "@ingroup GFX\n" ) +{ + GFX->describeResources( resourceTypes, filePath, unflaggedOnly ); +} + +DefineEngineFunction( describeGFXStateBlocks, void, ( const char *filePath ),, + "Dumps a description of all state blocks.\n" + "@param filePath A file to dump the state blocks to or an empty string to write to the console.\n" + "@ingroup GFX\n" ) +{ + GFX->dumpStates( filePath ); +} + +DefineEngineFunction( getPixelShaderVersion, F32, (),, + "Returns the pixel shader version for the active device.\n" + "@ingroup GFX\n" ) +{ + return GFX->getPixelShaderVersion(); +} + +DefineEngineFunction( setPixelShaderVersion, void, ( float version ),, + "@brief Sets the pixel shader version for the active device.\n" + "This can be used to force a lower pixel shader version than is supported by " + "the device for testing or performance optimization.\n" + "@param version The floating point shader version number.\n" + "@note This will only affect shaders/materials created after the call " + "and should be used before the game begins.\n" + "@see $pref::Video::forcedPixVersion\n" + "@ingroup GFX\n" ) +{ + GFX->setPixelShaderVersion( version ); +} + +DefineEngineFunction( getDisplayDeviceInformation, const char*, (),, + "Get the string describing the active GFX device.\n" + "@ingroup GFX\n" ) +{ + if (!GFXDevice::devicePresent()) + return "(no device)"; + + const GFXAdapter& adapter = GFX->getAdapter(); + return adapter.getName(); +} + +DefineEngineFunction( getBestHDRFormat, GFXFormat, (),, + "Returns the best texture format for storage of HDR data for the active device.\n" + "@ingroup GFX\n" ) +{ + // TODO: Maybe expose GFX::selectSupportedFormat() so that this + // specialized method can be moved to script. + + // Figure out the best HDR format. This is the smallest + // format which supports blending and filtering. + Vector formats; + formats.push_back( GFXFormatR10G10B10A2 ); + formats.push_back( GFXFormatR16G16B16A16F ); + formats.push_back( GFXFormatR16G16B16A16 ); + GFXFormat format = GFX->selectSupportedFormat( &GFXDefaultRenderTargetProfile, + formats, + true, + true, + true ); + + return format; +} diff --git a/Engine/source/gfx/gfxDevice.h b/Engine/source/gfx/gfxDevice.h new file mode 100644 index 000000000..ec9fd6797 --- /dev/null +++ b/Engine/source/gfx/gfxDevice.h @@ -0,0 +1,1074 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXDEVICE_H_ +#define _GFXDEVICE_H_ + +#ifndef _GFXADAPTER_H_ +#include "gfx/gfxAdapter.h" +#endif +#ifndef _GFXTARGET_H_ +#include "gfx/gfxTarget.h" +#endif +#ifndef _GFXVERTEXBUFFER_H_ +#include "gfx/gfxVertexBuffer.h" +#endif +#ifndef _GFXSTATEBLOCK_H_ +#include "gfx/gfxStateBlock.h" +#endif +#ifndef _GFXSHADER_H_ +#include "gfx/gfxShader.h" +#endif +#ifndef _GFXCUBEMAP_H_ +#include "gfx/gfxCubemap.h" +#endif +#ifndef _TDICTIONARY_H_ +#include "core/util/tDictionary.h" +#endif +#ifndef _TSIGNAL_H_ +#include "core/util/tSignal.h" +#endif +#ifndef _GFXDEVICESTATISTICS_H_ +#include "gfx/gfxDeviceStatistics.h" +#endif +#ifndef _MATHUTIL_FRUSTUM_H_ +#include "math/util/frustum.h" +#endif + + +class FontRenderBatcher; +class GFont; +class GFXCardProfiler; +class GFXDrawUtil; +class GFXFence; +class GFXOcclusionQuery; +class GFXPrimitiveBuffer; +class GFXShader; +class GFXStateBlock; +class GFXShaderConstBuffer; +class GFXTextureManager; + +// Global macro +#define GFX GFXDevice::get() + +#define MAX_MRT_TARGETS 4 + +//----------------------------------------------------------------------------- + +/// GFXDevice is the TSE graphics interface layer. This allows the TSE to +/// do many things, such as use multiple render devices for multi-head systems, +/// and allow a game to render in DirectX 9, OpenGL or any other API which has +/// a GFX implementation seamlessly. There are many concepts in GFX device which +/// may not be familiar to you, especially if you have not used DirectX. +/// @n +/// Buffers +/// There are three types of buffers in GFX: vertex, index and primitive. Please +/// note that index buffers are not accessable outside the GFX layer, they are wrapped +/// by primitive buffers. Primitive buffers will be explained in detail later. +/// Buffers are allocated and deallocated using their associated allocXBuffer and +/// freeXBuffer methods on the device. When a buffer is allocated you pass in a +/// pointer to, depending on the buffer, a vertex type pointer or a U16 pointer. +/// During allocation, this pointer is set to the address of where you should +/// copy in the information for this buffer. You must the tell the GFXDevice +/// that the information is in, and it should prepare the buffer for use by calling +/// the prepare method on it. Dynamic vertex buffer example: +/// @code +/// GFXVertexP *verts; // Making a buffer containing verticies with only position +/// +/// // Allocate a dynamic vertex buffer to hold 3 vertices and use *verts as the location to copy information into +/// GFXVertexBufferHandle vb = GFX->allocVertexBuffer( 3, &verts, true ); +/// +/// // Now set the information, we're making a triangle +/// verts[0].point = Point3F( 200.f, 200.f, 0.f ); +/// verts[1].point = Point3F( 200.f, 400.f, 0.f ); +/// verts[2].point = Point3F( 400.f, 200.f, 0.f ); +/// +/// // Tell GFX that the information is in and it should be made ready for use +/// // Note that nothing is done with verts, this should not and MUST NOT be deleted +/// // stored, or otherwise used after prepare is called. +/// GFX->prepare( vb ); +/// +/// // Because this is a dynamic vertex buffer, it is only assured to be valid until someone +/// // else allocates a dynamic vertex buffer, so we will render it now +/// GFX->setVertexBuffer( vb ); +/// GFX->drawPrimitive( GFXTriangleStrip, 0, 1 ); +/// +/// // Now because this is a dynamic vertex buffer it MUST NOT BE FREED you are only +/// // given a handle to a vertex buffer which belongs to the device +/// @endcode +/// +/// To use a static vertex buffer, it is very similar, this is an example using a +/// static primitive buffer: +/// @n +/// This takes place inside a constructor for a class which has a member variable +/// called mPB which is the primitive buffer for the class instance. +/// @code +/// U16 *idx; // This is going to be where to write indices +/// GFXPrimitiveInfo *primitiveInfo; // This will be where to write primitive information +/// +/// // Allocate a primitive buffer with 4 indices, and 1 primitive described for use +/// mPB = GFX->allocPrimitiveBuffer( 4, &idx, 1, &primitiveInfo ); +/// +/// // Write the index information, this is going to be for the outline of a triangle using +/// // a line strip +/// idx[0] = 0; +/// idx[1] = 1; +/// idx[2] = 2; +/// idx[3] = 0; +/// +/// // Write the information for the primitive +/// primitiveInfo->indexStart = 0; // Starting with index 0 +/// primitiveInfo->minVertex = 0; // The minimum vertex index is 0 +/// primitiveInfo->maxVertex = 3; // The maximum vertex index is 3 +/// primitiveInfo->primitiveCount = 3; // There are 3 lines we are drawing +/// primitiveInfo->type = GFXLineStrip; // This primitive info describes a line strip +/// @endcode +/// The following code takes place in the destructor for the same class +/// @code +/// // Because this is a static buffer it's our responsibility to free it when we are done +/// GFX->freePrimitiveBuffer( mPB ); +/// @endcode +/// This last bit takes place in the rendering function for the class +/// @code +/// // You need to set a vertex buffer as well, primitive buffers contain indexing +/// // information, not vertex information. This is so you could have, say, a static +/// // vertex buffer, and a dynamic primitive buffer. +/// +/// // This sets the primitive buffer to the static buffer we allocated in the constructor +/// GFX->setPrimitiveBuffer( mPB ); +/// +/// // Draw the first primitive contained in the set primitive buffer, our primitive buffer +/// // has only one primitive, so we could also technically call GFX->drawPrimitives(); and +/// // get the same result. +/// GFX->drawPrimitive( 0 ); +/// @endcode +/// If you need any more examples on how to use these buffers please see the rest of the engine. +/// @n +/// Primitive Buffers +/// @n +/// Primitive buffers wrap and extend the concept of index buffers. The purpose of a primitive +/// buffer is to let objects store all information they have to render their primitives in +/// a central place. Say that a shape is made up of triangle strips and triangle fans, it would +/// still have only one primitive buffer which contained primitive information for each strip +/// and fan. It could then draw itself with one call. +/// +/// TO BE FINISHED LATER +class GFXDevice +{ +private: + friend class GFXInit; + friend class GFXPrimitiveBufferHandle; + friend class GFXVertexBufferHandleBase; + friend class GFXTextureObject; + friend class GFXTexHandle; + friend class GFXVertexFormat; + friend class GFXTestFullscreenToggle; + friend class TestGFXTextureCube; + friend class TestGFXRenderTargetCube; + friend class TestGFXRenderTargetStack; + friend class GFXResource; + friend class LightMatInstance; // For stencil interface + + //-------------------------------------------------------------------------- + // Static GFX interface + //-------------------------------------------------------------------------- +public: + + enum GFXDeviceEventType + { + /// The device has been created, but not initialized + deCreate, + + /// The device has been initialized + deInit, + + /// The device is about to be destroyed. + deDestroy, + + /// The device has started rendering a frame + deStartOfFrame, + + /// The device is about to finish rendering a frame + deEndOfFrame, + }; + + typedef Signal DeviceEventSignal; + static DeviceEventSignal& getDeviceEventSignal(); + + static GFXDevice *get() { return smGFXDevice; } + + static void initConsole(); + static bool destroy(); + + static bool devicePresent() { return (smGFXDevice && smGFXDevice->getAdapterType() != NullDevice); } + +private: + /// @name Device management variables + /// @{ + static GFXDevice * smGFXDevice; ///< Global GFXDevice + + /// @} + + //-------------------------------------------------------------------------- + // Core GFX interface + //-------------------------------------------------------------------------- +private: + + /// Adapter for this device. + GFXAdapter mAdapter; + +protected: + /// List of valid video modes for this device. + Vector mVideoModes; + + /// The CardProfiler for this device. + GFXCardProfiler *mCardProfiler; + + /// Head of the resource list. + /// + /// @see GFXResource + GFXResource *mResourceListHead; + + /// Set once the device is active. + bool mCanCurrentlyRender; + + /// Set if we're in a mode where we want rendering to occur. + bool mAllowRender; + + /// This will allow querying to see if a device is initialized and ready to + /// have operations performed on it. + bool mInitialized; + + /// This is called before this, or any other device, is deleted in the global destroy() + /// method. It allows the device to clean up anything while everything is still valid. + virtual void preDestroy(); + + /// Set the adapter that this device is using. For use by GFXInit::createDevice only. + virtual void setAdapter(const GFXAdapter& adapter) { mAdapter = adapter; } + + /// Notify GFXDevice that we are initialized + virtual void deviceInited(); +public: + GFXDevice(); + virtual ~GFXDevice(); + + /// Initialize this GFXDevice, optionally specifying a platform window to + /// bind to. + virtual void init( const GFXVideoMode &mode, PlatformWindow *window = NULL ) = 0; + + /// Returns true if the scene has begun and its + /// safe to make rendering calls. + /// @see beginScene + /// @see endScene + bool canCurrentlyRender() const { return mCanCurrentlyRender; } + + void setAllowRender( bool render ) { mAllowRender = render; } + + inline bool allowRender() const { return mAllowRender; } + + GFXCardProfiler* getCardProfiler() const { return mCardProfiler; } + + /// Returns active graphics adapter type. + virtual GFXAdapterType getAdapterType()=0; + + /// Returns the Adapter that was used to create this device + virtual const GFXAdapter& getAdapter() { return mAdapter; } + + /// @} + + /// @name Debug Methods + /// @{ + + virtual void enterDebugEvent(ColorI color, const char *name) = 0; + virtual void leaveDebugEvent() = 0; + virtual void setDebugMarker(ColorI color, const char *name) = 0; + + /// @} + + /// @name Resource debug methods + /// @{ + + /// Lists how many of each GFX resource (e.g. textures, texture targets, shaders, etc.) GFX is aware of + /// @param unflaggedOnly If true, this method only counts unflagged resources + virtual void listResources(bool unflaggedOnly); + + /// Flags all resources GFX is currently aware of + virtual void flagCurrentResources(); + + /// Clears the flag on all resources GFX is currently aware of + virtual void clearResourceFlags(); + + /// Dumps a description of the specified resource types to the console + /// @param resNames A string of space separated class names (e.g. "GFXTextureObject GFXTextureTarget GFXShader") + /// to describe to the console + /// @param file A path to the file to write the descriptions to. If it is NULL or "", descriptions are + /// written to the console. + /// @param unflaggedOnly If true, this method only counts unflagged resources + /// @note resNames is case sensitive because there is no dStristr function. + virtual void describeResources(const char* resName, const char* file, bool unflaggedOnly); + + /// Returns the current GFXDeviceStatistics, stats are cleared every ::beginScene call. + GFXDeviceStatistics* getDeviceStatistics() { return &mDeviceStatistics; } +protected: + GFXDeviceStatistics mDeviceStatistics; + + /// This is a helper method for describeResourcesToFile. It walks through the + /// GFXResource list and sorts it by item type, putting the resources into the proper vector. + /// @see describeResources + virtual void fillResourceVectors(const char* resNames, bool unflaggedOnly, Vector &textureObjects, + Vector &textureTargets, Vector &windowTargets, Vector &vertexBuffers, + Vector &primitiveBuffers, Vector &fences, Vector &cubemaps, + Vector &shaders, Vector &stateblocks); +public: + + /// @} + + /// @name Video Mode Functions + /// @{ + /// Enumerates the supported video modes of the device + virtual void enumerateVideoModes() = 0; + + /// Returns the video mode list. + /// @see GFXVideoMode + const Vector* const getVideoModeList() const { return &mVideoModes; } + + /// Returns the first format from the list which meets all + /// the criteria of the texture profile and query options. + virtual GFXFormat selectSupportedFormat(GFXTextureProfile *profile, + const Vector &formats, bool texture, bool mustblend, bool mustfilter) = 0; + + /// @} + + //----------------------------------------------------------------------------- +protected: + + /// @name State tracking variables + /// @{ + + /// Set if ANY state is dirty, including matrices or primitive buffers. + bool mStateDirty; + + enum TexDirtyType + { + GFXTDT_Normal, + GFXTDT_Cube + }; + + GFXTexHandle mCurrentTexture[TEXTURE_STAGE_COUNT]; + GFXTexHandle mNewTexture[TEXTURE_STAGE_COUNT]; + GFXCubemapHandle mCurrentCubemap[TEXTURE_STAGE_COUNT]; + GFXCubemapHandle mNewCubemap[TEXTURE_STAGE_COUNT]; + + TexDirtyType mTexType[TEXTURE_STAGE_COUNT]; + bool mTextureDirty[TEXTURE_STAGE_COUNT]; + bool mTexturesDirty; + + // This maps a GFXStateBlockDesc hash value to a GFXStateBlockRef + typedef Map StateBlockMap; + StateBlockMap mCurrentStateBlocks; + + // This tracks whether or not our state block is dirty. + bool mStateBlockDirty; + GFXStateBlockRef mCurrentStateBlock; + GFXStateBlockRef mNewStateBlock; + + GFXShaderConstBuffer *mCurrentShaderConstBuffer; + + /// A global forced wireframe mode. + static bool smWireframe; + + /// The global vsync state. + static bool smDisableVSync; + + /// The forced shader model version if non-zero. + static F32 smForcedPixVersion; + + /// Disable all hardware occlusion queries causing + /// them to return only the visibile state. + static bool smDisableOcclusionQuery; + + /// @} + + /// @name Light Tracking + /// @{ + + GFXLightInfo mCurrentLight[LIGHT_STAGE_COUNT]; + bool mCurrentLightEnable[LIGHT_STAGE_COUNT]; + bool mLightDirty[LIGHT_STAGE_COUNT]; + bool mLightsDirty; + + ColorF mGlobalAmbientColor; + bool mGlobalAmbientColorDirty; + + /// @} + + /// @name Fixed function material tracking + /// @{ + + GFXLightMaterial mCurrentLightMaterial; + bool mLightMaterialDirty; + + /// @} + + /// @name Bitmap modulation and color stack + /// @{ + + /// + + /// @} + + /// @see getDeviceSwizzle32 + Swizzle *mDeviceSwizzle32; + + /// @see getDeviceSwizzle24 + Swizzle *mDeviceSwizzle24; + + + //----------------------------------------------------------------------------- + + /// @name Matrix managing variables + /// @{ + + /// + MatrixF mWorldMatrix[WORLD_STACK_MAX]; + bool mWorldMatrixDirty; + S32 mWorldStackSize; + + MatrixF mProjectionMatrix; + bool mProjectionMatrixDirty; + + MatrixF mViewMatrix; + bool mViewMatrixDirty; + + MatrixF mTextureMatrix[TEXTURE_STAGE_COUNT]; + bool mTextureMatrixDirty[TEXTURE_STAGE_COUNT]; + bool mTextureMatrixCheckDirty; + /// @} + + /// @name Current frustum planes + /// @{ + + /// + Frustum mFrustum; + + //----------------------------------------------------------------------------- + + /// @name Stateblock functions + /// @{ + + /// Called by GFXDevice to create a device specific stateblock + virtual GFXStateBlockRef createStateBlockInternal(const GFXStateBlockDesc& desc) = 0; + /// Called by GFXDevice to actually set a stateblock. + /// @param force If true, set all states + virtual void setStateBlockInternal(GFXStateBlock* block, bool force) = 0; + /// @} + + /// Called by base GFXDevice to actually set a const buffer + virtual void setShaderConstBufferInternal(GFXShaderConstBuffer* buffer) = 0; + + virtual void setTextureInternal(U32 textureUnit, const GFXTextureObject*texture) = 0; + + virtual void setLightInternal(U32 lightStage, const GFXLightInfo light, bool lightEnable) = 0; + virtual void setGlobalAmbientInternal(ColorF color) = 0; + virtual void setLightMaterialInternal(const GFXLightMaterial mat) = 0; + + virtual bool beginSceneInternal() = 0; + virtual void endSceneInternal() = 0; + + /// @name State Initialization. + /// @{ + + /// State initialization. This MUST BE CALLED in setVideoMode after the device + /// is created. + virtual void initStates() = 0; + /// @} + + //----------------------------------------------------------------------------- + + /// This function must be implemented differently per + /// API and it should set ONLY the current matrix. + /// For example, in OpenGL, there should be NO matrix stack + /// activity, all the stack stuff is managed in the GFX layer. + /// + /// OpenGL does not have separate world and + /// view matrices. It has ModelView which is world * view. + /// You must take this into consideration. + /// + /// @param mtype Which matrix to set, world/view/projection + /// @param mat Matrix to assign + virtual void setMatrix( GFXMatrixType mtype, const MatrixF &mat ) = 0; + + //----------------------------------------------------------------------------- +protected: + + + /// @name Buffer Allocation + /// These methods are implemented per-device and are called by the GFX layer + /// when a user calls an alloc + /// + /// @note Primitive Buffers are NOT implemented per device, they wrap index buffers + /// @{ + + /// This allocates a vertex buffer and returns a pointer to the allocated buffer. + /// This function should not be called directly - rather it should be used by + /// the GFXVertexBufferHandle class. + virtual GFXVertexBuffer *allocVertexBuffer( U32 numVerts, + const GFXVertexFormat *vertexFormat, + U32 vertSize, + GFXBufferType bufferType ) = 0; + + /// Called from GFXVertexFormat to allocate the hardware + /// specific vertex declaration for rendering. + virtual GFXVertexDecl* allocVertexDecl( const GFXVertexFormat *vertexFormat ) = 0; + + /// Sets the current vertex declaration on the device. + virtual void setVertexDecl( const GFXVertexDecl *decl ) = 0; + + /// Sets the vertex buffer on the device. + virtual void setVertexStream( U32 stream, GFXVertexBuffer *buffer ) = 0; + + /// Set the vertex stream frequency on the device. + virtual void setVertexStreamFrequency( U32 stream, U32 frequency ) = 0; + + /// The maximum number of supported vertex streams which + /// may be more than the device supports. + static const U32 VERTEX_STREAM_COUNT = 4; + + StrongRefPtr mCurrentVertexBuffer[VERTEX_STREAM_COUNT]; + bool mVertexBufferDirty[VERTEX_STREAM_COUNT]; + U32 mVertexBufferFrequency[VERTEX_STREAM_COUNT]; + bool mVertexBufferFrequencyDirty[VERTEX_STREAM_COUNT]; + + const GFXVertexDecl *mCurrVertexDecl; + bool mVertexDeclDirty; + + StrongRefPtr mCurrentPrimitiveBuffer; + bool mPrimitiveBufferDirty; + + /// This allocates a primitive buffer and returns a pointer to the allocated buffer. + /// A primitive buffer's type argument refers to the index data - the primitive data will + /// always be preserved from call to call. + /// + /// @note All index buffers use unsigned 16-bit indices. + virtual GFXPrimitiveBuffer *allocPrimitiveBuffer( U32 numIndices, + U32 numPrimitives, + GFXBufferType bufferType ) = 0; + + /// @} + + //--------------------------------------- + // SFX buffer + //--------------------------------------- +protected: + + GFXTexHandle mFrontBuffer[2]; + U32 mCurrentFrontBufferIdx; + + //--------------------------------------- + // Render target related + //--------------------------------------- + + /// A stack of previously active render targets. + Vector mRTStack; + + /// The current render target which may or may not + /// not be yet activated. + /// @see mRTDirty + GFXTargetRef mCurrentRT; + + /// This tracks a previously activated render target + /// which need to be deactivated. + GFXTargetRef mRTDeactivate; + + /// This is set when the current and/or deactivate render + /// targets have changed and the device need to update + /// its state on the next draw/clear. + bool mRTDirty; + + /// Updates the render targets and viewport in a device + /// specific manner when they are dirty. + virtual void _updateRenderTargets() = 0; + + /// The current viewport rect. + RectI mViewport; + + /// If true the viewport has been changed and + /// it must be updated on the next draw/clear. + bool mViewportDirty; + +public: + + /// @name Texture functions + /// @{ +protected: + GFXTextureManager * mTextureManager; + +public: + virtual GFXCubemap * createCubemap() = 0; + + inline GFXTextureManager *getTextureManager() + { + return mTextureManager; + } + + ///@} + + /// Swizzle to convert 32bpp bitmaps from RGBA to the native device format. + const Swizzle *getDeviceSwizzle32() const + { + return mDeviceSwizzle32; + } + + /// Swizzle to convert 24bpp bitmaps from RGB to the native device format. + const Swizzle *getDeviceSwizzle24() const + { + return mDeviceSwizzle24; + } + + /// @name Render Target functions + /// @{ + + /// Allocate a target for doing render to texture operations, with no + /// depth/stencil buffer. + virtual GFXTextureTarget *allocRenderToTextureTarget()=0; + + /// Allocate a target for a given window. + virtual GFXWindowTarget *allocWindowTarget(PlatformWindow *window)=0; + + /// Store the current render target to restore later. + void pushActiveRenderTarget(); + + /// Restore the previous render target. + void popActiveRenderTarget(); + + /// Assign a new active render target. + void setActiveRenderTarget( GFXTarget *target ); + + /// Returns the current active render target. + inline GFXTarget* getActiveRenderTarget() { return mCurrentRT; } + + ///@} + + /// @name Shader functions + /// @{ + virtual F32 getPixelShaderVersion() const = 0; + virtual void setPixelShaderVersion( F32 version ) = 0; + + /// Returns the number of texture samplers that can be used in a shader rendering pass + virtual U32 getNumSamplers() const = 0; + + /// Returns the number of simultaneous render targets supported by the device. + virtual U32 getNumRenderTargets() const = 0; + + virtual void setShader( GFXShader *shader ) {} + virtual void disableShaders() {} + + /// Set the buffer! (Actual set happens on the next draw call, just like textures, state blocks, etc) + void setShaderConstBuffer(GFXShaderConstBuffer* buffer); + + /// Creates a new empty shader which must be initialized + /// and deleted by the caller. + /// @see GFXShader::init + virtual GFXShader* createShader() = 0; + + /// @} + + //----------------------------------------------------------------------------- + + /// @name Rendering methods + /// @{ + + /// + virtual void clear( U32 flags, ColorI color, F32 z, U32 stencil ) = 0; + virtual bool beginScene(); + virtual void endScene(); + + virtual GFXTexHandle & getFrontBuffer(){ return mFrontBuffer[mCurrentFrontBufferIdx]; } + + void setPrimitiveBuffer( GFXPrimitiveBuffer *buffer ); + + /// Sets the vertex buffer. + /// + /// When setting the stream 0 vertex buffer it will automatically + /// set its associated vertex format as the active format. + /// + /// @param buffer The vertex buffer or NULL to clear the buffer. + /// @param stream The stream index of the vertex source stream to place the buffer. + /// @param frequency The stream frequency of the vertex buffer. + void setVertexBuffer( GFXVertexBuffer *buffer, U32 stream = 0, U32 frequency = 0 ); + + /// Sets the current vertex format. + /// + /// This should only be used if the vertex format of the stream 0 vertex + /// buffer is different from the one associated to it. Typically this + /// is used when rendering from multiple vertex streams. + /// + void setVertexFormat( const GFXVertexFormat *vertexFormat ); + + virtual void drawPrimitive( GFXPrimitiveType primType, U32 vertexStart, U32 primitiveCount ) = 0; + + /// The parameters to drawIndexedPrimitive are somewhat complicated. From a raw-data stand point + /// they evaluate to something like the following: + /// @code + /// U16 indicies[] = { 0, 1, 2, 1, 0, 0, 2 }; + /// Point3F verts[] = { Point3F( 0.0f, 0.0f, 0.0f ), Point3F( 0.0f, 1.0f, 0.0f ), Point3F( 0.0f, 0.0f, 1.0f ) }; + /// + /// GFX->drawIndexedPrimitive( GFXLineList, // Drawing a list of lines, each line is two verts + /// 0, // vertex 0 will be referenced so minIndex = 0 + /// 3, // 3 verticies will be used for this draw call + /// 1, // We want index 1 to be the first index used, so indicies 1-6 will be used + /// 3 // Drawing 3 LineList primitives, meaning 6 verts will be drawn + /// ); + /// + /// U16 *idxPtr = &indicies[1]; // 1 = startIndex, so the pointer is offset such that: + /// // idxPtr[0] is the same as indicies[1] + /// + /// U32 numVertsToDrawFromBuffer = primitiveCount * 2; // 2 verts define a line in the GFXLineList primitive type (6) + /// @endcode + /// + /// @param primType Type of primitive to draw + /// + /// @param startVertex This defines index zero. Its the offset from the start of + /// the vertex buffer to the first vertex. + /// + /// @param minIndex The smallest index into the vertex stream which will be used for this draw call. + /// This is a zero based index relative to startVertex. It is strictly a performance + /// hint for implementations. No vertex below minIndex will be referenced by this draw + /// call. For device implementors, this should _not_ be used to offset the vertex buffer, + /// or index buffer. + /// + /// @param numVerts The number of verticies which will be referenced in this draw call. This is not + /// the number of verticies which will be drawn. That is a function of 'primType' and + /// 'primitiveCount'. + /// + /// @param startIndex An offset from the start of the index buffer to specify where to start. If + /// 'idxBuffer' is a pointer to an array of integers, this could be written as + /// int *offsetIdx = idxBuffer + startIndex; + /// + /// @param primitiveCount The number of primitives of type 'primType' to draw. + /// + virtual void drawIndexedPrimitive( GFXPrimitiveType primType, + U32 startVertex, + U32 minIndex, + U32 numVerts, + U32 startIndex, + U32 primitiveCount ) = 0; + + void drawPrimitive( const GFXPrimitive &prim ); + void drawPrimitive( U32 primitiveIndex ); + void drawPrimitives(); + void drawPrimitiveBuffer( GFXPrimitiveBuffer *buffer ); + /// @} + + //----------------------------------------------------------------------------- + + /// Allocate a fence. The API specific implementation of GFXDevice is responsible + /// to make sure that the proper type is used. GFXGeneralFence should work in + /// all cases. + virtual GFXFence *createFence() = 0; + + /// Returns a hardware occlusion query object or NULL + /// if this device does not support them. + virtual GFXOcclusionQuery* createOcclusionQuery() { return NULL; } + + /// @name Light Settings + /// NONE of these should be overridden by API implementations + /// because of the state caching stuff. + /// @{ + void setLight(U32 stage, GFXLightInfo* light); + void setLightMaterial(GFXLightMaterial mat); + void setGlobalAmbientColor(ColorF color); + + /// @} + + /// @name Texture State Settings + /// NONE of these should be overridden by API implementations + /// because of the state caching stuff. + /// @{ + + /// + void setTexture(U32 stage, GFXTextureObject*texture); + void setCubeTexture( U32 stage, GFXCubemap *cubemap ); + inline GFXTextureObject* getCurrentTexture( U32 stage ) { return mCurrentTexture[stage]; } + + /// @} + + /// @name State Block Interface + /// @{ + + /// Creates a state block object based on the desc passed in. This object + /// represents an immutable state. + virtual GFXStateBlockRef createStateBlock( const GFXStateBlockDesc &desc ); + + /// Sets the current stateblock (actually activated in ::updateStates) + virtual void setStateBlock( GFXStateBlock *block ); + + /// This sets a stateblock directly from the description + /// structure. Its acceptable to use this for debug rendering + /// and other low frequency rendering tasks. + virtual void setStateBlockByDesc( const GFXStateBlockDesc &desc ); + + /// @} + + /// @name General state interface + /// @{ + /// Sets the dirty Render/Texture/Sampler states from the caching system + void updateStates(bool forceSetAll = false); + + /// Returns the forced global wireframe state. + static bool getWireframe() { return smWireframe; } + + /// Returns true if the occlusion query is disabled. + static bool getDisableOcclusionQuery() { return smDisableOcclusionQuery; } + /// @} + + //----------------------------------------------------------------------------- + + /// @name Matrix interface + /// @{ + + /// Sets the top of the world matrix stack + /// @param newWorld New world matrix to set + void setWorldMatrix( const MatrixF &newWorld ); + + /// Gets the matrix on the top of the world matrix stack + inline const MatrixF &getWorldMatrix() const { return mWorldMatrix[mWorldStackSize]; } + + /// Pushes the world matrix stack and copies the current top + /// matrix to the new top of the stack + void pushWorldMatrix(); + + /// Pops the world matrix stack + void popWorldMatrix(); + + /// Sets the projection matrix + /// @param newProj New projection matrix to set + void setProjectionMatrix( const MatrixF &newProj ); + + /// Gets the projection matrix + inline const MatrixF &getProjectionMatrix() const { return mProjectionMatrix; } + + /// Sets the view matrix + /// @param newView New view matrix to set + void setViewMatrix( const MatrixF &newView ); + + /// Gets the view matrix + inline const MatrixF &getViewMatrix() const { return mViewMatrix; } + + /// Multiplies the matrix at the top of the world matrix stack by a matrix + /// and replaces the top of the matrix stack with the result + /// @param mat Matrix to multiply + void multWorld( const MatrixF &mat ); + + /// Set texture matrix for a sampler + void setTextureMatrix( const U32 stage, const MatrixF &texMat ); + + /// Set an area of the target to render to. + void setViewport( const RectI &rect ); + + /// Get the current area of the target we will render to. + const RectI &getViewport() const { return mViewport; } + + virtual void setClipRect( const RectI &rect ) = 0; + virtual const RectI &getClipRect() const = 0; + + /// Set the projection frustum. + /// + /// @see MathUtils::makeFrustum() + virtual void setFrustum( F32 left, F32 right, + F32 bottom, F32 top, + F32 nearPlane, F32 farPlane, + bool bRotate = true); + + /// + virtual void setFrustum( const Frustum& frust, + bool bRotate = true ); + + /// Get the projection frustum. + void getFrustum( F32 *left, + F32 *right, + F32 *bottom, + F32 *top, + F32 *nearPlane, + F32 *farPlane, + bool *isOrtho ) const; + + /// Get the projection frustum. + const Frustum& getFrustum() const { return mFrustum; } + + /// This will construct and apply an orthographic projection matrix with the provided parameters + /// @param doRotate If set to true, the resulting matrix will be rotated PI/2 around the X axis + // for support in tsShapeInstance. You probably want to leave this as 'false'. + void setOrtho(F32 left, F32 right, F32 bottom, F32 top, F32 nearPlane, F32 farPlane, bool doRotate = false); + + /// Return true if the current frustum uses orthographic projection rather than perspective projection. + bool isFrustumOrtho() const { return mFrustum.isOrtho(); } + + /// @} + + /// Returns the scale for converting world space + /// units to screen space units... aka pixels. + /// + /// This is the true scale which is best used for GUI + /// drawing. For doing lod calculations you should be + /// using the functions in SceneState which is adjusted + /// for special cases like shadows and reflections. + /// + /// @see SceneState::getWorldToScreenScale() + /// @see SceneState::projectRadius() + /// + Point2F getWorldToScreenScale() const; + +public: + enum GenericShaderType + { + GSColor = 0, + GSTexture, + GSModColorTexture, + GSAddColorTexture, + GSTargetRestore, + GS_COUNT + }; + + /// This is a helper function to set a default shader for rendering GUI elements + /// on systems which do not support fixed-function operations as well as for + /// things which need just generic position/texture/color shaders + /// + /// @param type Type of generic shader, add your own if you need + virtual void setupGenericShaders( GenericShaderType type = GSColor ) {}; + + /// Get the fill convention for this device + virtual F32 getFillConventionOffset() const = 0; + + virtual U32 getMaxDynamicVerts() = 0; + virtual U32 getMaxDynamicIndices() = 0; + + virtual void doParanoidStateCheck(){}; + + /// Get access to this device's drawing utility class. + GFXDrawUtil *getDrawUtil(); + +#ifndef TORQUE_SHIPPING + /// This is a method designed for debugging. It will allow you to dump the states + /// in the render manager out to a file so that it can be diffed and examined. + void dumpStates( const char *fileName ) const; +#else + void dumpStates( const char *fileName ) const {}; +#endif + protected: + GFXDrawUtil *mDrawer; +}; + +//----------------------------------------------------------------------------- +// Matrix interface + +inline void GFXDevice::setWorldMatrix( const MatrixF &newWorld ) +{ + mWorldMatrixDirty = true; + mStateDirty = true; + mWorldMatrix[mWorldStackSize] = newWorld; +} + +inline void GFXDevice::pushWorldMatrix() +{ + mWorldMatrixDirty = true; + mStateDirty = true; + mWorldStackSize++; + AssertFatal( mWorldStackSize < WORLD_STACK_MAX, "GFX: Exceeded world matrix stack size" ); + mWorldMatrix[mWorldStackSize] = mWorldMatrix[mWorldStackSize - 1]; +} + +inline void GFXDevice::popWorldMatrix() +{ + mWorldMatrixDirty = true; + mStateDirty = true; + mWorldStackSize--; + AssertFatal( mWorldStackSize >= 0, "GFX: Negative WorldStackSize!" ); +} + +inline void GFXDevice::multWorld( const MatrixF &mat ) +{ + mWorldMatrixDirty = true; + mStateDirty = true; + mWorldMatrix[mWorldStackSize].mul(mat); +} + +inline void GFXDevice::setProjectionMatrix( const MatrixF &newProj ) +{ + mProjectionMatrixDirty = true; + mStateDirty = true; + mProjectionMatrix = newProj; +} + +inline void GFXDevice::setViewMatrix( const MatrixF &newView ) +{ + mStateDirty = true; + mViewMatrixDirty = true; + mViewMatrix = newView; +} + +inline void GFXDevice::setTextureMatrix( const U32 stage, const MatrixF &texMat ) +{ + AssertFatal( stage < TEXTURE_STAGE_COUNT, "Out of range texture sampler" ); + mStateDirty = true; + mTextureMatrixDirty[stage] = true; + mTextureMatrix[stage] = texMat; + mTextureMatrixCheckDirty = true; +} + +//----------------------------------------------------------------------------- +// Buffer management + +inline void GFXDevice::setVertexBuffer( GFXVertexBuffer *buffer, U32 stream, U32 frequency ) +{ + AssertFatal( stream < VERTEX_STREAM_COUNT, "GFXDevice::setVertexBuffer - Bad stream index!" ); + + if ( buffer && stream == 0 ) + setVertexFormat( &buffer->mVertexFormat ); + + if ( buffer != mCurrentVertexBuffer[stream] ) + { + mCurrentVertexBuffer[stream] = buffer; + mVertexBufferDirty[stream] = true; + mStateDirty = true; + } + + if ( mVertexBufferFrequency[stream] != frequency ) + { + mVertexBufferFrequency[stream] = frequency; + mVertexBufferFrequencyDirty[stream] = true; + mStateDirty = true; + } +} + +inline void GFXDevice::setVertexFormat( const GFXVertexFormat *vertexFormat ) +{ + if ( vertexFormat->getDecl() == mCurrVertexDecl ) + return; + + mCurrVertexDecl = vertexFormat->getDecl(); + mVertexDeclDirty = true; + mStateDirty = true; +} + + +#endif // _GFXDEVICE_H_ diff --git a/Engine/source/gfx/gfxDeviceStatistics.cpp b/Engine/source/gfx/gfxDeviceStatistics.cpp new file mode 100644 index 000000000..0e8e9d0bb --- /dev/null +++ b/Engine/source/gfx/gfxDeviceStatistics.cpp @@ -0,0 +1,74 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/gfxDeviceStatistics.h" +#include "platform/platform.h" +#include "console/console.h" + +GFXDeviceStatistics::GFXDeviceStatistics() +{ + clear(); +} + +void GFXDeviceStatistics::setPrefix(const String& prefix) +{ + // This is a bit silly, but we don't want to construct these + // strings every frame. + vnPolyCount = prefix + "polyCount"; + vnDrawCalls = prefix + "drawCalls"; + vnRenderTargetChanges = prefix + "renderTargetChanges"; +} + +/// Clear stats +void GFXDeviceStatistics::clear() +{ + mPolyCount = 0; + mDrawCalls = 0; + mRenderTargetChanges = 0; +} + +/// Copy from source (should just be a memcpy, but that may change later) used in +/// conjunction with end to get a subset of statistics. For example, statistics +/// for a particular render bin. +void GFXDeviceStatistics::start(GFXDeviceStatistics * source) +{ + mPolyCount = source->mPolyCount; + mDrawCalls = source->mDrawCalls; + mRenderTargetChanges = source->mRenderTargetChanges; +} + +/// Used with start to get a subset of stats on a device. Basically will do +/// this->mPolyCount = source->mPolyCount - this->mPolyCount. (Fancy!) +void GFXDeviceStatistics::end(GFXDeviceStatistics * source) +{ + mPolyCount = source->mPolyCount - mPolyCount; + mDrawCalls = source->mDrawCalls - mDrawCalls; + mRenderTargetChanges = source->mRenderTargetChanges - mRenderTargetChanges; +} + +/// Exports the stats to the console +void GFXDeviceStatistics::exportToConsole() +{ + Con::setIntVariable(vnPolyCount, mPolyCount); + Con::setIntVariable(vnDrawCalls, mDrawCalls); + Con::setIntVariable(vnRenderTargetChanges, mRenderTargetChanges); +} \ No newline at end of file diff --git a/Engine/source/gfx/gfxDeviceStatistics.h b/Engine/source/gfx/gfxDeviceStatistics.h new file mode 100644 index 000000000..ab82f95ec --- /dev/null +++ b/Engine/source/gfx/gfxDeviceStatistics.h @@ -0,0 +1,62 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _GFXDEVICESTATISTICS_H_ +#define _GFXDEVICESTATISTICS_H_ + +#ifndef _TORQUE_STRING_H_ +#include "core/util/str.h" +#endif + +// A class that hold a simple set of device stats. +class GFXDeviceStatistics +{ +public: + // Actual stats + S32 mPolyCount; + S32 mDrawCalls; + S32 mRenderTargetChanges; + + GFXDeviceStatistics(); + + void setPrefix(const String& prefix); + + /// Clear stats + void clear(); + + /// Copy from source (should just be a memcpy, but that may change later) used in + /// conjunction with end to get a subset of statistics. For example, statistics + /// for a particular render bin. + void start(GFXDeviceStatistics * source); + + /// Used with start to get a subset of stats on a device. Basically will do + /// this->mPolyCount = source->mPolyCount - this->mPolyCount. (Fancy!) + void end(GFXDeviceStatistics * source); + + /// Exports the stats to the console + void exportToConsole(); +private: + String vnPolyCount; + String vnDrawCalls; + String vnRenderTargetChanges; +}; + +#endif \ No newline at end of file diff --git a/Engine/source/gfx/gfxDrawUtil.cpp b/Engine/source/gfx/gfxDrawUtil.cpp new file mode 100644 index 000000000..d7e4b921e --- /dev/null +++ b/Engine/source/gfx/gfxDrawUtil.cpp @@ -0,0 +1,1577 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/gfxDrawUtil.h" + +#include "core/frameAllocator.h" +#include "core/strings/stringFunctions.h" +#include "core/strings/unicode.h" +#include "math/util/frustum.h" +#include "math/util/sphereMesh.h" +#include "math/mathUtils.h" +#include "gfx/gfxFontRenderBatcher.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/gfxPrimitiveBuffer.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxDebugEvent.h" + +#include "math/mPolyhedron.impl.h" + + +GFXDrawUtil::GFXDrawUtil( GFXDevice * d) +{ + mDevice = d; + mBitmapModulation.set(0xFF, 0xFF, 0xFF, 0xFF); + mTextAnchorColor.set(0xFF, 0xFF, 0xFF, 0xFF); + mFontRenderBatcher = new FontRenderBatcher(); + + _setupStateBlocks(); +} + +GFXDrawUtil::~GFXDrawUtil() +{ + delete mFontRenderBatcher; +} + +void GFXDrawUtil::_setupStateBlocks() +{ + // DrawBitmapStretchSR + GFXStateBlockDesc bitmapStretchSR; + bitmapStretchSR.setCullMode(GFXCullNone); + bitmapStretchSR.setZReadWrite(false); + bitmapStretchSR.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha); + bitmapStretchSR.samplersDefined = true; + + // Linear: Create wrap SB + bitmapStretchSR.samplers[0] = GFXSamplerStateDesc::getWrapLinear(); + mBitmapStretchWrapLinearSB = mDevice->createStateBlock(bitmapStretchSR); + + // Linear: Create clamp SB + bitmapStretchSR.samplers[0] = GFXSamplerStateDesc::getClampLinear(); + mBitmapStretchLinearSB = mDevice->createStateBlock(bitmapStretchSR); + + // Point: + bitmapStretchSR.samplers[0].minFilter = GFXTextureFilterPoint; + bitmapStretchSR.samplers[0].mipFilter = GFXTextureFilterPoint; + bitmapStretchSR.samplers[0].magFilter = GFXTextureFilterPoint; + + // Point: Create clamp SB, last created clamped so no work required here + mBitmapStretchSB = mDevice->createStateBlock(bitmapStretchSR); + + // Point: Create wrap SB, have to do this manually because getWrapLinear doesn't + bitmapStretchSR.samplers[0].addressModeU = GFXAddressWrap; + bitmapStretchSR.samplers[0].addressModeV = GFXAddressWrap; + bitmapStretchSR.samplers[0].addressModeW = GFXAddressWrap; + mBitmapStretchWrapSB = mDevice->createStateBlock(bitmapStretchSR); + + GFXStateBlockDesc rectFill; + rectFill.setCullMode(GFXCullNone); + rectFill.setZReadWrite(false); + rectFill.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha); + mRectFillSB = mDevice->createStateBlock(rectFill); +} + +//----------------------------------------------------------------------------- +// Color Modulation +//----------------------------------------------------------------------------- +void GFXDrawUtil::setBitmapModulation( const ColorI &modColor ) +{ + mBitmapModulation = modColor; +} + +void GFXDrawUtil::clearBitmapModulation() +{ + mBitmapModulation.set( 255, 255, 255, 255 ); +} + +void GFXDrawUtil::getBitmapModulation( ColorI *color ) +{ + mBitmapModulation.getColor( color ); +} + +void GFXDrawUtil::setTextAnchorColor( const ColorI &ancColor ) +{ + mTextAnchorColor = ancColor; +} + +//----------------------------------------------------------------------------- +// Draw Text +//----------------------------------------------------------------------------- +U32 GFXDrawUtil::drawText( GFont *font, const Point2I &ptDraw, const UTF16 *in_string, + const ColorI *colorTable, const U32 maxColorIndex, F32 rot ) +{ + return drawTextN( font, ptDraw, in_string, dStrlen(in_string), colorTable, maxColorIndex, rot ); +} + +U32 GFXDrawUtil::drawText( GFont *font, const Point2I &ptDraw, const UTF8 *in_string, + const ColorI *colorTable, const U32 maxColorIndex, F32 rot ) +{ + return drawTextN( font, ptDraw, in_string, dStrlen(in_string), colorTable, maxColorIndex, rot ); +} + +U32 GFXDrawUtil::drawText( GFont *font, const Point2F &ptDraw, const UTF8 *in_string, const ColorI *colorTable /*= NULL*/, const U32 maxColorIndex /*= 9*/, F32 rot /*= 0.f */ ) +{ + return drawText(font,Point2I((S32)ptDraw.x,(S32)ptDraw.y),in_string,colorTable,maxColorIndex,rot); +} + +U32 GFXDrawUtil::drawText( GFont *font, const Point2F &ptDraw, const UTF16 *in_string, const ColorI *colorTable /*= NULL*/, const U32 maxColorIndex /*= 9*/, F32 rot /*= 0.f */ ) +{ + return drawText(font,Point2I((S32)ptDraw.x,(S32)ptDraw.y),in_string,colorTable,maxColorIndex,rot); +} + +U32 GFXDrawUtil::drawTextN( GFont *font, const Point2I &ptDraw, const UTF8 *in_string, U32 n, + const ColorI *colorTable, const U32 maxColorIndex, F32 rot ) +{ + // return on zero length strings + if( n == 0 ) + return ptDraw.x; + + // Convert to UTF16 temporarily. + n++; // space for null terminator + FrameTemp ubuf( n * sizeof(UTF16) ); + convertUTF8toUTF16(in_string, ubuf, n); + + return drawTextN( font, ptDraw, ubuf, n, colorTable, maxColorIndex, rot ); +} + +U32 GFXDrawUtil::drawTextN( GFont *font, const Point2I &ptDraw, const UTF16 *in_string, + U32 n, const ColorI *colorTable, const U32 maxColorIndex, F32 rot ) +{ + // return on zero length strings + if( n == 0 ) + return ptDraw.x; + + // If it's over about 4000 verts we want to break it up + if( n > 666 ) + { + U32 left = drawTextN(font, ptDraw, in_string, 666, colorTable, maxColorIndex, rot); + + Point2I newDrawPt(left, ptDraw.y); + const UTF16* str = (const UTF16*)in_string; + + return drawTextN(font, newDrawPt, &(str[666]), n - 666, colorTable, maxColorIndex, rot); + } + + PROFILE_START(GFXDevice_drawTextN); + + const PlatformFont::CharInfo *tabci = NULL; + + S32 ptX = 0; + + // Queue everything for render. + mFontRenderBatcher->init(font, n); + + U32 i; + UTF16 c; + for(i = 0, c = in_string[i]; in_string[i] && i < n; i++, c = in_string[i]) + { + switch(c) + { + // We have to do a little dance here since \t = 0x9, \n = 0xa, and \r = 0xd + case 1: case 2: case 3: case 4: case 5: case 6: case 7: + case 11: case 12: + case 14: + { + // Color code + if (colorTable) + { + static U8 remap[15] = + { + 0x0, // 0 special null terminator + 0x0, // 1 ascii start-of-heading?? + 0x1, + 0x2, + 0x3, + 0x4, + 0x5, + 0x6, + 0x0, // 8 special backspace + 0x0, // 9 special tab + 0x0, // a special \n + 0x7, + 0x8, + 0x0, // a special \r + 0x9 + }; + + U8 remapped = remap[c]; + + // Ignore if the color is greater than the specified max index: + if ( remapped <= maxColorIndex ) + { + const ColorI &clr = colorTable[remapped]; + mBitmapModulation = clr; + } + } + + // And skip rendering this character. + continue; + } + + // reset color? + case 15: + { + mBitmapModulation = mTextAnchorColor; + + // And skip rendering this character. + continue; + } + + // push color: + case 16: + { + mTextAnchorColor = mBitmapModulation; + + // And skip rendering this character. + continue; + } + + // pop color: + case 17: + { + mBitmapModulation = mTextAnchorColor; + + // And skip rendering this character. + continue; + } + + // Tab character + case dT('\t'): + { + if ( tabci == NULL ) + tabci = &(font->getCharInfo( dT(' ') )); + + const U32 fontTabIncrement = tabci->xIncrement * GFont::TabWidthInSpaces; + + ptX += fontTabIncrement; + + // And skip rendering this character. + continue; + } + + // Don't draw invalid characters. + default: + { + if( !font->isValidChar( c ) ) + continue; + } + } + + // Queue char for rendering.. + mFontRenderBatcher->queueChar(c, ptX, mBitmapModulation); + } + + + mFontRenderBatcher->render(rot, Point2F((F32)ptDraw.x, (F32)ptDraw.y)); + + PROFILE_END(); + + return ptX + ptDraw.x; +} + +U32 GFXDrawUtil::drawTextN( GFont *font, const Point2F &ptDraw, const UTF8 *in_string, U32 n, const ColorI *colorTable /*= NULL*/, const U32 maxColorIndex /*= 9*/, F32 rot /*= 0.f */ ) +{ + return drawTextN(font,Point2I((S32)ptDraw.x,(S32)ptDraw.y),in_string,n,colorTable,maxColorIndex,rot); +} + +U32 GFXDrawUtil::drawTextN( GFont *font, const Point2F &ptDraw, const UTF16 *in_string, U32 n, const ColorI *colorTable /*= NULL*/, const U32 maxColorIndex /*= 9*/, F32 rot /*= 0.f */ ) +{ + return drawTextN(font,Point2I((S32)ptDraw.x,(S32)ptDraw.y),in_string,n,colorTable,maxColorIndex,rot); +} + +//----------------------------------------------------------------------------- +// Draw Bitmaps +//----------------------------------------------------------------------------- +void GFXDrawUtil::drawBitmap( GFXTextureObject* texture, const Point2I &in_rAt, const GFXBitmapFlip in_flip, const GFXTextureFilterType filter , bool in_wrap /*= true*/ ) +{ + drawBitmap(texture,Point2F((F32)in_rAt.x,(F32)in_rAt.y),in_flip,filter,in_wrap); +} + +void GFXDrawUtil::drawBitmapStretch( GFXTextureObject* texture, const RectI &dstRect, const GFXBitmapFlip in_flip, const GFXTextureFilterType filter , bool in_wrap /*= true*/ ) +{ + drawBitmapStretch(texture,RectF((F32)dstRect.point.x,(F32)dstRect.point.y,(F32)dstRect.extent.x,(F32)dstRect.extent.y),in_flip,filter,in_wrap); +} + +void GFXDrawUtil::drawBitmapSR( GFXTextureObject* texture, const Point2I &in_rAt, const RectI &srcRect, const GFXBitmapFlip in_flip, const GFXTextureFilterType filter , bool in_wrap /*= true*/ ) +{ + drawBitmapSR(texture,Point2F((F32)in_rAt.x,(F32)in_rAt.y),RectF((F32)srcRect.point.x,(F32)srcRect.point.y,(F32)srcRect.extent.x,(F32)srcRect.extent.y),in_flip,filter,in_wrap); +} + +void GFXDrawUtil::drawBitmapStretchSR( GFXTextureObject *texture, const RectI &dstRect, const RectI &srcRect, const GFXBitmapFlip in_flip, const GFXTextureFilterType filter , bool in_wrap /*= true*/ ) +{ + RectF dstRectF = RectF((F32)dstRect.point.x,(F32)dstRect.point.y,(F32)dstRect.extent.x,(F32)dstRect.extent.y); + RectF srcRectF = RectF((F32)srcRect.point.x,(F32)srcRect.point.y,(F32)srcRect.extent.x,(F32)srcRect.extent.y); + drawBitmapStretchSR(texture,dstRectF,srcRectF,in_flip,filter,in_wrap); +} + +void GFXDrawUtil::drawBitmap( GFXTextureObject*texture, const Point2F &in_rAt, const GFXBitmapFlip in_flip /*= GFXBitmapFlip_None*/, const GFXTextureFilterType filter /*= GFXTextureFilterPoint */ , bool in_wrap /*= true*/ ) +{ + AssertFatal( texture != 0, "No texture specified for drawBitmap()" ); + + RectI subRegion( 0, 0, texture->mBitmapSize.x, texture->mBitmapSize.y ); + RectI stretch( in_rAt.x, in_rAt.y, texture->mBitmapSize.x, texture->mBitmapSize.y ); + drawBitmapStretchSR( texture, stretch, subRegion, in_flip, filter, in_wrap ); +} + +void GFXDrawUtil::drawBitmapStretch( GFXTextureObject*texture, const RectF &dstRect, const GFXBitmapFlip in_flip /*= GFXBitmapFlip_None*/, const GFXTextureFilterType filter /*= GFXTextureFilterPoint */ , bool in_wrap /*= true*/ ) +{ + AssertFatal( texture != 0, "No texture specified for drawBitmapStretch()" ); + + RectF subRegion( 0.f, 0.f, (F32)texture->mBitmapSize.x, (F32)texture->mBitmapSize.y ); + drawBitmapStretchSR( texture, dstRect, subRegion, in_flip, filter, in_wrap ); +} + +void GFXDrawUtil::drawBitmapSR( GFXTextureObject*texture, const Point2F &in_rAt, const RectF &srcRect, const GFXBitmapFlip in_flip /*= GFXBitmapFlip_None*/, const GFXTextureFilterType filter /*= GFXTextureFilterPoint */ , bool in_wrap /*= true*/ ) +{ + AssertFatal( texture != 0, "No texture specified for drawBitmapSR()" ); + + RectF stretch( in_rAt.x, in_rAt.y, srcRect.len_x(), srcRect.len_y() ); + drawBitmapStretchSR( texture, stretch, srcRect, in_flip, filter, in_wrap ); +} + +void GFXDrawUtil::drawBitmapStretchSR( GFXTextureObject* texture, const RectF &dstRect, const RectF &srcRect, const GFXBitmapFlip in_flip /*= GFXBitmapFlip_None*/, const GFXTextureFilterType filter /*= GFXTextureFilterPoint */ , bool in_wrap /*= true*/ ) +{ + // Sanity if no texture is specified. + if(!texture) + return; + + GFXVertexBufferHandle verts(mDevice, 4, GFXBufferTypeVolatile ); + verts.lock(); + + F32 texLeft = (srcRect.point.x) / (texture->mTextureSize.x); + F32 texRight = (srcRect.point.x + srcRect.extent.x) / (texture->mTextureSize.x); + F32 texTop = (srcRect.point.y) / (texture->mTextureSize.y); + F32 texBottom = (srcRect.point.y + srcRect.extent.y) / (texture->mTextureSize.y); + + F32 screenLeft = dstRect.point.x; + F32 screenRight = (dstRect.point.x + dstRect.extent.x); + F32 screenTop = dstRect.point.y; + F32 screenBottom = (dstRect.point.y + dstRect.extent.y); + + if( in_flip & GFXBitmapFlip_X ) + { + F32 temp = texLeft; + texLeft = texRight; + texRight = temp; + } + if( in_flip & GFXBitmapFlip_Y ) + { + F32 temp = texTop; + texTop = texBottom; + texBottom = temp; + } + + const F32 fillConv = mDevice->getFillConventionOffset(); + verts[0].point.set( screenLeft - fillConv, screenTop - fillConv, 0.f ); + verts[1].point.set( screenRight - fillConv, screenTop - fillConv, 0.f ); + verts[2].point.set( screenLeft - fillConv, screenBottom - fillConv, 0.f ); + verts[3].point.set( screenRight - fillConv, screenBottom - fillConv, 0.f ); + + verts[0].color = verts[1].color = verts[2].color = verts[3].color = mBitmapModulation; + + verts[0].texCoord.set( texLeft, texTop ); + verts[1].texCoord.set( texRight, texTop ); + verts[2].texCoord.set( texLeft, texBottom ); + verts[3].texCoord.set( texRight, texBottom ); + + verts.unlock(); + + mDevice->setVertexBuffer( verts ); + + switch (filter) + { + case GFXTextureFilterPoint : + mDevice->setStateBlock(in_wrap ? mBitmapStretchWrapSB : mBitmapStretchSB); + break; + case GFXTextureFilterLinear : + mDevice->setStateBlock(in_wrap ? mBitmapStretchWrapLinearSB : mBitmapStretchLinearSB); + break; + default: + AssertFatal(false, "No GFXDrawUtil state block defined for this filter type!"); + mDevice->setStateBlock(mBitmapStretchSB); + break; + } + mDevice->setTexture( 0, texture ); + mDevice->setupGenericShaders( GFXDevice::GSModColorTexture ); + + mDevice->drawPrimitive( GFXTriangleStrip, 0, 2 ); +} + +//----------------------------------------------------------------------------- +// Draw Rectangle +//----------------------------------------------------------------------------- +void GFXDrawUtil::drawRect( const Point2I &upperLeft, const Point2I &lowerRight, const ColorI &color ) +{ + drawRect( Point2F((F32)upperLeft.x,(F32)upperLeft.y),Point2F((F32)lowerRight.x,(F32)lowerRight.y),color); +} + +void GFXDrawUtil::drawRect( const RectI &rect, const ColorI &color ) +{ + drawRect( rect.point, Point2I(rect.point.x + rect.extent.x - 1, rect.point.y + rect.extent.y - 1), color ); +} + +void GFXDrawUtil::drawRect( const RectF &rect, const ColorI &color ) +{ + drawRect( rect.point, Point2F(rect.point.x + rect.extent.x - 1, rect.point.y + rect.extent.y - 1), color ); +} + +void GFXDrawUtil::drawRect( const Point2F &upperLeft, const Point2F &lowerRight, const ColorI &color ) +{ + // + // Convert Box a----------x + // | | + // x----------b + // + // Into Triangle-Strip Outline + // v0-----------v2 + // | a x | + // | v1-----v3 | + // | | | | + // | v7-----v5 | + // | x b | + // v6-----------v4 + // + + // NorthWest and NorthEast facing offset vectors + // These adjust the thickness of the line, it'd be neat if one day + // they were passed in as arguments. + Point2F nw(-0.5f,-0.5f); /* \ */ + Point2F ne(0.5f,-0.5f); /* / */ + + GFXVertexBufferHandle verts (mDevice, 10, GFXBufferTypeVolatile ); + verts.lock(); + + F32 ulOffset = 0.5f - mDevice->getFillConventionOffset(); + + verts[0].point.set( upperLeft.x + ulOffset + nw.x, upperLeft.y + ulOffset + nw.y, 0.0f ); + verts[1].point.set( upperLeft.x + ulOffset - nw.x, upperLeft.y + ulOffset - nw.y, 0.0f ); + verts[2].point.set( lowerRight.x + ne.x, upperLeft.y + ulOffset + ne.y, 0.0f ); + verts[3].point.set( lowerRight.x - ne.x, upperLeft.y + ulOffset - ne.y, 0.0f ); + verts[4].point.set( lowerRight.x - nw.x, lowerRight.y - nw.y, 0.0f ); + verts[5].point.set( lowerRight.x + nw.x, lowerRight.y + nw.y, 0.0f ); + verts[6].point.set( upperLeft.x + ulOffset - ne.x, lowerRight.y - ne.y, 0.0f ); + verts[7].point.set( upperLeft.x + ulOffset + ne.x, lowerRight.y + ne.y, 0.0f ); + verts[8].point.set( upperLeft.x + ulOffset + nw.x, upperLeft.y + ulOffset + nw.y, 0.0f ); // same as 0 + verts[9].point.set( upperLeft.x + ulOffset - nw.x, upperLeft.y + ulOffset - nw.y, 0.0f ); // same as 1 + + for (int i=0; i<10; i++) + verts[i].color = color; + + verts.unlock(); + mDevice->setVertexBuffer( verts ); + + mDevice->setStateBlock(mRectFillSB); + mDevice->setupGenericShaders(); + mDevice->drawPrimitive( GFXTriangleStrip, 0, 8 ); +} + +//----------------------------------------------------------------------------- +// Draw Rectangle Fill +//----------------------------------------------------------------------------- +void GFXDrawUtil::drawRectFill( const RectF &rect, const ColorI &color ) +{ + drawRectFill(rect.point, Point2F(rect.extent.x + rect.point.x - 1, rect.extent.y + rect.point.y - 1), color ); +} + +void GFXDrawUtil::drawRectFill( const Point2I &upperLeft, const Point2I &lowerRight, const ColorI &color ) +{ + drawRectFill(Point2F((F32)upperLeft.x, (F32)upperLeft.y), Point2F((F32)lowerRight.x, (F32)lowerRight.y), color); +} + +void GFXDrawUtil::drawRectFill( const RectI &rect, const ColorI &color ) +{ + drawRectFill(rect.point, Point2I(rect.extent.x + rect.point.x - 1, rect.extent.y + rect.point.y - 1), color ); +} + +void GFXDrawUtil::drawRectFill( const Point2F &upperLeft, const Point2F &lowerRight, const ColorI &color ) +{ + // + // Convert Box a----------x + // | | + // x----------b + // Into Quad + // v0---------v1 + // | a x | + // | | + // | x b | + // v2---------v3 + // + + // NorthWest and NorthEast facing offset vectors + Point2F nw(-0.5,-0.5); /* \ */ + Point2F ne(0.5,-0.5); /* / */ + + GFXVertexBufferHandle verts(mDevice, 4, GFXBufferTypeVolatile); + verts.lock(); + + F32 ulOffset = 0.5f - mDevice->getFillConventionOffset(); + + verts[0].point.set( upperLeft.x+nw.x+ulOffset, upperLeft.y+nw.y+ulOffset, 0.0f ); + verts[1].point.set( lowerRight.x+ne.x, upperLeft.y+ne.y+ulOffset, 0.0f ); + verts[2].point.set( upperLeft.x-ne.x+ulOffset, lowerRight.y-ne.y, 0.0f ); + verts[3].point.set( lowerRight.x-nw.x, lowerRight.y-nw.y, 0.0f ); + + for (int i=0; i<4; i++) + verts[i].color = color; + + verts.unlock(); + + mDevice->setStateBlock(mRectFillSB); + mDevice->setVertexBuffer( verts ); + mDevice->setupGenericShaders(); + mDevice->drawPrimitive( GFXTriangleStrip, 0, 2 ); +} + +void GFXDrawUtil::draw2DSquare( const Point2F &screenPoint, F32 width, F32 spinAngle ) +{ + width *= 0.5; + + Point3F offset( screenPoint.x, screenPoint.y, 0.0 ); + + GFXVertexBufferHandle verts( mDevice, 4, GFXBufferTypeVolatile ); + verts.lock(); + + verts[0].point.set( -width, -width, 0.0f ); + verts[1].point.set( -width, width, 0.0f ); + verts[2].point.set( width, -width, 0.0f ); + verts[3].point.set( width, width, 0.0f ); + + verts[0].color = verts[1].color = verts[2].color = verts[3].color = mBitmapModulation; + + if(spinAngle != 0.f) + { + MatrixF rotMatrix( EulerF( 0.0, 0.0, spinAngle ) ); + + for( S32 i = 0; i < 4; i++ ) + { + rotMatrix.mulP( verts[i].point ); + verts[i].point += offset; + } + } + + verts.unlock(); + mDevice->setVertexBuffer( verts ); + + mDevice->setStateBlock(mRectFillSB); + mDevice->setupGenericShaders(); + + mDevice->drawPrimitive( GFXTriangleStrip, 0, 2 ); +} + +//----------------------------------------------------------------------------- +// Draw Line +//----------------------------------------------------------------------------- +void GFXDrawUtil::drawLine( const Point3F &startPt, const Point3F &endPt, const ColorI &color ) +{ + drawLine( startPt.x, startPt.y, startPt.z, endPt.x, endPt.y, endPt.z, color ); +} + +void GFXDrawUtil::drawLine( const Point2F &startPt, const Point2F &endPt, const ColorI &color ) +{ + drawLine( startPt.x, startPt.y, 0.0f, endPt.x, endPt.y, 0.0f, color ); +} + +void GFXDrawUtil::drawLine( const Point2I &startPt, const Point2I &endPt, const ColorI &color ) +{ + drawLine( startPt.x, startPt.y, 0.0f, endPt.x, endPt.y, 0.0f, color ); +} + +void GFXDrawUtil::drawLine( F32 x1, F32 y1, F32 x2, F32 y2, const ColorI &color ) +{ + drawLine( x1, y1, 0.0f, x2, y2, 0.0f, color ); +} + +void GFXDrawUtil::drawLine( F32 x1, F32 y1, F32 z1, F32 x2, F32 y2, F32 z2, const ColorI &color ) +{ + GFXVertexBufferHandle verts( mDevice, 2, GFXBufferTypeVolatile ); + verts.lock(); + + verts[0].point.set( x1, y1, z1 ); + verts[1].point.set( x2, y2, z2 ); + + verts[0].color = color; + verts[1].color = color; + + verts.unlock(); + + mDevice->setVertexBuffer( verts ); + mDevice->setStateBlock( mRectFillSB ); + mDevice->drawPrimitive( GFXLineList, 0, 1 ); +} + +//----------------------------------------------------------------------------- +// 3D World Draw Misc +//----------------------------------------------------------------------------- + +static SphereMesh gSphere; + +void GFXDrawUtil::drawSphere( const GFXStateBlockDesc &desc, F32 radius, const Point3F &pos, const ColorI &color, bool drawTop, bool drawBottom, const MatrixF *xfm ) +{ + MatrixF mat; + if ( xfm ) + mat = *xfm; + else + mat = MatrixF::Identity; + + mat.scale(Point3F(radius,radius,radius)); + mat.setPosition(pos); + GFX->pushWorldMatrix(); + GFX->multWorld(mat); + + const SphereMesh::TriangleMesh * sphereMesh = gSphere.getMesh(2); + S32 numPoly = sphereMesh->numPoly; + S32 totalPoly = 0; + GFXVertexBufferHandle verts(mDevice, numPoly*3, GFXBufferTypeVolatile); + verts.lock(); + S32 vertexIndex = 0; + for (S32 i=0; ipoly[i].pnt[0].z < -0.01f || sphereMesh->poly[i].pnt[1].z < -0.01f || sphereMesh->poly[i].pnt[2].z < -0.01f) + continue; + } + if (!drawTop) + { + if (sphereMesh->poly[i].pnt[0].z > 0.01f || sphereMesh->poly[i].pnt[1].z > 0.01f || sphereMesh->poly[i].pnt[2].z > 0.01f) + continue; + } + totalPoly++; + + verts[vertexIndex].point = sphereMesh->poly[i].pnt[0]; + verts[vertexIndex].color = color; + vertexIndex++; + + verts[vertexIndex].point = sphereMesh->poly[i].pnt[1]; + verts[vertexIndex].color = color; + vertexIndex++; + + verts[vertexIndex].point = sphereMesh->poly[i].pnt[2]; + verts[vertexIndex].color = color; + vertexIndex++; + } + verts.unlock(); + + mDevice->setStateBlockByDesc( desc ); + + mDevice->setVertexBuffer( verts ); + mDevice->setupGenericShaders(); + + mDevice->drawPrimitive( GFXTriangleList, 0, totalPoly ); + + GFX->popWorldMatrix(); +} + +//----------------------------------------------------------------------------- + +static const Point3F cubePoints[8] = +{ + Point3F(-1, -1, -1), Point3F(-1, -1, 1), Point3F(-1, 1, -1), Point3F(-1, 1, 1), + Point3F( 1, -1, -1), Point3F( 1, -1, 1), Point3F( 1, 1, -1), Point3F( 1, 1, 1) +}; + +static const U32 cubeFaces[6][4] = +{ + { 0, 4, 6, 2 }, { 0, 2, 3, 1 }, { 0, 1, 5, 4 }, + { 3, 2, 6, 7 }, { 7, 6, 4, 5 }, { 3, 7, 5, 1 } +}; + +void GFXDrawUtil::drawTriangle( const GFXStateBlockDesc &desc, const Point3F &p0, const Point3F &p1, const Point3F &p2, const ColorI &color, const MatrixF *xfm ) +{ + if ( desc.fillMode == GFXFillWireframe ) + _drawWireTriangle( desc, p0, p1, p2, color, xfm ); + else + _drawSolidTriangle( desc, p0, p1, p2, color, xfm ); +} + +void GFXDrawUtil::_drawWireTriangle( const GFXStateBlockDesc &desc, const Point3F &p0, const Point3F &p1, const Point3F &p2, const ColorI &color, const MatrixF *xfm ) +{ + GFXVertexBufferHandle verts(mDevice, 4, GFXBufferTypeVolatile); + verts.lock(); + + // Set up the line strip + verts[0].point = p0; + verts[0].color = color; + verts[1].point = p1; + verts[1].color = color; + verts[2].point = p2; + verts[2].color = color; + verts[3].point = p0; + verts[3].color = color; + + // Apply xfm if we were passed one. + if ( xfm != NULL ) + { + for ( U32 i = 0; i < 4; i++ ) + xfm->mulP( verts[i].point ); + } + + verts.unlock(); + + GFXStateBlockRef sb = mDevice->createStateBlock( desc ); + mDevice->setStateBlock( sb ); + + mDevice->setVertexBuffer( verts ); + mDevice->setupGenericShaders(); + + mDevice->drawPrimitive( GFXLineStrip, 0, 3 ); +} + +void GFXDrawUtil::_drawSolidTriangle( const GFXStateBlockDesc &desc, const Point3F &p0, const Point3F &p1, const Point3F &p2, const ColorI &color, const MatrixF *xfm ) +{ + GFXVertexBufferHandle verts(mDevice, 3, GFXBufferTypeVolatile); + verts.lock(); + + // Set up the line strip + verts[0].point = p0; + verts[0].color = color; + verts[1].point = p1; + verts[1].color = color; + verts[2].point = p2; + verts[2].color = color; + + // Apply xfm if we were passed one. + if ( xfm != NULL ) + { + for ( U32 i = 0; i < 3; i++ ) + xfm->mulP( verts[i].point ); + } + + verts.unlock(); + + GFXStateBlockRef sb = mDevice->createStateBlock( desc ); + mDevice->setStateBlock( sb ); + + mDevice->setVertexBuffer( verts ); + mDevice->setupGenericShaders(); + + mDevice->drawPrimitive( GFXTriangleList, 0, 1 ); +} + +void GFXDrawUtil::drawPolygon( const GFXStateBlockDesc& desc, const Point3F* points, U32 numPoints, const ColorI& color, const MatrixF* xfm /* = NULL */ ) +{ + const bool isWireframe = ( desc.fillMode == GFXFillWireframe ); + const U32 numVerts = isWireframe ? numPoints + 1 : numPoints; + GFXVertexBufferHandle< GFXVertexPC > verts( mDevice, numVerts, GFXBufferTypeVolatile ); + + verts.lock(); + for( U32 i = 0; i < numPoints; ++ i ) + { + verts[ i ].point = points[ i ]; + verts[ i ].color = color; + } + + if( xfm ) + { + for( U32 i = 0; i < numPoints; ++ i ) + xfm->mulP( verts[ i ].point ); + } + + if( isWireframe ) + { + verts[ numVerts - 1 ].point = verts[ 0 ].point; + verts[ numVerts - 1 ].color = color; + } + + verts.unlock(); + + mDevice->setStateBlockByDesc( desc ); + + mDevice->setVertexBuffer( verts ); + mDevice->setupGenericShaders(); + + if( desc.fillMode == GFXFillWireframe ) + mDevice->drawPrimitive( GFXLineStrip, 0, numPoints ); + else + mDevice->drawPrimitive( GFXTriangleFan, 0, numPoints - 2 ); +} + +void GFXDrawUtil::drawCube( const GFXStateBlockDesc &desc, const Box3F &box, const ColorI &color, const MatrixF *xfm ) +{ + drawCube( desc, box.getExtents(), box.getCenter(), color, xfm ); +} + +void GFXDrawUtil::drawCube( const GFXStateBlockDesc &desc, const Point3F &size, const Point3F &pos, const ColorI &color, const MatrixF *xfm ) +{ + if ( desc.fillMode == GFXFillWireframe ) + _drawWireCube( desc, size, pos, color, xfm ); + else + _drawSolidCube( desc, size, pos, color, xfm ); +} + +void GFXDrawUtil::_drawWireCube( const GFXStateBlockDesc &desc, const Point3F &size, const Point3F &pos, const ColorI &color, const MatrixF *xfm ) +{ + GFXVertexBufferHandle verts(mDevice, 30, GFXBufferTypeVolatile); + verts.lock(); + + Point3F halfSize = size * 0.5f; + + // setup 6 line loops + U32 vertexIndex = 0; + for(int i = 0; i < 6; i++) + { + for(int j = 0; j < 5; j++) + { + int idx = cubeFaces[i][j%4]; + + verts[vertexIndex].point = cubePoints[idx] * halfSize; + verts[vertexIndex].color = color; + vertexIndex++; + } + } + + // Apply xfm if we were passed one. + if ( xfm != NULL ) + { + for ( U32 i = 0; i < 30; i++ ) + xfm->mulP( verts[i].point ); + } + + // Apply position offset + for ( U32 i = 0; i < 30; i++ ) + verts[i].point += pos; + + verts.unlock(); + + mDevice->setStateBlockByDesc( desc ); + + mDevice->setVertexBuffer( verts ); + mDevice->setupGenericShaders(); + + for( U32 i=0; i<6; i++ ) + mDevice->drawPrimitive( GFXLineStrip, i*5, 4 ); +} + +void GFXDrawUtil::_drawSolidCube( const GFXStateBlockDesc &desc, const Point3F &size, const Point3F &pos, const ColorI &color, const MatrixF *xfm ) +{ + GFXVertexBufferHandle verts(mDevice, 36, GFXBufferTypeVolatile); + verts.lock(); + + Point3F halfSize = size * 0.5f; + + // setup 6 line loops + U32 vertexIndex = 0; + U32 idx; + for(int i = 0; i < 6; i++) + { + idx = cubeFaces[i][0]; + verts[vertexIndex].point = cubePoints[idx] * halfSize; + verts[vertexIndex].color = color; + vertexIndex++; + + idx = cubeFaces[i][1]; + verts[vertexIndex].point = cubePoints[idx] * halfSize; + verts[vertexIndex].color = color; + vertexIndex++; + + idx = cubeFaces[i][3]; + verts[vertexIndex].point = cubePoints[idx] * halfSize; + verts[vertexIndex].color = color; + vertexIndex++; + + idx = cubeFaces[i][1]; + verts[vertexIndex].point = cubePoints[idx] * halfSize; + verts[vertexIndex].color = color; + vertexIndex++; + + idx = cubeFaces[i][2]; + verts[vertexIndex].point = cubePoints[idx] * halfSize; + verts[vertexIndex].color = color; + vertexIndex++; + + idx = cubeFaces[i][3]; + verts[vertexIndex].point = cubePoints[idx] * halfSize; + verts[vertexIndex].color = color; + vertexIndex++; + } + + // Apply xfm if we were passed one. + if ( xfm != NULL ) + { + for ( U32 i = 0; i < 36; i++ ) + xfm->mulV( verts[i].point ); + } + + // Apply position offset + for ( U32 i = 0; i < 36; i++ ) + verts[i].point += pos; + + verts.unlock(); + + mDevice->setStateBlockByDesc( desc ); + + mDevice->setVertexBuffer( verts ); + mDevice->setupGenericShaders(); + + mDevice->drawPrimitive( GFXTriangleList, 0, 12 ); +} + +void GFXDrawUtil::drawPolyhedron( const GFXStateBlockDesc &desc, const AnyPolyhedron &poly, const ColorI &color, const MatrixF *xfm ) +{ + if ( desc.fillMode == GFXFillWireframe ) + _drawWirePolyhedron( desc, poly, color, xfm ); + else + _drawSolidPolyhedron( desc, poly, color, xfm ); +} + +void GFXDrawUtil::_drawWirePolyhedron( const GFXStateBlockDesc &desc, const AnyPolyhedron &poly, const ColorI &color, const MatrixF *xfm ) +{ + GFXDEBUGEVENT_SCOPE( GFXDrawUtil_DrawWirePolyhedron, ColorI::GREEN ); + + const U32 numEdges = poly.getNumEdges(); + const Point3F* points = poly.getPoints(); + const Polyhedron::Edge* edges = poly.getEdges(); + + // Allocate a temporary vertex buffer. + + GFXVertexBufferHandle< GFXVertexPC > verts( mDevice, numEdges * 2, GFXBufferTypeVolatile); + + // Fill it with the vertices for the edges. + + verts.lock(); + for( U32 i = 0; i < numEdges; ++ i ) + { + const U32 nvert = i * 2; + verts[ nvert + 0 ].point = points[ edges[ i ].vertex[ 0 ] ]; + verts[ nvert + 0 ].color = color; + + verts[ nvert + 1 ].point = points[ edges[ i ].vertex[ 1 ] ]; + verts[ nvert + 1 ].color = color; + } + + if( xfm ) + { + for( U32 i = 0; i < numEdges; ++ i ) + { + xfm->mulP( verts[ i + 0 ].point ); + xfm->mulP( verts[ i + 1 ].point ); + } + } + verts.unlock(); + + // Render the line list. + + mDevice->setStateBlockByDesc( desc ); + + mDevice->setVertexBuffer( verts ); + mDevice->setupGenericShaders(); + + mDevice->drawPrimitive( GFXLineList, 0, numEdges ); +} + +void GFXDrawUtil::_drawSolidPolyhedron( const GFXStateBlockDesc &desc, const AnyPolyhedron &poly, const ColorI &color, const MatrixF *xfm ) +{ + GFXDEBUGEVENT_SCOPE( GFXDrawUtil_DrawSolidPolyhedron, ColorI::GREEN ); + + const U32 numPoints = poly.getNumPoints(); + const Point3F* points = poly.getPoints(); + const PlaneF* planes = poly.getPlanes(); + const Point3F viewDir = GFX->getViewMatrix().getForwardVector(); + + // Create a temp buffer for the vertices and + // put all the polyhedron's points in there. + + GFXVertexBufferHandle< GFXVertexPC > verts( mDevice, numPoints, GFXBufferTypeVolatile ); + + verts.lock(); + for( U32 i = 0; i < numPoints; ++ i ) + { + verts[ i ].point = points[ i ]; + verts[ i ].color = color; + } + + if( xfm ) + { + for( U32 i = 0; i < numPoints; ++ i ) + xfm->mulP( verts[ i ].point ); + } + verts.unlock(); + + // Allocate a temp buffer for the face indices. + + const U32 numIndices = poly.getNumEdges() * 2; + const U32 numPlanes = poly.getNumPlanes(); + + GFXPrimitiveBufferHandle prims( mDevice, numIndices, 0, GFXBufferTypeVolatile ); + + // Unfortunately, since polygons may have varying numbers of + // vertices, we also need to retain that information. + + FrameTemp< U32 > numIndicesForPoly( numPlanes ); + U32 numPolys = 0; + + // Create all the polygon indices. + + U16* indices; + prims.lock( &indices ); + U32 idx = 0; + for( U32 i = 0; i < numPlanes; ++ i ) + { + // Since face extraction is somewhat costly, don't bother doing it for + // backfacing polygons if culling is enabled. + + if( !desc.cullDefined || desc.cullMode != GFXCullNone ) + { + F32 dot = mDot( planes[ i ], viewDir ); + + // See if it faces *the same way* as the view direction. This would + // normally mean that the face is *not* backfacing but since we expect + // planes on the polyhedron to be facing *inwards*, we need to reverse + // the logic here. + + if( dot > 0.f ) + continue; + } + + U32 numPoints = poly.extractFace( i, &indices[ idx ], numIndices - idx ); + numIndicesForPoly[ numPolys ] = numPoints; + idx += numPoints; + + numPolys ++; + } + prims.unlock(); + + // Set up state. + + mDevice->setStateBlockByDesc( desc ); + mDevice->setupGenericShaders(); + + mDevice->setVertexBuffer( verts ); + mDevice->setPrimitiveBuffer( prims ); + + // Render one triangle fan for each polygon. + + U32 startIndex = 0; + for( U32 i = 0; i < numPolys; ++ i ) + { + U32 numVerts = numIndicesForPoly[ i ]; + mDevice->drawIndexedPrimitive( GFXTriangleFan, 0, 0, numPoints, startIndex, numVerts - 2 ); + startIndex += numVerts; + } +} + +void GFXDrawUtil::drawObjectBox( const GFXStateBlockDesc &desc, const Point3F &size, const Point3F &pos, const MatrixF &objMat, const ColorI &color ) +{ + GFXTransformSaver saver; + + mDevice->setStateBlockByDesc( desc ); + + MatrixF scaledObjMat( true ); + scaledObjMat = objMat; + + scaledObjMat.scale( size ); + scaledObjMat.setPosition( pos ); + + PrimBuild::color( color ); + PrimBuild::begin( GFXLineList, 48 ); + + static const Point3F cubePoints[8] = + { + Point3F(-0.5, -0.5, -0.5), Point3F(-0.5, -0.5, 0.5), Point3F(-0.5, 0.5, -0.5), Point3F(-0.5, 0.5, 0.5), + Point3F( 0.5, -0.5, -0.5), Point3F( 0.5, -0.5, 0.5), Point3F( 0.5, 0.5, -0.5), Point3F( 0.5, 0.5, 0.5) + }; + + // 8 corner points of the box + for ( U32 i = 0; i < 8; i++ ) + { + //const Point3F &start = cubePoints[i]; + + // 3 lines per corner point + for ( U32 j = 0; j < 3; j++ ) + { + Point3F start = cubePoints[i]; + Point3F end = start; + end[j] *= 0.8f; + + scaledObjMat.mulP(start); + PrimBuild::vertex3fv(start); + scaledObjMat.mulP(end); + PrimBuild::vertex3fv(end); + } + } + + PrimBuild::end(); +} + +static const Point2F circlePoints[] = +{ + Point2F(0.707107f, 0.707107f), + Point2F(0.923880f, 0.382683f), + Point2F(1.000000f, 0.000000f), + Point2F(0.923880f, -0.382684f), + Point2F(0.707107f, -0.707107f), + Point2F(0.382683f, -0.923880f), + Point2F(0.000000f, -1.000000f), + Point2F(-0.382683f, -0.923880f), + Point2F(-0.707107f, -0.707107f), + Point2F(-0.923880f, -0.382684f), + Point2F(-1.000000f, 0.000000f), + Point2F(-0.923879f, 0.382684f), + Point2F(-0.707107f, 0.707107f), + Point2F(-0.382683f, 0.923880f), + Point2F(0.000000f, 1.000000f), + Point2F(0.382684f, 0.923879f) +}; + +void GFXDrawUtil::drawCapsule( const GFXStateBlockDesc &desc, const Point3F ¢er, F32 radius, F32 height, const ColorI &color, const MatrixF *xfm ) +{ + if ( desc.fillMode == GFXFillWireframe ) + _drawWireCapsule( desc, center, radius, height, color, xfm ); + else + _drawSolidCapsule( desc, center, radius, height, color, xfm ); +} + +void GFXDrawUtil::_drawSolidCapsule( const GFXStateBlockDesc &desc, const Point3F ¢er, F32 radius, F32 height, const ColorI &color, const MatrixF *xfm ) +{ + MatrixF mat; + if ( xfm ) + mat = *xfm; + else + mat = MatrixF::Identity; + + S32 numPoints = sizeof(circlePoints)/sizeof(Point2F); + GFXVertexBufferHandle verts(mDevice, numPoints * 2 + 2, GFXBufferTypeVolatile); + verts.lock(); + + for (S32 i=0; isetStateBlockByDesc( desc ); + + mDevice->setVertexBuffer( verts ); + mDevice->setupGenericShaders(); + + mDevice->drawPrimitive( GFXTriangleStrip, 0, 2 * numPoints ); + + Point3F sphereCenter; + MatrixF sphereMat; + + if ( xfm ) + sphereMat = *xfm; + else + sphereMat = MatrixF::Identity; + + sphereCenter.set( 0, 0, 0.5f * height ); + mat.mulV( sphereCenter ); + sphereCenter += center; + + drawSphere( desc, radius, sphereCenter, color, true, false, &sphereMat ); + + sphereCenter.set( 0, 0, -0.5f * height ); + mat.mulV( sphereCenter ); + sphereCenter += center; + + drawSphere( desc, radius, sphereCenter, color, false, true, &sphereMat ); +} + +void GFXDrawUtil::_drawWireCapsule( const GFXStateBlockDesc &desc, const Point3F ¢er, F32 radius, F32 height, const ColorI &color, const MatrixF *xfm ) +{ + MatrixF mat; + if ( xfm ) + mat = *xfm; + else + mat = MatrixF::Identity; + + mat.scale( Point3F(radius,radius,height*0.5f) ); + mat.setPosition(center); + mDevice->pushWorldMatrix(); + mDevice->multWorld(mat); + + S32 numPoints = sizeof(circlePoints)/sizeof(Point2F); + GFXVertexBufferHandle verts(mDevice, numPoints, GFXBufferTypeVolatile); + verts.lock(); + for (S32 i=0; i< numPoints; i++) + { + S32 idx = i & (~1); // just draw the even ones + F32 z = i & 1 ? 1.0f : -1.0f; + verts[i].point = Point3F(circlePoints[idx].x,circlePoints[idx].y, z); + verts[i].color = color; + } + verts.unlock(); + + mDevice->setStateBlockByDesc( desc ); + + mDevice->setVertexBuffer( verts ); + mDevice->setupGenericShaders(); + + for (S32 i=0; idrawPrimitive(GFXLineStrip, i, 1); + + mDevice->popWorldMatrix(); + + Point3F sphereCenter; + sphereCenter.z = center.z + 0.5f * height; + drawSphere( desc, radius,sphereCenter,color,true,false); + sphereCenter.z = center.z - 0.5f * height; + drawSphere( desc, radius,sphereCenter,color,false,true); +} + +void GFXDrawUtil::drawCone( const GFXStateBlockDesc &desc, const Point3F &basePnt, const Point3F &tipPnt, F32 baseRadius, const ColorI &color ) +{ + VectorF uvec = tipPnt - basePnt; + F32 height = uvec.len(); + uvec.normalize(); + MatrixF mat( true ); + MathUtils::getMatrixFromUpVector( uvec, &mat ); + mat.setPosition(basePnt); + + Point3F scale( baseRadius, baseRadius, height ); + mat.scale(scale); + + GFXTransformSaver saver; + + mDevice->pushWorldMatrix(); + mDevice->multWorld(mat); + + S32 numPoints = sizeof(circlePoints)/sizeof(Point2F); + GFXVertexBufferHandle verts(mDevice, numPoints + 2, GFXBufferTypeVolatile); + verts.lock(); + verts[0].point = Point3F(0.0f,0.0f,1.0f); + verts[0].color = color; + for (S32 i=0; isetStateBlockByDesc( desc ); + + mDevice->setVertexBuffer( verts ); + mDevice->setupGenericShaders( GFXDevice::GSModColorTexture ); + + mDevice->drawPrimitive( GFXTriangleFan, 0, numPoints ); + mDevice->drawPrimitive( GFXTriangleFan, 1, numPoints-1 ); + + mDevice->popWorldMatrix(); +} + +void GFXDrawUtil::drawCylinder( const GFXStateBlockDesc &desc, const Point3F &basePnt, const Point3F &tipPnt, F32 radius, const ColorI &color ) +{ + VectorF uvec = tipPnt - basePnt; + F32 height = uvec.len(); + uvec.normalize(); + MatrixF mat( true ); + MathUtils::getMatrixFromUpVector( uvec, &mat ); + mat.setPosition(basePnt); + + Point3F scale( radius, radius, height * 2 ); + mat.scale(scale); + GFXTransformSaver saver; + + mDevice->pushWorldMatrix(); + mDevice->multWorld(mat); + + S32 numPoints = sizeof(circlePoints)/sizeof(Point2F); + GFXVertexBufferHandle verts(mDevice, numPoints * 4 + 4, GFXBufferTypeVolatile); + verts.lock(); + for (S32 i=0; isetStateBlockByDesc( desc ); + + mDevice->setVertexBuffer( verts ); + mDevice->setupGenericShaders( GFXDevice::GSModColorTexture ); + + mDevice->drawPrimitive( GFXTriangleFan, 0, numPoints ); + mDevice->drawPrimitive( GFXTriangleFan, numPoints + 1, numPoints ); + mDevice->drawPrimitive( GFXTriangleStrip, 2 * numPoints + 2, 2 * numPoints); + + mDevice->popWorldMatrix(); +} + +void GFXDrawUtil::drawArrow( const GFXStateBlockDesc &desc, const Point3F &start, const Point3F &end, const ColorI &color ) +{ + GFXTransformSaver saver; + + // Direction and length of the arrow. + VectorF dir = end - start; + F32 len = dir.len(); + dir.normalize(); + len *= 0.2f; + + // Base of the cone will be a distance back from the end of the arrow + // proportional to the total distance of the arrow... 0.3f looks about right. + Point3F coneBase = end - dir * len * 0.3f; + + // Calculate the radius of the cone given that we want the cone to have + // an angle of 25 degrees (just because it looks good). + F32 coneLen = ( end - coneBase ).len(); + F32 coneDiameter = mTan( mDegToRad(25.0f) ) * coneLen; + + // Draw the cone on at the arrow's tip. + drawCone( desc, coneBase, end, coneDiameter / 2.0f, color ); + + // Get the difference in length from + // the start of the cone to the end + // of the cylinder so we can put the + // end of the cylinder right against where + // the cone starts. + Point3F coneDiff = end - coneBase; + + // Draw the cylinder. + F32 stickRadius = len * 0.025f; + drawCylinder( desc, start, end - coneDiff, stickRadius, color ); +} + +void GFXDrawUtil::drawFrustum( const Frustum &f, const ColorI &color ) +{ + const Point3F *points = f.getPoints(); + + // Draw near and far planes. + for (U32 offset = 0; offset < 8; offset+=4) + { + drawLine(points[offset+0], points[offset+1], color); + drawLine(points[offset+2], points[offset+3], color); + drawLine(points[offset+0], points[offset+2], color); + drawLine(points[offset+1], points[offset+3], color); + } + + // connect the near and far planes + drawLine(points[Frustum::NearTopLeft], points[Frustum::FarTopLeft], color); + drawLine(points[Frustum::NearTopRight], points[Frustum::FarTopRight], color); + drawLine(points[Frustum::NearBottomLeft], points[Frustum::FarBottomLeft], color); + drawLine(points[Frustum::NearBottomRight], points[Frustum::FarBottomRight], color); +} + +void GFXDrawUtil::drawSolidPlane( const GFXStateBlockDesc &desc, const Point3F &pos, const Point2F &size, const ColorI &color ) +{ + GFXVertexBufferHandle verts(mDevice, 4, GFXBufferTypeVolatile); + verts.lock(); + + verts[0].point = pos + Point3F( -size.x / 2.0f, -size.y / 2.0f, 0 ); + verts[0].color = color; + verts[1].point = pos + Point3F( -size.x / 2.0f, size.y / 2.0f, 0 ); + verts[1].color = color; + verts[2].point = pos + Point3F( size.x / 2.0f, size.y / 2.0f, 0 ); + verts[2].color = color; + verts[3].point = pos + Point3F( size.x / 2.0f, -size.y / 2.0f, 0 ); + verts[3].color = color; + + verts.unlock(); + + mDevice->setStateBlockByDesc( desc ); + + mDevice->setVertexBuffer( verts ); + mDevice->setupGenericShaders(); + + mDevice->drawPrimitive( GFXTriangleFan, 0, 2 ); +} + +void GFXDrawUtil::drawPlaneGrid( const GFXStateBlockDesc &desc, const Point3F &pos, const Point2F &size, const Point2F &step, const ColorI &color, Plane plane ) +{ + // Note that when calculating the number of steps, we +0.5 to round up, + // and +1 for the last line (ie. 4 steps needs 5 lines to be rendered) + U32 uSteps = 0; + if( step.x > 0 ) + uSteps = size.x / step.x + 0.5 + 1; + + U32 vSteps = 0; + if( step.y > 0 ) + vSteps = size.y / step.y + 0.5 + 1; + + if( uSteps <= 1 || vSteps <= 1 ) + return; + + const U32 numVertices = uSteps * 2 + vSteps * 2; + const U32 numLines = uSteps + vSteps; + + Point3F origin; + switch( plane ) + { + case PlaneXY: + origin = Point3F( pos.x - ( size.x / 2.0f ), pos.y - ( size.y / 2.0f ), pos.z ); + break; + + case PlaneXZ: + origin = Point3F( pos.x - ( size.x / 2.0f ), pos.y, pos.z - ( size.y / 2.0f ) ); + break; + + case PlaneYZ: + origin = Point3F( pos.x, pos.y - ( size.x / 2.0f ), pos.z - ( size.y / 2.0f ) ); + break; + } + + GFXVertexBufferHandle verts( mDevice, numVertices, GFXBufferTypeVolatile ); + verts.lock(); + + U32 vertCount = 0; + + if( plane == PlaneXY || plane == PlaneXZ ) + { + F32 start = mFloor( origin.x / step.x + 0.5f ) * step.x; + for ( U32 i = 0; i < uSteps; i++ ) + { + verts[vertCount].point = Point3F( start + step.x * i, origin.y, origin.z ); + verts[vertCount].color = color; + ++vertCount; + + if( plane == PlaneXY ) + verts[vertCount].point = Point3F( start + step.x * i, origin.y + size.y, origin.z ); + else + verts[vertCount].point = Point3F( start + step.x * i, origin.y, origin.z + size.y ); + + verts[vertCount].color = color; + ++vertCount; + } + } + + if( plane == PlaneXY || plane == PlaneYZ ) + { + U32 num; + F32 stp; + if( plane == PlaneXY ) + { + num = vSteps; + stp = step.y; + } + else + { + num = uSteps; + stp = step.x; + } + + F32 start = mFloor( origin.y / stp + 0.5f ) * stp; + + for ( U32 i = 0; i < num; i++ ) + { + verts[vertCount].point = Point3F( origin.x, start + stp * i, origin.z ); + verts[vertCount].color = color; + ++vertCount; + + if( plane == PlaneXY ) + verts[vertCount].point = Point3F( origin.x + size.x, start + stp * i, origin.z ); + else + verts[vertCount].point = Point3F( origin.x, start + stp * i, origin.z + size.x ); + + verts[vertCount].color = color; + ++vertCount; + } + } + + if( plane == PlaneXZ || plane == PlaneYZ ) + { + F32 start = mFloor( origin.z / step.y + 0.5f ) * step.y; + for ( U32 i = 0; i < vSteps; i++ ) + { + verts[vertCount].point = Point3F( origin.x, origin.y, start + step.y * i ); + verts[vertCount].color = color; + ++vertCount; + + if( plane == PlaneXZ ) + verts[vertCount].point = Point3F( origin.x + size.x, origin.y, start + step.y * i ); + else + verts[vertCount].point = Point3F( origin.x, origin.y + size.x, start + step.y * i ); + + verts[vertCount].color = color; + ++vertCount; + } + } + + verts.unlock(); + + mDevice->setStateBlockByDesc( desc ); + + mDevice->setVertexBuffer( verts ); + mDevice->setupGenericShaders(); + + mDevice->drawPrimitive( GFXLineList, 0, numLines ); +} + +void GFXDrawUtil::drawTransform( const GFXStateBlockDesc &desc, const MatrixF &mat, const Point3F *scale, const ColorI colors[3] ) +{ + GFXTransformSaver saver; + + GFX->multWorld( mat ); + + GFXVertexBufferHandle verts( mDevice, 6, GFXBufferTypeVolatile ); + verts.lock(); + + const static ColorI defColors[3] = + { + ColorI::RED, + ColorI::GREEN, + ColorI::BLUE + }; + + const ColorI *colArray = ( colors != NULL ) ? colors : defColors; + + verts[0].point = Point3F::Zero; + verts[0].color = colArray[0]; + verts[1].point = Point3F( 1, 0, 0 ); + verts[1].color = colArray[0]; + verts[2].point = Point3F::Zero; + verts[2].color = colArray[1]; + verts[3].point = Point3F( 0, 1, 0 ); + verts[3].color = colArray[1]; + verts[4].point = Point3F::Zero; + verts[4].color = colArray[2]; + verts[5].point = Point3F( 0, 0, 1 ); + verts[5].color = colArray[2]; + + if ( scale ) + { + verts[1].point *= *scale; + verts[3].point *= *scale; + verts[5].point *= *scale; + } + + verts.unlock(); + + mDevice->setStateBlockByDesc( desc ); + + mDevice->setVertexBuffer( verts ); + mDevice->setupGenericShaders(); + mDevice->drawPrimitive( GFXLineList, 0, 3 ); +} \ No newline at end of file diff --git a/Engine/source/gfx/gfxDrawUtil.h b/Engine/source/gfx/gfxDrawUtil.h new file mode 100644 index 000000000..906342428 --- /dev/null +++ b/Engine/source/gfx/gfxDrawUtil.h @@ -0,0 +1,181 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFX_GFXDRAWER_H_ +#define _GFX_GFXDRAWER_H_ + +#ifndef _GFXDEVICE_H_ +#include "gfx/gfxDevice.h" +#endif + +#ifndef _MPOLYHEDRON_H_ +#include "math/mPolyhedron.h" +#endif + + + +class FontRenderBatcher; +class Frustum; + + +/// Helper class containing utility functions for useful drawing routines +/// (line, box, rect, billboard, text). +class GFXDrawUtil +{ +public: + GFXDrawUtil(GFXDevice *); + ~GFXDrawUtil(); + + //----------------------------------------------------------------------------- + // Draw Rectangles + //----------------------------------------------------------------------------- + void drawRect( const Point2F &upperLeft, const Point2F &lowerRight, const ColorI &color ); + void drawRect( const RectF &rect, const ColorI &color ); + void drawRect( const Point2I &upperLeft, const Point2I &lowerRight, const ColorI &color ); + void drawRect( const RectI &rect, const ColorI &color ); + + void drawRectFill( const Point2F &upperL, const Point2F &lowerR, const ColorI &color ); + void drawRectFill( const RectF &rect, const ColorI &color ); + void drawRectFill( const Point2I &upperLeft, const Point2I &lowerRight, const ColorI &color ); + void drawRectFill( const RectI &rect, const ColorI &color ); + + void draw2DSquare( const Point2F &screenPoint, F32 width, F32 spinAngle ); + + //----------------------------------------------------------------------------- + // Draw Lines + //----------------------------------------------------------------------------- + void drawLine( const Point3F &startPt, const Point3F &endPt, const ColorI &color ); + void drawLine( const Point2F &startPt, const Point2F &endPt, const ColorI &color ); + void drawLine( const Point2I &startPt, const Point2I &endPt, const ColorI &color ); + void drawLine( F32 x1, F32 y1, F32 x2, F32 y2, const ColorI &color ); + void drawLine( F32 x1, F32 y1, F32 z1, F32 x2, F32 y2, F32 z2, const ColorI &color ); + + //----------------------------------------------------------------------------- + // Draw Text + //----------------------------------------------------------------------------- + U32 drawText( GFont *font, const Point2I &ptDraw, const UTF8 *in_string, const ColorI *colorTable = NULL, const U32 maxColorIndex = 9, F32 rot = 0.f ); + U32 drawTextN( GFont *font, const Point2I &ptDraw, const UTF8 *in_string, U32 n, const ColorI *colorTable = NULL, const U32 maxColorIndex = 9, F32 rot = 0.f ); + U32 drawText( GFont *font, const Point2I &ptDraw, const UTF16 *in_string, const ColorI *colorTable = NULL, const U32 maxColorIndex = 9, F32 rot = 0.f ); + U32 drawTextN( GFont *font, const Point2I &ptDraw, const UTF16 *in_string, U32 n, const ColorI *colorTable = NULL, const U32 maxColorIndex = 9, F32 rot = 0.f ); + + U32 drawText( GFont *font, const Point2F &ptDraw, const UTF8 *in_string, const ColorI *colorTable = NULL, const U32 maxColorIndex = 9, F32 rot = 0.f ); + U32 drawTextN( GFont *font, const Point2F &ptDraw, const UTF8 *in_string, U32 n, const ColorI *colorTable = NULL, const U32 maxColorIndex = 9, F32 rot = 0.f ); + U32 drawText( GFont *font, const Point2F &ptDraw, const UTF16 *in_string, const ColorI *colorTable = NULL, const U32 maxColorIndex = 9, F32 rot = 0.f ); + U32 drawTextN( GFont *font, const Point2F &ptDraw, const UTF16 *in_string, U32 n, const ColorI *colorTable = NULL, const U32 maxColorIndex = 9, F32 rot = 0.f ); + + //----------------------------------------------------------------------------- + // Color Modulation + //----------------------------------------------------------------------------- + void setBitmapModulation( const ColorI &modColor ); + void setTextAnchorColor( const ColorI &ancColor ); + void clearBitmapModulation(); + void getBitmapModulation( ColorI *color ); + + //----------------------------------------------------------------------------- + // Draw Bitmaps + //----------------------------------------------------------------------------- + void drawBitmap( GFXTextureObject*texture, const Point2F &in_rAt, const GFXBitmapFlip in_flip = GFXBitmapFlip_None, const GFXTextureFilterType filter = GFXTextureFilterPoint , bool in_wrap = true ); + void drawBitmapSR( GFXTextureObject*texture, const Point2F &in_rAt, const RectF &srcRect, const GFXBitmapFlip in_flip = GFXBitmapFlip_None, const GFXTextureFilterType filter = GFXTextureFilterPoint , bool in_wrap = true ); + void drawBitmapStretch( GFXTextureObject*texture, const RectF &dstRect, const GFXBitmapFlip in_flip = GFXBitmapFlip_None, const GFXTextureFilterType filter = GFXTextureFilterPoint , bool in_wrap = true ); + void drawBitmapStretchSR( GFXTextureObject*texture, const RectF &dstRect, const RectF &srcRect, const GFXBitmapFlip in_flip = GFXBitmapFlip_None, const GFXTextureFilterType filter = GFXTextureFilterPoint , bool in_wrap = true ); + + void drawBitmap( GFXTextureObject*texture, const Point2I &in_rAt, const GFXBitmapFlip in_flip = GFXBitmapFlip_None, const GFXTextureFilterType filter = GFXTextureFilterPoint , bool in_wrap = true ); + void drawBitmapSR( GFXTextureObject*texture, const Point2I &in_rAt, const RectI &srcRect, const GFXBitmapFlip in_flip = GFXBitmapFlip_None, const GFXTextureFilterType filter = GFXTextureFilterPoint , bool in_wrap = true ); + void drawBitmapStretch( GFXTextureObject*texture, const RectI &dstRect, const GFXBitmapFlip in_flip = GFXBitmapFlip_None, const GFXTextureFilterType filter = GFXTextureFilterPoint , bool in_wrap = true ); + void drawBitmapStretchSR( GFXTextureObject*texture, const RectI &dstRect, const RectI &srcRect, const GFXBitmapFlip in_flip = GFXBitmapFlip_None, const GFXTextureFilterType filter = GFXTextureFilterPoint , bool in_wrap = true ); + + //----------------------------------------------------------------------------- + // Draw 3D Shapes + //----------------------------------------------------------------------------- + void drawTriangle( const GFXStateBlockDesc &desc, const Point3F &p0, const Point3F &p1, const Point3F &p2, const ColorI &color, const MatrixF *xfm = NULL ); + void drawPolygon( const GFXStateBlockDesc& desc, const Point3F* points, U32 numPoints, const ColorI& color, const MatrixF* xfm = NULL ); + void drawCube( const GFXStateBlockDesc &desc, const Point3F &size, const Point3F &pos, const ColorI &color, const MatrixF *xfm = NULL ); + void drawCube( const GFXStateBlockDesc &desc, const Box3F &box, const ColorI &color, const MatrixF *xfm = NULL ); + void drawObjectBox( const GFXStateBlockDesc &desc, const Point3F &size, const Point3F &pos, const MatrixF &objMat, const ColorI &color ); + void drawSphere( const GFXStateBlockDesc &desc, F32 radius, const Point3F &pos, const ColorI &color, bool drawTop = true, bool drawBottom = true, const MatrixF *xfm = NULL ); + void drawCapsule( const GFXStateBlockDesc &desc, const Point3F ¢er, F32 radius, F32 height, const ColorI &color, const MatrixF *xfm = NULL ); + void drawCone( const GFXStateBlockDesc &desc, const Point3F &basePnt, const Point3F &tipPnt, F32 baseRadius, const ColorI &color ); + void drawCylinder( const GFXStateBlockDesc &desc, const Point3F &basePnt, const Point3F &tipPnt, F32 baseRadius, const ColorI &color ); + void drawArrow( const GFXStateBlockDesc &desc, const Point3F &start, const Point3F &end, const ColorI &color ); + void drawFrustum( const Frustum& f, const ColorI &color ); + + /// Draw a solid or wireframe (depending on fill mode of @a desc) polyhedron with the given color. + /// + /// @param desc Render state description. + /// @param poly Polyhedron. + /// @param color Color. + /// @param xfm Optional matrix to transform all vertices of the given polyhedron by. + void drawPolyhedron( const GFXStateBlockDesc &desc, const AnyPolyhedron &poly, const ColorI &color, const MatrixF *xfm = NULL ); + + /// Draws a solid XY plane centered on the point with the specified dimensions. + void drawSolidPlane( const GFXStateBlockDesc &desc, const Point3F &pos, const Point2F &size, const ColorI &color ); + + enum Plane + { + PlaneXY, + PlaneXZ, + PlaneYZ + }; + + /// Draws a grid on XY, XZ, or YZ plane centered on the point with the specified size and step size. + void drawPlaneGrid( const GFXStateBlockDesc &desc, const Point3F &pos, const Point2F &size, const Point2F &step, const ColorI &color, Plane plane = PlaneXY ); + + /// Draws axis lines representing the passed matrix. + /// If scale is NULL axes will be drawn the length they exist within the MatrixF. + /// If colors is NULL the default colors are RED, GREEEN, BLUE ( x, y, z ). + void drawTransform( const GFXStateBlockDesc &desc, const MatrixF &mat, const Point3F *scale = NULL, const ColorI colors[3] = NULL ); + +protected: + + void _setupStateBlocks(); + void _drawWireTriangle( const GFXStateBlockDesc &desc, const Point3F &p0, const Point3F &p1, const Point3F &p2, const ColorI &color, const MatrixF *xfm = NULL ); + void _drawSolidTriangle( const GFXStateBlockDesc &desc, const Point3F &p0, const Point3F &p1, const Point3F &p2, const ColorI &color, const MatrixF *xfm = NULL ); + void _drawWireCube( const GFXStateBlockDesc &desc, const Point3F &size, const Point3F &pos, const ColorI &color, const MatrixF *xfm = NULL ); + void _drawSolidCube( const GFXStateBlockDesc &desc, const Point3F &size, const Point3F &pos, const ColorI &color, const MatrixF *xfm = NULL ); + void _drawWireCapsule( const GFXStateBlockDesc &desc, const Point3F ¢er, F32 radius, F32 height, const ColorI &color, const MatrixF *xfm = NULL ); + void _drawSolidCapsule( const GFXStateBlockDesc &desc, const Point3F ¢er, F32 radius, F32 height, const ColorI &color, const MatrixF *xfm = NULL ); + void _drawWirePolyhedron( const GFXStateBlockDesc &desc, const AnyPolyhedron &poly, const ColorI &color, const MatrixF *xfm = NULL ); + void _drawSolidPolyhedron( const GFXStateBlockDesc &desc, const AnyPolyhedron &poly, const ColorI &color, const MatrixF *xfm = NULL ); + +protected: + + /// The device we're rendering to. + GFXDevice *mDevice; + + /// Bitmap modulation color; bitmaps are multiplied by this color when + /// drawn. + GFXVertexColor mBitmapModulation; + + /// Base text color; what color text is drawn at when no other color is + /// specified. + GFXVertexColor mTextAnchorColor; + + GFXStateBlockRef mBitmapStretchSB; + GFXStateBlockRef mBitmapStretchLinearSB; + GFXStateBlockRef mBitmapStretchWrapSB; + GFXStateBlockRef mBitmapStretchWrapLinearSB; + GFXStateBlockRef mRectFillSB; + + FontRenderBatcher* mFontRenderBatcher; +}; + +#endif // _GFX_GFXDRAWER_H_ diff --git a/Engine/source/gfx/gfxEnums.h b/Engine/source/gfx/gfxEnums.h new file mode 100644 index 000000000..a11d911bc --- /dev/null +++ b/Engine/source/gfx/gfxEnums.h @@ -0,0 +1,624 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXENUMS_H_ +#define _GFXENUMS_H_ + +#include "core/util/fourcc.h" + +// These are for the enum translation. It will help with porting to other platforms +// and API's. +#define GFX_UNSUPPORTED_VAL 0xDEADBEEF +#define GFX_UNINIT_VAL 0xDECAFBAD + +// Adjust these pools to your app's needs. Be aware dynamic vertices are much more +// expensive than static vertices. These are in gfxEnums because they should be +// consistant across all APIs/platforms so that the dynamic buffer performance +// and behavior is also consistant. -patw +#define MAX_DYNAMIC_VERTS (8192*2) +#define MAX_DYNAMIC_INDICES (8192*4) + +enum GFXBufferType +{ + GFXBufferTypeStatic, ///< Static vertex buffers are created and filled one time. + ///< incur a performance penalty. Resizing a static vertex buffer is not + ///< allowed. + GFXBufferTypeDynamic, ///< Dynamic vertex buffers are meant for vertices that can be changed + ///< often. Vertices written into dynamic vertex buffers will remain valid + ///< until the dynamic vertex buffer is released. Resizing a dynamic vertex buffer is not + ///< allowed. + GFXBufferTypeVolatile, ///< Volatile vertex or index buffers are meant for vertices or indices that are essentially + ///< only used once. They can be resized without any performance penalty. + + GFXBufferType_COUNT ///< Number of buffer types. +}; + +enum GFXTexCallbackCode +{ + GFXZombify, + GFXResurrect, +}; + + +enum GFXPrimitiveType +{ + GFXPT_FIRST = 0, + GFXPointList = 0, + GFXLineList, + GFXLineStrip, + GFXTriangleList, + GFXTriangleStrip, + GFXTriangleFan, + GFXPT_COUNT +}; + +enum GFXTextureType +{ + GFXTextureType_Normal, + GFXTextureType_KeepBitmap, + GFXTextureType_Dynamic, + GFXTextureType_RenderTarget, + GFXTextureType_Count +}; + +enum GFXBitmapFlip +{ + GFXBitmapFlip_None = 0, + GFXBitmapFlip_X = 1 << 0, + GFXBitmapFlip_Y = 1 << 1, + GFXBitmapFlip_XY = GFXBitmapFlip_X | GFXBitmapFlip_Y +}; + +enum GFXTextureOp +{ + GFXTOP_FIRST = 0, + GFXTOPDisable = 0, + GFXTOPSelectARG1, + GFXTOPSelectARG2, + GFXTOPModulate, + GFXTOPModulate2X, + GFXTOPModulate4X, + GFXTOPAdd, + GFXTOPAddSigned, + GFXTOPAddSigned2X, + GFXTOPSubtract, + GFXTOPAddSmooth, + GFXTOPBlendDiffuseAlpha, + GFXTOPBlendTextureAlpha, + GFXTOPBlendFactorAlpha, + GFXTOPBlendTextureAlphaPM, + GFXTOPBlendCURRENTALPHA, + GFXTOPPreModulate, + GFXTOPModulateAlphaAddColor, + GFXTOPModulateColorAddAlpha, + GFXTOPModulateInvAlphaAddColor, + GFXTOPModulateInvColorAddAlpha, + GFXTOPBumpEnvMap, + GFXTOPBumpEnvMapLuminance, + GFXTOPDotProduct3, + GFXTOPLERP, + GFXTOP_COUNT +}; + +enum GFXTextureAddressMode +{ + GFXAddress_FIRST = 0, + GFXAddressWrap = 0, + GFXAddressMirror, + GFXAddressClamp, + GFXAddressBorder, + GFXAddressMirrorOnce, + GFXAddress_COUNT +}; + +enum GFXTextureFilterType +{ + GFXTextureFilter_FIRST = 0, + GFXTextureFilterNone = 0, + GFXTextureFilterPoint, + GFXTextureFilterLinear, + GFXTextureFilterAnisotropic, + GFXTextureFilterPyramidalQuad, + GFXTextureFilterGaussianQuad, + GFXTextureFilter_COUNT +}; + +enum GFXFillMode +{ + GFXFill_FIRST = 1, + GFXFillPoint = 1, + GFXFillWireframe, + GFXFillSolid, + GFXFill_COUNT +}; + +enum GFXFormat +{ + // when adding formats make sure to place + // them in the correct group! + // + // if displacing the first entry in the group + // make sure to update the GFXFormat_xBIT entries! + // + GFXFormat_FIRST = 0, + + // 8 bit texture formats... + GFXFormatA8 = 0,// first in group... + GFXFormatL8, + GFXFormatA4L4, + + // 16 bit texture formats... + GFXFormatR5G6B5,// first in group... + GFXFormatR5G5B5A1, + GFXFormatR5G5B5X1, + GFXFormatA8L8, + GFXFormatL16, + GFXFormatR16F, + GFXFormatD16, + + // 24 bit texture formats... + GFXFormatR8G8B8,// first in group... + + // 32 bit texture formats... + GFXFormatR8G8B8A8,// first in group... + GFXFormatR8G8B8X8, + GFXFormatR32F, + GFXFormatR16G16, + GFXFormatR16G16F, + GFXFormatR10G10B10A2, + GFXFormatD32, + GFXFormatD24X8, + GFXFormatD24S8, + GFXFormatD24FS8, + + // 64 bit texture formats... + GFXFormatR16G16B16A16,// first in group... + GFXFormatR16G16B16A16F, + + // 128 bit texture formats... + GFXFormatR32G32B32A32F,// first in group... + + // unknown size... + GFXFormatDXT1,// first in group... + GFXFormatDXT2, + GFXFormatDXT3, + GFXFormatDXT4, + GFXFormatDXT5, + + GFXFormat_COUNT, + + GFXFormat_8BIT = GFXFormatA8, + GFXFormat_16BIT = GFXFormatR5G6B5, + GFXFormat_24BIT = GFXFormatR8G8B8, + GFXFormat_32BIT = GFXFormatR8G8B8A8, + GFXFormat_64BIT = GFXFormatR16G16B16A16, + GFXFormat_128BIT = GFXFormatR32G32B32A32F, + GFXFormat_UNKNOWNSIZE = GFXFormatDXT1, +}; + +/// Returns the byte size of the pixel for non-compressed formats. +inline U32 GFXFormat_getByteSize( GFXFormat format ) +{ + AssertFatal( format < GFXFormat_UNKNOWNSIZE, + "GFXDevice::formatByteSize - Cannot size a compressed format!" ); + + if ( format < GFXFormat_16BIT ) + return 1;// 8 bit... + else if ( format < GFXFormat_24BIT ) + return 2;// 16 bit... + else if ( format < GFXFormat_32BIT ) + return 3;// 24 bit... + else if ( format < GFXFormat_64BIT ) + return 4;// 32 bit... + else if ( format < GFXFormat_128BIT ) + return 8;// 64 bit... + + // This should be 128bits... else its a DDS and + // the assert should have gone off above. + return 16; +} + +enum GFXShadeMode +{ + GFXShadeFlat = 1, + GFXShadeGouraud, + GFXShadePhong, +}; + +enum GFXClearFlags +{ + GFXClearTarget = 1 << 0, + GFXClearZBuffer = 1 << 1, + GFXClearStencil = 1 << 2, +}; + +/// The supported blend modes. +enum GFXBlend +{ + GFXBlend_FIRST = 0, + GFXBlendZero = 0, /// (0, 0, 0, 0) + GFXBlendOne, /// (1, 1, 1, 1) + GFXBlendSrcColor, /// (Rs, Gs, Bs, As) + GFXBlendInvSrcColor, /// (1 - Rs, 1 - Gs, 1 - Bs, 1 - As) + GFXBlendSrcAlpha, /// (As, As, As, As) + GFXBlendInvSrcAlpha, /// ( 1 - As, 1 - As, 1 - As, 1 - As) + GFXBlendDestAlpha, /// (Ad Ad Ad Ad) + GFXBlendInvDestAlpha, /// (1 - Ad 1 - Ad 1 - Ad 1 - Ad) + GFXBlendDestColor, /// (Rd, Gd, Bd, Ad) + GFXBlendInvDestColor, /// (1 - Rd, 1 - Gd, 1 - Bd, 1 - Ad) + GFXBlendSrcAlphaSat, /// (f, f, f, 1) where f = min(As, 1 - Ad) + GFXBlend_COUNT +}; + +/// Constants that name each GFXDevice type. Any new GFXDevice subclass must be +/// added to this enum. A string representing its name must also be added to +/// GFXInit::getAdapterNameFromType(). +enum GFXAdapterType +{ + OpenGL = 0, + Direct3D9, + Direct3D8, + NullDevice, + Direct3D9_360, + GFXAdapterType_Count +}; + +enum GFXCullMode +{ + GFXCull_FIRST = 0, + GFXCullNone = 0, + GFXCullCW, + GFXCullCCW, + GFXCull_COUNT +}; + +enum GFXCmpFunc +{ + GFXCmp_FIRST = 0, + GFXCmpNever = 0, + GFXCmpLess, + GFXCmpEqual, + GFXCmpLessEqual, + GFXCmpGreater, + GFXCmpNotEqual, + GFXCmpGreaterEqual, + GFXCmpAlways, + GFXCmp_COUNT +}; + +enum GFXStencilOp +{ + GFXStencilOp_FIRST = 0, + GFXStencilOpKeep = 0, + GFXStencilOpZero, + GFXStencilOpReplace, + GFXStencilOpIncrSat, + GFXStencilOpDecrSat, + GFXStencilOpInvert, + GFXStencilOpIncr, + GFXStencilOpDecr, + GFXStencilOp_COUNT +}; + +enum GFXMaterialColorSource +{ + GFXMCSMaterial = 0, + GFXMCSColor1, + GFXMCSColor2, +}; + +enum GFXBlendOp +{ + GFXBlendOp_FIRST = 0, + GFXBlendOpAdd = 0, + GFXBlendOpSubtract, + GFXBlendOpRevSubtract, + GFXBlendOpMin, + GFXBlendOpMax, + GFXBlendOp_COUNT +}; + +enum GFXRenderState +{ + GFXRenderState_FIRST = 0, + GFXRSZEnable = 0, + GFXRSFillMode, + GFXRSShadeMode, + GFXRSZWriteEnable, + GFXRSAlphaTestEnable, + GFXRSLastPixel, + GFXRSSrcBlend, + GFXRSDestBlend, + GFXRSCullMode, + GFXRSZFunc, + GFXRSAlphaRef, + GFXRSAlphaFunc, + GFXRSDitherEnable, + GFXRSAlphaBlendEnable, + GFXRSFogEnable, + GFXRSSpecularEnable, + GFXRSFogColor, + GFXRSFogTableMode, + GFXRSFogStart, + GFXRSFogEnd, + GFXRSFogDensity, + GFXRSRangeFogEnable, + GFXRSStencilEnable, + GFXRSStencilFail, + GFXRSStencilZFail, + GFXRSStencilPass, + GFXRSStencilFunc, + GFXRSStencilRef, + GFXRSStencilMask, + GFXRSStencilWriteMask, + GFXRSTextureFactor, + GFXRSWrap0, + GFXRSWrap1, + GFXRSWrap2, + GFXRSWrap3, + GFXRSWrap4, + GFXRSWrap5, + GFXRSWrap6, + GFXRSWrap7, + GFXRSClipping, + GFXRSLighting, + GFXRSAmbient, + GFXRSFogVertexMode, + GFXRSColorVertex, + GFXRSLocalViewer, + GFXRSNormalizeNormals, + GFXRSDiffuseMaterialSource, + GFXRSSpecularMaterialSource, + GFXRSAmbientMaterialSource, + GFXRSEmissiveMaterialSource, + GFXRSVertexBlend, + GFXRSClipPlaneEnable, + GFXRSPointSize, + GFXRSPointSizeMin, + GFXRSPointSpriteEnable, + GFXRSPointScaleEnable, + GFXRSPointScale_A, + GFXRSPointScale_B, + GFXRSPointScale_C, + GFXRSMultiSampleantiAlias, + GFXRSMultiSampleMask, + GFXRSPatchEdgeStyle, + GFXRSDebugMonitorToken, + GFXRSPointSize_Max, + GFXRSIndexedVertexBlendEnable, + GFXRSColorWriteEnable, + GFXRSTweenFactor, + GFXRSBlendOp, + GFXRSPositionDegree, + GFXRSNormalDegree, + GFXRSScissorTestEnable, + GFXRSSlopeScaleDepthBias, + GFXRSAntiAliasedLineEnable, + GFXRSMinTessellationLevel, + GFXRSMaxTessellationLevel, + GFXRSAdaptiveTess_X, + GFXRSAdaptiveTess_Y, + GFXRSdaptiveTess_Z, + GFXRSAdaptiveTess_W, + GFXRSEnableAdaptiveTesselation, + GFXRSTwoSidedStencilMode, + GFXRSCCWStencilFail, + GFXRSCCWStencilZFail, + GFXRSCCWStencilPass, + GFXRSCCWStencilFunc, + GFXRSColorWriteEnable1, + GFXRSColorWriteEnable2, + GFXRSolorWriteEnable3, + GFXRSBlendFactor, + GFXRSSRGBWriteEnable, + GFXRSDepthBias, + GFXRSWrap8, + GFXRSWrap9, + GFXRSWrap10, + GFXRSWrap11, + GFXRSWrap12, + GFXRSWrap13, + GFXRSWrap14, + GFXRSWrap15, + GFXRSSeparateAlphaBlendEnable, + GFXRSSrcBlendAlpha, + GFXRSDestBlendAlpha, + GFXRSBlendOpAlpha, + GFXRenderState_COUNT ///< Don't use this one, this is a counter +}; + +#define GFXCOLORWRITEENABLE_RED 1 +#define GFXCOLORWRITEENABLE_GREEN 2 +#define GFXCOLORWRITEENABLE_BLUE 4 +#define GFXCOLORWRITEENABLE_ALPHA 8 + +enum GFXTextureStageState +{ + GFXTSS_FIRST = 0, + GFXTSSColorOp = 0, + GFXTSSColorArg1, + GFXTSSColorArg2, + GFXTSSAlphaOp, + GFXTSSAlphaArg1, + GFXTSSAlphaArg2, + GFXTSSBumpEnvMat00, + GFXTSSBumpEnvMat01, + GFXTSSBumpEnvMat10, + GFXTSSBumpEnvMat11, + GFXTSSTexCoordIndex, + GFXTSSBumpEnvlScale, + GFXTSSBumpEnvlOffset, + GFXTSSTextureTransformFlags, + GFXTSSColorArg0, + GFXTSSAlphaArg0, + GFXTSSResultArg, + GFXTSSConstant, + GFXTSS_COUNT ///< Don't use this one, this is a counter +}; + +enum GFXTextureTransformFlags +{ + GFXTTFFDisable = 0, + GFXTTFFCoord1D = 1, + GFXTTFFCoord2D = 2, + GFXTTFFCoord3D = 3, + GFXTTFFCoord4D = 4, + GFXTTFFProjected = 256, +}; + +// CodeReview: This number is used for the declaration of variables, but it +// should *not* be used for any run-time purposes [7/2/2007 Pat] +#define TEXTURE_STAGE_COUNT 16 + +enum GFXSamplerState +{ + GFXSAMP_FIRST = 0, + GFXSAMPAddressU = 0, + GFXSAMPAddressV, + GFXSAMPAddressW, + GFXSAMPBorderColor, + GFXSAMPMagFilter, + GFXSAMPMinFilter, + GFXSAMPMipFilter, + GFXSAMPMipMapLODBias, + GFXSAMPMaxMipLevel, + GFXSAMPMaxAnisotropy, + GFXSAMPSRGBTexture, + GFXSAMPElementIndex, + GFXSAMPDMapOffset, + GFXSAMP_COUNT ///< Don't use this one, this is a counter +}; + +enum GFXTextureArgument +{ + GFXTA_FIRST = 0, + GFXTADiffuse = 0, + GFXTACurrent, + GFXTATexture, + GFXTATFactor, + GFXTASpecular, + GFXTATemp, + GFXTAConstant, + GFXTA_COUNT, + GFXTAComplement = 0x00000010, // take 1.0 - x (read modifier) + GFXTAAlphaReplicate = 0x00000020, // replicate alpha to color components (read modifier) +}; + +// Matrix stuff +#define WORLD_STACK_MAX 24 + +enum GFXMatrixType +{ + GFXMatrixWorld = 256, + GFXMatrixView = 2, + GFXMatrixProjection = 3, + GFXMatrixTexture = 16, // This value is texture matrix for sampler 0, can use this for offset + GFXMatrixTexture0 = 16, + GFXMatrixTexture1 = 17, + GFXMatrixTexture2 = 18, + GFXMatrixTexture3 = 19, + GFXMatrixTexture4 = 20, + GFXMatrixTexture5 = 21, + GFXMatrixTexture6 = 22, + GFXMatrixTexture7 = 23, +}; + +// Light define +#define LIGHT_STAGE_COUNT 8 + +#define GFXVERTEXFLAG_F32 3 +#define GFXVERTEXFLAG_POINT2F 0 +#define GFXVERTEXFLAG_POINT3F 1 +#define GFXVERTEXFLAG_POINT4F 2 + +#define GFXVERTEXFLAG_TEXCOORD_F32(CoordIndex) ( GFXVERTEXFLAG_F32 << ( CoordIndex * 2 + 16 ) ) +#define GFXVERTEXFLAG_TEXCOORD_POINT2F(CoordIndex) ( GFXVERTEXFLAG_POINT2F ) +#define GFXVERTEXFLAG_TEXCOORD_POINT3F(CoordIndex) ( GFXVERTEXFLAG_POINT3F << ( CoordIndex * 2 + 16 ) ) +#define GFXVERTEXFLAG_TEXCOORD_POINT4F(CoordIndex) ( GFXVERTEXFLAG_POINT4F << ( CoordIndex * 2 + 16 ) ) + +#define STATE_STACK_SIZE 32 + +// Index Formats +enum GFXIndexFormat +{ + GFXIndexFormat_FIRST = 0, + GFXIndexFormat16 = 0, + GFXIndexFormat32, + GFXIndexFormat_COUNT +}; + +enum GFXShaderConstType +{ + /// GFX"S"hader"C"onstant"T"ype + // Scalar + GFXSCT_Float, + // Vectors + GFXSCT_Float2, + GFXSCT_Float3, + GFXSCT_Float4, + // Matrices + GFXSCT_Float2x2, + GFXSCT_Float3x3, + GFXSCT_Float4x4, + // Scalar + GFXSCT_Int, + // Vectors + GFXSCT_Int2, + GFXSCT_Int3, + GFXSCT_Int4, + // Samplers + GFXSCT_Sampler, + GFXSCT_SamplerCube +}; + + +/// Defines a vertex declaration type. +/// @see GFXVertexElement +/// @see GFXVertexFormat +enum GFXDeclType +{ + GFXDeclType_FIRST = 0, + + /// A single component F32. + GFXDeclType_Float = 0, + + /// A two-component F32. + /// @see Point2F + GFXDeclType_Float2, + + /// A three-component F32. + /// @see Point3F + GFXDeclType_Float3, + + /// A four-component F32. + /// @see Point4F + GFXDeclType_Float4, + + /// A four-component, packed, unsigned bytes mapped to 0 to 1 range. + /// @see GFXVertexColor + GFXDeclType_Color, + + /// The count of total GFXDeclTypes. + GFXDeclType_COUNT, +}; + +#endif // _GFXENUMS_H_ diff --git a/Engine/source/gfx/gfxFence.cpp b/Engine/source/gfx/gfxFence.cpp new file mode 100644 index 000000000..c6c69c769 --- /dev/null +++ b/Engine/source/gfx/gfxFence.cpp @@ -0,0 +1,142 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/gfxFence.h" + +#include "gfx/primBuilder.h" +#include "gfx/gfxTextureManager.h" + + +GFXGeneralFence::~GFXGeneralFence() +{ + // Release the ref pointers + mRenderTarget = NULL; + mRTTexHandle = NULL; + + // Unregister texture callback + GFXTextureManager::removeEventDelegate( this, &GFXGeneralFence::_onTextureEvent ); +} + +//------------------------------------------------------------------------------ + +void GFXGeneralFence::_init() +{ + // Register texture callback to re-create resources + if ( !mInitialized ) + GFXTextureManager::addEventDelegate( this, &GFXGeneralFence::_onTextureEvent ); + + // Set this to true for error checking + mInitialized = true; + + // Allocate resources + mInitialized &= mRTTexHandle.set( 2, 2, GFXFormatR8G8B8X8, &GFXDefaultRenderTargetProfile, avar("%s() - mInitialized (line %d)", __FUNCTION__, __LINE__) ); + mRenderTarget = GFX->allocRenderToTextureTarget(); + mInitialized &= ( mRenderTarget != NULL ); + + GFXStateBlockDesc d; + mRenderSB = GFX->createStateBlock(d); +} + +//------------------------------------------------------------------------------ + +void GFXGeneralFence::issue() +{ + PROFILE_SCOPE(GFXGeneralFence_Issue); + + // Resource creation will be done on first call to issue() + if( !mInitialized ) + _init(); + + // If we can't init, return. + if(!mInitialized) + return; + + AssertFatal( mInitialized, "Error occured during GFXGeneralFence::_init, sorry I can't be more specific, break and debug." ); + + RectI viewport = GFX->getViewport(); + + GFX->pushActiveRenderTarget(); + mRenderTarget->attachTexture( GFXTextureTarget::Color0, mRTTexHandle ); + GFX->setActiveRenderTarget( mRenderTarget ); + + // Set-up states + GFX->setStateBlock(mRenderSB); + GFX->pushWorldMatrix(); + GFX->setWorldMatrix( MatrixF::Identity ); + + // CodeReview: We can re-do this with a static vertex buffer at some point. [5/9/2007 Pat] + PrimBuild::begin( GFXTriangleList, 3 ); + PrimBuild::vertex2f( 0.f, 0.f ); + PrimBuild::vertex2f( 0.f, 1.f ); + PrimBuild::vertex2f( 1.f, 0.f ); + PrimBuild::end(); + + GFX->popWorldMatrix(); + GFX->popActiveRenderTarget(); + + GFX->setViewport(viewport); +} + +//------------------------------------------------------------------------------ + +void GFXGeneralFence::block() +{ + PROFILE_SCOPE(GFXGeneralFence_block); + if( !mInitialized ) + return; + + // We have to deal with the case where the lock fails (usually due to + // a device reset). + if(mRTTexHandle.lock()) + mRTTexHandle.unlock(); +} + +void GFXGeneralFence::_onTextureEvent( GFXTexCallbackCode code ) +{ + switch( code ) + { + case GFXZombify: + mRTTexHandle = NULL; + break; + + case GFXResurrect: + mRTTexHandle.set( 2, 2, GFXFormatR8G8B8X8, &GFXDefaultRenderTargetProfile, avar("%s() - GFXGeneralFence->mRTTexHandle (line %d)", __FUNCTION__, __LINE__) ); + break; + } +} + +void GFXGeneralFence::zombify() +{ + mRTTexHandle = NULL; +} + +void GFXGeneralFence::resurrect() +{ + mRTTexHandle.set( 2, 2, GFXFormatR8G8B8X8, &GFXDefaultRenderTargetProfile, avar("%s() - mRTTexHandle (line %d)", __FUNCTION__, __LINE__) ); +} + +const String GFXGeneralFence::describeSelf() const +{ + // We've got nothing + return String(); +} diff --git a/Engine/source/gfx/gfxFence.h b/Engine/source/gfx/gfxFence.h new file mode 100644 index 000000000..8130c3510 --- /dev/null +++ b/Engine/source/gfx/gfxFence.h @@ -0,0 +1,105 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXFENCE_H_ +#define _GFXFENCE_H_ + +#ifndef _GFXDEVICE_H_ +#include "gfx/gfxDevice.h" +#endif + + +/// +class GFXFence : public GFXResource +{ +protected: + + GFXDevice *mDevice; + +public: + + /// The states returned by getStatus() + enum FenceStatus + { + Unset, ///< The fence has not been set + Pending, ///< The fence has been set and has not been hit + Processed, ///< The fence has been processed + Unsupported ///< A non-blocking query of fence status is not supported by the implementation + }; + +public: + + GFXFence( GFXDevice *device ) : mDevice( device ) {}; + virtual ~GFXFence(){}; + + /// This method inserts the fence into the command buffer + virtual void issue() = 0; + + // CodeReview: Do we need a remove() [5/10/2007 Pat] + + /// This is a non-blocking call to get the status of the fence + /// @see GFXFence::FenceStatus + virtual FenceStatus getStatus() const = 0; + + /// This method will not return until the fence has been processed by the GPU + virtual void block() = 0; +}; + + +/// +class GFXGeneralFence : public GFXFence +{ +protected: + + bool mInitialized; + GFXTextureTargetRef mRenderTarget; + GFXTexHandle mRTTexHandle; + GFXStateBlockRef mRenderSB; + + void _init(); + + /// The callback used to get texture events. + /// @see GFXTextureManager::addEventDelegate + void _onTextureEvent( GFXTexCallbackCode code ); + +public: + + GFXGeneralFence( GFXDevice *device ) + : GFXFence( device ), + mInitialized( false ) + { + } + + virtual ~GFXGeneralFence(); + + virtual void issue(); + virtual FenceStatus getStatus() const { return GFXFence::Unsupported; }; + virtual void block(); + + // GFXResource interface + virtual void zombify(); + virtual void resurrect(); + /// The resource should put a description of itself (number of vertices, size/width of texture, etc.) in buffer + virtual const String describeSelf() const; +}; + +#endif // _GFXFENCE_H_ \ No newline at end of file diff --git a/Engine/source/gfx/gfxFontRenderBatcher.cpp b/Engine/source/gfx/gfxFontRenderBatcher.cpp new file mode 100644 index 000000000..cecdd52a2 --- /dev/null +++ b/Engine/source/gfx/gfxFontRenderBatcher.cpp @@ -0,0 +1,242 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/gfxFontRenderBatcher.h" +#include "gfx/gFont.h" + +FontRenderBatcher::FontRenderBatcher() : mStorage(8096) +{ + if (!mFontSB) + { + GFXStateBlockDesc f; + f.zDefined = true; + f.zEnable = false; + f.zWriteEnable = false; + f.cullDefined = true; + f.cullMode = GFXCullNone; + f.blendDefined = true; + f.blendEnable = true; + f.blendSrc = GFXBlendSrcAlpha; + f.blendDest = GFXBlendInvSrcAlpha; + f.samplersDefined = true; + f.samplers[0].alphaOp = GFXTOPModulate; + f.samplers[0].magFilter = GFXTextureFilterPoint; + f.samplers[0].minFilter = GFXTextureFilterPoint; + f.samplers[0].addressModeU = GFXAddressClamp; + f.samplers[0].addressModeV = GFXAddressClamp; + f.samplers[0].alphaArg1 = GFXTATexture; + f.samplers[0].alphaArg2 = GFXTADiffuse; + // This is an add operation because in D3D, when a texture of format D3DFMT_A8 + // is used, the RGB channels are all set to 0. Therefore a modulate would + // result in the text always being black. This may not be the case in OpenGL + // so it may have to change. -bramage + f.samplers[0].textureColorOp = GFXTOPAdd; + mFontSB = GFX->createStateBlock(f); + } +} + +void FontRenderBatcher::render( F32 rot, const Point2F &offset ) +{ + if( mLength == 0 ) + return; + + GFX->setStateBlock(mFontSB); + GFX->disableShaders(); + for(U32 i = 0; i < GFX->getNumSamplers(); i++) + GFX->setTexture(i, NULL); + + MatrixF rotMatrix; + + bool doRotation = rot != 0.f; + if(doRotation) + rotMatrix.set( EulerF( 0.0, 0.0, mDegToRad( rot ) ) ); + + // Write verts out. + U32 currentPt = 0; + GFXVertexBufferHandle verts(GFX, mLength * 6, GFXBufferTypeVolatile); + verts.lock(); + + for( S32 i = 0; i < mSheets.size(); i++ ) + { + // Do some early outs... + if(!mSheets[i]) + continue; + + if(!mSheets[i]->numChars) + continue; + + mSheets[i]->startVertex = currentPt; + const GFXTextureObject *tex = mFont->getTextureHandle(i); + + for( S32 j = 0; j < mSheets[i]->numChars; j++ ) + { + // Get some general info to proceed with... + const CharMarker &m = mSheets[i]->charIndex[j]; + const PlatformFont::CharInfo &ci = mFont->getCharInfo( m.c ); + + // Where are we drawing it? + F32 drawY = offset.y + mFont->getBaseline() - ci.yOrigin * TEXT_MAG; + F32 drawX = offset.x + m.x + ci.xOrigin; + + // Figure some values. + const F32 texWidth = (F32)tex->getWidth(); + const F32 texHeight = (F32)tex->getHeight(); + const F32 texLeft = (F32)(ci.xOffset) / texWidth; + const F32 texRight = (F32)(ci.xOffset + ci.width) / texWidth; + const F32 texTop = (F32)(ci.yOffset) / texHeight; + const F32 texBottom = (F32)(ci.yOffset + ci.height) / texHeight; + + const F32 fillConventionOffset = GFX->getFillConventionOffset(); + const F32 screenLeft = drawX - fillConventionOffset; + const F32 screenRight = drawX - fillConventionOffset + ci.width * TEXT_MAG; + const F32 screenTop = drawY - fillConventionOffset; + const F32 screenBottom = drawY - fillConventionOffset + ci.height * TEXT_MAG; + + // Build our vertices. We NEVER read back from the buffer, that's + // incredibly slow, so for rotation do it into tmp. This code is + // ugly as sin. + Point3F tmp; + + tmp.set( screenLeft, screenTop, 0.f ); + if(doRotation) + rotMatrix.mulP( tmp, &verts[currentPt].point); + else + verts[currentPt].point = tmp; + verts[currentPt].color = m.color; + verts[currentPt].texCoord.set( texLeft, texTop ); + currentPt++; + + tmp.set( screenLeft, screenBottom, 0.f ); + if(doRotation) + rotMatrix.mulP( tmp, &verts[currentPt].point); + else + verts[currentPt].point = tmp; + verts[currentPt].color = m.color; + verts[currentPt].texCoord.set( texLeft, texBottom ); + currentPt++; + + tmp.set( screenRight, screenBottom, 0.f ); + if(doRotation) + rotMatrix.mulP( tmp, &verts[currentPt].point); + else + verts[currentPt].point = tmp; + verts[currentPt].color = m.color; + verts[currentPt].texCoord.set( texRight, texBottom ); + currentPt++; + + tmp.set( screenRight, screenBottom, 0.f ); + if(doRotation) + rotMatrix.mulP( tmp, &verts[currentPt].point); + else + verts[currentPt].point = tmp; + verts[currentPt].color = m.color; + verts[currentPt].texCoord.set( texRight, texBottom ); + currentPt++; + + tmp.set( screenRight, screenTop, 0.f ); + if(doRotation) + rotMatrix.mulP( tmp, &verts[currentPt].point); + else + verts[currentPt].point = tmp; + verts[currentPt].color = m.color; + verts[currentPt].texCoord.set( texRight, texTop ); + currentPt++; + + tmp.set( screenLeft, screenTop, 0.f ); + if(doRotation) + rotMatrix.mulP( tmp, &verts[currentPt].point); + else + verts[currentPt].point = tmp; + verts[currentPt].color = m.color; + verts[currentPt].texCoord.set( texLeft, texTop ); + currentPt++; + } + } + + verts->unlock(); + + AssertFatal(currentPt <= mLength * 6, "FontRenderBatcher::render - too many verts for length of string!"); + + GFX->setVertexBuffer(verts); + + // Now do an optimal render! + for( S32 i = 0; i < mSheets.size(); i++ ) + { + if(!mSheets[i]) + continue; + + if(!mSheets[i]->numChars ) + continue; + + GFX->setupGenericShaders( GFXDevice::GSAddColorTexture ); + GFX->setTexture( 0, mFont->getTextureHandle(i) ); + GFX->drawPrimitive(GFXTriangleList, mSheets[i]->startVertex, mSheets[i]->numChars * 2); + } +} + +void FontRenderBatcher::queueChar( UTF16 c, S32 ¤tX, GFXVertexColor ¤tColor ) +{ + const PlatformFont::CharInfo &ci = mFont->getCharInfo( c ); + U32 sidx = ci.bitmapIndex; + + if( ci.width != 0 && ci.height != 0 ) + { + SheetMarker &sm = getSheetMarker(sidx); + + CharMarker &m = sm.charIndex[sm.numChars]; + sm.numChars++; + + m.c = c; + m.x = (F32)currentX; + m.color = currentColor; + } + + currentX += ci.xIncrement; +} + +FontRenderBatcher::SheetMarker & FontRenderBatcher::getSheetMarker( U32 sheetID ) +{ + // Allocate if it doesn't exist... + if(mSheets.size() <= sheetID || !mSheets[sheetID]) + { + if(sheetID >= mSheets.size()) + mSheets.setSize(sheetID+1); + + S32 size = sizeof( SheetMarker) + mLength * sizeof( CharMarker ); + mSheets[sheetID] = (SheetMarker *)mStorage.alloc(size); + mSheets[sheetID]->numChars = 0; + mSheets[sheetID]->startVertex = 0; // cosmetic initialization + } + + return *mSheets[sheetID]; +} + +void FontRenderBatcher::init( GFont *font, U32 n ) +{ + // Clear out batched results + dMemset(mSheets.address(), 0, mSheets.memSize()); + mSheets.clear(); + mStorage.freeBlocks(true); + + mFont = font; + mLength = n; +} \ No newline at end of file diff --git a/Engine/source/gfx/gfxFontRenderBatcher.h b/Engine/source/gfx/gfxFontRenderBatcher.h new file mode 100644 index 000000000..25f707db4 --- /dev/null +++ b/Engine/source/gfx/gfxFontRenderBatcher.h @@ -0,0 +1,67 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXFONTBATCHER_H_ +#define _GFXFONTBATCHER_H_ + +#include "core/dataChunker.h" +#include "gfx/gfxDevice.h" +#include "gfx/gFont.h" + +#define TEXT_MAG 1 + +class FontRenderBatcher +{ + struct CharMarker + { + S32 c; + F32 x; + GFXVertexColor color; + PlatformFont::CharInfo *ci; + }; + + struct SheetMarker + { + S32 numChars; + S32 startVertex; + CharMarker charIndex[1]; + }; + + DataChunker mStorage; + Vector mSheets; + GFont *mFont; + U32 mLength; + GFXStateBlockRef mFontSB; + + SheetMarker &getSheetMarker(U32 sheetID); + +public: + FontRenderBatcher(); + + void init(GFont *font, U32 n); + + void queueChar(UTF16 c, S32 ¤tX, GFXVertexColor ¤tColor); + + void render(F32 rot, const Point2F &offset ); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/gfx/gfxFormatUtils.cpp b/Engine/source/gfx/gfxFormatUtils.cpp new file mode 100644 index 000000000..1dc101d80 --- /dev/null +++ b/Engine/source/gfx/gfxFormatUtils.cpp @@ -0,0 +1,90 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/gfxFormatUtils.h" +#include "gfx/gfxDevice.h" + + +//RDTODO: complete format infos + + +//----------------------------------------------------------------------------- + +GFXFormatInfo::Data GFXFormatInfo::smFormatInfos[ GFXFormat_COUNT ] = +{ + // 8 bit texture formats... + GFXFormatInfo::Data( 1, false, false, false ), // GFXFormatA8 + GFXFormatInfo::Data( 1, false, false, false ), // GFXFormatL8 + GFXFormatInfo::Data( 1, true, false, false ), // GFXFormatA4L4 + + // 16 bit texture formats... + GFXFormatInfo::Data( 2, false, false, false ), // GFXFormatR5G6B5 + GFXFormatInfo::Data( 2, true, false, false ), // GFXFormatR5G5B5A1 + GFXFormatInfo::Data( 2, true, false, false ), // GFXFormatA8L8 + GFXFormatInfo::Data( 2, false, false, false ), // GFXFormatL16 + GFXFormatInfo::Data( 2, false, false, false ), // GFXFormatR16F + GFXFormatInfo::Data( 2, false, false, false ), // GFXFormatD16 + + // 24 bit texture formats... + GFXFormatInfo::Data( 3, false, false, false ), // GFXFormatR8G8B8 + + // 32 bit texture formats... + GFXFormatInfo::Data( 4, true, false, false ), // GFXFormatR8G8B8A8 + GFXFormatInfo::Data( 4, false, false, false ), // GFXFormatR8G8B8X8 + GFXFormatInfo::Data( 4, false, false, false ), // GFXFormatR32F + GFXFormatInfo::Data( 4, false, false, false ), // GFXFormatR16G16 + GFXFormatInfo::Data( 4, false, false, true ), // GFXFormatR16G16F + GFXFormatInfo::Data( 4, true, false, false ), // GFXFormatR10G10B10A2 + GFXFormatInfo::Data( 4, false, false, false ), // GFXFormatD32 + GFXFormatInfo::Data( 4, false, false, false ), // GFXFormatD24X8 + GFXFormatInfo::Data( 4, false, false, false ), // GFXFormatD24S8 + GFXFormatInfo::Data( 4, false, false, false ), // GFXFormatD24FS8 + + // 64 bit texture formats... + GFXFormatInfo::Data( 8, true, false, false ), // GFXFormatR16G16B16A16 + GFXFormatInfo::Data( 8, true, false, true ), // GFXFormatR16G16B16A16F + + // 128 bit texture formats... + GFXFormatInfo::Data( 16, true, false, true ), // GFXFormatR32G32B32A32F + + // Compressed formats... + GFXFormatInfo::Data( 0, false, true, false ), // GFXFormatDXT1 + GFXFormatInfo::Data( 0, true, true, false ), // GFXFormatDXT2 + GFXFormatInfo::Data( 0, true, true, false ), // GFXFormatDXT3 + GFXFormatInfo::Data( 0, true, true, false ), // GFXFormatDXT4 + GFXFormatInfo::Data( 0, true, true, false ), // GFXFormatDXT5 +}; + +//----------------------------------------------------------------------------- + +void GFXCopyPixels( GFXFormat fromFormat, U32 fromWidth, U32 fromHeight, U8* fromData, + GFXFormat toFormat, U32 toWidth, U32 toHeight, U8* toData ) +{ + if( fromFormat == toFormat + && fromWidth == toWidth + && fromHeight == fromHeight ) + dMemcpy( toData, fromData, fromWidth * fromHeight * GFXFormatInfo( fromFormat ).getBytesPerPixel() ); + else + { + AssertFatal( false, "Not implemented" ); + } +} diff --git a/Engine/source/gfx/gfxFormatUtils.h b/Engine/source/gfx/gfxFormatUtils.h new file mode 100644 index 000000000..9000dcf3a --- /dev/null +++ b/Engine/source/gfx/gfxFormatUtils.h @@ -0,0 +1,143 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXFORMATUTILS_H_ +#define _GFXFORMATUTILS_H_ + +#ifndef _PLATFORM_H_ + #include "platform/platform.h" +#endif +#ifndef _GFXENUMS_H_ + #include "gfx/gfxEnums.h" +#endif +#ifndef _COLOR_H_ + #include "core/color.h" +#endif + + +//WIP: still in early stages + + + +/// Some information about a GFXFormat. +struct GFXFormatInfo +{ + protected: + + struct Data + { + /// Bytes per single pixel. + U32 mBytesPerPixel; + + /// If true, format has alpha channel. + bool mHasAlpha; + + /// If true, format uses compression. + bool mIsCompressed; + + /// If true, channels are in floating-point. + bool mIsFloatingPoint; + + Data() {} + Data( U32 bpp, bool hasAlpha = false, bool isCompressed = false, bool isFP = false ) + : mBytesPerPixel( bpp ), + mHasAlpha( hasAlpha ), + mIsCompressed( isCompressed ), + mIsFloatingPoint( isFP ) {} + }; + + GFXFormat mFormat; + + static Data smFormatInfos[ GFXFormat_COUNT ]; + + public: + + GFXFormatInfo( GFXFormat format ) + : mFormat( format ) {} + + /// @return the number of bytes per pixel in this format. + /// @note For compressed formats that can't give a fixed value per pixel, + /// this will be zero. + U32 getBytesPerPixel() const { return smFormatInfos[ mFormat ].mBytesPerPixel; } + + /// @return true if the format has an alpha channel. + bool hasAlpha() const { return smFormatInfos[ mFormat ].mHasAlpha; } + + /// @return true if format uses compression. + bool isCompressed() const { return smFormatInfos[ mFormat ].mIsCompressed; } + + /// @return true if channels are stored in floating-point format. + bool isFloatingPoint() const { return smFormatInfos[ mFormat ].mIsFloatingPoint; } +}; + + +#if 0 +/// +extern void GFXCopyPixels( GFXFormat fromFormat, U32 fromWidth, U32 fromHeight, U8* fromData, + GFXFormat toFormat, U32 toWidth, U32 toHeight, U8* toData ); +#endif + + +inline void GFXPackPixel( GFXFormat format, U8*& ptr, U8 red, U8 green, U8 blue, U8 alpha, bool leastSignficantFirst = true ) +{ + switch( format ) + { + case GFXFormatR8G8B8A8: + if( leastSignficantFirst ) + { + ptr[ 0 ] = blue; + ptr[ 1 ] = green; + ptr[ 2 ] = red; + ptr[ 3 ] = alpha; + } + else + { + ptr[ 0 ] = red; + ptr[ 1 ] = green; + ptr[ 2 ] = blue; + ptr[ 3 ] = alpha; + } + ptr += 4; + break; + + case GFXFormatR8G8B8: + if( leastSignficantFirst ) + { + ptr[ 0 ] = blue; + ptr[ 1 ] = green; + ptr[ 2 ] = red; + } + else + { + ptr[ 0 ] = red; + ptr[ 1 ] = green; + ptr[ 2 ] = blue; + } + ptr += 3; + break; + + default: + AssertISV( false, "GFXPackPixel() - pixel format not implemented." ); + } +} + +#endif // _GFXFORMATUTILS_H_ diff --git a/Engine/source/gfx/gfxInit.cpp b/Engine/source/gfx/gfxInit.cpp new file mode 100644 index 000000000..304e317b4 --- /dev/null +++ b/Engine/source/gfx/gfxInit.cpp @@ -0,0 +1,499 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/gfxInit.h" + +#include "gfx/gfxTextureManager.h" +#include "gfx/gfxAPI.h" +#include "console/console.h" +#include "windowManager/platformWindowMgr.h" +#include "core/module.h" + + +Vector GFXInit::smAdapters( __FILE__, __LINE__ ); +GFXInit::RegisterDeviceSignal* GFXInit::smRegisterDeviceSignal; + + +MODULE_BEGIN( GFX ) + + MODULE_INIT + { + GFXInit::init(); + if( engineAPI::gUseConsoleInterop ) + GFXDevice::initConsole(); + GFXTextureManager::init(); + } + + MODULE_SHUTDOWN + { + GFXDevice::destroy(); + GFXInit::cleanup(); + } + +MODULE_END; + +IMPLEMENT_STATIC_CLASS( GFXInit, GFXAPI, + "Functions for tracking GFX adapters and initializing them into devices." +); + +ConsoleDoc( + "@class GFXInit\n" + "@ingroup GFX\n" + "@brief Functions for tracking GFX adapters and initializing them into devices.\n" +); + +inline static void _GFXInitReportAdapters(Vector &adapters) +{ + for (U32 i = 0; i < adapters.size(); i++) + { + switch (adapters[i]->mType) + { + case Direct3D9: + Con::printf(" Direct 3D (version 9.x) device found"); + break; + case OpenGL: + Con::printf(" OpenGL device found"); + break; + case NullDevice: + Con::printf(" Null device found"); + break; + case Direct3D8: + Con::printf(" Direct 3D (version 8.1) device found"); + break; + default : + Con::printf(" Unknown device found"); + break; + } + } +} + +inline static void _GFXInitGetInitialRes(GFXVideoMode &vm, const Point2I &initialSize) +{ + const U32 kDefaultWindowSizeX = 800; + const U32 kDefaultWindowSizeY = 600; + const bool kDefaultFullscreen = false; + const U32 kDefaultBitDepth = 32; + const U32 kDefaultRefreshRate = 60; + + // cache the desktop size of the main screen + GFXVideoMode desktopVm = GFXInit::getDesktopResolution(); + + // load pref variables, properly choose windowed / fullscreen + const String resString = Con::getVariable("$pref::Video::mode"); + + // Set defaults into the video mode, then have it parse the user string. + vm.resolution.x = kDefaultWindowSizeX; + vm.resolution.y = kDefaultWindowSizeY; + vm.fullScreen = kDefaultFullscreen; + vm.bitDepth = kDefaultBitDepth; + vm.refreshRate = kDefaultRefreshRate; + vm.wideScreen = false; + + vm.parseFromString(resString); +} + +GFXInit::RegisterDeviceSignal& GFXInit::getRegisterDeviceSignal() +{ + if (smRegisterDeviceSignal) + return *smRegisterDeviceSignal; + smRegisterDeviceSignal = new RegisterDeviceSignal(); + return *smRegisterDeviceSignal; +} + +void GFXInit::init() +{ + // init only once. + static bool doneOnce = false; + if(doneOnce) + return; + doneOnce = true; + + Con::printf( "GFX Init:" ); + + //find our adapters + Vector adapters( __FILE__, __LINE__ ); + GFXInit::enumerateAdapters(); + GFXInit::getAdapters(&adapters); + + if(!adapters.size()) + Con::errorf("Could not find a display adapter"); + + //loop through and tell the user what kind of adapters we found + _GFXInitReportAdapters(adapters); + Con::printf( "" ); +} + +void GFXInit::cleanup() +{ + while( smAdapters.size() ) + { + GFXAdapter* adapter = smAdapters.last(); + smAdapters.decrement(); + delete adapter; + } + + if( smRegisterDeviceSignal ) + SAFE_DELETE( smRegisterDeviceSignal ); +} + +GFXAdapter* GFXInit::getAdapterOfType( GFXAdapterType type ) +{ + GFXAdapter* adapter = NULL; + for( U32 i = 0; i < smAdapters.size(); i++ ) + { + if( smAdapters[i]->mType == type ) + { + adapter = smAdapters[i]; + break; + } + } + return adapter; +} + +GFXAdapter* GFXInit::chooseAdapter( GFXAdapterType type) +{ + GFXAdapter* adapter = GFXInit::getAdapterOfType(type); + + if(!adapter && type != OpenGL) + { + Con::errorf("The requested renderer, %s, doesn't seem to be available." + " Trying the default, OpenGL.", getAdapterNameFromType(type)); + adapter = GFXInit::getAdapterOfType(OpenGL); + } + + if(!adapter) + { + Con::errorf("The OpenGL renderer doesn't seem to be available. Trying the GFXNulDevice."); + adapter = GFXInit::getAdapterOfType(NullDevice); + } + + AssertFatal( adapter, "There is no rendering device available whatsoever."); + return adapter; +} + +const char* GFXInit::getAdapterNameFromType(GFXAdapterType type) +{ + static const char* _names[] = { "OpenGL", "D3D9", "D3D8", "NullDevice", "Xenon" }; + + if( type < 0 || type >= GFXAdapterType_Count ) + { + Con::errorf( "GFXInit::getAdapterNameFromType - Invalid renderer type, defaulting to OpenGL" ); + return _names[OpenGL]; + } + + return _names[type]; +} + +GFXAdapterType GFXInit::getAdapterTypeFromName(const char* name) +{ + S32 ret = -1; + for(S32 i = 0; i < GFXAdapterType_Count; i++) + { + if( !dStricmp( getAdapterNameFromType((GFXAdapterType)i), name ) ) + ret = i; + } + + if( ret == -1 ) + { + Con::errorf( "GFXInit::getAdapterTypeFromName - Invalid renderer name, defaulting to D3D9" ); + ret = Direct3D9; + } + + return (GFXAdapterType)ret; +} + +GFXAdapter *GFXInit::getBestAdapterChoice() +{ + // Get the user's preference for device... + const String renderer = Con::getVariable("$pref::Video::displayDevice"); + GFXAdapterType adapterType = getAdapterTypeFromName(renderer); + GFXAdapter *adapter = chooseAdapter(adapterType); + + // Did they have one? Return it. + if(adapter) + return adapter; + + // Didn't have one. So make it up. Find the highest SM available. Prefer + // D3D to GL because if we have a D3D device at all we're on windows, + // and in an unknown situation on Windows D3D is probably the safest bet. + // + // If D3D is unavailable, we're not on windows, so GL is de facto the + // best choice! + F32 highestSM9 = 0.f, highestSMGL = 0.f; + GFXAdapter *foundAdapter8 = NULL, *foundAdapter9 = NULL, + *foundAdapterGL = NULL; + + for(S32 i=0; imType) + { + case Direct3D9: + if(currAdapter->mShaderModel > highestSM9) + { + highestSM9 = currAdapter->mShaderModel; + foundAdapter9 = currAdapter; + } + break; + + case OpenGL: + if(currAdapter->mShaderModel > highestSMGL) + { + highestSMGL = currAdapter->mShaderModel; + foundAdapterGL = currAdapter; + } + break; + + case Direct3D8: + if(!foundAdapter8) + foundAdapter8 = currAdapter; + break; + + default: + break; + } + } + + // Return best found in order DX9, GL, DX8. + if(foundAdapter9) + return foundAdapter9; + + if(foundAdapterGL) + return foundAdapterGL; + + if(foundAdapter8) + return foundAdapter8; + + // Uh oh - we didn't find anything. Grab whatever we can that's not Null... + for(S32 i=0; imType != NullDevice) + return smAdapters[i]; + + // Dare we return a null device? No. Just return NULL. + return NULL; +} + +GFXVideoMode GFXInit::getInitialVideoMode() +{ + GFXVideoMode vm; + _GFXInitGetInitialRes(vm, Point2I(800,600)); + return vm; +} + +S32 GFXInit::getAdapterCount() +{ + return smAdapters.size(); +} + +void GFXInit::getAdapters(Vector *adapters) +{ + adapters->clear(); + for (U32 k = 0; k < smAdapters.size(); k++) + adapters->push_back(smAdapters[k]); +} + +GFXVideoMode GFXInit::getDesktopResolution() +{ + GFXVideoMode resVm; + + // Retrieve Resolution Information. + resVm.bitDepth = WindowManager->getDesktopBitDepth(); + resVm.resolution = WindowManager->getDesktopResolution(); + resVm.fullScreen = false; + resVm.refreshRate = 60; + + // Return results + return resVm; +} + +void GFXInit::enumerateAdapters() +{ + // Call each device class and have it report any adapters it supports. + if(smAdapters.size()) + { + // CodeReview Seems like this is ok to just ignore? [bjg, 5/19/07] + //Con::warnf("GFXInit::enumerateAdapters - already have a populated adapter list, aborting re-analysis."); + return; + } + + getRegisterDeviceSignal().trigger(GFXInit::smAdapters); +} + +GFXDevice *GFXInit::createDevice( GFXAdapter *adapter ) +{ + Con::printf("Attempting to create GFX device: %s", adapter->getName()); + + GFXDevice* temp = adapter->mCreateDeviceInstanceDelegate(adapter->mIndex); + if (temp) + { + Con::printf("Device created, setting adapter and enumerating modes"); + temp->setAdapter(*adapter); + temp->enumerateVideoModes(); + temp->getVideoModeList(); + } + else + Con::errorf("Failed to create GFX device"); + + GFXDevice::getDeviceEventSignal().trigger(GFXDevice::deCreate); + + return temp; +} + + +DefineEngineFunction( getDesktopResolution, Point3F, (),, + "Returns the width, height, and bitdepth of the screen/desktop.\n\n@ingroup GFX" ) +{ + GFXVideoMode res = GFXInit::getDesktopResolution(); + return Point3F( res.resolution.x, res.resolution.y, res.bitDepth ); +} + +DefineEngineStaticMethod( GFXInit, getAdapterCount, S32, (),, + "Return the number of graphics adapters available. @ingroup GFX") +{ + return GFXInit::getAdapterCount(); +} + +DefineEngineStaticMethod( GFXInit, getAdapterName, String, ( S32 index ),, + "Returns the name of the graphics adapter.\n" + "@param index The index of the adapter." ) +{ + Vector adapters( __FILE__, __LINE__ ); + GFXInit::getAdapters(&adapters); + + if(index >= 0 && index < adapters.size()) + return adapters[index]->mName; + + Con::errorf( "GFXInit::getAdapterName - Out of range adapter index." ); + return String::EmptyString; +} + +DefineEngineStaticMethod( GFXInit, getAdapterType, GFXAdapterType, ( S32 index ),, + "Returns the type (D3D9, D3D8, GL, Null) of a graphics adapter.\n" + "@param index The index of the adapter." ) +{ + Vector adapters( __FILE__, __LINE__ ); + GFXInit::getAdapters(&adapters); + + if( index >= 0 && index < adapters.size()) + return adapters[index]->mType; + + Con::errorf( "GFXInit::getAdapterType - Out of range adapter index." ); + return GFXAdapterType_Count; +} + +DefineEngineStaticMethod( GFXInit, getAdapterShaderModel, F32, ( S32 index ),, + "Returns the supported shader model of the graphics adapter or -1 if the index is bad.\n" + "@param index The index of the adapter." ) +{ + Vector adapters( __FILE__, __LINE__ ); + GFXInit::getAdapters(&adapters); + + if(index < 0 || index >= adapters.size()) + { + Con::errorf("GFXInit::getAdapterShaderModel - Out of range adapter index."); + return -1.0f; + } + + return adapters[index]->mShaderModel; +} + +DefineEngineStaticMethod( GFXInit, getDefaultAdapterIndex, S32, (),, + "Returns the index of the default graphics adapter. This is the graphics device " + "which will be used to initialize the engine." ) +{ + GFXAdapter *a = GFXInit::getBestAdapterChoice(); + + // We have to find the index of the adapter in the list to + // return an index that makes sense to the script. + Vector adapters( __FILE__, __LINE__ ); + GFXInit::getAdapters(&adapters); + for(S32 i=0; imIndex == a->mIndex && + adapters[i]->mType == a->mType ) + return i; + + Con::warnf( "GFXInit::getDefaultAdapterIndex - Didn't find the adapter in the list!" ); + return -1; +} + +DefineEngineStaticMethod( GFXInit, getAdapterModeCount, S32, ( S32 index ),, + "Gets the number of modes available on the specified adapter.\n\n" + "@param index Index of the adapter to get modes from.\n" + "@return The number of video modes supported by the adapter or -1 if the given adapter was not found." ) +{ + Vector adapters( __FILE__, __LINE__ ); + GFXInit::getAdapters(&adapters); + + if( index < 0 || index >= adapters.size()) + { + Con::warnf( "GFXInit::getAdapterModeCount - The index was out of range." ); + return -1; + } + + return adapters[index]->mAvailableModes.size(); +} + +DefineConsoleStaticMethod( GFXInit, getAdapterMode, String, ( S32 index, S32 modeIndex ),, + "Gets the details of the specified adapter mode.\n\n" + "@param index Index of the adapter to query.\n" + "@param modeIndex Index of the mode to get data from.\n" + "@return A video mode string in the format 'width height fullscreen bitDepth refreshRate aaLevel'.\n" + "@see GuiCanvas::getVideoMode()" ) +{ + Vector adapters( __FILE__, __LINE__ ); + GFXInit::getAdapters(&adapters); + + if ( index < 0 || index >= adapters.size() ) + { + Con::warnf( "GFXInit::getAdapterMode - The adapter index was out of range." ); + return String::EmptyString; + } + + if ( modeIndex < 0 || modeIndex >= adapters[index]->mAvailableModes.size() ) + { + Con::warnf( "GFXInit::getAdapterMode - The mode index was out of range." ); + return String::EmptyString; + } + + const GFXVideoMode &vm = adapters[index]->mAvailableModes[modeIndex]; + return vm.toString(); +} + +DefineEngineStaticMethod( GFXInit, createNullDevice, void, (),, + "Create the NULL graphics device used for testing or headless operation." ) +{ + // Enumerate things for GFX before we have an active device. + GFXInit::enumerateAdapters(); + + // Create a device. + GFXAdapter *a = GFXInit::chooseAdapter(NullDevice); + + GFXDevice *newDevice = GFX; + + // Do we have a global device already? (This is the site if you want + // to start rendering to multiple devices simultaneously) + if(newDevice == NULL) + newDevice = GFXInit::createDevice(a); + + newDevice->setAllowRender( false ); +} diff --git a/Engine/source/gfx/gfxInit.h b/Engine/source/gfx/gfxInit.h new file mode 100644 index 000000000..6e2d9d8b3 --- /dev/null +++ b/Engine/source/gfx/gfxInit.h @@ -0,0 +1,101 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXINIT_H_ +#define _GFXINIT_H_ + +#ifndef _GFXDEVICE_H_ + #include "gfx/gfxDevice.h" +#endif +#ifndef _ENGINEOBJECT_H_ + #include "console/engineObject.h" +#endif + + +/// Interface for tracking GFX adapters and initializing them into devices. +/// @note Implement this class per platform. +/// @note This is just a class so it can be friends with GFXDevice) +class GFXInit +{ + DECLARE_STATIC_CLASS( GFXInit ); + +public: + /// Allows device to register themselves as available + typedef Signal&)> RegisterDeviceSignal; + static RegisterDeviceSignal& getRegisterDeviceSignal(); + + /// Prepares the adapter list. + static void init(); + + /// Cleans out the adapter list. + static void cleanup(); + + /// Creates a GFXDevice based on an adapter from the + /// enumerateAdapters method. + /// + /// @param adapter Graphics adapter to create device + static GFXDevice *createDevice( GFXAdapter *adapter ); + + /// Enumerate all the graphics adapters on the system + static void enumerateAdapters(); + + /// Get the enumerated adapters. Should only call this after + /// a call to enumerateAdapters. + static void getAdapters( Vector *adapters ); + + /// Get the number of available adapters. + static S32 getAdapterCount(); + + /// Chooses a suitable GFXAdapter, based on type, preferences, and fallbacks. + /// If the requested type is omitted, we use the prefs value. + /// If the requested type isn't found, we use fallbacks: OpenGL, NullDevice + /// This method never returns NULL. + static GFXAdapter *chooseAdapter( GFXAdapterType type); + + /// Gets the first adapter of the requested type from the list of enumerated + /// adapters. Should only call this after a call to enumerateAdapters. + static GFXAdapter *getAdapterOfType( GFXAdapterType type ); + + /// Converts a GFXAdapterType to a string name. Useful for writing out prefs + static const char *getAdapterNameFromType( GFXAdapterType type ); + + /// Converts a string to a GFXAdapterType. Useful for reading in prefs. + static GFXAdapterType getAdapterTypeFromName( const char* name ); + + /// Returns a GFXVideoMode that describes the current state of the main monitor. + /// This should probably move to the abstract window manager + static GFXVideoMode getDesktopResolution(); + + /// Based on user preferences (or in the absence of a valid user selection, + /// a heuristic), return a "best" adapter. + static GFXAdapter *getBestAdapterChoice(); + + /// Get the initial video mode based on user preferences (or a heuristic). + static GFXVideoMode getInitialVideoMode(); +private: + /// List of known adapters. + static Vector smAdapters; + + static RegisterDeviceSignal* smRegisterDeviceSignal; +}; + +#endif diff --git a/Engine/source/gfx/gfxOcclusionQuery.cpp b/Engine/source/gfx/gfxOcclusionQuery.cpp new file mode 100644 index 000000000..3ee267cd4 --- /dev/null +++ b/Engine/source/gfx/gfxOcclusionQuery.cpp @@ -0,0 +1,43 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/gfxOcclusionQuery.h" + +String GFXOcclusionQuery::statusToString( OcclusionQueryStatus status ) +{ + String outStr; + if ( status == GFXOcclusionQuery::Unset ) + outStr = "Unset"; + else if ( status == GFXOcclusionQuery::Waiting ) + outStr = "Waiting"; + else if ( status == GFXOcclusionQuery::Occluded ) + outStr = "Occluded"; + else if ( status == GFXOcclusionQuery::NotOccluded ) + outStr = "Visible"; + else if ( status == GFXOcclusionQuery::Error ) + outStr = "Error"; + else + outStr = "Unknown"; + + return outStr; +} diff --git a/Engine/source/gfx/gfxOcclusionQuery.h b/Engine/source/gfx/gfxOcclusionQuery.h new file mode 100644 index 000000000..9bf87c46d --- /dev/null +++ b/Engine/source/gfx/gfxOcclusionQuery.h @@ -0,0 +1,85 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXOCCLUSIONQUERY_H_ +#define _GFXOCCLUSIONQUERY_H_ + +#ifndef _GFXDEVICE_H_ +#include "gfx/gfxDevice.h" +#endif + + +/// A geometry visibility query object. +/// @see GFXDevice::createOcclusionQuery +class GFXOcclusionQuery : public GFXResource +{ +protected: + + GFXDevice *mDevice; + + GFXOcclusionQuery( GFXDevice *device ) + : mDevice( device ) + { + } + +public: + + /// The states returned by getStatus() + /// If you modify this enum you should also modify statusToString() + enum OcclusionQueryStatus + { + Unset, ///< + Waiting, ///< + Error, ///< + Occluded, + NotOccluded + }; + + virtual ~GFXOcclusionQuery() {} + + /// Prepares the query returning true if the last query + /// has been processed and more geometry can be issued. + /// @see getStatus + virtual bool begin() = 0; + + /// Called after your geometry is drawn to submit + /// the query for processing. + virtual void end() = 0; + + /// Returns the status of the last submitted query. In general + /// you should avoid blocking for the result until the frame + /// following your query to keep from stalling the CPU. + /// @return Status + /// @param block If true CPU will block until the query finishes. + /// @param data Number of pixels rendered, valid only if status returned is NotOccluded. + virtual OcclusionQueryStatus getStatus( bool block, U32 *data = NULL ) = 0; + + /// Returns a status string. + static String statusToString( OcclusionQueryStatus status ); + + // GFXResource + virtual void zombify() = 0; + virtual void resurrect() = 0; + virtual const String describeSelf() const = 0; +}; + +#endif // _GFXOCCLUSIONQUERY_H_ \ No newline at end of file diff --git a/Engine/source/gfx/gfxPrimitiveBuffer.cpp b/Engine/source/gfx/gfxPrimitiveBuffer.cpp new file mode 100644 index 000000000..9a0a09119 --- /dev/null +++ b/Engine/source/gfx/gfxPrimitiveBuffer.cpp @@ -0,0 +1,82 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/strings/stringFunctions.h" +#include "console/console.h" + +#include "gfx/gfxDevice.h" +#include "gfx/gfxPrimitiveBuffer.h" + +//----------------------------------------------------------------------------- +#ifdef TORQUE_DEBUG +GFXPrimitiveBuffer *GFXPrimitiveBuffer::smHead = NULL; +U32 GFXPrimitiveBuffer::smActivePBCount = 0; + +void GFXPrimitiveBuffer::dumpActivePBs() +{ + if(!smActivePBCount) + { + Con::printf("GFXPrimitiveBuffer::dumpActivePBs - no currently active PBs to dump. You are A-OK!"); + return; + } + + Con::printf("GFXPrimitiveBuffer Usage Report - %d active PBs", smActivePBCount); + Con::printf("---------------------------------------------------------------"); + Con::printf(" Addr #idx #prims Profiler Path RefCount"); + for(GFXPrimitiveBuffer *walk = smHead; walk; walk=walk->mDebugNext) + { +#if defined(TORQUE_ENABLE_PROFILER) + Con::printf(" %x %6d %6d %s %d", walk, walk->mIndexCount, walk->mPrimitiveCount, walk->mDebugCreationPath.c_str(), walk->getRefCount()); +#else + Con::printf(" %x %6d %6d %s %d", walk, walk->mIndexCount, walk->mPrimitiveCount, "", walk->getRefCount()); +#endif + } + Con::printf("----- dump complete -------------------------------------------"); + AssertFatal(false, "There is a primitive buffer leak, check the log for more details."); +} + +#endif + +/// The resource should put a description of itself (number of vertices, size/width of texture, etc.) in buffer +const String GFXPrimitiveBuffer::describeSelf() const +{ +#if defined(TORQUE_DEBUG) && defined(TORQUE_ENABLE_PROFILER) + return String::ToString("indexCount: %6d primCount: %6d refCount: %d path: %s", + mIndexCount, mPrimitiveCount, getRefCount(), mDebugCreationPath.c_str()); +#else + return String::ToString("indexCount: %6d primCount: %6d refCount: %d path: %s", + mIndexCount, mPrimitiveCount, getRefCount(), ""); +#endif +} + +//----------------------------------------------------------------------------- +// Set +//----------------------------------------------------------------------------- +void GFXPrimitiveBufferHandle::set(GFXDevice *theDevice, U32 indexCount, U32 primitiveCount, GFXBufferType bufferType, String desc) +{ + StrongRefPtr::operator=( theDevice->allocPrimitiveBuffer(indexCount, primitiveCount, bufferType) ); + +#ifdef TORQUE_DEBUG + if( desc.isNotEmpty() ) + getPointer()->mDebugCreationPath = desc; +#endif +} diff --git a/Engine/source/gfx/gfxPrimitiveBuffer.h b/Engine/source/gfx/gfxPrimitiveBuffer.h new file mode 100644 index 000000000..c2955786c --- /dev/null +++ b/Engine/source/gfx/gfxPrimitiveBuffer.h @@ -0,0 +1,175 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXPRIMITIVEBUFFER_H_ +#define _GFXPRIMITIVEBUFFER_H_ + +#ifndef _GFXSTRUCTS_H_ +#include "gfx/gfxStructs.h" +#endif + +#ifdef TORQUE_ENABLE_PROFILER +#include "platform/profiler.h" +#endif + + +class GFXPrimitiveBuffer : public StrongRefBase, public GFXResource +{ + friend class GFXPrimitiveBufferHandle; + friend class GFXDevice; + +public: //protected: + + U32 mIndexCount; + U32 mPrimitiveCount; + GFXBufferType mBufferType; + GFXPrimitive *mPrimitiveArray; + GFXDevice *mDevice; + +#ifdef TORQUE_DEBUG + // In debug builds we provide a TOC leak tracking system. + static U32 smActivePBCount; + static GFXPrimitiveBuffer *smHead; + static void dumpActivePBs(); + + String mDebugCreationPath; + GFXPrimitiveBuffer *mDebugNext; + GFXPrimitiveBuffer *mDebugPrev; +#endif + + GFXPrimitiveBuffer( GFXDevice *device, + U32 indexCount, + U32 primitiveCount, + GFXBufferType bufferType ) + { + mDevice = device; + mIndexCount = indexCount; + mPrimitiveCount = primitiveCount; + mBufferType = bufferType; + if(primitiveCount) + { + mPrimitiveArray = new GFXPrimitive[primitiveCount]; + dMemset((void *) mPrimitiveArray, 0, primitiveCount * sizeof(GFXPrimitive)); + } + else + mPrimitiveArray = NULL; + +#if defined(TORQUE_DEBUG) + // Active copy tracking. + smActivePBCount++; +#if defined(TORQUE_ENABLE_PROFILE_PATH) + mDebugCreationPath = gProfiler->getProfilePath(); +#endif + mDebugNext = smHead; + mDebugPrev = NULL; + + if(smHead) + { + AssertFatal(smHead->mDebugPrev == NULL, "GFXPrimitiveBuffer::GFXPrimitiveBuffer - found unexpected previous in current head!"); + smHead->mDebugPrev = this; + } + + smHead = this; +#endif + } + + virtual ~GFXPrimitiveBuffer() + { + if( mPrimitiveArray != NULL ) + { + delete [] mPrimitiveArray; + mPrimitiveArray = NULL; + } + +#ifdef TORQUE_DEBUG + if(smHead == this) + smHead = this->mDebugNext; + + if(mDebugNext) + mDebugNext->mDebugPrev = mDebugPrev; + + if(mDebugPrev) + mDebugPrev->mDebugNext = mDebugNext; + + mDebugPrev = mDebugNext = NULL; + + smActivePBCount--; +#endif + } + + virtual void lock(U32 indexStart, U32 indexEnd, void **indexPtr)=0; ///< locks this primitive buffer for writing into + virtual void unlock()=0; ///< unlocks this primitive buffer. + virtual void prepare()=0; ///< prepares this primitive buffer for use on the device it was allocated on + + // GFXResource interface + /// The resource should put a description of itself (number of vertices, size/width of texture, etc.) in buffer + virtual const String describeSelf() const; +}; + +class GFXPrimitiveBufferHandle : public StrongRefPtr +{ + typedef StrongRefPtr Parent; +public: + enum Constants { + MaxIndexCount = 65535, + }; + GFXPrimitiveBufferHandle() {}; + + GFXPrimitiveBufferHandle(GFXDevice *theDevice, U32 indexCount, U32 primitiveCount, GFXBufferType bufferType, String desc = String::EmptyString ) + { + set(theDevice, indexCount, primitiveCount, bufferType, desc); + } + + void set(GFXDevice *theDevice, U32 indexCount, U32 primitiveCount, GFXBufferType bufferType, String desc = String::EmptyString ); + + void lock(U16 **indexBuffer, GFXPrimitive **primitiveBuffer = NULL, U32 indexStart = 0, U32 indexEnd = 0) + { + if(indexEnd == 0) + indexEnd = getPointer()->mIndexCount; + AssertFatal(indexStart < indexEnd && indexEnd <= getPointer()->mIndexCount, "Out of range index lock!"); + getPointer()->lock(indexStart, indexEnd, (void**)indexBuffer); + if(primitiveBuffer) + *primitiveBuffer = getPointer()->mPrimitiveArray; + } + + void unlock() + { + getPointer()->unlock(); + } + + void prepare() + { + getPointer()->prepare(); + } + + bool operator==(const GFXPrimitiveBufferHandle &buffer) const { + return getPointer() == buffer.getPointer(); + } + + GFXPrimitiveBufferHandle& operator=(GFXPrimitiveBuffer *ptr) + { + StrongObjectRef::set(ptr); + return *this; + } +}; + +#endif // _GFXPRIMITIVEBUFFER_H_ diff --git a/Engine/source/gfx/gfxResource.cpp b/Engine/source/gfx/gfxResource.cpp new file mode 100644 index 000000000..d06ae9587 --- /dev/null +++ b/Engine/source/gfx/gfxResource.cpp @@ -0,0 +1,60 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/gfxResource.h" +#include "gfx/gfxDevice.h" + +GFXResource::GFXResource() +{ + mPrevResource = mNextResource = NULL; + mOwningDevice = NULL; + mFlagged = false; +} + +GFXResource::~GFXResource() +{ + // Make sure we're not the head of the list and referencd on the device. + if(mOwningDevice && mOwningDevice->mResourceListHead == this) + { + AssertFatal(mPrevResource == NULL, + "GFXResource::~GFXResource - head of list but have a previous item!"); + mOwningDevice->mResourceListHead = mNextResource; + } + + // Unlink ourselves from the list. + if(mPrevResource) + mPrevResource->mNextResource = mNextResource; + if(mNextResource) + mNextResource->mPrevResource = mPrevResource; + + mPrevResource = mNextResource = NULL; +} + +void GFXResource::registerResourceWithDevice( GFXDevice *device ) +{ + mOwningDevice = device; + mNextResource = device->mResourceListHead; + device->mResourceListHead = this; + + if(mNextResource) + mNextResource->mPrevResource = this; +} \ No newline at end of file diff --git a/Engine/source/gfx/gfxResource.h b/Engine/source/gfx/gfxResource.h new file mode 100644 index 000000000..43420dbe2 --- /dev/null +++ b/Engine/source/gfx/gfxResource.h @@ -0,0 +1,106 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXRESOURCE_H_ +#define _GFXRESOURCE_H_ + +#ifndef _TORQUE_TYPES_H_ +#include "platform/types.h" +#endif + +#ifndef _TORQUE_STRING_H_ +#include "core/util/str.h" +#endif + +class GFXDevice; + +/// Mixin for the purpose of tracking GFX resources owned by a GFXDevice. +/// +/// There are many types of resource that are allocated from a GFXDevice that +/// must be participatory in device resets. For instance, all default pool +/// DirectX resources have to be involved when the device resets. Render +/// targets in all APIs need to unbind themselves when resets happen. +/// +/// This system is also handy for accounting purposes. For instance, we may +/// want to traverse all registered VBs, IBs, Textures, or RTs in order to +/// determine what, if any, items are still allocated. This can be used in +/// leak reports, memory usage reports, etc. +class GFXResource +{ +private: + friend class GFXDevice; + + GFXResource *mPrevResource; + GFXResource *mNextResource; + GFXDevice *mOwningDevice; + + /// Helper flag to check new resource allocations + bool mFlagged; + +public: + GFXResource(); + virtual ~GFXResource(); + + /// Registers this resource with the given device + void registerResourceWithDevice(GFXDevice *device); + + /// When called the resource should destroy all device sensitive information (e.g. D3D resources in D3DPOOL_DEFAULT + virtual void zombify()=0; + + /// When called the resource should restore all device sensitive information destroyed by zombify() + virtual void resurrect()=0; + + /// The resource should put a description of itself (number of vertices, size/width of texture, etc.) in buffer + virtual const String describeSelf() const = 0; + + inline GFXResource *getNextResource() const + { + return mNextResource; + } + + inline GFXResource *getPrevResource() const + { + return mPrevResource; + } + + inline GFXDevice *getOwningDevice() const + { + return mOwningDevice; + } + + inline bool isFlagged() + { + return mFlagged; + } + + inline void setFlag() + { + mFlagged = true; + } + + inline void clearFlag() + { + mFlagged = false; + } +}; + +#endif \ No newline at end of file diff --git a/Engine/source/gfx/gfxShader.cpp b/Engine/source/gfx/gfxShader.cpp new file mode 100644 index 000000000..0bbcb4942 --- /dev/null +++ b/Engine/source/gfx/gfxShader.cpp @@ -0,0 +1,174 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/gfxShader.h" + +#include "shaderGen/conditionerFeature.h" +#include "core/volume.h" +#include "console/engineAPI.h" + + +Vector GFXShader::smGlobalMacros; +bool GFXShader::smLogErrors = true; +bool GFXShader::smLogWarnings = true; + + +GFXShader::GFXShader() + : mPixVersion( 0.0f ), + mReloadKey( 0 ) +{ +} + +GFXShader::~GFXShader() +{ + Torque::FS::RemoveChangeNotification( mVertexFile, this, &GFXShader::_onFileChanged ); + Torque::FS::RemoveChangeNotification( mPixelFile, this, &GFXShader::_onFileChanged ); +} + +bool GFXShader::init( const Torque::Path &vertFile, + const Torque::Path &pixFile, + F32 pixVersion, + const Vector ¯os ) +{ + // Store the inputs for use in reloading. + mVertexFile = vertFile; + mPixelFile = pixFile; + mPixVersion = pixVersion; + mMacros = macros; + + // Before we compile the shader make sure the + // conditioner features have been updated. + ConditionerFeature::updateConditioners(); + + // Now do the real initialization. + if ( !_init() ) + return false; + + _updateDesc(); + + // Add file change notifications for reloads. + Torque::FS::AddChangeNotification( mVertexFile, this, &GFXShader::_onFileChanged ); + Torque::FS::AddChangeNotification( mPixelFile, this, &GFXShader::_onFileChanged ); + + return true; +} + +bool GFXShader::reload() +{ + // Before we compile the shader make sure the + // conditioner features have been updated. + ConditionerFeature::updateConditioners(); + + mReloadKey++; + + // Init does the work. + bool success = _init(); + if ( success ) + _updateDesc(); + + // Let anything that cares know that + // this shader has reloaded + mReloadSignal.trigger(); + + return success; +} + +void GFXShader::_updateDesc() +{ + mDescription = String::ToString( "Files: %s, %s Pix Version: %0.2f\nMacros: ", + mVertexFile.getFullPath().c_str(), mPixelFile.getFullPath().c_str(), mPixVersion ); + + GFXShaderMacro::stringize( smGlobalMacros, &mDescription ); + GFXShaderMacro::stringize( mMacros, &mDescription ); +} + +void GFXShader::addGlobalMacro( const String &name, const String &value ) +{ + // Check to see if we already have this macro. + Vector::iterator iter = smGlobalMacros.begin(); + for ( ; iter != smGlobalMacros.end(); iter++ ) + { + if ( iter->name == name ) + { + if ( iter->value != value ) + iter->value = value; + return; + } + } + + // Add a new macro. + smGlobalMacros.increment(); + smGlobalMacros.last().name = name; + smGlobalMacros.last().value = value; +} + +bool GFXShader::removeGlobalMacro( const String &name ) +{ + Vector::iterator iter = smGlobalMacros.begin(); + for ( ; iter != smGlobalMacros.end(); iter++ ) + { + if ( iter->name == name ) + { + smGlobalMacros.erase( iter ); + return true; + } + } + + return false; +} + +void GFXShader::_unlinkBuffer( GFXShaderConstBuffer *buf ) +{ + Vector::iterator iter = mActiveBuffers.begin(); + for ( ; iter != mActiveBuffers.end(); iter++ ) + { + if ( *iter == buf ) + { + mActiveBuffers.erase_fast( iter ); + return; + } + } + + AssertFatal( false, "GFXShader::_unlinkBuffer - buffer was not found?" ); +} + + +DefineEngineFunction( addGlobalShaderMacro, void, + ( const char *name, const char *value ), ( NULL ), + "Adds a global shader macro which will be merged with the script defined " + "macros on every shader. The macro will replace the value of an existing " + "macro of the same name. For the new macro to take effect all the shaders " + "in the system need to be reloaded.\n" + "@see resetLightManager, removeGlobalShaderMacro\n" + "@ingroup Rendering\n" ) +{ + GFXShader::addGlobalMacro( name, value ); +} + +DefineEngineFunction( removeGlobalShaderMacro, void, ( const char *name ),, + "Removes an existing global macro by name.\n" + "@see addGlobalShaderMacro\n" + "@ingroup Rendering\n" ) +{ + GFXShader::removeGlobalMacro( name ); +} diff --git a/Engine/source/gfx/gfxShader.h b/Engine/source/gfx/gfxShader.h new file mode 100644 index 000000000..8c77a5448 --- /dev/null +++ b/Engine/source/gfx/gfxShader.h @@ -0,0 +1,365 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXSHADER_H_ +#define _GFXSHADER_H_ + +#ifndef _GFXRESOURCE_H_ +#include "gfx/gfxResource.h" +#endif +#ifndef _TORQUE_STRING_H_ +#include "core/util/str.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _ALIGNEDARRAY_H_ +#include "core/util/tAlignedArray.h" +#endif +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif +#ifndef _GFXENUMS_H_ +#include "gfx/gfxEnums.h" +#endif +#ifndef _GFXSTRUCTS_H_ +#include "gfx/gfxStructs.h" +#endif +#ifndef _COLOR_H_ +#include "core/color.h" +#endif +#ifndef _REFBASE_H_ +#include "core/util/refBase.h" +#endif +#ifndef _PATH_H_ +#include "core/util/path.h" +#endif +#ifndef _TSIGNAL_H_ +#include "core/util/tSignal.h" +#endif + +class Point2I; +class Point2F; +class ColorF; +class MatrixF; +class GFXShader; +class GFXVertexFormat; + + +/// Instances of this struct are returned GFXShaderConstBuffer +struct GFXShaderConstDesc +{ +public: + String name; + GFXShaderConstType constType; + U32 arraySize; // > 1 means it is an array! +}; + +/// This is an opaque handle used by GFXShaderConstBuffer clients to set individual shader constants. +/// Derived classes can put whatever info they need into here, these handles are owned by the shader constant buffer +/// (or shader). Client code should not free these. +class GFXShaderConstHandle +{ +public: + + GFXShaderConstHandle() { mValid = false; } + virtual ~GFXShaderConstHandle() {} + + /// Returns true if this constant is valid and can + /// be set on the shader. + bool isValid() const { return mValid; } + + /// Returns the name of the constant handle. + virtual const String& getName() const = 0; + + /// Returns the type of the constant handle. + virtual GFXShaderConstType getType() const = 0; + + virtual U32 getArraySize() const = 0; + + /// Returns -1 if this handle does not point to a Sampler. + virtual S32 getSamplerRegister() const = 0; + +protected: + + /// The state of the constant which is + /// set from the derived class. + bool mValid; + +}; + + +/// GFXShaderConstBuffer is a collection of shader data which +/// are sent to the device in one call in most cases. +/// +/// The content of the buffer is persistant and if a value +/// does not change frequently there is a savings in not setting +/// the value every frame. +/// +class GFXShaderConstBuffer : public GFXResource, public StrongRefBase +{ +protected: + + /// The lost state of the buffer content. + /// + /// Derived classes need to set the lost state + /// on first creation of the buffer and shader + /// reloads. + /// + /// @see wasLost + bool mWasLost; + + GFXShaderConstBuffer() + : mWasLost( true ), + mInstPtr( NULL ) + { + } + +public: + + /// Return the shader that created this buffer + virtual GFXShader* getShader() = 0; + + /// The content of the buffer is in the lost state when + /// first created or when the shader is reloaded. When + /// the content is lost you must refill the buffer + /// with all the constants used by your shader. + /// + /// Use this property to avoid setting constants which do + /// not changefrom one frame to the next. + /// + bool wasLost() const { return mWasLost; } + + /// An inline helper which ensures the handle is valid + /// before the virtual set method is called. + /// + /// You should prefer using this method unless your sure the + /// handle is always valid or if your doing your own test. + /// + template< typename VALUE > + inline void setSafe( GFXShaderConstHandle *handle, const VALUE& v ) + { + if ( handle->isValid() ) + set( handle, v ); + } + + /// Set a shader constant. + /// + /// The constant handle is assumed to be valid. + /// + /// Perfer using setSafe unless you can check the handle + /// validity yourself and skip a significat amount of work. + /// + /// @see GFXShaderConstHandle::isValid() + /// + virtual void set(GFXShaderConstHandle* handle, const F32 f) = 0; + virtual void set(GFXShaderConstHandle* handle, const Point2F& fv) = 0; + virtual void set(GFXShaderConstHandle* handle, const Point3F& fv) = 0; + virtual void set(GFXShaderConstHandle* handle, const Point4F& fv) = 0; + virtual void set(GFXShaderConstHandle* handle, const PlaneF& fv) = 0; + virtual void set(GFXShaderConstHandle* handle, const ColorF& fv) = 0; + virtual void set(GFXShaderConstHandle* handle, const S32 f) = 0; + virtual void set(GFXShaderConstHandle* handle, const Point2I& fv) = 0; + virtual void set(GFXShaderConstHandle* handle, const Point3I& fv) = 0; + virtual void set(GFXShaderConstHandle* handle, const Point4I& fv) = 0; + virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv) = 0; + virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv) = 0; + virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv) = 0; + virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv) = 0; + virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv) = 0; + virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv) = 0; + virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv) = 0; + virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv) = 0; + + /// Set a variable sized matrix shader constant. + virtual void set( GFXShaderConstHandle* handle, + const MatrixF& mat, + const GFXShaderConstType matrixType = GFXSCT_Float4x4 ) = 0; + + /// Set a variable sized matrix shader constant from + /// an array of matricies. + virtual void set( GFXShaderConstHandle* handle, + const MatrixF* mat, + const U32 arraySize, + const GFXShaderConstType matrixType = GFXSCT_Float4x4 ) = 0; + + // TODO: Make this protected and put a real API around it. + U8 *mInstPtr; +}; + +typedef StrongRefPtr GFXShaderConstBufferRef; + + +//************************************************************************** +// Shader +//************************************************************************** +class GFXShader : public StrongRefBase, public GFXResource +{ + friend class GFXShaderConstBuffer; + +protected: + + /// These are system wide shader macros which are + /// merged with shader specific macros at creation. + static Vector smGlobalMacros; + + /// If true the shader errors are spewed to the console. + static bool smLogErrors; + + /// If true the shader warnings are spewed to the console. + static bool smLogWarnings; + + /// The vertex shader file. + Torque::Path mVertexFile; + + /// The pixel shader file. + Torque::Path mPixelFile; + + /// The macros to be passed to the shader. + Vector mMacros; + + /// The pixel version this is compiled for. + F32 mPixVersion; + + /// + String mDescription; + + /// Counter that is incremented each time this shader is reloaded. + U32 mReloadKey; + + Signal mReloadSignal; + + /// Vector of buffers that reference this shader. + /// It is the responsibility of the derived shader class to populate this + /// vector and to notify them when this shader is reloaded. Classes + /// derived from GFXShaderConstBuffer should call _unlinkBuffer from + /// their destructor. + Vector mActiveBuffers; + + /// A protected constructor so it cannot be instantiated. + GFXShader(); + +public: + + // TODO: Add this into init(). + GFXVertexFormat mInstancingFormat; + + /// Adds a global shader macro which will be merged with + /// the script defined macros on every shader reload. + /// + /// The macro will replace the value of an existing macro + /// of the same name. + /// + /// For the new macro to take effect all the shaders/materials + /// in the system need to be reloaded. + /// + /// @see MaterialManager::flushAndReInitInstances + /// @see ShaderData::reloadAll + static void addGlobalMacro( const String &name, const String &value = String::EmptyString ); + + /// Removes an existing global macro by name. + /// @see addGlobalMacro + static bool removeGlobalMacro( const String &name ); + + /// Toggle logging for shader errors. + static void setLogging( bool logErrors, + bool logWarning ) + { + smLogErrors = logErrors; + smLogWarnings = logWarning; + } + + /// The destructor. + virtual ~GFXShader(); + + /// + bool init( const Torque::Path &vertFile, + const Torque::Path &pixFile, + F32 pixVersion, + const Vector ¯os ); + + /// Reloads the shader from disk. + bool reload(); + + Signal getReloadSignal() { return mReloadSignal; } + + /// Allocate a constant buffer + virtual GFXShaderConstBufferRef allocConstBuffer() = 0; + + /// Returns our list of shader constants, the material can get this and just set the constants it knows about + virtual const Vector& getShaderConstDesc() const = 0; + + /// Returns a shader constant handle for the name constant. + /// + /// Since shaders can reload and later have handles that didn't + /// exist originally this will return a handle in an invalid state + /// if the constant doesn't exist at this time. + virtual GFXShaderConstHandle* getShaderConstHandle( const String& name ) = 0; + + /// Returns the alignment value for constType + virtual U32 getAlignmentValue(const GFXShaderConstType constType) const = 0; + + /// Returns the required vertex format for this shader. + /// Returns the pixel shader version. + F32 getPixVersion() const { return mPixVersion; } + + /// Returns a counter which is incremented each time this shader is reloaded. + U32 getReloadKey() const { return mReloadKey; } + + /// Device specific shaders can override this method to return + /// the shader disassembly. + virtual bool getDisassembly( String &outStr ) const { return false; } + + /// Returns the vertex shader file path. + const String& getVertexShaderFile() const { return mVertexFile.getFullPath(); } + + /// Returns the pixel shader file path. + const String& getPixelShaderFile() const { return mPixelFile.getFullPath(); } + + // GFXResource + const String describeSelf() const { return mDescription; } + +protected: + + /// Called when the shader files change on disk. + void _onFileChanged( const Torque::Path &path ) { reload(); } + + /// Internal initialization function overloaded for + /// each GFX device type. + virtual bool _init() = 0; + + /// Buffers call this from their destructor (so we don't have to ref count them). + void _unlinkBuffer( GFXShaderConstBuffer *buf ); + + /// Called to update the description string after init. + void _updateDesc(); +}; + +/// A strong pointer to a reference counted GFXShader. +typedef StrongRefPtr GFXShaderRef; + + +/// A weak pointer to a reference counted GFXShader. +typedef WeakRefPtr GFXShaderWeakRef; + + +#endif // GFXSHADER diff --git a/Engine/source/gfx/gfxStateBlock.cpp b/Engine/source/gfx/gfxStateBlock.cpp new file mode 100644 index 000000000..1c4c305cf --- /dev/null +++ b/Engine/source/gfx/gfxStateBlock.cpp @@ -0,0 +1,336 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#include "gfx/gfxStateBlock.h" +#include "core/crc.h" +#include "gfx/gfxDevice.h" +#include "core/strings/stringFunctions.h" +#include "gfx/gfxStringEnumTranslate.h" + +/// +/// GFXStateBlock +/// +const String GFXStateBlock::describeSelf() const +{ + return String::ToString("hashvalue: 0x%x", getDesc().getHashValue()); +} + +/// +/// GFXStateBlockDesc +/// +GFXStateBlockDesc::GFXStateBlockDesc() +{ + // Alpha blending + blendDefined = false; + blendEnable = false; + blendSrc = GFXBlendOne; + blendDest = GFXBlendZero; + blendOp = GFXBlendOpAdd; + + // Separate alpha blending + separateAlphaBlendDefined = false; + separateAlphaBlendEnable = false; + separateAlphaBlendSrc = GFXBlendOne; + separateAlphaBlendDest = GFXBlendZero; + separateAlphaBlendOp = GFXBlendOpAdd; + + // Alpha test + alphaDefined = false; + alphaTestEnable = false; + alphaTestRef = 0; + alphaTestFunc = GFXCmpGreaterEqual; + + // Color Writes + colorWriteDefined = false; + colorWriteRed = true; + colorWriteBlue = true; + colorWriteGreen = true; + colorWriteAlpha = true; + + // Rasterizer + cullDefined = false; + cullMode = GFXCullCCW; + + // Depth + zDefined = false; + zEnable = true; + zWriteEnable = true; + zFunc = GFXCmpLessEqual; + zBias = 0; + zSlopeBias = 0; + + // Stencil + stencilDefined = false; + stencilEnable = false; + stencilFailOp = GFXStencilOpKeep; + stencilZFailOp = GFXStencilOpKeep; + stencilPassOp = GFXStencilOpKeep; + stencilFunc = GFXCmpNever; + stencilRef = 0; + stencilMask = 0xFFFFFFFF; + stencilWriteMask = 0xFFFFFFFF; + + // FF lighting + ffLighting = false; + + vertexColorEnable = false; + + fillMode = GFXFillSolid; + + samplersDefined = false; + textureFactor.set( 255, 255, 255, 255 ); +} + +// This method just needs to return a unique value based on its contents. +U32 GFXStateBlockDesc::getHashValue() const +{ + return CRC::calculateCRC(this, sizeof(GFXStateBlockDesc)); +} + +/// Adds data from desc to this description, uses *defined parameters in desc to figure out +/// what blocks of state to actually copy from desc. +void GFXStateBlockDesc::addDesc(const GFXStateBlockDesc& desc) +{ + // Alpha blending + if (desc.blendDefined) + { + blendDefined = true; + blendEnable = desc.blendEnable; + blendSrc = desc.blendSrc; + blendDest = desc.blendDest; + blendOp = desc.blendOp; + } + + // Separate alpha blending + if ( desc.separateAlphaBlendDefined ) + { + separateAlphaBlendDefined = true; + separateAlphaBlendEnable = desc.separateAlphaBlendEnable; + separateAlphaBlendSrc = desc.separateAlphaBlendSrc; + separateAlphaBlendDest = desc.separateAlphaBlendDest; + separateAlphaBlendOp = desc.separateAlphaBlendOp; + } + + // Alpha test + if (desc.alphaDefined) + { + alphaDefined = true; + alphaTestEnable = desc.alphaTestEnable; + alphaTestRef = desc.alphaTestRef; + alphaTestFunc = desc.alphaTestFunc; + } + + // Color Writes + if (desc.colorWriteDefined) + { + colorWriteDefined = true; + colorWriteRed = desc.colorWriteRed; + colorWriteBlue = desc.colorWriteBlue; + colorWriteGreen = desc.colorWriteGreen; + colorWriteAlpha = desc.colorWriteAlpha; + } + + // Rasterizer + if (desc.cullDefined) + { + cullDefined = true; + cullMode = desc.cullMode; + } + + // Depth + if (desc.zDefined) + { + zDefined = true; + zEnable = desc.zEnable; + zWriteEnable = desc.zWriteEnable; + zFunc = desc.zFunc; + zBias = desc.zBias; + zSlopeBias = desc.zSlopeBias; + } + + // Stencil + if (desc.stencilDefined) + { + stencilDefined = true; + stencilEnable = desc.stencilEnable; + stencilFailOp = desc.stencilFailOp; + stencilZFailOp = desc.stencilZFailOp; + stencilPassOp = desc.stencilPassOp; + stencilFunc = desc.stencilFunc; + stencilRef = desc.stencilRef; + stencilMask = desc.stencilMask; + stencilWriteMask = desc.stencilWriteMask; + } + + if (desc.samplersDefined) + { + samplersDefined = true; + for (U32 i = 0; i < TEXTURE_STAGE_COUNT; i++) + { + samplers[i] = desc.samplers[i]; + } + textureFactor = desc.textureFactor; + } + + vertexColorEnable = desc.vertexColorEnable; + fillMode = desc.fillMode; +} + +/// Returns a string that describes the options set (used by GFXStateBlock::describeSelf) +const String GFXStateBlockDesc::describeSelf() const +{ + GFXStringEnumTranslate::init(); + + String ret; + ret = String::ToString(" AlphaBlend: %d, BlendSrc: %s, BlendDest: %s, BlendOp: %s\n", + blendEnable, GFXStringBlend[blendSrc], GFXStringBlend[blendDest], GFXStringBlendOp[blendOp]); + ret += String::ToString(" SeparateAlphaBlend: %d, SeparateAlphaBlendSrc: %s, SeparateAlphaBlendDest: %s, SeparateAlphaBlendOp: %s\n", + separateAlphaBlendEnable, GFXStringBlend[separateAlphaBlendSrc], GFXStringBlend[separateAlphaBlendDest], GFXStringBlendOp[separateAlphaBlendOp]); + ret += String::ToString(" AlphaTest: %d, AlphaTestFunc: %s, AlphaTestRef: %d\n", + alphaTestEnable, GFXStringCmpFunc[alphaTestFunc], alphaTestRef); + ret += String::ToString(" ColorWrites: r: %d g: %d b: %d a: %d", + colorWriteRed, colorWriteGreen, colorWriteBlue, colorWriteAlpha); + ret += String::ToString(" CullMode: %s\n", GFXStringCullMode[cullMode]); + ret += String::ToString(" ZEnable: %d, ZWriteEnable: %d, ZFunc: %s, ZBias: %f, ZSlopeBias: %f\n", + zEnable, zWriteEnable, GFXStringCmpFunc[zFunc], zBias, zSlopeBias); + ret += String::ToString(" Stencil: %d, StencilFailOp: %s, StencilZFailOp: %s, StencilPassOp: %s, \n stencilFunc: %s, stencilRef: %d, stencilMask: 0x%x, stencilWriteMask: 0x%x\n", + stencilEnable, GFXStringCmpFunc[stencilFailOp], GFXStringCmpFunc[stencilZFailOp], GFXStringCmpFunc[stencilPassOp], + GFXStringCmpFunc[stencilFunc], stencilRef, stencilMask, stencilWriteMask); + ret += String::ToString(" FF Lighting: %d, VertexColors: %d, fillMode: %s", + ffLighting, vertexColorEnable, GFXStringFillMode[fillMode]); + + return ret; +} + +// +// Utility functions +// + +void GFXStateBlockDesc::setCullMode( GFXCullMode m ) +{ + cullDefined = true; + cullMode = m; +} + +void GFXStateBlockDesc::setZReadWrite( bool read, bool write ) +{ + zDefined = true; + zEnable = read; + zWriteEnable = write; +} + +void GFXStateBlockDesc::setAlphaTest( bool enable, GFXCmpFunc func, S32 alphaRef ) +{ + alphaDefined = true; + alphaTestEnable = enable; + alphaTestFunc = func; + alphaTestRef = alphaRef; +} + +void GFXStateBlockDesc::setBlend( bool enable, GFXBlend src, GFXBlend dest, GFXBlendOp op ) +{ + blendDefined = true; + blendEnable = enable; + blendSrc = src; + blendDest = dest; + blendOp = op; +} + +void GFXStateBlockDesc::setSeparateAlphaBlend( bool enable, GFXBlend src, GFXBlend dest, GFXBlendOp op ) +{ + separateAlphaBlendDefined = true; + separateAlphaBlendEnable = enable; + separateAlphaBlendSrc = src; + separateAlphaBlendDest = dest; + separateAlphaBlendOp = op; +} + +void GFXStateBlockDesc::setColorWrites( bool red, bool green, bool blue, bool alpha ) +{ + colorWriteDefined = true; + colorWriteRed = red; + colorWriteGreen = green; + colorWriteBlue = blue; + colorWriteAlpha = alpha; +} + +GFXSamplerStateDesc::GFXSamplerStateDesc() +{ + textureColorOp = GFXTOPDisable; + addressModeU = GFXAddressWrap; + addressModeV = GFXAddressWrap; + addressModeW = GFXAddressWrap; + magFilter = GFXTextureFilterLinear; + minFilter = GFXTextureFilterLinear; + mipFilter = GFXTextureFilterLinear; + maxAnisotropy = 1; + alphaArg1 = GFXTATexture; + alphaArg2 = GFXTADiffuse; + alphaArg3 = GFXTACurrent; + colorArg1 = GFXTACurrent; + colorArg2 = GFXTATexture; + colorArg3 = GFXTACurrent; + alphaOp = GFXTOPModulate; + textureTransform = GFXTTFFDisable; + resultArg = GFXTACurrent; + mipLODBias = 0.0f; +} + +GFXSamplerStateDesc GFXSamplerStateDesc::getWrapLinear() +{ + // Linear with wrapping is already the default + GFXSamplerStateDesc ssd; + ssd.textureColorOp = GFXTOPModulate; + return ssd; +} + +GFXSamplerStateDesc GFXSamplerStateDesc::getWrapPoint() +{ + GFXSamplerStateDesc ssd; + ssd.textureColorOp = GFXTOPModulate; + ssd.magFilter = GFXTextureFilterPoint; + ssd.minFilter = GFXTextureFilterPoint; + ssd.mipFilter = GFXTextureFilterPoint; + return ssd; +} + +GFXSamplerStateDesc GFXSamplerStateDesc::getClampLinear() +{ + GFXSamplerStateDesc ssd; + ssd.textureColorOp = GFXTOPModulate; + ssd.addressModeU = GFXAddressClamp; + ssd.addressModeV = GFXAddressClamp; + ssd.addressModeW = GFXAddressClamp; + return ssd; +} + +GFXSamplerStateDesc GFXSamplerStateDesc::getClampPoint() +{ + GFXSamplerStateDesc ssd; + ssd.textureColorOp = GFXTOPModulate; + ssd.addressModeU = GFXAddressClamp; + ssd.addressModeV = GFXAddressClamp; + ssd.addressModeW = GFXAddressClamp; + ssd.magFilter = GFXTextureFilterPoint; + ssd.minFilter = GFXTextureFilterPoint; + ssd.mipFilter = GFXTextureFilterPoint; + return ssd; +} \ No newline at end of file diff --git a/Engine/source/gfx/gfxStateBlock.h b/Engine/source/gfx/gfxStateBlock.h new file mode 100644 index 000000000..f807f221f --- /dev/null +++ b/Engine/source/gfx/gfxStateBlock.h @@ -0,0 +1,219 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXSTATEBLOCK_H_ +#define _GFXSTATEBLOCK_H_ + +#ifndef _REFBASE_H_ +#include "core/util/refBase.h" +#endif +#ifndef _GFXENUMS_H_ +#include "gfx/gfxEnums.h" +#endif +#ifndef _GFXRESOURCE_H_ +#include "gfx/gfxResource.h" +#endif +#ifndef _COLOR_H_ +#include "core/color.h" +#endif + + +struct GFXSamplerStateDesc +{ + GFXTextureAddressMode addressModeU; + GFXTextureAddressMode addressModeV; + GFXTextureAddressMode addressModeW; + + GFXTextureFilterType magFilter; + GFXTextureFilterType minFilter; + GFXTextureFilterType mipFilter; + + /// The maximum anisotropy used when one of the filter types + /// is set to anisotropic. + /// + /// Defaults to 1. + /// + /// @see GFXTextureFilterType + U32 maxAnisotropy; + + /// Used to offset the mipmap selection by whole or + /// fractional amounts either postively or negatively. + /// + /// Defaults to zero. + F32 mipLODBias; + + GFXTextureOp textureColorOp; + + GFXTextureOp alphaOp; + GFXTextureArgument alphaArg1; + GFXTextureArgument alphaArg2; + GFXTextureArgument alphaArg3; + + GFXTextureArgument colorArg1; + GFXTextureArgument colorArg2; + GFXTextureArgument colorArg3; + + GFXTextureArgument resultArg; + + GFXTextureTransformFlags textureTransform; + + GFXSamplerStateDesc(); + + /// Returns an modulate, wrap, and linear sampled state. + static GFXSamplerStateDesc getWrapLinear(); + + /// Returns an modulate, wrap, and point sampled state. + static GFXSamplerStateDesc getWrapPoint(); + + /// Returns an modulate, clamp, and linear sampled state. + static GFXSamplerStateDesc getClampLinear(); + + /// Returns an modulate, clamp, and point sampled state. + static GFXSamplerStateDesc getClampPoint(); +}; + +/// GFXStateBlockDesc defines a render state, which is then used to create a GFXStateBlock instance. +struct GFXStateBlockDesc +{ + // Blending + bool blendDefined; + bool blendEnable; + GFXBlend blendSrc; + GFXBlend blendDest; + GFXBlendOp blendOp; + + /// @name Separate Alpha Blending + /// @{ + bool separateAlphaBlendDefined; + bool separateAlphaBlendEnable; + GFXBlend separateAlphaBlendSrc; + GFXBlend separateAlphaBlendDest; + GFXBlendOp separateAlphaBlendOp; + /// @} + + // Alpha test + bool alphaDefined; + bool alphaTestEnable; + S32 alphaTestRef; + GFXCmpFunc alphaTestFunc; + + // Color Writes + bool colorWriteDefined; + bool colorWriteRed; + bool colorWriteBlue; + bool colorWriteGreen; + bool colorWriteAlpha; + + // Rasterizer + bool cullDefined; + GFXCullMode cullMode; + + // Depth + bool zDefined; + bool zEnable; + bool zWriteEnable; + GFXCmpFunc zFunc; + F32 zBias; + F32 zSlopeBias; + + // Stencil + bool stencilDefined; + bool stencilEnable; + GFXStencilOp stencilFailOp; + GFXStencilOp stencilZFailOp; + GFXStencilOp stencilPassOp; + GFXCmpFunc stencilFunc; + U32 stencilRef; + U32 stencilMask; + U32 stencilWriteMask; + + // FF lighting + bool ffLighting; + + bool vertexColorEnable; + + GFXFillMode fillMode; + + // Sampler states + bool samplersDefined; + GFXSamplerStateDesc samplers[TEXTURE_STAGE_COUNT]; + ColorI textureFactor; + + GFXStateBlockDesc(); + + /// Returns the hash value of this state description + U32 getHashValue() const; + + /// Adds data from desc to this description, uses *defined parameters in desc to figure out + /// what blocks of state to actually copy from desc. + void addDesc( const GFXStateBlockDesc& desc ); + + /// Returns a string that describes the options set (used by GFXStateBlock::describeSelf) + const String describeSelf() const; + + /// Utility functions to make setting up stateblock descriptions less wordy. + void setCullMode( GFXCullMode m ); + + /// Helpers for setting the fill modes. + void setFillModePoint() { fillMode = GFXFillPoint; } + void setFillModeWireframe() { fillMode = GFXFillWireframe; } + void setFillModeSolid() { fillMode = GFXFillSolid; } + + void setZReadWrite( bool read, bool write = true ); + + void setAlphaTest( bool enable, + GFXCmpFunc func = GFXCmpGreaterEqual, + S32 alphaRef = 0 ); + + void setBlend( bool enable, + GFXBlend src = GFXBlendSrcAlpha, + GFXBlend dest = GFXBlendInvSrcAlpha, + GFXBlendOp op = GFXBlendOpAdd ); + + void setSeparateAlphaBlend( bool enable, + GFXBlend src = GFXBlendOne, + GFXBlend dest = GFXBlendZero, + GFXBlendOp op = GFXBlendOpAdd ); + + + /// + void setColorWrites( bool red, bool green, bool blue, bool alpha ); +}; + +class GFXStateBlock : public StrongRefBase, public GFXResource +{ +public: + virtual ~GFXStateBlock() { } + + /// Returns the hash value of the desc that created this block + virtual U32 getHashValue() const = 0; + + /// Returns a GFXStateBlockDesc that this block represents + virtual const GFXStateBlockDesc& getDesc() const = 0; + + /// Default implementation for GFXResource::describeSelf + virtual const String describeSelf() const; +}; + +typedef StrongRefPtr GFXStateBlockRef; + +#endif diff --git a/Engine/source/gfx/gfxStringEnumTranslate.cpp b/Engine/source/gfx/gfxStringEnumTranslate.cpp new file mode 100644 index 000000000..108422aec --- /dev/null +++ b/Engine/source/gfx/gfxStringEnumTranslate.cpp @@ -0,0 +1,418 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/strings/stringFunctions.h" + +#include "gfx/gfxStringEnumTranslate.h" +#include "gfx/gfxAPI.h" +#include "console/console.h" + +//------------------------------------------------------------------------------ + +const char *GFXStringIndexFormat[GFXIndexFormat_COUNT]; +const char *GFXStringSamplerState[GFXSAMP_COUNT]; +const char *GFXStringTextureFormat[GFXFormat_COUNT]; +const char *GFXStringTiledTextureFormat[GFXFormat_COUNT]; +const char *GFXStringRenderTargetFormat[GFXFormat_COUNT]; +const char *GFXStringRenderState[GFXRenderState_COUNT]; +const char *GFXStringTextureFilter[GFXTextureFilter_COUNT]; +const char *GFXStringBlend[GFXBlend_COUNT]; +const char *GFXStringBlendOp[GFXBlendOp_COUNT]; +const char *GFXStringStencilOp[GFXStencilOp_COUNT]; +const char *GFXStringCmpFunc[GFXCmp_COUNT]; +const char *GFXStringCullMode[GFXCull_COUNT]; +const char *GFXStringPrimType[GFXPT_COUNT]; +const char *GFXStringTextureStageState[GFXTSS_COUNT]; +const char *GFXStringTextureAddress[GFXAddress_COUNT]; +const char *GFXStringTextureOp[GFXTOP_COUNT]; +const char *GFXStringFillMode[GFXFill_COUNT]; + +StringValueLookupFn GFXStringRenderStateValueLookup[GFXRenderState_COUNT]; +StringValueLookupFn GFXStringSamplerStateValueLookup[GFXSAMP_COUNT]; +StringValueLookupFn GFXStringTextureStageStateValueLookup[GFXTSS_COUNT]; + +//------------------------------------------------------------------------------ + +const char *defaultStringValueLookup( const U32 &value ) +{ + static char retbuffer[256]; + + dSprintf( retbuffer, sizeof( retbuffer ), "%d", value ); + + return retbuffer; +} + +#define _STRING_VALUE_LOOKUP_FXN( table ) \ + const char * table##_lookup( const U32 &value ) { return table[value]; } + +_STRING_VALUE_LOOKUP_FXN(GFXStringTextureAddress); +_STRING_VALUE_LOOKUP_FXN(GFXStringTextureFilter); +_STRING_VALUE_LOOKUP_FXN(GFXStringBlend); +_STRING_VALUE_LOOKUP_FXN(GFXStringTextureOp); +_STRING_VALUE_LOOKUP_FXN(GFXStringCmpFunc); +_STRING_VALUE_LOOKUP_FXN(GFXStringStencilOp); +_STRING_VALUE_LOOKUP_FXN(GFXStringCullMode); +_STRING_VALUE_LOOKUP_FXN(GFXStringBlendOp); + +//------------------------------------------------------------------------------ + +#define INIT_LOOKUPTABLE( tablearray, enumprefix, type ) \ + for( int i = enumprefix##_FIRST; i < enumprefix##_COUNT; i++ ) \ + tablearray[i] = (type)GFX_UNINIT_VAL; +#define INIT_LOOKUPTABLE_EX( tablearray, enumprefix, type, typeTable ) \ + for( int i = enumprefix##_FIRST; i < enumprefix##_COUNT; i++ ) \ + {\ + tablearray[i] = (type)GFX_UNINIT_VAL;\ + typeTable[i] = &defaultStringValueLookup;\ + } + +#define VALIDATE_LOOKUPTABLE( tablearray, enumprefix ) \ + for( int i = enumprefix##_FIRST; i < enumprefix##_COUNT; i++ ) \ + if( (int)tablearray[i] == GFX_UNINIT_VAL ) \ + Con::warnf( "GFXStringEnumTranslate: Unassigned value in " #tablearray ": %i", i ); \ + else if( (int)tablearray[i] == GFX_UNSUPPORTED_VAL ) \ + Con::warnf( "GFXStringEnumTranslate: Unsupported value in " #tablearray ": %i", i ); + +//------------------------------------------------------------------------------ + +#define GFX_STRING_ASSIGN_MACRO( table, indexEnum ) table[indexEnum] = #indexEnum; +#define GFX_STRING_ASSIGN_MACRO_EX( table, indexEnum, typeTable ) table[indexEnum] = #indexEnum; table##ValueLookup[indexEnum] = &typeTable##_lookup; + +void GFXStringEnumTranslate::init() +{ + static bool sInitCalled = false; + + if( sInitCalled ) + return; + + sInitCalled = true; + + INIT_LOOKUPTABLE( GFXStringIndexFormat, GFXIndexFormat, const char * ); + GFX_STRING_ASSIGN_MACRO( GFXStringIndexFormat, GFXIndexFormat16 ); + GFX_STRING_ASSIGN_MACRO( GFXStringIndexFormat, GFXIndexFormat32 ); + VALIDATE_LOOKUPTABLE( GFXStringIndexFormat, GFXIndexFormat ); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE_EX( GFXStringSamplerState, GFXSAMP, const char *, GFXStringSamplerStateValueLookup ); + GFX_STRING_ASSIGN_MACRO_EX( GFXStringSamplerState, GFXSAMPAddressU, GFXStringTextureAddress ); + GFX_STRING_ASSIGN_MACRO_EX( GFXStringSamplerState, GFXSAMPAddressV, GFXStringTextureAddress ); + GFX_STRING_ASSIGN_MACRO_EX( GFXStringSamplerState, GFXSAMPAddressW, GFXStringTextureAddress ); + GFX_STRING_ASSIGN_MACRO( GFXStringSamplerState, GFXSAMPBorderColor ); + GFX_STRING_ASSIGN_MACRO_EX( GFXStringSamplerState, GFXSAMPMagFilter, GFXStringTextureFilter ); + GFX_STRING_ASSIGN_MACRO_EX( GFXStringSamplerState, GFXSAMPMinFilter, GFXStringTextureFilter ); + GFX_STRING_ASSIGN_MACRO_EX( GFXStringSamplerState, GFXSAMPMipFilter, GFXStringTextureFilter ); + GFX_STRING_ASSIGN_MACRO( GFXStringSamplerState, GFXSAMPMipMapLODBias ); + GFX_STRING_ASSIGN_MACRO( GFXStringSamplerState, GFXSAMPMaxMipLevel ); + GFX_STRING_ASSIGN_MACRO( GFXStringSamplerState, GFXSAMPMaxAnisotropy ); + + GFX_STRING_ASSIGN_MACRO( GFXStringSamplerState, GFXSAMPSRGBTexture ); + GFX_STRING_ASSIGN_MACRO( GFXStringSamplerState, GFXSAMPElementIndex ); + GFX_STRING_ASSIGN_MACRO( GFXStringSamplerState, GFXSAMPDMapOffset ); + + VALIDATE_LOOKUPTABLE( GFXStringSamplerState, GFXSAMP ); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE( GFXStringTextureFormat, GFXFormat, const char * ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatR8G8B8 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatR8G8B8A8 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatR8G8B8X8 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatR32F ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatR5G6B5 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatR5G5B5A1 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatR5G5B5X1 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatA4L4 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatA8L8 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatA8 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatL8 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatDXT1 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatDXT2 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatDXT3 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatDXT4 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatDXT5 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatD32 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatD24X8 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatD24S8 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatD24FS8 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatD16 ); + + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatR32G32B32A32F ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatR16G16B16A16F ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatL16 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatR16G16B16A16 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatR16G16 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatR16F ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatR16G16F ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFormat, GFXFormatR10G10B10A2 ); + VALIDATE_LOOKUPTABLE( GFXStringTextureFormat, GFXFormat); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE_EX( GFXStringRenderState, GFXRenderState, const char *, GFXStringRenderStateValueLookup ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSZEnable ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSFillMode ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSZWriteEnable ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSAlphaTestEnable ); + GFX_STRING_ASSIGN_MACRO_EX( GFXStringRenderState, GFXRSSrcBlend, GFXStringBlend ); + GFX_STRING_ASSIGN_MACRO_EX( GFXStringRenderState, GFXRSDestBlend, GFXStringBlend ); + GFX_STRING_ASSIGN_MACRO_EX( GFXStringRenderState, GFXRSCullMode, GFXStringCullMode ); + GFX_STRING_ASSIGN_MACRO_EX( GFXStringRenderState, GFXRSZFunc, GFXStringCmpFunc ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSAlphaRef ); + GFX_STRING_ASSIGN_MACRO_EX( GFXStringRenderState, GFXRSAlphaFunc, GFXStringCmpFunc ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSAlphaBlendEnable ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSStencilEnable ); + GFX_STRING_ASSIGN_MACRO_EX( GFXStringRenderState, GFXRSStencilFail, GFXStringStencilOp ); + GFX_STRING_ASSIGN_MACRO_EX( GFXStringRenderState, GFXRSStencilZFail, GFXStringStencilOp ); + GFX_STRING_ASSIGN_MACRO_EX( GFXStringRenderState, GFXRSStencilPass, GFXStringStencilOp ); + GFX_STRING_ASSIGN_MACRO_EX( GFXStringRenderState, GFXRSStencilFunc, GFXStringCmpFunc ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSStencilRef ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSStencilMask ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSStencilWriteMask ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSWrap0 ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSWrap1 ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSWrap2 ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSWrap3 ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSWrap4 ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSWrap5 ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSWrap6 ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSWrap7 ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSClipPlaneEnable ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSPointSize ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSPointSizeMin ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSPointSize_Max ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSPointSpriteEnable ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSMultiSampleantiAlias ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSMultiSampleMask ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSShadeMode ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSLastPixel ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSClipping ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSPointScaleEnable ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSPointScale_A ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSPointScale_B ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSPointScale_C ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSLighting ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSAmbient ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSFogVertexMode ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSColorVertex ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSLocalViewer ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSNormalizeNormals ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSDiffuseMaterialSource ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSSpecularMaterialSource ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSAmbientMaterialSource ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSEmissiveMaterialSource ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSVertexBlend ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSFogEnable ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSSpecularEnable ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSFogColor ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSFogTableMode ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSFogStart ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSFogEnd ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSFogDensity ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSRangeFogEnable ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSDebugMonitorToken ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSIndexedVertexBlendEnable ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSTweenFactor ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSTextureFactor ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSPatchEdgeStyle ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSDitherEnable ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSColorWriteEnable ); + GFX_STRING_ASSIGN_MACRO_EX( GFXStringRenderState, GFXRSBlendOp, GFXStringBlendOp ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSPositionDegree ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSNormalDegree ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSAntiAliasedLineEnable ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSAdaptiveTess_X ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSAdaptiveTess_Y ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSdaptiveTess_Z ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSAdaptiveTess_W ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSEnableAdaptiveTesselation ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSScissorTestEnable ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSSlopeScaleDepthBias ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSMinTessellationLevel ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSMaxTessellationLevel ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSTwoSidedStencilMode ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSCCWStencilFail ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSCCWStencilZFail ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSCCWStencilPass ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSCCWStencilFunc ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSColorWriteEnable1 ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSColorWriteEnable2 ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSolorWriteEnable3 ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSBlendFactor ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSSRGBWriteEnable ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSDepthBias ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSWrap8 ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSWrap9 ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSWrap10 ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSWrap11 ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSWrap12 ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSWrap13 ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSWrap14 ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSWrap15 ); + GFX_STRING_ASSIGN_MACRO( GFXStringRenderState, GFXRSSeparateAlphaBlendEnable ); + GFX_STRING_ASSIGN_MACRO_EX( GFXStringRenderState, GFXRSSrcBlendAlpha, GFXStringBlend ); + GFX_STRING_ASSIGN_MACRO_EX( GFXStringRenderState, GFXRSDestBlendAlpha, GFXStringBlend ); + GFX_STRING_ASSIGN_MACRO_EX( GFXStringRenderState, GFXRSBlendOpAlpha, GFXStringBlendOp ); + + VALIDATE_LOOKUPTABLE( GFXStringRenderState, GFXRenderState ); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE( GFXStringTextureFilter, GFXTextureFilter, const char * ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFilter, GFXTextureFilterNone ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFilter, GFXTextureFilterPoint ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFilter, GFXTextureFilterLinear ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFilter, GFXTextureFilterAnisotropic ); + + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFilter, GFXTextureFilterPyramidalQuad ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureFilter, GFXTextureFilterGaussianQuad ); + + VALIDATE_LOOKUPTABLE( GFXStringTextureFilter, GFXTextureFilter ); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE( GFXStringBlend, GFXBlend, const char * ); + GFX_STRING_ASSIGN_MACRO( GFXStringBlend, GFXBlendZero ); + GFX_STRING_ASSIGN_MACRO( GFXStringBlend, GFXBlendOne ); + GFX_STRING_ASSIGN_MACRO( GFXStringBlend, GFXBlendSrcColor ); + GFX_STRING_ASSIGN_MACRO( GFXStringBlend, GFXBlendInvSrcColor ); + GFX_STRING_ASSIGN_MACRO( GFXStringBlend, GFXBlendSrcAlpha ); + GFX_STRING_ASSIGN_MACRO( GFXStringBlend, GFXBlendInvSrcAlpha ); + GFX_STRING_ASSIGN_MACRO( GFXStringBlend, GFXBlendDestAlpha ); + GFX_STRING_ASSIGN_MACRO( GFXStringBlend, GFXBlendInvDestAlpha ); + GFX_STRING_ASSIGN_MACRO( GFXStringBlend, GFXBlendDestColor ); + GFX_STRING_ASSIGN_MACRO( GFXStringBlend, GFXBlendInvDestColor ); + GFX_STRING_ASSIGN_MACRO( GFXStringBlend, GFXBlendSrcAlphaSat ); + VALIDATE_LOOKUPTABLE( GFXStringBlend, GFXBlend ); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE( GFXStringBlendOp, GFXBlendOp, const char * ); + GFX_STRING_ASSIGN_MACRO( GFXStringBlendOp, GFXBlendOpAdd ); + GFX_STRING_ASSIGN_MACRO( GFXStringBlendOp, GFXBlendOpSubtract ); + GFX_STRING_ASSIGN_MACRO( GFXStringBlendOp, GFXBlendOpRevSubtract ); + GFX_STRING_ASSIGN_MACRO( GFXStringBlendOp, GFXBlendOpMin ); + GFX_STRING_ASSIGN_MACRO( GFXStringBlendOp, GFXBlendOpMax ); + VALIDATE_LOOKUPTABLE( GFXStringBlendOp, GFXBlendOp ); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE( GFXStringStencilOp, GFXStencilOp, const char * ); + GFX_STRING_ASSIGN_MACRO( GFXStringStencilOp, GFXStencilOpKeep ); + GFX_STRING_ASSIGN_MACRO( GFXStringStencilOp, GFXStencilOpZero ); + GFX_STRING_ASSIGN_MACRO( GFXStringStencilOp, GFXStencilOpReplace ); + GFX_STRING_ASSIGN_MACRO( GFXStringStencilOp, GFXStencilOpIncrSat ); + GFX_STRING_ASSIGN_MACRO( GFXStringStencilOp, GFXStencilOpDecrSat ); + GFX_STRING_ASSIGN_MACRO( GFXStringStencilOp, GFXStencilOpInvert ); + GFX_STRING_ASSIGN_MACRO( GFXStringStencilOp, GFXStencilOpIncr ); + GFX_STRING_ASSIGN_MACRO( GFXStringStencilOp, GFXStencilOpDecr ); + VALIDATE_LOOKUPTABLE( GFXStringStencilOp, GFXStencilOp ); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE( GFXStringCmpFunc, GFXCmp, const char * ); + GFX_STRING_ASSIGN_MACRO( GFXStringCmpFunc, GFXCmpNever ); + GFX_STRING_ASSIGN_MACRO( GFXStringCmpFunc, GFXCmpLess ); + GFX_STRING_ASSIGN_MACRO( GFXStringCmpFunc, GFXCmpEqual ); + GFX_STRING_ASSIGN_MACRO( GFXStringCmpFunc, GFXCmpLessEqual ); + GFX_STRING_ASSIGN_MACRO( GFXStringCmpFunc, GFXCmpGreater ); + GFX_STRING_ASSIGN_MACRO( GFXStringCmpFunc, GFXCmpNotEqual ); + GFX_STRING_ASSIGN_MACRO( GFXStringCmpFunc, GFXCmpGreaterEqual ); + GFX_STRING_ASSIGN_MACRO( GFXStringCmpFunc, GFXCmpAlways ); + VALIDATE_LOOKUPTABLE( GFXStringCmpFunc, GFXCmp ); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE( GFXStringCullMode, GFXCull, const char * ); + GFX_STRING_ASSIGN_MACRO( GFXStringCullMode, GFXCullNone ); + GFX_STRING_ASSIGN_MACRO( GFXStringCullMode, GFXCullCW ); + GFX_STRING_ASSIGN_MACRO( GFXStringCullMode, GFXCullCCW ); + VALIDATE_LOOKUPTABLE( GFXStringCullMode, GFXCull ); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE( GFXStringPrimType, GFXPT, const char * ); + GFX_STRING_ASSIGN_MACRO( GFXStringPrimType, GFXPointList ); + GFX_STRING_ASSIGN_MACRO( GFXStringPrimType, GFXLineList ); + GFX_STRING_ASSIGN_MACRO( GFXStringPrimType, GFXLineStrip ); + GFX_STRING_ASSIGN_MACRO( GFXStringPrimType, GFXTriangleList ); + GFX_STRING_ASSIGN_MACRO( GFXStringPrimType, GFXTriangleStrip ); + GFX_STRING_ASSIGN_MACRO( GFXStringPrimType, GFXTriangleFan ); + VALIDATE_LOOKUPTABLE( GFXStringPrimType, GFXPT ); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE_EX( GFXStringTextureStageState, GFXTSS, const char *, GFXStringTextureStageStateValueLookup ); + GFX_STRING_ASSIGN_MACRO_EX( GFXStringTextureStageState, GFXTSSColorOp, GFXStringTextureOp ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureStageState, GFXTSSColorArg1 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureStageState, GFXTSSColorArg2 ); + GFX_STRING_ASSIGN_MACRO_EX( GFXStringTextureStageState, GFXTSSAlphaOp, GFXStringTextureOp ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureStageState, GFXTSSAlphaArg1 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureStageState, GFXTSSAlphaArg2 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureStageState, GFXTSSBumpEnvMat00 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureStageState, GFXTSSBumpEnvMat01 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureStageState, GFXTSSBumpEnvMat10 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureStageState, GFXTSSBumpEnvMat11 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureStageState, GFXTSSTexCoordIndex ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureStageState, GFXTSSBumpEnvlScale ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureStageState, GFXTSSBumpEnvlOffset ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureStageState, GFXTSSTextureTransformFlags ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureStageState, GFXTSSColorArg0 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureStageState, GFXTSSAlphaArg0 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureStageState, GFXTSSResultArg ); + + GFX_STRING_ASSIGN_MACRO( GFXStringTextureStageState, GFXTSSConstant ); + VALIDATE_LOOKUPTABLE( GFXStringTextureStageState, GFXTSS ); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE( GFXStringTextureAddress, GFXAddress, const char * ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureAddress, GFXAddressWrap ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureAddress, GFXAddressMirror ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureAddress, GFXAddressClamp ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureAddress, GFXAddressBorder ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureAddress, GFXAddressMirrorOnce ); + VALIDATE_LOOKUPTABLE(GFXStringTextureAddress, GFXAddress ); +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + INIT_LOOKUPTABLE( GFXStringTextureOp, GFXTOP, const char * ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureOp, GFXTOPDisable ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureOp, GFXTOPSelectARG1 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureOp, GFXTOPSelectARG2 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureOp, GFXTOPModulate ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureOp, GFXTOPModulate2X ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureOp, GFXTOPModulate4X ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureOp, GFXTOPAdd ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureOp, GFXTOPAddSigned ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureOp, GFXTOPAddSigned2X ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureOp, GFXTOPSubtract ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureOp, GFXTOPAddSmooth ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureOp, GFXTOPBlendDiffuseAlpha ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureOp, GFXTOPBlendTextureAlpha ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureOp, GFXTOPBlendFactorAlpha ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureOp, GFXTOPBlendTextureAlphaPM ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureOp, GFXTOPBlendCURRENTALPHA ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureOp, GFXTOPPreModulate ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureOp, GFXTOPModulateAlphaAddColor ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureOp, GFXTOPModulateColorAddAlpha ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureOp, GFXTOPModulateInvAlphaAddColor ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureOp, GFXTOPModulateInvColorAddAlpha ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureOp, GFXTOPBumpEnvMap ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureOp, GFXTOPBumpEnvMapLuminance ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureOp, GFXTOPDotProduct3 ); + GFX_STRING_ASSIGN_MACRO( GFXStringTextureOp, GFXTOPLERP ); + VALIDATE_LOOKUPTABLE( GFXStringTextureOp, GFXTOP ); + + INIT_LOOKUPTABLE( GFXStringFillMode, GFXFill, const char * ); + GFX_STRING_ASSIGN_MACRO( GFXStringFillMode, GFXFillPoint ); + GFX_STRING_ASSIGN_MACRO( GFXStringFillMode, GFXFillWireframe ); + GFX_STRING_ASSIGN_MACRO( GFXStringFillMode, GFXFillSolid ); + VALIDATE_LOOKUPTABLE( GFXStringFillMode, GFXFill ); +} diff --git a/Engine/source/gfx/gfxStringEnumTranslate.h b/Engine/source/gfx/gfxStringEnumTranslate.h new file mode 100644 index 000000000..2184ab18f --- /dev/null +++ b/Engine/source/gfx/gfxStringEnumTranslate.h @@ -0,0 +1,89 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _GFXSTRINGENUMTRANSLATE_H_ +#define _GFXSTRINGENUMTRANSLATE_H_ + +#ifndef _PLATFORM_H_ + #include "platform/platform.h" +#endif +#ifndef _GFXENUMS_H_ + #include "gfx/gfxEnums.h" +#endif +#ifndef _CONSOLE_H_ + #include "console/console.h" +#endif +#ifndef _DYNAMIC_CONSOLETYPES_H_ + #include "console/dynamicTypes.h" +#endif +#ifndef _GFXAPI_H_ + #include "gfx/gfxAPI.h" +#endif + + +//------------------------------------------------------------------------------ + +namespace GFXStringEnumTranslate +{ + void init(); +}; + +//------------------------------------------------------------------------------ + +extern const char *GFXStringIndexFormat[GFXIndexFormat_COUNT]; +extern const char *GFXStringSamplerState[GFXSAMP_COUNT]; +extern const char *GFXStringTextureFormat[GFXFormat_COUNT]; +extern const char *GFXStringRenderState[GFXRenderState_COUNT]; +extern const char *GFXStringTextureFilter[GFXTextureFilter_COUNT]; +extern const char *GFXStringBlend[GFXBlend_COUNT]; +extern const char *GFXStringBlendOp[GFXBlendOp_COUNT]; +extern const char *GFXStringStencilOp[GFXStencilOp_COUNT]; +extern const char *GFXStringCmpFunc[GFXCmp_COUNT]; +extern const char *GFXStringCullMode[GFXCull_COUNT]; +extern const char *GFXStringPrimType[GFXPT_COUNT]; +extern const char *GFXStringTextureStageState[GFXTSS_COUNT]; +extern const char *GFXStringTextureAddress[GFXAddress_COUNT]; +extern const char *GFXStringTextureOp[GFXTOP_COUNT]; +extern const char *GFXStringFillMode[GFXFill_COUNT]; + +typedef const char *(*StringValueLookupFn)( const U32 &value ); + +extern StringValueLookupFn GFXStringRenderStateValueLookup[GFXRenderState_COUNT]; +extern StringValueLookupFn GFXStringSamplerStateValueLookup[GFXSAMP_COUNT]; +extern StringValueLookupFn GFXStringTextureStageStateValueLookup[GFXTSS_COUNT]; + +#define GFXREVERSE_LOOKUP( tablearray, enumprefix, val ) \ + for( int i = enumprefix##_FIRST; i < enumprefix##_COUNT; i++ ) \ + if( (int)tablearray##[i] == val ) \ + { \ + val = i; \ + break; \ + } \ + + +// TODOS! +extern EnumTable gTextureArgumentEnumTable_M; +//extern EnumTable srcBlendFactorTable; +//extern EnumTable dstBlendFactorTable; +//extern EnumTable::Enums srcBlendFactorLookup[9]; +//extern EnumTable::Enums dstBlendFactorLookup[9]; + +#endif \ No newline at end of file diff --git a/Engine/source/gfx/gfxStructs.cpp b/Engine/source/gfx/gfxStructs.cpp new file mode 100644 index 000000000..dca361af1 --- /dev/null +++ b/Engine/source/gfx/gfxStructs.cpp @@ -0,0 +1,79 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/gfxDevice.h" + + +GFXVideoMode::GFXVideoMode() +{ + bitDepth = 32; + fullScreen = false; + refreshRate = 60; + wideScreen = false; + resolution.set(800,600); + antialiasLevel = 0; +} + +void GFXVideoMode::parseFromString( const char *str ) +{ + if(!str) + return; + + // Copy the string, as dStrtok is destructive + char *tempBuf = new char[dStrlen( str ) + 1]; + dStrcpy( tempBuf, str ); + +#define PARSE_ELEM(type, var, func, tokParam, sep) \ + if(const char *ptr = dStrtok( tokParam, sep)) \ + { type tmp = func(ptr); if(tmp > 0) var = tmp; } + + PARSE_ELEM(S32, resolution.x, dAtoi, tempBuf, " x\0") + PARSE_ELEM(S32, resolution.y, dAtoi, NULL, " x\0") + PARSE_ELEM(S32, fullScreen, dAtob, NULL, " \0") + PARSE_ELEM(S32, bitDepth, dAtoi, NULL, " \0") + PARSE_ELEM(S32, refreshRate, dAtoi, NULL, " \0") + PARSE_ELEM(S32, antialiasLevel, dAtoi, NULL, " \0") + +#undef PARSE_ELEM + + delete [] tempBuf; +} + +const String GFXVideoMode::toString() const +{ + return String::ToString("%d %d %s %d %d %d", resolution.x, resolution.y, (fullScreen ? "true" : "false"), bitDepth, refreshRate, antialiasLevel); +} + +void GFXShaderMacro::stringize( const Vector ¯os, String *outString ) +{ + Vector::const_iterator itr = macros.begin(); + for ( ; itr != macros.end(); itr++ ) + { + (*outString) += itr->name; + if ( itr->value.isNotEmpty() ) + { + (*outString) += "="; + (*outString) += itr->value; + } + (*outString) += ";"; + } +} \ No newline at end of file diff --git a/Engine/source/gfx/gfxStructs.h b/Engine/source/gfx/gfxStructs.h new file mode 100644 index 000000000..0d1672712 --- /dev/null +++ b/Engine/source/gfx/gfxStructs.h @@ -0,0 +1,194 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXSTRUCTS_H_ +#define _GFXSTRUCTS_H_ + +#ifndef _COLOR_H_ +#include "core/color.h" +#endif +#ifndef _GFXVERTEXCOLOR_H_ +#include "gfx/gfxVertexColor.h" +#endif +#ifndef _GFXENUMS_H_ +#include "gfx/gfxEnums.h" +#endif +#ifndef _MMATH_H_ +#include "math/mMath.h" +#endif +#ifndef _PROFILER_H_ +#include "platform/profiler.h" +#endif +#ifndef _GFXRESOURCE_H_ +#include "gfx/gfxResource.h" +#endif +#ifndef _REFBASE_H_ +#include "core/util/refBase.h" +#endif +#ifndef _GFXVERTEXTYPES_H_ +#include "gfx/gfxVertexTypes.h" +#endif + + +//----------------------------------------------------------------------------- +// This class is used to interact with an API's fixed function lights. See GFX->setLight +class GFXLightInfo +{ +public: + enum Type { + Point = 0, + Spot = 1, + Vector = 2, + Ambient = 3, + }; + Type mType; + + Point3F mPos; + VectorF mDirection; + ColorF mColor; + ColorF mAmbient; + F32 mRadius; + F32 mInnerConeAngle; + F32 mOuterConeAngle; + + /// @todo Revisit below (currently unused by fixed function lights) + Point3F position; + ColorF ambient; + ColorF diffuse; + ColorF specular; + VectorF spotDirection; + F32 spotExponent; + F32 spotCutoff; + F32 constantAttenuation; + F32 linearAttenuation; + F32 quadraticAttenuation; +}; + +//----------------------------------------------------------------------------- + +// Material definition for FF lighting +struct GFXLightMaterial +{ + ColorF ambient; + ColorF diffuse; + ColorF specular; + ColorF emissive; + F32 shininess; +}; + +//----------------------------------------------------------------------------- + +struct GFXVideoMode +{ + GFXVideoMode(); + + Point2I resolution; + U32 bitDepth; + U32 refreshRate; + bool fullScreen; + bool wideScreen; + // When this is returned from GFX, it's the max, otherwise it's the desired AA level. + U32 antialiasLevel; + + inline bool operator == ( const GFXVideoMode &otherMode ) const + { + if( otherMode.fullScreen != fullScreen ) + return false; + if( otherMode.resolution != resolution ) + return false; + if( otherMode.bitDepth != bitDepth ) + return false; + if( otherMode.refreshRate != refreshRate ) + return false; + if( otherMode.wideScreen != wideScreen ) + return false; + if( otherMode.antialiasLevel != antialiasLevel) + return false; + + return true; + } + + inline bool operator !=( const GFXVideoMode& otherMode ) const + { + return !( *this == otherMode ); + } + + /// Fill whatever fields we can from the passed string, which should be + /// of form "width height [bitDepth [refreshRate] [antialiasLevel]]" Unspecified fields + /// aren't modified, so you may want to set defaults before parsing. + void parseFromString( const char *str ); + + /// Gets a string representation of the object as + /// "resolution.x resolution.y fullScreen bitDepth refreshRate antialiasLevel" + /// + /// \return (string) A string representation of the object. + const String toString() const; +}; + + +//----------------------------------------------------------------------------- + +struct GFXPrimitive +{ + GFXPrimitiveType type; + + U32 startVertex; /// offset into vertex buffer to change where vertex[0] is + U32 minIndex; /// minimal value we will see in the indices + U32 startIndex; /// start of indices in buffer + U32 numPrimitives; /// how many prims to render + U32 numVertices; /// how many vertices... (used for locking, we lock from minIndex to minIndex + numVertices) + + GFXPrimitive() + { + dMemset( this, 0, sizeof( GFXPrimitive ) ); + } +}; + +/// Passed to GFX for shader defines. +struct GFXShaderMacro +{ + GFXShaderMacro() {} + + GFXShaderMacro( const GFXShaderMacro ¯o ) + : name( macro.name ), + value( macro.value ) + {} + + GFXShaderMacro( const String &name_, + const String &value_ = String::EmptyString ) + : name( name_ ), + value( value_ ) + {} + + ~GFXShaderMacro() {} + + /// The macro name. + String name; + + /// The optional macro value. + String value; + + static void stringize( const Vector ¯os, String *outString ); +}; + + +#endif // _GFXSTRUCTS_H_ diff --git a/Engine/source/gfx/gfxTarget.cpp b/Engine/source/gfx/gfxTarget.cpp new file mode 100644 index 000000000..d58eb30d0 --- /dev/null +++ b/Engine/source/gfx/gfxTarget.cpp @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/gfxTarget.h" +#include "console/console.h" + +GFXTextureObject *GFXTextureTarget::sDefaultDepthStencil = reinterpret_cast( 0x1 ); + +const String GFXTarget::describeSelf() const +{ + return String(); +} \ No newline at end of file diff --git a/Engine/source/gfx/gfxTarget.h b/Engine/source/gfx/gfxTarget.h new file mode 100644 index 000000000..88ce4040f --- /dev/null +++ b/Engine/source/gfx/gfxTarget.h @@ -0,0 +1,207 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXTARGET_H_ +#define _GFXTARGET_H_ + +#ifndef _REFBASE_H_ +#include "core/util/refBase.h" +#endif +#ifndef _GFXENUMS_H_ +#include "gfx/gfxEnums.h" +#endif +#ifndef _GFXRESOURCE_H_ +#include "gfx/gfxResource.h" +#endif +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif + + +class Point2I; +class PlatformWindow; +class GFXCubemap; +class GFXTextureObject; + +/// Base class for a target to which GFX can render. +/// +/// Most modern graphics hardware supports selecting render targets. However, +/// there may be multiple types of render target, with wildly varying +/// device-level implementations, resource requirements, and so forth. +/// +/// This base class is used to represent a render target; it might be a context +/// tied to a window, or a set of surfaces or textures. +class GFXTarget : public StrongRefBase, public GFXResource +{ + friend class GFXD3D9Device; + friend class GFX360Device; + +private: + S32 mChangeToken; + S32 mLastAppliedChange; + +protected: + + /// Called whenever a change is made to this target. + inline void invalidateState() + { + mChangeToken++; + } + + /// Called when the device has applied pending state. + inline void stateApplied() + { + mLastAppliedChange = mChangeToken; + } +public: + + /// Constructor to initialize the state tracking logic. + GFXTarget() : mChangeToken( 0 ), + mLastAppliedChange( 0 ) + { + } + virtual ~GFXTarget() {} + + /// Called to check if we have pending state for the device to apply. + inline const bool isPendingState() const + { + return (mChangeToken != mLastAppliedChange); + } + + /// Returns the size in pixels of the render target. + virtual const Point2I getSize()=0; + + /// Returns the texture format of the render target. + virtual GFXFormat getFormat()=0; + + // GFXResourceInterface + /// The resource should put a description of itself (number of vertices, size/width of texture, etc.) in buffer + virtual const String describeSelf() const; + + /// This is called to set the render target. + virtual void activate() { } + + /// This is called when the target is not being used anymore. + virtual void deactivate() { } + + /// This tells the target that the contents of this target should be restored + /// when activate() is next called. + virtual void preserve() { } + + /// Copy this surface to the passed GFXTextureObject. + /// @param tex The GFXTextureObject to copy to. + virtual void resolveTo( GFXTextureObject *tex ) { } +}; + +/// A render target associated with an OS window. +/// +/// Various API/OS combinations will implement their own GFXTargets for +/// rendering to a window. However, they are all subclasses of GFXWindowTarget. +/// +/// This allows platform-neutral code to safely distinguish between various +/// types of render targets (using dynamic_cast<>), as well as letting it +/// gain access to useful things like the corresponding PlatformWindow. +class GFXWindowTarget : public GFXTarget +{ +protected: + PlatformWindow *mWindow; +public: + GFXWindowTarget() : mWindow(NULL){}; + GFXWindowTarget( PlatformWindow *windowObject ) + { + mWindow = windowObject; + } + virtual ~GFXWindowTarget() {} + + /// Returns a pointer to the window this target is bound to. + inline PlatformWindow *getWindow() { return mWindow; }; + + /// Present latest buffer, if buffer swapping is in effect. + virtual bool present()=0; + + /// Notify the target that the video mode on the window has changed. + virtual void resetMode()=0; +}; + +/// A render target associated with one or more textures. +/// +/// Although some APIs allow directly selecting any texture or surfaces, in +/// some cases it is necessary to allocate helper resources to enable RTT +/// operations. +/// +/// @note A GFXTextureTarget will retain references to textures that are +/// attached to it, so be sure to clear them out when you're done! +/// +/// @note Different APIs have different restrictions on what they can support +/// here. Be aware when mixing cubemaps vs. non-cubemaps, or targets of +/// different resolutions. The devices will attempt to limit behavior +/// to things that are safely portable, but they cannot catch every +/// possible situation for all drivers and API - so make sure to +/// actually test things! +class GFXTextureTarget : public GFXTarget +{ +public: + enum RenderSlot + { + DepthStencil, + Color0, Color1, Color2, Color3, Color4, + MaxRenderSlotId, + }; + + static GFXTextureObject *sDefaultDepthStencil; + + virtual ~GFXTextureTarget() {} + + /// Attach a surface to a given slot as part of this render target. + /// + /// @param slot What slot is used for multiple render target (MRT) effects. + /// Most of the time you'll use Color0. + /// @param tex A texture and miplevel to bind for rendering, or else NULL/0 + /// to clear a slot. + /// @param mipLevel What level of this texture are we rendering to? + /// @param zOffset If this is a depth texture, what z level are we + /// rendering to? + virtual void attachTexture(RenderSlot slot, GFXTextureObject *tex, U32 mipLevel=0, U32 zOffset = 0) = 0; + + /// Support binding to cubemaps. + /// + /// @param slot What slot is used for multiple render target (MRT) effects. + /// Most of the time you'll use Color0. + /// @param tex What cubemap will we be rendering to? + /// @param face A face identifier. + /// @param mipLevel What level of this texture are we rendering to? + virtual void attachTexture(RenderSlot slot, GFXCubemap *tex, U32 face, U32 mipLevel=0) = 0; + + /// Resolve the current render target data to the associated textures. This method + /// will get called automatically when a rendertarget is changed, before new geometry + /// is drawn to a different rendertarget. This method can also be called to + /// gather render target data without switching targets. + /// + /// By default, this method will resolve all color targets. + virtual void resolve()=0; +}; + +typedef StrongRefPtr GFXTargetRef; +typedef StrongRefPtr GFXWindowTargetRef; +typedef StrongRefPtr GFXTextureTargetRef; + +#endif // _GFXTARGET_H_ diff --git a/Engine/source/gfx/gfxTextureHandle.cpp b/Engine/source/gfx/gfxTextureHandle.cpp new file mode 100644 index 000000000..4ea34d8e0 --- /dev/null +++ b/Engine/source/gfx/gfxTextureHandle.cpp @@ -0,0 +1,161 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/gfxTextureHandle.h" + +#include "gfx/gfxDevice.h" +#include "gfx/gfxTextureManager.h" + + +GFXTexHandle GFXTexHandle::ZERO; +GFXTexHandle GFXTexHandle::ONE; +GFXTexHandle GFXTexHandle::ZUP; + + +GFXTexHandle::GFXTexHandle( GFXTextureObject *obj ) +{ + StrongObjectRef::set( obj ); +} + +GFXTexHandle::GFXTexHandle( const GFXTexHandle &handle, const String &desc ) +{ + StrongObjectRef::set( handle.getPointer() ); + + #ifdef TORQUE_DEBUG + if ( getPointer() ) + getPointer()->mDebugDescription = desc; + #endif +} + +GFXTexHandle::GFXTexHandle( const String &texName, GFXTextureProfile *profile, const String &desc ) +{ + set( texName, profile, desc ); +} + +bool GFXTexHandle::set( const String &texName, GFXTextureProfile *profile, const String &desc ) +{ + // Clear the existing texture first, so that + // its memory is free for the new allocation. + free(); + + // Create and set the new texture. + AssertFatal( texName.isNotEmpty(), "Texture name is empty" ); + StrongObjectRef::set( TEXMGR->createTexture( texName, profile ) ); + + #ifdef TORQUE_DEBUG + if ( getPointer() ) + getPointer()->mDebugDescription = desc; + #endif + + return isValid(); +} + +GFXTexHandle::GFXTexHandle( GBitmap *bmp, GFXTextureProfile *profile, bool deleteBmp, const String &desc ) +{ + set( bmp, profile, deleteBmp, desc ); +} + +bool GFXTexHandle::set( GBitmap *bmp, GFXTextureProfile *profile, bool deleteBmp, const String &desc ) +{ + // Clear the existing texture first, so that + // its memory is free for the new allocation. + free(); + + // Create and set the new texture. + AssertFatal( bmp, "Bitmap is NULL" ); + StrongObjectRef::set( TEXMGR->createTexture( bmp, String(), profile, deleteBmp ) ); + + #ifdef TORQUE_DEBUG + if ( getPointer() ) + getPointer()->mDebugDescription = desc; + #endif + + return isValid(); +} + +GFXTexHandle::GFXTexHandle( DDSFile *dds, GFXTextureProfile *profile, bool deleteDDS, const String &desc ) +{ + set( dds, profile, deleteDDS, desc ); +} + +bool GFXTexHandle::set( DDSFile *dds, GFXTextureProfile *profile, bool deleteDDS, const String &desc ) +{ + // Clear the existing texture first, so that + // its memory is free for the new allocation. + free(); + + // Create and set the new texture. + AssertFatal( dds, "Bitmap is NULL" ); + StrongObjectRef::set( TEXMGR->createTexture( dds, profile, deleteDDS ) ); + + #ifdef TORQUE_DEBUG + if ( getPointer() ) + getPointer()->mDebugDescription = desc; + #endif + + return isValid(); +} + +GFXTexHandle::GFXTexHandle( U32 width, U32 height, GFXFormat format, GFXTextureProfile *profile, const String &desc, U32 numMipLevels, S32 antialiasLevel) +{ + set( width, height, format, profile, desc, numMipLevels, antialiasLevel ); +} + +bool GFXTexHandle::set( U32 width, U32 height, GFXFormat format, GFXTextureProfile *profile, const String &desc, U32 numMipLevels, S32 antialiasLevel) +{ + // Clear the existing texture first, so that + // its memory is free for the new allocation. + free(); + + // Create and set the new texture. + StrongObjectRef::set( TEXMGR->createTexture( width, height, format, profile, numMipLevels, antialiasLevel ) ); + + #ifdef TORQUE_DEBUG + if ( getPointer() ) + getPointer()->mDebugDescription = desc; + #endif + + return isValid(); +} + +bool GFXTexHandle::set( U32 width, U32 height, U32 depth, void *pixels, GFXFormat format, GFXTextureProfile *profile, const String &desc, U32 numMipLevels ) +{ + // Clear the existing texture first, so that + // its memory is free for the new allocation. + free(); + + // Create and set the new texture. + StrongObjectRef::set( TEXMGR->createTexture( width, height, depth, pixels, format, profile ) ); + + #ifdef TORQUE_DEBUG + if ( getPointer() ) + getPointer()->mDebugDescription = desc; + #endif + + return isValid(); +} + +void GFXTexHandle::refresh() +{ + TEXMGR->reloadTexture( getPointer() ); +} diff --git a/Engine/source/gfx/gfxTextureHandle.h b/Engine/source/gfx/gfxTextureHandle.h new file mode 100644 index 000000000..9ae488e7a --- /dev/null +++ b/Engine/source/gfx/gfxTextureHandle.h @@ -0,0 +1,129 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXTEXTUREHANDLE_H_ +#define _GFXTEXTUREHANDLE_H_ + +#ifndef _GFXTEXTUREOBJECT_H_ +#include "gfx/gfxTextureObject.h" +#endif +#ifndef _MPOINT2_H_ +#include "math/mPoint2.h" +#endif + + +class GFXTextureProfile; + + +/// A reference counted handle to a texture resource. +class GFXTexHandle : public StrongRefPtr +{ +public: + GFXTexHandle() {} + GFXTexHandle( GFXTextureObject* obj ); + GFXTexHandle( const GFXTexHandle &handle, const String &desc ); + + // load texture + GFXTexHandle( const String &texName, GFXTextureProfile *profile, const String &desc ); + bool set( const String &texName, GFXTextureProfile *profile, const String &desc ); + + // register texture + GFXTexHandle( GBitmap *bmp, GFXTextureProfile *profile, bool deleteBmp, const String &desc ); + bool set( GBitmap *bmp, GFXTextureProfile *profile, bool deleteBmp, const String &desc ); + + GFXTexHandle( DDSFile *bmp, GFXTextureProfile *profile, bool deleteDDS, const String &desc ); + bool set( DDSFile *bmp, GFXTextureProfile *profile, bool deleteDDS, const String &desc ); + + // Sized bitmap + GFXTexHandle( U32 width, U32 height, GFXFormat format, GFXTextureProfile *profile, const String &desc, U32 numMipLevels = 1, S32 antialiasLevel = 0); + bool set( U32 width, U32 height, GFXFormat format, GFXTextureProfile *profile, const String &desc, U32 numMipLevels = 1, S32 antialiasLevel = 0); + bool set( U32 width, U32 height, U32 depth, void *pixels, GFXFormat format, GFXTextureProfile *profile, const String &desc, U32 numMipLevels = 1 ); + + /// Returns the width and height as a point. + Point2I getWidthHeight() const { return getPointer() ? Point2I( getPointer()->getWidth(), getPointer()->getHeight() ) : Point2I::Zero; } + + U32 getWidth() const { return getPointer() ? getPointer()->getWidth() : 0; } + U32 getHeight() const { return getPointer() ? getPointer()->getHeight() : 0; } + U32 getDepth() const { return getPointer() ? getPointer()->getDepth() : 0; } + GFXFormat getFormat() const { return getPointer() ? getPointer()->getFormat() : GFXFormat_COUNT; } + + /// Reloads the texture. + /// @see GFXTextureManager::reloadTexture + void refresh(); + + /// Releases the texture handle. + void free() { StrongObjectRef::set( NULL ); } + + GFXLockedRect *lock( U32 mipLevel = 0, RectI *inRect = NULL ) + { + return getPointer()->lock(mipLevel, inRect); + } + + void unlock( U32 mipLevel = 0) + { + getPointer()->unlock(mipLevel); + } + + // copy to bitmap. see gfxTetureObject.h for description of what types of textures + // can be copied into bitmaps. returns true if successful, false otherwise + bool copyToBmp(GBitmap* bmp) { return getPointer() ? getPointer()->copyToBmp(bmp) : false; } + + //--------------------------------------------------------------------------- + // Operator overloads + //--------------------------------------------------------------------------- + GFXTexHandle& operator=(const GFXTexHandle &t) + { + StrongObjectRef::set(t.getPointer()); + return *this; + } + + GFXTexHandle& operator=( GFXTextureObject *to) + { + StrongObjectRef::set(to); + return *this; + } + + bool operator==(const GFXTexHandle &t) const { return t.getPointer() == getPointer(); } + bool operator!=(const GFXTexHandle &t) const { return t.getPointer() != getPointer(); } + + /// Returns the texture object. + operator GFXTextureObject*() const { return getPointer(); } + + /// Returns the backing bitmap for this texture. + GBitmap* getBitmap() { return getPointer() ? getPointer()->getBitmap() : NULL; } + const GBitmap* getBitmap() const { return getPointer() ? getPointer()->getBitmap() : NULL; } + + + /// Helper 2x2 R8G8B8A8 texture filled with 0. + static GFXTexHandle ZERO; + + /// Helper 2x2 R8G8B8A8 texture filled with 255. + static GFXTexHandle ONE; + + /// Helper 2x2 R8G8B8A8 normal map texture filled + /// with 128, 128, 255. + static GFXTexHandle ZUP; + +}; + + +#endif // _GFXTEXTUREHANDLE_H_ diff --git a/Engine/source/gfx/gfxTextureManager.cpp b/Engine/source/gfx/gfxTextureManager.cpp new file mode 100644 index 000000000..2c2dd4fe6 --- /dev/null +++ b/Engine/source/gfx/gfxTextureManager.cpp @@ -0,0 +1,1229 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/gfxTextureManager.h" + +#include "gfx/gfxDevice.h" +#include "gfx/gfxCardProfile.h" +#include "gfx/gfxStringEnumTranslate.h" +#include "gfx/bitmap/ddsUtils.h" +#include "core/strings/stringFunctions.h" +#include "core/util/safeDelete.h" +#include "core/resourceManager.h" +#include "core/volume.h" +#include "core/util/dxt5nmSwizzle.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" + + +//#define DEBUG_SPEW + + +S32 GFXTextureManager::smTextureReductionLevel = 0; + +GFXTextureManager::EventSignal GFXTextureManager::smEventSignal; + +static const String sDDSExt( "dds" ); + +void GFXTextureManager::init() +{ + Con::addVariable( "$pref::Video::textureReductionLevel", TypeS32, &smTextureReductionLevel, + "The number of mipmap levels to drop on loaded textures to reduce " + "video memory usage. It will skip any textures that have been defined " + "as not allowing down scaling.\n" + "@ingroup GFX\n" ); +} + +GFXTextureManager::GFXTextureManager() +{ + mListHead = mListTail = NULL; + mTextureManagerState = GFXTextureManager::Living; + + // Set up the hash table + mHashCount = 1023; + mHashTable = new GFXTextureObject *[mHashCount]; + for(U32 i = 0; i < mHashCount; i++) + mHashTable[i] = NULL; +} + +GFXTextureManager::~GFXTextureManager() +{ + if( mHashTable ) + SAFE_DELETE_ARRAY( mHashTable ); + + mCubemapTable.clear(); +} + +U32 GFXTextureManager::getTextureDownscalePower( GFXTextureProfile *profile ) +{ + if ( !profile || profile->canDownscale() ) + return smTextureReductionLevel; + + return 0; +} + +bool GFXTextureManager::validateTextureQuality( GFXTextureProfile *profile, U32 &width, U32 &height ) +{ + U32 scaleFactor = getTextureDownscalePower( profile ); + if ( scaleFactor == 0 ) + return true; + + // Otherwise apply the appropriate scale... + width >>= scaleFactor; + height >>= scaleFactor; + + return true; +} + +void GFXTextureManager::kill() +{ + AssertFatal( mTextureManagerState != GFXTextureManager::Dead, "Texture Manager already killed!" ); + + // Release everything in the cache we can + // so we don't leak any textures. + cleanupCache(); + + GFXTextureObject *curr = mListHead; + GFXTextureObject *temp; + + // Actually delete all the textures we know about. + while( curr != NULL ) + { + temp = curr->mNext; + curr->kill(); + curr = temp; + } + + mCubemapTable.clear(); + + mTextureManagerState = GFXTextureManager::Dead; +} + +void GFXTextureManager::zombify() +{ + AssertFatal( mTextureManagerState != GFXTextureManager::Zombie, "Texture Manager already a zombie!" ); + + // Notify everyone that cares about the zombification! + smEventSignal.trigger( GFXZombify ); + + // Release unused pool textures. + cleanupPool(); + + // Release everything in the cache we can. + cleanupCache(); + + // Free all the device copies of the textures. + GFXTextureObject *temp = mListHead; + while( temp != NULL ) + { + freeTexture( temp, true ); + temp = temp->mNext; + } + + // Finally, note our state. + mTextureManagerState = GFXTextureManager::Zombie; +} + +void GFXTextureManager::resurrect() +{ + // Reupload all the device copies of the textures. + GFXTextureObject *temp = mListHead; + + while( temp != NULL ) + { + refreshTexture( temp ); + temp = temp->mNext; + } + + // Notify callback registries. + smEventSignal.trigger( GFXResurrect ); + + // Update our state. + mTextureManagerState = GFXTextureManager::Living; +} + +void GFXTextureManager::cleanupPool() +{ + PROFILE_SCOPE( GFXTextureManager_CleanupPool ); + + TexturePoolMap::Iterator iter = mTexturePool.begin(); + for ( ; iter != mTexturePool.end(); ) + { + if ( iter->value->getRefCount() == 1 ) + { + // This texture is unreferenced, so take the time + // now to completely remove it from the pool. + TexturePoolMap::Iterator unref = iter; + iter++; + unref->value = NULL; + mTexturePool.erase( unref ); + continue; + } + + iter++; + } +} + +void GFXTextureManager::requestDeleteTexture( GFXTextureObject *texture ) +{ + // If this is a non-cached texture then just really delete it. + if ( texture->mTextureLookupName.isEmpty() ) + { + delete texture; + return; + } + + // Set the time and store it. + texture->mDeleteTime = Platform::getTime(); + mToDelete.push_back_unique( texture ); +} + +void GFXTextureManager::cleanupCache( U32 secondsToLive ) +{ + PROFILE_SCOPE( GFXTextureManager_CleanupCache ); + + U32 killTime = Platform::getTime() - secondsToLive; + + for ( U32 i=0; i < mToDelete.size(); ) + { + GFXTextureObject *tex = mToDelete[i]; + + // If the texture was picked back up by a user + // then just remove it from the list. + if ( tex->getRefCount() != 0 ) + { + mToDelete.erase_fast( i ); + continue; + } + + // If its time has expired delete it for real. + if ( tex->mDeleteTime <= killTime ) + { + //Con::errorf( "Killed texture: %s", tex->mTextureLookupName.c_str() ); + delete tex; + mToDelete.erase_fast( i ); + continue; + } + + i++; + } +} + +GFXTextureObject *GFXTextureManager::_lookupTexture( const char *hashName, const GFXTextureProfile *profile ) +{ + GFXTextureObject *ret = hashFind( hashName ); + + // TODO: Profile checking HERE + + return ret; +} + +GFXTextureObject *GFXTextureManager::_lookupTexture( const DDSFile *ddsFile, const GFXTextureProfile *profile ) +{ + if( ddsFile->getTextureCacheString().isNotEmpty() ) + { + // Call _lookupTexture() + return _lookupTexture( ddsFile->getTextureCacheString(), profile ); + } + + return NULL; +} + +GFXTextureObject *GFXTextureManager::createTexture( GBitmap *bmp, const String &resourceName, GFXTextureProfile *profile, bool deleteBmp ) +{ + AssertFatal(bmp, "GFXTextureManager::createTexture() - Got NULL bitmap!"); + + GFXTextureObject *cacheHit = _lookupTexture( resourceName, profile ); + if( cacheHit != NULL) + { + // Con::errorf("Cached texture '%s'", (resourceName.isNotEmpty() ? resourceName.c_str() : "unknown")); + if (deleteBmp) + delete bmp; + return cacheHit; + } + + return _createTexture( bmp, resourceName, profile, deleteBmp, NULL ); +} + +GFXTextureObject *GFXTextureManager::_createTexture( GBitmap *bmp, + const String &resourceName, + GFXTextureProfile *profile, + bool deleteBmp, + GFXTextureObject *inObj ) +{ + PROFILE_SCOPE( GFXTextureManager_CreateTexture_Bitmap ); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[GFXTextureManager] _createTexture (GBitmap) '%s'", + resourceName.c_str() + ); + #endif + + // Massage the bitmap based on any resize rules. + U32 scalePower = getTextureDownscalePower( profile ); + + GBitmap *realBmp = bmp; + U32 realWidth = bmp->getWidth(); + U32 realHeight = bmp->getHeight(); + + if ( scalePower && + isPow2(bmp->getWidth()) && + isPow2(bmp->getHeight()) && + profile->canDownscale() ) + { + // We only work with power of 2 textures for now, so we + // don't have to worry about padding. + + // We downscale the bitmap on the CPU... this is the reason + // you should be using DDS which already has good looking mips. + GBitmap *padBmp = bmp; + padBmp->extrudeMipLevels(); + scalePower = getMin( scalePower, padBmp->getNumMipLevels() - 1 ); + + realWidth = getMax( (U32)1, padBmp->getWidth() >> scalePower ); + realHeight = getMax( (U32)1, padBmp->getHeight() >> scalePower ); + realBmp = new GBitmap( realWidth, realHeight, false, bmp->getFormat() ); + + // Copy to the new bitmap... + dMemcpy( realBmp->getWritableBits(), + padBmp->getBits(scalePower), + padBmp->getBytesPerPixel() * realWidth * realHeight ); + + // This line is commented out because createPaddedBitmap is commented out. + // If that line is added back in, this line should be added back in. + // delete padBmp; + } + + // Call the internal create... (use the real* variables now, as they + // reflect the reality of the texture we are creating.) + U32 numMips = 0; + GFXFormat realFmt = realBmp->getFormat(); + _validateTexParams( realWidth, realHeight, profile, numMips, realFmt ); + + GFXTextureObject *ret; + if ( inObj ) + { + // If the texture has changed in dimensions + // then we need to recreate it. + if ( inObj->getWidth() != realWidth || + inObj->getHeight() != realHeight || + inObj->getFormat() != realFmt ) + ret = _createTextureObject( realHeight, realWidth, 0, realFmt, profile, numMips, false, 0, inObj ); + else + ret = inObj; + } + else + ret = _createTextureObject(realHeight, realWidth, 0, realFmt, profile, numMips ); + + if(!ret) + { + Con::errorf("GFXTextureManager - failed to create texture (1) for '%s'", (resourceName.isNotEmpty() ? resourceName.c_str() : "unknown")); + return NULL; + } + + // Extrude mip levels + // Don't do this for fonts! + if( ret->mMipLevels > 1 && ( realBmp->getNumMipLevels() == 1 ) && ( realBmp->getFormat() != GFXFormatA8 ) && + isPow2( realBmp->getHeight() ) && isPow2( realBmp->getWidth() ) && !profile->noMip() ) + { + // NOTE: This should really be done by extruding mips INTO a DDS file instead + // of modifying the gbitmap + realBmp->extrudeMipLevels(false); + } + + // If _validateTexParams kicked back a different format, than there needs to be + // a conversion + DDSFile *bmpDDS = NULL; + if( realBmp->getFormat() != realFmt ) + { + const GFXFormat oldFmt = realBmp->getFormat(); + + // TODO: Set it up so that ALL format conversions use DDSFile. Rip format + // switching out of GBitmap entirely. + if( !realBmp->setFormat( realFmt ) ) + { + // This is not the ideal implementation... + bmpDDS = DDSFile::createDDSFileFromGBitmap( realBmp ); + + bool convSuccess = false; + + if( bmpDDS != NULL ) + { + // This shouldn't live here, I don't think + switch( realFmt ) + { + case GFXFormatDXT1: + case GFXFormatDXT2: + case GFXFormatDXT3: + case GFXFormatDXT4: + case GFXFormatDXT5: + // If this is a Normal Map profile, than the data needs to be conditioned + // to use the swizzle trick + if( ret->mProfile->getType() == GFXTextureProfile::NormalMap ) + { + PROFILE_START(DXT_DXTNMSwizzle); + static DXT5nmSwizzle sDXT5nmSwizzle; + DDSUtil::swizzleDDS( bmpDDS, sDXT5nmSwizzle ); + PROFILE_END(); + } + + convSuccess = DDSUtil::squishDDS( bmpDDS, realFmt ); + break; + default: + AssertFatal(false, "Attempting to convert to a non-DXT format"); + break; + } + } + + if( !convSuccess ) + { + Con::errorf( "[GFXTextureManager]: Failed to change source format from %s to %s. Cannot create texture.", + GFXStringTextureFormat[oldFmt], GFXStringTextureFormat[realFmt] ); + delete bmpDDS; + + return NULL; + } + } +#ifdef TORQUE_DEBUG + else + { + //Con::warnf( "[GFXTextureManager]: Changed bitmap format from %s to %s.", + // GFXStringTextureFormat[oldFmt], GFXStringTextureFormat[realFmt] ); + } +#endif + } + + // Call the internal load... + if( ( bmpDDS == NULL && !_loadTexture( ret, realBmp ) ) || // If we aren't doing a DDS format change, use bitmap load + ( bmpDDS != NULL && !_loadTexture( ret, bmpDDS ) ) ) // If there is a DDS, than load that instead. A format change took place. + { + Con::errorf("GFXTextureManager - failed to load GBitmap for '%s'", (resourceName.isNotEmpty() ? resourceName.c_str() : "unknown")); + return NULL; + } + + // Do statistics and book-keeping... + + // - info for the texture... + ret->mTextureLookupName = resourceName; + ret->mBitmapSize.set(realWidth, realHeight,0); + +#ifdef TORQUE_DEBUG + if (resourceName.isNotEmpty()) + ret->mDebugDescription = resourceName; + else + ret->mDebugDescription = "Anonymous Texture Object"; + +#endif + + if(profile->doStoreBitmap()) + { + // NOTE: may store a downscaled copy! + SAFE_DELETE( ret->mBitmap ); + SAFE_DELETE( ret->mDDS ); + + if( bmpDDS == NULL ) + ret->mBitmap = new GBitmap( *realBmp ); + else + ret->mDDS = bmpDDS; + } + else + { + // Delete the DDS if we made one + SAFE_DELETE( bmpDDS ); + } + + if ( !inObj ) + _linkTexture( ret ); + + // - output debug info? + // Save texture for debug purpose + // static int texId = 0; + // char buff[256]; + // dSprintf(buff, sizeof(buff), "tex_%d", texId++); + // bmp->writePNGDebug(buff); + // texId++; + + // Before we delete the bitmap save our transparency flag + ret->mHasTransparency = realBmp->getHasTransparency(); + + // Some final cleanup... + if(realBmp != bmp) + SAFE_DELETE(realBmp); + if (deleteBmp) + SAFE_DELETE(bmp); + + // Return the new texture! + return ret; +} + +GFXTextureObject *GFXTextureManager::createTexture( DDSFile *dds, GFXTextureProfile *profile, bool deleteDDS ) +{ + AssertFatal(dds, "GFXTextureManager::createTexture() - Got NULL dds!"); + + // Check the cache first... + GFXTextureObject *cacheHit = _lookupTexture( dds, profile ); + if ( cacheHit ) + { + // Con::errorf("Cached texture '%s'", (fileName.isNotEmpty() ? fileName.c_str() : "unknown")); + if( deleteDDS ) + delete dds; + + return cacheHit; + } + + return _createTexture( dds, profile, deleteDDS, NULL ); +} + +GFXTextureObject *GFXTextureManager::_createTexture( DDSFile *dds, + GFXTextureProfile *profile, + bool deleteDDS, + GFXTextureObject *inObj ) +{ + PROFILE_SCOPE( GFXTextureManager_CreateTexture_DDS ); + + const char *fileName = dds->getTextureCacheString(); + if( !fileName ) + fileName = "unknown"; + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[GFXTextureManager] _createTexture (DDS) '%s'", + fileName + ); + #endif + + // Ignore padding from the profile. + U32 numMips = dds->mMipMapCount; + GFXFormat fmt = dds->mFormat; + _validateTexParams( dds->getHeight(), dds->getWidth(), profile, numMips, fmt ); + + if( fmt != dds->mFormat ) + { + Con::errorf( "GFXTextureManager - failed to validate texture parameters for DDS file '%s'", fileName ); + return NULL; + } + + // Call the internal create... (use the real* variables now, as they + // reflect the reality of the texture we are creating.) + + GFXTextureObject *ret; + if ( inObj ) + { + // If the texture has changed in dimensions + // then we need to recreate it. + if ( inObj->getWidth() != dds->getWidth() || + inObj->getHeight() != dds->getHeight() || + inObj->getFormat() != fmt || + inObj->getMipLevels() != numMips ) + ret = _createTextureObject( dds->getHeight(), dds->getWidth(), 0, + fmt, profile, numMips, + true, 0, inObj ); + else + ret = inObj; + } + else + ret = _createTextureObject( dds->getHeight(), dds->getWidth(), 0, + fmt, profile, numMips, true ); + + + if(!ret) + { + Con::errorf("GFXTextureManager - failed to create texture (1) for '%s' DDSFile.", fileName); + return NULL; + } + + // Call the internal load... + if(!_loadTexture(ret, dds)) + { + Con::errorf("GFXTextureManager - failed to load DDS for '%s'", fileName); + return NULL; + } + + // Do statistics and book-keeping... + + // - info for the texture... + ret->mTextureLookupName = dds->getTextureCacheString(); + ret->mBitmapSize.set( dds->mWidth, dds->mHeight, 0 ); + +#ifdef TORQUE_DEBUG + ret->mDebugDescription = fileName; +#endif + + if(profile->doStoreBitmap()) + { + // NOTE: may store a downscaled copy! + SAFE_DELETE( ret->mBitmap ); + SAFE_DELETE( ret->mDDS ); + + ret->mDDS = new DDSFile( *dds ); + } + + if ( !inObj ) + _linkTexture( ret ); + + // - output debug info? + // Save texture for debug purpose + // static int texId = 0; + // char buff[256]; + // dSprintf(buff, sizeof(buff), "tex_%d", texId++); + // bmp->writePNGDebug(buff); + // texId++; + + // Save our transparency flag + ret->mHasTransparency = dds->getHasTransparency(); + + if( deleteDDS ) + delete dds; + + // Return the new texture! + return ret; +} + +GFXTextureObject *GFXTextureManager::createTexture( const Torque::Path &path, GFXTextureProfile *profile ) +{ + PROFILE_SCOPE( GFXTextureManager_createTexture ); + + // Resource handles used for loading. Hold on to them + // throughout this function so that change notifications + // don't get added, then removed, and then re-added. + + Resource< DDSFile > dds; + Resource< GBitmap > bitmap; + + // We need to handle path's that have had "incorrect" + // extensions parsed out of the file name + Torque::Path correctPath = path; + + bool textureExt = false; + + // Easiest case to handle is when there isn't an extension + if (path.getExtension().isEmpty()) + textureExt = true; + + // Since "dds" isn't registered with GBitmap currently we + // have to test it separately + if (sDDSExt.equal( path.getExtension(), String::NoCase ) ) + textureExt = true; + + // Now loop through the rest of the GBitmap extensions + // to see if we have any matches + for ( U32 i = 0; i < GBitmap::sRegistrations.size(); i++ ) + { + // If we have gotten a match (either in this loop or before) + // then we can exit + if (textureExt) + break; + + const GBitmap::Registration ® = GBitmap::sRegistrations[i]; + const Vector &extensions = reg.extensions; + + for ( U32 j = 0; j < extensions.size(); ++j ) + { + if ( extensions[j].equal( path.getExtension(), String::NoCase ) ) + { + // Found a valid texture extension + textureExt = true; + break; + } + } + } + + // If we didn't find a valid texture extension then assume that + // the parsed out "extension" was actually intended to be part of + // the texture name so add it back + if (!textureExt) + { + correctPath.setFileName( Torque::Path::Join( path.getFileName(), '.', path.getExtension() ) ); + correctPath.setExtension( String::EmptyString ); + } + + // Check the cache first... + String pathNoExt = Torque::Path::Join( correctPath.getRoot(), ':', correctPath.getPath() ); + pathNoExt = Torque::Path::Join( pathNoExt, '/', correctPath.getFileName() ); + + GFXTextureObject *retTexObj = _lookupTexture( pathNoExt, profile ); + if( retTexObj ) + return retTexObj; + + const U32 scalePower = getTextureDownscalePower( profile ); + + // If this is a valid file (has an extension) than load it + Path realPath; + if( Torque::FS::IsFile( correctPath ) ) + { + // Check for DDS + if( sDDSExt.equal(correctPath.getExtension(), String::NoCase ) ) + { + dds = DDSFile::load( correctPath, scalePower ); + if( dds != NULL ) + { + realPath = dds.getPath(); + retTexObj = createTexture( dds, profile, false ); + } + } + else // Let GBitmap take care of it + { + bitmap = GBitmap::load( correctPath ); + if( bitmap != NULL ) + { + realPath = bitmap.getPath(); + retTexObj = createTexture( bitmap, pathNoExt, profile, false ); + } + } + } + else + { + // NOTE -- We should probably remove the code from GBitmap that tries different + // extensions for things GBitmap loads, and move it here. I think it should + // be a bit more involved than just a list of extensions. Some kind of + // extension registration thing, maybe. + + // Check to see if there is a .DDS file with this name (if no extension is provided) + Torque::Path tryDDSPath = pathNoExt; + if( tryDDSPath.getExtension().isNotEmpty() ) + tryDDSPath.setFileName( tryDDSPath.getFullFileName() ); + tryDDSPath.setExtension( sDDSExt ); + + if( Torque::FS::IsFile( tryDDSPath ) ) + { + dds = DDSFile::load( tryDDSPath, scalePower ); + if( dds != NULL ) + { + realPath = dds.getPath(); + retTexObj = createTexture( dds, profile, false ); + } + } + + // Otherwise, retTexObj stays NULL, and fall through to the generic GBitmap + // load. + } + + // If we still don't have a texture object yet, feed the correctPath to GBitmap and + // it will try a bunch of extensions + if( retTexObj == NULL ) + { + // Find and load the texture. + bitmap = GBitmap::load( correctPath ); + + if ( bitmap != NULL ) + { + realPath = bitmap.getPath(); + retTexObj = createTexture( bitmap, pathNoExt, profile, false ); + } + } + + if ( retTexObj ) + { + // Store the path for later use. + retTexObj->mPath = realPath; + + // Register the texture file for change notifications. + FS::AddChangeNotification( retTexObj->getPath(), this, &GFXTextureManager::_onFileChanged ); + } + + // Could put in a final check for 'retTexObj == NULL' here as an error message. + + return retTexObj; +} + +GFXTextureObject *GFXTextureManager::createTexture( U32 width, U32 height, void *pixels, GFXFormat format, GFXTextureProfile *profile ) +{ + // For now, stuff everything into a GBitmap and pass it off... This may need to be revisited -- BJG + GBitmap *bmp = new GBitmap(width, height, 0, format); + dMemcpy(bmp->getWritableBits(), pixels, width * height * bmp->getBytesPerPixel()); + + return createTexture( bmp, String::EmptyString, profile, true ); +} + +GFXTextureObject *GFXTextureManager::createTexture( U32 width, U32 height, GFXFormat format, GFXTextureProfile *profile, U32 numMipLevels, S32 antialiasLevel ) +{ + // Deal with sizing issues... + U32 localWidth = width; + U32 localHeight = height; + + // TODO: Format check HERE! -patw + + validateTextureQuality(profile, localWidth, localHeight); + + U32 numMips = numMipLevels; + GFXFormat checkFmt = format; + _validateTexParams( localWidth, localHeight, profile, numMips, checkFmt ); + + AssertFatal( checkFmt == format, "Anonymous texture didn't get the format it wanted." ); + + GFXTextureObject *outTex = NULL; + + // If this is a pooled profile then look there first. + if ( profile->isPooled() ) + { + outTex = _findPooledTexure( localWidth, localHeight, checkFmt, + profile, numMips, antialiasLevel ); + + // If we got a pooled texture then its + // already setup... just return it. + if ( outTex ) + return outTex; + } + + // Create the texture if we didn't get one from the pool. + if ( !outTex ) + { + outTex = _createTextureObject( localHeight, localWidth, 0, format, profile, numMips, false, antialiasLevel ); + + // Make sure we add it to the pool. + if ( outTex && profile->isPooled() ) + mTexturePool.insertEqual( profile, outTex ); + } + + if ( !outTex ) + { + Con::errorf("GFXTextureManager - failed to create anonymous texture."); + return NULL; + } + + // And do book-keeping... + // - texture info + outTex->mBitmapSize.set(localWidth, localHeight, 0); + outTex->mAntialiasLevel = antialiasLevel; + + // PWTODO: Need to assign this a lookup name before _linkTexture() is called + // otherwise it won't get a hash insert call + + _linkTexture( outTex ); + + return outTex; +} + +GFXTextureObject *GFXTextureManager::createTexture( U32 width, + U32 height, + U32 depth, + void *pixels, + GFXFormat format, + GFXTextureProfile *profile ) +{ + PROFILE_SCOPE( GFXTextureManager_CreateTexture_3D ); + + // Create texture... + GFXTextureObject *ret = _createTextureObject( height, width, depth, format, profile, 1 ); + + if(!ret) + { + Con::errorf("GFXTextureManager - failed to create anonymous texture."); + return NULL; + } + + // Call the internal load... + if( !_loadTexture( ret, pixels ) ) + { + Con::errorf("GFXTextureManager - failed to load volume texture" ); + return NULL; + } + + + // And do book-keeping... + // - texture info + ret->mBitmapSize.set( width, height, depth ); + + _linkTexture( ret ); + + + // Return the new texture! + return ret; +} + +GFXTextureObject* GFXTextureManager::_findPooledTexure( U32 width, + U32 height, + GFXFormat format, + GFXTextureProfile *profile, + U32 numMipLevels, + S32 antialiasLevel ) +{ + PROFILE_SCOPE( GFXTextureManager_FindPooledTexure ); + + GFXTextureObject *outTex; + + // First see if we have a free one in the pool. + TexturePoolMap::Iterator iter = mTexturePool.find( profile ); + for ( ; iter != mTexturePool.end() && iter->key == profile; iter++ ) + { + outTex = iter->value; + + // If the reference count is 1 then we're the only + // ones holding on to this texture and we can hand + // it out if the size matches... else its in use. + if ( outTex->getRefCount() != 1 ) + continue; + + // Check for a match... if so return it. The assignment + // to a GFXTexHandle will take care of incrementing the + // reference count and keeping it from being handed out + // to anyone else. + if ( outTex->getFormat() == format && + outTex->getWidth() == width && + outTex->getHeight() == height && + outTex->getMipLevels() == numMipLevels && + outTex->mAntialiasLevel == antialiasLevel ) + return outTex; + } + + return NULL; +} + +void GFXTextureManager::hashInsert( GFXTextureObject *object ) +{ + if ( object->mTextureLookupName.isEmpty() ) + return; + + U32 key = object->mTextureLookupName.getHashCaseInsensitive() % mHashCount; + object->mHashNext = mHashTable[key]; + mHashTable[key] = object; +} + +void GFXTextureManager::hashRemove( GFXTextureObject *object ) +{ + if ( object->mTextureLookupName.isEmpty() ) + return; + + U32 key = object->mTextureLookupName.getHashCaseInsensitive() % mHashCount; + GFXTextureObject **walk = &mHashTable[key]; + while(*walk) + { + if(*walk == object) + { + *walk = object->mHashNext; + break; + } + walk = &((*walk)->mHashNext); + } +} + +GFXTextureObject* GFXTextureManager::hashFind( const String &name ) +{ + if ( name.isEmpty() ) + return NULL; + + U32 key = name.getHashCaseInsensitive() % mHashCount; + GFXTextureObject *walk = mHashTable[key]; + for(; walk; walk = walk->mHashNext) + { + if( walk->mTextureLookupName.equal( name, String::NoCase ) ) + break; + } + + return walk; +} + +void GFXTextureManager::freeTexture(GFXTextureObject *texture, bool zombify) +{ + // Ok, let the backend deal with it. + _freeTexture(texture, zombify); +} + +void GFXTextureManager::refreshTexture(GFXTextureObject *texture) +{ + _refreshTexture(texture); +} + +void GFXTextureManager::_linkTexture( GFXTextureObject *obj ) +{ + // info for the profile + GFXTextureProfile::updateStatsForCreation(obj); + + // info for the cache + hashInsert(obj); + + // info for the master list + if( mListHead == NULL ) + mListHead = obj; + + if( mListTail != NULL ) + mListTail->mNext = obj; + + obj->mPrev = mListTail; + mListTail = obj; +} + +void GFXTextureManager::deleteTexture( GFXTextureObject *texture ) +{ + if ( mTextureManagerState == GFXTextureManager::Dead ) + return; + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[GFXTextureManager] deleteTexture '%s'", + texture->mTextureLookupName.c_str() + ); + #endif + + if( mListHead == texture ) + mListHead = texture->mNext; + if( mListTail == texture ) + mListTail = texture->mPrev; + + hashRemove( texture ); + + // If we have a path for the texture then + // remove change notifications for it. + Path texPath = texture->getPath(); + if ( !texPath.isEmpty() ) + FS::RemoveChangeNotification( texPath, this, &GFXTextureManager::_onFileChanged ); + + GFXTextureProfile::updateStatsForDeletion(texture); + + freeTexture( texture ); +} + +void GFXTextureManager::_validateTexParams( const U32 width, const U32 height, + const GFXTextureProfile *profile, + U32 &inOutNumMips, GFXFormat &inOutFormat ) +{ + // Validate mipmap parameter. If this profile requests no mips, set mips to 1. + if( profile->noMip() ) + { + inOutNumMips = 1; + } + else if( !isPow2( width ) || !isPow2( height ) ) + { + // If a texture is not power-of-2 in size for both dimensions, it must + // have only 1 mip level. + inOutNumMips = 1; + } + + // Check format, and compatibility with texture profile requirements + bool autoGenSupp = ( inOutNumMips == 0 ); + + // If the format is non-compressed, and the profile requests a compressed format + // than change the format. + GFXFormat testingFormat = inOutFormat; + if( profile->getCompression() != GFXTextureProfile::None ) + { + const int offset = profile->getCompression() - GFXTextureProfile::DXT1; + testingFormat = GFXFormat( GFXFormatDXT1 + offset ); + + // No auto-gen mips on compressed textures + autoGenSupp = false; + } + + // inOutFormat is not modified by this method + bool chekFmt = GFX->getCardProfiler()->checkFormat( testingFormat, profile, autoGenSupp ); + + if( !chekFmt ) + { + // It tested for a compressed format, and didn't like it + if( testingFormat != inOutFormat && profile->getCompression() ) + testingFormat = inOutFormat; // Reset to requested format, and try again + + // Trying again here, so reset autogen mip + autoGenSupp = ( inOutNumMips == 0 ); + + // Wow more weak sauce. There should be a better way to do this. + switch( inOutFormat ) + { + case GFXFormatR8G8B8: + testingFormat = GFXFormatR8G8B8X8; + chekFmt = GFX->getCardProfiler()->checkFormat( testingFormat, profile, autoGenSupp ); + break; + + case GFXFormatA8: + testingFormat = GFXFormatR8G8B8A8; + chekFmt = GFX->getCardProfiler()->checkFormat( testingFormat, profile, autoGenSupp ); + break; + + default: + chekFmt = GFX->getCardProfiler()->checkFormat( testingFormat, profile, autoGenSupp ); + break; + } + } + + // Write back num mips that need to be generated by GBitmap + if( !chekFmt ) + Con::errorf( "Format %s not supported with specified profile.", GFXStringTextureFormat[inOutFormat] ); + else + { + inOutFormat = testingFormat; + + // If auto gen mipmaps were requested, and they aren't supported for whatever + // reason, than write out the number of mips that need to be generated. + // + // NOTE: Does this belong here? + if( inOutNumMips == 0 && !autoGenSupp ) + { + U32 currWidth = width; + U32 currHeight = height; + + inOutNumMips = 1; + do + { + currWidth >>= 1; + currHeight >>= 1; + if( currWidth == 0 ) + currWidth = 1; + if( currHeight == 0 ) + currHeight = 1; + + inOutNumMips++; + } while ( currWidth != 1 || currHeight != 1 ); + } + } +} + +GFXCubemap* GFXTextureManager::createCubemap( const Torque::Path &path ) +{ + // Very first thing... check the cache. + CubemapTable::Iterator iter = mCubemapTable.find( path.getFullPath() ); + if ( iter != mCubemapTable.end() ) + return iter->value; + + // Not in the cache... we have to load it ourselves. + + // First check for a DDS file. + if ( !sDDSExt.equal( path.getExtension(), String::NoCase ) ) + { + // At the moment we only support DDS cubemaps. + return NULL; + } + + const U32 scalePower = getTextureDownscalePower( NULL ); + + // Ok... load the DDS file then. + Resource dds = DDSFile::load( path, scalePower ); + if ( !dds || !dds->isCubemap() ) + { + // This wasn't a cubemap... give up too. + return NULL; + } + + // We loaded the cubemap dds, so now we create the GFXCubemap from it. + GFXCubemap *cubemap = GFX->createCubemap(); + cubemap->initStatic( dds ); + cubemap->_setPath( path.getFullPath() ); + + // Store the cubemap into the cache. + mCubemapTable.insertUnique( path.getFullPath(), cubemap ); + + return cubemap; +} + +void GFXTextureManager::releaseCubemap( GFXCubemap *cubemap ) +{ + if ( mTextureManagerState == GFXTextureManager::Dead ) + return; + + const String &path = cubemap->getPath(); + + CubemapTable::Iterator iter = mCubemapTable.find( path ); + if ( iter != mCubemapTable.end() && iter->value == cubemap ) + mCubemapTable.erase( iter ); + + // If we have a path for the texture then + // remove change notifications for it. + //Path texPath = texture->getPath(); + //if ( !texPath.isEmpty() ) + //FS::RemoveChangeNotification( texPath, this, &GFXTextureManager::_onFileChanged ); +} + +void GFXTextureManager::_onFileChanged( const Torque::Path &path ) +{ + String pathNoExt = Torque::Path::Join( path.getRoot(), ':', path.getPath() ); + pathNoExt = Torque::Path::Join( pathNoExt, '/', path.getFileName() ); + + // See if we've got it loaded. + GFXTextureObject *obj = hashFind( pathNoExt ); + if ( !obj || path != obj->getPath() ) + return; + + Con::errorf( "[GFXTextureManager::_onFileChanged] : File changed [%s]", path.getFullPath().c_str() ); + + const U32 scalePower = getTextureDownscalePower( obj->mProfile ); + + if ( sDDSExt.equal( path.getExtension(), String::NoCase) ) + { + Resource dds = DDSFile::load( path, scalePower ); + if ( dds ) + _createTexture( dds, obj->mProfile, false, obj ); + } + else + { + Resource bmp = GBitmap::load( path ); + if( bmp ) + _createTexture( bmp, obj->mTextureLookupName, obj->mProfile, false, obj ); + } +} + +void GFXTextureManager::reloadTextures() +{ + GFXTextureObject *tex = mListHead; + + while ( tex != NULL ) + { + const Torque::Path path( tex->mPath ); + if ( !path.isEmpty() ) + { + const U32 scalePower = getTextureDownscalePower( tex->mProfile ); + + if ( sDDSExt.equal( path.getExtension(), String::NoCase ) ) + { + Resource dds = DDSFile::load( path, scalePower ); + if ( dds ) + _createTexture( dds, tex->mProfile, false, tex ); + } + else + { + Resource bmp = GBitmap::load( path ); + if( bmp ) + _createTexture( bmp, tex->mTextureLookupName, tex->mProfile, false, tex ); + } + } + + tex = tex->mNext; + } +} + +DefineEngineFunction( flushTextureCache, void, (),, + "Releases all textures and resurrects the texture manager.\n" + "@ingroup GFX\n" ) +{ + if ( !GFX || !TEXMGR ) + return; + + TEXMGR->zombify(); + TEXMGR->resurrect(); +} + +DefineEngineFunction( cleanupTexturePool, void, (),, + "Release the unused pooled textures in texture manager freeing up video memory.\n" + "@ingroup GFX\n" ) +{ + if ( !GFX || !TEXMGR ) + return; + + TEXMGR->cleanupPool(); +} + +DefineEngineFunction( reloadTextures, void, (),, + "Reload all the textures from disk.\n" + "@ingroup GFX\n" ) +{ + if ( !GFX || !TEXMGR ) + return; + + TEXMGR->reloadTextures(); +} diff --git a/Engine/source/gfx/gfxTextureManager.h b/Engine/source/gfx/gfxTextureManager.h new file mode 100644 index 000000000..5ecbd07d5 --- /dev/null +++ b/Engine/source/gfx/gfxTextureManager.h @@ -0,0 +1,348 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXTEXTUREMANAGER_H_ +#define _GFXTEXTUREMANAGER_H_ + +#ifndef _GFXTEXTUREOBJECT_H_ +#include "gfx/gfxTextureObject.h" +#endif +#ifndef _GBITMAP_H_ +#include "gfx/bitmap/gBitmap.h" +#endif +#ifndef _DDSFILE_H_ +#include "gfx/bitmap/ddsFile.h" +#endif +#ifndef _RESOURCEMANAGER_H_ +#include "core/resourceManager.h" +#endif +#ifndef _TDICTIONARY_H_ +#include "core/util/tDictionary.h" +#endif +#ifndef _TSIGNAL_H_ +#include "core/util/tSignal.h" +#endif + + +namespace Torque +{ + class Path; +} + +class GFXCubemap; + + +class GFXTextureManager +{ +public: + enum + { + AA_MATCH_BACKBUFFER = -1 + }; + + GFXTextureManager(); + virtual ~GFXTextureManager(); + + /// Set up some global script interface stuff. + static void init(); + + /// Update width and height based on available resources. + /// + /// We provide a simple interface for managing texture memory usage. Specifically, + /// if the total video memory is below a certain threshold, we scale all texture + /// resolutions down by a specific factor (you can specify different scale factors + /// for different types of textures). + /// + /// @note The base GFXTextureManager class provides all the logic to do this scaling. + /// Subclasses need only implement getTotalVideoMemory(). + /// + /// @param type Type of the requested texture. This is used to determine scaling factors. + /// @param width Requested width - is changed to the actual width that should be used. + /// @param height Requested height - is changed to the actual height that should be used. + /// @return True if the texture request should be granted, false otherwise. + virtual bool validateTextureQuality(GFXTextureProfile *profile, U32 &width, U32 &height); + + /// + static U32 getTextureDownscalePower( GFXTextureProfile *profile ); + + virtual GFXTextureObject *createTexture( GBitmap *bmp, + const String &resourceName, + GFXTextureProfile *profile, + bool deleteBmp); + + virtual GFXTextureObject *createTexture( DDSFile *dds, + GFXTextureProfile *profile, + bool deleteDDS); + + virtual GFXTextureObject *createTexture( const Torque::Path &path, + GFXTextureProfile *profile ); + + virtual GFXTextureObject *createTexture( U32 width, + U32 height, + void *pixels, + GFXFormat format, + GFXTextureProfile *profile); + + virtual GFXTextureObject *createTexture( U32 width, + U32 height, + U32 depth, + void *pixels, + GFXFormat format, + GFXTextureProfile *profile ); + + virtual GFXTextureObject *createTexture( U32 width, + U32 height, + GFXFormat format, + GFXTextureProfile *profile, + U32 numMipLevels, + S32 antialiasLevel); + + void deleteTexture( GFXTextureObject *texture ); + void reloadTexture( GFXTextureObject *texture ); + + /// Request that the texture be deleted which will + /// either occur immediately or delayed if its cached. + void requestDeleteTexture( GFXTextureObject *texture ); + + /// @name Texture Necromancy + /// + /// Texture necromancy in three easy steps: + /// - If you want to destroy the texture manager, call kill(). + /// - If you want to switch resolutions, or otherwise reset the device, call zombify(). + /// - When you want to bring the manager back from zombie state, call resurrect(). + /// @{ + + /// + void kill(); + void zombify(); + void resurrect(); + + /// This releases any pooled textures which are + /// currently unused freeing up video memory. + void cleanupPool(); + + /// + void reloadTextures(); + + /// This releases cached textures that have not + /// been referenced for a period of time. + void cleanupCache( U32 secondsToLive = 0 ); + + /// Registers a callback for texture zombify and resurrect events. + /// @see GFXTexCallbackCode + /// @see removeEventDelegate + template + static void addEventDelegate( T obj, U func ); + + /// Unregisteres a texture event callback. + /// @see addEventDelegate + template + static void removeEventDelegate( T obj, U func ) { smEventSignal.remove( obj, func ); } + + /// @} + + /// Load a cubemap from a texture file. + GFXCubemap* createCubemap( const Torque::Path &path ); + + /// Used to remove a cubemap from the cache. + void releaseCubemap( GFXCubemap *cubemap ); + +protected: + + /// The amount of texture mipmaps to skip when loading a + /// texture that allows downscaling. + /// + /// Exposed to script via $pref::Video::textureReductionLevel. + /// + /// @see GFXTextureProfile::PreserveSize + /// + static S32 smTextureReductionLevel; + + GFXTextureObject *mListHead; + GFXTextureObject *mListTail; + + // We have a hash table for fast texture lookups + GFXTextureObject **mHashTable; + U32 mHashCount; + GFXTextureObject *hashFind( const String &name ); + void hashInsert(GFXTextureObject *object); + void hashRemove(GFXTextureObject *object); + + // The cache of loaded cubemap textures. + typedef HashTable CubemapTable; + CubemapTable mCubemapTable; + + /// The textures waiting to be deleted. + Vector mToDelete; + + enum TextureManagerState + { + Living, + Zombie, + Dead + + } mTextureManagerState; + + /// The texture pool collection type. + typedef HashTable > TexturePoolMap; + + /// All the allocated texture pool textures. + TexturePoolMap mTexturePool; + + //----------------------------------------------------------------------- + // Protected methods + //----------------------------------------------------------------------- + + /// Returns a free texture of the requested attributes from + /// from the shared texture pool. It returns NULL if no match + /// is found. + GFXTextureObject* _findPooledTexure( U32 width, + U32 height, + GFXFormat format, + GFXTextureProfile *profile, + U32 numMipLevels, + S32 antialiasLevel ); + + GFXTextureObject *_createTexture( GBitmap *bmp, + const String &resourceName, + GFXTextureProfile *profile, + bool deleteBmp, + GFXTextureObject *inObj ); + + GFXTextureObject *_createTexture( DDSFile *dds, + GFXTextureProfile *profile, + bool deleteDDS, + GFXTextureObject *inObj ); + + /// Frees the API handles to the texture, for D3D this is a release call + /// + /// @note freeTexture MUST NOT DELETE THE TEXTURE OBJECT + virtual void freeTexture( GFXTextureObject *texture, bool zombify = false ); + + virtual void refreshTexture( GFXTextureObject *texture ); + + /// @group Internal Texture Manager Interface + /// + /// These pure virtual functions are overloaded by each API-specific + /// subclass. + /// + /// The order of calls is: + /// @code + /// _createTexture() + /// _loadTexture + /// _refreshTexture() + /// _refreshTexture() + /// _refreshTexture() + /// ... + /// _freeTexture() + /// @endcode + /// + /// @{ + + /// Allocate a texture with the internal API. + /// + /// @param height Height of the texture. + /// @param width Width of the texture. + /// @param depth Depth of the texture. (Will normally be 1 unless + /// we are doing a cubemap or volumetexture.) + /// @param format Pixel format of the texture. + /// @param profile Profile for the texture. + /// @param numMipLevels If not-NULL, then use that many mips. + /// If NULL create the full mip chain + /// @param antialiasLevel, Use GFXTextureManager::AA_MATCH_BACKBUFFER to match the backbuffer settings (for render targets that want to share + /// the backbuffer z buffer. 0 for no antialiasing, > 0 for levels that match the GFXVideoMode struct. + virtual GFXTextureObject *_createTextureObject( U32 height, + U32 width, + U32 depth, + GFXFormat format, + GFXTextureProfile *profile, + U32 numMipLevels, + bool forceMips = false, + S32 antialiasLevel = 0, + GFXTextureObject *inTex = NULL ) = 0; + + /// Load a texture from a proper DDSFile instance. + virtual bool _loadTexture(GFXTextureObject *texture, DDSFile *dds)=0; + + /// Load data into a texture from a GBitmap using the internal API. + virtual bool _loadTexture(GFXTextureObject *texture, GBitmap *bmp)=0; + + /// Load data into a texture from a raw buffer using the internal API. + /// + /// Note that the size of the buffer is assumed from the parameters used + /// for this GFXTextureObject's _createTexture call. + virtual bool _loadTexture(GFXTextureObject *texture, void *raw)=0; + + /// Refresh a texture using the internal API. + virtual bool _refreshTexture(GFXTextureObject *texture)=0; + + /// Free a texture (but do not delete the GFXTextureObject) using the internal + /// API. + /// + /// This is only called during zombification for textures which need it, so you + /// don't need to do any internal safety checks. + virtual bool _freeTexture(GFXTextureObject *texture, bool zombify=false)=0; + + /// @} + + /// Store texture into the hash table cache and linked list. + void _linkTexture( GFXTextureObject *obj ); + + /// Validate the parameters for creating a texture. + void _validateTexParams( const U32 width, const U32 height, const GFXTextureProfile *profile, + U32 &inOutNumMips, GFXFormat &inOutFormat ); + + // New texture manager methods for the cleanup work: + GFXTextureObject *_lookupTexture( const char *filename, const GFXTextureProfile *profile ); + GFXTextureObject *_lookupTexture( const DDSFile *ddsFile, const GFXTextureProfile *profile ); + + void _onFileChanged( const Torque::Path &path ); + + /// The texture event signal type. + typedef Signal EventSignal; + + /// The texture event signal. + static EventSignal smEventSignal; +}; + + +template +inline void GFXTextureManager::addEventDelegate( T obj, U func ) +{ + EventSignal::DelegateSig d( obj, func ); + + AssertFatal( !smEventSignal.contains( d ), + "GFXTextureManager::addEventDelegate() - This is already registered!" ); + + smEventSignal.notify( d ); +} + +inline void GFXTextureManager::reloadTexture( GFXTextureObject *texture ) +{ + refreshTexture( texture ); +} + +/// Returns the GFXTextureManager singleton. Should only be +/// called after the GFX device has been initialized. +#define TEXMGR GFXDevice::get()->getTextureManager() + +#endif // _GFXTEXTUREMANAGER_H_ diff --git a/Engine/source/gfx/gfxTextureObject.cpp b/Engine/source/gfx/gfxTextureObject.cpp new file mode 100644 index 000000000..5f67528aa --- /dev/null +++ b/Engine/source/gfx/gfxTextureObject.cpp @@ -0,0 +1,255 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/gfxTextureObject.h" + +#include "gfx/gfxDevice.h" +#include "gfx/gfxTextureManager.h" +#include "core/util/safeDelete.h" +#include "core/strings/stringFunctions.h" +#include "core/stream/fileStream.h" +#include "console/console.h" +#include "console/engineAPI.h" + + +// TODO: Change this to be in non-shipping builds maybe? +#ifdef TORQUE_DEBUG + +GFXTextureObject *GFXTextureObject::smHead = NULL; +U32 GFXTextureObject::smActiveTOCount = 0; + +U32 GFXTextureObject::dumpActiveTOs() +{ + if(!smActiveTOCount) + { + Con::printf( "GFXTextureObject::dumpActiveTOs - no active TOs to dump." ); + return 0; + } + + Con::printf("GFXTextureObject Usage Report - %d active TOs", smActiveTOCount); + Con::printf("---------------------------------------------------------------"); + Con::printf(" Addr Dim. GFXTextureProfile ProfilerPath DebugDescription"); + + for(GFXTextureObject *walk = smHead; walk; walk=walk->mDebugNext) + Con::printf(" %x (%4d, %4d) %s %s %s", walk, walk->getWidth(), + walk->getHeight(), walk->mProfile->getName().c_str(), walk->mDebugCreationPath.c_str(), walk->mDebugDescription.c_str()); + + Con::printf("----- dump complete -------------------------------------------"); + return smActiveTOCount; +} + +DefineEngineFunction( dumpTextureObjects, void, (),, + "Dumps a list of all active texture objects to the console.\n" + "@note This function is only available in debug builds.\n" + "@ingroup GFX\n" ) +{ + GFXTextureObject::dumpActiveTOs(); +} + +#endif // TORQUE_DEBUG + +//----------------------------------------------------------------------------- +// GFXTextureObject +//----------------------------------------------------------------------------- +GFXTextureObject::GFXTextureObject(GFXDevice *aDevice, GFXTextureProfile *aProfile) +{ + mHashNext = mNext = mPrev = NULL; + + mDevice = aDevice; + mProfile = aProfile; + + mBitmap = NULL; + mMipLevels = 1; + mAntialiasLevel = 0; + + mTextureSize.set( 0, 0, 0 ); + + mDead = false; + + mDeleteTime = 0; + + mBitmap = NULL; + mDDS = NULL; + + mFormat = GFXFormatR8G8B8; + + mHasTransparency = false; + +#if defined(TORQUE_DEBUG) + // Active object tracking. + smActiveTOCount++; + mDebugDescription = "Anonymous Texture Object"; +#if defined(TORQUE_ENABLE_PROFILE_PATH) + mDebugCreationPath = gProfiler->getProfilePath(); +#endif + mDebugNext = smHead; + mDebugPrev = NULL; + + if(smHead) + { + AssertFatal(smHead->mDebugPrev == NULL, "GFXTextureObject::GFXTextureObject - found unexpected previous in current head!"); + smHead->mDebugPrev = this; + } + + smHead = this; +#endif +} + +//----------------------------------------------------------------------------- +// Destructor +//----------------------------------------------------------------------------- +GFXTextureObject::~GFXTextureObject() +{ + kill(); + +#ifdef TORQUE_DEBUG + if(smHead == this) + smHead = this->mDebugNext; + + if(mDebugNext) + mDebugNext->mDebugPrev = mDebugPrev; + + if(mDebugPrev) + mDebugPrev->mDebugNext = mDebugNext; + + mDebugPrev = mDebugNext = NULL; + + smActiveTOCount--; +#endif +} + +void GFXTextureObject::destroySelf() +{ + mDevice->mTextureManager->requestDeleteTexture(this); +} + +//----------------------------------------------------------------------------- +// kill - this function clears out the data in texture object. It's done like +// this because the texture object needs to release its pointers to textures +// before the GFXDevice is shut down. The texture objects themselves get +// deleted by the refcount structure - which may be after the GFXDevice has +// been destroyed. +//----------------------------------------------------------------------------- +void GFXTextureObject::kill() +{ + if( mDead ) + return; + +#ifdef TORQUE_DEBUG + // This makes sure that nobody is forgetting to call kill from the derived + // destructor. If they are, then we should get a pure virtual function + // call here. + pureVirtualCrash(); +#endif + + // If we're a dummy, don't do anything... + if( !mDevice || !mDevice->mTextureManager ) + { + mDead = true; + return; + } + + // Remove ourselves from the texture list and hash + mDevice->mTextureManager->deleteTexture(this); + + // Delete the stored bitmap. + SAFE_DELETE(mBitmap) + SAFE_DELETE(mDDS); + + // Clean up linked list + if(mNext) + mNext->mPrev = mPrev; + if(mPrev) + mPrev->mNext = mNext; + + mDead = true; +} + +const String GFXTextureObject::describeSelf() const +{ + return String::ToString(" (width: %4d, height: %4d) profile: %s creation path: %s", getWidth(), +#if defined(TORQUE_DEBUG) && defined(TORQUE_ENABLE_PROFILER) + getHeight(), mProfile->getName().c_str(), mDebugCreationPath.c_str()); +#else + getHeight(), mProfile->getName().c_str(), ""); +#endif +} + +F32 GFXTextureObject::getMaxUCoord() const +{ + return 1.0f; +} + +F32 GFXTextureObject::getMaxVCoord() const +{ + return 1.0f; +} + +U32 GFXTextureObject::getEstimatedSizeInBytes() const +{ + // We have to deal with DDS specially. + if ( mFormat >= GFXFormatDXT1 ) + return DDSFile::getSizeInBytes( mFormat, mTextureSize.x, mTextureSize.y, mMipLevels ); + + // Else we need to calculate the size ourselves. + S32 texSizeX = mTextureSize.x; + S32 texSizeY = mTextureSize.y; + S32 volDepth = getMax( 1, mTextureSize.z ); + U32 byteSize = GFXFormat_getByteSize( mFormat ); + U32 totalBytes = texSizeX * texSizeY * volDepth * byteSize; + + // Without mips we're done. + if ( mProfile->noMip() ) + return totalBytes; + + // NOTE: While we have mMipLevels, at the time of this + // comment it only stores the accessable mip levels and + // not the count of the autogen mips. + // + // So we figure out the mip count ourselves assuming its + // a complete mip chain. + while ( texSizeX > 1 || texSizeY > 1 ) + { + texSizeX = getMax( texSizeX >> 1, 1 ); + texSizeY = getMax( texSizeY >> 1, 1 ); + volDepth = getMax( volDepth >> 1, 1 ); + + totalBytes += texSizeX * texSizeY * volDepth * byteSize; + } + + return totalBytes; +} + +bool GFXTextureObject::dumpToDisk( const String &bmType, const String &path ) +{ + FileStream stream; + if ( !stream.open( path, Torque::FS::File::Write ) ) + return false; + + if ( mBitmap ) + return mBitmap->writeBitmap( bmType, stream ); + + GBitmap bitmap( getWidth(), getHeight(), false, getFormat() ); + copyToBmp( &bitmap ); + return bitmap.writeBitmap( bmType, stream ); +} diff --git a/Engine/source/gfx/gfxTextureObject.h b/Engine/source/gfx/gfxTextureObject.h new file mode 100644 index 000000000..b9db600f1 --- /dev/null +++ b/Engine/source/gfx/gfxTextureObject.h @@ -0,0 +1,225 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXTEXTUREOBJECT_H_ +#define _GFXTEXTUREOBJECT_H_ + +#ifndef _REFBASE_H_ +#include "core/util/refBase.h" +#endif +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _GFXENUMS_H_ +#include "gfx/gfxEnums.h" +#endif +#ifndef _GFXTEXTUREPROFILE_H_ +#include "gfx/gfxTextureProfile.h" +#endif +#ifndef _GFXRESOURCE_H_ +#include "gfx/gfxResource.h" +#endif + +class Point2I; +class GFXDevice; +class GFXTextureProfile; +class GBitmap; +struct DDSFile; +class RectI; + +/// Contains information on a locked region of a texture. +/// +/// In general, to access a given pixel in a locked rectangle, use this +/// equation: +/// +/// @code +/// U8 *pixelAtXY = bits + x * pitch + y * pixelSizeInBytes; +/// @endcode +/// +/// @note D3DLOCKED_RECT and this structure match up. If you change this +/// assumption, be sure to revisit the D3D GFX implementation. +/// +/// @see GFXTextureObject::lock() +struct GFXLockedRect +{ + /// Pitch of the lock. This is the spacing in bytes of the start + /// of each row of the locked region. + int pitch; + + /// Pointer to the start of locked rectangle. + U8* bits; +}; + + +class GFXTextureObject : public StrongRefBase, public GFXResource +{ +public: + + #ifdef TORQUE_DEBUG + // In debug builds we provide a TOC leak tracking system. + static U32 smActiveTOCount; + static GFXTextureObject *smHead; + static U32 dumpActiveTOs(); + + String mDebugCreationPath; + String mDebugDescription; + GFXTextureObject *mDebugNext; + GFXTextureObject *mDebugPrev; + #endif + + /// The path to the texture file if the + /// content was loaded from a resource. + String mPath; + + bool mDead; + + /// The device this texture belongs to. + GFXDevice *mDevice; + + /// The next texture in the linked list. + /// @see GFXTextureManager::mListHead + GFXTextureObject *mNext; + + /// The previous texture in the linked list. + /// @see GFXTextureManager::mListHead + GFXTextureObject *mPrev; + + /// The siblings in the cache hash table. + /// @see GFXTextureManager::mHashTable + GFXTextureObject *mHashNext; + + /// This is the file name or other unique string used + /// to hash this texture object. + String mTextureLookupName; + + /// The time at which all references to this + /// texture were removed. + U32 mDeleteTime; + + Point3I mBitmapSize; + Point3I mTextureSize; + U32 mMipLevels; + + // TODO: This looks unused in the engine... not even sure + // what it means. We should investigate and remove it. + S32 mAntialiasLevel; + + bool mHasTransparency; + + // These two should be removed, and replaced by a reference to a resource + // object, or data buffer. Something more generic. -patw + GBitmap *mBitmap; ///< GBitmap we are backed by. + DDSFile *mDDS; ///< DDSFile we're backed by. + + U32 getFormatByteSize() const { return GFXFormat_getByteSize( mFormat ); } + + GFXTextureProfile *mProfile; + GFXFormat mFormat; + + + GFXTextureObject(GFXDevice * aDevice, GFXTextureProfile *profile); + virtual ~GFXTextureObject(); + + GBitmap *getBitmap(); + DDSFile *getDDS(); + U32 getWidth() const { return mTextureSize.x; } + U32 getHeight() const { return mTextureSize.y; } + const Point3I& getSize() const { return mTextureSize; } + U32 getDepth() const { return mTextureSize.z; } + U32 getMipLevels() const { return mMipLevels; } + U32 getBitmapWidth() const { return mBitmapSize.x; } + U32 getBitmapHeight() const { return mBitmapSize.y; } + U32 getBitmapDepth() const { return mBitmapSize.z; } + GFXFormat getFormat() const { return mFormat; } + + /// Returns true if this texture is a render target. + bool isRenderTarget() const { return mProfile->isRenderTarget(); } + + /// Returns the file path to the texture if + /// it was loaded from disk. + const String& getPath() const { return mPath; } + + virtual F32 getMaxUCoord() const; + virtual F32 getMaxVCoord() const; + + /// Returns the estimated video memory usage + /// in bytes including mipmaps. + U32 getEstimatedSizeInBytes() const; + + /// Acquire a lock on part of the texture. The GFXLockedRect returned + /// is managed by the GFXTextureObject and does not need to be freed. + virtual GFXLockedRect * lock( U32 mipLevel = 0, RectI *inRect = NULL ) = 0; + + /// Releases a lock previously acquired. Note that the mipLevel parameter + /// must match the corresponding lock! + virtual void unlock( U32 mipLevel = 0) = 0; + + // copy the texture data into the specified bitmap. + // - this texture object must be a render target. the function will assert if this is not the case. + // - you must have called allocateBitmap() on the input bitmap first. the bitmap should have the + // same dimensions as this texture. the bitmap format can be RGB or RGBA (in the latter case + // the alpha values from the texture are copied too) + // - returns true if successful, false otherwise + // - this process is not fast. + virtual bool copyToBmp(GBitmap* bmp) = 0; + + #ifdef TORQUE_DEBUG + + // It is important for any derived objects to define this method + // and also call 'kill' from their destructors. If you fail to + // do either, you will get a pure virtual function call crash + // in debug mode. This is a precaution to make sure you don't + // forget to add 'kill' to your destructor. + virtual void pureVirtualCrash() = 0; + + #endif + + virtual void kill(); + + /// Debug helper function for writing the texture to disk. + bool dumpToDisk( const String &bmType, const String &path ); + + // GFXResource interface + /// The resource should put a description of itself (number of vertices, size/width of texture, etc.) in buffer + virtual const String describeSelf() const; + + // StrongRefBase + virtual void destroySelf(); +}; + +//----------------------------------------------------------------------------- + +inline GBitmap *GFXTextureObject::getBitmap() +{ + AssertFatal( mProfile->doStoreBitmap(), avar("GFXTextureObject::getBitmap - Cannot access bitmap for a '%s' texture.", mProfile->getName().c_str()) ); + + return mBitmap; +} + +inline DDSFile *GFXTextureObject::getDDS() +{ + AssertFatal( mProfile->doStoreBitmap(), avar("GFXTextureObject::getDDS - Cannot access bitmap for a '%s' texture.", mProfile->getName().c_str()) ); + + return mDDS; +} + +#endif // _GFXTEXTUREOBJECT_H_ diff --git a/Engine/source/gfx/gfxTextureProfile.cpp b/Engine/source/gfx/gfxTextureProfile.cpp new file mode 100644 index 000000000..1c75b11ef --- /dev/null +++ b/Engine/source/gfx/gfxTextureProfile.cpp @@ -0,0 +1,168 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/gfxTextureProfile.h" + +#include "gfx/gfxTextureObject.h" +#include "gfx/bitmap/gBitmap.h" +#include "core/strings/stringFunctions.h" +#include "console/console.h" +#include "console/engineAPI.h" + + +// Set up defaults... +GFX_ImplementTextureProfile(GFXDefaultRenderTargetProfile, + GFXTextureProfile::DiffuseMap, + GFXTextureProfile::PreserveSize | GFXTextureProfile::NoMipmap | GFXTextureProfile::RenderTarget, + GFXTextureProfile::None); +GFX_ImplementTextureProfile(GFXDefaultStaticDiffuseProfile, + GFXTextureProfile::DiffuseMap, + GFXTextureProfile::Static, + GFXTextureProfile::None); +GFX_ImplementTextureProfile(GFXDefaultStaticNormalMapProfile, + GFXTextureProfile::NormalMap, + GFXTextureProfile::Static, + GFXTextureProfile::None); +GFX_ImplementTextureProfile(GFXDefaultStaticDXT5nmProfile, + GFXTextureProfile::NormalMap, + GFXTextureProfile::Static, + GFXTextureProfile::DXT5); +GFX_ImplementTextureProfile(GFXDefaultPersistentProfile, + GFXTextureProfile::DiffuseMap, + GFXTextureProfile::PreserveSize | GFXTextureProfile::Static | GFXTextureProfile::KeepBitmap, + GFXTextureProfile::None); +GFX_ImplementTextureProfile(GFXSystemMemProfile, + GFXTextureProfile::DiffuseMap, + GFXTextureProfile::PreserveSize | GFXTextureProfile::NoMipmap | GFXTextureProfile::SystemMemory, + GFXTextureProfile::None); +GFX_ImplementTextureProfile(GFXDefaultZTargetProfile, + GFXTextureProfile::DiffuseMap, + GFXTextureProfile::PreserveSize | GFXTextureProfile::NoMipmap | GFXTextureProfile::ZTarget | GFXTextureProfile::NoDiscard, + GFXTextureProfile::None); + +//----------------------------------------------------------------------------- + +GFXTextureProfile *GFXTextureProfile::smHead = NULL; +U32 GFXTextureProfile::smProfileCount = 0; + +GFXTextureProfile::GFXTextureProfile(const String &name, Types type, U32 flag, Compression compression) +: mName( name ) +{ + // Take type, flag, and compression and produce a munged profile word. + mProfile = (type & (BIT(TypeBits + 1) - 1)) | + ((flag & (BIT(FlagBits + 1) - 1)) << TypeBits) | + ((compression & (BIT(CompressionBits + 1) - 1)) << (FlagBits + TypeBits)); + + // Stick us on the linked list. + mNext = smHead; + smHead = this; + ++smProfileCount; + + // Now do some sanity checking. (Ben is not proud of this code.) + AssertFatal( (testFlag(Dynamic) && !testFlag(Static)) + || (!testFlag(Dynamic) && !testFlag(Static)) + || (!testFlag(Dynamic) && testFlag(Static)), + "GFXTextureProfile::GFXTextureProfile - Cannot have a texture profile be both static and dynamic!"); + mDownscale = 0; +} + +void GFXTextureProfile::init() +{ + // Do something, anything? +} + +GFXTextureProfile * GFXTextureProfile::find(const String &name) +{ + // Not really necessary at this time. + return NULL; +} + +void GFXTextureProfile::collectStats( Flags flags, GFXTextureProfileStats *stats ) +{ + // Walk the profile list. + GFXTextureProfile *curr = smHead; + while ( curr ) + { + if ( curr->testFlag( flags ) ) + (*stats) += curr->getStats(); + + curr = curr->mNext; + } +} + +void GFXTextureProfile::updateStatsForCreation(GFXTextureObject *t) +{ + if(t->mProfile) + { + t->mProfile->incActiveCopies(); + t->mProfile->mStats.allocatedTextures++; + + U32 texSize = t->getHeight() * t->getWidth(); + U32 byteSize = t->getEstimatedSizeInBytes(); + + t->mProfile->mStats.allocatedTexels += texSize; + t->mProfile->mStats.allocatedBytes += byteSize; + + t->mProfile->mStats.activeTexels += texSize; + t->mProfile->mStats.activeBytes += byteSize; + } +} + +void GFXTextureProfile::updateStatsForDeletion(GFXTextureObject *t) +{ + if(t->mProfile) + { + t->mProfile->decActiveCopies(); + + U32 texSize = t->getHeight() * t->getWidth(); + U32 byteSize = t->getEstimatedSizeInBytes(); + + t->mProfile->mStats.activeTexels -= texSize; + t->mProfile->mStats.activeBytes -= byteSize; + } +} + +DefineEngineFunction( getTextureProfileStats, String, (),, + "Returns a list of texture profiles in the format: ProfileName TextureCount TextureMB\n" + "@ingroup GFX\n" ) +{ + StringBuilder result; + + GFXTextureProfile *profile = GFXTextureProfile::getHead(); + while ( profile ) + { + const GFXTextureProfileStats &stats = profile->getStats(); + + F32 mb = ( stats.activeBytes / 1024.0f ) / 1024.0f; + + result.format( "%s %d %0.2f\n", + profile->getName().c_str(), + stats.activeCount, + mb ); + + profile = profile->getNext(); + } + + return result.end(); +} + diff --git a/Engine/source/gfx/gfxTextureProfile.h b/Engine/source/gfx/gfxTextureProfile.h new file mode 100644 index 000000000..9153256e3 --- /dev/null +++ b/Engine/source/gfx/gfxTextureProfile.h @@ -0,0 +1,219 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXTEXTUREPROFILE_H_ +#define _GFXTEXTUREPROFILE_H_ + +#ifndef _TORQUE_STRING_H_ +#include "core/util/str.h" +#endif + +class GFXTextureObject; + +/// Helper struct for gathering profile stats. +class GFXTextureProfileStats +{ +public: + + /// Constructs and clears the stats. + GFXTextureProfileStats() { clear(); } + + /// Zeros all the stats. + void clear() + { + dMemset( this, 0, sizeof( GFXTextureProfileStats ) ); + } + + /// Adds stats together. + GFXTextureProfileStats& operator += ( const GFXTextureProfileStats &stats ) + { + activeCount += stats.activeCount; + activeTexels += stats.activeTexels; + activeBytes += stats.activeBytes; + allocatedTextures += stats.allocatedTextures; + allocatedTexels += stats.allocatedTexels; + allocatedBytes += stats.allocatedBytes; + return *this; + } + + U32 activeCount; ///< Count of textures of this profile type allocated. + U32 activeTexels; ///< Amount of texelspace currently allocated under this profile. + U32 activeBytes; ///< Amount of storage currently allocated under this profile. + U32 allocatedTextures; ///< Total number of textures allocated under this profile. + U32 allocatedTexels; ///< Total number of texels allocated under this profile. + U32 allocatedBytes; ///< Total number of bytes allocated under this profile. +}; + + +class GFXTextureProfile +{ +public: + enum Types + { + DiffuseMap, + NormalMap, + AlphaMap, + LuminanceMap + }; + + enum Flags + { + PreserveSize = BIT(0), ///< Never shrink this bitmap in low VRAM situations. + NoMipmap = BIT(1), ///< Do not generate mipmap chain for this texture. + SystemMemory = BIT(2), ///< System memory texture - isn't uploaded to card - useful as target for copying surface data out of video ram + RenderTarget = BIT(3), ///< This texture will be used as a render target. + Dynamic = BIT(4), ///< This texture may be refreshed. (Precludes Static) + Static = BIT(5), ///< This texture will never be modified once loaded. (Precludes Dynamic) + NoPadding = BIT(6), ///< Do not pad this texture if it's non pow2. + KeepBitmap = BIT(7), ///< Always keep a copy of this texture's bitmap. (Potentially in addition to the API managed copy?) + ZTarget = BIT(8), ///< This texture will be used as a Z target. + + /// Track and pool textures of this type for reuse. + /// + /// You should use this profile flag sparingly. Odd + /// sized textures and spikes in allocation can cause + /// the pool to contain unused textures which will remain + /// in memory until a flush occurs. + /// + Pooled = BIT(9), + + /// A hint that the device is not allowed to discard the content + /// of a target texture after presentation or deactivated. + /// + /// This is mainly a depth buffer optimization. + NoDiscard = BIT(10) + + }; + + enum Compression + { + None, + DXT1, + DXT2, + DXT3, + DXT4, + DXT5, + }; + + GFXTextureProfile(const String &name, Types type, U32 flags, Compression compression = None); + + // Accessors + String getName() const { return mName; }; + Types getType() const { return (Types)(mProfile & (BIT(TypeBits) - 1)); } + const Compression getCompression() const { return (Compression)((mProfile >> (FlagBits + TypeBits)) & (BIT(CompressionBits + 1) - 1)); }; + + bool testFlag(Flags flag) const + { + return (mProfile & (flag << TypeBits)) != 0; + } + + // Mutators + const U32 getDownscale() const { return mDownscale; } + void setDownscale(const U32 shift) { mDownscale = shift; } + void incActiveCopies() { mStats.activeCount++; } + void decActiveCopies() { AssertFatal( mStats.activeCount != 0, "Ran out of extant copies!"); mStats.activeCount--; } + + // And static interface... + static void init(); + static GFXTextureProfile *find(const String &name); + static void updateStatsForCreation(GFXTextureObject *t); + static void updateStatsForDeletion(GFXTextureObject *t); + + /// Collects the total stats for all the profiles which + /// include any of the flag bits. + static void collectStats( Flags flags, GFXTextureProfileStats *stats ); + + /// Returns the total profile count in the list. + static U32 getProfileCount() { return smProfileCount; } + + /// Returns the head of the profile list. + static GFXTextureProfile* getHead() { return smHead; } + + /// Returns the next profile in the list. + GFXTextureProfile* getNext() const { return mNext; } + + /// Returns the allocation stats for this texture profile. + inline const GFXTextureProfileStats& getStats() const { return mStats; } + + // Helper functions... + inline bool doStoreBitmap() const { return testFlag(KeepBitmap); } + inline bool canDownscale() const { return !testFlag(PreserveSize); } + inline bool isDynamic() const { return testFlag(Dynamic); } + inline bool isRenderTarget() const { return testFlag(RenderTarget); } + inline bool isZTarget() const { return testFlag(ZTarget); } + inline bool isSystemMemory() const { return testFlag(SystemMemory); } + inline bool noMip() const { return testFlag(NoMipmap); } + inline bool isPooled() const { return testFlag(Pooled); } + inline bool canDiscard() const { return !testFlag(NoDiscard); } + +private: + /// These constants control the packing for the profile; if you add flags, types, or + /// compression info then make sure these are giving enough bits! + enum Constants + { + TypeBits = 2, + FlagBits = 11, + CompressionBits = 3, + }; + + String mName; ///< Name of this profile... + U32 mDownscale; ///< Amount to shift textures of this type down, if any. + U32 mProfile; ///< Stores a munged version of the profile data. + U32 mActiveCount; ///< Count of textures of this profile type allocated. + U32 mActiveTexels; ///< Amount of texelspace currently allocated under this profile. + U32 mActiveBytes; ///< Amount of storage currently allocated under this profile. + U32 mAllocatedTextures; ///< Total number of textures allocated under this profile. + U32 mAllocatedTexels; ///< Total number of texels allocated under this profile. + U32 mAllocatedBytes; ///< Total number of bytes allocated under this profile. + + /// The texture profile stats. + GFXTextureProfileStats mStats; + + /// The number of profiles in the system. + static U32 smProfileCount; + + /// Keep a list of all the profiles. + GFXTextureProfile *mNext; + static GFXTextureProfile *smHead; +}; + +#define GFX_DeclareTextureProfile(name) extern GFXTextureProfile name +#define GFX_ImplementTextureProfile(name, type, flags, compression) GFXTextureProfile name(#name, type, flags, compression) + +// Set up some defaults.. + +// Texture we can render to. +GFX_DeclareTextureProfile(GFXDefaultRenderTargetProfile); +// Standard diffuse texture that stays in system memory. +GFX_DeclareTextureProfile(GFXDefaultPersistentProfile); +// Generic diffusemap. This works in most cases. +GFX_DeclareTextureProfile(GFXDefaultStaticDiffuseProfile); +// Generic normal map. +GFX_DeclareTextureProfile(GFXDefaultStaticNormalMapProfile); +// DXT5 swizzled normal map +GFX_DeclareTextureProfile(GFXDefaultStaticDXT5nmProfile); +// Texture that resides in system memory - used to copy data to +GFX_DeclareTextureProfile(GFXSystemMemProfile); +// Depth buffer texture +GFX_DeclareTextureProfile(GFXDefaultZTargetProfile); + +#endif diff --git a/Engine/source/gfx/gfxTransformSaver.h b/Engine/source/gfx/gfxTransformSaver.h new file mode 100644 index 000000000..85374f1dd --- /dev/null +++ b/Engine/source/gfx/gfxTransformSaver.h @@ -0,0 +1,145 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFX_GFXTRANSFORMSAVER_H_ +#define _GFX_GFXTRANSFORMSAVER_H_ + +#ifndef _GFXDEVICE_H_ +#include "gfx/gfxDevice.h" +#endif + + +/// Helper class to store viewport and matrix stack state, and restore it +/// later. +/// +/// When doing complex out-of-scene rendering, for instance, doing a +/// render to texture operation that needs its own transform state, it +/// is very easy to nuke important rendering state, like the viewport +/// or the projection matrix stored in vertex shader constant zero. +/// +/// This class simplifies save and cleanup of those properties. You can +/// either treat it as a stack helper, e.g. +/// +/// @code +/// void myFunc() +/// { +/// GFXTransformSaver saver; +/// +/// // Lots of nasty render state changes... +/// +/// // Everything is magically cleaned up when saver is destructed! +/// } +/// @endcode +/// +/// Or you can manually control when you do saves or restores: +/// +/// @code +/// void myFunc() +/// { +/// GFXTransformSaver saver(false, false); +/// +/// if(!somePrecondition) +/// return false; // Note early out. +/// +/// saver.save(); +/// +/// // Lots of nasty render state changes... +/// +/// // If we had passed (false, true) to the constructor then it would +/// // clean up automagically for us; but we want to do it manually. +/// saver.restore(); +/// } +/// @endcode +/// +class GFXTransformSaver +{ +protected: + + RectI mSavedViewport; + MatrixF mSavedProjectionMatrix, mSavedViewMatrix; + bool mHaveSavedData, mRestoreSavedDataOnDestruct; + +public: + + /// Constructor - controls how data is saved. + /// + /// @param saveDataNow If true, indicates that saveData() should be called + /// immediately. Otherwise, you can do it manually. + /// + /// @param restoreDataOnDestruct If true, indicates that restoreData() should + /// be called on destruct. Otherwise, you'll + /// have to do it manually. + GFXTransformSaver(bool saveDataNow = true, bool restoreDataOnDestruct = true) + { + mHaveSavedData = false; + + if(saveDataNow) + save(); + + mRestoreSavedDataOnDestruct = restoreDataOnDestruct; + } + + ~GFXTransformSaver() + { + if(mRestoreSavedDataOnDestruct) + restore(); + } + + void save() + { + AssertFatal(mHaveSavedData==false, "GFXTransformSaver::saveData - can't save twice!"); + mSavedViewport = GFX->getViewport(); + mSavedProjectionMatrix = GFX->getProjectionMatrix(); + mSavedViewMatrix = GFX->getViewMatrix(); + GFX->pushWorldMatrix(); + + // Note we have saved data! + mHaveSavedData = true; + } + + void restore() + { + AssertFatal(mHaveSavedData==true, "GFXTransformSaver::restoreData - no saved data to restore!"); + + GFX->popWorldMatrix(); + GFX->setViewMatrix(mSavedViewMatrix); + GFX->setProjectionMatrix(mSavedProjectionMatrix); + GFX->setViewport(mSavedViewport); + + // Once we've restored we do not want to be able to restore again... + mHaveSavedData = false; + + // And we don't want to restore on destruct! + mRestoreSavedDataOnDestruct = false; + } + + /// Returns the saved viewport. + const RectI& getViewport() const { return mSavedViewport; } + + /// Returns the saved projection matrix. + const MatrixF& getProjectionMatrix() const { return mSavedProjectionMatrix; } + + /// Returns the saved projection matrix. + const MatrixF& getViewMatrix() const { return mSavedViewMatrix; } +}; + +#endif // _GFX_GFXTRANSFORMSAVER_H_ \ No newline at end of file diff --git a/Engine/source/gfx/gfxVertexBuffer.cpp b/Engine/source/gfx/gfxVertexBuffer.cpp new file mode 100644 index 000000000..717c6af2b --- /dev/null +++ b/Engine/source/gfx/gfxVertexBuffer.cpp @@ -0,0 +1,60 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/gfxVertexBuffer.h" + +#include "core/strings/stringFunctions.h" +#include "gfx/gfxDevice.h" + + +void GFXVertexBufferHandleBase::set( GFXDevice *theDevice, + U32 numVerts, + const GFXVertexFormat *vertexFormat, + U32 vertexSize, + GFXBufferType type ) +{ + StrongRefPtr::operator=( theDevice->allocVertexBuffer( numVerts, vertexFormat, vertexSize, type ) ); +} + +const String GFXVertexBuffer::describeSelf() const +{ + const char *bufType; + + switch(mBufferType) + { + case GFXBufferTypeStatic: + bufType = "Static"; + break; + case GFXBufferTypeDynamic: + bufType = "Dynamic"; + break; + case GFXBufferTypeVolatile: + bufType = "Volatile"; + break; + default: + bufType = "Unknown"; + break; + } + + return String::ToString("numVerts: %i vertSize: %i bufferType: %s", mNumVerts, mVertexSize, bufType); +} \ No newline at end of file diff --git a/Engine/source/gfx/gfxVertexBuffer.h b/Engine/source/gfx/gfxVertexBuffer.h new file mode 100644 index 000000000..da3aecc82 --- /dev/null +++ b/Engine/source/gfx/gfxVertexBuffer.h @@ -0,0 +1,246 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXVERTEXBUFFER_H_ +#define _GFXVERTEXBUFFER_H_ + +#ifndef _GFXSTRUCTS_H_ +#include "gfx/gfxStructs.h" +#endif + + +//***************************************************************************** +// GFXVertexBuffer - base vertex buffer class +//***************************************************************************** +class GFXVertexBuffer : public StrongRefBase, public GFXResource +{ + friend class GFXVertexBufferHandleBase; + friend class GFXDevice; + +public: + + /// Number of vertices in this buffer. + U32 mNumVerts; + + /// A copy of the vertex format for this buffer. + GFXVertexFormat mVertexFormat; + + /// Vertex size in bytes. + U32 mVertexSize; + + /// GFX buffer type (static, dynamic or volatile). + GFXBufferType mBufferType; + + /// Device this vertex buffer was allocated on. + GFXDevice *mDevice; + + bool isLocked; + U32 lockedVertexStart; + U32 lockedVertexEnd; + void* lockedVertexPtr; + U32 mVolatileStart; + + GFXVertexBuffer( GFXDevice *device, + U32 numVerts, + const GFXVertexFormat *vertexFormat, + U32 vertexSize, + GFXBufferType bufferType ) + : mDevice( device ), + mVolatileStart( 0 ), + mNumVerts( numVerts ), + mVertexSize( vertexSize ), + mBufferType( bufferType ) + { + if ( vertexFormat ) + { + vertexFormat->getDecl(); + mVertexFormat.copy( *vertexFormat ); + } + } + + virtual void lock(U32 vertexStart, U32 vertexEnd, void **vertexPtr) = 0; + virtual void unlock() = 0; + virtual void prepare() = 0; + + // GFXResource + virtual const String describeSelf() const; +}; + + +//***************************************************************************** +// GFXVertexBufferHandleBase +//***************************************************************************** +class GFXVertexBufferHandleBase : public StrongRefPtr +{ + friend class GFXDevice; + +protected: + + void set( GFXDevice *theDevice, + U32 numVerts, + const GFXVertexFormat *vertexFormat, + U32 vertexSize, + GFXBufferType type ); + + void* lock(U32 vertexStart, U32 vertexEnd) + { + if(vertexEnd == 0) + vertexEnd = getPointer()->mNumVerts; + AssertFatal(vertexEnd > vertexStart, "Can't get a lock with the end before the start."); + AssertFatal(vertexEnd <= getPointer()->mNumVerts || getPointer()->mBufferType == GFXBufferTypeVolatile, "Tried to get vertices beyond the end of the buffer!"); + getPointer()->lock(vertexStart, vertexEnd, &getPointer()->lockedVertexPtr); + return getPointer()->lockedVertexPtr; + } + + void unlock() ///< unlocks the vertex data, making changes illegal. + { + getPointer()->unlock(); + } +}; + + +/// A handle object for allocating, filling, and reading a vertex buffer. +template +class GFXVertexBufferHandle : public GFXVertexBufferHandleBase +{ + typedef GFXVertexBufferHandleBase Parent; + + /// Sets this vertex buffer as the current + /// vertex buffer for the device it was allocated on + void prepare() { getPointer()->prepare(); } + +public: + + GFXVertexBufferHandle() {} + + GFXVertexBufferHandle( GFXDevice *theDevice, + U32 numVerts, + GFXBufferType type = GFXBufferTypeVolatile ) + { + set( theDevice, numVerts, type ); + } + + ~GFXVertexBufferHandle() {} + + void set( GFXDevice *theDevice, + U32 numVerts, + GFXBufferType type = GFXBufferTypeVolatile ) + { + Parent::set( theDevice, numVerts, getGFXVertexFormat(), sizeof(T), type ); + } + + T *lock(U32 vertexStart = 0, U32 vertexEnd = 0) ///< locks the vertex buffer range, and returns a pointer to the beginning of the vertex array + ///< also allows the array operators to work on this vertex buffer. + { + return (T*)Parent::lock(vertexStart, vertexEnd); + } + + void unlock() { Parent::unlock(); } + + T& operator[](U32 index) ///< Array operator allows indexing into a locked vertex buffer. The debug version of the code + ///< will range check the array access as well as validate the locked vertex buffer pointer. + { + return ((T*)getPointer()->lockedVertexPtr)[index]; + } + + const T& operator[](U32 index) const ///< Array operator allows indexing into a locked vertex buffer. The debug version of the code + ///< will range check the array access as well as validate the locked vertex buffer pointer. + { + index += getPointer()->mVolatileStart; + AssertFatal(getPointer()->lockedVertexPtr != NULL, "Cannot access verts from an unlocked vertex buffer!!!"); + AssertFatal(index >= getPointer()->lockedVertexStart && index < getPointer()->lockedVertexEnd, "Out of range vertex access!"); + index -= getPointer()->mVolatileStart; + return ((T*)getPointer()->lockedVertexPtr)[index]; + } + + T& operator[](S32 index) ///< Array operator allows indexing into a locked vertex buffer. The debug version of the code + ///< will range check the array access as well as validate the locked vertex buffer pointer. + { + index += getPointer()->mVolatileStart; + AssertFatal(getPointer()->lockedVertexPtr != NULL, "Cannot access verts from an unlocked vertex buffer!!!"); + AssertFatal(index >= getPointer()->lockedVertexStart && index < getPointer()->lockedVertexEnd, "Out of range vertex access!"); + index -= getPointer()->mVolatileStart; + return ((T*)getPointer()->lockedVertexPtr)[index]; + } + + const T& operator[](S32 index) const ///< Array operator allows indexing into a locked vertex buffer. The debug version of the code + ///< will range check the array access as well as validate the locked vertex buffer pointer. + { + index += getPointer()->mVolatileStart; + AssertFatal(getPointer()->lockedVertexPtr != NULL, "Cannot access verts from an unlocked vertex buffer!!!"); + AssertFatal(index >= getPointer()->lockedVertexStart && index < getPointer()->lockedVertexEnd, "Out of range vertex access!"); + index -= getPointer()->mVolatileStart; + return ((T*)getPointer()->lockedVertexPtr)[index]; + } + + GFXVertexBufferHandle& operator=(GFXVertexBuffer *ptr) + { + StrongObjectRef::set(ptr); + return *this; + } + +}; + + +/// This is a non-typed vertex buffer handle which can be +/// used when your vertex type is undefined until runtime. +class GFXVertexBufferDataHandle : public GFXVertexBufferHandleBase +{ + typedef GFXVertexBufferHandleBase Parent; + +protected: + + void prepare() { getPointer()->prepare(); } + +public: + + GFXVertexBufferDataHandle() + { + } + + void set( GFXDevice *theDevice, + U32 vertSize, + const GFXVertexFormat *vertexFormat, + U32 numVerts, + GFXBufferType type ) + { + Parent::set( theDevice, numVerts, vertexFormat, vertSize, type ); + } + + U8* lock( U32 vertexStart = 0, U32 vertexEnd = 0 ) + { + return (U8*)Parent::lock( vertexStart, vertexEnd ); + } + + void unlock() { Parent::unlock(); } + + GFXVertexBufferDataHandle& operator=( GFXVertexBuffer *ptr ) + { + StrongObjectRef::set(ptr); + return *this; + } +}; + + +#endif // _GFXVERTEXBUFFER_H_ + + diff --git a/Engine/source/gfx/gfxVertexColor.cpp b/Engine/source/gfx/gfxVertexColor.cpp new file mode 100644 index 000000000..1eebf2938 --- /dev/null +++ b/Engine/source/gfx/gfxVertexColor.cpp @@ -0,0 +1,25 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/gfxVertexColor.h" + +Swizzle *GFXVertexColor::mDeviceSwizzle = &Swizzles::null; diff --git a/Engine/source/gfx/gfxVertexColor.h b/Engine/source/gfx/gfxVertexColor.h new file mode 100644 index 000000000..4e8620de8 --- /dev/null +++ b/Engine/source/gfx/gfxVertexColor.h @@ -0,0 +1,68 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXVERTEXCOLOR_H_ +#define _GFXVERTEXCOLOR_H_ + +#ifndef _SWIZZLE_H_ +#include "core/util/swizzle.h" +#endif + + +class ColorI; + + +class GFXVertexColor +{ + +private: + U32 packedColorData; + static Swizzle *mDeviceSwizzle; + +public: + static void setSwizzle( Swizzle *val ) { mDeviceSwizzle = val; } + + GFXVertexColor() : packedColorData( 0xFFFFFFFF ) {} // White with full alpha + GFXVertexColor( const ColorI &color ) { set( color ); } + + void set( U8 red, U8 green, U8 blue, U8 alpha = 255 ) + { + packedColorData = red << 0 | green << 8 | blue << 16 | alpha << 24; + mDeviceSwizzle->InPlace( &packedColorData, sizeof( packedColorData ) ); + } + + void set( const ColorI &color ) + { + mDeviceSwizzle->ToBuffer( &packedColorData, (U8 *)&color, sizeof( packedColorData ) ); + } + + GFXVertexColor &operator=( const ColorI &color ) { set( color ); return *this; } + operator const U32 *() const { return &packedColorData; } + const U32& getPackedColorData() const { return packedColorData; } + + void getColor( ColorI *color ) const + { + mDeviceSwizzle->ToBuffer( color, &packedColorData, sizeof( packedColorData ) ); + } +}; + +#endif diff --git a/Engine/source/gfx/gfxVertexFormat.cpp b/Engine/source/gfx/gfxVertexFormat.cpp new file mode 100644 index 000000000..4b8172dde --- /dev/null +++ b/Engine/source/gfx/gfxVertexFormat.cpp @@ -0,0 +1,227 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/gfxVertexFormat.h" + +#include "platform/profiler.h" +#include "core/util/hashFunction.h" +#include "gfx/gfxDevice.h" + + +namespace GFXSemantic +{ + const String POSITION = String( "POSITION" ).intern(); + const String NORMAL = String( "NORMAL" ).intern(); + const String BINORMAL = String( "BINORMAL" ).intern(); + const String TANGENT = String( "TANGENT" ).intern(); + const String TANGENTW = String( "TANGENTW" ).intern(); + const String COLOR = String( "COLOR" ).intern(); + const String TEXCOORD = String( "TEXCOORD" ).intern(); +} + + +U32 GFXVertexElement::getSizeInBytes() const +{ + switch ( mType ) + { + case GFXDeclType_Float: + return 4; + + case GFXDeclType_Float2: + return 8; + + case GFXDeclType_Float3: + return 12; + + case GFXDeclType_Float4: + return 16; + + case GFXDeclType_Color: + return 4; + + default: + return 0; + }; +} + + +GFXVertexFormat::GFXVertexFormat() + : mDirty( true ), + mHasColor( false ), + mHasNormal( false ), + mHasTangent( false ), + mTexCoordCount( 0 ), + mSizeInBytes( 0 ), + mDecl( NULL ) +{ + VECTOR_SET_ASSOCIATION( mElements ); +} + +void GFXVertexFormat::copy( const GFXVertexFormat &format ) +{ + mDirty = format.mDirty; + mHasNormal = format.mHasNormal; + mHasTangent = format.mHasTangent; + mHasColor = format.mHasColor; + mTexCoordCount = format.mTexCoordCount; + mSizeInBytes = format.mSizeInBytes; + mDescription = format.mDescription; + mElements = format.mElements; + mDecl = format.mDecl; +} + +void GFXVertexFormat::append( const GFXVertexFormat &format, U32 streamIndex ) +{ + for ( U32 i=0; i < format.getElementCount(); i++ ) + { + mElements.increment(); + mElements.last() = format.getElement( i ); + if ( streamIndex != -1 ) + mElements.last().mStreamIndex = streamIndex; + } + + mDirty = true; +} + +void GFXVertexFormat::clear() +{ + mDirty = true; + mElements.clear(); + mDecl = NULL; +} + +void GFXVertexFormat::addElement( const String& semantic, GFXDeclType type, U32 index, U32 stream ) +{ + mDirty = true; + mElements.increment(); + mElements.last().mStreamIndex = stream; + mElements.last().mSemantic = semantic.intern(); + mElements.last().mSemanticIndex = index; + mElements.last().mType = type; +} + +const String& GFXVertexFormat::getDescription() const +{ + if ( mDirty ) + const_cast(this)->_updateDirty(); + + return mDescription; +} + +GFXVertexDecl* GFXVertexFormat::getDecl() const +{ + if ( !mDecl || mDirty ) + const_cast(this)->_updateDecl(); + + return mDecl; +} + +bool GFXVertexFormat::hasNormal() const +{ + if ( mDirty ) + const_cast(this)->_updateDirty(); + + return mHasNormal; +} + +bool GFXVertexFormat::hasTangent() const +{ + if ( mDirty ) + const_cast(this)->_updateDirty(); + + return mHasTangent; +} + +bool GFXVertexFormat::hasColor() const +{ + if ( mDirty ) + const_cast(this)->_updateDirty(); + + return mHasColor; +} + +U32 GFXVertexFormat::getTexCoordCount() const +{ + if ( mDirty ) + const_cast(this)->_updateDirty(); + + return mTexCoordCount; +} + +U32 GFXVertexFormat::getSizeInBytes() const +{ + if ( mDirty ) + const_cast(this)->_updateDirty(); + + return mSizeInBytes; +} + +void GFXVertexFormat::_updateDirty() +{ + PROFILE_SCOPE( GFXVertexFormat_updateDirty ); + + mTexCoordCount = 0; + + mHasColor = false; + mHasNormal = false; + mHasTangent = false; + mSizeInBytes = 0; + + String desc; + + for ( U32 i=0; i < mElements.size(); i++ ) + { + const GFXVertexElement &element = mElements[i]; + + desc += String::ToString( "%d,%s,%d,%d\n", element.mStreamIndex, + element.mSemantic.c_str(), + element.mSemanticIndex, + element.mType ); + + if ( element.isSemantic( GFXSemantic::NORMAL ) ) + mHasNormal = true; + else if ( element.isSemantic( GFXSemantic::TANGENT ) ) + mHasTangent = true; + else if ( element.isSemantic( GFXSemantic::COLOR ) ) + mHasColor = true; + else if ( element.isSemantic( GFXSemantic::TEXCOORD ) ) + ++mTexCoordCount; + + mSizeInBytes += element.getSizeInBytes(); + } + + // Intern the string for fast compares later. + mDescription = desc.intern(); + + mDirty = false; +} + +void GFXVertexFormat::_updateDecl() +{ + PROFILE_SCOPE( GFXVertexFormat_updateDecl ); + + if ( mDirty ) + _updateDirty(); + + mDecl = GFX->allocVertexDecl( this ); +} diff --git a/Engine/source/gfx/gfxVertexFormat.h b/Engine/source/gfx/gfxVertexFormat.h new file mode 100644 index 000000000..0f32e085e --- /dev/null +++ b/Engine/source/gfx/gfxVertexFormat.h @@ -0,0 +1,339 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXVERTEXFORMAT_H_ +#define _GFXVERTEXFORMAT_H_ + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _GFXENUMS_H_ +#include "gfx/gfxEnums.h" +#endif + + +/// The known Torque vertex element semantics. You can use +/// other semantic strings, but they will be interpreted as +/// a TEXCOORD. +/// @see GFXVertexElement +/// @see GFXVertexFormat +namespace GFXSemantic +{ + extern const String POSITION; + extern const String NORMAL; + extern const String BINORMAL; + extern const String TANGENT; + extern const String TANGENTW; + extern const String COLOR; + extern const String TEXCOORD; +} + + +/// This is a simple wrapper for the platform specific +/// vertex declaration data which is held by the vertex +/// format. +/// +/// If your using it... you probably shouldn't be. +/// +/// @see GFXVertexFormat +class GFXVertexDecl +{ +public: + virtual ~GFXVertexDecl() {} +}; + + +/// The element structure helps define the data layout +/// for GFXVertexFormat. +/// +/// @see GFXVertexFormat +/// +class GFXVertexElement +{ + friend class GFXVertexFormat; + +protected: + + /// The stream index when rendering from multiple + /// vertex streams. In most cases this is 0. + U32 mStreamIndex; + + /// A valid Torque shader symantic. + /// @see GFXSemantic + String mSemantic; + + /// The semantic index is used where there are + /// multiple semantics of the same type. For + /// instance with texcoords. + U32 mSemanticIndex; + + /// The element type. + GFXDeclType mType; + +public: + + /// Default constructor. + GFXVertexElement() + : mStreamIndex( 0 ), + mSemanticIndex( 0 ), + mType( GFXDeclType_Float4 ) + { + } + + /// Copy constructor. + GFXVertexElement( const GFXVertexElement &elem ) + : mStreamIndex( elem.mStreamIndex ), + mSemantic( elem.mSemantic ), + mSemanticIndex( elem.mSemanticIndex ), + mType( elem.mType ) + { + } + + /// Returns the stream index. + U32 getStreamIndex() const { return mStreamIndex; } + + /// Returns the semantic name which is usually a + /// valid Torque semantic. + /// @see GFXSemantic + const String& getSemantic() const { return mSemantic; } + + /// Returns the semantic index which is used where there + /// are multiple semantics of the same type. For instance + /// with texcoords. + U32 getSemanticIndex() const { return mSemanticIndex; } + + /// Returns the type for the semantic. + GFXDeclType getType() const { return mType; } + + /// Returns true of the semantic matches. + bool isSemantic( const String& str ) const { return ( mSemantic == str ); } + + /// Returns the size in bytes of the semantic type. + U32 getSizeInBytes() const; +}; + + +/// The vertex format structure usually created via the declare and +/// implement macros. +/// +/// You can use this class directly to create a vertex format, but +/// note that it is expected to live as long as the VB that uses it +/// exists. +/// +/// @see GFXDeclareVertexFormat +/// @see GFXImplementVertexFormat +/// @see GFXVertexElement +/// +class GFXVertexFormat +{ +public: + + /// Default constructor for an empty format. + GFXVertexFormat(); + + /// The copy constructor. + GFXVertexFormat( const GFXVertexFormat &format ) { copy( format ); } + + /// Copy the other vertex format. + void copy( const GFXVertexFormat &format ); + + /// Used to append a vertex format to the end of this one. + void append( const GFXVertexFormat &format, U32 streamIndex = -1 ); + + /// Returns a unique description string for this vertex format. + const String& getDescription() const; + + /// Clears all the vertex elements. + void clear(); + + /// Adds a vertex element to the format. + /// + /// @param semantic A valid Torque semantic string. + /// @param type The element type. + /// @param index The semantic index which is typically only used for texcoords. + /// + void addElement( const String& semantic, GFXDeclType type, U32 index = 0, U32 stream = 0 ); + + /// Returns true if there is a NORMAL semantic in this vertex format. + bool hasNormal() const; + + /// Returns true if there is a TANGENT semantic in this vertex format. + bool hasTangent() const; + + /// Returns true if there is a COLOR semantic in this vertex format. + bool hasColor() const; + + /// Returns the texture coordinate count by + /// counting the number of TEXCOORD semantics. + U32 getTexCoordCount() const; + + /// Returns true if these two formats are equal. + inline bool isEqual( const GFXVertexFormat &format ) const; + + /// Returns the total elements in this format. + U32 getElementCount() const { return mElements.size(); } + + /// Returns the vertex element by index. + const GFXVertexElement& getElement( U32 index ) const { return mElements[index]; } + + /// Returns the size in bytes of the format as described. + U32 getSizeInBytes() const; + + /// Returns the hardware specific vertex declaration for this format. + GFXVertexDecl* getDecl() const; + +protected: + + /// We disable the copy operator. + GFXVertexFormat& operator =( const GFXVertexFormat& ) { return *this; } + + /// Recreates the description and state when + /// the format has been modified. + void _updateDirty(); + + /// Requests the vertex declaration from the GFX device. + void _updateDecl(); + + /// Set when the element list is changed. + bool mDirty; + + /// Is set if there is a NORMAL semantic in this vertex format. + bool mHasNormal; + + /// Is true if there is a TANGENT semantic in this vertex format. + bool mHasTangent; + + /// Is true if there is a COLOR semantic in this vertex format. + bool mHasColor; + + /// The texture coordinate count by counting the + /// number of "TEXCOORD" semantics. + U32 mTexCoordCount; + + /// The size in bytes of the vertex format as described. + U32 mSizeInBytes; + + /// An interned string which uniquely identifies the format. + String mDescription; + + /// The elements of the vertex format. + Vector mElements; + + /// The hardware specific vertex declaration. + GFXVertexDecl *mDecl; +}; + + +inline bool GFXVertexFormat::isEqual( const GFXVertexFormat &format ) const +{ + // Comparing the strings works because we know both + // these are interned strings. This saves one comparison + // over the string equality operator. + return getDescription().c_str() == format.getDescription().c_str(); +} + + +/// This template class is usused to initialize the format in +/// the GFXImplement/DeclareVertexFormat macros. You shouldn't +/// need to use it directly in your code. +/// +/// @see GFXVertexFormat +/// @see GFXImplementVertexFormat +/// +template +class _GFXVertexFormatConstructor : public GFXVertexFormat +{ +protected: + + void _construct(); + +public: + + _GFXVertexFormatConstructor() { _construct(); } +}; + + +/// Helper template function which returns the correct +/// GFXVertexFormat object for a vertex structure. +/// @see GFXVertexFormat +template inline const GFXVertexFormat* getGFXVertexFormat(); + +#ifdef TORQUE_OS_XENON + + /// On the Xbox360 we want we want to be sure that verts + /// are on aligned boundariess. + #define GFX_VERTEX_STRUCT __declspec(align(16)) struct + +#else + #define GFX_VERTEX_STRUCT struct +#endif + + +/// The vertex format declaration which is usally placed in your header +/// file. It should be used in conjunction with the implementation macro. +/// +/// @param name The name for the vertex structure. +/// +/// @code +/// +/// // A simple vertex format declaration. +/// GFXDeclareVertexFormat( GFXVertexPCT ) +/// { +/// Point3F pos; +/// GFXVertexColor color; +/// Point2F texCoord; +/// } +/// +/// @endcode +/// +/// @see GFXImplementVertexFormat +/// +#define GFXDeclareVertexFormat( name ) \ + GFX_VERTEX_STRUCT name; \ + extern const GFXVertexFormat _gfxVertexFormat##name; \ + template<> inline const GFXVertexFormat* getGFXVertexFormat() { static _GFXVertexFormatConstructor vertexFormat; return &vertexFormat; } \ + GFX_VERTEX_STRUCT name \ + + +/// The vertex format implementation which is usally placed in your source +/// file. It should be used in conjunction with the declaration macro. +/// +/// @param name The name of the vertex structure. +/// +/// @code +/// +/// // A simple vertex format implementation. +/// GFXImplementVertexFormat( GFXVertexPCT ) +/// { +/// addElement( "POSITION", GFXDeclType_Float3 ); +/// addElement( "COLOR", GFXDeclType_Color ); +/// addElement( "TEXCOORD", GFXDeclType_Float2, 0 ); +/// } +/// +/// @endcode +/// +/// @see GFXDeclareVertexFormat +/// +#define GFXImplementVertexFormat( name ) \ + template<> void _GFXVertexFormatConstructor::_construct() \ + +#endif // _GFXVERTEXFORMAT_H_ diff --git a/Engine/source/gfx/gfxVertexTypes.cpp b/Engine/source/gfx/gfxVertexTypes.cpp new file mode 100644 index 000000000..ca4598270 --- /dev/null +++ b/Engine/source/gfx/gfxVertexTypes.cpp @@ -0,0 +1,119 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/gfxVertexTypes.h" + + +GFXImplementVertexFormat( GFXVertexP ) +{ + addElement( "POSITION", GFXDeclType_Float3 ); +} + +GFXImplementVertexFormat( GFXVertexPT ) +{ + addElement( "POSITION", GFXDeclType_Float3 ); + addElement( "TEXCOORD", GFXDeclType_Float2, 0 ); +} + +GFXImplementVertexFormat( GFXVertexPTT ) +{ + addElement( "POSITION", GFXDeclType_Float3 ); + addElement( "TEXCOORD", GFXDeclType_Float2, 0 ); + addElement( "TEXCOORD", GFXDeclType_Float2, 1 ); +} + +GFXImplementVertexFormat( GFXVertexPTTT ) +{ + addElement( "POSITION", GFXDeclType_Float3 ); + addElement( "TEXCOORD", GFXDeclType_Float2, 0 ); + addElement( "TEXCOORD", GFXDeclType_Float2, 1 ); + addElement( "TEXCOORD", GFXDeclType_Float2, 2 ); +} + +GFXImplementVertexFormat( GFXVertexPC ) +{ + addElement( "POSITION", GFXDeclType_Float3 ); + addElement( "COLOR", GFXDeclType_Color ); +} + +GFXImplementVertexFormat( GFXVertexPCN ) +{ + addElement( "POSITION", GFXDeclType_Float3 ); + addElement( "NORMAL", GFXDeclType_Float3 ); + addElement( "COLOR", GFXDeclType_Color ); +} + +GFXImplementVertexFormat( GFXVertexPCT ) +{ + addElement( "POSITION", GFXDeclType_Float3 ); + addElement( "COLOR", GFXDeclType_Color ); + addElement( "TEXCOORD", GFXDeclType_Float2, 0 ); +} + +GFXImplementVertexFormat( GFXVertexPCTT ) +{ + addElement( "POSITION", GFXDeclType_Float3 ); + addElement( "COLOR", GFXDeclType_Color ); + addElement( "TEXCOORD", GFXDeclType_Float2, 0 ); + addElement( "TEXCOORD", GFXDeclType_Float2, 1 ); +} + +GFXImplementVertexFormat( GFXVertexPN ) +{ + addElement( "POSITION", GFXDeclType_Float3 ); + addElement( "NORMAL", GFXDeclType_Float3 ); +} + +GFXImplementVertexFormat( GFXVertexPNT ) +{ + addElement( "POSITION", GFXDeclType_Float3 ); + addElement( "NORMAL", GFXDeclType_Float3 ); + addElement( "TEXCOORD", GFXDeclType_Float2, 0 ); +} + +GFXImplementVertexFormat( GFXVertexPNTT ) +{ + addElement( "POSITION", GFXDeclType_Float3 ); + addElement( "NORMAL", GFXDeclType_Float3 ); + addElement( "TANGENT", GFXDeclType_Float3 ); + addElement( "TEXCOORD", GFXDeclType_Float2, 0 ); +} + +GFXImplementVertexFormat( GFXVertexPNTBT ) +{ + addElement( "POSITION", GFXDeclType_Float3 ); + addElement( "NORMAL", GFXDeclType_Float3 ); + addElement( "TANGENT", GFXDeclType_Float3 ); + addElement( "BINORMAL", GFXDeclType_Float3 ); + addElement( "TEXCOORD", GFXDeclType_Float2, 0 ); +} + +GFXImplementVertexFormat( GFXVertexPNTTB ) +{ + addElement( "POSITION", GFXDeclType_Float3 ); + addElement( "NORMAL", GFXDeclType_Float3 ); + addElement( "TANGENT", GFXDeclType_Float3 ); + addElement( "BINORMAL", GFXDeclType_Float3 ); + addElement( "TEXCOORD", GFXDeclType_Float2, 0 ); + addElement( "TEXCOORD", GFXDeclType_Float2, 1 ); +} diff --git a/Engine/source/gfx/gfxVertexTypes.h b/Engine/source/gfx/gfxVertexTypes.h new file mode 100644 index 000000000..9072f533c --- /dev/null +++ b/Engine/source/gfx/gfxVertexTypes.h @@ -0,0 +1,167 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXVERTEXTYPES_H_ +#define _GFXVERTEXTYPES_H_ + +#ifndef _GFXVERTEXFORMAT_H_ +#include "gfx/gfxVertexFormat.h" +#endif +#ifndef _GFXVERTEXCOLOR_H_ +#include "gfx/gfxVertexColor.h" +#endif +#ifndef _MPOINT2_H_ +#include "math/mPoint2.h" +#endif +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif + + +GFXDeclareVertexFormat( GFXVertexP ) +{ + Point3F point; +}; + +GFXDeclareVertexFormat( GFXVertexPT ) +{ + Point3F point; + Point2F texCoord; +}; + +GFXDeclareVertexFormat( GFXVertexPTT ) +{ + Point3F point; + Point2F texCoord1; + Point2F texCoord2; +}; + +GFXDeclareVertexFormat( GFXVertexPTTT ) +{ + Point3F point; + Point2F texCoord1; + Point2F texCoord2; + Point2F texCoord3; +}; + +GFXDeclareVertexFormat( GFXVertexPC ) +{ + Point3F point; + GFXVertexColor color; +}; + +GFXDeclareVertexFormat( GFXVertexPCN ) +{ + Point3F point; + Point3F normal; + GFXVertexColor color; +}; + +GFXDeclareVertexFormat( GFXVertexPCT ) +{ + Point3F point; + GFXVertexColor color; + Point2F texCoord; +}; + +GFXDeclareVertexFormat( GFXVertexPCTT ) +{ + Point3F point; + GFXVertexColor color; + Point2F texCoord; + Point2F texCoord2; +}; + +GFXDeclareVertexFormat( GFXVertexPN ) +{ + Point3F point; + Point3F normal; +}; + +GFXDeclareVertexFormat( GFXVertexPNT ) +{ + Point3F point; + Point3F normal; + Point2F texCoord; +}; + +GFXDeclareVertexFormat( GFXVertexPNTT ) +{ + Point3F point; + Point3F normal; + Point3F tangent; + Point2F texCoord; +}; + +GFXDeclareVertexFormat( GFXVertexPNTBT ) +{ + Point3F point; + Point3F normal; + Point3F tangent; + Point3F binormal; + Point2F texCoord; +}; + +/* + +DEFINE_VERT( GFXVertexPCNT, + GFXVertexFlagXYZ | GFXVertexFlagNormal | GFXVertexFlagDiffuse | GFXVertexFlagTextureCount1 | GFXVertexFlagUV0) +{ + Point3F point; + Point3F normal; + GFXVertexColor color; + Point2F texCoord; +}; + +DEFINE_VERT( GFXVertexPCNTT, + GFXVertexFlagXYZ | GFXVertexFlagNormal | GFXVertexFlagDiffuse | GFXVertexFlagTextureCount2 | GFXVertexFlagUV0 | GFXVertexFlagUV1) +{ + Point3F point; + Point3F normal; + GFXVertexColor color; + Point2F texCoord[2]; +}; +*/ + +GFXDeclareVertexFormat( GFXVertexPNTTB ) +{ + Point3F point; + Point3F normal; + Point3F T; + Point3F B; + Point2F texCoord; + Point2F texCoord2; +}; + +/* +DEFINE_VERT( GFXVertexPNTB, + GFXVertexFlagXYZ | GFXVertexFlagNormal | GFXVertexFlagTextureCount2 | + GFXVertexFlagUV0 | GFXVertexFlagUVW1 ) +{ + Point3F point; + Point3F normal; + Point2F texCoord; + Point3F binormal; +}; +*/ + +#endif // _GFXVERTEXTYPES_H_ diff --git a/Engine/source/gfx/gl/gfxGLAppleFence.cpp b/Engine/source/gfx/gl/gfxGLAppleFence.cpp new file mode 100644 index 000000000..9daf28b3f --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLAppleFence.cpp @@ -0,0 +1,71 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/gl/gfxGLAppleFence.h" + +GFXGLAppleFence::GFXGLAppleFence(GFXDevice* device) : GFXFence(device), mIssued(false) +{ + glGenFencesAPPLE(1, &mHandle); +} + +GFXGLAppleFence::~GFXGLAppleFence() +{ + glDeleteFencesAPPLE(1, &mHandle); +} + +void GFXGLAppleFence::issue() +{ + glSetFenceAPPLE(mHandle); + mIssued = true; +} + +GFXFence::FenceStatus GFXGLAppleFence::getStatus() const +{ + if(!mIssued) + return GFXFence::Unset; + + GLboolean res = glTestFenceAPPLE(mHandle); + return res ? GFXFence::Processed : GFXFence::Pending; +} + +void GFXGLAppleFence::block() +{ + if(!mIssued) + return; + + glFinishFenceAPPLE(mHandle); +} + +void GFXGLAppleFence::zombify() +{ + glDeleteFencesAPPLE(1, &mHandle); +} + +void GFXGLAppleFence::resurrect() +{ + glGenFencesAPPLE(1, &mHandle); +} + +const String GFXGLAppleFence::describeSelf() const +{ + return String::ToString(" GL Handle: %i", mHandle); +} diff --git a/Engine/source/gfx/gl/gfxGLAppleFence.h b/Engine/source/gfx/gl/gfxGLAppleFence.h new file mode 100644 index 000000000..5a2029197 --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLAppleFence.h @@ -0,0 +1,50 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXGLAPPLEFENCE_H_ +#define _GFXGLAPPLEFENCE_H_ + +#include "gfx/gfxFence.h" +#include "gfx/gl/ggl/ggl.h" + +class GFXGLAppleFence : public GFXFence +{ +public: + GFXGLAppleFence(GFXDevice* device); + virtual ~GFXGLAppleFence(); + + // GFXFence interface + virtual void issue(); + virtual FenceStatus getStatus() const; + virtual void block(); + + // GFXResource interface + virtual void zombify(); + virtual void resurrect(); + virtual const String describeSelf() const; + +private: + GLuint mHandle; + bool mIssued; +}; + +#endif \ No newline at end of file diff --git a/Engine/source/gfx/gl/gfxGLCardProfiler.cpp b/Engine/source/gfx/gl/gfxGLCardProfiler.cpp new file mode 100644 index 000000000..900737b19 --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLCardProfiler.cpp @@ -0,0 +1,133 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/gl/gfxGLCardProfiler.h" +#include "gfx/gl/gfxGLDevice.h" +#include "gfx/gl/gfxGLEnumTranslate.h" + +void GFXGLCardProfiler::init() +{ + mChipSet = reinterpret_cast(glGetString(GL_VENDOR)); + + // get the major and minor parts of the GL version. These are defined to be + // in the order "[major].[minor] [other]|[major].[minor].[release] [other] in the spec + const char *versionStart = reinterpret_cast(glGetString(GL_VERSION)); + const char *versionEnd = versionStart; + // get the text for the version "x.x.xxxx " + for( S32 tok = 0; tok < 2; ++tok ) + { + char *text = dStrdup( versionEnd ); + dStrtok(text, ". "); + versionEnd += dStrlen( text ) + 1; + dFree( text ); + } + + mRendererString = "GL"; + mRendererString += String::SpanToString(versionStart, versionEnd - 1); + + mCardDescription = reinterpret_cast(glGetString(GL_RENDERER)); + mVersionString = reinterpret_cast(glGetString(GL_VERSION)); + + mVideoMemory = static_cast(GFX)->getTotalVideoMemory(); + + Parent::init(); + + // Set new enums here so if our profile script forces this to be false we keep the GL_ZEROs. + if(queryProfile("GL::suppFloatTexture")) + { + GFXGLTextureInternalFormat[GFXFormatR16G16F] = GL_RGBA_FLOAT16_ATI; + GFXGLTextureFormat[GFXFormatR16G16F] = GL_RGBA; + GFXGLTextureInternalFormat[GFXFormatR16G16B16A16F] = GL_RGBA_FLOAT16_ATI; + GFXGLTextureInternalFormat[GFXFormatR32G32B32A32F] = GL_RGBA_FLOAT32_ATI; + GFXGLTextureInternalFormat[GFXFormatR32F] = GL_RGBA_FLOAT32_ATI; + } +} + +void GFXGLCardProfiler::setupCardCapabilities() +{ + GLint maxTexSize; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexSize); + + const char* versionString = reinterpret_cast(glGetString(GL_VERSION)); + F32 glVersion = dAtof(versionString); + + // OpenGL doesn't have separate maximum width/height. + setCapability("maxTextureWidth", maxTexSize); + setCapability("maxTextureHeight", maxTexSize); + setCapability("maxTextureSize", maxTexSize); + + // If extensions haven't been inited, we're in trouble here. + bool suppVBO = (gglHasExtension(GL_ARB_vertex_buffer_object) || glVersion >= 1.499f); + setCapability("GL::suppVertexBufferObject", suppVBO); + + // check if render to texture supported is available + bool suppRTT = gglHasExtension(GL_EXT_framebuffer_object); + setCapability("GL::suppRenderTexture", suppRTT); + + bool suppBlit = gglHasExtension(GL_EXT_framebuffer_blit); + setCapability("GL::suppRTBlit", suppBlit); + + bool suppFloatTex = gglHasExtension(GL_ATI_texture_float); + setCapability("GL::suppFloatTexture", suppFloatTex); + + // Check for anisotropic filtering support. + bool suppAnisotropic = gglHasExtension( GL_EXT_texture_filter_anisotropic ); + setCapability( "GL::suppAnisotropic", suppAnisotropic ); + + // check to see if we have the fragment shader extension or the gl version is high enough for glsl to be core + // also check to see if the language version is high enough + F32 glslVersion = dAtof(reinterpret_cast(glGetString( GL_SHADING_LANGUAGE_VERSION))); + bool suppSPU = (gglHasExtension(GL_ARB_fragment_shader) || glVersion >= 1.999f) && glslVersion >= 1.0999; + setCapability("GL::suppFragmentShader", suppSPU); + + bool suppAppleFence = gglHasExtension(GL_APPLE_fence); + setCapability("GL::APPLE::suppFence", suppAppleFence); + + // When enabled, call glGenerateMipmapEXT() to generate mipmaps instead of relying on GL_GENERATE_MIPMAP + setCapability("GL::Workaround::needsExplicitGenerateMipmap", false); + // When enabled, binds and unbinds a texture target before doing the depth buffer copy. Failure to do + // so will cause a hard freeze on Mac OS 10.4 with a Radeon X1600 + setCapability("GL::Workaround::X1600DepthBufferCopy", false); + // When enabled, does not copy the last column and row of the depth buffer in a depth buffer copy. Failure + // to do so will cause a kernel panic on Mac OS 10.5(.1) with a Radeon HD 2600 (fixed in 10.5.2) + setCapability("GL::Workaround::HD2600DepthBufferCopy", false); + + // Certain Intel drivers have a divide by 0 crash if mipmaps are specified with + // glTexSubImage2D. + setCapability("GL::Workaround::noManualMips", false); +} + +bool GFXGLCardProfiler::_queryCardCap(const String& query, U32& foundResult) +{ + // Just doing what the D3D9 layer does + return 0; +} + +bool GFXGLCardProfiler::_queryFormat(const GFXFormat fmt, const GFXTextureProfile *profile, bool &inOutAutogenMips) +{ + // We assume if the format is valid that we can use it for any purpose. + // This may not be the case, but we have no way to check short of in depth + // testing of every format for every purpose. And by testing, I mean sitting + // down and doing it by hand, because there is no OpenGL API to check these + // things. + return GFXGLTextureInternalFormat[fmt] != GL_ZERO; +} diff --git a/Engine/source/gfx/gl/gfxGLCardProfiler.h b/Engine/source/gfx/gl/gfxGLCardProfiler.h new file mode 100644 index 000000000..01d54d7ac --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLCardProfiler.h @@ -0,0 +1,54 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXGLCARDPROFILE_H +#define _GFXGLCARDPROFILE_H + +#include "gfx/gfxCardProfile.h" + +/// A note on workarounds - The following settings are used exclusively to work around driver bugs: +/// GL::Workaround::needsExplicitGenerateMipmap +/// GL::Workaround::X1600DepthBufferCopy +/// GL::Workaround::HD2600DepthBufferCopy +/// If you find that you need to work around additional driver bugs, add a new setting +/// of the form GL::Workaround:: and set it to false in GFXGLCardProfiler::setupCardCapabilities() +/// Enclose your workaround code in if(GFX->getCardProfiler()->queryProfile("GL::Workaround::")) { +/// } +/// Remember to set the work around to true in the card profile script! + +class GFXGLCardProfiler : public GFXCardProfiler +{ +public: + void init(); + +protected: + virtual const String& getRendererString() const { return mRendererString; } + virtual void setupCardCapabilities(); + virtual bool _queryCardCap(const String& query, U32& foundResult); + virtual bool _queryFormat(const GFXFormat fmt, const GFXTextureProfile *profile, bool &inOutAutogenMips); + +private: + String mRendererString; + typedef GFXCardProfiler Parent; +}; + +#endif diff --git a/Engine/source/gfx/gl/gfxGLCubemap.cpp b/Engine/source/gfx/gl/gfxGLCubemap.cpp new file mode 100644 index 000000000..d5afbc4a8 --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLCubemap.cpp @@ -0,0 +1,248 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/gl/gfxGLDevice.h" +#include "gfx/gl/gfxGLTextureObject.h" +#include "gfx/gl/gfxGLEnumTranslate.h" +#include "gfx/gl/gfxGLUtils.h" +#include "gfx/gl/gfxGLCubemap.h" +#include "gfx/gfxTextureManager.h" +#include "gfx/gfxCardProfile.h" +#include "gfx/bitmap/DDSFile.h" + + +GLenum GFXGLCubemap::faceList[6] = +{ + GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z +}; + +GFXGLCubemap::GFXGLCubemap() : + mCubemap(0), + mDynamicTexSize(0), + mFaceFormat( GFXFormatR8G8B8A8 ) +{ + for(U32 i = 0; i < 6; i++) + mTextures[i] = NULL; + + GFXTextureManager::addEventDelegate( this, &GFXGLCubemap::_onTextureEvent ); +} + +GFXGLCubemap::~GFXGLCubemap() +{ + glDeleteTextures(1, &mCubemap); + GFXTextureManager::removeEventDelegate( this, &GFXGLCubemap::_onTextureEvent ); +} + +void GFXGLCubemap::fillCubeTextures(GFXTexHandle* faces) +{ + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, mCubemap); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_GENERATE_MIPMAP, GL_TRUE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + U32 reqWidth = faces[0]->getWidth(); + U32 reqHeight = faces[0]->getHeight(); + GFXFormat regFaceFormat = faces[0]->getFormat(); + mWidth = reqWidth; + mHeight = reqHeight; + mFaceFormat = regFaceFormat; + mMipLevels = 1; // Lie for now + AssertFatal(reqWidth == reqHeight, "GFXGLCubemap::fillCubeTextures - Width and height must be equal!"); + + for(U32 i = 0; i < 6; i++) + { + AssertFatal(faces[i], avar("GFXGLCubemap::fillCubeFaces - texture %i is NULL!", i)); + AssertFatal((faces[i]->getWidth() == reqWidth) && (faces[i]->getHeight() == reqHeight), "GFXGLCubemap::fillCubeFaces - All textures must have identical dimensions!"); + AssertFatal(faces[i]->getFormat() == regFaceFormat, "GFXGLCubemap::fillCubeFaces - All textures must have identical formats!"); + + mTextures[i] = faces[i]; + GFXFormat faceFormat = faces[i]->getFormat(); + + GFXGLTextureObject* glTex = static_cast(faces[i].getPointer()); + U8* buf = glTex->getTextureData(); + glTexImage2D(faceList[i], 0, GFXGLTextureInternalFormat[faceFormat], faces[i]->getWidth(), faces[i]->getHeight(), + 0, GFXGLTextureFormat[faceFormat], GFXGLTextureType[faceFormat], buf); + delete[] buf; + } + + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0); +} + +void GFXGLCubemap::initStatic(GFXTexHandle* faces) +{ + if(mCubemap) + return; + + if(faces) + { + AssertFatal(faces[0], "GFXGLCubemap::initStatic - empty texture passed"); + glGenTextures(1, &mCubemap); + fillCubeTextures(faces); + } +} + +void GFXGLCubemap::initStatic( DDSFile *dds ) +{ + if(mCubemap) + return; + + AssertFatal( dds, "GFXGLCubemap::initStatic - Got null DDS file!" ); + AssertFatal( dds->isCubemap(), "GFXGLCubemap::initStatic - Got non-cubemap DDS file!" ); + AssertFatal( dds->mSurfaces.size() == 6, "GFXGLCubemap::initStatic - DDS has less than 6 surfaces!" ); + + // HACK: I cannot put the genie back in the bottle and assign a + // DDSFile pointer back to a Resource<>. + // + // So we do a second lookup which works out ok for now, but shows + // the weakness in the ResourceManager not having a common base + // reference type. + // + mDDSFile = ResourceManager::get().load( dds->getSourcePath() ); + AssertFatal( mDDSFile == dds, "GFXGLCubemap::initStatic - Couldn't find DDSFile resource!" ); + + glGenTextures(1, &mCubemap); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_CUBE_MAP, mCubemap); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_GENERATE_MIPMAP, GL_TRUE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + mWidth = dds->getWidth(); + mHeight = dds->getHeight(); + mFaceFormat = dds->getFormat(); + mMipLevels = 1; + + // TODO: Support mipmaps here as well as decompressing the + // DDS if the format is unsupported. + + AssertFatal(mWidth == mHeight, "GFXGLCubemap::initStatic - Width and height must be equal!"); + + for(U32 i = 0; i < 6; i++) + { + if ( !dds->mSurfaces[i] ) + { + // TODO: The DDS can skip surfaces, but i'm unsure what i should + // do here when creating the cubemap. Ignore it for now. + continue; + } + + const U8 *buffer = dds->mSurfaces[i]->mMips[0]; + U32 surfaceSize = dds->getSurfaceSize( mHeight, mWidth, i ); + + glCompressedTexImage2D( faceList[i], 0, GFXGLTextureInternalFormat[mFaceFormat], + mWidth, mHeight, 0, surfaceSize, buffer ); + } + + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0); +} + +void GFXGLCubemap::initDynamic(U32 texSize, GFXFormat faceFormat) +{ + mDynamicTexSize = texSize; + mFaceFormat = faceFormat; + + glGenTextures(1, &mCubemap); + glBindTexture(GL_TEXTURE_CUBE_MAP, mCubemap); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + mWidth = texSize; + mHeight = texSize; + mMipLevels = 1; + for(U32 i = 0; i < 6; i++) + { + glTexImage2D( faceList[i], 0, GFXGLTextureInternalFormat[faceFormat], texSize, texSize, + 0, GFXGLTextureFormat[faceFormat], GFXGLTextureType[faceFormat], NULL); + } + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); +} + +void GFXGLCubemap::zombify() +{ + glDeleteTextures(1, &mCubemap); + mCubemap = 0; +} + +void GFXGLCubemap::resurrect() +{ + // Handled in tmResurrect +} + +void GFXGLCubemap::tmResurrect() +{ + if(mDynamicTexSize) + initDynamic(mDynamicTexSize,mFaceFormat); + else + { + if ( mDDSFile ) + initStatic( mDDSFile ); + else + initStatic( mTextures ); + } +} + +void GFXGLCubemap::setToTexUnit(U32 tuNum) +{ + static_cast(getOwningDevice())->setCubemapInternal(tuNum, this); +} + +void GFXGLCubemap::bind(U32 textureUnit) const +{ + glActiveTexture(GL_TEXTURE0 + textureUnit); + glBindTexture(GL_TEXTURE_CUBE_MAP, mCubemap); + + GFXGLStateBlockRef sb = static_cast(GFX)->getCurrentStateBlock(); + AssertFatal(sb, "GFXGLCubemap::bind - No active stateblock!"); + if (!sb) + return; + + const GFXSamplerStateDesc& ssd = sb->getDesc().samplers[textureUnit]; + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, minificationFilter(ssd.minFilter, ssd.mipFilter, 0)); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GFXGLTextureFilter[ssd.magFilter]); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GFXGLTextureAddress[ssd.addressModeU]); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GFXGLTextureAddress[ssd.addressModeV]); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GFXGLTextureAddress[ssd.addressModeW]); + + glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, ssd.mipLODBias); +} + +void GFXGLCubemap::_onTextureEvent( GFXTexCallbackCode code ) +{ + if ( code == GFXZombify ) + zombify(); + else + tmResurrect(); +} diff --git a/Engine/source/gfx/gl/gfxGLCubemap.h b/Engine/source/gfx/gl/gfxGLCubemap.h new file mode 100644 index 000000000..458043eb8 --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLCubemap.h @@ -0,0 +1,93 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXGLCUBEMAP_H_ +#define _GFXGLCUBEMAP_H_ + +#ifndef _GFXCUBEMAP_H_ +#include "gfx/gfxCubemap.h" +#endif +#ifndef __RESOURCE_H__ +#include "core/resource.h" +#endif + + +class GFXGLCubemap : public GFXCubemap +{ +public: + GFXGLCubemap(); + virtual ~GFXGLCubemap(); + + virtual void initStatic( GFXTexHandle *faces ); + virtual void initStatic( DDSFile *dds ); + virtual void initDynamic( U32 texSize, GFXFormat faceFormat = GFXFormatR8G8B8A8 ); + virtual U32 getSize() const { return mWidth; } + virtual GFXFormat getFormat() const { return mFaceFormat; } + + // Convenience methods for GFXGLTextureTarget + U32 getWidth() { return mWidth; } + U32 getHeight() { return mHeight; } + U32 getNumMipLevels() { return mMipLevels; } + U32 getHandle() { return mCubemap; } + + // GFXResource interface + virtual void zombify(); + virtual void resurrect(); + + /// Called by texCB; this is to ensure that all textures have been resurrected before we attempt to res the cubemap. + void tmResurrect(); + + static GLenum getEnumForFaceNumber(U32 face) { return faceList[face]; } ///< Performs lookup to get a GLenum for the given face number + +protected: + + friend class GFXDevice; + friend class GFXGLDevice; + + /// The callback used to get texture events. + /// @see GFXTextureManager::addEventDelegate + void _onTextureEvent( GFXTexCallbackCode code ); + + GLuint mCubemap; ///< Internal GL handle + U32 mDynamicTexSize; ///< Size of faces for a dynamic texture (used in resurrect) + + // Self explanatory + U32 mWidth; + U32 mHeight; + U32 mMipLevels; + GFXFormat mFaceFormat; + + GFXTexHandle mTextures[6]; ///< Keep refs to our textures for resurrection of static cubemaps + + /// The backing DDSFile uses to restore the faces + /// when the surface is lost. + Resource mDDSFile; + + // should only be called by GFXDevice + virtual void setToTexUnit( U32 tuNum ); ///< Binds the cubemap to the given texture unit + virtual void bind(U32 textureUnit) const; ///< Notifies our owning device that we want to be set to the given texture unit (used for GL internal state tracking) + void fillCubeTextures(GFXTexHandle* faces); ///< Copies the textures in faces into the cubemap + + static GLenum faceList[6]; ///< Lookup table +}; + +#endif diff --git a/Engine/source/gfx/gl/gfxGLDevice.cpp b/Engine/source/gfx/gl/gfxGLDevice.cpp new file mode 100644 index 000000000..8e9acd20c --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLDevice.cpp @@ -0,0 +1,769 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/gl/gfxGLDevice.h" + +#include "gfx/gfxCubemap.h" +#include "gfx/screenshot.h" +#include "gfx/gfxDrawUtil.h" + +#include "gfx/gl/gfxGLEnumTranslate.h" +#include "gfx/gl/gfxGLVertexBuffer.h" +#include "gfx/gl/gfxGLPrimitiveBuffer.h" +#include "gfx/gl/gfxGLTextureTarget.h" +#include "gfx/gl/gfxGLTextureManager.h" +#include "gfx/gl/gfxGLTextureObject.h" +#include "gfx/gl/gfxGLCubemap.h" +#include "gfx/gl/gfxGLCardProfiler.h" +#include "gfx/gl/gfxGLWindowTarget.h" +#include "gfx/gl/ggl/ggl.h" +#include "platform/platformDlibrary.h" +#include "gfx/gl/gfxGLShader.h" +#include "gfx/primBuilder.h" +#include "console/console.h" +#include "gfx/gl/gfxGLOcclusionQuery.h" + +GFXAdapter::CreateDeviceInstanceDelegate GFXGLDevice::mCreateDeviceInstance(GFXGLDevice::createInstance); + +GFXDevice *GFXGLDevice::createInstance( U32 adapterIndex ) +{ + return new GFXGLDevice(adapterIndex); +} + +namespace GL +{ + extern void gglPerformBinds(); + extern void gglPerformExtensionBinds(void *context); +} + +void loadGLCore() +{ + static bool coreLoaded = false; // Guess what this is for. + if(coreLoaded) + return; + coreLoaded = true; + + // Make sure we've got our GL bindings. + GL::gglPerformBinds(); +} + +void loadGLExtensions(void *context) +{ + static bool extensionsLoaded = false; + if(extensionsLoaded) + return; + extensionsLoaded = true; + + GL::gglPerformExtensionBinds(context); +} + +void GFXGLDevice::initGLState() +{ + // We don't currently need to sync device state with a known good place because we are + // going to set everything in GFXGLStateBlock, but if we change our GFXGLStateBlock strategy, this may + // need to happen. + + // Deal with the card profiler here when we know we have a valid context. + mCardProfiler = new GFXGLCardProfiler(); + mCardProfiler->init(); + glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, (GLint*)&mMaxShaderTextures); + glGetIntegerv(GL_MAX_TEXTURE_UNITS, (GLint*)&mMaxFFTextures); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + // Apple's drivers lie and claim that everything supports fragment shaders. Conveniently they don't lie about the number + // of supported image units. Checking for 16 or more image units ensures that we don't try and use pixel shaders on + // cards which don't support them. + if(mCardProfiler->queryProfile("GL::suppFragmentShader") && mMaxShaderTextures >= 16) + mPixelShaderVersion = 2.0f; + else + mPixelShaderVersion = 0.0f; + + // MACHAX - Setting mPixelShaderVersion to 3.0 will allow Advanced Lighting + // to run. At the time of writing (6/18) it doesn't quite work yet. + if(Con::getBoolVariable("$pref::machax::enableAdvancedLighting", false)) + mPixelShaderVersion = 3.0f; + + mSupportsAnisotropic = mCardProfiler->queryProfile( "GL::suppAnisotropic" ); +} + +GFXGLDevice::GFXGLDevice(U32 adapterIndex) : + mAdapterIndex(adapterIndex), + mCurrentVB(NULL), + mCurrentPB(NULL), + m_mCurrentWorld(true), + m_mCurrentView(true), + mContext(NULL), + mPixelFormat(NULL), + mPixelShaderVersion(0.0f), + mMaxShaderTextures(2), + mMaxFFTextures(2), + mClip(0, 0, 0, 0) +{ + loadGLCore(); + + GFXGLEnumTranslate::init(); + + GFXVertexColor::setSwizzle( &Swizzles::rgba ); + mDeviceSwizzle32 = &Swizzles::bgra; + mDeviceSwizzle24 = &Swizzles::bgr; + + mTextureManager = new GFXGLTextureManager(); + gScreenShot = new ScreenShot(); + + for(U32 i = 0; i < TEXTURE_STAGE_COUNT; i++) + mActiveTextureType[i] = GL_ZERO; +} + +GFXGLDevice::~GFXGLDevice() +{ + mCurrentStateBlock = NULL; + mCurrentPB = NULL; + mCurrentVB = NULL; + for(U32 i = 0; i < mVolatileVBs.size(); i++) + mVolatileVBs[i] = NULL; + for(U32 i = 0; i < mVolatilePBs.size(); i++) + mVolatilePBs[i] = NULL; + + GFXResource* walk = mResourceListHead; + while(walk) + { + walk->zombify(); + walk = walk->getNextResource(); + } + + if( mCardProfiler ) + SAFE_DELETE( mCardProfiler ); + + SAFE_DELETE( gScreenShot ); +} + +void GFXGLDevice::zombify() +{ + mTextureManager->zombify(); + if(mCurrentVB) + mCurrentVB->finish(); + if(mCurrentPB) + mCurrentPB->finish(); + //mVolatileVBs.clear(); + //mVolatilePBs.clear(); + GFXResource* walk = mResourceListHead; + while(walk) + { + walk->zombify(); + walk = walk->getNextResource(); + } +} + +void GFXGLDevice::resurrect() +{ + GFXResource* walk = mResourceListHead; + while(walk) + { + walk->resurrect(); + walk = walk->getNextResource(); + } + if(mCurrentVB) + mCurrentVB->prepare(); + if(mCurrentPB) + mCurrentPB->prepare(); + mTextureManager->resurrect(); +} + +GFXVertexBuffer* GFXGLDevice::findVolatileVBO(U32 numVerts, const GFXVertexFormat *vertexFormat, U32 vertSize) +{ + for(U32 i = 0; i < mVolatileVBs.size(); i++) + if ( mVolatileVBs[i]->mNumVerts >= numVerts && + mVolatileVBs[i]->mVertexFormat.isEqual( *vertexFormat ) && + mVolatileVBs[i]->mVertexSize == vertSize && + mVolatileVBs[i]->getRefCount() == 1 ) + return mVolatileVBs[i]; + + // No existing VB, so create one + StrongRefPtr buf(new GFXGLVertexBuffer(GFX, numVerts, vertexFormat, vertSize, GFXBufferTypeVolatile)); + buf->registerResourceWithDevice(this); + mVolatileVBs.push_back(buf); + return buf.getPointer(); +} + +GFXPrimitiveBuffer* GFXGLDevice::findVolatilePBO(U32 numIndices, U32 numPrimitives) +{ + for(U32 i = 0; i < mVolatilePBs.size(); i++) + if((mVolatilePBs[i]->mIndexCount >= numIndices) && (mVolatilePBs[i]->getRefCount() == 1)) + return mVolatilePBs[i]; + + // No existing PB, so create one + StrongRefPtr buf(new GFXGLPrimitiveBuffer(GFX, numIndices, numPrimitives, GFXBufferTypeVolatile)); + buf->registerResourceWithDevice(this); + mVolatilePBs.push_back(buf); + return buf.getPointer(); +} + +GFXVertexBuffer *GFXGLDevice::allocVertexBuffer( U32 numVerts, + const GFXVertexFormat *vertexFormat, + U32 vertSize, + GFXBufferType bufferType ) +{ + if(bufferType == GFXBufferTypeVolatile) + return findVolatileVBO(numVerts, vertexFormat, vertSize); + + GFXGLVertexBuffer* buf = new GFXGLVertexBuffer( GFX, numVerts, vertexFormat, vertSize, bufferType ); + buf->registerResourceWithDevice(this); + return buf; +} + +GFXPrimitiveBuffer *GFXGLDevice::allocPrimitiveBuffer( U32 numIndices, U32 numPrimitives, GFXBufferType bufferType ) +{ + if(bufferType == GFXBufferTypeVolatile) + return findVolatilePBO(numIndices, numPrimitives); + + GFXGLPrimitiveBuffer* buf = new GFXGLPrimitiveBuffer(GFX, numIndices, numPrimitives, bufferType); + buf->registerResourceWithDevice(this); + return buf; +} + +void GFXGLDevice::setVertexStream( U32 stream, GFXVertexBuffer *buffer ) +{ + AssertFatal( stream == 0, "GFXGLDevice::setVertexStream - We don't support multiple vertex streams!" ); + + // Reset the state the old VB required, then set the state the new VB requires. + if ( mCurrentVB ) + mCurrentVB->finish(); + + mCurrentVB = static_cast( buffer ); + if ( mCurrentVB ) + mCurrentVB->prepare(); +} + +void GFXGLDevice::setVertexStreamFrequency( U32 stream, U32 frequency ) +{ + // We don't support vertex stream frequency or mesh instancing in OGL yet. +} + +GFXCubemap* GFXGLDevice::createCubemap() +{ + GFXGLCubemap* cube = new GFXGLCubemap(); + cube->registerResourceWithDevice(this); + return cube; +}; + +void GFXGLDevice::endSceneInternal() +{ + // nothing to do for opengl + mCanCurrentlyRender = false; +} + +void GFXGLDevice::clear(U32 flags, ColorI color, F32 z, U32 stencil) +{ + // Make sure we have flushed our render target state. + _updateRenderTargets(); + + bool zwrite = true; + if (mCurrentGLStateBlock) + { + zwrite = mCurrentGLStateBlock->getDesc().zWriteEnable; + } + + glDepthMask(true); + ColorF c = color; + glClearColor(c.red, c.green, c.blue, c.alpha); + glClearDepth(z); + glClearStencil(stencil); + + GLbitfield clearflags = 0; + clearflags |= (flags & GFXClearTarget) ? GL_COLOR_BUFFER_BIT : 0; + clearflags |= (flags & GFXClearZBuffer) ? GL_DEPTH_BUFFER_BIT : 0; + clearflags |= (flags & GFXClearStencil) ? GL_STENCIL_BUFFER_BIT : 0; + + glClear(clearflags); + + if(!zwrite) + glDepthMask(false); +} + +// Given a primitive type and a number of primitives, return the number of indexes/vertexes used. +GLsizei GFXGLDevice::primCountToIndexCount(GFXPrimitiveType primType, U32 primitiveCount) +{ + switch (primType) + { + case GFXPointList : + return primitiveCount; + break; + case GFXLineList : + return primitiveCount * 2; + break; + case GFXLineStrip : + return primitiveCount + 1; + break; + case GFXTriangleList : + return primitiveCount * 3; + break; + case GFXTriangleStrip : + return 2 + primitiveCount; + break; + case GFXTriangleFan : + return 2 + primitiveCount; + break; + default: + AssertFatal(false, "GFXGLDevice::primCountToIndexCount - unrecognized prim type"); + break; + } + + return 0; +} + +inline void GFXGLDevice::preDrawPrimitive() +{ + if( mStateDirty ) + { + updateStates(); + } + + if(mCurrentShaderConstBuffer) + setShaderConstBufferInternal(mCurrentShaderConstBuffer); +} + +inline void GFXGLDevice::postDrawPrimitive(U32 primitiveCount) +{ + mDeviceStatistics.mDrawCalls++; + mDeviceStatistics.mPolyCount += primitiveCount; +} + +void GFXGLDevice::drawPrimitive( GFXPrimitiveType primType, U32 vertexStart, U32 primitiveCount ) +{ + preDrawPrimitive(); + + // There are some odd performance issues if a buffer is bound to GL_ELEMENT_ARRAY_BUFFER when glDrawArrays is called. Unbinding the buffer + // improves performance by 10%. + if(mCurrentPB) + mCurrentPB->finish(); + + glDrawArrays(GFXGLPrimType[primType], vertexStart, primCountToIndexCount(primType, primitiveCount)); + + if(mCurrentPB) + mCurrentPB->prepare(); + + postDrawPrimitive(primitiveCount); +} + +void GFXGLDevice::drawIndexedPrimitive( GFXPrimitiveType primType, + U32 startVertex, + U32 minIndex, + U32 numVerts, + U32 startIndex, + U32 primitiveCount ) +{ + AssertFatal( startVertex == 0, "GFXGLDevice::drawIndexedPrimitive() - Non-zero startVertex unsupported!" ); + + preDrawPrimitive(); + + U16* buf = (U16*)static_cast(mCurrentPrimitiveBuffer.getPointer())->getBuffer() + startIndex; + + glDrawElements(GFXGLPrimType[primType], primCountToIndexCount(primType, primitiveCount), GL_UNSIGNED_SHORT, buf); + + postDrawPrimitive(primitiveCount); +} + +void GFXGLDevice::setPB(GFXGLPrimitiveBuffer* pb) +{ + if(mCurrentPB) + mCurrentPB->finish(); + mCurrentPB = pb; +} + +void GFXGLDevice::setLightInternal(U32 lightStage, const GFXLightInfo light, bool lightEnable) +{ + if(!lightEnable) + { + glDisable(GL_LIGHT0 + lightStage); + return; + } + + if(light.mType == GFXLightInfo::Ambient) + { + AssertFatal(false, "Instead of setting an ambient light you should set the global ambient color."); + return; + } + + GLenum lightEnum = GL_LIGHT0 + lightStage; + glLightfv(lightEnum, GL_AMBIENT, (GLfloat*)&light.mAmbient); + glLightfv(lightEnum, GL_DIFFUSE, (GLfloat*)&light.mColor); + glLightfv(lightEnum, GL_SPECULAR, (GLfloat*)&light.mColor); + + F32 pos[4]; + + if(light.mType != GFXLightInfo::Vector) + { + dMemcpy(pos, &light.mPos, sizeof(light.mPos)); + pos[3] = 1.0; + } + else + { + dMemcpy(pos, &light.mDirection, sizeof(light.mDirection)); + pos[3] = 0.0; + } + // Harcoded attenuation + glLightf(lightEnum, GL_CONSTANT_ATTENUATION, 1.0f); + glLightf(lightEnum, GL_LINEAR_ATTENUATION, 0.1f); + glLightf(lightEnum, GL_QUADRATIC_ATTENUATION, 0.0f); + + glLightfv(lightEnum, GL_POSITION, (GLfloat*)&pos); + glEnable(lightEnum); +} + +void GFXGLDevice::setLightMaterialInternal(const GFXLightMaterial mat) +{ + // CodeReview - Setting these for front and back is unnecessary. We should consider + // checking what faces we're culling and setting this only for the unculled faces. + glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, (GLfloat*)&mat.ambient); + glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, (GLfloat*)&mat.diffuse); + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, (GLfloat*)&mat.specular); + glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, (GLfloat*)&mat.emissive); + glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, mat.shininess); +} + +void GFXGLDevice::setGlobalAmbientInternal(ColorF color) +{ + glLightModelfv(GL_LIGHT_MODEL_AMBIENT, (GLfloat*)&color); +} + +void GFXGLDevice::setTextureInternal(U32 textureUnit, const GFXTextureObject*texture) +{ + const GFXGLTextureObject *tex = static_cast(texture); + glActiveTexture(GL_TEXTURE0 + textureUnit); + if (tex) + { + // GFXGLTextureObject::bind also handles applying the current sampler state. + if(mActiveTextureType[textureUnit] != tex->getBinding() && mActiveTextureType[textureUnit] != GL_ZERO) + { + glBindTexture(mActiveTextureType[textureUnit], 0); + glDisable(mActiveTextureType[textureUnit]); + } + mActiveTextureType[textureUnit] = tex->getBinding(); + tex->bind(textureUnit); + } + else if(mActiveTextureType[textureUnit] != GL_ZERO) + { + glBindTexture(mActiveTextureType[textureUnit], 0); + glDisable(mActiveTextureType[textureUnit]); + mActiveTextureType[textureUnit] = GL_ZERO; + } + + glActiveTexture(GL_TEXTURE0); +} + +void GFXGLDevice::setCubemapInternal(U32 textureUnit, const GFXGLCubemap* texture) +{ + glActiveTexture(GL_TEXTURE0 + textureUnit); + if(texture) + { + if(mActiveTextureType[textureUnit] != GL_TEXTURE_CUBE_MAP && mActiveTextureType[textureUnit] != GL_ZERO) + { + glBindTexture(mActiveTextureType[textureUnit], 0); + glDisable(mActiveTextureType[textureUnit]); + } + mActiveTextureType[textureUnit] = GL_TEXTURE_CUBE_MAP; + texture->bind(textureUnit); + } + else if(mActiveTextureType[textureUnit] != GL_ZERO) + { + glBindTexture(mActiveTextureType[textureUnit], 0); + glDisable(mActiveTextureType[textureUnit]); + mActiveTextureType[textureUnit] = GL_ZERO; + } + + glActiveTexture(GL_TEXTURE0); +} + +void GFXGLDevice::setMatrix( GFXMatrixType mtype, const MatrixF &mat ) +{ + MatrixF modelview; + switch (mtype) + { + case GFXMatrixWorld : + { + glMatrixMode(GL_MODELVIEW); + m_mCurrentWorld = mat; + modelview = m_mCurrentWorld; + modelview *= m_mCurrentView; + modelview.transpose(); + glLoadMatrixf((F32*) modelview); + } + break; + case GFXMatrixView : + { + glMatrixMode(GL_MODELVIEW); + m_mCurrentView = mat; + modelview = m_mCurrentView; + modelview *= m_mCurrentWorld; + modelview.transpose(); + glLoadMatrixf((F32*) modelview); + } + break; + case GFXMatrixProjection : + { + glMatrixMode(GL_PROJECTION); + MatrixF t(mat); + t.transpose(); + glLoadMatrixf((F32*) t); + glMatrixMode(GL_MODELVIEW); + } + break; + // CodeReview - Add support for texture transform matrix types + default: + AssertFatal(false, "GFXGLDevice::setMatrix - Unknown matrix mode!"); + return; + } +} + +void GFXGLDevice::setClipRect( const RectI &inRect ) +{ + AssertFatal(mCurrentRT.isValid(), "GFXGLDevice::setClipRect - must have a render target set to do any rendering operations!"); + + // Clip the rect against the renderable size. + Point2I size = mCurrentRT->getSize(); + RectI maxRect(Point2I(0,0), size); + mClip = inRect; + mClip.intersect(maxRect); + + // Create projection matrix. See http://www.opengl.org/documentation/specs/man_pages/hardcopy/GL/html/gl/ortho.html + const F32 left = mClip.point.x; + const F32 right = mClip.point.x + mClip.extent.x; + const F32 bottom = mClip.extent.y; + const F32 top = 0.0f; + const F32 near = 0.0f; + const F32 far = 1.0f; + + const F32 tx = -(right + left)/(right - left); + const F32 ty = -(top + bottom)/(top - bottom); + const F32 tz = -(far + near)/(far - near); + + static Point4F pt; + pt.set(2.0f / (right - left), 0.0f, 0.0f, 0.0f); + mProjectionMatrix.setColumn(0, pt); + + pt.set(0.0f, 2.0f/(top - bottom), 0.0f, 0.0f); + mProjectionMatrix.setColumn(1, pt); + + pt.set(0.0f, 0.0f, -2.0f/(far - near), 0.0f); + mProjectionMatrix.setColumn(2, pt); + + pt.set(tx, ty, tz, 1.0f); + mProjectionMatrix.setColumn(3, pt); + + // Translate projection matrix. + static MatrixF translate(true); + pt.set(0.0f, -mClip.point.y, 0.0f, 1.0f); + translate.setColumn(3, pt); + + mProjectionMatrix *= translate; + + setMatrix(GFXMatrixProjection, mProjectionMatrix); + + MatrixF mTempMatrix(true); + setViewMatrix( mTempMatrix ); + setWorldMatrix( mTempMatrix ); + + // Set the viewport to the clip rect (with y flip) + RectI viewport(mClip.point.x, size.y - (mClip.point.y + mClip.extent.y), mClip.extent.x, mClip.extent.y); + setViewport(viewport); +} + +/// Creates a state block object based on the desc passed in. This object +/// represents an immutable state. +GFXStateBlockRef GFXGLDevice::createStateBlockInternal(const GFXStateBlockDesc& desc) +{ + return GFXStateBlockRef(new GFXGLStateBlock(desc)); +} + +/// Activates a stateblock +void GFXGLDevice::setStateBlockInternal(GFXStateBlock* block, bool force) +{ + AssertFatal(dynamic_cast(block), "GFXGLDevice::setStateBlockInternal - Incorrect stateblock type for this device!"); + GFXGLStateBlock* glBlock = static_cast(block); + GFXGLStateBlock* glCurrent = static_cast(mCurrentStateBlock.getPointer()); + if (force) + glCurrent = NULL; + + glBlock->activate(glCurrent); // Doesn't use current yet. + mCurrentGLStateBlock = glBlock; +} + +//------------------------------------------------------------------------------ + +GFXTextureTarget * GFXGLDevice::allocRenderToTextureTarget() +{ + GFXGLTextureTarget *targ = new GFXGLTextureTarget(); + targ->registerResourceWithDevice(this); + return targ; +} + +GFXFence * GFXGLDevice::createFence() +{ + GFXFence* fence = _createPlatformSpecificFence(); + if(!fence) + fence = new GFXGeneralFence( this ); + + fence->registerResourceWithDevice(this); + return fence; +} + +GFXOcclusionQuery* GFXGLDevice::createOcclusionQuery() +{ + GFXOcclusionQuery *query = new GFXGLOcclusionQuery( this ); + query->registerResourceWithDevice(this); + return query; +} + +void GFXGLDevice::setupGenericShaders( GenericShaderType type ) +{ + TORQUE_UNUSED(type); + // We have FF support, use that. + disableShaders(); +} + +GFXShader* GFXGLDevice::createShader() +{ + GFXGLShader* shader = new GFXGLShader(); + shader->registerResourceWithDevice( this ); + return shader; +} + +void GFXGLDevice::setShader( GFXShader *shader ) +{ + if ( shader ) + { + GFXGLShader *glShader = static_cast( shader ); + glShader->useProgram(); + } + else + glUseProgram(0); +} + +void GFXGLDevice::disableShaders() +{ + setShader(NULL); + setShaderConstBuffer( NULL ); +} + +void GFXGLDevice::setShaderConstBufferInternal(GFXShaderConstBuffer* buffer) +{ + static_cast(buffer)->activate(); +} + +U32 GFXGLDevice::getNumSamplers() const +{ + return mPixelShaderVersion > 0.001f ? mMaxShaderTextures : mMaxFFTextures; +} + +U32 GFXGLDevice::getNumRenderTargets() const +{ + return 1; +} + +void GFXGLDevice::_updateRenderTargets() +{ + if ( mRTDirty || mCurrentRT->isPendingState() ) + { + if ( mRTDeactivate ) + { + mRTDeactivate->deactivate(); + mRTDeactivate = NULL; + } + + // NOTE: The render target changes is not really accurate + // as the GFXTextureTarget supports MRT internally. So when + // we activate a GFXTarget it could result in multiple calls + // to SetRenderTarget on the actual device. + mDeviceStatistics.mRenderTargetChanges++; + + GFXGLTextureTarget *tex = dynamic_cast( mCurrentRT.getPointer() ); + if ( tex ) + { + tex->applyState(); + tex->makeActive(); + } + else + { + GFXGLWindowTarget *win = dynamic_cast( mCurrentRT.getPointer() ); + AssertFatal( win != NULL, + "GFXGLDevice::_updateRenderTargets() - invalid target subclass passed!" ); + + win->makeActive(); + + if( win->mContext != static_cast(GFX)->mContext ) + { + mRTDirty = false; + GFX->updateStates(true); + } + } + + mRTDirty = false; + } + + if ( mViewportDirty ) + { + glViewport( mViewport.point.x, mViewport.point.y, mViewport.extent.x, mViewport.extent.y ); + mViewportDirty = false; + } +} + +GFXFormat GFXGLDevice::selectSupportedFormat( GFXTextureProfile* profile, + const Vector& formats, + bool texture, + bool mustblend, + bool mustfilter ) +{ + for(U32 i = 0; i < formats.size(); i++) + { + // Single channel textures are not supported by FBOs. + if(profile->testFlag(GFXTextureProfile::RenderTarget) && (formats[i] == GFXFormatA8 || formats[i] == GFXFormatL8 || formats[i] == GFXFormatL16)) + continue; + if(GFXGLTextureInternalFormat[formats[i]] == GL_ZERO) + continue; + + return formats[i]; + } + + return GFXFormatR8G8B8A8; +} + +// +// Register this device with GFXInit +// +class GFXGLRegisterDevice +{ +public: + GFXGLRegisterDevice() + { + GFXInit::getRegisterDeviceSignal().notify(&GFXGLDevice::enumerateAdapters); + } +}; + +static GFXGLRegisterDevice pGLRegisterDevice; + +ConsoleFunction(cycleResources, void, 1, 1, "") +{ + static_cast(GFX)->zombify(); + static_cast(GFX)->resurrect(); +} diff --git a/Engine/source/gfx/gl/gfxGLDevice.h b/Engine/source/gfx/gl/gfxGLDevice.h new file mode 100644 index 000000000..d7b473cf4 --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLDevice.h @@ -0,0 +1,237 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXGLDEVICE_H_ +#define _GFXGLDEVICE_H_ + +#include "platform/platform.h" + +#include "gfx/gfxDevice.h" +#include "gfx/gfxInit.h" + +#ifndef GL_GGL_H +#include "gfx/gl/ggl/ggl.h" +#endif + +#include "windowManager/platformWindow.h" +#include "gfx/gfxFence.h" +#include "gfx/gfxResource.h" +#include "gfx/gl/gfxGLStateBlock.h" + +class GFXGLVertexBuffer; +class GFXGLPrimitiveBuffer; +class GFXGLTextureTarget; +class GFXGLCubemap; + +class GFXGLDevice : public GFXDevice +{ +public: + void zombify(); + void resurrect(); + GFXGLDevice(U32 adapterIndex); + virtual ~GFXGLDevice(); + + static void enumerateAdapters( Vector &adapterList ); + static GFXDevice *createInstance( U32 adapterIndex ); + + virtual void init( const GFXVideoMode &mode, PlatformWindow *window = NULL ); + + virtual void activate() { } + virtual void deactivate() { } + virtual GFXAdapterType getAdapterType() { return OpenGL; } + + virtual void enterDebugEvent(ColorI color, const char *name) { } + virtual void leaveDebugEvent() { } + virtual void setDebugMarker(ColorI color, const char *name) { } + + virtual void enumerateVideoModes(); + + virtual U32 getTotalVideoMemory(); + + virtual GFXCubemap * createCubemap(); + + virtual F32 getFillConventionOffset() const { return 0.0f; } + + + ///@} + + /// @name Render Target functions + /// @{ + + /// + virtual GFXTextureTarget *allocRenderToTextureTarget(); + virtual GFXWindowTarget *allocWindowTarget(PlatformWindow *window); + virtual void _updateRenderTargets(); + + ///@} + + /// @name Shader functions + /// @{ + virtual F32 getPixelShaderVersion() const { return mPixelShaderVersion; } + virtual void setPixelShaderVersion( F32 version ) { mPixelShaderVersion = version; } + + virtual void setShader(GFXShader* shd); + virtual void disableShaders(); ///< Equivalent to setShader(NULL) + + /// @attention GL cannot check if the given format supports blending or filtering! + virtual GFXFormat selectSupportedFormat(GFXTextureProfile *profile, + const Vector &formats, bool texture, bool mustblend, bool mustfilter); + + /// Returns the number of texture samplers that can be used in a shader rendering pass + virtual U32 getNumSamplers() const; + + /// Returns the number of simultaneous render targets supported by the device. + virtual U32 getNumRenderTargets() const; + + virtual GFXShader* createShader(); + + virtual void clear( U32 flags, ColorI color, F32 z, U32 stencil ); + virtual bool beginSceneInternal(); + virtual void endSceneInternal(); + + virtual void drawPrimitive( GFXPrimitiveType primType, U32 vertexStart, U32 primitiveCount ); + + virtual void drawIndexedPrimitive( GFXPrimitiveType primType, + U32 startVertex, + U32 minIndex, + U32 numVerts, + U32 startIndex, + U32 primitiveCount ); + + virtual void setClipRect( const RectI &rect ); + virtual const RectI &getClipRect() const { return mClip; } + + virtual void preDestroy() { Parent::preDestroy(); } + + virtual U32 getMaxDynamicVerts() { return MAX_DYNAMIC_VERTS; } + virtual U32 getMaxDynamicIndices() { return MAX_DYNAMIC_INDICES; } + + GFXFence *createFence(); + + GFXOcclusionQuery* createOcclusionQuery(); + + GFXGLStateBlockRef getCurrentStateBlock() { return mCurrentGLStateBlock; } + + virtual void setupGenericShaders( GenericShaderType type = GSColor ); + + /// + bool supportsAnisotropic() const { return mSupportsAnisotropic; } + +protected: + /// Called by GFXDevice to create a device specific stateblock + virtual GFXStateBlockRef createStateBlockInternal(const GFXStateBlockDesc& desc); + /// Called by GFXDevice to actually set a stateblock. + virtual void setStateBlockInternal(GFXStateBlock* block, bool force); + + /// Called by base GFXDevice to actually set a const buffer + virtual void setShaderConstBufferInternal(GFXShaderConstBuffer* buffer); + + virtual void setTextureInternal(U32 textureUnit, const GFXTextureObject*texture); + virtual void setCubemapInternal(U32 cubemap, const GFXGLCubemap* texture); + + virtual void setLightInternal(U32 lightStage, const GFXLightInfo light, bool lightEnable); + virtual void setLightMaterialInternal(const GFXLightMaterial mat); + virtual void setGlobalAmbientInternal(ColorF color); + + /// @name State Initalization. + /// @{ + + /// State initalization. This MUST BE CALLED in setVideoMode after the device + /// is created. + virtual void initStates() { } + + virtual void setMatrix( GFXMatrixType mtype, const MatrixF &mat ); + + virtual GFXVertexBuffer *allocVertexBuffer( U32 numVerts, + const GFXVertexFormat *vertexFormat, + U32 vertSize, + GFXBufferType bufferType ); + virtual GFXPrimitiveBuffer *allocPrimitiveBuffer( U32 numIndices, U32 numPrimitives, GFXBufferType bufferType ); + + // NOTE: The GL device doesn't need a vertex declaration at + // this time, but we need to return something to keep the system + // from retrying to allocate one on every call. + virtual GFXVertexDecl* allocVertexDecl( const GFXVertexFormat *vertexFormat ) + { + static GFXVertexDecl decl; + return &decl; + } + + virtual void setVertexDecl( const GFXVertexDecl *decl ) { } + + virtual void setVertexStream( U32 stream, GFXVertexBuffer *buffer ); + virtual void setVertexStreamFrequency( U32 stream, U32 frequency ); + +private: + typedef GFXDevice Parent; + + friend class GFXGLTextureObject; + friend class GFXGLCubemap; + friend class GFXGLWindowTarget; + friend class GFXGLPrimitiveBuffer; + friend class GFXGLVertexBuffer; + + static GFXAdapter::CreateDeviceInstanceDelegate mCreateDeviceInstance; + + U32 mAdapterIndex; + + StrongRefPtr mCurrentVB; + StrongRefPtr mCurrentPB; + + /// Since GL does not have separate world and view matrices we need to track them + MatrixF m_mCurrentWorld; + MatrixF m_mCurrentView; + + void* mContext; + void* mPixelFormat; + + F32 mPixelShaderVersion; + + bool mSupportsAnisotropic; + + U32 mMaxShaderTextures; + U32 mMaxFFTextures; + + RectI mClip; + + GFXGLStateBlockRef mCurrentGLStateBlock; + + GLenum mActiveTextureType[TEXTURE_STAGE_COUNT]; + + Vector< StrongRefPtr > mVolatileVBs; ///< Pool of existing volatile VBs so we can reuse previously created ones + Vector< StrongRefPtr > mVolatilePBs; ///< Pool of existing volatile PBs so we can reuse previously created ones + + GLsizei primCountToIndexCount(GFXPrimitiveType primType, U32 primitiveCount); + void preDrawPrimitive(); + void postDrawPrimitive(U32 primitiveCount); + + GFXVertexBuffer* findVolatileVBO(U32 numVerts, const GFXVertexFormat *vertexFormat, U32 vertSize); ///< Returns an existing volatile VB which has >= numVerts and the same vert flags/size, or creates a new VB if necessary + GFXPrimitiveBuffer* findVolatilePBO(U32 numIndices, U32 numPrimitives); ///< Returns an existing volatile PB which has >= numIndices, or creates a new PB if necessary + + void initGLState(); ///< Guaranteed to be called after all extensions have been loaded, use to init card profiler, shader version, max samplers, etc. + + GFXFence* _createPlatformSpecificFence(); ///< If our platform (e.g. OS X) supports a fence extenstion (e.g. GL_APPLE_fence) this will create one, otherwise returns NULL + + void setPB(GFXGLPrimitiveBuffer* pb); ///< Sets mCurrentPB +}; + +#endif diff --git a/Engine/source/gfx/gl/gfxGLDevice.mac.mm b/Engine/source/gfx/gl/gfxGLDevice.mac.mm new file mode 100644 index 000000000..f0d12f08a --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLDevice.mac.mm @@ -0,0 +1,357 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// Don't include Apple's GL header +#define __gl_h_ +// Include our GL header before Apple headers. +#include "gfx/gl/ggl/ggl.h" + +#include "platform/tmm_off.h" +#include +#include +#include "gfx/gl/gfxGLDevice.h" +#include "platform/tmm_on.h" + +#include "gfx/gl/gfxGLTextureTarget.h" +#include "gfx/gl/gfxGLCardProfiler.h" +#include "gfx/gl/gfxGLAppleFence.h" +#include "gfx/gl/gfxGLWindowTarget.h" +#include "platformMac/macGLUtils.h" +#include "windowManager/mac/macWindow.h" + +extern void loadGLCore(); +extern void loadGLExtensions(void* context); + +static String _getRendererForDisplay(CGDirectDisplayID display) +{ + Vector attributes = _createStandardPixelFormatAttributesForDisplay(display); + + NSOpenGLPixelFormat* fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes.address()]; + AssertFatal(fmt, "_getRendererForDisplay - Unable to create a pixel format object"); + + NSOpenGLContext* ctx = [[NSOpenGLContext alloc] initWithFormat:fmt shareContext:nil]; + AssertFatal(ctx, "_getRendererForDisplay - Unable to create an OpenGL context"); + + // Save the current context, just in case + NSOpenGLContext* currCtx = [NSOpenGLContext currentContext]; + [ctx makeCurrentContext]; + + // CodeReview [ags 12/19/07] This is a hack. We should call loadGLCore somewhere else, before we reach here. + // On Macs we can safely assume access to the OpenGL framework at anytime, perhaps this should go in main()? + loadGLCore(); + + // get the renderer string + String ret((const char*)glGetString(GL_RENDERER)); + + // Restore our old context, release the context and pixel format. + [currCtx makeCurrentContext]; + [ctx release]; + [fmt release]; + return ret; +} + +static void _createInitialContextAndFormat(void* &ctx, void* &fmt) +{ + AssertFatal(!fmt && !ctx, "_createInitialContextAndFormat - Already created initial context and format"); + + fmt = _createStandardPixelFormat(); + AssertFatal(fmt, "_createInitialContextAndFormat - Unable to create an OpenGL pixel format"); + + ctx = [[NSOpenGLContext alloc] initWithFormat: (NSOpenGLPixelFormat*)fmt shareContext: nil]; + AssertFatal(ctx, "_createInitialContextAndFormat - Unable to create an OpenGL context"); +} + +static NSOpenGLContext* _createContextForWindow(PlatformWindow *window, void* &context, void* &pixelFormat) +{ + NSOpenGLView* view = (NSOpenGLView*)window->getPlatformDrawable(); + AssertFatal([view isKindOfClass:[NSOpenGLView class]], avar("_createContextForWindow - Supplied a %s instead of a NSOpenGLView", [[view className] UTF8String])); + + NSOpenGLContext* ctx = NULL; + if(!context || !pixelFormat) + { + // Create the initial opengl context that the device and the first window will hold. + _createInitialContextAndFormat(context, pixelFormat); + ctx = (NSOpenGLContext*)context; + } + else + { + // Create a context which shares its resources with the device's initial context + ctx = [[NSOpenGLContext alloc] initWithFormat: (NSOpenGLPixelFormat*)pixelFormat shareContext: (NSOpenGLContext*)context]; + AssertFatal(ctx, "Unable to create a shared OpenGL context"); + } + + [view setPixelFormat: (NSOpenGLPixelFormat*)pixelFormat]; + [view setOpenGLContext: ctx]; + + return ctx; +} + +void GFXGLDevice::init( const GFXVideoMode &mode, PlatformWindow *window ) +{ + if(mInitialized) + return; + + NSOpenGLContext* ctx = _createContextForWindow(window, mContext, mPixelFormat); + [ctx makeCurrentContext]; + + loadGLCore(); + loadGLExtensions(ctx); + + initGLState(); + + mInitialized = true; + deviceInited(); +} + +void GFXGLDevice::enumerateAdapters( Vector &adapterList ) +{ + GFXAdapter *toAdd; + + Vector videoModes; + + CGDirectDisplayID display = CGMainDisplayID(); + + // Enumerate all available resolutions: + NSArray* modeArray = (NSArray*)CGDisplayAvailableModes(display); + + GFXVideoMode vmAdd; + NSEnumerator* enumerator = [modeArray objectEnumerator]; + NSDictionary* mode; + while((mode = [enumerator nextObject])) + { + vmAdd.resolution.x = [[mode valueForKey:@"Width"] intValue]; + vmAdd.resolution.y = [[mode valueForKey:@"Height"] intValue]; + vmAdd.bitDepth = [[mode valueForKey:@"BitsPerPixel"] intValue]; + vmAdd.refreshRate = [[mode valueForKey:@"RefreshRate"] intValue]; + + vmAdd.fullScreen = false; + + // skip if mode claims to be 8bpp + if( vmAdd.bitDepth == 8 ) + continue; + + // Only add this resolution if it is not already in the list: + bool alreadyInList = false; + for(Vector::iterator i = videoModes.begin(); i != videoModes.end(); i++) + { + if(vmAdd == *i) + { + alreadyInList = true; + break; + } + } + if( !alreadyInList ) + { + videoModes.push_back( vmAdd ); + } + } + + + // Get number of displays + CGDisplayCount dispCnt; + CGGetActiveDisplayList(0, NULL, &dispCnt); + + // Take advantage of GNU-C + CGDirectDisplayID displays[dispCnt]; + + CGGetActiveDisplayList(dispCnt, displays, &dispCnt); + for(U32 i = 0; i < dispCnt; i++) + { + toAdd = new GFXAdapter(); + toAdd->mType = OpenGL; + toAdd->mIndex = (U32)displays[i]; + toAdd->mCreateDeviceInstanceDelegate = mCreateDeviceInstance; + String renderer = _getRendererForDisplay(displays[i]); + AssertFatal(dStrlen(renderer.c_str()) < GFXAdapter::MaxAdapterNameLen, "GFXGLDevice::enumerateAdapter - renderer name too long, increae the size of GFXAdapter::MaxAdapterNameLen (or use String!)"); + dStrncpy(toAdd->mName, renderer.c_str(), GFXAdapter::MaxAdapterNameLen); + adapterList.push_back(toAdd); + + for (S32 j = videoModes.size() - 1 ; j >= 0 ; j--) + toAdd->mAvailableModes.push_back(videoModes[j]); + } +} + +void GFXGLDevice::enumerateVideoModes() +{ + mVideoModes.clear(); + + CGDirectDisplayID display = CGMainDisplayID(); + + // Enumerate all available resolutions: + NSArray* modeArray = (NSArray*)CGDisplayAvailableModes(display); + + GFXVideoMode toAdd; + NSEnumerator* enumerator = [modeArray objectEnumerator]; + NSDictionary* mode; + while((mode = [enumerator nextObject])) + { + toAdd.resolution.x = [[mode valueForKey:@"Width"] intValue]; + toAdd.resolution.y = [[mode valueForKey:@"Height"] intValue]; + toAdd.bitDepth = [[mode valueForKey:@"BitsPerPixel"] intValue]; + toAdd.refreshRate = [[mode valueForKey:@"RefreshRate"] intValue]; + + toAdd.fullScreen = false; + + // skip if mode claims to be 8bpp + if( toAdd.bitDepth == 8 ) + continue; + + // Only add this resolution if it is not already in the list: + bool alreadyInList = false; + for(Vector::iterator i = mVideoModes.begin(); i != mVideoModes.end(); i++) + { + if(toAdd == *i) + { + alreadyInList = true; + break; + } + } + if( !alreadyInList ) + { + mVideoModes.push_back( toAdd ); + } + } +} + +bool GFXGLDevice::beginSceneInternal() +{ + // Nothing to do here for GL. + mCanCurrentlyRender = true; + return true; +} + +U32 GFXGLDevice::getTotalVideoMemory() +{ + // Convert our adapterIndex (i.e. our CGDirectDisplayID) into an OpenGL display mask + GLuint display = CGDisplayIDToOpenGLDisplayMask((CGDirectDisplayID)mAdapterIndex); + CGLRendererInfoObj rend; +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 + GLint nrend; +#else + long int nrend; +#endif + CGLQueryRendererInfo(display, &rend, &nrend); + if(!nrend) + return 0; + +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 + GLint vidMem; +#else + long int vidMem; +#endif + CGLDescribeRenderer(rend, 0, kCGLRPVideoMemory, &vidMem); + CGLDestroyRendererInfo(rend); + + // convert bytes to MB + vidMem /= (1024 * 1024); + + return vidMem; +} + +//----------------------------------------------------------------------------- +GFXWindowTarget *GFXGLDevice::allocWindowTarget(PlatformWindow *window) +{ + void *ctx = NULL; + + // Init if needed, or create a new context + if(!mInitialized) + init(window->getVideoMode(), window); + else + ctx = _createContextForWindow(window, mContext, mPixelFormat); + + // Allocate the wintarget and create a new context. + GFXGLWindowTarget *gwt = new GFXGLWindowTarget(window, this); + gwt->mContext = ctx ? ctx : mContext; + + // And return... + return gwt; +} + +GFXFence* GFXGLDevice::_createPlatformSpecificFence() +{ + if(!mCardProfiler->queryProfile("GL::APPLE::suppFence")) + return NULL; + + return new GFXGLAppleFence(this); +} + +void GFXGLWindowTarget::makeActive() +{ + // If we're supposed to be running fullscreen, but haven't yet set up for it, + // do it now. + + if( !mFullscreenContext && mWindow->getVideoMode().fullScreen ) + { + static_cast< GFXGLDevice* >( mDevice )->zombify(); + _setupNewMode(); + } + + mFullscreenContext ? [(NSOpenGLContext*)mFullscreenContext makeCurrentContext] : [(NSOpenGLContext*)mContext makeCurrentContext]; +} + +bool GFXGLWindowTarget::present() +{ + GFX->updateStates(); + mFullscreenContext ? [(NSOpenGLContext*)mFullscreenContext flushBuffer] : [(NSOpenGLContext*)mContext flushBuffer]; + return true; +} + +void GFXGLWindowTarget::_teardownCurrentMode() +{ + GFX->setActiveRenderTarget(this); + static_cast(mDevice)->zombify(); + if(mFullscreenContext) + { + [NSOpenGLContext clearCurrentContext]; + [(NSOpenGLContext*)mFullscreenContext clearDrawable]; + } +} + +void GFXGLWindowTarget::_setupNewMode() +{ + if(mWindow->getVideoMode().fullScreen && !mFullscreenContext) + { + // We have to create a fullscreen context. + Vector attributes = _beginPixelFormatAttributesForDisplay(static_cast(mWindow)->getDisplay()); + _addColorAlphaDepthStencilAttributes(attributes, 24, 8, 24, 8); + _addFullscreenAttributes(attributes); + _endAttributeList(attributes); + + NSOpenGLPixelFormat* fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes.address()]; + mFullscreenContext = [[NSOpenGLContext alloc] initWithFormat:fmt shareContext:nil]; + [fmt release]; + [(NSOpenGLContext*)mFullscreenContext setFullScreen]; + [(NSOpenGLContext*)mFullscreenContext makeCurrentContext]; + // Restore resources in new context + static_cast(mDevice)->resurrect(); + GFX->updateStates(true); + } + else if(!mWindow->getVideoMode().fullScreen && mFullscreenContext) + { + [(NSOpenGLContext*)mFullscreenContext release]; + mFullscreenContext = NULL; + [(NSOpenGLContext*)mContext makeCurrentContext]; + GFX->clear(GFXClearTarget | GFXClearZBuffer | GFXClearStencil, ColorI(0, 0, 0), 1.0f, 0); + static_cast(mDevice)->resurrect(); + GFX->updateStates(true); + } +} diff --git a/Engine/source/gfx/gl/gfxGLDevice.win.cpp b/Engine/source/gfx/gl/gfxGLDevice.win.cpp new file mode 100644 index 000000000..d68b9ea2e --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLDevice.win.cpp @@ -0,0 +1,403 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformWin32/platformWin32.h" +#include "gfx/gfxCubemap.h" +#include "gfx/screenshot.h" + +#include "gfx/GL/gfxGLDevice.h" +#include "gfx/GL/gfxGLEnumTranslate.h" +#include "gfx/GL/gfxGLVertexBuffer.h" +#include "gfx/GL/gfxGLPrimitiveBuffer.h" +#include "gfx/gl/gfxGLTextureTarget.h" +#include "gfx/GL/gfxGLWindowTarget.h" +#include "gfx/GL/gfxGLTextureManager.h" +#include "gfx/GL/gfxGLTextureObject.h" +#include "gfx/GL/gfxGLCubemap.h" +#include "gfx/GL/gfxGLCardProfiler.h" +#include "windowManager/win32/win32Window.h" +#include "ggl/Win32/wgl.h" + +#define GETHWND(x) static_cast(x)->getHWND() + +// yonked from winWindow.cc +void CreatePixelFormat( PIXELFORMATDESCRIPTOR *pPFD, S32 colorBits, S32 depthBits, S32 stencilBits, bool stereo ) +{ + PIXELFORMATDESCRIPTOR src = + { + sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd + 1, // version number + PFD_DRAW_TO_WINDOW | // support window + PFD_SUPPORT_OPENGL | // support OpenGL + PFD_DOUBLEBUFFER, // double buffered + PFD_TYPE_RGBA, // RGBA type + colorBits, // color depth + 0, 0, 0, 0, 0, 0, // color bits ignored + 0, // no alpha buffer + 0, // shift bit ignored + 0, // no accumulation buffer + 0, 0, 0, 0, // accum bits ignored + depthBits, // z-buffer + stencilBits, // stencil buffer + 0, // no auxiliary buffer + PFD_MAIN_PLANE, // main layer + 0, // reserved + 0, 0, 0 // layer masks ignored + }; + + if ( stereo ) + { + //ri.Printf( PRINT_ALL, "...attempting to use stereo\n" ); + src.dwFlags |= PFD_STEREO; + //glConfig.stereoEnabled = true; + } + else + { + //glConfig.stereoEnabled = qfalse; + } + *pPFD = src; +} + +extern void loadGLCore(); +extern void loadGLExtensions(void* context); + +void GFXGLDevice::enumerateAdapters( Vector &adapterList ) +{ +// GL_ERROR_CHECK(); + WNDCLASS windowclass; + dMemset( &windowclass, 0, sizeof( WNDCLASS ) ); + + windowclass.lpszClassName = L"GFX-OpenGL"; + windowclass.style = CS_OWNDC; + windowclass.lpfnWndProc = DefWindowProc; + windowclass.hInstance = winState.appInstance; + + if( !RegisterClass( &windowclass ) ) + AssertFatal( false, "Failed to register the window class for the GL test window." ); + + // Now create a window + HWND hwnd = CreateWindow( L"GFX-OpenGL", L"", WS_POPUP, 0, 0, 640, 480, + NULL, NULL, winState.appInstance, NULL ); + AssertFatal( hwnd != NULL, "Failed to create the window for the GL test window." ); + + // Create a device context + HDC tempDC = GetDC( hwnd ); + AssertFatal( tempDC != NULL, "Failed to create device context" ); + + // Create pixel format descriptor... + PIXELFORMATDESCRIPTOR pfd; + CreatePixelFormat( &pfd, 16, 16, 8, false ); // 16 bit color, 16 bit depth, 8 bit stencil...everyone can do this + if( !SetPixelFormat( tempDC, ChoosePixelFormat( tempDC, &pfd ), &pfd ) ) + AssertFatal( false, "I don't know who's responcible for this, but I want caught..." ); + + // Create a rendering context! + HGLRC tempGLRC = wglCreateContext( tempDC ); + if( !wglMakeCurrent( tempDC, tempGLRC ) ) + AssertFatal( false, "I want them caught and killed." ); + + // Add the GL renderer + loadGLCore(); + loadGLExtensions(tempDC); + + GFXAdapter *toAdd = new GFXAdapter; + toAdd->mIndex = 0; + + const char* renderer = (const char*) glGetString( GL_RENDERER ); + AssertFatal( renderer != NULL, "GL_RENDERER returned NULL!" ); + + if (renderer) + { + dStrcpy(toAdd->mName, renderer); + dStrncat(toAdd->mName, " OpenGL", GFXAdapter::MaxAdapterNameLen); + } + else + dStrcpy(toAdd->mName, "OpenGL"); + + toAdd->mType = OpenGL; + toAdd->mShaderModel = 0.f; + toAdd->mCreateDeviceInstanceDelegate = mCreateDeviceInstance; + + // Enumerate all available resolutions: + DEVMODE devMode; + U32 modeNum = 0; + U32 stillGoing = true; + while ( stillGoing ) + { + dMemset( &devMode, 0, sizeof( devMode ) ); + devMode.dmSize = sizeof( devMode ); + + stillGoing = EnumDisplaySettings( NULL, modeNum++, &devMode ); + + if (( devMode.dmPelsWidth >= 480) && (devMode.dmPelsHeight >= 360 ) + && ( devMode.dmBitsPerPel == 16 || devMode.dmBitsPerPel == 32 )) + { + GFXVideoMode vmAdd; + + vmAdd.bitDepth = devMode.dmBitsPerPel; + vmAdd.fullScreen = true; + vmAdd.refreshRate = devMode.dmDisplayFrequency; + vmAdd.resolution.x = devMode.dmPelsWidth; + vmAdd.resolution.y = devMode.dmPelsHeight; + + // Only add this resolution if it is not already in the list: + bool alreadyInList = false; + for (Vector::iterator i = toAdd->mAvailableModes.begin(); i != toAdd->mAvailableModes.end(); i++) + { + if (vmAdd == *i) + { + alreadyInList = true; + break; + } + } + + if(alreadyInList) + continue; + + toAdd->mAvailableModes.push_back( vmAdd ); + } + } + + // Add to the list of available adapters. + adapterList.push_back(toAdd); + + // Cleanup our window + wglMakeCurrent(NULL, NULL); + wglDeleteContext(tempGLRC); + ReleaseDC(hwnd, tempDC); + DestroyWindow(hwnd); + UnregisterClass(L"GFX-OpenGL", winState.appInstance); +} + +void GFXGLDevice::enumerateVideoModes() +{ + mVideoModes.clear(); + + // Enumerate all available resolutions: + DEVMODE devMode; + U32 modeNum = 0; + U32 stillGoing = true; + while ( stillGoing ) + { + dMemset( &devMode, 0, sizeof( devMode ) ); + devMode.dmSize = sizeof( devMode ); + + stillGoing = EnumDisplaySettings( NULL, modeNum++, &devMode ); + + if (( devMode.dmPelsWidth >= 480) && (devMode.dmPelsHeight >= 360 ) + && ( devMode.dmBitsPerPel == 16 || devMode.dmBitsPerPel == 32 )) + //( smCanSwitchBitDepth || devMode.dmBitsPerPel == winState.desktopBitsPixel ) ) + { + GFXVideoMode toAdd; + + toAdd.bitDepth = devMode.dmBitsPerPel; + toAdd.fullScreen = false; + toAdd.refreshRate = devMode.dmDisplayFrequency; + toAdd.resolution.x = devMode.dmPelsWidth; + toAdd.resolution.y = devMode.dmPelsHeight; + + // Only add this resolution if it is not already in the list: + bool alreadyInList = false; + for (Vector::iterator i = mVideoModes.begin(); i != mVideoModes.end(); i++) + { + if (toAdd == *i) + { + alreadyInList = true; + break; + } + } + if ( !alreadyInList ) + { + //Con::printf("Resolution: %dx%d %d bpp %d Refresh rate: %d", toAdd.resolution.x, toAdd.resolution.y, toAdd.bitDepth, toAdd.refreshRate); + mVideoModes.push_back( toAdd ); + } + } + } +} + +void GFXGLDevice::init( const GFXVideoMode &mode, PlatformWindow *window ) +{ + AssertFatal(window, "GFXGLDevice::init - no window specified, can't init device without a window!"); + AssertFatal(dynamic_cast(window), "Invalid window class type!"); + HWND hwnd = GETHWND(window); + + RECT rect; + GetClientRect(hwnd, &rect); + + Point2I resolution; + resolution.x = rect.right - rect.left; + resolution.y = rect.bottom - rect.top; + + // Create a device context + HDC hdcGL = GetDC( hwnd ); + AssertFatal( hdcGL != NULL, "Failed to create device context" ); + + // Create pixel format descriptor... + PIXELFORMATDESCRIPTOR pfd; + CreatePixelFormat( &pfd, 16, 16, 8, false ); // 16 bit color, 16 bit depth, 8 bit stencil...everyone can do this + if( !SetPixelFormat( hdcGL, ChoosePixelFormat( hdcGL, &pfd ), &pfd ) ) + { + AssertFatal( false, "GFXGLDevice::init - cannot get the one and only pixel format we check for." ); + } + + // Create a rendering context! + mContext = wglCreateContext( hdcGL ); + if( !wglMakeCurrent( hdcGL, (HGLRC)mContext ) ) + AssertFatal( false , "GFXGLDevice::init - cannot make our context current. Or maybe we can't create it." ); + + loadGLCore(); + loadGLExtensions(hdcGL); + + wglSwapIntervalEXT(0); + + // It is very important that extensions be loaded + // before we call initGLState() + initGLState(); + + mProjectionMatrix.identity(); + + mInitialized = true; + deviceInited(); +} + +bool GFXGLDevice::beginSceneInternal() +{ + glGetError(); + return true; +} + +U32 GFXGLDevice::getTotalVideoMemory() +{ + // CodeReview [ags 12/21/07] Figure out how to do this. + return 0; +} + +//------------------------------------------------------------------------------ + +GFXWindowTarget *GFXGLDevice::allocWindowTarget( PlatformWindow *window ) +{ + HDC hdcGL = GetDC(GETHWND(window)); + + if(!mContext) + { + init(window->getVideoMode(), window); + GFXGLWindowTarget *ggwt = new GFXGLWindowTarget(window, this); + ggwt->registerResourceWithDevice(this); + ggwt->mContext = wglCreateContext(hdcGL); + AssertFatal(ggwt->mContext, "GFXGLDevice::allocWindowTarget - failed to allocate window target!"); + + return ggwt; + } + + GFXGLWindowTarget *ggwt = new GFXGLWindowTarget(window, this); + ggwt->registerResourceWithDevice(this); + + // Create pixel format descriptor... + PIXELFORMATDESCRIPTOR pfd; + CreatePixelFormat( &pfd, 16, 16, 8, false ); // 16 bit color, 16 bit depth, 8 bit stencil...everyone can do this + if( !SetPixelFormat( hdcGL, ChoosePixelFormat( hdcGL, &pfd ), &pfd ) ) + { + AssertFatal( false, "GFXGLDevice::allocWindowTarget - cannot get the one and only pixel format we check for." ); + } + + ggwt->mContext = wglCreateContext(hdcGL); + DWORD w = GetLastError(); + AssertFatal(ggwt->mContext, "GFXGLDevice::allocWindowTarget - failed to allocate window target!"); + + wglMakeCurrent(NULL, NULL); + bool res = wglShareLists((HGLRC)mContext, (HGLRC)ggwt->mContext); + w = GetLastError(); + + wglMakeCurrent(hdcGL, (HGLRC)ggwt->mContext); + AssertFatal(res, "GFXGLDevice::allocWindowTarget - wasn't able to share contexts!"); + + return ggwt; +} + +void GFXGLDevice::_updateRenderTargets() +{ + if ( mRTDirty || mCurrentRT->isPendingState() ) + { + // GL doesn't need to deactivate targets. + mRTDeactivate = NULL; + + // NOTE: The render target changes is not really accurate + // as the GFXTextureTarget supports MRT internally. So when + // we activate a GFXTarget it could result in multiple calls + // to SetRenderTarget on the actual device. + mDeviceStatistics.mRenderTargetChanges++; + + GFXGLTextureTarget *tex = dynamic_cast( mCurrentRT.getPointer() ); + if ( tex ) + { + tex->applyState(); + tex->makeActive(); + } + else + { + GFXGLWindowTarget *win = dynamic_cast( mCurrentRT.getPointer() ); + AssertFatal( win != NULL, + "GFXGLDevice::_updateRenderTargets() - invalid target subclass passed!" ); + + //DWORD w1 = GetLastError(); + HWND hwnd = GETHWND(win->getWindow()); + HDC winDc = GetDC(hwnd); + bool res = wglMakeCurrent(winDc,(HGLRC)win->mContext); + //DWORD w2 = GetLastError(); + AssertFatal(res==true,"GFXGLDevice::setActiveRenderTarget - failed"); + } + + mRTDirty = false; + } + + if ( mViewportDirty ) + { + glViewport( mViewport.point.x, mViewport.point.y, mViewport.extent.x, mViewport.extent.y ); + mViewportDirty = false; + } +} + +GFXFence* GFXGLDevice::_createPlatformSpecificFence() +{ + return NULL; +} + + +//----------------------------------------------------------------------------- + +void GFXGLWindowTarget::makeActive() +{ +} + +bool GFXGLWindowTarget::present() +{ + HWND hwnd = GETHWND(getWindow()); + SwapBuffers(GetDC(hwnd)); + return true; +} + +void GFXGLWindowTarget::_teardownCurrentMode() +{ +} + +void GFXGLWindowTarget::_setupNewMode() +{ +} diff --git a/Engine/source/gfx/gl/gfxGLEnumTranslate.cpp b/Engine/source/gfx/gl/gfxGLEnumTranslate.cpp new file mode 100644 index 000000000..7692b775b --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLEnumTranslate.cpp @@ -0,0 +1,211 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/gl/gfxGLEnumTranslate.h" + +GLenum GFXGLPrimType[GFXPT_COUNT]; +GLenum GFXGLBlend[GFXBlend_COUNT]; +GLenum GFXGLBlendOp[GFXBlendOp_COUNT]; +GLenum GFXGLSamplerState[GFXSAMP_COUNT]; +GLenum GFXGLTextureFilter[GFXTextureFilter_COUNT]; +GLenum GFXGLTextureAddress[GFXAddress_COUNT]; +GLenum GFXGLCmpFunc[GFXCmp_COUNT]; +GLenum GFXGLStencilOp[GFXStencilOp_COUNT]; +GLenum GFXGLTextureInternalFormat[GFXFormat_COUNT]; +GLenum GFXGLTextureFormat[GFXFormat_COUNT]; +GLenum GFXGLTextureType[GFXFormat_COUNT]; +GLenum GFXGLBufferType[GFXBufferType_COUNT]; +GLenum GFXGLCullMode[GFXCull_COUNT]; +GLenum GFXGLFillMode[GFXFill_COUNT]; + +void GFXGLEnumTranslate::init() +{ + // Buffer types + GFXGLBufferType[GFXBufferTypeStatic] = GL_STATIC_DRAW; + GFXGLBufferType[GFXBufferTypeDynamic] = GL_DYNAMIC_DRAW; + GFXGLBufferType[GFXBufferTypeVolatile] = GL_STREAM_DRAW; + + // Primitives + GFXGLPrimType[GFXPointList] = GL_POINTS; + GFXGLPrimType[GFXLineList] = GL_LINES; + GFXGLPrimType[GFXLineStrip] = GL_LINE_STRIP; + GFXGLPrimType[GFXTriangleList] = GL_TRIANGLES; + GFXGLPrimType[GFXTriangleStrip] = GL_TRIANGLE_STRIP; + GFXGLPrimType[GFXTriangleFan] = GL_TRIANGLE_FAN; + + // Blend + GFXGLBlend[GFXBlendZero] = GL_ZERO; + GFXGLBlend[GFXBlendOne] = GL_ONE; + GFXGLBlend[GFXBlendSrcColor] = GL_SRC_COLOR; + GFXGLBlend[GFXBlendInvSrcColor] = GL_ONE_MINUS_SRC_COLOR; + GFXGLBlend[GFXBlendSrcAlpha] = GL_SRC_ALPHA; + GFXGLBlend[GFXBlendInvSrcAlpha] = GL_ONE_MINUS_SRC_ALPHA; + GFXGLBlend[GFXBlendDestAlpha] = GL_DST_ALPHA; + GFXGLBlend[GFXBlendInvDestAlpha] = GL_ONE_MINUS_DST_ALPHA; + GFXGLBlend[GFXBlendDestColor] = GL_DST_COLOR; + GFXGLBlend[GFXBlendInvDestColor] = GL_ONE_MINUS_DST_COLOR; + GFXGLBlend[GFXBlendSrcAlphaSat] = GL_SRC_ALPHA_SATURATE; + + // Blend op + GFXGLBlendOp[GFXBlendOpAdd] = GL_FUNC_ADD; + GFXGLBlendOp[GFXBlendOpSubtract] = GL_FUNC_SUBTRACT; + GFXGLBlendOp[GFXBlendOpRevSubtract] = GL_FUNC_REVERSE_SUBTRACT; + GFXGLBlendOp[GFXBlendOpMin] = GL_MIN; + GFXGLBlendOp[GFXBlendOpMax] = GL_MAX; + + // Sampler + GFXGLSamplerState[GFXSAMPMagFilter] = GL_TEXTURE_MAG_FILTER; + GFXGLSamplerState[GFXSAMPMinFilter] = GL_TEXTURE_MIN_FILTER; + GFXGLSamplerState[GFXSAMPAddressU] = GL_TEXTURE_WRAP_S; + GFXGLSamplerState[GFXSAMPAddressV] = GL_TEXTURE_WRAP_T; + GFXGLSamplerState[GFXSAMPAddressW] = GL_TEXTURE_WRAP_R; + GFXGLSamplerState[GFXSAMPMipMapLODBias] = GL_TEXTURE_LOD_BIAS; + + // Comparison + GFXGLCmpFunc[GFXCmpNever] = GL_NEVER; + GFXGLCmpFunc[GFXCmpLess] = GL_LESS; + GFXGLCmpFunc[GFXCmpEqual] = GL_EQUAL; + GFXGLCmpFunc[GFXCmpLessEqual] = GL_LEQUAL; + GFXGLCmpFunc[GFXCmpGreater] = GL_GREATER; + GFXGLCmpFunc[GFXCmpNotEqual] = GL_NOTEQUAL; + GFXGLCmpFunc[GFXCmpGreaterEqual] = GL_GEQUAL; + GFXGLCmpFunc[GFXCmpAlways] = GL_ALWAYS; + + GFXGLTextureFilter[GFXTextureFilterNone] = GL_NEAREST; + GFXGLTextureFilter[GFXTextureFilterPoint] = GL_NEAREST; + GFXGLTextureFilter[GFXTextureFilterLinear] = GL_LINEAR; + + GFXGLTextureFilter[GFXTextureFilterAnisotropic] = GL_LINEAR; + GFXGLTextureFilter[GFXTextureFilterPyramidalQuad] = GL_LINEAR; + GFXGLTextureFilter[GFXTextureFilterGaussianQuad] = GL_LINEAR; + + GFXGLTextureAddress[GFXAddressWrap] = GL_REPEAT; + GFXGLTextureAddress[GFXAddressMirror] = GL_REPEAT; + GFXGLTextureAddress[GFXAddressClamp] = GL_CLAMP_TO_EDGE; + GFXGLTextureAddress[GFXAddressBorder] = GL_REPEAT; + GFXGLTextureAddress[GFXAddressMirrorOnce] = GL_REPEAT; + + // Stencil ops + GFXGLStencilOp[GFXStencilOpKeep] = GL_KEEP; + GFXGLStencilOp[GFXStencilOpZero] = GL_ZERO; + GFXGLStencilOp[GFXStencilOpReplace] = GL_REPLACE; + GFXGLStencilOp[GFXStencilOpIncrSat] = GL_INCR; + GFXGLStencilOp[GFXStencilOpDecrSat] = GL_DECR; + GFXGLStencilOp[GFXStencilOpInvert] = GL_INVERT; + + GFXGLStencilOp[GFXStencilOpIncr] = GL_INCR_WRAP; + GFXGLStencilOp[GFXStencilOpDecr] = GL_DECR_WRAP; + + + // Texture formats + GFXGLTextureInternalFormat[GFXFormatA8] = GL_ALPHA8; + GFXGLTextureInternalFormat[GFXFormatL8] = GL_LUMINANCE8; + GFXGLTextureInternalFormat[GFXFormatR5G6B5] = GL_RGB5_A1; // OpenGL has no R5G6B5 format. + GFXGLTextureInternalFormat[GFXFormatR5G5B5A1] = GL_RGB5_A1; + GFXGLTextureInternalFormat[GFXFormatR5G5B5X1] = GL_RGB5_A1; + GFXGLTextureInternalFormat[GFXFormatL16] = GL_LUMINANCE16; + GFXGLTextureInternalFormat[GFXFormatR16F] = GL_ZERO; + GFXGLTextureInternalFormat[GFXFormatD16] = GL_DEPTH_COMPONENT; + GFXGLTextureInternalFormat[GFXFormatR8G8B8] = GL_RGB8; + GFXGLTextureInternalFormat[GFXFormatR8G8B8A8] = GL_RGBA8; + GFXGLTextureInternalFormat[GFXFormatR8G8B8X8] = GL_RGBA8; + GFXGLTextureInternalFormat[GFXFormatR32F] = GL_ZERO; + GFXGLTextureInternalFormat[GFXFormatR16G16] = GL_RGBA16; + GFXGLTextureInternalFormat[GFXFormatR16G16F] = GL_ZERO; + GFXGLTextureInternalFormat[GFXFormatR10G10B10A2] = GL_ZERO; + GFXGLTextureInternalFormat[GFXFormatD32] = GL_DEPTH_COMPONENT32; + GFXGLTextureInternalFormat[GFXFormatD24X8] = GL_DEPTH_COMPONENT24; + GFXGLTextureInternalFormat[GFXFormatD24S8] = GL_DEPTH_COMPONENT24; + GFXGLTextureInternalFormat[GFXFormatR16G16B16A16] = GL_ZERO; + GFXGLTextureInternalFormat[GFXFormatR16G16B16A16F] = GL_ZERO; + GFXGLTextureInternalFormat[GFXFormatR32G32B32A32F] = GL_ZERO; + GFXGLTextureInternalFormat[GFXFormatDXT1] = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + GFXGLTextureInternalFormat[GFXFormatDXT2] = GL_ZERO; + GFXGLTextureInternalFormat[GFXFormatDXT3] = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + GFXGLTextureInternalFormat[GFXFormatDXT4] = GL_ZERO; + GFXGLTextureInternalFormat[GFXFormatDXT5] = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + + GFXGLTextureFormat[GFXFormatA8] = GL_ALPHA; + GFXGLTextureFormat[GFXFormatL8] = GL_LUMINANCE; + GFXGLTextureFormat[GFXFormatR5G6B5] = GL_RGBA; + GFXGLTextureFormat[GFXFormatR5G5B5A1] = GL_RGBA; + GFXGLTextureFormat[GFXFormatR5G5B5X1] = GL_RGBA; + GFXGLTextureFormat[GFXFormatL16] = GL_LUMINANCE; + GFXGLTextureFormat[GFXFormatR16F] = GL_ZERO; + GFXGLTextureFormat[GFXFormatD16] = GL_DEPTH_COMPONENT; + GFXGLTextureFormat[GFXFormatR8G8B8] = GL_RGB; + GFXGLTextureFormat[GFXFormatR8G8B8A8] = GL_BGRA; + GFXGLTextureFormat[GFXFormatR8G8B8X8] = GL_BGRA; + GFXGLTextureFormat[GFXFormatR32F] = GL_RGBA; + GFXGLTextureFormat[GFXFormatR16G16] = GL_RGBA; + GFXGLTextureFormat[GFXFormatR16G16F] = GL_ZERO; + GFXGLTextureFormat[GFXFormatR10G10B10A2] = GL_RGBA; + GFXGLTextureFormat[GFXFormatD32] = GL_DEPTH_COMPONENT; + GFXGLTextureFormat[GFXFormatD24X8] = GL_DEPTH_COMPONENT; + GFXGLTextureFormat[GFXFormatD24S8] = GL_DEPTH_COMPONENT; + GFXGLTextureFormat[GFXFormatR16G16B16A16] = GL_RGBA; + GFXGLTextureFormat[GFXFormatR16G16B16A16F] = GL_RGBA; + GFXGLTextureFormat[GFXFormatR32G32B32A32F] = GL_RGBA; + GFXGLTextureFormat[GFXFormatDXT1] = GL_RGBA; + GFXGLTextureFormat[GFXFormatDXT2] = GL_ZERO; + GFXGLTextureFormat[GFXFormatDXT3] = GL_RGBA; + GFXGLTextureFormat[GFXFormatDXT4] = GL_ZERO; + GFXGLTextureFormat[GFXFormatDXT5] = GL_RGBA; + + GFXGLTextureType[GFXFormatA8] = GL_UNSIGNED_BYTE; + GFXGLTextureType[GFXFormatL8] = GL_UNSIGNED_BYTE; + GFXGLTextureType[GFXFormatR5G6B5] = GL_UNSIGNED_BYTE; + GFXGLTextureType[GFXFormatR5G5B5A1] = GL_UNSIGNED_BYTE; + GFXGLTextureType[GFXFormatR5G5B5X1] = GL_UNSIGNED_BYTE; + GFXGLTextureType[GFXFormatL16] = GL_UNSIGNED_SHORT; + GFXGLTextureType[GFXFormatR16F] = GL_ZERO; + GFXGLTextureType[GFXFormatD16] = GL_UNSIGNED_SHORT; + GFXGLTextureType[GFXFormatR8G8B8] = GL_UNSIGNED_BYTE; + GFXGLTextureType[GFXFormatR8G8B8A8] = GL_UNSIGNED_BYTE; + GFXGLTextureType[GFXFormatR8G8B8X8] = GL_UNSIGNED_BYTE; + GFXGLTextureType[GFXFormatR32F] = GL_FLOAT; + GFXGLTextureType[GFXFormatR16G16] = GL_UNSIGNED_SHORT; + GFXGLTextureType[GFXFormatR16G16F] = GL_FLOAT; + GFXGLTextureType[GFXFormatR10G10B10A2] = GL_UNSIGNED_SHORT; + GFXGLTextureType[GFXFormatD32] = GL_UNSIGNED_BYTE; + GFXGLTextureType[GFXFormatD24X8] = GL_UNSIGNED_BYTE; + GFXGLTextureType[GFXFormatD24S8] = GL_UNSIGNED_BYTE; + GFXGLTextureType[GFXFormatR16G16B16A16] = GL_UNSIGNED_SHORT; + GFXGLTextureType[GFXFormatR16G16B16A16F] = GL_FLOAT; + GFXGLTextureType[GFXFormatR32G32B32A32F] = GL_FLOAT; + GFXGLTextureType[GFXFormatDXT1] = GL_UNSIGNED_BYTE; + GFXGLTextureType[GFXFormatDXT2] = GL_ZERO; + GFXGLTextureType[GFXFormatDXT3] = GL_UNSIGNED_BYTE; + GFXGLTextureType[GFXFormatDXT4] = GL_ZERO; + GFXGLTextureType[GFXFormatDXT5] = GL_UNSIGNED_BYTE; + + // Cull + GFXGLCullMode[GFXCullNone] = GL_BACK; + GFXGLCullMode[GFXCullCW] = GL_BACK; + GFXGLCullMode[GFXCullCCW] = GL_FRONT; + + // Fill + GFXGLFillMode[GFXFillPoint] = GL_POINT; + GFXGLFillMode[GFXFillWireframe] = GL_LINE; + GFXGLFillMode[GFXFillSolid] = GL_FILL; +} diff --git a/Engine/source/gfx/gl/gfxGLEnumTranslate.h b/Engine/source/gfx/gl/gfxGLEnumTranslate.h new file mode 100644 index 000000000..2affa463e --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLEnumTranslate.h @@ -0,0 +1,52 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXGLENUMTRANSLATE_H_ +#define _GFXGLENUMTRANSLATE_H_ + +#include "gfx/gfxEnums.h" +#include "gfx/gl/gfxGLDevice.h" + +namespace GFXGLEnumTranslate +{ + void init(); +}; + +extern GLenum GFXGLPrimType[GFXPT_COUNT]; +extern GLenum GFXGLBlend[GFXBlend_COUNT]; +extern GLenum GFXGLBlendOp[GFXBlendOp_COUNT]; +extern GLenum GFXGLSamplerState[GFXSAMP_COUNT]; +extern GLenum GFXGLTextureFilter[GFXTextureFilter_COUNT]; +extern GLenum GFXGLTextureAddress[GFXAddress_COUNT]; +extern GLenum GFXGLCmpFunc[GFXCmp_COUNT]; +extern GLenum GFXGLStencilOp[GFXStencilOp_COUNT]; + +extern GLenum GFXGLTextureInternalFormat[GFXFormat_COUNT]; +extern GLenum GFXGLTextureFormat[GFXFormat_COUNT]; +extern GLenum GFXGLTextureType[GFXFormat_COUNT]; + +extern GLenum GFXGLBufferType[GFXBufferType_COUNT]; +extern GLenum GFXGLCullMode[GFXCull_COUNT]; + +extern GLenum GFXGLFillMode[GFXFill_COUNT]; + +#endif diff --git a/Engine/source/gfx/gl/gfxGLOcclusionQuery.cpp b/Engine/source/gfx/gl/gfxGLOcclusionQuery.cpp new file mode 100644 index 000000000..6ecfb36cc --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLOcclusionQuery.cpp @@ -0,0 +1,89 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/gl/gfxGLOcclusionQuery.h" +#include "gfx/gl/ggl/ggl.h" + +GFXGLOcclusionQuery::GFXGLOcclusionQuery(GFXDevice* device) : + GFXOcclusionQuery(device), mQuery(0) +{ + glGenQueries(1, &mQuery); +} + +GFXGLOcclusionQuery::~GFXGLOcclusionQuery() +{ + glDeleteQueries(1, &mQuery); +} + +bool GFXGLOcclusionQuery::begin() +{ + glBeginQuery(GL_SAMPLES_PASSED, mQuery); + return true; +} + +void GFXGLOcclusionQuery::end() +{ + glEndQuery(GL_SAMPLES_PASSED); +} + +GFXOcclusionQuery::OcclusionQueryStatus GFXGLOcclusionQuery::getStatus(bool block, U32* data) +{ + // If this ever shows up near the top of a profile + // then your system is GPU bound. + PROFILE_SCOPE(GFXGLOcclusionQuery_getStatus); + + GLint numPixels = 0; + GLint queryDone = false; + + if (block) + queryDone = true; + else + glGetQueryObjectiv(mQuery, GL_QUERY_RESULT_AVAILABLE, &queryDone); + + if (queryDone) + glGetQueryObjectiv(mQuery, GL_QUERY_RESULT, &numPixels); + else + return Waiting; + + if (data) + *data = numPixels; + + return numPixels > 0 ? NotOccluded : Occluded; +} + +void GFXGLOcclusionQuery::zombify() +{ + glDeleteQueries(1, &mQuery); + mQuery = 0; +} + +void GFXGLOcclusionQuery::resurrect() +{ + glGenQueries(1, &mQuery); +} + +const String GFXGLOcclusionQuery::describeSelf() const +{ + // We've got nothing + return String(); +} diff --git a/Engine/source/gfx/gl/gfxGLOcclusionQuery.h b/Engine/source/gfx/gl/gfxGLOcclusionQuery.h new file mode 100644 index 000000000..f49022fd8 --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLOcclusionQuery.h @@ -0,0 +1,49 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFX_GL_OCCLUSIONQUERY_H_ +#define _GFX_GL_OCCLUSIONQUERY_H_ + +#ifndef _GFXOCCLUSIONQUERY_H_ +#include "gfx/gfxOcclusionQuery.h" +#endif + +class GFXGLOcclusionQuery : public GFXOcclusionQuery +{ +public: + GFXGLOcclusionQuery( GFXDevice *device ); + virtual ~GFXGLOcclusionQuery(); + + virtual bool begin(); + virtual void end(); + virtual OcclusionQueryStatus getStatus( bool block, U32 *data = NULL ); + + // GFXResource + virtual void zombify(); + virtual void resurrect(); + virtual const String describeSelf() const; + +private: + U32 mQuery; +}; + +#endif // _GFX_GL_OCCLUSIONQUERY_H_ diff --git a/Engine/source/gfx/gl/gfxGLPrimitiveBuffer.cpp b/Engine/source/gfx/gl/gfxGLPrimitiveBuffer.cpp new file mode 100644 index 000000000..22ba60fe0 --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLPrimitiveBuffer.cpp @@ -0,0 +1,116 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/gl/gfxGLDevice.h" +#include "gfx/gl/gfxGLPrimitiveBuffer.h" +#include "gfx/gl/gfxGLEnumTranslate.h" + +#include "gfx/gl/ggl/ggl.h" +#include "gfx/gl/gfxGLUtils.h" + +GFXGLPrimitiveBuffer::GFXGLPrimitiveBuffer(GFXDevice *device, U32 indexCount, U32 primitiveCount, GFXBufferType bufferType) : +GFXPrimitiveBuffer(device, indexCount, primitiveCount, bufferType), mZombieCache(NULL) +{ + PRESERVE_INDEX_BUFFER(); + // Generate a buffer and allocate the needed memory + glGenBuffers(1, &mBuffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mBuffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount * sizeof(U16), NULL, GFXGLBufferType[bufferType]); +} + +GFXGLPrimitiveBuffer::~GFXGLPrimitiveBuffer() +{ + // This is heavy handed, but it frees the buffer memory + glDeleteBuffersARB(1, &mBuffer); + + if( mZombieCache ) + delete [] mZombieCache; +} + +void GFXGLPrimitiveBuffer::lock(U32 indexStart, U32 indexEnd, void **indexPtr) +{ + // Preserve previous binding + PRESERVE_INDEX_BUFFER(); + + // Bind ourselves and map + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mBuffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, mIndexCount * sizeof(U16), NULL, GFXGLBufferType[mBufferType]); + + // Offset the buffer to indexStart + *indexPtr = (void*)((U8*)glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY) + (indexStart * sizeof(U16))); +} + +void GFXGLPrimitiveBuffer::unlock() +{ + // Preserve previous binding + PRESERVE_INDEX_BUFFER(); + + // Bind ourselves and unmap + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mBuffer); + bool res = glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + AssertFatal(res, "GFXGLPrimitiveBuffer::unlock - shouldn't fail!"); +} + +void GFXGLPrimitiveBuffer::prepare() +{ + // Bind + static_cast(mDevice)->setPB(this); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mBuffer); +} + +void GFXGLPrimitiveBuffer::finish() +{ + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} + +GLvoid* GFXGLPrimitiveBuffer::getBuffer() +{ + // NULL specifies no offset into the hardware buffer + return (GLvoid*)NULL; +} + +void GFXGLPrimitiveBuffer::zombify() +{ + if(mZombieCache) + return; + + mZombieCache = new U8[mIndexCount * sizeof(U16)]; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mBuffer); + glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, mIndexCount * sizeof(U16), mZombieCache); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + glDeleteBuffers(1, &mBuffer); + mBuffer = 0; +} + +void GFXGLPrimitiveBuffer::resurrect() +{ + if(!mZombieCache) + return; + + glGenBuffers(1, &mBuffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mBuffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, mIndexCount * sizeof(U16), mZombieCache, GFXGLBufferType[mBufferType]); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + delete[] mZombieCache; + mZombieCache = NULL; +} diff --git a/Engine/source/gfx/gl/gfxGLPrimitiveBuffer.h b/Engine/source/gfx/gl/gfxGLPrimitiveBuffer.h new file mode 100644 index 000000000..a1c0af647 --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLPrimitiveBuffer.h @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXGLPRIMITIVEBUFFER_H_ +#define _GFXGLPRIMITIVEBUFFER_H_ + +#include "gfx/gfxPrimitiveBuffer.h" + +/// This is a primitive buffer (index buffer to GL users) which uses VBOs. +class GFXGLPrimitiveBuffer : public GFXPrimitiveBuffer +{ +public: + GFXGLPrimitiveBuffer(GFXDevice *device, U32 indexCount, U32 primitiveCount, GFXBufferType bufferType); + ~GFXGLPrimitiveBuffer(); + + virtual void lock(U32 indexStart, U32 indexEnd, void **indexPtr); ///< calls glMapBuffer, offets pointer by indexStart + virtual void unlock(); ///< calls glUnmapBuffer, unbinds the buffer + virtual void prepare(); ///< binds the buffer + virtual void finish(); ///< We're done with this buffer + + virtual void* getBuffer(); ///< returns NULL + + // GFXResource interface + virtual void zombify(); + virtual void resurrect(); + +private: + /// Handle to our GL buffer object + GLuint mBuffer; + + U8* mZombieCache; +}; + +#endif \ No newline at end of file diff --git a/Engine/source/gfx/gl/gfxGLShader.cpp b/Engine/source/gfx/gl/gfxGLShader.cpp new file mode 100644 index 000000000..8b60fbd82 --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLShader.cpp @@ -0,0 +1,929 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/gl/gfxGLShader.h" + +#include "core/frameAllocator.h" +#include "core/stream/fileStream.h" +#include "core/strings/stringFunctions.h" +#include "math/mPoint2.h" +#include "gfx/gfxStructs.h" +#include "console/console.h" + + +class GFXGLShaderConstHandle : public GFXShaderConstHandle +{ + friend class GFXGLShader; + +public: + + GFXGLShaderConstHandle( GFXGLShader *shader ); + GFXGLShaderConstHandle( GFXGLShader *shader, const GFXShaderConstDesc &desc, GLuint loc, S32 samplerNum ); + virtual ~GFXGLShaderConstHandle(); + + void reinit( const GFXShaderConstDesc &desc, GLuint loc, S32 samplerNum ); + + const String& getName() const { return mDesc.name; } + GFXShaderConstType getType() const { return mDesc.constType; } + U32 getArraySize() const { return mDesc.arraySize; } + + U32 getSize() const; + void setValid( bool valid ) { mValid = valid; } + /// @warning This will always return the value assigned when the shader was + /// initialized. If the value is later changed this method won't reflect that. + S32 getSamplerRegister() const { return mSamplerNum; } + + GFXShaderConstDesc mDesc; + GFXGLShader* mShader; + GLuint mLocation; + U32 mOffset; + U32 mSize; + S32 mSamplerNum; +}; + +GFXGLShaderConstHandle::GFXGLShaderConstHandle( GFXGLShader *shader ) + : mShader( shader ), mSamplerNum(-1) +{ + mValid = false; +} + +static U32 shaderConstTypeSize(GFXShaderConstType type) +{ + switch(type) + { + case GFXSCT_Float: + case GFXSCT_Int: + case GFXSCT_Sampler: + case GFXSCT_SamplerCube: + return 4; + case GFXSCT_Float2: + case GFXSCT_Int2: + return 8; + case GFXSCT_Float3: + case GFXSCT_Int3: + return 12; + case GFXSCT_Float4: + case GFXSCT_Int4: + return 16; + case GFXSCT_Float2x2: + return 16; + case GFXSCT_Float3x3: + return 36; + case GFXSCT_Float4x4: + return 64; + default: + AssertFatal(false,"shaderConstTypeSize - Unrecognized constant type"); + return 0; + } +} + +GFXGLShaderConstHandle::GFXGLShaderConstHandle( GFXGLShader *shader, const GFXShaderConstDesc &desc, GLuint loc, S32 samplerNum ) + : mShader(shader) +{ + reinit(desc, loc, samplerNum); +} + +void GFXGLShaderConstHandle::reinit( const GFXShaderConstDesc& desc, GLuint loc, S32 samplerNum ) +{ + mDesc = desc; + mLocation = loc; + mSamplerNum = samplerNum; + mOffset = 0; + + U32 elemSize = shaderConstTypeSize(mDesc.constType); + AssertFatal(elemSize, "GFXGLShaderConst::GFXGLShaderConst - elemSize is 0"); + mSize = mDesc.arraySize * elemSize; + mValid = true; +} + + +U32 GFXGLShaderConstHandle::getSize() const +{ + return mSize; +} + +GFXGLShaderConstHandle::~GFXGLShaderConstHandle() +{ +} + +GFXGLShaderConstBuffer::GFXGLShaderConstBuffer(GFXGLShader* shader, U32 bufSize, U8* existingConstants) +{ + mShader = shader; + mBuffer = new U8[bufSize]; + mWasLost = true; + + // Copy the existing constant buffer to preserve sampler numbers + /// @warning This preserves a lot more than sampler numbers, obviously. If there + /// is any code that assumes a new constant buffer will have everything set to + /// 0, it will break. + dMemcpy(mBuffer, existingConstants, bufSize); +} + +GFXGLShaderConstBuffer::~GFXGLShaderConstBuffer() +{ + delete[] mBuffer; + + if ( mShader ) + mShader->_unlinkBuffer( this ); +} + +template +void GFXGLShaderConstBuffer::internalSet(GFXShaderConstHandle* handle, const ConstType& param) +{ + AssertFatal(handle, "GFXGLShaderConstBuffer::internalSet - Handle is NULL!" ); + AssertFatal(handle->isValid(), "GFXGLShaderConstBuffer::internalSet - Handle is not valid!" ); + AssertFatal(dynamic_cast(handle), "GFXGLShaderConstBuffer::set - Incorrect const buffer type"); + + GFXGLShaderConstHandle* _glHandle = static_cast(handle); + AssertFatal(mShader == _glHandle->mShader, "GFXGLShaderConstBuffer::set - Should only set handles which are owned by our shader"); + + dMemcpy(mBuffer + _glHandle->mOffset, ¶m, sizeof(ConstType)); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const F32 fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const Point2F& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const Point3F& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const Point4F& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const PlaneF& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const ColorF& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const S32 fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const Point2I& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const Point3I& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const Point4I& fv) +{ + internalSet(handle, fv); +} + +template +void GFXGLShaderConstBuffer::internalSet(GFXShaderConstHandle* handle, const AlignedArray& fv) +{ + AssertFatal(handle, "GFXGLShaderConstBuffer::internalSet - Handle is NULL!" ); + AssertFatal(handle->isValid(), "GFXGLShaderConstBuffer::internalSet - Handle is not valid!" ); + AssertFatal(dynamic_cast(handle), "GFXGLShaderConstBuffer::set - Incorrect const buffer type"); + + GFXGLShaderConstHandle* _glHandle = static_cast(handle); + AssertFatal(mShader == _glHandle->mShader, "GFXGLShaderConstBuffer::set - Should only set handles which are owned by our shader"); + const U8* fvBuffer = static_cast(fv.getBuffer()); + for(U32 i = 0; i < fv.size(); ++i) + { + dMemcpy(mBuffer + _glHandle->mOffset + i * sizeof(ConstType), fvBuffer, sizeof(ConstType)); + fvBuffer += fv.getElementSize(); + } +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const AlignedArray& fv) +{ + internalSet(handle, fv); +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const MatrixF& mat, const GFXShaderConstType matType) +{ + AssertFatal(handle, "GFXGLShaderConstBuffer::set - Handle is NULL!" ); + AssertFatal(handle->isValid(), "GFXGLShaderConstBuffer::set - Handle is not valid!" ); + AssertFatal(dynamic_cast(handle), "GFXGLShaderConstBuffer::set - Incorrect const buffer type"); + + GFXGLShaderConstHandle* _glHandle = static_cast(handle); + AssertFatal(mShader == _glHandle->mShader, "GFXGLShaderConstBuffer::set - Should only set handles which are owned by our shader"); + + switch(matType) + { + case GFXSCT_Float2x2: + reinterpret_cast(mBuffer + _glHandle->mOffset)[0] = mat[0]; + reinterpret_cast(mBuffer + _glHandle->mOffset)[1] = mat[1]; + reinterpret_cast(mBuffer + _glHandle->mOffset)[2] = mat[4]; + reinterpret_cast(mBuffer + _glHandle->mOffset)[3] = mat[5]; + break; + case GFXSCT_Float3x3: + reinterpret_cast(mBuffer + _glHandle->mOffset)[0] = mat[0]; + reinterpret_cast(mBuffer + _glHandle->mOffset)[1] = mat[1]; + reinterpret_cast(mBuffer + _glHandle->mOffset)[2] = mat[2]; + reinterpret_cast(mBuffer + _glHandle->mOffset)[3] = mat[4]; + reinterpret_cast(mBuffer + _glHandle->mOffset)[4] = mat[5]; + reinterpret_cast(mBuffer + _glHandle->mOffset)[5] = mat[6]; + reinterpret_cast(mBuffer + _glHandle->mOffset)[6] = mat[8]; + reinterpret_cast(mBuffer + _glHandle->mOffset)[7] = mat[9]; + reinterpret_cast(mBuffer + _glHandle->mOffset)[8] = mat[10]; + break; + case GFXSCT_Float4x4: + dMemcpy(mBuffer + _glHandle->mOffset, (const F32*)mat, sizeof(MatrixF)); + break; + default: + AssertFatal(false, "GFXGLShaderConstBuffer::set - Invalid matrix type"); + break; + } +} + +void GFXGLShaderConstBuffer::set(GFXShaderConstHandle* handle, const MatrixF* mat, const U32 arraySize, const GFXShaderConstType matrixType) +{ + AssertFatal(handle, "GFXGLShaderConstBuffer::set - Handle is NULL!" ); + AssertFatal(handle->isValid(), "GFXGLShaderConstBuffer::set - Handle is not valid!" ); + + GFXGLShaderConstHandle* _glHandle = static_cast(handle); + AssertFatal(mShader == _glHandle->mShader, "GFXGLShaderConstBuffer::set - Should only set handles which are owned by our shader"); + + switch (matrixType) { + case GFXSCT_Float4x4: + dMemcpy(mBuffer + _glHandle->mOffset, (F32*)mat, _glHandle->getSize()); + break; + default: + AssertFatal(false, "GFXGLShaderConstBuffer::set - setting array of non 4x4 matrices!"); + break; + } +} + +void GFXGLShaderConstBuffer::activate() +{ + mShader->setConstantsFromBuffer(this); + mWasLost = false; +} + +const String GFXGLShaderConstBuffer::describeSelf() const +{ + return String(); +} + +void GFXGLShaderConstBuffer::onShaderReload( GFXGLShader *shader ) +{ + AssertFatal( shader == mShader, "GFXGLShaderConstBuffer::onShaderReload, mismatched shaders!" ); + + delete[] mBuffer; + mBuffer = new U8[mShader->mConstBufferSize]; + dMemset(mBuffer, 0, mShader->mConstBufferSize); + mWasLost = true; +} + +GFXGLShader::GFXGLShader() : + mVertexShader(0), + mPixelShader(0), + mProgram(0), + mConstBufferSize(0), + mConstBuffer(NULL) +{ +} + +GFXGLShader::~GFXGLShader() +{ + clearShaders(); + for(HandleMap::Iterator i = mHandles.begin(); i != mHandles.end(); i++) + delete i->value; + + delete[] mConstBuffer; +} + +void GFXGLShader::clearShaders() +{ + glDeleteProgram(mProgram); + glDeleteShader(mVertexShader); + glDeleteShader(mPixelShader); + + mProgram = 0; + mVertexShader = 0; + mPixelShader = 0; +} + +bool GFXGLShader::_init() +{ + // Don't initialize empty shaders. + if ( mVertexFile.isEmpty() && mPixelFile.isEmpty() ) + return false; + + clearShaders(); + + mProgram = glCreateProgram(); + + // Set the macros and add the global ones. + Vector macros; + macros.merge( mMacros ); + macros.merge( smGlobalMacros ); + + // Add the shader version to the macros. + const U32 mjVer = (U32)mFloor( mPixVersion ); + const U32 mnVer = (U32)( ( mPixVersion - F32( mjVer ) ) * 10.01f ); + macros.increment(); + macros.last().name = "TORQUE_SM"; + macros.last().value = String::ToString( mjVer * 10 + mnVer ); + + // Default to true so we're "successful" if a vertex/pixel shader wasn't specified. + bool compiledVertexShader = true; + bool compiledPixelShader = true; + + // Compile the vertex and pixel shaders if specified. + if(!mVertexFile.isEmpty()) + compiledVertexShader = initShader(mVertexFile, true, macros); + if(!mPixelFile.isEmpty()) + compiledPixelShader = initShader(mPixelFile, false, macros); + + // If either shader was present and failed to compile, bail. + if(!compiledVertexShader || !compiledPixelShader) + return false; + + // Link it! + glLinkProgram( mProgram ); + + GLint linkStatus; + glGetProgramiv( mProgram, GL_LINK_STATUS, &linkStatus ); + + // Dump the info log to the console + U32 logLength = 0; + glGetProgramiv(mProgram, GL_INFO_LOG_LENGTH, (GLint*)&logLength); + if ( logLength ) + { + FrameAllocatorMarker fam; + char* log = (char*)fam.alloc( logLength ); + glGetProgramInfoLog( mProgram, logLength, NULL, log ); + + if ( linkStatus == GL_FALSE ) + { + if ( smLogErrors ) + { + Con::errorf( "GFXGLShader::init - Error linking shader!" ); + Con::errorf( "Program %s / %s: %s", + mVertexFile.getFullPath().c_str(), mPixelFile.getFullPath().c_str(), log); + } + } + else if ( smLogWarnings ) + { + Con::warnf( "Program %s / %s: %s", + mVertexFile.getFullPath().c_str(), mPixelFile.getFullPath().c_str(), log); + } + } + + // If we failed to link, bail. + if ( linkStatus == GL_FALSE ) + return false; + + initConstantDescs(); + initHandles(); + + // Notify Buffers we might have changed in size. + // If this was our first init then we won't have any activeBuffers + // to worry about unnecessarily calling. + Vector::iterator biter = mActiveBuffers.begin(); + for ( ; biter != mActiveBuffers.end(); biter++ ) + ((GFXGLShaderConstBuffer*)(*biter))->onShaderReload( this ); + + return true; +} + +void GFXGLShader::initConstantDescs() +{ + mConstants.clear(); + GLint numUniforms; + glGetProgramiv(mProgram, GL_ACTIVE_UNIFORMS, &numUniforms); + GLint maxNameLength; + glGetProgramiv(mProgram, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxNameLength); + FrameTemp uniformName(maxNameLength); + + for(U32 i = 0; i < numUniforms; i++) + { + GLint size; + GLenum type; + glGetActiveUniform(mProgram, i, maxNameLength, NULL, &size, &type, uniformName); + GFXShaderConstDesc desc; + + desc.name = String((char*)uniformName); + + // Remove array brackets from the name + desc.name = desc.name.substr(0, desc.name.find('[')); + + // Insert $ to match D3D behavior of having a $ prepended to parameters to main. + desc.name.insert(0, '$'); + desc.arraySize = size; + + switch(type) + { + case GL_FLOAT: + desc.constType = GFXSCT_Float; + break; + case GL_FLOAT_VEC2: + desc.constType = GFXSCT_Float2; + break; + case GL_FLOAT_VEC3: + desc.constType = GFXSCT_Float3; + break; + case GL_FLOAT_VEC4: + desc.constType = GFXSCT_Float4; + break; + case GL_INT: + desc.constType = GFXSCT_Int; + break; + case GL_INT_VEC2: + desc.constType = GFXSCT_Int2; + break; + case GL_INT_VEC3: + desc.constType = GFXSCT_Int3; + break; + case GL_INT_VEC4: + desc.constType = GFXSCT_Int4; + break; + case GL_FLOAT_MAT2: + desc.constType = GFXSCT_Float2x2; + break; + case GL_FLOAT_MAT3: + desc.constType = GFXSCT_Float3x3; + break; + case GL_FLOAT_MAT4: + desc.constType = GFXSCT_Float4x4; + break; + case GL_SAMPLER_1D: + case GL_SAMPLER_2D: + case GL_SAMPLER_3D: + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_2D_SHADOW: + desc.constType = GFXSCT_Sampler; + break; + case GL_SAMPLER_CUBE: + desc.constType = GFXSCT_SamplerCube; + break; + default: + AssertFatal(false, "GFXGLShader::initConstantDescs - unrecognized uniform type"); + // If we don't recognize the constant don't add its description. + continue; + } + + mConstants.push_back(desc); + } +} + +void GFXGLShader::initHandles() +{ + // Mark all existing handles as invalid. + // Those that are found when parsing the descriptions will then be marked valid again. + for ( HandleMap::Iterator iter = mHandles.begin(); iter != mHandles.end(); ++iter ) + (iter->value)->setValid( false ); + mValidHandles.clear(); + + // Loop through all ConstantDescriptions, + // if they aren't in the HandleMap add them, if they are reinitialize them. + S32 assignedSamplerNum = 0; + for ( U32 i = 0; i < mConstants.size(); i++ ) + { + GFXShaderConstDesc &desc = mConstants[i]; + + // Index element 1 of the name to skip the '$' we inserted earier. + U32 loc = glGetUniformLocation(mProgram, &desc.name.c_str()[1]); + + HandleMap::Iterator handle = mHandles.find(desc.name); + S32 sampler = (desc.constType == GFXSCT_Sampler || desc.constType == GFXSCT_SamplerCube) ? + assignedSamplerNum++ : -1; + if ( handle != mHandles.end() ) + { + handle->value->reinit( desc, loc, sampler ); + } + else + { + mHandles[desc.name] = new GFXGLShaderConstHandle( this, desc, loc, sampler ); + } + } + + // Loop through handles once more to set their offset and calculate our + // constBuffer size. + + if ( mConstBuffer ) + delete[] mConstBuffer; + mConstBufferSize = 0; + + for ( HandleMap::Iterator iter = mHandles.begin(); iter != mHandles.end(); ++iter ) + { + GFXGLShaderConstHandle* handle = iter->value; + if ( handle->isValid() ) + { + mValidHandles.push_back(handle); + handle->mOffset = mConstBufferSize; + mConstBufferSize += handle->getSize(); + } + } + + mConstBuffer = new U8[mConstBufferSize]; + dMemset(mConstBuffer, 0, mConstBufferSize); + + // Set our program so uniforms are assigned properly. + glUseProgram(mProgram); + // Iterate through uniforms to set sampler numbers. + for (HandleMap::Iterator iter = mHandles.begin(); iter != mHandles.end(); ++iter) + { + GFXGLShaderConstHandle* handle = iter->value; + if(handle->isValid() && (handle->getType() == GFXSCT_Sampler || handle->getType() == GFXSCT_SamplerCube)) + { + // Set sampler number on our program. + glUniform1i(handle->mLocation, handle->mSamplerNum); + // Set sampler in constant buffer so it does not get unset later. + dMemcpy(mConstBuffer + handle->mOffset, &handle->mLocation, handle->getSize()); + } + } + glUseProgram(0); +} + +GFXShaderConstHandle* GFXGLShader::getShaderConstHandle(const String& name) +{ + HandleMap::Iterator i = mHandles.find(name); + if(i != mHandles.end()) + return i->value; + else + { + GFXGLShaderConstHandle* handle = new GFXGLShaderConstHandle( this ); + mHandles[ name ] = handle; + + return handle; + } +} + +void GFXGLShader::setConstantsFromBuffer(GFXGLShaderConstBuffer* buffer) +{ + for(Vector::iterator i = mValidHandles.begin(); i != mValidHandles.end(); ++i) + { + GFXGLShaderConstHandle* handle = *i; + AssertFatal(handle, "GFXGLShader::setConstantsFromBuffer - Null handle"); + + // Don't set if the value has not be changed. + if(dMemcmp(mConstBuffer + handle->mOffset, buffer->mBuffer + handle->mOffset, handle->getSize()) == 0) + continue; + + // Copy new value into our const buffer and set in GL. + dMemcpy(mConstBuffer + handle->mOffset, buffer->mBuffer + handle->mOffset, handle->getSize()); + switch(handle->mDesc.constType) + { + case GFXSCT_Float: + glUniform1fv(handle->mLocation, handle->mDesc.arraySize, (GLfloat*)(mConstBuffer + handle->mOffset)); + break; + case GFXSCT_Float2: + glUniform2fv(handle->mLocation, handle->mDesc.arraySize, (GLfloat*)(mConstBuffer + handle->mOffset)); + break; + case GFXSCT_Float3: + glUniform3fv(handle->mLocation, handle->mDesc.arraySize, (GLfloat*)(mConstBuffer + handle->mOffset)); + break; + case GFXSCT_Float4: + glUniform4fv(handle->mLocation, handle->mDesc.arraySize, (GLfloat*)(mConstBuffer + handle->mOffset)); + break; + case GFXSCT_Int: + case GFXSCT_Sampler: + case GFXSCT_SamplerCube: + glUniform1iv(handle->mLocation, handle->mDesc.arraySize, (GLint*)(mConstBuffer + handle->mOffset)); + break; + case GFXSCT_Int2: + glUniform2iv(handle->mLocation, handle->mDesc.arraySize, (GLint*)(mConstBuffer + handle->mOffset)); + break; + case GFXSCT_Int3: + glUniform3iv(handle->mLocation, handle->mDesc.arraySize, (GLint*)(mConstBuffer + handle->mOffset)); + break; + case GFXSCT_Int4: + glUniform4iv(handle->mLocation, handle->mDesc.arraySize, (GLint*)(mConstBuffer + handle->mOffset)); + break; + case GFXSCT_Float2x2: + glUniformMatrix2fv(handle->mLocation, handle->mDesc.arraySize, true, (GLfloat*)(mConstBuffer + handle->mOffset)); + break; + case GFXSCT_Float3x3: + glUniformMatrix3fv(handle->mLocation, handle->mDesc.arraySize, true, (GLfloat*)(mConstBuffer + handle->mOffset)); + break; + case GFXSCT_Float4x4: + glUniformMatrix4fv(handle->mLocation, handle->mDesc.arraySize, true, (GLfloat*)(mConstBuffer + handle->mOffset)); + break; + } + } +} + +GFXShaderConstBufferRef GFXGLShader::allocConstBuffer() +{ + GFXGLShaderConstBuffer* buffer = new GFXGLShaderConstBuffer(this, mConstBufferSize, mConstBuffer); + buffer->registerResourceWithDevice(getOwningDevice()); + mActiveBuffers.push_back( buffer ); + return buffer; +} + +void GFXGLShader::useProgram() +{ + glUseProgram(mProgram); +} + +void GFXGLShader::zombify() +{ + clearShaders(); + dMemset(mConstBuffer, 0, mConstBufferSize); +} + +char* GFXGLShader::_handleIncludes( const Torque::Path& path, FileStream *s ) +{ + // TODO: The #line pragma on GLSL takes something called a + // "source-string-number" which it then never explains. + // + // Until i resolve this mystery i disabled this. + // + //String linePragma = String::ToString( "#line 1 \r\n"); + //U32 linePragmaLen = linePragma.length(); + + U32 shaderLen = s->getStreamSize(); + char* buffer = (char*)dMalloc(shaderLen + 1); + //dStrncpy( buffer, linePragma.c_str(), linePragmaLen ); + s->read(shaderLen, buffer); + buffer[shaderLen] = 0; + + char* p = dStrstr(buffer, "#include"); + while(p) + { + char* q = p; + p += 8; + if(dIsspace(*p)) + { + U32 n = 0; + while(dIsspace(*p)) ++p; + AssertFatal(*p == '"', "Bad #include directive"); + ++p; + static char includeFile[256]; + while(*p != '"') + { + AssertFatal(*p != 0, "Bad #include directive"); + includeFile[n++] = *p++; + AssertFatal(n < sizeof(includeFile), "#include directive too long"); + } + ++p; + includeFile[n] = 0; + + // First try it as a local file. + Torque::Path includePath = Torque::Path::Join(path.getPath(), '/', includeFile); + includePath = Torque::Path::CompressPath(includePath); + + FileStream includeStream; + + if ( !includeStream.open( includePath, Torque::FS::File::Read ) ) + { + // Try again assuming the path is absolute + // and/or relative. + includePath = String( includeFile ); + includePath = Torque::Path::CompressPath(includePath); + if ( !includeStream.open( includePath, Torque::FS::File::Read ) ) + { + AssertISV(false, avar("failed to open include '%s'.", includePath.getFullPath().c_str())); + + if ( smLogErrors ) + Con::errorf( "GFXGLShader::_handleIncludes - Failed to open include '%s'.", + includePath.getFullPath().c_str() ); + + // Fail... don't return the buffer. + dFree(buffer); + return NULL; + } + } + + char* includedText = _handleIncludes(includePath, &includeStream); + + // If a sub-include fails... cleanup and return. + if ( !includedText ) + { + dFree(buffer); + return NULL; + } + + // TODO: Disabled till this is fixed correctly. + // + // Count the number of lines in the file + // before the include. + /* + U32 includeLine = 0; + { + char* nl = dStrstr( buffer, "\n" ); + while ( nl ) + { + includeLine++; + nl = dStrstr( nl, "\n" ); + if(nl) ++nl; + } + } + */ + + String manip(buffer); + manip.erase(q-buffer, p-q); + String sItx(includedText); + + // TODO: Disabled till this is fixed correctly. + // + // Add a new line pragma to restore the proper + // file and line number after the include. + //sItx += String::ToString( "\r\n#line %d \r\n", includeLine ); + + dFree(includedText); + manip.insert(q-buffer, sItx); + char* manipBuf = dStrdup(manip.c_str()); + p = manipBuf + (p - buffer); + dFree(buffer); + buffer = manipBuf; + } + p = dStrstr(p, "#include"); + } + + return buffer; +} + +bool GFXGLShader::_loadShaderFromStream( GLuint shader, + const Torque::Path &path, + FileStream *s, + const Vector ¯os ) +{ + Vector buffers; + Vector lengths; + + // The GLSL version declaration must go first! + const char *versionDecl = "#version 120\r\n\r\n"; + buffers.push_back( dStrdup( versionDecl ) ); + lengths.push_back( dStrlen( versionDecl ) ); + + // Now add all the macros. + for( U32 i = 0; i < macros.size(); i++ ) + { + String define = String::ToString( "#define %s %s\n", macros[i].name.c_str(), macros[i].value.c_str() ); + buffers.push_back( dStrdup( define.c_str() ) ); + lengths.push_back( define.length() ); + } + + // Now finally add the shader source. + U32 shaderLen = s->getStreamSize(); + char *buffer = _handleIncludes(path, s); + if ( !buffer ) + return false; + + buffers.push_back(buffer); + lengths.push_back(shaderLen); + + glShaderSource(shader, buffers.size(), (const GLchar**)const_cast(buffers.address()), NULL); + + // Cleanup the shader source buffer. + for ( U32 i=0; i < buffers.size(); i++ ) + dFree( buffers[i] ); + + glCompileShader(shader); + + return true; +} + +bool GFXGLShader::initShader( const Torque::Path &file, + bool isVertex, + const Vector ¯os ) +{ + GLuint activeShader = glCreateShader(isVertex ? GL_VERTEX_SHADER : GL_FRAGMENT_SHADER); + if(isVertex) + mVertexShader = activeShader; + else + mPixelShader = activeShader; + glAttachShader(mProgram, activeShader); + + + // Ok it's not in the shader gen manager, so ask Torque for it + FileStream stream; + if ( !stream.open( file, Torque::FS::File::Read ) ) + { + AssertISV(false, avar("GFXGLShader::initShader - failed to open shader '%s'.", file.getFullPath().c_str())); + + if ( smLogErrors ) + Con::errorf( "GFXGLShader::initShader - Failed to open shader file '%s'.", + file.getFullPath().c_str() ); + + return false; + } + + if ( !_loadShaderFromStream( activeShader, file, &stream, macros ) ) + return false; + + GLint compile; + glGetShaderiv(activeShader, GL_COMPILE_STATUS, &compile); + + // Dump the info log to the console + U32 logLength = 0; + glGetShaderiv(activeShader, GL_INFO_LOG_LENGTH, (GLint*)&logLength); + + GLint compileStatus = GL_TRUE; + if ( logLength ) + { + FrameAllocatorMarker fam; + char* log = (char*)fam.alloc(logLength); + glGetShaderInfoLog(activeShader, logLength, NULL, log); + + // Always print errors + glGetShaderiv( activeShader, GL_COMPILE_STATUS, &compileStatus ); + + if ( compileStatus == GL_FALSE ) + { + if ( smLogErrors ) + { + Con::errorf( "GFXGLShader::initShader - Error compiling shader!" ); + Con::errorf( "Program %s: %s", file.getFullPath().c_str(), log ); + } + } + else if ( smLogWarnings ) + Con::warnf( "Program %s: %s", file.getFullPath().c_str(), log ); + } + + return compileStatus != GL_FALSE; +} + +/// Returns our list of shader constants, the material can get this and just set the constants it knows about +const Vector& GFXGLShader::getShaderConstDesc() const +{ + return mConstants; +} + +/// Returns the alignment value for constType +U32 GFXGLShader::getAlignmentValue(const GFXShaderConstType constType) const +{ + // Alignment is the same thing as size for us. + return shaderConstTypeSize(constType); +} + +const String GFXGLShader::describeSelf() const +{ + String ret; + ret = String::ToString(" Program: %i", mProgram); + ret += String::ToString(" Vertex Path: %s", mVertexFile.getFullPath().c_str()); + ret += String::ToString(" Pixel Path: %s", mPixelFile.getFullPath().c_str()); + + return ret; +} diff --git a/Engine/source/gfx/gl/gfxGLShader.h b/Engine/source/gfx/gl/gfxGLShader.h new file mode 100644 index 000000000..a8efd4e02 --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLShader.h @@ -0,0 +1,157 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXGLSHADER_H_ +#define _GFXGLSHADER_H_ + +#include "core/util/refBase.h" +#include "gfx/gfxShader.h" +#include "gfx/gl/ggl/ggl.h" +#include "core/util/tSignal.h" +#include "core/util/tDictionary.h" + +class GFXGLShaderConstHandle; +class FileStream; +class GFXGLShaderConstBuffer; + +class GFXGLShader : public GFXShader +{ + typedef Map HandleMap; +public: + GFXGLShader(); + virtual ~GFXGLShader(); + + /// @name GFXShader interface + /// @{ + virtual GFXShaderConstHandle* getShaderConstHandle(const String& name); + + /// Returns our list of shader constants, the material can get this and just set the constants it knows about + virtual const Vector& getShaderConstDesc() const; + + /// Returns the alignment value for constType + virtual U32 getAlignmentValue(const GFXShaderConstType constType) const; + + virtual GFXShaderConstBufferRef allocConstBuffer(); + + /// @} + + /// @name GFXResource interface + /// @{ + virtual void zombify(); + virtual void resurrect() { reload(); } + virtual const String describeSelf() const; + /// @} + + /// Activates this shader in the GL context. + void useProgram(); + +protected: + + friend class GFXGLShaderConstBuffer; + friend class GFXGLShaderConstHandle; + + virtual bool _init(); + + bool initShader( const Torque::Path &file, + bool isVertex, + const Vector ¯os ); + + void clearShaders(); + void initConstantDescs(); + void initHandles(); + void setConstantsFromBuffer(GFXGLShaderConstBuffer* buffer); + + static char* _handleIncludes( const Torque::Path &path, FileStream *s ); + + static bool _loadShaderFromStream( GLuint shader, + const Torque::Path& path, + FileStream* s, + const Vector& macros ); + + /// @name Internal GL handles + /// @{ + GLuint mVertexShader; + GLuint mPixelShader; + GLuint mProgram; + /// @} + + Vector mConstants; + U32 mConstBufferSize; + U8* mConstBuffer; + HandleMap mHandles; + Vector mValidHandles; +}; + +class GFXGLShaderConstBuffer : public GFXShaderConstBuffer +{ +public: + GFXGLShaderConstBuffer(GFXGLShader* shader, U32 bufSize, U8* existingConstants); + ~GFXGLShaderConstBuffer(); + + /// Called by GFXGLDevice to activate this buffer. + void activate(); + + /// Called when the shader this buffer references is reloaded. + void onShaderReload( GFXGLShader *shader ); + + // GFXShaderConstBuffer + virtual GFXShader* getShader() { return mShader; } + virtual void set(GFXShaderConstHandle* handle, const F32 fv); + virtual void set(GFXShaderConstHandle* handle, const Point2F& fv); + virtual void set(GFXShaderConstHandle* handle, const Point3F& fv); + virtual void set(GFXShaderConstHandle* handle, const Point4F& fv); + virtual void set(GFXShaderConstHandle* handle, const PlaneF& fv); + virtual void set(GFXShaderConstHandle* handle, const ColorF& fv); + virtual void set(GFXShaderConstHandle* handle, const S32 f); + virtual void set(GFXShaderConstHandle* handle, const Point2I& fv); + virtual void set(GFXShaderConstHandle* handle, const Point3I& fv); + virtual void set(GFXShaderConstHandle* handle, const Point4I& fv); + virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv); + virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv); + virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv); + virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv); + virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv); + virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv); + virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv); + virtual void set(GFXShaderConstHandle* handle, const AlignedArray& fv); + virtual void set(GFXShaderConstHandle* handle, const MatrixF& mat, const GFXShaderConstType matType = GFXSCT_Float4x4); + virtual void set(GFXShaderConstHandle* handle, const MatrixF* mat, const U32 arraySize, const GFXShaderConstType matrixType = GFXSCT_Float4x4); + + // GFXResource + virtual const String describeSelf() const; + virtual void zombify() {} + virtual void resurrect() {} + +private: + + friend class GFXGLShader; + U8* mBuffer; + WeakRefPtr mShader; + + template + void internalSet(GFXShaderConstHandle* handle, const ConstType& param); + + template + void internalSet(GFXShaderConstHandle* handle, const AlignedArray& fv); +}; + +#endif // _GFXGLSHADER_H_ \ No newline at end of file diff --git a/Engine/source/gfx/gl/gfxGLStateBlock.cpp b/Engine/source/gfx/gl/gfxGLStateBlock.cpp new file mode 100644 index 000000000..b6fb99f72 --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLStateBlock.cpp @@ -0,0 +1,187 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/gl/gfxGLStateBlock.h" +#include "gfx/gl/gfxGLDevice.h" +#include "gfx/gl/gfxGLEnumTranslate.h" +#include "gfx/gl/gfxGLUtils.h" +#include "gfx/gl/gfxGLTextureObject.h" + + +GFXGLStateBlock::GFXGLStateBlock(const GFXStateBlockDesc& desc) : + mDesc(desc), + mCachedHashValue(desc.getHashValue()) +{ +} + +GFXGLStateBlock::~GFXGLStateBlock() +{ +} + +/// Returns the hash value of the desc that created this block +U32 GFXGLStateBlock::getHashValue() const +{ + return mCachedHashValue; +} + +/// Returns a GFXStateBlockDesc that this block represents +const GFXStateBlockDesc& GFXGLStateBlock::getDesc() const +{ + return mDesc; +} + +/// Called by OpenGL device to active this state block. +/// @param oldState The current state, used to make sure we don't set redundant states on the device. Pass NULL to reset all states. +void GFXGLStateBlock::activate(const GFXGLStateBlock* oldState) +{ + // Big scary warning copied from Apple docs + // http://developer.apple.com/documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/opengl_performance/chapter_13_section_2.html#//apple_ref/doc/uid/TP40001987-CH213-SW12 + // Don't set a state that's already set. Once a feature is enabled, it does not need to be enabled again. + // Calling an enable function more than once does nothing except waste time because OpenGL does not check + // the state of a feature when you call glEnable or glDisable. For instance, if you call glEnable(GL_LIGHTING) + // more than once, OpenGL does not check to see if the lighting state is already enabled. It simply updates + // the state value even if that value is identical to the current value. + +#define STATE_CHANGE(state) (!oldState || oldState->mDesc.state != mDesc.state) +#define TOGGLE_STATE(state, enum) if(mDesc.state) glEnable(enum); else glDisable(enum) +#define CHECK_TOGGLE_STATE(state, enum) if(!oldState || oldState->mDesc.state != mDesc.state) if(mDesc.state) glEnable(enum); else glDisable(enum) + + // Blending + CHECK_TOGGLE_STATE(blendEnable, GL_BLEND); + if(STATE_CHANGE(blendSrc) || STATE_CHANGE(blendDest)) + glBlendFunc(GFXGLBlend[mDesc.blendSrc], GFXGLBlend[mDesc.blendDest]); + if(STATE_CHANGE(blendOp)) + glBlendEquation(GFXGLBlendOp[mDesc.blendOp]); + + // Alpha testing + CHECK_TOGGLE_STATE(alphaTestEnable, GL_ALPHA_TEST); + if(STATE_CHANGE(alphaTestFunc) || STATE_CHANGE(alphaTestRef)) + glAlphaFunc(GFXGLCmpFunc[mDesc.alphaTestFunc], (F32) mDesc.alphaTestRef * 1.0f/255.0f); + + // Color write masks + if(STATE_CHANGE(colorWriteRed) || STATE_CHANGE(colorWriteBlue) || STATE_CHANGE(colorWriteGreen) || STATE_CHANGE(colorWriteAlpha)) + glColorMask(mDesc.colorWriteRed, mDesc.colorWriteBlue, mDesc.colorWriteGreen, mDesc.colorWriteAlpha); + + // Culling + if(STATE_CHANGE(cullMode)) + { + TOGGLE_STATE(cullMode, GL_CULL_FACE); + glCullFace(GFXGLCullMode[mDesc.cullMode]); + } + + // Depth + CHECK_TOGGLE_STATE(zEnable, GL_DEPTH_TEST); + + if(STATE_CHANGE(zFunc)) + glDepthFunc(GFXGLCmpFunc[mDesc.zFunc]); + + if(STATE_CHANGE(zBias)) + { + if (mDesc.zBias == 0) + { + glDisable(GL_POLYGON_OFFSET_FILL); + } else { + F32 bias = mDesc.zBias * 10000.0f; + glEnable(GL_POLYGON_OFFSET_FILL); + glPolygonOffset(bias, bias); + } + } + + if(STATE_CHANGE(zWriteEnable)) + glDepthMask(mDesc.zWriteEnable); + + // Stencil + CHECK_TOGGLE_STATE(stencilEnable, GL_STENCIL_TEST); + if(STATE_CHANGE(stencilFunc) || STATE_CHANGE(stencilRef) || STATE_CHANGE(stencilMask)) + glStencilFunc(GFXGLCmpFunc[mDesc.stencilFunc], mDesc.stencilRef, mDesc.stencilMask); + if(STATE_CHANGE(stencilFailOp) || STATE_CHANGE(stencilZFailOp) || STATE_CHANGE(stencilPassOp)) + glStencilOp(GFXGLStencilOp[mDesc.stencilFailOp], GFXGLStencilOp[mDesc.stencilZFailOp], GFXGLStencilOp[mDesc.stencilPassOp]); + if(STATE_CHANGE(stencilWriteMask)) + glStencilMask(mDesc.stencilWriteMask); + + // "Misc" + CHECK_TOGGLE_STATE(ffLighting, GL_LIGHTING); + + glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); + CHECK_TOGGLE_STATE(vertexColorEnable, GL_COLOR_MATERIAL); + + if(STATE_CHANGE(fillMode)) + glPolygonMode(GL_FRONT_AND_BACK, GFXGLFillMode[mDesc.fillMode]); + +#undef CHECK_STATE +#undef TOGGLE_STATE +#undef CHECK_TOGGLE_STATE + + // TODO: states added for detail blend + + // Non per object texture mode states + for (U32 i = 0; i < getMin(getOwningDevice()->getNumSamplers(), (U32) TEXTURE_STAGE_COUNT); i++) + { + GFXGLTextureObject* tex = static_cast(getOwningDevice()->getCurrentTexture(i)); + const GFXSamplerStateDesc &ssd = mDesc.samplers[i]; + bool updateTexParam = true; + glActiveTexture(GL_TEXTURE0 + i); + switch (ssd.textureColorOp) + { + case GFXTOPDisable : + if(!tex) + break; + glDisable(GL_TEXTURE_2D); + updateTexParam = false; + break; + case GFXTOPModulate : + glEnable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + break; + case GFXTOPAdd : + glEnable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD); + break; + default : + glEnable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + break; + } + +#define SSF(state, enum, value, tex) if(!oldState || oldState->mDesc.samplers[i].state != mDesc.samplers[i].state) glTexParameteri(tex->getBinding(), enum, value) +#define SSW(state, enum, value, tex) if(!oldState || oldState->mDesc.samplers[i].state != mDesc.samplers[i].state) glTexParameteri(tex->getBinding(), enum, !tex->mIsNPoT2 ? value : GL_CLAMP_TO_EDGE) + // Per object texture mode states. + // TODO: Check dirty flag of samplers[i] and don't do this if it's dirty (it'll happen in the texture bind) + if (updateTexParam && tex) + { + SSF(minFilter, GL_TEXTURE_MIN_FILTER, minificationFilter(ssd.minFilter, ssd.mipFilter, tex->mMipLevels), tex); + SSF(mipFilter, GL_TEXTURE_MIN_FILTER, minificationFilter(ssd.minFilter, ssd.mipFilter, tex->mMipLevels), tex); + SSF(magFilter, GL_TEXTURE_MAG_FILTER, GFXGLTextureFilter[ssd.magFilter], tex); + SSW(addressModeU, GL_TEXTURE_WRAP_S, GFXGLTextureAddress[ssd.addressModeU], tex); + SSW(addressModeV, GL_TEXTURE_WRAP_T, GFXGLTextureAddress[ssd.addressModeV], tex); + + if( ( !oldState || oldState->mDesc.samplers[i].maxAnisotropy != ssd.maxAnisotropy ) && + static_cast< GFXGLDevice* >( GFX )->supportsAnisotropic() ) + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, ssd.maxAnisotropy); + + if( ( !oldState || oldState->mDesc.samplers[i].mipLODBias != ssd.mipLODBias ) ) + glTexEnvf(GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, ssd.mipLODBias); + } + } +#undef SSF +#undef SSW +} diff --git a/Engine/source/gfx/gl/gfxGLStateBlock.h b/Engine/source/gfx/gl/gfxGLStateBlock.h new file mode 100644 index 000000000..2e7e793ae --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLStateBlock.h @@ -0,0 +1,65 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXGLSTATEBLOCK_H_ +#define _GFXGLSTATEBLOCK_H_ + +#include "gfx/gfxStateBlock.h" + +class GFXGLStateBlock : public GFXStateBlock +{ +public: + // + // GFXGLStateBlock interface + // + GFXGLStateBlock(const GFXStateBlockDesc& desc); + virtual ~GFXGLStateBlock(); + + /// Called by OpenGL device to active this state block. + /// @param oldState The current state, used to make sure we don't set redundant states on the device. Pass NULL to reset all states. + void activate(const GFXGLStateBlock* oldState); + + + // + // GFXStateBlock interface + // + + /// Returns the hash value of the desc that created this block + virtual U32 getHashValue() const; + + /// Returns a GFXStateBlockDesc that this block represents + virtual const GFXStateBlockDesc& getDesc() const; + + // + // GFXResource + // + virtual void zombify() { } + /// When called the resource should restore all device sensitive information destroyed by zombify() + virtual void resurrect() { } +private: + GFXStateBlockDesc mDesc; + U32 mCachedHashValue; +}; + +typedef StrongRefPtr GFXGLStateBlockRef; + +#endif \ No newline at end of file diff --git a/Engine/source/gfx/gl/gfxGLTextureManager.cpp b/Engine/source/gfx/gl/gfxGLTextureManager.cpp new file mode 100644 index 000000000..f2bc4de0b --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLTextureManager.cpp @@ -0,0 +1,333 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/gl/gfxGLTextureManager.h" +#include "gfx/gl/gfxGLEnumTranslate.h" +#include "gfx/gfxCardProfile.h" +#include "core/util/safeDelete.h" +#include "gfx/gl/gfxGLUtils.h" + +#include + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +GFXGLTextureManager::GFXGLTextureManager() +{ +} + +//----------------------------------------------------------------------------- +// Destructor +//----------------------------------------------------------------------------- +GFXGLTextureManager::~GFXGLTextureManager() +{ + SAFE_DELETE_ARRAY( mHashTable ); +} + +//----------------------------------------------------------------------------- +// createTexture +//----------------------------------------------------------------------------- +GFXTextureObject *GFXGLTextureManager::_createTextureObject( U32 height, + U32 width, + U32 depth, + GFXFormat format, + GFXTextureProfile *profile, + U32 numMipLevels, + bool forceMips, + S32 antialiasLevel, + GFXTextureObject *inTex ) +{ + AssertFatal(format >= 0 && format < GFXFormat_COUNT, "GFXGLTextureManager::_createTexture - invalid format!"); + + GFXGLTextureObject *retTex; + if ( inTex ) + { + AssertFatal( dynamic_cast( inTex ), "GFXGLTextureManager::_createTexture() - Bad inTex type!" ); + retTex = static_cast( inTex ); + retTex->release(); + } + else + { + retTex = new GFXGLTextureObject( GFX, profile ); + retTex->registerResourceWithDevice( GFX ); + } + + innerCreateTexture(retTex, height, width, depth, format, profile, numMipLevels, forceMips); + + return retTex; +} + +//----------------------------------------------------------------------------- +// innerCreateTexture +//----------------------------------------------------------------------------- +// This just creates the texture, no info is actually loaded to it. We do that later. +void GFXGLTextureManager::innerCreateTexture( GFXGLTextureObject *retTex, + U32 height, + U32 width, + U32 depth, + GFXFormat format, + GFXTextureProfile *profile, + U32 numMipLevels, + bool forceMips) +{ + // No 24 bit formats. They trigger various oddities because hardware (and Apple's drivers apparently...) don't natively support them. + if(format == GFXFormatR8G8B8) + format = GFXFormatR8G8B8A8; + + retTex->mFormat = format; + retTex->mIsZombie = false; + retTex->mIsNPoT2 = false; + + GLenum binding = (depth == 0) ? GL_TEXTURE_2D : GL_TEXTURE_3D; + if((profile->testFlag(GFXTextureProfile::RenderTarget) || profile->testFlag(GFXTextureProfile::ZTarget)) && (!isPow2(width) || !isPow2(height)) && !depth) + retTex->mIsNPoT2 = true; + retTex->mBinding = binding; + + // Bind it + glActiveTexture(GL_TEXTURE0); + PRESERVE_2D_TEXTURE(); + PRESERVE_3D_TEXTURE(); + glBindTexture(binding, retTex->getHandle()); + + // Create it + // TODO: Reenable mipmaps on render targets when Apple fixes their drivers + if(forceMips && !retTex->mIsNPoT2) + { + glTexParameteri(binding, GL_GENERATE_MIPMAP_SGIS, GL_TRUE); + retTex->mMipLevels = 0; + } + else if(profile->testFlag(GFXTextureProfile::NoMipmap) || profile->testFlag(GFXTextureProfile::RenderTarget) || numMipLevels == 1 || retTex->mIsNPoT2) + { + retTex->mMipLevels = 1; + } + else + { + glTexParameteri(binding, GL_GENERATE_MIPMAP_SGIS, GL_TRUE); + retTex->mMipLevels = 0; + } + + if(!retTex->mIsNPoT2) + { + if(!isPow2(width)) + width = getNextPow2(width); + if(!isPow2(height)) + height = getNextPow2(height); + if(depth && !isPow2(depth)) + depth = getNextPow2(depth); + } + + AssertFatal(GFXGLTextureInternalFormat[format] != GL_ZERO, "GFXGLTextureManager::innerCreateTexture - invalid internal format"); + AssertFatal(GFXGLTextureFormat[format] != GL_ZERO, "GFXGLTextureManager::innerCreateTexture - invalid format"); + AssertFatal(GFXGLTextureType[format] != GL_ZERO, "GFXGLTextureManager::innerCreateTexture - invalid type"); + + if(binding != GL_TEXTURE_3D) + glTexImage2D(binding, 0, GFXGLTextureInternalFormat[format], width, height, 0, GFXGLTextureFormat[format], GFXGLTextureType[format], NULL); + else + glTexImage3D(GL_TEXTURE_3D, 0, GFXGLTextureInternalFormat[format], width, height, depth, 0, GFXGLTextureFormat[format], GFXGLTextureType[format], NULL); + + // Complete the texture + glTexParameteri(binding, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(binding, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(binding, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(binding, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if(binding == GL_TEXTURE_3D) + glTexParameteri(binding, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + // Get the size from GL (you never know...) + GLint texHeight, texWidth, texDepth = 0; + + glGetTexLevelParameteriv(binding, 0, GL_TEXTURE_WIDTH, &texWidth); + glGetTexLevelParameteriv(binding, 0, GL_TEXTURE_HEIGHT, &texHeight); + if(binding == GL_TEXTURE_3D) + glGetTexLevelParameteriv(binding, 0, GL_TEXTURE_DEPTH, &texDepth); + + retTex->mTextureSize.set(texWidth, texHeight, texDepth); +} + +//----------------------------------------------------------------------------- +// loadTexture - GBitmap +//----------------------------------------------------------------------------- + +static void _fastTextureLoad(GFXGLTextureObject* texture, GBitmap* pDL) +{ + glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, texture->getBuffer()); + U32 bufSize = pDL->getWidth(0) * pDL->getHeight(0) * pDL->getBytesPerPixel(); + glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB, bufSize, NULL, GL_STREAM_DRAW); + U8* pboMemory = (U8*)glMapBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY); + + if(pDL->getFormat() == GFXFormatR8G8B8A8 || pDL->getFormat() == GFXFormatR8G8B8X8) + GFX->getDeviceSwizzle32()->ToBuffer(pboMemory, pDL->getBits(0), bufSize); + else + dMemcpy(pboMemory, pDL->getBits(0), bufSize); + + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER_ARB); + + glTexSubImage2D(texture->getBinding(), 0, 0, 0, pDL->getWidth(0), pDL->getHeight(0), GFXGLTextureFormat[pDL->getFormat()], GFXGLTextureType[pDL->getFormat()], NULL); + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0); +} + +static void _slowTextureLoad(GFXGLTextureObject* texture, GBitmap* pDL) +{ + glTexSubImage2D(texture->getBinding(), 0, 0, 0, pDL->getWidth(0), pDL->getHeight(0), GFXGLTextureFormat[pDL->getFormat()], GFXGLTextureType[pDL->getFormat()], pDL->getBits(0)); +} + +bool GFXGLTextureManager::_loadTexture(GFXTextureObject *aTexture, GBitmap *pDL) +{ + GFXGLTextureObject *texture = static_cast(aTexture); + + AssertFatal(texture->getBinding() == GL_TEXTURE_2D, + "GFXGLTextureManager::_loadTexture(GBitmap) - This method can only be used with 2D textures"); + + if(texture->getBinding() != GL_TEXTURE_2D) + return false; + + // No 24bit formats. + if(pDL->getFormat() == GFXFormatR8G8B8) + pDL->setFormat(GFXFormatR8G8B8A8); + // Bind to edit + glActiveTexture(GL_TEXTURE0); + PRESERVE_2D_TEXTURE(); + glBindTexture(texture->getBinding(), texture->getHandle()); + + if(pDL->getFormat() == GFXFormatR8G8B8A8 || pDL->getFormat() == GFXFormatR8G8B8X8) + _fastTextureLoad(texture, pDL); + else + _slowTextureLoad(texture, pDL); + + glBindTexture(texture->getBinding(), 0); + + return true; +} + +bool GFXGLTextureManager::_loadTexture(GFXTextureObject *aTexture, DDSFile *dds) +{ + AssertFatal(!(dds->mFormat == GFXFormatDXT2 || dds->mFormat == GFXFormatDXT4), "GFXGLTextureManager::_loadTexture - OpenGL does not support DXT2 or DXT4 compressed textures"); + GFXGLTextureObject* texture = static_cast(aTexture); + + AssertFatal(texture->getBinding() == GL_TEXTURE_2D, + "GFXGLTextureManager::_loadTexture(DDSFile) - This method can only be used with 2D textures"); + + if(texture->getBinding() != GL_TEXTURE_2D) + return false; + + glActiveTexture(GL_TEXTURE0); + PRESERVE_2D_TEXTURE(); + glBindTexture(texture->getBinding(), texture->getHandle()); + U32 numMips = dds->mSurfaces[0]->mMips.size(); + if(GFX->getCardProfiler()->queryProfile("GL::Workaround::noManualMips")) + numMips = 1; + for(U32 i = 0; i < numMips; i++) + { + if(dds->mFormat == GFXFormatDXT1 || dds->mFormat == GFXFormatDXT3 || dds->mFormat == GFXFormatDXT5) + { + if((!isPow2(dds->getWidth()) || !isPow2(dds->getHeight())) && GFX->getCardProfiler()->queryProfile("GL::Workaround::noCompressedNPoTTextures")) + { + U32 squishFlag = squish::kDxt1; + switch (dds->mFormat) + { + case GFXFormatDXT3: + squishFlag = squish::kDxt3; + break; + case GFXFormatDXT5: + squishFlag = squish::kDxt5; + break; + default: + break; + } + U8* uncompressedTex = new U8[dds->getWidth(i) * dds->getHeight(i) * 4]; + squish::DecompressImage(uncompressedTex, dds->getWidth(i), dds->getHeight(i), dds->mSurfaces[0]->mMips[i], squishFlag); + glTexSubImage2D(texture->getBinding(), i, 0, 0, dds->getWidth(i), dds->getHeight(i), GL_RGBA, GL_UNSIGNED_BYTE, uncompressedTex); + delete[] uncompressedTex; + } + else + glCompressedTexSubImage2D(texture->getBinding(), i, 0, 0, dds->getWidth(i), dds->getHeight(i), GFXGLTextureInternalFormat[dds->mFormat], dds->getSurfaceSize(dds->getHeight(), dds->getWidth(), i), dds->mSurfaces[0]->mMips[i]); + } + else + glTexSubImage2D(texture->getBinding(), i, 0, 0, dds->getWidth(i), dds->getHeight(i), GFXGLTextureFormat[dds->mFormat], GFXGLTextureType[dds->mFormat], dds->mSurfaces[0]->mMips[i]); + } + glBindTexture(texture->getBinding(), 0); + + return true; +} + +bool GFXGLTextureManager::_loadTexture(GFXTextureObject *aTexture, void *raw) +{ + if(aTexture->getDepth() < 1) + return false; + + GFXGLTextureObject* texture = static_cast(aTexture); + + glActiveTexture(GL_TEXTURE0); + PRESERVE_3D_TEXTURE(); + glBindTexture(GL_TEXTURE_3D, texture->getHandle()); + glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, texture->getWidth(), texture->getHeight(), texture->getDepth(), GFXGLTextureFormat[texture->mFormat], GFXGLTextureType[texture->mFormat], raw); + glBindTexture(GL_TEXTURE_3D, 0); + + return true; +} + +bool GFXGLTextureManager::_freeTexture(GFXTextureObject *texture, bool zombify /*= false*/) +{ + if(zombify) + static_cast(texture)->zombify(); + else + static_cast(texture)->release(); + + return true; +} + +bool GFXGLTextureManager::_refreshTexture(GFXTextureObject *texture) +{ + U32 usedStrategies = 0; + GFXGLTextureObject* realTex = static_cast(texture); + + if(texture->mProfile->doStoreBitmap()) + { + if(realTex->isZombie()) + { + realTex->resurrect(); + innerCreateTexture(realTex, texture->getHeight(), texture->getWidth(), texture->getDepth(), texture->mFormat, texture->mProfile, texture->mMipLevels); + } + if(texture->mBitmap) + _loadTexture(texture, texture->mBitmap); + + if(texture->mDDS) + return false; + + usedStrategies++; + } + + if(texture->mProfile->isRenderTarget() || texture->mProfile->isDynamic() || texture->mProfile->isZTarget() || !usedStrategies) + { + realTex->release(); + realTex->resurrect(); + innerCreateTexture(realTex, texture->getHeight(), texture->getWidth(), texture->getDepth(), texture->mFormat, texture->mProfile, texture->mMipLevels); + realTex->reloadFromCache(); + usedStrategies++; + } + + AssertFatal(usedStrategies < 2, "GFXGLTextureManager::_refreshTexture - Inconsistent profile flags (store bitmap and dynamic/target"); + + return true; +} diff --git a/Engine/source/gfx/gl/gfxGLTextureManager.h b/Engine/source/gfx/gl/gfxGLTextureManager.h new file mode 100644 index 000000000..5ad7ac29a --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLTextureManager.h @@ -0,0 +1,62 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXGLTEXTUREMANAGER_H +#define _GFXGLTEXTUREMANAGER_H + +#include "gfx/gfxDevice.h" +#include "gfx/gfxTextureManager.h" +#include "gfx/gl/gfxGLTextureObject.h" +#include "gfx/gl/ggl/ggl.h" + +class GFXGLTextureManager : public GFXTextureManager +{ +public: + GFXGLTextureManager(); + ~GFXGLTextureManager(); + +protected: + + // GFXTextureManager + GFXTextureObject *_createTextureObject( U32 height, + U32 width, + U32 depth, + GFXFormat format, + GFXTextureProfile *profile, + U32 numMipLevels, + bool forceMips = false, + S32 antialiasLevel = 0, + GFXTextureObject *inTex = NULL ); + bool _loadTexture(GFXTextureObject *texture, DDSFile *dds); + bool _loadTexture(GFXTextureObject *texture, GBitmap *bmp); + bool _loadTexture(GFXTextureObject *texture, void *raw); + bool _refreshTexture(GFXTextureObject *texture); + bool _freeTexture(GFXTextureObject *texture, bool zombify = false); + +private: + friend class GFXGLTextureObject; + + /// Creates internal GL texture + void innerCreateTexture(GFXGLTextureObject *obj, U32 height, U32 width, U32 depth, GFXFormat format, GFXTextureProfile *profile, U32 numMipLevels, bool forceMips = false); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/gfx/gl/gfxGLTextureObject.cpp b/Engine/source/gfx/gl/gfxGLTextureObject.cpp new file mode 100644 index 000000000..eaf76ba80 --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLTextureObject.cpp @@ -0,0 +1,238 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/console.h" +#include "gfx/gl/ggl/ggl.h" +#include "math/mRect.h" +#include "gfx/gl/gfxGLTextureObject.h" +#include "gfx/gfxDevice.h" +#include "gfx/gl/gfxGLEnumTranslate.h" +#include "gfx/gl/gfxGLTextureManager.h" +#include "gfx/gl/gfxGLUtils.h" +#include "gfx/gfxCardProfile.h" + + +GFXGLTextureObject::GFXGLTextureObject(GFXDevice * aDevice, GFXTextureProfile *profile) : + GFXTextureObject(aDevice, profile), + mBinding(GL_TEXTURE_2D), + mBytesPerTexel(4), + mLockedRectRect(0, 0, 0, 0), + mGLDevice(static_cast(mDevice)), + mZombieCache(NULL) +{ + AssertFatal(dynamic_cast(mDevice), "GFXGLTextureObject::GFXGLTextureObject - Invalid device type, expected GFXGLDevice!"); + glGenTextures(1, &mHandle); + glGenBuffers(1, &mBuffer); +} + +GFXGLTextureObject::~GFXGLTextureObject() +{ + glDeleteBuffers(1, &mBuffer); + delete[] mZombieCache; + kill(); +} + +GFXLockedRect* GFXGLTextureObject::lock(U32 mipLevel, RectI *inRect) +{ + AssertFatal(mBinding != GL_TEXTURE_3D, "GFXGLTextureObject::lock - We don't support locking 3D textures yet"); + U32 width = mTextureSize.x >> mipLevel; + U32 height = mTextureSize.y >> mipLevel; + + if(inRect) + { + if((inRect->point.x + inRect->extent.x > width) || (inRect->point.y + inRect->extent.y > height)) + AssertFatal(false, "GFXGLTextureObject::lock - Rectangle too big!"); + + mLockedRectRect = *inRect; + } + else + { + mLockedRectRect = RectI(0, 0, width, height); + } + + mLockedRect.pitch = mLockedRectRect.extent.x * mBytesPerTexel; + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, mBuffer); + // CodeReview [ags 12/19/07] This one texel boundary is necessary to keep the clipmap code from crashing. Figure out why. + glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB, (mLockedRectRect.extent.x + 1) * (mLockedRectRect.extent.y + 1) * mBytesPerTexel, NULL, GL_STREAM_DRAW); + mLockedRect.bits = (U8*)glMapBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0); + + if( !mLockedRect.bits ) + return NULL; + + return &mLockedRect; +} + +void GFXGLTextureObject::unlock(U32 mipLevel) +{ + if(!mLockedRect.bits) + return; + + glActiveTexture(GL_TEXTURE0); + U32 boundTexture; + glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&boundTexture); + + glBindTexture(GL_TEXTURE_2D, mHandle); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, mBuffer); + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER_ARB); + glTexSubImage2D(GL_TEXTURE_2D, mipLevel, mLockedRectRect.point.x, mLockedRectRect.point.y, + mLockedRectRect.extent.x, mLockedRectRect.extent.y, GL_BGRA, GL_UNSIGNED_BYTE, NULL); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0); + + mLockedRect.bits = NULL; + + glBindTexture(GL_TEXTURE_2D, boundTexture); +} + +void GFXGLTextureObject::release() +{ + glDeleteTextures(1, &mHandle); + glDeleteBuffers(1, &mBuffer); + + mHandle = 0; + mBuffer = 0; +} + +bool GFXGLTextureObject::copyToBmp(GBitmap * bmp) +{ + GLint oldTex; + glGetIntegerv(0x8069, &oldTex); + glBindTexture(GL_TEXTURE_2D, mHandle); + + GLint textureFormat = GFXGLTextureFormat[bmp->getFormat()]; + // Don't swizzle outgoing textures. + if(textureFormat == GL_BGRA) + textureFormat = GL_RGBA; + + glGetTexImage(GL_TEXTURE_2D, 0, textureFormat, GL_UNSIGNED_BYTE, bmp->getWritableBits()); + + glBindTexture(GL_TEXTURE_2D, oldTex); + return true; +} + +void GFXGLTextureObject::bind(U32 textureUnit) const +{ + glActiveTexture(GL_TEXTURE0 + textureUnit); + glBindTexture(mBinding, mHandle); + glEnable(mBinding); + + GFXGLStateBlockRef sb = mGLDevice->getCurrentStateBlock(); + AssertFatal(sb, "GFXGLTextureObject::bind - No active stateblock!"); + if (!sb) + return; + + const GFXSamplerStateDesc ssd = sb->getDesc().samplers[textureUnit]; + glTexParameteri(mBinding, GL_TEXTURE_MIN_FILTER, minificationFilter(ssd.minFilter, ssd.mipFilter, mMipLevels)); + glTexParameteri(mBinding, GL_TEXTURE_MAG_FILTER, GFXGLTextureFilter[ssd.magFilter]); + glTexParameteri(mBinding, GL_TEXTURE_WRAP_S, !mIsNPoT2 ? GFXGLTextureAddress[ssd.addressModeU] : GL_CLAMP_TO_EDGE); + glTexParameteri(mBinding, GL_TEXTURE_WRAP_T, !mIsNPoT2 ? GFXGLTextureAddress[ssd.addressModeV] : GL_CLAMP_TO_EDGE); + if(mBinding == GL_TEXTURE_3D) + glTexParameteri(mBinding, GL_TEXTURE_WRAP_R, GFXGLTextureAddress[ssd.addressModeW]); + + glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, ssd.mipLODBias); +} + +U8* GFXGLTextureObject::getTextureData() +{ + U8* data = new U8[mTextureSize.x * mTextureSize.y * mBytesPerTexel]; + glBindTexture(GL_TEXTURE_2D, mHandle); + glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data); + return data; +} + +void GFXGLTextureObject::copyIntoCache() +{ + glBindTexture(mBinding, mHandle); + U32 cacheSize = mTextureSize.x * mTextureSize.y; + if(mBinding == GL_TEXTURE_3D) + cacheSize *= mTextureSize.z; + + cacheSize *= mBytesPerTexel; + mZombieCache = new U8[cacheSize]; + + glGetTexImage(mBinding, 0, GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], mZombieCache); + glBindTexture(mBinding, 0); +} + +void GFXGLTextureObject::reloadFromCache() +{ + if(!mZombieCache) + return; + + if(mBinding == GL_TEXTURE_3D) + { + static_cast(TEXMGR)->_loadTexture(this, mZombieCache); + delete[] mZombieCache; + mZombieCache = NULL; + return; + } + + glBindTexture(mBinding, mHandle); + glTexSubImage2D(mBinding, 0, 0, 0, mTextureSize.x, mTextureSize.y, GFXGLTextureFormat[mFormat], GFXGLTextureType[mFormat], mZombieCache); + + if(GFX->getCardProfiler()->queryProfile("GL::Workaround::needsExplicitGenerateMipmap") && mMipLevels != 1) + glGenerateMipmapEXT(mBinding); + + delete[] mZombieCache; + mZombieCache = NULL; + mIsZombie = false; +} + +void GFXGLTextureObject::zombify() +{ + if(mIsZombie) + return; + + mIsZombie = true; + if(!mProfile->doStoreBitmap() && !mProfile->isRenderTarget() && !mProfile->isDynamic() && !mProfile->isZTarget()) + copyIntoCache(); + + release(); +} + +void GFXGLTextureObject::resurrect() +{ + if(!mIsZombie) + return; + + glGenTextures(1, &mHandle); + glGenBuffers(1, &mBuffer); +} + +F32 GFXGLTextureObject::getMaxUCoord() const +{ + return mBinding == GL_TEXTURE_2D ? 1.0f : (F32)getWidth(); +} + +F32 GFXGLTextureObject::getMaxVCoord() const +{ + return mBinding == GL_TEXTURE_2D ? 1.0f : (F32)getHeight(); +} + +const String GFXGLTextureObject::describeSelf() const +{ + String ret = Parent::describeSelf(); + ret += String::ToString(" GL Handle: %i", mHandle); + + return ret; +} diff --git a/Engine/source/gfx/gl/gfxGLTextureObject.h b/Engine/source/gfx/gl/gfxGLTextureObject.h new file mode 100644 index 000000000..586dd8a93 --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLTextureObject.h @@ -0,0 +1,99 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXGLTEXTUREOBJECT_H +#define _GFXGLTEXTUREOBJECT_H + +#include "gfx/gfxTextureObject.h" +#include "gfx/gl/ggl/ggl.h" + +class GFXGLDevice; + +class GFXGLTextureObject : public GFXTextureObject +{ +public: + GFXGLTextureObject(GFXDevice * aDevice, GFXTextureProfile *profile); + virtual ~GFXGLTextureObject(); + + void release(); + + inline GLuint getHandle() const { return mHandle; } + inline GLenum getBinding() const { return mBinding; } + inline GLuint getBuffer() const { return mBuffer; } + + inline bool isZombie() const { return mIsZombie; } + + /// Binds the texture to the given texture unit + /// and applies the current sampler state because GL tracks + /// filtering and wrapper per object, while GFX tracks per sampler. + void bind(U32 textureUnit) const; + + /// @return An array containing the texture data + /// @note You are responsible for deleting the returned data! (Use delete[]) + U8* getTextureData(); + + virtual F32 getMaxUCoord() const; + virtual F32 getMaxVCoord() const; + + void reloadFromCache(); ///< Reloads texture from zombie cache, used by GFXGLTextureManager to resurrect the texture. + +#ifdef TORQUE_DEBUG + virtual void pureVirtualCrash() {} +#endif + + /// Get/set data from texture (for dynamic textures and render targets) + /// @attention DO NOT READ FROM THE RETURNED RECT! It is not guaranteed to work and may incur significant performance penalties. + virtual GFXLockedRect* lock(U32 mipLevel = 0, RectI *inRect = NULL); + virtual void unlock(U32 mipLevel = 0 ); + + virtual bool copyToBmp(GBitmap *); ///< Not implemented + + bool mIsNPoT2; + + // GFXResource interface + virtual void zombify(); + virtual void resurrect(); + virtual const String describeSelf() const; + +private: + friend class GFXGLTextureManager; + typedef GFXTextureObject Parent; + /// Internal GL object + GLuint mHandle; + GLuint mBuffer; + + GLenum mBinding; + + U32 mBytesPerTexel; + GFXLockedRect mLockedRect; + RectI mLockedRectRect; + + /// Pointer to owner device + GFXGLDevice* mGLDevice; + + bool mIsZombie; + U8* mZombieCache; + + void copyIntoCache(); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/gfx/gl/gfxGLTextureTarget.cpp b/Engine/source/gfx/gl/gfxGLTextureTarget.cpp new file mode 100644 index 000000000..656044d57 --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLTextureTarget.cpp @@ -0,0 +1,438 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/console.h" +#include "gfx/gl/gfxGLDevice.h" +#include "gfx/gl/gfxGLTextureTarget.h" +#include "gfx/gl/gfxGLTextureObject.h" +#include "gfx/gl/gfxGLCubemap.h" +#include "gfx/gfxTextureManager.h" +#include "gfx/gl/gfxGLUtils.h" + +/// Internal struct used to track texture information for FBO attachments +/// This serves as an abstract base so we can deal with cubemaps and standard +/// 2D/Rect textures through the same interface +class _GFXGLTargetDesc +{ +public: + _GFXGLTargetDesc(U32 _mipLevel, U32 _zOffset) : + mipLevel(_mipLevel), zOffset(_zOffset) + { + } + + virtual ~_GFXGLTargetDesc() {} + + virtual U32 getHandle() = 0; + virtual U32 getWidth() = 0; + virtual U32 getHeight() = 0; + virtual U32 getDepth() = 0; + virtual bool hasMips() = 0; + virtual GLenum getBinding() = 0; + + U32 getMipLevel() { return mipLevel; } + U32 getZOffset() { return zOffset; } + +private: + U32 mipLevel; + U32 zOffset; +}; + +/// Internal struct used to track 2D/Rect texture information for FBO attachment +class _GFXGLTextureTargetDesc : public _GFXGLTargetDesc +{ +public: + _GFXGLTextureTargetDesc(GFXGLTextureObject* tex, U32 _mipLevel, U32 _zOffset) + : _GFXGLTargetDesc(_mipLevel, _zOffset), mTex(tex) + { + } + + virtual ~_GFXGLTextureTargetDesc() {} + + virtual U32 getHandle() { return mTex->getHandle(); } + virtual U32 getWidth() { return mTex->getWidth(); } + virtual U32 getHeight() { return mTex->getHeight(); } + virtual U32 getDepth() { return mTex->getDepth(); } + virtual bool hasMips() { return mTex->mMipLevels != 1; } + virtual GLenum getBinding() { return mTex->getBinding(); } + +private: + StrongRefPtr mTex; +}; + +/// Internal struct used to track Cubemap texture information for FBO attachment +class _GFXGLCubemapTargetDesc : public _GFXGLTargetDesc +{ +public: + _GFXGLCubemapTargetDesc(GFXGLCubemap* tex, U32 _face, U32 _mipLevel, U32 _zOffset) + : _GFXGLTargetDesc(_mipLevel, _zOffset), mTex(tex), mFace(_face) + { + } + + virtual ~_GFXGLCubemapTargetDesc() {} + + virtual U32 getHandle() { return mTex->getHandle(); } + virtual U32 getWidth() { return mTex->getWidth(); } + virtual U32 getHeight() { return mTex->getHeight(); } + virtual U32 getDepth() { return 0; } + virtual bool hasMips() { return mTex->getNumMipLevels() != 1; } + virtual GLenum getBinding() { return GFXGLCubemap::getEnumForFaceNumber(mFace); } + +private: + StrongRefPtr mTex; + U32 mFace; +}; + +// Internal implementations +class _GFXGLTextureTargetImpl +{ +public: + GFXGLTextureTarget* mTarget; + + virtual ~_GFXGLTextureTargetImpl() {} + + virtual void applyState() = 0; + virtual void makeActive() = 0; + virtual void finish() = 0; +}; + +// Use FBOs to render to texture. This is the preferred implementation and is almost always used. +class _GFXGLTextureTargetFBOImpl : public _GFXGLTextureTargetImpl +{ +public: + GLuint mFramebuffer; + + _GFXGLTextureTargetFBOImpl(GFXGLTextureTarget* target); + virtual ~_GFXGLTextureTargetFBOImpl(); + + virtual void applyState(); + virtual void makeActive(); + virtual void finish(); +}; + +// Handy macro for checking the status of a framebuffer. Framebuffers can fail in +// all sorts of interesting ways, these are just the most common. Further, no existing GL profiling +// tool catches framebuffer errors when the framebuffer is created, so we actually need this. +#define CHECK_FRAMEBUFFER_STATUS()\ +{\ +GLenum status;\ +status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);\ +switch(status) {\ +case GL_FRAMEBUFFER_COMPLETE_EXT:\ +break;\ +case GL_FRAMEBUFFER_UNSUPPORTED_EXT:\ +AssertFatal(false, "Unsupported FBO");\ +break;\ +case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:\ +AssertFatal(false, "Incomplete FBO Attachment");\ +break;\ +case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:\ +AssertFatal(false, "Incomplete FBO dimensions");\ +break;\ +case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:\ +AssertFatal(false, "Incomplete FBO formats");\ +default:\ +/* programming error; will fail on all hardware */\ +AssertFatal(false, "Something really bad happened with an FBO");\ +}\ +} + +_GFXGLTextureTargetFBOImpl::_GFXGLTextureTargetFBOImpl(GFXGLTextureTarget* target) +{ + mTarget = target; + glGenFramebuffersEXT(1, &mFramebuffer); +} + +_GFXGLTextureTargetFBOImpl::~_GFXGLTextureTargetFBOImpl() +{ + glDeleteFramebuffersEXT(1, &mFramebuffer); +} + +void _GFXGLTextureTargetFBOImpl::applyState() +{ + // REMINDER: When we implement MRT support, check against GFXGLDevice::getNumRenderTargets() + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, mFramebuffer); + + _GFXGLTargetDesc* color0 = mTarget->getTargetDesc(GFXTextureTarget::Color0); + if(color0) + { + if(color0->getDepth() == 0) + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, color0->getBinding(), color0->getHandle(), color0->getMipLevel()); + else + glFramebufferTexture3DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, color0->getBinding(), color0->getHandle(), color0->getMipLevel(), color0->getZOffset()); + } + else + { + // Clears the texture (note that the binding is irrelevent) + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, 0, 0); + } + + _GFXGLTargetDesc* depthStecil = mTarget->getTargetDesc(GFXTextureTarget::DepthStencil); + if(depthStecil) + { + // Certain drivers have issues with depth only FBOs. That and the next two asserts assume we have a color target. + AssertFatal(color0, "GFXGLTextureTarget::applyState() - Cannot set DepthStencil target without Color0 target!"); + AssertFatal(depthStecil->getWidth() == color0->getWidth(), "GFXGLTextureTarget::applyState() - DepthStencil and Color0 targets MUST have the same width!"); + AssertFatal(depthStecil->getHeight() == color0->getHeight(), "GFXGLTextureTarget::applyState() - DepthStencil and Color0 targets MUST have the same height!"); + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, depthStecil->getBinding(), depthStecil->getHandle(), depthStecil->getMipLevel()); + } + else + { + // Clears the texture (note that the binding is irrelevent) + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, 0, 0); + } + + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); +} + +void _GFXGLTextureTargetFBOImpl::makeActive() +{ + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, mFramebuffer); + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, mFramebuffer); +} + +void _GFXGLTextureTargetFBOImpl::finish() +{ + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0); + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0); + + _GFXGLTargetDesc* color0 = mTarget->getTargetDesc(GFXTextureTarget::Color0); + if(!color0 || !(color0->hasMips())) + return; + + // Generate mips if necessary + // Assumes a 2D texture. + glActiveTexture(GL_TEXTURE0); + PRESERVE_2D_TEXTURE(); + glBindTexture(GL_TEXTURE_2D, color0->getHandle()); + glGenerateMipmapEXT(GL_TEXTURE_2D); +} + +// This implementations uses AUX buffers (we should always have at least one) to do render to texture. It is currently only used when we need access to the windows depth buffer. +class _GFXGLTextureTargetAUXBufferImpl : public _GFXGLTextureTargetImpl +{ +public: + _GFXGLTextureTargetAUXBufferImpl(GFXGLTextureTarget* target); + + virtual void applyState(); + virtual void makeActive(); + virtual void finish(); +}; + +_GFXGLTextureTargetAUXBufferImpl::_GFXGLTextureTargetAUXBufferImpl(GFXGLTextureTarget* target) +{ + mTarget = target; +} + +void _GFXGLTextureTargetAUXBufferImpl::applyState() +{ + +} + +void _GFXGLTextureTargetAUXBufferImpl::makeActive() +{ + glDrawBuffer(GL_AUX0); + glReadBuffer(GL_AUX0); +} + +void _GFXGLTextureTargetAUXBufferImpl::finish() +{ + // Bind the Color0 texture + _GFXGLTargetDesc* color0 = mTarget->getTargetDesc(GFXTextureTarget::Color0); + + glActiveTexture(GL_TEXTURE0); + // Assume we're a 2D texture for now. + PRESERVE_2D_TEXTURE(); + glBindTexture(color0->getBinding(), color0->getHandle()); + glCopyTexSubImage2D(color0->getBinding(), 0, 0, 0, 0, 0, color0->getWidth(), color0->getHeight()); + + glDrawBuffer(GL_BACK); + glReadBuffer(GL_BACK); +} + +// Actual GFXGLTextureTarget interface +GFXGLTextureTarget::GFXGLTextureTarget() +{ + for(U32 i=0; igetWidth(), mTargets[Color0]->getHeight()); + + return Point2I(0, 0); +} + +GFXFormat GFXGLTextureTarget::getFormat() +{ + // TODO: Fix me! + return GFXFormatR8G8B8A8; +} + +void GFXGLTextureTarget::attachTexture( RenderSlot slot, GFXTextureObject *tex, U32 mipLevel/*=0*/, U32 zOffset /*= 0*/ ) +{ + // GFXTextureTarget::sDefaultDepthStencil is a hint that we want the window's depth buffer. + if(tex == GFXTextureTarget::sDefaultDepthStencil) + _needsAux = true; + + if(slot == DepthStencil && tex != GFXTextureTarget::sDefaultDepthStencil) + _needsAux = false; + + // Triggers an update when we next render + invalidateState(); + + // We stash the texture and info into an internal struct. + GFXGLTextureObject* glTexture = static_cast(tex); + if(tex && tex != GFXTextureTarget::sDefaultDepthStencil) + mTargets[slot] = new _GFXGLTextureTargetDesc(glTexture, mipLevel, zOffset); + else + mTargets[slot] = NULL; +} + +void GFXGLTextureTarget::attachTexture( RenderSlot slot, GFXCubemap *tex, U32 face, U32 mipLevel/*=0*/ ) +{ + // No depth cubemaps, sorry + AssertFatal(slot != DepthStencil, "GFXGLTextureTarget::attachTexture (cube) - Cube depth textures not supported!"); + if(slot == DepthStencil) + return; + + // Triggers an update when we next render + invalidateState(); + + // We stash the texture and info into an internal struct. + GFXGLCubemap* glTexture = static_cast(tex); + if(tex) + mTargets[slot] = new _GFXGLCubemapTargetDesc(glTexture, face, mipLevel, 0); + else + mTargets[slot] = NULL; +} + +void GFXGLTextureTarget::clearAttachments() +{ + deactivate(); + for(S32 i=1; imakeActive(); +} + +void GFXGLTextureTarget::deactivate() +{ + _impl->finish(); +} + +void GFXGLTextureTarget::applyState() +{ + if(!isPendingState()) + return; + + // So we don't do this over and over again + stateApplied(); + + // Ensure we have the proper implementation (consider changing to an enum?) + if(_needsAux && dynamic_cast<_GFXGLTextureTargetAUXBufferImpl*>(_impl.ptr()) == NULL) + _impl = new _GFXGLTextureTargetAUXBufferImpl(this); + else if(!_needsAux && dynamic_cast<_GFXGLTextureTargetFBOImpl*>(_impl.ptr()) == NULL) + _impl = new _GFXGLTextureTargetFBOImpl(this); + + _impl->applyState(); +} + +_GFXGLTargetDesc* GFXGLTextureTarget::getTargetDesc(RenderSlot slot) const +{ + // This can only be called by our implementations, and then will not actually store the pointer so this is (almost) safe + return mTargets[slot].ptr(); +} + +void GFXGLTextureTarget::_onTextureEvent( GFXTexCallbackCode code ) +{ + invalidateState(); +} + +const String GFXGLTextureTarget::describeSelf() const +{ + String ret = String::ToString(" Color0 Attachment: %i", mTargets[Color0].isValid() ? mTargets[Color0]->getHandle() : 0); + ret += String::ToString(" Depth Attachment: %i", mTargets[DepthStencil].isValid() ? mTargets[DepthStencil]->getHandle() : 0); + + return ret; +} + +void GFXGLTextureTarget::resolve() +{ +} + +void GFXGLTextureTarget::resolveTo(GFXTextureObject* obj) +{ + AssertFatal(dynamic_cast(obj), "GFXGLTextureTarget::resolveTo - Incorrect type of texture, expected a GFXGLTextureObject"); + GFXGLTextureObject* glTexture = static_cast(obj); + + PRESERVE_FRAMEBUFFER(); + + GLuint dest; + GLuint src; + + glGenFramebuffersEXT(1, &dest); + glGenFramebuffersEXT(1, &src); + + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, dest); + glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, glTexture->getHandle(), 0); + + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, src); + glFramebufferTexture2DEXT(GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D,mTargets[Color0]->getHandle(), 0); + + glBlitFramebufferEXT(0, 0, mTargets[Color0]->getWidth(), mTargets[Color0]->getHeight(), + 0, 0, glTexture->getWidth(), glTexture->getHeight(), GL_COLOR_BUFFER_BIT, GL_NEAREST); + + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0); + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0); + + glDeleteFramebuffersEXT(1, &dest); + glDeleteFramebuffersEXT(1, &src); +} diff --git a/Engine/source/gfx/gl/gfxGLTextureTarget.h b/Engine/source/gfx/gl/gfxGLTextureTarget.h new file mode 100644 index 000000000..d0b34539a --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLTextureTarget.h @@ -0,0 +1,101 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXGLTEXTURETARGET_H_ +#define _GFXGLTEXTURETARGET_H_ + +#include "gfx/gfxTarget.h" +#include "core/util/autoPtr.h" + +class GFXGLTextureObject; +class _GFXGLTargetDesc; +class _GFXGLTextureTargetImpl; + +/// Render to texture support for OpenGL. +/// This class needs to make a number of assumptions due to the requirements +/// and complexity of render to texture in OpenGL. +/// 1) This class is only guaranteed to work with 2D textures or cubemaps. 3D textures +/// may or may not work. +/// 2) This class does not currently support multiple texture targets. Regardless +/// of how many targets you bind, only Color0 will be used. +/// 3) This class requires that the DepthStencil and Color0 targets have identical +/// dimensions. +/// 4) If the DepthStencil target is GFXTextureTarget::sDefaultStencil, then the +/// Color0 target should be the same size as the current backbuffer and should also +/// be the same format (typically R8G8B8A8) +class GFXGLTextureTarget : public GFXTextureTarget +{ +public: + GFXGLTextureTarget(); + virtual ~GFXGLTextureTarget(); + + virtual const Point2I getSize(); + virtual GFXFormat getFormat(); + virtual void attachTexture(RenderSlot slot, GFXTextureObject *tex, U32 mipLevel=0, U32 zOffset = 0); + virtual void attachTexture(RenderSlot slot, GFXCubemap *tex, U32 face, U32 mipLevel=0); + virtual void clearAttachments(); + + /// Functions to query internal state + /// @{ + + /// Returns the internal structure for the given slot. This should only be called by our internal implementations. + _GFXGLTargetDesc* getTargetDesc(RenderSlot slot) const; + + /// @} + + void deactivate(); + void zombify(); + void resurrect(); + virtual const String describeSelf() const; + + virtual void resolve(); + + virtual void resolveTo(GFXTextureObject* obj); + +protected: + + friend class GFXGLDevice; + + /// The callback used to get texture events. + /// @see GFXTextureManager::addEventDelegate + void _onTextureEvent( GFXTexCallbackCode code ); + + /// If true our implementation should use AUX buffers + bool _needsAux; + + /// Pointer to our internal implementation + AutoPtr<_GFXGLTextureTargetImpl> _impl; + + /// Array of _GFXGLTargetDesc's, an internal struct used to keep track of texture data. + AutoPtr<_GFXGLTargetDesc> mTargets[MaxRenderSlotId]; + + /// These redirect to our internal implementation + /// @{ + + void applyState(); + void makeActive(); + + /// @} + +}; + +#endif diff --git a/Engine/source/gfx/gl/gfxGLUtils.h b/Engine/source/gfx/gl/gfxGLUtils.h new file mode 100644 index 000000000..5f1ac18c7 --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLUtils.h @@ -0,0 +1,111 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef TORQUE_GFX_GL_GFXGLUTILS_H_ +#define TORQUE_GFX_GL_GFXGLUTILS_H_ + +#include "core/util/preprocessorHelpers.h" +#include "gfx/gl/gfxGLEnumTranslate.h" + +static inline GLenum minificationFilter(U32 minFilter, U32 mipFilter, U32 mipLevels) +{ + if(mipLevels == 1) + return GFXGLTextureFilter[minFilter]; + + // the compiler should interpret this as array lookups + switch( minFilter ) + { + case GFXTextureFilterLinear: + switch( mipFilter ) + { + case GFXTextureFilterLinear: + return GL_LINEAR_MIPMAP_LINEAR; + case GFXTextureFilterPoint: + return GL_LINEAR_MIPMAP_NEAREST; + default: + return GL_LINEAR; + } + default: + switch( mipFilter ) { + case GFXTextureFilterLinear: + return GL_NEAREST_MIPMAP_LINEAR; + case GFXTextureFilterPoint: + return GL_NEAREST_MIPMAP_NEAREST; + default: + return GL_NEAREST; + } + } +} + +/// Simple class which preserves a given GL integer. +/// This class determines the integer to preserve on construction and restores +/// it on destruction. +class GFXGLPreserveInteger +{ +public: + typedef void(*BindFn)(GLenum, GLuint); + + /// Preserve the integer. + /// @param binding The binding which should be set on destruction. + /// @param getBinding The parameter to be passed to glGetIntegerv to determine + /// the integer to be preserved. + /// @param binder The gl function to call to restore the integer. + GFXGLPreserveInteger(GLenum binding, GLint getBinding, BindFn binder) : + mBinding(binding), mPreserved(0), mBinder(binder) + { + AssertFatal(mBinder, "GFXGLPreserveInteger - Need a valid binder function"); + glGetIntegerv(getBinding, &mPreserved); + } + + /// Restores the integer. + ~GFXGLPreserveInteger() + { + mBinder(mBinding, mPreserved); + } + +private: + GLenum mBinding; + GLint mPreserved; + BindFn mBinder; +}; + +/// Helper macro to preserve the current VBO binding. +#define PRESERVE_VERTEX_BUFFER() \ +GFXGLPreserveInteger TORQUE_CONCAT(preserve_, __LINE__) (GL_ARRAY_BUFFER, GL_ARRAY_BUFFER_BINDING, glBindBuffer) + +/// Helper macro to preserve the current element array binding. +#define PRESERVE_INDEX_BUFFER() \ +GFXGLPreserveInteger TORQUE_CONCAT(preserve_, __LINE__) (GL_ELEMENT_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER_BINDING, glBindBuffer) + +/// Helper macro to preserve the current 2D texture binding. +#define PRESERVE_2D_TEXTURE() \ +GFXGLPreserveInteger TORQUE_CONCAT(preserve_, __LINE__) (GL_TEXTURE_2D, GL_TEXTURE_BINDING_2D, glBindTexture) + +/// Helper macro to preserve the current 3D texture binding. +#define PRESERVE_3D_TEXTURE() \ +GFXGLPreserveInteger TORQUE_CONCAT(preserve_, __LINE__) (GL_TEXTURE_3D, GL_TEXTURE_BINDING_3D, glBindTexture) + +#define PRESERVE_FRAMEBUFFER() \ +GFXGLPreserveInteger TORQUE_CONCAT(preserve_, __LINE__) (GL_READ_FRAMEBUFFER_EXT, GL_READ_FRAMEBUFFER_BINDING_EXT, glBindFramebufferEXT);\ +GFXGLPreserveInteger TORQUE_CONCAT(preserve2_, __LINE__) (GL_DRAW_FRAMEBUFFER_EXT, GL_DRAW_FRAMEBUFFER_BINDING_EXT, glBindFramebufferEXT) + +#endif diff --git a/Engine/source/gfx/gl/gfxGLVertexBuffer.cpp b/Engine/source/gfx/gl/gfxGLVertexBuffer.cpp new file mode 100644 index 000000000..11e7e53ea --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLVertexBuffer.cpp @@ -0,0 +1,177 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/gl/gfxGLVertexBuffer.h" + +#include "gfx/gl/gfxGLDevice.h" +#include "gfx/gl/gfxGLEnumTranslate.h" +#include "gfx/gl/gfxGLUtils.h" + + +GFXGLVertexBuffer::GFXGLVertexBuffer( GFXDevice *device, + U32 numVerts, + const GFXVertexFormat *vertexFormat, + U32 vertexSize, + GFXBufferType bufferType ) + : GFXVertexBuffer( device, numVerts, vertexFormat, vertexSize, bufferType ), + mZombieCache(NULL) +{ + PRESERVE_VERTEX_BUFFER(); + // Generate a buffer and allocate the needed memory. + glGenBuffers(1, &mBuffer); + glBindBuffer(GL_ARRAY_BUFFER, mBuffer); + glBufferData(GL_ARRAY_BUFFER, numVerts * vertexSize, NULL, GFXGLBufferType[bufferType]); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +GFXGLVertexBuffer::~GFXGLVertexBuffer() +{ + // While heavy handed, this does delete the buffer and frees the associated memory. + glDeleteBuffers(1, &mBuffer); + + if( mZombieCache ) + delete [] mZombieCache; +} + +void GFXGLVertexBuffer::lock( U32 vertexStart, U32 vertexEnd, void **vertexPtr ) +{ + PRESERVE_VERTEX_BUFFER(); + // Bind us, get a pointer into the buffer, then + // offset it by vertexStart so we act like the D3D layer. + glBindBuffer(GL_ARRAY_BUFFER, mBuffer); + glBufferData(GL_ARRAY_BUFFER, mNumVerts * mVertexSize, NULL, GFXGLBufferType[mBufferType]); + *vertexPtr = (void*)((U8*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY) + (vertexStart * mVertexSize)); + lockedVertexStart = vertexStart; + lockedVertexEnd = vertexEnd; +} + +void GFXGLVertexBuffer::unlock() +{ + PRESERVE_VERTEX_BUFFER(); + // Unmap the buffer and bind 0 to GL_ARRAY_BUFFER + glBindBuffer(GL_ARRAY_BUFFER, mBuffer); + bool res = glUnmapBuffer(GL_ARRAY_BUFFER); + AssertFatal(res, "GFXGLVertexBuffer::unlock - shouldn't fail!"); + + lockedVertexStart = 0; + lockedVertexEnd = 0; +} + +void GFXGLVertexBuffer::prepare() +{ + // Bind the buffer... + glBindBuffer(GL_ARRAY_BUFFER, mBuffer); + U8* buffer = (U8*)getBuffer(); + + // Loop thru the vertex format elements adding the array state... + U32 texCoordIndex = 0; + for ( U32 i=0; i < mVertexFormat.getElementCount(); i++ ) + { + const GFXVertexElement &element = mVertexFormat.getElement( i ); + + if ( element.isSemantic( GFXSemantic::POSITION ) ) + { + glEnableClientState( GL_VERTEX_ARRAY ); + glVertexPointer( element.getSizeInBytes() / 4, GL_FLOAT, mVertexSize, buffer ); + buffer += element.getSizeInBytes(); + } + else if ( element.isSemantic( GFXSemantic::NORMAL ) ) + { + glEnableClientState( GL_NORMAL_ARRAY ); + glNormalPointer( GL_FLOAT, mVertexSize, buffer ); + buffer += element.getSizeInBytes(); + } + else if ( element.isSemantic( GFXSemantic::COLOR ) ) + { + glEnableClientState( GL_COLOR_ARRAY ); + glColorPointer( element.getSizeInBytes(), GL_UNSIGNED_BYTE, mVertexSize, buffer ); + buffer += element.getSizeInBytes(); + } + else // Everything else is a texture coordinate. + { + glClientActiveTexture( GL_TEXTURE0 + texCoordIndex ); + glEnableClientState( GL_TEXTURE_COORD_ARRAY ); + glTexCoordPointer( element.getSizeInBytes() / 4, GL_FLOAT, mVertexSize, buffer ); + buffer += element.getSizeInBytes(); + ++texCoordIndex; + } + + } +} + +void GFXGLVertexBuffer::finish() +{ + glBindBuffer(GL_ARRAY_BUFFER, 0); + + U32 texCoordIndex = 0; + for ( U32 i=0; i < mVertexFormat.getElementCount(); i++ ) + { + const GFXVertexElement &element = mVertexFormat.getElement( i ); + + if ( element.isSemantic( GFXSemantic::POSITION ) ) + glDisableClientState( GL_VERTEX_ARRAY ); + else if ( element.isSemantic( GFXSemantic::NORMAL ) ) + glDisableClientState( GL_NORMAL_ARRAY ); + else if ( element.isSemantic( GFXSemantic::COLOR ) ) + glDisableClientState( GL_COLOR_ARRAY ); + else + { + glClientActiveTexture( GL_TEXTURE0 + texCoordIndex ); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + ++texCoordIndex; + } + } +} + +GLvoid* GFXGLVertexBuffer::getBuffer() +{ + // NULL specifies no offset into the hardware buffer + return (GLvoid*)NULL; +} + +void GFXGLVertexBuffer::zombify() +{ + if(mZombieCache || !mBuffer) + return; + + mZombieCache = new U8[mNumVerts * mVertexSize]; + glBindBuffer(GL_ARRAY_BUFFER, mBuffer); + glGetBufferSubData(GL_ARRAY_BUFFER, 0, mNumVerts * mVertexSize, mZombieCache); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glDeleteBuffers(1, &mBuffer); + mBuffer = 0; +} + +void GFXGLVertexBuffer::resurrect() +{ + if(!mZombieCache) + return; + + glGenBuffers(1, &mBuffer); + glBindBuffer(GL_ARRAY_BUFFER, mBuffer); + glBufferData(GL_ARRAY_BUFFER, mNumVerts * mVertexSize, mZombieCache, GFXGLBufferType[mBufferType]); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + delete[] mZombieCache; + mZombieCache = NULL; +} diff --git a/Engine/source/gfx/gl/gfxGLVertexBuffer.h b/Engine/source/gfx/gl/gfxGLVertexBuffer.h new file mode 100644 index 000000000..f7de259a3 --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLVertexBuffer.h @@ -0,0 +1,64 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXGLVERTEXBUFFER_H_ +#define _GFXGLVERTEXBUFFER_H_ + +#ifndef _GFXVERTEXBUFFER_H_ +#include "gfx/gfxVertexBuffer.h" +#endif +#ifndef GL_GGL_H +#include "gfx/gl/ggl/ggl.h" +#endif + +/// This is a vertex buffer which uses GL_ARB_vertex_buffer_object. +class GFXGLVertexBuffer : public GFXVertexBuffer +{ +public: + GFXGLVertexBuffer( GFXDevice *device, + U32 numVerts, + const GFXVertexFormat *vertexFormat, + U32 vertexSize, + GFXBufferType bufferType ); + + ~GFXGLVertexBuffer(); + + virtual void lock(U32 vertexStart, U32 vertexEnd, void **vertexPtr); ///< calls glMapBuffer and offsets the pointer by vertex start + virtual void unlock(); ///< calls glUnmapBuffer, unbinds the buffer + virtual void prepare(); ///< Binds the buffer + virtual void finish(); ///< We're done here + + GLvoid* getBuffer(); ///< returns NULL + + // GFXResource interface + virtual void zombify(); + virtual void resurrect(); + +private: + friend class GFXGLDevice; + /// GL buffer handle + GLuint mBuffer; + + U8* mZombieCache; +}; + +#endif diff --git a/Engine/source/gfx/gl/gfxGLWindowTarget.cpp b/Engine/source/gfx/gl/gfxGLWindowTarget.cpp new file mode 100644 index 000000000..852cd8359 --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLWindowTarget.cpp @@ -0,0 +1,79 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "windowManager/platformWindow.h" +#include "gfx/gl/gfxGLDevice.h" +#include "gfx/gl/gfxGLWindowTarget.h" +#include "gfx/gl/gfxGLTextureObject.h" +#include "gfx/gl/gfxGLUtils.h" + +GFXGLWindowTarget::GFXGLWindowTarget(PlatformWindow *win, GFXDevice *d) + : GFXWindowTarget(win), mDevice(d), mContext(NULL), mFullscreenContext(NULL) +{ + win->appEvent.notify(this, &GFXGLWindowTarget::_onAppSignal); +} + +void GFXGLWindowTarget::resetMode() +{ + if(mWindow->getVideoMode().fullScreen != mWindow->isFullscreen()) + { + _teardownCurrentMode(); + _setupNewMode(); + } +} + +void GFXGLWindowTarget::_onAppSignal(WindowId wnd, S32 event) +{ + if(event != WindowHidden) + return; + + // TODO: Investigate this further. + // Opening and then closing the console results in framerate dropping at an alarming rate down to 3-4 FPS and then + // rebounding to it's usual level. Clearing all the volatile VBs prevents this behavior, but I can't explain why. + // My fear is there is something fundamentally wrong with how we share objects between contexts and this is simply + // masking the issue for the most common case. + static_cast(mDevice)->mVolatileVBs.clear(); +} + +void GFXGLWindowTarget::resolveTo(GFXTextureObject* obj) +{ + AssertFatal(dynamic_cast(obj), "GFXGLTextureTarget::resolveTo - Incorrect type of texture, expected a GFXGLTextureObject"); + GFXGLTextureObject* glTexture = static_cast(obj); + + PRESERVE_FRAMEBUFFER(); + + GLuint dest; + + glGenFramebuffersEXT(1, &dest); + + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, dest); + glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, glTexture->getHandle(), 0); + + glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0); + + glBlitFramebufferEXT(0, 0, getSize().x, getSize().y, + 0, 0, glTexture->getWidth(), glTexture->getHeight(), GL_COLOR_BUFFER_BIT, GL_NEAREST); + + glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0); + + glDeleteFramebuffersEXT(1, &dest); +} diff --git a/Engine/source/gfx/gl/gfxGLWindowTarget.h b/Engine/source/gfx/gl/gfxGLWindowTarget.h new file mode 100644 index 000000000..88aa6a6f7 --- /dev/null +++ b/Engine/source/gfx/gl/gfxGLWindowTarget.h @@ -0,0 +1,62 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GFXGLWINDOWTARGET_H_ +#define _GFXGLWINDOWTARGET_H_ + +#include "gfx/gfxTarget.h" + +class GFXGLWindowTarget : public GFXWindowTarget +{ +public: + + GFXGLWindowTarget(PlatformWindow *win, GFXDevice *d); + const Point2I getSize() + { + return mWindow->getClientExtent(); + } + virtual GFXFormat getFormat() + { + // TODO: Fix me! + return GFXFormatR8G8B8A8; + } + void makeActive(); + virtual bool present(); + virtual void resetMode(); + virtual void zombify() { } + virtual void resurrect() { } + + virtual void resolveTo(GFXTextureObject* obj); + + void _onAppSignal(WindowId wnd, S32 event); + +private: + friend class GFXGLDevice; + Point2I size; + GFXDevice* mDevice; + void* mContext; + void* mFullscreenContext; + void _teardownCurrentMode(); + void _setupNewMode(); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/gfx/gl/ggl/generated/glc.h b/Engine/source/gfx/gl/ggl/generated/glc.h new file mode 100644 index 000000000..0554616b9 --- /dev/null +++ b/Engine/source/gfx/gl/ggl/generated/glc.h @@ -0,0 +1,666 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +/* +** License Applicability. Except to the extent portions of this file are +** made subject to an alternative license as permitted in the SGI Free +** Software License B, Version 1.1 (the "License"), the contents of this +** file are subject only to the provisions of the License. You may not use +** this file except in compliance with the License. You may obtain a copy +** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600 +** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at: +** +** http://oss.sgi.com/projects/FreeB +** +** Note that, as provided in the License, the Software is distributed on an +** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS +** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND +** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A +** PARTICULAR PURPOSE, AND NON-INFRINGEMENT. +** +** Original Code. The Original Code is: OpenGL Sample Private, +** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics, +** Inc. The Original Code is Copyright (c) 1991-2002 Silicon Graphics, Inc. +** Copyright in any portions created by third parties is as indicated +** elsewhere herein. All Rights Reserved. +** +** Additional Notice Provisions: This software was created using the +** OpenGL(R) version 1.2.1 Sample Private published by SGI, but has +** not been independently verified as being compliant with the OpenGL(R) +** version 1.2.1 Specification. +*/ + +typedef unsigned int GLenum; +typedef unsigned char GLboolean; +typedef unsigned int GLbitfield; +typedef void GLvoid; +typedef signed char GLbyte; /* 1-byte signed */ +typedef short GLshort; /* 2-byte signed */ +typedef int GLint; /* 4-byte signed */ +typedef unsigned char GLubyte; /* 1-byte unsigned */ +typedef unsigned short GLushort; /* 2-byte unsigned */ +typedef unsigned int GLuint; /* 4-byte unsigned */ +typedef int GLsizei; /* 4-byte signed */ +typedef float GLfloat; /* single precision float */ +typedef float GLclampf; /* single precision float in [0,1] */ +typedef double GLdouble; /* double precision float */ +typedef double GLclampd; /* double precision float in [0,1] */ + +/* Boolean values */ +#define GL_FALSE 0x0 +#define GL_TRUE 0x1 + +/* Data types */ +#define GL_BYTE 0x1400 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_SHORT 0x1402 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_INT 0x1404 +#define GL_UNSIGNED_INT 0x1405 +#define GL_FLOAT 0x1406 +#define GL_DOUBLE 0x140A +#define GL_2_BYTES 0x1407 +#define GL_3_BYTES 0x1408 +#define GL_4_BYTES 0x1409 + +/* Primitives */ +#define GL_POINTS 0x0000 +#define GL_LINES 0x0001 +#define GL_LINE_LOOP 0x0002 +#define GL_LINE_STRIP 0x0003 +#define GL_TRIANGLES 0x0004 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_TRIANGLE_FAN 0x0006 +#define GL_QUADS 0x0007 +#define GL_QUAD_STRIP 0x0008 +#define GL_POLYGON 0x0009 + +/* Vertex Arrays */ +#define GL_VERTEX_ARRAY 0x8074 +#define GL_NORMAL_ARRAY 0x8075 +#define GL_COLOR_ARRAY 0x8076 +#define GL_INDEX_ARRAY 0x8077 +#define GL_TEXTURE_COORD_ARRAY 0x8078 +#define GL_EDGE_FLAG_ARRAY 0x8079 +#define GL_VERTEX_ARRAY_SIZE 0x807A +#define GL_VERTEX_ARRAY_TYPE 0x807B +#define GL_VERTEX_ARRAY_STRIDE 0x807C +#define GL_NORMAL_ARRAY_TYPE 0x807E +#define GL_NORMAL_ARRAY_STRIDE 0x807F +#define GL_COLOR_ARRAY_SIZE 0x8081 +#define GL_COLOR_ARRAY_TYPE 0x8082 +#define GL_COLOR_ARRAY_STRIDE 0x8083 +#define GL_INDEX_ARRAY_TYPE 0x8085 +#define GL_INDEX_ARRAY_STRIDE 0x8086 +#define GL_TEXTURE_COORD_ARRAY_SIZE 0x8088 +#define GL_TEXTURE_COORD_ARRAY_TYPE 0x8089 +#define GL_TEXTURE_COORD_ARRAY_STRIDE 0x808A +#define GL_EDGE_FLAG_ARRAY_STRIDE 0x808C +#define GL_VERTEX_ARRAY_POINTER 0x808E +#define GL_NORMAL_ARRAY_POINTER 0x808F +#define GL_COLOR_ARRAY_POINTER 0x8090 +#define GL_INDEX_ARRAY_POINTER 0x8091 +#define GL_TEXTURE_COORD_ARRAY_POINTER 0x8092 +#define GL_EDGE_FLAG_ARRAY_POINTER 0x8093 +#define GL_V2F 0x2A20 +#define GL_V3F 0x2A21 +#define GL_C4UB_V2F 0x2A22 +#define GL_C4UB_V3F 0x2A23 +#define GL_C3F_V3F 0x2A24 +#define GL_N3F_V3F 0x2A25 +#define GL_C4F_N3F_V3F 0x2A26 +#define GL_T2F_V3F 0x2A27 +#define GL_T4F_V4F 0x2A28 +#define GL_T2F_C4UB_V3F 0x2A29 +#define GL_T2F_C3F_V3F 0x2A2A +#define GL_T2F_N3F_V3F 0x2A2B +#define GL_T2F_C4F_N3F_V3F 0x2A2C +#define GL_T4F_C4F_N3F_V4F 0x2A2D + +/* Matrix Mode */ +#define GL_MATRIX_MODE 0x0BA0 +#define GL_MODELVIEW 0x1700 +#define GL_PROJECTION 0x1701 +#define GL_TEXTURE 0x1702 + +/* Points */ +#define GL_POINT_SMOOTH 0x0B10 +#define GL_POINT_SIZE 0x0B11 +#define GL_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_POINT_SIZE_RANGE 0x0B12 + +/* Lines */ +#define GL_LINE_SMOOTH 0x0B20 +#define GL_LINE_STIPPLE 0x0B24 +#define GL_LINE_STIPPLE_PATTERN 0x0B25 +#define GL_LINE_STIPPLE_REPEAT 0x0B26 +#define GL_LINE_WIDTH 0x0B21 +#define GL_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_LINE_WIDTH_RANGE 0x0B22 + +/* Polygons */ +#define GL_POINT 0x1B00 +#define GL_LINE 0x1B01 +#define GL_FILL 0x1B02 +#define GL_CW 0x0900 +#define GL_CCW 0x0901 +#define GL_FRONT 0x0404 +#define GL_BACK 0x0405 +#define GL_POLYGON_MODE 0x0B40 +#define GL_POLYGON_SMOOTH 0x0B41 +#define GL_POLYGON_STIPPLE 0x0B42 +#define GL_EDGE_FLAG 0x0B43 +#define GL_CULL_FACE 0x0B44 +#define GL_CULL_FACE_MODE 0x0B45 +#define GL_FRONT_FACE 0x0B46 +#define GL_POLYGON_OFFSET_FACTOR 0x8038 +#define GL_POLYGON_OFFSET_UNITS 0x2A00 +#define GL_POLYGON_OFFSET_POINT 0x2A01 +#define GL_POLYGON_OFFSET_LINE 0x2A02 +#define GL_POLYGON_OFFSET_FILL 0x8037 + +/* Display Lists */ +#define GL_COMPILE 0x1300 +#define GL_COMPILE_AND_EXECUTE 0x1301 +#define GL_LIST_BASE 0x0B32 +#define GL_LIST_INDEX 0x0B33 +#define GL_LIST_MODE 0x0B30 + +/* Depth buffer */ +#define GL_NEVER 0x0200 +#define GL_LESS 0x0201 +#define GL_EQUAL 0x0202 +#define GL_LEQUAL 0x0203 +#define GL_GREATER 0x0204 +#define GL_NOTEQUAL 0x0205 +#define GL_GEQUAL 0x0206 +#define GL_ALWAYS 0x0207 +#define GL_DEPTH_TEST 0x0B71 +#define GL_DEPTH_BITS 0x0D56 +#define GL_DEPTH_CLEAR_VALUE 0x0B73 +#define GL_DEPTH_FUNC 0x0B74 +#define GL_DEPTH_RANGE 0x0B70 +#define GL_DEPTH_WRITEMASK 0x0B72 +#define GL_DEPTH_COMPONENT 0x1902 + +/* Lighting */ +#define GL_LIGHTING 0x0B50 +#define GL_LIGHT0 0x4000 +#define GL_LIGHT1 0x4001 +#define GL_LIGHT2 0x4002 +#define GL_LIGHT3 0x4003 +#define GL_LIGHT4 0x4004 +#define GL_LIGHT5 0x4005 +#define GL_LIGHT6 0x4006 +#define GL_LIGHT7 0x4007 +#define GL_SPOT_EXPONENT 0x1205 +#define GL_SPOT_CUTOFF 0x1206 +#define GL_CONSTANT_ATTENUATION 0x1207 +#define GL_LINEAR_ATTENUATION 0x1208 +#define GL_QUADRATIC_ATTENUATION 0x1209 +#define GL_AMBIENT 0x1200 +#define GL_DIFFUSE 0x1201 +#define GL_SPECULAR 0x1202 +#define GL_SHININESS 0x1601 +#define GL_EMISSION 0x1600 +#define GL_POSITION 0x1203 +#define GL_SPOT_DIRECTION 0x1204 +#define GL_AMBIENT_AND_DIFFUSE 0x1602 +#define GL_COLOR_INDEXES 0x1603 +#define GL_LIGHT_MODEL_TWO_SIDE 0x0B52 +#define GL_LIGHT_MODEL_LOCAL_VIEWER 0x0B51 +#define GL_LIGHT_MODEL_AMBIENT 0x0B53 +#define GL_FRONT_AND_BACK 0x0408 +#define GL_SHADE_MODEL 0x0B54 +#define GL_FLAT 0x1D00 +#define GL_SMOOTH 0x1D01 +#define GL_COLOR_MATERIAL 0x0B57 +#define GL_COLOR_MATERIAL_FACE 0x0B55 +#define GL_COLOR_MATERIAL_PARAMETER 0x0B56 +#define GL_NORMALIZE 0x0BA1 + +/* User clipping planes */ +#define GL_CLIP_PLANE0 0x3000 +#define GL_CLIP_PLANE1 0x3001 +#define GL_CLIP_PLANE2 0x3002 +#define GL_CLIP_PLANE3 0x3003 +#define GL_CLIP_PLANE4 0x3004 +#define GL_CLIP_PLANE5 0x3005 + +/* Accumulation buffer */ +#define GL_ACCUM_RED_BITS 0x0D58 +#define GL_ACCUM_GREEN_BITS 0x0D59 +#define GL_ACCUM_BLUE_BITS 0x0D5A +#define GL_ACCUM_ALPHA_BITS 0x0D5B +#define GL_ACCUM_CLEAR_VALUE 0x0B80 +#define GL_ACCUM 0x0100 +#define GL_ADD 0x0104 +#define GL_LOAD 0x0101 +#define GL_MULT 0x0103 +#define GL_RETURN 0x0102 + +/* Alpha testing */ +#define GL_ALPHA_TEST 0x0BC0 +#define GL_ALPHA_TEST_REF 0x0BC2 +#define GL_ALPHA_TEST_FUNC 0x0BC1 + +/* Blending */ +#define GL_BLEND 0x0BE2 +#define GL_BLEND_SRC 0x0BE1 +#define GL_BLEND_DST 0x0BE0 +#define GL_ZERO 0x0 +#define GL_ONE 0x1 +#define GL_SRC_COLOR 0x0300 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_SRC_ALPHA 0x0302 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_DST_ALPHA 0x0304 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 +#define GL_DST_COLOR 0x0306 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_SRC_ALPHA_SATURATE 0x0308 + +/* Render Mode */ +#define GL_FEEDBACK 0x1C01 +#define GL_RENDER 0x1C00 +#define GL_SELECT 0x1C02 + +/* Feedback */ +#define GL_2D 0x0600 +#define GL_3D 0x0601 +#define GL_3D_COLOR 0x0602 +#define GL_3D_COLOR_TEXTURE 0x0603 +#define GL_4D_COLOR_TEXTURE 0x0604 +#define GL_POINT_TOKEN 0x0701 +#define GL_LINE_TOKEN 0x0702 +#define GL_LINE_RESET_TOKEN 0x0707 +#define GL_POLYGON_TOKEN 0x0703 +#define GL_BITMAP_TOKEN 0x0704 +#define GL_DRAW_PIXEL_TOKEN 0x0705 +#define GL_COPY_PIXEL_TOKEN 0x0706 +#define GL_PASS_THROUGH_TOKEN 0x0700 +#define GL_FEEDBACK_BUFFER_POINTER 0x0DF0 +#define GL_FEEDBACK_BUFFER_SIZE 0x0DF1 +#define GL_FEEDBACK_BUFFER_TYPE 0x0DF2 + +/* Selection */ +#define GL_SELECTION_BUFFER_POINTER 0x0DF3 +#define GL_SELECTION_BUFFER_SIZE 0x0DF4 + +/* Fog */ +#define GL_FOG 0x0B60 +#define GL_FOG_MODE 0x0B65 +#define GL_FOG_DENSITY 0x0B62 +#define GL_FOG_COLOR 0x0B66 +#define GL_FOG_INDEX 0x0B61 +#define GL_FOG_START 0x0B63 +#define GL_FOG_END 0x0B64 +#define GL_LINEAR 0x2601 +#define GL_EXP 0x0800 +#define GL_EXP2 0x0801 + +/* Logic Ops */ +#define GL_LOGIC_OP 0x0BF1 +#define GL_INDEX_LOGIC_OP 0x0BF1 +#define GL_COLOR_LOGIC_OP 0x0BF2 +#define GL_LOGIC_OP_MODE 0x0BF0 +#define GL_CLEAR 0x1500 +#define GL_SET 0x150F +#define GL_COPY 0x1503 +#define GL_COPY_INVERTED 0x150C +#define GL_NOOP 0x1505 +#define GL_INVERT 0x150A +#define GL_AND 0x1501 +#define GL_NAND 0x150E +#define GL_OR 0x1507 +#define GL_NOR 0x1508 +#define GL_XOR 0x1506 +#define GL_EQUIV 0x1509 +#define GL_AND_REVERSE 0x1502 +#define GL_AND_INVERTED 0x1504 +#define GL_OR_REVERSE 0x150B +#define GL_OR_INVERTED 0x150D + +/* Stencil */ +#define GL_STENCIL_TEST 0x0B90 +#define GL_STENCIL_WRITEMASK 0x0B98 +#define GL_STENCIL_BITS 0x0D57 +#define GL_STENCIL_FUNC 0x0B92 +#define GL_STENCIL_VALUE_MASK 0x0B93 +#define GL_STENCIL_REF 0x0B97 +#define GL_STENCIL_FAIL 0x0B94 +#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 +#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 +#define GL_STENCIL_CLEAR_VALUE 0x0B91 +#define GL_STENCIL_INDEX 0x1901 +#define GL_KEEP 0x1E00 +#define GL_REPLACE 0x1E01 +#define GL_INCR 0x1E02 +#define GL_DECR 0x1E03 + +/* Buffers, Pixel Drawing/Reading */ +#define GL_NONE 0x0 +#define GL_LEFT 0x0406 +#define GL_RIGHT 0x0407 +/*GL_FRONT 0x0404 */ +/*GL_BACK 0x0405 */ +/*GL_FRONT_AND_BACK 0x0408 */ +#define GL_FRONT_LEFT 0x0400 +#define GL_FRONT_RIGHT 0x0401 +#define GL_BACK_LEFT 0x0402 +#define GL_BACK_RIGHT 0x0403 +#define GL_AUX0 0x0409 +#define GL_AUX1 0x040A +#define GL_AUX2 0x040B +#define GL_AUX3 0x040C +#define GL_COLOR_INDEX 0x1900 +#define GL_RED 0x1903 +#define GL_GREEN 0x1904 +#define GL_BLUE 0x1905 +#define GL_ALPHA 0x1906 +#define GL_LUMINANCE 0x1909 +#define GL_LUMINANCE_ALPHA 0x190A +#define GL_ALPHA_BITS 0x0D55 +#define GL_RED_BITS 0x0D52 +#define GL_GREEN_BITS 0x0D53 +#define GL_BLUE_BITS 0x0D54 +#define GL_INDEX_BITS 0x0D51 +#define GL_SUBPIXEL_BITS 0x0D50 +#define GL_AUX_BUFFERS 0x0C00 +#define GL_READ_BUFFER 0x0C02 +#define GL_DRAW_BUFFER 0x0C01 +#define GL_DOUBLEBUFFER 0x0C32 +#define GL_STEREO 0x0C33 +#define GL_BITMAP 0x1A00 +#define GL_COLOR 0x1800 +#define GL_DEPTH 0x1801 +#define GL_STENCIL 0x1802 +#define GL_DITHER 0x0BD0 +#define GL_RGB 0x1907 +#define GL_RGBA 0x1908 +#define GL_ALPHA4 0x803B +#define GL_ALPHA8 0x803C +#define GL_ALPHA12 0x803D +#define GL_ALPHA16 0x803E +#define GL_LUMINANCE4 0x803F +#define GL_LUMINANCE8 0x8040 +#define GL_LUMINANCE12 0x8041 +#define GL_LUMINANCE16 0x8042 +#define GL_LUMINANCE4_ALPHA4 0x8043 +#define GL_LUMINANCE6_ALPHA2 0x8044 +#define GL_LUMINANCE8_ALPHA8 0x8045 +#define GL_LUMINANCE12_ALPHA4 0x8046 +#define GL_LUMINANCE12_ALPHA12 0x8047 +#define GL_LUMINANCE16_ALPHA16 0x8048 +#define GL_INTENSITY 0x8049 +#define GL_INTENSITY4 0x804A +#define GL_INTENSITY8 0x804B +#define GL_INTENSITY12 0x804C +#define GL_INTENSITY16 0x804D +#define GL_RGB2 0x804E +#define GL_RGB4 0x804F +#define GL_RGB5 0x8050 +#define GL_RGB8 0x8051 +#define GL_RGB10 0x8052 +#define GL_RGB12 0x8053 +#define GL_RGB16 0x8054 +#define GL_RGBA2 0x8055 +#define GL_RGBA4 0x8056 +#define GL_RGB5_A1 0x8057 +#define GL_RGBA8 0x8058 +#define GL_RGB10_A2 0x8059 +#define GL_RGBA12 0x805A +#define GL_RGBA16 0x805B +#define GL_TEXTURE_RED_SIZE 0x805C +#define GL_TEXTURE_GREEN_SIZE 0x805D +#define GL_TEXTURE_BLUE_SIZE 0x805E +#define GL_TEXTURE_ALPHA_SIZE 0x805F +#define GL_TEXTURE_LUMINANCE_SIZE 0x8060 +#define GL_TEXTURE_INTENSITY_SIZE 0x8061 +/*#define GL_REPLACE 0x8062*/ +#define GL_PROXY_TEXTURE_1D 0x8063 +#define GL_PROXY_TEXTURE_2D 0x8064 + +/* Private limits */ +#define GL_MAX_LIST_NESTING 0x0B31 +#define GL_MAX_ATTRIB_STACK_DEPTH 0x0D35 +#define GL_MAX_MODELVIEW_STACK_DEPTH 0x0D36 +#define GL_MAX_NAME_STACK_DEPTH 0x0D37 +#define GL_MAX_PROJECTION_STACK_DEPTH 0x0D38 +#define GL_MAX_TEXTURE_STACK_DEPTH 0x0D39 +#define GL_MAX_EVAL_ORDER 0x0D30 +#define GL_MAX_LIGHTS 0x0D31 +#define GL_MAX_CLIP_PLANES 0x0D32 +#define GL_MAX_TEXTURE_SIZE 0x0D33 +#define GL_MAX_PIXEL_MAP_TABLE 0x0D34 +#define GL_MAX_VIEWPORT_DIMS 0x0D3A +#define GL_MAX_CLIENT_ATTRIB_STACK_DEPTH 0x0D3B + +/* Gets */ +#define GL_ATTRIB_STACK_DEPTH 0x0BB0 +#define GL_CLIENT_ATTRIB_STACK_DEPTH 0x0BB1 +#define GL_COLOR_CLEAR_VALUE 0x0C22 +#define GL_COLOR_WRITEMASK 0x0C23 +#define GL_CURRENT_INDEX 0x0B01 +#define GL_CURRENT_COLOR 0x0B00 +#define GL_CURRENT_NORMAL 0x0B02 +#define GL_CURRENT_RASTER_COLOR 0x0B04 +#define GL_CURRENT_RASTER_DISTANCE 0x0B09 +#define GL_CURRENT_RASTER_INDEX 0x0B05 +#define GL_CURRENT_RASTER_POSITION 0x0B07 +#define GL_CURRENT_RASTER_TEXTURE_COORDS 0x0B06 +#define GL_CURRENT_RASTER_POSITION_VALID 0x0B08 +#define GL_CURRENT_TEXTURE_COORDS 0x0B03 +#define GL_INDEX_CLEAR_VALUE 0x0C20 +#define GL_INDEX_MODE 0x0C30 +#define GL_INDEX_WRITEMASK 0x0C21 +#define GL_MODELVIEW_MATRIX 0x0BA6 +#define GL_MODELVIEW_STACK_DEPTH 0x0BA3 +#define GL_NAME_STACK_DEPTH 0x0D70 +#define GL_PROJECTION_MATRIX 0x0BA7 +#define GL_PROJECTION_STACK_DEPTH 0x0BA4 +#define GL_RENDER_MODE 0x0C40 +#define GL_RGBA_MODE 0x0C31 +#define GL_TEXTURE_MATRIX 0x0BA8 +#define GL_TEXTURE_STACK_DEPTH 0x0BA5 +#define GL_VIEWPORT 0x0BA2 + +/* Evaluators */ +#define GL_AUTO_NORMAL 0x0D80 +#define GL_MAP1_COLOR_4 0x0D90 +#define GL_MAP1_INDEX 0x0D91 +#define GL_MAP1_NORMAL 0x0D92 +#define GL_MAP1_TEXTURE_COORD_1 0x0D93 +#define GL_MAP1_TEXTURE_COORD_2 0x0D94 +#define GL_MAP1_TEXTURE_COORD_3 0x0D95 +#define GL_MAP1_TEXTURE_COORD_4 0x0D96 +#define GL_MAP1_VERTEX_3 0x0D97 +#define GL_MAP1_VERTEX_4 0x0D98 +#define GL_MAP2_COLOR_4 0x0DB0 +#define GL_MAP2_INDEX 0x0DB1 +#define GL_MAP2_NORMAL 0x0DB2 +#define GL_MAP2_TEXTURE_COORD_1 0x0DB3 +#define GL_MAP2_TEXTURE_COORD_2 0x0DB4 +#define GL_MAP2_TEXTURE_COORD_3 0x0DB5 +#define GL_MAP2_TEXTURE_COORD_4 0x0DB6 +#define GL_MAP2_VERTEX_3 0x0DB7 +#define GL_MAP2_VERTEX_4 0x0DB8 +#define GL_MAP1_GRID_DOMAIN 0x0DD0 +#define GL_MAP1_GRID_SEGMENTS 0x0DD1 +#define GL_MAP2_GRID_DOMAIN 0x0DD2 +#define GL_MAP2_GRID_SEGMENTS 0x0DD3 +#define GL_COEFF 0x0A00 +#define GL_DOMAIN 0x0A02 +#define GL_ORDER 0x0A01 + +/* Hints */ +#define GL_FOG_HINT 0x0C54 +#define GL_LINE_SMOOTH_HINT 0x0C52 +#define GL_PERSPECTIVE_CORRECTION_HINT 0x0C50 +#define GL_POINT_SMOOTH_HINT 0x0C51 +#define GL_POLYGON_SMOOTH_HINT 0x0C53 +#define GL_DONT_CARE 0x1100 +#define GL_FASTEST 0x1101 +#define GL_NICEST 0x1102 + +/* Scissor box */ +#define GL_SCISSOR_TEST 0x0C11 +#define GL_SCISSOR_BOX 0x0C10 + +/* Pixel Mode / Transfer */ +#define GL_MAP_COLOR 0x0D10 +#define GL_MAP_STENCIL 0x0D11 +#define GL_INDEX_SHIFT 0x0D12 +#define GL_INDEX_OFFSET 0x0D13 +#define GL_RED_SCALE 0x0D14 +#define GL_RED_BIAS 0x0D15 +#define GL_GREEN_SCALE 0x0D18 +#define GL_GREEN_BIAS 0x0D19 +#define GL_BLUE_SCALE 0x0D1A +#define GL_BLUE_BIAS 0x0D1B +#define GL_ALPHA_SCALE 0x0D1C +#define GL_ALPHA_BIAS 0x0D1D +#define GL_DEPTH_SCALE 0x0D1E +#define GL_DEPTH_BIAS 0x0D1F +#define GL_PIXEL_MAP_S_TO_S_SIZE 0x0CB1 +#define GL_PIXEL_MAP_I_TO_I_SIZE 0x0CB0 +#define GL_PIXEL_MAP_I_TO_R_SIZE 0x0CB2 +#define GL_PIXEL_MAP_I_TO_G_SIZE 0x0CB3 +#define GL_PIXEL_MAP_I_TO_B_SIZE 0x0CB4 +#define GL_PIXEL_MAP_I_TO_A_SIZE 0x0CB5 +#define GL_PIXEL_MAP_R_TO_R_SIZE 0x0CB6 +#define GL_PIXEL_MAP_G_TO_G_SIZE 0x0CB7 +#define GL_PIXEL_MAP_B_TO_B_SIZE 0x0CB8 +#define GL_PIXEL_MAP_A_TO_A_SIZE 0x0CB9 +#define GL_PIXEL_MAP_S_TO_S 0x0C71 +#define GL_PIXEL_MAP_I_TO_I 0x0C70 +#define GL_PIXEL_MAP_I_TO_R 0x0C72 +#define GL_PIXEL_MAP_I_TO_G 0x0C73 +#define GL_PIXEL_MAP_I_TO_B 0x0C74 +#define GL_PIXEL_MAP_I_TO_A 0x0C75 +#define GL_PIXEL_MAP_R_TO_R 0x0C76 +#define GL_PIXEL_MAP_G_TO_G 0x0C77 +#define GL_PIXEL_MAP_B_TO_B 0x0C78 +#define GL_PIXEL_MAP_A_TO_A 0x0C79 +#define GL_PACK_ALIGNMENT 0x0D05 +#define GL_PACK_LSB_FIRST 0x0D01 +#define GL_PACK_ROW_LENGTH 0x0D02 +#define GL_PACK_SKIP_PIXELS 0x0D04 +#define GL_PACK_SKIP_ROWS 0x0D03 +#define GL_PACK_SWAP_BYTES 0x0D00 +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_UNPACK_LSB_FIRST 0x0CF1 +#define GL_UNPACK_ROW_LENGTH 0x0CF2 +#define GL_UNPACK_SKIP_PIXELS 0x0CF4 +#define GL_UNPACK_SKIP_ROWS 0x0CF3 +#define GL_UNPACK_SWAP_BYTES 0x0CF0 +#define GL_ZOOM_X 0x0D16 +#define GL_ZOOM_Y 0x0D17 + +/* Texture mapping */ +#define GL_TEXTURE_ENV 0x2300 +#define GL_TEXTURE_ENV_MODE 0x2200 +#define GL_TEXTURE_1D 0x0DE0 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_TEXTURE_ENV_COLOR 0x2201 +#define GL_TEXTURE_GEN_S 0x0C60 +#define GL_TEXTURE_GEN_T 0x0C61 +#define GL_TEXTURE_GEN_MODE 0x2500 +#define GL_TEXTURE_BORDER_COLOR 0x1004 +#define GL_TEXTURE_WIDTH 0x1000 +#define GL_TEXTURE_HEIGHT 0x1001 +#define GL_TEXTURE_BORDER 0x1005 +#define GL_TEXTURE_COMPONENTS 0x1003 +#define GL_TEXTURE_RED_SIZE 0x805C +#define GL_TEXTURE_GREEN_SIZE 0x805D +#define GL_TEXTURE_BLUE_SIZE 0x805E +#define GL_TEXTURE_ALPHA_SIZE 0x805F +#define GL_TEXTURE_LUMINANCE_SIZE 0x8060 +#define GL_TEXTURE_INTENSITY_SIZE 0x8061 +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 +#define GL_OBJECT_LINEAR 0x2401 +#define GL_OBJECT_PLANE 0x2501 +#define GL_EYE_LINEAR 0x2400 +#define GL_EYE_PLANE 0x2502 +#define GL_SPHERE_MAP 0x2402 +#define GL_DECAL 0x2101 +#define GL_MODULATE 0x2100 +#define GL_NEAREST 0x2600 +#define GL_REPEAT 0x2901 +#define GL_CLAMP 0x2900 +#define GL_S 0x2000 +#define GL_T 0x2001 +#define GL_R 0x2002 +#define GL_Q 0x2003 +#define GL_TEXTURE_GEN_R 0x0C62 +#define GL_TEXTURE_GEN_Q 0x0C63 +#define GL_TEXTURE_PRIORITY 0x8066 +#define GL_TEXTURE_RESIDENT 0x8067 +#define GL_TEXTURE_BINDING_1D 0x8068 +#define GL_TEXTURE_BINDING_2D 0x8069 +#define GL_TEXTURE_BINDING_3D 0x806A + +/* Utility */ +#define GL_VENDOR 0x1F00 +#define GL_RENDERER 0x1F01 +#define GL_VERSION 0x1F02 +#define GL_EXTENSIONS 0x1F03 + +/* Errors */ +#define GL_NO_ERROR 0x0 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_OPERATION 0x0502 +#define GL_STACK_OVERFLOW 0x0503 +#define GL_STACK_UNDERFLOW 0x0504 +#define GL_OUT_OF_MEMORY 0x0505 + +/* glPush/PopAttrib bits */ +#define GL_CURRENT_BIT 0x00000001 +#define GL_POINT_BIT 0x00000002 +#define GL_LINE_BIT 0x00000004 +#define GL_POLYGON_BIT 0x00000008 +#define GL_POLYGON_STIPPLE_BIT 0x00000010 +#define GL_PIXEL_MODE_BIT 0x00000020 +#define GL_LIGHTING_BIT 0x00000040 +#define GL_FOG_BIT 0x00000080 +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_ACCUM_BUFFER_BIT 0x00000200 +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#define GL_VIEWPORT_BIT 0x00000800 +#define GL_TRANSFORM_BIT 0x00001000 +#define GL_ENABLE_BIT 0x00002000 +#define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_HINT_BIT 0x00008000 +#define GL_EVAL_BIT 0x00010000 +#define GL_LIST_BIT 0x00020000 +#define GL_TEXTURE_BIT 0x00040000 +#define GL_SCISSOR_BIT 0x00080000 +#define GL_ALL_ATTRIB_BITS 0x000FFFFF + diff --git a/Engine/source/gfx/gl/ggl/generated/glcfn.h b/Engine/source/gfx/gl/ggl/generated/glcfn.h new file mode 100644 index 000000000..f5af6c40e --- /dev/null +++ b/Engine/source/gfx/gl/ggl/generated/glcfn.h @@ -0,0 +1,347 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// OpenGL 1.1 core functions +GL_GROUP_BEGIN(GL_VERSION_1_1) +GL_FUNCTION(glAccum, void, (GLenum op, GLfloat value)) +GL_FUNCTION(glAlphaFunc, void, (GLenum func, GLclampf ref)) +GL_FUNCTION(glAreTexturesResident, void, (GLsizei n, const GLuint *textures, GLboolean *residences)) +GL_FUNCTION(glArrayElement, void, (GLint i)) +GL_FUNCTION(glBegin, void, (GLenum mode)) +GL_FUNCTION(glBindTexture, void, (GLenum target, GLuint texture)) +GL_FUNCTION(glBitmap, void, (GLsizei width, GLsizei height, GLfloat xorig, GLfloat yorig, GLfloat xmove, GLfloat ymove, const GLubyte *bitmap)) +GL_FUNCTION(glBlendFunc, void, (GLenum sfactor, GLenum dfactor)) +GL_FUNCTION(glCallList, void, (GLuint list)) +GL_FUNCTION(glCallLists, void, (GLsizei n, GLenum type, const GLvoid *lists)) +GL_FUNCTION(glClear, void, (GLbitfield mask)) +GL_FUNCTION(glClearAccum, void, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)) +GL_FUNCTION(glClearColor, void, (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)) +GL_FUNCTION(glClearDepth, void, (GLclampd depth)) +GL_FUNCTION(glClearIndex, void, (GLfloat c)) +GL_FUNCTION(glClearStencil, void, (GLint s)) +GL_FUNCTION(glClipPlane, void, (GLenum plane, const GLdouble *equation)) +GL_FUNCTION(glColor3b, void, (GLbyte red, GLbyte green, GLbyte blue)) +GL_FUNCTION(glColor3bv, void, (const GLbyte *v)) +GL_FUNCTION(glColor3d, void, (GLdouble red, GLdouble green, GLdouble blue)) +GL_FUNCTION(glColor3dv, void, (const GLdouble *v)) +GL_FUNCTION(glColor3f, void, (GLfloat red, GLfloat green, GLfloat blue)) +GL_FUNCTION(glColor3fv, void, (const GLfloat *v)) +GL_FUNCTION(glColor3i, void, (GLint red, GLint green, GLint blue)) +GL_FUNCTION(glColor3iv, void, (const GLint *v)) +GL_FUNCTION(glColor3s, void, (GLshort red, GLshort green, GLshort blue)) +GL_FUNCTION(glColor3sv, void, (const GLshort *v)) +GL_FUNCTION(glColor3ub, void, (GLubyte red, GLubyte green, GLubyte blue)) +GL_FUNCTION(glColor3ubv, void, (const GLubyte *v)) +GL_FUNCTION(glColor3ui, void, (GLuint red, GLuint green, GLuint blue)) +GL_FUNCTION(glColor3uiv, void, (const GLuint *v)) +GL_FUNCTION(glColor3us, void, (GLushort red, GLushort green, GLushort blue)) +GL_FUNCTION(glColor3usv, void, (const GLushort *v)) +GL_FUNCTION(glColor4b, void, (GLbyte red, GLbyte green, GLbyte blue, GLbyte alpha)) +GL_FUNCTION(glColor4bv, void, (const GLbyte *v)) +GL_FUNCTION(glColor4d, void, (GLdouble red, GLdouble green, GLdouble blue, GLdouble alpha)) +GL_FUNCTION(glColor4dv, void, (const GLdouble *v)) +GL_FUNCTION(glColor4f, void, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)) +GL_FUNCTION(glColor4fv, void, (const GLfloat *v)) +GL_FUNCTION(glColor4i, void, (GLint red, GLint green, GLint blue, GLint alpha)) +GL_FUNCTION(glColor4iv, void, (const GLint *v)) +GL_FUNCTION(glColor4s, void, (GLshort red, GLshort green, GLshort blue, GLshort alpha)) +GL_FUNCTION(glColor4sv, void, (const GLshort *v)) +GL_FUNCTION(glColor4ub, void, (GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha)) +GL_FUNCTION(glColor4ubv, void, (const GLubyte *v)) +GL_FUNCTION(glColor4ui, void, (GLuint red, GLuint green, GLuint blue, GLuint alpha)) +GL_FUNCTION(glColor4uiv, void, (const GLuint *v)) +GL_FUNCTION(glColor4us, void, (GLushort red, GLushort green, GLushort blue, GLushort alpha)) +GL_FUNCTION(glColor4usv, void, (const GLushort *v)) +GL_FUNCTION(glColorMask, void, (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)) +GL_FUNCTION(glColorMaterial, void, (GLenum face, GLenum mode)) +GL_FUNCTION(glCopyPixels, void, (GLint x, GLint y, GLsizei width, GLsizei height, GLenum type)) +GL_FUNCTION(glCopyTexImage1D, void, (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border)) +GL_FUNCTION(glCopyTexImage2D, void, (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border)) +GL_FUNCTION(glCopyTexSubImage1D, void, (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width)) +GL_FUNCTION(glCopyTexSubImage2D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height)) +GL_FUNCTION(glCullFace, void, (GLenum mode)) +GL_FUNCTION(glDeleteLists, void, (GLuint list, GLsizei range)) +GL_FUNCTION(glDeleteTextures, void, (GLsizei n, const GLuint *textures)) +GL_FUNCTION(glDepthFunc, void, (GLenum func)) +GL_FUNCTION(glDepthMask, void, (GLboolean flag)) +GL_FUNCTION(glDepthRange, void, (GLclampd zNear, GLclampd zFar)) +GL_FUNCTION(glDisable, void, (GLenum cap)) +GL_FUNCTION(glDisableClientState, void, (GLenum array)) +GL_FUNCTION(glDrawArrays, void, (GLenum mode, GLint first, GLsizei count)) +GL_FUNCTION(glDrawBuffer, void, (GLenum mode)) +GL_FUNCTION(glDrawElements, void, (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices)) +GL_FUNCTION(glDrawPixels, void, (GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels)) +GL_FUNCTION(glEdgeFlag, void, (GLboolean flag)) +GL_FUNCTION(glEdgeFlagPointer, void, (GLsizei stride, const GLboolean *pointer)) +GL_FUNCTION(glEnable, void, (GLenum cap)) +GL_FUNCTION(glEndList, void, (void)) +GL_FUNCTION(glEnableClientState, void, (GLenum array)) +GL_FUNCTION(glEnd, void, (void)) +GL_FUNCTION(glEvalCoord1d, void, (GLdouble u)) +GL_FUNCTION(glEvalCoord1f, void, (GLfloat u)) +GL_FUNCTION(glEvalCoord2d, void, (GLdouble u, GLdouble v)) +GL_FUNCTION(glEvalCoord2f, void, (GLfloat u, GLfloat v)) +GL_FUNCTION(glEvalMesh1, void, (GLenum mode, GLint i1, GLint i2)) +GL_FUNCTION(glEvalMesh2, void, (GLenum mode, GLint i1, GLint i2, GLint j1, GLint j2)) +GL_FUNCTION(glEvalPoint1, void, (GLint i)) +GL_FUNCTION(glEvalPoint2, void, (GLint i, GLint j)) +GL_FUNCTION(glFeedbackBuffer, void, (GLsizei size, GLenum type, GLfloat *buffer)) +GL_FUNCTION(glFinish, void, (void)) +GL_FUNCTION(glFlush, void, (void)) +GL_FUNCTION(glFogf, void, (GLenum pname, GLfloat param)) +GL_FUNCTION(glFogfv, void, (GLenum pname, const GLfloat *params)) +GL_FUNCTION(glFogi, void, (GLenum pname, GLint param)) +GL_FUNCTION(glFogiv, void, (GLenum pname, const GLint *params)) +GL_FUNCTION(glFrontFace, void, (GLenum mode)) +GL_FUNCTION(glFrustum, void, (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar)) +GL_FUNCTION(glGenLists, GLuint, (GLsizei range)) +GL_FUNCTION(glGenTextures, void, (GLsizei n, GLuint *textures)) +GL_FUNCTION(glGetBooleanv, void, (GLenum pname, GLboolean *params)) +GL_FUNCTION(glGetClipPlane, void, (GLenum plane, GLdouble *equation)) +GL_FUNCTION(glColorPointer, void, (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)) +GL_FUNCTION(glGetDoublev, void, (GLenum pname, GLdouble *params)) +GL_FUNCTION(glGetFloatv, void, (GLenum pname, GLfloat *params)) +GL_FUNCTION(glGetError, GLuint, (void)) +GL_FUNCTION(glGetIntegerv, void, (GLenum pname, GLint *params)) +GL_FUNCTION(glGetLightfv, void, (GLenum light, GLenum pname, GLfloat *params)) +GL_FUNCTION(glGetLightiv, void, (GLenum light, GLenum pname, GLint *params)) +GL_FUNCTION(glGetMapdv, void, (GLenum target, GLenum query, GLdouble *v)) +GL_FUNCTION(glGetMapfv, void, (GLenum target, GLenum query, GLfloat *v)) +GL_FUNCTION(glGetMapiv, void, (GLenum target, GLenum query, GLint *v)) +GL_FUNCTION(glGetMaterialfv, void, (GLenum face, GLenum pname, GLfloat *params)) +GL_FUNCTION(glGetMaterialiv, void, (GLenum face, GLenum pname, GLint *params)) +GL_FUNCTION(glGetPixelMapfv, void, (GLenum map, GLfloat *values)) +GL_FUNCTION(glGetPixelMapuiv, void, (GLenum map, GLuint *values)) +GL_FUNCTION(glGetPixelMapusv, void, (GLenum map, GLushort *values)) +GL_FUNCTION(glGetPointerv, void, (GLenum pname, GLvoid* *params)) +GL_FUNCTION(glGetPolygonStipple, void, (GLubyte *mask)) +GL_FUNCTION(glGetString, const GLubyte*, (GLenum name)) +GL_FUNCTION(glGetTexEnvfv, void, (GLenum target, GLenum pname, GLfloat *params)) +GL_FUNCTION(glGetTexEnviv, void, (GLenum target, GLenum pname, GLint *params)) +GL_FUNCTION(glGetTexGendv, void, (GLenum coord, GLenum pname, GLdouble *params)) +GL_FUNCTION(glGetTexGenfv, void, (GLenum coord, GLenum pname, GLfloat *params)) +GL_FUNCTION(glGetTexGeniv, void, (GLenum coord, GLenum pname, GLint *params)) +GL_FUNCTION(glGetTexImage, void, (GLenum target, GLint level, GLenum format, GLenum type, GLvoid *pixels)) +GL_FUNCTION(glGetTexLevelParameterfv, void, (GLenum target, GLint level, GLenum pname, GLfloat *params)) +GL_FUNCTION(glGetTexLevelParameteriv, void, (GLenum target, GLint level, GLenum pname, GLint *params)) +GL_FUNCTION(glGetTexParameterfv, void, (GLenum target, GLenum pname, GLfloat *params)) +GL_FUNCTION(glGetTexParameteriv, void, (GLenum target, GLenum pname, GLint *params)) +GL_FUNCTION(glHint, void, (GLenum target, GLenum mode)) +GL_FUNCTION(glIndexd, void, (GLdouble c)) +GL_FUNCTION(glIndexf, void, (GLfloat c)) +GL_FUNCTION(glIndexi, void, (GLint c)) +GL_FUNCTION(glIndexs, void, (GLshort c)) +GL_FUNCTION(glIndexub, void, (GLubyte c)) +GL_FUNCTION(glIndexMask, void, (GLuint mask)) +GL_FUNCTION(glIndexPointer, void, (GLenum type, GLsizei stride, const GLvoid *pointer)) +GL_FUNCTION(glInitNames, void, (void)) +GL_FUNCTION(glInterleavedArrays, void, (GLenum format, GLsizei stride, const GLvoid *pointer)) +GL_FUNCTION(glIsEnabled, GLboolean, (GLenum cap)) +GL_FUNCTION(glIsList, GLboolean, (GLuint list)) +GL_FUNCTION(glIsTexture, GLboolean, (GLuint texture)) +GL_FUNCTION(glLightf, void, (GLenum light, GLenum pname, GLfloat param)) +GL_FUNCTION(glLightfv, void, (GLenum light, GLenum pname, const GLfloat *params)) +GL_FUNCTION(glLighti, void, (GLenum light, GLenum pname, GLint param)) +GL_FUNCTION(glLightiv, void, (GLenum light, GLenum pname, const GLint *params)) +GL_FUNCTION(glLightModelf, void, (GLenum pname, GLfloat param)) +GL_FUNCTION(glLightModelfv, void, (GLenum pname, const GLfloat *params)) +GL_FUNCTION(glLightModeli, void, (GLenum pname, GLint param)) +GL_FUNCTION(glLightModeliv, void, (GLenum pname, const GLint *params)) +GL_FUNCTION(glLineStipple, void, (GLint factor, GLushort pattern)) +GL_FUNCTION(glLineWidth, void, (GLfloat width)) +GL_FUNCTION(glListBase, void, (GLuint base)) +GL_FUNCTION(glLoadIdentity, void, (void)) +GL_FUNCTION(glLoadMatrixd, void, (const GLdouble *m)) +GL_FUNCTION(glLoadMatrixf, void, (const GLfloat *m)) +GL_FUNCTION(glLoadName, void, (GLuint name)) +GL_FUNCTION(glLogicOp, void, (GLenum opcode)) +GL_FUNCTION(glMap1d, void, (GLenum target, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points)) +GL_FUNCTION(glMap1f, void, (GLenum target, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLdouble *points)) +GL_FUNCTION(glMap2d, void, (GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points)) +GL_FUNCTION(glMap2f, void, (GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points)) +GL_FUNCTION(glMapGrid1d, void, (GLint un, GLdouble u1, GLdouble u2)) +GL_FUNCTION(glMapGrid1f, void, (GLint un, GLfloat u1, GLfloat u2)) +GL_FUNCTION(glMapGrid2d, void, (GLint un, GLdouble u1, GLdouble u2, GLint vn, GLdouble v1, GLdouble v2)) +GL_FUNCTION(glMapGrid2f, void, (GLint un, GLfloat u1, GLfloat u2, GLint vn, GLfloat v1, GLfloat v2)) +GL_FUNCTION(glMaterialf, void, (GLenum face, GLenum pname, GLfloat param)) +GL_FUNCTION(glMaterialfv, void, (GLenum face, GLenum pname, const GLfloat* params)) +GL_FUNCTION(glMateriali, void, (GLenum face, GLenum pname, GLint param)) +GL_FUNCTION(glMaterialiv, void, (GLenum face, GLenum pname, const GLint* params)) +GL_FUNCTION(glMatrixMode, void, (GLenum mode)) +GL_FUNCTION(glMultMatrixd, void, (const GLdouble *m)) +GL_FUNCTION(glMultMatrixf, void, (const GLfloat *m)) +GL_FUNCTION(glNewList, void, (GLuint list, GLenum mode)) +GL_FUNCTION(glNormal3b, void, (GLbyte nx, GLbyte ny, GLbyte nz)) +GL_FUNCTION(glNormal3bv, void, (const GLbyte *v)) +GL_FUNCTION(glNormal3d, void, (GLdouble nx, GLdouble ny, GLdouble nz)) +GL_FUNCTION(glNormal3dv, void, (const GLdouble *v)) +GL_FUNCTION(glNormal3f, void, (GLfloat nx, GLfloat ny, GLfloat nz)) +GL_FUNCTION(glNormal3fv, void, (const GLfloat *v)) +GL_FUNCTION(glNormal3i, void, (GLint nx, GLint ny, GLint nz)) +GL_FUNCTION(glNormal3iv, void, (const GLint *v)) +GL_FUNCTION(glNormal3s, void, (GLshort nx, GLshort ny, GLshort nz)) +GL_FUNCTION(glNormal3sv, void, (const GLshort *v)) +GL_FUNCTION(glNormalPointer, void, (GLenum type, GLsizei stride, const GLvoid *pointer)) +GL_FUNCTION(glOrtho, void, (GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar)) +GL_FUNCTION(glPassThrough, void, (GLfloat token)) +GL_FUNCTION(glPixelMapfv, void, (GLenum map, GLint mapSize, const GLfloat *values)) +GL_FUNCTION(glPixelMapuiv, void, (GLenum map, GLint mapSize, const GLuint *values)) +GL_FUNCTION(glPixelMapusv, void, (GLenum map, GLint mapSize, const GLushort *values)) +GL_FUNCTION(glPixelStoref, void, (GLenum pname, GLfloat param)) +GL_FUNCTION(glPixelStorei, void, (GLenum pname, GLint param)) +GL_FUNCTION(glPixelTransferf, void, (GLenum pname, GLfloat param)) +GL_FUNCTION(glPixelTransferi, void, (GLenum pname, GLint param)) +GL_FUNCTION(glPixelZoom, void, (GLfloat xfactor, GLfloat yfactor)) +GL_FUNCTION(glPointSize, void, (GLfloat size)) +GL_FUNCTION(glPolygonMode, void, (GLenum face, GLenum mode)) +GL_FUNCTION(glPolygonOffset, void, (GLfloat factor, GLfloat units)) +GL_FUNCTION(glPopAttrib, void, (void)) +GL_FUNCTION(glPopMatrix, void, (void)) +GL_FUNCTION(glPopName, void, (void)) +GL_FUNCTION(glPrioritizeTextures, void, (GLsizei n, const GLuint *textures, const GLclampf *priorities)) +GL_FUNCTION(glPushAttrib, void, (GLbitfield mask)) +GL_FUNCTION(glPushMatrix, void, (void)) +GL_FUNCTION(glPushName, void, (GLuint name)) +GL_FUNCTION(glRasterPos2d, void, (GLdouble x, GLdouble y)) +GL_FUNCTION(glRasterPos2dv, void, (const GLdouble *v)) +GL_FUNCTION(glRasterPos2f, void, (GLfloat x, GLfloat y)) +GL_FUNCTION(glRasterPos2fv, void, (const GLfloat *v)) +GL_FUNCTION(glRasterPos2i, void, (GLint x, GLint y)) +GL_FUNCTION(glRasterPos2iv, void, (const GLint *v)) +GL_FUNCTION(glRasterPos2s, void, (GLshort x, GLshort y)) +GL_FUNCTION(glRasterPos2sv, void, (const GLshort *v)) +GL_FUNCTION(glRasterPos3d, void, (GLdouble x, GLdouble y, GLdouble z)) +GL_FUNCTION(glRasterPos3dv, void, (const GLdouble *v)) +GL_FUNCTION(glRasterPos3f, void, (GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glRasterPos3fv, void, (const GLfloat *v)) +GL_FUNCTION(glRasterPos3i, void, (GLint x, GLint y, GLint z)) +GL_FUNCTION(glRasterPos3iv, void, (const GLint *v)) +GL_FUNCTION(glRasterPos3s, void, (GLshort x, GLshort y, GLshort z)) +GL_FUNCTION(glRasterPos3sv, void, (const GLshort *v)) +GL_FUNCTION(glRasterPos4d, void, (GLdouble x, GLdouble y, GLdouble z, GLdouble w)) +GL_FUNCTION(glRasterPos4dv, void, (const GLdouble *v)) +GL_FUNCTION(glRasterPos4f, void, (GLfloat x, GLfloat y, GLfloat z, GLfloat w)) +GL_FUNCTION(glRasterPos4fv, void, (const GLfloat *v)) +GL_FUNCTION(glRasterPos4i, void, (GLint x, GLint y, GLint z, GLint w)) +GL_FUNCTION(glRasterPos4iv, void, (const GLint *v)) +GL_FUNCTION(glRasterPos4s, void, (GLshort x, GLshort y, GLshort z, GLshort w)) +GL_FUNCTION(glRasterPos4sv, void, (const GLshort *v)) +GL_FUNCTION(glReadBuffer, void, (GLenum mode)) +GL_FUNCTION(glReadPixels, void, (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels)) +GL_FUNCTION(glRectd, void, (GLdouble x1, GLdouble y1, GLdouble x2, GLdouble y2)) +GL_FUNCTION(glRectdv, void, (const GLdouble *v)) +GL_FUNCTION(glRectf, void, (GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2)) +GL_FUNCTION(glRectfv, void, (const GLfloat *v)) +GL_FUNCTION(glRecti, void, (GLint x1, GLint y1, GLint x2, GLint y2)) +GL_FUNCTION(glRectiv, void, (const GLint *v)) +GL_FUNCTION(glRects, void, (GLshort x1, GLshort y1, GLshort x2, GLshort y2)) +GL_FUNCTION(glRectsv, void, (const GLshort *v)) +GL_FUNCTION(glRenderMode, void, (GLenum mode)) +GL_FUNCTION(glRotated, void, (GLdouble angle, GLdouble x, GLdouble y, GLdouble z)) +GL_FUNCTION(glRotatef, void, (GLfloat angle, GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glScaled, void, (GLdouble x, GLdouble y, GLdouble z)) +GL_FUNCTION(glScalef, void, (GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glScissor, void, (GLint x, GLint y, GLsizei width, GLsizei height)) +GL_FUNCTION(glSelectBuffer, void, (GLsizei size, GLuint *buffer)) +GL_FUNCTION(glShadeModel, void, (GLenum mode)) +GL_FUNCTION(glStencilFunc, void, (GLenum func, GLint ref, GLuint mask)) +GL_FUNCTION(glStencilMask, void, (GLuint mask)) +GL_FUNCTION(glStencilOp, void, (GLenum fail, GLenum zfail, GLenum zpass)) +GL_FUNCTION(glTexCoord1d, void, (GLdouble s)) +GL_FUNCTION(glTexCoord1dv, void, (const GLdouble *v)) +GL_FUNCTION(glTexCoord1f, void, (GLfloat s)) +GL_FUNCTION(glTexCoord1fv, void, (const GLfloat *v)) +GL_FUNCTION(glTexCoord1i, void, (GLint s)) +GL_FUNCTION(glTexCoord1iv, void, (const GLint *v)) +GL_FUNCTION(glTexCoord1s, void, (GLshort s)) +GL_FUNCTION(glTexCoord1sv, void, (const GLshort *v)) +GL_FUNCTION(glTexCoord2d, void, (GLdouble s, GLdouble t)) +GL_FUNCTION(glTexCoord2dv, void, (const GLdouble *v)) +GL_FUNCTION(glTexCoord2f, void, (GLfloat s, GLfloat t)) +GL_FUNCTION(glTexCoord2fv, void, (const GLfloat *v)) +GL_FUNCTION(glTexCoord2i, void, (GLint s, GLint t)) +GL_FUNCTION(glTexCoord2iv, void, (const GLint *v)) +GL_FUNCTION(glTexCoord2s, void, (GLshort s, GLshort t)) +GL_FUNCTION(glTexCoord2sv, void, (const GLshort *v)) +GL_FUNCTION(glTexCoord3d, void, (GLdouble s, GLdouble t, GLdouble r)) +GL_FUNCTION(glTexCoord3dv, void, (const GLdouble *v)) +GL_FUNCTION(glTexCoord3f, void, (GLfloat s, GLfloat t, GLfloat r)) +GL_FUNCTION(glTexCoord3fv, void, (const GLfloat *v)) +GL_FUNCTION(glTexCoord3i, void, (GLint s, GLint t, GLint r)) +GL_FUNCTION(glTexCoord3iv, void, (const GLint *v)) +GL_FUNCTION(glTexCoord3s, void, (GLshort s, GLshort t, GLshort r)) +GL_FUNCTION(glTexCoord3sv, void, (const GLshort *v)) +GL_FUNCTION(glTexCoord4d, void, (GLdouble s, GLdouble t, GLdouble r, GLdouble q)) +GL_FUNCTION(glTexCoord4dv, void, (const GLdouble *v)) +GL_FUNCTION(glTexCoord4f, void, (GLfloat s, GLfloat t, GLfloat r, GLfloat q)) +GL_FUNCTION(glTexCoord4fv, void, (const GLfloat *v)) +GL_FUNCTION(glTexCoord4i, void, (GLint s, GLint t, GLint r, GLint q)) +GL_FUNCTION(glTexCoord4iv, void, (const GLint *v)) +GL_FUNCTION(glTexCoord4s, void, (GLshort s, GLshort t, GLshort r, GLshort q)) +GL_FUNCTION(glTexCoord4sv, void, (const GLshort *v)) +GL_FUNCTION(glTexCoordPointer, void, (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)) +GL_FUNCTION(glTexEnvf, void, (GLenum target, GLenum pname, GLfloat param)) +GL_FUNCTION(glTexEnvfv, void, (GLenum target, GLenum pname, const GLfloat *params)) +GL_FUNCTION(glTexEnvi, void, (GLenum target, GLenum pname, GLint param)) +GL_FUNCTION(glTexEnviv, void, (GLenum target, GLenum pname, const GLint *params)) +GL_FUNCTION(glTexGend, void, (GLenum coord, GLenum pname, GLdouble param)) +GL_FUNCTION(glTexGendv, void, (GLenum coord, GLenum pname, const GLdouble *param)) +GL_FUNCTION(glTexGenf, void, (GLenum coord, GLenum pname, GLfloat param)) +GL_FUNCTION(glTexGenfv, void, (GLenum coord, GLenum pname, const GLfloat *param)) +GL_FUNCTION(glTexGeni, void, (GLenum coord, GLenum pname, GLint param)) +GL_FUNCTION(glTexGeniv, void, (GLenum coord, GLenum pname, const GLint *param)) +GL_FUNCTION(glTexImage1D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels)) +GL_FUNCTION(glTexImage2D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels)) +GL_FUNCTION(glTexParameterf, void, (GLenum target, GLenum pname, GLfloat param)) +GL_FUNCTION(glTexParameteri, void, (GLenum target, GLenum pname, GLint param)) +GL_FUNCTION(glTexSubImage1D, void, (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels)) +GL_FUNCTION(glTexSubImage2D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels)) +GL_FUNCTION(glTranslated, void, (GLdouble x, GLdouble y, GLdouble z)) +GL_FUNCTION(glTranslatef, void, (GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glVertex2d, void, (GLdouble x, GLdouble y)) +GL_FUNCTION(glVertex2dv, void, (const GLdouble *v)) +GL_FUNCTION(glVertex2f, void, (GLfloat x, GLfloat y)) +GL_FUNCTION(glVertex2fv, void, (const GLfloat *v)) +GL_FUNCTION(glVertex2i, void, (GLint x, GLint y)) +GL_FUNCTION(glVertex2iv, void, (const GLint *v)) +GL_FUNCTION(glVertex2s, void, (GLshort x, GLshort y)) +GL_FUNCTION(glVertex2sv, void, (const GLshort *v)) +GL_FUNCTION(glVertex3d, void, (GLdouble x, GLdouble y, GLdouble z)) +GL_FUNCTION(glVertex3dv, void, (const GLdouble *v)) +GL_FUNCTION(glVertex3f, void, (GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glVertex3fv, void, (const GLfloat *v)) +GL_FUNCTION(glVertex3i, void, (GLint x, GLint y, GLint z)) +GL_FUNCTION(glVertex3iv, void, (const GLint *v)) +GL_FUNCTION(glVertex3s, void, (GLshort x, GLshort y, GLshort z)) +GL_FUNCTION(glVertex3sv, void, (const GLshort *v)) +GL_FUNCTION(glVertex4d, void, (GLdouble x, GLdouble y, GLdouble z, GLdouble w)) +GL_FUNCTION(glVertex4dv, void, (const GLdouble *v)) +GL_FUNCTION(glVertex4f, void, (GLfloat x, GLfloat y, GLfloat z, GLfloat w)) +GL_FUNCTION(glVertex4fv, void, (const GLfloat *v)) +GL_FUNCTION(glVertex4i, void, (GLint x, GLint y, GLint z, GLint w)) +GL_FUNCTION(glVertex4iv, void, (const GLint *v)) +GL_FUNCTION(glVertex4s, void, (GLshort x, GLshort y, GLshort z, GLshort w)) +GL_FUNCTION(glVertex4sv, void, (const GLshort *v)) +GL_FUNCTION(glVertexPointer, void, (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)) +GL_FUNCTION(glViewport, void, (GLint x, GLint y, GLsizei width, GLsizei height)) +GL_GROUP_END() + diff --git a/Engine/source/gfx/gl/ggl/generated/gle.h b/Engine/source/gfx/gl/ggl/generated/gle.h new file mode 100644 index 000000000..2b9b0e46f --- /dev/null +++ b/Engine/source/gfx/gl/ggl/generated/gle.h @@ -0,0 +1,3997 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +typedef int ptrdiff_t; +typedef void (*GLFunction)(); + +#ifdef GL_VERSION_1_2 +#define GL_UNSIGNED_BYTE_3_3_2 0x8032 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 +#define GL_UNSIGNED_INT_10_10_10_2 0x8036 +#define GL_RESCALE_NORMAL 0x803A +#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#define GL_BGR 0x80E0 +#define GL_BGRA 0x80E1 +#define GL_MAX_ELEMENTS_VERTICES 0x80E8 +#define GL_MAX_ELEMENTS_INDICES 0x80E9 +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_TEXTURE_MIN_LOD 0x813A +#define GL_TEXTURE_MAX_LOD 0x813B +#define GL_TEXTURE_BASE_LEVEL 0x813C +#define GL_TEXTURE_MAX_LEVEL 0x813D +#define GL_LIGHT_MODEL_COLOR_CONTROL 0x81F8 +#define GL_SINGLE_COLOR 0x81F9 +#define GL_SEPARATE_SPECULAR_COLOR 0x81FA +#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 +#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 +#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_ALIASED_POINT_SIZE_RANGE 0x846D +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#define GL_PACK_SKIP_IMAGES 0x806B +#define GL_PACK_IMAGE_HEIGHT 0x806C +#define GL_UNPACK_SKIP_IMAGES 0x806D +#define GL_UNPACK_IMAGE_HEIGHT 0x806E +#define GL_TEXTURE_3D 0x806F +#define GL_PROXY_TEXTURE_3D 0x8070 +#define GL_TEXTURE_DEPTH 0x8071 +#define GL_TEXTURE_WRAP_R 0x8072 +#define GL_MAX_3D_TEXTURE_SIZE 0x8073 +#define GL_TEXTURE_BINDING_3D 0x806A +#define glDrawRangeElements XGL_FUNCPTR(glDrawRangeElements) +#define glTexImage3D XGL_FUNCPTR(glTexImage3D) +#define glTexSubImage3D XGL_FUNCPTR(glTexSubImage3D) +#define glCopyTexSubImage3D XGL_FUNCPTR(glCopyTexSubImage3D) +#endif + +#ifdef GL_VERSION_1_3 +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE31 0x84DF +#define GL_ACTIVE_TEXTURE 0x84E0 +#define GL_CLIENT_ACTIVE_TEXTURE 0x84E1 +#define GL_MAX_TEXTURE_UNITS 0x84E2 +#define GL_NORMAL_MAP 0x8511 +#define GL_REFLECTION_MAP 0x8512 +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C +#define GL_COMPRESSED_ALPHA 0x84E9 +#define GL_COMPRESSED_LUMINANCE 0x84EA +#define GL_COMPRESSED_LUMINANCE_ALPHA 0x84EB +#define GL_COMPRESSED_INTENSITY 0x84EC +#define GL_COMPRESSED_RGB 0x84ED +#define GL_COMPRESSED_RGBA 0x84EE +#define GL_TEXTURE_COMPRESSION_HINT 0x84EF +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0 +#define GL_TEXTURE_COMPRESSED 0x86A1 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 +#define GL_MULTISAMPLE 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE 0x809F +#define GL_SAMPLE_COVERAGE 0x80A0 +#define GL_SAMPLE_BUFFERS 0x80A8 +#define GL_SAMPLES 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB +#define GL_MULTISAMPLE_BIT 0x20000000 +#define GL_TRANSPOSE_MODELVIEW_MATRIX 0x84E3 +#define GL_TRANSPOSE_PROJECTION_MATRIX 0x84E4 +#define GL_TRANSPOSE_TEXTURE_MATRIX 0x84E5 +#define GL_TRANSPOSE_COLOR_MATRIX 0x84E6 +#define GL_COMBINE 0x8570 +#define GL_COMBINE_RGB 0x8571 +#define GL_COMBINE_ALPHA 0x8572 +#define GL_SOURCE0_RGB 0x8580 +#define GL_SOURCE1_RGB 0x8581 +#define GL_SOURCE2_RGB 0x8582 +#define GL_SOURCE0_ALPHA 0x8588 +#define GL_SOURCE1_ALPHA 0x8589 +#define GL_SOURCE2_ALPHA 0x858A +#define GL_OPERAND0_RGB 0x8590 +#define GL_OPERAND1_RGB 0x8591 +#define GL_OPERAND2_RGB 0x8592 +#define GL_OPERAND0_ALPHA 0x8598 +#define GL_OPERAND1_ALPHA 0x8599 +#define GL_OPERAND2_ALPHA 0x859A +#define GL_RGB_SCALE 0x8573 +#define GL_ADD_SIGNED 0x8574 +#define GL_INTERPOLATE 0x8575 +#define GL_SUBTRACT 0x84E7 +#define GL_CONSTANT 0x8576 +#define GL_PRIMARY_COLOR 0x8577 +#define GL_PREVIOUS 0x8578 +#define GL_DOT3_RGB 0x86AE +#define GL_DOT3_RGBA 0x86AF +#define GL_CLAMP_TO_BORDER 0x812D +#define glActiveTexture XGL_FUNCPTR(glActiveTexture) +#define glClientActiveTexture XGL_FUNCPTR(glClientActiveTexture) +#define glCompressedTexImage1D XGL_FUNCPTR(glCompressedTexImage1D) +#define glCompressedTexImage2D XGL_FUNCPTR(glCompressedTexImage2D) +#define glCompressedTexImage3D XGL_FUNCPTR(glCompressedTexImage3D) +#define glCompressedTexSubImage1D XGL_FUNCPTR(glCompressedTexSubImage1D) +#define glCompressedTexSubImage2D XGL_FUNCPTR(glCompressedTexSubImage2D) +#define glCompressedTexSubImage3D XGL_FUNCPTR(glCompressedTexSubImage3D) +#define glGetCompressedTexImage XGL_FUNCPTR(glGetCompressedTexImage) +#define glLoadTransposeMatrixd XGL_FUNCPTR(glLoadTransposeMatrixd) +#define glLoadTransposeMatrixf XGL_FUNCPTR(glLoadTransposeMatrixf) +#define glMultTransposeMatrixd XGL_FUNCPTR(glMultTransposeMatrixd) +#define glMultTransposeMatrixf XGL_FUNCPTR(glMultTransposeMatrixf) +#define glMultiTexCoord1d XGL_FUNCPTR(glMultiTexCoord1d) +#define glMultiTexCoord1dv XGL_FUNCPTR(glMultiTexCoord1dv) +#define glMultiTexCoord1f XGL_FUNCPTR(glMultiTexCoord1f) +#define glMultiTexCoord1fv XGL_FUNCPTR(glMultiTexCoord1fv) +#define glMultiTexCoord1i XGL_FUNCPTR(glMultiTexCoord1i) +#define glMultiTexCoord1iv XGL_FUNCPTR(glMultiTexCoord1iv) +#define glMultiTexCoord1s XGL_FUNCPTR(glMultiTexCoord1s) +#define glMultiTexCoord1sv XGL_FUNCPTR(glMultiTexCoord1sv) +#define glMultiTexCoord2d XGL_FUNCPTR(glMultiTexCoord2d) +#define glMultiTexCoord2dv XGL_FUNCPTR(glMultiTexCoord2dv) +#define glMultiTexCoord2f XGL_FUNCPTR(glMultiTexCoord2f) +#define glMultiTexCoord2fv XGL_FUNCPTR(glMultiTexCoord2fv) +#define glMultiTexCoord2i XGL_FUNCPTR(glMultiTexCoord2i) +#define glMultiTexCoord2iv XGL_FUNCPTR(glMultiTexCoord2iv) +#define glMultiTexCoord2s XGL_FUNCPTR(glMultiTexCoord2s) +#define glMultiTexCoord2sv XGL_FUNCPTR(glMultiTexCoord2sv) +#define glMultiTexCoord3d XGL_FUNCPTR(glMultiTexCoord3d) +#define glMultiTexCoord3dv XGL_FUNCPTR(glMultiTexCoord3dv) +#define glMultiTexCoord3f XGL_FUNCPTR(glMultiTexCoord3f) +#define glMultiTexCoord3fv XGL_FUNCPTR(glMultiTexCoord3fv) +#define glMultiTexCoord3i XGL_FUNCPTR(glMultiTexCoord3i) +#define glMultiTexCoord3iv XGL_FUNCPTR(glMultiTexCoord3iv) +#define glMultiTexCoord3s XGL_FUNCPTR(glMultiTexCoord3s) +#define glMultiTexCoord3sv XGL_FUNCPTR(glMultiTexCoord3sv) +#define glMultiTexCoord4d XGL_FUNCPTR(glMultiTexCoord4d) +#define glMultiTexCoord4dv XGL_FUNCPTR(glMultiTexCoord4dv) +#define glMultiTexCoord4f XGL_FUNCPTR(glMultiTexCoord4f) +#define glMultiTexCoord4fv XGL_FUNCPTR(glMultiTexCoord4fv) +#define glMultiTexCoord4i XGL_FUNCPTR(glMultiTexCoord4i) +#define glMultiTexCoord4iv XGL_FUNCPTR(glMultiTexCoord4iv) +#define glMultiTexCoord4s XGL_FUNCPTR(glMultiTexCoord4s) +#define glMultiTexCoord4sv XGL_FUNCPTR(glMultiTexCoord4sv) +#define glSampleCoverage XGL_FUNCPTR(glSampleCoverage) +#endif + +#ifdef GL_VERSION_1_4 +#define GL_GENERATE_MIPMAP 0x8191 +#define GL_GENERATE_MIPMAP_HINT 0x8192 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_DEPTH_COMPONENT24 0x81A6 +#define GL_DEPTH_COMPONENT32 0x81A7 +#define GL_TEXTURE_DEPTH_SIZE 0x884A +#define GL_DEPTH_TEXTURE_MODE 0x884B +#define GL_TEXTURE_COMPARE_MODE 0x884C +#define GL_TEXTURE_COMPARE_FUNC 0x884D +#define GL_COMPARE_R_TO_TEXTURE 0x884E +#define GL_FOG_COORDINATE_SOURCE 0x8450 +#define GL_FOG_COORDINATE 0x8451 +#define GL_FRAGMENT_DEPTH 0x8452 +#define GL_CURRENT_FOG_COORDINATE 0x8453 +#define GL_FOG_COORDINATE_ARRAY_TYPE 0x8454 +#define GL_FOG_COORDINATE_ARRAY_STRIDE 0x8455 +#define GL_FOG_COORDINATE_ARRAY_POINTER 0x8456 +#define GL_FOG_COORDINATE_ARRAY 0x8457 +#define GL_POINT_SIZE_MIN 0x8126 +#define GL_POINT_SIZE_MAX 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128 +#define GL_POINT_DISTANCE_ATTENUATION 0x8129 +#define GL_COLOR_SUM 0x8458 +#define GL_CURRENT_SECONDARY_COLOR 0x8459 +#define GL_SECONDARY_COLOR_ARRAY_SIZE 0x845A +#define GL_SECONDARY_COLOR_ARRAY_TYPE 0x845B +#define GL_SECONDARY_COLOR_ARRAY_STRIDE 0x845C +#define GL_SECONDARY_COLOR_ARRAY_POINTER 0x845D +#define GL_SECONDARY_COLOR_ARRAY 0x845E +#define GL_BLEND_DST_RGB 0x80C8 +#define GL_BLEND_SRC_RGB 0x80C9 +#define GL_BLEND_DST_ALPHA 0x80CA +#define GL_BLEND_SRC_ALPHA 0x80CB +#define GL_INCR_WRAP 0x8507 +#define GL_DECR_WRAP 0x8508 +#define GL_TEXTURE_FILTER_CONTROL 0x8500 +#define GL_TEXTURE_LOD_BIAS 0x8501 +#define GL_MAX_TEXTURE_LOD_BIAS 0x84FD +#define GL_MIRRORED_REPEAT 0x8370 +#define glBlendEquation XGL_FUNCPTR(glBlendEquation) +#define glBlendColor XGL_FUNCPTR(glBlendColor) +#define glFogCoordf XGL_FUNCPTR(glFogCoordf) +#define glFogCoordfv XGL_FUNCPTR(glFogCoordfv) +#define glFogCoordd XGL_FUNCPTR(glFogCoordd) +#define glFogCoorddv XGL_FUNCPTR(glFogCoorddv) +#define glFogCoordPointer XGL_FUNCPTR(glFogCoordPointer) +#define glMultiDrawArrays XGL_FUNCPTR(glMultiDrawArrays) +#define glMultiDrawElements XGL_FUNCPTR(glMultiDrawElements) +#define glPointParameterf XGL_FUNCPTR(glPointParameterf) +#define glPointParameterfv XGL_FUNCPTR(glPointParameterfv) +#define glSecondaryColor3b XGL_FUNCPTR(glSecondaryColor3b) +#define glSecondaryColor3bv XGL_FUNCPTR(glSecondaryColor3bv) +#define glSecondaryColor3d XGL_FUNCPTR(glSecondaryColor3d) +#define glSecondaryColor3dv XGL_FUNCPTR(glSecondaryColor3dv) +#define glSecondaryColor3f XGL_FUNCPTR(glSecondaryColor3f) +#define glSecondaryColor3fv XGL_FUNCPTR(glSecondaryColor3fv) +#define glSecondaryColor3i XGL_FUNCPTR(glSecondaryColor3i) +#define glSecondaryColor3iv XGL_FUNCPTR(glSecondaryColor3iv) +#define glSecondaryColor3s XGL_FUNCPTR(glSecondaryColor3s) +#define glSecondaryColor3sv XGL_FUNCPTR(glSecondaryColor3sv) +#define glSecondaryColor3ub XGL_FUNCPTR(glSecondaryColor3ub) +#define glSecondaryColor3ubv XGL_FUNCPTR(glSecondaryColor3ubv) +#define glSecondaryColor3ui XGL_FUNCPTR(glSecondaryColor3ui) +#define glSecondaryColor3uiv XGL_FUNCPTR(glSecondaryColor3uiv) +#define glSecondaryColor3us XGL_FUNCPTR(glSecondaryColor3us) +#define glSecondaryColor3usv XGL_FUNCPTR(glSecondaryColor3usv) +#define glSecondaryColorPointer XGL_FUNCPTR(glSecondaryColorPointer) +#define glBlendFuncSeparate XGL_FUNCPTR(glBlendFuncSeparate) +#define glWindowPos2d XGL_FUNCPTR(glWindowPos2d) +#define glWindowPos2f XGL_FUNCPTR(glWindowPos2f) +#define glWindowPos2i XGL_FUNCPTR(glWindowPos2i) +#define glWindowPos2s XGL_FUNCPTR(glWindowPos2s) +#define glWindowPos2dv XGL_FUNCPTR(glWindowPos2dv) +#define glWindowPos2fv XGL_FUNCPTR(glWindowPos2fv) +#define glWindowPos2iv XGL_FUNCPTR(glWindowPos2iv) +#define glWindowPos2sv XGL_FUNCPTR(glWindowPos2sv) +#define glWindowPos3d XGL_FUNCPTR(glWindowPos3d) +#define glWindowPos3f XGL_FUNCPTR(glWindowPos3f) +#define glWindowPos3i XGL_FUNCPTR(glWindowPos3i) +#define glWindowPos3s XGL_FUNCPTR(glWindowPos3s) +#define glWindowPos3dv XGL_FUNCPTR(glWindowPos3dv) +#define glWindowPos3fv XGL_FUNCPTR(glWindowPos3fv) +#define glWindowPos3iv XGL_FUNCPTR(glWindowPos3iv) +#define glWindowPos3sv XGL_FUNCPTR(glWindowPos3sv) +#endif + +#ifdef GL_VERSION_1_5 +typedef ptrdiff_t GLsizeiptr; +typedef ptrdiff_t GLintptr; +#define GL_BUFFER_SIZE 0x8764 +#define GL_BUFFER_USAGE 0x8765 +#define GL_QUERY_COUNTER_BITS 0x8864 +#define GL_CURRENT_QUERY 0x8865 +#define GL_QUERY_RESULT 0x8866 +#define GL_QUERY_RESULT_AVAILABLE 0x8867 +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GL_VERTEX_ARRAY_BUFFER_BINDING 0x8896 +#define GL_NORMAL_ARRAY_BUFFER_BINDING 0x8897 +#define GL_COLOR_ARRAY_BUFFER_BINDING 0x8898 +#define GL_INDEX_ARRAY_BUFFER_BINDING 0x8899 +#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING 0x889A +#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING 0x889B +#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING 0x889C +#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING 0x889D +#define GL_WEIGHT_ARRAY_BUFFER_BINDING 0x889E +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F +#define GL_READ_ONLY 0x88B8 +#define GL_WRITE_ONLY 0x88B9 +#define GL_READ_WRITE 0x88BA +#define GL_BUFFER_ACCESS 0x88BB +#define GL_BUFFER_MAPPED 0x88BC +#define GL_BUFFER_MAP_POINTER 0x88BD +#define GL_STREAM_DRAW 0x88E0 +#define GL_STREAM_READ 0x88E1 +#define GL_STREAM_COPY 0x88E2 +#define GL_STATIC_DRAW 0x88E4 +#define GL_STATIC_READ 0x88E5 +#define GL_STATIC_COPY 0x88E6 +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_DYNAMIC_READ 0x88E9 +#define GL_DYNAMIC_COPY 0x88EA +#define GL_SAMPLES_PASSED 0x8914 +#define GL_FOG_COORD_SRC GL_FOG_COORDINATE_SOURCE +#define GL_FOG_COORD GL_FOG_COORDINATE +#define GL_CURRENT_FOG_COORD GL_CURRENT_FOG_COORDINATE +#define GL_FOG_COORD_ARRAY_TYPE GL_FOG_COORDINATE_ARRAY_TYPE +#define GL_FOG_COORD_ARRAY_STRIDE GL_FOG_COORDINATE_ARRAY_STRIDE +#define GL_FOG_COORD_ARRAY_POINTER GL_FOG_COORDINATE_ARRAY_POINTER +#define GL_FOG_COORD_ARRAY GL_FOG_COORDINATE_ARRAY +#define GL_FOG_COORD_ARRAY_BUFFER_BINDING GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING +#define GL_SRC0_RGB GL_SOURCE0_RGB +#define GL_SRC1_RGB GL_SOURCE1_RGB +#define GL_SRC2_RGB GL_SOURCE2_RGB +#define GL_SRC0_ALPHA GL_SOURCE0_ALPHA +#define GL_SRC1_ALPHA GL_SOURCE1_ALPHA +#define GL_SRC2_ALPHA GL_SOURCE2_ALPHA +#define glGenQueries XGL_FUNCPTR(glGenQueries) +#define glDeleteQueries XGL_FUNCPTR(glDeleteQueries) +#define glIsQuery XGL_FUNCPTR(glIsQuery) +#define glBeginQuery XGL_FUNCPTR(glBeginQuery) +#define glEndQuery XGL_FUNCPTR(glEndQuery) +#define glGetQueryiv XGL_FUNCPTR(glGetQueryiv) +#define glGetQueryObjectiv XGL_FUNCPTR(glGetQueryObjectiv) +#define glGetQueryObjectuiv XGL_FUNCPTR(glGetQueryObjectuiv) +#define glBindBuffer XGL_FUNCPTR(glBindBuffer) +#define glDeleteBuffers XGL_FUNCPTR(glDeleteBuffers) +#define glGenBuffers XGL_FUNCPTR(glGenBuffers) +#define glIsBuffer XGL_FUNCPTR(glIsBuffer) +#define glBufferData XGL_FUNCPTR(glBufferData) +#define glBufferSubData XGL_FUNCPTR(glBufferSubData) +#define glGetBufferSubData XGL_FUNCPTR(glGetBufferSubData) +#define glMapBuffer XGL_FUNCPTR(glMapBuffer) +#define glUnmapBuffer XGL_FUNCPTR(glUnmapBuffer) +#define glGetBufferParameteriv XGL_FUNCPTR(glGetBufferParameteriv) +#define glGetBufferPointerv XGL_FUNCPTR(glGetBufferPointerv) +#endif + +#ifdef GL_VERSION_2_0 +typedef char GLchar; +#define GL_BLEND_EQUATION_RGB GL_BLEND_EQUATION +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 +#define GL_CURRENT_VERTEX_ATTRIB 0x8626 +#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 +#define GL_VERTEX_PROGRAM_TWO_SIDE 0x8643 +#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 +#define GL_STENCIL_BACK_FUNC 0x8800 +#define GL_STENCIL_BACK_FAIL 0x8801 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 +#define GL_MAX_DRAW_BUFFERS 0x8824 +#define GL_DRAW_BUFFER0 0x8825 +#define GL_DRAW_BUFFER1 0x8826 +#define GL_DRAW_BUFFER2 0x8827 +#define GL_DRAW_BUFFER3 0x8828 +#define GL_DRAW_BUFFER4 0x8829 +#define GL_DRAW_BUFFER5 0x882A +#define GL_DRAW_BUFFER6 0x882B +#define GL_DRAW_BUFFER7 0x882C +#define GL_DRAW_BUFFER8 0x882D +#define GL_DRAW_BUFFER9 0x882E +#define GL_DRAW_BUFFER10 0x882F +#define GL_DRAW_BUFFER11 0x8830 +#define GL_DRAW_BUFFER12 0x8831 +#define GL_DRAW_BUFFER13 0x8832 +#define GL_DRAW_BUFFER14 0x8833 +#define GL_DRAW_BUFFER15 0x8834 +#define GL_BLEND_EQUATION_ALPHA 0x883D +#define GL_POINT_SPRITE 0x8861 +#define GL_COORD_REPLACE 0x8862 +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A +#define GL_MAX_TEXTURE_COORDS 0x8871 +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49 +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A +#define GL_MAX_VARYING_FLOATS 0x8B4B +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#define GL_SHADER_TYPE 0x8B4F +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT4 0x8B5C +#define GL_SAMPLER_1D 0x8B5D +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_3D 0x8B5F +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_SAMPLER_1D_SHADOW 0x8B61 +#define GL_SAMPLER_2D_SHADOW 0x8B62 +#define GL_DELETE_STATUS 0x8B80 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_LINK_STATUS 0x8B82 +#define GL_VALIDATE_STATUS 0x8B83 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_ATTACHED_SHADERS 0x8B85 +#define GL_ACTIVE_UNIFORMS 0x8B86 +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#define GL_SHADER_SOURCE_LENGTH 0x8B88 +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B +#define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#define GL_CURRENT_PROGRAM 0x8B8D +#define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0 +#define GL_LOWER_LEFT 0x8CA1 +#define GL_UPPER_LEFT 0x8CA2 +#define GL_STENCIL_BACK_REF 0x8CA3 +#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 +#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 +#define glBlendEquationSeparate XGL_FUNCPTR(glBlendEquationSeparate) +#define glDrawBuffers XGL_FUNCPTR(glDrawBuffers) +#define glStencilOpSeparate XGL_FUNCPTR(glStencilOpSeparate) +#define glStencilFuncSeparate XGL_FUNCPTR(glStencilFuncSeparate) +#define glStencilMaskSeparate XGL_FUNCPTR(glStencilMaskSeparate) +#define glAttachShader XGL_FUNCPTR(glAttachShader) +#define glBindAttribLocation XGL_FUNCPTR(glBindAttribLocation) +#define glCompileShader XGL_FUNCPTR(glCompileShader) +#define glCreateProgram XGL_FUNCPTR(glCreateProgram) +#define glCreateShader XGL_FUNCPTR(glCreateShader) +#define glDeleteProgram XGL_FUNCPTR(glDeleteProgram) +#define glDeleteShader XGL_FUNCPTR(glDeleteShader) +#define glDetachShader XGL_FUNCPTR(glDetachShader) +#define glDisableVertexAttribArray XGL_FUNCPTR(glDisableVertexAttribArray) +#define glEnableVertexAttribArray XGL_FUNCPTR(glEnableVertexAttribArray) +#define glGetActiveAttrib XGL_FUNCPTR(glGetActiveAttrib) +#define glGetActiveUniform XGL_FUNCPTR(glGetActiveUniform) +#define glGetAttachedShaders XGL_FUNCPTR(glGetAttachedShaders) +#define glGetAttribLocation XGL_FUNCPTR(glGetAttribLocation) +#define glGetProgramiv XGL_FUNCPTR(glGetProgramiv) +#define glGetProgramInfoLog XGL_FUNCPTR(glGetProgramInfoLog) +#define glGetShaderiv XGL_FUNCPTR(glGetShaderiv) +#define glGetShaderInfoLog XGL_FUNCPTR(glGetShaderInfoLog) +#define glShaderSource XGL_FUNCPTR(glShaderSource) +#define glGetUniformLocation XGL_FUNCPTR(glGetUniformLocation) +#define glGetUniformfv XGL_FUNCPTR(glGetUniformfv) +#define glGetUniformiv XGL_FUNCPTR(glGetUniformiv) +#define glGetVertexAttribdv XGL_FUNCPTR(glGetVertexAttribdv) +#define glGetVertexAttribfv XGL_FUNCPTR(glGetVertexAttribfv) +#define glGetVertexAttribiv XGL_FUNCPTR(glGetVertexAttribiv) +#define glGetVertexAttribPointerv XGL_FUNCPTR(glGetVertexAttribPointerv) +#define glIsProgram XGL_FUNCPTR(glIsProgram) +#define glIsShader XGL_FUNCPTR(glIsShader) +#define glLinkProgram XGL_FUNCPTR(glLinkProgram) +#define glGetShaderSource XGL_FUNCPTR(glGetShaderSource) +#define glUseProgram XGL_FUNCPTR(glUseProgram) +#define glUniform1f XGL_FUNCPTR(glUniform1f) +#define glUniform1fv XGL_FUNCPTR(glUniform1fv) +#define glUniform1i XGL_FUNCPTR(glUniform1i) +#define glUniform1iv XGL_FUNCPTR(glUniform1iv) +#define glUniform2f XGL_FUNCPTR(glUniform2f) +#define glUniform2fv XGL_FUNCPTR(glUniform2fv) +#define glUniform2i XGL_FUNCPTR(glUniform2i) +#define glUniform2iv XGL_FUNCPTR(glUniform2iv) +#define glUniform3f XGL_FUNCPTR(glUniform3f) +#define glUniform3fv XGL_FUNCPTR(glUniform3fv) +#define glUniform3i XGL_FUNCPTR(glUniform3i) +#define glUniform3iv XGL_FUNCPTR(glUniform3iv) +#define glUniform4f XGL_FUNCPTR(glUniform4f) +#define glUniform4fv XGL_FUNCPTR(glUniform4fv) +#define glUniform4i XGL_FUNCPTR(glUniform4i) +#define glUniform4iv XGL_FUNCPTR(glUniform4iv) +#define glUniformMatrix2fv XGL_FUNCPTR(glUniformMatrix2fv) +#define glUniformMatrix3fv XGL_FUNCPTR(glUniformMatrix3fv) +#define glUniformMatrix4fv XGL_FUNCPTR(glUniformMatrix4fv) +#define glValidateProgram XGL_FUNCPTR(glValidateProgram) +#define glVertexAttrib1d XGL_FUNCPTR(glVertexAttrib1d) +#define glVertexAttrib1dv XGL_FUNCPTR(glVertexAttrib1dv) +#define glVertexAttrib1f XGL_FUNCPTR(glVertexAttrib1f) +#define glVertexAttrib1fv XGL_FUNCPTR(glVertexAttrib1fv) +#define glVertexAttrib1s XGL_FUNCPTR(glVertexAttrib1s) +#define glVertexAttrib1sv XGL_FUNCPTR(glVertexAttrib1sv) +#define glVertexAttrib2d XGL_FUNCPTR(glVertexAttrib2d) +#define glVertexAttrib2dv XGL_FUNCPTR(glVertexAttrib2dv) +#define glVertexAttrib2f XGL_FUNCPTR(glVertexAttrib2f) +#define glVertexAttrib2fv XGL_FUNCPTR(glVertexAttrib2fv) +#define glVertexAttrib2s XGL_FUNCPTR(glVertexAttrib2s) +#define glVertexAttrib2sv XGL_FUNCPTR(glVertexAttrib2sv) +#define glVertexAttrib3d XGL_FUNCPTR(glVertexAttrib3d) +#define glVertexAttrib3dv XGL_FUNCPTR(glVertexAttrib3dv) +#define glVertexAttrib3f XGL_FUNCPTR(glVertexAttrib3f) +#define glVertexAttrib3fv XGL_FUNCPTR(glVertexAttrib3fv) +#define glVertexAttrib3s XGL_FUNCPTR(glVertexAttrib3s) +#define glVertexAttrib3sv XGL_FUNCPTR(glVertexAttrib3sv) +#define glVertexAttrib4Nbv XGL_FUNCPTR(glVertexAttrib4Nbv) +#define glVertexAttrib4Niv XGL_FUNCPTR(glVertexAttrib4Niv) +#define glVertexAttrib4Nsv XGL_FUNCPTR(glVertexAttrib4Nsv) +#define glVertexAttrib4Nub XGL_FUNCPTR(glVertexAttrib4Nub) +#define glVertexAttrib4Nubv XGL_FUNCPTR(glVertexAttrib4Nubv) +#define glVertexAttrib4Nuiv XGL_FUNCPTR(glVertexAttrib4Nuiv) +#define glVertexAttrib4Nusv XGL_FUNCPTR(glVertexAttrib4Nusv) +#define glVertexAttrib4bv XGL_FUNCPTR(glVertexAttrib4bv) +#define glVertexAttrib4d XGL_FUNCPTR(glVertexAttrib4d) +#define glVertexAttrib4dv XGL_FUNCPTR(glVertexAttrib4dv) +#define glVertexAttrib4f XGL_FUNCPTR(glVertexAttrib4f) +#define glVertexAttrib4fv XGL_FUNCPTR(glVertexAttrib4fv) +#define glVertexAttrib4iv XGL_FUNCPTR(glVertexAttrib4iv) +#define glVertexAttrib4s XGL_FUNCPTR(glVertexAttrib4s) +#define glVertexAttrib4sv XGL_FUNCPTR(glVertexAttrib4sv) +#define glVertexAttrib4ubv XGL_FUNCPTR(glVertexAttrib4ubv) +#define glVertexAttrib4uiv XGL_FUNCPTR(glVertexAttrib4uiv) +#define glVertexAttrib4usv XGL_FUNCPTR(glVertexAttrib4usv) +#define glVertexAttribPointer XGL_FUNCPTR(glVertexAttribPointer) +#endif + +#ifdef GL_3DFX_multisample +#define GL_MULTISAMPLE_3DFX 0x86B2 +#define GL_SAMPLE_BUFFERS_3DFX 0x86B3 +#define GL_SAMPLES_3DFX 0x86B4 +#define GL_MULTISAMPLE_BIT_3DFX 0x20000000 +#endif + +#ifdef GL_3DFX_tbuffer +#define glTbufferMask3DFX XGL_FUNCPTR(glTbufferMask3DFX) +#endif + +#ifdef GL_3DFX_texture_compression_FXT1 +#define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0 +#define GL_COMPRESSED_RGBA_FXT1_3DFX 0x86B1 +#endif + +#ifdef GL_APPLE_client_storage +#define GL_UNPACK_CLIENT_STORAGE_APPLE 0x85B2 +#endif + +#ifdef GL_APPLE_element_array +#define GL_ELEMENT_ARRAY_APPLE 0x8768 +#define GL_ELEMENT_ARRAY_TYPE_APPLE 0x8769 +#define GL_ELEMENT_ARRAY_POINTER_APPLE 0x876A +#define glDrawElementArrayAPPLE XGL_FUNCPTR(glDrawElementArrayAPPLE) +#define glDrawRangeElementArrayAPPLE XGL_FUNCPTR(glDrawRangeElementArrayAPPLE) +#define glElementPointerAPPLE XGL_FUNCPTR(glElementPointerAPPLE) +#define glMultiDrawElementArrayAPPLE XGL_FUNCPTR(glMultiDrawElementArrayAPPLE) +#define glMultiDrawRangeElementArrayAPPLE XGL_FUNCPTR(glMultiDrawRangeElementArrayAPPLE) +#endif + +#ifdef GL_APPLE_fence +#define GL_DRAW_PIXELS_APPLE 0x8A0A +#define GL_FENCE_APPLE 0x8A0B +#define glDeleteFencesAPPLE XGL_FUNCPTR(glDeleteFencesAPPLE) +#define glFinishFenceAPPLE XGL_FUNCPTR(glFinishFenceAPPLE) +#define glFinishObjectAPPLE XGL_FUNCPTR(glFinishObjectAPPLE) +#define glGenFencesAPPLE XGL_FUNCPTR(glGenFencesAPPLE) +#define glIsFenceAPPLE XGL_FUNCPTR(glIsFenceAPPLE) +#define glSetFenceAPPLE XGL_FUNCPTR(glSetFenceAPPLE) +#define glTestFenceAPPLE XGL_FUNCPTR(glTestFenceAPPLE) +#define glTestObjectAPPLE XGL_FUNCPTR(glTestObjectAPPLE) +#endif + +#ifdef GL_APPLE_float_pixels +#define GL_HALF_APPLE 0x140B +#define GL_COLOR_FLOAT_APPLE 0x8A0F +#define GL_RGBA_FLOAT32_APPLE 0x8814 +#define GL_RGB_FLOAT32_APPLE 0x8815 +#define GL_ALPHA_FLOAT32_APPLE 0x8816 +#define GL_INTENSITY_FLOAT32_APPLE 0x8817 +#define GL_LUMINANCE_FLOAT32_APPLE 0x8818 +#define GL_LUMINANCE_ALPHA_FLOAT32_APPLE 0x8819 +#define GL_RGBA_FLOAT16_APPLE 0x881A +#define GL_RGB_FLOAT16_APPLE 0x881B +#define GL_ALPHA_FLOAT16_APPLE 0x881C +#define GL_INTENSITY_FLOAT16_APPLE 0x881D +#define GL_LUMINANCE_FLOAT16_APPLE 0x881E +#define GL_LUMINANCE_ALPHA_FLOAT16_APPLE 0x881F +#endif + +#ifdef GL_APPLE_pixel_buffer +#define GL_MIN_PBUFFER_VIEWPORT_DIMS_APPLE 0x8A10 +#endif + +#ifdef GL_APPLE_specular_vector +#define GL_LIGHT_MODEL_SPECULAR_VECTOR_APPLE 0x85B0 +#endif + +#ifdef GL_APPLE_texture_range +#define GL_TEXTURE_STORAGE_HINT_APPLE 0x85BC +#define GL_STORAGE_PRIVATE_APPLE 0x85BD +#define GL_STORAGE_CACHED_APPLE 0x85BE +#define GL_STORAGE_SHARED_APPLE 0x85BF +#define GL_TEXTURE_RANGE_LENGTH_APPLE 0x85B7 +#define GL_TEXTURE_RANGE_POINTER_APPLE 0x85B8 +#define glTextureRangeAPPLE XGL_FUNCPTR(glTextureRangeAPPLE) +#define glGetTexParameterPointervAPPLE XGL_FUNCPTR(glGetTexParameterPointervAPPLE) +#endif + +#ifdef GL_APPLE_transform_hint +#define GL_TRANSFORM_HINT_APPLE 0x85B1 +#endif + +#ifdef GL_APPLE_vertex_array_object +#define GL_VERTEX_ARRAY_BINDING_APPLE 0x85B5 +#define glBindVertexArrayAPPLE XGL_FUNCPTR(glBindVertexArrayAPPLE) +#define glDeleteVertexArraysAPPLE XGL_FUNCPTR(glDeleteVertexArraysAPPLE) +#define glGenVertexArraysAPPLE XGL_FUNCPTR(glGenVertexArraysAPPLE) +#define glIsVertexArrayAPPLE XGL_FUNCPTR(glIsVertexArrayAPPLE) +#endif + +#ifdef GL_APPLE_vertex_array_range +#define GL_VERTEX_ARRAY_RANGE_APPLE 0x851D +#define GL_VERTEX_ARRAY_RANGE_LENGTH_APPLE 0x851E +#define GL_VERTEX_ARRAY_STORAGE_HINT_APPLE 0x851F +#define GL_MAX_VERTEX_ARRAY_RANGE_ELEMENT_APPLE 0x8520 +#define GL_VERTEX_ARRAY_RANGE_POINTER_APPLE 0x8521 +#define GL_STORAGE_CACHED_APPLE 0x85BE +#define GL_STORAGE_SHARED_APPLE 0x85BF +#define glFlushVertexArrayRangeAPPLE XGL_FUNCPTR(glFlushVertexArrayRangeAPPLE) +#define glVertexArrayParameteriAPPLE XGL_FUNCPTR(glVertexArrayParameteriAPPLE) +#define glVertexArrayRangeAPPLE XGL_FUNCPTR(glVertexArrayRangeAPPLE) +#endif + +#ifdef GL_APPLE_ycbcr_422 +#define GL_YCBCR_422_APPLE 0x85B9 +#define GL_UNSIGNED_SHORT_8_8_APPLE 0x85BA +#define GL_UNSIGNED_SHORT_8_8_REV_APPLE 0x85BB +#endif + +#ifdef GL_ARB_color_buffer_float +#define GL_RGBA_FLOAT_MODE_ARB 0x8820 +#define GL_CLAMP_VERTEX_COLOR_ARB 0x891A +#define GL_CLAMP_FRAGMENT_COLOR_ARB 0x891B +#define GL_CLAMP_READ_COLOR_ARB 0x891C +#define GL_FIXED_ONLY_ARB 0x891D +#define glClampColorARB XGL_FUNCPTR(glClampColorARB) +#endif + +#ifdef GL_ARB_depth_texture +#define GL_DEPTH_COMPONENT16_ARB 0x81A5 +#define GL_DEPTH_COMPONENT24_ARB 0x81A6 +#define GL_DEPTH_COMPONENT32_ARB 0x81A7 +#define GL_TEXTURE_DEPTH_SIZE_ARB 0x884A +#define GL_DEPTH_TEXTURE_MODE_ARB 0x884B +#endif + +#ifdef GL_ARB_draw_buffers +#define GL_MAX_DRAW_BUFFERS_ARB 0x8824 +#define GL_DRAW_BUFFER0_ARB 0x8825 +#define GL_DRAW_BUFFER1_ARB 0x8826 +#define GL_DRAW_BUFFER2_ARB 0x8827 +#define GL_DRAW_BUFFER3_ARB 0x8828 +#define GL_DRAW_BUFFER4_ARB 0x8829 +#define GL_DRAW_BUFFER5_ARB 0x882A +#define GL_DRAW_BUFFER6_ARB 0x882B +#define GL_DRAW_BUFFER7_ARB 0x882C +#define GL_DRAW_BUFFER8_ARB 0x882D +#define GL_DRAW_BUFFER9_ARB 0x882E +#define GL_DRAW_BUFFER10_ARB 0x882F +#define GL_DRAW_BUFFER11_ARB 0x8830 +#define GL_DRAW_BUFFER12_ARB 0x8831 +#define GL_DRAW_BUFFER13_ARB 0x8832 +#define GL_DRAW_BUFFER14_ARB 0x8833 +#define GL_DRAW_BUFFER15_ARB 0x8834 +#define glDrawBuffersARB XGL_FUNCPTR(glDrawBuffersARB) +#endif + +#ifdef GL_ARB_fragment_program +#define GL_FRAGMENT_PROGRAM_ARB 0x8804 +#define GL_PROGRAM_ALU_INSTRUCTIONS_ARB 0x8805 +#define GL_PROGRAM_TEX_INSTRUCTIONS_ARB 0x8806 +#define GL_PROGRAM_TEX_INDIRECTIONS_ARB 0x8807 +#define GL_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x8808 +#define GL_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x8809 +#define GL_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x880A +#define GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB 0x880B +#define GL_MAX_PROGRAM_TEX_INSTRUCTIONS_ARB 0x880C +#define GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB 0x880D +#define GL_MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x880E +#define GL_MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x880F +#define GL_MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x8810 +#define GL_MAX_TEXTURE_COORDS_ARB 0x8871 +#define GL_MAX_TEXTURE_IMAGE_UNITS_ARB 0x8872 +#endif + +#ifdef GL_ARB_fragment_program_shadow +#endif + +#ifdef GL_ARB_fragment_shader +#define GL_FRAGMENT_SHADER_ARB 0x8B30 +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB 0x8B49 +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT_ARB 0x8B8B +#endif + +#ifdef GL_ARB_half_float_pixel +#define GL_HALF_FLOAT_ARB 0x140B +#endif + +#ifdef GL_ARB_imaging +#define GL_CONSTANT_COLOR 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#define GL_BLEND_COLOR 0x8005 +#define GL_FUNC_ADD 0x8006 +#define GL_MIN 0x8007 +#define GL_MAX 0x8008 +#define GL_BLEND_EQUATION 0x8009 +#define GL_FUNC_SUBTRACT 0x800A +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#define GL_CONVOLUTION_1D 0x8010 +#define GL_CONVOLUTION_2D 0x8011 +#define GL_SEPARABLE_2D 0x8012 +#define GL_CONVOLUTION_BORDER_MODE 0x8013 +#define GL_CONVOLUTION_FILTER_SCALE 0x8014 +#define GL_CONVOLUTION_FILTER_BIAS 0x8015 +#define GL_REDUCE 0x8016 +#define GL_CONVOLUTION_FORMAT 0x8017 +#define GL_CONVOLUTION_WIDTH 0x8018 +#define GL_CONVOLUTION_HEIGHT 0x8019 +#define GL_MAX_CONVOLUTION_WIDTH 0x801A +#define GL_MAX_CONVOLUTION_HEIGHT 0x801B +#define GL_POST_CONVOLUTION_RED_SCALE 0x801C +#define GL_POST_CONVOLUTION_GREEN_SCALE 0x801D +#define GL_POST_CONVOLUTION_BLUE_SCALE 0x801E +#define GL_POST_CONVOLUTION_ALPHA_SCALE 0x801F +#define GL_POST_CONVOLUTION_RED_BIAS 0x8020 +#define GL_POST_CONVOLUTION_GREEN_BIAS 0x8021 +#define GL_POST_CONVOLUTION_BLUE_BIAS 0x8022 +#define GL_POST_CONVOLUTION_ALPHA_BIAS 0x8023 +#define GL_HISTOGRAM 0x8024 +#define GL_PROXY_HISTOGRAM 0x8025 +#define GL_HISTOGRAM_WIDTH 0x8026 +#define GL_HISTOGRAM_FORMAT 0x8027 +#define GL_HISTOGRAM_RED_SIZE 0x8028 +#define GL_HISTOGRAM_GREEN_SIZE 0x8029 +#define GL_HISTOGRAM_BLUE_SIZE 0x802A +#define GL_HISTOGRAM_ALPHA_SIZE 0x802B +#define GL_HISTOGRAM_LUMINANCE_SIZE 0x802C +#define GL_HISTOGRAM_SINK 0x802D +#define GL_MINMAX 0x802E +#define GL_MINMAX_FORMAT 0x802F +#define GL_MINMAX_SINK 0x8030 +#define GL_TABLE_TOO_LARGE 0x8031 +#define GL_COLOR_MATRIX 0x80B1 +#define GL_COLOR_MATRIX_STACK_DEPTH 0x80B2 +#define GL_MAX_COLOR_MATRIX_STACK_DEPTH 0x80B3 +#define GL_POST_COLOR_MATRIX_RED_SCALE 0x80B4 +#define GL_POST_COLOR_MATRIX_GREEN_SCALE 0x80B5 +#define GL_POST_COLOR_MATRIX_BLUE_SCALE 0x80B6 +#define GL_POST_COLOR_MATRIX_ALPHA_SCALE 0x80B7 +#define GL_POST_COLOR_MATRIX_RED_BIAS 0x80B8 +#define GL_POST_COLOR_MATRIX_GREEN_BIAS 0x80B9 +#define GL_POST_COLOR_MATRIX_BLUE_BIAS 0x80BA +#define GL_POST_COLOR_MATRIX_ALPHA_BIAS 0x80BB +#define GL_COLOR_TABLE 0x80D0 +#define GL_POST_CONVOLUTION_COLOR_TABLE 0x80D1 +#define GL_POST_COLOR_MATRIX_COLOR_TABLE 0x80D2 +#define GL_PROXY_COLOR_TABLE 0x80D3 +#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE 0x80D4 +#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE 0x80D5 +#define GL_COLOR_TABLE_SCALE 0x80D6 +#define GL_COLOR_TABLE_BIAS 0x80D7 +#define GL_COLOR_TABLE_FORMAT 0x80D8 +#define GL_COLOR_TABLE_WIDTH 0x80D9 +#define GL_COLOR_TABLE_RED_SIZE 0x80DA +#define GL_COLOR_TABLE_GREEN_SIZE 0x80DB +#define GL_COLOR_TABLE_BLUE_SIZE 0x80DC +#define GL_COLOR_TABLE_ALPHA_SIZE 0x80DD +#define GL_COLOR_TABLE_LUMINANCE_SIZE 0x80DE +#define GL_COLOR_TABLE_INTENSITY_SIZE 0x80DF +#define GL_IGNORE_BORDER 0x8150 +#define GL_CONSTANT_BORDER 0x8151 +#define GL_WRAP_BORDER 0x8152 +#define GL_REPLICATE_BORDER 0x8153 +#define GL_CONVOLUTION_BORDER_COLOR 0x8154 +#define glColorTable XGL_FUNCPTR(glColorTable) +#define glColorSubTable XGL_FUNCPTR(glColorSubTable) +#define glColorTableParameteriv XGL_FUNCPTR(glColorTableParameteriv) +#define glColorTableParameterfv XGL_FUNCPTR(glColorTableParameterfv) +#define glCopyColorSubTable XGL_FUNCPTR(glCopyColorSubTable) +#define glCopyColorTable XGL_FUNCPTR(glCopyColorTable) +#define glGetColorTable XGL_FUNCPTR(glGetColorTable) +#define glGetColorTableParameterfv XGL_FUNCPTR(glGetColorTableParameterfv) +#define glGetColorTableParameteriv XGL_FUNCPTR(glGetColorTableParameteriv) +#define glHistogram XGL_FUNCPTR(glHistogram) +#define glResetHistogram XGL_FUNCPTR(glResetHistogram) +#define glGetHistogram XGL_FUNCPTR(glGetHistogram) +#define glGetHistogramParameterfv XGL_FUNCPTR(glGetHistogramParameterfv) +#define glGetHistogramParameteriv XGL_FUNCPTR(glGetHistogramParameteriv) +#define glMinmax XGL_FUNCPTR(glMinmax) +#define glResetMinmax XGL_FUNCPTR(glResetMinmax) +#define glGetMinmaxParameterfv XGL_FUNCPTR(glGetMinmaxParameterfv) +#define glGetMinmaxParameteriv XGL_FUNCPTR(glGetMinmaxParameteriv) +#define glConvolutionFilter1D XGL_FUNCPTR(glConvolutionFilter1D) +#define glConvolutionFilter2D XGL_FUNCPTR(glConvolutionFilter2D) +#define glConvolutionParameterf XGL_FUNCPTR(glConvolutionParameterf) +#define glConvolutionParameterfv XGL_FUNCPTR(glConvolutionParameterfv) +#define glConvolutionParameteri XGL_FUNCPTR(glConvolutionParameteri) +#define glConvolutionParameteriv XGL_FUNCPTR(glConvolutionParameteriv) +#define glCopyConvolutionFilter1D XGL_FUNCPTR(glCopyConvolutionFilter1D) +#define glCopyConvolutionFilter2D XGL_FUNCPTR(glCopyConvolutionFilter2D) +#define glGetConvolutionFilter XGL_FUNCPTR(glGetConvolutionFilter) +#define glGetConvolutionParameterfv XGL_FUNCPTR(glGetConvolutionParameterfv) +#define glGetConvolutionParameteriv XGL_FUNCPTR(glGetConvolutionParameteriv) +#define glSeparableFilter2D XGL_FUNCPTR(glSeparableFilter2D) +#define glGetSeparableFilter XGL_FUNCPTR(glGetSeparableFilter) +#define glGetMinmax XGL_FUNCPTR(glGetMinmax) +#endif + +#ifdef GL_ARB_matrix_palette +#define GL_MATRIX_PALETTE_ARB 0x8840 +#define GL_MAX_MATRIX_PALETTE_STACK_DEPTH_ARB 0x8841 +#define GL_MAX_PALETTE_MATRICES_ARB 0x8842 +#define GL_CURRENT_PALETTE_MATRIX_ARB 0x8843 +#define GL_MATRIX_INDEX_ARRAY_ARB 0x8844 +#define GL_CURRENT_MATRIX_INDEX_ARB 0x8845 +#define GL_MATRIX_INDEX_ARRAY_SIZE_ARB 0x8846 +#define GL_MATRIX_INDEX_ARRAY_TYPE_ARB 0x8847 +#define GL_MATRIX_INDEX_ARRAY_STRIDE_ARB 0x8848 +#define GL_MATRIX_INDEX_ARRAY_POINTER_ARB 0x8849 +#define glCurrentPaletteMatrixARB XGL_FUNCPTR(glCurrentPaletteMatrixARB) +#define glMatrixIndexPointerARB XGL_FUNCPTR(glMatrixIndexPointerARB) +#define glMatrixIndexubvARB XGL_FUNCPTR(glMatrixIndexubvARB) +#define glMatrixIndexusvARB XGL_FUNCPTR(glMatrixIndexusvARB) +#define glMatrixIndexuivARB XGL_FUNCPTR(glMatrixIndexuivARB) +#endif + +#ifdef GL_ARB_multisample +#define GL_MULTISAMPLE_ARB 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE_ARB 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_ARB 0x809F +#define GL_SAMPLE_COVERAGE_ARB 0x80A0 +#define GL_SAMPLE_BUFFERS_ARB 0x80A8 +#define GL_SAMPLES_ARB 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE_ARB 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT_ARB 0x80AB +#define GL_MULTISAMPLE_BIT_ARB 0x20000000 +#define glSampleCoverageARB XGL_FUNCPTR(glSampleCoverageARB) +#endif + +#ifdef GL_ARB_multitexture +#define GL_TEXTURE0_ARB 0x84C0 +#define GL_TEXTURE1_ARB 0x84C1 +#define GL_TEXTURE2_ARB 0x84C2 +#define GL_TEXTURE3_ARB 0x84C3 +#define GL_TEXTURE4_ARB 0x84C4 +#define GL_TEXTURE5_ARB 0x84C5 +#define GL_TEXTURE6_ARB 0x84C6 +#define GL_TEXTURE7_ARB 0x84C7 +#define GL_TEXTURE8_ARB 0x84C8 +#define GL_TEXTURE9_ARB 0x84C9 +#define GL_TEXTURE10_ARB 0x84CA +#define GL_TEXTURE11_ARB 0x84CB +#define GL_TEXTURE12_ARB 0x84CC +#define GL_TEXTURE13_ARB 0x84CD +#define GL_TEXTURE14_ARB 0x84CE +#define GL_TEXTURE15_ARB 0x84CF +#define GL_TEXTURE16_ARB 0x84D0 +#define GL_TEXTURE17_ARB 0x84D1 +#define GL_TEXTURE18_ARB 0x84D2 +#define GL_TEXTURE19_ARB 0x84D3 +#define GL_TEXTURE20_ARB 0x84D4 +#define GL_TEXTURE21_ARB 0x84D5 +#define GL_TEXTURE22_ARB 0x84D6 +#define GL_TEXTURE23_ARB 0x84D7 +#define GL_TEXTURE24_ARB 0x84D8 +#define GL_TEXTURE25_ARB 0x84D9 +#define GL_TEXTURE26_ARB 0x84DA +#define GL_TEXTURE27_ARB 0x84DB +#define GL_TEXTURE28_ARB 0x84DC +#define GL_TEXTURE29_ARB 0x84DD +#define GL_TEXTURE30_ARB 0x84DE +#define GL_TEXTURE31_ARB 0x84DF +#define GL_ACTIVE_TEXTURE_ARB 0x84E0 +#define GL_CLIENT_ACTIVE_TEXTURE_ARB 0x84E1 +#define GL_MAX_TEXTURE_UNITS_ARB 0x84E2 +#define glActiveTextureARB XGL_FUNCPTR(glActiveTextureARB) +#define glClientActiveTextureARB XGL_FUNCPTR(glClientActiveTextureARB) +#define glMultiTexCoord1dARB XGL_FUNCPTR(glMultiTexCoord1dARB) +#define glMultiTexCoord1dvARB XGL_FUNCPTR(glMultiTexCoord1dvARB) +#define glMultiTexCoord1fARB XGL_FUNCPTR(glMultiTexCoord1fARB) +#define glMultiTexCoord1fvARB XGL_FUNCPTR(glMultiTexCoord1fvARB) +#define glMultiTexCoord1iARB XGL_FUNCPTR(glMultiTexCoord1iARB) +#define glMultiTexCoord1ivARB XGL_FUNCPTR(glMultiTexCoord1ivARB) +#define glMultiTexCoord1sARB XGL_FUNCPTR(glMultiTexCoord1sARB) +#define glMultiTexCoord1svARB XGL_FUNCPTR(glMultiTexCoord1svARB) +#define glMultiTexCoord2dARB XGL_FUNCPTR(glMultiTexCoord2dARB) +#define glMultiTexCoord2dvARB XGL_FUNCPTR(glMultiTexCoord2dvARB) +#define glMultiTexCoord2fARB XGL_FUNCPTR(glMultiTexCoord2fARB) +#define glMultiTexCoord2fvARB XGL_FUNCPTR(glMultiTexCoord2fvARB) +#define glMultiTexCoord2iARB XGL_FUNCPTR(glMultiTexCoord2iARB) +#define glMultiTexCoord2ivARB XGL_FUNCPTR(glMultiTexCoord2ivARB) +#define glMultiTexCoord2sARB XGL_FUNCPTR(glMultiTexCoord2sARB) +#define glMultiTexCoord2svARB XGL_FUNCPTR(glMultiTexCoord2svARB) +#define glMultiTexCoord3dARB XGL_FUNCPTR(glMultiTexCoord3dARB) +#define glMultiTexCoord3dvARB XGL_FUNCPTR(glMultiTexCoord3dvARB) +#define glMultiTexCoord3fARB XGL_FUNCPTR(glMultiTexCoord3fARB) +#define glMultiTexCoord3fvARB XGL_FUNCPTR(glMultiTexCoord3fvARB) +#define glMultiTexCoord3iARB XGL_FUNCPTR(glMultiTexCoord3iARB) +#define glMultiTexCoord3ivARB XGL_FUNCPTR(glMultiTexCoord3ivARB) +#define glMultiTexCoord3sARB XGL_FUNCPTR(glMultiTexCoord3sARB) +#define glMultiTexCoord3svARB XGL_FUNCPTR(glMultiTexCoord3svARB) +#define glMultiTexCoord4dARB XGL_FUNCPTR(glMultiTexCoord4dARB) +#define glMultiTexCoord4dvARB XGL_FUNCPTR(glMultiTexCoord4dvARB) +#define glMultiTexCoord4fARB XGL_FUNCPTR(glMultiTexCoord4fARB) +#define glMultiTexCoord4fvARB XGL_FUNCPTR(glMultiTexCoord4fvARB) +#define glMultiTexCoord4iARB XGL_FUNCPTR(glMultiTexCoord4iARB) +#define glMultiTexCoord4ivARB XGL_FUNCPTR(glMultiTexCoord4ivARB) +#define glMultiTexCoord4sARB XGL_FUNCPTR(glMultiTexCoord4sARB) +#define glMultiTexCoord4svARB XGL_FUNCPTR(glMultiTexCoord4svARB) +#endif + +#ifdef GL_ARB_occlusion_query +#define GL_QUERY_COUNTER_BITS_ARB 0x8864 +#define GL_CURRENT_QUERY_ARB 0x8865 +#define GL_QUERY_RESULT_ARB 0x8866 +#define GL_QUERY_RESULT_AVAILABLE_ARB 0x8867 +#define GL_SAMPLES_PASSED_ARB 0x8914 +#define glBeginQueryARB XGL_FUNCPTR(glBeginQueryARB) +#define glDeleteQueriesARB XGL_FUNCPTR(glDeleteQueriesARB) +#define glEndQueryARB XGL_FUNCPTR(glEndQueryARB) +#define glGenQueriesARB XGL_FUNCPTR(glGenQueriesARB) +#define glGetQueryObjectivARB XGL_FUNCPTR(glGetQueryObjectivARB) +#define glGetQueryObjectuivARB XGL_FUNCPTR(glGetQueryObjectuivARB) +#define glGetQueryivARB XGL_FUNCPTR(glGetQueryivARB) +#define glIsQueryARB XGL_FUNCPTR(glIsQueryARB) +#endif + +#ifdef GL_ARB_pixel_buffer_object +#define GL_PIXEL_PACK_BUFFER_ARB 0x88EB +#define GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING_ARB 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING_ARB 0x88EF +#endif + +#ifdef GL_ARB_point_parameters +#define GL_POINT_SIZE_MIN_ARB 0x8126 +#define GL_POINT_SIZE_MAX_ARB 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE_ARB 0x8128 +#define GL_POINT_DISTANCE_ATTENUATION_ARB 0x8129 +#define glPointParameterfARB XGL_FUNCPTR(glPointParameterfARB) +#define glPointParameterfvARB XGL_FUNCPTR(glPointParameterfvARB) +#endif + +#ifdef GL_ARB_point_sprite +#define GL_POINT_SPRITE_ARB 0x8861 +#define GL_COORD_REPLACE_ARB 0x8862 +#endif + +#ifdef GL_ARB_shader_objects +typedef char GLcharARB; +typedef unsigned int GLhandleARB; +#define GL_PROGRAM_OBJECT_ARB 0x8B40 +#define GL_SHADER_OBJECT_ARB 0x8B48 +#define GL_OBJECT_TYPE_ARB 0x8B4E +#define GL_OBJECT_SUBTYPE_ARB 0x8B4F +#define GL_FLOAT_VEC2_ARB 0x8B50 +#define GL_FLOAT_VEC3_ARB 0x8B51 +#define GL_FLOAT_VEC4_ARB 0x8B52 +#define GL_INT_VEC2_ARB 0x8B53 +#define GL_INT_VEC3_ARB 0x8B54 +#define GL_INT_VEC4_ARB 0x8B55 +#define GL_BOOL_ARB 0x8B56 +#define GL_BOOL_VEC2_ARB 0x8B57 +#define GL_BOOL_VEC3_ARB 0x8B58 +#define GL_BOOL_VEC4_ARB 0x8B59 +#define GL_FLOAT_MAT2_ARB 0x8B5A +#define GL_FLOAT_MAT3_ARB 0x8B5B +#define GL_FLOAT_MAT4_ARB 0x8B5C +#define GL_SAMPLER_1D_ARB 0x8B5D +#define GL_SAMPLER_2D_ARB 0x8B5E +#define GL_SAMPLER_3D_ARB 0x8B5F +#define GL_SAMPLER_CUBE_ARB 0x8B60 +#define GL_SAMPLER_1D_SHADOW_ARB 0x8B61 +#define GL_SAMPLER_2D_SHADOW_ARB 0x8B62 +#define GL_SAMPLER_2D_RECT_ARB 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW_ARB 0x8B64 +#define GL_OBJECT_DELETE_STATUS_ARB 0x8B80 +#define GL_OBJECT_COMPILE_STATUS_ARB 0x8B81 +#define GL_OBJECT_LINK_STATUS_ARB 0x8B82 +#define GL_OBJECT_VALIDATE_STATUS_ARB 0x8B83 +#define GL_OBJECT_INFO_LOG_LENGTH_ARB 0x8B84 +#define GL_OBJECT_ATTACHED_OBJECTS_ARB 0x8B85 +#define GL_OBJECT_ACTIVE_UNIFORMS_ARB 0x8B86 +#define GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB 0x8B87 +#define GL_OBJECT_SHADER_SOURCE_LENGTH_ARB 0x8B88 +#define glAttachObjectARB XGL_FUNCPTR(glAttachObjectARB) +#define glCompileShaderARB XGL_FUNCPTR(glCompileShaderARB) +#define glCreateProgramObjectARB XGL_FUNCPTR(glCreateProgramObjectARB) +#define glCreateShaderObjectARB XGL_FUNCPTR(glCreateShaderObjectARB) +#define glDeleteObjectARB XGL_FUNCPTR(glDeleteObjectARB) +#define glDetachObjectARB XGL_FUNCPTR(glDetachObjectARB) +#define glGetActiveUniformARB XGL_FUNCPTR(glGetActiveUniformARB) +#define glGetAttachedObjectsARB XGL_FUNCPTR(glGetAttachedObjectsARB) +#define glGetHandleARB XGL_FUNCPTR(glGetHandleARB) +#define glGetInfoLogARB XGL_FUNCPTR(glGetInfoLogARB) +#define glGetObjectParameterfvARB XGL_FUNCPTR(glGetObjectParameterfvARB) +#define glGetObjectParameterivARB XGL_FUNCPTR(glGetObjectParameterivARB) +#define glGetShaderSourceARB XGL_FUNCPTR(glGetShaderSourceARB) +#define glGetUniformLocationARB XGL_FUNCPTR(glGetUniformLocationARB) +#define glGetUniformfvARB XGL_FUNCPTR(glGetUniformfvARB) +#define glGetUniformivARB XGL_FUNCPTR(glGetUniformivARB) +#define glLinkProgramARB XGL_FUNCPTR(glLinkProgramARB) +#define glShaderSourceARB XGL_FUNCPTR(glShaderSourceARB) +#define glUniform1fARB XGL_FUNCPTR(glUniform1fARB) +#define glUniform1fvARB XGL_FUNCPTR(glUniform1fvARB) +#define glUniform1iARB XGL_FUNCPTR(glUniform1iARB) +#define glUniform1ivARB XGL_FUNCPTR(glUniform1ivARB) +#define glUniform2fARB XGL_FUNCPTR(glUniform2fARB) +#define glUniform2fvARB XGL_FUNCPTR(glUniform2fvARB) +#define glUniform2iARB XGL_FUNCPTR(glUniform2iARB) +#define glUniform2ivARB XGL_FUNCPTR(glUniform2ivARB) +#define glUniform3fARB XGL_FUNCPTR(glUniform3fARB) +#define glUniform3fvARB XGL_FUNCPTR(glUniform3fvARB) +#define glUniform3iARB XGL_FUNCPTR(glUniform3iARB) +#define glUniform3ivARB XGL_FUNCPTR(glUniform3ivARB) +#define glUniform4fARB XGL_FUNCPTR(glUniform4fARB) +#define glUniform4fvARB XGL_FUNCPTR(glUniform4fvARB) +#define glUniform4iARB XGL_FUNCPTR(glUniform4iARB) +#define glUniform4ivARB XGL_FUNCPTR(glUniform4ivARB) +#define glUniformMatrix2fvARB XGL_FUNCPTR(glUniformMatrix2fvARB) +#define glUniformMatrix3fvARB XGL_FUNCPTR(glUniformMatrix3fvARB) +#define glUniformMatrix4fvARB XGL_FUNCPTR(glUniformMatrix4fvARB) +#define glUseProgramObjectARB XGL_FUNCPTR(glUseProgramObjectARB) +#define glValidateProgramARB XGL_FUNCPTR(glValidateProgramARB) +#endif + +#ifdef GL_ARB_shading_language_100 +#define GL_SHADING_LANGUAGE_VERSION_ARB 0x8B8C +#endif + +#ifdef GL_ARB_shadow +#define GL_TEXTURE_COMPARE_MODE_ARB 0x884C +#define GL_TEXTURE_COMPARE_FUNC_ARB 0x884D +#define GL_COMPARE_R_TO_TEXTURE_ARB 0x884E +#endif + +#ifdef GL_ARB_shadow_ambient +#define GL_TEXTURE_COMPARE_FAIL_VALUE_ARB 0x80BF +#endif + +#ifdef GL_ARB_texture_border_clamp +#define GL_CLAMP_TO_BORDER_ARB 0x812D +#endif + +#ifdef GL_ARB_texture_compression +#define GL_COMPRESSED_ALPHA_ARB 0x84E9 +#define GL_COMPRESSED_LUMINANCE_ARB 0x84EA +#define GL_COMPRESSED_LUMINANCE_ALPHA_ARB 0x84EB +#define GL_COMPRESSED_INTENSITY_ARB 0x84EC +#define GL_COMPRESSED_RGB_ARB 0x84ED +#define GL_COMPRESSED_RGBA_ARB 0x84EE +#define GL_TEXTURE_COMPRESSION_HINT_ARB 0x84EF +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB 0x86A0 +#define GL_TEXTURE_COMPRESSED_ARB 0x86A1 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A3 +#define glCompressedTexImage1DARB XGL_FUNCPTR(glCompressedTexImage1DARB) +#define glCompressedTexImage2DARB XGL_FUNCPTR(glCompressedTexImage2DARB) +#define glCompressedTexImage3DARB XGL_FUNCPTR(glCompressedTexImage3DARB) +#define glCompressedTexSubImage1DARB XGL_FUNCPTR(glCompressedTexSubImage1DARB) +#define glCompressedTexSubImage2DARB XGL_FUNCPTR(glCompressedTexSubImage2DARB) +#define glCompressedTexSubImage3DARB XGL_FUNCPTR(glCompressedTexSubImage3DARB) +#define glGetCompressedTexImageARB XGL_FUNCPTR(glGetCompressedTexImageARB) +#endif + +#ifdef GL_ARB_texture_cube_map +#define GL_NORMAL_MAP_ARB 0x8511 +#define GL_REFLECTION_MAP_ARB 0x8512 +#define GL_TEXTURE_CUBE_MAP_ARB 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARB 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARB 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB 0x851C +#endif + +#ifdef GL_ARB_texture_env_add +#endif + +#ifdef GL_ARB_texture_env_combine +#define GL_SUBTRACT_ARB 0x84E7 +#define GL_COMBINE_ARB 0x8570 +#define GL_COMBINE_RGB_ARB 0x8571 +#define GL_COMBINE_ALPHA_ARB 0x8572 +#define GL_RGB_SCALE_ARB 0x8573 +#define GL_ADD_SIGNED_ARB 0x8574 +#define GL_INTERPOLATE_ARB 0x8575 +#define GL_CONSTANT_ARB 0x8576 +#define GL_PRIMARY_COLOR_ARB 0x8577 +#define GL_PREVIOUS_ARB 0x8578 +#define GL_SOURCE0_RGB_ARB 0x8580 +#define GL_SOURCE1_RGB_ARB 0x8581 +#define GL_SOURCE2_RGB_ARB 0x8582 +#define GL_SOURCE0_ALPHA_ARB 0x8588 +#define GL_SOURCE1_ALPHA_ARB 0x8589 +#define GL_SOURCE2_ALPHA_ARB 0x858A +#define GL_OPERAND0_RGB_ARB 0x8590 +#define GL_OPERAND1_RGB_ARB 0x8591 +#define GL_OPERAND2_RGB_ARB 0x8592 +#define GL_OPERAND0_ALPHA_ARB 0x8598 +#define GL_OPERAND1_ALPHA_ARB 0x8599 +#define GL_OPERAND2_ALPHA_ARB 0x859A +#endif + +#ifdef GL_ARB_texture_env_crossbar +#endif + +#ifdef GL_ARB_texture_env_dot3 +#define GL_DOT3_RGB_ARB 0x86AE +#define GL_DOT3_RGBA_ARB 0x86AF +#endif + +#ifdef GL_ARB_texture_float +#define GL_RGBA32F_ARB 0x8814 +#define GL_RGB32F_ARB 0x8815 +#define GL_ALPHA32F_ARB 0x8816 +#define GL_INTENSITY32F_ARB 0x8817 +#define GL_LUMINANCE32F_ARB 0x8818 +#define GL_LUMINANCE_ALPHA32F_ARB 0x8819 +#define GL_RGBA16F_ARB 0x881A +#define GL_RGB16F_ARB 0x881B +#define GL_ALPHA16F_ARB 0x881C +#define GL_INTENSITY16F_ARB 0x881D +#define GL_LUMINANCE16F_ARB 0x881E +#define GL_LUMINANCE_ALPHA16F_ARB 0x881F +#define GL_TEXTURE_RED_TYPE_ARB 0x8C10 +#define GL_TEXTURE_GREEN_TYPE_ARB 0x8C11 +#define GL_TEXTURE_BLUE_TYPE_ARB 0x8C12 +#define GL_TEXTURE_ALPHA_TYPE_ARB 0x8C13 +#define GL_TEXTURE_LUMINANCE_TYPE_ARB 0x8C14 +#define GL_TEXTURE_INTENSITY_TYPE_ARB 0x8C15 +#define GL_TEXTURE_DEPTH_TYPE_ARB 0x8C16 +#define GL_UNSIGNED_NORMALIZED_ARB 0x8C17 +#endif + +#ifdef GL_ARB_texture_mirrored_repeat +#define GL_MIRRORED_REPEAT_ARB 0x8370 +#endif + +#ifdef GL_ARB_texture_non_power_of_two +#endif + +#ifdef GL_ARB_texture_rectangle +#define GL_TEXTURE_RECTANGLE_ARB 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE_ARB 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE_ARB 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8 +#endif + +#ifdef GL_ARB_transpose_matrix +#define GL_TRANSPOSE_MODELVIEW_MATRIX_ARB 0x84E3 +#define GL_TRANSPOSE_PROJECTION_MATRIX_ARB 0x84E4 +#define GL_TRANSPOSE_TEXTURE_MATRIX_ARB 0x84E5 +#define GL_TRANSPOSE_COLOR_MATRIX_ARB 0x84E6 +#define glLoadTransposeMatrixfARB XGL_FUNCPTR(glLoadTransposeMatrixfARB) +#define glLoadTransposeMatrixdARB XGL_FUNCPTR(glLoadTransposeMatrixdARB) +#define glMultTransposeMatrixfARB XGL_FUNCPTR(glMultTransposeMatrixfARB) +#define glMultTransposeMatrixdARB XGL_FUNCPTR(glMultTransposeMatrixdARB) +#endif + +#ifdef GL_ARB_vertex_blend +#define GL_MAX_VERTEX_UNITS_ARB 0x86A4 +#define GL_ACTIVE_VERTEX_UNITS_ARB 0x86A5 +#define GL_WEIGHT_SUM_UNITY_ARB 0x86A6 +#define GL_VERTEX_BLEND_ARB 0x86A7 +#define GL_CURRENT_WEIGHT_ARB 0x86A8 +#define GL_WEIGHT_ARRAY_TYPE_ARB 0x86A9 +#define GL_WEIGHT_ARRAY_STRIDE_ARB 0x86AA +#define GL_WEIGHT_ARRAY_SIZE_ARB 0x86AB +#define GL_WEIGHT_ARRAY_POINTER_ARB 0x86AC +#define GL_WEIGHT_ARRAY_ARB 0x86AD +#define GL_MODELVIEW0_ARB 0x1700 +#define GL_MODELVIEW1_ARB 0x850A +#define GL_MODELVIEW2_ARB 0x8722 +#define GL_MODELVIEW3_ARB 0x8723 +#define GL_MODELVIEW4_ARB 0x8724 +#define GL_MODELVIEW5_ARB 0x8725 +#define GL_MODELVIEW6_ARB 0x8726 +#define GL_MODELVIEW7_ARB 0x8727 +#define GL_MODELVIEW8_ARB 0x8728 +#define GL_MODELVIEW9_ARB 0x8729 +#define GL_MODELVIEW10_ARB 0x872A +#define GL_MODELVIEW11_ARB 0x872B +#define GL_MODELVIEW12_ARB 0x872C +#define GL_MODELVIEW13_ARB 0x872D +#define GL_MODELVIEW14_ARB 0x872E +#define GL_MODELVIEW15_ARB 0x872F +#define GL_MODELVIEW16_ARB 0x8730 +#define GL_MODELVIEW17_ARB 0x8731 +#define GL_MODELVIEW18_ARB 0x8732 +#define GL_MODELVIEW19_ARB 0x8733 +#define GL_MODELVIEW20_ARB 0x8734 +#define GL_MODELVIEW21_ARB 0x8735 +#define GL_MODELVIEW22_ARB 0x8736 +#define GL_MODELVIEW23_ARB 0x8737 +#define GL_MODELVIEW24_ARB 0x8738 +#define GL_MODELVIEW25_ARB 0x8739 +#define GL_MODELVIEW26_ARB 0x873A +#define GL_MODELVIEW27_ARB 0x873B +#define GL_MODELVIEW28_ARB 0x873C +#define GL_MODELVIEW29_ARB 0x873D +#define GL_MODELVIEW30_ARB 0x873E +#define GL_MODELVIEW31_ARB 0x873F +#define glWeightbvARB XGL_FUNCPTR(glWeightbvARB) +#define glWeightsvARB XGL_FUNCPTR(glWeightsvARB) +#define glWeightivARB XGL_FUNCPTR(glWeightivARB) +#define glWeightfvARB XGL_FUNCPTR(glWeightfvARB) +#define glWeightdvARB XGL_FUNCPTR(glWeightdvARB) +#define glWeightubvARB XGL_FUNCPTR(glWeightubvARB) +#define glWeightusvARB XGL_FUNCPTR(glWeightusvARB) +#define glWeightuivARB XGL_FUNCPTR(glWeightuivARB) +#define glWeightPointerARB XGL_FUNCPTR(glWeightPointerARB) +#define glVertexBlendARB XGL_FUNCPTR(glVertexBlendARB) +#endif + +#ifdef GL_ARB_vertex_buffer_object +typedef ptrdiff_t GLsizeiptrARB; +typedef ptrdiff_t GLintptrARB; +#define GL_BUFFER_SIZE_ARB 0x8764 +#define GL_BUFFER_USAGE_ARB 0x8765 +#define GL_ARRAY_BUFFER_ARB 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER_ARB 0x8893 +#define GL_ARRAY_BUFFER_BINDING_ARB 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB 0x8895 +#define GL_VERTEX_ARRAY_BUFFER_BINDING_ARB 0x8896 +#define GL_NORMAL_ARRAY_BUFFER_BINDING_ARB 0x8897 +#define GL_COLOR_ARRAY_BUFFER_BINDING_ARB 0x8898 +#define GL_INDEX_ARRAY_BUFFER_BINDING_ARB 0x8899 +#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB 0x889A +#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB 0x889B +#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB 0x889C +#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB 0x889D +#define GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB 0x889E +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB 0x889F +#define GL_READ_ONLY_ARB 0x88B8 +#define GL_WRITE_ONLY_ARB 0x88B9 +#define GL_READ_WRITE_ARB 0x88BA +#define GL_BUFFER_ACCESS_ARB 0x88BB +#define GL_BUFFER_MAPPED_ARB 0x88BC +#define GL_BUFFER_MAP_POINTER_ARB 0x88BD +#define GL_STREAM_DRAW_ARB 0x88E0 +#define GL_STREAM_READ_ARB 0x88E1 +#define GL_STREAM_COPY_ARB 0x88E2 +#define GL_STATIC_DRAW_ARB 0x88E4 +#define GL_STATIC_READ_ARB 0x88E5 +#define GL_STATIC_COPY_ARB 0x88E6 +#define GL_DYNAMIC_DRAW_ARB 0x88E8 +#define GL_DYNAMIC_READ_ARB 0x88E9 +#define GL_DYNAMIC_COPY_ARB 0x88EA +#define glBindBufferARB XGL_FUNCPTR(glBindBufferARB) +#define glBufferDataARB XGL_FUNCPTR(glBufferDataARB) +#define glBufferSubDataARB XGL_FUNCPTR(glBufferSubDataARB) +#define glDeleteBuffersARB XGL_FUNCPTR(glDeleteBuffersARB) +#define glGenBuffersARB XGL_FUNCPTR(glGenBuffersARB) +#define glGetBufferParameterivARB XGL_FUNCPTR(glGetBufferParameterivARB) +#define glGetBufferPointervARB XGL_FUNCPTR(glGetBufferPointervARB) +#define glGetBufferSubDataARB XGL_FUNCPTR(glGetBufferSubDataARB) +#define glIsBufferARB XGL_FUNCPTR(glIsBufferARB) +#define glMapBufferARB XGL_FUNCPTR(glMapBufferARB) +#define glUnmapBufferARB XGL_FUNCPTR(glUnmapBufferARB) +#endif + +#ifdef GL_ARB_vertex_program +#define GL_COLOR_SUM_ARB 0x8458 +#define GL_VERTEX_PROGRAM_ARB 0x8620 +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB 0x8625 +#define GL_CURRENT_VERTEX_ATTRIB_ARB 0x8626 +#define GL_PROGRAM_LENGTH_ARB 0x8627 +#define GL_PROGRAM_STRING_ARB 0x8628 +#define GL_MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB 0x862E +#define GL_MAX_PROGRAM_MATRICES_ARB 0x862F +#define GL_CURRENT_MATRIX_STACK_DEPTH_ARB 0x8640 +#define GL_CURRENT_MATRIX_ARB 0x8641 +#define GL_VERTEX_PROGRAM_POINT_SIZE_ARB 0x8642 +#define GL_VERTEX_PROGRAM_TWO_SIDE_ARB 0x8643 +#define GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB 0x8645 +#define GL_PROGRAM_ERROR_POSITION_ARB 0x864B +#define GL_PROGRAM_BINDING_ARB 0x8677 +#define GL_MAX_VERTEX_ATTRIBS_ARB 0x8869 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB 0x886A +#define GL_PROGRAM_ERROR_STRING_ARB 0x8874 +#define GL_PROGRAM_FORMAT_ASCII_ARB 0x8875 +#define GL_PROGRAM_FORMAT_ARB 0x8876 +#define GL_PROGRAM_INSTRUCTIONS_ARB 0x88A0 +#define GL_MAX_PROGRAM_INSTRUCTIONS_ARB 0x88A1 +#define GL_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A2 +#define GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A3 +#define GL_PROGRAM_TEMPORARIES_ARB 0x88A4 +#define GL_MAX_PROGRAM_TEMPORARIES_ARB 0x88A5 +#define GL_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A6 +#define GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A7 +#define GL_PROGRAM_PARAMETERS_ARB 0x88A8 +#define GL_MAX_PROGRAM_PARAMETERS_ARB 0x88A9 +#define GL_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AA +#define GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AB +#define GL_PROGRAM_ATTRIBS_ARB 0x88AC +#define GL_MAX_PROGRAM_ATTRIBS_ARB 0x88AD +#define GL_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AE +#define GL_MAX_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AF +#define GL_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B0 +#define GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B1 +#define GL_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B2 +#define GL_MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B3 +#define GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB 0x88B4 +#define GL_MAX_PROGRAM_ENV_PARAMETERS_ARB 0x88B5 +#define GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB 0x88B6 +#define GL_TRANSPOSE_CURRENT_MATRIX_ARB 0x88B7 +#define GL_MATRIX0_ARB 0x88C0 +#define GL_MATRIX1_ARB 0x88C1 +#define GL_MATRIX2_ARB 0x88C2 +#define GL_MATRIX3_ARB 0x88C3 +#define GL_MATRIX4_ARB 0x88C4 +#define GL_MATRIX5_ARB 0x88C5 +#define GL_MATRIX6_ARB 0x88C6 +#define GL_MATRIX7_ARB 0x88C7 +#define GL_MATRIX8_ARB 0x88C8 +#define GL_MATRIX9_ARB 0x88C9 +#define GL_MATRIX10_ARB 0x88CA +#define GL_MATRIX11_ARB 0x88CB +#define GL_MATRIX12_ARB 0x88CC +#define GL_MATRIX13_ARB 0x88CD +#define GL_MATRIX14_ARB 0x88CE +#define GL_MATRIX15_ARB 0x88CF +#define GL_MATRIX16_ARB 0x88D0 +#define GL_MATRIX17_ARB 0x88D1 +#define GL_MATRIX18_ARB 0x88D2 +#define GL_MATRIX19_ARB 0x88D3 +#define GL_MATRIX20_ARB 0x88D4 +#define GL_MATRIX21_ARB 0x88D5 +#define GL_MATRIX22_ARB 0x88D6 +#define GL_MATRIX23_ARB 0x88D7 +#define GL_MATRIX24_ARB 0x88D8 +#define GL_MATRIX25_ARB 0x88D9 +#define GL_MATRIX26_ARB 0x88DA +#define GL_MATRIX27_ARB 0x88DB +#define GL_MATRIX28_ARB 0x88DC +#define GL_MATRIX29_ARB 0x88DD +#define GL_MATRIX30_ARB 0x88DE +#define GL_MATRIX31_ARB 0x88DF +#define glBindProgramARB XGL_FUNCPTR(glBindProgramARB) +#define glDeleteProgramsARB XGL_FUNCPTR(glDeleteProgramsARB) +#define glDisableVertexAttribArrayARB XGL_FUNCPTR(glDisableVertexAttribArrayARB) +#define glEnableVertexAttribArrayARB XGL_FUNCPTR(glEnableVertexAttribArrayARB) +#define glGenProgramsARB XGL_FUNCPTR(glGenProgramsARB) +#define glGetProgramEnvParameterdvARB XGL_FUNCPTR(glGetProgramEnvParameterdvARB) +#define glGetProgramEnvParameterfvARB XGL_FUNCPTR(glGetProgramEnvParameterfvARB) +#define glGetProgramLocalParameterdvARB XGL_FUNCPTR(glGetProgramLocalParameterdvARB) +#define glGetProgramLocalParameterfvARB XGL_FUNCPTR(glGetProgramLocalParameterfvARB) +#define glGetProgramStringARB XGL_FUNCPTR(glGetProgramStringARB) +#define glGetProgramivARB XGL_FUNCPTR(glGetProgramivARB) +#define glGetVertexAttribPointervARB XGL_FUNCPTR(glGetVertexAttribPointervARB) +#define glGetVertexAttribdvARB XGL_FUNCPTR(glGetVertexAttribdvARB) +#define glGetVertexAttribfvARB XGL_FUNCPTR(glGetVertexAttribfvARB) +#define glGetVertexAttribivARB XGL_FUNCPTR(glGetVertexAttribivARB) +#define glIsProgramARB XGL_FUNCPTR(glIsProgramARB) +#define glProgramEnvParameter4dARB XGL_FUNCPTR(glProgramEnvParameter4dARB) +#define glProgramEnvParameter4dvARB XGL_FUNCPTR(glProgramEnvParameter4dvARB) +#define glProgramEnvParameter4fARB XGL_FUNCPTR(glProgramEnvParameter4fARB) +#define glProgramEnvParameter4fvARB XGL_FUNCPTR(glProgramEnvParameter4fvARB) +#define glProgramLocalParameter4dARB XGL_FUNCPTR(glProgramLocalParameter4dARB) +#define glProgramLocalParameter4dvARB XGL_FUNCPTR(glProgramLocalParameter4dvARB) +#define glProgramLocalParameter4fARB XGL_FUNCPTR(glProgramLocalParameter4fARB) +#define glProgramLocalParameter4fvARB XGL_FUNCPTR(glProgramLocalParameter4fvARB) +#define glProgramStringARB XGL_FUNCPTR(glProgramStringARB) +#define glVertexAttrib1dARB XGL_FUNCPTR(glVertexAttrib1dARB) +#define glVertexAttrib1dvARB XGL_FUNCPTR(glVertexAttrib1dvARB) +#define glVertexAttrib1fARB XGL_FUNCPTR(glVertexAttrib1fARB) +#define glVertexAttrib1fvARB XGL_FUNCPTR(glVertexAttrib1fvARB) +#define glVertexAttrib1sARB XGL_FUNCPTR(glVertexAttrib1sARB) +#define glVertexAttrib1svARB XGL_FUNCPTR(glVertexAttrib1svARB) +#define glVertexAttrib2dARB XGL_FUNCPTR(glVertexAttrib2dARB) +#define glVertexAttrib2dvARB XGL_FUNCPTR(glVertexAttrib2dvARB) +#define glVertexAttrib2fARB XGL_FUNCPTR(glVertexAttrib2fARB) +#define glVertexAttrib2fvARB XGL_FUNCPTR(glVertexAttrib2fvARB) +#define glVertexAttrib2sARB XGL_FUNCPTR(glVertexAttrib2sARB) +#define glVertexAttrib2svARB XGL_FUNCPTR(glVertexAttrib2svARB) +#define glVertexAttrib3dARB XGL_FUNCPTR(glVertexAttrib3dARB) +#define glVertexAttrib3dvARB XGL_FUNCPTR(glVertexAttrib3dvARB) +#define glVertexAttrib3fARB XGL_FUNCPTR(glVertexAttrib3fARB) +#define glVertexAttrib3fvARB XGL_FUNCPTR(glVertexAttrib3fvARB) +#define glVertexAttrib3sARB XGL_FUNCPTR(glVertexAttrib3sARB) +#define glVertexAttrib3svARB XGL_FUNCPTR(glVertexAttrib3svARB) +#define glVertexAttrib4NbvARB XGL_FUNCPTR(glVertexAttrib4NbvARB) +#define glVertexAttrib4NivARB XGL_FUNCPTR(glVertexAttrib4NivARB) +#define glVertexAttrib4NsvARB XGL_FUNCPTR(glVertexAttrib4NsvARB) +#define glVertexAttrib4NubARB XGL_FUNCPTR(glVertexAttrib4NubARB) +#define glVertexAttrib4NubvARB XGL_FUNCPTR(glVertexAttrib4NubvARB) +#define glVertexAttrib4NuivARB XGL_FUNCPTR(glVertexAttrib4NuivARB) +#define glVertexAttrib4NusvARB XGL_FUNCPTR(glVertexAttrib4NusvARB) +#define glVertexAttrib4bvARB XGL_FUNCPTR(glVertexAttrib4bvARB) +#define glVertexAttrib4dARB XGL_FUNCPTR(glVertexAttrib4dARB) +#define glVertexAttrib4dvARB XGL_FUNCPTR(glVertexAttrib4dvARB) +#define glVertexAttrib4fARB XGL_FUNCPTR(glVertexAttrib4fARB) +#define glVertexAttrib4fvARB XGL_FUNCPTR(glVertexAttrib4fvARB) +#define glVertexAttrib4ivARB XGL_FUNCPTR(glVertexAttrib4ivARB) +#define glVertexAttrib4sARB XGL_FUNCPTR(glVertexAttrib4sARB) +#define glVertexAttrib4svARB XGL_FUNCPTR(glVertexAttrib4svARB) +#define glVertexAttrib4ubvARB XGL_FUNCPTR(glVertexAttrib4ubvARB) +#define glVertexAttrib4uivARB XGL_FUNCPTR(glVertexAttrib4uivARB) +#define glVertexAttrib4usvARB XGL_FUNCPTR(glVertexAttrib4usvARB) +#define glVertexAttribPointerARB XGL_FUNCPTR(glVertexAttribPointerARB) +#endif + +#ifdef GL_ARB_vertex_shader +#define GL_VERTEX_SHADER_ARB 0x8B31 +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB 0x8B4A +#define GL_MAX_VARYING_FLOATS_ARB 0x8B4B +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB 0x8B4C +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB 0x8B4D +#define GL_OBJECT_ACTIVE_ATTRIBUTES_ARB 0x8B89 +#define GL_OBJECT_ACTIVE_ATTRIBUTE_MAX_LENGTH_ARB 0x8B8A +#define glBindAttribLocationARB XGL_FUNCPTR(glBindAttribLocationARB) +#define glGetActiveAttribARB XGL_FUNCPTR(glGetActiveAttribARB) +#define glGetAttribLocationARB XGL_FUNCPTR(glGetAttribLocationARB) +#endif + +#ifdef GL_ARB_window_pos +#define glWindowPos2dARB XGL_FUNCPTR(glWindowPos2dARB) +#define glWindowPos2dvARB XGL_FUNCPTR(glWindowPos2dvARB) +#define glWindowPos2fARB XGL_FUNCPTR(glWindowPos2fARB) +#define glWindowPos2fvARB XGL_FUNCPTR(glWindowPos2fvARB) +#define glWindowPos2iARB XGL_FUNCPTR(glWindowPos2iARB) +#define glWindowPos2ivARB XGL_FUNCPTR(glWindowPos2ivARB) +#define glWindowPos2sARB XGL_FUNCPTR(glWindowPos2sARB) +#define glWindowPos2svARB XGL_FUNCPTR(glWindowPos2svARB) +#define glWindowPos3dARB XGL_FUNCPTR(glWindowPos3dARB) +#define glWindowPos3dvARB XGL_FUNCPTR(glWindowPos3dvARB) +#define glWindowPos3fARB XGL_FUNCPTR(glWindowPos3fARB) +#define glWindowPos3fvARB XGL_FUNCPTR(glWindowPos3fvARB) +#define glWindowPos3iARB XGL_FUNCPTR(glWindowPos3iARB) +#define glWindowPos3ivARB XGL_FUNCPTR(glWindowPos3ivARB) +#define glWindowPos3sARB XGL_FUNCPTR(glWindowPos3sARB) +#define glWindowPos3svARB XGL_FUNCPTR(glWindowPos3svARB) +#endif + +#ifdef GL_ATIX_point_sprites +#define GL_TEXTURE_POINT_MODE_ATIX 0x60b0 +#define GL_TEXTURE_POINT_ONE_COORD_ATIX 0x60b1 +#define GL_TEXTURE_POINT_SPRITE_ATIX 0x60b2 +#define GL_POINT_SPRITE_CULL_MODE_ATIX 0x60b3 +#define GL_POINT_SPRITE_CULL_CENTER_ATIX 0x60b4 +#define GL_POINT_SPRITE_CULL_CLIP_ATIX 0x60b5 +#endif + +#ifdef GL_ATIX_texture_env_combine3 +#define GL_MODULATE_ADD_ATIX 0x8744 +#define GL_MODULATE_SIGNED_ADD_ATIX 0x8745 +#define GL_MODULATE_SUBTRACT_ATIX 0x8746 +#endif + +#ifdef GL_ATIX_texture_env_route +#define GL_SECONDARY_COLOR_ATIX 0x8747 +#define GL_TEXTURE_OUTPUT_RGB_ATIX 0x8748 +#define GL_TEXTURE_OUTPUT_ALPHA_ATIX 0x8749 +#endif + +#ifdef GL_ATIX_vertex_shader_output_point_size +#define GL_OUTPUT_POINT_SIZE_ATIX 0x610E +#endif + +#ifdef GL_ATI_draw_buffers +#define GL_MAX_DRAW_BUFFERS_ATI 0x8824 +#define GL_DRAW_BUFFER0_ATI 0x8825 +#define GL_DRAW_BUFFER1_ATI 0x8826 +#define GL_DRAW_BUFFER2_ATI 0x8827 +#define GL_DRAW_BUFFER3_ATI 0x8828 +#define GL_DRAW_BUFFER4_ATI 0x8829 +#define GL_DRAW_BUFFER5_ATI 0x882A +#define GL_DRAW_BUFFER6_ATI 0x882B +#define GL_DRAW_BUFFER7_ATI 0x882C +#define GL_DRAW_BUFFER8_ATI 0x882D +#define GL_DRAW_BUFFER9_ATI 0x882E +#define GL_DRAW_BUFFER10_ATI 0x882F +#define GL_DRAW_BUFFER11_ATI 0x8830 +#define GL_DRAW_BUFFER12_ATI 0x8831 +#define GL_DRAW_BUFFER13_ATI 0x8832 +#define GL_DRAW_BUFFER14_ATI 0x8833 +#define GL_DRAW_BUFFER15_ATI 0x8834 +#define glDrawBuffersATI XGL_FUNCPTR(glDrawBuffersATI) +#endif + +#ifdef GL_ATI_element_array +#define GL_ELEMENT_ARRAY_ATI 0x8768 +#define GL_ELEMENT_ARRAY_TYPE_ATI 0x8769 +#define GL_ELEMENT_ARRAY_POINTER_ATI 0x876A +#define glDrawElementArrayATI XGL_FUNCPTR(glDrawElementArrayATI) +#define glDrawRangeElementArrayATI XGL_FUNCPTR(glDrawRangeElementArrayATI) +#define glElementPointerATI XGL_FUNCPTR(glElementPointerATI) +#endif + +#ifdef GL_ATI_envmap_bumpmap +#define GL_BUMP_ROT_MATRIX_ATI 0x8775 +#define GL_BUMP_ROT_MATRIX_SIZE_ATI 0x8776 +#define GL_BUMP_NUM_TEX_UNITS_ATI 0x8777 +#define GL_BUMP_TEX_UNITS_ATI 0x8778 +#define GL_DUDV_ATI 0x8779 +#define GL_DU8DV8_ATI 0x877A +#define GL_BUMP_ENVMAP_ATI 0x877B +#define GL_BUMP_TARGET_ATI 0x877C +#define glTexBumpParameterivATI XGL_FUNCPTR(glTexBumpParameterivATI) +#define glTexBumpParameterfvATI XGL_FUNCPTR(glTexBumpParameterfvATI) +#define glGetTexBumpParameterivATI XGL_FUNCPTR(glGetTexBumpParameterivATI) +#define glGetTexBumpParameterfvATI XGL_FUNCPTR(glGetTexBumpParameterfvATI) +#endif + +#ifdef GL_ATI_fragment_shader +#define GL_RED_BIT_ATI 0x00000001 +#define GL_2X_BIT_ATI 0x00000001 +#define GL_4X_BIT_ATI 0x00000002 +#define GL_GREEN_BIT_ATI 0x00000002 +#define GL_COMP_BIT_ATI 0x00000002 +#define GL_BLUE_BIT_ATI 0x00000004 +#define GL_8X_BIT_ATI 0x00000004 +#define GL_NEGATE_BIT_ATI 0x00000004 +#define GL_BIAS_BIT_ATI 0x00000008 +#define GL_HALF_BIT_ATI 0x00000008 +#define GL_QUARTER_BIT_ATI 0x00000010 +#define GL_EIGHTH_BIT_ATI 0x00000020 +#define GL_SATURATE_BIT_ATI 0x00000040 +#define GL_FRAGMENT_SHADER_ATI 0x8920 +#define GL_REG_0_ATI 0x8921 +#define GL_REG_1_ATI 0x8922 +#define GL_REG_2_ATI 0x8923 +#define GL_REG_3_ATI 0x8924 +#define GL_REG_4_ATI 0x8925 +#define GL_REG_5_ATI 0x8926 +#define GL_CON_0_ATI 0x8941 +#define GL_CON_1_ATI 0x8942 +#define GL_CON_2_ATI 0x8943 +#define GL_CON_3_ATI 0x8944 +#define GL_CON_4_ATI 0x8945 +#define GL_CON_5_ATI 0x8946 +#define GL_CON_6_ATI 0x8947 +#define GL_CON_7_ATI 0x8948 +#define GL_MOV_ATI 0x8961 +#define GL_ADD_ATI 0x8963 +#define GL_MUL_ATI 0x8964 +#define GL_SUB_ATI 0x8965 +#define GL_DOT3_ATI 0x8966 +#define GL_DOT4_ATI 0x8967 +#define GL_MAD_ATI 0x8968 +#define GL_LERP_ATI 0x8969 +#define GL_CND_ATI 0x896A +#define GL_CND0_ATI 0x896B +#define GL_DOT2_ADD_ATI 0x896C +#define GL_SECONDARY_INTERPOLATOR_ATI 0x896D +#define GL_SWIZZLE_STR_ATI 0x8976 +#define GL_SWIZZLE_STQ_ATI 0x8977 +#define GL_SWIZZLE_STR_DR_ATI 0x8978 +#define GL_SWIZZLE_STQ_DQ_ATI 0x8979 +#define GL_NUM_FRAGMENT_REGISTERS_ATI 0x896E +#define GL_NUM_FRAGMENT_CONSTANTS_ATI 0x896F +#define GL_NUM_PASSES_ATI 0x8970 +#define GL_NUM_INSTRUCTIONS_PER_PASS_ATI 0x8971 +#define GL_NUM_INSTRUCTIONS_TOTAL_ATI 0x8972 +#define GL_NUM_INPUT_INTERPOLATOR_COMPONENTS_ATI 0x8973 +#define GL_NUM_LOOPBACK_COMPONENTS_ATI 0x8974 +#define GL_COLOR_ALPHA_PAIRING_ATI 0x8975 +#define GL_SWIZZLE_STRQ_ATI 0x897A +#define GL_SWIZZLE_STRQ_DQ_ATI 0x897B +#define glAlphaFragmentOp1ATI XGL_FUNCPTR(glAlphaFragmentOp1ATI) +#define glAlphaFragmentOp2ATI XGL_FUNCPTR(glAlphaFragmentOp2ATI) +#define glAlphaFragmentOp3ATI XGL_FUNCPTR(glAlphaFragmentOp3ATI) +#define glBeginFragmentShaderATI XGL_FUNCPTR(glBeginFragmentShaderATI) +#define glBindFragmentShaderATI XGL_FUNCPTR(glBindFragmentShaderATI) +#define glColorFragmentOp1ATI XGL_FUNCPTR(glColorFragmentOp1ATI) +#define glColorFragmentOp2ATI XGL_FUNCPTR(glColorFragmentOp2ATI) +#define glColorFragmentOp3ATI XGL_FUNCPTR(glColorFragmentOp3ATI) +#define glDeleteFragmentShaderATI XGL_FUNCPTR(glDeleteFragmentShaderATI) +#define glEndFragmentShaderATI XGL_FUNCPTR(glEndFragmentShaderATI) +#define glGenFragmentShadersATI XGL_FUNCPTR(glGenFragmentShadersATI) +#define glPassTexCoordATI XGL_FUNCPTR(glPassTexCoordATI) +#define glSampleMapATI XGL_FUNCPTR(glSampleMapATI) +#define glSetFragmentShaderConstantATI XGL_FUNCPTR(glSetFragmentShaderConstantATI) +#endif + +#ifdef GL_ATI_map_object_buffer +#define glMapObjectBufferATI XGL_FUNCPTR(glMapObjectBufferATI) +#define glUnmapObjectBufferATI XGL_FUNCPTR(glUnmapObjectBufferATI) +#endif + +#ifdef GL_ATI_pn_triangles +#define GL_PN_TRIANGLES_ATI 0x87F0 +#define GL_MAX_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F1 +#define GL_PN_TRIANGLES_POINT_MODE_ATI 0x87F2 +#define GL_PN_TRIANGLES_NORMAL_MODE_ATI 0x87F3 +#define GL_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F4 +#define GL_PN_TRIANGLES_POINT_MODE_LINEAR_ATI 0x87F5 +#define GL_PN_TRIANGLES_POINT_MODE_CUBIC_ATI 0x87F6 +#define GL_PN_TRIANGLES_NORMAL_MODE_LINEAR_ATI 0x87F7 +#define GL_PN_TRIANGLES_NORMAL_MODE_QUADRATIC_ATI 0x87F8 +#define glPNTrianglesiATI XGL_FUNCPTR(glPNTrianglesiATI) +#define glPNTrianglesfATI XGL_FUNCPTR(glPNTrianglesfATI) +#endif + +#ifdef GL_ATI_separate_stencil +#define GL_STENCIL_BACK_FUNC_ATI 0x8800 +#define GL_STENCIL_BACK_FAIL_ATI 0x8801 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL_ATI 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS_ATI 0x8803 +#define glStencilOpSeparateATI XGL_FUNCPTR(glStencilOpSeparateATI) +#define glStencilFuncSeparateATI XGL_FUNCPTR(glStencilFuncSeparateATI) +#endif + +#ifdef GL_ATI_text_fragment_shader +#define GL_TEXT_FRAGMENT_SHADER_ATI 0x8200 +#endif + +#ifdef GL_ATI_texture_compression_3dc +#define GL_COMPRESSED_RGB_3DC_ATI 0x8837 +#endif + +#ifdef GL_ATI_texture_env_combine3 +#define GL_MODULATE_ADD_ATI 0x8744 +#define GL_MODULATE_SIGNED_ADD_ATI 0x8745 +#define GL_MODULATE_SUBTRACT_ATI 0x8746 +#endif + +#ifdef GL_ATI_texture_float +#define GL_RGBA_FLOAT32_ATI 0x8814 +#define GL_RGB_FLOAT32_ATI 0x8815 +#define GL_ALPHA_FLOAT32_ATI 0x8816 +#define GL_INTENSITY_FLOAT32_ATI 0x8817 +#define GL_LUMINANCE_FLOAT32_ATI 0x8818 +#define GL_LUMINANCE_ALPHA_FLOAT32_ATI 0x8819 +#define GL_RGBA_FLOAT16_ATI 0x881A +#define GL_RGB_FLOAT16_ATI 0x881B +#define GL_ALPHA_FLOAT16_ATI 0x881C +#define GL_INTENSITY_FLOAT16_ATI 0x881D +#define GL_LUMINANCE_FLOAT16_ATI 0x881E +#define GL_LUMINANCE_ALPHA_FLOAT16_ATI 0x881F +#endif + +#ifdef GL_ATI_texture_mirror_once +#define GL_MIRROR_CLAMP_ATI 0x8742 +#define GL_MIRROR_CLAMP_TO_EDGE_ATI 0x8743 +#endif + +#ifdef GL_ATI_vertex_array_object +#define GL_STATIC_ATI 0x8760 +#define GL_DYNAMIC_ATI 0x8761 +#define GL_PRESERVE_ATI 0x8762 +#define GL_DISCARD_ATI 0x8763 +#define GL_OBJECT_BUFFER_SIZE_ATI 0x8764 +#define GL_OBJECT_BUFFER_USAGE_ATI 0x8765 +#define GL_ARRAY_OBJECT_BUFFER_ATI 0x8766 +#define GL_ARRAY_OBJECT_OFFSET_ATI 0x8767 +#define glArrayObjectATI XGL_FUNCPTR(glArrayObjectATI) +#define glFreeObjectBufferATI XGL_FUNCPTR(glFreeObjectBufferATI) +#define glGetArrayObjectfvATI XGL_FUNCPTR(glGetArrayObjectfvATI) +#define glGetArrayObjectivATI XGL_FUNCPTR(glGetArrayObjectivATI) +#define glGetObjectBufferfvATI XGL_FUNCPTR(glGetObjectBufferfvATI) +#define glGetObjectBufferivATI XGL_FUNCPTR(glGetObjectBufferivATI) +#define glGetVariantArrayObjectfvATI XGL_FUNCPTR(glGetVariantArrayObjectfvATI) +#define glGetVariantArrayObjectivATI XGL_FUNCPTR(glGetVariantArrayObjectivATI) +#define glIsObjectBufferATI XGL_FUNCPTR(glIsObjectBufferATI) +#define glNewObjectBufferATI XGL_FUNCPTR(glNewObjectBufferATI) +#define glUpdateObjectBufferATI XGL_FUNCPTR(glUpdateObjectBufferATI) +#define glVariantArrayObjectATI XGL_FUNCPTR(glVariantArrayObjectATI) +#endif + +#ifdef GL_ATI_vertex_attrib_array_object +#define glGetVertexAttribArrayObjectfvATI XGL_FUNCPTR(glGetVertexAttribArrayObjectfvATI) +#define glGetVertexAttribArrayObjectivATI XGL_FUNCPTR(glGetVertexAttribArrayObjectivATI) +#define glVertexAttribArrayObjectATI XGL_FUNCPTR(glVertexAttribArrayObjectATI) +#endif + +#ifdef GL_ATI_vertex_streams +#define GL_MAX_VERTEX_STREAMS_ATI 0x876B +#define GL_VERTEX_SOURCE_ATI 0x876C +#define GL_VERTEX_STREAM0_ATI 0x876D +#define GL_VERTEX_STREAM1_ATI 0x876E +#define GL_VERTEX_STREAM2_ATI 0x876F +#define GL_VERTEX_STREAM3_ATI 0x8770 +#define GL_VERTEX_STREAM4_ATI 0x8771 +#define GL_VERTEX_STREAM5_ATI 0x8772 +#define GL_VERTEX_STREAM6_ATI 0x8773 +#define GL_VERTEX_STREAM7_ATI 0x8774 +#define glClientActiveVertexStreamATI XGL_FUNCPTR(glClientActiveVertexStreamATI) +#define glVertexBlendEnviATI XGL_FUNCPTR(glVertexBlendEnviATI) +#define glVertexBlendEnvfATI XGL_FUNCPTR(glVertexBlendEnvfATI) +#define glVertexStream2sATI XGL_FUNCPTR(glVertexStream2sATI) +#define glVertexStream2svATI XGL_FUNCPTR(glVertexStream2svATI) +#define glVertexStream2iATI XGL_FUNCPTR(glVertexStream2iATI) +#define glVertexStream2ivATI XGL_FUNCPTR(glVertexStream2ivATI) +#define glVertexStream2fATI XGL_FUNCPTR(glVertexStream2fATI) +#define glVertexStream2fvATI XGL_FUNCPTR(glVertexStream2fvATI) +#define glVertexStream2dATI XGL_FUNCPTR(glVertexStream2dATI) +#define glVertexStream2dvATI XGL_FUNCPTR(glVertexStream2dvATI) +#define glVertexStream3sATI XGL_FUNCPTR(glVertexStream3sATI) +#define glVertexStream3svATI XGL_FUNCPTR(glVertexStream3svATI) +#define glVertexStream3iATI XGL_FUNCPTR(glVertexStream3iATI) +#define glVertexStream3ivATI XGL_FUNCPTR(glVertexStream3ivATI) +#define glVertexStream3fATI XGL_FUNCPTR(glVertexStream3fATI) +#define glVertexStream3fvATI XGL_FUNCPTR(glVertexStream3fvATI) +#define glVertexStream3dATI XGL_FUNCPTR(glVertexStream3dATI) +#define glVertexStream3dvATI XGL_FUNCPTR(glVertexStream3dvATI) +#define glVertexStream4sATI XGL_FUNCPTR(glVertexStream4sATI) +#define glVertexStream4svATI XGL_FUNCPTR(glVertexStream4svATI) +#define glVertexStream4iATI XGL_FUNCPTR(glVertexStream4iATI) +#define glVertexStream4ivATI XGL_FUNCPTR(glVertexStream4ivATI) +#define glVertexStream4fATI XGL_FUNCPTR(glVertexStream4fATI) +#define glVertexStream4fvATI XGL_FUNCPTR(glVertexStream4fvATI) +#define glVertexStream4dATI XGL_FUNCPTR(glVertexStream4dATI) +#define glVertexStream4dvATI XGL_FUNCPTR(glVertexStream4dvATI) +#define glNormalStream3bATI XGL_FUNCPTR(glNormalStream3bATI) +#define glNormalStream3bvATI XGL_FUNCPTR(glNormalStream3bvATI) +#define glNormalStream3sATI XGL_FUNCPTR(glNormalStream3sATI) +#define glNormalStream3svATI XGL_FUNCPTR(glNormalStream3svATI) +#define glNormalStream3iATI XGL_FUNCPTR(glNormalStream3iATI) +#define glNormalStream3ivATI XGL_FUNCPTR(glNormalStream3ivATI) +#define glNormalStream3fATI XGL_FUNCPTR(glNormalStream3fATI) +#define glNormalStream3fvATI XGL_FUNCPTR(glNormalStream3fvATI) +#define glNormalStream3dATI XGL_FUNCPTR(glNormalStream3dATI) +#define glNormalStream3dvATI XGL_FUNCPTR(glNormalStream3dvATI) +#endif + +#ifdef GL_EXT_422_pixels +#define GL_422_EXT 0x80CC +#define GL_422_REV_EXT 0x80CD +#define GL_422_AVERAGE_EXT 0x80CE +#define GL_422_REV_AVERAGE_EXT 0x80CF +#endif + +#ifdef GL_EXT_Cg_shader +#define GL_CG_VERTEX_SHADER_EXT 0x890E +#define GL_CG_FRAGMENT_SHADER_EXT 0x890F +#endif + +#ifdef GL_EXT_abgr +#define GL_ABGR_EXT 0x8000 +#endif + +#ifdef GL_EXT_bgra +#define GL_BGR_EXT 0x80E0 +#define GL_BGRA_EXT 0x80E1 +#endif + +#ifdef GL_EXT_blend_color +#define GL_CONSTANT_COLOR_EXT 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR_EXT 0x8002 +#define GL_CONSTANT_ALPHA_EXT 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA_EXT 0x8004 +#define GL_BLEND_COLOR_EXT 0x8005 +#define glBlendColorEXT XGL_FUNCPTR(glBlendColorEXT) +#endif + +#ifdef GL_EXT_blend_equation_separate +#define GL_BLEND_EQUATION_RGB_EXT 0x8009 +#define GL_BLEND_EQUATION_ALPHA_EXT 0x883D +#define glBlendEquationSeparateEXT XGL_FUNCPTR(glBlendEquationSeparateEXT) +#endif + +#ifdef GL_EXT_blend_func_separate +#define GL_BLEND_DST_RGB_EXT 0x80C8 +#define GL_BLEND_SRC_RGB_EXT 0x80C9 +#define GL_BLEND_DST_ALPHA_EXT 0x80CA +#define GL_BLEND_SRC_ALPHA_EXT 0x80CB +#define glBlendFuncSeparateEXT XGL_FUNCPTR(glBlendFuncSeparateEXT) +#endif + +#ifdef GL_EXT_blend_logic_op +#endif + +#ifdef GL_EXT_blend_minmax +#define GL_FUNC_ADD_EXT 0x8006 +#define GL_MIN_EXT 0x8007 +#define GL_MAX_EXT 0x8008 +#define GL_BLEND_EQUATION_EXT 0x8009 +#define glBlendEquationEXT XGL_FUNCPTR(glBlendEquationEXT) +#endif + +#ifdef GL_EXT_blend_subtract +#define GL_FUNC_SUBTRACT_EXT 0x800A +#define GL_FUNC_REVERSE_SUBTRACT_EXT 0x800B +#endif + +#ifdef GL_EXT_clip_volume_hint +#define GL_CLIP_VOLUME_CLIPPING_HINT_EXT 0x80F0 +#endif + +#ifdef GL_EXT_cmyka +#define GL_CMYK_EXT 0x800C +#define GL_CMYKA_EXT 0x800D +#define GL_PACK_CMYK_HINT_EXT 0x800E +#define GL_UNPACK_CMYK_HINT_EXT 0x800F +#endif + +#ifdef GL_EXT_color_subtable +#define glColorSubTableEXT XGL_FUNCPTR(glColorSubTableEXT) +#define glCopyColorSubTableEXT XGL_FUNCPTR(glCopyColorSubTableEXT) +#endif + +#ifdef GL_EXT_compiled_vertex_array +#define glLockArraysEXT XGL_FUNCPTR(glLockArraysEXT) +#define glUnlockArraysEXT XGL_FUNCPTR(glUnlockArraysEXT) +#endif + +#ifdef GL_EXT_convolution +#define GL_CONVOLUTION_1D_EXT 0x8010 +#define GL_CONVOLUTION_2D_EXT 0x8011 +#define GL_SEPARABLE_2D_EXT 0x8012 +#define GL_CONVOLUTION_BORDER_MODE_EXT 0x8013 +#define GL_CONVOLUTION_FILTER_SCALE_EXT 0x8014 +#define GL_CONVOLUTION_FILTER_BIAS_EXT 0x8015 +#define GL_REDUCE_EXT 0x8016 +#define GL_CONVOLUTION_FORMAT_EXT 0x8017 +#define GL_CONVOLUTION_WIDTH_EXT 0x8018 +#define GL_CONVOLUTION_HEIGHT_EXT 0x8019 +#define GL_MAX_CONVOLUTION_WIDTH_EXT 0x801A +#define GL_MAX_CONVOLUTION_HEIGHT_EXT 0x801B +#define GL_POST_CONVOLUTION_RED_SCALE_EXT 0x801C +#define GL_POST_CONVOLUTION_GREEN_SCALE_EXT 0x801D +#define GL_POST_CONVOLUTION_BLUE_SCALE_EXT 0x801E +#define GL_POST_CONVOLUTION_ALPHA_SCALE_EXT 0x801F +#define GL_POST_CONVOLUTION_RED_BIAS_EXT 0x8020 +#define GL_POST_CONVOLUTION_GREEN_BIAS_EXT 0x8021 +#define GL_POST_CONVOLUTION_BLUE_BIAS_EXT 0x8022 +#define GL_POST_CONVOLUTION_ALPHA_BIAS_EXT 0x8023 +#define glConvolutionFilter1DEXT XGL_FUNCPTR(glConvolutionFilter1DEXT) +#define glConvolutionFilter2DEXT XGL_FUNCPTR(glConvolutionFilter2DEXT) +#define glConvolutionParameterfEXT XGL_FUNCPTR(glConvolutionParameterfEXT) +#define glConvolutionParameterfvEXT XGL_FUNCPTR(glConvolutionParameterfvEXT) +#define glConvolutionParameteriEXT XGL_FUNCPTR(glConvolutionParameteriEXT) +#define glConvolutionParameterivEXT XGL_FUNCPTR(glConvolutionParameterivEXT) +#define glCopyConvolutionFilter1DEXT XGL_FUNCPTR(glCopyConvolutionFilter1DEXT) +#define glCopyConvolutionFilter2DEXT XGL_FUNCPTR(glCopyConvolutionFilter2DEXT) +#define glGetConvolutionFilterEXT XGL_FUNCPTR(glGetConvolutionFilterEXT) +#define glGetConvolutionParameterfvEXT XGL_FUNCPTR(glGetConvolutionParameterfvEXT) +#define glGetConvolutionParameterivEXT XGL_FUNCPTR(glGetConvolutionParameterivEXT) +#define glGetSeparableFilterEXT XGL_FUNCPTR(glGetSeparableFilterEXT) +#define glSeparableFilter2DEXT XGL_FUNCPTR(glSeparableFilter2DEXT) +#endif + +#ifdef GL_EXT_coordinate_frame +#define GL_TANGENT_ARRAY_EXT 0x8439 +#define GL_BINORMAL_ARRAY_EXT 0x843A +#define GL_CURRENT_TANGENT_EXT 0x843B +#define GL_CURRENT_BINORMAL_EXT 0x843C +#define GL_TANGENT_ARRAY_TYPE_EXT 0x843E +#define GL_TANGENT_ARRAY_STRIDE_EXT 0x843F +#define GL_BINORMAL_ARRAY_TYPE_EXT 0x8440 +#define GL_BINORMAL_ARRAY_STRIDE_EXT 0x8441 +#define GL_TANGENT_ARRAY_POINTER_EXT 0x8442 +#define GL_BINORMAL_ARRAY_POINTER_EXT 0x8443 +#define GL_MAP1_TANGENT_EXT 0x8444 +#define GL_MAP2_TANGENT_EXT 0x8445 +#define GL_MAP1_BINORMAL_EXT 0x8446 +#define GL_MAP2_BINORMAL_EXT 0x8447 +#define glBinormalPointerEXT XGL_FUNCPTR(glBinormalPointerEXT) +#define glTangentPointerEXT XGL_FUNCPTR(glTangentPointerEXT) +#endif + +#ifdef GL_EXT_copy_texture +#define glCopyTexImage1DEXT XGL_FUNCPTR(glCopyTexImage1DEXT) +#define glCopyTexImage2DEXT XGL_FUNCPTR(glCopyTexImage2DEXT) +#define glCopyTexSubImage1DEXT XGL_FUNCPTR(glCopyTexSubImage1DEXT) +#define glCopyTexSubImage2DEXT XGL_FUNCPTR(glCopyTexSubImage2DEXT) +#define glCopyTexSubImage3DEXT XGL_FUNCPTR(glCopyTexSubImage3DEXT) +#endif + +#ifdef GL_EXT_cull_vertex +#define glCullParameterdvEXT XGL_FUNCPTR(glCullParameterdvEXT) +#define glCullParameterfvEXT XGL_FUNCPTR(glCullParameterfvEXT) +#endif + +#ifdef GL_EXT_depth_bounds_test +#define GL_DEPTH_BOUNDS_TEST_EXT 0x8890 +#define GL_DEPTH_BOUNDS_EXT 0x8891 +#define glDepthBoundsEXT XGL_FUNCPTR(glDepthBoundsEXT) +#endif + +#ifdef GL_EXT_draw_range_elements +#define GL_MAX_ELEMENTS_VERTICES 0x80E8 +#define GL_MAX_ELEMENTS_INDICES 0x80E9 +#define glDrawRangeElementsEXT XGL_FUNCPTR(glDrawRangeElementsEXT) +#endif + +#ifdef GL_EXT_fog_coord +#define GL_FOG_COORDINATE_SOURCE_EXT 0x8450 +#define GL_FOG_COORDINATE_EXT 0x8451 +#define GL_FRAGMENT_DEPTH_EXT 0x8452 +#define GL_CURRENT_FOG_COORDINATE_EXT 0x8453 +#define GL_FOG_COORDINATE_ARRAY_TYPE_EXT 0x8454 +#define GL_FOG_COORDINATE_ARRAY_STRIDE_EXT 0x8455 +#define GL_FOG_COORDINATE_ARRAY_POINTER_EXT 0x8456 +#define GL_FOG_COORDINATE_ARRAY_EXT 0x8457 +#define glFogCoordfEXT XGL_FUNCPTR(glFogCoordfEXT) +#define glFogCoordfvEXT XGL_FUNCPTR(glFogCoordfvEXT) +#define glFogCoorddEXT XGL_FUNCPTR(glFogCoorddEXT) +#define glFogCoorddvEXT XGL_FUNCPTR(glFogCoorddvEXT) +#define glFogCoordPointerEXT XGL_FUNCPTR(glFogCoordPointerEXT) +#endif + +#ifdef GL_EXT_fragment_lighting +#define GL_FRAGMENT_LIGHTING_EXT 0x8400 +#define GL_FRAGMENT_COLOR_MATERIAL_EXT 0x8401 +#define GL_FRAGMENT_COLOR_MATERIAL_FACE_EXT 0x8402 +#define GL_FRAGMENT_COLOR_MATERIAL_PARAMETER_EXT 0x8403 +#define GL_MAX_FRAGMENT_LIGHTS_EXT 0x8404 +#define GL_MAX_ACTIVE_LIGHTS_EXT 0x8405 +#define GL_CURRENT_RASTER_NORMAL_EXT 0x8406 +#define GL_LIGHT_ENV_MODE_EXT 0x8407 +#define GL_FRAGMENT_LIGHT_MODEL_LOCAL_VIEWER_EXT 0x8408 +#define GL_FRAGMENT_LIGHT_MODEL_TWO_SIDE_EXT 0x8409 +#define GL_FRAGMENT_LIGHT_MODEL_AMBIENT_EXT 0x840A +#define GL_FRAGMENT_LIGHT_MODEL_NORMAL_INTERPOLATION_EXT 0x840B +#define GL_FRAGMENT_LIGHT0_EXT 0x840C +#define GL_FRAGMENT_LIGHT7_EXT 0x8413 +#define glFragmentColorMaterialEXT XGL_FUNCPTR(glFragmentColorMaterialEXT) +#define glFragmentLightModelfEXT XGL_FUNCPTR(glFragmentLightModelfEXT) +#define glFragmentLightModelfvEXT XGL_FUNCPTR(glFragmentLightModelfvEXT) +#define glFragmentLightModeliEXT XGL_FUNCPTR(glFragmentLightModeliEXT) +#define glFragmentLightModelivEXT XGL_FUNCPTR(glFragmentLightModelivEXT) +#define glFragmentLightfEXT XGL_FUNCPTR(glFragmentLightfEXT) +#define glFragmentLightfvEXT XGL_FUNCPTR(glFragmentLightfvEXT) +#define glFragmentLightiEXT XGL_FUNCPTR(glFragmentLightiEXT) +#define glFragmentLightivEXT XGL_FUNCPTR(glFragmentLightivEXT) +#define glFragmentMaterialfEXT XGL_FUNCPTR(glFragmentMaterialfEXT) +#define glFragmentMaterialfvEXT XGL_FUNCPTR(glFragmentMaterialfvEXT) +#define glFragmentMaterialiEXT XGL_FUNCPTR(glFragmentMaterialiEXT) +#define glFragmentMaterialivEXT XGL_FUNCPTR(glFragmentMaterialivEXT) +#define glGetFragmentLightfvEXT XGL_FUNCPTR(glGetFragmentLightfvEXT) +#define glGetFragmentLightivEXT XGL_FUNCPTR(glGetFragmentLightivEXT) +#define glGetFragmentMaterialfvEXT XGL_FUNCPTR(glGetFragmentMaterialfvEXT) +#define glGetFragmentMaterialivEXT XGL_FUNCPTR(glGetFragmentMaterialivEXT) +#define glLightEnviEXT XGL_FUNCPTR(glLightEnviEXT) +#endif + +#ifdef GL_EXT_framebuffer_blit +#define GL_READ_FRAMEBUFFER_EXT 0x8CA8 +#define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9 +#define GL_DRAW_FRAMEBUFFER_BINDING_EXT 0x8CA6 +#define GL_READ_FRAMEBUFFER_BINDING_EXT 0x8CAA +#define glBlitFramebufferEXT XGL_FUNCPTR(glBlitFramebufferEXT) +#endif + +#ifdef GL_EXT_framebuffer_object +#define GL_INVALID_FRAMEBUFFER_OPERATION_EXT 0x0506 +#define GL_MAX_RENDERBUFFER_SIZE_EXT 0x84E8 +#define GL_FRAMEBUFFER_BINDING_EXT 0x8CA6 +#define GL_RENDERBUFFER_BINDING_EXT 0x8CA7 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT 0x8CD4 +#define GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT 0x8CD8 +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9 +#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC +#define GL_FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD +#define GL_FRAMEBUFFER_STATUS_ERROR_EXT 0x8CDE +#define GL_MAX_COLOR_ATTACHMENTS_EXT 0x8CDF +#define GL_COLOR_ATTACHMENT0_EXT 0x8CE0 +#define GL_COLOR_ATTACHMENT1_EXT 0x8CE1 +#define GL_COLOR_ATTACHMENT2_EXT 0x8CE2 +#define GL_COLOR_ATTACHMENT3_EXT 0x8CE3 +#define GL_COLOR_ATTACHMENT4_EXT 0x8CE4 +#define GL_COLOR_ATTACHMENT5_EXT 0x8CE5 +#define GL_COLOR_ATTACHMENT6_EXT 0x8CE6 +#define GL_COLOR_ATTACHMENT7_EXT 0x8CE7 +#define GL_COLOR_ATTACHMENT8_EXT 0x8CE8 +#define GL_COLOR_ATTACHMENT9_EXT 0x8CE9 +#define GL_COLOR_ATTACHMENT10_EXT 0x8CEA +#define GL_COLOR_ATTACHMENT11_EXT 0x8CEB +#define GL_COLOR_ATTACHMENT12_EXT 0x8CEC +#define GL_COLOR_ATTACHMENT13_EXT 0x8CED +#define GL_COLOR_ATTACHMENT14_EXT 0x8CEE +#define GL_COLOR_ATTACHMENT15_EXT 0x8CEF +#define GL_DEPTH_ATTACHMENT_EXT 0x8D00 +#define GL_STENCIL_ATTACHMENT_EXT 0x8D20 +#define GL_FRAMEBUFFER_EXT 0x8D40 +#define GL_RENDERBUFFER_EXT 0x8D41 +#define GL_RENDERBUFFER_WIDTH_EXT 0x8D42 +#define GL_RENDERBUFFER_HEIGHT_EXT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT_EXT 0x8D44 +#define GL_STENCIL_INDEX_EXT 0x8D45 +#define GL_STENCIL_INDEX1_EXT 0x8D46 +#define GL_STENCIL_INDEX4_EXT 0x8D47 +#define GL_STENCIL_INDEX8_EXT 0x8D48 +#define GL_STENCIL_INDEX16_EXT 0x8D49 +#define glBindFramebufferEXT XGL_FUNCPTR(glBindFramebufferEXT) +#define glBindRenderbufferEXT XGL_FUNCPTR(glBindRenderbufferEXT) +#define glCheckFramebufferStatusEXT XGL_FUNCPTR(glCheckFramebufferStatusEXT) +#define glDeleteFramebuffersEXT XGL_FUNCPTR(glDeleteFramebuffersEXT) +#define glDeleteRenderbuffersEXT XGL_FUNCPTR(glDeleteRenderbuffersEXT) +#define glFramebufferRenderbufferEXT XGL_FUNCPTR(glFramebufferRenderbufferEXT) +#define glFramebufferTexture1DEXT XGL_FUNCPTR(glFramebufferTexture1DEXT) +#define glFramebufferTexture2DEXT XGL_FUNCPTR(glFramebufferTexture2DEXT) +#define glFramebufferTexture3DEXT XGL_FUNCPTR(glFramebufferTexture3DEXT) +#define glGenFramebuffersEXT XGL_FUNCPTR(glGenFramebuffersEXT) +#define glGenRenderbuffersEXT XGL_FUNCPTR(glGenRenderbuffersEXT) +#define glGenerateMipmapEXT XGL_FUNCPTR(glGenerateMipmapEXT) +#define glGetFramebufferAttachmentParameterivEXT XGL_FUNCPTR(glGetFramebufferAttachmentParameterivEXT) +#define glGetRenderbufferParameterivEXT XGL_FUNCPTR(glGetRenderbufferParameterivEXT) +#define glIsFramebufferEXT XGL_FUNCPTR(glIsFramebufferEXT) +#define glIsRenderbufferEXT XGL_FUNCPTR(glIsRenderbufferEXT) +#define glRenderbufferStorageEXT XGL_FUNCPTR(glRenderbufferStorageEXT) +#endif + +#ifdef GL_EXT_histogram +#define GL_HISTOGRAM_EXT 0x8024 +#define GL_PROXY_HISTOGRAM_EXT 0x8025 +#define GL_HISTOGRAM_WIDTH_EXT 0x8026 +#define GL_HISTOGRAM_FORMAT_EXT 0x8027 +#define GL_HISTOGRAM_RED_SIZE_EXT 0x8028 +#define GL_HISTOGRAM_GREEN_SIZE_EXT 0x8029 +#define GL_HISTOGRAM_BLUE_SIZE_EXT 0x802A +#define GL_HISTOGRAM_ALPHA_SIZE_EXT 0x802B +#define GL_HISTOGRAM_LUMINANCE_SIZE_EXT 0x802C +#define GL_HISTOGRAM_SINK_EXT 0x802D +#define GL_MINMAX_EXT 0x802E +#define GL_MINMAX_FORMAT_EXT 0x802F +#define GL_MINMAX_SINK_EXT 0x8030 +#define glGetHistogramEXT XGL_FUNCPTR(glGetHistogramEXT) +#define glGetHistogramParameterfvEXT XGL_FUNCPTR(glGetHistogramParameterfvEXT) +#define glGetHistogramParameterivEXT XGL_FUNCPTR(glGetHistogramParameterivEXT) +#define glGetMinmaxEXT XGL_FUNCPTR(glGetMinmaxEXT) +#define glGetMinmaxParameterfvEXT XGL_FUNCPTR(glGetMinmaxParameterfvEXT) +#define glGetMinmaxParameterivEXT XGL_FUNCPTR(glGetMinmaxParameterivEXT) +#define glHistogramEXT XGL_FUNCPTR(glHistogramEXT) +#define glMinmaxEXT XGL_FUNCPTR(glMinmaxEXT) +#define glResetHistogramEXT XGL_FUNCPTR(glResetHistogramEXT) +#define glResetMinmaxEXT XGL_FUNCPTR(glResetMinmaxEXT) +#endif + +#ifdef GL_EXT_index_array_formats +#endif + +#ifdef GL_EXT_index_func +#define glIndexFuncEXT XGL_FUNCPTR(glIndexFuncEXT) +#endif + +#ifdef GL_EXT_index_material +#define glIndexMaterialEXT XGL_FUNCPTR(glIndexMaterialEXT) +#endif + +#ifdef GL_EXT_index_texture +#endif + +#ifdef GL_EXT_light_texture +#define GL_FRAGMENT_MATERIAL_EXT 0x8349 +#define GL_FRAGMENT_NORMAL_EXT 0x834A +#define GL_FRAGMENT_COLOR_EXT 0x834C +#define GL_ATTENUATION_EXT 0x834D +#define GL_SHADOW_ATTENUATION_EXT 0x834E +#define GL_TEXTURE_APPLICATION_MODE_EXT 0x834F +#define GL_TEXTURE_LIGHT_EXT 0x8350 +#define GL_TEXTURE_MATERIAL_FACE_EXT 0x8351 +#define GL_TEXTURE_MATERIAL_PARAMETER_EXT 0x8352 +#define GL_FRAGMENT_DEPTH_EXT 0x8452 +#define glApplyTextureEXT XGL_FUNCPTR(glApplyTextureEXT) +#define glTextureLightEXT XGL_FUNCPTR(glTextureLightEXT) +#define glTextureMaterialEXT XGL_FUNCPTR(glTextureMaterialEXT) +#endif + +#ifdef GL_EXT_misc_attribute +#endif + +#ifdef GL_EXT_multi_draw_arrays +#define glMultiDrawArraysEXT XGL_FUNCPTR(glMultiDrawArraysEXT) +#define glMultiDrawElementsEXT XGL_FUNCPTR(glMultiDrawElementsEXT) +#endif + +#ifdef GL_EXT_multisample +#define GL_MULTISAMPLE_EXT 0x809D +#define GL_SAMPLE_ALPHA_TO_MASK_EXT 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_EXT 0x809F +#define GL_SAMPLE_MASK_EXT 0x80A0 +#define GL_1PASS_EXT 0x80A1 +#define GL_2PASS_0_EXT 0x80A2 +#define GL_2PASS_1_EXT 0x80A3 +#define GL_4PASS_0_EXT 0x80A4 +#define GL_4PASS_1_EXT 0x80A5 +#define GL_4PASS_2_EXT 0x80A6 +#define GL_4PASS_3_EXT 0x80A7 +#define GL_SAMPLE_BUFFERS_EXT 0x80A8 +#define GL_SAMPLES_EXT 0x80A9 +#define GL_SAMPLE_MASK_VALUE_EXT 0x80AA +#define GL_SAMPLE_MASK_INVERT_EXT 0x80AB +#define GL_SAMPLE_PATTERN_EXT 0x80AC +#define GL_MULTISAMPLE_BIT_EXT 0x20000000 +#define glSampleMaskEXT XGL_FUNCPTR(glSampleMaskEXT) +#define glSamplePatternEXT XGL_FUNCPTR(glSamplePatternEXT) +#endif + +#ifdef GL_EXT_packed_pixels +#define GL_UNSIGNED_BYTE_3_3_2_EXT 0x8032 +#define GL_UNSIGNED_SHORT_4_4_4_4_EXT 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1_EXT 0x8034 +#define GL_UNSIGNED_INT_8_8_8_8_EXT 0x8035 +#define GL_UNSIGNED_INT_10_10_10_2_EXT 0x8036 +#endif + +#ifdef GL_EXT_paletted_texture +#define GL_TEXTURE_1D 0x0DE0 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_PROXY_TEXTURE_1D 0x8063 +#define GL_PROXY_TEXTURE_2D 0x8064 +#define GL_TEXTURE_3D_EXT 0x806F +#define GL_PROXY_TEXTURE_3D_EXT 0x8070 +#define GL_COLOR_TABLE_FORMAT_EXT 0x80D8 +#define GL_COLOR_TABLE_WIDTH_EXT 0x80D9 +#define GL_COLOR_TABLE_RED_SIZE_EXT 0x80DA +#define GL_COLOR_TABLE_GREEN_SIZE_EXT 0x80DB +#define GL_COLOR_TABLE_BLUE_SIZE_EXT 0x80DC +#define GL_COLOR_TABLE_ALPHA_SIZE_EXT 0x80DD +#define GL_COLOR_TABLE_LUMINANCE_SIZE_EXT 0x80DE +#define GL_COLOR_TABLE_INTENSITY_SIZE_EXT 0x80DF +#define GL_COLOR_INDEX1_EXT 0x80E2 +#define GL_COLOR_INDEX2_EXT 0x80E3 +#define GL_COLOR_INDEX4_EXT 0x80E4 +#define GL_COLOR_INDEX8_EXT 0x80E5 +#define GL_COLOR_INDEX12_EXT 0x80E6 +#define GL_COLOR_INDEX16_EXT 0x80E7 +#define GL_TEXTURE_INDEX_SIZE_EXT 0x80ED +#define GL_TEXTURE_CUBE_MAP_ARB 0x8513 +#define GL_PROXY_TEXTURE_CUBE_MAP_ARB 0x851B +#define glColorTableEXT XGL_FUNCPTR(glColorTableEXT) +#define glGetColorTableEXT XGL_FUNCPTR(glGetColorTableEXT) +#define glGetColorTableParameterfvEXT XGL_FUNCPTR(glGetColorTableParameterfvEXT) +#define glGetColorTableParameterivEXT XGL_FUNCPTR(glGetColorTableParameterivEXT) +#endif + +#ifdef GL_EXT_pixel_buffer_object +#define GL_PIXEL_PACK_BUFFER_EXT 0x88EB +#define GL_PIXEL_UNPACK_BUFFER_EXT 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING_EXT 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING_EXT 0x88EF +#endif + +#ifdef GL_EXT_pixel_transform +#define GL_PIXEL_TRANSFORM_2D_EXT 0x8330 +#define GL_PIXEL_MAG_FILTER_EXT 0x8331 +#define GL_PIXEL_MIN_FILTER_EXT 0x8332 +#define GL_PIXEL_CUBIC_WEIGHT_EXT 0x8333 +#define GL_CUBIC_EXT 0x8334 +#define GL_AVERAGE_EXT 0x8335 +#define GL_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT 0x8336 +#define GL_MAX_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT 0x8337 +#define GL_PIXEL_TRANSFORM_2D_MATRIX_EXT 0x8338 +#define glGetPixelTransformParameterfvEXT XGL_FUNCPTR(glGetPixelTransformParameterfvEXT) +#define glGetPixelTransformParameterivEXT XGL_FUNCPTR(glGetPixelTransformParameterivEXT) +#define glPixelTransformParameterfEXT XGL_FUNCPTR(glPixelTransformParameterfEXT) +#define glPixelTransformParameterfvEXT XGL_FUNCPTR(glPixelTransformParameterfvEXT) +#define glPixelTransformParameteriEXT XGL_FUNCPTR(glPixelTransformParameteriEXT) +#define glPixelTransformParameterivEXT XGL_FUNCPTR(glPixelTransformParameterivEXT) +#endif + +#ifdef GL_EXT_pixel_transform_color_table +#endif + +#ifdef GL_EXT_point_parameters +#define GL_POINT_SIZE_MIN_EXT 0x8126 +#define GL_POINT_SIZE_MAX_EXT 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE_EXT 0x8128 +#define GL_DISTANCE_ATTENUATION_EXT 0x8129 +#define glPointParameterfEXT XGL_FUNCPTR(glPointParameterfEXT) +#define glPointParameterfvEXT XGL_FUNCPTR(glPointParameterfvEXT) +#endif + +#ifdef GL_EXT_polygon_offset +#define GL_POLYGON_OFFSET_EXT 0x8037 +#define GL_POLYGON_OFFSET_FACTOR_EXT 0x8038 +#define GL_POLYGON_OFFSET_BIAS_EXT 0x8039 +#define glPolygonOffsetEXT XGL_FUNCPTR(glPolygonOffsetEXT) +#endif + +#ifdef GL_EXT_rescale_normal +#endif + +#ifdef GL_EXT_scene_marker +#define glBeginSceneEXT XGL_FUNCPTR(glBeginSceneEXT) +#define glEndSceneEXT XGL_FUNCPTR(glEndSceneEXT) +#endif + +#ifdef GL_EXT_secondary_color +#define GL_COLOR_SUM_EXT 0x8458 +#define GL_CURRENT_SECONDARY_COLOR_EXT 0x8459 +#define GL_SECONDARY_COLOR_ARRAY_SIZE_EXT 0x845A +#define GL_SECONDARY_COLOR_ARRAY_TYPE_EXT 0x845B +#define GL_SECONDARY_COLOR_ARRAY_STRIDE_EXT 0x845C +#define GL_SECONDARY_COLOR_ARRAY_POINTER_EXT 0x845D +#define GL_SECONDARY_COLOR_ARRAY_EXT 0x845E +#define glSecondaryColor3bEXT XGL_FUNCPTR(glSecondaryColor3bEXT) +#define glSecondaryColor3bvEXT XGL_FUNCPTR(glSecondaryColor3bvEXT) +#define glSecondaryColor3dEXT XGL_FUNCPTR(glSecondaryColor3dEXT) +#define glSecondaryColor3dvEXT XGL_FUNCPTR(glSecondaryColor3dvEXT) +#define glSecondaryColor3fEXT XGL_FUNCPTR(glSecondaryColor3fEXT) +#define glSecondaryColor3fvEXT XGL_FUNCPTR(glSecondaryColor3fvEXT) +#define glSecondaryColor3iEXT XGL_FUNCPTR(glSecondaryColor3iEXT) +#define glSecondaryColor3ivEXT XGL_FUNCPTR(glSecondaryColor3ivEXT) +#define glSecondaryColor3sEXT XGL_FUNCPTR(glSecondaryColor3sEXT) +#define glSecondaryColor3svEXT XGL_FUNCPTR(glSecondaryColor3svEXT) +#define glSecondaryColor3ubEXT XGL_FUNCPTR(glSecondaryColor3ubEXT) +#define glSecondaryColor3ubvEXT XGL_FUNCPTR(glSecondaryColor3ubvEXT) +#define glSecondaryColor3uiEXT XGL_FUNCPTR(glSecondaryColor3uiEXT) +#define glSecondaryColor3uivEXT XGL_FUNCPTR(glSecondaryColor3uivEXT) +#define glSecondaryColor3usEXT XGL_FUNCPTR(glSecondaryColor3usEXT) +#define glSecondaryColor3usvEXT XGL_FUNCPTR(glSecondaryColor3usvEXT) +#define glSecondaryColorPointerEXT XGL_FUNCPTR(glSecondaryColorPointerEXT) +#endif + +#ifdef GL_EXT_separate_specular_color +#define GL_LIGHT_MODEL_COLOR_CONTROL_EXT 0x81F8 +#define GL_SINGLE_COLOR_EXT 0x81F9 +#define GL_SEPARATE_SPECULAR_COLOR_EXT 0x81FA +#endif + +#ifdef GL_EXT_shadow_funcs +#endif + +#ifdef GL_EXT_shared_texture_palette +#define GL_SHARED_TEXTURE_PALETTE_EXT 0x81FB +#endif + +#ifdef GL_EXT_stencil_two_side +#define GL_STENCIL_TEST_TWO_SIDE_EXT 0x8910 +#define GL_ACTIVE_STENCIL_FACE_EXT 0x8911 +#define glActiveStencilFaceEXT XGL_FUNCPTR(glActiveStencilFaceEXT) +#endif + +#ifdef GL_EXT_stencil_wrap +#define GL_INCR_WRAP_EXT 0x8507 +#define GL_DECR_WRAP_EXT 0x8508 +#endif + +#ifdef GL_EXT_subtexture +#define glTexSubImage1DEXT XGL_FUNCPTR(glTexSubImage1DEXT) +#define glTexSubImage2DEXT XGL_FUNCPTR(glTexSubImage2DEXT) +#define glTexSubImage3DEXT XGL_FUNCPTR(glTexSubImage3DEXT) +#endif + +#ifdef GL_EXT_texture +#define GL_ALPHA4_EXT 0x803B +#define GL_ALPHA8_EXT 0x803C +#define GL_ALPHA12_EXT 0x803D +#define GL_ALPHA16_EXT 0x803E +#define GL_LUMINANCE4_EXT 0x803F +#define GL_LUMINANCE8_EXT 0x8040 +#define GL_LUMINANCE12_EXT 0x8041 +#define GL_LUMINANCE16_EXT 0x8042 +#define GL_LUMINANCE4_ALPHA4_EXT 0x8043 +#define GL_LUMINANCE6_ALPHA2_EXT 0x8044 +#define GL_LUMINANCE8_ALPHA8_EXT 0x8045 +#define GL_LUMINANCE12_ALPHA4_EXT 0x8046 +#define GL_LUMINANCE12_ALPHA12_EXT 0x8047 +#define GL_LUMINANCE16_ALPHA16_EXT 0x8048 +#define GL_INTENSITY_EXT 0x8049 +#define GL_INTENSITY4_EXT 0x804A +#define GL_INTENSITY8_EXT 0x804B +#define GL_INTENSITY12_EXT 0x804C +#define GL_INTENSITY16_EXT 0x804D +#define GL_RGB2_EXT 0x804E +#define GL_RGB4_EXT 0x804F +#define GL_RGB5_EXT 0x8050 +#define GL_RGB8_EXT 0x8051 +#define GL_RGB10_EXT 0x8052 +#define GL_RGB12_EXT 0x8053 +#define GL_RGB16_EXT 0x8054 +#define GL_RGBA2_EXT 0x8055 +#define GL_RGBA4_EXT 0x8056 +#define GL_RGB5_A1_EXT 0x8057 +#define GL_RGBA8_EXT 0x8058 +#define GL_RGB10_A2_EXT 0x8059 +#define GL_RGBA12_EXT 0x805A +#define GL_RGBA16_EXT 0x805B +#define GL_TEXTURE_RED_SIZE_EXT 0x805C +#define GL_TEXTURE_GREEN_SIZE_EXT 0x805D +#define GL_TEXTURE_BLUE_SIZE_EXT 0x805E +#define GL_TEXTURE_ALPHA_SIZE_EXT 0x805F +#define GL_TEXTURE_LUMINANCE_SIZE_EXT 0x8060 +#define GL_TEXTURE_INTENSITY_SIZE_EXT 0x8061 +#define GL_REPLACE_EXT 0x8062 +#define GL_PROXY_TEXTURE_1D_EXT 0x8063 +#define GL_PROXY_TEXTURE_2D_EXT 0x8064 +#endif + +#ifdef GL_EXT_texture3D +#define GL_PACK_SKIP_IMAGES_EXT 0x806B +#define GL_PACK_IMAGE_HEIGHT_EXT 0x806C +#define GL_UNPACK_SKIP_IMAGES_EXT 0x806D +#define GL_UNPACK_IMAGE_HEIGHT_EXT 0x806E +#define GL_TEXTURE_3D_EXT 0x806F +#define GL_PROXY_TEXTURE_3D_EXT 0x8070 +#define GL_TEXTURE_DEPTH_EXT 0x8071 +#define GL_TEXTURE_WRAP_R_EXT 0x8072 +#define GL_MAX_3D_TEXTURE_SIZE_EXT 0x8073 +#define glTexImage3DEXT XGL_FUNCPTR(glTexImage3DEXT) +#endif + +#ifdef GL_EXT_texture_compression_dxt1 +#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#endif + +#ifdef GL_EXT_texture_compression_s3tc +#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#endif + +#ifdef GL_EXT_texture_cube_map +#define GL_NORMAL_MAP_EXT 0x8511 +#define GL_REFLECTION_MAP_EXT 0x8512 +#define GL_TEXTURE_CUBE_MAP_EXT 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP_EXT 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_EXT 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_EXT 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_EXT 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP_EXT 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_EXT 0x851C +#endif + +#ifdef GL_EXT_texture_edge_clamp +#define GL_CLAMP_TO_EDGE_EXT 0x812F +#endif + +#ifdef GL_EXT_texture_env +#define GL_TEXTURE_ENV0_EXT 0 +#define GL_TEXTURE_ENV_SHIFT_EXT 0 +#define GL_ENV_BLEND_EXT 0 +#define GL_ENV_ADD_EXT 0 +#define GL_ENV_REPLACE_EXT 0 +#define GL_ENV_SUBTRACT_EXT 0 +#define GL_TEXTURE_ENV_MODE_ALPHA_EXT 0 +#define GL_ENV_REVERSE_BLEND_EXT 0 +#define GL_ENV_REVERSE_SUBTRACT_EXT 0 +#define GL_ENV_COPY_EXT 0 +#define GL_ENV_MODULATE_EXT 0 +#endif + +#ifdef GL_EXT_texture_env_add +#endif + +#ifdef GL_EXT_texture_env_combine +#define GL_COMBINE_EXT 0x8570 +#define GL_COMBINE_RGB_EXT 0x8571 +#define GL_COMBINE_ALPHA_EXT 0x8572 +#define GL_RGB_SCALE_EXT 0x8573 +#define GL_ADD_SIGNED_EXT 0x8574 +#define GL_INTERPOLATE_EXT 0x8575 +#define GL_CONSTANT_EXT 0x8576 +#define GL_PRIMARY_COLOR_EXT 0x8577 +#define GL_PREVIOUS_EXT 0x8578 +#define GL_SOURCE0_RGB_EXT 0x8580 +#define GL_SOURCE1_RGB_EXT 0x8581 +#define GL_SOURCE2_RGB_EXT 0x8582 +#define GL_SOURCE0_ALPHA_EXT 0x8588 +#define GL_SOURCE1_ALPHA_EXT 0x8589 +#define GL_SOURCE2_ALPHA_EXT 0x858A +#define GL_OPERAND0_RGB_EXT 0x8590 +#define GL_OPERAND1_RGB_EXT 0x8591 +#define GL_OPERAND2_RGB_EXT 0x8592 +#define GL_OPERAND0_ALPHA_EXT 0x8598 +#define GL_OPERAND1_ALPHA_EXT 0x8599 +#define GL_OPERAND2_ALPHA_EXT 0x859A +#endif + +#ifdef GL_EXT_texture_env_dot3 +#define GL_DOT3_RGB_EXT 0x8740 +#define GL_DOT3_RGBA_EXT 0x8741 +#endif + +#ifdef GL_EXT_texture_filter_anisotropic +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +#endif + +#ifdef GL_EXT_texture_lod_bias +#define GL_MAX_TEXTURE_LOD_BIAS_EXT 0x84FD +#define GL_TEXTURE_FILTER_CONTROL_EXT 0x8500 +#define GL_TEXTURE_LOD_BIAS_EXT 0x8501 +#endif + +#ifdef GL_EXT_texture_mirror_clamp +#define GL_MIRROR_CLAMP_EXT 0x8742 +#define GL_MIRROR_CLAMP_TO_EDGE_EXT 0x8743 +#define GL_MIRROR_CLAMP_TO_BORDER_EXT 0x8912 +#endif + +#ifdef GL_EXT_texture_object +#define GL_TEXTURE_PRIORITY_EXT 0x8066 +#define GL_TEXTURE_RESIDENT_EXT 0x8067 +#define GL_TEXTURE_1D_BINDING_EXT 0x8068 +#define GL_TEXTURE_2D_BINDING_EXT 0x8069 +#define GL_TEXTURE_3D_BINDING_EXT 0x806A +#define glAreTexturesResidentEXT XGL_FUNCPTR(glAreTexturesResidentEXT) +#define glBindTextureEXT XGL_FUNCPTR(glBindTextureEXT) +#define glDeleteTexturesEXT XGL_FUNCPTR(glDeleteTexturesEXT) +#define glGenTexturesEXT XGL_FUNCPTR(glGenTexturesEXT) +#define glIsTextureEXT XGL_FUNCPTR(glIsTextureEXT) +#define glPrioritizeTexturesEXT XGL_FUNCPTR(glPrioritizeTexturesEXT) +#endif + +#ifdef GL_EXT_texture_perturb_normal +#define GL_PERTURB_EXT 0x85AE +#define GL_TEXTURE_NORMAL_EXT 0x85AF +#define glTextureNormalEXT XGL_FUNCPTR(glTextureNormalEXT) +#endif + +#ifdef GL_EXT_texture_rectangle +#define GL_TEXTURE_RECTANGLE_EXT 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE_EXT 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE_EXT 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE_EXT 0x84F8 +#endif + +#ifdef GL_EXT_vertex_array +#define GL_DOUBLE_EXT 0x140A +#define GL_VERTEX_ARRAY_EXT 0x8074 +#define GL_NORMAL_ARRAY_EXT 0x8075 +#define GL_COLOR_ARRAY_EXT 0x8076 +#define GL_INDEX_ARRAY_EXT 0x8077 +#define GL_TEXTURE_COORD_ARRAY_EXT 0x8078 +#define GL_EDGE_FLAG_ARRAY_EXT 0x8079 +#define GL_VERTEX_ARRAY_SIZE_EXT 0x807A +#define GL_VERTEX_ARRAY_TYPE_EXT 0x807B +#define GL_VERTEX_ARRAY_STRIDE_EXT 0x807C +#define GL_VERTEX_ARRAY_COUNT_EXT 0x807D +#define GL_NORMAL_ARRAY_TYPE_EXT 0x807E +#define GL_NORMAL_ARRAY_STRIDE_EXT 0x807F +#define GL_NORMAL_ARRAY_COUNT_EXT 0x8080 +#define GL_COLOR_ARRAY_SIZE_EXT 0x8081 +#define GL_COLOR_ARRAY_TYPE_EXT 0x8082 +#define GL_COLOR_ARRAY_STRIDE_EXT 0x8083 +#define GL_COLOR_ARRAY_COUNT_EXT 0x8084 +#define GL_INDEX_ARRAY_TYPE_EXT 0x8085 +#define GL_INDEX_ARRAY_STRIDE_EXT 0x8086 +#define GL_INDEX_ARRAY_COUNT_EXT 0x8087 +#define GL_TEXTURE_COORD_ARRAY_SIZE_EXT 0x8088 +#define GL_TEXTURE_COORD_ARRAY_TYPE_EXT 0x8089 +#define GL_TEXTURE_COORD_ARRAY_STRIDE_EXT 0x808A +#define GL_TEXTURE_COORD_ARRAY_COUNT_EXT 0x808B +#define GL_EDGE_FLAG_ARRAY_STRIDE_EXT 0x808C +#define GL_EDGE_FLAG_ARRAY_COUNT_EXT 0x808D +#define GL_VERTEX_ARRAY_POINTER_EXT 0x808E +#define GL_NORMAL_ARRAY_POINTER_EXT 0x808F +#define GL_COLOR_ARRAY_POINTER_EXT 0x8090 +#define GL_INDEX_ARRAY_POINTER_EXT 0x8091 +#define GL_TEXTURE_COORD_ARRAY_POINTER_EXT 0x8092 +#define GL_EDGE_FLAG_ARRAY_POINTER_EXT 0x8093 +#define glArrayElementEXT XGL_FUNCPTR(glArrayElementEXT) +#define glColorPointerEXT XGL_FUNCPTR(glColorPointerEXT) +#define glDrawArraysEXT XGL_FUNCPTR(glDrawArraysEXT) +#define glEdgeFlagPointerEXT XGL_FUNCPTR(glEdgeFlagPointerEXT) +#define glGetPointervEXT XGL_FUNCPTR(glGetPointervEXT) +#define glIndexPointerEXT XGL_FUNCPTR(glIndexPointerEXT) +#define glNormalPointerEXT XGL_FUNCPTR(glNormalPointerEXT) +#define glTexCoordPointerEXT XGL_FUNCPTR(glTexCoordPointerEXT) +#define glVertexPointerEXT XGL_FUNCPTR(glVertexPointerEXT) +#endif + +#ifdef GL_EXT_vertex_shader +#define GL_VERTEX_SHADER_EXT 0x8780 +#define GL_VERTEX_SHADER_BINDING_EXT 0x8781 +#define GL_OP_INDEX_EXT 0x8782 +#define GL_OP_NEGATE_EXT 0x8783 +#define GL_OP_DOT3_EXT 0x8784 +#define GL_OP_DOT4_EXT 0x8785 +#define GL_OP_MUL_EXT 0x8786 +#define GL_OP_ADD_EXT 0x8787 +#define GL_OP_MADD_EXT 0x8788 +#define GL_OP_FRAC_EXT 0x8789 +#define GL_OP_MAX_EXT 0x878A +#define GL_OP_MIN_EXT 0x878B +#define GL_OP_SET_GE_EXT 0x878C +#define GL_OP_SET_LT_EXT 0x878D +#define GL_OP_CLAMP_EXT 0x878E +#define GL_OP_FLOOR_EXT 0x878F +#define GL_OP_ROUND_EXT 0x8790 +#define GL_OP_EXP_BASE_2_EXT 0x8791 +#define GL_OP_LOG_BASE_2_EXT 0x8792 +#define GL_OP_POWER_EXT 0x8793 +#define GL_OP_RECIP_EXT 0x8794 +#define GL_OP_RECIP_SQRT_EXT 0x8795 +#define GL_OP_SUB_EXT 0x8796 +#define GL_OP_CROSS_PRODUCT_EXT 0x8797 +#define GL_OP_MULTIPLY_MATRIX_EXT 0x8798 +#define GL_OP_MOV_EXT 0x8799 +#define GL_OUTPUT_VERTEX_EXT 0x879A +#define GL_OUTPUT_COLOR0_EXT 0x879B +#define GL_OUTPUT_COLOR1_EXT 0x879C +#define GL_OUTPUT_TEXTURE_COORD0_EXT 0x879D +#define GL_OUTPUT_TEXTURE_COORD1_EXT 0x879E +#define GL_OUTPUT_TEXTURE_COORD2_EXT 0x879F +#define GL_OUTPUT_TEXTURE_COORD3_EXT 0x87A0 +#define GL_OUTPUT_TEXTURE_COORD4_EXT 0x87A1 +#define GL_OUTPUT_TEXTURE_COORD5_EXT 0x87A2 +#define GL_OUTPUT_TEXTURE_COORD6_EXT 0x87A3 +#define GL_OUTPUT_TEXTURE_COORD7_EXT 0x87A4 +#define GL_OUTPUT_TEXTURE_COORD8_EXT 0x87A5 +#define GL_OUTPUT_TEXTURE_COORD9_EXT 0x87A6 +#define GL_OUTPUT_TEXTURE_COORD10_EXT 0x87A7 +#define GL_OUTPUT_TEXTURE_COORD11_EXT 0x87A8 +#define GL_OUTPUT_TEXTURE_COORD12_EXT 0x87A9 +#define GL_OUTPUT_TEXTURE_COORD13_EXT 0x87AA +#define GL_OUTPUT_TEXTURE_COORD14_EXT 0x87AB +#define GL_OUTPUT_TEXTURE_COORD15_EXT 0x87AC +#define GL_OUTPUT_TEXTURE_COORD16_EXT 0x87AD +#define GL_OUTPUT_TEXTURE_COORD17_EXT 0x87AE +#define GL_OUTPUT_TEXTURE_COORD18_EXT 0x87AF +#define GL_OUTPUT_TEXTURE_COORD19_EXT 0x87B0 +#define GL_OUTPUT_TEXTURE_COORD20_EXT 0x87B1 +#define GL_OUTPUT_TEXTURE_COORD21_EXT 0x87B2 +#define GL_OUTPUT_TEXTURE_COORD22_EXT 0x87B3 +#define GL_OUTPUT_TEXTURE_COORD23_EXT 0x87B4 +#define GL_OUTPUT_TEXTURE_COORD24_EXT 0x87B5 +#define GL_OUTPUT_TEXTURE_COORD25_EXT 0x87B6 +#define GL_OUTPUT_TEXTURE_COORD26_EXT 0x87B7 +#define GL_OUTPUT_TEXTURE_COORD27_EXT 0x87B8 +#define GL_OUTPUT_TEXTURE_COORD28_EXT 0x87B9 +#define GL_OUTPUT_TEXTURE_COORD29_EXT 0x87BA +#define GL_OUTPUT_TEXTURE_COORD30_EXT 0x87BB +#define GL_OUTPUT_TEXTURE_COORD31_EXT 0x87BC +#define GL_OUTPUT_FOG_EXT 0x87BD +#define GL_SCALAR_EXT 0x87BE +#define GL_VECTOR_EXT 0x87BF +#define GL_MATRIX_EXT 0x87C0 +#define GL_VARIANT_EXT 0x87C1 +#define GL_INVARIANT_EXT 0x87C2 +#define GL_LOCAL_CONSTANT_EXT 0x87C3 +#define GL_LOCAL_EXT 0x87C4 +#define GL_MAX_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87C5 +#define GL_MAX_VERTEX_SHADER_VARIANTS_EXT 0x87C6 +#define GL_MAX_VERTEX_SHADER_INVARIANTS_EXT 0x87C7 +#define GL_MAX_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87C8 +#define GL_MAX_VERTEX_SHADER_LOCALS_EXT 0x87C9 +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87CA +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_VARIANTS_EXT 0x87CB +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_INVARIANTS_EXT 0x87CC +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87CD +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCALS_EXT 0x87CE +#define GL_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87CF +#define GL_VERTEX_SHADER_VARIANTS_EXT 0x87D0 +#define GL_VERTEX_SHADER_INVARIANTS_EXT 0x87D1 +#define GL_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87D2 +#define GL_VERTEX_SHADER_LOCALS_EXT 0x87D3 +#define GL_VERTEX_SHADER_OPTIMIZED_EXT 0x87D4 +#define GL_X_EXT 0x87D5 +#define GL_Y_EXT 0x87D6 +#define GL_Z_EXT 0x87D7 +#define GL_W_EXT 0x87D8 +#define GL_NEGATIVE_X_EXT 0x87D9 +#define GL_NEGATIVE_Y_EXT 0x87DA +#define GL_NEGATIVE_Z_EXT 0x87DB +#define GL_NEGATIVE_W_EXT 0x87DC +#define GL_ZERO_EXT 0x87DD +#define GL_ONE_EXT 0x87DE +#define GL_NEGATIVE_ONE_EXT 0x87DF +#define GL_NORMALIZED_RANGE_EXT 0x87E0 +#define GL_FULL_RANGE_EXT 0x87E1 +#define GL_CURRENT_VERTEX_EXT 0x87E2 +#define GL_MVP_MATRIX_EXT 0x87E3 +#define GL_VARIANT_VALUE_EXT 0x87E4 +#define GL_VARIANT_DATATYPE_EXT 0x87E5 +#define GL_VARIANT_ARRAY_STRIDE_EXT 0x87E6 +#define GL_VARIANT_ARRAY_TYPE_EXT 0x87E7 +#define GL_VARIANT_ARRAY_EXT 0x87E8 +#define GL_VARIANT_ARRAY_POINTER_EXT 0x87E9 +#define GL_INVARIANT_VALUE_EXT 0x87EA +#define GL_INVARIANT_DATATYPE_EXT 0x87EB +#define GL_LOCAL_CONSTANT_VALUE_EXT 0x87EC +#define GL_LOCAL_CONSTANT_DATATYPE_EXT 0x87ED +#define glBeginVertexShaderEXT XGL_FUNCPTR(glBeginVertexShaderEXT) +#define glEndVertexShaderEXT XGL_FUNCPTR(glEndVertexShaderEXT) +#define glBindVertexShaderEXT XGL_FUNCPTR(glBindVertexShaderEXT) +#define glGenVertexShadersEXT XGL_FUNCPTR(glGenVertexShadersEXT) +#define glDeleteVertexShaderEXT XGL_FUNCPTR(glDeleteVertexShaderEXT) +#define glShaderOp1EXT XGL_FUNCPTR(glShaderOp1EXT) +#define glShaderOp2EXT XGL_FUNCPTR(glShaderOp2EXT) +#define glShaderOp3EXT XGL_FUNCPTR(glShaderOp3EXT) +#define glSwizzleEXT XGL_FUNCPTR(glSwizzleEXT) +#define glWriteMaskEXT XGL_FUNCPTR(glWriteMaskEXT) +#define glInsertComponentEXT XGL_FUNCPTR(glInsertComponentEXT) +#define glExtractComponentEXT XGL_FUNCPTR(glExtractComponentEXT) +#define glGenSymbolsEXT XGL_FUNCPTR(glGenSymbolsEXT) +#define glSetInvariantEXT XGL_FUNCPTR(glSetInvariantEXT) +#define glSetLocalConstantEXT XGL_FUNCPTR(glSetLocalConstantEXT) +#define glVariantbvEXT XGL_FUNCPTR(glVariantbvEXT) +#define glVariantsvEXT XGL_FUNCPTR(glVariantsvEXT) +#define glVariantivEXT XGL_FUNCPTR(glVariantivEXT) +#define glVariantfvEXT XGL_FUNCPTR(glVariantfvEXT) +#define glVariantdvEXT XGL_FUNCPTR(glVariantdvEXT) +#define glVariantubvEXT XGL_FUNCPTR(glVariantubvEXT) +#define glVariantusvEXT XGL_FUNCPTR(glVariantusvEXT) +#define glVariantuivEXT XGL_FUNCPTR(glVariantuivEXT) +#define glVariantPointerEXT XGL_FUNCPTR(glVariantPointerEXT) +#define glEnableVariantClientStateEXT XGL_FUNCPTR(glEnableVariantClientStateEXT) +#define glDisableVariantClientStateEXT XGL_FUNCPTR(glDisableVariantClientStateEXT) +#define glBindLightParameterEXT XGL_FUNCPTR(glBindLightParameterEXT) +#define glBindMaterialParameterEXT XGL_FUNCPTR(glBindMaterialParameterEXT) +#define glBindTexGenParameterEXT XGL_FUNCPTR(glBindTexGenParameterEXT) +#define glBindTextureUnitParameterEXT XGL_FUNCPTR(glBindTextureUnitParameterEXT) +#define glBindParameterEXT XGL_FUNCPTR(glBindParameterEXT) +#define glIsVariantEnabledEXT XGL_FUNCPTR(glIsVariantEnabledEXT) +#define glGetVariantBooleanvEXT XGL_FUNCPTR(glGetVariantBooleanvEXT) +#define glGetVariantIntegervEXT XGL_FUNCPTR(glGetVariantIntegervEXT) +#define glGetVariantFloatvEXT XGL_FUNCPTR(glGetVariantFloatvEXT) +#define glGetVariantPointervEXT XGL_FUNCPTR(glGetVariantPointervEXT) +#define glGetInvariantBooleanvEXT XGL_FUNCPTR(glGetInvariantBooleanvEXT) +#define glGetInvariantIntegervEXT XGL_FUNCPTR(glGetInvariantIntegervEXT) +#define glGetInvariantFloatvEXT XGL_FUNCPTR(glGetInvariantFloatvEXT) +#define glGetLocalConstantBooleanvEXT XGL_FUNCPTR(glGetLocalConstantBooleanvEXT) +#define glGetLocalConstantIntegervEXT XGL_FUNCPTR(glGetLocalConstantIntegervEXT) +#define glGetLocalConstantFloatvEXT XGL_FUNCPTR(glGetLocalConstantFloatvEXT) +#endif + +#ifdef GL_EXT_vertex_weighting +#define GL_MODELVIEW0_STACK_DEPTH_EXT 0x0BA3 +#define GL_MODELVIEW0_MATRIX_EXT 0x0BA6 +#define GL_MODELVIEW0_EXT 0x1700 +#define GL_MODELVIEW1_STACK_DEPTH_EXT 0x8502 +#define GL_MODELVIEW1_MATRIX_EXT 0x8506 +#define GL_VERTEX_WEIGHTING_EXT 0x8509 +#define GL_MODELVIEW1_EXT 0x850A +#define GL_CURRENT_VERTEX_WEIGHT_EXT 0x850B +#define GL_VERTEX_WEIGHT_ARRAY_EXT 0x850C +#define GL_VERTEX_WEIGHT_ARRAY_SIZE_EXT 0x850D +#define GL_VERTEX_WEIGHT_ARRAY_TYPE_EXT 0x850E +#define GL_VERTEX_WEIGHT_ARRAY_STRIDE_EXT 0x850F +#define GL_VERTEX_WEIGHT_ARRAY_POINTER_EXT 0x8510 +#define glVertexWeightPointerEXT XGL_FUNCPTR(glVertexWeightPointerEXT) +#define glVertexWeightfEXT XGL_FUNCPTR(glVertexWeightfEXT) +#define glVertexWeightfvEXT XGL_FUNCPTR(glVertexWeightfvEXT) +#endif + +#ifdef GL_GREMEDY_string_marker +#define glStringMarkerGREMEDY XGL_FUNCPTR(glStringMarkerGREMEDY) +#endif + +#ifdef GL_HP_convolution_border_modes +#endif + +#ifdef GL_HP_image_transform +#define glGetImageTransformParameterfvHP XGL_FUNCPTR(glGetImageTransformParameterfvHP) +#define glGetImageTransformParameterivHP XGL_FUNCPTR(glGetImageTransformParameterivHP) +#define glImageTransformParameterfHP XGL_FUNCPTR(glImageTransformParameterfHP) +#define glImageTransformParameterfvHP XGL_FUNCPTR(glImageTransformParameterfvHP) +#define glImageTransformParameteriHP XGL_FUNCPTR(glImageTransformParameteriHP) +#define glImageTransformParameterivHP XGL_FUNCPTR(glImageTransformParameterivHP) +#endif + +#ifdef GL_HP_occlusion_test +#define GL_OCCLUSION_TEST_RESULT_HP 0x8166 +#define GL_OCCLUSION_TEST_HP 0x8165 +#endif + +#ifdef GL_HP_texture_lighting +#endif + +#ifdef GL_IBM_cull_vertex +#define GL_CULL_VERTEX_IBM 103050 +#endif + +#ifdef GL_IBM_multimode_draw_arrays +#define glMultiModeDrawArraysIBM XGL_FUNCPTR(glMultiModeDrawArraysIBM) +#define glMultiModeDrawElementsIBM XGL_FUNCPTR(glMultiModeDrawElementsIBM) +#endif + +#ifdef GL_IBM_rasterpos_clip +#define GL_RASTER_POSITION_UNCLIPPED_IBM 103010 +#endif + +#ifdef GL_IBM_static_data +#define GL_ALL_STATIC_DATA_IBM 103060 +#define GL_STATIC_VERTEX_ARRAY_IBM 103061 +#endif + +#ifdef GL_IBM_texture_mirrored_repeat +#define GL_MIRRORED_REPEAT_IBM 0x8370 +#endif + +#ifdef GL_IBM_vertex_array_lists +#define GL_VERTEX_ARRAY_LIST_IBM 103070 +#define GL_NORMAL_ARRAY_LIST_IBM 103071 +#define GL_COLOR_ARRAY_LIST_IBM 103072 +#define GL_INDEX_ARRAY_LIST_IBM 103073 +#define GL_TEXTURE_COORD_ARRAY_LIST_IBM 103074 +#define GL_EDGE_FLAG_ARRAY_LIST_IBM 103075 +#define GL_FOG_COORDINATE_ARRAY_LIST_IBM 103076 +#define GL_SECONDARY_COLOR_ARRAY_LIST_IBM 103077 +#define GL_VERTEX_ARRAY_LIST_STRIDE_IBM 103080 +#define GL_NORMAL_ARRAY_LIST_STRIDE_IBM 103081 +#define GL_COLOR_ARRAY_LIST_STRIDE_IBM 103082 +#define GL_INDEX_ARRAY_LIST_STRIDE_IBM 103083 +#define GL_TEXTURE_COORD_ARRAY_LIST_STRIDE_IBM 103084 +#define GL_EDGE_FLAG_ARRAY_LIST_STRIDE_IBM 103085 +#define GL_FOG_COORDINATE_ARRAY_LIST_STRIDE_IBM 103086 +#define GL_SECONDARY_COLOR_ARRAY_LIST_STRIDE_IBM 103087 +#define glColorPointerListIBM XGL_FUNCPTR(glColorPointerListIBM) +#define glEdgeFlagPointerListIBM XGL_FUNCPTR(glEdgeFlagPointerListIBM) +#define glFogCoordPointerListIBM XGL_FUNCPTR(glFogCoordPointerListIBM) +#define glIndexPointerListIBM XGL_FUNCPTR(glIndexPointerListIBM) +#define glNormalPointerListIBM XGL_FUNCPTR(glNormalPointerListIBM) +#define glSecondaryColorPointerListIBM XGL_FUNCPTR(glSecondaryColorPointerListIBM) +#define glTexCoordPointerListIBM XGL_FUNCPTR(glTexCoordPointerListIBM) +#define glVertexPointerListIBM XGL_FUNCPTR(glVertexPointerListIBM) +#endif + +#ifdef GL_INGR_color_clamp +#define GL_RED_MIN_CLAMP_INGR 0x8560 +#define GL_GREEN_MIN_CLAMP_INGR 0x8561 +#define GL_BLUE_MIN_CLAMP_INGR 0x8562 +#define GL_ALPHA_MIN_CLAMP_INGR 0x8563 +#define GL_RED_MAX_CLAMP_INGR 0x8564 +#define GL_GREEN_MAX_CLAMP_INGR 0x8565 +#define GL_BLUE_MAX_CLAMP_INGR 0x8566 +#define GL_ALPHA_MAX_CLAMP_INGR 0x8567 +#endif + +#ifdef GL_INGR_interlace_read +#define GL_INTERLACE_READ_INGR 0x8568 +#endif + +#ifdef GL_INTEL_parallel_arrays +#define GL_PARALLEL_ARRAYS_INTEL 0x83F4 +#define GL_VERTEX_ARRAY_PARALLEL_POINTERS_INTEL 0x83F5 +#define GL_NORMAL_ARRAY_PARALLEL_POINTERS_INTEL 0x83F6 +#define GL_COLOR_ARRAY_PARALLEL_POINTERS_INTEL 0x83F7 +#define GL_TEXTURE_COORD_ARRAY_PARALLEL_POINTERS_INTEL 0x83F8 +#define glColorPointervINTEL XGL_FUNCPTR(glColorPointervINTEL) +#define glNormalPointervINTEL XGL_FUNCPTR(glNormalPointervINTEL) +#define glTexCoordPointervINTEL XGL_FUNCPTR(glTexCoordPointervINTEL) +#define glVertexPointervINTEL XGL_FUNCPTR(glVertexPointervINTEL) +#endif + +#ifdef GL_INTEL_texture_scissor +#define glTexScissorFuncINTEL XGL_FUNCPTR(glTexScissorFuncINTEL) +#define glTexScissorINTEL XGL_FUNCPTR(glTexScissorINTEL) +#endif + +#ifdef GL_KTX_buffer_region +#define GL_KTX_FRONT_REGION 0x0 +#define GL_KTX_BACK_REGION 0x1 +#define GL_KTX_Z_REGION 0x2 +#define GL_KTX_STENCIL_REGION 0x3 +#define glBufferRegionEnabledEXT XGL_FUNCPTR(glBufferRegionEnabledEXT) +#define glNewBufferRegionEXT XGL_FUNCPTR(glNewBufferRegionEXT) +#define glDeleteBufferRegionEXT XGL_FUNCPTR(glDeleteBufferRegionEXT) +#define glReadBufferRegionEXT XGL_FUNCPTR(glReadBufferRegionEXT) +#define glDrawBufferRegionEXT XGL_FUNCPTR(glDrawBufferRegionEXT) +#endif + +#ifdef GL_MESA_pack_invert +#define GL_PACK_INVERT_MESA 0x8758 +#endif + +#ifdef GL_MESA_resize_buffers +#define glResizeBuffersMESA XGL_FUNCPTR(glResizeBuffersMESA) +#endif + +#ifdef GL_MESA_window_pos +#define glWindowPos2dMESA XGL_FUNCPTR(glWindowPos2dMESA) +#define glWindowPos2dvMESA XGL_FUNCPTR(glWindowPos2dvMESA) +#define glWindowPos2fMESA XGL_FUNCPTR(glWindowPos2fMESA) +#define glWindowPos2fvMESA XGL_FUNCPTR(glWindowPos2fvMESA) +#define glWindowPos2iMESA XGL_FUNCPTR(glWindowPos2iMESA) +#define glWindowPos2ivMESA XGL_FUNCPTR(glWindowPos2ivMESA) +#define glWindowPos2sMESA XGL_FUNCPTR(glWindowPos2sMESA) +#define glWindowPos2svMESA XGL_FUNCPTR(glWindowPos2svMESA) +#define glWindowPos3dMESA XGL_FUNCPTR(glWindowPos3dMESA) +#define glWindowPos3dvMESA XGL_FUNCPTR(glWindowPos3dvMESA) +#define glWindowPos3fMESA XGL_FUNCPTR(glWindowPos3fMESA) +#define glWindowPos3fvMESA XGL_FUNCPTR(glWindowPos3fvMESA) +#define glWindowPos3iMESA XGL_FUNCPTR(glWindowPos3iMESA) +#define glWindowPos3ivMESA XGL_FUNCPTR(glWindowPos3ivMESA) +#define glWindowPos3sMESA XGL_FUNCPTR(glWindowPos3sMESA) +#define glWindowPos3svMESA XGL_FUNCPTR(glWindowPos3svMESA) +#define glWindowPos4dMESA XGL_FUNCPTR(glWindowPos4dMESA) +#define glWindowPos4dvMESA XGL_FUNCPTR(glWindowPos4dvMESA) +#define glWindowPos4fMESA XGL_FUNCPTR(glWindowPos4fMESA) +#define glWindowPos4fvMESA XGL_FUNCPTR(glWindowPos4fvMESA) +#define glWindowPos4iMESA XGL_FUNCPTR(glWindowPos4iMESA) +#define glWindowPos4ivMESA XGL_FUNCPTR(glWindowPos4ivMESA) +#define glWindowPos4sMESA XGL_FUNCPTR(glWindowPos4sMESA) +#define glWindowPos4svMESA XGL_FUNCPTR(glWindowPos4svMESA) +#endif + +#ifdef GL_MESA_ycbcr_texture +#define GL_UNSIGNED_SHORT_8_8_MESA 0x85BA +#define GL_UNSIGNED_SHORT_8_8_REV_MESA 0x85BB +#define GL_YCBCR_MESA 0x8757 +#endif + +#ifdef GL_NV_blend_square +#endif + +#ifdef GL_NV_copy_depth_to_color +#define GL_DEPTH_STENCIL_TO_RGBA_NV 0x886E +#define GL_DEPTH_STENCIL_TO_BGRA_NV 0x886F +#endif + +#ifdef GL_NV_depth_clamp +#define GL_DEPTH_CLAMP_NV 0x864F +#endif + +#ifdef GL_NV_evaluators +#define GL_EVAL_2D_NV 0x86C0 +#define GL_EVAL_TRIANGULAR_2D_NV 0x86C1 +#define GL_MAP_TESSELLATION_NV 0x86C2 +#define GL_MAP_ATTRIB_U_ORDER_NV 0x86C3 +#define GL_MAP_ATTRIB_V_ORDER_NV 0x86C4 +#define GL_EVAL_FRACTIONAL_TESSELLATION_NV 0x86C5 +#define GL_EVAL_VERTEX_ATTRIB0_NV 0x86C6 +#define GL_EVAL_VERTEX_ATTRIB1_NV 0x86C7 +#define GL_EVAL_VERTEX_ATTRIB2_NV 0x86C8 +#define GL_EVAL_VERTEX_ATTRIB3_NV 0x86C9 +#define GL_EVAL_VERTEX_ATTRIB4_NV 0x86CA +#define GL_EVAL_VERTEX_ATTRIB5_NV 0x86CB +#define GL_EVAL_VERTEX_ATTRIB6_NV 0x86CC +#define GL_EVAL_VERTEX_ATTRIB7_NV 0x86CD +#define GL_EVAL_VERTEX_ATTRIB8_NV 0x86CE +#define GL_EVAL_VERTEX_ATTRIB9_NV 0x86CF +#define GL_EVAL_VERTEX_ATTRIB10_NV 0x86D0 +#define GL_EVAL_VERTEX_ATTRIB11_NV 0x86D1 +#define GL_EVAL_VERTEX_ATTRIB12_NV 0x86D2 +#define GL_EVAL_VERTEX_ATTRIB13_NV 0x86D3 +#define GL_EVAL_VERTEX_ATTRIB14_NV 0x86D4 +#define GL_EVAL_VERTEX_ATTRIB15_NV 0x86D5 +#define GL_MAX_MAP_TESSELLATION_NV 0x86D6 +#define GL_MAX_RATIONAL_EVAL_ORDER_NV 0x86D7 +#define glEvalMapsNV XGL_FUNCPTR(glEvalMapsNV) +#define glGetMapAttribParameterfvNV XGL_FUNCPTR(glGetMapAttribParameterfvNV) +#define glGetMapAttribParameterivNV XGL_FUNCPTR(glGetMapAttribParameterivNV) +#define glGetMapControlPointsNV XGL_FUNCPTR(glGetMapControlPointsNV) +#define glGetMapParameterfvNV XGL_FUNCPTR(glGetMapParameterfvNV) +#define glGetMapParameterivNV XGL_FUNCPTR(glGetMapParameterivNV) +#define glMapControlPointsNV XGL_FUNCPTR(glMapControlPointsNV) +#define glMapParameterfvNV XGL_FUNCPTR(glMapParameterfvNV) +#define glMapParameterivNV XGL_FUNCPTR(glMapParameterivNV) +#endif + +#ifdef GL_NV_fence +#define GL_ALL_COMPLETED_NV 0x84F2 +#define GL_FENCE_STATUS_NV 0x84F3 +#define GL_FENCE_CONDITION_NV 0x84F4 +#define glDeleteFencesNV XGL_FUNCPTR(glDeleteFencesNV) +#define glFinishFenceNV XGL_FUNCPTR(glFinishFenceNV) +#define glGenFencesNV XGL_FUNCPTR(glGenFencesNV) +#define glGetFenceivNV XGL_FUNCPTR(glGetFenceivNV) +#define glIsFenceNV XGL_FUNCPTR(glIsFenceNV) +#define glSetFenceNV XGL_FUNCPTR(glSetFenceNV) +#define glTestFenceNV XGL_FUNCPTR(glTestFenceNV) +#endif + +#ifdef GL_NV_float_buffer +#define GL_FLOAT_R_NV 0x8880 +#define GL_FLOAT_RG_NV 0x8881 +#define GL_FLOAT_RGB_NV 0x8882 +#define GL_FLOAT_RGBA_NV 0x8883 +#define GL_FLOAT_R16_NV 0x8884 +#define GL_FLOAT_R32_NV 0x8885 +#define GL_FLOAT_RG16_NV 0x8886 +#define GL_FLOAT_RG32_NV 0x8887 +#define GL_FLOAT_RGB16_NV 0x8888 +#define GL_FLOAT_RGB32_NV 0x8889 +#define GL_FLOAT_RGBA16_NV 0x888A +#define GL_FLOAT_RGBA32_NV 0x888B +#define GL_TEXTURE_FLOAT_COMPONENTS_NV 0x888C +#define GL_FLOAT_CLEAR_COLOR_VALUE_NV 0x888D +#define GL_FLOAT_RGBA_MODE_NV 0x888E +#endif + +#ifdef GL_NV_fog_distance +#define GL_FOG_DISTANCE_MODE_NV 0x855A +#define GL_EYE_RADIAL_NV 0x855B +#define GL_EYE_PLANE_ABSOLUTE_NV 0x855C +#endif + +#ifdef GL_NV_fragment_program +#define GL_MAX_FRAGMENT_PROGRAM_LOCAL_PARAMETERS_NV 0x8868 +#define GL_FRAGMENT_PROGRAM_NV 0x8870 +#define GL_MAX_TEXTURE_COORDS_NV 0x8871 +#define GL_MAX_TEXTURE_IMAGE_UNITS_NV 0x8872 +#define GL_FRAGMENT_PROGRAM_BINDING_NV 0x8873 +#define GL_PROGRAM_ERROR_STRING_NV 0x8874 +#define glGetProgramNamedParameterdvNV XGL_FUNCPTR(glGetProgramNamedParameterdvNV) +#define glGetProgramNamedParameterfvNV XGL_FUNCPTR(glGetProgramNamedParameterfvNV) +#define glProgramNamedParameter4dNV XGL_FUNCPTR(glProgramNamedParameter4dNV) +#define glProgramNamedParameter4dvNV XGL_FUNCPTR(glProgramNamedParameter4dvNV) +#define glProgramNamedParameter4fNV XGL_FUNCPTR(glProgramNamedParameter4fNV) +#define glProgramNamedParameter4fvNV XGL_FUNCPTR(glProgramNamedParameter4fvNV) +#endif + +#ifdef GL_NV_fragment_program2 +#define GL_MAX_PROGRAM_EXEC_INSTRUCTIONS_NV 0x88F4 +#define GL_MAX_PROGRAM_CALL_DEPTH_NV 0x88F5 +#define GL_MAX_PROGRAM_IF_DEPTH_NV 0x88F6 +#define GL_MAX_PROGRAM_LOOP_DEPTH_NV 0x88F7 +#define GL_MAX_PROGRAM_LOOP_COUNT_NV 0x88F8 +#endif + +#ifdef GL_NV_fragment_program_option +#endif + +#ifdef GL_NV_half_float +typedef unsigned short GLhalf; +#define GL_HALF_FLOAT_NV 0x140B +#define glColor3hNV XGL_FUNCPTR(glColor3hNV) +#define glColor3hvNV XGL_FUNCPTR(glColor3hvNV) +#define glColor4hNV XGL_FUNCPTR(glColor4hNV) +#define glColor4hvNV XGL_FUNCPTR(glColor4hvNV) +#define glFogCoordhNV XGL_FUNCPTR(glFogCoordhNV) +#define glFogCoordhvNV XGL_FUNCPTR(glFogCoordhvNV) +#define glMultiTexCoord1hNV XGL_FUNCPTR(glMultiTexCoord1hNV) +#define glMultiTexCoord1hvNV XGL_FUNCPTR(glMultiTexCoord1hvNV) +#define glMultiTexCoord2hNV XGL_FUNCPTR(glMultiTexCoord2hNV) +#define glMultiTexCoord2hvNV XGL_FUNCPTR(glMultiTexCoord2hvNV) +#define glMultiTexCoord3hNV XGL_FUNCPTR(glMultiTexCoord3hNV) +#define glMultiTexCoord3hvNV XGL_FUNCPTR(glMultiTexCoord3hvNV) +#define glMultiTexCoord4hNV XGL_FUNCPTR(glMultiTexCoord4hNV) +#define glMultiTexCoord4hvNV XGL_FUNCPTR(glMultiTexCoord4hvNV) +#define glNormal3hNV XGL_FUNCPTR(glNormal3hNV) +#define glNormal3hvNV XGL_FUNCPTR(glNormal3hvNV) +#define glSecondaryColor3hNV XGL_FUNCPTR(glSecondaryColor3hNV) +#define glSecondaryColor3hvNV XGL_FUNCPTR(glSecondaryColor3hvNV) +#define glTexCoord1hNV XGL_FUNCPTR(glTexCoord1hNV) +#define glTexCoord1hvNV XGL_FUNCPTR(glTexCoord1hvNV) +#define glTexCoord2hNV XGL_FUNCPTR(glTexCoord2hNV) +#define glTexCoord2hvNV XGL_FUNCPTR(glTexCoord2hvNV) +#define glTexCoord3hNV XGL_FUNCPTR(glTexCoord3hNV) +#define glTexCoord3hvNV XGL_FUNCPTR(glTexCoord3hvNV) +#define glTexCoord4hNV XGL_FUNCPTR(glTexCoord4hNV) +#define glTexCoord4hvNV XGL_FUNCPTR(glTexCoord4hvNV) +#define glVertex2hNV XGL_FUNCPTR(glVertex2hNV) +#define glVertex2hvNV XGL_FUNCPTR(glVertex2hvNV) +#define glVertex3hNV XGL_FUNCPTR(glVertex3hNV) +#define glVertex3hvNV XGL_FUNCPTR(glVertex3hvNV) +#define glVertex4hNV XGL_FUNCPTR(glVertex4hNV) +#define glVertex4hvNV XGL_FUNCPTR(glVertex4hvNV) +#define glVertexAttrib1hNV XGL_FUNCPTR(glVertexAttrib1hNV) +#define glVertexAttrib1hvNV XGL_FUNCPTR(glVertexAttrib1hvNV) +#define glVertexAttrib2hNV XGL_FUNCPTR(glVertexAttrib2hNV) +#define glVertexAttrib2hvNV XGL_FUNCPTR(glVertexAttrib2hvNV) +#define glVertexAttrib3hNV XGL_FUNCPTR(glVertexAttrib3hNV) +#define glVertexAttrib3hvNV XGL_FUNCPTR(glVertexAttrib3hvNV) +#define glVertexAttrib4hNV XGL_FUNCPTR(glVertexAttrib4hNV) +#define glVertexAttrib4hvNV XGL_FUNCPTR(glVertexAttrib4hvNV) +#define glVertexAttribs1hvNV XGL_FUNCPTR(glVertexAttribs1hvNV) +#define glVertexAttribs2hvNV XGL_FUNCPTR(glVertexAttribs2hvNV) +#define glVertexAttribs3hvNV XGL_FUNCPTR(glVertexAttribs3hvNV) +#define glVertexAttribs4hvNV XGL_FUNCPTR(glVertexAttribs4hvNV) +#define glVertexWeighthNV XGL_FUNCPTR(glVertexWeighthNV) +#define glVertexWeighthvNV XGL_FUNCPTR(glVertexWeighthvNV) +#endif + +#ifdef GL_NV_light_max_exponent +#define GL_MAX_SHININESS_NV 0x8504 +#define GL_MAX_SPOT_EXPONENT_NV 0x8505 +#endif + +#ifdef GL_NV_multisample_filter_hint +#define GL_MULTISAMPLE_FILTER_HINT_NV 0x8534 +#endif + +#ifdef GL_NV_occlusion_query +#define GL_PIXEL_COUNTER_BITS_NV 0x8864 +#define GL_CURRENT_OCCLUSION_QUERY_ID_NV 0x8865 +#define GL_PIXEL_COUNT_NV 0x8866 +#define GL_PIXEL_COUNT_AVAILABLE_NV 0x8867 +#define glBeginOcclusionQueryNV XGL_FUNCPTR(glBeginOcclusionQueryNV) +#define glDeleteOcclusionQueriesNV XGL_FUNCPTR(glDeleteOcclusionQueriesNV) +#define glEndOcclusionQueryNV XGL_FUNCPTR(glEndOcclusionQueryNV) +#define glGenOcclusionQueriesNV XGL_FUNCPTR(glGenOcclusionQueriesNV) +#define glGetOcclusionQueryivNV XGL_FUNCPTR(glGetOcclusionQueryivNV) +#define glGetOcclusionQueryuivNV XGL_FUNCPTR(glGetOcclusionQueryuivNV) +#define glIsOcclusionQueryNV XGL_FUNCPTR(glIsOcclusionQueryNV) +#endif + +#ifdef GL_NV_packed_depth_stencil +#define GL_DEPTH_STENCIL_NV 0x84F9 +#define GL_UNSIGNED_INT_24_8_NV 0x84FA +#endif + +#ifdef GL_NV_pixel_data_range +#define GL_WRITE_PIXEL_DATA_RANGE_NV 0x8878 +#define GL_READ_PIXEL_DATA_RANGE_NV 0x8879 +#define GL_WRITE_PIXEL_DATA_RANGE_LENGTH_NV 0x887A +#define GL_READ_PIXEL_DATA_RANGE_LENGTH_NV 0x887B +#define GL_WRITE_PIXEL_DATA_RANGE_POINTER_NV 0x887C +#define GL_READ_PIXEL_DATA_RANGE_POINTER_NV 0x887D +#define glFlushPixelDataRangeNV XGL_FUNCPTR(glFlushPixelDataRangeNV) +#define glPixelDataRangeNV XGL_FUNCPTR(glPixelDataRangeNV) +#endif + +#ifdef GL_NV_point_sprite +#define GL_POINT_SPRITE_NV 0x8861 +#define GL_COORD_REPLACE_NV 0x8862 +#define GL_POINT_SPRITE_R_MODE_NV 0x8863 +#define glPointParameteriNV XGL_FUNCPTR(glPointParameteriNV) +#define glPointParameterivNV XGL_FUNCPTR(glPointParameterivNV) +#endif + +#ifdef GL_NV_primitive_restart +#define GL_PRIMITIVE_RESTART_NV 0x8558 +#define GL_PRIMITIVE_RESTART_INDEX_NV 0x8559 +#define glPrimitiveRestartIndexNV XGL_FUNCPTR(glPrimitiveRestartIndexNV) +#define glPrimitiveRestartNV XGL_FUNCPTR(glPrimitiveRestartNV) +#endif + +#ifdef GL_NV_register_combiners +#define GL_REGISTER_COMBINERS_NV 0x8522 +#define GL_VARIABLE_A_NV 0x8523 +#define GL_VARIABLE_B_NV 0x8524 +#define GL_VARIABLE_C_NV 0x8525 +#define GL_VARIABLE_D_NV 0x8526 +#define GL_VARIABLE_E_NV 0x8527 +#define GL_VARIABLE_F_NV 0x8528 +#define GL_VARIABLE_G_NV 0x8529 +#define GL_CONSTANT_COLOR0_NV 0x852A +#define GL_CONSTANT_COLOR1_NV 0x852B +#define GL_PRIMARY_COLOR_NV 0x852C +#define GL_SECONDARY_COLOR_NV 0x852D +#define GL_SPARE0_NV 0x852E +#define GL_SPARE1_NV 0x852F +#define GL_DISCARD_NV 0x8530 +#define GL_E_TIMES_F_NV 0x8531 +#define GL_SPARE0_PLUS_SECONDARY_COLOR_NV 0x8532 +#define GL_UNSIGNED_IDENTITY_NV 0x8536 +#define GL_UNSIGNED_INVERT_NV 0x8537 +#define GL_EXPAND_NORMAL_NV 0x8538 +#define GL_EXPAND_NEGATE_NV 0x8539 +#define GL_HALF_BIAS_NORMAL_NV 0x853A +#define GL_HALF_BIAS_NEGATE_NV 0x853B +#define GL_SIGNED_IDENTITY_NV 0x853C +#define GL_SIGNED_NEGATE_NV 0x853D +#define GL_SCALE_BY_TWO_NV 0x853E +#define GL_SCALE_BY_FOUR_NV 0x853F +#define GL_SCALE_BY_ONE_HALF_NV 0x8540 +#define GL_BIAS_BY_NEGATIVE_ONE_HALF_NV 0x8541 +#define GL_COMBINER_INPUT_NV 0x8542 +#define GL_COMBINER_MAPPING_NV 0x8543 +#define GL_COMBINER_COMPONENT_USAGE_NV 0x8544 +#define GL_COMBINER_AB_DOT_PRODUCT_NV 0x8545 +#define GL_COMBINER_CD_DOT_PRODUCT_NV 0x8546 +#define GL_COMBINER_MUX_SUM_NV 0x8547 +#define GL_COMBINER_SCALE_NV 0x8548 +#define GL_COMBINER_BIAS_NV 0x8549 +#define GL_COMBINER_AB_OUTPUT_NV 0x854A +#define GL_COMBINER_CD_OUTPUT_NV 0x854B +#define GL_COMBINER_SUM_OUTPUT_NV 0x854C +#define GL_MAX_GENERAL_COMBINERS_NV 0x854D +#define GL_NUM_GENERAL_COMBINERS_NV 0x854E +#define GL_COLOR_SUM_CLAMP_NV 0x854F +#define GL_COMBINER0_NV 0x8550 +#define GL_COMBINER1_NV 0x8551 +#define GL_COMBINER2_NV 0x8552 +#define GL_COMBINER3_NV 0x8553 +#define GL_COMBINER4_NV 0x8554 +#define GL_COMBINER5_NV 0x8555 +#define GL_COMBINER6_NV 0x8556 +#define GL_COMBINER7_NV 0x8557 +#define glCombinerInputNV XGL_FUNCPTR(glCombinerInputNV) +#define glCombinerOutputNV XGL_FUNCPTR(glCombinerOutputNV) +#define glCombinerParameterfNV XGL_FUNCPTR(glCombinerParameterfNV) +#define glCombinerParameterfvNV XGL_FUNCPTR(glCombinerParameterfvNV) +#define glCombinerParameteriNV XGL_FUNCPTR(glCombinerParameteriNV) +#define glCombinerParameterivNV XGL_FUNCPTR(glCombinerParameterivNV) +#define glFinalCombinerInputNV XGL_FUNCPTR(glFinalCombinerInputNV) +#define glGetCombinerInputParameterfvNV XGL_FUNCPTR(glGetCombinerInputParameterfvNV) +#define glGetCombinerInputParameterivNV XGL_FUNCPTR(glGetCombinerInputParameterivNV) +#define glGetCombinerOutputParameterfvNV XGL_FUNCPTR(glGetCombinerOutputParameterfvNV) +#define glGetCombinerOutputParameterivNV XGL_FUNCPTR(glGetCombinerOutputParameterivNV) +#define glGetFinalCombinerInputParameterfvNV XGL_FUNCPTR(glGetFinalCombinerInputParameterfvNV) +#define glGetFinalCombinerInputParameterivNV XGL_FUNCPTR(glGetFinalCombinerInputParameterivNV) +#endif + +#ifdef GL_NV_register_combiners2 +#define GL_PER_STAGE_CONSTANTS_NV 0x8535 +#define glCombinerStageParameterfvNV XGL_FUNCPTR(glCombinerStageParameterfvNV) +#define glGetCombinerStageParameterfvNV XGL_FUNCPTR(glGetCombinerStageParameterfvNV) +#endif + +#ifdef GL_NV_texgen_emboss +#define GL_EMBOSS_LIGHT_NV 0x855D +#define GL_EMBOSS_CONSTANT_NV 0x855E +#define GL_EMBOSS_MAP_NV 0x855F +#endif + +#ifdef GL_NV_texgen_reflection +#define GL_NORMAL_MAP_NV 0x8511 +#define GL_REFLECTION_MAP_NV 0x8512 +#endif + +#ifdef GL_NV_texture_compression_vtc +#endif + +#ifdef GL_NV_texture_env_combine4 +#define GL_COMBINE4_NV 0x8503 +#define GL_SOURCE3_RGB_NV 0x8583 +#define GL_SOURCE3_ALPHA_NV 0x858B +#define GL_OPERAND3_RGB_NV 0x8593 +#define GL_OPERAND3_ALPHA_NV 0x859B +#endif + +#ifdef GL_NV_texture_expand_normal +#define GL_TEXTURE_UNSIGNED_REMAP_MODE_NV 0x888F +#endif + +#ifdef GL_NV_texture_rectangle +#define GL_TEXTURE_RECTANGLE_NV 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE_NV 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE_NV 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE_NV 0x84F8 +#endif + +#ifdef GL_NV_texture_shader +#define GL_OFFSET_TEXTURE_RECTANGLE_NV 0x864C +#define GL_OFFSET_TEXTURE_RECTANGLE_SCALE_NV 0x864D +#define GL_DOT_PRODUCT_TEXTURE_RECTANGLE_NV 0x864E +#define GL_RGBA_UNSIGNED_DOT_PRODUCT_MAPPING_NV 0x86D9 +#define GL_UNSIGNED_INT_S8_S8_8_8_NV 0x86DA +#define GL_UNSIGNED_INT_8_8_S8_S8_REV_NV 0x86DB +#define GL_DSDT_MAG_INTENSITY_NV 0x86DC +#define GL_SHADER_CONSISTENT_NV 0x86DD +#define GL_TEXTURE_SHADER_NV 0x86DE +#define GL_SHADER_OPERATION_NV 0x86DF +#define GL_CULL_MODES_NV 0x86E0 +#define GL_OFFSET_TEXTURE_MATRIX_NV 0x86E1 +#define GL_OFFSET_TEXTURE_SCALE_NV 0x86E2 +#define GL_OFFSET_TEXTURE_BIAS_NV 0x86E3 +#define GL_PREVIOUS_TEXTURE_INPUT_NV 0x86E4 +#define GL_CONST_EYE_NV 0x86E5 +#define GL_PASS_THROUGH_NV 0x86E6 +#define GL_CULL_FRAGMENT_NV 0x86E7 +#define GL_OFFSET_TEXTURE_2D_NV 0x86E8 +#define GL_DEPENDENT_AR_TEXTURE_2D_NV 0x86E9 +#define GL_DEPENDENT_GB_TEXTURE_2D_NV 0x86EA +#define GL_DOT_PRODUCT_NV 0x86EC +#define GL_DOT_PRODUCT_DEPTH_REPLACE_NV 0x86ED +#define GL_DOT_PRODUCT_TEXTURE_2D_NV 0x86EE +#define GL_DOT_PRODUCT_TEXTURE_CUBE_MAP_NV 0x86F0 +#define GL_DOT_PRODUCT_DIFFUSE_CUBE_MAP_NV 0x86F1 +#define GL_DOT_PRODUCT_REFLECT_CUBE_MAP_NV 0x86F2 +#define GL_DOT_PRODUCT_CONST_EYE_REFLECT_CUBE_MAP_NV 0x86F3 +#define GL_HILO_NV 0x86F4 +#define GL_DSDT_NV 0x86F5 +#define GL_DSDT_MAG_NV 0x86F6 +#define GL_DSDT_MAG_VIB_NV 0x86F7 +#define GL_HILO16_NV 0x86F8 +#define GL_SIGNED_HILO_NV 0x86F9 +#define GL_SIGNED_HILO16_NV 0x86FA +#define GL_SIGNED_RGBA_NV 0x86FB +#define GL_SIGNED_RGBA8_NV 0x86FC +#define GL_SIGNED_RGB_NV 0x86FE +#define GL_SIGNED_RGB8_NV 0x86FF +#define GL_SIGNED_LUMINANCE_NV 0x8701 +#define GL_SIGNED_LUMINANCE8_NV 0x8702 +#define GL_SIGNED_LUMINANCE_ALPHA_NV 0x8703 +#define GL_SIGNED_LUMINANCE8_ALPHA8_NV 0x8704 +#define GL_SIGNED_ALPHA_NV 0x8705 +#define GL_SIGNED_ALPHA8_NV 0x8706 +#define GL_SIGNED_INTENSITY_NV 0x8707 +#define GL_SIGNED_INTENSITY8_NV 0x8708 +#define GL_DSDT8_NV 0x8709 +#define GL_DSDT8_MAG8_NV 0x870A +#define GL_DSDT8_MAG8_INTENSITY8_NV 0x870B +#define GL_SIGNED_RGB_UNSIGNED_ALPHA_NV 0x870C +#define GL_SIGNED_RGB8_UNSIGNED_ALPHA8_NV 0x870D +#define GL_HI_SCALE_NV 0x870E +#define GL_LO_SCALE_NV 0x870F +#define GL_DS_SCALE_NV 0x8710 +#define GL_DT_SCALE_NV 0x8711 +#define GL_MAGNITUDE_SCALE_NV 0x8712 +#define GL_VIBRANCE_SCALE_NV 0x8713 +#define GL_HI_BIAS_NV 0x8714 +#define GL_LO_BIAS_NV 0x8715 +#define GL_DS_BIAS_NV 0x8716 +#define GL_DT_BIAS_NV 0x8717 +#define GL_MAGNITUDE_BIAS_NV 0x8718 +#define GL_VIBRANCE_BIAS_NV 0x8719 +#define GL_TEXTURE_BORDER_VALUES_NV 0x871A +#define GL_TEXTURE_HI_SIZE_NV 0x871B +#define GL_TEXTURE_LO_SIZE_NV 0x871C +#define GL_TEXTURE_DS_SIZE_NV 0x871D +#define GL_TEXTURE_DT_SIZE_NV 0x871E +#define GL_TEXTURE_MAG_SIZE_NV 0x871F +#endif + +#ifdef GL_NV_texture_shader2 +#define GL_UNSIGNED_INT_S8_S8_8_8_NV 0x86DA +#define GL_UNSIGNED_INT_8_8_S8_S8_REV_NV 0x86DB +#define GL_DSDT_MAG_INTENSITY_NV 0x86DC +#define GL_DOT_PRODUCT_TEXTURE_3D_NV 0x86EF +#define GL_HILO_NV 0x86F4 +#define GL_DSDT_NV 0x86F5 +#define GL_DSDT_MAG_NV 0x86F6 +#define GL_DSDT_MAG_VIB_NV 0x86F7 +#define GL_HILO16_NV 0x86F8 +#define GL_SIGNED_HILO_NV 0x86F9 +#define GL_SIGNED_HILO16_NV 0x86FA +#define GL_SIGNED_RGBA_NV 0x86FB +#define GL_SIGNED_RGBA8_NV 0x86FC +#define GL_SIGNED_RGB_NV 0x86FE +#define GL_SIGNED_RGB8_NV 0x86FF +#define GL_SIGNED_LUMINANCE_NV 0x8701 +#define GL_SIGNED_LUMINANCE8_NV 0x8702 +#define GL_SIGNED_LUMINANCE_ALPHA_NV 0x8703 +#define GL_SIGNED_LUMINANCE8_ALPHA8_NV 0x8704 +#define GL_SIGNED_ALPHA_NV 0x8705 +#define GL_SIGNED_ALPHA8_NV 0x8706 +#define GL_SIGNED_INTENSITY_NV 0x8707 +#define GL_SIGNED_INTENSITY8_NV 0x8708 +#define GL_DSDT8_NV 0x8709 +#define GL_DSDT8_MAG8_NV 0x870A +#define GL_DSDT8_MAG8_INTENSITY8_NV 0x870B +#define GL_SIGNED_RGB_UNSIGNED_ALPHA_NV 0x870C +#define GL_SIGNED_RGB8_UNSIGNED_ALPHA8_NV 0x870D +#endif + +#ifdef GL_NV_texture_shader3 +#define GL_OFFSET_PROJECTIVE_TEXTURE_2D_NV 0x8850 +#define GL_OFFSET_PROJECTIVE_TEXTURE_2D_SCALE_NV 0x8851 +#define GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_NV 0x8852 +#define GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_SCALE_NV 0x8853 +#define GL_OFFSET_HILO_TEXTURE_2D_NV 0x8854 +#define GL_OFFSET_HILO_TEXTURE_RECTANGLE_NV 0x8855 +#define GL_OFFSET_HILO_PROJECTIVE_TEXTURE_2D_NV 0x8856 +#define GL_OFFSET_HILO_PROJECTIVE_TEXTURE_RECTANGLE_NV 0x8857 +#define GL_DEPENDENT_HILO_TEXTURE_2D_NV 0x8858 +#define GL_DEPENDENT_RGB_TEXTURE_3D_NV 0x8859 +#define GL_DEPENDENT_RGB_TEXTURE_CUBE_MAP_NV 0x885A +#define GL_DOT_PRODUCT_PASS_THROUGH_NV 0x885B +#define GL_DOT_PRODUCT_TEXTURE_1D_NV 0x885C +#define GL_DOT_PRODUCT_AFFINE_DEPTH_REPLACE_NV 0x885D +#define GL_HILO8_NV 0x885E +#define GL_SIGNED_HILO8_NV 0x885F +#define GL_FORCE_BLUE_TO_ONE_NV 0x8860 +#endif + +#ifdef GL_NV_vertex_array_range +#define GL_VERTEX_ARRAY_RANGE_NV 0x851D +#define GL_VERTEX_ARRAY_RANGE_LENGTH_NV 0x851E +#define GL_VERTEX_ARRAY_RANGE_VALID_NV 0x851F +#define GL_MAX_VERTEX_ARRAY_RANGE_ELEMENT_NV 0x8520 +#define GL_VERTEX_ARRAY_RANGE_POINTER_NV 0x8521 +#define glFlushVertexArrayRangeNV XGL_FUNCPTR(glFlushVertexArrayRangeNV) +#define glVertexArrayRangeNV XGL_FUNCPTR(glVertexArrayRangeNV) +#endif + +#ifdef GL_NV_vertex_array_range2 +#define GL_VERTEX_ARRAY_RANGE_WITHOUT_FLUSH_NV 0x8533 +#endif + +#ifdef GL_NV_vertex_program +#define GL_VERTEX_PROGRAM_NV 0x8620 +#define GL_VERTEX_STATE_PROGRAM_NV 0x8621 +#define GL_ATTRIB_ARRAY_SIZE_NV 0x8623 +#define GL_ATTRIB_ARRAY_STRIDE_NV 0x8624 +#define GL_ATTRIB_ARRAY_TYPE_NV 0x8625 +#define GL_CURRENT_ATTRIB_NV 0x8626 +#define GL_PROGRAM_LENGTH_NV 0x8627 +#define GL_PROGRAM_STRING_NV 0x8628 +#define GL_MODELVIEW_PROJECTION_NV 0x8629 +#define GL_IDENTITY_NV 0x862A +#define GL_INVERSE_NV 0x862B +#define GL_TRANSPOSE_NV 0x862C +#define GL_INVERSE_TRANSPOSE_NV 0x862D +#define GL_MAX_TRACK_MATRIX_STACK_DEPTH_NV 0x862E +#define GL_MAX_TRACK_MATRICES_NV 0x862F +#define GL_MATRIX0_NV 0x8630 +#define GL_MATRIX1_NV 0x8631 +#define GL_MATRIX2_NV 0x8632 +#define GL_MATRIX3_NV 0x8633 +#define GL_MATRIX4_NV 0x8634 +#define GL_MATRIX5_NV 0x8635 +#define GL_MATRIX6_NV 0x8636 +#define GL_MATRIX7_NV 0x8637 +#define GL_CURRENT_MATRIX_STACK_DEPTH_NV 0x8640 +#define GL_CURRENT_MATRIX_NV 0x8641 +#define GL_VERTEX_PROGRAM_POINT_SIZE_NV 0x8642 +#define GL_VERTEX_PROGRAM_TWO_SIDE_NV 0x8643 +#define GL_PROGRAM_PARAMETER_NV 0x8644 +#define GL_ATTRIB_ARRAY_POINTER_NV 0x8645 +#define GL_PROGRAM_TARGET_NV 0x8646 +#define GL_PROGRAM_RESIDENT_NV 0x8647 +#define GL_TRACK_MATRIX_NV 0x8648 +#define GL_TRACK_MATRIX_TRANSFORM_NV 0x8649 +#define GL_VERTEX_PROGRAM_BINDING_NV 0x864A +#define GL_PROGRAM_ERROR_POSITION_NV 0x864B +#define GL_VERTEX_ATTRIB_ARRAY0_NV 0x8650 +#define GL_VERTEX_ATTRIB_ARRAY1_NV 0x8651 +#define GL_VERTEX_ATTRIB_ARRAY2_NV 0x8652 +#define GL_VERTEX_ATTRIB_ARRAY3_NV 0x8653 +#define GL_VERTEX_ATTRIB_ARRAY4_NV 0x8654 +#define GL_VERTEX_ATTRIB_ARRAY5_NV 0x8655 +#define GL_VERTEX_ATTRIB_ARRAY6_NV 0x8656 +#define GL_VERTEX_ATTRIB_ARRAY7_NV 0x8657 +#define GL_VERTEX_ATTRIB_ARRAY8_NV 0x8658 +#define GL_VERTEX_ATTRIB_ARRAY9_NV 0x8659 +#define GL_VERTEX_ATTRIB_ARRAY10_NV 0x865A +#define GL_VERTEX_ATTRIB_ARRAY11_NV 0x865B +#define GL_VERTEX_ATTRIB_ARRAY12_NV 0x865C +#define GL_VERTEX_ATTRIB_ARRAY13_NV 0x865D +#define GL_VERTEX_ATTRIB_ARRAY14_NV 0x865E +#define GL_VERTEX_ATTRIB_ARRAY15_NV 0x865F +#define GL_MAP1_VERTEX_ATTRIB0_4_NV 0x8660 +#define GL_MAP1_VERTEX_ATTRIB1_4_NV 0x8661 +#define GL_MAP1_VERTEX_ATTRIB2_4_NV 0x8662 +#define GL_MAP1_VERTEX_ATTRIB3_4_NV 0x8663 +#define GL_MAP1_VERTEX_ATTRIB4_4_NV 0x8664 +#define GL_MAP1_VERTEX_ATTRIB5_4_NV 0x8665 +#define GL_MAP1_VERTEX_ATTRIB6_4_NV 0x8666 +#define GL_MAP1_VERTEX_ATTRIB7_4_NV 0x8667 +#define GL_MAP1_VERTEX_ATTRIB8_4_NV 0x8668 +#define GL_MAP1_VERTEX_ATTRIB9_4_NV 0x8669 +#define GL_MAP1_VERTEX_ATTRIB10_4_NV 0x866A +#define GL_MAP1_VERTEX_ATTRIB11_4_NV 0x866B +#define GL_MAP1_VERTEX_ATTRIB12_4_NV 0x866C +#define GL_MAP1_VERTEX_ATTRIB13_4_NV 0x866D +#define GL_MAP1_VERTEX_ATTRIB14_4_NV 0x866E +#define GL_MAP1_VERTEX_ATTRIB15_4_NV 0x866F +#define GL_MAP2_VERTEX_ATTRIB0_4_NV 0x8670 +#define GL_MAP2_VERTEX_ATTRIB1_4_NV 0x8671 +#define GL_MAP2_VERTEX_ATTRIB2_4_NV 0x8672 +#define GL_MAP2_VERTEX_ATTRIB3_4_NV 0x8673 +#define GL_MAP2_VERTEX_ATTRIB4_4_NV 0x8674 +#define GL_MAP2_VERTEX_ATTRIB5_4_NV 0x8675 +#define GL_MAP2_VERTEX_ATTRIB6_4_NV 0x8676 +#define GL_MAP2_VERTEX_ATTRIB7_4_NV 0x8677 +#define GL_MAP2_VERTEX_ATTRIB8_4_NV 0x8678 +#define GL_MAP2_VERTEX_ATTRIB9_4_NV 0x8679 +#define GL_MAP2_VERTEX_ATTRIB10_4_NV 0x867A +#define GL_MAP2_VERTEX_ATTRIB11_4_NV 0x867B +#define GL_MAP2_VERTEX_ATTRIB12_4_NV 0x867C +#define GL_MAP2_VERTEX_ATTRIB13_4_NV 0x867D +#define GL_MAP2_VERTEX_ATTRIB14_4_NV 0x867E +#define GL_MAP2_VERTEX_ATTRIB15_4_NV 0x867F +#define glAreProgramsResidentNV XGL_FUNCPTR(glAreProgramsResidentNV) +#define glBindProgramNV XGL_FUNCPTR(glBindProgramNV) +#define glDeleteProgramsNV XGL_FUNCPTR(glDeleteProgramsNV) +#define glExecuteProgramNV XGL_FUNCPTR(glExecuteProgramNV) +#define glGenProgramsNV XGL_FUNCPTR(glGenProgramsNV) +#define glGetProgramParameterdvNV XGL_FUNCPTR(glGetProgramParameterdvNV) +#define glGetProgramParameterfvNV XGL_FUNCPTR(glGetProgramParameterfvNV) +#define glGetProgramStringNV XGL_FUNCPTR(glGetProgramStringNV) +#define glGetProgramivNV XGL_FUNCPTR(glGetProgramivNV) +#define glGetTrackMatrixivNV XGL_FUNCPTR(glGetTrackMatrixivNV) +#define glGetVertexAttribPointervNV XGL_FUNCPTR(glGetVertexAttribPointervNV) +#define glGetVertexAttribdvNV XGL_FUNCPTR(glGetVertexAttribdvNV) +#define glGetVertexAttribfvNV XGL_FUNCPTR(glGetVertexAttribfvNV) +#define glGetVertexAttribivNV XGL_FUNCPTR(glGetVertexAttribivNV) +#define glIsProgramNV XGL_FUNCPTR(glIsProgramNV) +#define glLoadProgramNV XGL_FUNCPTR(glLoadProgramNV) +#define glProgramParameter4dNV XGL_FUNCPTR(glProgramParameter4dNV) +#define glProgramParameter4dvNV XGL_FUNCPTR(glProgramParameter4dvNV) +#define glProgramParameter4fNV XGL_FUNCPTR(glProgramParameter4fNV) +#define glProgramParameter4fvNV XGL_FUNCPTR(glProgramParameter4fvNV) +#define glProgramParameters4dvNV XGL_FUNCPTR(glProgramParameters4dvNV) +#define glProgramParameters4fvNV XGL_FUNCPTR(glProgramParameters4fvNV) +#define glRequestResidentProgramsNV XGL_FUNCPTR(glRequestResidentProgramsNV) +#define glTrackMatrixNV XGL_FUNCPTR(glTrackMatrixNV) +#define glVertexAttrib1dNV XGL_FUNCPTR(glVertexAttrib1dNV) +#define glVertexAttrib1dvNV XGL_FUNCPTR(glVertexAttrib1dvNV) +#define glVertexAttrib1fNV XGL_FUNCPTR(glVertexAttrib1fNV) +#define glVertexAttrib1fvNV XGL_FUNCPTR(glVertexAttrib1fvNV) +#define glVertexAttrib1sNV XGL_FUNCPTR(glVertexAttrib1sNV) +#define glVertexAttrib1svNV XGL_FUNCPTR(glVertexAttrib1svNV) +#define glVertexAttrib2dNV XGL_FUNCPTR(glVertexAttrib2dNV) +#define glVertexAttrib2dvNV XGL_FUNCPTR(glVertexAttrib2dvNV) +#define glVertexAttrib2fNV XGL_FUNCPTR(glVertexAttrib2fNV) +#define glVertexAttrib2fvNV XGL_FUNCPTR(glVertexAttrib2fvNV) +#define glVertexAttrib2sNV XGL_FUNCPTR(glVertexAttrib2sNV) +#define glVertexAttrib2svNV XGL_FUNCPTR(glVertexAttrib2svNV) +#define glVertexAttrib3dNV XGL_FUNCPTR(glVertexAttrib3dNV) +#define glVertexAttrib3dvNV XGL_FUNCPTR(glVertexAttrib3dvNV) +#define glVertexAttrib3fNV XGL_FUNCPTR(glVertexAttrib3fNV) +#define glVertexAttrib3fvNV XGL_FUNCPTR(glVertexAttrib3fvNV) +#define glVertexAttrib3sNV XGL_FUNCPTR(glVertexAttrib3sNV) +#define glVertexAttrib3svNV XGL_FUNCPTR(glVertexAttrib3svNV) +#define glVertexAttrib4dNV XGL_FUNCPTR(glVertexAttrib4dNV) +#define glVertexAttrib4dvNV XGL_FUNCPTR(glVertexAttrib4dvNV) +#define glVertexAttrib4fNV XGL_FUNCPTR(glVertexAttrib4fNV) +#define glVertexAttrib4fvNV XGL_FUNCPTR(glVertexAttrib4fvNV) +#define glVertexAttrib4sNV XGL_FUNCPTR(glVertexAttrib4sNV) +#define glVertexAttrib4svNV XGL_FUNCPTR(glVertexAttrib4svNV) +#define glVertexAttrib4ubNV XGL_FUNCPTR(glVertexAttrib4ubNV) +#define glVertexAttrib4ubvNV XGL_FUNCPTR(glVertexAttrib4ubvNV) +#define glVertexAttribPointerNV XGL_FUNCPTR(glVertexAttribPointerNV) +#define glVertexAttribs1dvNV XGL_FUNCPTR(glVertexAttribs1dvNV) +#define glVertexAttribs1fvNV XGL_FUNCPTR(glVertexAttribs1fvNV) +#define glVertexAttribs1svNV XGL_FUNCPTR(glVertexAttribs1svNV) +#define glVertexAttribs2dvNV XGL_FUNCPTR(glVertexAttribs2dvNV) +#define glVertexAttribs2fvNV XGL_FUNCPTR(glVertexAttribs2fvNV) +#define glVertexAttribs2svNV XGL_FUNCPTR(glVertexAttribs2svNV) +#define glVertexAttribs3dvNV XGL_FUNCPTR(glVertexAttribs3dvNV) +#define glVertexAttribs3fvNV XGL_FUNCPTR(glVertexAttribs3fvNV) +#define glVertexAttribs3svNV XGL_FUNCPTR(glVertexAttribs3svNV) +#define glVertexAttribs4dvNV XGL_FUNCPTR(glVertexAttribs4dvNV) +#define glVertexAttribs4fvNV XGL_FUNCPTR(glVertexAttribs4fvNV) +#define glVertexAttribs4svNV XGL_FUNCPTR(glVertexAttribs4svNV) +#define glVertexAttribs4ubvNV XGL_FUNCPTR(glVertexAttribs4ubvNV) +#endif + +#ifdef GL_NV_vertex_program1_1 +#endif + +#ifdef GL_NV_vertex_program2 +#endif + +#ifdef GL_NV_vertex_program2_option +#define GL_MAX_PROGRAM_EXEC_INSTRUCTIONS_NV 0x88F4 +#define GL_MAX_PROGRAM_CALL_DEPTH_NV 0x88F5 +#endif + +#ifdef GL_NV_vertex_program3 +#define MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB 0x8B4C +#endif + +#ifdef GL_OML_interlace +#define GL_INTERLACE_OML 0x8980 +#define GL_INTERLACE_READ_OML 0x8981 +#endif + +#ifdef GL_OML_resample +#define GL_PACK_RESAMPLE_OML 0x8984 +#define GL_UNPACK_RESAMPLE_OML 0x8985 +#define GL_RESAMPLE_REPLICATE_OML 0x8986 +#define GL_RESAMPLE_ZERO_FILL_OML 0x8987 +#define GL_RESAMPLE_AVERAGE_OML 0x8988 +#define GL_RESAMPLE_DECIMATE_OML 0x8989 +#endif + +#ifdef GL_OML_subsample +#define GL_FORMAT_SUBSAMPLE_24_24_OML 0x8982 +#define GL_FORMAT_SUBSAMPLE_244_244_OML 0x8983 +#endif + +#ifdef GL_PGI_misc_hints +#define GL_PREFER_DOUBLEBUFFER_HINT_PGI 107000 +#define GL_CONSERVE_MEMORY_HINT_PGI 107005 +#define GL_RECLAIM_MEMORY_HINT_PGI 107006 +#define GL_NATIVE_GRAPHICS_HANDLE_PGI 107010 +#define GL_NATIVE_GRAPHICS_BEGIN_HINT_PGI 107011 +#define GL_NATIVE_GRAPHICS_END_HINT_PGI 107012 +#define GL_ALWAYS_FAST_HINT_PGI 107020 +#define GL_ALWAYS_SOFT_HINT_PGI 107021 +#define GL_ALLOW_DRAW_OBJ_HINT_PGI 107022 +#define GL_ALLOW_DRAW_WIN_HINT_PGI 107023 +#define GL_ALLOW_DRAW_FRG_HINT_PGI 107024 +#define GL_ALLOW_DRAW_MEM_HINT_PGI 107025 +#define GL_STRICT_DEPTHFUNC_HINT_PGI 107030 +#define GL_STRICT_LIGHTING_HINT_PGI 107031 +#define GL_STRICT_SCISSOR_HINT_PGI 107032 +#define GL_FULL_STIPPLE_HINT_PGI 107033 +#define GL_CLIP_NEAR_HINT_PGI 107040 +#define GL_CLIP_FAR_HINT_PGI 107041 +#define GL_WIDE_LINE_HINT_PGI 107042 +#define GL_BACK_NORMALS_HINT_PGI 107043 +#endif + +#ifdef GL_PGI_vertex_hints +#define GL_VERTEX23_BIT_PGI 0x00000004 +#define GL_VERTEX4_BIT_PGI 0x00000008 +#define GL_COLOR3_BIT_PGI 0x00010000 +#define GL_COLOR4_BIT_PGI 0x00020000 +#define GL_EDGEFLAG_BIT_PGI 0x00040000 +#define GL_INDEX_BIT_PGI 0x00080000 +#define GL_MAT_AMBIENT_BIT_PGI 0x00100000 +#define GL_VERTEX_DATA_HINT_PGI 107050 +#define GL_VERTEX_CONSISTENT_HINT_PGI 107051 +#define GL_MATERIAL_SIDE_HINT_PGI 107052 +#define GL_MAX_VERTEX_HINT_PGI 107053 +#define GL_MAT_AMBIENT_AND_DIFFUSE_BIT_PGI 0x00200000 +#define GL_MAT_DIFFUSE_BIT_PGI 0x00400000 +#define GL_MAT_EMISSION_BIT_PGI 0x00800000 +#define GL_MAT_COLOR_INDEXES_BIT_PGI 0x01000000 +#define GL_MAT_SHININESS_BIT_PGI 0x02000000 +#define GL_MAT_SPECULAR_BIT_PGI 0x04000000 +#define GL_NORMAL_BIT_PGI 0x08000000 +#define GL_TEXCOORD1_BIT_PGI 0x10000000 +#define GL_TEXCOORD2_BIT_PGI 0x20000000 +#define GL_TEXCOORD3_BIT_PGI 0x40000000 +#define GL_TEXCOORD4_BIT_PGI 0x80000000 +#endif + +#ifdef GL_REND_screen_coordinates +#define GL_SCREEN_COORDINATES_REND 0x8490 +#define GL_INVERTED_SCREEN_W_REND 0x8491 +#endif + +#ifdef GL_S3_s3tc +#define GL_RGB_S3TC 0x83A0 +#define GL_RGB4_S3TC 0x83A1 +#define GL_RGBA_S3TC 0x83A2 +#define GL_RGBA4_S3TC 0x83A3 +#define GL_RGBA_DXT5_S3TC 0x83A4 +#define GL_RGBA4_DXT5_S3TC 0x83A5 +#endif + +#ifdef GL_SGIS_color_range +#define GL_EXTENDED_RANGE_SGIS 0x85A5 +#define GL_MIN_RED_SGIS 0x85A6 +#define GL_MAX_RED_SGIS 0x85A7 +#define GL_MIN_GREEN_SGIS 0x85A8 +#define GL_MAX_GREEN_SGIS 0x85A9 +#define GL_MIN_BLUE_SGIS 0x85AA +#define GL_MAX_BLUE_SGIS 0x85AB +#define GL_MIN_ALPHA_SGIS 0x85AC +#define GL_MAX_ALPHA_SGIS 0x85AD +#endif + +#ifdef GL_SGIS_detail_texture +#define glDetailTexFuncSGIS XGL_FUNCPTR(glDetailTexFuncSGIS) +#define glGetDetailTexFuncSGIS XGL_FUNCPTR(glGetDetailTexFuncSGIS) +#endif + +#ifdef GL_SGIS_fog_function +#define glFogFuncSGIS XGL_FUNCPTR(glFogFuncSGIS) +#define glGetFogFuncSGIS XGL_FUNCPTR(glGetFogFuncSGIS) +#endif + +#ifdef GL_SGIS_generate_mipmap +#define GL_GENERATE_MIPMAP_SGIS 0x8191 +#define GL_GENERATE_MIPMAP_HINT_SGIS 0x8192 +#endif + +#ifdef GL_SGIS_multisample +#define GL_MULTISAMPLE_SGIS 0x809D +#define GL_SAMPLE_ALPHA_TO_MASK_SGIS 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_SGIS 0x809F +#define GL_SAMPLE_MASK_SGIS 0x80A0 +#define GL_1PASS_SGIS 0x80A1 +#define GL_2PASS_0_SGIS 0x80A2 +#define GL_2PASS_1_SGIS 0x80A3 +#define GL_4PASS_0_SGIS 0x80A4 +#define GL_4PASS_1_SGIS 0x80A5 +#define GL_4PASS_2_SGIS 0x80A6 +#define GL_4PASS_3_SGIS 0x80A7 +#define GL_SAMPLE_BUFFERS_SGIS 0x80A8 +#define GL_SAMPLES_SGIS 0x80A9 +#define GL_SAMPLE_MASK_VALUE_SGIS 0x80AA +#define GL_SAMPLE_MASK_INVERT_SGIS 0x80AB +#define GL_SAMPLE_PATTERN_SGIS 0x80AC +#define GL_MULTISAMPLE_BIT_EXT 0x20000000 +#define glSampleMaskSGIS XGL_FUNCPTR(glSampleMaskSGIS) +#define glSamplePatternSGIS XGL_FUNCPTR(glSamplePatternSGIS) +#endif + +#ifdef GL_SGIS_pixel_texture +#endif + +#ifdef GL_SGIS_sharpen_texture +#define glGetSharpenTexFuncSGIS XGL_FUNCPTR(glGetSharpenTexFuncSGIS) +#define glSharpenTexFuncSGIS XGL_FUNCPTR(glSharpenTexFuncSGIS) +#endif + +#ifdef GL_SGIS_texture4D +#define glTexImage4DSGIS XGL_FUNCPTR(glTexImage4DSGIS) +#define glTexSubImage4DSGIS XGL_FUNCPTR(glTexSubImage4DSGIS) +#endif + +#ifdef GL_SGIS_texture_border_clamp +#define GL_CLAMP_TO_BORDER_SGIS 0x812D +#endif + +#ifdef GL_SGIS_texture_edge_clamp +#define GL_CLAMP_TO_EDGE_SGIS 0x812F +#endif + +#ifdef GL_SGIS_texture_filter4 +#define glGetTexFilterFuncSGIS XGL_FUNCPTR(glGetTexFilterFuncSGIS) +#define glTexFilterFuncSGIS XGL_FUNCPTR(glTexFilterFuncSGIS) +#endif + +#ifdef GL_SGIS_texture_lod +#define GL_TEXTURE_MIN_LOD_SGIS 0x813A +#define GL_TEXTURE_MAX_LOD_SGIS 0x813B +#define GL_TEXTURE_BASE_LEVEL_SGIS 0x813C +#define GL_TEXTURE_MAX_LEVEL_SGIS 0x813D +#endif + +#ifdef GL_SGIS_texture_select +#endif + +#ifdef GL_SGIX_async +#define GL_ASYNC_MARKER_SGIX 0x8329 +#define glAsyncMarkerSGIX XGL_FUNCPTR(glAsyncMarkerSGIX) +#define glDeleteAsyncMarkersSGIX XGL_FUNCPTR(glDeleteAsyncMarkersSGIX) +#define glFinishAsyncSGIX XGL_FUNCPTR(glFinishAsyncSGIX) +#define glGenAsyncMarkersSGIX XGL_FUNCPTR(glGenAsyncMarkersSGIX) +#define glIsAsyncMarkerSGIX XGL_FUNCPTR(glIsAsyncMarkerSGIX) +#define glPollAsyncSGIX XGL_FUNCPTR(glPollAsyncSGIX) +#endif + +#ifdef GL_SGIX_async_histogram +#define GL_ASYNC_HISTOGRAM_SGIX 0x832C +#define GL_MAX_ASYNC_HISTOGRAM_SGIX 0x832D +#endif + +#ifdef GL_SGIX_async_pixel +#define GL_ASYNC_TEX_IMAGE_SGIX 0x835C +#define GL_ASYNC_DRAW_PIXELS_SGIX 0x835D +#define GL_ASYNC_READ_PIXELS_SGIX 0x835E +#define GL_MAX_ASYNC_TEX_IMAGE_SGIX 0x835F +#define GL_MAX_ASYNC_DRAW_PIXELS_SGIX 0x8360 +#define GL_MAX_ASYNC_READ_PIXELS_SGIX 0x8361 +#endif + +#ifdef GL_SGIX_blend_alpha_minmax +#define GL_ALPHA_MIN_SGIX 0x8320 +#define GL_ALPHA_MAX_SGIX 0x8321 +#endif + +#ifdef GL_SGIX_clipmap +#endif + +#ifdef GL_SGIX_depth_texture +#define GL_DEPTH_COMPONENT16_SGIX 0x81A5 +#define GL_DEPTH_COMPONENT24_SGIX 0x81A6 +#define GL_DEPTH_COMPONENT32_SGIX 0x81A7 +#endif + +#ifdef GL_SGIX_flush_raster +#define glFlushRasterSGIX XGL_FUNCPTR(glFlushRasterSGIX) +#endif + +#ifdef GL_SGIX_fog_offset +#define GL_FOG_OFFSET_SGIX 0x8198 +#define GL_FOG_OFFSET_VALUE_SGIX 0x8199 +#endif + +#ifdef GL_SGIX_fog_texture +#define GL_TEXTURE_FOG_SGIX 0 +#define GL_FOG_PATCHY_FACTOR_SGIX 0 +#define GL_FRAGMENT_FOG_SGIX 0 +#define glTextureFogSGIX XGL_FUNCPTR(glTextureFogSGIX) +#endif + +#ifdef GL_SGIX_fragment_specular_lighting +#define glFragmentColorMaterialSGIX XGL_FUNCPTR(glFragmentColorMaterialSGIX) +#define glFragmentLightModelfSGIX XGL_FUNCPTR(glFragmentLightModelfSGIX) +#define glFragmentLightModelfvSGIX XGL_FUNCPTR(glFragmentLightModelfvSGIX) +#define glFragmentLightModeliSGIX XGL_FUNCPTR(glFragmentLightModeliSGIX) +#define glFragmentLightModelivSGIX XGL_FUNCPTR(glFragmentLightModelivSGIX) +#define glFragmentLightfSGIX XGL_FUNCPTR(glFragmentLightfSGIX) +#define glFragmentLightfvSGIX XGL_FUNCPTR(glFragmentLightfvSGIX) +#define glFragmentLightiSGIX XGL_FUNCPTR(glFragmentLightiSGIX) +#define glFragmentLightivSGIX XGL_FUNCPTR(glFragmentLightivSGIX) +#define glFragmentMaterialfSGIX XGL_FUNCPTR(glFragmentMaterialfSGIX) +#define glFragmentMaterialfvSGIX XGL_FUNCPTR(glFragmentMaterialfvSGIX) +#define glFragmentMaterialiSGIX XGL_FUNCPTR(glFragmentMaterialiSGIX) +#define glFragmentMaterialivSGIX XGL_FUNCPTR(glFragmentMaterialivSGIX) +#define glGetFragmentLightfvSGIX XGL_FUNCPTR(glGetFragmentLightfvSGIX) +#define glGetFragmentLightivSGIX XGL_FUNCPTR(glGetFragmentLightivSGIX) +#define glGetFragmentMaterialfvSGIX XGL_FUNCPTR(glGetFragmentMaterialfvSGIX) +#define glGetFragmentMaterialivSGIX XGL_FUNCPTR(glGetFragmentMaterialivSGIX) +#endif + +#ifdef GL_SGIX_framezoom +#define glFrameZoomSGIX XGL_FUNCPTR(glFrameZoomSGIX) +#endif + +#ifdef GL_SGIX_interlace +#define GL_INTERLACE_SGIX 0x8094 +#endif + +#ifdef GL_SGIX_ir_instrument1 +#endif + +#ifdef GL_SGIX_list_priority +#endif + +#ifdef GL_SGIX_pixel_texture +#define glPixelTexGenSGIX XGL_FUNCPTR(glPixelTexGenSGIX) +#endif + +#ifdef GL_SGIX_pixel_texture_bits +#endif + +#ifdef GL_SGIX_reference_plane +#define glReferencePlaneSGIX XGL_FUNCPTR(glReferencePlaneSGIX) +#endif + +#ifdef GL_SGIX_resample +#define GL_PACK_RESAMPLE_SGIX 0x842E +#define GL_UNPACK_RESAMPLE_SGIX 0x842F +#define GL_RESAMPLE_DECIMATE_SGIX 0x8430 +#define GL_RESAMPLE_REPLICATE_SGIX 0x8433 +#define GL_RESAMPLE_ZERO_FILL_SGIX 0x8434 +#endif + +#ifdef GL_SGIX_shadow +#define GL_TEXTURE_COMPARE_SGIX 0x819A +#define GL_TEXTURE_COMPARE_OPERATOR_SGIX 0x819B +#define GL_TEXTURE_LEQUAL_R_SGIX 0x819C +#define GL_TEXTURE_GEQUAL_R_SGIX 0x819D +#endif + +#ifdef GL_SGIX_shadow_ambient +#define GL_SHADOW_AMBIENT_SGIX 0x80BF +#endif + +#ifdef GL_SGIX_sprite +#define glSpriteParameterfSGIX XGL_FUNCPTR(glSpriteParameterfSGIX) +#define glSpriteParameterfvSGIX XGL_FUNCPTR(glSpriteParameterfvSGIX) +#define glSpriteParameteriSGIX XGL_FUNCPTR(glSpriteParameteriSGIX) +#define glSpriteParameterivSGIX XGL_FUNCPTR(glSpriteParameterivSGIX) +#endif + +#ifdef GL_SGIX_tag_sample_buffer +#define glTagSampleBufferSGIX XGL_FUNCPTR(glTagSampleBufferSGIX) +#endif + +#ifdef GL_SGIX_texture_add_env +#endif + +#ifdef GL_SGIX_texture_coordinate_clamp +#define GL_TEXTURE_MAX_CLAMP_S_SGIX 0x8369 +#define GL_TEXTURE_MAX_CLAMP_T_SGIX 0x836A +#define GL_TEXTURE_MAX_CLAMP_R_SGIX 0x836B +#endif + +#ifdef GL_SGIX_texture_lod_bias +#endif + +#ifdef GL_SGIX_texture_multi_buffer +#define GL_TEXTURE_MULTI_BUFFER_HINT_SGIX 0x812E +#endif + +#ifdef GL_SGIX_texture_range +#define GL_RGB_SIGNED_SGIX 0x85E0 +#define GL_RGBA_SIGNED_SGIX 0x85E1 +#define GL_ALPHA_SIGNED_SGIX 0x85E2 +#define GL_LUMINANCE_SIGNED_SGIX 0x85E3 +#define GL_INTENSITY_SIGNED_SGIX 0x85E4 +#define GL_LUMINANCE_ALPHA_SIGNED_SGIX 0x85E5 +#define GL_RGB16_SIGNED_SGIX 0x85E6 +#define GL_RGBA16_SIGNED_SGIX 0x85E7 +#define GL_ALPHA16_SIGNED_SGIX 0x85E8 +#define GL_LUMINANCE16_SIGNED_SGIX 0x85E9 +#define GL_INTENSITY16_SIGNED_SGIX 0x85EA +#define GL_LUMINANCE16_ALPHA16_SIGNED_SGIX 0x85EB +#define GL_RGB_EXTENDED_RANGE_SGIX 0x85EC +#define GL_RGBA_EXTENDED_RANGE_SGIX 0x85ED +#define GL_ALPHA_EXTENDED_RANGE_SGIX 0x85EE +#define GL_LUMINANCE_EXTENDED_RANGE_SGIX 0x85EF +#define GL_INTENSITY_EXTENDED_RANGE_SGIX 0x85F0 +#define GL_LUMINANCE_ALPHA_EXTENDED_RANGE_SGIX 0x85F1 +#define GL_RGB16_EXTENDED_RANGE_SGIX 0x85F2 +#define GL_RGBA16_EXTENDED_RANGE_SGIX 0x85F3 +#define GL_ALPHA16_EXTENDED_RANGE_SGIX 0x85F4 +#define GL_LUMINANCE16_EXTENDED_RANGE_SGIX 0x85F5 +#define GL_INTENSITY16_EXTENDED_RANGE_SGIX 0x85F6 +#define GL_LUMINANCE16_ALPHA16_EXTENDED_RANGE_SGIX 0x85F7 +#define GL_MIN_LUMINANCE_SGIS 0x85F8 +#define GL_MAX_LUMINANCE_SGIS 0x85F9 +#define GL_MIN_INTENSITY_SGIS 0x85FA +#define GL_MAX_INTENSITY_SGIS 0x85FB +#endif + +#ifdef GL_SGIX_texture_scale_bias +#define GL_POST_TEXTURE_FILTER_BIAS_SGIX 0x8179 +#define GL_POST_TEXTURE_FILTER_SCALE_SGIX 0x817A +#define GL_POST_TEXTURE_FILTER_BIAS_RANGE_SGIX 0x817B +#define GL_POST_TEXTURE_FILTER_SCALE_RANGE_SGIX 0x817C +#endif + +#ifdef GL_SGIX_vertex_preclip +#define GL_VERTEX_PRECLIP_SGIX 0x83EE +#define GL_VERTEX_PRECLIP_HINT_SGIX 0x83EF +#endif + +#ifdef GL_SGIX_vertex_preclip_hint +#define GL_VERTEX_PRECLIP_SGIX 0x83EE +#define GL_VERTEX_PRECLIP_HINT_SGIX 0x83EF +#endif + +#ifdef GL_SGIX_ycrcb +#endif + +#ifdef GL_SGI_color_matrix +#define GL_COLOR_MATRIX_SGI 0x80B1 +#define GL_COLOR_MATRIX_STACK_DEPTH_SGI 0x80B2 +#define GL_MAX_COLOR_MATRIX_STACK_DEPTH_SGI 0x80B3 +#define GL_POST_COLOR_MATRIX_RED_SCALE_SGI 0x80B4 +#define GL_POST_COLOR_MATRIX_GREEN_SCALE_SGI 0x80B5 +#define GL_POST_COLOR_MATRIX_BLUE_SCALE_SGI 0x80B6 +#define GL_POST_COLOR_MATRIX_ALPHA_SCALE_SGI 0x80B7 +#define GL_POST_COLOR_MATRIX_RED_BIAS_SGI 0x80B8 +#define GL_POST_COLOR_MATRIX_GREEN_BIAS_SGI 0x80B9 +#define GL_POST_COLOR_MATRIX_BLUE_BIAS_SGI 0x80BA +#define GL_POST_COLOR_MATRIX_ALPHA_BIAS_SGI 0x80BB +#endif + +#ifdef GL_SGI_color_table +#define GL_COLOR_TABLE_SGI 0x80D0 +#define GL_POST_CONVOLUTION_COLOR_TABLE_SGI 0x80D1 +#define GL_POST_COLOR_MATRIX_COLOR_TABLE_SGI 0x80D2 +#define GL_PROXY_COLOR_TABLE_SGI 0x80D3 +#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE_SGI 0x80D4 +#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE_SGI 0x80D5 +#define GL_COLOR_TABLE_SCALE_SGI 0x80D6 +#define GL_COLOR_TABLE_BIAS_SGI 0x80D7 +#define GL_COLOR_TABLE_FORMAT_SGI 0x80D8 +#define GL_COLOR_TABLE_WIDTH_SGI 0x80D9 +#define GL_COLOR_TABLE_RED_SIZE_SGI 0x80DA +#define GL_COLOR_TABLE_GREEN_SIZE_SGI 0x80DB +#define GL_COLOR_TABLE_BLUE_SIZE_SGI 0x80DC +#define GL_COLOR_TABLE_ALPHA_SIZE_SGI 0x80DD +#define GL_COLOR_TABLE_LUMINANCE_SIZE_SGI 0x80DE +#define GL_COLOR_TABLE_INTENSITY_SIZE_SGI 0x80DF +#define glColorTableParameterfvSGI XGL_FUNCPTR(glColorTableParameterfvSGI) +#define glColorTableParameterivSGI XGL_FUNCPTR(glColorTableParameterivSGI) +#define glColorTableSGI XGL_FUNCPTR(glColorTableSGI) +#define glCopyColorTableSGI XGL_FUNCPTR(glCopyColorTableSGI) +#define glGetColorTableParameterfvSGI XGL_FUNCPTR(glGetColorTableParameterfvSGI) +#define glGetColorTableParameterivSGI XGL_FUNCPTR(glGetColorTableParameterivSGI) +#define glGetColorTableSGI XGL_FUNCPTR(glGetColorTableSGI) +#endif + +#ifdef GL_SGI_texture_color_table +#define GL_TEXTURE_COLOR_TABLE_SGI 0x80BC +#define GL_PROXY_TEXTURE_COLOR_TABLE_SGI 0x80BD +#endif + +#ifdef GL_SUNX_constant_data +#define GL_UNPACK_CONSTANT_DATA_SUNX 0x81D5 +#define GL_TEXTURE_CONSTANT_DATA_SUNX 0x81D6 +#define glFinishTextureSUNX XGL_FUNCPTR(glFinishTextureSUNX) +#endif + +#ifdef GL_SUN_convolution_border_modes +#define GL_WRAP_BORDER_SUN 0x81D4 +#endif + +#ifdef GL_SUN_global_alpha +#define GL_GLOBAL_ALPHA_SUN 0x81D9 +#define GL_GLOBAL_ALPHA_FACTOR_SUN 0x81DA +#define glGlobalAlphaFactorbSUN XGL_FUNCPTR(glGlobalAlphaFactorbSUN) +#define glGlobalAlphaFactordSUN XGL_FUNCPTR(glGlobalAlphaFactordSUN) +#define glGlobalAlphaFactorfSUN XGL_FUNCPTR(glGlobalAlphaFactorfSUN) +#define glGlobalAlphaFactoriSUN XGL_FUNCPTR(glGlobalAlphaFactoriSUN) +#define glGlobalAlphaFactorsSUN XGL_FUNCPTR(glGlobalAlphaFactorsSUN) +#define glGlobalAlphaFactorubSUN XGL_FUNCPTR(glGlobalAlphaFactorubSUN) +#define glGlobalAlphaFactoruiSUN XGL_FUNCPTR(glGlobalAlphaFactoruiSUN) +#define glGlobalAlphaFactorusSUN XGL_FUNCPTR(glGlobalAlphaFactorusSUN) +#endif + +#ifdef GL_SUN_mesh_array +#define GL_QUAD_MESH_SUN 0x8614 +#define GL_TRIANGLE_MESH_SUN 0x8615 +#endif + +#ifdef GL_SUN_read_video_pixels +#define glReadVideoPixelsSUN XGL_FUNCPTR(glReadVideoPixelsSUN) +#endif + +#ifdef GL_SUN_slice_accum +#define GL_SLICE_ACCUM_SUN 0x85CC +#endif + +#ifdef GL_SUN_triangle_list +#define GL_RESTART_SUN 0x01 +#define GL_REPLACE_MIDDLE_SUN 0x02 +#define GL_REPLACE_OLDEST_SUN 0x03 +#define GL_TRIANGLE_LIST_SUN 0x81D7 +#define GL_REPLACEMENT_CODE_SUN 0x81D8 +#define GL_REPLACEMENT_CODE_ARRAY_SUN 0x85C0 +#define GL_REPLACEMENT_CODE_ARRAY_TYPE_SUN 0x85C1 +#define GL_REPLACEMENT_CODE_ARRAY_STRIDE_SUN 0x85C2 +#define GL_REPLACEMENT_CODE_ARRAY_POINTER_SUN 0x85C3 +#define GL_R1UI_V3F_SUN 0x85C4 +#define GL_R1UI_C4UB_V3F_SUN 0x85C5 +#define GL_R1UI_C3F_V3F_SUN 0x85C6 +#define GL_R1UI_N3F_V3F_SUN 0x85C7 +#define GL_R1UI_C4F_N3F_V3F_SUN 0x85C8 +#define GL_R1UI_T2F_V3F_SUN 0x85C9 +#define GL_R1UI_T2F_N3F_V3F_SUN 0x85CA +#define GL_R1UI_T2F_C4F_N3F_V3F_SUN 0x85CB +#define glReplacementCodePointerSUN XGL_FUNCPTR(glReplacementCodePointerSUN) +#define glReplacementCodeubSUN XGL_FUNCPTR(glReplacementCodeubSUN) +#define glReplacementCodeubvSUN XGL_FUNCPTR(glReplacementCodeubvSUN) +#define glReplacementCodeuiSUN XGL_FUNCPTR(glReplacementCodeuiSUN) +#define glReplacementCodeuivSUN XGL_FUNCPTR(glReplacementCodeuivSUN) +#define glReplacementCodeusSUN XGL_FUNCPTR(glReplacementCodeusSUN) +#define glReplacementCodeusvSUN XGL_FUNCPTR(glReplacementCodeusvSUN) +#endif + +#ifdef GL_SUN_vertex +#define glColor3fVertex3fSUN XGL_FUNCPTR(glColor3fVertex3fSUN) +#define glColor3fVertex3fvSUN XGL_FUNCPTR(glColor3fVertex3fvSUN) +#define glColor4fNormal3fVertex3fSUN XGL_FUNCPTR(glColor4fNormal3fVertex3fSUN) +#define glColor4fNormal3fVertex3fvSUN XGL_FUNCPTR(glColor4fNormal3fVertex3fvSUN) +#define glColor4ubVertex2fSUN XGL_FUNCPTR(glColor4ubVertex2fSUN) +#define glColor4ubVertex2fvSUN XGL_FUNCPTR(glColor4ubVertex2fvSUN) +#define glColor4ubVertex3fSUN XGL_FUNCPTR(glColor4ubVertex3fSUN) +#define glColor4ubVertex3fvSUN XGL_FUNCPTR(glColor4ubVertex3fvSUN) +#define glNormal3fVertex3fSUN XGL_FUNCPTR(glNormal3fVertex3fSUN) +#define glNormal3fVertex3fvSUN XGL_FUNCPTR(glNormal3fVertex3fvSUN) +#define glReplacementCodeuiColor3fVertex3fSUN XGL_FUNCPTR(glReplacementCodeuiColor3fVertex3fSUN) +#define glReplacementCodeuiColor3fVertex3fvSUN XGL_FUNCPTR(glReplacementCodeuiColor3fVertex3fvSUN) +#define glReplacementCodeuiColor4fNormal3fVertex3fSUN XGL_FUNCPTR(glReplacementCodeuiColor4fNormal3fVertex3fSUN) +#define glReplacementCodeuiColor4fNormal3fVertex3fvSUN XGL_FUNCPTR(glReplacementCodeuiColor4fNormal3fVertex3fvSUN) +#define glReplacementCodeuiColor4ubVertex3fSUN XGL_FUNCPTR(glReplacementCodeuiColor4ubVertex3fSUN) +#define glReplacementCodeuiColor4ubVertex3fvSUN XGL_FUNCPTR(glReplacementCodeuiColor4ubVertex3fvSUN) +#define glReplacementCodeuiNormal3fVertex3fSUN XGL_FUNCPTR(glReplacementCodeuiNormal3fVertex3fSUN) +#define glReplacementCodeuiNormal3fVertex3fvSUN XGL_FUNCPTR(glReplacementCodeuiNormal3fVertex3fvSUN) +#define glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN XGL_FUNCPTR(glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN) +#define glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN XGL_FUNCPTR(glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN) +#define glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN XGL_FUNCPTR(glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN) +#define glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN XGL_FUNCPTR(glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN) +#define glReplacementCodeuiTexCoord2fVertex3fSUN XGL_FUNCPTR(glReplacementCodeuiTexCoord2fVertex3fSUN) +#define glReplacementCodeuiTexCoord2fVertex3fvSUN XGL_FUNCPTR(glReplacementCodeuiTexCoord2fVertex3fvSUN) +#define glReplacementCodeuiVertex3fSUN XGL_FUNCPTR(glReplacementCodeuiVertex3fSUN) +#define glReplacementCodeuiVertex3fvSUN XGL_FUNCPTR(glReplacementCodeuiVertex3fvSUN) +#define glTexCoord2fColor3fVertex3fSUN XGL_FUNCPTR(glTexCoord2fColor3fVertex3fSUN) +#define glTexCoord2fColor3fVertex3fvSUN XGL_FUNCPTR(glTexCoord2fColor3fVertex3fvSUN) +#define glTexCoord2fColor4fNormal3fVertex3fSUN XGL_FUNCPTR(glTexCoord2fColor4fNormal3fVertex3fSUN) +#define glTexCoord2fColor4fNormal3fVertex3fvSUN XGL_FUNCPTR(glTexCoord2fColor4fNormal3fVertex3fvSUN) +#define glTexCoord2fColor4ubVertex3fSUN XGL_FUNCPTR(glTexCoord2fColor4ubVertex3fSUN) +#define glTexCoord2fColor4ubVertex3fvSUN XGL_FUNCPTR(glTexCoord2fColor4ubVertex3fvSUN) +#define glTexCoord2fNormal3fVertex3fSUN XGL_FUNCPTR(glTexCoord2fNormal3fVertex3fSUN) +#define glTexCoord2fNormal3fVertex3fvSUN XGL_FUNCPTR(glTexCoord2fNormal3fVertex3fvSUN) +#define glTexCoord2fVertex3fSUN XGL_FUNCPTR(glTexCoord2fVertex3fSUN) +#define glTexCoord2fVertex3fvSUN XGL_FUNCPTR(glTexCoord2fVertex3fvSUN) +#define glTexCoord4fColor4fNormal3fVertex4fSUN XGL_FUNCPTR(glTexCoord4fColor4fNormal3fVertex4fSUN) +#define glTexCoord4fColor4fNormal3fVertex4fvSUN XGL_FUNCPTR(glTexCoord4fColor4fNormal3fVertex4fvSUN) +#define glTexCoord4fVertex4fSUN XGL_FUNCPTR(glTexCoord4fVertex4fSUN) +#define glTexCoord4fVertex4fvSUN XGL_FUNCPTR(glTexCoord4fVertex4fvSUN) +#endif + +#ifdef GL_WIN_phong_shading +#define GL_PHONG_WIN 0x80EA +#define GL_PHONG_HINT_WIN 0x80EB +#endif + +#ifdef GL_WIN_specular_fog +#define GL_FOG_SPECULAR_TEXTURE_WIN 0x80EC +#endif + +#ifdef GL_WIN_swap_hint +#define glAddSwapHintRectWIN XGL_FUNCPTR(glAddSwapHintRectWIN) +#endif + + diff --git a/Engine/source/gfx/gl/ggl/generated/glefn.h b/Engine/source/gfx/gl/ggl/generated/glefn.h new file mode 100644 index 000000000..cedceeb5d --- /dev/null +++ b/Engine/source/gfx/gl/ggl/generated/glefn.h @@ -0,0 +1,2399 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef GL_VERSION_1_2 +GL_GROUP_BEGIN(GL_VERSION_1_2) +GL_FUNCTION(glDrawRangeElements ,void, (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices)) +GL_FUNCTION(glTexImage3D, void, (GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels)) +GL_FUNCTION(glTexSubImage3D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels)) +GL_FUNCTION(glCopyTexSubImage3D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height)) +GL_GROUP_END() +#endif + +#ifdef GL_VERSION_1_3 +GL_GROUP_BEGIN(GL_VERSION_1_3) +GL_FUNCTION(glActiveTexture,void,(GLenum texture)) +GL_FUNCTION(glClientActiveTexture,void,(GLenum texture)) +GL_FUNCTION(glCompressedTexImage1D,void,(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid *data)) +GL_FUNCTION(glCompressedTexImage2D,void,(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data)) +GL_FUNCTION(glCompressedTexImage3D,void,(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data)) +GL_FUNCTION(glCompressedTexSubImage1D,void,(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *data)) +GL_FUNCTION(glCompressedTexSubImage2D,void,(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data)) +GL_FUNCTION(glCompressedTexSubImage3D,void,(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *data)) +GL_FUNCTION(glGetCompressedTexImage,void,(GLenum target, GLint lod, GLvoid *img)) +GL_FUNCTION(glLoadTransposeMatrixd,void,(const GLdouble m[16])) +GL_FUNCTION(glLoadTransposeMatrixf,void,(const GLfloat m[16])) +GL_FUNCTION(glMultTransposeMatrixd,void,(const GLdouble m[16])) +GL_FUNCTION(glMultTransposeMatrixf,void,(const GLfloat m[16])) +GL_FUNCTION(glMultiTexCoord1d,void,(GLenum target, GLdouble s)) +GL_FUNCTION(glMultiTexCoord1dv,void,(GLenum target, const GLdouble *v)) +GL_FUNCTION(glMultiTexCoord1f,void,(GLenum target, GLfloat s)) +GL_FUNCTION(glMultiTexCoord1fv,void,(GLenum target, const GLfloat *v)) +GL_FUNCTION(glMultiTexCoord1i,void,(GLenum target, GLint s)) +GL_FUNCTION(glMultiTexCoord1iv,void,(GLenum target, const GLint *v)) +GL_FUNCTION(glMultiTexCoord1s,void,(GLenum target, GLshort s)) +GL_FUNCTION(glMultiTexCoord1sv,void,(GLenum target, const GLshort *v)) +GL_FUNCTION(glMultiTexCoord2d,void,(GLenum target, GLdouble s, GLdouble t)) +GL_FUNCTION(glMultiTexCoord2dv,void,(GLenum target, const GLdouble *v)) +GL_FUNCTION(glMultiTexCoord2f,void,(GLenum target, GLfloat s, GLfloat t)) +GL_FUNCTION(glMultiTexCoord2fv,void,(GLenum target, const GLfloat *v)) +GL_FUNCTION(glMultiTexCoord2i,void,(GLenum target, GLint s, GLint t)) +GL_FUNCTION(glMultiTexCoord2iv,void,(GLenum target, const GLint *v)) +GL_FUNCTION(glMultiTexCoord2s,void,(GLenum target, GLshort s, GLshort t)) +GL_FUNCTION(glMultiTexCoord2sv,void,(GLenum target, const GLshort *v)) +GL_FUNCTION(glMultiTexCoord3d,void,(GLenum target, GLdouble s, GLdouble t, GLdouble r)) +GL_FUNCTION(glMultiTexCoord3dv,void,(GLenum target, const GLdouble *v)) +GL_FUNCTION(glMultiTexCoord3f,void,(GLenum target, GLfloat s, GLfloat t, GLfloat r)) +GL_FUNCTION(glMultiTexCoord3fv,void,(GLenum target, const GLfloat *v)) +GL_FUNCTION(glMultiTexCoord3i,void,(GLenum target, GLint s, GLint t, GLint r)) +GL_FUNCTION(glMultiTexCoord3iv,void,(GLenum target, const GLint *v)) +GL_FUNCTION(glMultiTexCoord3s,void,(GLenum target, GLshort s, GLshort t, GLshort r)) +GL_FUNCTION(glMultiTexCoord3sv,void,(GLenum target, const GLshort *v)) +GL_FUNCTION(glMultiTexCoord4d,void,(GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q)) +GL_FUNCTION(glMultiTexCoord4dv,void,(GLenum target, const GLdouble *v)) +GL_FUNCTION(glMultiTexCoord4f,void,(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q)) +GL_FUNCTION(glMultiTexCoord4fv,void,(GLenum target, const GLfloat *v)) +GL_FUNCTION(glMultiTexCoord4i,void,(GLenum target, GLint s, GLint t, GLint r, GLint q)) +GL_FUNCTION(glMultiTexCoord4iv,void,(GLenum target, const GLint *v)) +GL_FUNCTION(glMultiTexCoord4s,void,(GLenum target, GLshort s, GLshort t, GLshort r, GLshort q)) +GL_FUNCTION(glMultiTexCoord4sv,void,(GLenum target, const GLshort *v)) +GL_FUNCTION(glSampleCoverage,void,(GLclampf value, GLboolean invert)) +GL_GROUP_END() +#endif + +#ifdef GL_VERSION_1_4 +GL_GROUP_BEGIN(GL_VERSION_1_4) +GL_FUNCTION(glBlendEquation,void,(GLenum mode)) +GL_FUNCTION(glBlendColor,void,(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)) +GL_FUNCTION(glFogCoordf,void,(GLfloat coord)) +GL_FUNCTION(glFogCoordfv,void,(const GLfloat *coord)) +GL_FUNCTION(glFogCoordd,void,(GLdouble coord)) +GL_FUNCTION(glFogCoorddv,void,(const GLdouble *coord)) +GL_FUNCTION(glFogCoordPointer,void,(GLenum type, GLsizei stride, const GLvoid *pointer)) +GL_FUNCTION(glMultiDrawArrays,void,(GLenum mode, GLint *first, GLsizei *count, GLsizei primcount)) +GL_FUNCTION(glMultiDrawElements,void,(GLenum mode, GLsizei *count, GLenum type, const GLvoid **indices, GLsizei primcount)) +GL_FUNCTION(glPointParameterf,void,(GLenum pname, GLfloat param)) +GL_FUNCTION(glPointParameterfv,void,(GLenum pname, GLfloat *params)) +GL_FUNCTION(glSecondaryColor3b,void,(GLbyte red, GLbyte green, GLbyte blue)) +GL_FUNCTION(glSecondaryColor3bv,void,(const GLbyte *v)) +GL_FUNCTION(glSecondaryColor3d,void,(GLdouble red, GLdouble green, GLdouble blue)) +GL_FUNCTION(glSecondaryColor3dv,void,(const GLdouble *v)) +GL_FUNCTION(glSecondaryColor3f,void,(GLfloat red, GLfloat green, GLfloat blue)) +GL_FUNCTION(glSecondaryColor3fv,void,(const GLfloat *v)) +GL_FUNCTION(glSecondaryColor3i,void,(GLint red, GLint green, GLint blue)) +GL_FUNCTION(glSecondaryColor3iv,void,(const GLint *v)) +GL_FUNCTION(glSecondaryColor3s,void,(GLshort red, GLshort green, GLshort blue)) +GL_FUNCTION(glSecondaryColor3sv,void,(const GLshort *v)) +GL_FUNCTION(glSecondaryColor3ub,void,(GLubyte red, GLubyte green, GLubyte blue)) +GL_FUNCTION(glSecondaryColor3ubv,void,(const GLubyte *v)) +GL_FUNCTION(glSecondaryColor3ui,void,(GLuint red, GLuint green, GLuint blue)) +GL_FUNCTION(glSecondaryColor3uiv,void,(const GLuint *v)) +GL_FUNCTION(glSecondaryColor3us,void,(GLushort red, GLushort green, GLushort blue)) +GL_FUNCTION(glSecondaryColor3usv,void,(const GLushort *v)) +GL_FUNCTION(glSecondaryColorPointer,void,(GLint size, GLenum type, GLsizei stride, GLvoid *pointer)) +GL_FUNCTION(glBlendFuncSeparate,void,(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha)) +GL_FUNCTION(glWindowPos2d,void,(GLdouble x, GLdouble y)) +GL_FUNCTION(glWindowPos2f,void,(GLfloat x, GLfloat y)) +GL_FUNCTION(glWindowPos2i,void,(GLint x, GLint y)) +GL_FUNCTION(glWindowPos2s,void,(GLshort x, GLshort y)) +GL_FUNCTION(glWindowPos2dv,void,(const GLdouble *p)) +GL_FUNCTION(glWindowPos2fv,void,(const GLfloat *p)) +GL_FUNCTION(glWindowPos2iv,void,(const GLint *p)) +GL_FUNCTION(glWindowPos2sv,void,(const GLshort *p)) +GL_FUNCTION(glWindowPos3d,void,(GLdouble x, GLdouble y, GLdouble z)) +GL_FUNCTION(glWindowPos3f,void,(GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glWindowPos3i,void,(GLint x, GLint y, GLint z)) +GL_FUNCTION(glWindowPos3s,void,(GLshort x, GLshort y, GLshort z)) +GL_FUNCTION(glWindowPos3dv,void,(const GLdouble *p)) +GL_FUNCTION(glWindowPos3fv,void,(const GLfloat *p)) +GL_FUNCTION(glWindowPos3iv,void,(const GLint *p)) +GL_FUNCTION(glWindowPos3sv,void,(const GLshort *p)) +GL_GROUP_END() +#endif + +#ifdef GL_VERSION_1_5 +GL_GROUP_BEGIN(GL_VERSION_1_5) +GL_FUNCTION(glGenQueries,void,(GLsizei n, GLuint* ids)) +GL_FUNCTION(glDeleteQueries,void,(GLsizei n, const GLuint* ids)) +GL_FUNCTION(glIsQuery,GLboolean,(GLuint id)) +GL_FUNCTION(glBeginQuery,void,(GLenum target, GLuint id)) +GL_FUNCTION(glEndQuery,void,(GLenum target)) +GL_FUNCTION(glGetQueryiv,void,(GLenum target, GLenum pname, GLint* params)) +GL_FUNCTION(glGetQueryObjectiv,void,(GLuint id, GLenum pname, GLint* params)) +GL_FUNCTION(glGetQueryObjectuiv,void,(GLuint id, GLenum pname, GLuint* params)) +GL_FUNCTION(glBindBuffer,void,(GLenum target, GLuint buffer)) +GL_FUNCTION(glDeleteBuffers,void,(GLsizei n, const GLuint* buffers)) +GL_FUNCTION(glGenBuffers,void,(GLsizei n, GLuint* buffers)) +GL_FUNCTION(glIsBuffer,GLboolean,(GLuint buffer)) +GL_FUNCTION(glBufferData,void,(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage)) +GL_FUNCTION(glBufferSubData,void,(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data)) +GL_FUNCTION(glGetBufferSubData,void,(GLenum target, GLintptr offset, GLsizeiptr size, GLvoid* data)) +GL_FUNCTION(glMapBuffer,GLvoid*,(GLenum target, GLenum access)) +GL_FUNCTION(glUnmapBuffer,GLboolean,(GLenum target)) +GL_FUNCTION(glGetBufferParameteriv,void,(GLenum target, GLenum pname, GLint* params)) +GL_FUNCTION(glGetBufferPointerv,void,(GLenum target, GLenum pname, GLvoid** params)) +GL_GROUP_END() +#endif + +#ifdef GL_VERSION_2_0 +GL_GROUP_BEGIN(GL_VERSION_2_0) +GL_FUNCTION(glBlendEquationSeparate,void,(GLenum, GLenum)) +GL_FUNCTION(glDrawBuffers,void,(GLsizei n, const GLenum* bufs)) +GL_FUNCTION(glStencilOpSeparate,void,(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass)) +GL_FUNCTION(glStencilFuncSeparate,void,(GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask)) +GL_FUNCTION(glStencilMaskSeparate,void,(GLenum, GLuint)) +GL_FUNCTION(glAttachShader,void,(GLuint program, GLuint shader)) +GL_FUNCTION(glBindAttribLocation,void,(GLuint program, GLuint index, const GLchar* name)) +GL_FUNCTION(glCompileShader,void,(GLuint shader)) +GL_FUNCTION(glCreateProgram,GLuint,(void)) +GL_FUNCTION(glCreateShader,GLuint,(GLenum type)) +GL_FUNCTION(glDeleteProgram,void,(GLuint program)) +GL_FUNCTION(glDeleteShader,void,(GLuint shader)) +GL_FUNCTION(glDetachShader,void,(GLuint program, GLuint shader)) +GL_FUNCTION(glDisableVertexAttribArray,void,(GLuint)) +GL_FUNCTION(glEnableVertexAttribArray,void,(GLuint)) +GL_FUNCTION(glGetActiveAttrib,void,(GLuint program, GLuint index, GLsizei maxLength, GLsizei* length, GLint* size, GLenum* type, GLchar* name)) +GL_FUNCTION(glGetActiveUniform,void,(GLuint program, GLuint index, GLsizei maxLength, GLsizei* length, GLint* size, GLenum* type, GLchar* name)) +GL_FUNCTION(glGetAttachedShaders,void,(GLuint program, GLsizei maxCount, GLsizei* count, GLuint* shaders)) +GL_FUNCTION(glGetAttribLocation,GLint,(GLuint program, const GLchar* name)) +GL_FUNCTION(glGetProgramiv,void,(GLuint program, GLenum pname, GLint* param)) +GL_FUNCTION(glGetProgramInfoLog,void,(GLuint program, GLsizei bufSize, GLsizei* length, GLchar* infoLog)) +GL_FUNCTION(glGetShaderiv,void,(GLuint shader, GLenum pname, GLint* param)) +GL_FUNCTION(glGetShaderInfoLog,void,(GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog)) +GL_FUNCTION(glShaderSource,void,(GLuint shader, GLsizei count, const GLchar** strings, const GLint* lengths)) +GL_FUNCTION(glGetUniformLocation,GLint,(GLint programObj, const GLchar* name)) +GL_FUNCTION(glGetUniformfv,void,(GLuint program, GLint location, GLfloat* params)) +GL_FUNCTION(glGetUniformiv,void,(GLuint program, GLint location, GLint* params)) +GL_FUNCTION(glGetVertexAttribdv,void,(GLuint, GLenum, GLdouble*)) +GL_FUNCTION(glGetVertexAttribfv,void,(GLuint, GLenum, GLfloat*)) +GL_FUNCTION(glGetVertexAttribiv,void,(GLuint, GLenum, GLint*)) +GL_FUNCTION(glGetVertexAttribPointerv,void,(GLuint, GLenum, GLvoid*)) +GL_FUNCTION(glIsProgram,GLboolean,(GLuint program)) +GL_FUNCTION(glIsShader,GLboolean,(GLuint shader)) +GL_FUNCTION(glLinkProgram,void,(GLuint program)) +GL_FUNCTION(glGetShaderSource,void,(GLint obj, GLsizei maxLength, GLsizei* length, GLchar* source)) +GL_FUNCTION(glUseProgram,void,(GLuint program)) +GL_FUNCTION(glUniform1f,void,(GLint location, GLfloat v0)) +GL_FUNCTION(glUniform1fv,void,(GLint location, GLsizei count, const GLfloat* value)) +GL_FUNCTION(glUniform1i,void,(GLint location, GLint v0)) +GL_FUNCTION(glUniform1iv,void,(GLint location, GLsizei count, const GLint* value)) +GL_FUNCTION(glUniform2f,void,(GLint location, GLfloat v0, GLfloat v1)) +GL_FUNCTION(glUniform2fv,void,(GLint location, GLsizei count, const GLfloat* value)) +GL_FUNCTION(glUniform2i,void,(GLint location, GLint v0, GLint v1)) +GL_FUNCTION(glUniform2iv,void,(GLint location, GLsizei count, const GLint* value)) +GL_FUNCTION(glUniform3f,void,(GLint location, GLfloat v0, GLfloat v1, GLfloat v2)) +GL_FUNCTION(glUniform3fv,void,(GLint location, GLsizei count, const GLfloat* value)) +GL_FUNCTION(glUniform3i,void,(GLint location, GLint v0, GLint v1, GLint v2)) +GL_FUNCTION(glUniform3iv,void,(GLint location, GLsizei count, const GLint* value)) +GL_FUNCTION(glUniform4f,void,(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3)) +GL_FUNCTION(glUniform4fv,void,(GLint location, GLsizei count, const GLfloat* value)) +GL_FUNCTION(glUniform4i,void,(GLint location, GLint v0, GLint v1, GLint v2, GLint v3)) +GL_FUNCTION(glUniform4iv,void,(GLint location, GLsizei count, const GLint* value)) +GL_FUNCTION(glUniformMatrix2fv,void,(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)) +GL_FUNCTION(glUniformMatrix3fv,void,(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)) +GL_FUNCTION(glUniformMatrix4fv,void,(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)) +GL_FUNCTION(glValidateProgram,void,(GLuint program)) +GL_FUNCTION(glVertexAttrib1d,void,(GLuint index, GLdouble x)) +GL_FUNCTION(glVertexAttrib1dv,void,(GLuint index, const GLdouble* v)) +GL_FUNCTION(glVertexAttrib1f,void,(GLuint index, GLfloat x)) +GL_FUNCTION(glVertexAttrib1fv,void,(GLuint index, const GLfloat* v)) +GL_FUNCTION(glVertexAttrib1s,void,(GLuint index, GLshort x)) +GL_FUNCTION(glVertexAttrib1sv,void,(GLuint index, const GLshort* v)) +GL_FUNCTION(glVertexAttrib2d,void,(GLuint index, GLdouble x, GLdouble y)) +GL_FUNCTION(glVertexAttrib2dv,void,(GLuint index, const GLdouble* v)) +GL_FUNCTION(glVertexAttrib2f,void,(GLuint index, GLfloat x, GLfloat y)) +GL_FUNCTION(glVertexAttrib2fv,void,(GLuint index, const GLfloat* v)) +GL_FUNCTION(glVertexAttrib2s,void,(GLuint index, GLshort x, GLshort y)) +GL_FUNCTION(glVertexAttrib2sv,void,(GLuint index, const GLshort* v)) +GL_FUNCTION(glVertexAttrib3d,void,(GLuint index, GLdouble x, GLdouble y, GLdouble z)) +GL_FUNCTION(glVertexAttrib3dv,void,(GLuint index, const GLdouble* v)) +GL_FUNCTION(glVertexAttrib3f,void,(GLuint index, GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glVertexAttrib3fv,void,(GLuint index, const GLfloat* v)) +GL_FUNCTION(glVertexAttrib3s,void,(GLuint index, GLshort x, GLshort y, GLshort z)) +GL_FUNCTION(glVertexAttrib3sv,void,(GLuint index, const GLshort* v)) +GL_FUNCTION(glVertexAttrib4Nbv,void,(GLuint index, const GLbyte* v)) +GL_FUNCTION(glVertexAttrib4Niv,void,(GLuint index, const GLint* v)) +GL_FUNCTION(glVertexAttrib4Nsv,void,(GLuint index, const GLshort* v)) +GL_FUNCTION(glVertexAttrib4Nub,void,(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w)) +GL_FUNCTION(glVertexAttrib4Nubv,void,(GLuint index, const GLubyte* v)) +GL_FUNCTION(glVertexAttrib4Nuiv,void,(GLuint index, const GLuint* v)) +GL_FUNCTION(glVertexAttrib4Nusv,void,(GLuint index, const GLushort* v)) +GL_FUNCTION(glVertexAttrib4bv,void,(GLuint index, const GLbyte* v)) +GL_FUNCTION(glVertexAttrib4d,void,(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w)) +GL_FUNCTION(glVertexAttrib4dv,void,(GLuint index, const GLdouble* v)) +GL_FUNCTION(glVertexAttrib4f,void,(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)) +GL_FUNCTION(glVertexAttrib4fv,void,(GLuint index, const GLfloat* v)) +GL_FUNCTION(glVertexAttrib4iv,void,(GLuint index, const GLint* v)) +GL_FUNCTION(glVertexAttrib4s,void,(GLuint index, GLshort x, GLshort y, GLshort z, GLshort w)) +GL_FUNCTION(glVertexAttrib4sv,void,(GLuint index, const GLshort* v)) +GL_FUNCTION(glVertexAttrib4ubv,void,(GLuint index, const GLubyte* v)) +GL_FUNCTION(glVertexAttrib4uiv,void,(GLuint index, const GLuint* v)) +GL_FUNCTION(glVertexAttrib4usv,void,(GLuint index, const GLushort* v)) +GL_FUNCTION(glVertexAttribPointer,void,(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* pointer)) +GL_GROUP_END() +#endif + +#ifdef GL_3DFX_multisample +GL_GROUP_BEGIN(GL_3DFX_multisample) +GL_GROUP_END() +#endif + +#ifdef GL_3DFX_tbuffer +GL_GROUP_BEGIN(GL_3DFX_tbuffer) +GL_FUNCTION(glTbufferMask3DFX,void,(GLuint mask)) +GL_GROUP_END() +#endif + +#ifdef GL_3DFX_texture_compression_FXT1 +GL_GROUP_BEGIN(GL_3DFX_texture_compression_FXT1) +GL_GROUP_END() +#endif + +#ifdef GL_APPLE_client_storage +GL_GROUP_BEGIN(GL_APPLE_client_storage) +GL_GROUP_END() +#endif + +#ifdef GL_APPLE_element_array +GL_GROUP_BEGIN(GL_APPLE_element_array) +GL_FUNCTION(glDrawElementArrayAPPLE,void,(GLenum mode, GLint first, GLsizei count)) +GL_FUNCTION(glDrawRangeElementArrayAPPLE,void,(GLenum mode, GLuint start, GLuint end, GLint first, GLsizei count)) +GL_FUNCTION(glElementPointerAPPLE,void,(GLenum type, const void* pointer)) +GL_FUNCTION(glMultiDrawElementArrayAPPLE,void,(GLenum mode, const GLint* first, const GLsizei *count, GLsizei primcount)) +GL_FUNCTION(glMultiDrawRangeElementArrayAPPLE,void,(GLenum mode, GLuint start, GLuint end, const GLint* first, const GLsizei *count, GLsizei primcount)) +GL_GROUP_END() +#endif + +#ifdef GL_APPLE_fence +GL_GROUP_BEGIN(GL_APPLE_fence) +GL_FUNCTION(glDeleteFencesAPPLE,void,(GLsizei n, const GLuint* fences)) +GL_FUNCTION(glFinishFenceAPPLE,void,(GLuint fence)) +GL_FUNCTION(glFinishObjectAPPLE,void,(GLenum object, GLint name)) +GL_FUNCTION(glGenFencesAPPLE,void,(GLsizei n, GLuint* fences)) +GL_FUNCTION(glIsFenceAPPLE,GLboolean,(GLuint fence)) +GL_FUNCTION(glSetFenceAPPLE,void,(GLuint fence)) +GL_FUNCTION(glTestFenceAPPLE,GLboolean,(GLuint fence)) +GL_FUNCTION(glTestObjectAPPLE,GLboolean,(GLenum object, GLuint name)) +GL_GROUP_END() +#endif + +#ifdef GL_APPLE_float_pixels +GL_GROUP_BEGIN(GL_APPLE_float_pixels) +GL_GROUP_END() +#endif + +#ifdef GL_APPLE_pixel_buffer +GL_GROUP_BEGIN(GL_APPLE_pixel_buffer) +GL_GROUP_END() +#endif + +#ifdef GL_APPLE_specular_vector +GL_GROUP_BEGIN(GL_APPLE_specular_vector) +GL_GROUP_END() +#endif + +#ifdef GL_APPLE_texture_range +GL_GROUP_BEGIN(GL_APPLE_texture_range) +GL_FUNCTION(glTextureRangeAPPLE,void,(GLenum target, GLsizei length, GLvoid *pointer)) +GL_FUNCTION(glGetTexParameterPointervAPPLE,void,(GLenum target, GLenum pname, GLvoid **params)) +GL_GROUP_END() +#endif + +#ifdef GL_APPLE_transform_hint +GL_GROUP_BEGIN(GL_APPLE_transform_hint) +GL_GROUP_END() +#endif + +#ifdef GL_APPLE_vertex_array_object +GL_GROUP_BEGIN(GL_APPLE_vertex_array_object) +GL_FUNCTION(glBindVertexArrayAPPLE,void,(GLuint array)) +GL_FUNCTION(glDeleteVertexArraysAPPLE,void,(GLsizei n, const GLuint* arrays)) +GL_FUNCTION(glGenVertexArraysAPPLE,void,(GLsizei n, const GLuint* arrays)) +GL_FUNCTION(glIsVertexArrayAPPLE,GLboolean,(GLuint array)) +GL_GROUP_END() +#endif + +#ifdef GL_APPLE_vertex_array_range +GL_GROUP_BEGIN(GL_APPLE_vertex_array_range) +GL_FUNCTION(glFlushVertexArrayRangeAPPLE,void,(GLsizei length, void* pointer)) +GL_FUNCTION(glVertexArrayParameteriAPPLE,void,(GLenum pname, GLint param)) +GL_FUNCTION(glVertexArrayRangeAPPLE,void,(GLsizei length, void* pointer)) +GL_GROUP_END() +#endif + +#ifdef GL_APPLE_ycbcr_422 +GL_GROUP_BEGIN(GL_APPLE_ycbcr_422) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_color_buffer_float +GL_GROUP_BEGIN(GL_ARB_color_buffer_float) +GL_FUNCTION(glClampColorARB,void,(GLenum target, GLenum clamp)) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_depth_texture +GL_GROUP_BEGIN(GL_ARB_depth_texture) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_draw_buffers +GL_GROUP_BEGIN(GL_ARB_draw_buffers) +GL_FUNCTION(glDrawBuffersARB,void,(GLsizei n, const GLenum* bufs)) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_fragment_program +GL_GROUP_BEGIN(GL_ARB_fragment_program) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_fragment_program_shadow +GL_GROUP_BEGIN(GL_ARB_fragment_program_shadow) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_fragment_shader +GL_GROUP_BEGIN(GL_ARB_fragment_shader) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_half_float_pixel +GL_GROUP_BEGIN(GL_ARB_half_float_pixel) +GL_GROUP_END() +#endif + +#if defined(GL_ARB_imaging) && !defined(GL_VERSION_1_4) +GL_GROUP_BEGIN(GL_ARB_imaging) +GL_FUNCTION(glColorTable,void,(GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *table)) +GL_FUNCTION(glColorSubTable,void,(GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const GLvoid *data)) +GL_FUNCTION(glColorTableParameteriv,void,(GLenum target, GLenum pname, const GLint *params)) +GL_FUNCTION(glColorTableParameterfv,void,(GLenum target, GLenum pname, const GLfloat *params)) +GL_FUNCTION(glCopyColorSubTable,void,(GLenum target, GLsizei start, GLint x, GLint y, GLsizei width)) +GL_FUNCTION(glCopyColorTable,void,(GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width)) +GL_FUNCTION(glGetColorTable,void,(GLenum target, GLenum format, GLenum type, GLvoid *table)) +GL_FUNCTION(glGetColorTableParameterfv,void,(GLenum target, GLenum pname, GLfloat *params)) +GL_FUNCTION(glGetColorTableParameteriv,void,(GLenum target, GLenum pname, GLint *params)) +GL_FUNCTION(glHistogram,void,(GLenum target, GLsizei width, GLenum internalformat, GLboolean sink)) +GL_FUNCTION(glResetHistogram,void,(GLenum target)) +GL_FUNCTION(glGetHistogram,void,(GLenum target, GLboolean reset, GLenum format, GLenum type, GLvoid *values)) +GL_FUNCTION(glGetHistogramParameterfv,void,(GLenum target, GLenum pname, GLfloat *params)) +GL_FUNCTION(glGetHistogramParameteriv,void,(GLenum target, GLenum pname, GLint *params)) +GL_FUNCTION(glMinmax,void,(GLenum target, GLenum internalformat, GLboolean sink)) +GL_FUNCTION(glResetMinmax,void,(GLenum target)) +GL_FUNCTION(glGetMinmaxParameterfv,void,(GLenum target, GLenum pname, GLfloat *params)) +GL_FUNCTION(glGetMinmaxParameteriv,void,(GLenum target, GLenum pname, GLint *params)) +GL_FUNCTION(glConvolutionFilter1D,void,(GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *image)) +GL_FUNCTION(glConvolutionFilter2D,void,(GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *image)) +GL_FUNCTION(glConvolutionParameterf,void,(GLenum target, GLenum pname, GLfloat params)) +GL_FUNCTION(glConvolutionParameterfv,void,(GLenum target, GLenum pname, const GLfloat *params)) +GL_FUNCTION(glConvolutionParameteri,void,(GLenum target, GLenum pname, GLint params)) +GL_FUNCTION(glConvolutionParameteriv,void,(GLenum target, GLenum pname, const GLint *params)) +GL_FUNCTION(glCopyConvolutionFilter1D,void,(GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width)) +GL_FUNCTION(glCopyConvolutionFilter2D,void,(GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height)) +GL_FUNCTION(glGetConvolutionFilter,void,(GLenum target, GLenum format, GLenum type, GLvoid *image)) +GL_FUNCTION(glGetConvolutionParameterfv,void,(GLenum target, GLenum pname, GLfloat *params)) +GL_FUNCTION(glGetConvolutionParameteriv,void,(GLenum target, GLenum pname, GLint *params)) +GL_FUNCTION(glSeparableFilter2D,void,(GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *row, const GLvoid *column)) +GL_FUNCTION(glGetSeparableFilter,void,(GLenum target, GLenum format, GLenum type, GLvoid *row, GLvoid *column, GLvoid *span)) +GL_FUNCTION(glGetMinmax,void,(GLenum target, GLboolean reset, GLenum format, GLenum types, GLvoid *values)) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_matrix_palette +GL_GROUP_BEGIN(GL_ARB_matrix_palette) +GL_FUNCTION(glCurrentPaletteMatrixARB,void,(GLint index)) +GL_FUNCTION(glMatrixIndexPointerARB,void,(GLint size, GLenum type, GLsizei stride, GLvoid *pointer)) +GL_FUNCTION(glMatrixIndexubvARB,void,(GLint size, GLubyte *indices)) +GL_FUNCTION(glMatrixIndexusvARB,void,(GLint size, GLushort *indices)) +GL_FUNCTION(glMatrixIndexuivARB,void,(GLint size, GLuint *indices)) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_multisample +GL_GROUP_BEGIN(GL_ARB_multisample) +GL_FUNCTION(glSampleCoverageARB,void,(GLclampf value, GLboolean invert)) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_multitexture +GL_GROUP_BEGIN(GL_ARB_multitexture) +GL_FUNCTION(glActiveTextureARB,void,(GLenum texture)) +GL_FUNCTION(glClientActiveTextureARB,void,(GLenum texture)) +GL_FUNCTION(glMultiTexCoord1dARB,void,(GLenum target, GLdouble s)) +GL_FUNCTION(glMultiTexCoord1dvARB,void,(GLenum target, const GLdouble *v)) +GL_FUNCTION(glMultiTexCoord1fARB,void,(GLenum target, GLfloat s)) +GL_FUNCTION(glMultiTexCoord1fvARB,void,(GLenum target, const GLfloat *v)) +GL_FUNCTION(glMultiTexCoord1iARB,void,(GLenum target, GLint s)) +GL_FUNCTION(glMultiTexCoord1ivARB,void,(GLenum target, const GLint *v)) +GL_FUNCTION(glMultiTexCoord1sARB,void,(GLenum target, GLshort s)) +GL_FUNCTION(glMultiTexCoord1svARB,void,(GLenum target, const GLshort *v)) +GL_FUNCTION(glMultiTexCoord2dARB,void,(GLenum target, GLdouble s, GLdouble t)) +GL_FUNCTION(glMultiTexCoord2dvARB,void,(GLenum target, const GLdouble *v)) +GL_FUNCTION(glMultiTexCoord2fARB,void,(GLenum target, GLfloat s, GLfloat t)) +GL_FUNCTION(glMultiTexCoord2fvARB,void,(GLenum target, const GLfloat *v)) +GL_FUNCTION(glMultiTexCoord2iARB,void,(GLenum target, GLint s, GLint t)) +GL_FUNCTION(glMultiTexCoord2ivARB,void,(GLenum target, const GLint *v)) +GL_FUNCTION(glMultiTexCoord2sARB,void,(GLenum target, GLshort s, GLshort t)) +GL_FUNCTION(glMultiTexCoord2svARB,void,(GLenum target, const GLshort *v)) +GL_FUNCTION(glMultiTexCoord3dARB,void,(GLenum target, GLdouble s, GLdouble t, GLdouble r)) +GL_FUNCTION(glMultiTexCoord3dvARB,void,(GLenum target, const GLdouble *v)) +GL_FUNCTION(glMultiTexCoord3fARB,void,(GLenum target, GLfloat s, GLfloat t, GLfloat r)) +GL_FUNCTION(glMultiTexCoord3fvARB,void,(GLenum target, const GLfloat *v)) +GL_FUNCTION(glMultiTexCoord3iARB,void,(GLenum target, GLint s, GLint t, GLint r)) +GL_FUNCTION(glMultiTexCoord3ivARB,void,(GLenum target, const GLint *v)) +GL_FUNCTION(glMultiTexCoord3sARB,void,(GLenum target, GLshort s, GLshort t, GLshort r)) +GL_FUNCTION(glMultiTexCoord3svARB,void,(GLenum target, const GLshort *v)) +GL_FUNCTION(glMultiTexCoord4dARB,void,(GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q)) +GL_FUNCTION(glMultiTexCoord4dvARB,void,(GLenum target, const GLdouble *v)) +GL_FUNCTION(glMultiTexCoord4fARB,void,(GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q)) +GL_FUNCTION(glMultiTexCoord4fvARB,void,(GLenum target, const GLfloat *v)) +GL_FUNCTION(glMultiTexCoord4iARB,void,(GLenum target, GLint s, GLint t, GLint r, GLint q)) +GL_FUNCTION(glMultiTexCoord4ivARB,void,(GLenum target, const GLint *v)) +GL_FUNCTION(glMultiTexCoord4sARB,void,(GLenum target, GLshort s, GLshort t, GLshort r, GLshort q)) +GL_FUNCTION(glMultiTexCoord4svARB,void,(GLenum target, const GLshort *v)) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_occlusion_query +GL_GROUP_BEGIN(GL_ARB_occlusion_query) +GL_FUNCTION(glBeginQueryARB,void,(GLenum target, GLuint id)) +GL_FUNCTION(glDeleteQueriesARB,void,(GLsizei n, const GLuint* ids)) +GL_FUNCTION(glEndQueryARB,void,(GLenum target)) +GL_FUNCTION(glGenQueriesARB,void,(GLsizei n, GLuint* ids)) +GL_FUNCTION(glGetQueryObjectivARB,void,(GLuint id, GLenum pname, GLint* params)) +GL_FUNCTION(glGetQueryObjectuivARB,void,(GLuint id, GLenum pname, GLuint* params)) +GL_FUNCTION(glGetQueryivARB,void,(GLenum target, GLenum pname, GLint* params)) +GL_FUNCTION(glIsQueryARB,GLboolean,(GLuint id)) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_pixel_buffer_object +GL_GROUP_BEGIN(GL_ARB_pixel_buffer_object) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_point_parameters +GL_GROUP_BEGIN(GL_ARB_point_parameters) +GL_FUNCTION(glPointParameterfARB,void,(GLenum pname, GLfloat param)) +GL_FUNCTION(glPointParameterfvARB,void,(GLenum pname, GLfloat* params)) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_point_sprite +GL_GROUP_BEGIN(GL_ARB_point_sprite) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_shader_objects +GL_GROUP_BEGIN(GL_ARB_shader_objects) +GL_FUNCTION(glAttachObjectARB,void,(GLhandleARB containerObj, GLhandleARB obj)) +GL_FUNCTION(glCompileShaderARB,void,(GLhandleARB shaderObj)) +GL_FUNCTION(glCreateProgramObjectARB,GLhandleARB,(void)) +GL_FUNCTION(glCreateShaderObjectARB,GLhandleARB,(GLenum shaderType)) +GL_FUNCTION(glDeleteObjectARB,void,(GLhandleARB obj)) +GL_FUNCTION(glDetachObjectARB,void,(GLhandleARB containerObj, GLhandleARB attachedObj)) +GL_FUNCTION(glGetActiveUniformARB,void,(GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei* length, GLint *size, GLenum *type, GLcharARB *name)) +GL_FUNCTION(glGetAttachedObjectsARB,void,(GLhandleARB containerObj, GLsizei maxCount, GLsizei* count, GLhandleARB *obj)) +GL_FUNCTION(glGetHandleARB,GLhandleARB,(GLenum pname)) +GL_FUNCTION(glGetInfoLogARB,void,(GLhandleARB obj, GLsizei maxLength, GLsizei* length, GLcharARB *infoLog)) +GL_FUNCTION(glGetObjectParameterfvARB,void,(GLhandleARB obj, GLenum pname, GLfloat* params)) +GL_FUNCTION(glGetObjectParameterivARB,void,(GLhandleARB obj, GLenum pname, GLint* params)) +GL_FUNCTION(glGetShaderSourceARB,void,(GLhandleARB obj, GLsizei maxLength, GLsizei* length, GLcharARB *source)) +GL_FUNCTION(glGetUniformLocationARB,GLint,(GLhandleARB programObj, const GLcharARB* name)) +GL_FUNCTION(glGetUniformfvARB,void,(GLhandleARB programObj, GLint location, GLfloat* params)) +GL_FUNCTION(glGetUniformivARB,void,(GLhandleARB programObj, GLint location, GLint* params)) +GL_FUNCTION(glLinkProgramARB,void,(GLhandleARB programObj)) +GL_FUNCTION(glShaderSourceARB,void,(GLhandleARB shaderObj, GLsizei count, const GLcharARB ** string, const GLint *length)) +GL_FUNCTION(glUniform1fARB,void,(GLint location, GLfloat v0)) +GL_FUNCTION(glUniform1fvARB,void,(GLint location, GLsizei count, const GLfloat* value)) +GL_FUNCTION(glUniform1iARB,void,(GLint location, GLint v0)) +GL_FUNCTION(glUniform1ivARB,void,(GLint location, GLsizei count, const GLint* value)) +GL_FUNCTION(glUniform2fARB,void,(GLint location, GLfloat v0, GLfloat v1)) +GL_FUNCTION(glUniform2fvARB,void,(GLint location, GLsizei count, const GLfloat* value)) +GL_FUNCTION(glUniform2iARB,void,(GLint location, GLint v0, GLint v1)) +GL_FUNCTION(glUniform2ivARB,void,(GLint location, GLsizei count, const GLint* value)) +GL_FUNCTION(glUniform3fARB,void,(GLint location, GLfloat v0, GLfloat v1, GLfloat v2)) +GL_FUNCTION(glUniform3fvARB,void,(GLint location, GLsizei count, const GLfloat* value)) +GL_FUNCTION(glUniform3iARB,void,(GLint location, GLint v0, GLint v1, GLint v2)) +GL_FUNCTION(glUniform3ivARB,void,(GLint location, GLsizei count, const GLint* value)) +GL_FUNCTION(glUniform4fARB,void,(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3)) +GL_FUNCTION(glUniform4fvARB,void,(GLint location, GLsizei count, const GLfloat* value)) +GL_FUNCTION(glUniform4iARB,void,(GLint location, GLint v0, GLint v1, GLint v2, GLint v3)) +GL_FUNCTION(glUniform4ivARB,void,(GLint location, GLsizei count, const GLint* value)) +GL_FUNCTION(glUniformMatrix2fvARB,void,(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)) +GL_FUNCTION(glUniformMatrix3fvARB,void,(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)) +GL_FUNCTION(glUniformMatrix4fvARB,void,(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)) +GL_FUNCTION(glUseProgramObjectARB,void,(GLhandleARB programObj)) +GL_FUNCTION(glValidateProgramARB,void,(GLhandleARB programObj)) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_shading_language_100 +GL_GROUP_BEGIN(GL_ARB_shading_language_100) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_shadow +GL_GROUP_BEGIN(GL_ARB_shadow) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_shadow_ambient +GL_GROUP_BEGIN(GL_ARB_shadow_ambient) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_texture_border_clamp +GL_GROUP_BEGIN(GL_ARB_texture_border_clamp) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_texture_compression +GL_GROUP_BEGIN(GL_ARB_texture_compression) +GL_FUNCTION(glCompressedTexImage1DARB,void,(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void* data)) +GL_FUNCTION(glCompressedTexImage2DARB,void,(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void* data)) +GL_FUNCTION(glCompressedTexImage3DARB,void,(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void* data)) +GL_FUNCTION(glCompressedTexSubImage1DARB,void,(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void* data)) +GL_FUNCTION(glCompressedTexSubImage2DARB,void,(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data)) +GL_FUNCTION(glCompressedTexSubImage3DARB,void,(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void* data)) +GL_FUNCTION(glGetCompressedTexImageARB,void,(GLenum target, GLint lod, void* img)) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_texture_cube_map +GL_GROUP_BEGIN(GL_ARB_texture_cube_map) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_texture_env_add +GL_GROUP_BEGIN(GL_ARB_texture_env_add) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_texture_env_combine +GL_GROUP_BEGIN(GL_ARB_texture_env_combine) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_texture_env_crossbar +GL_GROUP_BEGIN(GL_ARB_texture_env_crossbar) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_texture_env_dot3 +GL_GROUP_BEGIN(GL_ARB_texture_env_dot3) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_texture_float +GL_GROUP_BEGIN(GL_ARB_texture_float) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_texture_mirrored_repeat +GL_GROUP_BEGIN(GL_ARB_texture_mirrored_repeat) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_texture_non_power_of_two +GL_GROUP_BEGIN(GL_ARB_texture_non_power_of_two) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_texture_rectangle +GL_GROUP_BEGIN(GL_ARB_texture_rectangle) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_transpose_matrix +GL_GROUP_BEGIN(GL_ARB_transpose_matrix) +GL_FUNCTION(glLoadTransposeMatrixfARB,void,(GLfloat m[16])) +GL_FUNCTION(glLoadTransposeMatrixdARB,void,(GLdouble m[16])) +GL_FUNCTION(glMultTransposeMatrixfARB,void,(GLfloat m[16])) +GL_FUNCTION(glMultTransposeMatrixdARB,void,(GLdouble m[16])) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_vertex_blend +GL_GROUP_BEGIN(GL_ARB_vertex_blend) +GL_FUNCTION(glWeightbvARB,void,(GLint size, GLbyte *weights)) +GL_FUNCTION(glWeightsvARB,void,(GLint size, GLshort *weights)) +GL_FUNCTION(glWeightivARB,void,(GLint size, GLint *weights)) +GL_FUNCTION(glWeightfvARB,void,(GLint size, GLfloat *weights)) +GL_FUNCTION(glWeightdvARB,void,(GLint size, GLdouble *weights)) +GL_FUNCTION(glWeightubvARB,void,(GLint size, GLubyte *weights)) +GL_FUNCTION(glWeightusvARB,void,(GLint size, GLushort *weights)) +GL_FUNCTION(glWeightuivARB,void,(GLint size, GLuint *weights)) +GL_FUNCTION(glWeightPointerARB,void,(GLint size, GLenum type, GLsizei stride, GLvoid *pointer)) +GL_FUNCTION(glVertexBlendARB,void,(GLint count)) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_vertex_buffer_object +GL_GROUP_BEGIN(GL_ARB_vertex_buffer_object) +GL_FUNCTION(glBindBufferARB,void,(GLenum target, GLuint buffer)) +GL_FUNCTION(glBufferDataARB,void,(GLenum target, GLsizeiptrARB size, const GLvoid* data, GLenum usage)) +GL_FUNCTION(glBufferSubDataARB,void,(GLenum target, GLintptrARB offset, GLsizeiptrARB size, const GLvoid* data)) +GL_FUNCTION(glDeleteBuffersARB,void,(GLsizei n, const GLuint* buffers)) +GL_FUNCTION(glGenBuffersARB,void,(GLsizei n, GLuint* buffers)) +GL_FUNCTION(glGetBufferParameterivARB,void,(GLenum target, GLenum pname, GLint* params)) +GL_FUNCTION(glGetBufferPointervARB,void,(GLenum target, GLenum pname, GLvoid** params)) +GL_FUNCTION(glGetBufferSubDataARB,void,(GLenum target, GLintptrARB offset, GLsizeiptrARB size, GLvoid* data)) +GL_FUNCTION(glIsBufferARB,GLboolean,(GLuint buffer)) +GL_FUNCTION(glMapBufferARB,GLvoid *,(GLenum target, GLenum access)) +GL_FUNCTION(glUnmapBufferARB,GLboolean,(GLenum target)) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_vertex_program +GL_GROUP_BEGIN(GL_ARB_vertex_program) +GL_FUNCTION(glBindProgramARB,void,(GLenum target, GLuint program)) +GL_FUNCTION(glDeleteProgramsARB,void,(GLsizei n, const GLuint* programs)) +GL_FUNCTION(glDisableVertexAttribArrayARB,void,(GLuint index)) +GL_FUNCTION(glEnableVertexAttribArrayARB,void,(GLuint index)) +GL_FUNCTION(glGenProgramsARB,void,(GLsizei n, GLuint* programs)) +GL_FUNCTION(glGetProgramEnvParameterdvARB,void,(GLenum target, GLuint index, GLdouble* params)) +GL_FUNCTION(glGetProgramEnvParameterfvARB,void,(GLenum target, GLuint index, GLfloat* params)) +GL_FUNCTION(glGetProgramLocalParameterdvARB,void,(GLenum target, GLuint index, GLdouble* params)) +GL_FUNCTION(glGetProgramLocalParameterfvARB,void,(GLenum target, GLuint index, GLfloat* params)) +GL_FUNCTION(glGetProgramStringARB,void,(GLenum target, GLenum pname, void* string)) +GL_FUNCTION(glGetProgramivARB,void,(GLenum target, GLenum pname, GLint* params)) +GL_FUNCTION(glGetVertexAttribPointervARB,void,(GLuint index, GLenum pname, GLvoid** pointer)) +GL_FUNCTION(glGetVertexAttribdvARB,void,(GLuint index, GLenum pname, GLdouble* params)) +GL_FUNCTION(glGetVertexAttribfvARB,void,(GLuint index, GLenum pname, GLfloat* params)) +GL_FUNCTION(glGetVertexAttribivARB,void,(GLuint index, GLenum pname, GLint* params)) +GL_FUNCTION(glIsProgramARB,GLboolean,(GLuint program)) +GL_FUNCTION(glProgramEnvParameter4dARB,void,(GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w)) +GL_FUNCTION(glProgramEnvParameter4dvARB,void,(GLenum target, GLuint index, const GLdouble* params)) +GL_FUNCTION(glProgramEnvParameter4fARB,void,(GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)) +GL_FUNCTION(glProgramEnvParameter4fvARB,void,(GLenum target, GLuint index, const GLfloat* params)) +GL_FUNCTION(glProgramLocalParameter4dARB,void,(GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w)) +GL_FUNCTION(glProgramLocalParameter4dvARB,void,(GLenum target, GLuint index, const GLdouble* params)) +GL_FUNCTION(glProgramLocalParameter4fARB,void,(GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)) +GL_FUNCTION(glProgramLocalParameter4fvARB,void,(GLenum target, GLuint index, const GLfloat* params)) +GL_FUNCTION(glProgramStringARB,void,(GLenum target, GLenum format, GLsizei len, const void* string)) +GL_FUNCTION(glVertexAttrib1dARB,void,(GLuint index, GLdouble x)) +GL_FUNCTION(glVertexAttrib1dvARB,void,(GLuint index, const GLdouble* v)) +GL_FUNCTION(glVertexAttrib1fARB,void,(GLuint index, GLfloat x)) +GL_FUNCTION(glVertexAttrib1fvARB,void,(GLuint index, const GLfloat* v)) +GL_FUNCTION(glVertexAttrib1sARB,void,(GLuint index, GLshort x)) +GL_FUNCTION(glVertexAttrib1svARB,void,(GLuint index, const GLshort* v)) +GL_FUNCTION(glVertexAttrib2dARB,void,(GLuint index, GLdouble x, GLdouble y)) +GL_FUNCTION(glVertexAttrib2dvARB,void,(GLuint index, const GLdouble* v)) +GL_FUNCTION(glVertexAttrib2fARB,void,(GLuint index, GLfloat x, GLfloat y)) +GL_FUNCTION(glVertexAttrib2fvARB,void,(GLuint index, const GLfloat* v)) +GL_FUNCTION(glVertexAttrib2sARB,void,(GLuint index, GLshort x, GLshort y)) +GL_FUNCTION(glVertexAttrib2svARB,void,(GLuint index, const GLshort* v)) +GL_FUNCTION(glVertexAttrib3dARB,void,(GLuint index, GLdouble x, GLdouble y, GLdouble z)) +GL_FUNCTION(glVertexAttrib3dvARB,void,(GLuint index, const GLdouble* v)) +GL_FUNCTION(glVertexAttrib3fARB,void,(GLuint index, GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glVertexAttrib3fvARB,void,(GLuint index, const GLfloat* v)) +GL_FUNCTION(glVertexAttrib3sARB,void,(GLuint index, GLshort x, GLshort y, GLshort z)) +GL_FUNCTION(glVertexAttrib3svARB,void,(GLuint index, const GLshort* v)) +GL_FUNCTION(glVertexAttrib4NbvARB,void,(GLuint index, const GLbyte* v)) +GL_FUNCTION(glVertexAttrib4NivARB,void,(GLuint index, const GLint* v)) +GL_FUNCTION(glVertexAttrib4NsvARB,void,(GLuint index, const GLshort* v)) +GL_FUNCTION(glVertexAttrib4NubARB,void,(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w)) +GL_FUNCTION(glVertexAttrib4NubvARB,void,(GLuint index, const GLubyte* v)) +GL_FUNCTION(glVertexAttrib4NuivARB,void,(GLuint index, const GLuint* v)) +GL_FUNCTION(glVertexAttrib4NusvARB,void,(GLuint index, const GLushort* v)) +GL_FUNCTION(glVertexAttrib4bvARB,void,(GLuint index, const GLbyte* v)) +GL_FUNCTION(glVertexAttrib4dARB,void,(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w)) +GL_FUNCTION(glVertexAttrib4dvARB,void,(GLuint index, const GLdouble* v)) +GL_FUNCTION(glVertexAttrib4fARB,void,(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)) +GL_FUNCTION(glVertexAttrib4fvARB,void,(GLuint index, const GLfloat* v)) +GL_FUNCTION(glVertexAttrib4ivARB,void,(GLuint index, const GLint* v)) +GL_FUNCTION(glVertexAttrib4sARB,void,(GLuint index, GLshort x, GLshort y, GLshort z, GLshort w)) +GL_FUNCTION(glVertexAttrib4svARB,void,(GLuint index, const GLshort* v)) +GL_FUNCTION(glVertexAttrib4ubvARB,void,(GLuint index, const GLubyte* v)) +GL_FUNCTION(glVertexAttrib4uivARB,void,(GLuint index, const GLuint* v)) +GL_FUNCTION(glVertexAttrib4usvARB,void,(GLuint index, const GLushort* v)) +GL_FUNCTION(glVertexAttribPointerARB,void,(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer)) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_vertex_shader +GL_GROUP_BEGIN(GL_ARB_vertex_shader) +GL_FUNCTION(glBindAttribLocationARB,void,(GLhandleARB programObj, GLuint index, const GLcharARB* name)) +GL_FUNCTION(glGetActiveAttribARB,void,(GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei* length, GLint *size, GLenum *type, GLcharARB *name)) +GL_FUNCTION(glGetAttribLocationARB,GLint,(GLhandleARB programObj, const GLcharARB* name)) +GL_GROUP_END() +#endif + +#ifdef GL_ARB_window_pos +GL_GROUP_BEGIN(GL_ARB_window_pos) +GL_FUNCTION(glWindowPos2dARB,void,(GLdouble x, GLdouble y)) +GL_FUNCTION(glWindowPos2dvARB,void,(const GLdouble* p)) +GL_FUNCTION(glWindowPos2fARB,void,(GLfloat x, GLfloat y)) +GL_FUNCTION(glWindowPos2fvARB,void,(const GLfloat* p)) +GL_FUNCTION(glWindowPos2iARB,void,(GLint x, GLint y)) +GL_FUNCTION(glWindowPos2ivARB,void,(const GLint* p)) +GL_FUNCTION(glWindowPos2sARB,void,(GLshort x, GLshort y)) +GL_FUNCTION(glWindowPos2svARB,void,(const GLshort* p)) +GL_FUNCTION(glWindowPos3dARB,void,(GLdouble x, GLdouble y, GLdouble z)) +GL_FUNCTION(glWindowPos3dvARB,void,(const GLdouble* p)) +GL_FUNCTION(glWindowPos3fARB,void,(GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glWindowPos3fvARB,void,(const GLfloat* p)) +GL_FUNCTION(glWindowPos3iARB,void,(GLint x, GLint y, GLint z)) +GL_FUNCTION(glWindowPos3ivARB,void,(const GLint* p)) +GL_FUNCTION(glWindowPos3sARB,void,(GLshort x, GLshort y, GLshort z)) +GL_FUNCTION(glWindowPos3svARB,void,(const GLshort* p)) +GL_GROUP_END() +#endif + +#ifdef GL_ATIX_point_sprites +GL_GROUP_BEGIN(GL_ATIX_point_sprites) +GL_GROUP_END() +#endif + +#ifdef GL_ATIX_texture_env_combine3 +GL_GROUP_BEGIN(GL_ATIX_texture_env_combine3) +GL_GROUP_END() +#endif + +#ifdef GL_ATIX_texture_env_route +GL_GROUP_BEGIN(GL_ATIX_texture_env_route) +GL_GROUP_END() +#endif + +#ifdef GL_ATIX_vertex_shader_output_point_size +GL_GROUP_BEGIN(GL_ATIX_vertex_shader_output_point_size) +GL_GROUP_END() +#endif + +#ifdef GL_ATI_draw_buffers +GL_GROUP_BEGIN(GL_ATI_draw_buffers) +GL_FUNCTION(glDrawBuffersATI,void,(GLsizei n, const GLenum* bufs)) +GL_GROUP_END() +#endif + +#ifdef GL_ATI_element_array +GL_GROUP_BEGIN(GL_ATI_element_array) +GL_FUNCTION(glDrawElementArrayATI,void,(GLenum mode, GLsizei count)) +GL_FUNCTION(glDrawRangeElementArrayATI,void,(GLenum mode, GLuint start, GLuint end, GLsizei count)) +GL_FUNCTION(glElementPointerATI,void,(GLenum type, const void* pointer)) +GL_GROUP_END() +#endif + +#ifdef GL_ATI_envmap_bumpmap +GL_GROUP_BEGIN(GL_ATI_envmap_bumpmap) +GL_FUNCTION(glTexBumpParameterivATI,void,(GLenum pname, GLint *param)) +GL_FUNCTION(glTexBumpParameterfvATI,void,(GLenum pname, GLfloat *param)) +GL_FUNCTION(glGetTexBumpParameterivATI,void,(GLenum pname, GLint *param)) +GL_FUNCTION(glGetTexBumpParameterfvATI,void,(GLenum pname, GLfloat *param)) +GL_GROUP_END() +#endif + +#ifdef GL_ATI_fragment_shader +GL_GROUP_BEGIN(GL_ATI_fragment_shader) +GL_FUNCTION(glAlphaFragmentOp1ATI,void,(GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod)) +GL_FUNCTION(glAlphaFragmentOp2ATI,void,(GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod)) +GL_FUNCTION(glAlphaFragmentOp3ATI,void,(GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod)) +GL_FUNCTION(glBeginFragmentShaderATI,void,(void)) +GL_FUNCTION(glBindFragmentShaderATI,void,(GLuint id)) +GL_FUNCTION(glColorFragmentOp1ATI,void,(GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod)) +GL_FUNCTION(glColorFragmentOp2ATI,void,(GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod)) +GL_FUNCTION(glColorFragmentOp3ATI,void,(GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod)) +GL_FUNCTION(glDeleteFragmentShaderATI,void,(GLuint id)) +GL_FUNCTION(glEndFragmentShaderATI,void,(void)) +GL_FUNCTION(glGenFragmentShadersATI,GLuint,(GLuint range)) +GL_FUNCTION(glPassTexCoordATI,void,(GLuint dst, GLuint coord, GLenum swizzle)) +GL_FUNCTION(glSampleMapATI,void,(GLuint dst, GLuint interp, GLenum swizzle)) +GL_FUNCTION(glSetFragmentShaderConstantATI,void,(GLuint dst, const GLfloat* value)) +GL_GROUP_END() +#endif + +#ifdef GL_ATI_map_object_buffer +GL_GROUP_BEGIN(GL_ATI_map_object_buffer) +GL_FUNCTION(glMapObjectBufferATI,void*,(GLuint buffer)) +GL_FUNCTION(glUnmapObjectBufferATI,void,(GLuint buffer)) +GL_GROUP_END() +#endif + +#ifdef GL_ATI_pn_triangles +GL_GROUP_BEGIN(GL_ATI_pn_triangles) +GL_FUNCTION(glPNTrianglesiATI,void,(GLenum pname, GLint param)) +GL_FUNCTION(glPNTrianglesfATI,void,(GLenum pname, GLfloat param)) +GL_GROUP_END() +#endif + +#ifdef GL_ATI_separate_stencil +GL_GROUP_BEGIN(GL_ATI_separate_stencil) +GL_FUNCTION(glStencilOpSeparateATI,void,(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass)) +GL_FUNCTION(glStencilFuncSeparateATI,void,(GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask)) +GL_GROUP_END() +#endif + +#ifdef GL_ATI_text_fragment_shader +GL_GROUP_BEGIN(GL_ATI_text_fragment_shader) +GL_GROUP_END() +#endif + +#ifdef GL_ATI_texture_compression_3dc +GL_GROUP_BEGIN(GL_ATI_texture_compression_3dc) +GL_GROUP_END() +#endif + +#ifdef GL_ATI_texture_env_combine3 +GL_GROUP_BEGIN(GL_ATI_texture_env_combine3) +GL_GROUP_END() +#endif + +#ifdef GL_ATI_texture_float +GL_GROUP_BEGIN(GL_ATI_texture_float) +GL_GROUP_END() +#endif + +#ifdef GL_ATI_texture_mirror_once +GL_GROUP_BEGIN(GL_ATI_texture_mirror_once) +GL_GROUP_END() +#endif + +#ifdef GL_ATI_vertex_array_object +GL_GROUP_BEGIN(GL_ATI_vertex_array_object) +GL_FUNCTION(glArrayObjectATI,void,(GLenum array, GLint size, GLenum type, GLsizei stride, GLuint buffer, GLuint offset)) +GL_FUNCTION(glFreeObjectBufferATI,void,(GLuint buffer)) +GL_FUNCTION(glGetArrayObjectfvATI,void,(GLenum array, GLenum pname, GLfloat* params)) +GL_FUNCTION(glGetArrayObjectivATI,void,(GLenum array, GLenum pname, GLint* params)) +GL_FUNCTION(glGetObjectBufferfvATI,void,(GLuint buffer, GLenum pname, GLfloat* params)) +GL_FUNCTION(glGetObjectBufferivATI,void,(GLuint buffer, GLenum pname, GLint* params)) +GL_FUNCTION(glGetVariantArrayObjectfvATI,void,(GLuint id, GLenum pname, GLfloat* params)) +GL_FUNCTION(glGetVariantArrayObjectivATI,void,(GLuint id, GLenum pname, GLint* params)) +GL_FUNCTION(glIsObjectBufferATI,GLboolean,(GLuint buffer)) +GL_FUNCTION(glNewObjectBufferATI,GLuint,(GLsizei size, const void* pointer, GLenum usage)) +GL_FUNCTION(glUpdateObjectBufferATI,void,(GLuint buffer, GLuint offset, GLsizei size, const void* pointer, GLenum preserve)) +GL_FUNCTION(glVariantArrayObjectATI,void,(GLuint id, GLenum type, GLsizei stride, GLuint buffer, GLuint offset)) +GL_GROUP_END() +#endif + +#ifdef GL_ATI_vertex_attrib_array_object +GL_GROUP_BEGIN(GL_ATI_vertex_attrib_array_object) +GL_FUNCTION(glGetVertexAttribArrayObjectfvATI,void,(GLuint index, GLenum pname, GLfloat* params)) +GL_FUNCTION(glGetVertexAttribArrayObjectivATI,void,(GLuint index, GLenum pname, GLint* params)) +GL_FUNCTION(glVertexAttribArrayObjectATI,void,(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLuint buffer, GLuint offset)) +GL_GROUP_END() +#endif + +#ifdef GL_ATI_vertex_streams +GL_GROUP_BEGIN(GL_ATI_vertex_streams) +GL_FUNCTION(glClientActiveVertexStreamATI,void,(GLenum stream)) +GL_FUNCTION(glVertexBlendEnviATI,void,(GLenum pname, GLint param)) +GL_FUNCTION(glVertexBlendEnvfATI,void,(GLenum pname, GLfloat param)) +GL_FUNCTION(glVertexStream2sATI,void,(GLenum stream, GLshort x, GLshort y)) +GL_FUNCTION(glVertexStream2svATI,void,(GLenum stream, const GLshort *v)) +GL_FUNCTION(glVertexStream2iATI,void,(GLenum stream, GLint x, GLint y)) +GL_FUNCTION(glVertexStream2ivATI,void,(GLenum stream, const GLint *v)) +GL_FUNCTION(glVertexStream2fATI,void,(GLenum stream, GLfloat x, GLfloat y)) +GL_FUNCTION(glVertexStream2fvATI,void,(GLenum stream, const GLfloat *v)) +GL_FUNCTION(glVertexStream2dATI,void,(GLenum stream, GLdouble x, GLdouble y)) +GL_FUNCTION(glVertexStream2dvATI,void,(GLenum stream, const GLdouble *v)) +GL_FUNCTION(glVertexStream3sATI,void,(GLenum stream, GLshort x, GLshort y, GLshort z)) +GL_FUNCTION(glVertexStream3svATI,void,(GLenum stream, const GLshort *v)) +GL_FUNCTION(glVertexStream3iATI,void,(GLenum stream, GLint x, GLint y, GLint z)) +GL_FUNCTION(glVertexStream3ivATI,void,(GLenum stream, const GLint *v)) +GL_FUNCTION(glVertexStream3fATI,void,(GLenum stream, GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glVertexStream3fvATI,void,(GLenum stream, const GLfloat *v)) +GL_FUNCTION(glVertexStream3dATI,void,(GLenum stream, GLdouble x, GLdouble y, GLdouble z)) +GL_FUNCTION(glVertexStream3dvATI,void,(GLenum stream, const GLdouble *v)) +GL_FUNCTION(glVertexStream4sATI,void,(GLenum stream, GLshort x, GLshort y, GLshort z, GLshort w)) +GL_FUNCTION(glVertexStream4svATI,void,(GLenum stream, const GLshort *v)) +GL_FUNCTION(glVertexStream4iATI,void,(GLenum stream, GLint x, GLint y, GLint z, GLint w)) +GL_FUNCTION(glVertexStream4ivATI,void,(GLenum stream, const GLint *v)) +GL_FUNCTION(glVertexStream4fATI,void,(GLenum stream, GLfloat x, GLfloat y, GLfloat z, GLfloat w)) +GL_FUNCTION(glVertexStream4fvATI,void,(GLenum stream, const GLfloat *v)) +GL_FUNCTION(glVertexStream4dATI,void,(GLenum stream, GLdouble x, GLdouble y, GLdouble z, GLdouble w)) +GL_FUNCTION(glVertexStream4dvATI,void,(GLenum stream, const GLdouble *v)) +GL_FUNCTION(glNormalStream3bATI,void,(GLenum stream, GLbyte x, GLbyte y, GLbyte z)) +GL_FUNCTION(glNormalStream3bvATI,void,(GLenum stream, const GLbyte *v)) +GL_FUNCTION(glNormalStream3sATI,void,(GLenum stream, GLshort x, GLshort y, GLshort z)) +GL_FUNCTION(glNormalStream3svATI,void,(GLenum stream, const GLshort *v)) +GL_FUNCTION(glNormalStream3iATI,void,(GLenum stream, GLint x, GLint y, GLint z)) +GL_FUNCTION(glNormalStream3ivATI,void,(GLenum stream, const GLint *v)) +GL_FUNCTION(glNormalStream3fATI,void,(GLenum stream, GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glNormalStream3fvATI,void,(GLenum stream, const GLfloat *v)) +GL_FUNCTION(glNormalStream3dATI,void,(GLenum stream, GLdouble x, GLdouble y, GLdouble z)) +GL_FUNCTION(glNormalStream3dvATI,void,(GLenum stream, const GLdouble *v)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_422_pixels +GL_GROUP_BEGIN(GL_EXT_422_pixels) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_Cg_shader +GL_GROUP_BEGIN(GL_EXT_Cg_shader) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_abgr +GL_GROUP_BEGIN(GL_EXT_abgr) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_bgra +GL_GROUP_BEGIN(GL_EXT_bgra) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_blend_color +GL_GROUP_BEGIN(GL_EXT_blend_color) +GL_FUNCTION(glBlendColorEXT,void,(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_blend_equation_separate +GL_GROUP_BEGIN(GL_EXT_blend_equation_separate) +GL_FUNCTION(glBlendEquationSeparateEXT,void,(GLenum modeRGB, GLenum modeAlpha)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_blend_func_separate +GL_GROUP_BEGIN(GL_EXT_blend_func_separate) +GL_FUNCTION(glBlendFuncSeparateEXT,void,(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_blend_logic_op +GL_GROUP_BEGIN(GL_EXT_blend_logic_op) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_blend_minmax +GL_GROUP_BEGIN(GL_EXT_blend_minmax) +GL_FUNCTION(glBlendEquationEXT,void,(GLenum mode)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_blend_subtract +GL_GROUP_BEGIN(GL_EXT_blend_subtract) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_clip_volume_hint +GL_GROUP_BEGIN(GL_EXT_clip_volume_hint) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_cmyka +GL_GROUP_BEGIN(GL_EXT_cmyka) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_color_subtable +GL_GROUP_BEGIN(GL_EXT_color_subtable) +GL_FUNCTION(glColorSubTableEXT,void,(GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void* data)) +GL_FUNCTION(glCopyColorSubTableEXT,void,(GLenum target, GLsizei start, GLint x, GLint y, GLsizei width)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_compiled_vertex_array +GL_GROUP_BEGIN(GL_EXT_compiled_vertex_array) +GL_FUNCTION(glLockArraysEXT,void,(GLint first, GLsizei count)) +GL_FUNCTION(glUnlockArraysEXT,void,(void)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_convolution +GL_GROUP_BEGIN(GL_EXT_convolution) +GL_FUNCTION(glConvolutionFilter1DEXT,void,(GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void* image)) +GL_FUNCTION(glConvolutionFilter2DEXT,void,(GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* image)) +GL_FUNCTION(glConvolutionParameterfEXT,void,(GLenum target, GLenum pname, GLfloat param)) +GL_FUNCTION(glConvolutionParameterfvEXT,void,(GLenum target, GLenum pname, const GLfloat* params)) +GL_FUNCTION(glConvolutionParameteriEXT,void,(GLenum target, GLenum pname, GLint param)) +GL_FUNCTION(glConvolutionParameterivEXT,void,(GLenum target, GLenum pname, const GLint* params)) +GL_FUNCTION(glCopyConvolutionFilter1DEXT,void,(GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width)) +GL_FUNCTION(glCopyConvolutionFilter2DEXT,void,(GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height)) +GL_FUNCTION(glGetConvolutionFilterEXT,void,(GLenum target, GLenum format, GLenum type, void* image)) +GL_FUNCTION(glGetConvolutionParameterfvEXT,void,(GLenum target, GLenum pname, GLfloat* params)) +GL_FUNCTION(glGetConvolutionParameterivEXT,void,(GLenum target, GLenum pname, GLint* params)) +GL_FUNCTION(glGetSeparableFilterEXT,void,(GLenum target, GLenum format, GLenum type, void* row, void* column, void* span)) +GL_FUNCTION(glSeparableFilter2DEXT,void,(GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* row, const void* column)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_coordinate_frame +GL_GROUP_BEGIN(GL_EXT_coordinate_frame) +GL_FUNCTION(glBinormalPointerEXT,void,(GLenum type, GLsizei stride, void* pointer)) +GL_FUNCTION(glTangentPointerEXT,void,(GLenum type, GLsizei stride, void* pointer)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_copy_texture +GL_GROUP_BEGIN(GL_EXT_copy_texture) +GL_FUNCTION(glCopyTexImage1DEXT,void,(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border)) +GL_FUNCTION(glCopyTexImage2DEXT,void,(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border)) +GL_FUNCTION(glCopyTexSubImage1DEXT,void,(GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width)) +GL_FUNCTION(glCopyTexSubImage2DEXT,void,(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height)) +GL_FUNCTION(glCopyTexSubImage3DEXT,void,(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_cull_vertex +GL_GROUP_BEGIN(GL_EXT_cull_vertex) +GL_FUNCTION(glCullParameterdvEXT,void,(GLenum pname, GLdouble* params)) +GL_FUNCTION(glCullParameterfvEXT,void,(GLenum pname, GLfloat* params)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_depth_bounds_test +GL_GROUP_BEGIN(GL_EXT_depth_bounds_test) +GL_FUNCTION(glDepthBoundsEXT,void,(GLclampd zmin, GLclampd zmax)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_draw_range_elements +GL_GROUP_BEGIN(GL_EXT_draw_range_elements) +GL_FUNCTION(glDrawRangeElementsEXT,void,(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_fog_coord +GL_GROUP_BEGIN(GL_EXT_fog_coord) +GL_FUNCTION(glFogCoordfEXT,void,(GLfloat coord)) +GL_FUNCTION(glFogCoordfvEXT,void,(const GLfloat *coord)) +GL_FUNCTION(glFogCoorddEXT,void,(GLdouble coord)) +GL_FUNCTION(glFogCoorddvEXT,void,(const GLdouble *coord)) +GL_FUNCTION(glFogCoordPointerEXT,void,(GLenum type, GLsizei stride, const GLvoid *pointer)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_fragment_lighting +GL_GROUP_BEGIN(GL_EXT_fragment_lighting) +GL_FUNCTION(glFragmentColorMaterialEXT,void,(GLenum face, GLenum mode)) +GL_FUNCTION(glFragmentLightModelfEXT,void,(GLenum pname, GLfloat param)) +GL_FUNCTION(glFragmentLightModelfvEXT,void,(GLenum pname, GLfloat* params)) +GL_FUNCTION(glFragmentLightModeliEXT,void,(GLenum pname, GLint param)) +GL_FUNCTION(glFragmentLightModelivEXT,void,(GLenum pname, GLint* params)) +GL_FUNCTION(glFragmentLightfEXT,void,(GLenum light, GLenum pname, GLfloat param)) +GL_FUNCTION(glFragmentLightfvEXT,void,(GLenum light, GLenum pname, GLfloat* params)) +GL_FUNCTION(glFragmentLightiEXT,void,(GLenum light, GLenum pname, GLint param)) +GL_FUNCTION(glFragmentLightivEXT,void,(GLenum light, GLenum pname, GLint* params)) +GL_FUNCTION(glFragmentMaterialfEXT,void,(GLenum face, GLenum pname, const GLfloat param)) +GL_FUNCTION(glFragmentMaterialfvEXT,void,(GLenum face, GLenum pname, const GLfloat* params)) +GL_FUNCTION(glFragmentMaterialiEXT,void,(GLenum face, GLenum pname, const GLint param)) +GL_FUNCTION(glFragmentMaterialivEXT,void,(GLenum face, GLenum pname, const GLint* params)) +GL_FUNCTION(glGetFragmentLightfvEXT,void,(GLenum light, GLenum pname, GLfloat* params)) +GL_FUNCTION(glGetFragmentLightivEXT,void,(GLenum light, GLenum pname, GLint* params)) +GL_FUNCTION(glGetFragmentMaterialfvEXT,void,(GLenum face, GLenum pname, const GLfloat* params)) +GL_FUNCTION(glGetFragmentMaterialivEXT,void,(GLenum face, GLenum pname, const GLint* params)) +GL_FUNCTION(glLightEnviEXT,void,(GLenum pname, GLint param)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_framebuffer_blit +GL_GROUP_BEGIN(GL_EXT_framebuffer_blit) +GL_FUNCTION(glBlitFramebufferEXT,void,(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum fliter)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_framebuffer_object +GL_GROUP_BEGIN(GL_EXT_framebuffer_object) +GL_FUNCTION(glBindFramebufferEXT,void,(GLenum target, GLuint framebuffer)) +GL_FUNCTION(glBindRenderbufferEXT,void,(GLenum target, GLuint renderbuffer)) +GL_FUNCTION(glCheckFramebufferStatusEXT,GLenum,(GLenum target)) +GL_FUNCTION(glDeleteFramebuffersEXT,void,(GLsizei n, const GLuint* framebuffers)) +GL_FUNCTION(glDeleteRenderbuffersEXT,void,(GLsizei n, const GLuint* renderbuffers)) +GL_FUNCTION(glFramebufferRenderbufferEXT,void,(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)) +GL_FUNCTION(glFramebufferTexture1DEXT,void,(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)) +GL_FUNCTION(glFramebufferTexture2DEXT,void,(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)) +GL_FUNCTION(glFramebufferTexture3DEXT,void,(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset)) +GL_FUNCTION(glGenFramebuffersEXT,void,(GLsizei n, GLuint* framebuffers)) +GL_FUNCTION(glGenRenderbuffersEXT,void,(GLsizei n, GLuint* renderbuffers)) +GL_FUNCTION(glGenerateMipmapEXT,void,(GLenum target)) +GL_FUNCTION(glGetFramebufferAttachmentParameterivEXT,void,(GLenum target, GLenum attachment, GLenum pname, GLint* params)) +GL_FUNCTION(glGetRenderbufferParameterivEXT,void,(GLenum target, GLenum pname, GLint* params)) +GL_FUNCTION(glIsFramebufferEXT,GLboolean,(GLuint framebuffer)) +GL_FUNCTION(glIsRenderbufferEXT,GLboolean,(GLuint renderbuffer)) +GL_FUNCTION(glRenderbufferStorageEXT,void,(GLenum target, GLenum internalformat, GLsizei width, GLsizei height)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_histogram +GL_GROUP_BEGIN(GL_EXT_histogram) +GL_FUNCTION(glGetHistogramEXT,void,(GLenum target, GLboolean reset, GLenum format, GLenum type, void* values)) +GL_FUNCTION(glGetHistogramParameterfvEXT,void,(GLenum target, GLenum pname, GLfloat* params)) +GL_FUNCTION(glGetHistogramParameterivEXT,void,(GLenum target, GLenum pname, GLint* params)) +GL_FUNCTION(glGetMinmaxEXT,void,(GLenum target, GLboolean reset, GLenum format, GLenum type, void* values)) +GL_FUNCTION(glGetMinmaxParameterfvEXT,void,(GLenum target, GLenum pname, GLfloat* params)) +GL_FUNCTION(glGetMinmaxParameterivEXT,void,(GLenum target, GLenum pname, GLint* params)) +GL_FUNCTION(glHistogramEXT,void,(GLenum target, GLsizei width, GLenum internalformat, GLboolean sink)) +GL_FUNCTION(glMinmaxEXT,void,(GLenum target, GLenum internalformat, GLboolean sink)) +GL_FUNCTION(glResetHistogramEXT,void,(GLenum target)) +GL_FUNCTION(glResetMinmaxEXT,void,(GLenum target)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_index_array_formats +GL_GROUP_BEGIN(GL_EXT_index_array_formats) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_index_func +GL_GROUP_BEGIN(GL_EXT_index_func) +GL_FUNCTION(glIndexFuncEXT,void,(GLenum func, GLfloat ref)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_index_material +GL_GROUP_BEGIN(GL_EXT_index_material) +GL_FUNCTION(glIndexMaterialEXT,void,(GLenum face, GLenum mode)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_index_texture +GL_GROUP_BEGIN(GL_EXT_index_texture) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_light_texture +GL_GROUP_BEGIN(GL_EXT_light_texture) +GL_FUNCTION(glApplyTextureEXT,void,(GLenum mode)) +GL_FUNCTION(glTextureLightEXT,void,(GLenum pname)) +GL_FUNCTION(glTextureMaterialEXT,void,(GLenum face, GLenum mode)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_misc_attribute +GL_GROUP_BEGIN(GL_EXT_misc_attribute) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_multi_draw_arrays +GL_GROUP_BEGIN(GL_EXT_multi_draw_arrays) +GL_FUNCTION(glMultiDrawArraysEXT,void,(GLenum mode, GLint* first, GLsizei *count, GLsizei primcount)) +GL_FUNCTION(glMultiDrawElementsEXT,void,(GLenum mode, GLsizei* count, GLenum type, const GLvoid **indices, GLsizei primcount)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_multisample +GL_GROUP_BEGIN(GL_EXT_multisample) +GL_FUNCTION(glSampleMaskEXT,void,(GLclampf value, GLboolean invert)) +GL_FUNCTION(glSamplePatternEXT,void,(GLenum pattern)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_packed_pixels +GL_GROUP_BEGIN(GL_EXT_packed_pixels) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_paletted_texture +GL_GROUP_BEGIN(GL_EXT_paletted_texture) +GL_FUNCTION(glColorTableEXT,void,(GLenum target, GLenum internalFormat, GLsizei width, GLenum format, GLenum type, const void* data)) +GL_FUNCTION(glGetColorTableEXT,void,(GLenum target, GLenum format, GLenum type, void* data)) +GL_FUNCTION(glGetColorTableParameterfvEXT,void,(GLenum target, GLenum pname, GLfloat* params)) +GL_FUNCTION(glGetColorTableParameterivEXT,void,(GLenum target, GLenum pname, GLint* params)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_pixel_buffer_object +GL_GROUP_BEGIN(GL_EXT_pixel_buffer_object) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_pixel_transform +GL_GROUP_BEGIN(GL_EXT_pixel_transform) +GL_FUNCTION(glGetPixelTransformParameterfvEXT,void,(GLenum target, GLenum pname, const GLfloat* params)) +GL_FUNCTION(glGetPixelTransformParameterivEXT,void,(GLenum target, GLenum pname, const GLint* params)) +GL_FUNCTION(glPixelTransformParameterfEXT,void,(GLenum target, GLenum pname, const GLfloat param)) +GL_FUNCTION(glPixelTransformParameterfvEXT,void,(GLenum target, GLenum pname, const GLfloat* params)) +GL_FUNCTION(glPixelTransformParameteriEXT,void,(GLenum target, GLenum pname, const GLint param)) +GL_FUNCTION(glPixelTransformParameterivEXT,void,(GLenum target, GLenum pname, const GLint* params)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_pixel_transform_color_table +GL_GROUP_BEGIN(GL_EXT_pixel_transform_color_table) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_point_parameters +GL_GROUP_BEGIN(GL_EXT_point_parameters) +GL_FUNCTION(glPointParameterfEXT,void,(GLenum pname, GLfloat param)) +GL_FUNCTION(glPointParameterfvEXT,void,(GLenum pname, GLfloat* params)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_polygon_offset +GL_GROUP_BEGIN(GL_EXT_polygon_offset) +GL_FUNCTION(glPolygonOffsetEXT,void,(GLfloat factor, GLfloat bias)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_rescale_normal +GL_GROUP_BEGIN(GL_EXT_rescale_normal) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_scene_marker +GL_GROUP_BEGIN(GL_EXT_scene_marker) +GL_FUNCTION(glBeginSceneEXT,void,(void)) +GL_FUNCTION(glEndSceneEXT,void,(void)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_secondary_color +GL_GROUP_BEGIN(GL_EXT_secondary_color) +GL_FUNCTION(glSecondaryColor3bEXT,void,(GLbyte red, GLbyte green, GLbyte blue)) +GL_FUNCTION(glSecondaryColor3bvEXT,void,(const GLbyte *v)) +GL_FUNCTION(glSecondaryColor3dEXT,void,(GLdouble red, GLdouble green, GLdouble blue)) +GL_FUNCTION(glSecondaryColor3dvEXT,void,(const GLdouble *v)) +GL_FUNCTION(glSecondaryColor3fEXT,void,(GLfloat red, GLfloat green, GLfloat blue)) +GL_FUNCTION(glSecondaryColor3fvEXT,void,(const GLfloat *v)) +GL_FUNCTION(glSecondaryColor3iEXT,void,(GLint red, GLint green, GLint blue)) +GL_FUNCTION(glSecondaryColor3ivEXT,void,(const GLint *v)) +GL_FUNCTION(glSecondaryColor3sEXT,void,(GLshort red, GLshort green, GLshort blue)) +GL_FUNCTION(glSecondaryColor3svEXT,void,(const GLshort *v)) +GL_FUNCTION(glSecondaryColor3ubEXT,void,(GLubyte red, GLubyte green, GLubyte blue)) +GL_FUNCTION(glSecondaryColor3ubvEXT,void,(const GLubyte *v)) +GL_FUNCTION(glSecondaryColor3uiEXT,void,(GLuint red, GLuint green, GLuint blue)) +GL_FUNCTION(glSecondaryColor3uivEXT,void,(const GLuint *v)) +GL_FUNCTION(glSecondaryColor3usEXT,void,(GLushort red, GLushort green, GLushort blue)) +GL_FUNCTION(glSecondaryColor3usvEXT,void,(const GLushort *v)) +GL_FUNCTION(glSecondaryColorPointerEXT,void,(GLint size, GLenum type, GLsizei stride, GLvoid *pointer)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_separate_specular_color +GL_GROUP_BEGIN(GL_EXT_separate_specular_color) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_shadow_funcs +GL_GROUP_BEGIN(GL_EXT_shadow_funcs) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_shared_texture_palette +GL_GROUP_BEGIN(GL_EXT_shared_texture_palette) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_stencil_two_side +GL_GROUP_BEGIN(GL_EXT_stencil_two_side) +GL_FUNCTION(glActiveStencilFaceEXT,void,(GLenum face)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_stencil_wrap +GL_GROUP_BEGIN(GL_EXT_stencil_wrap) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_subtexture +GL_GROUP_BEGIN(GL_EXT_subtexture) +GL_FUNCTION(glTexSubImage1DEXT,void,(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void* pixels)) +GL_FUNCTION(glTexSubImage2DEXT,void,(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels)) +GL_FUNCTION(glTexSubImage3DEXT,void,(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void* pixels)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_texture +GL_GROUP_BEGIN(GL_EXT_texture) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_texture3D +GL_GROUP_BEGIN(GL_EXT_texture3D) +GL_FUNCTION(glTexImage3DEXT,void,(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void* pixels)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_texture_compression_dxt1 +GL_GROUP_BEGIN(GL_EXT_texture_compression_dxt1) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_texture_compression_s3tc +GL_GROUP_BEGIN(GL_EXT_texture_compression_s3tc) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_texture_cube_map +GL_GROUP_BEGIN(GL_EXT_texture_cube_map) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_texture_edge_clamp +GL_GROUP_BEGIN(GL_EXT_texture_edge_clamp) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_texture_env +GL_GROUP_BEGIN(GL_EXT_texture_env) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_texture_env_add +GL_GROUP_BEGIN(GL_EXT_texture_env_add) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_texture_env_combine +GL_GROUP_BEGIN(GL_EXT_texture_env_combine) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_texture_env_dot3 +GL_GROUP_BEGIN(GL_EXT_texture_env_dot3) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_texture_filter_anisotropic +GL_GROUP_BEGIN(GL_EXT_texture_filter_anisotropic) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_texture_lod_bias +GL_GROUP_BEGIN(GL_EXT_texture_lod_bias) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_texture_mirror_clamp +GL_GROUP_BEGIN(GL_EXT_texture_mirror_clamp) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_texture_object +GL_GROUP_BEGIN(GL_EXT_texture_object) +GL_FUNCTION(glAreTexturesResidentEXT,GLboolean,(GLsizei n, const GLuint* textures, GLboolean* residences)) +GL_FUNCTION(glBindTextureEXT,void,(GLenum target, GLuint texture)) +GL_FUNCTION(glDeleteTexturesEXT,void,(GLsizei n, const GLuint* textures)) +GL_FUNCTION(glGenTexturesEXT,void,(GLsizei n, GLuint* textures)) +GL_FUNCTION(glIsTextureEXT,GLboolean,(GLuint texture)) +GL_FUNCTION(glPrioritizeTexturesEXT,void,(GLsizei n, const GLuint* textures, const GLclampf* priorities)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_texture_perturb_normal +GL_GROUP_BEGIN(GL_EXT_texture_perturb_normal) +GL_FUNCTION(glTextureNormalEXT,void,(GLenum mode)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_texture_rectangle +GL_GROUP_BEGIN(GL_EXT_texture_rectangle) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_vertex_array +GL_GROUP_BEGIN(GL_EXT_vertex_array) +GL_FUNCTION(glArrayElementEXT,void,(GLint i)) +GL_FUNCTION(glColorPointerEXT,void,(GLint size, GLenum type, GLsizei stride, GLsizei count, const void* pointer)) +GL_FUNCTION(glDrawArraysEXT,void,(GLenum mode, GLint first, GLsizei count)) +GL_FUNCTION(glEdgeFlagPointerEXT,void,(GLsizei stride, GLsizei count, const GLboolean* pointer)) +GL_FUNCTION(glGetPointervEXT,void,(GLenum pname, void** params)) +GL_FUNCTION(glIndexPointerEXT,void,(GLenum type, GLsizei stride, GLsizei count, const void* pointer)) +GL_FUNCTION(glNormalPointerEXT,void,(GLenum type, GLsizei stride, GLsizei count, const void* pointer)) +GL_FUNCTION(glTexCoordPointerEXT,void,(GLint size, GLenum type, GLsizei stride, GLsizei count, const void* pointer)) +GL_FUNCTION(glVertexPointerEXT,void,(GLint size, GLenum type, GLsizei stride, GLsizei count, const void* pointer)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_vertex_shader +GL_GROUP_BEGIN(GL_EXT_vertex_shader) +GL_FUNCTION(glBeginVertexShaderEXT,void,(void)) +GL_FUNCTION(glEndVertexShaderEXT,void,(void)) +GL_FUNCTION(glBindVertexShaderEXT,void,(GLuint id)) +GL_FUNCTION(glGenVertexShadersEXT,GLuint,(GLuint range)) +GL_FUNCTION(glDeleteVertexShaderEXT,void,(GLuint id)) +GL_FUNCTION(glShaderOp1EXT,void,(GLenum op, GLuint res, GLuint arg1)) +GL_FUNCTION(glShaderOp2EXT,void,(GLenum op, GLuint res, GLuint arg1, GLuint arg2)) +GL_FUNCTION(glShaderOp3EXT,void,(GLenum op, GLuint res, GLuint arg1, GLuint arg2, GLuint arg3)) +GL_FUNCTION(glSwizzleEXT,void,(GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW)) +GL_FUNCTION(glWriteMaskEXT,void,(GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW)) +GL_FUNCTION(glInsertComponentEXT,void,(GLuint res, GLuint src, GLuint num)) +GL_FUNCTION(glExtractComponentEXT,void,(GLuint res, GLuint src, GLuint num)) +GL_FUNCTION(glGenSymbolsEXT,GLuint,(GLenum dataType, GLenum storageType, GLenum range, GLuint components)) +GL_FUNCTION(glSetInvariantEXT,void,(GLuint id, GLenum type, GLvoid *addr)) +GL_FUNCTION(glSetLocalConstantEXT,void,(GLuint id, GLenum type, GLvoid *addr)) +GL_FUNCTION(glVariantbvEXT,void,(GLuint id, GLbyte *addr)) +GL_FUNCTION(glVariantsvEXT,void,(GLuint id, GLshort *addr)) +GL_FUNCTION(glVariantivEXT,void,(GLuint id, GLint *addr)) +GL_FUNCTION(glVariantfvEXT,void,(GLuint id, GLfloat *addr)) +GL_FUNCTION(glVariantdvEXT,void,(GLuint id, GLdouble *addr)) +GL_FUNCTION(glVariantubvEXT,void,(GLuint id, GLubyte *addr)) +GL_FUNCTION(glVariantusvEXT,void,(GLuint id, GLushort *addr)) +GL_FUNCTION(glVariantuivEXT,void,(GLuint id, GLuint *addr)) +GL_FUNCTION(glVariantPointerEXT,void,(GLuint id, GLenum type, GLuint stride, GLvoid *addr)) +GL_FUNCTION(glEnableVariantClientStateEXT,void,(GLuint id)) +GL_FUNCTION(glDisableVariantClientStateEXT,void,(GLuint id)) +GL_FUNCTION(glBindLightParameterEXT,GLuint,(GLenum light, GLenum value)) +GL_FUNCTION(glBindMaterialParameterEXT,GLuint,(GLenum face, GLenum value)) +GL_FUNCTION(glBindTexGenParameterEXT,GLuint,(GLenum unit, GLenum coord, GLenum value)) +GL_FUNCTION(glBindTextureUnitParameterEXT,GLuint,(GLenum unit, GLenum value)) +GL_FUNCTION(glBindParameterEXT,GLuint,(GLenum value)) +GL_FUNCTION(glIsVariantEnabledEXT,GLboolean,(GLuint id, GLenum cap)) +GL_FUNCTION(glGetVariantBooleanvEXT,void,(GLuint id, GLenum value, GLboolean *data)) +GL_FUNCTION(glGetVariantIntegervEXT,void,(GLuint id, GLenum value, GLint *data)) +GL_FUNCTION(glGetVariantFloatvEXT,void,(GLuint id, GLenum value, GLfloat *data)) +GL_FUNCTION(glGetVariantPointervEXT,void,(GLuint id, GLenum value, GLvoid **data)) +GL_FUNCTION(glGetInvariantBooleanvEXT,void,(GLuint id, GLenum value, GLboolean *data)) +GL_FUNCTION(glGetInvariantIntegervEXT,void,(GLuint id, GLenum value, GLint *data)) +GL_FUNCTION(glGetInvariantFloatvEXT,void,(GLuint id, GLenum value, GLfloat *data)) +GL_FUNCTION(glGetLocalConstantBooleanvEXT,void,(GLuint id, GLenum value, GLboolean *data)) +GL_FUNCTION(glGetLocalConstantIntegervEXT,void,(GLuint id, GLenum value, GLint *data)) +GL_FUNCTION(glGetLocalConstantFloatvEXT,void,(GLuint id, GLenum value, GLfloat *data)) +GL_GROUP_END() +#endif + +#ifdef GL_EXT_vertex_weighting +GL_GROUP_BEGIN(GL_EXT_vertex_weighting) +GL_FUNCTION(glVertexWeightPointerEXT,void,(GLint size, GLenum type, GLsizei stride, void* pointer)) +GL_FUNCTION(glVertexWeightfEXT,void,(GLfloat weight)) +GL_FUNCTION(glVertexWeightfvEXT,void,(GLfloat* weight)) +GL_GROUP_END() +#endif + +#ifdef GL_GREMEDY_string_marker +GL_GROUP_BEGIN(GL_GREMEDY_string_marker) +GL_FUNCTION(glStringMarkerGREMEDY,void,(GLsizei len, const void* string)) +GL_GROUP_END() +#endif + +#ifdef GL_HP_convolution_border_modes +GL_GROUP_BEGIN(GL_HP_convolution_border_modes) +GL_GROUP_END() +#endif + +#ifdef GL_HP_image_transform +GL_GROUP_BEGIN(GL_HP_image_transform) +GL_FUNCTION(glGetImageTransformParameterfvHP,void,(GLenum target, GLenum pname, const GLfloat* params)) +GL_FUNCTION(glGetImageTransformParameterivHP,void,(GLenum target, GLenum pname, const GLint* params)) +GL_FUNCTION(glImageTransformParameterfHP,void,(GLenum target, GLenum pname, const GLfloat param)) +GL_FUNCTION(glImageTransformParameterfvHP,void,(GLenum target, GLenum pname, const GLfloat* params)) +GL_FUNCTION(glImageTransformParameteriHP,void,(GLenum target, GLenum pname, const GLint param)) +GL_FUNCTION(glImageTransformParameterivHP,void,(GLenum target, GLenum pname, const GLint* params)) +GL_GROUP_END() +#endif + +#ifdef GL_HP_occlusion_test +GL_GROUP_BEGIN(GL_HP_occlusion_test) +GL_GROUP_END() +#endif + +#ifdef GL_HP_texture_lighting +GL_GROUP_BEGIN(GL_HP_texture_lighting) +GL_GROUP_END() +#endif + +#ifdef GL_IBM_cull_vertex +GL_GROUP_BEGIN(GL_IBM_cull_vertex) +GL_GROUP_END() +#endif + +#ifdef GL_IBM_multimode_draw_arrays +GL_GROUP_BEGIN(GL_IBM_multimode_draw_arrays) +GL_FUNCTION(glMultiModeDrawArraysIBM,void,(const GLenum* mode, const GLint *first, const GLsizei *count, GLsizei primcount, GLint modestride)) +GL_FUNCTION(glMultiModeDrawElementsIBM,void,(const GLenum* mode, const GLsizei *count, GLenum type, const GLvoid * const *indices, GLsizei primcount, GLint modestride)) +GL_GROUP_END() +#endif + +#ifdef GL_IBM_rasterpos_clip +GL_GROUP_BEGIN(GL_IBM_rasterpos_clip) +GL_GROUP_END() +#endif + +#ifdef GL_IBM_static_data +GL_GROUP_BEGIN(GL_IBM_static_data) +GL_GROUP_END() +#endif + +#ifdef GL_IBM_texture_mirrored_repeat +GL_GROUP_BEGIN(GL_IBM_texture_mirrored_repeat) +GL_GROUP_END() +#endif + +#ifdef GL_IBM_vertex_array_lists +GL_GROUP_BEGIN(GL_IBM_vertex_array_lists) +GL_FUNCTION(glColorPointerListIBM,void,(GLint size, GLenum type, GLint stride, const GLvoid ** pointer, GLint ptrstride)) +GL_FUNCTION(glEdgeFlagPointerListIBM,void,(GLint stride, const GLboolean ** pointer, GLint ptrstride)) +GL_FUNCTION(glFogCoordPointerListIBM,void,(GLenum type, GLint stride, const GLvoid ** pointer, GLint ptrstride)) +GL_FUNCTION(glIndexPointerListIBM,void,(GLenum type, GLint stride, const GLvoid ** pointer, GLint ptrstride)) +GL_FUNCTION(glNormalPointerListIBM,void,(GLenum type, GLint stride, const GLvoid ** pointer, GLint ptrstride)) +GL_FUNCTION(glSecondaryColorPointerListIBM,void,(GLint size, GLenum type, GLint stride, const GLvoid ** pointer, GLint ptrstride)) +GL_FUNCTION(glTexCoordPointerListIBM,void,(GLint size, GLenum type, GLint stride, const GLvoid ** pointer, GLint ptrstride)) +GL_FUNCTION(glVertexPointerListIBM,void,(GLint size, GLenum type, GLint stride, const GLvoid ** pointer, GLint ptrstride)) +GL_GROUP_END() +#endif + +#ifdef GL_INGR_color_clamp +GL_GROUP_BEGIN(GL_INGR_color_clamp) +GL_GROUP_END() +#endif + +#ifdef GL_INGR_interlace_read +GL_GROUP_BEGIN(GL_INGR_interlace_read) +GL_GROUP_END() +#endif + +#ifdef GL_INTEL_parallel_arrays +GL_GROUP_BEGIN(GL_INTEL_parallel_arrays) +GL_FUNCTION(glColorPointervINTEL,void,(GLint size, GLenum type, const void** pointer)) +GL_FUNCTION(glNormalPointervINTEL,void,(GLenum type, const void** pointer)) +GL_FUNCTION(glTexCoordPointervINTEL,void,(GLint size, GLenum type, const void** pointer)) +GL_FUNCTION(glVertexPointervINTEL,void,(GLint size, GLenum type, const void** pointer)) +GL_GROUP_END() +#endif + +#ifdef GL_INTEL_texture_scissor +GL_GROUP_BEGIN(GL_INTEL_texture_scissor) +GL_FUNCTION(glTexScissorFuncINTEL,void,(GLenum target, GLenum lfunc, GLenum hfunc)) +GL_FUNCTION(glTexScissorINTEL,void,(GLenum target, GLclampf tlow, GLclampf thigh)) +GL_GROUP_END() +#endif + +#ifdef GL_KTX_buffer_region +GL_GROUP_BEGIN(GL_KTX_buffer_region) +GL_FUNCTION(glBufferRegionEnabledEXT,GLuint,(void)) +GL_FUNCTION(glNewBufferRegionEXT,GLuint,(GLenum region)) +GL_FUNCTION(glDeleteBufferRegionEXT,void,(GLenum region)) +GL_FUNCTION(glReadBufferRegionEXT,void,(GLuint region, GLint x, GLint y, GLsizei width, GLsizei height)) +GL_FUNCTION(glDrawBufferRegionEXT,void,(GLuint region, GLint x, GLint y, GLsizei width, GLsizei height, GLint xDest, GLint yDest)) +GL_GROUP_END() +#endif + +#ifdef GL_MESA_pack_invert +GL_GROUP_BEGIN(GL_MESA_pack_invert) +GL_GROUP_END() +#endif + +#ifdef GL_MESA_resize_buffers +GL_GROUP_BEGIN(GL_MESA_resize_buffers) +GL_FUNCTION(glResizeBuffersMESA,void,(void)) +GL_GROUP_END() +#endif + +#ifdef GL_MESA_window_pos +GL_GROUP_BEGIN(GL_MESA_window_pos) +GL_FUNCTION(glWindowPos2dMESA,void,(GLdouble x, GLdouble y)) +GL_FUNCTION(glWindowPos2dvMESA,void,(const GLdouble* p)) +GL_FUNCTION(glWindowPos2fMESA,void,(GLfloat x, GLfloat y)) +GL_FUNCTION(glWindowPos2fvMESA,void,(const GLfloat* p)) +GL_FUNCTION(glWindowPos2iMESA,void,(GLint x, GLint y)) +GL_FUNCTION(glWindowPos2ivMESA,void,(const GLint* p)) +GL_FUNCTION(glWindowPos2sMESA,void,(GLshort x, GLshort y)) +GL_FUNCTION(glWindowPos2svMESA,void,(const GLshort* p)) +GL_FUNCTION(glWindowPos3dMESA,void,(GLdouble x, GLdouble y, GLdouble z)) +GL_FUNCTION(glWindowPos3dvMESA,void,(const GLdouble* p)) +GL_FUNCTION(glWindowPos3fMESA,void,(GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glWindowPos3fvMESA,void,(const GLfloat* p)) +GL_FUNCTION(glWindowPos3iMESA,void,(GLint x, GLint y, GLint z)) +GL_FUNCTION(glWindowPos3ivMESA,void,(const GLint* p)) +GL_FUNCTION(glWindowPos3sMESA,void,(GLshort x, GLshort y, GLshort z)) +GL_FUNCTION(glWindowPos3svMESA,void,(const GLshort* p)) +GL_FUNCTION(glWindowPos4dMESA,void,(GLdouble x, GLdouble y, GLdouble z, GLdouble)) +GL_FUNCTION(glWindowPos4dvMESA,void,(const GLdouble* p)) +GL_FUNCTION(glWindowPos4fMESA,void,(GLfloat x, GLfloat y, GLfloat z, GLfloat w)) +GL_FUNCTION(glWindowPos4fvMESA,void,(const GLfloat* p)) +GL_FUNCTION(glWindowPos4iMESA,void,(GLint x, GLint y, GLint z, GLint w)) +GL_FUNCTION(glWindowPos4ivMESA,void,(const GLint* p)) +GL_FUNCTION(glWindowPos4sMESA,void,(GLshort x, GLshort y, GLshort z, GLshort w)) +GL_FUNCTION(glWindowPos4svMESA,void,(const GLshort* p)) +GL_GROUP_END() +#endif + +#ifdef GL_MESA_ycbcr_texture +GL_GROUP_BEGIN(GL_MESA_ycbcr_texture) +GL_GROUP_END() +#endif + +#ifdef GL_NV_blend_square +GL_GROUP_BEGIN(GL_NV_blend_square) +GL_GROUP_END() +#endif + +#ifdef GL_NV_copy_depth_to_color +GL_GROUP_BEGIN(GL_NV_copy_depth_to_color) +GL_GROUP_END() +#endif + +#ifdef GL_NV_depth_clamp +GL_GROUP_BEGIN(GL_NV_depth_clamp) +GL_GROUP_END() +#endif + +#ifdef GL_NV_evaluators +GL_GROUP_BEGIN(GL_NV_evaluators) +GL_FUNCTION(glEvalMapsNV,void,(GLenum target, GLenum mode)) +GL_FUNCTION(glGetMapAttribParameterfvNV,void,(GLenum target, GLuint index, GLenum pname, GLfloat* params)) +GL_FUNCTION(glGetMapAttribParameterivNV,void,(GLenum target, GLuint index, GLenum pname, GLint* params)) +GL_FUNCTION(glGetMapControlPointsNV,void,(GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLboolean packed, void* points)) +GL_FUNCTION(glGetMapParameterfvNV,void,(GLenum target, GLenum pname, GLfloat* params)) +GL_FUNCTION(glGetMapParameterivNV,void,(GLenum target, GLenum pname, GLint* params)) +GL_FUNCTION(glMapControlPointsNV,void,(GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLint uorder, GLint vorder, GLboolean packed, const void* points)) +GL_FUNCTION(glMapParameterfvNV,void,(GLenum target, GLenum pname, const GLfloat* params)) +GL_FUNCTION(glMapParameterivNV,void,(GLenum target, GLenum pname, const GLint* params)) +GL_GROUP_END() +#endif + +#ifdef GL_NV_fence +GL_GROUP_BEGIN(GL_NV_fence) +GL_FUNCTION(glDeleteFencesNV,void,(GLsizei n, const GLuint* fences)) +GL_FUNCTION(glFinishFenceNV,void,(GLuint fence)) +GL_FUNCTION(glGenFencesNV,void,(GLsizei n, GLuint* fences)) +GL_FUNCTION(glGetFenceivNV,void,(GLuint fence, GLenum pname, GLint* params)) +GL_FUNCTION(glIsFenceNV,GLboolean,(GLuint fence)) +GL_FUNCTION(glSetFenceNV,void,(GLuint fence, GLenum condition)) +GL_FUNCTION(glTestFenceNV,GLboolean,(GLuint fence)) +GL_GROUP_END() +#endif + +#ifdef GL_NV_float_buffer +GL_GROUP_BEGIN(GL_NV_float_buffer) +GL_GROUP_END() +#endif + +#ifdef GL_NV_fog_distance +GL_GROUP_BEGIN(GL_NV_fog_distance) +GL_GROUP_END() +#endif + +#ifdef GL_NV_fragment_program +GL_GROUP_BEGIN(GL_NV_fragment_program) +GL_FUNCTION(glGetProgramNamedParameterdvNV,void,(GLuint id, GLsizei len, const GLubyte* name, GLdouble *params)) +GL_FUNCTION(glGetProgramNamedParameterfvNV,void,(GLuint id, GLsizei len, const GLubyte* name, GLfloat *params)) +GL_FUNCTION(glProgramNamedParameter4dNV,void,(GLuint id, GLsizei len, const GLubyte* name, GLdouble x, GLdouble y, GLdouble z, GLdouble w)) +GL_FUNCTION(glProgramNamedParameter4dvNV,void,(GLuint id, GLsizei len, const GLubyte* name, const GLdouble v[])) +GL_FUNCTION(glProgramNamedParameter4fNV,void,(GLuint id, GLsizei len, const GLubyte* name, GLfloat x, GLfloat y, GLfloat z, GLfloat w)) +GL_FUNCTION(glProgramNamedParameter4fvNV,void,(GLuint id, GLsizei len, const GLubyte* name, const GLfloat v[])) +GL_GROUP_END() +#endif + +#ifdef GL_NV_fragment_program2 +GL_GROUP_BEGIN(GL_NV_fragment_program2) +GL_GROUP_END() +#endif + +#ifdef GL_NV_fragment_program_option +GL_GROUP_BEGIN(GL_NV_fragment_program_option) +GL_GROUP_END() +#endif + +#ifdef GL_NV_half_float +GL_GROUP_BEGIN(GL_NV_half_float) +GL_FUNCTION(glColor3hNV,void,(GLhalf red, GLhalf green, GLhalf blue)) +GL_FUNCTION(glColor3hvNV,void,(const GLhalf* v)) +GL_FUNCTION(glColor4hNV,void,(GLhalf red, GLhalf green, GLhalf blue, GLhalf alpha)) +GL_FUNCTION(glColor4hvNV,void,(const GLhalf* v)) +GL_FUNCTION(glFogCoordhNV,void,(GLhalf fog)) +GL_FUNCTION(glFogCoordhvNV,void,(const GLhalf* fog)) +GL_FUNCTION(glMultiTexCoord1hNV,void,(GLenum target, GLhalf s)) +GL_FUNCTION(glMultiTexCoord1hvNV,void,(GLenum target, const GLhalf* v)) +GL_FUNCTION(glMultiTexCoord2hNV,void,(GLenum target, GLhalf s, GLhalf t)) +GL_FUNCTION(glMultiTexCoord2hvNV,void,(GLenum target, const GLhalf* v)) +GL_FUNCTION(glMultiTexCoord3hNV,void,(GLenum target, GLhalf s, GLhalf t, GLhalf r)) +GL_FUNCTION(glMultiTexCoord3hvNV,void,(GLenum target, const GLhalf* v)) +GL_FUNCTION(glMultiTexCoord4hNV,void,(GLenum target, GLhalf s, GLhalf t, GLhalf r, GLhalf q)) +GL_FUNCTION(glMultiTexCoord4hvNV,void,(GLenum target, const GLhalf* v)) +GL_FUNCTION(glNormal3hNV,void,(GLhalf nx, GLhalf ny, GLhalf nz)) +GL_FUNCTION(glNormal3hvNV,void,(const GLhalf* v)) +GL_FUNCTION(glSecondaryColor3hNV,void,(GLhalf red, GLhalf green, GLhalf blue)) +GL_FUNCTION(glSecondaryColor3hvNV,void,(const GLhalf* v)) +GL_FUNCTION(glTexCoord1hNV,void,(GLhalf s)) +GL_FUNCTION(glTexCoord1hvNV,void,(const GLhalf* v)) +GL_FUNCTION(glTexCoord2hNV,void,(GLhalf s, GLhalf t)) +GL_FUNCTION(glTexCoord2hvNV,void,(const GLhalf* v)) +GL_FUNCTION(glTexCoord3hNV,void,(GLhalf s, GLhalf t, GLhalf r)) +GL_FUNCTION(glTexCoord3hvNV,void,(const GLhalf* v)) +GL_FUNCTION(glTexCoord4hNV,void,(GLhalf s, GLhalf t, GLhalf r, GLhalf q)) +GL_FUNCTION(glTexCoord4hvNV,void,(const GLhalf* v)) +GL_FUNCTION(glVertex2hNV,void,(GLhalf x, GLhalf y)) +GL_FUNCTION(glVertex2hvNV,void,(const GLhalf* v)) +GL_FUNCTION(glVertex3hNV,void,(GLhalf x, GLhalf y, GLhalf z)) +GL_FUNCTION(glVertex3hvNV,void,(const GLhalf* v)) +GL_FUNCTION(glVertex4hNV,void,(GLhalf x, GLhalf y, GLhalf z, GLhalf w)) +GL_FUNCTION(glVertex4hvNV,void,(const GLhalf* v)) +GL_FUNCTION(glVertexAttrib1hNV,void,(GLuint index, GLhalf x)) +GL_FUNCTION(glVertexAttrib1hvNV,void,(GLuint index, const GLhalf* v)) +GL_FUNCTION(glVertexAttrib2hNV,void,(GLuint index, GLhalf x, GLhalf y)) +GL_FUNCTION(glVertexAttrib2hvNV,void,(GLuint index, const GLhalf* v)) +GL_FUNCTION(glVertexAttrib3hNV,void,(GLuint index, GLhalf x, GLhalf y, GLhalf z)) +GL_FUNCTION(glVertexAttrib3hvNV,void,(GLuint index, const GLhalf* v)) +GL_FUNCTION(glVertexAttrib4hNV,void,(GLuint index, GLhalf x, GLhalf y, GLhalf z, GLhalf w)) +GL_FUNCTION(glVertexAttrib4hvNV,void,(GLuint index, const GLhalf* v)) +GL_FUNCTION(glVertexAttribs1hvNV,void,(GLuint index, GLsizei n, const GLhalf* v)) +GL_FUNCTION(glVertexAttribs2hvNV,void,(GLuint index, GLsizei n, const GLhalf* v)) +GL_FUNCTION(glVertexAttribs3hvNV,void,(GLuint index, GLsizei n, const GLhalf* v)) +GL_FUNCTION(glVertexAttribs4hvNV,void,(GLuint index, GLsizei n, const GLhalf* v)) +GL_FUNCTION(glVertexWeighthNV,void,(GLhalf weight)) +GL_FUNCTION(glVertexWeighthvNV,void,(const GLhalf* weight)) +GL_GROUP_END() +#endif + +#ifdef GL_NV_light_max_exponent +GL_GROUP_BEGIN(GL_NV_light_max_exponent) +GL_GROUP_END() +#endif + +#ifdef GL_NV_multisample_filter_hint +GL_GROUP_BEGIN(GL_NV_multisample_filter_hint) +GL_GROUP_END() +#endif + +#ifdef GL_NV_occlusion_query +GL_GROUP_BEGIN(GL_NV_occlusion_query) +GL_FUNCTION(glBeginOcclusionQueryNV,void,(GLuint id)) +GL_FUNCTION(glDeleteOcclusionQueriesNV,void,(GLsizei n, const GLuint* ids)) +GL_FUNCTION(glEndOcclusionQueryNV,void,(void)) +GL_FUNCTION(glGenOcclusionQueriesNV,void,(GLsizei n, GLuint* ids)) +GL_FUNCTION(glGetOcclusionQueryivNV,void,(GLuint id, GLenum pname, GLint* params)) +GL_FUNCTION(glGetOcclusionQueryuivNV,void,(GLuint id, GLenum pname, GLuint* params)) +GL_FUNCTION(glIsOcclusionQueryNV,GLboolean,(GLuint id)) +GL_GROUP_END() +#endif + +#ifdef GL_NV_packed_depth_stencil +GL_GROUP_BEGIN(GL_NV_packed_depth_stencil) +GL_GROUP_END() +#endif + +#ifdef GL_NV_pixel_data_range +GL_GROUP_BEGIN(GL_NV_pixel_data_range) +GL_FUNCTION(glFlushPixelDataRangeNV,void,(GLenum target)) +GL_FUNCTION(glPixelDataRangeNV,void,(GLenum target, GLsizei length, void* pointer)) +GL_GROUP_END() +#endif + +#ifdef GL_NV_point_sprite +GL_GROUP_BEGIN(GL_NV_point_sprite) +GL_FUNCTION(glPointParameteriNV,void,(GLenum pname, GLint param)) +GL_FUNCTION(glPointParameterivNV,void,(GLenum pname, const GLint* params)) +GL_GROUP_END() +#endif + +#ifdef GL_NV_primitive_restart +GL_GROUP_BEGIN(GL_NV_primitive_restart) +GL_FUNCTION(glPrimitiveRestartIndexNV,void,(GLuint index)) +GL_FUNCTION(glPrimitiveRestartNV,void,(void)) +GL_GROUP_END() +#endif + +#ifdef GL_NV_register_combiners +GL_GROUP_BEGIN(GL_NV_register_combiners) +GL_FUNCTION(glCombinerInputNV,void,(GLenum stage, GLenum portion, GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage)) +GL_FUNCTION(glCombinerOutputNV,void,(GLenum stage, GLenum portion, GLenum abOutput, GLenum cdOutput, GLenum sumOutput, GLenum scale, GLenum bias, GLboolean abDotProduct, GLboolean cdDotProduct, GLboolean muxSum)) +GL_FUNCTION(glCombinerParameterfNV,void,(GLenum pname, GLfloat param)) +GL_FUNCTION(glCombinerParameterfvNV,void,(GLenum pname, const GLfloat* params)) +GL_FUNCTION(glCombinerParameteriNV,void,(GLenum pname, GLint param)) +GL_FUNCTION(glCombinerParameterivNV,void,(GLenum pname, const GLint* params)) +GL_FUNCTION(glFinalCombinerInputNV,void,(GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage)) +GL_FUNCTION(glGetCombinerInputParameterfvNV,void,(GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLfloat* params)) +GL_FUNCTION(glGetCombinerInputParameterivNV,void,(GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLint* params)) +GL_FUNCTION(glGetCombinerOutputParameterfvNV,void,(GLenum stage, GLenum portion, GLenum pname, GLfloat* params)) +GL_FUNCTION(glGetCombinerOutputParameterivNV,void,(GLenum stage, GLenum portion, GLenum pname, GLint* params)) +GL_FUNCTION(glGetFinalCombinerInputParameterfvNV,void,(GLenum variable, GLenum pname, GLfloat* params)) +GL_FUNCTION(glGetFinalCombinerInputParameterivNV,void,(GLenum variable, GLenum pname, GLint* params)) +GL_GROUP_END() +#endif + +#ifdef GL_NV_register_combiners2 +GL_GROUP_BEGIN(GL_NV_register_combiners2) +GL_FUNCTION(glCombinerStageParameterfvNV,void,(GLenum stage, GLenum pname, const GLfloat* params)) +GL_FUNCTION(glGetCombinerStageParameterfvNV,void,(GLenum stage, GLenum pname, GLfloat* params)) +GL_GROUP_END() +#endif + +#ifdef GL_NV_texgen_emboss +GL_GROUP_BEGIN(GL_NV_texgen_emboss) +GL_GROUP_END() +#endif + +#ifdef GL_NV_texgen_reflection +GL_GROUP_BEGIN(GL_NV_texgen_reflection) +GL_GROUP_END() +#endif + +#ifdef GL_NV_texture_compression_vtc +GL_GROUP_BEGIN(GL_NV_texture_compression_vtc) +GL_GROUP_END() +#endif + +#ifdef GL_NV_texture_env_combine4 +GL_GROUP_BEGIN(GL_NV_texture_env_combine4) +GL_GROUP_END() +#endif + +#ifdef GL_NV_texture_expand_normal +GL_GROUP_BEGIN(GL_NV_texture_expand_normal) +GL_GROUP_END() +#endif + +#ifdef GL_NV_texture_rectangle +GL_GROUP_BEGIN(GL_NV_texture_rectangle) +GL_GROUP_END() +#endif + +#ifdef GL_NV_texture_shader +GL_GROUP_BEGIN(GL_NV_texture_shader) +GL_GROUP_END() +#endif + +#ifdef GL_NV_texture_shader2 +GL_GROUP_BEGIN(GL_NV_texture_shader2) +GL_GROUP_END() +#endif + +#ifdef GL_NV_texture_shader3 +GL_GROUP_BEGIN(GL_NV_texture_shader3) +GL_GROUP_END() +#endif + +#ifdef GL_NV_vertex_array_range +GL_GROUP_BEGIN(GL_NV_vertex_array_range) +GL_FUNCTION(glFlushVertexArrayRangeNV,void,(void)) +GL_FUNCTION(glVertexArrayRangeNV,void,(GLsizei length, void* pointer)) +GL_GROUP_END() +#endif + +#ifdef GL_NV_vertex_array_range2 +GL_GROUP_BEGIN(GL_NV_vertex_array_range2) +GL_GROUP_END() +#endif + +#ifdef GL_NV_vertex_program +GL_GROUP_BEGIN(GL_NV_vertex_program) +GL_FUNCTION(glAreProgramsResidentNV,GLboolean,(GLsizei n, const GLuint* ids, GLboolean *residences)) +GL_FUNCTION(glBindProgramNV,void,(GLenum target, GLuint id)) +GL_FUNCTION(glDeleteProgramsNV,void,(GLsizei n, const GLuint* ids)) +GL_FUNCTION(glExecuteProgramNV,void,(GLenum target, GLuint id, const GLfloat* params)) +GL_FUNCTION(glGenProgramsNV,void,(GLsizei n, GLuint* ids)) +GL_FUNCTION(glGetProgramParameterdvNV,void,(GLenum target, GLuint index, GLenum pname, GLdouble* params)) +GL_FUNCTION(glGetProgramParameterfvNV,void,(GLenum target, GLuint index, GLenum pname, GLfloat* params)) +GL_FUNCTION(glGetProgramStringNV,void,(GLuint id, GLenum pname, GLubyte* program)) +GL_FUNCTION(glGetProgramivNV,void,(GLuint id, GLenum pname, GLint* params)) +GL_FUNCTION(glGetTrackMatrixivNV,void,(GLenum target, GLuint address, GLenum pname, GLint* params)) +GL_FUNCTION(glGetVertexAttribPointervNV,void,(GLuint index, GLenum pname, GLvoid** pointer)) +GL_FUNCTION(glGetVertexAttribdvNV,void,(GLuint index, GLenum pname, GLdouble* params)) +GL_FUNCTION(glGetVertexAttribfvNV,void,(GLuint index, GLenum pname, GLfloat* params)) +GL_FUNCTION(glGetVertexAttribivNV,void,(GLuint index, GLenum pname, GLint* params)) +GL_FUNCTION(glIsProgramNV,GLboolean,(GLuint id)) +GL_FUNCTION(glLoadProgramNV,void,(GLenum target, GLuint id, GLsizei len, const GLubyte* program)) +GL_FUNCTION(glProgramParameter4dNV,void,(GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w)) +GL_FUNCTION(glProgramParameter4dvNV,void,(GLenum target, GLuint index, const GLdouble* params)) +GL_FUNCTION(glProgramParameter4fNV,void,(GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)) +GL_FUNCTION(glProgramParameter4fvNV,void,(GLenum target, GLuint index, const GLfloat* params)) +GL_FUNCTION(glProgramParameters4dvNV,void,(GLenum target, GLuint index, GLuint num, const GLdouble* params)) +GL_FUNCTION(glProgramParameters4fvNV,void,(GLenum target, GLuint index, GLuint num, const GLfloat* params)) +GL_FUNCTION(glRequestResidentProgramsNV,void,(GLsizei n, GLuint* ids)) +GL_FUNCTION(glTrackMatrixNV,void,(GLenum target, GLuint address, GLenum matrix, GLenum transform)) +GL_FUNCTION(glVertexAttrib1dNV,void,(GLuint index, GLdouble x)) +GL_FUNCTION(glVertexAttrib1dvNV,void,(GLuint index, const GLdouble* v)) +GL_FUNCTION(glVertexAttrib1fNV,void,(GLuint index, GLfloat x)) +GL_FUNCTION(glVertexAttrib1fvNV,void,(GLuint index, const GLfloat* v)) +GL_FUNCTION(glVertexAttrib1sNV,void,(GLuint index, GLshort x)) +GL_FUNCTION(glVertexAttrib1svNV,void,(GLuint index, const GLshort* v)) +GL_FUNCTION(glVertexAttrib2dNV,void,(GLuint index, GLdouble x, GLdouble y)) +GL_FUNCTION(glVertexAttrib2dvNV,void,(GLuint index, const GLdouble* v)) +GL_FUNCTION(glVertexAttrib2fNV,void,(GLuint index, GLfloat x, GLfloat y)) +GL_FUNCTION(glVertexAttrib2fvNV,void,(GLuint index, const GLfloat* v)) +GL_FUNCTION(glVertexAttrib2sNV,void,(GLuint index, GLshort x, GLshort y)) +GL_FUNCTION(glVertexAttrib2svNV,void,(GLuint index, const GLshort* v)) +GL_FUNCTION(glVertexAttrib3dNV,void,(GLuint index, GLdouble x, GLdouble y, GLdouble z)) +GL_FUNCTION(glVertexAttrib3dvNV,void,(GLuint index, const GLdouble* v)) +GL_FUNCTION(glVertexAttrib3fNV,void,(GLuint index, GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glVertexAttrib3fvNV,void,(GLuint index, const GLfloat* v)) +GL_FUNCTION(glVertexAttrib3sNV,void,(GLuint index, GLshort x, GLshort y, GLshort z)) +GL_FUNCTION(glVertexAttrib3svNV,void,(GLuint index, const GLshort* v)) +GL_FUNCTION(glVertexAttrib4dNV,void,(GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w)) +GL_FUNCTION(glVertexAttrib4dvNV,void,(GLuint index, const GLdouble* v)) +GL_FUNCTION(glVertexAttrib4fNV,void,(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)) +GL_FUNCTION(glVertexAttrib4fvNV,void,(GLuint index, const GLfloat* v)) +GL_FUNCTION(glVertexAttrib4sNV,void,(GLuint index, GLshort x, GLshort y, GLshort z, GLshort w)) +GL_FUNCTION(glVertexAttrib4svNV,void,(GLuint index, const GLshort* v)) +GL_FUNCTION(glVertexAttrib4ubNV,void,(GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w)) +GL_FUNCTION(glVertexAttrib4ubvNV,void,(GLuint index, const GLubyte* v)) +GL_FUNCTION(glVertexAttribPointerNV,void,(GLuint index, GLint size, GLenum type, GLsizei stride, const void* pointer)) +GL_FUNCTION(glVertexAttribs1dvNV,void,(GLuint index, GLsizei n, const GLdouble* v)) +GL_FUNCTION(glVertexAttribs1fvNV,void,(GLuint index, GLsizei n, const GLfloat* v)) +GL_FUNCTION(glVertexAttribs1svNV,void,(GLuint index, GLsizei n, const GLshort* v)) +GL_FUNCTION(glVertexAttribs2dvNV,void,(GLuint index, GLsizei n, const GLdouble* v)) +GL_FUNCTION(glVertexAttribs2fvNV,void,(GLuint index, GLsizei n, const GLfloat* v)) +GL_FUNCTION(glVertexAttribs2svNV,void,(GLuint index, GLsizei n, const GLshort* v)) +GL_FUNCTION(glVertexAttribs3dvNV,void,(GLuint index, GLsizei n, const GLdouble* v)) +GL_FUNCTION(glVertexAttribs3fvNV,void,(GLuint index, GLsizei n, const GLfloat* v)) +GL_FUNCTION(glVertexAttribs3svNV,void,(GLuint index, GLsizei n, const GLshort* v)) +GL_FUNCTION(glVertexAttribs4dvNV,void,(GLuint index, GLsizei n, const GLdouble* v)) +GL_FUNCTION(glVertexAttribs4fvNV,void,(GLuint index, GLsizei n, const GLfloat* v)) +GL_FUNCTION(glVertexAttribs4svNV,void,(GLuint index, GLsizei n, const GLshort* v)) +GL_FUNCTION(glVertexAttribs4ubvNV,void,(GLuint index, GLsizei n, const GLubyte* v)) +GL_GROUP_END() +#endif + +#ifdef GL_NV_vertex_program1_1 +GL_GROUP_BEGIN(GL_NV_vertex_program1_1) +GL_GROUP_END() +#endif + +#ifdef GL_NV_vertex_program2 +GL_GROUP_BEGIN(GL_NV_vertex_program2) +GL_GROUP_END() +#endif + +#ifdef GL_NV_vertex_program2_option +GL_GROUP_BEGIN(GL_NV_vertex_program2_option) +GL_GROUP_END() +#endif + +#ifdef GL_NV_vertex_program3 +GL_GROUP_BEGIN(GL_NV_vertex_program3) +GL_GROUP_END() +#endif + +#ifdef GL_OML_interlace +GL_GROUP_BEGIN(GL_OML_interlace) +GL_GROUP_END() +#endif + +#ifdef GL_OML_resample +GL_GROUP_BEGIN(GL_OML_resample) +GL_GROUP_END() +#endif + +#ifdef GL_OML_subsample +GL_GROUP_BEGIN(GL_OML_subsample) +GL_GROUP_END() +#endif + +#ifdef GL_PGI_misc_hints +GL_GROUP_BEGIN(GL_PGI_misc_hints) +GL_GROUP_END() +#endif + +#ifdef GL_PGI_vertex_hints +GL_GROUP_BEGIN(GL_PGI_vertex_hints) +GL_GROUP_END() +#endif + +#ifdef GL_REND_screen_coordinates +GL_GROUP_BEGIN(GL_REND_screen_coordinates) +GL_GROUP_END() +#endif + +#ifdef GL_S3_s3tc +GL_GROUP_BEGIN(GL_S3_s3tc) +GL_GROUP_END() +#endif + +#ifdef GL_SGIS_color_range +GL_GROUP_BEGIN(GL_SGIS_color_range) +GL_GROUP_END() +#endif + +#ifdef GL_SGIS_detail_texture +GL_GROUP_BEGIN(GL_SGIS_detail_texture) +GL_FUNCTION(glDetailTexFuncSGIS,void,(GLenum target, GLsizei n, const GLfloat* points)) +GL_FUNCTION(glGetDetailTexFuncSGIS,void,(GLenum target, GLfloat* points)) +GL_GROUP_END() +#endif + +#ifdef GL_SGIS_fog_function +GL_GROUP_BEGIN(GL_SGIS_fog_function) +GL_FUNCTION(glFogFuncSGIS,void,(GLsizei n, const GLfloat* points)) +GL_FUNCTION(glGetFogFuncSGIS,void,(GLfloat* points)) +GL_GROUP_END() +#endif + +#ifdef GL_SGIS_generate_mipmap +GL_GROUP_BEGIN(GL_SGIS_generate_mipmap) +GL_GROUP_END() +#endif + +#ifdef GL_SGIS_multisample +GL_GROUP_BEGIN(GL_SGIS_multisample) +GL_FUNCTION(glSampleMaskSGIS,void,(GLclampf value, GLboolean invert)) +GL_FUNCTION(glSamplePatternSGIS,void,(GLenum pattern)) +GL_GROUP_END() +#endif + +#ifdef GL_SGIS_pixel_texture +GL_GROUP_BEGIN(GL_SGIS_pixel_texture) +GL_GROUP_END() +#endif + +#ifdef GL_SGIS_sharpen_texture +GL_GROUP_BEGIN(GL_SGIS_sharpen_texture) +GL_FUNCTION(glGetSharpenTexFuncSGIS,void,(GLenum target, GLfloat* points)) +GL_FUNCTION(glSharpenTexFuncSGIS,void,(GLenum target, GLsizei n, const GLfloat* points)) +GL_GROUP_END() +#endif + +#ifdef GL_SGIS_texture4D +GL_GROUP_BEGIN(GL_SGIS_texture4D) +GL_FUNCTION(glTexImage4DSGIS,void,(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLsizei extent, GLint border, GLenum format, GLenum type, const void* pixels)) +GL_FUNCTION(glTexSubImage4DSGIS,void,(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint woffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei extent, GLenum format, GLenum type, const void* pixels)) +GL_GROUP_END() +#endif + +#ifdef GL_SGIS_texture_border_clamp +GL_GROUP_BEGIN(GL_SGIS_texture_border_clamp) +GL_GROUP_END() +#endif + +#ifdef GL_SGIS_texture_edge_clamp +GL_GROUP_BEGIN(GL_SGIS_texture_edge_clamp) +GL_GROUP_END() +#endif + +#ifdef GL_SGIS_texture_filter4 +GL_GROUP_BEGIN(GL_SGIS_texture_filter4) +GL_FUNCTION(glGetTexFilterFuncSGIS,void,(GLenum target, GLenum filter, GLfloat* weights)) +GL_FUNCTION(glTexFilterFuncSGIS,void,(GLenum target, GLenum filter, GLsizei n, const GLfloat* weights)) +GL_GROUP_END() +#endif + +#ifdef GL_SGIS_texture_lod +GL_GROUP_BEGIN(GL_SGIS_texture_lod) +GL_GROUP_END() +#endif + +#ifdef GL_SGIS_texture_select +GL_GROUP_BEGIN(GL_SGIS_texture_select) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_async +GL_GROUP_BEGIN(GL_SGIX_async) +GL_FUNCTION(glAsyncMarkerSGIX,void,(GLuint marker)) +GL_FUNCTION(glDeleteAsyncMarkersSGIX,void,(GLuint marker, GLsizei range)) +GL_FUNCTION(glFinishAsyncSGIX,GLint,(GLuint* markerp)) +GL_FUNCTION(glGenAsyncMarkersSGIX,GLuint,(GLsizei range)) +GL_FUNCTION(glIsAsyncMarkerSGIX,GLboolean,(GLuint marker)) +GL_FUNCTION(glPollAsyncSGIX,GLint,(GLuint* markerp)) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_async_histogram +GL_GROUP_BEGIN(GL_SGIX_async_histogram) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_async_pixel +GL_GROUP_BEGIN(GL_SGIX_async_pixel) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_blend_alpha_minmax +GL_GROUP_BEGIN(GL_SGIX_blend_alpha_minmax) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_clipmap +GL_GROUP_BEGIN(GL_SGIX_clipmap) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_depth_texture +GL_GROUP_BEGIN(GL_SGIX_depth_texture) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_flush_raster +GL_GROUP_BEGIN(GL_SGIX_flush_raster) +GL_FUNCTION(glFlushRasterSGIX,void,(void)) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_fog_offset +GL_GROUP_BEGIN(GL_SGIX_fog_offset) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_fog_texture +GL_GROUP_BEGIN(GL_SGIX_fog_texture) +GL_FUNCTION(glTextureFogSGIX,void,(GLenum pname)) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_fragment_specular_lighting +GL_GROUP_BEGIN(GL_SGIX_fragment_specular_lighting) +GL_FUNCTION(glFragmentColorMaterialSGIX,void,(GLenum face, GLenum mode)) +GL_FUNCTION(glFragmentLightModelfSGIX,void,(GLenum pname, GLfloat param)) +GL_FUNCTION(glFragmentLightModelfvSGIX,void,(GLenum pname, GLfloat* params)) +GL_FUNCTION(glFragmentLightModeliSGIX,void,(GLenum pname, GLint param)) +GL_FUNCTION(glFragmentLightModelivSGIX,void,(GLenum pname, GLint* params)) +GL_FUNCTION(glFragmentLightfSGIX,void,(GLenum light, GLenum pname, GLfloat param)) +GL_FUNCTION(glFragmentLightfvSGIX,void,(GLenum light, GLenum pname, GLfloat* params)) +GL_FUNCTION(glFragmentLightiSGIX,void,(GLenum light, GLenum pname, GLint param)) +GL_FUNCTION(glFragmentLightivSGIX,void,(GLenum light, GLenum pname, GLint* params)) +GL_FUNCTION(glFragmentMaterialfSGIX,void,(GLenum face, GLenum pname, const GLfloat param)) +GL_FUNCTION(glFragmentMaterialfvSGIX,void,(GLenum face, GLenum pname, const GLfloat* params)) +GL_FUNCTION(glFragmentMaterialiSGIX,void,(GLenum face, GLenum pname, const GLint param)) +GL_FUNCTION(glFragmentMaterialivSGIX,void,(GLenum face, GLenum pname, const GLint* params)) +GL_FUNCTION(glGetFragmentLightfvSGIX,void,(GLenum light, GLenum value, GLfloat* data)) +GL_FUNCTION(glGetFragmentLightivSGIX,void,(GLenum light, GLenum value, GLint* data)) +GL_FUNCTION(glGetFragmentMaterialfvSGIX,void,(GLenum face, GLenum pname, const GLfloat* data)) +GL_FUNCTION(glGetFragmentMaterialivSGIX,void,(GLenum face, GLenum pname, const GLint* data)) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_framezoom +GL_GROUP_BEGIN(GL_SGIX_framezoom) +GL_FUNCTION(glFrameZoomSGIX,void,(GLint factor)) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_interlace +GL_GROUP_BEGIN(GL_SGIX_interlace) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_ir_instrument1 +GL_GROUP_BEGIN(GL_SGIX_ir_instrument1) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_list_priority +GL_GROUP_BEGIN(GL_SGIX_list_priority) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_pixel_texture +GL_GROUP_BEGIN(GL_SGIX_pixel_texture) +GL_FUNCTION(glPixelTexGenSGIX,void,(GLenum mode)) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_pixel_texture_bits +GL_GROUP_BEGIN(GL_SGIX_pixel_texture_bits) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_reference_plane +GL_GROUP_BEGIN(GL_SGIX_reference_plane) +GL_FUNCTION(glReferencePlaneSGIX,void,(const GLdouble* equation)) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_resample +GL_GROUP_BEGIN(GL_SGIX_resample) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_shadow +GL_GROUP_BEGIN(GL_SGIX_shadow) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_shadow_ambient +GL_GROUP_BEGIN(GL_SGIX_shadow_ambient) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_sprite +GL_GROUP_BEGIN(GL_SGIX_sprite) +GL_FUNCTION(glSpriteParameterfSGIX,void,(GLenum pname, GLfloat param)) +GL_FUNCTION(glSpriteParameterfvSGIX,void,(GLenum pname, GLfloat* params)) +GL_FUNCTION(glSpriteParameteriSGIX,void,(GLenum pname, GLint param)) +GL_FUNCTION(glSpriteParameterivSGIX,void,(GLenum pname, GLint* params)) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_tag_sample_buffer +GL_GROUP_BEGIN(GL_SGIX_tag_sample_buffer) +GL_FUNCTION(glTagSampleBufferSGIX,void,(void)) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_texture_add_env +GL_GROUP_BEGIN(GL_SGIX_texture_add_env) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_texture_coordinate_clamp +GL_GROUP_BEGIN(GL_SGIX_texture_coordinate_clamp) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_texture_lod_bias +GL_GROUP_BEGIN(GL_SGIX_texture_lod_bias) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_texture_multi_buffer +GL_GROUP_BEGIN(GL_SGIX_texture_multi_buffer) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_texture_range +GL_GROUP_BEGIN(GL_SGIX_texture_range) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_texture_scale_bias +GL_GROUP_BEGIN(GL_SGIX_texture_scale_bias) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_vertex_preclip +GL_GROUP_BEGIN(GL_SGIX_vertex_preclip) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_vertex_preclip_hint +GL_GROUP_BEGIN(GL_SGIX_vertex_preclip_hint) +GL_GROUP_END() +#endif + +#ifdef GL_SGIX_ycrcb +GL_GROUP_BEGIN(GL_SGIX_ycrcb) +GL_GROUP_END() +#endif + +#ifdef GL_SGI_color_matrix +GL_GROUP_BEGIN(GL_SGI_color_matrix) +GL_GROUP_END() +#endif + +#ifdef GL_SGI_color_table +GL_GROUP_BEGIN(GL_SGI_color_table) +GL_FUNCTION(glColorTableParameterfvSGI,void,(GLenum target, GLenum pname, const GLfloat* params)) +GL_FUNCTION(glColorTableParameterivSGI,void,(GLenum target, GLenum pname, const GLint* params)) +GL_FUNCTION(glColorTableSGI,void,(GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void* table)) +GL_FUNCTION(glCopyColorTableSGI,void,(GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width)) +GL_FUNCTION(glGetColorTableParameterfvSGI,void,(GLenum target, GLenum pname, GLfloat* params)) +GL_FUNCTION(glGetColorTableParameterivSGI,void,(GLenum target, GLenum pname, GLint* params)) +GL_FUNCTION(glGetColorTableSGI,void,(GLenum target, GLenum format, GLenum type, void* table)) +GL_GROUP_END() +#endif + +#ifdef GL_SGI_texture_color_table +GL_GROUP_BEGIN(GL_SGI_texture_color_table) +GL_GROUP_END() +#endif + +#ifdef GL_SUNX_constant_data +GL_GROUP_BEGIN(GL_SUNX_constant_data) +GL_FUNCTION(glFinishTextureSUNX,void,(void)) +GL_GROUP_END() +#endif + +#ifdef GL_SUN_convolution_border_modes +GL_GROUP_BEGIN(GL_SUN_convolution_border_modes) +GL_GROUP_END() +#endif + +#ifdef GL_SUN_global_alpha +GL_GROUP_BEGIN(GL_SUN_global_alpha) +GL_FUNCTION(glGlobalAlphaFactorbSUN,void,(GLbyte factor)) +GL_FUNCTION(glGlobalAlphaFactordSUN,void,(GLdouble factor)) +GL_FUNCTION(glGlobalAlphaFactorfSUN,void,(GLfloat factor)) +GL_FUNCTION(glGlobalAlphaFactoriSUN,void,(GLint factor)) +GL_FUNCTION(glGlobalAlphaFactorsSUN,void,(GLshort factor)) +GL_FUNCTION(glGlobalAlphaFactorubSUN,void,(GLubyte factor)) +GL_FUNCTION(glGlobalAlphaFactoruiSUN,void,(GLuint factor)) +GL_FUNCTION(glGlobalAlphaFactorusSUN,void,(GLushort factor)) +GL_GROUP_END() +#endif + +#ifdef GL_SUN_mesh_array +GL_GROUP_BEGIN(GL_SUN_mesh_array) +GL_GROUP_END() +#endif + +#ifdef GL_SUN_read_video_pixels +GL_GROUP_BEGIN(GL_SUN_read_video_pixels) +GL_FUNCTION(glReadVideoPixelsSUN,void,(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels)) +GL_GROUP_END() +#endif + +#ifdef GL_SUN_slice_accum +GL_GROUP_BEGIN(GL_SUN_slice_accum) +GL_GROUP_END() +#endif + +#ifdef GL_SUN_triangle_list +GL_GROUP_BEGIN(GL_SUN_triangle_list) +GL_FUNCTION(glReplacementCodePointerSUN,void,(GLenum type, GLsizei stride, const void* pointer)) +GL_FUNCTION(glReplacementCodeubSUN,void,(GLubyte code)) +GL_FUNCTION(glReplacementCodeubvSUN,void,(const GLubyte* code)) +GL_FUNCTION(glReplacementCodeuiSUN,void,(GLuint code)) +GL_FUNCTION(glReplacementCodeuivSUN,void,(const GLuint* code)) +GL_FUNCTION(glReplacementCodeusSUN,void,(GLushort code)) +GL_FUNCTION(glReplacementCodeusvSUN,void,(const GLushort* code)) +GL_GROUP_END() +#endif + +#ifdef GL_SUN_vertex +GL_GROUP_BEGIN(GL_SUN_vertex) +GL_FUNCTION(glColor3fVertex3fSUN,void,(GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glColor3fVertex3fvSUN,void,(const GLfloat* c, const GLfloat *v)) +GL_FUNCTION(glColor4fNormal3fVertex3fSUN,void,(GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glColor4fNormal3fVertex3fvSUN,void,(const GLfloat* c, const GLfloat *n, const GLfloat *v)) +GL_FUNCTION(glColor4ubVertex2fSUN,void,(GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y)) +GL_FUNCTION(glColor4ubVertex2fvSUN,void,(const GLubyte* c, const GLfloat *v)) +GL_FUNCTION(glColor4ubVertex3fSUN,void,(GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glColor4ubVertex3fvSUN,void,(const GLubyte* c, const GLfloat *v)) +GL_FUNCTION(glNormal3fVertex3fSUN,void,(GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glNormal3fVertex3fvSUN,void,(const GLfloat* n, const GLfloat *v)) +GL_FUNCTION(glReplacementCodeuiColor3fVertex3fSUN,void,(GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glReplacementCodeuiColor3fVertex3fvSUN,void,(const GLuint* rc, const GLfloat *c, const GLfloat *v)) +GL_FUNCTION(glReplacementCodeuiColor4fNormal3fVertex3fSUN,void,(GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glReplacementCodeuiColor4fNormal3fVertex3fvSUN,void,(const GLuint* rc, const GLfloat *c, const GLfloat *n, const GLfloat *v)) +GL_FUNCTION(glReplacementCodeuiColor4ubVertex3fSUN,void,(GLuint rc, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glReplacementCodeuiColor4ubVertex3fvSUN,void,(const GLuint* rc, const GLubyte *c, const GLfloat *v)) +GL_FUNCTION(glReplacementCodeuiNormal3fVertex3fSUN,void,(GLuint rc, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glReplacementCodeuiNormal3fVertex3fvSUN,void,(const GLuint* rc, const GLfloat *n, const GLfloat *v)) +GL_FUNCTION(glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN,void,(GLuint rc, GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN,void,(const GLuint* rc, const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v)) +GL_FUNCTION(glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN,void,(GLuint rc, GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN,void,(const GLuint* rc, const GLfloat *tc, const GLfloat *n, const GLfloat *v)) +GL_FUNCTION(glReplacementCodeuiTexCoord2fVertex3fSUN,void,(GLuint rc, GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glReplacementCodeuiTexCoord2fVertex3fvSUN,void,(const GLuint* rc, const GLfloat *tc, const GLfloat *v)) +GL_FUNCTION(glReplacementCodeuiVertex3fSUN,void,(GLuint rc, GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glReplacementCodeuiVertex3fvSUN,void,(const GLuint* rc, const GLfloat *v)) +GL_FUNCTION(glTexCoord2fColor3fVertex3fSUN,void,(GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glTexCoord2fColor3fVertex3fvSUN,void,(const GLfloat* tc, const GLfloat *c, const GLfloat *v)) +GL_FUNCTION(glTexCoord2fColor4fNormal3fVertex3fSUN,void,(GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glTexCoord2fColor4fNormal3fVertex3fvSUN,void,(const GLfloat* tc, const GLfloat *c, const GLfloat *n, const GLfloat *v)) +GL_FUNCTION(glTexCoord2fColor4ubVertex3fSUN,void,(GLfloat s, GLfloat t, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glTexCoord2fColor4ubVertex3fvSUN,void,(const GLfloat* tc, const GLubyte *c, const GLfloat *v)) +GL_FUNCTION(glTexCoord2fNormal3fVertex3fSUN,void,(GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glTexCoord2fNormal3fVertex3fvSUN,void,(const GLfloat* tc, const GLfloat *n, const GLfloat *v)) +GL_FUNCTION(glTexCoord2fVertex3fSUN,void,(GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z)) +GL_FUNCTION(glTexCoord2fVertex3fvSUN,void,(const GLfloat* tc, const GLfloat *v)) +GL_FUNCTION(glTexCoord4fColor4fNormal3fVertex4fSUN,void,(GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z, GLfloat w)) +GL_FUNCTION(glTexCoord4fColor4fNormal3fVertex4fvSUN,void,(const GLfloat* tc, const GLfloat *c, const GLfloat *n, const GLfloat *v)) +GL_FUNCTION(glTexCoord4fVertex4fSUN,void,(GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat x, GLfloat y, GLfloat z, GLfloat w)) +GL_FUNCTION(glTexCoord4fVertex4fvSUN,void,(const GLfloat* tc, const GLfloat *v)) +GL_GROUP_END() +#endif + +#ifdef GL_WIN_phong_shading +GL_GROUP_BEGIN(GL_WIN_phong_shading) +GL_GROUP_END() +#endif + +#ifdef GL_WIN_specular_fog +GL_GROUP_BEGIN(GL_WIN_specular_fog) +GL_GROUP_END() +#endif + +#ifdef GL_WIN_swap_hint +GL_GROUP_BEGIN(GL_WIN_swap_hint) +GL_FUNCTION(glAddSwapHintRectWIN,void,(GLint x, GLint y, GLsizei width, GLsizei height)) +GL_GROUP_END() +#endif + + diff --git a/Engine/source/gfx/gl/ggl/generated/glxe.h b/Engine/source/gfx/gl/ggl/generated/glxe.h new file mode 100644 index 000000000..18cc857e9 --- /dev/null +++ b/Engine/source/gfx/gl/ggl/generated/glxe.h @@ -0,0 +1,405 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef GLX_3DFX_multisample +#define GLX_SAMPLE_BUFFERS_3DFX 0x8050 +#define GLX_SAMPLES_3DFX 0x8051 +#endif + +#ifdef GLX_ARB_fbconfig_float +#define GLX_RGBA_FLOAT_BIT 0x00000004 +#define GLX_RGBA_FLOAT_TYPE 0x20B9 +#endif + +#ifdef GLX_ARB_get_proc_address +#define glXGetProcAddressARB XGL_FUNCPTR(glXGetProcAddressARB) +#endif + +#ifdef GLX_ARB_multisample +#define GLX_SAMPLE_BUFFERS_ARB 100000 +#define GLX_SAMPLES_ARB 100001 +#endif + +#ifdef GLX_ATI_pixel_format_float +#define GLX_RGBA_FLOAT_ATI_BIT 0x00000100 +#endif + +#ifdef GLX_ATI_render_texture +#define GLX_BIND_TO_TEXTURE_RGB_ATI 0x9800 +#define GLX_BIND_TO_TEXTURE_RGBA_ATI 0x9801 +#define GLX_TEXTURE_FORMAT_ATI 0x9802 +#define GLX_TEXTURE_TARGET_ATI 0x9803 +#define GLX_MIPMAP_TEXTURE_ATI 0x9804 +#define GLX_TEXTURE_RGB_ATI 0x9805 +#define GLX_TEXTURE_RGBA_ATI 0x9806 +#define GLX_NO_TEXTURE_ATI 0x9807 +#define GLX_TEXTURE_CUBE_MAP_ATI 0x9808 +#define GLX_TEXTURE_1D_ATI 0x9809 +#define GLX_TEXTURE_2D_ATI 0x980A +#define GLX_MIPMAP_LEVEL_ATI 0x980B +#define GLX_CUBE_MAP_FACE_ATI 0x980C +#define GLX_TEXTURE_CUBE_MAP_POSITIVE_X_ATI 0x980D +#define GLX_TEXTURE_CUBE_MAP_NEGATIVE_X_ATI 0x980E +#define GLX_TEXTURE_CUBE_MAP_POSITIVE_Y_ATI 0x980F +#define GLX_TEXTURE_CUBE_MAP_NEGATIVE_Y_ATI 0x9810 +#define GLX_TEXTURE_CUBE_MAP_POSITIVE_Z_ATI 0x9811 +#define GLX_TEXTURE_CUBE_MAP_NEGATIVE_Z_ATI 0x9812 +#define GLX_FRONT_LEFT_ATI 0x9813 +#define GLX_FRONT_RIGHT_ATI 0x9814 +#define GLX_BACK_LEFT_ATI 0x9815 +#define GLX_BACK_RIGHT_ATI 0x9816 +#define GLX_AUX0_ATI 0x9817 +#define GLX_AUX1_ATI 0x9818 +#define GLX_AUX2_ATI 0x9819 +#define GLX_AUX3_ATI 0x981A +#define GLX_AUX4_ATI 0x981B +#define GLX_AUX5_ATI 0x981C +#define GLX_AUX6_ATI 0x981D +#define GLX_AUX7_ATI 0x981E +#define GLX_AUX8_ATI 0x981F +#define GLX_AUX9_ATI 0x9820 +#define GLX_BIND_TO_TEXTURE_LUMINANCE_ATI 0x9821 +#define GLX_BIND_TO_TEXTURE_INTENSITY_ATI 0x9822 +#define glXBindTexImageATI XGL_FUNCPTR(glXBindTexImageATI) +#define glXReleaseTexImageATI XGL_FUNCPTR(glXReleaseTexImageATI) +#define glXDrawableAttribATI XGL_FUNCPTR(glXDrawableAttribATI) +#endif + +#ifdef GLX_EXT_import_context +typedef XID GLXContextID; +#define GLX_SHARE_CONTEXT_EXT 0x800A +#define GLX_VISUAL_ID_EXT 0x800B +#define GLX_SCREEN_EXT 0x800C +#define glXFreeContextEXT XGL_FUNCPTR(glXFreeContextEXT) +#define glXGetContextIDEXT XGL_FUNCPTR(glXGetContextIDEXT) +#define glXImportContextEXT XGL_FUNCPTR(glXImportContextEXT) +#define glXQueryContextInfoEXT XGL_FUNCPTR(glXQueryContextInfoEXT) +#endif + +#ifdef GLX_EXT_scene_marker +#endif + +#ifdef GLX_EXT_visual_info +#define GLX_X_VISUAL_TYPE_EXT 0x22 +#define GLX_TRANSPARENT_TYPE_EXT 0x23 +#define GLX_TRANSPARENT_INDEX_VALUE_EXT 0x24 +#define GLX_TRANSPARENT_RED_VALUE_EXT 0x25 +#define GLX_TRANSPARENT_GREEN_VALUE_EXT 0x26 +#define GLX_TRANSPARENT_BLUE_VALUE_EXT 0x27 +#define GLX_TRANSPARENT_ALPHA_VALUE_EXT 0x28 +#define GLX_NONE_EXT 0x8000 +#define GLX_TRUE_COLOR_EXT 0x8002 +#define GLX_DIRECT_COLOR_EXT 0x8003 +#define GLX_PSEUDO_COLOR_EXT 0x8004 +#define GLX_STATIC_COLOR_EXT 0x8005 +#define GLX_GRAY_SCALE_EXT 0x8006 +#define GLX_STATIC_GRAY_EXT 0x8007 +#define GLX_TRANSPARENT_RGB_EXT 0x8008 +#define GLX_TRANSPARENT_INDEX_EXT 0x8009 +#endif + +#ifdef GLX_EXT_visual_rating +#define GLX_VISUAL_CAVEAT_EXT 0x20 +#define GLX_SLOW_VISUAL_EXT 0x8001 +#define GLX_NON_CONFORMANT_VISUAL_EXT 0x800D +#endif + +#ifdef GLX_MESA_agp_offset +#define glXGetAGPOffsetMESA XGL_FUNCPTR(glXGetAGPOffsetMESA) +#endif + +#ifdef GLX_MESA_copy_sub_buffer +#define glXCopySubBufferMESA XGL_FUNCPTR(glXCopySubBufferMESA) +#endif + +#ifdef GLX_MESA_pixmap_colormap +#define glXCreateGLXPixmapMESA XGL_FUNCPTR(glXCreateGLXPixmapMESA) +#endif + +#ifdef GLX_MESA_release_buffers +#define glXReleaseBuffersMESA XGL_FUNCPTR(glXReleaseBuffersMESA) +#endif + +#ifdef GLX_MESA_set_3dfx_mode +#define GLX_3DFX_WINDOW_MODE_MESA 0x1 +#define GLX_3DFX_FULLSCREEN_MODE_MESA 0x2 +#define glXSet3DfxModeMESA XGL_FUNCPTR(glXSet3DfxModeMESA) +#endif + +#ifdef GLX_NV_float_buffer +#define GLX_FLOAT_COMPONENTS_NV 0x20B0 +#endif + +#ifdef GLX_NV_vertex_array_range +#define glXAllocateMemoryNV XGL_FUNCPTR(glXAllocateMemoryNV) +#define glXFreeMemoryNV XGL_FUNCPTR(glXFreeMemoryNV) +#endif + +#ifdef GLX_OML_swap_method +#define GLX_SWAP_METHOD_OML 0x8060 +#define GLX_SWAP_EXCHANGE_OML 0x8061 +#define GLX_SWAP_COPY_OML 0x8062 +#define GLX_SWAP_UNDEFINED_OML 0x8063 +#endif + +#ifdef GLX_OML_sync_control +#define glXGetMscRateOML XGL_FUNCPTR(glXGetMscRateOML) +#define glXGetSyncValuesOML XGL_FUNCPTR(glXGetSyncValuesOML) +#define glXSwapBuffersMscOML XGL_FUNCPTR(glXSwapBuffersMscOML) +#define glXWaitForMscOML XGL_FUNCPTR(glXWaitForMscOML) +#define glXWaitForSbcOML XGL_FUNCPTR(glXWaitForSbcOML) +#endif + +#ifdef GLX_SGIS_blended_overlay +#define GLX_BLENDED_RGBA_SGIS 0x8025 +#endif + +#ifdef GLX_SGIS_color_range +#define GLX_MAX_GREEN_SGIS 0 +#define GLX_MIN_RED_SGIS 0 +#define GLX_MIN_BLUE_SGIS 0 +#define GLX_MAX_RED_SGIS 0 +#define GLX_MAX_ALPHA_SGIS 0 +#define GLX_MIN_GREEN_SGIS 0 +#define GLX_MIN_ALPHA_SGIS 0 +#define GLX_EXTENDED_RANGE_SGIS 0 +#define GLX_MAX_BLUE_SGIS 0 +#endif + +#ifdef GLX_SGIS_multisample +#define GLX_SAMPLE_BUFFERS_SGIS 100000 +#define GLX_SAMPLES_SGIS 100001 +#endif + +#ifdef GLX_SGIS_shared_multisample +#define GLX_MULTISAMPLE_SUB_RECT_WIDTH_SGIS 0x8026 +#define GLX_MULTISAMPLE_SUB_RECT_HEIGHT_SGIS 0x8027 +#endif + +#ifdef GLX_SGIX_fbconfig +typedef XID GLXFBConfigIDSGIX; +typedef struct __GLXFBConfigRec *GLXFBConfigSGIX; +#define GLX_WINDOW_BIT_SGIX 0x00000001 +#define GLX_RGBA_BIT_SGIX 0x00000001 +#define GLX_PIXMAP_BIT_SGIX 0x00000002 +#define GLX_COLOR_INDEX_BIT_SGIX 0x00000002 +#define GLX_SCREEN_EXT 0x800C +#define GLX_DRAWABLE_TYPE_SGIX 0x8010 +#define GLX_RENDER_TYPE_SGIX 0x8011 +#define GLX_X_RENDERABLE_SGIX 0x8012 +#define GLX_FBCONFIG_ID_SGIX 0x8013 +#define GLX_RGBA_TYPE_SGIX 0x8014 +#define GLX_COLOR_INDEX_TYPE_SGIX 0x8015 +#define glXChooseFBConfigSGIX XGL_FUNCPTR(glXChooseFBConfigSGIX) +#define glXCreateContextWithConfigSGIX XGL_FUNCPTR(glXCreateContextWithConfigSGIX) +#define glXCreateGLXPixmapWithConfigSGIX XGL_FUNCPTR(glXCreateGLXPixmapWithConfigSGIX) +#define glXGetFBConfigAttribSGIX XGL_FUNCPTR(glXGetFBConfigAttribSGIX) +#define glXGetFBConfigFromVisualSGIX XGL_FUNCPTR(glXGetFBConfigFromVisualSGIX) +#define glXGetVisualFromFBConfigSGIX XGL_FUNCPTR(glXGetVisualFromFBConfigSGIX) +#endif + +#ifdef GLX_SGIX_pbuffer +typedef XID GLXPbufferSGIX; +typedef struct { int type; unsigned long serial; Bool send_event; Display *display; GLXDrawable drawable; int event_type; int draw_type; unsigned int mask; int x, y; int width, height; int count; } GLXBufferClobberEventSGIX; +#define GLX_FRONT_LEFT_BUFFER_BIT_SGIX 0x00000001 +#define GLX_FRONT_RIGHT_BUFFER_BIT_SGIX 0x00000002 +#define GLX_PBUFFER_BIT_SGIX 0x00000004 +#define GLX_BACK_LEFT_BUFFER_BIT_SGIX 0x00000004 +#define GLX_BACK_RIGHT_BUFFER_BIT_SGIX 0x00000008 +#define GLX_AUX_BUFFERS_BIT_SGIX 0x00000010 +#define GLX_DEPTH_BUFFER_BIT_SGIX 0x00000020 +#define GLX_STENCIL_BUFFER_BIT_SGIX 0x00000040 +#define GLX_ACCUM_BUFFER_BIT_SGIX 0x00000080 +#define GLX_SAMPLE_BUFFERS_BIT_SGIX 0x00000100 +#define GLX_MAX_PBUFFER_WIDTH_SGIX 0x8016 +#define GLX_MAX_PBUFFER_HEIGHT_SGIX 0x8017 +#define GLX_MAX_PBUFFER_PIXELS_SGIX 0x8018 +#define GLX_OPTIMAL_PBUFFER_WIDTH_SGIX 0x8019 +#define GLX_OPTIMAL_PBUFFER_HEIGHT_SGIX 0x801A +#define GLX_PRESERVED_CONTENTS_SGIX 0x801B +#define GLX_LARGEST_PBUFFER_SGIX 0x801C +#define GLX_WIDTH_SGIX 0x801D +#define GLX_HEIGHT_SGIX 0x801E +#define GLX_EVENT_MASK_SGIX 0x801F +#define GLX_DAMAGED_SGIX 0x8020 +#define GLX_SAVED_SGIX 0x8021 +#define GLX_WINDOW_SGIX 0x8022 +#define GLX_PBUFFER_SGIX 0x8023 +#define GLX_BUFFER_CLOBBER_MASK_SGIX 0x08000000 +#define glXCreateGLXPbufferSGIX XGL_FUNCPTR(glXCreateGLXPbufferSGIX) +#define glXDestroyGLXPbufferSGIX XGL_FUNCPTR(glXDestroyGLXPbufferSGIX) +#define glXGetSelectedEventSGIX XGL_FUNCPTR(glXGetSelectedEventSGIX) +#define glXQueryGLXPbufferSGIX XGL_FUNCPTR(glXQueryGLXPbufferSGIX) +#define glXSelectEventSGIX XGL_FUNCPTR(glXSelectEventSGIX) +#endif + +#ifdef GLX_SGIX_swap_barrier +#define glXBindSwapBarrierSGIX XGL_FUNCPTR(glXBindSwapBarrierSGIX) +#define glXQueryMaxSwapBarriersSGIX XGL_FUNCPTR(glXQueryMaxSwapBarriersSGIX) +#endif + +#ifdef GLX_SGIX_swap_group +#define glXJoinSwapGroupSGIX XGL_FUNCPTR(glXJoinSwapGroupSGIX) +#endif + +#ifdef GLX_SGIX_video_resize +#define GLX_SYNC_FRAME_SGIX 0x00000000 +#define GLX_SYNC_SWAP_SGIX 0x00000001 +#define glXBindChannelToWindowSGIX XGL_FUNCPTR(glXBindChannelToWindowSGIX) +#define glXChannelRectSGIX XGL_FUNCPTR(glXChannelRectSGIX) +#define glXChannelRectSyncSGIX XGL_FUNCPTR(glXChannelRectSyncSGIX) +#define glXQueryChannelDeltasSGIX XGL_FUNCPTR(glXQueryChannelDeltasSGIX) +#define glXQueryChannelRectSGIX XGL_FUNCPTR(glXQueryChannelRectSGIX) +#endif + +#ifdef GLX_SGIX_visual_select_group +#define GLX_VISUAL_SELECT_GROUP_SGIX 0x8028 +#endif + +#ifdef GLX_SGI_cushion +#define glXCushionSGI XGL_FUNCPTR(glXCushionSGI) +#endif + +#ifdef GLX_SGI_make_current_read +#define glXGetCurrentReadDrawableSGI XGL_FUNCPTR(glXGetCurrentReadDrawableSGI) +#define glXMakeCurrentReadSGI XGL_FUNCPTR(glXMakeCurrentReadSGI) +#endif + +#ifdef GLX_SGI_swap_control +#define glXSwapIntervalSGI XGL_FUNCPTR(glXSwapIntervalSGI) +#endif + +#ifdef GLX_SGI_video_sync +#define glXGetVideoSyncSGI XGL_FUNCPTR(glXGetVideoSyncSGI) +#define glXWaitVideoSyncSGI XGL_FUNCPTR(glXWaitVideoSyncSGI) +#endif + +#ifdef GLX_SUN_get_transparent_index +#define glXGetTransparentIndexSUN XGL_FUNCPTR(glXGetTransparentIndexSUN) +#endif + +#ifdef GLX_SUN_video_resize +#define GL_VIDEO_RESIZE_COMPENSATION_SUN 0x85CD +#define GLX_VIDEO_RESIZE_SUN 0x8171 +#define glXVideoResizeSUN XGL_FUNCPTR(glXVideoResizeSUN) +#define glXGetVideoResizeSUN XGL_FUNCPTR(glXGetVideoResizeSUN) +#endif + +#ifdef GLX_VERSION_1_1 +#define glXQueryExtensionsString XGL_FUNCPTR(glXQueryExtensionsString) +#define glXGetClientString XGL_FUNCPTR(glXGetClientString) +#define glXQueryServerString XGL_FUNCPTR(glXQueryServerString) +#endif + +#ifdef GLX_VERSION_1_2 +#define glXGetCurrentDisplay XGL_FUNCPTR(glXGetCurrentDisplay) +#endif + +#ifdef GLX_VERSION_1_3 +typedef XID GLXWindow; +typedef XID GLXPbuffer; +typedef XID GLXFBConfigID; +typedef struct __GLXFBConfigRec *GLXFBConfig; +typedef struct { int event_type; int draw_type; unsigned long serial; Bool send_event; Display *display; GLXDrawable drawable; unsigned int buffer_mask; unsigned int aux_buffer; int x, y; int width, height; int count; } GLXPbufferClobberEvent; +typedef union __GLXEvent { GLXPbufferClobberEvent glxpbufferclobber; long pad[24]; } GLXEvent; +#define GLX_WINDOW_BIT 0x00000001 +#define GLX_PIXMAP_BIT 0x00000002 +#define GLX_PBUFFER_BIT 0x00000004 +#define GLX_RGBA_BIT 0x00000001 +#define GLX_COLOR_INDEX_BIT 0x00000002 +#define GLX_PBUFFER_CLOBBER_MASK 0x08000000 +#define GLX_FRONT_LEFT_BUFFER_BIT 0x00000001 +#define GLX_FRONT_RIGHT_BUFFER_BIT 0x00000002 +#define GLX_BACK_LEFT_BUFFER_BIT 0x00000004 +#define GLX_BACK_RIGHT_BUFFER_BIT 0x00000008 +#define GLX_AUX_BUFFERS_BIT 0x00000010 +#define GLX_DEPTH_BUFFER_BIT 0x00000020 +#define GLX_STENCIL_BUFFER_BIT 0x00000040 +#define GLX_ACCUM_BUFFER_BIT 0x00000080 +#define GLX_CONFIG_CAVEAT 0x20 +#define GLX_X_VISUAL_TYPE 0x22 +#define GLX_TRANSPARENT_TYPE 0x23 +#define GLX_TRANSPARENT_INDEX_VALUE 0x24 +#define GLX_TRANSPARENT_RED_VALUE 0x25 +#define GLX_TRANSPARENT_GREEN_VALUE 0x26 +#define GLX_TRANSPARENT_BLUE_VALUE 0x27 +#define GLX_TRANSPARENT_ALPHA_VALUE 0x28 +#define GLX_DONT_CARE 0xFFFFFFFF +#define GLX_NONE 0x8000 +#define GLX_SLOW_CONFIG 0x8001 +#define GLX_TRUE_COLOR 0x8002 +#define GLX_DIRECT_COLOR 0x8003 +#define GLX_PSEUDO_COLOR 0x8004 +#define GLX_STATIC_COLOR 0x8005 +#define GLX_GRAY_SCALE 0x8006 +#define GLX_STATIC_GRAY 0x8007 +#define GLX_TRANSPARENT_RGB 0x8008 +#define GLX_TRANSPARENT_INDEX 0x8009 +#define GLX_VISUAL_ID 0x800B +#define GLX_SCREEN 0x800C +#define GLX_NON_CONFORMANT_CONFIG 0x800D +#define GLX_DRAWABLE_TYPE 0x8010 +#define GLX_RENDER_TYPE 0x8011 +#define GLX_X_RENDERABLE 0x8012 +#define GLX_FBCONFIG_ID 0x8013 +#define GLX_RGBA_TYPE 0x8014 +#define GLX_COLOR_INDEX_TYPE 0x8015 +#define GLX_MAX_PBUFFER_WIDTH 0x8016 +#define GLX_MAX_PBUFFER_HEIGHT 0x8017 +#define GLX_MAX_PBUFFER_PIXELS 0x8018 +#define GLX_PRESERVED_CONTENTS 0x801B +#define GLX_LARGEST_PBUFFER 0x801C +#define GLX_WIDTH 0x801D +#define GLX_HEIGHT 0x801E +#define GLX_EVENT_MASK 0x801F +#define GLX_DAMAGED 0x8020 +#define GLX_SAVED 0x8021 +#define GLX_WINDOW 0x8022 +#define GLX_PBUFFER 0x8023 +#define GLX_PBUFFER_HEIGHT 0x8040 +#define GLX_PBUFFER_WIDTH 0x8041 +#define glXChooseFBConfig XGL_FUNCPTR(glXChooseFBConfig) +#define glXGetFBConfigs XGL_FUNCPTR(glXGetFBConfigs) +#define glXGetVisualFromFBConfig XGL_FUNCPTR(glXGetVisualFromFBConfig) +#define glXGetFBConfigAttrib XGL_FUNCPTR(glXGetFBConfigAttrib) +#define glXCreateWindow XGL_FUNCPTR(glXCreateWindow) +#define glXDestroyWindow XGL_FUNCPTR(glXDestroyWindow) +#define glXCreatePixmap XGL_FUNCPTR(glXCreatePixmap) +#define glXDestroyPixmap XGL_FUNCPTR(glXDestroyPixmap) +#define glXCreatePbuffer XGL_FUNCPTR(glXCreatePbuffer) +#define glXDestroyPbuffer XGL_FUNCPTR(glXDestroyPbuffer) +#define glXQueryDrawable XGL_FUNCPTR(glXQueryDrawable) +#define glXCreateNewContext XGL_FUNCPTR(glXCreateNewContext) +#define glXMakeContextCurrent XGL_FUNCPTR(glXMakeContextCurrent) +#define glXGetCurrentReadDrawable XGL_FUNCPTR(glXGetCurrentReadDrawable) +#define glXQueryContext XGL_FUNCPTR(glXQueryContext) +#define glXSelectEvent XGL_FUNCPTR(glXSelectEvent) +#define glXGetSelectedEvent XGL_FUNCPTR(glXGetSelectedEvent) +#endif + +#ifdef GLX_VERSION_1_4 +#define GLX_SAMPLE_BUFFERS 100000 +#define GLX_SAMPLES 100001 +#define glXGetProcAddress XGL_FUNCPTR(glXGetProcAddress) +#endif + + diff --git a/Engine/source/gfx/gl/ggl/generated/glxefn.h b/Engine/source/gfx/gl/ggl/generated/glxefn.h new file mode 100644 index 000000000..ea706137c --- /dev/null +++ b/Engine/source/gfx/gl/ggl/generated/glxefn.h @@ -0,0 +1,288 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef GLX_3DFX_multisample +GL_GROUP_BEGIN(GLX_3DFX_multisample) +GL_GROUP_END() +#endif + +#ifdef GLX_ARB_fbconfig_float +GL_GROUP_BEGIN(GLX_ARB_fbconfig_float) +GL_GROUP_END() +#endif + +#ifdef GLX_ARB_get_proc_address +GL_GROUP_BEGIN(GLX_ARB_get_proc_address) +GL_FUNCTION(glXGetProcAddressARB, GLFunction, (const GLubyte*)) +GL_GROUP_END() +#endif + +#ifdef GLX_ARB_multisample +GL_GROUP_BEGIN(GLX_ARB_multisample) +GL_GROUP_END() +#endif + +#ifdef GLX_ATI_pixel_format_float +GL_GROUP_BEGIN(GLX_ATI_pixel_format_float) +GL_GROUP_END() +#endif + +#ifdef GLX_ATI_render_texture +GL_GROUP_BEGIN(GLX_ATI_render_texture) +GL_FUNCTION(glXBindTexImageATI,void,(Display *dpy, GLXPbuffer pbuf, int buffer)) +GL_FUNCTION(glXReleaseTexImageATI,void,(Display *dpy, GLXPbuffer pbuf, int buffer)) +GL_FUNCTION(glXDrawableAttribATI,void,(Display *dpy, GLXDrawable draw, const int *attrib_list)) +GL_GROUP_END() +#endif + +#ifdef GLX_EXT_import_context +GL_GROUP_BEGIN(GLX_EXT_import_context) +GL_FUNCTION(glXFreeContextEXT,void,(Display* dpy, GLXContext context)) +GL_FUNCTION(glXGetContextIDEXT,GLXContextID,(const GLXContext context)) +GL_FUNCTION(glXImportContextEXT,GLXContext,(Display* dpy, GLXContextID contextID)) +GL_FUNCTION(glXQueryContextInfoEXT,int,(Display* dpy, GLXContext context, int attribute,int *value)) +GL_GROUP_END() +#endif + +#ifdef GLX_EXT_scene_marker +GL_GROUP_BEGIN(GLX_EXT_scene_marker) +GL_GROUP_END() +#endif + +#ifdef GLX_EXT_visual_info +GL_GROUP_BEGIN(GLX_EXT_visual_info) +GL_GROUP_END() +#endif + +#ifdef GLX_EXT_visual_rating +GL_GROUP_BEGIN(GLX_EXT_visual_rating) +GL_GROUP_END() +#endif + +#ifdef GLX_MESA_agp_offset +GL_GROUP_BEGIN(GLX_MESA_agp_offset) +GL_FUNCTION(glXGetAGPOffsetMESA,unsigned int,(const void* pointer)) +GL_GROUP_END() +#endif + +#ifdef GLX_MESA_copy_sub_buffer +GL_GROUP_BEGIN(GLX_MESA_copy_sub_buffer) +GL_FUNCTION(glXCopySubBufferMESA,void,(Display* dpy, GLXDrawable drawable, int x, int y, int width, int height)) +GL_GROUP_END() +#endif + +#ifdef GLX_MESA_pixmap_colormap +GL_GROUP_BEGIN(GLX_MESA_pixmap_colormap) +GL_FUNCTION(glXCreateGLXPixmapMESA,GLXPixmap,(Display* dpy, XVisualInfo *visual, Pixmap pixmap, Colormap cmap)) +GL_GROUP_END() +#endif + +#ifdef GLX_MESA_release_buffers +GL_GROUP_BEGIN(GLX_MESA_release_buffers) +GL_FUNCTION(glXReleaseBuffersMESA,Bool,(Display* dpy, GLXDrawable d)) +GL_GROUP_END() +#endif + +#ifdef GLX_MESA_set_3dfx_mode +GL_GROUP_BEGIN(GLX_MESA_set_3dfx_mode) +GL_FUNCTION(glXSet3DfxModeMESA,GLboolean,(GLint mode)) +GL_GROUP_END() +#endif + +#ifdef GLX_NV_float_buffer +GL_GROUP_BEGIN(GLX_NV_float_buffer) +GL_GROUP_END() +#endif + +#ifdef GLX_NV_vertex_array_range +GL_GROUP_BEGIN(GLX_NV_vertex_array_range) +GL_FUNCTION(glXAllocateMemoryNV,void *,(GLsizei size, GLfloat readFrequency, GLfloat writeFrequency, GLfloat priority)) +GL_FUNCTION(glXFreeMemoryNV,void,(void *pointer)) +GL_GROUP_END() +#endif + +#ifdef GLX_OML_swap_method +GL_GROUP_BEGIN(GLX_OML_swap_method) +GL_GROUP_END() +#endif + +#ifdef GLX_OML_sync_control +GL_GROUP_BEGIN(GLX_OML_sync_control) +GL_FUNCTION(glXGetMscRateOML,Bool,(Display* dpy, GLXDrawable drawable, int32_t* numerator, int32_t* denominator)) +GL_FUNCTION(glXGetSyncValuesOML,Bool,(Display* dpy, GLXDrawable drawable, int64_t* ust, int64_t* msc, int64_t* sbc)) +GL_FUNCTION(glXSwapBuffersMscOML,int64_t,(Display* dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder)) +GL_FUNCTION(glXWaitForMscOML,Bool,(Display* dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder, int64_t* ust, int64_t* msc, int64_t* sbc)) +GL_FUNCTION(glXWaitForSbcOML,Bool,(Display* dpy, GLXDrawable drawable, int64_t target_sbc, int64_t* ust, int64_t* msc, int64_t* sbc)) +GL_GROUP_END() +#endif + +#ifdef GLX_SGIS_blended_overlay +GL_GROUP_BEGIN(GLX_SGIS_blended_overlay) +GL_GROUP_END() +#endif + +#ifdef GLX_SGIS_color_range +GL_GROUP_BEGIN(GLX_SGIS_color_range) +GL_GROUP_END() +#endif + +#ifdef GLX_SGIS_multisample +GL_GROUP_BEGIN(GLX_SGIS_multisample) +GL_GROUP_END() +#endif + +#ifdef GLX_SGIS_shared_multisample +GL_GROUP_BEGIN(GLX_SGIS_shared_multisample) +GL_GROUP_END() +#endif + +#ifdef GLX_SGIX_fbconfig +GL_GROUP_BEGIN(GLX_SGIX_fbconfig) +GL_FUNCTION(glXChooseFBConfigSGIX,GLXFBConfigSGIX*,(Display *dpy, int screen, const int *attrib_list, int *nelements)) +GL_FUNCTION(glXCreateContextWithConfigSGIX,GLXContext,(Display* dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct)) +GL_FUNCTION(glXCreateGLXPixmapWithConfigSGIX,GLXPixmap,(Display* dpy, GLXFBConfig config, Pixmap pixmap)) +GL_FUNCTION(glXGetFBConfigAttribSGIX,int,(Display* dpy, GLXFBConfigSGIX config, int attribute, int *value)) +GL_FUNCTION(glXGetFBConfigFromVisualSGIX,GLXFBConfigSGIX,(Display* dpy, XVisualInfo *vis)) +GL_FUNCTION(glXGetVisualFromFBConfigSGIX,XVisualInfo*,(Display *dpy, GLXFBConfig config)) +GL_GROUP_END() +#endif + +#ifdef GLX_SGIX_pbuffer +GL_GROUP_BEGIN(GLX_SGIX_pbuffer) +GL_FUNCTION(glXCreateGLXPbufferSGIX,GLXPbuffer,(Display* dpy, GLXFBConfig config, unsigned int width, unsigned int height, int *attrib_list)) +GL_FUNCTION(glXDestroyGLXPbufferSGIX,void,(Display* dpy, GLXPbuffer pbuf)) +GL_FUNCTION(glXGetSelectedEventSGIX,void,(Display* dpy, GLXDrawable drawable, unsigned long *mask)) +GL_FUNCTION(glXQueryGLXPbufferSGIX,void,(Display* dpy, GLXPbuffer pbuf, int attribute, unsigned int *value)) +GL_FUNCTION(glXSelectEventSGIX,void,(Display* dpy, GLXDrawable drawable, unsigned long mask)) +GL_GROUP_END() +#endif + +#ifdef GLX_SGIX_swap_barrier +GL_GROUP_BEGIN(GLX_SGIX_swap_barrier) +GL_FUNCTION(glXBindSwapBarrierSGIX,void,(Display *dpy, GLXDrawable drawable, int barrier)) +GL_FUNCTION(glXQueryMaxSwapBarriersSGIX,Bool,(Display *dpy, int screen, int *max)) +GL_GROUP_END() +#endif + +#ifdef GLX_SGIX_swap_group +GL_GROUP_BEGIN(GLX_SGIX_swap_group) +GL_FUNCTION(glXJoinSwapGroupSGIX,void,(Display *dpy, GLXDrawable drawable, GLXDrawable member)) +GL_GROUP_END() +#endif + +#ifdef GLX_SGIX_video_resize +GL_GROUP_BEGIN(GLX_SGIX_video_resize) +GL_FUNCTION(glXBindChannelToWindowSGIX,int,(Display* display, int screen, int channel, Window window)) +GL_FUNCTION(glXChannelRectSGIX,int,(Display* display, int screen, int channel, int x, int y, int w, int h)) +GL_FUNCTION(glXChannelRectSyncSGIX,int,(Display* display, int screen, int channel, GLenum synctype)) +GL_FUNCTION(glXQueryChannelDeltasSGIX,int,(Display* display, int screen, int channel, int *x, int *y, int *w, int *h)) +GL_FUNCTION(glXQueryChannelRectSGIX,int,(Display* display, int screen, int channel, int *dx, int *dy, int *dw, int *dh)) +GL_GROUP_END() +#endif + +#ifdef GLX_SGIX_visual_select_group +GL_GROUP_BEGIN(GLX_SGIX_visual_select_group) +GL_GROUP_END() +#endif + +#ifdef GLX_SGI_cushion +GL_GROUP_BEGIN(GLX_SGI_cushion) +GL_FUNCTION(glXCushionSGI,void,(Display* dpy, Window window, float cushion)) +GL_GROUP_END() +#endif + +#ifdef GLX_SGI_make_current_read +GL_GROUP_BEGIN(GLX_SGI_make_current_read) +GL_FUNCTION(glXGetCurrentReadDrawableSGI,GLXDrawable,(void)) +GL_FUNCTION(glXMakeCurrentReadSGI,Bool,(Display* dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx)) +GL_GROUP_END() +#endif + +#ifdef GLX_SGI_swap_control +GL_GROUP_BEGIN(GLX_SGI_swap_control) +GL_FUNCTION(glXSwapIntervalSGI,int,(int interval)) +GL_GROUP_END() +#endif + +#ifdef GLX_SGI_video_sync +GL_GROUP_BEGIN(GLX_SGI_video_sync) +GL_FUNCTION(glXGetVideoSyncSGI,int,(uint* count)) +GL_FUNCTION(glXWaitVideoSyncSGI,int,(int divisor, int remainder, unsigned int* count)) +GL_GROUP_END() +#endif + +#ifdef GLX_SUN_get_transparent_index +GL_GROUP_BEGIN(GLX_SUN_get_transparent_index) +GL_FUNCTION(glXGetTransparentIndexSUN,Status,(Display* dpy, Window overlay, Window underlay, unsigned long *pTransparentIndex)) +GL_GROUP_END() +#endif + +#ifdef GLX_SUN_video_resize +GL_GROUP_BEGIN(GLX_SUN_video_resize) +GL_FUNCTION(glXVideoResizeSUN,int,(Display* display, GLXDrawable window, float factor)) +GL_FUNCTION(glXGetVideoResizeSUN,int,(Display* display, GLXDrawable window, float* factor)) +GL_GROUP_END() +#endif + +#ifdef GLX_VERSION_1_1 +GL_GROUP_BEGIN(GLX_VERSION_1_1) +GL_FUNCTION(glXQueryExtensionsString, const char*, (Display *dpy, int screen)) +GL_FUNCTION(glXGetClientString, const char*, (Display *dpy, int name)) +GL_FUNCTION(glXQueryServerString, const char*, (Display *dpy, int screen, int name)) +GL_GROUP_END() +#endif + +#ifdef GLX_VERSION_1_2 +GL_GROUP_BEGIN(GLX_VERSION_1_2) +GL_FUNCTION(glXGetCurrentDisplay, Display*, (void)) +GL_GROUP_END() +#endif + +#ifdef GLX_VERSION_1_3 +GL_GROUP_BEGIN(GLX_VERSION_1_3) +GL_FUNCTION(glXChooseFBConfig,GLXFBConfig*,(Display *dpy, int screen, const int *attrib_list, int *nelements)) +GL_FUNCTION(glXGetFBConfigs,GLXFBConfig*,(Display *dpy, int screen, int *nelements)) +GL_FUNCTION(glXGetVisualFromFBConfig,XVisualInfo*,(Display *dpy, GLXFBConfig config)) +GL_FUNCTION(glXGetFBConfigAttrib,int,(Display *dpy, GLXFBConfig config, int attribute, int *value)) +GL_FUNCTION(glXCreateWindow,GLXWindow,(Display *dpy, GLXFBConfig config, Window win, const int *attrib_list)) +GL_FUNCTION(glXDestroyWindow,void,(Display *dpy, GLXWindow win)) +GL_FUNCTION(glXCreatePixmap,GLXPixmap,(Display *dpy, GLXFBConfig config, Pixmap pixmap, const int *attrib_list)) +GL_FUNCTION(glXDestroyPixmap,void,(Display *dpy, GLXPixmap pixmap)) +GL_FUNCTION(glXCreatePbuffer,GLXPbuffer,(Display *dpy, GLXFBConfig config, const int *attrib_list)) +GL_FUNCTION(glXDestroyPbuffer,void,(Display *dpy, GLXPbuffer pbuf)) +GL_FUNCTION(glXQueryDrawable,void,(Display *dpy, GLXDrawable draw, int attribute, unsigned int *value)) +GL_FUNCTION(glXCreateNewContext,GLXContext,(Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct)) +GL_FUNCTION(glXMakeContextCurrent,Bool,(Display *display, GLXDrawable draw, GLXDrawable read, GLXContext ctx)) +GL_FUNCTION(glXGetCurrentReadDrawable,GLXDrawable,(void)) +GL_FUNCTION(glXQueryContext,int,(Display *dpy, GLXContext ctx, int attribute, int *value)) +GL_FUNCTION(glXSelectEvent,void,(Display *dpy, GLXDrawable draw, unsigned long event_mask)) +GL_FUNCTION(glXGetSelectedEvent,void,(Display *dpy, GLXDrawable draw, unsigned long *event_mask)) +GL_GROUP_END() +#endif + +#ifdef GLX_VERSION_1_4 +GL_GROUP_BEGIN(GLX_VERSION_1_4) +GL_FUNCTION(glXGetProcAddress, GLFunction, (const GLubyte *procName)) +GL_GROUP_END() +#endif + + diff --git a/Engine/source/gfx/gl/ggl/generated/glxfn.h b/Engine/source/gfx/gl/ggl/generated/glxfn.h new file mode 100644 index 000000000..d020ec20f --- /dev/null +++ b/Engine/source/gfx/gl/ggl/generated/glxfn.h @@ -0,0 +1,43 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// X11 gl functions +GL_GROUP_BEGIN(ARB_glx) +GL_FUNCTION(glXQueryExtension, Bool, (Display *dpy, int *errorBase, int *eventBase)) +GL_FUNCTION(glXQueryVersion, Bool, (Display *dpy, int *major, int *minor)) +GL_FUNCTION(glXGetConfig, int, (Display *dpy, XVisualInfo *vis, int attrib, int *value)) +GL_FUNCTION(glXChooseVisual, XVisualInfo*, (Display *dpy, int screen, int *attribList)) +GL_FUNCTION(glXCreateGLXPixmap, GLXPixmap, (Display *dpy, XVisualInfo *vis, Pixmap pixmap)) +GL_FUNCTION(glXDestroyGLXPixmap, void, (Display *dpy, GLXPixmap pix)) +GL_FUNCTION(glXCreateContext, GLXContext, (Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct)) +GL_FUNCTION(glXDestroyContext, void, (Display *dpy, GLXContext ctx)) +GL_FUNCTION(glXIsDirect, Bool, (Display *dpy, GLXContext ctx)) +GL_FUNCTION(glXCopyContext, void, (Display *dpy, GLXContext src, GLXContext dst, GLuint mask)) +GL_FUNCTION(glXMakeCurrent, Bool, (Display *dpy, GLXDrawable drawable, GLXContext ctx)) +GL_FUNCTION(glXGetCurrentContext, GLXContext, (void)) +GL_FUNCTION(glXGetCurrentDrawable, GLXDrawable, (void)) +GL_FUNCTION(glXWaitGL, void, (void)) +GL_FUNCTION(glXWaitX, void, (void)) +GL_FUNCTION(glXSwapBuffers, void, (Display *dpy, GLXDrawable drawable)) +GL_FUNCTION(glXUseXFont, void, (Font font, int first, int count, int listBase)) +GL_GROUP_END() + diff --git a/Engine/source/gfx/gl/ggl/generated/wgle.h b/Engine/source/gfx/gl/ggl/generated/wgle.h new file mode 100644 index 000000000..8864e1c72 --- /dev/null +++ b/Engine/source/gfx/gl/ggl/generated/wgle.h @@ -0,0 +1,382 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef WGL_3DFX_multisample +#define WGL_SAMPLE_BUFFERS_3DFX 0x2060 +#define WGL_SAMPLES_3DFX 0x2061 +#endif + +#ifdef WGL_ARB_buffer_region +#define WGL_FRONT_COLOR_BUFFER_BIT_ARB 0x00000001 +#define WGL_BACK_COLOR_BUFFER_BIT_ARB 0x00000002 +#define WGL_DEPTH_BUFFER_BIT_ARB 0x00000004 +#define WGL_STENCIL_BUFFER_BIT_ARB 0x00000008 +#define wglCreateBufferRegionARB XGL_FUNCPTR(wglCreateBufferRegionARB) +#define wglDeleteBufferRegionARB XGL_FUNCPTR(wglDeleteBufferRegionARB) +#define wglRestoreBufferRegionARB XGL_FUNCPTR(wglRestoreBufferRegionARB) +#define wglSaveBufferRegionARB XGL_FUNCPTR(wglSaveBufferRegionARB) +#endif + +#ifdef WGL_ARB_extensions_string +#define wglGetExtensionsStringARB XGL_FUNCPTR(wglGetExtensionsStringARB) +#endif + +#ifdef WGL_ARB_make_current_read +#define wglGetCurrentReadDCARB XGL_FUNCPTR(wglGetCurrentReadDCARB) +#define wglMakeContextCurrentARB XGL_FUNCPTR(wglMakeContextCurrentARB) +#endif + +#ifdef WGL_ARB_multisample +#define WGL_SAMPLE_BUFFERS_ARB 0x2041 +#define WGL_SAMPLES_ARB 0x2042 +#endif + +#ifdef WGL_ARB_pbuffer +DECLARE_HANDLE(HPBUFFERARB); +#define WGL_DRAW_TO_PBUFFER_ARB 0x202D +#define WGL_MAX_PBUFFER_PIXELS_ARB 0x202E +#define WGL_MAX_PBUFFER_WIDTH_ARB 0x202F +#define WGL_MAX_PBUFFER_HEIGHT_ARB 0x2030 +#define WGL_PBUFFER_LARGEST_ARB 0x2033 +#define WGL_PBUFFER_WIDTH_ARB 0x2034 +#define WGL_PBUFFER_HEIGHT_ARB 0x2035 +#define WGL_PBUFFER_LOST_ARB 0x2036 +#define wglCreatePbufferARB XGL_FUNCPTR(wglCreatePbufferARB) +#define wglDestroyPbufferARB XGL_FUNCPTR(wglDestroyPbufferARB) +#define wglGetPbufferDCARB XGL_FUNCPTR(wglGetPbufferDCARB) +#define wglQueryPbufferARB XGL_FUNCPTR(wglQueryPbufferARB) +#define wglReleasePbufferDCARB XGL_FUNCPTR(wglReleasePbufferDCARB) +#endif + +#ifdef WGL_ARB_pixel_format +#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_DRAW_TO_BITMAP_ARB 0x2002 +#define WGL_ACCELERATION_ARB 0x2003 +#define WGL_NEED_PALETTE_ARB 0x2004 +#define WGL_NEED_SYSTEM_PALETTE_ARB 0x2005 +#define WGL_SWAP_LAYER_BUFFERS_ARB 0x2006 +#define WGL_SWAP_METHOD_ARB 0x2007 +#define WGL_NUMBER_OVERLAYS_ARB 0x2008 +#define WGL_NUMBER_UNDERLAYS_ARB 0x2009 +#define WGL_TRANSPARENT_ARB 0x200A +#define WGL_SHARE_DEPTH_ARB 0x200C +#define WGL_SHARE_STENCIL_ARB 0x200D +#define WGL_SHARE_ACCUM_ARB 0x200E +#define WGL_SUPPORT_GDI_ARB 0x200F +#define WGL_SUPPORT_OPENGL_ARB 0x2010 +#define WGL_DOUBLE_BUFFER_ARB 0x2011 +#define WGL_STEREO_ARB 0x2012 +#define WGL_PIXEL_TYPE_ARB 0x2013 +#define WGL_COLOR_BITS_ARB 0x2014 +#define WGL_RED_BITS_ARB 0x2015 +#define WGL_RED_SHIFT_ARB 0x2016 +#define WGL_GREEN_BITS_ARB 0x2017 +#define WGL_GREEN_SHIFT_ARB 0x2018 +#define WGL_BLUE_BITS_ARB 0x2019 +#define WGL_BLUE_SHIFT_ARB 0x201A +#define WGL_ALPHA_BITS_ARB 0x201B +#define WGL_ALPHA_SHIFT_ARB 0x201C +#define WGL_ACCUM_BITS_ARB 0x201D +#define WGL_ACCUM_RED_BITS_ARB 0x201E +#define WGL_ACCUM_GREEN_BITS_ARB 0x201F +#define WGL_ACCUM_BLUE_BITS_ARB 0x2020 +#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021 +#define WGL_DEPTH_BITS_ARB 0x2022 +#define WGL_STENCIL_BITS_ARB 0x2023 +#define WGL_AUX_BUFFERS_ARB 0x2024 +#define WGL_NO_ACCELERATION_ARB 0x2025 +#define WGL_GENERIC_ACCELERATION_ARB 0x2026 +#define WGL_FULL_ACCELERATION_ARB 0x2027 +#define WGL_SWAP_EXCHANGE_ARB 0x2028 +#define WGL_SWAP_COPY_ARB 0x2029 +#define WGL_SWAP_UNDEFINED_ARB 0x202A +#define WGL_TYPE_RGBA_ARB 0x202B +#define WGL_TYPE_COLORINDEX_ARB 0x202C +#define WGL_TRANSPARENT_RED_VALUE_ARB 0x2037 +#define WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038 +#define WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039 +#define WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A +#define WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B +#define wglChoosePixelFormatARB XGL_FUNCPTR(wglChoosePixelFormatARB) +#define wglGetPixelFormatAttribfvARB XGL_FUNCPTR(wglGetPixelFormatAttribfvARB) +#define wglGetPixelFormatAttribivARB XGL_FUNCPTR(wglGetPixelFormatAttribivARB) +#endif + +#ifdef WGL_ARB_pixel_format_float +#define WGL_TYPE_RGBA_FLOAT_ARB 0x21A0 +#endif + +#ifdef WGL_ARB_render_texture +#define WGL_BIND_TO_TEXTURE_RGB_ARB 0x2070 +#define WGL_BIND_TO_TEXTURE_RGBA_ARB 0x2071 +#define WGL_TEXTURE_FORMAT_ARB 0x2072 +#define WGL_TEXTURE_TARGET_ARB 0x2073 +#define WGL_MIPMAP_TEXTURE_ARB 0x2074 +#define WGL_TEXTURE_RGB_ARB 0x2075 +#define WGL_TEXTURE_RGBA_ARB 0x2076 +#define WGL_NO_TEXTURE_ARB 0x2077 +#define WGL_TEXTURE_CUBE_MAP_ARB 0x2078 +#define WGL_TEXTURE_1D_ARB 0x2079 +#define WGL_TEXTURE_2D_ARB 0x207A +#define WGL_MIPMAP_LEVEL_ARB 0x207B +#define WGL_CUBE_MAP_FACE_ARB 0x207C +#define WGL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x207D +#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x207E +#define WGL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x207F +#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x2080 +#define WGL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x2081 +#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x2082 +#define WGL_FRONT_LEFT_ARB 0x2083 +#define WGL_FRONT_RIGHT_ARB 0x2084 +#define WGL_BACK_LEFT_ARB 0x2085 +#define WGL_BACK_RIGHT_ARB 0x2086 +#define WGL_AUX0_ARB 0x2087 +#define WGL_AUX1_ARB 0x2088 +#define WGL_AUX2_ARB 0x2089 +#define WGL_AUX3_ARB 0x208A +#define WGL_AUX4_ARB 0x208B +#define WGL_AUX5_ARB 0x208C +#define WGL_AUX6_ARB 0x208D +#define WGL_AUX7_ARB 0x208E +#define WGL_AUX8_ARB 0x208F +#define WGL_AUX9_ARB 0x2090 +#define wglBindTexImageARB XGL_FUNCPTR(wglBindTexImageARB) +#define wglReleaseTexImageARB XGL_FUNCPTR(wglReleaseTexImageARB) +#define wglSetPbufferAttribARB XGL_FUNCPTR(wglSetPbufferAttribARB) +#endif + +#ifdef WGL_ATI_pixel_format_float +#define WGL_TYPE_RGBA_FLOAT_ATI 0x21A0 +#define GL_RGBA_FLOAT_MODE_ATI 0x8820 +#define GL_COLOR_CLEAR_UNCLAMPED_VALUE_ATI 0x8835 +#endif + +#ifdef WGL_ATI_render_texture_rectangle +#define WGL_TEXTURE_RECTANGLE_ATI 0x21A5 +#endif + +#ifdef WGL_EXT_depth_float +#define WGL_DEPTH_FLOAT_EXT 0x2040 +#endif + +#ifdef WGL_EXT_display_color_table +#define wglBindDisplayColorTableEXT XGL_FUNCPTR(wglBindDisplayColorTableEXT) +#define wglCreateDisplayColorTableEXT XGL_FUNCPTR(wglCreateDisplayColorTableEXT) +#define wglDestroyDisplayColorTableEXT XGL_FUNCPTR(wglDestroyDisplayColorTableEXT) +#define wglLoadDisplayColorTableEXT XGL_FUNCPTR(wglLoadDisplayColorTableEXT) +#endif + +#ifdef WGL_EXT_extensions_string +#define wglGetExtensionsStringEXT XGL_FUNCPTR(wglGetExtensionsStringEXT) +#endif + +#ifdef WGL_EXT_make_current_read +#define wglGetCurrentReadDCEXT XGL_FUNCPTR(wglGetCurrentReadDCEXT) +#define wglMakeContextCurrentEXT XGL_FUNCPTR(wglMakeContextCurrentEXT) +#endif + +#ifdef WGL_EXT_multisample +#define WGL_SAMPLE_BUFFERS_EXT 0x2041 +#define WGL_SAMPLES_EXT 0x2042 +#endif + +#ifdef WGL_EXT_pbuffer +DECLARE_HANDLE(HPBUFFEREXT);; +#define WGL_DRAW_TO_PBUFFER_EXT 0x202D +#define WGL_MAX_PBUFFER_PIXELS_EXT 0x202E +#define WGL_MAX_PBUFFER_WIDTH_EXT 0x202F +#define WGL_MAX_PBUFFER_HEIGHT_EXT 0x2030 +#define WGL_OPTIMAL_PBUFFER_WIDTH_EXT 0x2031 +#define WGL_OPTIMAL_PBUFFER_HEIGHT_EXT 0x2032 +#define WGL_PBUFFER_LARGEST_EXT 0x2033 +#define WGL_PBUFFER_WIDTH_EXT 0x2034 +#define WGL_PBUFFER_HEIGHT_EXT 0x2035 +#define wglCreatePbufferEXT XGL_FUNCPTR(wglCreatePbufferEXT) +#define wglDestroyPbufferEXT XGL_FUNCPTR(wglDestroyPbufferEXT) +#define wglGetPbufferDCEXT XGL_FUNCPTR(wglGetPbufferDCEXT) +#define wglQueryPbufferEXT XGL_FUNCPTR(wglQueryPbufferEXT) +#define wglReleasePbufferDCEXT XGL_FUNCPTR(wglReleasePbufferDCEXT) +#endif + +#ifdef WGL_EXT_pixel_format +#define WGL_NUMBER_PIXEL_FORMATS_EXT 0x2000 +#define WGL_DRAW_TO_WINDOW_EXT 0x2001 +#define WGL_DRAW_TO_BITMAP_EXT 0x2002 +#define WGL_ACCELERATION_EXT 0x2003 +#define WGL_NEED_PALETTE_EXT 0x2004 +#define WGL_NEED_SYSTEM_PALETTE_EXT 0x2005 +#define WGL_SWAP_LAYER_BUFFERS_EXT 0x2006 +#define WGL_SWAP_METHOD_EXT 0x2007 +#define WGL_NUMBER_OVERLAYS_EXT 0x2008 +#define WGL_NUMBER_UNDERLAYS_EXT 0x2009 +#define WGL_TRANSPARENT_EXT 0x200A +#define WGL_TRANSPARENT_VALUE_EXT 0x200B +#define WGL_SHARE_DEPTH_EXT 0x200C +#define WGL_SHARE_STENCIL_EXT 0x200D +#define WGL_SHARE_ACCUM_EXT 0x200E +#define WGL_SUPPORT_GDI_EXT 0x200F +#define WGL_SUPPORT_OPENGL_EXT 0x2010 +#define WGL_DOUBLE_BUFFER_EXT 0x2011 +#define WGL_STEREO_EXT 0x2012 +#define WGL_PIXEL_TYPE_EXT 0x2013 +#define WGL_COLOR_BITS_EXT 0x2014 +#define WGL_RED_BITS_EXT 0x2015 +#define WGL_RED_SHIFT_EXT 0x2016 +#define WGL_GREEN_BITS_EXT 0x2017 +#define WGL_GREEN_SHIFT_EXT 0x2018 +#define WGL_BLUE_BITS_EXT 0x2019 +#define WGL_BLUE_SHIFT_EXT 0x201A +#define WGL_ALPHA_BITS_EXT 0x201B +#define WGL_ALPHA_SHIFT_EXT 0x201C +#define WGL_ACCUM_BITS_EXT 0x201D +#define WGL_ACCUM_RED_BITS_EXT 0x201E +#define WGL_ACCUM_GREEN_BITS_EXT 0x201F +#define WGL_ACCUM_BLUE_BITS_EXT 0x2020 +#define WGL_ACCUM_ALPHA_BITS_EXT 0x2021 +#define WGL_DEPTH_BITS_EXT 0x2022 +#define WGL_STENCIL_BITS_EXT 0x2023 +#define WGL_AUX_BUFFERS_EXT 0x2024 +#define WGL_NO_ACCELERATION_EXT 0x2025 +#define WGL_GENERIC_ACCELERATION_EXT 0x2026 +#define WGL_FULL_ACCELERATION_EXT 0x2027 +#define WGL_SWAP_EXCHANGE_EXT 0x2028 +#define WGL_SWAP_COPY_EXT 0x2029 +#define WGL_SWAP_UNDEFINED_EXT 0x202A +#define WGL_TYPE_RGBA_EXT 0x202B +#define WGL_TYPE_COLORINDEX_EXT 0x202C +#define wglChoosePixelFormatEXT XGL_FUNCPTR(wglChoosePixelFormatEXT) +#define wglGetPixelFormatAttribfvEXT XGL_FUNCPTR(wglGetPixelFormatAttribfvEXT) +#define wglGetPixelFormatAttribivEXT XGL_FUNCPTR(wglGetPixelFormatAttribivEXT) +#endif + +#ifdef WGL_EXT_swap_control +#define wglGetSwapIntervalEXT XGL_FUNCPTR(wglGetSwapIntervalEXT) +#define wglSwapIntervalEXT XGL_FUNCPTR(wglSwapIntervalEXT) +#endif + +#ifdef WGL_I3D_digital_video_control +#define WGL_DIGITAL_VIDEO_CURSOR_ALPHA_FRAMEBUFFER_I3D 0x2050 +#define WGL_DIGITAL_VIDEO_CURSOR_ALPHA_VALUE_I3D 0x2051 +#define WGL_DIGITAL_VIDEO_CURSOR_INCLUDED_I3D 0x2052 +#define WGL_DIGITAL_VIDEO_GAMMA_CORRECTED_I3D 0x2053 +#define wglGetDigitalVideoParametersI3D XGL_FUNCPTR(wglGetDigitalVideoParametersI3D) +#define wglSetDigitalVideoParametersI3D XGL_FUNCPTR(wglSetDigitalVideoParametersI3D) +#endif + +#ifdef WGL_I3D_gamma +#define WGL_GAMMA_TABLE_SIZE_I3D 0x204E +#define WGL_GAMMA_EXCLUDE_DESKTOP_I3D 0x204F +#define wglGetGammaTableI3D XGL_FUNCPTR(wglGetGammaTableI3D) +#define wglGetGammaTableParametersI3D XGL_FUNCPTR(wglGetGammaTableParametersI3D) +#define wglSetGammaTableI3D XGL_FUNCPTR(wglSetGammaTableI3D) +#define wglSetGammaTableParametersI3D XGL_FUNCPTR(wglSetGammaTableParametersI3D) +#endif + +#ifdef WGL_I3D_genlock +#define WGL_GENLOCK_SOURCE_MULTIVIEW_I3D 0x2044 +#define WGL_GENLOCK_SOURCE_EXTERNAL_SYNC_I3D 0x2045 +#define WGL_GENLOCK_SOURCE_EXTERNAL_FIELD_I3D 0x2046 +#define WGL_GENLOCK_SOURCE_EXTERNAL_TTL_I3D 0x2047 +#define WGL_GENLOCK_SOURCE_DIGITAL_SYNC_I3D 0x2048 +#define WGL_GENLOCK_SOURCE_DIGITAL_FIELD_I3D 0x2049 +#define WGL_GENLOCK_SOURCE_EDGE_FALLING_I3D 0x204A +#define WGL_GENLOCK_SOURCE_EDGE_RISING_I3D 0x204B +#define WGL_GENLOCK_SOURCE_EDGE_BOTH_I3D 0x204C +#define wglDisableGenlockI3D XGL_FUNCPTR(wglDisableGenlockI3D) +#define wglEnableGenlockI3D XGL_FUNCPTR(wglEnableGenlockI3D) +#define wglGenlockSampleRateI3D XGL_FUNCPTR(wglGenlockSampleRateI3D) +#define wglGenlockSourceDelayI3D XGL_FUNCPTR(wglGenlockSourceDelayI3D) +#define wglGenlockSourceEdgeI3D XGL_FUNCPTR(wglGenlockSourceEdgeI3D) +#define wglGenlockSourceI3D XGL_FUNCPTR(wglGenlockSourceI3D) +#define wglGetGenlockSampleRateI3D XGL_FUNCPTR(wglGetGenlockSampleRateI3D) +#define wglGetGenlockSourceDelayI3D XGL_FUNCPTR(wglGetGenlockSourceDelayI3D) +#define wglGetGenlockSourceEdgeI3D XGL_FUNCPTR(wglGetGenlockSourceEdgeI3D) +#define wglGetGenlockSourceI3D XGL_FUNCPTR(wglGetGenlockSourceI3D) +#define wglIsEnabledGenlockI3D XGL_FUNCPTR(wglIsEnabledGenlockI3D) +#define wglQueryGenlockMaxSourceDelayI3D XGL_FUNCPTR(wglQueryGenlockMaxSourceDelayI3D) +#endif + +#ifdef WGL_I3D_image_buffer +#define WGL_IMAGE_BUFFER_MIN_ACCESS_I3D 0x00000001 +#define WGL_IMAGE_BUFFER_LOCK_I3D 0x00000002 +#define wglAssociateImageBufferEventsI3D XGL_FUNCPTR(wglAssociateImageBufferEventsI3D) +#define wglCreateImageBufferI3D XGL_FUNCPTR(wglCreateImageBufferI3D) +#define wglDestroyImageBufferI3D XGL_FUNCPTR(wglDestroyImageBufferI3D) +#define wglReleaseImageBufferEventsI3D XGL_FUNCPTR(wglReleaseImageBufferEventsI3D) +#endif + +#ifdef WGL_I3D_swap_frame_lock +#define wglDisableFrameLockI3D XGL_FUNCPTR(wglDisableFrameLockI3D) +#define wglEnableFrameLockI3D XGL_FUNCPTR(wglEnableFrameLockI3D) +#define wglIsEnabledFrameLockI3D XGL_FUNCPTR(wglIsEnabledFrameLockI3D) +#define wglQueryFrameLockMasterI3D XGL_FUNCPTR(wglQueryFrameLockMasterI3D) +#endif + +#ifdef WGL_I3D_swap_frame_usage +#define wglBeginFrameTrackingI3D XGL_FUNCPTR(wglBeginFrameTrackingI3D) +#define wglEndFrameTrackingI3D XGL_FUNCPTR(wglEndFrameTrackingI3D) +#define wglGetFrameUsageI3D XGL_FUNCPTR(wglGetFrameUsageI3D) +#define wglQueryFrameTrackingI3D XGL_FUNCPTR(wglQueryFrameTrackingI3D) +#endif + +#ifdef WGL_NV_float_buffer +#define WGL_FLOAT_COMPONENTS_NV 0x20B0 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_R_NV 0x20B1 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RG_NV 0x20B2 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RGB_NV 0x20B3 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RGBA_NV 0x20B4 +#define WGL_TEXTURE_FLOAT_R_NV 0x20B5 +#define WGL_TEXTURE_FLOAT_RG_NV 0x20B6 +#define WGL_TEXTURE_FLOAT_RGB_NV 0x20B7 +#define WGL_TEXTURE_FLOAT_RGBA_NV 0x20B8 +#endif + +#ifdef WGL_NV_render_depth_texture +#define WGL_NO_TEXTURE_ARB 0x2077 +#define WGL_BIND_TO_TEXTURE_DEPTH_NV 0x20A3 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_DEPTH_NV 0x20A4 +#define WGL_DEPTH_TEXTURE_FORMAT_NV 0x20A5 +#define WGL_TEXTURE_DEPTH_COMPONENT_NV 0x20A6 +#define WGL_DEPTH_COMPONENT_NV 0x20A7 +#endif + +#ifdef WGL_NV_render_texture_rectangle +#define WGL_BIND_TO_TEXTURE_RECTANGLE_RGB_NV 0x20A0 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_RGBA_NV 0x20A1 +#define WGL_TEXTURE_RECTANGLE_NV 0x20A2 +#endif + +#ifdef WGL_NV_vertex_array_range +#define wglAllocateMemoryNV XGL_FUNCPTR(wglAllocateMemoryNV) +#define wglFreeMemoryNV XGL_FUNCPTR(wglFreeMemoryNV) +#endif + +#ifdef WGL_OML_sync_control +#define wglGetMscRateOML XGL_FUNCPTR(wglGetMscRateOML) +#define wglGetSyncValuesOML XGL_FUNCPTR(wglGetSyncValuesOML) +#define wglSwapBuffersMscOML XGL_FUNCPTR(wglSwapBuffersMscOML) +#define wglSwapLayerBuffersMscOML XGL_FUNCPTR(wglSwapLayerBuffersMscOML) +#define wglWaitForMscOML XGL_FUNCPTR(wglWaitForMscOML) +#define wglWaitForSbcOML XGL_FUNCPTR(wglWaitForSbcOML) +#endif + diff --git a/Engine/source/gfx/gl/ggl/generated/wglefn.h b/Engine/source/gfx/gl/ggl/generated/wglefn.h new file mode 100644 index 000000000..b7595a3f6 --- /dev/null +++ b/Engine/source/gfx/gl/ggl/generated/wglefn.h @@ -0,0 +1,245 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef WGL_3DFX_multisample +GL_GROUP_BEGIN(WGL_3DFX_multisample) +GL_GROUP_END() +#endif + +#ifdef WGL_ARB_buffer_region +GL_GROUP_BEGIN(WGL_ARB_buffer_region) +GL_FUNCTION(wglCreateBufferRegionARB,HANDLE,(HDC hDC, int iLayerPlane, UINT uType)) +GL_FUNCTION(wglDeleteBufferRegionARB,VOID,(HANDLE hRegion)) +GL_FUNCTION(wglRestoreBufferRegionARB,BOOL,(HANDLE hRegion, int x, int y, int width, int height, int xSrc, int ySrc)) +GL_FUNCTION(wglSaveBufferRegionARB,BOOL,(HANDLE hRegion, int x, int y, int width, int height)) +GL_GROUP_END() +#endif + +#ifdef WGL_ARB_extensions_string +GL_GROUP_BEGIN(WGL_ARB_extensions_string) +GL_FUNCTION(wglGetExtensionsStringARB,const char*,(HDC hdc)) +GL_GROUP_END() +#endif + +#ifdef WGL_ARB_make_current_read +GL_GROUP_BEGIN(WGL_ARB_make_current_read) +GL_FUNCTION(wglGetCurrentReadDCARB,HDC,(VOID)) +GL_FUNCTION(wglMakeContextCurrentARB,BOOL,(HDC hDrawDC, HDC hReadDC, HGLRC hglrc)) +GL_GROUP_END() +#endif + +#ifdef WGL_ARB_multisample +GL_GROUP_BEGIN(WGL_ARB_multisample) +GL_GROUP_END() +#endif + +#ifdef WGL_ARB_pbuffer +GL_GROUP_BEGIN(WGL_ARB_pbuffer) +GL_FUNCTION(wglCreatePbufferARB,HPBUFFERARB,(HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int* piAttribList)) +GL_FUNCTION(wglDestroyPbufferARB,BOOL,(HPBUFFERARB hPbuffer)) +GL_FUNCTION(wglGetPbufferDCARB,HDC,(HPBUFFERARB hPbuffer)) +GL_FUNCTION(wglQueryPbufferARB,BOOL,(HPBUFFERARB hPbuffer, int iAttribute, int* piValue)) +GL_FUNCTION(wglReleasePbufferDCARB,int,(HPBUFFERARB hPbuffer, HDC hDC)) +GL_GROUP_END() +#endif + +#ifdef WGL_ARB_pixel_format +GL_GROUP_BEGIN(WGL_ARB_pixel_format) +GL_FUNCTION(wglChoosePixelFormatARB,BOOL,(HDC hdc, const int* piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats)) +GL_FUNCTION(wglGetPixelFormatAttribfvARB,BOOL,(HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int* piAttributes, FLOAT *pfValues)) +GL_FUNCTION(wglGetPixelFormatAttribivARB,BOOL,(HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int* piAttributes, int *piValues)) +GL_GROUP_END() +#endif + +#ifdef WGL_ARB_pixel_format_float +GL_GROUP_BEGIN(WGL_ARB_pixel_format_float) +GL_GROUP_END() +#endif + +#ifdef WGL_ARB_render_texture +GL_GROUP_BEGIN(WGL_ARB_render_texture) +GL_FUNCTION(wglBindTexImageARB,BOOL,(HPBUFFERARB hPbuffer, int iBuffer)) +GL_FUNCTION(wglReleaseTexImageARB,BOOL,(HPBUFFERARB hPbuffer, int iBuffer)) +GL_FUNCTION(wglSetPbufferAttribARB,BOOL,(HPBUFFERARB hPbuffer, const int* piAttribList)) +GL_GROUP_END() +#endif + +#ifdef WGL_ATI_pixel_format_float +GL_GROUP_BEGIN(WGL_ATI_pixel_format_float) +GL_GROUP_END() +#endif + +#ifdef WGL_ATI_render_texture_rectangle +GL_GROUP_BEGIN(WGL_ATI_render_texture_rectangle) +GL_GROUP_END() +#endif + +#ifdef WGL_EXT_depth_float +GL_GROUP_BEGIN(WGL_EXT_depth_float) +GL_GROUP_END() +#endif + +#ifdef WGL_EXT_display_color_table +GL_GROUP_BEGIN(WGL_EXT_display_color_table) +GL_FUNCTION(wglBindDisplayColorTableEXT,GLboolean,(GLushort id)) +GL_FUNCTION(wglCreateDisplayColorTableEXT,GLboolean,(GLushort id)) +GL_FUNCTION(wglDestroyDisplayColorTableEXT,void,(GLushort id)) +GL_FUNCTION(wglLoadDisplayColorTableEXT,GLboolean,(GLushort* table, GLuint length)) +GL_GROUP_END() +#endif + +#ifdef WGL_EXT_extensions_string +GL_GROUP_BEGIN(WGL_EXT_extensions_string) +GL_FUNCTION(wglGetExtensionsStringEXT,const char*,(void)) +GL_GROUP_END() +#endif + +#ifdef WGL_EXT_make_current_read +GL_GROUP_BEGIN(WGL_EXT_make_current_read) +GL_FUNCTION(wglGetCurrentReadDCEXT,HDC,(VOID)) +GL_FUNCTION(wglMakeContextCurrentEXT,BOOL,(HDC hDrawDC, HDC hReadDC, HGLRC hglrc)) +GL_GROUP_END() +#endif + +#ifdef WGL_EXT_multisample +GL_GROUP_BEGIN(WGL_EXT_multisample) +GL_GROUP_END() +#endif + +#ifdef WGL_EXT_pbuffer +GL_GROUP_BEGIN(WGL_EXT_pbuffer) +GL_FUNCTION(wglCreatePbufferEXT,HPBUFFEREXT,(HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int* piAttribList)) +GL_FUNCTION(wglDestroyPbufferEXT,BOOL,(HPBUFFEREXT hPbuffer)) +GL_FUNCTION(wglGetPbufferDCEXT,HDC,(HPBUFFEREXT hPbuffer)) +GL_FUNCTION(wglQueryPbufferEXT,BOOL,(HPBUFFEREXT hPbuffer, int iAttribute, int* piValue)) +GL_FUNCTION(wglReleasePbufferDCEXT,int,(HPBUFFEREXT hPbuffer, HDC hDC)) +GL_GROUP_END() +#endif + +#ifdef WGL_EXT_pixel_format +GL_GROUP_BEGIN(WGL_EXT_pixel_format) +GL_FUNCTION(wglChoosePixelFormatEXT,BOOL,(HDC hdc, const int* piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats)) +GL_FUNCTION(wglGetPixelFormatAttribfvEXT,BOOL,(HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int* piAttributes, FLOAT *pfValues)) +GL_FUNCTION(wglGetPixelFormatAttribivEXT,BOOL,(HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int* piAttributes, int *piValues)) +GL_GROUP_END() +#endif + +#ifdef WGL_EXT_swap_control +GL_GROUP_BEGIN(WGL_EXT_swap_control) +GL_FUNCTION(wglGetSwapIntervalEXT,int,(void)) +GL_FUNCTION(wglSwapIntervalEXT,BOOL,(int interval)) +GL_GROUP_END() +#endif + +#ifdef WGL_I3D_digital_video_control +GL_GROUP_BEGIN(WGL_I3D_digital_video_control) +GL_FUNCTION(wglGetDigitalVideoParametersI3D,BOOL,(HDC hDC, int iAttribute, int* piValue)) +GL_FUNCTION(wglSetDigitalVideoParametersI3D,BOOL,(HDC hDC, int iAttribute, const int* piValue)) +GL_GROUP_END() +#endif + +#ifdef WGL_I3D_gamma +GL_GROUP_BEGIN(WGL_I3D_gamma) +GL_FUNCTION(wglGetGammaTableI3D,BOOL,(HDC hDC, int iEntries, USHORT* puRed, USHORT *puGreen, USHORT *puBlue)) +GL_FUNCTION(wglGetGammaTableParametersI3D,BOOL,(HDC hDC, int iAttribute, int* piValue)) +GL_FUNCTION(wglSetGammaTableI3D,BOOL,(HDC hDC, int iEntries, const USHORT* puRed, const USHORT *puGreen, const USHORT *puBlue)) +GL_FUNCTION(wglSetGammaTableParametersI3D,BOOL,(HDC hDC, int iAttribute, const int* piValue)) +GL_GROUP_END() +#endif + +#ifdef WGL_I3D_genlock +GL_GROUP_BEGIN(WGL_I3D_genlock) +GL_FUNCTION(wglDisableGenlockI3D,BOOL,(HDC hDC)) +GL_FUNCTION(wglEnableGenlockI3D,BOOL,(HDC hDC)) +GL_FUNCTION(wglGenlockSampleRateI3D,BOOL,(HDC hDC, UINT uRate)) +GL_FUNCTION(wglGenlockSourceDelayI3D,BOOL,(HDC hDC, UINT uDelay)) +GL_FUNCTION(wglGenlockSourceEdgeI3D,BOOL,(HDC hDC, UINT uEdge)) +GL_FUNCTION(wglGenlockSourceI3D,BOOL,(HDC hDC, UINT uSource)) +GL_FUNCTION(wglGetGenlockSampleRateI3D,BOOL,(HDC hDC, UINT* uRate)) +GL_FUNCTION(wglGetGenlockSourceDelayI3D,BOOL,(HDC hDC, UINT* uDelay)) +GL_FUNCTION(wglGetGenlockSourceEdgeI3D,BOOL,(HDC hDC, UINT* uEdge)) +GL_FUNCTION(wglGetGenlockSourceI3D,BOOL,(HDC hDC, UINT* uSource)) +GL_FUNCTION(wglIsEnabledGenlockI3D,BOOL,(HDC hDC, BOOL* pFlag)) +GL_FUNCTION(wglQueryGenlockMaxSourceDelayI3D,BOOL,(HDC hDC, UINT* uMaxLineDelay, UINT *uMaxPixelDelay)) +GL_GROUP_END() +#endif + +#ifdef WGL_I3D_image_buffer +GL_GROUP_BEGIN(WGL_I3D_image_buffer) +GL_FUNCTION(wglAssociateImageBufferEventsI3D,BOOL,(HDC hdc, HANDLE* pEvent, LPVOID *pAddress, DWORD *pSize, UINT count)) +GL_FUNCTION(wglCreateImageBufferI3D,LPVOID,(HDC hDC, DWORD dwSize, UINT uFlags)) +GL_FUNCTION(wglDestroyImageBufferI3D,BOOL,(HDC hDC, LPVOID pAddress)) +GL_FUNCTION(wglReleaseImageBufferEventsI3D,BOOL,(HDC hdc, LPVOID* pAddress, UINT count)) +GL_GROUP_END() +#endif + +#ifdef WGL_I3D_swap_frame_lock +GL_GROUP_BEGIN(WGL_I3D_swap_frame_lock) +GL_FUNCTION(wglDisableFrameLockI3D,BOOL,(VOID)) +GL_FUNCTION(wglEnableFrameLockI3D,BOOL,(VOID)) +GL_FUNCTION(wglIsEnabledFrameLockI3D,BOOL,(BOOL* pFlag)) +GL_FUNCTION(wglQueryFrameLockMasterI3D,BOOL,(BOOL* pFlag)) +GL_GROUP_END() +#endif + +#ifdef WGL_I3D_swap_frame_usage +GL_GROUP_BEGIN(WGL_I3D_swap_frame_usage) +GL_FUNCTION(wglBeginFrameTrackingI3D,BOOL,(void)) +GL_FUNCTION(wglEndFrameTrackingI3D,BOOL,(void)) +GL_FUNCTION(wglGetFrameUsageI3D,BOOL,(float* pUsage)) +GL_FUNCTION(wglQueryFrameTrackingI3D,BOOL,(DWORD* pFrameCount, DWORD *pMissedFrames, float *pLastMissedUsage)) +GL_GROUP_END() +#endif + +#ifdef WGL_NV_float_buffer +GL_GROUP_BEGIN(WGL_NV_float_buffer) +GL_GROUP_END() +#endif + +#ifdef WGL_NV_render_depth_texture +GL_GROUP_BEGIN(WGL_NV_render_depth_texture) +GL_GROUP_END() +#endif + +#ifdef WGL_NV_render_texture_rectangle +GL_GROUP_BEGIN(WGL_NV_render_texture_rectangle) +GL_GROUP_END() +#endif + +#ifdef WGL_NV_vertex_array_range +GL_GROUP_BEGIN(WGL_NV_vertex_array_range) +GL_FUNCTION(wglAllocateMemoryNV,void *,(GLsizei size, GLfloat readFrequency, GLfloat writeFrequency, GLfloat priority)) +GL_FUNCTION(wglFreeMemoryNV,void,(void *pointer)) +GL_GROUP_END() +#endif + +#ifdef WGL_OML_sync_control +GL_GROUP_BEGIN(WGL_OML_sync_control) +GL_FUNCTION(wglGetMscRateOML,BOOL,(HDC hdc, INT32* numerator, INT32 *denominator)) +GL_FUNCTION(wglGetSyncValuesOML,BOOL,(HDC hdc, INT64* ust, INT64 *msc, INT64 *sbc)) +GL_FUNCTION(wglSwapBuffersMscOML,INT64,(HDC hdc, INT64 target_msc, INT64 divisor, INT64 remainder)) +GL_FUNCTION(wglSwapLayerBuffersMscOML,INT64,(HDC hdc, INT fuPlanes, INT64 target_msc, INT64 divisor, INT64 remainder)) +GL_FUNCTION(wglWaitForMscOML,BOOL,(HDC hdc, INT64 target_msc, INT64 divisor, INT64 remainder, INT64* ust, INT64 *msc, INT64 *sbc)) +GL_FUNCTION(wglWaitForSbcOML,BOOL,(HDC hdc, INT64 target_sbc, INT64* ust, INT64 *msc, INT64 *sbc)) +GL_GROUP_END() +#endif + diff --git a/Engine/source/gfx/gl/ggl/generated/wglfn.h b/Engine/source/gfx/gl/ggl/generated/wglfn.h new file mode 100644 index 000000000..1ac8c54f0 --- /dev/null +++ b/Engine/source/gfx/gl/ggl/generated/wglfn.h @@ -0,0 +1,47 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// Windows WGL functions +GL_GROUP_BEGIN(ARB_win32) +GL_FUNCTION(wglCopyContext, BOOL, (HGLRC, HGLRC, UINT)) +GL_FUNCTION(wglCreateContext, HGLRC, (HDC)) +GL_FUNCTION(wglCreateLayerContext, HGLRC, (HDC, GLint)) +GL_FUNCTION(wglDeleteContext, BOOL, (HGLRC)) +GL_FUNCTION(wglGetCurrentContext, HGLRC, (VOID)) +GL_FUNCTION(wglGetCurrentDC, HDC, (VOID)) +GL_FUNCTION(wglGetProcAddress, PROC, (LPCSTR)) +GL_FUNCTION(wglMakeCurrent, BOOL, (HDC, HGLRC)) +GL_FUNCTION(wglShareLists, BOOL, (HGLRC, HGLRC)) +GL_FUNCTION(wglDescribeLayerPlane, BOOL, (HDC, GLint, GLint, UINT, LPLAYERPLANEDESCRIPTOR)) +GL_FUNCTION(wglSetLayerPaletteEntries, GLint, (HDC, GLint, GLint, GLint, CONST COLORREF *)) +GL_FUNCTION(wglGetLayerPaletteEntries, GLint, (HDC, GLint, GLint, GLint, COLORREF *)) +GL_FUNCTION(wglRealizeLayerPalette, BOOL, (HDC, GLint, BOOL)) +GL_FUNCTION(wglSwapLayerBuffers, BOOL, (HDC, UINT)) + +// Ascii and Unicode versions +GL_FUNCTION(wglUseFontBitmapsA, BOOL, (HDC, DWORD, DWORD, DWORD)) +GL_FUNCTION(wglUseFontOutlinesA, BOOL, (HDC, DWORD, DWORD, DWORD, FLOAT, FLOAT, GLint, LPGLYPHMETRICSFLOAT)) +GL_FUNCTION(wglUseFontBitmapsW, BOOL, (HDC, DWORD, DWORD, DWORD)) +GL_FUNCTION(wglUseFontOutlinesW, BOOL, (HDC, DWORD, DWORD, DWORD, FLOAT, FLOAT, GLint, LPGLYPHMETRICSFLOAT)) + +GL_GROUP_END() + diff --git a/Engine/source/gfx/gl/ggl/generator/parse.cpp b/Engine/source/gfx/gl/ggl/generator/parse.cpp new file mode 100644 index 000000000..77ed80ce8 --- /dev/null +++ b/Engine/source/gfx/gl/ggl/generator/parse.cpp @@ -0,0 +1,626 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +#include "os/osFile.h" +#include "os/osPrint.h" +#include "util/utilStr.h" +#include "util/utilString.h" +#include "util/utilArray.h" +#include "util/utilMap.h" +#include "util/utilSort.h" + +using namespace Torque; + +#include +#include + +//----------------------------------------------------------------------------- + +const char* OrderTable[] = +{ + // Extension list as of 8.25.2005 + // http://oss.sgi.com/projects/ogl-sample/registry/ + + // Core + "GL_VERSION_1_1", + "GL_VERSION_1_2", + "GL_VERSION_1_3", + "GL_VERSION_1_4", + "GL_VERSION_1_5", + + // X Windows + "GLX_VERSION_1_1", + "GLX_VERSION_1_2", + "GLX_VERSION_1_3", + "GLX_VERSION_1_4", + "GLX_VERSION_1_5", + + // Arb extension + "GL_ARB_multitexture", + "GLX_ARB_get_proc_address", + "GL_ARB_transpose_matrix", + "WGL_ARB_buffer_region", + "GL_ARB_multisample", + "GL_ARB_texture_env_add", + "GL_ARB_texture_cube_map", + "WGL_ARB_extensions_string", + "WGL_ARB_pixel_format", + "WGL_ARB_make_current_read", + "WGL_ARB_pbuffer", + "GL_ARB_texture_compression", + "GL_ARB_texture_border_clamp", + "GL_ARB_point_parameters", + "GL_ARB_vertex_blend", + "GL_ARB_matrix_palette", + "GL_ARB_texture_env_combine", + "GL_ARB_texture_env_crossbar", + "GL_ARB_texture_env_dot3", + "WGL_ARB_render_texture", + "GL_ARB_texture_mirrored_repeat", + "GL_ARB_depth_texture", + "GL_ARB_shadow", + "GL_ARB_shadow_ambient", + "GL_ARB_window_pos", + "GL_ARB_vertex_program", + "GL_ARB_fragment_program", + "GL_ARB_vertex_buffer_object", + "GL_ARB_occlusion_query", + "GL_ARB_shader_objects", + "GL_ARB_vertex_shader", + "GL_ARB_fragment_shader", + "GL_ARB_shading_language_100", + "GL_ARB_texture_non_power_of_two", + "GL_ARB_point_sprite", + "GL_ARB_fragment_program_shadow", + "GL_ARB_draw_buffers", + "GL_ARB_texture_rectangle", + "GL_ARB_color_buffer_float", + "GL_ARB_half_float_pixel", + "GL_ARB_texture_float", + "GL_ARB_pixel_buffer_object", + + // Misc extensions + "GL_EXT_abgr", + "GL_EXT_blend_color", + "GL_EXT_polygon_offset", + "GL_EXT_texture", + "GL_EXT_texture3D", + "GL_SGIS_texture_filter4", + "GL_EXT_subtexture", + "GL_EXT_copy_texture", + "GL_EXT_histogram", + "GL_EXT_convolution", + "GL_SGI_color_matrix", + "GL_SGI_color_table", + "GL_SGIS_pixel_texture", + "GL_SGIX_pixel_texture", + "GL_SGIS_texture4D", + "GL_SGI_texture_color_table", + "GL_EXT_cmyka", + "GL_EXT_texture_object", + "GL_SGIS_detail_texture", + "GL_SGIS_sharpen_texture", + "GL_EXT_packed_pixels", + "GL_SGIS_texture_lod", + "GL_SGIS_multisample", + "GL_EXT_rescale_normal", + "GLX_EXT_visual_info", + "GL_EXT_vertex_array", + "GL_EXT_misc_attribute", + "GL_SGIS_generate_mipmap", + "GL_SGIX_clipmap", + "GL_SGIX_shadow", + "GL_SGIS_texture_edge_clamp", + "GL_SGIS_texture_border_clamp", + "GL_EXT_blend_minmax", + "GL_EXT_blend_subtract", + "GL_EXT_blend_logic_op", + "GLX_SGI_swap_control", + "GLX_SGI_video_sync", + "GLX_SGI_make_current_read", + "GLX_SGIX_video_source", + "GLX_EXT_visual_rating", + "GL_SGIX_interlace", + "GLX_EXT_import_context", + "GLX_SGIX_fbconfig", + "GLX_SGIX_pbuffer", + "GL_SGIS_texture_select", + "GL_SGIX_sprite", + "GL_SGIX_texture_multi_buffer", + "GL_EXT_point_parameters", + "GL_SGIX_instruments", + "GL_SGIX_texture_scale_bias", + "GL_SGIX_framezoom", + "GL_SGIX_tag_sample_buffer", + "GL_SGIX_reference_plane", + "GL_SGIX_flush_raster", + "GLX_SGI_cushion", + "GL_SGIX_depth_texture", + "GL_SGIS_fog_function", + "GL_SGIX_fog_offset", + "GL_HP_image_transform", + "GL_HP_convolution_border_modes", + "GL_SGIX_texture_add_env", + "GL_EXT_color_subtable", + "GLU_EXT_object_space_tess", + "GL_PGI_vertex_hints", + "GL_PGI_misc_hints", + "GL_EXT_paletted_texture", + "GL_EXT_clip_volume_hint", + "GL_SGIX_list_priority", + "GL_SGIX_ir_instrument1", + "GLX_SGIX_video_resize", + "GL_SGIX_texture_lod_bias", + "GLU_SGI_filter4_parameters", + "GLX_SGIX_dm_buffer", + "GL_SGIX_shadow_ambient", + "GLX_SGIX_swap_group", + "GLX_SGIX_swap_barrier", + "GL_EXT_index_texture", + "GL_EXT_index_material", + "GL_EXT_index_func", + "GL_EXT_index_array_formats", + "GL_EXT_compiled_vertex_array", + "GL_EXT_cull_vertex", + "GLU_EXT_nurbs_tessellator", + "GL_SGIX_ycrcb", + "GL_EXT_fragment_lighting", + "GL_IBM_rasterpos_clip", + "GL_HP_texture_lighting", + "GL_EXT_draw_range_elements", + "GL_WIN_phong_shading", + "GL_WIN_specular_fog", + "GLX_SGIS_color_range", + "GL_EXT_light_texture", + "GL_SGIX_blend_alpha_minmax", + "GL_EXT_scene_marker", + "GL_SGIX_pixel_texture_bits", + "GL_EXT_bgra", + "GL_SGIX_async", + "GL_SGIX_async_pixel", + "GL_SGIX_async_histogram", + "GL_INTEL_texture_scissor", + "GL_INTEL_parallel_arrays", + "GL_HP_occlusion_test", + "GL_EXT_pixel_transform", + "GL_EXT_pixel_transform_color_table", + "GL_EXT_shared_texture_palette", + "GLX_SGIS_blended_overlay", + "GL_EXT_separate_specular_color", + "GL_EXT_secondary_color", + "GL_EXT_texture_env", + "GL_EXT_texture_perturb_normal", + "GL_EXT_multi_draw_arrays", + "GL_EXT_fog_coord", + "GL_REND_screen_coordinates", + "GL_EXT_coordinate_frame", + "GL_EXT_texture_env_combine", + "GL_APPLE_specular_vector", + "GL_APPLE_transform_hint", + "GL_SUNX_constant_data", + "GL_SUN_global_alpha", + "GL_SUN_triangle_list", + "GL_SUN_vertex", + "WGL_EXT_display_color_table", + "WGL_EXT_extensions_string", + "WGL_EXT_make_current_read", + "WGL_EXT_pixel_format", + "WGL_EXT_pbuffer", + "WGL_EXT_swap_control", + "GL_EXT_blend_func_separate", + "GL_INGR_color_clamp", + "GL_INGR_interlace_read", + "GL_EXT_stencil_wrap", + "WGL_EXT_depth_float", + "GL_EXT_422_pixels", + "GL_NV_texgen_reflection", + "GL_SGIX_texture_range", + "GL_SUN_convolution_border_modes", + "GLX_SUN_get_transparent_index", + "GL_EXT_texture_env_add", + "GL_EXT_texture_lod_bias", + "GL_EXT_texture_filter_anisotropic", + "GL_EXT_vertex_weighting", + "GL_NV_light_max_exponent", + "GL_NV_vertex_array_range", + "GL_NV_register_combiners", + "GL_NV_fog_distance", + "GL_NV_texgen_emboss", + "GL_NV_blend_square", + "GL_NV_texture_env_combine4", + "GL_MESA_resize_buffers", + "GL_MESA_window_pos", + "GL_EXT_texture_compression_s3tc", + "GL_IBM_cull_vertex", + "GL_IBM_multimode_draw_arrays", + "GL_IBM_vertex_array_lists", + "GL_3DFX_texture_compression_FXT1", + "GL_3DFX_multisample", + "GL_3DFX_tbuffer", + "WGL_EXT_multisample", + "GL_SGIX_vertex_preclip", + "GL_SGIX_resample", + "GL_SGIS_texture_color_mask", + "GLX_MESA_copy_sub_buffer", + "GLX_MESA_pixmap_colormap", + "GLX_MESA_release_buffers", + "GLX_MESA_set_3dfx_mode", + "GL_EXT_texture_env_dot3", + "GL_ATI_texture_mirror_once", + "GL_NV_fence", + "GL_IBM_static_data", + "GL_IBM_texture_mirrored_repeat", + "GL_NV_evaluators", + "GL_NV_packed_depth_stencil", + "GL_NV_register_combiners2", + "GL_NV_texture_compression_vtc", + "GL_NV_texture_rectangle", + "GL_NV_texture_shader", + "GL_NV_texture_shader2", + "GL_NV_vertex_array_range2", + "GL_NV_vertex_program", + "GLX_SGIX_visual_select_group", + "GL_SGIX_texture_coordinate_clamp", + "GLX_OML_swap_method", + "GLX_OML_sync_control", + "GL_OML_interlace", + "GL_OML_subsample", + "GL_OML_resample", + "WGL_OML_sync_control", + "GL_NV_copy_depth_to_color", + "GL_ATI_envmap_bumpmap", + "GL_ATI_fragment_shader", + "GL_ATI_pn_triangles", + "GL_ATI_vertex_array_object", + "GL_EXT_vertex_shader", + "GL_ATI_vertex_streams", + "WGL_I3D_digital_video_control", + "WGL_I3D_gamma", + "WGL_I3D_genlock", + "WGL_I3D_image_buffer", + "WGL_I3D_swap_frame_lock", + "WGL_I3D_swap_frame_usage", + "GL_ATI_element_array", + "GL_SUN_mesh_array", + "GL_SUN_slice_accum", + "GL_NV_multisample_filter_hint", + "GL_NV_depth_clamp", + "GL_NV_occlusion_query", + "GL_NV_point_sprite", + "WGL_NV_render_depth_texture", + "WGL_NV_render_texture_rectangle", + "GL_NV_texture_shader3", + "GL_NV_vertex_program1_1", + "GL_EXT_shadow_funcs", + "GL_EXT_stencil_two_side", + "GL_ATI_text_fragment_shader", + "GL_APPLE_client_storage", + "GL_APPLE_element_array", + "GL_APPLE_fence", + "GL_APPLE_vertex_array_object", + "GL_APPLE_vertex_array_range", + "GL_APPLE_ycbcr_422", + "GL_S3_s3tc", + "GL_ATI_draw_buffers", + "WGL_ATI_pixel_format_float", + "GL_ATI_texture_env_combine3", + "GL_ATI_texture_float", + "GL_NV_float_buffer", + "GL_NV_fragment_program", + "GL_NV_half_float", + "GL_NV_pixel_data_range", + "GL_NV_primitive_restart", + "GL_NV_texture_expand_normal", + "GL_NV_vertex_program2", + "GL_ATI_map_object_buffer", + "GL_ATI_separate_stencil", + "GL_ATI_vertex_attrib_array_object", + "GL_OES_byte_coordinates", + "GL_OES_fixed_point", + "GL_OES_single_precision", + "GL_OES_compressed_paletted_texture", + "GL_OES_read_format", + "GL_OES_query_matrix", + "GL_EXT_depth_bounds_test", + "GL_EXT_texture_mirror_clamp", + "GL_EXT_blend_equation_separate", + "GL_MESA_pack_invert", + "GL_MESA_ycbcr_texture", + "GL_EXT_pixel_buffer_object", + "GL_NV_fragment_program_option", + "GL_NV_fragment_program2", + "GL_NV_vertex_program2_option", + "GL_NV_vertex_program3", + "GLX_SGIX_hyperpipe", + "GLX_MESA_agp_offset", + "GL_EXT_texture_compression_dxt1", + "GL_EXT_framebuffer_object", + "GL_GREMEDY_string_marker", +}; + +Map OutputOrder; + + +//----------------------------------------------------------------------------- + +String trim(const char* str) +{ + String ts = ""; + if (str) { + int s = 0, e = strLength(str); + for (; s != e; s++) { + C8 c = str[s]; + if (c != ' ' && c != '\t' && c != '\n') + break; + } + while (s != e) { + C8 c = str[--e]; + if (c != ' ' && c != '\t' && c != '\n') + break; + } + if (s != e) + ts.insert(0,str + s,e-s+1); + } + return ts; +} + + +//----------------------------------------------------------------------------- + +struct Group +{ + String name; + String link; + Array defines; + Array functions; + Array types; + int order; +}; +typedef Array GroupList; + +static inline int weight(const String& str) +{ + if (str.find("GL_VERSION") != String::NPos) + return 0; + + String prefix = str.substr(0,3); + if (prefix == "GL_") + return 1; + if (prefix == "GLU") + return 2; + if (prefix == "GLX") + return 3; + if (prefix == "WGL") + return 4; + return 5; +} + +bool operator<(const Group& a,const Group& b) +{ + int wa = weight(a.name); + int wb = weight(b.name); + return (wa == wb)? a.name < b.name: wa < wb; + // return a.order < b.order; +} + +bool loadFile(Group& group,String name) +{ + FILE* file = fopen(name.c_str(),"r"); + if (!file) { + Print("Could not open file " + name); + return false; + } + + char buf[512]; + group.name = trim(fgets(buf,sizeof(buf),file)); + + Map::Iterator entry = OutputOrder.find(group.name); + if (entry == OutputOrder.end()) { + Print ("[" + group.name + "]"); + } + group.order = (entry != OutputOrder.end())? entry->value: 2000; + + while (!feof(file)) { + String str = trim(fgets(buf,sizeof(buf),file)); + if (!str.length()) + continue; + + if (str.find("http:") != String::NPos) { + group.link = trim(str); + continue; + } + if (str.find("typedef") != String::NPos) { + group.types.pushBack(str); + continue; + } + if (str.find("DECLARE") != String::NPos) { + group.types.pushBack(str); + continue; + } + if (str.find("(") != String::NPos) { + group.functions.pushBack(str); + continue; + } + group.defines.pushBack(str); + } + fclose( file ); + return true; +} + +void loadDir(GroupList& groups,String name,const char* filter) +{ + DIR *dir = opendir(name); + if (!dir) { + Print("Could not open file " + name); + return; + } + + struct dirent *fEntry; + while ((fEntry = readdir(dir)) != 0) { + if (fEntry->d_name[0] == '.') + continue; + String file = name + "/" + String(fEntry->d_name); + if (filter[0] != '*' && file.find(filter) == String::NPos) + continue; + //Print("Loading " + file); + groups.pushBack(Group()); + if (!loadFile(groups.last(),file)) + groups.popBack(); + } + + quickSort(groups.begin(),groups.end()); +} + + +//----------------------------------------------------------------------------- + +void write(FILE* file,String line) +{ + fwrite(line,1,line.length(),file); +} + +void outputHeader(GroupList& groups, String name) +{ + FILE* file = fopen(name.c_str(),"w"); + if (!file) + Print("Could not open file " + name); + + // Output all the group name together at the top + for (GroupList::Iterator grp = groups.begin(); grp != groups.end(); grp++) + write(file,"#define " + grp->name + "\n"); + +#if 0 // Types now with the group + // Output all the types for all the extensions + for (GroupList::Iterator grp = groups.begin(); grp != groups.end(); grp++) + for (Array::Iterator itr = grp->types.begin(); + itr != grp->types.end(); itr++) + write(file,*itr + ";\n"); +#endif + + // Output the defines for each group + for (GroupList::Iterator grp = groups.begin(); grp != groups.end(); grp++) { + if (!grp->name) + continue; + write(file,"\n#ifdef " + grp->name + "\n"); + for (Array::Iterator itr = grp->types.begin(); + itr != grp->types.end(); itr++) + write(file,*itr + ";\n"); + for (Array::Iterator itr = grp->defines.begin(); + itr != grp->defines.end(); itr++) { + write(file,"#define " + *itr + "\n"); + } + for (Array::Iterator itr = grp->functions.begin(); + itr != grp->functions.end(); itr++) { + String& str = *itr; + + // Parse function "return name (args)". Start at the back because + // args is enclosed in (), the name has no spaces, and the return type + // can be several tokens, such as "void *" or "const char *" + int b = str.length(); + int a = b - 1; + while (str[a] != '(') + a--; + while (str[--a] == ' ') + ; + b = a; + while (str[a] != ' ') + a--; + String name = str.substr(a+1,b - a); + + // + write(file,"#define "+name+" XGL_FUNCPTR("+name+")\n"); + } + write(file,"#endif\n"); + } + fclose(file); +} + +void outputFunctions(GroupList& groups, String name) +{ + FILE* file = fopen(name.c_str(),"w"); + if (!file) + Print("Could not open file " + name); + + // Output the functions for each group + for (GroupList::Iterator grp = groups.begin(); grp != groups.end(); grp++) { + if (!grp->name) + continue; + if (grp->name == "GL_ARB_imaging") + // Imaging is include as part of 1.4... + write(file,"\n#if defined(GL_ARB_imaging) && !defined(GL_VERSION_1_4)\n"); + else + write(file,"\n#ifdef " + grp->name + "\n"); + write(file,"GL_GROUP_BEGIN(" + grp->name + ")\n"); + for (Array::Iterator itr = grp->functions.begin(); + itr != grp->functions.end(); itr++) { + String& str = *itr; + + // Parse function "return name (args)". Start at the back because + // args is enclosed in (), the name has no spaces, and the return type + // can be several tokens, such as "void *" or "const char *" + int b = str.length(); + int a = b - 1; + while (str[a] != '(') + a--; + String args = str.substr(a,b - a); + + while (str[--a] == ' ') + ; + b = a; + while (str[a] != ' ') + a--; + String name = str.substr(a+1,b - a); + + while (str[a] == ' ') + a--; + String rtype = str.substr(0,a+1); + // + write(file,"GL_FUNCTION("+name+","+rtype+","+args+")\n"); + } + write(file,"GL_GROUP_END()\n#endif\n"); + } + fclose(file); +} + + +//----------------------------------------------------------------------------- + +int main(int argc, char* argv[]) +{ + Kernel::installDefaultPrint(); + + // Build a name -> order map for faster lookups. + for (int i = 0; i < sizeof(OrderTable) / sizeof(const char*); i++) + OutputOrder.insert(String(OrderTable[i]),i+1); + + GroupList extensions; + extensions.reserve(800); + loadDir(extensions,"core","VERSION"); + loadDir(extensions,"extensions","*"); + outputHeader(extensions,"glext.h"); + outputFunctions(extensions,"glfnext.h"); + + return 0; +} + diff --git a/Engine/source/gfx/gl/ggl/ggl.cpp b/Engine/source/gfx/gl/ggl/ggl.cpp new file mode 100644 index 000000000..0f8f1a84d --- /dev/null +++ b/Engine/source/gfx/gl/ggl/ggl.cpp @@ -0,0 +1,89 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/strings/stringFunctions.h" + +#include "platform/platformDlibrary.h" +#include "console/console.h" + +namespace GL +{ + +//----------------------------------------------------------------------------- +// Current active extensions +struct GLExtensionPtrs; +GLExtensionPtrs* _GGLptr; + +struct GLExtensionFlags; +GLExtensionFlags* _GGLflag; + + +//----------------------------------------------------------------------------- + +static inline int versionCombine( int major, int minor ) +{ + return major * 100 + minor; +} + +bool bindFunction( DLibrary *dll, void *&fnAddress, const char *name ) +{ + fnAddress = dll->bind( name ); + + if (!fnAddress) + Con::warnf( "GLExtensions: DLL bind failed for %s", name ); + + return fnAddress != 0; +} + +bool hasExtension( const char *name, const char *extensions ) +{ + // Extensions are compared against the extension strings + if (extensions && *extensions) { + const char* ptr = dStrstr(extensions,name); + if (ptr) { + char end = ptr[dStrlen(name)]; + if (end == ' ' || end == 0) + return true; + } + } + return false; +} + +bool hasVersion( const char *name, const char* prefix, int major, int minor ) +{ + // Extension group names are compared against the version number. The prefix + // string should be "GL_VERSION" or "GLX_VERSION".. group names are + // "GL_VERSION_1_1", "GL_VERSION_1_2", "GLX_VERSION_1_2", etc. + while (*name && *prefix && *name == *prefix) + name++, prefix++; + if (*name == '_' && *prefix == '\0') { + int maj = dAtoi(++name); + while (dIsdigit(*name)) + *name++; + int min = dAtoi(++name); + return versionCombine(maj,min) <= versionCombine(major,minor); + } + return false; +} + +} // Namespace + diff --git a/Engine/source/gfx/gl/ggl/ggl.h b/Engine/source/gfx/gl/ggl/ggl.h new file mode 100644 index 000000000..7d75ea865 --- /dev/null +++ b/Engine/source/gfx/gl/ggl/ggl.h @@ -0,0 +1,117 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef GL_GGL_H +#define GL_GGL_H + +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif + +//----------------------------------------------------------------------------- +// Configuration file which defines which parts of GL to include + +#ifndef DOXYGEN +#include "gfx/gl/ggl/gglConfig.h" + + +//----------------------------------------------------------------------------- + +#if defined(__CYGWIN__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32) + #define XGL_DLL __stdcall +#else + #define XGL_DLL +#endif + + +//----------------------------------------------------------------------------- +// Include core (OpenGL 1.1) definitions + +#include "gfx/gl/ggl/generated/glc.h" +#include "gfx/gl/ggl/generated/gle.h" + + +//----------------------------------------------------------------------------- +// All core functionality is implemented as function pointers. + +#define GL_GROUP_BEGIN(name) +#define GL_FUNCTION(name, type, args) extern type (XGL_DLL *name) args; +#define GL_GROUP_END() +#include "gfx/gl/ggl/generated/glcfn.h" +#undef GL_GROUP_BEGIN +#undef GL_FUNCTION +#undef GL_GROUP_END + +/// OpenGL interface. +namespace GL +{ + +//----------------------------------------------------------------------------- +// Extensions use indirection in order to support multiple contexts + +struct GLExtensionPtrs { + bool bound; + + // Include all OpenGL extensions for all platform + #define GL_GROUP_BEGIN(name) + #define GL_FUNCTION(name, type, args) type (XGL_DLL *_##name) args; + #define GL_GROUP_END() + #include "gfx/gl/ggl/generated/glefn.h" + #undef GL_GROUP_BEGIN + #undef GL_FUNCTION + #undef GL_GROUP_END + + GLExtensionPtrs() { bound = false; } +}; + +struct GLExtensionFlags { + bool bound; + + // Define extension "has" variables + #define GL_GROUP_BEGIN(name) bool has_##name; + #define GL_FUNCTION(name, type, args) + #define GL_GROUP_END() + #include "gfx/gl/ggl/generated/glefn.h" + #undef GL_GROUP_BEGIN + #undef GL_FUNCTION + #undef GL_GROUP_END + + GLExtensionFlags() { bound = false; } +}; + +// Extension loading has been reimplemented on each platform, and each platform +// has a different version of GLExtensionPtrs and GLExtensionFlags. When binding +// extensions for that platform, you MUST use these pointers. From there the XGL_FUNCPTR +// define will handle converting a gl*EXT call into the proper member function call. +extern GLExtensionPtrs* _GGLptr; +#define XGL_FUNCPTR(name) (GL::_GGLptr->_##name) +#endif // Doxygen + +extern GLExtensionFlags* _GGLflag; +#define gglHasExtension(name) (GL::_GGLflag->has_##name) + + + +} // Namespace + +#endif + diff --git a/Engine/source/gfx/gl/ggl/gglConfig.h b/Engine/source/gfx/gl/ggl/gglConfig.h new file mode 100644 index 000000000..9665b42bc --- /dev/null +++ b/Engine/source/gfx/gl/ggl/gglConfig.h @@ -0,0 +1,378 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Undefine extensions not required + +#define GL_VERSION_1_2 +#define GL_VERSION_1_3 +#define GL_VERSION_1_4 +#define GL_VERSION_1_5 +#define GL_VERSION_2_0 + +#define GL_3DFX_multisample +#define GL_3DFX_tbuffer +#define GL_3DFX_texture_compression_FXT1 + +#define GL_APPLE_client_storage +#define GL_APPLE_element_array +#define GL_APPLE_fence +#define GL_APPLE_specular_vector +#define GL_APPLE_transform_hint +#define GL_APPLE_vertex_array_object +#define GL_APPLE_vertex_array_range +#define GL_APPLE_ycbcr_422 + +#define GL_ARB_depth_texture +#define GL_ARB_fragment_program +#define GL_ARB_fragment_shader +#define GL_ARB_imaging +#define GL_ARB_matrix_palette +#define GL_ARB_multisample +#define GL_ARB_multitexture +#define GL_ARB_occlusion_query +#define GL_ARB_pixel_buffer_object +#define GL_ARB_point_parameters +#define GL_ARB_point_sprite +#define GL_ARB_shader_objects +#define GL_ARB_shading_language_100 +#define GL_ARB_shadow +#define GL_ARB_shadow_ambient +#define GL_ARB_texture_border_clamp +#define GL_ARB_texture_compression +#define GL_ARB_texture_cube_map +#define GL_ARB_texture_env_add +#define GL_ARB_texture_env_combine +#define GL_ARB_texture_env_crossbar +#define GL_ARB_texture_env_dot3 +#define GL_ARB_texture_mirrored_repeat +#define GL_ARB_texture_non_power_of_two +#define GL_ARB_texture_rectangle +#define GL_ARB_transpose_matrix +#define GL_ARB_vertex_blend +#define GL_ARB_vertex_buffer_object +#define GL_ARB_vertex_program +#define GL_ARB_vertex_shader +#define GL_ARB_window_pos + +#define GL_ATIX_point_sprites +#define GL_ATIX_texture_env_combine3 +#define GL_ATIX_texture_env_route +#define GL_ATIX_vertex_shader_output_point_size + +#define GL_ATI_draw_buffers +#define GL_ATI_element_array +#define GL_ATI_envmap_bumpmap +#define GL_ATI_fragment_shader +#define GL_ATI_map_object_buffer +#define GL_ATI_pn_triangles +#define GL_ATI_separate_stencil +#define GL_ATI_text_fragment_shader +#define GL_ATI_texture_env_combine3 +#define GL_ATI_texture_float +#define GL_ATI_texture_mirror_once +#define GL_ATI_vertex_array_object +#define GL_ATI_vertex_attrib_array_object +#define GL_ATI_vertex_streams + +#define GL_EXT_422_pixels +#define GL_EXT_Cg_shader +#define GL_EXT_abgr +#define GL_EXT_bgra +#define GL_EXT_blend_color +#define GL_EXT_blend_equation_separate +#define GL_EXT_blend_func_separate +#define GL_EXT_blend_logic_op +#define GL_EXT_blend_minmax +#define GL_EXT_blend_subtract +#define GL_EXT_clip_volume_hint +#define GL_EXT_cmyka +#define GL_EXT_color_subtable +#define GL_EXT_compiled_vertex_array +#define GL_EXT_convolution +#define GL_EXT_coordinate_frame +#define GL_EXT_copy_texture +#define GL_EXT_cull_vertex +#define GL_EXT_depth_bounds_test +#define GL_EXT_draw_range_elements +#define GL_EXT_fog_coord +#define GL_EXT_fragment_lighting +#define GL_EXT_framebuffer_blit +#define GL_EXT_framebuffer_object +#define GL_EXT_histogram +#define GL_EXT_index_array_formats +#define GL_EXT_index_func +#define GL_EXT_index_material +#define GL_EXT_index_texture +#define GL_EXT_light_texture +#define GL_EXT_misc_attribute +#define GL_EXT_multi_draw_arrays +#define GL_EXT_multisample +#define GL_EXT_packed_pixels +#define GL_EXT_paletted_texture +#define GL_EXT_pixel_buffer_object +#define GL_EXT_pixel_transform +#define GL_EXT_pixel_transform_color_table +#define GL_EXT_point_parameters +#define GL_EXT_polygon_offset +#define GL_EXT_rescale_normal +#define GL_EXT_scene_marker +#define GL_EXT_secondary_color +#define GL_EXT_separate_specular_color +#define GL_EXT_shadow_funcs +#define GL_EXT_shared_texture_palette +#define GL_EXT_stencil_two_side +#define GL_EXT_stencil_wrap +#define GL_EXT_subtexture +#define GL_EXT_texture +#define GL_EXT_texture3D +#define GL_EXT_texture_compression_dxt1 +#define GL_EXT_texture_compression_s3tc +#define GL_EXT_texture_cube_map +#define GL_EXT_texture_edge_clamp +#define GL_EXT_texture_env +#define GL_EXT_texture_env_add +#define GL_EXT_texture_env_combine +#define GL_EXT_texture_env_dot3 +#define GL_EXT_texture_filter_anisotropic +#define GL_EXT_texture_lod_bias +#define GL_EXT_texture_mirror_clamp +#define GL_EXT_texture_object +#define GL_EXT_texture_perturb_normal +#define GL_EXT_texture_rectangle +#define GL_EXT_vertex_array +#define GL_EXT_vertex_shader +#define GL_EXT_vertex_weighting +#define GL_GREMEDY_string_marker + +#define GL_HP_convolution_border_modes +#define GL_HP_image_transform +#define GL_HP_occlusion_test +#define GL_HP_texture_lighting + +#define GL_IBM_cull_vertex +#define GL_IBM_multimode_draw_arrays +#define GL_IBM_rasterpos_clip +#define GL_IBM_static_data +#define GL_IBM_texture_mirrored_repeat +#define GL_IBM_vertex_array_lists + +#define GL_INGR_color_clamp +#define GL_INGR_interlace_read + +#define GL_INTEL_parallel_arrays +#define GL_INTEL_texture_scissor + +#define GL_KTX_buffer_region + +#define GL_MESA_pack_invert +#define GL_MESA_resize_buffers +#define GL_MESA_window_pos +#define GL_MESA_ycbcr_texture + +#define GL_NV_blend_square +#define GL_NV_copy_depth_to_color +#define GL_NV_depth_clamp +#define GL_NV_evaluators +#define GL_NV_fence +#define GL_NV_float_buffer +#define GL_NV_fog_distance +#define GL_NV_fragment_program +#define GL_NV_fragment_program2 +#define GL_NV_fragment_program_option +#define GL_NV_half_float +#define GL_NV_light_max_exponent +#define GL_NV_multisample_filter_hint +#define GL_NV_occlusion_query +#define GL_NV_packed_depth_stencil +#define GL_NV_pixel_data_range +#define GL_NV_point_sprite +#define GL_NV_primitive_restart +#define GL_NV_register_combiners +#define GL_NV_register_combiners2 +#define GL_NV_texgen_emboss +#define GL_NV_texgen_reflection +#define GL_NV_texture_compression_vtc +#define GL_NV_texture_env_combine4 +#define GL_NV_texture_expand_normal +#define GL_NV_texture_rectangle +#define GL_NV_texture_shader +#define GL_NV_texture_shader2 +#define GL_NV_texture_shader3 +#define GL_NV_vertex_array_range +#define GL_NV_vertex_array_range2 +#define GL_NV_vertex_program +#define GL_NV_vertex_program1_1 +#define GL_NV_vertex_program2 +#define GL_NV_vertex_program2_option +#define GL_NV_vertex_program3 + +#define GL_OML_interlace +#define GL_OML_resample +#define GL_OML_subsample + +#define GL_PGI_misc_hints +#define GL_PGI_vertex_hints + +#define GL_REND_screen_coordinates + +#define GL_S3_s3tc + +#define GL_SGIS_color_range +#define GL_SGIS_detail_texture +#define GL_SGIS_fog_function +#define GL_SGIS_generate_mipmap +#define GL_SGIS_multisample +#define GL_SGIS_pixel_texture +#define GL_SGIS_sharpen_texture +#define GL_SGIS_texture4D +#define GL_SGIS_texture_border_clamp +#define GL_SGIS_texture_edge_clamp +#define GL_SGIS_texture_filter4 +#define GL_SGIS_texture_lod +#define GL_SGIS_texture_select + +#define GL_SGIX_async +#define GL_SGIX_async_histogram +#define GL_SGIX_async_pixel +#define GL_SGIX_blend_alpha_minmax +#define GL_SGIX_clipmap +#define GL_SGIX_depth_texture +#define GL_SGIX_flush_raster +#define GL_SGIX_fog_offset +#define GL_SGIX_fog_texture +#define GL_SGIX_fragment_specular_lighting +#define GL_SGIX_framezoom +#define GL_SGIX_interlace +#define GL_SGIX_ir_instrument1 +#define GL_SGIX_list_priority +#define GL_SGIX_pixel_texture +#define GL_SGIX_pixel_texture_bits +#define GL_SGIX_reference_plane +#define GL_SGIX_resample +#define GL_SGIX_shadow +#define GL_SGIX_shadow_ambient +#define GL_SGIX_sprite +#define GL_SGIX_tag_sample_buffer +#define GL_SGIX_texture_add_env +#define GL_SGIX_texture_coordinate_clamp +#define GL_SGIX_texture_lod_bias +#define GL_SGIX_texture_multi_buffer +#define GL_SGIX_texture_range +#define GL_SGIX_texture_scale_bias +#define GL_SGIX_vertex_preclip +#define GL_SGIX_vertex_preclip_hint +#define GL_SGIX_ycrcb + +#define GL_SGI_color_matrix +#define GL_SGI_color_table +#define GL_SGI_texture_color_table + +#define GL_SUNX_constant_data + +#define GL_SUN_convolution_border_modes +#define GL_SUN_global_alpha +#define GL_SUN_mesh_array +#define GL_SUN_slice_accum +#define GL_SUN_triangle_list +#define GL_SUN_vertex + +#define GL_WIN_phong_shading +#define GL_WIN_specular_fog +#define GL_WIN_swap_hint + +#define GLU_EXT_nurbs_tessellator +#define GLU_EXT_object_space_tess +#define GLU_SGI_filter4_parameters + +#define GLX_VERSION_1_1 +#define GLX_VERSION_1_2 +#define GLX_VERSION_1_3 +#define GLX_VERSION_1_4 +#define GLX_3DFX_multisample +#define GLX_ARB_fbconfig_float +#define GLX_ARB_get_proc_address +#define GLX_ARB_multisample +#define GLX_ATI_pixel_format_float +#define GLX_ATI_render_texture +#define GLX_EXT_import_context +#define GLX_EXT_scene_marker +#define GLX_EXT_visual_info +#define GLX_EXT_visual_rating +#define GLX_MESA_agp_offset +#define GLX_MESA_copy_sub_buffer +#define GLX_MESA_pixmap_colormap +#define GLX_MESA_release_buffers +#define GLX_MESA_set_3dfx_mode +#define GLX_NV_float_buffer +#define GLX_NV_vertex_array_range +#define GLX_OML_swap_method +#define GLX_OML_sync_control +#define GLX_SGIS_blended_overlay +#define GLX_SGIS_color_range +#define GLX_SGIS_multisample +#define GLX_SGIS_shared_multisample +#define GLX_SGIX_fbconfig +#define GLX_SGIX_pbuffer +#define GLX_SGIX_swap_barrier +#define GLX_SGIX_swap_group +#define GLX_SGIX_video_resize +#define GLX_SGIX_visual_select_group +#define GLX_SGI_cushion +#define GLX_SGI_make_current_read +#define GLX_SGI_swap_control +#define GLX_SGI_video_sync +#define GLX_SUN_get_transparent_index +#define GLX_SUN_video_resize + +#define WGL_3DFX_multisample +#define WGL_ARB_buffer_region +#define WGL_ARB_extensions_string +#define WGL_ARB_make_current_read +#define WGL_ARB_multisample +#define WGL_ARB_pbuffer +#define WGL_ARB_pixel_format +#define WGL_ARB_pixel_format_float +#define WGL_ARB_render_texture +#define WGL_ATI_pixel_format_float +#define WGL_ATI_render_texture_rectangle +#define WGL_EXT_depth_float +#define WGL_EXT_display_color_table +#define WGL_EXT_extensions_string +#define WGL_EXT_make_current_read +#define WGL_EXT_multisample +#define WGL_EXT_pbuffer +#define WGL_EXT_pixel_format +#define WGL_EXT_swap_control +#define WGL_I3D_digital_video_control +#define WGL_I3D_gamma +#define WGL_I3D_genlock +#define WGL_I3D_image_buffer +#define WGL_I3D_swap_frame_lock +#define WGL_I3D_swap_frame_usage +#define WGL_NV_float_buffer +#define WGL_NV_render_depth_texture +#define WGL_NV_render_texture_rectangle +#define WGL_NV_vertex_array_range +#define WGL_OML_sync_control + + diff --git a/Engine/source/gfx/gl/ggl/mac/agl.h b/Engine/source/gfx/gl/ggl/mac/agl.h new file mode 100644 index 000000000..144856da9 --- /dev/null +++ b/Engine/source/gfx/gl/ggl/mac/agl.h @@ -0,0 +1,110 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +/* +** The contents of this file are subject to the GLX Public License Version 1.0 +** (the "License"). You may not use this file except in compliance with the +** License. You may obtain a copy of the License at Silicon Graphics, Inc., +** attn: Legal Services, 2011 N. Shoreline Blvd., Mountain View, CA 94043 +** or at http://www.sgi.com/software/opensource/glx/license.html. +** +** Software distributed under the License is distributed on an "AS IS" +** basis. ALL WARRANTIES ARE DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY +** IMPLIED WARRANTIES OF MERCHANTABILITY, OF FITNESS FOR A PARTICULAR +** PURPOSE OR OF NON- INFRINGEMENT. See the License for the specific +** language governing rights and limitations under the License. +** +** The Original Software is GLX version 1.2 source code, released February, +** 1999. The developer of the Original Software is Silicon Graphics, Inc. +** Those portions of the Subject Software created by Silicon Graphics, Inc. +** are Copyright (c) 1991-9 Silicon Graphics, Inc. All Rights Reserved. +*/ + +#ifndef GFX_GLX_H +#define GFX_GLX_H + +#ifndef GFX_GGL_H + #include "../ggl.h" +#endif + +#include "platform/tmm_off.h" + +// AGL includes the standard gl defs, which we already provide in ggl.h +// Defining __gl_h_ here prevents osx's gl defs from being included. +#define __gl_h_ +#include + +#include "platform/tmm_on.h" + +namespace GL +{ + +//----------------------------------------------------------------------------- +#ifndef DOXYGEN + +#define GLX_VERSION_1_0 1 + +#define GLX_USE_GL 1 +#define GLX_BUFFER_SIZE 2 +#define GLX_LEVEL 3 +#define GLX_RGBA 4 +#define GLX_DOUBLEBUFFER 5 +#define GLX_STEREO 6 +#define GLX_AUX_BUFFERS 7 +#define GLX_RED_SIZE 8 +#define GLX_GREEN_SIZE 9 +#define GLX_BLUE_SIZE 10 +#define GLX_ALPHA_SIZE 11 +#define GLX_DEPTH_SIZE 12 +#define GLX_STENCIL_SIZE 13 +#define GLX_ACCUM_RED_SIZE 14 +#define GLX_ACCUM_GREEN_SIZE 15 +#define GLX_ACCUM_BLUE_SIZE 16 +#define GLX_ACCUM_ALPHA_SIZE 17 +#define GLX_BAD_SCREEN 1 +#define GLX_BAD_ATTRIBUTE 2 +#define GLX_NO_EXTENSION 3 +#define GLX_BAD_VISUAL 4 +#define GLX_BAD_CONTEXT 5 +#define GLX_BAD_VALUE 6 +#define GLX_BAD_ENUM 7 + + +//----------------------------------------------------------------------------- +// Extensions use indirection in order to support multiple contexts + +struct AGLExtensionPtrs: public GLExtensionPtrs { +}; + +struct AGLExtensionFlags: public GLExtensionFlags { +}; + +#endif // Doxygen + +//----------------------------------------------------------------------------- + +#undef XGL_FUNCPTR +#define XGL_FUNCPTR(name) (((AGLExtensionPtrs*)GL::_GGLptr)->_##name) + +} // Namespace +#endif + diff --git a/Engine/source/gfx/gl/ggl/mac/aglBind.cpp b/Engine/source/gfx/gl/ggl/mac/aglBind.cpp new file mode 100644 index 000000000..0be023d8a --- /dev/null +++ b/Engine/source/gfx/gl/ggl/mac/aglBind.cpp @@ -0,0 +1,195 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include +#include "agl.h" + +#include "platform/platform.h" + +//----------------------------------------------------------------------------- +// Instantiation of function pointers. + +#define GL_GROUP_BEGIN(name) +#define GL_FUNCTION(name, type, args) type (XGL_DLL *name) args; +#define GL_GROUP_END() +#include "../generated/glcfn.h" +#undef GL_GROUP_BEGIN +#undef GL_FUNCTION +#undef GL_GROUP_END + + +#define EXECUTE_ONLY_ONCE() \ + static bool _doneOnce = false; \ + if(_doneOnce) return; \ + _doneOnce = true; + +namespace GL +{ + +//----------------------------------------------------------------------------- +// The OpenGL Bundle is shared by all the devices. +static CFBundleRef _openglFrameworkRef; + +// Unlike WGL, AGL does not require a set of function pointers per +// context pixel format. All functions in the DLL are bound in a single +// table and shared by all contexts. +static AGLExtensionPtrs _LibraryFunctions; +static AGLExtensionFlags _ExtensionFlags; + +bool hasExtension(const char *name,const char* extensions); +bool hasVersion(const char *name,const char* prefix,int major,int minor); + + +//----------------------------------------------------------------------------- +void* MacGetProcAddress(CFBundleRef bundle,char* name) +{ + CFStringRef cfName = CFStringCreateWithCString(kCFAllocatorDefault, name, CFStringGetSystemEncoding()); + void* ret = CFBundleGetFunctionPointerForName(bundle, cfName); + CFRelease(cfName); + return ret; +} + + +//----------------------------------------------------------------------------- +bool bindFunction(CFBundleRef bundle,void *&fnAddress, const char *name) +{ + CFStringRef cfName = CFStringCreateWithCString(0,name,CFStringGetSystemEncoding()); + fnAddress = CFBundleGetFunctionPointerForName(bundle, cfName); + CFRelease(cfName); + return fnAddress != 0; +} + + +//----------------------------------------------------------------------------- +bool gglBindCoreFunctions(CFBundleRef bundle,AGLExtensionPtrs* glp) +{ + bool bound = true; + + // Bind static functions which are quarenteed to be part of the + // OpenGL library. In this case, OpenGL 1.0 and GLX 1.0 functions + #define GL_GROUP_BEGIN(name) + #define GL_GROUP_END() + #define GL_FUNCTION(fn_name, fn_return, fn_args) \ + bound &= bindFunction(bundle,*(void**)&fn_name, #fn_name); + #include "../generated/glcfn.h" + #undef GL_FUNCTION + #undef GL_GROUP_BEGIN + #undef GL_GROUP_END + + // Try and bind all known extension functions. We'll check later to + // see which ones are actually valid for a context. + #define GL_GROUP_BEGIN(name) + #define GL_FUNCTION(fn_name, fn_return, fn_args) \ + bindFunction(bundle,*(void**)&glp->_##fn_name, #fn_name); + #define GL_GROUP_END() + #include "../generated/glefn.h" + #undef GL_FUNCTION + #undef GL_GROUP_BEGIN + #undef GL_GROUP_END + + return bound; +} + + +//----------------------------------------------------------------------------- +bool gglBindExtensions(GLExtensionFlags* gl) +{ + dMemset(gl,0,sizeof(GLExtensionFlags)); + + // Get GL version and extensions + const char* glExtensions = (const char*)glGetString(GL_EXTENSIONS); + const char* glVersion = (const char*) glGetString(GL_VERSION); + if (!glExtensions || !glVersion) + return false; + + // Parse the GL version string "major.minor" + const char *itr = glVersion; + int glMajor = atoi(itr); + while (isdigit(*itr)) + *itr++; + int glMinor = atoi(++itr); + + // Check which extensions are available on the active context. + // GL and GLX versions ubove 1.0 are also tested here. + #define GL_GROUP_BEGIN(name) \ + gl->has_##name = hasVersion(#name,"GL_VERSION",glMajor,glMinor) || \ + hasExtension(#name,glExtensions); + #define GL_FUNCTION(fn_name, fn_return, fn_args) + #define GL_GROUP_END() + #include "../generated/glefn.h" + #undef GL_FUNCTION + #undef GL_GROUP_BEGIN + #undef GL_GROUP_END + + gl->bound = true; + return true; +} + +#define _hasGLXExtension(display,name) (display->glx.has_##name) + +//----------------------------------------------------------------------------- +void gglPerformBinds() +{ + // Some of the following code is copied from the Apple Opengl Documentation. + + // Load and bind OpenGL bundle functions + if (!_openglFrameworkRef) { + + // Load OpenGL.framework + _openglFrameworkRef = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); + if (!_openglFrameworkRef) { + warn("Could not create OpenGL Framework bundle"); + return; + } + if (!CFBundleLoadExecutable(_openglFrameworkRef)) { + warn("Could not load MachO executable"); + return; + } + + // Bind our functions. + if (!gglBindCoreFunctions(_openglFrameworkRef, &_LibraryFunctions)) { + warn("GLDevice: Failed to bind all core functions"); + return; + } + + // Save the pointer to the set of opengl functions + _GGLptr = &_LibraryFunctions; + } +} + +//----------------------------------------------------------------------------- +void gglPerformExtensionBinds(void *context) +{ + // we don't care about the passed context when binding the opengl functions, + // we only care about the current opengl context. + if( !_openglFrameworkRef || !_GGLptr ) + { + gglPerformBinds(); + } + gglBindExtensions( &_ExtensionFlags ); + _GGLflag = &_ExtensionFlags; +} + + + +} // Namespace + diff --git a/Engine/source/gfx/gl/ggl/win32/wgl.h b/Engine/source/gfx/gl/ggl/win32/wgl.h new file mode 100644 index 000000000..ef2d159ed --- /dev/null +++ b/Engine/source/gfx/gl/ggl/win32/wgl.h @@ -0,0 +1,102 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef GFX_WGL_H +#define GFX_WGL_H + +#ifndef GFX_GGL_H + #include "../ggl.h" +#endif +#ifndef _PLATFORMDLIBRARY_H + #include "platform/platformDlibrary.h" +#endif + +#ifndef _WIN32_WINNT + #define _WIN32_WINNT 0x0500 +#endif +#include + + +namespace GL +{ + +//----------------------------------------------------------------------------- +// Include WGL 1.0 definitions + +#ifndef DOXYGEN +#include "../generated/wgle.h" + + +//----------------------------------------------------------------------------- +// All core functionality is implemented as function pointers. + +#define GL_GROUP_BEGIN(name) +#define GL_FUNCTION(name, type, args) extern type (XGL_DLL *name) args; +#define GL_GROUP_END() +#include "../generated/wglfn.h" +#undef GL_GROUP_BEGIN +#undef GL_FUNCTION +#undef GL_GROUP_END + + +//----------------------------------------------------------------------------- +// Extensions use indirection in order to support multiple contexts + +struct WGLExtensionPtrs: public GLExtensionPtrs { + // Include all OpenGL extensions for all platform + #define GL_GROUP_BEGIN(name) + #define GL_FUNCTION(name, type, args) type (XGL_DLL *_##name) args; + #define GL_GROUP_END() + #include "../generated/wglefn.h" + #undef GL_GROUP_BEGIN + #undef GL_FUNCTION + #undef GL_GROUP_END +}; + +struct WGLExtensionFlags: public GLExtensionFlags { + // Define extension "has" variables + #define GL_GROUP_BEGIN(name) bool has_##name; + #define GL_FUNCTION(name, type, args) + #define GL_GROUP_END() + #include "../generated/wglefn.h" + #undef GL_GROUP_BEGIN + #undef GL_FUNCTION + #undef GL_GROUP_END +}; +#endif // Doyxygen + + +//----------------------------------------------------------------------------- + +bool gglBindExtensions(DLibrary*,WGLExtensionPtrs*,WGLExtensionFlags*,HDC); + +#undef XGL_FUNCPTR +#define XGL_FUNCPTR(name) (((GL::WGLExtensionPtrs*)GL::_GGLptr)->_##name) + +#undef gglHasExtension +#define gglHasExtension(name) (((GL::WGLExtensionFlags*)GL::_GGLflag)->has_##name) + + +} // Namespace + +#endif + diff --git a/Engine/source/gfx/gl/ggl/win32/wglBind.cpp b/Engine/source/gfx/gl/ggl/win32/wglBind.cpp new file mode 100644 index 000000000..99a3750eb --- /dev/null +++ b/Engine/source/gfx/gl/ggl/win32/wglBind.cpp @@ -0,0 +1,173 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "wgl.h" + +#include "core/strings/stringFunctions.h" + +#include "console/console.h" + +//----------------------------------------------------------------------------- +// Instantiation of GL function pointers in global namespace. + +#define GL_GROUP_BEGIN(name) +#define GL_FUNCTION(name, type, args) type (__stdcall *name) args; +#define GL_GROUP_END() +#include "../generated/glcfn.h" +#undef GL_GROUP_BEGIN +#undef GL_FUNCTION +#undef GL_GROUP_END + + +namespace GL +{ + +bool bindFunction(DLibrary* dll,void *&fnAddress, const char *name); +bool hasExtension(const char *name,const char* extensions); +bool hasVersion(const char *name,const char* prefix,int major,int minor); + + +//----------------------------------------------------------------------------- +// Instantiation of WGL function pointers. These are in the GL namespace to +// avoid problems with definitions in windows.h + +#define GL_GROUP_BEGIN(name) +#define GL_FUNCTION(name, type, args) type (__stdcall *name) args; +#define GL_GROUP_END() +#include "../generated/wglfn.h" +#undef GL_GROUP_BEGIN +#undef GL_FUNCTION +#undef GL_GROUP_END + + +//----------------------------------------------------------------------------- + +static bool bindExtension(void *&fnAddress, const char *name) +{ + fnAddress = (void*)wglGetProcAddress(name); + if (!fnAddress) + Con::printf("GLExtensions: Extension bind failed for %s",name); + + return fnAddress != 0; +} + + +//----------------------------------------------------------------------------- + +bool gglBindCoreFunctions(DLibrary* dll) +{ + bool bound = true; + + // Bind static functions which are quarenteed to be part of the + // OpenGL library. In this case, OpenGL 1.0 and WGL functions + #define GL_GROUP_BEGIN(name) + #define GL_GROUP_END() + #define GL_FUNCTION(fn_name, fn_return, fn_args) \ + bound &= bindFunction(dll, *(void**)&fn_name, #fn_name); + #include "../generated/glcfn.h" + #include "../generated/wglfn.h" + #undef GL_FUNCTION + #undef GL_GROUP_BEGIN + #undef GL_GROUP_END + + return bound; +} + +bool gglBindExtensions(DLibrary* dll,WGLExtensionPtrs* glp,WGLExtensionFlags* glf,HDC hDC) +{ + dMemset(glp,0,sizeof(WGLExtensionPtrs)); + dMemset(glf,0,sizeof(WGLExtensionFlags)); + + // Get WGL extensions, there is no version + const char* wgl = 0; + if (bindExtension(*(void**)&glp->_wglGetExtensionsStringARB,"wglGetExtensionsStringARB")) + wgl = glp->_wglGetExtensionsStringARB(hDC); + else + if (bindExtension(*(void**)&glp->_wglGetExtensionsStringEXT,"wglGetExtensionsStringEXT")) + wgl = glp->_wglGetExtensionsStringEXT(); + + // Get OpenGL version and extensions + const char* glExtensions = (const char*)glGetString(GL_EXTENSIONS); + const char* glVersion = (const char*) glGetString(GL_VERSION); + if (!glExtensions || !glVersion) + return false; + + // Parse the version string major.minor + const char *itr = glVersion; + int glMajor = dAtoi(itr); + while (dIsdigit(*itr)) + *itr++; + int glMinor = dAtoi(++itr); + + // Test for, and bind, all known extensions. GL and GLX versions ubove 1.0 + // are also treated as extensions and bound here. + #define GL_GROUP_BEGIN(name) \ + if (hasVersion(#name,"GL_VERSION",glMajor,glMinor) || \ + hasExtension(#name,glExtensions) || \ + hasExtension(#name,wgl)) \ + { \ + glf->has_##name = true; + #define GL_FUNCTION(fn_name, fn_return, fn_args) \ + bindExtension(*(void**)&glp->_##fn_name, #fn_name); + #define GL_GROUP_END() \ + } + #include "../generated/glefn.h" + #include "../generated/wglefn.h" + #undef GL_FUNCTION + #undef GL_GROUP_BEGIN + #undef GL_GROUP_END + + glf->bound = glp->bound = true; + return true; +} + +static DLibraryRef _hGL = NULL; + +void gglPerformBinds() +{ + if(!_hGL) + { + _hGL = OsLoadLibrary("opengl32.dll"); + gglBindCoreFunctions(_hGL); + } +} + +void gglPerformExtensionBinds(void *context) +{ + if(!_hGL) + { + _hGL = OsLoadLibrary("opengl32.dll"); + } + if(!_GGLptr) + { + static WGLExtensionPtrs ptrs; + static WGLExtensionFlags flags; + + _GGLptr = &ptrs; + _GGLflag = &flags; + } + + gglBindExtensions(_hGL, (WGLExtensionPtrs*)_GGLptr, (WGLExtensionFlags*)_GGLflag, (HDC)context); +} + +} // Namespace + diff --git a/Engine/source/gfx/gl/ggl/x11/glx.cpp b/Engine/source/gfx/gl/ggl/x11/glx.cpp new file mode 100644 index 000000000..f0c74e8e9 --- /dev/null +++ b/Engine/source/gfx/gl/ggl/x11/glx.cpp @@ -0,0 +1,413 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "os/osDlibrary.h" +#include "os/osLog.h" +#include "util/utilArray.h" + +#include "glx.h" + +namespace GL +{ +using namespace Torque; +using namespace GL; + +extern bool gglBindCoreFunctions(DLibrary* dll,GLXExtensionPtrs*); +void gglBindGLX(::Display* display,int screen,GLXExtensionFlags* glx); +bool gglBindExtensions(GLExtensionFlags* gl); + +struct GGLContext; + +//----------------------------------------------------------------------------- +// The OpenGL DLL is shared by all the devices. +static DLibraryRef _hGL; + +// Unlike WGL, GLX does not require a set of function pointers per +// context pixel format. All functions in the DLL are bound in a single +// table and shared by all contexts. +static GLXExtensionPtrs _LibraryFunctions; + + +//----------------------------------------------------------------------------- + +struct GGLDisplay: public _GLDisplay +{ + ::Display* display; + int screen; + GLXExtensionFlags glx; // GLX extension flags + Array contextList; +}; + +#define _hasGLXExtension(display,name) (display->glx.has_##name) + +GLDisplay gglCreateDisplay(::Display* display,int screen) +{ + // Load and bind DLL functions + if (!_hGL) { + static LogCategory log("/Gfx/Device/GL"); + _hGL = LoadLibrary("libGL.so"); + if (!_hGL) { + log.print("GLDevice: OpenGL dll failed to load"); + return false; + } + if (!gglBindCoreFunctions(_hGL.ptr(),&_LibraryFunctions)) { + log.print("GLDevice: Failed to bind all core functions"); + return false; + } + _GGLptr = &_LibraryFunctions; + log.print("OpenGL library loaded"); + } + + // + GGLDisplay* dp = new GGLDisplay; + dp->display = display; + dp->screen = screen; + gglBindGLX(display,screen,&dp->glx); + return dp; +} + +void gglDeleteDisplay(GLDisplay dp) +{ + GGLDisplay* display = (GGLDisplay*)dp; + Assert(!display->contextList.size(),"gglDeleteDisplay: Not all context destroyed"); + delete display; +} + + +//----------------------------------------------------------------------------- + +struct GGLFormat: public _GLFormat { + GLFormatInfo mode; + U32 pformat; +}; + +static bool _getFBConfig(GGLDisplay* display,U32 pformat,GLXFBConfig& config) +{ + // Get config info from format ID + int attributes[] = { GLX_FBCONFIG_ID, pformat, None }; + int count = 0; + GLXFBConfig* configList = glXChooseFBConfig(display->display, + display->screen,attributes,&count); + if (!count) + return false; + config = *configList; + XFree(configList); + return true; +} + +GLFormat gglSelectFormat(GLDisplay dp,GLFormatInfo& mode) +{ + GGLDisplay* display = (GGLDisplay*)dp; + U32 pformat; + + if (!_hasGLXExtension(display,GLX_VERSION_1_3)) { + // Find GL compatible X visual using 1.2 interface. + // 1.2 or earlier does not include pbuffer support. + if (mode.target != GLFormatInfo::Window) + return 0; + + int attributes[] = { + GLX_RGBA, + GLX_RED_SIZE, mode.red, + GLX_BLUE_SIZE, mode.blue, + GLX_GREEN_SIZE, mode.green, + GLX_DEPTH_SIZE, mode.z, + GLX_ALPHA_SIZE, mode.alpha, + GLX_STENCIL_SIZE, mode.stencil, + GLX_DOUBLEBUFFER, true, + GLX_DOUBLEBUFFER, + None + }; + XVisualInfo* visual = glXChooseVisual(display->display, display->screen, attributes); + if (!visual) { + static LogCategory log("/Gfx/Device/GL"); + log.print("GGLContext: Could not get an accelerated visual"); + return 0; + } + pformat = visual->visualid; + XFree(visual); + } + else { + // Find GL compatible X visual using 1.3 interface. + int attributes[] = { + GLX_DRAWABLE_TYPE, (mode.target == GLFormatInfo::Buffer)? + GLX_PBUFFER_BIT: GLX_WINDOW_BIT, + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_RED_SIZE, mode.red, + GLX_BLUE_SIZE, mode.blue, + GLX_GREEN_SIZE, mode.green, + GLX_DEPTH_SIZE, mode.z, + GLX_ALPHA_SIZE, mode.alpha, + GLX_STENCIL_SIZE, mode.stencil, + GLX_DOUBLEBUFFER, true, + None + }; + int count = 0; + GLXFBConfig* configList = glXChooseFBConfig(display->display, + display->screen,attributes,&count); + if (!count) + return 0; + + // Return the config ID + int xid; + glXGetFBConfigAttrib(display->display,configList[0],GLX_FBCONFIG_ID,&xid); + XFree(configList); + pformat = xid; + } + + if (pformat) { + GGLFormat* format = new GGLFormat; + format->pformat = pformat; + format->mode = mode; + return format; + } + return 0; +} + +bool gglFormatInfo(GLDisplay display,GLFormat pformat,GLFormatInfo& mode) +{ + return 0; +} + +bool gglAreCompatible(GLFormat,GLFormat) +{ + return false; +} + +void gglDeleteFormat(GLFormat fp) +{ + GGLFormat* format = (GGLFormat*)fp; + delete format; +} + +XVisualInfo* gglGetFormatVisual(GLDisplay dp,GLFormat fp) +{ + GGLDisplay* display = (GGLDisplay*)dp; + GGLFormat* format = (GGLFormat*)fp; + + if (!_hasGLXExtension(display,GLX_VERSION_1_3)) { + // Format is a visual id + int count; + XVisualInfo match; + match.visualid = format->pformat; + return XGetVisualInfo(display->display,VisualIDMask,&match,&count); + } + else { + // Format is an FBConfig id + GLXFBConfig config; + if (!_getFBConfig(display,format->pformat,config)) + return 0; + return glXGetVisualFromFBConfig(display->display,config); + } +} + + +//----------------------------------------------------------------------------- + +struct GGLSurface: public _GLSurface { + enum Type { + Window, PBuffer, + } type; + GGLDisplay* display; + GLXDrawable drawable; // Used with GLX 1.3 interface + ::Window window; // Used with GLX 1.2 interface +}; + +GLSurface gglCreateSurface(GLDisplay dp,::Window hwin,GLFormat fp) +{ + GGLDisplay* display = (GGLDisplay*)dp; + GGLFormat* format = (GGLFormat*)fp; + GGLSurface* surface = 0; + + if (!_hasGLXExtension(display,GLX_VERSION_1_3)) { + // Return our own GGL surface rep. + surface = new GGLSurface; + surface->type = GGLSurface::Window; + surface->display = display; + surface->drawable = 0; + surface->window = hwin; + } + else { + // Get config info from format ID + GLXFBConfig config; + if (!_getFBConfig(display,format->pformat,config)) + return 0; + + // Create GLX window + GLXWindow window = glXCreateWindow(display->display,config,hwin,0); + if (!window) { + GLenum error = glGetError(); + static LogCategory log("/Gfx/Device/GL"); + log.print(format("Create window error: %d",error)); + return 0; + } + + // Return our own GGL surface rep. + surface = new GGLSurface; + surface->type = GGLSurface::Window; + surface->display = display; + surface->drawable = window; + surface->window = 0; + } + return surface; +} + +bool gglGetSurfaceSize(GLSurface sp,Vector2I& size) +{ + GGLSurface* surface = (GGLSurface*)sp; + if (surface->type != GGLSurface::Window) + return false; + if (!_hasGLXExtension(surface->display,GLX_VERSION_1_3)) { + size.x = 300; size.y = 300; + } + else { + glXQueryDrawable(surface->display->display,surface->drawable, + GLX_WIDTH,(U32*)&size.x); + glXQueryDrawable(surface->display->display,surface->drawable, + GLX_HEIGHT,(U32*)&size.y); + } + return true; +} + +void gglDeleteSurface(GLSurface sp) +{ + GGLSurface* surface = (GGLSurface*)sp; + if (surface->type = GGLSurface::Window) { + glXDestroyWindow(surface->display->display,surface->drawable); + delete surface; + } +} + + +//----------------------------------------------------------------------------- + +struct GGLContext: public _GLContext +{ + GGLDisplay* display; + GLXContext context; + GLExtensionFlags glf; // GL has extension flags +}; + +GLContext gglCreateContext(GLDisplay dp,GLFormat fp) +{ + GGLDisplay* display = (GGLDisplay*)dp; + GGLFormat* format = (GGLFormat*)fp; + GGLContext* context = 0; + + GLXContext ctx,share = display->contextList.size()? + display->contextList[0]->context: 0; + + if (!_hasGLXExtension(display,GLX_VERSION_1_3)) { + // Get visual from format id. + int count; + XVisualInfo match; + match.visualid = format->pformat; + XVisualInfo* visual = XGetVisualInfo(display->display,VisualIDMask,&match,&count); + + ctx = glXCreateContext(display->display,visual,share,true); + XFree(visual); + } + else { + // Get FBConfig from format id + GLXFBConfig config; + if (!_getFBConfig(display,format->pformat,config)) + return 0; + + // Need to share contexts... + ctx = glXCreateNewContext(display->display,config, + GLX_RGBA_TYPE,share,true); + } + + if (ctx) { + context = new GGLContext; + context->context = ctx; + context->display = display; + display->contextList.pushBack(context); + return context; + } + return 0; +} + +void gglDeleteContext(GLContext ctx) +{ + GGLContext* context = (GGLContext*)ctx; + glXDestroyContext(context->display->display,context->context); + erase(context->display->contextList,context); + delete context; +} + +bool gglMakeCurrent(GLSurface sp,GLContext ctx) +{ + GGLSurface* surface = (GGLSurface*)sp; + GGLContext* context = (GGLContext*)ctx; + if (!_hasGLXExtension(surface->display,GLX_VERSION_1_3)) { + if (!glXMakeCurrent(surface->display->display, + surface->window,context->context)) + return false; + } + else + if (!glXMakeContextCurrent(surface->display->display, + surface->drawable,surface->drawable,context->context)) + return false; + + // The first time a context is made current we need to + // check which extensions are valid. + if (!context->glf.bound) + gglBindExtensions(&context->glf); + _GGLflag = &context->glf; + return true; +} + +void gglSwapBuffers(GLSurface sp) +{ + GGLSurface* surface = (GGLSurface*)sp; + if (!_hasGLXExtension(surface->display,GLX_VERSION_1_3)) + glXSwapBuffers(surface->display->display,surface->window); + else + glXSwapBuffers(surface->display->display,surface->drawable); +} + + +//----------------------------------------------------------------------------- + +GLSurface gglCreatePBufferSurface(GLDisplay display,U32 width,U32 height,bool cubeMap,GLFormat pformat) +{ + return 0; +} + +bool gglBindPBufferSurface(GLSurface sp,U32 target) +{ + return 0; +} + +bool gglReleasePBufferSurface(GLSurface sp,U32 target) +{ + return 0; +} + +bool gglSetPbufferTarget(GLSurface sp,U32 mip,U32 face) +{ + return 0; +} + +} // Namespace + diff --git a/Engine/source/gfx/gl/ggl/x11/glx.h b/Engine/source/gfx/gl/ggl/x11/glx.h new file mode 100644 index 000000000..ede18dadd --- /dev/null +++ b/Engine/source/gfx/gl/ggl/x11/glx.h @@ -0,0 +1,141 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +/* +** The contents of this file are subject to the GLX Public License Version 1.0 +** (the "License"). You may not use this file except in compliance with the +** License. You may obtain a copy of the License at Silicon Graphics, Inc., +** attn: Legal Services, 2011 N. Shoreline Blvd., Mountain View, CA 94043 +** or at http://www.sgi.com/software/opensource/glx/license.html. +** +** Software distributed under the License is distributed on an "AS IS" +** basis. ALL WARRANTIES ARE DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY +** IMPLIED WARRANTIES OF MERCHANTABILITY, OF FITNESS FOR A PARTICULAR +** PURPOSE OR OF NON- INFRINGEMENT. See the License for the specific +** language governing rights and limitations under the License. +** +** The Original Software is GLX version 1.2 source code, released February, +** 1999. The developer of the Original Software is Silicon Graphics, Inc. +** Those portions of the Subject Software created by Silicon Graphics, Inc. +** are Copyright (c) 1991-9 Silicon Graphics, Inc. All Rights Reserved. +*/ + +#ifndef GFX_GLX_H +#define GFX_GLX_H + +#ifndef GFX_GGL_H + #include "../ggl.h" +#endif + +#include +#include + +namespace GL +{ + +//----------------------------------------------------------------------------- +#ifndef DOXYGEN + +#define GLX_VERSION_1_0 1 + +#define GLX_USE_GL 1 +#define GLX_BUFFER_SIZE 2 +#define GLX_LEVEL 3 +#define GLX_RGBA 4 +#define GLX_DOUBLEBUFFER 5 +#define GLX_STEREO 6 +#define GLX_AUX_BUFFERS 7 +#define GLX_RED_SIZE 8 +#define GLX_GREEN_SIZE 9 +#define GLX_BLUE_SIZE 10 +#define GLX_ALPHA_SIZE 11 +#define GLX_DEPTH_SIZE 12 +#define GLX_STENCIL_SIZE 13 +#define GLX_ACCUM_RED_SIZE 14 +#define GLX_ACCUM_GREEN_SIZE 15 +#define GLX_ACCUM_BLUE_SIZE 16 +#define GLX_ACCUM_ALPHA_SIZE 17 +#define GLX_BAD_SCREEN 1 +#define GLX_BAD_ATTRIBUTE 2 +#define GLX_NO_EXTENSION 3 +#define GLX_BAD_VISUAL 4 +#define GLX_BAD_CONTEXT 5 +#define GLX_BAD_VALUE 6 +#define GLX_BAD_ENUM 7 + +typedef XID GLXDrawable; +typedef XID GLXPixmap; +typedef struct __GLXcontextRec *GLXContext; + +#include "../generated/glxe.h" + +//----------------------------------------------------------------------------- +// All core functionality is implemented as function pointers. + +#define GL_GROUP_BEGIN(name) +#define GL_FUNCTION(name, type, args) extern type (XGL_DLL *name) args; +#define GL_GROUP_END() +#include "../generated/glxfn.h" +#undef GL_GROUP_BEGIN +#undef GL_FUNCTION +#undef GL_GROUP_END + + +//----------------------------------------------------------------------------- +// Extensions use indirection in order to support multiple contexts + +struct GLXExtensionPtrs: public GLExtensionPtrs { + // Include all GLX extentions in global function table + #define GL_GROUP_BEGIN(name) + #define GL_FUNCTION(name, type, args) type (XGL_DLL *_##name) args; + #define GL_GROUP_END() + #include "../generated/glxefn.h" + #undef GL_GROUP_BEGIN + #undef GL_FUNCTION + #undef GL_GROUP_END +}; + +struct GLXExtensionFlags { + // Define extension "has" variables + #define GL_GROUP_BEGIN(name) bool has_##name; + #define GL_FUNCTION(name, type, args) + #define GL_GROUP_END() + #include "../generated/glxefn.h" + #undef GL_GROUP_BEGIN + #undef GL_FUNCTION + #undef GL_GROUP_END +}; + +#endif // Doxygen + +//----------------------------------------------------------------------------- + +GLDisplay gglCreateDisplay(::Display*,int screen); +GLSurface gglCreateSurface(GLDisplay,Window,GLFormat); +XVisualInfo* gglGetFormatVisual(GLDisplay dp,GLFormat format); + +#undef XGL_FUNCPTR +#define XGL_FUNCPTR(name) (((GLXExtensionPtrs*)GL::_GGLptr)->_##name) + +} // Namespace +#endif + diff --git a/Engine/source/gfx/gl/ggl/x11/glxBind.cpp b/Engine/source/gfx/gl/ggl/x11/glxBind.cpp new file mode 100644 index 000000000..dccba8928 --- /dev/null +++ b/Engine/source/gfx/gl/ggl/x11/glxBind.cpp @@ -0,0 +1,176 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "glx.h" + +#include "util/utilAssert.h" +#include "util/utilStr.h" +#include "util/utilString.h" +#include "util/utilMemory.h" +#include "os/osLog.h" +#include "os/osDlibrary.h" + +//----------------------------------------------------------------------------- +// Instantiation of GL function pointers in global namespace. + +#define GL_GROUP_BEGIN(name) +#define GL_FUNCTION(name, type, args) type (XGL_DLL *name) args; +#define GL_GROUP_END() +#include "../generated/glcfn.h" +#undef GL_GROUP_BEGIN +#undef GL_FUNCTION +#undef GL_GROUP_END + + +namespace GL +{ +using namespace Torque; + +bool bindFunction(DLibrary* dll,void *&fnAddress, const char *name); +bool hasExtension(const char *name,const char* extensions); +bool hasVersion(const char *name,const char* prefix,int major,int minor); + + +//----------------------------------------------------------------------------- +// Instantiation of GLX function pointers. + +#define GL_GROUP_BEGIN(name) +#define GL_FUNCTION(name, type, args) type (XGL_DLL *name) args; +#define GL_GROUP_END() +#include "../generated/glxfn.h" +#undef GL_GROUP_BEGIN +#undef GL_FUNCTION +#undef GL_GROUP_END + + +//----------------------------------------------------------------------------- + +bool bindFunction(DLibrary* dll,GLFunction (*gproc)(const GLubyte*), + void *&fnAddress, const char *name) +{ + // Use the getProcAddress if we have it. + fnAddress = gproc? (void*)(*gproc)((const GLubyte*)name): dll->bind(name); + return fnAddress != 0; +} + + +//----------------------------------------------------------------------------- + +bool gglBindCoreFunctions(DLibrary* dll,GLXExtensionPtrs* glp) +{ + bool bound = true; + + // Bind static functions which are quarenteed to be part of the + // OpenGL library. In this case, OpenGL 1.0 and GLX 1.0 functions + #define GL_GROUP_BEGIN(name) + #define GL_GROUP_END() + #define GL_FUNCTION(fn_name, fn_return, fn_args) \ + bound &= bindFunction(dll,*(void**)&fn_name, #fn_name); + #include "../generated/glcfn.h" + #include "../generated/glxfn.h" + #undef GL_FUNCTION + #undef GL_GROUP_BEGIN + #undef GL_GROUP_END + + // Check for the getProcAddress first, otherwise we'll expect everything + // to be in the DLL. + memset(glp,0,sizeof(GLXExtensionPtrs)); + glp->_glXGetProcAddressARB = (GLFunction (*)(const GLubyte*))dll->bind("glXGetProcAddressARB"); + + // Try and bind all known extension functions. We'll check later to + // see which ones are actually valid for a context. + #define GL_GROUP_BEGIN(name) + #define GL_FUNCTION(fn_name, fn_return, fn_args) \ + bindFunction(dll,glp->_glXGetProcAddressARB,*(void**)&glp->_##fn_name, #fn_name); + #define GL_GROUP_END() + #include "../generated/glefn.h" + #include "../generated/glxefn.h" + #undef GL_FUNCTION + #undef GL_GROUP_BEGIN + #undef GL_GROUP_END + + return bound; +} + + +//----------------------------------------------------------------------------- + +void gglBindGLX(::Display* display,int screen,GLXExtensionFlags* glx) +{ + // Check GLX version and glx extensions + int glxMajor,glxMinor; + glXQueryVersion(display,&glxMajor,&glxMinor); + const char* glxExtensions = glXQueryExtensionsString(display,screen); + + static LogCategory log("/Gfx/Device/GL"); + log.print(format("GLX Version: %d.%d",glxMajor,glxMinor)); + Assert(glxMajor == 1 && glxMinor >= 1,"GLXBind: Need GLX version 1.1 or greater"); + + #define GL_GROUP_BEGIN(name) \ + glx->has_##name = hasVersion(#name,"GLX_VERSION",glxMajor,glxMinor) || \ + hasExtension(#name,glxExtensions); + #define GL_FUNCTION(fn_name, fn_return, fn_args) + #define GL_GROUP_END() + #include "../generated/glxefn.h" + #undef GL_FUNCTION + #undef GL_GROUP_BEGIN + #undef GL_GROUP_END +} + + +//----------------------------------------------------------------------------- + +bool gglBindExtensions(GLExtensionFlags* gl) +{ + memset(gl,0,sizeof(GLExtensionFlags)); + + // Get GL version and extensions + const char* glExtensions = (const char*)glGetString(GL_EXTENSIONS); + const char* glVersion = (const char*) glGetString(GL_VERSION); + if (!glExtensions || !glVersion) + return false; + + // Parse the GL version string "major.minor" + const char *itr = glVersion; + int glMajor = atoi(itr); + while (isDigit(*itr)) + *itr++; + int glMinor = atoi(++itr); + + // Check which extensions are available on the active context. + // GL and GLX versions ubove 1.0 are also tested here. + #define GL_GROUP_BEGIN(name) \ + gl->has_##name = hasVersion(#name,"GL_VERSION",glMajor,glMinor) || \ + hasExtension(#name,glExtensions); + #define GL_FUNCTION(fn_name, fn_return, fn_args) + #define GL_GROUP_END() + #include "../generated/glefn.h" + #undef GL_FUNCTION + #undef GL_GROUP_BEGIN + #undef GL_GROUP_END + + gl->bound = true; + return true; +} + +} // Namespace + diff --git a/Engine/source/gfx/primBuilder.cpp b/Engine/source/gfx/primBuilder.cpp new file mode 100644 index 000000000..42ac2d495 --- /dev/null +++ b/Engine/source/gfx/primBuilder.cpp @@ -0,0 +1,334 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#include "primBuilder.h" +#include "gfxDevice.h" +#include "console/console.h" + + +//***************************************************************************** +// Primitive Builder +//***************************************************************************** +namespace PrimBuild +{ +Vector mTempVertBuff; +GFXVertexBufferHandle mVertBuff; +GFXPrimitiveType mType; +U32 mCurVertIndex; +ColorI mCurColor( 255, 255, 255 ); +Point2F mCurTexCoord; +const ColorI _colWhite( 255, 255, 255, 255 ); + +#ifdef TORQUE_DEBUG +U32 mMaxVerts; + +#define INIT_VERTEX_SIZE(x) mMaxVerts = x; +#define VERTEX_BOUNDS_CHECK() AssertFatal( mCurVertIndex < mMaxVerts, "PrimBuilder encountered an out of bounds vertex! Break and debug!" ); + +// This next check shouldn't really be used a lot unless you are tracking down +// a specific bug. -pw +#define VERTEX_SIZE_CHECK() AssertFatal( mCurVertIndex <= mMaxVerts, "PrimBuilder allocated more verts than you used! Break and debug or rendering artifacts could occur." ); + +#else + +#define INIT_VERTEX_SIZE(x) +#define VERTEX_BOUNDS_CHECK() +#define VERTEX_SIZE_CHECK() + +#endif + +//----------------------------------------------------------------------------- +// begin +//----------------------------------------------------------------------------- +void begin( GFXPrimitiveType type, U32 maxVerts ) +{ + AssertFatal( type >= GFXPT_FIRST && type < GFXPT_COUNT, "PrimBuilder::end() - Bad primitive type!" ); + + mType = type; + mCurVertIndex = 0; + INIT_VERTEX_SIZE( maxVerts ); + mTempVertBuff.setSize( maxVerts ); +} + +void beginToBuffer( GFXPrimitiveType type, U32 maxVerts ) +{ + AssertFatal( type >= GFXPT_FIRST && type < GFXPT_COUNT, "PrimBuilder::end() - Bad primitive type!" ); + + mType = type; + mCurVertIndex = 0; + INIT_VERTEX_SIZE( maxVerts ); + mTempVertBuff.setSize( maxVerts ); +} + +//----------------------------------------------------------------------------- +// end +//----------------------------------------------------------------------------- +GFXVertexBuffer * endToBuffer( U32 &numPrims ) +{ + mVertBuff.set(GFX, mTempVertBuff.size(), GFXBufferTypeVolatile); + GFXVertexPCT *verts = mVertBuff.lock(); + dMemcpy( verts, mTempVertBuff.address(), mTempVertBuff.size() * sizeof(GFXVertexPCT) ); + mVertBuff.unlock(); + + VERTEX_SIZE_CHECK(); + + switch( mType ) + { + case GFXPointList: + { + numPrims = mCurVertIndex; + break; + } + + case GFXLineList: + { + numPrims = mCurVertIndex / 2; + break; + } + + case GFXLineStrip: + { + numPrims = mCurVertIndex - 1; + break; + } + + case GFXTriangleList: + { + numPrims = mCurVertIndex / 3; + break; + } + + case GFXTriangleStrip: + case GFXTriangleFan: + { + numPrims = mCurVertIndex - 2; + break; + } + case GFXPT_COUNT: + // handle warning + break; + } + + return mVertBuff; +} + +void end( bool useGenericShaders ) +{ + if ( mCurVertIndex == 0 ) + return; + + VERTEX_SIZE_CHECK(); + + U32 vertStride = 1; + U32 stripStart = 0; + + AssertFatal( mType >= GFXPT_FIRST && mType < GFXPT_COUNT, "PrimBuilder::end() - Bad primitive type!" ); + + switch( mType ) + { + default: + case GFXPointList: + { + vertStride = 1; + break; + } + + case GFXLineList: + { + vertStride = 2; + break; + } + + case GFXTriangleList: + { + vertStride = 3; + break; + } + + case GFXLineStrip: + { + stripStart = 1; + vertStride = 1; + break; + } + + case GFXTriangleStrip: + case GFXTriangleFan: + { + stripStart = 2; + vertStride = 1; + break; + } + } + + if ( useGenericShaders ) + GFX->setupGenericShaders( GFXDevice::GSModColorTexture ); + + const GFXVertexPCT *srcVerts = mTempVertBuff.address(); + U32 numVerts = mCurVertIndex; + + // Make sure we don't have a dirty prim buffer left. + GFX->setPrimitiveBuffer( NULL ); + + if ( stripStart > 0 ) + { + // TODO: Fix this to allow > MAX_DYNAMIC_VERTS! + + U32 copyVerts = getMin( (U32)MAX_DYNAMIC_VERTS, numVerts ); + mVertBuff.set( GFX, copyVerts, GFXBufferTypeVolatile ); + + GFXVertexPCT *verts = mVertBuff.lock(); + dMemcpy( verts, srcVerts, copyVerts * sizeof( GFXVertexPCT ) ); + mVertBuff.unlock(); + + U32 numPrims = ( copyVerts / vertStride ) - stripStart; + GFX->setVertexBuffer( mVertBuff ); + GFX->drawPrimitive( mType, 0, numPrims ); + } + else + { + while ( numVerts > 0 ) + { + U32 copyVerts = getMin( (U32)MAX_DYNAMIC_VERTS, numVerts ); + copyVerts -= copyVerts % vertStride; + + mVertBuff.set( GFX, copyVerts, GFXBufferTypeVolatile ); + + GFXVertexPCT *verts = mVertBuff.lock(); + dMemcpy( verts, srcVerts, copyVerts * sizeof( GFXVertexPCT ) ); + mVertBuff.unlock(); + + U32 numPrims = copyVerts / vertStride; + GFX->setVertexBuffer( mVertBuff ); + GFX->drawPrimitive( mType, 0, numPrims ); + + srcVerts += copyVerts; + numVerts -= copyVerts; + } + } +} + +//----------------------------------------------------------------------------- +// vertex2f +//----------------------------------------------------------------------------- +void vertex2f( F32 x, F32 y ) +{ + VERTEX_BOUNDS_CHECK(); + GFXVertexPCT *vert = &mTempVertBuff[mCurVertIndex++]; + + vert->point.x = x; + vert->point.y = y; + vert->point.z = 0.0; + vert->color = mCurColor; + vert->texCoord = mCurTexCoord; +} + +//----------------------------------------------------------------------------- +// vertex3f +//----------------------------------------------------------------------------- +void vertex3f( F32 x, F32 y, F32 z ) +{ + VERTEX_BOUNDS_CHECK(); + GFXVertexPCT *vert = &mTempVertBuff[mCurVertIndex++]; + + vert->point.x = x; + vert->point.y = y; + vert->point.z = z; + vert->color = mCurColor; + vert->texCoord = mCurTexCoord; +} + +//----------------------------------------------------------------------------- +// vertex3fv +//----------------------------------------------------------------------------- +void vertex3fv( const F32 *data ) +{ + VERTEX_BOUNDS_CHECK(); + GFXVertexPCT *vert = &mTempVertBuff[mCurVertIndex++]; + + vert->point.set( data[0], data[1], data[2] ); + vert->color = mCurColor; + vert->texCoord = mCurTexCoord; +} + +//----------------------------------------------------------------------------- +// vertex2fv +//----------------------------------------------------------------------------- +void vertex2fv( const F32 *data ) +{ + VERTEX_BOUNDS_CHECK(); + GFXVertexPCT *vert = &mTempVertBuff[mCurVertIndex++]; + + vert->point.set( data[0], data[1], 0.f ); + vert->color = mCurColor; + vert->texCoord = mCurTexCoord; +} + + + +//----------------------------------------------------------------------------- +// color +//----------------------------------------------------------------------------- +void color( const ColorI &inColor ) +{ + mCurColor = inColor; +} + +void color( const ColorF &inColor ) +{ + mCurColor = inColor; +} + +void color3i( U8 red, U8 green, U8 blue ) +{ + mCurColor.set( red, green, blue ); +} + +void color4i( U8 red, U8 green, U8 blue, U8 alpha ) +{ + mCurColor.set( red, green, blue, alpha ); +} + +void color3f( F32 red, F32 green, F32 blue ) +{ + mCurColor.set( U8( red * 255 ), U8( green * 255 ), U8( blue * 255 ) ); +} + +void color4f( F32 red, F32 green, F32 blue, F32 alpha ) +{ + mCurColor.set( U8( red * 255 ), U8( green * 255 ), U8( blue * 255 ), U8( alpha * 255 ) ); +} + + +//----------------------------------------------------------------------------- +// texCoord +//----------------------------------------------------------------------------- +void texCoord2f( F32 x, F32 y ) +{ + mCurTexCoord.set( x, y ); +} + +void shutdown() +{ + mVertBuff = NULL; +} + +} // namespace PrimBuild diff --git a/Engine/source/gfx/primBuilder.h b/Engine/source/gfx/primBuilder.h new file mode 100644 index 000000000..07fc2b007 --- /dev/null +++ b/Engine/source/gfx/primBuilder.h @@ -0,0 +1,85 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PRIMBUILDER_H_ +#define _PRIMBUILDER_H_ + +#include "gfx/gfxVertexBuffer.h" + +//************************************************************************** +// +//************************************************************************** + +/// Primitive Builder. +/// +/// A simple interface to put together lines and polygons +/// quickly and easily - OpenGL style. This is basically +/// a convenient way to fill a vertex buffer, then draw it. +/// +/// There are two ways to use it. You can use the begin() +/// and end() calls to have it draw immediately after calling +/// end(). This is the "OpenGL" or "immediate" style of usage. +/// +/// The other way to use this is to use the beginToBuffer() +/// and endToBuffer() calls, which let you store the +/// results of your intermediate calls for later use. +/// This is much more efficient than using the immediate style. +/// +namespace PrimBuild +{ + extern const ColorI _colWhite; + + void beginToBuffer( GFXPrimitiveType type, U32 maxVerts ); + GFXVertexBuffer *endToBuffer( U32 &outNumPrims ); + + void begin( GFXPrimitiveType type, U32 maxVerts ); + void end( bool useGenericShaders = true ); + + void vertex2f( F32 x, F32 y ); + void vertex3f( F32 x, F32 y, F32 z ); + + void vertex2fv( const F32 *data ); + inline void vertex2fv( const Point2F &pnt ) { vertex2fv( (F32 *) &pnt ); }; + inline void vertex2fv( const Point2F *pnt ) { vertex2fv( (F32 *) pnt ); }; + + void vertex3fv( const F32 *data ); + inline void vertex3fv( const Point3F &pnt ) { vertex3fv( (F32 *) &pnt ); }; + inline void vertex3fv( const Point3F *pnt ) { vertex3fv( (F32 *) pnt ); }; + + inline void vertex2i( S32 x, S32 y ) { vertex2f((F32)x, (F32)y); } + inline void vertex3i( S32 x, S32 y, S32 z ) { vertex3f((F32)x, (F32)y, (F32)z); } + + void color( const ColorI & ); + void color( const ColorF & ); + void color3i( U8 red, U8 green, U8 blue ); + void color4i( U8 red, U8 green, U8 blue, U8 alpha ); + void color3f( F32 red, F32 green, F32 blue ); + void color4f( F32 red, F32 green, F32 blue, F32 alpha ); + + inline void colorWhite() { color( _colWhite ); } + + void texCoord2f( F32 x, F32 y ); + + void shutdown(); +} + +#endif \ No newline at end of file diff --git a/Engine/source/gfx/screenshot.cpp b/Engine/source/gfx/screenshot.cpp new file mode 100644 index 000000000..dc8b5fef2 --- /dev/null +++ b/Engine/source/gfx/screenshot.cpp @@ -0,0 +1,283 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/screenshot.h" + +#include "math/util/frustum.h" +#include "core/stream/fileStream.h" +#include "gui/core/guiCanvas.h" +#include "gfx/bitmap/pngUtils.h" +#include "console/engineAPI.h" + + +// Note: This will be initialized by the device. +ScreenShot *gScreenShot = NULL; + +inline void sBlendPixelRGB888( U8* src, U8* dst, F32 factor ) +{ + U32 inFactor = factor * BIT(8); + U32 outFactor = BIT(8) - inFactor; + + dst[0] = ((U32)src[0]*inFactor + (U32)dst[0]*outFactor) >> 8; + dst[1] = ((U32)src[1]*inFactor + (U32)dst[1]*outFactor) >> 8; + dst[2] = ((U32)src[2]*inFactor + (U32)dst[2]*outFactor) >> 8; +} + + +ScreenShot::ScreenShot() + : mPending( false ), + mWriteJPG( false ), + mCurrTile( 0, 0 ), + mTiles( 1 ) +{ + mFilename[0] = 0; +} + +void ScreenShot::setPending( const char *filename, bool writeJPG, S32 tiles, F32 overlap ) +{ + dStrcpy( mFilename, filename ); + mWriteJPG = writeJPG; + mTiles = getMax( tiles, 1 ); + mPixelOverlap.set(getMin(overlap, 0.25f), getMin(overlap, 0.25f)); + + mPending = true; +} + +void ScreenShot::tileFrustum( Frustum& frustum ) +{ + AssertFatal( mPending, "ScreenShot::tileFrustum() - This should only be called during screenshots!" ); + + // We do not need to make changes on a single tile. + if ( mTiles == 1 ) + return; + + frustum.tileFrustum(mTiles, mCurrTile, mFrustumOverlap); +} + +void ScreenShot::tileGui( const Point2I &screenSize ) +{ + AssertFatal( mPending, "ScreenShot::tileGui() - This should only be called during screenshots!" ); + + // We do not need to make changes on a single tile. + if ( mTiles == 1 ) + return; + + GFX->setWorldMatrix( MatrixF::Identity ); + + S32 currTileX = mCurrTile.x; + S32 currTileY = (S32)mTiles - mCurrTile.y - 1; //Vertically flipped tile index + + MatrixF tileMat( true ); + Point3F tilePos(0,0,0); + tilePos.x = currTileX * (-screenSize.x) + mPixelOverlap.x * screenSize.x * (currTileX * 2 + 1); + tilePos.y = currTileY * (-screenSize.y) + mPixelOverlap.y * screenSize.y * (currTileY * 2 + 1); + + tileMat.setPosition( tilePos + Point3F(0.5f, 0.5f, 0) ); + tileMat.scale( Point3F( (F32)mTiles * (1-mPixelOverlap.x*2), (F32)mTiles * (1-mPixelOverlap.y*2), 0 ) ); + + GFX->setViewMatrix( tileMat ); +} + +void ScreenShot::capture( GuiCanvas *canvas ) +{ + AssertFatal( mPending, "ScreenShot::capture() - The capture wasn't pending!" ); + + if ( mTiles == 1 ) + { + _singleCapture( canvas ); + return; + } + + char filename[256]; + + Point2I canvasSize = canvas->getPlatformWindow()->getVideoMode().resolution; + + // Calculate the real final size taking overlap in account + Point2I overlapPixels( canvasSize.x * mPixelOverlap.x, canvasSize.y * mPixelOverlap.y ); + + // Calculate the overlap to be used by the frustum tiling, + // so it properly expands the frustum to overlap the amount of pixels we want + mFrustumOverlap.x = ((F32)canvasSize.x/(canvasSize.x - overlapPixels.x*2)) * ((F32)overlapPixels.x/(F32)canvasSize.x); + mFrustumOverlap.y = ((F32)canvasSize.y/(canvasSize.y - overlapPixels.y*2)) * ((F32)overlapPixels.y/(F32)canvasSize.y); + + //overlapPixels.set(0,0); + // Get a buffer to write a row of tiles into. + GBitmap *outBuffer = new GBitmap( canvasSize.x * mTiles - overlapPixels.x * mTiles * 2 , canvasSize.y - overlapPixels.y ); + + // Open up the file on disk. + dSprintf( filename, 256, "%s.%s", mFilename, "png" ); + FileStream fs; + if ( !fs.open( filename, Torque::FS::File::Write ) ) + Con::errorf( "ScreenShot::capture() - Failed to open output file '%s'!", filename ); + + // Open a PNG stream for the final image + DeferredPNGWriter pngWriter; + pngWriter.begin(outBuffer->getFormat(), outBuffer->getWidth(), canvasSize.y * mTiles - overlapPixels.y * mTiles * 2, fs, 0); + + // Render each tile to generate a huge screenshot. + for( U32 ty=0; ty < mTiles; ty++ ) + { + for( S32 tx=0; tx < mTiles; tx++ ) + { + // Set the current tile offset for tileFrustum(). + mCurrTile.set( tx, mTiles - ty - 1 ); + + // Let the canvas render the scene. + canvas->renderFrame( false ); + + // Now grab the current back buffer. + GBitmap *gb = _captureBackBuffer(); + + // The current GFX device couldn't capture the backbuffer or it's unable of doing so. + if (gb == NULL) + return; + + + // Copy the captured bitmap into its tile + // within the output bitmap. + const U32 inStride = gb->getWidth() * gb->getBytesPerPixel(); + const U8 *inColor = gb->getBits() + inStride * overlapPixels.y; + const U32 outStride = outBuffer->getWidth() * outBuffer->getBytesPerPixel(); + const U32 inOverlapOffset = overlapPixels.x * gb->getBytesPerPixel(); + const U32 inOverlapStride = overlapPixels.x * gb->getBytesPerPixel()*2; + const U32 outOffset = (tx * (gb->getWidth() - overlapPixels.x*2 )) * gb->getBytesPerPixel(); + U8 *outColor = outBuffer->getWritableBits() + outOffset; + for( U32 row=0; row < gb->getHeight() - overlapPixels.y; row++ ) + { + dMemcpy( outColor, inColor + inOverlapOffset, inStride - inOverlapStride ); + + //Grandient blend the left overlap area of this tile over the previous tile left border + if (tx && !(ty && row < overlapPixels.y)) + { + U8 *blendOverlapSrc = (U8*)inColor; + U8 *blendOverlapDst = outColor - inOverlapOffset; + for ( U32 px=0; px < overlapPixels.x; px++) + { + F32 blendFactor = (F32)px / (F32)overlapPixels.x; + sBlendPixelRGB888(blendOverlapSrc, blendOverlapDst, blendFactor); + + blendOverlapSrc += gb->getBytesPerPixel(); + blendOverlapDst += outBuffer->getBytesPerPixel(); + } + } + + //Gradient blend against the rows the excess overlap rows already in the buffer + if (ty && row < overlapPixels.y) + { + F32 rowBlendFactor = (F32)row / (F32)overlapPixels.y; + U8 *blendSrc = outColor + outStride * (outBuffer->getHeight() - overlapPixels.y); + U8 *blendDst = outColor; + for ( U32 px=0; px < gb->getWidth() - overlapPixels.x*2; px++) + { + sBlendPixelRGB888(blendSrc, blendDst, 1.0-rowBlendFactor); + blendSrc += gb->getBytesPerPixel(); + blendDst += outBuffer->getBytesPerPixel(); + } + } + + + inColor += inStride; + outColor += outStride; + } + + delete gb; + } + + // Write the captured tile row into the PNG stream + pngWriter.append(outBuffer, outBuffer->getHeight()-overlapPixels.y); + } + + //Close the PNG stream + pngWriter.end(); + + // We captured... clear the flag. + mPending = false; +} + + +void ScreenShot::_singleCapture( GuiCanvas *canvas ) +{ + // Let the canvas render the scene. + canvas->renderFrame( false ); + + // Now grab the current back buffer. + GBitmap *bitmap = _captureBackBuffer(); + + // The current GFX device couldn't capture the backbuffer or it's unable of doing so. + if (bitmap == NULL) + return; + + // We captured... clear the flag. + mPending = false; + + // We gotta attach the extension ourselves. + char filename[256]; + dSprintf( filename, 256, "%s.%s", mFilename, mWriteJPG ? "jpg" : "png" ); + + // Open up the file on disk. + FileStream fs; + if ( !fs.open( filename, Torque::FS::File::Write ) ) + Con::errorf( "ScreenShot::_singleCapture() - Failed to open output file '%s'!", filename ); + else + { + // Write it and close. + if ( mWriteJPG ) + bitmap->writeBitmap( "jpg", fs ); + else + bitmap->writeBitmap( "png", fs ); + + fs.close(); + } + + // Cleanup. + delete bitmap; +} + + +DefineEngineFunction( screenShot, void, + ( const char *file, const char *format, U32 tileCount, F32 tileOverlap ), + ( 1, 0 ), + "Takes a screenshot with optional tiling to produce huge screenshots.\n" + "@param file The output image file path.\n" + "@param format Either JPEG or PNG.\n" + "@param tileCount If greater than 1 will tile the current screen size to take a large format screenshot.\n" + "@param tileOverlap The amount of horizontal and vertical overlap between the tiles used to remove tile edge artifacts from post effects.\n" + "@ingroup GFX\n" ) +{ + if ( !gScreenShot ) + { + Con::errorf( "Screenshot module not initialized by device" ); + return; + } + + Torque::Path ssPath( file ); + Torque::FS::CreatePath( ssPath ); + Torque::FS::FileSystemRef fs = Torque::FS::GetFileSystem(ssPath); + Torque::Path newPath = fs->mapTo(ssPath); + + gScreenShot->setPending( newPath.getFullPath(), + dStricmp( format, "JPEG" ) == 0, + tileCount, + tileOverlap ); +} + diff --git a/Engine/source/gfx/screenshot.h b/Engine/source/gfx/screenshot.h new file mode 100644 index 000000000..63de45280 --- /dev/null +++ b/Engine/source/gfx/screenshot.h @@ -0,0 +1,99 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _SCREENSHOT_H_ +#define _SCREENSHOT_H_ + +#include "gfx/gfxDevice.h" + +class GuiCanvas; +class Point2I; +class Frustum; + +//************************************************************************** +/*! + This class will eventually support various capabilities such as panoramics, + high rez captures, and cubemap captures. + + Right now it just captures standard screenshots, but it does support + captures from multisample back buffers, so antialiased captures will work. +*/ +//************************************************************************** + +class ScreenShot +{ + /// This is overloaded to copy the current GFX + /// backbuffer to a new bitmap. + virtual GBitmap* _captureBackBuffer() { return NULL; } + + /// This is set to toggle the capture. + bool mPending; + + /// If true write the file as a JPG else its a PNG. + bool mWriteJPG; + + /// The full path to the screenshot file to write. + char mFilename[256]; + + /// The number of times to tile the backbuffer to + /// generate screenshots larger than normally possible. + U32 mTiles; + + /// How much pixel percentage overlap between tiles + Point2F mPixelOverlap; + + /// How much the frustum must be adjusted for overlap + Point2F mFrustumOverlap; + + /// The current tile we're rendering. + Point2I mCurrTile; + + /// Helper for taking simple single tile screenshots and + /// outputing it as a single file to disk. + void _singleCapture( GuiCanvas *canvas ); + +public: + + /// Constructor. + ScreenShot(); + + /// Used to start the screenshot capture. + void setPending( const char *filename, bool writeJPG, S32 tiles, F32 overlap ); + + /// Returns true if we're in the middle of screenshot capture. + bool isPending() const { return mPending; } + + /// Prepares the view frustum for tiled screenshot rendering. + /// @see GuiTSCtrl::setupFrustum + void tileFrustum( Frustum& frustum ); + + /// + void tileGui( const Point2I &screenSize ); + + /// Called from the canvas to capture a pending screenshot. + /// @see GuiCanvas::onRenderEvent + void capture( GuiCanvas *canvas ); + +}; + +extern ScreenShot *gScreenShot; + +#endif // _SCREENSHOT_H_ diff --git a/Engine/source/gfx/sim/cubemapData.cpp b/Engine/source/gfx/sim/cubemapData.cpp new file mode 100644 index 000000000..aa06144af --- /dev/null +++ b/Engine/source/gfx/sim/cubemapData.cpp @@ -0,0 +1,287 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/sim/cubemapData.h" + +#include "core/stream/bitStream.h" +#include "console/consoleTypes.h" +#include "gfx/gfxCubemap.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/gfxDebugEvent.h" +#include "scene/sceneManager.h" +#include "console/engineAPI.h" +#include "math/mathUtils.h" + + +IMPLEMENT_CONOBJECT( CubemapData ); + +CubemapData::CubemapData() +{ + mCubemap = NULL; + mDynamic = false; + mDynamicSize = 512; + mDynamicNearDist = 0.1f; + mDynamicFarDist = 100.0f; + mDynamicObjectTypeMask = 0; +#ifdef INIT_HACK + mInit = false; +#endif +} + +CubemapData::~CubemapData() +{ + mCubemap = NULL; +} + +ConsoleDocClass( CubemapData, + "@brief Used to create static or dynamic cubemaps.\n\n" + "This object is used with Material, WaterObject, and other objects for cubemap reflections.\n\n" + "A simple declaration of a static cubemap:\n" + "@tsexample\n" + "singleton CubemapData( SkyboxCubemap )\n" + "{\n" + " cubeFace[0] = \"./skybox_1\";\n" + " cubeFace[1] = \"./skybox_2\";\n" + " cubeFace[2] = \"./skybox_3\";\n" + " cubeFace[3] = \"./skybox_4\";\n" + " cubeFace[4] = \"./skybox_5\";\n" + " cubeFace[5] = \"./skybox_6\";\n" + "};\n" + "@endtsexample\n" + "@note The dynamic cubemap functionality in CubemapData has been depreciated in favor of ReflectorDesc.\n" + "@see ReflectorDesc\n" + "@ingroup GFX\n" ); + +void CubemapData::initPersistFields() +{ + addField( "cubeFace", TypeStringFilename, Offset(mCubeFaceFile, CubemapData), 6, + "@brief The 6 cubemap face textures for a static cubemap.\n\n" + "They are in the following order:\n" + " - cubeFace[0] is -X\n" + " - cubeFace[1] is +X\n" + " - cubeFace[2] is -Z\n" + " - cubeFace[3] is +Z\n" + " - cubeFace[4] is -Y\n" + " - cubeFace[5] is +Y\n" ); + + addField("dynamic", TypeBool, Offset(mDynamic, CubemapData), + "Set to true if this is a dynamic cubemap. The default is false." ); + + addField("dynamicSize", TypeS32, Offset(mDynamicSize, CubemapData), + "The size of each dynamic cubemap face in pixels." ); + + addField("dynamicNearDist", TypeF32, Offset(mDynamicNearDist, CubemapData), + "The near clip distance used when rendering to the dynamic cubemap." ); + + addField("dynamicFarDist", TypeF32, Offset(mDynamicFarDist, CubemapData), + "The far clip distance used when rendering to the dynamic cubemap." ); + + addField("dynamicObjectTypeMask", TypeS32, Offset(mDynamicObjectTypeMask, CubemapData), + "The typemask used to filter the objects rendered to the dynamic cubemap." ); + + Parent::initPersistFields(); +} + +bool CubemapData::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + // Do NOT call this here as it forces every single cubemap defined to load its images, immediately, without mercy + // createMap(); + + return true; +} + +void CubemapData::createMap() +{ + if( !mCubemap ) + { + if( mDynamic ) + { + mCubemap = GFX->createCubemap(); + mCubemap->initDynamic( mDynamicSize ); + mDepthBuff = GFXTexHandle( mDynamicSize, mDynamicSize, GFXFormatD24S8, + &GFXDefaultZTargetProfile, avar("%s() - mDepthBuff (line %d)", __FUNCTION__, __LINE__)); + mRenderTarget = GFX->allocRenderToTextureTarget(); + } + else + { + bool initSuccess = true; + + for( U32 i=0; i<6; i++ ) + { + if( !mCubeFaceFile[i].isEmpty() ) + { + if(!mCubeFace[i].set(mCubeFaceFile[i], &GFXDefaultStaticDiffuseProfile, avar("%s() - mCubeFace[%d] (line %d)", __FUNCTION__, i, __LINE__) )) + { + Con::errorf("CubemapData::createMap - Failed to load texture '%s'", mCubeFaceFile[i].c_str()); + initSuccess = false; + } + } + } + + if( initSuccess ) + { + mCubemap = GFX->createCubemap(); + mCubemap->initStatic( mCubeFace ); + } + } + } +} + +void CubemapData::updateDynamic(SceneManager* sm, const Point3F& pos) +{ + AssertFatal(mDynamic, "This is not a dynamic cubemap!"); + + GFXDEBUGEVENT_SCOPE( CubemapData_updateDynamic, ColorI::WHITE ); + +#ifdef INIT_HACK + if( mInit ) return; + mInit = true; +#endif + + GFX->pushActiveRenderTarget(); + mRenderTarget->attachTexture(GFXTextureTarget::DepthStencil, mDepthBuff ); + + // store current matrices + GFXTransformSaver saver; + F32 oldVisibleDist = sm->getVisibleDistance(); + + // set projection to 90 degrees vertical and horizontal + { + F32 left, right, top, bottom; + MathUtils::makeFrustum( &left, &right, &top, &bottom, M_HALFPI_F, 1.0f, mDynamicNearDist ); + GFX->setFrustum( left, right, bottom, top, mDynamicNearDist, mDynamicFarDist ); + } + sm->setVisibleDistance(mDynamicFarDist); + + // We don't use a special clipping projection, but still need to initialize + // this for objects like SkyBox which will use it during a reflect pass. + gClientSceneGraph->setNonClipProjection( (MatrixF&) GFX->getProjectionMatrix() ); + + // Loop through the six faces of the cube map. + for(U32 i=0; i<6; i++) + { + // Standard view that will be overridden below. + VectorF vLookatPt(0.0f, 0.0f, 0.0f), vUpVec(0.0f, 0.0f, 0.0f), vRight(0.0f, 0.0f, 0.0f); + + switch( i ) + { + case 0 : // D3DCUBEMAP_FACE_POSITIVE_X: + vLookatPt = VectorF( 1.0f, 0.0f, 0.0f ); + vUpVec = VectorF( 0.0f, 1.0f, 0.0f ); + break; + case 1 : // D3DCUBEMAP_FACE_NEGATIVE_X: + vLookatPt = VectorF( -1.0f, 0.0f, 0.0f ); + vUpVec = VectorF( 0.0f, 1.0f, 0.0f ); + break; + case 2 : // D3DCUBEMAP_FACE_POSITIVE_Y: + vLookatPt = VectorF( 0.0f, 1.0f, 0.0f ); + vUpVec = VectorF( 0.0f, 0.0f,-1.0f ); + break; + case 3 : // D3DCUBEMAP_FACE_NEGATIVE_Y: + vLookatPt = VectorF( 0.0f, -1.0f, 0.0f ); + vUpVec = VectorF( 0.0f, 0.0f, 1.0f ); + break; + case 4 : // D3DCUBEMAP_FACE_POSITIVE_Z: + vLookatPt = VectorF( 0.0f, 0.0f, 1.0f ); + vUpVec = VectorF( 0.0f, 1.0f, 0.0f ); + break; + case 5: // D3DCUBEMAP_FACE_NEGATIVE_Z: + vLookatPt = VectorF( 0.0f, 0.0f, -1.0f ); + vUpVec = VectorF( 0.0f, 1.0f, 0.0f ); + break; + } + + // create camera matrix + VectorF cross = mCross( vUpVec, vLookatPt ); + cross.normalizeSafe(); + + MatrixF matView(true); + matView.setColumn( 0, cross ); + matView.setColumn( 1, vLookatPt ); + matView.setColumn( 2, vUpVec ); + matView.setPosition( pos ); + matView.inverse(); + + GFX->pushWorldMatrix(); + GFX->setWorldMatrix(matView); + + mRenderTarget->attachTexture( GFXTextureTarget::Color0, mCubemap, i ); + GFX->setActiveRenderTarget( mRenderTarget ); + GFX->clear( GFXClearStencil | GFXClearTarget | GFXClearZBuffer, ColorI( 64, 64, 64 ), 1.f, 0 ); + + // render scene + sm->renderScene( SPT_Reflect, mDynamicObjectTypeMask ); + + // Resolve render target for each face + mRenderTarget->resolve(); + GFX->popWorldMatrix(); + } + + // restore render surface and depth buffer + GFX->popActiveRenderTarget(); + + mRenderTarget->attachTexture(GFXTextureTarget::Color0, NULL); + sm->setVisibleDistance(oldVisibleDist); +} + +void CubemapData::updateFaces() +{ + bool initSuccess = true; + + for( U32 i=0; i<6; i++ ) + { + if( !mCubeFaceFile[i].isEmpty() ) + { + if(!mCubeFace[i].set(mCubeFaceFile[i], &GFXDefaultStaticDiffuseProfile, avar("%s() - mCubeFace[%d] (line %d)", __FUNCTION__, i, __LINE__) )) + { + initSuccess = false; + Con::errorf("CubemapData::createMap - Failed to load texture '%s'", mCubeFaceFile[i].c_str()); + } + } + } + + if( initSuccess ) + { + mCubemap = NULL; + mCubemap = GFX->createCubemap(); + + mCubemap->initStatic( mCubeFace ); + } +} + +DefineEngineMethod( CubemapData, updateFaces, void, (),, + "Update the assigned cubemaps faces." ) +{ + object->updateFaces(); +} + +DefineEngineMethod( CubemapData, getFilename, const char*, (),, + "Returns the script filename of where the CubemapData object was " + "defined. This is used by the material editor." ) +{ + return object->getFilename(); +} \ No newline at end of file diff --git a/Engine/source/gfx/sim/cubemapData.h b/Engine/source/gfx/sim/cubemapData.h new file mode 100644 index 000000000..76604a886 --- /dev/null +++ b/Engine/source/gfx/sim/cubemapData.h @@ -0,0 +1,87 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _CUBEMAPDATA_H_ +#define _CUBEMAPDATA_H_ + +#ifndef _SIMOBJECT_H_ +#include "console/simObject.h" +#endif + +#ifndef _GFXCUBEMAP_H_ +#include "gfx/gfxCubemap.h" +#endif + +#ifndef _GFXTARGET_H_ +#include "gfx/gfxTarget.h" +#endif + +#ifndef _SCENEMANAGER_H_ +#include "scene/sceneManager.h" +#endif + + +/// A script interface for creating static or dynamic cubemaps. +class CubemapData : public SimObject +{ + typedef SimObject Parent; + +public: + + GFXCubemapHandle mCubemap; + + CubemapData(); + ~CubemapData(); + + bool onAdd(); + static void initPersistFields(); + + DECLARE_CONOBJECT(CubemapData); + + // Force creation of cubemap + void createMap(); + + // Update a dynamic cubemap @ pos + void updateDynamic(SceneManager* sm, const Point3F& pos); + void updateFaces(); + + // Dynamic cube map support + bool mDynamic; + U32 mDynamicSize; + F32 mDynamicNearDist; + F32 mDynamicFarDist; + U32 mDynamicObjectTypeMask; + +protected: + + FileName mCubeFaceFile[6]; + GFXTexHandle mCubeFace[6]; + + GFXTexHandle mDepthBuff; + GFXTextureTargetRef mRenderTarget; +#ifdef INIT_HACK + bool mInit; +#endif +}; + +#endif // CUBEMAPDATA + diff --git a/Engine/source/gfx/sim/debugDraw.cpp b/Engine/source/gfx/sim/debugDraw.cpp new file mode 100644 index 000000000..2dfe00141 --- /dev/null +++ b/Engine/source/gfx/sim/debugDraw.cpp @@ -0,0 +1,433 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/sim/debugDraw.h" + +#include "gfx/gFont.h" +#include "gfx/gfxDrawUtil.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/gfxDebugEvent.h" +#include "math/mathUtils.h" +#include "math/util/frustum.h" +#include "console/console.h" +#include "scene/sceneManager.h" +#include "core/module.h" +#include "console/engineAPI.h" + +#include "math/mPolyhedron.impl.h" + + +MODULE_BEGIN( DebugDrawer ) + + MODULE_INIT_AFTER( Sim ) + MODULE_INIT_AFTER( GFX ) + + // DebugDrawer will register itself as a SimObject and + // thus get automatically shut down with Sim. + + MODULE_INIT + { + DebugDrawer::init(); + } + +MODULE_END; + + +DebugDrawer* DebugDrawer::sgDebugDrawer = NULL; + +IMPLEMENT_CONOBJECT(DebugDrawer); + +ConsoleDocClass( DebugDrawer, + "@brief A debug helper for rendering debug primitives to the scene.\n\n" + + "The DebugDrawer is used to render debug primitives to the scene for testing. It is " + "often useful when debugging collision code or complex 3d algorithms to have " + "them draw debug information, like culling hulls or bounding volumes, normals, " + "simple lines, and so forth.\n\n" + + "A key feature of the DebugDrawer is that each primitive gets a \"time to live\" (TTL) " + "which allows them to continue to render to the scene for a fixed period of time. You " + "can freeze or resume the system at any time to allow you to examine the output.\n" + + "@tsexample\n" + "DebugDraw.drawLine( %player.getMuzzlePoint( 0 ), %hitPoint );\n" + "DebugDraw.setLastTTL( 5000 ); // 5 seconds.\n" + "@endtsexample\n" + + "The DebugDrawer renders solely in world space and all primitives are rendered with the " + "cull mode disabled.\n" + + "@note This feature can easily be used to cheat in online games, so you should be sure " + "it is disabled in your shipping game. By default the DebugDrawer is disabled in all " + "TORQUE_SHIPPING builds.\n" + + "@ingroup GFX\n" ); + +DebugDrawer::DebugDrawer() +{ + mHead = NULL; + isFrozen = false; + shouldToggleFreeze = false; + +#ifdef ENABLE_DEBUGDRAW + isDrawing = true; +#else + isDrawing = false; +#endif +} + +DebugDrawer::~DebugDrawer() +{ + if( sgDebugDrawer == this ) + sgDebugDrawer = NULL; +} + +DebugDrawer* DebugDrawer::get() +{ + if (sgDebugDrawer) + { + return sgDebugDrawer; + } else { + DebugDrawer::init(); + return sgDebugDrawer; + } +} + +void DebugDrawer::init() +{ +#ifdef ENABLE_DEBUGDRAW + sgDebugDrawer = new DebugDrawer(); + sgDebugDrawer->registerObject("DebugDraw"); + Sim::getRootGroup()->addObject( sgDebugDrawer ); + Con::warnf( "DebugDrawer Enabled!" ); +#endif +} + +void DebugDrawer::setupStateBlocks() +{ + GFXStateBlockDesc d; + + d.setCullMode(GFXCullNone); + mRenderZOnSB = GFX->createStateBlock(d); + + d.setZReadWrite(false); + mRenderZOffSB = GFX->createStateBlock(d); +} + +void DebugDrawer::render() +{ +#ifdef ENABLE_DEBUGDRAW + if(!isDrawing) + return; + + GFXDEBUGEVENT_SCOPE( DebugDrawer, ColorI::GREEN ); + + if (!mRenderZOnSB) + { + setupStateBlocks(); + String fontCacheDir = Con::getVariable("$GUI::fontCacheDirectory"); + mFont = GFont::create("Arial", 12, fontCacheDir); + } + + SimTime curTime = Sim::getCurrentTime(); + + GFX->disableShaders(); + + for(DebugPrim **walk = &mHead; *walk; ) + { + DebugPrim *p = *walk; + + // Set up the state block... + GFXStateBlockRef currSB; + if(p->useZ) + currSB = mRenderZOnSB; + else + currSB = mRenderZOffSB; + GFX->setStateBlock( currSB ); + + Point3F d; + + switch(p->type) + { + case DebugPrim::Tri: + PrimBuild::begin( GFXLineStrip, 4); + + PrimBuild::color(p->color); + + PrimBuild::vertex3fv(p->a); + PrimBuild::vertex3fv(p->b); + PrimBuild::vertex3fv(p->c); + PrimBuild::vertex3fv(p->a); + + PrimBuild::end(); + break; + case DebugPrim::Box: + d = p->a - p->b; + GFX->getDrawUtil()->drawCube(currSB->getDesc(), d * 0.5, (p->a + p->b) * 0.5, p->color); + break; + case DebugPrim::Line: + PrimBuild::begin( GFXLineStrip, 2); + + PrimBuild::color(p->color); + + PrimBuild::vertex3fv(p->a); + PrimBuild::vertex3fv(p->b); + + PrimBuild::end(); + break; + case DebugPrim::Text: + { + GFXTransformSaver saver; + Point3F result; + if (MathUtils::mProjectWorldToScreen(p->a, &result, GFX->getViewport(), GFX->getWorldMatrix(), GFX->getProjectionMatrix())) + { + GFX->setClipRect(GFX->getViewport()); + GFX->getDrawUtil()->setBitmapModulation(p->color); + GFX->getDrawUtil()->drawText(mFont, Point2I(result.x, result.y), p->mText); + } + } + break; + } + + // Ok, we've got data, now freeze here if needed. + if (shouldToggleFreeze) + { + isFrozen = !isFrozen; + shouldToggleFreeze = false; + } + + if(p->dieTime <= curTime && !isFrozen && p->dieTime != U32_MAX) + { + *walk = p->next; + mPrimChunker.free(p); + } + else + walk = &((*walk)->next); + } +#endif +} + +void DebugDrawer::drawBox(const Point3F &a, const Point3F &b, const ColorF &color) +{ + if(isFrozen || !isDrawing) + return; + + DebugPrim *n = mPrimChunker.alloc(); + + n->useZ = true; + n->dieTime = 0; + n->a = a; + n->b = b; + n->color = color; + n->type = DebugPrim::Box; + + n->next = mHead; + mHead = n; +} + +void DebugDrawer::drawLine(const Point3F &a, const Point3F &b, const ColorF &color) +{ + if(isFrozen || !isDrawing) + return; + + DebugPrim *n = mPrimChunker.alloc(); + + n->useZ = true; + n->dieTime = 0; + n->a = a; + n->b = b; + n->color = color; + n->type = DebugPrim::Line; + + n->next = mHead; + mHead = n; +} + +void DebugDrawer::drawTri(const Point3F &a, const Point3F &b, const Point3F &c, const ColorF &color) +{ + if(isFrozen || !isDrawing) + return; + + DebugPrim *n = mPrimChunker.alloc(); + + n->useZ = true; + n->dieTime = 0; + n->a = a; + n->b = b; + n->c = c; + n->color = color; + n->type = DebugPrim::Tri; + + n->next = mHead; + mHead = n; +} + +void DebugDrawer::drawPolyhedron( const AnyPolyhedron& polyhedron, const ColorF& color ) +{ + const PolyhedronData::Edge* edges = polyhedron.getEdges(); + const Point3F* points = polyhedron.getPoints(); + const U32 numEdges = polyhedron.getNumEdges(); + + for( U32 i = 0; i < numEdges; ++ i ) + { + const PolyhedronData::Edge& edge = edges[ i ]; + drawLine( points[ edge.vertex[ 0 ] ], points[ edge.vertex[ 1 ] ], color ); + } +} + +void DebugDrawer::drawPolyhedronDebugInfo( const AnyPolyhedron& polyhedron, const MatrixF& transform, const Point3F& scale ) +{ + Point3F center = polyhedron.getCenterPoint(); + center.convolve( scale ); + transform.mulP( center ); + + // Render plane indices and normals. + + const U32 numPlanes = polyhedron.getNumPlanes(); + for( U32 i = 0; i < numPlanes; ++ i ) + { + const AnyPolyhedron::PlaneType& plane = polyhedron.getPlanes()[ i ]; + + Point3F planePos = plane.getPosition(); + planePos.convolve( scale ); + transform.mulP( planePos ); + + Point3F normal = plane.getNormal(); + transform.mulV( normal ); + + drawText( planePos, String::ToString( i ), ColorI::BLACK ); + drawLine( planePos, planePos + normal, ColorI::GREEN ); + } + + // Render edge indices and direction indicators. + + const U32 numEdges = polyhedron.getNumEdges(); + for( U32 i = 0; i < numEdges; ++ i ) + { + const AnyPolyhedron::EdgeType& edge = polyhedron.getEdges()[ i ]; + + Point3F v1 = polyhedron.getPoints()[ edge.vertex[ 0 ] ]; + Point3F v2 = polyhedron.getPoints()[ edge.vertex[ 1 ] ]; + + v1.convolve( scale ); + v2.convolve( scale ); + transform.mulP( v1 ); + transform.mulP( v2 ); + + const Point3F midPoint = v1 + ( v2 - v1 ) / 2.f; + + drawText( midPoint, String::ToString( "%i (%i, %i)", i, edge.face[ 0 ], edge.face[ 1 ] ), ColorI::WHITE ); + + // Push out the midpoint away from the center to place the direction indicator. + + Point3F pushDir = midPoint - center; + pushDir.normalize(); + const Point3F dirPoint = midPoint + pushDir; + const Point3F lineDir = ( v2 - v1 ) / 2.f; + + drawLine( dirPoint, dirPoint + lineDir, ColorI::RED ); + } + + // Render point indices and coordinates. + + const U32 numPoints = polyhedron.getNumPoints(); + for( U32 i = 0; i < numPoints; ++ i ) + { + Point3F p = polyhedron.getPoints()[ i ]; + + p.convolve( scale ); + transform.mulP( p ); + + drawText( p, String::ToString( "%i: (%.2f, %.2f, %.2f)", i, p.x, p.y, p.z ), ColorF::WHITE ); + } +} + +void DebugDrawer::drawText(const Point3F& pos, const String& text, const ColorF &color) +{ + if(isFrozen || !isDrawing) + return; + + DebugPrim *n = mPrimChunker.alloc(); + + n->useZ = false; + n->dieTime = 0; + n->a = pos; + n->color = color; + dStrncpy(n->mText, text.c_str(), 256); + n->type = DebugPrim::Text; + + n->next = mHead; + mHead = n; +} + +void DebugDrawer::setLastTTL(U32 ms) +{ + AssertFatal(mHead, "Tried to set last with nothing in the list!"); + if (ms != U32_MAX) + mHead->dieTime = Sim::getCurrentTime() + ms; + else + mHead->dieTime = U32_MAX; +} + +void DebugDrawer::setLastZTest(bool enabled) +{ + AssertFatal(mHead, "Tried to set last with nothing in the list!"); + mHead->useZ = enabled; +} + +DefineEngineMethod( DebugDrawer, drawLine, void, ( Point3F a, Point3F b, ColorF color ), ( ColorF::WHITE ), + "Draws a line primitive between two 3d points." ) +{ + object->drawLine( a, b, color ); +} + +DefineEngineMethod( DebugDrawer, drawBox, void, ( Point3F a, Point3F b, ColorF color ), ( ColorF::WHITE ), + "Draws an axis aligned box primitive within the two 3d points." ) +{ + object->drawBox( a, b, color ); +} + +DefineEngineMethod( DebugDrawer, setLastTTL, void, ( U32 ms ),, + "Sets the \"time to live\" (TTL) for the last rendered primitive." ) +{ + object->setLastTTL( ms ); +} + +DefineEngineMethod( DebugDrawer, setLastZTest, void, ( bool enabled ),, + "Sets the z buffer reading state for the last rendered primitive." ) +{ + object->setLastZTest( enabled ); +} + +DefineEngineMethod( DebugDrawer, toggleFreeze, void, (),, + "Toggles freeze mode which keeps the currently rendered primitives from expiring." ) +{ + object->toggleFreeze(); +} + +DefineEngineMethod( DebugDrawer, toggleDrawing, void, (),, + "Toggles the rendering of DebugDrawer primitives." ) +{ + object->toggleDrawing(); +} + diff --git a/Engine/source/gfx/sim/debugDraw.h b/Engine/source/gfx/sim/debugDraw.h new file mode 100644 index 000000000..b1ab55970 --- /dev/null +++ b/Engine/source/gfx/sim/debugDraw.h @@ -0,0 +1,197 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _DEBUGDRAW_H_ +#define _DEBUGDRAW_H_ + +#ifndef _SIMOBJECT_H_ +#include "console/simObject.h" +#endif + +#ifndef _GFXDEVICE_H_ +#include "gfx/gfxDevice.h" +#endif + +#ifndef _PRIMBUILDER_H_ +#include "gfx/primBuilder.h" +#endif + +#ifndef _GFONT_H_ +#include "gfx/gFont.h" +#endif + +#ifndef _DATACHUNKER_H_ +#include "core/dataChunker.h" +#endif + +#ifndef _MPOLYHEDRON_H_ +#include "math/mPolyhedron.h" +#endif + + +class GFont; + + +// We enable the debug drawer for non-shipping +// builds.... you better be using shipping builds +// for your final release. +#ifndef TORQUE_SHIPPING +#define ENABLE_DEBUGDRAW +#endif + + +/// Debug output class. +/// +/// This class provides you with a flexible means of drawing debug output. It is +/// often useful when debugging collision code or complex 3d algorithms to have +/// them draw debug information, like culling hulls or bounding volumes, normals, +/// simple lines, and so forth. In TGE1.2, which was based directly on a simple +/// OpenGL rendering layer, it was a simple matter to do debug rendering directly +/// inline. +/// +/// Unfortunately, this doesn't hold true with more complex rendering scenarios, +/// where render modes and targets may be in abritrary states. In addition, it is +/// often useful to be able to freeze frame debug information for closer inspection. +/// +/// Therefore, Torque provides a global DebugDrawer instance, called gDebugDraw, which +/// you can use to draw debug information. It exposes a number of methods for drawing +/// a variety of debug primitives, including lines, triangles and boxes. +/// Internally, DebugDrawer maintains a list of active debug primitives, and draws the +/// contents of the list after each frame is done rendering. This way, you can be +/// assured that your debug rendering won't interfere with TSE's various effect +/// rendering passes or render-to-target calls. +/// +/// The DebugDrawer can also be used for more interesting uses, like freezing its +/// primitive list so you can look at a situation more closely, or dumping the +/// primitive list to disk for closer analysis. +/// +/// DebugDrawer is accessible by script under the name DebugDrawer, and by C++ under +/// the symbol gDebugDraw. There are a variety of methods available for drawing +/// different sorts of output; see the class reference for more information. +/// +/// DebugDrawer works solely in worldspace. Primitives are rendered with cull mode of +/// none. +/// +class DebugDrawer : public SimObject +{ +public: + DECLARE_CONOBJECT(DebugDrawer); + + DebugDrawer(); + ~DebugDrawer(); + + static DebugDrawer* get(); + + /// Called at engine init to set up the global debug draw object. + static void init(); + + /// Called globally to render debug draw state. Also does state updates. + void render(); + + void toggleFreeze() { shouldToggleFreeze = true; }; + void toggleDrawing() + { +#ifdef ENABLE_DEBUGDRAW + isDrawing = !isDrawing; +#endif + }; + + + /// @name ddrawmeth Debug Draw Methods + /// + /// @{ + + void drawBox(const Point3F &a, const Point3F &b, const ColorF &color = ColorF(1.0f,1.0f,1.0f)); + void drawLine(const Point3F &a, const Point3F &b, const ColorF &color = ColorF(1.0f,1.0f,1.0f)); + void drawTri(const Point3F &a, const Point3F &b, const Point3F &c, const ColorF &color = ColorF(1.0f,1.0f,1.0f)); + void drawText(const Point3F& pos, const String& text, const ColorF &color = ColorF(1.0f,1.0f,1.0f)); + + /// Render a wireframe view of the given polyhedron. + void drawPolyhedron( const AnyPolyhedron& polyhedron, const ColorF& color = ColorF( 1.f, 1.f, 1.f ) ); + + /// Render the plane indices, edge indices, edge direction indicators, and point coordinates + /// of the given polyhedron for debugging. + /// + /// Green lines are plane normals. Red lines point from edge midpoints along the edge direction (i.e. to the + /// second vertex). This shows if the orientation is correct to yield CW ordering for face[0]. Indices and + /// coordinates of vertices are shown in white. Plane indices are rendered in black. Edge indices and their + /// plane indices are rendered in white. + void drawPolyhedronDebugInfo( const AnyPolyhedron& polyhedron, const MatrixF& transform, const Point3F& scale ); + + /// Set the TTL for the last item we entered... + /// + /// Primitives default to lasting one frame (ie, ttl=0) + enum { + DD_INFINITE = U32_MAX + }; + // How long should this primitive be draw for, 0 = one frame, DD_INFINITE = draw forever + void setLastTTL(U32 ms); + + /// Disable/enable z testing on the last primitive. + /// + /// Primitives default to z testing on. + void setLastZTest(bool enabled); + + /// @} +private: + typedef SimObject Parent; + + static DebugDrawer* sgDebugDrawer; + + struct DebugPrim + { + /// Color used for this primitive. + ColorF color; + + /// Points used to store positional data. Exact semantics determined by type. + Point3F a, b, c; + enum { + Tri, + Box, + Line, + Text + } type; ///< Type of the primitive. The meanings of a,b,c are determined by this. + + SimTime dieTime; ///< Time at which we should remove this from the list. + bool useZ; ///< If true, do z-checks for this primitive. + char mText[256]; // Text to display + + DebugPrim *next; + }; + + + FreeListChunker mPrimChunker; + DebugPrim *mHead; + + bool isFrozen; + bool shouldToggleFreeze; + bool isDrawing; + + GFXStateBlockRef mRenderZOffSB; + GFXStateBlockRef mRenderZOnSB; + + Resource mFont; + + void setupStateBlocks(); +}; + +#endif // _DEBUGDRAW_H_ diff --git a/Engine/source/gfx/sim/gfxStateBlockData.cpp b/Engine/source/gfx/sim/gfxStateBlockData.cpp new file mode 100644 index 000000000..3e2792bbc --- /dev/null +++ b/Engine/source/gfx/sim/gfxStateBlockData.cpp @@ -0,0 +1,362 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/sim/gfxStateBlockData.h" + +#include "console/consoleTypes.h" +#include "gfx/gfxStringEnumTranslate.h" + + +IMPLEMENT_CONOBJECT( GFXStateBlockData ); + +ConsoleDocClass( GFXStateBlockData, + "@brief A state block description for rendering.\n\n" + "This object is used with ShaderData in CustomMaterial and PostEffect to define the " + "render state.\n" + "@tsexample\n" + "singleton GFXStateBlockData( PFX_DOFDownSampleStateBlock )\n" + "{\n" + " zDefined = true;\n" + " zEnable = false;\n" + " zWriteEnable = false;\n" + "\n" + " samplersDefined = true;\n" + " samplerStates[0] = SamplerClampLinear;\n" + " samplerStates[1] = SamplerClampPoint;\n" + "\n" + " // Copy the clamped linear sampler, but change\n" + " // the u coord to wrap for this special case.\n" + " samplerStates[2] = new GFXSamplerStateData( : SamplerClampLinear )\n" + " {\n" + " addressModeU = GFXAddressWrap;\n" + " };\n" + "};\n" + "@endtsexample\n" + "@note The 'xxxxDefined' fields are used to know what groups of fields are modified " + "when combining multiple state blocks in material processing. You should take care to " + "enable the right ones when setting values.\n" + "@ingroup GFX\n" ); + +GFXStateBlockData::GFXStateBlockData() +{ + for (U32 i = 0; i < TEXTURE_STAGE_COUNT; i++) + mSamplerStates[i] = NULL; +} + +void GFXStateBlockData::initPersistFields() +{ + addGroup( "Alpha Blending" ); + + addField( "blendDefined", TypeBool, Offset(mState.blendDefined, GFXStateBlockData), + "Set to true if the alpha blend state is not all defaults." ); + + addField( "blendEnable", TypeBool, Offset(mState.blendEnable, GFXStateBlockData), + "Enables alpha blending. The default is false." ); + + addField( "blendSrc", TypeGFXBlend, Offset(mState.blendSrc, GFXStateBlockData), + "The source blend state. The default is GFXBlendOne." ); + + addField("blendDest", TypeGFXBlend, Offset(mState.blendDest, GFXStateBlockData), + "The destination blend state. The default is GFXBlendZero." ); + + addField("blendOp", TypeGFXBlendOp, Offset(mState.blendOp, GFXStateBlockData), + "The arithmetic operation applied to alpha blending. The default is GFXBlendOpAdd." ); + + endGroup( "Alpha Blending" ); + + + addGroup( "Separate Alpha Blending" ); + + addField( "separateAlphaBlendDefined", TypeBool, Offset(mState.separateAlphaBlendDefined, GFXStateBlockData), + "Set to true if the seperate alpha blend state is not all defaults." ); + + addField( "separateAlphaBlendEnable", TypeBool, Offset(mState.separateAlphaBlendEnable, GFXStateBlockData), + "Enables the separate blend mode for the alpha channel. The default is false." ); + + addField( "separateAlphaBlendSrc", TypeGFXBlend, Offset(mState.separateAlphaBlendSrc, GFXStateBlockData), + "The source blend state. The default is GFXBlendOne." ); + + addField( "separateAlphaBlendDest", TypeGFXBlend, Offset(mState.separateAlphaBlendDest, GFXStateBlockData), + "The destination blend state. The default is GFXBlendZero." ); + + addField( "separateAlphaBlendOp", TypeGFXBlendOp, Offset(mState.separateAlphaBlendOp, GFXStateBlockData), + "The arithmetic operation applied to separate alpha blending. The default is GFXBlendOpAdd." ); + + endGroup( "Separate Alpha Blending" ); + + + addGroup( "Alpha Test" ); + + addField( "alphaDefined", TypeBool, Offset(mState.alphaDefined, GFXStateBlockData), + "Set to true if the alpha test state is not all defaults." ); + + addField( "alphaTestEnable", TypeBool, Offset(mState.alphaTestEnable, GFXStateBlockData), + "Enables per-pixel alpha testing. The default is false." ); + + addField( "alphaTestFunc", TypeGFXCmpFunc, Offset(mState.alphaTestFunc, GFXStateBlockData), + "The test function used to accept or reject a pixel based on its alpha value. The default is GFXCmpGreaterEqual." ); + + addField( "alphaTestRef", TypeS32, Offset(mState.alphaTestRef, GFXStateBlockData), + "The reference alpha value against which pixels are tested. The default is zero." ); + + endGroup( "Alpha Test" ); + + + addGroup( "Color Write" ); + + addField( "colorWriteDefined", TypeBool, Offset(mState.colorWriteDefined, GFXStateBlockData), + "Set to true if the color write state is not all defaults." ); + + addField( "colorWriteRed", TypeBool, Offset(mState.colorWriteRed, GFXStateBlockData), + "Enables red channel writes. The default is true." ); + + addField( "colorWriteBlue", TypeBool, Offset(mState.colorWriteBlue, GFXStateBlockData), + "Enables blue channel writes. The default is true." ); + + addField( "colorWriteGreen", TypeBool, Offset(mState.colorWriteGreen, GFXStateBlockData), + "Enables green channel writes. The default is true." ); + + addField( "colorWriteAlpha", TypeBool, Offset(mState.colorWriteAlpha, GFXStateBlockData), + "Enables alpha channel writes. The default is true." ); + + endGroup( "Color Write" ); + + + addGroup( "Culling" ); + + addField("cullDefined", TypeBool, Offset(mState.cullDefined, GFXStateBlockData), + "Set to true if the culling state is not all defaults." ); + + addField("cullMode", TypeGFXCullMode, Offset(mState.cullMode, GFXStateBlockData), + "Defines how back facing triangles are culled if at all. The default is GFXCullCCW." ); + + endGroup( "Culling" ); + + + addGroup( "Depth" ); + + addField( "zDefined", TypeBool, Offset(mState.zDefined, GFXStateBlockData), + "Set to true if the depth state is not all defaults." ); + + addField( "zEnable", TypeBool, Offset(mState.zEnable, GFXStateBlockData), + "Enables z-buffer reads. The default is true." ); + + addField( "zWriteEnable", TypeBool, Offset(mState.zWriteEnable, GFXStateBlockData), + "Enables z-buffer writes. The default is true." ); + + addField( "zFunc", TypeGFXCmpFunc, Offset(mState.zFunc, GFXStateBlockData), + "The depth comparision function which a pixel must pass to be written to the z-buffer. The default is GFXCmpLessEqual." ); + + addField( "zBias", TypeF32, Offset(mState.zBias, GFXStateBlockData), + "A floating-point bias used when comparing depth values. The default is zero." ); + + addField( "zSlopeBias", TypeF32, Offset(mState.zSlopeBias, GFXStateBlockData), + "An additional floating-point bias based on the maximum depth slop of the triangle being rendered. The default is zero." ); + + endGroup( "Depth" ); + + + addGroup( "Stencil" ); + + addField( "stencilDefined", TypeBool, Offset(mState.stencilDefined, GFXStateBlockData), + "Set to true if the stencil state is not all defaults." ); + + addField( "stencilEnable", TypeBool, Offset(mState.stencilEnable, GFXStateBlockData), + "Enables stenciling. The default is false." ); + + addField( "stencilFailOp", TypeGFXStencilOp, Offset(mState.stencilFailOp, GFXStateBlockData), + "The stencil operation to perform if the stencil test fails. The default is GFXStencilOpKeep." ); + + addField( "stencilZFailOp", TypeGFXStencilOp, Offset(mState.stencilZFailOp, GFXStateBlockData), + "The stencil operation to perform if the stencil test passes and the depth test fails. The default is GFXStencilOpKeep." ); + + addField( "stencilPassOp", TypeGFXStencilOp, Offset(mState.stencilPassOp, GFXStateBlockData), + "The stencil operation to perform if both the stencil and the depth tests pass. The default is GFXStencilOpKeep." ); + + addField( "stencilFunc", TypeGFXCmpFunc, Offset(mState.stencilFunc, GFXStateBlockData), + "The comparison function to test the reference value to a stencil buffer entry. The default is GFXCmpNever." ); + + addField( "stencilRef", TypeS32, Offset(mState.stencilRef, GFXStateBlockData), + "The reference value for the stencil test. The default is zero." ); + + addField( "stencilMask", TypeS32, Offset(mState.stencilMask, GFXStateBlockData), + "The mask applied to the reference value and each stencil buffer entry to determine the significant bits for the stencil test. The default is 0xFFFFFFFF." ); + + addField( "stencilWriteMask", TypeS32, Offset(mState.stencilWriteMask, GFXStateBlockData), + "The write mask applied to values written into the stencil buffer. The default is 0xFFFFFFFF." ); + + endGroup( "Stencil" ); + + + addGroup( "Fixed Function" ); + + addField( "ffLighting", TypeBool, Offset(mState.ffLighting, GFXStateBlockData), + "Enables fixed function lighting when rendering without a shader on geometry with vertex normals. The default is false." ); + + addField( "vertexColorEnable", TypeBool, Offset(mState.vertexColorEnable, GFXStateBlockData), + "Enables fixed function vertex coloring when rendering without a shader. The default is false." ); + + endGroup( "Fixed Function" ); + + + addGroup( "Sampler States" ); + + addField( "samplersDefined", TypeBool, Offset(mState.samplersDefined, GFXStateBlockData), + "Set to true if the sampler states are not all defaults." ); + + addField( "samplerStates", TYPEID(), Offset(mSamplerStates, GFXStateBlockData), TEXTURE_STAGE_COUNT, + "The array of texture sampler states.\n" + "@note Not all graphics devices support 16 samplers. In general " + "all systems support 4 samplers with most modern cards doing 8." ); + + addField( "textureFactor", TypeColorI, Offset(mState.textureFactor, GFXStateBlockData), + "The color used for multiple-texture blending with the GFXTATFactor texture-blending argument or " + "the GFXTOPBlendFactorAlpha texture-blending operation. The default is opaque white (255, 255, 255, 255)." ); + + endGroup( "Sampler States" ); + + Parent::initPersistFields(); +} + +bool GFXStateBlockData::onAdd() +{ + if (!Parent::onAdd()) + return false; + + for (U32 i = 0; i < TEXTURE_STAGE_COUNT; i++) + { + if (mSamplerStates[i]) + mSamplerStates[i]->setSamplerState(mState.samplers[i]); + } + return true; +} + + +IMPLEMENT_CONOBJECT( GFXSamplerStateData ); + +ConsoleDocClass( GFXSamplerStateData, + "@brief A sampler state used by GFXStateBlockData.\n\n" + "The samplers define how a texture will be sampled when used from the shader " + "or fixed function device\n" + "@tsexample\n" + "singleton GFXSamplerStateData(SamplerClampLinear)\n" + "{\n" + " textureColorOp = GFXTOPModulate;\n" + " addressModeU = GFXAddressClamp;\n" + " addressModeV = GFXAddressClamp;\n" + " addressModeW = GFXAddressClamp;\n" + " magFilter = GFXTextureFilterLinear;\n" + " minFilter = GFXTextureFilterLinear;\n" + " mipFilter = GFXTextureFilterLinear;\n" + "};\n" + "@endtsexample\n" + "There are a few predefined samplers in the core scripts which you can use with " + "GFXStateBlockData for the most common rendering cases:\n" + " - SamplerClampLinear\n" + " - SamplerClampPoint\n" + " - SamplerWrapLinear\n" + " - SamplerWrapPoint\n" + "\n" + "@see GFXStateBlockData\n" + "@ingroup GFX\n" ); + +void GFXSamplerStateData::initPersistFields() +{ + Parent::initPersistFields(); + + addGroup( "Color Op" ); + + addField("textureColorOp", TypeGFXTextureOp, Offset(mState.textureColorOp, GFXSamplerStateData), + "The texture color blending operation. The default value is GFXTOPDisable which disables the sampler." ); + + addField("colorArg1", TYPEID< GFXTextureArgument >(), Offset(mState.colorArg1, GFXSamplerStateData), + "The first color argument for the texture stage. The default value is GFXTACurrent." ); + + addField("colorArg2", TYPEID< GFXTextureArgument >(), Offset(mState.colorArg2, GFXSamplerStateData), + "The second color argument for the texture stage. The default value is GFXTATexture." ); + + addField("colorArg3", TYPEID< GFXTextureArgument >(), Offset(mState.colorArg3, GFXSamplerStateData), + "The third color argument for triadic operations (multiply, add, and linearly interpolate). The default value is GFXTACurrent." ); + + endGroup( "Color Op" ); + + addGroup( "Alpha Op" ); + + addField("alphaOp", TypeGFXTextureOp, Offset(mState.alphaOp, GFXSamplerStateData), + "The texture alpha blending operation. The default value is GFXTOPModulate." ); + + addField("alphaArg1", TYPEID< GFXTextureArgument >(), Offset(mState.alphaArg1, GFXSamplerStateData), + "The first alpha argument for the texture stage. The default value is GFXTATexture." ); + + addField("alphaArg2", TYPEID< GFXTextureArgument >(), Offset(mState.alphaArg2, GFXSamplerStateData), + "The second alpha argument for the texture stage. The default value is GFXTADiffuse." ); + + addField("alphaArg3", TYPEID< GFXTextureArgument >(), Offset(mState.alphaArg3, GFXSamplerStateData), + "The third alpha channel selector operand for triadic operations (multiply, add, and linearly interpolate). The default value is GFXTACurrent." ); + + endGroup( "Alpha Op" ); + + addGroup( "Address Mode" ); + + addField("addressModeU", TypeGFXTextureAddressMode, Offset(mState.addressModeU, GFXSamplerStateData), + "The texture address mode for the u coordinate. The default is GFXAddressWrap." ); + + addField("addressModeV", TypeGFXTextureAddressMode, Offset(mState.addressModeV, GFXSamplerStateData), + "The texture address mode for the v coordinate. The default is GFXAddressWrap." ); + + addField("addressModeW", TypeGFXTextureAddressMode, Offset(mState.addressModeW, GFXSamplerStateData), + "The texture address mode for the w coordinate. The default is GFXAddressWrap." ); + + endGroup( "Address Mode" ); + + addGroup( "Filter State" ); + + addField("magFilter", TypeGFXTextureFilterType, Offset(mState.magFilter, GFXSamplerStateData), + "The texture magnification filter. The default is GFXTextureFilterLinear." ); + + addField("minFilter", TypeGFXTextureFilterType, Offset(mState.minFilter, GFXSamplerStateData), + "The texture minification filter. The default is GFXTextureFilterLinear." ); + + addField("mipFilter", TypeGFXTextureFilterType, Offset(mState.mipFilter, GFXSamplerStateData), + "The texture mipmap filter used during minification. The default is GFXTextureFilterLinear." ); + + addField("mipLODBias", TypeF32, Offset(mState.mipLODBias, GFXSamplerStateData), + "The mipmap level of detail bias. The default value is zero." ); + + addField("maxAnisotropy", TypeS32, Offset(mState.maxAnisotropy, GFXSamplerStateData), + "The maximum texture anisotropy. The default value is 1." ); + + endGroup( "Filter State" ); + + addField("textureTransform", TypeGFXTextureTransformFlags, Offset(mState.textureTransform, GFXSamplerStateData), + "Sets the texture transform state. The default is GFXTTFFDisable." ); + + addField("resultArg", TypeGFXTextureArgument, Offset(mState.resultArg, GFXSamplerStateData), + "The selection of the destination register for the result of this stage. The default is GFXTACurrent." ); +} + +/// Copies the data of this object into desc +void GFXSamplerStateData::setSamplerState(GFXSamplerStateDesc& desc) +{ + desc = mState; +} diff --git a/Engine/source/gfx/sim/gfxStateBlockData.h b/Engine/source/gfx/sim/gfxStateBlockData.h new file mode 100644 index 000000000..b8c6f7ea4 --- /dev/null +++ b/Engine/source/gfx/sim/gfxStateBlockData.h @@ -0,0 +1,71 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef __GFXSTATEBLOCKDATA_H_ +#define __GFXSTATEBLOCKDATA_H_ + +#ifndef _GFXSTATEBLOCK_H_ +#include "gfx/gfxStateBlock.h" +#endif +#ifndef _SIMOBJECT_H_ +#include "console/simObject.h" +#endif + + +class GFXSamplerStateData; + +/// Allows definition of render state via script, basically wraps a GFXStateBlockDesc +class GFXStateBlockData : public SimObject +{ + typedef SimObject Parent; + + GFXStateBlockDesc mState; + GFXSamplerStateData* mSamplerStates[TEXTURE_STAGE_COUNT]; +public: + GFXStateBlockData(); + + // SimObject + virtual bool onAdd(); + static void initPersistFields(); + + // GFXStateBlockData + const GFXStateBlockDesc getState() const { return mState; } + + DECLARE_CONOBJECT(GFXStateBlockData); +}; + +/// Allows definition of sampler state via script, basically wraps a GFXSamplerStateDesc +class GFXSamplerStateData : public SimObject +{ + typedef SimObject Parent; + GFXSamplerStateDesc mState; +public: + // SimObject + static void initPersistFields(); + + /// Copies the data of this object into desc + void setSamplerState(GFXSamplerStateDesc& desc); + + DECLARE_CONOBJECT(GFXSamplerStateData); +}; + + +#endif // __GFXSTATEBLOCKDATA_H_ diff --git a/Engine/source/gfx/test/stanfordBunny.cpp b/Engine/source/gfx/test/stanfordBunny.cpp new file mode 100644 index 000000000..90a79fae6 --- /dev/null +++ b/Engine/source/gfx/test/stanfordBunny.cpp @@ -0,0 +1,13623 @@ +/* + + File: stanfordbunny.c + + Abstract: See + + Version: 1.0 + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple + Computer, Inc. ("Apple") in consideration of your agreement to the + following terms, and your use, installation, modification or + redistribution of this Apple software constitutes acceptance of these + terms. If you do not agree with these terms, please do not use, + install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and + subject to these terms, Apple grants you a personal, non-exclusive + license, under Apple's copyrights in this original Apple software (the + "Apple Software"), to use, reproduce, modify and redistribute the Apple + Software, with or without modifications, in source and/or binary forms; + provided that if you redistribute the Apple Software in its entirety and + without modifications, you must retain this notice and the following + text and disclaimers in all such redistributions of the Apple Software. + Neither the name, trademarks, service marks or logos of Apple Computer, + Inc. may be used to endorse or promote products derived from the Apple + Software without specific prior written permission from Apple. Except + as expressly stated in this notice, no other rights or licenses, express + or implied, are granted by Apple herein, including but not limited to + any patent rights that may be infringed by your derivative works or by + other works in which the Apple Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE + MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION + THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND + OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, + MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED + AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), + STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + Copyright © 2006 Apple Computer, Inc., All Rights Reserved + +*/ + +/* + stanfordbunny.c + + This file contains the data and associated APIs to create a display list + of a complex model, the "Stanford Bunny" a reconstructed 3D scan of a Bunny. + + It provides GenStanfordBunnyWireList() and GenStanfordBunnySolidList() + to construct a display list and return it's ID using 3 arrays of data: + face_indicies, vertices and normals that you see below. +*/ + + + +/* + Stanford Bunny Data (culled) + + See for original data +*/ + +#include "gfx/gfxDevice.h" +#include "gfx/gfxVertexBuffer.h" +#include "gfx/gfxStructs.h" + +// 8146 Verticies +// 8127 Normals +// 16301 Triangles + +short face_indicies[16301][6] = { +// surface + {1538,2410,1101 ,0,1,2 }, {713,6196,101 ,3,4,5 }, {696,704,101 ,6,7,5 }, + {1192,117,1113 ,8,9,10 }, {1260,173,1857 ,11,12,13 }, {540,1192,1113 ,14,8,10 }, + {704,696,398 ,7,6,15 }, {398,696,458 ,15,6,16 }, {1605,772,4540 ,17,18,19 }, + {1192,540,1857 ,8,14,13 }, {772,713,809 ,18,3,20 }, {713,173,1260 ,3,12,11 }, + {173,1192,1857 ,12,8,13 }, {809,713,1260 ,20,3,11 }, {804,4128,4120 ,21,22,23 }, + {1133,804,4120 ,24,21,23 }, {4128,4140,4531 ,22,25,26 }, {4140,1534,4136 ,25,27,28 }, + {4531,4140,4136 ,26,25,28 }, {4136,1534,1254 ,28,27,29 }, {4515,1609,4485 ,30,31,32 }, + {710,1396,889 ,33,34,35 }, {1245,3618,4204 ,36,37,38 }, {2046,1905,768 ,39,40,41 }, + {2453,5699,5698 ,42,43,44 }, {1254,2046,768 ,29,39,41 }, {768,1905,1393 ,41,40,45 }, + {1393,1490,4112 ,45,46,47 }, {5657,5658,5680 ,48,49,50 }, {1490,4281,839 ,46,51,52 }, + {4112,1490,839 ,47,46,52 }, {839,4281,1330 ,52,51,53 }, {6897,8079,3286 ,54,55,56 }, + {920,4291,1937 ,57,58,59 }, {321,1132,1222 ,60,61,62 }, {5338,4915,4914 ,63,64,65 }, + {1093,4537,840 ,66,67,68 }, {4537,1697,840 ,67,69,68 }, {705,659,4158 ,70,71,72 }, + {7248,7323,7322 ,73,74,75 }, {5413,5103,798 ,76,77,78 }, {5244,5289,5266 ,79,80,81 }, + {4197,4217,4258 ,82,83,84 }, {277,360,214 ,85,86,87 }, {4498,4536,4535 ,88,89,90 }, + {4497,4498,4535 ,91,88,90 }, {231,654,775 ,92,93,94 }, {4535,4536,792 ,90,89,95 }, + {8065,6577,8094 ,96,97,98 }, {1595,703,4145 ,99,100,101 }, {5203,1089,4369 ,102,103,104 }, + {2698,5203,4369 ,105,102,104 }, {1653,4491,226 ,106,107,108 }, + {2941,2940,2907 ,109,110,111 }, {6413,6414,3340 ,112,113,114 }, + {5231,3276,922 ,115,116,117 }, {1628,792,883 ,118,95,119 }, {4273,2042,542 ,120,121,122 }, + {4091,4126,4125 ,123,124,125 }, {4077,4091,4125 ,126,123,125 }, + {5618,103,2011 ,127,128,129 }, {2026,934,4251 ,130,131,132 }, + {4123,4173,4172 ,133,134,135 }, {3397,4123,4172 ,136,133,135 }, + {4122,4123,3397 ,137,133,136 }, {82,284,80 ,138,139,140 }, {5113,6120,5845 ,141,142,143 }, + {803,696,101 ,144,6,5 }, {139,1420,2137 ,145,146,147 }, {1348,1310,737 ,148,149,150 }, + {1423,196,396 ,151,152,153 }, {4403,4356,4432 ,154,155,156 }, + {143,781,142 ,157,158,159 }, {1148,1925,4132 ,160,161,162 }, + {1031,804,1133 ,163,21,24 }, {458,1031,1133 ,16,163,24 }, {1318,2137,1420 ,164,147,146 }, + {4255,4328,4301 ,165,166,167 }, {715,174,1652 ,168,169,170 }, + {5483,8116,5261 ,171,172,173 }, {1468,1451,542 ,174,175,122 }, + {2206,2219,4978 ,176,177,178 }, {804,4140,4128 ,21,25,22 }, {1222,1132,4521 ,62,61,179 }, + {4521,1318,606 ,179,164,180 }, {5162,5618,1443 ,181,127,182 }, + {3221,5261,8119 ,183,173,184 }, {4067,4077,4071 ,185,126,186 }, + {4526,1651,4515 ,187,188,30 }, {4526,891,1651 ,187,189,188 }, + {6320,6345,3533 ,190,191,192 }, {4140,779,1534 ,25,193,27 }, + {839,1330,1481 ,52,53,194 }, {1534,544,1254 ,27,195,29 }, {139,1318,1420 ,145,164,146 }, + {1842,940,1840 ,196,197,198 }, {5730,5729,3615 ,199,200,201 }, + {174,715,1147 ,169,168,202 }, {1345,1846,1093 ,203,204,66 }, + {4084,1178,4113 ,205,206,207 }, {3397,4172,1257 ,136,135,208 }, + {2042,1468,542 ,121,174,122 }, {787,4130,175 ,209,210,211 }, + {1461,1311,1310 ,212,213,149 }, {1311,457,1310 ,213,214,149 }, + {4211,321,885 ,215,60,216 }, {116,4211,885 ,217,215,216 }, {3761,4326,1463 ,218,219,220 }, + {544,1905,2046 ,195,40,39 }, {1254,544,2046 ,29,195,39 }, {1310,457,229 ,149,214,221 }, + {57,4160,735 ,222,223,224 }, {4029,4214,4019 ,225,226,227 }, + {4367,4421,4390 ,228,229,230 }, {4255,4301,4300 ,165,167,231 }, + {4411,962,1006 ,232,233,234 }, {2707,1335,4446 ,235,236,237 }, + {888,757,120 ,238,239,240 }, {4980,4547,2163 ,241,242,243 }, + {891,1461,200 ,189,212,244 }, {891,200,1651 ,189,244,188 }, {4838,311,24 ,245,246,247 }, + {169,105,1880 ,248,249,250 }, {1393,4179,1490 ,45,251,46 }, {1490,4179,4281 ,46,251,51 }, + {3263,8102,8089 ,252,253,254 }, {700,757,896 ,255,239,256 }, + {4465,4484,4514 ,257,258,259 }, {4202,700,464 ,260,255,261 }, + {3283,2968,1693 ,262,263,264 }, {1609,4514,4484 ,31,259,258 }, + {4485,1609,4484 ,32,31,258 }, {1609,714,4514 ,31,265,259 }, {606,1318,139 ,180,164,145 }, + {1747,979,970 ,266,267,268 }, {4136,1254,399 ,28,29,269 }, {4473,4451,4497 ,270,271,91 }, + {4057,2420,4263 ,272,273,274 }, {1396,770,889 ,34,275,35 }, {4421,4420,4390 ,229,276,230 }, + {1069,4138,1209 ,277,278,279 }, {3948,3670,1877 ,280,281,282 }, + {2707,5162,1335 ,235,181,236 }, {3484,863,1300 ,283,284,285 }, + {4978,5000,6414 ,178,286,113 }, {190,1161,1021 ,287,288,289 }, + {896,757,888 ,256,239,238 }, {1348,737,497 ,148,150,290 }, {639,1348,497 ,291,148,290 }, + {1112,4202,4194 ,292,260,293 }, {3958,3957,3903 ,294,295,296 }, + {64,613,11 ,297,298,299 }, {1344,456,795 ,300,301,302 }, {27,43,545 ,303,304,305 }, + {1033,888,30 ,306,238,307 }, {1023,4224,1033 ,308,309,306 }, + {4194,4202,464 ,293,260,261 }, {497,737,190 ,290,150,287 }, {737,356,190 ,150,310,287 }, + {2872,1245,4204 ,311,36,38 }, {4375,4531,4136 ,312,26,28 }, {1241,241,1389 ,313,314,315 }, + {4128,4531,4375 ,22,26,312 }, {4120,4128,4375 ,23,22,312 }, {709,1833,653 ,316,317,318 }, + {1793,4150,69 ,319,320,321 }, {69,1319,70 ,321,322,323 }, {1792,1375,24 ,324,325,247 }, + {6320,7500,6345 ,190,326,191 }, {1225,57,1245 ,327,222,36 }, + {1257,4172,4214 ,208,135,226 }, {997,190,1021 ,328,287,289 }, + {464,4224,1023 ,261,309,308 }, {3,464,1023 ,329,261,308 }, {714,639,950 ,265,291,330 }, + {714,950,4514 ,265,330,259 }, {7747,7746,7698 ,331,332,333 }, + {652,1296,1383 ,334,335,336 }, {1203,1204,1202 ,337,338,339 }, + {970,6018,1747 ,268,340,266 }, {4430,4451,4473 ,341,271,270 }, + {120,4362,4411 ,240,342,232 }, {7848,7874,7823 ,343,344,345 }, + {467,466,360 ,346,347,86 }, {1259,4095,4179 ,348,349,251 }, {5,413,315 ,350,351,352 }, + {4098,4057,4263 ,353,272,274 }, {1057,1596,1069 ,354,355,277 }, + {971,1057,1069 ,356,354,277 }, {44,412,1793 ,357,358,319 }, {4019,4255,4300 ,227,165,231 }, + {5378,5412,5377 ,359,360,361 }, {1259,4114,4059 ,348,362,363 }, + {4982,5000,4978 ,364,286,178 }, {4334,965,1419 ,365,366,367 }, + {1564,1085,1595 ,368,369,99 }, {4172,4215,4214 ,135,370,226 }, + {1225,2872,2428 ,327,311,371 }, {4110,1633,503 ,372,373,374 }, + {4328,4378,4354 ,166,375,376 }, {513,1343,376 ,377,378,379 }, + {3997,3998,4039 ,380,381,382 }, {8101,8122,8087 ,383,384,385 }, + {5000,6415,6414 ,286,386,113 }, {660,703,1625 ,387,100,388 }, + {805,2352,24 ,389,390,247 }, {1599,799,1194 ,391,392,393 }, {5850,4011,5784 ,394,395,396 }, + {3871,5850,5784 ,397,394,396 }, {2872,1225,1245 ,311,327,36 }, + {1633,513,376 ,373,377,379 }, {3871,5784,878 ,397,396,398 }, + {659,4171,802 ,71,399,400 }, {708,1222,365 ,401,62,402 }, {117,458,1133 ,9,16,24 }, + {4497,4535,4534 ,91,90,403 }, {1967,1966,1903 ,404,405,406 }, + {3259,4091,4068 ,407,123,408 }, {4185,3113,52 ,409,410,411 }, + {8090,8132,3006 ,412,413,414 }, {211,965,4334 ,415,366,365 }, + {4214,4254,4019 ,226,416,227 }, {1023,2045,211 ,308,417,415 }, + {1923,1023,211 ,418,308,415 }, {4043,2798,2956 ,419,420,399 }, + {4535,792,1628 ,90,95,118 }, {398,458,117 ,15,16,9 }, {139,2137,777 ,145,147,421 }, + {4534,4535,1628 ,403,90,118 }, {4411,4362,962 ,232,342,233 }, + {5199,5198,5177 ,422,423,424 }, {1838,4427,363 ,425,426,427 }, + {5213,5233,5212 ,428,429,430 }, {1419,799,1599 ,367,392,391 }, + {211,30,965 ,415,307,366 }, {347,102,2423 ,431,432,433 }, {138,3871,878 ,434,397,398 }, + {4182,4201,4051 ,435,436,437 }, {2313,1222,708 ,438,62,401 }, + {885,2313,708 ,216,438,401 }, {3327,3977,3513 ,439,440,441 }, + {3535,1345,840 ,442,203,68 }, {915,913,916 ,443,444,445 }, {678,634,682 ,446,447,448 }, + {634,346,682 ,447,449,448 }, {7254,6347,7860 ,450,451,452 }, + {8061,8082,8102 ,453,454,253 }, {1595,1085,703 ,99,369,100 }, + {2660,2753,2349 ,455,456,457 }, {2338,2680,4039 ,458,459,382 }, + {4040,2338,4039 ,460,458,382 }, {5307,5306,4393 ,461,462,463 }, + {4537,705,1697 ,67,70,69 }, {6563,8061,8063 ,464,453,465 }, {1305,4446,1300 ,466,237,285 }, + {4312,4311,490 ,467,468,469 }, {4301,4328,4354 ,167,166,376 }, + {467,503,466 ,346,374,347 }, {4051,1933,1695 ,437,470,471 }, + {6859,6860,6911 ,472,473,474 }, {2455,2212,5001 ,475,476,477 }, + {1197,6341,4983 ,478,479,480 }, {5240,5239,5218 ,481,482,483 }, + {705,1346,1697 ,70,484,69 }, {1731,1747,604 ,485,266,486 }, {491,586,678 ,487,488,446 }, + {833,491,678 ,489,487,446 }, {586,679,678 ,488,490,446 }, {679,86,634 ,490,491,447 }, + {678,679,634 ,446,490,447 }, {1485,346,634 ,492,449,447 }, {86,1485,634 ,491,492,447 }, + {1485,496,346 ,492,493,449 }, {591,638,346 ,494,495,449 }, {496,591,346 ,493,494,449 }, + {29,15,638 ,496,497,495 }, {591,29,638 ,494,496,495 }, {29,438,15 ,496,498,497 }, + {745,961,438 ,499,500,498 }, {745,446,961 ,499,501,500 }, {4187,1023,1923 ,502,308,418 }, + {189,1021,4187 ,503,289,502 }, {365,606,139 ,402,180,145 }, {883,792,146 ,119,95,504 }, + {174,1597,892 ,169,505,506 }, {826,1021,189 ,507,289,503 }, {5570,1350,5641 ,508,509,510 }, + {8072,530,3668 ,511,512,513 }, {5699,5727,5726 ,43,514,515 }, + {2927,2926,2882 ,516,517,518 }, {6912,6943,6942 ,519,520,521 }, + {6341,1197,2808 ,479,478,522 }, {536,1305,1300 ,523,466,285 }, + {5178,5199,5177 ,524,422,424 }, {4311,4383,490 ,468,399,469 }, + {174,1422,1597 ,169,525,505 }, {5642,5658,5657 ,526,49,48 }, + {586,86,679 ,488,491,490 }, {1921,446,745 ,527,501,499 }, {1199,39,446 ,528,529,501 }, + {1625,4222,4251 ,388,530,132 }, {1487,304,2680 ,531,532,459 }, + {304,1487,488 ,532,531,533 }, {2150,964,3084 ,534,535,536 }, + {5726,5727,5764 ,515,514,537 }, {803,4,696 ,144,538,6 }, {3511,4029,4019 ,539,225,227 }, + {4,1031,458 ,538,163,16 }, {696,4,458 ,6,538,16 }, {5727,5765,5764 ,514,540,537 }, + {4369,1089,4393 ,104,103,463 }, {4326,3341,3258 ,219,541,542 }, + {4158,659,802 ,72,71,400 }, {5834,5833,5816 ,543,544,545 }, {3008,7051,7029 ,546,547,548 }, + {965,888,799 ,366,238,392 }, {950,1652,4483 ,330,170,549 }, {4514,950,4483 ,259,330,549 }, + {1112,116,885 ,292,217,216 }, {3862,1973,7810 ,550,551,552 }, + {2093,2257,1749 ,553,554,555 }, {466,416,143 ,347,556,157 }, + {811,4954,812 ,557,558,559 }, {92,1921,745 ,560,527,499 }, {92,446,1921 ,560,501,527 }, + {317,344,93 ,561,562,563 }, {93,344,39 ,563,562,529 }, {1626,3253,349 ,564,565,566 }, + {4379,4431,4430 ,567,568,341 }, {4378,4379,4430 ,375,567,341 }, + {4503,779,4140 ,569,193,25 }, {5283,5284,5337 ,570,571,572 }, + {804,4503,4140 ,21,569,25 }, {2070,1367,1244 ,573,574,575 }, + {360,466,214 ,86,347,87 }, {229,116,1112 ,221,217,292 }, {4077,4109,4076 ,126,576,577 }, + {1023,1033,2045 ,308,306,417 }, {314,1419,1599 ,578,367,391 }, + {875,2155,1920 ,579,580,581 }, {4444,4482,4464 ,582,583,584 }, + {30,888,965 ,307,238,366 }, {4900,4545,5578 ,585,586,587 }, {1260,1461,891 ,11,212,189 }, + {809,1260,891 ,20,11,189 }, {1271,4548,2252 ,588,589,590 }, {2246,351,352 ,591,592,593 }, + {6946,6968,6916 ,594,595,596 }, {6555,2093,1749 ,597,553,555 }, + {235,1292,1517 ,598,599,600 }, {1292,1850,1517 ,599,601,600 }, + {1520,586,491 ,602,488,487 }, {251,1520,491 ,603,602,487 }, {1520,2225,586 ,602,604,488 }, + {63,86,586 ,605,491,488 }, {2225,63,586 ,604,605,488 }, {63,244,1485 ,605,606,492 }, + {86,63,1485 ,491,605,492 }, {1485,244,496 ,492,606,493 }, {244,590,591 ,606,607,494 }, + {496,244,591 ,493,606,494 }, {590,1198,29 ,607,608,496 }, {591,590,29 ,494,607,496 }, + {1198,441,438 ,608,609,498 }, {29,1198,438 ,496,608,498 }, {441,956,745 ,609,610,499 }, + {438,441,745 ,498,609,499 }, {745,956,92 ,499,610,560 }, {92,868,446 ,560,611,501 }, + {1247,1199,446 ,612,528,501 }, {868,1247,446 ,611,612,501 }, + {93,39,1199 ,563,529,528 }, {1247,93,1199 ,612,563,528 }, {2150,3084,832 ,534,536,613 }, + {488,2150,832 ,533,534,613 }, {6044,5933,5934 ,614,615,616 }, + {8076,1295,3040 ,617,618,619 }, {779,544,1534 ,193,195,27 }, + {2338,1487,2680 ,458,531,459 }, {1487,2150,488 ,531,534,533 }, + {1905,4179,1393 ,40,251,45 }, {681,683,302 ,620,621,622 }, {1260,1311,1461 ,11,213,212 }, + {5162,1443,1335 ,181,182,236 }, {4482,4525,4513 ,583,623,624 }, + {4334,1419,314 ,365,367,578 }, {950,715,1652 ,330,168,170 }, + {1305,2707,4446 ,466,235,237 }, {120,4411,1006 ,240,232,234 }, + {4545,3621,5578 ,586,625,587 }, {7264,7263,7222 ,626,627,628 }, + {6698,6699,6750 ,629,630,631 }, {1520,534,63 ,602,632,605 }, + {2225,1520,63 ,604,602,605 }, {63,534,244 ,605,632,606 }, {317,93,4539 ,561,563,633 }, + {3084,964,3094 ,536,535,634 }, {1294,874,169 ,635,636,248 }, + {2789,2832,2831 ,637,638,639 }, {4165,87,2477 ,640,641,642 }, + {832,3084,3094 ,613,536,634 }, {4430,4431,4451 ,341,568,271 }, + {348,249,268 ,643,644,645 }, {5618,5616,1443 ,127,646,182 }, + {715,497,1147 ,168,290,202 }, {4468,3673,3473 ,647,648,649 }, + {7746,7745,7697 ,332,650,651 }, {3749,5380,5304 ,652,653,654 }, + {888,120,1006 ,238,240,234 }, {2132,2131,2085 ,655,656,657 }, + {8107,3006,2731 ,658,414,659 }, {5812,5823,7073 ,660,661,662 }, + {153,1243,1292 ,663,664,599 }, {235,153,1292 ,598,663,599 }, + {1292,1243,1850 ,599,664,601 }, {112,251,1850 ,665,603,601 }, + {1243,112,1850 ,664,665,601 }, {1593,1520,251 ,666,602,603 }, + {112,1593,251 ,665,666,603 }, {1593,172,534 ,666,667,632 }, {1520,1593,534 ,602,666,632 }, + {727,726,627 ,668,669,670 }, {590,589,1198 ,607,671,608 }, {1198,357,441 ,608,672,609 }, + {442,310,956 ,673,674,610 }, {441,442,956 ,609,673,610 }, {310,60,92 ,674,675,560 }, + {956,310,92 ,610,674,560 }, {92,60,868 ,560,675,611 }, {60,1788,1247 ,675,676,612 }, + {868,60,1247 ,611,675,612 }, {1788,113,93 ,676,677,563 }, {1247,1788,93 ,612,676,563 }, + {1059,1294,169 ,678,635,248 }, {105,783,1066 ,249,679,680 }, + {141,4206,4186 ,681,682,683 }, {6860,6912,6911 ,473,519,474 }, + {5013,6484,5455 ,684,685,686 }, {964,1294,1059 ,535,635,678 }, + {3094,964,1059 ,634,535,678 }, {128,883,4594 ,687,119,688 }, + {883,128,76 ,119,687,689 }, {3750,1463,3681 ,690,220,691 }, {6094,3373,3300 ,692,693,694 }, + {4179,1490,4281 ,251,46,51 }, {1490,4310,4281 ,46,695,51 }, {1461,1310,1348 ,212,149,148 }, + {700,896,4224 ,255,256,309 }, {1021,1161,3 ,289,288,329 }, {1350,5642,5641 ,509,526,510 }, + {459,211,4334 ,696,415,365 }, {4237,4257,4275 ,697,698,699 }, + {5801,5817,5800 ,700,701,702 }, {4444,4464,4443 ,582,584,703 }, + {6350,6349,6321 ,704,705,706 }, {2045,1033,30 ,417,306,307 }, + {1742,7051,3008 ,707,547,546 }, {7052,3660,3454 ,708,709,710 }, + {6790,6809,6754 ,711,712,713 }, {1593,16,172 ,666,714,667 }, + {3399,4550,4447 ,715,716,717 }, {526,582,625 ,718,719,720 }, + {725,724,625 ,721,722,720 }, {8129,7693,8116 ,723,724,172 }, + {1195,4395,5194 ,725,726,727 }, {3008,3975,3944 ,546,728,729 }, + {129,128,695 ,730,687,731 }, {129,76,128 ,730,689,687 }, {6149,3701,3648 ,732,733,734 }, + {3495,3494,3434 ,735,736,737 }, {8116,7693,8071 ,172,724,738 }, + {195,4350,4098 ,739,740,353 }, {1150,38,110 ,741,742,743 }, {1311,540,457 ,213,14,214 }, + {497,190,997 ,290,287,328 }, {459,4334,4412 ,696,365,744 }, {1750,4894,1698 ,745,746,747 }, + {4412,4334,314 ,744,365,578 }, {4544,6353,6686 ,748,749,750 }, + {5521,8071,8125 ,751,738,752 }, {314,1599,4244 ,578,391,753 }, + {5536,5522,5557 ,754,755,756 }, {5850,3749,4011 ,394,652,395 }, + {1995,2049,2015 ,757,758,759 }, {6299,404,6417 ,760,761,762 }, + {172,16,1743 ,667,714,763 }, {582,583,625 ,719,764,720 }, {783,688,1066 ,679,765,680 }, + {169,874,105 ,248,636,249 }, {874,783,105 ,636,679,249 }, {615,4281,4310 ,766,51,695 }, + {226,1150,110 ,108,741,743 }, {774,1634,683 ,767,768,621 }, {4174,4217,4197 ,769,83,82 }, + {540,4211,116 ,14,215,217 }, {200,1461,1348 ,244,212,148 }, {464,700,4224 ,261,255,309 }, + {1147,497,997 ,202,290,328 }, {103,2030,2011 ,128,770,129 }, + {4429,1923,459 ,771,418,696 }, {1699,1751,1750 ,772,773,745 }, + {1698,1699,1750 ,747,772,745 }, {4587,4588,4589 ,774,775,776 }, + {1811,1885,1810 ,777,778,779 }, {6553,3252,3283 ,780,781,262 }, + {4482,4513,4464 ,583,624,584 }, {976,4964,4965 ,782,783,784 }, + {2050,2049,1995 ,785,758,757 }, {1950,2050,1995 ,786,785,757 }, + {2050,2075,2049 ,785,787,758 }, {2075,2096,2049 ,787,788,758 }, + {3661,2212,2455 ,789,476,475 }, {1331,1243,153 ,790,664,663 }, + {597,1331,153 ,791,790,663 }, {553,112,1243 ,792,665,664 }, {1331,553,1243 ,790,792,664 }, + {553,1727,1593 ,792,793,666 }, {112,553,1593 ,665,792,666 }, + {1727,1743,16 ,793,763,714 }, {1593,1727,16 ,666,793,714 }, {170,1290,1240 ,794,795,796 }, + {1184,999,1188 ,797,798,799 }, {526,527,582 ,718,800,719 }, {583,626,625 ,764,801,720 }, + {3598,3547,6598 ,802,803,804 }, {2680,304,2662 ,459,532,805 }, + {2853,3190,2416 ,806,807,808 }, {3300,5977,6094 ,694,809,692 }, + {60,145,1788 ,675,810,676 }, {310,2694,60 ,674,811,675 }, {8126,6891,3148 ,812,813,814 }, + {7924,7923,7873 ,815,816,817 }, {3007,4486,6070 ,818,819,820 }, + {1065,1340,1450 ,821,822,823 }, {688,1065,1066 ,765,821,680 }, + {3359,3325,3326 ,824,825,826 }, {5178,5177,4630 ,524,424,827 }, + {1980,2007,1979 ,828,829,830 }, {1984,1916,1917 ,831,832,833 }, + {277,214,1992 ,85,87,834 }, {1539,96,1587 ,835,836,837 }, {4335,4151,1833 ,838,839,317 }, + {768,1393,864 ,41,45,840 }, {457,540,116 ,214,14,217 }, {1652,892,4525 ,170,506,623 }, + {2030,3913,2011 ,770,841,129 }, {2014,1923,736 ,842,418,843 }, + {4392,2341,7051 ,844,845,547 }, {2097,2096,2075 ,846,788,787 }, + {2007,1978,1979 ,829,847,830 }, {1936,1333,2341 ,848,849,845 }, + {1411,1331,597 ,850,790,791 }, {5353,6836,144 ,851,852,853 }, + {1721,1778,1777 ,854,855,856 }, {392,170,1240 ,857,794,796 }, + {1475,1476,1547 ,858,859,860 }, {433,434,485 ,861,862,863 }, + {527,583,582 ,800,764,719 }, {385,386,433 ,864,865,861 }, {434,527,526 ,862,800,718 }, + {337,338,385 ,866,867,864 }, {238,338,237 ,868,867,869 }, {386,434,433 ,865,862,861 }, + {338,386,385 ,867,865,864 }, {338,337,237 ,867,866,869 }, {3382,3381,3356 ,870,871,872 }, + {2582,2474,2585 ,873,874,875 }, {5077,4549,1809 ,876,877,878 }, + {3305,3358,3324 ,879,880,881 }, {3383,3382,3357 ,882,870,883 }, + {3900,3478,3455 ,884,885,886 }, {1066,1065,1450 ,680,821,823 }, + {2036,2081,2035 ,887,888,889 }, {2081,2126,2068 ,888,890,891 }, + {2081,2068,2035 ,888,891,889 }, {2126,2125,2068 ,890,892,891 }, + {2274,1882,4980 ,893,894,241 }, {864,4112,839 ,840,47,52 }, {3341,863,3258 ,541,284,542 }, + {863,536,1300 ,284,523,285 }, {1393,4112,864 ,45,47,840 }, {4211,4212,321 ,215,895,60 }, + {1597,1422,18 ,505,525,896 }, {2014,4187,1923 ,842,502,418 }, + {1923,4429,736 ,418,771,843 }, {736,4429,459 ,843,771,696 }, + {358,189,4184 ,897,503,898 }, {1950,1929,1885 ,786,899,778 }, + {2050,2097,2075 ,785,846,787 }, {1780,1843,1842 ,900,901,196 }, + {1548,1549,1617 ,902,903,904 }, {8130,8120,8098 ,905,906,907 }, + {3080,1302,13 ,908,909,910 }, {1844,1845,1873 ,911,912,913 }, + {2035,2068,1978 ,889,891,847 }, {1059,169,652 ,678,248,334 }, + {485,434,526 ,863,862,718 }, {2650,3981,3054 ,914,915,916 }, + {3453,2650,3054 ,917,914,916 }, {2892,1305,536 ,918,466,523 }, + {7693,8129,3976 ,724,723,919 }, {443,171,444 ,920,921,922 }, + {2743,3687,1378 ,923,924,925 }, {3545,3595,3574 ,926,927,928 }, + {913,2892,536 ,444,918,523 }, {3432,3492,3462 ,929,930,931 }, + {3358,3383,3357 ,880,882,883 }, {5769,5768,5731 ,932,933,934 }, + {6045,6080,5453 ,935,936,937 }, {548,595,645 ,938,939,940 }, + {8070,3534,3668 ,941,942,513 }, {1586,1195,3315 ,943,725,944 }, + {399,1254,768 ,269,29,41 }, {4375,4136,399 ,312,28,269 }, {4212,294,321 ,895,945,60 }, + {356,4194,1161 ,310,293,288 }, {190,356,1161 ,287,310,288 }, + {1422,1147,997 ,525,202,328 }, {1652,174,892 ,170,169,506 }, + {1422,826,18 ,525,507,896 }, {4184,4187,2014 ,898,502,842 }, + {5977,3300,1936 ,809,694,848 }, {1752,1812,1811 ,946,947,777 }, + {1751,1752,1811 ,773,946,777 }, {1886,1885,1811 ,948,778,777 }, + {1812,1886,1811 ,947,948,777 }, {1951,1950,1885 ,949,786,778 }, + {1886,1951,1885 ,948,949,778 }, {1951,1996,1950 ,949,950,786 }, + {2051,2050,1950 ,951,785,786 }, {1996,2051,1950 ,950,951,786 }, + {2098,2097,2050 ,952,846,785 }, {2051,2098,2050 ,951,952,785 }, + {2098,2143,2097 ,952,953,846 }, {595,646,645 ,939,954,940 }, + {3492,3545,3491 ,930,926,955 }, {1045,1144,992 ,956,957,958 }, + {8128,8066,3277 ,959,960,961 }, {1874,1913,1912 ,962,963,964 }, + {386,435,434 ,865,965,862 }, {395,171,59 ,966,921,967 }, {59,171,443 ,967,921,920 }, + {3917,3916,3868 ,968,969,970 }, {3148,6891,3008 ,814,813,546 }, + {743,742,647 ,971,972,973 }, {1403,1404,1475 ,974,975,858 }, + {788,2558,600 ,976,977,978 }, {3542,3573,3541 ,979,980,981 }, + {3958,3959,3998 ,294,982,381 }, {938,4,803 ,983,538,144 }, {4,264,1031 ,538,984,163 }, + {264,4235,804 ,984,985,21 }, {4194,464,3 ,293,261,329 }, {505,5309,469 ,986,987,988 }, + {1635,1699,1442 ,989,772,990 }, {4443,4442,4391 ,703,991,992 }, + {4443,4464,4442 ,703,584,991 }, {4464,4463,4442 ,584,993,991 }, + {1328,1405,1327 ,994,995,996 }, {1838,363,1837 ,425,427,997 }, + {724,816,815 ,722,998,999 }, {1914,1983,1982 ,1000,1001,1002 }, + {1619,1686,1685 ,1003,1004,1005 }, {2124,2193,2123 ,1006,1007,1008 }, + {1681,1682,1739 ,1009,1010,1011 }, {291,336,335 ,1012,1013,1014 }, + {1663,1630,3081 ,1015,1016,1017 }, {726,725,626 ,669,721,801 }, + {627,726,626 ,670,669,801 }, {726,818,725 ,669,1018,721 }, {1476,1548,1547 ,859,902,860 }, + {906,817,818 ,1019,1020,1018 }, {3960,3959,3916 ,1021,982,969 }, + {3917,3960,3916 ,968,1021,969 }, {3960,4000,3999 ,1021,1022,1023 }, + {3959,3960,3999 ,982,1021,1023 }, {7115,7139,7129 ,1024,1025,1026 }, + {3189,3016,2150 ,1027,1028,534 }, {1031,264,804 ,163,984,21 }, + {804,4235,4503 ,21,985,569 }, {4503,4235,779 ,569,985,193 }, + {779,709,544 ,193,316,195 }, {173,704,398 ,12,7,15 }, {1161,4194,3 ,288,293,329 }, + {2352,1792,24 ,390,324,247 }, {4715,4716,4714 ,1029,1030,1031 }, + {6432,3265,1941 ,1032,1033,1034 }, {1603,1636,1635 ,1035,1036,989 }, + {1566,1603,1635 ,1037,1035,989 }, {1703,1752,1702 ,1038,946,1039 }, + {2143,2166,2165 ,953,1040,1041 }, {290,333,289 ,1042,1043,1044 }, + {1405,1404,1327 ,995,975,996 }, {1405,1477,1404 ,995,1045,975 }, + {7003,7002,6963 ,1046,1047,1048 }, {3303,3304,3323 ,1049,1050,1051 }, + {722,723,814 ,1052,1053,1054 }, {627,626,583 ,670,801,764 }, + {528,627,583 ,1055,670,764 }, {4426,6577,6837 ,1056,97,1057 }, + {1908,28,3636 ,1058,1059,1060 }, {3358,3357,3324 ,880,883,881 }, + {4042,2946,4041 ,1061,1062,1063 }, {2946,2571,3194 ,1062,1064,1065 }, + {3190,2555,2416 ,807,1066,808 }, {2558,19,600 ,977,1067,978 }, + {544,4454,1905 ,195,1068,40 }, {4454,4135,1905 ,1068,1069,40 }, + {117,1133,4213 ,9,24,1070 }, {1905,4135,4179 ,40,1069,251 }, + {1805,1490,4179 ,1071,46,251 }, {1805,4310,1490 ,1071,695,46 }, + {1805,615,4310 ,1071,766,695 }, {4521,1591,1318 ,179,1072,164 }, + {106,1207,5146 ,1073,1074,1075 }, {4421,4462,4420 ,229,1076,276 }, + {1338,4214,4029 ,1077,226,225 }, {7457,7515,7539 ,1078,1079,1080 }, + {1495,1567,1566 ,1081,1082,1037 }, {1494,1495,1566 ,1083,1081,1037 }, + {1567,1568,1603 ,1082,1084,1035 }, {1566,1567,1603 ,1037,1082,1035 }, + {1568,1637,1636 ,1084,1085,1036 }, {1603,1568,1636 ,1035,1084,1036 }, + {1637,1704,1703 ,1085,1086,1038 }, {1636,1637,1703 ,1036,1085,1038 }, + {1753,1752,1703 ,1087,946,1038 }, {1704,1753,1703 ,1086,1087,1038 }, + {1753,1795,1752 ,1087,1088,946 }, {1813,1812,1752 ,1089,947,946 }, + {1795,1813,1752 ,1088,1089,946 }, {1887,1886,1812 ,1090,948,947 }, + {1813,1887,1812 ,1089,1090,947 }, {1887,1952,1951 ,1090,1091,949 }, + {1886,1887,1951 ,948,1090,949 }, {1952,1996,1951 ,1091,950,949 }, + {2052,2051,1996 ,1092,951,950 }, {1952,2052,1996 ,1091,1092,950 }, + {2099,2098,2051 ,1093,952,951 }, {2052,2099,2051 ,1092,1093,951 }, + {2099,2143,2098 ,1093,953,952 }, {8080,8094,8069 ,1094,98,1095 }, + {3932,1059,652 ,1096,678,334 }, {6311,8122,1168 ,1097,384,1098 }, + {624,723,623 ,1099,1053,1100 }, {1979,1978,1911 ,830,847,1101 }, + {387,386,338 ,1102,865,867 }, {238,239,338 ,868,1103,867 }, {1687,1782,1781 ,1104,1105,1106 }, + {340,593,909 ,1107,1108,1109 }, {1918,908,1190 ,1110,1111,1112 }, + {5117,350,2291 ,1113,1114,1115 }, {2433,2400,2434 ,1116,1117,1118 }, + {865,776,867 ,1119,1120,1121 }, {4213,1133,4120 ,1070,24,23 }, + {321,294,1132 ,60,945,61 }, {101,173,713 ,5,12,3 }, {655,195,4056 ,1122,739,1123 }, + {195,4098,4056 ,739,353,1123 }, {660,118,67 ,387,1124,1125 }, + {1609,200,714 ,31,244,265 }, {1428,1495,1494 ,1126,1081,1083 }, + {1495,1568,1567 ,1081,1084,1082 }, {1753,1813,1795 ,1087,1089,1088 }, + {3194,2571,3016 ,1065,1064,1028 }, {2032,2123,2122 ,1127,1008,1128 }, + {647,549,2609 ,973,1129,1130 }, {2259,6314,6646 ,1131,1132,1133 }, + {1328,1329,1405 ,994,1134,995 }, {1406,1447,1446 ,1135,1136,1137 }, + {1777,1778,1839 ,856,855,1138 }, {8084,8110,8124 ,1139,1140,1141 }, + {436,435,386 ,1142,965,865 }, {339,387,338 ,1143,1102,867 }, + {486,528,434 ,1144,1055,862 }, {528,583,527 ,1055,764,800 }, + {3456,3566,3861 ,1145,1146,1147 }, {529,528,486 ,1148,1055,1144 }, + {437,486,436 ,1149,1144,1142 }, {1873,1874,1912 ,913,962,964 }, + {1616,1617,1683 ,1150,904,1151 }, {3462,3492,3461 ,931,930,1152 }, + {1880,105,2558 ,250,249,977 }, {3545,3544,3491 ,926,1153,955 }, + {2433,111,2400 ,1116,1154,1117 }, {101,704,173 ,5,7,12 }, {1132,1591,4521 ,61,1072,179 }, + {5765,5801,5764 ,540,700,537 }, {277,781,11 ,85,158,299 }, {613,277,11 ,298,85,299 }, + {879,4123,4122 ,1155,133,137 }, {3421,8113,3278 ,1156,1157,1158 }, + {911,2158,6684 ,1159,1160,1161 }, {1351,1428,8008 ,1162,1126,1163 }, + {1351,1395,1428 ,1162,1164,1126 }, {1567,1568,1495 ,1082,1084,1081 }, + {1814,1813,1753 ,1165,1089,1087 }, {1656,3227,3198 ,1166,1167,1168 }, + {449,547,546 ,1169,1170,1171 }, {3959,3999,3998 ,982,1023,381 }, + {448,449,546 ,1172,1169,1171 }, {647,691,743 ,973,1173,971 }, + {2191,2192,392 ,1174,1175,857 }, {547,644,546 ,1170,1176,1171 }, + {627,628,727 ,670,1177,668 }, {1234,1235,1328 ,1178,1179,994 }, + {1976,2032,2006 ,1180,1127,1181 }, {3323,3322,3303 ,1051,1182,1049 }, + {486,434,435 ,1144,862,965 }, {387,436,386 ,1102,1142,865 }, + {528,527,434 ,1055,800,862 }, {1405,1406,1446 ,995,1135,1137 }, + {304,488,3080 ,532,533,908 }, {3331,3364,3330 ,1183,1184,1185 }, + {1296,1880,2558 ,335,250,977 }, {7397,7453,7424 ,1186,1187,1188 }, + {1318,867,2137 ,164,1121,147 }, {3817,4756,4682 ,1189,1190,1191 }, + {5240,5264,5239 ,481,1192,482 }, {5616,5618,2011 ,646,127,129 }, + {5258,5282,5281 ,1193,1194,1195 }, {2026,1927,4234 ,130,1196,1197 }, + {2258,1263,5455 ,1198,1199,686 }, {3362,6350,6321 ,1200,704,706 }, + {2158,2272,6684 ,1160,1201,1161 }, {7151,3291,3290 ,1202,1203,1204 }, + {1353,1429,1428 ,1205,1206,1126 }, {1395,1353,1428 ,1164,1205,1126 }, + {1496,1495,1428 ,1207,1081,1126 }, {1429,1496,1428 ,1206,1207,1126 }, + {1569,1567,1495 ,1208,1082,1081 }, {1496,1569,1495 ,1207,1208,1081 }, + {1604,1568,1567 ,1209,1084,1082 }, {1569,1604,1567 ,1208,1209,1082 }, + {1638,1637,1568 ,1210,1085,1084 }, {1604,1638,1568 ,1209,1210,1084 }, + {1638,1705,1704 ,1210,1211,1086 }, {1637,1638,1704 ,1085,1210,1086 }, + {1754,1753,1704 ,1212,1087,1086 }, {1705,1754,1704 ,1211,1212,1086 }, + {1815,1814,1753 ,1213,1165,1087 }, {1754,1815,1753 ,1212,1213,1087 }, + {1862,1813,1814 ,1214,1089,1165 }, {1815,1862,1814 ,1213,1214,1165 }, + {1888,1887,1813 ,1215,1090,1089 }, {1862,1888,1813 ,1214,1215,1089 }, + {1953,1952,1887 ,1216,1091,1090 }, {1888,1953,1887 ,1215,1216,1090 }, + {2053,2052,1952 ,1217,1092,1091 }, {1953,2053,1952 ,1216,1217,1091 }, + {2100,2099,2052 ,1218,1093,1092 }, {2053,2100,2052 ,1217,1218,1092 }, + {828,827,741 ,1219,1220,1221 }, {385,433,432 ,864,861,1222 }, + {1842,1843,1910 ,196,901,1223 }, {2009,1188,862 ,1224,799,1225 }, + {1780,1803,1843 ,900,1226,901 }, {436,486,435 ,1142,1144,965 }, + {432,484,483 ,1222,1227,1228 }, {3987,3946,21 ,1229,1230,1231 }, + {1780,1781,1803 ,900,1106,1226 }, {1062,1014,929 ,1232,1233,1234 }, + {1182,1048,953 ,1235,1236,1237 }, {1183,1182,953 ,1238,1235,1237 }, + {2945,3900,3455 ,1239,884,886 }, {7115,7129,7102 ,1024,1026,1240 }, + {4173,4216,4215 ,134,1241,370 }, {4335,4280,642 ,838,1242,1243 }, + {1112,885,4202 ,292,216,260 }, {2026,4234,934 ,130,1197,131 }, + {3818,1907,2785 ,1244,1245,1246 }, {4328,4329,4378 ,166,1247,375 }, + {1207,106,3878 ,1074,1073,1248 }, {6203,322,803 ,1249,1250,144 }, + {4329,4379,4378 ,1247,567,375 }, {2376,6570,1263 ,1251,1252,1199 }, + {5059,1516,3177 ,1253,1254,1255 }, {5502,2208,5081 ,1256,1257,1258 }, + {3319,2245,2336 ,1259,1260,1261 }, {1352,1395,1351 ,1262,1164,1162 }, + {1569,1638,1604 ,1208,1210,1209 }, {2054,2053,1953 ,1263,1217,1216 }, + {3916,3959,3904 ,969,982,1264 }, {908,909,1190 ,1111,1109,1112 }, + {5117,2291,186 ,1113,1115,1265 }, {628,728,727 ,1177,1266,668 }, + {334,335,431 ,1267,1014,1268 }, {2123,2192,2191 ,1008,1175,1174 }, + {1290,1184,2009 ,795,797,1224 }, {1447,1407,1479 ,1136,1269,1270 }, + {3463,3494,3432 ,1271,736,929 }, {1857,540,1311 ,13,14,213 }, + {863,3484,3258 ,284,283,542 }, {4077,4076,4071 ,126,577,186 }, + {4255,4275,4328 ,165,699,166 }, {322,5,4 ,1250,350,538 }, {938,322,4 ,983,1250,538 }, + {5,315,264 ,350,352,984 }, {4,5,264 ,538,350,984 }, {4275,4329,4328 ,699,1247,166 }, + {6619,2245,1265 ,1272,1260,1273 }, {625,624,581 ,720,1099,1274 }, + {582,625,581 ,719,720,1274 }, {1781,1782,1803 ,1106,1105,1226 }, + {1779,1841,1840 ,1275,1276,198 }, {1918,2009,908 ,1110,1224,1111 }, + {2192,2193,170 ,1175,1007,794 }, {1145,1144,1045 ,1277,957,956 }, + {2122,2123,2191 ,1128,1008,1174 }, {524,525,580 ,1278,1279,1280 }, + {4518,1928,1879 ,1281,1282,1283 }, {926,1011,1010 ,1284,1285,1286 }, + {926,925,827 ,1284,1287,1220 }, {2068,2033,2034 ,891,1288,1289 }, + {6295,3283,8103 ,1290,262,1291 }, {3355,3354,3322 ,1292,1293,1182 }, + {1978,2068,2034 ,847,891,1289 }, {1233,1327,1232 ,1294,996,1295 }, + {221,2768,2541 ,1296,1297,1298 }, {264,315,4235 ,984,352,985 }, + {4235,4159,779 ,985,1299,193 }, {779,4159,709 ,193,1299,316 }, + {5056,5005,4950 ,1300,1301,1302 }, {3702,5520,1990 ,1303,1304,1305 }, + {6562,6299,4554 ,1306,760,1307 }, {1354,1430,1429 ,1308,1309,1206 }, + {1353,1354,1429 ,1205,1308,1206 }, {1430,1497,1496 ,1309,1310,1207 }, + {1429,1430,1496 ,1206,1309,1207 }, {1497,1570,1569 ,1310,1311,1208 }, + {1496,1497,1569 ,1207,1310,1208 }, {1570,1639,1638 ,1311,1312,1210 }, + {1569,1570,1638 ,1208,1311,1210 }, {1706,1705,1638 ,1313,1211,1210 }, + {1639,1706,1638 ,1312,1313,1210 }, {1706,1755,1754 ,1313,1314,1212 }, + {1705,1706,1754 ,1211,1313,1212 }, {1816,1815,1754 ,1315,1213,1212 }, + {1755,1816,1754 ,1314,1315,1212 }, {1863,1862,1815 ,1316,1214,1213 }, + {1816,1863,1815 ,1315,1316,1213 }, {1863,1889,1888 ,1316,1317,1215 }, + {1862,1863,1888 ,1214,1316,1215 }, {1889,1954,1953 ,1317,1318,1216 }, + {1888,1889,1953 ,1215,1317,1216 }, {2055,2054,1953 ,1319,1263,1216 }, + {1954,2055,1953 ,1318,1319,1216 }, {2076,2053,2054 ,1320,1217,1263 }, + {2055,2076,2054 ,1319,1320,1263 }, {2101,2100,2053 ,1321,1218,1217 }, + {2076,2101,2053 ,1320,1321,1217 }, {2168,2167,2100 ,1322,1323,1218 }, + {2101,2168,2100 ,1321,1322,1218 }, {2168,2090,2089 ,1322,1324,1325 }, + {2167,2168,2089 ,1323,1322,1325 }, {862,960,340 ,1225,1326,1107 }, + {908,340,909 ,1111,1107,1109 }, {1617,1684,1683 ,904,1327,1151 }, + {1047,906,2844 ,1328,1019,1329 }, {1326,1325,1231 ,1330,1331,1332 }, + {1143,1142,991 ,1333,1334,1335 }, {1232,1326,1231 ,1295,1330,1332 }, + {1188,999,862 ,799,798,1225 }, {524,580,623 ,1278,1280,1100 }, + {2193,2192,2123 ,1007,1175,1008 }, {433,485,484 ,861,863,1227 }, + {432,433,484 ,1222,861,1227 }, {339,387,239 ,1143,1102,1103 }, + {1234,1233,1142 ,1178,1294,1334 }, {108,2134,35 ,1336,1337,1338 }, + {790,742,743 ,1339,972,971 }, {1113,4213,4212 ,10,1070,895 }, + {540,1113,4211 ,14,10,215 }, {364,365,4362 ,1340,402,342 }, {365,139,4362 ,402,145,342 }, + {139,963,962 ,145,1341,233 }, {703,660,4145 ,100,387,101 }, {1168,4571,6311 ,1098,1342,1097 }, + {8079,3421,8104 ,55,1156,1343 }, {544,653,4454 ,195,318,1068 }, + {4569,2454,2272 ,1344,1345,1201 }, {6071,6377,6082 ,1346,1347,1348 }, + {6950,4981,5006 ,1349,1350,1351 }, {6562,5967,404 ,1306,1352,761 }, + {6570,6562,4554 ,1252,1306,1307 }, {1353,1267,1354 ,1205,1353,1308 }, + {1498,1571,1570 ,1354,1355,1311 }, {1497,1498,1570 ,1310,1354,1311 }, + {1570,1571,1639 ,1311,1355,1312 }, {1144,1145,1234 ,957,1277,1178 }, + {2033,2124,2123 ,1288,1006,1008 }, {3719,3515,959 ,1356,1357,1358 }, + {1547,1616,1615 ,860,1150,1359 }, {1546,1547,1615 ,1360,860,1359 }, + {2084,1048,1049 ,1361,1236,1362 }, {742,828,741 ,972,1219,1221 }, + {689,3315,3828 ,1363,944,1364 }, {526,525,484 ,718,1279,1227 }, + {485,526,484 ,863,718,1227 }, {1240,1290,1918 ,796,795,1110 }, + {828,926,827 ,1219,1284,1220 }, {35,751,6228 ,1338,1365,1366 }, + {744,790,743 ,1367,1339,971 }, {829,828,742 ,1368,1219,972 }, + {5801,5800,5764 ,700,702,537 }, {6577,4426,6837 ,97,1056,1057 }, + {469,468,367 ,988,1369,1370 }, {653,4053,4135 ,318,1371,1069 }, + {4454,653,4135 ,1068,318,1069 }, {376,1343,1564 ,379,378,368 }, + {2259,6646,8103 ,1131,1133,1291 }, {4179,4095,1805 ,251,349,1071 }, + {5843,1028,2958 ,1372,1373,1374 }, {5497,4530,2239 ,1375,1376,1377 }, + {2158,4569,2272 ,1160,1344,1201 }, {3582,3454,3660 ,1378,710,709 }, + {6299,6562,404 ,760,1306,761 }, {1465,1498,1497 ,1379,1354,1310 }, + {1430,1465,1497 ,1309,1379,1310 }, {632,1184,1290 ,1380,797,795 }, + {506,632,690 ,1381,1380,1382 }, {170,506,690 ,794,1381,1382 }, + {3903,3957,3956 ,296,295,1383 }, {1000,860,1876 ,1384,1385,1386 }, + {1782,1844,1843 ,1105,911,901 }, {1777,1839,1838 ,856,1138,425 }, + {1803,1782,1843 ,1226,1105,901 }, {401,448,2445 ,1387,1172,1388 }, + {6612,6648,6611 ,1389,1390,1391 }, {790,829,742 ,1339,1368,972 }, + {198,197,126 ,1392,1393,1394 }, {127,198,126 ,1395,1392,1394 }, + {368,367,197 ,1396,1370,1393 }, {198,368,197 ,1392,1396,1393 }, + {368,469,367 ,1396,988,1370 }, {614,615,1805 ,1397,766,1071 }, + {6646,6295,8103 ,1133,1290,1291 }, {1564,1595,4145 ,368,99,101 }, + {1094,839,1481 ,1398,52,194 }, {2408,5231,2219 ,1399,115,177 }, + {6562,3898,5967 ,1306,1400,1352 }, {1116,5497,2239 ,1401,1375,1377 }, + {7478,7477,7422 ,1402,1403,1404 }, {8020,2320,8019 ,1405,1406,1407 }, + {1355,1354,1267 ,1408,1308,1353 }, {1314,1355,1267 ,1409,1408,1353 }, + {1431,1430,1354 ,1410,1309,1308 }, {1355,1431,1354 ,1408,1410,1308 }, + {1431,1466,1465 ,1410,1411,1379 }, {1430,1431,1465 ,1309,1410,1379 }, + {1466,1499,1498 ,1411,1412,1354 }, {1465,1466,1498 ,1379,1411,1354 }, + {1499,1572,1571 ,1412,1413,1355 }, {1498,1499,1571 ,1354,1412,1355 }, + {1640,1639,1571 ,1414,1312,1355 }, {1572,1640,1571 ,1413,1414,1355 }, + {1640,1707,1706 ,1414,1415,1313 }, {1639,1640,1706 ,1312,1414,1313 }, + {1756,1755,1706 ,1416,1314,1313 }, {1707,1756,1706 ,1415,1416,1313 }, + {1756,1817,1816 ,1416,1417,1315 }, {1755,1756,1816 ,1314,1416,1315 }, + {1817,1818,1863 ,1417,1418,1316 }, {1816,1817,1863 ,1315,1417,1316 }, + {1818,1890,1889 ,1418,1419,1317 }, {1863,1818,1889 ,1316,1418,1317 }, + {1890,1955,1954 ,1419,1420,1318 }, {1889,1890,1954 ,1317,1419,1318 }, + {1955,2056,2055 ,1420,1421,1319 }, {1954,1955,2055 ,1318,1420,1319 }, + {2056,2057,2076 ,1421,1422,1320 }, {2055,2056,2076 ,1319,1421,1320 }, + {2102,2101,2076 ,1423,1321,1320 }, {2057,2102,2076 ,1422,1423,1320 }, + {2102,2169,2168 ,1423,1424,1322 }, {2101,2102,2168 ,1321,1423,1322 }, + {2169,2135,2090 ,1424,1425,1324 }, {2168,2169,2090 ,1322,1424,1324 }, + {631,2090,2135 ,1426,1324,1425 }, {992,906,1045 ,958,1019,956 }, + {902,901,814 ,1427,1428,1054 }, {1845,1874,1873 ,912,962,913 }, + {692,293,1182 ,1429,1430,1235 }, {1683,1777,1776 ,1151,856,1431 }, + {1910,940,1842 ,1223,197,196 }, {1739,1775,1801 ,1011,1432,1433 }, + {1236,1235,1145 ,1434,1179,1277 }, {1980,1979,1911 ,828,830,1101 }, + {991,1142,990 ,1335,1334,1435 }, {1848,1916,1915 ,1436,832,1437 }, + {3654,3712,3653 ,1438,1439,1440 }, {927,926,828 ,1441,1284,1219 }, + {3378,3377,3351 ,1442,1443,1444 }, {3534,8081,8072 ,942,1445,511 }, + {829,927,828 ,1368,1441,1219 }, {4329,4276,4355 ,1247,1446,1447 }, + {864,839,1094 ,840,52,1398 }, {193,768,864 ,1448,41,840 }, {4717,367,468 ,1449,1370,1369 }, + {2245,6344,1265 ,1260,1450,1273 }, {1263,6323,6509 ,1199,1451,1452 }, + {6163,6029,6066 ,1453,1454,1455 }, {1431,1499,1466 ,1410,1412,1411 }, + {1756,1818,1817 ,1416,1418,1417 }, {1981,2008,2037 ,1456,1457,1458 }, + {5587,5626,5586 ,1459,1460,1461 }, {595,596,646 ,939,1462,954 }, + {728,726,727 ,1266,669,668 }, {305,440,243 ,1463,1464,1465 }, + {692,1182,1183 ,1429,1235,1238 }, {154,692,1183 ,1466,1429,1238 }, + {1235,1236,1237 ,1179,1434,1467 }, {2128,2129,2149 ,1468,1469,1470 }, + {1012,1011,926 ,1471,1285,1284 }, {927,1012,926 ,1441,1471,1284 }, + {1012,1060,1011 ,1471,1472,1285 }, {1104,1103,1011 ,1473,1474,1285 }, + {1060,1104,1011 ,1472,1473,1285 }, {1104,1157,1103 ,1473,1475,1474 }, + {1157,1040,1201 ,1475,1476,1477 }, {874,36,3866 ,636,1478,1479 }, + {1103,1157,1201 ,1474,1475,1477 }, {409,150,89 ,1480,1481,1482 }, + {880,4093,1441 ,1483,1484,1485 }, {4109,4149,4090 ,576,1486,1487 }, + {4613,278,4590 ,1488,1489,1490 }, {3898,4987,5967 ,1400,1491,1352 }, + {4568,185,3247 ,1492,1493,1494 }, {1955,2057,2056 ,1420,1422,1421 }, + {484,525,524 ,1227,1279,1278 }, {431,432,483 ,1268,1222,1228 }, + {483,484,524 ,1228,1227,1278 }, {596,647,646 ,1462,973,954 }, + {1687,1781,1780 ,1104,1106,900 }, {1327,1404,1403 ,996,975,974 }, + {1190,909,1189 ,1112,1109,1495 }, {2585,2650,3453 ,875,914,917 }, + {933,692,154 ,1496,1429,1466 }, {155,933,154 ,1497,1496,1466 }, + {990,1141,1140 ,1435,1498,1499 }, {1235,1370,1329 ,1179,1500,1134 }, + {1000,1373,1515 ,1384,1501,1502 }, {1978,1977,1867 ,847,1503,1504 }, + {2130,2198,2197 ,1505,1506,1507 }, {1224,1040,1906 ,1508,1476,1509 }, + {2036,2035,1978 ,887,889,847 }, {3286,8104,79 ,56,1343,1510 }, + {2007,2036,1978 ,829,887,847 }, {3855,3854,3812 ,1511,1512,1513 }, + {3381,3380,3354 ,871,1514,1293 }, {3813,3855,3812 ,1515,1511,1513 }, + {2651,2746,3768 ,1516,1517,1518 }, {1463,3258,3681 ,220,542,691 }, + {1874,1847,1913 ,962,1519,963 }, {2082,2081,2036 ,1520,888,887 }, + {6432,1941,3252 ,1032,1034,781 }, {369,470,469 ,1521,1522,988 }, + {368,369,469 ,1396,1521,988 }, {507,505,469 ,1523,986,988 }, + {470,507,469 ,1522,1523,988 }, {4213,4120,294 ,1070,23,945 }, + {277,1992,781 ,85,834,158 }, {1494,3862,1428 ,1083,550,1126 }, + {4569,2808,2454 ,1344,522,1345 }, {2245,6619,2336 ,1260,1272,1261 }, + {6480,5276,5350 ,1524,1525,1526 }, {7239,7261,7219 ,1527,1528,1529 }, + {1356,1432,1431 ,1530,1531,1410 }, {1355,1356,1431 ,1408,1530,1410 }, + {1500,1499,1431 ,1532,1412,1410 }, {1432,1500,1431 ,1531,1532,1410 }, + {1573,1572,1499 ,1533,1413,1412 }, {1500,1573,1499 ,1532,1533,1412 }, + {1573,1641,1640 ,1533,1534,1414 }, {1572,1573,1640 ,1413,1533,1414 }, + {1708,1707,1640 ,1535,1415,1414 }, {1641,1708,1640 ,1534,1535,1414 }, + {1708,1757,1756 ,1535,1536,1416 }, {1707,1708,1756 ,1415,1535,1416 }, + {1757,1796,1756 ,1536,1537,1416 }, {1796,1819,1818 ,1537,1538,1418 }, + {1756,1796,1818 ,1416,1537,1418 }, {1891,1890,1818 ,1539,1419,1418 }, + {1819,1891,1818 ,1538,1539,1418 }, {1956,1955,1890 ,1540,1420,1419 }, + {1891,1956,1890 ,1539,1540,1419 }, {1956,2058,2057 ,1540,1541,1422 }, + {1955,1956,2057 ,1420,1540,1422 }, {2058,2103,2102 ,1541,1542,1423 }, + {2057,2058,2102 ,1422,1541,1423 }, {2103,2170,2169 ,1542,1543,1424 }, + {2102,2103,2169 ,1423,1542,1424 }, {2226,2135,2169 ,1544,1425,1424 }, + {2170,2226,2169 ,1543,1544,1424 }, {2226,2152,2136 ,1544,1545,1546 }, + {2135,2226,2136 ,1425,1544,1546 }, {1184,1188,2009 ,797,799,1224 }, + {1980,1981,2036 ,828,1456,887 }, {1985,729,730 ,1547,1548,1549 }, + {2197,933,155 ,1507,1496,1497 }, {2196,2197,155 ,1550,1507,1497 }, + {2037,2038,2082 ,1458,1551,1520 }, {2083,2128,2127 ,1552,1468,1553 }, + {2038,2083,2082 ,1551,1552,1520 }, {2198,2199,2216 ,1506,1554,1555 }, + {992,1144,991 ,958,957,1335 }, {6393,3358,3305 ,1556,880,879 }, + {1911,1978,1867 ,1101,847,1504 }, {1263,4554,6323 ,1199,1307,1451 }, + {337,385,336 ,866,864,1013 }, {1549,1548,1476 ,903,902,859 }, + {1477,1476,1404 ,1045,859,975 }, {3664,296,3475 ,1557,1558,1559 }, + {2746,3566,3768 ,1517,1146,1518 }, {526,625,582 ,718,720,719 }, + {2122,2031,2032 ,1128,1560,1127 }, {2148,2147,2126 ,1561,1562,890 }, + {1618,1685,1684 ,1563,1005,1327 }, {525,581,580 ,1279,1274,1280 }, + {3987,4227,3946 ,1229,1564,1230 }, {294,4120,4375 ,945,23,312 }, + {4548,4568,3247 ,589,1492,1494 }, {5113,6098,5914 ,141,1565,1566 }, + {1501,1500,1432 ,1567,1532,1531 }, {2259,8103,6351 ,1131,1291,1568 }, + {860,1238,1183 ,1385,1569,1238 }, {2082,2083,2127 ,1520,1552,1553 }, + {2128,2149,2148 ,1468,1470,1561 }, {2149,2197,2196 ,1470,1507,1550 }, + {2127,2128,2148 ,1553,1468,1561 }, {2148,2149,2196 ,1561,1470,1550 }, + {2130,2131,2198 ,1505,656,1506 }, {724,723,624 ,722,1053,1099 }, + {2199,2200,2216 ,1554,1570,1555 }, {1684,1685,1721 ,1327,1005,854 }, + {1683,1721,1777 ,1151,854,856 }, {1618,1619,1685 ,1563,1003,1005 }, + {240,4183,260 ,1571,1572,1573 }, {3263,2746,2651 ,252,1517,1516 }, + {392,305,2190 ,857,1463,1574 }, {3915,3903,3956 ,1575,296,1383 }, + {2869,2832,2868 ,1576,638,1577 }, {1913,1981,1980 ,963,1456,828 }, + {1885,4698,1810 ,778,1578,779 }, {649,444,492 ,1579,922,1580 }, + {399,768,193 ,269,41,1448 }, {1591,399,1032 ,1072,269,1581 }, + {1032,399,193 ,1581,269,1448 }, {4375,399,1591 ,312,269,1072 }, + {3777,4261,5733 ,1582,1583,1584 }, {1492,1749,5075 ,1585,555,1586 }, + {5889,6163,6066 ,1587,1453,1455 }, {5511,5512,5548 ,1588,1589,1590 }, + {3252,6553,6432 ,781,780,1032 }, {1757,1820,1819 ,1536,1591,1538 }, + {1796,1757,1819 ,1537,1536,1538 }, {1820,1891,1819 ,1591,1539,1538 }, + {2129,2130,2149 ,1469,1505,1470 }, {1913,1914,1981 ,963,1000,1456 }, + {1982,1983,2008 ,1002,1001,1457 }, {1983,2038,2037 ,1001,1551,1458 }, + {1981,1982,2008 ,1456,1002,1457 }, {2008,1983,2037 ,1457,1001,1458 }, + {2067,2005,2006 ,1592,1593,1181 }, {1867,1977,2390 ,1504,1503,1594 }, + {2131,2199,2198 ,656,1554,1506 }, {625,724,624 ,720,722,1099 }, + {741,740,644 ,1221,1595,1176 }, {1239,1240,1190 ,1596,796,1112 }, + {645,741,644 ,940,1221,1176 }, {547,548,644 ,1170,938,1176 }, + {690,632,1290 ,1382,1380,795 }, {2034,2033,1977 ,1289,1288,1503 }, + {2120,2121,2189 ,1597,1598,1599 }, {440,394,243 ,1464,1600,1465 }, + {2609,744,691 ,1130,1367,1173 }, {1779,1780,1842 ,1275,900,196 }, + {4035,4036,2151 ,1601,1602,1603 }, {1088,1186,1452 ,1604,1605,1606 }, + {6347,7254,6348 ,451,450,1607 }, {271,370,369 ,1608,1609,1521 }, + {370,418,369 ,1609,1610,1521 }, {418,471,470 ,1610,1611,1522 }, + {508,507,470 ,1612,1523,1522 }, {471,508,470 ,1611,1612,1522 }, + {566,565,507 ,1613,1614,1523 }, {508,566,507 ,1612,1613,1523 }, + {755,4084,4113 ,1615,205,207 }, {2030,5203,2698 ,770,102,105 }, + {229,1112,356 ,221,292,310 }, {7826,7853,7801 ,1616,1617,1618 }, + {1030,978,4991 ,1619,1620,1621 }, {6736,6790,6754 ,1622,711,713 }, + {6092,6176,5940 ,1623,1624,1625 }, {1357,1356,1315 ,1626,1530,1627 }, + {4576,4596,7524 ,1628,1629,1630 }, {1357,1433,1432 ,1626,1631,1531 }, + {1356,1357,1432 ,1530,1626,1531 }, {1433,1502,1501 ,1631,1632,1567 }, + {1432,1433,1501 ,1531,1631,1567 }, {1535,1500,1501 ,1633,1532,1567 }, + {1502,1535,1501 ,1632,1633,1567 }, {1574,1573,1500 ,1634,1533,1532 }, + {1535,1574,1500 ,1633,1634,1532 }, {1574,1642,1641 ,1634,1635,1534 }, + {1573,1574,1641 ,1533,1634,1534 }, {1642,1709,1708 ,1635,1636,1535 }, + {1641,1642,1708 ,1534,1635,1535 }, {1709,1758,1757 ,1636,1637,1536 }, + {1708,1709,1757 ,1535,1636,1536 }, {1758,1821,1820 ,1637,1638,1591 }, + {1757,1758,1820 ,1536,1637,1591 }, {1892,1891,1820 ,1639,1539,1591 }, + {1821,1892,1820 ,1638,1639,1591 }, {1957,1956,1891 ,1640,1540,1539 }, + {1892,1957,1891 ,1639,1640,1539 }, {1957,2016,1956 ,1640,1641,1540 }, + {2016,2059,2058 ,1641,1642,1541 }, {1956,2016,2058 ,1540,1641,1541 }, + {2104,2103,2058 ,1643,1542,1541 }, {2059,2104,2058 ,1642,1643,1541 }, + {2171,2170,2103 ,1644,1543,1542 }, {2104,2171,2103 ,1643,1644,1542 }, + {2171,2227,2226 ,1644,1645,1544 }, {2170,2171,2226 ,1543,1644,1544 }, + {2227,2161,2152 ,1645,1646,1545 }, {2226,2227,2152 ,1544,1645,1545 }, + {2161,391,2152 ,1646,1647,1545 }, {776,782,777 ,1120,1648,421 }, + {1914,1982,1981 ,1000,1002,1456 }, {1142,1141,990 ,1334,1498,1435 }, + {1403,1475,1474 ,974,858,1649 }, {3645,3644,3591 ,1650,1651,1652 }, + {450,548,547 ,1653,938,1170 }, {548,645,644 ,938,940,1176 }, + {1240,1239,305 ,796,1596,1463 }, {1617,1618,1684 ,904,1563,1327 }, + {221,241,240 ,1296,314,1571 }, {102,221,240 ,432,1296,1571 }, + {109,2039,349 ,1654,1655,566 }, {7747,7745,7746 ,331,650,332 }, + {1104,1040,1157 ,1473,1476,1475 }, {3913,2030,2698 ,841,770,105 }, + {5641,5642,5657 ,510,526,48 }, {1463,4326,3258 ,220,219,542 }, + {5337,5338,4914 ,572,63,65 }, {639,715,950 ,291,168,330 }, {4222,4131,4251 ,530,1656,132 }, + {1357,1315,1316 ,1626,1627,1657 }, {1502,1574,1535 ,1632,1634,1633 }, + {1709,1759,1758 ,1636,1658,1637 }, {6686,3534,8070 ,750,942,941 }, + {1785,1849,1848 ,1659,1660,1436 }, {2216,968,293 ,1555,1661,1430 }, + {1784,1785,1848 ,1662,1659,1436 }, {1875,1917,1916 ,1663,833,832 }, + {1848,1875,1916 ,1436,1663,832 }, {449,450,547 ,1169,1653,1170 }, + {1106,1203,1202 ,1664,337,339 }, {1326,1327,1403 ,1330,996,974 }, + {687,1744,2088 ,1665,1666,1667 }, {596,595,548 ,1462,939,938 }, + {1685,1722,1721 ,1005,1668,854 }, {2216,2200,968 ,1555,1570,1661 }, + {102,1553,221 ,432,1669,1296 }, {7194,7208,7225 ,1670,1671,1672 }, + {1144,1234,1142 ,957,1178,1334 }, {471,566,508 ,1611,1613,1612 }, + {200,1348,714 ,244,148,265 }, {1348,639,714 ,148,291,265 }, {737,229,356 ,150,221,310 }, + {1033,896,888 ,306,256,238 }, {4483,4525,4482 ,549,623,583 }, + {4473,4497,4520 ,270,91,1673 }, {826,189,738 ,507,503,1674 }, + {1822,1821,1758 ,1675,1638,1637 }, {1759,1822,1758 ,1658,1675,1637 }, + {1821,1822,1892 ,1638,1675,1639 }, {1957,2059,2016 ,1640,1642,1641 }, + {2172,2171,2104 ,1676,1644,1643 }, {1849,1875,1848 ,1660,1663,1436 }, + {2033,2032,1976 ,1288,1127,1180 }, {1233,1234,1327 ,1294,1178,996 }, + {339,436,387 ,1143,1142,1102 }, {221,748,241 ,1296,1677,314 }, + {284,4283,1241 ,139,1678,313 }, {2893,5307,4393 ,1679,461,463 }, + {272,371,370 ,1680,1681,1609 }, {271,272,370 ,1608,1680,1609 }, + {371,419,418 ,1681,1682,1610 }, {370,371,418 ,1609,1681,1610 }, + {419,472,471 ,1682,1683,1611 }, {418,419,471 ,1610,1682,1611 }, + {567,566,471 ,1684,1613,1611 }, {472,567,471 ,1683,1684,1611 }, + {715,639,497 ,168,291,290 }, {4483,1652,4525 ,549,170,623 }, + {1021,3,4187 ,289,329,502 }, {4497,4534,136 ,91,403,1685 }, {4525,1673,4513 ,623,1686,624 }, + {1274,1275,1316 ,1687,1688,1657 }, {1275,1358,1357 ,1688,1689,1626 }, + {1316,1275,1357 ,1657,1688,1626 }, {1434,1433,1357 ,1690,1631,1626 }, + {1358,1434,1357 ,1689,1690,1626 }, {1503,1502,1433 ,1691,1632,1631 }, + {1434,1503,1433 ,1690,1691,1631 }, {1575,1574,1502 ,1692,1634,1632 }, + {1503,1575,1502 ,1691,1692,1632 }, {1643,1642,1574 ,1693,1635,1634 }, + {1575,1643,1574 ,1692,1693,1634 }, {1643,1710,1709 ,1693,1694,1636 }, + {1642,1643,1709 ,1635,1693,1636 }, {1710,1711,1709 ,1694,1695,1636 }, + {1711,1760,1759 ,1695,1696,1658 }, {1709,1711,1759 ,1636,1695,1658 }, + {1823,1822,1759 ,1697,1675,1658 }, {1760,1823,1759 ,1696,1697,1658 }, + {1823,1893,1892 ,1697,1698,1639 }, {1822,1823,1892 ,1675,1697,1639 }, + {1893,1958,1957 ,1698,1699,1640 }, {1892,1893,1957 ,1639,1698,1640 }, + {2060,2059,1957 ,1700,1642,1640 }, {1958,2060,1957 ,1699,1700,1640 }, + {2060,2077,2059 ,1700,1701,1642 }, {2105,2104,2059 ,1702,1643,1642 }, + {2077,2105,2059 ,1701,1702,1642 }, {2173,2172,2104 ,1703,1676,1643 }, + {2105,2173,2104 ,1702,1703,1643 }, {2173,2174,2171 ,1703,1704,1644 }, + {2172,2173,2171 ,1676,1703,1644 }, {2174,2228,2227 ,1704,1705,1645 }, + {2171,2174,2227 ,1644,1704,1645 }, {2228,2214,2161 ,1705,1706,1646 }, + {2227,2228,2161 ,1645,1705,1646 }, {680,391,2161 ,1707,1647,1646 }, + {2214,680,2161 ,1706,1707,1646 }, {680,1624,1623 ,1707,1708,1709 }, + {391,680,1623 ,1647,1707,1709 }, {8061,8097,8082 ,453,1710,454 }, + {2038,2069,2083 ,1551,1711,1552 }, {1778,1779,1840 ,855,1275,198 }, + {4426,4021,8100 ,1056,1712,1713 }, {2121,2122,2190 ,1598,1128,1574 }, + {170,690,1290 ,794,1382,795 }, {7977,7976,7927 ,1714,1715,1716 }, + {1847,1914,1913 ,1519,1000,963 }, {242,1688,1389 ,1717,1718,315 }, + {241,242,1389 ,314,1717,315 }, {1688,133,1241 ,1718,1719,313 }, + {1389,1688,1241 ,315,1718,313 }, {3451,3516,2582 ,1720,1721,873 }, + {1912,1980,1911 ,964,828,1101 }, {204,272,271 ,1722,1680,1608 }, + {568,567,472 ,1723,1684,1683 }, {4224,896,1033 ,309,256,306 }, + {888,1006,799 ,238,234,392 }, {4520,4497,136 ,1673,91,1685 }, + {7928,7927,7876 ,1724,1716,1725 }, {1576,1575,1503 ,1726,1692,1691 }, + {1576,1643,1575 ,1726,1693,1692 }, {1643,1711,1710 ,1693,1695,1694 }, + {8124,3271,2810 ,1141,1727,1728 }, {905,904,858 ,1729,1730,1731 }, + {2126,2147,2146 ,890,1562,1732 }, {859,905,858 ,1733,1729,1731 }, + {1841,1842,1840 ,1276,196,198 }, {1619,1618,1549 ,1003,1563,903 }, + {815,903,902 ,999,1734,1427 }, {2541,242,241 ,1298,1717,314 }, + {883,76,146 ,119,689,504 }, {3516,3537,2582 ,1721,1735,873 }, + {3477,5785,4162 ,1736,1737,1738 }, {5480,4527,4162 ,1739,1740,1738 }, + {999,1985,960 ,798,1547,1326 }, {272,204,205 ,1680,1722,1741 }, + {4106,8142,8078 ,1742,1743,1744 }, {4483,4482,4444 ,549,583,582 }, + {174,1147,1422 ,169,202,525 }, {136,4534,1628 ,1685,403,118 }, + {189,4187,4184 ,503,502,898 }, {1644,1643,1576 ,1745,1693,1726 }, + {1643,1644,1711 ,1693,1745,1695 }, {2105,2174,2173 ,1702,1704,1703 }, + {1726,1785,1784 ,1746,1659,1662 }, {1725,1726,1784 ,1747,1746,1662 }, + {1838,1839,4427 ,425,1138,426 }, {2031,2067,2029 ,1560,1592,1748 }, + {273,372,371 ,1749,1750,1681 }, {372,420,419 ,1750,1751,1682 }, + {371,372,419 ,1681,1750,1682 }, {420,473,472 ,1751,1752,1683 }, + {419,420,472 ,1682,1751,1683 }, {569,568,472 ,1753,1723,1683 }, + {473,569,472 ,1752,1753,1683 }, {569,665,664 ,1753,1754,1755 }, + {568,569,664 ,1723,1753,1755 }, {1422,997,826 ,525,328,507 }, + {826,997,1021 ,507,328,289 }, {1034,1127,1172 ,1756,1757,1758 }, + {1172,1127,1221 ,1758,1757,1759 }, {1127,1276,1275 ,1757,1760,1688 }, + {1221,1127,1275 ,1759,1757,1688 }, {1276,1359,1358 ,1760,1761,1689 }, + {1275,1276,1358 ,1688,1760,1689 }, {1359,1435,1434 ,1761,1762,1690 }, + {1358,1359,1434 ,1689,1761,1690 }, {1504,1503,1434 ,1763,1691,1690 }, + {1435,1504,1434 ,1762,1763,1690 }, {1577,1576,1503 ,1764,1726,1691 }, + {1504,1577,1503 ,1763,1764,1691 }, {1645,1644,1576 ,1765,1745,1726 }, + {1577,1645,1576 ,1764,1765,1726 }, {1645,1712,1711 ,1765,1766,1695 }, + {1644,1645,1711 ,1745,1765,1695 }, {1761,1760,1711 ,1767,1696,1695 }, + {1712,1761,1711 ,1766,1767,1695 }, {1761,1824,1823 ,1767,1768,1697 }, + {1760,1761,1823 ,1696,1767,1697 }, {1894,1893,1823 ,1769,1698,1697 }, + {1824,1894,1823 ,1768,1769,1697 }, {1894,1959,1958 ,1769,1770,1699 }, + {1893,1894,1958 ,1698,1769,1699 }, {1959,2061,2060 ,1770,1771,1700 }, + {1958,1959,2060 ,1699,1770,1700 }, {2061,2062,2077 ,1771,1772,1701 }, + {2060,2061,2077 ,1700,1771,1701 }, {2062,2106,2105 ,1772,1773,1702 }, + {2077,2062,2105 ,1701,1772,1702 }, {2106,2175,2174 ,1773,1774,1704 }, + {2105,2106,2174 ,1702,1773,1704 }, {2175,2229,2228 ,1774,1775,1705 }, + {2174,2175,2228 ,1704,1774,1705 }, {2229,1559,2214 ,1775,1776,1706 }, + {2228,2229,2214 ,1705,1775,1706 }, {633,680,2214 ,1777,1707,1706 }, + {1559,633,2214 ,1776,1777,1706 }, {633,1624,680 ,1777,1708,1707 }, + {1187,1186,1624 ,1778,1605,1708 }, {633,1187,1624 ,1777,1778,1708 }, + {1187,1452,1186 ,1778,1606,1605 }, {1187,1919,1452 ,1778,1779,1606 }, + {8110,5521,8125 ,1140,751,752 }, {1326,1403,1402 ,1330,974,1780 }, + {1657,1658,1725 ,1781,1782,1747 }, {1688,134,133 ,1718,1783,1719 }, + {134,80,284 ,1783,140,139 }, {133,134,284 ,1719,1783,139 }, {8125,8071,8068 ,752,738,1784 }, + {272,273,371 ,1680,1749,1681 }, {493,175,492 ,1785,211,1580 }, + {6384,3362,3387 ,1786,1200,1787 }, {1034,1035,1127 ,1756,1788,1757 }, + {1959,2062,2061 ,1770,1772,1771 }, {3712,3758,3711 ,1439,1789,1790 }, + {3445,3648,3596 ,1791,734,1792 }, {1017,3513,3452 ,1793,441,1794 }, + {108,35,36 ,1336,1338,1478 }, {3148,3008,2743 ,814,546,923 }, + {1658,1726,1725 ,1782,1746,1747 }, {5057,8096,6122 ,1795,1796,1797 }, + {3456,3405,2920 ,1145,1798,1799 }, {5237,5600,2086 ,1800,1801,1802 }, + {5151,5171,6114 ,1803,1804,1805 }, {1549,1618,1617 ,903,1563,904 }, + {982,1035,1034 ,1806,1788,1756 }, {943,982,1034 ,1807,1806,1756 }, + {1959,2017,2062 ,1770,1808,1772 }, {3687,2743,3873 ,924,923,1809 }, + {3597,3445,3596 ,1810,1791,1792 }, {2121,2120,2028 ,1598,1597,1811 }, + {3537,2072,2474 ,1735,1812,874 }, {581,624,623 ,1274,1099,1100 }, + {903,990,989 ,1734,1435,1813 }, {2148,2196,2147 ,1561,1550,1562 }, + {453,82,80 ,1814,138,140 }, {5137,4631,5136 ,1815,1816,1817 }, + {1477,1549,1476 ,1045,903,859 }, {4367,4390,4343 ,228,230,1818 }, + {412,4150,1793 ,358,320,319 }, {274,373,372 ,1819,1820,1750 }, + {273,274,372 ,1749,1819,1750 }, {373,374,420 ,1820,1821,1751 }, + {372,373,420 ,1750,1820,1751 }, {374,474,473 ,1821,1822,1752 }, + {420,374,473 ,1751,1821,1752 }, {474,570,569 ,1822,1823,1753 }, + {473,474,569 ,1752,1822,1753 }, {570,666,665 ,1823,1824,1754 }, + {569,570,665 ,1753,1823,1754 }, {666,762,761 ,1824,1825,1826 }, + {665,666,761 ,1754,1824,1826 }, {762,846,845 ,1825,1827,1828 }, + {761,762,845 ,1826,1825,1828 }, {846,944,943 ,1827,1829,1807 }, + {845,846,943 ,1828,1827,1807 }, {943,944,982 ,1807,1829,1806 }, + {944,1036,1035 ,1829,1830,1788 }, {982,944,1035 ,1806,1829,1788 }, + {1036,1128,1127 ,1830,1831,1757 }, {1035,1036,1127 ,1788,1830,1757 }, + {1277,1276,1127 ,1832,1760,1757 }, {1128,1277,1127 ,1831,1832,1757 }, + {1360,1359,1276 ,1833,1761,1760 }, {1277,1360,1276 ,1832,1833,1760 }, + {1360,1436,1435 ,1833,1834,1762 }, {1359,1360,1435 ,1761,1833,1762 }, + {1505,1504,1435 ,1835,1763,1762 }, {1436,1505,1435 ,1834,1835,1762 }, + {1505,1578,1577 ,1835,1836,1764 }, {1504,1505,1577 ,1763,1835,1764 }, + {1578,1646,1645 ,1836,1837,1765 }, {1577,1578,1645 ,1764,1836,1765 }, + {1646,1713,1712 ,1837,1838,1766 }, {1645,1646,1712 ,1765,1837,1766 }, + {1713,1762,1761 ,1838,1839,1767 }, {1712,1713,1761 ,1766,1838,1767 }, + {1825,1824,1761 ,1840,1768,1767 }, {1762,1825,1761 ,1839,1840,1767 }, + {1825,1895,1894 ,1840,1841,1769 }, {1824,1825,1894 ,1768,1840,1769 }, + {1895,1960,1959 ,1841,1842,1770 }, {1894,1895,1959 ,1769,1841,1770 }, + {1960,1997,1959 ,1842,1843,1770 }, {1959,1997,2017 ,1770,1843,1808 }, + {1997,2063,2062 ,1843,1844,1772 }, {2017,1997,2062 ,1808,1843,1772 }, + {2063,2107,2106 ,1844,1845,1773 }, {2062,2063,2106 ,1772,1844,1773 }, + {2107,2108,2106 ,1845,1846,1773 }, {2108,2176,2175 ,1846,1847,1774 }, + {2106,2108,2175 ,1773,1846,1774 }, {2176,2230,2229 ,1847,1848,1775 }, + {2175,2176,2229 ,1774,1847,1775 }, {2230,866,1559 ,1848,1849,1776 }, + {2229,2230,1559 ,1775,1848,1776 }, {866,550,633 ,1849,1850,1777 }, + {1559,866,633 ,1776,1849,1777 }, {550,1244,1187 ,1850,575,1778 }, + {633,550,1187 ,1777,1850,1778 }, {1244,1367,1187 ,575,574,1778 }, + {2195,341,2194 ,1851,1852,1853 }, {7275,7254,7253 ,1854,450,1855 }, + {1977,2033,1976 ,1503,1288,1180 }, {2582,3537,2474 ,873,1735,874 }, + {902,903,901 ,1427,1734,1428 }, {1232,1231,1140 ,1295,1332,1499 }, + {1141,1232,1140 ,1498,1295,1499 }, {2082,2127,2081 ,1520,1553,888 }, + {51,301,453 ,1856,1857,1814 }, {80,51,453 ,140,1856,1814 }, {94,82,301 ,1858,138,1857 }, + {1693,3425,3283 ,264,1859,262 }, {5219,5240,5218 ,1860,481,483 }, + {3344,4221,592 ,1861,1862,1863 }, {772,891,4540 ,18,189,19 }, + {274,374,373 ,1819,1821,1820 }, {4314,1561,1525 ,1864,1865,1866 }, + {3591,3644,3590 ,1652,1651,1867 }, {2743,3931,3873 ,923,1868,1809 }, + {2125,2126,2146 ,892,890,1732 }, {723,815,814 ,1053,999,1054 }, + {2196,155,165 ,1550,1497,1869 }, {2195,2196,165 ,1851,1550,1869 }, + {2939,80,134 ,1870,140,1783 }, {5680,2453,5698 ,50,42,44 }, {274,320,374 ,1819,1871,1821 }, + {2063,2108,2107 ,1844,1846,1845 }, {550,1408,1244 ,1850,1872,575 }, + {1367,2070,614 ,574,573,1397 }, {7510,7509,7452 ,1873,1874,1875 }, + {859,858,816 ,1733,1731,998 }, {305,243,2189 ,1463,1465,1599 }, + {51,355,301 ,1856,1876,1857 }, {94,157,196 ,1858,1877,152 }, + {2743,3008,3944 ,923,546,729 }, {5180,5179,5136 ,1878,1879,1817 }, + {5658,2453,5680 ,49,42,50 }, {770,2352,805 ,275,390,389 }, {4064,4069,3317 ,1880,1881,1882 }, + {375,475,474 ,1883,1884,1822 }, {374,375,474 ,1821,1883,1822 }, + {571,570,474 ,1885,1823,1822 }, {475,571,474 ,1884,1885,1822 }, + {667,666,570 ,1886,1824,1823 }, {571,667,570 ,1885,1886,1823 }, + {763,762,666 ,1887,1825,1824 }, {667,763,666 ,1886,1887,1824 }, + {847,846,762 ,1888,1827,1825 }, {763,847,762 ,1887,1888,1825 }, + {945,944,846 ,1889,1829,1827 }, {847,945,846 ,1888,1889,1827 }, + {1037,1036,944 ,1890,1830,1829 }, {945,1037,944 ,1889,1890,1829 }, + {1129,1128,1036 ,1891,1831,1830 }, {1037,1129,1036 ,1890,1891,1830 }, + {1129,1278,1277 ,1891,1892,1832 }, {1128,1129,1277 ,1831,1891,1832 }, + {1361,1360,1277 ,1893,1833,1832 }, {1278,1361,1277 ,1892,1893,1832 }, + {1437,1436,1360 ,1894,1834,1833 }, {1361,1437,1360 ,1893,1894,1833 }, + {1506,1505,1436 ,1895,1835,1834 }, {1437,1506,1436 ,1894,1895,1834 }, + {1506,1579,1578 ,1895,1896,1836 }, {1505,1506,1578 ,1835,1895,1836 }, + {1579,1647,1646 ,1896,1897,1837 }, {1578,1579,1646 ,1836,1896,1837 }, + {1647,1714,1713 ,1897,1898,1838 }, {1646,1647,1713 ,1837,1897,1838 }, + {1714,1763,1762 ,1898,1899,1839 }, {1713,1714,1762 ,1838,1898,1839 }, + {1826,1825,1762 ,1900,1840,1839 }, {1763,1826,1762 ,1899,1900,1839 }, + {1896,1895,1825 ,1901,1841,1840 }, {1826,1896,1825 ,1900,1901,1840 }, + {1961,1960,1895 ,1902,1842,1841 }, {1896,1961,1895 ,1901,1902,1841 }, + {1961,1997,1960 ,1902,1843,1842 }, {1961,2018,1997 ,1902,1903,1843 }, + {2018,2064,2063 ,1903,1904,1844 }, {1997,2018,2063 ,1843,1903,1844 }, + {2109,2108,2063 ,1905,1846,1844 }, {2064,2109,2063 ,1904,1905,1844 }, + {2109,2177,2176 ,1905,1906,1847 }, {2108,2109,2176 ,1846,1905,1847 }, + {2177,780,2230 ,1906,1907,1848 }, {2176,2177,2230 ,1847,1906,1848 }, + {2230,780,866 ,1848,1907,1849 }, {780,389,550 ,1907,1908,1850 }, + {866,780,550 ,1849,1907,1850 }, {389,1409,1408 ,1908,1909,1872 }, + {550,389,1408 ,1850,1908,1872 }, {1409,1095,1244 ,1909,1910,575 }, + {1408,1409,1244 ,1872,1909,575 }, {1095,2070,1244 ,1910,573,575 }, + {3364,3363,3330 ,1184,1911,1185 }, {5817,5816,5800 ,701,545,702 }, + {3869,3890,3854 ,1912,1913,1512 }, {3975,2812,3684 ,728,1914,1915 }, + {1547,1548,1616 ,860,902,1150 }, {723,722,623 ,1053,1052,1100 }, + {905,992,904 ,1729,958,1730 }, {250,157,94 ,1916,1877,1858 }, + {301,250,94 ,1857,1916,1858 }, {3944,3975,3684 ,729,728,1915 }, + {1455,297,140 ,1917,1918,1919 }, {1192,398,117 ,8,15,9 }, {946,945,847 ,1920,1889,1888 }, + {1764,1826,1763 ,1921,1900,1899 }, {1065,921,1340 ,821,1922,822 }, + {2192,170,392 ,1175,794,857 }, {991,990,903 ,1335,1435,1734 }, + {2149,2130,2197 ,1470,1505,1507 }, {2746,3263,8139 ,1517,252,1923 }, + {173,398,1192 ,12,15,8 }, {772,809,891 ,18,20,189 }, {4514,4483,4465 ,259,549,257 }, + {1038,1037,945 ,1924,1890,1889 }, {946,1038,945 ,1920,1924,1889 }, + {1038,1129,1037 ,1924,1891,1890 }, {1714,1764,1763 ,1898,1921,1899 }, + {1827,1826,1764 ,1925,1900,1921 }, {2110,2109,2064 ,1926,1905,1904 }, + {3997,4039,4038 ,380,382,1927 }, {3996,3997,4038 ,1928,380,1927 }, + {3999,4041,4040 ,1023,1063,460 }, {1106,1204,1203 ,1664,338,337 }, + {906,1046,1045 ,1019,1929,956 }, {909,910,1189 ,1109,1930,1495 }, + {3572,3590,3589 ,1931,1867,1932 }, {1302,1303,13 ,909,1933,910 }, + {1844,1873,1912 ,911,913,964 }, {340,4223,593 ,1107,1934,1108 }, + {1850,251,1517 ,601,603,600 }, {1334,3871,138 ,1935,397,434 }, + {4202,562,700 ,260,1936,255 }, {4682,1376,3899 ,1191,1937,1938 }, + {106,5139,5138 ,1073,1939,1940 }, {1245,57,735 ,36,222,224 }, + {463,467,360 ,1941,346,86 }, {3749,3662,4011 ,652,1942,395 }, + {1135,1285,5067 ,1943,1944,1945 }, {6898,6563,8063 ,1946,464,465 }, + {572,611,571 ,1947,1948,1885 }, {668,667,571 ,1949,1886,1885 }, + {611,668,571 ,1948,1949,1885 }, {764,763,667 ,1950,1887,1886 }, + {668,764,667 ,1949,1950,1886 }, {848,847,763 ,1951,1888,1887 }, + {764,848,763 ,1950,1951,1887 }, {947,946,847 ,1952,1920,1888 }, + {848,947,847 ,1951,1952,1888 }, {1039,1038,946 ,1953,1924,1920 }, + {947,1039,946 ,1952,1953,1920 }, {1039,1129,1038 ,1953,1891,1924 }, + {1039,1173,1129 ,1953,1954,1891 }, {1279,1278,1129 ,1955,1892,1891 }, + {1173,1279,1129 ,1954,1955,1891 }, {1362,1361,1278 ,1956,1893,1892 }, + {1279,1362,1278 ,1955,1956,1892 }, {1438,1437,1361 ,1957,1894,1893 }, + {1362,1438,1361 ,1956,1957,1893 }, {1507,1506,1437 ,1958,1895,1894 }, + {1438,1507,1437 ,1957,1958,1894 }, {1580,1579,1506 ,1959,1896,1895 }, + {1507,1580,1506 ,1958,1959,1895 }, {1580,1648,1647 ,1959,1960,1897 }, + {1579,1580,1647 ,1896,1959,1897 }, {1648,1715,1714 ,1960,1961,1898 }, + {1647,1648,1714 ,1897,1960,1898 }, {1715,1735,1714 ,1961,1962,1898 }, + {1735,1765,1764 ,1962,1963,1921 }, {1714,1735,1764 ,1898,1962,1921 }, + {1828,1827,1764 ,1964,1925,1921 }, {1765,1828,1764 ,1963,1964,1921 }, + {1864,1826,1827 ,1965,1900,1925 }, {1828,1864,1827 ,1964,1965,1925 }, + {1897,1896,1826 ,1966,1901,1900 }, {1864,1897,1826 ,1965,1966,1900 }, + {1962,1961,1896 ,1967,1902,1901 }, {1897,1962,1896 ,1966,1967,1901 }, + {2019,2018,1961 ,1968,1903,1902 }, {1962,2019,1961 ,1967,1968,1902 }, + {2065,2064,2018 ,1969,1904,1903 }, {2019,2065,2018 ,1968,1969,1903 }, + {2111,2110,2064 ,1970,1926,1904 }, {2065,2111,2064 ,1969,1970,1904 }, + {2144,2109,2110 ,1971,1905,1926 }, {2111,2144,2110 ,1970,1971,1926 }, + {2178,2177,2109 ,1972,1906,1905 }, {2144,2178,2109 ,1971,1972,1905 }, + {2178,734,780 ,1972,1973,1907 }, {2177,2178,780 ,1906,1972,1907 }, + {390,389,780 ,1974,1908,1907 }, {734,390,780 ,1973,1974,1907 }, + {1372,1409,389 ,1975,1909,1908 }, {390,1372,389 ,1974,1975,1908 }, + {615,1095,1409 ,766,1910,1909 }, {1372,615,1409 ,1975,766,1909 }, + {3430,3489,3429 ,1976,1977,1978 }, {3380,3430,3379 ,1514,1976,1979 }, + {4038,4039,2662 ,1927,382,805 }, {3489,3488,3429 ,1977,1980,1978 }, + {580,581,623 ,1280,1274,1100 }, {293,1181,1182 ,1430,1981,1235 }, + {1616,1683,1682 ,1150,1151,1010 }, {2037,2082,2036 ,1458,1520,887 }, + {239,2290,2292 ,1103,1982,1983 }, {906,992,905 ,1019,958,1729 }, + {157,91,223 ,1877,1984,1985 }, {681,302,14 ,620,622,1986 }, {694,4165,2477 ,1987,640,642 }, + {562,708,700 ,1936,401,255 }, {467,4110,503 ,346,372,374 }, {3293,7133,7110 ,1988,1989,1990 }, + {765,764,668 ,1991,1950,1949 }, {1174,1173,1039 ,1992,1954,1953 }, + {1174,1279,1173 ,1992,1955,1954 }, {1828,1897,1864 ,1964,1966,1965 }, + {1962,2065,2019 ,1967,1969,1968 }, {2111,2178,2144 ,1970,1972,1971 }, + {2243,2325,2324 ,1993,1994,1995 }, {3323,3355,3322 ,1051,1292,1182 }, + {1142,1233,1141 ,1334,1294,1498 }, {830,928,927 ,1996,1997,1441 }, + {829,830,927 ,1368,1996,1441 }, {526,582,525 ,718,719,1279 }, + {6577,8088,8094 ,97,1998,98 }, {2127,2126,2081 ,1553,890,888 }, + {1980,2036,2007 ,828,887,829 }, {1876,460,822 ,1386,1999,2000 }, + {4244,1599,4223 ,753,391,1934 }, {14,38,681 ,1986,742,620 }, + {3662,3749,5304 ,1942,652,654 }, {4631,5180,5136 ,1816,1878,1817 }, + {959,535,6686 ,1358,2001,750 }, {700,708,757 ,255,401,239 }, + {4187,3,1023 ,502,329,308 }, {765,848,764 ,1991,1951,1950 }, + {1280,1279,1174 ,2002,1955,1992 }, {1363,1362,1279 ,2003,1956,1955 }, + {1280,1363,1279 ,2002,2003,1955 }, {1898,1897,1828 ,2004,1966,1964 }, + {2231,734,2178 ,2005,1973,1972 }, {2697,3327,1211 ,2006,439,2007 }, + {254,2662,2472 ,2008,805,2009 }, {1013,1012,927 ,2010,1471,1441 }, + {928,1013,927 ,1997,2010,1441 }, {1686,1687,1780 ,1004,1104,900 }, + {1089,2893,4393 ,103,1679,463 }, {1260,1857,1311 ,11,13,213 }, + {1319,1521,729 ,322,2011,1548 }, {139,777,963 ,145,421,1341 }, + {2785,3878,3419 ,1246,1248,2012 }, {7515,7573,7538 ,1079,2013,2014 }, + {2141,8085,973 ,2015,2016,2017 }, {3534,8138,8081 ,942,2018,1445 }, + {766,765,668 ,2019,1991,1949 }, {669,766,668 ,2020,2019,1949 }, + {849,848,765 ,2021,1951,1991 }, {766,849,765 ,2019,2021,1991 }, + {948,947,848 ,2022,1952,1951 }, {849,948,848 ,2021,2022,1951 }, + {948,983,947 ,2022,2023,1952 }, {1082,1039,947 ,2024,1953,1952 }, + {983,1082,947 ,2023,2024,1952 }, {1175,1174,1039 ,2025,1992,1953 }, + {1082,1175,1039 ,2024,2025,1953 }, {1281,1280,1174 ,2026,2002,1992 }, + {1175,1281,1174 ,2025,2026,1992 }, {1364,1363,1280 ,2027,2003,2002 }, + {1281,1364,1280 ,2026,2027,2002 }, {1364,1362,1363 ,2027,1956,2003 }, + {1439,1438,1362 ,2028,1957,1956 }, {1364,1439,1362 ,2027,2028,1956 }, + {1508,1507,1438 ,2029,1958,1957 }, {1439,1508,1438 ,2028,2029,1957 }, + {1581,1580,1507 ,2030,1959,1958 }, {1508,1581,1507 ,2029,2030,1958 }, + {1581,1649,1648 ,2030,2031,1960 }, {1580,1581,1648 ,1959,2030,1960 }, + {1649,1716,1715 ,2031,2032,1961 }, {1648,1649,1715 ,1960,2031,1961 }, + {1717,1735,1715 ,2033,1962,1961 }, {1716,1717,1715 ,2032,2033,1961 }, + {1766,1765,1735 ,2034,1963,1962 }, {1717,1766,1735 ,2033,2034,1962 }, + {1766,1829,1828 ,2034,2035,1964 }, {1765,1766,1828 ,1963,2034,1964 }, + {1899,1898,1828 ,2036,2004,1964 }, {1829,1899,1828 ,2035,2036,1964 }, + {1930,1897,1898 ,2037,1966,2004 }, {1899,1930,1898 ,2036,2037,2004 }, + {1963,1962,1897 ,2038,1967,1966 }, {1930,1963,1897 ,2037,2038,1966 }, + {2066,2065,1962 ,2039,1969,1967 }, {1963,2066,1962 ,2038,2039,1967 }, + {2066,2078,2065 ,2039,2040,1969 }, {2112,2111,2065 ,2041,1970,1969 }, + {2078,2112,2065 ,2040,2041,1969 }, {2179,2178,2111 ,2042,1972,1970 }, + {2112,2179,2111 ,2041,2042,1970 }, {2232,2231,2178 ,2043,2005,1972 }, + {2179,2232,2178 ,2042,2043,1972 }, {1448,734,2231 ,2044,1973,2005 }, + {2232,1448,2231 ,2043,2044,2005 }, {775,390,734 ,94,1974,1973 }, + {1448,775,734 ,2044,94,1973 }, {654,1372,390 ,93,1975,1974 }, + {775,654,390 ,94,93,1974 }, {1372,654,615 ,1975,93,766 }, {3080,13,2472 ,908,910,2009 }, + {2922,2923,2972 ,2045,2046,2047 }, {1061,1060,1012 ,2048,1472,1471 }, + {1013,1061,1012 ,2010,2048,1471 }, {1105,1104,1060 ,2049,1473,1472 }, + {1061,1105,1060 ,2048,2049,1472 }, {1521,730,729 ,2011,1549,1548 }, + {5180,5199,5179 ,1878,422,1879 }, {1683,1776,1775 ,1151,1431,1432 }, + {6647,5471,6611 ,2050,2051,1391 }, {1776,1777,1802 ,1431,856,2052 }, + {14,74,38 ,1986,2053,742 }, {3349,3422,3684 ,2054,2055,1915 }, + {3289,3307,3288 ,2056,2057,2058 }, {6139,4240,3797 ,2059,2060,2061 }, + {2045,30,211 ,417,307,415 }, {18,826,738 ,896,507,1674 }, {984,983,948 ,2062,2023,2022 }, + {849,984,948 ,2021,2062,2022 }, {1439,1440,1508 ,2028,2063,2029 }, + {1767,1766,1717 ,2064,2034,2033 }, {1716,1767,1717 ,2032,2064,2033 }, + {1330,654,1481 ,53,93,194 }, {2833,3054,3076 ,2065,916,2066 }, + {593,539,910 ,1108,2067,1930 }, {172,1743,3122 ,667,763,2068 }, + {394,440,1449 ,1600,1464,2069 }, {1615,1682,1614 ,1359,1010,2070 }, + {1239,1190,1449 ,1596,1112,2069 }, {1778,1840,1839 ,855,198,1138 }, + {786,963,777 ,2071,1341,421 }, {2588,74,14 ,2072,2053,1986 }, + {5453,5923,6118 ,937,2073,2074 }, {708,365,757 ,401,402,239 }, + {120,364,4362 ,240,1340,342 }, {365,364,120 ,402,1340,240 }, + {4362,139,962 ,342,145,233 }, {757,365,120 ,239,402,240 }, {5569,3121,5570 ,2075,2076,508 }, + {478,277,613 ,2077,85,298 }, {984,1082,983 ,2062,2024,2023 }, + {8079,8077,3421 ,55,2078,1156 }, {2093,4921,2257 ,553,2079,554 }, + {1536,1582,1581 ,2080,2081,2030 }, {1582,1650,1649 ,2081,2082,2031 }, + {1717,1716,1649 ,2033,2032,2031 }, {1672,1717,1649 ,2083,2033,2031 }, + {1327,1326,1232 ,996,1330,1295 }, {3592,3645,3591 ,2084,1650,1652 }, + {908,862,340 ,1111,1225,1107 }, {2029,2121,2028 ,1748,1598,1811 }, + {2127,2148,2126 ,1553,1561,890 }, {1615,1616,1682 ,1359,1150,1010 }, + {2196,2195,2147 ,1550,1851,1562 }, {1365,1440,1439 ,2085,2063,2028 }, + {5199,5219,5198 ,422,1860,423 }, {423,383,325 ,2086,2087,2088 }, + {4691,324,383 ,2089,2090,2087 }, {669,707,766 ,2020,2091,2019 }, + {850,849,766 ,2092,2021,2019 }, {767,850,766 ,2093,2092,2019 }, + {985,984,849 ,2094,2062,2021 }, {850,985,849 ,2092,2094,2021 }, + {985,1083,1082 ,2094,2095,2024 }, {984,985,1082 ,2062,2094,2024 }, + {1083,1176,1175 ,2095,2096,2025 }, {1082,1083,1175 ,2024,2095,2025 }, + {1176,1282,1281 ,2096,2097,2026 }, {1175,1176,1281 ,2025,2096,2026 }, + {1282,1365,1364 ,2097,2085,2027 }, {1281,1282,1364 ,2026,2097,2027 }, + {3347,4060,3317 ,2098,2099,1882 }, {1521,4244,730 ,2011,753,1549 }, + {4481,4480,4421 ,2100,2101,229 }, {2451,549,450 ,2102,1129,1653 }, + {3948,4326,3670 ,280,219,281 }, {3861,8097,8083 ,1147,1710,2103 }, + {732,283,871 ,2104,2105,2106 }, {4481,1674,4462 ,2100,2107,1076 }, + {1718,1716,1717 ,2108,2032,2033 }, {1768,1767,1716 ,2109,2064,2032 }, + {1718,1768,1716 ,2108,2109,2032 }, {1767,1768,1766 ,2064,2109,2034 }, + {1768,1830,1829 ,2109,2110,2035 }, {1766,1768,1829 ,2034,2109,2035 }, + {1830,1900,1899 ,2110,2111,2036 }, {1829,1830,1899 ,2035,2110,2036 }, + {1899,1900,1930 ,2036,2111,2037 }, {1900,1964,1963 ,2111,2112,2038 }, + {1930,1900,1963 ,2037,2111,2038 }, {2020,2066,1963 ,2113,2039,2038 }, + {1964,2020,1963 ,2112,2113,2038 }, {2079,2078,2066 ,2114,2040,2039 }, + {2020,2079,2066 ,2113,2114,2039 }, {2113,2112,2078 ,2115,2041,2040 }, + {2079,2113,2078 ,2114,2115,2040 }, {2180,2179,2112 ,2116,2042,2041 }, + {2113,2180,2112 ,2115,2116,2041 }, {2233,2232,2179 ,2117,2043,2042 }, + {2180,2233,2179 ,2116,2117,2042 }, {1371,1448,2232 ,2118,2044,2043 }, + {2233,1371,2232 ,2117,2118,2043 }, {231,775,1448 ,92,94,2044 }, + {1371,231,1448 ,2118,92,2044 }, {5180,5220,5199 ,1878,2119,422 }, + {5241,5240,5199 ,2120,481,422 }, {3699,3698,3645 ,2121,2122,1650 }, + {3646,3699,3645 ,2123,2121,1650 }, {7232,3288,7233 ,2124,2058,2125 }, + {26,70,460 ,2126,323,1999 }, {290,335,334 ,1042,1014,1267 }, + {2190,305,2189 ,1574,1463,1599 }, {1914,1943,1983 ,1000,2127,1001 }, + {2198,293,692 ,1506,1430,1429 }, {2197,2198,692 ,1507,1506,1429 }, + {1132,981,1591 ,61,2128,1072 }, {8118,8126,4835 ,2129,812,2130 }, + {8133,8107,2301 ,2131,658,2132 }, {965,799,1419 ,366,392,367 }, + {4131,2026,4251 ,1656,130,132 }, {767,766,707 ,2093,2019,2091 }, + {985,949,1083 ,2094,2133,2095 }, {5030,5061,1130 ,2134,2135,2136 }, + {324,4691,325 ,2090,2089,2088 }, {810,732,716 ,2137,2104,2138 }, + {4441,4481,4421 ,2139,2100,229 }, {412,736,1080 ,358,843,2140 }, + {770,1932,2352 ,275,2141,390 }, {2181,2180,2113 ,2142,2116,2115 }, + {5220,5241,5199 ,2119,2120,422 }, {2582,2585,3372 ,873,875,2143 }, + {3355,3381,3354 ,1292,871,1293 }, {3572,3573,3590 ,1931,980,1867 }, + {6230,3436,5876 ,2144,2145,2146 }, {815,857,903 ,999,2147,1734 }, + {1402,1403,1474 ,1780,974,1649 }, {1682,1681,1614 ,1010,1009,2070 }, + {1337,1845,1844 ,2148,912,911 }, {817,816,724 ,1020,998,722 }, + {1182,1181,1049 ,1235,1981,1362 }, {3316,1295,2810 ,2149,618,1728 }, + {440,1239,1449 ,1464,1596,2069 }, {741,2730,740 ,1221,2150,1595 }, + {2029,2080,2121 ,1748,2151,1598 }, {4202,885,562 ,260,216,1936 }, + {326,423,325 ,2152,2086,2088 }, {479,514,423 ,2153,2154,2086 }, + {4378,4430,4354 ,375,341,376 }, {4214,4215,4254 ,226,370,416 }, + {892,1597,810 ,506,505,2137 }, {767,851,850 ,2093,2155,2092 }, + {58,8128,227 ,2156,959,2157 }, {4972,949,851 ,2158,2133,2155 }, + {850,949,985 ,2092,2133,2094 }, {5029,1130,1083 ,2159,2136,2095 }, + {1083,1130,1176 ,2095,2136,2096 }, {5029,5030,1130 ,2159,2134,2136 }, + {884,614,4095 ,2160,1397,349 }, {1080,459,980 ,2140,696,2161 }, + {732,738,283 ,2104,1674,2105 }, {4180,1258,412 ,2162,2163,358 }, + {1319,980,1521 ,322,2161,2011 }, {732,871,716 ,2104,2106,2138 }, + {1224,1904,3048 ,1508,2164,2165 }, {4426,8100,6837 ,1056,1713,1057 }, + {283,1057,871 ,2105,354,2106 }, {1830,1865,1900 ,2110,2166,2111 }, + {8085,8091,8067 ,2016,2167,2168 }, {5285,5284,5240 ,2169,571,481 }, + {3404,2582,3372 ,2170,873,2143 }, {862,999,960 ,1225,798,1326 }, + {2193,506,170 ,1007,1381,794 }, {989,990,1140 ,1813,1435,1499 }, + {1912,1913,1980 ,964,963,828 }, {1722,1686,1779 ,1668,1004,1275 }, + {1404,1476,1475 ,975,859,858 }, {1290,2009,1918 ,795,1224,1110 }, + {5370,5328,6091 ,2171,2172,2173 }, {8107,8137,3006 ,658,2174,414 }, + {280,279,4692 ,2175,2176,2177 }, {5241,5285,5240 ,2120,2169,481 }, + {424,423,326 ,2178,2086,2152 }, {424,479,423 ,2178,2153,2086 }, + {515,514,479 ,2179,2154,2153 }, {575,574,514 ,2180,2181,2154 }, + {515,575,514 ,2179,2180,2154 }, {1223,4160,57 ,2182,223,222 }, + {635,1854,2657 ,2183,2184,2185 }, {1258,2014,736 ,2163,842,843 }, + {1080,980,25 ,2140,2161,2186 }, {1258,736,412 ,2163,843,358 }, + {4471,5295,4472 ,2187,2188,2189 }, {4354,4430,4402 ,376,341,2190 }, + {1866,1865,1830 ,2191,2166,2110 }, {1866,1901,1900 ,2191,2192,2111 }, + {1865,1866,1900 ,2166,2191,2111 }, {1901,1902,1900 ,2192,2193,2111 }, + {1965,1964,1900 ,2194,2112,2111 }, {1902,1965,1900 ,2193,2194,2111 }, + {1965,1998,1964 ,2194,2195,2112 }, {2021,2020,1964 ,2196,2113,2112 }, + {1998,2021,1964 ,2195,2196,2112 }, {2021,2079,2020 ,2196,2114,2113 }, + {2114,2113,2079 ,2197,2115,2114 }, {2021,2114,2079 ,2196,2197,2114 }, + {2182,2181,2113 ,2198,2142,2115 }, {2114,2182,2113 ,2197,2198,2115 }, + {2215,2180,2181 ,2199,2116,2142 }, {2182,2215,2181 ,2198,2199,2142 }, + {9,2233,2180 ,2200,2117,2116 }, {2215,9,2180 ,2199,2200,2116 }, + {9,1371,2233 ,2200,2118,2117 }, {232,231,1371 ,2201,92,2118 }, + {9,232,1371 ,2200,2201,2118 }, {1094,1481,231 ,1398,194,92 }, + {232,1094,231 ,2201,1398,92 }, {2147,2195,2194 ,1562,1851,1853 }, + {2198,2216,293 ,1506,1555,1430 }, {1744,1059,3932 ,1666,678,1096 }, + {2009,862,908 ,1224,1225,1111 }, {388,437,339 ,2202,1149,1143 }, + {388,339,239 ,2202,1143,1103 }, {1144,1143,991 ,957,1333,1335 }, + {1329,1406,1405 ,1134,1135,995 }, {259,410,455 ,2203,2204,2205 }, + {817,859,816 ,1020,1733,998 }, {1345,1093,840 ,203,66,68 }, {196,124,607 ,152,2206,2207 }, + {576,4907,673 ,2208,2209,2210 }, {4525,892,1673 ,623,506,1686 }, + {4160,1163,2012 ,223,2211,2212 }, {810,1597,18 ,2137,505,896 }, + {5599,5640,5639 ,2213,2214,2215 }, {736,459,1080 ,843,696,2140 }, + {5598,5599,5639 ,2216,2213,2215 }, {5237,5573,5307 ,1800,2217,461 }, + {1206,138,1734 ,2218,434,2219 }, {4520,136,4496 ,1673,1685,2220 }, + {4473,4520,4496 ,270,1673,2220 }, {1831,1866,1830 ,2221,2191,2110 }, + {2182,9,2215 ,2198,2200,2199 }, {1052,1094,232 ,2222,1398,2201 }, + {5339,5338,5284 ,2223,63,571 }, {5285,5339,5284 ,2169,2223,571 }, + {2033,2068,2124 ,1288,891,1006 }, {2068,2125,2124 ,891,892,1006 }, + {1144,1142,1143 ,957,1334,1333 }, {336,385,432 ,1013,864,1222 }, + {1722,1779,1778 ,1668,1275,855 }, {2080,2067,2121 ,2151,1592,1598 }, + {2029,2067,2080 ,1748,1592,2151 }, {239,339,338 ,1103,1143,867 }, + {628,627,528 ,1177,670,1055 }, {3697,3698,3743 ,2224,2122,2225 }, + {582,581,525 ,719,1274,1279 }, {327,326,281 ,2226,2152,2227 }, + {327,384,326 ,2226,2228,2152 }, {425,424,326 ,2229,2178,2152 }, + {384,425,326 ,2228,2229,2152 }, {480,479,424 ,2230,2153,2178 }, + {425,480,424 ,2229,2230,2178 }, {516,515,479 ,2231,2179,2153 }, + {480,516,479 ,2230,2231,2153 }, {576,575,515 ,2208,2180,2179 }, + {516,576,515 ,2231,2208,2179 }, {575,576,673 ,2180,2208,2210 }, + {5817,5834,5816 ,701,543,545 }, {892,810,1673 ,506,2137,1686 }, + {4442,4441,4391 ,991,2139,992 }, {283,738,358 ,2105,1674,897 }, + {459,4412,980 ,696,744,2161 }, {46,739,5 ,2232,2233,350 }, {883,146,76 ,119,504,689 }, + {1057,283,188 ,354,2105,2234 }, {1903,1966,1965 ,406,405,2194 }, + {1902,1903,1965 ,2193,406,2194 }, {1999,1998,1965 ,2235,2195,2194 }, + {1966,1999,1965 ,405,2235,2194 }, {2022,2021,1998 ,2236,2196,2195 }, + {1999,2022,1998 ,2235,2236,2195 }, {2115,2114,2021 ,2237,2197,2196 }, + {2022,2115,2021 ,2236,2237,2196 }, {2115,2145,2114 ,2237,2238,2197 }, + {2183,2182,2114 ,2239,2198,2197 }, {2145,2183,2114 ,2238,2239,2197 }, + {2183,9,2182 ,2239,2200,2198 }, {1513,232,9 ,2240,2201,2200 }, + {1632,76,129 ,2241,689,730 }, {2013,58,877 ,2242,2156,2243 }, + {728,819,726 ,1266,2244,669 }, {1153,6797,8123 ,2245,2246,2247 }, + {1182,1049,1048 ,1235,1362,1236 }, {335,336,431 ,1014,1013,1268 }, + {901,903,989 ,1428,1734,1813 }, {5339,4916,4915 ,2223,2248,64 }, + {1868,3760,1783 ,2249,2250,2251 }, {3784,4884,4239 ,2252,2253,2254 }, + {5338,5339,4915 ,63,2223,64 }, {4513,1673,4481 ,624,1686,2100 }, + {1295,3316,3348 ,618,2149,2255 }, {4244,4223,340 ,753,1934,1107 }, + {730,4244,340 ,1549,753,1107 }, {1114,1115,2047 ,2256,2257,2258 }, + {4480,4462,4421 ,2101,1076,229 }, {798,5103,5378 ,78,77,359 }, + {40,9,2183 ,2259,2200,2239 }, {40,163,9 ,2259,2260,2200 }, {233,1513,9 ,2261,2240,2200 }, + {163,233,9 ,2260,2261,2200 }, {1514,232,1513 ,2262,2201,2240 }, + {233,1514,1513 ,2261,2262,2240 }, {1055,1052,232 ,2263,2222,2201 }, + {1514,1055,232 ,2262,2263,2201 }, {1054,1094,1052 ,2264,1398,2222 }, + {1055,1054,1052 ,2263,2264,2222 }, {1054,865,1094 ,2264,1119,1398 }, + {2893,3946,5307 ,1679,1230,461 }, {227,1482,58 ,2157,2265,2156 }, + {1238,860,1000 ,1569,1385,1384 }, {305,1239,440 ,1463,1596,1464 }, + {1657,1656,1552 ,1781,1166,2266 }, {814,815,902 ,1054,999,1427 }, + {8092,8120,8130 ,2267,906,905 }, {2131,2200,2199 ,656,1570,1554 }, + {817,905,859 ,1020,1729,1733 }, {2032,2033,2123 ,1127,1288,1008 }, + {3946,4227,5307 ,1230,1564,461 }, {249,1664,268 ,644,2268,645 }, + {1234,1328,1327 ,1178,994,996 }, {864,865,867 ,840,1119,1121 }, + {8140,8063,8102 ,2269,465,253 }, {577,576,516 ,2270,2208,2231 }, + {576,577,674 ,2208,2270,2271 }, {358,4184,2014 ,897,898,842 }, + {6553,6295,6646 ,780,1290,1133 }, {1441,23,4181 ,1485,2272,2273 }, + {1927,870,710 ,1196,2274,33 }, {810,716,1674 ,2137,2138,2107 }, + {593,1599,539 ,1108,391,2067 }, {4152,4180,4448 ,2275,2162,2276 }, + {358,4180,4152 ,897,2162,2275 }, {188,358,4152 ,2234,897,2275 }, + {4480,4481,4462 ,2101,2100,1076 }, {941,119,1049 ,2277,2278,1362 }, + {188,4152,4138 ,2234,2275,278 }, {4152,188,4138 ,2275,2234,278 }, + {1674,716,4512 ,2107,2138,2279 }, {6509,6531,8092 ,1452,2280,2267 }, + {4060,4064,3317 ,2099,1880,1882 }, {1999,1966,2022 ,2235,405,2236 }, + {233,1055,1514 ,2261,2263,2262 }, {1055,865,1054 ,2263,1119,2264 }, + {2137,867,776 ,147,1121,1120 }, {3758,3757,3711 ,1789,2281,1790 }, + {3194,3016,3189 ,1065,1028,1027 }, {442,441,357 ,673,609,672 }, + {1910,1911,1867 ,1223,1101,1504 }, {1049,1181,1050 ,1362,1981,2282 }, + {1802,1777,1838 ,2052,856,425 }, {336,432,431 ,1013,1222,1268 }, + {5305,5572,3147 ,2283,2284,2285 }, {904,991,903 ,1730,1335,1734 }, + {1406,1407,1447 ,1135,1269,1136 }, {8114,8108,6797 ,2286,2287,2246 }, + {437,436,339 ,1149,1142,1143 }, {1190,1189,1449 ,1112,1495,2069 }, + {8081,8093,8072 ,1445,2288,511 }, {940,1910,1867 ,197,1223,1504 }, + {328,426,425 ,2289,2290,2229 }, {384,328,425 ,2228,2289,2229 }, + {426,481,480 ,2290,2291,2230 }, {425,426,480 ,2229,2290,2230 }, + {481,517,516 ,2291,2292,2231 }, {480,481,516 ,2230,2291,2231 }, + {578,577,516 ,2293,2270,2231 }, {517,578,516 ,2292,2293,2231 }, + {675,676,677 ,2294,2295,2296 }, {577,578,675 ,2270,2293,2294 }, + {738,189,358 ,1674,503,897 }, {661,1441,4181 ,2297,1485,2273 }, + {4513,4481,4463 ,624,2100,993 }, {5,739,413 ,350,2233,351 }, + {980,4244,1521 ,2161,753,2011 }, {1424,379,608 ,2298,2299,2300 }, + {416,604,502 ,556,486,2301 }, {25,980,1319 ,2186,2161,322 }, + {4448,4180,412 ,2276,2162,358 }, {138,878,1734 ,434,398,2219 }, + {5558,3121,5569 ,2302,2076,2075 }, {4430,4473,4402 ,341,270,2190 }, + {879,3397,3370 ,1155,136,2303 }, {4152,4448,4138 ,2275,2276,278 }, + {4108,4148,4124 ,2304,2305,2306 }, {8131,8075,8107 ,2307,2308,658 }, + {1967,1968,1966 ,404,2309,405 }, {1968,2023,2022 ,2309,2310,2236 }, + {1966,1968,2022 ,405,2309,2236 }, {2116,2115,2022 ,2311,2237,2236 }, + {2023,2116,2022 ,2310,2311,2236 }, {2116,2145,2115 ,2311,2238,2237 }, + {2184,2183,2145 ,2312,2239,2238 }, {2116,2184,2145 ,2311,2312,2238 }, + {83,40,2183 ,2313,2259,2239 }, {2184,83,2183 ,2312,2313,2239 }, + {1001,163,40 ,2314,2260,2259 }, {83,1001,40 ,2313,2314,2259 }, + {234,233,163 ,2315,2261,2260 }, {1001,234,163 ,2314,2315,2260 }, + {1053,1055,233 ,2316,2263,2261 }, {234,1053,233 ,2315,2316,2261 }, + {776,865,1055 ,1120,1119,2263 }, {1053,776,1055 ,2316,1120,2263 }, + {5194,1102,1454 ,727,2317,2318 }, {1744,3932,2088 ,1666,1096,1667 }, + {1721,1722,1778 ,854,1668,855 }, {2197,692,933 ,1507,1429,1496 }, + {1775,1776,1802 ,1432,1431,2052 }, {1240,1918,1190 ,796,1110,1112 }, + {1446,1447,1478 ,1137,1136,2319 }, {3880,3868,3831 ,2320,970,2321 }, + {3832,3880,3831 ,2322,2320,2321 }, {2191,392,2190 ,1174,857,1574 }, + {4587,2320,8020 ,774,1406,1405 }, {1843,1911,1910 ,901,1101,1223 }, + {5013,2256,6484 ,684,2323,685 }, {1867,2390,940 ,1504,1594,197 }, + {578,579,676 ,2293,2324,2295 }, {1338,1257,4214 ,1077,208,226 }, + {4215,4255,4254 ,370,165,416 }, {4412,314,980 ,744,578,2161 }, + {810,18,732 ,2137,896,2104 }, {980,314,4244 ,2161,578,753 }, + {4464,4513,4463 ,584,624,993 }, {1673,810,1674 ,1686,2137,2107 }, + {4463,4481,4441 ,993,2100,2139 }, {8066,8092,8098 ,960,2267,907 }, + {1225,490,1223 ,327,469,2182 }, {412,1080,69 ,358,2140,321 }, + {4150,412,69 ,320,358,321 }, {880,1441,661 ,1483,1485,2297 }, + {3644,3697,3696 ,1651,2224,2325 }, {188,4152,4138 ,2234,2275,278 }, + {83,234,1001 ,2313,2315,2314 }, {782,776,1053 ,1648,1120,2316 }, + {1691,1670,1733 ,2326,2327,2328 }, {392,1240,305 ,857,796,1463 }, + {2122,2191,2190 ,1128,1174,1574 }, {2067,2122,2121 ,1592,1128,1598 }, + {1479,1477,1478 ,1270,1045,2319 }, {1602,1610,749 ,2329,2330,2331 }, + {1478,1477,1405 ,2319,1045,995 }, {3704,6140,6169 ,2332,2333,2334 }, + {1365,1439,1364 ,2085,2028,2027 }, {3918,3917,3868 ,2335,968,970 }, + {517,579,578 ,2292,2324,2293 }, {2223,6484,2256 ,2336,685,2323 }, + {5834,1942,141 ,543,2337,681 }, {1462,4177,4116 ,2338,2339,2340 }, + {130,89,150 ,2341,1482,1481 }, {136,1628,883 ,1685,118,119 }, + {4441,4421,4367 ,2139,229,228 }, {1677,1676,1610 ,2342,2343,2330 }, + {69,1080,25 ,321,2140,2186 }, {3643,3644,3696 ,2344,1651,2325 }, + {1069,188,4138 ,277,2234,278 }, {1660,1053,234 ,2345,2316,2315 }, + {1378,3148,2743 ,925,814,923 }, {3880,3918,3868 ,2320,2335,970 }, + {1094,865,864 ,1398,1119,840 }, {1802,1838,1837 ,2052,425,997 }, + {1634,1423,396 ,768,151,153 }, {742,741,645 ,972,1221,940 }, + {1915,1916,1943 ,1437,832,2127 }, {1914,1915,1943 ,1000,1437,2127 }, + {1682,1683,1775 ,1010,1151,1432 }, {1446,1478,1405 ,1137,2319,995 }, + {1235,1329,1328 ,1179,1134,994 }, {3961,3960,3917 ,2346,1021,968 }, + {3918,3961,3917 ,2335,2346,968 }, {1145,1235,1234 ,1277,1179,1178 }, + {7151,3290,7175 ,1202,1204,2347 }, {329,427,426 ,2348,2349,2290 }, + {328,329,426 ,2289,2348,2290 }, {426,427,481 ,2290,2349,2291 }, + {427,518,517 ,2349,2350,2292 }, {481,427,517 ,2291,2349,2292 }, + {518,519,579 ,2350,2351,2324 }, {517,518,579 ,2292,2350,2324 }, + {519,617,579 ,2351,2352,2324 }, {579,617,677 ,2324,2352,2296 }, + {2026,870,1927 ,130,2274,1196 }, {732,18,738 ,2104,896,1674 }, + {4442,4463,4441 ,991,993,2139 }, {4068,4067,1253 ,408,185,2353 }, + {358,2014,4180 ,897,842,2162 }, {4391,4441,4367 ,992,2139,228 }, + {1226,1321,1320 ,2354,2355,2356 }, {1321,1398,1397 ,2355,2357,2358 }, + {1320,1321,1397 ,2356,2355,2358 }, {1397,1398,1445 ,2358,2357,2359 }, + {1398,1470,1469 ,2357,2360,2361 }, {1445,1398,1469 ,2359,2357,2361 }, + {1470,1542,1541 ,2360,2362,2363 }, {1469,1470,1541 ,2361,2360,2363 }, + {1611,1610,1541 ,2364,2330,2363 }, {1542,1611,1541 ,2362,2364,2363 }, + {1678,1677,1610 ,2365,2342,2330 }, {1611,1678,1610 ,2364,2365,2330 }, + {1720,1676,1677 ,2366,2343,2342 }, {1678,1720,1677 ,2365,2366,2342 }, + {1720,1771,4817 ,2366,2367,2368 }, {4138,4448,412 ,278,2276,358 }, + {1674,4512,4462 ,2107,2279,1076 }, {6837,8088,6577 ,1057,1998,97 }, + {4036,109,2151 ,1602,1654,1603 }, {1969,2024,2023 ,2369,2370,2310 }, + {1968,1969,2023 ,2309,2369,2310 }, {2024,2117,2116 ,2370,2371,2311 }, + {2023,2024,2116 ,2310,2370,2311 }, {2185,2184,2116 ,2372,2312,2311 }, + {2117,2185,2116 ,2371,2372,2311 }, {152,83,2184 ,2373,2313,2312 }, + {2185,152,2184 ,2372,2373,2312 }, {1288,234,83 ,2374,2315,2313 }, + {152,1288,83 ,2373,2374,2313 }, {861,1660,234 ,2375,2345,2315 }, + {1288,861,234 ,2374,2375,2315 }, {998,1053,1660 ,2376,2316,2345 }, + {861,998,1660 ,2375,2376,2345 }, {786,782,1053 ,2071,1648,2316 }, + {998,786,1053 ,2376,2071,2316 }, {4327,1427,561 ,2377,2378,2379 }, + {169,1880,1296 ,248,250,335 }, {652,169,1296 ,334,248,335 }, + {4000,4042,4041 ,1022,1061,1063 }, {3999,4000,4041 ,1023,1022,1063 }, + {3961,4001,4000 ,2346,2380,1022 }, {2129,2069,2130 ,1469,1711,1505 }, + {1981,2037,2036 ,1456,1458,887 }, {858,904,903 ,1731,1730,1734 }, + {647,742,645 ,973,972,940 }, {1943,1916,1983 ,2127,832,1001 }, + {1916,1984,1983 ,832,831,1001 }, {3372,2585,3453 ,2143,875,917 }, + {1046,1145,1045 ,1929,1277,956 }, {6098,5113,6174 ,1565,141,2381 }, + {518,427,519 ,2350,2349,2351 }, {870,1396,710 ,2274,34,33 }, + {1673,1674,4481 ,1686,2107,2100 }, {716,1675,4512 ,2138,2382,2279 }, + {4138,412,44 ,278,358,357 }, {2474,2072,8096 ,874,1812,1796 }, + {5021,1969,5019 ,2383,2369,2384 }, {5019,1969,1968 ,2384,2369,2309 }, + {2186,2185,2117 ,2385,2372,2371 }, {861,786,998 ,2375,2071,2376 }, + {3712,3711,3653 ,1439,1790,1440 }, {6169,6141,6154 ,2334,2386,2387 }, + {3356,3355,3323 ,872,1292,1051 }, {3960,3961,4000 ,1021,2346,1022 }, + {1801,1802,1837 ,1433,2052,997 }, {725,817,724 ,721,1020,722 }, + {2518,448,546 ,2388,1172,1171 }, {2069,2038,2530 ,1711,1551,2389 }, + {1784,1848,1847 ,1662,1436,1519 }, {647,645,646 ,973,940,954 }, + {723,724,815 ,1053,722,999 }, {815,816,857 ,999,998,2147 }, {3698,3744,3743 ,2122,2390,2225 }, + {237,337,336 ,869,866,1013 }, {519,618,617 ,2351,2391,2352 }, + {855,897,951 ,2392,2393,2394 }, {897,952,951 ,2393,2395,2394 }, + {4180,2014,1258 ,2162,842,2163 }, {952,5034,951 ,2395,2396,2394 }, + {1321,1369,1398 ,2355,2397,2357 }, {1678,1771,1720 ,2365,2367,2366 }, + {1596,188,1069 ,355,2234,277 }, {4536,4452,4820 ,89,2398,2399 }, + {393,4042,4000 ,2400,1061,1022 }, {4001,393,4000 ,2380,2400,1022 }, + {632,341,1184 ,1380,1852,797 }, {341,1373,1184 ,1852,1501,797 }, + {1682,1775,1739 ,1010,1432,1011 }, {1475,1547,1546 ,858,860,1360 }, + {2083,2069,2128 ,1552,1711,1468 }, {2069,2129,2128 ,1711,1469,1468 }, + {7175,3290,7196 ,2347,1204,2401 }, {857,858,903 ,2147,1731,1734 }, + {816,858,857 ,998,1731,2147 }, {1325,1326,1402 ,1331,1330,1780 }, + {1303,1302,687 ,1933,909,1665 }, {330,331,329 ,2402,2403,2348 }, + {331,428,427 ,2403,2404,2349 }, {329,331,427 ,2348,2403,2349 }, + {428,520,519 ,2404,2405,2351 }, {427,428,519 ,2349,2404,2351 }, + {520,619,618 ,2405,2406,2391 }, {519,520,618 ,2351,2405,2391 }, + {718,717,618 ,2407,2408,2391 }, {619,718,618 ,2406,2407,2391 }, + {717,718,855 ,2408,2407,2392 }, {1042,1041,952 ,2409,2410,2395 }, + {898,1042,952 ,2411,2409,2395 }, {1136,1135,1041 ,2412,1943,2410 }, + {1042,1136,1041 ,2409,2412,2410 }, {1227,1226,1135 ,2413,2354,1943 }, + {1136,1227,1135 ,2412,2413,1943 }, {1227,1322,1321 ,2413,2414,2355 }, + {1226,1227,1321 ,2354,2413,2355 }, {1321,1322,1369 ,2355,2414,2397 }, + {1322,1399,1398 ,2414,2415,2357 }, {1369,1322,1398 ,2397,2414,2357 }, + {1399,1471,1470 ,2415,2416,2360 }, {1398,1399,1470 ,2357,2415,2360 }, + {1471,1543,1542 ,2416,2417,2362 }, {1470,1471,1542 ,2360,2416,2362 }, + {1543,1612,1611 ,2417,2418,2364 }, {1542,1543,1611 ,2362,2417,2364 }, + {1679,1678,1611 ,2419,2365,2364 }, {1612,1679,1611 ,2418,2419,2364 }, + {1772,1771,1678 ,2420,2367,2365 }, {1679,1772,1678 ,2419,2420,2365 }, + {1835,1834,1771 ,2421,2422,2367 }, {1772,1835,1771 ,2420,2421,2367 }, + {322,6203,843 ,1250,1249,2423 }, {3380,3379,3353 ,1514,1979,2424 }, + {3354,3380,3353 ,1293,1514,2424 }, {4644,4740,1970 ,2425,2426,2427 }, + {1970,2000,1969 ,2427,2428,2369 }, {1969,2000,2024 ,2369,2428,2370 }, + {8090,8065,8080 ,412,96,1094 }, {2118,2117,2024 ,2429,2371,2370 }, + {2025,2118,2024 ,2430,2429,2370 }, {2187,2186,2117 ,2431,2385,2371 }, + {2118,2187,2117 ,2429,2431,2371 }, {2187,2185,2186 ,2431,2372,2385 }, + {161,152,2185 ,2432,2373,2372 }, {2187,161,2185 ,2431,2432,2372 }, + {1193,1288,152 ,2433,2374,2373 }, {161,1193,152 ,2432,2433,2373 }, + {1193,1287,1288 ,2433,2434,2374 }, {907,861,1288 ,2435,2375,2374 }, + {1287,907,1288 ,2434,2435,2374 }, {629,786,861 ,2436,2071,2375 }, + {907,629,861 ,2435,2436,2375 }, {629,962,786 ,2436,233,2071 }, + {3356,3381,3355 ,872,871,1292 }, {3004,2946,4042 ,2437,1062,1061 }, + {8106,8135,3449 ,2438,2439,2440 }, {529,628,528 ,1148,1177,1055 }, + {1848,1915,1914 ,1436,1437,1000 }, {1847,1848,1914 ,1519,1436,1000 }, + {2476,2310,3844 ,2441,2442,2443 }, {1686,1780,1779 ,1004,900,1275 }, + {236,237,336 ,2444,869,1013 }, {330,2393,286 ,2402,2445,2446 }, + {520,521,619 ,2405,2447,2406 }, {8115,8136,8140 ,2448,2449,2269 }, + {1519,79,2544 ,2450,1510,2451 }, {4021,4835,8106 ,1712,2130,2438 }, + {4446,1335,2607 ,237,236,2452 }, {5833,5834,141 ,544,543,681 }, + {3430,3429,3379 ,1976,1978,1979 }, {488,832,1302 ,533,613,909 }, + {3080,488,1302 ,908,533,909 }, {1057,188,1596 ,354,2234,355 }, + {871,594,893 ,2106,2453,2454 }, {716,871,893 ,2138,2106,2454 }, + {393,3004,4042 ,2400,2437,1061 }, {8139,3263,8089 ,1923,252,254 }, + {1876,822,1985 ,1386,2000,1547 }, {822,729,1985 ,2000,1548,1547 }, + {1685,1686,1722 ,1005,1004,1668 }, {2193,632,506 ,1007,1380,1381 }, + {1684,1721,1683 ,1327,854,1151 }, {2131,2201,2200 ,656,2455,1570 }, + {2193,2194,341 ,1007,1853,1852 }, {1184,1373,999 ,797,1501,798 }, + {1779,1842,1841 ,1275,196,1276 }, {286,331,330 ,2446,2403,2402 }, + {428,521,520 ,2404,2447,2405 }, {916,913,863 ,445,444,284 }, + {283,358,188 ,2105,897,2234 }, {1227,1286,1322 ,2413,2456,2414 }, + {1773,1772,1679 ,2457,2420,2419 }, {1871,1835,1872 ,2458,2421,2459 }, + {8074,8092,8066 ,2460,2267,960 }, {3354,3353,3321 ,1293,2424,2461 }, + {2796,304,3080 ,2462,532,908 }, {1675,716,893 ,2382,2138,2454 }, + {1193,907,1287 ,2433,2435,2434 }, {820,629,907 ,2463,2436,2435 }, + {6102,3831,3868 ,2464,2321,970 }, {3004,2087,2571 ,2437,2465,1064 }, + {1006,1100,799 ,234,2466,392 }, {729,822,1319 ,1548,2000,322 }, + {1146,1145,1046 ,2467,1277,1929 }, {1047,1146,1046 ,1328,2467,1929 }, + {1474,1475,1546 ,1649,858,1360 }, {1373,1000,999 ,1501,1384,798 }, + {3264,8144,2301 ,2468,2469,2132 }, {1000,1876,999 ,1384,1386,798 }, + {287,332,331 ,2470,2471,2403 }, {332,429,428 ,2471,2472,2404 }, + {331,332,428 ,2403,2471,2404 }, {429,522,521 ,2472,2473,2447 }, + {428,429,521 ,2404,2472,2447 }, {522,620,619 ,2473,2474,2406 }, + {521,522,619 ,2447,2473,2406 }, {720,718,719 ,2475,2407,2476 }, + {619,620,718 ,2406,2474,2407 }, {812,899,898 ,559,2477,2411 }, + {811,812,898 ,557,559,2411 }, {1043,898,899 ,2478,2411,2477 }, + {1043,1137,1136 ,2478,2479,2412 }, {1137,1228,1227 ,2479,2480,2413 }, + {1136,1137,1227 ,2412,2479,2413 }, {1228,1229,1286 ,2480,2481,2456 }, + {1227,1228,1286 ,2413,2480,2456 }, {1229,1323,1322 ,2481,2482,2414 }, + {1286,1229,1322 ,2456,2481,2414 }, {1323,1400,1399 ,2482,2483,2415 }, + {1322,1323,1399 ,2414,2482,2415 }, {1400,1472,1471 ,2483,2484,2416 }, + {1399,1400,1471 ,2415,2483,2416 }, {1472,1544,1543 ,2484,2485,2417 }, + {1471,1472,1543 ,2416,2484,2417 }, {1613,1612,1543 ,2486,2418,2417 }, + {1544,1613,1543 ,2485,2486,2417 }, {1613,1680,1679 ,2486,2487,2419 }, + {1612,1613,1679 ,2418,2486,2419 }, {1680,1774,1773 ,2487,2488,2457 }, + {1679,1680,1773 ,2419,2487,2457 }, {1800,1772,1773 ,2489,2420,2457 }, + {1774,1800,1773 ,2488,2489,2457 }, {1836,1835,1772 ,2490,2421,2420 }, + {1800,1836,1772 ,2489,2490,2420 }, {1836,1872,1835 ,2490,2459,2421 }, + {296,630,3475 ,1558,2491,1559 }, {2027,2000,2002 ,2492,2428,2493 }, + {1675,893,4512 ,2382,2454,2279 }, {8069,8100,3482 ,1095,1713,2494 }, + {2119,2118,2025 ,2495,2429,2430 }, {2027,2119,2025 ,2492,2495,2430 }, + {2188,2187,2118 ,2496,2431,2429 }, {2119,2188,2118 ,2495,2496,2429 }, + {179,161,2187 ,2497,2432,2431 }, {2188,179,2187 ,2496,2497,2431 }, + {1374,1193,161 ,2498,2433,2432 }, {179,1374,161 ,2497,2498,2432 }, + {954,907,1193 ,2499,2435,2433 }, {1374,954,1193 ,2498,2499,2433 }, + {914,820,907 ,2500,2463,2435 }, {954,914,907 ,2499,2500,2435 }, + {487,629,820 ,2501,2436,2463 }, {914,487,820 ,2500,2501,2463 }, + {1100,962,629 ,2466,233,2436 }, {487,1100,629 ,2501,2466,2436 }, + {3491,3490,3461 ,955,2502,1152 }, {2946,3004,2571 ,1062,2437,1064 }, + {3931,3684,3664 ,1868,1915,1557 }, {1843,1844,1911 ,901,911,1101 }, + {8068,8071,3264 ,1784,738,2468 }, {2087,132,3016 ,2465,2503,1028 }, + {909,593,910 ,1109,1108,1930 }, {1233,1232,1141 ,1294,1295,1498 }, + {1447,1479,1478 ,1136,1270,2319 }, {1844,1912,1911 ,911,964,1101 }, + {3434,3494,3463 ,737,736,1271 }, {2571,2087,3016 ,1064,2465,1028 }, + {1775,1802,1801 ,1432,2052,1433 }, {1876,1985,999 ,1386,1547,798 }, + {718,720,4954 ,2407,2475,558 }, {4258,4217,85 ,84,83,2504 }, + {6853,6905,6892 ,2505,2506,2507 }, {1137,1229,1228 ,2479,2481,2480 }, + {1302,832,1744 ,909,613,1666 }, {832,1059,1744 ,613,678,1666 }, + {222,179,2188 ,2508,2497,2496 }, {2222,2210,6563 ,2509,2510,464 }, + {132,2837,584 ,2503,2511,2512 }, {2364,3342,3343 ,2513,2514,2515 }, + {1548,1617,1616 ,902,904,1150 }, {239,238,2290 ,1103,868,1982 }, + {822,460,70 ,2000,1999,323 }, {3698,3697,3644 ,2122,2224,1651 }, + {5988,5972,4147 ,2516,399,2517 }, {620,720,719 ,2474,2475,2476 }, + {5309,505,469 ,987,986,988 }, {1137,1180,1229 ,2479,2518,2481 }, + {1349,2002,1971 ,2519,2493,2520 }, {3647,3646,3594 ,2521,2123,2522 }, + {2698,4369,5335 ,105,104,2523 }, {3016,132,584 ,1028,2503,2512 }, + {626,725,625 ,801,721,720 }, {290,291,335 ,1042,1012,1014 }, + {992,991,904 ,958,1335,1730 }, {1302,1744,687 ,909,1666,1665 }, + {1978,2034,1977 ,847,1289,1503 }, {289,333,332 ,1044,1043,2471 }, + {333,430,429 ,1043,2524,2472 }, {332,333,429 ,2471,1043,2472 }, + {430,482,429 ,2524,2525,2472 }, {482,523,522 ,2525,2526,2473 }, + {429,482,522 ,2472,2525,2473 }, {523,621,620 ,2526,2527,2474 }, + {522,523,620 ,2473,2526,2474 }, {621,721,720 ,2527,2528,2475 }, + {620,621,720 ,2474,2527,2475 }, {721,813,812 ,2528,2529,559 }, + {720,721,812 ,2475,2528,559 }, {813,900,899 ,2529,2530,2477 }, + {812,813,899 ,559,2529,2477 }, {900,988,899 ,2530,2531,2477 }, + {988,1044,1043 ,2531,2532,2478 }, {899,988,1043 ,2477,2531,2478 }, + {1044,1138,1137 ,2532,2533,2479 }, {1043,1044,1137 ,2478,2532,2479 }, + {1138,1139,1180 ,2533,2534,2518 }, {1137,1138,1180 ,2479,2533,2518 }, + {1139,1230,1229 ,2534,2535,2481 }, {1180,1139,1229 ,2518,2534,2481 }, + {1230,1324,1323 ,2535,2536,2482 }, {1229,1230,1323 ,2481,2535,2482 }, + {1324,1401,1400 ,2536,2537,2483 }, {1323,1324,1400 ,2482,2536,2483 }, + {1473,1472,1400 ,2538,2484,2483 }, {1401,1473,1400 ,2537,2538,2483 }, + {1473,1545,1544 ,2538,2539,2485 }, {1472,1473,1544 ,2484,2538,2485 }, + {1614,1613,1544 ,2070,2486,2485 }, {1545,1614,1544 ,2539,2070,2485 }, + {1614,1681,1680 ,2070,1009,2487 }, {1613,1614,1680 ,2486,2070,2487 }, + {1681,1739,1680 ,1009,1011,2487 }, {1739,1774,1680 ,1011,2488,2487 }, + {1801,1800,1774 ,1433,2489,2488 }, {1739,1801,1774 ,1011,1433,2488 }, + {1801,1837,1836 ,1433,997,2490 }, {1800,1801,1836 ,2489,1433,2490 }, + {1837,380,1836 ,997,2540,2490 }, {5497,3350,4530 ,1375,2541,1376 }, + {2003,2002,1349 ,2542,2493,2519 }, {1349,1971,1948 ,2519,2520,2543 }, + {2028,2027,2002 ,1811,2492,2493 }, {2003,2028,2002 ,2542,1811,2493 }, + {2028,2120,2119 ,1811,1597,2495 }, {2027,2028,2119 ,2492,1811,2495 }, + {2189,2188,2119 ,1599,2496,2495 }, {2120,2189,2119 ,1597,1599,2495 }, + {243,222,2188 ,1465,2508,2496 }, {2189,243,2188 ,1599,1465,2496 }, + {394,179,222 ,1600,2497,2508 }, {243,394,222 ,1465,1600,2508 }, + {1449,1374,179 ,2069,2498,2497 }, {394,1449,179 ,1600,2069,2497 }, + {1449,910,954 ,2069,1930,2499 }, {1374,1449,954 ,2498,2069,2499 }, + {539,914,954 ,2067,2500,2499 }, {910,539,954 ,1930,2067,2499 }, + {77,487,914 ,2544,2501,2500 }, {539,77,914 ,2067,2544,2500 }, + {1194,1100,487 ,393,2466,2501 }, {77,1194,487 ,2544,393,2501 }, + {1194,799,1100 ,393,392,2466 }, {3699,3700,3745 ,2121,2545,2546 }, + {2837,2501,108 ,2511,2547,1336 }, {584,2837,108 ,2512,2511,1336 }, + {2501,107,108 ,2547,2548,1336 }, {929,1014,1013 ,1234,1233,2010 }, + {928,929,1013 ,1997,1234,2010 }, {1014,1062,1061 ,1233,1232,2048 }, + {1013,1014,1061 ,2010,1233,2048 }, {1062,1106,1105 ,1232,1664,2049 }, + {1061,1062,1105 ,2048,1232,2049 }, {1105,1106,1202 ,2049,1664,339 }, + {304,2796,2662 ,532,2462,805 }, {3868,3916,3879 ,970,969,2549 }, + {239,387,339 ,1103,1102,1143 }, {621,622,721 ,2527,2550,2528 }, + {1044,1139,1138 ,2532,2534,2533 }, {391,1623,1088 ,1647,1709,1604 }, + {108,107,2134 ,1336,2548,1337 }, {2243,2324,2242 ,1993,1995,2551 }, + {2121,2190,2189 ,1598,1574,1599 }, {1189,910,1449 ,1495,1930,2069 }, + {107,751,35 ,2548,1365,1338 }, {2134,107,35 ,1337,2548,1338 }, + {5698,5697,5679 ,44,2552,2553 }, {523,622,621 ,2526,2550,2527 }, + {1044,988,1139 ,2532,2531,2534 }, {7271,7270,7229 ,2554,2555,2556 }, + {3547,3546,3494 ,803,2557,736 }, {2742,2029,2003 ,2558,1748,2542 }, + {539,1194,77 ,2067,393,2544 }, {7139,7161,7138 ,1025,2559,2560 }, + {2005,2031,2742 ,1593,1560,2558 }, {290,334,333 ,1042,1267,1043 }, + {334,431,430 ,1267,1268,2524 }, {333,334,430 ,1043,1267,2524 }, + {431,483,482 ,1268,1228,2525 }, {430,431,482 ,2524,1268,2525 }, + {483,524,523 ,1228,1278,2526 }, {482,483,523 ,2525,1228,2526 }, + {524,623,622 ,1278,1100,2550 }, {523,524,622 ,2526,1278,2550 }, + {623,722,721 ,1100,1052,2528 }, {622,623,721 ,2550,1100,2528 }, + {814,813,721 ,1054,2529,2528 }, {722,814,721 ,1052,1054,2528 }, + {901,900,813 ,1428,2530,2529 }, {814,901,813 ,1054,1428,2529 }, + {901,989,988 ,1428,1813,2531 }, {900,901,988 ,2530,1428,2531 }, + {989,1140,1139 ,1813,1499,2534 }, {988,989,1139 ,2531,1813,2534 }, + {1140,1231,1230 ,1499,1332,2535 }, {1139,1140,1230 ,2534,1499,2535 }, + {1231,1325,1324 ,1332,1331,2536 }, {1230,1231,1324 ,2535,1332,2536 }, + {1402,1401,1324 ,1780,2537,2536 }, {1325,1402,1324 ,1331,1780,2536 }, + {1474,1473,1401 ,1649,2538,2537 }, {1402,1474,1401 ,1780,1649,2537 }, + {1474,1546,1545 ,1649,1360,2539 }, {1473,1474,1545 ,2538,1649,2539 }, + {1546,1615,1614 ,1360,1359,2070 }, {1545,1546,1614 ,2539,1360,2070 }, + {2293,2358,292 ,2561,2562,2563 }, {2292,2293,292 ,1983,2561,2563 }, + {253,966,921 ,2564,2565,1922 }, {7129,7139,7138 ,1026,1025,2560 }, + {406,2408,2219 ,2566,1399,177 }, {8085,8073,973 ,2016,2567,2017 }, + {2421,4056,2420 ,2568,1123,273 }, {3315,1195,5194 ,944,725,727 }, + {247,253,921 ,2569,2564,1922 }, {8143,8123,8128 ,2570,2247,959 }, + {2363,2364,2440 ,2571,2513,2572 }, {2914,2895,2857 ,2573,2574,2575 }, + {2437,2436,2358 ,2576,2577,2562 }, {2928,2927,2883 ,2578,516,2579 }, + {3152,3196,1619 ,2580,2581,1003 }, {2626,2732,2625 ,2582,2583,2584 }, + {3151,3152,1550 ,2585,2580,2586 }, {1479,3151,1550 ,1270,2585,2586 }, + {1550,3152,1619 ,2586,2580,1003 }, {2942,554,2366 ,2587,2588,2589 }, + {2857,2765,2914 ,2575,2590,2573 }, {2880,2881,2924 ,2591,2592,2593 }, + {2840,3250,2875 ,2594,2595,2596 }, {2895,2380,2581 ,2574,2597,2598 }, + {3197,3196,3152 ,2599,2581,2580 }, {2515,2599,2598 ,2600,2601,2602 }, + {2890,2889,2848 ,2603,2604,2605 }, {1010,2890,2848 ,1286,2603,2605 }, + {1630,56,3081 ,1016,2606,1017 }, {2635,2388,2389 ,2607,2608,2609 }, + {2952,2489,2570 ,2610,2611,2612 }, {2430,91,2133 ,2613,1984,2614 }, + {6951,6870,6796 ,2615,2616,2617 }, {2804,2786,1062 ,2618,2619,1232 }, + {2786,2829,1062 ,2619,2620,1232 }, {2879,2878,2846 ,2621,2622,2623 }, + {2847,2879,2846 ,2624,2621,2623 }, {2736,2737,2786 ,2625,2626,2619 }, + {2544,2744,3137 ,2451,2627,2628 }, {1201,1200,1103 ,1477,2629,1474 }, + {2508,2507,388 ,2630,2631,2202 }, {6945,6944,6913 ,2632,2633,2634 }, + {2566,2526,2527 ,2635,2636,2637 }, {2566,2614,2526 ,2635,2638,2636 }, + {2476,1304,2300 ,2441,2639,2640 }, {3357,3356,3323 ,883,872,1051 }, + {1010,2848,925 ,1286,2605,1287 }, {2738,2737,2685 ,2641,2626,2642 }, + {2787,2786,2737 ,2643,2619,2626 }, {2738,2787,2737 ,2641,2643,2626 }, + {2684,2683,2612 ,2644,2645,2646 }, {437,2559,2590 ,1149,2647,2648 }, + {2900,2633,2300 ,2649,2650,2640 }, {3596,3648,3595 ,1792,734,927 }, + {2507,2508,2559 ,2631,2630,2647 }, {2508,2590,2559 ,2630,2648,2647 }, + {630,3317,1377 ,2491,1882,2651 }, {2787,2788,2786 ,2643,2652,2619 }, + {4587,135,2320 ,774,2653,1406 }, {2830,2829,2786 ,2654,2620,2619 }, + {2788,2830,2786 ,2652,2654,2619 }, {2830,2866,1107 ,2654,2655,2656 }, + {2829,2830,1107 ,2620,2654,2656 }, {2684,2737,2683 ,2644,2626,2645 }, + {2737,2736,2683 ,2626,2625,2645 }, {2633,2552,2300 ,2650,2657,2640 }, + {2507,437,388 ,2631,1149,2202 }, {2507,2559,437 ,2631,2647,1149 }, + {6435,6480,6479 ,2658,1524,2659 }, {406,2224,746 ,2566,2660,2661 }, + {2265,2297,2296 ,2662,2663,2664 }, {2613,2612,2526 ,2665,2646,2636 }, + {172,244,534 ,667,606,632 }, {2725,2726,2724 ,2666,2667,2668 }, + {172,2543,244 ,667,2669,606 }, {2833,3981,3075 ,2065,915,2670 }, + {6321,7251,3288 ,706,2671,2058 }, {2583,2629,685 ,2672,2673,2674 }, + {2718,2717,2665 ,2675,2676,2677 }, {549,2522,2609 ,1129,2678,1130 }, + {2769,2847,2846 ,2679,2624,2623 }, {6479,6480,6507 ,2659,1524,2680 }, + {2718,2770,2769 ,2675,2681,2679 }, {6809,6860,6859 ,712,473,472 }, + {4672,5892,5878 ,2682,2683,2684 }, {2343,8097,8061 ,2685,1710,453 }, + {966,3039,1090 ,2565,2686,2687 }, {819,818,726 ,2244,1018,669 }, + {3468,3439,3440 ,2688,2689,2690 }, {2445,448,2518 ,1388,1172,2388 }, + {2717,2718,2769 ,2676,2675,2679 }, {691,647,2609 ,1173,973,1130 }, + {921,966,1090 ,1922,2565,2687 }, {1047,2921,1146 ,1328,2691,2467 }, + {3154,3197,3153 ,2692,2599,2693 }, {978,1030,1077 ,1620,1619,2694 }, + {1479,1407,1480 ,1270,1269,2695 }, {7196,3288,7232 ,2401,2058,2124 }, + {2770,2815,2769 ,2681,2696,2679 }, {5328,2238,5301 ,2172,2697,2698 }, + {589,590,244 ,671,607,606 }, {2543,589,244 ,2669,671,606 }, {649,175,702 ,1579,211,2699 }, + {3180,2967,2385 ,2700,2701,2702 }, {2141,4985,6707 ,2015,2703,2704 }, + {2671,2724,2723 ,2705,2668,2706 }, {2193,2125,2563 ,1007,892,2707 }, + {3700,3746,3745 ,2545,2708,2546 }, {8140,8102,3263 ,2269,253,252 }, + {3019,3058,1407 ,2709,2710,1269 }, {204,271,203 ,1722,1608,2711 }, + {2769,2815,2847 ,2679,2696,2624 }, {2636,5650,5452 ,2712,2713,2714 }, + {2343,1074,8073 ,2685,2715,2567 }, {744,743,691 ,1367,971,1173 }, + {1370,3019,1407 ,1500,2709,1269 }, {2553,171,395 ,2716,921,966 }, + {3236,2792,3269 ,2717,2718,2719 }, {2084,860,953 ,1361,1385,1237 }, + {8076,2810,1295 ,617,1728,618 }, {2637,165,1238 ,2720,1869,1569 }, + {2664,2717,628 ,2721,2676,1177 }, {584,108,1294 ,2512,1336,635 }, + {3122,2543,172 ,2068,2669,667 }, {2543,2897,589 ,2669,2722,671 }, + {2897,442,589 ,2722,673,671 }, {289,332,288 ,1044,2471,2723 }, + {3352,3353,3378 ,2724,2424,1442 }, {1098,2760,1096 ,2725,2726,2727 }, + {292,388,239 ,2563,2202,1103 }, {2129,2131,2130 ,1469,656,1505 }, + {6039,6134,5488 ,2728,2729,2730 }, {4881,4293,4957 ,2731,2732,2733 }, + {1477,1550,1549 ,1045,2586,903 }, {4827,4887,5334 ,2734,2735,2736 }, + {2041,1743,553 ,2737,763,792 }, {3273,2543,3122 ,2738,2669,2068 }, + {3273,2623,2543 ,2738,2739,2669 }, {2623,2404,2897 ,2739,2740,2722 }, + {2543,2623,2897 ,2669,2739,2722 }, {2404,442,2897 ,2740,673,2722 }, + {2515,2514,2442 ,2600,2741,2742 }, {748,2541,241 ,1677,1298,314 }, + {252,966,1158 ,2743,2565,2744 }, {3461,3490,3430 ,1152,2502,1976 }, + {4924,1166,7073 ,2745,2746,662 }, {7074,4924,7073 ,2747,2745,662 }, + {3247,6430,144 ,1494,2748,853 }, {7344,7343,7285 ,2749,2750,2751 }, + {2903,2425,1411 ,2752,2753,850 }, {597,2903,1411 ,791,2752,850 }, + {2425,2759,2041 ,2753,2754,2737 }, {1411,2425,2041 ,850,2753,2737 }, + {2759,2458,1743 ,2754,2755,763 }, {2041,2759,1743 ,2737,2754,763 }, + {3122,2623,3273 ,2068,2739,2738 }, {2404,2479,442 ,2740,2756,673 }, + {2842,310,442 ,2757,674,673 }, {2479,2842,442 ,2756,2757,673 }, + {2842,2694,310 ,2757,811,674 }, {4887,5377,5376 ,2735,361,2758 }, + {3353,3379,3378 ,2424,1979,1442 }, {2728,2727,2676 ,2759,2760,2761 }, + {2870,2528,2367 ,2762,2763,2764 }, {2666,2719,2718 ,2765,2766,2675 }, + {2687,2635,2761 ,2767,2607,2768 }, {2718,2719,2770 ,2675,2766,2681 }, + {1407,1406,1370 ,1269,1135,1500 }, {8092,6531,8120 ,2267,2280,906 }, + {2614,2612,2613 ,2638,2646,2665 }, {3153,3197,3152 ,2693,2599,2580 }, + {2597,2640,2596 ,2769,2770,2771 }, {2372,597,2654 ,2772,791,2773 }, + {2372,2903,597 ,2772,2752,791 }, {2458,2546,3122 ,2755,2774,2068 }, + {1743,2458,3122 ,763,2755,2068 }, {2546,2699,3122 ,2774,2775,2068 }, + {3122,2699,2623 ,2068,2775,2739 }, {2479,2627,2842 ,2756,2776,2757 }, + {2627,2694,2842 ,2776,811,2757 }, {2411,2528,2870 ,2777,2763,2762 }, + {2424,2411,2870 ,2778,2777,2762 }, {2635,3250,2388 ,2607,2595,2608 }, + {2556,2430,2388 ,2779,2613,2608 }, {2931,2979,2978 ,2780,2781,2782 }, + {3136,2372,2654 ,2783,2772,2773 }, {2372,2425,2903 ,2772,2753,2752 }, + {2458,2457,2546 ,2755,2784,2774 }, {2546,2828,2623 ,2774,2785,2739 }, + {2699,2546,2623 ,2775,2774,2739 }, {2854,2703,2479 ,2786,2787,2756 }, + {2404,2854,2479 ,2740,2786,2756 }, {2479,2703,2627 ,2756,2787,2776 }, + {2615,2614,2566 ,2788,2638,2635 }, {198,127,199 ,1392,1395,2789 }, + {3236,3269,2456 ,2717,2719,2790 }, {2649,3114,2536 ,2791,2792,2793 }, + {2687,2761,2857 ,2767,2768,2575 }, {2840,2388,3250 ,2594,2608,2595 }, + {3089,3112,3132 ,2794,2795,2796 }, {3181,2425,2372 ,2797,2753,2772 }, + {2758,2759,2425 ,2798,2754,2753 }, {3181,2758,2425 ,2797,2798,2753 }, + {2758,2457,2458 ,2798,2784,2755 }, {2759,2758,2458 ,2754,2798,2755 }, + {2828,2992,2623 ,2785,2799,2739 }, {2992,2348,2404 ,2799,2800,2740 }, + {2623,2992,2404 ,2739,2799,2740 }, {2404,2348,2854 ,2740,2800,2786 }, + {3513,2582,3404 ,441,873,2170 }, {2739,2738,2685 ,2801,2641,2642 }, + {2705,2739,2685 ,2802,2801,2642 }, {3048,1904,3049 ,2165,2164,2803 }, + {2728,2756,2727 ,2759,2804,2760 }, {3090,3994,3993 ,2805,2806,2807 }, + {3112,3090,3993 ,2795,2805,2807 }, {1840,940,1869 ,198,197,2808 }, + {3128,3169,3127 ,2809,2810,2811 }, {2616,3136,2548 ,2812,2783,2813 }, + {2486,2485,7615 ,2814,2815,2816 }, {2616,2372,3136 ,2812,2772,2783 }, + {2642,2758,3181 ,2817,2798,2797 }, {2288,2642,3181 ,2818,2817,2797 }, + {2758,2642,2457 ,2798,2817,2784 }, {2546,2992,2828 ,2774,2799,2785 }, + {2348,2757,2703 ,2800,2819,2787 }, {2854,2348,2703 ,2786,2800,2787 }, + {2757,2632,2627 ,2819,2820,2776 }, {2703,2757,2627 ,2787,2819,2776 }, + {2627,2689,145 ,2776,2821,810 }, {162,238,100 ,2822,868,2823 }, + {2777,2823,2822 ,2824,2825,2826 }, {2788,2787,2738 ,2652,2643,2641 }, + {2739,2788,2738 ,2801,2652,2641 }, {3069,3070,3111 ,2827,2828,2829 }, + {2780,2779,2727 ,2830,2831,2760 }, {2756,2780,2727 ,2804,2830,2760 }, + {2528,3183,3135 ,2763,2832,2833 }, {2367,2528,3135 ,2764,2763,2833 }, + {2838,2372,2616 ,2834,2772,2812 }, {2838,2996,2372 ,2834,2835,2772 }, + {2288,3181,2372 ,2818,2797,2772 }, {2996,2288,2372 ,2835,2818,2772 }, + {2642,2605,2457 ,2817,2836,2784 }, {2605,3225,2546 ,2836,2837,2774 }, + {2457,2605,2546 ,2784,2836,2774 }, {2546,3225,2992 ,2774,2837,2799 }, + {2348,2574,2757 ,2800,2838,2819 }, {2574,2448,2632 ,2838,2839,2820 }, + {2757,2574,2632 ,2819,2838,2820 }, {2632,2448,2627 ,2820,2839,2776 }, + {2448,2553,2689 ,2839,2716,2821 }, {2627,2448,2689 ,2776,2839,2821 }, + {5698,5699,5726 ,44,43,515 }, {2622,2549,2567 ,2840,2841,2842 }, + {2890,2936,2889 ,2603,2843,2604 }, {2830,2867,2866 ,2654,2844,2655 }, + {2692,3081,56 ,2845,1017,2606 }, {2534,198,199 ,2846,1392,2789 }, + {2764,2839,3142 ,2847,2848,2849 }, {1907,1248,3878 ,1245,2850,1248 }, + {3142,2839,90 ,2849,2848,2851 }, {2344,2554,2702 ,2852,2853,2854 }, + {2401,2995,2702 ,2855,2856,2854 }, {2554,2401,2702 ,2853,2855,2854 }, + {3070,3069,3031 ,2828,2827,2857 }, {3032,3070,3031 ,2858,2828,2857 }, + {2804,1062,929 ,2618,1232,1234 }, {3097,2838,2616 ,2859,2834,2812 }, + {2834,3097,2616 ,2860,2859,2812 }, {3097,2996,2838 ,2859,2835,2834 }, + {2904,2288,2996 ,2861,2818,2835 }, {3097,2904,2996 ,2859,2861,2835 }, + {2606,2642,2288 ,2862,2817,2818 }, {2904,2606,2288 ,2861,2862,2818 }, + {2606,2783,2605 ,2862,2863,2836 }, {2642,2606,2605 ,2817,2862,2836 }, + {2783,2519,3225 ,2863,2864,2837 }, {2605,2783,3225 ,2836,2863,2837 }, + {3225,2519,2992 ,2837,2864,2799 }, {2767,299,2348 ,2865,2866,2800 }, + {2992,2767,2348 ,2799,2865,2800 }, {2348,299,2574 ,2800,2866,2838 }, + {8083,8099,5771 ,2103,2867,2868 }, {2820,2819,2773 ,2869,2870,2871 }, + {369,199,271 ,1521,2789,1608 }, {2152,821,2136 ,1545,2872,1546 }, + {5013,5950,8108 ,684,2873,2287 }, {1285,1135,1226 ,1944,1943,2354 }, + {2205,2222,6563 ,2874,2509,464 }, {2839,2202,90 ,2848,2875,2851 }, + {1249,2488,2617 ,2876,2877,2878 }, {2488,2542,2371 ,2877,2879,2880 }, + {2617,2488,2371 ,2878,2877,2880 }, {2269,2506,2505 ,2881,2882,2883 }, + {3207,3237,3206 ,2884,2885,2886 }, {2736,2786,929 ,2625,2619,1234 }, + {2735,2736,929 ,2887,2625,1234 }, {2855,3097,2834 ,2888,2859,2860 }, + {2519,2766,2992 ,2864,2889,2799 }, {2992,2766,2767 ,2799,2889,2865 }, + {3020,3021,3059 ,2890,2891,2892 }, {2665,2717,2664 ,2677,2676,2721 }, + {2006,2005,2742 ,1181,1593,2558 }, {2633,2656,2584 ,2650,2893,2894 }, + {2656,2331,2584 ,2893,2895,2894 }, {3054,2763,1787 ,916,2896,2897 }, + {2504,2331,2461 ,2898,2895,2899 }, {5914,6098,5191 ,1566,1565,2900 }, + {3236,3237,2792 ,2717,2885,2718 }, {1663,3081,115 ,1015,1017,2901 }, + {2786,2804,929 ,2619,2618,1234 }, {3317,1804,3347 ,1882,2902,2098 }, + {3191,2403,4961 ,2903,2904,2905 }, {2403,2970,2834 ,2904,2906,2860 }, + {2970,2855,2834 ,2906,2888,2860 }, {2855,2904,3097 ,2888,2861,2859 }, + {2783,2520,2519 ,2863,2907,2864 }, {2519,2520,2766 ,2864,2907,2889 }, + {2766,3015,299 ,2889,2908,2866 }, {2767,2766,299 ,2865,2889,2866 }, + {3015,3078,299 ,2908,2909,2866 }, {3078,2752,2574 ,2909,2910,2838 }, + {299,3078,2574 ,2866,2909,2838 }, {2752,2951,2448 ,2910,2911,2839 }, + {2574,2752,2448 ,2838,2910,2839 }, {2951,2378,2553 ,2911,2912,2716 }, + {2448,2951,2553 ,2839,2911,2716 }, {2553,2378,171 ,2716,2912,921 }, + {1687,1686,3196 ,1104,1004,2581 }, {2886,2885,2821 ,2913,2914,2915 }, + {2774,2773,2721 ,2916,2871,2917 }, {2526,2612,2565 ,2636,2646,2918 }, + {2626,2839,2764 ,2582,2848,2847 }, {411,2495,2899 ,2919,2920,2921 }, + {3223,2495,2911 ,2922,2920,2923 }, {2899,2495,3223 ,2921,2920,2922 }, + {2495,2366,2911 ,2920,2589,2923 }, {2911,2366,1336 ,2923,2589,2924 }, + {2371,2656,2633 ,2880,2893,2650 }, {3747,3746,3700 ,2925,2708,2545 }, + {2946,2338,4040 ,1062,458,460 }, {2696,2970,2403 ,2926,2906,2904 }, + {2696,2855,2970 ,2926,2888,2906 }, {2603,2904,2855 ,2927,2861,2888 }, + {2898,2606,2904 ,2928,2862,2861 }, {2603,2898,2904 ,2927,2928,2861 }, + {2898,2784,2783 ,2928,2929,2863 }, {2606,2898,2783 ,2862,2928,2863 }, + {2783,2784,2520 ,2863,2929,2907 }, {2766,3043,3015 ,2889,2930,2908 }, + {3015,3043,3078 ,2908,2930,2909 }, {3043,2712,2752 ,2930,2931,2910 }, + {3078,3043,2752 ,2909,2930,2910 }, {2641,2951,2752 ,2932,2911,2910 }, + {2712,2641,2752 ,2931,2932,2910 }, {2641,2498,2378 ,2932,2933,2912 }, + {2951,2641,2378 ,2911,2932,2912 }, {5681,5699,2453 ,2934,43,42 }, + {2638,2592,2666 ,2935,2936,2765 }, {2526,2565,2525 ,2636,2918,2937 }, + {8116,8109,8119 ,172,2938,184 }, {1088,1452,1592 ,1604,1606,2939 }, + {2820,2884,2883 ,2869,2940,2579 }, {2444,2443,2368 ,2941,2942,2943 }, + {2715,3095,3013 ,2944,2945,2946 }, {3179,2617,2503 ,2947,2878,2948 }, + {2666,2667,2719 ,2765,2949,2766 }, {987,3452,3977 ,2950,1794,440 }, + {3327,1387,3636 ,439,2951,1060 }, {1336,785,2085 ,2924,2952,657 }, + {3061,3101,3100 ,2953,2954,2955 }, {2868,2869,2907 ,1577,1576,111 }, + {3237,3236,3206 ,2885,2717,2886 }, {2165,2696,2403 ,1041,2926,2904 }, + {2969,2855,2696 ,2956,2888,2926 }, {2483,2898,2603 ,2957,2928,2927 }, + {3186,2483,2603 ,2958,2957,2927 }, {3220,2784,2898 ,2959,2929,2928 }, + {2483,3220,2898 ,2957,2959,2928 }, {2784,3220,2520 ,2929,2959,2907 }, + {2520,2917,2766 ,2907,2960,2889 }, {2766,2917,3043 ,2889,2960,2930 }, + {2498,2496,171 ,2933,2961,921 }, {2378,2498,171 ,2912,2933,921 }, + {3264,2301,872 ,2468,2132,2962 }, {2492,493,2496 ,2963,1785,2961 }, + {1390,131,151 ,2964,2965,2966 }, {3215,3013,2354 ,2967,2946,2968 }, + {4240,3796,3797 ,2060,2969,2061 }, {8084,8127,8110 ,1139,2970,1140 }, + {2631,2840,2586 ,2971,2594,2972 }, {2380,2586,2581 ,2597,2972,2598 }, + {2599,2600,2673 ,2601,2973,2974 }, {2600,2674,2673 ,2973,2975,2974 }, + {3596,3595,3545 ,1792,927,926 }, {3595,3594,3544 ,927,2522,1153 }, + {1002,2431,1283 ,2976,2977,2978 }, {2363,2362,2297 ,2571,2979,2663 }, + {2362,2363,2439 ,2979,2571,2980 }, {2363,2440,2439 ,2571,2572,2980 }, + {2440,2512,2439 ,2572,2981,2980 }, {2671,2670,2595 ,2705,2982,2983 }, + {3138,2537,1050 ,2984,2985,2282 }, {2515,2600,2599 ,2600,2973,2601 }, + {3504,3468,3441 ,2986,2688,2987 }, {2166,2947,2696 ,1040,2988,2926 }, + {2165,2166,2696 ,1041,1040,2926 }, {2253,2603,2855 ,2989,2927,2888 }, + {2969,2253,2855 ,2956,2989,2888 }, {2253,3186,2603 ,2989,2958,2927 }, + {3220,2561,2520 ,2959,2990,2907 }, {2561,2562,2520 ,2990,2991,2907 }, + {2520,2562,2917 ,2907,2991,2960 }, {3043,2539,2712 ,2930,2992,2931 }, + {2539,2716,2712 ,2992,2993,2931 }, {2490,2641,2712 ,2994,2932,2931 }, + {2716,2490,2712 ,2993,2994,2931 }, {2490,2498,2641 ,2994,2933,2932 }, + {2845,2844,2800 ,2995,1329,2996 }, {2977,2999,2976 ,2997,2998,2999 }, + {2364,2441,2440 ,2513,3000,2572 }, {111,258,1653 ,1154,3001,106 }, + {4543,4547,4980 ,3002,242,241 }, {2929,2930,2928 ,3003,3004,2578 }, + {2493,2467,1789 ,3005,3006,3007 }, {2434,2400,2545 ,1118,1117,3008 }, + {3492,3491,3461 ,930,955,1152 }, {2673,2674,2700 ,2974,2975,3009 }, + {2653,2964,2131 ,3010,3011,656 }, {3382,3432,3407 ,870,929,3012 }, + {1049,119,2084 ,1362,2278,1361 }, {2570,2489,2298 ,2612,2611,3013 }, + {2723,2722,2669 ,2706,3014,3015 }, {2671,2723,2669 ,2705,2706,3015 }, + {2671,2669,2670 ,2705,3015,2982 }, {4554,6299,1989 ,1307,760,3016 }, + {355,250,301 ,1876,1916,1857 }, {2947,2969,2696 ,2988,2956,2926 }, + {2919,2253,2969 ,3017,2989,2956 }, {2253,2483,3186 ,2989,2957,2958 }, + {3220,2562,2561 ,2959,2991,2990 }, {2562,114,2917 ,2991,3018,2960 }, + {2989,3043,2917 ,3019,2930,2960 }, {114,2989,2917 ,3018,3019,2960 }, + {2989,2539,3043 ,3019,2992,2930 }, {2751,2498,2490 ,3020,2933,2994 }, + {2751,2492,2496 ,3020,2963,2961 }, {2498,2751,2496 ,2933,3020,2961 }, + {3431,3461,3430 ,3021,1152,1976 }, {2133,157,250 ,2614,1877,1916 }, + {925,2848,2802 ,1287,2605,3022 }, {3123,3153,3152 ,3023,2693,2580 }, + {4563,2382,126 ,3024,3025,1394 }, {1047,2877,2921 ,1328,3026,2691 }, + {3212,2942,2366 ,3027,2587,2589 }, {3154,1590,3198 ,2692,3028,1168 }, + {1480,3123,3152 ,2695,3023,2580 }, {2625,2732,2839 ,2584,2583,2848 }, + {2732,3116,2839 ,2583,3029,2848 }, {3462,3461,3431 ,931,1152,3021 }, + {835,8127,4106 ,3030,2970,1742 }, {2862,1984,1917 ,3031,831,833 }, + {2069,2530,1984 ,1711,2389,831 }, {2862,2069,1984 ,3031,1711,831 }, + {1238,1000,1515 ,1569,1384,1502 }, {2570,2298,2963 ,2612,3013,3032 }, + {2537,2677,1050 ,2985,3033,2282 }, {1972,2003,1349 ,3034,2542,2519 }, + {8109,8110,8127 ,2938,1140,2970 }, {2619,2969,2947 ,3035,2956,2988 }, + {2089,2619,2947 ,1325,3035,2988 }, {2619,2919,2969 ,3035,3017,2956 }, + {2253,2491,2483 ,2989,3036,2957 }, {2491,3214,3220 ,3036,3037,2959 }, + {2483,2491,3220 ,2957,3036,2959 }, {3214,2562,3220 ,3037,2991,2959 }, + {2482,2539,2989 ,3038,2992,3019 }, {2858,2873,2492 ,3039,3040,2963 }, + {2751,2858,2492 ,3020,3039,2963 }, {2873,494,2492 ,3040,3041,2963 }, + {6430,3777,5733 ,2748,1582,1584 }, {3228,1725,178 ,3042,1747,3043 }, + {3221,8127,835 ,183,2970,3030 }, {3116,3219,2839 ,3029,3044,2848 }, + {2988,1336,2653 ,3045,2924,3010 }, {2604,2964,2653 ,3046,3011,3010 }, + {2768,221,1553 ,1297,1296,1669 }, {3698,3699,3744 ,2122,2121,2390 }, + {2475,3215,2793 ,3047,2967,3048 }, {3433,3432,3382 ,3049,929,870 }, + {3957,3997,3996 ,295,380,1928 }, {3956,3957,3996 ,1383,295,1928 }, + {2343,8085,8067 ,2685,2016,2168 }, {3432,3462,3431 ,929,931,3021 }, + {1337,1844,1782 ,2148,911,1105 }, {2537,2572,968 ,2985,3050,1661 }, + {2851,2908,2503 ,3051,3052,2948 }, {2908,3179,2503 ,3052,2947,2948 }, + {5681,5659,5660 ,2934,3053,3054 }, {2943,2851,2503 ,3055,3051,2948 }, + {2919,2679,2253 ,3017,3056,2989 }, {2679,2463,2491 ,3056,3057,3036 }, + {2253,2679,2491 ,2989,3056,3036 }, {2463,2398,3214 ,3057,3058,3037 }, + {2491,2463,3214 ,3036,3057,3037 }, {2575,2562,3214 ,3059,2991,3037 }, + {2398,2575,3214 ,3058,3059,3037 }, {2575,114,2562 ,3059,3018,2991 }, + {2482,2989,114 ,3038,3019,3018 }, {2762,2716,2539 ,3060,2993,2992 }, + {2482,2762,2539 ,3038,3060,2992 }, {2762,3280,2716 ,3060,3061,2993 }, + {3092,2490,2716 ,3062,2994,2993 }, {3280,3092,2716 ,3061,3062,2993 }, + {3092,2858,2751 ,3062,3039,3020 }, {2490,3092,2751 ,2994,3062,3020 }, + {3192,3285,6689 ,3063,3064,3065 }, {2383,199,127 ,3066,2789,1395 }, + {4254,4255,4019 ,416,165,227 }, {2316,2383,2382 ,3067,3066,3025 }, + {2315,2316,2382 ,3068,3067,3025 }, {2383,127,2382 ,3066,1395,3025 }, + {4031,2650,2585 ,3069,914,875 }, {2646,2614,2615 ,3070,2638,2788 }, + {2686,2685,2614 ,3071,2642,2638 }, {925,2782,2730 ,1287,3072,2150 }, + {845,943,4970 ,1828,1807,3073 }, {2839,3219,2202 ,2848,3044,2875 }, + {3219,650,2202 ,3044,3074,2875 }, {3432,3431,3407 ,929,3021,3012 }, + {3543,3542,3488 ,3075,979,1980 }, {2576,1665,2967 ,3076,3077,2701 }, + {3745,3746,3744 ,2546,2708,2390 }, {2628,3180,2835 ,3078,2700,3079 }, + {2293,2263,2294 ,2561,3080,3081 }, {3592,3591,3542 ,2084,1652,979 }, + {3543,3592,3542 ,3075,2084,979 }, {1183,155,154 ,1238,1497,1466 }, + {2537,3138,2572 ,2985,2984,3050 }, {2619,2902,2919 ,3035,3082,3017 }, + {2919,2902,2679 ,3017,3082,3056 }, {3044,114,2575 ,3083,3018,3059 }, + {3044,2402,114 ,3083,3084,3018 }, {2402,2482,114 ,3084,3038,3018 }, + {2540,2762,2482 ,3085,3060,3038 }, {2762,2540,3280 ,3060,3085,3061 }, + {2794,3092,3280 ,3086,3062,3061 }, {2540,2794,3280 ,3085,3086,3061 }, + {2492,494,493 ,2963,3041,1785 }, {3904,3958,3903 ,1264,294,296 }, + {3183,219,1855 ,2832,3087,3088 }, {2646,2686,2614 ,3070,3071,2638 }, + {3671,978,1077 ,3089,1620,2694 }, {1304,2431,4377 ,2639,2977,3090 }, + {2686,2705,2685 ,3071,2802,2642 }, {2544,3137,2966 ,2451,2628,3091 }, + {2298,2793,2744 ,3013,3048,2627 }, {925,2802,2782 ,1287,3022,3072 }, + {6348,7253,6349 ,1607,1855,705 }, {3064,3063,3025 ,3092,3093,3094 }, + {1336,2604,2653 ,2924,3046,3010 }, {3026,3064,3025 ,3095,3092,3094 }, + {2628,2576,2967 ,3078,3076,2701 }, {3061,3062,3101 ,2953,3096,2954 }, + {3062,3102,3101 ,3096,3097,2954 }, {3024,3062,3023 ,3098,3096,3099 }, + {3878,106,1156 ,1248,1073,3100 }, {3431,3430,3380 ,3021,1976,1514 }, + {460,1876,1853 ,1999,1386,3101 }, {2619,631,2902 ,3035,1426,3082 }, + {2902,631,2679 ,3082,1426,3056 }, {2402,3044,2575 ,3084,3083,3059 }, + {2916,2482,2402 ,3102,3038,3084 }, {2714,2858,3092 ,3103,3039,3062 }, + {2794,2714,3092 ,3086,3103,3062 }, {3082,2873,2858 ,3104,3040,3039 }, + {2714,3082,2858 ,3103,3104,3039 }, {3495,3434,3496 ,735,737,3105 }, + {2619,2090,631 ,3035,1324,1426 }, {845,4970,760 ,1828,3073,3106 }, + {2131,2865,2201 ,656,3107,2455 }, {2973,3021,2972 ,3108,2891,2047 }, + {4614,4613,4590 ,3109,1488,1490 }, {2594,2593,2511 ,3110,3111,3112 }, + {3062,3061,3023 ,3096,2953,3099 }, {3215,2354,2426 ,2967,2968,3113 }, + {2354,3013,3173 ,2968,2946,3114 }, {2793,3215,2426 ,3048,2967,3113 }, + {2924,2925,2974 ,2593,3115,3116 }, {2744,2793,3193 ,2627,3048,3117 }, + {631,2944,2679 ,1426,3118,3056 }, {2395,2463,2679 ,3119,3057,3056 }, + {2480,2398,2463 ,3120,3058,3057 }, {2395,2480,2463 ,3119,3120,3057 }, + {2398,2480,2575 ,3058,3120,3059 }, {2575,3174,2402 ,3059,3121,3084 }, + {2289,2482,2916 ,3122,3038,3102 }, {2482,2289,2540 ,3038,3122,3085 }, + {2836,2873,3082 ,3123,3040,3104 }, {1806,494,2873 ,3124,3041,3040 }, + {2836,1806,2873 ,3123,3124,3040 }, {494,1806,802 ,3041,3124,400 }, + {3890,3926,3889 ,1913,3125,3126 }, {6434,6435,6479 ,3127,2658,2659 }, + {4576,7524,4575 ,1628,1630,3128 }, {203,2383,2316 ,2711,3066,3067 }, + {2317,203,2316 ,3129,2711,3067 }, {270,199,2383 ,3130,2789,3066 }, + {203,270,2383 ,2711,3130,3066 }, {2609,790,744 ,1130,1339,1367 }, + {3011,2624,2914 ,3131,3132,2573 }, {6707,6643,8101 ,2704,3133,383 }, + {3149,2537,3143 ,3134,2985,3135 }, {3064,3104,3063 ,3092,3136,3093 }, + {3104,3103,3063 ,3136,3137,3093 }, {2152,391,821 ,1545,1647,2872 }, + {2669,2668,2593 ,3015,3138,3111 }, {2576,795,1665 ,3076,302,3077 }, + {3193,2630,2894 ,3117,3139,3140 }, {2628,2967,3180 ,3078,2701,2700 }, + {3193,2793,2630 ,3117,3048,3139 }, {2570,2963,2544 ,2612,3032,2451 }, + {2341,8064,3008 ,845,3141,546 }, {4212,4213,294 ,895,1070,945 }, + {928,830,929 ,1997,1996,1234 }, {2644,2944,631 ,3142,3118,1426 }, + {2478,2644,631 ,3143,3142,1426 }, {2644,2679,2944 ,3142,3056,3118 }, + {2396,2395,2679 ,3144,3119,3056 }, {2644,2396,2679 ,3142,3144,3056 }, + {2312,2339,2575 ,3145,3146,3059 }, {2480,2312,2575 ,3120,3145,3059 }, + {2575,2339,3174 ,3059,3146,3121 }, {2339,2648,2402 ,3146,3147,3084 }, + {3174,2339,2402 ,3121,3146,3084 }, {2916,3115,2289 ,3102,3148,3122 }, + {3077,2794,2540 ,3149,3086,3085 }, {2289,3077,2540 ,3122,3149,3085 }, + {2347,2714,2794 ,3150,3103,3086 }, {3077,2347,2794 ,3149,3150,3086 }, + {2836,3082,2714 ,3123,3104,3103 }, {2347,2836,2714 ,3150,3123,3103 }, + {3496,3434,3464 ,3105,737,3151 }, {2316,2275,2317 ,3067,3152,3129 }, + {1165,4831,3472 ,3153,3154,3155 }, {2293,2294,2358 ,2561,3081,2562 }, + {3489,3543,3488 ,1977,3075,1980 }, {2544,2966,2622 ,2451,3091,2840 }, + {6796,8063,8136 ,2617,465,2449 }, {2669,2667,2668 ,3015,2949,3138 }, + {2818,2882,2881 ,3156,518,2592 }, {2882,2926,2925 ,518,517,3115 }, + {2881,2882,2925 ,2592,518,3115 }, {1550,1477,1479 ,2586,1045,1270 }, + {953,1048,2084 ,1237,1236,1361 }, {2926,2975,2974 ,517,3157,3116 }, + {2975,2997,2974 ,3157,3158,3116 }, {2925,2926,2974 ,3115,517,3116 }, + {3155,1552,3100 ,3159,2266,2955 }, {3101,3155,3100 ,2954,3159,2955 }, + {2295,2296,2360 ,3160,2664,3161 }, {3024,3023,2997 ,3098,3099,3158 }, + {2511,2510,2438 ,3112,3162,3163 }, {2975,3024,2997 ,3157,3098,3158 }, + {3156,3155,3101 ,3164,3159,2954 }, {3137,3193,2894 ,2628,3117,3140 }, + {2312,2480,2395 ,3145,3120,3119 }, {2396,2312,2395 ,3144,3145,3119 }, + {2648,2806,2916 ,3147,3165,3102 }, {2402,2648,2916 ,3084,3147,3102 }, + {2916,2806,3115 ,3102,3165,3148 }, {2377,2347,3077 ,3166,3150,3149 }, + {3042,2377,3077 ,3167,3166,3149 }, {2355,1806,2836 ,3168,3124,3123 }, + {3490,3489,3430 ,2502,1977,1976 }, {1923,211,459 ,418,415,696 }, + {1145,1146,1236 ,1277,2467,1434 }, {1187,1367,1919 ,1778,574,1779 }, + {2597,2596,2512 ,2769,2771,2981 }, {2691,2353,2570 ,3169,3170,2612 }, + {3149,3143,968 ,3134,3135,1661 }, {5616,5615,3274 ,646,3171,3172 }, + {2721,2720,2667 ,2917,3173,2949 }, {2669,2721,2667 ,3015,2917,2949 }, + {2817,2818,2881 ,3174,3156,2592 }, {6968,7027,7008 ,595,3175,3176 }, + {3277,8066,8098 ,961,960,907 }, {3155,3156,1552 ,3159,3164,2266 }, + {2438,2437,2360 ,3163,2576,3161 }, {2361,2438,2360 ,3177,3163,3161 }, + {2667,2720,2719 ,2949,3173,2766 }, {3137,2744,3193 ,2628,2627,3117 }, + {119,460,2084 ,2278,1999,1361 }, {1443,5616,3274 ,182,646,3172 }, + {2135,2136,2478 ,1425,1546,3143 }, {2648,298,2806 ,3147,3178,3165 }, + {2806,2860,2289 ,3165,3179,3122 }, {3115,2806,2289 ,3148,3165,3122 }, + {2466,3042,3077 ,3180,3167,3149 }, {2289,2466,3077 ,3122,3180,3149 }, + {2377,2355,2836 ,3166,3168,3123 }, {2347,2377,2836 ,3150,3166,3123 }, + {2355,2465,1806 ,3168,3181,3124 }, {3041,1860,3618 ,3182,3183,37 }, + {553,1743,1727 ,792,763,793 }, {6388,6435,6434 ,3184,2658,3127 }, + {204,203,2317 ,1722,2711,3129 }, {2318,204,2317 ,3185,1722,3129 }, + {5191,6388,6434 ,2900,3184,3127 }, {3228,178,2532 ,3042,3043,3186 }, + {1107,2866,1205 ,2656,2655,3187 }, {3260,3149,968 ,3188,3134,1661 }, + {2909,2911,2678 ,3189,2923,3190 }, {2777,2801,2776 ,2824,3191,3192 }, + {3104,3158,3157 ,3136,3193,3194 }, {2963,2744,2544 ,3032,2627,2451 }, + {1657,1725,3228 ,1781,1747,3042 }, {2874,2628,2386 ,3195,3078,3196 }, + {2818,2817,2771 ,3156,3174,3197 }, {2772,2818,2771 ,3198,3156,3197 }, + {2136,1512,2478 ,1546,3199,3143 }, {3246,2644,2478 ,3200,3142,3143 }, + {1512,3246,2478 ,3199,3200,3143 }, {2399,2396,2644 ,3201,3144,3142 }, + {2399,2529,2312 ,3201,3202,3145 }, {2396,2399,2312 ,3144,3201,3145 }, + {2312,2529,2339 ,3145,3202,3146 }, {3245,298,2648 ,3203,3178,3147 }, + {2339,3245,2648 ,3146,3203,3147 }, {2860,2753,2289 ,3179,456,3122 }, + {2289,2753,2466 ,3122,456,3180 }, {110,111,226 ,743,1154,108 }, + {2590,2589,437 ,2648,3204,1149 }, {5390,5037,6317 ,3205,3206,3207 }, + {2521,2533,2506 ,3208,3209,2882 }, {2867,2940,1205 ,2844,110,3187 }, + {2011,3001,5615 ,129,3210,3171 }, {1208,1668,1942 ,3211,3212,2337 }, + {1687,1740,1782 ,1104,3213,1105 }, {2966,3137,2894 ,3091,2628,3140 }, + {3060,3061,3100 ,3214,2953,2955 }, {2779,2778,2727 ,2831,3215,2760 }, + {165,1373,341 ,1869,1501,1852 }, {787,802,4130 ,209,400,210 }, + {3173,2385,2434 ,3114,2702,1118 }, {2385,2433,2434 ,2702,1116,1118 }, + {2011,3913,3001 ,129,841,3210 }, {2296,2361,2360 ,2664,3177,3161 }, + {2720,2772,2771 ,3173,3198,3197 }, {2719,2720,2771 ,2766,3173,3197 }, + {2883,2882,2818 ,2579,518,3156 }, {2386,2628,2835 ,3196,3078,3079 }, + {2549,2894,2370 ,2841,3140,3216 }, {3102,3156,3101 ,3097,3164,2954 }, + {2266,3246,1512 ,3217,3200,3199 }, {821,2266,1512 ,2872,3217,3199 }, + {2266,2644,3246 ,3217,3142,3200 }, {2414,2399,2644 ,3218,3201,3142 }, + {2266,2414,2644 ,3217,3218,3142 }, {2414,2299,2529 ,3218,3219,3202 }, + {2399,2414,2529 ,3201,3218,3202 }, {2299,2447,2339 ,3219,3220,3146 }, + {2529,2299,2339 ,3202,3219,3146 }, {2339,2447,3245 ,3146,3220,3203 }, + {2447,2446,298 ,3220,3221,3178 }, {3245,2447,298 ,3203,3220,3178 }, + {2806,2753,2860 ,3165,456,3179 }, {2753,2538,3042 ,456,3222,3167 }, + {2466,2753,3042 ,3180,456,3167 }, {3119,2377,3042 ,3223,3166,3167 }, + {2538,3119,3042 ,3222,3223,3167 }, {3119,2747,2355 ,3223,3224,3168 }, + {2377,3119,2355 ,3166,3223,3168 }, {2355,2747,2465 ,3168,3224,3181 }, + {3970,3969,3925 ,3225,3226,3227 }, {2319,204,2318 ,3228,1722,3185 }, + {1211,2255,2697 ,2007,3229,2006 }, {2489,2715,2475 ,2611,2944,3047 }, + {2506,2533,2805 ,2882,3209,3230 }, {3990,3299,3254 ,3231,3232,3233 }, + {2602,2675,2600 ,3234,3235,2973 }, {2417,2874,2386 ,3236,3195,3196 }, + {2427,2379,2677 ,3237,3238,3033 }, {286,287,331 ,2446,2470,2403 }, + {2624,2949,3405 ,3132,3239,1798 }, {3429,3460,3459 ,1978,3240,3241 }, + {2886,2863,2931 ,2913,3242,2780 }, {2863,2932,2931 ,3242,3243,2780 }, + {2512,2511,2438 ,2981,3112,3163 }, {593,4223,1599 ,1108,1934,391 }, + {3199,1726,1658 ,3244,1746,1782 }, {2511,2512,2595 ,3112,2981,2983 }, + {2510,2593,2592 ,3162,3111,2936 }, {2532,1874,1337 ,3186,962,2148 }, + {2549,2370,2567 ,2841,3216,2842 }, {2038,1983,1984 ,1551,1001,831 }, + {2963,2298,2744 ,3032,3013,2627 }, {2819,2883,2818 ,2870,2579,3156 }, + {2475,3013,3215 ,3047,2946,2967 }, {2462,2413,2417 ,3245,3246,3236 }, + {2413,2900,2417 ,3246,2649,3236 }, {2715,2462,2417 ,2944,3245,3236 }, + {2900,2551,2874 ,2649,3247,3195 }, {1304,1255,2576 ,2639,3248,3076 }, + {2551,2576,3175 ,3247,3076,3249 }, {2551,1304,2576 ,3247,2639,3076 }, + {5616,2011,5615 ,646,129,3171 }, {2446,2849,2806 ,3221,3250,3165 }, + {298,2446,2806 ,3178,3221,3165 }, {2849,3144,2806 ,3250,3251,3165 }, + {2806,3144,2753 ,3165,3251,456 }, {2346,3119,2538 ,3252,3223,3222 }, + {2573,2346,2538 ,3253,3252,3222 }, {2747,2681,2465 ,3224,3254,3181 }, + {8118,1218,8126 ,2129,3255,812 }, {135,204,2319 ,2653,1722,3228 }, + {2320,135,2319 ,1406,2653,3228 }, {135,205,204 ,2653,1741,1722 }, + {8138,4106,8078 ,2018,1742,1744 }, {135,273,205 ,2653,1749,1741 }, + {3913,4248,3001 ,841,3256,3210 }, {2369,2445,2368 ,3257,1388,2943 }, + {2351,3149,2201 ,3258,3134,2455 }, {2588,2449,2556 ,2072,3259,2779 }, + {6911,6912,6942 ,474,519,521 }, {6943,6963,6962 ,520,1048,3260 }, + {3428,3429,3459 ,3261,1978,3241 }, {788,1390,3091 ,976,2964,3262 }, + {3260,968,2201 ,3188,1661,2455 }, {3325,3359,3305 ,825,824,879 }, + {2417,2386,2715 ,3236,3196,2944 }, {3199,1658,3156 ,3244,1782,3164 }, + {2439,2512,2438 ,2980,2981,3163 }, {6531,6897,8134 ,2280,54,3263 }, + {2354,3173,2434 ,2968,3114,1118 }, {3103,3104,3157 ,3137,3136,3194 }, + {2622,2966,2894 ,2840,3091,3140 }, {795,456,1665 ,302,301,3077 }, + {336,291,236 ,1013,1012,2444 }, {2356,2909,1785 ,3264,3189,1659 }, + {3267,2356,1785 ,3265,3264,1659 }, {3267,1785,1726 ,3265,1659,1746 }, + {3229,3267,1726 ,3266,3265,1746 }, {5346,5364,5363 ,3267,3268,3269 }, + {2754,2349,2849 ,3270,457,3250 }, {2446,2754,2849 ,3221,3270,3250 }, + {2849,2349,3144 ,3250,457,3251 }, {3144,2349,2753 ,3251,457,456 }, + {2811,2573,2538 ,3271,3253,3222 }, {2753,2811,2538 ,456,3271,3222 }, + {2792,2732,2626 ,2718,2583,2582 }, {8062,8065,8090 ,3272,96,412 }, + {2445,2481,2444 ,1388,3273,2941 }, {2822,2886,2821 ,2826,2913,2915 }, + {3212,2587,2942 ,3027,3274,2587 }, {1383,788,3091 ,336,976,3262 }, + {2932,2979,2931 ,3243,2781,2780 }, {2943,2503,2691 ,3055,2948,3169 }, + {2617,2852,2647 ,2878,3275,3276 }, {4571,972,2162 ,1342,3277,3278 }, + {3158,3200,3199 ,3193,3279,3244 }, {3157,3158,3199 ,3194,3193,3244 }, + {2653,2129,2069 ,3010,1469,1711 }, {2900,2874,2417 ,2649,3195,3236 }, + {2678,2911,1917 ,3190,2923,833 }, {2911,1336,2988 ,2923,2924,3045 }, + {821,3185,2266 ,2872,3280,3217 }, {3005,2414,2266 ,3281,3218,3217 }, + {3272,2754,2446 ,3282,3270,3221 }, {2447,3272,2446 ,3220,3282,3221 }, + {2349,890,2660 ,457,3283,455 }, {2753,2660,2811 ,456,455,3271 }, + {2380,2895,2914 ,2597,2574,2573 }, {2370,1482,227 ,3216,2265,2157 }, + {270,203,271 ,3130,2711,1608 }, {3134,3135,1855 ,3284,2833,3088 }, + {2367,3135,3134 ,2764,2833,3284 }, {643,2676,2602 ,3285,2761,3234 }, + {2760,2749,1096 ,2726,3286,2727 }, {4395,4347,4373 ,726,3287,3288 }, + {2807,2555,2545 ,3289,1066,3008 }, {329,2393,330 ,2348,2445,2402 }, + {3293,3291,7151 ,1988,1203,1202 }, {3089,3088,3048 ,2794,3290,2165 }, + {3049,3089,3048 ,2803,2794,2165 }, {554,2569,2366 ,2588,3291,2589 }, + {554,2268,2085 ,2588,3292,657 }, {3013,3095,3173 ,2946,2945,3114 }, + {2894,2630,2370 ,3140,3139,3216 }, {3231,411,2356 ,3293,2919,3264 }, + {3230,3231,2356 ,3294,3293,3264 }, {3199,3200,3229 ,3244,3279,3266 }, + {3200,3230,3229 ,3279,3294,3266 }, {2653,2069,2862 ,3010,1711,3031 }, + {2462,2715,2489 ,3245,2944,2611 }, {3157,3199,3156 ,3194,3244,3164 }, + {1725,1784,178 ,1747,1662,3043 }, {2031,2067,2006 ,1560,1592,1181 }, + {2551,3175,2874 ,3247,3249,3195 }, {8080,8069,8141 ,1094,1095,3295 }, + {1917,2911,2988 ,833,2923,3045 }, {3159,3158,3104 ,3296,3193,3136 }, + {3105,3159,3104 ,3297,3296,3136 }, {3065,3064,3026 ,3298,3092,3095 }, + {2464,840,1697 ,3299,68,69 }, {1088,3185,821 ,1604,3280,2872 }, + {1088,821,391 ,1604,2872,1647 }, {1088,2266,3185 ,1604,3217,3280 }, + {2309,3005,2266 ,3300,3281,3217 }, {1088,2309,2266 ,1604,3300,3217 }, + {3005,2309,2414 ,3281,3300,3218 }, {2309,2734,2299 ,3300,3301,3219 }, + {2414,2309,2299 ,3218,3300,3219 }, {2734,2311,2447 ,3301,3302,3220 }, + {2299,2734,2447 ,3219,3301,3220 }, {2447,2311,3272 ,3220,3302,3282 }, + {3272,2311,2754 ,3282,3302,3270 }, {2811,2660,2573 ,3271,455,3253 }, + {2591,2592,2638 ,3303,2936,2935 }, {2380,2914,2624 ,2597,2573,3132 }, + {2895,2581,2857 ,2574,2598,2575 }, {2930,2931,2977 ,3004,2780,2997 }, + {3000,3031,2980 ,3304,2857,3305 }, {3165,3207,3164 ,3306,2884,3307 }, + {2587,3256,554 ,3274,3308,2588 }, {2821,2885,2820 ,2915,2914,2869 }, + {3673,4185,52 ,648,409,411 }, {2949,2624,3011 ,3239,3132,3131 }, + {3429,3488,3460 ,1978,1980,3240 }, {2726,2777,2776 ,2667,2824,3192 }, + {5284,5338,5337 ,571,63,572 }, {4904,5377,4887 ,3309,361,2735 }, + {368,2534,369 ,1396,2846,1521 }, {3102,3103,3156 ,3097,3137,3164 }, + {2411,3081,2692 ,2777,1017,2845 }, {2936,2935,2889 ,2843,3310,2604 }, + {5284,5283,5239 ,571,570,482 }, {2268,2132,2085 ,3292,655,657 }, + {2979,3029,2978 ,2781,3311,2782 }, {2617,2647,2353 ,2878,3276,3170 }, + {78,2302,2495 ,3312,3313,2920 }, {2503,2617,2353 ,2948,2878,3170 }, + {3204,3234,3233 ,3314,3315,3316 }, {2302,2741,2495 ,3313,3317,2920 }, + {3162,3204,3203 ,3318,3314,3319 }, {3161,3162,3203 ,3320,3318,3319 }, + {3268,2302,3233 ,3321,3313,3316 }, {3234,3268,3233 ,3315,3321,3316 }, + {2302,2332,2741 ,3313,3322,3317 }, {3160,3161,3202 ,3323,3320,3324 }, + {3203,3233,3232 ,3319,3316,3325 }, {3107,3161,3140 ,3326,3320,3327 }, + {3161,3203,3202 ,3320,3319,3324 }, {3200,3201,3230 ,3279,3328,3294 }, + {2988,2653,2862 ,3045,3010,3031 }, {860,460,1853 ,1385,1999,3101 }, + {2508,2509,2592 ,2630,3329,2936 }, {840,2464,3450 ,68,3299,3330 }, + {2597,2673,2672 ,2769,2974,3331 }, {2311,2750,2754 ,3302,3332,3270 }, + {2750,890,2349 ,3332,3283,457 }, {2754,2750,2349 ,3270,3332,457 }, + {254,109,4036 ,2008,1654,1602 }, {687,1155,831 ,1665,3333,3334 }, + {546,2602,2560 ,1171,3234,3335 }, {2497,2132,2268 ,3336,655,3292 }, + {5264,5284,5239 ,1192,571,482 }, {8094,8088,8100 ,98,1998,1713 }, + {4041,2946,4040 ,1063,1062,460 }, {2456,2792,2626 ,2790,2718,2582 }, + {3207,3206,3164 ,2884,2886,3307 }, {3111,3165,3110 ,2829,3306,3337 }, + {486,437,529 ,1144,1149,1148 }, {2971,3019,1370 ,3338,2709,1500 }, + {2683,2736,2735 ,2645,2625,2887 }, {2704,2683,2735 ,3339,2645,2887 }, + {2730,741,827 ,2150,1221,1220 }, {220,131,8 ,3340,2965,3341 }, + {8093,8078,8076 ,2288,1744,617 }, {2801,2777,2822 ,3191,2824,2826 }, + {2675,2727,2674 ,3235,2760,2975 }, {3232,3233,78 ,3325,3316,3312 }, + {3233,2302,78 ,3316,3313,3312 }, {3203,3204,3233 ,3319,3314,3316 }, + {2445,2444,2368 ,1388,2941,2943 }, {6804,6805,6854 ,3342,3343,3344 }, + {5276,5330,5350 ,1525,3345,1526 }, {1294,108,874 ,635,1336,636 }, + {3684,3422,3664 ,1915,2055,1557 }, {554,90,2987 ,2588,2851,3346 }, + {90,2269,2987 ,2851,2881,3346 }, {3161,3160,3140 ,3320,3323,3327 }, + {3201,3200,3158 ,3328,3279,3193 }, {3159,3201,3158 ,3296,3328,3193 }, + {2630,2948,1482 ,3139,3347,2265 }, {2630,2577,2948 ,3139,3348,3347 }, + {2630,2853,2577 ,3139,806,3348 }, {2977,3028,3027 ,2997,3349,3350 }, + {2817,2881,2880 ,3174,2592,2591 }, {3105,3104,3064 ,3297,3136,3092 }, + {3065,3105,3064 ,3298,3297,3092 }, {2365,2441,2364 ,3351,3000,2513 }, + {3029,3066,3065 ,3311,3352,3298 }, {3028,3029,3065 ,3349,3311,3298 }, + {2978,3029,3028 ,2782,3311,3349 }, {2977,2978,3028 ,2997,2782,3349 }, + {2311,2419,2750 ,3302,3353,3332 }, {787,494,802 ,209,3041,400 }, + {2750,2419,890 ,3332,3353,3283 }, {1211,3636,939 ,2007,1060,3354 }, + {3636,1453,939 ,1060,3355,3354 }, {3487,3541,3540 ,3356,981,3357 }, + {221,2541,748 ,1296,1298,1677 }, {2532,1337,3226 ,3186,2148,3358 }, + {2263,2293,2262 ,3080,2561,3359 }, {3206,3236,3205 ,2886,2717,3360 }, + {3206,3205,3163 ,2886,3360,3361 }, {3165,3164,3110 ,3306,3307,3337 }, + {3036,3074,3035 ,3362,3363,3364 }, {2665,2664,2590 ,2677,2721,2648 }, + {1146,2921,1237 ,2467,2691,1467 }, {1237,2971,1370 ,1467,3338,1500 }, + {2930,2977,2928 ,3004,2997,2578 }, {3994,4036,4035 ,2806,1602,1601 }, + {3283,1556,2968 ,262,3365,263 }, {3182,2536,49 ,3366,2793,3367 }, + {2536,1015,3017 ,2793,3368,3369 }, {2533,2536,3182 ,3209,2793,3366 }, + {2535,2536,2533 ,3370,2793,3209 }, {2649,2536,2535 ,2791,2793,3370 }, + {2597,2672,2671 ,2769,3331,2705 }, {3227,3226,3198 ,1167,3358,1168 }, + {3067,3066,3029 ,3371,3352,3311 }, {3046,3067,3029 ,3372,3371,3311 }, + {3202,3203,3232 ,3324,3319,3325 }, {2781,2780,2756 ,3373,2830,2804 }, + {2729,2728,2676 ,3374,2759,2761 }, {2701,2729,2676 ,3375,3374,2761 }, + {2729,2756,2728 ,3374,2804,2759 }, {8087,8145,6797 ,385,3376,2246 }, + {2879,2923,2922 ,2621,2046,2045 }, {2878,2879,2922 ,2622,2621,2045 }, + {3209,3239,3238 ,3377,3378,3379 }, {199,270,271 ,2789,3130,1608 }, + {3066,3107,3106 ,3352,3326,3380 }, {3085,3066,3106 ,3381,3352,3380 }, + {1071,3661,2455 ,3382,789,475 }, {2822,2821,2776 ,2826,2915,3192 }, + {3027,3065,3026 ,3350,3298,3095 }, {3605,3604,3556 ,3383,3384,3385 }, + {1011,2864,1010 ,1285,3386,1286 }, {1688,451,134 ,1718,3387,1783 }, + {1592,1056,2309 ,2939,3388,3300 }, {1088,1592,2309 ,1604,2939,3300 }, + {2918,2734,2309 ,3389,3301,3300 }, {1056,2918,2309 ,3388,3389,3300 }, + {2918,2412,2311 ,3389,3390,3302 }, {2734,2918,2311 ,3301,3389,3302 }, + {2311,2412,2419 ,3302,3390,3353 }, {2840,2875,2687 ,2594,2596,2767 }, + {2629,2494,794 ,2673,3391,3392 }, {2637,1373,165 ,2720,1501,1869 }, + {3061,3060,3022 ,2953,3214,3393 }, {3236,3235,3205 ,2717,3394,3360 }, + {2565,2612,2524 ,2918,2646,3395 }, {2424,2870,2367 ,2778,2762,2764 }, + {3744,3746,3796 ,2390,2708,2969 }, {2905,2424,2367 ,399,2778,2764 }, + {2931,2930,2885 ,2780,3004,2914 }, {3169,3168,3127 ,2810,3396,2811 }, + {5363,6260,5346 ,3269,3397,3267 }, {2601,2600,2517 ,3398,2973,3399 }, + {2727,2778,2755 ,2760,3215,3400 }, {2597,2598,2673 ,2769,2602,2974 }, + {819,2769,2846 ,2244,2679,2623 }, {2436,2435,2358 ,2577,3401,2562 }, + {2729,2781,2756 ,3374,3373,2804 }, {2698,3187,4248 ,105,3402,3256 }, + {3065,3066,3085 ,3298,3352,3381 }, {2297,2362,2361 ,2663,2979,3177 }, + {3072,3125,3071 ,3403,3404,3405 }, {2999,2977,3027 ,2998,2997,3350 }, + {2359,2437,2358 ,3406,2576,2562 }, {2427,3117,2379 ,3237,3407,3238 }, + {6748,6757,1990 ,3408,3409,1305 }, {2581,2687,2857 ,2598,2767,2575 }, + {455,410,1665 ,2205,2204,3077 }, {2155,5444,2157 ,580,3410,3411 }, + {2443,2515,2442 ,2942,2600,2742 }, {2987,2497,2268 ,3346,3336,3292 }, + {2125,2193,2124 ,892,1007,1006 }, {3129,3086,3130 ,3412,3413,3414 }, + {1346,1806,2465 ,484,3124,3181 }, {2525,2565,2524 ,2937,2918,3395 }, + {2801,2822,2776 ,3191,2826,3192 }, {1740,1337,1782 ,3213,2148,1105 }, + {3405,2949,2920 ,1798,3239,1799 }, {2629,2583,2461 ,2673,2672,2899 }, + {2675,2674,2600 ,3235,2975,2973 }, {3108,3107,3066 ,3415,3326,3352 }, + {3067,3108,3066 ,3371,3415,3352 }, {2488,2693,2542 ,2877,3416,2879 }, + {2673,2725,2724 ,2974,2666,2668 }, {3256,90,554 ,3308,2851,2588 }, + {3011,2914,2965 ,3131,2573,3417 }, {7250,7233,3288 ,3418,2125,2058 }, + {1479,1480,3151 ,1270,2695,2585 }, {1480,3152,3151 ,2695,2580,2585 }, + {2884,2929,2928 ,2940,3003,2578 }, {2296,2297,2361 ,2664,2663,3177 }, + {2672,2673,2671 ,3331,2974,2705 }, {3742,3793,3792 ,3419,3420,3421 }, + {6742,4784,3192 ,3422,3423,3063 }, {3228,3227,1656 ,3042,1167,1166 }, + {2694,145,60 ,811,810,675 }, {2652,2918,1056 ,3424,3389,3388 }, + {2652,2745,2412 ,3424,3425,3390 }, {2918,2652,2412 ,3389,3424,3390 }, + {2412,2420,2419 ,3390,273,3353 }, {2513,2512,2440 ,3426,2981,2572 }, + {5166,6257,5473 ,3427,3428,3429 }, {219,3195,1855 ,3087,3430,3088 }, + {199,369,2534 ,2789,1521,2846 }, {2823,2863,2822 ,2825,3242,2826 }, + {2825,2824,2778 ,3431,3432,3215 }, {4256,4275,4255 ,3433,699,165 }, + {2555,2807,2416 ,1066,3289,808 }, {2915,2631,2711 ,3434,2971,3435 }, + {2525,2524,2485 ,2937,3395,2815 }, {4216,4256,4255 ,1241,3433,165 }, + {4215,4216,4255 ,370,1241,165 }, {2612,2611,2524 ,2646,3436,3395 }, + {4548,3247,2252 ,589,1494,590 }, {2779,2825,2778 ,2831,3431,3215 }, + {2294,2359,2358 ,3081,3406,2562 }, {2925,2924,2881 ,3115,2593,2592 }, + {3162,3161,3107 ,3318,3320,3326 }, {3108,3162,3107 ,3415,3318,3326 }, + {2693,2494,2542 ,3416,3391,2879 }, {6349,7252,6321 ,705,3437,706 }, + {2577,2416,2710 ,3348,808,3438 }, {4190,1265,1271 ,3439,1273,588 }, + {6416,6418,5089 ,3440,3441,3442 }, {2841,2521,2506 ,3443,3208,2882 }, + {2597,2671,2640 ,2769,2705,2770 }, {818,819,2800 ,1018,2244,2996 }, + {2450,3192,6689 ,3444,3063,3065 }, {2802,2827,2781 ,3022,3445,3373 }, + {2802,2848,2827 ,3022,2605,3445 }, {2497,2506,2344 ,3336,2882,2852 }, + {2702,2427,3149 ,2854,3237,3134 }, {2998,3025,2975 ,3446,3094,3157 }, + {3741,3742,3792 ,3447,3419,3421 }, {3744,3795,3794 ,2390,3448,3449 }, + {2782,2802,2781 ,3072,3022,3373 }, {31,2859,2652 ,3450,3451,3424 }, + {1056,1592,1004 ,3388,2939,3452 }, {2859,2695,2745 ,3451,3453,3425 }, + {2652,2859,2745 ,3424,3451,3425 }, {2745,2695,2412 ,3425,3453,3390 }, + {2695,2421,2420 ,3453,2568,273 }, {2412,2695,2420 ,3390,3453,273 }, + {3742,3743,3793 ,3419,2225,3420 }, {3324,3357,3323 ,881,883,1051 }, + {3743,3794,3793 ,2225,3449,3420 }, {1549,1550,1619 ,903,2586,1003 }, + {287,288,332 ,2470,2723,2471 }, {3269,2792,2456 ,2719,2718,2790 }, + {8114,5950,8108 ,2286,2873,2287 }, {2710,2416,2631 ,3438,808,2971 }, + {3166,3209,3208 ,3454,3377,3455 }, {2740,2739,2705 ,3456,2801,2802 }, + {2686,2740,2705 ,3071,3456,2802 }, {503,1633,466 ,374,373,347 }, + {1032,193,867 ,1581,1448,1121 }, {2985,3036,2984 ,3457,3362,3458 }, + {2740,2788,2739 ,3456,2652,2801 }, {7287,7286,7239 ,3459,3460,1527 }, + {2506,2805,2554 ,2882,3230,2853 }, {2437,2510,2509 ,2576,3162,3329 }, + {2436,2437,2509 ,2577,2576,3329 }, {2567,2370,227 ,2842,3216,2157 }, + {2413,2387,2900 ,3246,3461,2649 }, {2367,3134,3116 ,2764,3284,3029 }, + {2387,2633,2900 ,3461,2650,2649 }, {3824,8131,8133 ,3462,2307,2131 }, + {3954,8062,4957 ,3463,3272,2733 }, {2677,2379,886 ,3033,3238,3464 }, + {2445,2517,2481 ,1388,3399,3273 }, {2681,2464,2465 ,3254,3299,3181 }, + {2505,2506,2497 ,2883,2882,3336 }, {2202,650,2841 ,2875,3074,3443 }, + {6809,6808,6754 ,712,3465,713 }, {2514,2515,2513 ,2741,2600,3426 }, + {3743,3744,3794 ,2225,2390,3449 }, {2560,2518,546 ,3335,2388,1171 }, + {2782,2781,2729 ,3072,3373,3374 }, {2730,2782,2729 ,2150,3072,3374 }, + {6353,8121,6686 ,749,3466,750 }, {3460,3488,3487 ,3240,1980,3356 }, + {3478,3900,8141 ,885,884,3295 }, {4377,1255,1304 ,3090,3248,2639 }, + {3697,3743,3742 ,2224,2225,3419 }, {2774,2820,2773 ,2916,2869,2871 }, + {3025,3063,3062 ,3094,3093,3096 }, {3135,3183,1855 ,2833,2832,3088 }, + {7273,7251,7252 ,3467,2671,3437 }, {2567,3277,2901 ,2842,961,3468 }, + {2867,2868,2940 ,2844,1577,110 }, {1387,3372,1908 ,2951,2143,1058 }, + {147,4027,146 ,3469,3470,504 }, {5103,5418,5412 ,77,3471,360 }, + {1318,1032,867 ,164,1581,1121 }, {1787,1098,1908 ,2897,2725,1058 }, + {6735,6711,6308 ,3472,3473,3474 }, {8123,8108,8128 ,2247,2287,959 }, + {3959,3958,3904 ,982,294,1264 }, {2141,6707,8101 ,2015,2704,383 }, + {2816,2880,2879 ,3475,2591,2621 }, {3345,4106,8138 ,3476,1742,2018 }, + {2774,2776,2820 ,2916,3192,2869 }, {2775,2776,2774 ,3477,3192,2916 }, + {2776,2821,2820 ,3192,2915,2869 }, {3216,1657,3228 ,3478,1781,3042 }, + {2610,2682,790 ,3479,3480,1339 }, {2577,2710,2915 ,3348,3438,3434 }, + {220,4522,95 ,3340,3481,3482 }, {2545,2791,1789 ,3008,3483,3007 }, + {3227,2532,3226 ,1167,3186,3358 }, {2923,2973,2972 ,2046,3108,2047 }, + {2885,2930,2884 ,2914,3004,2940 }, {1590,1656,3198 ,3028,1166,1168 }, + {3212,2366,2495 ,3027,2589,2920 }, {3408,3383,3357 ,3484,882,883 }, + {3129,3128,3074 ,3412,2809,3363 }, {3086,3129,3074 ,3413,3412,3363 }, + {2506,2554,2344 ,2882,2853,2852 }, {2901,8098,2659 ,3468,907,3485 }, + {1917,2988,2862 ,833,3045,3031 }, {2353,2952,2570 ,3170,2610,2612 }, + {1235,1237,1370 ,1179,1467,1500 }, {1249,2693,2488 ,2876,3416,2877 }, + {2841,3213,2521 ,3443,3486,3208 }, {8096,8115,8140 ,1796,2448,2269 }, + {1847,178,1784 ,1519,3043,1662 }, {355,51,2939 ,1876,1856,1870 }, + {4248,3187,3972 ,3256,3402,3487 }, {4236,1334,4371 ,3488,1935,3489 }, + {3031,3030,2980 ,2857,3490,3305 }, {2724,2776,2723 ,2668,3192,2706 }, + {3031,3069,3030 ,2857,2827,3490 }, {2416,2807,1789 ,808,3289,3007 }, + {3890,3889,3854 ,1913,3126,1512 }, {3012,1332,2380 ,3491,3492,2597 }, + {1218,6891,8126 ,3255,813,812 }, {3275,2962,74 ,3493,3494,2053 }, + {2791,3275,2588 ,3483,3493,2072 }, {2278,2323,2322 ,3495,3496,3497 }, + {2323,282,281 ,3496,3498,2227 }, {2322,2323,281 ,3497,3496,2227 }, + {2484,2522,2451 ,3499,2678,2102 }, {3111,3110,3069 ,2829,3337,2827 }, + {3275,74,2588 ,3493,2053,2072 }, {2880,2924,2923 ,2591,2593,2046 }, + {2508,2592,2591 ,2630,2936,3303 }, {2493,2388,2840 ,3005,2608,2594 }, + {2986,3037,2985 ,3500,3501,3457 }, {5353,5352,4022 ,851,3502,3503 }, + {3037,3036,2985 ,3501,3362,3457 }, {2631,2467,2840 ,2971,3006,2594 }, + {3239,3241,2411 ,3378,3504,2777 }, {100,7960,162 ,2823,3505,2822 }, + {3213,2533,2521 ,3486,3209,3208 }, {2587,3142,90 ,3274,2849,2851 }, + {2515,2598,2597 ,2600,2602,2769 }, {2513,2515,2597 ,3426,2600,2769 }, + {7747,7797,7745 ,331,3506,650 }, {55,300,1155 ,3507,3508,3333 }, + {3855,3869,3854 ,1511,1912,1512 }, {2656,2461,2331 ,2893,2899,2895 }, + {3364,3390,3363 ,1184,3509,1911 }, {3129,3170,3128 ,3412,3510,2809 }, + {5556,5535,5536 ,3511,3512,754 }, {7702,7752,7701 ,3513,3514,3515 }, + {2835,2385,3173 ,3079,2702,3114 }, {3074,3128,3073 ,3363,2809,3516 }, + {2586,2840,2687 ,2972,2594,2767 }, {4371,1334,3864 ,3489,1935,3517 }, + {5970,2504,4104 ,3518,2898,3519 }, {5445,6577,4293 ,3520,97,2732 }, + {7874,7873,7847 ,344,817,3521 }, {2322,2277,2278 ,3497,3522,3495 }, + {1168,6643,4985 ,1098,3133,2703 }, {2948,2915,1482 ,3347,3434,2265 }, + {2915,2710,2631 ,3434,3438,2971 }, {1201,2986,1200 ,1477,3500,2629 }, + {2986,2985,1200 ,3500,3457,2629 }, {2449,2588,91 ,3259,2072,1984 }, + {1553,102,347 ,1669,432,431 }, {2879,2880,2923 ,2621,2591,2046 }, + {3103,3157,3156 ,3137,3194,3164 }, {2649,2535,2533 ,2791,3370,3209 }, + {2949,3011,2965 ,3239,3131,3417 }, {3213,2649,2533 ,3486,2791,3209 }, + {2875,3250,2635 ,2596,2595,2607 }, {817,906,905 ,1020,1019,1729 }, + {2789,2788,2740 ,637,2652,3456 }, {2948,2577,2915 ,3347,3348,3434 }, + {2435,2436,2508 ,3401,2577,2630 }, {2831,2830,2788 ,639,2654,2652 }, + {2789,2831,2788 ,637,639,2652 }, {6796,8136,8096 ,2617,2449,1796 }, + {2683,2682,2611 ,2645,3480,3436 }, {2807,2545,1789 ,3289,3008,3007 }, + {2533,2709,2805 ,3209,3523,3230 }, {2542,2494,2656 ,2879,3391,2893 }, + {3232,78,411 ,3325,3312,2919 }, {3231,3232,411 ,3293,3325,2919 }, + {411,78,2495 ,2919,3312,2920 }, {3027,3028,3065 ,3350,3349,3298 }, + {3485,3539,2686 ,3524,3525,3071 }, {1787,2620,1098 ,2897,3526,2725 }, + {2831,2868,2867 ,639,1577,2844 }, {2830,2831,2867 ,2654,639,2844 }, + {4240,6139,4260 ,2060,2059,3527 }, {3024,3025,3062 ,3098,3094,3096 }, + {2875,2635,2687 ,2596,2607,2767 }, {2923,2924,2973 ,2046,2593,3108 }, + {1204,1106,1205 ,338,1664,3187 }, {347,2423,2428 ,431,433,371 }, + {5308,5309,564 ,3528,987,3529 }, {2812,3349,3684 ,1914,2054,1915 }, + {3349,296,3422 ,2054,1558,2055 }, {2835,3180,2385 ,3079,2700,2702 }, + {821,1512,2136 ,2872,3199,1546 }, {6707,4985,6643 ,2704,2703,3133 }, + {2650,8096,8140 ,914,1796,2269 }, {8078,8142,8084 ,1744,1743,1139 }, + {2426,3190,2853 ,3113,807,806 }, {6351,8112,6322 ,1568,3530,3531 }, + {6914,6913,6861 ,3532,2634,3533 }, {1804,3317,296 ,2902,1882,1558 }, + {2436,2509,2508 ,2577,3329,2630 }, {2846,2877,1047 ,2623,3026,1328 }, + {2354,2555,3190 ,2968,1066,807 }, {1194,539,1599 ,393,2067,391 }, + {2426,2354,3190 ,3113,2968,807 }, {2741,2332,3212 ,3317,3322,3027 }, + {2609,2610,790 ,1130,3479,1339 }, {2443,2442,2365 ,2942,2742,3351 }, + {2612,2683,2611 ,2646,2645,3436 }, {1255,4377,795 ,3248,3090,302 }, + {2141,8091,8085 ,2015,2167,2016 }, {2400,111,2962 ,1117,1154,3494 }, + {3238,2424,2792 ,3379,2778,2718 }, {2691,2851,2943 ,3169,3051,3055 }, + {2555,2354,2434 ,1066,2968,1118 }, {3278,2851,2691 ,1158,3051,3169 }, + {382,1005,3732 ,3534,3535,3536 }, {2369,2368,2333 ,3257,2943,3537 }, + {3182,49,2709 ,3366,3367,3523 }, {4039,2680,2662 ,382,459,805 }, + {3746,3797,3796 ,2708,2061,2969 }, {2400,3275,2791 ,1117,3493,3483 }, + {2545,2400,2791 ,3008,1117,3483 }, {3915,3956,3955 ,1575,1383,3538 }, + {3939,3915,3955 ,3539,1575,3538 }, {3956,3996,3995 ,1383,1928,3540 }, + {3955,3956,3995 ,3538,1383,3540 }, {4038,4037,3995 ,1927,3541,3540 }, + {3996,4038,3995 ,1928,1927,3540 }, {238,237,100 ,868,869,2823 }, + {1853,1876,860 ,3101,1386,1385 }, {2760,2906,2428 ,2726,3542,371 }, + {2749,2760,2428 ,3286,2726,371 }, {2906,3270,2428 ,3542,3543,371 }, + {3270,347,2428 ,3543,431,371 }, {964,584,1294 ,535,2512,635 }, + {2719,2771,2770 ,2766,3197,2681 }, {2400,2962,3275 ,1117,3494,3493 }, + {4211,1113,4212 ,215,10,895 }, {8141,8069,3478 ,3295,1095,885 }, + {2242,2324,2323 ,2551,1995,3496 }, {2278,2242,2323 ,3495,2551,3496 }, + {2324,2391,282 ,1995,3544,3498 }, {2323,2324,282 ,3496,1995,3498 }, + {3226,1337,1740 ,3358,2148,3213 }, {2742,2031,2029 ,2558,1560,1748 }, + {2523,2564,2522 ,3545,3546,2678 }, {2610,2609,2522 ,3479,1130,2678 }, + {2564,2610,2522 ,3546,3479,2678 }, {5383,5396,6506 ,3547,3548,3549 }, + {2981,3032,3000 ,3550,2858,3304 }, {1103,1200,2890 ,1474,2629,2603 }, + {2353,2462,2489 ,3170,3245,2611 }, {872,836,3264 ,2962,3551,2468 }, + {3086,3074,3036 ,3413,3363,3362 }, {3037,3086,3036 ,3501,3413,3362 }, + {2682,2683,2704 ,3480,2645,3339 }, {5570,5641,5599 ,508,510,2213 }, + {2578,2635,2389 ,3552,2607,2609 }, {3238,3237,3207 ,3379,2885,2884 }, + {3208,3238,3207 ,3455,3379,2884 }, {3109,3108,3067 ,3553,3415,3371 }, + {2516,2600,2515 ,3554,2973,2600 }, {3068,3109,3067 ,3555,3553,3371 }, + {2933,2980,2979 ,3556,3305,2781 }, {2932,2933,2979 ,3243,3556,2781 }, + {3030,3029,2979 ,3490,3311,2781 }, {3030,3046,3029 ,3490,3372,3311 }, + {2980,3030,2979 ,3305,3490,2781 }, {3068,3067,3046 ,3555,3371,3372 }, + {3030,3068,3046 ,3490,3555,3372 }, {3235,2456,3268 ,3394,2790,3321 }, + {2533,3182,2709 ,3209,3366,3523 }, {2388,2430,2389 ,2608,2613,2609 }, + {2497,2865,2132 ,3336,3107,655 }, {8102,8082,8089 ,253,454,254 }, + {3594,3646,3592 ,2522,2123,2084 }, {3544,3594,3543 ,1153,2522,3075 }, + {3646,3645,3592 ,2123,1650,2084 }, {3594,3592,3593 ,2522,2084,3557 }, + {3594,3593,3543 ,2522,3557,3075 }, {4038,2662,4037 ,1927,805,3541 }, + {2662,254,4037 ,805,2008,3541 }, {5371,5404,5350 ,3558,3559,1526 }, + {562,885,708 ,1936,216,401 }, {80,2939,51 ,140,1870,1856 }, {2013,2711,2586 ,2242,3435,2972 }, + {2380,2013,2586 ,2597,2242,2972 }, {1011,1103,2864 ,1285,1474,3386 }, + {1103,2891,2864 ,1474,3560,3386 }, {2620,2760,1098 ,3526,2726,2725 }, + {2620,2305,2906 ,3526,3561,3542 }, {2760,2620,2906 ,2726,3526,3542 }, + {2305,3270,2906 ,3561,3543,3542 }, {1553,347,3270 ,1669,431,3543 }, + {2305,1553,3270 ,3561,1669,3543 }, {7704,7727,7682 ,3562,3563,3564 }, + {124,1423,5482 ,2206,151,3565 }, {2325,2392,2391 ,1994,3566,3544 }, + {2324,2325,2391 ,1995,1994,3544 }, {1929,1950,1884 ,899,786,3567 }, + {2392,328,2391 ,3566,2289,3544 }, {3166,3208,3207 ,3454,3455,2884 }, + {3165,3166,3207 ,3306,3454,2884 }, {3239,2411,2424 ,3378,2777,2778 }, + {2523,2522,2484 ,3545,2678,3499 }, {2967,111,2433 ,2701,1154,1116 }, + {2385,2967,2433 ,2702,2701,1116 }, {2967,3014,111 ,2701,3568,1154 }, + {511,2003,1972 ,3569,2542,3034 }, {1200,2985,2937 ,2629,3457,3570 }, + {2555,2434,2545 ,1066,1118,3008 }, {3237,3238,2792 ,2885,3379,2718 }, + {3048,3088,3047 ,2165,3290,3571 }, {2592,2667,2666 ,2936,2949,2765 }, + {2967,259,3014 ,2701,2203,3568 }, {2517,2516,2444 ,3399,3554,2941 }, + {2481,2517,2444 ,3273,3399,2941 }, {3234,3235,3268 ,3315,3394,3321 }, + {3205,3235,3204 ,3360,3394,3314 }, {3938,3939,3090 ,3572,3539,2805 }, + {3166,3165,3111 ,3454,3306,2829 }, {3088,3087,3047 ,3290,3573,3571 }, + {2586,2687,2581 ,2972,2767,2598 }, {70,1319,822 ,323,322,2000 }, + {3595,3648,3594 ,927,734,2522 }, {3544,3543,3489 ,1153,3075,1977 }, + {3490,3544,3489 ,2502,1153,1977 }, {2846,2844,2845 ,2623,1329,2995 }, + {2846,2845,819 ,2623,2995,2244 }, {3134,1855,2267 ,3284,3088,3574 }, + {3314,2333,2368 ,3575,3537,2943 }, {2150,3016,964 ,534,1028,535 }, + {3048,3047,1224 ,2165,3571,1508 }, {2790,2789,2740 ,3576,637,3456 }, + {2790,2832,2789 ,3576,638,637 }, {2883,2927,2882 ,2579,516,518 }, + {3399,6659,4550 ,715,3577,716 }, {6353,4544,5444 ,749,748,3410 }, + {1908,3453,1787 ,1058,917,2897 }, {2977,2976,2928 ,2997,2999,2578 }, + {3998,3999,4040 ,381,1023,460 }, {113,697,4539 ,677,3578,633 }, + {3134,2267,650 ,3284,3574,3074 }, {2732,2367,3116 ,2583,2764,3029 }, + {3116,3134,3219 ,3029,3284,3044 }, {3219,3134,650 ,3044,3284,3074 }, + {3033,3032,2981 ,3579,2858,3550 }, {2852,2387,2647 ,3275,3461,3276 }, + {906,1047,1046 ,1019,1328,1929 }, {3058,3123,1407 ,2710,3023,1269 }, + {3069,3110,3068 ,2827,3337,3555 }, {4298,141,4186 ,3580,681,683 }, + {1688,242,451 ,1718,1717,3387 }, {3125,3166,3111 ,3404,3454,2829 }, + {3644,3643,3590 ,1651,2344,1867 }, {28,88,3636 ,1059,3581,1060 }, + {4037,254,4036 ,3541,2008,1602 }, {3381,3407,3380 ,871,3012,1514 }, + {3407,3431,3380 ,3012,3021,1514 }, {1200,2937,2890 ,2629,3570,2603 }, + {2614,2684,2612 ,2638,2644,2646 }, {7910,2164,6483 ,3582,3583,3584 }, + {2298,2475,2793 ,3013,3047,3048 }, {7252,7251,6321 ,3437,2671,706 }, + {3016,584,964 ,1028,2512,535 }, {3227,3228,2532 ,1167,3042,3186 }, + {3241,3081,2411 ,3504,1017,2777 }, {2496,492,171 ,2961,1580,921 }, + {3349,1804,296 ,2054,2902,1558 }, {7067,7066,7028 ,3585,3586,3587 }, + {2326,2393,2392 ,3588,2445,3566 }, {2325,2326,2392 ,1994,3588,3566 }, + {2393,329,2392 ,2445,2348,3566 }, {329,328,2392 ,2348,2289,3566 }, + {3434,3382,3408 ,737,870,3484 }, {2936,2983,2935 ,2843,3589,3310 }, + {3240,3241,3239 ,3590,3504,3378 }, {3198,3226,3197 ,1168,3358,2599 }, + {2441,2513,2440 ,3000,3426,2572 }, {2853,2416,2577 ,806,808,3348 }, + {2805,2709,651 ,3230,3523,3591 }, {2764,3142,2587 ,2847,2849,3274 }, + {2702,3149,2351 ,2854,3134,3258 }, {2344,2702,2351 ,2852,2854,3258 }, + {2848,2826,2827 ,2605,3592,3445 }, {2682,830,829 ,3480,1996,1368 }, + {493,492,2496 ,1785,1580,2961 }, {2663,921,688 ,3593,1922,765 }, + {3198,3197,3154 ,1168,2599,2692 }, {3226,1740,3197 ,3358,3213,2599 }, + {2837,930,107 ,2511,3594,2548 }, {2726,2755,2777 ,2667,3400,2824 }, + {2517,2600,2516 ,3399,2973,3554 }, {2602,2600,2601 ,3234,2973,3398 }, + {3072,3071,3033 ,3403,3405,3579 }, {3034,3072,3033 ,3595,3403,3579 }, + {3087,3086,3037 ,3573,3413,3501 }, {2704,2735,830 ,3339,2887,1996 }, + {2682,2704,830 ,3480,3339,1996 }, {1168,8122,6643 ,1098,384,3133 }, + {4901,5591,6162 ,3596,3597,3598 }, {688,921,1065 ,765,1922,821 }, + {921,1090,1340 ,1922,2687,822 }, {2590,529,2589 ,2648,1148,3204 }, + {2097,7910,2096 ,846,3582,788 }, {2942,2587,554 ,2587,3274,2588 }, + {2590,2664,529 ,2648,2721,1148 }, {2763,2620,1787 ,2896,3526,2897 }, + {2763,3120,2620 ,2896,3599,3526 }, {2500,2305,2620 ,3600,3561,3526 }, + {3120,2500,2620 ,3599,3600,3526 }, {2500,2768,1553 ,3600,1297,1669 }, + {2305,2500,1553 ,3561,3600,1669 }, {2982,2981,2934 ,3601,3550,3602 }, + {2935,2982,2934 ,3310,3601,3602 }, {3047,3087,3037 ,3571,3573,3501 }, + {2610,2611,2682 ,3479,3436,3480 }, {2039,1626,349 ,1655,564,566 }, + {2730,2729,2701 ,2150,3374,3375 }, {2770,2816,2847 ,2681,3475,2624 }, + {3059,3099,3058 ,2892,3603,2710 }, {3099,3123,3058 ,3603,3023,2710 }, + {3019,3020,3058 ,2709,2890,2710 }, {2135,2478,631 ,1425,3143,1426 }, + {2770,2771,2816 ,2681,3197,3475 }, {2424,2905,2367 ,2778,399,2764 }, + {2267,1855,2649 ,3574,3088,2791 }, {3648,3647,3594 ,734,2521,2522 }, + {3574,3595,3544 ,928,927,1153 }, {3107,3105,3106 ,3326,3297,3380 }, + {3107,3140,3105 ,3326,3327,3297 }, {2933,2932,2863 ,3556,3243,3242 }, + {1590,3154,3123 ,3028,2692,3023 }, {3124,1590,3123 ,3604,3028,3023 }, + {2084,460,860 ,1361,1999,1385 }, {2674,2726,2700 ,2975,2667,3009 }, + {2727,2755,2726 ,2760,3400,2667 }, {3169,3211,3168 ,2810,3605,3396 }, + {2677,213,123 ,3033,3606,3607 }, {3211,3210,3168 ,3605,3608,3396 }, + {2796,3080,2472 ,2462,908,2009 }, {2255,1211,939 ,3229,2007,3354 }, + {3866,35,783 ,1479,1338,679 }, {2846,2878,2877 ,2623,2622,3026 }, + {2435,388,292 ,3401,2202,2563 }, {2358,2435,292 ,2562,3401,2563 }, + {2833,2763,3054 ,2065,2896,916 }, {2833,3120,2763 ,2065,3599,2896 }, + {2550,2500,3120 ,3609,3600,3599 }, {2833,2550,3120 ,2065,3609,3599 }, + {2550,2768,2500 ,3609,1297,3600 }, {2167,2089,2947 ,1323,1325,2988 }, + {2167,2947,2166 ,1323,2988,1040 }, {388,2435,2508 ,2202,3401,2630 }, + {3069,3068,3030 ,2827,3555,3490 }, {2591,2590,2508 ,3303,2648,2630 }, + {7514,7513,7484 ,3610,3611,3612 }, {2280,2325,2243 ,3613,1994,1993 }, + {2662,2796,2472 ,805,2462,2009 }, {2997,3023,3022 ,3158,3099,3393 }, + {2591,2638,2590 ,3303,2935,2648 }, {2666,2665,2590 ,2765,2677,2648 }, + {2563,2194,2193 ,2707,1853,1007 }, {2983,2982,2935 ,3589,3601,3310 }, + {1098,1096,28 ,2725,2727,1059 }, {1908,1098,28 ,1058,2725,1059 }, + {1197,1987,2808 ,478,3614,522 }, {401,2445,2369 ,1387,1388,3257 }, + {3322,3321,7387 ,1182,2461,3615 }, {2792,2424,2367 ,2718,2778,2764 }, + {3593,3592,3543 ,3557,2084,3075 }, {3090,3955,3994 ,2805,3538,2806 }, + {600,131,1390 ,978,2965,2964 }, {3163,3205,3162 ,3361,3360,3318 }, + {788,600,1390 ,976,978,2964 }, {2626,2764,2587 ,2582,2847,3274 }, + {2332,2626,2587 ,3322,2582,3274 }, {2442,2441,2365 ,2742,3000,3351 }, + {2441,2442,2513 ,3000,2742,3426 }, {2638,2666,2590 ,2935,2765,2648 }, + {2617,2371,2387 ,2878,2880,3461 }, {2887,2933,2863 ,3616,3556,3242 }, + {2827,2826,2780 ,3445,3592,2830 }, {2791,2588,2429 ,3483,2072,3617 }, + {818,2800,2844 ,1018,2996,1329 }, {2593,2668,2667 ,3111,3138,2949 }, + {740,2730,2701 ,1595,2150,3375 }, {2755,2778,2777 ,3400,3215,2824 }, + {2727,2726,2674 ,2760,2667,2975 }, {2885,2884,2820 ,2914,2940,2869 }, + {2332,2587,3212 ,3322,3274,3027 }, {3123,1480,1407 ,3023,2695,1269 }, + {2364,2363,177 ,2513,2571,3618 }, {3357,3382,3356 ,883,870,872 }, + {2768,2460,2541 ,1297,3619,1298 }, {3654,3653,3604 ,1438,1440,3384 }, + {492,444,171 ,1580,922,921 }, {2557,3566,3456 ,3620,1146,1145 }, + {5837,4771,5804 ,3621,3622,3623 }, {2665,2666,2718 ,2677,2765,2675 }, + {2878,2922,2921 ,2622,2045,2691 }, {2550,2460,2768 ,3609,3619,1297 }, + {108,36,874 ,1336,1478,636 }, {2857,2761,2765 ,2575,2768,2590 }, + {7923,7973,7948 ,816,3624,3625 }, {2281,2327,2326 ,3626,3627,3588 }, + {2327,286,2393 ,3627,2446,2445 }, {2326,2327,2393 ,3588,3627,2445 }, + {5038,4743,5702 ,3628,3629,3630 }, {2156,6783,6464 ,3631,3632,3633 }, + {2528,924,3183 ,2763,3634,2832 }, {3195,3114,2649 ,3430,2792,2791 }, + {3494,3546,3492 ,736,2557,930 }, {2143,2099,2166 ,953,1093,1040 }, + {2611,2610,2524 ,3436,3479,3395 }, {3845,6215,1949 ,3635,3636,3637 }, + {3130,4032,3129 ,3414,3638,3412 }, {2982,3033,2981 ,3601,3579,3550 }, + {3071,3125,3111 ,3405,3404,2829 }, {489,3761,2955 ,3639,218,3640 }, + {3163,3162,3108 ,3361,3318,3415 }, {3109,3163,3108 ,3553,3361,3415 }, + {3205,3204,3162 ,3360,3314,3318 }, {3235,3234,3204 ,3394,3315,3314 }, + {5337,4866,4830 ,572,3641,3642 }, {2673,2724,2671 ,2974,2668,2705 }, + {3201,3231,3230 ,3328,3293,3294 }, {7704,7753,7727 ,3562,3643,3563 }, + {2815,2770,2847 ,2696,2681,2624 }, {2781,2827,2780 ,3373,3445,2830 }, + {1984,2530,2038 ,831,2389,1551 }, {1004,2652,1056 ,3452,3424,3388 }, + {7910,2097,2164 ,3582,846,3583 }, {2974,2997,3022 ,3116,3158,3393 }, + {2494,2629,2656 ,3391,2673,2893 }, {2973,2974,3022 ,3108,3116,3393 }, + {2869,2868,2832 ,1576,1577,638 }, {2194,2563,2146 ,1853,2707,1732 }, + {8135,4835,3148 ,2439,2130,814 }, {2952,2353,2489 ,2610,3170,2611 }, + {3216,1656,1657 ,3478,1166,1781 }, {2869,2832,2907 ,1576,638,111 }, + {5653,350,6115 ,3644,1114,3645 }, {2877,2878,2921 ,3026,2622,2691 }, + {2793,2426,2853 ,3048,3113,806 }, {1845,1337,1874 ,912,2148,962 }, + {1519,2990,79 ,2450,3646,1510 }, {6554,6619,4190 ,3647,1272,3439 }, + {777,2137,776 ,421,147,1120 }, {4445,2871,712 ,3648,3649,3650 }, + {2572,3138,1181 ,3050,2984,1981 }, {2868,2907,2940 ,1577,111,110 }, + {3331,3330,3291 ,1183,1185,1203 }, {2497,2201,2865 ,3336,2455,3107 }, + {3032,3031,3000 ,2858,2857,3304 }, {2889,2888,2826 ,2604,3651,3592 }, + {2626,2625,2839 ,2582,2584,2848 }, {2848,2889,2826 ,2605,2604,3592 }, + {3071,3111,3070 ,3405,2829,2828 }, {3071,3070,3032 ,3405,2828,2858 }, + {3033,3071,3032 ,3579,3405,2858 }, {2351,2201,2497 ,3258,2455,3336 }, + {2516,2515,2443 ,3554,2600,2942 }, {2444,2516,2443 ,2941,3554,2942 }, + {2442,2514,2513 ,2742,2741,3426 }, {3545,3574,3544 ,926,928,1153 }, + {3931,3944,3684 ,1868,729,1915 }, {3541,3572,3571 ,981,1931,3652 }, + {3195,219,3114 ,3430,3087,2792 }, {2711,2013,877 ,3435,2242,2243 }, + {689,3828,4206 ,1363,1364,682 }, {5488,6257,6039 ,2730,3428,2728 }, + {3114,2471,2536 ,2792,3653,2793 }, {1238,165,155 ,1569,1869,1497 }, + {2973,3022,3021 ,3108,3393,2891 }, {2930,2929,2884 ,3004,3003,2940 }, + {2387,2531,2647 ,3461,3654,3276 }, {3194,1487,2338 ,1065,531,458 }, + {3022,3060,3059 ,3393,3214,2892 }, {3021,3022,3059 ,2891,3393,2892 }, + {3075,2803,2833 ,2670,3655,2065 }, {2608,2550,2833 ,3656,3609,2065 }, + {2850,2608,2833 ,3657,3656,2065 }, {2608,2470,2460 ,3656,3658,3619 }, + {2550,2608,2460 ,3609,3656,3619 }, {2470,2541,2460 ,3658,1298,3619 }, + {3422,296,3664 ,2055,1558,1557 }, {3060,3100,3099 ,3214,2955,3603 }, + {3194,3189,2150 ,1065,1027,534 }, {3400,2410,3402 ,3659,1,3660 }, + {1218,1742,3008 ,3255,707,546 }, {3170,4032,1160 ,3510,3638,3661 }, + {2099,2100,2166 ,1093,1218,1040 }, {3059,3060,3099 ,2892,3214,3603 }, + {3100,3124,3099 ,2955,3604,3603 }, {2972,3020,3019 ,2047,2890,2709 }, + {3183,924,219 ,2832,3634,3087 }, {2889,2935,2888 ,2604,3310,3651 }, + {2935,2934,2888 ,3310,3602,3651 }, {740,2676,643 ,1595,2761,3285 }, + {2006,2032,2031 ,1181,1127,1560 }, {740,2701,2676 ,1595,3375,2761 }, + {2852,2617,2387 ,3275,2878,3461 }, {2360,2437,2359 ,3161,2576,3406 }, + {2294,2295,2359 ,3081,3160,3406 }, {2971,2972,3019 ,3338,2047,2709 }, + {1224,3047,3037 ,1508,3571,3501 }, {2922,2972,2971 ,2045,2047,3338 }, + {2793,2853,2630 ,3048,806,3139 }, {2592,2593,2667 ,2936,3111,2949 }, + {628,2717,728 ,1177,2676,1266 }, {2921,2922,2971 ,2691,2045,3338 }, + {3100,1590,3124 ,2955,3028,3604 }, {3211,3241,3240 ,3605,3504,3590 }, + {3210,3211,3240 ,3608,3605,3590 }, {2983,3034,2982 ,3589,3595,3601 }, + {3463,3432,3433 ,1271,929,3049 }, {2651,2803,3075 ,1516,3655,2670 }, + {2651,2850,2803 ,1516,3657,3655 }, {2651,2608,2850 ,1516,3656,3657 }, + {2418,2541,2470 ,3662,1298,3658 }, {451,242,2541 ,3387,1717,1298 }, + {2418,451,2541 ,3662,3387,1298 }, {2931,2978,2977 ,2780,2782,2997 }, + {2841,2649,3213 ,3443,2791,3486 }, {596,549,647 ,1462,1129,973 }, + {3034,3033,2982 ,3595,3579,3601 }, {875,6836,6353 ,579,852,749 }, + {2282,2328,2327 ,3663,3664,3627 }, {2328,287,286 ,3664,2470,2446 }, + {2327,2328,286 ,3627,3664,2446 }, {4246,1729,3837 ,3665,3666,3667 }, + {4377,2431,967 ,3090,2977,3668 }, {3020,3059,3058 ,2890,2892,2710 }, + {650,2267,2841 ,3074,3574,3443 }, {219,2471,3114 ,3087,3653,2792 }, + {2823,2824,2863 ,2825,3432,3242 }, {3065,3085,3105 ,3298,3381,3297 }, + {3085,3106,3105 ,3381,3380,3297 }, {2596,2640,2595 ,2771,2770,2983 }, + {2723,2775,2774 ,2706,3477,2916 }, {2722,2723,2774 ,3014,2706,2916 }, + {2509,2510,2592 ,3329,3162,2936 }, {2295,2360,2359 ,3160,3161,3406 }, + {3229,1726,3199 ,3266,1746,3244 }, {2300,1304,2551 ,2640,2639,3247 }, + {1336,2085,2604 ,2924,657,3046 }, {3566,2397,3768 ,1146,3669,1518 }, + {2267,2649,2841 ,3574,2791,3443 }, {1920,2155,2157 ,581,580,3411 }, + {3459,3460,3487 ,3241,3240,3356 }, {1789,2791,2429 ,3007,3483,3617 }, + {2656,2629,2461 ,2893,2673,2899 }, {3110,3164,3109 ,3337,3307,3553 }, + {1224,3037,2986 ,1508,3501,3500 }, {6071,6082,6088 ,1346,1348,3670 }, + {155,1183,1238 ,1497,1238,1569 }, {4978,5592,2206 ,178,3671,176 }, + {3546,3545,3492 ,2557,926,930 }, {2381,2608,2651 ,3672,3656,1516 }, + {2381,2470,2608 ,3672,3658,3656 }, {2468,2418,2470 ,3673,3662,3658 }, + {1313,5085,2251 ,3674,3675,3676 }, {1606,4055,4054 ,3677,3678,3679 }, + {689,1586,3315 ,1363,943,944 }, {2741,3212,2495 ,3317,3027,2920 }, + {2329,288,287 ,3680,2723,2470 }, {2328,2329,287 ,3664,3680,2470 }, + {3345,8138,5352 ,3476,2018,3502 }, {2647,2531,2413 ,3276,3654,3246 }, + {2778,2824,2823 ,3215,3432,2825 }, {2777,2778,2823 ,2824,3215,2825 }, + {2824,2887,2863 ,3432,3616,3242 }, {2387,2371,2633 ,3461,2880,2650 }, + {3202,3201,3159 ,3324,3328,3296 }, {3202,3232,3201 ,3324,3325,3328 }, + {2640,2671,2595 ,2770,2705,2983 }, {2900,2300,2551 ,2649,2640,3247 }, + {2604,2085,2964 ,3046,657,3011 }, {2964,2085,2131 ,3011,657,656 }, + {2677,886,213 ,3033,3464,3606 }, {3268,2456,2302 ,3321,2790,3313 }, + {6436,5255,6480 ,3681,3682,1524 }, {3916,3904,3879 ,969,1264,2549 }, + {2524,2610,2564 ,3395,3479,3546 }, {282,327,281 ,3498,2226,2227 }, + {3421,3278,2851 ,1156,1158,3051 }, {2792,2367,2732 ,2718,2764,2583 }, + {5191,5170,5914 ,2900,3683,1566 }, {2547,2470,2381 ,3684,3658,3672 }, + {2397,2547,2381 ,3669,3684,3672 }, {2547,3053,2470 ,3684,3685,3658 }, + {2655,2468,2470 ,3686,3673,3658 }, {3053,2655,2470 ,3685,3686,3658 }, + {2655,2418,2468 ,3686,3662,3673 }, {2467,2493,2840 ,3006,3005,2594 }, + {2524,2564,2523 ,3395,3546,3545 }, {2946,3194,2338 ,1062,1065,458 }, + {178,1847,1874 ,3043,1519,962 }, {2924,2974,2973 ,2593,3116,3108 }, + {2328,2283,2329 ,3664,3687,3680 }, {2909,1849,1785 ,3189,1660,1659 }, + {3148,1378,8135 ,814,925,2439 }, {6098,6388,5191 ,1565,3184,2900 }, + {2524,2523,2484 ,3395,3545,3499 }, {6970,3340,4831 ,3688,114,3154 }, + {3126,3125,3072 ,3689,3404,3403 }, {2664,628,529 ,2721,1177,1148 }, + {2584,2310,2476 ,2894,2442,2441 }, {2552,2584,2476 ,2657,2894,2441 }, + {2633,2584,2552 ,2650,2894,2657 }, {3160,3202,3159 ,3323,3324,3296 }, + {3647,3700,3646 ,2521,2545,2123 }, {2928,2926,2927 ,2578,517,516 }, + {2976,2998,2975 ,2999,3446,3157 }, {2362,2439,2438 ,2979,2980,3163 }, + {2361,2362,2438 ,3177,2979,3163 }, {2456,2626,2332 ,2790,2582,3322 }, + {2302,2456,2332 ,3313,2790,3322 }, {3164,3163,3109 ,3307,3361,3553 }, + {2532,178,1874 ,3186,3043,962 }, {2670,2669,2595 ,2982,3015,2983 }, + {3873,3931,3475 ,1809,1868,1559 }, {6309,6735,6308 ,3690,3472,3474 }, + {6088,5255,6436 ,3670,3682,3681 }, {1265,4548,1271 ,1273,589,588 }, + {2195,165,341 ,1851,1869,1852 }, {2547,2469,2655 ,3684,3691,3686 }, + {3053,2547,2655 ,3685,3684,3686 }, {2469,2418,2655 ,3691,3662,3686 }, + {2469,451,2418 ,3691,3387,3662 }, {2939,134,451 ,1870,1783,3387 }, + {3167,3209,3166 ,3692,3377,3454 }, {8078,8084,8124 ,1744,1139,1141 }, + {3216,3228,1656 ,3478,3042,1166 }, {2330,289,288 ,3693,1044,2723 }, + {2329,2330,288 ,3680,3693,2723 }, {925,2730,827 ,1287,2150,1220 }, + {1010,925,926 ,1286,1287,1284 }, {1146,1237,1236 ,2467,1467,1434 }, + {2844,2846,1047 ,1329,2623,1328 }, {3022,3023,3061 ,3393,3099,2953 }, + {2937,2936,2890 ,3570,2843,2603 }, {3105,3140,3159 ,3297,3327,3296 }, + {3140,3160,3159 ,3327,3323,3296 }, {3232,3231,3201 ,3325,3293,3328 }, + {3021,3020,2972 ,2891,2890,2047 }, {2976,2975,2926 ,2999,3157,517 }, + {2928,2976,2926 ,2578,2999,517 }, {2595,2639,2594 ,2983,3694,3110 }, + {2595,2594,2511 ,2983,3110,3112 }, {2356,3267,3229 ,3264,3265,3266 }, + {2909,3223,2911 ,3189,2922,2923 }, {2891,2890,1010 ,3560,2603,1286 }, + {3238,3239,2424 ,3379,3378,2778 }, {1103,2890,2891 ,1474,2603,3560 }, + {3167,3166,3125 ,3692,3454,3404 }, {3306,7276,3326 ,3695,3696,826 }, + {2497,2344,2351 ,3336,2852,3258 }, {1482,2915,2711 ,2265,3434,3435 }, + {3434,3433,3382 ,737,3049,870 }, {5219,5218,5198 ,1860,483,423 }, + {8101,8087,8099 ,383,385,2867 }, {3208,3209,3238 ,3455,3377,3379 }, + {2397,2651,3768 ,3669,1516,1518 }, {2688,451,2469 ,3697,3387,3691 }, + {2658,2939,451 ,3698,1870,3387 }, {2688,2658,451 ,3697,3698,3387 }, + {3645,3698,3644 ,1650,2122,1651 }, {6639,1213,4983 ,3699,3700,480 }, + {2985,2984,2937 ,3457,3458,3570 }, {3546,3597,3545 ,2557,1810,926 }, + {6257,5166,6039 ,3428,3427,2728 }, {2283,2285,2329 ,3687,3701,3680 }, + {2129,2130,2069 ,1469,1505,1711 }, {1855,3195,2649 ,3088,3430,2791 }, + {5327,6525,6524 ,3702,3703,3704 }, {2702,2254,2427 ,2854,3705,3237 }, + {2475,2715,3013 ,3047,2944,2946 }, {3095,2715,2835 ,2945,2944,3079 }, + {2512,2596,2595 ,2981,2771,2983 }, {2563,2125,2146 ,2707,892,1732 }, + {2691,2570,2544 ,3169,2612,2451 }, {2639,2669,2593 ,3694,3015,3111 }, + {2595,2669,2639 ,2983,3015,3694 }, {3026,3025,2998 ,3095,3094,3446 }, + {2899,3223,2909 ,2921,2922,3189 }, {3700,3699,3646 ,2545,2121,2123 }, + {3073,3127,3072 ,3516,2811,3403 }, {2429,2588,2556 ,3617,2072,2779 }, + {1789,2429,2556 ,3007,3617,2779 }, {2864,2891,1010 ,3386,3560,1286 }, + {3126,3167,3125 ,3689,3692,3404 }, {1482,2711,877 ,2265,3435,2243 }, + {2518,2517,2445 ,2388,3399,1388 }, {2984,2983,2936 ,3458,3589,2843 }, + {1740,1687,3196 ,3213,1104,2581 }, {3197,1740,3196 ,2599,3213,2581 }, + {2493,1789,2556 ,3005,3007,2779 }, {554,2987,2268 ,2588,3346,3292 }, + {2876,2706,2469 ,3706,3707,3691 }, {2547,2876,2469 ,3684,3706,3691 }, + {2706,2688,2469 ,3707,3697,3691 }, {2658,355,2939 ,3698,1876,1870 }, + {3035,3034,2983 ,3364,3595,3589 }, {2388,2493,2556 ,2608,3005,2779 }, + {685,3371,1309 ,2674,3708,3709 }, {3187,4236,4371 ,3402,3488,3489 }, + {157,2133,91 ,1877,2614,1984 }, {5455,1263,6509 ,686,1199,1452 }, + {4452,4795,4820 ,2398,3710,2399 }, {3073,3072,3034 ,3516,3403,3595 }, + {3035,3073,3034 ,3364,3516,3595 }, {6173,6088,6388 ,3711,3670,3184 }, + {110,74,2962 ,743,2053,3494 }, {2715,2386,2835 ,2944,3196,3079 }, + {369,418,470 ,1521,1610,1522 }, {2549,2622,2894 ,2841,2840,3140 }, + {2639,2593,2594 ,3694,3111,3110 }, {2773,2772,2720 ,2871,3198,3173 }, + {2721,2773,2720 ,2917,2871,3173 }, {2676,2675,2602 ,2761,3235,3234 }, + {3168,3167,3126 ,3396,3692,3689 }, {1452,1004,1592 ,1606,3452,2939 }, + {1936,5140,5359 ,848,3712,3713 }, {5326,5327,6524 ,3714,3702,3704 }, + {2201,968,2200 ,2455,1661,1570 }, {2401,651,2995 ,2855,3591,2856 }, + {2531,2387,2413 ,3654,3461,3246 }, {2937,2984,2936 ,3570,3458,2843 }, + {3168,3210,3167 ,3396,3608,3692 }, {2194,2146,2147 ,1853,1732,1562 }, + {2837,107,2501 ,2511,2548,2547 }, {2652,1004,31 ,3424,3452,3450 }, + {3054,3981,3076 ,916,915,2066 }, {2557,2397,3052 ,3620,3669,3715 }, + {2876,2547,2397 ,3706,3684,3669 }, {2557,2876,2397 ,3620,3706,3669 }, + {2984,3035,2983 ,3458,3364,3589 }, {2250,2286,2330 ,3716,3717,3693 }, + {2285,2250,2330 ,3701,3716,3693 }, {2286,290,289 ,3717,1042,1044 }, + {2330,2286,289 ,3693,3717,1044 }, {8067,8083,8097 ,2168,2103,1710 }, + {2000,2025,2024 ,2428,2430,2370 }, {3127,3126,3072 ,2811,3689,3403 }, + {3179,1249,2617 ,2947,2876,2878 }, {3472,3446,1165 ,3155,3718,3153 }, + {2647,2413,2462 ,3276,3246,3245 }, {3230,2356,3229 ,3294,3264,3266 }, + {2370,2630,1482 ,3216,3139,2265 }, {2637,1238,1373 ,2720,1569,1501 }, + {2131,2129,2653 ,656,1469,3010 }, {411,2899,2909 ,2919,2921,3189 }, + {2356,411,2909 ,3264,2919,3189 }, {3534,8072,3668 ,942,511,513 }, + {3127,3168,3126 ,2811,3396,3689 }, {2513,2597,2512 ,3426,2769,2981 }, + {6170,3537,3451 ,3719,1735,1720 }, {3210,3209,3167 ,3608,3377,3692 }, + {2587,90,3256 ,3274,2851,3308 }, {2717,2769,819 ,2676,2679,2244 }, + {2805,651,2401 ,3230,3591,2855 }, {2528,56,924 ,2763,2606,3634 }, + {2416,1789,2467 ,808,3007,3006 }, {2711,2631,2586 ,3435,2971,2972 }, + {2748,2876,2557 ,3720,3706,3620 }, {2580,2688,2706 ,3721,3697,3707 }, + {2688,2580,2658 ,3697,3721,3698 }, {2459,355,2658 ,3722,1876,3698 }, + {355,2459,250 ,1876,3722,1916 }, {3210,3240,3239 ,3608,3590,3378 }, + {3209,3210,3239 ,3377,3608,3378 }, {437,2589,529 ,1149,3204,1148 }, + {2151,109,349 ,1603,1654,566 }, {790,2682,829 ,1339,3480,1368 }, + {2003,2029,2028 ,2542,1748,1811 }, {2518,2560,2517 ,2388,3335,3399 }, + {2269,2505,2497 ,2881,2883,3336 }, {2987,2269,2497 ,3346,2881,3336 }, + {2819,2818,2772 ,2870,3156,3198 }, {3095,2835,3173 ,2945,3079,3114 }, + {162,2290,238 ,2822,1982,868 }, {2724,2726,2776 ,2668,2667,3192 }, + {2723,2776,2775 ,2706,3192,3477 }, {4086,284,557 ,3723,139,3724 }, + {1255,795,2576 ,3248,302,3076 }, {3025,3024,2975 ,3094,3098,3157 }, + {2884,2928,2883 ,2940,2578,2579 }, {2537,2427,2677 ,2985,3237,3033 }, + {2598,2599,2673 ,2602,2601,2974 }, {2269,2841,2506 ,2881,3443,2882 }, + {2552,2476,2300 ,2657,2441,2640 }, {8065,8094,8080 ,96,98,1094 }, + {1238,1515,1373 ,1569,1502,1501 }, {2489,2475,2298 ,2611,3047,3013 }, + {3429,3428,3378 ,1978,3261,1442 }, {3143,2537,968 ,3135,2985,1661 }, + {3699,3745,3744 ,2121,2546,2390 }, {2554,2805,2401 ,2853,3230,2855 }, + {1969,4644,1970 ,2369,2425,2427 }, {1482,877,58 ,2265,2243,2156 }, + {2748,2579,2706 ,3720,3725,3707 }, {2876,2748,2706 ,3706,3720,3707 }, + {2579,2580,2706 ,3725,3721,3707 }, {2580,2578,2658 ,3721,3552,3698 }, + {2578,2459,2658 ,3552,3722,3698 }, {2614,2613,2526 ,2638,2665,2636 }, + {3124,3123,3099 ,3604,3023,3603 }, {3597,3596,3545 ,1810,1792,926 }, + {2544,79,2691 ,2451,1510,3169 }, {6155,6413,6970 ,3726,112,3688 }, + {4302,4303,166 ,3727,3728,3729 }, {2685,2737,2684 ,2642,2626,2644 }, + {2692,56,2528 ,2845,2606,2763 }, {2411,2692,2528 ,2777,2845,2763 }, + {2602,2601,2517 ,3234,3398,3399 }, {2569,554,1336 ,3291,2588,2924 }, + {2647,2462,2353 ,3276,3245,3170 }, {2773,2819,2772 ,2871,2870,3198 }, + {2438,2510,2437 ,3163,3162,2576 }, {2771,2817,2816 ,3197,3174,3475 }, + {2816,2817,2880 ,3475,3174,2591 }, {3359,6393,3305 ,824,1556,879 }, + {4146,4170,3844 ,3730,3731,2443 }, {2819,2820,2883 ,2870,2869,2579 }, + {2202,2841,2269 ,2875,3443,2881 }, {2888,2887,2824 ,3651,3616,3432 }, + {2826,2888,2824 ,3592,3651,3432 }, {2676,2727,2675 ,2761,2760,3235 }, + {3379,3429,3378 ,1979,1978,1442 }, {554,785,1336 ,2588,2952,2924 }, + {2569,1336,2366 ,3291,2924,2589 }, {3087,3088,3131 ,3573,3290,3732 }, + {4068,4091,4077 ,408,123,126 }, {2826,2825,2779 ,3592,3431,2831 }, + {2780,2826,2779 ,2830,3592,2831 }, {1849,2909,1875 ,1660,3189,1663 }, + {2685,2684,2614 ,2642,2644,2638 }, {1205,1106,1107 ,3187,1664,2656 }, + {2949,2748,2557 ,3239,3720,3620 }, {2949,2965,2579 ,3239,3417,3725 }, + {2748,2949,2579 ,3720,3239,3725 }, {2765,2580,2579 ,2590,3721,3725 }, + {2389,2459,2578 ,2609,3722,3552 }, {2389,250,2459 ,2609,1916,3722 }, + {3164,3206,3163 ,3307,2886,3361 }, {725,818,817 ,721,1018,1020 }, + {4172,4173,4215 ,135,134,370 }, {6525,6573,6572 ,3703,3733,3734 }, + {291,290,2286 ,1012,1042,3717 }, {2287,291,2286 ,3735,1012,3717 }, + {6524,6525,6572 ,3704,3703,3734 }, {2371,2542,2656 ,2880,2879,2893 }, + {554,2085,785 ,2588,657,2952 }, {2572,1181,293 ,3050,1981,1430 }, + {2678,1917,1875 ,3190,833,1663 }, {2909,2678,1875 ,3189,3190,1663 }, + {1665,410,2967 ,3077,2204,2701 }, {2847,2816,2879 ,2624,3475,2621 }, + {149,2471,219 ,3736,3653,3087 }, {3175,2576,2628 ,3249,3076,3078 }, + {2427,2537,3149 ,3237,2985,3134 }, {3074,3073,3035 ,3363,3516,3364 }, + {3154,3153,3123 ,2692,2693,3023 }, {2742,2003,1565 ,2558,2542,3737 }, + {511,1565,2003 ,3569,3737,2542 }, {3494,3492,3493 ,736,930,3738 }, + {13,1303,1626 ,910,1933,564 }, {3792,2941,3791 ,3421,109,3739 }, + {2504,2310,2502 ,2898,2442,3740 }, {2331,2502,2310 ,2895,3740,2442 }, + {2584,2331,2310 ,2894,2895,2442 }, {90,2202,2269 ,2851,2875,2881 }, + {2826,2824,2825 ,3592,3432,3431 }, {3110,3109,3068 ,3337,3553,3555 }, + {2965,2765,2579 ,3417,2590,3725 }, {2761,2580,2765 ,2768,3721,2590 }, + {2761,2578,2580 ,2768,3552,3721 }, {2133,250,2389 ,2614,1916,2609 }, + {253,247,2663 ,2564,2569,3593 }, {3494,3493,3432 ,736,3738,929 }, + {2921,2971,1237 ,2691,3338,1467 }, {2886,2931,2885 ,2913,2780,2914 }, + {3434,3463,3433 ,737,1271,3049 }, {972,1073,2314 ,3277,3741,3742 }, + {2286,2250,2287 ,3717,3716,3735 }, {236,291,2287 ,2444,1012,3735 }, + {99,236,2287 ,3743,2444,3735 }, {1172,2993,1081 ,1758,3744,3745 }, + {819,2845,2800 ,2244,2995,2996 }, {2560,2602,2517 ,3335,3234,3399 }, + {3993,3994,4035 ,2807,2806,1601 }, {2999,3027,3026 ,2998,3350,3095 }, + {968,2572,293 ,1661,3050,1430 }, {2976,3026,2998 ,2999,3095,3446 }, + {2510,2511,2593 ,3162,3112,3111 }, {2166,2100,2167 ,1040,1218,1323 }, + {292,239,2292 ,2563,1103,1983 }, {2874,3175,2628 ,3195,3249,3078 }, + {3063,3103,3102 ,3093,3137,3097 }, {3062,3063,3102 ,3096,3093,3097 }, + {2722,2774,2721 ,3014,2916,2917 }, {2503,2353,2691 ,2948,3170,3169 }, + {3149,3260,2201 ,3134,3188,2455 }, {2981,2980,2933 ,3550,3305,3556 }, + {2981,3000,2980 ,3550,3304,3305 }, {6388,6088,6436 ,3184,3670,3681 }, + {2416,2467,2631 ,808,3006,2971 }, {3128,3127,3073 ,2809,2811,3516 }, + {4426,6837,6577 ,1056,1057,97 }, {2908,1249,3179 ,3052,2876,2947 }, + {3138,1050,1181 ,2984,2282,1981 }, {1406,1329,1370 ,1135,1134,1500 }, + {2700,2726,2725 ,3009,2667,2666 }, {2673,2700,2725 ,2974,3009,2666 }, + {2888,2934,2933 ,3651,3602,3556 }, {2976,2999,3026 ,2999,2998,3095 }, + {2887,2888,2933 ,3616,3651,3556 }, {6573,6613,6612 ,3733,3746,1389 }, + {2722,2721,2669 ,3014,2917,3015 }, {2949,2557,2920 ,3239,3620,1799 }, + {2914,2765,2965 ,2573,2590,3417 }, {2635,2578,2761 ,2607,3552,2768 }, + {2430,2133,2389 ,2613,2614,2609 }, {2131,2132,2865 ,656,655,3107 }, + {3036,3035,2984 ,3362,3364,3458 }, {6572,6573,6612 ,3734,3733,1389 }, + {2735,929,830 ,2887,1234,1996 }, {5702,3009,5038 ,3630,3747,3628 }, + {3482,3478,8069 ,2494,885,1095 }, {2556,2449,2430 ,2779,3259,2613 }, + {2449,91,2430 ,3259,1984,2613 }, {2934,2981,2933 ,3602,3550,3556 }, + {3235,3236,2456 ,3394,2717,2790 }, {2863,2886,2822 ,3242,2913,2826 }, + {7371,7423,7370 ,3748,3749,3750 }, {2249,6897,6531 ,3751,54,2280 }, + {3258,3484,1009 ,542,283,3752 }, {7797,7848,7823 ,3506,343,345 }, + {3763,2707,1305 ,3753,235,466 }, {4083,4157,5458 ,3754,3755,3756 }, + {1376,4610,4788 ,1937,3757,3758 }, {7826,7876,7853 ,1616,1725,1617 }, + {5352,8138,4022 ,3502,2018,3503 }, {6649,6648,6612 ,3759,1390,1389 }, + {1156,106,5138 ,3100,1073,1940 }, {2622,2567,2901 ,2840,2842,3468 }, + {3648,3701,3647 ,734,733,2521 }, {4248,3972,1159 ,3256,3487,3760 }, + {836,1068,1385 ,3551,3761,3762 }, {3819,1159,5414 ,3763,3760,3764 }, + {7412,7410,7411 ,3765,3766,3767 }, {3306,3326,3325 ,3695,826,825 }, + {1668,689,4206 ,3212,1363,682 }, {1668,4206,141 ,3212,682,681 }, + {7825,7850,7824 ,3768,3769,3770 }, {1009,2607,1250 ,3752,2452,3771 }, + {7246,7268,7291 ,3772,3773,3774 }, {7979,8003,8002 ,3775,3776,3777 }, + {3690,958,872 ,3778,3779,2962 }, {5414,1159,3673 ,3764,3760,648 }, + {2731,3690,872 ,659,3778,2962 }, {3690,2394,958 ,3778,3780,3779 }, + {1594,3312,958 ,3781,3782,3779 }, {2394,1594,958 ,3780,3781,3779 }, + {5731,3615,5682 ,934,201,3783 }, {1594,3098,3420 ,3781,3784,3785 }, + {3312,1594,3420 ,3782,3781,3785 }, {3098,3254,3299 ,3784,3233,3232 }, + {3420,3098,3299 ,3785,3784,3232 }, {6355,6356,5151 ,3786,3787,1803 }, + {6311,8114,8122 ,1097,2286,384 }, {3169,3128,3170 ,2810,2809,3510 }, + {1510,3893,5862 ,3788,3789,3790 }, {8075,8105,8107 ,2308,3791,658 }, + {3454,3340,4424 ,710,114,3792 }, {4424,3340,6414 ,3792,114,113 }, + {5327,5300,6525 ,3702,3793,3703 }, {1942,1668,141 ,2337,3212,681 }, + {6415,4424,6414 ,386,3792,113 }, {8089,8097,3861 ,254,1710,1147 }, + {5259,4827,5282 ,3794,2734,1194 }, {5302,5706,6008 ,3795,3796,3797 }, + {2892,1669,771 ,918,3798,3799 }, {2582,3513,1017 ,873,441,1793 }, + {5299,6041,5251 ,3800,3801,3802 }, {7796,7823,7795 ,3803,345,3804 }, + {3371,685,794 ,3708,2674,3392 }, {3306,3325,3305 ,3695,825,879 }, + {3932,652,55 ,1096,334,3507 }, {3998,4040,4039 ,381,460,382 }, + {637,3585,3736 ,3805,3806,3807 }, {3474,637,3736 ,3808,3805,3807 }, + {1068,3685,3057 ,3761,3809,3810 }, {3685,3474,3736 ,3809,3808,3807 }, + {1385,1068,3057 ,3762,3761,3810 }, {3685,3736,3057 ,3809,3807,3810 }, + {541,1385,3057 ,3811,3762,3810 }, {3147,3477,4162 ,2285,1736,1738 }, + {8096,8136,8115 ,1796,2449,2448 }, {3477,4129,5785 ,1736,3812,1737 }, + {5680,5698,5679 ,50,44,2553 }, {836,1385,541 ,3551,3762,3811 }, + {2746,8089,3861 ,1517,254,1147 }, {3113,1734,269 ,410,2219,3813 }, + {3413,3441,3391 ,3814,2987,3815 }, {3701,3700,3647 ,733,2545,2521 }, + {296,3317,630 ,1558,1882,2491 }, {3493,3492,3432 ,3738,930,929 }, + {8124,8110,3271 ,1141,1140,1727 }, {3939,3955,3090 ,3539,3538,2805 }, + {4027,4712,1020 ,3470,3816,3817 }, {5231,922,5000 ,115,117,286 }, + {6041,5298,5273 ,3801,3818,3819 }, {3891,3890,3869 ,3820,1913,1912 }, + {7823,7847,7795 ,345,3521,3804 }, {2856,588,3549 ,3821,3822,3823 }, + {3690,1594,2394 ,3778,3781,3780 }, {5698,5726,5725 ,44,515,3824 }, + {4178,5297,4023 ,3825,3826,3827 }, {1694,836,541 ,3828,3551,3811 }, + {3453,3054,1787 ,917,916,2897 }, {531,34,62 ,3829,3830,3831 }, + {3441,3468,3440 ,2987,2688,2690 }, {3187,4371,3972 ,3402,3489,3487 }, + {3491,3544,3490 ,955,1153,2502 }, {7074,7073,7038 ,2747,662,3832 }, + {6613,6649,6612 ,3746,3759,1389 }, {6650,6680,6648 ,3833,3834,1390 }, + {3855,3891,3869 ,1511,3820,1912 }, {3504,3503,3468 ,2986,3835,2688 }, + {3441,3440,3391 ,2987,2690,3815 }, {3549,3369,62 ,3823,3836,3831 }, + {3529,3371,794 ,3837,3708,3392 }, {3369,531,62 ,3836,3829,3831 }, + {3529,794,3476 ,3837,3392,3838 }, {3568,3529,3476 ,3839,3837,3838 }, + {1783,543,1341 ,2251,3840,3841 }, {3891,3926,3890 ,3820,3125,1913 }, + {3612,3819,3473 ,3842,3763,649 }, {5785,4129,4162 ,1737,3812,1738 }, + {5242,5241,5220 ,3843,2120,2119 }, {7253,7252,6349 ,1855,3437,705 }, + {3764,3718,3316 ,3844,3845,2149 }, {3948,915,4326 ,280,443,219 }, + {3264,836,1694 ,2468,3551,3828 }, {882,3264,1694 ,3846,2468,3828 }, + {5258,5259,5282 ,1193,3794,1194 }, {3924,3968,3923 ,3847,3848,3849 }, + {5224,5244,5223 ,3850,79,3851 }, {5224,5223,4679 ,3850,3851,3852 }, + {7982,8007,7958 ,3853,3854,3855 }, {3339,3690,2731 ,3856,3778,659 }, + {3006,3339,2731 ,414,3856,659 }, {3279,1594,3690 ,3857,3781,3778 }, + {3339,3279,3690 ,3856,3857,3778 }, {1415,3098,1594 ,3858,3784,3781 }, + {3279,1415,1594 ,3857,3858,3781 }, {5286,5285,5241 ,3859,2169,2120 }, + {3392,3413,3391 ,3860,3814,3815 }, {5640,5679,5678 ,2214,2553,3861 }, + {1974,3549,62 ,3862,3823,3831 }, {4157,5481,5304 ,3755,3863,654 }, + {5639,5640,5656 ,2215,2214,3864 }, {5656,5640,5678 ,3864,2214,3861 }, + {5380,5385,5304 ,653,3865,654 }, {6649,6650,6648 ,3759,3833,1390 }, + {3511,3254,3098 ,539,3233,3784 }, {1415,3511,3098 ,3858,539,3784 }, + {3511,4019,3254 ,539,227,3233 }, {5013,8074,5950 ,684,2460,2873 }, + {6698,6697,6680 ,629,3866,3834 }, {3396,3764,3316 ,3867,3844,2149 }, + {2256,6390,2162 ,2323,3868,3278 }, {3828,4199,4186 ,1364,3869,683 }, + {4206,3828,4186 ,682,1364,683 }, {3864,138,1206 ,3517,434,2218 }, + {3972,3864,1206 ,3487,3517,2218 }, {6650,6698,6680 ,3833,629,3834 }, + {4009,4008,3967 ,3870,3871,3872 }, {6073,6055,3659 ,3873,3874,3875 }, + {834,96,1539 ,3876,836,835 }, {4861,4896,4669 ,3877,3878,3879 }, + {1724,3425,1249 ,3880,1859,2876 }, {834,1588,2856 ,3876,3881,3821 }, + {5354,4866,3907 ,3882,3641,3883 }, {3266,543,1783 ,3884,3840,2251 }, + {7796,7795,7745 ,3803,3804,650 }, {2785,1907,3878 ,1246,1245,1248 }, + {6574,6614,6613 ,3885,3886,3746 }, {3652,3651,3602 ,3887,3888,3889 }, + {3603,3652,3602 ,3890,3887,3889 }, {3392,3391,3332 ,3860,3815,3891 }, + {3309,3333,3308 ,3892,3893,3894 }, {3333,3392,3332 ,3893,3860,3891 }, + {3309,3308,3294 ,3892,3894,3895 }, {3333,3332,3308 ,3893,3891,3894 }, + {1415,3279,3339 ,3858,3857,3856 }, {3476,794,3252 ,3838,3392,781 }, + {1207,5153,5146 ,1074,3896,1075 }, {4291,920,4926 ,58,57,3897 }, + {3681,3258,1250 ,691,542,3771 }, {2813,3911,3738 ,3898,3899,3900 }, + {3294,3308,7110 ,3895,3894,1990 }, {3810,3853,3809 ,3901,3902,3903 }, + {5816,5833,5832 ,545,544,3904 }, {3968,4009,3967 ,3848,3870,3872 }, + {2472,13,1626 ,2009,910,564 }, {2659,3286,601 ,3485,56,3905 }, + {4005,2422,4004 ,3906,3907,3908 }, {4007,2798,4005 ,3909,420,3906 }, + {789,3878,1156 ,3910,1248,3100 }, {1510,1410,3893 ,3788,3911,3789 }, + {28,1096,88 ,1059,2727,3581 }, {3439,3438,3389 ,2689,3912,3913 }, + {3604,3603,3554 ,3384,3890,3914 }, {3861,8083,3456 ,1147,2103,1145 }, + {109,254,2039 ,1654,2008,1655 }, {3286,79,2990 ,56,1510,3646 }, + {2832,3694,2907 ,638,3915,111 }, {1882,4543,4980 ,894,3002,241 }, + {4714,4778,4777 ,1031,3916,3917 }, {6310,6742,6735 ,3918,3422,3472 }, + {3243,1111,4845 ,3919,3920,3921 }, {3336,6285,7095 ,3922,3923,3924 }, + {3911,1940,3738 ,3899,3925,3900 }, {3764,1412,3841 ,3844,3926,3927 }, + {3718,3764,3841 ,3845,3844,3927 }, {1412,2813,3738 ,3926,3898,3900 }, + {3841,1412,3738 ,3927,3926,3900 }, {3911,3447,1940 ,3899,3928,3925 }, + {552,3900,2945 ,3929,884,1239 }, {978,1029,977 ,1620,3930,3931 }, + {3373,206,1804 ,693,3932,2902 }, {3425,1693,1249 ,1859,264,2876 }, + {2357,2912,2422 ,3933,3934,3907 }, {3981,8140,3263 ,915,2269,252 }, + {3713,3712,3654 ,3935,1439,1438 }, {3655,3713,3654 ,3936,3935,1438 }, + {1215,6950,5006 ,3937,1349,1351 }, {5701,5682,5683 ,3938,3783,3939 }, + {8112,6351,8079 ,3530,1568,55 }, {5732,5731,5682 ,3940,934,3783 }, + {5701,5732,5682 ,3938,3940,3783 }, {4186,4199,1607 ,683,3869,3941 }, + {5832,3727,5831 ,3904,3942,3943 }, {931,1256,2910 ,3944,3945,3946 }, + {3224,931,2910 ,3947,3944,3946 }, {3653,3678,3652 ,1440,3948,3887 }, + {5732,5770,5769 ,3940,3949,932 }, {5731,5732,5769 ,934,3940,932 }, + {5770,5768,5769 ,3949,933,932 }, {7649,7699,7648 ,3950,3951,3952 }, + {5092,3766,4585 ,3953,3954,3955 }, {3619,2790,2740 ,3956,3576,3456 }, + {3554,3553,3502 ,3914,3957,3958 }, {3503,3554,3502 ,3835,3914,3958 }, + {592,1018,3368 ,1863,3959,3960 }, {20,348,75 ,3961,643,3962 }, + {7452,7451,7424 ,1875,3963,1188 }, {4005,4004,3964 ,3906,3908,3964 }, + {2422,731,4004 ,3907,3965,3908 }, {2798,2357,2422 ,420,3933,3907 }, + {6279,6278,7092 ,3966,3967,3968 }, {6870,6898,6796 ,2616,1946,2617 }, + {5308,564,4868 ,3528,3529,3969 }, {3712,3713,3714 ,1439,3935,3970 }, + {8138,3534,4022 ,2018,942,3503 }, {3537,3725,2072 ,1735,3971,1812 }, + {1556,3283,3252 ,3365,262,781 }, {406,746,2958 ,2566,2661,1374 }, + {3329,3362,3328 ,3972,1200,3973 }, {3616,4186,2795 ,3974,683,3975 }, + {3218,5480,4162 ,3976,1739,1738 }, {3962,3961,3918 ,3977,2346,2335 }, + {1256,931,1518 ,3945,3944,3978 }, {3652,3677,3651 ,3887,3979,3888 }, + {3708,3707,3651 ,3980,3981,3888 }, {4764,4765,4797 ,3982,3983,3984 }, + {6603,6744,1116 ,3985,3986,1401 }, {5838,6015,6083 ,3987,3988,3989 }, + {4837,1375,955 ,3990,325,3991 }, {3899,3760,5146 ,1938,2250,1075 }, + {3503,3502,3467 ,3835,3958,3992 }, {3440,3439,3390 ,2690,2689,3509 }, + {3391,3440,3390 ,3815,2690,3509 }, {635,20,75 ,2183,3961,3962 }, + {8134,6897,8120 ,3263,54,906 }, {5266,5288,5243 ,81,3993,3994 }, + {5306,5305,5335 ,462,2283,2523 }, {7425,7454,7397 ,3995,3996,1186 }, + {4868,4888,5379 ,3969,3997,3998 }, {5805,4129,5385 ,3999,3812,3865 }, + {6467,2407,6484 ,4000,4001,685 }, {4868,5340,5287 ,3969,4002,4003 }, + {6847,6899,3891 ,4004,4005,3820 }, {4129,5458,5385 ,3812,3756,3865 }, + {4199,694,4487 ,3869,1987,4006 }, {1333,2812,8064 ,849,1914,3141 }, + {3601,3650,3624 ,4007,4008,4009 }, {3257,5855,1974 ,4010,4011,3862 }, + {1584,694,4199 ,4012,1987,3869 }, {3809,3808,3753 ,3903,4013,4014 }, + {3252,1941,3283 ,781,1034,262 }, {4713,5768,5770 ,4015,933,3949 }, + {3907,4914,917 ,3883,65,4016 }, {1561,1459,407 ,1865,4017,4018 }, + {7595,7649,7594 ,4019,3950,4020 }, {4252,4157,4083 ,4021,3755,3754 }, + {2945,3339,3006 ,1239,3856,414 }, {7874,7924,7873 ,344,815,817 }, + {5392,5717,5122 ,4022,4023,4024 }, {3755,3809,3753 ,4025,3903,4014 }, + {5217,4797,5258 ,4026,3984,1193 }, {1018,3681,1486 ,3959,691,4027 }, + {3750,3681,1018 ,690,691,3959 }, {3681,1250,1486 ,691,3771,4027 }, + {4221,2955,592 ,1862,3640,1863 }, {592,3750,1018 ,1863,690,3959 }, + {4516,1152,1692 ,4028,4029,4030 }, {588,592,3368 ,3822,1863,3960 }, + {3274,3819,3612 ,3172,3763,3842 }, {834,1670,1588 ,3876,2327,3881 }, + {2314,6467,2223 ,3742,4000,2336 }, {530,2044,959 ,512,4031,1358 }, + {3747,3701,6249 ,2925,733,4032 }, {2203,1415,3339 ,4033,3858,3856 }, + {2945,2203,3339 ,1239,4033,3856 }, {3534,8121,6353 ,942,3466,749 }, + {3603,3602,3553 ,3890,3889,3957 }, {3554,3603,3553 ,3914,3890,3957 }, + {3748,3218,5385 ,4034,3976,3865 }, {1444,382,3318 ,4035,3534,4036 }, + {6030,5475,5576 ,4037,4038,4039 }, {3663,3511,1415 ,4040,539,3858 }, + {2203,3663,1415 ,4033,4040,3858 }, {5858,5254,3436 ,4041,4042,2145 }, + {556,5295,4471 ,4043,2188,2187 }, {4394,4323,4273 ,4044,4045,120 }, + {4043,2798,4007 ,419,420,3909 }, {6750,6749,6697 ,631,4046,3866 }, + {530,8072,3040 ,512,511,619 }, {4199,4487,1607 ,3869,4006,3941 }, + {2246,7067,5755 ,591,3585,4047 }, {4323,1297,2042 ,4045,4048,121 }, + {3809,3833,3808 ,3903,4049,4013 }, {4273,4323,2042 ,120,4045,121 }, + {803,6204,6203 ,144,4050,1249 }, {7797,7796,7745 ,3506,3803,650 }, + {3663,4029,3511 ,4040,225,539 }, {8092,8130,8098 ,2267,905,907 }, + {733,184,5006 ,4051,4052,1351 }, {4105,5392,5122 ,4053,4022,4024 }, + {1152,3274,3612 ,4029,3172,3842 }, {3819,4468,3473 ,3763,647,649 }, + {3001,1159,3819 ,3210,3760,3763 }, {3819,5414,4468 ,3763,3764,647 }, + {5957,4105,5122 ,4054,4053,4024 }, {3886,3887,3885 ,4055,4056,4057 }, + {8105,3006,8137 ,3791,414,2174 }, {3612,3473,2843 ,3842,649,4058 }, + {3577,2843,4469 ,4059,4058,4060 }, {1102,1733,4165 ,2317,2328,640 }, + {1692,2843,3577 ,4030,4058,4059 }, {1692,3612,2843 ,4030,3842,4058 }, + {3368,1018,3577 ,3960,3959,4059 }, {1486,1692,3577 ,4027,4030,4059 }, + {3344,592,588 ,1861,1863,3822 }, {1211,3327,3636 ,2007,439,1060 }, + {3844,1283,2476 ,2443,2978,2441 }, {3668,530,959 ,513,512,1358 }, + {3807,3806,3752 ,4061,4062,4063 }, {5799,5800,5815 ,4064,702,4065 }, + {3828,1454,4199 ,1364,2318,3869 }, {22,1468,2042 ,4066,174,121 }, + {2013,2380,1332 ,2242,2597,3492 }, {3292,3331,3291 ,4067,1183,1203 }, + {2607,4516,1250 ,2452,4028,3771 }, {3922,3965,3921 ,4068,4069,4070 }, + {3755,3754,3707 ,4025,4071,3981 }, {3677,3708,3651 ,3979,3980,3888 }, + {3708,3755,3707 ,3980,4025,3981 }, {3755,3753,3754 ,4025,4014,4071 }, + {1297,22,2042 ,4048,4066,121 }, {22,1451,1468 ,4066,175,174 }, + {3289,3288,7196 ,2056,2058,2401 }, {5928,3903,3915 ,4072,296,1575 }, + {7102,7129,7101 ,1240,1026,4073 }, {22,3786,1451 ,4066,4074,175 }, + {5693,5715,5714 ,4075,4076,4077 }, {3484,1300,1009 ,283,285,3752 }, + {4009,4007,4008 ,3870,3909,3871 }, {1018,1486,3577 ,3959,4027,4059 }, + {6281,6280,7090 ,4078,4079,4080 }, {6698,6750,6697 ,629,631,3866 }, + {3981,2833,3076 ,915,2065,2066 }, {2155,6353,5444 ,580,749,3410 }, + {2681,3119,2346 ,3254,3223,3252 }, {4856,4570,3834 ,4081,4082,4083 }, + {3006,552,2945 ,414,3929,1239 }, {2265,177,2297 ,2662,3618,2663 }, + {1483,834,1539 ,4084,3876,835 }, {3887,3923,3922 ,4056,3849,4068 }, + {3885,3887,3922 ,4057,4056,4068 }, {6386,4856,3834 ,4085,4081,4083 }, + {303,915,3948 ,4086,443,280 }, {68,4813,1655 ,4087,4088,4089 }, + {8100,4021,3482 ,1713,1712,2494 }, {1335,1443,1152 ,236,182,4029 }, + {3753,3807,3752 ,4014,4061,4063 }, {3871,1334,3748 ,397,1935,4034 }, + {1250,4516,1486 ,3771,4028,4027 }, {3555,3554,3503 ,4090,3914,3835 }, + {3972,1206,5389 ,3487,2218,4091 }, {6096,3765,6050 ,4092,4093,4094 }, + {3258,1009,1250 ,542,3752,3771 }, {4527,5305,3147 ,1740,2283,2285 }, + {4915,917,4914 ,64,4016,65 }, {3965,3964,3921 ,4069,3964,4070 }, + {5228,5247,5227 ,4095,4096,4097 }, {1935,913,915 ,4098,444,443 }, + {4866,5337,5354 ,3641,572,3882 }, {3968,3967,3923 ,3848,3872,3849 }, + {303,1935,915 ,4086,4098,443 }, {3263,2651,3075 ,252,1516,2670 }, + {3747,3700,3701 ,2925,2545,733 }, {3278,2691,79 ,1158,3169,1510 }, + {177,2363,2297 ,3618,2571,2663 }, {106,5146,5139 ,1073,1075,1939 }, + {3457,3348,2043 ,4099,2255,4100 }, {3348,684,2043 ,2255,4101,4100 }, + {2910,1256,3004 ,3946,3945,2437 }, {1219,1170,3471 ,4102,4103,4104 }, + {2795,4186,1607 ,3975,683,3941 }, {202,5957,5122 ,4105,4054,4024 }, + {1935,3622,913 ,4098,4106,444 }, {3850,3851,3885 ,4107,4108,4057 }, + {3851,3886,3885 ,4108,4055,4057 }, {3851,3850,3808 ,4108,4107,4013 }, + {3833,3851,3808 ,4049,4108,4013 }, {3966,4007,3965 ,4109,3909,4069 }, + {4008,4007,3967 ,3871,3909,3872 }, {4007,4005,4006 ,3909,3906,4110 }, + {7108,7095,7109 ,4111,3924,4112 }, {2607,1335,4516 ,2452,236,4028 }, + {5806,248,3350 ,4113,4114,2541 }, {3330,3329,3290 ,1185,3972,1204 }, + {3291,3330,3290 ,1203,1185,1204 }, {3330,3363,3329 ,1185,1911,3972 }, + {3389,3388,3329 ,3913,4115,3972 }, {3363,3389,3329 ,1911,3913,3972 }, + {7899,7898,7875 ,4116,4117,4118 }, {3931,3664,3475 ,1868,1557,1559 }, + {8125,8068,8117 ,752,1784,4119 }, {4516,1692,1486 ,4028,4030,4027 }, + {3972,5389,4185 ,3487,4091,409 }, {3001,4248,1159 ,3210,3256,3760 }, + {3709,3708,3652 ,4120,3980,3887 }, {8038,8052,8022 ,4121,4122,4123 }, + {5871,3337,5116 ,4124,4125,4126 }, {3294,7110,3309 ,3895,1990,3892 }, + {2380,2624,3012 ,2597,3132,3491 }, {3732,3818,2785 ,3536,1244,1246 }, + {5592,5705,1608 ,3671,4127,4128 }, {2813,3447,3911 ,3898,3928,3899 }, + {684,11,2043 ,4101,299,4100 }, {3926,3925,3889 ,3125,3227,3126 }, + {2968,2693,1693 ,263,3416,264 }, {1295,3348,3040 ,618,2255,619 }, + {2039,2472,1626 ,1655,2009,564 }, {3727,3616,1794 ,3942,3974,4129 }, + {3808,3850,3806 ,4013,4107,4062 }, {3849,3850,3884 ,4130,4107,4131 }, + {3724,5644,5495 ,4132,4133,4134 }, {3348,3457,3040 ,2255,4099,619 }, + {3556,3554,3555 ,3385,3914,4090 }, {7995,8020,7973 ,4135,1405,3624 }, + {273,272,205 ,1749,1680,1741 }, {5198,4710,5177 ,423,4136,424 }, + {2912,1379,931 ,3934,4137,3944 }, {4007,4006,3965 ,3909,4110,4069 }, + {3966,3965,3922 ,4109,4069,4068 }, {3923,3966,3922 ,3849,4109,4068 }, + {2912,3178,731 ,3934,4138,3965 }, {2798,2422,4005 ,420,3907,3906 }, + {6786,6785,6749 ,4139,4140,4046 }, {6750,6786,6749 ,631,4139,4046 }, + {3412,3387,3388 ,4141,1787,4115 }, {3389,3412,3388 ,3913,4141,4115 }, + {3438,3437,3387 ,3912,4142,1787 }, {1670,834,1483 ,2327,3876,4084 }, + {2698,5335,4236 ,105,2523,3488 }, {3706,3752,3705 ,4143,4063,4144 }, + {3624,3650,3601 ,4009,4008,4007 }, {6611,5401,6571 ,1391,4145,4146 }, + {5800,5816,5815 ,702,545,4065 }, {1588,2856,5855 ,3881,3821,4011 }, + {1005,3338,4368 ,3535,4147,4148 }, {3883,3922,3921 ,4149,4068,4070 }, + {3849,3884,3883 ,4130,4131,4149 }, {918,558,2044 ,4150,4151,4031 }, + {530,918,2044 ,512,4150,4031 }, {2868,2832,2869 ,1577,638,1576 }, + {3447,3736,3585 ,3928,3807,3806 }, {915,3341,4326 ,443,541,219 }, + {3622,4289,2892 ,4106,4152,918 }, {7090,6280,7091 ,4080,4079,4153 }, + {3283,3476,3252 ,262,3838,781 }, {913,3622,2892 ,444,4106,918 }, + {3880,3881,3918 ,2320,4154,2335 }, {4289,3172,2892 ,4152,4155,918 }, + {3850,3885,3884 ,4107,4057,4131 }, {694,2477,1607 ,1987,642,3941 }, + {5438,5439,6705 ,4156,4157,4158 }, {3296,3313,177 ,4159,4160,3618 }, + {3313,3343,3342 ,4160,2515,2514 }, {177,3313,3342 ,3618,4160,2514 }, + {166,85,4250 ,3729,2504,4161 }, {3178,2912,931 ,4138,3934,3944 }, + {7847,7846,7795 ,3521,4162,3804 }, {5831,1794,5830 ,3943,4129,4163 }, + {3257,1588,5855 ,4010,3881,4011 }, {2856,3549,1974 ,3821,3823,3862 }, + {5458,4157,5385 ,3756,3755,3865 }, {1152,3612,1692 ,4029,3842,4030 }, + {531,3577,4469 ,3829,4059,4060 }, {96,834,3909 ,836,3876,4164 }, + {3412,3438,3387 ,4141,3912,1787 }, {1733,1483,4165 ,2328,4084,640 }, + {1733,1670,1483 ,2328,2327,4084 }, {5305,4527,4236 ,2283,1740,3488 }, + {2405,4900,5578 ,4165,585,587 }, {2296,2264,2265 ,2664,4166,2662 }, + {3553,3601,3552 ,3957,4007,4167 }, {3849,3848,3806 ,4130,4168,4062 }, + {3848,3849,3883 ,4168,4130,4149 }, {1335,1152,4516 ,236,4029,4028 }, + {4162,5805,5385 ,1738,3999,3865 }, {3696,3697,3742 ,2325,2224,3419 }, + {3540,3541,3571 ,3357,981,3652 }, {6804,6803,6785 ,3342,4169,4140 }, + {5354,3907,4866 ,3882,3883,3641 }, {3285,4900,2405 ,3064,585,4165 }, + {6116,3336,7095 ,4170,3922,3924 }, {7974,7995,7973 ,4171,4135,3624 }, + {2088,3932,55 ,1667,1096,3507 }, {5764,5800,5799 ,537,702,4064 }, + {36,35,3866 ,1478,1338,1479 }, {3967,4007,3966 ,3872,3909,4109 }, + {3172,3093,1669 ,4155,4172,3798 }, {1538,3402,2410 ,0,3660,1 }, + {882,3271,8117 ,3846,1727,4119 }, {3613,2203,2945 ,4173,4033,1239 }, + {3455,3613,2945 ,886,4173,1239 }, {2892,3172,1669 ,918,4155,3798 }, + {3652,3708,3677 ,3887,3980,3979 }, {3468,3503,3467 ,2688,3835,3992 }, + {1248,1207,3878 ,2850,1074,1248 }, {5138,5139,5260 ,1940,1939,4174 }, + {834,1588,3257 ,3876,3881,4010 }, {635,2657,20 ,2183,2185,3961 }, + {3438,3466,3437 ,3912,4175,4142 }, {3501,3500,3437 ,4176,4177,4142 }, + {3466,3501,3437 ,4175,4176,4142 }, {3579,3550,3551 ,4178,4179,4180 }, + {3552,3579,3551 ,4167,4178,4180 }, {1454,1102,694 ,2318,2317,1987 }, + {1102,4165,694 ,2317,640,1987 }, {3473,3673,2153 ,649,648,4181 }, + {5109,5107,5108 ,4182,4183,4184 }, {874,3866,783 ,636,1479,679 }, + {4913,4866,3907 ,4185,3641,3883 }, {2368,2443,2365 ,2943,2942,3351 }, + {4003,1003,4002 ,4186,4187,4188 }, {1909,3663,2203 ,4189,4040,4033 }, + {3613,1909,2203 ,4173,4189,4033 }, {1338,4029,3663 ,1077,225,4040 }, + {1909,1338,3663 ,4189,1077,4040 }, {3093,5119,4942 ,4172,4190,4191 }, + {1669,3093,4942 ,3798,4172,4191 }, {6226,5562,3905 ,4192,4193,4194 }, + {3656,3655,3626 ,4195,3936,4196 }, {5705,5992,1608 ,4127,4197,4128 }, + {1022,969,793 ,4198,4199,4200 }, {4855,4809,1469 ,4201,4202,2361 }, + {3297,3314,3313 ,4203,3575,4160 }, {3313,3314,3343 ,4160,3575,2515 }, + {6786,6804,6785 ,4139,3342,4140 }, {3653,3652,3603 ,1440,3887,3890 }, + {5573,1525,407 ,2217,1866,4018 }, {3888,3924,3887 ,4204,3847,4056 }, + {3391,3390,3331 ,3815,3509,1183 }, {3332,3391,3331 ,3891,3815,1183 }, + {6803,6853,5587 ,4169,2505,1459 }, {3579,3601,3550 ,4178,4007,4179 }, + {3601,3578,3550 ,4007,4205,4179 }, {3624,3623,3578 ,4009,4206,4205 }, + {3601,3624,3578 ,4007,4009,4205 }, {3727,1794,5831 ,3942,4129,3943 }, + {3341,915,863 ,541,443,284 }, {3389,3438,3412 ,3913,3912,4141 }, + {931,1629,1518 ,3944,4207,3978 }, {1588,3344,2856 ,3881,1861,3821 }, + {3552,3601,3579 ,4167,4007,4178 }, {4003,4002,3963 ,4186,4188,4208 }, + {4393,5306,5335 ,463,462,2523 }, {3573,3591,3590 ,980,1652,1867 }, + {5572,407,3147 ,2284,4018,2285 }, {3290,3329,3289 ,1204,3972,2056 }, + {3218,4162,5385 ,3976,1738,3865 }, {8103,3283,3425 ,1291,262,1859 }, + {4558,4599,4631 ,4209,4210,1816 }, {1454,1584,4199 ,2318,4012,3869 }, + {5335,5305,4236 ,2523,2283,3488 }, {3314,2368,2365 ,3575,2943,3351 }, + {3343,3314,2365 ,2515,3575,3351 }, {2428,2872,784 ,371,311,4211 }, + {687,2088,1155 ,1665,1667,3333 }, {5697,5698,5725 ,2552,44,3824 }, + {5725,5726,5763 ,3824,515,4212 }, {3628,5045,6235 ,4213,4214,4215 }, + {5726,5764,5763 ,515,537,4212 }, {3248,6076,5982 ,4216,4217,4218 }, + {5045,6165,6235 ,4214,4219,4215 }, {6103,5988,6123 ,4220,2516,4221 }, + {4713,5837,5804 ,4015,3621,3623 }, {1333,3300,2812 ,849,694,1914 }, + {3300,3349,2812 ,694,2054,1914 }, {3336,3335,3309 ,3922,4222,3892 }, + {5471,5435,6640 ,2051,4223,4224 }, {5026,5165,4881 ,4225,4226,2731 }, + {8106,4835,8135 ,2438,2130,2439 }, {3998,3997,3957 ,381,380,295 }, + {3224,3178,931 ,3947,4138,3944 }, {3624,3650,3623 ,4009,4008,4206 }, + {75,348,268 ,3962,643,645 }, {5119,5424,5420 ,4190,4227,4228 }, + {1300,2607,1009 ,285,2452,3752 }, {4866,4913,4865 ,3641,4185,4229 }, + {4942,5119,5420 ,4191,4190,4228 }, {5424,201,84 ,4227,4230,4231 }, + {5420,5424,84 ,4228,4227,4231 }, {201,2643,3759 ,4230,4232,4233 }, + {84,201,3759 ,4231,4230,4233 }, {1160,3096,3241 ,3661,4234,3504 }, + {2316,8059,2275 ,3067,4235,3152 }, {3573,3572,3541 ,980,1931,981 }, + {3678,3709,3652 ,3948,4120,3887 }, {3710,3708,3709 ,4236,3980,4120 }, + {4846,1622,1517 ,4237,4238,600 }, {6324,3365,3335 ,4239,4240,4222 }, + {1851,1733,1102 ,4241,2328,2317 }, {3744,3796,3795 ,2390,2969,3448 }, + {4373,1851,1102 ,3288,4241,2317 }, {5763,5764,5799 ,4212,537,4064 }, + {2955,3750,592 ,3640,690,1863 }, {3850,3849,3806 ,4107,4130,4062 }, + {5877,5474,5489 ,4242,4243,4244 }, {5414,3673,4468 ,3764,648,647 }, + {7436,7464,7412 ,4245,4246,3765 }, {3338,1005,4207 ,4147,3535,4247 }, + {3852,3887,3886 ,4248,4056,4055 }, {3729,3772,3728 ,4249,4250,4251 }, + {3718,684,3348 ,3845,4101,2255 }, {1940,11,684 ,3925,299,4101 }, + {6257,5488,5473 ,3428,2730,3429 }, {7981,7980,7955 ,4252,4253,4254 }, + {6804,6854,6853 ,3342,3344,2505 }, {3756,3755,3708 ,4255,4025,3980 }, + {3656,3627,6534 ,4195,4256,4257 }, {3627,3656,3626 ,4256,4195,4196 }, + {5356,5663,5909 ,4258,4259,4260 }, {4928,3907,917 ,4261,3883,4016 }, + {1334,4236,5480 ,1935,3488,1739 }, {1851,1691,1733 ,4241,2326,2328 }, + {3373,3897,3349 ,693,4262,2054 }, {3751,3806,3729 ,4263,4062,4249 }, + {3806,3805,3729 ,4062,4264,4249 }, {3473,2153,4469 ,649,4181,4060 }, + {2843,3473,4469 ,4058,649,4060 }, {3324,3323,3304 ,881,1051,1050 }, + {976,7335,975 ,782,4265,4266 }, {3881,3920,3919 ,4154,4267,4268 }, + {3920,3963,3962 ,4267,4208,3977 }, {3919,3920,3962 ,4268,4267,3977 }, + {3963,4002,3962 ,4208,4188,3977 }, {2634,21,3759 ,4269,1231,4233 }, + {2643,2634,3759 ,4232,4269,4233 }, {3752,3751,3730 ,4063,4263,4270 }, + {3705,3752,3730 ,4144,4063,4270 }, {3919,3962,3918 ,4268,3977,2335 }, + {7720,7770,7747 ,4271,4272,331 }, {3547,6556,6598 ,803,4273,804 }, + {3372,3453,1908 ,2143,917,1058 }, {5923,6162,5591 ,2073,3598,3597 }, + {3837,1729,4286 ,3667,3666,4274 }, {3883,3885,3922 ,4149,4057,4068 }, + {3332,3331,3292 ,3891,1183,4067 }, {3714,3713,3655 ,3970,3935,3936 }, + {5870,3248,5939 ,4275,4216,4276 }, {3851,3852,3886 ,4108,4248,4055 }, + {3884,3885,3883 ,4131,4057,4149 }, {5283,5337,4830 ,570,572,3642 }, + {3316,3718,3348 ,2149,3845,2255 }, {3718,3841,684 ,3845,3927,4101 }, + {3841,1940,684 ,3927,3925,4101 }, {2634,3672,3987 ,4269,4277,1229 }, + {7930,7981,7955 ,4278,4252,4254 }, {6803,6804,6853 ,4169,3342,2505 }, + {3757,3758,3773 ,2281,1789,4279 }, {4105,3411,5392 ,4053,4280,4022 }, + {3398,3613,3455 ,4281,4173,886 }, {3478,3398,3455 ,885,4281,886 }, + {21,2634,3987 ,1231,4269,1229 }, {3300,3373,3349 ,694,693,2054 }, + {3848,3847,3805 ,4168,4282,4264 }, {3806,3848,3805 ,4062,4168,4264 }, + {7310,7344,7286 ,4283,2749,3460 }, {7698,7697,7648 ,333,651,3952 }, + {5411,5410,5376 ,4284,4285,2758 }, {3729,3728,3674 ,4249,4251,4286 }, + {3847,3846,3805 ,4282,4287,4264 }, {5307,5573,5572 ,461,2217,2284 }, + {6154,6141,3725 ,2387,2386,3971 }, {5306,5307,5572 ,462,461,2284 }, + {3881,3882,3920 ,4154,4288,4267 }, {3672,4423,3987 ,4277,4289,1229 }, + {3808,3806,3807 ,4013,4062,4061 }, {5239,5283,4830 ,482,570,3642 }, + {3252,794,2693 ,781,3392,3416 }, {3146,1909,3613 ,4290,4189,4173 }, + {3398,3146,3613 ,4281,4290,4173 }, {3735,1338,1909 ,4291,1077,4189 }, + {3146,3735,1909 ,4290,4291,4189 }, {3397,1257,1338 ,136,208,1077 }, + {3735,3397,1338 ,4291,136,1077 }, {3308,3332,3292 ,3894,3891,4067 }, + {3809,3852,3833 ,3903,4248,4049 }, {3852,3851,3833 ,4248,4108,4049 }, + {6279,7091,6280 ,3966,4153,4079 }, {5922,3445,5908 ,4292,1791,4293 }, + {3327,3404,1387 ,439,2170,2951 }, {6906,6905,6853 ,4294,2506,2505 }, + {6854,6906,6853 ,3344,4294,2505 }, {5194,4373,1102 ,727,3288,2317 }, + {1412,541,2813 ,3926,3811,3898 }, {3736,3447,2813 ,3807,3928,3898 }, + {3925,3924,3889 ,3227,3847,3126 }, {4423,160,4227 ,4289,4295,1564 }, + {3808,3807,3753 ,4013,4061,4014 }, {3883,3882,3847 ,4149,4288,4282 }, + {132,1518,2837 ,2503,3978,2511 }, {5296,5322,4023 ,4296,4297,3827 }, + {6283,7102,7088 ,4298,1240,4299 }, {3315,5194,1454 ,944,727,2318 }, + {3388,3387,3329 ,4115,1787,3972 }, {3675,3729,3674 ,4300,4249,4286 }, + {3729,3805,3772 ,4249,4264,4250 }, {3315,1454,3828 ,944,2318,1364 }, + {3987,4423,4227 ,1229,4289,1564 }, {7091,6279,7092 ,4153,3966,3968 }, + {160,5236,5237 ,4295,4301,1800 }, {4227,160,5237 ,1564,4295,1800 }, + {5263,5239,4830 ,4302,482,3642 }, {3711,3710,3653 ,1790,4236,1440 }, + {1670,3344,1588 ,2327,1861,3881 }, {541,3057,2813 ,3811,3810,3898 }, + {794,2494,2693 ,3392,3391,3416 }, {3764,1694,1412 ,3844,3828,3926 }, + {3309,3333,3334 ,3892,3893,399 }, {1606,1253,4055 ,3677,2353,3678 }, + {3178,3224,731 ,4138,3947,3965 }, {3967,3966,3923 ,3872,4109,3849 }, + {3344,2473,4221 ,1861,4303,1862 }, {5236,4834,5600 ,4301,4304,1801 }, + {3252,2693,2968 ,781,3416,263 }, {1556,3252,2968 ,3365,781,263 }, + {3864,1334,138 ,3517,1935,434 }, {1694,541,1412 ,3828,3811,3926 }, + {3404,3372,1387 ,2170,2143,2951 }, {882,1694,3396 ,3846,3828,3867 }, + {5237,5236,5600 ,1800,4301,1801 }, {4834,5787,2086 ,4304,4305,1802 }, + {7171,7208,7194 ,4306,1671,1670 }, {3556,3604,3554 ,3385,3384,3914 }, + {3521,3556,3503 ,4307,3385,3835 }, {3504,3521,3503 ,2986,4307,3835 }, + {3556,3555,3503 ,3385,4090,3835 }, {3542,3541,3487 ,979,981,3356 }, + {7495,7494,7462 ,4308,4309,4310 }, {4293,8065,3954 ,2732,96,3463 }, + {3969,3968,3924 ,3226,3848,3847 }, {3889,3924,3888 ,3126,3847,4204 }, + {3329,3328,3289 ,3972,3973,2056 }, {3848,3883,3847 ,4168,4149,4282 }, + {253,1158,966 ,2564,2744,2565 }, {5600,4834,2086 ,1801,4304,1802 }, + {2086,5787,377 ,1802,4305,4311 }, {7240,7287,7239 ,4312,3459,1527 }, + {3846,3881,3832 ,4287,4154,2322 }, {8120,2659,8098 ,906,3485,907 }, + {959,2044,501 ,1358,4031,4313 }, {3719,959,501 ,1356,1358,4313 }, + {6958,6957,6905 ,4314,4315,2506 }, {6276,7094,7093 ,4316,4317,4318 }, + {3882,3881,3846 ,4288,4154,4287 }, {3752,3806,3751 ,4063,4062,4263 }, + {141,4298,5832 ,681,3580,3904 }, {3925,3969,3924 ,3227,3226,3847 }, + {5181,5180,4631 ,4319,1878,1816 }, {2473,2955,4221 ,4303,3640,1862 }, + {3965,4005,3964 ,4069,3906,3964 }, {2422,2912,731 ,3907,3934,3965 }, + {3457,918,530 ,4099,4150,512 }, {75,2938,635 ,3962,4320,2183 }, + {3040,3457,530 ,619,4099,512 }, {3711,3757,3710 ,1790,2281,4236 }, + {6261,4044,4570 ,4321,4322,4082 }, {3057,3736,2813 ,3810,3807,3898 }, + {1694,3764,3396 ,3828,3844,3867 }, {1159,3972,4185 ,3760,3487,409 }, + {3274,3001,3819 ,3172,3210,3763 }, {5289,5288,5266 ,80,3993,81 }, + {3810,3811,3853 ,3901,4323,3902 }, {3889,3888,3853 ,3126,4204,3902 }, + {75,268,107 ,3962,645,2548 }, {6906,6958,6905 ,4294,4314,2506 }, + {1256,1518,132 ,3945,3978,2503 }, {2087,1256,132 ,2465,3945,2503 }, + {3846,3832,3804 ,4287,2322,4324 }, {3897,1804,3349 ,4262,2902,2054 }, + {5180,5242,5220 ,1878,3843,2119 }, {3800,1524,4697 ,4325,4326,4327 }, + {3676,3675,3623 ,4328,4300,4206 }, {6680,5471,6647 ,3834,2051,2050 }, + {3847,3882,3846 ,4282,4288,4287 }, {3650,3676,3623 ,4008,4328,4206 }, + {3964,3963,3920 ,3964,4208,4267 }, {3651,3706,3650 ,3888,4143,4008 }, + {3748,5385,5380 ,4034,3865,653 }, {5022,4669,1196 ,4329,3879,4330 }, + {3854,3889,3853 ,1512,3126,3902 }, {6116,7095,7094 ,4170,3924,4317 }, + {1159,4185,3673 ,3760,409,648 }, {7225,7246,7267 ,1672,3772,4331 }, + {7752,7751,7701 ,3514,4332,3515 }, {4797,5259,5258 ,3984,3794,1193 }, + {834,2856,1588 ,3876,3821,3881 }, {3909,834,3257 ,4164,3876,4010 }, + {5855,2856,1974 ,4011,3821,3862 }, {7312,7311,7262 ,4333,4334,4335 }, + {7797,7823,7796 ,3506,345,3803 }, {7825,7824,7771 ,3768,3770,4336 }, + {5286,5339,5285 ,3859,2223,2169 }, {107,268,751 ,2548,645,1365 }, + {7096,5473,3518 ,4337,3429,4338 }, {4369,4393,5335 ,104,463,2523 }, + {1339,3398,3478 ,4339,4281,885 }, {3449,1339,3478 ,2440,4339,885 }, + {3811,3810,3756 ,4323,3901,4255 }, {3757,3811,3756 ,2281,4323,4255 }, + {3757,3773,3811 ,2281,4279,4323 }, {3812,3854,3811 ,1513,1512,4323 }, + {407,3477,3147 ,4018,1736,2285 }, {1339,3564,3146 ,4339,4340,4290 }, + {3398,1339,3146 ,4281,4339,4290 }, {1334,5480,3218 ,1935,1739,3976 }, + {1670,1691,3344 ,2327,2326,1861 }, {1691,2473,3344 ,2326,4303,1861 }, + {3710,3709,3678 ,4236,4120,3948 }, {3653,3710,3678 ,1440,4236,3948 }, + {3962,4002,3961 ,3977,4188,2346 }, {3602,3601,3553 ,3889,4007,3957 }, + {4002,4001,3961 ,4188,2380,2346 }, {731,1003,4003 ,3965,4187,4186 }, + {3805,3846,3804 ,4264,4287,4324 }, {2219,5231,5000 ,177,115,286 }, + {8064,3975,3008 ,3141,728,546 }, {4078,1004,1919 ,4341,3452,1779 }, + {3368,3577,3369 ,3960,4059,3836 }, {3542,3591,3573 ,979,1652,980 }, + {7424,7423,7371 ,1188,3749,3748 }, {3038,1783,1341 ,4342,2251,3841 }, + {5288,4868,5287 ,3993,3969,4003 }, {4487,694,1607 ,4006,1987,3941 }, + {3969,4010,3968 ,3226,4343,3848 }, {3564,3782,3735 ,4340,4344,4291 }, + {3146,3564,3735 ,4290,4340,4291 }, {3370,3397,3735 ,2303,136,4291 }, + {3782,3370,3735 ,4344,2303,4291 }, {1454,694,1584 ,2318,1987,4012 }, + {4710,5218,4770 ,4136,483,4345 }, {1443,3274,1152 ,182,3172,4029 }, + {4236,4527,5480 ,3488,1740,1739 }, {4527,3147,4162 ,1740,2285,1738 }, + {4006,4005,3965 ,4110,3906,4069 }, {5572,5573,407 ,2284,2217,4018 }, + {1256,2087,3004 ,3945,2465,2437 }, {1309,3935,685 ,3709,4346,2674 }, + {1483,1539,87 ,4084,835,641 }, {3871,3748,5850 ,397,4034,394 }, + {3329,3387,3362 ,3972,1787,1200 }, {3566,2557,3052 ,1146,3620,3715 }, + {4653,4654,4679 ,4347,4348,3852 }, {6116,6276,6286 ,4170,4316,4349 }, + {6853,6892,5626 ,2505,2507,1460 }, {147,4712,4027 ,3469,3816,3470 }, + {3243,1099,1111 ,3919,4350,3920 }, {3187,2698,4236 ,3402,105,3488 }, + {5218,5238,4770 ,483,4351,4345 }, {3748,3749,5850 ,4034,652,394 }, + {2713,4091,3259 ,4352,123,407 }, {3304,3303,7333 ,1050,1049,4353 }, + {7948,7947,7896 ,3625,4354,4355 }, {3446,3472,5608 ,3718,3155,4356 }, + {6276,6116,7094 ,4316,4170,4317 }, {3309,7110,7109 ,3892,1990,4112 }, + {3707,3706,3651 ,3981,4143,3888 }, {3553,3552,3519 ,3957,4167,4357 }, + {3520,3553,3519 ,4358,3957,4357 }, {4004,731,4003 ,3908,3965,4186 }, + {3773,3758,3812 ,4279,1789,1513 }, {1003,2910,393 ,4187,3946,2400 }, + {3881,3880,3832 ,4154,2320,2322 }, {3271,3396,3316 ,1727,3867,2149 }, + {3289,3328,3307 ,2056,3973,2057 }, {4022,8111,5353 ,3503,4359,851 }, + {1630,924,56 ,1016,3634,2606 }, {3451,3537,3516 ,1720,1735,1721 }, + {4600,4653,4599 ,4360,4347,4210 }, {3566,3052,2397 ,1146,3715,3669 }, + {5385,4157,5304 ,3865,3755,654 }, {3883,3921,3920 ,4149,4070,4267 }, + {3753,3752,3706 ,4014,4063,4143 }, {7983,8095,8105 ,4361,4362,3791 }, + {4002,1003,4001 ,4188,4187,2380 }, {2544,2622,601 ,2451,2840,3905 }, + {149,454,2471 ,3736,4363,3653 }, {3456,2920,2557 ,1145,1799,3620 }, + {4165,1483,87 ,640,4084,641 }, {3502,3553,3520 ,3958,3957,4358 }, + {3331,3390,3364 ,1183,3509,1184 }, {2043,143,918 ,4100,157,4150 }, + {4056,2421,4161 ,1123,2568,4364 }, {3457,2043,918 ,4099,4100,4150 }, + {5833,141,5832 ,544,681,3904 }, {3757,3756,3710 ,2281,4255,4236 }, + {6186,3518,5488 ,4365,4338,2730 }, {7203,7240,7220 ,4366,4312,4367 }, + {3130,3086,3087 ,3414,3413,3573 }, {6997,6996,6957 ,4368,4369,4315 }, + {507,5309,505 ,1523,987,986 }, {3812,3758,3813 ,1513,1789,1515 }, + {115,3081,3241 ,2901,1017,3504 }, {3096,115,3241 ,4234,2901,3504 }, + {1657,1552,3156 ,1781,2266,3164 }, {923,924,1630 ,4370,3634,1016 }, + {1663,923,1630 ,1015,4370,1016 }, {923,3640,924 ,4370,4371,3634 }, + {149,219,924 ,3736,3087,3634 }, {3640,149,924 ,4371,3736,3634 }, + {1383,1296,788 ,336,335,976 }, {53,2346,2573 ,4372,3252,3253 }, + {3882,3883,3920 ,4288,4149,4267 }, {6958,6997,6957 ,4314,4368,4315 }, + {7397,7371,7372 ,1186,3748,4373 }, {3513,3404,3327 ,441,2170,439 }, + {268,1664,751 ,645,2268,1365 }, {3811,3854,3853 ,4323,1512,3902 }, + {2651,2397,2381 ,1516,3669,3672 }, {6997,5752,6996 ,4368,4374,4369 }, + {3112,3993,3132 ,2795,2807,2796 }, {6285,7109,7095 ,3923,4112,3924 }, + {3170,1160,3211 ,3510,3661,3605 }, {869,2681,2346 ,4375,3254,3252 }, + {53,869,2346 ,4372,4375,3252 }, {3450,2464,2681 ,3330,3299,3254 }, + {869,3450,2681 ,4375,3330,3254 }, {7039,7038,5752 ,4376,3832,4374 }, + {7109,6285,3309 ,4112,3923,3892 }, {3651,3650,3624 ,3888,4008,4009 }, + {3749,3748,5380 ,652,4034,653 }, {3373,1804,3897 ,693,2902,4262 }, + {6276,6287,6286 ,4316,4377,4349 }, {4770,5238,5263 ,4345,4351,4302 }, + {3009,5701,5683 ,3747,3938,3939 }, {2624,3405,1153 ,3132,1798,2245 }, + {1794,3616,2795 ,4129,3974,3975 }, {3377,3378,3427 ,1443,1442,4378 }, + {6509,8092,8074 ,1452,2267,2460 }, {4032,3170,3129 ,3638,3510,3412 }, + {2829,1107,1106 ,2620,2656,1664 }, {6997,7039,5752 ,4368,4376,4374 }, + {1066,2558,105 ,680,977,249 }, {2850,2833,2803 ,3657,2065,3655 }, + {2910,3004,393 ,3946,2437,2400 }, {5238,5239,5263 ,4351,482,4302 }, + {5198,5218,4710 ,423,483,4136 }, {3486,3487,3540 ,4379,3356,3357 }, + {6390,2256,5950 ,3868,2323,2873 }, {538,53,3569 ,4380,4372,4381 }, + {3468,3467,3439 ,2688,3992,2689 }, {1387,1908,3636 ,2951,1058,1060 }, + {1378,1339,3449 ,925,4339,2440 }, {3334,3333,3309 ,399,3893,3892 }, + {3502,3520,3501 ,3958,4358,4176 }, {3921,3964,3920 ,4070,3964,4267 }, + {3706,3705,3650 ,4143,4144,4008 }, {3501,3519,3500 ,4176,4357,4177 }, + {3519,3552,3500 ,4357,4167,4177 }, {3552,3551,3500 ,4167,4180,4177 }, + {3602,3625,3601 ,3889,4382,4007 }, {3625,3651,3601 ,4382,3888,4007 }, + {1693,2693,1249 ,264,3416,2876 }, {1524,1907,3818 ,4326,1245,1244 }, + {1109,1160,4032 ,4383,3661,3638 }, {1109,3096,1160 ,4383,4234,3661 }, + {149,3640,923 ,3736,4371,4370 }, {2218,409,454 ,4384,1480,4363 }, + {1303,687,831 ,1933,1665,3334 }, {3428,3459,3458 ,3261,3241,4385 }, + {3378,3428,3427 ,1442,3261,4378 }, {88,3637,1453 ,3581,4386,3355 }, + {3636,88,1453 ,1060,3581,3355 }, {3378,3428,3406 ,1442,3261,399 }, + {6277,6287,6276 ,4387,4377,4316 }, {3266,4775,495 ,3884,4388,4389 }, + {3913,2698,4248 ,841,105,3256 }, {6133,3518,7084 ,4390,4338,4391 }, + {1487,3194,2150 ,531,1065,534 }, {3571,3572,3589 ,3652,1931,1932 }, + {3531,53,538 ,4392,4372,4380 }, {5936,6143,6125 ,4393,4394,4395 }, + {382,3732,3318 ,3534,3536,4036 }, {4630,4710,4709 ,827,4136,4396 }, + {5815,5816,5832 ,4065,545,3904 }, {3439,3467,3438 ,2689,3992,3912 }, + {3502,3501,3466 ,3958,4176,4175 }, {3650,3705,3676 ,4008,4144,4328 }, + {3705,3730,3676 ,4144,4270,4328 }, {3730,3675,3676 ,4270,4300,4328 }, + {3730,3751,3675 ,4270,4263,4300 }, {3751,3729,3675 ,4263,4249,4300 }, + {4004,4003,3963 ,3908,4186,4208 }, {3964,4004,3963 ,3964,3908,4208 }, + {3651,3624,3601 ,3888,4009,4007 }, {3602,3651,3625 ,3889,3888,4382 }, + {6186,7084,3518 ,4365,4391,4338 }, {1334,3218,3748 ,1935,3976,4034 }, + {1003,393,4001 ,4187,2400,2380 }, {3321,3322,3354 ,2461,1182,1293 }, + {3853,3888,3852 ,3902,4204,4248 }, {3756,3810,3755 ,4255,3901,4025 }, + {3772,5882,3728 ,4250,4397,4251 }, {3131,4033,4032 ,3732,4398,3638 }, + {3130,3131,4032 ,3414,3732,3638 }, {225,1109,4032 ,4399,4383,3638 }, + {4033,225,4032 ,4398,4399,3638 }, {3901,3096,1109 ,4400,4234,4383 }, + {225,3901,1109 ,4399,4400,4383 }, {3901,3949,3096 ,4400,4401,4234 }, + {2010,115,3096 ,4402,2901,4234 }, {3949,2010,3096 ,4401,4402,4234 }, + {2415,1663,115 ,4403,1015,2901 }, {2010,2415,115 ,4402,4403,2901 }, + {1301,923,1663 ,4404,4370,1015 }, {2415,1301,1663 ,4403,4404,1015 }, + {182,149,923 ,4405,3736,4370 }, {1301,182,923 ,4404,4405,4370 }, + {182,230,149 ,4405,4406,3736 }, {12,454,149 ,4407,4363,3736 }, + {230,12,149 ,4406,4407,3736 }, {130,2218,454 ,2341,4384,4363 }, + {12,130,454 ,4407,2341,4363 }, {150,409,2218 ,1481,1480,4384 }, + {6509,8074,5013 ,1452,2460,684 }, {2218,130,150 ,4384,2341,1481 }, + {3427,3428,3458 ,4378,3261,4385 }, {3459,3487,3486 ,3241,3356,4379 }, + {3958,3998,3957 ,294,381,295 }, {1946,131,4384 ,4408,2965,4409 }, + {2522,549,2451 ,2678,1129,2102 }, {495,3788,1341 ,4389,4410,3841 }, + {7777,7776,7730 ,4411,4412,4413 }, {1378,3687,3564 ,925,924,4340 }, + {1339,1378,3564 ,4339,925,4340 }, {3382,3407,3381 ,870,3012,871 }, + {3050,538,3532 ,4414,4380,4415 }, {3479,3531,538 ,4416,4392,4380 }, + {3050,3479,538 ,4414,4416,4380 }, {3479,53,3531 ,4416,4372,4392 }, + {295,869,53 ,4417,4375,4372 }, {3479,295,53 ,4416,4417,4372 }, + {295,2645,3450 ,4417,4418,3330 }, {869,295,3450 ,4375,4417,3330 }, + {3535,840,3450 ,442,68,3330 }, {2645,3535,3450 ,4418,442,3330 }, + {3687,3873,3782 ,924,1809,4344 }, {3488,3542,3487 ,1980,979,3356 }, + {3888,3887,3852 ,4204,4056,4248 }, {3853,3852,3809 ,3902,4248,3903 }, + {3707,3754,3706 ,3981,4071,4143 }, {3754,3753,3706 ,4071,4014,4143 }, + {3520,3519,3501 ,4358,4357,4176 }, {3467,3502,3466 ,3992,3958,4175 }, + {731,3224,1003 ,3965,3947,4187 }, {3224,2910,1003 ,3947,3946,4187 }, + {7691,450,449 ,4419,1653,1169 }, {3390,3439,3389 ,3509,2689,3913 }, + {3467,3466,3438 ,3992,4175,3912 }, {3738,1940,3841 ,3900,3925,3927 }, + {3710,3756,3708 ,4236,4255,3980 }, {3564,3687,3782 ,4340,924,4344 }, + {3873,3475,3370 ,1809,1559,2303 }, {3782,3873,3370 ,4344,1809,2303 }, + {4162,4129,5805 ,1738,3812,3999 }, {7075,7074,7038 ,4420,2747,3832 }, + {498,2010,3949 ,4421,4402,4401 }, {3901,498,3949 ,4400,4421,4401 }, + {2088,55,1155 ,1667,3507,3333 }, {3458,3459,3486 ,4385,3241,4379 }, + {1096,1975,3637 ,2727,4422,4386 }, {88,1096,3637 ,3581,2727,4386 }, + {7039,7075,7038 ,4376,4420,3832 }, {4600,4653,4679 ,4360,4347,3852 }, + {7451,7479,7423 ,3963,4423,3749 }, {630,879,3370 ,2491,1155,2303 }, + {3475,630,3370 ,1559,2491,2303 }, {543,495,1341 ,3840,4389,3841 }, + {7983,4744,5026 ,4361,4424,4225 }, {3617,3532,3375 ,4425,4415,4426 }, + {1208,1586,689 ,3211,943,1363 }, {2527,2486,2487 ,2637,2814,4427 }, + {2831,2832,2868 ,639,638,1577 }, {5845,6174,5113 ,143,2381,141 }, + {3604,3653,3603 ,3384,1440,3890 }, {3810,3809,3755 ,3901,3903,4025 }, + {1975,1096,784 ,4422,2727,4211 }, {498,2415,2010 ,4421,4403,4402 }, + {3696,3742,3741 ,2325,3419,3447 }, {3695,3696,3741 ,4428,2325,3447 }, + {300,183,499 ,3508,4429,4430 }, {3091,1390,300 ,3262,2964,3508 }, + {4617,4924,7074 ,4431,2745,2747 }, {7075,4617,7074 ,4420,4431,2747 }, + {5337,4866,5354 ,572,3641,3882 }, {1922,3244,4397 ,4432,4433,4434 }, + {2407,2258,6484 ,4001,1198,685 }, {3924,3923,3887 ,3847,3849,4056 }, + {6316,2162,6390 ,4435,3278,3868 }, {1299,295,3479 ,4436,4417,4416 }, + {295,3041,2645 ,4417,3182,4418 }, {3902,5970,4104 ,4437,3518,3519 }, + {2314,2223,2256 ,3742,2336,2323 }, {3426,3485,2615 ,4438,3524,2788 }, + {2566,3426,2615 ,2635,4438,2788 }, {2615,3485,2646 ,2788,3524,3070 }, + {2646,3485,2686 ,3070,3524,3071 }, {3539,3588,2740 ,3525,4439,3456 }, + {2686,3539,2740 ,3071,3525,3456 }, {2740,3588,3619 ,3456,4439,3956 }, + {3588,3641,2790 ,4439,4440,3576 }, {3619,3588,2790 ,3956,4439,3576 }, + {3641,3694,2832 ,4440,3915,638 }, {2790,3641,2832 ,3576,4440,638 }, + {3393,3392,3333 ,4441,3860,3893 }, {6467,6484,2223 ,4000,685,2336 }, + {3365,3333,3309 ,4240,3893,3892 }, {3132,4034,4033 ,2796,4442,4398 }, + {3131,3132,4033 ,3732,2796,4398 }, {4034,97,225 ,4442,4443,4399 }, + {4033,4034,225 ,4398,4442,4399 }, {1110,3901,225 ,4444,4400,4399 }, + {97,1110,225 ,4443,4444,4399 }, {585,498,3901 ,4445,4421,4400 }, + {1110,585,3901 ,4444,4445,4400 }, {919,2415,498 ,4446,4403,4421 }, + {585,919,498 ,4445,4446,4421 }, {54,1301,2415 ,4447,4404,4403 }, + {919,54,2415 ,4446,4447,4403 }, {224,182,1301 ,4448,4405,4404 }, + {54,224,1301 ,4447,4448,4404 }, {3056,230,182 ,4449,4406,4405 }, + {224,3056,182 ,4448,4449,4405 }, {400,12,230 ,4450,4407,4406 }, + {3056,400,230 ,4449,4450,4406 }, {95,130,12 ,3482,2341,4407 }, + {400,95,12 ,4450,3482,4407 }, {1798,89,130 ,4451,1482,2341 }, + {1155,300,499 ,3333,3508,4430 }, {1096,2749,784 ,2727,3286,4211 }, + {784,2749,2428 ,4211,3286,371 }, {1155,499,957 ,3333,4430,4452 }, + {1626,831,3253 ,564,3334,565 }, {3184,2618,3895 ,4453,4454,4455 }, + {831,1155,957 ,3334,3333,4452 }, {1004,4078,31 ,3452,4341,3450 }, + {8108,8066,8128 ,2287,960,959 }, {3770,3050,3617 ,4456,4414,4425 }, + {3346,3770,3617 ,4457,4456,4425 }, {3424,3479,3050 ,4458,4416,4414 }, + {3770,3424,3050 ,4456,4458,4414 }, {3635,1299,3479 ,4459,4436,4416 }, + {3424,3635,3479 ,4458,4459,4416 }, {3635,295,1299 ,4459,4417,4436 }, + {3635,1860,3041 ,4459,3183,3182 }, {295,3635,3041 ,4417,4459,3182 }, + {3618,1860,4204 ,37,3183,38 }, {832,3094,1059 ,613,634,678 }, + {3955,3995,3994 ,3538,3540,2806 }, {4617,1166,4924 ,4431,2746,2745 }, + {3365,3393,3333 ,4240,4441,3893 }, {2471,454,2536 ,3653,4363,2793 }, + {55,652,1383 ,3507,334,336 }, {1296,2558,788 ,335,977,976 }, + {3442,3441,3413 ,4460,2987,3814 }, {3393,3413,3392 ,4441,3814,3860 }, + {3342,2364,177 ,2514,2513,3618 }, {1558,919,585 ,4461,4446,4445 }, + {3643,3696,3695 ,2344,2325,4428 }, {3642,3643,3695 ,4462,2344,4428 }, + {3378,3406,3428 ,1442,399,3261 }, {6260,6053,5320 ,3397,4463,4464 }, + {7241,7240,7203 ,4465,4312,4366 }, {6996,5752,6957 ,4369,4374,4315 }, + {3335,3365,3309 ,4222,4240,3892 }, {2536,454,409 ,2793,4363,1480 }, + {3346,3375,4098 ,4457,4426,353 }, {1303,831,1626 ,1933,3334,564 }, + {2743,3944,3931 ,923,729,1868 }, {53,2573,3569 ,4372,3253,4381 }, + {3895,2618,3376 ,4455,4454,4466 }, {3539,3570,3588 ,3525,4467,4439 }, + {730,960,1985 ,1549,1326,1547 }, {1378,3449,8135 ,925,2440,2439 }, + {3420,3299,3474 ,3785,3232,3808 }, {58,2013,1332 ,2156,2242,3492 }, + {224,400,3056 ,4448,4450,4449 }, {8,4522,220 ,3341,3481,3340 }, + {1390,151,183 ,2964,2966,4429 }, {300,1390,183 ,3508,2964,4429 }, + {8131,7714,7983 ,2307,4468,4361 }, {1383,3091,300 ,336,3262,3508 }, + {55,1383,300 ,3507,336,3508 }, {3390,3389,3363 ,3509,3913,1911 }, + {3119,2681,2747 ,3223,3254,3224 }, {3805,3804,3772 ,4264,4324,4250 }, + {831,957,3253 ,3334,4452,565 }, {3299,637,3474 ,3232,3805,3808 }, + {366,1202,1204 ,4469,339,338 }, {3826,3770,3346 ,4470,4456,4457 }, + {784,4204,1860 ,4211,38,3183 }, {912,309,1166 ,4471,4472,2746 }, + {2162,2314,2256 ,3278,3742,2323 }, {3211,1160,3241 ,3605,3661,3504 }, + {4617,912,1166 ,4431,4471,2746 }, {79,3421,3278 ,1510,1156,1158 }, + {1537,4191,3802 ,4473,4474,4475 }, {3377,3427,3426 ,1443,4378,4438 }, + {2487,3377,3426 ,4427,1443,4438 }, {3427,3486,3485 ,4378,4379,3524 }, + {3426,3427,3485 ,4438,4378,3524 }, {3486,3540,3539 ,4379,3357,3525 }, + {3485,3486,3539 ,3524,4379,3525 }, {3540,3571,3570 ,3357,3652,4467 }, + {3539,3540,3570 ,3525,3357,4467 }, {3571,3589,3588 ,3652,1932,4439 }, + {3570,3571,3588 ,4467,3652,4439 }, {3589,3642,3641 ,1932,4462,4440 }, + {3588,3589,3641 ,4439,1932,4440 }, {3642,3695,3694 ,4462,4428,3915 }, + {3641,3642,3694 ,4440,4462,3915 }, {3695,3741,2907 ,4428,3447,111 }, + {3694,3695,2907 ,3915,4428,111 }, {3792,3791,2907 ,3421,3739,111 }, + {3741,3792,2907 ,3447,3421,111 }, {3881,3919,3918 ,4154,4268,2335 }, + {3420,3474,3685 ,3785,3808,3809 }, {3312,3420,3685 ,3782,3785,3809 }, + {4035,4034,3132 ,1601,4442,2796 }, {3993,4035,3132 ,2807,1601,2796 }, + {4035,2151,97 ,1601,1603,4443 }, {4034,4035,97 ,4442,1601,4443 }, + {2151,349,1110 ,1603,566,4444 }, {97,2151,1110 ,4443,1603,4444 }, + {3253,585,1110 ,565,4445,4444 }, {349,3253,1110 ,566,565,4444 }, + {957,1558,585 ,4452,4461,4445 }, {3253,957,585 ,565,4452,4445 }, + {2071,919,1558 ,4476,4446,4461 }, {957,2071,1558 ,4452,4476,4461 }, + {499,54,919 ,4430,4447,4446 }, {2071,499,919 ,4476,4430,4446 }, + {183,224,54 ,4429,4448,4447 }, {499,183,54 ,4430,4429,4447 }, + {151,400,224 ,2966,4450,4448 }, {183,151,224 ,4429,2966,4448 }, + {220,95,400 ,3340,3482,4450 }, {151,220,400 ,2966,3340,4450 }, + {3590,3643,3642 ,1867,2344,4462 }, {3589,3590,3642 ,1932,1867,4462 }, + {4037,4036,3994 ,3541,1602,2806 }, {958,3312,3685 ,3779,3782,3809 }, + {3393,3442,3413 ,4441,4460,3814 }, {1068,958,3685 ,3761,3779,3809 }, + {939,3346,4350 ,3354,4457,740 }, {1453,3826,3346 ,3355,4470,4457 }, + {939,1453,3346 ,3354,3355,4457 }, {3992,3770,3826 ,4477,4456,4470 }, + {1453,3992,3826 ,3355,4477,4470 }, {3637,3424,3770 ,4386,4458,4456 }, + {3992,3637,3770 ,4477,4386,4456 }, {1975,3635,3424 ,4422,4459,4458 }, + {3637,1975,3424 ,4386,4422,4458 }, {1975,784,1860 ,4422,4211,3183 }, + {3635,1975,1860 ,4459,4422,3183 }, {2829,1106,1062 ,2620,1664,1232 }, + {4031,2585,2474 ,3069,875,874 }, {8007,2287,2250 ,3854,3735,3716 }, + {8077,8113,3421 ,2078,1157,1156 }, {8096,5057,6796 ,1796,1795,2617 }, + {957,499,2071 ,4452,4430,4476 }, {131,220,151 ,2965,3340,2966 }, + {912,2240,2375 ,4471,4478,4479 }, {254,2472,2039 ,2008,2009,1655 }, + {3995,4037,3994 ,3540,3541,2806 }, {3278,1724,2851 ,1158,3880,3051 }, + {1668,1208,689 ,3212,3211,1363 }, {872,958,1068 ,2962,3779,3761 }, + {836,872,1068 ,3551,2962,3761 }, {1453,3637,3992 ,3355,4386,4477 }, + {3211,3169,3170 ,3605,2810,3510 }, {309,912,2375 ,4472,4471,4479 }, + {3427,3458,3486 ,4378,4385,4379 }, {1564,1922,1085 ,368,4432,369 }, + {1085,1859,703 ,369,4480,100 }, {1859,1625,703 ,4480,388,100 }, + {1859,4222,1625 ,4480,530,388 }, {4181,23,4100 ,2273,2272,4481 }, + {1933,4181,1695 ,470,2273,471 }, {4131,1087,2026 ,1656,4482,130 }, + {2026,1087,870 ,130,4482,2274 }, {1063,1092,1366 ,4483,4484,4485 }, + {1092,1424,1366 ,4484,2298,4485 }, {5728,5766,5727 ,4486,4487,514 }, + {4181,4100,1695 ,2273,4481,471 }, {245,1458,1148 ,4488,4489,160 }, + {1148,1458,1925 ,160,4489,161 }, {4061,4064,4060 ,4490,1880,2099 }, + {5766,5765,5727 ,4487,540,514 }, {4107,1932,770 ,4491,2141,275 }, + {1396,4107,770 ,34,4491,275 }, {4262,2,702 ,4492,4493,2699 }, + {4090,4148,4108 ,1487,2305,2304 }, {416,2091,604 ,556,4494,486 }, + {4090,4108,4089 ,1487,2304,4495 }, {2,265,443 ,4493,4496,920 }, + {1932,1792,2352 ,2141,324,390 }, {4090,4089,4070 ,1487,4495,4497 }, + {1600,311,209 ,4498,246,4499 }, {4257,4276,4275 ,698,1446,699 }, + {461,2,443 ,399,4493,920 }, {1792,955,1375 ,324,3991,325 }, {4109,4126,4175 ,576,124,4500 }, + {5700,5728,5727 ,4501,4486,514 }, {4061,4060,3347 ,4490,2099,2098 }, + {494,787,175 ,3041,209,211 }, {1872,1836,1808 ,2459,2490,4502 }, + {443,697,59 ,920,3578,967 }, {493,494,175 ,1785,3041,211 }, {8119,5261,8116 ,184,173,172 }, + {4175,3784,4218 ,4500,2252,4503 }, {5699,5700,5727 ,43,4501,514 }, + {6311,6390,8114 ,1097,3868,2286 }, {121,4155,1730 ,4504,4505,4506 }, + {665,761,760 ,1754,1826,3106 }, {1310,229,737 ,149,221,150 }, + {3867,1101,1851 ,4507,2,4241 }, {251,491,1517 ,603,487,600 }, + {3794,3795,3796 ,3449,3448,2969 }, {2423,1225,2428 ,433,327,371 }, + {2427,2254,3117 ,3237,3705,3407 }, {4357,4405,2941 ,4508,4509,109 }, + {3792,4357,2941 ,3421,4508,109 }, {4405,2940,2941 ,4509,110,109 }, + {4499,1205,2940 ,4510,3187,110 }, {5282,4827,5334 ,1194,2734,2736 }, + {2310,2504,4146 ,2442,2898,3730 }, {3794,3796,4259 ,3449,2969,4511 }, + {3794,4305,3793 ,3449,4512,3420 }, {3793,4357,3792 ,3420,4508,3421 }, + {4474,2940,4405 ,4513,110,4509 }, {4474,4499,2940 ,4513,4510,110 }, + {4474,1205,4499 ,4513,3187,4510 }, {596,548,549 ,1462,938,1129 }, + {23,1441,1063 ,2272,1485,4483 }, {4070,4069,4064 ,4497,1881,1880 }, + {4146,3844,2310 ,3730,2443,2442 }, {3585,4493,3447 ,3806,4514,3928 }, + {4533,64,3447 ,4515,297,3928 }, {4493,4533,3447 ,4514,4515,3928 }, + {4533,207,64 ,4515,4516,297 }, {4472,4491,1653 ,2189,107,106 }, + {237,236,99 ,869,2444,3743 }, {3794,4259,4305 ,3449,4511,4512 }, + {4434,4405,4357 ,4517,4509,4508 }, {4434,4474,4405 ,4517,4513,4509 }, + {4499,1205,4474 ,4510,3187,4513 }, {549,548,450 ,1129,938,1653 }, + {1425,121,1730 ,4518,4504,4506 }, {477,478,64 ,4519,2077,297 }, + {207,477,64 ,4516,4519,297 }, {1212,1178,4075 ,4520,206,4521 }, + {8109,5521,8110 ,2938,751,1140 }, {4357,4381,4434 ,4508,4522,4517 }, + {4474,4500,4499 ,4513,4523,4510 }, {1529,277,478 ,4524,85,2077 }, + {477,1529,478 ,4519,4524,2077 }, {1529,276,277 ,4524,4525,85 }, + {5641,5657,5640 ,510,48,2214 }, {4381,4357,4305 ,4522,4508,4512 }, + {4332,4381,4305 ,4526,4522,4512 }, {4434,4453,4474 ,4517,4527,4513 }, + {4453,4500,4474 ,4527,4523,4513 }, {4371,3864,3972 ,3489,3517,3487 }, + {276,463,277 ,4525,1941,85 }, {38,74,110 ,742,2053,743 }, {5680,5679,5640 ,50,2553,2214 }, + {8037,8050,4589 ,4528,4529,776 }, {3950,5352,3799 ,4530,3502,4531 }, + {4277,4259,4240 ,4532,4511,2060 }, {4332,4305,4259 ,4526,4512,4511 }, + {4277,4332,4259 ,4532,4526,4511 }, {4406,4381,4434 ,399,4522,4517 }, + {4381,4406,4434 ,4522,399,4517 }, {4381,4453,4434 ,4522,4527,4517 }, + {4500,366,1204 ,4523,4469,338 }, {4099,4327,561 ,4533,2377,2379 }, + {122,1467,755 ,4534,4535,1615 }, {4113,1178,1212 ,207,206,4520 }, + {504,4330,513 ,4536,399,377 }, {467,504,513 ,346,4536,377 }, + {4161,2421,2695 ,4364,2568,3453 }, {4259,3796,4240 ,4511,2969,2060 }, + {5641,5640,5599 ,510,2214,2213 }, {4260,4277,4240 ,3527,4532,2060 }, + {4358,4381,4332 ,4537,4522,4526 }, {4407,4406,4381 ,4538,399,4522 }, + {4358,4407,4381 ,4537,4538,4522 }, {4407,4435,4381 ,4538,4539,4522 }, + {4406,4407,4381 ,399,4538,4522 }, {4381,4435,4453 ,4522,4539,4527 }, + {4453,4501,4500 ,4527,4540,4523 }, {4501,4538,366 ,4540,4541,4469 }, + {4500,4501,366 ,4523,4540,4469 }, {342,343,1993 ,4542,4543,4544 }, + {4471,4472,1653 ,2187,2189,106 }, {513,1154,1343 ,377,4545,378 }, + {1343,1154,4399 ,378,4545,4546 }, {8111,4022,6836 ,4359,3503,852 }, + {2697,3977,3327 ,2006,440,439 }, {6892,6905,5626 ,2507,2506,1460 }, + {4359,4358,4332 ,4547,4537,4526 }, {4277,4359,4332 ,4532,4547,4526 }, + {4359,4407,4358 ,4547,4538,4537 }, {4453,4475,4501 ,4527,4548,4540 }, + {4538,1040,366 ,4541,1476,4469 }, {2240,3978,3945 ,4478,4549,4550 }, + {2313,321,1222 ,438,60,62 }, {378,1467,122 ,4551,4535,4534 }, + {4397,1859,1085 ,4434,4480,369 }, {8095,8090,8105 ,4362,412,3791 }, + {8131,7983,8105 ,2307,4361,3791 }, {2955,3761,3750 ,3640,218,690 }, + {5569,5570,5599 ,2075,508,2213 }, {4306,4359,4277 ,4552,4547,4532 }, + {4407,4408,4435 ,4538,4553,4539 }, {4435,4408,4453 ,4539,4553,4527 }, + {4453,4408,4475 ,4527,4553,4548 }, {4475,1904,4501 ,4548,2164,4540 }, + {1904,1906,4538 ,2164,1509,4541 }, {4501,1904,4538 ,4540,2164,4541 }, + {1906,1040,4538 ,1509,1476,4541 }, {1224,1201,1040 ,1508,1477,1476 }, + {887,4131,4222 ,4554,1656,530 }, {4156,887,4222 ,4555,4554,530 }, + {887,1087,4131 ,4554,4482,1656 }, {8113,8077,1724 ,1157,2078,3880 }, + {4360,4359,4306 ,4556,4547,4552 }, {4360,4382,4359 ,4556,4557,4547 }, + {4408,4407,4359 ,4553,4538,4547 }, {4382,4408,4359 ,4557,4553,4547 }, + {4476,1904,4475 ,4558,2164,4548 }, {1904,1224,1906 ,2164,1508,1509 }, + {649,492,175 ,1579,1580,211 }, {365,4521,606 ,402,179,180 }, + {1424,1528,1807 ,2298,4559,4560 }, {1807,1528,608 ,4560,4559,2300 }, + {1366,1424,1807 ,4485,2298,4560 }, {1087,1531,870 ,4482,4561,2274 }, + {1531,4107,1396 ,4561,4491,34 }, {3761,1463,3750 ,218,220,690 }, + {4307,4360,4306 ,4562,4556,4552 }, {4307,4382,4360 ,4562,4557,4556 }, + {4476,4475,4408 ,4558,4548,4553 }, {3977,2697,66 ,440,2006,4563 }, + {1222,4521,365 ,62,179,402 }, {4515,1651,1609 ,30,188,31 }, {260,4311,4312 ,1573,468,467 }, + {870,1531,1396 ,2274,4561,34 }, {1284,23,1063 ,4564,2272,4483 }, + {1509,377,1792 ,4565,4311,324 }, {1932,1509,1792 ,2141,4565,324 }, + {5223,5222,5182 ,3851,4566,4567 }, {1268,5301,2238 ,4568,2698,2697 }, + {1691,1101,2473 ,2326,2,4303 }, {6735,6742,3192 ,3472,3422,3063 }, + {489,2473,1101 ,3639,4303,2 }, {1868,1783,2217 ,2249,2251,4569 }, + {1783,3038,2217 ,2251,4342,4569 }, {4409,4436,4408 ,4570,4571,4553 }, + {4382,4409,4408 ,4557,4570,4553 }, {4436,4437,4408 ,4571,4572,4553 }, + {4437,3049,4476 ,4572,2803,4558 }, {4408,4437,4476 ,4553,4572,4558 }, + {4476,3049,1904 ,4558,2803,2164 }, {5834,5835,1942 ,543,4573,2337 }, + {4280,4159,413 ,1242,1299,351 }, {1837,363,380 ,997,427,2540 }, + {102,4312,4313 ,432,467,4574 }, {1079,987,1832 ,4575,2950,4576 }, + {4103,1731,604 ,4577,485,486 }, {4104,2504,3902 ,3519,2898,4437 }, + {23,1284,4100 ,2272,4564,4481 }, {2588,14,91 ,2072,1986,1984 }, + {1792,377,955 ,324,4311,3991 }, {3784,4239,4217 ,2252,2254,83 }, + {465,604,4304 ,4578,486,4579 }, {4888,4932,5379 ,3997,4580,3998 }, + {4653,4679,5182 ,4347,3852,4567 }, {8000,8023,7999 ,4581,4582,4583 }, + {4361,3938,4382 ,4584,3572,4557 }, {4307,4361,4382 ,4562,4584,4557 }, + {4382,3938,4409 ,4557,3572,4570 }, {3938,4437,4436 ,3572,4572,4571 }, + {4409,3938,4436 ,4570,3572,4571 }, {659,1730,802 ,71,4506,400 }, + {6949,6989,6947 ,4585,4586,4587 }, {2834,4961,2403 ,2860,2905,2904 }, + {1051,6,1858 ,4588,4589,4590 }, {1305,2892,3763 ,466,918,3753 }, + {1940,64,11 ,3925,297,299 }, {4356,4433,4432 ,155,4591,156 }, + {2504,2583,3902 ,2898,2672,4437 }, {4383,4311,490 ,399,468,469 }, + {210,1695,194 ,4592,471,4593 }, {4218,3784,4217 ,4503,2252,83 }, + {1695,4100,1933 ,471,4481,470 }, {4890,4918,4933 ,4594,4595,4596 }, + {3088,3132,3131 ,3290,2796,3732 }, {1091,5236,160 ,4597,4301,4295 }, + {1101,1691,1851 ,2,2326,4241 }, {4361,3939,3938 ,4584,3539,3572 }, + {3938,3112,4437 ,3572,2795,4572 }, {4437,3112,3049 ,4572,2795,2803 }, + {4253,4398,4048 ,4598,4599,4600 }, {1625,4251,4192 ,388,132,4601 }, + {4380,4452,4451 ,4602,2398,271 }, {2375,2240,3945 ,4479,4478,4550 }, + {771,1669,2707 ,3799,3798,235 }, {5986,1251,5563 ,4603,4604,4605 }, + {1976,4351,2390 ,1180,4606,1594 }, {4074,890,2959 ,4607,3283,4608 }, + {1421,1223,490 ,4609,2182,469 }, {207,4533,4493 ,4516,4515,4514 }, + {4494,207,4493 ,4610,4516,4514 }, {50,651,2709 ,4611,3591,3523 }, + {2856,3344,588 ,3821,1861,3822 }, {3938,3090,3112 ,3572,2805,2795 }, + {3112,3089,3049 ,2795,2794,2803 }, {0,4253,4048 ,4612,4598,4600 }, + {376,1564,842 ,379,368,4613 }, {5835,212,1942 ,4573,4614,2337 }, + {1633,4103,466 ,373,4577,347 }, {1051,793,6 ,4588,4200,4589 }, + {4889,4890,4933 ,4615,4594,4596 }, {5571,5643,1350 ,4616,4617,509 }, + {302,683,223 ,622,621,1985 }, {987,66,1832 ,2950,4563,4576 }, + {1748,1421,490 ,4618,4609,469 }, {1225,1223,57 ,327,2182,222 }, + {3786,1418,1934 ,4074,4619,4620 }, {275,1529,477 ,4621,4524,4519 }, + {1,4062,969 ,4622,4623,4199 }, {4080,1883,4253 ,4624,4625,4598 }, + {735,2012,1345 ,224,2212,203 }, {1462,1927,710 ,2338,1196,33 }, + {4829,4799,4770 ,4626,4627,4345 }, {5194,4395,4373 ,727,726,3288 }, + {2892,771,3763 ,918,3799,3753 }, {4192,4182,118 ,4601,435,1124 }, + {7008,7007,6967 ,3176,4628,4629 }, {275,276,1529 ,4621,4525,4524 }, + {1162,463,276 ,4630,1941,4525 }, {275,1162,276 ,4621,4630,4525 }, + {1631,504,467 ,4631,4536,346 }, {2044,558,465 ,4031,4151,4578 }, + {463,1631,467 ,1941,4631,346 }, {504,513,4330 ,4536,377,399 }, + {1070,1560,245 ,4632,4633,4488 }, {7804,7856,7803 ,4634,4635,4636 }, + {4062,500,969 ,4623,4637,4199 }, {802,439,1346 ,400,4638,484 }, + {4080,4253,316 ,4624,4598,4639 }, {5817,5818,5834 ,701,4640,543 }, + {4398,759,4242 ,4599,4641,4642 }, {1949,217,216 ,3637,4643,4644 }, + {1976,2390,1977 ,1180,1594,1503 }, {1747,1731,979 ,266,485,267 }, + {1154,513,4331 ,4545,377,4645 }, {4075,1993,344 ,4521,4544,562 }, + {4933,4918,4966 ,4596,4595,4646 }, {4239,85,4217 ,2254,2504,83 }, + {4714,4777,5267 ,1031,3917,4647 }, {1426,4062,500 ,4648,4623,4637 }, + {1377,4069,630 ,2651,1881,2491 }, {4329,4355,4380 ,1247,1447,4602 }, + {1742,1562,7051 ,707,4649,547 }, {1153,8087,6797 ,2245,385,2246 }, + {4149,4174,4148 ,1486,769,2305 }, {379,1114,1467 ,2299,2256,4535 }, + {1070,245,1163 ,4632,4488,2211 }, {4156,4222,1859 ,4555,530,4480 }, + {4502,4156,1859 ,4650,4555,4480 }, {1212,4075,447 ,4520,4521,4651 }, + {122,755,4113 ,4534,1615,207 }, {245,1560,1458 ,4488,4633,4489 }, + {1441,1092,1063 ,1485,4484,4483 }, {1308,1306,4245 ,4652,4653,4654 }, + {510,2092,191 ,4655,4656,4657 }, {759,4265,4242 ,4641,4658,4642 }, + {1858,6,217 ,4590,4589,4643 }, {4088,4124,4087 ,4659,2306,4660 }, + {4355,4356,4380 ,1447,155,4602 }, {196,607,94 ,152,2207,1858 }, + {2261,608,672 ,4661,2300,4662 }, {887,4050,1087 ,4554,4663,4482 }, + {1087,4050,1531 ,4482,4663,4561 }, {4107,4396,1932 ,4491,4664,2141 }, + {4145,660,67 ,101,387,1125 }, {4175,4218,4217 ,4500,4503,83 }, + {2660,890,4074 ,455,3283,4607 }, {512,1308,4245 ,4665,4652,4654 }, + {4279,1489,4139 ,4666,4667,4668 }, {362,4048,192 ,4669,4600,4670 }, + {215,71,81 ,4671,4672,4673 }, {5818,5835,5834 ,4640,4573,543 }, + {4124,4123,4087 ,2306,133,4660 }, {4109,4175,4149 ,576,4500,1486 }, + {1925,698,1425 ,161,4674,4518 }, {4396,377,1509 ,4664,4311,4565 }, + {1932,4396,1509 ,2141,4664,4565 }, {608,378,672 ,2300,4551,4662 }, + {754,1063,800 ,4675,4483,4676 }, {1063,1366,800 ,4483,4485,4676 }, + {1807,608,2261 ,4560,2300,4661 }, {4262,699,2 ,4492,4677,4493 }, + {4197,4258,4238 ,82,84,4678 }, {3532,4074,4057 ,4415,4607,272 }, + {209,311,648 ,4499,246,4679 }, {4141,1489,4279 ,4680,4667,4666 }, + {4398,4242,192 ,4599,4642,4670 }, {5802,5801,5765 ,4681,700,540 }, + {783,2663,688 ,679,3593,765 }, {4253,4049,4398 ,4598,4682,4599 }, + {4088,4087,630 ,4659,4660,2491 }, {4069,4088,630 ,1881,4659,2491 }, + {1308,512,1883 ,4652,4665,4625 }, {5684,4106,3345 ,4683,1742,3476 }, + {1595,4145,67 ,99,101,1125 }, {4356,4403,4380 ,155,154,4602 }, + {376,979,1731 ,379,267,485 }, {3532,2660,4074 ,4415,455,4607 }, + {4137,4095,1259 ,399,349,348 }, {124,5482,2661 ,2206,3565,4684 }, + {1284,1063,754 ,4564,4483,4675 }, {229,457,116 ,221,214,217 }, + {1695,1933,701 ,471,470,4685 }, {1933,4100,4274 ,470,4481,4686 }, + {1933,4274,701 ,470,4686,4685 }, {4257,4238,4276 ,698,4678,1446 }, + {4111,1871,1872 ,4687,2458,2459 }, {889,770,844 ,35,275,4688 }, + {4048,4398,192 ,4600,4599,4670 }, {215,81,422 ,4671,4673,4689 }, + {216,215,422 ,4644,4671,4689 }, {5766,5802,5765 ,4487,4681,540 }, + {4087,879,630 ,4660,1155,2491 }, {65,71,215 ,4690,4672,4671 }, + {1926,1308,1883 ,4691,4652,4625 }, {1462,710,4177 ,2338,33,2339 }, + {4160,2012,735 ,223,2212,224 }, {3375,3532,4057 ,4426,4415,272 }, + {1425,698,121 ,4518,4674,4504 }, {4350,3346,4098 ,740,4457,353 }, + {3585,4494,4493 ,3806,4610,4514 }, {5199,5240,5219 ,422,481,1860 }, + {476,275,477 ,4692,4621,4519 }, {2,658,265 ,4493,4693,4496 }, + {4137,1259,4095 ,399,348,349 }, {4251,4470,4182 ,132,4694,435 }, + {4352,3299,3990 ,4695,3232,3231 }, {345,1489,4119 ,4696,4667,4697 }, + {362,192,71 ,4669,4670,4672 }, {1306,655,345 ,4653,1122,4696 }, + {3324,3304,3305 ,881,1050,879 }, {4098,3375,4057 ,353,4426,272 }, + {1935,3172,4289 ,4098,4155,4152 }, {275,318,463 ,4621,4698,1941 }, + {1162,275,463 ,4630,4621,1941 }, {1695,701,194 ,471,4685,4593 }, + {463,318,1631 ,1941,4698,4631 }, {504,4118,513 ,4536,4699,377 }, + {1113,117,4213 ,10,9,1070 }, {4117,4156,4502 ,4700,4555,4650 }, + {4117,4157,4156 ,4700,3755,4555 }, {4252,887,4156 ,4021,4554,4555 }, + {3141,4127,4176 ,4701,4702,4703 }, {4227,5237,5307 ,1564,1800,461 }, + {443,444,702 ,920,922,2699 }, {2255,939,4350 ,3229,3354,740 }, + {683,196,223 ,621,152,1985 }, {345,4161,1489 ,4696,4364,4667 }, + {4161,4210,1489 ,4364,4704,4667 }, {5801,5802,5817 ,700,4681,701 }, + {65,362,71 ,4690,4669,4672 }, {2859,31,4139 ,3451,3450,4668 }, + {824,4245,2092 ,4705,4654,4656 }, {4245,1306,345 ,4654,4653,4696 }, + {4177,889,379 ,2339,35,2299 }, {265,4539,697 ,4496,633,3578 }, + {4157,4252,4156 ,3755,4021,4555 }, {4252,4102,887 ,4021,4706,4554 }, + {4155,699,4262 ,4505,4677,4492 }, {5237,2086,1525 ,1800,1802,1866 }, + {4102,4050,887 ,4706,4663,4554 }, {1531,1530,4107 ,4561,4707,4491 }, + {1530,4314,4396 ,4707,1864,4664 }, {4107,1530,4396 ,4491,4707,4664 }, + {4058,4061,3347 ,4708,4490,2098 }, {4081,1079,1926 ,4709,4575,4691 }, + {759,824,2092 ,4641,4705,4656 }, {4279,4139,563 ,4666,4668,4710 }, + {0,4048,362 ,4612,4600,4669 }, {4062,1426,500 ,4623,4648,4637 }, + {3317,4069,1377 ,1882,1881,2651 }, {3452,987,1079 ,1794,2950,4575 }, + {416,466,2091 ,556,347,4494 }, {466,604,2091 ,347,486,4494 }, + {3622,1935,4289 ,4106,4098,4152 }, {4201,4181,4051 ,436,2273,437 }, + {4314,2086,377 ,1864,1802,4311 }, {4396,4314,377 ,4664,1864,4311 }, + {447,4075,344 ,4651,4521,562 }, {4977,4999,4998 ,4711,4712,4713 }, + {4276,4302,4356 ,1446,3727,155 }, {6563,2343,8061 ,464,2685,453 }, + {5573,5237,1525 ,2217,1800,1866 }, {558,416,465 ,4151,556,4578 }, + {4109,4090,4076 ,576,1487,577 }, {1467,4084,755 ,4535,205,1615 }, + {66,2697,45 ,4563,2006,4714 }, {3532,3617,3050 ,4415,4425,4414 }, + {21,3946,1089 ,1231,1230,103 }, {133,284,1241 ,1719,139,313 }, + {2695,2859,4210 ,3453,3451,4704 }, {4161,2695,4210 ,4364,3453,4704 }, + {759,2092,510 ,4641,4656,4655 }, {5802,5818,5817 ,4681,4640,701 }, + {510,191,263 ,4655,4657,4715 }, {71,27,81 ,4672,303,4673 }, {4087,4123,879 ,4660,133,1155 }, + {4062,1426,4082 ,4623,4648,4716 }, {502,604,465 ,2301,486,4578 }, + {4058,4065,4061 ,4708,4717,4490 }, {279,325,4691 ,2176,2088,2089 }, + {1163,4160,1223 ,2211,223,2182 }, {4151,510,263 ,839,4655,4715 }, + {175,4262,702 ,211,4492,2699 }, {1619,3196,1686 ,1003,2581,1004 }, + {192,4242,43 ,4670,4642,304 }, {4312,490,1225 ,467,469,327 }, + {413,4159,4235 ,351,1299,985 }, {4082,3452,1079 ,4716,1794,4575 }, + {4238,4258,4302 ,4678,84,3727 }, {193,864,867 ,1448,840,1121 }, + {4079,4113,1212 ,4718,207,4520 }, {672,378,122 ,4662,4551,4534 }, + {4824,1602,4886 ,4719,2329,4720 }, {84,3759,2030 ,4231,4233,770 }, + {2995,2254,2702 ,2856,3705,2854 }, {4400,4450,3585 ,4721,4722,3806 }, + {637,4400,3585 ,3805,4721,3806 }, {4495,4494,3585 ,4723,4610,3806 }, + {4450,4495,3585 ,4722,4723,3806 }, {4238,4302,4276 ,4678,3727,1446 }, + {4257,4238,4276 ,698,4678,1446 }, {4258,4303,4302 ,84,3728,3727 }, + {1150,683,681 ,741,621,620 }, {2697,2255,45 ,2006,3229,4714 }, + {1006,962,1100 ,234,233,2466 }, {4101,1600,209 ,4724,4498,4499 }, + {192,43,27 ,4670,304,303 }, {4059,4193,609 ,363,4725,4726 }, + {1195,4347,4395 ,725,3287,726 }, {1150,774,683 ,741,767,621 }, + {4139,31,4078 ,4668,3450,4341 }, {7799,7825,7798 ,4727,3768,4728 }, + {4779,4778,4716 ,4729,3916,1030 }, {122,4113,4079 ,4534,207,4718 }, + {841,122,4079 ,4730,4534,4718 }, {4065,1253,4067 ,4717,2353,185 }, + {756,1748,490 ,4731,4618,469 }, {4311,756,490 ,468,4731,469 }, + {4494,4495,207 ,4610,4723,4516 }, {1366,1807,4133 ,4485,4560,4732 }, + {4263,2420,4056 ,274,273,1123 }, {38,1150,681 ,742,741,620 }, + {4062,1,4063 ,4623,4622,4733 }, {808,4472,5295 ,4734,2189,2188 }, + {71,192,27 ,4672,4670,303 }, {4051,1695,210 ,437,471,4592 }, + {5681,5700,5699 ,2934,4501,43 }, {4071,4070,4064 ,186,4497,1880 }, + {4335,4265,4151 ,838,4658,839 }, {4242,642,43 ,4642,1243,304 }, + {3347,206,4058 ,2098,3932,4708 }, {4121,4139,4078 ,4735,4668,4341 }, + {0,362,65 ,4612,4669,4690 }, {3271,8125,8117 ,1727,752,4119 }, + {216,65,215 ,4644,4690,4671 }, {5618,5420,103 ,127,4228,128 }, + {1931,4644,5019 ,4736,2425,2384 }, {4067,4071,4065 ,185,186,4717 }, + {240,4724,1790 ,1571,4737,4738 }, {260,1210,4311 ,1573,4739,468 }, + {4183,1210,260 ,1572,4739,1573 }, {1070,4073,1560 ,4632,4740,4633 }, + {1730,4262,175 ,4506,4492,211 }, {476,477,207 ,4692,4519,4516 }, + {1134,476,207 ,4741,4692,4516 }, {800,1366,4133 ,4676,4485,4732 }, + {1441,4093,1092 ,1485,1484,4484 }, {4182,661,4201 ,435,2297,436 }, + {4470,880,661 ,4694,1483,2297 }, {4242,4265,642 ,4642,4658,1243 }, + {770,805,844 ,275,389,4688 }, {208,4114,1259 ,4742,362,348 }, + {143,2043,781 ,157,4100,158 }, {1421,1163,1223 ,4609,2211,2182 }, + {4313,2423,102 ,4574,433,432 }, {4265,4335,642 ,4658,838,1243 }, + {1481,654,231 ,194,93,92 }, {759,510,4151 ,4641,4655,839 }, {5986,5563,4550 ,4603,4605,716 }, + {4148,4123,4124 ,2305,133,2306 }, {322,46,5 ,1250,2232,350 }, + {4942,5420,5618 ,4191,4228,127 }, {1092,1462,4116 ,4484,2338,2340 }, + {4376,4073,1070 ,4743,4740,4632 }, {1560,2001,1458 ,4633,4744,4489 }, + {4116,4177,379 ,2340,2339,2299 }, {4376,1421,1748 ,4743,4609,4618 }, + {275,129,318 ,4621,730,4698 }, {4274,4100,72 ,4686,4481,4745 }, + {4376,1163,1421 ,4743,2211,4609 }, {4093,1462,1092 ,1484,2338,4484 }, + {5420,84,103 ,4228,4231,128 }, {5162,4942,5618 ,181,4191,127 }, + {4491,4519,226 ,107,4746,108 }, {1926,1832,1308 ,4691,4576,4652 }, + {4049,512,824 ,4682,4665,4705 }, {1992,214,781 ,834,87,158 }, + {3132,3088,3089 ,2796,3290,2794 }, {21,1089,5203 ,1231,103,102 }, + {614,609,1367 ,1397,4726,574 }, {4059,609,614 ,363,4726,1397 }, + {4265,759,4151 ,4658,4641,839 }, {802,1730,4130 ,400,4506,210 }, + {608,379,378 ,2300,2299,4551 }, {824,512,4245 ,4705,4665,4654 }, + {3577,531,3369 ,4059,3829,3836 }, {4403,4432,4452 ,154,156,2398 }, + {4148,4173,4123 ,2305,134,133 }, {4196,4195,4173 ,4747,4748,134 }, + {4540,891,4526 ,19,189,187 }, {1163,4376,1070 ,2211,4743,4632 }, + {4274,72,701 ,4686,4745,4685 }, {1631,753,504 ,4631,4749,4536 }, + {504,753,4118 ,4536,4749,4699 }, {4134,658,2 ,4750,4693,4493 }, + {4100,1284,1368 ,4481,4564,4751 }, {1458,825,1925 ,4489,4752,161 }, + {3562,5944,3943 ,4753,4754,4755 }, {4394,4273,4796 ,4044,120,4756 }, + {1445,1469,1397 ,2359,2361,2358 }, {825,698,1925 ,4752,4674,161 }, + {4100,1368,72 ,4481,4751,4745 }, {4081,1926,4080 ,4709,4691,4624 }, + {844,805,1115 ,4688,389,2257 }, {1345,2012,1846 ,203,2212,204 }, + {705,4132,659 ,70,162,71 }, {4151,263,208 ,839,4715,4742 }, {4504,4151,208 ,4757,839,4742 }, + {1833,4151,4504 ,317,839,4757 }, {111,3014,258 ,1154,3568,3001 }, + {1625,4192,660 ,388,4601,387 }, {316,4253,0 ,4639,4598,4612 }, + {4380,4403,4452 ,4602,154,2398 }, {1308,1832,1306 ,4652,4576,4653 }, + {4148,4196,4173 ,2305,4747,134 }, {3946,2893,1089 ,1230,1679,103 }, + {214,466,142 ,87,347,159 }, {4076,4090,4070 ,577,1487,4497 }, + {262,194,312 ,4758,4593,4759 }, {4083,4102,4252 ,3754,4706,4021 }, + {670,4144,5043 ,4760,4761,4762 }, {1459,4050,4102 ,4017,4663,4706 }, + {4083,1459,4102 ,3754,4017,4706 }, {1459,4066,4050 ,4017,4763,4663 }, + {658,4243,265 ,4693,4764,4496 }, {376,1731,4103 ,379,485,4577 }, + {1926,1883,4080 ,4691,4625,4624 }, {4051,4181,1933 ,437,2273,470 }, + {2092,4245,4141 ,4656,4654,4680 }, {4093,4234,1462 ,1484,1197,2338 }, + {4234,1927,1462 ,1197,1196,2338 }, {563,4139,4121 ,4710,4668,4735 }, + {2031,2122,2067 ,1560,1128,1592 }, {81,27,545 ,4673,303,305 }, + {104,774,1150 ,4765,767,741 }, {4280,4335,1833 ,1242,838,317 }, + {4195,4216,4173 ,4748,1241,134 }, {979,1595,67 ,267,99,1125 }, + {702,444,649 ,2699,922,1579 }, {4243,317,265 ,4764,561,4496 }, + {4066,1459,1531 ,4763,4017,4561 }, {4050,4066,1531 ,4663,4763,4561 }, + {1459,1561,1530 ,4017,1865,4707 }, {1531,1459,1530 ,4561,4017,4707 }, + {266,699,4155 ,4766,4677,4505 }, {121,266,4155 ,4504,4766,4505 }, + {21,5203,2030 ,1231,102,770 }, {3476,1941,3568 ,3838,1034,3839 }, + {1530,1561,4314 ,4707,1865,1864 }, {4174,4175,4217 ,769,4500,83 }, + {3532,538,2660 ,4415,4380,455 }, {5643,5642,1350 ,4617,526,509 }, + {655,4056,345 ,1122,1123,4696 }, {4141,4119,1489 ,4680,4697,4667 }, + {81,545,323 ,4673,305,4767 }, {4065,4071,4064 ,4717,186,1880 }, + {4081,4080,500 ,4709,4624,4637 }, {963,786,962 ,1341,2071,233 }, + {2536,3017,49 ,2793,3369,3367 }, {191,4279,4114 ,4657,4666,362 }, + {263,191,4114 ,4715,4657,362 }, {793,316,0 ,4200,4639,4612 }, + {4978,6414,6413 ,178,113,112 }, {2502,2331,2504 ,3740,2895,2898 }, + {4067,4068,4077 ,185,408,126 }, {702,2,443 ,2699,4493,920 }, + {699,4134,2 ,4677,4750,4493 }, {6351,8103,8077 ,1568,1291,2078 }, + {416,502,465 ,556,2301,4578 }, {604,1747,4304 ,486,266,4579 }, + {885,321,2313 ,216,60,438 }, {1591,1032,1318 ,1072,1581,164 }, + {4201,661,4181 ,436,2297,2273 }, {2707,1669,5162 ,235,3798,181 }, + {1489,4210,4139 ,4667,4704,4668 }, {4234,4093,880 ,1197,1484,1483 }, + {191,4141,4279 ,4657,4680,4666 }, {208,263,4114 ,4742,4715,362 }, + {1331,1411,2041 ,790,850,2737 }, {5839,5807,5840 ,4768,4769,4770 }, + {1333,1936,3300 ,849,848,694 }, {5288,5308,4868 ,3993,3528,3969 }, + {5970,4146,2504 ,3518,3730,2898 }, {609,1919,1367 ,4726,1779,574 }, + {609,4078,1919 ,4726,4341,1779 }, {4195,4237,4216 ,4748,697,1241 }, + {4347,3867,1851 ,3287,4507,4241 }, {793,0,6 ,4200,4612,4589 }, + {4237,4275,4256 ,697,699,3433 }, {500,4080,316 ,4637,4624,4639 }, + {45,195,655 ,4714,739,1122 }, {8096,4031,2474 ,1796,3069,874 }, + {8110,8125,3271 ,1140,752,1727 }, {2558,1066,19 ,977,680,1067 }, + {318,129,695 ,4698,730,731 }, {447,344,317 ,4651,562,561 }, {265,317,4539 ,4496,561,633 }, + {1186,1623,1624 ,1605,1709,1708 }, {658,1212,447 ,4693,4520,4651 }, + {4079,1212,658 ,4718,4520,4693 }, {94,607,82 ,1858,2207,138 }, + {315,413,4235 ,352,351,985 }, {1669,4942,5162 ,3798,4191,181 }, + {4056,4161,345 ,1123,4364,4696 }, {43,642,739 ,304,1243,2233 }, + {934,880,4470 ,131,1483,4694 }, {653,1259,4053 ,318,348,1371 }, + {49,3017,1015 ,3367,3369,3368 }, {805,24,1115 ,389,247,2257 }, + {5240,5284,5264 ,481,571,1192 }, {1532,217,1949 ,4771,4643,3637 }, + {500,316,793 ,4637,4639,4200 }, {969,500,793 ,4199,4637,4200 }, + {147,792,4712 ,3469,95,3816 }, {979,67,970 ,267,1125,268 }, {4251,4182,4192 ,132,435,4601 }, + {4375,1591,981 ,312,1072,2128 }, {4400,4401,4450 ,4721,4772,4722 }, + {1730,4155,4262 ,4506,4505,4492 }, {1633,376,4103 ,373,379,4577 }, + {664,760,663 ,1755,3106,4773 }, {672,122,266 ,4662,4534,4766 }, + {1179,207,4495 ,4774,4516,4723 }, {1210,261,4311 ,4739,4775,468 }, + {294,4375,1132 ,945,312,61 }, {4779,4803,4778 ,4729,4776,3916 }, + {237,99,100 ,869,3743,2823 }, {4095,614,1805 ,349,1397,1071 }, + {6122,8096,2072 ,1797,1796,1812 }, {180,1946,4385 ,4777,4408,4778 }, + {3299,4352,4400 ,3232,4695,4721 }, {1131,1728,1015 ,4779,4780,3368 }, + {89,1131,1015 ,1482,4779,3368 }, {1728,408,1015 ,4780,4781,3368 }, + {7,49,1015 ,4782,3367,3368 }, {408,7,1015 ,4781,4782,3368 }, + {4114,4279,563 ,362,4666,4710 }, {2709,7,50 ,3523,4782,4611 }, + {842,1564,1595 ,4613,368,99 }, {4049,759,4398 ,4682,4641,4599 }, + {1665,456,455 ,3077,301,2205 }, {2866,2867,1205 ,2655,2844,3187 }, + {806,706,843 ,4783,4784,2423 }, {1132,4375,981 ,61,312,2128 }, + {266,122,841 ,4766,4534,4730 }, {1179,1134,207 ,4774,4741,4516 }, + {4133,1807,672 ,4732,4560,4662 }, {1601,476,1134 ,4785,4692,4741 }, + {1179,1601,1134 ,4774,4785,4741 }, {6643,8122,8101 ,3133,384,383 }, + {3452,3513,3977 ,1794,441,440 }, {1284,754,4073 ,4564,4675,4740 }, + {476,129,275 ,4692,730,4621 }, {1368,1284,4073 ,4751,4564,4740 }, + {4706,4763,4704 ,4786,4787,4788 }, {4431,4380,4451 ,568,4602,271 }, + {1066,4336,19 ,680,4789,1067 }, {4336,4384,600 ,4789,4409,978 }, + {19,4336,600 ,1067,4789,978 }, {4063,1426,4062 ,4733,4648,4623 }, + {260,4312,102 ,1573,467,432 }, {8,4505,1798 ,3341,4790,4451 }, + {4505,1799,1798 ,4790,4791,4451 }, {1799,1131,89 ,4791,4779,1482 }, + {1798,1799,89 ,4451,4791,1482 }, {176,408,1728 ,4792,4781,4780 }, + {1131,176,1728 ,4779,4792,4780 }, {4114,563,4059 ,362,4710,363 }, + {3308,3294,7110 ,3894,3895,1990 }, {4059,563,4193 ,363,4710,4725 }, + {563,4121,4193 ,4710,4735,4725 }, {1595,1564,4145 ,99,368,101 }, + {1092,4116,1424 ,4484,2340,2298 }, {318,753,1631 ,4698,4749,4631 }, + {1807,2261,672 ,4560,4661,4662 }, {261,4333,4311 ,4775,4793,468 }, + {1368,4073,4376 ,4751,4740,4743 }, {5458,4129,4083 ,3756,3812,3754 }, + {226,4519,1150 ,108,4746,741 }, {4177,710,889 ,2339,33,35 }, + {4315,4337,4336 ,4794,4795,4789 }, {1066,4315,4336 ,680,4794,4789 }, + {4337,4385,4384 ,4795,4778,4409 }, {4336,4337,4384 ,4789,4795,4409 }, + {180,4455,8 ,4777,4796,3341 }, {4455,4505,8 ,4796,4790,3341 }, + {1797,176,1131 ,4797,4792,4779 }, {1799,1797,1131 ,4791,4797,4779 }, + {782,786,777 ,1648,2071,421 }, {4121,4078,609 ,4735,4341,4726 }, + {759,4049,824 ,4641,4682,4705 }, {4311,4333,756 ,468,4793,4731 }, + {379,844,1114 ,2299,4688,2256 }, {2070,1095,614 ,573,1910,1397 }, + {4238,4257,4276 ,4678,698,1446 }, {4083,4129,1459 ,3754,3812,4017 }, + {4129,3477,1459 ,3812,1736,4017 }, {800,4133,1458 ,4676,4732,4489 }, + {1560,800,2001 ,4633,4676,4744 }, {69,25,1319 ,321,2186,322 }, + {118,4051,210 ,1124,437,4592 }, {4315,1450,4337 ,4794,823,4795 }, + {4385,4413,180 ,4778,4798,4777 }, {4413,4455,180 ,4798,4796,4777 }, + {4506,1797,1799 ,4799,4797,4791 }, {4505,4506,1799 ,4790,4799,4791 }, + {573,823,408 ,4800,4801,4781 }, {176,573,408 ,4792,4800,4781 }, + {50,7,408 ,4611,4782,4781 }, {823,50,408 ,4801,4611,4781 }, {2092,4141,191 ,4656,4680,4657 }, + {4193,4121,609 ,4725,4735,4726 }, {934,4234,880 ,131,1197,1483 }, + {4245,4119,4141 ,4654,4697,4680 }, {1926,1079,1832 ,4691,4575,4576 }, + {3985,789,3858 ,4802,3910,4803 }, {66,1211,2697 ,4563,2007,2006 }, + {4085,1368,4376 ,4804,4751,4743 }, {4149,4148,4090 ,1486,2305,1487 }, + {4085,4376,1748 ,4804,4743,4618 }, {4237,4238,4257 ,697,4678,698 }, + {2001,800,1458 ,4744,4676,4489 }, {4276,4356,4355 ,1446,155,1447 }, + {4108,4124,4088 ,2304,2306,4659 }, {7029,2341,3008 ,548,845,546 }, + {3763,771,2707 ,3753,3799,235 }, {1808,1836,380 ,4502,2490,2540 }, + {4337,4338,4385 ,4795,4805,4778 }, {4385,4338,4413 ,4778,4805,4798 }, + {4456,4506,4505 ,4806,4799,4790 }, {4455,4456,4505 ,4796,4806,4790 }, + {4506,895,1797 ,4799,4807,4797 }, {895,573,176 ,4807,4800,4792 }, + {1797,895,176 ,4797,4807,4792 }, {73,50,823 ,4808,4611,4801 }, + {1621,73,823 ,4809,4808,4801 }, {148,651,50 ,4810,3591,4611 }, + {73,148,50 ,4808,4810,4611 }, {212,1208,1942 ,4614,3211,2337 }, + {4251,934,4470 ,132,131,4694 }, {6,0,65 ,4589,4612,4690 }, {884,4059,614 ,2160,363,1397 }, + {1259,4059,4095 ,348,363,349 }, {1306,45,655 ,4653,4714,1122 }, + {1467,1114,4084 ,4535,2256,205 }, {3872,5188,5786 ,4811,4812,4813 }, + {4197,4238,4196 ,82,4678,4747 }, {379,889,844 ,2299,35,4688 }, + {842,1595,979 ,4613,99,267 }, {261,312,756 ,4775,4759,4731 }, + {312,194,756 ,4759,4593,4731 }, {1458,4133,825 ,4489,4732,4752 }, + {4238,4237,4196 ,4678,697,4747 }, {43,739,545 ,304,2233,305 }, + {2650,4031,8096 ,914,3069,1796 }, {4316,4338,4337 ,4814,4805,4795 }, + {1450,4316,4337 ,823,4814,4795 }, {4414,4456,4455 ,4815,4806,4796 }, + {4413,4414,4455 ,4798,4815,4796 }, {4456,4507,4506 ,4806,4816,4799 }, + {4506,4507,895 ,4799,4816,4807 }, {452,1621,823 ,4817,4809,4801 }, + {573,452,823 ,4800,4817,4801 }, {73,2254,148 ,4808,3705,4810 }, + {4373,4347,1851 ,3288,3287,4241 }, {217,6,216 ,4643,4589,4644 }, + {6,65,216 ,4589,4690,4644 }, {4210,2859,4139 ,4704,3451,4668 }, + {4071,4076,4070 ,186,577,4497 }, {4084,2047,1600 ,205,2258,4498 }, + {455,4471,259 ,2205,2187,2203 }, {194,701,756 ,4593,4685,4731 }, + {698,266,121 ,4674,4766,4504 }, {918,416,558 ,4150,556,4151 }, + {4077,4125,4109 ,126,125,576 }, {4144,670,261 ,4761,4760,4775 }, + {670,312,261 ,4760,4759,4775 }, {1114,2047,4084 ,2256,2258,205 }, + {4313,4312,1225 ,4574,467,327 }, {1148,4132,705 ,160,162,70 }, + {259,4471,258 ,2203,2187,3001 }, {1340,4266,1450 ,822,4818,823 }, + {1450,4266,4316 ,823,4818,4814 }, {2254,2995,148 ,3705,2856,4810 }, + {4363,4414,4413 ,4819,4815,4798 }, {4338,4363,4413 ,4805,4819,4798 }, + {4415,4457,4456 ,4820,4821,4806 }, {4414,4415,4456 ,4815,4820,4806 }, + {4456,4457,4507 ,4806,4821,4816 }, {895,452,573 ,4807,4817,4800 }, + {752,2254,73 ,4822,3705,4808 }, {8086,8087,1153 ,4823,385,2245 }, + {4171,659,802 ,399,71,400 }, {4245,345,4119 ,4654,4696,4697 }, + {4135,1259,4179 ,1069,348,251 }, {4159,4280,1833 ,1299,1242,317 }, + {3793,4305,4357 ,3420,4512,4508 }, {664,663,568 ,1755,4773,1723 }, + {4149,4175,4174 ,1486,4500,769 }, {5136,5178,4630 ,1817,524,827 }, + {4089,4088,4069 ,4495,4659,1881 }, {4070,4089,4069 ,4497,4495,1881 }, + {3978,6679,6418 ,4549,4824,3441 }, {4084,4101,1178 ,205,4724,206 }, + {4084,1600,4101 ,205,4498,4724 }, {2,461,443 ,4493,399,920 }, + {660,4192,118 ,387,4601,1124 }, {3615,5681,5660 ,201,2934,3054 }, + {1090,4266,1340 ,2687,4818,822 }, {4266,4317,4316 ,4818,4825,4814 }, + {4317,4363,4338 ,4825,4819,4805 }, {4316,4317,4338 ,4814,4825,4805 }, + {4364,4415,4414 ,4826,4820,4815 }, {4363,4364,4414 ,4819,4826,4815 }, + {4507,1769,895 ,4816,4827,4807 }, {1769,1084,895 ,4827,4828,4807 }, + {1084,246,452 ,4828,4829,4817 }, {895,1084,452 ,4807,4828,4817 }, + {246,1654,1621 ,4829,4830,4809 }, {452,246,1621 ,4817,4829,4809 }, + {1654,73,1621 ,4830,4808,4809 }, {1654,167,73 ,4830,4831,4808 }, + {167,752,73 ,4831,4822,4808 }, {2659,8120,3286 ,3485,906,56 }, + {5182,5181,4631 ,4567,4319,1816 }, {1883,512,4049 ,4625,4665,4682 }, + {4446,2607,1300 ,237,2452,285 }, {4089,4108,4088 ,4495,2304,4659 }, + {5158,5182,4631 ,4832,4567,1816 }, {7,2709,49 ,4782,3523,3367 }, + {3039,2040,1090 ,2686,4833,2687 }, {323,545,46 ,4767,305,2232 }, + {4352,4401,4400 ,4695,4772,4721 }, {2043,11,781 ,4100,299,158 }, + {2040,537,1090 ,4833,4834,2687 }, {537,4267,4266 ,4834,4835,4818 }, + {1090,537,4266 ,2687,4834,4818 }, {4266,4267,4317 ,4818,4835,4825 }, + {4318,4364,4363 ,4836,4826,4819 }, {4317,4318,4363 ,4825,4836,4819 }, + {4457,4523,4507 ,4821,4837,4816 }, {4523,1084,1769 ,4837,4828,4827 }, + {4507,4523,1769 ,4816,4837,4827 }, {1589,167,1654 ,4838,4831,4830 }, + {2276,2254,752 ,4839,3705,4822 }, {167,2276,752 ,4831,4839,4822 }, + {123,48,1049 ,3607,4840,1362 }, {2276,3117,2254 ,4839,3407,3705 }, + {1883,4049,4253 ,4625,4682,4598 }, {8063,8061,8102 ,465,453,253 }, + {130,95,1798 ,2341,3482,4451 }, {642,4280,413 ,1243,1242,351 }, + {699,841,4079 ,4677,4730,4718 }, {825,4133,698 ,4752,4732,4674 }, + {67,118,359 ,1125,1124,4841 }, {67,359,970 ,1125,4841,268 }, + {4133,672,698 ,4732,4662,4674 }, {136,1179,4495 ,1685,4774,4723 }, + {5182,5180,5181 ,4567,1878,4319 }, {5197,1928,4518 ,4842,1282,1281 }, + {4267,4318,4317 ,4835,4836,4825 }, {4415,4458,4457 ,4820,4843,4821 }, + {4458,4477,4457 ,4843,4844,4821 }, {4457,4477,4523 ,4821,4844,4837 }, + {246,641,1654 ,4829,4845,4830 }, {641,1589,1654 ,4845,4838,4830 }, + {354,2276,167 ,4846,4839,4831 }, {2379,3117,2276 ,3238,3407,4839 }, + {354,2379,2276 ,4846,3238,4839 }, {4275,4276,4329 ,699,1446,1247 }, + {1600,24,311 ,4498,247,246 }, {1115,24,1600 ,2257,247,4498 }, + {2314,2407,6467 ,3742,4001,4000 }, {659,1425,1730 ,71,4518,4506 }, + {118,210,359 ,1124,4592,4841 }, {266,841,699 ,4766,4730,4677 }, + {1601,76,476 ,4785,689,4692 }, {1925,1425,659 ,161,4518,71 }, + {76,1632,476 ,689,2241,4692 }, {476,1632,129 ,4692,2241,730 }, + {462,318,695 ,4847,4698,731 }, {4470,661,4182 ,4694,2297,435 }, + {7479,7478,7422 ,4423,1402,1404 }, {537,4268,4267 ,4834,4848,4835 }, + {4267,4268,4318 ,4835,4848,4836 }, {4364,4386,4415 ,4826,4849,4820 }, + {4386,4438,4415 ,4849,4850,4820 }, {4415,4438,4458 ,4820,4850,4843 }, + {4458,4438,4477 ,4843,4850,4844 }, {1084,854,246 ,4828,4851,4829 }, + {854,641,246 ,4851,4845,4829 }, {4451,4452,4498 ,271,2398,88 }, + {4196,4237,4195 ,4747,697,4748 }, {376,842,979 ,379,4613,267 }, + {4216,4237,4256 ,1241,697,3433 }, {4504,208,653 ,4757,4742,318 }, + {323,46,706 ,4767,2232,4784 }, {698,672,266 ,4674,4662,4766 }, + {4073,754,1560 ,4740,4675,4633 }, {8095,7983,8062 ,4362,4361,3272 }, + {753,462,695 ,4749,4847,731 }, {318,462,753 ,4698,4847,4749 }, + {3618,735,3535 ,37,224,442 }, {1245,735,3618 ,36,224,37 }, {4130,1730,175 ,210,4506,211 }, + {4339,4340,4364 ,4852,4853,4826 }, {4318,4339,4364 ,4836,4852,4826 }, + {4340,4386,4364 ,4853,4849,4826 }, {4523,1770,1084 ,4837,4854,4828 }, + {1770,854,1084 ,4854,4851,4828 }, {894,10,641 ,4855,4856,4845 }, + {854,894,641 ,4851,4855,4845 }, {1878,1589,641 ,4857,4838,4845 }, + {10,1878,641 ,4856,4857,4845 }, {1392,167,1589 ,4858,4831,4838 }, + {1878,1392,1589 ,4857,4858,4838 }, {1392,354,167 ,4858,4846,4831 }, + {537,6199,4268 ,4834,4859,4848 }, {214,142,781 ,87,159,158 }, + {8089,8082,8097 ,254,454,1710 }, {46,322,843 ,2232,1250,2423 }, + {4053,1259,4135 ,1371,348,1069 }, {1833,4504,653 ,317,4757,318 }, + {1163,245,2012 ,2211,4488,2212 }, {1424,4116,379 ,2298,2340,2299 }, + {615,614,1095 ,766,1397,1910 }, {4065,4064,4061 ,4717,1880,4490 }, + {148,2995,651 ,4810,2856,3591 }, {4132,1925,659 ,162,161,71 }, + {754,800,1560 ,4675,4676,4633 }, {1808,4111,1872 ,4502,4687,2459 }, + {4268,4269,4318 ,4848,4860,4836 }, {4269,4340,4339 ,4860,4853,4852 }, + {4318,4269,4339 ,4836,4860,4852 }, {4477,1770,4523 ,4844,4854,4837 }, + {1770,1737,4523 ,4854,4861,4837 }, {4523,1737,1770 ,4837,4861,4854 }, + {1737,894,854 ,4861,4855,4851 }, {1770,1737,854 ,4854,4861,4851 }, + {1086,354,1392 ,4862,4846,4858 }, {4163,2379,354 ,4863,3238,4846 }, + {1086,4163,354 ,4862,4863,4846 }, {5334,4887,5376 ,2736,2735,2758 }, + {5950,8074,8066 ,2873,2460,960 }, {4765,4764,4706 ,3983,3982,4786 }, + {4095,4059,884 ,349,363,2160 }, {4182,4051,118 ,435,437,1124 }, + {5182,5221,5180 ,4567,4864,1878 }, {709,653,544 ,316,318,195 }, + {341,632,2193 ,1852,1380,1007 }, {735,1345,3535 ,224,203,442 }, + {72,1368,4085 ,4745,4751,4804 }, {3477,407,1459 ,1736,4018,4017 }, + {4773,4844,4804 ,4865,4866,4867 }, {1835,1871,1870 ,2421,2458,4868 }, + {5377,5411,5376 ,361,4284,2758 }, {4269,4284,4340 ,4860,4869,4853 }, + {4438,4478,4477 ,4850,4870,4844 }, {4477,4478,1770 ,4844,4870,4854 }, + {1770,4478,1737 ,4854,4870,4861 }, {693,1878,10 ,4871,4857,4856 }, + {693,1392,1878 ,4871,4858,4857 }, {213,4163,1086 ,3606,4863,4862 }, + {102,240,260 ,432,1571,1573 }, {4074,2959,4057 ,4607,4608,272 }, + {2012,245,1846 ,2212,4488,204 }, {683,396,196 ,621,153,152 }, + {245,1148,1846 ,4488,160,204 }, {226,111,1653 ,108,1154,106 }, + {379,1467,378 ,2299,4535,4551 }, {4315,1066,1450 ,4794,680,823 }, + {5243,5242,5180 ,3994,3843,1878 }, {6225,4321,5630 ,4872,4873,4874 }, + {701,72,1748 ,4685,4745,4618 }, {72,4085,1748 ,4745,4804,4618 }, + {5814,5815,5831 ,4875,4065,3943 }, {5799,5815,5814 ,4064,4065,4875 }, + {5798,5799,5814 ,4876,4064,4875 }, {5815,5832,5831 ,4065,3904,3943 }, + {3945,3978,6418 ,4550,4549,3441 }, {4229,4284,4269 ,4877,4869,4860 }, + {4387,4439,4438 ,4878,4879,4850 }, {4386,4387,4438 ,4849,4878,4850 }, + {4438,4439,4478 ,4850,4879,4870 }, {942,693,10 ,4880,4871,4856 }, + {894,942,10 ,4855,4880,4856 }, {228,1086,1392 ,4881,4862,4858 }, + {228,213,1086 ,4881,3606,4862 }, {1806,439,802 ,3124,4638,400 }, + {2431,1304,2476 ,2977,2639,2441 }, {5221,5243,5180 ,4864,3994,1878 }, + {4379,4380,4431 ,567,4602,568 }, {5265,5241,5242 ,4882,2120,3843 }, + {4158,802,1346 ,72,400,484 }, {2041,553,1331 ,2737,792,790 }, + {6346,7254,7860 ,4883,450,452 }, {697,113,59 ,3578,677,967 }, + {5243,5265,5242 ,3994,4882,3843 }, {600,4384,131 ,978,4409,2965 }, + {705,4537,1093 ,70,67,66 }, {1846,705,1093 ,204,70,66 }, {4174,4197,4148 ,769,82,2305 }, + {261,756,4333 ,4775,4731,4793 }, {5763,5799,5798 ,4212,4064,4876 }, + {5762,5763,5798 ,4884,4212,4876 }, {1220,875,1920 ,4885,579,581 }, + {7076,1076,7075 ,4886,4887,4420 }, {4284,4341,4340 ,4869,4888,4853 }, + {4341,4387,4386 ,4888,4878,4849 }, {4340,4341,4386 ,4853,4888,4849 }, + {4387,4416,4439 ,4878,4889,4879 }, {1738,942,894 ,4890,4880,4855 }, + {1737,1738,894 ,4861,4890,4855 }, {942,33,693 ,4880,4891,4871 }, + {1856,1392,693 ,4892,4858,4871 }, {33,1856,693 ,4891,4892,4871 }, + {1856,1457,1392 ,4892,4893,4858 }, {1457,228,1392 ,4893,4881,4858 }, + {3447,64,1940 ,3928,297,3925 }, {48,123,213 ,4840,3607,3606 }, + {5287,5286,5241 ,4003,3859,2120 }, {4144,261,1210 ,4761,4775,4739 }, + {1973,4685,4906 ,551,4894,4895 }, {5265,5287,5241 ,4882,4003,2120 }, + {5874,6090,6066 ,4896,4897,1455 }, {2390,1869,940 ,1594,2808,197 }, + {4158,1346,705 ,72,484,70 }, {701,1748,756 ,4685,4618,4731 }, + {3990,3254,4019 ,3231,3233,227 }, {1015,2536,409 ,3368,2793,1480 }, + {3549,588,3369 ,3823,3822,3836 }, {5724,5725,5762 ,4898,3824,4884 }, + {4246,4285,4284 ,3665,4899,4869 }, {4270,4246,4284 ,4900,3665,4869 }, + {4284,4285,4341 ,4869,4899,4888 }, {4341,4365,4387 ,4888,4901,4878 }, + {4387,4365,4416 ,4878,4901,4889 }, {4508,1738,1737 ,4902,4890,4861 }, + {4478,4508,1737 ,4870,4902,4861 }, {1738,986,942 ,4890,4903,4880 }, + {986,1493,942 ,4903,4904,4880 }, {942,1493,33 ,4880,4904,4891 }, + {285,1856,33 ,4905,4892,4891 }, {285,1457,1856 ,4905,4893,4892 }, + {285,228,1457 ,4905,4881,4893 }, {48,213,228 ,4840,3606,4881 }, + {2677,123,1050 ,3033,3607,2282 }, {642,413,739 ,1243,351,2233 }, + {3990,4353,4352 ,3231,4906,4695 }, {4101,209,1178 ,4724,4499,206 }, + {4313,1225,2423 ,4574,327,433 }, {653,208,1259 ,318,4742,348 }, + {1976,4351,2390 ,1180,4606,1594 }, {4351,1976,2390 ,4606,1180,1594 }, + {4351,801,2390 ,4606,4907,1594 }, {2390,801,1869 ,1594,4907,2808 }, + {4352,4353,4401 ,4695,4906,4772 }, {1946,180,131 ,4408,4777,2965 }, + {4539,93,113 ,633,563,677 }, {4835,8126,3148 ,2130,812,814 }, + {588,3368,3369 ,3822,3960,3836 }, {4285,4319,4341 ,4899,4908,4888 }, + {4341,4319,4365 ,4888,4908,4901 }, {4459,4508,4478 ,4909,4902,4870 }, + {4439,4459,4478 ,4879,4909,4870 }, {4508,4509,1738 ,4902,4910,4890 }, + {1738,4509,986 ,4890,4910,4903 }, {1493,598,33 ,4904,4911,4891 }, + {598,285,33 ,4911,4905,4891 }, {285,4143,228 ,4905,4912,4881 }, + {4143,4092,228 ,4912,4913,4881 }, {228,4092,48 ,4881,4913,4840 }, + {47,941,1049 ,4914,2277,1362 }, {48,47,1049 ,4840,4914,1362 }, + {45,2255,195 ,4714,3229,739 }, {1088,1623,1186 ,1604,1709,1605 }, + {142,466,143 ,159,347,157 }, {4367,4343,6225 ,228,1818,4872 }, + {1976,2006,4351 ,1180,1181,4606 }, {1869,363,4427 ,2808,427,426 }, + {2364,3343,2365 ,2513,2515,3351 }, {4243,447,317 ,4764,4651,561 }, + {340,960,730 ,1107,1326,1549 }, {5725,5763,5762 ,3824,4212,4884 }, + {5678,5679,5724 ,3861,2553,4898 }, {5522,5558,5557 ,755,2302,756 }, + {4246,4319,4285 ,3665,4908,4899 }, {4365,4417,4416 ,4901,4915,4889 }, + {4417,4459,4439 ,4915,4909,4879 }, {4416,4417,4439 ,4889,4915,4879 }, + {4460,4509,4508 ,4916,4910,4902 }, {4459,4460,4508 ,4909,4916,4902 }, + {986,599,1493 ,4903,4917,4904 }, {1493,599,598 ,4904,4917,4911 }, + {397,4092,4143 ,4918,4913,4912 }, {285,397,4143 ,4905,4918,4912 }, + {4092,4428,48 ,4913,4919,4840 }, {4428,47,48 ,4919,4914,4840 }, + {1834,1835,1870 ,2422,2421,4868 }, {4082,4081,500 ,4716,4709,4637 }, + {5418,5103,7917 ,3471,77,4920 }, {2255,4350,195 ,3229,740,739 }, + {987,3977,66 ,2950,440,4563 }, {2431,1511,967 ,2977,4921,3668 }, + {136,4495,4450 ,1685,4723,4722 }, {4496,136,4450 ,2220,1685,4722 }, + {4134,4079,658 ,4750,4718,4693 }, {1005,3818,3732 ,3535,1244,3536 }, + {3075,3981,3263 ,2670,915,252 }, {801,415,1869 ,4907,4922,2808 }, + {1869,415,363 ,2808,4922,427 }, {4091,3141,4126 ,123,4701,124 }, + {699,4079,4134 ,4677,4718,4750 }, {913,536,863 ,444,523,284 }, + {1343,4399,1564 ,378,4546,368 }, {443,265,697 ,920,4496,3578 }, + {5217,4763,4797 ,4026,4787,3984 }, {5679,5725,5724 ,2553,3824,4898 }, + {5308,5288,5289 ,3528,3993,80 }, {3837,4320,4319 ,3667,4923,4908 }, + {4246,3837,4319 ,3665,3667,4908 }, {4320,4366,4365 ,4923,4924,4901 }, + {4319,4320,4365 ,4908,4923,4901 }, {4365,4366,4417 ,4901,4924,4915 }, + {4418,4460,4459 ,4925,4916,4909 }, {4417,4418,4459 ,4915,4925,4909 }, + {4509,853,986 ,4910,4926,4903 }, {986,853,599 ,4903,4926,4917 }, + {598,397,285 ,4911,4918,4905 }, {4092,4168,4428 ,4913,4927,4919 }, + {4428,4168,47 ,4919,4927,4914 }, {4062,4082,500 ,4623,4716,4637 }, + {4159,1833,709 ,1299,317,316 }, {1117,5090,6418 ,4928,4929,3441 }, + {658,447,4243 ,4693,4651,4764 }, {223,196,157 ,1985,152,1877 }, + {1179,883,1601 ,4774,119,4785 }, {4399,1922,1564 ,4546,4432,368 }, + {1601,883,76 ,4785,119,689 }, {1307,4203,801 ,4930,4931,4907 }, + {801,4203,415 ,4907,4931,4922 }, {5340,5339,5286 ,4002,2223,3859 }, + {4302,166,4356 ,3727,3729,155 }, {4384,4385,1946 ,4409,4778,4408 }, + {545,739,46 ,305,2233,2232 }, {4110,467,1633 ,372,346,373 }, + {467,513,1633 ,346,377,373 }, {2153,164,3980 ,4181,4932,4933 }, + {3414,4528,2142 ,4934,4935,4936 }, {3837,4286,4320 ,3667,4274,4923 }, + {4366,4418,4417 ,4924,4925,4915 }, {4479,4524,4509 ,4937,4938,4910 }, + {4460,4479,4509 ,4916,4937,4910 }, {4509,4524,853 ,4910,4938,4926 }, + {599,750,598 ,4917,4939,4911 }, {598,750,397 ,4911,4939,4918 }, + {397,4168,4092 ,4918,4927,4913 }, {610,47,4168 ,4940,4914,4927 }, + {4282,941,47 ,4941,2277,4914 }, {610,4282,47 ,4940,4941,4914 }, + {4282,119,941 ,4941,2278,2277 }, {4054,4055,4058 ,3679,3678,4708 }, + {1528,1424,608 ,4559,2298,2300 }, {258,3014,259 ,3001,3568,2203 }, + {2006,1565,1307 ,1181,3737,4930 }, {1307,1565,4203 ,4930,3737,4931 }, + {415,380,363 ,4922,2540,427 }, {5287,5340,5286 ,4003,4002,3859 }, + {4109,4125,4126 ,576,125,124 }, {4058,1253,4065 ,4708,2353,4717 }, + {5103,5412,5378 ,77,360,359 }, {4298,4186,3616 ,3580,683,3974 }, + {4469,2153,3980 ,4060,4181,4933 }, {3009,5702,5701 ,3747,3630,3938 }, + {4366,4388,4418 ,4924,4942,4925 }, {4440,4479,4460 ,4943,4937,4916 }, + {4418,4440,4460 ,4925,4943,4916 }, {853,1563,599 ,4926,4944,4917 }, + {1563,1149,599 ,4944,4945,4917 }, {1149,1924,750 ,4945,4946,4939 }, + {599,1149,750 ,4917,4945,4939 }, {4219,4168,397 ,4947,4927,4918 }, + {657,4282,610 ,4948,4941,4940 }, {657,119,4282 ,4948,2278,4941 }, + {4081,4082,1079 ,4709,4716,4575 }, {4057,2959,2420 ,272,4608,273 }, + {5643,5659,5642 ,4617,3053,526 }, {4471,1653,258 ,2187,106,3001 }, + {1651,200,1609 ,188,244,31 }, {2742,1565,2006 ,2558,3737,1181 }, + {4203,796,415 ,4931,4949,4922 }, {415,796,380 ,4922,4949,2540 }, + {95,4522,1798 ,3482,3481,4451 }, {3604,3626,3654 ,3384,4196,1438 }, + {4451,4498,4497 ,271,88,91 }, {3727,4298,3616 ,3942,3580,3974 }, + {531,4469,3980 ,3829,4060,4933 }, {4320,4342,4366 ,4923,4950,4924 }, + {4342,4389,4388 ,4950,4951,4942 }, {4366,4342,4388 ,4924,4950,4942 }, + {4389,4440,4418 ,4951,4943,4925 }, {4388,4389,4418 ,4942,4951,4925 }, + {4524,1736,853 ,4938,4952,4926 }, {1736,773,853 ,4952,4953,4926 }, + {773,1563,853 ,4953,4944,4926 }, {773,1149,1563 ,4953,4945,4944 }, + {1924,98,397 ,4946,4954,4918 }, {750,1924,397 ,4939,4946,4918 }, + {98,769,397 ,4954,4955,4918 }, {769,4219,397 ,4955,4947,4918 }, + {769,4168,4219 ,4955,4927,4947 }, {656,610,4168 ,4956,4940,4927 }, + {656,657,610 ,4956,4948,4940 }, {2959,2419,2420 ,4608,3353,273 }, + {918,143,416 ,4150,157,556 }, {4019,4300,3990 ,227,231,3231 }, + {3626,3655,3654 ,4196,3936,1438 }, {4300,4301,3990 ,231,167,3231 }, + {1565,1972,4203 ,3737,3034,4931 }, {4203,1972,796 ,4931,3034,4949 }, + {796,1808,380 ,4949,4502,2540 }, {439,1806,1346 ,4638,3124,484 }, + {8088,6837,8100 ,1998,1057,1713 }, {463,360,277 ,1941,86,85 }, + {4301,4354,4353 ,167,376,4906 }, {3990,4301,4353 ,3231,167,4906 }, + {4354,4402,4401 ,376,2190,4772 }, {1869,4427,1839 ,2808,426,1138 }, + {792,4536,4712 ,95,89,3816 }, {4655,4681,4654 ,4957,4958,4348 }, + {5377,4904,5378 ,361,3309,359 }, {34,531,3980 ,3830,3829,4933 }, + {4342,4320,4286 ,4950,4923,4274 }, {3371,1941,3265 ,3708,1034,1033 }, + {4510,4524,4479 ,4959,4938,4937 }, {4510,1719,1736 ,4959,4960,4952 }, + {4524,4510,1736 ,4938,4959,4952 }, {1719,773,1736 ,4960,4953,4952 }, + {1149,98,1924 ,4945,4954,4946 }, {4169,4168,769 ,4961,4927,4955 }, + {4169,605,4168 ,4961,4962,4927 }, {605,656,4168 ,4962,4956,4927 }, + {657,26,119 ,4948,2126,2278 }, {26,460,119 ,2126,1999,2278 }, + {643,2602,546 ,3285,3234,1171 }, {466,4103,604 ,347,4577,486 }, + {66,2697,1211 ,4563,2006,2007 }, {478,613,64 ,2077,298,297 }, + {4353,4354,4401 ,4906,376,4772 }, {1565,511,1972 ,3737,3569,3034 }, + {453,301,82 ,1814,1857,138 }, {1972,1948,796 ,3034,2543,4949 }, + {796,1948,1808 ,4949,2543,4502 }, {915,916,863 ,443,445,284 }, + {4473,4450,4401 ,270,4722,4772 }, {4402,4473,4401 ,2190,270,4772 }, + {6679,1117,6418 ,4824,4928,3441 }, {4298,3727,5832 ,3580,3942,3904 }, + {3673,52,164 ,648,411,4932 }, {3294,3292,3293 ,3895,4067,1988 }, + {4461,4479,4440 ,4963,4937,4943 }, {4461,4511,4510 ,4963,4964,4959 }, + {4479,4461,4510 ,4937,4963,4959 }, {4511,1719,4510 ,4964,4960,4959 }, + {560,98,1149 ,4965,4954,4945 }, {769,1209,4169 ,4955,279,4961 }, + {4169,1209,605 ,4961,279,4962 }, {656,605,657 ,4956,4962,4948 }, + {1347,26,657 ,4966,2126,4948 }, {1427,1971,4740 ,2378,2520,2426 }, + {2504,2461,2583 ,2898,2899,2672 }, {4473,4496,4450 ,270,2220,4722 }, + {1972,1349,1948 ,3034,2519,2543 }, {103,84,2030 ,128,4231,770 }, + {1840,1869,1839 ,198,2808,1138 }, {136,883,1179 ,1685,119,4774 }, + {5679,5697,5725 ,2553,2552,3824 }, {2153,3673,164 ,4181,648,4932 }, + {7648,7697,7647 ,3952,651,4967 }, {4420,4440,4389 ,276,4943,4951 }, + {4419,4420,4389 ,4968,276,4951 }, {4420,4461,4440 ,276,4963,4943 }, + {594,1149,773 ,2453,4945,4953 }, {1149,594,560 ,4945,2453,4965 }, + {1209,769,98 ,279,4955,4954 }, {605,1347,657 ,4962,4966,4948 }, + {1919,1004,1452 ,1779,3452,1606 }, {1832,66,1306 ,4576,4563,4653 }, + {3569,2660,538 ,4381,455,4380 }, {3759,21,2030 ,4233,1231,770 }, + {1948,1427,1808 ,2543,2378,4502 }, {1808,1427,4111 ,4502,2378,4687 }, + {643,546,644 ,3285,1171,1176 }, {4111,1870,1871 ,4687,4868,2458 }, + {4126,4198,4175 ,124,4969,4500 }, {5389,1206,1734 ,4091,2218,2219 }, + {5784,4011,878 ,396,395,398 }, {5814,5831,5830 ,4875,3943,4163 }, + {3892,3905,6166 ,4970,4194,4971 }, {5798,5814,5830 ,4876,4875,4163 }, + {4343,4389,4342 ,1818,4951,4950 }, {4343,4420,4419 ,1818,276,4968 }, + {4389,4343,4419 ,4951,1818,4968 }, {893,773,1719 ,2454,4953,4960 }, + {893,594,773 ,2454,2453,4953 }, {98,1069,1209 ,4954,277,279 }, + {605,44,1347 ,4962,357,4966 }, {1347,70,26 ,4966,323,2126 }, + {66,45,1306 ,4563,4714,4653 }, {3668,959,8070 ,513,1358,941 }, + {4723,1676,4895 ,4972,2343,4973 }, {1049,1050,123 ,1362,2282,3607 }, + {1015,409,89 ,3368,1480,1482 }, {4111,4327,1870 ,4687,2377,4868 }, + {4175,4198,3784 ,4500,4969,2252 }, {2208,3801,5090 ,1257,4974,4929 }, + {1117,2208,5090 ,4928,1257,4929 }, {5389,1734,3113 ,4091,2219,410 }, + {4185,5389,3113 ,409,4091,410 }, {2960,5941,808 ,4975,4976,4734 }, + {4390,4420,4343 ,230,276,1818 }, {4512,4511,4461 ,2279,4964,4963 }, + {4511,4512,1719 ,4964,2279,4960 }, {4512,893,1719 ,2279,2454,4960 }, + {560,971,98 ,4965,356,4954 }, {98,971,1069 ,4954,356,277 }, {4138,44,605 ,278,357,4962 }, + {1209,4138,605 ,279,278,4962 }, {44,1793,1347 ,357,319,4966 }, + {1793,69,1347 ,319,321,4966 }, {1347,69,70 ,4966,321,323 }, {6836,3534,6353 ,852,942,749 }, + {5657,5680,5640 ,48,50,2214 }, {4056,4098,4263 ,1123,353,274 }, + {1846,1148,705 ,204,160,70 }, {844,1115,1114 ,4688,2257,2256 }, + {1115,1600,2047 ,2257,4498,2258 }, {4969,4992,1030 ,4977,4978,1619 }, + {356,1112,4194 ,310,292,293 }, {3346,3617,3375 ,4457,4425,4426 }, + {4329,4380,4379 ,1247,4602,567 }, {1427,4327,4111 ,2378,2377,4687 }, + {4327,4099,1870 ,2377,4533,4868 }, {4197,4196,4148 ,82,4747,2305 }, + {5412,5411,5377 ,360,4284,361 }, {4462,4461,4420 ,1076,4963,276 }, + {4462,4512,4461 ,1076,2279,4963 }, {1057,560,594 ,354,4965,2453 }, + {871,1057,594 ,2106,354,2453 }, {1057,971,560 ,354,356,4965 }, + {4836,137,4783 ,4979,4980,4981 }, {4833,4848,4822 ,4982,4983,4984 }, + {4687,274,4637 ,4985,1819,4986 }, {4176,4127,5163 ,4703,4702,4987 }, + {7374,7373,7348 ,4988,4989,4990 }, {4857,4873,509 ,4991,4992,4993 }, + {4886,1602,361 ,4720,2329,4994 }, {5030,5031,5047 ,2134,4995,4996 }, + {4688,4748,4687 ,4997,4998,4985 }, {574,616,4874 ,2181,4999,5000 }, + {4657,4716,4656 ,5001,1030,5002 }, {3801,3660,7052 ,4974,709,708 }, + {996,3784,4176 ,5003,2252,4703 }, {4614,4692,279 ,3109,2177,2176 }, + {2208,5088,5393 ,1257,5004,5005 }, {4716,4715,4656 ,1030,1029,5002 }, + {4657,4656,4605 ,5001,5002,5006 }, {4751,4880,417 ,5007,5008,5009 }, + {5016,561,4644 ,5010,2379,2425 }, {4644,561,4740 ,2425,2379,2426 }, + {8122,8114,8145 ,384,2286,3376 }, {3801,2208,5393 ,4974,1257,5005 }, + {4614,2322,4692 ,3109,3497,2177 }, {707,669,612 ,2091,2020,5011 }, + {1750,1751,1811 ,745,773,777 }, {1511,4377,967 ,4921,3090,3668 }, + {4935,4936,4973 ,5012,5013,5014 }, {3261,6554,4190 ,5015,3647,3439 }, + {8081,8078,8093 ,1445,1744,2288 }, {4604,4603,4562 ,5016,5017,5018 }, + {811,718,4954 ,557,2407,558 }, {4804,4833,4822 ,4867,4982,4984 }, + {4974,4996,4995 ,5019,5020,5021 }, {1264,1252,3929 ,5022,5023,5024 }, + {8091,8101,8099 ,2167,383,2867 }, {4817,1771,1834 ,2368,2367,2422 }, + {5350,6529,6507 ,1526,5025,2680 }, {4848,4886,417 ,4983,4720,5009 }, + {618,717,677 ,2391,2408,2296 }, {2301,8107,872 ,2132,658,2962 }, + {4749,4810,4785 ,5026,5027,5028 }, {4809,4855,4782 ,4202,4201,5029 }, + {4656,4715,4681 ,5002,1029,4958 }, {4686,4492,4773 ,5030,5031,4865 }, + {7593,7620,7645 ,5032,5033,5034 }, {4683,4647,4643 ,5035,5036,5037 }, + {5049,1928,4859 ,5038,1282,5039 }, {6942,6943,6962 ,521,520,3260 }, + {4871,1834,1870 ,5040,2422,4868 }, {4907,4935,4934 ,2209,5012,5041 }, + {8064,2341,1333 ,3141,845,849 }, {4954,720,812 ,558,2475,559 }, + {8089,2746,8139 ,254,1517,1923 }, {5063,5064,5062 ,5042,5043,5044 }, + {4607,4659,4658 ,5045,5046,5047 }, {4976,4998,4997 ,5048,4713,5049 }, + {4659,319,4658 ,5046,5050,5047 }, {319,367,4717 ,5050,1370,1449 }, + {4658,319,4717 ,5047,5050,1449 }, {8071,8144,3264 ,738,2469,2468 }, + {8127,8142,4106 ,2970,1743,1742 }, {4690,4721,383 ,5051,5052,2087 }, + {4907,576,674 ,2209,2208,2271 }, {717,4938,677 ,2408,5053,2296 }, + {2476,1283,2431 ,2441,2978,2977 }, {5966,1455,4987 ,5054,1917,1491 }, + {1346,2464,1697 ,484,3299,69 }, {1610,1676,749 ,2330,2343,2331 }, + {4934,4972,851 ,5041,2158,2155 }, {1610,1602,4855 ,2330,2329,4201 }, + {6755,6792,6738 ,5055,5056,5057 }, {1536,1581,1508 ,2080,2030,2029 }, + {4951,717,4977 ,5058,2408,4711 }, {6390,6311,6316 ,3868,1097,4435 }, + {565,4869,564 ,1614,5059,3529 }, {4890,4889,564 ,4594,4615,3529 }, + {4869,4890,564 ,5059,4594,3529 }, {417,137,4836 ,5009,4980,4979 }, + {1077,1030,1078 ,2694,1619,5060 }, {4286,3528,4342 ,4274,5061,4950 }, + {361,4751,417 ,4994,5007,5009 }, {5098,5097,5065 ,5062,5063,5064 }, + {1870,4099,4871 ,4868,4533,5040 }, {4918,4967,4966 ,4595,5065,4646 }, + {4931,4964,937 ,5066,783,5067 }, {4930,4931,937 ,5068,5066,5067 }, + {1320,4667,4618 ,2356,5069,5070 }, {2271,6309,6308 ,5071,3690,3474 }, + {4966,4967,978 ,4646,5065,1620 }, {4433,4356,4404 ,4591,155,5072 }, + {1831,1830,1768 ,2221,2110,2109 }, {7185,7184,7159 ,5073,5074,5075 }, + {4795,4432,4433 ,3710,156,4591 }, {1879,1928,5049 ,1283,1282,5038 }, + {4972,4994,949 ,2158,5076,2133 }, {468,469,505 ,1369,988,986 }, + {1024,4686,4647 ,5077,5030,5036 }, {8105,8090,3006 ,3791,412,414 }, + {4967,4991,978 ,5065,1621,1620 }, {446,640,4959 ,501,5078,5079 }, + {7565,7620,7593 ,5080,5033,5032 }, {4951,4977,4976 ,5058,4711,5048 }, + {3609,6159,6144 ,5081,5082,5083 }, {761,845,760 ,1826,1828,3106 }, + {6484,2258,5455 ,685,1198,686 }, {561,4871,4099 ,2379,5040,4533 }, + {4018,2271,6308 ,5084,5071,3474 }, {2808,6606,2454 ,522,5085,1345 }, + {567,568,663 ,1684,1723,4773 }, {2089,2090,2619 ,1325,1324,3035 }, + {7253,7254,6346 ,1855,450,4883 }, {5949,404,5967 ,5086,761,1352 }, + {1164,6386,1298 ,5087,4085,5088 }, {1635,1636,1702 ,989,1036,1039 }, + {5094,4683,1365 ,5089,5035,2085 }, {1699,1752,1751 ,772,946,773 }, + {4615,1566,1635 ,5090,1037,989 }, {1494,4609,1973 ,1083,5091,551 }, + {5017,5292,4144 ,5092,5093,4761 }, {1442,4615,1635 ,990,5090,989 }, + {6322,6897,2249 ,3531,54,3751 }, {4536,4820,4712 ,89,2399,3816 }, + {4452,4536,4498 ,2398,89,88 }, {4989,307,4986 ,5094,5095,5096 }, + {4606,4607,4658 ,5097,5045,5047 }, {2067,2031,2005 ,1592,1560,1593 }, + {1676,4817,4895 ,2343,2368,4973 }, {4782,4667,1397 ,5029,5069,2358 }, + {1028,406,2958 ,1373,2566,1374 }, {6644,5056,4950 ,5098,1300,1302 }, + {1365,1282,1317 ,2085,2097,5099 }, {3534,6686,8121 ,942,750,3466 }, + {1169,4607,4606 ,5100,5045,5097 }, {4647,4773,4661 ,5036,4865,5101 }, + {2015,1994,1995 ,759,5102,757 }, {7287,7310,7286 ,3459,4283,3460 }, + {951,5033,4999 ,2394,5103,4712 }, {2155,875,6353 ,580,579,749 }, + {4874,616,673 ,5000,4999,2210 }, {1602,749,4723 ,2329,2331,4972 }, + {1365,4757,4666 ,2085,5104,5105 }, {7613,7612,7551 ,5106,5107,5108 }, + {2650,8140,3981 ,914,2269,915 }, {8120,6897,3286 ,906,54,56 }, + {4594,4027,5028 ,688,3470,5109 }, {4844,4848,4833 ,4866,4983,4982 }, + {4594,146,4027 ,688,504,3470 }, {4996,4997,5030 ,5020,5049,2134 }, + {5030,5047,5061 ,2134,4996,2135 }, {4594,5028,3249 ,688,5109,5110 }, + {792,147,146 ,95,3469,504 }, {4688,4689,4749 ,4997,5111,5026 }, + {128,4594,4905 ,687,688,5112 }, {5036,128,4905 ,399,687,5112 }, + {1202,366,1040 ,339,4469,1476 }, {4615,1442,4806 ,5090,990,5113 }, + {5127,4801,124 ,5114,5115,2206 }, {128,5036,4905 ,687,399,5112 }, + {3243,128,4905 ,3919,687,5112 }, {616,574,575 ,4999,2181,2180 }, + {7556,6760,4636 ,5116,5117,5118 }, {4997,4998,5018 ,5049,4713,5119 }, + {1105,1202,1040 ,2049,339,1476 }, {7372,7371,7310 ,4373,3748,4283 }, + {1649,1581,1582 ,2031,2030,2081 }, {7204,7241,7203 ,5120,4465,4366 }, + {5910,5838,6343 ,5121,3987,5122 }, {1442,4898,4806 ,990,5123,5113 }, + {1099,3243,4905 ,4350,3919,5112 }, {695,128,3243 ,731,687,3919 }, + {7572,7599,7571 ,5124,5125,5126 }, {4734,4661,4902 ,5127,5101,5128 }, + {135,4637,274 ,2653,4986,1819 }, {4817,1834,4871 ,2368,2422,5040 }, + {7823,7874,7847 ,345,344,3521 }, {7452,7509,7451 ,1875,1874,3963 }, + {137,4762,4843 ,4980,5129,5130 }, {4615,4806,4670 ,5090,5113,5131 }, + {1659,4615,4670 ,5132,5090,5131 }, {8103,1724,8077 ,1291,3880,2078 }, + {5091,5090,7052 ,5133,4929,708 }, {126,197,4659 ,1394,1393,5046 }, + {4607,126,4659 ,5045,1394,5046 }, {4659,197,319 ,5046,1393,5050 }, + {2006,1307,4351 ,1181,4930,4606 }, {4751,4821,4880 ,5007,5134,5008 }, + {4870,4871,5016 ,5135,5040,5010 }, {5061,5093,1317 ,2135,5136,5099 }, + {4838,4808,311 ,245,5137,246 }, {4880,4762,137 ,5008,5129,4980 }, + {760,4970,4920 ,3106,3073,5138 }, {5021,4644,1969 ,2383,2425,2369 }, + {856,811,855 ,5139,557,2392 }, {1768,4735,1831 ,2109,5140,2221 }, + {4869,565,566 ,5059,1614,1613 }, {4843,1718,4783 ,5130,2108,4981 }, + {662,4890,4869 ,5141,4594,5059 }, {4683,4643,4757 ,5035,5037,5104 }, + {324,325,383 ,2090,2088,2087 }, {917,4915,4929 ,4016,64,5142 }, + {4667,4782,4492 ,5069,5029,5031 }, {753,3243,4845 ,4749,3919,3921 }, + {753,462,3243 ,4749,4847,3919 }, {753,695,462 ,4749,731,4847 }, + {280,326,325 ,2175,2152,2088 }, {4976,4977,4998 ,5048,4711,4713 }, + {4566,4571,1168 ,5143,1342,1098 }, {2583,685,3902 ,2672,2674,4437 }, + {5187,4845,1111 ,5144,3921,3920 }, {6689,3285,2405 ,3065,3064,4165 }, + {1790,4183,240 ,4738,1572,1571 }, {1040,1104,1105 ,1476,1473,2049 }, + {4985,4566,1168 ,2703,5143,1098 }, {4643,4818,4734 ,5037,5145,5127 }, + {7815,5439,5440 ,5146,4157,5147 }, {5093,1365,1317 ,5136,2085,5099 }, + {1177,1130,5061 ,5148,2136,2135 }, {4994,5030,5029 ,5076,2134,2159 }, + {4751,4862,4821 ,5007,5149,5134 }, {1517,4780,4846 ,600,5150,4237 }, + {4989,972,4571 ,5094,3277,1342 }, {4984,753,4845 ,5151,4749,3921 }, + {4750,4786,4749 ,5152,5153,5026 }, {4818,4661,4734 ,5145,5101,5127 }, + {4638,4663,4637 ,5154,5155,4986 }, {5750,5739,5775 ,5156,5157,5158 }, + {6463,4989,4566 ,5159,5094,5143 }, {6704,6736,6703 ,5160,1622,5161 }, + {5629,5938,6605 ,5162,5163,5164 }, {383,4811,4786 ,2087,5165,5153 }, + {4690,4689,4639 ,5051,5111,5166 }, {4740,561,1427 ,2426,2379,2378 }, + {4566,4989,4571 ,5143,5094,1342 }, {1024,5071,4686 ,5077,5167,5030 }, + {4997,5031,5030 ,5049,4995,2134 }, {5031,5032,5064 ,4995,5168,5043 }, + {557,82,4801 ,3724,138,5115 }, {7693,3824,8133 ,724,3462,2131 }, + {4824,4812,1602 ,4719,5169,2329 }, {760,4920,663 ,3106,5138,4773 }, + {4973,4974,4995 ,5014,5019,5021 }, {4690,4749,4689 ,5051,5026,5111 }, + {4751,5015,4862 ,5007,5170,5149 }, {1282,1176,1317 ,2097,2096,5099 }, + {4855,4812,4782 ,4201,5169,5029 }, {4850,1931,1903 ,5171,4736,406 }, + {8127,8084,8142 ,2970,1139,1743 }, {2527,2487,3426 ,2637,4427,4438 }, + {4605,4606,4658 ,5006,5097,5047 }, {3762,5044,5442 ,5172,5173,5174 }, + {674,675,4936 ,2271,2294,5013 }, {4331,4984,4845 ,4645,5151,3921 }, + {5083,4331,4845 ,5175,4645,3921 }, {4331,753,4984 ,4645,4749,5151 }, + {5246,6039,5227 ,5176,2728,4097 }, {4862,4850,1831 ,5149,5171,2221 }, + {417,4880,137 ,5009,5008,4980 }, {4811,514,4858 ,5165,2154,5177 }, + {4929,4930,4963 ,5142,5068,5178 }, {5844,1119,1028 ,5179,5180,1373 }, + {7064,7065,2073 ,5181,5182,5183 }, {5056,3319,5005 ,1300,1259,1301 }, + {6570,4554,1263 ,1252,1307,1199 }, {4870,1931,4862 ,5135,4736,5149 }, + {4643,4734,1508 ,5037,5127,2029 }, {1884,1885,1929 ,3567,778,899 }, + {855,951,4977 ,2392,2394,4711 }, {4986,1073,972 ,5096,3741,3277 }, + {1169,4606,4605 ,5100,5097,5006 }, {4689,4688,4638 ,5111,4997,5154 }, + {1252,5999,3929 ,5023,5184,5024 }, {4989,4986,972 ,5094,5096,3277 }, + {4889,4933,4932 ,4615,4596,4580 }, {4688,4687,4638 ,4997,4985,5154 }, + {5096,5097,4912 ,5185,5063,5186 }, {5071,4912,4686 ,5167,5186,5030 }, + {564,4889,4868 ,3529,4615,3969 }, {675,677,4937 ,2294,2296,5187 }, + {5033,5066,5065 ,5103,5188,5064 }, {5034,5033,951 ,2396,5103,2394 }, + {897,898,952 ,2393,2411,2395 }, {1672,1650,1582 ,2083,2082,2081 }, + {4845,5052,5083 ,3921,5189,5175 }, {4118,753,4331 ,4699,4749,4645 }, + {5702,4743,5701 ,3630,3629,3938 }, {4812,4824,4492 ,5169,4719,5031 }, + {673,4934,767 ,2210,5041,2093 }, {1536,4734,1582 ,2080,5127,2081 }, + {17,961,4959 ,5190,500,5079 }, {1659,4670,4685 ,5132,5131,4894 }, + {4644,5021,5019 ,2425,2383,2384 }, {4783,1672,4902 ,4981,2083,5128 }, + {7646,7675,7645 ,5191,5192,5034 }, {1381,1169,4605 ,5193,5100,5006 }, + {5061,1317,1177 ,2135,5099,5148 }, {4933,4966,4965 ,4596,4646,784 }, + {206,3347,1804 ,3932,2098,2902 }, {4888,4889,4932 ,3997,4615,4580 }, + {4932,4933,4965 ,4580,4596,784 }, {4889,4888,4868 ,4615,3997,3969 }, + {4986,5441,42 ,5096,5194,5195 }, {1285,4618,5097 ,1944,5070,5063 }, + {4716,4717,468 ,1030,1449,1369 }, {4997,5018,5031 ,5049,5119,4995 }, + {1073,4986,42 ,3741,5096,5195 }, {1297,4752,1488 ,4048,5196,5197 }, + {280,325,279 ,2175,2088,2176 }, {4817,1676,1720 ,2368,2343,2366 }, + {1213,1197,4983 ,3700,478,480 }, {4331,5083,5052 ,4645,5175,5189 }, + {8132,8141,3900 ,413,3295,884 }, {943,1034,1081 ,1807,1756,3745 }, + {1931,1967,1903 ,4736,404,406 }, {4563,126,4607 ,3024,1394,5045 }, + {1169,4563,4607 ,5100,3024,5045 }, {4969,4970,4992 ,4977,3073,4978 }, + {6686,8070,959 ,750,941,1358 }, {4898,4867,4806 ,5123,5198,5113 }, + {1365,4683,4757 ,2085,5035,5104 }, {3482,4021,8106 ,2494,1712,2438 }, + {977,976,4965 ,3931,782,784 }, {4618,4667,4912 ,5070,5069,5186 }, + {4695,4725,4772 ,5199,5200,5201 }, {4935,4973,4972 ,5012,5014,2158 }, + {509,572,475 ,4993,1947,1884 }, {4938,4976,4975 ,5053,5048,5202 }, + {4848,417,4836 ,4983,5009,4979 }, {4749,4811,4810 ,5026,5165,5027 }, + {4974,4975,4996 ,5019,5202,5020 }, {5033,5065,5064 ,5103,5064,5043 }, + {5032,5033,5064 ,5168,5103,5043 }, {3316,2810,3271 ,2149,1728,1727 }, + {7512,7570,7536 ,5203,5204,5205 }, {4018,6308,6307 ,5084,3474,5206 }, + {1381,4604,4562 ,5193,5016,5018 }, {375,374,320 ,1883,1821,1871 }, + {3824,7714,8131 ,3462,4468,2307 }, {4661,4822,4902 ,5101,4984,5128 }, + {4972,4973,4994 ,2158,5014,5076 }, {4973,4995,4994 ,5014,5021,5076 }, + {7693,8133,8071 ,724,2131,738 }, {4811,4858,4840 ,5165,5177,5207 }, + {2322,281,4692 ,3497,2227,2177 }, {4118,4331,513 ,4699,4645,377 }, + {4810,509,475 ,5027,4993,1884 }, {4640,4691,4690 ,5208,2089,5051 }, + {442,357,589 ,673,672,671 }, {1442,1699,4898 ,990,772,5123 }, + {2219,5000,4982 ,177,286,364 }, {4611,4634,4754 ,5209,5210,5211 }, + {6299,5012,1989 ,760,5212,3016 }, {4782,1469,4809 ,5029,2361,4202 }, + {4619,39,344 ,5213,529,562 }, {4754,4959,4903 ,5211,5079,5214 }, + {4903,4959,640 ,5214,5079,5078 }, {4780,1291,4819 ,5150,5215,5216 }, + {4684,5082,6603 ,5217,5218,3985 }, {5166,5205,5227 ,3427,5219,4097 }, + {1154,1922,4399 ,4545,4432,4546 }, {4858,514,574 ,5177,2154,2181 }, + {3243,462,695 ,3919,4847,731 }, {1635,1702,1699 ,989,1039,772 }, + {4780,1517,5079 ,5150,600,5220 }, {5079,1517,833 ,5220,600,489 }, + {2464,1346,2465 ,3299,484,3181 }, {662,4919,4918 ,5141,5221,4595 }, + {4890,662,4918 ,4594,5141,4595 }, {4919,4968,4967 ,5221,5222,5065 }, + {4918,4919,4967 ,4595,5221,5065 }, {4968,1030,4991 ,5222,1619,1621 }, + {4840,574,4874 ,5207,2181,5000 }, {1136,1042,1043 ,2412,2409,2478 }, + {4967,4968,4991 ,5065,5222,1621 }, {4806,4867,4621 ,5113,5198,5223 }, + {4867,797,4621 ,5198,5224,5223 }, {4749,4785,4748 ,5026,5028,4998 }, + {273,135,274 ,1749,2653,1819 }, {559,4958,4955 ,5225,5226,5227 }, + {4693,559,4955 ,5228,5225,5227 }, {5012,6299,6417 ,5212,760,762 }, + {4840,4858,574 ,5207,5177,2181 }, {4725,4726,4729 ,5200,5229,5230 }, + {4772,4725,4729 ,5201,5200,5230 }, {1291,4772,4819 ,5215,5201,5216 }, + {4812,4855,1602 ,5169,4201,2329 }, {307,6659,4986 ,5095,3577,5096 }, + {17,438,961 ,5190,498,500 }, {4492,4824,4844 ,5031,4719,4866 }, + {1971,1427,1948 ,2520,2378,2543 }, {4898,1698,4867 ,5123,747,5198 }, + {1698,5084,4867 ,747,5231,5198 }, {1810,4698,4894 ,779,1578,746 }, + {1884,1950,1995 ,3567,786,757 }, {4609,4615,1659 ,5091,5090,5132 }, + {2258,2376,1263 ,1198,1251,1199 }, {5093,5094,1365 ,5136,5089,2085 }, + {4785,4810,4748 ,5028,5027,4998 }, {4750,4749,4690 ,5152,5026,5051 }, + {1081,5046,1078 ,3745,5232,5060 }, {4938,4951,4976 ,5053,5058,5048 }, + {5050,1922,1154 ,5233,4432,4545 }, {936,4963,937 ,5234,5178,5067 }, + {4748,375,4687 ,4998,1883,4985 }, {2212,2271,5001 ,476,5071,477 }, + {5944,3562,6037 ,4754,4753,5235 }, {1041,5067,5033 ,2410,1945,5103 }, + {4560,4600,4559 ,5236,4360,5237 }, {579,677,676 ,2324,2296,2295 }, + {7543,7605,7542 ,5238,5239,5240 }, {5412,5418,5411 ,360,3471,4284 }, + {4819,4772,4701 ,5216,5201,5241 }, {2808,1920,6606 ,522,581,5085 }, + {6699,6751,6750 ,630,5242,631 }, {5442,4672,3762 ,5174,2682,5172 }, + {5004,4817,4871 ,5243,2368,5040 }, {197,367,319 ,1393,1370,5050 }, + {5096,4912,1024 ,5185,5186,5077 }, {4723,4895,4751 ,4972,4973,5007 }, + {5016,1931,4870 ,5010,4736,5135 }, {4611,17,4634 ,5209,5190,5210 }, + {4910,4923,4611 ,5244,5245,5209 }, {4923,4945,4611 ,5245,5246,5209 }, + {975,937,976 ,4266,5067,782 }, {833,1517,491 ,489,600,487 }, + {1081,1078,1030 ,3745,5060,1619 }, {4843,4735,1718 ,5130,5140,2108 }, + {4239,4884,85 ,2254,2253,2504 }, {505,5308,5290 ,986,3528,5247 }, + {2322,4614,4590 ,3497,3109,1490 }, {2277,2322,4590 ,3522,3497,1490 }, + {4641,4640,4612 ,5248,5208,5249 }, {4176,4198,4126 ,4703,4969,124 }, + {357,1198,589 ,672,608,671 }, {6570,2376,6562 ,1252,1251,1306 }, + {4670,4621,5073 ,5131,5223,5250 }, {4761,640,4847 ,5251,5078,5252 }, + {5709,5710,6957 ,5253,5254,4315 }, {2224,406,2206 ,2660,2566,176 }, + {2219,4982,4978 ,177,364,178 }, {2814,4922,4636 ,5255,5256,5118 }, + {5067,5066,5033 ,1945,5188,5103 }, {4772,4729,4701 ,5201,5230,5241 }, + {2163,4586,158 ,243,5257,5258 }, {4692,281,280 ,2177,2227,2175 }, + {4727,4667,4492 ,5259,5069,5031 }, {4746,1264,5078 ,5260,5022,5261 }, + {1285,5098,5066 ,1944,5062,5188 }, {4686,4727,4492 ,5030,5259,5031 }, + {951,4999,4977 ,2394,4712,4711 }, {4932,4965,4964 ,4580,784,783 }, + {4948,4962,935 ,5262,5263,5264 }, {1541,1610,4855 ,2363,2330,4201 }, + {2252,3247,144 ,590,1494,853 }, {6342,1749,1492 ,5265,555,1585 }, + {1028,1119,406 ,1373,5180,2566 }, {4923,638,4945 ,5245,495,5246 }, + {7603,7683,7655 ,5266,5267,5268 }, {4647,4686,4773 ,5036,5030,4865 }, + {898,897,811 ,2411,2393,557 }, {4773,4492,4844 ,4865,5031,4866 }, + {4850,4862,1931 ,5171,5149,4736 }, {4661,4804,4822 ,5101,4867,4984 }, + {4762,4735,4843 ,5129,5140,5130 }, {6751,6787,6786 ,5242,5269,4139 }, + {4640,4641,4691 ,5208,5248,2089 }, {281,326,280 ,2227,2152,2175 }, + {2376,3898,6562 ,1251,1400,1306 }, {6349,6358,6357 ,705,5270,5271 }, + {1469,1541,4855 ,2361,2363,4201 }, {4792,4726,5002 ,5272,5229,5273 }, + {5047,5062,5061 ,4996,5044,2135 }, {2093,603,1551 ,553,5274,5275 }, + {897,855,811 ,2393,2392,557 }, {4620,4619,4823 ,5276,5213,5277 }, + {4621,797,4925 ,5223,5224,5278 }, {4749,4748,4688 ,5026,4998,4997 }, + {1990,5520,5059 ,1305,1304,1253 }, {7105,7104,7092 ,5279,5280,3968 }, + {5067,1285,5066 ,1945,1944,5188 }, {4821,4862,4762 ,5134,5149,5129 }, + {4869,566,662 ,5059,1613,5141 }, {4879,4910,4941 ,5281,5244,5282 }, + {4910,4611,4941 ,5244,5209,5282 }, {4726,4725,4700 ,5229,5200,5283 }, + {4867,4789,797 ,5198,5284,5224 }, {4894,4698,4532 ,746,1578,5285 }, + {4945,638,4611 ,5246,495,5209 }, {4844,4833,4804 ,4866,4982,4867 }, + {1566,4615,4609 ,1037,5090,5091 }, {4934,4935,4972 ,5041,5012,2158 }, + {1717,1672,1718 ,2033,2083,2108 }, {4293,6577,8065 ,2732,97,96 }, + {612,4893,707 ,5011,5286,2091 }, {4650,4649,4595 ,5287,5288,5289 }, + {4596,4650,4595 ,1629,5287,5289 }, {4920,4969,4968 ,5138,4977,5222 }, + {4703,4702,4649 ,5290,5291,5288 }, {4690,383,4750 ,5051,2087,5152 }, + {4992,4993,1030 ,4978,5292,1619 }, {7311,7372,7310 ,4334,4373,4283 }, + {4685,4670,4906 ,4894,5131,4895 }, {4787,4729,4792 ,5293,5230,5272 }, + {4608,4925,4585 ,5294,5278,3955 }, {4726,4700,5002 ,5229,5283,5273 }, + {4925,4593,4585 ,5278,5295,3955 }, {4592,4927,4732 ,5296,5297,5298 }, + {4925,4732,4593 ,5278,5298,5295 }, {4761,4619,4620 ,5251,5213,5276 }, + {4789,4592,4732 ,5284,5296,5298 }, {6308,6711,6307 ,3474,3473,5206 }, + {7185,7220,7184 ,5073,4367,5074 }, {833,4695,4772 ,489,5199,5201 }, + {1025,6714,6389 ,5299,5300,5301 }, {4806,4621,4670 ,5113,5223,5131 }, + {5950,8066,8108 ,2873,960,2287 }, {5866,3311,5864 ,5302,5303,5304 }, + {4609,1659,1973 ,5091,5132,551 }, {4611,638,17 ,5209,495,5190 }, + {1494,1566,4609 ,1083,1037,5091 }, {4667,4727,5068 ,5069,5259,5305 }, + {7524,4596,4595 ,1630,1629,5289 }, {4674,4649,4650 ,5306,5288,5287 }, + {4674,4703,4649 ,5306,5290,5288 }, {4848,4783,4822 ,4983,4981,4984 }, + {5062,5064,5094 ,5044,5043,5089 }, {4930,937,4963 ,5068,5067,5178 }, + {3621,4980,158 ,625,241,5258 }, {5082,6744,6603 ,5218,3986,3985 }, + {4670,5073,4608 ,5131,5250,5294 }, {4729,4787,5025 ,5230,5293,5307 }, + {4584,7818,4557 ,5308,5309,5310 }, {3783,4639,4638 ,5311,5166,5154 }, + {616,575,673 ,4999,2180,2210 }, {797,4789,4732 ,5224,5284,5298 }, + {4678,4903,4794 ,5312,5214,5313 }, {4789,4927,4592 ,5284,5297,5296 }, + {4819,4701,4573 ,5216,5241,5314 }, {5068,4727,4686 ,5305,5259,5030 }, + {4800,4700,4725 ,5315,5283,5200 }, {4695,4800,4725 ,5199,5315,5200 }, + {6028,6040,5927 ,5316,5317,5318 }, {4879,4923,4910 ,5281,5245,5244 }, + {6523,5349,5326 ,5319,5320,3714 }, {4800,4923,4879 ,5315,5245,5281 }, + {5090,3801,7052 ,4929,4974,708 }, {4698,559,4532 ,1578,5225,5285 }, + {1698,4894,4789 ,747,746,5284 }, {2154,5474,2273 ,5321,4243,5322 }, + {7698,7746,7697 ,333,332,651 }, {717,4951,4938 ,2408,5058,5053 }, + {4651,4650,4596 ,5323,5287,1629 }, {4576,4651,4596 ,1628,5323,1629 }, + {1519,2544,601 ,2450,2451,3905 }, {4650,4703,4674 ,5287,5290,5306 }, + {674,4936,4935 ,2271,5013,5012 }, {4848,4836,4783 ,4983,4979,4981 }, + {8071,8133,8144 ,738,2131,2469 }, {4663,4687,4637 ,5155,4985,4986 }, + {4643,1508,4666 ,5037,2029,5105 }, {5672,7009,5694 ,5324,5325,5326 }, + {4670,4608,4906 ,5131,5294,4895 }, {4754,4633,4694 ,5211,5327,5328 }, + {4701,4729,5025 ,5241,5230,5307 }, {4633,4632,4694 ,5327,5329,5328 }, + {8091,8099,8083 ,2167,2867,2103 }, {4250,4404,166 ,4161,5072,3729 }, + {4837,4834,4720 ,3990,4304,5330 }, {3600,3578,3623 ,5331,4205,4206 }, + {4929,4963,4948 ,5142,5178,5262 }, {4963,936,4962 ,5178,5234,5263 }, + {917,4929,4948 ,4016,5142,5262 }, {4963,4962,4948 ,5178,5263,5262 }, + {4283,4086,4774 ,1678,3723,5332 }, {4700,4800,4879 ,5283,5315,5281 }, + {682,346,4923 ,448,449,5245 }, {797,4732,4925 ,5224,5298,5278 }, + {5647,6890,6852 ,5333,5334,5335 }, {4958,1995,1994 ,5226,757,5102 }, + {5084,1698,4789 ,5231,747,5284 }, {4894,4532,4789 ,746,5285,5284 }, + {5084,4789,4867 ,5231,5284,5198 }, {2391,328,327 ,3544,2289,2226 }, + {1317,1176,1177 ,5099,2096,5148 }, {5061,5094,5093 ,2135,5089,5136 }, + {137,4843,4783 ,4980,5130,4981 }, {1831,1902,1866 ,2221,2193,2191 }, + {4704,4703,4650 ,4788,5290,5287 }, {4651,4704,4650 ,5323,4788,5287 }, + {665,760,664 ,1754,3106,1755 }, {4936,4974,4973 ,5013,5019,5014 }, + {4907,674,4935 ,2209,2271,5012 }, {4587,4637,135 ,774,4986,2653 }, + {4879,4941,4790 ,5281,5282,5336 }, {4995,4996,5030 ,5021,5020,2134 }, + {4729,4726,4792 ,5230,5229,5272 }, {4810,475,4748 ,5027,1884,4998 }, + {4998,4999,5033 ,4713,4712,5103 }, {4620,4823,343 ,5276,5277,4543 }, + {8079,8104,3286 ,55,1343,56 }, {6613,6650,6649 ,3746,3833,3759 }, + {4880,4821,4762 ,5008,5134,5129 }, {2279,2251,7042 ,5337,3676,5338 }, + {5061,5062,5094 ,2135,5044,5089 }, {1120,6342,1492 ,5339,5265,1585 }, + {4824,4886,4848 ,4719,4720,4983 }, {876,1167,4017 ,5340,5341,5342 }, + {1330,615,654 ,53,766,93 }, {4558,4598,4584 ,4209,5343,5308 }, + {4800,682,4923 ,5315,448,5245 }, {4948,935,4947 ,5262,5264,5344 }, + {82,557,284 ,138,3724,139 }, {4879,4790,4700 ,5281,5336,5283 }, + {4789,4532,838 ,5284,5285,5345 }, {4955,4958,1994 ,5227,5226,5102 }, + {1210,4183,5017 ,4739,1572,5092 }, {4675,4704,4651 ,5346,4788,5323 }, + {4590,3693,2277 ,1490,5347,3522 }, {833,1291,4780 ,489,5215,5150 }, + {1119,2408,406 ,5180,1399,2566 }, {4912,5071,1024 ,5186,5167,5077 }, + {683,1634,396 ,621,768,153 }, {718,620,719 ,2407,2474,2476 }, + {2908,2851,1249 ,3052,3051,2876 }, {4796,1937,4291 ,4756,59,58 }, + {5089,5091,7052 ,3442,5133,708 }, {4621,4925,4608 ,5223,5278,5294 }, + {5073,4621,4608 ,5250,5223,5294 }, {5396,5395,6506 ,3548,5348,3549 }, + {4634,17,4754 ,5210,5190,5211 }, {402,401,7712 ,5349,1387,5350 }, + {7640,4577,4575 ,5351,5352,3128 }, {682,4800,4695 ,448,5315,5199 }, + {4902,1672,1582 ,5128,2083,2081 }, {5326,6524,6523 ,3714,3704,5319 }, + {4927,4789,838 ,5297,5284,5345 }, {4611,4694,4941 ,5209,5328,5282 }, + {559,4698,4958 ,5225,1578,5226 }, {4698,1884,4958 ,1578,3567,5226 }, + {4958,1884,1995 ,5226,3567,757 }, {833,4772,1291 ,489,5201,5215 }, + {1968,1967,1931 ,2309,404,4736 }, {4577,4578,4576 ,5352,5353,1628 }, + {4578,4624,4651 ,5353,5354,5323 }, {4576,4578,4651 ,1628,5353,5323 }, + {4651,4624,4675 ,5323,5354,5346 }, {4579,4578,7641 ,5355,5353,5356 }, + {4757,4643,4666 ,5104,5037,5105 }, {4639,4689,4638 ,5166,5111,5154 }, + {5081,6605,5938 ,1258,5164,5163 }, {4874,673,707 ,5000,2210,2091 }, + {4941,4694,4790 ,5282,5328,5336 }, {6711,6735,7031 ,3473,3472,5357 }, + {4711,4771,5837 ,5358,3622,3621 }, {4926,4796,4291 ,3897,4756,58 }, + {4601,4654,4653 ,5359,4348,4347 }, {4803,4777,4778 ,4776,3917,3916 }, + {3861,3566,2746 ,1147,1146,1517 }, {1602,4723,361 ,2329,4972,4994 }, + {798,4913,5413 ,78,4185,76 }, {4735,1768,1718 ,5140,2109,2108 }, + {5771,8099,8086 ,2868,2867,4823 }, {2000,4740,1971 ,2428,2426,2520 }, + {4562,4602,4561 ,5018,5360,5361 }, {943,4993,4970 ,1807,5292,3073 }, + {943,1081,4993 ,1807,3745,5292 }, {4600,4599,4558 ,4360,4210,4209 }, + {4559,4600,4558 ,5237,4360,4209 }, {559,4693,267 ,5225,5228,5362 }, + {1091,160,4834 ,4597,4295,4304 }, {4638,4687,4663 ,5154,4985,5155 }, + {4624,4704,4675 ,5354,4788,5346 }, {5082,4684,4979 ,5218,5217,5363 }, + {320,4687,375 ,1871,4985,1883 }, {4656,4681,4655 ,5002,4958,4957 }, + {4923,346,638 ,5245,449,495 }, {4667,5068,4912 ,5069,5305,5186 }, + {4829,4852,4865 ,4626,5364,4229 }, {5043,4144,5985 ,4762,4761,5365 }, + {974,975,7335 ,5366,4266,4265 }, {5837,4713,4711 ,3621,4015,5358 }, + {1699,1698,4898 ,772,747,5123 }, {4754,17,4959 ,5211,5190,5079 }, + {4656,4655,4602 ,5002,4957,5360 }, {4604,4656,4602 ,5016,5002,5360 }, + {4603,4604,4602 ,5017,5016,5360 }, {4602,4601,4560 ,5360,5359,5236 }, + {4573,4701,2733 ,5314,5241,5367 }, {4694,4611,4754 ,5328,5209,5211 }, + {4903,640,4761 ,5214,5078,5251 }, {3788,4680,3672 ,4410,5368,4277 }, + {4834,160,1091 ,4304,4295,4597 }, {4720,4834,1091 ,5330,4304,4597 }, + {4680,4720,1091 ,5368,5330,4597 }, {6141,2072,3725 ,2386,1812,3971 }, + {883,146,4594 ,119,504,688 }, {1176,1130,1177 ,2096,2136,5148 }, + {2315,2382,4563 ,3068,3025,3024 }, {2407,1073,2156 ,4001,3741,3631 }, + {4624,4705,4704 ,5354,5369,4788 }, {4705,4706,4704 ,5369,4786,4788 }, + {7186,7185,7168 ,5370,5073,5371 }, {124,196,1423 ,2206,152,151 }, + {3954,8065,8062 ,3463,96,3272 }, {4603,4602,4562 ,5017,5360,5018 }, + {1968,1931,5019 ,2309,4736,2384 }, {640,446,4847 ,5078,501,5252 }, + {678,682,4695 ,446,448,5199 }, {3786,22,1418 ,4074,4066,4619 }, + {4921,4979,2257 ,2079,5363,554 }, {1941,3476,3283 ,1034,3838,262 }, + {4787,4739,4815 ,5293,5372,5373 }, {5025,4787,4815 ,5307,5293,5373 }, + {5768,4713,5804 ,933,4015,3623 }, {4449,4564,4805 ,5374,5375,5376 }, + {4711,4449,4805 ,5358,5374,5376 }, {4564,4646,4645 ,5375,5377,5378 }, + {4805,4564,4645 ,5376,5375,5378 }, {4646,4926,4645 ,5377,3897,5378 }, + {1724,1249,2851 ,3880,2876,3051 }, {4912,5068,4686 ,5186,5305,5030 }, + {976,937,4964 ,782,5067,783 }, {4655,4654,4602 ,4957,4348,5360 }, + {4561,4602,4560 ,5361,5360,5236 }, {2391,327,282 ,3544,2226,3498 }, + {4601,4653,4600 ,5359,4347,4360 }, {675,4937,4936 ,2294,5187,5013 }, + {5095,5096,1024 ,5379,5185,5077 }, {4823,4619,343 ,5277,5213,4543 }, + {1365,4666,1440 ,2085,5105,2063 }, {4619,344,343 ,5213,562,4543 }, + {4761,4847,4619 ,5251,5252,5213 }, {4794,4903,4761 ,5313,5214,5251 }, + {509,4873,572 ,4993,4992,1947 }, {4579,4624,4578 ,5355,5354,5353 }, + {4676,4706,4705 ,5380,4786,5369 }, {4624,4676,4705 ,5354,5380,5369 }, + {4569,4983,2808 ,1344,480,522 }, {2382,127,126 ,3025,1395,1394 }, + {1172,1081,1034 ,1758,3745,1756 }, {1672,1649,1650 ,2083,2031,2082 }, + {4834,5236,1091 ,4304,4301,4597 }, {4847,446,4619 ,5252,501,5213 }, + {446,39,4619 ,501,529,5213 }, {111,110,2962 ,1154,743,3494 }, + {4589,4638,4637 ,776,5154,4986 }, {4571,2162,6311 ,1342,3278,1097 }, + {4176,3784,4198 ,4703,2252,4969 }, {4701,4815,4956 ,5241,5373,5381 }, + {4696,4753,4713 ,5382,5383,4015 }, {5770,4696,4713 ,3949,5382,4015 }, + {4753,4449,4711 ,5383,5374,5358 }, {4713,4753,4711 ,4015,5383,5358 }, + {4939,4926,4646 ,5384,3897,5377 }, {4876,4796,4926 ,5385,4756,3897 }, + {4939,4876,4926 ,5384,5385,3897 }, {4323,4394,4796 ,4045,4044,4756 }, + {4876,4323,4796 ,5385,4045,4756 }, {3975,8064,2812 ,728,3141,1914 }, + {4916,4930,4929 ,2248,5068,5142 }, {4892,343,342 ,5386,4543,4542 }, + {4691,4721,4690 ,2089,5052,5051 }, {468,4803,4779 ,1369,4776,4729 }, + {4822,4783,4902 ,4984,4981,5128 }, {468,4779,4716 ,1369,4729,1030 }, + {5479,6741,6706 ,5387,5388,5389 }, {4601,4600,4560 ,5359,4360,5236 }, + {4994,4995,5030 ,5076,5021,2134 }, {4532,559,838 ,5285,5225,5345 }, + {4754,4903,4678 ,5211,5214,5312 }, {559,267,838 ,5225,5362,5345 }, + {4776,4680,3788 ,5390,5368,4410 }, {4452,4432,4795 ,2398,156,3710 }, + {7101,7128,7114 ,4073,5391,5392 }, {8049,2318,2317 ,5393,3185,3129 }, + {4625,4624,4579 ,5394,5354,5355 }, {4625,4626,4624 ,5394,5395,5354 }, + {4626,4676,4624 ,5395,5380,5354 }, {4998,5033,5032 ,4713,5103,5168 }, + {4717,4716,4657 ,1449,1030,5001 }, {8116,8071,5521 ,172,738,751 }, + {4612,278,4641 ,5249,1489,5248 }, {5031,5064,5063 ,4995,5043,5042 }, + {4721,4691,383 ,5052,2089,2087 }, {4639,4640,4690 ,5166,5208,5051 }, + {1073,42,2156 ,3741,5195,3631 }, {7029,7051,2341 ,548,547,845 }, + {6524,6572,6571 ,3704,3734,4146 }, {1902,1901,1866 ,2193,2192,2191 }, + {6989,5693,6947 ,4586,4075,4587 }, {6968,6967,6916 ,595,4629,596 }, + {6735,3192,2450 ,3472,3063,3444 }, {2733,4701,4956 ,5367,5241,5381 }, + {2407,2156,2258 ,4001,3631,1198 }, {4753,4854,4449 ,5383,5396,5374 }, + {4877,4876,4939 ,5397,5385,5384 }, {4940,4877,4939 ,5398,5397,5384 }, + {4752,4323,4876 ,5196,4045,5385 }, {4877,4752,4876 ,5397,5196,5385 }, + {717,855,4977 ,2408,2392,4711 }, {6711,7031,2450 ,3473,5357,3444 }, + {5520,1516,5059 ,1304,1254,1253 }, {4931,4930,4916 ,5066,5068,2248 }, + {5079,833,4780 ,5220,489,5150 }, {4980,2163,158 ,241,243,5258 }, + {5477,4672,5442 ,5399,2682,5174 }, {1307,801,4351 ,4930,4907,4606 }, + {4846,4780,3139 ,4237,5150,5400 }, {5659,2453,5658 ,3053,42,49 }, + {4633,4754,4678 ,5327,5211,5312 }, {4700,4790,5002 ,5283,5336,5273 }, + {5060,4720,4680 ,5401,5330,5368 }, {4776,5060,4680 ,5390,5401,5368 }, + {5060,4837,4720 ,5401,3990,5330 }, {4937,4975,4974 ,5187,5202,5019 }, + {4626,4625,4579 ,5395,5394,5355 }, {4626,4706,4676 ,5395,4786,5380 }, + {4612,4640,4639 ,5249,5208,5166 }, {4658,4717,4657 ,5047,1449,5001 }, + {677,4938,4937 ,2296,5053,5187 }, {3783,4612,4639 ,5311,5249,5166 }, + {4750,383,4786 ,5152,2087,5153 }, {577,675,674 ,2270,2294,2271 }, + {4893,4874,707 ,5286,5000,2091 }, {4602,4654,4601 ,5360,4348,5359 }, + {5943,6292,6283 ,5402,5403,4298 }, {6523,6524,6571 ,5319,3704,4146 }, + {1167,1120,1492 ,5341,5339,1585 }, {5672,5694,6949 ,5324,5326,4585 }, + {4774,4086,557 ,5332,3723,3724 }, {2156,6464,3898 ,3631,3633,1400 }, + {4701,5025,4815 ,5241,5307,5373 }, {4854,4616,4564 ,5396,5404,5375 }, + {4449,4854,4564 ,5374,5396,5375 }, {4616,4662,4646 ,5404,5405,5377 }, + {4564,4616,4646 ,5375,5404,5377 }, {4940,4939,4646 ,5398,5384,5377 }, + {4662,4940,4646 ,5405,5398,5377 }, {406,2219,2206 ,2566,177,176 }, + {4613,4614,278 ,1488,3109,1489 }, {617,618,677 ,2352,2391,2296 }, + {5064,5096,5095 ,5043,5185,5379 }, {4824,4848,4844 ,4719,4983,4866 }, + {4937,4938,4975 ,5187,5053,5202 }, {5018,5032,5031 ,5119,5168,4995 }, + {4840,4874,4873 ,5207,5000,4992 }, {972,2314,2162 ,3277,3742,3278 }, + {6681,6704,6655 ,5406,5160,5407 }, {4948,4947,917 ,5262,5344,4016 }, + {3358,6393,3357 ,880,1556,883 }, {6342,6555,1749 ,5265,597,555 }, + {4693,4955,4953 ,5228,5227,5408 }, {267,4693,4953 ,5362,5228,5408 }, + {4955,1994,4971 ,5227,5102,5409 }, {4953,4955,4971 ,5408,5227,5409 }, + {4838,5060,4776 ,245,5401,5390 }, {4838,4837,5060 ,245,3990,5401 }, + {4818,4647,4661 ,5145,5036,5101 }, {4683,1024,4647 ,5035,5077,5036 }, + {4580,4626,4579 ,5410,5395,5355 }, {4707,4706,4626 ,5411,4786,5395 }, + {4707,4737,4706 ,5411,5412,4786 }, {4706,4737,4765 ,4786,5412,3983 }, + {4826,5259,4765 ,5413,3794,3983 }, {4658,4657,4605 ,5047,5001,5006 }, + {361,4723,4751 ,4994,4972,5007 }, {4857,509,4810 ,4991,4993,5027 }, + {4873,4874,4893 ,4992,5000,5286 }, {4840,4857,4810 ,5207,4991,5027 }, + {375,4748,475 ,1883,4998,1884 }, {1320,4618,1285 ,2356,5070,1944 }, + {4664,4614,279 ,5414,3109,2176 }, {1583,2621,3683 ,5415,5416,5417 }, + {922,6416,6415 ,117,3440,386 }, {5000,922,6415 ,286,117,386 }, + {6795,5491,5516 ,5418,5419,5420 }, {4922,4608,4585 ,5256,5294,3955 }, + {1073,2407,2314 ,3741,4001,3742 }, {4793,4718,4753 ,5421,5422,5383 }, + {4718,4814,4854 ,5422,5423,5396 }, {4753,4718,4854 ,5383,5422,5396 }, + {4814,4722,4616 ,5423,5424,5404 }, {4854,4814,4616 ,5396,5423,5404 }, + {4722,4665,4662 ,5424,5425,5405 }, {4616,4722,4662 ,5404,5424,5405 }, + {4943,4940,4662 ,5426,5398,5405 }, {4665,4943,4662 ,5425,5426,5405 }, + {1655,2432,68 ,4089,5427,4087 }, {4614,4641,278 ,3109,5248,1489 }, + {4895,4817,5004 ,4973,2368,5243 }, {4975,4976,4997 ,5202,5048,5049 }, + {6417,404,5388 ,762,761,5428 }, {5016,4644,1931 ,5010,2425,4736 }, + {1699,1702,1752 ,772,1039,946 }, {4975,4997,4996 ,5202,5049,5020 }, + {4936,4937,4974 ,5013,5187,5019 }, {4810,4811,4840 ,5027,5165,5207 }, + {6957,5752,5709 ,4315,4374,5253 }, {4866,4865,4852 ,3641,4229,5364 }, + {4734,4902,1582 ,5127,5128,2081 }, {4829,4830,4852 ,4626,3642,5364 }, + {961,446,4959 ,500,501,5079 }, {5015,4870,4862 ,5170,5135,5149 }, + {1702,1636,1703 ,1039,1036,1038 }, {638,15,17 ,495,497,5190 }, + {4842,4838,4776 ,5429,245,5390 }, {24,1375,4837 ,247,325,3990 }, + {4838,24,4837 ,245,247,3990 }, {4919,4920,4968 ,5221,5138,5222 }, + {4581,4627,4626 ,5430,5431,5395 }, {4580,4581,4626 ,5410,5430,5395 }, + {4626,4708,4707 ,5395,5432,5411 }, {4707,4708,4737 ,5411,5432,5412 }, + {4737,4708,4765 ,5412,5432,3983 }, {384,327,328 ,2228,2226,2289 }, + {4751,5004,4871 ,5007,5243,5040 }, {4871,561,5016 ,5040,2379,5010 }, + {4895,5004,4751 ,4973,5243,5007 }, {4857,4840,4873 ,4991,5207,4992 }, + {4782,4812,4492 ,5029,5169,5031 }, {6842,6895,6865 ,5433,5434,5435 }, + {4820,4433,4849 ,2399,4591,5436 }, {4801,82,607 ,5115,138,2207 }, + {935,4962,936 ,5264,5263,5234 }, {7138,7161,7169 ,2560,2559,5437 }, + {1265,6344,4548 ,1273,1450,589 }, {4730,4885,4743 ,5438,5439,3629 }, + {4885,4747,4793 ,5439,5440,5421 }, {4743,4885,4793 ,3629,5439,5421 }, + {4793,4747,4718 ,5421,5440,5422 }, {4742,4877,4940 ,5441,5397,5398 }, + {4943,4742,4940 ,5426,5441,5398 }, {4742,4752,4877 ,5441,5196,5397 }, + {4897,1488,4752 ,5442,5197,5196 }, {410,259,2967 ,2204,2203,2701 }, + {4920,4919,662 ,5138,5221,5141 }, {1041,5034,952 ,2410,2396,2395 }, + {5034,1041,5033 ,2396,2410,5103 }, {2156,42,6783 ,3631,5195,3632 }, + {344,1993,343 ,562,4544,4543 }, {4587,4589,4637 ,774,776,4986 }, + {4787,4792,4816 ,5293,5272,5443 }, {4633,4678,4632 ,5327,5312,5329 }, + {4678,4794,4761 ,5312,5313,5251 }, {4758,4761,4620 ,5444,5251,5276 }, + {4841,4842,4745 ,5445,5429,5446 }, {4745,4776,495 ,5446,5390,4389 }, + {4917,4838,4842 ,5447,245,5429 }, {4841,4917,4842 ,5445,5447,5429 }, + {4258,85,4303 ,84,2504,3728 }, {5095,1024,4683 ,5379,5077,5035 }, + {5094,5095,4683 ,5089,5379,5035 }, {4627,4677,4626 ,5431,5448,5395 }, + {4626,4677,4708 ,5395,5448,5432 }, {4708,4766,4765 ,5432,5449,3983 }, + {4766,4827,4826 ,5449,2734,5413 }, {949,850,851 ,2133,2092,2155 }, + {4886,361,417 ,4720,4994,5009 }, {4580,7672,4581 ,5410,5450,5430 }, + {1397,4667,1320 ,2358,5069,2356 }, {6172,6147,5920 ,5451,5452,5453 }, + {8144,8133,2301 ,2469,2131,2132 }, {7027,7049,7007 ,3175,5454,4628 }, + {3320,6639,2158 ,5455,3699,1160 }, {911,3320,2158 ,1159,5455,1160 }, + {6309,6310,6735 ,3690,3918,3472 }, {1920,2157,6606 ,581,3411,5085 }, + {6686,5105,4544 ,750,5456,748 }, {1332,8123,8143 ,3492,2247,2570 }, + {4730,4731,4747 ,5438,5457,5440 }, {4885,4730,4747 ,5439,5438,5440 }, + {4899,4752,4742 ,5458,5196,5441 }, {4882,4899,4742 ,5459,5458,5441 }, + {3895,4897,4752 ,4455,5442,5196 }, {4899,3895,4752 ,5458,4455,5196 }, + {5065,5097,5096 ,5064,5063,5185 }, {5064,5065,5096 ,5043,5064,5185 }, + {663,4920,662 ,4773,5138,5141 }, {7163,7171,7194 ,5460,4306,1670 }, + {4773,4804,4661 ,4865,4867,5101 }, {4605,4604,1381 ,5006,5016,5193 }, + {1831,4850,1902 ,2221,5171,2193 }, {4968,4969,1030 ,5222,4977,1619 }, + {4906,4608,4922 ,4895,5294,5256 }, {4906,4922,2814 ,4895,5256,5255 }, + {4830,4866,4852 ,3642,3641,5364 }, {4739,4787,4816 ,5372,5293,5443 }, + {1994,2048,3184 ,5102,5461,4453 }, {4971,1994,1460 ,5409,5102,5462 }, + {1460,1994,3184 ,5462,5102,4453 }, {4745,4775,4841 ,5446,4388,5445 }, + {4917,4808,4838 ,5447,5137,245 }, {241,4724,240 ,314,4737,1571 }, + {4581,4628,4627 ,5430,5463,5431 }, {4627,4628,4677 ,5431,5463,5448 }, + {4677,4628,4708 ,5448,5463,5432 }, {4738,4767,4766 ,5464,5465,5449 }, + {4708,4738,4766 ,5432,5464,5449 }, {4766,4828,4827 ,5449,5466,2734 }, + {423,4811,383 ,2086,5165,2087 }, {6744,3350,1116 ,3986,2541,1401 }, + {6415,6416,4424 ,386,3440,3792 }, {6416,5089,4424 ,3440,3442,3792 }, + {5171,5125,5172 ,1804,5467,5468 }, {5105,5040,4544 ,5456,5469,748 }, + {5949,5388,404 ,5086,5428,761 }, {4731,4719,4718 ,5457,5470,5422 }, + {4747,4731,4718 ,5440,5457,5422 }, {4719,4673,4814 ,5470,5471,5423 }, + {4718,4719,4814 ,5422,5470,5423 }, {4673,4781,4722 ,5471,5472,5424 }, + {4814,4673,4722 ,5423,5471,5424 }, {4944,4943,4665 ,5473,5426,5425 }, + {4671,4944,4665 ,5474,5473,5425 }, {4944,4882,4742 ,5473,5459,5441 }, + {4943,4944,4742 ,5426,5473,5441 }, {3247,3777,6430 ,1494,1582,2748 }, + {423,514,4811 ,2086,2154,5165 }, {5018,4998,5032 ,5119,4713,5168 }, + {7927,7976,7952 ,1716,1715,5475 }, {718,811,856 ,2407,557,5139 }, + {8076,8124,2810 ,617,1141,1728 }, {4931,4932,4964 ,5066,4580,783 }, + {1042,898,1043 ,2409,2411,2478 }, {955,4834,4837 ,3991,4304,3990 }, + {4792,5002,4699 ,5272,5273,5476 }, {4802,4841,4775 ,5477,5445,4388 }, + {4917,648,4808 ,5447,4679,5137 }, {4716,4778,4714 ,1030,3916,1031 }, + {4734,1536,1508 ,5127,2080,2029 }, {5047,5031,5062 ,4996,4995,5044 }, + {4993,1081,1030 ,5292,3745,1619 }, {4762,1831,4735 ,5129,2221,5140 }, + {4743,5038,4730 ,3629,3628,5438 }, {4581,4582,4628 ,5430,5478,5463 }, + {4767,4798,4766 ,5465,5479,5449 }, {4766,4798,4828 ,5449,5479,5466 }, + {4828,4887,4827 ,5466,2735,2734 }, {5064,5095,5094 ,5043,5379,5089 }, + {4863,4904,4887 ,5480,3309,2735 }, {8145,8114,6797 ,3376,2286,2246 }, + {1167,1492,4017 ,5341,1585,5342 }, {5946,5991,7083 ,5481,5482,5483 }, + {4445,712,6757 ,3648,3650,3409 }, {5092,4730,4839 ,3953,5438,5484 }, + {7055,1219,7079 ,5485,4102,5486 }, {4635,4591,4731 ,5487,5488,5457 }, + {4730,4635,4731 ,5438,5487,5457 }, {4591,4825,4719 ,5488,5489,5470 }, + {4731,4591,4719 ,5457,5488,5470 }, {4825,4791,4673 ,5489,5490,5471 }, + {4719,4825,4673 ,5470,5489,5471 }, {4791,4728,4781 ,5490,5491,5472 }, + {4673,4791,4781 ,5471,5490,5472 }, {4781,4728,4722 ,5472,5491,5424 }, + {4728,837,4665 ,5491,5492,5425 }, {4722,4728,4665 ,5424,5491,5425 }, + {4665,837,4671 ,5425,5492,5474 }, {837,4946,4944 ,5492,5493,5473 }, + {4671,837,4944 ,5474,5492,5473 }, {4922,3766,4636 ,5256,3954,5118 }, + {4756,3817,5099 ,1190,1189,5494 }, {3817,3800,5099 ,1189,4325,5494 }, + {4682,4756,4736 ,1191,1190,5495 }, {7031,6735,2450 ,5357,3472,3444 }, + {4873,4893,612 ,4992,5286,5011 }, {6704,6703,6655 ,5160,5161,5407 }, + {4816,4792,4699 ,5443,5272,5476 }, {3945,6418,6416 ,4550,3441,3440 }, + {4775,4861,4802 ,4388,3877,5477 }, {4802,4917,4841 ,5477,5447,5445 }, + {4751,4871,5015 ,5007,5040,5170 }, {5031,5063,5062 ,4995,5042,5044 }, + {1790,5017,4183 ,4738,5092,1572 }, {4970,4993,4992 ,3073,5292,4978 }, + {4783,1718,1672 ,4981,2108,2083 }, {4555,4556,4582 ,5496,5497,5478 }, + {79,8104,3421 ,1510,1343,1156 }, {4583,4628,4582 ,5498,5463,5478 }, + {4652,4709,4708 ,5499,4396,5432 }, {4628,4652,4708 ,5463,5499,5432 }, + {4708,4709,4738 ,5432,4396,5464 }, {4767,4768,4798 ,5465,5500,5479 }, + {4798,4768,4828 ,5479,5500,5466 }, {4863,4887,4828 ,5480,2735,5466 }, + {4851,4863,4828 ,5501,5480,5466 }, {311,4808,648 ,246,5137,4679 }, + {673,4907,4934 ,2210,2209,5041 }, {6808,6809,6859 ,3465,712,472 }, + {1583,1606,2621 ,5415,3677,5416 }, {7004,7045,7026 ,5502,5503,5504 }, + {5089,3454,4424 ,3442,710,3792 }, {5844,5812,7073 ,5179,660,662 }, + {1666,4882,4944 ,5505,5459,5473 }, {4946,1666,4944 ,5493,5505,5473 }, + {1666,4899,4882 ,5505,5458,5459 }, {4921,5082,4979 ,2079,5218,5363 }, + {5066,5098,5065 ,5188,5062,5064 }, {4733,5099,4697 ,5506,5494,4327 }, + {4733,4756,5099 ,5506,1190,5494 }, {3634,4741,4813 ,5507,5508,4088 }, + {4733,4736,4756 ,5506,5495,1190 }, {5026,4881,4957 ,4225,2731,2733 }, + {8122,8145,8087 ,384,3376,385 }, {5022,1196,4788 ,4329,4330,3758 }, + {4760,4669,5022 ,5509,3879,4329 }, {4759,4861,4669 ,5510,3877,3879 }, + {4760,4759,4669 ,5509,5510,3879 }, {4892,4917,4802 ,5386,5447,5477 }, + {4892,648,4917 ,5386,4679,5447 }, {675,578,676 ,2294,2293,2295 }, + {718,856,855 ,2407,5139,2392 }, {6348,6349,6357 ,1607,705,5271 }, + {663,662,566 ,4773,5141,1613 }, {668,611,669 ,1949,1948,2020 }, + {4605,4656,4604 ,5006,5002,5016 }, {5217,4703,4704 ,4026,5290,4788 }, + {5067,1041,1135 ,1945,2410,1943 }, {3855,3813,3814 ,1511,1515,5511 }, + {3303,7387,7386 ,1049,3615,5512 }, {4583,4629,4628 ,5498,5513,5463 }, + {4629,4652,4628 ,5513,5499,5463 }, {4709,4768,4767 ,4396,5500,5465 }, + {4738,4709,4767 ,5464,4396,5465 }, {851,767,4934 ,2155,2093,5041 }, + {6153,5040,5105 ,5514,5469,5456 }, {3898,6464,4987 ,1400,3633,1491 }, + {6917,6916,6895 ,5515,596,5434 }, {6757,3702,1990 ,3409,1303,1305 }, + {911,5027,158 ,1159,5516,5258 }, {5027,911,6684 ,5516,1159,1161 }, + {6681,6655,6656 ,5406,5407,5517 }, {5405,6681,6656 ,5518,5406,5517 }, + {5688,5738,6890 ,5519,5520,5334 }, {5738,5750,6890 ,5520,5156,5334 }, + {1460,3184,4899 ,5462,4453,5458 }, {1666,1460,4899 ,5505,5462,5458 }, + {4899,3184,3895 ,5458,4453,4455 }, {4618,4912,5097 ,5070,5186,5063 }, + {8094,8100,8069 ,98,1713,1095 }, {6853,5626,5587 ,2505,1460,1459 }, + {6098,6173,6388 ,1565,3711,3184 }, {2164,2165,3191 ,3583,1041,2903 }, + {7169,7186,7168 ,5437,5370,5371 }, {4741,4815,1655 ,5508,5373,4089 }, + {4813,4741,1655 ,4088,5508,4089 }, {4815,4878,4872 ,5373,5521,5522 }, + {1655,4815,4872 ,4089,5373,5522 }, {4878,4739,4872 ,5521,5372,5522 }, + {4668,4697,4872 ,5523,4327,5522 }, {4739,4668,4872 ,5372,5523,5522 }, + {4668,4733,4697 ,5523,5506,4327 }, {4610,4682,4736 ,3757,1191,5495 }, + {4610,1376,4682 ,3757,1937,1191 }, {5002,4790,4699 ,5273,5336,5476 }, + {4760,5022,4788 ,5509,4329,3758 }, {4660,4760,4788 ,5524,5509,3758 }, + {4759,4802,4861 ,5510,5477,3877 }, {4802,4875,4892 ,5477,5525,5386 }, + {1469,4782,1397 ,2361,5029,2358 }, {707,673,767 ,2091,2210,2093 }, + {4647,4818,4643 ,5036,5145,5037 }, {749,1676,4723 ,2331,2343,4972 }, + {663,566,567 ,4773,1613,1684 }, {4724,4949,1790 ,4737,5526,4738 }, + {1440,4666,1508 ,2063,5105,2029 }, {611,612,669 ,1948,5011,2020 }, + {5098,1285,5097 ,5062,1944,5063 }, {4630,4629,4583 ,827,5513,5498 }, + {4629,4630,4652 ,5513,827,5499 }, {4709,4710,4768 ,4396,4136,5500 }, + {4829,4828,4768 ,4626,5466,5500 }, {4829,4851,4828 ,4626,5501,5466 }, + {2290,7935,7936 ,1982,5527,5528 }, {2376,2156,3898 ,1251,3631,1400 }, + {3609,6195,5920 ,5081,5529,5453 }, {3319,2336,5005 ,1259,1261,1301 }, + {8081,8138,8078 ,1445,2018,1744 }, {5003,4591,4635 ,5530,5488,5487 }, + {4860,5003,4635 ,5531,5530,5487 }, {4591,4909,4825 ,5488,5532,5489 }, + {4909,4791,4825 ,5532,5490,5489 }, {837,4952,4946 ,5492,5533,5493 }, + {4952,1460,1666 ,5533,5462,5505 }, {4946,4952,1666 ,5493,5533,5505 }, + {4698,1885,1884 ,1578,778,3567 }, {4635,4730,5092 ,5487,5438,3953 }, + {7743,7769,7768 ,5534,5535,5536 }, {4741,4956,4815 ,5508,5381,5373 }, + {4815,4739,4878 ,5373,5372,5521 }, {4816,4668,4739 ,5443,5523,5372 }, + {4699,4733,4668 ,5476,5506,5523 }, {4816,4699,4668 ,5443,5476,5523 }, + {4632,4788,4610 ,5329,3758,3757 }, {4891,4632,4610 ,5537,5329,3757 }, + {4632,4660,4788 ,5329,5524,3758 }, {4678,4760,4660 ,5312,5509,5524 }, + {4632,4678,4660 ,5329,5312,5524 }, {4620,4802,4759 ,5276,5477,5510 }, + {4802,4620,4875 ,5477,5276,5525 }, {5015,4871,4870 ,5170,5040,5135 }, + {678,4695,833 ,446,5199,489 }, {4862,1831,4762 ,5149,2221,5129 }, + {4970,4969,4920 ,3073,4977,5138 }, {4749,4786,4811 ,5026,5153,5165 }, + {4641,4664,279 ,5248,5414,2176 }, {557,4883,4774 ,3724,5538,5332 }, + {156,1297,1488 ,5539,4048,5197 }, {557,4801,4883 ,3724,5115,5538 }, + {4264,1488,4897 ,5540,5197,5442 }, {4641,279,4691 ,5248,2176,2089 }, + {4597,4630,4583 ,5541,827,5498 }, {4652,4630,4709 ,5499,827,4396 }, + {4710,4630,5177 ,4136,827,424 }, {4710,4769,4768 ,4136,5542,5500 }, + {4769,4799,4768 ,5542,4627,5500 }, {4768,4799,4829 ,5500,4627,4626 }, + {4829,4864,4863 ,4626,5543,5480 }, {4851,4829,4863 ,5501,4626,5480 }, + {3006,8132,552 ,414,413,3929 }, {6611,5471,6640 ,1391,2051,4224 }, + {1562,4392,7051 ,4649,844,547 }, {4586,911,158 ,5257,1159,5258 }, + {7568,7594,7567 ,5544,4020,5545 }, {7552,7613,7551 ,5546,5106,5108 }, + {4585,4593,5003 ,3955,5295,5530 }, {4860,4585,5003 ,5531,3955,5530 }, + {5003,4593,4591 ,5530,5295,5488 }, {4593,4732,4909 ,5295,5298,5532 }, + {4591,4593,4909 ,5488,5295,5532 }, {4732,4927,4791 ,5298,5297,5490 }, + {4909,4732,4791 ,5532,5298,5490 }, {4927,838,4728 ,5297,5345,5491 }, + {4791,4927,4728 ,5490,5297,5491 }, {267,837,4728 ,5362,5492,5491 }, + {838,267,4728 ,5345,5362,5491 }, {267,4953,4952 ,5362,5408,5533 }, + {837,267,4952 ,5492,5362,5533 }, {7647,7697,7675 ,4967,651,5192 }, + {2000,1971,2002 ,2428,2520,2493 }, {3276,2375,922 ,116,4479,117 }, + {403,1264,3929 ,5547,5022,5024 }, {4832,4736,4733 ,5548,5495,5506 }, + {4699,4832,4733 ,5476,5548,5506 }, {4891,4610,4736 ,5537,3757,5495 }, + {4678,4759,4760 ,5312,5510,5509 }, {4759,4758,4620 ,5510,5444,5276 }, + {4875,4620,4892 ,5525,5276,5386 }, {4850,1903,1902 ,5171,406,2193 }, + {4614,4664,4641 ,3109,5414,5248 }, {7672,7716,4581 ,5450,5549,5430 }, + {1970,4740,2000 ,2427,2426,2428 }, {4724,241,1241 ,4737,314,313 }, + {1241,4283,4774 ,313,1678,5332 }, {4724,1241,4774 ,4737,313,5332 }, + {607,124,4801 ,2207,2206,5115 }, {4491,808,5202 ,107,4734,5550 }, + {6039,5166,5227 ,2728,3427,4097 }, {4331,5052,1154 ,4645,5189,4545 }, + {1154,5052,993 ,4545,5189,5551 }, {4769,4770,4799 ,5542,4345,4627 }, + {4829,4865,4864 ,4626,4229,5543 }, {4864,4865,4863 ,5543,4229,5480 }, + {4865,4913,798 ,4229,4185,78 }, {4863,4865,798 ,5480,4229,78 }, + {3264,8117,8068 ,2468,4119,1784 }, {3626,6534,3627 ,4196,4257,4256 }, + {6418,5091,5089 ,3441,5133,3442 }, {4549,6621,6605 ,877,5552,5164 }, + {5669,5710,5709 ,5553,5254,5253 }, {6389,6714,6868 ,5301,5300,5554 }, + {5092,4585,4860 ,3953,3955,5531 }, {4953,4971,1460 ,5408,5409,5462 }, + {4952,4953,1460 ,5533,5408,5462 }, {185,3777,3247 ,1493,1582,1494 }, + {1166,309,2408 ,2746,4472,1399 }, {4560,4559,7918 ,5236,5237,5555 }, + {922,3945,6416 ,117,4550,3440 }, {2156,2376,2258 ,3631,1251,1198 }, + {4790,4832,4699 ,5336,5548,5476 }, {4790,4736,4832 ,5336,5495,5548 }, + {4694,4891,4736 ,5328,5537,5495 }, {4790,4694,4736 ,5336,5328,5495 }, + {4694,4632,4891 ,5328,5329,5537 }, {4761,4759,4678 ,5251,5510,5312 }, + {4759,4761,4758 ,5510,5251,5444 }, {4620,343,4892 ,5276,4543,5386 }, + {4117,5481,4157 ,4700,3863,3755 }, {206,3815,1606 ,3932,5556,3677 }, + {247,921,2663 ,2569,1922,3593 }, {6044,5934,5980 ,614,616,5557 }, + {3797,3746,3747 ,2061,2708,2925 }, {6211,6246,6245 ,5558,5559,5560 }, + {4443,4422,6212 ,703,5561,5562 }, {1344,1511,3941 ,300,4921,5563 }, + {1268,5995,6105 ,4568,5564,5565 }, {6210,6211,6245 ,5566,5558,5560 }, + {5515,5960,6045 ,5567,5568,935 }, {6157,1017,1426 ,5569,1793,4648 }, + {5140,2094,5130 ,3712,5570,5571 }, {5130,5499,5140 ,5571,5572,3712 }, + {5499,2621,5140 ,5572,5416,3712 }, {1627,3688,1729 ,5573,5574,3666 }, + {6247,6275,1557 ,5575,5576,5577 }, {5340,5379,4916 ,4002,3998,2248 }, + {5339,5340,4916 ,2223,4002,2248 }, {2256,5013,5950 ,2323,684,2873 }, + {6123,5988,4147 ,4221,2516,2517 }, {4392,5130,2094 ,844,5571,5570 }, + {5204,3683,5704 ,5578,5417,5579 }, {5499,5204,5704 ,5572,5578,5579 }, + {6377,6079,5978 ,1347,5580,5581 }, {5978,6082,6377 ,5581,1348,1347 }, + {7948,7994,7947 ,3625,5582,4354 }, {4306,4278,4307 ,4552,5583,4562 }, + {4489,3778,1583 ,5584,5585,5415 }, {6744,5082,5786 ,3986,5218,4813 }, + {5131,5204,5499 ,5586,5578,5572 }, {5130,5131,5499 ,5571,5586,5572 }, + {6390,5950,8114 ,3868,2873,2286 }, {1557,3310,5989 ,5577,5587,5588 }, + {778,1742,5498 ,5589,707,5590 }, {3820,778,5498 ,5591,5589,5590 }, + {313,1562,1742 ,5592,4649,707 }, {778,313,1742 ,5589,5592,707 }, + {313,5132,5130 ,5592,5593,5571 }, {1562,313,5130 ,4649,5592,5571 }, + {5130,5132,5131 ,5571,5593,5586 }, {5132,5141,5204 ,5593,5594,5578 }, + {5131,5132,5204 ,5586,5593,5578 }, {5141,5145,3683 ,5594,5595,5417 }, + {5204,5141,3683 ,5578,5594,5417 }, {5145,1289,1583 ,5595,5596,5415 }, + {3683,5145,1583 ,5417,5595,5415 }, {1289,3952,4489 ,5596,5597,5584 }, + {1583,1289,4489 ,5415,5596,5584 }, {4342,3528,4321 ,4950,5061,4873 }, + {1289,2713,3952 ,5596,4352,5597 }, {5141,1289,5145 ,5594,5596,5595 }, + {1289,3141,2713 ,5596,4701,4352 }, {5231,2375,3276 ,115,4479,116 }, + {5419,3820,3936 ,5598,5591,5599 }, {4024,5419,3936 ,5600,5598,5599 }, + {3798,778,3820 ,5601,5589,5591 }, {5419,3798,3820 ,5598,5601,5591 }, + {3798,4325,313 ,5601,5602,5592 }, {778,3798,313 ,5589,5601,5592 }, + {4325,5133,5132 ,5602,5603,5593 }, {313,4325,5132 ,5592,5602,5593 }, + {5133,533,5141 ,5603,5604,5594 }, {5132,5133,5141 ,5593,5603,5594 }, + {533,1944,5141 ,5604,5605,5594 }, {1944,2270,1289 ,5605,5606,5596 }, + {5141,1944,1289 ,5594,5605,5596 }, {2270,3222,1289 ,5606,5607,5596 }, + {1289,3222,3141 ,5596,5607,4701 }, {5133,1944,533 ,5603,5605,5604 }, + {5163,4127,3141 ,4987,4702,4701 }, {3222,5163,3141 ,5607,4987,4701 }, + {7090,7089,6281 ,4080,5608,4078 }, {4426,1414,4024 ,1056,5609,5600 }, + {3733,4426,4024 ,5610,1056,5600 }, {1414,5422,5419 ,5609,5611,5598 }, + {4024,1414,5419 ,5600,5609,5598 }, {4014,3798,5419 ,5612,5601,5598 }, + {5422,4014,5419 ,5611,5612,5598 }, {4014,3947,4325 ,5612,5613,5602 }, + {3798,4014,4325 ,5601,5612,5602 }, {5133,5561,1944 ,5603,5614,5605 }, + {2270,3002,3222 ,5606,5615,5607 }, {3222,3002,5163 ,5607,5615,4987 }, + {4545,4980,3621 ,586,241,625 }, {3947,5134,5133 ,5613,5616,5603 }, + {4325,3947,5133 ,5602,5613,5603 }, {5133,5134,5561 ,5603,5616,5614 }, + {5134,1786,1944 ,5616,5617,5605 }, {5561,5134,1944 ,5614,5616,5605 }, + {1786,5144,2270 ,5617,5618,5606 }, {1944,1786,2270 ,5605,5617,5606 }, + {5144,996,3002 ,5618,5003,5615 }, {2270,5144,3002 ,5606,5618,5615 }, + {3002,996,5163 ,5615,5003,4987 }, {5163,996,4176 ,4987,5003,4703 }, + {4014,159,3947 ,5612,5619,5613 }, {3947,159,5134 ,5613,5619,5616 }, + {6033,5987,5982 ,5620,5621,4218 }, {4012,1414,4426 ,5622,5609,1056 }, + {3816,4012,4426 ,5623,5622,1056 }, {5268,5422,1414 ,5624,5611,5609 }, + {4012,5268,1414 ,5622,5624,5609 }, {5268,5773,4014 ,5624,5625,5612 }, + {5422,5268,4014 ,5611,5624,5612 }, {5773,3971,4014 ,5625,5626,5612 }, + {3971,2961,159 ,5626,5627,5619 }, {4014,3971,159 ,5612,5626,5619 }, + {2961,5135,5134 ,5627,5628,5616 }, {159,2961,5134 ,5619,5627,5616 }, + {5135,5142,1786 ,5628,5629,5617 }, {5134,5135,1786 ,5616,5628,5617 }, + {1786,5142,5144 ,5617,5629,5618 }, {5142,5311,996 ,5629,5630,5003 }, + {5144,5142,996 ,5618,5629,5003 }, {5311,3784,996 ,5630,2252,5003 }, + {5336,4012,3816 ,5631,5622,5623 }, {5157,3971,5773 ,5632,5626,5625 }, + {5268,5157,5773 ,5624,5632,5625 }, {5447,5426,2140 ,5633,5634,5635 }, + {5157,5268,4012 ,5632,5624,5622 }, {5336,5157,4012 ,5631,5632,5622 }, + {5135,5456,5142 ,5628,5636,5629 }, {6905,5669,5626 ,2506,5553,1460 }, + {5251,5300,5299 ,3802,3793,3800 }, {4097,3816,5445 ,5637,5623,3520 }, + {4293,4097,5445 ,2732,5637,3520 }, {4294,5336,3816 ,5638,5631,5623 }, + {4097,4294,3816 ,5637,5638,5623 }, {4294,3055,5157 ,5638,5639,5632 }, + {5336,4294,5157 ,5631,5638,5632 }, {3055,1391,3971 ,5639,5640,5626 }, + {5157,3055,3971 ,5632,5639,5626 }, {1391,5343,2961 ,5640,5641,5627 }, + {3971,1391,2961 ,5626,5640,5627 }, {5343,5269,5135 ,5641,5642,5628 }, + {2961,5343,5135 ,5627,5641,5628 }, {5269,5310,5456 ,5642,5643,5636 }, + {5135,5269,5456 ,5628,5642,5636 }, {5310,4884,5142 ,5643,2253,5629 }, + {5456,5310,5142 ,5636,5643,5629 }, {5142,4884,5311 ,5629,2253,5630 }, + {5311,4884,3784 ,5630,2253,2252 }, {5343,5310,5269 ,5641,5643,5642 }, + {3100,1552,1590 ,2955,2266,3028 }, {4881,3536,4293 ,2731,5644,2732 }, + {5343,5360,5310 ,5641,5645,5643 }, {5310,5574,4884 ,5643,5646,2253 }, + {5058,3320,2163 ,5647,5455,243 }, {3536,4097,4293 ,5644,5637,2732 }, + {3403,4294,4097 ,5648,5638,5637 }, {3536,3403,4097 ,5644,5648,5637 }, + {3403,1690,3055 ,5648,5649,5639 }, {4294,3403,3055 ,5638,5648,5639 }, + {1690,5164,1391 ,5649,5650,5640 }, {3055,1690,1391 ,5639,5649,5640 }, + {5164,5312,5343 ,5650,5651,5641 }, {1391,5164,5343 ,5640,5650,5641 }, + {5312,5226,5360 ,5651,5652,5645 }, {5343,5312,5360 ,5641,5651,5645 }, + {5226,5601,5310 ,5652,5653,5643 }, {5360,5226,5310 ,5645,5652,5643 }, + {5601,711,5574 ,5653,5654,5646 }, {5310,5601,5574 ,5643,5653,5646 }, + {711,85,4884 ,5654,2504,2253 }, {5574,711,4884 ,5646,5654,2253 }, + {125,5127,124 ,399,5114,2206 }, {5164,5226,5312 ,5650,5652,5651 }, + {5226,711,5601 ,5652,5654,5653 }, {5308,505,5309 ,3528,986,987 }, + {5325,5297,5298 ,5655,3826,3818 }, {5379,4931,4916 ,3998,5066,2248 }, + {5200,5224,4679 ,5656,3850,3852 }, {4558,4631,4598 ,4209,1816,5343 }, + {8090,8080,8132 ,412,1094,413 }, {2320,2319,8049 ,1406,3228,5393 }, + {6256,5609,5953 ,5657,5658,5659 }, {5394,3499,405 ,5660,5661,5662 }, + {758,5361,3525 ,5663,5664,5665 }, {6243,6244,5356 ,5666,5667,4258 }, + {1158,253,249 ,2744,2564,644 }, {5322,5321,5320 ,4297,5668,4464 }, + {6306,5943,6336 ,5669,5402,5670 }, {5122,5717,3507 ,4024,4023,5671 }, + {1661,3611,6111 ,5672,5673,5674 }, {4805,1791,4711 ,5376,5675,5358 }, + {3320,911,2163 ,5455,1159,243 }, {5186,3536,4881 ,5676,5644,2731 }, + {5165,5186,4881 ,4226,5676,2731 }, {4370,3403,3536 ,5677,5648,5644 }, + {5186,4370,3536 ,5676,5677,5644 }, {4370,5156,1690 ,5677,5678,5649 }, + {3403,4370,1690 ,5648,5677,5649 }, {5156,5529,5164 ,5678,5679,5650 }, + {1690,5156,5164 ,5649,5678,5650 }, {5529,1386,5164 ,5679,5680,5650 }, + {1386,5159,5226 ,5680,5681,5652 }, {5164,1386,5226 ,5650,5680,5652 }, + {5159,3840,5226 ,5681,5682,5652 }, {3840,4250,711 ,5682,4161,5654 }, + {5226,3840,711 ,5652,5682,5654 }, {711,4250,85 ,5654,4161,2504 }, + {3499,5394,405 ,5661,5660,5662 }, {6245,6246,6273 ,5560,5559,5683 }, + {6246,6274,6273 ,5559,5684,5683 }, {6111,6063,5575 ,5674,5685,5686 }, + {1451,1538,2954 ,175,0,5687 }, {4490,4273,2954 ,5688,120,5687 }, + {542,1451,2954 ,122,175,5687 }, {5114,2291,2342 ,5689,1115,5690 }, + {1561,407,1525 ,1865,4018,1866 }, {4860,4635,5092 ,5531,5487,3953 }, + {3817,4682,5153 ,1189,1191,3896 }, {5960,5515,6005 ,5568,5567,5691 }, + {747,5782,5781 ,5692,5693,5694 }, {1741,6104,6230 ,5695,5696,2144 }, + {6055,3906,5129 ,3874,5697,5698 }, {2534,368,198 ,2846,1396,1392 }, + {6274,3666,5880 ,5684,5699,5700 }, {5296,5323,5322 ,4296,5701,4297 }, + {5575,202,5958 ,5686,4105,5702 }, {6042,5230,5921 ,5703,5704,5705 }, + {202,5122,5958 ,4105,4024,5702 }, {5898,5653,6115 ,5706,3644,3645 }, + {7487,7516,7540 ,5707,5708,5709 }, {5156,1386,5529 ,5678,5680,5679 }, + {1386,3840,5159 ,5680,5682,5681 }, {5139,5146,5260 ,1939,1075,4174 }, + {6053,5874,5925 ,4463,4896,5710 }, {4153,4374,3953 ,5711,5712,5713 }, + {5891,3822,5602 ,5714,5715,5716 }, {6052,5486,3311 ,5717,5718,5303 }, + {5948,5602,3507 ,5719,5716,5671 }, {5717,5948,3507 ,4023,5719,5671 }, + {5774,5750,5775 ,5720,5156,5158 }, {5808,5807,5774 ,5721,4769,5720 }, + {5775,5808,5774 ,5158,5721,5720 }, {5808,5840,5807 ,5721,4770,4769 }, + {4273,542,2954 ,120,122,5687 }, {4648,1125,1269 ,5722,5723,5724 }, + {7846,7845,7793 ,4162,5725,5726 }, {4859,5501,5165 ,5039,5727,4226 }, + {5026,4859,5165 ,4225,5039,4226 }, {5501,5537,5165 ,5727,5728,4226 }, + {2799,5186,5165 ,5729,5676,4226 }, {5537,2799,5165 ,5728,5729,4226 }, + {2799,5313,4370 ,5729,5730,5677 }, {5186,2799,4370 ,5676,5729,5677 }, + {5313,5358,5156 ,5730,5731,5678 }, {4370,5313,5156 ,5677,5730,5678 }, + {5358,1533,5156 ,5731,5732,5678 }, {1533,3857,1386 ,5732,5733,5680 }, + {5156,1533,1386 ,5678,5732,5680 }, {3857,5270,3840 ,5733,5734,5682 }, + {1386,3857,3840 ,5680,5733,5682 }, {4404,4250,3840 ,5072,4161,5682 }, + {5270,4404,3840 ,5734,5072,5682 }, {5200,4681,5224 ,5656,4958,3850 }, + {4410,6119,5303 ,5735,5736,5737 }, {3845,3497,5319 ,3635,5738,5739 }, + {2238,1272,1268 ,2697,5740,4568 }, {5647,5689,5688 ,5333,5741,5519 }, + {5689,5739,5738 ,5741,5157,5520 }, {5739,5750,5738 ,5157,5156,5520 }, + {5776,5775,5739 ,5742,5158,5157 }, {5809,5808,5775 ,5743,5721,5158 }, + {5776,5809,5775 ,5742,5743,5158 }, {5809,5840,5808 ,5743,4770,5721 }, + {5461,5962,5252 ,5744,5745,5746 }, {2004,1937,4490 ,5747,59,5688 }, + {6235,6165,5703 ,4215,4219,5748 }, {4766,4826,4765 ,5449,5413,3983 }, + {7794,7846,7793 ,5749,4162,5726 }, {3857,4015,5270 ,5733,5750,5734 }, + {4753,4696,4793 ,5383,5382,5421 }, {6418,5090,5091 ,3441,4929,5133 }, + {4653,5182,4599 ,4347,4567,4210 }, {5368,5888,6077 ,5751,5752,5753 }, + {3562,3856,6037 ,4753,5754,5235 }, {6244,6272,5663 ,5667,5755,4259 }, + {6240,6268,6254 ,5756,5757,5758 }, {2048,2015,2049 ,5461,759,758 }, + {5566,5581,5605 ,5759,5760,5761 }, {5647,5664,5689 ,5333,5762,5741 }, + {5841,5840,5809 ,5763,4770,5743 }, {1293,932,532 ,5764,5765,5766 }, + {4271,932,1464 ,5767,5765,5768 }, {1937,4273,4490 ,59,120,5688 }, + {4711,1791,4771 ,5358,5675,3622 }, {1928,5501,4859 ,1282,5727,5039 }, + {5501,2799,5537 ,5727,5729,5728 }, {5313,1533,5358 ,5730,5732,5731 }, + {5260,1868,2217 ,4174,2249,4569 }, {5902,686,6093 ,5769,5770,5771 }, + {3583,5391,5652 ,5772,5773,5774 }, {6046,5713,5686 ,5775,5776,5777 }, + {5935,3560,6218 ,5778,5779,5780 }, {5504,5541,5566 ,5781,5782,5759 }, + {5531,5504,5566 ,5783,5781,5759 }, {5566,5541,5581 ,5759,5782,5760 }, + {5541,5582,5605 ,5782,5784,5761 }, {5581,5541,5605 ,5760,5782,5761 }, + {5582,5622,5647 ,5784,5785,5333 }, {5605,5582,5647 ,5761,5784,5333 }, + {5647,5622,5664 ,5333,5785,5762 }, {5622,5690,5689 ,5785,5786,5741 }, + {5664,5622,5689 ,5762,5785,5741 }, {5690,5740,5739 ,5786,5787,5157 }, + {5689,5690,5739 ,5741,5786,5157 }, {5777,5776,5739 ,5788,5742,5157 }, + {5740,5777,5739 ,5787,5788,5157 }, {5810,5809,5776 ,5789,5743,5742 }, + {5777,5810,5776 ,5788,5789,5742 }, {5842,5841,5809 ,5790,5763,5743 }, + {5810,5842,5809 ,5789,5790,5743 }, {5842,5840,5841 ,5790,4770,5763 }, + {1246,4271,5840 ,5791,5767,4770 }, {5842,1246,5840 ,5790,5791,4770 }, + {532,932,4271 ,5766,5765,5767 }, {1246,532,4271 ,5791,5766,5767 }, + {1689,1937,2004 ,5792,59,5747 }, {4743,5732,5701 ,3629,3940,3938 }, + {489,4326,3761 ,3639,219,218 }, {4744,5049,4859 ,4424,5038,5039 }, + {4096,2799,5501 ,5793,5729,5727 }, {1928,4096,5501 ,1282,5793,5727 }, + {4096,3876,5313 ,5793,5794,5730 }, {2799,4096,5313 ,5729,5793,5730 }, + {3876,5155,1533 ,5794,5795,5732 }, {5313,3876,1533 ,5730,5794,5732 }, + {5155,5620,3857 ,5795,5796,5733 }, {1533,5155,3857 ,5732,5795,5733 }, + {5620,5344,4015 ,5796,5797,5750 }, {3857,5620,4015 ,5733,5796,5750 }, + {5344,4849,5270 ,5797,5436,5734 }, {4015,5344,5270 ,5750,5797,5734 }, + {4849,4433,4404 ,5436,4591,5072 }, {5270,4849,4404 ,5734,5436,5072 }, + {1791,4805,3367 ,5675,5376,5798 }, {5391,3583,3608 ,5773,5772,5799 }, + {3498,5960,6005 ,5800,5568,5691 }, {5652,6046,5686 ,5774,5775,5777 }, + {5463,5505,5504 ,5801,5802,5781 }, {5505,5541,5504 ,5802,5782,5781 }, + {5582,5606,5622 ,5784,5803,5785 }, {5690,5741,5740 ,5786,5804,5787 }, + {5857,1246,5842 ,5805,5791,5790 }, {5155,5344,5620 ,5795,5797,5796 }, + {5290,5308,5289 ,5247,3528,80 }, {4793,5770,5732 ,5421,3949,3940 }, + {4743,4793,5732 ,3629,5421,3940 }, {4797,4765,5259 ,3984,3983,3794 }, + {4715,4714,4681 ,1029,1031,4958 }, {3498,5478,5960 ,5800,5806,5568 }, + {2568,5391,3608 ,5807,5773,5799 }, {5478,6056,6045 ,5806,5808,935 }, + {5880,6137,6136 ,5700,5809,5810 }, {5663,5880,6136 ,4259,5700,5810 }, + {5427,5428,5463 ,5811,5812,5801 }, {5463,5485,5505 ,5801,5813,5802 }, + {5741,5778,5777 ,5804,5814,5788 }, {5740,5741,5777 ,5787,5804,5788 }, + {5778,5810,5777 ,5814,5789,5788 }, {421,1293,746 ,5815,5764,2661 }, + {920,1191,3986 ,57,5816,5817 }, {495,4776,3788 ,4389,5390,4410 }, + {5188,6744,5786 ,4812,3986,4813 }, {3876,4205,5155 ,5794,5818,5795 }, + {3787,5918,5562 ,5819,5820,4193 }, {5224,5267,5244 ,3850,4647,79 }, + {1191,920,1689 ,5816,57,5792 }, {3367,920,3986 ,5798,57,5817 }, + {5960,5478,6045 ,5568,5806,935 }, {5894,2568,3608 ,5821,5807,5799 }, + {6137,3989,4028 ,5809,5822,5823 }, {5391,6046,5652 ,5773,5775,5774 }, + {6136,6137,4028 ,5810,5809,5823 }, {5429,5464,5463 ,5824,5825,5801 }, + {5428,5429,5463 ,5812,5824,5801 }, {5463,5464,5485 ,5801,5825,5813 }, + {5464,5506,5505 ,5825,5826,5802 }, {5485,5464,5505 ,5813,5825,5802 }, + {5506,5542,5541 ,5826,5827,5782 }, {5505,5506,5541 ,5802,5826,5782 }, + {5542,5543,5541 ,5827,5828,5782 }, {5543,5583,5582 ,5828,5829,5784 }, + {5541,5543,5582 ,5782,5828,5784 }, {5582,5583,5606 ,5784,5829,5803 }, + {5583,5623,5622 ,5829,5830,5785 }, {5606,5583,5622 ,5803,5829,5785 }, + {5623,5665,5622 ,5830,5831,5785 }, {5622,5665,5690 ,5785,5831,5786 }, + {5665,5707,5690 ,5831,5832,5786 }, {5690,5707,5741 ,5786,5832,5804 }, + {5707,5779,5778 ,5832,5833,5814 }, {5741,5707,5778 ,5804,5832,5814 }, + {5811,5810,5778 ,5834,5789,5814 }, {5779,5811,5778 ,5833,5834,5814 }, + {5811,5822,5810 ,5834,5835,5789 }, {5843,5842,5810 ,1372,5790,5789 }, + {5822,5843,5810 ,5835,1372,5789 }, {2958,5857,5842 ,1374,5805,5790 }, + {5843,2958,5842 ,1372,1374,5790 }, {1662,1246,5857 ,5836,5791,5805 }, + {2958,1662,5857 ,1374,5836,5805 }, {746,532,1246 ,2661,5766,5791 }, + {1662,746,1246 ,5836,2661,5791 }, {3367,3986,2953 ,5798,5817,5837 }, + {1791,3367,2953 ,5675,5798,5837 }, {556,455,1344 ,4043,2205,300 }, + {994,4096,1928 ,5838,5793,1282 }, {5197,994,1928 ,4842,5838,1282 }, + {994,5421,4096 ,5838,5839,5793 }, {5421,4529,3876 ,5839,5840,5794 }, + {4096,5421,3876 ,5793,5839,5794 }, {4529,3865,4205 ,5840,5841,5818 }, + {3876,4529,4205 ,5794,5840,5818 }, {3865,5357,5155 ,5841,5842,5795 }, + {4205,3865,5155 ,5818,5841,5795 }, {5357,5154,5155 ,5842,5843,5795 }, + {5154,4345,5344 ,5843,5844,5797 }, {5155,5154,5344 ,5795,5843,5797 }, + {4345,4820,4849 ,5844,2399,5436 }, {5344,4345,4849 ,5797,5844,5436 }, + {5337,4914,4866 ,572,65,3641 }, {5290,5289,5244 ,5247,80,79 }, + {3875,3184,2048 ,5845,4453,5461 }, {920,1937,1689 ,57,59,5792 }, + {4028,3989,5193 ,5823,5822,5846 }, {3989,5252,5193 ,5822,5746,5846 }, + {5395,5429,5428 ,5348,5824,5812 }, {5506,5543,5542 ,5826,5828,5827 }, + {5665,5666,5707 ,5831,5847,5832 }, {5811,5843,5822 ,5834,1372,5835 }, + {2958,746,1662 ,1374,2661,5836 }, {3446,5884,3781 ,3718,5848,5849 }, + {6344,4568,4548 ,1450,1492,589 }, {5421,3865,4529 ,5839,5841,5840 }, + {6749,6785,5548 ,4046,4140,1590 }, {5244,5221,5222 ,79,4864,4566 }, + {6056,5883,6080 ,5808,5850,936 }, {5904,1124,5849 ,5851,5852,5853 }, + {5252,5962,4466 ,5746,5745,5854 }, {5121,5167,5120 ,5855,5856,5857 }, + {5167,5190,5166 ,5856,5858,3427 }, {1791,2953,5836 ,5675,5837,5859 }, + {4679,5223,5182 ,3852,3851,4567 }, {5288,5287,5243 ,3993,4003,3994 }, + {4932,4931,5379 ,4580,5066,3998 }, {5515,5898,6115 ,5567,5706,3645 }, + {5117,5515,6115 ,1113,5567,3645 }, {4466,3469,6104 ,5854,5860,5696 }, + {5396,5430,5429 ,3548,5861,5824 }, {5395,5396,5429 ,5348,3548,5824 }, + {5430,5465,5464 ,5861,5862,5825 }, {5429,5430,5464 ,5824,5861,5825 }, + {5465,5507,5506 ,5862,5863,5826 }, {5464,5465,5506 ,5825,5862,5826 }, + {5507,5532,5506 ,5863,5864,5826 }, {5532,5544,5543 ,5864,5865,5828 }, + {5506,5532,5543 ,5826,5864,5828 }, {5544,5584,5583 ,5865,5866,5829 }, + {5543,5544,5583 ,5828,5865,5829 }, {5583,5584,5623 ,5829,5866,5830 }, + {5623,5666,5665 ,5830,5847,5831 }, {5592,746,2224 ,3671,2661,2660 }, + {7538,7573,7602 ,2014,2013,5867 }, {5421,994,3865 ,5839,5838,5841 }, + {4669,4896,4775 ,3879,3878,4388 }, {6045,6056,6080 ,935,5808,936 }, + {5883,5954,5453 ,5850,5868,937 }, {6080,5883,5453 ,936,5850,937 }, + {5954,3657,5923 ,5868,5869,2073 }, {5386,3905,3892 ,5870,4194,4970 }, + {4281,615,1330 ,51,766,53 }, {5167,5205,5190 ,5856,5219,5858 }, + {4771,1791,5836 ,3622,5675,5859 }, {5804,4771,5803 ,3623,3622,5871 }, + {5453,5954,5923 ,937,5868,2073 }, {5461,5984,6113 ,5744,5872,5873 }, + {5894,3608,6113 ,5821,5799,5873 }, {5584,5624,5623 ,5866,5874,5830 }, + {5624,5648,5623 ,5874,5875,5830 }, {5648,5667,5666 ,5875,5876,5847 }, + {5623,5648,5666 ,5830,5875,5847 }, {5667,5708,5707 ,5876,5877,5832 }, + {5666,5667,5707 ,5847,5876,5832 }, {5708,5751,5707 ,5877,5878,5832 }, + {5751,5779,5707 ,5878,5833,5832 }, {5751,5812,5811 ,5878,660,5834 }, + {5779,5751,5811 ,5833,5878,5834 }, {5844,5843,5811 ,5179,1372,5834 }, + {5812,5844,5811 ,660,5179,5834 }, {4724,4908,5245 ,4737,5879,5880 }, + {5984,5894,6113 ,5872,5821,5873 }, {3657,6178,5923 ,5869,5881,2073 }, + {3830,5196,5197 ,5882,5883,4842 }, {4518,3830,5197 ,1281,5882,4842 }, + {4372,994,5197 ,5884,5838,4842 }, {5196,4372,5197 ,5883,5884,4842 }, + {4372,5425,3865 ,5884,5885,5841 }, {994,4372,3865 ,5838,5884,5841 }, + {5425,3083,5357 ,5885,5886,5842 }, {3865,5425,5357 ,5841,5885,5842 }, + {5357,3083,5154 ,5842,5886,5843 }, {3083,1020,4345 ,5886,3817,5844 }, + {5154,3083,4345 ,5843,5886,5844 }, {1020,4712,4820 ,3817,3816,2399 }, + {4345,1020,4820 ,5844,3817,2399 }, {4775,4896,4861 ,4388,3878,3877 }, + {3575,3584,5293 ,5887,5888,5889 }, {3576,5294,5912 ,5890,5891,5892 }, + {2408,309,5231 ,1399,4472,115 }, {4687,320,274 ,4985,1871,1819 }, + {5768,5804,5767 ,933,3623,5893 }, {6178,4901,6162 ,5881,3596,3598 }, + {5923,6178,6162 ,2073,5881,3598 }, {3366,3310,5461 ,5894,5587,5744 }, + {3310,5984,5461 ,5587,5872,5744 }, {7043,2279,7042 ,5895,5337,5338 }, + {5050,3244,1922 ,5233,4433,4432 }, {1196,4669,3266 ,4330,3879,3884 }, + {5989,3310,3366 ,5588,5587,5894 }, {6068,5989,3366 ,5896,5588,5894 }, + {772,1605,713 ,18,17,3 }, {6132,3628,3632 ,5897,4213,5898 }, + {5109,5121,5107 ,4182,5855,4183 }, {5109,5147,5121 ,4182,5899,5855 }, + {5168,5167,5121 ,5900,5856,5855 }, {5147,5168,5121 ,5899,5900,5855 }, + {5206,5205,5167 ,5901,5219,5856 }, {5168,5206,5167 ,5900,5901,5856 }, + {5206,5229,5205 ,5901,5902,5219 }, {7310,7370,7344 ,4283,3750,2749 }, + {5229,4178,5205 ,5902,3825,5219 }, {5305,5306,5572 ,2283,462,2284 }, + {75,1629,2938 ,3962,4207,4320 }, {5976,6028,5927 ,5903,5316,5318 }, + {5364,5397,5396 ,3268,5904,3548 }, {5363,5364,5396 ,3269,3268,3548 }, + {5397,5431,5430 ,5904,5905,5861 }, {5396,5397,5430 ,3548,5904,5861 }, + {5431,5466,5465 ,5905,5906,5862 }, {5430,5431,5465 ,5861,5905,5862 }, + {5466,5467,5465 ,5906,5907,5862 }, {5467,5508,5507 ,5907,5908,5863 }, + {5465,5467,5507 ,5862,5907,5863 }, {5507,5508,5532 ,5863,5908,5864 }, + {5508,5545,5544 ,5908,5909,5865 }, {5532,5508,5544 ,5864,5908,5865 }, + {5545,5585,5584 ,5909,5910,5866 }, {5544,5545,5584 ,5865,5909,5866 }, + {5585,5625,5624 ,5910,5911,5874 }, {5584,5585,5624 ,5866,5910,5874 }, + {5624,5625,5648 ,5874,5911,5875 }, {5625,5668,5667 ,5911,5912,5876 }, + {5648,5625,5667 ,5875,5911,5876 }, {5668,5709,5708 ,5912,5253,5877 }, + {5667,5668,5708 ,5876,5912,5877 }, {5708,5709,5751 ,5877,5253,5878 }, + {1557,5989,6068 ,5577,5588,5896 }, {5812,7038,5823 ,660,3832,661 }, + {3666,1557,6068 ,5699,5577,5896 }, {5115,6060,6281 ,5913,5914,4078 }, + {2279,1313,2251 ,5337,3674,3676 }, {3943,5662,4045 ,4755,5915,5916 }, + {5255,5276,6480 ,3682,1525,1524 }, {3669,1196,3266 ,5917,4330,3884 }, + {6239,6055,6073 ,5918,3874,3873 }, {6238,6239,6073 ,5919,5918,3873 }, + {3469,5651,6104 ,5860,5920,5696 }, {5651,5254,6104 ,5920,4042,5696 }, + {5403,3631,5417 ,5921,5922,5923 }, {5489,5911,5932 ,4244,5924,5925 }, + {5474,5370,5932 ,4243,2171,5925 }, {5804,5803,5767 ,3623,5871,5893 }, + {5108,5107,5106 ,4184,4183,5926 }, {5169,5168,5147 ,5927,5900,5899 }, + {5109,5169,5147 ,4182,5927,5899 }, {5206,4178,5229 ,5901,3825,5902 }, + {2713,3141,4091 ,4352,4701,123 }, {4771,5836,5803 ,3622,5859,5871 }, + {5585,5607,5625 ,5910,5928,5911 }, {5709,5752,5751 ,5253,4374,5878 }, + {6268,1661,3906 ,5757,5672,5697 }, {5261,5538,3830 ,173,5929,5882 }, + {5538,3417,5196 ,5929,5930,5883 }, {3830,5538,5196 ,5882,5929,5883 }, + {5500,4372,5196 ,5931,5884,5883 }, {3417,5500,5196 ,5930,5931,5883 }, + {5500,5821,5425 ,5931,5932,5885 }, {4372,5500,5425 ,5884,5931,5885 }, + {5821,4072,5425 ,5932,5933,5885 }, {4072,1938,3083 ,5933,5934,5886 }, + {5425,4072,3083 ,5885,5933,5886 }, {1938,4027,1020 ,5934,3470,3817 }, + {3083,1938,1020 ,5886,5934,3817 }, {5593,5552,5594 ,5935,5936,5937 }, + {3321,7437,7386 ,2461,5938,5512 }, {3899,1196,3669 ,1938,4330,5917 }, + {5185,3395,6142 ,5939,5940,5941 }, {5579,5564,4565 ,5942,5943,5944 }, + {5169,5206,5168 ,5927,5901,5900 }, {5321,5364,5346 ,5668,3268,3267 }, + {5364,5398,5397 ,3268,5945,5904 }, {5398,5432,5431 ,5945,5946,5905 }, + {5397,5398,5431 ,5904,5945,5905 }, {5432,5467,5466 ,5946,5907,5906 }, + {5431,5432,5466 ,5905,5946,5906 }, {6905,6957,5710 ,2506,4315,5254 }, + {7268,7317,7316 ,3773,5947,5948 }, {8078,8124,8076 ,1744,1141,617 }, + {4572,3693,3638 ,5949,5347,5950 }, {5390,5579,5415 ,3205,5942,5951 }, + {4763,4764,4797 ,4787,3982,3984 }, {5148,5169,5109 ,5952,5927,4182 }, + {5110,5148,5109 ,5953,5952,4182 }, {5207,5206,5169 ,5954,5901,5927 }, + {5148,5207,5169 ,5952,5954,5927 }, {5249,4178,5206 ,5955,3825,5901 }, + {5207,5249,5206 ,5954,5955,5901 }, {5249,5272,4178 ,5955,5956,3825 }, + {5296,5297,5323 ,4296,3826,5701 }, {5347,5321,5322 ,5957,5668,4297 }, + {5323,5347,5322 ,5701,5957,4297 }, {5365,5364,5321 ,5958,3268,5668 }, + {5347,5365,5321 ,5957,5958,5668 }, {5365,5399,5398 ,5958,5959,5945 }, + {5364,5365,5398 ,3268,5958,5945 }, {5399,5433,5432 ,5959,5960,5946 }, + {5398,5399,5432 ,5945,5959,5946 }, {5433,5468,5467 ,5960,5961,5907 }, + {5432,5433,5467 ,5946,5960,5907 }, {5468,5509,5508 ,5961,5962,5908 }, + {5467,5468,5508 ,5907,5961,5908 }, {5509,5546,5545 ,5962,5963,5909 }, + {5508,5509,5545 ,5908,5962,5909 }, {5546,5586,5585 ,5963,1461,5910 }, + {5545,5546,5585 ,5909,5963,5910 }, {5585,5586,5607 ,5910,1461,5928 }, + {5586,5626,5625 ,1461,1460,5911 }, {5607,5586,5625 ,5928,1461,5911 }, + {5625,5626,5668 ,5911,1460,5912 }, {5668,5669,5709 ,5912,5553,5253 }, + {5293,3558,3629 ,5889,5964,5965 }, {5731,5768,5729 ,934,933,200 }, + {5768,5767,5729 ,933,5893,200 }, {5326,6041,5327 ,3714,3801,3702 }, + {5146,3760,1868 ,1075,2250,2249 }, {5731,5729,5730 ,934,200,199 }, + {5731,5730,3615 ,934,199,201 }, {3952,2713,3259 ,5597,4352,407 }, + {1156,5138,5128 ,3100,1940,5966 }, {5500,4072,5821 ,5931,5933,5932 }, + {7837,4897,3895 ,5967,5442,4455 }, {3760,3899,3669 ,2250,1938,5917 }, + {184,4295,4016 ,4052,5968,5969 }, {1064,5579,5390 ,5970,5942,3205 }, + {7166,5110,5109 ,5971,5953,4182 }, {5546,5547,5586 ,5963,5972,1461 }, + {6234,6167,6002 ,5973,5974,5975 }, {5243,5287,5265 ,3994,4003,4882 }, + {3221,5262,5261 ,183,5976,173 }, {5352,3950,3345 ,3502,4530,3476 }, + {5262,5527,5538 ,5976,5977,5929 }, {5261,5262,5538 ,173,5976,5929 }, + {5527,5746,3417 ,5977,5978,5930 }, {5538,5527,3417 ,5929,5977,5930 }, + {5619,5500,3417 ,5979,5931,5930 }, {5746,5619,3417 ,5978,5979,5930 }, + {5619,1671,4072 ,5979,5980,5933 }, {5500,5619,4072 ,5931,5979,5933 }, + {1671,3249,1938 ,5980,5110,5934 }, {4072,1671,1938 ,5933,5980,5934 }, + {572,4873,611 ,1947,4992,1948 }, {6128,1620,6129 ,5981,5982,5983 }, + {4299,1064,5390 ,5984,5970,3205 }, {5446,4299,5390 ,5985,5984,3205 }, + {4299,5579,1064 ,5984,5942,5970 }, {4299,1002,5579 ,5984,2976,5942 }, + {5323,5324,5348 ,5701,5986,5987 }, {5509,5547,5546 ,5962,5972,5963 }, + {4827,5259,4826 ,2734,3794,5413 }, {3249,4905,4594 ,5110,5112,688 }, + {5272,5298,5297 ,5956,3818,3826 }, {5619,5735,1671 ,5979,5988,5980 }, + {6803,5587,5548 ,4169,1459,1590 }, {4863,5378,4904 ,5480,359,3309 }, + {5260,5146,1868 ,4174,1075,2249 }, {5149,5148,5110 ,5989,5952,5953 }, + {5111,5149,5110 ,5990,5989,5953 }, {5208,5207,5148 ,5991,5954,5952 }, + {5149,5208,5148 ,5989,5991,5952 }, {5250,5249,5207 ,5992,5955,5954 }, + {5208,5250,5207 ,5991,5992,5954 }, {5273,5272,5249 ,3819,5956,5955 }, + {5250,5273,5249 ,5992,3819,5955 }, {7683,7704,7682 ,5267,3562,3564 }, + {5348,5347,5323 ,5987,5957,5701 }, {5325,5323,5297 ,5655,5701,3826 }, + {5366,5365,5347 ,5993,5958,5957 }, {5348,5366,5347 ,5987,5993,5957 }, + {5366,5400,5399 ,5993,5994,5959 }, {5365,5366,5399 ,5958,5993,5959 }, + {5400,5434,5433 ,5994,5995,5960 }, {5399,5400,5433 ,5959,5994,5960 }, + {5434,5469,5468 ,5995,5996,5961 }, {5433,5434,5468 ,5960,5995,5961 }, + {5469,5510,5509 ,5996,5997,5962 }, {5468,5469,5509 ,5961,5996,5962 }, + {5510,5511,5509 ,5997,1588,5962 }, {5511,5548,5547 ,1588,1590,5972 }, + {5509,5511,5547 ,5962,1588,5972 }, {5548,5587,5586 ,1590,1459,1461 }, + {5547,5548,5586 ,5972,1590,1461 }, {5860,2321,5951 ,5998,5999,6000 }, + {3620,5991,5946 ,6001,5482,5481 }, {6907,6959,6958 ,6002,6003,4314 }, + {4696,5770,4793 ,5382,3949,5421 }, {6163,5889,6175 ,1453,1587,6004 }, + {5128,2217,5443 ,5966,4569,6005 }, {5266,5243,5221 ,81,3994,4864 }, + {5340,4868,5379 ,4002,3969,3998 }, {5223,5244,5222 ,3851,79,4566 }, + {3221,5527,5262 ,183,5977,5976 }, {3278,8113,1724 ,1158,1157,3880 }, + {5010,2140,5426 ,6006,5635,5634 }, {1166,1119,5844 ,2746,5180,5179 }, + {2375,3945,922 ,4479,4550,117 }, {1116,3350,5497 ,1401,2541,1375 }, + {1488,4264,156 ,5197,5540,5539 }, {3973,4299,5446 ,6007,5984,5985 }, + {5209,5208,5149 ,6008,5991,5989 }, {7566,7621,7565 ,6009,6010,5080 }, + {7621,7647,7620 ,6010,4967,5033 }, {5324,5366,5348 ,5986,5993,5987 }, + {5469,5511,5510 ,5996,1588,5997 }, {6208,6209,6243 ,6011,6012,5666 }, + {5933,3548,5934 ,615,6013,616 }, {3548,5489,5934 ,6013,4244,616 }, + {4278,4260,6138 ,5583,3527,6014 }, {3878,789,3419 ,1248,3910,2012 }, + {1119,1166,2408 ,5180,2746,1399 }, {156,22,1297 ,5539,4066,4048 }, + {5325,5298,6041 ,5655,3818,3801 }, {156,1934,22 ,5539,4620,4066 }, + {1218,3820,5498 ,3255,5591,5590 }, {6736,6754,6703 ,1622,713,5161 }, + {7923,7896,7897 ,816,4355,6015 }, {5772,5746,5527 ,6016,5978,5977 }, + {3984,5772,5527 ,6017,6016,5977 }, {5772,5577,5619 ,6016,6018,5979 }, + {5746,5772,5619 ,5978,6016,5979 }, {5577,4228,5735 ,6018,6019,5988 }, + {5619,5577,5735 ,5979,6018,5988 }, {4228,1099,1671 ,6019,4350,5980 }, + {5735,4228,1671 ,5988,6019,5980 }, {1099,4905,3249 ,4350,5112,5110 }, + {1671,1099,3249 ,5980,4350,5110 }, {3766,7556,4636 ,3954,5116,5118 }, + {3249,5036,4905 ,5110,399,5112 }, {3941,4299,3973 ,5563,5984,6007 }, + {636,3941,3973 ,6020,5563,6007 }, {3941,1511,4299 ,5563,4921,5984 }, + {5158,4631,4599 ,4832,1816,4210 }, {5112,5149,5111 ,6021,5989,5990 }, + {5273,5250,5251 ,3819,5992,3802 }, {2690,303,4296 ,6022,4086,6023 }, + {4191,2690,4296 ,4474,6022,6023 }, {6151,5860,5951 ,6024,5998,6000 }, + {6035,5654,4045 ,6025,6026,5916 }, {1741,6230,5947 ,5695,2144,6027 }, + {5948,5891,5602 ,5719,5714,5716 }, {5979,5929,6132 ,6028,6029,5897 }, + {798,5378,4863 ,78,359,5480 }, {8,1798,4522 ,3341,4451,3481 }, + {5218,5239,5238 ,483,482,4351 }, {5179,5199,5178 ,1879,422,524 }, + {5179,5178,5136 ,1879,524,1817 }, {2797,1935,303 ,6030,4098,4086 }, + {2690,2797,303 ,6022,6030,4086 }, {3150,636,2960 ,6031,6020,4975 }, + {5388,2960,636 ,5428,4975,6020 }, {5150,5149,5112 ,6032,5989,6021 }, + {5113,5150,5112 ,141,6032,6021 }, {5150,5170,5149 ,6032,3683,5989 }, + {5210,5209,5149 ,6033,6008,5989 }, {5170,5210,5149 ,3683,6033,5989 }, + {5210,5208,5209 ,6033,5991,6008 }, {5251,5250,5208 ,3802,5992,5991 }, + {5210,5251,5208 ,6033,3802,5991 }, {3985,3172,1935 ,4802,4155,4098 }, + {2797,3985,1935 ,6030,4802,4098 }, {5349,5324,5325 ,5320,5986,5655 }, + {3482,8106,3449 ,2494,2438,2440 }, {5367,5366,5324 ,6034,5993,5986 }, + {5349,5367,5324 ,5320,6034,5986 }, {5367,5401,5400 ,6034,4145,5994 }, + {5366,5367,5400 ,5993,6034,5994 }, {5401,5435,5434 ,4145,4223,5995 }, + {5400,5401,5434 ,5994,4145,5995 }, {5435,5470,5469 ,4223,6035,5996 }, + {5434,5435,5469 ,5995,4223,5996 }, {5470,5471,5469 ,6035,2051,5996 }, + {5469,5471,5511 ,5996,2051,1588 }, {5652,5686,5651 ,5774,5777,5920 }, + {5651,5747,5254 ,5920,6036,4042 }, {3525,5361,5935 ,5665,5664,5778 }, + {6051,5979,6132 ,6037,6028,5897 }, {6040,6028,5246 ,5317,5316,5176 }, + {5853,5318,6023 ,6038,6039,6040 }, {3731,5922,6075 ,6041,4292,6042 }, + {4654,5200,4679 ,4348,5656,3852 }, {4866,4914,3907 ,3641,65,3883 }, + {4916,4929,4915 ,2248,5142,64 }, {1525,2086,4314 ,1866,1802,1864 }, + {3985,3858,3093 ,4802,4803,4172 }, {3172,3985,3093 ,4155,4802,4172 }, + {5772,4228,5577 ,6016,6019,6018 }, {495,4775,4745 ,4389,4388,5446 }, + {5052,4845,5070 ,5189,3921,6043 }, {789,1156,3858 ,3910,3100,4803 }, + {3858,5128,5119 ,4803,5966,4190 }, {3093,3858,5119 ,4172,4803,4190 }, + {59,145,2689 ,967,810,2821 }, {5435,5471,5470 ,4223,2051,6035 }, + {6246,6247,3666 ,5559,5575,5699 }, {6166,6152,7034 ,4971,6044,6045 }, + {3835,5878,5550 ,6046,2684,6047 }, {6044,5980,5294 ,614,5557,5891 }, + {5889,6339,6034 ,1587,6048,6049 }, {3249,5028,1938 ,5110,5109,5934 }, + {6131,6132,3632 ,6050,5897,5898 }, {6185,5386,3892 ,6051,5870,4970 }, + {5749,5895,3444 ,6052,6053,6054 }, {5490,5749,3444 ,6055,6052,6054 }, + {5325,5326,5349 ,5655,3714,5320 }, {5644,5643,5571 ,4133,4617,4616 }, + {5495,5644,5571 ,4134,4133,4616 }, {5128,5443,5424 ,5966,6005,4227 }, + {3345,4226,835 ,3476,6056,3030 }, {5684,3345,835 ,4683,3476,3030 }, + {4226,1986,3984 ,6056,6057,6017 }, {835,4226,3984 ,3030,6056,6017 }, + {1986,2345,5772 ,6057,6058,6016 }, {3984,1986,5772 ,6017,6057,6016 }, + {2345,5187,4228 ,6058,5144,6019 }, {5772,2345,4228 ,6016,6058,6019 }, + {1111,1099,4228 ,3920,4350,6019 }, {5187,1111,4228 ,5144,3920,6019 }, + {68,2432,3818 ,4087,5427,1244 }, {5119,5128,5424 ,4190,5966,4227 }, + {5443,2217,201 ,6005,4569,4230 }, {5424,5443,201 ,4227,6005,4230 }, + {556,3150,2960 ,4043,6031,4975 }, {556,636,3150 ,4043,6020,6031 }, + {1344,3941,636 ,300,5563,6020 }, {556,1344,636 ,4043,300,6020 }, + {8020,8019,7972 ,1405,1407,6059 }, {2217,3038,2643 ,4569,4342,4232 }, + {201,2217,2643 ,4230,4569,4232 }, {7897,7896,7845 ,6015,4355,5725 }, + {3856,3562,6013 ,5754,4753,6060 }, {6247,1557,3666 ,5575,5577,5699 }, + {6098,6174,6173 ,1565,2381,3711 }, {5790,5813,5104 ,6061,6062,6063 }, + {5755,5790,5104 ,4047,6061,6063 }, {5086,5104,5813 ,6064,6063,6062 }, + {5813,5846,5086 ,6062,6065,6064 }, {5846,4225,257 ,6065,6066,6067 }, + {5714,5715,7028 ,4077,4076,3587 }, {4225,2871,257 ,6066,3649,6067 }, + {5461,3608,5962 ,5744,5799,5745 }, {5644,5660,5643 ,4133,3054,4617 }, + {3038,1341,2634 ,4342,3841,4269 }, {3345,4052,4226 ,3476,6068,6056 }, + {4226,4052,1986 ,6056,6068,6057 }, {1224,2986,1201 ,1508,3500,1477 }, + {3858,1156,5128 ,4803,3100,5966 }, {2643,3038,2634 ,4232,4342,4269 }, + {3788,3672,2634 ,4410,4277,4269 }, {1341,3788,2634 ,3841,4410,4269 }, + {5729,5728,5700 ,200,4486,4501 }, {210,194,262 ,4592,4593,4758 }, + {145,59,113 ,810,967,677 }, {4681,5200,4654 ,4958,5656,4348 }, + {3626,3580,3557 ,4196,6069,6070 }, {4894,1750,1810 ,746,745,779 }, + {4764,4763,4706 ,3982,4787,4786 }, {284,4086,4283 ,139,3723,1678 }, + {5211,5210,5170 ,6071,6033,3683 }, {7873,7923,7897 ,817,816,6015 }, + {5957,202,6063 ,4054,4105,5685 }, {8109,8116,5521 ,2938,172,751 }, + {1700,807,3563 ,6072,6073,6074 }, {3568,1941,3529 ,3839,1034,3837 }, + {5694,5716,5715 ,5326,6075,4076 }, {5716,5756,5755 ,6075,6076,4047 }, + {5715,5716,5755 ,4076,6075,4047 }, {5755,5756,5790 ,4047,6076,6061 }, + {5756,5791,5813 ,6076,6077,6062 }, {5790,5756,5813 ,6061,6076,6062 }, + {5791,5847,5846 ,6077,6078,6065 }, {5813,5791,5846 ,6062,6077,6065 }, + {5847,32,4225 ,6078,6079,6066 }, {5846,5847,4225 ,6065,6078,6066 }, + {32,181,2871 ,6079,6080,3649 }, {4225,32,2871 ,6066,6079,3649 }, + {5151,5125,5171 ,1803,5467,1804 }, {181,712,2871 ,6080,3650,3649 }, + {6242,6270,6269 ,6081,6082,6083 }, {1191,1689,1195 ,5816,5792,725 }, + {1689,4490,4347 ,5792,5688,3287 }, {1586,1191,1195 ,943,5816,725 }, + {612,611,4873 ,5011,1948,4992 }, {3615,5729,5700 ,201,200,4501 }, + {5295,556,808 ,2188,4043,4734 }, {2953,212,5835 ,5837,4614,4573 }, + {5836,2953,5835 ,5859,5837,4573 }, {4599,5182,5158 ,4210,4567,4832 }, + {4166,1016,1251 ,6084,6085,4604 }, {5788,5579,1002 ,6086,5942,2976 }, + {5316,5588,5550 ,6087,6088,6047 }, {6184,1170,1620 ,6089,4103,5982 }, + {6989,5694,5693 ,4586,5326,4075 }, {5716,5744,5756 ,6075,6090,6076 }, + {4106,5684,835 ,1742,4683,3030 }, {5924,6042,5865 ,6091,5703,6092 }, + {5649,5924,5865 ,6093,6091,6092 }, {6000,5851,3870 ,6094,6095,6096 }, + {4490,2954,3867 ,5688,5687,4507 }, {4347,4490,3867 ,3287,5688,4507 }, + {6644,4950,4981 ,5098,1302,1350 }, {3915,3939,4307 ,1575,3539,4562 }, + {3950,3799,4052 ,4530,4531,6068 }, {3345,3950,4052 ,3476,4530,6068 }, + {3799,3282,1986 ,4531,6097,6057 }, {4052,3799,1986 ,6068,4531,6057 }, + {3282,3769,2345 ,6097,6098,6058 }, {1986,3282,2345 ,6057,6097,6058 }, + {3769,5070,5187 ,6098,6043,5144 }, {2345,3769,5187 ,6058,6098,5144 }, + {4681,4714,5224 ,4958,1031,3850 }, {1005,68,3818 ,3535,4087,1244 }, + {5138,5260,5128 ,1940,4174,5966 }, {5767,5766,5728 ,5893,4487,4486 }, + {5729,5767,5728 ,200,5893,4486 }, {5143,1416,297 ,6099,6100,1918 }, + {1416,1455,140 ,6100,1917,1919 }, {4519,5202,1455 ,4746,5550,1917 }, + {1416,4519,1455 ,6100,4746,1917 }, {4519,4491,5202 ,4746,107,5550 }, + {4491,4472,808 ,107,2189,4734 }, {4710,4770,4769 ,4136,4345,5542 }, + {3580,3626,3605 ,6069,4196,3383 }, {5966,297,1455 ,5054,1918,1917 }, + {7745,7795,7744 ,650,3804,6101 }, {6255,6256,6275 ,6102,5657,5576 }, + {6247,6255,6275 ,5575,6102,5576 }, {5782,3836,3385 ,5693,6103,6104 }, + {2990,1519,601 ,3646,2450,3905 }, {6256,5953,1557 ,5657,5659,5577 }, + {6275,6256,1557 ,5576,5657,5577 }, {5756,5792,5791 ,6076,6105,6077 }, + {5852,2213,32 ,6106,6107,6079 }, {5847,5852,32 ,6078,6106,6079 }, + {4322,181,32 ,6108,6080,6079 }, {2213,4322,32 ,6107,6108,6079 }, + {5851,5881,3870 ,6095,6109,6096 }, {1700,3563,2896 ,6072,6074,6110 }, + {4770,5263,4830 ,4345,4302,3642 }, {309,2375,5231 ,4472,4479,115 }, + {4697,5099,3800 ,4327,5494,4325 }, {882,8117,3264 ,3846,4119,2468 }, + {3769,5736,5070 ,6098,6111,6043 }, {1937,4796,4273 ,59,4756,120 }, + {5441,5143,297 ,5194,6099,1918 }, {4447,5143,3399 ,717,6099,715 }, + {670,262,312 ,4760,4758,4759 }, {6158,6284,6034 ,6112,6113,6049 }, + {543,3266,495 ,3840,3884,4389 }, {7073,1166,5844 ,662,2746,5179 }, + {1689,2004,4490 ,5792,5747,5688 }, {5767,5803,5766 ,5893,5871,4487 }, + {5803,5802,5766 ,5871,4681,4487 }, {5819,5818,5802 ,6114,4640,4681 }, + {5803,5819,5802 ,5871,6114,4681 }, {5819,5836,5835 ,6114,5859,4573 }, + {5818,5819,5835 ,4640,6114,4573 }, {2953,3986,1208 ,5837,5817,3211 }, + {212,2953,1208 ,4614,5837,3211 }, {1191,1586,1208 ,5816,943,3211 }, + {3986,1191,1208 ,5817,5816,3211 }, {5953,3870,3310 ,5659,6096,5587 }, + {1557,5953,3310 ,5577,5659,5587 }, {5946,3415,5907 ,5481,6115,6116 }, + {3217,5008,2342 ,6117,6118,5690 }, {3870,3686,5984 ,6096,6119,5872 }, + {3310,3870,5984 ,5587,6096,5872 }, {3686,5915,5894 ,6119,6120,5821 }, + {5984,3686,5894 ,5872,6119,5821 }, {6091,5328,5384 ,2173,2172,6121 }, + {6003,6004,5871 ,6122,6123,4124 }, {5328,5301,5384 ,2172,2698,6121 }, + {3715,3018,5851 ,6124,6125,6095 }, {5718,5757,5756 ,6126,6127,6076 }, + {5943,5514,6009 ,5402,6128,6129 }, {5757,5793,5792 ,6127,6130,6105 }, + {5756,5757,5792 ,6076,6127,6105 }, {5793,5791,5792 ,6130,6077,6105 }, + {5793,5825,5847 ,6130,6131,6078 }, {5791,5793,5847 ,6077,6130,6078 }, + {5847,5825,5852 ,6078,6131,6106 }, {5825,445,2213 ,6131,6132,6107 }, + {5852,5825,2213 ,6106,6131,6107 }, {1456,4322,2213 ,6133,6108,6107 }, + {445,1456,2213 ,6132,6133,6107 }, {1456,181,4322 ,6133,6080,6108 }, + {852,712,181 ,6134,3650,6080 }, {1456,852,181 ,6133,6134,6080 }, + {852,4230,712 ,6134,6135,3650 }, {4249,3702,712 ,6136,1303,3650 }, + {4230,4249,712 ,6135,6136,3650 }, {3606,3607,4164 ,6137,6138,6139 }, + {7873,7846,7847 ,817,4162,3521 }, {5477,5789,6036 ,5399,6140,6141 }, + {5905,5528,5820 ,6142,6143,6144 }, {1538,1101,3867 ,0,2,4507 }, + {4805,4645,3367 ,5376,5378,5798 }, {6950,6644,4981 ,1349,5098,1350 }, + {5274,5300,5251 ,6145,3793,3802 }, {2158,6639,4983 ,1160,3699,480 }, + {6300,6301,6708 ,6146,6147,6148 }, {5342,5143,4447 ,6149,6099,717 }, + {5685,1416,5143 ,6150,6100,6099 }, {5036,3249,4905 ,399,5110,5112 }, + {4830,4829,4770 ,3642,4626,4345 }, {5615,3001,3274 ,3171,3210,3172 }, + {6223,6254,6055 ,6151,5758,3874 }, {5898,5662,5653 ,5706,5915,3644 }, + {6243,6271,6270 ,5666,6152,6082 }, {5610,5609,6248 ,6153,5658,6154 }, + {1028,5843,5844 ,1373,1372,5179 }, {5915,6072,2568 ,6120,6155,5807 }, + {5894,5915,2568 ,5821,6120,5807 }, {6207,6208,6242 ,6156,6011,6081 }, + {7621,7620,7565 ,6010,5033,5080 }, {5718,5719,5757 ,6126,6157,6127 }, + {4231,4249,4230 ,6158,6136,6135 }, {852,4231,4230 ,6134,6158,6135 }, + {7648,7647,7621 ,3952,4967,6010 }, {4231,1185,4249 ,6158,6159,6136 }, + {5915,5905,5820 ,6120,6142,6144 }, {5528,5712,5975 ,6143,6160,6161 }, + {1700,6266,3563 ,6072,399,6074 }, {180,8,131 ,4777,3341,2965 }, + {4400,637,3299 ,4721,3805,3232 }, {5975,5391,2568 ,6161,5773,5807 }, + {1410,5540,3893 ,3911,6162,3789 }, {6072,5975,2568 ,6155,6161,5807 }, + {5253,6046,5391 ,6163,5775,5773 }, {5353,1342,3799 ,851,6164,4531 }, + {5352,5353,3799 ,3502,851,4531 }, {1342,3255,3282 ,6164,6165,6097 }, + {3799,1342,3282 ,4531,6164,6097 }, {3255,1527,3877 ,6165,6166,6167 }, + {3282,3255,3877 ,6097,6165,6167 }, {3255,3282,3877 ,6165,6097,6167 }, + {1527,3255,3877 ,6166,6165,6167 }, {3255,4013,3769 ,6165,6168,6098 }, + {3282,3255,3769 ,6097,6165,6098 }, {4013,4220,5736 ,6168,6169,6111 }, + {3769,4013,5736 ,6098,6168,6111 }, {4220,5052,5070 ,6169,5189,6043 }, + {5736,4220,5070 ,6111,6169,6043 }, {3669,3266,1783 ,5917,3884,2251 }, + {3800,1248,1907 ,4325,2850,1245 }, {1524,3800,1907 ,4326,4325,1245 }, + {5685,774,1416 ,6150,767,6100 }, {5222,5221,5182 ,4566,4864,4567 }, + {2432,1524,3818 ,5427,4326,1244 }, {5128,5260,2217 ,5966,4174,4569 }, + {4883,4801,5127 ,5538,5115,5114 }, {1195,1689,4347 ,725,5792,3287 }, + {4550,5563,4447 ,716,4605,717 }, {5482,5342,4447 ,3565,6149,717 }, + {5126,5482,4447 ,6170,3565,717 }, {5423,5143,5342 ,6171,6099,6149 }, + {5482,5423,5342 ,3565,6171,6149 }, {1634,5685,5143 ,768,6150,6099 }, + {5423,1634,5143 ,6171,768,6099 }, {1416,774,104 ,6100,767,4765 }, + {8052,3638,8051 ,4122,5950,6172 }, {2659,601,2622 ,3485,3905,2840 }, + {3800,3817,1248 ,4325,1189,2850 }, {4872,4697,1524 ,5522,4327,4326 }, + {2954,1538,3867 ,5687,0,4507 }, {5356,5909,4167 ,4258,4260,6173 }, + {5654,6188,5916 ,6026,6174,6175 }, {5975,5253,5391 ,6161,6163,5773 }, + {2342,5008,2273 ,5690,6118,5322 }, {5896,5713,6046 ,6176,5776,5775 }, + {5253,5896,6046 ,6163,6176,5775 }, {564,5309,507 ,3529,987,1523 }, + {5611,5634,5633 ,6177,6178,6179 }, {6949,5634,5672 ,4585,6178,5324 }, + {5633,5634,6949 ,6179,6178,4585 }, {6088,6082,6069 ,3670,1348,6180 }, + {5672,5719,5718 ,5324,6157,6126 }, {5848,2994,445 ,6181,6182,6132 }, + {5825,5848,445 ,6131,6181,6132 }, {2994,1456,445 ,6182,6133,6132 }, + {3703,1185,4231 ,6183,6159,6158 }, {5820,5528,5975 ,6144,6143,6161 }, + {6532,3547,3494 ,6184,803,736 }, {1516,4233,3177 ,1254,6185,1255 }, + {3494,3495,6532 ,736,735,6184 }, {7422,7478,7450 ,1404,1402,399 }, + {5160,3524,5969 ,6186,6187,6188 }, {3018,5530,5851 ,6125,6189,6095 }, + {5965,5329,6076 ,6190,6191,4217 }, {5886,6087,6084 ,6192,6193,6194 }, + {3217,2342,2273 ,6117,5690,5322 }, {3829,3217,2273 ,6195,6117,5322 }, + {5713,5896,4648 ,5776,6176,5722 }, {3562,3943,6013 ,4753,4755,6060 }, + {5353,5733,1342 ,851,1584,6164 }, {1342,1527,3255 ,6164,6166,6165 }, + {4013,3255,1527 ,6168,6165,6166 }, {5244,5266,5221 ,79,81,4864 }, + {3817,1207,1248 ,1189,1074,2850 }, {5685,1634,774 ,6150,768,767 }, + {7509,7508,7479 ,1874,6196,4423 }, {1251,5126,5563 ,4604,6170,4605 }, + {2661,5126,1251 ,4684,6170,4604 }, {5482,1634,5423 ,3565,768,6171 }, + {3816,6577,5445 ,5623,97,3520 }, {3244,5050,993 ,4433,5233,5551 }, + {1376,4788,1196 ,1937,3758,4330 }, {1700,81,323 ,6072,4673,4767 }, + {3939,4361,4307 ,3539,4584,4562 }, {4321,4343,4342 ,4873,1818,4950 }, + {5980,6091,6010 ,5557,2173,6197 }, {1426,1017,3452 ,4648,1793,1794 }, + {6175,3620,6163 ,6004,6001,1453 }, {5331,5371,5350 ,6198,3558,1526 }, + {5331,5372,5371 ,6198,6199,3558 }, {5372,5406,5405 ,6199,6200,5518 }, + {5371,5372,5405 ,3558,6199,5518 }, {5406,5436,5405 ,6200,6201,5518 }, + {5008,2342,2273 ,6118,5690,5322 }, {5594,5612,5611 ,5937,6202,6177 }, + {5593,5594,5611 ,5935,5937,6177 }, {5612,5635,5634 ,6202,6203,6178 }, + {5611,5612,5634 ,6177,6202,6178 }, {5635,5673,5672 ,6203,6204,5324 }, + {5634,5635,5672 ,6178,6203,5324 }, {5673,5674,5672 ,6204,6205,5324 }, + {5674,5720,5719 ,6205,6206,6157 }, {5672,5674,5719 ,5324,6205,6157 }, + {5720,5758,5757 ,6206,6207,6127 }, {5719,5720,5757 ,6157,6206,6127 }, + {5758,5794,5793 ,6207,6208,6130 }, {5757,5758,5793 ,6127,6207,6130 }, + {5794,5826,5825 ,6208,6209,6131 }, {5793,5794,5825 ,6130,6208,6131 }, + {5825,5826,5848 ,6131,6209,6181 }, {5826,3145,2994 ,6209,6210,6182 }, + {5848,5826,2994 ,6181,6209,6182 }, {3940,1456,2994 ,6211,6133,6182 }, + {3145,3940,2994 ,6210,6211,6182 }, {1598,852,1456 ,6212,6134,6133 }, + {3940,1598,1456 ,6211,6212,6133 }, {3928,4231,852 ,6213,6158,6134 }, + {1598,3928,852 ,6212,6213,6134 }, {3587,3703,4231 ,6214,6183,6158 }, + {3928,3587,4231 ,6213,6214,6158 }, {2861,1185,3703 ,6215,6159,6183 }, + {3587,2861,3703 ,6214,6215,6183 }, {1988,1516,1185 ,6216,1254,6159 }, + {2861,1988,1185 ,6215,6216,6159 }, {4295,4233,1516 ,5968,6185,1254 }, + {1988,4295,1516 ,6216,5968,1254 }, {4295,4324,4233 ,5968,6217,6185 }, + {733,1026,4233 ,4051,6218,6185 }, {4324,733,4233 ,6217,4051,6185 }, + {7220,7239,7219 ,4367,1527,1529 }, {7186,7203,7185 ,5370,4366,5073 }, + {3781,5884,5451 ,5849,5848,6219 }, {5879,5987,6033 ,6220,5621,5620 }, + {5969,5965,6076 ,6188,6190,4217 }, {6001,6095,6081 ,6221,6222,6223 }, + {5869,5886,6084 ,6224,6192,6194 }, {4094,4642,5670 ,6225,6226,6227 }, + {3497,747,5319 ,5738,5692,5739 }, {6010,6016,5692 ,6197,6228,6229 }, + {5734,4467,1342 ,6230,6231,6164 }, {5733,5734,1342 ,1584,6230,6164 }, + {4467,602,1527 ,6231,6232,6166 }, {1342,4467,1527 ,6164,6231,6166 }, + {4220,4013,1527 ,6169,6168,6166 }, {602,4220,1527 ,6232,6169,6166 }, + {1537,1745,4191 ,4473,6233,4474 }, {1251,306,4166 ,4604,6234,6084 }, + {2661,5482,5126 ,4684,3565,6170 }, {3266,4669,4775 ,3884,3879,4388 }, + {1745,1444,4191 ,6233,4035,4474 }, {6242,6243,6270 ,6081,5666,6082 }, + {5171,5195,6114 ,1804,6235,1805 }, {5278,5277,5232 ,6236,6237,6238 }, + {5533,4010,6172 ,6239,4343,5451 }, {4465,4444,6210 ,257,582,5566 }, + {5845,6234,6174 ,143,5973,2381 }, {3943,5860,5956 ,4755,5998,6240 }, + {5636,5674,5673 ,6241,6205,6204 }, {5635,5636,5673 ,6203,6241,6204 }, + {5720,5759,5758 ,6206,6242,6207 }, {5758,5759,5794 ,6207,6242,6208 }, + {3942,3928,1598 ,6243,6213,6212 }, {3942,3587,3928 ,6243,6214,6213 }, + {184,733,4324 ,4052,4051,6217 }, {4295,184,4324 ,5968,4052,6217 }, + {7462,7435,7409 ,4310,6244,6245 }, {5973,6110,6081 ,6246,6247,6223 }, + {5711,5879,6033 ,6248,6220,5620 }, {2837,75,930 ,2511,3962,3594 }, + {6001,5996,6156 ,6221,6249,6250 }, {6001,6181,5705 ,6221,6251,4127 }, + {6081,3395,5185 ,6223,5940,5939 }, {5927,6040,5227 ,5318,5317,4097 }, + {6197,6223,6222 ,6252,6151,6253 }, {4026,4467,5734 ,6254,6231,6230 }, + {4261,4026,5734 ,1583,6254,6230 }, {4467,4026,602 ,6231,6254,6232 }, + {5459,4220,602 ,6255,6169,6232 }, {5459,993,5052 ,6255,5551,5189 }, + {4220,5459,5052 ,6169,6255,5189 }, {4908,4724,4774 ,5879,4737,5332 }, + {252,5526,966 ,2743,6256,2565 }, {3838,1016,4166 ,6257,6085,6084 }, + {5127,2661,1251 ,5114,4684,4604 }, {1016,5127,1251 ,6085,5114,4604 }, + {5127,124,2661 ,5114,2206,4684 }, {125,124,5127 ,399,2206,5114 }, + {1790,4949,5245 ,4738,5526,5880 }, {1444,3318,2690 ,4035,4036,6022 }, + {4191,1444,2690 ,4474,4035,6022 }, {3318,3419,2797 ,4036,2012,6030 }, + {2659,2622,2901 ,3485,2840,3468 }, {5609,6135,5953 ,5658,6258,5659 }, + {5278,5331,5277 ,6236,6198,6237 }, {5406,5437,5436 ,6200,6259,6201 }, + {5516,5552,5551 ,5420,5936,6260 }, {5612,5636,5635 ,6202,6241,6203 }, + {5720,5745,5759 ,6206,6261,6242 }, {1151,3587,3942 ,6262,6214,6243 }, + {2861,4295,1988 ,6215,5968,6216 }, {4016,4295,4348 ,5969,5968,6263 }, + {5898,6038,6035 ,5706,6264,6025 }, {6001,5705,5996 ,6221,4127,6249 }, + {3395,5854,6142 ,5940,6265,5941 }, {3583,5651,6067 ,5772,5920,6266 }, + {75,3510,1629 ,3962,6267,4207 }, {5654,5916,4425 ,6026,6175,6268 }, + {5987,5451,5608 ,5621,6219,4356 }, {5662,3943,5944 ,5915,4755,4754 }, + {5653,5662,5944 ,3644,5915,4754 }, {4209,4290,3777 ,6269,6270,1582 }, + {789,3985,2797 ,3910,4802,6030 }, {2690,3318,2797 ,6022,4036,6030 }, + {5803,5836,5819 ,5871,5859,6114 }, {7128,7160,7159 ,5391,6271,5075 }, + {993,5050,1154 ,5551,5233,4545 }, {5214,5213,5171 ,6272,428,1804 }, + {5172,5214,5171 ,5468,6272,1804 }, {5234,5233,5213 ,6273,429,428 }, + {5256,5232,5233 ,6274,6238,429 }, {5234,5256,5233 ,6273,6274,429 }, + {5279,5278,5232 ,6275,6236,6238 }, {5256,5279,5232 ,6274,6275,6238 }, + {5332,5331,5278 ,6276,6198,6236 }, {5279,5332,5278 ,6275,6276,6236 }, + {5332,5373,5372 ,6276,6277,6199 }, {5331,5332,5372 ,6198,6276,6199 }, + {5373,5407,5406 ,6277,6278,6200 }, {5372,5373,5406 ,6199,6277,6200 }, + {5407,5408,5406 ,6278,6279,6200 }, {5408,5438,5437 ,6279,4156,6259 }, + {5406,5408,5437 ,6200,6279,6259 }, {5479,5517,5516 ,5387,6280,5420 }, + {5517,5553,5552 ,6280,6281,5936 }, {5516,5517,5552 ,5420,6280,5936 }, + {5553,5554,5552 ,6281,6282,5936 }, {5554,5595,5594 ,6282,6283,5937 }, + {5552,5554,5594 ,5936,6282,5937 }, {5594,5595,5612 ,5937,6283,6202 }, + {5595,5637,5636 ,6283,6284,6241 }, {5612,5595,5636 ,6202,6283,6241 }, + {5637,5675,5674 ,6284,6285,6205 }, {5636,5637,5674 ,6241,6284,6205 }, + {5675,5721,5720 ,6285,6286,6206 }, {5674,5675,5720 ,6205,6285,6206 }, + {5721,5722,5745 ,6286,6287,6261 }, {5720,5721,5745 ,6206,6286,6261 }, + {5722,5760,5759 ,6287,6288,6242 }, {5745,5722,5759 ,6261,6287,6242 }, + {5760,5795,5794 ,6288,6289,6208 }, {5759,5760,5794 ,6242,6288,6208 }, + {5795,5827,5826 ,6289,6290,6209 }, {5794,5795,5826 ,6208,6289,6209 }, + {5827,5828,3145 ,6290,6291,6210 }, {5826,5827,3145 ,6209,6290,6210 }, + {5828,4288,3145 ,6291,6292,6210 }, {4288,4287,3145 ,6292,6293,6210 }, + {3480,3940,3145 ,6294,6211,6210 }, {4287,3480,3145 ,6293,6294,6210 }, + {995,1598,3940 ,6295,6212,6211 }, {3480,995,3940 ,6294,6295,6211 }, + {3983,3942,1598 ,6296,6243,6212 }, {995,3983,1598 ,6295,6296,6212 }, + {1522,1151,3942 ,6297,6262,6243 }, {3983,1522,3942 ,6296,6297,6243 }, + {1522,3587,1151 ,6297,6214,6262 }, {1732,2861,3587 ,6298,6215,6214 }, + {1522,1732,3587 ,6297,6298,6214 }, {4232,4295,2861 ,6299,5968,6215 }, + {1732,4232,2861 ,6298,6299,6215 }, {4232,4348,4295 ,6299,6263,5968 }, + {4321,3528,5630 ,4873,5061,4874 }, {4017,4016,4232 ,5342,5969,6299 }, + {5982,5924,5649 ,4218,6091,6093 }, {5705,6181,5992 ,4127,6251,4197 }, + {6130,6132,6131 ,6300,5897,6050 }, {5472,3443,5869 ,6301,6302,6224 }, + {7168,7185,7160 ,5371,5073,6271 }, {1555,4290,4209 ,6303,6270,6269 }, + {1554,1555,4209 ,6304,6303,6269 }, {1555,4261,4290 ,6303,1583,6270 }, + {3771,4026,4261 ,6305,6254,1583 }, {1555,3771,4261 ,6303,6305,1583 }, + {3010,602,4026 ,6306,6232,6254 }, {3771,3010,4026 ,6305,6306,6254 }, + {3242,5459,602 ,6307,6255,6232 }, {3010,3242,602 ,6306,6307,6232 }, + {3242,3244,993 ,6307,4433,5551 }, {5459,3242,993 ,6255,6307,5551 }, + {1207,3817,5153 ,1074,1189,3896 }, {6749,5548,5512 ,4046,1590,1589 }, + {5457,5387,1252 ,6308,6309,5023 }, {5387,3838,3839 ,6309,6257,6310 }, + {1388,1016,3838 ,6311,6085,6257 }, {5387,1388,3838 ,6309,6311,6257 }, + {5617,5127,1016 ,6312,5114,6085 }, {2621,1606,3815 ,5416,3677,5556 }, + {5321,5346,5320 ,5668,3267,4464 }, {5172,5215,5214 ,5468,6313,6272 }, + {5234,5279,5256 ,6273,6275,6274 }, {5332,5374,5373 ,6276,6314,6277 }, + {5374,5408,5407 ,6314,6279,6278 }, {5373,5374,5407 ,6277,6314,6278 }, + {7371,7370,7310 ,3748,3750,4283 }, {5479,5492,5517 ,5387,6315,6280 }, + {5517,5518,5553 ,6280,6316,6281 }, {5796,5828,5827 ,6317,6291,6290 }, + {5795,5796,5827 ,6289,6317,6290 }, {3665,3480,4287 ,6318,6294,6293 }, + {4288,3665,4287 ,6292,6318,6293 }, {1491,1732,1522 ,6319,6298,6297 }, + {4348,4232,4016 ,6263,6299,5969 }, {4921,1551,5082 ,2079,5275,5218 }, + {5786,5082,3872 ,4813,5218,4811 }, {6081,5185,5992 ,6223,5939,4197 }, + {6181,6081,5992 ,6251,6223,4197 }, {3726,3771,1555 ,6320,6305,6303 }, + {3726,3010,3771 ,6320,6306,6305 }, {4834,955,5787 ,4304,3991,4305 }, + {5387,3839,1252 ,6309,6310,5023 }, {4241,5387,5457 ,6321,6309,6308 }, + {5225,1388,5387 ,6322,6311,6309 }, {4883,5617,1016 ,5538,6312,6085 }, + {1388,4883,1016 ,6311,5538,6085 }, {5617,4883,5127 ,6312,5538,5114 }, + {3760,3669,1783 ,2250,5917,2251 }, {5267,5290,5244 ,4647,5247,79 }, + {5215,5172,5216 ,6313,5468,6323 }, {4842,4776,4745 ,5429,5390,5446 }, + {5492,5518,5517 ,6315,6316,6280 }, {5518,5554,5553 ,6316,6282,6281 }, + {5696,5722,5721 ,6324,6287,6286 }, {5675,5696,5721 ,6285,6324,6286 }, + {5760,5796,5795 ,6288,6317,6289 }, {4188,995,3480 ,6325,6295,6294 }, + {3665,4188,3480 ,6318,6325,6294 }, {2957,603,2093 ,6326,5274,553 }, + {5189,5188,3872 ,6327,4812,4811 }, {803,101,6204 ,144,5,4050 }, + {248,1554,4530 ,4114,6304,1376 }, {3118,3010,3726 ,6328,6306,6320 }, + {3118,3242,3010 ,6328,6307,6306 }, {455,556,4471 ,2205,4043,2187 }, + {5205,5228,5227 ,5219,4095,4097 }, {4649,5152,5125 ,5288,6329,5467 }, + {4702,5173,5125 ,5291,6330,5467 }, {5152,4702,5125 ,6329,5291,5467 }, + {4702,5172,5173 ,5291,5468,6330 }, {4702,5216,5172 ,5291,6323,5468 }, + {5257,5234,5214 ,6331,6273,6272 }, {5280,5279,5234 ,6332,6275,6273 }, + {5257,5280,5234 ,6331,6332,6273 }, {5333,5332,5279 ,6333,6276,6275 }, + {5280,5333,5279 ,6332,6333,6275 }, {5375,5374,5332 ,6334,6314,6276 }, + {5333,5375,5332 ,6333,6334,6276 }, {5375,5409,5408 ,6334,6335,6279 }, + {5374,5375,5408 ,6314,6334,6279 }, {5409,5439,5438 ,6335,4157,4156 }, + {5408,5409,5438 ,6279,6335,4156 }, {7160,7138,7168 ,6271,2560,5371 }, + {5493,5519,5518 ,6336,6337,6316 }, {5479,5493,5492 ,5387,6336,6315 }, + {5519,5555,5554 ,6337,6338,6282 }, {5518,5519,5554 ,6316,6337,6282 }, + {5555,5596,5595 ,6338,6339,6283 }, {5554,5555,5595 ,6282,6338,6283 }, + {5596,5638,5637 ,6339,6340,6284 }, {5595,5596,5637 ,6283,6339,6284 }, + {5638,5676,5675 ,6340,6341,6285 }, {5637,5638,5675 ,6284,6340,6285 }, + {5676,5677,5696 ,6341,6342,6324 }, {5675,5676,5696 ,6285,6341,6324 }, + {5677,5723,5722 ,6342,6343,6287 }, {5696,5677,5722 ,6324,6342,6287 }, + {5723,5761,5760 ,6343,6344,6288 }, {5722,5723,5760 ,6287,6343,6288 }, + {5761,5783,5760 ,6344,6345,6288 }, {5783,5797,5796 ,6345,6346,6317 }, + {5760,5783,5796 ,6288,6345,6317 }, {5797,5829,5828 ,6346,6347,6291 }, + {5796,5797,5828 ,6317,6346,6291 }, {5829,1667,4288 ,6347,6348,6292 }, + {5828,5829,4288 ,6291,6347,6292 }, {1667,1019,3665 ,6348,6349,6318 }, + {4288,1667,3665 ,6292,6348,6318 }, {1540,4188,3665 ,6350,6325,6318 }, + {1019,1540,3665 ,6349,6350,6318 }, {1540,995,4188 ,6350,6295,6325 }, + {2499,3983,995 ,6351,6296,6295 }, {1540,2499,995 ,6350,6351,6295 }, + {2499,3988,3983 ,6351,6352,6296 }, {3974,1522,3983 ,6353,6297,6296 }, + {3988,3974,3983 ,6352,6353,6296 }, {218,1491,1522 ,6354,6319,6297 }, + {3974,218,1522 ,6353,6354,6297 }, {218,1732,1491 ,6354,6298,6319 }, + {2809,4232,1732 ,6355,6299,6298 }, {218,2809,1732 ,6354,6355,6298 }, + {2809,4208,4232 ,6355,6356,6299 }, {7129,7138,7160 ,1026,2560,6271 }, + {7138,7169,7168 ,2560,5437,5371 }, {876,5859,1120 ,5340,6357,5339 }, + {5859,37,1120 ,6357,6358,5339 }, {2957,1120,37 ,6326,5339,6358 }, + {37,555,2957 ,6358,6359,6326 }, {555,551,603 ,6359,6360,5274 }, + {2957,555,603 ,6326,6359,5274 }, {1523,1551,603 ,6361,5275,5274 }, + {551,1523,603 ,6360,6361,5274 }, {1108,3872,1551 ,6362,4811,5275 }, + {1523,1108,1551 ,6361,6362,5275 }, {3361,5189,3872 ,6363,6327,4811 }, + {1108,3361,3872 ,6362,6363,4811 }, {3361,5188,5189 ,6363,4812,6327 }, + {1861,3350,5188 ,6364,2541,4812 }, {3361,1861,5188 ,6363,6364,4812 }, + {1861,5856,3350 ,6364,6365,2541 }, {5856,5806,3350 ,6365,4113,2541 }, + {3418,248,5806 ,6366,4114,4113 }, {3418,5341,1554 ,6366,6367,6304 }, + {4025,1555,1554 ,6368,6303,6304 }, {5341,4025,1554 ,6367,6368,6304 }, + {3774,3726,1555 ,6369,6320,6303 }, {4025,3774,1555 ,6368,6369,6303 }, + {2991,3118,3726 ,6370,6328,6320 }, {3774,2991,3726 ,6369,6370,6320 }, + {5201,3242,3118 ,6371,6307,6328 }, {2991,5201,3118 ,6370,6371,6328 }, + {5201,5174,3242 ,6371,6372,6307 }, {4397,3244,3242 ,4434,4433,6307 }, + {5174,4397,3242 ,6372,4434,6307 }, {1253,3778,4068 ,2353,5585,408 }, + {5020,4272,4241 ,6373,6374,6321 }, {5245,5387,4241 ,5880,6309,6321 }, + {4272,5245,4241 ,6374,5880,6321 }, {5245,4908,5387 ,5880,5879,6309 }, + {4908,5161,5225 ,5879,6375,6322 }, {5387,4908,5225 ,6309,5879,6322 }, + {5161,4774,1388 ,6375,5332,6311 }, {5225,5161,1388 ,6322,6375,6311 }, + {5153,4682,3899 ,3896,1191,1938 }, {4702,5152,4649 ,5291,6329,5288 }, + {1922,4397,1085 ,4432,4434,369 }, {5519,5535,5555 ,6337,3512,6338 }, + {5596,5613,5638 ,6339,6376,6340 }, {5638,5677,5676 ,6340,6342,6341 }, + {2499,3974,3988 ,6351,6353,6352 }, {4208,2809,876 ,6356,6355,5340 }, + {876,37,5859 ,5340,6358,6357 }, {2913,414,551 ,6377,6378,6360 }, + {555,2913,551 ,6359,6377,6360 }, {671,1523,551 ,6379,6361,6360 }, + {414,671,551 ,6378,6379,6360 }, {4309,1108,1523 ,6380,6362,6361 }, + {671,4309,1523 ,6379,6380,6361 }, {3630,5806,5856 ,6381,4113,6365 }, + {1861,3630,5856 ,6364,6381,6365 }, {3287,4025,5341 ,6382,6368,6367 }, + {3418,3287,5341 ,6366,6382,6367 }, {4025,5661,3774 ,6368,6383,6369 }, + {5174,4117,4397 ,6372,4700,4434 }, {6785,6803,5548 ,4140,4169,1590 }, + {5292,4272,5175 ,5093,6374,6384 }, {4908,4774,5161 ,5879,5332,6375 }, + {5224,4714,5267 ,3850,1031,4647 }, {3732,2785,3419 ,3536,1246,2012 }, + {3318,3732,3419 ,4036,3536,2012 }, {5235,5280,5257 ,6385,6332,6331 }, + {5439,5410,5440 ,4157,4285,5147 }, {5104,2246,5755 ,6063,591,4047 }, + {6697,5512,5471 ,3866,1589,2051 }, {6680,6697,5471 ,3834,3866,2051 }, + {5590,6173,6002 ,6386,3711,5975 }, {5555,5568,5596 ,6338,6387,6339 }, + {5638,5655,5677 ,6340,6388,6342 }, {1585,3974,2499 ,6389,6353,6351 }, + {876,4346,37 ,5340,6390,6358 }, {37,2913,555 ,6358,6377,6359 }, + {4309,3361,1108 ,6380,6363,6362 }, {1384,3418,5806 ,6391,6366,4113 }, + {3630,1384,5806 ,6381,6391,4113 }, {4025,3287,5661 ,6368,6382,6383 }, + {3775,2991,3774 ,6392,6370,6369 }, {5661,3775,3774 ,6383,6392,6369 }, + {4397,4117,1859 ,4434,4700,4480 }, {4272,5020,5175 ,6374,6373,6384 }, + {6697,6749,5512 ,3866,4046,1589 }, {3367,4645,4926 ,5798,5378,3897 }, + {1416,104,4519 ,6100,4765,4746 }, {5217,5216,4702 ,4026,6323,5291 }, + {4703,5217,4702 ,5290,4026,5291 }, {5217,5235,5216 ,4026,6385,6323 }, + {5217,5258,5235 ,4026,1193,6385 }, {5281,5280,5235 ,1195,6332,6385 }, + {5258,5281,5235 ,1193,1195,6385 }, {5281,5282,5280 ,1195,1194,6332 }, + {5282,5334,5333 ,1194,2736,6333 }, {5280,5282,5333 ,6332,1194,6333 }, + {5334,5376,5375 ,2736,2758,6334 }, {5333,5334,5375 ,6333,2736,6334 }, + {5410,5409,5375 ,4285,6335,6334 }, {5376,5410,5375 ,2758,4285,6334 }, + {5409,5410,5439 ,6335,4285,4157 }, {5172,5125,5173 ,5468,5467,6330 }, + {1353,1395,1352 ,1205,1164,1262 }, {5556,5557,5568 ,3511,756,6387 }, + {5555,5556,5568 ,6338,3511,6387 }, {5557,5597,5596 ,756,6393,6339 }, + {5568,5557,5596 ,6387,756,6339 }, {5557,5598,5597 ,756,2216,6393 }, + {5596,5597,5613 ,6339,6393,6376 }, {5598,5639,5638 ,2216,2215,6340 }, + {5613,5598,5638 ,6376,2216,6340 }, {5639,5656,5655 ,2215,3864,6388 }, + {5638,5639,5655 ,6340,2215,6388 }, {5656,5678,5677 ,3864,3861,6342 }, + {5655,5656,5677 ,6388,3864,6342 }, {5678,5724,5723 ,3861,4898,6343 }, + {5677,5678,5723 ,6342,3861,6343 }, {5724,5762,5761 ,4898,4884,6344 }, + {5723,5724,5761 ,6343,4898,6344 }, {5761,5762,5783 ,6344,4884,6345 }, + {5762,5798,5797 ,4884,4876,6346 }, {5783,5762,5797 ,6345,4884,6346 }, + {5798,5830,5829 ,4876,4163,6347 }, {5797,5798,5829 ,6346,4876,6347 }, + {1794,1667,5829 ,4129,6348,6347 }, {5830,1794,5829 ,4163,4129,6347 }, + {1794,2795,1019 ,4129,3975,6349 }, {1667,1794,1019 ,6348,4129,6349 }, + {1607,1540,1019 ,3941,6350,6349 }, {2795,1607,1019 ,3975,3941,6349 }, + {2477,2499,1540 ,642,6351,6350 }, {1607,2477,1540 ,3941,642,6350 }, + {87,1585,2499 ,641,6389,6351 }, {2477,87,2499 ,642,641,6351 }, + {87,3974,1585 ,641,6353,6389 }, {1587,218,3974 ,837,6354,6353 }, + {87,1587,3974 ,641,837,6353 }, {96,2809,218 ,836,6355,6354 }, + {1587,96,218 ,837,836,6354 }, {96,3257,876 ,836,4010,5340 }, + {2809,96,876 ,6355,836,5340 }, {876,3257,4346 ,5340,4010,6390 }, + {3257,5737,37 ,4010,6394,6358 }, {4346,3257,37 ,6390,4010,6358 }, + {5737,1974,37 ,6394,3862,6358 }, {1974,62,2913 ,3862,3831,6377 }, + {37,1974,2913 ,6358,3862,6377 }, {34,414,2913 ,3830,6378,6377 }, + {62,34,2913 ,3831,3830,6377 }, {3980,671,414 ,4933,6379,6378 }, + {34,3980,414 ,3830,4933,6378 }, {164,4309,671 ,4932,6380,6379 }, + {3980,164,671 ,4933,4932,6379 }, {52,3361,4309 ,411,6363,6380 }, + {164,52,4309 ,4932,411,6380 }, {3113,1861,3361 ,410,6364,6363 }, + {52,3113,3361 ,411,410,6363 }, {3113,269,1861 ,410,3813,6364 }, + {1734,3630,1861 ,2219,6381,6364 }, {269,1734,1861 ,3813,2219,6364 }, + {878,1384,3630 ,398,6391,6381 }, {1734,878,3630 ,2219,398,6381 }, + {878,3418,1384 ,398,6366,6391 }, {4011,3287,3418 ,395,6382,6366 }, + {878,4011,3418 ,398,395,6366 }, {4011,3662,5661 ,395,1942,6383 }, + {3287,4011,5661 ,6382,395,6383 }, {5304,3775,5661 ,654,6392,6383 }, + {3662,5304,5661 ,1942,654,6383 }, {5304,2991,3775 ,654,6370,6392 }, + {5481,5201,2991 ,3863,6371,6370 }, {5304,5481,2991 ,654,3863,6370 }, + {5481,4117,5201 ,3863,4700,6371 }, {4117,5174,5201 ,4700,6372,6371 }, + {104,1150,4519 ,4765,741,4746 }, {3899,1376,1196 ,1938,1937,4330 }, + {7410,7462,7409 ,3766,4310,6245 }, {7033,6152,5945 ,6395,6044,6396 }, + {5562,5945,6152 ,4193,6396,6044 }, {1790,4272,5292 ,4738,6374,5093 }, + {5017,1790,5292 ,5092,4738,5093 }, {4272,1790,5245 ,6374,4738,5880 }, + {3419,789,2797 ,2012,3910,6030 }, {5153,3899,5146 ,3896,1938,1075 }, + {4502,1859,4117 ,4650,4480,4700 }, {6648,6680,6647 ,1390,3834,2050 }, + {6648,6647,6611 ,1390,2050,1391 }, {5557,8043,5598 ,756,6397,2216 }, + {6530,1990,1217 ,6398,1305,6399 }, {5613,5597,5598 ,6376,6393,2216 }, + {1539,1587,87 ,835,837,641 }, {96,3909,3257 ,836,4164,4010 }, + {3257,1974,5737 ,4010,3862,6394 }, {3688,4292,4153 ,5574,6400,5711 }, + {3470,6003,5917 ,6401,6122,6402 }, {350,5653,1273 ,1114,3644,6403 }, + {4286,5580,3528 ,4274,6404,5061 }, {5392,5947,5717 ,4022,6027,4023 }, + {2044,465,501 ,4031,4578,4313 }, {670,5043,5985 ,4760,4762,5365 }, + {7960,100,2306 ,3505,2823,6405 }, {5882,3674,3728 ,4397,4286,4251 }, + {5575,5958,6112 ,5686,5702,6406 }, {8023,8038,8022 ,4582,4121,4123 }, + {4284,4229,4270 ,4869,4877,4900 }, {3778,1253,1583 ,5585,2353,5415 }, + {6267,3607,3606 ,6407,6138,6137 }, {6012,5963,6004 ,6408,6409,6123 }, + {6241,6242,6269 ,6410,6081,6083 }, {5129,5293,5314 ,5698,5889,6411 }, + {3659,5129,5314 ,3875,5698,6411 }, {6141,6122,2072 ,2386,1797,1812 }, + {3559,5567,5897 ,6412,6413,6414 }, {4058,206,4054 ,4708,3932,3679 }, + {5650,5981,3905 ,2713,6415,4194 }, {6227,5906,5462 ,6416,6417,6418 }, + {3813,3758,5926 ,1515,1789,6419 }, {5662,6035,4045 ,5915,6025,5916 }, + {5687,5048,5101 ,6420,6421,6422 }, {5361,6227,5462 ,5664,6416,6418 }, + {206,4054,4055 ,3932,3679,3678 }, {5488,3518,5473 ,2730,4338,3429 }, + {3528,5580,5630 ,5061,6404,4874 }, {6113,3608,5461 ,5873,5799,5744 }, + {6057,5994,5893 ,6423,6424,6425 }, {5994,5524,1008 ,6424,6426,6427 }, + {3599,6057,5893 ,6428,6423,6425 }, {4153,3688,3953 ,5711,5574,5713 }, + {5893,5994,1008 ,6425,6424,6427 }, {6096,2357,4043 ,4092,3933,419 }, + {2142,6031,5706 ,4936,6429,3796 }, {3007,6070,3582 ,818,820,1378 }, + {7437,7436,7386 ,5938,4245,5512 }, {6168,5646,5316 ,6430,6431,6087 }, + {5524,5646,1008 ,6426,6431,6427 }, {5646,5743,5316 ,6431,6432,6087 }, + {5346,6260,5320 ,3267,3397,4464 }, {8095,8062,8090 ,4362,3272,412 }, + {6128,6129,5123 ,5981,5983,6433 }, {5646,6168,1008 ,6431,6430,6427 }, + {6023,5646,5524 ,6040,6431,6426 }, {5875,3797,5490 ,6434,2061,6055 }, + {5550,5878,5549 ,6047,2684,6435 }, {5452,5650,3905 ,2714,2713,4194 }, + {3803,3720,3804 ,6436,6437,4324 }, {6001,6003,6095 ,6221,6122,6222 }, + {5345,6057,3599 ,6438,6423,6428 }, {5392,1741,5947 ,4022,5695,6027 }, + {187,2159,1171 ,6439,6440,6441 }, {3804,3720,5882 ,4324,6437,4397 }, + {3720,6160,3674 ,6437,6442,4286 }, {5882,3720,3674 ,4397,6437,4286 }, + {5254,5576,3436 ,4042,4039,2145 }, {7688,7689,7712 ,6443,6444,5350 }, + {4528,5550,5549 ,4935,6047,6435 }, {2159,4528,5549 ,6440,4935,6435 }, + {6230,6104,3436 ,2144,5696,2145 }, {5303,2636,5315 ,5737,2712,6445 }, + {3688,4153,3953 ,5574,5711,5713 }, {5880,3666,6137 ,5700,5699,5809 }, + {6059,3679,5906 ,6446,6447,6417 }, {6094,5977,5742 ,692,809,6448 }, + {3373,6094,5742 ,693,692,6448 }, {4807,5426,5970 ,6449,5634,3518 }, + {381,3522,1627 ,6450,6451,5573 }, {2636,5303,5650 ,2712,5737,2713 }, + {5862,3893,4623 ,3790,3789,6452 }, {3893,5861,4623 ,3789,6453,6452 }, + {5609,5851,6135 ,5658,6095,6258 }, {5297,4178,5272 ,3826,3825,5956 }, + {5044,5192,5303 ,5173,6454,5737 }, {5922,6149,3445 ,4292,732,1791 }, + {7089,7103,5115 ,5608,6455,5913 }, {4433,4820,4795 ,4591,2399,3710 }, + {5462,5906,5961 ,6418,6417,6456 }, {5192,4410,6251 ,6454,5735,6457 }, + {6224,6248,6213 ,6458,6154,6459 }, {2636,5452,5386 ,2712,2714,5870 }, + {6010,5384,6016 ,6197,6121,6228 }, {6058,5345,5416 ,6460,6438,6461 }, + {5384,3631,5403 ,6121,5922,5921 }, {5235,5257,5214 ,6385,6331,6272 }, + {6254,3906,6055 ,5758,5697,3874 }, {3411,5193,1741 ,4280,5846,5695 }, + {6003,5973,6095 ,6122,6246,6222 }, {3415,7082,6186 ,6115,6462,4365 }, + {6205,6206,6240 ,6463,6464,5756 }, {5213,5195,5171 ,428,6235,1804 }, + {5888,5902,6093 ,5752,5769,5771 }, {7423,7479,7422 ,3749,4423,1404 }, + {5322,5320,5695 ,4297,4464,6465 }, {5874,5976,5925 ,4896,5903,5710 }, + {7690,449,448 ,6466,1169,1172 }, {5958,5122,3507 ,5702,4024,5671 }, + {3930,1627,4247 ,6467,5573,6468 }, {3628,6235,5703 ,4213,4215,5748 }, + {5567,5526,5897 ,6413,6256,6414 }, {5205,5248,5247 ,5219,6469,4096 }, + {6254,6268,3906 ,5758,5757,5697 }, {5609,6256,6213 ,5658,5657,6459 }, + {6102,3868,3879 ,2464,970,2549 }, {5055,3979,1261 ,6470,6471,6472 }, + {4247,4246,381 ,6468,3665,6450 }, {381,1627,3930 ,6450,5573,6467 }, + {7293,7248,7322 ,6473,73,75 }, {5926,3758,3714 ,6419,1789,3970 }, + {6004,6003,5974 ,6123,6122,6474 }, {5993,3953,5580 ,6475,5713,6404 }, + {7423,7422,7370 ,3749,1404,3750 }, {1701,1949,216 ,6476,3637,4644 }, + {5851,5530,5881 ,6095,6189,6109 }, {4574,6062,5974 ,6477,6478,6474 }, + {1532,1051,217 ,4771,4588,4643 }, {6075,5922,5908 ,6042,4292,4293 }, + {5662,5898,6035 ,5915,5706,6025 }, {1605,6206,6205 ,17,6464,6463 }, + {5971,5903,5902 ,6479,6480,5769 }, {5902,5903,686 ,5769,6480,5770 }, + {5753,5055,5916 ,6481,6470,6175 }, {3522,6227,758 ,6451,6416,5663 }, + {6024,3410,5486 ,6482,6483,5718 }, {6225,5610,6248 ,4872,6153,6154 }, + {4517,6058,5416 ,6484,6460,6461 }, {5923,5591,3979 ,2073,3597,6471 }, + {1854,635,5971 ,2184,2183,6479 }, {4540,4526,4515 ,19,187,30 }, + {5318,3360,6023 ,6039,6485,6040 }, {5972,5988,4147 ,399,2516,2517 }, + {6225,6248,6224 ,4872,6154,6458 }, {1661,6179,3906 ,5672,6486,5697 }, + {6070,4486,3582 ,820,819,1378 }, {6240,6241,1661 ,5756,6410,5672 }, + {3717,3715,5630 ,6487,6124,4874 }, {3902,4807,5970 ,4437,6449,3518 }, + {3051,5474,5877 ,6488,4243,4242 }, {5388,5446,6317 ,5428,5985,3207 }, + {6032,5472,5869 ,6489,6301,6224 }, {5472,3409,6032 ,6301,6490,6489 }, + {3360,3386,5472 ,6485,6491,6301 }, {3409,5472,6032 ,6490,6301,6489 }, + {20,2657,3506 ,3961,2185,6492 }, {6018,4304,1747 ,340,4579,266 }, + {5185,6142,3632 ,5939,5941,5898 }, {5887,5888,6087 ,6493,5752,6193 }, + {6089,6050,6087 ,6494,4094,6193 }, {6023,6024,5646 ,6040,6482,6431 }, + {5055,5476,5916 ,6470,6495,6175 }, {5906,6006,3560 ,6417,6496,5779 }, + {5630,5610,5609 ,4874,6153,5658 }, {5888,6093,6050 ,5752,5771,4094 }, + {5319,747,5781 ,5739,5692,5694 }, {4229,4246,4270 ,4877,3665,4900 }, + {3680,3499,405 ,6497,5661,5662 }, {6084,3499,3680 ,6194,5661,6497 }, + {5961,5906,3560 ,6456,6417,5779 }, {6006,6043,3560 ,6496,6498,5779 }, + {5866,5867,4094 ,5302,6499,6225 }, {3560,6043,5318 ,5779,6498,6039 }, + {5867,5869,5866 ,6499,6224,5302 }, {5273,5298,5272 ,3819,3818,5956 }, + {5513,6123,4147 ,6500,4221,2517 }, {1417,3522,381 ,6501,6451,6450 }, + {3638,278,8051 ,5950,1489,6172 }, {5866,4094,4410 ,5302,6225,5735 }, + {5192,5866,4410 ,6454,5302,5735 }, {3525,5935,5885 ,5665,5778,6502 }, + {6168,5316,1008 ,6430,6087,6427 }, {3517,1729,5580 ,6503,3666,6404 }, + {6052,3311,3762 ,5717,5303,5172 }, {5303,5981,5650 ,5737,6415,2713 }, + {5303,5315,5044 ,5737,6445,5173 }, {1700,706,807 ,6072,4784,6073 }, + {3581,3508,6017 ,6504,6505,6506 }, {3508,5964,6017 ,6505,6507,6506 }, + {3879,5317,6102 ,2549,6508,2464 }, {5317,5382,3831 ,6508,6509,2321 }, + {6102,5317,3831 ,2464,6508,2321 }, {5382,4154,3803 ,6509,6510,6436 }, + {3831,5382,3803 ,2321,6509,6436 }, {1606,4054,206 ,3677,3679,3932 }, + {3953,3018,6124 ,5713,6125,6511 }, {5567,3559,5955 ,6413,6412,6512 }, + {5351,3386,5318 ,6513,6491,6039 }, {3443,6077,5887 ,6302,5753,6493 }, + {5988,20,4147 ,2516,3961,2517 }, {6145,5247,4023 ,6514,4096,3827 }, + {6087,6050,5394 ,6193,4094,5660 }, {5416,5345,5671 ,6461,6438,6515 }, + {507,565,564 ,1523,1614,3529 }, {3576,5912,6017 ,5890,5892,6506 }, + {807,3606,3563 ,6073,6137,6074 }, {5747,6030,5254 ,6036,4037,4042 }, + {6118,5923,3979 ,2074,2073,6471 }, {5753,6118,3979 ,6481,2074,6471 }, + {1729,5993,5580 ,3666,6475,6404 }, {6093,686,6086 ,5771,5770,6516 }, + {4260,6139,6138 ,3527,2059,6014 }, {5867,5869,4094 ,6499,6224,6225 }, + {3522,758,1627 ,6451,5663,5573 }, {5909,6136,5824 ,4260,5810,6517 }, + {5630,3715,5610 ,4874,6124,6153 }, {4147,3506,3384 ,2517,6492,6518 }, + {5888,6089,6087 ,5752,6494,6193 }, {6124,3018,3715 ,6511,6125,6124 }, + {3829,2273,3051 ,6195,5322,6488 }, {4517,5416,3018 ,6484,6461,6125 }, + {5853,3560,5318 ,6038,5779,6039 }, {3509,6128,5123 ,6519,5981,6433 }, + {4443,6212,6211 ,703,5562,5558 }, {5490,3747,5749 ,6055,2925,6052 }, + {5296,4023,5297 ,4296,3827,3826 }, {4055,4054,206 ,3678,3679,3932 }, + {6103,6123,5897 ,4220,4221,6414 }, {4486,3454,3582 ,819,710,1378 }, + {2204,6032,5867 ,6520,6489,6499 }, {5869,5867,5866 ,6224,6499,5302 }, + {6032,5869,5867 ,6489,6224,6499 }, {3658,6023,5524 ,6521,6040,6426 }, + {3584,6078,3558 ,5888,6522,5964 }, {3506,5368,3409 ,6492,5751,6490 }, + {5315,2636,5477 ,6445,2712,5399 }, {3680,405,4642 ,6497,5662,6226 }, + {6119,4410,5670 ,5736,5735,6227 }, {4374,4517,3018 ,5712,6484,6125 }, + {5299,5300,6041 ,3800,3793,3801 }, {3018,5416,5905 ,6125,6461,6142 }, + {5293,3584,3558 ,5889,5888,5964 }, {6218,3658,5935 ,5780,6521,5778 }, + {5246,5227,6040 ,5176,4097,5317 }, {3405,8086,1153 ,1798,4823,2245 }, + {3563,3606,1510 ,6074,6137,3788 }, {3905,5562,6152 ,4194,4193,6044 }, + {6163,3620,3860 ,1453,6001,6523 }, {5928,3904,3903 ,4072,1264,296 }, + {5882,3772,3804 ,4397,4250,4324 }, {3905,6152,6166 ,4194,6044,4971 }, + {5192,6251,5303 ,6454,6457,5737 }, {1417,381,4229 ,6501,6450,4877 }, + {635,5534,5971 ,2183,6524,6479 }, {6226,5981,5562 ,4192,6415,4193 }, + {1629,3510,1518 ,4207,6267,3978 }, {4367,6225,6224 ,228,4872,6458 }, + {3360,5472,5450 ,6485,6301,6525 }, {6244,5663,5356 ,5667,4259,4258 }, + {6006,5351,6043 ,6496,6513,6498 }, {3384,3506,3409 ,6518,6492,6490 }, + {5368,6077,3443 ,5751,5753,6302 }, {3409,5368,3443 ,6490,5751,6302 }, + {6077,5888,5887 ,5753,5752,6493 }, {4623,5861,5939 ,6452,6453,4276 }, + {6119,5670,5303 ,5736,6227,5737 }, {7973,7972,7948 ,3624,6059,3625 }, + {5567,2040,3039 ,6413,4833,2686 }, {6270,3780,3611 ,6082,6526,5673 }, + {5868,3720,3803 ,6527,6437,6436 }, {3525,4292,3688 ,5665,6400,5574 }, + {3337,5123,3951 ,4125,6433,6528 }, {970,5997,6018 ,268,6529,340 }, + {4154,5868,3803 ,6510,6527,6436 }, {6251,4410,5303 ,6457,5735,5737 }, + {5671,5345,3599 ,6515,6438,6428 }, {4286,3517,5580 ,4274,6503,6404 }, + {6161,6160,3720 ,6530,6442,6437 }, {5868,6161,3720 ,6527,6530,6437 }, + {6161,6187,6160 ,6530,6531,6442 }, {2896,3497,3845 ,6110,5738,3635 }, + {5903,2357,686 ,6480,3933,5770 }, {5906,3679,6006 ,6417,6447,6496 }, + {6227,3522,5906 ,6416,6451,6417 }, {4528,5316,5550 ,4935,6087,6047 }, + {3953,3717,5580 ,5713,6487,6404 }, {6139,5875,6250 ,2059,6434,6532 }, + {5390,6317,5446 ,3205,3207,5985 }, {4293,3954,4957 ,2732,3463,2733 }, + {5417,5451,5987 ,5923,6219,5621 }, {6207,6242,6241 ,6156,6081,6410 }, + {41,5037,5390 ,6533,3206,3205 }, {5116,3337,3951 ,4126,4125,6528 }, + {6306,6336,6335 ,5669,5670,6534 }, {1729,4153,5993 ,3666,5711,6475 }, + {249,6103,1158 ,644,4220,2744 }, {6103,249,5988 ,4220,644,2516 }, + {4642,6022,5670 ,6226,6535,6227 }, {5533,6172,6195 ,6239,5451,5529 }, + {6239,6223,6055 ,5918,6151,3874 }, {2960,5388,5949 ,4975,5428,5086 }, + {6043,5351,5318 ,6498,6513,6039 }, {5318,3386,3360 ,6039,6491,6485 }, + {3386,3409,5472 ,6491,6490,6301 }, {297,1416,140 ,1918,6100,1919 }, + {3902,685,3935 ,4437,2674,4346 }, {5631,5453,5863 ,6536,937,6537 }, + {6045,5453,5631 ,935,937,6536 }, {4515,6207,6206 ,30,6156,6464 }, + {5879,5417,5987 ,6220,5923,5621 }, {6050,6086,6096 ,4094,6516,4092 }, + {101,6196,6204 ,5,4,4050 }, {253,6228,751 ,2564,1366,1365 }, + {3679,5351,6006 ,6447,6513,6496 }, {3715,5851,5609 ,6124,6095,5658 }, + {3360,5450,3410 ,6485,6525,6483 }, {3410,5450,2204 ,6483,6525,6520 }, + {5450,6032,2204 ,6525,6489,6520 }, {3608,3583,6067 ,5799,5772,6266 }, + {6206,6241,6240 ,6464,6410,5756 }, {6024,6023,3410 ,6482,6040,6483 }, + {3411,1741,5392 ,4280,5695,4022 }, {1700,323,706 ,6072,4767,4784 }, + {6212,6213,6247 ,5562,6459,5575 }, {1022,793,1051 ,4198,4200,4588 }, + {6195,6172,5920 ,5529,5451,5453 }, {3658,5853,6023 ,6521,6038,6040 }, + {5928,6236,3879 ,4072,6538,2549 }, {6213,6256,6255 ,6459,5657,6102 }, + {6247,6213,6255 ,5575,6459,6102 }, {5515,6045,6038 ,5567,935,6264 }, + {6038,6045,5631 ,6264,935,6536 }, {6210,6245,6244 ,5566,5560,5667 }, + {4540,4515,6206 ,19,30,6464 }, {3797,5875,6139 ,2061,6434,2059 }, + {5893,1008,3414 ,6425,6427,4934 }, {6897,8112,8079 ,54,3530,55 }, + {5294,6010,5692 ,5891,6197,6229 }, {4278,4306,4277 ,5583,4552,4532 }, + {3563,1510,3497 ,6074,3788,5738 }, {2896,3563,3497 ,6110,6074,5738 }, + {5415,41,5390 ,5951,6533,3205 }, {4422,4391,6212 ,5561,992,5562 }, + {6074,3515,501 ,6539,1357,4313 }, {3470,5917,3079 ,6401,6402,6540 }, + {2657,1854,3506 ,2185,2184,6492 }, {7719,7697,7744 ,6541,651,6101 }, + {3879,6236,5317 ,2549,6538,6508 }, {3410,2204,5486 ,6483,6520,5718 }, + {3597,3546,3598 ,1810,2557,802 }, {5486,2204,3311 ,5718,6520,5303 }, + {262,5997,970 ,4758,6529,268 }, {359,262,970 ,4841,4758,268 }, + {5997,6019,6018 ,6529,6542,340 }, {6019,4304,6018 ,6542,4579,340 }, + {2318,8049,2319 ,3185,5393,3228 }, {959,3515,535 ,1358,1357,2001 }, + {5940,5926,3714 ,1625,6419,3970 }, {6224,6213,4391 ,6458,6459,992 }, + {1158,6103,252 ,2744,4220,2743 }, {5489,5474,5911 ,4244,4243,5924 }, + {6005,5515,5117 ,5691,5567,1113 }, {6209,6210,6244 ,6012,5566,5667 }, + {6245,6273,6272 ,5560,5683,5755 }, {6243,5356,6271 ,5666,4258,6152 }, + {6206,6207,6241 ,6464,6156,6410 }, {6204,6196,6203 ,4050,4,1249 }, + {5449,5314,3523 ,6543,6411,6544 }, {5951,2321,3509 ,6000,5999,6519 }, + {5934,5370,6091 ,616,2171,2173 }, {1510,5862,747 ,3788,3790,5692 }, + {3497,1510,747 ,5738,3788,5692 }, {949,5029,1083 ,2133,2159,2095 }, + {6138,6139,6250 ,6014,2059,6532 }, {808,556,2960 ,4734,4043,4975 }, + {4292,4517,4374 ,6400,6484,5712 }, {5416,3599,5302 ,6461,6428,3795 }, + {4144,5175,5985 ,4761,6384,5365 }, {5953,6000,3870 ,5659,6094,6096 }, + {7856,7855,7803 ,4635,6545,4636 }, {6061,465,4304 ,6546,4578,4579 }, + {5988,249,20 ,2516,644,3961 }, {6061,501,465 ,6546,4313,4578 }, + {5934,6091,5980 ,616,2173,5557 }, {1729,3517,4286 ,3666,6503,4274 }, + {5213,5214,5234 ,428,6272,6273 }, {6005,5117,186 ,5691,1113,1265 }, + {6244,6245,6272 ,5667,5560,5755 }, {6273,5880,5663 ,5683,5700,4259 }, + {6272,6273,5663 ,5755,5683,4259 }, {5956,6151,5951 ,6240,6024,6000 }, + {6203,6196,6239 ,1249,4,5918 }, {3629,3524,5160 ,5965,6187,6186 }, + {3523,3629,5160 ,6544,5965,6186 }, {5329,5711,6033 ,6191,6248,5620 }, + {6076,5329,6033 ,4217,6191,5620 }, {5576,5475,5876 ,4039,4038,2146 }, + {5530,5915,3870 ,6189,6120,6096 }, {5924,1696,6042 ,6091,6547,5703 }, + {3436,5576,5876 ,2145,4039,2146 }, {5475,5489,5876 ,4038,4244,2146 }, + {843,706,46 ,2423,4784,2232 }, {5302,3599,5706 ,3795,6428,3796 }, + {5895,5749,3731 ,6053,6052,6041 }, {6145,5925,5899 ,6514,5710,6548 }, + {5195,5213,5212 ,6235,428,430 }, {6004,5963,5871 ,6123,6409,4124 }, + {1125,2142,4528 ,5723,4936,4935 }, {706,806,807 ,4784,4783,6073 }, + {882,3396,3271 ,3846,3867,1727 }, {262,5998,5997 ,4758,6549,6529 }, + {5998,6020,6019 ,6549,6550,6542 }, {5997,5998,6019 ,6529,6549,6542 }, + {6020,6021,4304 ,6550,6551,4579 }, {6019,6020,4304 ,6542,6550,4579 }, + {6048,6061,4304 ,6552,6546,4579 }, {6021,6048,4304 ,6551,6552,4579 }, + {6074,501,6061 ,6539,4313,6546 }, {6048,6074,6061 ,6552,6539,6546 }, + {3632,3628,5703 ,5898,4213,5748 }, {6118,5753,5863 ,2074,6481,6537 }, + {5861,5870,5939 ,6453,4275,4276 }, {5453,6118,5863 ,937,2074,6537 }, + {3829,5475,3217 ,6195,4038,6117 }, {6012,6013,5963 ,6408,6060,6409 }, + {6129,6130,3951 ,5983,6300,6528 }, {5123,6129,3951 ,6433,5983,6528 }, + {5789,5386,6258 ,6140,5870,6553 }, {6217,5355,5703 ,6554,6555,5748 }, + {5881,5530,3870 ,6109,6189,6096 }, {6174,6234,6002 ,2381,5973,5975 }, + {1701,1700,2896 ,6476,6072,6110 }, {5875,5490,3444 ,6434,6055,6054 }, + {5528,5416,5302 ,6143,6461,3795 }, {5749,5922,3731 ,6052,4292,6041 }, + {5891,5876,5933 ,5714,2146,615 }, {938,803,322 ,983,144,1250 }, + {5869,3680,4094 ,6224,6497,6225 }, {5903,2912,2357 ,6480,3934,3933 }, + {1153,3012,2624 ,2245,3491,3132 }, {3599,5893,5706 ,6428,6425,3796 }, + {6222,6223,6239 ,6253,6151,5918 }, {6229,5987,5608 ,6556,5621,4356 }, + {4094,3680,4642 ,6225,6497,6226 }, {6577,3816,4426 ,97,5623,1056 }, + {262,670,5998 ,4758,4760,6549 }, {6048,3515,6074 ,6552,1357,6539 }, + {6035,6038,5654 ,6025,6264,6026 }, {5974,6003,3470 ,6474,6122,6401 }, + {5631,5863,6188 ,6536,6537,6174 }, {5654,5631,6188 ,6026,6536,6174 }, + {4292,3525,6058 ,6400,5665,6460 }, {6013,5956,5963 ,6060,6240,6409 }, + {5872,6051,6130 ,6557,6037,6300 }, {6129,5872,6130 ,5983,6557,6300 }, + {5530,5905,5915 ,6189,6142,6120 }, {1293,532,746 ,5764,5766,2661 }, + {6250,5875,3444 ,6532,6434,6054 }, {5953,6135,6000 ,5659,6258,6094 }, + {3446,3781,5487 ,3718,5849,6558 }, {5301,3631,5384 ,2698,5922,6121 }, + {686,2357,6086 ,5770,3933,6516 }, {5749,3747,6249 ,6052,2925,4032 }, + {6227,5361,758 ,6416,5664,5663 }, {7745,7744,7697 ,650,6101,651 }, + {670,5985,5998 ,4760,5365,6549 }, {6021,6020,5998 ,6551,6550,6549 }, + {6048,6085,3515 ,6552,6559,1357 }, {5628,4642,405 ,6560,6226,5662 }, + {7886,2295,2263 ,6561,3160,3080 }, {6164,6165,7062 ,6562,4219,6563 }, + {3856,5995,1268 ,5754,5564,4568 }, {3856,6013,6012 ,5754,6060,6408 }, + {5995,3856,6012 ,5564,5754,6408 }, {1121,5979,6051 ,6564,6028,6037 }, + {5872,1121,6051 ,6557,6564,6037 }, {3606,4164,1410 ,6137,6139,3911 }, + {5216,5235,5215 ,6323,6385,6313 }, {5475,3829,5877 ,4038,6195,4242 }, + {5686,5747,5651 ,5777,6036,5920 }, {5253,5302,3779 ,6163,3795,6565 }, + {2142,3779,6031 ,4936,6565,6429 }, {1608,421,746 ,4128,5815,2661 }, + {7802,7826,7801 ,6566,1616,1618 }, {3973,5388,636 ,6007,5428,6020 }, + {3989,3366,5252 ,5822,5894,5746 }, {1608,1293,421 ,4128,5764,5815 }, + {6086,2357,6096 ,6516,3933,4092 }, {3822,6044,3576 ,5715,614,5890 }, + {1153,8123,1332 ,2245,2247,3492 }, {3758,3712,3714 ,1789,1439,3970 }, + {5670,6022,3787 ,6227,6535,5819 }, {6135,5851,6000 ,6258,6095,6094 }, + {6172,5124,6146 ,5451,6567,6568 }, {4045,5654,4425 ,5916,6026,6268 }, + {3926,3970,3925 ,3125,3225,3227 }, {5175,5998,5985 ,6384,6549,5365 }, + {5175,5494,5998 ,6384,6569,6549 }, {5494,6021,5998 ,6569,6551,6549 }, + {6049,6048,6021 ,6570,6552,6551 }, {2260,6049,6021 ,6571,6570,6551 }, + {6085,6100,3515 ,6559,6572,1357 }, {3515,6100,535 ,1357,6572,2001 }, + {3548,5876,5489 ,6013,2146,4244 }, {5898,5515,6038 ,5706,5567,6264 }, + {1272,3856,1268 ,5740,5754,4568 }, {4164,5449,5540 ,6139,6543,6162 }, + {1661,6269,3611 ,5672,6083,5673 }, {3039,5526,5567 ,2686,6256,6413 }, + {6209,6244,6243 ,6012,5667,5666 }, {2621,5704,3683 ,5416,5579,5417 }, + {5323,5325,5324 ,5701,5655,5986 }, {4528,2159,1125 ,4935,6440,5723 }, + {3953,6124,3715 ,5713,6511,6124 }, {3891,3855,6847 ,3820,1511,4004 }, + {5494,5175,5020 ,6569,6384,6373 }, {1214,6085,6048 ,6573,6559,6552 }, + {1214,6101,6100 ,6573,6574,6572 }, {6085,1214,6100 ,6559,6573,6572 }, + {6101,6121,535 ,6574,6575,2001 }, {6100,6101,535 ,6572,6574,2001 }, + {6140,6153,535 ,2333,5514,2001 }, {6121,6140,535 ,6575,2333,2001 }, + {6140,3704,5040 ,2333,2332,5469 }, {6196,713,6197 ,4,3,6252 }, + {3584,6112,6078 ,5888,6406,6522 }, {6017,5912,3581 ,6506,5892,6504 }, + {5934,5489,5932 ,616,4244,5925 }, {1410,4164,5540 ,3911,6139,6162 }, + {5610,5630,5609 ,6153,4874,5658 }, {4167,5957,6063 ,6173,4054,5685 }, + {6136,4028,5824 ,5810,5823,6517 }, {3328,3288,3307 ,3973,2058,2057 }, + {6104,5858,3436 ,5696,4041,2145 }, {5976,5927,5925 ,5903,5318,5710 }, + {6228,253,2663 ,1366,2564,3593 }, {5905,5416,5528 ,6142,6461,6143 }, + {6008,5706,3779 ,3797,3796,6565 }, {5706,3414,2142 ,3796,4934,4936 }, + {5931,5928,3915 ,6576,4072,1575 }, {4308,5931,3915 ,6577,6576,1575 }, + {5931,6237,6236 ,6576,6578,6538 }, {6003,6001,5917 ,6122,6221,6402 }, + {758,3525,3688 ,5663,5665,5574 }, {1627,758,3688 ,5573,5663,5574 }, + {5929,5045,3628 ,6029,4214,4213 }, {7240,7239,7220 ,4312,1527,4367 }, + {6153,6140,5040 ,5514,2333,5469 }, {5979,1121,6125 ,6028,6564,4395 }, + {6078,6112,3508 ,6522,6406,6505 }, {5904,6189,3621 ,5851,6579,625 }, + {5849,5578,6189 ,5853,587,6579 }, {5904,5849,6189 ,5851,5853,6579 }, + {3583,5652,5651 ,5772,5774,5920 }, {5166,5120,5167 ,3427,5857,5856 }, + {5608,3472,3007 ,4356,3155,818 }, {5982,6229,5608 ,4218,6556,4356 }, + {5193,5252,4466 ,5846,5746,5854 }, {5326,5325,6041 ,3714,5655,3801 }, + {5322,5695,6145 ,4297,6465,6514 }, {4465,6210,6209 ,257,5566,6012 }, + {5928,5931,6236 ,4072,6576,6538 }, {3561,5317,6236 ,6580,6508,6538 }, + {6237,3561,6236 ,6578,6580,6538 }, {4260,4278,4277 ,3527,5583,4532 }, + {5384,5403,6016 ,6121,5921,6228 }, {5846,257,5086 ,6065,6067,6064 }, + {5303,5670,5981 ,5737,6227,6415 }, {3525,5885,5345 ,5665,6502,6438 }, + {5942,5382,5317 ,6581,6509,6508 }, {3561,5942,5317 ,6580,6581,6508 }, + {4488,4154,5382 ,6582,6510,6509 }, {3007,1696,5924 ,818,6547,6091 }, + {1811,1810,1750 ,777,779,745 }, {6621,5629,6605 ,5552,5162,5164 }, + {6058,3525,5345 ,6460,5665,6438 }, {5961,3560,5935 ,6456,5779,5778 }, + {5487,3781,5417 ,6558,5849,5923 }, {5904,3621,4200 ,5851,625,6583 }, + {7128,7129,7160 ,5391,1026,6271 }, {5302,6008,3779 ,3795,3797,6565 }, + {3472,4486,3007 ,3155,819,818 }, {5962,3469,4466 ,5745,5860,5854 }, + {3659,5314,5449 ,3875,6411,6543 }, {5314,3629,3523 ,6411,5965,6544 }, + {4164,3659,5449 ,6139,3875,6543 }, {6001,6081,6181 ,6221,6223,6251 }, + {5942,4488,5382 ,6581,6582,6509 }, {6026,5868,4154 ,6584,6527,6510 }, + {4488,6026,4154 ,6582,6584,6510 }, {3386,168,3409 ,6491,6585,6490 }, + {6148,6161,5868 ,6586,6530,6527 }, {4153,3953,5993 ,5711,5713,6475 }, + {7351,7403,7402 ,6587,6588,6589 }, {1,1022,6202 ,4622,4198,6590 }, + {2896,3845,1949 ,6110,3635,3637 }, {5963,5956,3337 ,6409,6240,4125 }, + {5933,6044,3822 ,615,614,5715 }, {5890,5686,5713 ,6591,5777,5776 }, + {5947,6230,5876 ,6027,2144,2146 }, {6014,6083,6015 ,6592,3989,3988 }, + {7129,7128,7101 ,1026,5391,4073 }, {6196,6222,6239 ,4,6253,5918 }, + {3409,3443,5472 ,6490,6302,6301 }, {3701,6149,5922 ,733,732,4292 }, + {6132,5929,3628 ,5897,6029,4213 }, {7370,7343,7344 ,3750,2750,2749 }, + {5930,7058,5979 ,6593,6594,6028 }, {3666,6068,6137 ,5699,5896,5809 }, + {5939,5649,5865 ,4276,6093,6092 }, {3836,5939,5865 ,6103,4276,6092 }, + {5608,3007,5924 ,4356,818,6091 }, {5982,5608,5924 ,4218,4356,6091 }, + {601,3286,2990 ,3905,56,3646 }, {5663,6136,5909 ,4259,5810,4260 }, + {5952,5991,3620 ,6595,5482,6001 }, {4147,20,3506 ,2517,3961,6492 }, + {6034,5952,3620 ,6049,6595,6001 }, {3012,1153,1332 ,3491,2245,3492 }, + {6026,6148,5868 ,6584,6586,6527 }, {249,348,20 ,644,643,3961 }, + {6161,6599,6187 ,6530,6596,6531 }, {5910,6354,5590 ,5121,6597,6386 }, + {6249,3701,5922 ,4032,733,4292 }, {5915,5975,6072 ,6120,6161,6155 }, + {3804,3832,3831 ,4324,2322,2321 }, {3831,3803,3804 ,2321,6436,4324 }, + {3929,5999,4166 ,5024,5184,6084 }, {3838,5999,1252 ,6257,5184,5023 }, + {6140,6101,6122 ,2333,6574,1797 }, {6295,6553,3283 ,1290,780,262 }, + {6219,6191,6220 ,6598,6599,6600 }, {6660,2138,6219 ,6601,6602,6598 }, + {5185,3632,5703 ,5939,5898,5748 }, {5355,5185,5703 ,6555,5939,5748 }, + {7909,7886,2263 ,6603,6561,3080 }, {3834,1298,6386 ,4083,5088,4085 }, + {5621,5560,1164 ,6604,6605,5087 }, {1298,5621,1164 ,5088,6604,5087 }, + {5913,5076,5560 ,6606,6607,6605 }, {5621,5913,5560 ,6604,6606,6605 }, + {5913,5919,5904 ,6606,6608,5851 }, {5076,5913,5904 ,6607,6606,5851 }, + {5919,5369,5904 ,6608,6609,5851 }, {5320,6053,6145 ,4464,4463,6514 }, + {4623,5939,3836 ,6452,4276,6103 }, {5782,4623,3836 ,5693,6452,6103 }, + {6248,5609,6213 ,6154,5658,6459 }, {3787,6159,5918 ,5819,5082,5820 }, + {1696,3007,3582 ,6547,818,1378 }, {3607,6073,4164 ,6138,3873,6139 }, + {5962,3608,3469 ,5745,5799,5860 }, {6034,3620,6175 ,6049,6001,6004 }, + {5602,3822,5964 ,5716,5715,6507 }, {5964,3576,6017 ,6507,5890,6506 }, + {5706,5893,3414 ,3796,6425,4934 }, {5247,6145,5899 ,4096,6514,6548 }, + {5253,3779,5896 ,6163,6565,6176 }, {5896,3779,4648 ,6176,6565,5722 }, + {3292,3294,3308 ,4067,3895,3894 }, {4485,6209,6208 ,32,6012,6011 }, + {6169,6191,6190 ,2334,6599,6610 }, {6190,6191,6219 ,6610,6599,6598 }, + {3522,6059,5906 ,6451,6446,6417 }, {5452,3905,5386 ,2714,4194,5870 }, + {5449,3523,3893 ,6543,6544,3789 }, {5540,5449,3893 ,6162,6543,3789 }, + {3523,5160,5861 ,6544,6186,6453 }, {3893,3523,5861 ,3789,6544,6453 }, + {7286,7344,7285 ,3460,2749,2751 }, {747,5862,5782 ,5692,3790,5693 }, + {5862,4623,5782 ,3790,6452,5693 }, {6159,3609,5918 ,5082,5081,5820 }, + {806,6073,3607 ,4783,3873,6138 }, {6203,6239,6238 ,1249,5918,5919 }, + {6034,6175,5889 ,6049,6004,1587 }, {6179,3575,3906 ,6486,5887,5697 }, + {5205,4178,5248 ,5219,3825,6469 }, {7973,8020,7972 ,3624,1405,6059 }, + {6170,3725,3537 ,3719,3971,1735 }, {7568,7567,7508 ,5544,5545,6196 }, + {187,1171,3498 ,6439,6441,5800 }, {7311,7310,7287 ,4334,4283,3459 }, + {6076,3248,5870 ,4217,4216,4275 }, {5969,6076,5870 ,6188,4217,4275 }, + {5969,5870,5861 ,6188,4275,6453 }, {5160,5969,5861 ,6186,6188,6453 }, + {5937,1298,3834 ,6611,5088,4083 }, {4044,5937,3834 ,4322,6611,4083 }, + {5937,1382,1298 ,6611,6612,5088 }, {1298,1382,5621 ,5088,6612,6604 }, + {1382,6047,5913 ,6612,6613,6606 }, {5621,1382,5913 ,6604,6612,6606 }, + {6047,5603,5913 ,6613,6614,6606 }, {5913,5603,5919 ,6606,6614,6608 }, + {5402,5369,5919 ,6615,6609,6608 }, {5603,5402,5919 ,6614,6615,6608 }, + {5687,6065,5369 ,6420,6616,6609 }, {5402,5687,5369 ,6615,6420,6609 }, + {2912,5903,1379 ,3934,6480,4137 }, {3620,5907,3860 ,6001,6116,6523 }, + {5703,6165,1464 ,5748,4219,5768 }, {5602,5964,3508 ,5716,6507,6505 }, + {3507,5602,3508 ,5671,5716,6505 }, {3773,3812,3811 ,4279,1513,4323 }, + {537,5567,5955 ,4834,6413,6512 }, {5610,3715,5609 ,6153,6124,5658 }, + {6292,6060,6282 ,5403,5914,6617 }, {7509,7568,7508 ,1874,5544,6196 }, + {381,3930,4247 ,6450,6467,6468 }, {6154,6182,6169 ,2387,6618,2334 }, + {6169,6182,6191 ,2334,6618,6599 }, {5072,4044,6261 ,6619,4322,4321 }, + {3527,5687,5402 ,6620,6420,6615 }, {158,4200,3621 ,5258,6583,625 }, + {2582,1017,3451 ,873,1793,1720 }, {6089,5888,6050 ,6494,5752,4094 }, + {6165,6164,1464 ,4219,6562,5768 }, {6073,3659,4164 ,3873,3875,6139 }, + {2160,5328,5370 ,6621,2172,2171 }, {5956,5860,6151 ,6240,5998,6024 }, + {6038,5631,5654 ,6264,6536,6026 }, {6147,6172,6146 ,5452,5451,6568 }, + {6013,3943,5956 ,6060,4755,6240 }, {422,1701,216 ,4689,6476,4644 }, + {5126,4447,5563 ,6170,717,4605 }, {3018,5905,5530 ,6125,6142,6189 }, + {5294,5692,5912 ,5891,6229,5892 }, {6056,5478,5883 ,5808,5806,5850 }, + {4278,4308,4307 ,5583,6577,4562 }, {5878,5892,5549 ,2684,2683,6435 }, + {5462,5961,5935 ,6418,6456,5778 }, {3725,6170,6182 ,3971,3719,6618 }, + {6154,3725,6182 ,2387,3971,6618 }, {6170,6192,6191 ,3719,6622,6599 }, + {6182,6170,6191 ,6618,3719,6599 }, {6192,6200,6191 ,6622,6623,6599 }, + {6191,6200,6220 ,6599,6623,6600 }, {6200,6231,6220 ,6623,6624,6600 }, + {7851,7850,7825 ,6625,3769,3768 }, {6047,1382,5603 ,6613,6612,6614 }, + {5247,5899,5227 ,4096,6548,4097 }, {5703,1464,932 ,5748,5768,5765 }, + {5246,6134,6039 ,5176,2729,2728 }, {5980,6010,5294 ,5557,6197,5891 }, + {6028,6029,3860 ,5316,1454,6523 }, {5958,3507,3508 ,5702,5671,6505 }, + {6112,5958,3508 ,6406,5702,6505 }, {3631,5487,5417 ,5922,6558,5923 }, + {1723,5476,1261 ,6626,6495,6472 }, {4307,4308,3915 ,4562,6577,1575 }, + {2321,3262,3509 ,5999,6627,6519 }, {3471,5627,3262 ,4104,6628,6627 }, + {6177,3471,3262 ,6629,4104,6627 }, {6246,3666,6274 ,5559,5699,5684 }, + {5947,5948,5717 ,6027,5719,4023 }, {5876,3548,5933 ,2146,6013,615 }, + {4541,3631,3079 ,6630,5922,6540 }, {6192,6170,6200 ,6622,3719,6623 }, + {6231,6262,5072 ,6624,6631,6619 }, {3982,4044,5072 ,6632,4322,6619 }, + {6262,3982,5072 ,6631,6632,6619 }, {4044,3982,5937 ,4322,6632,6611 }, + {3982,1007,1382 ,6632,6633,6612 }, {5937,3982,1382 ,6611,6632,6612 }, + {5484,5603,1382 ,6634,6614,6612 }, {1007,5484,1382 ,6633,6634,6612 }, + {6106,5402,5603 ,6635,6615,6614 }, {5484,6106,5603 ,6634,6635,6614 }, + {5748,3527,5402 ,6636,6620,6615 }, {6106,5748,5402 ,6635,6636,6615 }, + {4653,4600,4679 ,4347,4360,3852 }, {6523,5367,5349 ,5319,6034,5320 }, + {6057,5935,5524 ,6423,5778,6426 }, {966,5526,3039 ,2565,6256,2686 }, + {806,3607,6267 ,4783,6138,6407 }, {5588,3835,5550 ,6088,6046,6047 }, + {1293,6217,932 ,5764,6554,5765 }, {3415,5488,5246 ,6115,2730,5176 }, + {5911,5474,5932 ,5924,4243,5925 }, {1661,6111,6179 ,5672,5674,6486 }, + {635,2938,5534 ,2183,4320,6524 }, {6050,3765,5394 ,4094,4093,5660 }, + {4425,6177,3262 ,6268,6629,6627 }, {2321,4425,3262 ,5999,6268,6627 }, + {5534,1629,5971 ,6524,4207,6479 }, {2938,1629,5534 ,4320,4207,6524 }, + {2204,5867,5866 ,6520,6499,5302 }, {7291,7268,7316 ,3774,3773,5948 }, + {6213,6212,4391 ,6459,5562,992 }, {3631,4541,5487 ,5922,6630,6558 }, + {6170,6193,6200 ,3719,6637,6623 }, {6262,6231,3982 ,6631,6624,6632 }, + {3982,3667,1007 ,6632,6638,6633 }, {6011,5484,1007 ,6639,6634,6633 }, + {5484,5748,6106 ,6634,6636,6635 }, {5451,3446,5608 ,6219,3718,4356 }, + {6217,5703,932 ,6554,5748,5765 }, {3415,6186,5488 ,6115,4365,2730 }, + {5899,5925,5227 ,6548,5710,4097 }, {3560,5853,3658 ,5779,6038,6521 }, + {4153,4292,4374 ,5711,6400,5712 }, {5559,5687,3527 ,6640,6420,6620 }, + {1741,4466,6104 ,5695,5854,5696 }, {955,377,5787 ,3991,4311,4305 }, + {5302,5253,5975 ,3795,6163,6161 }, {5860,4045,2321 ,5998,5916,5999 }, + {3248,5982,5649 ,4216,4218,6093 }, {1261,3979,5591 ,6472,6471,3597 }, + {1629,1379,5971 ,4207,4137,6479 }, {3575,6179,3584 ,5887,6486,5888 }, + {5886,5887,6087 ,6192,6493,6193 }, {2645,3041,3535 ,4418,3182,442 }, + {3451,6183,6170 ,1720,6641,3719 }, {6183,6193,6170 ,6641,6637,3719 }, + {6193,6221,6200 ,6637,6642,6623 }, {6200,6221,6231 ,6623,6642,6624 }, + {3982,6127,3667 ,6632,6643,6638 }, {6127,1058,1007 ,6643,6644,6633 }, + {3667,6127,1007 ,6638,6643,6633 }, {6097,6011,1007 ,6645,6639,6633 }, + {1058,6097,1007 ,6644,6645,6633 }, {5460,5484,6011 ,6646,6634,6639 }, + {6097,5460,6011 ,6645,6646,6639 }, {1608,5992,1293 ,4128,4197,5764 }, + {5355,6217,1293 ,6555,6554,5764 }, {3558,6078,3581 ,5964,6522,6504 }, + {5957,5824,4105 ,4054,6517,4053 }, {5627,6184,6128 ,6628,6089,5981 }, + {4045,4425,2321 ,5916,6268,5999 }, {6104,5254,5858 ,5696,4042,4041 }, + {5824,4028,3411 ,6517,5823,4280 }, {5455,6509,5013 ,686,1452,684 }, + {5322,6145,4023 ,4297,6514,3827 }, {5971,1379,5903 ,6479,4137,6480 }, + {5627,6128,3509 ,6628,5981,6519 }, {3262,5627,3509 ,6627,6628,6519 }, + {6044,5294,3576 ,614,5891,5890 }, {572,571,475 ,1947,1885,1884 }, + {1605,6197,713 ,17,6252,3 }, {3041,3618,3535 ,3182,37,442 }, + {3451,6194,6193 ,1720,6647,6637 }, {6183,3451,6193 ,6641,1720,6637 }, + {6194,6201,6221 ,6647,6648,6642 }, {6193,6194,6221 ,6637,6647,6642 }, + {6232,6231,6221 ,6649,6624,6642 }, {6201,6232,6221 ,6648,6649,6642 }, + {6232,1746,3982 ,6649,6650,6632 }, {6231,6232,3982 ,6624,6649,6632 }, + {3982,1746,6127 ,6632,6650,6643 }, {6025,5748,5484 ,6651,6636,6634 }, + {5460,6025,5484 ,6646,6651,6634 }, {3716,5559,5748 ,6652,6640,6636 }, + {6025,3716,5748 ,6651,6652,6636 }, {5629,5048,5559 ,5162,6421,6640 }, + {3716,5629,5559 ,6652,5162,6640 }, {5992,5355,1293 ,4197,6555,5764 }, + {5580,3717,5630 ,6404,6487,4874 }, {5403,5417,5879 ,5921,5923,6220 }, + {6016,5403,5879 ,6228,5921,6220 }, {6150,1723,7053 ,6653,6626,6654 }, + {5909,5824,4167 ,4260,6517,6173 }, {6270,6271,3780 ,6082,6152,6526 }, + {6142,5854,3632 ,5941,6265,5898 }, {1605,6205,6223 ,17,6463,6151 }, + {6223,6205,6254 ,6151,6463,5758 }, {6055,5129,3659 ,3874,5698,3875 }, + {5705,5592,4567 ,4127,3671,6655 }, {5228,5205,5247 ,4095,5219,4096 }, + {5291,6148,6026 ,6656,6586,6584 }, {5232,5277,5255 ,6238,6237,3682 }, + {4444,6211,6210 ,582,5558,5566 }, {5011,5457,1252 ,6657,6308,5023 }, + {3443,5886,5869 ,6302,6192,6224 }, {3943,4045,5860 ,4755,5916,5998 }, + {3845,5319,6216 ,3635,5739,6658 }, {3953,4374,3018 ,5713,5712,6125 }, + {2291,350,2342 ,1115,1114,5690 }, {35,6228,2663 ,1338,1366,3593 }, + {3451,6171,6194 ,1720,6659,6647 }, {5912,5692,5329 ,5892,6229,6191 }, + {5632,5912,5329 ,6660,5892,6191 }, {5965,5632,5329 ,6190,6660,6191 }, + {3629,3581,3524 ,5965,6504,6187 }, {3471,6150,1219 ,4104,6653,4102 }, + {5854,6131,3632 ,6265,6050,5898 }, {6197,1605,6223 ,6252,17,6151 }, + {1605,4540,6206 ,17,19,6464 }, {6196,6197,6222 ,4,6252,6253 }, + {2636,5386,5789 ,2712,5870,6140 }, {5457,5020,4241 ,6308,6373,6321 }, + {5020,5457,5011 ,6373,6308,6657 }, {5973,5871,6110 ,6246,4124,6247 }, + {42,297,6783 ,5195,1918,3632 }, {5907,3415,5246 ,6116,6115,5176 }, + {5925,5927,5227 ,5710,5318,4097 }, {4572,3638,8038 ,5949,5950,4121 }, + {5987,6229,5982 ,5621,6556,4218 }, {6188,5863,5916 ,6174,6537,6175 }, + {6093,6086,6050 ,5771,6516,4094 }, {3443,5887,5886 ,6302,6493,6192 }, + {6171,3451,1017 ,6659,1720,1793 }, {6201,6194,6171 ,6648,6647,6659 }, + {6263,873,1746 ,6661,6662,6650 }, {6232,6263,1746 ,6649,6661,6650 }, + {873,6109,6127 ,6662,6663,6643 }, {1746,873,6127 ,6650,6662,6643 }, + {6109,4046,1058 ,6663,6664,6644 }, {6127,6109,1058 ,6643,6663,6644 }, + {3045,6097,1058 ,6665,6645,6644 }, {4046,3045,1058 ,6664,6665,6644 }, + {5900,5460,6097 ,6666,6646,6645 }, {3045,5900,6097 ,6665,6666,6645 }, + {5900,5448,5460 ,6666,6667,6646 }, {5381,6025,5460 ,6668,6651,6646 }, + {5448,5381,5460 ,6667,6668,6646 }, {7976,7975,7952 ,1715,6669,5475 }, + {5251,6041,5273 ,3802,3801,3819 }, {5692,6016,5879 ,6229,6228,6220 }, + {5329,5692,5879 ,6191,6229,6220 }, {6090,5874,6053 ,4897,4896,4463 }, + {6029,5976,6066 ,1454,5903,1455 }, {5996,5705,4567 ,6249,4127,6655 }, + {3582,3660,5362 ,1378,709,6670 }, {6205,6240,6254 ,6463,5756,5758 }, + {5863,5753,5916 ,6537,6481,6175 }, {5849,1124,5578 ,5853,5852,587 }, + {6212,6247,6246 ,5562,5575,5559 }, {6211,6212,6246 ,5558,5562,5559 }, + {5567,537,2040 ,6413,4834,4833 }, {4484,4465,6209 ,258,257,6012 }, + {262,359,210 ,4758,4841,4592 }, {1455,5202,808 ,1917,5550,4734 }, + {2140,4565,5447 ,5635,5944,5633 }, {6149,3648,3445 ,732,734,1791 }, + {3385,3836,5921 ,6104,6103,5705 }, {6269,6270,3611 ,6083,6082,5673 }, + {1854,5971,5368 ,2184,6479,5751 }, {5124,3970,6146 ,6567,3225,6568 }, + {3611,6063,6111 ,5673,5685,5674 }, {6157,6171,1017 ,5569,6659,1793 }, + {6157,4063,6171 ,5569,4733,6659 }, {6202,6201,6171 ,6590,6648,6659 }, + {4063,6202,6171 ,4733,6590,6659 }, {6202,6233,6232 ,6590,6671,6649 }, + {6201,6202,6232 ,6648,6590,6649 }, {6232,6233,6263 ,6649,6671,6661 }, + {5183,3716,6025 ,6672,6652,6651 }, {5381,5183,6025 ,6668,6672,6651 }, + {5691,5629,3716 ,6673,5162,6652 }, {5183,5691,3716 ,6672,6673,6652 }, + {5691,5938,5629 ,6673,5163,5162 }, {6078,3508,3581 ,6522,6505,6504 }, + {6203,6238,806 ,1249,5919,4783 }, {7203,7220,7185 ,4366,4367,5073 }, + {3620,5946,5907 ,6001,5481,6116 }, {5533,6195,3609 ,6239,5529,5081 }, + {5992,5185,5355 ,4197,5939,6555 }, {3611,3780,6063 ,5673,6526,5685 }, + {3829,3051,5877 ,6195,6488,4242 }, {6024,5743,5646 ,6482,6432,6431 }, + {5526,252,5897 ,6256,2743,6414 }, {5368,5971,5901 ,5751,6479,6674 }, + {5368,5901,5888 ,5751,6674,5752 }, {808,4987,1455 ,4734,1491,1917 }, + {3606,1410,1510 ,6137,3911,3788 }, {5749,6249,5922 ,6052,4032,4292 }, + {2204,5866,5864 ,6520,5302,5304 }, {3780,4167,6063 ,6526,6173,5685 }, + {5884,3446,5451 ,5848,3718,6219 }, {4269,6199,4229 ,4860,4859,4877 }, + {1426,4063,6157 ,4648,4733,5569 }, {1,6202,4063 ,4622,6590,4733 }, + {8049,8035,8018 ,5393,6675,6676 }, {843,6203,806 ,2423,1249,4783 }, + {3581,5632,5965 ,6504,6660,6190 }, {3524,3581,5965 ,6187,6504,6190 }, + {5824,5957,4167 ,6517,4054,6173 }, {6179,6111,5575 ,6486,5674,5686 }, + {4485,4484,6209 ,32,258,6012 }, {5489,5475,5877 ,4244,4038,4242 }, + {7511,7510,7453 ,6677,1873,1187 }, {1700,422,81 ,6072,4689,4673 }, + {3900,552,8132 ,884,3929,413 }, {5129,3906,3575 ,5698,5697,5887 }, + {6037,3856,1272 ,5235,5754,5740 }, {75,107,930 ,3962,2548,3594 }, + {3688,4153,1729 ,5574,5711,3666 }, {253,1664,249 ,2564,2268,644 }, + {808,5941,4987 ,4734,4976,1491 }, {5941,5967,4987 ,4976,1352,1491 }, + {5876,5891,5948 ,2146,5714,5719 }, {5947,5876,5948 ,6027,2146,5719 }, + {3584,5575,6112 ,5888,5686,6406 }, {6057,5524,5994 ,6423,6426,6424 }, + {3801,5362,3660 ,4974,6670,709 }, {4147,3384,3409 ,2517,6518,6490 }, + {5986,306,1251 ,4603,6234,4604 }, {6202,6252,6233 ,6590,6678,6671 }, + {6233,6252,6263 ,6671,6678,6661 }, {6252,1484,873 ,6678,6679,6662 }, + {6263,6252,873 ,6661,6678,6662 }, {1484,5525,6109 ,6679,6680,6663 }, + {873,1484,6109 ,6662,6679,6663 }, {5525,5589,4046 ,6680,6681,6664 }, + {6109,5525,4046 ,6663,6680,6664 }, {1242,3045,4046 ,6682,6665,6664 }, + {5589,1242,4046 ,6681,6682,6664 }, {5539,5900,3045 ,6683,6666,6665 }, + {1242,5539,3045 ,6682,6683,6665 }, {5539,5448,5900 ,6683,6667,6666 }, + {5271,5381,5448 ,6684,6668,6667 }, {5539,5271,5448 ,6683,6684,6667 }, + {5271,5183,5381 ,6684,6672,6668 }, {5938,5502,5081 ,5163,1256,1258 }, + {806,6238,6073 ,4783,5919,3873 }, {6066,5976,5874 ,1455,5903,4896 }, + {5632,3581,5912 ,6660,6504,5892 }, {1696,3582,5362 ,6547,1378,6670 }, + {3524,5965,5969 ,6187,6190,6188 }, {6273,6274,5880 ,5683,5684,5700 }, + {3499,5394,5628 ,5661,5660,6560 }, {3547,3598,3546 ,803,802,2557 }, + {3879,3904,5928 ,2549,1264,4072 }, {6063,202,5575 ,5685,4105,5686 }, + {5981,6226,3905 ,6415,4192,4194 }, {3087,3131,3130 ,3573,3732,3414 }, + {5943,6283,7088 ,5402,4298,4299 }, {3629,3558,3581 ,5965,5964,6504 }, + {5316,3835,5588 ,6087,6046,6088 }, {3248,5649,5939 ,4216,6093,4276 }, + {7333,7332,3304 ,4353,6685,1050 }, {7208,7226,7225 ,1671,6686,1672 }, + {5949,5967,2960 ,5086,1352,4975 }, {2960,5967,5941 ,4975,1352,4976 }, + {6179,5575,3584 ,6486,5686,5888 }, {5345,5885,6057 ,6438,6502,6423 }, + {253,751,6228 ,2564,1365,1366 }, {168,4147,3409 ,6585,2517,6490 }, + {1022,1,969 ,4198,4622,4199 }, {3515,3719,501 ,1357,1356,4313 }, + {58,1332,8143 ,2156,3492,2570 }, {6202,1022,6252 ,6590,4198,6678 }, + {5176,5183,5271 ,6687,6672,6684 }, {6064,5691,5183 ,6688,6673,6672 }, + {5176,6064,5183 ,6687,6688,6672 }, {5502,5938,5691 ,1256,5163,6673 }, + {6064,5502,5691 ,6688,1256,6673 }, {6218,3560,3658 ,5780,5779,6521 }, + {5351,168,3386 ,6513,6585,6491 }, {4268,6199,4269 ,4848,4859,4860 }, + {6084,6087,3499 ,6194,6193,5661 }, {5361,5462,5935 ,5664,6418,5778 }, + {6087,5394,3499 ,6193,5660,5661 }, {3822,3576,5964 ,5715,5890,6507 }, + {3456,8083,5771 ,1145,2103,2868 }, {2263,2262,7909 ,3080,3359,6603 }, + {5824,3411,4105 ,6517,4280,4053 }, {5351,5513,168 ,6513,6500,6585 }, + {2206,5592,2224 ,176,3671,2660 }, {807,806,6267 ,6073,4783,6407 }, + {5513,4147,168 ,6500,2517,6585 }, {7923,7948,7896 ,816,3625,4355 }, + {2204,5864,3311 ,6520,5304,5303 }, {5129,3575,5293 ,5698,5887,5889 }, + {2663,783,35 ,3593,679,1338 }, {6252,6264,1484 ,6678,6689,6679 }, + {5754,5271,5539 ,6690,6684,6683 }, {5502,6064,5176 ,1256,6688,6687 }, + {5854,3951,6130 ,6265,6528,6300 }, {6051,6132,6130 ,6037,5897,6300 }, + {5211,5191,6433 ,6071,2900,6691 }, {5695,5320,6145 ,6465,4464,6514 }, + {2573,2660,3569 ,3253,455,4381 }, {4485,6208,6207 ,32,6011,6156 }, + {4517,4292,6058 ,6484,6400,6460 }, {6266,1700,3563 ,399,6072,6074 }, + {3870,5915,3686 ,6096,6120,6119 }, {5932,5370,5934 ,5925,2171,616 }, + {422,1700,1701 ,4689,6072,6476 }, {4321,6225,4343 ,4873,4872,1818 }, + {4356,166,4404 ,155,3729,5072 }, {5978,6114,5195 ,5581,1805,6235 }, + {5212,5978,5195 ,430,5581,6235 }, {4246,4247,1729 ,3665,6468,3666 }, + {5314,5293,3629 ,6411,5889,5965 }, {5528,5302,5712 ,6143,3795,6160 }, + {3797,3747,5490 ,2061,2925,6055 }, {4515,4485,6207 ,30,32,6156 }, + {5915,5820,5975 ,6120,6144,6161 }, {3679,5990,5351 ,6447,6692,6513 }, + {807,6267,3606 ,6073,6407,6137 }, {1022,6253,6252 ,4198,6693,6678 }, + {6253,6265,6264 ,6693,6694,6689 }, {6252,6253,6264 ,6678,6693,6689 }, + {6265,4189,1484 ,6694,6695,6679 }, {6264,6265,1484 ,6689,6694,6679 }, + {4189,5184,5525 ,6695,6696,6680 }, {1484,4189,5525 ,6679,6695,6680 }, + {5184,3874,5589 ,6696,6697,6681 }, {5525,5184,5589 ,6680,6696,6681 }, + {3908,1242,5589 ,6698,6682,6681 }, {3874,3908,5589 ,6697,6698,6681 }, + {3776,5539,1242 ,6699,6683,6682 }, {3908,3776,1242 ,6698,6699,6682 }, + {5780,5754,5539 ,6700,6690,6683 }, {3776,5780,5539 ,6699,6700,6683 }, + {4344,5271,5754 ,6701,6684,6690 }, {5780,4344,5754 ,6700,6701,6690 }, + {5968,5176,5271 ,6702,6687,6684 }, {4344,5968,5271 ,6701,6702,6684 }, + {5088,5502,5176 ,5004,1256,6687 }, {5968,5088,5176 ,6702,5004,6687 }, + {6240,1661,6268 ,5756,5672,5757 }, {1723,6150,3471 ,6626,6653,4104 }, + {5476,1723,3471 ,6495,6626,4104 }, {4367,6224,4391 ,228,6458,992 }, + {5233,5978,5212 ,429,5581,430 }, {1204,4499,4500 ,338,4510,4523 }, + {6014,6015,5838 ,6592,3988,3987 }, {5910,6014,5838 ,5121,6592,3987 }, + {3408,3435,3434 ,3484,6703,737 }, {6076,6033,5982 ,4217,5620,4218 }, + {1008,5316,3414 ,6427,6087,4934 }, {1252,3839,3838 ,5023,6310,6257 }, + {6059,3559,3679 ,6446,6412,6447 }, {5897,5990,3679 ,6414,6692,6447 }, + {5316,4528,3414 ,6087,4935,4934 }, {1629,931,1379 ,4207,3944,4137 }, + {6123,5513,5351 ,4221,6500,6513 }, {5981,6007,5562 ,6415,6704,4193 }, + {6137,6068,3989 ,5809,5896,5822 }, {1022,1051,6253 ,4198,4588,6693 }, + {3395,5116,5854 ,5940,4126,6265 }, {3780,5356,4167 ,6526,4258,6173 }, + {5192,5044,3762 ,6454,5173,5172 }, {5916,5476,6177 ,6175,6495,6629 }, + {6052,3762,3835 ,5717,5172,6046 }, {5477,2636,5789 ,5399,2712,6140 }, + {8080,8141,8132 ,1094,3295,413 }, {4299,1511,2431 ,5984,4921,2977 }, + {5486,6052,3835 ,5718,5717,6046 }, {4028,5193,3411 ,5823,5846,4280 }, + {7532,7565,7507 ,6705,5080,6706 }, {5901,5971,5888 ,6674,6479,5752 }, + {5971,5902,5888 ,6479,5769,5752 }, {3506,1854,5368 ,6492,2184,5751 }, + {5990,6123,5351 ,6692,4221,6513 }, {3559,5897,3679 ,6412,6414,6447 }, + {6145,6053,5925 ,6514,4463,5710 }, {5869,6084,3680 ,6224,6194,6497 }, + {3366,5461,5252 ,5894,5744,5746 }, {3908,3874,3776 ,6698,6697,6699 }, + {5116,3951,5854 ,4126,6528,6265 }, {2208,5502,5088 ,1257,1256,5004 }, + {5450,5472,6032 ,6525,6301,6489 }, {5246,5488,6134 ,5176,2730,2729 }, + {2159,5549,1171 ,6440,6435,6441 }, {3836,5865,5921 ,6103,6092,5705 }, + {5892,3498,1171 ,2683,5800,6441 }, {6042,1696,5230 ,5703,6547,5704 }, + {4247,1627,1729 ,6468,5573,3666 }, {4425,5916,6177 ,6268,6175,6629 }, + {5549,5892,1171 ,6435,2683,6441 }, {4278,6138,5931 ,5583,6014,6576 }, + {4308,4278,5931 ,6577,5583,6576 }, {6138,6250,6237 ,6014,6532,6578 }, + {5316,5743,3835 ,6087,6432,6046 }, {5233,5232,5978 ,429,6238,5581 }, + {5983,6082,5232 ,6707,1348,6238 }, {5590,6014,5910 ,6386,6592,5121 }, + {5931,6138,6237 ,6576,6014,6578 }, {6250,3444,3561 ,6532,6054,6580 }, + {3559,6059,3522 ,6412,6446,6451 }, {1696,5362,5230 ,6547,6670,5704 }, + {1701,2896,1949 ,6476,6110,3637 }, {5865,6042,5921 ,6092,5703,5705 }, + {6036,5789,5883 ,6141,6140,5850 }, {5478,6036,5883 ,5806,6141,5850 }, + {6237,6250,3561 ,6578,6532,6580 }, {1620,5872,6129 ,5982,6557,5983 }, + {5245,4949,4724 ,5880,5526,4737 }, {3469,3608,6067 ,5860,5799,6266 }, + {5891,5933,3822 ,5714,615,5715 }, {1051,1858,217 ,4588,4590,4643 }, + {1051,1532,6253 ,4588,4771,6693 }, {6253,1532,6265 ,6693,4771,6694 }, + {1532,5275,4189 ,4771,6708,6695 }, {6265,1532,4189 ,6694,4771,6695 }, + {4349,5184,4189 ,6709,6696,6695 }, {5275,4349,4189 ,6708,6709,6695 }, + {3927,3874,5184 ,6710,6697,6696 }, {4349,3927,5184 ,6709,6710,6696 }, + {3927,6117,3874 ,6710,6711,6697 }, {5959,3776,3874 ,6712,6699,6697 }, + {6117,5959,3874 ,6711,6712,6697 }, {5565,5780,3776 ,6713,6700,6699 }, + {5959,5565,3776 ,6712,6713,6699 }, {5565,3610,4344 ,6713,6714,6701 }, + {5780,5565,4344 ,6700,6713,6701 }, {5873,5968,4344 ,6715,6702,6701 }, + {3610,5873,4344 ,6714,6715,6701 }, {5393,5088,5968 ,5005,5004,6702 }, + {5873,5393,5968 ,6715,5005,6702 }, {6110,5871,3395 ,6247,4124,5940 }, + {6105,6062,1268 ,5565,6478,4568 }, {7370,7422,7396 ,3750,1404,6716 }, + {2343,6563,2210 ,2685,464,2510 }, {5995,6012,5974 ,5564,6408,6474 }, + {5789,6258,5954 ,6140,6553,5868 }, {5883,5789,5954 ,5850,6140,5868 }, + {6258,5386,5954 ,6553,5870,5868 }, {5476,3471,6177 ,6495,4104,6629 }, + {5954,5386,3657 ,5868,5870,5869 }, {5983,5232,5255 ,6707,6238,3682 }, + {6241,6269,1661 ,6410,6083,5672 }, {5895,5942,3561 ,6053,6581,6580 }, + {5743,5486,3835 ,6432,5718,6046 }, {5386,6185,6178 ,5870,6051,5881 }, + {3657,5386,6178 ,5869,5870,5881 }, {5232,6082,5978 ,6238,1348,5581 }, + {6271,5356,3780 ,6152,4258,6526 }, {3444,5895,3561 ,6054,6053,6580 }, + {3731,4488,5942 ,6041,6582,6581 }, {3559,3522,1417 ,6412,6451,6501 }, + {5955,3559,1417 ,6512,6412,6501 }, {3717,3953,3715 ,6487,5713,6124 }, + {5630,5610,6225 ,4874,6153,4872 }, {6185,3892,6178 ,6051,4970,5881 }, + {5956,5951,3337 ,6240,6000,4125 }, {5712,5302,5975 ,6160,3795,6161 }, + {5417,3781,5451 ,5923,5849,6219 }, {5895,3731,5942 ,6053,6041,6581 }, + {6174,6002,6173 ,2381,5975,3711 }, {5935,3658,5524 ,5778,6521,6426 }, + {5426,5447,5970 ,5634,5633,3518 }, {537,5955,1417 ,4834,6512,6501 }, + {5743,6024,5486 ,6432,6482,5718 }, {6007,3787,5562 ,6704,5819,4193 }, + {252,6103,5897 ,2743,4220,6414 }, {5885,5935,6057 ,6502,5778,6423 }, + {4574,5301,1268 ,6477,2698,4568 }, {5974,3470,3079 ,6474,6401,6540 }, + {5995,5974,6062 ,5564,6474,6478 }, {5781,5782,3385 ,5694,5693,6104 }, + {6568,5395,5427 ,6717,5348,5811 }, {6075,6026,4488 ,6042,6584,6582 }, + {3731,6075,4488 ,6041,6042,6582 }, {5628,3609,6144 ,6560,5081,5083 }, + {6091,5384,6010 ,2173,6121,6197 }, {6069,6082,5983 ,6180,1348,6707 }, + {5255,6069,5983 ,3682,6180,6707 }, {58,8143,8128 ,2156,2570,959 }, + {4170,5447,5579 ,3731,5633,5942 }, {1002,1283,5788 ,2976,2978,6086 }, + {3844,4170,5579 ,2443,3731,5942 }, {5788,3844,5579 ,6086,2443,5942 }, + {4299,2431,1002 ,5984,2977,2976 }, {6075,5291,6026 ,6042,6656,6584 }, + {5908,6148,5291 ,4293,6586,6656 }, {6023,3360,3410 ,6040,6485,6483 }, + {6095,5973,6081 ,6222,6246,6223 }, {6081,6110,3395 ,6223,6247,5940 }, + {5981,3787,6007 ,6415,5819,6704 }, {5562,5918,5945 ,4193,5820,6396 }, + {5996,4567,6156 ,6249,6655,6250 }, {7743,7744,7769 ,5534,6101,5535 }, + {5329,5879,5711 ,6191,6220,6248 }, {5393,5362,3801 ,5005,6670,4974 }, + {5871,5963,3337 ,4124,6409,4125 }, {6105,5995,6062 ,5565,5564,6478 }, + {3217,5475,6030 ,6117,4038,4037 }, {4410,4094,5670 ,5735,6225,6227 }, + {6075,5908,5291 ,6042,4293,6656 }, {5559,3527,5748 ,6640,6620,6636 }, + {1121,5936,6125 ,6564,4393,4395 }, {6167,6259,5590 ,5974,6718,6386 }, + {6002,6167,5590 ,5975,5974,6386 }, {6003,5871,5973 ,6122,4124,6246 }, + {5871,5116,3395 ,4124,4126,5940 }, {5671,3599,5416 ,6515,6428,6461 }, + {6199,537,4229 ,4859,4834,4877 }, {537,1417,4229 ,4834,6501,4877 }, + {5670,3787,5981 ,6227,5819,6415 }, {6012,6004,5974 ,6408,6123,6474 }, + {6068,3366,3989 ,5896,5894,5822 }, {1620,1122,5872 ,5982,6719,6557 }, + {6215,5275,1532 ,3636,6708,4771 }, {1949,6215,1532 ,3637,3636,4771 }, + {3845,4349,5275 ,3635,6709,6708 }, {6215,3845,5275 ,3636,3635,6708 }, + {3845,3927,4349 ,3635,6710,6709 }, {3845,6216,3927 ,3635,6658,6710 }, + {6216,5319,6117 ,6658,5739,6711 }, {3927,6216,6117 ,6710,6658,6711 }, + {6117,5319,5959 ,6711,5739,6712 }, {5319,5781,5959 ,5739,5694,6712 }, + {5781,3385,5565 ,5694,6104,6713 }, {5959,5781,5565 ,6712,5694,6713 }, + {5565,3385,3610 ,6713,6104,6714 }, {3385,5921,3610 ,6104,5705,6714 }, + {5230,5873,3610 ,5704,6715,6714 }, {5921,5230,3610 ,5705,5704,6714 }, + {5362,5393,5873 ,6670,5005,6715 }, {5230,5362,5873 ,5704,6670,6715 }, + {8131,8105,8075 ,2307,3791,2308 }, {5897,6123,5990 ,6414,4221,6692 }, + {5951,3509,5123 ,6000,6519,6433 }, {3337,5951,5123 ,4125,6000,6433 }, + {6208,6243,6242 ,6011,5666,6081 }, {3469,6067,5651 ,5860,6266,5920 }, + {3498,6036,5478 ,5800,6141,5806 }, {5193,4466,1741 ,5846,5854,5695 }, + {4444,4443,6211 ,582,703,5558 }, {5854,6130,6131 ,6265,6300,6050 }, + {3902,3935,4807 ,4437,4346,6449 }, {7082,3415,5946 ,6462,6115,5481 }, + {7873,7897,7846 ,817,6015,4162 }, {8086,8099,8087 ,4823,2867,385 }, + {5520,1185,1516 ,1304,6159,1254 }, {257,3284,1380 ,6067,6720,6721 }, + {5706,6031,3779 ,3796,6429,6565 }, {1313,2373,1947 ,3674,6722,6723 }, + {7089,7115,7103 ,5608,1024,6455 }, {2220,1072,6709 ,6724,6725,6726 }, + {5085,1313,1947 ,3675,3674,6723 }, {2872,4204,784 ,311,38,4211 }, + {4542,6378,6481 ,6727,6728,6729 }, {5069,257,255 ,6730,6067,6731 }, + {352,351,255 ,593,592,6731 }, {5578,3621,6189 ,587,625,6579 }, + {6965,6964,6944 ,6732,6733,2633 }, {7005,7046,7004 ,6734,6735,5502 }, + {6914,6945,6913 ,3532,2632,2634 }, {6945,6965,6944 ,2632,6732,2633 }, + {2406,6530,1217 ,6736,6398,6399 }, {1215,6644,6950 ,3937,5098,1349 }, + {7719,7744,7743 ,6541,6101,5534 }, {4986,3399,5441 ,5096,715,5194 }, + {4831,4486,3472 ,3154,819,3155 }, {144,6430,5353 ,853,2748,851 }, + {7005,7004,6964 ,6734,5502,6733 }, {6965,7005,6964 ,6732,6734,6733 }, + {7046,7045,7004 ,6735,5503,5502 }, {2335,2284,7045 ,6737,6738,5503 }, + {7046,2335,7045 ,6735,6737,5503 }, {3609,5920,6990 ,5081,5453,6739 }, + {6858,6859,6910 ,6740,472,6741 }, {6158,7087,7086 ,6112,6742,6743 }, + {5978,6079,6114 ,5581,5580,1805 }, {3598,6598,3597 ,802,804,1810 }, + {6598,5908,3597 ,804,4293,1810 }, {4988,5020,5011 ,6744,6373,6657 }, + {6569,4988,5011 ,6745,6744,6657 }, {1118,4853,2284 ,6746,6747,6738 }, + {2335,1118,2284 ,6737,6746,6738 }, {6284,7086,5952 ,6113,6743,6595 }, + {3445,3597,5908 ,1791,1810,4293 }, {7062,1464,6164 ,6563,5768,6562 }, + {1380,6313,6312 ,6721,6748,6749 }, {879,4122,3397 ,1155,137,136 }, + {6859,6911,6910 ,472,474,6741 }, {255,257,1380 ,6731,6067,6721 }, + {6532,3496,6556 ,6184,3105,4273 }, {6557,6599,6598 ,6750,6596,804 }, + {6556,6557,6598 ,4273,6750,804 }, {6148,5908,6598 ,6586,4293,804 }, + {6599,6148,6598 ,6596,6586,804 }, {7453,7452,7424 ,1187,1875,1188 }, + {4568,5074,185 ,1492,6751,1493 }, {7736,2451,450 ,6752,2102,1653 }, + {3496,6557,6556 ,3105,6750,4273 }, {3733,4021,4426 ,5610,1712,1056 }, + {6690,7022,3649 ,6753,6754,6755 }, {5363,5383,6506 ,3269,3547,3549 }, + {2304,3302,2303 ,6756,6757,6758 }, {6571,5367,6523 ,4146,6034,5319 }, + {6468,6510,3496 ,6759,6760,3105 }, {3464,6468,3496 ,3151,6759,3105 }, + {6510,6558,6557 ,6760,6761,6750 }, {3496,6510,6557 ,3105,6760,6750 }, + {6558,6559,6557 ,6761,6762,6750 }, {6559,6600,6599 ,6762,6763,6596 }, + {6557,6559,6599 ,6750,6762,6596 }, {6600,6187,6599 ,6763,6531,6596 }, + {5823,7038,7073 ,661,3832,662 }, {6808,6807,6753 ,3465,6764,6765 }, + {3421,2851,3278 ,1156,3051,1158 }, {6703,6754,6701 ,5161,713,6766 }, + {6754,6789,6753 ,713,6767,6765 }, {7451,7509,7479 ,3963,1874,4423 }, + {3358,3357,3383 ,880,883,882 }, {5694,5715,5693 ,5326,4076,4075 }, + {3408,6468,3435 ,3484,6759,6703 }, {6533,6559,6558 ,6768,6762,6761 }, + {6510,6533,6558 ,6760,6768,6761 }, {6190,6219,2138 ,6610,6598,6602 }, + {6808,6859,6858 ,3465,472,6740 }, {6789,6808,6753 ,6767,3465,6765 }, + {5086,257,5069 ,6064,6067,6730 }, {7267,7291,7266 ,4331,3774,6769 }, + {5027,2272,4755 ,5516,1201,6770 }, {3936,3733,4024 ,5599,5610,5600 }, + {5693,7028,6988 ,4075,3587,6771 }, {5694,6989,6949 ,5326,4586,4585 }, + {5367,6571,5401 ,6034,4146,4145 }, {6393,6439,3408 ,1556,6772,3484 }, + {3357,6393,3408 ,883,1556,3484 }, {6439,6469,6468 ,6772,6773,6759 }, + {3408,6439,6468 ,3484,6772,6759 }, {6469,6511,6510 ,6773,6774,6760 }, + {6468,6469,6510 ,6759,6773,6760 }, {6510,6511,6533 ,6760,6774,6768 }, + {6511,6560,6559 ,6774,6775,6762 }, {6533,6511,6559 ,6768,6774,6762 }, + {6601,6600,6559 ,6776,6763,6762 }, {6560,6601,6559 ,6775,6776,6762 }, + {6601,6099,6187 ,6776,6777,6531 }, {6600,6601,6187 ,6763,6776,6531 }, + {297,6464,6783 ,1918,3633,3632 }, {6919,6918,6895 ,6778,6779,5434 }, + {2211,4988,1270 ,6780,6744,6781 }, {4988,6569,1270 ,6744,6745,6781 }, + {6948,6947,6920 ,6782,4587,6783 }, {6525,6526,6573 ,3703,6784,3733 }, + {6614,6651,6650 ,3886,6785,3833 }, {5300,6526,6525 ,3793,6784,3703 }, + {6866,6896,6842 ,6786,6787,5433 }, {6318,6319,5151 ,6788,6789,1803 }, + {7064,7048,7007 ,5181,6790,4628 }, {5415,5579,4565 ,5951,5942,5944 }, + {6393,6419,6439 ,1556,6791,6772 }, {6469,6485,6511 ,6773,6792,6774 }, + {3449,3478,3482 ,2440,885,2494 }, {6910,6911,6941 ,6741,474,6793 }, + {2271,3281,6309 ,5071,6794,3690 }, {5742,3815,3373 ,6448,5556,693 }, + {2244,6660,4570 ,6795,6601,4082 }, {5087,1117,6679 ,6796,4928,4824 }, + {2340,2303,2308 ,6797,6758,6798 }, {6389,6868,5041 ,5301,5554,6799 }, + {5559,5048,5687 ,6640,6421,6420 }, {2093,6342,2957 ,553,5265,6326 }, + {2303,3302,2308 ,6758,6757,6798 }, {6158,7086,6284 ,6112,6743,6113 }, + {7083,5991,5952 ,5483,5482,6595 }, {6318,5151,6356 ,6788,1803,3787 }, + {7453,7510,7452 ,1187,1873,1875 }, {2095,5081,1117 ,6800,1258,4928 }, + {7924,7974,7923 ,815,4171,816 }, {2237,2236,2373 ,6801,6802,6722 }, + {7087,5514,7088 ,6742,6128,4299 }, {6090,6053,6260 ,4897,4463,3397 }, + {5009,6391,6463 ,6803,6804,5159 }, {4575,7585,7640 ,3128,6805,5351 }, + {5514,7087,6158 ,6128,6742,6112 }, {5447,5564,5579 ,5633,5943,5942 }, + {5751,5752,7038 ,5878,4374,3832 }, {3359,6420,6419 ,824,6806,6791 }, + {6393,3359,6419 ,1556,824,6791 }, {6419,6420,6439 ,6791,6806,6772 }, + {6420,6470,6469 ,6806,6807,6773 }, {6439,6420,6469 ,6772,6806,6773 }, + {6470,6471,6485 ,6807,6808,6792 }, {6469,6470,6485 ,6773,6807,6792 }, + {6471,6512,6511 ,6808,6809,6774 }, {6485,6471,6511 ,6792,6808,6774 }, + {6561,6560,6511 ,6810,6775,6774 }, {6512,6561,6511 ,6809,6810,6774 }, + {6561,6180,6601 ,6810,6811,6776 }, {6560,6561,6601 ,6775,6810,6776 }, + {6601,6180,6099 ,6776,6811,6777 }, {3674,6160,6099 ,4286,6442,6777 }, + {6180,3674,6099 ,6811,4286,6777 }, {7033,7034,6152 ,6395,6045,6044 }, + {6682,5087,6679 ,6812,6796,4824 }, {184,4016,4017 ,4052,5969,5342 }, + {6942,6962,6961 ,521,3260,6813 }, {7000,7001,7024 ,6814,6815,6816 }, + {6160,6187,6099 ,6442,6531,6777 }, {7649,7648,7621 ,3950,3952,6010 }, + {5454,6682,3978 ,6817,6812,4549 }, {6711,2450,5014 ,3473,3444,6818 }, + {6554,3261,6521 ,3647,5015,6819 }, {350,5117,6115 ,1114,1113,3645 }, + {6713,6644,1215 ,6820,5098,3937 }, {6713,5056,6644 ,6820,1300,5098 }, + {3261,4190,2384 ,5015,3439,6821 }, {6844,6896,6866 ,6822,6787,6786 }, + {7105,7130,7104 ,5279,6823,5280 }, {7220,7219,7184 ,4367,1529,5074 }, + {6962,7001,7000 ,3260,6815,6814 }, {7025,7043,7042 ,6824,5895,5338 }, + {6319,5118,5125 ,6789,6825,5467 }, {3359,6394,6420 ,824,6826,6806 }, + {6420,6471,6470 ,6806,6808,6807 }, {6602,6180,6561 ,6827,6811,6810 }, + {6918,6946,6919 ,6779,594,6778 }, {1025,6657,6714 ,5299,6828,5300 }, + {2337,2405,5578 ,6829,4165,587 }, {2245,6359,6344 ,1260,6830,1450 }, + {5512,5511,5471 ,1589,1588,2051 }, {6379,6380,6394 ,6831,6832,6826 }, + {3359,6379,6394 ,824,6831,6826 }, {6380,6421,6420 ,6832,6833,6806 }, + {6394,6380,6420 ,6826,6832,6806 }, {6421,6472,6471 ,6833,6834,6808 }, + {6420,6421,6471 ,6806,6833,6808 }, {6472,6513,6512 ,6834,6835,6809 }, + {6471,6472,6512 ,6808,6834,6809 }, {6513,3600,6561 ,6835,5331,6810 }, + {6512,6513,6561 ,6809,6835,6810 }, {3623,6602,6561 ,4206,6827,6810 }, + {3600,3623,6561 ,5331,4206,6810 }, {6602,3623,6180 ,6827,4206,6811 }, + {6359,5074,4568 ,6830,6751,1492 }, {6344,6359,4568 ,1450,6830,1492 }, + {6739,6738,5437 ,6836,5057,6259 }, {6754,6753,6701 ,713,6765,6766 }, + {1990,5059,1217 ,1305,1253,6399 }, {7042,2251,7077 ,5338,3676,6837 }, + {7041,7042,7077 ,6838,5338,6837 }, {2251,5085,7077 ,3676,3675,6837 }, + {5085,4142,7077 ,3675,6839,6837 }, {6966,7006,6965 ,6840,6841,6732 }, + {2406,1217,6814 ,6736,6399,6842 }, {6421,6440,6472 ,6833,6843,6834 }, + {5527,3221,835 ,5977,183,3030 }, {2320,8049,8018 ,1406,5393,6676 }, + {6573,6574,6613 ,3733,3885,3746 }, {6342,2093,6555 ,5265,553,597 }, + {6854,6893,6906 ,3344,6844,4294 }, {6088,6069,5255 ,3670,6180,3682 }, + {6389,5041,5102 ,5301,6799,6845 }, {6688,6389,5102 ,6846,5301,6845 }, + {5054,5053,6688 ,6847,6848,6846 }, {5053,6389,6688 ,6848,5301,6846 }, + {6708,5053,5054 ,6148,6848,6847 }, {6696,6708,5054 ,6849,6148,6847 }, + {6442,6300,6708 ,6850,6146,6148 }, {2237,2308,6297 ,6801,6798,6851 }, + {6297,6300,6442 ,6851,6146,6850 }, {2308,6300,6297 ,6798,6146,6851 }, + {2409,2340,2237 ,6852,6797,6801 }, {3326,6380,6379 ,826,6832,6831 }, + {7047,7046,7005 ,6853,6735,6734 }, {7006,7047,7005 ,6841,6853,6734 }, + {2455,5001,4018 ,475,477,5084 }, {6613,6614,6650 ,3746,3886,3833 }, + {2340,2409,1075 ,6797,6852,6854 }, {2340,2308,2237 ,6797,6798,6801 }, + {4853,2340,1075 ,6747,6797,6854 }, {6959,6998,6997 ,6003,6855,4368 }, + {8019,8018,7972 ,1407,6676,6059 }, {5436,6681,5405 ,6201,5406,5518 }, + {5331,5330,5277 ,6198,3345,6237 }, {4960,306,5986 ,6856,6234,4603 }, + {4191,4296,3802 ,4474,6023,4475 }, {6737,6736,6704 ,6857,1622,5160 }, + {6681,6737,6704 ,5406,6857,5160 }, {2284,2248,7044 ,6738,6858,6859 }, + {7045,7044,7002 ,5503,6859,1047 }, {7006,7005,6965 ,6841,6734,6732 }, + {6941,6942,6961 ,6793,521,6813 }, {6966,6965,6914 ,6840,6732,3532 }, + {6141,6140,6122 ,2386,2333,1797 }, {6347,6381,6380 ,451,6860,6832 }, + {3326,6347,6380 ,826,451,6832 }, {6381,6422,6421 ,6860,6861,6833 }, + {6380,6381,6421 ,6832,6860,6833 }, {6422,6441,6440 ,6861,6862,6843 }, + {6421,6422,6440 ,6833,6861,6843 }, {6441,6473,6472 ,6862,6863,6834 }, + {6440,6441,6472 ,6843,6862,6834 }, {3550,6513,6472 ,4179,6835,6834 }, + {6473,3550,6472 ,6863,4179,6834 }, {6513,3550,3600 ,6835,4179,5331 }, + {6742,6746,4784 ,3422,6864,3423 }, {6864,6863,6839 ,6865,6866,6867 }, + {6840,6864,6839 ,6868,6865,6867 }, {7045,2284,7044 ,5503,6738,6859 }, + {4853,1075,2248 ,6747,6854,6858 }, {2284,4853,2248 ,6738,6747,6858 }, + {4746,6302,2210 ,5260,6869,2510 }, {2373,2236,2235 ,6722,6802,6870 }, + {7241,7287,7240 ,4465,3459,4312 }, {5140,1936,2094 ,3712,848,5570 }, + {7026,7045,7002 ,5504,5503,1047 }, {7026,7002,7003 ,5504,1047,1046 }, + {6915,6966,6914 ,6871,6840,3532 }, {7049,7064,7007 ,5454,5181,4628 }, + {6571,6572,6611 ,4146,3734,1391 }, {6840,6839,6792 ,6868,6867,5056 }, + {6812,6840,6792 ,6872,6868,5056 }, {6912,6913,6943 ,519,2634,520 }, + {1118,2304,2340 ,6746,6756,6797 }, {7002,7044,7043 ,1047,6859,5895 }, + {6291,6690,6027 ,6873,6753,6874 }, {6690,6687,6027 ,6753,6875,6874 }, + {455,456,1344 ,2205,301,300 }, {6351,8077,8079 ,1568,2078,55 }, + {3495,3496,6532 ,735,3105,6184 }, {6690,3649,4549 ,6753,6755,877 }, + {6687,6690,4549 ,6875,6753,877 }, {2236,6291,6027 ,6802,6873,6874 }, + {1947,2373,2235 ,6723,6722,6870 }, {2235,2236,6027 ,6870,6802,6874 }, + {3649,6661,6621 ,6755,6876,5552 }, {4549,3649,6621 ,877,6755,5552 }, + {5028,4027,1938 ,5109,3470,5934 }, {6661,5048,5629 ,6876,6421,5162 }, + {7004,7026,7003 ,5502,5504,1046 }, {206,3373,3815 ,3932,693,5556 }, + {7004,7003,6963 ,5502,1046,1048 }, {6915,6914,6862 ,6871,3532,6877 }, + {6312,6313,6747 ,6749,6748,6878 }, {7048,7047,7006 ,6790,6853,6841 }, + {6812,6792,6793 ,6872,5056,6879 }, {6794,6812,6793 ,6880,6872,6879 }, + {6911,6942,6941 ,474,521,6793 }, {6861,6913,6912 ,3533,2634,519 }, + {6860,6861,6912 ,473,3533,519 }, {6754,6808,6789 ,713,3465,6767 }, + {6687,4549,5077 ,6875,877,876 }, {6617,6616,6576 ,6881,6882,6883 }, + {6617,6655,6616 ,6881,5407,6882 }, {6655,6654,6616 ,5407,6884,6882 }, + {3556,3557,3580 ,3385,6070,6069 }, {4010,7030,4043 ,4343,6885,419 }, + {4009,4010,4043 ,3870,4343,419 }, {7030,3765,6096 ,6885,4093,4092 }, + {6685,6687,5077 ,6886,6875,876 }, {6621,6661,5629 ,5552,6876,5162 }, + {5404,6617,6576 ,3559,6881,6883 }, {6964,7004,6963 ,6733,5502,1048 }, + {6894,6915,6862 ,6887,6871,6877 }, {6894,6862,6863 ,6887,6877,6866 }, + {6348,6382,6381 ,1607,6888,6860 }, {6347,6348,6381 ,451,1607,6860 }, + {6382,6423,6422 ,6888,6889,6861 }, {6381,6382,6422 ,6860,6888,6861 }, + {6423,3465,6441 ,6889,6890,6862 }, {6422,6423,6441 ,6861,6889,6862 }, + {3465,3500,6473 ,6890,4177,6863 }, {6441,3465,6473 ,6862,6890,6863 }, + {6473,3500,3550 ,6863,4177,4179 }, {6794,6793,6755 ,6880,6879,5055 }, + {6756,6794,6755 ,6891,6880,5055 }, {6913,6944,6943 ,2634,2633,520 }, + {6480,5350,6507 ,1524,1526,2680 }, {7007,7048,7006 ,4628,6790,6841 }, + {5930,6125,6143 ,6593,4395,4394 }, {6140,6121,6101 ,2333,6575,6574 }, + {6893,6856,6906 ,6844,6892,4294 }, {4981,4950,1216 ,1350,1302,6893 }, + {5011,1264,4746 ,6657,5022,5260 }, {2279,2248,1313 ,5337,6858,3674 }, + {6964,6963,6943 ,6733,1048,520 }, {6864,6894,6863 ,6865,6887,6866 }, + {6741,6740,6705 ,5388,6894,4158 }, {6310,6746,6742 ,3918,6864,3422 }, + {5907,5246,3860 ,6116,5176,6523 }, {6382,3465,6423 ,6888,6890,6889 }, + {7102,7101,7088 ,1240,4073,4299 }, {4567,6155,6156 ,6655,3726,6250 }, + {3399,5143,5441 ,715,6099,5194 }, {6756,6755,6740 ,6891,5055,6894 }, + {3973,5446,5388 ,6007,5985,5428 }, {2235,6027,2139 ,6870,6874,6895 }, + {6737,6790,6736 ,6857,711,1622 }, {6619,6554,6521 ,1272,3647,6819 }, + {7076,7075,7039 ,4886,4420,4376 }, {1026,4981,1216 ,6218,1350,6893 }, + {1072,1026,1216 ,6725,6218,6893 }, {6172,4010,5124 ,5451,4343,6567 }, + {5686,5890,5747 ,5777,6591,6036 }, {4261,3777,4290 ,1583,1582,6270 }, + {6750,6751,6786 ,631,5242,4139 }, {6961,7000,6960 ,6813,6814,6896 }, + {7000,7042,7041 ,6814,5338,6838 }, {6999,7000,7041 ,6897,6814,6838 }, + {6909,6910,6960 ,6898,6741,6896 }, {7000,6999,6960 ,6814,6897,6896 }, + {6858,6910,6857 ,6740,6741,6899 }, {6910,6961,6960 ,6741,6813,6896 }, + {6944,6964,6943 ,2633,6733,520 }, {6738,6791,6737 ,5057,6900,6857 }, + {6348,6357,6382 ,1607,5271,6888 }, {6383,3437,3465 ,6901,4142,6890 }, + {6382,6383,3465 ,6888,6901,6890 }, {5694,7009,5716 ,5326,5325,6075 }, + {351,5069,255 ,592,6730,6731 }, {3418,1554,248 ,6366,6304,4114 }, + {5491,5479,5516 ,5419,5387,5420 }, {6741,6756,6740 ,5388,6891,6894 }, + {2221,2291,5114 ,6902,1115,5689 }, {1264,5011,1252 ,5022,6657,5023 }, + {7025,7002,7043 ,6824,1047,5895 }, {6862,6914,6861 ,6877,3532,3533 }, + {6998,7040,7039 ,6855,6903,4376 }, {7040,7076,7039 ,6903,4886,4376 }, + {6910,6909,6857 ,6741,6898,6899 }, {6838,6858,6806 ,6904,6740,6905 }, + {6807,6838,6806 ,6764,6904,6905 }, {6858,6857,6806 ,6740,6899,6905 }, + {3435,3464,3434 ,6703,3151,737 }, {4780,4819,3139 ,5150,5216,5400 }, + {6532,6556,3547 ,6184,4273,803 }, {6146,3970,3926 ,6568,3225,3125 }, + {6899,6146,3926 ,4005,6568,3125 }, {3787,6022,6159 ,5819,6535,5082 }, + {5533,5628,3765 ,6239,6560,4093 }, {7030,5533,3765 ,6885,6239,4093 }, + {4835,4021,3936 ,2130,1712,5599 }, {6753,6752,6700 ,6765,6906,6907 }, + {4547,5058,2163 ,242,5647,243 }, {6071,5590,6354 ,1346,6386,6597 }, + {7675,7697,7719 ,5192,651,6541 }, {6755,6738,6739 ,5055,5057,6836 }, + {6358,6383,6382 ,5270,6901,6888 }, {6357,6358,6382 ,5271,5270,6888 }, + {5628,5533,3609 ,6560,6239,5081 }, {6740,6755,6739 ,6894,5055,6836 }, + {1882,4547,4543 ,894,242,3002 }, {6810,6861,6860 ,6908,3533,473 }, + {6809,6810,6860 ,712,6908,473 }, {6807,6806,6752 ,6764,6905,6906 }, + {3464,3435,6468 ,3151,6703,6759 }, {4043,4007,4009 ,419,3909,3870 }, + {4987,6464,5966 ,1491,3633,5054 }, {5059,3177,4233 ,1253,1255,6185 }, + {4950,5005,4115 ,1302,1301,6909 }, {6862,6861,6810 ,6877,3533,6908 }, + {6701,6753,6700 ,6766,6765,6907 }, {6302,5078,1074 ,6869,5261,2715 }, + {6618,6656,6617 ,6910,5517,6881 }, {6791,6790,6737 ,6900,711,6857 }, + {7744,7795,7794 ,6101,3804,5749 }, {5437,6738,6681 ,6259,5057,5406 }, + {6840,6894,6864 ,6868,6887,6865 }, {6740,6739,5437 ,6894,6836,6259 }, + {342,209,4892 ,4542,4499,5386 }, {6844,6843,6795 ,6822,6911,5418 }, + {5693,6988,6947 ,4075,6771,4587 }, {6810,6809,6790 ,6908,712,711 }, + {6864,6915,6894 ,6865,6871,6887 }, {7620,7647,7675 ,5033,4967,5192 }, + {5950,5013,8108 ,2873,684,2287 }, {6753,6807,6752 ,6765,6764,6906 }, + {7769,7794,7768 ,5535,5749,5536 }, {1170,5627,3471 ,4103,6628,4104 }, + {6847,6900,6899 ,4004,6912,4005 }, {4043,7030,6096 ,419,6885,4092 }, + {3408,3382,3383 ,3484,870,882 }, {6752,6806,6788 ,6906,6905,6913 }, + {6701,6700,6652 ,6766,6907,6914 }, {6653,6701,6652 ,6915,6766,6914 }, + {6656,6655,6617 ,5517,5407,6881 }, {6738,6737,6681 ,5057,6857,5406 }, + {1076,4617,7075 ,4887,4431,4420 }, {5477,5442,5315 ,5399,5174,6445 }, + {6843,6842,6813 ,6911,5433,6916 }, {6350,3362,6358 ,704,1200,5270 }, + {6349,6350,6358 ,705,704,5270 }, {3362,6384,6383 ,1200,1786,6901 }, + {6358,3362,6383 ,5270,1200,6901 }, {6384,3387,6383 ,1786,1787,6901 }, + {6383,3387,3437 ,6901,1787,4142 }, {6791,6810,6790 ,6900,6908,711 }, + {5910,6079,6354 ,5121,5580,6597 }, {6987,7007,6966 ,6917,4628,6840 }, + {6844,6795,5551 ,6822,5418,6260 }, {3814,6799,3855 ,5511,6918,1511 }, + {6799,6761,3855 ,6918,6919,1511 }, {6761,6848,6847 ,6919,6920,4004 }, + {3855,6761,6847 ,1511,6919,4004 }, {6848,6901,6900 ,6920,6921,6912 }, + {6847,6848,6900 ,4004,6920,6912 }, {6901,6899,6900 ,6921,4005,6912 }, + {6147,6146,6899 ,5452,6568,4005 }, {6901,6147,6899 ,6921,5452,4005 }, + {6744,5188,3350 ,3986,4812,2541 }, {7900,7926,7899 ,6922,6923,4116 }, + {3277,8098,2901 ,961,907,3468 }, {6144,4642,5628 ,5083,6226,6560 }, + {2160,5474,2154 ,6621,4243,5321 }, {6616,6653,6615 ,6882,6915,6924 }, + {6297,6696,6694 ,6851,6849,6925 }, {6534,6578,3656 ,4257,6926,4195 }, + {973,6465,2141 ,2017,6927,2015 }, {5593,6920,6896 ,5935,6783,6787 }, + {6795,6843,6813 ,5418,6911,6916 }, {3504,3441,3505 ,2986,2987,6928 }, + {3556,3521,3504 ,3385,4307,2986 }, {1285,1226,1320 ,1944,2354,2356 }, + {3605,3626,3604 ,3383,4196,3384 }, {6572,6612,6611 ,3734,1389,1391 }, + {6125,5930,5979 ,4395,6593,6028 }, {5404,5371,5405 ,3559,3558,5518 }, + {6715,6761,6799 ,6929,6919,6918 }, {3814,6715,6799 ,5511,6929,6918 }, + {1936,2341,2094 ,848,845,5570 }, {1590,1552,1656 ,3028,2266,1166 }, + {3605,3556,3580 ,3383,3385,6069 }, {6159,4642,6144 ,5082,6226,5083 }, + {5087,5077,2095 ,6796,876,6800 }, {6466,6758,973 ,6930,6931,2017 }, + {6909,6908,6857 ,6898,6932,6899 }, {5437,6681,5436 ,6259,5406,6201 }, + {2957,6342,1120 ,6326,5265,5339 }, {6564,6508,6639 ,6933,6934,3699 }, + {3320,6564,6639 ,5455,6933,3699 }, {3192,4784,6387 ,3063,3423,6935 }, + {3656,6578,3714 ,4195,6926,3970 }, {7148,7147,7120 ,6936,6937,6938 }, + {5405,6618,5404 ,5518,6910,3559 }, {5473,7096,5120 ,3429,4337,5857 }, + {6022,4642,6159 ,6535,6226,5082 }, {7286,7285,7261 ,3460,2751,1528 }, + {2139,6685,6682 ,6895,6886,6812 }, {3714,3655,3656 ,3970,3936,4195 }, + {1809,6605,5081 ,878,5164,1258 }, {6682,6685,5087 ,6812,6886,6796 }, + {2095,1809,5081 ,6800,878,1258 }, {5077,1809,2095 ,876,878,6800 }, + {6685,5077,5087 ,6886,876,6796 }, {5359,3815,5977 ,3713,5556,809 }, + {1074,6466,973 ,2715,6930,2017 }, {6466,6465,6758 ,6930,6927,6931 }, + {6653,6652,6615 ,6915,6914,6924 }, {6576,6616,6575 ,6883,6882,6939 }, + {6354,6079,6377 ,6597,5580,1347 }, {1562,5130,4392 ,4649,5571,844 }, + {4856,2244,4570 ,4081,6795,4082 }, {3288,3328,3362 ,2058,3973,1200 }, + {6321,3288,3362 ,706,2058,1200 }, {5441,297,42 ,5194,1918,5195 }, + {6001,6156,5917 ,6221,6250,6402 }, {6443,6486,3557 ,6940,6941,6070 }, + {3504,6443,3557 ,2986,6940,6070 }, {6486,6534,3626 ,6941,4257,4196 }, + {3557,6486,3626 ,6070,6941,4196 }, {5747,5890,6214 ,6036,6591,6942 }, + {6578,6092,3714 ,6926,1623,3970 }, {5742,5977,3815 ,6448,809,5556 }, + {6302,1074,2210 ,6869,2715,2510 }, {2342,5008,2221 ,5690,6118,6902 }, + {6640,5435,6611 ,4224,4223,1391 }, {6176,6716,6715 ,1624,6943,6929 }, + {7232,7231,7196 ,2124,6944,2401 }, {6716,6762,6761 ,6943,6945,6919 }, + {6715,6716,6761 ,6929,6943,6919 }, {6762,6815,6761 ,6945,6946,6919 }, + {6761,6815,6848 ,6919,6946,6920 }, {6902,6901,6848 ,6947,6921,6920 }, + {6815,6902,6848 ,6946,6947,6920 }, {6952,6147,6901 ,6948,5452,6921 }, + {6902,6952,6901 ,6947,6948,6921 }, {6952,6990,5920 ,6948,6739,5453 }, + {6147,6952,5920 ,5452,6948,5453 }, {7001,7002,7025 ,6815,1047,6824 }, + {6652,6700,6699 ,6914,6907,630 }, {2234,2139,5454 ,6949,6895,6817 }, + {2241,2234,5454 ,6950,6949,6817 }, {5454,2139,6682 ,6817,6895,6812 }, + {5300,5327,6041 ,3793,3702,3801 }, {4207,1444,1745 ,4247,4035,6233 }, + {5498,1742,1218 ,5590,707,3255 }, {6616,6615,6575 ,6882,6924,6939 }, + {6844,6866,6843 ,6822,6786,6911 }, {1380,3284,6313 ,6721,6720,6748 }, + {5593,6896,6867 ,5935,6787,6951 }, {6896,6919,6895 ,6787,6778,5434 }, + {5024,2138,6660 ,6952,6602,6601 }, {5087,2095,1117 ,6796,6800,4928 }, + {6486,6487,6534 ,6941,6953,4257 }, {6717,6763,6762 ,6954,6955,6945 }, + {6716,6717,6762 ,6943,6954,6945 }, {6763,6816,6815 ,6955,6956,6946 }, + {6762,6763,6815 ,6945,6955,6946 }, {6903,6902,6815 ,6957,6947,6946 }, + {6816,6903,6815 ,6956,6957,6946 }, {3393,3365,3394 ,4441,4240,6958 }, + {5111,5110,7166 ,5990,5953,5971 }, {6805,6855,6854 ,3343,6959,3344 }, + {6391,4550,6659 ,6804,716,3577 }, {6642,6652,6651 ,6960,6914,6785 }, + {6958,6959,6997 ,4314,6003,4368 }, {6575,6615,6574 ,6939,6924,3885 }, + {6651,6652,6699 ,6785,6914,630 }, {6752,6751,6699 ,6906,5242,630 }, + {6641,6642,6651 ,6961,6960,6785 }, {2260,6021,5494 ,6571,6551,6569 }, + {7239,7286,7261 ,1527,3460,1528 }, {6466,6315,6465 ,6930,6962,6927 }, + {6315,6392,306 ,6962,6963,6234 }, {6528,6576,6527 ,6964,6883,6965 }, + {5086,351,2246 ,6064,592,591 }, {6313,4445,6747 ,6748,3648,6878 }, + {2336,6521,5496 ,1261,6819,6966 }, {6228,1664,253 ,1366,2268,2564 }, + {7050,7067,7028 ,6967,3585,3587 }, {6443,3441,3442 ,6940,2987,4460 }, + {6708,6710,5053 ,6148,6968,6848 }, {6443,6487,6486 ,6940,6953,6941 }, + {6565,6579,6578 ,6969,6970,6926 }, {6534,6565,6578 ,4257,6969,6926 }, + {6579,6092,6578 ,6970,1623,6926 }, {6691,6717,6716 ,6971,6954,6943 }, + {6176,6691,6716 ,1624,6971,6943 }, {6903,6953,6952 ,6957,6972,6948 }, + {6902,6903,6952 ,6947,6957,6948 }, {6991,6990,6952 ,6973,6739,6948 }, + {6953,6991,6952 ,6972,6973,6948 }, {6991,7032,5918 ,6973,6974,5820 }, + {6990,6991,5918 ,6739,6973,5820 }, {1275,6108,1221 ,1688,6975,1759 }, + {6642,6641,6614 ,6960,6961,3886 }, {6615,6642,6614 ,6924,6960,3886 }, + {6615,6614,6574 ,6924,3886,3885 }, {3311,5192,3762 ,5303,6454,5172 }, + {6999,6998,6959 ,6897,6855,6003 }, {6315,306,4960 ,6962,6234,6856 }, + {6909,6960,6908 ,6898,6896,6932 }, {6576,6575,6527 ,6883,6939,6965 }, + {5104,5086,2246 ,6063,6064,591 }, {3284,4445,6313 ,6720,3648,6748 }, + {5438,6705,5437 ,4156,4158,6259 }, {5251,5210,6433 ,3802,6033,6691 }, + {6444,6488,6487 ,6976,6977,6953 }, {6443,6444,6487 ,6940,6976,6953 }, + {6488,6535,6534 ,6977,6978,4257 }, {6487,6488,6534 ,6953,6977,4257 }, + {6534,6535,6565 ,4257,6978,6969 }, {6535,6580,6579 ,6978,6979,6970 }, + {6565,6535,6579 ,6969,6978,6970 }, {6622,6092,6579 ,6980,1623,6970 }, + {6580,6622,6579 ,6979,6980,6970 }, {6622,6662,6176 ,6980,6981,1624 }, + {6092,6622,6176 ,1623,6980,1624 }, {6176,6662,6691 ,1624,6981,6971 }, + {6662,6718,6717 ,6981,6982,6954 }, {6691,6662,6717 ,6971,6981,6954 }, + {6718,6764,6763 ,6982,6983,6955 }, {6717,6718,6763 ,6954,6982,6955 }, + {6817,6816,6763 ,6984,6956,6955 }, {6764,6817,6763 ,6983,6984,6955 }, + {6871,6903,6816 ,6985,6957,6956 }, {6817,6871,6816 ,6984,6985,6956 }, + {6871,6954,6953 ,6985,6986,6972 }, {6903,6871,6953 ,6957,6985,6972 }, + {6992,6991,6953 ,6987,6973,6972 }, {6954,6992,6953 ,6986,6987,6972 }, + {6992,5945,7032 ,6987,6396,6974 }, {6991,6992,7032 ,6973,6987,6974 }, + {5945,5918,7032 ,6396,5820,6974 }, {2808,1987,1920 ,522,3614,581 }, + {6315,5009,6465 ,6962,6803,6927 }, {3425,1724,8103 ,1859,3880,1291 }, + {6960,6999,6959 ,6896,6897,6003 }, {7974,7973,7923 ,4171,3624,816 }, + {3359,3326,6379 ,824,826,6831 }, {6071,6354,6377 ,1346,6597,1347 }, + {5633,6949,5611 ,6179,4585,6177 }, {405,3499,5628 ,5662,5661,6560 }, + {6661,6688,5048 ,6876,6846,6421 }, {7027,7007,7008 ,3175,4628,3176 }, + {2406,1990,6530 ,6736,1305,6398 }, {3442,6444,6443 ,4460,6976,6940 }, + {6706,7814,5479 ,5389,6988,5387 }, {7092,7104,7091 ,3968,5280,4153 }, + {1879,5049,4744 ,1283,5038,4424 }, {6856,6907,6906 ,6892,6002,4294 }, + {7041,7040,6998 ,6838,6903,6855 }, {1394,2074,1027 ,6989,6990,6991 }, + {6793,6792,6755 ,6879,5056,5055 }, {2304,2303,2340 ,6756,6758,6797 }, + {3609,6990,5918 ,5081,6739,5820 }, {6345,4595,5118 ,191,5289,6825 }, + {6444,6474,6488 ,6976,6992,6977 }, {6488,6514,6535 ,6977,6993,6978 }, + {6999,7041,6998 ,6897,6838,6855 }, {6960,6959,6907 ,6896,6003,6002 }, + {4900,2274,4545 ,585,893,586 }, {6614,6641,6651 ,3886,6961,6785 }, + {6793,6811,6792 ,6879,399,5056 }, {7566,7565,7532 ,6009,5080,6705 }, + {3394,6395,3442 ,6958,6994,4460 }, {3393,3394,3442 ,4441,6958,4460 }, + {6395,6445,6444 ,6994,6995,6976 }, {3442,6395,6444 ,4460,6994,6976 }, + {6445,6446,6474 ,6995,6996,6992 }, {6444,6445,6474 ,6976,6995,6992 }, + {6446,6489,6488 ,6996,6997,6977 }, {6474,6446,6488 ,6992,6996,6977 }, + {6489,6490,6514 ,6997,6998,6993 }, {6488,6489,6514 ,6977,6997,6993 }, + {6490,6536,6535 ,6998,6999,6978 }, {6514,6490,6535 ,6993,6998,6978 }, + {6536,6581,6580 ,6999,7000,6979 }, {6535,6536,6580 ,6978,6999,6979 }, + {6623,6622,6580 ,7001,6980,6979 }, {6581,6623,6580 ,7000,7001,6979 }, + {6623,6663,6662 ,7001,7002,6981 }, {6622,6623,6662 ,6980,7001,6981 }, + {6663,6719,6718 ,7002,7003,6982 }, {6662,6663,6718 ,6981,7002,6982 }, + {6719,6765,6764 ,7003,7004,6983 }, {6718,6719,6764 ,6982,7003,6983 }, + {6765,6766,6764 ,7004,7005,6983 }, {6766,6818,6817 ,7005,7006,6984 }, + {6764,6766,6817 ,6983,7005,6984 }, {6818,6849,6817 ,7006,7007,6984 }, + {6872,6871,6817 ,7008,6985,6984 }, {6849,6872,6817 ,7007,7008,6984 }, + {6872,6955,6954 ,7008,7009,6986 }, {6871,6872,6954 ,6985,7008,6986 }, + {6955,6993,6992 ,7009,7010,6987 }, {6954,6955,6992 ,6986,7009,6987 }, + {6993,7033,5945 ,7010,6395,6396 }, {6992,6993,5945 ,6987,7010,6396 }, + {3970,5124,3969 ,3225,6567,3226 }, {4845,5187,5070 ,3921,5144,6043 }, + {5878,3835,3762 ,2684,6046,5172 }, {4672,5878,3762 ,2682,2684,5172 }, + {6908,6960,6907 ,6932,6896,6002 }, {7041,7077,7076 ,6838,6837,4886 }, + {7048,2074,7047 ,6790,6990,6853 }, {2222,4746,2210 ,2509,5260,2510 }, + {6479,6528,6478 ,2659,6964,7011 }, {6963,7002,7001 ,1048,1047,6815 }, + {248,4530,3350 ,4114,1376,2541 }, {4489,4068,3778 ,5584,408,5585 }, + {6301,6710,6708 ,6147,6968,6148 }, {6811,6839,6792 ,399,6867,5056 }, + {5926,5940,6715 ,6419,1625,6929 }, {5081,2208,1117 ,1258,1257,4928 }, + {6014,5590,6259 ,6592,6386,6718 }, {3394,6396,6395 ,6958,7012,6994 }, + {6396,6446,6445 ,7012,6996,6995 }, {6395,6396,6445 ,6994,7012,6995 }, + {6663,6720,6719 ,7002,7013,7003 }, {6720,6766,6765 ,7013,7005,7004 }, + {6719,6720,6765 ,7003,7013,7004 }, {6873,6872,6849 ,7014,7008,7007 }, + {6818,6873,6849 ,7006,7014,7007 }, {6955,6922,6993 ,7009,7015,7010 }, + {6855,6856,6805 ,6959,6892,3343 }, {3291,3293,3292 ,1203,1988,4067 }, + {4704,4763,5217 ,4788,4787,4026 }, {1987,1220,1920 ,3614,4885,581 }, + {7040,7041,7076 ,6903,6838,4886 }, {6392,6315,6466 ,6963,6962,6930 }, + {6528,6527,6478 ,6964,6965,7011 }, {6388,6436,6435 ,3184,3681,2658 }, + {2138,3704,6190 ,6602,2332,6610 }, {5315,5442,5044 ,6445,5174,5173 }, + {3394,6385,6396 ,6958,7016,7012 }, {6446,6490,6489 ,6996,6998,6997 }, + {6663,6692,6720 ,7002,7017,7013 }, {6873,6922,6872 ,7014,7015,7008 }, + {6872,6922,6955 ,7008,7015,7009 }, {6994,6993,6922 ,7018,7010,7015 }, + {6994,7034,7033 ,7018,6045,6395 }, {6993,6994,7033 ,7010,7018,6395 }, + {4672,5477,6036 ,2682,5399,6141 }, {5744,5716,7009 ,6090,6075,5325 }, + {685,2629,794 ,2674,2673,3392 }, {2336,6619,6521 ,1261,1272,6819 }, + {6619,1265,4190 ,1272,1273,3439 }, {6908,6907,6856 ,6932,6002,6892 }, + {5005,2336,5496 ,1301,1261,6966 }, {6857,6908,6856 ,6899,6932,6892 }, + {3600,3550,3578 ,5331,4179,4205 }, {6951,2211,6870 ,2615,6780,2616 }, + {1264,403,5078 ,5022,5547,5261 }, {2409,2237,2373 ,6852,6801,6722 }, + {5075,1215,184 ,1586,3937,4052 }, {7068,6166,7034 ,7019,4971,6045 }, + {6324,6360,3394 ,4239,7020,6958 }, {3365,6324,3394 ,4240,4239,6958 }, + {6360,6361,6385 ,7020,7021,7016 }, {3394,6360,6385 ,6958,7020,7016 }, + {6361,6397,6396 ,7021,7022,7012 }, {6385,6361,6396 ,7016,7021,7012 }, + {6397,6447,6446 ,7022,7023,6996 }, {6396,6397,6446 ,7012,7022,6996 }, + {6447,6475,6446 ,7023,7024,6996 }, {6475,6491,6490 ,7024,7025,6998 }, + {6446,6475,6490 ,6996,7024,6998 }, {6491,6537,6536 ,7025,7026,6999 }, + {6490,6491,6536 ,6998,7025,6999 }, {6537,6582,6581 ,7026,7027,7000 }, + {6536,6537,6581 ,6999,7026,7000 }, {6624,6623,6581 ,7028,7001,7000 }, + {6582,6624,6581 ,7027,7028,7000 }, {6624,6664,6663 ,7028,7029,7002 }, + {6623,6624,6663 ,7001,7028,7002 }, {6663,6664,6692 ,7002,7029,7017 }, + {6664,6721,6720 ,7029,7030,7013 }, {6692,6664,6720 ,7017,7029,7013 }, + {6721,6767,6766 ,7030,7031,7005 }, {6720,6721,6766 ,7013,7030,7005 }, + {6767,6819,6818 ,7031,7032,7006 }, {6766,6767,6818 ,7005,7031,7006 }, + {6874,6873,6818 ,7033,7014,7006 }, {6819,6874,6818 ,7032,7033,7006 }, + {6874,6923,6922 ,7033,7034,7015 }, {6873,6874,6922 ,7014,7033,7015 }, + {6971,6994,6922 ,7035,7018,7015 }, {6923,6971,6922 ,7034,7035,7015 }, + {6971,7035,7034 ,7035,7036,6045 }, {6994,6971,7034 ,7018,7035,6045 }, + {3311,5866,5192 ,5303,5302,6454 }, {7035,7068,7034 ,7036,7019,6045 }, + {2357,2798,4043 ,3933,420,419 }, {8019,2320,8018 ,1407,1406,6676 }, + {1204,1205,4499 ,338,3187,4510 }, {5076,5904,4200 ,6607,5851,6583 }, + {6687,6685,2139 ,6875,6886,6895 }, {6027,6687,2139 ,6874,6875,6895 }, + {2374,2455,4018 ,7037,475,5084 }, {1551,4921,2093 ,5275,2079,553 }, + {3952,4068,4489 ,5597,408,5584 }, {6036,5892,4672 ,6141,2683,2682 }, + {535,6153,5105 ,2001,5514,5456 }, {2455,2374,1027 ,475,7037,6991 }, + {5274,5251,6433 ,6145,3802,6691 }, {6805,6856,6855 ,3343,6892,6959 }, + {6434,6479,6478 ,3127,2659,7011 }, {6433,6434,6478 ,6691,3127,7011 }, + {7161,7186,7169 ,2559,5370,5437 }, {912,2209,2240 ,4471,7038,4478 }, + {4445,6748,6747 ,3648,3408,6878 }, {5647,5688,6890 ,5333,5519,5334 }, + {7533,7509,7510 ,7039,1874,1873 }, {6836,5353,8111 ,852,851,4359 }, + {2211,2222,6870 ,6780,2509,2616 }, {4017,5075,184 ,5342,1586,4052 }, + {6324,6361,6360 ,4239,7021,7020 }, {2290,7936,2292 ,1982,5528,1983 }, + {5394,3765,5628 ,5660,4093,6560 }, {2074,1071,1027 ,6990,3382,6991 }, + {2248,2279,7043 ,6858,5337,5895 }, {6997,6998,7039 ,4368,6855,4376 }, + {6758,6465,973 ,6931,6927,2017 }, {2205,6563,6898 ,2874,464,1946 }, + {6690,6694,7022 ,6753,6925,6754 }, {6696,5054,3649 ,6849,6847,6755 }, + {6173,6071,6088 ,3711,1346,3670 }, {7972,8018,7994 ,6059,6676,5582 }, + {2211,1270,2222 ,6780,6781,2509 }, {1749,6713,1215 ,555,6820,3937 }, + {6566,6583,6582 ,7040,7041,7027 }, {6537,6566,6582 ,7026,7040,7027 }, + {6583,6624,6582 ,7041,7028,7027 }, {4550,4960,5986 ,716,6856,4603 }, + {5009,6315,6391 ,6803,6962,6804 }, {6315,4960,6391 ,6962,6856,6804 }, + {6391,4960,4550 ,6804,6856,716 }, {3217,6030,5747 ,6117,4037,6036 }, + {5006,4981,1026 ,1351,1350,6218 }, {5503,5463,5504 ,7042,5801,5781 }, + {6569,5011,4746 ,6745,6657,5260 }, {3936,3820,4835 ,5599,5591,2130 }, + {6148,6599,6161 ,6586,6596,6530 }, {5191,6434,6433 ,2900,3127,6691 }, + {6291,6297,6690 ,6873,6851,6753 }, {6694,6696,7022 ,6925,6849,6754 }, + {7795,7846,7794 ,3804,4162,5749 }, {5075,1749,1215 ,1586,555,3937 }, + {6116,6325,6324 ,4170,7043,4239 }, {3336,6116,6324 ,3922,4170,4239 }, + {6325,6362,6361 ,7043,7044,7021 }, {6324,6325,6361 ,4239,7043,7021 }, + {6362,6398,6397 ,7044,7045,7022 }, {6361,6362,6397 ,7021,7044,7022 }, + {6398,6448,6447 ,7045,7046,7023 }, {6397,6398,6447 ,7022,7045,7023 }, + {6447,6448,6475 ,7023,7046,7024 }, {6448,6492,6491 ,7046,7047,7025 }, + {6475,6448,6491 ,7024,7046,7025 }, {6492,6538,6537 ,7047,7048,7026 }, + {6491,6492,6537 ,7025,7047,7026 }, {6537,6538,6566 ,7026,7048,7040 }, + {6538,6584,6583 ,7048,7049,7041 }, {6566,6538,6583 ,7040,7048,7041 }, + {6625,6624,6583 ,7050,7028,7041 }, {6584,6625,6583 ,7049,7050,7041 }, + {6625,6665,6664 ,7050,7051,7029 }, {6624,6625,6664 ,7028,7050,7029 }, + {6665,6722,6721 ,7051,7052,7030 }, {6664,6665,6721 ,7029,7051,7030 }, + {6722,6768,6767 ,7052,7053,7031 }, {6721,6722,6767 ,7030,7052,7031 }, + {6768,6820,6819 ,7053,7054,7032 }, {6767,6768,6819 ,7031,7053,7032 }, + {6875,6874,6819 ,7055,7033,7032 }, {6820,6875,6819 ,7054,7055,7032 }, + {6875,6924,6923 ,7055,7056,7034 }, {6874,6875,6923 ,7033,7055,7034 }, + {6924,6972,6971 ,7056,7057,7035 }, {6923,6924,6971 ,7034,7056,7035 }, + {7036,7035,6971 ,7058,7036,7035 }, {6972,7036,6971 ,7057,7058,7035 }, + {7036,7069,7068 ,7058,7059,7019 }, {7035,7036,7068 ,7036,7058,7019 }, + {4901,3892,7068 ,3596,4970,7019 }, {7069,4901,7068 ,7059,3596,7019 }, + {6527,6575,6526 ,6965,6939,6784 }, {6575,6574,6526 ,6939,3885,6784 }, + {6478,6527,5300 ,7011,6965,3793 }, {6527,6526,5300 ,6965,6784,3793 }, + {6433,6478,5300 ,6691,7011,3793 }, {5274,6433,5300 ,6145,6691,3793 }, + {1269,1125,2159 ,5724,5723,6440 }, {187,1269,2159 ,6439,5724,6440 }, + {4391,4422,4443 ,992,5561,703 }, {6702,6701,6653 ,7060,6766,6915 }, + {6654,6702,6653 ,6884,7060,6915 }, {6297,6694,6690 ,6851,6925,6753 }, + {6465,4985,2141 ,6927,2703,2015 }, {3156,1658,1657 ,3164,1782,1781 }, + {2257,5056,6713 ,554,1300,6820 }, {949,4994,5029 ,2133,5076,2159 }, + {6788,6787,6751 ,6913,5269,5242 }, {6700,6752,6699 ,6907,6906,630 }, + {6806,6805,6787 ,6905,3343,5269 }, {6752,6788,6751 ,6906,6913,5242 }, + {6788,6806,6787 ,6913,6905,5269 }, {6857,6855,6805 ,6899,6959,3343 }, + {6857,6856,6855 ,6899,6892,6959 }, {6806,6857,6805 ,6905,6899,3343 }, + {3454,5089,7052 ,710,3442,708 }, {3926,3891,6899 ,3125,3820,4005 }, + {4978,4567,5592 ,178,6655,3671 }, {2236,2237,6291 ,6802,6801,6873 }, + {5590,6071,6173 ,6386,1346,3711 }, {6918,6917,6895 ,6779,5515,5434 }, + {1749,2257,6713 ,555,554,6820 }, {4979,3319,5056 ,5363,1259,1300 }, + {6492,6515,6538 ,7047,7061,7048 }, {6768,6821,6820 ,7053,7062,7054 }, + {6821,6876,6875 ,7062,7063,7055 }, {6820,6821,6875 ,7054,7062,7055 }, + {6876,6925,6924 ,7063,7064,7056 }, {6875,6876,6924 ,7055,7063,7056 }, + {6973,6972,6924 ,7065,7057,7056 }, {6925,6973,6924 ,7064,7065,7056 }, + {6973,7036,6972 ,7065,7058,7057 }, {7030,4010,5533 ,6885,4343,6239 }, + {1881,2234,2241 ,7066,6949,6950 }, {2209,1881,2241 ,7038,7066,6950 }, + {1881,2209,1076 ,7066,7038,4887 }, {4142,1881,1076 ,6839,7066,4887 }, + {7077,4142,1076 ,6837,6839,4887 }, {5009,6463,4985 ,6803,5159,2703 }, + {7076,7077,1076 ,4886,6837,4887 }, {5926,6715,3814 ,6419,6929,5511 }, + {6108,1172,1221 ,6975,1758,1759 }, {7436,7412,7386 ,4245,3765,5512 }, + {3441,6443,3505 ,2987,6940,6928 }, {5370,5474,2160 ,2171,4243,6621 }, + {2257,4979,5056 ,554,5363,1300 }, {6286,6326,6325 ,4349,7067,7043 }, + {6116,6286,6325 ,4170,4349,7043 }, {6326,6363,6362 ,7067,7068,7044 }, + {6325,6326,6362 ,7043,7067,7044 }, {6363,6399,6398 ,7068,7069,7045 }, + {6362,6363,6398 ,7044,7068,7045 }, {6399,6449,6448 ,7069,7070,7046 }, + {6398,6399,6448 ,7045,7069,7046 }, {6449,6493,6492 ,7070,7071,7047 }, + {6448,6449,6492 ,7046,7070,7047 }, {6493,6516,6515 ,7071,7072,7061 }, + {6492,6493,6515 ,7047,7071,7061 }, {6516,6539,6538 ,7072,7073,7048 }, + {6515,6516,6538 ,7061,7072,7048 }, {6539,6585,6584 ,7073,7074,7049 }, + {6538,6539,6584 ,7048,7073,7049 }, {6626,6625,6584 ,7075,7050,7049 }, + {6585,6626,6584 ,7074,7075,7049 }, {6626,6666,6665 ,7075,7076,7051 }, + {6625,6626,6665 ,7050,7075,7051 }, {6666,6723,6722 ,7076,7077,7052 }, + {6665,6666,6722 ,7051,7076,7052 }, {6769,6768,6722 ,7078,7053,7052 }, + {6723,6769,6722 ,7077,7078,7052 }, {6769,6822,6821 ,7078,7079,7062 }, + {6768,6769,6821 ,7053,7078,7062 }, {6822,6877,6876 ,7079,7080,7063 }, + {6821,6822,6876 ,7062,7079,7063 }, {6877,6926,6925 ,7080,7081,7064 }, + {6876,6877,6925 ,7063,7080,7064 }, {6974,6973,6925 ,7082,7065,7064 }, + {6926,6974,6925 ,7081,7082,7064 }, {6974,7010,6973 ,7082,7083,7065 }, + {7010,7036,6973 ,7083,7058,7065 }, {7010,7070,7069 ,7083,7084,7059 }, + {7036,7010,7069 ,7058,7083,7059 }, {3813,5926,3814 ,1515,6419,5511 }, + {7972,7994,7948 ,6059,5582,3625 }, {4082,1426,3452 ,4716,4648,1794 }, + {2235,2234,1881 ,6870,6949,7066 }, {307,4989,6463 ,5095,5094,5159 }, + {2209,912,4617 ,7038,4471,4431 }, {7044,2248,7043 ,6859,6858,5895 }, + {2248,1075,1313 ,6858,6854,3674 }, {186,187,1171 ,1265,6439,6441 }, + {3133,1269,187 ,7085,5724,6439 }, {186,3133,187 ,1265,7085,6439 }, + {5890,4648,1269 ,6591,5722,5724 }, {3133,5890,1269 ,7085,6591,5724 }, + {1126,6037,1272 ,7086,5235,5740 }, {2237,6297,6291 ,6801,6851,6873 }, + {6748,1990,2406 ,3408,1305,6736 }, {6747,6748,2406 ,6878,3408,6736 }, + {4684,2245,3319 ,5217,1260,1259 }, {4979,4684,3319 ,5363,5217,1259 }, + {6305,6327,6326 ,7087,7088,7067 }, {6286,6305,6326 ,4349,7087,7067 }, + {6326,6327,6363 ,7067,7088,7068 }, {6769,6770,6822 ,7078,7089,7079 }, + {4901,7069,7070 ,3596,7059,7084 }, {4900,6378,2274 ,585,6728,893 }, + {6689,2405,2337 ,3065,4165,6829 }, {6378,1882,2274 ,6728,894,893 }, + {3556,3504,3557 ,3385,2986,6070 }, {4570,5072,6261 ,4082,6619,4321 }, + {5444,4544,5040 ,3410,748,5469 }, {7533,7568,7509 ,7039,5544,1874 }, + {6666,6724,6723 ,7076,7090,7077 }, {6724,6770,6769 ,7090,7089,7078 }, + {6723,6724,6769 ,7077,7090,7078 }, {6787,6805,6804 ,5269,3343,3342 }, + {978,977,4965 ,1620,3931,784 }, {6615,6652,6642 ,6924,6914,6960 }, + {3786,3400,1451 ,4074,3659,175 }, {6005,186,1171 ,5691,1265,6441 }, + {1026,733,5006 ,6218,4051,1351 }, {6507,6529,6479 ,2680,5025,2659 }, + {4988,5494,5020 ,6744,6569,6373 }, {8109,8127,8119 ,2938,2970,184 }, + {2246,2452,7066 ,591,7091,3586 }, {4684,6603,6359 ,5217,3985,6830 }, + {2245,4684,6359 ,1260,5217,6830 }, {6286,6287,6305 ,4349,4377,7087 }, + {6287,6328,6327 ,4377,7092,7088 }, {6305,6287,6327 ,7087,4377,7088 }, + {6328,6364,6363 ,7092,7093,7068 }, {6327,6328,6363 ,7088,7092,7068 }, + {6364,6400,6399 ,7093,7094,7069 }, {6363,6364,6399 ,7068,7093,7069 }, + {6400,6424,6399 ,7094,7095,7069 }, {6424,6450,6449 ,7095,7096,7070 }, + {6399,6424,6449 ,7069,7095,7070 }, {6450,6494,6493 ,7096,7097,7071 }, + {6449,6450,6493 ,7070,7096,7071 }, {6495,6516,6493 ,7098,7072,7071 }, + {6494,6495,6493 ,7097,7098,7071 }, {6540,6539,6516 ,7099,7073,7072 }, + {6495,6540,6516 ,7098,7099,7072 }, {6586,6585,6539 ,7100,7074,7073 }, + {6540,6586,6539 ,7099,7100,7073 }, {6586,6627,6626 ,7100,7101,7075 }, + {6585,6586,6626 ,7074,7100,7075 }, {6627,6667,6666 ,7101,7102,7076 }, + {6626,6627,6666 ,7075,7101,7076 }, {6667,6668,6666 ,7102,7103,7076 }, + {6668,6725,6724 ,7103,7104,7090 }, {6666,6668,6724 ,7076,7103,7090 }, + {6725,6771,6770 ,7104,7105,7089 }, {6724,6725,6770 ,7090,7104,7089 }, + {6771,6823,6822 ,7105,7106,7079 }, {6770,6771,6822 ,7089,7105,7079 }, + {6823,6878,6877 ,7106,7107,7080 }, {6822,6823,6877 ,7079,7106,7080 }, + {6927,6926,6877 ,7108,7081,7080 }, {6878,6927,6877 ,7107,7108,7080 }, + {6975,6974,6926 ,7109,7082,7081 }, {6927,6975,6926 ,7108,7109,7081 }, + {6975,7011,7010 ,7109,7110,7083 }, {6974,6975,7010 ,7082,7109,7083 }, + {7011,7071,7070 ,7110,7111,7084 }, {7010,7011,7070 ,7083,7110,7084 }, + {7071,1261,7070 ,7111,6472,7084 }, {1261,5591,7070 ,6472,3597,7084 }, + {4986,6659,3399 ,5096,3577,715 }, {6854,6855,6893 ,3344,6959,6844 }, + {1076,2209,4617 ,4887,7038,4431 }, {4746,5078,6302 ,5260,5261,6869 }, + {2291,2221,3133 ,1115,6902,7085 }, {186,2291,3133 ,1265,1115,7085 }, + {2221,6214,5890 ,6902,6942,6591 }, {3133,2221,5890 ,7085,6902,6591 }, + {4950,4115,1216 ,1302,6909,6893 }, {5072,6220,6231 ,6619,6600,6624 }, + {2260,5494,4988 ,6571,6569,6744 }, {2211,2260,4988 ,6780,6571,6744 }, + {5444,5040,5024 ,3410,5469,6952 }, {2157,5444,5024 ,3411,3410,6952 }, + {5591,4901,7070 ,3597,3596,7084 }, {6627,6668,6667 ,7101,7103,7102 }, + {5330,5331,5350 ,3345,6198,1526 }, {5611,6920,5593 ,6177,6783,5935 }, + {6234,6259,6167 ,5973,6718,5974 }, {2717,819,728 ,2676,2244,1266 }, + {2025,2000,2027 ,2430,2428,2492 }, {2342,2221,5114 ,5690,6902,5689 }, + {5435,5401,6611 ,4223,4145,1391 }, {6529,6576,6528 ,5025,6883,6964 }, + {6870,2222,2205 ,2616,2509,2874 }, {6603,1116,5074 ,3985,1401,6751 }, + {6359,6603,5074 ,6830,3985,6751 }, {6476,6495,6494 ,7112,7098,7097 }, + {6450,6476,6494 ,7096,7112,7097 }, {6586,6628,6627 ,7100,7113,7101 }, + {6627,6628,6668 ,7101,7113,7103 }, {6163,3860,6029 ,1453,6523,1454 }, + {5059,7081,2220 ,1253,7114,6724 }, {1171,187,3498 ,6441,6439,5800 }, + {7022,6696,3649 ,6754,6849,6755 }, {2260,2211,6951 ,6571,6780,2615 }, + {2157,5024,2244 ,3411,6952,6795 }, {6278,6288,6287 ,3967,7115,4377 }, + {6277,6278,6287 ,4387,3967,4377 }, {6288,6329,6328 ,7115,7116,7092 }, + {6287,6288,6328 ,4377,7115,7092 }, {6329,6365,6364 ,7116,7117,7093 }, + {6328,6329,6364 ,7092,7116,7093 }, {6365,6401,6400 ,7117,7118,7094 }, + {6364,6365,6400 ,7093,7117,7094 }, {6401,6425,6424 ,7118,7119,7095 }, + {6400,6401,6424 ,7094,7118,7095 }, {6425,6451,6450 ,7119,7120,7096 }, + {6424,6425,6450 ,7095,7119,7096 }, {6450,6451,6476 ,7096,7120,7112 }, + {6451,6496,6495 ,7120,7121,7098 }, {6476,6451,6495 ,7112,7120,7098 }, + {6496,6541,6540 ,7121,7122,7099 }, {6495,6496,6540 ,7098,7121,7099 }, + {6541,6587,6586 ,7122,7123,7100 }, {6540,6541,6586 ,7099,7122,7100 }, + {6587,6607,6586 ,7123,7124,7100 }, {6607,6629,6628 ,7124,7125,7113 }, + {6586,6607,6628 ,7100,7124,7113 }, {6629,6669,6668 ,7125,7126,7103 }, + {6628,6629,6668 ,7113,7125,7103 }, {6669,6726,6725 ,7126,7127,7104 }, + {6668,6669,6725 ,7103,7126,7104 }, {6772,6771,6725 ,7128,7105,7104 }, + {6726,6772,6725 ,7127,7128,7104 }, {6772,6824,6823 ,7128,7129,7106 }, + {6771,6772,6823 ,7105,7128,7106 }, {6824,6879,6878 ,7129,7130,7107 }, + {6823,6824,6878 ,7106,7129,7107 }, {6879,6880,6878 ,7130,7131,7107 }, + {6880,6928,6927 ,7131,7132,7108 }, {6878,6880,6927 ,7107,7131,7108 }, + {6976,6975,6927 ,7133,7109,7108 }, {6928,6976,6927 ,7132,7133,7108 }, + {6976,7012,7011 ,7133,7134,7110 }, {6975,6976,7011 ,7109,7133,7110 }, + {7053,7071,7011 ,6654,7111,7110 }, {7012,7053,7011 ,7134,6654,7110 }, + {1723,1261,7071 ,6626,6472,7111 }, {7053,1723,7071 ,6654,6626,7111 }, + {8127,3221,8119 ,2970,183,184 }, {6792,6839,6810 ,5056,6867,6908 }, + {7533,7595,7568 ,7039,4019,5544 }, {3979,5055,5753 ,6471,6470,6481 }, + {5008,3217,6214 ,6118,6117,6942 }, {2221,5008,6214 ,6902,6118,6942 }, + {6214,3217,5747 ,6942,6117,6036 }, {6654,6653,6616 ,6884,6915,6882 }, + {6529,6528,6479 ,5025,6964,2659 }, {8038,3638,8052 ,4121,5950,4122 }, + {6049,2260,6951 ,6570,6571,2615 }, {6606,2157,2244 ,5085,3411,6795 }, + {4990,1124,6065 ,7135,5852,6616 }, {1116,2239,185 ,1401,1377,1493 }, + {5074,1116,185 ,6751,1401,1493 }, {6824,6880,6879 ,7129,7131,7130 }, + {6880,6929,6928 ,7131,7136,7132 }, {6929,6976,6928 ,7136,7133,7132 }, + {6977,7013,7012 ,7137,7138,7134 }, {6976,6977,7012 ,7133,7137,7134 }, + {7013,7053,7012 ,7138,6654,7134 }, {7570,7625,7598 ,5204,7139,7140 }, + {6910,6941,6961 ,6741,6793,6813 }, {7000,7024,7042 ,6814,6816,5338 }, + {5328,2160,2238 ,2172,6621,2697 }, {5045,7061,6165 ,4214,7141,4219 }, + {4649,5125,5118 ,5288,5467,6825 }, {1214,6049,6951 ,6573,6570,2615 }, + {6606,2244,4856 ,5085,6795,4081 }, {3040,8072,8076 ,619,511,617 }, + {6824,6850,6880 ,7129,7142,7131 }, {6929,6977,6976 ,7136,7137,7133 }, + {7054,7053,7013 ,7143,6654,7138 }, {7081,4233,2220 ,7114,6185,6724 }, + {5124,4010,3969 ,6567,4343,3226 }, {7454,7511,7453 ,3996,6677,1187 }, + {5057,1214,6951 ,1795,6573,2615 }, {2454,6606,4856 ,1345,5085,4081 }, + {4209,3777,185 ,6269,1582,1493 }, {2239,4209,185 ,1377,6269,1493 }, + {6279,6289,6288 ,3966,7144,7115 }, {6278,6279,6288 ,3967,3966,7115 }, + {6289,6330,6329 ,7144,7145,7116 }, {6288,6289,6329 ,7115,7144,7116 }, + {6366,6365,6329 ,7146,7117,7116 }, {6330,6366,6329 ,7145,7146,7116 }, + {6366,6402,6401 ,7146,7147,7118 }, {6365,6366,6401 ,7117,7146,7118 }, + {6402,6403,6425 ,7147,7148,7119 }, {6401,6402,6425 ,7118,7147,7119 }, + {6403,6452,6451 ,7148,7149,7120 }, {6425,6403,6451 ,7119,7148,7120 }, + {6452,6497,6496 ,7149,7150,7121 }, {6451,6452,6496 ,7120,7149,7121 }, + {6497,6542,6541 ,7150,7151,7122 }, {6496,6497,6541 ,7121,7150,7122 }, + {6542,6588,6587 ,7151,7152,7123 }, {6541,6542,6587 ,7122,7151,7123 }, + {6588,6589,6607 ,7152,7153,7124 }, {6587,6588,6607 ,7123,7152,7124 }, + {6589,6630,6629 ,7153,7154,7125 }, {6607,6589,6629 ,7124,7153,7125 }, + {6630,6670,6669 ,7154,7155,7126 }, {6629,6630,6669 ,7125,7154,7126 }, + {6727,6726,6669 ,7156,7127,7126 }, {6670,6727,6669 ,7155,7156,7126 }, + {6773,6772,6726 ,7157,7128,7127 }, {6727,6773,6726 ,7156,7157,7127 }, + {6773,6825,6824 ,7157,7158,7129 }, {6772,6773,6824 ,7128,7157,7129 }, + {6825,6826,6850 ,7158,7159,7142 }, {6824,6825,6850 ,7129,7158,7142 }, + {6826,6881,6880 ,7159,7160,7131 }, {6850,6826,6880 ,7142,7159,7131 }, + {6881,6930,6929 ,7160,7161,7136 }, {6880,6881,6929 ,7131,7160,7136 }, + {6930,6978,6977 ,7161,7162,7137 }, {6929,6930,6977 ,7136,7161,7137 }, + {6978,7014,7013 ,7162,7163,7138 }, {6977,6978,7013 ,7137,7162,7138 }, + {7055,7054,7013 ,5485,7143,7138 }, {7014,7055,7013 ,7163,5485,7138 }, + {7055,7053,7054 ,5485,6654,7143 }, {7055,7079,7053 ,5485,5486,6654 }, + {6618,6617,5404 ,6910,6881,3559 }, {2273,2342,350 ,5322,5690,1114 }, + {1273,2273,350 ,6403,5322,1114 }, {5254,6030,5576 ,4042,4037,4039 }, + {1214,5057,6122 ,6573,1795,1797 }, {6101,1214,6122 ,6574,6573,1797 }, + {2272,2454,6386 ,1201,1345,4085 }, {4853,1118,2340 ,6747,6746,6797 }, + {875,2252,6836 ,579,590,852 }, {7053,7079,6150 ,6654,5486,6653 }, + {6366,6403,6402 ,7146,7148,7147 }, {6542,6589,6588 ,7151,7153,7152 }, + {1219,6150,7079 ,4102,6653,5486 }, {6916,6967,6915 ,596,4629,6871 }, + {6841,6840,6812 ,7164,6868,6872 }, {6967,6987,6966 ,4629,6917,6840 }, + {6794,6841,6812 ,6880,7164,6872 }, {6864,6894,6840 ,6865,6887,6868 }, + {6651,6699,6698 ,6785,630,629 }, {6967,6966,6915 ,4629,6840,6871 }, + {5944,1273,5653 ,4754,6403,3644 }, {4465,4483,4444 ,257,549,582 }, + {6392,3929,306 ,6963,5024,6234 }, {3302,6301,6300 ,6757,6147,6146 }, + {4755,2272,1164 ,6770,1201,5087 }, {2454,4856,6386 ,1345,4081,4085 }, + {6682,6679,3978 ,6812,4824,4549 }, {2252,144,6836 ,590,853,852 }, + {6542,6567,6589 ,7151,7165,7153 }, {6773,6826,6825 ,7157,7159,7158 }, + {5838,6355,6079 ,3987,3786,5580 }, {6650,6651,6698 ,3833,6785,629 }, + {7450,7478,7422 ,399,1402,1404 }, {5944,1126,1273 ,4754,7086,6403 }, + {1074,973,8073 ,2715,2017,2567 }, {3929,4166,306 ,5024,6084,6234 }, + {3675,3674,6180 ,4300,4286,6811 }, {2272,6386,1164 ,1201,4085,5087 }, + {6961,6962,7000 ,6813,3260,6814 }, {1220,1271,875 ,4885,588,579 }, + {6280,6290,6289 ,4079,7166,7144 }, {6279,6280,6289 ,3966,4079,7144 }, + {6290,6331,6330 ,7166,7167,7145 }, {6289,6290,6330 ,7144,7166,7145 }, + {6331,6367,6366 ,7167,7168,7146 }, {6330,6331,6366 ,7145,7167,7146 }, + {6367,6404,6403 ,7168,7169,7148 }, {6366,6367,6403 ,7146,7168,7148 }, + {6404,6426,6403 ,7169,7170,7148 }, {6426,6453,6452 ,7170,7171,7149 }, + {6403,6426,6452 ,7148,7170,7149 }, {6498,6497,6452 ,7172,7150,7149 }, + {6453,6498,6452 ,7171,7172,7149 }, {6498,6543,6542 ,7172,7173,7151 }, + {6497,6498,6542 ,7150,7172,7151 }, {6543,6544,6567 ,7173,7174,7165 }, + {6542,6543,6567 ,7151,7173,7165 }, {6544,6590,6589 ,7174,7175,7153 }, + {6567,6544,6589 ,7165,7174,7153 }, {6590,6631,6630 ,7175,7176,7154 }, + {6589,6590,6630 ,7153,7175,7154 }, {6631,6671,6670 ,7176,7177,7155 }, + {6630,6631,6670 ,7154,7176,7155 }, {6728,6727,6670 ,7178,7156,7155 }, + {6671,6728,6670 ,7177,7178,7155 }, {6728,6774,6773 ,7178,7179,7157 }, + {6727,6728,6773 ,7156,7178,7157 }, {6774,6775,6773 ,7179,7180,7157 }, + {6775,6827,6826 ,7180,7181,7159 }, {6773,6775,6826 ,7157,7180,7159 }, + {6827,6882,6881 ,7181,7182,7160 }, {6826,6827,6881 ,7159,7181,7160 }, + {6882,6931,6930 ,7182,7183,7161 }, {6881,6882,6930 ,7160,7182,7161 }, + {6931,6979,6978 ,7183,7184,7162 }, {6930,6931,6978 ,7161,7183,7162 }, + {6979,7015,7014 ,7184,7185,7163 }, {6978,6979,7014 ,7162,7184,7163 }, + {7056,7055,7014 ,7186,5485,7163 }, {7015,7056,7014 ,7185,7186,7163 }, + {1170,1219,7055 ,4103,4102,5485 }, {7056,1170,7055 ,7186,4103,5485 }, + {5499,5704,2621 ,5572,5579,5416 }, {6660,6220,5072 ,6601,6600,6619 }, + {4570,6660,5072 ,4082,6601,6619 }, {1121,5872,1122 ,6564,6557,6719 }, + {6786,6787,6804 ,4139,5269,3342 }, {3834,4570,4044 ,4083,4082,4322 }, + {5426,4807,5010 ,5634,6449,6006 }, {4803,5290,5267 ,4776,5247,4647 }, + {2154,2273,1273 ,5321,5322,6403 }, {1126,2154,1273 ,7086,5321,6403 }, + {5516,5551,6795 ,5420,6260,5418 }, {403,3929,6466 ,5547,5024,6930 }, + {5054,6688,6661 ,6847,6846,6876 }, {2015,2048,1994 ,759,5461,5102 }, + {7001,7025,7024 ,6815,6824,6816 }, {7048,7064,2074 ,6790,5181,6990 }, + {1271,2252,875 ,588,590,579 }, {7064,2073,2074 ,5181,5183,6990 }, + {6498,6544,6543 ,7172,7174,7173 }, {6728,6775,6774 ,7178,7180,7179 }, + {6660,6219,6220 ,6601,6598,6600 }, {4486,3340,3454 ,819,114,710 }, + {6526,6574,6573 ,6784,3885,3733 }, {5738,5688,5689 ,5520,5519,5741 }, + {307,6391,6659 ,5095,6804,3577 }, {3929,6392,6466 ,5024,6963,6930 }, + {5113,5914,5150 ,141,1566,6032 }, {6435,6436,6480 ,2658,3681,1524 }, + {3649,5054,6661 ,6755,6847,6876 }, {7067,2246,7066 ,3585,591,3586 }, + {1197,2384,1987 ,478,6821,3614 }, {4190,1271,1220 ,3439,588,4885 }, + {7007,7006,6966 ,4628,6841,6840 }, {6631,6672,6671 ,7176,7187,7177 }, + {6672,6728,6671 ,7187,7178,7177 }, {6807,6808,6858 ,6764,3465,6740 }, + {6703,6701,6702 ,5161,6766,7060 }, {2337,5578,1124 ,6829,587,5852 }, + {4990,2337,1124 ,7135,6829,5852 }, {1025,2450,6657 ,5299,3444,6828 }, + {6689,2337,4990 ,3065,6829,7135 }, {1608,746,5592 ,4128,2661,3671 }, + {5277,5330,5276 ,6237,3345,1525 }, {7068,3892,6166 ,7019,4970,4971 }, + {6034,6284,5952 ,6049,6113,6595 }, {403,6466,1074 ,5547,6930,2715 }, + {5078,403,1074 ,5261,5547,2715 }, {6807,6858,6838 ,6764,6740,6904 }, + {158,4755,5560 ,5258,6770,6605 }, {5076,158,5560 ,6607,5258,6605 }, + {1213,3261,1197 ,3700,5015,478 }, {1987,4190,1220 ,3614,3439,4885 }, + {1620,6128,6184 ,5982,5981,6089 }, {6281,3526,6290 ,4078,7188,7166 }, + {6280,6281,6290 ,4079,4078,7166 }, {3526,6332,6331 ,7188,7189,7167 }, + {6290,3526,6331 ,7166,7188,7167 }, {6368,6367,6331 ,7190,7168,7167 }, + {6332,6368,6331 ,7189,7190,7167 }, {6368,6405,6404 ,7190,7191,7169 }, + {6367,6368,6404 ,7168,7190,7169 }, {6405,6427,6426 ,7191,7192,7170 }, + {6404,6405,6426 ,7169,7191,7170 }, {6427,6454,6453 ,7192,7193,7171 }, + {6426,6427,6453 ,7170,7192,7171 }, {6454,6499,6498 ,7193,7194,7172 }, + {6453,6454,6498 ,7171,7193,7172 }, {6499,6517,6498 ,7194,7195,7172 }, + {6517,6545,6544 ,7195,7196,7174 }, {6498,6517,6544 ,7172,7195,7174 }, + {6545,6591,6590 ,7196,7197,7175 }, {6544,6545,6590 ,7174,7196,7175 }, + {6632,6631,6590 ,7198,7176,7175 }, {6591,6632,6590 ,7197,7198,7175 }, + {6632,6673,6672 ,7198,7199,7187 }, {6631,6632,6672 ,7176,7198,7187 }, + {6729,6728,6672 ,7200,7178,7187 }, {6673,6729,6672 ,7199,7200,7187 }, + {6729,6776,6775 ,7200,7201,7180 }, {6728,6729,6775 ,7178,7200,7180 }, + {6776,6828,6827 ,7201,7202,7181 }, {6775,6776,6827 ,7180,7201,7181 }, + {6828,6883,6882 ,7202,7203,7182 }, {6827,6828,6882 ,7181,7202,7182 }, + {6883,6932,6931 ,7203,7204,7183 }, {6882,6883,6931 ,7182,7203,7183 }, + {6932,6980,6979 ,7204,7205,7184 }, {6931,6932,6979 ,7183,7204,7184 }, + {6980,7016,7015 ,7205,7206,7185 }, {6979,6980,7015 ,7184,7205,7185 }, + {7057,7056,7015 ,7207,7186,7185 }, {7016,7057,7015 ,7206,7207,7185 }, + {1122,1170,7056 ,6719,4103,7186 }, {7057,1122,7056 ,7207,6719,7186 }, + {1170,1122,1620 ,4103,6719,5982 }, {6655,6703,6654 ,5407,5161,6884 }, + {6430,5733,5353 ,2748,1584,851 }, {4023,5248,4178 ,3827,6469,3825 }, + {6319,6345,5118 ,6789,191,6825 }, {5734,5733,4261 ,6230,1584,1583 }, + {2450,6689,6657 ,3444,3065,6828 }, {2621,3815,5359 ,5416,5556,3713 }, + {7196,7231,7212 ,2401,6944,7208 }, {2160,2154,1126 ,6621,5321,7086 }, + {1272,2160,1126 ,5740,6621,7086 }, {7024,7025,7042 ,6816,6824,5338 }, + {4755,1164,5560 ,6770,5087,6605 }, {2384,4190,1987 ,6821,3439,3614 }, + {7998,7997,7950 ,7209,7210,7211 }, {5514,6158,6293 ,6128,6112,7212 }, + {6703,6702,6654 ,5161,7060,6884 }, {6307,6301,3302 ,5206,6147,6757 }, + {4018,6307,3302 ,5084,5206,6757 }, {6657,6689,4990 ,6828,3065,7135 }, + {6521,3261,6639 ,6819,5015,3699 }, {5496,6521,6564 ,6966,6819,6933 }, + {6521,6639,6508 ,6819,3699,6934 }, {6741,6794,6756 ,5388,6880,6891 }, + {6896,6895,6842 ,6787,5434,5433 }, {5005,5496,4115 ,1301,6966,6909 }, + {6792,6810,6791 ,5056,6908,6900 }, {1075,2409,2373 ,6854,6852,6722 }, + {5057,6951,6796 ,1795,2615,2617 }, {6345,6319,3533 ,191,6789,192 }, + {6521,6508,6564 ,6819,6934,6933 }, {3261,2384,1197 ,5015,6821,478 }, + {5369,1124,5904 ,6609,5852,5851 }, {5566,5605,5604 ,5759,5761,7213 }, + {5140,2621,5359 ,3712,5416,3713 }, {3505,6443,3504 ,6928,6940,2986 }, + {5496,6564,3320 ,6966,6933,5455 }, {5514,5943,7088 ,6128,5402,4299 }, + {7065,7064,7049 ,5182,5181,5454 }, {3261,1213,6639 ,5015,3700,3699 }, + {5496,3320,5058 ,6966,5455,5647 }, {6867,6844,5551 ,6951,6822,6260 }, + {4926,920,3367 ,3897,57,5798 }, {5014,2450,1025 ,6818,3444,5299 }, + {2238,2160,1272 ,2697,6621,5740 }, {6906,6907,6958 ,4294,6002,4314 }, + {876,4232,4208 ,5340,6299,6356 }, {6711,6710,6301 ,3473,6968,6147 }, + {1216,4115,4547 ,6893,6909,242 }, {7387,3303,3322 ,3615,1049,1182 }, + {5552,5593,6867 ,5936,5935,6951 }, {1124,5369,6065 ,5852,6609,6616 }, + {3526,6281,6060 ,7188,4078,5914 }, {6060,6333,6332 ,5914,7214,7189 }, + {3526,6060,6332 ,7188,5914,7189 }, {6369,6368,6332 ,7215,7190,7189 }, + {6333,6369,6332 ,7214,7215,7189 }, {6406,6405,6368 ,7216,7191,7190 }, + {6369,6406,6368 ,7215,7216,7190 }, {6428,6427,6405 ,7217,7192,7191 }, + {6406,6428,6405 ,7216,7217,7191 }, {6455,6454,6427 ,7218,7193,7192 }, + {6428,6455,6427 ,7217,7218,7192 }, {6455,6500,6499 ,7218,7219,7194 }, + {6454,6455,6499 ,7193,7218,7194 }, {6500,6518,6517 ,7219,7220,7195 }, + {6499,6500,6517 ,7194,7219,7195 }, {6518,6546,6545 ,7220,7221,7196 }, + {6517,6518,6545 ,7195,7220,7196 }, {6546,6592,6591 ,7221,7222,7197 }, + {6545,6546,6591 ,7196,7221,7197 }, {6592,6633,6632 ,7222,7223,7198 }, + {6591,6592,6632 ,7197,7222,7198 }, {6633,6674,6673 ,7223,7224,7199 }, + {6632,6633,6673 ,7198,7223,7199 }, {6730,6729,6673 ,7225,7200,7199 }, + {6674,6730,6673 ,7224,7225,7199 }, {6730,6777,6776 ,7225,7226,7201 }, + {6729,6730,6776 ,7200,7225,7201 }, {6777,6829,6828 ,7226,7227,7202 }, + {6776,6777,6828 ,7201,7226,7202 }, {6829,6884,6883 ,7227,7228,7203 }, + {6828,6829,6883 ,7202,7227,7203 }, {6884,6933,6932 ,7228,7229,7204 }, + {6883,6884,6932 ,7203,7228,7204 }, {6933,6981,6980 ,7229,7230,7205 }, + {6932,6933,6980 ,7204,7229,7205 }, {6981,6126,7016 ,7230,7231,7206 }, + {6980,6981,7016 ,7205,7230,7206 }, {6126,6107,7016 ,7231,7232,7206 }, + {7016,6107,7057 ,7206,7232,7207 }, {5693,5714,7028 ,4075,4077,3587 }, + {1122,7057,5936 ,6719,7207,4393 }, {4554,1989,6323 ,1307,3016,1451 }, + {3860,5246,6028 ,6523,5176,5316 }, {4115,5058,4547 ,6909,5647,242 }, + {6319,6318,6304 ,6789,6788,7233 }, {1121,1122,5936 ,6564,6719,4393 }, + {4115,5496,5058 ,6909,6966,5647 }, {1882,1072,4547 ,894,6725,242 }, + {6711,5014,1025 ,3473,6818,5299 }, {6178,3892,4901 ,5881,4970,3596 }, + {6867,5551,5552 ,6951,6260,5936 }, {7370,7396,7343 ,3750,6716,2750 }, + {6307,6711,6301 ,5206,3473,6147 }, {2343,8067,8097 ,2685,2168,1710 }, + {4784,4542,3285 ,3423,6727,3064 }, {4542,6481,4900 ,6727,6729,585 }, + {7197,5112,5111 ,7234,6021,5990 }, {6387,4784,3285 ,6935,3423,3064 }, + {3285,4542,4900 ,3064,6727,585 }, {6633,6645,6674 ,7223,7235,7224 }, + {6995,6107,6126 ,7236,7232,7231 }, {6981,6995,6126 ,7230,7236,7231 }, + {5503,5504,5531 ,7042,5781,5783 }, {5713,4648,5890 ,5776,5722,6591 }, + {2374,2304,1118 ,7037,6756,6746 }, {1027,2374,1118 ,6991,7037,6746 }, + {5838,6318,6355 ,3987,6788,3786 }, {6378,2220,1882 ,6728,6724,894 }, + {6709,1072,1882 ,6726,6725,894 }, {2244,5024,6660 ,6795,6952,6601 }, + {6710,6711,1025 ,6968,3473,5299 }, {1524,1655,4872 ,4326,4089,5522 }, + {6839,6862,6810 ,6867,6877,6908 }, {7650,7699,7649 ,7237,3951,3950 }, + {7595,7594,7568 ,4019,4020,5544 }, {5970,4170,4146 ,3518,3731,3730 }, + {2374,4018,3302 ,7037,5084,6757 }, {4144,5292,5175 ,4761,5093,6384 }, + {1217,2220,6378 ,6399,6724,6728 }, {1072,1216,4547 ,6725,6893,242 }, + {5024,5040,2138 ,6952,5469,6602 }, {5040,3704,2138 ,5469,2332,6602 }, + {5111,7166,7197 ,5990,5971,7234 }, {6234,5845,7255 ,5973,143,7238 }, + {5112,7197,6120 ,6021,7234,142 }, {6281,7089,5115 ,4078,5608,5913 }, + {6829,6851,6884 ,7227,7239,7228 }, {6885,6934,6933 ,7240,7241,7229 }, + {6884,6885,6933 ,7228,7240,7229 }, {6934,6982,6981 ,7241,7242,7230 }, + {6933,6934,6981 ,7229,7241,7230 }, {6981,6982,6995 ,7230,7242,7236 }, + {6982,7017,6107 ,7242,7243,7232 }, {6995,6982,6107 ,7236,7242,7232 }, + {7017,5930,6143 ,7243,6593,4394 }, {6107,7017,6143 ,7232,7243,4394 }, + {7424,7451,7423 ,1188,3963,3749 }, {4835,1218,8118 ,2130,3255,2129 }, + {7650,7649,7595 ,7237,3950,4019 }, {2209,2241,2240 ,7038,6950,4478 }, + {1394,1027,1118 ,6989,6991,6746 }, {2335,1394,1118 ,6737,6989,6746 }, + {4784,2406,4542 ,3423,6736,6727 }, {6814,1217,4542 ,6842,6399,6727 }, + {4530,1554,4209 ,1376,6304,6269 }, {4574,1268,6062 ,6477,4568,6478 }, + {2220,6709,1882 ,6724,6726,894 }, {5166,5473,5120 ,3427,3429,5857 }, + {1655,1524,2432 ,4089,4326,5427 }, {5427,5395,5428 ,5811,5348,5812 }, + {8093,8076,8072 ,2288,617,511 }, {6569,4746,2222 ,6745,5260,2509 }, + {5277,5276,5255 ,6237,1525,3682 }, {6180,3623,3675 ,6811,4206,4300 }, + {6868,6657,5041 ,5554,6828,6799 }, {2304,2374,3302 ,6756,7037,6757 }, + {6318,6356,6355 ,6788,3787,3786 }, {6746,6747,4784 ,6864,6878,3423 }, + {4542,1217,6378 ,6727,6399,6728 }, {7160,7185,7159 ,6271,5073,5075 }, + {7267,7246,7291 ,4331,3772,3774 }, {4552,1309,3371 ,7244,3709,3708 }, + {3265,4552,3371 ,1033,7244,3708 }, {7127,7128,7159 ,7245,5391,5075 }, + {6292,6334,6333 ,5403,7246,7214 }, {6060,6292,6333 ,5914,5403,7214 }, + {6334,6335,6333 ,7246,6534,7214 }, {6370,6369,6333 ,7247,7215,7214 }, + {6335,6370,6333 ,6534,7247,7214 }, {6407,6406,6369 ,7248,7216,7215 }, + {6370,6407,6369 ,7247,7248,7215 }, {6429,6428,6406 ,7249,7217,7216 }, + {6407,6429,6406 ,7248,7249,7216 }, {6429,6456,6455 ,7249,7250,7218 }, + {6428,6429,6455 ,7217,7249,7218 }, {6456,6501,6500 ,7250,7251,7219 }, + {6455,6456,6500 ,7218,7250,7219 }, {6519,6518,6500 ,7252,7220,7219 }, + {6501,6519,6500 ,7251,7252,7219 }, {6547,6546,6518 ,7253,7221,7220 }, + {6519,6547,6518 ,7252,7253,7220 }, {6547,6593,6592 ,7253,7254,7222 }, + {6546,6547,6592 ,7221,7253,7222 }, {6593,6634,6633 ,7254,7255,7223 }, + {6592,6593,6633 ,7222,7254,7223 }, {6633,6634,6645 ,7223,7255,7235 }, + {6634,6675,6674 ,7255,7256,7224 }, {6645,6634,6674 ,7235,7255,7224 }, + {6675,6731,6730 ,7256,7257,7225 }, {6674,6675,6730 ,7224,7256,7225 }, + {6731,6778,6777 ,7257,7258,7226 }, {6730,6731,6777 ,7225,7257,7226 }, + {6778,6830,6829 ,7258,7259,7227 }, {6777,6778,6829 ,7226,7258,7227 }, + {6829,6830,6851 ,7227,7259,7239 }, {6830,6885,6884 ,7259,7240,7228 }, + {6851,6830,6884 ,7239,7259,7228 }, {6935,6934,6885 ,7260,7241,7240 }, + {6934,6935,6982 ,7241,7260,7242 }, {1664,6228,751 ,2268,1366,1365 }, + {7047,2335,7046 ,6853,6737,6735 }, {5053,1025,6389 ,6848,5299,5301 }, + {6714,6657,6868 ,5300,6828,5554 }, {3872,5082,1551 ,4811,5218,5275 }, + {2406,6814,4542 ,6736,6842,6727 }, {6312,6747,6746 ,6749,6878,6864 }, + {3510,75,2837 ,6267,3962,2511 }, {2837,1518,3510 ,2511,3978,6267 }, + {6114,6355,5151 ,1805,3786,1803 }, {5085,1947,1881 ,3675,6723,7066 }, + {5041,6065,5687 ,6799,6616,6420 }, {3336,3309,6285 ,3922,3892,3923 }, + {4831,3340,4486 ,3154,114,819 }, {6318,5838,6303 ,6788,3987,7261 }, + {6303,6304,6318 ,7261,7233,6788 }, {7116,7115,7089 ,7262,1024,5608 }, + {6432,6553,3265 ,1032,780,1033 }, {5914,5170,5150 ,1566,3683,6032 }, + {7149,7173,7165 ,7263,7264,7265 }, {6292,6335,6334 ,5403,6534,7246 }, + {6371,6370,6335 ,7266,7247,6534 }, {6370,6371,6407 ,7247,7266,7248 }, + {6593,6608,6634 ,7254,7267,7255 }, {6688,5102,5101 ,6846,6845,6422 }, + {5491,6795,6741 ,5419,5418,5388 }, {1380,6312,6310 ,6721,6749,3918 }, + {6747,2406,4784 ,6878,6736,3423 }, {6310,6312,6746 ,3918,6749,6864 }, + {1126,5944,6037 ,7086,4754,5235 }, {4574,3079,5301 ,6477,6540,2698 }, + {209,648,4892 ,4499,4679,5386 }, {3079,3631,5301 ,6540,5922,2698 }, + {4233,1026,1072 ,6185,6218,6725 }, {5102,5041,5007 ,6845,6799,7268 }, + {5007,5041,5687 ,7268,6799,6420 }, {7047,1394,2335 ,6853,6989,6737 }, + {5479,5491,6741 ,5387,5419,5388 }, {6795,6794,6741 ,5418,6880,5388 }, + {255,1380,256 ,6731,6721,7269 }, {3277,2567,227 ,961,2842,2157 }, + {6083,6303,5838 ,3989,7261,3987 }, {7797,7875,7848 ,3506,4118,343 }, + {6620,3265,6553 ,7270,1033,780 }, {1309,4552,3935 ,3709,7244,4346 }, + {6079,6343,5838 ,5580,5122,3987 }, {6895,6916,6864 ,5434,596,6865 }, + {6962,6963,7001 ,3260,1048,6815 }, {1170,6184,5627 ,4103,6089,6628 }, + {4588,4587,8020 ,775,774,1405 }, {6292,6306,6335 ,5403,5669,6534 }, + {6608,6635,6634 ,7267,7271,7255 }, {6676,6675,6634 ,7272,7256,7255 }, + {6635,6676,6634 ,7271,7272,7255 }, {6676,6732,6731 ,7272,7273,7257 }, + {6675,6676,6731 ,7256,7272,7257 }, {6732,6779,6778 ,7273,7274,7258 }, + {6731,6732,6778 ,7257,7273,7258 }, {6779,6800,6778 ,7274,7275,7258 }, + {6800,6831,6830 ,7275,7276,7259 }, {6778,6800,6830 ,7258,7275,7259 }, + {6831,6886,6885 ,7276,7277,7240 }, {6830,6831,6885 ,7259,7276,7240 }, + {6936,6935,6885 ,7278,7260,7240 }, {6886,6936,6885 ,7277,7278,7240 }, + {6936,6983,6982 ,7278,7279,7242 }, {6935,6936,6982 ,7260,7278,7242 }, + {6983,7018,7017 ,7279,7280,7243 }, {6982,6983,7017 ,7242,7279,7243 }, + {7058,5930,7017 ,6594,6593,7243 }, {7018,7058,7017 ,7280,6594,7243 }, + {1120,1167,876 ,5339,5341,5340 }, {3498,6005,1171 ,5800,5691,6441 }, + {5101,5007,5687 ,6422,7268,6420 }, {2240,2241,3978 ,4478,6950,4549 }, + {6795,6813,6794 ,5418,6916,6880 }, {3281,1380,6310 ,6794,6721,3918 }, + {6688,5101,5048 ,6846,6422,6421 }, {352,255,256 ,593,6731,7269 }, + {353,352,256 ,7281,593,7269 }, {6842,6841,6794 ,5433,7164,6880 }, + {6813,6842,6794 ,6916,5433,6880 }, {5101,5102,5007 ,6422,6845,7268 }, + {6865,6840,6841 ,5435,6868,7164 }, {6842,6865,6841 ,5433,5435,7164 }, + {7500,4595,6345 ,326,5289,191 }, {7271,7320,7270 ,2554,7282,2555 }, + {7898,7874,7848 ,4117,344,343 }, {6314,6553,6646 ,1132,780,1133 }, + {7539,7515,7538 ,1080,1079,2014 }, {5051,6620,6553 ,7283,7270,780 }, + {6314,5051,6553 ,1132,7283,780 }, {5051,3265,6620 ,7283,1033,7270 }, + {6798,4552,3265 ,7284,7244,1033 }, {5051,6798,3265 ,7283,7284,1033 }, + {5010,4807,4552 ,6006,6449,7244 }, {6798,5010,4552 ,7284,6006,7244 }, + {5943,6306,6292 ,5402,5669,5403 }, {2247,2452,353 ,7285,7091,7281 }, + {1380,3281,256 ,6721,6794,7269 }, {4588,8020,7995 ,775,1405,4135 }, + {6048,6049,1214 ,6552,6570,6573 }, {3820,1218,4835 ,5591,3255,2130 }, + {6372,6371,6335 ,7286,7266,6534 }, {6336,6372,6335 ,5670,7286,6534 }, + {6372,6408,6407 ,7286,7287,7248 }, {6371,6372,6407 ,7266,7286,7248 }, + {6408,6429,6407 ,7287,7249,7248 }, {6457,6456,6429 ,7288,7250,7249 }, + {6408,6457,6429 ,7287,7288,7249 }, {6502,6501,6456 ,7289,7251,7250 }, + {6457,6502,6456 ,7288,7289,7250 }, {6520,6519,6501 ,7290,7252,7251 }, + {6502,6520,6501 ,7289,7290,7251 }, {6520,6548,6547 ,7290,7291,7253 }, + {6519,6520,6547 ,7252,7290,7253 }, {6548,6594,6593 ,7291,7292,7254 }, + {6547,6548,6593 ,7253,7291,7254 }, {6609,6608,6593 ,7293,7267,7254 }, + {6594,6609,6593 ,7292,7293,7254 }, {6609,6635,6608 ,7293,7271,7267 }, + {7058,5929,5979 ,6594,6029,6028 }, {5359,5977,1936 ,3713,809,848 }, + {6914,6965,6945 ,3532,6732,2632 }, {7066,2452,7065 ,3586,7091,5182 }, + {6895,6864,6840 ,5434,6865,6868 }, {5404,6576,6529 ,3559,6883,5025 }, + {5350,5404,6529 ,1526,3559,5025 }, {5205,5166,5190 ,5219,3427,5858 }, + {6855,6856,6893 ,6959,6892,6844 }, {2308,3302,6300 ,6798,6757,6146 }, + {6297,6442,6696 ,6851,6850,6849 }, {6865,6895,6840 ,5435,5434,6868 }, + {6916,6915,6864 ,596,6871,6865 }, {7028,7066,7027 ,3587,3586,3175 }, + {1492,5075,4017 ,1585,1586,5342 }, {876,4017,4232 ,5340,5342,6299 }, + {7875,7898,7848 ,4118,4117,343 }, {7925,7924,7874 ,7294,815,344 }, + {7023,6314,2259 ,7295,1132,1131 }, {5645,6798,5051 ,7296,7284,7283 }, + {2140,5010,6798 ,5635,6006,7284 }, {5645,2140,6798 ,7296,5635,7284 }, + {7846,7897,7845 ,4162,6015,5725 }, {2452,352,353 ,7091,593,7281 }, + {6114,6079,6355 ,1805,5580,3786 }, {5248,4023,5247 ,6469,3827,4096 }, + {6502,6548,6520 ,7289,7291,7290 }, {6594,6635,6609 ,7292,7271,7293 }, + {2452,2247,7065 ,7091,7285,5182 }, {7028,7027,6968 ,3587,3175,595 }, + {5917,4541,3079 ,6402,6630,6540 }, {1283,3844,5788 ,2978,2443,6086 }, + {6464,297,5966 ,3633,1918,5054 }, {1071,2455,1027 ,3382,475,6991 }, + {6442,6708,6696 ,6850,6148,6849 }, {6320,3533,7439 ,190,192,7297 }, + {7925,7974,7924 ,7294,4171,815 }, {3426,2566,2527 ,4438,2635,2637 }, + {7023,5051,6314 ,7295,7283,1132 }, {5892,6036,3498 ,2683,6141,5800 }, + {4170,5970,5447 ,3731,3518,5633 }, {6947,6988,6946 ,4587,6771,594 }, + {7066,7065,7027 ,3586,5182,3175 }, {5363,5396,5383 ,3269,3548,3547 }, + {6458,6457,6408 ,7298,7288,7287 }, {6503,6502,6457 ,7299,7289,7288 }, + {6458,6503,6457 ,7298,7299,7288 }, {6549,6548,6502 ,7300,7291,7289 }, + {6503,6549,6502 ,7299,7300,7289 }, {6595,6594,6548 ,7301,7292,7291 }, + {6549,6595,6548 ,7300,7301,7291 }, {6636,6635,6594 ,7302,7271,7292 }, + {6595,6636,6594 ,7301,7302,7292 }, {6677,6676,6635 ,7303,7272,7271 }, + {6636,6677,6635 ,7302,7303,7271 }, {6733,6732,6676 ,7304,7273,7272 }, + {6677,6733,6676 ,7303,7304,7272 }, {6780,6779,6732 ,7305,7274,7273 }, + {6733,6780,6732 ,7304,7305,7273 }, {6801,6800,6779 ,7306,7275,7274 }, + {6780,6801,6779 ,7305,7306,7274 }, {6801,6832,6831 ,7306,7307,7276 }, + {6800,6801,6831 ,7275,7306,7276 }, {6887,6886,6831 ,7308,7277,7276 }, + {6832,6887,6831 ,7307,7308,7276 }, {6937,6936,6886 ,7309,7278,7277 }, + {6887,6937,6886 ,7308,7309,7277 }, {6937,6984,6983 ,7309,7310,7279 }, + {6936,6937,6983 ,7278,7309,7279 }, {7019,7018,6983 ,7311,7280,7279 }, + {6984,7019,6983 ,7310,7311,7279 }, {7019,7059,7058 ,7311,7312,6594 }, + {7018,7019,7058 ,7280,7311,6594 }, {7059,5045,5929 ,7312,4214,6029 }, + {7058,7059,5929 ,6594,7312,6029 }, {6967,7007,6987 ,4629,4628,6917 }, + {6988,7028,6968 ,6771,3587,595 }, {6920,6947,6919 ,6783,4587,6778 }, + {6863,6862,6839 ,6866,6877,6867 }, {6169,6190,3704 ,2334,6610,2332 }, + {5476,5055,1261 ,6495,6470,6472 }, {4595,4649,5118 ,5289,5288,6825 }, + {6920,6919,6896 ,6783,6778,6787 }, {4597,4583,7788 ,5541,5498,7313 }, + {7534,7533,7511 ,7314,7039,6677 }, {7115,7102,7103 ,1024,1240,6455 }, + {4553,2259,6351 ,7315,1131,1568 }, {6322,4553,6351 ,3531,7315,1568 }, + {4553,6296,2259 ,7315,7316,1131 }, {6298,7023,2259 ,7317,7295,1131 }, + {6296,6298,2259 ,7316,7317,1131 }, {5100,5051,7023 ,7318,7283,7295 }, + {6298,5100,7023 ,7317,7318,7295 }, {5100,7078,5051 ,7318,7319,7283 }, + {1262,5645,5051 ,7320,7296,7283 }, {7078,1262,5051 ,7319,7320,7283 }, + {4565,2140,5645 ,5944,5635,7296 }, {1262,4565,5645 ,7320,5944,7296 }, + {6463,4566,4985 ,5159,5143,2703 }, {6009,6337,6336 ,6129,7321,5670 }, + {5943,6009,6336 ,5402,6129,5670 }, {6337,6373,6372 ,7321,7322,7286 }, + {6336,6337,6372 ,5670,7321,7286 }, {6373,6409,6408 ,7322,7323,7287 }, + {6372,6373,6408 ,7286,7322,7287 }, {6459,6458,6408 ,7324,7298,7287 }, + {6409,6459,6408 ,7323,7324,7287 }, {6550,6549,6503 ,7325,7300,7299 }, + {6637,6636,6595 ,7326,7302,7301 }, {6780,6832,6801 ,7305,7307,7306 }, + {6988,6968,6946 ,6771,595,594 }, {6947,6946,6918 ,4587,594,6779 }, + {6792,6839,6811 ,5056,6867,399 }, {6156,1165,5917 ,6250,3153,6402 }, + {2241,5454,3978 ,6950,6817,4549 }, {2234,2235,2139 ,6949,6870,6895 }, + {3192,6387,3285 ,3063,6935,3064 }, {1313,1075,2373 ,3674,6854,6722 }, + {8037,8036,7996 ,4528,7327,7328 }, {5115,7103,6282 ,5913,6455,6617 }, + {41,1262,7078 ,6533,7320,7319 }, {5100,41,7078 ,7318,6533,7319 }, + {6029,6028,5976 ,1454,5316,5903 }, {1270,6569,2222 ,6781,6745,2509 }, + {6867,6896,6844 ,6951,6787,6822 }, {3838,4166,5999 ,6257,6084,5184 }, + {6637,6677,6636 ,7326,7303,7302 }, {6780,6801,6832 ,7305,7306,7307 }, + {7059,7072,5045 ,7312,7329,4214 }, {4577,4576,4575 ,5352,1628,3128 }, + {6792,6811,6793 ,5056,399,6879 }, {5447,4565,5564 ,5633,5944,5943 }, + {6947,6918,6919 ,4587,6779,6778 }, {3437,3500,3465 ,4142,4177,6890 }, + {3500,3551,3550 ,4177,4180,4179 }, {3670,4326,489 ,281,219,3639 }, + {3051,2273,5474 ,6488,5322,4243 }, {5415,4565,41 ,5951,5944,6533 }, + {6866,6842,6843 ,6786,5433,6911 }, {7818,4584,4598 ,5309,5308,5343 }, + {7187,7186,7161 ,7330,5370,2559 }, {1634,5482,1423 ,768,3565,151 }, + {4553,6298,6296 ,7315,7317,7316 }, {1262,41,4565 ,7320,6533,5944 }, + {6391,307,6463 ,6804,5095,5159 }, {2247,353,1071 ,7285,7281,3382 }, + {1215,5006,184 ,3937,1351,4052 }, {6293,6338,6337 ,7212,7331,7321 }, + {6009,6293,6337 ,6129,7212,7321 }, {6338,6374,6373 ,7331,7332,7322 }, + {6337,6338,6373 ,7321,7331,7322 }, {6410,6409,6373 ,7333,7323,7322 }, + {6374,6410,6373 ,7332,7333,7322 }, {6460,6459,6409 ,7334,7324,7323 }, + {6410,6460,6409 ,7333,7334,7323 }, {6460,6458,6459 ,7334,7298,7324 }, + {6504,6503,6458 ,7335,7299,7298 }, {6460,6504,6458 ,7334,7335,7298 }, + {6551,6550,6503 ,7336,7325,7299 }, {6504,6551,6503 ,7335,7336,7299 }, + {6551,6549,6550 ,7336,7300,7325 }, {6596,6595,6549 ,7337,7301,7300 }, + {6551,6596,6549 ,7336,7337,7300 }, {6638,6637,6595 ,7338,7326,7301 }, + {6596,6638,6595 ,7337,7338,7301 }, {6678,6677,6637 ,7339,7303,7326 }, + {6638,6678,6637 ,7338,7339,7326 }, {6678,6693,6677 ,7339,7340,7303 }, + {6734,6733,6677 ,7341,7304,7303 }, {6693,6734,6677 ,7340,7341,7303 }, + {6781,6780,6733 ,7342,7305,7304 }, {6734,6781,6733 ,7341,7342,7304 }, + {6781,6801,6780 ,7342,7306,7305 }, {6833,6832,6801 ,7343,7307,7306 }, + {6781,6833,6801 ,7342,7343,7306 }, {6888,6887,6832 ,7344,7308,7307 }, + {6833,6888,6832 ,7343,7344,7307 }, {6888,6938,6937 ,7344,7345,7309 }, + {6887,6888,6937 ,7308,7344,7309 }, {6938,6985,6984 ,7345,7346,7310 }, + {6937,6938,6984 ,7309,7345,7310 }, {7020,7019,6984 ,7347,7311,7310 }, + {6985,7020,6984 ,7346,7347,7310 }, {7020,7060,7059 ,7347,7348,7312 }, + {7019,7020,7059 ,7311,7347,7312 }, {7060,7061,7072 ,7348,7141,7329 }, + {7059,7060,7072 ,7312,7348,7329 }, {7072,7061,5045 ,7329,7141,4214 }, + {5672,5744,7009 ,5324,6090,5325 }, {2074,2247,1071 ,6990,7285,3382 }, + {1071,353,3661 ,3382,7281,789 }, {1165,4541,5917 ,3153,6630,6402 }, + {1165,3446,5487 ,3153,3718,6558 }, {4541,1165,5487 ,6630,3153,6558 }, + {6319,5125,5151 ,6789,5467,1803 }, {4597,5136,4630 ,5541,1817,827 }, + {4598,4631,5137 ,5343,1816,1815 }, {7705,7754,7704 ,7349,7350,3562 }, + {3352,7438,3321 ,2724,7351,2461 }, {2249,6352,6322 ,3751,7352,3531 }, + {3176,4553,6322 ,7353,7315,3531 }, {6352,3176,6322 ,7352,7353,3531 }, + {6376,6298,4553 ,7354,7317,7315 }, {3176,6376,4553 ,7353,7354,7315 }, + {6438,5100,6298 ,7355,7318,7317 }, {6376,6438,6298 ,7354,7355,7317 }, + {5037,41,5100 ,3206,6533,7318 }, {6438,5037,5100 ,7355,3206,7318 }, + {1809,4549,6605 ,878,877,5164 }, {6505,6504,6460 ,7356,7335,7334 }, + {6597,6596,6551 ,7357,7337,7336 }, {7020,7061,7060 ,7347,7141,7348 }, + {5211,5170,5191 ,6071,3683,2900 }, {2452,2246,352 ,7091,591,593 }, + {6919,6946,6918 ,6778,594,6779 }, {351,5086,5069 ,592,6064,6730 }, + {6969,1165,6156 ,7358,3153,6250 }, {5001,2271,4018 ,477,5071,5084 }, + {3336,6324,3335 ,3922,4239,4222 }, {4445,6757,6748 ,3648,3409,3408 }, + {2220,4233,1072 ,6724,6185,6725 }, {7818,4598,5137 ,5309,5343,1815 }, + {7204,7203,7186 ,5120,4366,5370 }, {535,5105,6686 ,2001,5456,750 }, + {7227,7247,7269 ,7359,7360,7361 }, {6956,6376,3176 ,7362,7354,7353 }, + {5137,4597,7788 ,1815,5541,7313 }, {4142,5085,1881 ,6839,3675,7066 }, + {7065,2247,2073 ,5182,7285,5183 }, {7027,7065,7049 ,3175,5182,5454 }, + {6505,6551,6504 ,7356,7336,7335 }, {6597,6638,6596 ,7357,7338,7337 }, + {6678,6734,6693 ,7339,7341,7340 }, {6834,6833,6781 ,7363,7343,7342 }, + {7020,7037,7061 ,7347,7364,7141 }, {5751,7038,5812 ,5878,3832,660 }, + {6949,6947,6948 ,4585,4587,6782 }, {5059,4233,7081 ,1253,6185,7114 }, + {1125,3779,2142 ,5723,6565,4936 }, {4068,3952,3259 ,408,5597,407 }, + {7998,8021,7997 ,7209,7365,7210 }, {5910,6343,6079 ,5121,5122,5580 }, + {6710,1025,5053 ,6968,5299,6848 }, {3281,6310,6309 ,6794,3918,3690 }, + {6481,6378,4900 ,6729,6728,585 }, {7818,5137,7788 ,5309,1815,7313 }, + {7187,7204,7186 ,7330,5120,5370 }, {7908,7907,7858 ,7366,7367,7368 }, + {227,8128,3277 ,2157,959,961 }, {6956,6438,6376 ,7362,7355,7354 }, + {6158,6034,6293 ,6112,6049,7212 }, {6034,6339,6338 ,6049,6048,7331 }, + {6293,6034,6338 ,7212,6049,7331 }, {6339,6375,6374 ,6048,7369,7332 }, + {6338,6339,6374 ,7331,6048,7332 }, {6411,6410,6374 ,7370,7333,7332 }, + {6375,6411,6374 ,7369,7370,7332 }, {6461,6460,6410 ,7371,7334,7333 }, + {6411,6461,6410 ,7370,7371,7333 }, {6506,6505,6460 ,3549,7356,7334 }, + {6461,6506,6460 ,7371,3549,7334 }, {5395,6551,6505 ,5348,7336,7356 }, + {6506,5395,6505 ,3549,5348,7356 }, {5395,6568,6551 ,5348,6717,7336 }, + {5427,6597,6551 ,5811,7357,7336 }, {6568,5427,6551 ,6717,5811,7336 }, + {5463,6638,6597 ,5801,7338,7357 }, {5427,5463,6597 ,5811,5801,7357 }, + {5463,5503,6638 ,5801,7042,7338 }, {5531,6678,6638 ,5783,7339,7338 }, + {5503,5531,6638 ,7042,5783,7338 }, {5566,6734,6678 ,5759,7341,7339 }, + {5531,5566,6678 ,5783,5759,7339 }, {6782,6781,6734 ,7372,7342,7341 }, + {5566,6782,6734 ,5759,7372,7341 }, {6835,6834,6781 ,7373,7363,7342 }, + {6782,6835,6781 ,7372,7373,7342 }, {6835,6833,6834 ,7373,7343,7363 }, + {6889,6888,6833 ,7374,7344,7343 }, {6835,6889,6833 ,7373,7374,7343 }, + {6889,6904,6888 ,7374,7375,7344 }, {6939,6938,6888 ,7376,7345,7344 }, + {6904,6939,6888 ,7375,7376,7344 }, {6939,6940,6938 ,7376,7377,7345 }, + {6986,6985,6938 ,7378,7346,7345 }, {6940,6986,6938 ,7377,7378,7345 }, + {6986,7021,7020 ,7378,7379,7347 }, {6985,6986,7020 ,7346,7378,7347 }, + {7020,7021,7037 ,7347,7379,7364 }, {7021,7062,7061 ,7379,6563,7141 }, + {7037,7021,7061 ,7364,7379,7141 }, {4752,1297,4323 ,5196,4048,4045 }, + {712,3702,6757 ,3650,1303,3409 }, {4010,4009,3968 ,4343,3870,3848 }, + {3529,1941,3371 ,3837,1034,3708 }, {6155,6970,6969 ,3726,3688,7358 }, + {6156,6155,6969 ,6250,3726,7358 }, {6970,4831,1165 ,3688,3154,3153 }, + {6969,6970,1165 ,7358,3688,3153 }, {978,4965,4966 ,1620,784,4646 }, + {1217,5059,2220 ,6399,1253,6724 }, {3281,2271,2212 ,6794,5071,476 }, + {2073,2247,2074 ,5183,7285,6990 }, {5027,6684,2272 ,5516,1161,1201 }, + {5136,4597,5137 ,1817,5541,1815 }, {4803,468,505 ,4776,1369,986 }, + {7103,7102,6283 ,6455,1240,4298 }, {6323,2249,6531 ,1451,3751,2280 }, + {4551,6352,2249 ,7380,7352,3751 }, {6323,4551,2249 ,1451,7380,3751 }, + {1989,3176,6352 ,3016,7353,7352 }, {4551,1989,6352 ,7380,3016,7352 }, + {5012,6956,3176 ,5212,7362,7353 }, {1989,5012,3176 ,3016,5212,7353 }, + {6417,6438,6956 ,762,7355,7362 }, {5012,6417,6956 ,5212,762,7362 }, + {6417,5388,5037 ,762,5428,3206 }, {6438,6417,5037 ,7355,762,3206 }, + {5037,5388,6317 ,3206,5428,3207 }, {353,256,2212 ,7281,7269,476 }, + {5211,6433,5210 ,6071,6691,6033 }, {6165,7061,7062 ,4219,7141,6563 }, + {5604,6782,5566 ,7213,7372,5759 }, {6940,6939,6904 ,7377,7376,7375 }, + {6889,6940,6904 ,7374,7377,7375 }, {835,3984,5527 ,3030,6017,5977 }, + {4545,2274,4980 ,586,893,241 }, {3661,353,2212 ,789,7281,476 }, + {256,3281,2212 ,7269,6794,476 }, {6465,5009,4985 ,6927,6803,2703 }, + {6968,7008,6967 ,595,3176,4629 }, {5940,6176,6715 ,1625,1624,6929 }, + {3321,3353,3352 ,2461,2424,2724 }, {2074,1394,7047 ,6990,6989,6853 }, + {5974,3079,4574 ,6474,6540,6477 }, {7660,7707,7659 ,7381,7382,7383 }, + {6792,6791,6738 ,5056,6900,5057 }, {6009,5514,6293 ,6129,6128,7212 }, + {5744,5718,5756 ,6090,6126,6076 }, {6531,6509,6323 ,2280,1452,1451 }, + {4586,2163,911 ,5257,243,1159 }, {6946,6916,6917 ,594,596,5515 }, + {6411,6462,6461 ,7370,7384,7371 }, {6462,6506,6461 ,7384,3549,7371 }, + {5715,7050,7028 ,4076,6967,3587 }, {3779,1125,4648 ,6565,5723,5722 }, + {2871,3284,257 ,3649,6720,6067 }, {5106,5120,7096 ,5926,5857,4337 }, + {4392,2094,2341 ,844,5570,845 }, {5041,4990,6065 ,6799,7135,6616 }, + {4803,5267,4777 ,4776,4647,3917 }, {5604,6835,6782 ,7213,7373,7372 }, + {5669,6905,5710 ,5553,2506,5254 }, {2343,2210,1074 ,2685,2510,2715 }, + {158,5027,4755 ,5258,5516,6770 }, {8007,2250,7957 ,3854,3716,7385 }, + {7050,5715,5755 ,6967,4076,4047 }, {1947,2235,1881 ,6723,6870,7066 }, + {5405,6656,6618 ,5518,5517,6910 }, {4552,4807,3935 ,7244,6449,4346 }, + {6657,4990,5041 ,6828,7135,6799 }, {4043,2956,2798 ,419,399,420 }, + {5611,6949,6920 ,6177,4585,6783 }, {7262,7287,7241 ,4335,3459,4465 }, + {7769,7744,7794 ,5535,6101,5749 }, {4021,3733,3936 ,1712,5610,5599 }, + {6323,1989,4551 ,1451,3016,7380 }, {4983,4569,2158 ,480,1344,1160 }, + {6949,6948,6920 ,4585,6782,6783 }, {6918,6946,6917 ,6779,594,5515 }, + {4229,381,4246 ,4877,6450,3665 }, {6092,5940,3714 ,1623,1625,3970 }, + {6483,2164,4961 ,3584,3583,2905 }, {5889,6066,6375 ,1587,1455,7369 }, + {6339,5889,6375 ,6048,1587,7369 }, {6090,6411,6375 ,4897,7370,7369 }, + {6066,6090,6375 ,1455,4897,7369 }, {6090,6260,6462 ,4897,3397,7384 }, + {6411,6090,6462 ,7370,4897,7384 }, {5363,6506,6462 ,3269,3549,7384 }, + {6260,5363,6462 ,3397,3269,7384 }, {4530,4209,2239 ,1376,6269,1377 }, + {5120,5106,5121 ,5857,5926,5855 }, {5605,5647,5604 ,5761,5333,7213 }, + {5647,6835,5604 ,5333,7373,7213 }, {5647,6852,6835 ,5333,5335,7373 }, + {6890,6889,6835 ,5334,7374,7373 }, {6852,6890,6835 ,5335,5334,7373 }, + {5750,6940,6889 ,5156,7377,7374 }, {6890,5750,6889 ,5334,5156,7374 }, + {5774,6986,6940 ,5720,7378,7377 }, {5750,5774,6940 ,5156,5720,7377 }, + {5807,7021,6986 ,4769,7379,7378 }, {5774,5807,6986 ,5720,4769,7378 }, + {7021,5807,7062 ,7379,4769,6563 }, {5807,5839,7062 ,4769,4768,6563 }, + {5839,5840,7062 ,4768,4770,6563 }, {5840,4271,1464 ,4770,5767,5768 }, + {7062,5840,1464 ,6563,4770,5768 }, {5668,5626,5669 ,5912,1460,5553 }, + {5076,4200,158 ,6607,6583,5258 }, {4983,6341,2808 ,480,479,522 }, + {7050,5755,7067 ,6967,4047,3585 }, {4978,6413,6155 ,178,112,3726 }, + {4567,4978,6155 ,6655,178,3726 }, {6413,3340,6970 ,112,114,3688 }, + {7733,7758,7707 ,7386,7387,7382 }, {7708,7733,7707 ,7388,7386,7382 }, + {7733,7780,7758 ,7386,7389,7387 }, {7834,7884,7833 ,7390,7391,7392 }, + {7523,7581,7522 ,7393,7394,7395 }, {7518,7546,7545 ,7396,7397,7398 }, + {7629,7684,7683 ,7399,7400,5267 }, {7656,7629,7683 ,7401,7399,5267 }, + {2328,2282,2283 ,3664,3663,3687 }, {2281,2280,8054 ,3626,3613,7402 }, + {2278,2277,4572 ,3495,3522,5949 }, {7956,8004,7981 ,7403,7404,4252 }, + {7173,7172,7165 ,7264,7405,7265 }, {7574,7656,7603 ,7406,7401,5266 }, + {2250,8006,7957 ,3716,7407,7385 }, {7907,7932,7906 ,7367,7408,7409 }, + {4803,505,5290 ,4776,986,5247 }, {7827,7878,7826 ,7410,7411,1616 }, + {7852,7901,7851 ,7412,7413,6625 }, {7116,7141,7140 ,7262,7414,7415 }, + {7263,7312,7262 ,627,4333,4335 }, {402,7690,448 ,5349,6466,1172 }, + {7540,7574,7603 ,5709,7406,5266 }, {7629,7656,7574 ,7399,7401,7406 }, + {8021,8037,7997 ,7365,4528,7210 }, {7604,7605,7657 ,7416,5239,7417 }, + {7726,7774,7725 ,7418,7419,7420 }, {3351,7497,7438 ,1444,7421,7351 }, + {3352,3351,7438 ,2724,1444,7351 }, {7297,7273,7274 ,7422,3467,7423 }, + {3298,3830,4518 ,7424,5882,1281 }, {7678,7677,7623 ,7425,7426,7427 }, + {7399,7425,7398 ,7428,3995,7429 }, {7379,7378,7319 ,7430,7431,7432 }, + {7252,7297,7274 ,3437,7422,7423 }, {7327,7326,7273 ,7433,7434,3467 }, + {7297,7327,7273 ,7422,7433,3467 }, {7327,7355,7326 ,7433,7435,7434 }, + {7403,7457,7486 ,6588,1078,7436 }, {7457,7539,7538 ,1078,1080,2014 }, + {7856,7905,7855 ,4635,7437,6545 }, {7626,7625,7624 ,7438,7139,7439 }, + {7405,7461,7460 ,7440,7441,7442 }, {7431,7405,7460 ,7443,7440,7442 }, + {7461,7489,7460 ,7441,7444,7442 }, {7489,7519,7518 ,7444,7445,7396 }, + {7460,7489,7518 ,7442,7444,7396 }, {7757,7758,7779 ,7446,7387,7447 }, + {7833,7832,7809 ,7392,7448,7449 }, {7707,7758,7757 ,7382,7387,7446 }, + {7732,7707,7757 ,7450,7382,7446 }, {4022,3534,6836 ,3503,942,852 }, + {7320,7380,7379 ,7282,7451,7430 }, {7833,7883,7832 ,7392,7452,7448 }, + {7519,7547,7546 ,7445,7453,7397 }, {7518,7519,7546 ,7396,7445,7397 }, + {7771,7770,7721 ,4336,4272,7454 }, {7547,7607,7546 ,7453,7455,7397 }, + {7661,7660,7607 ,7456,7381,7455 }, {7606,7659,7633 ,7457,7383,7458 }, + {7726,7725,7681 ,7418,7420,7459 }, {7622,7676,7650 ,7460,7461,7237 }, + {7606,7633,7578 ,7457,7458,7462 }, {7661,7708,7660 ,7456,7388,7381 }, + {7480,7511,7454 ,7463,6677,3996 }, {7781,7780,7733 ,7464,7389,7386 }, + {7325,7382,7381 ,7465,7466,7467 }, {7885,7884,7834 ,7468,7391,7390 }, + {6322,8112,6897 ,3531,3530,54 }, {2048,2096,3875 ,5461,788,5845 }, + {7885,7909,7884 ,7468,6603,7391 }, {2262,7936,7884 ,3359,5528,7391 }, + {7909,2262,7884 ,6603,3359,7391 }, {7382,7405,7431 ,7466,7440,7443 }, + {7582,7581,7523 ,7469,7394,7393 }, {7438,7497,7437 ,7351,7421,5938 }, + {7273,7250,7251 ,3467,3418,2671 }, {7497,7496,7437 ,7421,7470,5938 }, + {2526,2525,2485 ,2636,2937,2815 }, {7273,7296,7250 ,3467,7471,3418 }, + {7684,7705,7683 ,7400,7349,5267 }, {7705,7704,7683 ,7349,3562,5267 }, + {6784,2278,4572 ,7472,3495,5949 }, {7110,7133,7108 ,1990,1989,4111 }, + {7355,7405,7382 ,7435,7440,7466 }, {7582,7637,7581 ,7469,7473,7394 }, + {7714,4744,7983 ,4468,4424,4361 }, {5026,8062,7983 ,4225,3272,4361 }, + {5643,5660,5659 ,4617,3054,3053 }, {7698,7720,7747 ,333,4271,331 }, + {7613,7634,7612 ,5106,7474,5107 }, {7516,7575,7574 ,5708,7475,7406 }, + {6784,4572,8039 ,7472,5949,7476 }, {7274,7273,7252 ,7423,3467,3437 }, + {3824,4744,7714 ,3462,4424,4468 }, {3824,1879,4744 ,3462,1283,4424 }, + {3976,3830,3298 ,919,5882,7424 }, {7398,7425,7397 ,7429,3995,1186 }, + {7253,7297,7252 ,1855,7422,3437 }, {7356,7355,7327 ,7477,7435,7433 }, + {2487,2486,7615 ,4427,2814,2816 }, {6316,6311,2162 ,4435,1097,3278 }, + {3824,3298,1879 ,3462,7424,1283 }, {7935,2290,7960 ,5527,1982,3505 }, + {7461,7490,7489 ,7441,7478,7444 }, {7489,7490,7519 ,7444,7478,7445 }, + {7608,7607,7547 ,7479,7455,7453 }, {2250,2285,8006 ,3716,3701,7407 }, + {7881,7907,7906 ,7480,7367,7409 }, {7709,7708,7661 ,7481,7388,7456 }, + {7262,7311,7287 ,4335,4334,3459 }, {7693,3298,3824 ,724,7424,3462 }, + {3298,7693,3976 ,7424,724,919 }, {2292,7936,2262 ,1983,5528,3359 }, + {7734,7733,7708 ,7482,7386,7388 }, {7709,7734,7708 ,7481,7482,7388 }, + {7835,7834,7780 ,7483,7390,7389 }, {7781,7835,7780 ,7464,7483,7389 }, + {7229,7270,7247 ,2556,2555,7360 }, {1788,145,113 ,676,810,677 }, + {3290,3289,7196 ,1204,2056,2401 }, {7517,7516,7459 ,7484,5708,7485 }, + {7883,7934,7882 ,7452,7486,7487 }, {3294,3293,7110 ,3895,1988,1990 }, + {7150,7149,3293 ,7488,7263,1988 }, {7150,7173,7149 ,7488,7264,7263 }, + {7637,7713,7692 ,7473,7489,7490 }, {7251,7250,3288 ,2671,3418,2058 }, + {7960,2290,162 ,3505,1982,2822 }, {7224,7223,7189 ,7491,7492,7493 }, + {6276,7107,6277 ,4316,7494,4387 }, {6282,7103,6283 ,6617,6455,4298 }, + {7713,7736,7692 ,7489,6752,7490 }, {7809,7832,7808 ,7449,7448,7495 }, + {7266,7315,7265 ,6769,7496,7497 }, {7230,7229,7210 ,7498,2556,7499 }, + {7211,7210,7173 ,7500,7499,7264 }, {7211,7230,7210 ,7500,7498,7499 }, + {7321,7353,7320 ,7501,7502,7282 }, {7188,7187,7161 ,7503,7330,2559 }, + {7631,7658,7605 ,7504,7505,5239 }, {7997,8037,7996 ,7210,4528,7328 }, + {7266,7265,7245 ,6769,7497,7506 }, {7935,7960,7933 ,5527,3505,7507 }, + {7931,7981,7930 ,7508,4252,4278 }, {7804,7803,7775 ,4634,4636,7509 }, + {7753,7804,7775 ,3643,4634,7509 }, {7779,7809,7808 ,7447,7449,7495 }, + {7757,7779,7808 ,7446,7447,7495 }, {7727,7775,7726 ,3563,7509,7418 }, + {7934,7933,7882 ,7486,7507,7487 }, {8025,8024,7980 ,7510,7511,4253 }, + {7383,7355,7382 ,7512,7435,7466 }, {7321,7320,7271 ,7501,7282,2554 }, + {7980,8003,7955 ,4253,3776,4254 }, {7855,7904,7854 ,6545,7513,7514 }, + {7353,7380,7320 ,7502,7451,7282 }, {7541,7516,7517 ,7515,5708,7484 }, + {7541,7575,7516 ,7515,7475,5708 }, {7934,7935,7933 ,7486,5527,7507 }, + {7713,2451,7736 ,7489,2102,6752 }, {7732,7757,7731 ,7450,7446,7516 }, + {8116,3976,8129 ,172,919,723 }, {7707,7732,7731 ,7382,7450,7516 }, + {7535,7534,7480 ,7517,7314,7463 }, {7685,7707,7731 ,7518,7382,7516 }, + {7755,7805,7754 ,7519,7520,7350 }, {7707,7685,7659 ,7382,7518,7383 }, + {1274,6108,1275 ,1687,6975,1688 }, {7980,8024,8003 ,4253,7511,3776 }, + {2281,2326,2280 ,3626,3588,3613 }, {7164,7195,7171 ,7521,7522,4306 }, + {7209,7228,7227 ,7523,7524,7359 }, {7228,7247,7227 ,7524,7360,7359 }, + {7855,7854,7803 ,6545,7514,4636 }, {7574,7575,7629 ,7406,7475,7399 }, + {7805,7804,7754 ,7520,4634,7350 }, {7578,7633,7577 ,7462,7458,7525 }, + {7325,7381,7354 ,7465,7467,7526 }, {7755,7754,7705 ,7519,7350,7349 }, + {8005,8004,7956 ,7527,7404,7403 }, {7540,7603,7628 ,5709,5266,7528 }, + {7798,7825,7771 ,4728,3768,4336 }, {7093,7107,6276 ,4318,7494,4316 }, + {7093,7119,7107 ,4318,7529,7494 }, {7119,7147,7146 ,7529,6937,7530 }, + {7683,7682,7655 ,5267,3564,5268 }, {7880,7879,7857 ,7531,7532,7533 }, + {7604,7657,7630 ,7416,7417,7534 }, {7275,7253,7254 ,1854,1855,450 }, + {7298,7297,7253 ,7535,7422,1855 }, {2285,2283,8004 ,3701,3687,7404 }, + {8005,2285,8004 ,7527,3701,7404 }, {7254,7253,6348 ,450,1855,1607 }, + {5558,5569,5599 ,2302,2075,2213 }, {7603,7655,7628 ,5266,5268,7528 }, + {7427,7426,7399 ,7536,7537,7428 }, {7275,7298,7253 ,1854,7535,1855 }, + {7298,7327,7297 ,7535,7433,7422 }, {7406,7405,7355 ,7538,7440,7435 }, + {7356,7406,7355 ,7477,7538,7435 }, {7490,7547,7519 ,7478,7453,7445 }, + {7662,7661,7607 ,7539,7456,7455 }, {7608,7662,7607 ,7479,7539,7455 }, + {1314,7638,1315 ,1409,7540,1627 }, {1877,3802,3948 ,282,4475,280 }, + {3948,3802,4296 ,280,4475,6023 }, {7720,7698,7648 ,4271,333,3952 }, + {7655,7682,7628 ,5268,3564,7528 }, {68,4368,3530 ,4087,4148,7541 }, + {7481,7535,7480 ,7542,7517,7463 }, {7734,7781,7733 ,7482,7464,7386 }, + {7537,7601,7572 ,7543,7544,5124 }, {7886,7885,7834 ,6561,7468,7390 }, + {7835,7886,7834 ,7483,6561,7390 }, {7886,7909,7885 ,6561,6603,7468 }, + {7884,7935,7883 ,7391,5527,7452 }, {7221,7241,7204 ,7545,4465,5120 }, + {3976,8116,5483 ,919,172,171 }, {7900,7951,7926 ,6922,7546,6923 }, + {1877,3825,3802 ,282,7547,4475 }, {59,2553,395 ,967,2716,966 }, + {7901,7900,7851 ,7413,6922,6625 }, {2283,8041,8004 ,3687,7548,7404 }, + {7753,7775,7727 ,3643,7509,3563 }, {7497,7523,7496 ,7421,7393,7470 }, + {7878,7877,7826 ,7411,7549,1616 }, {7459,7516,7458 ,7485,5708,7550 }, + {7900,7899,7849 ,6922,4116,7551 }, {7515,7487,7540 ,1079,5707,5709 }, + {7648,7699,7720 ,3952,3951,4271 }, {7149,7165,7133 ,7263,7265,1989 }, + {8107,2731,872 ,658,659,2962 }, {2410,3400,1877 ,1,3659,282 }, + {1315,1355,1314 ,1627,1408,1409 }, {7907,7958,7932 ,7367,3855,7408 }, + {7454,7453,7397 ,3996,1187,1186 }, {59,2689,2553 ,967,2821,2716 }, + {209,342,1178 ,4499,4542,206 }, {7328,7327,7298 ,7552,7433,7535 }, + {7275,7328,7298 ,1854,7552,7535 }, {7686,7659,7685 ,7553,7383,7518 }, + {7328,7356,7327 ,7552,7477,7433 }, {7432,7405,7406 ,7554,7440,7538 }, + {7633,7659,7632 ,7458,7383,7555 }, {7659,7686,7632 ,7383,7553,7555 }, + {7537,7572,7514 ,7543,5124,3610 }, {7601,7600,7572 ,7544,7556,5124 }, + {7432,7461,7405 ,7554,7441,7440 }, {7548,7547,7490 ,7557,7453,7478 }, + {7609,7608,7547 ,7558,7479,7453 }, {7548,7609,7547 ,7557,7558,7453 }, + {7663,7662,7608 ,7559,7539,7479 }, {7609,7663,7608 ,7558,7559,7479 }, + {7663,7661,7662 ,7559,7456,7539 }, {7710,7709,7661 ,7560,7481,7456 }, + {7663,7710,7661 ,7559,7560,7456 }, {7735,7734,7709 ,7561,7482,7481 }, + {7710,7735,7709 ,7560,7561,7481 }, {7295,7325,7354 ,7562,7465,7526 }, + {2410,1877,3670 ,1,282,281 }, {3400,1538,1451 ,3659,0,175 }, + {3400,3786,1877 ,3659,4074,282 }, {1877,3786,3825 ,282,4074,7547 }, + {3802,3825,1537 ,4475,7547,4473 }, {976,977,1029 ,782,3931,3930 }, + {7849,7899,7875 ,7551,4116,4118 }, {7782,7781,7734 ,7563,7464,7482 }, + {7836,7835,7781 ,7564,7483,7464 }, {7782,7836,7781 ,7563,7564,7464 }, + {7296,7295,7250 ,7471,7562,3418 }, {7836,7886,7835 ,7564,6561,7483 }, + {7721,7720,7676 ,7454,4271,7461 }, {2263,2295,2294 ,3080,3160,3081 }, + {7883,7882,7832 ,7452,7487,7448 }, {3296,3295,2265 ,4159,7565,2662 }, + {7637,7666,7636 ,7473,7566,7567 }, {7229,7247,7210 ,2556,7360,7499 }, + {7801,7853,7800 ,1618,1617,7568 }, {7110,7108,7109 ,1990,4111,4112 }, + {7802,7801,7751 ,6566,1618,4332 }, {7479,7508,7478 ,4423,6196,1402 }, + {3786,1934,3825 ,4074,4620,7547 }, {1934,3514,1537 ,4620,7569,4473 }, + {3825,1934,1537 ,7547,4620,4473 }, {3514,7764,1537 ,7569,7570,4473 }, + {1537,7764,1745 ,4473,7570,6233 }, {7764,7739,4207 ,7570,7571,4247 }, + {1745,7764,4207 ,6233,7570,4247 }, {4961,2164,3191 ,2905,3583,2903 }, + {7620,7646,7645 ,5033,5191,5034 }, {7457,7487,7515 ,1078,5707,1079 }, + {7429,7487,7457 ,7572,5707,1078 }, {3351,7523,7497 ,1444,7393,7421 }, + {2283,2282,2281 ,3687,3663,3626 }, {7296,7325,7295 ,7471,7465,7562 }, + {7106,7105,7092 ,7573,5279,3968 }, {7879,7905,7856 ,7532,7437,4635 }, + {7484,7483,7427 ,3612,7574,7536 }, {7289,7347,7288 ,7575,7576,7577 }, + {7513,7512,7483 ,3611,5203,7574 }, {7373,7399,7347 ,4989,7428,7576 }, + {7456,7455,7400 ,7578,7579,7580 }, {7754,7804,7753 ,7350,4634,3643 }, + {7320,7379,7319 ,7282,7430,7432 }, {7465,7464,7436 ,7581,4246,4245 }, + {7627,7626,7572 ,7582,7438,5124 }, {7313,7346,7312 ,7583,7584,4333 }, + {7488,7517,7459 ,7585,7484,7485 }, {3514,1934,7764 ,7569,4620,7570 }, + {937,975,974 ,5067,4266,5366 }, {8035,2275,8034 ,6675,3152,7586 }, + {7430,7429,7457 ,7587,7572,1078 }, {7209,7227,7246 ,7523,7359,3772 }, + {7429,7430,7457 ,7572,7587,1078 }, {7221,7263,7241 ,7545,627,4465 }, + {7345,7397,7372 ,7588,1186,4373 }, {7736,450,7691 ,6752,1653,4419 }, + {7582,7667,7637 ,7469,7589,7473 }, {7667,7668,7637 ,7589,7590,7473 }, + {7996,7995,7949 ,7328,4135,7591 }, {7377,7430,7429 ,7592,7587,7572 }, + {7318,7292,7352 ,7593,7594,7595 }, {7268,7292,7318 ,3773,7594,7593 }, + {7227,7269,7268 ,7359,7361,3773 }, {7246,7227,7268 ,3772,7359,3773 }, + {7269,7292,7268 ,7361,7594,3773 }, {7637,7636,7580 ,7473,7567,7596 }, + {7581,7637,7580 ,7394,7473,7596 }, {7230,7271,7229 ,7498,2554,2556 }, + {6346,7275,7253 ,4883,1854,1855 }, {7357,7356,7328 ,7597,7477,7552 }, + {7832,7882,7859 ,7448,7487,7598 }, {7646,7620,7675 ,5191,5033,5192 }, + {1934,2350,7764 ,4620,7599,7570 }, {7764,2350,7739 ,7570,7599,7571 }, + {3338,3530,4368 ,4147,7541,4148 }, {4445,3284,2871 ,3648,6720,3649 }, + {7422,7477,7449 ,1404,1403,7600 }, {7778,7731,7807 ,7601,7516,7602 }, + {7832,7859,7831 ,7448,7598,7603 }, {7731,7757,7807 ,7516,7446,7602 }, + {7357,7406,7356 ,7597,7538,7477 }, {7491,7490,7461 ,7604,7478,7441 }, + {7432,7491,7461 ,7554,7604,7441 }, {7549,7548,7490 ,7605,7557,7478 }, + {7610,7609,7548 ,7606,7558,7557 }, {7549,7610,7548 ,7605,7606,7557 }, + {7960,7959,7933 ,3505,7607,7507 }, {7516,7574,7540 ,5708,7406,5709 }, + {7689,7690,402 ,6444,6466,5349 }, {7225,7245,7207 ,1672,7506,7608 }, + {7225,7207,7194 ,1672,7608,1670 }, {7291,7315,7266 ,3774,7496,6769 }, + {7291,7349,7315 ,3774,7609,7496 }, {7163,7164,7171 ,5460,7521,4306 }, + {7174,7173,7150 ,7610,7264,7488 }, {7996,8036,7995 ,7328,7327,4135 }, + {7664,7663,7609 ,7611,7559,7558 }, {7610,7664,7609 ,7606,7611,7558 }, + {7806,7805,7755 ,7612,7520,7519 }, {6796,6898,8063 ,2617,1946,465 }, + {2350,7813,7739 ,7599,7613,7571 }, {7813,3338,7739 ,7613,4147,7571 }, + {4741,3634,3530 ,5508,5507,7541 }, {7808,7832,7831 ,7495,7448,7603 }, + {7806,7829,7805 ,7612,7614,7520 }, {7633,7632,7576 ,7458,7555,7615 }, + {3297,7734,7735 ,4203,7482,7561 }, {7710,3297,7735 ,7560,4203,7561 }, + {7783,7782,7734 ,7616,7563,7482 }, {3297,7783,7734 ,4203,7616,7482 }, + {7295,7354,7324 ,7562,7526,7617 }, {2265,7836,7782 ,2662,7564,7563 }, + {7316,7317,7350 ,5948,5947,7618 }, {7629,7657,7705 ,7399,7417,7349 }, + {7959,7960,2306 ,7607,3505,6405 }, {8039,4572,8038 ,7476,5949,4121 }, + {7401,7456,7400 ,7619,7578,7580 }, {7245,7265,7244 ,7506,7497,7620 }, + {7245,7244,7207 ,7506,7620,7608 }, {7132,7164,7163 ,7621,7521,5460 }, + {7148,7132,7163 ,6936,7621,5460 }, {7783,2265,7782 ,7616,2662,7563 }, + {7250,7295,7249 ,3418,7562,7622 }, {7849,7875,7797 ,7551,4118,3506 }, + {7932,7931,7906 ,7408,7508,7409 }, {156,3691,1934 ,5539,7623,4620 }, + {1934,3691,2350 ,4620,7623,7599 }, {7763,7838,3338 ,7624,7625,4147 }, + {7813,7763,3338 ,7613,7624,4147 }, {4911,4741,3530 ,7626,5508,7541 }, + {936,937,974 ,5234,5067,5366 }, {7289,7288,7242 ,7575,7577,7627 }, + {7829,7857,7805 ,7614,7533,7520 }, {2265,7886,7836 ,2662,6561,7564 }, + {2907,3791,2941 ,111,3739,109 }, {7668,7713,7637 ,7590,7489,7473 }, + {7604,7575,7541 ,7416,7475,7515 }, {7575,7604,7629 ,7475,7416,7399 }, + {7604,7630,7629 ,7416,7534,7399 }, {7630,7657,7629 ,7534,7417,7399 }, + {7118,7119,7145 ,7628,7529,7629 }, {7386,7412,7385 ,5512,3765,7630 }, + {7206,7243,7224 ,7631,7632,7491 }, {7206,7224,7190 ,7631,7491,7633 }, + {7191,7206,7190 ,7634,7631,7633 }, {7290,7289,7243 ,7635,7575,7632 }, + {7290,7348,7289 ,7635,4990,7575 }, {7538,7537,7485 ,2014,7543,7636 }, + {7654,7653,7601 ,7637,7638,7544 }, {8036,8037,4589 ,7327,4528,776 }, + {156,3740,3691 ,5539,7639,7623 }, {3691,3740,2350 ,7623,7639,7599 }, + {3740,7763,7813 ,7639,7624,7613 }, {2350,3740,7813 ,7599,7639,7613 }, + {7763,7122,7838 ,7624,7640,7625 }, {7838,7122,3338 ,7625,7640,4147 }, + {7122,8046,3530 ,7640,7641,7541 }, {3338,7122,3530 ,4147,7640,7541 }, + {3737,1350,791 ,7642,509,7643 }, {7957,7956,7931 ,7385,7403,7508 }, + {7748,7771,7722 ,7644,4336,7645 }, {7932,7957,7931 ,7408,7385,7508 }, + {7577,7633,7576 ,7525,7458,7615 }, {7995,8036,4588 ,4135,7327,775 }, + {7581,7580,7522 ,7394,7596,7395 }, {8041,2283,2281 ,7548,3687,3626 }, + {7905,7904,7855 ,7437,7513,6545 }, {7690,7689,7613 ,6466,6444,5106 }, + {7635,7690,7613 ,7646,6466,5106 }, {7193,7192,7162 ,7647,7648,7649 }, + {7147,7193,7162 ,6937,7647,7649 }, {7222,7263,7221 ,628,627,7545 }, + {7117,7116,7089 ,7650,7262,5608 }, {7090,7117,7089 ,4080,7650,5608 }, + {7143,7170,7190 ,7651,7652,7633 }, {7170,7191,7190 ,7652,7634,7633 }, + {7144,7170,7143 ,7653,7652,7651 }, {7131,7144,7143 ,7654,7653,7651 }, + {7130,7131,7143 ,6823,7654,7651 }, {7119,7118,7107 ,7529,7628,7494 }, + {7553,7579,7552 ,7655,7656,5546 }, {7553,7613,7579 ,7655,5106,7656 }, + {7193,7194,7192 ,7647,1670,7648 }, {7981,8004,7980 ,4252,7404,4253 }, + {7521,7580,7520 ,7657,7596,7658 }, {3740,1413,7763 ,7639,7659,7624 }, + {7122,3423,8046 ,7640,7660,7641 }, {3423,8046,3530 ,7660,7641,7541 }, + {8046,3423,3530 ,7641,7660,7541 }, {8046,3633,4911 ,7641,7661,7626 }, + {3530,8046,4911 ,7541,7641,7626 }, {6869,4741,4911 ,7662,5508,7626 }, + {3633,6869,4911 ,7661,7662,7626 }, {1350,3737,5571 ,509,7642,4616 }, + {6869,4956,4741 ,7662,5381,5508 }, {7162,7192,7170 ,7649,7648,7652 }, + {7749,7748,7723 ,7663,7644,7664 }, {7748,7799,7771 ,7644,4727,4336 }, + {2281,8054,8024 ,3626,7402,7511 }, {6658,5570,3121 ,7665,508,2076 }, + {7272,7293,7322 ,7666,6473,75 }, {7654,7703,7653 ,7637,7667,7638 }, + {7774,7773,7725 ,7419,7668,7420 }, {7207,7206,7191 ,7608,7631,7634 }, + {7346,7345,7312 ,7584,7588,4333 }, {3615,5700,5681 ,201,4501,2934 }, + {3530,3634,68 ,7541,5507,4087 }, {7522,7580,7521 ,7395,7596,7657 }, + {7148,7163,7147 ,6936,5460,6937 }, {7171,7195,7209 ,4306,7522,7523 }, + {7851,7900,7850 ,6625,6922,3769 }, {7248,7293,7272 ,73,6473,7666 }, + {7205,7204,7188 ,7669,5120,7503 }, {7602,7628,7601 ,5867,7528,7544 }, + {7428,7427,7373 ,7670,7536,4989 }, {7374,7428,7373 ,4988,7670,4989 }, + {7194,7207,7192 ,1670,7608,7648 }, {7465,7521,7464 ,7581,7657,4246 }, + {7854,7878,7827 ,7514,7411,7410 }, {7580,7553,7520 ,7596,7655,7658 }, + {7579,7613,7552 ,7656,5106,5546 }, {3740,156,4264 ,7639,5539,5540 }, + {1413,6477,7763 ,7659,7671,7624 }, {7763,6477,7122 ,7624,7671,7640 }, + {7122,6477,3423 ,7640,7671,7660 }, {3423,3633,8046 ,7660,7661,7641 }, + {5039,6869,3633 ,7672,7662,7661 }, {2733,4956,6869 ,5367,5381,7662 }, + {5039,2733,6869 ,7672,5367,7662 }, {5046,1081,2993 ,5232,3745,3744 }, + {7680,7679,7625 ,7673,7674,7139 }, {7118,7145,7144 ,7628,7629,7653 }, + {7651,7680,7625 ,7675,7673,7139 }, {7858,7907,7881 ,7368,7367,7480 }, + {8054,8053,8024 ,7402,7676,7511 }, {7511,7533,7510 ,6677,7039,1873 }, + {7692,7736,7691 ,7490,6752,4419 }, {7272,7322,7321 ,7666,75,7501 }, + {3843,935,3933 ,7677,5264,7678 }, {6552,2278,6784 ,7679,3495,7472 }, + {7325,7383,7382 ,7465,7512,7466 }, {2278,2243,2242 ,3495,1993,2551 }, + {5681,2453,5659 ,2934,42,3053 }, {7192,7207,7191 ,7648,7608,7634 }, + {7462,7494,7435 ,4310,4309,6244 }, {795,1511,1344 ,302,4921,300 }, + {1511,795,4377 ,4921,302,3090 }, {7426,7425,7399 ,7537,3995,7428 }, + {7347,7346,7313 ,7576,7584,7583 }, {7189,7205,7188 ,7493,7669,7503 }, + {7288,7347,7313 ,7577,7576,7583 }, {7131,7130,7105 ,7654,6823,5279 }, + {7313,7312,7263 ,7583,4333,627 }, {7680,7701,7679 ,7673,3515,7674 }, + {7224,7242,7223 ,7491,7627,7492 }, {7437,7496,7465 ,5938,7470,7581 }, + {7496,7522,7465 ,7470,7395,7581 }, {4897,7837,4264 ,5442,5967,5540 }, + {7837,3912,3740 ,5967,7680,7639 }, {3740,3912,1413 ,7639,7680,7659 }, + {6477,7669,3423 ,7671,7681,7660 }, {3423,7861,3633 ,7660,7682,7661 }, + {7385,7412,7384 ,7630,3765,7683 }, {8136,8063,8140 ,2449,465,2269 }, + {7553,7552,7495 ,7655,5546,4308 }, {7520,7553,7495 ,7658,7655,4308 }, + {7878,7929,7877 ,7411,7684,7549 }, {7626,7624,7570 ,7438,7439,5204 }, + {8060,3783,8037 ,7685,5311,4528 }, {8043,5557,5558 ,6397,756,2302 }, + {7205,7221,7204 ,7669,7545,5120 }, {3326,6346,6347 ,826,4883,451 }, + {8021,8051,8037 ,7365,6172,4528 }, {2280,2243,8053 ,3613,1993,7676 }, + {7571,7626,7570 ,5126,7438,5204 }, {2484,2485,2524 ,3499,2815,3395 }, + {6898,6870,2205 ,1946,2616,2874 }, {7599,7626,7571 ,5125,7438,5126 }, + {7513,7571,7512 ,3611,5126,5203 }, {7651,7625,7626 ,7675,7139,7438 }, + {7223,7222,7189 ,7492,628,7493 }, {2141,8101,8091 ,2015,383,2167 }, + {3862,7810,1494 ,550,552,1083 }, {14,302,223 ,1986,622,1985 }, + {91,14,223 ,1984,1986,1985 }, {7384,7359,7332 ,7683,7686,6685 }, + {7333,7384,7332 ,4353,7683,6685 }, {7411,7410,7359 ,3767,3766,7686 }, + {7384,7411,7359 ,7683,3767,7686 }, {7232,7248,7231 ,2124,73,6944 }, + {7691,7690,7666 ,4419,6466,7566 }, {7223,7264,7222 ,7492,626,628 }, + {7288,7263,7264 ,7577,627,626 }, {7798,7851,7825 ,4728,6625,3768 }, + {7850,7849,7824 ,3769,7551,3770 }, {7999,8022,7998 ,4583,4123,7209 }, + {7751,7750,7701 ,4332,7687,3515 }, {8040,8039,8001 ,7688,7476,7689 }, + {8002,8040,8001 ,3777,7688,7689 }, {1413,3912,6477 ,7659,7680,7671 }, + {7669,3863,7861 ,7681,7690,7682 }, {3423,7669,7861 ,7660,7681,7682 }, + {3863,3538,3633 ,7690,7691,7661 }, {7861,3863,3633 ,7682,7690,7661 }, + {3633,3538,5039 ,7661,7691,7672 }, {5039,4573,2733 ,7672,5314,5367 }, + {4303,85,166 ,3728,2504,3729 }, {7876,7927,7902 ,1725,1716,7692 }, + {7958,8007,7957 ,3855,3854,7385 }, {7706,7730,7657 ,7693,4413,7417 }, + {7955,8003,7979 ,4254,3776,3775 }, {7299,7275,6346 ,7694,1854,4883 }, + {7329,7328,7275 ,7695,7552,1854 }, {7299,7329,7275 ,7694,7695,1854 }, + {7329,7357,7328 ,7695,7597,7552 }, {7652,7651,7626 ,7696,7675,7438 }, + {8054,2280,8053 ,7402,3613,7676 }, {2326,2325,2280 ,3588,1994,3613 }, + {7730,7776,7729 ,4413,4412,7697 }, {8043,5558,5599 ,6397,2302,2213 }, + {4558,7918,4559 ,4209,5555,5237 }, {7754,7753,7704 ,7350,3643,3562 }, + {7399,7398,7346 ,7428,7429,7584 }, {7701,7700,7679 ,3515,7698,7674 }, + {7349,7375,7315 ,7609,7699,7496 }, {3402,1538,3400 ,3660,0,3659 }, + {1973,3862,1494 ,551,550,1083 }, {7597,7596,7534 ,7700,7701,7314 }, + {7534,7535,7481 ,7314,7517,7542 }, {7929,7977,7928 ,7684,1714,1724 }, + {7104,7117,7090 ,5280,7650,4080 }, {7091,7104,7090 ,4153,5280,4080 }, + {7656,7683,7603 ,7401,5267,5266 }, {7635,7613,7553 ,7646,5106,7655 }, + {7522,7521,7465 ,7395,7657,7581 }, {3895,3912,7837 ,4455,7680,5967 }, + {3912,7388,6477 ,7680,7702,7671 }, {7388,7669,6477 ,7702,7681,7671 }, + {3863,587,5039 ,7690,7703,7672 }, {3538,3863,5039 ,7691,7690,7672 }, + {587,3401,4573 ,7703,7704,5314 }, {5039,587,4573 ,7672,7703,5314 }, + {7462,7520,7495 ,4310,7658,4308 }, {7265,7290,7243 ,7497,7635,7632 }, + {7569,7597,7534 ,7705,7700,7314 }, {7407,7406,7357 ,7706,7538,7597 }, + {7407,7432,7406 ,7706,7554,7538 }, {7270,7292,7247 ,2555,7594,7360 }, + {1991,597,153 ,7707,791,663 }, {8105,8137,8107 ,3791,2174,658 }, + {7219,7261,7218 ,1529,1528,7708 }, {5523,8055,3121 ,7709,7710,2076 }, + {5644,3724,3615 ,4133,4132,201 }, {3724,5683,5682 ,4132,3939,3783 }, + {3615,3724,5682 ,201,4132,3783 }, {7346,7398,7345 ,7584,7429,7588 }, + {7464,7463,7412 ,4246,7711,3765 }, {7689,402,7712 ,6444,5349,5350 }, + {7130,7117,7104 ,6823,7650,5280 }, {7623,7677,7622 ,7427,7426,7460 }, + {7133,7165,7132 ,1989,7265,7621 }, {7144,7145,7170 ,7653,7629,7652 }, + {3634,4813,68 ,5507,4088,4087 }, {7334,3912,3895 ,7712,7680,4455 }, + {3376,7334,3895 ,4466,7712,4455 }, {7388,7584,7669 ,7702,7713,7681 }, + {7584,3863,7669 ,7713,7690,7681 }, {587,7583,3401 ,7703,7714,7704 }, + {7954,7953,7903 ,7715,7716,7717 }, {7904,7954,7903 ,7513,7715,7717 }, + {7139,7140,7161 ,1025,7415,2559 }, {7115,7116,7139 ,1024,7262,1025 }, + {7263,7262,7241 ,627,4335,4465 }, {7492,7490,7491 ,7718,7478,7604 }, + {5556,5555,5535 ,3511,6338,3512 }, {8023,8022,7999 ,4582,4123,4583 }, + {6431,5495,6522 ,7719,4134,7720 }, {3121,791,6658 ,2076,7643,7665 }, + {6658,791,1350 ,7665,7643,509 }, {1991,2654,597 ,7707,2773,791 }, + {1350,5570,6658 ,509,508,7665 }, {7715,3724,5495 ,7721,4132,4134 }, + {3338,4207,7739 ,4147,4247,7571 }, {7458,7516,7487 ,7550,5708,5707 }, + {7482,7481,7425 ,7722,7542,3995 }, {7722,7721,7676 ,7645,7454,7461 }, + {7677,7722,7676 ,7426,7645,7461 }, {7455,7514,7484 ,7579,3610,3612 }, + {7751,7801,7750 ,4332,1618,7687 }, {7244,7265,7243 ,7620,7497,7632 }, + {5026,4744,4859 ,4225,4424,5039 }, {2618,7334,3376 ,4454,7712,4466 }, + {7334,2618,3912 ,7712,4454,7680 }, {3912,2618,7388 ,7680,4454,7702 }, + {4546,3863,7584 ,7723,7690,7713 }, {4546,881,3863 ,7723,7724,7690 }, + {881,587,3863 ,7724,7703,7690 }, {7876,7902,7852 ,1725,7692,7412 }, + {8050,3783,4589 ,4529,5311,776 }, {382,1444,4207 ,3534,4035,4247 }, + {7721,7770,7720 ,7454,4272,4271 }, {7550,7549,7490 ,7725,7605,7478 }, + {7492,7550,7490 ,7718,7725,7478 }, {7824,7797,7747 ,3770,3506,331 }, + {7379,7404,7378 ,7430,7726,7431 }, {7116,7140,7139 ,7262,7415,1025 }, + {382,4207,1005 ,3534,4247,3535 }, {8059,8034,2275 ,4235,7586,3152 }, + {7689,7688,7613 ,6444,6443,5106 }, {7886,2296,2295 ,6561,2664,3160 }, + {3737,5495,5571 ,7642,4134,4616 }, {1218,3008,6891 ,3255,546,813 }, + {1517,1622,235 ,600,4238,598 }, {145,2694,2627 ,810,811,2776 }, + {3934,5683,3724 ,7727,3939,4132 }, {3934,3009,5683 ,7727,3747,3939 }, + {2419,2959,890 ,3353,4608,3283 }, {7647,7675,7696 ,4967,5192,7728 }, + {7495,7552,7494 ,4308,5546,4309 }, {7130,7143,7142 ,6823,7651,7729 }, + {8073,8085,2343 ,2567,2016,2685 }, {3693,4590,278 ,5347,1490,1489 }, + {7978,7977,7929 ,7730,1714,7684 }, {7876,7852,7853 ,1725,7412,1617 }, + {4819,3401,3139 ,5216,7704,5400 }, {6292,6282,6283 ,5403,6617,4298 }, + {2618,3682,7388 ,4454,7731,7702 }, {3682,7839,7388 ,7731,7732,7702 }, + {7388,7839,7584 ,7702,7732,7713 }, {881,6712,587 ,7724,7733,7703 }, + {6712,7911,7583 ,7733,7734,7714 }, {587,6712,7583 ,7703,7733,7714 }, + {7911,3139,3401 ,7734,5400,7704 }, {7583,7911,3401 ,7714,7734,7704 }, + {7771,7824,7770 ,4336,3770,4272 }, {7803,7854,7828 ,4636,7514,7735 }, + {7117,7142,7141 ,7650,7729,7414 }, {7485,7514,7456 ,7636,3610,7578 }, + {7849,7850,7824 ,7551,3769,3770 }, {7711,7710,7663 ,7736,7560,7559 }, + {7850,7849,7824 ,3769,7551,3770 }, {7664,7711,7663 ,7611,7736,7559 }, + {7675,7647,7696 ,5192,4967,7728 }, {5535,5519,5536 ,3512,6337,754 }, + {2296,7886,2264 ,2664,6561,4166 }, {818,2844,906 ,1018,1329,1019 }, + {3121,8055,791 ,2076,7710,7643 }, {3737,6522,5495 ,7642,7720,4134 }, + {4573,3401,4819 ,5314,7704,5216 }, {6169,6140,6141 ,2334,2333,2386 }, + {3934,5038,3009 ,7727,3628,3747 }, {4883,1388,4774 ,5538,6311,5332 }, + {7117,7130,7142 ,7650,6823,7729 }, {7375,7428,7374 ,7699,7670,4988 }, + {3693,278,3638 ,5347,1489,5950 }, {8001,8039,8023 ,7689,7476,4582 }, + {8001,8023,8000 ,7689,4582,4581 }, {7877,7928,7876 ,7549,1724,1725 }, + {5495,6431,7715 ,4134,7719,7721 }, {5080,7839,3682 ,7737,7732,7731 }, + {5080,7584,7839 ,7737,7713,7732 }, {5080,2307,7584 ,7737,7738,7713 }, + {7584,2307,4546 ,7713,7738,7723 }, {6712,3139,7911 ,7733,5400,7734 }, + {6797,8108,8123 ,2246,2287,2247 }, {7312,7345,7311 ,4333,7588,4334 }, + {7116,7117,7141 ,7262,7650,7414 }, {7825,7851,7798 ,3768,6625,4728 }, + {7933,7959,7908 ,7507,7607,7366 }, {3297,3296,7783 ,4203,4159,7616 }, + {7882,7933,7908 ,7487,7507,7366 }, {886,2379,213 ,3464,3238,3606 }, + {5519,7962,5536 ,6337,7739,754 }, {7962,5522,5536 ,7739,755,754 }, + {3702,4249,1185 ,1303,6136,6159 }, {5523,2708,8055 ,7709,7740,7710 }, + {2708,6437,791 ,7740,399,7643 }, {8055,2708,791 ,7710,7740,7643 }, + {3737,7785,6522 ,7642,7741,7720 }, {6431,1266,3724 ,7719,7742,4132 }, + {7715,6431,3724 ,7721,7719,4132 }, {1266,3739,3934 ,7742,7743,7727 }, + {3724,1266,3934 ,4132,7742,7727 }, {3702,1185,5520 ,1303,6159,1304 }, + {7725,7702,7703 ,7420,3513,7667 }, {7675,7719,7696 ,5192,6541,7728 }, + {7385,7384,7333 ,7630,7683,4353 }, {7625,7678,7623 ,7139,7425,7427 }, + {7373,7427,7399 ,4989,7536,7428 }, {7750,7772,7748 ,7687,7744,7644 }, + {3875,2618,3184 ,5845,4454,4453 }, {3875,3682,2618 ,5845,7731,4454 }, + {2307,3483,4546 ,7738,7745,7723 }, {3483,881,4546 ,7745,7724,7723 }, + {2277,3693,4572 ,3522,5347,5949 }, {7997,7996,7950 ,7210,7328,7211 }, + {2486,2527,2526 ,2814,2637,2636 }, {7803,7828,7775 ,4636,7735,7509 }, + {7376,7351,7402 ,7746,6587,6589 }, {3296,2265,7783 ,4159,2662,7616 }, + {7882,7908,7859 ,7487,7366,7598 }, {2264,7886,2265 ,4166,6561,2662 }, + {7912,5519,5493 ,7747,6337,6336 }, {5519,7912,7962 ,6337,7747,7739 }, + {7962,7984,5522 ,7739,7748,755 }, {2708,3914,791 ,7740,7749,7643 }, + {6437,2708,791 ,399,7740,7643 }, {3914,3790,3737 ,7749,399,7642 }, + {791,3914,3737 ,7643,7749,7642 }, {7785,7360,6522 ,7741,7750,7720 }, + {6522,7360,6431 ,7720,7750,7719 }, {3739,3910,3934 ,7743,7751,7727 }, + {3934,3910,5038 ,7727,7751,3628 }, {7597,7622,7596 ,7700,7460,7701 }, + {7625,7623,7624 ,7139,7427,7439 }, {7190,7189,7142 ,7633,7493,7729 }, + {7173,7210,7172 ,7264,7499,7405 }, {8039,8038,8023 ,7476,4121,4582 }, + {7540,7628,7602 ,5709,7528,5867 }, {7765,5080,3682 ,7752,7737,7731 }, + {3875,7765,3682 ,5845,7752,7731 }, {3483,1526,6712 ,7745,7753,7733 }, + {881,3483,6712 ,7724,7745,7733 }, {1526,3171,6712 ,7753,7754,7733 }, + {3171,3139,6712 ,7754,5400,7733 }, {3171,4846,3139 ,7754,4237,5400 }, + {7877,7876,7826 ,7549,1725,1616 }, {3295,3296,177 ,7565,4159,3618 }, + {3297,3313,3296 ,4203,4160,4159 }, {7912,7963,7962 ,7747,7755,7739 }, + {7962,7963,7984 ,7739,7755,7748 }, {7963,7985,5522 ,7755,7756,755 }, + {7984,7963,5522 ,7748,7755,755 }, {5522,7985,5523 ,755,7756,7709 }, + {3914,7467,3737 ,7749,7757,7642 }, {3790,3914,3737 ,399,7749,7642 }, + {3737,7467,7785 ,7642,7757,7741 }, {7360,1266,6431 ,7750,7742,7719 }, + {3910,3739,1266 ,7751,7743,7742 }, {3567,5038,3910 ,7758,3628,7751 }, + {3614,4839,5038 ,7759,5484,3628 }, {3567,3614,5038 ,7758,7759,3628 }, + {3614,5092,4839 ,7759,3953,5484 }, {7701,7724,7700 ,3515,7760,7698 }, + {7143,7190,7142 ,7651,7633,7729 }, {7288,7313,7263 ,7577,7583,627 }, + {7727,7726,7681 ,3563,7418,7459 }, {7682,7727,7681 ,3564,3563,7459 }, + {7580,7636,7553 ,7596,7567,7655 }, {7636,7666,7635 ,7567,7566,7646 }, + {7761,7765,3875 ,7761,7752,5845 }, {7761,5080,7765 ,7761,7737,7752 }, + {3639,2307,5080 ,7762,7738,7737 }, {7761,3639,5080 ,7761,7762,7737 }, + {7936,7935,7884 ,5528,5527,7391 }, {7777,7778,7807 ,4411,7601,7602 }, + {7607,7660,7606 ,7455,7381,7457 }, {5493,7888,7912 ,6336,7763,7747 }, + {5214,5215,5235 ,6272,6313,6385 }, {7985,8044,2708 ,7756,7764,7740 }, + {5523,7985,2708 ,7709,7756,7740 }, {3914,3188,7467 ,7749,7765,7757 }, + {7467,6482,7785 ,7757,7766,7741 }, {7785,6482,7360 ,7741,7766,7750 }, + {7360,6482,1266 ,7750,7766,7742 }, {4020,3910,1266 ,7767,7751,7742 }, + {2097,2165,2164 ,846,1041,3583 }, {7426,7482,7425 ,7537,7722,3995 }, + {7773,7802,7751 ,7668,6566,4332 }, {7573,7540,7602 ,2013,5709,5867 }, + {7151,7150,3293 ,1202,7488,1988 }, {7731,7778,7777 ,7516,7601,4411 }, + {7756,7731,7777 ,7768,7516,4411 }, {7685,7731,7756 ,7518,7516,7768 }, + {7212,7211,7173 ,7208,7500,7264 }, {6610,3483,2307 ,7769,7745,7738 }, + {3639,6610,2307 ,7762,7769,7738 }, {3483,6610,1526 ,7745,7769,7753 }, + {8067,8091,8083 ,2168,2167,2103 }, {7320,7319,7292 ,7282,7432,7594 }, + {7515,7540,7573 ,1079,5709,2013 }, {7830,7829,7806 ,7770,7614,7612 }, + {7636,7635,7553 ,7567,7646,7655 }, {7950,7996,7949 ,7211,7328,7591 }, + {7481,7480,7454 ,7542,7463,3996 }, {7397,7424,7371 ,1186,1188,3748 }, + {7888,7938,7912 ,7763,7771,7747 }, {7912,7938,7963 ,7747,7771,7755 }, + {7985,8027,8044 ,7756,7772,7764 }, {8044,8027,2708 ,7764,7772,7740 }, + {8027,3689,3914 ,7772,7773,7749 }, {2708,8027,3914 ,7740,7772,7749 }, + {6482,1939,1266 ,7766,7774,7742 }, {1939,7862,1266 ,7774,7775,7742 }, + {1266,7862,4020 ,7742,7775,7767 }, {4020,3567,3910 ,7767,7758,7751 }, + {3567,5092,3614 ,7758,3953,7759 }, {7600,7627,7572 ,7556,7582,5124 }, + {7145,7146,7162 ,7629,7530,7649 }, {7146,7147,7162 ,7530,6937,7649 }, + {7773,7751,7752 ,7668,4332,3514 }, {402,448,7759 ,5349,1172,399 }, + {7270,7320,7292 ,2555,7282,7594 }, {7403,7429,7457 ,6588,7572,1078 }, + {7174,7212,7173 ,7610,7208,7264 }, {7935,7934,7883 ,5527,7486,7452 }, + {7686,7685,7632 ,7553,7518,7555 }, {6483,7761,3875 ,3584,7761,5845 }, + {2096,6483,3875 ,788,3584,5845 }, {6610,3767,1526 ,7769,7776,7753 }, + {3767,3171,1526 ,7776,7754,7753 }, {3767,6412,4846 ,7776,7777,4237 }, + {3171,3767,4846 ,7754,7776,4237 }, {235,1622,4846 ,598,4238,4237 }, + {6412,235,4846 ,7777,598,4237 }, {1993,4075,342 ,4544,4521,4542 }, + {3405,3456,8086 ,1798,1145,4823 }, {7272,7271,7230 ,7666,2554,7498 }, + {7956,7981,7931 ,7403,4252,7508 }, {3136,2654,2548 ,2783,2773,2813 }, + {5479,7840,5493 ,5387,7778,6336 }, {2287,8007,99 ,3735,3854,3743 }, + {7840,7888,5493 ,7778,7763,6336 }, {7963,7938,7985 ,7755,7771,7756 }, + {8027,3723,3689 ,7772,7779,7773 }, {3723,3821,3914 ,7779,7780,7749 }, + {3689,3723,3914 ,7773,7779,7749 }, {3914,3821,3188 ,7749,7780,7765 }, + {3188,7787,7467 ,7765,7781,7757 }, {7467,7787,6482 ,7757,7781,7766 }, + {1939,3374,7862 ,7774,7782,7775 }, {3374,3586,4020 ,7782,7783,7767 }, + {7862,3374,4020 ,7775,7782,7767 }, {3586,3722,4020 ,7783,7784,7767 }, + {3823,3567,4020 ,7785,7758,7767 }, {3722,3823,4020 ,7784,7785,7767 }, + {3823,5092,3567 ,7785,3953,7758 }, {7189,7188,7141 ,7493,7503,7414 }, + {7242,7264,7223 ,7627,626,7492 }, {7658,7706,7657 ,7505,7693,7417 }, + {7554,7523,3351 ,7786,7393,1444 }, {7128,7127,7114 ,5391,7245,5392 }, + {7910,6483,2096 ,3582,3584,788 }, {2207,3639,7761 ,7787,7762,7761 }, + {6483,2207,7761 ,3584,7787,7761 }, {8086,3456,5771 ,4823,1145,2868 }, + {7352,7377,7403 ,7595,7592,6588 }, {7212,7230,7211 ,7208,7498,7500 }, + {7614,7582,7523 ,7788,7469,7393 }, {7554,7614,7523 ,7786,7788,7393 }, + {7276,6346,3326 ,3696,4883,826 }, {4555,4582,7716 ,5496,5478,5549 }, + {7840,7889,7888 ,7778,7789,7763 }, {7889,7913,7888 ,7789,7790,7763 }, + {7913,7939,7938 ,7790,7791,7771 }, {7888,7913,7938 ,7763,7790,7771 }, + {7939,7964,7938 ,7791,7792,7771 }, {7938,7964,7985 ,7771,7792,7756 }, + {7964,8010,7985 ,7792,7793,7756 }, {7985,8010,8027 ,7756,7793,7772 }, + {3723,1123,3821 ,7779,7794,7780 }, {3821,7784,7787 ,7780,7795,7781 }, + {3188,3821,7787 ,7765,7780,7781 }, {7787,7784,6482 ,7781,7795,7766 }, + {7784,1939,6482 ,7795,7774,7766 }, {3766,5092,3823 ,3954,3953,7785 }, + {7556,3766,3823 ,5116,3954,7785 }, {8133,8131,8107 ,2131,2307,658 }, + {7242,7288,7264 ,7627,7577,626 }, {8022,8021,7998 ,4123,7365,7209 }, + {7351,7352,7403 ,6587,7595,6588 }, {7377,7429,7403 ,7592,7572,6588 }, + {7276,7299,6346 ,3696,7694,4883 }, {7330,7329,7299 ,7796,7695,7694 }, + {7358,7357,7329 ,7797,7597,7695 }, {7330,7358,7329 ,7796,7797,7695 }, + {1973,1659,4685 ,551,5132,4894 }, {3896,6610,3639 ,7798,7769,7762 }, + {2207,3896,3639 ,7787,7798,7762 }, {3896,3767,6610 ,7798,7776,7769 }, + {7317,7318,7351 ,5947,7593,6587 }, {7250,7249,7232 ,3418,7622,2124 }, + {7233,7250,7232 ,2125,3418,2124 }, {7640,7641,4577 ,5351,5356,5352 }, + {6320,7439,7500 ,190,7297,326 }, {7500,7524,4595 ,326,1630,5289 }, + {4557,4558,4584 ,5310,4209,5308 }, {7433,7432,7407 ,7799,7554,7706 }, + {3766,4922,4585 ,3954,5256,3955 }, {5479,7814,7840 ,5387,6988,7778 }, + {7863,7864,7889 ,7800,7801,7789 }, {7840,7863,7889 ,7778,7800,7789 }, + {7864,7914,7913 ,7801,7802,7790 }, {7889,7864,7913 ,7789,7801,7790 }, + {7913,7914,7939 ,7790,7802,7791 }, {7964,7986,8010 ,7792,7803,7793 }, + {7986,8010,8027 ,7803,7793,7772 }, {8010,7986,8027 ,7793,7803,7772 }, + {8010,8045,3723 ,7793,7804,7779 }, {8027,8010,3723 ,7772,7793,7779 }, + {1123,7784,3821 ,7794,7795,7780 }, {7784,5023,1939 ,7795,7805,7774 }, + {5023,3374,1939 ,7805,7782,7774 }, {3374,3722,3586 ,7782,7784,7783 }, + {7556,3823,3722 ,5116,7785,7784 }, {3121,5522,5523 ,2076,755,7709 }, + {7653,7702,7652 ,7638,3513,7696 }, {7120,7119,7093 ,6938,7529,4318 }, + {7272,7321,7271 ,7666,7501,2554 }, {7208,7209,7226 ,1671,7523,6686 }, + {7433,7434,7432 ,7799,7806,7554 }, {7492,7491,7432 ,7718,7604,7554 }, + {7434,7492,7432 ,7806,7718,7554 }, {3830,3976,5483 ,5882,919,171 }, + {4961,2207,6483 ,2905,7787,3584 }, {6802,3767,3896 ,7807,7776,7798 }, + {6802,3842,3767 ,7807,7808,7776 }, {3767,3842,6412 ,7776,7808,7777 }, + {6412,3842,235 ,7777,7808,598 }, {2485,2486,2526 ,2815,2814,2636 }, + {7243,7242,7224 ,7632,7627,7491 }, {7318,7352,7351 ,7593,7595,6587 }, + {7488,7541,7517 ,7585,7515,7484 }, {7439,7468,7500 ,7297,7809,326 }, + {7468,7524,7500 ,7809,1630,326 }, {99,8007,2306 ,3743,3854,6405 }, + {7611,7610,7549 ,7810,7606,7605 }, {7550,7611,7549 ,7725,7810,7605 }, + {6706,7841,7840 ,5389,7811,7778 }, {7814,6706,7840 ,6988,5389,7778 }, + {7841,7864,7863 ,7811,7801,7800 }, {7840,7841,7863 ,7778,7811,7800 }, + {7939,7914,7964 ,7791,7802,7792 }, {8010,7986,8045 ,7793,7803,7804 }, + {8045,61,1123 ,7804,7812,7794 }, {3723,8045,1123 ,7779,7804,7794 }, + {1123,3512,7784 ,7794,7813,7795 }, {7784,7176,5023 ,7795,7814,7805 }, + {5023,3827,3374 ,7805,7815,7782 }, {3827,7817,3722 ,7815,7816,7784 }, + {3374,3827,3722 ,7782,7815,7784 }, {8009,7556,3722 ,7817,5116,7784 }, + {8022,8052,8021 ,4123,4122,7365 }, {8052,8051,8021 ,4122,6172,7365 }, + {7776,7806,7755 ,4412,7612,7519 }, {7209,7246,7225 ,7523,3772,1672 }, + {7226,7209,7225 ,6686,7523,1672 }, {7729,7755,7705 ,7697,7519,7349 }, + {7728,7729,7705 ,7818,7697,7349 }, {7248,7294,7323 ,73,7819,74 }, + {7611,7664,7610 ,7810,7611,7606 }, {3352,3378,3351 ,2724,1442,1444 }, + {3859,2207,4961 ,7820,7787,2905 }, {3859,3896,2207 ,7820,7798,7787 }, + {7268,7318,7317 ,3773,7593,5947 }, {6143,7057,6107 ,4394,7207,7232 }, + {6304,7439,3533 ,7233,7297,192 }, {7525,7524,7468 ,7821,1630,7809 }, + {7525,4575,7524 ,7821,3128,1630 }, {4589,3783,4638 ,776,5311,5154 }, + {2333,7711,7664 ,3537,7736,7611 }, {2333,7710,7711 ,3537,7560,7736 }, + {7841,7865,7864 ,7811,7822,7801 }, {7914,7940,7964 ,7802,7823,7792 }, + {7940,7987,7986 ,7823,7824,7803 }, {7964,7940,7986 ,7792,7823,7803 }, + {7986,8028,8045 ,7803,7825,7804 }, {61,1097,3512 ,7812,7826,7813 }, + {1123,61,3512 ,7794,7812,7813 }, {3512,1097,7784 ,7813,7826,7795 }, + {7176,6683,5023 ,7814,7827,7805 }, {5023,6683,3827 ,7805,7827,7815 }, + {3827,3692,7817 ,7815,7828,7816 }, {7817,7762,3722 ,7816,7829,7784 }, + {7762,8009,3722 ,7829,7817,7784 }, {6760,7556,8009 ,5117,5116,7817 }, + {7762,6760,8009 ,7829,5117,7817 }, {5522,3121,5558 ,755,2076,2302 }, + {7625,7624,7598 ,7139,7439,7140 }, {7600,7653,7627 ,7556,7638,7582 }, + {7653,7652,7627 ,7638,7696,7582 }, {7349,7401,7375 ,7609,7619,7699 }, + {7459,7458,7404 ,7485,7550,7726 }, {3314,3297,7710 ,3575,4203,7560 }, + {2333,3314,7710 ,3537,3575,7560 }, {1274,1316,1315 ,1687,1657,1627 }, + {2548,3896,3859 ,2813,7798,7820 }, {2654,6802,3896 ,2773,7807,7798 }, + {2548,2654,3896 ,2813,2773,7798 }, {7171,7209,7208 ,4306,7523,1671 }, + {7628,7681,7654 ,7528,7459,7637 }, {5112,6120,5113 ,6021,142,141 }, + {7057,6143,5936 ,7207,4394,4393 }, {6083,6304,6303 ,3989,7233,7261 }, + {7413,7439,6304 ,7830,7297,7233 }, {7469,7468,7439 ,7831,7809,7297 }, + {7413,7469,7439 ,7830,7831,7297 }, {7525,7585,4575 ,7821,6805,3128 }, + {7555,3351,3377 ,7832,1444,1443 }, {177,2265,3295 ,3618,2662,7565 }, + {6705,7815,6706 ,4158,5146,5389 }, {6706,7815,7841 ,5389,5146,7811 }, + {7865,7890,7864 ,7822,7833,7801 }, {7864,7890,7914 ,7801,7833,7802 }, + {7914,7890,7940 ,7802,7833,7823 }, {7986,7987,8028 ,7803,7824,7825 }, + {8028,8056,8045 ,7825,7834,7804 }, {8045,8056,61 ,7804,7834,7812 }, + {1097,7301,7784 ,7826,7835,7795 }, {7784,7301,7176 ,7795,7835,7814 }, + {7176,7301,6683 ,7814,7835,7827 }, {448,401,402 ,1172,1387,5349 }, + {7347,7399,7346 ,7576,7428,7584 }, {7514,7572,7513 ,3610,5124,3611 }, + {7657,7728,7705 ,7417,7818,7349 }, {7780,7834,7809 ,7389,7390,7449 }, + {7628,7654,7601 ,7528,7637,7544 }, {2834,3859,4961 ,2860,7820,2905 }, + {2834,2548,3859 ,2860,2813,7820 }, {1991,3842,6802 ,7707,7808,7807 }, + {2654,1991,6802 ,2773,7707,7807 }, {3842,1991,153 ,7808,7707,663 }, + {7834,7833,7809 ,7390,7392,7449 }, {7255,6167,6234 ,7238,5974,5973 }, + {7361,6304,6083 ,7836,7233,3989 }, {7361,7413,6304 ,7836,7830,7233 }, + {7469,7525,7468 ,7831,7821,7809 }, {7641,7640,7585 ,5356,5351,6805 }, + {7668,7667,7582 ,7590,7589,7469 }, {2285,2330,2329 ,3701,3693,3680 }, + {2165,2097,2143 ,1041,846,953 }, {7815,7842,7841 ,5146,7837,7811 }, + {7841,7842,7865 ,7811,7837,7822 }, {7890,7915,7940 ,7833,7838,7823 }, + {7987,8011,8028 ,7824,7839,7825 }, {8011,3003,8056 ,7839,7840,7834 }, + {8028,8011,8056 ,7825,7839,7834 }, {8056,3003,61 ,7834,7840,7812 }, + {7301,3416,6683 ,7835,7841,7827 }, {6683,3416,3827 ,7827,7841,7815 }, + {3416,3894,3827 ,7841,7842,7815 }, {3827,3894,3692 ,7815,7842,7828 }, + {3692,7762,7817 ,7828,7829,7816 }, {7760,6760,7762 ,7843,5117,7829 }, + {7760,2814,6760 ,7843,5255,5117 }, {2049,2096,2048 ,758,788,5461 }, + {6760,2814,4636 ,5117,5255,5118 }, {7902,7952,7901 ,7692,5475,7413 }, + {7119,7120,7147 ,7529,6938,6937 }, {7292,7319,7352 ,7594,7432,7595 }, + {7703,7702,7653 ,7667,3513,7638 }, {2834,2616,2548 ,2860,2812,2813 }, + {7625,7679,7624 ,7139,7674,7439 }, {7414,7413,7361 ,7844,7830,7836 }, + {7526,7525,7469 ,7845,7821,7831 }, {7526,7585,7525 ,7845,6805,7821 }, + {4583,4556,7788 ,5498,5497,7313 }, {7865,7842,7890 ,7822,7837,7833 }, + {7915,7965,7940 ,7838,7846,7823 }, {7940,7965,7987 ,7823,7846,7824 }, + {3301,1097,61 ,7847,7826,7812 }, {3003,3301,61 ,7840,7847,7812 }, + {1097,7811,7301 ,7826,7848,7835 }, {5035,7762,3692 ,7849,7829,7828 }, + {3894,5035,3692 ,7842,7849,7828 }, {5035,6846,7762 ,7849,7850,7829 }, + {401,2334,7712 ,1387,7851,5350 }, {7749,7750,7748 ,7663,7687,7644 }, + {7121,7132,7120 ,7852,7621,6938 }, {7132,7148,7120 ,7621,6936,6938 }, + {7829,7880,7857 ,7614,7531,7533 }, {7438,7437,3321 ,7351,5938,2461 }, + {7345,7372,7311 ,7588,4373,4334 }, {2484,7713,7668 ,3499,7489,7590 }, + {7949,7974,7925 ,7591,4171,7294 }, {6278,7106,7092 ,3967,7573,3968 }, + {7243,7289,7242 ,7632,7575,7627 }, {7596,7595,7533 ,7701,4019,7039 }, + {7140,7141,7161 ,7415,7414,2559 }, {7770,7824,7747 ,4272,3770,331 }, + {7291,7316,7350 ,3774,5948,7618 }, {7336,6083,6014 ,7853,3989,6592 }, + {7336,7361,6083 ,7853,7836,3989 }, {7470,7469,7413 ,7854,7831,7830 }, + {7414,7470,7413 ,7844,7854,7830 }, {7557,7585,7526 ,7855,6805,7845 }, + {7557,7616,7585 ,7855,7856,6805 }, {7616,7641,7585 ,7856,5356,6805 }, + {7658,7657,7605 ,7505,7417,5239 }, {7300,7299,7276 ,7857,7694,3696 }, + {3306,7300,7276 ,3695,7857,3696 }, {7868,4557,7818 ,7858,5310,5309 }, + {2281,2282,2327 ,3626,3663,3627 }, {6705,5439,7815 ,4158,4157,5146 }, + {7842,7866,7890 ,7837,7859,7833 }, {7866,7915,7890 ,7859,7838,7833 }, + {7965,7988,8011 ,7846,7860,7839 }, {7987,7965,8011 ,7824,7846,7839 }, + {3301,7812,7811 ,7847,7861,7848 }, {1097,3301,7811 ,7826,7847,7848 }, + {7811,7812,7301 ,7848,7861,7835 }, {6745,7762,6846 ,7862,7829,7850 }, + {7762,6745,7760 ,7829,7862,7843 }, {6745,3789,2814 ,7862,7863,5255 }, + {7760,6745,2814 ,7843,7862,5255 }, {3789,1973,4906 ,7863,551,4895 }, + {2814,3789,4906 ,5255,7863,4895 }, {7750,7800,7772 ,7687,7568,7744 }, + {7194,7193,7147 ,1670,7647,6937 }, {8006,8005,7956 ,7407,7527,7403 }, + {7300,7330,7299 ,7857,7796,7694 }, {7408,7407,7357 ,7864,7706,7597 }, + {7358,7408,7357 ,7797,7864,7597 }, {7434,7433,7407 ,7806,7799,7706 }, + {7408,7434,7407 ,7864,7806,7706 }, {7493,7492,7434 ,7865,7718,7806 }, + {7493,7550,7492 ,7865,7725,7718 }, {7362,7361,7336 ,7866,7836,7853 }, + {7362,7414,7361 ,7866,7844,7836 }, {7470,7526,7469 ,7854,7845,7831 }, + {7616,7671,7641 ,7856,7867,5356 }, {7641,7671,4579 ,5356,7867,5355 }, + {7815,5440,7842 ,5146,5147,7837 }, {7842,5440,5410 ,7837,5147,4285 }, + {7867,7916,7915 ,7868,7869,7838 }, {7866,7867,7915 ,7859,7868,7838 }, + {7916,7941,7915 ,7869,7870,7838 }, {7915,7941,7965 ,7838,7870,7846 }, + {7988,8029,3003 ,7860,7871,7840 }, {8011,7988,3003 ,7839,7860,7840 }, + {3003,4030,3301 ,7840,7872,7847 }, {7812,8026,7301 ,7861,7873,7835 }, + {8026,3991,3416 ,7873,7874,7841 }, {7301,8026,3416 ,7835,7873,7841 }, + {3991,3481,3416 ,7874,7875,7841 }, {3481,8042,3894 ,7875,399,7842 }, + {3416,3481,3894 ,7841,7875,7842 }, {3789,1852,1973 ,7863,7876,551 }, + {8036,4589,4588 ,7327,776,775 }, {7400,7455,7428 ,7580,7579,7670 }, + {7121,7120,7094 ,7852,6938,4317 }, {7957,8006,7956 ,7385,7407,7403 }, + {7859,7908,7858 ,7598,7366,7368 }, {7611,7687,7664 ,7810,7877,7611 }, + {7687,2333,7664 ,7877,3537,7611 }, {7302,6014,6259 ,7878,6592,6718 }, + {7302,7336,6014 ,7878,7853,6592 }, {7415,7414,7362 ,7879,7844,7866 }, + {7501,7526,7470 ,7880,7845,7854 }, {7558,7557,7526 ,7881,7855,7845 }, + {7501,7558,7526 ,7880,7881,7845 }, {7672,7671,7616 ,5450,7867,7856 }, + {7671,7672,4579 ,7867,5450,5355 }, {6531,8134,8120 ,2280,3263,906 }, + {7676,7720,7699 ,7461,4271,3951 }, {5410,7867,7866 ,4285,7868,7859 }, + {7842,5410,7866 ,7837,4285,7859 }, {7916,7942,7941 ,7869,7882,7870 }, + {7942,7966,7965 ,7882,7883,7846 }, {7941,7942,7965 ,7870,7882,7846 }, + {7966,7988,7965 ,7883,7860,7846 }, {8029,4622,4030 ,7871,7884,7872 }, + {3003,8029,4030 ,7840,7871,7872 }, {4030,4622,3301 ,7872,7884,7847 }, + {3481,6294,3894 ,7875,7885,7842 }, {8042,3481,3894 ,399,7875,7842 }, + {6294,7670,5035 ,7885,7886,7849 }, {3894,6294,5035 ,7842,7885,7849 }, + {936,3933,935 ,5234,7678,5264 }, {7400,7428,7375 ,7580,7670,7699 }, + {8006,2285,8005 ,7407,3701,7527 }, {7685,7756,7730 ,7518,7768,4413 }, + {7708,7707,7660 ,7388,7382,7381 }, {7857,7879,7856 ,7533,7532,4635 }, + {7884,7883,7833 ,7391,7452,7392 }, {7363,7362,7336 ,7887,7866,7853 }, + {7302,7363,7336 ,7878,7887,7853 }, {7440,7470,7414 ,7888,7854,7844 }, + {7415,7440,7414 ,7879,7888,7844 }, {7440,7501,7470 ,7888,7880,7854 }, + {7617,7616,7557 ,7889,7856,7855 }, {7558,7617,7557 ,7881,7889,7855 }, + {7685,7706,7658 ,7518,7693,7505 }, {7867,5410,5411 ,7868,4285,4284 }, + {7555,7554,3351 ,7832,7786,1444 }, {7949,7995,7974 ,7591,4135,4171 }, + {4264,7837,3740 ,5540,5967,7639 }, {7942,7989,7988 ,7882,7890,7860 }, + {7966,7942,7988 ,7883,7882,7860 }, {2950,3301,4622 ,7891,7847,7884 }, + {2950,7812,3301 ,7891,7861,7847 }, {7812,2950,8026 ,7861,7891,7873 }, + {3481,7670,6294 ,7875,7886,7885 }, {7670,6604,6846 ,7886,7892,7850 }, + {5035,7670,6846 ,7849,7886,7850 }, {6604,5042,6745 ,7892,7893,7862 }, + {6846,6604,6745 ,7850,7892,7862 }, {5042,7737,3789 ,7893,7894,7863 }, + {6745,5042,3789 ,7862,7893,7863 }, {7737,7080,1852 ,7894,7895,7876 }, + {3789,7737,1852 ,7863,7894,7876 }, {7080,7810,1973 ,7895,552,551 }, + {1852,7080,1973 ,7876,7895,551 }, {8004,8025,7980 ,7404,7510,4253 }, + {7685,7658,7631 ,7518,7505,7504 }, {7352,7319,7377 ,7595,7432,7592 }, + {7622,7650,7595 ,7460,7237,4019 }, {7730,7756,7777 ,4413,7768,4411 }, + {7295,7324,7323 ,7562,7617,74 }, {7706,7685,7730 ,7693,7518,4413 }, + {7294,7295,7323 ,7819,7562,74 }, {6234,6167,6259 ,5973,5974,6718 }, + {6167,7302,6259 ,5974,7878,6718 }, {7363,7415,7362 ,7887,7879,7866 }, + {7502,7501,7440 ,7896,7880,7888 }, {7502,7558,7501 ,7896,7881,7880 }, + {7617,7672,7616 ,7889,5450,7856 }, {1210,5017,4144 ,4739,5092,4761 }, + {4562,8057,1381 ,5018,7897,5193 }, {4839,4730,5038 ,5484,5438,3628 }, + {7867,5418,7916 ,7868,3471,7869 }, {7916,5418,7942 ,7869,3471,7882 }, + {7989,8012,7988 ,7890,7898,7860 }, {8030,8029,7988 ,7899,7871,7860 }, + {8012,8030,7988 ,7898,7899,7860 }, {8030,4622,8029 ,7899,7884,7871 }, + {2950,7466,8026 ,7891,7900,7873 }, {7466,3991,8026 ,7900,7874,7873 }, + {1312,3481,3991 ,7901,7875,7874 }, {7466,1312,3991 ,7900,7901,7874 }, + {3481,3937,7670 ,7875,7902,7886 }, {5042,7063,7737 ,7893,7903,7894 }, + {7737,7063,7080 ,7894,7903,7895 }, {7928,7977,7927 ,1724,1714,1716 }, + {7225,7267,7245 ,1672,4331,7506 }, {7958,7957,7932 ,3855,7385,7408 }, + {7632,7685,7631 ,7555,7518,7504 }, {7615,7614,7554 ,2816,7788,7786 }, + {7555,7615,7554 ,7832,2816,7786 }, {7615,7582,7614 ,2816,7469,7788 }, + {2485,7668,7582 ,2815,7590,7469 }, {7615,2485,7582 ,2816,2815,7469 }, + {8051,8060,8037 ,6172,7685,4528 }, {6319,6304,3533 ,6789,7233,192 }, + {7666,7690,7635 ,7566,6466,7646 }, {2485,2484,7668 ,2815,3499,7590 }, + {7303,7302,6167 ,7904,7878,5974 }, {7389,7415,7363 ,7905,7879,7887 }, + {7441,7440,7415 ,7906,7888,7879 }, {7389,7441,7415 ,7905,7906,7879 }, + {7559,7558,7502 ,7907,7881,7896 }, {7642,7672,7617 ,7908,5450,7889 }, + {7642,4555,7672 ,7908,5496,5450 }, {4555,7716,7672 ,5496,5549,5450 }, + {2484,2451,7713 ,3499,2102,7489 }, {5411,5418,7867 ,4284,3471,7868 }, + {7315,7375,7314 ,7496,7699,7909 }, {5418,7917,7942 ,3471,4920,7882 }, + {8012,7989,8030 ,7898,7890,7899 }, {3843,4622,8030 ,7677,7884,7899 }, + {7740,3937,3481 ,7910,7902,7875 }, {1312,7740,3481 ,7901,7910,7875 }, + {3937,7740,7670 ,7902,7910,7886 }, {7810,3862,1494 ,552,550,1083 }, + {5598,8043,5599 ,2216,6397,2213 }, {6706,6741,6705 ,5389,5388,4158 }, + {7267,7266,7245 ,4331,6769,7506 }, {7295,7294,7249 ,7562,7819,7622 }, + {3305,7300,3306 ,879,7857,3695 }, {7331,7330,7300 ,7911,7796,7857 }, + {7331,7358,7330 ,7911,7797,7796 }, {7824,7849,7797 ,3770,7551,3506 }, + {7337,7363,7302 ,7912,7887,7878 }, {7303,7337,7302 ,7904,7912,7878 }, + {7337,7389,7363 ,7912,7905,7887 }, {7441,7502,7440 ,7906,7896,7888 }, + {7586,7617,7558 ,7913,7889,7881 }, {7559,7586,7558 ,7907,7913,7881 }, + {7586,7642,7617 ,7913,7908,7889 }, {7409,7408,7358 ,6245,7864,7797 }, + {7435,7434,7408 ,6244,7806,7864 }, {7409,7435,7408 ,6245,6244,7864 }, + {7613,7688,7634 ,5106,6443,7474 }, {7917,7967,7942 ,4920,7914,7882 }, + {7942,7967,7989 ,7882,7914,7890 }, {3933,4622,3843 ,7678,7884,7677 }, + {3251,2950,4622 ,7915,7891,7884 }, {3933,3251,4622 ,7678,7915,7884 }, + {3251,7466,2950 ,7915,7900,7891 }, {7466,7498,1312 ,7900,7916,7901 }, + {7740,6054,7670 ,7910,7917,7886 }, {6054,7786,7670 ,7917,7918,7886 }, + {7670,7786,6604 ,7886,7918,7892 }, {7786,6845,5042 ,7918,7919,7893 }, + {6604,7786,5042 ,7892,7918,7893 }, {6845,6743,7063 ,7919,7920,7903 }, + {5042,6845,7063 ,7893,7919,7903 }, {6743,1351,7080 ,7920,1162,7895 }, + {7063,6743,7080 ,7903,7920,7895 }, {1351,8008,7810 ,1162,1163,552 }, + {7080,1351,7810 ,7895,1162,552 }, {7810,8008,3862 ,552,1163,550 }, + {4582,4581,7716 ,5478,5430,5549 }, {7534,7596,7533 ,7314,7701,7039 }, + {7676,7699,7650 ,7461,3951,7237 }, {7175,7212,7174 ,2347,7208,7610 }, + {7551,7550,7493 ,5108,7725,7865 }, {7255,7303,6167 ,7238,7904,5974 }, + {7390,7389,7337 ,7921,7905,7912 }, {7503,7502,7441 ,7922,7896,7906 }, + {7503,7559,7502 ,7922,7907,7896 }, {7643,7642,7586 ,7923,7908,7913 }, + {7612,7611,7550 ,5107,7810,7725 }, {7551,7612,7550 ,5108,5107,7725 }, + {860,1183,953 ,1385,1238,1237 }, {8013,7989,7967 ,7924,7890,7914 }, + {8013,8030,7989 ,7924,7899,7890 }, {7816,7466,3251 ,7925,7900,7915 }, + {6198,7498,7466 ,7926,7916,7900 }, {7816,6198,7466 ,7925,7926,7900 }, + {6198,1312,7498 ,7926,7901,7916 }, {8049,2317,8035 ,5393,3129,6675 }, + {7902,7901,7852 ,7692,7413,7412 }, {7175,7174,7151 ,2347,7610,1202 }, + {7665,7687,7611 ,7927,7877,7810 }, {2334,2333,7687 ,7851,3537,7877 }, + {5672,5718,5744 ,5324,6126,6090 }, {7304,7303,7255 ,7928,7904,7238 }, + {7304,7337,7303 ,7928,7912,7904 }, {7442,7441,7389 ,7929,7906,7905 }, + {7390,7442,7389 ,7921,7929,7905 }, {7527,7559,7503 ,7930,7907,7922 }, + {7527,7586,7559 ,7930,7913,7907 }, {7694,4555,7642 ,7931,5496,7908 }, + {7643,7694,7642 ,7923,7931,7908 }, {935,8030,8013 ,5264,7899,7924 }, + {4058,4055,1253 ,4708,3678,2353 }, {7816,3251,3933 ,7925,7915,7678 }, + {6198,7961,1312 ,7926,7932,7901 }, {3565,7740,1312 ,7933,7910,7901 }, + {7961,3565,1312 ,7932,7933,7901 }, {7740,3565,6054 ,7910,7933,7917 }, + {7248,7272,7231 ,73,7666,6944 }, {7291,7350,7349 ,3774,7618,7609 }, + {7350,7376,7349 ,7618,7746,7609 }, {7349,7376,7401 ,7609,7746,7619 }, + {7376,7402,7401 ,7746,6589,7619 }, {8050,8037,3783 ,4529,4528,5311 }, + {7401,7400,7375 ,7619,7580,7699 }, {7192,7191,7170 ,7648,7634,7652 }, + {4126,3141,4176 ,124,4701,4703 }, {6347,6346,7860 ,451,4883,452 }, + {5483,5261,3830 ,171,173,5882 }, {6282,6060,5115 ,6617,5914,5913 }, + {5109,5108,7166 ,4182,4184,5971 }, {1934,1418,22 ,4620,4619,4066 }, + {7891,7918,4558 ,7934,5555,4209 }, {2410,3670,489 ,1,281,3639 }, + {7256,7255,5845 ,7935,7238,143 }, {6120,7256,5845 ,142,7935,143 }, + {7338,7337,7304 ,7936,7912,7928 }, {7338,7390,7337 ,7936,7921,7912 }, + {7442,7503,7441 ,7929,7922,7906 }, {7587,7586,7527 ,7937,7913,7930 }, + {7587,7643,7586 ,7937,7923,7913 }, {7717,4556,4555 ,7938,5497,5496 }, + {7694,7717,4555 ,7931,7938,5496 }, {2317,2275,8035 ,3129,3152,6675 }, + {4558,4557,7891 ,4209,5310,7934 }, {17,15,438 ,5190,497,498 }, + {4423,3672,160 ,4289,4277,4295 }, {4680,160,3672 ,5368,4295,4277 }, + {974,7816,3933 ,5366,7925,7678 }, {936,974,3933 ,5234,5366,7678 }, + {3565,7961,6198 ,7933,7932,7926 }, {3565,6759,6054 ,7933,7939,7917 }, + {6054,6759,7786 ,7917,7939,7918 }, {6759,7738,6845 ,7939,7940,7919 }, + {7786,6759,6845 ,7918,7939,7919 }, {7738,1067,6743 ,7940,7941,7920 }, + {6845,7738,6743 ,7919,7940,7920 }, {1067,1352,1351 ,7941,1262,1162 }, + {6743,1067,1351 ,7920,7941,1162 }, {7627,7652,7626 ,7582,7696,7438 }, + {7315,7314,7265 ,7496,7909,7497 }, {7402,7403,7401 ,6589,6588,7619 }, + {7569,7534,7536 ,7705,7314,5205 }, {5106,7134,5108 ,5926,7942,4184 }, + {7166,7177,7197 ,5971,7943,7234 }, {7197,7177,6120 ,7234,7943,142 }, + {7277,7255,7256 ,7944,7238,7935 }, {7277,7304,7255 ,7944,7928,7238 }, + {7391,7390,7338 ,7945,7921,7936 }, {7471,7503,7442 ,7946,7922,7929 }, + {7528,7527,7503 ,7947,7930,7922 }, {7471,7528,7503 ,7946,7947,7922 }, + {7644,7643,7587 ,7948,7923,7937 }, {7717,7766,4556 ,7938,7949,5497 }, + {4556,7766,7788 ,5497,7949,7313 }, {7891,7868,7918 ,7934,7858,5555 }, + {7918,7968,4560 ,5555,7950,5236 }, {7607,7606,7578 ,7455,7457,7462 }, + {7805,7857,7804 ,7520,7533,4634 }, {7189,7222,7205 ,7493,628,7669 }, + {7652,7702,7651 ,7696,3513,7675 }, {1091,160,4680 ,4597,4295,5368 }, + {974,7335,7816 ,5366,4265,7925 }, {7335,6198,7816 ,4265,7926,7925 }, + {6759,1067,7738 ,7939,7941,7940 }, {7483,7482,7426 ,7574,7722,7537 }, + {7605,7604,7542 ,5239,7416,5240 }, {7210,7247,7228 ,7499,7360,7524 }, + {7207,7244,7206 ,7608,7620,7631 }, {7456,7403,7485 ,7578,6588,7636 }, + {7773,7827,7802 ,7668,7410,6566 }, {3303,7385,7333 ,1049,7630,4353 }, + {7094,7120,7093 ,4317,6938,4318 }, {7487,7458,7515 ,5707,7550,1079 }, + {3518,6133,7096 ,4338,4390,4337 }, {6133,5106,7096 ,4390,5926,4337 }, + {7152,5108,7134 ,7951,4184,7942 }, {7152,7166,5108 ,7951,5971,4184 }, + {7213,6120,7177 ,7952,142,7943 }, {7339,7338,7304 ,7953,7936,7928 }, + {7277,7339,7304 ,7944,7953,7928 }, {7416,7442,7390 ,7954,7929,7921 }, + {7391,7416,7390 ,7945,7954,7921 }, {7416,7471,7442 ,7954,7946,7929 }, + {7528,7587,7527 ,7947,7937,7930 }, {7673,7643,7644 ,7955,7923,7948 }, + {7673,7694,7643 ,7955,7931,7923 }, {7673,7717,7694 ,7955,7938,7931 }, + {7819,7818,7788 ,7956,5309,7313 }, {7766,7819,7788 ,7949,7956,7313 }, + {7919,7918,7868 ,7957,5555,7858 }, {7918,7919,7968 ,5555,7957,7950 }, + {4560,7968,4561 ,5236,7950,5361 }, {7857,7856,7804 ,7533,4635,4634 }, + {7430,7458,7487 ,7587,7550,5707 }, {4957,8062,5026 ,2733,3272,4225 }, + {1945,3565,6198 ,7958,7933,7926 }, {7335,1945,6198 ,4265,7958,7926 }, + {3565,1945,6759 ,7933,7958,7939 }, {1067,1353,1352 ,7941,1205,1262 }, + {7118,7131,7105 ,7628,7654,5279 }, {7801,7800,7750 ,1618,7568,7687 }, + {7401,7403,7456 ,7619,6588,7578 }, {7701,7750,7724 ,3515,7687,7760 }, + {7404,7430,7378 ,7726,7587,7431 }, {7429,7430,7487 ,7572,7587,5707 }, + {7132,7165,7164 ,7621,7265,7521 }, {7458,7487,7515 ,7550,5707,1079 }, + {7111,5106,6133 ,7959,5926,4390 }, {7178,7177,7166 ,7960,7943,5971 }, + {7152,7178,7166 ,7951,7960,5971 }, {7234,7256,6120 ,7961,7935,142 }, + {7213,7234,6120 ,7952,7961,142 }, {7234,7277,7256 ,7961,7944,7935 }, + {7339,7391,7338 ,7953,7945,7936 }, {7472,7471,7416 ,7962,7946,7954 }, + {7472,7528,7471 ,7962,7947,7946 }, {7588,7587,7528 ,7963,7937,7947 }, + {7588,7644,7587 ,7963,7948,7937 }, {7718,7717,7673 ,7964,7938,7955 }, + {7718,7766,7717 ,7964,7949,7938 }, {7819,7868,7818 ,7956,7858,5309 }, + {7943,7968,7919 ,7965,7950,7957 }, {7968,8014,4561 ,7950,7966,5361 }, + {7437,7465,7436 ,5938,7581,4245 }, {7188,7204,7187 ,7503,5120,7330 }, + {4582,4556,4583 ,5478,5497,5498 }, {3734,1945,7335 ,7967,7958,4265 }, + {1945,3448,6759 ,7958,7968,7939 }, {3448,5614,6759 ,7968,7969,7939 }, + {5614,1267,1067 ,7969,1353,7941 }, {6759,5614,1067 ,7939,7969,7941 }, + {1067,1267,1353 ,7941,1353,1205 }, {7750,7749,7723 ,7687,7663,7664 }, + {7724,7750,7723 ,7760,7687,7664 }, {7106,7118,7105 ,7573,7628,5279 }, + {7538,7602,7537 ,2014,5867,7543 }, {7403,7486,7485 ,6588,7436,7636 }, + {7927,7952,7902 ,1716,5475,7692 }, {7725,7773,7752 ,7420,7668,3514 }, + {7486,7457,7538 ,7436,1078,2014 }, {7545,7546,7578 ,7398,7397,7462 }, + {7123,7134,5106 ,7970,7942,5926 }, {7111,7123,5106 ,7959,7970,5926 }, + {7123,7152,7134 ,7970,7951,7942 }, {7178,7213,7177 ,7960,7952,7943 }, + {7278,7277,7234 ,7971,7944,7961 }, {7364,7391,7339 ,7972,7945,7953 }, + {7417,7416,7391 ,7973,7954,7945 }, {7364,7417,7391 ,7972,7973,7945 }, + {7529,7528,7472 ,7974,7947,7962 }, {7618,7644,7588 ,7975,7948,7963 }, + {7618,7673,7644 ,7975,7955,7948 }, {7767,7766,7718 ,7976,7949,7964 }, + {7869,7868,7819 ,7977,7858,7956 }, {7869,7919,7868 ,7977,7957,7858 }, + {7990,7968,7943 ,7978,7950,7965 }, {7968,7990,8014 ,7950,7978,7966 }, + {7990,8031,4561 ,7978,7979,5361 }, {8014,7990,4561 ,7966,7978,5361 }, + {4561,8031,4562 ,5361,7979,5018 }, {99,2306,100 ,3743,6405,2823 }, + {8051,278,3783 ,6172,1489,5311 }, {7518,7545,7544 ,7396,7398,7980 }, + {7456,7514,7455 ,7578,3610,7579 }, {7544,7545,7578 ,7980,7398,7462 }, + {7141,7188,7161 ,7414,7503,2559 }, {976,3734,7335 ,782,7967,4265 }, + {5614,7937,1267 ,7969,7981,1353 }, {7570,7598,7569 ,5204,7140,7705 }, + {7598,7624,7597 ,7140,7439,7700 }, {7679,7700,7678 ,7674,7698,7425 }, + {7780,7809,7779 ,7389,7449,7447 }, {7084,7111,6133 ,4391,7959,4390 }, + {7153,7152,7123 ,7982,7951,7970 }, {7214,7213,7178 ,7983,7952,7960 }, + {7235,7234,7213 ,7984,7961,7952 }, {7214,7235,7213 ,7983,7984,7952 }, + {7340,7339,7277 ,7985,7953,7944 }, {7278,7340,7277 ,7971,7985,7944 }, + {7340,7364,7339 ,7985,7972,7953 }, {7417,7472,7416 ,7973,7962,7954 }, + {7589,7588,7528 ,7986,7963,7947 }, {7529,7589,7528 ,7974,7986,7947 }, + {7589,7618,7588 ,7986,7975,7963 }, {7674,7673,7618 ,7987,7955,7975 }, + {7674,7718,7673 ,7987,7964,7955 }, {7789,7819,7766 ,7988,7956,7949 }, + {7767,7789,7766 ,7976,7988,7949 }, {7892,7919,7869 ,7989,7957,7977 }, + {7944,7943,7919 ,7990,7965,7957 }, {7892,7944,7919 ,7989,7990,7957 }, + {8032,8031,7990 ,7991,7979,7978 }, {8031,8032,4562 ,7979,7991,5018 }, + {8008,1428,3862 ,1163,1126,550 }, {8032,8057,4562 ,7991,7897,5018 }, + {7657,7729,7728 ,7417,7697,7818 }, {1029,3734,976 ,3930,7967,782 }, + {7499,1945,3734 ,7992,7958,7967 }, {1029,7499,3734 ,3930,7992,7967 }, + {7499,4297,3448 ,7992,7993,7968 }, {1945,7499,3448 ,7958,7992,7968 }, + {7624,7623,7597 ,7439,7427,7700 }, {7724,7723,7678 ,7760,7664,7425 }, + {7700,7724,7678 ,7698,7760,7425 }, {7131,7118,7144 ,7654,7628,7653 }, + {7486,7538,7485 ,7436,2014,7636 }, {7602,7601,7537 ,5867,7544,7543 }, + {7799,7798,7771 ,4727,4728,4336 }, {7853,7852,7800 ,1617,7412,7568 }, + {7485,7537,7514 ,7636,7543,3610 }, {7273,7325,7296 ,3467,7465,7471 }, + {7147,7163,7194 ,6937,5460,1670 }, {7097,7111,7084 ,7994,7959,4391 }, + {7097,7123,7111 ,7994,7970,7959 }, {7179,7178,7152 ,7995,7960,7951 }, + {7153,7179,7152 ,7982,7995,7951 }, {7235,7278,7234 ,7984,7971,7961 }, + {7365,7364,7340 ,7996,7972,7985 }, {7473,7472,7417 ,7997,7962,7973 }, + {7473,7529,7472 ,7997,7974,7962 }, {7619,7618,7589 ,7998,7975,7986 }, + {7695,7718,7674 ,7999,7964,7987 }, {7695,7767,7718 ,7999,7976,7964 }, + {7843,7819,7789 ,8000,7956,7988 }, {7843,7869,7819 ,8000,7977,7956 }, + {7843,7892,7869 ,8000,7989,7977 }, {7944,7990,7943 ,7990,7978,7965 }, + {8032,1381,8057 ,7991,5193,7897 }, {8025,2281,8024 ,7510,3626,7511 }, + {7355,7383,7325 ,7435,7512,7465 }, {7222,7221,7205 ,628,7545,7669 }, + {4578,4577,7641 ,5353,5352,5356 }, {4297,6695,5614 ,7993,8001,7969 }, + {3448,4297,5614 ,7968,7993,7969 }, {6695,7887,7937 ,8001,8002,7981 }, + {5614,6695,7937 ,7969,8001,7981 }, {7887,1314,1267 ,8002,1409,1353 }, + {7937,7887,1267 ,7981,8002,1353 }, {7722,7771,7721 ,7645,4336,7454 }, + {7598,7597,7569 ,7140,7700,7705 }, {7901,7951,7900 ,7413,7546,6922 }, + {7758,7780,7779 ,7387,7389,7447 }, {2487,7615,7555 ,4427,2816,7832 }, + {7085,7084,6186 ,8003,4391,4365 }, {7082,7085,6186 ,6462,8003,4365 }, + {7085,7097,7084 ,8003,7994,4391 }, {7124,7123,7097 ,8004,7970,7994 }, + {7124,7153,7123 ,8004,7982,7970 }, {7198,7178,7179 ,8005,7960,7995 }, + {7198,7214,7178 ,8005,7983,7960 }, {7279,7278,7235 ,8006,7971,7984 }, + {7279,7340,7278 ,8006,7985,7971 }, {7418,7417,7364 ,8007,7973,7972 }, + {7365,7418,7364 ,7996,8007,7972 }, {7504,7529,7473 ,8008,7974,7997 }, + {7504,7589,7529 ,8008,7986,7974 }, {7741,7767,7695 ,8009,7976,7999 }, + {7741,7789,7767 ,8009,7988,7976 }, {7893,7892,7843 ,8010,7989,8000 }, + {7991,7990,7944 ,8011,7978,7990 }, {7991,8032,7990 ,8011,7991,7978 }, + {8032,8058,1381 ,7991,8012,5193 }, {3304,7300,3305 ,1050,7857,879 }, + {1029,978,3671 ,3930,1620,3089 }, {5437,6705,6740 ,6259,4158,6894 }, + {1178,342,4075 ,206,4542,4521 }, {7679,7678,7625 ,7674,7425,7139 }, + {7570,7569,7536 ,5204,7705,5205 }, {7624,7679,7625 ,7439,7674,7139 }, + {7681,7703,7654 ,7459,7667,7637 }, {7827,7826,7802 ,7410,1616,6566 }, + {3304,7331,7300 ,1050,7911,7857 }, {7359,7358,7331 ,7686,7797,7911 }, + {7098,7097,7085 ,8013,7994,8003 }, {7154,7153,7124 ,8014,7982,8004 }, + {7154,7179,7153 ,8014,7995,7982 }, {7236,7235,7214 ,8015,7984,7983 }, + {7198,7236,7214 ,8005,8015,7983 }, {7305,7340,7279 ,8016,7985,8006 }, + {7305,7365,7340 ,8016,7996,7985 }, {7418,7473,7417 ,8007,7997,7973 }, + {7590,7589,7504 ,8017,7986,8008 }, {7790,7789,7741 ,8018,7988,8009 }, + {7790,7843,7789 ,8018,8000,7988 }, {7945,7944,7892 ,8019,7990,7989 }, + {7893,7945,7892 ,8010,8019,7989 }, {8015,8032,7991 ,8020,7991,8011 }, + {8015,8058,8032 ,8020,8012,7991 }, {213,2379,4163 ,3606,3238,4863 }, + {7850,7900,7849 ,3769,6922,7551 }, {7359,7409,7358 ,7686,6245,7797 }, + {7119,7146,7145 ,7529,7530,7629 }, {7854,7903,7878 ,7514,7717,7411 }, + {3671,7499,1029 ,3089,7992,3930 }, {3671,1077,4297 ,3089,2694,7993 }, + {7499,3671,4297 ,7992,3089,7993 }, {1077,308,4297 ,2694,8021,7993 }, + {4297,308,6695 ,7993,8021,8001 }, {308,4047,7887 ,8021,8022,8002 }, + {6695,308,7887 ,8001,8021,8002 }, {7887,4047,1314 ,8002,8022,1409 }, + {2403,3191,2165 ,2904,2903,1041 }, {7107,7118,7106 ,7494,7628,7573 }, + {8041,2281,8025 ,7548,3626,7510 }, {7494,7493,7434 ,4309,7865,7806 }, + {7435,7494,7434 ,6244,4309,7806 }, {8004,8041,8025 ,7404,7548,7510 }, + {5946,7085,7082 ,5481,8003,6462 }, {7125,7124,7097 ,8023,8004,7994 }, + {7098,7125,7097 ,8013,8023,7994 }, {7180,7179,7154 ,8024,7995,8014 }, + {7180,7198,7179 ,8024,8005,7995 }, {7280,7279,7235 ,8025,8006,7984 }, + {7236,7280,7235 ,8015,8025,7984 }, {7280,7305,7279 ,8025,8016,8006 }, + {7366,7365,7305 ,8026,7996,8016 }, {7366,7418,7365 ,8026,8007,7996 }, + {7443,7473,7418 ,8027,7997,8007 }, {7505,7504,7473 ,8028,8008,7997 }, + {7443,7505,7473 ,8027,8028,7997 }, {7505,7560,7504 ,8028,8029,8008 }, + {7560,7590,7504 ,8029,8017,8008 }, {7742,7741,7695 ,8030,8009,7999 }, + {7844,7843,7790 ,8031,8000,8018 }, {7844,7893,7843 ,8031,8010,8000 }, + {7945,7991,7944 ,8019,8011,7990 }, {8047,8058,8015 ,8032,8012,8020 }, + {8047,6921,1381 ,8032,8033,5193 }, {8058,8047,1381 ,8012,8032,5193 }, + {2487,7555,3377 ,4427,7832,1443 }, {7634,7611,7612 ,7474,7810,5107 }, + {7542,7604,7541 ,5240,7416,7515 }, {4047,7638,1314 ,8022,7540,1409 }, + {8030,935,3843 ,7899,5264,7677 }, {3298,4518,1879 ,7424,1281,1283 }, + {6277,7107,6278 ,4387,7494,3967 }, {7776,7830,7806 ,4412,7770,7612 }, + {7982,2306,8007 ,3853,6405,3854 }, {7634,7665,7611 ,7474,7927,7810 }, + {7688,7687,7665 ,6443,7877,7927 }, {7634,7688,7665 ,7474,6443,7927 }, + {7083,7085,5946 ,5483,8003,5481 }, {7083,7098,7085 ,5483,8013,8003 }, + {7155,7154,7124 ,8034,8014,8004 }, {7125,7155,7124 ,8023,8034,8004 }, + {7215,7198,7180 ,8035,8005,8024 }, {7199,7215,7180 ,8036,8035,8024 }, + {7257,7236,7198 ,8037,8015,8005 }, {7215,7257,7198 ,8035,8037,8005 }, + {7281,7280,7236 ,8038,8025,8015 }, {7257,7281,7236 ,8037,8038,8015 }, + {7306,7305,7280 ,8039,8016,8025 }, {7281,7306,7280 ,8038,8039,8025 }, + {7419,7418,7366 ,8040,8007,8026 }, {7419,7443,7418 ,8040,8027,8007 }, + {7505,7561,7560 ,8028,8041,8029 }, {7791,7790,7741 ,8042,8018,8009 }, + {7742,7791,7741 ,8030,8042,8009 }, {7894,7893,7844 ,8043,8010,8031 }, + {7969,7991,7945 ,8044,8011,8019 }, {8016,8015,7991 ,8045,8020,8011 }, + {7969,8016,7991 ,8044,8045,8011 }, {3785,6921,8047 ,8046,8033,8032 }, + {6921,1169,1381 ,8033,5100,5193 }, {3785,1169,6921 ,8046,5100,8033 }, + {3948,4296,303 ,280,6023,4086 }, {1583,1253,1606 ,5415,2353,3677 }, + {5493,5518,5492 ,6336,6316,6315 }, {7959,2306,7958 ,7607,6405,3855 }, + {5557,5556,5536 ,756,3511,754 }, {1315,1356,1355 ,1627,1530,1408 }, + {308,7638,4047 ,8021,7540,8022 }, {402,7759,448 ,5349,399,1172 }, + {7681,7725,7703 ,7459,7420,7667 }, {7908,7959,7907 ,7366,7607,7367 }, + {4368,68,1005 ,4148,4087,3535 }, {7326,7355,7325 ,7434,7435,7465 }, + {643,644,740 ,3285,1176,1595 }, {7099,7098,7083 ,8047,8013,5483 }, + {7181,7180,7154 ,8048,8024,8014 }, {7155,7181,7154 ,8034,8048,8014 }, + {7181,7199,7180 ,8048,8036,8024 }, {7181,7215,7199 ,8048,8035,8036 }, + {7258,7257,7215 ,8049,8037,8035 }, {7282,7281,7257 ,8050,8038,8037 }, + {7258,7282,7257 ,8049,8050,8037 }, {7282,7306,7281 ,8050,8039,8038 }, + {7282,7307,7306 ,8050,8051,8039 }, {7444,7443,7419 ,8052,8027,8040 }, + {7791,7844,7790 ,8042,8031,8018 }, {7920,7945,7893 ,8053,8019,8010 }, + {7894,7920,7893 ,8043,8053,8010 }, {7920,7969,7945 ,8053,8044,8019 }, + {8048,8047,8015 ,8054,8032,8020 }, {8016,8048,8015 ,8045,8054,8020 }, + {7546,7607,7578 ,7397,7455,7462 }, {3842,153,235 ,7808,663,598 }, + {7638,1274,1315 ,7540,1687,1627 }, {7639,308,1077 ,8055,8021,2694 }, + {1078,7639,1077 ,5060,8055,2694 }, {7639,2993,308 ,8055,3744,8021 }, + {308,2993,7638 ,8021,3744,7540 }, {2993,1274,7638 ,3744,1687,7540 }, + {7624,7625,7570 ,7439,7139,5204 }, {7244,7243,7206 ,7620,7632,7631 }, + {7463,7462,7410 ,7711,4310,3766 }, {7107,7106,6278 ,7494,7573,3967 }, + {7464,7462,7463 ,4246,4310,7711 }, {2306,7982,7958 ,6405,3853,3855 }, + {7126,7125,7098 ,8056,8023,8013 }, {7099,7126,7098 ,8047,8056,8013 }, + {7167,7181,7155 ,8057,8048,8034 }, {7237,7215,7181 ,8058,8035,8048 }, + {7237,7258,7215 ,8058,8049,8035 }, {7237,7282,7258 ,8058,8050,8049 }, + {7341,7307,7282 ,8059,8051,8050 }, {7307,7341,7282 ,8051,8059,8050 }, + {7367,7392,7366 ,8060,8061,8026 }, {7392,7419,7366 ,8061,8040,8026 }, + {7506,7505,7443 ,8062,8028,8027 }, {7444,7506,7443 ,8052,8062,8027 }, + {7506,7562,7561 ,8062,8063,8041 }, {7505,7506,7561 ,8028,8062,8041 }, + {7820,7844,7791 ,8064,8031,8042 }, {7820,7894,7844 ,8064,8043,8031 }, + {7970,7969,7920 ,8065,8044,8053 }, {7970,8016,7969 ,8065,8045,8044 }, + {3721,3785,8047 ,8066,8046,8032 }, {8048,3721,8047 ,8054,8066,8032 }, + {3721,1169,3785 ,8066,5100,8046 }, {4580,4579,7672 ,5410,5355,5450 }, + {3293,7149,7133 ,1988,7263,1989 }, {7542,7541,7488 ,5240,7515,7585 }, + {7959,7958,7907 ,7607,3855,7367 }, {7677,7676,7622 ,7426,7461,7460 }, + {5046,7639,1078 ,5232,8055,5060 }, {5046,2993,7639 ,5232,3744,8055 }, + {489,2955,2473 ,3639,3640,4303 }, {7571,7570,7512 ,5126,5204,5203 }, + {7807,7830,7776 ,7602,7770,4412 }, {7151,7174,7150 ,1202,7610,7488 }, + {7777,7807,7776 ,4411,7602,4412 }, {4557,7868,7891 ,5310,7858,7934 }, + {7776,7755,7729 ,4412,7519,7697 }, {5952,7099,7083 ,6595,8047,5483 }, + {7135,7155,7125 ,8067,8034,8023 }, {7126,7135,7125 ,8056,8067,8023 }, + {7156,7167,7155 ,8068,8057,8034 }, {7135,7156,7155 ,8067,8068,8034 }, + {7167,7156,7181 ,8057,8068,8048 }, {7156,7200,7181 ,8068,8069,8048 }, + {7200,7216,7181 ,8069,8070,8048 }, {7216,7237,7181 ,8070,8058,8048 }, + {7283,7282,7237 ,8071,8050,8058 }, {7283,7307,7282 ,8071,8051,8050 }, + {7307,7368,7341 ,8051,8072,8059 }, {7392,7444,7419 ,8061,8052,8040 }, + {7870,7894,7820 ,8073,8043,8064 }, {7921,7920,7894 ,8074,8053,8043 }, + {7870,7921,7894 ,8073,8074,8043 }, {8017,8016,7970 ,8075,8045,8065 }, + {4563,1169,3721 ,3024,5100,8066 }, {7484,7513,7483 ,3612,3611,7574 }, + {7596,7622,7595 ,7701,7460,4019 }, {7404,7458,7430 ,7726,7550,7587 }, + {7231,7230,7212 ,6944,7498,7208 }, {2243,6552,8053 ,1993,7679,7676 }, + {489,1101,2410 ,3639,2,1 }, {7100,7099,5952 ,8076,8047,6595 }, + {7100,7112,7099 ,8076,8077,8047 }, {7112,7126,7099 ,8077,8056,8047 }, + {7156,7135,7126 ,8068,8067,8056 }, {7135,7156,7126 ,8067,8068,8056 }, + {7156,7201,7200 ,8068,8078,8069 }, {7201,7216,7200 ,8078,8070,8069 }, + {7238,7237,7216 ,8079,8058,8070 }, {7201,7238,7216 ,8078,8079,8070 }, + {7308,7307,7283 ,8080,8051,8071 }, {7308,7368,7307 ,8080,8072,8051 }, + {7445,7444,7392 ,8081,8052,8061 }, {7445,7506,7444 ,8081,8062,8052 }, + {7506,7563,7562 ,8062,8082,8063 }, {7821,7870,7820 ,8083,8073,8064 }, + {7921,7970,7920 ,8074,8065,8053 }, {8033,8048,8016 ,8084,8054,8045 }, + {8017,8033,8016 ,8075,8084,8045 }, {7332,7331,3304 ,6685,7911,1050 }, + {7332,7359,7331 ,6685,7686,7911 }, {7464,7521,7462 ,4246,7657,4310 }, + {7196,7212,7175 ,2401,7208,2347 }, {6108,1274,2993 ,6975,1687,3744 }, + {1172,6108,2993 ,1758,6975,3744 }, {7427,7483,7426 ,7536,7574,7537 }, + {5107,5121,5106 ,4183,5855,5926 }, {7410,7409,7359 ,3766,6245,7686 }, + {7136,7135,7126 ,8085,8067,8056 }, {7112,7136,7126 ,8077,8085,8056 }, + {7136,7157,7156 ,8085,8086,8068 }, {7135,7136,7156 ,8067,8085,8068 }, + {7259,7237,7238 ,8087,8058,8079 }, {7259,7283,7237 ,8087,8071,8058 }, + {7369,7368,7308 ,8088,8072,8080 }, {7369,7393,7368 ,8088,8089,8072 }, + {7474,7506,7445 ,8090,8062,8081 }, {7474,7564,7563 ,8090,8091,8082 }, + {7506,7474,7563 ,8062,8090,8082 }, {7871,7870,7821 ,8092,8073,8083 }, + {7971,7970,7921 ,8093,8065,8074 }, {7971,8017,7970 ,8093,8075,8065 }, + {6340,8048,8033 ,8094,8054,8084 }, {6340,3721,8048 ,8094,8066,8054 }, + {2315,4563,3721 ,3068,3024,8066 }, {6340,2315,3721 ,8094,3068,8066 }, + {7521,7520,7462 ,7657,7658,4310 }, {7828,7827,7773 ,7735,7410,7668 }, + {7412,7463,7410 ,3765,7711,3766 }, {7723,7748,7722 ,7664,7644,7645 }, + {7953,7978,7929 ,7716,7730,7684 }, {7133,7132,7108 ,1989,7621,4111 }, + {7632,7631,7605 ,7555,7504,5239 }, {7552,7551,7493 ,5546,5108,7865 }, + {7494,7552,7493 ,4309,5546,7865 }, {7086,7100,5952 ,6743,8076,6595 }, + {7202,7201,7156 ,8095,8078,8068 }, {7157,7202,7156 ,8086,8095,8068 }, + {7309,7308,7283 ,8096,8080,8071 }, {7259,7309,7283 ,8087,8096,8071 }, + {7394,7393,7369 ,8097,8089,8088 }, {7420,7445,7392 ,8098,8081,8061 }, + {7922,7921,7870 ,8099,8074,8073 }, {7871,7922,7870 ,8092,8099,8073 }, + {7992,8017,7971 ,8100,8075,8093 }, {7992,8033,8017 ,8100,8084,8075 }, + {8024,8040,8002 ,7511,7688,3777 }, {7774,7828,7773 ,7419,7735,7668 }, + {7852,7851,7825 ,7412,6625,3768 }, {2293,2292,2262 ,2561,1983,3359 }, + {7723,7722,7677 ,7664,7645,7426 }, {7678,7723,7677 ,7425,7664,7426 }, + {7314,7374,7348 ,7909,4988,4990 }, {7145,7162,7170 ,7629,7649,7652 }, + {7903,7953,7929 ,7717,7716,7684 }, {8024,8002,8003 ,7511,3777,3776 }, + {8024,8053,8040 ,7511,7676,7688 }, {7576,7632,7605 ,7615,7555,5239 }, + {7688,7712,2334 ,6443,5350,7851 }, {7687,7688,2334 ,7877,6443,7851 }, + {7113,7112,7100 ,8101,8077,8076 }, {7086,7113,7100 ,6743,8101,8076 }, + {7217,7238,7201 ,8102,8079,8078 }, {7202,7217,7201 ,8095,8102,8078 }, + {7217,7259,7238 ,8102,8087,8079 }, {7309,7369,7308 ,8096,8088,8080 }, + {7446,7445,7420 ,8103,8081,8098 }, {7475,7474,7445 ,8104,8090,8081 }, + {7446,7475,7445 ,8103,8104,8081 }, {7474,7530,7564 ,8090,8105,8091 }, + {7822,7871,7821 ,8106,8092,8083 }, {7922,7971,7921 ,8099,8093,8074 }, + {8034,8033,7992 ,7586,8084,8100 }, {8034,6340,8033 ,7586,8094,8084 }, + {2369,2334,401 ,3257,7851,1387 }, {7730,7729,7657 ,4413,7697,7417 }, + {7903,7929,7878 ,7717,7684,7411 }, {7314,7348,7290 ,7909,4990,7635 }, + {7348,7373,7347 ,4990,4989,7576 }, {7800,7852,7825 ,7568,7412,3768 }, + {7772,7748,7722 ,7744,7644,7645 }, {7748,7772,7722 ,7644,7744,7645 }, + {7455,7427,7428 ,7579,7536,7670 }, {7512,7536,7482 ,5203,5205,7722 }, + {7904,7903,7854 ,7513,7717,7514 }, {7165,7172,7195 ,7265,7405,7522 }, + {7576,7605,7543 ,7615,5239,5238 }, {7249,7294,7248 ,7622,7819,73 }, + {7249,7248,7232 ,7622,73,2124 }, {7623,7622,7597 ,7427,7460,7700 }, + {7137,7136,7112 ,8107,8085,8077 }, {7113,7137,7112 ,8101,8107,8077 }, + {7137,7158,7157 ,8107,8108,8086 }, {7136,7137,7157 ,8085,8107,8086 }, + {7158,7182,7157 ,8108,8109,8086 }, {7182,7202,7157 ,8109,8095,8086 }, + {7260,7259,7217 ,8110,8087,8102 }, {7342,7369,7309 ,8111,8088,8096 }, + {7342,7394,7369 ,8111,8097,8088 }, {7531,7530,7474 ,8112,8105,8090 }, + {7475,7531,7474 ,8104,8112,8090 }, {7530,7531,7564 ,8105,8112,8091 }, + {7872,7871,7822 ,8113,8092,8106 }, {7946,7971,7922 ,8114,8093,8099 }, + {7993,7992,7971 ,8115,8100,8093 }, {7946,7993,7971 ,8114,8115,8093 }, + {8059,6340,8034 ,4235,8094,7586 }, {2316,2315,6340 ,3067,3068,8094 }, + {8059,2316,6340 ,4235,3067,8094 }, {2333,2334,2369 ,3537,7851,3257 }, + {7326,7325,7273 ,7434,7465,3467 }, {7425,7481,7454 ,3995,7542,3996 }, + {7231,7272,7230 ,6944,7666,7498 }, {7269,7247,7292 ,7361,7360,7594 }, + {7455,7484,7427 ,7579,3612,7536 }, {8060,8051,3783 ,7685,6172,5311 }, + {7800,7799,7748 ,7568,4727,7644 }, {7772,7800,7748 ,7744,7568,7644 }, + {7483,7512,7482 ,7574,5203,7722 }, {7375,7374,7314 ,7699,4988,7909 }, + {7209,7210,7228 ,7523,7499,7524 }, {7164,7165,7195 ,7521,7265,7522 }, + {7172,7210,7209 ,7405,7499,7523 }, {7195,7172,7209 ,7522,7405,7523 }, + {3321,7386,7387 ,2461,5512,3615 }, {7158,7183,7182 ,8108,8116,8109 }, + {7183,7202,7182 ,8116,8095,8109 }, {7218,7217,7202 ,7708,8102,8095 }, + {7183,7218,7202 ,8116,7708,8095 }, {7284,7309,7259 ,8117,8096,8087 }, + {7260,7284,7259 ,8110,8117,8087 }, {7284,7342,7309 ,8117,8111,8096 }, + {7395,7394,7342 ,8118,8097,8111 }, {7395,7421,7394 ,8118,8119,8097 }, + {7447,7475,7446 ,8120,8104,8103 }, {7591,7564,7531 ,8121,8091,8112 }, + {7895,7871,7872 ,8122,8092,8113 }, {7895,7922,7871 ,8122,8099,8092 }, + {7895,7946,7922 ,8122,8114,8099 }, {8035,8034,7992 ,6675,7586,8100 }, + {7993,8035,7992 ,8115,6675,8100 }, {7629,7705,7684 ,7399,7349,7400 }, + {7692,7691,7666 ,7490,4419,7566 }, {7637,7692,7666 ,7473,7490,7566 }, + {7690,7691,449 ,6466,4419,1169 }, {7929,7928,7877 ,7684,1724,7549 }, + {7378,7377,7319 ,7431,7592,7432 }, {7828,7854,7827 ,7735,7514,7410 }, + {278,4612,3783 ,1489,5249,5311 }, {7800,7825,7799 ,7568,3768,4727 }, + {7572,7571,7513 ,5124,5126,3611 }, {7265,7314,7290 ,7497,7909,7635 }, + {7386,7385,3303 ,5512,7630,1049 }, {7398,7397,7345 ,7429,1186,7588 }, + {7350,7317,7376 ,7618,5947,7746 }, {7317,7351,7376 ,5947,6587,7746 }, + {7114,7113,7086 ,5392,8101,6743 }, {7087,7114,7086 ,6742,5392,6743 }, + {7218,7260,7217 ,7708,8110,8102 }, {7343,7342,7284 ,2750,8111,8117 }, + {7476,7475,7447 ,8123,8104,8120 }, {7592,7591,7531 ,8124,8121,8112 }, + {7793,7822,7792 ,5726,8106,8125 }, {7793,7845,7822 ,5726,5725,8106 }, + {7845,7872,7822 ,5725,8113,8106 }, {7947,7946,7895 ,4354,8114,8122 }, + {7947,7993,7946 ,4354,8115,8114 }, {5644,3615,5660 ,4133,201,3054 }, + {7108,7121,7094 ,4111,7852,4317 }, {7108,7132,7121 ,4111,7621,7852 }, + {7601,7653,7600 ,7544,7638,7556 }, {7523,7522,7496 ,7393,7395,7470 }, + {7775,7828,7774 ,7509,7735,7419 }, {7480,7534,7511 ,7463,7314,6677 }, + {7348,7347,7289 ,4990,7576,7575 }, {7651,7702,7680 ,7675,3513,7673 }, + {7702,7701,7680 ,3513,3515,7673 }, {8053,6552,6784 ,7676,7679,7472 }, + {8053,6784,8039 ,7676,7472,7476 }, {8040,8053,8039 ,7688,7676,7476 }, + {7775,7774,7726 ,7509,7419,7418 }, {7378,7430,7377 ,7431,7587,7592 }, + {7095,7108,7094 ,3924,4111,4317 }, {6552,2243,2278 ,7679,1993,3495 }, + {7114,7127,7137 ,5392,7245,8107 }, {7113,7114,7137 ,8101,5392,8107 }, + {7127,7159,7158 ,7245,5075,8108 }, {7137,7127,7158 ,8107,7245,8108 }, + {7184,7183,7158 ,5074,8116,8108 }, {7159,7184,7158 ,5075,5074,8108 }, + {7261,7260,7218 ,1528,8110,7708 }, {7261,7284,7260 ,1528,8117,8110 }, + {7396,7395,7342 ,6716,8118,8111 }, {7343,7396,7342 ,2750,6716,8111 }, + {7507,7531,7475 ,6706,8112,8104 }, {7476,7507,7475 ,8123,6706,8104 }, + {7593,7592,7531 ,5032,8124,8112 }, {7896,7895,7872 ,4355,8122,8113 }, + {7845,7896,7872 ,5725,4355,8113 }, {7994,7993,7947 ,5582,8115,4354 }, + {7142,7189,7141 ,7729,7493,7414 }, {7725,7752,7702 ,7420,3514,3513 }, + {7682,7681,7628 ,3564,7459,7528 }, {7412,7411,7384 ,3765,3767,7683 }, + {7952,7951,7901 ,5475,7546,7413 }, {7572,7626,7599 ,5124,7438,5125 }, + {7190,7224,7189 ,7633,7491,7493 }, {7482,7536,7481 ,7722,5205,7542 }, + {7536,7534,7481 ,5205,7314,7542 }, {7660,7659,7606 ,7381,7383,7457 }, + {7088,7101,7087 ,4299,4073,6742 }, {7101,7114,7087 ,4073,5392,6742 }, + {7219,7218,7183 ,1529,7708,8116 }, {7184,7219,7183 ,5074,1529,8116 }, + {7285,7284,7261 ,2751,8117,1528 }, {7285,7343,7284 ,2751,2750,8117 }, + {7422,7395,7396 ,1404,8118,6716 }, {7449,7448,7395 ,7600,8126,8118 }, + {7422,7449,7395 ,1404,7600,8118 }, {7449,7477,7448 ,7600,1403,8126 }, + {7565,7531,7507 ,5080,8112,6706 }, {7531,7565,7593 ,8112,5080,5032 }, + {7565,7593,7592 ,5080,5032,8124 }, {7593,7565,7592 ,5032,5080,8124 }, + {7593,7645,7592 ,5032,5034,8124 }, {7896,7947,7895 ,4355,4354,8122 }, + {8018,8035,7993 ,6676,6675,8115 }, {7994,8018,7993 ,5582,6676,8115 }, + {5658,5642,5659 ,49,526,3053 } +}; +F32 vertices [8146][3] = { +{-0.128951f,0.113893f,0.0385904f},{-0.183541f,0.121141f,0.0176007f},{-0.3356f,0.267483f,0.245606f}, +{0.0990868f,0.131443f,0.150773f},{-0.0330697f,0.108447f,0.0577602f},{-0.0525675f,0.103219f,0.0581332f}, +{-0.129472f,0.111128f,0.0174721f},{0.321716f,0.0172985f,0.188251f},{0.350352f,0.00204496f,0.112787f}, +{-0.0496094f,0.0212148f,0.245825f},{0.284245f,0.0735475f,0.150304f},{-0.296733f,0.306228f,-0.0948973f}, +{0.364127f,-0.0330021f,0.152767f},{0.399804f,-0.11262f,0.0755796f},{-0.10546f,0.41535f,-0.00363974f}, +{-0.407308f,0.165107f,0.333295f},{-0.354815f,0.10291f,0.350047f},{-0.415655f,0.1722f,0.323829f}, +{0.154706f,0.137848f,0.112556f},{0.363426f,-0.0163596f,0.0577281f},{0.344481f,-0.0712067f,-0.05722f}, +{-0.47357f,0.192405f,0.152844f},{-0.433269f,-0.00220573f,0.173326f},{-0.336095f,0.297592f,0.112524f}, +{-0.431192f,0.27075f,0.192f},{0.136655f,0.102968f,0.212456f},{0.172493f,0.0906273f,0.222398f}, +{-0.0907206f,0.100833f,0.0403717f},{-0.212472f,0.206855f,0.0920485f},{-0.390158f,0.166136f,0.336426f}, +{0.0818719f,0.1177f,0.189036f},{-0.181875f,0.080962f,0.174573f},{-0.396981f,-0.110421f,-0.0611684f}, +{0.263654f,0.0893219f,0.152702f},{-0.469351f,0.0768914f,0.0379088f},{0.367586f,-0.0504292f,-0.00258511f}, +{0.373245f,-0.0554837f,0.00500307f},{-0.449931f,0.0420887f,0.0196393f},{-0.104874f,0.430758f,-0.0208675f}, +{-0.392389f,0.230186f,0.285811f},{-0.0333784f,0.0170798f,0.258757f},{-0.0197004f,0.434816f,-0.137706f}, +{-0.131067f,0.378811f,-0.0749943f},{-0.091563f,0.102653f,0.0566477f},{0.175284f,0.113482f,0.190798f}, +{-0.190955f,0.149211f,0.11763f},{-0.0545288f,0.10055f,0.0386676f},{0.216376f,0.0805312f,0.210058f}, +{0.231906f,0.0788913f,0.208501f},{0.328282f,0.00609625f,0.189769f},{0.313646f,0.0244043f,0.192785f}, +{-0.149188f,0.359988f,0.0320504f},{-0.466033f,0.134156f,0.0375229f},{-0.234677f,0.209048f,0.17485f}, +{0.385283f,-0.0772258f,0.133456f},{0.382538f,-0.0547828f,0.0755796f},{0.370396f,-0.091817f,0.193274f}, +{-0.244857f,0.283997f,0.11408f},{-0.047275f,0.301502f,-0.0171956f},{-0.337446f,0.231304f,0.282833f}, +{-0.358197f,0.206437f,0.307669f},{-0.338095f,-0.305122f,0.135758f},{-0.470593f,0.0595544f,0.0379023f}, +{-0.377309f,0.116614f,0.351275f},{-0.30036f,0.324536f,-0.112678f},{-0.115717f,0.107591f,0.0241086f}, +{-0.190904f,0.150715f,0.0987171f},{-0.335529f,0.291643f,0.0379345f},{-0.484316f,0.0419215f,0.264172f}, +{0.152538f,0.102801f,0.211254f},{0.153812f,0.0916176f,0.225215f},{-0.103138f,0.104698f,0.03515f}, +{-0.297048f,0.295598f,0.0953281f},{0.30371f,0.0399987f,0.18802f},{-0.0883798f,0.41845f,-0.0178644f}, +{0.363773f,-0.092788f,-0.0374522f},{-0.352076f,0.35386f,-0.0974052f},{0.0567345f,0.0797466f,0.236108f}, +{0.294335f,-0.114228f,0.291643f},{0.0244911f,0.341809f,-0.0740619f},{-0.166352f,0.361223f,0.0361275f}, +{-0.0908749f,0.0966914f,0.0203724f},{-0.166281f,0.380907f,0.020636f},{-0.0159899f,0.0208225f,0.260622f}, +{-0.474798f,0.157956f,0.154394f},{-0.395045f,0.398315f,-0.246642f},{-0.390222f,0.11662f,0.352786f}, +{-0.444934f,-0.0373236f,0.0384554f},{-0.215192f,0.210083f,0.100627f},{0.345137f,0.00202567f,0.153461f}, +{0.308855f,-0.0536767f,0.250738f},{-0.108379f,0.398566f,-0.000488712f},{-0.377316f,0.201955f,0.313469f}, +{-0.375985f,0.234179f,0.285843f},{-0.145947f,0.392849f,0.0136523f},{0.358667f,-0.0149577f,0.13352f}, +{-0.436697f,-0.00081026f,0.0371307f},{0.399283f,-0.148799f,0.133494f},{0.212189f,0.114485f,0.152053f}, +{0.103035f,-0.451593f,0.311926f},{0.120237f,-0.454622f,0.308067f},{0.00337286f,0.115282f,0.039375f}, +{-0.222993f,0.303122f,0.0742677f},{-0.475924f,0.161339f,0.133758f},{-0.120218f,0.428983f,-0.0409697f}, +{0.369708f,-0.0305328f,0.035433f},{-0.482419f,0.11327f,0.230475f},{0.367676f,-0.0728915f,-0.0214784f}, +{0.377534f,-0.0744864f,-0.000623756f},{0.401881f,-0.148638f,0.0949037f},{-0.0891837f,0.436147f,-0.0219092f}, +{-0.0690814f,0.436153f,-0.035285f},{-0.358043f,0.0803189f,0.354516f},{-0.358088f,0.233304f,0.28655f}, +{-0.279319f,0.131166f,0.262429f},{0.385425f,-0.113488f,0.172059f},{0.0420919f,0.1286f,0.114562f}, +{0.00494195f,0.120401f,0.0939713f},{-0.337381f,0.293245f,0.0591042f},{0.192235f,0.0807048f,0.225292f}, +{0.040645f,0.109752f,0.187454f},{-0.315466f,0.284177f,0.210495f},{-0.355651f,0.284788f,0.207601f}, +{0.230845f,0.0689946f,0.221376f},{-0.16468f,0.398669f,-0.0160702f},{-0.17619f,0.390978f,-0.014932f}, +{-0.262805f,-0.44967f,0.30907f},{-0.242143f,-0.449754f,0.311906f},{-0.374988f,0.341597f,-0.0915855f}, +{-0.352281f,0.342844f,-0.0752323f},{0.356976f,-0.0173049f,0.150998f},{0.362082f,-0.0148741f,0.0948973f}, +{0.380981f,-0.114093f,-0.0206617f},{-0.184435f,0.345584f,0.0454455f},{-0.16558f,0.340909f,0.0421787f}, +{-0.165709f,-0.44913f,0.288852f},{-0.337619f,0.365731f,-0.130787f},{-0.0571911f,-0.128935f,0.325623f}, +{-0.451268f,0.191614f,0.0565062f},{0.00446607f,0.0954117f,0.188926f},{-0.123356f,0.411967f,-0.0662551f}, +{-0.464734f,-0.114524f,0.0774059f},{-0.306225f,0.300492f,-0.0590077f},{-0.299421f,0.28583f,-0.0583004f}, +{-0.354731f,0.187467f,-0.0365647f},{-0.341722f,0.209897f,0.299611f},{-0.372673f,0.355731f,-0.115045f}, +{-0.385148f,0.355165f,-0.137288f},{0.300727f,0.0312016f,0.203305f},{0.36657f,-0.0529501f,0.171216f}, +{0.351934f,-0.00997397f,0.15514f},{0.369599f,-0.0327063f,0.114176f},{0.00206744f,0.0217871f,0.263509f}, +{-0.356776f,0.0418636f,0.346581f},{0.198151f,0.0257355f,0.254262f},{0.193315f,0.0190926f,0.258641f}, +{-0.427719f,0.00261084f,0.190399f},{-0.126475f,0.393608f,0.0137423f},{-0.296354f,0.0562426f,-0.0510337f}, +{-0.347265f,0.359448f,-0.298396f},{-0.479319f,0.22715f,0.16903f},{0.0213916f,0.0220122f,0.268834f}, +{0.136095f,-0.453715f,0.305103f},{-0.0334105f,0.025877f,0.251471f},{-0.467107f,0.112029f,0.0377737f}, +{0.173676f,0.0207453f,0.26203f},{-0.388287f,0.395016f,-0.225517f},{0.286335f,0.0563905f,0.187177f}, +{0.310623f,-0.0532523f,-0.0824153f},{0.380795f,-0.0549564f,0.0370728f},{0.11616f,0.0201923f,0.278827f}, +{-0.316392f,0.233002f,0.268442f},{-0.356088f,0.115495f,0.345635f},{0.0212115f,0.12235f,0.0571364f}, +{0.137176f,0.139404f,0.078647f},{-0.300958f,0.264255f,0.243015f},{0.331607f,0.0236327f,0.15067f}, +{0.251944f,-0.450976f,0.265889f},{0.216614f,-0.108614f,0.324626f},{0.0391209f,0.0243529f,0.272146f}, +{0.350159f,0.00359473f,0.094788f},{-0.396447f,-0.0920678f,-0.0587827f},{0.375457f,-0.0619272f,0.15114f}, +{0.378859f,-0.0548986f,0.11417f},{-0.389926f,0.0207453f,-0.016649f},{-0.39327f,0.168007f,-0.0230282f}, +{0.154043f,-0.0357931f,-0.124575f},{0.175965f,-0.0327128f,-0.11952f},{0.175354f,0.128549f,0.150741f}, +{0.13623f,0.136156f,0.134343f},{0.0985016f,0.136529f,0.115032f},{-0.131163f,0.107514f,0.129051f}, +{-0.108649f,0.108993f,0.0566349f},{-0.0375068f,0.0839973f,0.175737f},{-0.298868f,0.291508f,0.0583325f}, +{-0.192608f,0.14804f,0.134388f},{-0.146976f,0.401826f,-5.1427e-05f},{-0.260792f,-0.433774f,0.302235f}, +{-0.242266f,-0.432822f,0.30464f},{-0.223154f,-0.432128f,0.300621f},{0.0791068f,0.13325f,0.0571686f}, +{-0.472972f,0.157127f,0.17238f},{0.0598019f,0.0811099f,-0.0603196f},{-0.202678f,-0.451117f,0.299971f}, +{-0.185881f,-0.450905f,0.295753f},{-0.180223f,-0.444307f,0.292126f},{-0.295923f,0.435491f,-0.378264f}, +{-0.316868f,0.344902f,-0.112511f},{-0.109878f,0.101553f,0.133636f},{-0.413f,0.264339f,0.224707f}, +{-0.317749f,0.291528f,0.0568664f},{0.0993119f,0.117025f,0.191344f},{-0.468773f,-0.114106f,0.115938f}, +{0.247809f,0.0718048f,0.209132f},{-0.314025f,0.307527f,-0.0538375f},{-0.106804f,0.102318f,0.0163725f}, +{-0.108328f,0.0983956f,0.000623793f},{-0.124166f,0.104466f,-0.00183272f},{-0.430954f,-0.0187196f,0.0181731f}, +{0.359516f,-0.056757f,0.189949f},{0.360924f,-0.0149963f,0.114183f},{-0.203379f,0.301547f,0.0699592f}, +{0.043931f,0.0177872f,0.276653f},{-0.127337f,0.404874f,0.00522815f},{0.377033f,-0.0575802f,0.132555f}, +{0.396801f,-0.148973f,0.15276f},{-0.0911514f,0.443073f,-0.0347513f},{-0.0342529f,0.307598f,-0.0218771f}, +{0.250387f,0.0819974f,0.190296f},{0.0618919f,0.132337f,0.0979776f},{0.370126f,-0.0498955f,0.154092f}, +{-0.0687534f,0.038256f,0.2229f},{-0.0509598f,0.0395936f,0.228051f},{-0.0323752f,0.0376065f,0.242114f}, +{-0.0172824f,0.0393235f,0.24722f},{-0.376454f,0.0422173f,0.34442f},{0.101807f,-0.434835f,0.310807f}, +{0.117163f,-0.4376f,0.307746f},{0.137092f,-0.437143f,0.30435f},{0.157136f,-0.433986f,0.301926f}, +{-0.221996f,0.323739f,0.0583004f},{-0.204993f,0.322929f,0.060056f},{-0.183592f,0.321295f,0.0548343f}, +{0.0599755f,0.0178001f,0.277393f},{-0.371612f,0.134066f,0.346529f},{-0.279319f,0.293347f,0.151384f}, +{0.30418f,0.0563133f,0.149526f},{0.351574f,-0.0295231f,-0.00458504f},{-0.42516f,0.173576f,0.00514455f}, +{0.34978f,-0.0516575f,-0.0410598f},{-0.128784f,0.378316f,0.0167776f},{-0.374165f,0.0802225f,0.35258f}, +{0.335857f,-0.0209511f,-0.0349313f},{0.348609f,-0.0342754f,-0.0208739f},{0.403219f,-0.148555f,0.0756182f}, +{-0.35369f,-0.109778f,-0.0902029f},{-0.334462f,-0.109334f,-0.0956625f},{-0.376461f,-0.110086f,-0.0797852f}, +{-0.0688499f,0.453792f,-0.0389762f},{-0.059101f,0.454365f,-0.0439343f},{-0.241449f,0.312247f,0.0639594f}, +{-0.275866f,0.297437f,0.0577409f},{-0.2969f,0.288724f,0.040526f},{-0.12452f,0.104569f,0.134652f}, +{-0.0449085f,0.106698f,0.07684f},{-0.352564f,0.250764f,0.267805f},{-0.335652f,0.284769f,0.209781f}, +{-0.391682f,-0.0901193f,0.243947f},{0.358397f,-0.0700492f,-0.0368027f},{-0.455037f,0.160406f,0.031118f}, +{-0.207244f,-0.435819f,0.296801f},{-0.202177f,-0.427382f,0.290885f},{-0.184473f,-0.42978f,0.288955f}, +{-0.167245f,-0.431086f,0.284589f},{-0.145227f,-0.433259f,0.278943f},{-0.336938f,0.340073f,-0.0762226f}, +{-0.328333f,0.331713f,-0.0703771f},{-0.316347f,0.321379f,-0.0740426f},{-0.0900582f,-0.471773f,0.341359f}, +{-0.0745475f,-0.4318f,0.343346f},{-0.0611524f,-0.427793f,0.349043f},{-0.0524774f,-0.43423f,0.355699f}, +{-0.0348703f,-0.432513f,0.355416f},{0.174666f,0.132105f,0.133629f},{-0.183251f,0.359828f,0.0370985f}, +{0.249275f,0.0920421f,0.17031f},{0.0221054f,-0.433337f,0.336742f},{0.0347738f,-0.433311f,0.332144f}, +{0.0444905f,-0.433279f,0.329301f},{0.0604707f,-0.433246f,0.324092f},{0.0795826f,-0.433169f,0.317919f}, +{0.0923282f,-0.433124f,0.313591f},{0.174236f,-0.432449f,0.299399f},{0.231996f,0.02189f,0.248693f}, +{0.00507055f,0.11743f,0.131449f},{-0.234034f,0.228925f,0.155545f},{-0.29074f,0.399447f,-0.339243f}, +{-0.127562f,0.398585f,-0.0706215f},{-0.237796f,0.133533f,0.232089f},{-0.313929f,0.1495f,0.285862f}, +{0.381296f,-0.054815f,0.0948651f},{-0.145844f,0.379223f,0.0246809f},{-0.121864f,0.413382f,-0.00367832f}, +{-0.491158f,0.0606347f,0.172946f},{0.400736f,-0.130665f,0.0370921f},{0.0808816f,0.0201151f,0.278145f}, +{-0.186242f,0.340336f,-0.0426803f},{-0.156436f,0.340195f,-0.0631619f},{-0.224472f,-0.266313f,0.252731f}, +{-0.128655f,-0.114562f,-0.142401f},{-0.35713f,0.187769f,0.318234f},{-0.429134f,0.265882f,0.206386f}, +{-0.286734f,0.292544f,0.0523327f},{-0.331015f,0.359448f,-0.337012f},{0.100733f,0.101122f,0.215254f}, +{-0.0531848f,0.10417f,0.0738497f},{-0.14904f,0.117983f,0.0386033f},{-0.373393f,0.250249f,0.26711f}, +{-0.354667f,0.335179f,-0.0613485f},{-0.268779f,-0.426436f,0.293341f},{-0.139356f,-0.426771f,0.273869f}, +{0.0225041f,0.1217f,0.133989f},{-0.0367802f,0.104563f,0.0376901f},{-0.0706633f,0.0942864f,0.0189769f}, +{-0.0814154f,-0.420617f,0.334337f},{-0.0697952f,-0.416919f,0.335648f},{-0.0543552f,-0.414475f,0.34179f}, +{-0.0346131f,-0.419498f,0.351449f},{-0.0170702f,-0.416186f,0.34624f},{0.00188095f,-0.415935f,0.338169f}, +{0.0178869f,-0.420668f,0.334645f},{0.0227227f,-0.414559f,0.328748f},{0.040407f,-0.417208f,0.323945f}, +{0.0598148f,-0.417697f,0.317784f},{0.0748561f,-0.419485f,0.314562f},{0.0834925f,-0.416739f,0.312395f}, +{0.0984952f,-0.418398f,0.310614f},{0.117794f,-0.423285f,0.306016f},{0.137111f,-0.423485f,0.305263f}, +{0.156243f,-0.418347f,0.308177f},{0.0988103f,0.076422f,0.243336f},{0.155008f,0.0210347f,0.268956f}, +{-0.411038f,0.251838f,0.247265f},{-0.410543f,0.241999f,0.261284f},{-0.391753f,0.24632f,0.26695f}, +{-0.177489f,0.115868f,0.134214f},{-0.411945f,0.133436f,0.346246f},{-0.219327f,0.284595f,0.0821324f}, +{0.352751f,-0.0704608f,-0.0463586f},{0.398987f,-0.130729f,0.114176f},{0.116977f,-0.0373878f,-0.131128f}, +{-0.368583f,-0.130304f,-0.0862995f},{-0.3536f,-0.12826f,-0.09237f},{-0.334333f,-0.127784f,-0.0978939f}, +{0.273512f,0.0615929f,0.194058f},{-0.141073f,0.358805f,0.025742f},{0.0806308f,0.134607f,0.114048f}, +{-0.364178f,0.164824f,0.332523f},{0.155439f,0.131526f,0.151584f},{-0.31679f,0.289103f,0.040764f}, +{-0.321736f,0.316562f,-0.055458f},{-0.0391209f,-0.127449f,0.348703f},{-0.112816f,0.109611f,0.0410405f}, +{0.0798013f,-0.0758304f,0.343391f},{0.0260024f,0.107771f,0.182155f},{0.0239188f,0.112517f,0.170445f}, +{0.419128f,-0.26322f,0.170818f},{-0.261185f,-0.414012f,0.28657f},{-0.241172f,-0.412675f,0.287026f}, +{-0.223064f,-0.41227f,0.283753f},{-0.203772f,-0.416855f,0.282943f},{-0.184422f,-0.416925f,0.28019f}, +{-0.16511f,-0.417112f,0.276949f},{-0.152256f,-0.417196f,0.276872f},{-0.144545f,-0.412295f,0.271689f}, +{-0.130668f,-0.413524f,0.27093f},{-0.33933f,0.299868f,0.00039872f},{-0.454754f,0.269998f,0.156593f}, +{-0.355445f,0.290942f,0.187936f},{-0.373869f,0.292685f,0.171158f},{0.0630687f,-0.0750779f,0.341372f}, +{0.301582f,0.036359f,-0.0174399f},{-0.484972f,0.043542f,0.227652f},{-0.0878975f,-0.413839f,0.323211f}, +{-0.0364779f,-0.410373f,0.346343f},{0.117851f,-0.412816f,0.316787f},{0.137098f,-0.412019f,0.321604f}, +{0.153001f,-0.412225f,0.319591f},{0.17729f,-0.415324f,0.301707f},{-0.12652f,0.0335809f,0.221356f}, +{-0.107344f,0.0338317f,0.222655f},{-0.206402f,0.0359345f,0.223974f},{0.0985531f,0.0223723f,0.27902f}, +{0.386113f,-0.167943f,-0.0205717f},{0.059982f,0.0266615f,0.27331f},{-0.324829f,0.228083f,0.278538f}, +{-0.143298f,0.411575f,-0.0170091f},{0.230079f,0.0996624f,0.170142f},{0.00333428f,0.11907f,0.0747822f}, +{-0.0342851f,0.0952124f,0.1515f},{0.367097f,-0.0327707f,0.133501f},{0.305479f,-0.430276f,0.202965f}, +{0.325279f,-0.432378f,0.19436f},{-0.201129f,0.301309f,-0.0341211f},{-0.0687792f,0.41562f,-0.106234f}, +{0.285402f,-0.132542f,-0.130118f},{-0.0900582f,-0.113913f,-0.148195f},{-0.470496f,0.263483f,0.113199f}, +{0.327536f,0.0208932f,0.172078f},{0.349542f,-0.0149705f,0.174387f},{-0.0496415f,0.456307f,-0.0524292f}, +{0.284888f,-0.115135f,0.298344f},{0.155542f,0.11745f,0.189016f},{-0.0713836f,0.10136f,0.0766085f}, +{-0.462342f,0.0766857f,0.0217871f},{0.0812353f,-0.0669496f,0.329192f},{-0.304309f,0.282583f,-0.0389569f}, +{-0.0486705f,-0.130169f,0.338889f},{-0.20372f,-0.40854f,0.273586f},{-0.184428f,-0.408527f,0.272853f}, +{-0.16513f,-0.408553f,0.27212f},{-0.0558985f,-0.105444f,-0.178663f},{-0.0901482f,0.0915726f,0.00273948f}, +{-0.0718337f,-0.401498f,0.322466f},{-0.0522974f,-0.402662f,0.335115f},{-0.0364715f,-0.402199f,0.338381f}, +{-0.017321f,-0.402579f,0.335468f},{0.0019967f,-0.39737f,0.321578f},{0.0213466f,-0.399299f,0.320356f}, +{0.0406707f,-0.398978f,0.322536f},{0.0598727f,-0.40427f,0.318427f},{0.0792611f,-0.403119f,0.324491f}, +{0.0985981f,-0.40245f,0.327597f},{0.117851f,-0.402424f,0.330645f},{0.135806f,-0.396515f,0.336793f}, +{0.143484f,-0.402212f,0.331796f},{0.156288f,-0.402759f,0.327501f},{0.174705f,-0.396778f,0.321295f}, +{-0.393251f,0.175088f,0.330491f},{-0.266657f,0.267921f,0.222089f},{0.0760586f,0.0268223f,0.274274f}, +{-0.374171f,0.17521f,0.329848f},{-0.355291f,0.17002f,0.325764f},{-0.338089f,0.249195f,0.268101f}, +{-0.320527f,0.247998f,0.262757f},{-0.415784f,-0.109244f,-0.0416578f},{-0.395386f,0.21018f,0.303913f}, +{-0.373586f,0.262403f,0.248712f},{0.32452f,-0.413421f,0.205273f},{0.343323f,-0.413382f,0.207029f}, +{0.362815f,-0.411697f,0.208823f},{-0.164191f,0.322324f,0.0389441f},{0.313955f,0.0451368f,0.154735f}, +{-0.158712f,0.3742f,0.0292852f},{0.359059f,-0.0334201f,0.173133f},{-0.0546896f,0.469323f,-0.0593164f}, +{-0.0400727f,0.470847f,-0.0605961f},{0.0435195f,0.129552f,0.0968393f},{-0.0157969f,0.114247f,0.0771229f}, +{0.117157f,0.11671f,0.191499f},{0.174936f,0.0809942f,0.231182f},{-0.335555f,0.259117f,0.257201f}, +{-0.375753f,0.328079f,-0.0684286f},{-0.333819f,0.326189f,-0.0550915f},{0.079872f,0.130202f,0.15094f}, +{-0.299608f,0.266673f,-0.01771f},{-0.319523f,0.302595f,-0.0367641f},{-0.338307f,0.319167f,-0.0382881f}, +{-0.259654f,-0.401923f,0.265567f},{-0.243262f,-0.398174f,0.263728f},{-0.223244f,-0.399826f,0.264204f}, +{-0.202125f,-0.398084f,0.262995f},{-0.184454f,-0.395691f,0.263561f},{-0.165162f,-0.39537f,0.266602f}, +{-0.145806f,-0.395466f,0.26576f},{-0.125568f,-0.396727f,0.264596f},{-0.33623f,0.347474f,-0.0918877f}, +{-0.320829f,0.338645f,-0.0939005f},{-0.312437f,0.328272f,-0.0923378f},{-0.0557121f,-0.394527f,0.325006f}, +{-0.0364844f,-0.394418f,0.326536f},{-0.0172053f,-0.394688f,0.322485f},{0.0600206f,-0.393145f,0.332857f}, +{0.0792804f,-0.393036f,0.334504f},{0.0985531f,-0.392971f,0.336433f},{0.117832f,-0.39245f,0.340124f}, +{0.156339f,-0.393138f,0.334845f},{0.0406707f,0.0795794f,0.232591f},{0.398286f,-0.112762f,0.0371114f}, +{-0.471802f,0.0156329f,0.116536f},{-0.261165f,0.293142f,0.0956368f},{-0.389849f,0.0793929f,0.346034f}, +{-0.306746f,0.246095f,0.256661f},{-0.298302f,0.250976f,0.25041f},{-0.280772f,0.250551f,0.242918f}, +{-0.46694f,0.210668f,0.209247f},{-0.390171f,0.13925f,0.349031f},{0.0996849f,0.137295f,0.0955082f}, +{0.392878f,-0.114517f,0.148445f},{0.387965f,-0.0771036f,0.114151f},{-0.167509f,0.121359f,0.0380695f}, +{-0.292045f,0.246281f,-0.0204173f},{-0.305653f,0.278718f,-0.0245008f},{-0.330192f,0.311321f,-0.0337481f}, +{-0.353953f,0.322311f,-0.0375743f},{-0.241449f,-0.391994f,0.247227f},{0.133755f,0.0177294f,0.27601f}, +{-0.225064f,-0.391692f,0.245606f},{-0.206987f,-0.391981f,0.254288f},{-0.108894f,-0.390431f,0.265098f}, +{-0.12789f,0.110627f,0.113739f},{0.061924f,-0.0584675f,0.301945f},{-0.164365f,0.120048f,0.0967365f}, +{-0.355046f,0.31082f,-0.0183531f},{-0.0707984f,-0.389782f,0.304061f},{-0.0525739f,-0.386701f,0.310003f}, +{-0.0365165f,-0.386663f,0.310286f},{-0.0172696f,-0.386669f,0.309868f},{-0.00438252f,-0.386444f,0.312517f}, +{0.00376513f,-0.378708f,0.321295f},{0.0171538f,-0.381898f,0.327108f},{0.0257387f,-0.378104f,0.334478f}, +{0.0406578f,-0.379377f,0.337777f},{0.0599562f,-0.378895f,0.342388f},{0.077647f,-0.380804f,0.345982f}, +{0.0985402f,-0.38256f,0.350053f},{0.117819f,-0.380734f,0.347635f},{0.137118f,-0.382952f,0.347024f}, +{0.156358f,-0.378824f,0.342722f},{0.174801f,-0.375428f,0.33705f},{-0.293306f,0.246314f,-0.0577537f}, +{-0.475679f,0.0771036f,0.0506543f},{-0.0662712f,-0.117623f,-0.186586f},{-0.344249f,0.391113f,-0.324369f}, +{-0.370872f,0.116569f,0.349307f},{-0.281731f,0.208894f,-0.0190347f},{-0.5f,0.077991f,0.134201f}, +{0.324681f,0.0208289f,3.86026e-05f},{-0.229899f,0.187364f,0.172335f},{0.0615768f,0.0731359f,0.242584f}, +{0.0284267f,0.126562f,0.092563f},{-0.288219f,0.291238f,-0.147847f},{-0.4415f,-0.0193434f,0.147532f}, +{-0.467686f,0.192663f,0.210759f},{-0.0701425f,0.0924215f,0.13388f},{-0.0727147f,0.0979968f,0.0376001f}, +{0.323954f,-0.394007f,0.213292f},{0.342847f,-0.391544f,0.213176f},{0.360828f,-0.392122f,0.216964f}, +{0.383348f,-0.393878f,0.215118f},{-0.147085f,0.03751f,0.223375f},{-0.458689f,0.0765956f,0.0121733f}, +{-0.302842f,0.289637f,-0.244211f},{-0.340597f,0.0807112f,0.352227f},{0.289364f,-0.0535095f,0.267676f}, +{-0.454792f,0.0574001f,0.0140382f},{-0.0704125f,0.457548f,-0.0739462f},{-0.186518f,0.37503f,0.0173564f}, +{-0.296971f,0.266294f,-0.0383975f},{-0.372493f,-0.095058f,0.263213f},{0.211855f,0.119019f,0.132903f}, +{0.00716052f,-0.077483f,0.319726f},{0.051172f,0.127642f,0.144291f},{-0.146391f,0.0978682f,0.152773f}, +{-0.220929f,-0.380824f,0.227735f},{-0.216241f,-0.383261f,0.241401f},{-0.203604f,-0.379036f,0.247278f}, +{-0.190981f,-0.378271f,0.254436f},{-0.181271f,-0.377904f,0.258603f},{-0.16522f,-0.377525f,0.26302f}, +{-0.145799f,-0.377525f,0.263201f},{-0.126462f,-0.377846f,0.259464f},{-0.107247f,-0.382239f,0.256744f}, +{0.322456f,0.0356259f,0.151847f},{-0.0665799f,-0.379133f,0.281078f},{-0.0513521f,-0.377171f,0.28455f}, +{-0.039391f,-0.375152f,0.289258f},{-0.0298029f,-0.374271f,0.298216f},{-0.0215267f,-0.374946f,0.305939f}, +{-0.0127359f,-0.370882f,0.315977f},{0.0824057f,-0.373371f,0.353378f},{0.0985209f,-0.372914f,0.357963f}, +{0.117813f,-0.373351f,0.35251f},{0.137092f,-0.373358f,0.352329f},{0.382756f,-0.0957782f,-0.00136328f}, +{0.394679f,-0.112903f,0.133449f},{-0.389026f,0.097283f,0.348497f},{-0.431128f,0.0196778f,0.305193f}, +{-0.480425f,0.0439793f,0.0615414f},{-0.359651f,0.151455f,0.335198f},{-0.371837f,0.151802f,0.340677f}, +{-0.390171f,0.152741f,0.343436f},{-0.485364f,0.0427446f,0.0765506f},{0.0792932f,0.075213f,0.243651f}, +{0.211874f,0.12071f,0.11453f},{0.368583f,-0.386328f,0.219067f},{0.378274f,-0.386367f,0.21906f}, +{-0.337825f,0.0426482f,0.344201f},{0.249532f,0.0981319f,0.152027f},{0.249686f,0.101135f,0.132594f}, +{0.362307f,-0.0139031f,0.0751937f},{0.00535994f,0.324311f,-0.055323f},{-0.38965f,0.24396f,-0.0210861f}, +{-0.449429f,0.0762419f,-0.000784522f},{-0.313697f,0.286287f,-0.0181602f},{0.191984f,0.110247f,0.189306f}, +{0.00652389f,0.103746f,0.175383f},{-0.165008f,0.392573f,0.00273305f},{-0.35423f,0.294576f,0.173287f}, +{-0.148886f,0.0846597f,0.176888f},{0.207527f,0.0906723f,0.204206f},{-0.107138f,-0.373834f,0.252796f}, +{-0.0889843f,-0.372149f,0.249992f},{-0.304759f,0.320221f,-0.0956882f},{-0.127182f,0.0754059f,0.190611f}, +{-0.108315f,0.0574837f,0.2049f},{-0.0575576f,-0.37148f,0.267104f},{-0.00203533f,-0.363551f,0.329006f}, +{0.00388088f,-0.355828f,0.334677f},{0.0214687f,-0.361326f,0.336883f},{0.0406707f,-0.360625f,0.344819f}, +{0.055667f,-0.361725f,0.348799f},{0.0642198f,-0.358439f,0.352214f},{0.0792354f,-0.359455f,0.35694f}, +{0.0985466f,-0.358947f,0.36204f},{0.117877f,-0.3595f,0.356509f},{0.137105f,-0.359757f,0.353866f}, +{0.156378f,-0.360297f,0.347982f},{0.175998f,-0.359152f,0.34514f},{0.0201054f,0.075766f,0.229575f}, +{-0.297833f,0.399453f,-0.320176f},{-0.259847f,0.0370792f,0.247214f},{0.138578f,0.0243465f,0.272544f}, +{-0.165155f,0.0381917f,0.222835f},{-0.403103f,0.116607f,0.351969f},{0.347426f,-0.0926658f,-0.0576316f}, +{-0.0582393f,0.457156f,-0.0944021f},{-0.298656f,0.344491f,-0.167519f},{-0.411636f,0.151661f,0.338497f}, +{0.0989132f,0.137185f,0.0767821f},{-0.409958f,0.207415f,0.298743f},{0.293691f,0.0650204f,0.156272f}, +{-0.0904505f,0.103559f,0.0759075f},{0.330603f,-0.375428f,0.21762f},{0.34297f,-0.373139f,0.213922f}, +{0.360808f,-0.371872f,0.216546f},{0.368557f,-0.377094f,0.219009f},{0.381483f,-0.372547f,0.219614f}, +{-0.43289f,0.251657f,0.227395f},{-0.313485f,0.255098f,0.254429f},{0.330089f,-0.0531044f,0.230706f}, +{0.30589f,0.0219928f,0.206784f},{0.38237f,-0.0548664f,0.0563198f},{-0.0897302f,0.0956175f,0.133906f}, +{-0.0879168f,0.0444037f,0.215536f},{-0.184441f,0.134401f,0.132915f},{0.19945f,0.101727f,0.198026f}, +{0.193605f,0.0958233f,0.20917f},{-0.354416f,0.26648f,0.245555f},{-0.279338f,0.282004f,0.207678f}, +{-0.355966f,0.29347f,0.0546414f},{-0.354564f,0.296814f,0.0935083f},{-0.201322f,-0.362393f,0.24315f}, +{-0.188229f,-0.361403f,0.253625f},{-0.179206f,-0.358567f,0.258988f},{-0.165188f,-0.359371f,0.263709f}, +{-0.145793f,-0.359403f,0.263953f},{-0.126449f,-0.359776f,0.259644f},{-0.107138f,-0.360413f,0.252352f}, +{-0.0891901f,-0.360021f,0.246384f},{-0.280136f,0.293167f,0.0344748f},{-0.461268f,0.0941706f,0.0216842f}, +{-0.336494f,0.291135f,0.190476f},{-0.0558021f,-0.358059f,0.265053f},{-0.0402142f,-0.356979f,0.28747f}, +{-0.0301695f,-0.353416f,0.302608f},{-0.0231215f,-0.359397f,0.309508f},{-0.0135977f,-0.353481f,0.321681f}, +{-0.410967f,0.0985306f,0.345783f},{-0.396711f,0.102801f,0.350773f},{-0.184447f,0.0336581f,0.224674f}, +{-0.111086f,0.423491f,-0.0134915f},{-0.415186f,0.115971f,0.347674f},{-0.128141f,0.419125f,-0.017665f}, +{-0.290862f,0.286274f,-0.096402f},{0.0463683f,0.437201f,-0.168316f},{0.342841f,-0.1328f,-0.0902865f}, +{0.392421f,-0.0813864f,0.0755796f},{0.356911f,-0.0199286f,0.0177872f},{-0.467024f,-0.0708209f,0.0964471f}, +{0.120951f,0.0267194f,0.27448f},{0.39419f,-0.368914f,0.21897f},{0.215842f,0.0233819f,0.252313f}, +{0.273377f,0.0811099f,0.153577f},{-0.455577f,-0.0533295f,0.056577f},{-0.372969f,0.334041f,-0.0745571f}, +{-0.0136491f,0.113437f,0.0573872f},{-0.357477f,0.241433f,0.278769f},{-0.318064f,0.290531f,0.191627f}, +{-0.335851f,0.278583f,0.225047f},{0.0617826f,0.127681f,0.153719f},{-0.296881f,0.294119f,0.0776695f}, +{-0.319003f,0.265046f,0.24715f},{-0.373631f,0.290094f,0.039928f},{0.00397734f,0.118176f,0.0573101f}, +{-0.260651f,0.281702f,0.190142f},{-0.0537893f,0.0964342f,0.0183338f},{-0.0705605f,-0.359037f,0.248056f}, +{0.0407607f,0.122652f,0.154567f},{-0.0711907f,0.0987235f,0.114543f},{-0.393714f,0.293534f,0.135147f}, +{-0.398814f,0.379653f,-0.243941f},{-0.390955f,-0.0752966f,-0.0572136f},{0.0226327f,0.119938f,0.0371885f}, +{0.0990097f,0.135777f,0.0580818f},{0.118314f,0.138742f,0.0781197f},{0.193155f,0.128774f,0.0961062f}, +{0.00290986f,-0.337115f,0.339397f},{0.021816f,-0.338973f,0.343912f},{0.0373589f,-0.346973f,0.348111f}, +{0.0422784f,-0.339674f,0.352953f},{0.0599305f,-0.341577f,0.356876f},{0.0792289f,-0.34159f,0.357185f}, +{0.0984695f,-0.341307f,0.36004f},{0.117845f,-0.341372f,0.359075f},{0.13715f,-0.341886f,0.354227f}, +{0.154841f,-0.339893f,0.35168f},{0.159619f,-0.346709f,0.349931f},{0.174454f,-0.341944f,0.348754f}, +{0.137098f,0.0797274f,0.234539f},{0.117382f,0.0827112f,0.233671f},{0.370126f,-0.168323f,-0.0577537f}, +{0.174396f,0.134163f,0.114022f},{-0.37837f,0.0210347f,-0.026295f},{-0.108617f,0.021382f,0.232044f}, +{-0.244954f,0.282261f,0.132735f},{0.134796f,0.118331f,0.188508f},{0.080155f,0.135185f,0.0954117f}, +{0.154551f,0.135873f,0.13087f},{-0.0725411f,0.100325f,0.0573808f},{0.344815f,-0.357931f,0.214012f}, +{0.362172f,-0.354844f,0.214276f},{0.379804f,-0.352291f,0.21708f},{0.384653f,-0.358947f,0.218803f}, +{0.397688f,-0.359384f,0.218797f},{-0.3901f,0.188508f,0.322067f},{-0.0718144f,-0.112768f,-0.167171f}, +{-0.0893123f,0.0619337f,-0.0559532f},{-0.196769f,0.307952f,0.0660493f},{-0.0233851f,-0.14159f,0.366895f}, +{0.234459f,0.103842f,0.15258f},{0.360461f,-0.0519726f,-0.020398f},{0.29238f,0.0452911f,0.195595f}, +{-0.375309f,0.320311f,-0.0550657f},{-0.316623f,0.297585f,0.135912f},{-0.372744f,0.282557f,0.208784f}, +{-0.280592f,0.294556f,0.0755024f},{0.0436802f,0.118305f,0.171557f},{0.288746f,0.0260571f,-0.0418957f}, +{-0.129755f,0.113996f,0.0951288f},{-0.180081f,-0.338169f,0.260667f},{-0.1652f,-0.341037f,0.266506f}, +{-0.145831f,-0.340947f,0.267734f},{-0.126501f,-0.341346f,0.263303f},{-0.111459f,-0.340401f,0.257233f}, +{-0.102952f,-0.343745f,0.252982f},{-0.0870229f,-0.340838f,0.24742f},{-0.0707341f,-0.340246f,0.248249f}, +{-0.0517122f,0.0829363f,0.168059f},{0.210993f,0.110421f,0.169995f},{-0.413405f,0.28711f,0.152953f}, +{-0.497717f,0.108736f,0.150156f},{0.0393395f,0.123218f,0.0331629f},{0.23114f,0.112247f,0.112472f}, +{-0.130822f,0.421536f,-0.0386354f},{-0.0864056f,0.0360117f,0.224057f},{-0.0163243f,0.0771229f,0.207485f}, +{-0.00137296f,0.0807627f,0.209697f},{-0.330996f,0.341243f,-0.336857f},{-0.0541558f,0.100904f,0.114061f}, +{-0.126514f,0.0204431f,0.231858f},{-0.301518f,0.303784f,-0.0767113f},{-0.0140221f,0.0699463f,0.219871f}, +{0.364416f,-0.0319861f,0.0164239f},{-0.224549f,0.247941f,0.115726f},{0.274798f,-0.0509115f,0.27484f}, +{0.00366868f,0.07248f,0.224784f},{-0.287434f,0.264455f,0.238854f},{0.370429f,-0.0281663f,0.0755924f}, +{-0.486129f,0.0955853f,0.211286f},{0.402524f,-0.352805f,0.217183f},{-0.392492f,-0.263876f,0.0960741f}, +{-0.373374f,0.363596f,-0.131899f},{-0.14733f,0.115932f,0.0203659f},{0.0461496f,0.417517f,-0.14905f}, +{-0.033507f,0.477419f,-0.0714961f},{0.0626121f,-0.0658371f,0.325713f},{-0.375084f,-0.149693f,0.26284f}, +{-0.239854f,-0.356509f,0.0785891f},{0.0611009f,0.100196f,0.210476f},{-0.317877f,0.29727f,0.153204f}, +{0.0992733f,-0.0652519f,0.325842f},{-0.278129f,0.268152f,0.230417f},{-0.015932f,0.110305f,0.0387448f}, +{-0.0342465f,0.10972f,0.0948008f},{-0.412993f,0.28383f,0.1722f},{-0.0337063f,0.0961191f,0.00265588f}, +{-0.0527154f,0.0908974f,-0.00167195f},{-0.0925019f,0.43587f,-0.0756246f},{0.0438153f,0.125867f,0.0425581f}, +{0.174287f,0.134928f,0.0941706f},{0.0213144f,-0.318408f,0.354863f},{0.0406578f,-0.323224f,0.361603f}, +{0.0599305f,-0.322896f,0.366046f},{0.0792418f,-0.323166f,0.36249f},{0.097312f,-0.323829f,0.363519f}, +{0.117806f,-0.327424f,0.364271f},{0.138397f,-0.323951f,0.364387f},{0.157805f,-0.321604f,0.357699f}, +{0.177978f,-0.320755f,0.346786f},{0.027848f,0.0706215f,0.238475f},{-0.223636f,0.0383203f,0.226314f}, +{0.156378f,0.0796759f,0.235767f},{0.319929f,0.0325649f,0.169229f},{-0.147516f,0.116594f,0.0961384f}, +{-0.313749f,0.293438f,0.17811f},{0.136738f,0.138626f,0.116125f},{0.362358f,-0.337057f,0.210244f}, +{0.381406f,-0.336684f,0.213376f},{0.400826f,-0.336677f,0.215897f},{0.419559f,-0.337655f,0.210829f}, +{0.394679f,-0.0948008f,0.0948716f},{0.394659f,-0.0958233f,0.0384039f},{-0.407849f,0.0805119f,0.336343f}, +{-0.446053f,0.00109963f,0.0566477f},{-0.35713f,0.225485f,-0.0908974f},{-0.290335f,0.285129f,-0.168438f}, +{-0.410299f,-0.0898621f,0.228141f},{-0.392196f,-0.111135f,0.247098f},{-0.0742517f,0.0636057f,0.19465f}, +{-0.239397f,0.266126f,0.172747f},{-0.353374f,0.279753f,0.221408f},{-0.342693f,0.295778f,0.0180895f}, +{-0.0354876f,0.101129f,0.021517f},{-0.394749f,0.288923f,0.172605f},{-0.167168f,-0.32107f,0.268577f}, +{-0.145838f,-0.322485f,0.272036f},{-0.126526f,-0.322742f,0.268795f},{-0.107189f,-0.323552f,0.26003f}, +{-0.0880133f,-0.319681f,0.25241f},{-0.0712421f,-0.318022f,0.250693f},{-0.0627793f,-0.324871f,0.257233f}, +{-0.410318f,-0.0753159f,-0.0372528f},{0.251095f,0.101746f,0.11134f},{0.295132f,0.0662294f,0.136947f}, +{0.00121216f,-0.316614f,0.345314f},{0.0165557f,-0.3217f,0.349564f},{0.10499f,-0.317848f,0.370618f}, +{0.117826f,-0.31788f,0.370683f},{0.130649f,-0.317958f,0.36993f},{0.176743f,0.0583454f,0.244372f}, +{0.00338572f,0.0562169f,0.241201f},{0.117774f,0.062088f,0.253291f},{-0.497955f,0.0649368f,0.134272f}, +{-0.0534099f,0.0723128f,0.189705f},{-0.0364844f,0.0692196f,0.207273f},{-0.14578f,0.0250796f,0.232591f}, +{-0.0344716f,0.0794894f,0.189962f},{-0.374126f,0.210926f,0.306852f},{-0.238844f,0.227845f,0.173602f}, +{-0.410041f,0.290004f,0.114492f},{0.193695f,0.128131f,0.11462f},{-0.291917f,0.284917f,-0.187685f}, +{-0.185264f,0.100659f,-0.0401788f},{0.378094f,-0.0563068f,0.0186489f},{-0.335382f,0.168773f,-0.0424552f}, +{-0.433976f,0.0221343f,0.0171313f},{-0.0405421f,0.318511f,-0.00552392f},{-0.446175f,0.191441f,0.0372014f}, +{-0.303505f,0.396077f,-0.300685f},{-0.369811f,0.296563f,0.0990451f},{-0.41217f,0.00878431f,0.29855f}, +{-0.294347f,0.26549f,-0.148452f},{-0.355812f,0.360175f,-0.111141f},{-0.122121f,0.0838623f,0.17701f}, +{0.0430565f,0.127186f,0.132041f},{0.251764f,0.0635028f,0.215164f},{-0.414492f,0.283952f,0.0753931f}, +{0.0616346f,0.114093f,0.190187f},{-0.393785f,0.291817f,0.153937f},{-0.21853f,0.153397f,0.19463f}, +{0.0606958f,0.12844f,0.0401531f},{0.156468f,0.137963f,0.0765185f},{0.212903f,0.120986f,0.0972252f}, +{0.285743f,0.0747886f,0.133443f},{0.322745f,0.0380438f,0.133648f},{0.0598791f,0.122337f,0.171763f}, +{0.00871031f,-0.310562f,0.351905f},{0.022324f,-0.300466f,0.358908f},{0.0406643f,-0.300415f,0.366676f}, +{0.0599112f,-0.304537f,0.371184f},{0.0776341f,-0.302247f,0.37139f},{0.0824636f,-0.309083f,0.368606f}, +{0.0985209f,-0.304286f,0.373448f},{0.117813f,-0.304202f,0.374747f},{0.137195f,-0.304408f,0.372367f}, +{0.154828f,-0.298685f,0.366991f},{0.0213273f,0.057548f,0.247812f},{0.0985338f,0.0622102f,0.25522f}, +{0.0792868f,0.0621138f,0.253786f},{0.058355f,0.0599402f,0.252802f},{-0.315717f,0.074795f,-0.0470338f}, +{-0.147941f,-0.114858f,-0.139577f},{-0.499453f,0.0780746f,0.153526f},{0.0407157f,0.0706859f,0.241639f}, +{-0.495396f,0.0597666f,0.154915f},{-0.497685f,0.0649883f,0.147159f},{-0.228092f,-0.345076f,0.134433f}, +{-0.294791f,0.267618f,-0.0566284f},{0.390299f,-0.0928009f,0.13505f},{-0.460947f,-0.0740812f,0.151326f}, +{0.34706f,-0.0158773f,-0.00152405f},{-0.109517f,-0.0777853f,-0.137243f},{0.376653f,-0.0786599f,0.17076f}, +{0.365869f,-0.0745892f,0.190193f},{0.363696f,-0.318266f,0.207685f},{0.381502f,-0.319244f,0.204617f}, +{0.40073f,-0.318884f,0.209607f},{0.420138f,-0.319025f,0.208566f},{0.437951f,-0.319417f,0.20245f}, +{0.369933f,-0.0920999f,-0.0271052f},{0.368705f,-0.130832f,-0.0560946f},{-0.049873f,-0.115167f,-0.203183f}, +{0.211038f,0.0167455f,0.257136f},{-0.379412f,0.294782f,0.094132f},{-0.26359f,-0.323951f,0.152619f}, +{-0.262368f,-0.314035f,0.17366f},{-0.237912f,-0.316794f,0.186206f},{-0.0326967f,0.10682f,0.0438636f}, +{-0.20235f,0.169075f,0.116073f},{0.118134f,-0.0765313f,0.340639f},{0.21082f,0.0727243f,0.222758f}, +{0.273814f,0.0845117f,0.133552f},{-0.168879f,-0.303199f,0.271104f},{-0.145831f,-0.303958f,0.276428f}, +{-0.130816f,-0.302563f,0.275625f},{-0.122237f,-0.305829f,0.272621f},{-0.106399f,-0.303109f,0.263882f}, +{-0.0943796f,-0.310286f,0.256275f},{-0.0640912f,-0.298698f,0.260358f},{0.116778f,0.137475f,0.0591364f}, +{-0.00123792f,-0.300711f,0.346651f},{0.0064017f,-0.29565f,0.352953f},{0.193656f,0.0561783f,0.240912f}, +{0.0406964f,0.0575415f,0.251998f},{-0.454329f,0.265078f,0.171268f},{-0.374191f,0.188579f,0.321874f}, +{0.393528f,-0.0958104f,0.112845f},{-0.28784f,0.306234f,-0.186463f},{-0.294412f,0.22542f,-0.0373107f}, +{0.116006f,0.0725186f,0.244153f},{-0.406286f,0.187454f,0.318363f},{0.0225812f,0.0918942f,0.209479f}, +{0.00791934f,0.0883766f,0.206103f},{0.389103f,-0.0944214f,0.0165911f},{0.0820649f,0.110228f,0.202257f}, +{0.34052f,-0.0172085f,-0.0195042f},{-0.0263304f,0.489116f,-0.0886531f},{0.248947f,0.0197486f,0.243478f}, +{-0.168088f,0.11853f,0.0191827f},{-0.316135f,0.285476f,0.0214334f},{0.199309f,0.123707f,0.138098f}, +{-0.130539f,0.340433f,-0.0769428f},{-0.168757f,0.282673f,-0.0395293f},{-0.262477f,-0.306961f,0.190913f}, +{-0.248754f,-0.306363f,0.199158f},{-0.241404f,-0.30264f,0.209144f},{-0.226517f,-0.30462f,0.214861f}, +{-0.220517f,-0.297997f,0.228745f},{-0.334128f,0.292537f,0.0193499f},{0.12034f,0.102511f,0.213646f}, +{-0.00977785f,0.106678f,0.150645f},{-0.158596f,-0.299476f,0.275477f},{-0.0922382f,-0.295058f,0.258191f}, +{-0.0836661f,-0.298428f,0.254474f},{-0.0729784f,-0.298473f,0.253889f},{0.26649f,0.0916369f,0.117077f}, +{-0.190666f,0.149082f,0.0814894f},{0.0624771f,-0.285798f,0.370689f},{0.0792096f,-0.282055f,0.372721f}, +{0.0985145f,-0.281901f,0.37465f},{0.117839f,-0.282023f,0.372728f},{0.135021f,-0.285078f,0.371737f}, +{-0.384634f,0.285682f,-0.0195363f},{-0.365921f,0.285611f,-0.167583f},{-0.436446f,-0.072017f,-0.00124753f}, +{-0.393296f,0.420102f,-0.300125f},{0.117993f,0.13842f,0.114485f},{-0.00438894f,0.0613614f,0.234333f}, +{0.138333f,0.0583068f,0.250847f},{0.156365f,0.0574451f,0.249677f},{-0.0237452f,0.0260378f,0.254352f}, +{-0.0153532f,0.492762f,-0.111482f},{0.377837f,-0.168593f,-0.0397929f},{-0.185393f,0.0721263f,0.186496f}, +{-0.483801f,0.0392528f,0.243741f},{0.042587f,0.0987364f,0.206804f},{-0.224215f,0.077676f,-0.0580238f}, +{0.230002f,-0.0166554f,-0.0977975f},{-0.4969f,0.0778046f,0.108485f},{0.367573f,-0.299026f,0.205903f}, +{0.380936f,-0.299971f,0.193582f},{0.400666f,-0.305829f,0.201531f},{0.420099f,-0.305752f,0.203434f}, +{0.436356f,-0.306016f,0.20063f},{0.341741f,-5.1417e-05f,0.171615f},{-0.202093f,0.36168f,-0.0182052f}, +{-0.201186f,0.151178f,0.0380438f},{-0.488078f,0.0601395f,0.0764606f},{-0.448252f,-0.0895855f,0.0194399f}, +{-0.393412f,0.344047f,-0.149654f},{0.117459f,0.135951f,0.132729f},{-0.164313f,0.113675f,-0.000199332f}, +{0.0989518f,0.127655f,0.167152f},{-0.0500788f,-0.228308f,0.323366f},{-0.27694f,-0.0338896f,-0.106755f}, +{-0.370396f,0.0185782f,-0.0336966f},{-0.295794f,-0.126658f,-0.111752f},{-0.0936851f,-0.131616f,-0.151423f}, +{-0.238973f,-0.29491f,0.222932f},{-0.202286f,-0.297161f,0.245465f},{-0.0333719f,0.109694f,0.0763513f}, +{-0.0296614f,0.0897592f,0.167371f},{0.0786695f,0.123347f,0.175994f},{-0.168943f,-0.283489f,0.272975f}, +{-0.160821f,-0.281419f,0.276036f},{-0.145825f,-0.285753f,0.278242f},{-0.129748f,-0.285824f,0.277483f}, +{-0.120128f,-0.286074f,0.274557f},{-0.108096f,-0.280833f,0.269747f},{0.400743f,-0.26821f,0.171223f}, +{0.00295487f,-0.278332f,0.355899f},{0.0232565f,-0.279888f,0.357892f},{0.0406385f,-0.278293f,0.364316f}, +{0.0567345f,-0.277907f,0.368689f},{0.141478f,-0.276448f,0.368638f},{0.156448f,-0.27821f,0.365229f}, +{0.175676f,-0.279399f,0.358818f},{0.211045f,0.0524614f,0.239362f},{0.214877f,0.0620109f,0.23178f}, +{0.232105f,0.0548535f,0.232835f},{-0.145909f,0.111578f,0.00139547f},{-0.0464005f,0.0519598f,0.221433f}, +{-0.0185621f,0.057683f,0.231478f},{-0.0368445f,0.0611492f,0.217067f},{-0.0323945f,0.0551622f,0.226597f}, +{-0.19781f,0.074332f,0.192772f},{0.192299f,0.127025f,0.132755f},{-0.205212f,0.0821774f,-0.0591942f}, +{0.388569f,-0.0771293f,0.0370728f},{0.400678f,-0.297257f,0.19555f},{0.420054f,-0.297135f,0.198842f}, +{0.441655f,-0.301283f,0.19654f},{-0.335304f,0.297888f,0.131488f},{-0.0276101f,0.469104f,-0.12489f}, +{0.352101f,-0.0110286f,0.0177551f},{0.360448f,-0.0154014f,0.0383075f},{-0.279878f,-0.245182f,0.263554f}, +{-0.287602f,0.306273f,-0.167165f},{0.193502f,0.121919f,0.155243f},{-0.285531f,0.295643f,0.136697f}, +{-0.313794f,-0.127957f,-0.107437f},{-0.355831f,0.0205974f,-0.0426095f},{-0.130803f,0.359609f,-0.077766f}, +{-0.186859f,0.28138f,-0.0334073f},{-0.24433f,-0.134356f,-0.131822f},{-0.167233f,-0.133996f,-0.132356f}, +{-0.223951f,-0.281438f,0.243188f},{-0.20572f,-0.282975f,0.250712f},{-0.182113f,0.1366f,0.0737919f}, +{0.135484f,0.108517f,0.202894f},{-0.18356f,-0.279534f,0.265901f},{-0.0879747f,-0.278223f,0.260789f}, +{-0.0702068f,-0.279232f,0.261265f},{0.305427f,0.0570914f,0.132311f},{-0.375142f,0.289232f,0.0207582f}, +{0.264046f,0.0732838f,0.189634f},{-0.411848f,0.287926f,0.0950838f},{-0.205019f,0.0557153f,0.210122f}, +{-0.480084f,0.19674f,0.133552f},{0.338147f,0.00136971f,0.0018842f},{-0.476972f,0.230957f,0.175345f}, +{-0.355728f,0.297624f,0.133636f},{-0.246651f,0.279303f,0.167962f},{-0.0530112f,0.0594643f,0.209794f}, +{-0.126539f,0.055741f,0.208636f},{-0.218845f,0.228192f,0.0964406f},{-0.336816f,-0.298267f,0.151834f}, +{-0.213984f,0.22834f,0.0841131f},{-0.391283f,0.306183f,-0.092087f},{0.0407736f,0.0879972f,0.222765f}, +{-0.45168f,-0.00158838f,0.11534f},{-0.455892f,-0.0366419f,0.0770072f},{0.381489f,-0.28293f,0.187782f}, +{0.401958f,-0.285271f,0.18701f},{0.420028f,-0.28419f,0.190418f},{0.440233f,-0.2852f,0.189164f}, +{0.454818f,-0.287116f,0.184952f},{-0.455397f,0.116048f,0.0136652f},{0.393431f,-0.149236f,0.165538f}, +{0.397322f,-0.130832f,0.133475f},{-0.389045f,0.304453f,-0.0738818f},{0.0624385f,0.131861f,0.116678f}, +{0.0188322f,0.124157f,0.0999389f},{-0.39282f,0.284447f,0.190309f},{-0.410717f,0.28057f,0.187344f}, +{-0.411199f,0.14786f,-0.0154207f},{-0.148224f,-0.0326613f,-0.120858f},{-0.277596f,-0.127739f,-0.117417f}, +{-0.109337f,-0.1326f,-0.147597f},{-0.432639f,0.0430211f,0.00153052f},{0.0408443f,-0.128112f,-0.22517f}, +{0.0573968f,-0.129739f,-0.22351f},{-0.352564f,-0.296453f,0.132806f},{-0.275538f,0.0185846f,-0.0867432f}, +{0.193212f,-0.0152085f,-0.111154f},{0.0769332f,-0.0398701f,-0.138259f},{-0.164197f,-0.260545f,0.278866f}, +{-0.145831f,-0.263123f,0.278891f},{-0.126565f,-0.263149f,0.278416f},{-0.0620655f,-0.272274f,0.272744f}, +{0.337304f,0.0145397f,0.15276f},{0.00310277f,0.112209f,0.149577f},{-0.0144401f,0.115237f,0.0962863f}, +{-0.327472f,0.350182f,-0.107154f},{0.00212531f,-0.260641f,0.36076f},{0.0213594f,-0.260738f,0.360857f}, +{0.0393653f,-0.261162f,0.366098f},{0.0534356f,-0.264596f,0.368779f},{0.0611781f,-0.258808f,0.373814f}, +{0.0792032f,-0.259554f,0.376354f},{0.0985724f,-0.259548f,0.375911f},{0.11661f,-0.258937f,0.372213f}, +{0.124263f,-0.264455f,0.369448f},{0.137131f,-0.260082f,0.368824f},{0.156442f,-0.260197f,0.367474f}, +{0.174088f,-0.26149f,0.362419f},{0.119292f,0.139082f,0.0968779f},{-0.2802f,0.290557f,0.171351f}, +{0.229996f,0.110646f,0.132195f},{-0.109022f,0.434558f,-0.0368477f},{-0.422999f,-0.0430147f,-0.0129578f}, +{-0.491306f,0.112672f,0.0954632f},{-0.0883412f,0.282075f,-0.017948f},{-0.374158f,0.301206f,-0.0189126f}, +{0.389772f,-0.0770072f,0.0948587f},{-0.483769f,0.113212f,0.211196f},{0.394511f,-0.28037f,0.180206f}, +{0.342667f,-0.0349056f,-0.0382753f},{-0.473531f,0.156477f,0.0759461f},{0.389534f,-0.149706f,0.175306f}, +{0.0982959f,0.134517f,0.133417f},{-0.337054f,0.333996f,-0.0643002f},{-0.276277f,0.294023f,0.13069f}, +{-0.277306f,0.0785955f,-0.0460114f},{-0.0315778f,-0.0332529f,-0.136658f},{-0.127317f,-0.132041f,-0.143925f}, +{-0.426549f,0.02861f,0.00250155f},{-0.128854f,0.301077f,-0.0589884f},{-0.278753f,-0.469413f,0.302203f}, +{0.0773062f,-0.12761f,-0.219813f},{0.172454f,-0.0427896f,-0.123256f},{-0.187843f,-0.26084f,0.272191f}, +{-0.113729f,-0.258757f,0.276936f},{-0.104051f,-0.259207f,0.271914f},{-0.0879875f,-0.259824f,0.26484f}, +{-0.0720845f,-0.259599f,0.266577f},{-0.0622327f,-0.263258f,0.277489f},{-0.394241f,0.269374f,0.228842f}, +{-0.335729f,0.357956f,-0.114884f},{0.0471721f,-0.255252f,0.371371f},{0.231275f,0.0376065f,0.242577f}, +{0.214254f,0.039047f,0.245555f},{0.193701f,0.0395293f,0.248011f},{0.137041f,0.0399408f,0.264982f}, +{-0.395611f,-0.0363718f,-0.0398958f},{-0.184415f,0.0511366f,0.207254f},{-0.165162f,0.0555931f,0.206206f}, +{0.13061f,0.0531944f,0.257889f},{0.0631459f,0.0534131f,0.257059f},{0.0792997f,0.0532909f,0.260519f}, +{-0.464458f,-0.0707308f,0.13496f},{0.0191151f,0.123244f,0.0768143f},{0.0229864f,0.0375293f,0.259985f}, +{0.0627729f,0.0895791f,0.227163f},{-0.463654f,-0.0535095f,0.115585f},{-0.467435f,0.175088f,0.249355f}, +{-0.335201f,0.126498f,-0.0476833f},{-0.374236f,0.166104f,0.335076f},{-0.386859f,0.219858f,0.297997f}, +{0.380821f,-0.264872f,0.188431f},{0.388351f,-0.261998f,0.177435f},{0.423237f,-0.271322f,0.180637f}, +{0.436163f,-0.271342f,0.18065f},{0.439211f,-0.262004f,0.171268f},{0.456632f,-0.264982f,0.169377f}, +{-0.456857f,0.178387f,0.0565448f},{-0.482386f,0.113315f,0.246545f},{-0.467062f,-0.0924408f,0.115816f}, +{0.194987f,0.116318f,0.171358f},{-0.261352f,0.307244f,0.0568342f},{-0.200421f,0.169313f,0.0971352f}, +{-0.374416f,0.271509f,0.233639f},{-0.335163f,0.112466f,-0.0477927f},{-0.258773f,0.226115f,-0.00123467f}, +{-0.393804f,0.03497f,-0.0210218f},{-0.354018f,0.0368027f,-0.039317f},{-0.355484f,-0.0172342f,-0.0600495f}, +{-0.31609f,0.322536f,-0.355358f},{0.0926755f,-0.130157f,-0.217909f},{-0.335259f,0.149905f,-0.0439343f}, +{-0.181123f,-0.254043f,0.278435f},{0.0232758f,0.118369f,0.151114f},{-0.261442f,0.291328f,0.114311f}, +{0.393675f,-0.243696f,0.164818f},{-0.241166f,0.285862f,0.0968522f},{0.00107712f,-0.243542f,0.364078f}, +{0.0200218f,-0.24322f,0.365615f},{0.0341693f,-0.246654f,0.369499f},{0.0419697f,-0.240822f,0.374194f}, +{0.0599434f,-0.241111f,0.381923f},{0.0792032f,-0.240925f,0.385602f},{0.0986367f,-0.241163f,0.383222f}, +{0.117961f,-0.24178f,0.375203f},{0.137105f,-0.241966f,0.371827f},{0.155162f,-0.241085f,0.371737f}, +{0.162937f,-0.246699f,0.368901f},{0.17383f,-0.244648f,0.364509f},{0.174834f,0.0401402f,0.253387f}, +{0.079274f,0.0400695f,0.267934f},{0.0985466f,0.0401531f,0.269933f},{-0.203836f,0.345873f,0.0429697f}, +{-0.164274f,0.0726086f,-0.0688466f},{-0.358075f,0.0621845f,0.353944f},{-0.145851f,0.0558053f,0.208431f}, +{-0.23478f,0.273348f,0.119552f},{-0.075422f,-0.128607f,-0.188123f},{-0.374101f,0.219845f,0.299508f}, +{-0.487467f,0.0957654f,0.246648f},{0.0421626f,0.379396f,-0.112202f},{-0.494547f,0.0776953f,0.0956625f}, +{-0.185155f,0.361628f,-0.0377029f},{-0.222581f,0.320916f,-0.0162438f},{-0.333845f,0.455805f,-0.360246f}, +{-0.0507476f,0.089682f,0.150297f},{-0.0236616f,0.476936f,-0.0794251f},{0.379033f,-0.132401f,-0.0399087f}, +{-0.301878f,0.374702f,-0.262763f},{0.145664f,0.124092f,0.178522f},{-0.108244f,0.0928395f,0.151944f}, +{0.0404135f,0.126813f,0.0586219f},{0.135054f,-0.127571f,-0.203164f},{-0.0112247f,0.438005f,-0.144606f}, +{-0.072734f,0.359937f,-0.0974245f},{-0.220543f,0.301103f,-0.0205524f},{-0.373818f,0.130832f,-0.0395228f}, +{-0.392614f,-0.243664f,0.169242f},{-0.261757f,-0.243966f,0.268281f},{0.0427349f,-0.0352593f,-0.139185f}, +{0.173277f,-0.0130157f,-0.115823f},{-0.233218f,0.26767f,-0.00997394f},{-0.354545f,0.149802f,-0.0412012f}, +{0.0590817f,-0.038436f,-0.138549f},{0.0962187f,-0.0401659f,-0.135404f},{-0.205199f,-0.245934f,0.271065f}, +{-0.18439f,-0.24041f,0.280261f},{-0.16513f,-0.240243f,0.28266f},{-0.145838f,-0.240417f,0.280621f}, +{-0.126539f,-0.240442f,0.280351f},{-0.110527f,-0.240725f,0.276801f},{-0.100849f,-0.241124f,0.272139f}, +{-0.0879618f,-0.241356f,0.26866f},{-0.0735121f,-0.243021f,0.271695f},{-0.00337933f,0.492537f,-0.113347f}, +{-0.317472f,0.297238f,0.116948f},{-0.0153661f,-0.243426f,0.356516f},{0.0277515f,-0.237652f,0.370252f}, +{0.0180412f,0.0443137f,0.254815f},{0.00199026f,0.0394907f,0.251567f},{-0.353812f,0.421253f,-0.335127f}, +{0.117768f,0.040063f,0.268416f},{-0.413405f,0.0750072f,0.329256f},{-0.374242f,0.0575351f,0.350259f}, +{-0.052844f,-0.108042f,-0.188348f},{0.384242f,-0.0774573f,0.0178387f},{-0.295814f,0.248108f,-0.0951802f}, +{0.374782f,-0.0371177f,0.0563133f},{-0.431687f,-0.0206167f,0.174149f},{-0.262426f,0.0814058f,-0.0432848f}, +{-0.232131f,0.222996f,0.147841f},{-0.498682f,0.0778946f,0.118131f},{0.381869f,-0.0774123f,0.152754f}, +{0.39637f,-0.0947558f,0.0563198f},{0.395785f,-0.0947944f,0.0755731f},{-0.0144465f,0.475528f,-0.0930967f}, +{-0.499794f,0.0954053f,0.134105f},{-0.181695f,0.13424f,0.111701f},{0.0992669f,-0.0618051f,0.315527f}, +{-0.173129f,0.125771f,0.0961706f},{0.0410308f,0.437073f,-0.180007f},{0.0606893f,0.131822f,0.0790457f}, +{0.0422205f,0.128587f,0.0778303f},{-0.299563f,-0.286512f,0.210231f},{-0.224967f,-0.134337f,-0.130665f}, +{-0.240542f,-0.24324f,0.268872f},{-0.222382f,-0.236063f,0.278094f},{-0.203759f,-0.236327f,0.277837f}, +{-0.0662455f,-0.244134f,0.282017f},{-0.0150638f,0.0949101f,0.171576f},{0.136841f,0.0919456f,0.224951f}, +{-0.0157776f,-0.22544f,0.361185f},{0.000794167f,-0.225234f,0.368072f},{0.0213787f,-0.223877f,0.374052f}, +{0.0406257f,-0.223716f,0.376637f},{0.0598534f,-0.223292f,0.38229f},{0.0791775f,-0.222861f,0.387647f}, +{0.0985981f,-0.222919f,0.387023f},{0.117948f,-0.223639f,0.377994f},{0.137118f,-0.223948f,0.373403f}, +{0.156416f,-0.223999f,0.372702f},{-0.090592f,0.0532009f,0.206051f},{-0.34297f,0.0636636f,0.352458f}, +{-0.0703997f,0.290158f,-0.0165332f},{-0.284708f,0.379017f,-0.377833f},{-0.45514f,0.209556f,0.0755989f}, +{-0.494347f,0.11282f,0.114749f},{0.268683f,-0.0564612f,0.282975f},{0.194537f,-0.111244f,0.342343f}, +{-0.296611f,0.363667f,-0.261374f},{-0.297511f,0.3259f,-0.302222f},{0.343941f,0.00187775f,0.0181281f}, +{-0.468908f,0.192592f,0.191454f},{-0.375361f,0.225684f,-0.0372335f},{-0.355014f,0.303019f,-0.000675201f}, +{-0.0488313f,0.474744f,-0.0766149f},{-0.246246f,0.281399f,0.151879f},{-0.257455f,0.269065f,0.208636f}, +{0.174744f,0.100158f,0.210199f},{0.079036f,0.134716f,0.0765378f},{0.0479759f,-0.0634577f,0.315347f}, +{-0.410916f,-0.244957f,0.0971287f},{-0.300309f,-0.225928f,0.270416f},{-0.285795f,-0.230604f,0.273258f}, +{-0.278888f,-0.223343f,0.279052f},{-0.261577f,-0.222134f,0.282904f},{-0.242298f,-0.222289f,0.282107f}, +{-0.22298f,-0.221877f,0.285393f},{-0.203714f,-0.222083f,0.285406f},{-0.184409f,-0.222237f,0.2821f}, +{-0.165091f,-0.22189f,0.285695f},{-0.145857f,-0.222089f,0.283624f},{-0.126565f,-0.222385f,0.2803f}, +{-0.108591f,-0.221703f,0.277496f},{-0.100791f,-0.227491f,0.27358f},{-0.0881419f,-0.223144f,0.272596f}, +{-0.0739558f,-0.223639f,0.284351f},{-0.336217f,0.297823f,0.151391f},{-0.147182f,0.0735861f,0.191962f}, +{-0.29937f,0.29637f,0.113135f},{0.00855597f,-0.219588f,0.371474f},{0.173477f,-0.226604f,0.366233f}, +{-0.0686891f,0.0249445f,0.235433f},{-0.107273f,0.0425002f,0.216681f},{0.156384f,0.0377673f,0.260976f}, +{0.0407028f,0.0399023f,0.263438f},{-0.444516f,0.265792f,0.186676f},{-0.467911f,0.154201f,0.265496f}, +{-0.302463f,0.414012f,-0.326099f},{-0.298592f,0.324703f,-0.321835f},{0.358082f,-0.131404f,-0.0702742f}, +{-0.353754f,-0.0912832f,-0.0882094f},{-0.299068f,-0.470126f,0.283869f},{-0.24132f,0.074795f,-0.0535545f}, +{0.374872f,-0.0370663f,0.0755796f},{-0.442111f,0.186791f,0.0245266f},{-0.288733f,0.291187f,-0.160702f}, +{-0.369458f,0.343667f,-0.225948f},{-0.20646f,0.188856f,0.0770972f},{-0.207276f,0.36332f,-0.00239219f}, +{-0.201894f,0.337012f,0.0508022f},{0.370101f,-0.030417f,0.0964921f},{-0.353863f,0.341475f,-0.266004f}, +{0.270104f,0.0767435f,0.174123f},{-0.0709399f,0.0747307f,0.171718f},{-0.295724f,-0.145172f,-0.112659f}, +{-0.293743f,-0.218173f,0.276744f},{-0.413405f,0.288653f,0.134073f},{-0.0186972f,-0.208701f,0.366625f}, +{0.00206101f,-0.206116f,0.373248f},{0.021353f,-0.205884f,0.376374f},{0.0405871f,-0.205492f,0.381493f}, +{0.0599112f,-0.205466f,0.381756f},{0.0791711f,-0.205145f,0.385036f},{0.0985788f,-0.20519f,0.385107f}, +{0.117954f,-0.20571f,0.37865f},{0.135883f,-0.207164f,0.371152f},{0.156429f,-0.210713f,0.370966f}, +{0.174756f,-0.206218f,0.364522f},{-0.139291f,0.0428411f,0.217427f},{-0.126533f,0.0425131f,0.215742f}, +{-0.0522266f,0.074705f,-0.0390855f},{-0.333394f,0.0598566f,0.34896f},{-0.287885f,0.291277f,-0.131764f}, +{-0.431173f,0.00439213f,0.228957f},{-0.341844f,0.305032f,-0.30435f},{-0.289981f,0.327424f,-0.226411f}, +{-0.128449f,0.417761f,-0.0551043f},{0.311414f,0.0249767f,-0.0201601f},{-0.44314f,0.0112151f,0.176419f}, +{0.0824057f,0.10318f,0.211826f},{-0.0110447f,0.0887367f,0.188309f},{-0.2751f,0.294196f,0.114035f}, +{0.136385f,0.13952f,0.0976303f},{-0.15259f,0.4069f,-0.0203916f},{-0.355439f,0.296917f,0.15249f}, +{-0.298379f,0.284621f,0.207093f},{-0.188627f,0.134748f,0.0421466f},{0.0227613f,-0.0735539f,0.322916f}, +{-0.300161f,-0.204315f,0.28037f},{-0.280792f,-0.203897f,0.284615f},{-0.262837f,-0.204418f,0.289843f}, +{-0.241012f,-0.204463f,0.290389f},{-0.223006f,-0.20344f,0.290955f},{-0.20372f,-0.203415f,0.291714f}, +{-0.184447f,-0.204051f,0.283586f},{-0.165085f,-0.203865f,0.286023f},{-0.145883f,-0.203762f,0.286615f}, +{-0.126617f,-0.20409f,0.282403f},{-0.107267f,-0.204598f,0.277039f},{-0.0923797f,-0.206405f,0.274557f}, +{-0.0837562f,-0.204218f,0.281071f},{-0.355046f,0.297367f,0.112987f},{-0.336307f,-0.150278f,0.285238f}, +{-0.487762f,0.134806f,0.114614f},{-0.482329f,0.0442815f,0.212868f},{-0.0140028f,-0.201762f,0.371712f}, +{0.143561f,-0.201923f,0.369551f},{0.156435f,-0.201974f,0.368638f},{-0.0880261f,0.024996f,0.233221f}, +{0.059982f,0.039973f,0.265908f},{0.348371f,0.00301599f,0.0364619f},{-0.443899f,0.00239864f,0.152715f}, +{-0.184351f,0.0597987f,0.197691f},{-0.210749f,0.187782f,0.114382f},{-0.461558f,-0.0538053f,0.0771036f}, +{-0.10955f,0.418713f,-0.0740105f},{-0.414472f,-0.091817f,-0.0382946f},{0.257905f,0.0859651f,0.173544f}, +{-0.297826f,0.293663f,0.170663f},{-0.451365f,0.272699f,0.0952381f},{-0.39556f,-0.0537346f,0.230526f}, +{0.0591974f,0.130395f,0.0599724f},{-0.375438f,0.296023f,0.132079f},{-0.491286f,0.0431047f,0.115115f}, +{-0.0537957f,-0.127237f,-0.212315f},{-0.255082f,-0.19854f,0.294325f},{-0.248722f,-0.198617f,0.294601f}, +{-0.373792f,0.288287f,0.1911f},{-0.437841f,-0.00634706f,0.157153f},{-0.017186f,-0.18802f,0.376239f}, +{0.00208673f,-0.187865f,0.378702f},{0.0213594f,-0.187898f,0.378142f},{0.0406257f,-0.187563f,0.382972f}, +{0.0599434f,-0.187647f,0.381595f},{0.0792032f,-0.187698f,0.380361f},{0.0985466f,-0.187724f,0.380329f}, +{0.117897f,-0.187975f,0.377743f},{0.135915f,-0.187492f,0.372753f},{0.143619f,-0.193074f,0.369165f}, +{0.156455f,-0.18674f,0.367422f},{0.17401f,-0.184701f,0.361686f},{-0.0700203f,0.055143f,0.207614f}, +{-0.0324266f,0.323289f,-0.0133693f},{-0.445693f,-0.0196842f,0.0576059f},{-0.165226f,0.099881f,-0.0395164f}, +{-0.390203f,0.130298f,0.351384f},{-0.489756f,0.0774895f,0.0763577f},{0.397733f,-0.130909f,0.0178001f}, +{-0.423919f,-0.0212533f,0.186965f},{-0.166274f,0.102022f,0.138716f},{-0.0904505f,0.0690332f,0.183981f}, +{-0.424838f,-0.0223851f,0.00402561f},{-0.415996f,0.0336452f,-0.00662999f},{0.264181f,0.0920742f,0.134542f}, +{-0.318205f,-0.188123f,0.283174f},{-0.300154f,-0.185499f,0.289611f},{-0.280862f,-0.185582f,0.28875f}, +{-0.265815f,-0.186804f,0.292094f},{-0.257217f,-0.183473f,0.296093f},{-0.242266f,-0.184669f,0.299109f}, +{-0.224633f,-0.182792f,0.295489f},{-0.219803f,-0.189782f,0.293573f},{-0.202402f,-0.186226f,0.292949f}, +{-0.184441f,-0.185788f,0.286177f},{-0.165123f,-0.186129f,0.282422f},{-0.145786f,-0.185647f,0.287598f}, +{-0.126623f,-0.18555f,0.288177f},{-0.107254f,-0.186309f,0.279528f},{-0.0896981f,-0.187306f,0.281039f}, +{-0.443513f,0.276358f,0.15258f},{-0.0693836f,0.0744928f,-0.0386868f},{-0.0388637f,0.480525f,-0.0943957f}, +{-0.238401f,0.0368927f,0.232385f},{-0.0433909f,0.0347127f,0.237664f},{-0.0368316f,0.043542f,0.234983f}, +{0.16266f,0.0439021f,0.255214f},{-0.389939f,-0.0189576f,-0.0366419f},{-0.392827f,0.0589627f,0.341256f}, +{0.375991f,-0.116807f,-0.0382431f},{0.0163692f,0.340201f,-0.0590527f},{-0.373039f,0.0968457f,0.351513f}, +{0.118276f,0.0920421f,0.224764f},{-0.429308f,-0.0382046f,-0.000225055f},{-0.457744f,0.0940291f,0.0119932f}, +{-0.49056f,0.0783962f,0.262975f},{-0.469474f,0.264268f,0.132761f},{-0.391174f,0.0139095f,0.310685f}, +{-0.385611f,0.245272f,-0.0363332f},{-0.353696f,0.296132f,0.16404f},{-0.324514f,0.333224f,-0.078229f}, +{-0.445108f,0.278448f,0.118652f},{-0.430279f,0.283663f,0.113212f},{-0.128526f,0.100627f,-0.0182566f}, +{-0.36657f,0.325887f,-0.206386f},{-0.0509855f,0.0969615f,0.131391f},{-0.210132f,-0.180721f,0.294126f}, +{-0.0818463f,-0.180721f,0.294106f},{-0.471004f,0.0357802f,0.191126f},{-0.448297f,-0.000610921f,0.13561f}, +{-0.442606f,-0.0213112f,0.0427639f},{-0.446162f,-0.0722549f,0.0192856f},{-0.0179448f,-0.171043f,0.377518f}, +{0.00211245f,-0.17004f,0.378657f},{0.0213466f,-0.170046f,0.378406f},{0.040645f,-0.169834f,0.38119f}, +{0.0600013f,-0.170014f,0.379062f},{0.0792032f,-0.170033f,0.378354f},{0.0985145f,-0.169911f,0.380059f}, +{0.117845f,-0.169995f,0.379563f},{0.137201f,-0.17022f,0.376792f},{0.153162f,-0.168606f,0.372104f}, +{-0.448915f,0.0937333f,-0.000842398f},{0.23289f,-0.166278f,0.335951f},{-0.204704f,0.284769f,0.0757275f}, +{-0.41471f,0.190798f,-0.0013247f},{-0.410382f,0.208546f,-0.00140187f},{0.0517186f,0.38346f,-0.142471f}, +{0.172467f,0.0976303f,-0.0358766f},{0.392601f,-0.0994695f,0.130214f},{-0.165143f,0.0250153f,0.233234f}, +{-0.29827f,0.296132f,0.150831f},{-0.454452f,0.274158f,0.115559f},{-0.322289f,0.35957f,-0.357005f}, +{0.242156f,0.106408f,0.12179f},{-0.355786f,0.296743f,0.017112f},{0.0784122f,-0.0586798f,0.301765f}, +{-0.319466f,-0.169577f,0.290094f},{-0.300148f,-0.171647f,0.293836f},{-0.300161f,-0.16476f,0.295264f}, +{-0.279596f,-0.168162f,0.292409f},{-0.267975f,-0.167229f,0.292486f},{-0.258265f,-0.166953f,0.295997f}, +{-0.242253f,-0.166612f,0.300723f},{-0.222999f,-0.166676f,0.300248f},{-0.203714f,-0.166805f,0.298601f}, +{-0.190859f,-0.167011f,0.296016f},{-0.181155f,-0.167435f,0.290428f},{-0.16513f,-0.167892f,0.284531f}, +{-0.145748f,-0.167429f,0.290029f},{-0.126636f,-0.167358f,0.29093f},{-0.107286f,-0.16811f,0.281476f}, +{-0.0915051f,-0.168207f,0.282132f},{-0.0812225f,-0.16667f,0.297444f},{-0.336269f,0.43371f,-0.360361f}, +{-0.460194f,-0.0583197f,0.0643581f},{-0.442053f,-0.0419022f,0.0256198f},{-0.46739f,-0.0706922f,0.115688f}, +{-0.437983f,-0.0187454f,0.0340954f},{-0.457654f,0.0156908f,0.0572972f},{0.290991f,0.0614128f,0.172091f}, +{0.214742f,-0.166014f,0.341918f},{-0.0160863f,0.102318f,0.155519f},{-0.19774f,0.0611749f,0.198425f}, +{-0.359625f,0.0961448f,0.352092f},{-0.288039f,0.321392f,-0.205775f},{-0.355407f,0.293135f,0.0276068f}, +{0.188698f,0.125179f,0.147976f},{0.154262f,0.138607f,0.0940549f},{-0.427314f,-0.0734253f,-0.0193563f}, +{0.0803543f,0.0910968f,0.226983f},{-0.411167f,0.2732f,0.208296f},{-0.338648f,0.35368f,-0.102029f}, +{-0.0346195f,-0.146696f,0.362464f},{-0.312951f,-0.162529f,0.294518f},{-0.287312f,-0.162586f,0.294827f}, +{0.0412044f,0.120472f,0.0201666f},{-0.316572f,0.433407f,-0.377158f},{-0.453796f,-0.0718498f,0.0387576f}, +{-0.0511013f,-0.0979711f,-0.165281f},{0.0969969f,0.13352f,0.0388605f},{-0.0171667f,-0.152555f,0.373692f}, +{0.00208029f,-0.152355f,0.37629f},{0.0213466f,-0.152265f,0.377435f},{0.040645f,-0.152239f,0.377911f}, +{0.0599627f,-0.152227f,0.377975f},{0.0792225f,-0.15222f,0.377821f},{0.0985274f,-0.152207f,0.37757f}, +{0.117826f,-0.152175f,0.377834f},{0.13715f,-0.152317f,0.376425f},{0.154892f,-0.152966f,0.372168f}, +{0.060117f,-0.116163f,-0.218372f},{0.31025f,0.043169f,0.171075f},{-0.390357f,0.0435742f,0.33777f}, +{-0.200421f,0.0423909f,0.216038f},{-0.184435f,0.0424231f,0.216263f},{-0.373863f,0.291676f,0.0576702f}, +{0.398563f,-0.112672f,0.0948651f},{0.284779f,0.036822f,-0.0329957f},{-0.356718f,0.368072f,-0.127121f}, +{0.362004f,-0.11354f,-0.0547699f},{0.37592f,-0.0959776f,0.184907f},{-0.352371f,0.329661f,-0.0511109f}, +{-0.350416f,0.349609f,-0.0879136f},{-0.335491f,0.306376f,-0.0183081f},{-0.144269f,0.413292f,-0.0335102f}, +{-0.323736f,-0.150741f,0.292306f},{-0.31508f,-0.147416f,0.296183f},{-0.300141f,-0.148715f,0.298659f}, +{-0.280882f,-0.148703f,0.298859f},{-0.261558f,-0.148896f,0.296454f},{-0.242253f,-0.1486f,0.300537f}, +{-0.222999f,-0.148433f,0.302981f},{-0.203739f,-0.148516f,0.301894f},{-0.187541f,-0.148992f,0.29583f}, +{-0.17792f,-0.149333f,0.291303f},{-0.165162f,-0.149828f,0.285136f},{-0.14578f,-0.149487f,0.289341f}, +{-0.126507f,-0.149468f,0.289354f},{-0.107247f,-0.149706f,0.285361f},{-0.0910871f,-0.149449f,0.289058f}, +{-0.0810617f,-0.15332f,0.29628f},{0.0787981f,0.131018f,0.0376194f},{0.135594f,0.138047f,0.0597023f}, +{-0.0820135f,0.450468f,-0.0409954f},{0.301183f,0.0536382f,0.169024f},{-0.485132f,0.0616443f,0.282312f}, +{0.212131f,-0.149873f,0.339121f},{0.232433f,-0.146503f,0.328195f},{0.248754f,-0.148066f,0.325469f}, +{-0.340905f,-0.186457f,0.272332f},{-0.0107617f,0.0523713f,0.2385f},{0.0427671f,0.0982798f,-0.035986f}, +{-0.0806887f,-0.12606f,-0.173506f},{0.382197f,-0.0955403f,0.172059f},{0.355509f,-0.0540304f,-0.0333044f}, +{-0.041063f,0.458423f,-0.0611106f},{-0.406119f,-0.0569306f,0.221491f},{-0.445584f,-0.111585f,0.0194849f}, +{-0.467069f,-0.0925436f,0.0965628f},{-0.495499f,0.115353f,0.154947f},{-0.44869f,-0.00164623f,0.0768464f}, +{-0.392023f,0.306074f,-0.111411f},{-0.076496f,-0.147506f,0.303444f},{0.174756f,0.134362f,0.0740491f}, +{0.193958f,0.128851f,0.0764992f},{0.202891f,0.125372f,0.0889489f},{-0.0161249f,-0.128883f,0.364271f}, +{-0.0108711f,-0.139282f,0.370477f},{0.00423457f,-0.13323f,0.372734f},{0.0213594f,-0.130105f,0.374708f}, +{0.0406514f,-0.130099f,0.374599f},{0.0599434f,-0.134394f,0.377383f},{0.0792675f,-0.130028f,0.37503f}, +{0.0985466f,-0.130182f,0.372824f},{0.116559f,-0.135539f,0.372322f},{0.137111f,-0.139237f,0.371872f}, +{0.156403f,-0.130774f,0.363783f},{0.175573f,-0.130221f,0.358779f},{-0.184473f,0.336015f,0.0502106f}, +{-0.461281f,-0.0544227f,0.13352f},{-0.355394f,0.323424f,-0.246577f},{-0.451744f,-0.00167199f,0.0960677f}, +{-0.488778f,0.0948844f,0.0762548f},{0.0491334f,0.377615f,-0.128253f},{-0.290123f,0.281174f,-0.147828f}, +{-0.316212f,0.293643f,0.0745378f},{-0.0885084f,0.00246939f,-0.107488f},{-0.245648f,0.267271f,0.188579f}, +{-0.35522f,-0.131314f,0.277071f},{-0.3365f,-0.131655f,0.284145f},{-0.0710685f,0.0887624f,-0.000945289f}, +{-0.0934343f,0.0889425f,-0.00949164f},{-0.322739f,-0.130086f,0.292222f},{-0.315183f,-0.132356f,0.295907f}, +{-0.300141f,-0.130523f,0.300273f},{-0.280882f,-0.130414f,0.301405f},{-0.261577f,-0.1306f,0.299547f}, +{-0.242304f,-0.1306f,0.29927f},{-0.223006f,-0.130626f,0.299257f},{-0.20372f,-0.130697f,0.297836f}, +{-0.190917f,-0.135443f,0.295482f},{-0.182994f,-0.130414f,0.289052f},{-0.165085f,-0.131719f,0.284235f}, +{-0.145838f,-0.131475f,0.287598f},{-0.126507f,-0.131237f,0.289669f},{-0.107228f,-0.135597f,0.291778f}, +{-0.0879168f,-0.13098f,0.293013f},{-0.0879425f,-0.130915f,0.294621f},{-0.0714736f,-0.130446f,0.305527f}, +{0.23069f,0.113077f,0.0939777f},{-0.00217037f,-0.124543f,0.368593f},{0.119446f,-0.123707f,0.368496f}, +{0.137131f,-0.126099f,0.365795f},{0.115916f,-0.127546f,-0.208752f},{0.0421819f,0.35959f,-0.110189f}, +{0.231108f,-0.129925f,0.322504f},{0.252638f,-0.130735f,0.320858f},{-0.345188f,0.0937848f,0.351076f}, +{0.335356f,0.013035f,0.165834f},{0.271017f,0.0599081f,-0.0193498f},{-0.298412f,0.278538f,0.223858f}, +{-0.328951f,0.294306f,-0.000379391f},{-0.418363f,-0.014051f,-0.00224428f},{-0.4484f,-0.0194592f,0.076885f}, +{-0.45278f,0.173801f,0.037285f},{-0.107222f,-0.126234f,0.295116f},{0.24287f,0.106646f,0.0991222f}, +{0.286901f,0.0755024f,0.11489f},{0.276155f,0.0845375f,0.111495f},{0.0599434f,-0.116639f,0.373654f}, +{0.189489f,-0.132259f,0.354304f},{0.100566f,0.0614064f,-0.078184f},{-0.320572f,0.340767f,-0.359242f}, +{-0.336127f,0.0973602f,0.345577f},{0.392601f,-0.0814186f,0.0563262f},{-0.475049f,0.0359538f,0.206598f}, +{-0.202871f,0.0996367f,-0.0388219f},{-0.316951f,0.284415f,0.00184562f},{-0.277454f,0.294537f,0.0929809f}, +{-0.411881f,0.0543841f,-0.0163403f},{-0.354937f,-0.114202f,0.277496f},{-0.338848f,-0.113758f,0.281329f}, +{-0.319877f,-0.114202f,0.288807f},{-0.298855f,-0.113508f,0.297515f},{-0.280856f,-0.11235f,0.300833f}, +{-0.26159f,-0.112273f,0.301514f},{-0.242311f,-0.112556f,0.297701f},{-0.221404f,-0.115038f,0.294524f}, +{-0.207964f,-0.111225f,0.295174f},{-0.199309f,-0.114434f,0.292441f},{-0.184338f,-0.113205f,0.288563f}, +{-0.165117f,-0.11352f,0.284197f},{-0.145799f,-0.113373f,0.28655f},{-0.130745f,-0.114434f,0.292351f}, +{-0.122173f,-0.111205f,0.295257f},{-0.107202f,-0.112594f,0.296164f},{-0.0895438f,-0.110395f,0.295277f}, +{-0.0847208f,-0.11736f,0.293862f},{-0.0688177f,-0.113231f,0.299566f},{0.314353f,0.048365f,0.130472f}, +{0.296232f,0.0672069f,0.114408f},{0.00345646f,-0.109315f,0.363069f},{0.0200218f,-0.107328f,0.365249f}, +{0.0278029f,-0.112376f,0.370966f},{0.0406385f,-0.112299f,0.371165f},{0.0792482f,-0.112266f,0.371261f}, +{0.0921224f,-0.112318f,0.369847f},{0.0998071f,-0.107225f,0.367133f},{0.117819f,-0.108222f,0.364914f}, +{0.137118f,-0.108517f,0.359686f},{0.154744f,-0.111231f,0.353577f},{0.169187f,-0.113726f,0.351455f}, +{0.176898f,-0.108588f,0.347018f},{-0.467133f,0.174959f,0.210861f},{0.231218f,-0.113398f,0.318016f}, +{0.251828f,-0.112318f,0.316897f},{-0.359857f,0.393962f,-0.302936f},{-0.208434f,0.229272f,0.0730202f}, +{-0.358165f,0.219794f,0.298222f},{-0.088547f,0.37865f,-0.0349249f},{-0.24251f,0.327996f,0.0392592f}, +{-0.463049f,-0.112775f,0.153268f},{-0.438703f,0.274789f,0.168599f},{0.165303f,0.106826f,0.204071f}, +{-0.453513f,-0.111006f,0.0386933f},{-0.306514f,-0.108421f,0.293007f},{-0.226227f,-0.108337f,0.293045f}, +{0.333002f,0.0246938f,0.134394f},{0.347587f,0.00345971f,0.133533f},{0.338957f,0.0162631f,0.133475f}, +{0.0406707f,-0.103675f,0.367075f},{0.0599434f,-0.103566f,0.367712f},{0.0792289f,-0.103592f,0.367641f}, +{0.159477f,-0.104897f,0.348362f},{-0.290444f,0.419639f,-0.358619f},{-0.108759f,0.0726922f,0.186817f}, +{-0.265885f,0.250294f,0.225896f},{-0.335877f,0.29619f,0.167975f},{0.0435195f,-0.0766727f,0.33979f}, +{-0.186865f,-0.0281727f,-0.11972f},{-0.352957f,-0.0965949f,0.276917f},{-0.338732f,-0.0955661f,0.281258f}, +{-0.319453f,-0.0954246f,0.283238f},{-0.301447f,-0.0940806f,0.28956f},{-0.29364f,-0.0991994f,0.295386f}, +{-0.28083f,-0.0990579f,0.297534f},{-0.261596f,-0.0989743f,0.298177f},{-0.24878f,-0.0991415f,0.296164f}, +{-0.241012f,-0.0939198f,0.291701f},{-0.226195f,-0.0947751f,0.292621f},{-0.216536f,-0.0945114f,0.296106f}, +{-0.206923f,-0.0944471f,0.296865f},{-0.197109f,-0.0947172f,0.293367f},{-0.184235f,-0.0950259f,0.288338f}, +{-0.165072f,-0.0954438f,0.282718f},{-0.145786f,-0.0953667f,0.28421f},{-0.127684f,-0.0940548f,0.290788f}, +{-0.120019f,-0.0992058f,0.29446f},{-0.105929f,-0.0954053f,0.296241f},{-0.0879361f,-0.0943121f,0.298826f}, +{-0.069956f,-0.0952252f,0.298318f},{-0.0487605f,-0.0967557f,0.301926f},{-0.181438f,0.136401f,0.0929488f}, +{-0.0902769f,0.101141f,0.113881f},{0.0040931f,-0.0975982f,0.353577f},{0.0213594f,-0.0953667f,0.354574f}, +{0.0420018f,-0.0918363f,0.357538f},{0.0599562f,-0.0907559f,0.35831f},{0.0792354f,-0.0907495f,0.358773f}, +{0.0985145f,-0.0908138f,0.358329f},{0.117716f,-0.0909424f,0.355905f},{0.133748f,-0.0955853f,0.354484f}, +{0.138578f,-0.0892833f,0.347783f},{0.156268f,-0.0918363f,0.341719f},{0.173933f,-0.0944986f,0.336664f}, +{0.19156f,-0.0969615f,0.333533f},{-0.26022f,0.285888f,0.170528f},{0.213739f,-0.0920099f,0.319154f}, +{0.233475f,-0.093296f,0.314524f},{0.251166f,-0.0991415f,0.313752f},{-0.374191f,0.0666924f,0.352407f}, +{-0.451043f,-0.0192405f,0.0961448f},{-0.332269f,-0.219575f,0.259066f},{0.16922f,0.0707759f,0.240893f}, +{0.338417f,-0.0910389f,-0.0671361f},{0.346854f,-0.0555995f,0.209562f},{0.261487f,0.087174f,0.165654f}, +{0.0286261f,0.125449f,0.0756375f},{-0.129755f,0.108183f,0.00322178f},{-0.392241f,0.283785f,0.039973f}, +{-0.228768f,0.249053f,0.13489f},{-0.447918f,0.154857f,0.0198f},{-0.28085f,-0.0902543f,0.293039f}, +{-0.26159f,-0.0902672f,0.292994f},{-0.11362f,-0.0901707f,0.293765f},{-0.0622456f,-0.0902736f,0.291688f}, +{-0.0538922f,-0.0907688f,0.293026f},{0.134308f,-0.0709559f,0.325321f},{-0.470091f,0.152883f,0.210977f}, +{0.0999292f,-0.0751423f,0.343082f},{0.00420885f,-0.0888267f,0.342671f},{0.0204013f,-0.0871612f,0.346156f}, +{0.0343429f,-0.0868396f,0.348561f},{0.178763f,-0.0881322f,0.330054f},{0.19646f,-0.0908845f,0.326806f}, +{0.253796f,-0.0897463f,0.309816f},{0.156384f,0.070808f,0.241735f},{-0.468966f,0.0231054f,0.152175f}, +{0.280695f,0.0716119f,0.166708f},{-0.357554f,0.245761f,-0.184843f},{0.374049f,-0.0370535f,0.0402302f}, +{-0.186544f,-0.115649f,-0.13042f},{-0.335002f,0.0190412f,-0.0548728f},{-0.162197f,0.122311f,0.0764606f}, +{-0.354558f,-0.0732066f,0.268615f},{-0.337439f,-0.0784991f,0.277329f},{-0.319511f,-0.0774187f,0.27992f}, +{-0.300167f,-0.0772065f,0.283534f},{-0.280869f,-0.0770072f,0.286615f},{-0.261596f,-0.0769043f,0.287316f}, +{-0.242278f,-0.0769107f,0.287534f},{-0.222974f,-0.0768271f,0.289772f},{-0.20372f,-0.0766727f,0.290885f}, +{-0.18439f,-0.0770264f,0.285644f},{-0.16513f,-0.0773223f,0.281798f},{-0.145883f,-0.0773737f,0.280351f}, +{-0.126475f,-0.0771165f,0.285656f},{-0.108514f,-0.0758368f,0.290955f},{-0.100752f,-0.0810135f,0.295367f}, +{-0.0879618f,-0.0809556f,0.29655f},{-0.0686634f,-0.0767049f,0.291386f},{-0.0557764f,-0.081052f,0.293881f}, +{-0.050201f,-0.0766663f,0.296762f},{-0.0362336f,-0.0759976f,0.29799f},{0.40091f,-0.245092f,0.150394f}, +{-0.0724446f,0.0829427f,0.152985f},{0.402138f,-0.256538f,0.157493f},{-0.490245f,0.0784348f,0.246738f}, +{-0.210395f,0.208585f,0.0772065f},{-0.292573f,0.346523f,-0.263657f},{0.134423f,-0.0784412f,0.33788f}, +{0.156352f,-0.0750201f,0.324215f},{0.175657f,-0.0749815f,0.322626f},{0.194833f,-0.0749622f,0.319424f}, +{0.212852f,-0.0760747f,0.314954f},{0.227038f,-0.0798431f,0.313025f},{0.234697f,-0.0746085f,0.310575f}, +{0.250323f,-0.0727565f,0.304672f},{0.0985466f,0.0534002f,0.261689f},{-0.16412f,0.0707566f,0.18865f}, +{-0.316167f,0.149918f,-0.0431754f},{-0.390055f,0.201903f,0.312916f},{-0.377869f,0.291637f,-0.000598033f}, +{0.117048f,0.126986f,0.171859f},{0.228131f,0.107681f,0.147397f},{-0.298328f,0.289502f,0.188965f}, +{-0.171721f,0.126465f,0.0792193f},{-0.394267f,0.293843f,0.118659f},{-0.368435f,0.269805f,-0.18838f}, +{-0.343079f,-0.0673612f,0.271503f},{-0.0879618f,-0.0721327f,0.292659f},{-0.0188386f,-0.0762934f,0.300775f}, +{-0.431944f,0.281341f,0.151494f},{-0.319935f,0.295103f,0.0825183f},{-0.449281f,0.0184367f,0.188753f}, +{-0.495003f,0.077213f,0.174181f},{-0.299055f,0.379898f,-0.382258f},{-0.454696f,-0.0543648f,0.153725f}, +{-0.393733f,0.325218f,-0.129874f},{-0.373265f,-0.262551f,0.170258f},{-0.291672f,0.307296f,-0.114421f}, +{0.0441111f,0.40254f,-0.166966f},{-0.468316f,-0.114234f,0.0966721f},{0.220575f,-0.0708402f,0.312273f}, +{-0.351915f,0.39645f,-0.316678f},{-0.262812f,-0.285476f,0.229433f},{0.35421f,-0.0017234f,0.0884666f}, +{-0.205791f,-0.115848f,-0.128941f},{0.0423748f,-0.0679528f,0.322678f},{-0.112154f,0.0929166f,-0.0174592f}, +{-0.338815f,-0.0536574f,0.266815f},{-0.318192f,-0.0603067f,0.277399f},{-0.300148f,-0.0547378f,0.279798f}, +{-0.280869f,-0.0545706f,0.282075f},{-0.261583f,-0.0544162f,0.28419f},{-0.242285f,-0.0545127f,0.282068f}, +{-0.223012f,-0.0546413f,0.279824f},{-0.202794f,-0.0572007f,0.28174f},{-0.184409f,-0.0545899f,0.280403f}, +{-0.16585f,-0.0558438f,0.278448f},{-0.145844f,-0.0640237f,0.276596f},{-0.126539f,-0.059355f,0.278518f}, +{-0.106437f,-0.0557217f,0.281663f},{-0.0879618f,-0.0543713f,0.284711f},{-0.069538f,-0.056847f,0.288081f}, +{-0.0525224f,-0.0628726f,0.294936f},{-0.0349024f,-0.0605189f,0.295386f},{-0.0236681f,-0.0628726f,0.295888f}, +{-0.0159191f,-0.0575223f,0.291746f},{0.00101924f,-0.0573551f,0.29255f},{0.0210186f,-0.0616893f,0.296447f}, +{0.037121f,-0.0612199f,0.300068f},{0.0586637f,-0.0628147f,0.316852f},{-0.336025f,-0.204122f,0.266287f}, +{-0.467313f,0.0428668f,0.0391242f},{-0.225179f,0.228018f,0.115655f},{0.117453f,-0.0579981f,0.303013f}, +{0.137375f,-0.0581846f,0.304575f},{0.154191f,-0.0535481f,0.307862f},{0.159882f,-0.0619465f,0.315077f}, +{0.177039f,-0.058146f,0.316704f},{0.193579f,-0.0581074f,0.315887f},{0.207572f,-0.0610977f,0.313475f}, +{0.215424f,-0.0565769f,0.310035f},{0.231745f,-0.0562232f,0.30453f},{0.137086f,0.0706859f,0.241111f}, +{-0.378223f,0.24814f,-0.0724864f},{-0.335221f,0.135822f,-0.0458699f},{-0.399457f,-0.0121604f,-0.0235491f}, +{-0.0535578f,0.379184f,-0.110112f},{-0.373367f,-0.0371435f,-0.0575544f},{-0.341786f,0.0280763f,0.335539f}, +{-0.308058f,0.311559f,-0.0721327f},{-0.401502f,0.251773f,0.254165f},{-0.373033f,-0.0535931f,0.248455f}, +{-0.35549f,-0.055876f,0.259921f},{-0.321003f,-0.0483972f,0.272152f},{-0.145857f,-0.0505321f,0.271638f}, +{-0.0525482f,-0.0540433f,0.291367f},{-0.0397254f,-0.0538568f,0.293502f},{0.0228385f,-0.0523263f,0.291245f}, +{-0.30326f,0.295431f,0.163101f},{0.0406771f,-0.0539018f,0.293322f},{0.0602585f,-0.0515353f,0.292653f}, +{-0.457262f,-0.0493746f,0.141185f},{0.0962509f,-0.0508729f,0.29354f},{0.101492f,-0.0563455f,0.300158f}, +{0.169477f,-0.0530979f,0.3128f},{0.201296f,-0.0528472f,0.312299f},{0.117749f,0.053233f,0.260467f}, +{0.390685f,-0.111643f,0.156985f},{-0.475345f,0.161223f,0.114472f},{-0.261197f,0.288878f,0.151693f}, +{-0.0545738f,0.307116f,-0.00269443f},{0.136841f,0.127707f,0.169808f},{-0.361349f,-0.0472461f,0.253355f}, +{-0.206948f,-0.0458634f,0.274229f},{-0.158558f,-0.0459149f,0.272531f},{-0.126572f,-0.0460757f,0.271059f}, +{-0.113704f,-0.0459535f,0.273689f},{-0.0673773f,-0.0400694f,0.280814f},{-0.0525675f,-0.0362882f,0.28356f}, +{-0.0365037f,-0.036121f,0.286338f},{-0.0172245f,-0.0361146f,0.28664f},{0.00206101f,-0.0360632f,0.286518f}, +{0.0208578f,-0.0357095f,0.289142f},{-0.392351f,0.292659f,0.0961898f},{0.0406771f,-0.0358573f,0.29028f}, +{0.0598984f,-0.035285f,0.291958f},{0.0781615f,-0.0362239f,0.293122f},{-0.475621f,0.174612f,0.133687f}, +{0.0998392f,-0.0348349f,0.29282f},{0.117716f,-0.0360889f,0.289277f},{0.136314f,-0.0352207f,0.285862f}, +{0.14387f,-0.045259f,0.296479f},{0.160956f,-0.0419407f,0.299695f},{0.17583f,-0.0401852f,0.302826f}, +{0.194936f,-0.0400952f,0.303386f},{0.214118f,-0.040243f,0.299997f},{0.400627f,-0.130632f,0.0948716f}, +{0.332828f,0.00700302f,-0.00363974f},{-0.330687f,0.0772065f,0.348323f},{-0.437873f,-0.0194528f,0.157249f}, +{-0.294431f,0.286209f,-0.0771293f},{-0.29355f,0.247529f,-0.0371885f},{0.0945532f,0.12343f,0.178676f}, +{-0.0610366f,0.0857529f,0.151423f},{-0.407038f,0.27657f,0.20301f},{-0.373811f,-0.0350985f,0.244642f}, +{-0.356866f,-0.0345455f,0.252011f},{-0.337137f,-0.0355423f,0.258706f},{-0.319485f,-0.0328349f,0.264853f}, +{-0.300148f,-0.032597f,0.270011f},{-0.282489f,-0.0302112f,0.270975f},{-0.277653f,-0.036822f,0.27556f}, +{-0.261583f,-0.0366419f,0.276969f},{-0.245507f,-0.0366934f,0.275818f},{-0.240671f,-0.0301212f,0.271554f}, +{-0.223006f,-0.0325391f,0.269052f},{-0.203727f,-0.0325648f,0.269348f},{-0.184435f,-0.0366419f,0.276364f}, +{-0.171515f,-0.0366676f,0.276287f},{-0.163728f,-0.0314395f,0.270943f},{-0.145728f,-0.0327385f,0.26304f}, +{-0.126591f,-0.0328221f,0.263175f},{-0.107337f,-0.0325198f,0.26794f},{-0.087949f,-0.0367898f,0.27574f}, +{0.0959036f,-0.034224f,0.294183f},{0.155201f,-0.0310601f,0.28484f},{0.232279f,-0.0337417f,0.288016f}, +{-0.13297f,0.0688852f,0.199402f},{0.391347f,-0.0905115f,0.120575f},{-0.222125f,0.188631f,0.0190476f}, +{-0.327948f,-0.150542f,-0.104074f},{-0.315054f,-0.145494f,-0.109051f},{-0.341986f,-0.0289958f,0.25367f}, +{-0.261596f,-0.0278126f,0.27212f},{-0.184415f,-0.0277805f,0.272699f},{-0.0880004f,-0.0278705f,0.270538f}, +{-0.0686055f,-0.0279026f,0.273438f},{0.0856082f,-0.0265715f,0.29401f},{0.17585f,-0.0274011f,0.289965f}, +{0.194974f,-0.0273303f,0.290267f},{0.214189f,-0.027311f,0.287695f},{0.194672f,0.06612f,0.235484f}, +{0.267969f,-0.0357545f,0.266493f},{-0.466728f,0.262821f,0.150381f},{0.385425f,-0.13188f,-0.0206167f}, +{0.389643f,-0.0724414f,0.075586f},{-0.281467f,0.0204109f,0.24486f},{-0.261525f,0.0205717f,0.241574f}, +{-0.310392f,0.290357f,-0.0356644f},{-0.145876f,0.111668f,0.115758f},{-0.432285f,0.0749429f,-0.0124433f}, +{-0.313697f,0.378174f,-0.37222f},{-0.167541f,-0.0280248f,-0.121835f},{-0.354661f,-0.0164046f,0.251362f}, +{-0.337542f,-0.0145526f,0.251555f},{-0.319485f,-0.0195621f,0.258197f},{-0.300148f,-0.0149705f,0.260056f}, +{-0.280862f,-0.014887f,0.261554f},{-0.261596f,-0.0147005f,0.263207f},{-0.242291f,-0.0145526f,0.263927f}, +{-0.222999f,-0.0146362f,0.261291f},{-0.203733f,-0.0146362f,0.263233f},{-0.184415f,-0.0144947f,0.265516f}, +{-0.164975f,-0.0144047f,0.263085f},{-0.148789f,-0.0191891f,0.257831f},{-0.144102f,-0.0126362f,0.253651f}, +{-0.127877f,-0.0141346f,0.253747f},{-0.120244f,-0.0194013f,0.257014f},{-0.106038f,-0.0157037f,0.259406f}, +{-0.0880133f,-0.0146104f,0.26248f},{-0.0687213f,-0.0144304f,0.26585f},{-0.0526189f,-0.0141989f,0.271464f}, +{-0.0365165f,-0.018411f,0.27801f},{-0.0172053f,-0.0138709f,0.278679f},{0.00208029f,-0.0137295f,0.280505f}, +{0.0214173f,-0.0134787f,0.285586f},{0.0407221f,-0.01335f,0.287695f},{0.0600013f,-0.0131635f,0.291328f}, +{0.0792225f,-0.0131957f,0.29118f},{0.0985016f,-0.0144626f,0.289393f},{0.117729f,-0.0134851f,0.284865f}, +{0.137124f,-0.0134915f,0.279895f},{0.154763f,-0.016321f,0.275908f},{0.17574f,-0.0187775f,0.277612f}, +{0.195f,-0.0187132f,0.281303f},{0.214209f,-0.0186554f,0.279039f},{0.232587f,-0.022443f,0.277393f}, +{0.234607f,-0.0142053f,0.268654f},{0.25132f,-0.0183917f,0.265387f},{0.269866f,-0.0204881f,0.259439f}, +{-0.120006f,0.380965f,0.00785186f},{0.372056f,-0.0647696f,-0.00768462f},{-0.243892f,0.0184367f,0.240076f}, +{-0.239089f,0.0251375f,0.2358f},{-0.0170766f,0.0842352f,0.193164f},{-0.263885f,0.147224f,-0.0295231f}, +{-0.18664f,-0.0739912f,-0.129475f},{0.000311863f,0.455419f,-0.155603f},{-0.150854f,0.281258f,-0.0423073f}, +{0.20698f,-0.0177743f,-0.10689f},{-0.319511f,-0.0106427f,0.254622f},{-0.113922f,-0.0103469f,0.254899f}, +{-0.0365615f,-0.00949807f,0.273014f},{0.159561f,-0.00969741f,0.272583f},{0.175689f,-0.00992251f,0.269599f}, +{0.195f,-0.00995464f,0.272146f},{0.214221f,-0.00994179f,0.270396f},{0.394685f,-0.113077f,0.0178708f}, +{0.400666f,-0.148715f,0.114189f},{-0.222929f,0.0250667f,0.234173f},{-0.475608f,0.112221f,0.0505772f}, +{0.077692f,-0.019337f,-0.134529f},{-0.316244f,0.168773f,-0.0416707f},{-0.110032f,0.377866f,-0.0883573f}, +{-0.296997f,0.14986f,-0.0396772f},{-0.315826f,0.0936369f,-0.0470403f},{0.190602f,-0.0323334f,-0.116961f}, +{0.0620462f,-0.0157037f,-0.135423f},{-0.20372f,0.0249639f,0.233382f},{-0.111054f,0.321475f,-0.0743384f}, +{-0.316868f,0.0550658f,-0.0475161f},{-0.337446f,-0.0023536f,0.255272f},{-0.318115f,0.00142761f,0.253362f}, +{-0.299672f,0.00104178f,0.252127f},{-0.280965f,0.00269444f,0.250243f},{-0.26159f,0.00277804f,0.250114f}, +{-0.242298f,0.00290668f,0.250043f},{-0.223019f,0.00295167f,0.25086f},{-0.205019f,0.00393556f,0.251522f}, +{-0.197296f,-0.00129897f,0.256673f},{-0.187644f,-0.0012604f,0.256731f},{-0.182808f,0.00538888f,0.250545f}, +{-0.165053f,0.00322176f,0.250944f},{-0.145741f,0.00293882f,0.246532f},{-0.126552f,0.00269444f,0.244899f}, +{-0.107318f,0.0028745f,0.247227f},{-0.0880004f,0.00292596f,0.24967f},{-0.070374f,0.00535674f,0.250912f}, +{-0.0655895f,-0.00108035f,0.257329f},{-0.0516415f,0.00210285f,0.261696f},{-0.0365937f,0.00380054f,0.265612f}, +{-0.0172245f,0.00390985f,0.270017f},{0.000485494f,0.00627631f,0.272699f},{0.00534707f,-0.000321504f,0.277117f}, +{0.0214173f,0.00432143f,0.27902f},{0.0407607f,0.00445003f,0.282512f},{0.0600077f,0.00450149f,0.284499f}, +{0.0792675f,0.00448859f,0.284557f},{0.0985081f,0.00456576f,0.284165f},{0.117723f,0.00455291f,0.283419f}, +{0.138642f,0.00290668f,0.279039f},{0.156853f,0.00437928f,0.2714f},{0.175657f,0.00355616f,0.265027f}, +{0.194974f,0.0035176f,0.265368f},{0.214241f,0.00346614f,0.263342f},{0.231874f,0.000932473f,0.260339f}, +{0.24642f,-0.00137614f,0.257316f},{0.252175f,0.00538245f,0.251587f},{0.26894f,0.00381982f,0.247175f}, +{0.322064f,-0.0572007f,0.2416f},{-0.291949f,0.326305f,-0.244404f},{0.269679f,-0.0710202f,-0.116948f}, +{-0.216819f,0.248796f,-0.00958167f},{-0.0730684f,-0.0932189f,-0.147172f},{-0.354384f,0.00255942f,0.286846f}, +{-0.148301f,-0.0143211f,-0.117212f},{-0.167258f,-0.115186f,-0.135996f},{-0.202228f,0.26403f,-0.0192791f}, +{-0.240812f,0.26194f,-0.00583259f},{-0.321658f,-0.108916f,-0.100903f},{-0.406144f,-0.109861f,-0.0514581f}, +{-0.184435f,0.0248224f,0.232462f},{-0.0559564f,0.00782613f,0.254391f},{0.236787f,0.00754962f,0.253677f}, +{-0.471461f,0.153892f,0.192476f},{0.354989f,-0.0195428f,0.165815f},{-0.0901997f,-0.0957333f,-0.143802f}, +{-0.354236f,0.000160776f,-0.0532459f},{0.134655f,-0.0128999f,-0.11999f},{-0.22026f,0.262988f,-0.0135172f}, +{-0.0951127f,0.335848f,-0.0834057f},{-0.0741166f,-0.106421f,-0.153333f},{-0.377425f,0.102852f,0.350574f}, +{-0.223025f,0.0160702f,0.241124f},{-0.203733f,0.0163467f,0.240127f},{-0.184435f,0.0163724f,0.239883f}, +{-0.165117f,0.0162567f,0.239793f},{-0.145761f,0.0162889f,0.238417f},{-0.100932f,0.0162567f,0.237523f}, +{-0.0880326f,0.0161667f,0.238951f},{-0.0687277f,0.0162567f,0.242089f},{-0.186582f,-0.0970837f,-0.130993f}, +{-0.205849f,-0.0971673f,-0.131198f},{-0.225983f,-0.0937719f,-0.131147f},{-0.244613f,-0.0927558f,-0.129166f}, +{0.0436095f,-0.0169962f,-0.133642f},{-0.408382f,0.166136f,-0.0139224f},{-0.148024f,-0.0964985f,-0.136401f}, +{-0.167329f,-0.09673f,-0.134388f},{-0.0181698f,-0.468686f,0.352194f},{-0.0138485f,-0.474975f,0.347751f}, +{-0.277743f,0.131031f,-0.0370406f},{-0.392981f,0.111701f,-0.0340182f},{-0.36958f,-0.147365f,-0.0868654f}, +{-0.335568f,-0.145288f,-0.100267f},{-0.244214f,-0.153127f,-0.128105f},{-0.0341115f,0.355397f,-0.101212f}, +{0.0795183f,-0.471322f,0.319308f},{-0.205611f,-0.152838f,-0.129764f},{-0.354609f,0.168599f,-0.0385261f}, +{-0.27928f,0.0571686f,0.270982f},{0.28728f,0.037973f,0.209768f},{-0.196344f,0.15885f,0.115514f}, +{-0.0909392f,0.321437f,-0.0765248f},{-0.411855f,0.0734253f,-0.021562f},{-0.0920967f,0.359847f,-0.0930324f}, +{0.00476189f,0.378239f,-0.128079f},{-0.257673f,0.263824f,0.00112538f},{-0.337176f,0.294003f,0.179235f}, +{0.193759f,-0.468075f,0.296003f},{0.203868f,-0.464377f,0.29401f},{0.226909f,-0.464377f,0.281824f}, +{0.232439f,-0.471187f,0.268165f},{-0.223359f,0.0587248f,0.226636f},{0.337877f,-0.0567763f,0.223099f}, +{0.283409f,-0.0394521f,0.260898f},{0.305871f,-0.038674f,0.243973f},{-0.359792f,0.411254f,-0.319675f}, +{-0.316932f,-0.0879393f,-0.100492f},{-0.295357f,0.0945178f,-0.0458249f},{0.0972734f,-0.0184753f,-0.130112f}, +{-0.319999f,0.0210283f,-0.0620752f},{-0.208865f,-0.48248f,0.296756f},{0.281396f,0.0496897f,0.200804f}, +{-0.0529083f,-0.470326f,0.357101f},{-0.0337449f,-0.470204f,0.355718f},{-0.224896f,-0.153005f,-0.128652f}, +{0.00438891f,-0.470583f,0.342085f},{0.0221504f,-0.470403f,0.336041f},{0.0362593f,-0.467927f,0.332909f}, +{0.0427028f,-0.471567f,0.329391f},{-0.263564f,-0.15341f,-0.125424f},{0.0613195f,-0.470731f,0.324369f}, +{0.0817562f,-0.450185f,0.318607f},{0.0937622f,-0.452223f,0.314877f},{-0.305151f,0.0611363f,0.319868f}, +{-0.261365f,0.170676f,0.226269f},{0.154873f,-0.453645f,0.302691f},{0.136127f,-0.0378509f,-0.128311f}, +{0.174036f,-0.45349f,0.29981f},{0.192531f,-0.450294f,0.297367f},{0.207353f,-0.448899f,0.294859f}, +{0.214967f,-0.453947f,0.292145f},{0.229758f,-0.449645f,0.285791f},{0.242529f,-0.450461f,0.277554f}, +{-0.0154175f,0.365416f,-0.0681393f},{-0.220948f,0.0948201f,0.228051f},{-0.00928267f,0.455734f,-0.103862f}, +{-0.298984f,0.267445f,-0.185949f},{0.306624f,-0.11352f,0.283611f},{-0.26986f,-0.102743f,-0.120941f}, +{-0.277705f,-0.108697f,-0.115308f},{-0.203251f,0.266191f,0.0780875f},{0.116006f,-0.473155f,0.309173f}, +{-0.393952f,-0.00196778f,0.282595f},{-0.260201f,-0.0911417f,-0.125533f},{-0.20664f,0.0760426f,0.208784f}, +{0.0097135f,0.476647f,-0.128587f},{-0.207714f,0.11282f,0.207672f},{-0.239873f,0.0960033f,0.249375f}, +{0.0360663f,0.124607f,0.138832f},{-0.112797f,0.338169f,-0.0814507f},{-0.242034f,-0.473245f,0.309964f}, +{-0.22116f,-0.473226f,0.307559f},{-0.200228f,-0.471837f,0.302923f},{-0.185547f,-0.471965f,0.29855f}, +{-0.175104f,-0.46884f,0.296363f},{-0.165863f,-0.473663f,0.29091f},{0.080335f,-0.0869425f,-0.19045f}, +{-0.0541687f,-0.450931f,0.358381f},{-0.0333912f,-0.452834f,0.356625f},{-0.0183178f,-0.453747f,0.352214f}, +{-0.00994504f,-0.450423f,0.349217f},{0.00439533f,-0.451465f,0.343661f},{0.0232372f,-0.450738f,0.337121f}, +{0.035777f,-0.450262f,0.33262f},{0.0452815f,-0.449876f,0.329552f},{0.0613517f,-0.449175f,0.324562f}, +{0.018665f,0.454686f,-0.13352f},{0.311247f,-0.0895276f,0.271033f},{0.271274f,-0.456088f,0.2118f}, +{0.285441f,-0.447471f,0.207325f},{-0.280271f,-0.151236f,-0.117662f},{-0.373528f,0.0931996f,-0.0408604f}, +{-0.292257f,0.000778124f,-0.0879071f},{0.397997f,-0.14896f,0.0178644f},{-0.242124f,0.112556f,0.245175f}, +{-0.261217f,-0.113392f,-0.12716f},{-0.296405f,0.362252f,-0.378322f},{0.115459f,-0.0204816f,-0.125121f}, +{-0.186608f,0.262988f,-0.0231375f},{0.291949f,0.00689366f,0.23196f},{-0.378467f,0.270114f,-0.0726857f}, +{-0.241282f,0.20991f,0.191331f},{-0.266432f,0.214675f,0.220945f},{-0.321922f,0.148928f,0.300318f}, +{-0.225006f,0.150625f,0.207273f},{-0.449828f,0.0165461f,0.208109f},{0.284508f,0.00328608f,0.240012f}, +{-0.427584f,0.278917f,0.17049f},{0.00499982f,0.381267f,-0.0894312f},{-0.0553069f,0.379377f,-0.0579595f}, +{-0.256998f,0.232591f,0.213659f},{0.270477f,-0.112845f,0.308093f},{0.344745f,-0.150709f,-0.0930774f}, +{0.192775f,-0.434263f,0.297823f},{0.209186f,-0.432417f,0.295637f},{0.219372f,-0.431883f,0.293373f}, +{0.231095f,-0.4316f,0.287881f},{0.244581f,-0.431517f,0.279149f},{0.255146f,-0.431465f,0.271811f}, +{0.26494f,-0.432809f,0.261349f},{0.27321f,-0.432964f,0.247317f},{0.285917f,-0.0718819f,0.279567f}, +{0.34805f,-0.0900614f,0.229112f},{0.284939f,-0.431819f,0.227575f},{0.290688f,-0.436957f,0.212559f}, +{-0.0194303f,0.32381f,-0.0228095f},{0.0190058f,0.416668f,-0.11754f},{-0.317016f,0.0406546f,0.324202f}, +{-0.225031f,-0.115771f,-0.13096f},{-0.29591f,-0.108099f,-0.111006f},{-0.128745f,-0.0962477f,-0.138915f}, +{-0.0921482f,0.378952f,-0.0964985f},{-0.257146f,0.212231f,0.213305f},{-0.31108f,0.215099f,0.26967f}, +{0.266625f,0.0558632f,0.208238f},{-0.0705347f,0.303026f,0.00482944f},{-0.160982f,0.264551f,0.0449439f}, +{-0.241436f,-0.45848f,0.31334f},{-0.221925f,-0.453098f,0.308042f},{-0.347986f,0.126401f,-0.0469438f}, +{-0.056741f,0.419929f,-0.059683f},{-0.0377255f,0.417562f,-0.0774766f},{0.00501911f,0.418546f,-0.108415f}, +{-0.108431f,0.360291f,-0.0161152f},{-0.123684f,0.360297f,0.0013183f},{0.118681f,-0.0667567f,0.323301f}, +{-0.0209672f,-0.434443f,0.352278f},{-0.0112955f,-0.434044f,0.34923f},{0.00418956f,-0.433484f,0.343494f}, +{-0.288412f,0.311244f,-0.202566f},{-0.248072f,0.078467f,0.258558f},{-0.239661f,0.0757982f,0.249683f}, +{-0.149638f,0.267715f,0.0368927f},{-0.259879f,0.0952252f,0.267863f},{-0.231166f,0.0767757f,0.241549f}, +{-0.0733899f,0.417614f,-0.038436f},{0.298727f,0.0175557f,0.219131f},{-0.259866f,0.132851f,0.251034f}, +{-0.312057f,0.0155944f,0.265104f},{-0.338449f,0.149506f,0.320871f},{-0.300669f,0.00340824f,-0.0818816f}, +{-0.352905f,-0.0371435f,-0.0703514f},{-0.11135f,0.359725f,-0.0874891f},{-0.109395f,-0.114292f,-0.144587f}, +{-0.244433f,-0.115784f,-0.131648f},{-0.468921f,0.0167261f,0.133224f},{0.366917f,-0.111482f,0.208218f}, +{-0.198383f,0.11336f,0.189576f},{-0.0144787f,0.417626f,-0.0947879f},{-0.221199f,0.0771487f,0.229909f}, +{0.388229f,-0.0951802f,0.152728f},{-0.0728047f,0.360516f,-0.0380631f},{-0.0275329f,0.417562f,-0.0853734f}, +{-0.167503f,0.30617f,0.0426546f},{-0.204106f,0.13197f,0.189602f},{-0.198376f,0.13516f,0.172773f}, +{-0.192216f,0.125488f,0.167673f},{0.361046f,-0.170882f,-0.0731745f},{-0.229083f,0.28365f,0.0875856f}, +{0.358242f,-0.113347f,0.225941f},{-0.319581f,0.0599724f,0.339983f},{-0.0514099f,0.365204f,-0.0523841f}, +{0.268168f,0.0377544f,0.222404f},{-0.223347f,0.267413f,0.095669f},{-0.0961931f,0.384071f,-0.0266679f}, +{-0.112733f,0.380348f,-0.00300953f},{-0.0167101f,0.49093f,-0.096177f},{-0.490592f,0.0611877f,0.262905f}, +{-0.0644706f,0.415916f,-0.0521012f},{-0.0676409f,0.398431f,-0.0530208f},{0.189097f,-0.416366f,0.29518f}, +{0.197984f,-0.414379f,0.292962f},{0.21145f,-0.415858f,0.291116f},{0.230838f,-0.413607f,0.284338f}, +{0.244838f,-0.415363f,0.277721f},{0.254284f,-0.413954f,0.270718f},{0.267223f,-0.413929f,0.261072f}, +{0.277094f,-0.414038f,0.252693f},{0.28557f,-0.413279f,0.244648f},{0.293492f,-0.417189f,0.231073f}, +{0.305878f,-0.416784f,0.212713f},{-0.227327f,0.128941f,0.224642f},{-0.223224f,0.112466f,0.227247f}, +{-0.318803f,0.192f,0.286119f},{-0.101537f,0.393704f,-0.0115687f},{-0.297634f,-0.0333687f,-0.0998939f}, +{0.380833f,-0.409485f,0.203865f},{-0.3536f,-0.14678f,-0.0937976f},{-0.433192f,-0.208842f,0.0983956f}, +{-0.296804f,0.112357f,-0.0439471f},{-0.311974f,-0.108466f,-0.106807f},{0.327903f,-0.112755f,0.266737f}, +{-0.312842f,0.0968265f,0.324909f},{-0.321896f,0.0938169f,0.336722f},{-0.133099f,0.360361f,0.0160252f}, +{-0.182062f,0.285933f,0.0615414f},{0.0271084f,0.458397f,-0.14642f},{-0.0142793f,0.398547f,-0.0889875f}, +{-0.260168f,0.0768786f,0.267303f},{-0.245635f,0.248346f,0.189332f},{-0.255288f,0.250031f,0.207112f}, +{-0.254149f,0.180946f,0.213967f},{-0.0864249f,0.361982f,-0.0313945f},{-0.160506f,0.299064f,0.0497283f}, +{-0.146526f,0.304022f,0.0418829f},{-0.16522f,0.285142f,0.054307f},{0.3565f,-0.0403074f,0.185563f}, +{0.401939f,-0.130542f,0.0756053f},{-0.471544f,0.0158323f,0.0947558f},{-0.214549f,0.188586f,0.0321405f}, +{-0.0324845f,0.379506f,-0.0701778f},{-0.00051769f,0.474737f,-0.113488f},{-0.44822f,-0.0546284f,0.0385583f}, +{-0.246645f,0.0419408f,0.241221f},{-0.339844f,0.168355f,0.316575f},{-0.247905f,0.0936947f,0.258853f}, +{0.301106f,-0.410617f,0.225131f},{-0.277724f,0.15476f,0.247806f},{-0.282046f,0.0767114f,0.280949f}, +{0.402042f,-0.408662f,0.188142f},{0.418954f,-0.410295f,0.170355f},{0.435513f,-0.408321f,0.152812f}, +{0.438639f,-0.413022f,0.133706f},{0.0369602f,0.397331f,-0.12026f},{-0.015006f,0.379615f,-0.0788013f}, +{-0.296013f,0.194772f,0.249735f},{-0.271403f,0.0767499f,0.273618f},{-0.296052f,0.233439f,0.253876f}, +{-0.0931256f,0.358702f,-0.026057f},{0.0373653f,0.416655f,-0.134652f},{0.289987f,-0.0919456f,0.286853f}, +{-0.30328f,0.230276f,0.258461f},{0.289203f,-0.015723f,0.248269f},{-0.302868f,0.212533f,0.260371f}, +{-0.44323f,-0.0558117f,0.0205267f},{-0.192968f,0.266171f,0.0703579f},{0.374203f,-0.082634f,-0.0143404f}, +{0.0159127f,0.46994f,-0.135108f},{0.0217646f,0.378901f,-0.095611f},{0.0231021f,0.473882f,-0.151879f}, +{0.301756f,-0.0208418f,0.240513f},{0.309402f,-0.0145526f,0.228989f},{0.182943f,-0.402064f,0.308781f}, +{0.195637f,-0.397074f,0.30462f},{0.208427f,-0.395601f,0.296775f},{0.216311f,-0.399395f,0.289367f}, +{0.231893f,-0.396225f,0.281206f},{0.2504f,-0.396482f,0.270808f},{0.267165f,-0.396199f,0.261143f}, +{0.278348f,-0.400424f,0.254674f},{0.287988f,-0.395203f,0.249285f},{0.299434f,-0.395974f,0.239967f}, +{0.307318f,-0.396444f,0.228726f},{0.313742f,-0.401936f,0.215215f},{-0.303987f,0.113392f,0.300588f}, +{-0.294502f,0.113173f,0.285811f},{0.31917f,-0.0224945f,0.222327f},{0.400608f,-0.39247f,0.205743f}, +{0.408922f,-0.397061f,0.1951f},{0.420208f,-0.392611f,0.18883f},{0.427822f,-0.397479f,0.177113f}, +{0.439635f,-0.392412f,0.1684f},{0.446156f,-0.399505f,0.150465f},{0.361876f,-0.0899392f,0.208707f}, +{-0.231739f,0.0949937f,0.240803f},{0.22797f,-0.0427896f,0.297444f},{-0.000562705f,0.412919f,-0.102955f}, +{0.208209f,-0.114646f,0.336581f},{0.325954f,-0.0160188f,0.209434f},{-0.229314f,-0.422893f,0.297373f}, +{0.335549f,-0.0257162f,0.204546f},{0.342674f,-0.0180701f,0.189614f},{0.25267f,0.0384875f,0.231658f}, +{-0.247024f,0.193113f,0.203543f},{-0.293878f,0.160278f,0.252693f},{-0.277229f,0.176194f,0.231568f}, +{-0.183457f,0.30354f,0.0561461f},{0.0275457f,0.419292f,-0.126272f},{-0.354101f,0.131256f,0.338709f}, +{0.00573935f,0.343995f,-0.0542169f},{-0.0781551f,0.4f,-0.0418507f},{-0.3189f,0.113263f,0.32172f}, +{-0.146404f,0.283489f,0.0418765f},{-0.334025f,0.0150671f,0.30518f},{-0.00970067f,0.32399f,-0.0313044f}, +{-0.182737f,0.266159f,0.0625575f},{-0.0179705f,0.45576f,-0.0938105f},{-0.000446955f,0.455728f,-0.11372f}, +{-0.320874f,0.213749f,0.28192f},{0.302707f,0.0025787f,0.224906f},{-0.0689592f,0.379358f,-0.0494132f}, +{-0.104206f,0.378342f,-0.0179094f},{-0.1296f,0.268184f,0.0197164f},{0.370371f,-0.0281856f,0.0563133f}, +{0.187129f,-0.392251f,0.315f},{0.316977f,-0.392238f,0.220057f},{-0.284277f,0.108646f,0.277837f}, +{-0.278888f,0.114041f,0.272088f},{0.150313f,-0.00275876f,0.275464f},{0.412485f,-0.387647f,0.200412f}, +{0.431275f,-0.387942f,0.182316f},{0.455345f,-0.389711f,0.15114f},{-0.0200026f,0.310922f,-0.0380052f}, +{0.175824f,0.0522877f,-0.0882287f},{0.283087f,-0.0592392f,0.275515f},{0.00377156f,0.361879f,-0.0719591f}, +{0.390254f,-0.131533f,-0.00779394f},{0.242452f,0.0339861f,0.239407f},{-0.238729f,0.194444f,0.190971f}, +{-0.316668f,0.169814f,0.286049f},{-0.261217f,0.112659f,0.26275f},{-0.0322916f,0.455812f,-0.074795f}, +{-0.0542909f,0.3464f,-0.0339217f},{-0.127864f,0.341989f,0.00335682f},{-0.115446f,0.305971f,0.0322626f}, +{-0.129755f,0.323848f,0.0176007f},{-0.0725218f,0.318768f,0.0133436f},{-0.207887f,0.168895f,0.0386483f}, +{0.0360728f,0.453194f,-0.156779f},{0.0101058f,0.457323f,-0.124903f},{-0.211533f,0.188663f,0.0418186f}, +{-0.0685862f,0.325848f,0.002135f},{0.307324f,-0.0717597f,0.265432f},{-0.0945404f,0.40009f,-0.0183209f}, +{0.177837f,-0.382605f,0.330009f},{0.191991f,-0.377975f,0.322234f},{0.200344f,-0.382894f,0.311617f}, +{0.213064f,-0.378001f,0.30754f},{0.230722f,-0.376554f,0.301514f},{0.236652f,-0.382116f,0.290717f}, +{0.250394f,-0.377563f,0.282364f},{0.258966f,-0.38229f,0.272062f},{0.270091f,-0.377743f,0.265541f}, +{0.283795f,-0.375737f,0.258988f},{0.290463f,-0.378882f,0.253516f},{0.305235f,-0.376612f,0.245362f}, +{0.313382f,-0.382438f,0.232005f},{0.322231f,-0.377113f,0.225607f},{-0.292052f,0.0570336f,0.285714f}, +{0.262085f,-0.0423974f,0.279104f},{-0.305601f,0.0956368f,0.315276f},{-0.297235f,0.0772451f,0.304858f}, +{-0.496135f,0.0952059f,0.111591f},{-0.169921f,0.26612f,0.0528215f},{0.402376f,-0.375493f,0.214102f}, +{0.417096f,-0.373068f,0.204971f},{0.426922f,-0.375383f,0.195081f},{0.43851f,-0.372522f,0.187312f}, +{0.445326f,-0.379178f,0.174316f},{0.456529f,-0.372972f,0.168065f},{0.463731f,-0.380155f,0.151333f}, +{-0.319536f,0.0236584f,0.302132f},{0.02316f,0.398142f,-0.110672f},{-0.396113f,-0.0172985f,0.225954f}, +{-0.27838f,0.0343655f,0.251625f},{-0.204119f,0.247343f,0.0765635f},{-0.317221f,0.415234f,-0.373956f}, +{-0.00159804f,0.325687f,-0.0402495f},{-0.334713f,0.133533f,0.32381f},{-0.0906884f,0.286422f,0.00184562f}, +{0.331388f,-0.0875084f,0.251304f},{0.323703f,-0.094878f,0.262667f},{-0.335253f,0.191968f,0.304112f}, +{-0.0371403f,0.436661f,-0.0764413f},{0.0386708f,0.435954f,-0.149725f},{-0.0347031f,0.34287f,-0.0372014f}, +{-0.0741873f,0.340799f,-0.01971f},{-0.326018f,0.186438f,0.29691f},{0.0039066f,0.436693f,-0.113276f}, +{-0.470258f,0.19256f,0.17213f},{-0.1118f,0.339095f,-0.00407059f},{0.211913f,-0.109598f,-0.153326f}, +{0.170133f,0.0323526f,0.258873f},{0.203135f,-0.373306f,0.315803f},{0.24377f,-0.372367f,0.295527f}, +{0.261847f,-0.372773f,0.276416f},{-0.305415f,0.195048f,0.264442f},{-0.305382f,0.0776631f,0.321167f}, +{-0.465918f,0.174753f,0.172265f},{-0.241661f,0.0589627f,0.245458f},{-0.233565f,0.247799f,0.157506f}, +{0.470959f,-0.370734f,0.151024f},{0.00161085f,0.397589f,-0.0976882f},{-0.24631f,0.12907f,0.242384f}, +{0.341066f,-0.0399023f,0.206759f},{-0.210633f,0.208341f,0.0389377f},{-0.16785f,0.247285f,0.0393235f}, +{-0.192801f,0.0938427f,0.183113f},{0.249326f,-0.038436f,0.283213f},{-0.337317f,0.0215106f,0.322909f}, +{-0.153317f,0.299264f,0.0489309f},{0.0229285f,0.436655f,-0.130948f},{0.337767f,-0.0763126f,-0.0658564f}, +{-0.144063f,0.340651f,0.0202759f},{-0.0110833f,0.31743f,-0.0566734f},{-0.22815f,0.170766f,0.189544f}, +{-0.182615f,0.378534f,-0.0300955f},{0.403109f,-0.148568f,0.0563969f},{0.359426f,-0.0361403f,0.00128615f}, +{0.189502f,-0.359159f,0.335854f},{0.199071f,-0.359313f,0.327063f},{0.213135f,-0.359082f,0.319141f}, +{0.232253f,-0.357442f,0.31525f},{0.237815f,-0.362953f,0.309649f},{0.249886f,-0.357718f,0.301347f}, +{0.256021f,-0.363133f,0.290749f},{0.267769f,-0.358059f,0.28192f},{0.274766f,-0.363615f,0.272499f}, +{0.288528f,-0.359326f,0.266628f},{0.309479f,-0.359493f,0.252706f},{0.320102f,-0.358857f,0.242069f}, +{0.329478f,-0.360316f,0.229523f},{0.249211f,0.0551815f,0.223446f},{0.263847f,-0.0760683f,0.298569f}, +{-0.261467f,0.0591171f,0.261985f},{0.400839f,-0.148748f,0.0371628f},{-0.24278f,0.229446f,0.191351f}, +{0.420253f,-0.356452f,0.208701f},{0.441384f,-0.356554f,0.195061f},{0.452979f,-0.35586f,0.185743f}, +{0.462535f,-0.357005f,0.173551f},{0.477017f,-0.356818f,0.152979f},{-0.0931771f,0.328195f,0.000559486f}, +{-0.147105f,0.322556f,0.0240057f},{-0.332674f,0.215286f,0.291341f},{-0.484425f,0.0606154f,0.1922f}, +{0.0209736f,0.359635f,-0.0773866f},{0.370621f,-0.103174f,0.19856f},{0.0443297f,0.398135f,-0.130356f}, +{-0.347979f,0.195595f,0.310697f},{-0.188872f,0.111746f,0.168046f},{-0.302122f,0.0224752f,0.262448f}, +{-0.19581f,0.158921f,0.094595f},{-0.474637f,0.189756f,0.11262f},{-0.328218f,0.118562f,0.327571f}, +{0.302302f,-0.353873f,0.261619f},{0.337844f,-0.355133f,0.222398f},{0.287023f,0.0218385f,0.224835f}, +{-0.330565f,0.168226f,0.307302f},{0.43289f,-0.351185f,0.201994f},{0.470535f,-0.351847f,0.165847f}, +{-0.129517f,0.3041f,0.0381531f},{-0.495852f,0.115186f,0.132401f},{-0.371998f,-0.281168f,0.0942992f}, +{0.320964f,0.00302242f,0.202135f},{-0.066895f,0.346728f,-0.0293945f},{-0.0516607f,0.323623f,-0.0016398f}, +{-0.305247f,0.172001f,0.264429f},{-0.369978f,0.442159f,-0.334922f},{-0.273551f,0.213196f,0.231227f}, +{-0.0336999f,0.398508f,-0.0774766f},{-0.297016f,0.176271f,0.248076f},{0.191695f,-0.341108f,0.339211f}, +{0.210138f,-0.341057f,0.326305f},{0.230517f,-0.340343f,0.322015f},{0.244433f,-0.338542f,0.315752f}, +{0.252831f,-0.341102f,0.306717f},{0.262657f,-0.338806f,0.296859f},{0.271904f,-0.341185f,0.289078f}, +{0.285769f,-0.339462f,0.279714f},{0.29328f,-0.344915f,0.271271f},{0.306579f,-0.339539f,0.265554f}, +{0.326128f,-0.340896f,0.248629f},{0.334359f,-0.345429f,0.234443f},{0.342423f,-0.340285f,0.225851f}, +{0.349587f,-0.343204f,0.214932f},{-0.295216f,0.28457f,-0.205325f},{0.340031f,-0.0957268f,0.244892f}, +{-0.453384f,0.0383525f,0.308678f},{-0.206524f,0.09455f,0.209112f},{0.435783f,-0.33696f,0.202939f}, +{0.446336f,-0.339204f,0.197138f},{0.457866f,-0.337822f,0.187351f},{0.466059f,-0.338111f,0.176547f}, +{0.471904f,-0.338459f,0.167892f},{0.481846f,-0.338864f,0.153969f},{0.303202f,-0.0957333f,0.280055f}, +{0.0814218f,-0.0515803f,0.29255f},{-0.292264f,0.345217f,-0.341024f},{-0.0167551f,0.356053f,-0.0587505f}, +{-0.19363f,0.107617f,0.182425f},{-0.164204f,0.243098f,0.0192984f},{-0.247487f,0.230038f,0.203376f}, +{-0.124462f,0.283644f,0.0264107f},{-0.219212f,0.247761f,0.0982477f},{-0.21082f,0.130903f,0.203293f}, +{-0.292605f,0.211903f,0.251201f},{-0.310257f,0.172207f,0.274229f},{-0.241192f,0.170798f,0.20827f}, +{-0.218337f,0.132022f,0.212688f},{0.319665f,-0.335674f,0.258641f},{0.337111f,-0.336053f,0.238623f}, +{-0.324443f,0.16811f,0.299051f},{-0.310984f,0.0778753f,0.329893f},{-0.319781f,0.0780104f,0.33977f}, +{-0.212723f,0.247503f,0.0867303f},{-0.111903f,0.327655f,0.00719593f},{-0.287132f,0.171544f,0.23987f}, +{-0.195708f,0.242571f,0.0661265f},{0.317562f,-0.076139f,0.257664f},{-0.106804f,0.318067f,0.0255683f}, +{-0.302739f,0.130337f,0.282878f},{-0.310894f,0.135976f,0.290222f},{-0.191585f,0.28338f,0.0687952f}, +{0.194016f,-0.319443f,0.337095f},{0.211733f,-0.321552f,0.328806f},{0.231353f,-0.322774f,0.323301f}, +{0.24642f,-0.322466f,0.316903f},{0.256098f,-0.322536f,0.308241f},{0.267371f,-0.321765f,0.298537f}, +{0.273506f,-0.326838f,0.293785f},{0.287344f,-0.322485f,0.285579f},{0.307743f,-0.323861f,0.269805f}, +{0.323073f,-0.32143f,0.26149f},{0.332546f,-0.323867f,0.251092f},{0.340134f,-0.320414f,0.243246f}, +{0.347445f,-0.324067f,0.22879f},{0.353149f,-0.328247f,0.21652f},{-0.298476f,0.095341f,0.302948f}, +{-0.291113f,0.0951159f,0.290724f},{-0.48919f,0.0783126f,0.230668f},{0.457493f,-0.320871f,0.190232f}, +{0.467326f,-0.324928f,0.17838f},{0.474811f,-0.320138f,0.16993f},{0.483763f,-0.321134f,0.157011f}, +{0.488161f,-0.321031f,0.148625f},{-0.0857175f,0.396045f,-0.0339217f},{0.345233f,-0.114582f,0.246526f}, +{-0.0343365f,0.360728f,-0.0558889f},{-0.277596f,0.193653f,0.228842f},{-0.455699f,-0.0891225f,0.038719f}, +{0.402235f,-0.13053f,0.0563583f},{-0.488714f,0.0780746f,0.192135f},{0.346564f,-0.1702f,-0.0924986f}, +{-0.360197f,0.287618f,-0.207794f},{0.169702f,-0.313784f,0.354754f},{0.299865f,-0.318048f,0.275464f}, +{0.355612f,-0.314749f,0.220244f},{-0.177753f,0.242481f,0.0468217f},{0.448632f,-0.315919f,0.196881f}, +{0.311498f,0.00544677f,0.212662f},{-0.243262f,0.14869f,0.227414f},{-0.0769911f,0.379383f,-0.04405f}, +{-0.314482f,0.133552f,-0.0457734f},{-0.427822f,0.00120256f,0.0205267f},{-0.297923f,0.24859f,-0.113122f}, +{-0.236767f,0.180811f,0.193653f},{-0.285808f,0.376689f,-0.360123f},{-0.288335f,0.304633f,-0.13287f}, +{-0.354223f,-0.222089f,0.246018f},{0.206987f,-0.310447f,0.33343f},{0.219893f,-0.301328f,0.329514f}, +{0.23089f,-0.301251f,0.3228f},{0.244728f,-0.30273f,0.315688f},{0.254548f,-0.299733f,0.308582f}, +{0.269043f,-0.300884f,0.298859f},{0.289016f,-0.300582f,0.288184f},{0.30128f,-0.300659f,0.277631f}, +{0.308932f,-0.302312f,0.272866f},{0.325871f,-0.299572f,0.263927f},{0.333214f,-0.309154f,0.255915f}, +{0.343516f,-0.299733f,0.246076f},{0.351194f,-0.306865f,0.232899f},{-0.324096f,0.127372f,0.316569f}, +{0.458355f,-0.303045f,0.188367f},{0.474715f,-0.303276f,0.172342f},{0.484348f,-0.303739f,0.158034f}, +{0.488881f,-0.300743f,0.148433f},{-0.185644f,0.247124f,0.0580239f},{-0.318411f,0.0184432f,0.285946f}, +{-0.0494679f,0.415948f,-0.0680621f},{-0.266908f,0.231768f,0.224417f},{0.375901f,-0.0972251f,-0.0192984f}, +{-0.310476f,0.0367319f,0.308395f},{0.325941f,-0.0720298f,0.247214f},{-0.0904505f,0.342626f,-0.0160445f}, +{0.323137f,-0.0387383f,0.22861f},{-0.34477f,0.182007f,0.315257f},{-0.482824f,0.0947237f,0.0602039f}, +{0.172461f,-0.296769f,0.359423f},{0.180094f,-0.302177f,0.350683f},{0.192299f,-0.297347f,0.346175f}, +{0.209636f,-0.297097f,0.337314f},{0.358738f,-0.296884f,0.225099f},{-0.231481f,0.144838f,0.218501f}, +{-0.174866f,0.25187f,0.0510595f},{0.0382849f,0.358741f,-0.0940355f},{0.00948199f,0.40337f,-0.105437f}, +{-0.0533199f,0.355944f,-0.0437928f},{-0.329292f,0.154535f,0.309013f},{-0.299132f,0.0402945f,0.28493f}, +{-0.469062f,0.0256133f,0.0585576f},{-0.0876338f,0.318446f,0.0195621f},{-0.282431f,0.21445f,0.244815f}, +{-0.185753f,0.0938748f,0.170605f},{-0.2486f,0.162773f,0.222147f},{-0.409804f,-0.0203466f,-0.0172149f}, +{0.246278f,-0.0565512f,0.297617f},{0.31135f,-0.282692f,0.272609f},{0.371734f,-0.293926f,0.198457f}, +{0.274052f,-0.011035f,0.254854f},{0.462734f,-0.28037f,0.176682f},{0.473396f,-0.283309f,0.168239f}, +{0.482065f,-0.280004f,0.154928f},{0.488502f,-0.286955f,0.147256f},{0.355336f,-0.0938298f,0.221742f}, +{-0.385644f,-0.0936176f,-0.070255f},{-0.229443f,0.266911f,0.112292f},{-0.28103f,0.232713f,0.246024f}, +{-0.0279059f,0.436661f,-0.0857271f},{-0.0987525f,0.336832f,-0.00780037f},{-0.132468f,0.287444f,0.033761f}, +{0.192711f,-0.279515f,0.352574f},{0.201521f,-0.279393f,0.348388f},{0.211668f,-0.279393f,0.340433f}, +{0.22242f,-0.27938f,0.333378f},{0.233243f,-0.279367f,0.326395f},{0.245172f,-0.27783f,0.31727f}, +{0.253358f,-0.280332f,0.307932f},{0.268136f,-0.279386f,0.297643f},{0.288328f,-0.279059f,0.287335f}, +{0.303903f,-0.277464f,0.277837f},{0.32661f,-0.278718f,0.263561f},{0.345934f,-0.278891f,0.24623f}, +{0.36021f,-0.279412f,0.227253f},{0.36857f,-0.279464f,0.207698f},{0.372068f,-0.285174f,0.199029f}, +{-0.499177f,0.0955082f,0.153455f},{-0.482046f,0.209968f,0.133494f},{-0.0162342f,0.337552f,-0.0352014f}, +{-0.076258f,0.309418f,0.0135044f},{-0.0918524f,0.0832128f,-0.0219671f},{-0.349998f,0.15015f,0.330182f}, +{-0.290624f,0.0768914f,0.291476f},{0.281312f,-0.0972187f,0.295515f},{-0.0157326f,0.436661f,-0.096402f}, +{-0.016067f,0.313353f,-0.0512716f},{-0.266927f,0.0447767f,0.256918f},{-0.324392f,0.0462815f,0.338529f}, +{-0.298624f,0.0591621f,0.303161f},{0.353098f,-0.102164f,0.229716f},{-0.21318f,0.261574f,0.0861387f}, +{0.492849f,-0.283226f,0.134684f},{0.0356741f,0.375403f,-0.101309f},{0.269242f,-0.0938169f,0.302961f}, +{0.379361f,-0.150484f,-0.0398829f},{0.270863f,-0.0711231f,0.289502f},{0.362139f,-0.151886f,-0.0719526f}, +{-0.462303f,0.0608019f,0.0229896f},{-0.0906884f,0.305135f,0.0184882f},{-0.0543552f,0.337352f,-0.0225845f}, +{-0.2624f,0.148439f,0.243992f},{-0.293184f,0.131089f,0.270634f},{-0.197296f,0.094132f,0.193865f}, +{-0.279531f,0.0439793f,0.261503f},{-0.1169f,0.271001f,0.0107649f},{0.190441f,-0.262172f,0.355063f}, +{0.201984f,-0.261895f,0.349217f},{0.213565f,-0.261863f,0.343372f},{0.227301f,-0.261754f,0.334973f}, +{0.236838f,-0.263168f,0.327996f},{0.24894f,-0.260178f,0.320993f},{0.256567f,-0.266352f,0.309225f}, +{0.266528f,-0.260603f,0.301051f},{0.273293f,-0.266474f,0.293804f},{0.286933f,-0.261959f,0.285431f}, +{0.304257f,-0.261676f,0.276583f},{0.313299f,-0.261484f,0.27275f},{0.326166f,-0.261477f,0.263066f}, +{0.345298f,-0.261664f,0.245465f},{0.360191f,-0.262101f,0.227369f},{0.368622f,-0.262718f,0.211286f}, +{0.373535f,-0.263258f,0.201196f},{0.355747f,-0.0973281f,-0.0497219f},{-0.153548f,0.343011f,0.0336388f}, +{0.475203f,-0.262641f,0.15096f},{0.484431f,-0.260873f,0.133886f},{0.296695f,-0.0675219f,0.272589f}, +{0.0291791f,0.36586f,-0.0880936f},{-0.252953f,0.0538568f,0.254911f},{-0.296611f,0.305817f,-0.244442f}, +{0.394196f,-0.149301f,-0.00137614f},{-0.294939f,0.0174657f,0.250101f},{-0.0417382f,0.337487f,-0.0269187f}, +{-0.111884f,0.285277f,0.0182438f},{-0.300199f,-0.305321f,0.170843f},{-0.310874f,0.195203f,0.273663f}, +{-0.00157232f,0.374972f,-0.0819909f},{-0.467641f,-0.114054f,0.135192f},{-0.445532f,-0.0192534f,0.13469f}, +{-0.48319f,0.0278319f,0.0975724f},{0.335234f,-0.16685f,-0.104138f},{-0.446426f,0.0595158f,0.0026816f}, +{-0.0854732f,-0.131076f,-0.165879f},{-0.210762f,0.14786f,0.186386f},{-0.0732292f,0.438963f,-0.0937912f}, +{-0.352095f,0.359474f,-0.285496f},{-0.0802643f,0.422379f,-0.0257162f},{-0.00456257f,0.356265f,-0.0636635f}, +{0.257931f,-0.0343397f,0.272988f},{-0.107009f,0.301945f,0.0255362f},{-0.00114789f,0.3377f,-0.0415099f}, +{-0.0524774f,0.437343f,-0.0565962f},{0.0514549f,0.383396f,-0.136047f},{-0.292032f,0.0369249f,0.266107f}, +{-0.306997f,0.0260635f,0.278821f},{0.191142f,-0.244854f,0.356233f},{0.203566f,-0.244513f,0.351693f}, +{0.214048f,-0.244507f,0.344246f},{0.231854f,-0.244236f,0.33615f},{0.252522f,-0.244571f,0.323925f}, +{0.270097f,-0.244931f,0.304042f},{0.288315f,-0.245921f,0.287566f},{0.302289f,-0.242121f,0.279554f}, +{0.3109f,-0.245703f,0.273117f},{0.325864f,-0.244674f,0.265188f},{0.345368f,-0.244359f,0.245671f}, +{0.36014f,-0.244777f,0.227472f},{0.36967f,-0.245246f,0.212874f},{0.375084f,-0.245651f,0.203595f}, +{0.381914f,-0.246275f,0.190669f},{0.38936f,-0.245638f,0.174753f},{0.291145f,-0.0359409f,0.253278f}, +{0.254272f,-0.0530337f,0.29037f},{-0.28575f,0.144632f,0.258808f},{0.0141571f,0.329494f,-0.0638436f}, +{-0.414929f,0.245169f,0.0165204f},{-0.317208f,0.131674f,0.303855f},{-0.204415f,-0.261458f,0.263316f}, +{-0.423675f,-0.108736f,-0.0320054f},{0.29573f,0.0261407f,0.214739f},{-0.307177f,0.0458571f,0.313128f}, +{0.244658f,-0.239465f,0.332022f},{0.264008f,-0.239696f,0.314607f},{0.281171f,-0.240037f,0.294113f}, +{0.33515f,-0.239632f,0.25931f},{-0.475133f,0.157525f,0.0939584f},{-0.381605f,0.418109f,-0.31127f}, +{-0.317819f,-0.314434f,0.132928f},{0.387007f,-0.149873f,-0.0206167f},{-0.213379f,0.0719012f,0.221864f}, +{-0.300649f,0.284679f,-0.225845f},{-0.0719495f,0.00154982f,-0.110222f},{-0.294026f,0.342208f,-0.359886f}, +{-0.416755f,-0.208431f,0.173589f},{-0.397791f,0.244089f,-0.00489371f},{-0.0996592f,0.290222f,0.0133694f}, +{-0.0757886f,0.290743f,-0.00540174f},{-0.0452944f,0.384206f,-0.0660557f},{-0.0610817f,0.441446f,-0.0430918f}, +{-0.307685f,0.144851f,0.27774f},{0.38801f,-0.112652f,-0.00266871f},{0.336725f,-0.000199339f,0.183357f}, +{0.230157f,0.0578695f,-0.0557795f},{0.193065f,-0.22724f,0.359236f},{0.207109f,-0.22553f,0.353204f}, +{0.214093f,-0.228404f,0.348246f},{0.231925f,-0.226526f,0.34206f},{0.245539f,-0.226372f,0.333423f}, +{0.25296f,-0.226436f,0.327153f},{0.264831f,-0.2256f,0.31826f},{0.273602f,-0.228938f,0.305939f}, +{0.2842f,-0.224437f,0.298788f},{0.293023f,-0.228031f,0.292685f},{0.307209f,-0.227182f,0.283605f}, +{0.325337f,-0.226662f,0.267792f},{0.33742f,-0.225176f,0.258924f},{0.347478f,-0.227542f,0.246391f}, +{0.360371f,-0.227433f,0.227928f},{0.370146f,-0.227825f,0.213839f},{0.375457f,-0.228237f,0.204347f}, +{0.382949f,-0.225581f,0.190965f},{0.389721f,-0.227414f,0.176367f},{-0.464149f,0.17485f,0.191557f}, +{0.335317f,-0.00309312f,-0.0148612f},{-0.294605f,0.246108f,-0.0770907f},{-0.231636f,0.247857f,0.147918f}, +{-0.257378f,0.195544f,0.212797f},{-0.300174f,0.149108f,0.266191f},{-0.266483f,0.126832f,0.258397f}, +{-0.184467f,0.073078f,-0.0691939f},{0.316778f,-0.222109f,0.278094f},{0.393463f,-0.22135f,0.167634f}, +{0.398994f,-0.222141f,0.152883f},{0.40428f,-0.223215f,0.133481f},{-0.224337f,0.188322f,0.154188f}, +{0.0944117f,-0.00196778f,-0.126137f},{-0.141645f,0.261509f,0.0257677f},{-0.153027f,0.289978f,0.0494518f}, +{-0.202595f,0.225678f,0.0589692f},{-0.350725f,0.323347f,-0.265856f},{0.371689f,-0.0461207f,0.136742f}, +{-0.287788f,0.306292f,-0.147879f},{0.193508f,-0.20555f,0.359879f},{0.21318f,-0.209279f,0.354638f}, +{0.230407f,-0.209041f,0.345648f},{0.243481f,-0.20899f,0.33624f},{0.254413f,-0.208913f,0.329443f}, +{0.267898f,-0.208861f,0.320684f},{0.276869f,-0.209016f,0.310929f},{0.288103f,-0.209697f,0.301347f}, +{0.308038f,-0.2089f,0.288222f},{0.319832f,-0.207517f,0.278834f},{0.32796f,-0.209993f,0.269477f}, +{0.338552f,-0.209286f,0.258751f},{0.349098f,-0.209562f,0.245799f},{0.361194f,-0.205698f,0.22924f}, +{0.370628f,-0.206161f,0.21452f},{0.37565f,-0.206508f,0.204926f},{0.381772f,-0.204598f,0.19265f}, +{-0.18529f,0.230616f,0.0361275f},{-0.192518f,0.233105f,0.0540176f},{-0.26629f,0.193145f,0.221073f}, +{-0.309517f,0.153905f,0.275007f},{0.00359793f,-0.0376451f,-0.136844f},{0.400395f,-0.112588f,0.056339f}, +{0.377425f,-0.112543f,0.18883f},{-0.27231f,0.227221f,0.233259f},{-0.392177f,0.323874f,-0.150098f}, +{0.392782f,-0.0995724f,0.0242693f},{0.297447f,-0.204656f,0.295566f},{0.389238f,-0.203325f,0.179113f}, +{0.393997f,-0.203846f,0.168702f},{0.400254f,-0.203286f,0.152606f},{0.404016f,-0.205543f,0.137488f}, +{0.406575f,-0.203736f,0.0958555f},{0.373728f,-0.0370471f,0.0916627f},{-0.285036f,0.196302f,0.239285f}, +{-0.49265f,0.113205f,0.172702f},{0.391894f,-0.0905887f,0.0307064f},{-0.0491335f,0.400045f,-0.0677341f}, +{0.387496f,-0.131507f,0.172104f},{-0.30445f,0.042571f,0.302254f},{-0.289537f,0.331526f,-0.205775f}, +{0.210852f,-0.192984f,0.353641f},{0.230388f,-0.186656f,0.3435f},{0.2464f,-0.187447f,0.334999f}, +{0.256336f,-0.189357f,0.329636f},{0.26829f,-0.187139f,0.321404f},{0.277544f,-0.187287f,0.312067f}, +{0.287859f,-0.187396f,0.302119f},{0.297582f,-0.195955f,0.295823f},{0.3078f,-0.187319f,0.288036f}, +{0.320893f,-0.187293f,0.278641f},{0.330063f,-0.187415f,0.269316f},{0.336108f,-0.187647f,0.261021f}, +{0.3465f,-0.187949f,0.247799f},{0.405495f,-0.20337f,0.114061f},{-0.463545f,0.155307f,0.0387769f}, +{0.349606f,-0.0372335f,0.194515f},{-0.25541f,0.153847f,0.234912f},{0.335838f,-0.0763834f,0.238951f}, +{0.275666f,0.0441979f,0.211537f},{-0.40626f,0.244191f,0.00486803f},{-0.247198f,0.214655f,0.203582f}, +{-0.194151f,0.251947f,0.0684094f},{-0.393611f,-0.26093f,0.0749301f},{-0.336989f,0.11408f,0.337558f}, +{0.193643f,-0.184219f,0.35469f},{0.214215f,-0.182181f,0.348728f},{0.363619f,-0.184116f,0.227555f}, +{0.372885f,-0.184528f,0.21245f},{0.377219f,-0.183409f,0.203685f},{0.383579f,-0.18629f,0.191351f}, +{0.389901f,-0.185685f,0.179049f},{0.393913f,-0.185467f,0.168824f},{0.399412f,-0.186483f,0.153088f}, +{0.403026f,-0.186547f,0.134369f},{0.153992f,-0.0128613f,-0.119212f},{0.342526f,-0.0724093f,0.226012f}, +{0.351702f,-0.0712453f,0.212816f},{-0.323607f,0.0270281f,0.316819f},{-0.0144658f,0.347044f,-0.0496318f}, +{0.24024f,0.0426288f,0.236269f},{-0.410202f,0.0403524f,0.321244f},{0.295923f,-0.178888f,0.293463f}, +{-0.380415f,0.441587f,-0.323456f},{0.315099f,-0.0676699f,0.254069f},{0.255937f,0.0257355f,0.23643f}, +{-0.233983f,0.153783f,0.214951f},{-0.433906f,-0.109958f,-0.0181473f},{-0.293139f,0.346458f,-0.282949f}, +{-0.47429f,0.245143f,0.0948394f},{-0.298727f,0.325096f,-0.339359f},{0.270522f,0.0224816f,0.232616f}, +{-0.0683805f,0.449986f,-0.0894955f},{0.160712f,-0.171879f,0.369146f},{0.174441f,-0.167917f,0.363262f}, +{0.187348f,-0.165776f,0.355075f},{0.197418f,-0.167114f,0.348883f},{0.24514f,-0.170104f,0.333404f}, +{0.25141f,-0.16494f,0.328877f},{0.266342f,-0.165725f,0.318806f},{0.277287f,-0.165692f,0.311874f}, +{0.28674f,-0.165789f,0.302878f},{0.294534f,-0.16611f,0.291585f},{0.306186f,-0.165937f,0.285791f}, +{0.320392f,-0.165744f,0.278126f},{0.329388f,-0.165905f,0.268461f},{0.335632f,-0.166104f,0.26041f}, +{0.346127f,-0.166381f,0.247375f},{0.363381f,-0.166792f,0.227176f},{0.372846f,-0.167236f,0.212366f}, +{0.376017f,-0.167487f,0.205871f},{0.38365f,-0.16858f,0.191942f},{0.389933f,-0.166516f,0.179422f}, +{-0.392833f,0.0234912f,0.319732f},{-0.494894f,0.0964214f,0.174059f},{-0.0558921f,0.396836f,-0.0624288f}, +{-0.253506f,0.117868f,0.254564f},{-0.0270828f,0.450976f,-0.084441f},{-0.0342787f,0.379268f,-0.11507f}, +{-0.381399f,-0.0122504f,-0.0409311f},{0.369933f,-0.151256f,-0.0590527f},{0.0296164f,0.38456f,-0.104524f}, +{-0.0463297f,0.431909f,-0.0671232f},{-0.310083f,0.0582618f,0.327507f},{0.331279f,-0.00854636f,0.198232f}, +{0.35731f,-0.0743127f,0.205871f},{-0.392602f,-0.0367319f,0.227928f},{-0.215926f,0.0532652f,0.217864f}, +{-0.286309f,0.0630462f,0.280576f},{-0.46703f,0.191994f,0.0951931f},{-0.369458f,-0.277586f,0.132684f}, +{0.392743f,-0.117739f,0.00504166f},{-0.0631459f,0.365101f,-0.0467509f},{-0.325035f,0.00603198f,0.262551f}, +{-0.314392f,-0.0357866f,-0.090563f},{-0.0242726f,0.346928f,-0.0468859f},{0.394499f,-0.131205f,0.00184562f}, +{0.353773f,-0.0515031f,0.196663f},{0.173123f,-0.150137f,0.36467f},{0.190126f,-0.14977f,0.355461f}, +{0.199875f,-0.149822f,0.346921f},{0.265705f,-0.148503f,0.31808f},{0.276772f,-0.148452f,0.311302f}, +{0.285705f,-0.148632f,0.301598f},{0.294502f,-0.148818f,0.29165f},{0.306527f,-0.148613f,0.286537f}, +{0.321633f,-0.148272f,0.280094f},{0.330391f,-0.148478f,0.270075f},{0.337838f,-0.147269f,0.26014f}, +{0.347072f,-0.149712f,0.246455f},{0.360615f,-0.149519f,0.228887f},{0.370171f,-0.14995f,0.214572f}, +{0.375168f,-0.150304f,0.204784f},{0.381952f,-0.150812f,0.19184f},{0.298502f,-0.0759397f,0.275194f}, +{0.33124f,-0.0341532f,0.215356f},{-0.271448f,0.0951159f,0.273721f},{-0.0428379f,0.374792f,-0.062506f}, +{0.22062f,-0.145429f,0.331745f},{0.114508f,0.00446288f,-0.119282f},{-0.457262f,0.227337f,0.0693869f}, +{0.333793f,-0.0678563f,0.235941f},{-0.282194f,0.0950195f,0.280801f},{-0.353799f,0.226996f,-0.10954f}, +{-0.372088f,0.422546f,-0.321089f},{0.278161f,-0.0846404f,0.290981f},{0.373618f,-0.150953f,-0.0526349f}, +{-0.309517f,0.113656f,0.309482f},{0.197817f,-0.131153f,0.347841f},{0.210472f,-0.132478f,0.339745f}, +{0.219572f,-0.132677f,0.330292f},{0.266393f,-0.1328f,0.315308f},{0.27321f,-0.129957f,0.310016f}, +{0.284489f,-0.131507f,0.299926f},{0.294437f,-0.131539f,0.291804f},{0.306829f,-0.131243f,0.287161f}, +{0.321067f,-0.13107f,0.279361f},{0.33014f,-0.131211f,0.26985f},{0.33868f,-0.131423f,0.259509f}, +{0.348288f,-0.131822f,0.245246f},{0.359657f,-0.132337f,0.22787f},{0.369187f,-0.132099f,0.209877f}, +{0.373663f,-0.137539f,0.202727f},{0.379676f,-0.133372f,0.18964f},{-0.393232f,0.266429f,-0.0018713f}, +{-0.386332f,0.318556f,-0.0768207f},{-0.384435f,0.284216f,-0.00197419f},{-0.234812f,0.117643f,0.236436f}, +{-0.23489f,0.0535288f,0.23578f},{-0.374834f,0.170168f,-0.0319604f},{-0.0530176f,0.034687f,-0.0872898f}, +{-0.391007f,0.326273f,-0.111803f},{-0.105858f,0.346156f,-0.0124497f},{-0.284901f,-0.307379f,0.175287f}, +{0.0462268f,0.397987f,-0.147699f},{0.396698f,-0.112768f,0.114164f},{-0.294573f,0.346574f,-0.205781f}, +{-0.382042f,0.248281f,-0.0404103f},{0.301756f,-0.0592071f,0.263098f},{-0.451204f,0.0230218f,0.0365069f}, +{-0.496013f,0.0583776f,0.113443f},{-0.367014f,0.453825f,-0.336015f},{0.265088f,0.0171763f,0.238275f}, +{-0.350133f,0.114054f,-0.0474583f},{0.0793125f,-0.0969293f,-0.202637f},{-0.184801f,0.230391f,0.0214463f}, +{-0.297061f,0.264165f,-0.167287f},{0.0243175f,0.418411f,-0.166252f},{-0.467223f,0.19274f,0.230018f}, +{0.262966f,-0.118594f,0.314202f},{0.319414f,-0.118298f,0.277052f},{0.335613f,-0.118517f,0.260802f}, +{-0.214363f,0.270905f,0.0846661f},{-0.301241f,0.247413f,-0.132774f},{-0.216234f,0.117546f,0.218051f}, +{-0.340494f,0.127443f,0.332504f},{-0.486219f,0.134652f,0.095341f},{-0.0820585f,0.412855f,-0.0283914f}, +{-0.112675f,-0.091489f,-0.140169f},{-0.0356548f,0.30491f,-0.0375936f},{0.033642f,0.347982f,-0.0946079f}, +{-0.289055f,0.321347f,-0.218642f},{-0.289184f,0.180727f,0.236848f},{-0.334507f,-0.0908009f,-0.0943185f}, +{-0.380448f,0.248204f,-0.0540368f},{0.0440211f,0.38148f,-0.149088f},{-0.373059f,-0.0920935f,-0.0786277f}, +{-0.315627f,-0.0169255f,-0.0797981f},{0.00336643f,0.327083f,-0.0718112f},{-0.425436f,0.207395f,0.0221344f}, +{0.24876f,-0.472712f,-0.00379407f},{0.237996f,-0.475419f,-0.0214655f},{0.232973f,-0.473644f,-0.0391434f}, +{0.229333f,-0.47401f,-0.0584097f},{0.226768f,-0.473483f,-0.0764348f},{0.21536f,-0.483161f,-0.0790199f}, +{0.215553f,-0.479669f,-0.0921128f},{0.243217f,-0.466917f,0.259413f},{0.246272f,-0.467946f,0.247426f}, +{0.2504f,-0.468242f,0.230372f},{-0.357651f,0.244089f,-0.170348f},{-0.296708f,0.346574f,-0.186476f}, +{-0.284097f,0.395949f,-0.37966f},{-0.317807f,-0.306826f,0.154715f},{-0.27665f,-0.0892061f,-0.114967f}, +{0.377882f,-0.452609f,0.0563905f},{0.365676f,-0.455542f,0.0379345f},{0.36284f,-0.450699f,0.0208804f}, +{0.350294f,-0.457143f,0.0219286f},{0.246008f,-0.467168f,-0.0164175f},{0.223842f,-0.470146f,-0.0938298f}, +{0.212466f,-0.469425f,-0.113649f},{0.177528f,0.0819909f,-0.0581396f},{0.250445f,-0.0753995f,-0.128144f}, +{-0.287994f,0.321437f,-0.18647f},{0.257706f,-0.456783f,0.246899f},{0.266753f,-0.453702f,0.228102f}, +{-0.463313f,-0.0535996f,0.0963442f},{-0.292862f,0.266171f,-0.115655f},{-0.297691f,0.418154f,-0.340291f}, +{-0.486406f,0.0608212f,0.211479f},{-0.392788f,0.092788f,-0.0334844f},{-0.333645f,0.073824f,-0.0471689f}, +{0.401514f,-0.441831f,0.0767628f},{0.397302f,-0.441947f,0.0568471f},{0.388287f,-0.44295f,0.0376966f}, +{0.378776f,-0.444076f,0.0248802f},{0.359496f,-0.445253f,0.00628921f},{0.344159f,-0.450371f,0.0042957f}, +{-0.201231f,0.169287f,0.0771036f},{0.25568f,-0.458268f,-0.0205074f},{0.248497f,-0.453471f,-0.0392013f}, +{0.238356f,-0.459477f,-0.0585383f},{0.236433f,-0.456005f,-0.0791228f},{0.233108f,-0.455432f,-0.0939584f}, +{0.228137f,-0.453645f,-0.108517f},{0.22152f,-0.46122f,-0.110086f},{0.207739f,-0.462738f,-0.12325f}, +{0.193013f,-0.469998f,-0.129411f},{0.0398026f,-0.0865888f,-0.191074f},{-0.467647f,0.0190926f,0.246281f}, +{-0.292798f,0.305984f,-0.22515f},{-0.0710363f,-0.0359988f,-0.136619f},{-0.496019f,0.056204f,0.13433f}, +{0.261358f,-0.447111f,0.256468f},{0.26712f,-0.446609f,0.243651f},{-0.469294f,0.0183531f,0.0758561f}, +{-0.355683f,0.217601f,-0.0730137f},{-0.206736f,0.169075f,0.134896f},{-0.298148f,0.43724f,-0.361525f}, +{-0.29227f,0.266262f,-0.096357f},{-0.286824f,0.396965f,-0.360117f},{-0.429559f,0.152137f,1.28796e-05f}, +{0.417263f,-0.431394f,0.113746f},{0.418292f,-0.432346f,0.0939777f},{0.421462f,-0.43025f,0.0760104f}, +{0.417102f,-0.430546f,0.0568278f},{0.411803f,-0.430751f,0.0441787f},{0.405071f,-0.431832f,0.0345455f}, +{0.39608f,-0.428835f,0.0193756f},{0.381856f,-0.434423f,0.0154336f},{0.364422f,-0.435478f,-0.00126682f}, +{0.289216f,-0.0535674f,-0.0970258f},{-0.453339f,0.134864f,0.0193306f},{0.269724f,-0.452873f,-0.0171763f}, +{0.244555f,-0.449497f,-0.0585769f},{0.241179f,-0.449799f,-0.0713803f},{0.212524f,-0.450153f,-0.127932f}, +{0.155471f,0.0844796f,-0.0584097f},{-0.462837f,-0.0924729f,0.15249f},{-0.481685f,0.0599273f,0.0604418f}, +{-0.47656f,0.0597601f,0.0508215f},{-0.294315f,0.376772f,-0.302241f},{0.0440661f,0.419118f,-0.17339f}, +{-0.20855f,0.188798f,0.0578245f},{-0.287113f,0.417118f,-0.380277f},{-0.370043f,-0.258249f,0.186502f}, +{-0.210177f,0.16521f,0.154014f},{-0.401611f,-0.0144818f,0.216019f},{0.439886f,-0.415434f,0.114472f}, +{0.44069f,-0.416218f,0.0951867f},{0.440446f,-0.415112f,0.0759204f},{0.43597f,-0.415356f,0.0566927f}, +{0.423385f,-0.421093f,0.0375679f},{0.421539f,-0.41407f,0.020308f},{0.405296f,-0.4218f,0.0159866f}, +{0.315569f,-0.0725507f,-0.0834893f},{-0.108765f,0.0418508f,-0.0803318f},{0.302238f,-0.0549371f,-0.0891289f}, +{0.271486f,-0.43169f,-0.0343847f},{0.256265f,-0.438925f,-0.0425581f},{0.251686f,-0.436108f,-0.0600173f}, +{0.248471f,-0.43623f,-0.0766277f},{0.24631f,-0.434648f,-0.0921128f},{0.237391f,-0.440835f,-0.109984f}, +{0.230446f,-0.433742f,-0.127115f},{0.215707f,-0.436205f,-0.134716f},{0.00438248f,-0.0895598f,-0.188193f}, +{-0.293428f,0.266049f,-0.131738f},{-0.300553f,0.380226f,-0.282229f},{-0.296058f,0.326035f,-0.282981f}, +{-0.150725f,0.37847f,-0.061072f},{-0.45377f,0.0130671f,0.153905f},{-0.431726f,0.0403845f,0.320234f}, +{-0.458683f,0.0119096f,0.140979f},{-0.353413f,0.305341f,-0.246449f},{-0.204119f,0.169069f,0.0578631f}, +{-0.109042f,0.272705f,-0.00145331f},{0.449043f,-0.409749f,0.0951416f},{0.436105f,-0.410643f,0.0375422f}, +{0.40271f,-0.413234f,0.000861727f},{0.306546f,-0.074287f,-0.0900743f},{0.269499f,-0.0541912f,-0.108228f}, +{0.100129f,0.070165f,-0.0682357f},{0.262104f,-0.428867f,-0.0521269f},{0.243442f,-0.431086f,-0.110164f}, +{0.218157f,-0.0130735f,-0.101431f},{-0.108019f,-0.476364f,-0.188161f},{-0.335703f,-0.278917f,0.190425f}, +{-0.375515f,0.270152f,-0.130504f},{-0.431944f,0.190862f,0.0184432f},{-0.487537f,0.078184f,0.211369f}, +{-0.289775f,0.331468f,-0.186476f},{0.0238481f,0.341397f,-0.0924857f},{-0.288521f,0.39191f,-0.34404f}, +{-0.446143f,0.0075689f,0.264294f},{-0.225732f,0.208109f,0.13487f},{0.0427799f,0.363577f,-0.128607f}, +{0.458786f,-0.395499f,0.133726f},{0.461525f,-0.396122f,0.115688f},{0.459699f,-0.399807f,0.0951031f}, +{0.460323f,-0.397119f,0.0742484f},{0.458426f,-0.395183f,0.056577f},{0.446169f,-0.400977f,0.0373943f}, +{0.439462f,-0.396843f,0.0184432f},{0.428928f,-0.402225f,0.0122118f},{0.419314f,-0.399698f,-0.000353668f}, +{0.40662f,-0.403916f,-0.00723448f},{0.0976528f,0.0429054f,-0.0970322f},{0.287975f,-0.41598f,-0.036629f}, +{0.275512f,-0.417671f,-0.0584611f},{0.266586f,-0.418668f,-0.0745378f},{0.253018f,-0.421536f,-0.091161f}, +{0.24723f,-0.417035f,-0.110479f},{0.231848f,-0.41652f,-0.131038f},{0.307492f,-0.0907044f,-0.0960162f}, +{0.440838f,-0.2645f,0.0228546f},{0.459384f,-0.319617f,0.00039872f},{-0.03415f,-0.0165011f,-0.131044f}, +{-0.296032f,0.326208f,-0.12925f},{-0.259333f,-0.27547f,0.242835f},{-0.303955f,0.309939f,-0.303907f}, +{-0.239005f,0.247696f,0.173621f},{-0.208993f,0.152182f,0.0179416f},{-0.192061f,0.145751f,0.059818f}, +{-0.209662f,0.208521f,0.0578181f},{-0.0908363f,-0.0219285f,-0.124105f},{-0.300752f,0.304575f,-0.264204f}, +{-0.128597f,0.261561f,-0.000932427f},{-0.292573f,0.2663f,-0.0770715f},{0.468425f,-0.389859f,0.107919f}, +{0.469159f,-0.389807f,0.095058f},{0.46856f,-0.389717f,0.0790007f},{0.457275f,-0.390682f,0.03742f}, +{0.449937f,-0.391357f,0.0247388f},{0.432722f,-0.392901f,0.00583906f},{0.41172f,-0.394187f,-0.0133114f}, +{0.304617f,-0.411684f,-0.0309572f},{0.286933f,-0.407781f,-0.058609f},{0.280965f,-0.40809f,-0.0777981f}, +{0.272824f,-0.408469f,-0.0935468f},{0.13452f,0.0551622f,-0.0911482f},{0.00276838f,-0.0574065f,-0.142613f}, +{0.0986881f,-0.111392f,-0.208566f},{-0.0535256f,-0.0160316f,-0.128793f},{-0.480194f,0.112318f,0.0601846f}, +{-0.291717f,0.331552f,-0.167159f},{-0.291486f,0.376772f,-0.321514f},{0.0521751f,0.396354f,-0.154664f}, +{-0.466503f,0.262262f,0.0949037f},{-0.304456f,0.303926f,-0.282345f},{-0.230684f,0.208173f,0.154329f}, +{-0.438227f,-0.0888203f,-0.00416705f},{-0.317356f,-0.279991f,0.207871f},{-0.309556f,0.298486f,-0.301161f}, +{-0.394203f,0.00450149f,0.299174f},{-0.497608f,0.0648661f,0.118202f},{0.474933f,-0.375789f,0.133629f}, +{0.47793f,-0.375473f,0.114311f},{0.479036f,-0.375467f,0.0950195f},{0.47784f,-0.375454f,0.0757275f}, +{0.474194f,-0.375776f,0.0564869f},{0.470213f,-0.374702f,0.0414842f},{0.4642f,-0.378284f,0.0329765f}, +{0.456767f,-0.376547f,0.0194078f},{0.447642f,-0.382708f,0.0118903f},{0.439333f,-0.378206f,0.000366566f}, +{0.427507f,-0.384714f,-0.00714445f},{0.417784f,-0.379435f,-0.0165654f},{-0.0898074f,0.0744092f,-0.0384811f}, +{0.174776f,-0.0560882f,-0.130272f},{0.295061f,-0.127295f,-0.121662f},{0.306707f,-0.393621f,-0.0401016f}, +{0.294945f,-0.398193f,-0.0587891f},{0.292759f,-0.395338f,-0.0759461f},{0.289241f,-0.393987f,-0.0908781f}, +{0.272901f,-0.395241f,-0.112292f},{0.255384f,-0.402244f,-0.11734f},{0.326739f,-0.0737533f,-0.0754767f}, +{0.0427671f,0.0614836f,-0.0778946f},{0.024787f,0.0598694f,-0.0759268f},{0.0609787f,-0.0979068f,-0.204971f}, +{0.370113f,-0.108202f,-0.0410212f},{-0.293151f,0.346593f,-0.225067f},{-0.349182f,-0.294312f,0.142934f}, +{-0.198035f,0.154304f,0.0579017f},{-0.460979f,0.0270666f,0.192534f},{-0.284065f,0.227549f,-0.0155815f}, +{-0.211939f,0.163828f,0.0225845f},{0.263307f,0.0682615f,-0.015575f},{-0.144294f,-0.489791f,-0.179949f}, +{0.302778f,-0.387962f,-0.0587505f},{0.298405f,-0.388778f,-0.0717211f},{0.283737f,-0.389698f,-0.106762f}, +{0.306045f,0.0186232f,-0.0359667f},{-0.0324716f,0.064043f,-0.0604032f},{-0.0153082f,0.0546413f,-0.0698948f}, +{0.270367f,0.0232468f,-0.0589048f},{0.058818f,-0.471593f,-0.152047f},{-0.238632f,0.0343269f,-0.0881515f}, +{0.251802f,0.0835021f,0.000527333f},{0.052143f,0.412617f,-0.165088f},{-0.468483f,0.0215491f,0.265831f}, +{-0.231687f,0.203267f,0.167236f},{-0.221269f,0.170843f,0.173268f},{-0.335838f,-0.487309f,-0.0170734f}, +{-0.317948f,0.209016f,-0.0551107f},{-0.236909f,0.268364f,0.153243f},{-0.355079f,0.287393f,-0.246384f}, +{-0.217675f,0.171081f,0.0178065f},{-0.439738f,0.0120318f,0.289688f},{0.4833f,-0.35786f,0.13487f}, +{0.484611f,-0.361365f,0.114292f},{0.485865f,-0.361275f,0.0949809f},{0.484656f,-0.35912f,0.0740555f}, +{0.481518f,-0.357223f,0.0563648f},{0.474387f,-0.357886f,0.0371307f},{0.462078f,-0.359905f,0.0167197f}, +{0.453493f,-0.358374f,0.00308674f},{0.444111f,-0.362361f,-0.0054146f},{0.0754349f,0.029954f,-0.102312f}, +{-0.474747f,0.0408347f,0.0529051f},{0.323382f,-0.376927f,-0.03742f},{0.314842f,-0.377467f,-0.045722f}, +{0.310508f,-0.375879f,-0.0588663f},{0.30654f,-0.373197f,-0.0730652f},{0.299865f,-0.373756f,-0.0897206f}, +{0.29492f,-0.379743f,-0.0974952f},{0.289422f,-0.375679f,-0.110389f},{0.271101f,-0.378528f,-0.12743f}, +{0.00521203f,0.0642294f,-0.0609819f},{0.321961f,0.00183919f,-0.0339667f},{0.286959f,-0.0164046f,-0.0771872f}, +{0.420176f,-0.265104f,0.0233819f},{0.0740008f,-0.0641651f,-0.143892f},{-0.0716794f,0.0824154f,-0.0214269f}, +{-0.29319f,0.346394f,-0.302228f},{-0.280541f,-0.282036f,0.227324f},{-0.146101f,0.253818f,0.0192406f}, +{-0.390807f,-0.220514f,0.206148f},{0.0528182f,0.402739f,-0.162246f},{-0.233102f,0.193158f,0.179949f}, +{0.486489f,-0.352252f,0.127153f},{0.487975f,-0.352124f,0.114273f},{0.489042f,-0.352079f,0.0949487f}, +{0.488078f,-0.352059f,0.0788785f},{0.468322f,-0.354085f,0.0241922f},{0.0194559f,0.0802225f,-0.0425388f}, +{0.0245297f,0.0399794f,-0.0934954f},{-0.482663f,0.0772772f,0.0602553f},{0.330353f,-0.362123f,-0.0421208f}, +{0.317839f,-0.368535f,-0.0525771f},{0.280033f,-0.371969f,-0.123237f},{0.00321209f,0.0560947f,-0.074467f}, +{-0.0921032f,-0.0124755f,-0.115996f},{0.155805f,0.0435163f,-0.0979968f},{0.0239831f,0.0742355f,-0.0537089f}, +{-0.30209f,0.342954f,-0.148047f},{-0.376994f,-0.248468f,0.193524f},{-0.414832f,-0.0382303f,-0.0205588f}, +{0.489524f,-0.338581f,0.133584f},{0.492798f,-0.338304f,0.114247f},{0.494219f,-0.338182f,0.0949166f}, +{0.492907f,-0.338272f,0.0756117f},{0.488952f,-0.337687f,0.0576573f},{0.484521f,-0.343552f,0.0499148f}, +{0.48103f,-0.339385f,0.0371307f},{0.471615f,-0.340298f,0.0210733f},{0.465313f,-0.340876f,0.0115109f}, +{0.455661f,-0.337333f,-0.00201921f},{0.448323f,-0.346883f,-0.00728592f},{0.229906f,0.0176136f,-0.0870004f}, +{0.341992f,-0.359969f,-0.0333044f},{0.32272f,-0.355056f,-0.0600366f},{0.312495f,-0.356863f,-0.0761004f}, +{0.303948f,-0.356149f,-0.0911996f},{0.291717f,-0.357255f,-0.110556f},{0.279017f,-0.362921f,-0.123044f}, +{-0.0522717f,0.0835278f,-0.0215491f},{-0.0338478f,0.0893026f,-0.0163596f},{0.156378f,0.0574451f,-0.0872576f}, +{0.267853f,-0.168811f,-0.147146f},{-0.126996f,0.0363654f,-0.0887431f},{0.0581042f,0.100196f,-0.0409376f}, +{-0.486476f,0.112498f,0.0762162f},{-0.293994f,0.326189f,-0.263683f},{-0.396801f,-0.208456f,0.211575f}, +{-0.43289f,-0.204662f,0.135134f},{-0.458406f,-0.0975531f,0.0515611f},{-0.216016f,0.173004f,0.154812f}, +{-0.235005f,0.268229f,0.138819f},{0.487081f,-0.334253f,0.146368f},{-0.0743738f,-0.469959f,-0.181248f}, +{-0.301569f,0.0376644f,-0.0603903f},{-0.497852f,0.0824411f,0.166387f},{0.346326f,-0.34215f,-0.0371949f}, +{0.332333f,-0.343796f,-0.058474f},{0.319626f,-0.349854f,-0.0715218f},{0.272406f,-0.359423f,-0.128658f}, +{0.26303f,-0.355448f,-0.142593f},{-0.0144658f,-0.115945f,-0.219247f},{-0.0125495f,0.0629755f,-0.0593807f}, +{-0.444941f,0.172072f,0.0224495f},{0.00631167f,-0.0170863f,-0.127764f},{-0.0145301f,-0.106228f,-0.209845f}, +{-0.45222f,0.0166683f,0.285078f},{-0.477821f,0.0350407f,0.274229f},{-0.23015f,0.22796f,0.134928f}, +{-0.207681f,0.188823f,0.0963763f},{-0.218221f,0.208353f,0.11563f},{-0.0711649f,-0.482081f,0.33824f}, +{-0.374094f,-0.00108035f,0.286422f},{0.36958f,-0.0675798f,0.176342f},{0.492753f,-0.320472f,0.133578f}, +{0.496836f,-0.320125f,0.114241f},{0.498785f,-0.319958f,0.0948909f},{0.498148f,-0.320028f,0.0755539f}, +{0.493563f,-0.320356f,0.056249f},{0.488553f,-0.319424f,0.0412334f},{0.484058f,-0.322781f,0.0328607f}, +{0.473416f,-0.322298f,0.0177486f},{-0.22552f,-0.03306f,-0.116273f},{0.339709f,-0.333494f,-0.0574579f}, +{0.326713f,-0.335603f,-0.0747114f},{0.308263f,-0.338452f,-0.0923764f},{0.291325f,-0.340073f,-0.111836f}, +{0.280914f,-0.338613f,-0.125436f},{0.272721f,-0.342388f,-0.13397f},{0.265339f,-0.34143f,-0.145802f}, +{0.174396f,-0.105939f,-0.17647f},{0.265249f,-0.0190926f,-0.0887624f},{-0.0135334f,0.0801711f,-0.0390084f}, +{-0.110829f,-0.0155043f,-0.117257f},{-0.321427f,-0.122806f,-0.102132f},{-0.431456f,0.226842f,0.037002f}, +{-0.292598f,0.346593f,-0.244365f},{-0.289524f,0.376715f,-0.334407f},{-0.442336f,-0.0914504f,0.00441145f}, +{0.155136f,0.0992123f,-0.0394971f},{-0.220401f,0.0890261f,-0.0467252f},{-0.297453f,0.226758f,-0.0571235f}, +{-0.469004f,0.175049f,0.230147f},{-0.48101f,0.026893f,0.136169f},{-0.225771f,-0.290621f,0.23369f}, +{-0.47629f,0.208f,0.173692f},{-0.47476f,0.13559f,0.0549565f},{0.364159f,-0.322453f,-0.020726f}, +{0.357233f,-0.317565f,-0.0383332f},{0.351915f,-0.325732f,-0.0437799f},{0.321292f,-0.331642f,-0.0844281f}, +{0.298476f,-0.334195f,-0.104402f},{0.31126f,-0.0140253f,-0.0577152f},{0.2842f,-0.11408f,-0.124851f}, +{-0.493222f,0.0603582f,0.0957654f},{-0.39111f,-0.0106042f,0.244256f},{-0.332321f,0.414868f,-0.355223f}, +{-0.287962f,0.37674f,-0.343995f},{-0.288611f,0.321456f,-0.167165f},{0.191849f,0.0695348f,-0.0683f}, +{-0.292753f,0.346343f,-0.321507f},{0.272162f,0.0412913f,-0.0399923f},{-0.364384f,-0.290068f,0.113553f}, +{-0.290245f,0.303585f,-0.207402f},{-0.438266f,0.0121475f,0.198039f},{-0.353182f,-0.258943f,0.20755f}, +{-0.0692872f,-0.478042f,0.349088f},{0.493981f,-0.302543f,0.133571f},{0.49845f,-0.302164f,0.114215f}, +{0.5f,-0.302022f,0.0948523f},{0.499408f,-0.302125f,0.075451f},{0.494373f,-0.30255f,0.0561654f}, +{0.488656f,-0.304537f,0.0410662f},{0.483975f,-0.30199f,0.0326163f},{0.472972f,-0.30226f,0.0202695f}, +{-0.390042f,-0.0561139f,-0.0530401f},{-0.404878f,-0.0396f,-0.0322047f},{-0.261004f,0.172316f,-0.0231825f}, +{0.346963f,-0.318073f,-0.058892f},{0.339401f,-0.315096f,-0.0698112f},{0.331613f,-0.317353f,-0.078454f}, +{0.323736f,-0.317096f,-0.089862f},{0.31263f,-0.323109f,-0.0999839f},{0.305659f,-0.318221f,-0.10846f}, +{0.294913f,-0.320723f,-0.117089f},{0.286123f,-0.320543f,-0.12869f},{0.27793f,-0.3268f,-0.13651f}, +{0.267532f,-0.319366f,-0.146092f},{0.227559f,0.0757082f,-0.0364232f},{-0.203444f,0.0201601f,-0.0971351f}, +{0.243847f,0.07048f,-0.0308672f},{-0.289602f,0.281271f,-0.115675f},{-0.290766f,0.231555f,-0.0258319f}, +{0.384582f,-0.301264f,-0.00464292f},{-0.262683f,-0.4823f,0.299836f},{-0.372847f,-0.242108f,0.20863f}, +{-0.354024f,-0.299405f,0.114273f},{-0.411032f,-0.225543f,0.151416f},{-0.226414f,0.170818f,0.00573617f}, +{-0.409308f,0.226379f,0.00172344f},{-0.458477f,-0.115012f,0.0517025f},{0.367078f,-0.304923f,-0.0234912f}, +{0.363471f,-0.29817f,-0.0387833f},{0.35414f,-0.310646f,-0.0525321f},{0.440221f,-0.285418f,0.00548537f}, +{-0.489634f,0.0610013f,0.230771f},{-0.332147f,0.289515f,-0.319668f},{-0.256098f,-0.294389f,0.219202f}, +{-0.293325f,0.361718f,-0.28291f},{-0.292663f,0.32653f,-0.147854f},{-0.3932f,-0.261503f,0.116517f}, +{-0.28883f,0.301283f,-0.122131f},{-0.400505f,-0.231021f,0.173641f},{-0.429925f,0.00713804f,0.209775f}, +{0.498077f,-0.284345f,0.114189f},{0.499132f,-0.284261f,0.0948201f},{0.496688f,-0.284467f,0.075451f}, +{0.491164f,-0.284962f,0.0561461f},{0.487203f,-0.289791f,0.0432977f},{0.482676f,-0.28275f,0.0357609f}, +{0.473917f,-0.282885f,0.0238256f},{-0.447249f,0.22717f,0.0597537f},{-0.438143f,0.226983f,0.0499534f}, +{-0.489145f,0.0429632f,0.095849f},{0.357027f,-0.296839f,-0.0525642f},{0.350564f,-0.299006f,-0.0612585f}, +{0.342082f,-0.295997f,-0.0719462f},{0.333517f,-0.303662f,-0.0786791f},{0.326456f,-0.299733f,-0.091444f}, +{0.317511f,-0.300447f,-0.104061f},{0.306643f,-0.302846f,-0.112331f},{0.285698f,-0.30264f,-0.128067f}, +{-0.472245f,0.174682f,0.152979f},{-0.469969f,0.152953f,0.230263f},{-0.483094f,0.0261792f,0.114871f}, +{0.233037f,-0.0750651f,-0.134279f},{-0.497949f,0.10864f,0.137256f},{-0.28955f,0.281232f,-0.131738f}, +{0.309447f,-0.150008f,-0.116639f},{-0.378332f,-0.206643f,0.233356f},{-0.374178f,0.0174464f,0.323186f}, +{-0.154873f,0.253696f,0.0322498f},{-0.379097f,0.268429f,-0.0550014f},{-0.217025f,0.18946f,0.136144f}, +{-0.400029f,0.226224f,-0.00787111f},{0.371573f,-0.287991f,-0.0247452f},{0.294225f,-0.297347f,-0.119456f}, +{-0.418485f,0.228025f,0.0135044f},{-0.42152f,0.239954f,0.0242179f},{-0.146127f,0.0584097f,-0.0732259f}, +{-0.394048f,0.187267f,-0.0230346f},{-0.35032f,0.454493f,-0.352638f},{0.193405f,0.0027459f,-0.107791f}, +{0.081589f,0.100608f,-0.0416835f},{-0.0187743f,-0.00719588f,-0.126941f},{-0.292534f,0.361622f,-0.302228f}, +{-0.111498f,-0.470692f,0.322247f},{-0.395161f,0.417421f,-0.282634f},{-0.278509f,-0.48138f,0.29565f}, +{-0.452034f,0.0168805f,0.170657f},{0.249912f,-0.148297f,-0.151339f},{-0.473769f,0.209099f,0.18973f}, +{-0.337304f,-0.228623f,0.251394f},{-0.381907f,-0.26976f,0.114736f},{0.489659f,-0.269393f,0.131989f}, +{0.491434f,-0.262898f,0.113945f},{0.492939f,-0.262699f,0.0949745f},{0.491229f,-0.263754f,0.0761712f}, +{0.488727f,-0.26875f,0.0603775f},{0.482374f,-0.260397f,0.0549307f},{0.476316f,-0.262924f,0.0415485f}, +{-0.336732f,0.341301f,-0.320755f},{-0.370255f,0.226462f,-0.0519211f},{-0.486875f,0.0963699f,0.264159f}, +{-0.129002f,-0.0141925f,-0.118453f},{-0.473975f,0.0342111f,0.171956f},{0.384988f,-0.284827f,0.000218661f}, +{0.375277f,-0.280518f,-0.0187839f},{0.368731f,-0.277721f,-0.0397479f},{0.359631f,-0.277663f,-0.057773f}, +{0.353272f,-0.283605f,-0.0653548f},{0.346391f,-0.279721f,-0.0750522f},{0.32944f,-0.28219f,-0.0926465f}, +{0.319466f,-0.282113f,-0.104003f},{0.31234f,-0.282724f,-0.110183f},{0.29427f,-0.284422f,-0.118093f}, +{0.285814f,-0.285123f,-0.126459f},{0.275236f,-0.28192f,-0.137121f},{-0.299846f,0.416623f,-0.384965f}, +{-0.348873f,0.287103f,-0.285007f},{-0.481731f,0.112466f,0.261445f},{-0.490721f,0.0610913f,0.246835f}, +{-0.482721f,0.133417f,0.076885f},{-0.331993f,0.323031f,-0.336889f},{-0.361419f,-0.286872f,0.133931f}, +{0.0434166f,0.0389441f,-0.0937719f},{-0.381882f,-0.226649f,0.213209f},{-0.335645f,0.242629f,-0.188026f}, +{-0.464934f,0.0268287f,0.171313f},{-0.212331f,0.183621f,0.128427f},{-0.356551f,-0.2654f,0.192315f}, +{-0.464811f,-0.0710202f,0.0771808f},{0.0998135f,0.0067136f,-0.119469f},{-0.369374f,0.251902f,-0.150503f}, +{0.384724f,-0.263773f,-0.00166552f},{0.379303f,-0.262139f,-0.0197357f},{0.338102f,-0.275991f,-0.0849683f}, +{-0.260818f,0.0960612f,-0.036674f},{0.229462f,-0.0564998f,-0.129989f},{-0.0901997f,0.0448153f,-0.0784219f}, +{0.275017f,0.0672905f,0.000250814f},{-0.208537f,0.342838f,-0.0226938f},{-0.218665f,0.33842f,-0.0145654f}, +{-0.393907f,0.361911f,-0.225819f},{-0.288753f,0.291296f,-0.115675f},{-0.357632f,0.0262114f,0.336015f}, +{-0.2802f,-0.319816f,0.151204f},{0.00395806f,0.491913f,-0.130401f},{-0.108546f,0.0785827f,-0.037928f}, +{0.375007f,-0.257644f,-0.0355551f},{0.370068f,-0.261085f,-0.0441015f},{0.363824f,-0.260159f,-0.0559853f}, +{0.357625f,-0.260693f,-0.0655927f},{0.348629f,-0.261451f,-0.0752387f},{0.33897f,-0.262281f,-0.0850198f}, +{0.329677f,-0.263091f,-0.0945757f},{0.319382f,-0.263979f,-0.103945f},{0.305614f,-0.265239f,-0.113938f}, +{0.287537f,-0.265188f,-0.132986f},{0.0602585f,-0.0592071f,-0.144002f},{-0.374506f,0.339243f,-0.209807f}, +{-0.48773f,0.113192f,0.191936f},{-0.337098f,0.00798691f,0.287521f},{-0.0892158f,-0.452095f,-0.183222f}, +{-0.147966f,0.251985f,0.00030869f},{-0.317626f,-0.201383f,0.276441f},{-0.429514f,0.00733738f,0.2843f}, +{-0.456201f,0.191775f,0.0726279f},{-0.368409f,0.306633f,-0.168027f},{0.371798f,-0.0463908f,0.0113694f}, +{-0.449712f,-0.0191184f,0.115456f},{0.388711f,-0.241812f,-0.000347237f},{0.29854f,-0.261246f,-0.123199f}, +{0.193322f,0.0785634f,-0.0559982f},{-0.445076f,0.208418f,0.0577345f},{-0.446632f,0.114652f,0.000135062f}, +{-0.291299f,0.361577f,-0.321514f},{-0.143825f,0.0790135f,-0.0590656f},{-0.375097f,-0.0163917f,0.247973f}, +{-0.363078f,0.304755f,-0.189846f},{-0.384164f,0.248281f,-0.0467959f},{-0.486393f,0.0956818f,0.230578f}, +{0.395592f,-0.243368f,0.0176072f},{0.382673f,-0.240442f,-0.0205845f},{0.375734f,-0.24106f,-0.0366226f}, +{0.370049f,-0.241542f,-0.0461528f},{0.361844f,-0.24133f,-0.0578631f},{0.35668f,-0.247207f,-0.0655027f}, +{0.344455f,-0.24369f,-0.075348f},{0.336545f,-0.248879f,-0.0850455f},{0.329247f,-0.244101f,-0.0926529f}, +{0.319954f,-0.245812f,-0.104106f},{0.310488f,-0.246629f,-0.113681f},{0.302045f,-0.247388f,-0.123456f}, +{0.292071f,-0.244236f,-0.134555f},{0.191746f,-0.131372f,-0.183711f},{-0.0560657f,0.0633034f,-0.05722f}, +{-0.33852f,-0.267425f,0.209106f},{-0.408189f,-0.0210411f,0.207968f},{-0.355092f,0.00830841f,0.306119f}, +{-0.28775f,0.412212f,-0.363345f},{-0.0922511f,0.398026f,-0.094878f},{-0.470445f,0.153024f,0.249542f}, +{-0.306122f,0.290994f,-0.258841f},{0.394518f,-0.131931f,0.151461f},{0.0388572f,0.456101f,-0.170921f}, +{0.403649f,-0.224996f,0.0363525f},{0.398505f,-0.222951f,0.0189062f},{0.211135f,-0.130671f,-0.169917f}, +{0.0247484f,0.09219f,-0.0328735f},{-0.217874f,-0.35478f,0.114993f},{-0.148172f,0.0707695f,-0.0669753f}, +{-0.445616f,0.0105785f,0.0410791f},{-0.395753f,-0.226346f,0.190772f},{-0.289807f,0.311373f,-0.128562f}, +{-0.413726f,-0.00173626f,0.226334f},{-0.473853f,0.17447f,0.114408f},{-0.372769f,-0.278968f,0.115f}, +{0.407103f,-0.224147f,0.0562876f},{0.393553f,-0.221459f,0.00189706f},{0.389463f,-0.221806f,-0.00772964f}, +{0.383238f,-0.222353f,-0.0206103f},{0.376756f,-0.222925f,-0.0331693f},{0.369998f,-0.223485f,-0.0430468f}, +{0.360358f,-0.224282f,-0.0560239f},{0.344854f,-0.2256f,-0.0719269f},{0.329452f,-0.226874f,-0.0912253f}, +{0.318147f,-0.227851f,-0.107347f},{0.311839f,-0.228333f,-0.116691f},{0.302617f,-0.2256f,-0.126182f}, +{-0.126938f,0.0753609f,-0.0523327f},{-0.418189f,-0.0564933f,-0.0256004f},{-0.201379f,0.320523f,-0.0340053f}, +{0.289576f,0.0437799f,-0.0211054f},{-0.289962f,0.361538f,-0.33761f},{0.389624f,-0.0725443f,0.0563262f}, +{-0.276998f,-0.313218f,0.167654f},{-0.40718f,-0.221736f,0.171834f},{0.0390244f,0.449098f,-0.179878f}, +{-0.332185f,0.305341f,-0.333899f},{-0.312926f,-0.273162f,0.222655f},{0.408614f,-0.223125f,0.0928395f}, +{0.407874f,-0.22135f,0.0749108f},{-0.428935f,-0.0908717f,-0.0191826f},{-0.0479631f,0.471458f,-0.091399f}, +{-0.425977f,-0.0560368f,-0.0160638f},{0.0831967f,-0.0666924f,-0.148786f},{-0.288489f,0.361519f,-0.347256f}, +{-0.129748f,-0.0760812f,-0.133899f},{-0.479653f,0.210064f,0.146323f},{-0.343356f,0.357937f,-0.306935f}, +{-0.486457f,0.0412077f,0.153989f},{0.391669f,-0.126678f,0.159185f},{-0.362853f,0.219581f,-0.0601395f}, +{0.0237516f,-0.097611f,-0.205505f},{-0.359548f,0.440365f,-0.343513f},{0.250683f,0.061297f,-0.0380052f}, +{-0.352397f,0.2565f,-0.259509f},{0.406704f,-0.202302f,0.075721f},{0.405707f,-0.202399f,0.0564484f}, +{0.403431f,-0.202592f,0.0372078f},{0.399534f,-0.202932f,0.0179673f},{0.395135f,-0.203305f,0.00197423f}, +{0.390936f,-0.203665f,-0.00767819f},{0.383875f,-0.204238f,-0.0204431f},{0.376171f,-0.204868f,-0.0333237f}, +{0.36902f,-0.205453f,-0.0429439f},{0.359419f,-0.205299f,-0.057683f},{0.346622f,-0.20825f,-0.0732967f}, +{0.338899f,-0.207903f,-0.0846403f},{0.33007f,-0.208643f,-0.0942606f},{0.320938f,-0.209492f,-0.107334f}, +{0.31209f,-0.207247f,-0.117662f},{0.304186f,-0.208276f,-0.124137f},{-0.348384f,0.342317f,-0.284036f}, +{-0.463435f,0.174162f,0.0758754f},{-0.0486512f,0.452526f,-0.109765f},{-0.437288f,-0.036912f,0.0178515f}, +{-0.287653f,0.361377f,-0.357017f},{-0.356146f,0.241433f,-0.151095f},{-0.195598f,0.155313f,0.075914f}, +{-0.148108f,-0.0737147f,-0.130735f},{0.14133f,-0.107103f,-0.194894f},{-0.468625f,0.0943635f,0.0377994f}, +{-0.200338f,0.221523f,0.0386161f},{-0.221958f,0.0961384f,-0.0375936f},{-0.434401f,-0.0554902f,-3.21349e-05f}, +{-0.375329f,0.247966f,-0.0918041f},{-0.489807f,0.0955532f,0.192039f},{-0.465647f,-0.0924215f,0.135082f}, +{-0.476965f,0.210109f,0.155982f},{-0.439147f,-0.0508151f,0.012752f},{0.133317f,0.083007f,-0.0597923f}, +{-0.302971f,0.361506f,-0.204521f},{-0.319453f,-0.286955f,0.19263f},{-0.214652f,0.193659f,0.122118f}, +{0.404177f,-0.184483f,0.114234f},{0.405186f,-0.184412f,0.0949552f},{0.406189f,-0.184335f,0.0756825f}, +{0.405534f,-0.184406f,0.0564291f},{0.403225f,-0.184631f,0.0372078f},{0.398781f,-0.185023f,0.017948f}, +{0.393939f,-0.18692f,0.00299671f},{0.390113f,-0.184129f,-0.00556893f},{0.384145f,-0.18618f,-0.0205524f}, +{0.376242f,-0.186766f,-0.0365133f},{0.37111f,-0.187139f,-0.0461786f},{0.366667f,-0.187512f,-0.0558181f}, +{0.358789f,-0.187203f,-0.0704929f},{0.350661f,-0.193286f,-0.078184f},{0.343761f,-0.18836f,-0.0895662f}, +{0.333304f,-0.194669f,-0.0972444f},{0.325401f,-0.188663f,-0.107752f},{0.308463f,-0.187685f,-0.11752f}, +{-0.433809f,0.209929f,0.0358059f},{-0.345034f,0.306595f,-0.289315f},{-0.382981f,0.261805f,-0.0373814f}, +{-0.341838f,0.341352f,-0.304601f},{-0.388113f,0.346079f,-0.204952f},{-0.398852f,0.0174142f,-0.00798686f}, +{-0.412035f,0.0197743f,-0.00122181f},{-0.296122f,-0.0897206f,-0.106923f},{-0.301948f,0.364516f,-0.223478f}, +{-0.386737f,-0.241124f,0.18856f},{-0.319112f,0.290537f,-0.321147f},{-0.338076f,0.206218f,-0.0533487f}, +{-0.12697f,-0.407267f,-0.133867f},{-0.337954f,0.304826f,-0.320568f},{-0.419134f,0.21025f,0.0134851f}, +{-0.388872f,0.226096f,-0.0177036f},{-0.388595f,0.345294f,-0.132086f},{0.117434f,0.0805762f,-0.0601845f}, +{-0.29901f,0.363352f,-0.24279f},{-0.311588f,-0.313225f,0.14451f},{-0.214331f,0.203376f,0.0319475f}, +{0.393611f,-0.167204f,0.168843f},{0.398113f,-0.166876f,0.152773f},{0.40091f,-0.166696f,0.133513f}, +{0.402305f,-0.166606f,0.114221f},{0.403534f,-0.166528f,0.094923f},{0.404768f,-0.166451f,0.0756503f}, +{0.404312f,-0.166496f,0.0564226f},{0.401148f,-0.166773f,0.037195f},{0.395868f,-0.167249f,0.017993f}, +{0.392692f,-0.165918f,0.00290668f},{0.390479f,-0.169133f,-0.00570398f},{0.328179f,-0.168355f,-0.107971f}, +{-0.242484f,0.0954567f,-0.0339667f},{0.0977235f,-0.07684f,-0.168927f},{-0.184415f,0.0829427f,-0.0602038f}, +{-0.228298f,-0.255497f,0.259722f},{-0.126128f,0.11471f,0.0564998f},{-0.146963f,0.11898f,0.0773223f}, +{-0.430079f,0.282325f,0.0953989f},{-0.335414f,0.294782f,0.073824f},{-0.366808f,0.23007f,-0.0692132f}, +{-0.0931578f,0.0893219f,0.149326f},{-0.305775f,0.448204f,-0.372451f},{-0.316032f,0.450011f,-0.373281f}, +{-0.188209f,0.132562f,0.150548f},{-0.206929f,0.154124f,0.168522f},{-0.317549f,0.453014f,-0.358863f}, +{-0.130333f,0.0906209f,0.167557f},{-0.303402f,0.433298f,-0.345352f},{-0.308443f,0.443632f,-0.350227f}, +{-0.179335f,0.124279f,0.0366934f},{-0.191168f,0.128189f,0.0249896f},{-0.313228f,0.437652f,-0.336478f}, +{-0.322366f,0.450899f,-0.34361f},{-0.444799f,0.276518f,0.0951288f},{-0.337523f,0.455567f,-0.337385f}, +{-0.356281f,0.455972f,-0.341931f},{-0.311697f,0.42108f,-0.317887f},{-0.321691f,0.43542f,-0.323424f}, +{-0.337471f,0.449105f,-0.326665f},{-0.389502f,0.308234f,-0.129102f},{-0.298617f,0.296402f,0.132041f}, +{-0.215308f,0.160136f,0.179551f},{-0.392595f,0.260873f,0.244687f},{-0.335825f,0.441696f,-0.317353f}, +{-0.355593f,0.452333f,-0.320601f},{-0.167567f,0.0814058f,0.173416f},{-0.355805f,0.274969f,0.230449f}, +{-0.163104f,0.121951f,0.0571043f},{-0.172686f,0.125867f,0.0598116f},{-0.183509f,0.13296f,0.0551494f}, +{-0.438111f,0.274165f,0.0738883f},{-0.39037f,0.280261f,0.206212f},{-0.286933f,0.295437f,0.09891f}, +{-0.189541f,0.367107f,0.0278641f},{-0.310167f,0.405755f,-0.299785f},{-0.318315f,0.4167f,-0.302633f}, +{-0.324835f,0.428173f,-0.309591f},{-0.337414f,0.432629f,-0.303334f},{-0.36994f,0.449928f,-0.321591f}, +{0.230948f,0.0913283f,0.187473f},{-0.372377f,0.296679f,0.116266f},{0.270329f,-0.11226f,-0.131115f}, +{-0.109241f,0.0828205f,0.172181f},{-0.36284f,0.28774f,-0.188508f},{-0.352358f,0.287277f,-0.265683f}, +{-0.200942f,0.153596f,0.15177f},{0.00113499f,-0.0836629f,0.330485f},{-0.317067f,0.296203f,0.0975274f}, +{-0.397412f,0.274454f,0.215987f},{-0.429121f,0.278949f,0.07693f},{-0.323337f,0.296878f,-0.0186875f}, +{0.0342465f,0.466442f,-0.171712f},{0.0799106f,0.0706215f,-0.0687952f},{-0.341639f,0.218289f,-0.0877785f}, +{-0.431449f,0.283406f,0.132639f},{-0.330237f,0.42272f,-0.296106f},{-0.355143f,0.44095f,-0.302074f}, +{-0.338597f,0.314241f,-0.0275489f},{0.0233915f,-0.0809749f,0.338073f},{-0.0720588f,0.068403f,0.18575f}, +{-0.37576f,0.276531f,0.223311f},{-0.127504f,0.0984277f,0.151005f},{-0.354069f,0.0557667f,-0.0424938f}, +{-0.371419f,0.295662f,0.151609f},{-0.415745f,0.27138f,0.0370792f},{-0.370236f,0.315739f,-0.039973f}, +{-0.16884f,0.110607f,0.128999f},{-0.0144401f,0.113746f,0.11509f},{-0.160699f,0.0890775f,0.163107f}, +{-0.307247f,0.393351f,-0.284814f},{-0.315491f,0.401235f,-0.281521f},{-0.32171f,0.412205f,-0.289958f}, +{-0.369342f,0.447079f,-0.307064f},{-0.376493f,0.441323f,-0.300775f},{-0.387566f,0.438835f,-0.315012f}, +{-0.0306775f,0.108562f,0.111765f},{-0.451262f,0.26457f,0.0748851f},{-0.294997f,0.272962f,0.232475f}, +{-0.394325f,0.290004f,0.07675f},{-0.279422f,0.286518f,0.190367f},{-0.320758f,0.295894f,0.168618f}, +{-0.342372f,0.27374f,0.234976f},{-0.0900518f,0.0837143f,0.156786f},{-0.0354748f,0.100672f,0.136857f}, +{-0.109858f,0.0888846f,0.162252f},{0.176268f,0.121777f,0.172683f},{-0.166596f,0.0948201f,0.152947f}, +{-0.0411016f,0.104318f,0.115881f},{-0.149034f,0.107829f,0.128915f},{-0.186428f,-0.134111f,-0.131449f}, +{0.240864f,0.0891547f,0.184547f},{-0.266072f,0.309135f,0.0362561f},{-0.353317f,0.293238f,0.0387062f}, +{0.015141f,0.485644f,-0.152741f},{0.325215f,-0.0565898f,-0.0726407f},{-0.335562f,0.417601f,-0.281727f}, +{-0.35414f,0.431452f,-0.287849f},{0.15632f,0.109533f,0.200926f},{-0.111652f,0.107849f,0.112041f}, +{0.171162f,0.125732f,0.164606f},{0.264869f,0.0522877f,-0.0369055f},{0.400203f,-0.28601f,0.00717664f}, +{-0.319388f,0.279194f,0.223626f},{-0.409752f,0.281322f,0.0571364f},{-0.433713f,0.267953f,0.0543648f}, +{-0.267326f,0.278653f,0.208997f},{-0.0725668f,0.100846f,0.0960612f},{-0.261191f,0.290402f,0.133031f}, +{-0.183837f,0.113469f,0.152259f},{-0.464696f,0.244044f,0.0771615f},{0.260529f,0.0686216f,0.202148f}, +{-0.0344844f,0.0789814f,-0.0375743f},{-0.450934f,-0.0369441f,0.0577474f},{-0.196942f,0.340021f,-0.0344555f}, +{0.0789781f,0.0928973f,-0.0507379f},{0.214067f,0.0979454f,0.190264f},{0.205669f,0.10882f,0.181866f}, +{0.00593869f,0.48718f,-0.148947f},{-0.279383f,0.27774f,0.218797f},{-0.309968f,0.385808f,-0.260873f}, +{-0.320372f,0.399363f,-0.266821f},{-0.353239f,0.425633f,-0.278313f},{-0.371496f,0.433343f,-0.284197f}, +{-0.38938f,0.434545f,-0.300672f},{-0.380595f,0.294466f,0.150002f},{-0.14542f,-0.415112f,-0.13224f}, +{-0.0912929f,0.076422f,0.171576f},{0.155104f,0.127051f,0.16802f},{-0.335671f,0.296556f,0.0937076f}, +{-0.351838f,0.295617f,0.0773287f},{-0.243693f,0.319585f,0.0538697f},{0.137073f,0.132883f,0.152182f}, +{-0.47013f,0.156355f,0.0566799f},{-0.462323f,-0.0917205f,0.0598438f},{0.11935f,0.132227f,0.153474f}, +{-0.440922f,-0.0770072f,0.00667504f},{-0.146082f,0.0971866f,-0.0368348f},{-0.355728f,0.131931f,-0.0438699f}, +{-0.479653f,0.0475676f,0.192245f},{-0.35839f,0.294383f,0.0687695f},{-0.146603f,0.0898878f,0.168136f}, +{0.0804765f,0.132845f,0.132523f},{-0.33106f,0.404347f,-0.259246f},{-0.340597f,0.41281f,-0.265162f}, +{-0.355246f,0.418919f,-0.262332f},{-0.386293f,0.430526f,-0.2861f},{-0.461538f,-0.072152f,0.0592135f}, +{-0.288309f,0.0437028f,-0.0627182f},{-0.343336f,0.296164f,0.08347f},{0.0623806f,0.130375f,0.135288f}, +{0.0797948f,-0.0629498f,0.318909f},{-0.230877f,0.263702f,0.128967f},{-0.365876f,0.310183f,-0.182168f}, +{-0.464966f,-0.0927365f,0.0772773f},{-0.47721f,0.0314074f,0.22461f},{-0.42026f,0.00966527f,0.0123597f}, +{-0.406125f,0.187711f,-0.0143918f},{-0.177888f,0.0991286f,0.151969f},{0.027237f,0.126073f,0.112279f}, +{0.0190058f,0.122922f,0.118691f},{0.00530206f,0.119572f,0.112807f},{-0.3051f,0.373756f,-0.243593f}, +{-0.313459f,0.384123f,-0.244114f},{-0.322089f,0.394482f,-0.249677f},{-0.374557f,0.421697f,-0.264667f}, +{-0.378088f,0.427452f,-0.273914f},{0.219044f,0.102254f,0.177596f},{-0.387206f,0.269207f,-0.0319604f}, +{-0.48074f,0.0297547f,0.0799074f},{-0.393991f,0.286853f,0.0585898f},{0.0944375f,0.0846982f,0.234976f}, +{0.0764445f,0.126478f,0.16584f},{-0.388441f,-0.110948f,-0.0707823f},{-0.364101f,0.229986f,-0.0788528f}, +{-0.480361f,0.227729f,0.152683f},{-0.388557f,0.290627f,-0.0904215f},{0.308257f,0.0406997f,0.00110609f}, +{-0.400929f,-0.07048f,-0.0452718f},{-0.408286f,-0.0569885f,-0.0353236f},{-0.411064f,0.00485518f,0.00178774f}, +{-0.375721f,0.00127326f,-0.0385518f},{-0.385296f,0.295219f,0.110813f},{-0.0561622f,0.103682f,0.0916241f}, +{-0.467249f,0.209781f,0.0951224f},{-0.335041f,0.401756f,-0.24441f},{-0.355632f,0.412006f,-0.246732f}, +{-0.391798f,0.414746f,-0.265683f},{0.470805f,-0.247876f,0.0585576f},{-0.238587f,0.32462f,0.00147907f}, +{-0.109151f,0.110074f,0.0755603f},{-0.360043f,0.257895f,0.258191f},{0.101537f,0.0912768f,0.225864f}, +{-0.167053f,0.116504f,0.115141f},{0.287023f,0.0577538f,-0.0010739f},{0.284856f,0.0531816f,-0.0135301f}, +{-0.468876f,0.174335f,0.0951802f},{-0.399045f,-0.0575158f,-0.0449503f},{-0.400093f,0.378727f,-0.226436f}, +{-0.374956f,0.293463f,0.0760297f},{-0.425462f,0.277631f,0.0640816f},{-0.147458f,0.119385f,0.0581653f}, +{-0.307588f,0.374297f,-0.229079f},{-0.317787f,0.383943f,-0.225871f},{-0.325491f,0.393126f,-0.235574f}, +{-0.350609f,0.407144f,-0.240352f},{-0.372808f,0.413678f,-0.247793f},{0.47703f,-0.246468f,0.0764284f}, +{0.457545f,-0.244719f,0.054577f},{-0.392794f,0.206263f,-0.0201601f},{-0.318044f,0.274557f,0.233832f}, +{-0.19529f,0.141005f,0.15885f},{-0.418903f,-0.00291306f,0.195994f},{-0.110251f,0.109939f,0.0938877f}, +{0.336957f,0.0148291f,0.024996f},{0.329703f,0.0246037f,0.0209897f},{0.321279f,0.0350921f,0.0179866f}, +{0.312546f,0.0445259f,0.0194785f},{0.300553f,0.0499855f,0.00521528f},{-0.0685219f,-0.132896f,-0.203331f}, +{-0.244992f,0.32716f,0.0155172f},{-0.446407f,-0.0366998f,0.153982f},{-0.306334f,0.295019f,0.0873348f}, +{-0.336578f,0.396997f,-0.226526f},{-0.354583f,0.403067f,-0.225382f},{0.457339f,-0.237819f,0.0758818f}, +{0.439282f,-0.242822f,0.055696f},{-0.147137f,0.104035f,0.139082f},{-0.0884827f,0.102627f,0.0958747f}, +{-0.0929713f,0.0635414f,0.192791f},{0.201521f,0.0841066f,0.217446f},{-0.199193f,0.357339f,0.0339603f}, +{0.302103f,0.0538632f,0.0212405f},{0.290585f,0.0616122f,0.0160509f},{0.26568f,0.0744992f,0.000971048f}, +{-0.43916f,-0.103328f,-0.00632775f},{-0.442478f,-0.111842f,0.00203854f},{-0.497402f,0.0911225f,0.166406f}, +{-0.402846f,0.203852f,-0.0139802f},{-0.457789f,-0.0685766f,0.15732f},{0.264181f,0.0370149f,-0.0539018f}, +{-0.352989f,0.266654f,-0.266223f},{-0.349895f,0.305231f,-0.265753f},{-0.392872f,0.00293882f,-0.0182309f}, +{-0.483062f,0.0447574f,0.171904f},{-0.24251f,-0.278364f,0.244037f},{-0.461667f,-0.114781f,0.0613742f}, +{-0.0333334f,0.476094f,-0.111418f},{-0.308463f,0.37058f,-0.211029f},{-0.317376f,0.379827f,-0.208926f}, +{-0.372178f,0.404051f,-0.227247f},{-0.379489f,0.405768f,-0.237549f},{-0.299357f,0.269027f,-0.00139544f}, +{0.477088f,-0.244282f,0.0955082f},{0.439153f,-0.236635f,0.0765249f},{0.421128f,-0.237825f,0.0749429f}, +{0.419314f,-0.242944f,0.0557153f},{-0.460374f,0.110331f,0.0227003f},{-0.103575f,0.065895f,0.19373f}, +{-0.261287f,0.298331f,0.0754188f},{-0.243089f,0.300878f,0.0763898f},{-0.236362f,0.293277f,0.0843896f}, +{-0.454072f,0.27439f,0.134825f},{0.354757f,-0.00544677f,0.0437092f},{0.339497f,0.0163017f,0.0414006f}, +{0.332623f,0.0255554f,0.0390341f},{0.32326f,0.0375487f,0.037285f},{0.283171f,0.0696569f,0.0240378f}, +{0.269686f,0.0779203f,0.0181924f},{0.245751f,0.0899457f,0.00502237f},{-0.405611f,-0.0968008f,-0.0510208f}, +{-0.438028f,-0.0347256f,0.172837f},{-0.383103f,0.00792259f,-0.0261085f},{-0.338809f,0.360342f,-0.318999f}, +{-0.489891f,0.0399794f,0.133591f},{0.0094627f,-0.081798f,0.332973f},{-0.329896f,0.387981f,-0.203415f}, +{-0.340217f,0.394778f,-0.20973f},{-0.355574f,0.314614f,-0.0240507f},{-0.377991f,0.305109f,-0.0350792f}, +{0.460464f,-0.236353f,0.0941063f},{-0.275962f,0.296003f,0.0702357f},{0.100617f,0.108286f,0.205749f}, +{-0.0984117f,0.10646f,0.0950516f},{0.356628f,-0.00610268f,0.058384f},{0.349535f,0.00374265f,0.0549243f}, +{0.339934f,0.017022f,0.0585833f},{0.315807f,0.0460242f,0.0391756f},{0.306816f,0.0548278f,0.0411177f}, +{0.294232f,0.0638886f,0.0342047f},{0.250484f,0.0926272f,0.0191312f},{0.233706f,0.102003f,0.0171892f}, +{-0.147465f,0.036166f,-0.0884216f},{-0.396781f,0.341796f,-0.169486f},{-0.445191f,0.0280056f,0.0249381f}, +{-0.455976f,-0.0343011f,0.117044f},{-0.399013f,0.00875856f,-0.00724091f},{-0.127176f,0.0845696f,-0.0427317f}, +{-0.198408f,0.156471f,0.135269f},{0.115016f,-0.0638114f,0.317102f},{-0.304714f,0.35912f,-0.186592f}, +{-0.31072f,0.367512f,-0.188766f},{-0.320218f,0.377171f,-0.190232f},{-0.355548f,0.397447f,-0.206238f}, +{-0.373445f,0.395544f,-0.207402f},{0.477088f,-0.245683f,0.114575f},{0.451506f,-0.234211f,0.100293f}, +{0.441243f,-0.234005f,0.0964471f},{0.431976f,-0.235073f,0.0881644f},{0.412736f,-0.234275f,0.0805955f}, +{0.0239703f,0.103148f,0.190881f},{0.332636f,0.0271889f,0.0590271f},{0.32333f,0.0394714f,0.0564226f}, +{0.284232f,0.0735796f,0.0411948f},{0.27004f,0.0828591f,0.03506f},{0.212665f,0.113f,0.0187711f}, +{-0.478534f,0.0301598f,0.255979f},{-0.479448f,0.196611f,0.117495f},{-0.356821f,0.305463f,-0.227144f}, +{-0.460966f,0.191904f,0.0822739f},{-0.367432f,0.287933f,-0.149931f},{-0.45667f,-0.0321533f,0.0962156f}, +{0.248201f,0.0532009f,-0.0508022f},{-0.0157262f,0.109566f,0.133494f},{-0.284592f,0.29538f,0.113746f}, +{-0.0297643f,0.486023f,-0.0808334f},{-0.333722f,0.385505f,-0.187061f},{-0.342976f,0.390939f,-0.191402f}, +{-0.355657f,0.392547f,-0.189936f},{0.458824f,-0.236513f,0.115424f},{0.421507f,-0.235311f,0.0960162f}, +{-0.25921f,0.294865f,0.0855085f},{0.355419f,-0.00317029f,0.0764863f},{0.349342f,0.00503524f,0.0742613f}, +{0.307035f,0.0570336f,0.0588727f},{0.295138f,0.0672519f,0.0544034f},{0.262233f,0.0901515f,0.0422559f}, +{0.250394f,0.0972316f,0.0371307f},{0.228137f,0.107701f,0.0250796f},{0.19574f,0.119154f,0.0164239f}, +{-0.312656f,0.361332f,-0.372072f},{-0.481377f,0.209865f,0.117417f},{-0.443989f,-0.0430726f,0.168753f}, +{-0.459654f,-0.0406611f,0.112292f},{-0.44478f,0.278126f,0.137809f},{-0.390061f,0.280788f,0.0173564f}, +{-0.128552f,0.115482f,0.0745571f},{-0.371072f,0.297926f,-0.00272658f},{-0.304694f,0.354104f,-0.169692f}, +{-0.314495f,0.36494f,-0.168612f},{-0.32263f,0.3724f,-0.170059f},{-0.369805f,0.39173f,-0.192058f}, +{-0.394872f,0.379126f,-0.208585f},{0.474046f,-0.249818f,0.134298f},{0.453641f,-0.23443f,0.115418f}, +{0.440137f,-0.234076f,0.110331f},{0.420954f,-0.237388f,0.115302f},{0.413598f,-0.233452f,0.100183f}, +{0.25294f,-0.111566f,-0.137037f},{0.0383942f,0.104601f,0.196084f},{0.115466f,0.108762f,0.204881f}, +{0.34041f,0.0172792f,0.0793672f},{0.333317f,0.0269252f,0.0777274f},{0.324096f,0.0392913f,0.075078f}, +{0.286187f,0.0757339f,0.0602232f},{0.274708f,0.0836243f,0.0544677f},{0.264985f,0.0908846f,0.0569564f}, +{0.240156f,0.103527f,0.038539f},{0.229713f,0.109469f,0.0399409f},{0.210678f,0.118607f,0.039465f}, +{0.178737f,0.124163f,0.0155043f},{-0.478348f,0.214565f,0.168824f},{-0.0894087f,-0.0338703f,-0.131456f}, +{0.0999292f,-0.0885438f,-0.188676f},{-0.339047f,0.283894f,-0.305527f},{0.0879553f,-0.0823446f,0.351108f}, +{0.225797f,0.0867239f,0.198463f},{0.122147f,0.12426f,0.179261f},{-0.335253f,0.38092f,-0.170773f}, +{-0.348378f,0.389055f,-0.179621f},{-0.377785f,0.384843f,-0.184393f},{-0.389978f,0.37728f,-0.190778f}, +{0.459808f,-0.240565f,0.131944f},{0.439822f,-0.235362f,0.119745f},{0.411418f,-0.231735f,0.108935f}, +{0.407669f,-0.22553f,0.113996f},{0.305698f,0.0594129f,0.0766921f},{0.287164f,0.0762033f,0.0766535f}, +{0.249706f,0.100743f,0.0565255f},{0.196254f,0.123835f,0.0345841f},{0.186711f,0.125617f,0.0280377f}, +{0.172474f,0.127231f,0.0207132f},{0.154731f,0.12907f,0.017665f},{-0.374281f,-0.0746278f,-0.0723256f}, +{-0.498122f,0.0952895f,0.121231f},{-0.163258f,0.379737f,-0.0499984f},{0.162281f,0.123822f,0.175261f}, +{-0.44386f,-0.127134f,0.185685f},{-0.316205f,0.359931f,-0.151564f},{-0.354854f,0.385492f,-0.168085f}, +{-0.374268f,0.380804f,-0.170631f},{0.440259f,-0.239105f,0.134111f},{-0.0803994f,0.0880743f,0.144413f}, +{0.339741f,0.0176715f,0.098492f},{0.332796f,0.0273818f,0.0974567f},{0.322424f,0.0410083f,0.0941771f}, +{0.316019f,0.0491302f,0.0851934f},{0.274972f,0.0856821f,0.0744221f},{0.266425f,0.0920099f,0.0758175f}, +{0.231353f,0.11098f,0.0588534f},{0.212369f,0.120221f,0.0584097f},{0.188364f,0.127661f,0.0408733f}, +{0.17457f,0.130658f,0.0373557f},{0.136275f,0.130671f,0.0183531f},{0.11834f,0.0629433f,-0.0799717f}, +{-0.380486f,0.225883f,-0.0275361f},{-0.479396f,0.130022f,0.0632584f},{-0.477133f,0.0945629f,0.0505965f}, +{-0.367663f,0.295219f,0.084216f},{-0.0727276f,0.458262f,-0.0540883f},{-0.085576f,0.449574f,-0.0604868f}, +{-0.338063f,0.376001f,-0.154259f},{0.456767f,-0.248269f,0.14977f},{0.419372f,-0.244224f,0.134716f}, +{0.407443f,-0.240468f,0.132735f},{0.306257f,0.058847f,0.095251f},{0.287801f,0.0757339f,0.0952188f}, +{0.252381f,0.101f,0.075168f},{0.200768f,0.124543f,0.0515546f},{0.191335f,0.128581f,0.0587441f}, +{0.156165f,0.134021f,0.0397479f},{0.136558f,0.135256f,0.0385776f},{0.118996f,0.130954f,0.0188997f}, +{0.100482f,0.129096f,0.0171442f},{-0.0688242f,-0.0185718f,-0.127745f},{-0.458008f,-0.0671618f,0.0515482f}, +{0.420054f,-0.285148f,0.00508667f},{-0.350346f,0.442442f,-0.351911f},{-0.452863f,-0.0364875f,0.134729f}, +{-0.095042f,0.443433f,-0.0547635f},{-0.0495f,-0.189582f,0.356001f},{-0.303575f,0.339359f,-0.132311f}, +{-0.31009f,0.346568f,-0.131603f},{-0.320289f,0.356259f,-0.132285f},{-0.330321f,0.368245f,-0.145224f}, +{-0.354294f,0.378142f,-0.14941f},{-0.37131f,0.375396f,-0.154053f},{0.456825f,-0.25558f,0.159062f}, +{0.438388f,-0.247921f,0.153198f},{0.423404f,-0.248301f,0.148227f},{-0.40898f,0.277001f,0.0416578f}, +{-0.045976f,0.106029f,0.0999646f},{-0.103595f,0.104698f,0.116903f},{0.34005f,0.0165011f,0.116993f}, +{0.333021f,0.0263915f,0.116016f},{0.322456f,0.0400566f,0.114016f},{0.275718f,0.0854314f,0.0930131f}, +{0.266522f,0.0921321f,0.096775f},{0.242156f,0.106968f,0.0805376f},{0.231269f,0.112427f,0.0765956f}, +{0.213617f,0.120929f,0.0772001f},{0.17329f,0.133198f,0.055188f},{0.116591f,0.13552f,0.0403331f}, +{0.0802193f,0.127379f,0.0192598f},{-0.493428f,0.0950774f,0.0955275f},{0.2484f,0.0440115f,-0.0601652f}, +{-0.370294f,0.253812f,-0.169512f},{-0.111022f,0.432783f,-0.0540754f},{-0.341767f,0.371763f,-0.140233f}, +{0.00490979f,0.107778f,0.165281f},{0.354532f,-0.00598052f,0.126903f},{0.306489f,0.0576638f,0.11372f}, +{0.251905f,0.101791f,0.0923829f},{0.155001f,0.136703f,0.0576573f},{0.0638982f,0.126054f,0.0237292f}, +{-0.471158f,0.227446f,0.0947944f},{0.210871f,-0.0339346f,-0.115122f},{-0.364416f,0.301225f,-0.175711f}, +{-0.415199f,0.171673f,-0.00596764f},{-0.0313398f,0.106003f,0.124022f},{-0.373933f,-0.114942f,0.265239f}, +{-0.30726f,0.337359f,-0.118234f},{-0.354751f,0.372734f,-0.135635f},{-0.365773f,0.371879f,-0.139565f}, +{-0.377734f,0.368914f,-0.148497f},{-0.249732f,0.278743f,0.182431f},{0.414472f,-0.255606f,0.157571f}, +{-0.366409f,0.244616f,0.275818f},{0.0588116f,0.123977f,0.0193756f},{-0.0135591f,-0.0344555f,-0.136722f}, +{-0.334996f,-0.0174078f,-0.070943f},{-0.331845f,0.0327707f,-0.0486544f},{-0.296154f,0.189544f,-0.0344876f}, +{-0.311447f,0.0182952f,-0.068628f},{-0.411861f,0.00317034f,0.28592f},{-0.336385f,0.0394071f,-0.0446609f}, +{-0.373824f,0.149577f,-0.0365519f},{-0.206145f,-0.0328928f,-0.118819f},{-0.164853f,0.36213f,-0.0554644f}, +{-0.0502331f,0.364876f,-0.104202f},{0.0241696f,0.436738f,-0.172361f},{-0.0166265f,0.37692f,-0.118337f}, +{-0.0686377f,0.380663f,-0.106048f},{-0.29883f,-0.487836f,0.113443f},{-0.294437f,-0.484492f,0.132182f}, +{-0.308141f,-0.482139f,0.178213f},{-0.315858f,-0.470853f,0.190759f},{-0.322353f,-0.472705f,0.211215f}, +{-0.321221f,-0.473354f,0.227131f},{-0.318231f,-0.473683f,0.247111f},{-0.312199f,-0.470326f,0.264217f}, +{-0.261795f,-0.470602f,0.309167f},{-0.445468f,-0.10873f,0.186689f},{-0.0117649f,0.455272f,-0.144748f}, +{-0.143323f,0.321115f,-0.0665766f},{-0.0528568f,-0.072287f,-0.145269f},{-0.388891f,0.147744f,-0.0298704f}, +{-0.309575f,0.112421f,-0.0459663f},{-0.2584f,0.111939f,-0.0322755f},{-0.130366f,0.321269f,-0.0709109f}, +{-0.0524003f,-0.48237f,0.339783f},{-0.446195f,0.0441143f,0.316884f},{0.0216617f,-0.0345262f,-0.137951f}, +{-0.352847f,-0.483708f,0.0576445f},{-0.356075f,-0.469773f,0.0596958f},{-0.350429f,-0.475136f,0.0710717f}, +{-0.341156f,-0.469335f,0.0783448f},{-0.33214f,-0.475084f,0.0933861f},{-0.323581f,-0.473946f,0.103637f}, +{-0.313916f,-0.47374f,0.112247f},{-0.304296f,-0.473528f,0.120806f},{-0.295865f,-0.474049f,0.132092f}, +{-0.309215f,-0.47648f,0.173525f},{-0.388344f,-0.183197f,0.240886f},{-0.309209f,0.0606862f,-0.0464551f}, +{-0.144937f,-0.469689f,0.286795f},{-0.1356f,-0.476428f,0.291116f},{-0.127562f,-0.46976f,0.302588f}, +{-0.0715572f,-0.470249f,0.35494f},{-0.409167f,-0.16748f,0.227555f},{-0.387206f,-0.136735f,0.256403f}, +{-0.392647f,-0.167596f,0.24468f},{-0.37884f,0.348703f,-0.110453f},{-0.3565f,-0.472139f,0.0207839f}, +{-0.357188f,-0.468531f,0.0413749f},{-0.287049f,-0.46821f,0.139822f},{-0.307768f,-0.464139f,0.169345f}, +{-0.312887f,-0.453194f,0.195717f},{-0.313858f,-0.451362f,0.207813f},{-0.316077f,-0.453792f,0.227613f}, +{-0.314012f,-0.451928f,0.247651f},{-0.309286f,-0.455181f,0.261207f},{-0.304244f,-0.449902f,0.266995f}, +{-0.295235f,-0.449838f,0.283444f},{-0.286007f,-0.449561f,0.292402f},{-0.277171f,-0.450313f,0.299952f}, +{-0.374519f,-0.186734f,0.249908f},{-0.331883f,-0.183576f,0.278467f},{-0.462709f,0.150928f,0.280904f}, +{-0.433076f,0.152747f,0.323128f},{-0.104116f,-0.464307f,0.334298f},{-0.0812482f,-0.461503f,0.352503f}, +{-0.0733385f,-0.451188f,0.354908f},{-0.336172f,-0.166644f,0.281759f},{-0.43777f,-0.108312f,0.197183f}, +{-0.147838f,-0.133494f,-0.137751f},{-0.0286004f,-0.225665f,0.353288f},{-0.408935f,0.226353f,0.280621f}, +{-0.429083f,0.225813f,0.264442f},{-0.370602f,-0.166496f,0.26077f},{-0.298154f,-0.314164f,0.152426f}, +{-0.0732806f,0.0510787f,-0.0722163f},{-0.334481f,-0.451728f,0.0750458f},{-0.328919f,-0.46104f,0.0899135f}, +{-0.3187f,-0.451317f,0.0951738f},{-0.312122f,-0.457619f,0.107006f},{-0.298617f,-0.453034f,0.113488f}, +{-0.291473f,-0.460133f,0.125867f},{-0.279428f,-0.454069f,0.132092f},{-0.29737f,-0.452873f,0.171982f}, +{-0.45276f,0.174856f,0.284865f},{-0.44431f,0.173737f,0.29774f},{-0.427289f,0.16712f,0.318903f}, +{-0.406575f,-0.18373f,0.22281f},{-0.366802f,-0.217016f,0.238655f},{-0.141523f,-0.451227f,0.283952f}, +{-0.127375f,-0.449658f,0.302408f},{-0.113157f,-0.44805f,0.322588f},{-0.103524f,-0.449651f,0.334819f}, +{-0.0899618f,-0.448269f,0.342323f},{0.271126f,-0.130645f,-0.13741f},{-0.0707276f,-0.203961f,0.30428f}, +{0.00260761f,-0.072017f,0.302408f},{-0.455802f,-0.0919906f,0.169216f},{-0.44559f,-0.0912961f,0.186283f}, +{-0.0584965f,-0.210964f,0.323957f},{0.173792f,0.00269444f,-0.113758f},{-0.348648f,-0.455509f,0.0179094f}, +{-0.349555f,-0.452879f,0.0386161f},{-0.350937f,-0.451349f,0.0576959f},{-0.284753f,-0.445658f,0.118749f}, +{-0.308392f,-0.440108f,0.208173f},{-0.310733f,-0.437607f,0.229247f},{-0.309234f,-0.439041f,0.247343f}, +{-0.298547f,-0.434102f,0.265612f},{-0.291383f,-0.432648f,0.279033f},{-0.282592f,-0.432314f,0.288492f}, +{-0.272618f,-0.43661f,0.296852f},{-0.458715f,0.178895f,0.272531f},{-0.0676602f,-0.18647f,0.32253f}, +{-0.437976f,-0.0909038f,0.196875f},{-0.136584f,-0.445799f,0.288981f},{-0.0789525f,-0.443819f,0.349481f}, +{-0.428742f,-0.091116f,0.208373f},{-0.0783094f,-0.204212f,0.290229f},{-0.0325874f,-0.208662f,0.359738f}, +{-0.476387f,0.095849f,0.29264f},{-0.464033f,0.192155f,0.248481f},{-0.355021f,-0.18438f,0.264718f}, +{-0.421417f,-0.0858814f,0.216456f},{0.211874f,-0.0764349f,-0.137063f},{-0.419778f,-0.133899f,0.216379f}, +{-0.345446f,-0.44749f,0.0345069f},{-0.342796f,-0.444474f,0.0628726f},{-0.323922f,-0.444275f,0.0805248f}, +{-0.304701f,-0.443548f,0.102254f},{-0.445847f,0.190984f,0.282139f},{-0.304759f,-0.432591f,0.208778f}, +{-0.471949f,0.22553f,0.189505f},{-0.305209f,-0.42996f,0.245233f},{-0.472509f,0.133822f,0.267766f}, +{-0.0580335f,-0.23016f,0.305907f},{-0.412189f,0.111244f,-0.0244944f},{-0.350809f,-0.201775f,0.259194f}, +{-0.0508891f,-0.209247f,0.341642f},{-0.132706f,-0.43178f,0.286441f},{-0.123234f,-0.432751f,0.30037f}, +{-0.114218f,-0.430378f,0.308453f},{-0.106373f,-0.431169f,0.321893f},{-0.0908621f,-0.431697f,0.335636f}, +{-0.0644835f,-0.435356f,0.353127f},{-0.380763f,-0.0849297f,0.253034f},{-0.449288f,0.15413f,0.303514f}, +{-0.427443f,0.0979583f,0.336549f},{-0.438742f,-0.157519f,0.176856f},{-0.482116f,0.0947301f,0.28201f}, +{-0.358661f,-0.0896756f,0.271914f},{-0.463943f,0.11282f,0.299669f},{-0.446722f,0.114215f,0.321578f}, +{-0.450188f,0.0614321f,0.31981f},{-0.335607f,-0.433182f,0.0214527f},{-0.335639f,-0.43232f,0.0373557f}, +{-0.335767f,-0.432803f,0.0577666f},{-0.328237f,-0.435549f,0.0709881f},{-0.317929f,-0.430771f,0.0761326f}, +{-0.310128f,-0.435304f,0.0892769f},{-0.296978f,-0.432571f,0.0957654f},{-0.276869f,-0.433388f,0.110524f}, +{-0.261345f,-0.428771f,0.115553f},{-0.452278f,-0.129121f,0.171351f},{-0.387547f,0.358644f,-0.150857f}, +{-0.446857f,-0.147513f,0.169577f},{-0.295409f,-0.417614f,0.246751f},{-0.29301f,-0.420887f,0.262281f}, +{-0.283827f,-0.413819f,0.267406f},{-0.276895f,-0.417755f,0.282338f},{-0.426626f,-0.151667f,0.205633f}, +{-0.419507f,-0.151211f,0.216675f},{-0.464561f,0.244564f,0.187544f},{-0.0957429f,-0.426546f,0.329423f}, +{-0.427842f,-0.109006f,0.208855f},{-0.0315842f,-0.131005f,0.356342f},{-0.221398f,0.341102f,0.0392399f}, +{-0.436317f,0.0953217f,0.328613f},{-0.44842f,0.0965886f,0.319385f},{-0.0449986f,-0.204238f,0.353802f}, +{-0.409996f,-0.111668f,0.228552f},{-0.448921f,0.0793415f,0.320652f},{-0.413983f,-0.189016f,0.209865f}, +{-0.418858f,-0.170207f,0.213517f},{-0.393039f,-0.149989f,0.249072f},{-0.472271f,0.114196f,0.286049f}, +{-0.0770361f,-0.184045f,0.303456f},{-0.0587087f,-0.111366f,0.307225f},{-0.466773f,0.13143f,0.28448f}, +{-0.304373f,-0.426906f,0.08338f},{-0.283203f,-0.42724f,0.0985177f},{-0.470818f,0.0794573f,0.303206f}, +{0.0180348f,-0.0658371f,0.300537f},{-0.472207f,0.0373171f,0.287296f},{-0.424388f,-0.0574644f,0.203434f}, +{-0.427327f,-0.186952f,0.188206f},{-0.355272f,0.247703f,-0.205633f},{-0.460972f,0.221845f,0.219196f}, +{-0.220324f,0.282094f,-0.0189769f},{-0.424035f,-0.167602f,0.206501f},{-0.124147f,-0.416437f,0.282898f}, +{-0.109241f,-0.416295f,0.30163f},{-0.100746f,-0.418707f,0.315951f},{-0.0343944f,-0.111752f,0.340491f}, +{-0.428613f,-0.0390084f,0.190444f},{-0.435346f,-0.151584f,0.188849f},{-0.433398f,0.177049f,0.306505f}, +{-0.292097f,0.0731423f,-0.0469374f},{-0.477171f,0.117597f,0.271355f},{-0.0671586f,-0.220334f,0.299341f}, +{-0.435584f,0.21434f,0.271271f},{-0.447121f,0.208231f,0.264737f},{-0.454374f,0.194328f,0.267734f}, +{-0.428761f,0.208746f,0.282788f},{-0.0505161f,-0.112524f,0.320318f},{-0.319511f,-0.419511f,0.057175f}, +{-0.30845f,-0.41908f,0.0689946f},{-0.295852f,-0.417035f,0.0745828f},{-0.280985f,-0.413292f,0.0778046f}, +{-0.276342f,-0.419292f,0.0914569f},{-0.260008f,-0.414514f,0.0950452f},{-0.254246f,-0.419215f,0.105797f}, +{-0.240407f,-0.41562f,0.111405f},{-0.461442f,-0.128395f,0.151854f},{-0.430112f,0.0803511f,0.327166f}, +{-0.05822f,-0.191975f,0.341243f},{-0.205334f,0.361525f,0.0194656f},{-0.462625f,0.20989f,0.229748f}, +{-0.464291f,0.225054f,0.206225f},{-0.280007f,-0.405132f,0.244384f},{-0.278727f,-0.404682f,0.250751f}, +{-0.273512f,-0.404321f,0.2605f},{-0.410048f,0.0592521f,0.327919f},{-0.420376f,-0.116427f,0.216385f}, +{-0.0372239f,-0.191145f,0.365609f},{-0.0676474f,-0.147635f,0.320208f},{-0.334912f,-0.035658f,-0.0790007f}, +{-0.11571f,-0.410964f,0.289772f},{-0.0967975f,-0.410778f,0.307231f},{-0.459918f,0.0791679f,0.313257f}, +{-0.462799f,0.173641f,0.266088f},{-0.37394f,-0.132311f,0.265773f},{-0.45458f,0.131385f,0.306106f}, +{-0.410099f,-0.133423f,0.229388f},{-0.458336f,0.0964535f,0.311083f},{-0.432144f,-0.168991f,0.189698f}, +{-0.434587f,0.197093f,0.28893f},{-0.384557f,0.377666f,-0.178637f},{-0.4486f,-0.0571364f,0.170059f}, +{-0.300611f,-0.412051f,0.061027f},{-0.268567f,-0.411479f,0.0835214f},{-0.245236f,-0.411138f,0.101141f}, +{-0.430729f,0.114665f,0.337526f},{-0.18547f,0.381428f,0.0018006f},{-0.446433f,0.225388f,0.245452f}, +{-0.259577f,-0.398058f,0.244584f},{-0.0629658f,-0.182476f,0.334748f},{-0.455841f,-0.10945f,0.169776f}, +{-0.354686f,-0.167313f,0.270351f},{0.021816f,0.453555f,-0.172824f},{-0.435938f,0.256969f,0.215987f}, +{-0.0266197f,-0.19027f,0.371377f},{-0.108488f,-0.400058f,0.282358f},{-0.0891708f,-0.400842f,0.300203f}, +{-0.0369474f,-0.167924f,0.367345f},{-0.480599f,0.0447832f,0.280563f},{-0.428247f,-0.131031f,0.205395f}, +{-0.471255f,0.0615029f,0.30219f},{-0.467905f,0.0962027f,0.302376f},{-0.0124852f,-0.112241f,0.356728f}, +{-0.0659497f,-0.201543f,0.315546f},{-0.43069f,0.0579081f,0.325719f},{-0.391721f,0.361043f,-0.168503f}, +{-0.0402592f,-0.108016f,0.328118f},{-0.0692936f,-0.165249f,0.322337f},{-0.415758f,0.231973f,0.269123f}, +{-0.0491078f,-0.168175f,0.356812f},{-0.411855f,-0.150805f,0.227131f},{-0.276252f,-0.403736f,0.0591621f}, +{-0.263139f,-0.393949f,0.0586862f},{-0.256587f,-0.398482f,0.0750908f},{-0.238754f,-0.400064f,0.0943507f}, +{-0.221173f,-0.398187f,0.111971f},{-0.0518022f,-0.0313495f,-0.13678f},{-0.461873f,0.126678f,0.295373f}, +{-0.0625993f,-0.164754f,0.334028f},{-0.469712f,0.248879f,0.172548f},{-0.316585f,0.30408f,-0.337539f}, +{-0.0603357f,-0.142793f,0.331031f},{-0.453995f,0.254313f,0.193306f},{-0.450413f,0.248108f,0.208013f}, +{-0.407579f,-0.202386f,0.205923f},{-0.0902254f,-0.390258f,0.286531f},{-0.454368f,0.227427f,0.228777f}, +{-0.458053f,0.230726f,0.216012f},{-0.0643806f,-0.130118f,0.316434f},{-0.0579306f,-0.16876f,0.342966f}, +{-0.386557f,0.300614f,-0.0578695f},{-0.39601f,0.0348349f,0.328883f},{-0.413051f,0.214733f,0.289624f}, +{-0.0550433f,-0.147391f,0.341378f},{-0.396743f,0.361866f,-0.187402f},{-0.0361757f,-0.0912382f,0.305373f}, +{-0.246677f,-0.390277f,0.0792386f},{-0.226819f,-0.3942f,0.100441f},{-0.26366f,-0.134787f,-0.126446f}, +{-0.436922f,-0.132131f,0.193434f},{-0.0280152f,-0.167911f,0.37211f},{-0.277692f,0.11217f,-0.0390727f}, +{-0.0964053f,-0.386515f,0.274377f},{-0.0776663f,-0.386161f,0.292003f},{-0.36648f,0.256198f,-0.207614f}, +{-0.396286f,-0.190219f,0.229266f},{-0.454702f,0.211504f,0.249484f},{-0.0332691f,-0.0973731f,0.320819f}, +{-0.241224f,-0.374187f,0.0783962f},{-0.236639f,-0.385737f,0.087277f},{-0.224665f,-0.375756f,0.0965114f}, +{-0.214909f,-0.377351f,0.114614f},{-0.360847f,-0.149674f,0.271348f},{-0.226536f,-0.378438f,0.208411f}, +{-0.211958f,-0.374168f,0.234558f},{-0.0197968f,-0.0909424f,0.325385f},{-0.0126588f,-0.0958169f,0.341037f}, +{-0.485203f,0.0782483f,0.283611f},{-0.0904955f,-0.378817f,0.263721f},{-0.0708563f,-0.373782f,0.263747f}, +{-0.435166f,0.231472f,0.252204f},{-0.44177f,-0.0567249f,0.181435f},{-0.433931f,-0.0563455f,0.191724f}, +{-0.478322f,0.0742806f,0.295245f},{-0.445044f,0.130986f,0.32208f},{-0.0440468f,-0.116491f,0.332086f}, +{-0.358898f,0.270249f,-0.244905f},{-0.415553f,-0.0548728f,0.211466f},{-0.195482f,0.374914f,0.00135046f}, +{-0.398286f,0.398322f,-0.265959f},{-0.422491f,-0.182303f,0.20245f},{-0.0459182f,-0.146323f,0.352953f}, +{-0.25505f,-0.376264f,0.0585705f},{-0.221964f,-0.359371f,0.208373f},{-0.216093f,-0.359487f,0.222231f}, +{-0.21026f,-0.356882f,0.230481f},{-0.457905f,0.155063f,0.288119f},{-0.427893f,0.242931f,0.244635f}, +{-0.0752998f,-0.37139f,0.253111f},{-0.360834f,-0.114736f,0.274769f},{-0.0265812f,-0.115848f,0.350857f}, +{-0.460439f,0.205389f,0.24441f},{-0.416607f,-0.0167583f,0.196129f},{-0.350076f,-0.14824f,0.277245f}, +{-0.411231f,-0.0384168f,0.209408f},{-0.315588f,0.00127969f,-0.0730394f},{0.172898f,-0.129436f,-0.189807f}, +{-0.0764638f,-0.166014f,0.30682f},{-0.428452f,0.19211f,0.298402f},{-0.250825f,-0.368805f,0.0646989f}, +{-0.389058f,0.322427f,-0.0952188f},{-0.356712f,-0.206849f,0.252834f},{-0.0465419f,-0.357403f,0.278377f}, +{-0.224099f,0.347449f,0.0191119f},{-0.404106f,-0.150387f,0.237523f},{-0.43651f,0.136484f,0.328948f}, +{-0.465236f,0.0237292f,0.280358f},{-0.0378927f,-0.229034f,0.34296f},{-0.225662f,-0.354555f,0.0966657f}, +{-0.218459f,-0.360895f,0.133224f},{-0.222896f,-0.359718f,0.149725f},{-0.226658f,-0.35885f,0.169487f}, +{-0.446072f,0.241349f,0.225652f},{-0.208961f,-0.338079f,0.231587f},{-0.199334f,-0.338716f,0.243671f}, +{-0.190434f,-0.338407f,0.253085f},{-0.430259f,0.0928652f,-0.0149255f},{-0.370763f,-0.20236f,0.24396f}, +{-0.428697f,0.131423f,0.336812f},{-0.134944f,-0.137848f,-0.140883f},{-0.380165f,-0.168901f,0.253413f}, +{-0.452137f,-0.0750715f,0.171486f},{-0.394569f,-0.13215f,0.248159f},{-0.223205f,-0.347867f,0.118093f}, +{-0.228395f,-0.3415f,0.153513f},{-0.229205f,-0.337693f,0.171049f},{-0.226941f,-0.341288f,0.187949f}, +{-0.222208f,-0.337764f,0.206135f},{-0.216453f,-0.335346f,0.219665f},{-0.0562201f,-0.340066f,0.26477f}, +{-0.045288f,-0.339288f,0.281103f},{-0.0397254f,-0.338864f,0.28927f},{-0.0306775f,-0.338214f,0.302916f}, +{-0.0162857f,-0.338002f,0.322864f},{-0.443153f,-0.0753416f,0.186393f},{-0.436272f,-0.0714447f,0.194534f}, +{-0.443584f,0.144947f,0.317508f},{-0.489775f,0.132587f,0.152317f},{-0.427597f,-0.0741005f,0.205923f}, +{-0.417887f,-0.0715154f,0.215511f},{-0.426838f,0.145391f,0.332716f},{-0.409778f,-0.0735732f,0.224424f}, +{-0.243153f,-0.337134f,0.138073f},{-0.238452f,-0.33559f,0.146426f},{-0.234761f,0.336902f,0.0319668f}, +{-0.373387f,0.055458f,-0.0377222f},{-0.00782935f,-0.332369f,0.334369f},{-0.399386f,-0.0709559f,0.233349f}, +{-0.39073f,-0.0730523f,0.24169f},{0.0324137f,-0.332806f,0.353249f},{-0.376943f,-0.0711102f,0.251529f}, +{-0.465454f,0.0420951f,0.300209f},{-0.354879f,0.254474f,-0.242256f},{-0.368152f,-0.0732902f,0.259702f}, +{-0.41426f,0.194174f,0.307849f},{-0.175792f,0.340015f,-0.0504935f},{-0.335864f,0.00357545f,0.269503f}, +{-0.247333f,-0.325314f,0.159545f},{-0.240471f,-0.322408f,0.170895f},{-0.229861f,-0.320549f,0.193357f}, +{-0.223835f,-0.318286f,0.208328f},{-0.216009f,-0.317411f,0.22324f},{-0.208305f,-0.316961f,0.233716f}, +{-0.200177f,-0.316536f,0.243838f},{-0.191431f,-0.316221f,0.253355f},{-0.181309f,-0.316112f,0.261619f}, +{-0.387148f,-0.0592585f,0.240423f},{-0.0554613f,-0.321855f,0.268049f},{-0.0459503f,-0.321289f,0.281219f}, +{-0.0403621f,-0.32089f,0.289322f},{-0.0314427f,-0.320228f,0.303116f},{-0.0189415f,-0.319321f,0.322369f}, +{-0.0096428f,-0.318704f,0.335745f},{-0.0709527f,-0.0763191f,-0.143185f},{-0.412138f,0.0923057f,-0.0229381f}, +{-0.315684f,0.037748f,-0.054159f},{-0.371953f,0.0373943f,-0.032089f},{-0.0837626f,-0.0820745f,-0.141339f}, +{-0.31917f,0.112447f,-0.0468409f},{-0.383554f,0.306099f,-0.0502749f},{-0.14951f,0.301977f,-0.0557538f}, +{-0.146989f,0.359506f,-0.0680235f},{-0.0904248f,0.416559f,-0.0912253f},{-0.242909f,0.281901f,-0.00522811f}, +{-0.14686f,0.340272f,-0.0684029f},{-0.277113f,0.00260441f,-0.0934632f},{-0.20972f,-0.303701f,0.234783f}, +{-0.192158f,-0.302948f,0.253696f},{-0.184525f,-0.297354f,0.261445f},{-0.056831f,-0.303765f,0.26886f}, +{-0.0474165f,-0.303135f,0.28221f},{-0.0416996f,-0.302781f,0.290126f},{-0.0336099f,-0.302768f,0.302871f}, +{-0.0201698f,-0.301997f,0.324903f},{-0.0113919f,-0.300704f,0.336915f},{-0.0915244f,-0.0767114f,-0.138401f}, +{-0.31216f,-0.0945757f,-0.104903f},{-0.456374f,0.113887f,0.308447f},{-0.401116f,-0.172303f,0.235144f}, +{-0.0199254f,-0.107077f,0.34858f},{-0.373367f,0.0742934f,-0.0393878f},{-0.379554f,0.0314266f,-0.0254075f}, +{-0.244844f,0.00772325f,-0.102247f},{0.118289f,-0.0103276f,-0.120999f},{-0.165966f,0.320832f,-0.0557924f}, +{0.00993857f,0.455458f,-0.164033f},{-0.233404f,0.301f,-0.0111057f},{-0.0537764f,0.398206f,-0.11527f}, +{-0.0713321f,0.322536f,-0.07711f},{-0.28993f,-0.0385711f,-0.105804f},{-0.0270956f,-0.0990386f,0.332851f}, +{-0.00941129f,-0.0826083f,0.319334f},{-0.255114f,0.31945f,0.0384232f},{-0.0271278f,-0.296923f,0.316292f}, +{-0.00624098f,-0.065567f,0.292544f},{-0.244638f,0.304627f,0.000353705f},{0.00179091f,-0.0658114f,0.293675f}, +{-0.461127f,0.187479f,0.260963f},{-0.360635f,-0.270557f,0.17285f},{-0.277834f,0.149751f,-0.0346741f}, +{-0.459937f,0.0661715f,0.313636f},{-0.356075f,0.252288f,-0.226777f},{-0.300714f,0.0763063f,-0.046275f}, +{-0.386563f,0.341648f,-0.116253f},{-0.0601685f,-0.287084f,0.268679f},{-0.0514228f,-0.285534f,0.283702f}, +{-0.0382014f,-0.284692f,0.306009f},{-0.0291856f,-0.282936f,0.322395f},{-0.0161056f,-0.28219f,0.341063f}, +{-0.00232471f,-0.286537f,0.350111f},{-0.335253f,-0.258924f,0.224443f},{-0.385155f,0.333037f,-0.0948522f}, +{-0.0333591f,0.43542f,-0.127803f},{-0.411855f,-0.207029f,0.191672f},{-0.446426f,0.022861f,0.300524f}, +{-0.277055f,0.170085f,-0.031208f},{-0.258972f,0.00383911f,-0.100222f},{-0.317266f,-0.245445f,0.248603f}, +{-0.272728f,0.300087f,0.0339153f},{0.230388f,-0.091444f,-0.138401f},{-0.0138677f,-0.131848f,-0.228597f}, +{-0.201257f,-0.274763f,0.258956f},{-0.0455002f,-0.279547f,0.297772f},{-0.225276f,0.00369118f,-0.1068f}, +{-0.370486f,0.256436f,-0.188766f},{-0.382705f,0.288885f,-0.0146748f},{0.00393876f,0.417485f,-0.150645f}, +{-0.385271f,0.287875f,-0.0345519f},{-0.260792f,-0.0337803f,-0.108704f},{-0.244831f,-0.0334458f,-0.111713f}, +{0.134803f,-0.116003f,-0.199814f},{-0.392602f,0.0739076f,-0.032269f},{-0.239179f,0.224848f,0.00285523f}, +{-0.334828f,0.0558953f,-0.0452461f},{-0.372088f,-0.0197486f,-0.0509951f},{-0.460844f,0.239163f,0.200495f}, +{-0.0567988f,-0.266589f,0.285836f},{-0.0479953f,-0.264763f,0.302518f},{-0.0426835f,-0.269921f,0.308286f}, +{-0.0364136f,-0.265336f,0.322581f},{-0.0279445f,-0.261644f,0.338645f},{-0.0204399f,-0.265175f,0.346375f}, +{-0.0110511f,-0.263548f,0.353577f},{-0.0415196f,-0.213491f,0.350619f},{-0.366557f,-0.114839f,-0.0851419f}, +{-0.387206f,0.286878f,-0.0535867f},{-0.0459696f,-0.223099f,0.337192f},{-0.23932f,0.111945f,-0.0249381f}, +{-0.368429f,-0.18074f,0.257278f},{-0.39747f,0.150677f,-0.024115f},{-0.40736f,0.0371628f,-0.0137487f}, +{-0.279833f,0.0576059f,-0.0560432f},{-0.18682f,-0.0372271f,-0.123777f},{-0.200994f,0.287045f,-0.0307f}, +{-0.405437f,0.0665188f,0.332163f},{-0.392486f,-0.00592905f,0.264339f},{-0.167876f,-0.0127777f,-0.115559f}, +{-0.431706f,0.110852f,-0.014887f},{-0.383843f,0.297264f,-0.0405903f},{-0.359869f,-0.136562f,0.274609f}, +{-0.205707f,-0.134285f,-0.130022f},{-0.379097f,-0.128182f,-0.07911f},{-0.167509f,-0.0371756f,-0.123816f}, +{-0.148236f,0.00388414f,-0.109051f},{-0.108071f,-0.0344876f,-0.125636f},{-0.130231f,-0.0316775f,-0.120556f}, +{-0.12252f,-0.0370213f,-0.122819f},{-0.390344f,-0.203717f,0.223781f},{-0.060863f,-0.248262f,0.289296f}, +{-0.053069f,-0.248204f,0.302363f},{-0.0462461f,-0.24533f,0.317038f},{-0.0402077f,-0.246346f,0.327642f}, +{-0.0311919f,-0.245748f,0.34132f},{-0.0216617f,-0.250088f,0.350182f},{-0.479345f,0.10862f,0.276891f}, +{-0.0165558f,0.416456f,-0.13696f},{-0.240613f,0.00181343f,-0.105263f},{-0.251307f,-0.00141475f,-0.104061f}, +{-0.243983f,-0.345249f,0.0791357f},{-0.37976f,-0.146928f,-0.0790264f},{-0.284489f,0.186753f,-0.0306292f}, +{-0.166853f,-0.488621f,-0.151378f},{-0.176988f,-0.48165f,-0.146484f},{-0.182602f,-0.485354f,-0.132883f}, +{-0.182563f,-0.469779f,-0.130819f},{-0.188415f,-0.472615f,-0.114112f},{-0.192512f,-0.473065f,-0.0986913f}, +{-0.196087f,-0.475657f,-0.0901064f},{-0.20235f,-0.47093f,-0.0717211f},{0.132764f,-0.0231825f,-0.122986f}, +{0.039944f,-0.48882f,-0.149474f},{0.0224976f,-0.0872319f,-0.190502f},{0.135433f,-0.056204f,-0.131603f}, +{-0.343092f,-0.463438f,0.00420567f},{-0.487267f,0.130806f,0.17258f},{-0.163702f,-0.472956f,-0.165364f}, +{-0.171027f,-0.467946f,-0.152497f},{0.0600398f,0.0716697f,-0.0695862f},{0.0408508f,-0.0974438f,-0.205595f}, +{0.300392f,-0.193556f,-0.123572f},{-0.335619f,-0.449535f,0.00314461f},{-0.173612f,0.381512f,-0.0386097f}, +{-0.186685f,0.381075f,-0.0166104f},{-0.482695f,0.130813f,0.191782f},{0.00456896f,0.0809042f,-0.037928f}, +{-0.322411f,0.376715f,-0.357763f},{-0.327215f,0.382174f,-0.349963f},{-0.33297f,0.377602f,-0.33707f}, +{-0.342153f,0.376753f,-0.319134f},{-0.351407f,0.377486f,-0.301778f},{-0.358731f,0.376528f,-0.286988f}, +{-0.278682f,-0.454268f,0.148587f},{-0.289023f,-0.458513f,0.162754f},{-0.480489f,0.126414f,0.211106f}, +{-0.478753f,0.126446f,0.223967f},{-0.314533f,0.396926f,-0.373435f},{-0.337992f,0.395717f,-0.337192f}, +{-0.372744f,0.393184f,-0.287315f},{-0.145664f,0.399228f,-0.0548149f},{-0.369689f,0.404225f,-0.30208f}, +{-0.341741f,0.412334f,-0.341507f},{-0.47602f,0.131777f,0.231665f},{-0.175091f,-0.462834f,-0.138176f}, +{-0.181431f,-0.450474f,-0.112922f},{-0.187457f,-0.452378f,-0.0945114f},{-0.198215f,-0.457599f,-0.0711102f}, +{-0.322347f,-0.449432f,-0.0207646f},{-0.342114f,-0.444552f,0.0145719f},{-0.475576f,0.130928f,0.249651f}, +{-0.388409f,0.328401f,-0.169358f},{-0.372094f,0.323552f,-0.188772f},{-0.360223f,0.323527f,-0.227272f}, +{-0.346783f,0.324787f,-0.280885f},{-0.306334f,-0.447471f,0.184972f},{-0.377014f,0.354587f,-0.231073f}, +{-0.0322337f,0.0547957f,-0.0699784f},{-0.213797f,0.357223f,0.0104113f},{-0.489724f,0.134935f,0.133899f}, +{-0.388544f,0.426584f,-0.314903f},{-0.360107f,0.341494f,-0.246744f},{-0.362088f,0.26956f,-0.22697f}, +{-0.144899f,-0.453497f,-0.168876f},{-0.161966f,-0.451529f,-0.151211f},{-0.168705f,-0.448545f,-0.137327f}, +{-0.177663f,-0.450487f,-0.128858f},{-0.191148f,-0.446744f,-0.0748786f},{-0.31416f,-0.436713f,-0.0157422f}, +{-0.320385f,-0.433047f,0.000713822f},{-0.331632f,-0.436893f,0.00825056f},{-0.398807f,0.272673f,0.0160702f}, +{-0.259255f,0.306582f,0.0174271f},{-0.16639f,0.0193627f,-0.0963892f},{-0.2655f,-0.443079f,0.133751f}, +{-0.267403f,-0.442867f,0.143391f},{-0.274734f,-0.442063f,0.153011f},{-0.281171f,-0.433812f,0.169056f}, +{-0.295209f,-0.439838f,0.175518f},{-0.299312f,-0.434674f,0.188206f},{-0.183753f,0.0188611f,-0.0958747f}, +{-0.145092f,0.0895598f,-0.0480499f},{-0.0156812f,-0.0939005f,-0.187737f},{-0.357644f,0.287502f,-0.227073f}, +{-0.388158f,0.288332f,-0.0727115f},{-0.436201f,0.137925f,-0.000482281f},{-0.443339f,0.13341f,0.00564614f}, +{-0.152873f,-0.447098f,-0.158869f},{-0.205546f,-0.434385f,-0.0600752f},{0.249809f,-0.0934632f,-0.132144f}, +{0.117897f,0.0717212f,-0.0700427f},{-0.460078f,-0.0407382f,0.096267f},{-0.299132f,-0.431536f,-0.0201794f}, +{-0.373547f,0.270056f,-0.149796f},{-0.371355f,0.26994f,-0.169081f},{-0.256085f,-0.435066f,0.130497f}, +{-0.260657f,-0.432256f,0.151358f},{-0.307132f,-0.429189f,0.226957f},{-0.410871f,0.260808f,0.0192406f}, +{-0.105749f,0.430977f,-0.0668081f},{-0.476682f,0.187891f,0.13361f},{-0.328989f,0.397312f,-0.352194f}, +{-0.146269f,-0.434886f,-0.151243f},{-0.163432f,-0.433066f,-0.131983f},{-0.169901f,-0.431382f,-0.113977f}, +{-0.172757f,-0.428777f,-0.0962863f},{-0.178994f,-0.435002f,-0.0914054f},{-0.182042f,-0.430301f,-0.075168f}, +{-0.188068f,-0.430121f,-0.0630205f},{-0.286296f,-0.426526f,-0.0209511f},{-0.295679f,-0.423536f,-0.0106942f}, +{-0.299158f,-0.419125f,0.00205783f},{-0.30964f,-0.421794f,0.00842418f},{-0.319067f,-0.420539f,0.0211762f}, +{-0.317787f,-0.415877f,0.0403717f},{-0.241513f,-0.42742f,0.130504f},{-0.246574f,-0.426957f,0.146503f}, +{-0.265088f,-0.424977f,0.165782f},{-0.278605f,-0.41899f,0.188264f},{-0.289878f,-0.422186f,0.194675f}, +{-0.296367f,-0.421485f,0.207543f},{-0.296624f,-0.416861f,0.226842f},{-0.214041f,0.35705f,0.000868158f}, +{-0.372178f,0.360188f,-0.245767f},{-0.126732f,-0.431999f,-0.168876f},{-0.137542f,-0.430205f,-0.159088f}, +{-0.150487f,-0.428944f,-0.139693f},{-0.107415f,0.0218128f,-0.0993151f},{-0.107826f,-0.0982734f,-0.142163f}, +{-0.260014f,-0.410617f,-0.0196071f},{-0.275139f,-0.418797f,-0.0182245f},{-0.280573f,-0.410868f,0.00176845f}, +{-0.301145f,-0.413131f,0.0196264f},{-0.478277f,0.24097f,0.165397f},{-0.479788f,0.243169f,0.150992f}, +{-0.23289f,-0.419176f,0.124035f},{-0.22716f,-0.414501f,0.131655f},{-0.237886f,-0.41652f,0.151256f}, +{-0.245224f,-0.411183f,0.167319f},{-0.258143f,-0.416636f,0.172162f},{-0.261242f,-0.410231f,0.188219f}, +{-0.279152f,-0.410733f,0.208804f},{-0.234761f,0.338857f,0.0169963f},{-0.109099f,-0.452146f,-0.183042f}, +{-0.12879f,-0.415852f,-0.150034f},{-0.137793f,-0.420694f,-0.146503f},{-0.160075f,-0.418713f,-0.114035f}, +{-0.16293f,-0.414913f,-0.093386f},{-0.167233f,-0.411961f,-0.0760554f},{0.137401f,0.0762162f,-0.0700106f}, +{0.193206f,0.0219222f,-0.0992637f},{0.117524f,0.0372528f,-0.107186f},{-0.242619f,-0.416353f,-0.0356644f}, +{-0.269223f,-0.407247f,-0.0048487f},{-0.283293f,-0.406013f,0.0177808f},{-0.290901f,-0.405099f,0.0402945f}, +{-0.286155f,-0.405035f,0.0532973f},{-0.477178f,0.13523f,0.211067f},{-0.359001f,0.238642f,-0.130349f}, +{-0.36293f,0.23879f,-0.11426f},{-0.227366f,-0.410913f,0.117514f},{-0.229591f,-0.410585f,0.1464f}, +{-0.251204f,-0.408251f,0.181776f},{-0.265866f,-0.406694f,0.201074f},{-0.277969f,-0.405363f,0.226771f}, +{-0.344474f,0.321842f,-0.289502f},{-0.364229f,0.372869f,-0.272808f},{-0.39909f,0.359654f,-0.208148f}, +{-0.167573f,0.0368927f,-0.0891675f},{-0.149921f,-0.409479f,-0.111752f},{-0.155606f,-0.408765f,-0.0957525f}, +{-0.171252f,-0.408855f,-0.0626539f},{-0.128803f,0.0930517f,-0.0337481f},{-0.236954f,-0.399897f,-0.031761f}, +{-0.245462f,-0.39474f,-0.0222887f},{-0.253468f,-0.396849f,-0.0136394f},{-0.260998f,-0.394527f,0.00144692f}, +{-0.266863f,-0.393839f,0.0208096f},{-0.271139f,-0.397891f,0.0370406f},{-0.266123f,-0.39137f,0.0419858f}, +{-0.218028f,-0.398206f,0.130208f},{-0.225591f,-0.397363f,0.149455f},{-0.230806f,-0.39528f,0.164496f}, +{-0.234845f,-0.397846f,0.173094f},{-0.239385f,-0.395852f,0.188129f},{-0.240941f,-0.393396f,0.2058f}, +{-0.255069f,-0.398714f,0.210662f},{-0.260741f,-0.398077f,0.226726f},{0.422581f,-0.303431f,-0.008855f}, +{-0.252979f,0.320176f,0.0204238f},{0.00404165f,0.0687116f,-0.0526414f},{0.0234815f,0.028057f,-0.100048f}, +{-0.0830745f,0.449953f,-0.0670911f},{-0.121877f,-0.402958f,-0.124716f},{-0.132301f,-0.403363f,-0.111636f}, +{-0.142712f,-0.403344f,-0.095984f},{-0.159342f,-0.401923f,-0.0683258f},{-0.166024f,-0.395119f,-0.0611942f}, +{0.0255393f,-0.0174078f,-0.13015f},{0.210421f,0.0209189f,-0.0930066f},{0.233301f,-0.11307f,-0.146889f}, +{-0.430722f,0.244603f,0.0369442f},{-0.476554f,0.228989f,0.109829f},{-0.47928f,0.226076f,0.118395f}, +{-0.480772f,0.227626f,0.133404f},{-0.236536f,-0.391434f,0.223472f},{-0.231886f,-0.391839f,0.233137f}, +{-0.374023f,0.377255f,-0.266467f},{-0.392621f,0.402617f,-0.285676f},{-0.36331f,0.354966f,-0.253329f}, +{-0.360352f,0.305559f,-0.207871f},{-0.0127617f,0.0691425f,-0.0529565f},{0.225211f,-0.100518f,-0.143616f}, +{0.22833f,-0.0351178f,-0.111739f},{0.400203f,-0.263914f,0.0221408f},{0.286573f,-0.0366419f,-0.0902029f}, +{-0.107479f,0.0613164f,-0.058474f},{-0.0914923f,-0.401138f,-0.148098f},{-0.0929006f,-0.395145f,-0.13352f}, +{-0.104617f,-0.399666f,-0.133211f},{-0.110173f,-0.396367f,-0.114093f},{-0.107247f,-0.390348f,-0.0948522f}, +{-0.127697f,-0.397434f,-0.09228f},{-0.128777f,-0.393499f,-0.074049f},{-0.146777f,-0.391113f,-0.0637793f}, +{0.0422655f,-0.00248225f,-0.127166f},{-0.0146008f,0.0365648f,-0.0938812f},{-0.234967f,-0.384721f,-0.0298961f}, +{-0.244812f,-0.378399f,-0.0178837f},{-0.255571f,-0.377197f,0.00137618f},{-0.26114f,-0.376528f,0.0207518f}, +{-0.260188f,-0.376483f,0.0402431f},{-0.477473f,0.209826f,0.107881f},{-0.34704f,0.303643f,-0.280718f}, +{-0.216215f,-0.381248f,0.131378f},{-0.222665f,-0.379615f,0.149326f},{-0.227957f,-0.378978f,0.168689f}, +{-0.229198f,-0.378753f,0.188039f},{-0.421314f,0.195351f,0.0116652f},{-0.157689f,0.393672f,-0.0470981f}, +{-0.358873f,0.35858f,-0.267483f},{-0.394415f,0.342664f,-0.187473f},{0.250214f,0.0217163f,-0.0747114f}, +{-0.0781615f,-0.389061f,-0.137552f},{-0.0940581f,-0.38829f,-0.114215f},{-0.0967204f,-0.388026f,-0.101341f}, +{-0.110759f,-0.386779f,-0.0755217f},{-0.224793f,-0.379332f,-0.0364618f},{0.309421f,-0.0351564f,-0.0752451f}, +{-0.354301f,0.212225f,-0.053233f},{-0.356635f,0.2038f,-0.0401016f},{-0.212697f,-0.371692f,0.11727f}, +{-0.0316228f,-0.0987171f,-0.188586f},{0.100437f,0.099045f,-0.0386418f},{-0.376178f,0.319128f,-0.169448f}, +{-0.362467f,0.319096f,-0.211157f},{-0.300559f,0.395724f,-0.384277f},{-0.365618f,0.363963f,-0.259812f}, +{0.286451f,0.0156522f,-0.0540175f},{-0.110263f,0.00234718f,-0.109418f},{-0.067493f,-0.378502f,-0.149243f}, +{-0.0773962f,-0.376142f,-0.133758f},{-0.0834796f,-0.375563f,-0.11426f},{-0.0887785f,-0.374985f,-0.094968f}, +{-0.0947783f,-0.374297f,-0.0758946f},{0.327723f,-0.0941063f,-0.0786534f},{-0.261332f,0.0396836f,-0.0784283f}, +{0.0601363f,0.00210928f,-0.129205f},{-0.234491f,-0.361545f,-0.0312144f},{-0.24433f,-0.360458f,-0.0179544f}, +{-0.250625f,-0.359802f,-0.00518309f},{-0.254902f,-0.359339f,0.00448862f},{-0.258381f,-0.358934f,0.0206553f}, +{-0.256709f,-0.359005f,0.0401981f},{-0.252696f,-0.359326f,0.0564162f},{-0.248773f,-0.359622f,0.0660429f}, +{-0.226395f,-0.361004f,0.187975f},{-0.43997f,0.24032f,0.0500048f},{-0.185708f,0.0393235f,-0.0903636f}, +{0.399167f,-0.275515f,0.0147327f},{-0.0604257f,-0.373454f,-0.159667f},{0.021726f,0.00198706f,-0.117398f}, +{-0.447256f,0.245632f,0.0579596f},{0.192859f,-0.110691f,-0.169872f},{-0.224626f,0.343153f,-0.00208995f}, +{-0.0538279f,0.437452f,-0.112003f},{-0.46213f,0.169589f,0.056577f},{-0.0297193f,0.454834f,-0.127893f}, +{0.174493f,0.040108f,-0.0943442f},{0.0798784f,0.0608919f,-0.0775023f},{-0.128925f,0.00378121f,-0.110685f}, +{0.306341f,-0.131018f,-0.115533f},{-0.0521945f,-0.359069f,-0.170985f},{-0.0679753f,-0.35905f,-0.153082f}, +{-0.0764767f,-0.358188f,-0.136973f},{-0.0822835f,-0.357609f,-0.127276f},{-0.085396f,-0.357255f,-0.114395f}, +{-0.0885663f,-0.356863f,-0.0950709f},{-0.0949005f,-0.355339f,-0.0771744f},{-0.244137f,0.0433684f,-0.0812385f}, +{0.00293558f,0.00318962f,-0.117385f},{-0.224826f,-0.359513f,-0.0394714f},{-0.238709f,-0.335687f,-0.0346741f}, +{-0.246516f,-0.342497f,-0.0181602f},{-0.250452f,-0.346484f,-0.00528599f},{-0.254458f,-0.340947f,0.00241795f}, +{-0.259416f,-0.341108f,0.0204624f},{-0.262072f,-0.339925f,0.0405711f},{-0.260541f,-0.339442f,0.0580367f}, +{-0.250715f,-0.346066f,0.0657085f},{-0.229764f,-0.34777f,0.0946079f},{-0.478084f,0.143294f,0.0695798f}, +{-0.0194624f,0.454918f,-0.137661f},{0.230504f,0.0393814f,-0.0744413f},{-0.0131475f,0.00237936f,-0.119089f}, +{-0.261159f,-0.336709f,0.0775474f},{-0.336951f,0.323102f,-0.320594f},{-0.482161f,0.146793f,0.15314f}, +{-0.36412f,0.292286f,-0.172464f},{-0.341118f,0.323173f,-0.304447f},{-0.154821f,0.402887f,-0.0399472f}, +{-0.480985f,0.14397f,0.17247f},{-0.373953f,0.305714f,-0.150085f},{0.0102794f,0.469335f,-0.163043f}, +{-0.047185f,-0.340516f,-0.185473f},{-0.0574934f,-0.341931f,-0.17566f},{-0.0636925f,-0.339854f,-0.168181f}, +{-0.0706248f,-0.340658f,-0.15314f},{-0.0778142f,-0.339886f,-0.136979f},{-0.0816469f,-0.339539f,-0.127417f}, +{-0.0869393f,-0.338999f,-0.114536f},{-0.0922639f,-0.33842f,-0.0952124f},{-0.0962638f,-0.33887f,-0.0771679f}, +{-0.249024f,-0.326324f,-0.0227131f},{-0.257783f,-0.31788f,-0.0144175f},{-0.261165f,-0.323372f,-0.000257208f}, +{-0.266278f,-0.326858f,0.0179351f},{-0.2693f,-0.329141f,0.0326421f},{-0.14104f,0.379949f,-0.068075f}, +{0.217597f,-0.0904601f,-0.14069f},{-0.478329f,0.144021f,0.185338f},{-0.297132f,0.16867f,-0.03742f}, +{-0.349812f,0.27331f,-0.282345f},{-0.0403556f,0.45639f,-0.116228f},{0.00156584f,0.473284f,-0.154117f}, +{-0.185734f,0.0511109f,-0.0847947f},{-0.0329411f,0.0695283f,-0.0532716f},{0.281827f,-0.0642809f,-0.104826f}, +{-0.0320729f,0.00306098f,-0.118446f},{0.208871f,-0.118466f,-0.163217f},{0.154847f,-0.090473f,-0.168831f}, +{-0.167374f,-0.073914f,-0.129115f},{-0.072599f,0.340851f,-0.0886917f},{-0.366667f,0.381724f,-0.279457f}, +{-0.230234f,0.324427f,-0.00771678f},{-0.444722f,0.260564f,0.0609241f},{-0.390074f,0.266377f,-0.0179994f}, +{-0.205019f,0.0589885f,-0.0783897f},{0.155696f,0.0757082f,-0.0711424f},{0.289036f,0.00604484f,-0.0610784f}, +{-0.05386f,-0.32471f,-0.188232f},{-0.0694608f,-0.322703f,-0.169332f},{-0.0759301f,-0.322035f,-0.153191f}, +{-0.0800071f,-0.326137f,-0.137089f},{-0.0837948f,-0.318942f,-0.132272f},{-0.0893701f,-0.3208f,-0.114633f}, +{-0.0966689f,-0.319919f,-0.0953024f},{-0.0994277f,-0.324112f,-0.0823703f},{-0.106489f,-0.321019f,-0.0733867f}, +{0.292431f,-0.0723835f,-0.100453f},{-0.149445f,-0.470924f,-0.172438f},{0.0788817f,0.000926046f,-0.129231f}, +{0.0984116f,0.0203595f,-0.112736f},{0.118257f,-0.112266f,-0.202592f},{0.20846f,-0.0947365f,-0.144658f}, +{0.17383f,-0.073406f,-0.137037f},{-0.316964f,-0.304125f,-0.00106747f},{-0.464509f,0.227427f,0.0788464f}, +{-0.42925f,0.260982f,0.0385454f},{-0.163574f,0.397498f,-0.0332915f},{-0.351902f,0.235922f,-0.147539f}, +{-0.22152f,0.0564291f,-0.0771293f},{-0.0656667f,-0.318678f,-0.179255f},{0.252535f,-0.0537153f,-0.117816f}, +{-0.0121893f,-0.0160059f,-0.130941f},{-0.127453f,-0.47147f,-0.182393f},{0.0793254f,0.0197872f,-0.113578f}, +{0.458831f,-0.267097f,0.0243722f},{-0.317376f,-0.295476f,-0.0150156f},{-0.333626f,-0.295296f,0.00389057f}, +{-0.336764f,-0.2979f,0.0185653f},{-0.257429f,0.283187f,0.00455936f},{-0.405932f,-0.241375f,0.133391f}, +{-0.354153f,0.0745571f,-0.0441465f},{-0.419096f,0.153346f,-0.00814763f},{-0.327099f,0.327359f,-0.349841f}, +{-0.322951f,0.394309f,-0.360857f},{-0.37075f,0.287991f,-0.130658f},{-0.36621f,0.267438f,-0.206064f}, +{-0.162898f,0.000514465f,-0.108871f},{-0.0462654f,-0.312871f,-0.199325f},{-0.0594933f,-0.300961f,-0.192058f}, +{-0.0672487f,-0.304775f,-0.182322f},{-0.0766374f,-0.303913f,-0.169435f},{-0.0791132f,-0.30808f,-0.153217f}, +{-0.0874795f,-0.302756f,-0.133976f},{-0.0951449f,-0.30208f,-0.114762f},{-0.0997557f,-0.306196f,-0.101907f}, +{-0.103241f,-0.300428f,-0.0942606f},{-0.11018f,-0.303219f,-0.0795022f},{0.321511f,-0.0410662f,-0.0695154f}, +{-0.0158227f,-0.485991f,-0.167647f},{0.139388f,-0.0693933f,-0.138169f},{-0.333099f,-0.281046f,-0.0147905f}, +{-0.340777f,-0.283181f,-0.00391625f},{-0.351394f,-0.27911f,0.00481658f},{-0.357329f,-0.284029f,0.0211119f}, +{-0.386068f,-0.0377737f,-0.0490209f},{-0.321331f,0.235285f,-0.148297f},{-0.374062f,-0.280004f,0.0586155f}, +{-0.372666f,-0.282261f,0.0763448f},{0.249629f,-0.0174978f,-0.0920742f},{-0.164429f,0.0917784f,-0.0503135f}, +{0.335099f,-0.00899008f,-0.0222115f},{-0.374043f,0.250539f,-0.110157f},{0.213424f,0.0407125f,-0.0809684f}, +{-0.363529f,0.337031f,-0.230636f},{0.214073f,0.0646603f,-0.0599209f},{-0.0494036f,-0.298383f,-0.201749f}, +{-0.0826758f,-0.298711f,-0.153262f},{0.288766f,-0.16813f,-0.13006f},{0.349883f,-0.104794f,-0.0615607f}, +{-0.369618f,-0.275419f,0.0223337f},{-0.372994f,-0.277071f,0.0392592f},{-0.363252f,0.274158f,-0.210945f}, +{-0.371876f,0.252358f,-0.130369f},{-0.166056f,0.0586155f,-0.0782997f},{-0.0469342f,0.0687309f,-0.0523006f}, +{-0.0710235f,-0.281856f,-0.185775f},{-0.0795184f,-0.290126f,-0.172843f},{-0.084097f,-0.283644f,-0.168522f}, +{-0.0888235f,-0.284647f,-0.153436f},{-0.0927912f,-0.284152f,-0.134047f},{-0.0977621f,-0.2852f,-0.119077f}, +{-0.102334f,-0.281837f,-0.110601f},{-0.110096f,-0.283251f,-0.095013f},{0.193167f,-0.0444487f,-0.125932f}, +{0.210254f,-0.0442237f,-0.126176f},{-0.338134f,-0.268429f,-0.02416f},{-0.353317f,-0.261464f,-0.0176714f}, +{-0.356571f,-0.267033f,-0.00273944f},{-0.370892f,-0.259053f,0.00223789f},{-0.378017f,-0.261207f,0.018829f}, +{-0.380563f,-0.266255f,0.036031f},{-0.388769f,-0.257387f,0.0409119f},{-0.393296f,-0.259284f,0.0599917f}, +{-0.224581f,0.0217292f,-0.0973795f},{-0.277493f,0.0697534f,-0.0489566f},{-0.348191f,0.38202f,-0.311495f}, +{0.230028f,-0.150413f,-0.164348f},{-0.174428f,0.365828f,-0.0464615f},{-0.00840811f,0.473888f,-0.144368f}, +{-0.127317f,0.0454583f,-0.0789106f},{-0.0519373f,-0.281245f,-0.207955f},{0.328263f,0.00381982f,-0.0205267f}, +{-0.385071f,-0.255535f,0.0264493f},{-0.403219f,-0.253587f,0.0716247f},{-0.412279f,-0.24295f,0.0788335f}, +{-0.410434f,-0.242712f,0.115668f},{-0.479229f,0.245175f,0.114061f},{-0.48065f,0.245272f,0.133314f}, +{-0.391457f,0.382155f,-0.259612f},{0.0414745f,0.078602f,-0.057355f},{0.097119f,0.032134f,-0.104755f}, +{-0.380975f,0.283476f,-0.0952445f},{-0.297859f,0.0199222f,-0.0754959f},{-0.0166522f,0.476062f,-0.133867f}, +{0.251429f,0.0748529f,-0.0162503f},{-0.0652552f,-0.273599f,-0.199029f},{-0.0789203f,-0.263027f,-0.187409f}, +{-0.0867143f,-0.266744f,-0.16957f},{-0.0926819f,-0.266152f,-0.153474f},{-0.0976142f,-0.267895f,-0.135738f}, +{-0.105775f,-0.264969f,-0.11498f},{-0.11371f,-0.266043f,-0.10136f},{0.223366f,-0.0452654f,-0.125063f}, +{-0.164191f,0.0825826f,-0.0599595f},{-0.25966f,-0.4712f,-0.0566155f},{0.152616f,-0.130073f,-0.200251f}, +{-0.0534999f,-0.0905823f,-0.151551f},{-0.358661f,-0.248108f,-0.036076f},{-0.376686f,-0.248043f,-0.0189383f}, +{-0.392094f,-0.241774f,0.000803851f},{-0.394306f,-0.243728f,0.0184432f},{-0.400145f,-0.245285f,0.03297f}, +{-0.409405f,-0.240314f,0.0406161f},{-0.412511f,-0.241253f,0.0587698f},{-0.476059f,0.249876f,0.155802f}, +{-0.381837f,0.372689f,-0.253587f},{0.0425099f,0.051426f,-0.0864795f},{-0.242034f,0.0572844f,-0.0712903f}, +{-0.0562265f,-0.265136f,-0.213819f},{-0.0698724f,-0.258506f,-0.203993f},{-0.0831645f,-0.258114f,-0.182509f}, +{-0.101318f,-0.260763f,-0.130954f},{-0.0545867f,0.00329893f,-0.113752f},{0.212292f,0.0944536f,-0.0180444f}, +{0.227436f,0.0886209f,-0.0160766f},{-0.371393f,-0.23879f,-0.0384425f},{-0.389373f,-0.238487f,-0.0192598f}, +{-0.405399f,-0.236391f,0.0231697f},{-0.261307f,-0.265336f,0.252268f},{-0.478856f,0.152291f,0.101656f}, +{-0.480149f,0.152413f,0.114511f},{-0.194711f,0.375621f,-0.00891288f},{-0.479936f,0.152529f,0.133803f}, +{-0.374621f,0.285663f,-0.112993f},{-0.382448f,0.336967f,-0.195331f},{-0.260792f,0.0700299f,-0.0518826f}, +{-0.0886049f,-0.241594f,-0.185068f},{-0.0948683f,-0.248005f,-0.166535f},{-0.0981866f,-0.252121f,-0.150317f}, +{-0.105325f,-0.246873f,-0.13424f},{-0.112263f,-0.246918f,-0.116922f},{0.0821356f,-0.110093f,-0.211627f}, +{0.289306f,-0.150799f,-0.128838f},{-0.205109f,0.00398703f,-0.106389f},{0.23579f,0.0818816f,-0.0195878f}, +{0.136121f,-0.081618f,-0.163789f},{-0.00316069f,0.0486287f,-0.0831098f},{-0.378866f,-0.22953f,-0.0397801f}, +{-0.387393f,-0.222649f,-0.0356902f},{-0.396486f,-0.225935f,-0.0234333f},{-0.404749f,-0.22207f,-0.0145719f}, +{-0.408363f,-0.222977f,0.000681669f},{-0.414556f,-0.223279f,0.0187132f},{-0.420665f,-0.226301f,0.0392914f}, +{-0.425784f,-0.221337f,0.0586155f},{-0.420003f,-0.226192f,0.0779525f},{-0.419192f,-0.225961f,0.097238f}, +{-0.417244f,-0.22625f,0.116575f},{-0.41498f,-0.224546f,0.135044f},{0.000530508f,0.43643f,-0.153616f}, +{0.247841f,-0.0313559f,-0.101321f},{-0.0731263f,-0.242809f,-0.209009f},{-0.101801f,-0.242712f,-0.150336f}, +{-0.0732871f,0.0348092f,-0.0873284f},{0.220356f,-0.122761f,-0.158651f},{0.1365f,0.0392913f,-0.106254f}, +{0.155336f,0.0330022f,-0.105945f},{0.100579f,-0.0536767f,-0.136889f},{0.115652f,-0.0799138f,-0.168798f}, +{-0.423469f,-0.217395f,0.0263722f},{-0.427533f,-0.216951f,0.0392463f},{-0.425494f,-0.216983f,0.0779011f}, +{-0.424337f,-0.21688f,0.0971866f},{-0.425662f,-0.216598f,0.116234f},{-0.423797f,-0.216681f,0.12851f}, +{-0.423861f,0.225183f,0.0220893f},{0.100174f,-0.066583f,-0.146233f},{0.117781f,0.100608f,-0.0368284f}, +{-0.0833832f,-0.235883f,-0.199106f},{-0.0978393f,-0.231247f,-0.170998f},{-0.104392f,-0.227542f,-0.162303f}, +{-0.107845f,-0.228681f,-0.150445f},{-0.111389f,-0.228263f,-0.134343f},{-0.116778f,-0.227954f,-0.12181f}, +{0.249667f,-0.128967f,-0.145706f},{0.232472f,0.0238128f,-0.0833221f},{-0.392711f,-0.207087f,-0.0382303f}, +{-0.402485f,-0.210778f,-0.0256197f},{-0.409521f,-0.204836f,-0.0176264f},{-0.417019f,-0.205781f,-0.000688062f}, +{-0.421044f,-0.208868f,0.0135237f},{-0.426826f,-0.203151f,0.0212534f},{-0.433874f,-0.203408f,0.0392656f}, +{-0.436709f,-0.205222f,0.0569371f},{-0.434806f,-0.20726f,0.077856f},{-0.434021f,-0.208842f,0.115675f}, +{-0.426948f,-0.203672f,0.15186f},{-0.419392f,-0.211967f,0.157686f},{-0.347349f,0.218392f,-0.0859072f}, +{-0.14086f,0.412385f,-0.0475226f},{0.154384f,0.0215427f,-0.112858f},{-0.243057f,0.0221793f,-0.0944857f}, +{-0.0784766f,-0.222494f,-0.212701f},{-0.0877432f,-0.221594f,-0.20236f},{-0.0960066f,-0.220906f,-0.18609f}, +{-0.184673f,0.00709305f,-0.102762f},{0.00357221f,0.0290087f,-0.101013f},{-0.358982f,-0.204495f,-0.0763319f}, +{-0.374499f,-0.206366f,-0.0561397f},{-0.107196f,-0.404501f,-0.146388f},{-0.421803f,-0.200129f,0.00706088f}, +{-0.441404f,-0.198405f,0.0617279f},{-0.441841f,-0.198167f,0.0778239f},{-0.442954f,-0.198026f,0.097103f}, +{-0.442626f,-0.197916f,0.116363f},{-0.424305f,-0.199215f,0.171094f},{-0.42233f,-0.199756f,0.181563f}, +{-0.033552f,-0.110974f,-0.208964f},{-0.32481f,0.410244f,-0.363647f},{-0.0341886f,-0.0863831f,-0.153114f}, +{0.215347f,0.00317677f,-0.0975853f},{-0.10544f,-0.206263f,-0.166702f},{-0.111595f,-0.210225f,-0.150535f}, +{-0.114463f,-0.209916f,-0.134433f},{-0.116205f,-0.214244f,-0.121559f},{-0.0201055f,0.0293688f,-0.101296f}, +{0.205476f,0.0341211f,-0.0879907f},{0.173072f,0.0181537f,-0.109257f},{-0.361824f,-0.192013f,-0.0803061f}, +{-0.369959f,-0.185345f,-0.0759332f},{-0.376383f,-0.192547f,-0.0622745f},{0.0600784f,0.0621138f,-0.0787177f}, +{-0.397058f,-0.190901f,-0.0428603f},{-0.40473f,-0.187197f,-0.0339732f},{-0.413649f,-0.188984f,-0.0203209f}, +{-0.420009f,-0.191859f,-0.00282304f},{-0.425642f,-0.184708f,0.00212857f},{-0.429642f,-0.186438f,0.0198643f}, +{-0.437494f,-0.185814f,0.0391563f},{-0.44478f,-0.185177f,0.0584419f},{-0.447198f,-0.184791f,0.0777853f}, +{-0.448767f,-0.184624f,0.0970323f},{-0.447564f,-0.184586f,0.116279f},{-0.443822f,-0.183865f,0.134227f}, +{-0.439828f,-0.189216f,0.141912f},{-0.434227f,-0.185383f,0.154863f},{-0.432086f,-0.185505f,0.170991f}, +{-0.372679f,0.204006f,-0.0321276f},{-0.380731f,0.208058f,-0.0269509f},{-0.385669f,0.292717f,-0.108125f}, +{-0.384955f,0.275155f,-0.0501334f},{-0.457513f,0.0372528f,0.0313302f},{-0.0781293f,-0.209074f,-0.214385f}, +{-0.0885985f,-0.202334f,-0.204315f},{-0.0979165f,-0.200945f,-0.190463f},{-0.100585f,-0.203717f,-0.181891f}, +{-0.290669f,0.41126f,-0.387428f},{0.246542f,-0.0402495f,-0.109823f},{-0.388872f,-0.187074f,-0.0538118f}, +{-0.421507f,-0.183107f,-0.0125269f},{-0.376924f,0.270249f,-0.111244f},{0.135272f,0.0239992f,-0.115713f}, +{-0.221777f,0.0372528f,-0.0892061f},{0.458972f,-0.281965f,0.0151571f},{-0.0687985f,-0.207048f,-0.221607f}, +{-0.11216f,-0.187557f,-0.147654f},{-0.125125f,-0.188746f,-0.129906f},{0.136224f,-0.0950966f,-0.185917f}, +{-0.159561f,0.0490338f,-0.0826726f},{-0.377965f,-0.167352f,-0.077065f},{-0.39329f,-0.17031f,-0.0587762f}, +{-0.408022f,-0.171068f,-0.038436f},{-0.418806f,-0.170213f,-0.0255233f},{-0.427141f,-0.169647f,-0.0158773f}, +{-0.435854f,-0.168728f,0.000495179f},{-0.437925f,-0.172734f,0.0198257f},{-0.447076f,-0.167744f,0.0390856f}, +{-0.448728f,-0.167544f,0.0583583f},{-0.449712f,-0.167326f,0.0776695f},{-0.450297f,-0.167146f,0.0969229f}, +{-0.449378f,-0.167216f,0.116215f},{-0.447352f,-0.167255f,0.135494f},{-0.444323f,-0.165081f,0.153101f}, +{-0.437745f,-0.172175f,0.158059f},{-0.437012f,-0.167917f,0.170966f},{-0.128308f,0.265824f,-0.0182373f}, +{-0.376872f,0.270306f,-0.0919713f},{-0.345265f,0.336864f,-0.291669f},{-0.0700781f,-0.189023f,-0.223221f}, +{-0.080573f,-0.185692f,-0.212334f},{-0.0877818f,-0.185081f,-0.202456f},{-0.096013f,-0.184386f,-0.189428f}, +{-0.10144f,-0.183943f,-0.179705f},{-0.104682f,-0.183647f,-0.166747f},{-0.146892f,0.0471175f,-0.0806276f}, +{-0.107704f,0.0503778f,-0.0712196f},{-0.0904633f,0.050918f,-0.071869f},{-0.445179f,-0.163641f,0.0197807f}, +{-0.441044f,0.20481f,0.0434584f},{-0.463165f,0.257715f,0.0824154f},{-0.436195f,0.124215f,-0.00681005f}, +{-0.463442f,0.258603f,0.168477f},{-0.0122279f,0.490132f,-0.127771f},{0.192447f,-0.0923057f,-0.150741f}, +{-0.3861f,-0.159468f,-0.0706472f},{-0.397045f,-0.146979f,-0.0587698f},{-0.403296f,-0.153828f,-0.0512009f}, +{-0.41109f,-0.150014f,-0.0392527f},{-0.42851f,-0.152034f,-0.0189962f},{-0.438639f,-0.152702f,-0.00389053f}, +{-0.442375f,-0.149513f,0.00473942f},{-0.449436f,-0.150426f,0.0197421f},{-0.456413f,-0.15069f,0.0377351f}, +{-0.456812f,-0.153821f,0.0583325f},{-0.458169f,-0.153641f,0.0776052f},{-0.456612f,-0.153764f,0.0968458f}, +{-0.455628f,-0.153757f,0.116144f},{-0.455841f,-0.151474f,0.137005f},{-0.4526f,-0.149384f,0.154612f}, +{-0.457108f,0.24904f,0.0697727f},{-0.435938f,0.174599f,0.0144369f},{-0.0689142f,-0.16804f,-0.222166f}, +{-0.0786438f,-0.167577f,-0.212385f},{-0.0840327f,-0.167126f,-0.202559f},{-0.0928748f,-0.166419f,-0.186303f}, +{-0.102688f,-0.166516f,-0.165609f},{-0.111601f,-0.165641f,-0.149005f},{-0.387663f,-0.146169f,-0.0707115f}, +{-0.460053f,-0.145236f,0.0454391f},{-0.460496f,-0.145031f,0.0583261f},{-0.462857f,-0.144896f,0.0775538f}, +{-0.462085f,-0.144812f,0.0968072f},{-0.461165f,-0.144761f,0.116106f},{-0.460696f,-0.144613f,0.132137f}, +{0.20037f,0.0470274f,-0.0826468f},{-0.377708f,0.301283f,-0.134021f},{-0.0979422f,-0.161397f,-0.173499f}, +{-0.119028f,-0.15995f,-0.142497f},{0.0999871f,0.079528f,-0.0589499f},{-0.413495f,-0.129668f,-0.0401402f}, +{-0.432227f,-0.131147f,-0.0199608f},{-0.438915f,-0.133674f,-0.00619914f},{-0.441867f,-0.13098f,0.00160125f}, +{-0.448889f,-0.133185f,0.0197421f},{-0.45678f,-0.132594f,0.0357674f},{-0.46004f,-0.132388f,0.0454262f}, +{-0.462721f,-0.132111f,0.0582489f},{-0.465982f,-0.131777f,0.0775023f},{-0.466663f,-0.131616f,0.0967493f}, +{-0.466638f,-0.131533f,0.116028f},{-0.464985f,-0.131501f,0.135288f},{-0.457262f,-0.136041f,0.157654f}, +{-0.299678f,-0.473072f,-0.0419022f},{-0.066014f,-0.152439f,-0.219549f},{-0.0736086f,-0.147384f,-0.208488f}, +{-0.0819427f,-0.153545f,-0.199479f},{-0.0862963f,-0.14867f,-0.186367f},{-0.0939488f,-0.148085f,-0.166966f}, +{-0.109556f,-0.148574f,-0.150355f},{-0.221694f,-0.48738f,-0.0727243f},{-0.388229f,-0.128594f,-0.0705443f}, +{-0.396473f,-0.128941f,-0.0591492f},{-0.423617f,-0.126382f,-0.0322497f},{-0.282952f,0.025549f,-0.0794444f}, +{-0.439114f,0.213549f,0.0500113f},{0.213347f,0.0806791f,-0.0397029f},{-0.402633f,-0.123334f,-0.0517282f}, +{0.279788f,-0.0226423f,-0.084949f},{0.00770069f,-0.0975017f,-0.202469f},{-0.463043f,0.028475f,0.0439793f}, +{-0.438684f,0.159937f,0.012154f},{-0.0821935f,-0.135037f,-0.183261f},{0.112443f,0.0452011f,-0.0996109f}, +{-0.440008f,0.0363911f,0.0118517f},{0.0788109f,-0.0764541f,-0.171068f},{-0.0513071f,0.0541204f,-0.0704157f}, +{-0.0725218f,0.0608791f,-0.0584676f},{0.138854f,-0.0875727f,-0.173519f},{0.262078f,-0.0821195f,-0.124337f}, +{-0.0899425f,0.0356066f,-0.0881129f},{0.26658f,-0.0933218f,-0.125514f},{0.276104f,-0.0914312f,-0.121025f}, +{0.401579f,-0.301399f,-0.00446929f},{0.289235f,-0.0925565f,-0.113353f},{-0.0525868f,0.045259f,-0.0791357f}, +{0.0232886f,-0.079618f,-0.172843f},{0.0404006f,-0.114704f,-0.220147f},{-0.127575f,0.0222308f,-0.0996624f}, +{-0.091055f,-0.415993f,-0.167731f},{0.461493f,-0.258995f,0.0334973f},{0.0801485f,0.0388862f,-0.0953474f}, +{0.0859811f,0.0112665f,-0.121064f},{0.211128f,-0.0580817f,-0.13316f},{-0.0155011f,0.0200122f,-0.109476f}, +{0.13569f,0.0984984f,-0.0425517f},{0.209334f,0.0733224f,-0.0531558f},{0.37383f,-0.297662f,-0.0146426f}, +{0.174473f,-0.0865953f,-0.153281f},{-0.0290248f,-0.00725378f,-0.126877f},{0.264291f,0.0159287f,-0.0684544f}, +{0.30427f,-0.0999389f,-0.105508f},{0.312405f,-0.103913f,-0.101238f},{0.325838f,-0.111083f,-0.093624f}, +{-0.0629594f,-0.448423f,-0.178367f},{0.153825f,0.00549181f,-0.117688f},{0.0600527f,0.0406289f,-0.0930709f}, +{0.193785f,-0.0587441f,-0.134587f},{0.232806f,0.00174273f,-0.0917398f},{0.176403f,0.0629755f,-0.0804797f}, +{0.440729f,-0.274448f,0.0136845f},{0.185399f,0.0156072f,-0.10664f},{0.32742f,-0.0140767f,-0.0403781f}, +{0.119909f,-0.0675284f,-0.140188f},{-0.120462f,-0.417369f,-0.160014f},{-0.184846f,0.0625961f,-0.0775216f}, +{0.334127f,-0.108717f,-0.0832449f},{0.339111f,-0.118543f,-0.0857078f},{0.348841f,-0.12961f,-0.0804411f}, +{-0.278059f,0.0395357f,-0.0715282f},{0.212832f,0.054352f,-0.0712839f},{0.303788f,0.0003923f,-0.0545191f}, +{-0.0927848f,-0.471593f,-0.185357f},{0.438767f,-0.321842f,-0.0139288f},{0.0999292f,0.0890518f,-0.0499019f}, +{-0.279441f,-0.470139f,-0.048963f},{0.0730555f,0.0125012f,-0.122363f},{0.00391304f,0.0395164f,-0.0928394f}, +{-0.260947f,0.0601653f,-0.0610141f},{-0.205894f,-0.449568f,-0.0642744f},{0.19574f,0.0603518f,-0.0749622f}, +{0.119163f,-0.094087f,-0.189229f},{-0.0131282f,-0.0585062f,-0.143738f},{0.250722f,-0.165963f,-0.157017f}, +{-0.260921f,0.0503457f,-0.0701842f},{0.268876f,-0.184875f,-0.148606f},{-0.105344f,0.0338253f,-0.0893669f}, +{0.455017f,-0.300942f,0.00419281f},{0.156976f,-0.110363f,-0.188618f},{-0.074033f,0.0204431f,-0.097939f}, +{-0.10908f,-0.418257f,-0.165628f},{0.273441f,-0.298261f,-0.137044f},{-0.109234f,-0.428674f,-0.175023f}, +{0.406022f,-0.243195f,0.03742f},{0.00558501f,-0.131558f,-0.23043f},{0.025385f,-0.152947f,-0.238134f}, +{0.41743f,-0.248706f,0.0421723f},{0.0629336f,0.012572f,-0.12242f},{0.0616732f,0.0293945f,-0.101669f}, +{0.0579434f,0.0198579f,-0.113565f},{0.269679f,0.00122184f,-0.0735732f},{0.0437703f,-0.146934f,-0.230874f}, +{-0.242703f,0.0852513f,-0.042333f},{-0.186017f,-0.00108035f,-0.107206f},{-0.0730748f,0.0450918f,-0.0788142f}, +{0.264979f,-0.303926f,-0.144748f},{-0.0814283f,0.433549f,-0.0899842f},{0.420144f,-0.274981f,0.0141282f}, +{0.00217032f,-0.46875f,-0.16359f},{0.0806758f,-0.0558438f,-0.1386f},{0.230292f,-0.165487f,-0.167686f}, +{-0.0914344f,-0.485078f,-0.183917f},{0.079737f,0.0510144f,-0.0862352f},{0.059519f,0.0513617f,-0.086531f}, +{-0.0636282f,0.435066f,-0.105038f},{-0.0721102f,0.311488f,-0.0673805f},{0.0599562f,-0.0869232f,-0.19056f}, +{-0.0704897f,-0.490711f,-0.165313f},{0.1921f,0.0920035f,-0.034687f},{0.17421f,-0.0966528f,-0.167056f}, +{0.321433f,0.0167261f,-0.0155172f},{0.0605029f,-0.0743577f,-0.167403f},{0.0802386f,0.0808591f,-0.0602617f}, +{0.0422526f,0.0701264f,-0.0677534f},{-0.127317f,0.0551558f,-0.0697598f},{0.156667f,-0.0688402f,-0.134427f}, +{0.287216f,-0.00226358f,-0.0682293f},{0.137787f,0.0655992f,-0.0798302f},{0.0426642f,-0.0758882f,-0.168644f}, +{0.0249027f,0.0505129f,-0.0853477f},{-0.0142793f,0.0467831f,-0.0810392f},{-0.112752f,0.40072f,-0.0819266f}, +{-0.0764188f,0.420405f,-0.10026f},{-0.148532f,0.021099f,-0.0983505f},{-0.0322337f,0.0449889f,-0.0789685f}, +{0.0228963f,0.47257f,-0.16847f},{0.344397f,-0.112003f,-0.0732838f},{0.334835f,-0.0549564f,-0.0632262f}, +{0.00402235f,-0.0809749f,-0.165081f},{0.0234044f,-0.0553422f,-0.143532f},{0.193618f,0.0389377f,-0.0904022f}, +{-0.0916209f,-0.427208f,-0.176412f},{-0.292843f,0.400572f,-0.387647f},{-0.277351f,-0.432546f,-0.0328735f}, +{0.0236101f,-0.130761f,-0.229671f},{0.0407672f,0.0189962f,-0.109791f},{0.232697f,-0.13069f,-0.155558f}, +{-0.0537443f,0.0202373f,-0.097791f},{-0.255513f,-0.419279f,-0.0314331f},{0.17466f,0.0740234f,-0.0699334f}, +{-0.270593f,0.296801f,0.0237677f},{-0.176981f,0.35514f,-0.0475869f},{-0.0332369f,0.0160252f,-0.107681f}, +{0.341156f,-0.0549757f,-0.0554966f},{0.171097f,0.0880808f,-0.0495739f},{0.322231f,-0.020308f,-0.0531044f}, +{-0.0737501f,-0.486299f,-0.181094f},{-0.0327803f,-0.0931031f,-0.173191f},{0.258316f,0.0655092f,-0.025459f}, +{0.244568f,-0.00190989f,-0.0878557f},{0.0420533f,-0.0569692f,-0.145031f},{-0.033552f,-0.0750973f,-0.145648f}, +{-0.299814f,0.281284f,0.0215106f},{-0.279737f,0.283412f,0.0196843f},{-0.209373f,0.327385f,-0.0267129f}, +{0.196408f,0.0842545f,-0.0456062f},{-0.0161635f,-0.0777531f,-0.149191f},{-0.2435f,-0.47057f,-0.0622038f}, +{0.00624094f,-0.0729365f,-0.149339f},{0.0225748f,-0.0716054f,-0.151667f},{0.151915f,-0.0584418f,-0.128607f}, +{0.302399f,-0.0201666f,-0.0695926f},{0.235443f,-0.146613f,-0.15968f},{0.202723f,0.00639853f,-0.103077f}, +{-0.0153082f,-0.470872f,-0.168438f},{0.022652f,0.0167455f,-0.107334f},{-0.219732f,0.0707759f,-0.0663708f}, +{0.0407607f,-0.0696055f,-0.151082f},{0.0616411f,-0.0680557f,-0.152548f},{-0.279319f,-0.487778f,-0.0510337f}, +{-0.293659f,-0.489888f,-0.0501784f},{0.00330855f,0.0187904f,-0.109469f},{0.0112504f,0.0501527f,-0.0848847f}, +{-0.30555f,0.276789f,0.00826342f},{-0.296746f,0.274049f,0.0108936f},{-0.283763f,0.27257f,0.0122118f}, +{-0.280612f,0.265593f,0.00686153f},{0.26386f,-0.137719f,-0.142786f},{0.269647f,-0.0361596f,-0.0978554f}, +{0.260156f,-0.0403396f,-0.104575f},{-0.203038f,0.0361982f,-0.0896177f},{0.417604f,-0.298164f,-0.00448858f}, +{-0.205939f,-0.073959f,-0.131282f},{-0.0925919f,-0.436269f,-0.180843f},{-0.0775634f,-0.436153f,-0.17975f}, +{0.116546f,0.024758f,-0.115083f},{0.20718f,-0.00464937f,-0.10401f},{0.285299f,-0.0771229f,-0.109115f}, +{-0.0350568f,0.0268866f,-0.0985177f},{-0.0529211f,-0.469638f,-0.17683f},{0.115234f,-0.073078f,-0.15377f}, +{0.192241f,-0.0755088f,-0.13833f},{0.0680782f,-0.0558631f,-0.140079f},{0.134211f,-0.0758111f,-0.150696f}, +{-0.129465f,-0.447863f,-0.175904f},{-0.110122f,-0.437568f,-0.179467f},{-0.146391f,-0.399132f,-0.0756053f}, +{-0.090174f,0.0221536f,-0.0997267f},{0.296984f,-0.0316839f,-0.0818816f},{0.0440661f,0.0283978f,-0.100518f}, +{0.153336f,-0.0782933f,-0.147989f},{0.173522f,0.02861f,-0.10127f},{-0.245719f,0.0662872f,-0.0612649f}, +{-0.279467f,0.248005f,-0.0012411f},{-0.260773f,0.24596f,-0.000218624f},{0.321485f,-0.129147f,-0.106768f}, +{0.023713f,-0.114099f,-0.220867f},{0.24413f,-0.0576059f,-0.124697f},{-0.0915759f,-0.406855f,-0.157082f}, +{-0.296058f,-0.270261f,0.234983f},{0.00615734f,0.0945629f,-0.0194463f},{0.168081f,-0.0794766f,-0.144581f}, +{0.252497f,0.00464294f,-0.0807627f},{0.24822f,0.0340825f,-0.0687502f},{0.313916f,0.00494521f,-0.0416064f}, +{0.0403749f,-0.469702f,-0.154934f},{-0.293164f,0.254763f,-0.00702226f},{0.0280087f,-0.0414585f,-0.142111f}, +{0.0618211f,0.0931031f,-0.0507122f},{-0.17102f,0.00729881f,-0.103051f},{-0.25896f,0.0220122f,-0.0907302f}, +{-0.0683676f,-0.430282f,-0.176702f},{0.143471f,0.0490145f,-0.0974759f},{0.154763f,0.0894312f,-0.0509244f}, +{-0.245468f,-0.429954f,-0.0434134f},{-0.0755121f,-0.0124948f,-0.121404f},{-0.261712f,-0.45576f,-0.0506865f}, +{0.188447f,0.0503714f,-0.0862159f},{-0.0160284f,0.0915083f,-0.0186553f},{-0.288245f,0.242037f,-0.0126362f}, +{0.439166f,-0.300878f,-0.00482298f},{-0.0333334f,0.0365197f,-0.0880615f},{0.317967f,-0.0967814f,-0.0894826f}, +{0.0200926f,0.0669368f,-0.0639915f},{-0.281396f,-0.450725f,-0.0420436f},{0.161168f,-0.0859651f,-0.159204f}, +{-0.013334f,-0.0866789f,-0.168876f},{-0.260336f,-0.435716f,-0.0411498f},{-0.299878f,-0.48938f,-0.0368348f}, +{0.292592f,-0.109083f,-0.117334f},{-0.275538f,0.230571f,-0.006212f},{0.331504f,-0.140124f,-0.101926f}, +{0.305942f,-0.112993f,-0.110987f},{-0.242639f,-0.437381f,-0.049381f},{0.317446f,-0.113977f,-0.103482f}, +{-0.0706248f,-0.414482f,-0.167506f},{0.0406385f,0.0091251f,-0.118633f},{0.252342f,-0.320954f,-0.156136f}, +{0.331214f,-0.127102f,-0.098209f},{-0.286836f,0.4f,-0.387441f},{-0.00711554f,-0.0828655f,-0.163146f}, +{0.323568f,-0.148651f,-0.108871f},{-0.205218f,0.0724285f,-0.0683129f},{-0.223134f,-0.454969f,-0.0629433f}, +{0.38266f,-0.335706f,-0.0232275f},{-0.27431f,0.21917f,-0.00699654f},{-0.260381f,0.211685f,-0.00508663f}, +{0.390312f,-0.260422f,0.0117103f},{0.337832f,-0.0386418f,-0.05059f},{0.117459f,0.0530465f,-0.0919584f}, +{0.0412752f,-0.0419022f,-0.14186f},{-0.227938f,0.0485451f,-0.0817144f},{0.0407221f,-0.1673f,-0.236796f}, +{-0.194158f,-0.250577f,0.273869f},{-0.184525f,0.0924215f,-0.0508601f},{0.0057715f,-0.0839266f,-0.174837f}, +{0.0450371f,0.0886145f,-0.0488473f},{0.0313655f,0.0688788f,-0.0662808f},{0.160294f,0.06794f,-0.079335f}, +{-0.299466f,-0.441446f,-0.0306742f},{0.11807f,-0.0567377f,-0.135372f},{0.176113f,-0.472351f,-0.135089f}, +{-0.126842f,0.0658114f,-0.0616507f},{0.1534f,-0.100363f,-0.182406f},{0.245191f,-0.114536f,-0.141526f}, +{-0.202826f,-0.490389f,-0.073779f},{-0.267294f,0.205112f,-0.012932f},{-0.240606f,0.205492f,0.00168485f}, +{0.327459f,-0.0332272f,-0.058564f},{0.235661f,0.0680685f,-0.041034f},{0.0310183f,-0.140105f,-0.232719f}, +{0.046066f,-0.16876f,-0.235111f},{-0.205585f,0.0910453f,-0.0491688f},{0.0606829f,-0.108337f,-0.213601f}, +{0.0407607f,-0.107411f,-0.2147f},{0.0204913f,-0.107128f,-0.215125f},{0.00281983f,-0.105373f,-0.210572f}, +{0.00645958f,-0.114749f,-0.220302f},{-0.147851f,-0.488614f,-0.17031f},{-0.123697f,-0.451297f,-0.179313f}, +{0.204485f,0.089592f,-0.0321211f},{0.11699f,0.0893798f,-0.0503521f},{0.134745f,0.0903315f,-0.0517154f}, +{0.440002f,-0.250082f,0.0402109f},{0.459384f,-0.251735f,0.0420758f},{-0.259615f,0.192746f,-0.0167261f}, +{-0.241024f,0.190868f,-0.00066877f},{-0.0119771f,-0.0980033f,-0.198862f},{0.0374296f,-0.153371f,-0.236552f}, +{0.276483f,-0.150227f,-0.137668f},{-0.111543f,-0.408559f,-0.15114f},{0.289222f,-0.204649f,-0.134684f}, +{0.271126f,-0.203061f,-0.148297f},{0.419784f,-0.320067f,-0.0175557f},{0.466393f,-0.311077f,0.0125913f}, +{0.103788f,-0.127289f,-0.213157f},{0.0621813f,-0.0796309f,-0.178985f},{0.213032f,-0.148741f,-0.175615f}, +{-0.276123f,0.19364f,-0.0243014f},{-0.237057f,0.175345f,-0.000983873f},{-0.051217f,-0.0594772f,-0.143346f}, +{-0.0341115f,-0.0593293f,-0.144195f},{-0.195013f,0.140182f,0.0354008f},{-0.0339764f,-0.483142f,-0.170406f}, +{0.267313f,-0.151204f,-0.143571f},{0.382132f,-0.321662f,-0.0154464f},{0.40372f,-0.320575f,-0.0174657f}, +{0.157876f,-0.12471f,-0.195923f},{-0.0725218f,-0.447722f,-0.179679f},{-0.0453651f,-0.133153f,-0.221298f}, +{-0.0336806f,-0.13051f,-0.223787f},{0.205791f,-0.147436f,-0.179486f},{-0.243204f,-0.489933f,-0.0578631f}, +{0.235147f,-0.0296067f,-0.103167f},{-0.247056f,0.172014f,-0.0121411f},{-0.221713f,0.154066f,-2.57039e-05f}, +{-0.201141f,0.135912f,0.0175429f},{0.288097f,-0.18638f,-0.132729f},{-0.241725f,-0.454281f,-0.0580045f}, +{-0.223064f,-0.473014f,-0.0674833f},{-0.0658854f,-0.465335f,-0.178657f},{0.252683f,-0.304164f,-0.154522f}, +{0.100559f,-0.0970258f,-0.199235f},{0.172474f,-0.114562f,-0.183679f},{0.0369409f,0.0861901f,-0.0460435f}, +{0.362075f,-0.339404f,-0.0242629f},{-0.0222147f,-0.0861901f,-0.159821f},{-0.233565f,0.161043f,-0.00777465f}, +{-0.213115f,0.148137f,0.00656572f},{0.0732227f,-0.117109f,-0.217337f},{0.18628f,-0.115874f,-0.178978f}, +{-0.123922f,-0.486454f,-0.18665f},{0.398839f,-0.335353f,-0.0244236f},{0.12106f,-0.0842031f,-0.173937f}, +{-0.296405f,0.0342433f,-0.0656313f},{-0.256805f,0.155757f,-0.0229317f},{-0.240356f,0.151571f,-0.0168869f}, +{-0.228626f,0.151365f,-0.0101476f},{-0.215733f,0.136684f,-0.00267514f},{-0.207392f,0.130748f,0.00387771f}, +{0.278091f,-0.176985f,-0.140973f},{0.00451752f,0.111064f,0.0211183f},{0.0224784f,0.115508f,0.018546f}, +{-0.279962f,-0.294164f,0.208308f},{0.317472f,0.0347706f,0.00812194f},{-0.226047f,0.135121f,-0.0132664f}, +{-0.202601f,0.121314f,7.07559e-05f},{-0.184576f,0.116607f,-0.000681631f},{-0.0169223f,0.103913f,0.0169963f}, +{-0.0125623f,0.107128f,0.0231697f},{0.040735f,0.11462f,0.00070096f},{0.060207f,0.119263f,0.00183276f}, +{0.0800906f,0.121507f,0.000392289f},{0.0970676f,0.121469f,-0.00239862f},{0.116057f,0.122093f,-0.00194847f}, +{0.136745f,0.122343f,-0.00261726f},{0.15668f,0.121314f,-0.00171054f},{0.175811f,0.118646f,0.000971048f}, +{0.194151f,0.112221f,-0.000225055f},{0.134372f,0.00556251f,-0.120093f},{-0.115472f,0.0872769f,-0.0327256f}, +{-0.114977f,0.0688852f,-0.0518761f},{-0.0382078f,-0.103366f,-0.193608f},{0.271293f,-0.0128099f,-0.0826275f}, +{-0.252992f,0.138716f,-0.0265072f},{-0.242188f,0.134099f,-0.0231053f},{-0.215167f,0.124703f,-0.00695796f}, +{0.00827945f,0.108106f,0.00857852f},{0.022832f,0.109945f,0.00102249f},{0.212755f,0.107791f,0.00616702f}, +{0.230613f,0.0964599f,0.00186491f},{0.22543f,-0.132883f,-0.161088f},{0.299415f,0.0143018f,-0.0474454f}, +{0.356455f,-0.0437028f,-0.0169641f},{-0.0465548f,0.0114209f,-0.107849f},{0.0974341f,0.052217f,-0.0876628f}, +{-0.221945f,0.114665f,-0.0193305f},{-0.202511f,0.112221f,-0.0181023f},{-0.186158f,0.112742f,-0.0161152f}, +{-0.239404f,-0.484917f,-0.0675219f},{-0.029102f,-0.117887f,-0.217536f},{0.406305f,-0.255284f,0.0327835f}, +{0.419893f,-0.255754f,0.033208f},{-0.0154754f,0.0985306f,-0.000263639f},{0.00136006f,0.101585f,-0.00249508f}, +{0.043005f,0.109611f,-0.0146876f},{0.0620076f,0.11327f,-0.016456f},{0.0792418f,0.113746f,-0.0197807f}, +{0.0978779f,0.113392f,-0.0183852f},{0.117909f,0.112839f,-0.0178644f},{0.137471f,0.114331f,-0.018501f}, +{0.155722f,0.113064f,-0.0185975f},{0.174686f,0.110357f,-0.0158837f},{0.21527f,0.101457f,-0.0032539f}, +{0.462657f,-0.297823f,0.0121604f},{0.440478f,-0.255458f,0.0329186f},{0.240201f,-0.104659f,-0.139391f}, +{-0.169046f,0.108524f,-0.0214012f},{-0.149214f,0.106916f,-0.0149063f},{0.0234172f,0.101907f,-0.01753f}, +{0.184396f,0.106157f,-0.0178579f},{0.193778f,0.101367f,-0.0190926f},{-0.133767f,-0.465335f,-0.178547f}, +{0.186409f,-0.0962027f,-0.161062f},{-0.261165f,-0.490434f,-0.0584804f},{-0.070599f,-0.397383f,-0.151481f}, +{-0.247622f,0.108164f,-0.0284299f},{-0.228555f,0.105804f,-0.0256197f},{-0.186595f,0.107688f,-0.0281663f}, +{-0.160519f,0.105386f,-0.025922f},{-0.147664f,0.103579f,-0.0243207f},{-0.0743545f,0.0868204f,-0.00658498f}, +{-0.0456031f,0.0891804f,-0.00969099f},{0.0395839f,0.103418f,-0.0260313f},{0.0621041f,0.107341f,-0.0307257f}, +{0.0794476f,0.107984f,-0.0316067f},{0.0958008f,0.106614f,-0.030282f},{0.126276f,0.106267f,-0.0302498f}, +{0.136899f,0.107109f,-0.0312851f},{0.150571f,0.106961f,-0.0313816f},{0.178949f,0.10244f,-0.0266872f}, +{0.150924f,-0.486299f,-0.125578f},{0.134089f,-0.486807f,-0.128459f},{0.118392f,-0.486319f,-0.133185f}, +{0.0979808f,-0.483766f,-0.142265f},{0.0787209f,-0.484049f,-0.14442f},{0.059982f,-0.483933f,-0.146986f}, +{0.0217517f,-0.486319f,-0.150915f},{0.00719267f,-0.489219f,-0.149513f},{-0.0549082f,-0.482345f,-0.175068f}, +{0.199855f,-0.476094f,-0.120543f},{0.157419f,-0.473052f,-0.139314f},{0.136558f,-0.470429f,-0.144278f}, +{0.117253f,-0.470191f,-0.1475f},{0.0979615f,-0.470023f,-0.149558f},{0.0786759f,-0.469998f,-0.151043f}, +{-0.22442f,-0.0713611f,-0.131063f},{0.0219896f,-0.470287f,-0.158329f},{-0.0307547f,-0.468165f,-0.172586f}, +{-0.324398f,-0.270171f,0.217665f},{0.0275329f,0.382361f,-0.144728f},{-0.0119193f,0.384026f,-0.123764f}, +{-0.243462f,-0.0706344f,-0.1266f},{-0.0151732f,0.39838f,-0.130722f},{-0.0697823f,0.398154f,-0.107617f}, +{-0.260908f,-0.0702357f,-0.11772f},{-0.276946f,-0.0708402f,-0.111238f},{-0.204202f,0.277444f,-0.0249509f}, +{-0.316591f,-0.489946f,-0.0348027f},{-0.321048f,-0.491084f,-0.0186618f},{0.152622f,-0.465959f,-0.142356f}, +{0.0141957f,-0.464461f,-0.161101f},{-0.29283f,-0.0713353f,-0.105617f},{-0.302418f,-0.0715925f,-0.101791f}, +{-0.315376f,-0.0719334f,-0.0965306f},{-0.334584f,-0.0724543f,-0.0895598f},{-0.112591f,0.302691f,-0.0615093f}, +{-0.349574f,-0.0743063f,-0.0856757f},{-0.358185f,-0.0715411f,-0.0805247f},{0.00820872f,0.399987f,-0.144671f}, +{-0.178859f,0.320749f,-0.0490531f},{-0.104971f,0.307752f,-0.0656828f},{-0.0410694f,0.441092f,-0.121565f}, +{-0.319614f,-0.474319f,-0.0342883f},{-0.333967f,-0.471162f,-0.0213819f},{-0.348937f,-0.484627f,0.00558826f}, +{0.265435f,-0.471522f,0.00476514f},{-0.0139835f,0.35932f,-0.107906f},{-0.0550175f,0.357673f,-0.0990772f}, +{0.194409f,-0.452731f,-0.134549f},{0.17511f,-0.452397f,-0.138684f},{0.162223f,-0.452275f,-0.140079f}, +{0.152564f,-0.452101f,-0.141938f},{0.136455f,-0.451857f,-0.144909f},{0.117151f,-0.451567f,-0.1486f}, +{0.0978714f,-0.451336f,-0.151744f},{0.0785859f,-0.45113f,-0.154561f},{0.0592939f,-0.451008f,-0.156516f}, +{0.0399955f,-0.450892f,-0.158567f},{0.0270891f,-0.455387f,-0.159744f},{0.0193981f,-0.449664f,-0.162477f}, +{0.00133434f,-0.450146f,-0.167326f},{-0.0147359f,-0.449799f,-0.171467f},{-0.0307868f,-0.449561f,-0.174721f}, +{-0.0500788f,-0.44942f,-0.176952f},{-0.242336f,-0.482878f,0.298177f},{-0.319247f,0.126562f,-0.0472203f}, +{-0.431095f,0.0555095f,-0.0061284f},{-0.287203f,-0.468345f,-0.044796f},{-0.38992f,0.13053f,-0.0323076f}, +{-0.349137f,-0.474191f,0.00606413f},{0.324231f,-0.464403f,0.0193563f},{0.323909f,-0.455207f,0.00261087f}, +{0.305087f,-0.4565f,-0.00248222f},{0.28811f,-0.462313f,-0.00134399f},{0.278348f,-0.461175f,-0.00605123f}, +{0.00435675f,0.360104f,-0.113617f},{-0.0341886f,0.364934f,-0.107283f},{-0.318012f,0.189827f,-0.0402752f}, +{-0.274509f,-0.458886f,-0.0477541f},{-0.300212f,-0.455368f,-0.037285f},{-0.316141f,-0.456108f,-0.0309186f}, +{0.297441f,-0.450699f,-0.00994178f},{0.287891f,-0.450957f,-0.0121411f},{-0.399508f,0.13024f,-0.0256133f}, +{0.200846f,-0.438417f,-0.139552f},{0.193019f,-0.432301f,-0.144613f},{0.175001f,-0.433189f,-0.146098f}, +{0.155702f,-0.433298f,-0.145513f},{0.136423f,-0.433311f,-0.146619f},{0.117144f,-0.433201f,-0.148548f}, +{0.0978393f,-0.432976f,-0.151384f},{0.0785666f,-0.432841f,-0.155043f},{0.0592167f,-0.432655f,-0.156818f}, +{0.039944f,-0.432616f,-0.158156f},{0.0271084f,-0.432443f,-0.159699f},{0.0174431f,-0.432198f,-0.162393f}, +{0.00134721f,-0.431812f,-0.166644f},{-0.014768f,-0.43144f,-0.170869f},{-0.0308511f,-0.431214f,-0.173673f}, +{-0.0501495f,-0.431182f,-0.175242f},{-0.0301823f,0.399897f,-0.12462f},{-0.268213f,-0.449658f,-0.046365f}, +{-0.33625f,0.00163985f,-0.0621202f},{0.345535f,-0.441259f,-0.00639849f},{0.34068f,-0.43333f,-0.0138259f}, +{0.323073f,-0.435671f,-0.0155429f},{0.305042f,-0.436352f,-0.0183467f},{0.289531f,-0.43389f,-0.0237677f}, +{0.274978f,-0.440031f,-0.0245394f},{0.207283f,-0.428874f,-0.142137f},{-0.277486f,0.0933989f,-0.0426095f}, +{-0.322244f,-0.0305328f,-0.0829812f},{-0.223347f,-0.434533f,-0.0549436f},{-0.259757f,-0.0169448f,-0.105681f}, +{-0.0928941f,0.310215f,-0.0684094f},{-0.166165f,0.340118f,-0.0569049f},{-0.188499f,0.320652f,-0.0430661f}, +{0.381329f,-0.429542f,0.00839203f},{0.355034f,-0.431163f,-0.0114594f},{0.219919f,-0.41499f,-0.139725f}, +{0.210324f,-0.414636f,-0.144092f},{0.194286f,-0.414263f,-0.149654f},{0.174924f,-0.414083f,-0.152683f}, +{0.155638f,-0.414045f,-0.154741f},{0.136391f,-0.418938f,-0.155474f},{0.117054f,-0.41861f,-0.158818f}, +{0.104251f,-0.418964f,-0.159448f},{0.0964117f,-0.411627f,-0.171467f},{0.0784058f,-0.417138f,-0.171377f}, +{0.0591203f,-0.417003f,-0.173808f},{0.0398283f,-0.417382f,-0.171075f},{0.0205492f,-0.417742f,-0.169647f}, +{0.00121859f,-0.412887f,-0.172342f},{-0.0148709f,-0.413151f,-0.169705f},{-0.0309347f,-0.413254f,-0.168895f}, +{-0.050201f,-0.413459f,-0.16784f},{-0.376846f,0.0291824f,0.336182f},{-0.0667085f,-0.0604096f,-0.141764f}, +{-0.0752677f,-0.0574966f,-0.139732f},{-0.0910935f,-0.0558696f,-0.135616f},{-0.10962f,-0.0549243f,-0.132491f}, +{-0.05377f,0.417266f,-0.115745f},{-0.128083f,-0.054069f,-0.128986f},{0.377477f,-0.422443f,-0.00421206f}, +{0.361381f,-0.416212f,-0.0185396f},{0.343838f,-0.418379f,-0.0225073f},{0.322964f,-0.420443f,-0.025041f}, +{0.310077f,-0.420385f,-0.0269252f},{0.136333f,-0.408276f,-0.167114f},{0.11697f,-0.407775f,-0.172985f}, +{0.0783544f,-0.407157f,-0.180187f},{0.059056f,-0.407177f,-0.181647f},{0.0397575f,-0.40746f,-0.180001f}, +{0.0204591f,-0.407492f,-0.179583f},{-0.372737f,0.188187f,-0.0315874f},{-0.396814f,-0.245272f,0.151635f}, +{0.0300472f,0.402907f,-0.161988f},{-0.185033f,-0.415022f,-0.0589884f},{-0.20417f,-0.415749f,-0.0533873f}, +{-0.219167f,-0.417845f,-0.0483007f},{-0.227629f,-0.415363f,-0.0426931f},{-0.380833f,-0.271683f,0.093952f}, +{-0.0345552f,0.417343f,-0.126768f},{0.380705f,-0.411922f,-0.0144947f},{0.339015f,-0.410701f,-0.028102f}, +{0.3229f,-0.410675f,-0.0285714f},{-0.251223f,-0.065387f,-0.121064f},{0.250477f,-0.395171f,-0.128118f}, +{0.234034f,-0.397981f,-0.133552f},{0.219848f,-0.400971f,-0.140954f},{0.212215f,-0.39481f,-0.148793f}, +{0.194312f,-0.399916f,-0.156046f},{0.174834f,-0.394283f,-0.166696f},{0.155542f,-0.394161f,-0.168837f}, +{0.13742f,-0.394341f,-0.175608f},{0.1169f,-0.392913f,-0.18292f},{0.0975756f,-0.39274f,-0.185345f}, +{0.0782643f,-0.392868f,-0.186013f},{0.0589916f,-0.392888f,-0.186933f},{0.0396804f,-0.392952f,-0.187254f}, +{0.0203884f,-0.392933f,-0.187499f},{0.00426672f,-0.393312f,-0.1851f},{-0.0102666f,-0.391357f,-0.18229f}, +{-0.0150188f,-0.398688f,-0.177518f},{-0.0311083f,-0.394534f,-0.172586f},{-0.0471593f,-0.395293f,-0.164104f}, +{-0.0565931f,-0.395942f,-0.15894f},{-0.153072f,0.320954f,-0.0621009f},{-0.107093f,0.396296f,-0.0868846f}, +{-0.167715f,0.302543f,-0.0497283f},{-0.185103f,0.30145f,-0.0428025f},{-0.108058f,0.345346f,-0.0850583f}, +{0.398556f,-0.396341f,-0.019292f},{0.382325f,-0.399434f,-0.0228417f},{0.364673f,-0.401614f,-0.0251117f}, +{0.359734f,-0.394f,-0.0299476f},{0.342192f,-0.396418f,-0.030507f},{0.322829f,-0.396495f,-0.030925f}, +{0.226369f,-0.391762f,-0.141198f},{0.194119f,-0.389749f,-0.1642f},{0.129658f,-0.388296f,-0.181383f}, +{-0.432073f,0.00232147f,0.247658f},{-0.184923f,-0.396682f,-0.0553037f},{-0.202884f,-0.398251f,-0.0495482f}, +{-0.223424f,-0.398296f,-0.0397093f},{-0.328552f,-0.0035883f,-0.0680685f},{-0.376268f,-0.264924f,0.15395f}, +{-0.355079f,-0.00288092f,0.268069f},{-0.0902833f,0.343147f,-0.0872769f},{0.377496f,-0.39182f,-0.0286229f}, +{0.256419f,-0.379737f,-0.13725f},{0.247854f,-0.376026f,-0.146317f},{0.234047f,-0.378007f,-0.152149f}, +{0.216588f,-0.381267f,-0.154972f},{0.211746f,-0.373904f,-0.162381f},{0.194087f,-0.375679f,-0.167885f}, +{0.176081f,-0.376091f,-0.17456f},{0.155464f,-0.379595f,-0.177113f},{0.13915f,-0.379493f,-0.178136f}, +{0.134513f,-0.372406f,-0.181383f},{0.116797f,-0.374348f,-0.184927f},{0.0975113f,-0.374059f,-0.189145f}, +{0.0783351f,-0.373814f,-0.193357f},{0.0589724f,-0.378483f,-0.194155f},{0.0395968f,-0.37856f,-0.195595f}, +{0.0202984f,-0.378599f,-0.19418f},{0.0030449f,-0.375711f,-0.194624f},{-0.0119836f,-0.374612f,-0.189846f}, +{-0.0269284f,-0.373711f,-0.182599f},{-0.0354426f,-0.377313f,-0.176271f},{-0.0504003f,-0.376695f,-0.166818f}, +{-0.210794f,-0.392843f,-0.0463393f},{-0.341433f,0.0888203f,-0.0460564f},{-0.0527925f,0.340221f,-0.0890389f}, +{0.401373f,-0.380669f,-0.0243658f},{0.380589f,-0.377705f,-0.0326999f},{0.361471f,-0.377859f,-0.0330021f}, +{0.341986f,-0.37784f,-0.0325327f},{0.226298f,-0.371924f,-0.160329f},{0.168332f,-0.370245f,-0.179705f}, +{0.1554f,-0.370136f,-0.181595f},{0.0589402f,-0.368676f,-0.200817f},{0.0395839f,-0.368554f,-0.203395f}, +{0.0202212f,-0.369062f,-0.199428f},{0.00733414f,-0.36921f,-0.1989f},{-0.354249f,0.0934375f,-0.0446931f}, +{-0.398344f,-0.251227f,0.136806f},{-0.108051f,-0.377563f,-0.0586862f},{-0.127337f,-0.377679f,-0.058384f}, +{-0.146635f,-0.377589f,-0.0602488f},{-0.165844f,-0.377673f,-0.060146f},{-0.185091f,-0.378174f,-0.0551172f}, +{-0.201019f,-0.378895f,-0.0484357f},{-0.2108f,-0.379229f,-0.0450918f},{-0.360641f,-0.031954f,-0.0637793f}, +{-0.0316164f,0.341442f,-0.0910903f},{0.431693f,-0.3751f,-0.00904149f},{0.396473f,-0.373351f,-0.0290151f}, +{0.253159f,-0.359448f,-0.152561f},{0.232581f,-0.357468f,-0.165924f},{0.213289f,-0.357159f,-0.17094f}, +{0.195167f,-0.35777f,-0.17483f},{0.174718f,-0.356329f,-0.181904f},{0.155329f,-0.356021f,-0.185917f}, +{0.136025f,-0.356169f,-0.185582f},{0.116745f,-0.355873f,-0.189383f},{0.0987331f,-0.356522f,-0.192862f}, +{0.084605f,-0.359815f,-0.197273f},{0.0768625f,-0.354201f,-0.199788f},{0.0588437f,-0.354825f,-0.203505f}, +{0.0395003f,-0.354612f,-0.206887f},{0.0201762f,-0.354831f,-0.206109f},{0.00404809f,-0.355043f,-0.204772f}, +{-0.0103759f,-0.353095f,-0.200675f},{-0.0152053f,-0.360503f,-0.194618f},{-0.0312884f,-0.356683f,-0.187235f}, +{-0.030118f,-0.479399f,0.347571f},{0.0219446f,0.397531f,-0.15269f},{-0.358667f,0.110723f,-0.0458442f}, +{-0.424781f,0.0588084f,-0.0108742f},{0.433861f,-0.359384f,-0.0142889f},{0.418909f,-0.360207f,-0.0223272f}, +{0.406106f,-0.3644f,-0.026983f},{0.398486f,-0.358799f,-0.0295875f},{0.380615f,-0.359686f,-0.0323269f}, +{0.361361f,-0.359853f,-0.0312401f},{-0.0793962f,0.398103f,-0.102235f},{-0.200968f,0.251413f,-0.0134979f}, +{-0.341304f,0.0793994f,-0.0464294f},{0.245494f,-0.353307f,-0.160914f},{0.187605f,-0.351982f,-0.179139f}, +{0.0910099f,-0.350683f,-0.198199f},{-0.0441111f,-0.352735f,-0.180618f},{-0.233243f,0.281978f,-0.0114851f}, +{-0.0771647f,0.377428f,-0.102164f},{-0.108128f,-0.359358f,-0.0574708f},{-0.127408f,-0.359423f,-0.0577023f}, +{-0.146712f,-0.359242f,-0.0606604f},{-0.165985f,-0.359172f,-0.0619658f},{-0.185232f,-0.359538f,-0.0583004f}, +{-0.204511f,-0.360201f,-0.0518568f},{-0.339915f,0.269303f,-0.283605f},{0.254966f,-0.340008f,-0.156072f}, +{0.245442f,-0.339577f,-0.161442f},{0.232549f,-0.339179f,-0.166464f},{0.213154f,-0.338658f,-0.173326f}, +{0.197167f,-0.338439f,-0.176895f},{0.187438f,-0.338214f,-0.179653f},{0.174608f,-0.338008f,-0.182618f}, +{0.155297f,-0.337758f,-0.186746f},{0.135986f,-0.337481f,-0.190971f},{0.116662f,-0.341661f,-0.196309f}, +{0.103813f,-0.341803f,-0.196888f},{0.0960773f,-0.335938f,-0.200688f},{0.0780586f,-0.336568f,-0.204579f}, +{0.0587473f,-0.336491f,-0.206225f},{0.0394489f,-0.336253f,-0.20962f},{0.0217324f,-0.338349f,-0.212752f}, +{0.00291629f,-0.337629f,-0.213241f},{-0.0121379f,-0.336652f,-0.208206f},{-0.0271728f,-0.335816f,-0.201248f}, +{-0.0355584f,-0.339333f,-0.19445f},{0.43806f,-0.342523f,-0.0153435f},{0.418954f,-0.34186f,-0.0241021f}, +{0.399823f,-0.34624f,-0.0280055f},{0.38064f,-0.346413f,-0.0280827f},{0.353471f,-0.346156f,-0.0265007f}, +{-0.412408f,0.130015f,-0.0195942f},{-0.316122f,-0.255265f,0.240365f},{-0.186936f,-0.0145526f,-0.112871f}, +{-0.296907f,0.131153f,-0.0426031f},{0.116668f,-0.332388f,-0.199248f},{0.0168837f,-0.331211f,-0.217305f}, +{0.0071991f,-0.331321f,-0.217118f},{-0.375753f,0.00609625f,0.303244f},{-0.109498f,-0.341667f,-0.0609241f}, +{-0.127491f,-0.340857f,-0.0605575f},{-0.146835f,-0.340677f,-0.0623324f},{-0.164474f,-0.342812f,-0.0642037f}, +{-0.186563f,-0.341648f,-0.063059f},{-0.20453f,-0.341282f,-0.0579338f},{-0.219411f,-0.340561f,-0.0491753f}, +{-0.227919f,-0.344323f,-0.0415485f},{-0.373702f,0.11208f,-0.0423523f},{0.0166908f,0.412739f,-0.157738f}, +{-0.206177f,-0.0147069f,-0.112755f},{0.232414f,-0.321205f,-0.16431f},{0.213135f,-0.320716f,-0.171113f}, +{0.193843f,-0.320343f,-0.176708f},{0.174557f,-0.31999f,-0.182065f},{0.155259f,-0.319797f,-0.186476f}, +{0.139195f,-0.319263f,-0.192142f},{0.129401f,-0.318671f,-0.198527f},{0.116598f,-0.318369f,-0.203492f}, +{0.0972605f,-0.318086f,-0.20764f},{0.0779685f,-0.31772f,-0.212656f},{0.058638f,-0.317707f,-0.21398f}, +{0.0408508f,-0.319945f,-0.214688f},{0.0232758f,-0.317199f,-0.22189f},{0.00712193f,-0.317347f,-0.220906f}, +{-0.00791295f,-0.316041f,-0.218687f},{-0.0164593f,-0.319456f,-0.214546f},{-0.0314813f,-0.318671f,-0.2069f}, +{-0.333761f,0.0945243f,-0.0467638f},{-0.101826f,-0.33561f,-0.0671297f},{-0.169342f,-0.335777f,-0.065985f}, +{-0.178943f,-0.33588f,-0.0656699f},{-0.128867f,0.289489f,-0.0502813f},{-0.389348f,0.055143f,-0.0314395f}, +{0.0361306f,-0.312729f,-0.219633f},{0.0188064f,0.379345f,-0.136735f},{-0.114701f,-0.326723f,-0.0646731f}, +{-0.127568f,-0.326813f,-0.0641008f},{-0.143671f,-0.326723f,-0.0642037f},{-0.148539f,-0.319597f,-0.0669946f}, +{-0.166217f,-0.321642f,-0.0696376f},{-0.185386f,-0.321867f,-0.0692003f},{-0.200408f,-0.320729f,-0.0664802f}, +{-0.20889f,-0.324208f,-0.0617858f},{-0.222549f,-0.322581f,-0.0527636f},{-0.230279f,-0.328845f,-0.0446866f}, +{-0.27829f,-0.0147905f,-0.0996752f},{-0.402839f,-0.255696f,0.0866918f},{-0.156641f,0.359429f,-0.0620752f}, +{-0.259853f,0.129867f,-0.0303977f},{-0.225507f,-0.0148805f,-0.111746f},{0.232311f,-0.302794f,-0.167403f}, +{0.214324f,-0.303141f,-0.175171f},{0.19374f,-0.301759f,-0.182271f},{0.174396f,-0.301206f,-0.189023f}, +{0.15513f,-0.301013f,-0.193872f},{0.142391f,-0.305534f,-0.195479f},{0.134507f,-0.299341f,-0.202058f}, +{0.116527f,-0.299746f,-0.208643f},{0.0971769f,-0.299553f,-0.211813f},{0.081081f,-0.299264f,-0.215794f}, +{0.0714157f,-0.299135f,-0.217761f},{0.0585608f,-0.299129f,-0.218829f},{0.0392624f,-0.298923f,-0.222064f}, +{0.0231922f,-0.298633f,-0.226571f},{0.00704477f,-0.298704f,-0.226906f},{-0.0123372f,-0.299238f,-0.222308f}, +{-0.0332305f,-0.302395f,-0.212347f},{-0.148166f,-0.0508858f,-0.125301f},{-0.127607f,-0.317276f,-0.0687245f}, +{-0.242896f,-0.315733f,-0.0365133f},{-0.167451f,-0.0509308f,-0.126793f},{-0.349825f,-0.274268f,0.182084f}, +{-0.302971f,0.0889425f,-0.0468152f},{-0.18675f,-0.0509437f,-0.127417f},{-0.298257f,0.209826f,-0.0373686f}, +{-0.206067f,-0.0509694f,-0.127006f},{-0.244806f,-0.0151764f,-0.108157f},{-0.296392f,-0.0161731f,-0.0915919f}, +{-0.224112f,-0.0520305f,-0.125938f},{0.245371f,-0.298781f,-0.161191f},{0.20662f,-0.297367f,-0.179917f}, +{-0.0283432f,-0.294974f,-0.218765f},{-0.238362f,-0.0558181f,-0.124401f},{-0.24251f,-0.265773f,0.253985f}, +{-0.244851f,-0.0492267f,-0.118369f},{-0.127684f,-0.302858f,-0.0759525f},{-0.146989f,-0.302871f,-0.0744606f}, +{-0.166287f,-0.302807f,-0.0762933f},{-0.185483f,-0.303051f,-0.0754702f},{-0.203521f,-0.302871f,-0.0691617f}, +{-0.21109f,-0.308685f,-0.0650333f},{-0.223887f,-0.304826f,-0.0569242f},{-0.236465f,-0.31026f,-0.046783f}, +{-0.280856f,-0.314929f,-0.00204493f},{-0.296888f,-0.313347f,0.00282951f},{-0.143819f,0.287103f,-0.0472782f}, +{-0.258921f,-0.0529436f,-0.113276f},{-0.351143f,0.0140575f,-0.0489502f},{-0.276875f,-0.052352f,-0.110086f}, +{-0.294849f,-0.0535738f,-0.106627f},{-0.409984f,0.019472f,0.307707f},{-0.399006f,0.0548214f,-0.0255812f}, +{-0.270586f,-0.020083f,-0.103309f},{0.266541f,-0.281946f,-0.143217f},{0.255744f,-0.286988f,-0.155622f}, +{0.247262f,-0.283284f,-0.164477f},{0.23233f,-0.284312f,-0.171621f},{0.219347f,-0.283901f,-0.176425f}, +{0.209707f,-0.283611f,-0.180534f},{0.193611f,-0.283329f,-0.185049f},{0.174293f,-0.282936f,-0.190077f}, +{0.15821f,-0.282486f,-0.196109f},{0.148584f,-0.282312f,-0.198836f},{0.135748f,-0.282036f,-0.202727f}, +{0.116353f,-0.281586f,-0.208797f},{0.0971448f,-0.28147f,-0.211228f},{0.0778142f,-0.281316f,-0.214578f}, +{0.0584965f,-0.281078f,-0.218199f},{0.0391981f,-0.28075f,-0.222964f},{0.0231729f,-0.28039f,-0.228591f}, +{0.00698689f,-0.280062f,-0.232835f},{-0.0123823f,-0.280383f,-0.230764f},{-0.0317321f,-0.281168f,-0.222803f}, +{-0.315324f,-0.0533874f,-0.0960741f},{-0.238606f,-0.294839f,-0.0495289f},{-0.24714f,-0.29882f,-0.0383653f}, +{-0.261332f,-0.29655f,-0.0361339f},{-0.268985f,-0.302711f,-0.0272724f},{-0.281782f,-0.302974f,-0.0227774f}, +{-0.302392f,-0.29992f,-0.0184303f},{-0.331465f,-0.053979f,-0.086994f},{-0.298386f,-0.24034f,0.26075f}, +{-0.42761f,0.12824f,-0.012797f},{-0.335369f,-0.242693f,0.242372f},{-0.341195f,-0.0541976f,-0.0824604f}, +{-0.353973f,-0.0546413f,-0.0768078f},{-0.368892f,-0.0567249f,-0.0698305f},{-0.127761f,-0.288569f,-0.0824411f}, +{-0.147072f,-0.28855f,-0.0815279f},{-0.166352f,-0.288389f,-0.0835857f},{-0.187238f,-0.28664f,-0.0815858f}, +{-0.204878f,-0.284955f,-0.0752902f},{-0.225694f,-0.288589f,-0.0600302f},{-0.281904f,-0.293039f,-0.0341789f}, +{-0.294656f,-0.293315f,-0.0295875f},{-0.377612f,-0.054069f,-0.0618244f},{-0.1754f,0.296756f,-0.0446545f}, +{-0.2782f,-0.265471f,0.245973f},{-0.362107f,-0.229041f,0.234423f},{0.26858f,-0.260242f,-0.148568f}, +{0.257635f,-0.267091f,-0.157378f},{0.248175f,-0.26648f,-0.165757f},{0.232266f,-0.265863f,-0.174573f}, +{0.219417f,-0.270159f,-0.178419f},{0.211533f,-0.264313f,-0.183132f},{0.193624f,-0.264789f,-0.189801f}, +{0.174178f,-0.264686f,-0.191132f},{0.159162f,-0.265895f,-0.195531f},{0.150596f,-0.262506f,-0.200804f}, +{0.135581f,-0.263824f,-0.203575f},{0.116302f,-0.263651f,-0.206964f},{0.0969904f,-0.263316f,-0.21207f}, +{0.0841355f,-0.267606f,-0.215331f},{0.0764059f,-0.261831f,-0.219999f},{0.0584065f,-0.262641f,-0.222584f}, +{0.0391338f,-0.262339f,-0.227279f},{0.0230957f,-0.261953f,-0.233394f},{0.00694188f,-0.266332f,-0.234957f}, +{-0.0124466f,-0.26421f,-0.234661f},{-0.0317964f,-0.262358f,-0.231047f},{-0.0467992f,-0.261567f,-0.222887f}, +{-0.117704f,0.383499f,-0.0835278f},{-0.0364072f,-0.48212f,0.341198f},{-0.127806f,-0.279161f,-0.0867046f}, +{-0.147137f,-0.279078f,-0.08565f},{-0.16639f,-0.278904f,-0.0886274f},{-0.18246f,-0.279303f,-0.0873605f}, +{-0.220678f,-0.280769f,-0.0675541f},{-0.243526f,-0.282145f,-0.0568277f},{-0.262773f,-0.282698f,-0.0507636f}, +{-0.279988f,-0.276287f,-0.056249f},{-0.285325f,-0.283521f,-0.0440564f},{-0.302797f,-0.281296f,-0.0386161f}, +{-0.321954f,-0.282673f,-0.0228095f},{-0.220665f,0.229311f,0.0031639f},{-0.0906949f,0.286428f,-0.0364425f}, +{0.0101379f,0.436443f,-0.16184f},{0.27719f,-0.263914f,-0.140484f},{0.006884f,-0.257233f,-0.236211f}, +{-0.0124208f,-0.257323f,-0.235844f},{-0.354661f,0.0166168f,0.323713f},{-0.127883f,-0.264937f,-0.0931095f}, +{-0.14724f,-0.264782f,-0.0925758f},{-0.16648f,-0.264699f,-0.0958426f},{-0.185689f,-0.26522f,-0.0914376f}, +{-0.206235f,-0.267007f,-0.0815793f},{-0.224247f,-0.266416f,-0.0773737f},{-0.243558f,-0.266422f,-0.0763512f}, +{-0.262837f,-0.266577f,-0.0745442f},{-0.282033f,-0.272332f,-0.0624996f},{-0.297942f,-0.27293f,-0.0529115f}, +{-0.317356f,-0.274866f,-0.034732f},{-0.347812f,-0.0224109f,-0.0663258f},{0.257731f,-0.244269f,-0.159005f}, +{0.248124f,-0.243896f,-0.163995f},{0.23287f,-0.24587f,-0.172798f},{0.211456f,-0.24796f,-0.183132f}, +{0.193405f,-0.246474f,-0.191087f},{0.177335f,-0.246134f,-0.196154f},{0.167618f,-0.245818f,-0.200322f}, +{0.154744f,-0.245413f,-0.205807f},{0.135478f,-0.245117f,-0.210707f},{0.117485f,-0.245902f,-0.213594f}, +{0.100193f,-0.249503f,-0.215736f},{0.0953442f,-0.242487f,-0.218488f},{0.0776277f,-0.244552f,-0.221131f}, +{0.0583486f,-0.244204f,-0.226707f},{0.0403234f,-0.244847f,-0.231767f},{0.0229349f,-0.24351f,-0.236661f}, +{0.00681327f,-0.243394f,-0.23886f},{-0.0125044f,-0.24351f,-0.238738f},{-0.0306132f,-0.242847f,-0.237137f}, +{-0.0383171f,-0.248609f,-0.233414f},{-0.0510755f,-0.244616f,-0.22661f},{-0.334828f,0.188579f,-0.0405839f}, +{-0.333163f,0.273882f,-0.295952f},{-0.198428f,-0.260988f,-0.0868846f},{-0.282091f,-0.26185f,-0.0785055f}, +{-0.301376f,-0.258152f,-0.0702807f},{-0.307382f,-0.262924f,-0.060416f},{-0.320366f,-0.258976f,-0.0545963f}, +{-0.326694f,-0.26495f,-0.039465f},{-0.33634f,-0.260789f,-0.0343526f},{-0.300212f,-0.249072f,0.252564f}, +{-0.336835f,-0.251272f,0.232552f},{0.283467f,-0.240983f,-0.142163f},{0.270657f,-0.240346f,-0.15069f}, +{0.219115f,-0.242822f,-0.178837f},{0.109749f,-0.240217f,-0.217208f},{0.0324845f,-0.239047f,-0.234745f}, +{-0.0638275f,-0.240713f,-0.219105f},{-0.129581f,-0.248333f,-0.100813f},{-0.146031f,-0.246899f,-0.100344f}, +{-0.166544f,-0.250494f,-0.102704f},{-0.166577f,-0.24351f,-0.105045f},{-0.185843f,-0.246526f,-0.0976367f}, +{-0.205058f,-0.247098f,-0.0892833f},{-0.224453f,-0.246918f,-0.0900935f},{-0.243732f,-0.246777f,-0.0927944f}, +{-0.262985f,-0.246995f,-0.0935661f},{-0.280991f,-0.246468f,-0.091071f},{-0.288643f,-0.252551f,-0.0838687f}, +{-0.301395f,-0.248088f,-0.0803832f},{-0.314218f,-0.253966f,-0.0657406f},{-0.332758f,-0.254088f,-0.051889f}, +{-0.349246f,-0.256423f,-0.031851f},{-0.264111f,-0.0108871f,-0.103058f},{-0.459564f,0.0280763f,0.295232f}, +{-0.222761f,0.242751f,-0.005331f},{0.231121f,-0.22425f,-0.16921f},{0.216517f,-0.223093f,-0.175872f}, +{0.208395f,-0.230565f,-0.181145f},{0.193405f,-0.228655f,-0.186785f},{0.177297f,-0.228102f,-0.194849f}, +{0.167721f,-0.227542f,-0.20317f},{0.154751f,-0.227092f,-0.208739f},{0.13542f,-0.226784f,-0.213588f}, +{0.122597f,-0.231227f,-0.215755f},{0.114803f,-0.225594f,-0.21816f},{0.0968425f,-0.226391f,-0.221073f}, +{0.0775763f,-0.226166f,-0.225266f},{0.0582778f,-0.225826f,-0.230468f},{0.0402527f,-0.226436f,-0.234481f}, +{0.0247612f,-0.224443f,-0.237208f},{0.0067361f,-0.225215f,-0.239786f},{-0.0125752f,-0.225253f,-0.240468f}, +{-0.0319829f,-0.225453f,-0.239336f},{-0.0523938f,-0.227125f,-0.230205f},{-0.067075f,-0.227041f,-0.220314f}, +{-0.302804f,0.325057f,-0.357101f},{-0.12481f,-0.24104f,-0.105932f},{-0.15376f,-0.241272f,-0.103604f}, +{-0.298251f,-0.24297f,-0.0873862f},{-0.320552f,-0.244069f,-0.0723128f},{-0.339349f,-0.244757f,-0.0569306f}, +{-0.0142214f,0.343577f,-0.0948908f},{-0.207077f,0.240359f,-0.00654639f},{0.291029f,-0.222366f,-0.13541f}, +{0.283357f,-0.22733f,-0.142124f},{0.270483f,-0.222147f,-0.150703f},{0.2555f,-0.220122f,-0.157976f}, +{0.247018f,-0.222893f,-0.161422f},{-0.0447735f,-0.22124f,-0.235742f},{-0.128083f,-0.227144f,-0.1088f}, +{-0.147394f,-0.22735f,-0.105675f},{-0.166647f,-0.227285f,-0.108067f},{-0.181682f,-0.22616f,-0.104948f}, +{-0.190016f,-0.228919f,-0.101026f},{-0.203559f,-0.230404f,-0.0983441f},{-0.224517f,-0.232796f,-0.0976946f}, +{-0.243815f,-0.232603f,-0.101527f},{-0.263101f,-0.232841f,-0.102376f},{-0.283988f,-0.23079f,-0.0994694f}, +{-0.300379f,-0.22971f,-0.096685f},{-0.314482f,-0.226816f,-0.0928073f},{-0.3207f,-0.234185f,-0.0835986f}, +{-0.333562f,-0.230108f,-0.0782547f},{-0.336687f,-0.235201f,-0.0708466f},{-0.352545f,-0.236166f,-0.0560239f}, +{-0.28748f,-0.479747f,0.292415f},{0.207797f,-0.204083f,-0.181563f},{0.194273f,-0.208585f,-0.187209f}, +{0.177258f,-0.20991f,-0.195582f},{0.167413f,-0.209408f,-0.202411f},{0.15468f,-0.208964f,-0.208553f}, +{0.13533f,-0.208726f,-0.212643f},{0.122462f,-0.208488f,-0.216462f},{0.112855f,-0.208366f,-0.218623f}, +{0.0967975f,-0.208167f,-0.222334f},{0.0774541f,-0.207652f,-0.229793f},{0.0581364f,-0.207505f,-0.232642f}, +{0.0420469f,-0.207472f,-0.233742f},{0.0324394f,-0.207427f,-0.234951f},{0.0228063f,-0.207376f,-0.236571f}, +{0.00668465f,-0.207363f,-0.237787f},{-0.0126459f,-0.207228f,-0.239786f},{-0.0320472f,-0.207337f,-0.239484f}, +{-0.0447799f,-0.212193f,-0.235697f},{-0.0525289f,-0.207177f,-0.230996f},{-0.208485f,-0.223137f,-0.103862f}, +{-0.224588f,-0.222951f,-0.106312f},{-0.243835f,-0.222919f,-0.108556f},{-0.263159f,-0.223176f,-0.108627f}, +{-0.279229f,-0.223478f,-0.104273f},{-0.333671f,-0.220372f,-0.0866596f},{-0.351207f,-0.220282f,-0.0727115f}, +{-0.358821f,-0.226726f,-0.0594643f},{-0.370667f,-0.222218f,-0.0515289f},{-0.386113f,0.0409569f,-0.0281148f}, +{-0.239307f,0.243812f,-0.00250151f},{0.254394f,-0.203492f,-0.157403f},{0.244806f,-0.203196f,-0.161686f}, +{0.231938f,-0.202772f,-0.167885f},{0.216929f,-0.203704f,-0.176142f},{-0.0388702f,0.396682f,-0.120768f}, +{-0.128173f,-0.208501f,-0.113861f},{-0.147458f,-0.208707f,-0.111475f},{-0.166737f,-0.208662f,-0.112884f}, +{-0.186017f,-0.208906f,-0.110865f},{-0.205341f,-0.208977f,-0.110035f},{-0.224639f,-0.208881f,-0.112807f}, +{-0.243918f,-0.208829f,-0.115173f},{-0.263242f,-0.209112f,-0.11361f},{-0.282496f,-0.209588f,-0.108691f}, +{-0.298579f,-0.209871f,-0.105495f},{-0.315884f,-0.211241f,-0.100067f},{-0.332989f,-0.203653f,-0.0944986f}, +{-0.0356677f,-0.0449182f,-0.141745f},{-0.0517443f,-0.0449503f,-0.141706f},{0.192402f,-0.186624f,-0.187769f}, +{0.177104f,-0.187293f,-0.194682f},{0.167528f,-0.186959f,-0.199293f},{0.154654f,-0.190991f,-0.206353f}, +{0.13533f,-0.190798f,-0.21009f},{0.120314f,-0.192013f,-0.215009f},{0.111633f,-0.188714f,-0.219125f}, +{0.0967203f,-0.189936f,-0.224141f},{0.0773898f,-0.189557f,-0.229999f},{0.0580528f,-0.189344f,-0.233574f}, +{0.0430629f,-0.190888f,-0.233973f},{0.0259123f,-0.189068f,-0.239131f},{0.00660105f,-0.188926f,-0.2416f}, +{-0.0127488f,-0.189068f,-0.24086f},{-0.0320601f,-0.189235f,-0.239555f},{-0.0513328f,-0.18984f,-0.232706f}, +{-0.308193f,-0.205389f,-0.104312f},{-0.348738f,-0.200778f,-0.0857979f},{-0.36502f,-0.2078f,-0.0682293f}, +{0.254374f,-0.185415f,-0.155905f},{0.244761f,-0.185081f,-0.160876f},{0.231841f,-0.184618f,-0.167589f}, +{0.215688f,-0.184026f,-0.176174f},{0.206183f,-0.183673f,-0.180978f},{0.0386965f,-0.184701f,-0.235838f}, +{-0.131459f,-0.194405f,-0.119462f},{-0.147542f,-0.189962f,-0.118614f},{-0.166834f,-0.190007f,-0.118607f}, +{-0.186126f,-0.190116f,-0.117642f},{-0.20381f,-0.192386f,-0.119224f},{-0.224703f,-0.19465f,-0.120864f}, +{-0.242741f,-0.188926f,-0.125308f},{-0.250503f,-0.19483f,-0.12208f},{-0.264966f,-0.192753f,-0.119224f}, +{-0.28265f,-0.190926f,-0.114067f},{-0.298695f,-0.191241f,-0.110492f},{-0.314829f,-0.188631f,-0.106144f}, +{-0.321189f,-0.196579f,-0.102087f},{-0.384666f,-0.200495f,-0.0488087f},{0.154513f,-0.168696f,-0.202257f}, +{0.135208f,-0.168265f,-0.208701f},{0.122231f,-0.167988f,-0.213401f},{0.112617f,-0.172059f,-0.219884f}, +{0.0965724f,-0.171564f,-0.227272f},{0.0772612f,-0.171287f,-0.232134f},{0.057982f,-0.171236f,-0.233568f}, +{0.0258287f,-0.170895f,-0.239851f},{0.0064853f,-0.170785f,-0.242179f},{-0.0128388f,-0.170985f,-0.240468f}, +{-0.0308383f,-0.172142f,-0.238134f},{-0.0514292f,-0.171821f,-0.231722f},{-0.231925f,-0.0466544f,-0.121996f}, +{-0.000286191f,0.396785f,-0.137282f},{-0.208659f,-0.185293f,-0.123424f},{-0.224742f,-0.185152f,-0.125771f}, +{-0.260162f,-0.185505f,-0.123867f},{-0.335362f,-0.183981f,-0.0998939f},{-0.353162f,-0.183698f,-0.0911675f}, +{-0.298759f,0.345281f,-0.371531f},{0.308964f,-0.170007f,-0.116054f},{-0.302694f,-0.0482235f,-0.103032f}, +{0.244555f,-0.171448f,-0.161004f},{0.215681f,-0.16593f,-0.175338f},{0.206035f,-0.165615f,-0.180052f}, +{0.193129f,-0.165281f,-0.185447f},{0.173856f,-0.164792f,-0.192727f},{-0.0385808f,-0.166979f,-0.235182f}, +{-0.128449f,-0.170522f,-0.133179f},{-0.147651f,-0.17112f,-0.126915f},{-0.166924f,-0.171371f,-0.124819f}, +{-0.186222f,-0.171326f,-0.12552f},{-0.205527f,-0.171268f,-0.127623f},{-0.224806f,-0.171326f,-0.128613f}, +{-0.244111f,-0.171416f,-0.128092f},{-0.263435f,-0.171737f,-0.125076f},{-0.281441f,-0.173306f,-0.117405f}, +{-0.296418f,-0.167056f,-0.113283f},{-0.314115f,-0.167384f,-0.10999f},{-0.327723f,-0.178129f,-0.104582f}, +{-0.365856f,-0.179563f,-0.0851162f},{-0.311562f,0.346207f,-0.370239f},{-0.112906f,-0.0277612f,-0.121623f}, +{0.115839f,-0.147288f,-0.212437f},{0.109389f,-0.158542f,-0.219015f},{0.0956914f,-0.150407f,-0.222301f}, +{0.0771711f,-0.149031f,-0.226379f},{0.0586958f,-0.150002f,-0.229555f},{0.00640814f,-0.152928f,-0.239028f}, +{-0.0116234f,-0.154008f,-0.237459f},{-0.0257387f,-0.157808f,-0.236037f},{-0.032999f,-0.148002f,-0.231105f}, +{-0.051455f,-0.149603f,-0.226173f},{-0.315093f,-0.235465f,0.25749f},{-0.327755f,-0.164342f,-0.105354f}, +{-0.337484f,-0.164696f,-0.101244f},{-0.353445f,-0.165262f,-0.0944921f},{-0.368332f,-0.164336f,-0.0870583f}, +{0.193065f,-0.146979f,-0.1862f},{0.173721f,-0.146606f,-0.192502f},{0.154513f,-0.146034f,-0.200945f}, +{0.135137f,-0.145764f,-0.205717f},{-0.0193917f,-0.148838f,-0.233806f},{-0.123883f,-0.149892f,-0.142754f}, +{-0.132726f,-0.153764f,-0.133989f},{-0.147741f,-0.152612f,-0.129636f},{-0.167014f,-0.152793f,-0.128234f}, +{-0.186312f,-0.152735f,-0.130169f},{-0.00889684f,0.422186f,-0.143655f},{0.102977f,-0.140529f,-0.216012f}, +{-0.318225f,-0.223105f,0.263252f},{-0.366892f,-0.00498377f,-0.0479984f},{-0.110251f,-0.491759f,-0.181525f}, +{-0.0913894f,-0.491913f,-0.170451f},{-0.131369f,-0.491315f,-0.169191f},{-0.111292f,-0.49145f,-0.169943f}, +{-0.0513714f,-0.491663f,-0.151969f},{-0.0331983f,-0.491624f,-0.149641f},{-0.0130961f,-0.49174f,-0.150439f}, +{0.0435452f,-0.489901f,-0.13197f},{0.0612102f,-0.489663f,-0.131745f},{0.0793768f,-0.489631f,-0.129423f}, +{0.0996656f,-0.489405f,-0.130517f},{0.155735f,-0.487618f,-0.114164f},{0.174171f,-0.486608f,-0.112215f}, +{0.189656f,-0.484576f,-0.112292f},{-0.165329f,-0.488055f,-0.165667f},{-0.125909f,-0.491836f,-0.151371f}, +{-0.108263f,-0.491585f,-0.151146f},{-0.0900904f,-0.49154f,-0.148818f},{-0.0699945f,-0.491643f,-0.149596f}, +{-0.0160992f,-0.490897f,-0.133668f},{0.00396448f,-0.490171f,-0.130407f},{0.0226391f,-0.4901f,-0.132845f}, +{0.0807787f,-0.488531f,-0.11381f},{0.0982894f,-0.488306f,-0.112389f},{0.114906f,-0.488319f,-0.11154f}, +{0.135118f,-0.488261f,-0.112492f},{0.191971f,-0.48628f,-0.09446f},{0.202884f,-0.484119f,-0.103309f}, +{0.20808f,-0.483258f,-0.0937976f},{-0.147793f,-0.491798f,-0.149712f},{-0.0728047f,-0.490479f,-0.133153f}, +{-0.0527861f,-0.48983f,-0.129809f},{-0.0355262f,-0.490274f,-0.129012f},{0.0236487f,-0.487901f,-0.116549f}, +{0.0419633f,-0.487168f,-0.112414f},{0.0601042f,-0.48718f,-0.110041f},{0.117163f,-0.485644f,-0.097193f}, +{0.135915f,-0.486377f,-0.095849f},{0.15513f,-0.487213f,-0.0912961f},{0.17367f,-0.487496f,-0.0935918f}, +{-0.449989f,0.00813478f,0.248461f},{-0.145728f,-0.491373f,-0.133513f},{-0.128584f,-0.491174f,-0.130298f}, +{-0.10991f,-0.490229f,-0.128678f},{-0.0921225f,-0.489714f,-0.128671f},{-0.031835f,-0.483238f,-0.116736f}, +{-0.0167037f,-0.484808f,-0.116517f},{0.00189381f,-0.484351f,-0.113276f},{0.0818527f,-0.485258f,-0.101489f}, +{0.0984116f,-0.484917f,-0.0985306f},{0.173901f,-0.486576f,-0.0760618f},{0.192023f,-0.486705f,-0.0736632f}, +{-0.168641f,-0.49165f,-0.132992f},{-0.0911964f,-0.483785f,-0.118948f},{-0.0730427f,-0.48464f,-0.120575f}, +{-0.0523617f,-0.482775f,-0.118221f},{0.00335356f,-0.475322f,-0.106074f},{0.0212051f,-0.480087f,-0.106177f}, +{0.0343301f,-0.480049f,-0.103611f},{0.041751f,-0.470757f,-0.0971737f},{0.0607922f,-0.471419f,-0.0942992f}, +{0.0806115f,-0.471117f,-0.0906723f},{0.100894f,-0.474075f,-0.0899521f},{0.119067f,-0.47585f,-0.0877528f}, +{0.133176f,-0.479046f,-0.0850133f},{0.139909f,-0.470962f,-0.0775409f},{0.157799f,-0.481946f,-0.0774637f}, +{0.196035f,-0.486415f,-0.0577216f},{0.211578f,-0.484203f,-0.0575994f},{0.224408f,-0.482055f,-0.0584868f}, +{-0.165348f,-0.491412f,-0.114614f},{-0.14686f,-0.490775f,-0.112742f},{-0.129395f,-0.488351f,-0.109109f}, +{-0.111466f,-0.484775f,-0.113057f},{-0.0891579f,-0.471522f,-0.108466f},{-0.0720395f,-0.471091f,-0.110614f}, +{-0.0527539f,-0.471252f,-0.110929f},{-0.032253f,-0.469728f,-0.108279f},{-0.013842f,-0.471046f,-0.106427f}, +{0.0209929f,-0.468191f,-0.099135f},{0.121511f,-0.465696f,-0.0803254f},{0.155632f,-0.472004f,-0.0701456f}, +{0.167721f,-0.478049f,-0.0589434f},{0.177007f,-0.484441f,-0.0562425f},{-0.184801f,-0.487926f,-0.112749f}, +{-0.104829f,-0.478531f,-0.108537f},{-0.00903832f,-0.46511f,-0.10192f},{0.004241f,-0.464802f,-0.099553f}, +{0.100585f,-0.462332f,-0.0827369f},{0.157169f,-0.467252f,-0.0572972f},{0.176551f,-0.484949f,-0.0377415f}, +{0.193264f,-0.486377f,-0.036121f},{0.212665f,-0.484074f,-0.0356001f},{0.224858f,-0.482512f,-0.0334201f}, +{-0.352043f,-0.278075f,0.16793f},{-0.18556f,-0.490968f,-0.0940098f},{-0.167451f,-0.491032f,-0.0915404f}, +{-0.14724f,-0.489013f,-0.0923507f},{-0.131041f,-0.483663f,-0.0930645f},{-0.112103f,-0.469548f,-0.0943121f}, +{-0.0604193f,-0.461059f,-0.103823f},{-0.0504261f,-0.459426f,-0.0961384f},{-0.0323109f,-0.459471f,-0.0937076f}, +{-0.0139642f,-0.459091f,-0.0916176f},{0.00604159f,-0.459297f,-0.0922414f},{0.0226713f,-0.45886f,-0.0893476f}, +{0.0390823f,-0.45877f,-0.0861644f},{0.0606829f,-0.457098f,-0.0773866f},{0.0806051f,-0.456603f,-0.0738947f}, +{0.0981608f,-0.456506f,-0.0734896f},{0.118192f,-0.457844f,-0.0712967f},{0.133902f,-0.459059f,-0.0670525f}, +{0.137812f,-0.458506f,-0.0571235f},{0.166589f,-0.477091f,-0.0439407f},{0.230665f,-0.482583f,-0.0170991f}, +{-0.196646f,-0.486422f,-0.0956625f},{-0.14614f,-0.484782f,-0.0751744f},{-0.126166f,-0.477297f,-0.0899778f}, +{-0.098611f,-0.46286f,-0.097791f},{-0.0876403f,-0.459458f,-0.0915533f},{-0.071255f,-0.458699f,-0.0931738f}, +{0.00337286f,-0.457902f,-0.0760361f},{0.0219189f,-0.457149f,-0.0742355f},{0.042169f,-0.456937f,-0.0752066f}, +{0.0984052f,-0.455722f,-0.0560882f},{0.116044f,-0.455445f,-0.0558374f},{0.151233f,-0.46104f,-0.049709f}, +{0.157226f,-0.469014f,-0.0381724f},{0.172171f,-0.485952f,-0.0186167f},{0.193097f,-0.486377f,-0.0180573f}, +{0.209939f,-0.485496f,-0.0155172f},{-0.185348f,-0.491637f,-0.0737532f},{-0.164024f,-0.490582f,-0.0734317f}, +{-0.127047f,-0.469368f,-0.0775602f},{-0.101569f,-0.460133f,-0.088936f},{-0.0714929f,-0.458892f,-0.0750972f}, +{-0.0540208f,-0.458911f,-0.0745764f},{-0.0364265f,-0.458705f,-0.0742484f},{-0.015469f,-0.458365f,-0.0733738f}, +{0.0257065f,-0.457432f,-0.0580045f},{0.0419826f,-0.456969f,-0.057175f},{0.0585351f,-0.456648f,-0.0541783f}, +{0.0773255f,-0.456313f,-0.0567248f},{0.136783f,-0.457207f,-0.0380245f},{0.147703f,-0.459304f,-0.0399601f}, +{0.149902f,-0.475174f,-0.0189254f},{0.157439f,-0.483457f,-0.0158194f},{0.174904f,-0.487335f,0.000244383f}, +{0.192376f,-0.487361f,0.000745975f},{0.210267f,-0.48664f,0.000649515f},{0.232549f,-0.483856f,-0.000456558f}, +{0.245937f,-0.48329f,0.00167842f},{-0.18529f,-0.491405f,-0.0561847f},{-0.167104f,-0.490087f,-0.0567055f}, +{-0.151047f,-0.484608f,-0.0584933f},{-0.109993f,-0.460133f,-0.0715025f},{-0.0922768f,-0.458937f,-0.0762226f}, +{-0.0308768f,-0.459117f,-0.0565898f},{-0.0139642f,-0.458551f,-0.0561204f},{0.00377799f,-0.457844f,-0.0550079f}, +{0.0625928f,-0.456969f,-0.0380502f},{0.0804443f,-0.456288f,-0.0380952f},{0.0988939f,-0.455683f,-0.0361532f}, +{0.119369f,-0.455046f,-0.037465f},{0.141034f,-0.465233f,-0.0178001f},{0.153747f,-0.486036f,0.00079742f}, +{0.213f,-0.485727f,0.0189962f},{0.229558f,-0.485804f,0.0199737f},{0.248606f,-0.484119f,0.0210347f}, +{0.26858f,-0.47558f,0.0111829f},{0.288714f,-0.472416f,0.0155494f},{0.306231f,-0.470776f,0.0226167f}, +{0.310443f,-0.463844f,0.00981964f},{-0.224781f,-0.491733f,-0.0549307f},{-0.206305f,-0.491907f,-0.0569756f}, +{-0.135613f,-0.470229f,-0.0626025f},{-0.124019f,-0.461033f,-0.068718f},{-0.0896531f,-0.45994f,-0.0568727f}, +{-0.0721617f,-0.459902f,-0.0563904f},{-0.0514485f,-0.459979f,-0.0551493f},{0.0043246f,-0.458435f,-0.0382045f}, +{0.0226648f,-0.458024f,-0.0361017f},{0.0414231f,-0.457709f,-0.0386097f},{0.0994469f,-0.456223f,-0.0193434f}, +{0.11672f,-0.456378f,-0.0175493f},{0.130005f,-0.457342f,-0.0212147f},{0.132829f,-0.472731f,0.00142119f}, +{0.141529f,-0.484267f,0.00572974f},{0.156886f,-0.488074f,0.0182824f},{0.173599f,-0.487438f,0.0210476f}, +{0.192595f,-0.487026f,0.0232919f},{0.270638f,-0.483528f,0.0369313f},{0.284952f,-0.479406f,0.0300312f}, +{0.310193f,-0.475811f,0.0396836f},{0.339658f,-0.462377f,0.0256391f},{-0.203045f,-0.491862f,-0.0386418f}, +{-0.184647f,-0.491309f,-0.0366355f},{-0.169342f,-0.490061f,-0.0335102f},{-0.155252f,-0.486203f,-0.0400759f}, +{-0.139999f,-0.470178f,-0.0516832f},{-0.127748f,-0.461014f,-0.0536188f},{-0.108225f,-0.459914f,-0.054667f}, +{-0.0706569f,-0.460564f,-0.0393814f},{-0.0535706f,-0.460442f,-0.0360696f},{-0.0367223f,-0.459979f,-0.0354779f}, +{-0.0162921f,-0.459374f,-0.0367126f},{0.0429086f,-0.457979f,-0.0213112f},{0.0609852f,-0.457612f,-0.0168162f}, +{0.0772612f,-0.45713f,-0.0159802f},{0.118276f,-0.459194f,-0.00186487f},{0.136436f,-0.486428f,0.0216328f}, +{0.199257f,-0.484287f,0.0293238f},{0.217154f,-0.481476f,0.0303463f},{0.232845f,-0.483676f,0.0385004f}, +{0.250696f,-0.486023f,0.0412141f},{0.289036f,-0.481714f,0.0449761f},{0.299518f,-0.480499f,0.0459921f}, +{0.327009f,-0.471303f,0.0436513f},{0.343368f,-0.465252f,0.0412077f},{-0.337967f,-0.286254f,0.172303f}, +{-0.260143f,-0.492505f,-0.0377673f},{-0.242118f,-0.492666f,-0.0351628f},{-0.221855f,-0.492312f,-0.0361146f}, +{-0.164705f,-0.489463f,-0.0183659f},{-0.14695f,-0.474859f,-0.036957f},{-0.1267f,-0.460628f,-0.0399087f}, +{-0.110257f,-0.460275f,-0.0357287f},{-0.0942703f,-0.460185f,-0.0367512f},{-0.0310762f,-0.460262f,-0.0180123f}, +{-0.0133469f,-0.459773f,-0.0178515f},{0.00447894f,-0.459111f,-0.0178386f},{0.0255651f,-0.45848f,-0.0171698f}, +{0.0810745f,-0.457406f,0.0002058f},{0.097865f,-0.457046f,0.000855296f},{0.120855f,-0.463786f,0.00681652f}, +{0.118437f,-0.470152f,0.0181152f},{0.12425f,-0.481592f,0.0237549f},{0.1367f,-0.487566f,0.0388476f}, +{0.154005f,-0.486242f,0.0387576f},{0.176743f,-0.48138f,0.0340954f},{0.194634f,-0.478551f,0.0351179f}, +{0.212723f,-0.475374f,0.0358574f},{0.22687f,-0.479457f,0.0424488f},{0.249918f,-0.484319f,0.0566349f}, +{0.26966f,-0.485065f,0.056159f},{0.286888f,-0.483457f,0.0581461f},{0.304309f,-0.481502f,0.0598823f}, +{0.315511f,-0.478956f,0.0598052f},{0.327999f,-0.474049f,0.0578567f},{0.344597f,-0.467297f,0.0551172f}, +{0.358268f,-0.462313f,0.0590335f},{0.366667f,-0.458294f,0.0572715f},{-0.402948f,-0.00846919f,0.222533f}, +{-0.262921f,-0.299225f,0.207421f},{-0.278419f,-0.492003f,-0.036031f},{-0.241172f,-0.49273f,-0.0190412f}, +{-0.22352f,-0.492139f,-0.0177743f},{-0.206788f,-0.491856f,-0.0170091f},{-0.186383f,-0.491251f,-0.0181473f}, +{-0.131356f,-0.46212f,-0.0335487f},{-0.0887399f,-0.460731f,-0.0191055f},{-0.0707727f,-0.460982f,-0.0164432f}, +{-0.0521559f,-0.460898f,-0.0187068f},{0.003823f,-0.460146f,0.000823143f},{0.0216231f,-0.459522f,0.000874589f}, +{0.0395389f,-0.458718f,0.000784559f},{0.0607344f,-0.45787f,0.00129258f},{0.101151f,-0.45832f,0.0177872f}, +{0.111183f,-0.461329f,0.014604f},{0.111813f,-0.474814f,0.0349442f},{0.118945f,-0.484544f,0.0412399f}, +{0.168789f,-0.475779f,0.0393492f},{0.234195f,-0.47585f,0.0525964f},{0.269107f,-0.48583f,0.0746986f}, +{0.288071f,-0.484242f,0.0758882f},{0.30537f,-0.482087f,0.079811f},{0.32416f,-0.47803f,0.0775281f}, +{0.344796f,-0.469792f,0.0765442f},{-0.388422f,-0.255927f,0.150561f},{-0.297801f,-0.492595f,-0.0188868f}, +{-0.28047f,-0.492601f,-0.0171313f},{-0.264644f,-0.492762f,-0.0178772f},{-0.200666f,-0.491412f,-0.000302223f}, +{-0.184525f,-0.491f,-0.00152405f},{-0.166377f,-0.490505f,0.00285523f},{-0.148854f,-0.486319f,-0.00328605f}, +{-0.125253f,-0.46084f,-0.0216392f},{-0.10872f,-0.460551f,-0.0185653f},{-0.0523552f,-0.46113f,-0.000713785f}, +{-0.0344008f,-0.460982f,0.00396774f},{-0.0170895f,-0.46048f,-0.000128595f},{0.045095f,-0.459136f,0.0183596f}, +{0.0618983f,-0.458763f,0.0190219f},{0.0790875f,-0.458423f,0.0222051f},{0.105955f,-0.462815f,0.0268223f}, +{0.118353f,-0.485785f,0.0573294f},{0.132205f,-0.488158f,0.0591107f},{0.148146f,-0.481541f,0.052352f}, +{0.158841f,-0.473869f,0.0475483f},{0.242536f,-0.476377f,0.0667246f},{0.251789f,-0.47801f,0.0780554f}, +{0.256014f,-0.481702f,0.0742999f},{0.361348f,-0.462094f,0.0778367f},{0.371265f,-0.4565f,0.0741841f}, +{0.383393f,-0.450474f,0.0784477f},{0.397643f,-0.443439f,0.0674384f},{-0.411135f,-0.00341467f,0.245548f}, +{-0.258638f,-0.492601f,-0.00105461f},{-0.240375f,-0.492293f,0.00120898f},{-0.222729f,-0.491901f,0.00151122f}, +{-0.147889f,-0.490621f,0.0184046f},{-0.120012f,-0.461149f,-0.00846916f},{-0.107961f,-0.461091f,-0.00315744f}, +{-0.0910678f,-0.460898f,0.00417352f},{-0.0740459f,-0.460847f,0.000559486f},{-0.0140221f,-0.46077f,0.0184882f}, +{0.00419598f,-0.460551f,0.0208032f},{0.0244911f,-0.460107f,0.0198257f},{0.080708f,-0.458995f,0.0389763f}, +{0.0930806f,-0.459606f,0.0371435f},{0.102206f,-0.467586f,0.0423202f},{0.0986688f,-0.470789f,0.0581653f}, +{0.13623f,-0.488184f,0.0748786f},{0.267995f,-0.483624f,0.0962734f},{0.286071f,-0.484467f,0.0948265f}, +{0.30382f,-0.481882f,0.0960741f},{0.314881f,-0.479592f,0.0962284f},{0.329105f,-0.474776f,0.0986657f}, +{0.344635f,-0.468905f,0.0984599f},{0.356056f,-0.463818f,0.0903444f},{0.364062f,-0.459651f,0.0960548f}, +{-0.314405f,-0.492216f,0.000662377f},{-0.297273f,-0.492743f,0.00171057f},{-0.279666f,-0.492441f,0.00209641f}, +{-0.222736f,-0.492036f,0.0190669f},{-0.205077f,-0.491598f,0.0193434f},{-0.187361f,-0.491077f,0.0195557f}, +{-0.166808f,-0.491367f,0.0210669f},{-0.133896f,-0.490949f,0.0273496f},{-0.105106f,-0.461857f,0.00686153f}, +{-0.07121f,-0.461323f,0.0179158f},{-0.0534227f,-0.461078f,0.0195557f},{-0.0327031f,-0.461072f,0.0208225f}, +{0.00517987f,-0.46059f,0.0368284f},{0.0227034f,-0.46023f,0.0383075f},{0.0410372f,-0.459786f,0.040436f}, +{0.0597762f,-0.459394f,0.0380245f},{0.107241f,-0.482544f,0.0744349f},{0.116154f,-0.486184f,0.0766213f}, +{0.25449f,-0.475644f,0.0960805f},{0.288759f,-0.482737f,0.110228f},{0.303235f,-0.480936f,0.106858f}, +{0.308109f,-0.477997f,0.113964f},{0.324732f,-0.473451f,0.110299f},{0.381599f,-0.451188f,0.0953989f}, +{0.396293f,-0.44423f,0.0976946f},{0.405579f,-0.439664f,0.0950002f},{-0.338539f,-0.48983f,0.000366566f}, +{-0.279531f,-0.492312f,0.0194142f},{-0.261969f,-0.492048f,0.0198386f},{-0.244446f,-0.491875f,0.0203273f}, +{-0.181894f,-0.491965f,0.0371692f},{-0.165181f,-0.491675f,0.0379602f},{-0.147992f,-0.491328f,0.0411305f}, +{-0.128456f,-0.491045f,0.0401788f},{-0.118411f,-0.490608f,0.0418379f},{-0.0931128f,-0.461921f,0.0210219f}, +{-0.0829137f,-0.461213f,0.0224623f},{-0.070091f,-0.461676f,0.0254783f},{-0.0473393f,-0.461297f,0.0336259f}, +{-0.0361178f,-0.461374f,0.0379216f},{-0.0264976f,-0.461721f,0.0401852f},{-0.0166458f,-0.460975f,0.0395614f}, +{0.0614803f,-0.459503f,0.0548536f},{0.0783222f,-0.459792f,0.0585255f},{0.096894f,-0.472789f,0.07911f}, +{0.118077f,-0.486344f,0.0929231f},{0.135517f,-0.48875f,0.0974695f},{0.250921f,-0.475547f,0.11089f}, +{0.268162f,-0.483329f,0.113617f},{0.345143f,-0.465252f,0.1168f},{0.354924f,-0.461966f,0.1119f}, +{0.365136f,-0.457162f,0.115501f},{0.3823f,-0.449715f,0.11208f},{-0.298662f,-0.297714f,0.189081f}, +{-0.377451f,-0.269091f,0.135025f},{-0.335613f,-0.491315f,0.0186875f},{-0.318752f,-0.49237f,0.0201859f}, +{-0.301428f,-0.492531f,0.0209575f},{-0.239938f,-0.491727f,0.0356066f},{-0.223681f,-0.491913f,0.0390856f}, +{-0.204672f,-0.492016f,0.0401338f},{-0.147626f,-0.489032f,0.0596766f},{-0.128346f,-0.490955f,0.0575802f}, +{-0.113202f,-0.490486f,0.0582875f},{-0.0844828f,-0.462133f,0.0346163f},{-0.0740201f,-0.4623f,0.0407897f}, +{-0.0580978f,-0.461741f,0.0396258f},{-0.0177004f,-0.461664f,0.0535996f},{-0.00942416f,-0.461098f,0.056712f}, +{0.00554642f,-0.460982f,0.0576895f},{0.0231343f,-0.46068f,0.0580753f},{0.0440918f,-0.46023f,0.0589949f}, +{0.0782129f,-0.46032f,0.0737854f},{0.0864827f,-0.462474f,0.0754703f},{0.105678f,-0.48147f,0.0965178f}, +{0.150828f,-0.487341f,0.112922f},{0.255172f,-0.481136f,0.12071f},{0.268432f,-0.482853f,0.130806f}, +{0.284476f,-0.48138f,0.126877f},{0.290823f,-0.47839f,0.133282f},{0.307112f,-0.474589f,0.132375f}, +{0.326578f,-0.469232f,0.129153f},{0.340249f,-0.464892f,0.129179f},{0.393482f,-0.443285f,0.114183f}, +{0.403733f,-0.438372f,0.117604f},{-0.293917f,-0.294312f,0.19991f},{-0.24359f,-0.287939f,0.232102f}, +{-0.352487f,-0.486274f,0.0193949f},{-0.296714f,-0.49201f,0.0359088f},{-0.280348f,-0.491984f,0.039227f}, +{-0.261236f,-0.491907f,0.0401466f},{-0.205444f,-0.488518f,0.0571493f},{-0.186203f,-0.487148f,0.0540176f}, +{-0.166435f,-0.486878f,0.0577859f},{-0.108617f,-0.490447f,0.0739076f},{-0.0683805f,-0.462699f,0.0558889f}, +{-0.0517765f,-0.462223f,0.0588341f},{-0.0363622f,-0.461889f,0.055831f},{-0.0263046f,-0.461445f,0.0574837f}, +{0.0231086f,-0.460795f,0.075676f},{0.04069f,-0.460532f,0.0761005f},{0.0583614f,-0.460101f,0.0763898f}, +{0.0958972f,-0.471734f,0.0962542f},{0.117408f,-0.484872f,0.114479f},{0.135992f,-0.487939f,0.114344f}, +{0.24377f,-0.476043f,0.125482f},{0.252632f,-0.483894f,0.134382f},{0.348654f,-0.459426f,0.137687f}, +{0.363053f,-0.45412f,0.134652f},{0.381997f,-0.44605f,0.132388f},{0.401579f,-0.43623f,0.133063f}, +{-0.352249f,-0.485689f,0.0385068f},{-0.336179f,-0.490563f,0.0381017f},{-0.317935f,-0.491489f,0.0414971f}, +{-0.260194f,-0.491901f,0.0559918f},{-0.242291f,-0.492235f,0.0587827f},{-0.223681f,-0.491283f,0.0605511f}, +{-0.142507f,-0.487026f,0.0685895f},{-0.129999f,-0.486968f,0.0777146f},{-0.0948298f,-0.48956f,0.0805505f}, +{-0.0349732f,-0.462262f,0.0750973f},{-0.0164529f,-0.461857f,0.0784991f},{-0.0175332f,-0.461394f,0.0708723f}, +{0.00209959f,-0.461381f,0.0748401f},{0.0640912f,-0.460448f,0.0936112f},{0.0788045f,-0.460641f,0.0956111f}, +{0.0865277f,-0.462455f,0.0929745f},{0.0987974f,-0.471065f,0.114922f},{0.136751f,-0.486602f,0.130787f}, +{0.154899f,-0.486119f,0.135205f},{0.170982f,-0.483753f,0.135179f},{0.230079f,-0.475168f,0.13361f}, +{0.233385f,-0.481219f,0.139745f},{0.23325f,-0.484184f,0.150188f},{0.249886f,-0.482955f,0.150516f}, +{0.263995f,-0.481033f,0.151674f},{0.273769f,-0.477727f,0.152632f},{0.289132f,-0.47401f,0.149275f}, +{0.308122f,-0.468699f,0.15069f},{0.326147f,-0.464795f,0.146735f},{0.343587f,-0.456024f,0.155088f}, +{0.416781f,-0.428198f,0.134568f},{0.426806f,-0.422732f,0.130735f},{-0.373091f,-0.224449f,0.226289f}, +{-0.315472f,-0.490749f,0.0568856f},{-0.298354f,-0.49129f,0.058011f},{-0.281107f,-0.491585f,0.0589306f}, +{-0.194756f,-0.478872f,0.0621974f},{-0.184139f,-0.477367f,0.0630012f},{-0.170737f,-0.476776f,0.0651941f}, +{-0.161541f,-0.477914f,0.0680943f},{-0.146069f,-0.477573f,0.0755796f},{-0.109678f,-0.489811f,0.0959969f}, +{-0.0912672f,-0.489155f,0.0983313f},{-0.0687599f,-0.463483f,0.0739848f},{-0.0555513f,-0.463246f,0.076467f}, +{-0.00233756f,-0.461567f,0.0857722f},{0.00869102f,-0.461381f,0.093669f},{0.0229478f,-0.461175f,0.0934825f}, +{0.0410951f,-0.460885f,0.0969101f},{0.0883026f,-0.463213f,0.105714f},{0.11344f,-0.475991f,0.135925f}, +{0.122109f,-0.482808f,0.134105f},{0.178042f,-0.484891f,0.15222f},{0.193663f,-0.485393f,0.151783f}, +{0.210858f,-0.485039f,0.154966f},{0.331362f,-0.461181f,0.153352f},{0.363818f,-0.448603f,0.153333f}, +{0.38291f,-0.440153f,0.150812f},{0.400138f,-0.430475f,0.153699f},{-0.428459f,0.0276519f,0.314106f}, +{-0.412016f,-0.00104821f,0.266345f},{-0.337407f,-0.489116f,0.0595544f},{-0.278824f,-0.491193f,0.0745893f}, +{-0.260316f,-0.491592f,0.0776052f},{-0.244568f,-0.491978f,0.0792708f},{-0.222562f,-0.486126f,0.0746729f}, +{-0.204357f,-0.476557f,0.0697534f},{-0.133214f,-0.478042f,0.0855921f},{-0.126983f,-0.477309f,0.0939198f}, +{-0.120919f,-0.481534f,0.0970387f},{-0.0656217f,-0.463863f,0.0924022f},{-0.0524196f,-0.463619f,0.094878f}, +{-0.033687f,-0.46241f,0.0964406f},{-0.0134626f,-0.462062f,0.0955982f},{-0.000260458f,-0.461818f,0.0980676f}, +{0.0259766f,-0.461683f,0.103296f},{0.0439118f,-0.460802f,0.111971f},{0.0603614f,-0.460583f,0.115135f}, +{0.0776984f,-0.460763f,0.115906f},{0.100746f,-0.465863f,0.131996f},{0.138301f,-0.483026f,0.15004f}, +{0.154397f,-0.484685f,0.154805f},{0.213385f,-0.484042f,0.170323f},{0.23134f,-0.483071f,0.170194f}, +{0.247146f,-0.481367f,0.169937f},{0.256587f,-0.478673f,0.17139f},{0.269403f,-0.473072f,0.168972f}, +{0.286984f,-0.466371f,0.170329f},{0.305608f,-0.464628f,0.162876f},{0.324809f,-0.454744f,0.171068f}, +{0.41134f,-0.426051f,0.149371f},{0.422993f,-0.418765f,0.150793f},{-0.31908f,-0.48965f,0.0752645f}, +{-0.301376f,-0.490338f,0.0783705f},{-0.240169f,-0.491553f,0.0943635f},{-0.229887f,-0.491746f,0.0897528f}, +{-0.111742f,-0.488872f,0.114209f},{-0.0909585f,-0.488711f,0.115405f},{-0.0327417f,-0.46257f,0.112479f}, +{-0.0152182f,-0.462204f,0.113977f},{0.00272336f,-0.461792f,0.11417f},{0.00360437f,-0.461529f,0.117983f}, +{0.0219253f,-0.461175f,0.113656f},{0.0347416f,-0.461631f,0.116684f},{0.0799299f,-0.460345f,0.131706f}, +{0.119517f,-0.469618f,0.152793f},{0.131034f,-0.475798f,0.156812f},{0.159869f,-0.482943f,0.169107f}, +{0.17403f,-0.484325f,0.171294f},{0.191412f,-0.484415f,0.172001f},{0.306006f,-0.458043f,0.174535f}, +{0.343889f,-0.447998f,0.173319f},{0.361008f,-0.444462f,0.167795f},{0.383148f,-0.432192f,0.169126f}, +{-0.221604f,-0.246654f,0.267053f},{-0.212086f,-0.27158f,0.254243f},{-0.347523f,-0.483804f,0.0699656f}, +{-0.335864f,-0.485766f,0.0786727f},{-0.295048f,-0.489753f,0.0946272f},{-0.2782f,-0.490775f,0.0961513f}, +{-0.259956f,-0.491187f,0.09455f},{-0.121491f,-0.476673f,0.109926f},{-0.119041f,-0.484312f,0.117276f}, +{-0.109614f,-0.488743f,0.130144f},{-0.0914151f,-0.486479f,0.131835f},{-0.0691071f,-0.469805f,0.118343f}, +{-0.0545481f,-0.463676f,0.117726f},{0.0204205f,-0.461355f,0.129018f},{0.0282659f,-0.461664f,0.132832f}, +{0.0416546f,-0.461066f,0.135025f},{0.0579113f,-0.460545f,0.135931f},{0.0933378f,-0.46014f,0.138928f}, +{0.107672f,-0.463901f,0.145211f},{0.138217f,-0.469721f,0.169634f},{0.154571f,-0.477014f,0.175306f}, +{0.19381f,-0.483689f,0.187557f},{0.21271f,-0.48338f,0.189968f},{0.231385f,-0.481907f,0.193621f}, +{0.242246f,-0.481103f,0.188142f},{0.25141f,-0.474898f,0.192212f},{0.269094f,-0.46702f,0.185602f}, +{0.293344f,-0.459561f,0.179094f},{0.362461f,-0.439735f,0.175557f},{0.396074f,-0.425543f,0.169737f}, +{0.399997f,-0.420784f,0.174039f},{-0.430259f,0.00304813f,0.26468f},{-0.316598f,-0.265516f,0.229362f}, +{-0.327922f,-0.484139f,0.0914633f},{-0.316308f,-0.486891f,0.0962863f},{-0.259879f,-0.491309f,0.111868f}, +{-0.243944f,-0.492023f,0.115803f},{-0.121466f,-0.482885f,0.132896f},{-0.0500402f,-0.463522f,0.133031f}, +{-0.0322401f,-0.462815f,0.133121f},{-0.0136877f,-0.461934f,0.134967f},{0.00335356f,-0.4614f,0.13332f}, +{0.0129867f,-0.461741f,0.135603f},{0.0622906f,-0.460062f,0.151121f},{0.080663f,-0.459496f,0.153217f}, +{0.0981094f,-0.459451f,0.153834f},{0.111459f,-0.460969f,0.155018f},{0.171084f,-0.474621f,0.19274f}, +{0.175554f,-0.481579f,0.186605f},{0.286907f,-0.455587f,0.193036f},{0.306296f,-0.449124f,0.1871f}, +{0.32598f,-0.444892f,0.184393f},{0.344127f,-0.435838f,0.190219f},{0.362062f,-0.430269f,0.190386f}, +{0.375483f,-0.42753f,0.184849f},{-0.33396f,0.239008f,-0.169165f},{-0.281917f,-0.48992f,0.114646f}, +{-0.242105f,-0.492582f,0.133816f},{-0.124565f,-0.478551f,0.13415f},{-0.109177f,-0.488544f,0.15204f}, +{-0.092579f,-0.485328f,0.150625f},{-0.0680782f,-0.46468f,0.133256f},{-0.00444683f,-0.461831f,0.143732f}, +{0.0050577f,-0.461207f,0.152079f},{0.0233336f,-0.460853f,0.154329f},{0.0421433f,-0.460275f,0.151847f}, +{0.0987396f,-0.45839f,0.170458f},{0.117716f,-0.458667f,0.170856f},{0.155477f,-0.467522f,0.186187f}, +{0.194383f,-0.483161f,0.209299f},{0.211906f,-0.482981f,0.209807f},{0.230086f,-0.48156f,0.209357f}, +{0.24867f,-0.473457f,0.211678f},{0.260477f,-0.467548f,0.20301f},{0.310321f,-0.438082f,0.194566f}, +{0.386248f,-0.417376f,0.189582f},{-0.335459f,0.246854f,-0.208836f},{-0.402743f,-0.236642f,0.149744f}, +{-0.313723f,-0.482608f,0.110054f},{-0.279338f,-0.489071f,0.129816f},{-0.261487f,-0.490685f,0.133816f}, +{-0.121517f,-0.483174f,0.150477f},{-0.069718f,-0.465033f,0.149481f},{-0.0525546f,-0.463104f,0.153577f}, +{-0.0356162f,-0.462377f,0.154079f},{-0.0186843f,-0.462043f,0.152625f},{-0.00848528f,-0.461329f,0.154066f}, +{0.0423491f,-0.460011f,0.169075f},{0.0605543f,-0.459387f,0.173403f},{0.0767017f,-0.458905f,0.172213f}, +{0.1307f,-0.457689f,0.179287f},{0.136205f,-0.457239f,0.186984f},{0.151439f,-0.458705f,0.193138f}, +{0.173747f,-0.470255f,0.209022f},{0.184351f,-0.479573f,0.209839f},{0.211758f,-0.483374f,0.227562f}, +{0.2268f,-0.481708f,0.226256f},{0.23707f,-0.47875f,0.219896f},{0.37212f,-0.421279f,0.196154f}, +{-0.323961f,-0.233658f,0.25414f},{-0.285865f,-0.251638f,0.255098f},{-0.468824f,0.0225716f,0.22699f}, +{-0.299254f,-0.277387f,0.224662f},{-0.241147f,-0.490351f,0.150297f},{-0.230549f,-0.492048f,0.152844f}, +{-0.127626f,-0.480448f,0.158349f},{-0.123774f,-0.487624f,0.171255f},{-0.108836f,-0.486975f,0.174946f}, +{-0.0972541f,-0.48475f,0.168445f},{-0.0860262f,-0.472049f,0.170927f},{-0.0317193f,-0.462127f,0.16739f}, +{-0.0195074f,-0.461664f,0.163686f},{-0.0138999f,-0.461001f,0.171197f},{0.00345646f,-0.460332f,0.174149f}, +{0.020472f,-0.460217f,0.170593f},{0.0814025f,-0.458352f,0.187203f},{0.0982058f,-0.457516f,0.189885f}, +{0.116713f,-0.456294f,0.193749f},{0.163426f,-0.461734f,0.201215f},{0.172673f,-0.473039f,0.224829f}, +{0.190962f,-0.483579f,0.226391f},{0.3207f,-0.423974f,0.199479f},{-0.350211f,-0.23589f,0.23906f}, +{-0.372802f,-0.0054532f,0.268416f},{-0.354828f,-0.243812f,0.226546f},{-0.445172f,0.00870071f,0.23097f}, +{-0.465107f,0.026012f,0.20773f},{-0.383052f,-0.00770392f,0.261021f},{-0.278547f,-0.488486f,0.151063f}, +{-0.261725f,-0.489521f,0.152651f},{-0.138005f,-0.479644f,0.167017f},{-0.131626f,-0.47994f,0.168599f}, +{-0.0711907f,-0.463959f,0.171358f},{-0.0532813f,-0.462229f,0.17348f},{-0.0302338f,-0.461464f,0.172908f}, +{0.0242275f,-0.459458f,0.188058f},{0.0425677f,-0.458963f,0.190219f},{0.0601042f,-0.458538f,0.191704f}, +{0.137774f,-0.454886f,0.20726f},{0.15459f,-0.455985f,0.208778f},{0.166075f,-0.460931f,0.212662f}, +{0.177631f,-0.48212f,0.233947f},{0.193913f,-0.484312f,0.24506f},{0.212652f,-0.483058f,0.246622f}, +{0.225128f,-0.480923f,0.248449f},{0.233648f,-0.477187f,0.246847f},{-0.355857f,-0.28183f,0.153378f}, +{-0.389238f,-0.260448f,0.134658f},{-0.29737f,-0.260757f,0.243446f},{-0.366872f,-0.275046f,0.145224f}, +{-0.296643f,-0.483894f,0.154587f},{-0.259275f,-0.488955f,0.167995f},{-0.24215f,-0.488672f,0.171326f}, +{-0.226247f,-0.489778f,0.173358f},{-0.173753f,-0.489907f,0.179936f},{-0.164159f,-0.489193f,0.188155f}, +{-0.146957f,-0.487881f,0.188341f},{-0.128861f,-0.487406f,0.192856f},{-0.109312f,-0.485245f,0.189383f}, +{-0.0912736f,-0.46994f,0.193383f},{-0.0498216f,-0.461586f,0.186348f},{-0.0412624f,-0.461799f,0.182515f}, +{-0.0314492f,-0.460648f,0.190425f},{-0.0145751f,-0.460024f,0.191029f},{0.00553356f,-0.459869f,0.190386f}, +{0.0617761f,-0.457394f,0.206656f},{0.0800842f,-0.456963f,0.208874f},{0.0986302f,-0.456082f,0.210733f}, +{0.119144f,-0.455124f,0.209453f},{0.156075f,-0.45668f,0.222276f},{0.1574f,-0.484126f,0.250918f}, +{0.175335f,-0.48446f,0.247175f},{-0.319626f,-0.20782f,0.27165f},{-0.329877f,-0.29554f,0.165345f}, +{-0.318694f,-0.29873f,0.170175f},{-0.451796f,0.0128677f,0.223247f},{-0.303878f,-0.310151f,0.0028488f}, +{-0.280785f,-0.321527f,0.0187711f},{-0.278489f,-0.302846f,0.191081f},{-0.360262f,-0.250693f,0.212096f}, +{-0.298862f,-0.485206f,0.169969f},{-0.280046f,-0.487785f,0.172785f},{-0.223237f,-0.487393f,0.190052f}, +{-0.205553f,-0.488795f,0.189325f},{-0.185952f,-0.488698f,0.193376f},{-0.113517f,-0.484608f,0.203151f}, +{-0.0732742f,-0.462783f,0.191261f},{-0.0518279f,-0.461342f,0.191479f},{0.00290986f,-0.458802f,0.206257f}, +{0.0227742f,-0.458345f,0.209923f},{0.0404006f,-0.457934f,0.210283f},{0.116302f,-0.454487f,0.225697f}, +{0.13398f,-0.453992f,0.225973f},{0.142211f,-0.48338f,0.250995f},{0.15612f,-0.484962f,0.26394f}, +{0.17347f,-0.484872f,0.265689f},{0.192113f,-0.483811f,0.267413f},{0.207804f,-0.482261f,0.267342f}, +{0.217199f,-0.479657f,0.268879f},{-0.414048f,-0.00264297f,0.20955f},{-0.455635f,0.0113823f,0.240758f}, +{-0.401682f,-0.00607055f,0.25549f},{-0.316006f,-0.307752f,0.0176329f},{-0.299061f,-0.317032f,0.0229575f}, +{-0.281904f,-0.324832f,0.0377994f},{-0.261352f,-0.487695f,0.187943f},{-0.241513f,-0.487174f,0.191672f}, +{-0.165979f,-0.486139f,0.206688f},{-0.146667f,-0.486634f,0.211189f},{-0.128224f,-0.486563f,0.209337f}, +{-0.106476f,-0.478358f,0.212508f},{-0.0748819f,-0.461889f,0.204566f},{-0.0675123f,-0.461014f,0.207228f}, +{-0.052889f,-0.460262f,0.208797f},{-0.0343944f,-0.459464f,0.210733f},{-0.0158484f,-0.45931f,0.208643f}, +{0.0431722f,-0.456564f,0.225253f},{0.0604064f,-0.456101f,0.228379f},{0.0771647f,-0.45569f,0.22915f}, +{0.0974213f,-0.455252f,0.228295f},{0.117942f,-0.484197f,0.266962f},{0.133774f,-0.484261f,0.266236f}, +{0.319678f,-0.462101f,0.0100833f},{-0.443185f,0.00970388f,0.279348f},{-0.382152f,-0.251735f,0.178387f}, +{-0.313729f,-0.311791f,0.0269573f},{-0.312694f,-0.315971f,0.0396258f},{-0.29856f,-0.32015f,0.0378252f}, +{-0.282194f,-0.328124f,0.0543841f},{-0.275146f,-0.331256f,0.0615222f},{-0.298875f,-0.486126f,0.189126f}, +{-0.282123f,-0.487528f,0.192798f},{-0.220871f,-0.486287f,0.207678f},{-0.202588f,-0.485798f,0.20998f}, +{-0.184055f,-0.485541f,0.208038f},{-0.129433f,-0.485984f,0.226739f},{-0.117099f,-0.484383f,0.226829f}, +{-0.0914215f,-0.464364f,0.209132f},{-0.0150767f,-0.458095f,0.225035f},{0.0032764f,-0.457169f,0.22915f}, +{0.0200283f,-0.456757f,0.229909f},{0.0820456f,-0.4543f,0.243561f},{0.100437f,-0.453754f,0.239143f}, +{0.101254f,-0.48284f,0.26522f},{0.139619f,-0.48448f,0.283238f},{0.155567f,-0.484357f,0.282377f}, +{0.174236f,-0.482827f,0.286017f},{0.19356f,-0.481251f,0.28282f},{0.213694f,-0.473342f,0.284544f}, +{-0.242465f,-0.257683f,0.258429f},{-0.335754f,-0.305109f,0.0369313f},{-0.32342f,-0.309578f,0.0344619f}, +{-0.300225f,-0.323256f,0.0579403f},{-0.311357f,-0.484068f,0.191132f},{-0.277576f,-0.487013f,0.207569f}, +{-0.25948f,-0.486852f,0.210135f},{-0.241854f,-0.486299f,0.210591f},{-0.185361f,-0.485168f,0.225523f}, +{-0.167522f,-0.485534f,0.228469f},{-0.149304f,-0.485862f,0.22697f},{-0.100997f,-0.468261f,0.221974f}, +{-0.0878911f,-0.461992f,0.219286f},{-0.0706183f,-0.459451f,0.225189f},{-0.0522459f,-0.4585f,0.229279f}, +{-0.0360921f,-0.45814f,0.230366f},{0.0267998f,-0.455291f,0.245587f},{0.0428314f,-0.45495f,0.244558f}, +{0.0601749f,-0.454275f,0.24751f},{0.0805086f,-0.483103f,0.272724f},{0.100457f,-0.484537f,0.283888f}, +{0.11845f,-0.48464f,0.286525f},{0.194846f,-0.476898f,0.290769f},{-0.349381f,-0.00533103f,0.254024f}, +{-0.417945f,0.0292209f,0.315366f},{-0.35014f,-0.296383f,0.0380181f},{-0.328983f,-0.311906f,0.0467638f}, +{-0.319491f,-0.316652f,0.0564419f},{-0.299132f,-0.326459f,0.075496f},{-0.28065f,-0.331513f,0.0751037f}, +{-0.259783f,-0.338375f,0.0941963f},{-0.314077f,-0.483554f,0.206939f},{-0.300154f,-0.486422f,0.211247f}, +{-0.241365f,-0.485901f,0.227195f},{-0.223919f,-0.485676f,0.227915f},{-0.206376f,-0.48529f,0.228481f}, +{-0.145735f,-0.485734f,0.244552f},{-0.127813f,-0.485907f,0.247388f},{-0.113614f,-0.485888f,0.249619f}, +{-0.0850616f,-0.460905f,0.230385f},{-0.0312048f,-0.45677f,0.244764f},{-0.0144465f,-0.456352f,0.245529f}, +{0.00379085f,-0.455651f,0.249831f},{0.0451786f,-0.482885f,0.284441f},{0.0596797f,-0.483939f,0.285316f}, +{0.0820391f,-0.484435f,0.285759f},{0.135118f,-0.482891f,0.295347f},{0.152448f,-0.480988f,0.297219f}, +{0.155619f,-0.473052f,0.30219f},{0.175303f,-0.470782f,0.298473f},{-0.256394f,-0.259394f,0.256828f}, +{-0.341291f,-0.307823f,0.0568599f},{-0.331067f,-0.314022f,0.0603389f},{-0.315787f,-0.320806f,0.0768979f}, +{-0.290624f,-0.329906f,0.0832063f},{-0.278303f,-0.333411f,0.0946529f},{-0.297794f,-0.486184f,0.226649f}, +{-0.280695f,-0.486621f,0.22787f},{-0.263139f,-0.486177f,0.228424f},{-0.201752f,-0.485181f,0.243426f}, +{-0.185348f,-0.484968f,0.246744f},{-0.16666f,-0.485489f,0.248352f},{-0.109061f,-0.485309f,0.264403f}, +{-0.0923604f,-0.485817f,0.270088f},{-0.0695573f,-0.45857f,0.245883f},{-0.0523038f,-0.456828f,0.247941f}, +{0.0218867f,-0.454416f,0.258217f},{0.0355583f,-0.453278f,0.26003f},{0.0248191f,-0.483174f,0.288042f}, +{0.0613839f,-0.48466f,0.301817f},{0.0791196f,-0.483997f,0.302061f},{0.0968168f,-0.483444f,0.302344f}, +{0.118443f,-0.481592f,0.302299f},{0.136578f,-0.474036f,0.304775f},{-0.285788f,-0.286988f,0.217832f}, +{-0.361201f,-0.288962f,0.044198f},{-0.353516f,-0.299328f,0.0592907f},{-0.334938f,-0.313469f,0.075631f}, +{-0.300444f,-0.327224f,0.0950195f},{-0.289094f,-0.33123f,0.0962799f},{-0.26431f,-0.335848f,0.111283f}, +{-0.312752f,-0.483721f,0.229716f},{-0.258432f,-0.485978f,0.243246f},{-0.242124f,-0.485926f,0.24668f}, +{-0.223096f,-0.4852f,0.249008f},{-0.165155f,-0.484962f,0.263406f},{-0.147092f,-0.48484f,0.26603f}, +{-0.129935f,-0.485161f,0.267162f},{-0.0487541f,-0.456423f,0.25695f},{-0.0353011f,-0.455644f,0.259033f}, +{-0.0153146f,-0.454911f,0.262506f},{0.00496123f,-0.453651f,0.265573f},{0.00542424f,-0.483746f,0.291965f}, +{0.0242018f,-0.484139f,0.303103f},{0.0405871f,-0.484306f,0.304479f},{0.101897f,-0.480113f,0.309109f}, +{-0.335484f,0.250577f,-0.225986f},{-0.362898f,-0.290209f,0.0564741f},{-0.354339f,-0.30028f,0.0768464f}, +{-0.334616f,-0.314601f,0.0951159f},{-0.31888f,-0.321205f,0.0966464f},{-0.298135f,-0.325674f,0.114292f}, +{-0.278232f,-0.331443f,0.114922f},{-0.298148f,-0.485489f,0.24551f},{-0.279885f,-0.486229f,0.248931f}, +{-0.220935f,-0.485341f,0.264673f},{-0.203469f,-0.485058f,0.265387f},{-0.185444f,-0.485007f,0.268075f}, +{-0.126944f,-0.483637f,0.281663f},{-0.108611f,-0.48428f,0.285001f},{-0.0926562f,-0.484164f,0.286441f}, +{-0.0740266f,-0.484402f,0.286891f},{-0.0540337f,-0.483515f,0.290383f},{-0.0409344f,-0.48338f,0.293058f}, +{-0.0318029f,-0.483586f,0.301965f},{-0.0139385f,-0.483849f,0.304878f},{0.00431173f,-0.484074f,0.303334f}, +{0.0427028f,-0.482505f,0.318678f},{0.0602649f,-0.482962f,0.314331f},{0.0708948f,-0.481316f,0.315218f}, +{0.0968618f,-0.47529f,0.315025f},{-0.306276f,-0.2129f,0.274448f},{-0.368274f,-0.235009f,0.223163f}, +{-0.343388f,-0.30862f,0.0938362f},{-0.316758f,-0.31979f,0.114929f},{-0.283467f,-0.329102f,0.11871f}, +{-0.263911f,-0.331243f,0.130414f},{-0.311948f,-0.482968f,0.245992f},{-0.279628f,-0.485631f,0.266242f}, +{-0.260149f,-0.485907f,0.265136f},{-0.242015f,-0.48565f,0.267689f},{-0.183419f,-0.483496f,0.282338f}, +{-0.167593f,-0.48246f,0.280197f},{-0.148552f,-0.481676f,0.279997f},{-0.0880133f,-0.48347f,0.301084f}, +{-0.0700074f,-0.483457f,0.30381f},{-0.0527539f,-0.483599f,0.304813f},{0.00475545f,-0.483721f,0.320067f}, +{0.0216552f,-0.482628f,0.322813f},{-0.315678f,-0.293425f,0.183177f},{-0.3545f,-0.298074f,0.0959712f}, +{-0.331002f,-0.314266f,0.112832f},{-0.299164f,-0.320819f,0.132941f},{-0.279917f,-0.325578f,0.134388f}, +{-0.309144f,-0.482165f,0.257034f},{-0.299048f,-0.483946f,0.266159f},{-0.240542f,-0.485373f,0.28266f}, +{-0.223237f,-0.485193f,0.284589f},{-0.204627f,-0.483972f,0.28646f},{-0.123247f,-0.481599f,0.296454f}, +{-0.107987f,-0.482968f,0.305257f},{-0.0522073f,-0.483193f,0.321269f},{-0.0351532f,-0.483669f,0.322601f}, +{-0.0178998f,-0.483727f,0.323604f},{0.0305231f,-0.480197f,0.328266f},{-0.328674f,-0.273869f,0.208707f}, +{-0.40064f,-0.25104f,0.0488023f},{-0.362519f,-0.289849f,0.0907431f},{-0.341458f,-0.307572f,0.116331f}, +{-0.455841f,0.0126491f,0.268384f},{-0.279165f,-0.484795f,0.283148f},{-0.261506f,-0.485431f,0.286383f}, +{-0.184827f,-0.479534f,0.292415f},{-0.117035f,-0.480132f,0.307701f},{-0.0909135f,-0.482885f,0.320941f}, +{-0.0729848f,-0.482994f,0.32379f},{-0.0158548f,-0.482525f,0.33678f},{0.00325711f,-0.481226f,0.334362f}, +{-0.38266f,-0.270782f,0.0813672f},{-0.332115f,-0.310569f,0.126498f},{-0.307389f,-0.476396f,0.27412f}, +{-0.294328f,-0.482017f,0.281894f},{-0.222954f,-0.482865f,0.298061f},{-0.101678f,-0.480898f,0.322433f}, +{-0.186711f,0.248082f,-0.0135751f},{-0.337054f,0.254307f,-0.24396f},{-0.202891f,0.23162f,0.00283594f}, +{-0.286515f,0.364336f,-0.372927f},{-0.335414f,0.260551f,-0.265149f},{-0.052098f,0.307945f,-0.0569499f}, +{-0.164152f,0.26057f,-0.024578f},{-0.305337f,0.251098f,-0.166741f},{-0.313472f,0.285663f,-0.283026f}, +{-0.302617f,0.215832f,-0.0542812f},{-0.316019f,0.243555f,-0.169171f},{-0.300804f,0.227684f,-0.0737082f}, +{-0.181181f,0.274711f,-0.031671f},{-0.0531462f,0.322594f,-0.0754188f},{-0.312958f,0.259509f,-0.210025f}, +{-0.302566f,0.23205f,-0.0907302f},{0.0233979f,0.356484f,-0.115662f},{-0.316771f,0.22252f,-0.0919199f}, +{0.00642742f,0.340221f,-0.0927558f},{-0.319633f,0.270544f,-0.262069f},{-0.31789f,0.215247f,-0.0716247f}, +{-0.182493f,0.241632f,-0.00751742f},{-0.145638f,0.259972f,-0.0193563f},{-0.318405f,0.226957f,-0.111032f}, +{-0.165168f,0.271027f,-0.0328992f},{-0.109562f,0.2732f,-0.0165396f},{-0.107003f,0.281837f,-0.0365711f}, +{-0.323716f,0.274055f,-0.296479f},{-0.16659f,0.243259f,0.0006045f},{-0.319781f,0.265651f,-0.243709f}, +{-0.148924f,0.270326f,-0.0333044f},{-0.0362722f,0.324607f,-0.0756438f},{-0.311234f,0.220604f,-0.0803125f}, +{-0.3227f,0.271239f,-0.284229f},{-0.323588f,0.256815f,-0.22843f},{-0.220781f,0.206829f,0.0185396f}, +{-0.167914f,0.251284f,-0.0151699f},{-0.0329861f,0.310312f,-0.0527443f},{-0.127337f,0.273367f,-0.0322948f}, +{-0.317382f,0.284158f,-0.302556f},{-0.127871f,0.280666f,-0.0416578f},{-0.187341f,0.234301f,0.00378125f}, +{0.0259895f,0.366509f,-0.128723f},{0.0123822f,0.333057f,-0.0819716f},{-0.313787f,0.265008f,-0.224777f}, +{-0.309061f,0.301977f,-0.319835f},{-0.303871f,0.269368f,-0.2069f},{-0.0717501f,0.30253f,-0.055368f}, +{-0.333034f,0.229555f,-0.136619f},{-0.316327f,0.232745f,-0.128478f},{-0.338886f,0.200733f,-0.0441529f}, +{0.00223463f,0.350227f,-0.103682f},{0.025295f,0.350658f,-0.108054f},{-0.0903219f,0.299785f,-0.0571364f}, +{-0.209681f,0.219363f,0.0212341f},{-0.337054f,0.234758f,-0.152272f},{-0.301788f,0.250449f,-0.149892f}, +{-0.313421f,0.313733f,-0.349333f},{-0.353355f,0.231812f,-0.127121f},{-0.0154754f,0.325109f,-0.0739269f}, +{-0.312546f,0.202521f,-0.0423137f},{-0.109736f,0.29165f,-0.0513167f},{-0.0731263f,0.292106f,-0.0378766f}, +{-0.305575f,0.235472f,-0.111611f},{-0.311228f,0.241394f,-0.150471f},{-0.305884f,0.31271f,-0.343847f}, +{-0.336295f,0.224366f,-0.114556f},{-0.0516286f,0.299514f,-0.0367383f},{-0.343401f,0.237722f,-0.161197f}, +{-0.0322787f,0.314794f,-0.0613163f},{-0.321163f,0.251079f,-0.204977f},{-0.309453f,0.279605f,-0.247323f}, +{-0.316295f,0.249214f,-0.188206f},{-0.0175589f,0.332697f,-0.0845503f},{-0.305434f,0.309495f,-0.323842f}, +{-0.21671f,0.219761f,0.014752f},{-0.306656f,0.273541f,-0.222578f},{-0.334147f,0.213974f,-0.0735667f}, +{-0.173252f,0.237356f,0.018829f},{-0.202177f,0.223048f,0.0189447f},{-0.311614f,0.284152f,-0.265889f}, +{-0.332655f,0.221189f,-0.0998553f},{-0.0605351f,0.294479f,-0.0306485f},{-0.305781f,0.256326f,-0.181872f}, +{-0.0967461f,0.291045f,-0.0472396f} +}; +F32 normals [8127][3] = { +{-0.832532f,-0.483226f,0.270894f},{-0.606785f,-0.758191f,0.23866f},{-0.859166f,-0.504979f,0.0826439f}, +{-0.240595f,0.95358f,-0.181105f},{-0.265911f,0.923509f,-0.276446f},{-0.24535f,0.952364f,-0.181124f}, +{-0.25194f,0.962547f,-0.100147f},{-0.246923f,0.963593f,-0.102551f},{-0.246718f,0.967484f,-0.0557298f}, +{-0.256239f,0.966543f,-0.0116836f},{-0.268399f,0.963145f,0.0176902f},{-0.202139f,0.974793f,-0.094442f}, +{-0.233286f,0.967463f,-0.0979436f},{-0.222973f,0.972356f,-0.0693283f},{-0.216571f,0.976026f,-0.0216751f}, +{-0.248769f,0.966837f,-0.0577895f},{-0.251386f,0.966483f,-0.0521194f},{-0.221256f,0.940455f,-0.258053f}, +{-0.203433f,0.958112f,-0.201585f},{-0.185273f,0.9538f,-0.236513f},{-0.202063f,0.969567f,-0.138239f}, +{-0.277194f,0.958376f,0.0684025f},{-0.323528f,0.932333f,0.16151f},{-0.301745f,0.944805f,0.127648f}, +{-0.263034f,0.964572f,0.0203178f},{-0.298731f,0.932268f,0.204049f},{-0.339448f,0.915328f,0.21668f}, +{-0.256592f,0.921503f,0.291534f},{-0.336811f,0.892243f,0.300767f},{-0.288256f,0.886991f,0.36077f}, +{-0.129143f,0.959514f,-0.25031f},{-0.104011f,0.980522f,-0.166606f},{-0.0715617f,0.949967f,-0.304043f}, +{-0.193212f,0.979873f,0.0501861f},{-0.264442f,0.963268f,0.0467423f},{-0.202161f,0.973002f,0.111346f}, +{0.780632f,0.608123f,0.144219f},{0.893498f,0.412557f,0.177367f},{0.918654f,0.348847f,0.185421f}, +{-0.235108f,0.898248f,0.371314f},{-0.0326766f,0.912646f,0.407443f},{-0.281134f,0.890492f,0.357753f}, +{-0.717587f,-0.696125f,-0.0218705f},{-0.848457f,-0.528571f,-0.0270921f},{-0.865329f,-0.500314f,-0.029859f}, +{-0.121792f,0.898055f,0.422685f},{0.101699f,0.854369f,0.509619f},{-0.140896f,0.896477f,0.420093f}, +{-0.808979f,-0.581233f,0.0878659f},{-0.770083f,-0.637941f,0.00173141f},{-0.756549f,-0.652533f,0.0428222f}, +{0.0971026f,0.799445f,0.592839f},{-0.145356f,0.83366f,0.532806f},{-0.00267177f,0.775686f,0.631113f}, +{-4.50738e-05f,-0.710945f,-0.703248f},{-0.0235271f,-0.794254f,-0.60713f},{0.259285f,-0.884284f,-0.388348f}, +{-0.945709f,0.157642f,0.284225f},{-0.904527f,0.212372f,0.36977f},{-0.885759f,0.334308f,0.321977f}, +{-0.263036f,0.950565f,0.16504f},{-0.323719f,0.90817f,0.265394f},{-0.28809f,0.926484f,0.242139f}, +{-0.95459f,0.0441123f,-0.29464f},{-0.969511f,-0.169456f,-0.177012f},{-0.954454f,-0.223864f,-0.197238f}, +{0.611328f,0.777271f,0.148758f},{0.684309f,0.653679f,0.323149f},{0.915668f,0.320355f,0.242746f}, +{0.819723f,0.357047f,0.447853f},{0.422096f,0.860388f,0.285599f},{0.187582f,0.923427f,0.334806f}, +{0.432844f,0.790824f,0.432717f},{0.0680594f,-0.941994f,0.328655f},{-0.290907f,-0.832029f,0.472335f}, +{0.146264f,-0.741517f,0.654797f},{-0.735765f,-0.280452f,0.616439f},{-0.722188f,-0.547927f,0.422161f}, +{-0.768976f,-0.134488f,0.624971f},{-0.453323f,0.851967f,-0.262013f},{-0.381382f,0.908381f,-0.171438f}, +{-0.417129f,0.849952f,-0.321845f},{0.254919f,0.880365f,0.399968f},{-0.190589f,0.854832f,0.482637f}, +{-0.210229f,0.889628f,0.405419f},{0.708171f,0.617f,0.343227f},{0.614328f,0.678828f,0.402236f}, +{0.720949f,0.588198f,0.36641f},{-0.346537f,0.869928f,0.350909f},{-0.60894f,0.719521f,0.333889f}, +{-0.33076f,0.856751f,0.395696f},{0.0127889f,0.924737f,0.380393f},{-0.142866f,0.666357f,0.731818f}, +{-0.034274f,0.677512f,0.734712f},{-0.0935795f,0.661009f,0.74452f},{-0.509054f,0.763082f,0.398208f}, +{0.224064f,-0.899254f,-0.375683f},{-0.272324f,-0.820453f,-0.50269f},{0.575326f,-0.786607f,-0.224161f}, +{-0.0317741f,0.983694f,0.177022f},{-0.208638f,0.977128f,-0.0411155f},{-0.0326322f,0.9994f,-0.0116285f}, +{-0.959056f,-0.281756f,0.0287399f},{-0.968295f,-0.230842f,0.0954859f},{-0.932971f,-0.284521f,-0.220485f}, +{-0.936459f,-0.146232f,-0.318844f},{-0.385196f,0.795521f,0.467728f},{-0.571132f,0.811969f,-0.120481f}, +{-0.285455f,0.884252f,0.369613f},{0.770701f,0.508164f,0.384433f},{0.653749f,0.544785f,0.525187f}, +{0.911702f,0.212638f,0.351546f},{-0.149382f,0.141765f,-0.978564f},{-0.215627f,0.158924f,-0.963456f}, +{-0.172656f,0.332476f,-0.927173f},{-0.11137f,0.20172f,-0.973091f},{-0.120247f,0.218757f,-0.968342f}, +{-0.116053f,0.208667f,-0.971077f},{-0.110441f,0.873446f,0.474232f},{-0.120825f,0.884438f,0.450746f}, +{-0.869758f,0.377088f,0.318317f},{-0.925243f,0.175597f,0.33629f},{-0.919523f,0.0685275f,0.387016f}, +{-0.482565f,0.874002f,0.0570216f},{-0.328802f,0.876258f,0.352222f},{-0.120162f,0.895601f,0.428322f}, +{0.118699f,0.915735f,0.38385f},{-0.89304f,0.449975f,0.00125259f},{-0.956562f,0.289896f,0.0308044f}, +{-0.950694f,0.305572f,-0.0529737f},{-0.202962f,0.974794f,-0.0926428f},{-0.149628f,0.983888f,-0.0978547f}, +{-0.14631f,0.98241f,-0.116032f},{0.694537f,0.621044f,0.363211f},{0.620447f,0.729884f,0.286908f}, +{0.765911f,0.603359f,0.22212f},{0.852062f,0.457413f,0.254488f},{0.752783f,0.532628f,0.386814f}, +{-0.239568f,0.74193f,0.626216f},{-0.075257f,0.613884f,0.785801f},{0.123843f,0.376122f,0.918257f}, +{-0.474381f,0.23936f,-0.847154f},{-0.455168f,-0.811384f,-0.366713f},{-0.213933f,-0.514973f,-0.830082f}, +{-0.254102f,0.948463f,-0.189341f},{-0.367517f,0.829036f,0.421461f},{-0.361859f,0.854456f,0.372778f}, +{-0.322071f,0.851882f,0.412998f},{-0.13554f,0.98971f,-0.045867f},{-0.160853f,0.986362f,-0.0348832f}, +{-0.131247f,0.991314f,0.00835482f},{-0.542619f,0.828757f,0.136845f},{-0.343869f,0.809416f,0.476025f}, +{-0.462981f,0.852059f,0.244222f},{-0.263369f,0.901217f,0.344157f},{-0.338921f,0.884725f,0.319992f}, +{-0.511586f,0.803891f,0.303379f},{0.874238f,0.377572f,0.305199f},{0.829018f,0.495984f,0.25832f}, +{0.792623f,0.48816f,0.365306f},{0.195735f,0.962956f,0.18548f},{0.116284f,0.964873f,0.235578f}, +{0.190608f,0.956302f,0.221709f},{-0.25398f,0.966403f,-0.0394976f},{-0.378434f,0.83816f,0.39278f}, +{0.662297f,0.72069f,0.204862f},{0.530429f,0.822517f,0.205209f},{0.684042f,0.704221f,0.190155f}, +{-0.0569504f,0.997668f,-0.0376106f},{0.0157645f,0.999316f,-0.0334653f},{0.0157935f,0.995784f,-0.0903618f}, +{-0.422862f,-0.852884f,-0.306229f},{-0.12403f,-0.948113f,-0.292743f},{-0.622856f,-0.765261f,-0.162564f}, +{-0.901512f,-0.161439f,0.401515f},{-0.83239f,-0.442392f,0.333789f},{-0.279616f,0.315223f,-0.90689f}, +{-0.187629f,0.239955f,-0.952479f},{-0.201018f,0.207097f,-0.957446f},{-0.351005f,0.870672f,0.344566f}, +{-0.356193f,0.83271f,0.423934f},{-0.930524f,0.364666f,-0.0338037f},{-0.934408f,0.34659f,-0.0822029f}, +{-0.581141f,-0.79047f,-0.193474f},{-0.401732f,-0.866777f,-0.295481f},{0.219798f,0.943023f,0.249793f}, +{0.441648f,0.744463f,0.500721f},{-0.170662f,0.962922f,-0.208942f},{-0.140103f,0.977176f,-0.15968f}, +{-0.174618f,0.973903f,-0.14499f},{-0.816847f,-0.468775f,-0.336171f},{-0.897091f,0.0904234f,-0.432495f}, +{-0.65512f,-0.498546f,-0.567689f},{-0.207628f,0.956946f,0.202842f},{-0.168441f,0.72412f,0.668788f}, +{-0.0670168f,0.91956f,0.387192f},{0.311933f,0.593536f,0.741898f},{0.17352f,0.787655f,0.591177f}, +{0.180038f,0.58603f,0.790035f},{-0.861929f,-0.365565f,0.351341f},{-0.914147f,-0.309319f,0.262025f}, +{-0.791212f,-0.558632f,0.248825f},{-0.053992f,0.998524f,0.00585528f},{0.654485f,0.747701f,0.112219f}, +{0.34414f,0.92576f,0.15664f},{-0.219221f,0.914674f,0.339579f},{-0.258173f,0.858933f,0.442246f}, +{-0.143065f,0.905573f,0.399337f},{0.84601f,0.500754f,0.183061f},{0.317856f,0.577094f,0.752283f}, +{0.161512f,0.786348f,0.596297f},{0.21221f,0.640586f,0.737981f},{-0.166751f,0.982628f,-0.0814596f}, +{-0.188703f,0.980337f,-0.0577052f},{-0.17194f,0.985102f,-0.00323838f},{-0.234144f,0.969227f,0.0759959f}, +{-0.208683f,0.971131f,0.115569f},{-0.17246f,0.983756f,0.0498251f},{-0.837886f,-0.545703f,0.0124777f}, +{-0.881945f,-0.46478f,0.0784393f},{-0.936872f,-0.341756f,-0.073981f},{-0.152191f,0.98821f,0.0167007f}, +{0.529805f,0.839465f,0.12085f},{0.35177f,0.933835f,0.0648909f},{0.645035f,0.758214f,0.0950878f}, +{0.897254f,0.430807f,0.0966417f},{0.82479f,0.54689f,0.143639f},{0.833868f,0.533647f,0.141017f}, +{0.398204f,0.869206f,-0.293111f},{0.368214f,0.912358f,-0.178942f},{0.460623f,0.862636f,-0.209012f}, +{0.757324f,0.632608f,0.162072f},{-0.196495f,0.847757f,0.492644f},{-0.281075f,0.806466f,0.520201f}, +{-0.178493f,0.831846f,0.525522f},{-0.965141f,0.251922f,-0.070975f},{-0.968395f,0.204029f,-0.143468f}, +{-0.98048f,0.122575f,-0.153733f},{-0.181622f,0.862125f,0.473026f},{-0.24619f,0.905469f,0.345711f}, +{-0.228317f,0.86714f,0.44265f},{-0.366278f,0.419914f,-0.830369f},{-0.283198f,0.181294f,-0.94177f}, +{-0.125999f,0.144635f,-0.98143f},{-0.139033f,0.985514f,-0.0971168f},{-0.689549f,0.612864f,0.385901f}, +{-0.466431f,0.7979f,0.381836f},{-0.429588f,0.835885f,0.341687f},{0.92412f,0.353599f,-0.144809f}, +{0.860566f,0.471663f,-0.192248f},{0.897956f,0.425678f,-0.111685f},{0.207155f,0.859726f,0.466859f}, +{0.500149f,-0.865185f,-0.036124f},{0.196539f,-0.87016f,-0.451878f},{0.407742f,-0.874513f,-0.262628f}, +{-0.165294f,0.961411f,0.219924f},{-0.174258f,0.925978f,0.334962f},{0.0209707f,0.954479f,-0.297539f}, +{-0.0411579f,0.958112f,-0.28342f},{-0.0430633f,0.986192f,-0.15991f},{-0.138388f,0.984372f,0.108905f}, +{-0.0864297f,0.979549f,0.181694f},{0.355448f,-0.675269f,-0.646272f},{0.982785f,-0.167672f,0.0775822f}, +{0.964467f,-0.220328f,0.145802f},{-0.106177f,0.990299f,-0.0896399f},{0.585177f,0.809954f,0.0392717f}, +{0.351387f,0.916776f,0.189866f},{0.28604f,0.941601f,-0.177678f},{-0.330406f,0.875489f,0.352634f}, +{0.421234f,0.845704f,0.327638f},{-0.000578385f,0.935843f,0.352418f},{0.797729f,0.521056f,0.303528f}, +{0.851595f,0.355876f,0.384888f},{0.833935f,0.461972f,0.301884f},{-0.281361f,0.950692f,0.130465f}, +{0.494345f,0.869264f,0.0014269f},{0.323062f,0.915374f,0.240255f},{0.236605f,0.903551f,0.357229f}, +{0.326762f,0.897894f,0.29498f},{-0.797988f,-0.554353f,0.236451f},{-0.715613f,-0.675862f,0.176376f}, +{-0.604376f,-0.751052f,0.265801f},{-0.986977f,-0.121592f,-0.105315f},{-0.980444f,-0.196796f,-0.000786544f}, +{-0.991928f,-0.0157392f,-0.12582f},{-0.164281f,0.191446f,-0.967657f},{-0.0978269f,0.992432f,0.074216f}, +{-0.0896928f,0.987081f,0.132768f},{-0.0518716f,0.987449f,0.149177f},{-0.0994765f,0.995001f,0.00874661f}, +{-0.0983143f,0.994234f,-0.0428241f},{-0.151126f,0.986293f,0.0662396f},{-0.105553f,0.987126f,0.120173f}, +{0.969847f,-0.00525719f,-0.243657f},{0.986459f,0.017394f,-0.163085f},{0.977914f,0.0433675f,-0.204458f}, +{0.785811f,0.528888f,0.32059f},{0.773372f,0.522143f,0.35953f},{0.866441f,0.410223f,0.2846f}, +{-0.51235f,0.858582f,0.0183004f},{0.220746f,0.613517f,0.7582f},{0.359838f,0.596592f,0.717353f}, +{0.263219f,0.954822f,-0.137951f},{0.236716f,0.968405f,-0.0784723f},{0.0393797f,0.987006f,-0.155785f}, +{-0.100373f,0.924854f,0.366837f},{-0.0789204f,0.886723f,0.455514f},{-0.0325974f,0.953865f,0.298462f}, +{-0.0996998f,0.958219f,0.268097f},{-0.124818f,0.990033f,0.0652242f},{0.858404f,0.470083f,0.20534f}, +{-0.351088f,0.90292f,0.247937f},{-0.266096f,0.623714f,0.734965f},{0.0546192f,0.518634f,0.85325f}, +{-0.00627665f,0.528708f,0.848781f},{0.0310592f,0.974423f,0.222564f},{0.225176f,0.961336f,0.158522f}, +{0.238458f,0.899672f,0.365688f},{0.112761f,0.816349f,0.566444f},{0.0456267f,0.831381f,0.553826f}, +{0.0552438f,0.806235f,0.589011f},{0.00507421f,0.703522f,0.710656f},{0.0903773f,0.728552f,0.679002f}, +{-0.417529f,0.874211f,0.247839f},{-0.503292f,0.783758f,0.363895f},{-0.760404f,-0.626818f,-0.169957f}, +{0.528696f,0.762456f,0.373016f},{-0.0576661f,0.99596f,0.0688321f},{-0.0702293f,0.979726f,0.187628f}, +{-0.050923f,0.994669f,-0.0896719f},{0.550602f,-0.79647f,0.249946f},{0.435275f,-0.856718f,0.276713f}, +{0.503614f,-0.851182f,0.147859f},{0.924068f,0.378481f,-0.0533867f},{0.91027f,0.413033f,-0.0285087f}, +{0.909985f,0.413706f,0.027824f},{0.151382f,0.614505f,0.774252f},{0.121735f,0.71707f,0.686288f}, +{-0.0654101f,0.661894f,0.746738f},{0.449607f,0.836161f,-0.314146f},{0.410327f,0.856681f,0.312616f}, +{-0.296897f,0.85146f,0.432283f},{0.657107f,-0.725019f,0.206298f},{0.302314f,-0.951098f,0.0633903f}, +{0.376669f,-0.920947f,0.0998864f},{0.358164f,0.823977f,0.439068f},{0.708367f,0.624326f,0.329293f}, +{0.265183f,0.860485f,0.435022f},{0.264323f,0.827217f,0.495829f},{-0.208083f,0.974072f,-0.088802f}, +{-0.0406154f,0.999099f,-0.0123014f},{-0.21748f,0.975297f,-0.0387011f},{0.811224f,0.532897f,0.240699f}, +{0.325329f,0.935848f,0.135462f},{0.298507f,0.933061f,0.200724f},{0.370731f,0.911821f,0.176466f}, +{0.177805f,0.850282f,0.495384f},{0.0782578f,0.861702f,0.501344f},{-0.862905f,-0.0410319f,0.503697f}, +{-0.780384f,-0.483438f,0.396596f},{-0.920067f,-0.0587012f,0.387339f},{0.120623f,0.907232f,0.402964f}, +{0.0912084f,0.872958f,0.479193f},{-0.223768f,0.248002f,-0.942562f},{-0.006037f,0.829609f,0.558313f}, +{-0.086073f,0.83628f,0.541505f},{-0.0751572f,0.794053f,0.603184f},{-0.1793f,0.932707f,0.312905f}, +{-0.35696f,0.931368f,0.0716437f},{0.723383f,0.662743f,0.193622f},{0.748428f,0.357617f,0.558539f}, +{0.382063f,0.804684f,0.454436f},{0.41987f,0.797587f,0.433088f},{0.60187f,0.723171f,0.338786f}, +{0.465439f,0.84667f,0.257909f},{0.604528f,0.760774f,0.236153f},{-0.14065f,0.878178f,0.457187f}, +{-0.156203f,0.926759f,0.341641f},{0.30724f,0.899647f,0.310225f},{0.983246f,0.063408f,-0.1709f}, +{0.96155f,0.0864831f,-0.260656f},{0.979863f,0.0529409f,-0.192523f},{0.0766483f,-0.724763f,-0.684722f}, +{0.0795982f,-0.728566f,-0.680335f},{0.240247f,-0.855735f,-0.458256f},{-0.168843f,0.172447f,-0.97044f}, +{-0.0397846f,0.99753f,-0.0578866f},{-0.187576f,0.976181f,-0.109024f},{-0.315613f,0.922592f,0.221838f}, +{-0.344408f,0.911946f,0.223021f},{-0.0535464f,0.762474f,0.6448f},{-0.150055f,0.814483f,0.560447f}, +{-0.11104f,0.754474f,0.646868f},{-0.838926f,0.210014f,-0.502093f},{-0.83238f,0.28807f,-0.473454f}, +{-0.797072f,0.470878f,-0.378087f},{-0.86169f,0.262894f,-0.434025f},{-0.815344f,0.425554f,-0.392578f}, +{0.57735f,0.57735f,0.57735f},{0.421857f,0.686834f,0.591857f},{-0.249799f,0.940143f,0.231799f}, +{-0.296301f,0.880261f,0.370604f},{0.0118961f,0.905903f,0.423318f},{0.0313234f,0.355953f,0.933979f}, +{0.0352841f,0.306272f,0.95129f},{-0.0893656f,0.270821f,0.958473f},{-0.713881f,0.490776f,-0.499512f}, +{-0.410273f,0.869831f,-0.273989f},{-0.894055f,0.352175f,-0.276838f},{-0.817393f,0.308118f,-0.486757f}, +{-0.875796f,0.121223f,-0.467212f},{0.669572f,-0.707121f,-0.227273f},{0.818913f,-0.535545f,-0.206332f}, +{0.84874f,-0.466955f,-0.248178f},{0.00732999f,0.884841f,0.465835f},{0.756543f,0.624106f,0.19528f}, +{0.00141355f,0.916707f,0.399559f},{0.0128935f,0.936489f,0.35046f},{0.555117f,-0.0206778f,-0.831515f}, +{0.733425f,-0.110287f,-0.670765f},{-0.336867f,0.804122f,0.489804f},{-0.547861f,0.721964f,-0.42263f}, +{-0.517719f,0.852632f,0.0706092f},{-0.659477f,0.748223f,0.0724769f},{0.00701106f,0.622457f,0.782623f}, +{-0.00356288f,0.708901f,0.705299f},{-0.0351258f,0.774387f,0.631736f},{-0.385602f,0.794284f,-0.469494f}, +{-0.409104f,0.75301f,-0.515373f},{-0.343588f,0.739178f,-0.579278f},{0.44348f,0.409973f,0.797024f}, +{0.172274f,0.553567f,0.814791f},{0.444401f,0.516545f,0.731907f},{-0.899018f,0.315509f,-0.303678f}, +{-0.0287092f,0.99401f,-0.105451f},{0.0238513f,0.996213f,-0.0836077f},{0.0498361f,0.992292f,-0.113461f}, +{-0.243806f,0.954737f,0.170399f},{0.935547f,0.345553f,-0.0731101f},{0.866464f,0.488525f,-0.10288f}, +{0.887404f,0.427894f,-0.171524f},{0.919118f,0.358308f,0.163822f},{-0.939902f,-0.308644f,0.146026f}, +{-0.985943f,-0.128784f,0.106447f},{-0.968025f,-0.242516f,0.0641434f},{-0.319472f,-0.322908f,0.890881f}, +{-0.212183f,-0.00597968f,0.977212f},{-0.460175f,-0.0230708f,0.887528f},{-0.311636f,0.272517f,0.910284f}, +{0.148652f,-0.861322f,-0.485827f},{0.249015f,-0.75173f,-0.61065f},{0.283906f,-0.838223f,-0.465596f}, +{-0.0533076f,-0.660568f,-0.748871f},{0.102555f,-0.808587f,-0.579369f},{0.78009f,0.447597f,0.437169f}, +{0.659098f,0.426598f,0.619358f},{0.700411f,0.468918f,0.538089f},{0.981422f,-0.0277144f,-0.189847f}, +{0.990634f,0.0115244f,-0.136058f},{0.969545f,-0.000610817f,-0.244911f},{-0.999433f,0.0331006f,-0.00624168f}, +{-0.969336f,0.0523667f,-0.240095f},{-0.985831f,-0.0135757f,-0.167188f},{-0.11484f,-0.497945f,-0.859571f}, +{0.145066f,-0.756207f,-0.638049f},{-0.992501f,0.0955116f,-0.0762813f},{0.0307977f,0.813263f,0.581081f}, +{-0.252137f,0.914982f,0.315017f},{0.0936196f,0.978662f,0.182908f},{0.0174373f,0.996305f,-0.0841026f}, +{0.0180337f,0.991815f,-0.126402f},{0.044532f,-0.527634f,-0.848304f},{0.0660403f,-0.585473f,-0.807998f}, +{0.100115f,-0.56197f,-0.821077f},{-0.359698f,0.0989121f,-0.927811f},{-0.43688f,0.0954944f,-0.894437f}, +{-0.37905f,0.204351f,-0.902531f},{0.000580824f,0.0870738f,-0.996202f},{0.128226f,0.0453576f,-0.990707f}, +{0.0914407f,-0.0043354f,-0.995801f},{-0.651647f,0.621586f,-0.434727f},{-0.803714f,0.589137f,-0.0834296f}, +{-0.559601f,0.820384f,0.117543f},{0.759113f,0.438907f,0.480737f},{0.573133f,0.785088f,0.234854f}, +{0.738396f,0.63573f,0.224987f},{-0.340618f,-0.160374f,0.926423f},{-0.0783287f,-0.180108f,0.980523f}, +{-0.46722f,-0.379277f,0.798658f},{-0.083493f,-0.271426f,0.958831f},{0.0491597f,-0.0662771f,0.996589f}, +{0.0134786f,0.189284f,0.98183f},{0.00493481f,0.319204f,0.947673f},{-0.0212024f,0.401033f,0.915818f}, +{-0.385416f,0.416591f,0.823351f},{-0.0488352f,0.466255f,0.883301f},{-0.290291f,0.499278f,0.816365f}, +{-0.128063f,0.542822f,0.830026f},{-0.129216f,0.549796f,0.825244f},{-0.32094f,0.562335f,0.762087f}, +{-0.267665f,0.633191f,0.72624f},{-0.033667f,0.973168f,0.227618f},{0.0168639f,0.985589f,0.168313f}, +{-0.566884f,0.712608f,0.413319f},{0.108542f,0.994073f,0.0061294f},{0.125559f,0.990888f,-0.0487381f}, +{0.0163092f,0.994743f,0.101096f},{-0.807412f,-0.589942f,-0.00735334f},{-0.838698f,-0.540232f,0.0688061f}, +{-0.881405f,-0.469341f,0.0533315f},{0.783992f,-0.593224f,-0.182874f},{0.992591f,-0.109235f,-0.0532095f}, +{0.818459f,-0.442588f,-0.366388f},{-0.974179f,-0.225775f,0.000958332f},{-0.958019f,-0.275422f,-0.0796435f}, +{0.707968f,-0.265502f,0.654438f},{0.636608f,-0.281028f,0.71816f},{0.661075f,-0.188496f,0.726257f}, +{0.0874356f,-0.56697f,-0.819085f},{0.0433353f,-0.476473f,-0.87812f},{0.11842f,-0.528335f,-0.840737f}, +{0.129623f,0.104684f,-0.986022f},{-0.998419f,-0.0542573f,-0.0146808f},{-0.688453f,0.673068f,-0.270208f}, +{0.0156894f,0.999522f,0.0266441f},{-0.868316f,-0.494385f,0.0401279f},{-0.18112f,0.613563f,0.768594f}, +{-0.15902f,0.663238f,0.73132f},{-0.271218f,0.702918f,0.657531f},{-0.257402f,0.951051f,-0.171013f}, +{0.981363f,0.0833966f,-0.173124f},{0.99075f,0.0716342f,-0.115248f},{0.973192f,0.181782f,-0.140896f}, +{0.945709f,0.233387f,-0.226198f},{0.914622f,0.287571f,-0.2842f},{0.942306f,0.247063f,-0.225875f}, +{-0.938596f,-0.343675f,-0.030422f},{-0.261763f,0.960149f,-0.097945f},{0.948742f,0.313295f,0.0416476f}, +{-0.95564f,-0.294067f,0.0166649f},{-0.957934f,-0.285956f,0.0243055f},{-0.972972f,-0.208945f,-0.0983253f}, +{-0.979352f,-0.200837f,-0.0230909f},{-0.991689f,-0.0808817f,-0.100057f},{-0.945654f,-0.320158f,-0.0568986f}, +{0.690643f,-0.557553f,-0.460594f},{-0.432122f,-0.506947f,-0.745839f},{0.275703f,-0.688974f,-0.6703f}, +{0.0354541f,0.98518f,-0.167817f},{-0.32858f,-0.451626f,0.829499f},{-0.404259f,-0.441563f,0.800997f}, +{-0.36329f,-0.46504f,0.807315f},{-0.505347f,-0.18888f,-0.841991f},{-0.461011f,-0.138001f,-0.876598f}, +{-0.492927f,-0.276201f,-0.825068f},{0.846028f,0.49353f,0.201657f},{-0.417774f,-0.370217f,0.829701f}, +{-0.372866f,-0.459566f,0.806083f},{-0.301297f,-0.340957f,0.890487f},{0.0386461f,0.5845f,0.810473f}, +{-0.117987f,0.798897f,0.589782f},{-0.252652f,0.769314f,0.586791f},{-0.0898694f,0.716685f,0.691582f}, +{0.984322f,0.159456f,0.0753966f},{0.981936f,0.164752f,0.0930547f},{0.991128f,0.103448f,0.0834458f}, +{0.288081f,0.922413f,0.257223f},{0.196528f,0.928668f,0.314568f},{-0.276998f,0.950331f,0.141926f}, +{-0.947481f,0.302294f,-0.104397f},{-0.880527f,0.290802f,-0.374307f},{-0.982123f,0.082863f,-0.16902f}, +{0.0308069f,0.690279f,0.722888f},{-0.136452f,0.749634f,0.647634f},{-0.0115687f,0.611309f,0.791308f}, +{0.235361f,0.812168f,0.533844f},{0.42321f,0.747665f,0.511752f},{-0.00294655f,0.777584f,0.628772f}, +{-0.0585491f,0.0909536f,-0.994133f},{0.139261f,0.0860385f,-0.986511f},{0.0998423f,0.12309f,-0.98736f}, +{0.103544f,0.953622f,-0.282637f},{0.124829f,0.977293f,-0.171217f},{0.194063f,0.963728f,-0.183217f}, +{-0.464984f,0.339376f,-0.817688f},{-0.437427f,0.436841f,-0.78602f},{-0.428677f,0.450404f,-0.783181f}, +{-0.197968f,0.136253f,-0.970692f},{-0.334069f,0.160007f,-0.928868f},{-0.237943f,0.127333f,-0.962896f}, +{-0.520129f,0.00462802f,-0.854075f},{-0.455464f,0.0948686f,-0.885185f},{-0.332832f,0.0880139f,-0.93887f}, +{-0.481026f,-0.475911f,-0.73629f},{-0.438014f,-0.337269f,-0.833302f},{-0.347452f,-0.547715f,-0.761108f}, +{-0.461713f,-0.302499f,-0.833856f},{-0.29253f,-0.428292f,0.854981f},{-0.254224f,-0.27951f,0.925875f}, +{-0.528736f,-0.202208f,0.824349f},{-0.27128f,-0.0983893f,0.957458f},{-0.119153f,0.0275033f,0.992495f}, +{-0.263646f,0.0184969f,0.964442f},{-0.203003f,-0.0257421f,0.97884f},{0.0881956f,0.0331346f,0.995552f}, +{0.289706f,0.260591f,0.920958f},{0.238046f,0.369706f,0.898138f},{0.186732f,0.437215f,0.879758f}, +{0.160958f,0.506869f,0.846863f},{0.14827f,0.502056f,0.85203f},{-0.00055618f,0.615531f,0.788112f}, +{-0.00716591f,0.635817f,0.771807f},{0.956568f,0.250869f,-0.148464f},{-0.0660797f,0.638031f,-0.76717f}, +{-0.0601667f,0.665157f,-0.744276f},{-0.140985f,0.719068f,-0.680488f},{0.783289f,-0.583404f,-0.214702f}, +{0.948733f,-0.302305f,-0.092287f},{0.965091f,-0.248812f,-0.0818032f},{-0.276455f,0.810676f,0.516117f}, +{-0.396968f,0.850953f,0.343942f},{-0.229114f,0.779813f,0.582579f},{0.119628f,0.98719f,-0.105573f}, +{0.212732f,0.971432f,-0.105193f},{-0.380376f,0.529341f,-0.758362f},{0.0247164f,-0.999286f,-0.0285692f}, +{0.0221136f,-0.999343f,-0.0287203f},{0.0171425f,-0.999755f,-0.01398f},{0.0309597f,-0.415753f,-0.908951f}, +{0.00724507f,-0.383477f,-0.923522f},{0.0188414f,-0.424607f,-0.905182f},{0.210939f,0.157303f,0.964759f}, +{-0.0297303f,0.747411f,0.663697f},{0.933011f,0.289119f,-0.214245f},{0.925733f,0.281444f,-0.252602f}, +{0.903201f,0.364361f,-0.226868f},{0.861819f,-0.100476f,0.497165f},{0.91382f,0.0166522f,0.405778f}, +{0.85081f,0.0456878f,0.523484f},{-0.922116f,0.28202f,-0.264888f},{-0.924562f,0.232143f,-0.302149f}, +{-0.916155f,0.231573f,-0.327161f},{0.801727f,0.22916f,-0.552014f},{0.806146f,0.340978f,-0.483593f}, +{0.844248f,0.252886f,-0.472539f},{-0.900107f,0.432791f,-0.0499879f},{-0.929252f,0.170879f,-0.327554f}, +{-0.889537f,0.200244f,-0.410641f},{-0.909881f,0.119352f,-0.397331f},{0.319048f,-0.927929f,0.19276f}, +{-0.0570574f,-0.998339f,0.00799306f},{-0.837236f,0.0170867f,-0.546575f},{-0.766305f,-0.107681f,-0.633389f}, +{-0.831586f,0.0795901f,-0.549664f},{0.377541f,0.316521f,0.870217f},{0.332509f,0.499481f,0.799972f}, +{0.460076f,0.517386f,0.721555f},{0.79541f,-0.566159f,-0.216304f},{0.911327f,-0.362557f,-0.195027f}, +{-0.74796f,-0.0695523f,-0.660089f},{-0.662762f,-0.235216f,-0.710929f},{-0.539195f,-0.222657f,-0.812214f}, +{0.0218027f,-0.433793f,0.900749f},{-0.00311731f,-0.163603f,0.986521f},{-0.0173914f,0.0456827f,0.998805f}, +{0.0400381f,0.178126f,0.983193f},{0.283483f,0.312944f,0.906479f},{0.134246f,-0.174071f,0.975539f}, +{0.212289f,-0.161507f,0.963768f},{0.229441f,-0.18882f,0.954832f},{0.412163f,0.305418f,0.858395f}, +{0.328776f,0.398539f,0.856197f},{0.424166f,0.363353f,0.829492f},{0.265865f,0.453263f,0.850804f}, +{0.18611f,0.546428f,0.816566f},{0.143454f,0.625752f,0.766718f},{0.120938f,0.656564f,0.744512f}, +{0.932122f,0.322601f,-0.16455f},{0.804729f,0.514737f,-0.29573f},{0.834634f,0.525578f,-0.164783f}, +{-0.988431f,0.0168326f,-0.150736f},{-0.992003f,-0.00163991f,-0.126204f},{-0.967043f,-0.0141034f,-0.254221f}, +{-0.00142562f,-0.579066f,-0.815279f},{-0.125423f,-0.384951f,-0.914375f},{-0.103741f,-0.44905f,-0.887464f}, +{-0.553529f,0.736759f,0.388319f},{-0.678903f,0.627379f,0.381427f},{-0.0588021f,0.897443f,0.437193f}, +{-0.943585f,-0.29927f,-0.141721f},{-0.965152f,-0.168369f,-0.200336f},{0.581388f,-0.170418f,-0.795579f}, +{0.894217f,0.330236f,-0.302192f},{0.891125f,-0.205523f,-0.404545f},{0.210297f,0.767182f,0.605977f}, +{0.00638082f,0.857806f,0.513935f},{0.470693f,0.834009f,0.287883f},{0.370541f,0.871789f,0.320441f}, +{0.440424f,0.862366f,0.249702f},{-0.873822f,-0.484636f,0.0395298f},{-0.891498f,-0.450702f,0.0458181f}, +{-0.87457f,-0.484822f,-0.00871369f},{0.197942f,0.935148f,-0.293798f},{0.197289f,-0.730094f,-0.654248f}, +{0.22188f,-0.765928f,-0.603426f},{0.238886f,-0.773997f,-0.586398f},{-0.683216f,-0.224521f,-0.694842f}, +{-0.127828f,0.30359f,-0.944189f},{-0.0162157f,0.431431f,-0.902f},{-0.193427f,0.498023f,-0.845316f}, +{-0.280182f,-0.707712f,-0.64857f},{-0.079535f,-0.668278f,-0.739648f},{-0.392839f,-0.472588f,-0.788884f}, +{0.176156f,0.338308f,0.924401f},{-0.581431f,0.267686f,-0.768298f},{-0.594621f,0.186383f,-0.782104f}, +{-0.642564f,0.383097f,-0.663587f},{0.0191206f,-0.600232f,0.799598f},{0.100352f,-0.466051f,0.879049f}, +{0.187243f,-0.175769f,0.96646f},{0.209566f,-0.253734f,0.944299f},{0.111776f,-0.225572f,0.967793f}, +{-0.420204f,-0.794529f,-0.438352f},{-0.0541604f,-0.944389f,-0.324339f},{-0.958418f,0.269381f,0.0941702f}, +{-0.924451f,0.370113f,0.0916944f},{-0.950281f,0.306645f,-0.0541689f},{0.980787f,-0.180823f,0.0732094f}, +{0.981284f,-0.17298f,0.0846207f},{-0.0940962f,0.884837f,0.4563f},{-0.534859f,0.744233f,0.400053f}, +{0.718215f,0.189478f,-0.669526f},{0.713162f,0.106936f,-0.692795f},{0.801152f,-0.0516452f,-0.596229f}, +{0.560765f,-0.485962f,-0.67036f},{0.647819f,-0.462094f,-0.60564f},{0.552192f,-0.596363f,-0.582611f}, +{0.461443f,-0.823847f,-0.329161f},{0.839854f,0.511735f,0.181034f},{0.824616f,0.534256f,0.185955f}, +{-0.439976f,0.866796f,0.234702f},{-0.118575f,0.752426f,0.647916f},{0.149029f,0.629448f,0.762618f}, +{0.037462f,0.830588f,0.555625f},{-0.327739f,0.0394449f,0.943944f},{-0.516321f,0.0528115f,0.854765f}, +{-0.414224f,-0.0682792f,0.90761f},{0.288967f,0.0433499f,-0.956357f},{0.275983f,-0.147043f,-0.949848f}, +{0.598083f,-0.304596f,-0.741294f},{0.293406f,-0.917829f,-0.267401f},{0.601447f,-0.775694f,-0.191208f}, +{0.0463522f,0.74153f,0.669317f},{-0.704225f,-0.683019f,-0.193784f},{-0.708969f,-0.696362f,-0.111547f}, +{-0.680494f,-0.689726f,-0.247398f},{-0.443656f,0.340732f,0.828898f},{-0.338882f,0.238957f,0.909977f}, +{-0.467223f,0.237607f,0.851614f},{-0.443441f,-0.0478543f,-0.895025f},{-0.542019f,0.148455f,-0.827149f}, +{-0.496647f,0.0266507f,-0.867543f},{0.452023f,0.320446f,0.83246f},{0.187312f,-0.289498f,0.938672f}, +{0.817561f,0.484433f,-0.311319f},{0.119843f,0.692326f,0.711563f},{-0.5423f,0.839989f,-0.0181133f}, +{-0.589584f,0.802126f,-0.0947837f},{0.272709f,0.847446f,0.455483f},{-0.999451f,0.0035897f,0.0329507f}, +{0.0652443f,0.925068f,0.374155f},{-0.417802f,0.0621403f,0.906411f},{-0.299806f,0.0578951f,0.952242f}, +{-0.230259f,-0.156221f,0.960508f},{-0.482247f,-0.62297f,0.615911f},{-0.72808f,-0.351511f,0.588506f}, +{-0.223557f,0.145718f,0.963737f},{-0.284359f,0.277931f,0.917548f},{-0.394802f,0.167585f,0.903353f}, +{-0.36651f,-0.490079f,-0.790881f},{0.958509f,-0.237533f,-0.157604f},{-0.340748f,-0.792406f,0.505947f}, +{-0.800663f,-0.422511f,0.424763f},{-0.793826f,-0.330462f,0.510524f},{-0.345477f,0.371662f,0.861692f}, +{-0.35377f,0.384884f,0.852474f},{-0.235416f,0.24056f,0.941653f},{-0.00349561f,-0.229967f,0.973192f}, +{-0.452241f,0.131272f,-0.882182f},{0.213681f,-0.17502f,0.961097f},{0.385113f,-0.504767f,0.772592f}, +{0.241816f,0.0985017f,0.96531f},{0.245202f,0.25789f,0.934542f},{0.141131f,0.377939f,0.91501f}, +{0.123254f,0.47787f,0.869741f},{-0.0193965f,0.480999f,0.876507f},{0.220321f,0.498587f,0.838373f}, +{0.158681f,0.595297f,0.787681f},{0.214315f,0.598022f,0.772294f},{0.103356f,-0.545978f,0.8314f}, +{0.198197f,-0.0823329f,0.976698f},{0.65635f,-0.1957f,-0.728633f},{0.628686f,-0.318384f,-0.709497f}, +{0.490731f,-0.0500569f,-0.869872f},{0.996762f,0.0511583f,-0.0620418f},{0.308099f,0.64451f,0.699773f}, +{0.424921f,0.473751f,0.771364f},{0.435549f,0.496107f,0.751116f},{-0.0848771f,-0.255893f,-0.962972f}, +{0.354992f,0.509584f,0.783776f},{0.394104f,0.427023f,0.813839f},{0.585515f,-0.690378f,-0.424912f}, +{0.527878f,-0.549372f,-0.647716f},{0.884681f,-0.463029f,-0.0542522f},{0.022149f,-0.999754f,0.000742555f}, +{-0.0122399f,-0.999713f,0.0206051f},{0.040471f,-0.998944f,0.0217503f},{-0.170834f,0.582988f,-0.794317f}, +{-0.178266f,0.547618f,-0.817518f},{-0.212052f,0.610169f,-0.763366f},{0.825517f,0.493208f,-0.274349f}, +{0.801331f,0.529124f,-0.279101f},{0.818077f,0.552458f,-0.159812f},{0.467831f,-0.658629f,-0.589357f}, +{0.435167f,-0.759272f,-0.483876f},{0.236562f,-0.797224f,-0.555402f},{-0.644491f,0.666855f,0.374081f}, +{-0.00960394f,0.452187f,0.891871f},{-0.177981f,0.559222f,0.809687f},{-0.199909f,0.532918f,0.822213f}, +{0.337658f,0.462369f,0.819879f},{0.229458f,0.26022f,0.937888f},{0.416462f,0.423903f,0.804279f}, +{0.76476f,0.595295f,0.246506f},{-0.929246f,0.137717f,-0.342835f},{-0.875507f,-0.151751f,-0.458759f}, +{-0.894451f,0.21627f,-0.391389f},{0.310225f,0.947793f,0.0738153f},{0.231035f,0.95555f,0.183157f}, +{-0.296947f,0.852665f,0.429865f},{-0.985861f,0.0887629f,-0.142124f},{0.0185413f,0.943008f,0.332254f}, +{0.057313f,0.886239f,0.459669f},{-0.679383f,-0.0801837f,-0.729389f},{0.113925f,-0.311895f,-0.943262f}, +{-0.139823f,0.0525498f,0.988781f},{-0.218846f,0.661597f,0.717214f},{-0.148582f,-0.206342f,-0.967133f}, +{0.7509f,-0.182971f,-0.634563f},{0.420695f,-0.203165f,0.88416f},{-0.420831f,-0.473534f,-0.773736f}, +{-0.229455f,-0.26095f,-0.937686f},{-0.246277f,-0.0434649f,-0.968224f},{0.13588f,0.282837f,0.949495f}, +{0.176315f,0.366131f,0.913707f},{0.116814f,0.371617f,0.921008f},{-0.00480627f,0.35768f,0.933832f}, +{0.0834085f,0.119904f,0.989276f},{0.161637f,-0.054116f,0.985365f},{0.00341052f,0.0398947f,0.999198f}, +{-0.0800531f,-0.726618f,0.682362f},{0.0654456f,-0.609214f,0.790301f},{-0.0633954f,-0.615952f,0.785229f}, +{-0.0539942f,-0.764143f,0.642783f},{0.00401556f,-0.756327f,0.654181f},{0.0838838f,-0.339017f,0.937033f}, +{0.0587523f,-0.4979f,0.865242f},{0.148705f,0.00212254f,0.988879f},{0.182487f,0.0571239f,0.981547f}, +{0.53245f,-0.726799f,-0.43389f},{0.571984f,-0.776758f,-0.263593f},{0.490454f,-0.797822f,-0.350621f}, +{0.914028f,0.298546f,-0.274632f},{0.908921f,0.0880075f,-0.407576f},{0.96965f,0.0520819f,-0.238887f}, +{-0.0686965f,0.325763f,-0.942952f},{-0.109446f,0.392498f,-0.913218f},{-0.132643f,0.414957f,-0.900121f}, +{0.465453f,-0.78761f,-0.403763f},{0.43965f,-0.708592f,-0.551911f},{0.469105f,-0.777133f,-0.419529f}, +{0.453067f,-0.76698f,-0.454393f},{0.454134f,-0.76286f,-0.460225f},{0.876326f,-0.436944f,-0.202812f}, +{0.900929f,-0.413532f,-0.131602f},{0.921355f,-0.355754f,-0.156663f},{-0.0920299f,0.658693f,0.746762f}, +{-0.155912f,0.746973f,0.646315f},{-0.28089f,0.68316f,0.674087f},{-0.0731005f,0.675791f,0.733459f}, +{-0.128286f,0.631681f,0.76454f},{0.210613f,0.314174f,0.925709f},{-0.449802f,0.403711f,-0.796678f}, +{-0.396683f,0.413545f,-0.819526f},{-0.273958f,0.956358f,0.101619f},{0.11902f,0.990924f,0.0624892f}, +{0.108584f,0.967612f,0.227895f},{0.0300621f,0.972509f,0.230917f},{-0.32827f,0.355206f,0.875253f}, +{0.278549f,0.478117f,0.832955f},{0.288245f,0.594488f,0.750666f},{0.0643547f,0.0265242f,0.997575f}, +{0.207224f,-0.0685004f,0.975892f},{0.0639272f,0.178665f,0.981831f},{-0.0313133f,-0.861465f,-0.506851f}, +{0.132764f,-0.817868f,-0.55988f},{0.122697f,-0.893873f,-0.431204f},{0.988041f,0.149777f,-0.036627f}, +{0.970806f,0.237743f,-0.0318535f},{0.982795f,0.17944f,0.0437546f},{0.252469f,0.598346f,0.760422f}, +{0.26065f,0.585606f,0.767547f},{0.264146f,0.574259f,0.774889f},{0.906751f,-0.385086f,-0.171788f}, +{0.684151f,-0.723747f,0.0901569f},{0.773584f,-0.525695f,0.353853f},{0.990162f,-0.133992f,0.0403111f}, +{-0.989415f,0.0943284f,0.110271f},{-0.493016f,-0.774648f,-0.39605f},{0.19466f,0.662258f,0.723548f}, +{0.559884f,0.373378f,0.739675f},{0.320414f,0.527874f,0.786564f},{0.974845f,-0.221106f,0.0280791f}, +{0.987799f,-0.15154f,0.0359129f},{0.948007f,-0.317943f,-0.0139217f},{0.807472f,-0.295769f,-0.510401f}, +{0.831477f,-0.257606f,-0.492224f},{0.830739f,-0.29772f,-0.470357f},{0.629038f,-0.619501f,-0.469605f}, +{0.731654f,-0.44459f,-0.516742f},{0.663327f,-0.609815f,-0.43373f},{-0.888495f,-0.373328f,0.266838f}, +{-0.853903f,-0.411981f,0.317996f},{-0.871296f,-0.349591f,0.344428f},{0.173781f,0.830788f,-0.528764f}, +{0.214551f,0.837516f,-0.502528f},{0.326037f,0.775801f,-0.540215f},{-0.109706f,-0.228527f,0.967336f}, +{-0.147056f,-0.079098f,0.98596f},{-0.19074f,0.0999919f,0.976535f},{0.678873f,-0.52574f,-0.512571f}, +{0.362062f,-0.744174f,-0.561351f},{-0.991329f,0.122094f,0.0485786f},{-0.974345f,0.217247f,-0.0587819f}, +{-0.287749f,0.942918f,0.167646f},{-0.360444f,0.226196f,0.90494f},{-0.212829f,0.253452f,0.943645f}, +{-0.1543f,0.180708f,0.971358f},{-0.240401f,0.248335f,0.938369f},{-0.281255f,0.382109f,0.880277f}, +{-0.291117f,0.434942f,0.852101f},{-0.241476f,0.380203f,0.892824f},{-0.138281f,0.282514f,0.949244f}, +{-0.118735f,0.0590326f,0.99117f},{0.794851f,-0.419047f,-0.438875f},{0.232186f,0.0291142f,0.972236f}, +{0.121233f,-0.0318358f,0.992113f},{0.199799f,0.0839047f,0.976238f},{0.346783f,-0.915641f,-0.203328f}, +{0.193206f,-0.847024f,-0.495199f},{0.289267f,-0.947851f,-0.133803f},{0.348006f,0.499509f,0.793336f}, +{0.208545f,0.31167f,0.927023f},{0.176429f,0.45395f,0.873386f},{0.131126f,-0.651578f,0.747162f}, +{0.477843f,0.446816f,0.756321f},{0.344739f,0.568001f,0.747349f},{0.900389f,-0.107033f,-0.421715f}, +{0.929053f,-0.117313f,-0.350853f},{0.924176f,-0.147349f,-0.352402f},{-0.0313202f,0.13272f,0.990659f}, +{-0.119674f,0.169113f,0.978304f},{0.0505059f,-0.0361727f,0.998068f},{0.178549f,0.161106f,0.970652f}, +{0.30095f,0.0349999f,0.952997f},{0.886396f,0.461951f,0.030055f},{0.869061f,0.492341f,-0.0483173f}, +{0.857969f,0.51363f,0.0086094f},{0.914986f,-0.377543f,-0.142346f},{0.93273f,-0.351435f,-0.0806737f}, +{0.927026f,-0.374765f,-0.0131579f},{0.946514f,-0.00024888f,-0.322663f},{-0.276014f,0.950969f,-0.139548f}, +{-0.264043f,0.963609f,-0.0416947f},{-0.23099f,0.971773f,0.0479709f},{-0.156866f,0.9795f,0.126387f}, +{-0.314375f,0.932833f,0.176042f},{-0.0245953f,0.890615f,0.454092f},{-0.448809f,-0.0923177f,0.888846f}, +{-0.502441f,-0.0916905f,0.859736f},{0.276028f,0.933338f,-0.229541f},{0.314576f,0.894929f,-0.316455f}, +{0.285181f,0.94836f,-0.13887f},{0.110223f,0.0102139f,0.993854f},{0.211547f,0.0628589f,0.975344f}, +{0.321092f,-0.0486882f,0.945795f},{-0.0376397f,0.660927f,0.749506f},{-0.00832555f,-0.522454f,0.852627f}, +{-0.132767f,-0.384428f,0.913558f},{0.223466f,0.216042f,0.950468f},{0.238549f,0.376728f,0.895081f}, +{0.238472f,0.243627f,0.940094f},{0.302267f,0.125011f,0.94499f},{0.248175f,0.366485f,0.896715f}, +{0.168671f,0.349832f,0.921503f},{0.199354f,0.22033f,0.954836f},{0.273515f,0.176255f,0.945581f}, +{0.191339f,0.153628f,0.969427f},{-0.0112707f,0.119995f,0.99271f},{0.0725509f,0.159063f,0.984599f}, +{-0.00629055f,0.274279f,0.96163f},{0.277965f,0.117253f,0.953408f},{0.110563f,-0.343456f,0.932638f}, +{0.216365f,-0.123f,0.968534f},{0.900057f,0.245717f,0.359889f},{0.863173f,0.257873f,0.43409f}, +{0.881976f,0.160588f,0.443091f},{0.370564f,-0.353545f,0.858888f},{0.336535f,-0.0878704f,0.937562f}, +{0.256114f,-0.421237f,0.870037f},{0.905595f,0.0066398f,-0.424091f},{0.932738f,0.00768876f,-0.360474f}, +{0.937288f,0.0876965f,-0.337345f},{-0.0369066f,-0.945463f,0.323632f},{-0.118818f,-0.685618f,0.718199f}, +{-0.0967924f,-0.804577f,0.585907f},{0.916949f,0.263347f,-0.299756f},{0.898078f,0.264743f,-0.351236f}, +{-0.75019f,0.547434f,0.370853f},{-0.604395f,0.689198f,0.399641f},{-0.672897f,0.729871f,0.120408f}, +{-0.245761f,-0.73762f,-0.628903f},{-0.470701f,-0.346268f,-0.811504f},{0.0348372f,-0.579103f,-0.81451f}, +{-0.230634f,-0.210479f,0.950003f},{-0.274659f,-0.0711949f,0.958902f},{-0.350123f,-0.292919f,0.889726f}, +{-0.320295f,0.106153f,0.941352f},{-0.450114f,0.11922f,0.884977f},{0.136917f,0.164934f,0.976755f}, +{0.20114f,-0.423575f,0.883248f},{0.269589f,0.227421f,0.935736f},{0.256735f,0.173565f,0.950769f}, +{0.297017f,0.215377f,0.930266f},{0.300127f,-0.148128f,0.942328f},{-0.16051f,-0.429314f,-0.888778f}, +{-0.0358626f,-0.295419f,-0.954694f},{-0.0163357f,-0.445626f,-0.89507f},{0.458862f,-0.882519f,-0.10298f}, +{0.414609f,-0.88476f,-0.212836f},{0.489972f,-0.830752f,-0.264156f},{-0.079701f,-0.146064f,0.986059f}, +{-0.0857675f,-0.0777074f,0.99328f},{0.00161508f,-0.337113f,0.941463f},{0.280664f,-0.42611f,0.860034f}, +{-0.643998f,-0.577076f,-0.502246f},{-0.0959296f,-0.748871f,-0.655736f},{0.986673f,0.113871f,0.116232f}, +{0.948721f,0.230491f,0.21634f},{0.944986f,0.307393f,0.111848f},{0.960212f,-0.067786f,-0.27092f}, +{0.960641f,-0.049122f,-0.273417f},{0.967143f,-0.0398146f,-0.251094f},{0.91918f,0.127727f,-0.37255f}, +{0.949064f,0.123437f,-0.289897f},{0.568969f,0.205887f,0.796169f},{0.844097f,0.53266f,-0.0614229f}, +{0.137129f,0.880111f,0.454534f},{0.246395f,0.858197f,0.450319f},{-0.269803f,0.959994f,0.0749557f}, +{0.234905f,0.788011f,0.569085f},{-0.389489f,0.854769f,0.343027f},{-0.959211f,0.275599f,0.0629244f}, +{-0.953233f,0.301814f,0.0159892f},{-0.949614f,0.309566f,0.0490065f},{0.403744f,0.911277f,-0.0810251f}, +{0.923159f,0.364994f,0.120653f},{-0.62943f,-0.776664f,0.0247036f},{-0.652388f,-0.757587f,-0.0212481f}, +{-0.44652f,-0.894258f,-0.030358f},{-0.0901651f,-0.339501f,0.936274f},{-0.0379441f,-0.271751f,0.961619f}, +{-0.332831f,-0.401591f,0.853199f},{-0.0344199f,-0.209496f,0.977203f},{-0.126661f,-0.114418f,0.985325f}, +{-0.148426f,0.0111855f,0.98886f},{-0.295706f,0.235843f,0.925708f},{-0.369193f,0.300019f,0.879594f}, +{-0.309604f,0.364565f,0.878201f},{-0.187306f,0.220824f,0.957159f},{-0.158949f,0.314932f,0.93571f}, +{-0.161938f,0.456189f,0.875024f},{-0.148022f,0.444858f,0.883284f},{0.739283f,-0.653128f,-0.163966f}, +{0.86335f,-0.487178f,-0.131467f},{0.933787f,0.353346f,-0.0564575f},{-0.0460242f,-0.614627f,-0.787474f}, +{-0.113948f,-0.561688f,-0.819465f},{0.0168824f,-0.095401f,0.995296f},{-0.274895f,-0.200561f,0.940323f}, +{0.0946811f,0.635809f,0.766018f},{0.231209f,-0.764021f,0.60234f},{0.142163f,-0.126555f,0.98172f}, +{0.25344f,0.386675f,0.886708f},{0.250599f,0.501617f,0.827998f},{0.195129f,0.482999f,0.853602f}, +{0.0557201f,0.685491f,0.725946f},{-0.0774665f,0.667998f,0.74012f},{-0.0840826f,0.594974f,0.799335f}, +{0.01558f,0.551834f,0.833808f},{0.00736914f,0.617291f,0.786701f},{-0.0983184f,0.553799f,0.826825f}, +{0.18891f,0.283061f,-0.940314f},{0.178401f,0.241742f,-0.9538f},{0.100226f,0.273544f,-0.956623f}, +{0.769923f,-0.243603f,0.58981f},{0.77465f,-0.286361f,0.563839f},{0.69038f,-0.0458631f,0.721991f}, +{-0.327666f,0.777839f,0.536285f},{-0.342358f,0.791542f,0.506214f},{-0.327755f,0.859105f,0.393083f}, +{0.883778f,0.438968f,0.162f},{0.877874f,0.383199f,0.287219f},{0.0517423f,0.993679f,-0.0996198f}, +{0.13168f,0.990813f,-0.0308326f},{-0.229953f,-0.378124f,0.896741f},{0.140701f,0.329084f,0.93376f}, +{0.173597f,0.234941f,0.956382f},{0.286773f,-0.404816f,0.868266f},{0.409235f,-0.270151f,0.871519f}, +{-0.379485f,-0.523586f,-0.76279f},{-0.483885f,-0.460032f,-0.744464f},{-0.429615f,-0.568608f,-0.70151f}, +{0.165898f,0.0505138f,0.984848f},{0.185924f,0.175633f,0.96674f},{0.14884f,0.141895f,0.978628f}, +{0.0918044f,0.142985f,0.985458f},{0.0755089f,0.581447f,0.810073f},{0.333992f,-0.909345f,-0.248074f}, +{0.442823f,-0.869149f,-0.220199f},{0.669263f,-0.711774f,-0.213223f},{0.329347f,-0.663973f,0.671319f}, +{0.307978f,-0.627619f,0.715013f},{0.2937f,-0.525645f,0.798397f},{0.459327f,-0.888265f,0.00217148f}, +{0.553339f,-0.766244f,0.32663f},{0.416483f,-0.890913f,-0.18115f},{0.448413f,-0.472512f,0.758722f}, +{0.463214f,-0.583903f,0.666701f},{0.0228558f,0.153535f,0.987879f},{0.108558f,0.236491f,0.96555f}, +{0.728112f,-0.605165f,-0.321914f},{0.858305f,-0.313243f,-0.406437f},{0.599198f,0.0898905f,0.795539f}, +{0.819297f,0.491738f,0.294864f},{0.243573f,-0.893407f,-0.377487f},{0.196902f,-0.829986f,-0.521874f}, +{0.788151f,-0.606299f,0.105922f},{-0.0230661f,0.00367931f,-0.999727f},{0.0283941f,0.0276503f,-0.999214f}, +{0.0669312f,0.0129073f,-0.997674f},{-0.334345f,-0.481378f,0.81024f},{-0.309593f,-0.428943f,0.848622f}, +{-0.302578f,-0.400369f,0.864958f},{-0.254114f,0.379369f,0.889666f},{0.498502f,0.24239f,0.832312f}, +{0.615686f,0.102185f,0.781338f},{0.553289f,0.0582414f,0.830951f},{-0.00204334f,-0.480179f,0.877168f}, +{-0.0856082f,-0.206093f,0.97478f},{0.327397f,-0.357655f,0.874582f},{0.215444f,-0.466779f,0.85773f}, +{0.162921f,-0.132008f,0.977768f},{0.0647925f,0.233047f,0.970305f},{0.111038f,0.205046f,0.972433f}, +{0.0158613f,-0.0419174f,0.998995f},{0.341844f,-0.269747f,0.900211f},{0.116874f,-0.149929f,0.981765f}, +{0.191889f,-0.0992769f,0.976382f},{0.0101111f,0.773999f,0.633107f},{-0.0530789f,0.700445f,0.71173f}, +{0.511712f,-0.845175f,-0.154368f},{0.850173f,-0.499442f,-0.166622f},{0.846485f,-0.503522f,-0.172997f}, +{0.82965f,-0.522815f,-0.195821f},{0.023405f,-0.999625f,-0.0142142f},{0.0242401f,-0.999656f,-0.00998008f}, +{-0.000303005f,-0.999841f,-0.017843f},{-0.933294f,0.332437f,0.135822f},{-0.881595f,0.351294f,0.31525f}, +{-0.928424f,0.283676f,0.239911f},{-0.772711f,0.476617f,-0.41923f},{-0.402891f,0.911185f,-0.0861461f}, +{-0.76101f,0.648736f,0.0022703f},{-0.525614f,0.828955f,-0.191216f},{-0.187238f,0.982224f,-0.0133145f}, +{-0.147426f,0.988429f,-0.0356911f},{-0.248234f,-0.244597f,-0.937311f},{-0.198606f,-0.362544f,-0.910559f}, +{0.3608f,-0.683461f,-0.63459f},{0.0953136f,0.0853679f,-0.99178f},{0.51823f,-0.849414f,-0.0996677f}, +{0.814328f,-0.553142f,-0.175794f},{0.748936f,-0.635619f,-0.187304f},{-0.191851f,-0.439151f,0.87769f}, +{-0.228831f,-0.274946f,0.933831f},{-0.144682f,-0.240387f,0.959834f},{0.0186087f,-0.229308f,0.973176f}, +{0.0844466f,-0.272325f,0.958493f},{0.048156f,-0.214678f,0.975497f},{0.0185073f,-0.0533244f,0.998406f}, +{-0.0645704f,0.122728f,0.990338f},{-0.0960451f,0.353497f,0.930492f},{-0.123316f,0.466299f,0.87599f}, +{-0.101722f,0.334442f,0.93691f},{-0.130158f,0.278541f,0.951564f},{-0.134554f,0.476146f,0.869011f}, +{-0.0530084f,0.525905f,0.84889f},{-0.164811f,0.321452f,0.932473f},{0.0823322f,0.227239f,0.970353f}, +{-0.0657198f,0.130476f,0.989271f},{-0.0768859f,-0.720243f,0.689448f},{0.239307f,0.762061f,0.601661f}, +{0.112851f,0.567339f,0.815715f},{0.141879f,0.648949f,0.747486f},{0.203347f,0.494481f,0.845067f}, +{-0.0796564f,-0.697736f,0.711913f},{-0.0953443f,-0.671122f,0.73519f},{-0.964209f,-0.241382f,0.109707f}, +{-0.972866f,-0.118573f,0.198676f},{-0.962037f,-0.177494f,0.207316f},{0.376109f,0.345327f,0.859821f}, +{0.260354f,0.334831f,0.905596f},{0.413984f,0.0796125f,0.906796f},{0.206149f,0.427033f,0.880423f}, +{0.255093f,0.478282f,0.840342f},{0.186982f,0.425432f,0.885463f},{0.188722f,0.418679f,0.888308f}, +{0.932563f,-0.313456f,-0.179087f},{0.00751608f,-0.992196f,0.124464f},{0.61083f,0.754516f,0.239982f}, +{0.210359f,0.975986f,0.0565786f},{0.237949f,0.971085f,-0.0193416f},{-0.992479f,-0.119535f,-0.0264097f}, +{-0.991905f,0.122125f,-0.0347969f},{-0.994124f,0.0788717f,-0.0741417f},{0.317099f,0.917356f,0.240638f}, +{-0.9839f,0.176112f,-0.0304254f},{-0.229285f,0.931458f,-0.282516f},{-0.249293f,0.95181f,-0.178636f}, +{-0.380595f,-0.0953641f,-0.919811f},{-0.356386f,-0.185595f,-0.91572f},{-0.532308f,0.33788f,-0.7762f}, +{-0.623105f,0.373117f,-0.687404f},{-0.591311f,0.340755f,-0.730915f},{-0.0939356f,0.50456f,-0.858251f}, +{-0.0584824f,0.31504f,-0.947275f},{-0.0717267f,0.392258f,-0.917055f},{-0.409762f,-0.0557136f,-0.91049f}, +{-0.432608f,0.0796261f,-0.898059f},{-0.290013f,-0.0614834f,-0.955046f},{-0.253383f,-0.515742f,0.818418f}, +{-0.147352f,0.43482f,0.88838f},{0.948899f,-0.0521516f,-0.311241f},{0.175427f,0.203394f,-0.963253f}, +{0.284098f,-0.0864908f,0.954886f},{0.156203f,-0.102557f,0.982386f},{-0.0343121f,-0.670962f,0.740697f}, +{0.282425f,0.113884f,0.952505f},{0.252265f,-0.0799734f,0.964348f},{0.608191f,-0.555626f,-0.566907f}, +{-0.281625f,0.0199879f,-0.959316f},{-0.321533f,0.14723f,-0.935382f},{0.00403764f,-0.409977f,0.912087f}, +{0.296786f,0.412455f,0.861278f},{0.314424f,0.491877f,0.811908f},{0.15597f,-0.148834f,0.976484f}, +{-0.265697f,-0.575302f,0.773585f},{-0.0127738f,-0.679576f,0.733494f},{-0.326234f,-0.480712f,0.813933f}, +{-0.872833f,-0.464274f,-0.150372f},{-0.994023f,0.0946819f,-0.0543493f},{-0.550761f,-0.834547f,0.0139191f}, +{0.0263528f,0.37496f,0.926666f},{0.216957f,0.471372f,0.854832f},{0.778666f,0.0794795f,0.622385f}, +{0.595223f,-0.107183f,0.79638f},{0.0160022f,0.466013f,0.884633f},{-0.217392f,0.653551f,0.72499f}, +{-0.331892f,-0.59942f,-0.728384f},{-0.200273f,-0.752239f,-0.627716f},{0.510712f,-0.829747f,-0.225151f}, +{0.546848f,-0.823202f,-0.15263f},{0.255635f,-0.202235f,0.945384f},{0.233842f,-0.286493f,0.929107f}, +{0.308997f,0.326208f,0.893369f},{0.539946f,0.154095f,0.827474f},{0.536519f,0.226703f,0.812867f}, +{-0.013535f,0.996342f,0.0843795f},{-0.430286f,-0.0933455f,-0.897853f},{-0.293771f,-0.0785507f,-0.952643f}, +{-0.316683f,-0.174593f,-0.932325f},{-0.616447f,0.263836f,-0.741878f},{-0.584456f,0.323832f,-0.744006f}, +{-0.533083f,0.332606f,-0.777943f},{-0.470009f,0.0371177f,-0.881881f},{-0.313697f,-0.230665f,-0.92108f}, +{-0.095108f,-0.430469f,0.89758f},{-0.205516f,-0.31961f,0.924993f},{-0.271736f,-0.129682f,0.953594f}, +{-0.16233f,-0.128129f,0.978382f},{-0.103393f,-0.147975f,0.983572f},{0.0371042f,-0.0618592f,0.997395f}, +{0.0800676f,0.0692281f,0.994383f},{0.0655334f,0.326109f,0.943058f},{0.0500331f,0.458989f,0.887032f}, +{-0.012976f,0.250275f,0.968088f},{-0.00278086f,0.254525f,0.967062f},{-0.00602506f,0.403435f,0.914989f}, +{-0.0166253f,0.507921f,0.861243f},{-0.066194f,0.569683f,0.819194f},{0.00612044f,0.50687f,0.862001f}, +{0.087804f,0.358904f,0.929235f},{0.135736f,0.0527765f,0.989338f},{0.219893f,0.0168975f,0.975378f}, +{0.155385f,0.67077f,0.725205f},{0.0911761f,0.307906f,0.947038f},{0.380764f,-0.112959f,0.917747f}, +{0.484482f,-0.25873f,0.835665f},{0.173278f,-0.0255334f,0.984542f},{-0.119044f,0.0165311f,0.992751f}, +{-0.0621672f,-0.226799f,0.971955f},{0.168582f,-0.017391f,0.985534f},{0.192135f,-0.114706f,0.974642f}, +{0.0907603f,0.0712779f,0.993319f},{0.900404f,0.273238f,-0.338546f},{0.867039f,0.330615f,-0.372743f}, +{0.817124f,0.438432f,-0.374281f},{0.272694f,0.0257412f,0.961756f},{-0.264704f,0.876336f,0.402451f}, +{-0.374633f,0.788444f,0.487858f},{-0.239863f,-0.388392f,-0.889729f},{0.322604f,-0.898675f,-0.297171f}, +{0.110378f,0.0512505f,-0.992567f},{0.195878f,0.100581f,-0.975456f},{-0.299195f,0.368799f,-0.88004f}, +{-0.281542f,0.416945f,-0.864229f},{-0.334622f,0.516404f,-0.788261f},{-0.388498f,-0.387132f,-0.836181f}, +{-0.413425f,-0.223328f,-0.882726f},{-0.528806f,-0.229046f,-0.817253f},{-0.567126f,0.252032f,-0.784123f}, +{-0.0622147f,-0.575088f,0.815722f},{-0.262476f,-0.0975698f,0.959993f},{-0.27464f,-0.0410216f,0.960672f}, +{0.863329f,0.259147f,-0.43302f},{0.79449f,0.262755f,-0.547491f},{0.948858f,-0.049514f,-0.311796f}, +{-0.00933192f,0.0898895f,0.995908f},{-0.0154903f,0.0603768f,0.998055f},{0.233006f,0.519745f,0.821933f}, +{0.252677f,0.61801f,0.744458f},{-0.995603f,0.0694375f,-0.062876f},{-0.984647f,0.104033f,-0.140169f}, +{0.834566f,0.388194f,-0.390903f},{0.776226f,0.519924f,-0.356584f},{0.231295f,-0.0027237f,0.97288f}, +{0.123915f,0.171485f,0.977363f},{-0.278264f,0.885621f,0.371812f},{-0.208952f,0.742162f,0.636816f}, +{0.328508f,0.831803f,0.447423f},{-0.790702f,0.415924f,-0.449218f},{-0.592127f,0.348571f,-0.726556f}, +{-0.830674f,0.424403f,-0.360365f},{-0.62552f,0.184799f,-0.758006f},{-0.707192f,0.19258f,-0.680289f}, +{-0.613638f,0.127083f,-0.779293f},{-0.11711f,0.556457f,-0.822582f},{-0.179243f,-0.348841f,0.919881f}, +{0.214004f,0.377502f,0.900941f},{0.205075f,0.336747f,0.918992f},{0.174838f,0.422701f,0.889244f}, +{0.995333f,0.0317781f,-0.0911228f},{0.190036f,0.479935f,0.856475f},{0.231393f,0.433377f,0.871f}, +{0.0713411f,0.536196f,0.841073f},{0.461727f,-0.531031f,0.710503f},{0.642623f,-0.481829f,0.595716f}, +{0.0459413f,-0.172653f,-0.983911f},{-0.0211f,-0.331505f,-0.943218f},{-0.40236f,-0.188535f,-0.895858f}, +{-0.0220802f,0.497801f,0.86701f},{-0.261378f,0.528952f,0.807398f},{-0.310862f,0.246872f,0.917834f}, +{0.0737522f,0.294186f,0.952898f},{-0.0285864f,0.741674f,0.670151f},{0.130811f,0.779388f,0.612734f}, +{-0.329985f,0.70659f,0.625971f},{-0.150434f,0.157564f,-0.975983f},{-0.491554f,0.143916f,-0.858873f}, +{-0.549467f,0.207409f,-0.809362f},{0.0243846f,-0.998918f,-0.0395968f},{0.0154174f,-0.997923f,-0.0625502f}, +{-9.56649e-05f,-0.999511f,-0.031265f},{-0.0736797f,-0.847043f,0.526393f},{0.264565f,-0.526851f,0.807734f}, +{0.0778558f,-0.911717f,0.403374f},{-0.0722179f,-0.489121f,0.869221f},{-0.0497511f,-0.601297f,0.797475f}, +{0.0221068f,-0.414748f,0.909668f},{-0.024475f,-0.345695f,0.938028f},{-0.00234594f,-0.214816f,0.976652f}, +{-0.13038f,-0.0390172f,0.990696f},{-0.113822f,-0.00113113f,0.9935f},{0.00753531f,0.0784319f,0.996891f}, +{0.162659f,0.186062f,0.96898f},{0.197465f,0.373061f,0.90655f},{0.0726495f,0.270119f,0.960082f}, +{-0.00348439f,0.248771f,0.968556f},{0.132326f,0.32448f,0.936591f},{0.125685f,0.424566f,0.896631f}, +{0.153659f,0.430838f,0.889251f},{0.0608871f,0.531499f,0.844868f},{0.0273649f,0.554899f,0.831467f}, +{0.23097f,0.2435f,0.941998f},{0.391681f,-0.408344f,0.824525f},{-0.0899755f,-0.418629f,0.903689f}, +{-0.028856f,-0.230136f,0.972731f},{0.192937f,0.450434f,0.871714f},{0.304815f,0.404069f,0.862448f}, +{0.121643f,0.294766f,0.947795f},{0.0704177f,0.301208f,0.950955f},{-0.0185777f,0.496515f,0.867829f}, +{0.30631f,-0.12352f,0.943884f},{0.0381325f,-0.0587734f,0.997543f},{0.168162f,0.216513f,0.961688f}, +{0.17938f,0.201108f,0.963005f},{0.752987f,-0.233579f,-0.615184f},{0.752213f,-0.142831f,-0.643253f}, +{0.770254f,-0.2229f,-0.597515f},{-0.0432317f,0.381112f,0.923518f},{0.626181f,-0.779599f,0.0110793f}, +{0.621676f,-0.776663f,0.101553f},{0.521547f,-0.846536f,0.106608f},{0.344659f,-0.897823f,-0.274087f}, +{0.170478f,0.931913f,0.320119f},{0.0544409f,0.956743f,0.285794f},{-0.285666f,0.883754f,0.370641f}, +{-0.486945f,0.656165f,0.576482f},{-0.475298f,0.151973f,-0.8666f},{-0.169494f,-0.477562f,-0.862094f}, +{-0.0223069f,-0.554443f,-0.831923f},{0.19763f,0.0676612f,-0.977939f},{0.12502f,0.240439f,-0.96258f}, +{0.096951f,0.348996f,-0.932096f},{0.106352f,0.388327f,0.915364f},{0.186485f,0.465086f,0.865401f}, +{0.085202f,0.629671f,0.772176f},{-0.640431f,-0.404267f,-0.653006f},{-0.780979f,-0.316721f,-0.538294f}, +{-0.865468f,-0.312483f,-0.39156f},{0.0972787f,-0.197405f,0.975483f},{-0.0786699f,0.434336f,0.897309f}, +{-0.076951f,0.450712f,0.889346f},{-0.0668306f,0.439939f,0.895537f},{0.156395f,0.469386f,0.869033f}, +{0.394926f,-0.0689772f,0.91612f},{0.144095f,0.697262f,0.702184f},{0.268524f,0.659339f,0.702259f}, +{0.137937f,0.582651f,0.800932f},{-0.150031f,0.52089f,0.840336f},{-0.157645f,0.567902f,0.807858f}, +{-0.0967734f,0.630328f,0.770274f},{0.622746f,0.181545f,0.761071f},{0.239313f,0.574192f,0.782964f}, +{0.108848f,0.65576f,0.747082f},{0.711315f,0.172817f,0.681297f},{0.874118f,0.338625f,-0.348211f}, +{0.849983f,0.440713f,-0.288621f},{0.82446f,0.469055f,0.316627f},{0.839611f,0.504318f,0.201784f}, +{0.839027f,0.51583f,0.173072f},{-0.0827286f,0.994895f,-0.0577841f},{-0.0780838f,0.996946f,-0.00112269f}, +{-0.0196054f,0.999522f,-0.0238855f},{0.331187f,0.80367f,0.494399f},{0.464725f,0.734652f,0.494285f}, +{-0.657859f,0.081471f,0.748722f},{-0.601514f,-0.443733f,0.66429f},{-0.284661f,-0.303841f,0.909202f}, +{-0.573281f,0.338316f,-0.746251f},{-0.481339f,0.161541f,-0.861521f},{-0.512204f,0.100924f,-0.852913f}, +{-0.362885f,0.109735f,-0.92535f},{-0.104948f,0.555117f,0.825125f},{0.165705f,0.470171f,0.86688f}, +{0.168167f,0.431262f,0.886416f},{0.162941f,-0.206219f,0.964844f},{-0.0583317f,-0.297053f,0.953078f}, +{0.354429f,-0.0278913f,0.934667f},{0.251351f,0.451336f,0.856223f},{0.280874f,0.479454f,0.831405f}, +{-0.140207f,0.763332f,0.630608f},{0.0888948f,0.824089f,0.559442f},{0.236753f,0.565998f,0.78968f}, +{0.276147f,0.490722f,0.826399f},{0.165164f,0.456367f,0.874328f},{0.884691f,0.122447f,0.44981f}, +{0.470696f,0.578142f,0.666482f},{0.885033f,-0.390748f,0.253046f},{0.629966f,-0.274177f,-0.726615f}, +{0.647971f,-0.158293f,-0.745035f},{0.611495f,-0.0616235f,-0.788845f},{0.639939f,-0.751124f,-0.162144f}, +{0.673719f,-0.105217f,-0.731459f},{0.639491f,-0.601652f,0.478608f},{0.513526f,-0.849419f,0.121567f}, +{0.610413f,-0.647956f,0.455575f},{0.334601f,0.299348f,0.893551f},{0.0441093f,0.711599f,0.7012f}, +{0.181251f,0.742361f,0.645018f},{0.00122868f,0.846108f,0.53301f},{-0.281939f,0.868014f,0.408732f}, +{-0.471426f,0.186922f,-0.861869f},{-0.580898f,0.240434f,-0.777656f},{-0.583811f,0.05227f,-0.810205f}, +{0.0320676f,-0.999039f,-0.0298842f},{0.018545f,-0.99914f,-0.0370961f},{0.0146236f,-0.99989f,-0.0025338f}, +{-0.0617785f,-0.382466f,0.921902f},{-0.00404173f,-0.25056f,0.968093f},{0.122372f,-0.278325f,0.95266f}, +{0.0523822f,-0.161823f,0.985429f},{-0.0111141f,-0.0130864f,0.999853f},{0.0363037f,0.192458f,0.980633f}, +{-0.0404656f,0.143099f,0.988881f},{0.0160188f,0.175992f,0.984261f},{-0.210937f,0.0895094f,0.973393f}, +{-0.109152f,0.311871f,0.943834f},{0.0474923f,0.41157f,0.91014f},{0.0645066f,0.39909f,0.91464f}, +{-0.00659182f,0.496609f,0.867949f},{-0.0486732f,0.553081f,0.831704f},{0.0117206f,0.595074f,0.803586f}, +{0.0765092f,0.587289f,0.805753f},{0.347041f,0.370975f,0.861359f},{0.0452194f,0.614496f,0.787623f}, +{0.00440938f,0.620018f,0.784575f},{0.0859346f,0.688023f,0.720583f},{0.0447807f,0.383445f,0.922477f}, +{0.195784f,0.612478f,0.765859f},{0.145692f,0.695086f,0.704009f},{-0.00125758f,0.736867f,0.676037f}, +{0.24172f,0.519188f,0.819766f},{0.285311f,0.497986f,0.818906f},{0.460259f,-0.715681f,-0.525322f}, +{0.983586f,0.0216339f,0.179138f},{0.943684f,0.208261f,0.257076f},{0.981252f,0.0914436f,0.169655f}, +{0.14865f,0.0434738f,0.987934f},{-0.0207364f,0.626854f,0.778861f},{0.0720591f,0.436571f,0.89678f}, +{0.139963f,0.204553f,0.968797f},{-0.995807f,-0.0625395f,0.066763f},{-0.212539f,0.290927f,-0.932839f}, +{-0.418814f,0.226659f,-0.87933f},{0.0559445f,-0.155077f,0.986317f},{-0.202485f,-0.659883f,-0.723571f}, +{0.271867f,0.425229f,0.863289f},{0.235252f,0.505083f,0.83039f},{-0.14351f,0.68616f,0.713155f}, +{-0.393631f,0.778218f,0.489317f},{-0.220183f,0.743723f,0.631186f},{0.00776272f,0.335611f,0.941969f}, +{0.91128f,0.340794f,-0.231146f},{0.876158f,0.161572f,0.454138f},{0.824709f,0.261035f,0.501712f}, +{-0.512031f,0.218737f,0.83065f},{0.299492f,0.57243f,0.763301f},{0.443001f,0.393616f,0.805491f}, +{-0.350699f,0.860797f,0.368836f},{-0.485456f,0.0648393f,-0.871853f},{-0.570127f,0.246992f,-0.78355f}, +{-0.483251f,-0.0510707f,-0.873991f},{-0.510278f,-0.349902f,-0.785611f},{-0.500311f,-0.348015f,-0.792827f}, +{0.121175f,0.0514562f,-0.991297f},{-0.881409f,-0.279087f,-0.381089f},{-0.51307f,-0.43176f,-0.741851f}, +{-0.655812f,-0.39313f,-0.644484f},{-0.156642f,0.119218f,0.980434f},{-0.0217293f,0.0257904f,0.999431f}, +{-0.637142f,0.394017f,0.66242f},{0.108426f,0.88485f,0.453084f},{0.426378f,-0.185955f,0.885225f}, +{-0.0933708f,0.463751f,0.881032f},{-0.0882345f,0.196905f,0.976444f},{0.0172453f,0.226536f,0.97385f}, +{-0.0643816f,0.38211f,0.921871f},{-0.0728973f,0.463505f,0.88309f},{0.99305f,0.0973475f,0.0661499f}, +{0.993768f,0.090838f,0.0645981f},{0.99306f,0.0946657f,0.0697913f},{0.477953f,0.386349f,0.788857f}, +{0.0407022f,0.683789f,0.728544f},{0.0318931f,0.748566f,0.662293f},{0.181964f,-0.732915f,-0.655534f}, +{0.243709f,0.468222f,0.849337f},{0.153119f,0.654531f,0.740367f},{0.0758308f,0.737048f,0.671573f}, +{-0.17887f,0.637327f,0.749546f},{-0.296601f,0.712073f,0.63638f},{-0.541293f,0.466978f,0.699238f}, +{-0.566555f,0.696726f,0.439987f},{-0.134021f,0.932858f,0.334385f},{0.0218045f,-0.997464f,0.0677578f}, +{0.0271397f,-0.997929f,0.0583154f},{0.0252167f,-0.998572f,0.0471037f},{-0.621606f,-0.278433f,0.732176f}, +{-0.570494f,-0.50975f,0.643965f},{-0.681334f,-0.207411f,0.701972f},{-0.559511f,-0.36605f,-0.74361f}, +{0.493383f,0.0945182f,-0.864661f},{0.584445f,-0.0956861f,-0.805772f},{0.611821f,0.0811993f,-0.786817f}, +{0.0579123f,-0.316769f,0.946733f},{-0.0781528f,-0.569633f,0.818175f},{-0.956457f,0.0398561f,0.289139f}, +{-0.992877f,0.118206f,-0.0149156f},{-0.767728f,-0.639771f,0.0358726f},{0.183182f,-0.187221f,0.965087f}, +{0.158643f,-0.200877f,0.966685f},{0.0660394f,-0.240335f,0.968441f},{0.129513f,-0.216463f,0.967662f}, +{0.17963f,0.059801f,0.981915f},{0.216525f,0.230871f,0.948586f},{0.13377f,-0.00599154f,0.990994f}, +{0.13257f,0.0957259f,0.98654f},{0.147001f,0.309975f,0.939311f},{-0.0514512f,0.392173f,0.918452f}, +{-0.0894499f,0.442738f,0.892178f},{-0.184403f,0.382964f,0.905171f},{-0.0898477f,0.43927f,0.893851f}, +{-0.0528206f,0.613729f,0.787748f},{0.0169646f,0.649408f,0.760251f},{0.0341286f,0.644321f,0.763993f}, +{0.163776f,0.613155f,0.772799f},{-0.346824f,0.758038f,0.552352f},{-0.000173428f,0.166072f,0.986114f}, +{0.94866f,-0.152144f,-0.277302f},{0.980893f,-0.150273f,-0.12356f},{0.938342f,-0.31065f,-0.151691f}, +{0.0818533f,-0.546118f,0.833699f},{0.994091f,0.0830656f,0.0698837f},{0.993339f,0.0874817f,0.0749937f}, +{-0.227107f,0.963144f,-0.144138f},{-0.10378f,-0.515128f,0.850807f},{0.315257f,0.0500735f,0.947684f}, +{0.264788f,0.243961f,0.932936f},{0.296313f,0.265407f,0.917473f},{0.328641f,0.479454f,0.813707f}, +{0.229493f,0.231328f,0.945421f},{0.336808f,0.301467f,0.892008f},{0.232176f,0.498203f,0.835397f}, +{0.962198f,0.269438f,0.0397187f},{0.953436f,0.295835f,-0.0586683f},{0.942935f,0.331643f,0.0297584f}, +{0.198719f,0.341757f,0.918538f},{0.44266f,0.233925f,0.865639f},{-0.302218f,-0.92107f,0.245547f}, +{-0.468439f,-0.879868f,0.079976f},{-0.273014f,-0.949768f,-0.152985f},{0.279096f,0.87342f,0.399053f}, +{0.109999f,0.983735f,0.142007f},{0.33597f,0.0460234f,0.940748f},{-0.0755804f,0.557677f,0.82661f}, +{0.469378f,0.32101f,0.822579f},{-0.417018f,0.693837f,0.587099f},{-0.996744f,-0.0371724f,0.071545f}, +{0.202101f,0.433878f,0.878012f},{0.129701f,0.607736f,0.783476f},{0.0245816f,0.617155f,0.786458f}, +{-0.161749f,0.406649f,0.899152f},{-0.370242f,0.314838f,0.873956f},{0.32194f,0.854132f,0.408429f}, +{0.229067f,0.971816f,-0.0556929f},{-0.215353f,-0.504048f,0.836397f},{-0.165197f,-0.256448f,0.952336f}, +{-0.0296231f,-0.105381f,0.993991f},{0.0989861f,-0.0810697f,0.991781f},{0.323746f,-0.196872f,0.925435f}, +{0.343294f,-0.202076f,0.917232f},{0.380756f,0.0343652f,0.924037f},{0.377954f,0.253315f,0.890496f}, +{0.352625f,0.162393f,0.921566f},{0.235176f,0.0282973f,0.971541f},{0.278209f,0.0768769f,0.957439f}, +{0.18879f,0.230847f,0.954499f},{0.100841f,0.209443f,0.972607f},{-0.0516694f,0.294141f,0.954364f}, +{-0.0359873f,0.460162f,0.887105f},{0.0150075f,0.491099f,0.870974f},{0.0174386f,0.594376f,0.803998f}, +{0.0599003f,0.645757f,0.76119f},{0.0237448f,0.664496f,0.746914f},{-0.0117554f,0.643774f,0.765126f}, +{-0.00783735f,0.687184f,0.726442f},{-0.0677373f,0.701206f,0.709734f},{0.14681f,0.639021f,0.755049f}, +{0.161852f,-0.766316f,-0.621742f},{0.313848f,0.615783f,0.722711f},{0.357795f,-0.814972f,-0.455855f}, +{0.577611f,-0.687363f,-0.440337f},{0.0259876f,-0.995169f,0.0946733f},{0.030387f,-0.997499f,0.063819f}, +{0.0198724f,-0.996136f,0.0855424f},{0.368854f,0.251844f,0.894719f},{0.22752f,0.354024f,0.907139f}, +{0.0791086f,0.471746f,0.878178f},{0.827758f,0.36769f,-0.423817f},{0.845807f,0.387535f,-0.366644f}, +{0.240204f,0.227618f,0.943659f},{-0.370066f,0.155968f,0.915819f},{0.022549f,-0.996353f,0.0822977f}, +{0.0262031f,-0.996683f,0.0770459f},{0.416581f,-0.150609f,0.896536f},{0.835817f,-0.534199f,-0.126657f}, +{0.903079f,-0.405958f,-0.140167f},{0.222092f,-0.177756f,0.958686f},{0.0364991f,-0.0694043f,0.996921f}, +{0.0238959f,-0.354001f,0.93494f},{0.230632f,0.323804f,0.917583f},{0.144523f,-0.435178f,0.888669f}, +{-0.1093f,-0.210785f,0.971403f},{0.821085f,0.318833f,-0.47346f},{-0.754431f,0.566074f,-0.332255f}, +{-0.757881f,0.423543f,-0.496214f},{-0.82559f,0.142302f,-0.546032f},{-0.847383f,-0.192788f,-0.494747f}, +{-0.914762f,-0.15749f,-0.372033f},{0.276055f,0.263812f,0.924228f},{-0.0770771f,-0.957563f,-0.277728f}, +{0.139261f,-0.967934f,-0.209068f},{0.321303f,-0.905573f,-0.276951f},{0.431478f,0.0505868f,0.900704f}, +{0.249985f,0.200263f,0.947313f},{0.304387f,0.278311f,0.910984f},{-0.0499762f,-0.111835f,0.992469f}, +{0.246997f,0.369415f,0.895838f},{0.101263f,0.450865f,0.886829f},{0.0237676f,0.496742f,0.867573f}, +{-0.0263901f,0.301213f,0.953192f},{-0.141989f,0.111937f,0.983519f},{-0.179951f,-0.0840402f,0.980079f}, +{-0.385978f,-0.0345515f,0.921861f},{-0.371599f,-0.166121f,0.91341f},{-0.125026f,-0.19002f,0.973787f}, +{-0.373366f,-0.377655f,0.847333f},{-0.2744f,-0.324717f,0.905132f},{-0.0187571f,-0.159511f,0.987018f}, +{-0.0551324f,-0.088822f,0.994521f},{-0.0779111f,0.0233012f,0.996688f},{-0.05431f,0.00554412f,0.998509f}, +{0.0110976f,-0.11894f,0.992839f},{0.116006f,0.0256008f,0.992919f},{-0.00714221f,0.0399637f,0.999176f}, +{0.0629546f,0.0515646f,0.996683f},{0.104175f,0.0657995f,0.99238f},{0.123421f,0.142901f,0.982011f}, +{0.199415f,0.236513f,0.950944f},{0.161276f,0.334805f,0.928383f},{0.278101f,0.368124f,0.887211f}, +{0.249535f,0.469344f,0.847023f},{0.138731f,0.593551f,0.792749f},{0.0456122f,0.613564f,0.788327f}, +{0.00602844f,0.598335f,0.801223f},{-0.0404027f,0.653058f,0.756229f},{-0.113609f,0.70629f,0.698747f}, +{-0.149797f,0.759453f,0.633082f},{-0.0969605f,0.157836f,0.982693f},{0.30952f,0.343163f,0.886812f}, +{0.250895f,0.239551f,0.937905f},{0.364302f,0.214746f,0.906183f},{0.677437f,-0.618769f,-0.39775f}, +{0.403265f,0.340129f,0.849523f},{0.361741f,-0.604613f,-0.709638f},{0.520482f,-0.635742f,-0.570027f}, +{-0.220053f,-0.110282f,0.969234f},{0.664744f,0.0727426f,-0.743522f},{0.69493f,-0.197012f,-0.691562f}, +{0.711349f,0.0990558f,-0.695824f},{0.791779f,-0.192596f,-0.579648f},{0.83793f,0.467497f,-0.281638f}, +{0.803215f,0.569194f,-0.175682f},{0.265927f,-0.221511f,-0.938198f},{0.672927f,-0.206138f,-0.710405f}, +{0.543858f,0.126279f,-0.829622f},{0.543691f,-0.808459f,0.225377f},{0.576513f,-0.710938f,0.402741f}, +{-0.956165f,0.283562f,0.0730865f},{-0.822554f,0.542533f,0.170477f},{-0.703695f,0.6879f,0.177784f}, +{-0.602137f,0.489269f,-0.630909f},{-0.532431f,0.695438f,-0.48258f},{-0.390972f,0.546069f,-0.740911f}, +{-0.255914f,-0.161228f,0.95316f},{-0.384142f,-0.179441f,0.905669f},{0.243979f,0.262092f,0.933693f}, +{0.99634f,-0.0342846f,0.0783019f},{0.709472f,-0.120123f,-0.694421f},{-0.0644449f,0.0727868f,0.995263f}, +{0.775684f,0.125389f,-0.618541f},{-0.107575f,-0.100668f,0.989087f},{0.0138886f,0.527502f,0.84944f}, +{-0.772811f,0.296101f,-0.561326f},{-0.742911f,0.337742f,-0.577939f},{-0.795858f,0.46664f,-0.385819f}, +{0.475773f,0.8358f,-0.274004f},{0.0905323f,0.351765f,0.9317f},{0.141134f,0.3954f,0.907601f}, +{0.181249f,0.353132f,0.917849f},{0.0710616f,0.250557f,0.96549f},{0.0950218f,0.0252217f,0.995156f}, +{0.093489f,-0.137843f,0.986032f},{0.0487989f,-0.207587f,0.976999f},{-0.183824f,-0.148739f,0.97164f}, +{-0.0301569f,-0.227055f,0.973415f},{-0.292098f,-0.143991f,0.945487f},{-0.075602f,-0.179751f,0.980803f}, +{-0.0417652f,-0.0777302f,0.996099f},{0.00792155f,-0.0523368f,0.998598f},{0.0587392f,-0.104993f,0.992737f}, +{0.118203f,-0.133507f,0.983974f},{0.0643076f,-0.145089f,0.987327f},{-0.146965f,-0.0484049f,0.987957f}, +{-0.147134f,-0.0615316f,0.987201f},{-0.108244f,0.067494f,0.991831f},{-0.113617f,0.0485507f,0.992338f}, +{-0.238631f,0.0427924f,0.970167f},{-0.16731f,0.157442f,0.973252f},{-0.0554322f,0.247821f,0.967219f}, +{0.0215249f,0.307941f,0.951162f},{0.089122f,0.370946f,0.924368f},{0.173814f,0.436493f,0.882758f}, +{0.334641f,0.366563f,0.868129f},{0.201333f,0.420597f,0.884626f},{0.107478f,0.499501f,0.859621f}, +{0.0882418f,0.554137f,0.827735f},{0.077174f,0.58413f,0.807983f},{0.0904184f,0.624511f,0.775765f}, +{0.1935f,0.237683f,0.951874f},{0.305689f,0.296056f,0.904934f},{0.327435f,0.184844f,0.926617f}, +{0.269561f,-0.927616f,-0.258583f},{0.193659f,-0.895755f,-0.400149f},{0.394681f,0.163975f,0.904068f}, +{0.258788f,0.468532f,0.844693f},{-0.0718795f,0.684182f,0.72576f},{0.683177f,-0.701106f,-0.204252f}, +{-0.516514f,0.796576f,-0.314133f},{-0.720986f,-0.629455f,-0.289768f},{-0.779838f,-0.545293f,-0.307421f}, +{-0.89702f,-0.302861f,-0.321916f},{-0.483131f,0.87171f,0.081887f},{-0.489306f,0.870408f,-0.0544948f}, +{-0.717503f,0.692303f,0.0768497f},{0.957277f,-0.28916f,0.00262905f},{0.988026f,-0.0837556f,0.129575f}, +{0.24601f,0.323063f,0.913843f},{0.604944f,-0.0749556f,0.792732f},{-0.155861f,0.510953f,0.84536f}, +{0.149976f,0.587202f,0.795425f},{0.0187902f,-0.999677f,-0.0171062f},{0.025801f,-0.999018f,-0.036023f}, +{-0.00739064f,-0.99961f,-0.0269177f},{0.678978f,-0.0969692f,0.727727f},{0.269299f,0.415525f,0.868802f}, +{-0.549574f,0.640399f,-0.536524f},{-0.634002f,0.560561f,-0.532741f},{0.629036f,0.600896f,0.49319f}, +{0.7046f,0.544216f,0.455376f},{0.806775f,0.433922f,0.401032f},{-0.349169f,0.43309f,0.830972f}, +{-0.249238f,0.533495f,0.808247f},{0.223309f,0.158713f,0.96174f},{0.283775f,-0.0974038f,0.953931f}, +{0.297757f,-0.225284f,0.927679f},{0.293836f,-0.261361f,0.91943f},{0.182718f,-0.192612f,0.964113f}, +{0.147264f,-0.0935166f,0.984666f},{0.147869f,-0.0805794f,0.985719f},{0.122862f,-0.0441671f,0.991441f}, +{0.153585f,-0.0603901f,0.986288f},{0.248531f,-0.180208f,0.951713f},{0.192206f,-0.16612f,0.967192f}, +{0.160758f,-0.0170921f,0.986846f},{0.124724f,0.00386966f,0.992184f},{-0.152929f,-0.176901f,0.972275f}, +{-0.334302f,-0.0505609f,0.941109f},{-0.325042f,0.204004f,0.923434f},{-0.273952f,0.324963f,0.905179f}, +{-0.183982f,0.404077f,0.896031f},{-0.12399f,0.480282f,0.868306f},{-0.132311f,0.480545f,0.866931f}, +{-0.0539982f,0.44909f,0.891853f},{0.0186793f,0.528253f,0.848882f},{0.0540516f,0.603218f,0.795742f}, +{0.0574721f,0.58057f,0.812179f},{0.0488323f,0.540287f,0.840063f},{0.107447f,0.574946f,0.811106f}, +{0.836185f,-0.500978f,-0.223194f},{0.706114f,-0.222507f,-0.672231f},{0.73513f,-0.156479f,-0.659619f}, +{0.994869f,-0.0398721f,0.0929814f},{0.990704f,-0.0215186f,0.134325f},{0.601227f,0.0332051f,0.798388f}, +{-0.612229f,0.501538f,-0.611258f},{-0.604453f,0.336919f,-0.721888f},{-0.644079f,0.465962f,-0.606664f}, +{0.389418f,-0.206132f,0.897699f},{-0.210594f,-0.0239807f,0.977279f},{0.823588f,0.444795f,-0.351937f}, +{0.523614f,-0.851564f,-0.0258188f},{0.333779f,-0.101378f,0.937184f},{-0.239351f,0.170456f,0.955853f}, +{-0.191253f,0.418555f,0.887826f},{0.993128f,0.0770358f,-0.0881045f},{0.995817f,0.0332385f,-0.0851114f}, +{0.266118f,-0.0563829f,0.96229f},{-0.0899401f,0.57571f,0.812692f},{0.941752f,-0.336122f,0.0111489f}, +{0.956098f,-0.268499f,0.117412f},{0.972634f,0.227543f,0.0469783f},{0.0213099f,0.745877f,0.665743f}, +{-0.855257f,-0.0444835f,-0.516291f},{-0.204136f,0.966554f,0.155248f},{-0.942272f,0.216219f,0.25568f}, +{-0.980849f,0.182453f,0.0681602f},{-0.936853f,0.329239f,0.117934f},{-0.944458f,0.300396f,0.133269f}, +{0.405896f,0.789675f,0.460067f},{-0.883435f,0.0948705f,-0.458849f},{-0.229111f,-0.238176f,0.943812f}, +{-0.527854f,-0.219852f,0.820387f},{-0.493896f,-0.293839f,0.818368f},{-0.00712655f,-0.57856f,-0.815609f}, +{-0.147518f,0.530632f,0.834666f},{0.0992787f,0.378513f,0.920256f},{0.343353f,-0.0633624f,0.937067f}, +{0.371543f,-0.190139f,0.908737f},{0.393868f,-0.207759f,0.895379f},{0.457386f,-0.212961f,0.863392f}, +{0.399808f,-0.170027f,0.900691f},{0.310284f,-0.131588f,0.941493f},{0.326734f,-0.070236f,0.942503f}, +{0.21877f,-0.0742296f,0.972949f},{0.146588f,-0.114926f,0.982499f},{0.141094f,-0.138347f,0.980282f}, +{0.23755f,-0.115062f,0.964537f},{-0.0402561f,-0.269229f,0.962234f},{-0.0749331f,-0.492771f,0.866927f}, +{-0.0734197f,-0.180458f,0.980839f},{-0.00426604f,-0.0404009f,0.999174f},{-0.139124f,0.0871926f,0.986429f}, +{-0.215771f,0.244843f,0.945249f},{-0.234529f,0.347527f,0.907866f},{-0.224147f,0.450837f,0.864005f}, +{-0.2128f,0.484206f,0.848682f},{-0.219569f,0.479628f,0.849557f},{-0.187117f,0.496056f,0.847889f}, +{-0.188186f,0.459699f,0.867907f},{-0.112351f,0.57933f,0.807313f},{-0.106516f,0.620372f,0.777041f}, +{-0.0679673f,0.612159f,0.787808f},{-0.0326748f,0.580432f,0.813653f},{0.708862f,-0.680962f,-0.183865f}, +{0.847722f,-0.484375f,-0.216214f},{0.712572f,-0.688967f,-0.132536f},{0.645906f,-0.7559f,-0.10687f}, +{0.856818f,-0.487012f,-0.169355f},{0.26561f,0.427572f,0.86408f},{0.141693f,-0.00470907f,0.989899f}, +{0.142388f,-0.0233211f,0.989536f},{0.685045f,-0.0214021f,0.728186f},{-0.0585203f,0.657791f,0.750924f}, +{0.318829f,0.468303f,0.824039f},{-0.90048f,0.294898f,-0.319642f},{0.420585f,-0.898045f,-0.12893f}, +{0.0573068f,-0.998341f,0.0055865f},{0.367788f,-0.911475f,-0.184246f},{0.372103f,-0.184797f,0.909609f}, +{0.397361f,-0.124209f,0.909217f},{0.370408f,-0.452713f,0.811078f},{0.339769f,-0.0340991f,0.939891f}, +{0.375499f,0.00669293f,0.926799f},{0.331121f,0.0276573f,0.943183f},{0.189051f,0.235624f,0.953279f}, +{0.234955f,-0.852191f,-0.467511f},{0.207225f,0.610604f,0.764343f},{0.0281201f,0.611182f,0.79099f}, +{0.592523f,0.330052f,-0.734834f},{0.299147f,-0.110764f,0.947757f},{0.289337f,-0.160901f,0.943607f}, +{-0.184455f,0.301245f,0.935536f},{-0.114346f,0.596484f,0.794438f},{0.892855f,0.450094f,-0.0149891f}, +{0.914416f,0.403767f,0.0285614f},{0.995914f,0.0792773f,0.0432455f},{0.995102f,0.0918383f,0.0365886f}, +{0.0882295f,0.392913f,0.915333f},{0.01841f,0.690734f,0.722874f},{-0.998624f,0.0360211f,-0.0381155f}, +{-0.484736f,-0.873671f,0.0415806f},{-0.28694f,-0.957947f,-0.00188694f},{-0.116894f,-0.567173f,-0.815261f}, +{-0.125439f,-0.579902f,-0.804971f},{-0.241306f,-0.463478f,-0.852619f},{-0.0328103f,-0.946855f,-0.319981f}, +{0.139347f,-0.115985f,0.983428f},{0.116236f,0.0964643f,0.988526f},{0.254626f,-0.170183f,0.951947f}, +{0.391114f,-0.158803f,0.906538f},{0.396983f,-0.203492f,0.894984f},{0.234799f,-0.252738f,0.938612f}, +{0.165006f,-0.21172f,0.9633f},{0.0594331f,-0.20056f,0.977877f},{-0.0990798f,-0.253082f,0.962358f}, +{-0.226912f,-0.176631f,0.957764f},{-0.600585f,-0.199035f,0.774392f},{-0.44706f,-0.174961f,0.877226f}, +{-0.582458f,-0.189821f,0.790386f},{-0.258913f,0.0474452f,0.964735f},{-0.175723f,-0.096949f,0.979654f}, +{-0.0275966f,-0.125996f,0.991647f},{-0.0882985f,0.00297313f,0.99609f},{0.00613423f,0.264642f,0.964327f}, +{-0.0170057f,0.424457f,0.905289f},{-0.110029f,0.389298f,0.914516f},{-0.173889f,0.480406f,0.859635f}, +{-0.174587f,0.499081f,0.848786f},{-0.169377f,0.539228f,0.824951f},{-0.13077f,0.599198f,0.789849f}, +{-0.104141f,0.592033f,0.799156f},{-0.100737f,0.612211f,0.784251f},{0.46098f,-0.118947f,0.879403f}, +{0.507723f,-0.148427f,0.848638f},{0.460838f,-0.157004f,0.873486f},{0.0273984f,0.521382f,0.852883f}, +{-0.0558594f,0.574362f,0.816694f},{-0.230996f,-0.451956f,-0.861612f},{-0.603773f,-0.456419f,-0.653559f}, +{0.0498736f,0.479708f,0.87601f},{0.592333f,0.0544071f,0.803854f},{0.982772f,0.101441f,0.154494f}, +{0.979766f,0.0918827f,0.177808f},{0.690902f,-0.676617f,-0.254645f},{0.680962f,-0.684141f,-0.261232f}, +{0.514943f,-0.723689f,-0.459465f},{0.279941f,0.804335f,-0.524097f},{0.470227f,0.806527f,-0.35833f}, +{0.602172f,0.587103f,-0.541017f},{0.219912f,-0.190058f,0.956827f},{-0.707877f,-0.246572f,0.6619f}, +{0.223647f,-0.190582f,0.955856f},{0.66088f,-0.466033f,0.58826f},{0.78418f,-0.57566f,0.231685f}, +{-0.0963573f,0.66013f,0.744945f},{0.529264f,0.395781f,0.750491f},{-0.0938949f,0.51176f,0.853982f}, +{0.0167492f,0.0845491f,0.996279f},{-0.29642f,0.739859f,0.60394f},{0.770559f,-0.27077f,0.576994f}, +{0.395407f,0.551992f,-0.734138f},{0.426667f,0.671553f,-0.605781f},{-0.678447f,-0.734224f,-0.0250153f}, +{-0.670977f,-0.740986f,-0.0270176f},{0.719373f,0.597154f,0.354838f},{-0.0260727f,-0.794933f,-0.606137f}, +{-0.488692f,-0.0546212f,-0.870745f},{-0.85862f,-0.0460396f,0.510541f},{-0.833654f,-0.028232f,0.551565f}, +{-0.711846f,-0.0760288f,0.698209f},{-0.794342f,0.00412712f,0.607457f},{0.916589f,-0.296311f,-0.268448f}, +{-0.756481f,-0.335536f,0.561384f},{-0.448513f,0.655173f,0.607935f},{-0.491382f,0.649039f,0.580769f}, +{-0.359336f,0.545212f,0.757378f},{-0.454763f,0.408567f,0.791368f},{-0.360103f,0.534543f,0.764585f}, +{-0.419343f,0.270591f,0.866563f},{-0.308274f,-0.158676f,0.937971f},{-0.537672f,-0.0421306f,0.842101f}, +{-0.173191f,-0.228231f,0.958079f},{-0.435517f,-0.276149f,0.856777f},{-0.530407f,-0.286438f,0.797886f}, +{-0.554514f,-0.329894f,0.763992f},{0.785711f,0.574001f,0.230609f},{0.741859f,0.498856f,0.448093f}, +{0.303562f,0.946105f,-0.112852f},{0.307248f,0.93655f,-0.168738f},{0.395085f,-0.646918f,0.652231f}, +{0.259732f,-0.848029f,-0.461938f},{0.238077f,0.968948f,0.0667771f},{0.222715f,0.964406f,0.142547f}, +{0.332458f,0.941366f,0.0574574f},{0.331289f,0.943105f,-0.0283073f},{-0.673966f,0.432063f,0.599243f}, +{-0.361289f,0.120792f,0.924597f},{0.081858f,0.397394f,0.91399f},{-0.0442536f,0.141609f,0.988933f}, +{-0.224349f,0.314769f,0.922273f},{-0.186689f,0.451686f,0.872426f},{-0.208966f,0.483781f,0.849876f}, +{-0.23828f,0.528725f,0.814661f},{-0.226058f,0.597649f,0.769229f},{-0.20108f,0.616536f,0.761216f}, +{-0.186587f,0.626264f,0.756954f},{-0.485704f,0.733153f,-0.476002f},{-0.584897f,0.708289f,-0.395249f}, +{0.91989f,0.0667137f,-0.386461f},{0.955529f,0.0549196f,-0.289737f},{0.911446f,-0.138902f,-0.387263f}, +{0.167664f,-0.966341f,-0.195126f},{0.479603f,-0.758439f,-0.441306f},{0.196735f,0.71953f,0.666013f}, +{0.178837f,0.211949f,0.960778f},{-0.365789f,0.882121f,0.29675f},{0.108296f,-0.661035f,-0.742499f}, +{0.231028f,-0.767635f,-0.597799f},{0.528096f,-0.802825f,-0.276744f},{0.812965f,-0.521272f,-0.259545f}, +{-0.707441f,-0.161392f,0.688099f},{-0.818057f,-0.262473f,0.511752f},{-0.83981f,-0.276777f,0.467027f}, +{-0.78566f,-0.272051f,0.555632f},{0.232743f,0.972491f,0.00962511f},{0.319045f,0.947739f,-0.000587843f}, +{0.355517f,0.91667f,-0.182547f},{0.0257376f,0.844356f,0.535164f},{-0.344861f,0.925942f,0.153956f}, +{-0.322892f,0.577177f,0.750071f},{0.979764f,0.15852f,-0.122203f},{-0.157063f,0.691591f,-0.705006f}, +{-0.217615f,0.649092f,-0.728919f},{-0.115872f,0.61574f,-0.779383f},{-0.156937f,-0.472687f,0.867143f}, +{0.375524f,0.469339f,0.799189f},{0.963422f,-0.262459f,-0.0541521f},{0.543008f,-0.133496f,0.829048f}, +{-0.0294388f,0.115412f,0.992881f},{-0.315051f,0.544746f,0.777171f},{-0.201034f,0.829596f,0.520919f}, +{-0.347545f,0.838266f,0.420146f},{-0.744658f,-0.0792734f,0.662722f},{0.539302f,-0.77313f,0.333802f}, +{0.503749f,-0.7688f,0.393933f},{-0.809059f,-0.0753806f,0.582874f},{-0.776578f,-0.217583f,0.591257f}, +{0.192285f,0.832061f,0.520289f},{0.0294664f,0.792039f,0.60976f},{0.0936467f,0.940049f,0.32793f}, +{0.0481948f,0.903587f,0.425685f},{0.62816f,0.554507f,0.545836f},{0.946447f,-0.0202053f,0.322227f}, +{0.123842f,0.398537f,0.908753f},{0.0566087f,-0.712531f,-0.699353f},{0.00541469f,-0.696924f,-0.717125f}, +{-0.827308f,0.442382f,-0.346208f},{0.945003f,0.288744f,-0.153608f},{-0.164464f,0.501697f,-0.849265f}, +{-0.212151f,0.449517f,-0.867713f},{-0.217853f,0.603f,-0.767418f},{0.777729f,-0.537961f,-0.32517f}, +{-0.350058f,0.556344f,0.753619f},{-0.438878f,0.486298f,0.75558f},{-0.340488f,0.390308f,0.855411f}, +{-0.249393f,0.667711f,0.701402f},{-0.169594f,0.899467f,0.402737f},{-0.40296f,0.798587f,0.447082f}, +{-0.260137f,0.904443f,0.3381f},{0.297284f,0.951307f,0.0814693f},{0.747864f,0.242122f,-0.618124f}, +{0.693854f,0.21889f,-0.686042f},{0.706679f,0.147446f,-0.692f},{0.0362161f,0.811871f,0.582713f}, +{-0.402915f,0.903071f,0.148733f},{-0.611856f,0.758072f,-0.225742f},{-0.60932f,0.783829f,-0.119753f}, +{0.566185f,0.76417f,0.308997f},{-0.331398f,0.274975f,0.902532f},{-0.286723f,-0.182686f,0.940434f}, +{-0.34717f,0.076168f,0.934704f},{-0.172591f,0.212991f,0.96169f},{-0.1893f,0.383452f,0.903953f}, +{-0.231082f,0.432922f,0.871309f},{-0.25959f,0.479614f,0.838202f},{-0.318628f,0.575777f,0.752966f}, +{-0.322619f,0.603173f,0.729451f},{-0.358251f,0.602617f,0.713098f},{-0.390833f,0.610403f,0.688954f}, +{0.388946f,-0.442439f,0.808065f},{0.477906f,0.397443f,0.783355f},{0.661378f,0.162789f,0.732174f}, +{-0.0720481f,0.806311f,0.587088f},{-0.565128f,0.822945f,0.0582318f},{-0.390616f,0.822168f,0.414076f}, +{-0.573525f,0.52527f,0.628618f},{-0.82288f,-0.00567614f,0.568186f},{-0.714775f,0.20468f,0.668733f}, +{0.19947f,0.979005f,0.0419626f},{0.340742f,0.93565f,0.0919395f},{-0.777639f,-0.623126f,-0.0836173f}, +{-0.828711f,-0.559582f,0.0103613f},{-0.771514f,-0.606945f,-0.190748f},{-0.685167f,-0.680435f,-0.259914f}, +{-0.953719f,0.300617f,0.00699773f},{-0.873393f,0.41651f,-0.252396f},{-0.829566f,0.377382f,-0.411587f}, +{0.500125f,0.797272f,0.337983f},{-0.502071f,0.526381f,0.686184f},{-0.419821f,0.617315f,0.665336f}, +{-0.961307f,0.160805f,-0.223676f},{0.989366f,0.0418744f,-0.139286f},{0.958475f,0.210319f,-0.192591f}, +{0.00907638f,0.395259f,0.918525f},{-0.121054f,0.339914f,0.932633f},{-0.0711068f,0.590537f,0.803872f}, +{-0.0363095f,0.710202f,0.703061f},{0.0153851f,0.853133f,0.521466f},{-0.112796f,0.818173f,0.5638f}, +{-0.16988f,0.971006f,-0.168189f},{-0.0077306f,0.996263f,-0.0860288f},{0.243035f,0.943361f,0.22584f}, +{-0.0621356f,0.340509f,0.938186f},{-0.0491563f,0.366334f,0.929184f},{-0.176118f,0.454378f,0.873226f}, +{-0.189273f,0.46869f,0.862847f},{-0.259481f,0.511469f,0.819188f},{-0.449379f,0.578745f,0.680524f}, +{0.0913338f,0.894366f,0.437912f},{0.42565f,-0.422144f,0.800385f},{0.59795f,-0.23884f,0.765122f}, +{0.45527f,-0.177488f,0.872483f},{0.386973f,-0.921255f,-0.0392579f},{0.250819f,-0.869655f,-0.425193f}, +{0.306345f,-0.905818f,-0.292655f},{-0.99852f,-0.0334156f,-0.0429045f},{-0.964216f,0.258421f,0.0592056f}, +{-0.987625f,0.154451f,-0.027243f},{-0.997861f,0.0519454f,-0.0396961f},{-0.83911f,0.481331f,0.253406f}, +{-0.926526f,-0.254974f,-0.276656f},{-0.692555f,0.643989f,0.325032f},{0.99397f,-0.107486f,0.0217039f}, +{-0.218578f,0.93918f,0.264886f},{-0.304393f,0.911539f,0.276481f},{-0.277804f,0.90877f,0.311387f}, +{-0.30602f,0.562103f,0.768369f},{-0.325307f,0.591888f,0.737457f},{-0.367669f,0.600766f,0.709859f}, +{-0.426539f,0.593136f,0.682828f},{-0.357713f,0.671509f,0.648935f},{-0.343895f,0.711826f,0.612406f}, +{0.540354f,0.319822f,0.778288f},{0.319022f,0.317524f,0.892974f},{0.0164285f,-0.758999f,-0.650885f}, +{0.810879f,0.41449f,-0.413125f},{0.402384f,-0.808354f,-0.429711f},{-0.692319f,0.269885f,0.66922f}, +{-0.802128f,-0.0286058f,0.596466f},{0.0118075f,0.999179f,-0.0387475f},{0.039404f,0.998234f,-0.0444579f}, +{-0.255023f,0.966562f,-0.0268538f},{0.181189f,0.943149f,0.278642f},{0.135556f,0.913165f,0.384388f}, +{0.407739f,0.656533f,0.634597f},{0.297586f,0.648132f,0.700976f},{0.384275f,0.922206f,-0.0432202f}, +{-0.0859526f,-0.656785f,-0.749163f},{0.599155f,-0.0773312f,-0.79689f},{0.324199f,0.527672f,0.785148f}, +{-0.932281f,-0.0620485f,-0.356374f},{-0.977376f,0.113279f,-0.178615f},{-0.93158f,0.134419f,-0.337773f}, +{0.140383f,-0.767237f,-0.625812f},{0.213569f,-0.832839f,-0.510654f},{0.532393f,-0.808362f,-0.251216f}, +{0.246787f,0.464288f,0.850607f},{0.193003f,0.739826f,0.644521f},{0.104031f,0.856008f,0.506387f}, +{-0.184839f,0.440609f,0.878464f},{-0.619346f,0.0773028f,0.781304f},{-0.779055f,-0.108587f,0.61748f}, +{-0.72702f,-0.208468f,0.654204f},{-0.693424f,-0.173667f,0.699288f},{-0.0203507f,0.99796f,-0.060504f}, +{-0.116628f,0.989023f,0.0907244f},{-0.138448f,0.974696f,0.175497f},{-0.118911f,0.974713f,0.189194f}, +{0.782602f,0.568475f,0.253714f},{-0.660721f,-0.733755f,-0.158278f},{0.956928f,0.238728f,0.165221f}, +{0.549901f,0.697434f,0.45956f},{0.499327f,0.758075f,0.419517f},{0.607054f,0.674272f,0.420528f}, +{0.512313f,-0.831211f,-0.215926f},{0.697816f,-0.69249f,-0.183061f},{0.0784108f,0.317364f,0.945057f}, +{-0.013652f,0.281661f,0.959417f},{-0.103051f,0.383828f,0.917636f},{-0.184355f,0.471683f,0.862281f}, +{-0.197612f,0.535434f,0.821133f},{-0.240097f,0.582367f,0.776661f},{-0.260232f,0.584369f,0.76863f}, +{-0.342142f,0.66669f,0.662165f},{-0.919643f,0.348874f,-0.180397f},{-0.951161f,0.258725f,-0.168385f}, +{0.196828f,-0.0334996f,0.979865f},{0.93696f,-0.10587f,-0.333013f},{0.898716f,0.0143765f,-0.438296f}, +{0.934906f,-0.162736f,-0.315386f},{-0.0443143f,-0.514988f,-0.856051f},{-0.576905f,-0.302724f,0.758643f}, +{0.999693f,-0.0144882f,0.020096f},{-0.902492f,-0.418109f,-0.103404f},{-0.91035f,-0.370171f,-0.185029f}, +{-0.972665f,0.169551f,-0.158669f},{-0.537973f,0.299744f,0.787869f},{-0.317389f,0.276084f,0.907217f}, +{-0.43221f,0.364523f,0.824814f},{0.530498f,0.182756f,-0.827751f},{0.497767f,0.331848f,-0.801315f}, +{0.59232f,0.276457f,-0.756789f},{0.903917f,-0.0343129f,-0.426329f},{-0.129496f,-0.412196f,-0.901845f}, +{-0.997442f,-0.0199234f,-0.0686405f},{-0.116131f,0.99273f,0.0316319f},{-0.148505f,0.984841f,0.0896363f}, +{-0.107525f,0.991118f,0.0782455f},{0.872192f,0.459426f,0.167954f},{-0.302342f,0.32664f,0.895486f}, +{-0.394453f,0.402819f,0.82592f},{0.988901f,-0.142468f,0.042162f},{-0.28951f,0.597491f,0.747789f}, +{0.886142f,-0.0227338f,-0.462855f},{0.372933f,-0.92439f,-0.080149f},{0.324752f,0.473531f,0.818721f}, +{0.0181762f,0.577834f,0.815952f},{-0.446479f,-0.0219026f,0.894526f},{-0.41044f,-0.340059f,0.846108f}, +{-0.497253f,-0.366512f,0.786389f},{-0.188972f,0.87435f,-0.446992f},{-0.27229f,-0.212348f,0.938492f}, +{-0.260154f,-0.26183f,0.92939f},{-0.424896f,-0.295882f,0.855522f},{-0.201163f,-0.282722f,0.937871f}, +{-0.349685f,-0.362444f,0.863918f},{-0.258599f,-0.400252f,0.879162f},{-0.0663588f,-0.124558f,0.989991f}, +{-0.277381f,-0.292648f,0.915105f},{-0.0129428f,0.051201f,0.998604f},{-0.21943f,0.0640172f,0.973526f}, +{-0.0834486f,0.148989f,0.985311f},{-0.176729f,0.302573f,0.936598f},{-0.303486f,0.374783f,0.876033f}, +{-0.22937f,0.520291f,0.822609f},{-0.399844f,0.536487f,0.743173f},{-0.190027f,0.270728f,0.943714f}, +{-0.0885366f,0.238847f,0.967013f},{-0.208084f,0.301494f,0.930485f},{-0.218262f,0.436112f,0.873022f}, +{-0.209885f,0.518052f,0.829199f},{-0.251104f,0.521023f,0.815771f},{-0.27394f,0.616177f,0.738432f}, +{-0.267678f,0.649952f,0.711275f},{-0.271756f,0.870571f,0.410189f},{-0.248053f,0.886882f,0.389757f}, +{-0.341894f,0.876298f,0.339426f},{0.913529f,-0.0778003f,-0.399265f},{-0.220965f,0.27626f,-0.935337f}, +{0.324672f,0.945114f,-0.0366963f},{-0.549577f,0.390051f,0.7388f},{-0.0404058f,0.453485f,0.890348f}, +{-0.265051f,0.34101f,0.90192f},{0.671957f,0.182503f,-0.717751f},{0.625907f,0.322451f,-0.710116f}, +{0.561647f,-0.442315f,0.699221f},{0.329729f,0.556732f,0.762449f},{0.915737f,0.214104f,-0.339978f}, +{-0.366646f,-0.323675f,0.872241f},{-0.474031f,-0.323003f,0.819124f},{-0.462516f,-0.19931f,0.863918f}, +{-0.60706f,-0.19681f,0.769899f},{-0.413657f,-0.149255f,0.898115f},{-0.551041f,-0.226188f,0.803239f}, +{-0.256732f,-0.2343f,0.937653f},{-0.432891f,0.81967f,0.375162f},{-0.82982f,0.47565f,0.291813f}, +{0.935737f,-0.0667342f,-0.346327f},{0.410462f,-0.898124f,-0.157782f},{0.381093f,0.353227f,0.854399f}, +{0.314514f,0.455215f,0.832983f},{-0.107452f,-0.0516191f,0.992869f},{-0.436083f,-0.386618f,0.812624f}, +{-0.326328f,-0.290193f,0.89961f},{-0.358768f,-0.351906f,0.86455f},{-0.491272f,-0.274142f,0.826739f}, +{-0.249382f,-0.0817001f,0.964953f},{-0.333758f,-0.24729f,0.909644f},{-0.364851f,-0.0687879f,0.928522f}, +{-0.168992f,-0.211946f,0.962559f},{-0.199238f,-0.291946f,0.935453f},{-0.2244f,-0.252274f,0.941277f}, +{-0.152452f,-0.164372f,0.974546f},{-0.112379f,-0.0502458f,0.992394f},{-0.0843327f,0.0708477f,0.993916f}, +{-0.0420862f,0.0883098f,0.995204f},{-0.116689f,0.21632f,0.969324f},{-0.217116f,0.527381f,0.821419f}, +{-0.175943f,0.699574f,0.692561f},{-0.29065f,0.677972f,0.675186f},{-0.207183f,0.94949f,-0.235676f}, +{0.576123f,-0.814343f,-0.0701916f},{-0.330163f,0.771793f,0.543441f},{-0.39642f,0.831665f,0.388823f}, +{-0.396915f,0.639425f,0.658478f},{-0.183854f,0.352624f,0.917526f},{-0.170048f,0.255627f,0.951703f}, +{-0.0999526f,0.132185f,0.986173f},{-0.204507f,0.401786f,0.892605f},{-0.225599f,0.496702f,0.838089f}, +{-0.23613f,0.499564f,0.833474f},{-0.272788f,0.477579f,0.835168f},{-0.255052f,0.575954f,0.776676f}, +{-0.288071f,0.716377f,0.635468f},{0.939666f,0.00962893f,-0.341958f},{0.811251f,-0.550205f,-0.197856f}, +{0.84832f,-0.501184f,-0.170784f},{0.906024f,-0.41817f,-0.0652293f},{0.806224f,0.109341f,0.581419f}, +{0.807168f,0.301015f,0.507808f},{0.391369f,0.919183f,-0.0439765f},{0.259863f,0.0750233f,0.962727f}, +{0.332689f,0.161066f,0.92918f},{0.378018f,0.180702f,0.907992f},{-0.325281f,-0.413715f,0.850313f}, +{0.227168f,-0.359984f,-0.904879f},{0.43349f,-0.686914f,-0.583297f},{0.741767f,0.00615409f,0.670629f}, +{0.519002f,0.52435f,0.67505f},{-0.97891f,0.0906991f,-0.183055f},{0.394563f,0.9161f,0.0712859f}, +{0.389378f,0.920614f,0.0292228f},{0.319017f,0.528083f,0.786992f},{-0.307802f,-0.340839f,0.888306f}, +{-0.126457f,0.361748f,0.92366f},{-0.16985f,0.756014f,0.632134f},{-0.185053f,0.754408f,0.629781f}, +{0.0966245f,-0.689971f,-0.717359f},{0.492416f,-0.869194f,-0.0450272f},{0.996683f,0.0751418f,-0.0312672f}, +{-0.234427f,0.685005f,0.689792f},{0.867339f,0.0838635f,-0.490602f},{0.925486f,0.164947f,-0.34098f}, +{-0.204274f,0.789897f,0.578217f},{0.337036f,-0.144287f,0.93037f},{0.872065f,-0.43883f,-0.216636f}, +{0.723614f,-0.646477f,-0.241765f},{0.303861f,0.202101f,0.931033f},{0.236392f,0.303918f,0.922904f}, +{-0.03706f,-0.379717f,0.92436f},{-0.24061f,-0.442195f,0.864043f},{-0.320437f,-0.327133f,0.88899f}, +{-0.315056f,-0.394821f,0.86305f},{-0.394311f,-0.306347f,0.866412f},{-0.291312f,-0.0705552f,0.954023f}, +{-0.304152f,-0.0144173f,0.952514f},{-0.286319f,-0.243813f,0.926594f},{-0.315406f,-0.311765f,0.896282f}, +{-0.305999f,-0.289459f,0.906961f},{-0.243082f,-0.160769f,0.95659f},{-0.139866f,-0.103768f,0.984718f}, +{-0.0984367f,0.00461992f,0.995133f},{0.000226813f,0.128423f,0.991719f},{-0.0322293f,0.139832f,0.989651f}, +{-0.0463018f,0.159905f,0.986046f},{-0.0349219f,0.332265f,0.942539f},{-0.0872712f,0.549626f,0.83084f}, +{-0.143092f,0.695318f,0.704314f},{0.844038f,0.41268f,0.342484f},{-0.0784911f,0.135579f,0.987653f}, +{-0.151022f,0.495322f,0.855482f},{0.864087f,-0.487038f,-0.127076f},{-0.118199f,0.191062f,0.974435f}, +{-0.128221f,0.381595f,0.915393f},{-0.16414f,0.47926f,0.862188f},{-0.180967f,0.512564f,0.839363f}, +{-0.147203f,0.567275f,0.810266f},{-0.151267f,0.653834f,0.741363f},{-0.188561f,0.726022f,0.661315f}, +{0.824964f,-0.480821f,-0.297061f},{0.905172f,0.260718f,-0.335692f},{-0.751042f,0.593217f,0.289876f}, +{-0.32475f,-0.35033f,-0.878525f},{-0.481899f,-0.260127f,-0.836724f},{-0.624517f,-0.221113f,-0.749058f}, +{-0.103103f,0.445253f,0.889449f},{-0.309203f,-0.277461f,-0.90962f},{-0.294538f,-0.402181f,-0.86689f}, +{0.864967f,0.288401f,-0.410679f},{0.897178f,0.275958f,-0.344845f},{0.767788f,-0.232109f,0.597183f}, +{0.760242f,-0.495563f,0.420059f},{0.808193f,-0.49461f,0.319663f},{0.733594f,0.286807f,-0.616102f}, +{0.640647f,0.238282f,-0.729927f},{-0.281333f,-0.352565f,0.892496f},{-0.232012f,0.873399f,0.428187f}, +{-0.248022f,0.814072f,0.52514f},{0.837728f,-0.0939249f,-0.53795f},{0.870655f,-0.279119f,-0.405033f}, +{-0.881121f,-0.0520004f,-0.470024f},{-0.0155312f,-0.466044f,0.884625f},{-0.129013f,-0.669994f,0.73107f}, +{-0.197947f,-0.475871f,0.85695f},{-0.244171f,-0.34963f,0.90451f},{-0.140039f,-0.330352f,0.933411f}, +{-0.057988f,-0.337569f,0.939513f},{-0.122666f,-0.113582f,0.985927f},{-0.205128f,-0.00877181f,0.978696f}, +{-0.337272f,-0.0746786f,0.938441f},{-0.306237f,-0.258884f,0.916078f},{-0.278592f,-0.259471f,0.924695f}, +{-0.261924f,-0.250819f,0.931926f},{-0.209337f,-0.0287817f,0.97742f},{-0.0601506f,0.0672967f,0.995918f}, +{0.0611062f,0.0532419f,0.99671f},{0.0713627f,0.103608f,0.992055f},{-0.0695355f,0.803765f,0.59087f}, +{-0.644098f,0.273464f,-0.714392f},{0.0732606f,0.601907f,0.795199f},{-0.2022f,0.892853f,0.402403f}, +{-0.116181f,0.690582f,0.713862f},{0.839387f,0.072094f,-0.538733f},{0.906994f,0.157665f,-0.390518f}, +{0.880792f,0.258935f,-0.396433f},{0.849277f,0.279145f,-0.448115f},{0.88698f,-0.0393897f,-0.460125f}, +{-0.256429f,-0.366661f,0.894318f},{0.384314f,-0.129266f,0.914108f},{-0.91019f,-0.38715f,-0.147201f}, +{-0.873766f,-0.460888f,-0.15529f},{0.0533412f,-0.987276f,0.1498f},{-0.0332455f,-0.999425f,-0.00667568f}, +{-0.0177683f,-0.998598f,-0.0498695f},{0.743946f,-0.315207f,-0.589228f},{-0.0300893f,0.428489f,0.903046f}, +{-0.141131f,-0.616613f,0.774513f},{-0.143887f,-0.543126f,0.827231f},{0.151877f,-0.0435705f,0.987439f}, +{0.135903f,0.0967646f,0.985985f},{0.181863f,-0.0316108f,0.982816f},{0.817395f,0.471839f,-0.330504f}, +{0.834915f,0.44021f,-0.330353f},{-0.439625f,0.363522f,-0.821329f},{-0.23135f,-0.491517f,-0.839576f}, +{0.881996f,0.263243f,0.390879f},{0.804092f,0.450491f,-0.387936f},{0.379275f,-0.920389f,-0.0950521f}, +{0.657543f,-0.0898175f,0.748044f},{0.60158f,0.0546622f,0.79694f},{0.424673f,-0.380215f,0.821638f}, +{0.480442f,-0.295344f,0.825801f},{0.296132f,0.463751f,0.835009f},{0.271331f,0.0872357f,0.958525f}, +{0.272219f,-0.172097f,0.94672f},{0.653222f,-0.218414f,0.72498f},{0.637907f,-0.0586593f,0.767876f}, +{0.432417f,-0.0287288f,0.901216f},{0.375382f,0.133798f,0.917162f},{0.668195f,0.332693f,0.665456f}, +{0.751623f,0.310123f,0.58214f},{0.70471f,0.352257f,0.615873f},{0.354198f,-0.110598f,0.928607f}, +{0.316442f,-0.111138f,0.942079f},{0.500513f,0.429239f,0.751825f},{0.485563f,0.505267f,0.713396f}, +{0.467589f,0.389971f,0.793274f},{0.269565f,0.432078f,0.860606f},{0.526376f,-0.206247f,0.824858f}, +{0.547881f,-0.180711f,0.816804f},{0.516456f,-0.177985f,0.837613f},{0.265807f,0.621366f,0.737056f}, +{0.396606f,0.461164f,0.793745f},{0.268517f,0.679316f,0.682956f},{0.608431f,-0.475134f,0.635657f}, +{0.581453f,0.335069f,0.741378f},{0.565053f,-0.011672f,0.824972f},{0.542132f,-0.254652f,0.800778f}, +{0.480758f,-0.379163f,0.790637f},{0.456013f,-0.393873f,0.79807f},{0.879688f,-0.114436f,0.461577f}, +{0.858958f,-0.0758828f,0.506393f},{0.85334f,-0.175889f,0.49079f},{0.864541f,0.245988f,0.438246f}, +{0.459771f,0.488017f,0.741923f},{0.648193f,0.136459f,0.749149f},{0.745059f,-0.146613f,0.650686f}, +{0.320391f,0.613417f,0.721851f},{0.381497f,0.502289f,0.775993f},{0.276193f,0.651878f,0.706238f}, +{0.787947f,-0.266373f,0.555144f},{0.738517f,-0.18476f,0.648426f},{-0.10106f,-0.198674f,-0.974841f}, +{-0.213871f,-0.405287f,-0.88882f},{0.120389f,-0.573452f,-0.810345f},{0.549648f,0.115702f,0.827345f}, +{0.656562f,-0.0125393f,0.754168f},{0.523616f,0.221222f,0.822732f},{0.5305f,-0.207482f,0.821901f}, +{0.503594f,-0.178747f,0.845247f},{0.464655f,-0.297417f,0.834049f},{0.436071f,-0.269518f,0.858605f}, +{0.552355f,-0.130993f,0.823253f},{0.71381f,-0.196261f,0.672277f},{0.255606f,0.690306f,0.676863f}, +{0.210124f,0.77675f,0.593723f},{0.853942f,-0.0356107f,0.519148f},{0.456866f,-0.524287f,0.718607f}, +{0.497688f,-0.585216f,0.640179f},{-0.239292f,-0.426292f,-0.872361f},{-0.133658f,-0.421392f,-0.896975f}, +{-0.0754212f,-0.532674f,-0.842953f},{0.6607f,-0.607619f,0.440766f},{0.628709f,-0.577003f,0.521336f}, +{0.641454f,-0.668221f,0.376852f},{0.666737f,-0.498983f,0.553603f},{0.800721f,0.0591873f,0.596106f}, +{0.748422f,0.000704595f,0.663222f},{0.790442f,-0.196599f,0.58013f},{0.7523f,-0.31572f,0.578244f}, +{0.770543f,-0.115037f,0.626921f},{0.657429f,-0.378401f,0.651613f},{0.558381f,-0.300341f,0.773309f}, +{0.605352f,-0.483657f,0.632159f},{0.508999f,-0.563832f,0.650394f},{0.547093f,-0.479533f,0.686103f}, +{0.647568f,0.075846f,0.758224f},{0.657391f,0.209588f,0.723816f},{0.778641f,0.458995f,0.427835f}, +{0.796311f,-0.124677f,0.591899f},{0.287351f,0.191424f,0.938502f},{0.732084f,0.104478f,0.673154f}, +{0.576522f,0.362983f,0.732029f},{0.465965f,0.367877f,0.804701f},{0.730207f,0.0875507f,0.677594f}, +{-0.399503f,0.326363f,-0.85667f},{-0.354372f,0.129678f,-0.926069f},{-0.448822f,0.605935f,-0.656811f}, +{-0.617025f,0.688594f,-0.380944f},{0.572087f,-0.696272f,0.4335f},{0.5902f,-0.307268f,0.746492f}, +{0.512315f,-0.27866f,0.812331f},{0.615482f,-0.524555f,0.588238f},{0.493002f,-0.409266f,0.767757f}, +{0.542152f,-0.375549f,0.751687f},{0.531583f,-0.39377f,0.74991f},{0.465902f,0.343074f,0.81562f}, +{0.556731f,-0.769617f,0.312633f},{0.254734f,-0.853269f,-0.455018f},{0.844112f,0.373341f,0.384825f}, +{0.798595f,0.33797f,0.498018f},{0.975491f,0.218904f,-0.0223324f},{0.411182f,-0.19688f,0.890038f}, +{0.55109f,-0.118472f,0.825993f},{0.555506f,-0.31389f,0.769991f},{0.483713f,-0.52489f,0.700365f}, +{0.476981f,-0.179667f,0.860354f},{-0.447194f,0.0450272f,-0.893303f},{0.323563f,-0.182347f,0.92847f}, +{0.11422f,0.271978f,-0.955501f},{-0.0334828f,0.360681f,-0.932088f},{0.0801935f,0.352312f,-0.93244f}, +{-0.165139f,-0.53863f,-0.8262f},{0.820463f,0.471607f,-0.323152f},{0.817037f,0.503878f,-0.280281f}, +{0.565589f,-0.744425f,-0.354882f},{0.593147f,-0.757668f,-0.272244f},{0.698756f,-0.639595f,-0.320402f}, +{0.416088f,-0.0895367f,0.904905f},{0.492055f,0.0671786f,0.867968f},{0.529662f,0.0245061f,0.847854f}, +{-0.290736f,-0.569161f,0.769109f},{0.343488f,-0.0248503f,0.938828f},{0.351263f,-0.283942f,0.892183f}, +{-0.100958f,0.304861f,-0.947031f},{-0.147563f,0.426841f,-0.892206f},{0.160646f,0.724895f,0.669865f}, +{0.739509f,-0.237147f,0.629991f},{0.755263f,-0.262598f,0.600517f},{0.720221f,-0.175747f,0.671115f}, +{-0.244267f,-0.453948f,-0.856893f},{-0.0660144f,-0.604178f,-0.79411f},{0.573614f,-0.482652f,0.661827f}, +{0.560916f,-0.327772f,0.760223f},{0.380138f,0.160625f,0.910876f},{0.851176f,0.264634f,-0.453287f}, +{0.34345f,-0.0915945f,0.934694f},{0.260014f,0.0541945f,0.964083f},{0.313326f,0.212754f,0.925507f}, +{0.396535f,0.543902f,-0.739548f},{0.444644f,0.49191f,-0.748543f},{0.503687f,0.52311f,-0.687499f}, +{-0.305173f,-0.397453f,-0.86539f},{0.660256f,0.283758f,0.695373f},{0.781524f,0.0706021f,0.619867f}, +{0.793597f,0.216873f,0.568481f},{0.733662f,0.193507f,0.65138f},{0.323353f,0.369017f,0.871361f}, +{0.582228f,-0.340961f,0.738076f},{0.549202f,0.284132f,0.785905f},{0.290724f,0.169076f,0.94175f}, +{0.53424f,-0.844963f,0.0250082f},{0.912901f,-0.035308f,0.406652f},{0.825201f,0.0115751f,0.56472f}, +{0.94043f,0.103742f,0.323773f},{-0.358271f,0.346487f,-0.866942f},{-0.277017f,0.312182f,-0.908738f}, +{-0.322405f,0.20153f,-0.9249f},{-0.981484f,-0.040964f,-0.187113f},{-0.846049f,-0.367576f,-0.386119f}, +{-0.723049f,-0.665594f,-0.184891f},{-0.681233f,0.638163f,0.358706f},{-0.887797f,0.251246f,0.385606f}, +{-0.945932f,0.286633f,0.151838f},{0.485297f,0.0684109f,0.871669f},{0.514312f,0.461732f,0.722694f}, +{0.622169f,0.403788f,0.670717f},{0.681605f,0.244739f,0.689578f},{0.498462f,-0.119438f,0.858644f}, +{0.606269f,-0.213205f,0.766148f},{0.768382f,0.484224f,-0.41847f},{0.815839f,0.442994f,-0.371703f}, +{-0.373507f,-0.270642f,-0.887269f},{-0.246314f,-0.0015448f,-0.969189f},{-0.431788f,-0.307208f,-0.848046f}, +{-0.347352f,-0.0362057f,-0.937036f},{0.0188641f,-0.999595f,-0.0213147f},{-0.00396728f,-0.999933f,-0.010845f}, +{0.0160447f,-0.999596f,-0.0234478f},{0.564655f,-0.441574f,0.697264f},{0.69031f,-0.153769f,0.706985f}, +{0.653448f,0.150736f,0.741812f},{0.651377f,0.314426f,0.690539f},{0.627538f,0.228138f,0.744412f}, +{0.477152f,0.341111f,0.809919f},{-0.983667f,-0.0636223f,0.168378f},{0.743203f,-0.429802f,0.512757f}, +{0.674133f,-0.406715f,0.616545f},{0.710554f,-0.395237f,0.582151f},{0.80919f,0.278181f,0.51752f}, +{0.826218f,0.290013f,0.482966f},{0.781084f,0.319842f,0.536291f},{0.374297f,-0.414591f,0.829468f}, +{0.292665f,-0.215483f,0.931619f},{0.147606f,0.803867f,0.576204f},{0.218746f,0.781826f,0.583866f}, +{0.512944f,-0.418974f,0.749233f},{0.567309f,-0.478639f,0.670123f},{0.577802f,-0.447301f,0.682691f}, +{0.685652f,-0.52024f,0.509148f},{0.448118f,-0.740268f,0.501192f},{0.684582f,0.410588f,0.6023f}, +{0.617891f,0.399018f,0.677492f},{0.632734f,0.295336f,0.715839f},{0.870482f,0.162018f,0.46477f}, +{0.840489f,0.186369f,0.508768f},{0.770366f,-0.159666f,0.617287f},{0.489274f,-0.00781279f,0.872095f}, +{0.522915f,-0.119012f,0.844036f},{0.525504f,-0.253163f,0.812253f},{0.584816f,-0.690932f,0.424975f}, +{0.765617f,0.283534f,0.577442f},{0.680954f,0.47874f,0.554174f},{0.75469f,0.139645f,0.641047f}, +{0.756042f,0.181321f,0.628906f},{0.706788f,-0.577248f,0.408944f},{0.270976f,0.449686f,0.85109f}, +{0.670575f,0.280147f,0.686911f},{0.731219f,0.377028f,0.56848f},{0.777606f,0.343926f,0.52635f}, +{0.764272f,0.413505f,0.494875f},{0.984362f,0.00908258f,0.175924f},{0.994702f,0.0701487f,0.0751525f}, +{0.988355f,0.0821244f,0.128101f},{0.819972f,-0.180455f,0.543215f},{0.786792f,0.106034f,0.608043f}, +{0.738693f,0.417579f,0.529113f},{0.813713f,0.199231f,0.546057f},{0.790175f,-0.192608f,0.58183f}, +{0.784095f,-0.245785f,0.569899f},{0.974171f,0.0716883f,0.214129f},{0.769246f,-0.371558f,0.519813f}, +{0.996425f,0.0802f,0.0265568f},{0.995753f,0.0774648f,0.0497599f},{0.994732f,0.0839796f,0.058788f}, +{0.0684754f,0.794747f,0.603066f},{0.890577f,-0.0326624f,0.453658f},{0.88509f,0.0273287f,0.464616f}, +{0.8882f,-0.0223627f,0.458912f},{0.673587f,-0.673846f,0.303664f},{0.443082f,-0.835915f,0.32392f}, +{0.601954f,-0.695112f,0.393027f},{0.529223f,-0.697322f,0.483389f},{0.520604f,-0.78592f,0.333618f}, +{0.854102f,0.0823997f,0.513536f},{0.871333f,-0.199525f,0.448296f},{0.789235f,0.160319f,0.592795f}, +{0.760528f,0.173113f,0.625803f},{0.567484f,0.383543f,0.7286f},{0.155292f,0.0116503f,0.9878f}, +{0.198612f,0.0248425f,0.979763f},{0.530868f,-0.169252f,0.830381f},{0.492979f,-0.111174f,0.862909f}, +{0.57538f,-0.112175f,0.810157f},{0.729484f,-0.0330009f,0.683201f},{0.788689f,-0.0150649f,0.614608f}, +{0.777266f,0.0200755f,0.628852f},{0.775266f,-0.284338f,0.564016f},{0.723422f,-0.280706f,0.630766f}, +{0.813826f,0.330595f,0.477906f},{0.780357f,0.334197f,0.528541f},{0.749978f,-0.563062f,0.347123f}, +{0.828104f,-0.38699f,0.405564f},{0.817079f,0.255468f,0.516834f},{0.768042f,0.385863f,0.511098f}, +{0.859619f,0.103461f,0.500351f},{0.774398f,0.172551f,0.608714f},{0.772662f,-0.40118f,0.491984f}, +{0.701697f,-0.127203f,0.701028f},{0.540935f,-0.797161f,0.268184f},{0.888043f,-0.0861904f,0.45161f}, +{0.710171f,0.329627f,0.622096f},{0.869048f,0.194343f,0.454958f},{0.175361f,0.624014f,0.761482f}, +{0.637965f,0.41983f,0.645556f},{0.652155f,0.403813f,0.641583f},{0.62877f,0.443097f,0.638994f}, +{-0.971605f,0.236558f,-0.00496129f},{0.59756f,0.462793f,0.654786f},{0.580389f,0.451229f,0.677895f}, +{0.626065f,0.431011f,0.649825f},{0.476493f,0.537168f,0.695992f},{0.620057f,0.465976f,0.631186f}, +{0.571138f,0.569021f,0.591622f},{0.683396f,-0.0668563f,0.72698f},{0.793913f,-0.0870938f,0.601762f}, +{0.815752f,-0.501744f,0.287753f},{0.606837f,-0.736726f,0.298302f},{0.896606f,-0.245435f,0.368592f}, +{0.896765f,0.0226329f,0.441927f},{0.844094f,0.216265f,0.490648f},{0.777054f,0.398813f,0.486966f}, +{0.785435f,0.364075f,0.500541f},{0.839493f,0.275943f,0.468089f},{0.243468f,-0.824286f,-0.511152f}, +{0.346621f,-0.880084f,-0.32451f},{0.536899f,0.0021648f,0.843644f},{0.585054f,0.0566912f,0.809011f}, +{0.650729f,-0.0826509f,0.754798f},{0.375913f,0.296442f,0.877959f},{0.110642f,-0.716504f,-0.688752f}, +{-0.193341f,-0.395496f,-0.897888f},{0.628335f,0.415744f,0.657534f},{0.827336f,0.279735f,0.487097f}, +{0.612704f,0.444252f,0.653631f},{0.48401f,0.4591f,0.744958f},{0.643482f,0.399456f,0.652967f}, +{0.57849f,0.362153f,0.730886f},{0.603739f,0.353751f,0.714395f},{0.669212f,0.391928f,0.631307f}, +{0.637042f,0.3499f,0.686839f},{0.801865f,0.000502496f,0.597504f},{0.832038f,0.0306209f,0.553873f}, +{0.811466f,-0.055155f,0.581791f},{0.470295f,-0.121854f,0.874056f},{0.767111f,-0.500143f,0.401744f}, +{0.739372f,0.426622f,0.520886f},{0.411713f,-0.155696f,0.897915f},{0.455116f,-0.227652f,0.860839f}, +{0.381047f,-0.107122f,0.918329f},{0.693332f,0.310097f,0.650485f},{0.730533f,0.151737f,0.665805f}, +{0.776021f,0.246385f,0.580591f},{0.697968f,-0.373382f,0.611086f},{0.866595f,-0.263565f,0.42373f}, +{0.811826f,0.486604f,0.322731f},{0.761744f,0.331298f,0.556766f},{-0.298661f,0.350782f,-0.887555f}, +{0.915881f,0.147323f,0.373441f},{0.92463f,0.311156f,0.21964f},{0.368686f,-0.785445f,0.497139f}, +{0.531938f,-0.737281f,0.416483f},{0.38531f,-0.870855f,0.305203f},{0.671745f,-0.645467f,0.363499f}, +{0.716224f,0.342647f,0.60796f},{0.769271f,0.376456f,0.51624f},{0.821107f,0.176575f,0.542775f}, +{0.849541f,0.0720743f,0.522576f},{0.828452f,0.10049f,0.550971f},{0.77968f,0.163857f,0.604359f}, +{0.566189f,0.00160388f,0.824274f},{0.476003f,0.0827108f,0.875546f},{0.554853f,-0.0521107f,0.830315f}, +{0.589359f,-0.181224f,0.787282f},{0.662155f,-0.284197f,0.693385f},{0.61982f,-0.514361f,0.592668f}, +{0.581998f,0.163587f,0.796566f},{0.513561f,0.322466f,0.795154f},{0.562209f,0.321646f,0.761882f}, +{0.535793f,0.38685f,0.750515f},{0.526098f,0.419382f,0.739824f},{0.490478f,0.469349f,0.734264f}, +{0.64664f,0.294523f,-0.703643f},{0.624458f,-0.561986f,0.542424f},{0.803143f,-0.258757f,0.536663f}, +{0.84475f,-0.0441311f,0.533338f},{0.792842f,0.173526f,0.584201f},{0.702839f,0.333032f,0.628576f}, +{0.863811f,0.113649f,0.490831f},{0.843999f,0.0339543f,0.535269f},{0.725844f,0.0529413f,0.685819f}, +{-0.73513f,-0.674473f,0.0683321f},{0.504364f,-0.490432f,0.710699f},{0.37711f,-0.530281f,0.759335f}, +{0.583048f,-0.597629f,0.550359f},{0.0436126f,-0.966946f,-0.251225f},{0.537038f,0.494685f,0.683284f}, +{0.546936f,0.0266999f,0.836748f},{0.724826f,-0.463617f,0.509594f},{0.693024f,-0.379127f,0.613173f}, +{0.709473f,-0.552913f,0.436962f},{0.514175f,0.22697f,0.827109f},{0.557267f,0.12334f,0.821122f}, +{0.482026f,0.308139f,0.820184f},{0.513391f,0.555068f,0.654469f},{0.453179f,0.569752f,0.685574f}, +{0.406531f,-0.413933f,0.814489f},{0.811775f,0.578688f,-0.0783687f},{0.973187f,0.225135f,-0.0471338f}, +{0.434216f,0.566594f,0.700305f},{0.538412f,-0.036777f,0.841879f},{0.491574f,0.0444878f,0.869698f}, +{0.415802f,0.190611f,0.889256f},{0.617904f,-0.566391f,0.54534f},{0.699226f,-0.174741f,0.693216f}, +{0.746495f,-0.220216f,0.627894f},{0.648682f,0.147411f,0.746647f},{0.568483f,0.442257f,0.693711f}, +{0.57011f,0.139636f,0.809615f},{0.948333f,-0.29193f,-0.124263f},{0.476851f,0.0790041f,0.875427f}, +{0.895368f,0.438086f,0.0799757f},{0.861625f,0.504983f,0.0509301f},{0.905583f,0.412128f,0.100346f}, +{0.385951f,0.474715f,0.791004f},{0.500776f,0.315259f,0.806124f},{0.78597f,0.498529f,-0.365678f}, +{-0.039175f,-0.970836f,-0.236521f},{0.279225f,0.728435f,0.625633f},{0.355854f,0.545909f,0.758519f}, +{0.614204f,-0.411429f,0.673409f},{0.4737f,-0.4708f,0.744281f},{0.577135f,-0.434087f,0.691725f}, +{-0.49125f,0.856358f,-0.159137f},{0.183845f,0.927353f,0.32591f},{0.430745f,0.871104f,0.235873f}, +{0.533354f,0.00972195f,0.845836f},{0.523564f,0.106788f,0.845268f},{0.533085f,-0.134271f,0.835339f}, +{0.616118f,-0.527777f,0.584679f},{0.57025f,-0.527772f,0.629501f},{0.394466f,0.440697f,0.806339f}, +{0.426039f,0.454103f,0.782484f},{0.545621f,-0.597095f,-0.588026f},{0.686729f,-0.529675f,-0.497844f}, +{0.509716f,-0.260083f,0.820089f},{0.623851f,-0.377081f,0.684558f},{0.629402f,0.220525f,0.745132f}, +{0.529436f,0.339631f,0.777399f},{0.623309f,0.420085f,0.659556f},{0.80518f,0.0871658f,0.58659f}, +{0.780352f,-0.0962468f,0.617889f},{0.523776f,-0.321197f,0.788981f},{0.515573f,-0.306043f,0.800326f}, +{0.544485f,-0.219115f,0.809645f},{0.625398f,-0.231908f,0.745048f},{0.706314f,-0.255021f,0.660368f}, +{0.673997f,-0.0995925f,0.73199f},{0.175291f,0.557259f,0.811625f},{-0.316229f,0.390378f,-0.864641f}, +{0.602475f,-0.105106f,0.791187f},{0.511986f,-0.0480917f,0.857646f},{0.549755f,0.286096f,0.784805f}, +{0.401885f,0.448359f,0.798412f},{0.647712f,0.0793625f,0.75774f},{0.693393f,-0.138186f,0.707185f}, +{0.501942f,-0.419292f,0.756471f},{0.417585f,0.557046f,0.71786f},{0.48062f,0.607109f,0.63279f}, +{0.634624f,-0.714085f,-0.295524f},{0.301532f,0.609365f,0.733316f},{0.653937f,-0.26285f,0.70942f}, +{0.622959f,-0.417671f,0.661418f},{-0.27087f,-0.335809f,-0.902143f},{0.431305f,-0.544847f,0.719109f}, +{0.438527f,0.4447f,0.780984f},{0.475877f,0.4592f,0.750118f},{0.614748f,-0.108916f,0.781167f}, +{0.648812f,-0.709042f,-0.276231f},{0.807503f,-0.288409f,0.514548f},{0.329362f,0.199389f,0.922911f}, +{-0.288854f,-0.228841f,0.929621f},{0.032487f,-0.0237096f,0.999191f},{0.379595f,-0.203006f,0.902605f}, +{0.525301f,0.348523f,0.776267f},{0.353571f,0.251066f,0.901084f},{0.747355f,0.353664f,0.562478f}, +{-0.555693f,-0.784513f,-0.275219f},{0.411451f,0.499707f,0.762234f},{0.247906f,0.683824f,0.686241f}, +{0.432255f,0.553228f,0.712106f},{0.0447714f,0.929401f,0.366345f},{0.359402f,-0.545287f,0.757293f}, +{0.518952f,-0.151605f,0.841252f},{0.491923f,0.130876f,0.860746f},{0.414154f,0.50786f,0.755351f}, +{0.678972f,-0.1642f,0.715566f},{0.711066f,0.00492155f,0.703108f},{0.503994f,0.317354f,0.803291f}, +{0.588691f,0.195381f,0.784391f},{0.526698f,0.331299f,0.782834f},{0.716063f,0.386632f,0.58118f}, +{0.483935f,0.46453f,0.741632f},{0.495087f,0.569512f,0.656159f},{0.380152f,0.463059f,0.800663f}, +{0.30855f,0.620777f,0.720717f},{0.5734f,-0.641764f,-0.509266f},{0.380204f,0.394298f,0.836644f}, +{0.809792f,0.158265f,0.564969f},{0.555507f,0.484609f,0.675697f},{-0.740533f,-0.671531f,0.025636f}, +{-0.723145f,-0.674855f,0.147076f},{0.615847f,0.522535f,0.589652f},{0.512179f,-0.340231f,0.788616f}, +{0.536573f,-0.109383f,0.836735f},{0.529299f,0.12282f,0.839499f},{0.540318f,0.326725f,0.77544f}, +{0.538417f,0.44289f,0.716907f},{0.687982f,0.0622659f,0.723052f},{0.732887f,-0.246965f,0.633944f}, +{-0.477133f,0.364336f,-0.799752f},{-0.458535f,0.357291f,-0.813689f},{-0.45039f,0.349904f,-0.821411f}, +{0.2928f,0.188137f,0.937482f},{0.18232f,-0.391871f,0.901774f},{0.00676346f,-0.547607f,0.836708f}, +{0.916547f,-0.0457656f,-0.397299f},{0.762617f,-0.476852f,0.437066f},{0.815111f,-0.371701f,0.444335f}, +{0.786397f,-0.282666f,0.549254f},{-0.575086f,-0.0839676f,0.813773f},{0.712856f,0.388338f,0.583978f}, +{0.899224f,-0.35067f,-0.261584f},{0.774318f,-0.202262f,0.599601f},{0.702776f,-0.0325274f,0.710668f}, +{0.733857f,-0.17232f,0.657084f},{0.659872f,-0.0417907f,0.750215f},{0.307708f,-0.261296f,0.914899f}, +{0.208132f,-0.0752668f,0.975201f},{0.35429f,-0.556443f,0.751565f},{0.428666f,0.437099f,0.79069f}, +{0.509459f,0.418039f,0.752127f},{0.455826f,0.346165f,0.819996f},{0.673234f,-0.0798483f,0.735105f}, +{0.836109f,0.296137f,0.461763f},{0.781771f,0.338466f,0.523713f},{-0.206937f,-0.731467f,0.649718f}, +{0.182463f,0.804027f,0.565904f},{0.495997f,0.491771f,0.715645f},{0.697516f,-0.155143f,0.699573f}, +{0.640765f,-0.107144f,0.760224f},{0.667024f,-0.208429f,0.715288f},{0.69754f,-0.266859f,0.665f}, +{0.549992f,-0.105262f,0.82851f},{0.525844f,0.0216014f,0.850307f},{0.627803f,-0.145946f,0.764567f}, +{0.575798f,-0.110136f,0.81014f},{-0.97179f,0.206791f,0.113409f},{0.189983f,0.501541f,0.844016f}, +{0.5093f,0.462767f,0.725576f},{0.818183f,-0.189226f,0.542928f},{0.837951f,-0.0837523f,0.539281f}, +{0.494371f,-0.37279f,-0.785254f},{-0.46232f,-0.0898225f,0.882152f},{0.371185f,0.394377f,0.840648f}, +{0.473626f,-0.168205f,0.864514f},{-0.461698f,0.245268f,0.852454f},{0.529431f,-0.618975f,0.580148f}, +{0.431288f,-0.596394f,0.676983f},{0.518712f,-0.358702f,0.776061f},{0.359538f,0.526882f,0.770148f}, +{0.620336f,0.0945776f,0.778613f},{0.558303f,-0.222333f,0.799291f},{0.462001f,-0.280565f,0.841331f}, +{0.228678f,0.745663f,0.625854f},{0.581988f,-0.357027f,0.730631f},{0.671159f,-0.124645f,0.73076f}, +{0.657414f,0.107867f,0.745769f},{0.635504f,0.331112f,0.697495f},{0.527313f,0.468268f,0.708989f}, +{0.786941f,-0.0742031f,0.61255f},{0.78316f,0.0971358f,0.614187f},{0.679903f,-0.0312478f,-0.732636f}, +{0.722731f,-0.119831f,-0.680662f},{-0.304819f,0.292514f,-0.906378f},{-0.701588f,-0.688721f,0.182861f}, +{0.292159f,-0.372893f,0.880678f},{0.35086f,0.338823f,0.872981f},{0.49325f,-0.543568f,0.679145f}, +{0.482678f,-0.713783f,0.50748f},{-0.0412281f,-0.625784f,-0.778906f},{0.347424f,0.53187f,0.772276f}, +{0.363542f,0.452761f,0.814153f},{0.683475f,0.00488942f,0.729957f},{0.622766f,0.0726325f,0.779029f}, +{0.501262f,-0.479754f,0.72012f},{0.25502f,0.730842f,0.633115f},{0.337312f,0.626247f,0.702876f}, +{0.893756f,-0.323824f,-0.310383f},{0.661542f,-0.288546f,0.692173f},{0.615062f,-0.137833f,0.776338f}, +{0.679829f,-0.16072f,0.715543f},{0.715517f,0.135139f,0.6854f},{0.673723f,0.259116f,0.692067f}, +{0.607929f,0.357916f,0.708744f},{0.564176f,0.484896f,0.668268f},{0.605081f,0.132927f,0.784989f}, +{0.702442f,-0.0913318f,0.705856f},{0.524211f,-0.518737f,-0.675363f},{0.215163f,-0.85976f,0.46316f}, +{0.0578387f,0.325075f,-0.943918f},{-0.0515991f,0.412961f,-0.909286f},{-0.108005f,0.514926f,-0.850403f}, +{0.54637f,-0.00381208f,0.837535f},{0.614893f,-0.293542f,0.731942f},{0.541219f,-0.245194f,0.80434f}, +{0.398648f,0.234369f,0.886652f},{0.358081f,-0.189674f,0.914222f},{0.335383f,0.0542744f,0.940517f}, +{0.460648f,-0.309278f,0.831956f},{0.423308f,0.144847f,0.894332f},{0.435355f,0.202096f,0.877282f}, +{0.603528f,0.44959f,0.6585f},{0.679775f,-0.0385942f,0.732405f},{0.653824f,0.107886f,0.748916f}, +{0.759406f,0.0271836f,0.650049f},{0.574702f,0.413968f,0.705938f},{0.360767f,0.550165f,0.753104f}, +{-0.887326f,0.448617f,-0.106749f},{-0.933431f,0.327008f,-0.147555f},{0.5428f,-0.207117f,0.813923f}, +{0.494759f,-0.106389f,0.862493f},{-0.395446f,-0.207517f,-0.89474f},{-0.355182f,-0.249591f,-0.900861f}, +{0.495157f,-0.00965679f,0.86875f},{0.643235f,0.350247f,0.680864f},{0.570022f,0.501044f,0.651176f}, +{0.617122f,0.271777f,0.738443f},{0.862234f,0.144092f,0.485582f},{0.977463f,0.0865015f,0.192573f}, +{0.967945f,0.0949319f,0.232529f},{-0.304308f,0.397193f,-0.865814f},{0.270602f,-0.246793f,0.93052f}, +{0.577148f,0.34371f,0.740786f},{0.400594f,0.586566f,0.703893f},{0.302236f,0.559956f,0.771429f}, +{0.521137f,0.302706f,0.797988f},{0.502864f,0.414301f,0.758605f},{0.594349f,-0.0767115f,0.80054f}, +{0.573793f,-0.176939f,0.799659f},{0.62133f,0.0448709f,0.782264f},{0.581222f,0.0850307f,0.80929f}, +{0.682527f,-0.0396461f,0.729784f},{0.636348f,0.03267f,0.77071f},{0.388821f,-0.0387993f,0.920496f}, +{0.542248f,-0.00517707f,0.840202f},{0.581726f,-0.0418682f,0.812306f},{0.655859f,-0.313951f,0.686501f}, +{0.727557f,-0.0868233f,0.680531f},{0.744094f,0.140968f,0.653033f},{0.662563f,0.288153f,0.69136f}, +{0.497747f,-0.511148f,0.700696f},{-0.687492f,0.198204f,-0.69862f},{-0.565646f,0.0336114f,-0.823963f}, +{-0.63122f,0.176192f,-0.755326f},{0.715262f,0.396856f,0.575244f},{0.713432f,0.39338f,0.579885f}, +{-0.923237f,0.361203f,-0.131016f},{-0.998356f,0.0419347f,0.0390748f},{-0.996558f,0.0211225f,-0.0801658f}, +{0.440074f,0.261607f,0.859009f},{0.507926f,0.00577762f,0.861381f},{0.598678f,-0.238457f,0.764672f}, +{0.595584f,0.0324451f,0.802637f},{0.681167f,-0.213054f,0.700442f},{0.760255f,-0.0271448f,0.649057f}, +{0.769925f,0.103971f,0.629607f},{0.716696f,0.189305f,0.6712f},{0.664742f,0.357915f,0.655755f}, +{0.796516f,0.168019f,0.580802f},{0.807462f,0.0110927f,0.589815f},{0.846207f,0.128317f,0.517174f}, +{0.632386f,0.0778414f,-0.770732f},{0.698958f,0.0421958f,-0.713917f},{0.745128f,-0.0767328f,-0.662492f}, +{0.3335f,-0.145014f,0.93153f},{0.865745f,0.491554f,0.0941269f},{0.707032f,0.416355f,0.571625f}, +{0.80999f,0.565921f,0.153785f},{0.877285f,0.466554f,0.112687f},{0.908767f,0.406955f,0.0923594f}, +{0.668257f,-0.432233f,0.605481f},{0.672297f,-0.440256f,0.595141f},{0.590598f,0.120871f,0.797862f}, +{0.454966f,0.559227f,0.693016f},{0.549444f,0.578269f,0.603089f},{0.535451f,-0.533401f,0.654809f}, +{0.778287f,-0.614793f,-0.127668f},{0.770378f,-0.637588f,7.24516e-05f},{0.518565f,-0.0232879f,0.854721f}, +{0.49947f,0.00200443f,0.866329f},{0.475824f,0.117396f,0.871671f},{0.478578f,0.335545f,0.811401f}, +{0.552119f,0.216155f,0.805259f},{0.746915f,-0.129145f,0.652258f},{0.72802f,0.199855f,0.655778f}, +{0.753832f,-0.195787f,0.62722f},{0.656384f,0.422092f,0.625299f},{0.636566f,0.465569f,0.614841f}, +{0.914171f,0.107873f,0.390712f},{0.829913f,0.220967f,0.512268f},{0.932403f,0.11802f,0.341608f}, +{-0.25645f,-0.502436f,-0.825706f},{-0.941174f,0.222725f,-0.254137f},{0.623564f,-0.563809f,0.54156f}, +{0.439151f,0.502801f,0.744539f},{0.784586f,-0.297617f,0.54392f},{0.114849f,-0.45399f,-0.883574f}, +{0.694739f,-0.719229f,0.00687289f},{0.901992f,0.424732f,0.0775473f},{0.0386906f,-0.76215f,-0.646243f}, +{0.534589f,0.215435f,0.817192f},{0.418646f,0.233704f,0.877564f},{0.491832f,0.162436f,0.855404f}, +{-0.578384f,0.497412f,-0.646571f},{-0.83752f,0.114356f,-0.534306f},{-0.751325f,-0.0297917f,-0.659259f}, +{0.729163f,0.361824f,0.580865f},{0.759f,0.303823f,0.575857f},{0.22265f,-0.950099f,-0.218494f}, +{0.749635f,-0.441831f,0.49278f},{0.62442f,0.407023f,0.666657f},{0.480825f,0.343945f,0.806542f}, +{0.484674f,0.377617f,0.788984f},{-0.359597f,-0.178119f,-0.915949f},{-0.132927f,-0.480289f,-0.866979f}, +{0.618301f,0.114174f,0.777605f},{0.650738f,-0.0654459f,0.756476f},{0.818155f,-0.134811f,0.558971f}, +{0.751298f,0.243416f,0.613433f},{0.760882f,0.478202f,0.438613f},{0.766475f,0.346567f,0.540747f}, +{0.548821f,-0.33437f,0.766154f},{0.914253f,0.110328f,0.389832f},{-0.908437f,0.392483f,0.143871f}, +{-0.916873f,0.398846f,-0.0162913f},{0.577586f,0.157636f,0.800965f},{0.956451f,-9.39214e-05f,0.291893f}, +{0.513721f,0.405479f,0.756093f},{0.459552f,0.406286f,0.789775f},{0.63472f,0.110533f,0.764796f}, +{0.638935f,0.168088f,0.750672f},{0.851403f,-0.491367f,-0.183499f},{0.743817f,0.0571238f,0.665938f}, +{0.624312f,0.0274681f,0.780692f},{0.56851f,-0.179512f,0.802852f},{0.899508f,0.0827162f,0.429003f}, +{0.841008f,0.0246865f,0.540459f},{0.817259f,0.0591081f,0.573232f},{0.827772f,0.18779f,0.528704f}, +{0.498712f,-0.488039f,0.716313f},{0.697539f,-0.186868f,0.691751f},{0.583311f,-0.151649f,0.797967f}, +{0.773136f,0.0139612f,0.634087f},{0.794927f,-0.0103512f,0.606617f},{0.565734f,0.479916f,0.670542f}, +{-0.864742f,0.103488f,0.491439f},{0.828208f,-0.00768752f,0.560368f},{0.561537f,-0.286341f,0.776328f}, +{0.508557f,0.150787f,0.847722f},{0.545509f,0.274793f,0.791776f},{0.594812f,-0.0292075f,0.803334f}, +{0.603583f,0.135798f,0.78565f},{0.442723f,0.0788834f,0.893182f},{0.554001f,0.29244f,0.779463f}, +{0.600309f,-0.0313817f,0.799152f},{0.399517f,-0.0519389f,0.915253f},{0.452671f,0.0560515f,0.889914f}, +{0.591323f,0.26021f,0.7633f},{0.608531f,0.316253f,0.727787f},{0.652664f,0.0892208f,0.752376f}, +{0.608645f,0.00582163f,0.793422f},{0.534761f,0.021969f,0.844718f},{0.552932f,0.0923861f,0.828089f}, +{0.615998f,0.155852f,0.772177f},{0.708251f,0.0938871f,0.69969f},{0.448258f,-0.39374f,0.802517f}, +{0.955745f,0.0600352f,0.288004f},{0.48462f,-0.528963f,0.696665f},{0.786507f,0.367476f,0.496355f}, +{0.946708f,0.312889f,0.0764525f},{0.970324f,0.231022f,0.0714221f},{0.622192f,-0.479217f,0.619054f}, +{0.528436f,0.369101f,0.764539f},{0.780853f,0.00919292f,0.624647f},{0.387582f,-0.131106f,0.912465f}, +{0.50548f,-0.154984f,0.848805f},{0.877049f,0.473586f,0.0806271f},{0.829991f,0.555528f,0.0500242f}, +{0.0380677f,-0.422558f,-0.905536f},{-0.0386355f,-0.416857f,-0.908151f},{0.101346f,-0.34475f,-0.933208f}, +{-0.65478f,0.227072f,-0.720903f},{0.554038f,0.392875f,0.733956f},{0.234576f,0.762263f,0.603265f}, +{0.25995f,0.711212f,0.653149f},{0.498703f,-0.320812f,0.805217f},{0.524817f,-0.317889f,0.789629f}, +{0.775137f,-0.392992f,0.494691f},{0.58636f,-0.0781912f,0.806268f},{0.840356f,0.34648f,0.416837f}, +{0.876942f,0.455574f,0.153053f},{0.908257f,0.364583f,0.2053f},{0.875224f,-0.483566f,-0.0121026f}, +{0.917423f,-0.388275f,0.0870448f},{0.562132f,0.160272f,0.811369f},{0.170127f,-0.327533f,0.929397f}, +{0.768738f,-0.0521177f,0.637436f},{0.761336f,-0.026129f,0.647831f},{0.886198f,-0.0253639f,0.462612f}, +{0.884802f,-0.0240317f,0.465348f},{0.875003f,-0.0269654f,0.483366f},{0.888156f,-0.303303f,-0.345232f}, +{0.720994f,0.41486f,0.55503f},{0.73089f,0.493643f,0.471292f},{0.799211f,0.510579f,0.317129f}, +{0.766819f,0.495596f,0.407889f},{0.716985f,0.389142f,0.578361f},{0.66729f,-0.114149f,0.735998f}, +{0.654942f,-0.233812f,0.718598f},{0.821195f,-0.308784f,0.479887f},{0.775726f,-0.3568f,0.520521f}, +{0.717386f,-0.30496f,0.626383f},{0.208891f,-0.812635f,-0.544049f},{0.859058f,0.105923f,0.500799f}, +{0.881403f,0.14631f,0.449135f},{0.841431f,0.0925177f,0.532386f},{0.565092f,0.0003129f,0.825028f}, +{0.54228f,-0.0389849f,0.839293f},{-0.372746f,0.134499f,-0.918134f},{0.72628f,0.0503029f,-0.685556f}, +{0.787154f,-0.0814957f,-0.611349f},{0.769883f,-0.297935f,-0.564371f},{0.744067f,0.141766f,0.652891f}, +{0.574057f,0.0905282f,0.813795f},{0.761955f,0.157928f,0.62808f},{0.887165f,0.0459843f,0.459156f}, +{0.885574f,0.209166f,0.414738f},{0.72059f,0.381289f,0.579111f},{0.90903f,0.274057f,0.313939f}, +{0.450923f,-0.195169f,0.870964f},{0.739011f,0.117632f,0.663344f},{0.571164f,-0.541364f,0.617007f}, +{0.889148f,0.0404144f,0.455832f},{-0.407865f,0.585904f,-0.700259f},{0.680773f,-0.427493f,0.594809f}, +{0.715782f,-0.442783f,0.54f},{0.568523f,-0.363063f,0.738219f},{0.309482f,-0.182632f,0.933202f}, +{-0.919947f,0.0886574f,-0.381888f},{0.861114f,-0.0447529f,0.506438f},{0.821222f,-0.0124579f,0.570473f}, +{0.81439f,-0.0385379f,0.579037f},{0.192437f,0.0496368f,0.980053f},{0.478856f,0.612574f,0.628848f}, +{-0.511654f,0.318298f,-0.798058f},{-0.601647f,0.281855f,-0.747381f},{0.251524f,0.118615f,-0.960555f}, +{0.225671f,0.123337f,-0.966365f},{0.912175f,-0.0206556f,0.409281f},{0.902009f,-0.018796f,0.431308f}, +{0.93251f,3.12715e-05f,0.361143f},{0.660423f,0.0123608f,0.750792f},{0.841922f,0.338374f,0.420321f}, +{0.48217f,-0.30124f,0.822658f},{0.213331f,-0.966013f,-0.145976f},{0.982792f,0.184059f,-0.0155821f}, +{0.882267f,0.470176f,-0.0232345f},{0.897305f,0.413593f,0.154221f},{-0.452893f,0.251605f,-0.855326f}, +{-0.42283f,0.351254f,-0.835366f},{0.831731f,0.186743f,0.522829f},{0.885981f,0.159897f,0.435283f}, +{0.528091f,-0.0916303f,0.84423f},{-0.5275f,0.392926f,-0.753229f},{-0.414898f,0.226583f,-0.881203f}, +{-0.449899f,0.203977f,-0.869473f},{0.792407f,0.309002f,0.525936f},{0.702105f,-0.174158f,0.690447f}, +{0.601373f,-0.0740094f,0.795533f},{0.550285f,0.795586f,0.253436f},{0.234523f,0.778306f,0.582442f}, +{0.324053f,0.512421f,0.795245f},{0.574058f,-0.409459f,0.709084f},{0.248926f,-0.872222f,-0.421028f}, +{0.291238f,0.681894f,0.67097f},{-0.206327f,0.155371f,-0.966069f},{-0.179661f,0.246138f,-0.952438f}, +{-0.129275f,0.294127f,-0.946983f},{-0.236405f,0.381324f,-0.893703f},{0.686435f,0.401971f,0.605992f}, +{-0.408368f,0.355119f,-0.840908f},{0.820717f,-0.249024f,0.514208f},{0.729541f,-0.276127f,0.625719f}, +{0.981621f,0.137155f,0.132695f},{0.883296f,0.312291f,-0.349661f},{0.873408f,0.450495f,-0.184969f}, +{0.498945f,0.610413f,0.615183f},{0.778182f,0.38333f,0.497485f},{0.283819f,0.610949f,0.739046f}, +{0.883323f,0.255649f,0.392918f},{0.799895f,0.0390677f,0.598868f},{0.810366f,0.0758698f,0.580991f}, +{0.852675f,-0.235047f,0.466582f},{0.888261f,-0.0854651f,0.451318f},{0.87399f,-0.0845339f,0.478535f}, +{0.0351843f,-0.998869f,-0.0319747f},{0.0305101f,-0.99916f,-0.0273486f},{0.537026f,0.273246f,0.798085f}, +{0.0538744f,-0.983474f,-0.172848f},{-0.316547f,-0.843145f,-0.434632f},{0.468404f,0.603343f,0.645426f}, +{-0.178733f,-0.51791f,-0.836555f},{0.591741f,-0.474756f,-0.651498f},{0.12968f,-0.977424f,-0.166811f}, +{0.421402f,-0.892298f,-0.161939f},{-0.737569f,0.5865f,0.33468f},{-0.849297f,0.462132f,0.255202f}, +{-0.394842f,-0.918423f,0.0244794f},{-0.463997f,0.132699f,-0.875841f},{-0.322235f,0.0572273f,-0.944928f}, +{-0.373209f,0.0745975f,-0.924743f},{0.486459f,-0.162385f,0.85848f},{-0.482457f,-0.842529f,-0.239541f}, +{0.499279f,-0.259573f,0.826645f},{0.411235f,0.323673f,0.852128f},{0.554669f,-0.34206f,0.75851f}, +{0.473195f,-0.178033f,0.862781f},{0.849404f,0.518863f,0.096404f},{0.876804f,0.461771f,0.134098f}, +{0.758995f,-0.279535f,0.588036f},{0.425511f,-0.715773f,-0.553723f},{0.340117f,-0.913034f,-0.225142f}, +{0.720202f,0.392223f,0.57225f},{-0.887605f,0.375631f,-0.266567f},{-0.870968f,-0.10751f,-0.479434f}, +{-0.880827f,0.148713f,-0.449475f},{0.625715f,-0.186516f,0.757425f},{0.49642f,-0.788635f,0.362796f}, +{0.450044f,-0.869811f,0.202213f},{0.769115f,-0.312414f,0.557547f},{0.727369f,-0.115659f,0.676429f}, +{0.0593964f,-0.478543f,0.876053f},{0.164209f,0.0300051f,0.985969f},{-0.0644165f,0.0478143f,0.996777f}, +{0.152902f,0.149191f,0.976915f},{0.483827f,-0.665811f,0.567986f},{0.896442f,-0.0319358f,0.442009f}, +{0.914686f,-0.0438937f,0.401775f},{-0.451712f,-0.831749f,-0.322723f},{-0.297702f,-0.794045f,-0.529968f}, +{0.888428f,0.139476f,0.437312f},{0.175709f,-0.322229f,0.930212f},{0.514633f,-0.835931f,0.190714f}, +{0.92474f,0.379415f,0.0300102f},{0.918099f,0.386049f,0.0897775f},{0.814101f,-0.545264f,-0.199817f}, +{0.912396f,0.0460901f,0.406706f},{-0.777079f,-0.577772f,-0.249656f},{-0.669531f,-0.626988f,-0.398264f}, +{0.0198351f,-0.999244f,0.0334435f},{0.0138369f,-0.999067f,0.0408985f},{0.0159619f,-0.999343f,0.0325355f}, +{0.886832f,-0.0337723f,0.460856f},{-0.90871f,0.244633f,-0.338232f},{-0.0909099f,0.558564f,-0.824464f}, +{0.60105f,0.682827f,-0.415314f},{-0.790674f,-0.288535f,-0.539984f},{0.0880274f,-0.995293f,0.0405437f}, +{0.00338008f,-0.520431f,0.853897f},{0.712797f,0.446657f,0.540757f},{0.813274f,-0.529637f,0.240976f}, +{0.908879f,-0.35302f,0.222074f},{0.718433f,-0.162821f,0.676272f},{0.181672f,0.904069f,-0.386852f}, +{-0.417861f,0.905006f,0.0797298f},{-0.713732f,0.577259f,0.396685f},{-0.076924f,-0.696852f,-0.713078f}, +{-0.181321f,-0.570804f,-0.800816f},{-0.191037f,-0.442903f,-0.875981f},{-0.0504743f,-0.580727f,-0.812532f}, +{-0.926669f,-0.356093f,-0.120341f},{-0.892653f,-0.449437f,-0.0343083f},{-0.986856f,-0.133881f,-0.0905015f}, +{0.587545f,-0.717401f,0.374334f},{0.998502f,0.0530506f,-0.0133723f},{0.949732f,0.30454f,0.0725536f}, +{0.998781f,0.0489191f,-0.00662695f},{0.996435f,0.0816746f,0.0211487f},{0.68741f,0.0823538f,0.721585f}, +{0.584366f,0.244026f,0.77393f},{0.269489f,0.147477f,0.951644f},{0.519998f,-0.60239f,0.605581f}, +{0.567408f,-0.484303f,0.665957f},{-0.676466f,-0.181577f,-0.713739f},{-0.802923f,-0.132063f,-0.58127f}, +{-0.582632f,-0.207995f,-0.78567f},{0.746876f,-0.118077f,0.654396f},{0.975213f,-0.209994f,-0.0697239f}, +{0.60946f,0.229956f,0.758735f},{0.764709f,-0.00540347f,0.644353f},{0.680325f,-0.379919f,0.626754f}, +{0.736298f,-0.112824f,0.667185f},{0.616818f,-0.00552029f,0.787086f},{0.895137f,-0.311959f,-0.318451f}, +{-0.709982f,-0.133843f,-0.691384f},{-0.577864f,-0.187655f,-0.794266f},{0.83979f,-0.15125f,0.521417f}, +{0.596297f,0.0624722f,0.800329f},{0.0105197f,-0.998823f,0.0473539f},{0.0265853f,-0.99901f,0.0356652f}, +{0.0124122f,-0.998977f,0.0434877f},{-0.678991f,0.664674f,-0.311736f},{0.33402f,0.150594f,0.930458f}, +{-0.432098f,0.335744f,0.836999f},{0.692931f,-0.0653427f,0.718037f},{0.266962f,0.858519f,0.437808f}, +{0.875412f,-0.110105f,0.47067f},{0.931732f,-0.0463402f,0.360177f},{0.917928f,0.390128f,0.0721717f}, +{0.926793f,-0.0150992f,0.375268f},{0.742293f,0.37572f,0.554829f},{0.722769f,-0.643509f,0.251992f}, +{0.921411f,-0.112764f,0.371869f},{-0.552981f,0.11925f,-0.824616f},{0.0969255f,0.700357f,0.707181f}, +{0.815723f,-0.0576197f,0.575566f},{-0.959286f,0.0233389f,-0.281471f},{0.938652f,0.278602f,0.203255f}, +{0.291122f,-0.576791f,0.763256f},{0.224105f,-0.751766f,0.620181f},{0.262298f,-0.881578f,0.392453f}, +{-0.558518f,-0.125543f,-0.819937f},{-0.419377f,-0.0666438f,-0.905363f},{-0.510491f,-0.319457f,-0.798339f}, +{0.341107f,-0.0284138f,0.939595f},{0.858203f,-0.0563061f,0.510212f},{0.880166f,0.164233f,0.44535f}, +{0.689148f,0.496612f,0.527686f},{0.771216f,-0.14831f,0.619056f},{0.771271f,0.507287f,-0.384448f}, +{0.834734f,0.227488f,-0.501467f},{0.85188f,-0.0369246f,0.522434f},{0.386071f,0.227234f,-0.894044f}, +{0.325297f,0.269005f,-0.906542f},{0.378206f,0.379987f,-0.844139f},{0.662992f,-0.227769f,0.713136f}, +{0.594588f,-0.0322742f,0.803382f},{0.804086f,-0.0198517f,0.594182f},{0.736753f,0.0181734f,0.675918f}, +{0.326986f,0.196476f,0.924379f},{0.299563f,0.339299f,0.891705f},{0.884397f,0.126928f,0.449145f}, +{0.415895f,0.703623f,0.576147f},{0.270987f,0.684654f,0.676621f},{0.886993f,0.123335f,0.445007f}, +{0.618728f,-0.106254f,0.778386f},{0.00880166f,-0.999707f,-0.0225414f},{0.0201949f,-0.999687f,-0.0147667f}, +{0.0280835f,-0.999382f,-0.0211496f},{0.310203f,-0.397361f,0.863642f},{-0.0407621f,0.187927f,-0.981337f}, +{0.494633f,-0.865635f,-0.0775528f},{0.590065f,0.0157559f,0.807202f},{0.756401f,-0.214401f,0.617972f}, +{0.70386f,-0.501947f,0.502623f},{0.513046f,0.0939776f,0.853201f},{0.561868f,-0.598621f,0.570926f}, +{-0.882151f,-0.304941f,0.358916f},{-0.922118f,-0.257219f,0.289027f},{-0.869252f,-0.420449f,0.260043f}, +{-0.0299809f,-0.993548f,0.109381f},{-0.00991282f,-0.999769f,0.0190808f},{0.227082f,-0.483709f,0.845257f}, +{0.318876f,-0.00218194f,0.947794f},{-0.721335f,-0.58379f,0.372645f},{-0.844342f,-0.429109f,0.320862f}, +{-0.793675f,-0.561927f,0.233064f},{-0.446212f,0.0339292f,-0.894284f},{-0.542424f,0.173159f,-0.822066f}, +{-0.523215f,0.18899f,-0.83098f},{0.852689f,0.312839f,0.418393f},{0.326618f,0.746278f,-0.579991f}, +{0.43824f,0.765982f,-0.470337f},{0.394471f,0.836057f,-0.381316f},{0.947904f,0.0496112f,0.31467f}, +{-0.69292f,-0.718449f,0.0607708f},{-0.83053f,-0.546689f,-0.106539f},{-0.968231f,0.0429053f,0.246349f}, +{-0.823249f,0.447806f,0.3489f},{0.0290057f,-0.99844f,0.0477127f},{0.135844f,0.356041f,-0.924544f}, +{0.157125f,0.356013f,-0.921177f},{0.702615f,-0.705316f,0.094134f},{-0.207965f,0.00367548f,-0.978129f}, +{-0.553098f,0.275765f,-0.786152f},{-0.646691f,0.197265f,-0.736802f},{-0.67218f,0.236112f,-0.70173f}, +{0.730094f,-0.00643599f,0.683316f},{0.942911f,-0.321198f,0.0880354f},{0.837478f,0.339639f,0.428107f}, +{0.522261f,0.307107f,0.795569f},{0.57665f,-0.609907f,0.543588f},{0.59619f,-0.248945f,0.763272f}, +{0.663708f,-0.395918f,0.634619f},{0.363764f,0.0543848f,0.929902f},{-0.624526f,-0.732207f,0.271734f}, +{-0.62029f,-0.71074f,0.331798f},{0.911724f,0.135763f,0.387721f},{0.181805f,0.489394f,0.852901f}, +{0.336708f,-0.102173f,0.93605f},{0.354085f,0.0237752f,0.934911f},{0.57874f,0.741724f,-0.338977f}, +{0.545642f,0.698697f,-0.462706f},{0.566298f,0.757711f,-0.324314f},{-0.314115f,0.947718f,0.0562388f}, +{0.581017f,-0.458852f,0.672216f},{-0.334189f,0.465636f,-0.819452f},{-0.312672f,0.554287f,-0.771364f}, +{0.581397f,-0.348002f,0.73544f},{-0.0906116f,0.499157f,0.861761f},{0.0294859f,-0.0565748f,-0.997963f}, +{-0.0298575f,0.0125533f,-0.999475f},{-0.0240975f,-0.0732973f,-0.997019f},{-0.367049f,0.148835f,-0.918217f}, +{-0.0153553f,0.739279f,-0.673224f},{0.3737f,0.71988f,-0.584911f},{0.291589f,0.00169327f,0.956542f}, +{-0.405861f,0.384739f,-0.829007f},{-0.444386f,0.455033f,-0.771664f},{-0.701359f,0.404044f,-0.587234f}, +{0.606879f,-0.193662f,0.770839f},{0.561341f,-0.104168f,0.821003f},{0.39326f,0.358897f,0.846486f}, +{0.328906f,-0.335092f,0.882912f},{-0.0572828f,0.247977f,-0.967071f},{0.880482f,-0.0150773f,0.47384f}, +{-0.358699f,0.160404f,-0.919568f},{0.36513f,0.322964f,0.873141f},{0.871962f,0.0410214f,0.487851f}, +{0.288687f,0.0169397f,0.957274f},{0.551894f,-0.577205f,0.601871f},{0.360338f,-0.865636f,-0.347608f}, +{0.293413f,-0.897843f,-0.328309f},{0.399519f,0.467113f,0.78879f},{0.696026f,-0.0722678f,0.714371f}, +{-0.0496104f,-0.0161746f,-0.998638f},{0.017165f,-0.0241149f,-0.999562f},{0.221142f,-0.382684f,0.897023f}, +{-0.175494f,0.594691f,-0.784566f},{0.0255035f,0.0935805f,-0.995285f},{-0.0210553f,0.317065f,-0.94817f}, +{0.509115f,0.633526f,0.58262f},{0.490691f,-0.311449f,0.81377f},{0.33886f,0.225938f,0.913305f}, +{0.465134f,-0.14352f,-0.873529f},{0.365383f,-0.100181f,-0.925451f},{-0.633866f,0.676616f,0.374707f}, +{-0.287038f,0.322961f,-0.901834f},{-0.720707f,-0.115035f,-0.683629f},{-0.499874f,-0.0901865f,-0.86139f}, +{-0.257732f,0.840352f,-0.476846f},{0.509487f,-0.632754f,0.583134f},{0.287168f,-0.31054f,0.906145f}, +{0.299274f,0.0483362f,0.952942f},{-0.100852f,0.454565f,-0.884986f},{0.67887f,0.428883f,-0.595982f}, +{0.497894f,-0.411631f,0.763323f},{0.244059f,0.609261f,0.754478f},{0.782364f,-0.223263f,0.581429f}, +{-0.310859f,0.769689f,0.557625f},{-0.370555f,0.816573f,0.442603f},{0.396677f,0.000531581f,0.917958f}, +{-0.0780473f,0.136668f,-0.987538f},{-0.193533f,0.894149f,0.403785f},{-0.469226f,0.810899f,0.349672f}, +{-0.599307f,0.720881f,0.348083f},{0.597735f,0.798804f,-0.0680163f},{-0.390329f,0.730714f,-0.56009f}, +{0.959444f,0.0389925f,0.27919f},{0.0866216f,-0.0782957f,-0.99316f},{0.0659964f,-0.0736851f,-0.995095f}, +{0.2912f,0.0574939f,0.954933f},{0.88907f,0.321536f,0.325836f},{-0.0155458f,0.906453f,0.42202f}, +{0.666373f,-0.500383f,-0.552778f},{0.8608f,0.346833f,0.372466f},{0.843227f,0.25116f,0.475275f}, +{-0.453813f,-0.0143082f,-0.890982f},{-0.24794f,-0.307459f,-0.918692f},{0.257164f,0.0550692f,0.964797f}, +{-0.366218f,-0.429376f,0.825543f},{-0.4966f,-0.309925f,0.810762f},{0.127606f,-0.14502f,-0.981166f}, +{-0.762129f,-0.60679f,0.225756f},{0.0143175f,-0.999788f,-0.0147732f},{0.0109175f,-0.99987f,-0.0118935f}, +{0.00619323f,-0.999932f,-0.00986292f},{-0.142064f,-0.566734f,-0.811561f},{-0.982566f,-0.0233312f,-0.184445f}, +{-0.982201f,0.185549f,-0.0292062f},{-0.52164f,0.791731f,-0.31789f},{-0.644517f,0.597376f,-0.477221f}, +{-0.696846f,0.483527f,-0.529723f},{-0.883865f,0.259645f,0.389059f},{-0.89337f,0.245748f,0.376163f}, +{0.092153f,-0.238835f,-0.966678f},{-0.898211f,0.397905f,-0.186783f},{0.997552f,-0.0618775f,-0.032576f}, +{0.993174f,-0.107765f,-0.0446236f},{-0.940601f,0.262619f,-0.215175f},{-0.897251f,0.333562f,-0.289268f}, +{0.439447f,-0.896273f,0.0598437f},{0.400452f,-0.910927f,0.0992435f},{0.427674f,-0.902604f,0.0490067f}, +{0.0360786f,-0.997277f,0.0643199f},{0.0645358f,-0.994324f,0.0845833f},{0.224178f,-0.969841f,0.0956641f}, +{-0.980638f,-0.00736646f,-0.195689f},{-0.572463f,-0.725649f,-0.381733f},{-0.618061f,-0.659854f,-0.42731f}, +{-0.362638f,-0.894922f,-0.260017f},{0.00462647f,-0.999587f,-0.0283808f},{-0.00164635f,-0.999876f,-0.015652f}, +{-0.00983998f,-0.999952f,0.000305002f},{0.973157f,-0.183172f,-0.139335f},{0.996745f,-0.0737784f,-0.0324859f}, +{0.986885f,-0.111135f,-0.11708f},{0.997794f,0.0325288f,-0.0578666f},{0.995577f,0.0928073f,0.0145992f}, +{-0.83468f,-0.496603f,0.238109f},{0.96726f,0.253285f,0.0159274f},{0.961391f,0.272285f,0.0398579f}, +{-0.359224f,0.378528f,-0.853039f},{-0.492686f,0.422685f,-0.760656f},{-0.0326768f,0.862869f,-0.504371f}, +{-0.0986429f,0.811704f,-0.575678f},{-0.0482041f,0.821139f,-0.56869f},{0.695949f,-0.682593f,-0.222985f}, +{-0.231695f,0.317407f,-0.919549f},{-0.339532f,0.320155f,-0.884431f},{-0.395214f,0.859704f,0.323596f}, +{0.3429f,0.415894f,-0.84229f},{0.385219f,0.263828f,-0.884308f},{0.378023f,0.302815f,-0.874873f}, +{-0.960881f,0.24907f,0.121132f},{-0.971621f,0.226807f,0.0671622f},{-0.37359f,0.599063f,-0.708204f}, +{-0.377794f,0.819743f,-0.430457f},{-0.609038f,0.677555f,-0.412301f},{0.385864f,-0.911894f,0.139852f}, +{0.0697907f,-0.995996f,0.0558698f},{0.845772f,0.507229f,0.165494f},{0.777954f,0.593335f,0.206739f}, +{0.920531f,0.362211f,0.146376f},{0.912664f,0.389791f,0.122911f},{0.984758f,0.16318f,0.0601944f}, +{0.997034f,0.0679387f,0.0361566f},{0.994588f,-0.101538f,-0.0220202f},{-0.666404f,0.563915f,-0.487755f}, +{-0.714725f,0.362688f,-0.598018f},{0.83658f,-0.340717f,-0.429007f},{0.863925f,-0.450595f,-0.224937f}, +{-0.799294f,0.529921f,0.283395f},{-0.967002f,0.195641f,0.163192f},{-0.339817f,0.914487f,-0.219634f}, +{-0.545492f,0.8063f,-0.228733f},{0.672512f,-0.114677f,-0.731148f},{-0.762563f,-0.532268f,-0.367679f}, +{-0.876042f,-0.304687f,-0.373786f},{-0.844516f,-0.278079f,-0.457673f},{-0.929772f,-0.312106f,-0.195229f}, +{-0.536093f,0.714569f,-0.449439f},{-0.33016f,0.913565f,-0.237471f},{-0.379477f,0.861928f,-0.336271f}, +{0.971925f,-0.229338f,-0.052599f},{-0.861158f,0.00599323f,-0.508301f},{-0.900303f,0.0329582f,-0.434013f}, +{-0.885967f,-0.0618478f,-0.459606f},{-0.536935f,-0.162785f,-0.827769f},{0.100273f,-0.323968f,-0.940739f}, +{-0.118235f,-0.477087f,-0.870867f},{0.74417f,-0.572401f,-0.344338f},{-0.875321f,-0.0583348f,-0.480011f}, +{0.940173f,0.151028f,-0.305393f},{0.962733f,-0.240052f,0.124578f},{0.76268f,-0.354927f,-0.54069f}, +{-0.999025f,0.00702945f,0.043579f},{-0.982233f,-0.171173f,0.0769339f},{-0.942957f,0.159886f,-0.292009f}, +{-0.478959f,0.775714f,-0.410934f},{0.982423f,-0.186548f,-0.00665627f},{0.990741f,-0.132479f,0.0296812f}, +{0.931631f,-0.354107f,-0.0816785f},{0.791666f,-0.0679739f,-0.607161f},{0.754519f,-0.0879699f,-0.650356f}, +{0.793904f,-0.0796276f,-0.602807f},{-0.677332f,0.731321f,-0.0799375f},{-0.656232f,0.708389f,-0.259893f}, +{-0.838214f,0.504165f,-0.207881f},{0.204869f,-0.840839f,0.501018f},{0.153821f,-0.480468f,0.863417f}, +{0.0704106f,-0.942853f,0.325686f},{0.956408f,-0.228265f,-0.18215f},{0.986183f,-0.0936427f,-0.136652f}, +{0.995545f,0.0406436f,-0.085079f},{-0.839642f,0.486502f,-0.24149f},{0.769548f,-0.526779f,-0.36097f}, +{-0.910351f,-0.333461f,-0.245081f},{-0.757463f,-0.364873f,-0.541404f},{-0.744719f,0.390985f,-0.540855f}, +{-0.863937f,-0.467813f,-0.186452f},{-0.743624f,0.127538f,-0.656321f},{-0.138916f,-0.41781f,-0.897851f}, +{0.95161f,-0.305239f,-0.035608f},{-0.0063863f,-0.628727f,-0.7776f},{-0.952006f,0.144361f,-0.269899f}, +{0.651594f,-0.183612f,-0.736011f},{0.674141f,-0.29853f,-0.675584f},{0.741378f,-0.230745f,-0.630171f}, +{-0.15322f,0.899928f,-0.408231f},{-0.207807f,0.846397f,-0.490335f},{-0.0451371f,0.827896f,-0.559063f}, +{-0.899822f,-0.311671f,-0.305256f},{-0.786522f,0.47361f,0.396329f},{-0.850935f,0.406667f,0.332463f}, +{-0.885878f,0.335753f,0.32014f},{0.686391f,-0.683815f,-0.247517f},{-0.67231f,-0.656937f,-0.341221f}, +{0.0f,0.0f,1.0f},{-0.937959f,-0.346653f,-0.00801302f},{-0.98739f,0.133802f,0.0846035f}, +{-0.0273161f,-0.138734f,-0.989953f},{0.0374685f,-0.224627f,-0.973724f},{0.705075f,-0.356919f,-0.612763f}, +{0.73297f,-0.423725f,-0.532176f},{0.77507f,-0.397151f,-0.491464f},{0.835103f,-0.224489f,-0.502203f}, +{0.813602f,-0.520484f,-0.25913f},{0.622879f,-0.584838f,-0.519602f},{0.698437f,-0.544724f,-0.464178f}, +{0.742741f,-0.582086f,-0.330926f},{0.598783f,-0.742132f,-0.301162f},{-0.949238f,0.306132f,0.0723165f}, +{-0.87596f,0.150958f,0.458154f},{0.986936f,0.159859f,0.0200684f},{0.956088f,0.286087f,0.0636382f}, +{0.984588f,0.121724f,0.125577f},{0.728274f,-0.0628471f,-0.682398f},{0.665955f,-0.0317118f,-0.745318f}, +{0.728609f,-0.14214f,-0.670019f},{-0.980512f,-0.0184633f,-0.195589f},{0.653931f,-0.666453f,0.358071f}, +{0.795056f,-0.276985f,-0.539598f},{0.829892f,-0.0953654f,-0.549713f},{0.861028f,-0.224006f,-0.456565f}, +{0.714815f,-0.275027f,-0.642962f},{-0.99155f,0.0806621f,0.101594f},{-0.127067f,0.871035f,-0.474503f}, +{0.593073f,-0.762618f,-0.258221f},{0.741611f,-0.633575f,-0.220445f},{0.879746f,-0.306992f,-0.363048f}, +{0.964765f,-0.0275073f,0.261671f},{-0.493436f,0.858968f,0.136728f},{-0.485737f,0.873949f,-0.0165395f}, +{-0.348249f,0.195388f,-0.916813f},{-0.865865f,0.42357f,0.266207f},{-0.991741f,0.0369164f,0.122831f}, +{-0.95484f,0.230839f,0.187067f},{0.413983f,-0.524267f,-0.744151f},{0.429364f,-0.694634f,-0.577174f}, +{0.190692f,-0.890844f,-0.412351f},{0.930414f,0.288332f,0.226261f},{0.997636f,-0.0670532f,0.0150621f}, +{0.995219f,0.0057273f,0.0975008f},{0.876074f,0.45196f,0.168009f},{0.878814f,-0.41968f,-0.227055f}, +{-0.230254f,-0.803082f,0.549584f},{-0.452718f,-0.646761f,0.613796f},{0.685239f,0.594863f,-0.420221f}, +{0.712106f,0.108611f,-0.69362f},{0.80921f,0.100493f,-0.578862f},{0.69558f,-0.243057f,-0.676086f}, +{0.749341f,-0.229107f,-0.621287f},{-0.46364f,-0.34574f,-0.815783f},{-0.829148f,-0.522963f,0.197543f}, +{-0.780415f,-0.576732f,0.241522f},{-0.919049f,-0.349159f,0.182857f},{-0.918924f,0.166482f,-0.35758f}, +{-0.949718f,0.0941859f,-0.298605f},{-0.970658f,0.0626362f,-0.232164f},{0.826395f,0.189191f,-0.530357f}, +{0.909795f,0.130265f,-0.394086f},{0.909374f,-0.00413196f,-0.41596f},{0.895746f,0.0197669f,-0.444126f}, +{0.690116f,-0.378259f,-0.616977f},{-0.895177f,-0.362898f,0.258771f},{0.446098f,-0.890696f,0.0875017f}, +{0.391035f,-0.914116f,0.107161f},{0.459105f,-0.882593f,0.101258f},{-0.633135f,-0.51279f,0.579816f}, +{-0.649023f,-0.452888f,0.611279f},{-0.59273f,-0.407957f,0.694437f},{0.904743f,-0.242724f,0.350034f}, +{0.828868f,-0.426799f,-0.361691f},{0.792659f,-0.553534f,-0.255525f},{-0.943462f,-0.127474f,-0.30599f}, +{-0.912574f,-0.0911195f,-0.39863f},{0.74525f,0.196846f,-0.637067f},{0.789092f,0.275901f,-0.548828f}, +{0.0114303f,-0.999602f,-0.0257713f},{0.814482f,-0.153989f,-0.55938f},{0.883559f,-0.106128f,-0.456136f}, +{0.0991691f,-0.75282f,-0.650713f},{0.1192f,-0.891997f,-0.436042f},{0.0416995f,-0.990848f,-0.128381f}, +{-0.868636f,0.464477f,0.172431f},{0.648167f,0.0160177f,-0.761329f},{0.685066f,0.331524f,-0.648673f}, +{0.713803f,-0.585761f,-0.383887f},{0.548529f,-0.764757f,-0.338028f},{-0.941654f,-0.0652673f,-0.330193f}, +{-0.947257f,-0.0320283f,-0.318871f},{-0.736538f,-0.154852f,-0.658432f},{0.841077f,-0.0220143f,-0.540467f}, +{0.839122f,0.304265f,-0.450884f},{0.671902f,-0.415255f,-0.613279f},{0.743045f,-0.290646f,-0.602834f}, +{0.791078f,-0.213203f,-0.573359f},{-0.414459f,0.780265f,0.468414f},{-0.333447f,0.81949f,0.466099f}, +{-0.350316f,0.897013f,0.269529f},{-0.445173f,0.203844f,-0.871934f},{-0.554085f,0.282607f,-0.783022f}, +{-0.371315f,0.14061f,-0.917799f},{-0.525501f,-0.42254f,-0.73845f},{-0.193952f,-0.922888f,-0.332656f}, +{-0.644837f,0.638904f,0.419509f},{-0.577435f,0.74013f,0.344638f},{0.642653f,-0.725827f,-0.245299f}, +{-0.534687f,0.832182f,-0.146915f},{-0.451134f,0.798492f,-0.398608f},{0.0169719f,-0.999804f,-0.0101645f}, +{0.036643f,-0.999184f,-0.016996f},{-0.947189f,0.122828f,0.296219f},{-0.986756f,0.0836779f,0.138966f}, +{-0.716169f,0.350797f,-0.60336f},{-0.200614f,-0.294627f,-0.934318f},{-0.321563f,-0.17239f,-0.931063f}, +{-0.956011f,0.291732f,-0.0305733f},{-0.684357f,0.683399f,-0.254208f},{0.60638f,-0.0863361f,-0.790474f}, +{0.625317f,0.0625792f,-0.777858f},{-0.9141f,0.225904f,-0.336733f},{0.74609f,-0.49078f,-0.449982f}, +{0.754326f,-0.426754f,-0.498872f},{0.721635f,-0.498468f,-0.480389f},{-0.696162f,-0.506157f,-0.509081f}, +{-0.677645f,-0.550992f,-0.487038f},{-0.924725f,0.272964f,-0.265283f},{0.747567f,-0.162517f,-0.643997f}, +{0.733726f,-0.26049f,-0.627528f},{-0.841458f,-0.3679f,0.395725f},{-0.780482f,-0.625175f,0.00217309f}, +{-0.467462f,0.83911f,-0.278161f},{-0.768839f,0.627927f,-0.120804f},{0.100807f,-0.994218f,-0.0369808f}, +{0.0348387f,-0.995477f,-0.0883868f},{-0.391848f,0.873182f,-0.28984f},{0.032085f,0.683438f,-0.729303f}, +{0.0152515f,0.645162f,-0.763893f},{-0.0140963f,0.692749f,-0.721041f},{0.798704f,-0.184977f,-0.572586f}, +{-0.523094f,0.852059f,-0.0191815f},{-0.954618f,-0.0164208f,-0.29738f},{-0.975711f,0.0897672f,-0.199826f}, +{-0.965971f,0.18126f,-0.184511f},{-0.950108f,0.0759588f,-0.30253f},{0.992414f,0.121164f,0.0208189f}, +{0.611404f,0.221804f,-0.759598f},{0.990116f,-0.0971424f,-0.101157f},{-0.7541f,-0.0822748f,-0.651585f}, +{-0.894907f,-0.409213f,-0.178004f},{-0.977252f,-0.183154f,-0.106927f},{-0.137259f,0.411548f,-0.900993f}, +{-0.0444384f,0.517873f,-0.854303f},{-0.230663f,0.547608f,-0.804313f},{0.980831f,0.19448f,0.0121421f}, +{-0.147885f,0.685989f,-0.712425f},{-0.107152f,0.598248f,-0.794114f},{-0.638269f,0.716773f,-0.280801f}, +{-0.851021f,0.359455f,0.382826f},{-0.82939f,0.350349f,0.435165f},{-0.194385f,-0.436604f,-0.878403f}, +{-0.704735f,-0.236526f,-0.668882f},{-0.894576f,0.111168f,0.43287f},{0.723662f,-0.0895417f,-0.684321f}, +{-0.26078f,0.930097f,-0.258678f},{-0.666087f,0.17054f,-0.726116f},{-0.589705f,-0.152056f,-0.793175f}, +{0.0588107f,0.66234f,-0.746892f},{0.0464484f,0.641371f,-0.765824f},{0.732878f,0.108509f,-0.671651f}, +{0.769934f,0.058901f,-0.6354f},{0.704258f,0.155158f,-0.692782f},{-0.905845f,0.0736368f,-0.417159f}, +{-0.886491f,-0.0258386f,-0.462024f},{-0.850694f,0.0210112f,-0.525241f},{0.767409f,-0.243228f,-0.593231f}, +{0.827619f,-0.193028f,-0.527055f},{0.779302f,-0.23095f,-0.582538f},{-0.884043f,-0.4635f,-0.0602925f}, +{-0.959193f,-0.26739f,-0.0919339f},{-0.85836f,-0.41451f,0.302322f},{0.765939f,-0.614183f,-0.190045f}, +{0.755147f,-0.00353785f,-0.655546f},{0.768294f,-0.249242f,-0.589579f},{0.76295f,0.0228429f,-0.646054f}, +{0.772987f,-0.200477f,-0.601914f},{0.839712f,0.218607f,-0.497086f},{0.000715683f,-0.980848f,0.194775f}, +{-0.635974f,-0.730907f,0.247612f},{-0.639593f,-0.35236f,-0.683201f},{-0.684919f,-0.396537f,-0.611264f}, +{-0.618439f,-0.339162f,-0.708874f},{0.127168f,-0.681979f,-0.720231f},{0.0782779f,-0.738573f,-0.669614f}, +{0.0113648f,-0.991394f,-0.130418f},{0.289444f,0.140849f,-0.946775f},{0.335955f,0.19986f,-0.92043f}, +{0.193556f,0.347962f,-0.91731f},{-0.974989f,0.117221f,-0.188825f},{0.219525f,0.217545f,-0.951043f}, +{-0.897894f,-0.341032f,0.27836f},{-0.897788f,-0.411493f,0.157002f},{-0.839134f,-0.369163f,0.399466f}, +{-0.895872f,-0.175299f,0.408268f},{0.843528f,-0.381703f,-0.37784f},{-0.832408f,0.491697f,-0.2556f}, +{0.518818f,0.0828733f,-0.850858f},{0.506607f,0.0628409f,-0.859884f},{0.54957f,0.240652f,-0.800037f}, +{-0.638102f,0.465486f,-0.61331f},{-0.513594f,0.704289f,-0.4901f},{-0.472045f,0.46126f,-0.751274f}, +{-0.952576f,-0.11264f,0.282687f},{0.99936f,0.00995633f,0.0343486f},{0.963471f,0.196356f,0.182123f}, +{0.984349f,0.0784931f,0.157783f},{0.279703f,0.283064f,-0.91741f},{0.181413f,0.312065f,-0.93258f}, +{0.236822f,0.427557f,-0.872416f},{-0.105609f,0.685177f,-0.72068f},{-0.976885f,-0.0286416f,0.211839f}, +{0.725157f,-0.00896551f,-0.688525f},{0.724382f,0.0375398f,-0.688376f},{0.766879f,-0.221373f,-0.602404f}, +{0.747531f,-0.347926f,-0.565814f},{0.105336f,-0.993702f,-0.0382186f},{0.399919f,-0.874604f,-0.274102f}, +{-0.642518f,0.276432f,-0.714673f},{-0.671372f,0.263996f,-0.692507f},{0.675291f,-0.602507f,-0.425403f}, +{0.218857f,-0.971126f,0.0949512f},{0.628034f,-0.747951f,0.21481f},{0.46298f,-0.856397f,0.228545f}, +{0.808891f,-0.569684f,-0.14545f},{0.653049f,-0.392291f,-0.647792f},{-0.0164474f,-0.999166f,0.0373811f}, +{-0.00770074f,-0.99916f,0.0402574f},{0.000142267f,-0.9996f,0.0282806f},{-0.141438f,0.915478f,-0.37669f}, +{-0.0262741f,0.8654f,-0.500392f},{-0.108226f,0.86563f,-0.488847f},{-0.189009f,0.743456f,-0.641521f}, +{-0.398889f,0.836374f,-0.375986f},{-0.934459f,0.0509862f,-0.352402f},{0.793651f,-0.04301f,-0.606851f}, +{0.713646f,0.148586f,-0.684567f},{-0.789885f,-0.580768f,0.196952f},{-0.807347f,-0.562697f,0.177657f}, +{-0.802356f,-0.546869f,0.239081f},{-0.118066f,-0.971948f,0.203415f},{-0.427385f,0.756545f,0.494956f}, +{0.73633f,0.308414f,-0.602245f},{0.859609f,0.0481623f,-0.508678f},{0.0357142f,-0.425029f,-0.904475f}, +{-0.216403f,-0.407845f,-0.887036f},{0.591956f,-0.749826f,-0.29555f},{0.579828f,-0.624834f,-0.52286f}, +{0.745975f,-0.250335f,-0.617133f},{0.770701f,-0.326509f,-0.547185f},{-0.889108f,-0.0981269f,-0.447054f}, +{-0.454529f,-0.0502772f,-0.889312f},{-0.637814f,-0.765383f,-0.0859141f},{-0.790282f,-0.609604f,0.0619454f}, +{0.786256f,0.100661f,-0.609646f},{0.970064f,0.194078f,0.145976f},{0.945551f,0.300532f,0.124952f}, +{-0.963703f,0.07142f,0.257248f},{0.0192169f,-0.978854f,-0.203654f},{0.893996f,-0.0181557f,-0.447706f}, +{-0.96542f,0.108981f,0.236827f},{-0.696219f,-0.631337f,-0.341603f},{-0.612492f,-0.74952f,-0.251142f}, +{-0.29042f,-0.876351f,-0.384273f},{0.639278f,-0.729907f,0.241988f},{0.69726f,-0.654375f,0.29261f}, +{-0.978716f,0.13926f,0.150735f},{0.000183522f,-0.998022f,0.0628701f},{-0.921737f,0.0503909f,-0.384528f}, +{-0.800558f,-0.493875f,-0.339402f},{-0.497304f,0.332329f,-0.801403f},{0.615217f,-0.432449f,0.659163f}, +{0.797934f,-0.505531f,-0.328238f},{0.865963f,-0.084911f,-0.492847f},{-0.305809f,-0.410663f,-0.858974f}, +{0.248637f,-0.581692f,-0.774476f},{-0.0103987f,-0.99808f,0.0610528f},{-0.951984f,0.221019f,0.21184f}, +{0.977876f,-0.181766f,-0.103535f},{-0.921649f,0.36585f,0.129297f},{0.699153f,-0.675216f,-0.23509f}, +{0.755519f,-0.604696f,-0.25206f},{0.638633f,-0.518986f,-0.568157f},{0.71167f,-0.542609f,-0.446207f}, +{0.484392f,-0.401776f,-0.777136f},{0.703104f,-0.546784f,-0.454612f},{-0.858558f,0.0931788f,-0.504178f}, +{-0.921669f,0.165028f,-0.351129f},{-0.806021f,-0.148181f,-0.573037f},{-0.710689f,-0.664555f,-0.230841f}, +{-0.840258f,-0.120608f,0.528602f},{0.887732f,-0.18165f,-0.423007f},{0.904292f,-0.119169f,-0.409946f}, +{0.867545f,-0.118966f,-0.482921f},{0.99829f,0.0581998f,-0.00551678f},{-0.914838f,0.361162f,0.180649f}, +{-0.912489f,0.395871f,0.103196f},{0.531903f,0.275915f,-0.800593f},{0.515947f,0.267172f,-0.813891f}, +{0.519825f,0.372712f,-0.768679f},{0.762899f,-0.17899f,-0.621246f},{0.702701f,-0.150915f,-0.695296f}, +{-0.294184f,0.897705f,-0.327998f},{0.147592f,0.958411f,-0.244265f},{0.177148f,0.966717f,-0.1846f}, +{0.176799f,0.971614f,-0.15719f},{-0.465063f,0.0616191f,0.883131f},{-0.418584f,-0.261446f,0.869731f}, +{0.583074f,-0.793946f,0.172264f},{0.731105f,-0.0128284f,-0.682144f},{0.676149f,-0.462984f,-0.573121f}, +{0.723236f,-0.330051f,-0.606626f},{0.704461f,0.394819f,-0.58979f},{0.843936f,-0.107503f,-0.525562f}, +{-0.96141f,-0.00372427f,-0.275096f},{-0.894378f,0.323282f,-0.309154f},{0.956972f,0.136056f,0.256306f}, +{-0.933431f,-0.350667f,-0.0757507f},{-0.130992f,0.607162f,-0.783706f},{-0.154968f,0.416175f,-0.895982f}, +{-0.260322f,0.567362f,-0.781238f},{-0.0362481f,0.607833f,-0.793237f},{-0.141952f,0.705861f,-0.693981f}, +{-0.121685f,0.645941f,-0.753627f},{-0.297167f,0.482335f,-0.824042f},{0.740034f,0.428893f,-0.518074f}, +{0.657119f,0.4288f,-0.619939f},{0.507662f,-0.441258f,-0.73998f},{-0.898184f,-0.155866f,-0.411062f}, +{-0.731846f,-0.184488f,-0.656023f},{-0.585754f,-0.776209f,-0.23322f},{-0.977273f,0.0661205f,-0.201412f}, +{-0.869666f,0.468964f,0.154123f},{-0.874285f,0.47548f,0.0976949f},{-0.765902f,0.148325f,0.625615f}, +{-0.899666f,0.406081f,0.160309f},{-0.935898f,0.330841f,0.120995f},{-0.974013f,0.125771f,0.188361f}, +{-0.976974f,0.0530194f,0.206668f},{0.923756f,0.129146f,0.360549f},{0.0350245f,-0.887122f,0.460203f}, +{0.67463f,-0.30715f,-0.671218f},{-0.504845f,-0.486101f,0.713328f},{-0.516171f,-0.350276f,0.781584f}, +{0.31792f,-0.3715f,-0.872305f},{0.511809f,-0.490299f,-0.705449f},{-0.978509f,0.204314f,-0.0278446f}, +{0.147245f,0.551976f,-0.820756f},{0.130809f,0.437729f,-0.88954f},{0.00867079f,0.703226f,-0.710913f}, +{0.425153f,-0.903936f,0.0463115f},{0.403859f,-0.905871f,0.127655f},{-0.770367f,-0.617828f,-0.157556f}, +{0.714366f,0.00755797f,-0.699732f},{0.887058f,-0.253175f,-0.386045f},{0.904446f,-0.229688f,-0.359473f}, +{0.889749f,-0.237271f,-0.389934f},{-0.0191102f,-0.999817f,-0.00115212f},{0.0066128f,-0.999505f,0.0307467f}, +{0.00427601f,-0.998407f,-0.0562644f},{0.709271f,-0.160033f,-0.686531f},{0.74534f,-0.288142f,-0.601201f}, +{0.608205f,-0.355153f,-0.709896f},{0.125865f,0.782562f,-0.609717f},{-0.0739411f,0.799269f,-0.596407f}, +{0.0508085f,0.72803f,-0.68366f},{-0.814112f,-0.561646f,-0.147568f},{0.972331f,0.159512f,0.170672f}, +{0.861139f,-0.229934f,-0.453399f},{0.894211f,-0.209492f,-0.395601f},{-0.220335f,-0.862894f,0.454827f}, +{-0.215799f,-0.863267f,0.456291f},{0.819689f,0.0614222f,-0.569506f},{0.857209f,0.0364918f,-0.513673f}, +{-0.95851f,-0.25396f,0.129474f},{0.847391f,-0.263974f,-0.460701f},{0.410327f,-0.908244f,0.0820033f}, +{0.250237f,-0.965597f,0.0707417f},{0.529549f,-0.264119f,-0.806113f},{0.541477f,0.764778f,-0.349167f}, +{-0.0183646f,0.673017f,-0.739399f},{-0.00922519f,0.686325f,-0.727236f},{-0.969929f,-0.182964f,0.160506f}, +{-0.0327101f,-0.996404f,-0.0781646f},{0.542598f,0.111129f,-0.832609f},{-0.054401f,0.718347f,-0.693555f}, +{0.960249f,-0.258809f,-0.104594f},{0.868777f,-0.0882508f,-0.487276f},{0.0262056f,-0.999314f,-0.0261689f}, +{-0.742036f,-0.634751f,0.215576f},{-0.790094f,-0.603961f,-0.104797f},{0.684195f,0.0425916f,-0.728054f}, +{0.909851f,-0.135259f,-0.39227f},{0.846745f,0.00228132f,-0.531994f},{-0.982988f,-0.164733f,0.0812222f}, +{0.995198f,-0.0892049f,-0.0402945f},{0.986315f,0.147268f,0.0741227f},{0.566721f,0.31205f,-0.76253f}, +{0.401851f,0.226639f,-0.887215f},{0.0372775f,-0.264135f,-0.963765f},{-0.976716f,0.0122543f,0.214188f}, +{-0.313664f,0.917903f,-0.243042f},{-0.352028f,0.889474f,-0.291398f},{0.108126f,-0.83625f,-0.537583f}, +{0.171862f,-0.95061f,-0.258466f},{0.86963f,-0.270204f,-0.4132f},{-0.903531f,0.323979f,0.28048f}, +{-0.683437f,0.620562f,0.384468f},{-0.691721f,-0.7163f,-0.0918445f},{-0.736096f,0.554191f,0.388633f}, +{-0.645063f,0.68258f,0.34348f},{-0.614513f,-0.77154f,0.164621f},{0.669319f,-0.537608f,-0.512825f}, +{0.327855f,-0.918252f,0.222092f},{0.298594f,-0.934062f,0.19588f},{0.354796f,-0.918097f,0.176686f}, +{-0.506672f,0.834157f,0.217865f},{0.0345878f,-0.999201f,-0.020016f},{0.931099f,0.276016f,-0.238473f}, +{-0.020536f,-0.30336f,-0.952655f},{-0.469175f,-0.414153f,-0.779969f},{0.118301f,-0.895303f,-0.429462f}, +{0.099007f,-0.97542f,-0.196861f},{0.0272237f,-0.999335f,-0.0242418f},{-0.674057f,0.449826f,-0.585921f}, +{0.703665f,0.384147f,-0.597735f},{0.33071f,0.351781f,-0.875718f},{0.0978065f,0.508281f,-0.855619f}, +{0.546689f,-0.0132231f,-0.837231f},{0.896842f,-0.151815f,-0.415484f},{-0.960075f,0.245652f,0.133831f}, +{-0.98563f,0.084896f,0.146033f},{-0.899805f,0.257965f,0.351859f},{0.827633f,-0.396764f,-0.39699f}, +{-0.883847f,0.292303f,0.365202f},{-0.965708f,0.175981f,0.190889f},{-0.272344f,-0.945067f,-0.180768f}, +{0.0209144f,-0.999074f,0.0376027f},{0.032975f,-0.998556f,-0.0424153f},{0.0318291f,-0.998875f,-0.0351631f}, +{0.0352428f,-0.998819f,-0.0334404f},{0.0388307f,-0.998288f,0.0437481f},{-0.647332f,-0.523114f,-0.554359f}, +{-0.355664f,-0.613777f,-0.704826f},{0.959933f,-0.275879f,-0.0491777f},{0.992104f,-0.125231f,0.00679531f}, +{-0.0944883f,0.823707f,0.559088f},{-0.999729f,-0.0207259f,-0.0106073f},{0.563333f,0.0340497f,-0.825528f}, +{0.998869f,0.0105482f,0.0463714f},{-0.470932f,0.711152f,0.522003f},{0.278589f,0.329839f,-0.901994f}, +{-0.886327f,0.432631f,-0.165089f},{-0.926808f,0.375345f,-0.0119485f},{0.204039f,-0.372273f,-0.905418f}, +{-0.992881f,0.0451062f,0.110243f},{-0.59668f,0.742712f,0.303894f},{-0.791167f,0.0261632f,-0.61104f}, +{0.438938f,-0.894537f,-0.0844837f},{-0.00869637f,-0.999954f,0.00417063f},{-0.0213252f,-0.999674f,0.0140172f}, +{-0.153982f,0.624937f,-0.765339f},{0.794822f,-0.532167f,-0.291645f},{0.811671f,-0.521067f,-0.263968f}, +{-0.517259f,-0.574715f,-0.63415f},{-0.93562f,0.340752f,-0.0922097f},{0.236175f,-0.960352f,-0.148136f}, +{0.553704f,-0.827051f,-0.096947f},{0.870867f,0.381557f,0.309847f},{0.853989f,0.341659f,0.392392f}, +{-0.154021f,-0.650535f,-0.743694f},{0.0262376f,-0.999048f,0.0348535f},{0.0225118f,-0.999678f,0.0117443f}, +{-0.124984f,-0.435542f,-0.891449f},{-0.37736f,-0.556838f,-0.739954f},{0.872701f,0.335303f,0.354917f}, +{0.853307f,0.326584f,0.40646f},{0.93813f,0.199247f,0.283218f},{0.0217241f,-0.999386f,-0.0274869f}, +{-0.597863f,-0.38968f,-0.700507f},{0.959073f,0.10872f,0.261455f},{-0.18484f,-0.257829f,-0.948345f}, +{0.154603f,-0.413225f,-0.897409f},{0.717788f,-0.687313f,0.111275f},{0.847382f,-0.520852f,0.103229f}, +{0.924113f,0.287993f,0.251147f},{0.911501f,0.262502f,0.316637f},{0.76148f,-0.482196f,-0.433169f}, +{0.940386f,0.129455f,0.314508f},{0.863147f,0.448876f,0.231275f},{0.761826f,-0.641831f,0.0876031f}, +{0.906001f,0.324256f,0.272067f},{0.0985965f,-0.902022f,-0.420279f},{-0.88257f,0.38391f,0.271447f}, +{-0.968928f,0.130172f,0.21032f},{-0.102488f,-0.994179f,-0.0332447f},{-0.0442014f,-0.992883f,-0.110588f}, +{0.962835f,0.199899f,0.181627f},{0.221024f,0.37711f,-0.899409f},{0.217884f,0.261235f,-0.940363f}, +{0.204653f,0.371797f,-0.905474f},{-0.385283f,0.722821f,0.573662f},{0.800531f,-0.19496f,-0.566693f}, +{0.975873f,0.0781187f,0.203887f},{0.976396f,0.098517f,0.192213f},{0.964255f,0.132192f,0.229645f}, +{0.95341f,0.0958946f,0.286032f},{0.946655f,0.14687f,0.286835f},{0.940817f,0.252628f,0.225928f}, +{0.905792f,0.35846f,0.225938f},{0.90519f,0.368597f,0.211583f},{0.916708f,0.349413f,0.193796f}, +{0.904462f,0.381361f,0.191082f},{0.824432f,0.565116f,0.0309246f},{0.82786f,0.560907f,-0.00562512f}, +{-0.96563f,-0.0745097f,0.249011f},{-0.351993f,-0.922569f,0.15801f},{-0.05336f,-0.995682f,0.075956f}, +{-0.360501f,-0.839956f,0.405602f},{0.864828f,0.398074f,0.305956f},{0.795907f,0.528246f,0.295782f}, +{0.956112f,0.21274f,0.201471f},{0.974732f,0.0578164f,0.215767f},{0.962779f,0.0784324f,0.258661f}, +{0.234707f,-0.73487f,0.636301f},{-0.210907f,-0.231875f,-0.949606f},{0.971296f,0.156885f,0.178808f}, +{0.951489f,0.120919f,0.282925f},{0.0275537f,-0.999051f,-0.0337455f},{-0.318759f,-0.932821f,-0.168041f}, +{0.816302f,0.491849f,0.302878f},{0.778816f,0.567945f,0.266241f},{0.617103f,-0.751326f,0.233866f}, +{0.986863f,-0.0263569f,0.159393f},{0.914441f,0.385846f,0.122154f},{0.939377f,0.3255f,0.107795f}, +{-0.269202f,-0.242719f,-0.931997f},{-0.62682f,0.758358f,0.178856f},{-0.842996f,0.537838f,-0.00937066f}, +{-0.624626f,0.757251f,-0.190825f},{-0.0746514f,-0.561558f,-0.824063f},{0.974361f,0.14028f,0.175902f}, +{0.721718f,0.541276f,-0.431444f},{0.691845f,-0.678714f,0.246368f},{0.940354f,-0.258607f,0.221034f}, +{0.961877f,-0.119882f,0.245806f},{0.63723f,-0.429472f,-0.639915f},{0.989181f,0.0945617f,0.11216f}, +{0.990752f,0.0975521f,0.094307f},{0.985711f,0.123981f,0.114028f},{0.977333f,0.166331f,0.130976f}, +{0.956945f,0.25428f,0.139992f},{0.923567f,0.353306f,0.148994f},{0.91272f,0.377482f,0.156363f}, +{0.916948f,0.362123f,0.167552f},{0.911395f,0.387113f,0.139649f},{0.835581f,0.53732f,0.114417f}, +{0.968796f,0.231741f,0.0879191f},{-0.704802f,0.0615895f,0.706725f},{-0.765553f,-0.532367f,0.361267f}, +{-0.755914f,-0.172051f,0.631659f},{0.863069f,0.405931f,0.300552f},{0.838471f,0.491299f,0.235781f}, +{0.902746f,0.311662f,0.296507f},{0.972589f,0.0739923f,0.220444f},{0.61259f,-0.265774f,-0.744377f}, +{0.968436f,0.216794f,0.123015f},{0.976937f,-0.152909f,0.14904f},{-0.171894f,0.773899f,-0.609535f}, +{-0.279005f,0.84322f,-0.459496f},{0.0318361f,-0.999257f,-0.0217057f},{-0.686432f,-0.418899f,0.59442f}, +{0.940718f,-0.314328f,0.127464f},{0.127985f,-0.970691f,-0.203416f},{-0.140049f,0.795548f,0.589483f}, +{0.890566f,0.394901f,0.225712f},{-0.17875f,0.0676498f,-0.981566f},{-0.130389f,0.136394f,-0.982036f}, +{-0.693075f,-0.719421f,-0.045614f},{-0.854093f,-0.520116f,0.00228063f},{-0.697985f,-0.682744f,0.216051f}, +{0.958009f,0.268102f,0.101683f},{0.910047f,0.341379f,0.235107f},{-0.130869f,0.189174f,-0.973184f}, +{-0.136388f,0.204777f,-0.96926f},{-0.360244f,0.91344f,-0.189348f},{0.00927586f,0.997961f,-0.0631495f}, +{-0.269327f,0.956986f,-0.107895f},{0.0065507f,0.999964f,-0.00535563f},{-0.0516087f,0.998536f,0.0162233f}, +{-0.00549771f,0.998424f,0.0558435f},{-0.966082f,-0.223912f,0.128644f},{-0.959726f,-0.273362f,0.0647994f}, +{0.195186f,0.974535f,0.110378f},{0.102488f,0.979166f,0.175299f},{0.647374f,0.671496f,0.360554f}, +{-0.3164f,0.947491f,0.0463864f},{0.0622768f,0.854897f,0.515046f},{0.0187868f,0.827973f,0.560453f}, +{0.793441f,0.573205f,0.204667f},{0.570489f,0.666316f,0.480172f},{0.0149417f,0.788806f,0.614461f}, +{0.551521f,0.679451f,0.483912f},{-0.33077f,0.874133f,0.355644f},{-0.381363f,0.807787f,0.449491f}, +{-0.0394864f,0.843368f,0.535884f},{-0.875505f,-0.465724f,0.128814f},{-0.186669f,0.821205f,0.539237f}, +{-0.40397f,0.803553f,0.437162f},{0.0275706f,0.940562f,0.3385f},{0.0127497f,0.923202f,0.384103f}, +{0.0978516f,0.88668f,0.451912f},{-0.978017f,0.136329f,0.157789f},{0.63961f,0.752729f,0.155875f}, +{0.574715f,0.728057f,0.373678f},{0.296871f,0.73371f,0.611177f},{0.583868f,0.792562f,-0.175909f}, +{0.613508f,0.789373f,-0.0223071f},{0.30635f,0.812628f,0.495768f},{0.7584f,0.598623f,0.257838f}, +{0.751662f,0.579045f,0.315772f},{0.626681f,0.697524f,0.347462f},{0.327733f,0.893655f,0.306548f}, +{0.0817467f,0.949563f,0.302734f},{0.609838f,0.702151f,0.367535f},{-0.141646f,0.863148f,0.484677f}, +{-0.248361f,0.827147f,0.504127f},{0.251704f,0.958776f,0.131885f},{-0.00860057f,0.843532f,0.53701f}, +{0.610942f,0.685026f,0.39685f},{0.563461f,0.706036f,0.428981f},{0.290591f,0.953423f,-0.0808732f}, +{-0.0416996f,0.942389f,0.331909f},{-0.242277f,-0.954806f,0.172186f},{-0.508478f,-0.833053f,0.217884f}, +{-0.706606f,-0.680659f,-0.193422f},{-0.780268f,-0.571677f,-0.253706f},{0.203519f,0.955669f,-0.212783f}, +{-0.409056f,0.847362f,0.338602f},{-0.10077f,0.935414f,0.338889f},{-0.143977f,0.951576f,0.271611f}, +{-0.167925f,0.867559f,0.468127f},{0.117004f,0.992992f,-0.0166474f},{-0.0198098f,0.997063f,0.0739749f}, +{-0.0233485f,0.980743f,0.193904f},{-0.0633045f,0.881613f,0.467708f},{-0.0576471f,0.742484f,0.667378f}, +{-0.38597f,0.7806f,0.491621f},{-0.424397f,0.710223f,0.561668f},{-0.329934f,0.7797f,0.532176f}, +{-0.66792f,0.709634f,0.224284f},{-0.451594f,0.832424f,0.321144f},{-0.0026761f,0.997846f,-0.0655392f}, +{0.0393049f,0.947095f,0.318538f},{-0.0874497f,0.213337f,-0.973057f},{-0.14254f,0.226346f,-0.963561f}, +{-0.079948f,0.963489f,0.255531f},{0.00520031f,0.979625f,-0.200767f},{0.160151f,0.97548f,0.150967f}, +{-0.30432f,0.934853f,-0.182866f},{-0.382215f,0.88348f,-0.270877f},{-0.0554854f,0.991601f,-0.116832f}, +{0.238752f,0.970688f,0.0276203f},{0.658975f,0.641087f,0.393396f},{-0.166042f,0.978496f,0.122378f}, +{0.0307127f,0.987931f,0.151817f},{-0.343655f,0.938408f,-0.0359483f},{0.334978f,0.937134f,-0.0978196f}, +{0.843998f,0.536343f,-0.00166254f},{0.0366952f,0.998946f,-0.0275677f},{-0.406013f,0.896936f,0.175099f}, +{-0.523428f,0.7564f,-0.392279f},{-0.719595f,0.569109f,-0.397866f},{0.0774109f,0.330299f,-0.940697f}, +{-0.926674f,0.36204f,0.101006f},{0.573185f,0.791566f,0.211852f},{0.759727f,0.648451f,-0.0482235f}, +{0.912176f,0.39349f,0.114457f},{-0.973178f,-0.227499f,0.0341798f},{0.27077f,0.673918f,0.6874f}, +{0.737332f,0.667635f,-0.102979f},{0.79274f,0.608946f,0.0273588f},{0.606984f,0.722816f,0.330314f}, +{0.898969f,0.435381f,0.0479348f},{0.596787f,0.704856f,-0.383437f},{-0.940796f,-0.0582889f,0.333923f}, +{0.00474336f,-0.999893f,-0.0138285f},{-0.00631413f,-0.999977f,0.00228954f},{0.0132945f,-0.999842f,0.0117666f}, +{0.725395f,0.687263f,0.0383686f},{-0.623881f,-0.449673f,-0.639192f},{-0.710202f,-0.337333f,-0.617915f}, +{-0.564536f,-0.513949f,-0.645876f},{0.205114f,0.939815f,-0.273268f},{0.306274f,0.933228f,-0.187833f}, +{0.293334f,0.918292f,-0.265886f},{-0.730003f,0.627909f,0.269862f},{0.0412235f,0.990147f,-0.133823f}, +{-0.0506354f,0.990554f,-0.127428f},{-0.797838f,0.144275f,0.585354f},{-0.814328f,-0.00914634f,0.580333f}, +{-0.875474f,-0.0288966f,0.482401f},{-0.87953f,0.267082f,0.393819f},{0.193281f,0.980464f,-0.0365016f}, +{0.241335f,0.970224f,0.0205498f},{0.2625f,0.963919f,-0.0442113f},{-0.0568875f,0.994164f,-0.0916615f}, +{-0.00599234f,0.955773f,0.294044f},{-0.632698f,0.175517f,-0.754246f},{-0.709847f,0.319233f,-0.627859f}, +{-0.644735f,0.295137f,-0.705132f},{0.0790042f,0.929158f,0.361142f},{0.76758f,0.541513f,0.342907f}, +{0.804976f,0.457231f,0.378091f},{0.147986f,0.988531f,0.0301111f},{0.696325f,0.673718f,0.247458f}, +{0.711674f,0.542561f,0.446261f},{0.242014f,0.964107f,-0.109214f},{0.161173f,0.924593f,0.345182f}, +{-0.999615f,-0.0214551f,0.0176068f},{-0.892926f,0.154387f,0.422904f},{-0.837955f,-0.521028f,0.162363f}, +{-0.839437f,-0.532918f,0.106511f},{0.0638424f,0.997652f,-0.0248022f},{-0.715259f,-0.673496f,0.186566f}, +{-0.61264f,-0.789564f,0.0355033f},{0.31859f,0.837066f,0.444771f},{0.316983f,0.920076f,-0.230177f}, +{0.428733f,0.866239f,-0.25655f},{0.271337f,0.960436f,-0.0627656f},{0.324601f,0.944442f,0.051613f}, +{-0.581084f,0.522689f,0.623809f},{-0.440341f,0.699445f,0.562918f},{-0.287863f,-0.196649f,-0.937264f}, +{-0.330953f,-0.324711f,-0.886021f},{0.339123f,0.808747f,0.480546f},{-0.021721f,0.882003f,0.470743f}, +{0.127878f,0.991291f,0.0314696f},{0.098346f,0.992921f,0.0665993f},{0.0284123f,-0.997479f,0.0650288f}, +{0.020356f,-0.99678f,0.0775616f},{0.0240698f,-0.997295f,0.069456f},{0.278418f,0.949947f,-0.141718f}, +{0.604354f,0.51753f,0.605738f},{0.185444f,0.978943f,-0.0853253f},{-0.902527f,-0.429563f,0.0303291f}, +{0.180423f,0.975577f,0.125284f},{0.291481f,0.956568f,-0.00407742f},{0.349613f,0.890753f,-0.290396f}, +{0.356001f,0.892578f,-0.27671f},{-0.772158f,0.517877f,0.368206f},{-0.833386f,-0.170946f,0.525591f}, +{-0.467427f,0.879952f,-0.0848387f},{0.745154f,0.610309f,-0.268828f},{-0.860918f,-0.0426011f,-0.506957f}, +{-0.425821f,0.844817f,-0.323977f},{-0.138972f,0.83396f,0.534038f},{0.641568f,0.757832f,0.118662f}, +{0.82573f,0.549377f,0.127887f},{0.436349f,0.851679f,0.290243f},{0.149454f,0.967097f,0.205881f}, +{0.123489f,0.962764f,0.240489f},{0.0777148f,0.948839f,0.306049f},{0.259818f,0.964051f,0.0556885f}, +{0.671152f,0.613816f,0.415674f},{0.737405f,0.584125f,0.339162f},{0.0787638f,0.972075f,0.221056f}, +{0.0226107f,0.965058f,0.261059f},{-0.318905f,0.888959f,-0.328713f},{-0.387093f,0.91673f,0.0988183f}, +{0.338681f,0.924639f,0.174179f},{-0.0464892f,0.917725f,0.394486f},{0.34519f,0.784268f,0.515527f}, +{0.0981635f,0.845149f,0.525441f},{0.32706f,0.938825f,-0.107887f},{0.334149f,0.940787f,-0.057133f}, +{0.360584f,0.904615f,-0.227268f},{0.370049f,0.916384f,-0.152656f},{0.219202f,0.949682f,-0.223729f}, +{0.0417123f,0.962056f,0.269644f},{0.0445164f,0.999004f,0.0029002f},{0.0311419f,0.99584f,0.0856323f}, +{-0.0105715f,0.904381f,0.426594f},{0.250169f,0.889893f,0.381453f},{-0.479884f,0.734289f,0.480136f}, +{0.0616234f,0.91504f,0.398629f},{-0.873565f,-0.484911f,0.0417901f},{0.193391f,0.97755f,0.0836386f}, +{-0.24517f,-0.897212f,-0.367291f},{-0.738477f,0.556206f,-0.381164f},{0.00208972f,0.995448f,-0.0952887f}, +{0.023146f,0.996478f,-0.0805908f},{-0.200122f,0.828964f,0.522273f},{-0.223522f,0.953553f,0.201925f}, +{0.261343f,0.928881f,-0.262449f},{0.331113f,0.923764f,-0.192418f},{0.573723f,0.819049f,0.000855026f}, +{0.362715f,0.829104f,0.42547f},{-0.0573298f,0.844648f,0.532243f},{-0.0897245f,0.990344f,-0.105682f}, +{0.791768f,0.584989f,0.175761f},{0.788555f,0.524059f,0.321781f},{0.396688f,0.784354f,0.476893f}, +{-0.142441f,0.853827f,0.50069f},{-0.497251f,0.741148f,0.451045f},{-0.52758f,0.754131f,-0.391083f}, +{-0.74581f,0.423423f,-0.514276f},{-0.835334f,0.507605f,-0.211079f},{-0.760969f,0.639595f,0.108834f}, +{0.665519f,0.564337f,0.488476f},{0.184704f,0.965314f,0.184535f},{-0.377948f,0.905644f,-0.19226f}, +{-0.374352f,0.926577f,-0.0362743f},{0.481967f,0.867384f,0.123909f},{0.524646f,0.846426f,-0.0911632f}, +{-0.0773877f,0.88845f,0.452402f},{-0.697527f,-0.161082f,0.698218f},{-0.728014f,-0.219408f,0.649505f}, +{-0.806167f,-0.22881f,0.545657f},{0.859404f,0.505542f,0.0765056f},{0.18694f,0.936136f,0.297831f}, +{0.627902f,0.752182f,-0.199901f},{0.439551f,0.827994f,0.348167f},{-0.106603f,0.891453f,0.440395f}, +{-0.739824f,0.137059f,0.658692f},{-0.696005f,0.314806f,0.645348f},{0.777963f,0.596105f,0.198575f}, +{0.625854f,0.727341f,0.281572f},{0.586037f,0.75803f,0.286273f},{-0.273339f,0.876824f,0.395556f}, +{-0.117479f,0.873974f,0.471559f},{-0.141536f,0.827666f,0.543081f},{0.0223492f,-0.998109f,0.0572571f}, +{-0.000931591f,-0.998825f,0.0484584f},{-0.360291f,0.888095f,0.285442f},{-0.105675f,0.913747f,0.392299f}, +{-0.13649f,0.989925f,0.0376649f},{0.0384947f,0.98542f,0.165729f},{0.647598f,0.705639f,-0.287558f}, +{-0.629182f,0.60845f,-0.483651f},{-0.18035f,0.850769f,0.493626f},{-0.200136f,0.659248f,0.724802f}, +{-0.40814f,0.771931f,0.487385f},{-0.56766f,0.779026f,0.266235f},{-0.50392f,0.782727f,0.365244f}, +{0.059644f,0.998218f,-0.00191144f},{0.493305f,0.788252f,0.367843f},{0.233534f,0.915322f,0.328097f}, +{0.100081f,0.994977f,-0.001886f},{0.0540968f,0.9859f,0.158351f},{0.0288702f,0.997117f,-0.0701744f}, +{-0.579249f,0.78716f,-0.211777f},{0.474653f,0.813336f,0.336437f},{0.549723f,0.788931f,0.274576f}, +{-0.633886f,0.679356f,0.369682f},{-0.0300771f,0.878075f,0.477576f},{0.0456401f,0.99866f,-0.0244092f}, +{0.0727139f,0.973018f,0.21897f},{0.0735803f,0.671302f,-0.737522f},{0.0494458f,0.395419f,-0.917169f}, +{0.00888718f,0.859255f,-0.511471f},{-0.854026f,0.2938f,0.429328f},{0.267715f,0.935591f,0.230212f}, +{-0.0884375f,0.963257f,-0.253602f},{-0.27228f,0.959388f,-0.0737389f},{-0.55698f,0.773671f,-0.302004f}, +{-0.71943f,0.67545f,-0.161826f},{-0.653106f,0.624328f,-0.428564f},{-0.379852f,0.787478f,-0.485377f}, +{-0.0496136f,0.821031f,0.568724f},{-0.556395f,0.828516f,-0.0631306f},{-0.00650614f,0.939219f,0.343256f}, +{-0.0378066f,0.973412f,-0.225918f},{-0.554061f,0.40543f,-0.727078f},{-0.569182f,0.171273f,-0.804175f}, +{-0.709345f,0.399365f,-0.580807f},{0.312263f,0.8806f,-0.356421f},{0.670924f,0.693388f,0.262818f}, +{-0.537859f,0.0528585f,0.841376f},{0.383935f,0.853309f,0.352785f},{-0.437111f,0.89267f,0.109884f}, +{-0.318147f,0.945762f,0.0657079f},{0.817501f,0.574998f,0.0327173f},{0.804964f,0.593238f,-0.0100991f}, +{0.82956f,0.536471f,0.155014f},{0.807816f,0.543091f,0.229097f},{0.767553f,0.561873f,0.308482f}, +{0.734804f,0.509692f,0.447523f},{-0.184696f,0.932773f,-0.309551f},{-0.164745f,0.95509f,-0.246299f}, +{0.272873f,0.875473f,0.398857f},{-0.547438f,0.648874f,0.528464f},{-0.509731f,0.801615f,0.312391f}, +{-0.693304f,0.660768f,0.287602f},{0.830856f,0.553276f,-0.0596929f},{0.817452f,0.574321f,0.04391f}, +{0.82274f,0.56071f,0.0932945f},{0.799149f,0.580408f,0.156486f},{-0.187866f,0.976313f,0.107331f}, +{0.83228f,0.547521f,-0.0867808f},{0.814186f,0.578133f,-0.0535041f},{0.810153f,0.585831f,0.0213339f}, +{0.809173f,0.580772f,0.0891247f},{0.80695f,0.590596f,-0.00528696f},{0.801762f,0.595959f,0.044831f}, +{0.77998f,0.602313f,0.169853f},{0.744047f,0.592678f,0.308427f},{-0.966823f,0.0452095f,0.251413f}, +{-0.956155f,0.198707f,0.215134f},{0.0574333f,0.997765f,-0.0341582f},{0.806921f,0.589501f,-0.0369692f}, +{0.802278f,0.59668f,0.0179628f},{0.766982f,0.633314f,0.10321f},{0.650228f,0.622419f,0.435658f}, +{0.704453f,0.647844f,0.289904f},{0.628921f,0.597557f,0.497378f},{-0.743343f,0.229344f,-0.628365f}, +{-0.69546f,0.312394f,-0.647105f},{-0.660438f,0.266932f,-0.701832f},{0.806766f,0.581782f,-0.103241f}, +{0.80487f,0.593418f,-0.00630056f},{0.769639f,0.637487f,0.035579f},{0.734675f,0.659159f,0.160504f}, +{0.80405f,0.549513f,-0.227025f},{0.803626f,0.593911f,-0.0381408f},{0.769531f,0.638363f,-0.0177426f}, +{0.765295f,0.642865f,0.0323697f},{0.567755f,0.649438f,0.505849f},{0.381409f,-0.893429f,-0.237301f}, +{-0.00752063f,0.229679f,-0.973237f},{0.807026f,0.580752f,-0.10694f},{0.758431f,0.650052f,-0.047062f}, +{0.732079f,0.672795f,0.106805f},{0.704091f,0.702637f,0.102745f},{0.698117f,0.699929f,0.150773f}, +{0.663589f,0.679392f,0.313173f},{0.574373f,0.681896f,0.452896f},{-0.803084f,0.330145f,-0.496045f}, +{0.798028f,0.532226f,-0.282643f},{0.766386f,0.563802f,-0.307862f},{0.80442f,0.55547f,-0.210621f}, +{0.76591f,0.631894f,-0.11871f},{0.715646f,0.697201f,0.0419614f},{0.603596f,0.722225f,0.337732f}, +{0.520807f,0.654939f,0.547553f},{0.229172f,0.800804f,0.553347f},{0.0812578f,0.986317f,-0.143444f}, +{-0.9886f,0.111022f,-0.101703f},{0.741263f,0.671205f,0.00366621f},{0.704182f,0.709381f,0.0301024f}, +{0.639249f,0.747109f,0.182178f},{0.586867f,0.654313f,0.47693f},{-0.604248f,0.685136f,0.406783f}, +{0.749005f,0.617153f,-0.241066f},{0.692909f,0.716962f,-0.0764364f},{0.694198f,0.719117f,-0.0309967f}, +{0.667752f,0.740189f,0.0789108f},{0.709131f,0.696508f,-0.109592f},{0.666081f,0.734678f,-0.128782f}, +{0.67466f,0.736436f,0.0499464f},{0.637302f,0.765583f,0.0879164f},{0.605727f,0.778945f,0.162297f}, +{0.584384f,0.769989f,0.25615f},{0.569427f,0.738575f,0.360916f},{0.701706f,0.63332f,-0.326365f}, +{0.672913f,0.705054f,-0.2238f},{0.646507f,0.761239f,0.0504462f},{0.540585f,0.730735f,0.416886f}, +{0.554591f,0.652851f,0.515959f},{-0.45615f,0.77338f,-0.440239f},{-0.861977f,-0.205293f,0.463519f}, +{-0.851613f,0.0465911f,0.522096f},{-0.884595f,-0.0397011f,0.464667f},{-0.265588f,0.792521f,0.548975f}, +{0.626767f,0.74215f,-0.237436f},{0.650336f,0.759537f,0.0128991f},{0.607046f,0.766684f,0.209022f}, +{0.42584f,0.81007f,-0.403047f},{0.479766f,0.78941f,-0.382957f},{0.440625f,0.776807f,-0.449912f}, +{-0.965918f,-0.188346f,-0.177559f},{-0.917033f,-0.300542f,-0.262156f},{0.655378f,0.670166f,-0.348365f}, +{0.655846f,0.747655f,-0.104302f},{0.638829f,0.768028f,-0.0450667f},{0.620536f,0.777113f,0.105025f}, +{0.419917f,0.795884f,0.436164f},{-0.484423f,0.76896f,-0.417174f},{0.283428f,-0.883937f,-0.371919f}, +{-0.877507f,-0.432781f,-0.206596f},{-0.0647534f,0.116705f,-0.991053f},{-0.00251298f,-0.196393f,-0.980522f}, +{-0.120996f,-0.17257f,-0.977537f},{0.625618f,0.75948f,-0.178302f},{0.622883f,0.775196f,-0.105295f}, +{0.618509f,0.784484f,0.045081f},{0.555738f,0.808838f,0.192188f},{0.540989f,0.792397f,0.281847f}, +{0.494361f,0.783838f,0.375773f},{-0.472517f,-0.398134f,0.786268f},{-0.503204f,-0.412649f,0.75928f}, +{-0.0691448f,0.651623f,-0.755385f},{-0.0855156f,0.570482f,-0.816846f},{-0.886059f,-0.355448f,-0.297583f}, +{0.594546f,0.762545f,-0.255029f},{0.622101f,0.721187f,-0.304761f},{0.605176f,0.774254f,-0.185182f}, +{0.610974f,0.791608f,0.00823471f},{0.573345f,0.817999f,0.0464058f},{0.560795f,0.821271f,0.10499f}, +{0.414525f,0.848734f,0.328358f},{0.736676f,0.649956f,0.186727f},{0.0552009f,0.917445f,0.394014f}, +{0.58116f,0.774985f,-0.248299f},{0.603961f,0.79543f,-0.0502213f},{0.566922f,0.823735f,0.00775409f}, +{0.433232f,0.874383f,0.218552f},{0.338907f,0.844676f,0.414324f},{0.335796f,0.825149f,0.45428f}, +{0.335634f,0.727757f,0.598097f},{0.592382f,0.796432f,-0.121568f},{0.564733f,0.823747f,-0.0501802f}, +{0.471839f,0.874441f,0.112784f},{0.391375f,0.8556f,0.338782f},{0.272028f,0.795259f,0.541815f}, +{-0.321054f,-0.946891f,-0.017938f},{-0.583011f,0.804225f,-0.115411f},{-0.0186705f,0.88413f,0.466868f}, +{0.58237f,0.771302f,-0.256787f},{0.583304f,0.788563f,-0.194745f},{0.56576f,0.815156f,-0.124239f}, +{0.508372f,0.860502f,0.0330708f},{0.427096f,0.759848f,0.490122f},{0.0181058f,0.259257f,-0.965639f}, +{-0.0500199f,0.232695f,-0.971262f},{0.0170665f,0.950699f,0.309644f},{-0.0152952f,0.950205f,0.311251f}, +{-0.881404f,0.0726797f,-0.466739f},{-0.877608f,0.0389104f,-0.477797f},{0.30436f,0.386033f,-0.870829f}, +{0.165782f,0.556552f,-0.814105f},{0.346919f,0.302604f,-0.887737f},{0.513981f,0.856343f,-0.0500122f}, +{0.511201f,0.859461f,-0.000758396f},{0.361243f,0.903013f,0.23253f},{0.452609f,0.687968f,0.567314f}, +{0.407572f,0.674302f,0.615794f},{0.57013f,0.802797f,-0.174555f},{0.51097f,0.852913f,-0.107003f}, +{0.453526f,0.888135f,0.0743731f},{0.413537f,0.896599f,0.158418f},{0.389912f,0.886016f,0.250887f}, +{0.445681f,0.789273f,0.422395f},{0.309789f,0.731113f,0.607869f},{-0.0846571f,0.900316f,0.426924f}, +{0.545197f,0.794322f,-0.267979f},{0.547967f,0.836466f,-0.00756271f},{0.470132f,0.882429f,0.0171804f}, +{0.431296f,0.900927f,0.0481153f},{0.416849f,0.879298f,0.230372f},{0.440959f,0.838019f,0.321371f}, +{0.407999f,0.737634f,0.537989f},{-0.886332f,0.390751f,0.248453f},{-0.833616f,0.516196f,0.196536f}, +{0.471464f,0.881296f,-0.0322266f},{0.438674f,0.898631f,0.00523751f},{0.406783f,0.824159f,0.394068f}, +{0.2856f,0.832072f,0.475488f},{0.457025f,0.885608f,-0.0826232f},{0.446409f,0.893715f,-0.0446337f}, +{0.402155f,0.90619f,0.130735f},{0.173173f,0.782164f,0.598523f},{-0.13066f,-0.990258f,0.0481328f}, +{0.516393f,0.806743f,0.287235f},{-0.430925f,0.715581f,0.54977f},{0.448735f,0.327623f,-0.831445f}, +{0.493816f,0.1669f,-0.853399f},{-0.512482f,0.435375f,0.740143f},{-0.501902f,0.485164f,0.716038f}, +{0.00864245f,0.296578f,-0.95497f},{-0.638979f,0.481665f,-0.599754f},{-0.619463f,0.418906f,-0.663915f}, +{-0.676628f,-0.0650151f,0.733449f},{-0.653662f,-0.082975f,0.752224f},{-0.807571f,0.313101f,0.499796f}, +{-0.757473f,0.403682f,0.513103f},{-0.852017f,0.200593f,0.483559f},{-0.864641f,0.0993041f,0.492479f}, +{-0.819801f,0.214178f,0.531088f},{-0.890242f,0.0583071f,0.451741f},{-0.685458f,0.430081f,0.587519f}, +{-0.512972f,0.206372f,0.833229f},{-0.788466f,-0.229526f,-0.570648f},{0.0241731f,-0.998842f,-0.0416034f}, +{0.0326615f,-0.998887f,-0.0340157f},{0.0295508f,-0.999062f,-0.03164f},{-0.467001f,0.782354f,0.412107f}, +{-0.292685f,0.821794f,0.488866f},{-0.412556f,0.764212f,0.495759f},{-0.610348f,0.435518f,0.661664f}, +{-0.811272f,-0.319379f,0.48973f},{-0.81612f,-0.309379f,0.48809f},{-0.70417f,0.414397f,0.576559f}, +{-0.687124f,0.513775f,0.513707f},{-0.47835f,0.641065f,0.600181f},{-0.421403f,0.744624f,0.517644f}, +{-0.741206f,0.448145f,0.499779f},{-0.814976f,0.396613f,0.422508f},{-0.812736f,-0.386413f,-0.436056f}, +{-0.104997f,0.47965f,-0.871155f},{-0.0162628f,0.460022f,-0.887759f},{-0.765465f,0.240563f,0.596819f}, +{-0.597611f,0.57362f,0.560198f},{-0.689789f,0.505065f,0.518749f},{-0.727175f,0.423293f,0.540407f}, +{-0.308127f,0.849674f,0.427916f},{-0.0791706f,0.609536f,0.788796f},{-0.823089f,-0.0371484f,0.566695f}, +{-0.82769f,-0.0336199f,0.560178f},{-0.815599f,-0.0632867f,0.575146f},{-0.127929f,-0.000264798f,-0.991783f}, +{-0.84084f,0.290014f,0.457034f},{-0.859387f,0.296015f,0.416928f},{-0.89802f,-0.0693067f,0.434461f}, +{-0.825519f,-0.0620058f,0.560957f},{-0.818802f,-0.152933f,0.55333f},{-0.80371f,-0.147357f,0.576486f}, +{-0.579463f,-0.023075f,-0.814672f},{-0.673952f,0.194501f,-0.712711f},{-0.588945f,0.0471562f,-0.806796f}, +{-0.425986f,-0.0397759f,-0.903855f},{-0.613002f,0.594276f,0.520638f},{-0.509363f,0.698473f,0.502678f}, +{-0.636005f,0.594838f,0.491595f},{-0.498753f,-0.295648f,0.814762f},{-0.780513f,-0.394895f,0.484621f}, +{-0.726784f,-0.181705f,0.662396f},{-0.654261f,-0.705981f,0.27117f},{-0.454155f,-0.890405f,-0.0303461f}, +{-0.78205f,-0.621982f,-0.0391931f},{-0.813216f,-0.3632f,0.454716f},{-0.846288f,-0.351263f,0.400513f}, +{-0.833476f,-0.307216f,0.459277f},{-0.851383f,-0.494771f,0.174211f},{-0.924493f,-0.309175f,-0.222986f}, +{-0.424515f,0.751553f,0.50493f},{-0.801391f,-0.0292747f,0.597424f},{-0.823191f,-0.323435f,0.466634f}, +{-0.81734f,-0.314257f,0.482906f},{-0.827421f,-0.291732f,0.479861f},{-0.536161f,0.21014f,0.817541f}, +{-0.479851f,0.395272f,0.783264f},{-0.585877f,0.38605f,0.712541f},{-0.818216f,-0.122578f,0.561691f}, +{-0.827807f,-0.206853f,0.521486f},{-0.405365f,0.515397f,0.755014f},{-0.679631f,0.446817f,0.58177f}, +{-0.544681f,0.503073f,0.671f},{-0.748484f,-0.190258f,0.635274f},{-0.582454f,0.295646f,-0.757193f}, +{-0.238605f,-0.733799f,-0.636087f},{-0.211375f,-0.785263f,-0.581965f},{-0.248305f,-0.659941f,-0.7091f}, +{-0.671462f,-0.241978f,0.700418f},{-0.702697f,0.40569f,0.584494f},{-0.424814f,-0.45386f,0.78329f}, +{0.50287f,0.771715f,-0.389331f},{-0.681696f,-0.191082f,0.706242f},{-0.716836f,-0.265053f,0.644897f}, +{-0.770713f,-0.297385f,0.563528f},{-0.747238f,-0.118241f,0.653953f},{-0.954785f,-0.184225f,0.23334f}, +{-0.548361f,-0.727836f,0.411769f},{-0.921405f,-0.371992f,0.112408f},{-0.503367f,-0.393242f,0.769404f}, +{-0.576726f,-0.342498f,0.741675f},{-0.361183f,0.225455f,-0.90483f},{-0.891653f,0.404043f,0.204217f}, +{0.0338141f,-0.928257f,0.370398f},{-0.0219507f,-0.943046f,0.331938f},{-0.0745498f,-0.684872f,0.72484f}, +{-0.806646f,-0.13619f,0.57513f},{-0.808811f,-0.404271f,0.427071f},{-0.44685f,0.609374f,0.654972f}, +{-0.461291f,0.570642f,0.679396f},{-0.170721f,-0.981654f,0.0849051f},{0.548492f,0.127866f,-0.826321f}, +{0.481372f,0.192323f,-0.855157f},{0.542861f,0.164021f,-0.82365f},{-0.29651f,0.199589f,-0.933941f}, +{0.227659f,0.120444f,-0.966263f},{-0.59647f,0.306507f,-0.741807f},{0.0284581f,0.296894f,-0.954486f}, +{0.0122738f,0.499735f,-0.866092f},{-0.829669f,-0.29228f,0.475629f},{-0.485308f,-0.240124f,0.840724f}, +{-0.438823f,-0.341511f,0.831146f},{-0.605016f,0.775383f,0.180933f},{-0.712909f,0.673776f,-0.194388f}, +{-0.433914f,-0.114761f,-0.893615f},{-0.513091f,-0.0345913f,-0.857637f},{-0.521915f,0.0848665f,-0.848765f}, +{-0.655513f,0.204912f,0.726852f},{-0.425762f,-0.24177f,-0.871937f},{-0.814908f,-0.301909f,0.494749f}, +{-0.532045f,-0.147684f,0.833737f},{-0.896942f,-0.179224f,0.404195f},{-0.560803f,0.260994f,0.785737f}, +{-0.676909f,-0.32187f,0.661962f},{-0.804451f,-0.351504f,0.478856f},{-0.826835f,-0.314315f,0.466423f}, +{0.24554f,-0.848717f,0.468391f},{0.252259f,-0.880717f,0.400878f},{0.261369f,-0.906495f,0.331593f}, +{-0.871316f,0.402879f,0.280174f},{-0.947823f,0.241453f,0.208163f},{-0.736231f,0.393781f,0.550364f}, +{-0.897723f,0.376006f,0.22959f},{-0.499015f,-0.219682f,0.838286f},{-0.685411f,0.694441f,-0.219006f}, +{-0.496778f,0.833956f,0.240268f},{-0.679974f,-0.471155f,0.561826f},{-0.587924f,-0.513635f,0.62492f}, +{-0.626815f,-0.437178f,0.644963f},{-0.814539f,-0.302632f,0.494915f},{0.0151252f,-0.997929f,0.0625192f}, +{-0.368475f,0.238131f,-0.898621f},{-0.356529f,0.312421f,-0.8805f},{-0.457637f,-0.114404f,0.881748f}, +{0.029499f,-0.999511f,-0.0103668f},{-0.00938924f,-0.999942f,0.00521503f},{-0.00868702f,-0.99974f,-0.0210859f}, +{-0.883607f,-0.125016f,0.451231f},{-0.897507f,0.0204293f,0.440526f},{-0.667722f,0.554902f,0.496217f}, +{-0.763542f,0.385739f,0.51789f},{-0.510416f,-0.322454f,0.797182f},{-0.464866f,-0.34522f,0.815305f}, +{-0.158149f,0.34114f,-0.926613f},{-0.627815f,0.613943f,0.478458f},{-0.44089f,0.789623f,0.426745f}, +{-0.828792f,-0.311666f,0.464724f},{-0.554504f,0.725721f,0.407252f},{-0.662594f,-0.0304637f,0.748359f}, +{-0.435102f,-0.462729f,0.772378f},{-0.666116f,0.499234f,0.554125f},{-0.732412f,0.118538f,0.670464f}, +{-0.869508f,-0.48865f,0.0719463f},{-0.311367f,-0.328529f,-0.891694f},{-0.971581f,-0.23594f,0.0190483f}, +{-0.863437f,-0.282645f,0.417838f},{-0.302687f,-0.925788f,-0.226489f},{-0.488993f,-0.817903f,-0.303186f}, +{-0.80689f,-0.288878f,0.515246f},{-0.551655f,0.717482f,0.425319f},{-0.432414f,-0.240387f,0.869041f}, +{-0.897626f,0.378674f,0.225551f},{-0.576051f,0.646691f,0.499956f},{-0.526094f,0.728597f,0.438601f}, +{-0.77966f,0.166922f,0.603545f},{-0.787693f,0.266878f,0.555261f},{-0.601311f,-0.0491254f,-0.797504f}, +{-0.785811f,0.00908449f,-0.6184f},{-0.769137f,0.0463938f,-0.637398f},{-0.445453f,-0.202091f,-0.872199f}, +{-0.654855f,-0.272176f,-0.705043f},{-0.544025f,-0.184676f,-0.818493f},{0.030312f,0.401667f,-0.915284f}, +{-0.041871f,0.443354f,-0.895368f},{-0.0727087f,0.385587f,-0.919802f},{-0.511658f,0.7034f,0.493391f}, +{-0.805663f,0.152777f,0.572334f},{-0.751126f,-0.47213f,0.461414f},{-0.798257f,-0.322169f,0.508913f}, +{-0.576619f,0.0724766f,0.813792f},{-0.520617f,0.718185f,0.461702f},{-0.33865f,0.562898f,0.753964f}, +{0.234955f,0.238834f,-0.942207f},{0.251331f,0.314931f,-0.915233f},{0.185063f,0.330729f,-0.925403f}, +{-0.886911f,0.375469f,0.269096f},{-0.154109f,0.250557f,-0.955757f},{-0.400531f,0.813668f,0.421331f}, +{-0.5416f,-0.770518f,0.336113f},{-0.544135f,0.16461f,-0.822691f},{-0.211698f,0.175625f,-0.961426f}, +{-0.334504f,-0.0129159f,-0.942306f},{-0.394133f,0.000893468f,-0.919053f},{-0.391245f,0.0852153f,-0.916333f}, +{-0.648911f,0.15809f,-0.74426f},{-0.812766f,-0.31308f,0.491318f},{-0.692476f,-0.419684f,0.586807f}, +{-0.81264f,-0.0624337f,0.579412f},{-0.633244f,-0.291183f,0.717088f},{-0.952223f,0.27226f,0.138367f}, +{-0.452731f,0.502721f,0.736415f},{-0.719407f,-0.663803f,-0.2045f},{-0.613654f,-0.789512f,-0.00995104f}, +{-0.737211f,-0.205266f,0.643728f},{-0.575835f,0.262539f,-0.774266f},{-0.543006f,0.155016f,-0.825297f}, +{-0.769728f,0.282775f,0.572326f},{-0.794855f,0.0192248f,0.606495f},{-0.466456f,-0.18462f,0.865063f}, +{-0.502563f,-0.290176f,0.814388f},{-0.581134f,-0.13327f,0.802821f},{-0.339314f,-0.250814f,0.906619f}, +{-0.831761f,-0.0775132f,0.549696f},{0.0198175f,-0.999708f,-0.0138038f},{0.00494828f,-0.999849f,-0.0166457f}, +{0.0221211f,-0.999637f,-0.0153761f},{-0.35896f,0.0528321f,-0.931857f},{-0.394276f,0.80845f,0.436984f}, +{-0.633605f,0.20823f,0.745108f},{-0.594276f,0.417433f,0.687449f},{-0.546438f,0.464911f,0.696608f}, +{-0.595217f,0.476599f,0.64697f},{-0.362789f,-0.186007f,-0.913119f},{-0.420958f,0.66549f,0.616375f}, +{-0.543996f,0.546085f,0.637071f},{-0.325797f,-0.26756f,0.90679f},{-0.183163f,-0.194712f,0.963607f}, +{-0.445429f,0.0859319f,-0.891184f},{-0.545556f,0.105608f,-0.831394f},{-0.610353f,0.539882f,-0.579653f}, +{-0.545833f,-0.242049f,0.802171f},{-0.759919f,-0.00155307f,0.650016f},{-0.752876f,-0.0855841f,0.652574f}, +{-0.534606f,-0.263421f,0.802998f},{-0.552957f,-0.182488f,0.81298f},{-0.635767f,0.182895f,0.7499f}, +{-0.559979f,0.292242f,0.775253f},{-0.598309f,0.249397f,0.761464f},{-0.638105f,0.200247f,0.743453f}, +{-0.66314f,0.0604394f,0.746051f},{-0.510999f,0.0128487f,0.859485f},{-0.452527f,-0.107616f,0.885233f}, +{-0.446189f,-0.437712f,0.780592f},{-0.818986f,0.569003f,0.0741459f},{-0.256962f,-0.883326f,0.392053f}, +{0.160324f,0.287601f,-0.944236f},{-0.98996f,-0.125316f,0.0653766f},{-0.984502f,-0.118441f,-0.129335f}, +{0.0653595f,-0.997831f,0.00782317f},{-0.17971f,-0.965573f,0.188077f},{0.00449062f,-0.995378f,0.0959267f}, +{-0.50379f,-0.181022f,0.844646f},{0.00183938f,-0.468409f,-0.88351f},{-0.450589f,0.612839f,0.649152f}, +{-0.638726f,0.29257f,0.71164f},{-0.572547f,0.229584f,0.787071f},{-0.545939f,0.374596f,0.749419f}, +{-0.346339f,0.933504f,-0.092841f},{-0.532212f,0.180167f,0.827218f},{-0.657665f,-0.128369f,0.742293f}, +{-0.557517f,-0.316215f,0.767582f},{-0.537161f,0.59042f,0.60238f},{-0.433749f,0.634675f,0.63957f}, +{-0.907702f,-0.239757f,-0.344374f},{-0.815025f,-0.244928f,-0.525113f},{-0.508138f,-0.465009f,0.724957f}, +{-0.597399f,-0.419279f,0.683608f},{-0.157794f,0.0739844f,-0.984697f},{-0.215131f,0.345583f,-0.913395f}, +{-0.641043f,-0.381159f,0.666169f},{-0.463103f,-0.204799f,-0.862318f},{-0.420183f,-0.257033f,-0.870276f}, +{-0.426429f,-0.833762f,0.350713f},{-0.288873f,-0.855688f,0.42936f},{-0.262027f,-0.885132f,0.384556f}, +{-0.505665f,-0.329907f,-0.79716f},{-0.423011f,-0.843442f,0.33116f},{-0.188296f,-0.95367f,0.234643f}, +{-0.186668f,-0.96361f,0.191339f},{0.016626f,-0.42944f,-0.902942f},{0.229574f,-0.618164f,-0.751778f}, +{0.213167f,-0.630559f,-0.746294f},{-0.653506f,0.170378f,0.737497f},{-0.754205f,0.139048f,0.641748f}, +{-0.733566f,-0.113507f,-0.670073f},{-0.714556f,0.0437677f,-0.698208f},{-0.534084f,0.6353f,0.557806f}, +{-0.477724f,0.66335f,0.575975f},{-0.564231f,-0.278572f,0.777201f},{-0.0153342f,-0.98466f,0.173808f}, +{-0.00899519f,-0.990321f,0.138506f},{-0.747073f,0.149048f,0.647816f},{-0.739693f,0.330268f,0.586325f}, +{-0.75432f,0.0203637f,0.656191f},{-0.584713f,-0.0127331f,0.81114f},{-0.627251f,0.0874965f,0.773886f}, +{-0.375288f,0.744741f,0.551832f},{-0.85599f,0.509751f,-0.086229f},{-0.856581f,0.450396f,-0.251818f}, +{-0.964796f,0.114569f,-0.236733f},{-0.788723f,0.613207f,-0.0435051f},{-0.705179f,0.661605f,-0.254952f}, +{-0.622839f,-0.125899f,0.772154f},{-0.643766f,0.0804802f,0.760979f},{-0.561995f,-0.357569f,0.745859f}, +{-0.650896f,-0.230573f,0.723305f},{-0.647344f,0.0159482f,0.762031f},{-0.719224f,0.0847135f,0.689595f}, +{-0.66153f,-0.118747f,0.740458f},{-0.201788f,0.273426f,-0.940489f},{-0.265744f,0.281923f,-0.9219f}, +{-0.222384f,0.236431f,-0.945857f},{0.373598f,0.281689f,-0.883785f},{0.400531f,0.272846f,-0.874717f}, +{0.437973f,0.267974f,-0.85812f},{-0.615037f,-0.456725f,0.642753f},{-0.826765f,0.552627f,-0.10518f}, +{-0.613802f,-0.0665211f,0.786653f},{-0.794276f,-0.309824f,-0.522623f},{-0.558536f,-0.666482f,-0.493801f}, +{-0.663791f,-0.693794f,-0.27934f},{-0.614065f,-0.405564f,0.677083f},{-0.675169f,0.476757f,0.562895f}, +{-0.589291f,0.541487f,0.599607f},{-0.388849f,-0.419335f,0.820338f},{-0.637315f,-0.0303753f,0.770004f}, +{-0.0304552f,0.316204f,-0.948202f},{-0.26997f,0.334033f,-0.903071f},{-0.214728f,0.536029f,-0.816434f}, +{-0.415018f,0.564237f,-0.71372f},{-0.453154f,0.844034f,-0.286808f},{0.153137f,0.210379f,-0.965552f}, +{0.249746f,0.276643f,-0.927952f},{-0.836283f,0.441701f,0.324856f},{-0.694397f,-0.327434f,-0.640781f}, +{-0.634576f,-0.386536f,-0.669255f},{-0.722049f,-0.311531f,-0.617732f},{-0.701986f,0.404823f,0.585947f}, +{-0.769988f,0.330764f,0.545631f},{-0.81952f,0.310075f,0.481913f},{-0.793846f,0.44448f,0.415026f}, +{0.348806f,-0.234002f,-0.907512f},{-0.556915f,0.764365f,0.32495f},{-0.702632f,-0.354421f,-0.617004f}, +{-0.591409f,-0.175052f,-0.787142f},{-0.603679f,-0.341051f,-0.720595f},{-0.829158f,0.205157f,0.520006f}, +{-0.0234405f,-0.0669621f,-0.99748f},{0.0827914f,-0.212663f,-0.973612f},{-0.613392f,-0.382432f,-0.69101f}, +{-0.669679f,-0.44593f,-0.593865f},{-0.587962f,-0.438583f,-0.679666f},{-0.514604f,-0.418881f,-0.748145f}, +{-0.683355f,0.203558f,-0.701135f},{-0.343854f,-0.869667f,0.35418f},{-0.693639f,0.102823f,0.712946f}, +{-0.643381f,0.507362f,0.573276f},{-0.179229f,-0.798935f,0.57409f},{-0.625846f,-0.345565f,-0.699215f}, +{0.193112f,-0.566099f,0.801399f},{0.363743f,-0.582821f,0.726644f},{-0.590179f,-0.699363f,0.403211f}, +{-0.782717f,-0.13318f,0.607962f},{-0.750313f,0.182009f,0.635534f},{-0.685217f,0.425887f,0.590845f}, +{-0.765752f,-0.0469561f,0.641419f},{-0.55663f,-0.659276f,0.505489f},{-0.430096f,0.189334f,-0.882706f}, +{-0.868659f,-0.194001f,0.455845f},{-0.948159f,0.316867f,-0.0242684f},{-0.923184f,0.288381f,0.2541f}, +{-0.864769f,-0.393138f,0.312437f},{-0.673731f,0.121125f,0.728982f},{-0.447896f,-0.0639182f,-0.891798f}, +{-0.732419f,0.332546f,0.594117f},{-0.697571f,0.617732f,-0.363044f},{-0.232532f,-0.885746f,0.401724f}, +{-0.549515f,-0.43611f,0.71263f},{-0.915083f,0.184311f,0.358682f},{-0.631895f,0.505717f,0.587332f}, +{-0.048755f,-0.912305f,0.406599f},{-0.00499332f,-0.708072f,0.706123f},{-0.740317f,0.109004f,0.663362f}, +{-0.748566f,-0.168053f,0.64141f},{-0.819601f,-0.148101f,0.553462f},{-0.825063f,-0.0489577f,0.562915f}, +{-0.885336f,-0.0479825f,0.462469f},{-0.826903f,0.073578f,0.55751f},{-0.88385f,0.0608632f,0.463794f}, +{-0.838473f,-0.318529f,0.442156f},{-0.647852f,0.483823f,0.58839f},{-0.67151f,-0.362356f,0.646353f}, +{-0.887521f,-0.297247f,0.352066f},{-0.858956f,-0.223478f,0.460709f},{-0.811293f,0.176758f,0.557279f}, +{-0.809109f,0.259872f,0.527075f},{-0.513998f,0.694608f,0.503314f},{-0.461626f,-0.819637f,-0.339258f}, +{-0.310498f,-0.762119f,-0.568125f},{-0.323819f,-0.907785f,-0.266585f},{-0.873213f,0.321721f,0.366054f}, +{-0.0693406f,-0.767637f,0.637123f},{-0.0292936f,-0.951498f,0.306258f},{0.202785f,-0.741235f,0.639882f}, +{-0.730227f,0.361624f,0.579652f},{-0.658946f,0.469511f,0.587664f},{-0.809055f,-0.1302f,0.573131f}, +{-0.764863f,0.244937f,0.595811f},{-0.775799f,0.182781f,0.603926f},{0.250327f,0.459893f,-0.851959f}, +{-0.228841f,-0.44984f,0.863294f},{-0.783305f,0.442176f,0.436936f},{0.311199f,-0.345385f,-0.885361f}, +{0.17222f,-0.27239f,-0.946649f},{-0.791398f,-0.0531614f,0.608985f},{-0.786133f,0.0675726f,0.614352f}, +{-0.66844f,-0.297679f,-0.681598f},{-0.580798f,-0.26834f,-0.768549f},{-0.638322f,0.206607f,0.741524f}, +{-0.598039f,0.28478f,0.749166f},{-0.706041f,-0.141691f,0.693851f},{-0.504882f,0.619097f,0.601509f}, +{-0.424321f,0.739317f,0.52284f},{-0.420945f,0.809959f,0.408377f},{-0.470592f,0.407277f,0.782732f}, +{-0.647062f,-0.190766f,-0.738187f},{-0.681777f,-0.143191f,-0.717409f},{-0.772775f,-0.260891f,-0.578581f}, +{-0.473436f,-0.635563f,-0.609851f},{-0.470861f,-0.668675f,-0.575468f},{-0.549722f,-0.641587f,-0.534951f}, +{-0.913123f,-0.21534f,0.346171f},{-0.850555f,-0.110966f,0.514045f},{-0.800178f,-0.0676003f,0.59594f}, +{-0.759561f,-0.0435877f,0.648974f},{-0.75589f,0.0642564f,0.651538f},{-0.744366f,0.180982f,0.642779f}, +{-0.978468f,-0.150371f,0.141381f},{-0.626719f,0.274355f,-0.729351f},{-0.817811f,0.468598f,0.334068f}, +{-0.732834f,0.078916f,0.675815f},{-0.602859f,0.410502f,0.684141f},{-0.423418f,0.689193f,0.587988f}, +{-0.451121f,-0.705396f,-0.546724f},{-0.39819f,-0.683051f,-0.612279f},{-0.449073f,-0.679805f,-0.579826f}, +{-0.970855f,0.186967f,0.149948f},{-0.133586f,-0.655213f,0.743539f},{-0.746645f,-0.422237f,0.51404f}, +{-0.824048f,-0.311098f,0.473459f},{-0.833598f,-0.199787f,0.514975f},{-0.718462f,0.256136f,0.646689f}, +{-0.843945f,-0.00335243f,0.53642f},{-0.695026f,0.203394f,0.689615f},{-0.572752f,0.593211f,0.565735f}, +{-0.802339f,0.45988f,0.380477f},{-0.885006f,0.353562f,0.302916f},{-0.66682f,0.60401f,0.43649f}, +{-0.5106f,0.593405f,0.622221f},{-0.396866f,0.798109f,0.453344f},{-0.438631f,-0.817212f,0.373856f}, +{0.628597f,0.070023f,-0.774572f},{0.567272f,0.0837215f,-0.819264f},{0.539457f,0.0680128f,-0.839262f}, +{-0.339831f,-0.126378f,-0.931957f},{-0.0729324f,-0.0291307f,-0.996911f},{0.44108f,0.201069f,-0.874654f}, +{-0.804002f,-0.209477f,0.556508f},{-0.69699f,0.239007f,0.676077f},{-0.682645f,0.263004f,0.68178f}, +{-0.432575f,-0.75762f,0.488766f},{-0.468404f,0.0411905f,0.882554f},{-0.64841f,0.269969f,0.711815f}, +{-0.550359f,0.491757f,0.674744f},{-0.378679f,0.762194f,0.525036f},{-0.348953f,0.788101f,0.507079f}, +{-0.582953f,0.580175f,0.568825f},{-0.732857f,0.474231f,-0.487879f},{-0.553702f,0.702597f,-0.446959f}, +{0.373769f,0.188421f,-0.908182f},{-0.813838f,-0.0701697f,0.576839f},{-0.788545f,-0.0220965f,0.614579f}, +{-0.759679f,-0.0248215f,0.649824f},{-0.707009f,0.225235f,0.670379f},{-0.738227f,0.138017f,0.660282f}, +{0.0382768f,-0.996456f,0.0749003f},{-0.766233f,0.258764f,0.588157f},{-0.686959f,0.567356f,0.454087f}, +{-0.73823f,-0.0898627f,0.668536f},{-0.372659f,0.774285f,0.511477f},{-0.723401f,0.205919f,0.659006f}, +{0.160182f,-0.5659f,-0.808764f},{0.434856f,-0.616598f,-0.656283f},{0.0231616f,-0.97957f,-0.199767f}, +{-0.66567f,-0.564697f,0.487853f},{0.316206f,0.241841f,-0.917348f},{0.375475f,0.220209f,-0.900292f}, +{-0.7103f,-0.384795f,0.589412f},{-0.764348f,-0.17699f,0.620037f},{-0.802285f,-0.0445413f,0.595277f}, +{-0.77726f,0.0111166f,0.629081f},{-0.732092f,0.0259054f,0.680713f},{-0.688352f,0.102232f,0.718136f}, +{-0.680752f,0.226134f,0.696735f},{-0.884425f,0.348781f,0.310071f},{-0.861093f,0.272212f,0.429441f}, +{-0.343287f,-0.894841f,0.285332f},{-0.674998f,-0.708227f,0.206865f},{-0.800865f,0.372938f,0.468542f}, +{-0.503765f,0.611881f,0.609772f},{-0.392682f,0.749152f,0.533453f},{-0.650023f,0.325047f,0.686888f}, +{-0.291221f,-0.315675f,-0.903072f},{-0.30522f,-0.105777f,-0.946389f},{-0.253134f,-0.266368f,-0.930039f}, +{-0.673406f,0.265473f,0.689963f},{-0.827281f,0.337787f,0.448896f},{-0.775433f,-0.55732f,0.296814f}, +{-0.749854f,-0.461074f,0.474477f},{-0.803676f,0.408894f,0.432332f},{-0.681633f,0.527799f,0.506759f}, +{0.607975f,-0.172449f,-0.775002f},{0.434956f,-0.900438f,-0.00486778f},{-0.580756f,0.519123f,0.627084f}, +{0.397173f,0.329672f,-0.856487f},{-0.360323f,-0.650174f,-0.668911f},{0.0343635f,0.0931413f,-0.99506f}, +{-0.631268f,-0.222999f,-0.742813f},{-0.746227f,-0.214046f,-0.630341f},{-0.671459f,-0.189288f,-0.716459f}, +{-0.659546f,-0.0381326f,-0.750696f},{-0.815974f,0.00295256f,0.578082f},{-0.888849f,0.0688656f,0.452996f}, +{-0.803058f,0.243254f,0.54399f},{-0.833593f,0.316012f,0.453055f},{-0.579459f,0.631209f,0.51556f}, +{-0.530057f,0.818813f,0.220418f},{0.229917f,-0.548714f,0.803773f},{0.189054f,-0.33771f,0.922069f}, +{0.629261f,0.0979529f,-0.770996f},{-0.676455f,-0.307285f,0.669316f},{-0.624391f,-0.452539f,0.636666f}, +{-0.751741f,-0.0822216f,0.654312f},{-0.659358f,0.21247f,0.721183f},{-0.466465f,-0.872416f,-0.145944f}, +{-0.297602f,-0.903282f,-0.309055f},{-0.00581529f,-0.986921f,-0.161101f},{-0.850233f,0.271603f,0.450927f}, +{-0.645747f,0.76093f,0.0632147f},{-0.742278f,-0.558166f,0.370775f},{-0.752377f,-0.470443f,0.461099f}, +{-0.817796f,0.574776f,0.0290093f},{-0.383178f,0.76701f,0.514655f},{-0.630893f,0.362954f,0.685739f}, +{0.0332487f,-0.998789f,-0.0362681f},{0.0243115f,-0.99914f,-0.0335984f},{0.279421f,-0.915639f,0.289013f}, +{-0.472456f,-0.157848f,-0.867104f},{-0.82926f,0.269673f,0.489493f},{-0.668103f,-0.469037f,0.577618f}, +{-0.622098f,0.656668f,-0.426358f},{-0.902728f,0.430072f,0.0109956f},{-0.0496615f,0.304177f,-0.95132f}, +{-0.883749f,-0.145445f,-0.444784f},{-0.265136f,0.243864f,-0.932863f},{-0.610512f,-0.789531f,-0.0625791f}, +{-0.190525f,0.190871f,-0.962948f},{-0.153502f,0.660391f,-0.735065f},{0.118652f,0.907375f,-0.403227f}, +{0.154264f,0.8649f,-0.477652f},{-0.0161343f,0.881f,-0.472842f},{0.235805f,0.915596f,-0.325698f}, +{0.217685f,0.886898f,-0.407463f},{-0.729908f,0.583186f,-0.356552f},{0.116651f,0.282444f,-0.952165f}, +{0.215742f,0.267895f,-0.938982f},{0.0109213f,0.907924f,-0.418992f},{0.162564f,0.653304f,-0.739437f}, +{0.0479323f,0.611091f,-0.790108f},{0.766837f,0.53508f,-0.354471f},{-0.653129f,-0.103326f,-0.750164f}, +{-0.867818f,-0.0822351f,-0.49003f},{-0.821129f,-0.155542f,-0.549139f},{0.51254f,0.652523f,-0.558137f}, +{0.589058f,0.59275f,-0.549234f},{0.219218f,0.840223f,-0.495953f},{0.278717f,0.785049f,-0.553185f}, +{0.197953f,0.797583f,-0.569804f},{-0.830511f,-0.209543f,-0.516084f},{-0.785971f,-0.188003f,-0.588986f}, +{-0.330683f,0.346221f,-0.87794f},{-0.324171f,0.619309f,-0.715101f},{-0.000413993f,-0.998852f,0.0479042f}, +{0.0241025f,0.940366f,-0.33931f},{-0.687919f,-0.155575f,-0.708917f},{-0.608044f,0.279841f,-0.742948f}, +{-0.869399f,-0.148854f,-0.471156f},{0.15548f,0.819973f,-0.550881f},{0.0859084f,0.827993f,-0.554118f}, +{-0.927365f,0.0475724f,-0.371121f},{-0.802364f,-0.227847f,-0.551632f},{-0.833075f,-0.210273f,-0.511635f}, +{-0.915617f,-0.0470975f,-0.399283f},{-0.876015f,-0.166462f,-0.452646f},{-0.782091f,-0.310435f,-0.540337f}, +{-0.69515f,-0.343201f,-0.631648f},{-0.625069f,-0.446537f,-0.640229f},{-0.625458f,-0.0937403f,-0.774606f}, +{-0.957594f,0.0112719f,-0.2879f},{-0.634462f,-0.376214f,-0.675219f},{-0.922026f,-0.141245f,-0.360442f}, +{-0.945026f,-0.0123246f,-0.326763f},{-0.915351f,-0.124506f,-0.382924f},{-0.847758f,-0.270915f,-0.455973f}, +{-0.785936f,-0.330092f,-0.522823f},{-0.764905f,-0.404768f,-0.501082f},{-0.572019f,-0.577086f,-0.582895f}, +{-0.475734f,-0.497352f,-0.725478f},{0.0412363f,-0.999149f,-0.000575673f},{-0.974726f,-0.011953f,-0.223085f}, +{-0.476842f,-0.660929f,-0.579478f},{-0.972092f,-0.00139264f,-0.234595f},{-0.948887f,-0.0918976f,-0.301941f}, +{-0.899332f,-0.194675f,-0.391539f},{-0.830531f,-0.355461f,-0.428795f},{-0.480972f,-0.670668f,-0.564686f}, +{-0.839168f,-0.338982f,-0.425309f},{-0.693691f,-0.500137f,-0.51832f},{-0.543033f,-0.647155f,-0.535075f}, +{-0.912901f,-0.218345f,-0.344872f},{-0.238438f,0.675042f,-0.698187f},{-0.181185f,0.617331f,-0.765555f}, +{-0.966653f,0.0317663f,-0.254111f},{-0.920666f,-0.00972829f,-0.390229f},{-0.970752f,-0.0188526f,-0.239345f}, +{-0.955074f,-0.110583f,-0.274962f},{-0.939826f,-0.171711f,-0.295367f},{-0.89025f,-0.290814f,-0.350546f}, +{-0.772973f,-0.474061f,-0.421638f},{-0.553283f,-0.649761f,-0.521238f},{-0.771538f,-0.484487f,-0.412312f}, +{-0.976721f,0.0834547f,-0.197616f},{-0.96782f,-0.0501943f,-0.246587f},{-0.602259f,0.376395f,-0.703997f}, +{-0.576564f,0.215634f,-0.788084f},{-0.666104f,0.00345015f,-0.745851f},{-0.658184f,-0.594629f,-0.461746f}, +{-0.98324f,0.0764916f,-0.165493f},{-0.978554f,0.0576061f,-0.197773f},{-0.963464f,-0.11138f,-0.243581f}, +{-0.927319f,-0.231403f,-0.294163f},{-0.821827f,-0.421623f,-0.38319f},{-0.694751f,-0.508234f,-0.508939f}, +{-0.570395f,-0.621536f,-0.536976f},{-0.979728f,0.108607f,-0.168339f},{-0.729935f,-0.560912f,-0.390607f}, +{-0.548836f,-0.641527f,-0.535932f},{-0.137297f,-0.0651369f,-0.988386f},{-0.983567f,0.0087842f,-0.180332f}, +{-0.955803f,-0.176433f,-0.235186f},{-0.869009f,-0.366445f,-0.332476f},{-0.774158f,-0.455288f,-0.439764f}, +{-0.619558f,-0.627742f,-0.471262f},{-0.491225f,-0.73335f,-0.469995f},{-0.858948f,-0.422974f,-0.288621f}, +{-0.322444f,0.919917f,-0.223118f},{-0.851542f,0.52224f,-0.0462868f},{0.329143f,0.78615f,-0.523099f}, +{0.346796f,0.808097f,-0.476143f},{0.260363f,0.82633f,-0.49939f},{0.49845f,0.168966f,-0.850293f}, +{0.542044f,0.204992f,-0.814964f},{0.531619f,0.163787f,-0.830996f},{0.476991f,0.648298f,-0.593455f}, +{0.412125f,0.600022f,-0.685657f},{0.534435f,0.512549f,-0.672066f},{0.0905934f,0.864983f,-0.493555f}, +{-0.0394386f,0.854323f,-0.518244f},{-0.530297f,0.721798f,-0.444739f},{0.245727f,-0.163502f,-0.95545f}, +{0.260433f,-0.101818f,-0.960108f},{-0.0886096f,0.68229f,-0.725691f},{-0.280944f,0.805369f,-0.521968f}, +{-0.157878f,0.797038f,-0.582928f},{-0.328697f,0.704327f,-0.629191f},{-0.940973f,-0.0805013f,0.32877f}, +{-0.978425f,0.154397f,-0.137284f},{-0.98397f,-0.0434644f,-0.172959f},{-0.923355f,-0.277758f,-0.265077f}, +{-0.845702f,-0.37754f,-0.377163f},{-0.728052f,-0.547275f,-0.412833f},{-0.523263f,-0.68013f,-0.513438f}, +{-0.761618f,-0.560802f,-0.324714f},{-0.0442249f,0.846128f,-0.531142f},{0.118938f,0.815643f,-0.566198f}, +{-0.106843f,0.701031f,-0.705082f},{-0.269174f,0.720933f,-0.638593f},{-0.956881f,0.109636f,0.268995f}, +{-0.87996f,0.403169f,0.251248f},{0.0556408f,0.312043f,-0.948437f},{0.232607f,0.302752f,-0.924249f}, +{0.0834377f,0.381388f,-0.920642f},{-0.000786142f,0.810607f,-0.58559f},{-0.035975f,0.777677f,-0.627634f}, +{0.0508906f,0.747998f,-0.661747f},{-0.107152f,0.734086f,-0.670549f},{-0.106257f,0.727624f,-0.677696f}, +{-0.31261f,0.743435f,-0.591252f},{-0.102666f,0.76875f,-0.631255f},{0.169555f,0.774775f,-0.609078f}, +{-0.0233887f,0.767772f,-0.640296f},{-0.357587f,0.907916f,-0.21868f},{-0.158598f,0.747223f,-0.645372f}, +{0.0443315f,0.544164f,-0.837807f},{0.0199804f,0.53045f,-0.847481f},{0.000180536f,0.693536f,-0.720422f}, +{0.184154f,0.705218f,-0.684657f},{-0.549089f,-0.833126f,0.0663552f},{-0.355312f,-0.924539f,0.137772f}, +{-0.550829f,-0.82148f,0.147507f},{-0.228807f,0.69465f,-0.681988f},{0.554866f,0.643871f,-0.526834f}, +{0.44528f,0.677502f,-0.585421f},{0.435356f,0.728495f,-0.528923f},{-0.0265682f,0.551005f,-0.834079f}, +{-0.0283525f,0.549752f,-0.834846f},{-0.0534209f,0.591202f,-0.804753f},{0.374862f,0.383933f,-0.843845f}, +{0.393388f,0.487811f,-0.779286f},{-0.0113779f,0.578591f,-0.815539f},{-0.600071f,-0.0107687f,-0.799875f}, +{-0.768247f,0.169597f,-0.617279f},{0.249316f,0.206271f,-0.9462f},{0.260273f,0.258905f,-0.930175f}, +{0.200955f,0.168393f,-0.965019f},{-0.0240418f,-0.994179f,0.105028f},{0.00728482f,-0.993241f,0.115838f}, +{-0.965055f,0.204971f,-0.163264f},{-0.947471f,0.271797f,-0.168596f},{-0.976652f,0.151255f,-0.152557f}, +{-0.969842f,-0.135725f,-0.202448f},{-0.920074f,-0.273845f,-0.28013f},{-0.867173f,-0.400469f,-0.296032f}, +{-0.629318f,-0.659676f,-0.410837f},{-0.912934f,-0.31712f,-0.256878f},{0.353662f,0.330311f,-0.87511f}, +{0.402182f,0.318681f,-0.858307f},{0.451212f,0.315875f,-0.834644f},{0.0698329f,0.848959f,-0.523824f}, +{0.1656f,0.746272f,-0.644713f},{0.0791368f,0.199257f,-0.976747f},{-0.799717f,-0.230111f,-0.554528f}, +{-0.823964f,0.15489f,-0.545062f},{-0.843488f,0.216541f,-0.491566f},{0.00578011f,0.749809f,-0.661629f}, +{-0.0349199f,0.699277f,-0.713997f},{-0.0829151f,0.75947f,-0.645237f},{-0.880051f,0.380791f,0.283742f}, +{-0.303249f,0.728386f,-0.614406f},{-0.0616567f,-0.982204f,-0.177406f},{-0.580776f,-0.680002f,-0.447545f}, +{0.64457f,0.353625f,-0.677849f},{0.611951f,0.453782f,-0.647764f},{0.620319f,0.469135f,-0.628583f}, +{0.14269f,0.455273f,-0.878844f},{-0.114267f,0.845521f,-0.521571f},{-0.209425f,0.898253f,-0.386372f}, +{-0.260342f,0.837888f,-0.479755f},{-0.298369f,0.85314f,-0.427935f},{-0.617404f,-0.37837f,-0.689673f}, +{-0.680221f,-0.42967f,-0.593871f},{-0.710034f,-0.37957f,-0.593109f},{-0.78232f,-0.388716f,-0.486698f}, +{-0.832582f,0.30817f,-0.46026f},{-0.461526f,0.804212f,-0.374483f},{-0.490626f,0.695119f,-0.525448f}, +{-0.61646f,0.663409f,-0.424106f},{-0.639165f,0.549961f,-0.537597f},{-0.469639f,0.537434f,-0.700431f}, +{0.679093f,0.422242f,-0.600453f},{0.698225f,0.270889f,-0.662647f},{0.615003f,0.328574f,-0.716806f}, +{0.233794f,0.579118f,-0.781001f},{0.226217f,0.491376f,-0.841056f},{0.259529f,0.520396f,-0.813531f}, +{0.285164f,0.525232f,-0.801757f},{0.252231f,0.40395f,-0.87932f},{0.228237f,0.438116f,-0.869461f}, +{0.373608f,0.508213f,-0.775975f},{0.414642f,0.498775f,-0.761115f},{0.338994f,0.504203f,-0.794269f}, +{-0.672568f,-0.267274f,-0.690085f},{-0.738537f,-0.304573f,-0.601497f},{-0.581012f,-0.305672f,-0.754314f}, +{-0.816326f,-0.311619f,-0.486318f},{-0.840138f,-0.337409f,-0.424645f},{-0.897044f,-0.216641f,-0.385201f}, +{-0.900858f,-0.0265083f,-0.433303f},{-0.865957f,0.140418f,-0.480001f},{-0.847461f,0.273879f,-0.454753f}, +{-0.842143f,0.354717f,-0.406167f},{-0.737181f,0.516162f,-0.436052f},{-0.927486f,0.302631f,0.219509f}, +{-0.981675f,0.141154f,-0.128021f},{-0.965402f,-0.216887f,-0.144771f},{-0.743468f,-0.592587f,-0.309994f}, +{-0.576586f,-0.709313f,-0.405492f},{-0.80673f,-0.53517f,-0.250559f},{-0.948932f,0.0757926f,0.30624f}, +{0.0720768f,0.624395f,-0.777776f},{-0.102689f,0.384984f,-0.917192f},{-0.649688f,-0.339633f,-0.680114f}, +{-0.763436f,-0.24085f,-0.599297f},{-0.842123f,-0.328308f,-0.427834f},{-0.93705f,-0.0397002f,-0.346932f}, +{-0.832593f,0.415712f,-0.366023f},{0.0558894f,0.647015f,-0.760426f},{0.186694f,0.515431f,-0.836347f}, +{0.167057f,0.796417f,-0.581216f},{-0.013721f,0.746685f,-0.665036f},{-0.074801f,0.743277f,-0.664789f}, +{-0.557577f,-0.401232f,-0.726719f},{-0.695221f,-0.350494f,-0.627552f},{-0.758386f,-0.342505f,-0.554564f}, +{-0.923356f,0.113223f,-0.366871f},{-0.509779f,0.79534f,-0.327962f},{-0.979265f,0.119692f,0.163447f}, +{-0.98726f,0.0809253f,0.137f},{-0.904176f,-0.388755f,-0.177018f},{0.455558f,0.263758f,-0.850235f}, +{0.487306f,0.146717f,-0.860818f},{0.205814f,0.624123f,-0.753732f},{-0.0595557f,0.7814f,-0.621182f}, +{-0.100202f,0.733338f,-0.67244f},{-0.798938f,-0.286607f,-0.52873f},{-0.827743f,-0.32863f,-0.4548f}, +{-0.861636f,-0.297967f,-0.410852f},{-0.778058f,-0.249262f,-0.576623f},{-0.86606f,-0.265874f,-0.423381f}, +{-0.886519f,-0.259833f,-0.382847f},{-0.884378f,-0.314477f,-0.344935f},{-0.874401f,-0.254297f,-0.413226f}, +{-0.934063f,-0.0868268f,-0.346391f},{-0.951526f,0.0806207f,-0.296814f},{-0.889652f,0.184177f,-0.417849f}, +{-0.850233f,0.348223f,-0.394771f},{-0.787832f,0.5495f,-0.278155f},{-0.990759f,-0.0191876f,0.134268f}, +{-0.971217f,0.155734f,-0.180232f},{-0.971098f,-0.0403574f,-0.235244f},{-0.952427f,-0.20938f,-0.221457f}, +{-0.895477f,-0.335529f,-0.292474f},{-0.689745f,-0.654384f,-0.309893f},{-0.697157f,-0.662355f,-0.274332f}, +{-0.991092f,-0.133136f,0.00332425f},{-0.34596f,-0.657323f,0.669506f},{-0.139928f,0.746144f,-0.650914f}, +{-0.88565f,-0.260006f,-0.384736f},{-0.119213f,0.536603f,-0.835372f},{0.0401096f,0.531459f,-0.846134f}, +{0.217478f,0.763288f,-0.608355f},{-0.345093f,0.574183f,-0.742445f},{-0.332209f,0.464239f,-0.821048f}, +{-0.363386f,0.523473f,-0.770666f},{-0.11312f,0.740586f,-0.662372f},{-0.812245f,0.237191f,-0.532915f}, +{-0.692428f,0.460837f,-0.555133f},{-0.745763f,0.212997f,-0.631244f},{-0.592546f,0.508668f,-0.624617f}, +{-0.944421f,-0.251383f,0.21184f},{-0.0282219f,0.728704f,-0.684247f},{-0.89321f,-0.192984f,-0.406121f}, +{-0.918234f,-0.245104f,-0.311078f},{-0.92194f,-0.272905f,-0.274863f},{-0.893661f,-0.357294f,-0.271499f}, +{-0.924488f,-0.268229f,-0.270878f},{-0.921025f,-0.271284f,-0.279497f},{-0.195757f,-0.980249f,-0.0281213f}, +{0.304488f,0.726339f,-0.616214f},{0.371242f,0.605966f,-0.703552f},{0.469584f,0.571727f,-0.672769f}, +{-0.882291f,-0.437208f,0.174387f},{0.18895f,0.750629f,-0.63313f},{0.0412555f,0.674764f,-0.736879f}, +{-0.90528f,-0.318957f,-0.280597f},{-0.888689f,-0.362498f,-0.280763f},{-0.924107f,-0.260881f,-0.279224f}, +{-0.959584f,-0.106115f,-0.260649f},{-0.885816f,-0.0467579f,-0.461675f},{-0.659046f,0.746035f,0.0953459f}, +{-0.674056f,0.737495f,-0.0418221f},{0.367991f,0.483938f,-0.793969f},{-0.817986f,-0.561808f,-0.123579f}, +{-0.991833f,0.0445393f,-0.119513f},{-0.986462f,0.0322381f,-0.160793f},{-0.774363f,-0.547466f,-0.317242f}, +{-0.868264f,-0.453342f,-0.201488f},{-0.26792f,0.738131f,-0.619178f},{-0.284771f,0.799236f,-0.529271f}, +{-0.119769f,0.803515f,-0.583111f},{-0.0182616f,0.559203f,-0.828829f},{-0.0234112f,0.565326f,-0.824535f}, +{-0.0600253f,0.646342f,-0.760683f},{-0.927258f,-0.33292f,0.171338f},{0.0436789f,0.832412f,-0.552433f}, +{0.00637399f,-0.0948247f,-0.995474f},{0.0884458f,0.788312f,-0.608885f},{-0.0589162f,0.60668f,-0.79276f}, +{-0.184244f,0.75806f,-0.625619f},{-0.786197f,0.366074f,-0.49788f},{-0.74957f,0.414396f,-0.51616f}, +{-0.668454f,0.559474f,-0.490059f},{-0.587639f,0.579841f,-0.564327f},{-0.0358703f,0.534826f,-0.844201f}, +{-0.880005f,-0.0687891f,-0.469956f},{-0.915141f,-0.110246f,-0.387766f},{-0.920034f,-0.193473f,-0.340743f}, +{-0.917471f,-0.210934f,-0.337274f},{-0.907767f,-0.289137f,-0.303906f},{-0.925365f,-0.259778f,-0.27607f}, +{-0.908746f,-0.296639f,-0.293576f},{-0.89718f,-0.329847f,-0.293716f},{-0.92929f,-0.237296f,-0.283039f}, +{0.0873267f,-0.833744f,-0.545202f},{0.132631f,-0.275063f,-0.952234f},{0.133723f,0.778328f,-0.613452f}, +{0.112997f,0.888124f,-0.445496f},{-0.996352f,0.0845663f,-0.0114163f},{-0.242757f,0.902481f,-0.355804f}, +{-0.192791f,0.925555f,-0.325852f},{0.0821069f,0.599657f,-0.796034f},{0.0450017f,0.462362f,-0.885549f}, +{0.0456863f,0.445472f,-0.89413f},{0.0488839f,0.541727f,-0.839132f},{0.117665f,0.639629f,-0.759625f}, +{-0.0755639f,0.649307f,-0.756763f},{-0.523536f,-0.790108f,-0.318809f},{-0.802758f,0.404456f,-0.438171f}, +{-0.905192f,-0.304054f,-0.296949f},{-0.882081f,-0.454427f,-0.124212f},{-0.993901f,0.017008f,-0.108954f}, +{-0.937838f,-0.181207f,-0.296014f},{-0.665141f,-0.586076f,-0.462712f},{-0.778656f,-0.544372f,-0.312015f}, +{-0.997034f,0.0503957f,0.0581647f},{-0.451822f,-0.741887f,-0.49544f},{-0.530233f,-0.728497f,-0.433757f}, +{-0.514687f,-0.777484f,-0.361407f},{0.436724f,-0.897585f,0.0601103f},{-0.267713f,0.874596f,-0.404242f}, +{-0.184382f,0.894412f,-0.407468f},{-0.239753f,0.839678f,-0.487299f},{-0.660937f,0.453012f,-0.598283f}, +{-0.668512f,0.361127f,-0.650138f},{-0.674786f,0.061231f,-0.735469f},{-0.93075f,-0.0503379f,-0.362175f}, +{-0.929642f,-0.0906662f,-0.357134f},{-0.64978f,-0.615394f,-0.446178f},{-0.58863f,-0.71698f,-0.373436f}, +{0.00318272f,-0.939555f,0.342383f},{-0.145101f,-0.961075f,0.235116f},{-0.680715f,0.113708f,-0.72367f}, +{-0.887669f,0.397071f,-0.233192f},{-0.954059f,0.0816392f,-0.288281f},{-0.792749f,0.556533f,-0.248635f}, +{-0.679507f,0.672828f,-0.292527f},{-0.45138f,0.853066f,-0.261789f},{-0.603588f,0.761234f,-0.237075f}, +{-0.922933f,0.26974f,-0.274656f},{-0.96959f,-0.0548561f,-0.238506f},{-0.947935f,-0.133122f,-0.289307f}, +{-0.924055f,-0.213494f,-0.317085f},{-0.899426f,-0.251276f,-0.357622f},{-0.900932f,-0.266772f,-0.342278f}, +{-0.194343f,0.850455f,-0.488832f},{-0.0324944f,0.788577f,-0.614077f},{-0.916005f,0.348945f,0.19792f}, +{-0.812545f,-0.531355f,0.239652f},{-0.70015f,0.345013f,-0.625105f},{-0.657777f,-0.143039f,-0.739506f}, +{-0.764772f,0.307474f,-0.566201f},{-0.688961f,-0.668823f,-0.279299f},{-0.87333f,-0.281439f,-0.397601f}, +{-0.429668f,-0.366092f,-0.825447f},{-0.233538f,-0.771659f,-0.59161f},{-0.426967f,0.109738f,-0.897584f}, +{-0.737624f,-0.655111f,-0.163528f},{-0.905149f,-0.408362f,-0.118095f},{-0.999381f,-0.00136333f,-0.0351535f}, +{-0.904803f,-0.318869f,-0.282231f},{-0.946042f,-0.284675f,-0.154804f},{0.0884216f,0.584999f,-0.8062f}, +{0.106585f,0.427367f,-0.897773f},{0.0498672f,0.628808f,-0.77596f},{-0.704076f,0.547598f,-0.452122f}, +{-0.664798f,0.333039f,-0.668677f},{-0.472316f,0.827564f,-0.303406f},{-0.550143f,0.780893f,-0.295885f}, +{-0.722361f,-0.592277f,-0.356934f},{-0.872574f,0.386936f,-0.298154f},{-0.928608f,0.133556f,-0.346195f}, +{-0.792346f,0.542703f,-0.278677f},{-0.72946f,0.638489f,-0.245398f},{-0.870852f,0.385273f,-0.305257f}, +{-0.963596f,-0.0794412f,-0.255286f},{-0.947973f,-0.173342f,-0.267021f},{-0.888068f,-0.328748f,-0.321342f}, +{-0.907185f,-0.261738f,-0.329407f},{0.0476047f,0.889729f,-0.453999f},{0.0592263f,0.829014f,-0.556083f}, +{-0.0314531f,0.840112f,-0.5415f},{0.331963f,-0.0387972f,-0.942494f},{-0.0368371f,-0.256588f,-0.965819f}, +{-0.0167172f,-0.297411f,-0.954603f},{0.185203f,0.0534945f,-0.981243f},{-0.870331f,0.453847f,0.191171f}, +{-0.622077f,-0.0313076f,-0.78233f},{-0.699579f,0.420684f,-0.577594f},{-0.803813f,0.488717f,-0.339176f}, +{0.0329742f,-0.999242f,0.0207125f},{0.164832f,-0.983764f,0.0709828f},{-0.0206047f,0.918457f,-0.394983f}, +{-0.0153557f,0.905575f,-0.423907f},{-0.0935167f,0.715321f,-0.692511f},{0.0511931f,0.851253f,-0.522252f}, +{-0.0160317f,-0.999497f,0.0273526f},{-0.968668f,-0.178708f,-0.17247f},{-0.899931f,-0.411584f,-0.143951f}, +{-0.753191f,-0.543083f,-0.371166f},{-0.921122f,-0.369519f,-0.122436f},{-0.675025f,0.556727f,-0.484146f}, +{-0.922432f,0.21482f,-0.320891f},{-0.946594f,-0.313147f,0.0767992f},{-0.826512f,-0.505872f,0.246925f}, +{-0.101095f,0.874518f,-0.474339f},{0.178633f,0.891713f,-0.415859f},{0.174257f,0.866981f,-0.466882f}, +{-0.0200044f,0.642351f,-0.76615f},{0.0617693f,0.424787f,-0.903184f},{-0.0536696f,0.441809f,-0.895502f}, +{-0.977742f,-0.116558f,0.174454f},{-0.704941f,0.55243f,-0.444837f},{-0.785385f,0.27646f,-0.553842f}, +{-0.797305f,0.473351f,-0.374492f},{-0.842062f,0.352114f,-0.408592f},{-0.818045f,-0.42511f,-0.387406f}, +{0.0565383f,0.364246f,-0.929585f},{0.0346147f,0.516041f,-0.855864f},{0.363058f,0.509097f,-0.78039f}, +{0.440208f,0.416773f,-0.79531f},{0.328956f,0.493552f,-0.805106f},{0.187626f,0.556664f,-0.809273f}, +{0.208331f,0.517454f,-0.829964f},{-0.989202f,-0.121884f,-0.0813819f},{0.499549f,0.158873f,-0.851593f}, +{0.432908f,0.0386735f,-0.900608f},{0.273405f,0.49063f,-0.827364f},{0.0817157f,0.606752f,-0.79068f}, +{0.106964f,0.0401378f,-0.993452f},{0.0945015f,-0.110277f,-0.989398f},{-0.0850992f,0.719827f,-0.688917f}, +{0.417252f,0.52599f,-0.741104f},{0.395665f,0.461008f,-0.794306f},{0.103608f,0.642954f,-0.758865f}, +{0.116833f,0.681212f,-0.722704f},{0.368249f,0.572081f,-0.732882f},{-0.77825f,-0.597763f,-0.192372f}, +{-0.931479f,-0.327635f,-0.15812f},{-0.968138f,-0.239452f,-0.0732868f},{0.0234646f,-0.992831f,0.1172f}, +{0.00620094f,0.863367f,-0.504538f},{-0.74072f,-0.135665f,-0.657973f},{-0.75342f,-0.0540682f,-0.655313f}, +{-0.674416f,-0.0614504f,-0.73579f},{-0.612793f,0.0468009f,-0.788856f},{-0.734032f,0.00265937f,-0.67911f}, +{-0.706752f,0.0267475f,-0.706955f},{-0.526979f,0.0954262f,-0.844504f},{-0.782884f,-0.564665f,-0.261241f}, +{0.679924f,-0.0978195f,-0.726729f},{0.561959f,-0.391283f,-0.728766f},{-0.585405f,0.436328f,-0.683314f}, +{0.0137966f,0.957329f,-0.288671f},{-0.139726f,0.939267f,-0.313454f},{-0.0246292f,0.926615f,-0.375203f}, +{-0.687732f,-0.384911f,-0.615523f},{-0.734801f,-0.248514f,-0.631117f},{-0.789134f,-0.0194749f,-0.613912f}, +{-0.782444f,-0.0322267f,-0.621886f},{-0.73717f,0.00902504f,-0.675647f},{-0.701868f,0.228167f,-0.674774f}, +{-0.0342424f,0.914195f,-0.403826f},{0.0216571f,0.853809f,-0.520136f},{-0.158441f,0.87924f,-0.449259f}, +{-0.640767f,0.226042f,-0.733705f},{-0.751944f,0.506839f,-0.421539f},{-0.363757f,0.83771f,-0.407337f}, +{0.296575f,0.632464f,-0.715564f},{0.26247f,0.617033f,-0.741876f},{0.159429f,0.413905f,-0.89625f}, +{-0.636593f,-0.395732f,-0.661925f},{0.00776096f,0.570112f,-0.82153f},{0.0359855f,0.664895f,-0.746069f}, +{0.0310453f,0.609546f,-0.792142f},{0.288612f,0.808413f,-0.513002f},{0.393032f,0.753305f,-0.52731f}, +{0.325346f,0.75768f,-0.565748f},{-0.974308f,-0.178684f,-0.137097f},{-0.953546f,-0.22885f,-0.195905f}, +{-0.625887f,0.490681f,-0.606216f},{-0.609771f,0.642855f,-0.463592f},{-0.307379f,-0.933886f,-0.182687f}, +{0.303272f,0.817697f,-0.489282f},{-0.0330104f,0.731284f,-0.681274f},{0.0176375f,0.73728f,-0.675357f}, +{-0.799932f,-0.0896961f,-0.593349f},{-0.739251f,-0.0909561f,-0.66726f},{-0.730596f,-0.0295249f,-0.682171f}, +{-0.735792f,0.141394f,-0.662282f},{0.398786f,0.719891f,-0.568091f},{0.175395f,0.891763f,-0.417127f}, +{-0.878951f,-0.343843f,-0.33048f},{0.102957f,-0.628029f,-0.771349f},{-0.00816262f,-0.613874f,-0.789362f}, +{-0.893497f,-0.435569f,0.109282f},{0.0100863f,-0.0486995f,-0.998763f},{0.150525f,0.0452677f,-0.987569f}, +{0.0764546f,0.26098f,-0.962312f},{0.152391f,0.171026f,-0.97341f},{0.345637f,0.689084f,-0.636944f}, +{0.357883f,0.609575f,-0.707346f},{-0.100902f,0.519759f,-0.848333f},{-0.140263f,0.68451f,-0.715383f}, +{-0.10744f,0.788916f,-0.605035f},{0.394548f,0.75452f,-0.524434f},{0.388926f,0.688586f,-0.612034f}, +{-0.738403f,-0.340227f,-0.582242f},{-0.782643f,-0.254177f,-0.568211f},{0.247413f,-0.564007f,-0.787834f}, +{0.256883f,-0.117003f,-0.959334f},{-0.786296f,-0.0782728f,-0.612872f},{-0.707897f,-0.152897f,-0.689568f}, +{-0.723229f,0.022688f,-0.690235f},{-0.749203f,0.184557f,-0.636108f},{-0.701033f,0.199585f,-0.68463f}, +{-0.709307f,0.157231f,-0.687141f},{-0.696854f,0.196286f,-0.689831f},{-0.118736f,0.910259f,-0.396649f}, +{-0.175142f,0.912401f,-0.369931f},{-0.131566f,0.87213f,-0.471254f},{0.378213f,0.661221f,-0.647875f}, +{0.0959242f,0.458718f,-0.883389f},{0.383769f,0.604907f,-0.697716f},{0.349285f,0.490389f,-0.798447f}, +{0.329539f,0.53652f,-0.776885f},{-0.616684f,0.525528f,-0.586107f},{-0.422189f,0.264445f,-0.867079f}, +{-0.298524f,0.112001f,-0.947808f},{-0.306054f,0.243506f,-0.920345f},{-0.685699f,0.495668f,-0.533039f}, +{-0.657282f,0.676852f,-0.331438f},{-0.265994f,0.899443f,-0.346771f},{0.188719f,0.811568f,-0.552939f}, +{0.403312f,0.792154f,-0.458074f},{0.377123f,0.811736f,-0.44594f},{0.302501f,0.527583f,-0.79382f}, +{-0.0959399f,0.939362f,-0.329235f},{-0.772767f,-0.320831f,-0.547631f},{-0.690519f,0.217566f,-0.689818f}, +{-0.661912f,0.307201f,-0.683739f},{0.351612f,0.447101f,-0.822478f},{0.303686f,0.461482f,-0.833552f}, +{-0.147613f,0.832848f,-0.533455f},{0.312312f,0.449462f,-0.836926f},{-0.781602f,-0.335284f,-0.526007f}, +{-0.917526f,-0.0964975f,-0.385791f},{-0.838488f,-0.158383f,-0.521394f},{0.0f,-1.0f,0.0f}, +{-0.886577f,-0.14044f,-0.440747f},{-0.968258f,-0.0498201f,-0.244938f},{-0.655652f,0.450153f,-0.606203f}, +{-0.604448f,0.678246f,-0.41788f},{-0.14275f,-0.918332f,0.369173f},{0.0989465f,0.694789f,-0.712374f}, +{0.166933f,0.837658f,-0.52006f},{0.21055f,0.72595f,-0.654725f},{0.316211f,0.415471f,-0.852874f}, +{-0.547079f,-0.625644f,-0.556123f},{-0.6564f,-0.45908f,-0.598652f},{-0.620557f,-0.50989f,-0.595752f}, +{-0.334171f,0.525758f,-0.782246f},{-0.733579f,-0.147398f,-0.663427f},{-0.792315f,0.0928118f,-0.603012f}, +{-0.690414f,0.293632f,-0.661142f},{0.578691f,-0.376963f,-0.723198f},{-0.599053f,0.374824f,-0.707561f}, +{0.015523f,0.715745f,-0.698189f},{-0.107927f,0.774788f,-0.62294f},{-0.0441408f,0.723233f,-0.689192f}, +{0.407924f,0.684044f,-0.604716f},{-0.194453f,0.780287f,-0.594425f},{-0.114251f,0.723885f,-0.680394f}, +{0.555647f,0.406394f,-0.725328f},{0.518593f,0.360257f,-0.77542f},{0.520778f,0.303692f,-0.797848f}, +{0.240672f,0.416642f,-0.876633f},{0.0327207f,-0.998585f,-0.0419202f},{-0.0431421f,0.587874f,-0.807801f}, +{-0.766352f,0.134119f,-0.628264f},{-0.862351f,-0.0512968f,-0.503705f},{-0.888499f,-0.189282f,-0.418021f}, +{-0.804259f,-0.259012f,-0.534865f},{-0.645443f,-0.635757f,-0.42334f},{-0.698401f,-0.436983f,-0.566817f}, +{-0.765176f,-0.285133f,-0.577239f},{-0.830084f,-0.296906f,-0.472025f},{-0.795711f,-0.359287f,-0.487603f}, +{-0.743921f,-0.239702f,-0.623799f},{-0.780822f,-0.174298f,-0.599948f},{-0.811998f,-0.130463f,-0.568893f}, +{-0.879525f,0.0516754f,-0.473039f},{-0.820533f,0.14866f,-0.55193f},{-0.807281f,0.13951f,-0.573441f}, +{-0.712668f,0.21512f,-0.667704f},{-0.729673f,0.319276f,-0.604683f},{-0.696901f,0.37933f,-0.608636f}, +{-0.656775f,0.415176f,-0.629504f},{-0.691862f,0.261617f,-0.672966f},{-0.568101f,0.174111f,-0.80433f}, +{-0.112366f,0.606799f,-0.786873f},{-0.05266f,0.579328f,-0.813392f},{-0.1848f,0.629204f,-0.754951f}, +{-0.196257f,0.888754f,-0.414246f},{-0.240208f,0.903375f,-0.355264f},{0.544748f,0.342669f,-0.765393f}, +{0.385739f,0.3243f,-0.863733f},{0.457805f,0.216345f,-0.862328f},{0.384613f,0.32892f,-0.862487f}, +{0.0803373f,0.547268f,-0.833093f},{0.0454906f,0.620927f,-0.782547f},{-0.674372f,0.00883527f,-0.738339f}, +{-0.84339f,-0.19135f,-0.502075f},{-0.931514f,-0.058715f,-0.358935f},{-0.758433f,-0.616169f,-0.212403f}, +{-0.600183f,0.119047f,-0.790954f},{-0.365026f,0.727664f,-0.580743f},{-0.775646f,0.368886f,-0.51215f}, +{-0.665811f,0.318583f,-0.674686f},{-0.523593f,0.612358f,-0.592341f},{0.599859f,0.0667783f,-0.797314f}, +{-0.0627656f,0.904092f,-0.422703f},{-0.800314f,-0.47969f,-0.359714f},{-0.804728f,-0.352697f,-0.477513f}, +{-0.797558f,0.251342f,-0.548387f},{0.299336f,-0.933466f,0.197581f},{0.329159f,-0.935342f,0.129572f}, +{-0.219073f,0.903725f,-0.367817f},{-0.212267f,0.916372f,-0.339419f},{-0.131723f,0.689763f,-0.711952f}, +{-0.0554175f,0.330594f,-0.942145f},{0.0320061f,0.18098f,-0.982966f},{-0.280935f,0.879313f,-0.384558f}, +{-0.251187f,0.930229f,-0.267543f},{-0.292435f,0.903886f,-0.312204f},{-0.718851f,0.057869f,-0.692752f}, +{-0.982328f,0.12595f,-0.138447f},{0.786614f,0.464934f,-0.406294f},{-0.700668f,0.366514f,-0.612153f}, +{0.312493f,0.817967f,-0.48299f},{-0.601982f,-0.595711f,-0.531738f},{-0.489698f,-0.677372f,-0.548965f}, +{-0.811554f,-0.468049f,-0.349729f},{-0.761489f,0.347982f,-0.546848f},{-0.731756f,0.145687f,-0.665814f}, +{0.174515f,0.884687f,-0.432289f},{-0.114839f,0.792609f,-0.598818f},{0.112595f,0.652698f,-0.749205f}, +{0.767002f,0.399536f,-0.502075f},{0.15278f,0.810745f,-0.565111f},{-0.702188f,0.107794f,-0.703784f}, +{-0.633727f,0.299399f,-0.713267f},{-0.0571531f,-0.643154f,0.763601f},{-0.427399f,0.823243f,-0.373633f}, +{-0.47606f,0.788375f,-0.389657f},{-0.518836f,0.696978f,-0.495006f},{-0.761843f,0.502152f,-0.409193f}, +{-0.908978f,0.15949f,-0.385125f},{-0.917297f,0.0193351f,-0.397734f},{-0.926845f,-0.050602f,-0.372019f}, +{-0.927027f,-0.202271f,-0.315766f},{-0.590106f,-0.644232f,-0.48656f},{-0.603943f,-0.643506f,-0.470269f}, +{-0.580711f,-0.717758f,-0.384186f},{-0.677655f,-0.7017f,-0.220003f},{-0.804464f,-0.533413f,-0.261358f}, +{-0.864175f,-0.356112f,-0.355508f},{-0.851693f,-0.371734f,-0.369368f},{-0.854062f,-0.407542f,-0.323244f}, +{-0.851561f,-0.394576f,-0.345188f},{-0.877129f,-0.167053f,-0.450265f},{-0.918302f,-0.0351973f,-0.394312f}, +{-0.927236f,-0.0126858f,-0.374262f},{-0.95147f,0.0250336f,-0.306721f},{-0.915624f,0.0651938f,-0.396715f}, +{-0.876806f,0.15456f,-0.455326f},{-0.885586f,0.147487f,-0.440438f},{-0.883614f,0.193033f,-0.426574f}, +{-0.825263f,0.319221f,-0.465875f},{-0.790863f,0.32737f,-0.517074f},{-0.709898f,0.0806033f,-0.699677f}, +{-0.0398124f,0.680137f,-0.732003f},{0.584153f,0.293525f,-0.756708f},{0.618851f,0.395068f,-0.678929f}, +{-0.767229f,0.225208f,-0.600534f},{-0.77519f,0.253299f,-0.578723f},{-0.682595f,0.190452f,-0.705544f}, +{-0.776026f,0.168611f,-0.607745f},{-0.859731f,0.371183f,-0.350837f},{-0.673324f,0.285739f,-0.6819f}, +{-0.727279f,0.537275f,-0.427084f},{-0.735501f,0.339622f,-0.586256f},{-0.716775f,0.674215f,-0.177957f}, +{-0.74042f,0.661547f,-0.118885f},{-0.43986f,0.804958f,-0.398203f},{-0.940599f,0.0319039f,-0.338018f}, +{-0.581107f,-0.717514f,-0.384043f},{-0.606471f,-0.659641f,-0.443923f},{-0.913605f,-0.148399f,-0.378555f}, +{-0.933232f,0.0702974f,-0.352329f},{-0.834932f,0.33923f,-0.433372f},{-0.738723f,0.209806f,-0.640523f}, +{-0.755023f,0.427757f,-0.496955f},{-0.724755f,0.681174f,-0.103601f},{-0.489712f,0.813523f,-0.313628f}, +{-0.868385f,-0.320696f,-0.378234f},{-0.899023f,0.150755f,-0.411134f},{-0.620953f,-0.356866f,-0.6979f}, +{-0.736245f,0.329334f,-0.591171f},{-0.752901f,0.257652f,-0.605604f},{-0.758671f,0.506889f,-0.409246f}, +{-0.670159f,0.564671f,-0.481698f},{-0.41136f,0.798592f,-0.439357f},{-0.712829f,0.660545f,-0.235702f}, +{-0.955273f,0.267109f,-0.126908f},{-0.993407f,0.0231793f,-0.11227f},{-0.933768f,-0.285835f,-0.215351f}, +{-0.522153f,-0.80213f,-0.28973f},{-0.591668f,-0.703124f,-0.394392f},{-0.686102f,-0.632814f,-0.358901f}, +{-0.674643f,-0.686631f,-0.270916f},{-0.751492f,-0.557023f,-0.353533f},{-0.851592f,-0.408749f,-0.328199f}, +{-0.874671f,-0.330786f,-0.354304f},{-0.889016f,-0.355231f,-0.288896f},{-0.808405f,-0.536273f,-0.242677f}, +{-0.865771f,-0.41462f,-0.280233f},{-0.919658f,-0.178283f,-0.349921f},{-0.929822f,0.0642691f,-0.362355f}, +{-0.955185f,0.0169424f,-0.295525f},{-0.930455f,0.000111387f,-0.366407f},{-0.91058f,0.167131f,-0.378036f}, +{-0.920372f,0.185707f,-0.344133f},{-0.899459f,0.2174f,-0.379093f},{-0.884971f,0.276707f,-0.374512f}, +{-0.864626f,0.258211f,-0.430985f},{-0.798431f,0.0357707f,-0.601023f},{-0.706052f,-0.322651f,-0.630387f}, +{-0.645647f,-0.42174f,-0.636612f},{-0.686848f,-0.423865f,-0.590405f},{-0.762974f,-0.311095f,-0.566648f}, +{-0.881004f,-0.0543548f,-0.469976f},{-0.878294f,0.0736529f,-0.472414f},{-0.855078f,0.167816f,-0.49059f}, +{-0.795931f,0.234467f,-0.558139f},{-0.731186f,0.272107f,-0.62556f},{-0.691631f,0.234792f,-0.683022f}, +{-0.652312f,0.437466f,-0.618961f},{-0.736543f,0.354978f,-0.575756f},{-0.841663f,0.118126f,-0.526925f}, +{-0.821913f,0.158685f,-0.547063f},{-0.766839f,0.226806f,-0.60043f},{-0.700081f,0.449037f,-0.555205f}, +{-0.633108f,0.633201f,-0.445232f},{-0.701542f,0.248827f,-0.667775f},{-0.756746f,0.56066f,-0.336147f}, +{-0.629406f,0.77327f,0.0768261f},{-0.652054f,-0.665617f,-0.363016f},{-0.850827f,-0.17411f,-0.495762f}, +{-0.92283f,0.0165544f,-0.384853f},{-0.92248f,0.063835f,-0.380731f},{-0.900127f,0.114247f,-0.420379f}, +{-0.782641f,0.243676f,-0.572796f},{-0.77963f,0.230663f,-0.582213f},{-0.845516f,0.11178f,-0.522119f}, +{-0.709714f,0.466652f,-0.527772f},{-0.400168f,0.870759f,-0.285735f},{-0.292425f,0.156734f,-0.943357f}, +{-0.735463f,-0.631546f,-0.245446f},{-0.797337f,-0.491385f,-0.350419f},{-0.908647f,0.250044f,-0.334423f}, +{-0.671733f,-0.467832f,-0.574377f},{-0.707944f,0.390645f,-0.588398f},{-0.807272f,0.194343f,-0.557264f}, +{-0.610848f,-0.732514f,-0.30048f},{-0.674118f,-0.457605f,-0.579795f},{0.451244f,0.0484673f,-0.891083f}, +{0.473483f,0.0961672f,-0.875538f},{-0.641274f,-0.741973f,-0.195559f},{-0.496208f,0.338717f,-0.799405f}, +{-0.461432f,0.358428f,-0.811548f},{0.566993f,0.562716f,-0.601556f},{-0.016211f,0.337597f,-0.941151f}, +{0.0142408f,0.296582f,-0.954901f},{0.152933f,0.162301f,-0.974818f},{0.460267f,0.760721f,-0.457665f}, +{0.266763f,-0.4287f,0.863165f},{-0.229672f,0.792f,-0.565674f},{-0.183382f,0.922747f,-0.338982f}, +{-0.0423757f,0.683786f,-0.728451f},{-0.078403f,0.92181f,-0.379631f},{-0.175821f,0.91269f,-0.368895f}, +{0.0319953f,0.763247f,-0.645315f},{0.702159f,0.515647f,-0.491f},{0.773998f,0.507949f,-0.378041f}, +{0.700035f,0.505806f,-0.504094f},{0.488668f,0.329755f,-0.807754f},{0.494024f,0.59918f,-0.630019f}, +{0.553235f,0.493178f,-0.671347f},{0.416349f,0.563334f,-0.713658f},{0.631496f,0.041896f,-0.774246f}, +{-0.164784f,0.487377f,-0.857502f},{-0.0916461f,0.350752f,-0.931973f},{-0.123292f,0.333143f,-0.93478f}, +{0.432491f,0.372184f,-0.821237f},{0.371927f,0.337693f,-0.864658f},{0.366353f,0.303488f,-0.879591f}, +{0.301134f,0.453315f,-0.838942f},{0.338526f,0.404715f,-0.849474f},{0.335097f,0.376204f,-0.863818f}, +{0.418205f,0.224577f,-0.880153f},{0.301306f,0.574076f,-0.761349f},{0.242002f,0.593726f,-0.767414f}, +{0.282472f,0.61884f,-0.732971f},{0.0259057f,0.734429f,-0.678191f},{0.289283f,0.717467f,-0.633684f}, +{0.000842052f,0.572893f,-0.819629f},{0.611392f,0.270866f,-0.743527f},{0.38074f,0.252503f,-0.889539f}, +{0.465128f,0.413081f,-0.782956f},{0.171625f,0.243028f,-0.954716f},{0.125825f,0.386253f,-0.91377f}, +{0.0129325f,0.398473f,-0.917089f},{0.0361355f,0.47008f,-0.881884f},{0.341164f,-0.747721f,0.569667f}, +{0.200665f,-0.688634f,0.69679f},{0.235842f,0.431982f,-0.8705f},{0.636529f,0.504031f,-0.583766f}, +{0.608404f,0.487187f,-0.626493f},{0.228318f,0.295752f,-0.927578f},{-0.363634f,0.203676f,-0.909003f}, +{0.588486f,0.667092f,-0.456807f},{0.581279f,0.615243f,-0.532532f},{-0.0425343f,0.776099f,-0.629175f}, +{-0.033611f,0.737196f,-0.674843f},{0.451443f,0.142728f,-0.880811f},{0.0148181f,-0.998955f,-0.0432437f}, +{0.430652f,0.505955f,-0.747361f},{0.663306f,0.015291f,-0.748192f},{0.381259f,0.835846f,-0.394973f}, +{0.319437f,0.838482f,-0.441485f},{0.478502f,0.508641f,-0.715765f},{0.343964f,0.541458f,-0.767145f}, +{0.0565948f,-0.83426f,-0.548459f},{-0.2399f,0.915786f,-0.322155f},{-0.17528f,0.938477f,-0.297554f}, +{-0.345293f,0.876527f,-0.335369f},{0.155107f,-0.700209f,0.696886f},{0.518676f,0.656536f,-0.547664f}, +{0.540738f,0.696976f,-0.470985f},{-0.530286f,0.670052f,-0.519449f},{0.173892f,0.450541f,-0.875657f}, +{0.264071f,0.476953f,-0.838321f},{0.242757f,0.218363f,-0.945191f},{0.166356f,-0.768676f,0.617627f}, +{-0.113909f,0.326739f,-0.938225f},{0.484652f,0.733872f,-0.475967f},{0.25989f,0.909259f,-0.325124f}, +{-0.13513f,0.367255f,-0.920252f},{-0.0836041f,0.28648f,-0.954431f},{0.694915f,0.407838f,-0.592251f}, +{0.755633f,0.298784f,-0.582878f},{0.183817f,0.688876f,-0.701185f},{0.329009f,0.56222f,-0.758724f}, +{0.450194f,0.417842f,-0.789135f},{0.426558f,0.624877f,-0.653894f},{0.474707f,0.334435f,-0.814129f}, +{-0.368151f,0.702815f,-0.6087f},{0.441703f,0.751622f,-0.48986f},{0.324125f,0.385029f,-0.864116f}, +{0.553038f,0.362224f,-0.750296f},{0.592331f,0.191065f,-0.782712f},{0.545859f,0.282084f,-0.788965f}, +{0.643405f,0.153596f,-0.749959f},{0.603579f,0.440697f,-0.664439f},{0.561321f,0.45505f,-0.691266f}, +{0.274957f,0.426057f,-0.861901f},{0.51976f,0.49844f,-0.693835f},{0.492771f,0.292664f,-0.819466f}, +{0.475031f,0.455529f,-0.752887f},{0.447749f,0.310442f,-0.838538f},{0.60608f,0.361225f,-0.708649f}, +{0.671187f,0.621191f,-0.404513f},{0.499191f,0.431184f,-0.751591f},{0.496577f,0.735805f,-0.460437f}, +{-0.182961f,0.799778f,-0.571734f},{-0.148508f,0.75117f,-0.643187f},{-0.125094f,0.69274f,-0.710255f}, +{-0.0456011f,0.630923f,-0.774504f},{0.515427f,0.473627f,-0.714151f},{0.325487f,0.479803f,-0.814768f}, +{0.0854529f,0.600774f,-0.794838f},{0.397275f,0.715379f,-0.574809f},{0.723071f,0.581009f,-0.373626f}, +{0.538154f,0.430644f,-0.724524f},{-0.318307f,0.836747f,-0.445572f},{0.309693f,0.443584f,-0.841025f}, +{0.609157f,0.175619f,-0.77336f},{0.0230232f,0.685541f,-0.72767f},{0.574796f,0.145307f,-0.805292f}, +{0.0529214f,0.706485f,-0.705747f},{0.462671f,0.340719f,-0.818441f},{0.292814f,0.517741f,-0.803868f}, +{-0.23533f,0.822868f,-0.517212f},{0.108122f,0.103404f,-0.988745f},{0.707184f,0.394909f,-0.586463f}, +{0.529927f,0.343312f,-0.775445f},{0.0648392f,0.783754f,-0.617677f},{0.02647f,0.524744f,-0.850848f}, +{-0.045521f,0.7608f,-0.647388f},{0.118751f,0.88849f,-0.443264f},{-0.0161997f,0.457597f,-0.889012f}, +{0.000280184f,0.398345f,-0.917235f},{0.0561452f,0.767709f,-0.638334f},{-0.624146f,-0.0435425f,-0.780093f}, +{0.231384f,-0.0713779f,-0.970241f},{0.403915f,0.224348f,-0.88686f},{0.210782f,0.871545f,-0.442697f}, +{0.239799f,0.817561f,-0.523536f},{0.449956f,0.539841f,-0.711415f},{0.777722f,0.411413f,-0.475276f}, +{-0.0354915f,0.335593f,-0.941338f},{-0.568895f,-0.812661f,-0.126259f},{0.191192f,0.806213f,-0.55988f}, +{-0.0525749f,0.804373f,-0.591794f},{-0.00818761f,0.759069f,-0.650959f},{0.0183587f,-0.996702f,0.0790466f}, +{0.798654f,0.469085f,-0.376976f},{0.00703419f,0.455561f,-0.890177f},{-0.379388f,0.711838f,-0.591059f}, +{-0.356592f,0.682883f,-0.637584f},{0.159572f,0.698134f,-0.697958f},{-0.0127765f,0.446995f,-0.894445f}, +{0.429407f,0.375184f,-0.82149f},{0.375785f,0.710967f,-0.5944f},{-0.353617f,0.82554f,-0.439818f}, +{-0.29696f,0.869412f,-0.39489f},{-0.206536f,0.683309f,-0.700309f},{0.0821383f,0.485265f,-0.870501f}, +{0.0984004f,0.402228f,-0.910236f},{0.516561f,0.229612f,-0.82489f},{0.550054f,0.0979327f,-0.829367f}, +{0.385639f,-0.695462f,0.606312f},{-0.366341f,0.482633f,-0.795525f},{-0.402403f,0.371983f,-0.836481f}, +{0.155905f,0.37037f,-0.915707f},{0.349217f,0.269209f,-0.897538f},{0.0164691f,-0.998683f,0.0485895f}, +{0.598748f,0.120536f,-0.791815f},{0.610598f,0.110001f,-0.784264f},{-0.514242f,0.289975f,-0.807137f}, +{-0.0559095f,0.147385f,-0.987498f},{-0.318715f,0.0545386f,-0.94628f},{0.504873f,0.254761f,-0.824742f}, +{0.208381f,0.0932082f,-0.973596f},{0.387806f,0.408758f,-0.82615f},{0.433037f,0.482513f,-0.761354f}, +{0.290996f,0.751733f,-0.591793f},{0.447236f,0.828435f,-0.337157f},{0.00941225f,0.76729f,-0.641231f}, +{-0.394672f,0.547728f,-0.737718f},{0.0124283f,0.71336f,-0.700687f},{-0.00554958f,0.640843f,-0.767652f}, +{-0.0250379f,0.60922f,-0.792606f},{-0.355539f,0.551191f,-0.754838f},{-0.0722814f,0.578202f,-0.812685f}, +{0.567135f,0.265433f,-0.779682f},{0.0651969f,0.379966f,-0.9227f},{-0.690634f,-0.632789f,-0.350148f}, +{-0.588416f,-0.793163f,-0.157031f},{-0.61951f,-0.714354f,-0.325432f},{0.26334f,0.909731f,-0.321001f}, +{0.162899f,0.168777f,-0.9721f},{-0.250455f,-0.579707f,-0.775379f},{0.0868959f,0.251874f,-0.963851f}, +{-0.00789398f,0.248566f,-0.968583f},{0.169066f,-0.886262f,-0.431227f},{0.230861f,0.171183f,-0.95781f}, +{-0.326545f,0.302039f,-0.895623f},{0.390293f,0.218435f,-0.894403f},{0.46466f,0.36971f,-0.804615f}, +{0.395036f,0.206159f,-0.895234f},{0.355156f,0.127125f,-0.926123f},{0.426136f,0.16671f,-0.889166f}, +{0.227801f,-0.666285f,0.71005f},{-0.143781f,0.574938f,-0.805465f},{-0.17141f,0.442747f,-0.88011f}, +{-0.267554f,0.646156f,-0.714771f},{-0.303341f,0.558085f,-0.772351f},{-0.291798f,0.614407f,-0.733047f}, +{-0.273007f,0.547826f,-0.790793f},{0.492345f,0.234013f,-0.838352f},{-0.077035f,0.628769f,-0.773767f}, +{-0.218071f,0.684619f,-0.695515f},{-0.244835f,0.698392f,-0.672535f},{-0.22162f,0.646343f,-0.730154f}, +{-0.214732f,0.54545f,-0.810169f},{-0.268715f,0.470151f,-0.840684f},{0.0603743f,-0.691798f,-0.719563f}, +{0.606051f,0.3876f,-0.694599f},{0.265445f,0.352554f,-0.897354f},{-0.153875f,0.507607f,-0.847737f}, +{-0.0218791f,0.2504f,-0.967895f},{0.551176f,0.495749f,-0.671147f},{0.611981f,0.287921f,-0.736601f}, +{0.256431f,0.513728f,-0.818735f},{0.0377112f,-0.996204f,0.078455f},{0.303029f,0.247999f,-0.920147f}, +{0.111975f,0.673271f,-0.730868f},{0.171041f,0.507994f,-0.844208f},{0.212217f,0.634694f,-0.743053f}, +{0.064679f,0.308196f,-0.949122f},{0.137869f,0.696416f,-0.704271f},{-0.132098f,0.735681f,-0.664322f}, +{-0.265268f,0.687997f,-0.675495f},{-0.177326f,0.583504f,-0.792514f},{-0.159021f,0.5532f,-0.81773f}, +{-0.0648649f,0.494838f,-0.866561f},{0.742841f,0.289633f,-0.603574f},{-0.244684f,0.734364f,-0.633119f}, +{-0.230733f,0.626359f,-0.744605f},{-0.0475614f,0.476977f,-0.877628f},{0.806145f,0.328443f,-0.492195f}, +{0.553227f,0.516329f,-0.653716f},{-0.157719f,0.757436f,-0.633573f},{-0.115361f,0.701471f,-0.7033f}, +{-0.121287f,0.647932f,-0.751979f},{-0.119817f,0.518635f,-0.846559f},{0.686161f,0.434175f,-0.583674f}, +{0.414612f,0.725276f,-0.549611f},{0.0854752f,0.862823f,-0.498227f},{-0.12717f,0.823634f,-0.55268f}, +{-0.039744f,0.417788f,-0.907675f},{0.058142f,0.464484f,-0.883671f},{0.331886f,0.247708f,-0.910216f}, +{0.385355f,0.193432f,-0.902267f},{-0.0793643f,0.273121f,-0.9587f},{0.0536832f,0.505359f,-0.861238f}, +{-0.615467f,0.0831187f,-0.783768f},{0.313339f,0.713027f,-0.627226f},{0.689499f,0.582774f,-0.430075f}, +{-0.128185f,0.762866f,-0.633723f},{0.0196996f,0.887205f,-0.460954f},{-0.00598953f,0.836441f,-0.548024f}, +{-0.0213968f,0.757067f,-0.652987f},{-0.00143753f,0.690355f,-0.72347f},{-0.0019361f,0.639495f,-0.768793f}, +{-0.00192568f,0.582949f,-0.812506f},{0.0273509f,0.473471f,-0.880385f},{-0.02277f,0.363068f,-0.931484f}, +{0.0540861f,-0.998258f,0.0235792f},{0.0645095f,0.441878f,-0.894753f},{0.0700004f,0.944914f,-0.319746f}, +{0.0117661f,0.405937f,-0.913825f},{0.0176947f,0.515517f,-0.856697f},{0.680651f,0.415756f,-0.60321f}, +{0.130425f,-0.961724f,0.240989f},{0.0967715f,-0.959725f,0.263748f},{0.0194044f,-0.999714f,-0.0139699f}, +{0.133409f,0.926465f,-0.351945f},{0.117102f,0.858963f,-0.498468f},{0.103475f,0.763646f,-0.637289f}, +{0.111402f,0.690044f,-0.715143f},{0.101804f,0.595361f,-0.796982f},{0.128685f,0.522187f,-0.843067f}, +{0.0402739f,0.41697f,-0.908027f},{0.399144f,-0.91055f,-0.107621f},{-0.435934f,-0.825475f,-0.358542f}, +{-0.109148f,0.471589f,-0.875037f},{-0.0636207f,0.541546f,-0.83826f},{0.16092f,0.918152f,-0.362079f}, +{0.132701f,0.495073f,-0.858657f},{-0.45753f,0.383971f,-0.802018f},{0.599068f,0.532026f,-0.598386f}, +{0.18527f,0.936653f,-0.297248f},{0.206933f,0.904728f,-0.372352f},{0.249515f,0.825249f,-0.506662f}, +{0.271295f,0.741368f,-0.613818f},{0.310646f,0.614839f,-0.724894f},{0.241053f,0.565042f,-0.789063f}, +{0.241853f,0.535239f,-0.809337f},{0.176168f,0.543811f,-0.820509f},{0.0455045f,0.57979f,-0.813494f}, +{-0.0771866f,0.507401f,-0.858246f},{0.440673f,-0.660931f,-0.607436f},{0.500526f,0.292855f,-0.814684f}, +{0.0414138f,-0.998864f,0.0235917f},{-0.0509805f,-0.997048f,0.0574192f},{-0.415864f,0.575909f,-0.703836f}, +{0.338532f,0.763192f,-0.550395f},{0.35893f,0.71351f,-0.601724f},{0.385059f,0.664926f,-0.640003f}, +{0.328951f,0.645277f,-0.689499f},{0.221126f,0.632886f,-0.741996f},{0.108873f,0.668475f,-0.735723f}, +{0.0340051f,0.683036f,-0.729593f},{-0.00556542f,0.544468f,-0.838763f},{-0.00862487f,-0.999916f,-0.00968689f}, +{-0.522209f,-0.433801f,-0.734244f},{-0.171697f,-0.631512f,-0.756117f},{0.133674f,0.312652f,-0.940415f}, +{-0.497572f,0.211301f,-0.841293f},{-0.362949f,0.167034f,-0.916716f},{0.0326084f,-0.00788205f,-0.999437f}, +{0.0178542f,-0.0277267f,-0.999456f},{-0.47475f,0.365741f,-0.800529f},{-0.473612f,0.341419f,-0.811865f}, +{-0.461667f,0.388046f,-0.797674f},{-0.424027f,0.360027f,-0.831012f},{-0.432038f,0.35578f,-0.828712f}, +{-0.431902f,0.351066f,-0.830791f},{-0.456517f,0.110316f,-0.882849f},{-0.361814f,0.114788f,-0.925156f}, +{-0.228224f,-0.316424f,-0.920755f},{-0.148414f,-0.339823f,-0.928706f},{-0.261238f,-0.215537f,-0.940903f}, +{-0.328999f,-0.10573f,-0.938393f},{-0.451714f,0.350036f,-0.820627f},{-0.347968f,0.0215898f,-0.937258f}, +{-0.309485f,-0.0149301f,-0.950787f},{0.463137f,0.067709f,-0.883696f},{-0.241012f,-0.400193f,-0.884171f}, +{-0.0357501f,-0.449568f,-0.892531f},{-0.0140368f,-0.988301f,-0.151869f},{0.0435599f,-0.988519f,-0.144679f}, +{-0.534633f,0.0470525f,-0.843773f},{-0.478002f,-0.0821379f,-0.87451f},{-0.401819f,0.0454912f,-0.914588f}, +{-0.357866f,-0.0533086f,-0.93225f},{-0.451596f,0.267947f,-0.851038f},{-0.360766f,0.261536f,-0.895236f}, +{0.410366f,-0.166006f,-0.896684f},{-0.513599f,0.162015f,-0.842596f},{0.3566f,-0.711176f,0.605859f}, +{-0.120089f,0.322861f,-0.938797f},{-0.248431f,0.441079f,-0.862399f},{-0.175283f,0.367227f,-0.913466f}, +{-0.450494f,0.0551368f,-0.891075f},{-0.42014f,0.147097f,-0.895458f},{-0.557979f,0.0600499f,-0.82768f}, +{0.366826f,-0.521694f,-0.770243f},{0.365232f,-0.318742f,-0.874648f},{0.363532f,-0.145989f,-0.920072f}, +{0.20774f,0.0420311f,-0.977281f},{0.0751624f,0.217239f,-0.97322f},{-0.321394f,-0.326397f,-0.888915f}, +{-0.393182f,-0.385769f,-0.83462f},{-0.392275f,-0.212391f,-0.894992f},{-0.406007f,-0.534047f,-0.741588f}, +{0.315182f,-0.194353f,-0.928917f},{-0.0685202f,-0.992297f,-0.103202f},{-0.0284892f,0.181927f,-0.982899f}, +{-0.53002f,-0.387743f,-0.754145f},{0.357128f,-0.656742f,-0.664191f},{0.276723f,-0.451058f,-0.848511f}, +{0.175629f,-0.170525f,-0.969575f},{0.0425135f,0.164236f,-0.985505f},{-0.0143207f,0.317665f,-0.948095f}, +{0.0122568f,0.405787f,-0.913885f},{-0.47831f,-0.644036f,-0.597024f},{-0.475634f,-0.613191f,-0.63069f}, +{-0.348139f,-0.163224f,-0.923124f},{-0.411986f,-0.109838f,-0.904546f},{-0.581602f,-0.567347f,-0.582972f}, +{-0.517116f,-0.623338f,-0.586551f},{-0.105834f,-0.0441267f,-0.993404f},{0.0528876f,-0.29564f,-0.953834f}, +{-0.50201f,-0.715353f,-0.486061f},{-0.461784f,-0.701117f,-0.543314f},{-0.626786f,0.0544235f,-0.777288f}, +{-0.752071f,0.129508f,-0.646232f},{-0.263653f,-0.0708322f,-0.962014f},{0.438893f,-0.660336f,-0.609368f}, +{0.217609f,-0.414783f,-0.883516f},{0.147652f,-0.530349f,-0.834822f},{-0.338162f,0.160716f,-0.927263f}, +{0.307363f,0.120576f,-0.943922f},{0.0490676f,0.223205f,-0.973536f},{-0.403132f,0.0502257f,-0.913763f}, +{-0.404395f,0.167715f,-0.899075f},{-0.273011f,0.356315f,-0.893591f},{0.00575582f,0.31997f,-0.94741f}, +{-0.154191f,0.0769798f,-0.985038f},{-0.0460386f,-0.00195168f,-0.998938f},{-0.417116f,-0.198723f,-0.886862f}, +{-0.529885f,-0.00125872f,-0.848068f},{-0.196126f,-0.974976f,0.104673f},{0.287061f,-0.528227f,-0.799107f}, +{0.162375f,-0.468503f,-0.868412f},{0.11783f,-0.339373f,-0.933243f},{0.00841994f,-0.0591842f,-0.998212f}, +{0.0947982f,0.174364f,-0.980107f},{0.351317f,0.0632065f,-0.93412f},{0.024536f,0.157757f,-0.987173f}, +{0.10294f,-0.457501f,-0.88323f},{0.122128f,-0.417088f,-0.900623f},{0.102582f,-0.465494f,-0.879086f}, +{0.167471f,-0.388124f,-0.906263f},{-0.0613336f,0.143051f,-0.987813f},{-0.29684f,0.289434f,-0.910008f}, +{-0.152998f,-0.0536475f,-0.986769f},{-0.519057f,-0.225463f,-0.824467f},{-0.145503f,0.163285f,-0.97579f}, +{-0.499829f,-0.705746f,-0.50209f},{-0.0914336f,-0.843831f,0.528762f},{0.0807299f,-0.328036f,-0.94121f}, +{-0.806231f,0.368991f,-0.462426f},{0.330734f,-0.580551f,-0.744027f},{0.583488f,0.141328f,-0.79973f}, +{-0.320216f,0.320748f,-0.891394f},{-0.412065f,0.356592f,-0.838478f},{-0.491405f,0.167616f,-0.854649f}, +{0.199711f,-0.705233f,-0.680266f},{0.146874f,-0.613057f,-0.776267f},{0.227035f,-0.450278f,-0.863542f}, +{0.122738f,-0.209788f,-0.970012f},{0.112813f,-0.0401108f,-0.992806f},{-0.203394f,-0.724243f,-0.658865f}, +{0.0196344f,-0.138169f,-0.990214f},{0.0655215f,-0.302574f,-0.950871f},{-0.0604679f,-0.00683363f,-0.998147f}, +{-0.221203f,-0.308623f,-0.925106f},{-0.226549f,-0.188684f,-0.955549f},{-0.433699f,0.367571f,-0.822677f}, +{0.191396f,-0.331647f,-0.923784f},{0.172897f,-0.271091f,-0.946898f},{-0.214423f,0.265942f,-0.939839f}, +{-0.172306f,0.202648f,-0.963973f},{-0.235133f,0.261565f,-0.936107f},{-0.159272f,0.192711f,-0.968243f}, +{-0.366161f,0.368952f,-0.854284f},{-0.416712f,0.28621f,-0.862806f},{-0.322311f,0.235694f,-0.916823f}, +{-0.108791f,0.0573097f,-0.992411f},{-0.23577f,-0.0337679f,-0.971222f},{-0.106879f,-0.064881f,-0.992153f}, +{-0.02569f,-0.335202f,-0.941796f},{-0.577113f,0.0728067f,-0.813412f},{-0.46981f,-0.468811f,-0.747994f}, +{-0.0801707f,-0.0791455f,-0.993634f},{-0.0991288f,-0.0698804f,-0.992618f},{0.166914f,-0.585763f,-0.793109f}, +{0.124322f,-0.458685f,-0.879859f},{0.0818947f,-0.254017f,-0.963726f},{0.203168f,-0.284487f,-0.936905f}, +{-0.378567f,0.271463f,-0.88487f},{-0.415195f,-0.628034f,-0.658169f},{-0.332351f,-0.620089f,-0.710656f}, +{-0.254031f,-0.712061f,-0.654551f},{-0.376504f,-0.711748f,-0.593009f},{-0.37075f,-0.39528f,-0.840416f}, +{0.0194336f,-0.0314723f,-0.999316f},{-0.301979f,-0.494561f,-0.814996f},{-0.294411f,-0.766308f,-0.571046f}, +{-0.106275f,0.131253f,-0.985636f},{0.0212621f,0.103805f,-0.99437f},{-0.0399005f,0.257478f,-0.96546f}, +{-0.113825f,0.251492f,-0.961143f},{-0.176062f,-0.583995f,-0.792434f},{-0.427957f,0.302497f,-0.851674f}, +{-0.289149f,-0.76017f,-0.581838f},{-0.366384f,-0.718341f,-0.591396f},{-0.546571f,-0.25365f,-0.798074f}, +{-0.39526f,-0.289349f,-0.871807f},{-0.403287f,-0.227755f,-0.886277f},{-0.489823f,-0.266145f,-0.830205f}, +{0.480623f,0.0180865f,-0.876741f},{0.00374673f,0.181041f,-0.983468f},{-0.38202f,-0.586857f,-0.713905f}, +{0.158242f,-0.536317f,-0.829049f},{0.0826999f,-0.411103f,-0.90783f},{0.233891f,-0.411181f,-0.881037f}, +{-0.254003f,-0.718621f,-0.647353f},{-0.0677313f,-0.248287f,-0.966316f},{-0.355975f,-0.0219649f,-0.934237f}, +{-0.244444f,-0.773455f,-0.58482f},{0.0347758f,0.0843179f,-0.995832f},{-0.0978183f,-0.384104f,-0.918094f}, +{0.0148392f,-0.349137f,-0.936954f},{-0.234185f,-0.31982f,-0.918081f},{-0.296005f,-0.312823f,-0.902509f}, +{-0.149664f,-0.762597f,-0.629322f},{0.298053f,-0.548791f,-0.78102f},{0.0532691f,0.187605f,-0.980799f}, +{-0.033569f,-0.249132f,-0.967888f},{-0.389285f,-0.307134f,-0.868404f},{-0.314508f,-0.334821f,-0.888245f}, +{-0.262679f,-0.400729f,-0.877733f},{-0.211967f,-0.335246f,-0.917976f},{-0.0722862f,-0.746754f,-0.661161f}, +{-0.209982f,-0.10056f,-0.97252f},{-0.61991f,-0.237285f,-0.747935f},{-0.486362f,-0.715322f,-0.501763f}, +{0.591243f,0.0442269f,-0.80528f},{-0.1822f,-0.425288f,-0.886529f},{-0.12628f,-0.280556f,-0.951494f}, +{-0.293787f,-0.25386f,-0.921544f},{-0.45819f,-0.694794f,-0.554367f},{-0.265962f,-0.2459f,-0.932093f}, +{0.552218f,-0.296904f,-0.77904f},{0.616997f,-0.208742f,-0.758776f},{0.532225f,-0.0320237f,-0.845997f}, +{0.518351f,0.0671195f,-0.85253f},{0.0783362f,-0.99421f,0.0735581f},{0.118141f,-0.991686f,0.051016f}, +{-0.162888f,-0.271886f,-0.948444f},{-0.293131f,0.331585f,-0.896731f},{0.598523f,-0.060331f,-0.798831f}, +{-0.309627f,-0.348615f,-0.884646f},{0.466335f,-0.598079f,-0.651792f},{0.663968f,-0.130472f,-0.73629f}, +{-0.426057f,-0.246834f,-0.870373f},{-0.320111f,-0.413622f,-0.852318f},{-0.275072f,-0.289065f,-0.916939f}, +{-0.121374f,-0.00139793f,-0.992606f},{-0.104278f,0.0295214f,-0.99411f},{-0.441475f,0.425385f,-0.79003f}, +{-0.354671f,-0.736701f,0.575743f},{-0.307603f,-0.735628f,0.603517f},{-0.14795f,-0.963575f,0.222786f}, +{-0.247067f,-0.209033f,-0.946184f},{0.478306f,-0.542814f,-0.690346f},{0.573596f,-0.413039f,-0.70738f}, +{0.0413827f,0.111407f,-0.992913f},{0.697966f,-0.174554f,-0.694531f},{0.0884714f,-0.995767f,-0.0249182f}, +{0.66496f,-0.0925447f,-0.741123f},{0.530716f,0.0144358f,-0.847427f},{0.424264f,0.0554731f,-0.903838f}, +{0.43501f,0.059857f,-0.898434f},{-0.0618874f,0.058737f,-0.996353f},{-0.153908f,0.138131f,-0.978382f}, +{-0.433835f,-0.75467f,-0.492201f},{0.311733f,0.130743f,-0.941132f},{0.430592f,-0.545578f,-0.718982f}, +{0.563177f,-0.165806f,-0.809531f},{0.528658f,-0.0254374f,-0.848454f},{0.463703f,0.116946f,-0.878239f}, +{0.408836f,0.0773136f,-0.909327f},{0.432356f,-0.362167f,-0.825774f},{-0.0226638f,-0.365996f,-0.93034f}, +{-0.0502062f,-0.277693f,-0.959357f},{-0.0211832f,-0.252399f,-0.967391f},{-0.500045f,-0.0644449f,-0.863598f}, +{-0.551538f,-0.0144755f,-0.834024f},{-0.34341f,-0.0575228f,-0.937423f},{-0.312247f,-0.0818833f,-0.946465f}, +{-0.182016f,-0.0683459f,-0.980918f},{-0.613054f,-0.35332f,-0.706633f},{-0.169602f,0.137656f,-0.975851f}, +{0.501381f,-0.187936f,-0.844569f},{0.410831f,0.034876f,-0.911044f},{0.556872f,-0.242513f,-0.794406f}, +{0.430962f,0.0348824f,-0.901696f},{0.466346f,0.0318584f,-0.884029f},{0.476106f,0.0373906f,-0.878593f}, +{-0.384256f,-0.333572f,0.860858f},{0.409459f,-0.402863f,-0.818563f},{0.347749f,-0.57205f,-0.742852f}, +{0.3489f,-0.310127f,-0.884359f},{0.330387f,0.0285511f,-0.943413f},{0.386763f,0.0233366f,-0.921884f}, +{0.434189f,-0.216166f,-0.874501f},{0.403344f,-0.155649f,-0.901713f},{0.397898f,-0.0907694f,-0.912928f}, +{0.444065f,0.110133f,-0.8892f},{0.436053f,0.102098f,-0.89411f},{0.450432f,0.0181053f,-0.892627f}, +{0.459103f,0.0236729f,-0.888068f},{-0.400662f,-0.908358f,-0.119813f},{-0.25029f,0.0413912f,-0.967286f}, +{-0.285704f,0.0674397f,-0.955942f},{-0.250687f,0.0544366f,-0.966537f},{0.414188f,-0.541434f,-0.731641f}, +{0.263469f,-0.554858f,-0.789124f},{0.478863f,-0.182555f,-0.858699f},{0.499925f,-0.229435f,-0.835126f}, +{0.44019f,-0.441255f,-0.782001f},{0.273243f,-0.564661f,-0.778778f},{0.308691f,-0.458051f,-0.833606f}, +{0.258205f,-0.263016f,-0.929598f},{0.266399f,-0.0140867f,-0.96376f},{0.325926f,-0.0461329f,-0.944269f}, +{0.350478f,-0.219007f,-0.910605f},{0.354576f,-0.145572f,-0.923626f},{0.370918f,-0.170775f,-0.912828f}, +{0.36704f,-0.104143f,-0.924357f},{0.397743f,0.0893554f,-0.913135f},{0.412708f,0.16809f,-0.89522f}, +{0.461831f,0.0794305f,-0.883404f},{0.491097f,0.00610044f,-0.871083f},{0.469434f,0.0393794f,-0.882089f}, +{-0.318099f,0.1176f,-0.940736f},{0.397764f,-0.22051f,-0.890595f},{0.339251f,-0.140336f,-0.930169f}, +{0.415097f,0.104971f,-0.903701f},{0.448675f,0.0117692f,-0.893618f},{0.335919f,-0.319947f,-0.885885f}, +{0.31524f,-0.196916f,-0.928357f},{0.441539f,0.052358f,-0.895713f},{0.374789f,0.0703258f,-0.924439f}, +{0.291724f,-0.429357f,-0.854722f},{0.198636f,-0.335479f,-0.920868f},{0.241156f,-0.318072f,-0.916883f}, +{0.309921f,-0.504065f,-0.806144f},{0.342391f,-0.433425f,-0.833613f},{0.279045f,-0.345422f,-0.896001f}, +{0.282074f,-0.247687f,-0.926869f},{0.279043f,-0.0375461f,-0.959544f},{0.26891f,-0.0713013f,-0.960523f}, +{0.277696f,-0.230241f,-0.93267f},{0.282046f,-0.160948f,-0.945804f},{0.24807f,-0.11132f,-0.962325f}, +{0.324934f,0.0120236f,-0.94566f},{0.404792f,0.0859546f,-0.91036f},{0.421959f,-0.0172375f,-0.906451f}, +{0.405509f,0.0607184f,-0.912072f},{0.370021f,0.0255989f,-0.92867f},{-0.273495f,0.102608f,-0.956385f}, +{-0.214751f,-0.0217704f,-0.976426f},{0.0389195f,-0.998942f,-0.0245161f},{0.301224f,-0.125571f,-0.945249f}, +{0.247679f,0.00834955f,-0.968806f},{-0.582805f,-0.287323f,-0.760121f},{0.153013f,-0.257305f,-0.954139f}, +{0.0623255f,-0.321166f,-0.94497f},{0.118664f,-0.440027f,-0.890109f},{0.186817f,-0.485921f,-0.853804f}, +{0.256389f,-0.395781f,-0.881829f},{0.242238f,-0.192341f,-0.95096f},{0.215539f,-0.00689006f,-0.976471f}, +{0.250731f,-0.139028f,-0.958021f},{0.261066f,-0.213902f,-0.941324f},{0.256085f,-0.0887571f,-0.962571f}, +{0.230182f,-0.188229f,-0.95477f},{0.34271f,-0.116949f,-0.932133f},{0.483111f,0.0179296f,-0.875376f}, +{0.506741f,0.0262392f,-0.861699f},{0.420864f,0.0887582f,-0.902771f},{0.387847f,0.0724752f,-0.91887f}, +{0.373944f,0.0811103f,-0.923898f},{-0.463775f,-0.155676f,-0.872169f},{0.205632f,-0.363896f,-0.908457f}, +{0.414665f,-0.202407f,-0.887177f},{0.51326f,-0.0593179f,-0.856181f},{0.477654f,0.0737985f,-0.875443f}, +{0.456738f,0.120889f,-0.881349f},{-0.124059f,-0.0143952f,-0.99217f},{0.159493f,-0.171302f,-0.972223f}, +{0.0728596f,-0.286864f,-0.955197f},{0.14769f,-0.548362f,-0.823096f},{0.222186f,-0.501618f,-0.83607f}, +{0.140263f,-0.412915f,-0.899904f},{0.0834118f,-0.318639f,-0.944199f},{0.132404f,-0.165691f,-0.977249f}, +{0.211184f,-0.0144253f,-0.97734f},{0.247741f,-0.159525f,-0.955603f},{0.282763f,-0.251771f,-0.925557f}, +{0.309661f,-0.0746755f,-0.94791f},{0.352027f,-0.173439f,-0.91978f},{0.314486f,-0.260742f,-0.91275f}, +{0.309214f,-0.0476167f,-0.9498f},{0.297153f,0.0785501f,-0.951593f},{0.341974f,0.140095f,-0.929208f}, +{0.361015f,0.0969296f,-0.927509f},{0.319776f,0.0989742f,-0.94231f},{0.13153f,0.190633f,-0.97281f}, +{0.0931867f,0.114043f,-0.989096f},{0.223886f,-0.0861198f,-0.970803f},{0.168265f,-0.0797806f,-0.982508f}, +{0.312417f,-0.196147f,-0.929474f},{0.326859f,-0.0967924f,-0.940103f},{-0.359361f,0.0730781f,-0.930333f}, +{0.158572f,-0.0861268f,-0.983584f},{0.109713f,-0.282932f,-0.952845f},{0.138901f,-0.618307f,-0.773565f}, +{0.227161f,-0.643871f,-0.730635f},{0.27837f,-0.418527f,-0.864492f},{0.262264f,-0.228899f,-0.937456f}, +{0.15929f,-0.233517f,-0.959216f},{0.095923f,-0.265918f,-0.959211f},{0.273692f,-0.203507f,-0.940041f}, +{0.395505f,-0.250086f,-0.883761f},{0.321622f,-0.368589f,-0.872182f},{0.370407f,-0.233665f,-0.898999f}, +{0.268743f,-0.0340914f,-0.962608f},{0.190561f,-0.22345f,-0.955906f},{0.199725f,-0.252784f,-0.946684f}, +{0.219432f,-0.0917187f,-0.971307f},{0.249687f,0.0682264f,-0.96592f},{0.251793f,0.115452f,-0.96087f}, +{0.311541f,0.0876862f,-0.946178f},{0.277111f,0.139705f,-0.950627f},{0.335169f,-0.213682f,-0.917606f}, +{0.413841f,-0.243567f,-0.877161f},{-0.508553f,0.375086f,-0.775038f},{0.158959f,-0.33245f,-0.929628f}, +{0.149185f,-0.0420049f,-0.987917f},{0.119061f,-0.346273f,-0.930548f},{0.104111f,-0.669696f,-0.735301f}, +{0.144693f,-0.679733f,-0.719046f},{0.165334f,-0.354326f,-0.92039f},{0.190832f,-0.188236f,-0.963405f}, +{0.213776f,-0.300113f,-0.929641f},{0.184987f,-0.345558f,-0.919984f},{0.214224f,-0.303985f,-0.928279f}, +{0.256223f,-0.283327f,-0.924162f},{0.23446f,-0.110913f,-0.965778f},{0.222045f,0.0280285f,-0.974634f}, +{0.219076f,-0.154819f,-0.963347f},{0.20427f,-0.26067f,-0.94357f},{0.200559f,-0.182029f,-0.962622f}, +{0.20429f,-0.0842184f,-0.975281f},{0.25253f,0.0505589f,-0.966267f},{0.332164f,0.0709402f,-0.94055f}, +{0.441632f,0.0665385f,-0.894726f},{-0.351427f,0.362608f,-0.863142f},{0.247619f,0.00508906f,-0.968844f}, +{0.389279f,-0.0348041f,-0.920462f},{0.443012f,0.0647495f,-0.894174f},{0.109221f,-0.993986f,-0.00792956f}, +{0.203599f,-0.978122f,-0.0427252f},{-0.238324f,0.306725f,-0.921478f},{0.1839f,-0.223995f,-0.957082f}, +{0.409799f,0.203448f,-0.889198f},{0.102517f,-0.279304f,-0.954714f},{0.13668f,-0.0744809f,-0.987811f}, +{0.186384f,-0.311169f,-0.931899f},{0.174915f,-0.699354f,-0.693043f},{0.187465f,-0.654656f,-0.732313f}, +{0.136217f,-0.352045f,-0.926018f},{0.203846f,-0.210139f,-0.956184f},{0.244663f,-0.312758f,-0.917781f}, +{0.108868f,-0.329495f,-0.93786f},{0.203825f,-0.317197f,-0.926197f},{0.204169f,-0.301991f,-0.931191f}, +{0.20256f,-0.0767667f,-0.976256f},{0.150877f,-0.00640872f,-0.988532f},{0.230954f,-0.186151f,-0.954991f}, +{0.155388f,-0.298392f,-0.941709f},{0.1697f,-0.192309f,-0.96655f},{0.18909f,-0.116842f,-0.974984f}, +{0.282293f,-0.0916951f,-0.954936f},{0.331828f,-0.0941502f,-0.93863f},{0.308796f,0.0477273f,-0.94993f}, +{-0.408249f,-0.720043f,-0.561134f},{0.268533f,-0.263567f,-0.926511f},{0.091933f,-0.285835f,-0.953859f}, +{0.118349f,-0.0897115f,-0.988911f},{0.145821f,-0.461532f,-0.875057f},{0.126957f,-0.694749f,-0.707958f}, +{0.114736f,-0.552027f,-0.825894f},{0.0458875f,-0.363814f,-0.930341f},{0.17179f,-0.349481f,-0.92106f}, +{0.241402f,-0.314832f,-0.917936f},{0.187544f,-0.248817f,-0.95022f},{0.154279f,-0.317391f,-0.935661f}, +{0.171403f,-0.273071f,-0.946601f},{0.218886f,-0.0469395f,-0.974621f},{0.23039f,-0.0452667f,-0.972045f}, +{0.334638f,-0.235148f,-0.912537f},{0.238913f,-0.201569f,-0.94989f},{0.201881f,-0.159874f,-0.966273f}, +{0.25613f,-0.189546f,-0.947877f},{0.257277f,-0.113556f,-0.959642f},{0.220836f,-0.0757441f,-0.972365f}, +{0.162348f,0.0763838f,-0.983773f},{0.189445f,0.265051f,-0.945441f},{0.136332f,-0.0751422f,-0.987809f}, +{0.095285f,-0.27107f,-0.957832f},{0.101441f,-0.104338f,-0.989355f},{0.0567444f,-0.445659f,-0.893402f}, +{-0.000193409f,-0.676142f,-0.736771f},{0.0109145f,-0.465338f,-0.885066f},{0.0534069f,-0.389789f,-0.919354f}, +{0.0916234f,-0.502817f,-0.859523f},{0.160832f,-0.40552f,-0.899826f},{0.159299f,-0.195571f,-0.967665f}, +{0.132088f,-0.240001f,-0.961744f},{0.0841788f,-0.31464f,-0.945471f},{0.133508f,-0.134741f,-0.981845f}, +{0.17912f,-0.0888595f,-0.979806f},{0.208443f,-0.195479f,-0.9583f},{0.259759f,-0.212322f,-0.942043f}, +{0.226557f,-0.164269f,-0.960046f},{0.138192f,-0.0514023f,-0.989071f},{0.093177f,-0.0381038f,-0.99492f}, +{0.0672073f,-0.120768f,-0.990403f},{0.176091f,0.262835f,-0.948636f},{0.0965525f,-0.994059f,-0.0502461f}, +{-0.0002735f,-0.997933f,0.0642685f},{-0.00876615f,-0.997232f,0.073829f},{-0.0215592f,-0.996239f,0.083924f}, +{0.198791f,-0.12984f,-0.971403f},{-0.613472f,-0.357682f,-0.704071f},{0.145189f,-0.0897178f,-0.985328f}, +{0.0700315f,-0.316691f,-0.94594f},{-0.0368761f,-0.677374f,-0.734714f},{-0.0508762f,-0.574966f,-0.816594f}, +{0.00461845f,-0.453498f,-0.891245f},{-0.00453975f,-0.52994f,-0.848023f},{0.00720484f,-0.395835f,-0.918294f}, +{0.0863324f,-0.18462f,-0.979011f},{0.129269f,-0.278945f,-0.951567f},{0.227027f,-0.32294f,-0.918786f}, +{0.265326f,-0.189893f,-0.945274f},{0.255582f,-0.118537f,-0.959493f},{0.287931f,-0.233008f,-0.928872f}, +{0.268068f,-0.185136f,-0.945444f},{0.196184f,-0.0750087f,-0.977694f},{0.0985332f,0.00685483f,-0.99511f}, +{0.162505f,-0.0579435f,-0.985005f},{0.151121f,-0.336992f,-0.9293f},{0.208861f,-0.129676f,-0.96931f}, +{-0.333093f,-0.877541f,-0.344923f},{-0.855978f,-0.334846f,-0.393928f},{0.342906f,-0.296651f,-0.891299f}, +{0.278857f,-0.0874291f,-0.956345f},{0.191219f,-0.979642f,0.0611353f},{-0.0449772f,-0.984866f,-0.167378f}, +{0.257991f,-0.139881f,-0.955967f},{0.140787f,0.0217321f,-0.989801f},{0.171542f,-0.0350602f,-0.984553f}, +{0.191266f,-0.111461f,-0.975189f},{0.134866f,0.0460265f,-0.989794f},{-0.474359f,-0.186298f,-0.860393f}, +{-0.0743062f,-0.679009f,0.730359f},{0.17509f,-0.179612f,-0.968031f},{0.18737f,-0.182224f,-0.965239f}, +{0.0634384f,-0.553884f,-0.830173f},{-0.0614459f,-0.573997f,-0.816549f},{-0.0548518f,-0.463718f,-0.884283f}, +{-0.0542619f,-0.46565f,-0.883304f},{-0.0886851f,-0.485649f,-0.869643f},{-0.031275f,-0.343695f,-0.938561f}, +{0.109803f,-0.424685f,-0.898658f},{0.157277f,-0.316265f,-0.935543f},{0.181625f,-0.149741f,-0.9719f}, +{0.250089f,-0.202109f,-0.946893f},{0.224643f,-0.195189f,-0.954692f},{0.189713f,-0.127546f,-0.97352f}, +{0.120296f,-0.0534839f,-0.991296f},{-0.457533f,-0.498441f,-0.736356f},{-0.0184083f,-0.864544f,0.502219f}, +{0.0450966f,-0.998981f,0.00189569f},{0.0182236f,-0.999668f,-0.0182031f},{-0.308813f,-0.950316f,0.0391569f}, +{0.249777f,-0.228349f,-0.940993f},{0.0407418f,-0.378347f,-0.924767f},{-0.164432f,0.40839f,-0.897875f}, +{-0.314956f,0.0949649f,-0.944343f},{-0.541635f,-0.409308f,-0.734233f},{-0.159007f,-0.282733f,-0.945927f}, +{-0.0413941f,-0.283658f,-0.958032f},{0.0651972f,-0.232411f,-0.97043f},{0.0368408f,-0.149265f,-0.988111f}, +{0.0695572f,-0.163889f,-0.984024f},{0.0598061f,-0.11698f,-0.991332f},{0.0909766f,0.0319594f,-0.99534f}, +{0.102819f,-0.0478997f,-0.993546f},{0.0437593f,-0.077876f,-0.996002f},{0.0155373f,0.0713922f,-0.997327f}, +{-0.348753f,0.139677f,-0.926748f},{0.23431f,-0.891494f,0.387733f},{-0.553502f,-0.308364f,-0.773659f}, +{-0.616738f,-0.173523f,-0.767805f},{-0.380602f,0.11266f,-0.917851f},{0.22381f,-0.132088f,-0.965641f}, +{-0.0792494f,-0.411621f,-0.907903f},{-0.193881f,-0.479576f,-0.855813f},{-0.197234f,-0.449732f,-0.871114f}, +{-0.0877846f,-0.449835f,-0.888787f},{-0.185332f,-0.415287f,-0.890612f},{-0.207097f,-0.395328f,-0.894889f}, +{-0.118296f,-0.403333f,-0.907374f},{0.00189189f,-0.999994f,0.00306737f},{-0.457134f,-0.420451f,-0.783741f}, +{-0.635975f,-0.131724f,-0.760385f},{-0.269536f,-0.956413f,-0.112361f},{-0.288356f,-0.418766f,-0.861096f}, +{-0.36962f,-0.401844f,-0.837797f},{-0.377607f,-0.407334f,-0.83156f},{-0.348602f,-0.404377f,-0.845551f}, +{-0.248868f,-0.324939f,-0.912403f},{-0.279433f,-0.340647f,-0.897707f},{-0.214021f,-0.324631f,-0.921309f}, +{-0.112611f,-0.190842f,-0.97514f},{-0.0751645f,-0.194146f,-0.978089f},{-0.0507894f,-0.146409f,-0.987919f}, +{-0.0101824f,-0.0328948f,-0.999407f},{0.0159028f,-0.0202976f,-0.999668f},{-0.0595336f,0.00126352f,-0.998226f}, +{-0.0968821f,0.0913483f,-0.991095f},{-0.139114f,0.286357f,-0.94797f},{-0.921666f,-0.384264f,-0.0535948f}, +{0.0780428f,-0.996611f,-0.0260006f},{-0.283515f,-0.477441f,-0.831667f},{-0.331705f,-0.449341f,-0.829496f}, +{-0.385828f,-0.392684f,-0.834827f},{-0.517772f,-0.225955f,-0.82514f},{-0.55825f,-0.15923f,-0.81425f}, +{-0.592719f,-0.108802f,-0.798027f},{0.230233f,-0.0502752f,-0.971836f},{0.119686f,0.0323427f,-0.992285f}, +{-0.158251f,-0.204838f,-0.965918f},{-0.250495f,-0.436161f,-0.864301f},{-0.453561f,-0.372427f,-0.809679f}, +{-0.371939f,-0.334884f,-0.865745f},{-0.351896f,-0.847199f,0.398023f},{-0.0471725f,-0.995583f,0.0811798f}, +{-0.178855f,0.347392f,-0.920505f},{-0.0643786f,-0.901551f,0.427855f},{0.165713f,-0.0165558f,-0.986035f}, +{0.093963f,0.185729f,-0.978098f},{-0.085456f,0.0715648f,-0.993768f},{-0.383911f,-0.292464f,-0.875829f}, +{-0.429875f,-0.347318f,-0.833413f},{-0.481882f,-0.391585f,-0.783869f},{-0.433764f,-0.41101f,-0.801823f}, +{-0.492389f,-0.348149f,-0.797713f},{-0.48093f,-0.351514f,-0.803209f},{-0.369498f,-0.397625f,-0.83986f}, +{-0.434404f,-0.368349f,-0.821956f},{-0.328686f,-0.300934f,-0.895212f},{-0.272589f,-0.176483f,-0.945806f}, +{-0.194385f,-0.0887588f,-0.976901f},{-0.213207f,0.0173482f,-0.976853f},{-0.203672f,0.0458605f,-0.977965f}, +{-0.212849f,0.138942f,-0.967156f},{-0.201355f,0.286949f,-0.936545f},{-0.170263f,-0.958942f,0.226804f}, +{0.0336438f,-0.997239f,0.066202f},{0.476486f,-0.877819f,0.0489346f},{-0.175802f,-0.482737f,-0.857938f}, +{-0.240114f,-0.406166f,-0.881688f},{-0.317766f,-0.319497f,-0.892719f},{-0.450952f,-0.15198f,-0.879514f}, +{-0.511979f,-0.296898f,-0.806058f},{-0.508813f,-0.393695f,-0.76558f},{0.112715f,0.225438f,-0.967715f}, +{-0.598289f,-0.679083f,-0.42532f},{-0.235634f,-0.951767f,-0.196511f},{-0.476286f,-0.803592f,-0.356919f}, +{-0.362864f,-0.223333f,-0.904683f},{-0.420486f,-0.21012f,-0.882633f},{-0.279394f,0.219675f,-0.93471f}, +{-0.0115128f,-0.999142f,0.0397777f},{-0.000605356f,-0.997024f,0.077089f},{-0.0082265f,-0.999963f,0.00249397f}, +{-0.0261943f,-0.998588f,-0.0462172f},{0.0705986f,0.26835f,-0.960731f},{-0.0994969f,0.328516f,-0.939243f}, +{-0.393652f,-0.0258967f,-0.918895f},{-0.523073f,-0.360866f,-0.772121f},{-0.533142f,-0.277189f,-0.799328f}, +{-0.483933f,-0.15738f,-0.860837f},{-0.436994f,-0.0964095f,-0.894283f},{-0.363398f,0.0677128f,-0.92917f}, +{-0.473792f,-0.00518424f,-0.880622f},{-0.390418f,0.0204487f,-0.920411f},{-0.355069f,0.15848f,-0.921309f}, +{-0.18989f,-0.469618f,-0.862207f},{0.0387132f,-0.998767f,0.031081f},{-0.22247f,-0.972103f,-0.0743105f}, +{-0.0842592f,-0.983493f,0.160133f},{-0.460062f,0.220102f,-0.860173f},{0.0931486f,-0.955462f,0.280027f}, +{0.0532537f,-0.998488f,-0.0136487f},{-0.203823f,-0.966645f,-0.155093f},{0.0599921f,-0.998181f,0.00590705f}, +{0.0167739f,-0.999717f,-0.0168777f},{0.111033f,-0.986245f,0.122446f},{0.0914944f,-0.881277f,0.463658f}, +{0.0355514f,-0.998636f,0.0382277f},{0.437185f,-0.872861f,0.216754f},{0.422546f,-0.855048f,0.300579f}, +{0.42756f,-0.880253f,0.205784f},{-0.322485f,-0.87414f,-0.363155f},{-0.00853661f,-0.991127f,-0.132642f}, +{-0.145474f,-0.888027f,-0.436171f},{-0.493414f,-0.735429f,0.464422f},{-0.335755f,-0.760172f,0.556243f}, +{-0.493879f,-0.817461f,0.296379f},{0.261385f,-0.811544f,0.522565f},{0.032797f,-0.995392f,0.090103f}, +{0.114284f,-0.915866f,0.384874f},{-0.423877f,-0.903915f,-0.0571463f},{-0.595218f,-0.723646f,0.34936f}, +{0.21114f,-0.821121f,0.530264f},{-0.00545385f,-0.999841f,-0.0169869f},{-0.0187241f,-0.995127f,-0.0968045f}, +{0.0192527f,-0.997316f,0.0706471f},{0.0217938f,-0.996704f,0.0781471f},{0.0252164f,-0.997289f,0.0691295f}, +{0.0388396f,-0.996338f,0.076169f},{-0.0958933f,-0.67412f,0.732371f},{-0.0528083f,-0.6354f,0.770375f}, +{-0.277386f,-0.89954f,0.337469f},{-0.415031f,-0.742829f,0.525313f},{0.0269597f,-0.998768f,0.0416694f}, +{0.0208328f,-0.998511f,0.0504246f},{0.0197848f,-0.999201f,0.0347204f},{0.441117f,-0.887602f,0.132582f}, +{0.220524f,-0.949395f,-0.223647f},{0.254325f,-0.918615f,-0.302432f},{-0.411947f,-0.905298f,-0.103613f}, +{0.0330157f,-0.999407f,0.00973608f},{0.0360647f,-0.999324f,0.00715541f},{0.0281243f,-0.999554f,0.0100368f}, +{0.0317264f,-0.999288f,-0.0204045f},{0.0207974f,-0.999648f,-0.0164574f},{0.390465f,-0.895183f,0.214907f}, +{0.068996f,-0.997368f,-0.0222887f},{0.00696426f,-0.999677f,0.0244445f},{0.113192f,-0.990527f,-0.0777368f}, +{0.0300783f,-0.997791f,-0.0592262f},{-0.101247f,-0.989736f,0.100855f},{-0.488221f,-0.872483f,-0.0203287f}, +{0.0192702f,-0.995406f,0.0937832f},{-0.00526962f,-0.999976f,0.004602f},{0.168434f,-0.985586f,-0.015808f}, +{-0.274512f,-0.959802f,0.0585097f},{-0.125707f,-0.991656f,-0.0285798f},{-0.440623f,-0.879403f,-0.180285f}, +{-0.508811f,-0.860146f,0.0355013f},{-0.295831f,-0.930916f,-0.214194f},{-0.133933f,-0.979029f,-0.153508f}, +{-0.504832f,-0.82857f,-0.242108f},{-0.199422f,-0.931407f,-0.304485f},{-0.0191935f,-0.999509f,-0.0247501f}, +{-0.00421024f,-0.997259f,-0.0738707f},{-0.456138f,-0.792245f,-0.405322f},{0.536549f,-0.617989f,0.574635f}, +{0.159432f,-0.964327f,0.211319f},{0.119699f,-0.987882f,0.098801f},{0.0753162f,-0.996306f,0.041258f}, +{0.0769156f,-0.995434f,0.056522f},{0.199961f,-0.972262f,0.121333f},{0.0324883f,-0.998411f,0.0460362f}, +{0.00150144f,-0.998946f,0.0458802f},{0.0212488f,-0.999318f,0.0302135f},{0.0380284f,-0.999242f,-0.00828349f}, +{0.0575431f,-0.998323f,0.00632132f},{-0.0275333f,-0.993345f,-0.111842f},{0.179328f,-0.983237f,-0.032964f}, +{0.134155f,-0.989332f,0.056792f},{-0.275129f,-0.942456f,0.189949f},{-0.434564f,-0.859275f,0.269818f}, +{-0.501779f,-0.819213f,0.277684f},{0.230928f,-0.92776f,0.293145f},{0.457012f,-0.817257f,0.351042f}, +{0.431776f,-0.889879f,0.147261f},{-0.00337819f,-0.99996f,0.00822642f},{-0.0679747f,-0.924453f,0.375188f}, +{0.421036f,-0.80576f,0.41651f},{0.261081f,-0.824249f,0.502445f},{-0.400411f,-0.863751f,0.30595f}, +{-0.00950766f,-0.998909f,0.0457252f},{0.108334f,-0.993724f,-0.0278652f},{0.119746f,-0.987411f,0.103343f}, +{0.246439f,-0.960619f,0.128374f},{-0.0457223f,-0.994671f,-0.0924088f},{0.254503f,-0.966784f,0.0235859f}, +{0.267335f,-0.961599f,0.0621237f},{0.139755f,-0.968292f,0.207074f},{-0.0581971f,-0.996083f,0.0665645f}, +{-0.100193f,-0.994413f,0.0332144f},{0.256728f,-0.860271f,0.440482f},{0.0205264f,-0.995617f,0.0912464f}, +{0.145715f,-0.989314f,-0.00497361f},{0.442628f,-0.725749f,0.526658f},{0.422509f,-0.759082f,0.495258f}, +{0.0250255f,-0.999672f,0.00538897f},{0.0226148f,-0.999695f,-0.0098717f},{-0.0512331f,-0.95503f,0.29205f}, +{0.0094949f,-0.999954f,0.00102734f},{-0.134225f,-0.965527f,-0.223029f},{-0.0342345f,-0.997022f,-0.0691043f}, +{0.0220392f,-0.999002f,-0.0388572f},{0.0200358f,-0.999289f,-0.031944f},{-0.160766f,-0.975433f,-0.150616f}, +{0.0182022f,-0.999505f,-0.0256645f},{0.157028f,-0.799171f,0.580231f},{0.348365f,-0.535744f,0.769168f}, +{-0.0879878f,-0.890344f,0.446705f},{-0.299798f,-0.892556f,0.336847f},{-0.485275f,-0.745735f,0.456494f}, +{-0.0470124f,-0.998452f,-0.0297115f},{0.0765366f,-0.877318f,0.473766f},{-0.0188492f,-0.998556f,-0.0503023f}, +{0.0232935f,-0.998163f,0.0559258f},{-0.0291428f,-0.923105f,0.383443f},{0.0719425f,-0.983426f,0.166427f}, +{-0.352676f,-0.869485f,0.345856f},{0.0223918f,-0.995144f,0.0958476f},{0.0235307f,-0.996475f,0.0805263f}, +{-0.0904133f,-0.98791f,0.125936f},{-0.669071f,-0.733856f,-0.117467f},{0.409527f,-0.911178f,-0.0452039f}, +{-0.477475f,-0.771456f,0.420563f},{0.0244424f,-0.995812f,0.0881007f},{0.0470478f,-0.997189f,0.0583127f}, +{-0.627038f,-0.766081f,0.141222f},{-0.641998f,-0.766623f,-0.0113149f},{-0.598191f,-0.738564f,-0.310951f}, +{-0.435507f,-0.821898f,-0.367176f},{-0.00972171f,-0.998838f,0.0472077f},{-0.433635f,-0.802921f,0.408997f}, +{0.0943655f,-0.869567f,0.484715f},{-0.113401f,-0.985403f,0.126968f},{-0.0507322f,-0.910702f,0.409937f}, +{-0.147138f,-0.692524f,0.706229f},{0.0576281f,-0.990996f,0.120851f},{0.0329572f,-0.994475f,0.0996614f}, +{0.0424437f,-0.995673f,0.0826672f},{-0.458454f,-0.748406f,0.479278f},{0.183653f,-0.959272f,-0.214638f}, +{0.0274192f,-0.999423f,-0.020069f},{0.0224728f,-0.999636f,-0.0148969f},{0.0689027f,-0.996106f,0.0550065f}, +{0.267426f,-0.943674f,0.194839f},{-0.143591f,-0.609172f,0.77993f},{-0.683555f,-0.706749f,0.182371f}, +{0.0207577f,-0.999514f,-0.0232647f},{-0.00208528f,-0.999923f,-0.0122105f},{0.00944962f,-0.999952f,0.00269041f}, +{0.0197769f,-0.999642f,-0.0180048f},{0.0818378f,-0.995107f,0.0553525f},{-0.60003f,-0.772993f,0.206023f}, +{0.132217f,-0.736984f,0.662852f},{0.0259287f,-0.996915f,0.0740829f},{-0.219604f,-0.975282f,0.0244756f}, +{0.181724f,-0.972486f,0.145768f},{0.186707f,-0.976889f,-0.104062f},{-0.358453f,-0.845749f,0.395247f}, +{0.125563f,-0.984195f,0.124874f},{-0.172515f,-0.972719f,0.155102f},{0.0503893f,-0.998717f,-0.00495466f}, +{0.252812f,-0.945431f,0.20554f},{0.327671f,-0.916653f,0.228865f},{0.413063f,-0.88025f,0.233537f}, +{0.507231f,-0.849177f,0.147022f},{0.418226f,-0.898864f,0.130885f},{-0.254397f,-0.928918f,0.26906f}, +{0.331623f,-0.931502f,0.149429f},{0.31091f,-0.914002f,0.260643f},{0.66593f,-0.668128f,0.331876f}, +{0.344914f,-0.804073f,0.484252f},{0.368575f,-0.849033f,0.378543f},{0.0591826f,-0.99639f,0.0608577f}, +{-0.623304f,-0.750806f,-0.218594f},{-0.617055f,-0.758178f,-0.210734f},{-0.588556f,-0.784469f,-0.195473f}, +{-0.559468f,-0.824327f,-0.0864911f},{0.0300425f,-0.998085f,0.0540763f},{0.0202993f,-0.999654f,-0.0167502f}, +{0.0324792f,-0.999177f,-0.0242951f},{0.0377085f,-0.998735f,-0.0332751f},{0.0247975f,-0.999377f,-0.0251006f}, +{-0.175498f,-0.980598f,-0.0873358f},{0.0152553f,-0.999611f,-0.0233534f},{-0.0155738f,-0.999348f,-0.0325671f}, +{0.433514f,-0.891799f,0.129464f},{0.0318165f,-0.999425f,-0.0117401f},{0.0301593f,-0.998859f,-0.0370258f}, +{0.0351686f,-0.998546f,-0.0408672f},{0.0354106f,-0.997615f,0.0592413f},{0.0265466f,-0.993097f,0.114249f}, +{-0.246175f,-0.969217f,-0.004021f},{0.0260654f,-0.999081f,-0.0340246f},{0.439339f,-0.795647f,0.417046f}, +{0.453894f,-0.753803f,0.475144f},{-0.0218013f,-0.997889f,0.0611717f},{-0.353667f,-0.930265f,-0.0976006f}, +{-0.573537f,-0.714299f,-0.401039f},{-0.258594f,-0.948539f,-0.182764f},{-0.4667f,-0.864393f,-0.187126f}, +{0.395986f,-0.871117f,0.290431f},{0.177311f,-0.984137f,-0.0060141f},{-0.0176953f,-0.999403f,-0.0296736f}, +{-0.467747f,-0.865832f,-0.177615f},{0.0250974f,-0.998326f,-0.052109f},{-0.639944f,-0.768392f,0.00664921f}, +{-0.436519f,-0.882692f,-0.174088f},{-0.0443429f,-0.996264f,-0.0741012f},{0.148824f,-0.978852f,0.14036f}, +{0.290687f,-0.919037f,0.266219f},{0.352249f,-0.880871f,0.316206f},{0.0831872f,-0.874957f,0.477f}, +{-0.0582198f,-0.996291f,0.0633602f},{-0.210873f,-0.96402f,-0.161858f},{0.133774f,-0.990679f,-0.0256777f}, +{0.438413f,-0.846291f,0.302631f},{0.033499f,-0.997861f,0.056144f},{-0.45141f,-0.872907f,-0.1851f}, +{0.0452908f,-0.996803f,0.0658251f},{-0.0145148f,-0.999841f,0.0103397f},{0.498847f,-0.852849f,0.154272f}, +{-0.333123f,-0.851253f,0.40546f},{-0.48286f,-0.805861f,-0.342687f},{-0.296849f,-0.934096f,-0.198358f}, +{0.0291155f,-0.99953f,-0.00955752f},{-0.222844f,-0.947564f,0.229046f},{-0.0128039f,-0.996041f,0.087971f}, +{-0.522135f,-0.852551f,0.0230707f},{-0.397436f,-0.898396f,-0.186892f},{-0.522835f,-0.827296f,-0.205488f}, +{-0.641568f,-0.688765f,0.337629f},{0.0315253f,-0.999262f,-0.0219513f},{-0.0624349f,-0.891708f,0.448284f}, +{-0.19033f,-0.639321f,0.745012f},{0.465534f,-0.885002f,-0.00708205f},{0.0205535f,-0.999617f,0.0185422f}, +{0.0343446f,-0.999226f,-0.019187f},{-0.012597f,-0.921971f,0.387055f},{-0.0375038f,-0.964409f,0.261741f}, +{0.0311963f,-0.999185f,-0.0256275f},{-0.179976f,-0.983404f,0.0229121f},{0.019335f,-0.999568f,0.0221399f}, +{0.0401681f,-0.998975f,0.0208602f},{-0.460601f,-0.886659f,-0.0410255f},{-0.440598f,-0.897705f,-4.98216e-05f}, +{-0.504286f,-0.842614f,0.188935f},{-0.692687f,-0.706962f,0.142793f},{-0.655434f,-0.755063f,0.0168972f}, +{0.0579206f,-0.997201f,0.0472857f},{0.0291897f,-0.999256f,0.0252153f},{0.277683f,-0.837692f,0.470282f}, +{-0.35385f,-0.827547f,0.43584f},{-0.14467f,-0.922792f,0.357107f},{-0.243562f,-0.650974f,0.718965f}, +{-0.0765156f,-0.90654f,0.415127f},{-0.0870873f,-0.647042f,0.757464f},{-0.0998985f,-0.705678f,0.701455f}, +{-0.121883f,-0.585755f,0.801271f},{-0.0845681f,-0.794925f,0.600785f},{0.322318f,-0.89145f,0.318478f}, +{0.268049f,-0.899651f,0.344641f},{0.394246f,-0.896125f,0.20379f},{0.365052f,-0.901085f,0.234057f}, +{-0.328019f,-0.934659f,-0.137167f},{-0.360418f,-0.920976f,0.147993f},{-0.45244f,-0.82013f,0.350265f}, +{-0.591845f,-0.602434f,0.535531f},{0.0557912f,-0.997563f,0.0418935f},{0.0480669f,-0.998536f,0.0248022f}, +{-0.705643f,-0.70726f,-0.0430247f},{0.0151378f,-0.95688f,0.290088f},{0.0243109f,-0.999251f,0.0300928f}, +{0.0207376f,-0.998779f,0.0448426f},{0.0115761f,-0.99813f,0.060023f},{0.0210507f,-0.999506f,-0.0233455f}, +{-0.297012f,-0.954334f,-0.0321031f},{-0.354475f,-0.691456f,0.629472f},{0.00592118f,-0.999939f,0.0093082f}, +{0.00944358f,-0.99995f,0.00317306f},{0.00479116f,-0.999985f,-0.00271793f},{0.111701f,-0.93354f,0.340625f}, +{-0.256367f,-0.898633f,0.355998f},{-0.243354f,-0.885941f,0.394826f},{0.0241804f,-0.803704f,0.594538f}, +{-0.538186f,-0.842816f,-0.00415148f},{-0.251625f,-0.957127f,0.1435f},{-0.339474f,-0.897541f,0.281383f}, +{0.446715f,-0.894676f,-0.000879809f},{0.0248744f,-0.995502f,0.0914169f},{-0.309336f,-0.665887f,0.6789f}, +{0.391327f,-0.920154f,-0.0134252f},{0.0404577f,-0.997928f,0.0500338f},{-0.00232434f,-0.9993f,0.037331f}, +{-0.00513422f,-0.999965f,-0.00653303f},{-0.253237f,-0.892645f,0.372903f},{-0.272659f,-0.844382f,0.461169f}, +{0.0187416f,-0.996571f,0.080588f},{-0.502938f,-0.647014f,0.573085f},{0.300552f,-0.922456f,-0.242371f}, +{0.271425f,-0.952077f,-0.140989f},{-0.00736048f,-0.999908f,0.0114276f},{-0.16623f,-0.958139f,0.233104f}, +{0.0206399f,-0.999591f,0.0197855f},{0.00345179f,-0.998907f,-0.0466221f},{0.0212683f,-0.999708f,-0.0114886f}, +{0.0375103f,-0.999227f,-0.0118001f},{-0.348836f,-0.931621f,0.101959f},{-0.314378f,-0.680497f,0.661883f}, +{-0.239775f,-0.428203f,0.871292f},{0.0269942f,-0.999498f,-0.016598f},{0.205362f,-0.975052f,0.0842651f}, +{0.270583f,-0.666424f,0.69474f},{-0.00150245f,-0.999758f,-0.0219692f},{-0.714f,-0.698681f,0.0452555f}, +{-0.688861f,-0.724634f,0.0193742f},{0.401174f,-0.906291f,0.133027f},{-0.505844f,-0.788873f,0.348999f}, +{-0.247088f,-0.952407f,0.178519f},{-0.205733f,-0.59121f,0.779836f},{0.0494061f,-0.992878f,0.108406f}, +{0.0367044f,-0.994233f,0.100762f},{0.0351902f,-0.99526f,0.0906619f},{0.213513f,-0.950656f,0.225089f}, +{-0.792844f,-0.571214f,0.212401f},{-0.741162f,-0.631635f,0.227412f},{-0.803971f,-0.570798f,0.166793f}, +{0.017301f,-0.999692f,-0.0177914f},{-0.229972f,-0.922314f,0.310563f},{-0.216697f,-0.792922f,0.569489f}, +{0.235083f,-0.926277f,0.294528f},{0.00602081f,-0.999691f,-0.0241236f},{-0.777905f,-0.572679f,0.258656f}, +{-0.814979f,-0.579368f,-0.0119202f},{-0.134095f,-0.712885f,0.688341f},{0.0263432f,-0.991925f,0.124058f}, +{-0.286608f,-0.917236f,0.276647f},{-0.274125f,-0.949667f,0.151618f},{-0.15952f,-0.639275f,0.752251f}, +{0.0140026f,-0.513734f,0.857835f},{0.0268088f,-0.997391f,0.0670283f},{0.54123f,-0.811167f,0.221538f}, +{-0.114704f,-0.964508f,0.23784f},{-0.114607f,-0.950567f,0.288597f},{-0.650764f,-0.732086f,-0.201387f}, +{-0.679137f,-0.732762f,0.042813f},{-0.626677f,-0.75023f,0.210786f},{-0.744444f,-0.615384f,0.259046f}, +{-0.783919f,-0.549936f,0.28817f},{0.0951864f,-0.992793f,0.0728156f},{-0.167854f,-0.82374f,0.541551f}, +{-0.556335f,-0.758532f,-0.339294f},{-0.572388f,-0.778919f,-0.256236f},{-0.675517f,-0.72405f,-0.139386f}, +{-0.697059f,-0.69526f,0.175279f},{-0.679396f,-0.680833f,0.273657f},{-0.745357f,-0.557158f,0.366084f}, +{-0.177695f,-0.919051f,0.351809f},{-0.246284f,-0.738901f,0.627192f},{-0.392196f,-0.627861f,0.672289f}, +{-0.637037f,-0.759177f,-0.133544f},{-0.651607f,-0.758376f,0.0165816f},{-0.628765f,-0.747946f,0.212678f}, +{-0.696044f,-0.539656f,0.473597f},{-0.631972f,-0.595965f,0.495417f},{0.0272724f,-0.999054f,0.0338618f}, +{0.0369473f,-0.938149f,0.344253f},{0.0546958f,-0.953689f,0.295779f},{-0.448396f,-0.847467f,-0.28415f}, +{-0.686933f,-0.723593f,0.0673533f},{-0.714324f,-0.659988f,0.232717f},{-0.625211f,-0.744034f,0.235634f}, +{-0.710254f,-0.586809f,0.388837f},{-0.580688f,-0.774537f,0.250786f},{-0.0388857f,-0.882911f,0.467928f}, +{-0.0236237f,-0.998928f,0.0398115f},{-0.527334f,-0.834f,-0.162363f},{-0.712196f,-0.701721f,-0.0190777f}, +{-0.719809f,-0.662478f,0.20736f},{-0.620255f,-0.739483f,0.261626f},{-0.668833f,-0.679571f,0.301408f}, +{-0.187982f,-0.790274f,0.583207f},{-0.26728f,-0.633922f,0.725743f},{-0.404074f,-0.88524f,-0.230377f}, +{-0.652531f,-0.733136f,0.19161f},{-0.715147f,-0.651889f,0.252201f},{-0.666044f,-0.696571f,0.266786f}, +{-0.588823f,-0.721757f,0.363807f},{-0.631337f,-0.667355f,0.395032f},{-0.626106f,-0.621972f,0.470257f}, +{-0.689142f,-0.508164f,0.516578f},{0.483577f,-0.845639f,0.225937f},{0.222438f,-0.938139f,0.26536f}, +{0.452834f,-0.838274f,0.303706f},{-0.385865f,-0.875169f,-0.29187f},{-0.379143f,-0.892042f,-0.245992f}, +{-0.367463f,-0.921055f,-0.128949f},{-0.448753f,-0.891519f,-0.0617599f},{-0.605329f,-0.795648f,0.0228318f}, +{-0.616404f,-0.740274f,0.268404f},{-0.60119f,-0.724447f,0.337264f},{0.339494f,-0.926629f,-0.161565f}, +{0.311439f,-0.949616f,-0.0351357f},{0.168152f,-0.911037f,0.376479f},{0.244289f,-0.95705f,0.156134f}, +{-0.397373f,-0.877227f,-0.269383f},{-0.338469f,-0.915208f,-0.218707f},{-0.297257f,-0.945486f,-0.133023f}, +{-0.464221f,-0.885413f,0.0232871f},{-0.527235f,-0.836455f,0.149552f},{-0.537636f,-0.781501f,0.316549f}, +{0.263596f,-0.94726f,0.182252f},{0.107578f,-0.844528f,0.524594f},{-0.0229386f,-0.670058f,0.741954f}, +{-0.198094f,-0.980182f,-0.00106648f},{0.300789f,-0.87079f,0.38891f},{-0.331517f,-0.918344f,-0.216197f}, +{-0.462501f,-0.834991f,0.29813f},{-0.569042f,-0.72583f,0.386474f},{-0.53184f,-0.784895f,0.317941f}, +{-0.530304f,-0.751916f,0.391663f},{-0.553343f,-0.688627f,0.46862f},{-0.626114f,-0.554425f,0.548265f}, +{-0.302206f,-0.872003f,0.385073f},{-0.281619f,-0.878895f,0.385013f},{0.370219f,-0.889681f,0.267219f}, +{-0.184527f,-0.981478f,0.0514892f},{-0.283202f,-0.942306f,-0.178485f},{-0.337896f,-0.936388f,-0.0948912f}, +{-0.353937f,-0.934617f,0.0349132f},{-0.428673f,-0.891312f,0.147655f},{-0.5048f,-0.763794f,0.40224f}, +{-0.516355f,-0.788889f,0.333213f},{-0.498756f,-0.722068f,0.479437f},{-0.511297f,-0.653887f,0.55768f}, +{-0.0552784f,-0.998325f,-0.0170444f},{-0.0497695f,-0.998759f,0.00176456f},{0.562496f,-0.806987f,0.179916f}, +{-0.299019f,-0.940457f,-0.161642f},{-0.414565f,-0.881518f,0.225968f},{-0.502966f,-0.770283f,0.392032f}, +{-0.0454346f,-0.996589f,-0.0688842f},{-0.367346f,-0.899865f,-0.23516f},{-0.2963f,-0.947716f,-0.118494f}, +{-0.332368f,-0.926584f,0.175994f},{-0.341852f,-0.900192f,0.269799f},{-0.460975f,-0.794915f,0.394476f}, +{-0.459568f,-0.762351f,0.455653f},{-0.492738f,-0.570427f,0.657132f},{-0.00268755f,-0.999991f,0.00317682f}, +{-0.0378324f,-0.99816f,0.0473877f},{-0.307912f,-0.950429f,-0.0433015f},{-0.363829f,-0.853433f,0.373204f}, +{-0.444851f,-0.787192f,0.427126f},{-0.44801f,-0.714161f,0.537829f},{-0.417495f,-0.673544f,0.609948f}, +{0.538354f,-0.616967f,0.574044f},{0.0257254f,-0.999666f,0.00248564f},{-0.0522176f,-0.990073f,-0.130498f}, +{-0.00701713f,-0.999776f,0.0199769f},{-0.0505058f,-0.997468f,0.0500581f},{-0.139269f,-0.980868f,0.136026f}, +{0.353183f,-0.908848f,-0.221941f},{-0.170674f,-0.984269f,-0.045659f},{-0.385411f,-0.915059f,-0.118848f}, +{-0.279051f,-0.940992f,0.191479f},{-0.387699f,-0.811757f,0.436738f},{-0.402329f,-0.599593f,0.691824f}, +{-0.392088f,-0.494074f,0.775988f},{0.25138f,-0.960718f,0.117599f},{0.25867f,-0.934443f,0.244756f}, +{-0.000258648f,-0.999939f,-0.0110008f},{-0.450518f,-0.754245f,0.477648f},{-0.369431f,-0.928961f,-0.0234975f}, +{-0.246785f,-0.963682f,-0.10205f},{-0.269227f,-0.960722f,-0.0673043f},{-0.277024f,-0.921078f,0.273628f}, +{-0.312528f,-0.895043f,0.318157f},{-0.397753f,-0.824121f,0.403258f},{-0.395394f,-0.819083f,0.415653f}, +{-0.407799f,-0.784016f,0.467994f},{-0.361726f,-0.476824f,0.80112f},{0.419553f,-0.807576f,0.414483f}, +{-0.0328999f,-0.995562f,-0.0881688f},{0.00490456f,-0.999923f,0.0114243f},{-0.00500412f,-0.999536f,0.0300628f}, +{-0.0225893f,-0.999116f,0.0354473f},{-0.250415f,-0.96782f,-0.0248562f},{-0.262979f,-0.963299f,-0.0538211f}, +{-0.259915f,-0.893097f,0.367181f},{-0.406335f,-0.754039f,0.516059f},{-0.374501f,-0.707038f,0.599872f}, +{-0.00901535f,-0.999941f,-0.00597997f},{0.0112928f,-0.999817f,0.0154339f},{-0.0553316f,-0.996185f,0.0674767f}, +{-0.262157f,-0.946199f,0.189687f},{-0.289524f,-0.868456f,0.402442f},{-0.338735f,-0.659233f,0.671319f}, +{-0.334709f,-0.593664f,0.731801f},{-0.371741f,-0.513727f,0.773236f},{-0.365128f,-0.483605f,0.795492f}, +{-0.00059238f,-0.999824f,0.0187576f},{-0.708771f,-0.535678f,0.459013f},{-0.268248f,-0.931937f,0.244002f}, +{-0.257741f,-0.923495f,0.284124f},{-0.302267f,-0.864319f,0.40198f},{-0.328919f,-0.814504f,0.477908f}, +{-0.398278f,-0.744518f,0.535786f},{-0.341668f,-0.543084f,0.767022f},{-0.00906552f,-0.998416f,-0.0555298f}, +{0.0048979f,-0.999787f,0.0200287f},{0.00803717f,-0.999851f,0.0152808f},{-0.0119875f,-0.999745f,0.0191419f}, +{-0.0895555f,-0.994392f,0.056257f},{0.0254007f,-0.998967f,-0.0376913f},{-0.312509f,-0.768606f,0.558197f}, +{0.364893f,-0.920631f,-0.138894f},{0.0128993f,-0.999899f,0.0059179f},{-0.0288258f,-0.999178f,0.0285066f}, +{-0.258087f,-0.956813f,0.133793f},{-0.255757f,-0.886008f,0.386754f},{-0.290083f,-0.856254f,0.427413f}, +{-0.283366f,-0.70297f,0.652332f},{-0.293061f,-0.650643f,0.700556f},{-0.276473f,-0.622532f,0.732131f}, +{-0.299256f,-0.582439f,0.755785f},{0.0163878f,-0.999752f,0.0150724f},{-0.000507724f,-0.999995f,0.00315279f}, +{-0.0421698f,-0.998888f,0.0210646f},{-0.250928f,-0.931038f,0.264959f},{-0.25111f,-0.884116f,0.394059f}, +{-0.23645f,-0.852751f,0.465732f},{0.328467f,-0.825255f,0.459417f},{0.021008f,-0.999752f,-0.00734064f}, +{0.00493785f,-0.999854f,0.0163817f},{-0.0179672f,-0.999771f,0.0116054f},{-0.0863018f,-0.994636f,0.0570237f}, +{-0.265653f,-0.809295f,0.523899f},{-0.24574f,-0.773703f,0.583948f},{-0.636252f,-0.7509f,-0.17701f}, +{-0.0316337f,-0.995426f,-0.0901415f},{0.0238889f,-0.999625f,0.013358f},{-0.0237546f,-0.999559f,0.0178184f}, +{-0.152329f,-0.985341f,0.0768047f},{-0.254989f,-0.67342f,0.693891f},{-0.268603f,-0.639303f,0.720517f}, +{-0.235042f,-0.608826f,0.757685f},{-0.204491f,-0.976698f,-0.0651494f},{-0.150398f,-0.979806f,-0.131759f}, +{0.021122f,-0.999776f,-0.00166307f},{0.0218361f,-0.999755f,0.00350407f},{-0.00245073f,-0.999989f,-0.00403658f}, +{0.0329289f,-0.998216f,0.049806f},{-0.0214891f,-0.999676f,0.0136586f},{-0.151138f,-0.988004f,0.0317164f}, +{-0.471599f,-0.880218f,0.053017f},{-0.087744f,-0.99605f,0.0136261f},{0.00488223f,-0.999985f,0.00253027f}, +{0.0235519f,-0.999662f,0.0109848f},{0.00771536f,-0.999958f,0.00493556f},{-0.0483468f,-0.998722f,0.0147213f}, +{-0.0966289f,-0.995311f,-0.00443566f},{-0.100578f,-0.994867f,0.0110831f},{-0.2308f,-0.783992f,0.576271f}, +{-0.0949228f,-0.994152f,-0.0515007f},{0.0585386f,-0.998263f,0.00666826f},{0.0257647f,-0.999628f,-0.00891625f}, +{0.00705262f,-0.999964f,0.0048063f},{0.0893046f,-0.99592f,0.0129442f},{-0.0840676f,-0.996023f,0.0294995f}, +{-0.0915318f,-0.995284f,0.0321161f},{-0.442534f,-0.744053f,0.500548f},{-0.188343f,-0.832876f,0.520427f}, +{-0.212909f,-0.727666f,0.652053f},{-0.149082f,-0.630633f,0.761627f},{-0.0149772f,-0.999886f,0.00202056f}, +{0.0580058f,-0.998296f,0.00634372f},{0.0261921f,-0.999637f,0.00628144f},{0.0210074f,-0.999747f,-0.00809831f}, +{0.0894734f,-0.978034f,0.188264f},{-0.00727228f,-0.995179f,-0.0978041f},{-0.0580299f,-0.995908f,0.0692871f}, +{-0.0377081f,-0.999179f,0.0148206f},{-0.167246f,-0.975051f,0.145963f},{-0.604768f,-0.766228f,0.217143f}, +{-0.323243f,-0.754984f,-0.570538f},{-0.0413738f,-0.561193f,0.82665f},{0.0582311f,-0.995559f,0.0739708f}, +{0.212125f,-0.976309f,0.0427084f},{0.173089f,-0.984599f,0.0245825f},{0.051426f,-0.998628f,0.00991884f}, +{0.185538f,-0.971576f,0.14702f},{-0.0620059f,-0.998041f,-0.00827753f},{-0.03141f,-0.998079f,0.0533925f}, +{-0.00191083f,-0.999499f,0.031603f},{-0.0122951f,-0.99968f,0.0221021f},{-0.389179f,-0.888326f,0.243755f}, +{-0.171659f,-0.788917f,0.590037f},{-0.100815f,-0.675771f,0.730185f},{0.00989494f,-0.999864f,0.0132298f}, +{0.164735f,-0.973664f,0.15761f},{0.0399642f,-0.999185f,-0.00564971f},{0.0490862f,-0.99459f,0.0915527f}, +{-0.208849f,-0.878834f,-0.42899f},{-0.102638f,-0.991526f,0.0796346f},{-0.00117614f,-0.998978f,0.0451781f}, +{-0.0267789f,-0.554178f,0.831967f},{0.00127388f,-0.547167f,0.837023f},{-0.0125311f,-0.994257f,-0.106286f}, +{0.0688277f,-0.990612f,0.118109f},{0.438693f,-0.870826f,0.221835f},{0.153572f,-0.988126f,0.00473824f}, +{0.0123614f,-0.999638f,-0.0239179f},{0.25956f,-0.840154f,0.476205f},{-0.0867319f,-0.990761f,0.104262f}, +{0.0170529f,-0.999175f,0.0368684f},{-0.0207686f,-0.999401f,0.0276937f},{-0.389356f,-0.798411f,0.459284f}, +{0.00408834f,-0.999335f,0.0362461f},{0.281034f,-0.921743f,0.267225f},{0.538217f,-0.831567f,0.137184f}, +{0.254132f,-0.96649f,0.0362606f},{0.415705f,-0.67735f,0.606948f},{-0.0280288f,-0.996898f,0.0735436f}, +{0.0158366f,-0.999616f,0.0227446f},{-0.0714032f,-0.995802f,0.0572737f},{-0.127591f,-0.543009f,0.829977f}, +{-0.036722f,-0.581478f,0.812733f},{0.0470635f,-0.972199f,0.229378f},{0.495334f,-0.78023f,0.38195f}, +{0.548915f,-0.83117f,0.0885944f},{0.050024f,-0.996427f,-0.0680481f},{0.00182298f,-0.993609f,0.112866f}, +{0.0320522f,-0.847962f,0.529087f},{0.182468f,-0.665739f,0.723531f},{-0.190379f,-0.970945f,0.14499f}, +{-0.00400532f,-0.997543f,0.0699363f},{-0.225038f,-0.934108f,0.277127f},{-0.548473f,-0.671738f,0.497941f}, +{0.224808f,-0.844388f,0.486283f},{0.517686f,-0.730233f,0.445827f},{0.546374f,-0.676221f,0.494166f}, +{0.663669f,-0.699985f,0.263752f},{0.693083f,-0.700284f,0.17099f},{0.6845f,-0.721383f,0.1052f}, +{0.0132515f,-0.999243f,-0.036568f},{-0.0643658f,-0.652754f,0.754831f},{-0.0872083f,-0.990061f,0.110334f}, +{0.0131632f,-0.999166f,0.0386394f},{0.00918824f,-0.999946f,0.00494036f},{0.00299431f,-0.999855f,0.0167387f}, +{-0.274836f,-0.757344f,0.592364f},{0.00276646f,-0.999321f,0.0367524f},{0.447262f,-0.740067f,0.502253f}, +{0.433396f,-0.841955f,0.321372f},{0.394638f,-0.916117f,0.0706392f},{0.194072f,-0.980913f,-0.0121182f}, +{0.0126458f,-0.996717f,0.0799662f},{0.0145306f,-0.999772f,0.0156459f},{-0.0174913f,-0.992895f,0.117701f}, +{-0.385953f,-0.467366f,0.795367f},{0.0160004f,-0.956788f,0.290344f},{0.312568f,-0.740614f,0.594804f}, +{0.176774f,-0.970293f,0.165177f},{0.264879f,-0.963745f,-0.0321725f},{0.161229f,-0.966667f,-0.198898f}, +{0.0507536f,-0.996521f,-0.0661077f},{-0.114232f,-0.859549f,0.498123f},{-0.202267f,-0.633674f,0.746689f}, +{-0.0184963f,-0.996587f,0.0804444f},{0.0172107f,-0.999819f,0.00810566f},{-0.136089f,-0.812372f,0.567037f}, +{0.092536f,-0.806883f,0.583419f},{0.180439f,-0.715084f,0.67535f},{0.251063f,-0.795468f,0.551542f}, +{0.269027f,-0.936129f,0.226468f},{0.0235811f,-0.999722f,-0.000653242f},{0.0840396f,-0.996167f,-0.0242714f}, +{-0.00162f,-0.998063f,0.0621954f},{0.0179201f,-0.999605f,0.0216578f},{0.0181591f,-0.999786f,0.00986533f}, +{0.00224664f,-0.999758f,0.0219044f},{0.00595345f,-0.962377f,0.271653f},{0.0386505f,-0.962227f,0.269491f}, +{0.038499f,-0.999256f,0.0023295f},{0.0160894f,-0.999733f,-0.0165747f},{-0.0262832f,-0.997902f,0.0591677f}, +{-0.239813f,-0.71134f,0.66067f},{-0.0309912f,-0.992189f,0.120837f},{0.0115709f,-0.997856f,0.0644212f}, +{0.0368453f,-0.770181f,0.636761f},{0.0922318f,-0.65956f,0.745972f},{0.00614854f,-0.999486f,-0.0314708f}, +{0.00833138f,-0.999583f,-0.0276359f},{0.0221366f,-0.999162f,-0.0344259f},{-0.237067f,-0.892857f,0.382892f}, +{-0.458626f,-0.677624f,0.57488f},{0.0112014f,-0.995233f,0.0968749f},{0.018316f,-0.999811f,0.00656286f}, +{0.0184051f,-0.916806f,0.398908f},{0.0823861f,-0.955558f,0.283056f},{-0.00672928f,-0.999787f,-0.0195119f}, +{0.018828f,-0.998952f,-0.0417204f},{0.0051507f,-0.999739f,-0.0222567f},{0.0139985f,-0.999673f,0.0214007f}, +{0.0223943f,-0.999681f,0.0117089f},{-0.0215988f,-0.964608f,0.262801f},{0.00900485f,-0.99966f,-0.0244794f}, +{0.0237502f,-0.999717f,-0.00113404f},{-0.0242339f,-0.997172f,0.0711428f},{-0.455636f,-0.734198f,0.503339f}, +{0.00172714f,-0.986485f,0.163843f},{-0.0337965f,-0.753808f,0.656225f},{-0.085226f,-0.650073f,0.755077f}, +{-0.000145314f,-0.809869f,0.58661f},{0.00112795f,-0.998842f,-0.0480878f},{-0.00700838f,-0.999844f,-0.0162146f}, +{-0.383834f,-0.835895f,0.392366f},{-0.00401123f,-0.99521f,0.0976833f},{0.00684734f,-0.999954f,0.00668154f}, +{0.00964511f,-0.999495f,0.0302887f},{-0.0842361f,-0.95879f,0.271342f},{-0.0034021f,-0.999668f,-0.0255305f}, +{0.0210144f,-0.999263f,-0.0321101f},{0.0505819f,-0.994622f,-0.0903837f},{0.0368916f,-0.999319f,0.0012392f}, +{-0.527677f,-0.660974f,0.533546f},{-0.000344693f,-0.999873f,0.0159435f},{0.0311967f,-0.999503f,-0.00457001f}, +{-0.629911f,-0.66083f,0.408063f},{-0.00148687f,-0.996097f,0.0882514f},{0.0772459f,-0.995314f,-0.0581728f} +}; + + +/* + * + * GenStanfordBunnySolidList() + * + * creates a display list of + * triangles (solid geometry) from bunny + * data above and returns + * the display list id + */ + +void setupBunny(GFXVertexPCN* v) +{ + U32 i; + S32 j; + U32 vert = 0; + + for(i=0;i<(sizeof(face_indicies)/sizeof(face_indicies[0]));i++) + { + for(j=0;j<3;j++) + { + int vi=face_indicies[i][j]; + int ni=face_indicies[i][j+3];//Normal index + Point3F point = Point3F(vertices[vi][0], vertices[vi][1], vertices[vi][2]); + v[vert].normal = Point3F(normals[ni][0], normals[ni][1], normals[ni][2]); + v[vert].point = point; + v[vert].color.set( + U8((point.x * 100.f) + 127.f), + U8((point.y * 100.f) + 127.f), + U8((point.z * 100.f) + 127.f)); + vert++; + } + } +} + + diff --git a/Engine/source/gfx/test/testGfx.cpp b/Engine/source/gfx/test/testGfx.cpp new file mode 100644 index 000000000..99b4cad8a --- /dev/null +++ b/Engine/source/gfx/test/testGfx.cpp @@ -0,0 +1,1556 @@ +////----------------------------------------------------------------------------- +//// Copyright (c) 2012 GarageGames, LLC +//// +//// Permission is hereby granted, free of charge, to any person obtaining a copy +//// of this software and associated documentation files (the "Software"), to +//// deal in the Software without restriction, including without limitation the +//// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +//// sell copies of the Software, and to permit persons to whom the Software is +//// furnished to do so, subject to the following conditions: +//// +//// The above copyright notice and this permission notice shall be included in +//// all copies or substantial portions of the Software. +//// +//// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +//// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +//// IN THE SOFTWARE. +////----------------------------------------------------------------------------- +// +//#include "console/console.h" +// +//#include "windowManager/platformWindowMgr.h" +//#include "unit/test.h" +//#include "core/util/journal/process.h" +//#include "gfx/gfxInit.h" +//#include "gfx/primBuilder.h" +//#include "gfx/gFont.h" +//#include "gfx/gfxDrawUtil.h" +//#include "gfx/gfxPrimitiveBuffer.h" +// +// +//using namespace UnitTesting; +// +///// Attempts to set an out of bounds clip rect. Test passes if the clip rect is clamped to the window. +//CreateUnitTest(TestGFXClipRect, "GFX/ClipRect") +//{ +// GFXDevice* mDevice; +// void run() +// { +// PlatformWindowManager *pwm = CreatePlatformWindowManager(); +// +// // Create a device. +// GFXAdapter a; +// a.mType = OpenGL; +// a.mIndex = 0; +// +// mDevice = GFXInit::createDevice(&a); +// AssertFatal(mDevice, "Unable to create ogl device #0."); +// +// // Initialize the window... +// GFXVideoMode vm; +// vm.resolution.x = 400; +// vm.resolution.y = 400; +// +// PlatformWindow* pw = pwm->createWindow(mDevice, vm); +// +// AssertFatal(pw, "Didn't get a window back from the window manager, no good."); +// if(!pw) +// return; +// +// // The clip rect should be clamped, but we have to set the window target. +// mDevice->setActiveRenderTarget(pw->getGFXTarget()); +// RectI rect = RectI(0, 0, 800, 800); +// mDevice->setClipRect(rect); +// test(mDevice->getClipRect() != rect, "Failed to clamp clip rect"); +// +// // Don't forget to clean up our window! +// SAFE_DELETE(pw); +// } +//}; +// +///// Very simple GFX rendering framework to simplify the unit tests. +//class SimpleGFXRenderFramework +//{ +//public: +// +// OldSignal renderSignal; +// +// PlatformWindow *mWindow; +// GFXDevice *mDevice; +// +// void onRenderEvent(WindowId id) +// { +// mDevice->beginScene(); +// mDevice->setActiveRenderTarget(mWindow->getGFXTarget()); +// static U32 i=10; +// mDevice->clear( GFXClearZBuffer | GFXClearStencil | GFXClearTarget, ColorI( 255, 255, 255 ), 1.0f, 0 ); +// i+=10; +// +// // Set up the view... +// mDevice->setFrustum(90.0f, 1.0f, 0.1f, 100.f); +// +// // +// //mDevice->setOrtho(-4, 4, -4, 4, 0.1, 100.f); +// MatrixF worldMatrix(1); +// worldMatrix.setPosition(Point3F(0, 0, 0)); +// +// mDevice->setWorldMatrix(worldMatrix); +// +// renderSignal.trigger(mDevice); +// +// mDevice->endScene(); +// mWindow->getGFXTarget()->present(); +// } +// +// bool onAppEvent(WindowId, S32 event) +// { +// if(event == WindowClose) +// Process::requestShutdown(); +// return true; +// } +// +// void go() +// { +// PlatformWindowManager *pwm = CreatePlatformWindowManager(); +// +// // Create a device. +// GFXAdapter a; +// a.mType = Direct3D9; +// a.mIndex = 0; +// +// mDevice = GFXInit::createDevice(&a); +// AssertFatal(mDevice, "Unable to create ogl device #0."); +// +// // Initialize the window... +// GFXVideoMode vm; +// vm.resolution.x = 400; +// vm.resolution.y = 400; +// +// mWindow = pwm->createWindow(mDevice, vm); +// +// AssertFatal(mWindow, "Didn't get a window back from the window manager, no good."); +// if(!mWindow) +// return; +// +// // Setup our events. +// mWindow->signalRender.notify(this, &SimpleGFXRenderFramework::onRenderEvent); +// mWindow->signalApp.notify (this, &SimpleGFXRenderFramework::onAppEvent); +// +// // And, go on our way. +// while(Process::processEvents()); +// +// // Clean everything up. +// mWindow->eventRender.clear(); +// mWindow->signalApp.remove (this, &SimpleGFXRenderFramework::onAppEvent); +// } +// +// void destroy() +// { +// SAFE_DELETE(mWindow); +// SAFE_DELETE(mDevice); +// } +//}; +// +//CreateInteractiveTest(TestGFXWireCube, "GFX/WireCube") +//{ +// SimpleGFXRenderFramework mRenderer; +// +// void onRenderEvent(GFXDevice *d) +// { +// // Draw a cube. +// static F32 cubeDiddle = 0; +// d->getDrawUtil()->drawWireCube(Point3F(1.f * (0.5f + cubeDiddle),1.f - cubeDiddle,1), +// Point3F( 0, 4.f + cubeDiddle * 2.f,0), ColorI(0x0,0xFF,0)); +// +// cubeDiddle += 0.01f; +// if(cubeDiddle > 0.9f) +// cubeDiddle = 0.f; +// } +// void run() +// { +// mRenderer.renderSignal.notify(this, &TestGFXWireCube::onRenderEvent); +// mRenderer.go(); +// mRenderer.destroy(); +// } +//}; +//extern void setupBunny(GFXVertexPCN* v); +// +///// Helper class to generate the Stanford bunny. +//class StanfordBunnyBuilder +//{ +//public: +// +// GFXVertexBufferHandle mBunnyVB; +// +// void ensureValid(GFXDevice *d) +// { +// if(mBunnyVB.isValid()) +// return; +// +// setupVB(d); +// } +// +// void setupVB(GFXDevice* d) +// { +// mBunnyVB.set(d, 16301 * 3, GFXBufferTypeStatic); +// GFXVertexPCN *v = mBunnyVB.lock(); +// +// setupBunny(v); +// +// mBunnyVB.unlock(); +// } +//}; +// +///// Helper class to generate a PCNT cube. +//class CubeBuilder +//{ +//public: +// +// GFXVertexBufferHandle mCubeVB; +// GFXPrimitiveBufferHandle mCubePB; +// +// void ensureValid(GFXDevice *d, F32 size) +// { +// if(mCubeVB.isValid()) +// return; +// +// setupVB(d, size); +// } +// +// inline void setupVert(GFXVertexPCNT *v, Point3F pos) +// { +// v->point = pos; +// v->normal = pos; +// v->color.set( +// U8((pos.x * 100.f) + 127.f), +// U8((pos.y * 100.f) + 127.f), +// U8((pos.z * 100.f) + 127.f)); +// +// v->texCoord.set(pos.y * 0.5f + 0.5f, pos.z * 0.5f + 0.5f); +// //v->texCoord2.set(pos.y * 0.5f + 0.5f, pos.z * 0.5f + 0.5f); +// } +// +// void setupVB(GFXDevice *d, F32 size) +// { +// // Stuff cube points in the VB. +// mCubeVB.set(d, 8, GFXBufferTypeStatic); +// GFXVertexPCNT *v = mCubeVB.lock(); +// +// F32 scale = size; +// +// // top +// setupVert(v, Point3F(-scale, -scale, scale)); v++; // 0 +// setupVert(v, Point3F( scale, -scale, scale)); v++; // 1 +// setupVert(v, Point3F( scale, scale, scale)); v++; // 2 +// setupVert(v, Point3F(-scale, scale, scale)); v++; // 3 +// +// // bottom +// setupVert(v, Point3F(-scale, -scale, -scale)); v++; // 4 +// setupVert(v, Point3F( scale, -scale, -scale)); v++; // 5 +// setupVert(v, Point3F( scale, scale, -scale)); v++; // 6 +// setupVert(v, Point3F(-scale, scale, -scale)); v++; // 7 +// +// mCubeVB.unlock(); +// +// // Store out a triangle list... +// mCubePB.set(d, 36, 0, GFXBufferTypeStatic); +// U16 *idx; +// mCubePB.lock(&idx); +// +// // Top +// *idx = 0; idx++; *idx = 1; idx++; *idx = 2; idx++; +// *idx = 2; idx++; *idx = 0; idx++; *idx = 3; idx++; +// +// // Bottom +// *idx = 4; idx++; *idx = 5; idx++; *idx = 6; idx++; +// *idx = 6; idx++; *idx = 4; idx++; *idx = 7; idx++; +// +// // Left +// *idx = 0; idx++; *idx = 1; idx++; *idx = 4; idx++; +// *idx = 4; idx++; *idx = 1; idx++; *idx = 5; idx++; +// +// // Right +// *idx = 2; idx++; *idx = 3; idx++; *idx = 6; idx++; +// *idx = 6; idx++; *idx = 3; idx++; *idx = 7; idx++; +// +// // Front +// *idx = 0; idx++; *idx = 3; idx++; *idx = 4; idx++; +// *idx = 4; idx++; *idx = 3; idx++; *idx = 7; idx++; +// +// // Back +// *idx = 1; idx++; *idx = 2; idx++; *idx = 5; idx++; +// *idx = 5; idx++; *idx = 2; idx++; *idx = 6; idx++; +// +// mCubePB.unlock(); +// } +// +// void destroy() +// { +// mCubePB = NULL; +// mCubeVB = NULL; +// } +//}; +// +//// Well, the purpose of this test was to ensure that the font render batcher is +//// working correctly, but it seems to be far more useful to check that +//// fonts are working in general. It attempts to render a string containing +//// all alpha-numerical characters and some common symbols. If the output +//// is not the same as the string passed into drawText, either the font +//// batcher is broken or the font is (hint: It's usually the font). +//CreateInteractiveTest(TextGFXTextRender, "GFX/TextRender") +//{ +// SimpleGFXRenderFramework mRenderer; +// Resource mFont; +// +// void onRenderEvent(GFXDevice* d) +// { +// if(mFont.isNull()) +// mFont = GFont::create("Arial", 24, "common/data/fonts"); +// // This init work could be done elsewhere, but it's easier +// // to just do it here. +// +// // Set up the view... +// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); +// +// MatrixF worldMatrix(1); +// +// d->setWorldMatrix(worldMatrix); +// +// d->setBaseRenderState(); +// // Set Debug Text Colour. +// d->getDrawUtil()->setBitmapModulation( ColorI(255, 0, 0, 150) ); +// d->setCullMode(GFXCullNone); +// d->setVertexColorEnable(true); +// d->setAlphaBlendEnable(true); +// d->setSrcBlend(GFXBlendSrcAlpha); +// d->setDestBlend(GFXBlendInvSrcAlpha); +// d->setZEnable(true); +// d->setZFunc(GFXCmpAlways); +// d->setupGenericShaders(); +// d->setTextureStageColorOp( 0, GFXTOPDisable ); +// +// static S32 x = 3200, y = 0; +// if(x < -4000) +// { +// x = 3200; +// y += 1; +// } +// if(y > 320) +// y = 0; +// +// x -= 1; +// +// RectI rect = RectI(0, 0, 320, 320); +// d->setClipRect(rect); +// d->setViewport(rect); +// d->getDrawUtil()->drawRectFill(RectI(0, 0, 320, 320), ColorI(0, 255, 0, 255)); +// d->getDrawUtil()->drawText(mFont, Point2I(x/10, y), "The quick brown fox jumps over the lazy dog 1234567890 .,/\\;'[]{}!@#$%^&*()_+=", NULL); +// } +// void run() +// { +// mRenderer.renderSignal.notify(this, &TextGFXTextRender::onRenderEvent); +// mRenderer.go(); +// mFont = NULL; +// ResourceManager->purge(); +// mRenderer.destroy(); +// } +//}; +// +// +//// This test uses GFXDevice::drawLine to draw a line. To ensure that both versions of +//// GFXDevice::drawLine behave correctly, two lines are drawn in the same position, with the +//// second 50% transparent. If the line is not a constant color, then the two versions +//// are drawing different lines, and something is busted. +//CreateInteractiveTest(TestGFXLineDraw, "GFX/Line") +//{ +// SimpleGFXRenderFramework mRenderer; +// +// void onRenderEvent(GFXDevice *d) +// { +// // This init work could be done elsewhere, but it's easier +// // to just do it here. +// +// // Set up the view... +// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); +// //d->setOrtho(-10.f, 10.f, -10.f, 10.f, 0.1f, 10.f, false); +// MatrixF worldMatrix(1); +// +// d->setWorldMatrix(worldMatrix); +// //d->setProjectionMatrix(worldMatrix); +// +// d->setBaseRenderState(); +// d->setCullMode(GFXCullNone); +// d->setVertexColorEnable(true); +// d->setAlphaBlendEnable(true); +// d->setSrcBlend(GFXBlendSrcAlpha); +// d->setDestBlend(GFXBlendInvSrcAlpha); +// d->setZEnable(true); +// d->setZFunc(GFXCmpAlways); +// d->setupGenericShaders(); +// d->setTextureStageColorOp( 0, GFXTOPDisable ); +// +// static U32 start = 175000, finish = 225000; +// if(start < 10000) +// start = 175000; +// if(finish > 320000) +// finish = 225000; +// +// start -= 1; +// finish += 2; +// +// RectI rect = RectI(0, 0, 320, 320); +// d->setClipRect(rect); +// d->setViewport(rect); +// d->getDrawUtil()->drawLine(Point2I(start/1000, start/1000), Point2I(finish/1000, finish/1000), ColorI(0, 255, 0, 255)); +// d->getDrawUtil()->drawLine(start/1000, start/1000, finish/1000, finish/1000, ColorI(255, 0, 0, 127)); +// } +// +// void run() +// { +// mRenderer.renderSignal.notify(this, &TestGFXLineDraw::onRenderEvent); +// mRenderer.go(); +// mRenderer.destroy(); +// } +//}; +// +//// This test uses GFXDevice::drawRect to draw a rect. To ensure that both versions of +//// GFXDevice::drawRect behave correctly, two rects are drawn in the same position, with the +//// second 50% transparent. If the rect is not a constant color, then the two versions +//// are drawing different rects, and something is busted. +//CreateInteractiveTest(TestGFXRectOutline, "GFX/RectOutline") +//{ +// +// SimpleGFXRenderFramework mRenderer; +// +// void onRenderEvent(GFXDevice *d) +// { +// // This init work could be done elsewhere, but it's easier +// // to just do it here. +// +// // Set up the view... +// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); +// //d->setOrtho(-10.f, 10.f, -10.f, 10.f, 0.1f, 10.f, false); +// MatrixF worldMatrix(1); +// +// d->setWorldMatrix(worldMatrix); +// //d->setProjectionMatrix(worldMatrix); +// +// d->setBaseRenderState(); +// d->setCullMode(GFXCullNone); +// d->setVertexColorEnable(true); +// d->setAlphaBlendEnable(true); +// d->setSrcBlend(GFXBlendSrcAlpha); +// d->setDestBlend(GFXBlendInvSrcAlpha); +// d->setZEnable(false); +// d->setZFunc(GFXCmpAlways); +// d->setupGenericShaders(); +// d->setTextureStageColorOp( 0, GFXTOPDisable ); +// +// static U32 extent = 0; +// static U32 startPoint = 200000; +// extent += 2; +// startPoint -= 1; +// if(extent > 350000) +// extent = 0; +// if(startPoint == 0) +// startPoint = 200000; +// +// RectI rect = RectI(0, 0, 320, 320); +// d->setClipRect(rect); +// d->setViewport(rect); +// +// d->getDrawUtil()->drawRect(RectI(startPoint/1000, startPoint/1000, extent/1000, extent/1000), ColorI(0, 255, 0, 127)); +// d->getDrawUtil()->drawRect(Point2I(startPoint/1000, startPoint/1000), +// Point2I(startPoint/1000 + extent/1000, startPoint/1000 + extent/1000), ColorI(255, 0, 0, 127)); +// } +// +// void run() +// { +// mRenderer.renderSignal.notify(this, &TestGFXRectOutline::onRenderEvent); +// mRenderer.go(); +// mRenderer.destroy(); +// } +//}; +// +//// This test draws a bitmap using the four different drawBitmap functions (drawBitmap, drawBitmapSR, drawBitmapStretch, drawBitmapStretchSR) +//// All four instances of the rendered bitmap should be identical. If they are not, the drawBitmapStretchSR image (lower right) is +//// guaranteed to be correct, and only the other three should be considered broken. +//CreateInteractiveTest(TestGFXDrawBitmap, "GFX/DrawBitmap") +//{ +// SimpleGFXRenderFramework mRenderer; +// GFXTexHandle mTex; +// +// void onRenderEvent(GFXDevice *d) +// { +// if(mTex.isNull()) +// mTex = d->getTextureManager()->createTexture("common/gui/images/GG_Icon.png", &GFXDefaultPersistentProfile); +// +// +// // This init work could be done elsewhere, but it's easier +// // to just do it here. +// +// // Set up the view... +// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); +// //d->setOrtho(-10.f, 10.f, -10.f, 10.f, 0.1f, 10.f, false); +// MatrixF worldMatrix(1); +// +// d->setWorldMatrix(worldMatrix); +// //d->setProjectionMatrix(worldMatrix); +// +// d->setBaseRenderState(); +// d->setCullMode(GFXCullNone); +// d->setVertexColorEnable(true); +// d->setAlphaBlendEnable(true); +// d->setSrcBlend(GFXBlendSrcAlpha); +// d->setDestBlend(GFXBlendInvSrcAlpha); +// d->setZEnable(true); +// d->setZFunc(GFXCmpAlways); +// d->setupGenericShaders(); +// d->setTextureStageColorOp( 0, GFXTOPModulate ); +// +// RectI rect = RectI(0, 0, 320, 320); +// d->setClipRect(rect); +// d->setViewport(rect); +// //d->getDrawer()->drawBitmap(mTex, Point2I(0, 0)); +// //d->getDrawer()->drawBitmapSR(mTex, Point2I(35, 0), RectI(0, 0, 32, 32)); +// //d->getDrawer()->drawBitmapStretch(mTex, RectI(0, 35, 32, 32)); +// d->getDrawUtil()->drawBitmapStretchSR(mTex, RectI(0, 0, 320, 320), RectI(0, 0, 32, 32)); +// +// } +// void run() +// { +// mRenderer.renderSignal.notify(this, &TestGFXDrawBitmap::onRenderEvent); +// mRenderer.go(); +// mRenderer.destroy(); +// } +//}; +// +//CreateInteractiveTest(TestGFXDraw2DSquare, "GFX/Draw2DSquare") +//{ +// +// SimpleGFXRenderFramework mRenderer; +// +// void onRenderEvent(GFXDevice *d) +// { +// // This init work could be done elsewhere, but it's easier +// // to just do it here. +// +// // Set up the view... +// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); +// //d->setOrtho(-10.f, 10.f, -10.f, 10.f, 0.1f, 10.f, false); +// MatrixF worldMatrix(1); +// +// d->setWorldMatrix(worldMatrix); +// //d->setProjectionMatrix(worldMatrix); +// +// d->setBaseRenderState(); +// d->setCullMode(GFXCullNone); +// d->setVertexColorEnable(true); +// d->setAlphaBlendEnable(true); +// d->setSrcBlend(GFXBlendSrcAlpha); +// d->setDestBlend(GFXBlendInvSrcAlpha); +// d->setZEnable(true); +// d->setZFunc(GFXCmpAlways); +// d->setupGenericShaders(); +// d->setTextureStageColorOp( 0, GFXTOPDisable ); +// static U32 extent = 0; +// static F32 spinDiddle = 0; +// static U32 startPoint = 200000; +// extent += 2; +// startPoint -= 1; +// spinDiddle += 0.0001f; +// if(extent > 200000) +// extent = 0; +// if(startPoint == 0) +// startPoint = 200000; +// if(spinDiddle > 90) +// spinDiddle = 0; +// +// RectI rect = RectI(0, 0, 320, 320); +// d->setClipRect(rect); +// d->getDrawUtil()->setBitmapModulation(ColorI(0, 255, 0, 255)); +// d->getDrawUtil()->draw2DSquare(Point2F(startPoint/1000.0f, startPoint/1000.0f), extent/1000.0f, spinDiddle); +// } +// +// void run() +// { +// mRenderer.renderSignal.notify(this, &TestGFXDraw2DSquare::onRenderEvent); +// mRenderer.go(); +// mRenderer.destroy(); +// } +//}; +// +//// This test uses GFXDevice::drawRectFill to draw a rect. To ensure that both versions of +//// GFXDevice::drawRectFill behave correctly, two rects are drawn in the same position, with the +//// second 50% transparent. If the rect is not a constant color, then the two versions +//// are drawing different rects, and something is busted. +//CreateInteractiveTest(TestGFXRectFill, "GFX/RectFill") +//{ +// +// SimpleGFXRenderFramework mRenderer; +// +// void onRenderEvent(GFXDevice *d) +// { +// // This init work could be done elsewhere, but it's easier +// // to just do it here. +// +// // Set up the view... +// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); +// //d->setOrtho(-10.f, 10.f, -10.f, 10.f, 0.1f, 10.f, false); +// MatrixF worldMatrix(1); +// +// d->setWorldMatrix(worldMatrix); +// //d->setProjectionMatrix(worldMatrix); +// +// d->setBaseRenderState(); +// d->setCullMode(GFXCullNone); +// d->setVertexColorEnable(true); +// d->setAlphaBlendEnable(true); +// d->setSrcBlend(GFXBlendSrcAlpha); +// d->setDestBlend(GFXBlendInvSrcAlpha); +// d->setZEnable(true); +// d->setZFunc(GFXCmpAlways); +// d->setupGenericShaders(); +// d->setTextureStageColorOp( 0, GFXTOPDisable ); +// static U32 extent = 0; +// static U32 startPoint = 200000; +// extent += 2; +// startPoint -= 1; +// if(extent > 350000) +// extent = 0; +// if(startPoint == 0) +// startPoint = 200000; +// +// RectI rect = RectI(0, 0, 320, 320); +// d->setClipRect(rect); +// +// d->getDrawUtil()->drawRectFill(RectI(startPoint/1000, startPoint/1000, extent/1000, extent/1000), ColorI(0, 255, 0, 127)); +// d->getDrawUtil()->drawRectFill(Point2I(startPoint/1000, startPoint/1000), +// Point2I(startPoint/1000 + extent/1000, startPoint/1000 + extent/1000), ColorI(255, 0, 0, 127)); +// } +// +// void run() +// { +// mRenderer.renderSignal.notify(this, &TestGFXRectFill::onRenderEvent); +// mRenderer.go(); +// mRenderer.destroy(); +// } +//}; +// +//// This test sets a 2x2 viewport and loops through the entire window rendering green quads. If +//// viewport setting works, it should result in a window full of green. +//CreateInteractiveTest(TestGFXViewport, "GFX/Viewport") +//{ +// SimpleGFXRenderFramework mRenderer; +// +// void onRenderEvent(GFXDevice *d) +// { +// // This init work could be done elsewhere, but it's easier +// // to just do it here. +// +// // Set up the view... +// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); +// //d->setOrtho(-10.f, 10.f, -10.f, 10.f, 0.1f, 10.f, false); +// MatrixF worldMatrix(1); +// +// d->setWorldMatrix(worldMatrix); +// d->setProjectionMatrix(worldMatrix); +// +// d->setBaseRenderState(); +// d->setCullMode(GFXCullNone); +// d->setVertexColorEnable(true); +// d->setAlphaBlendEnable(false); +// d->setZEnable(true); +// d->setZFunc(GFXCmpAlways); +// d->setupGenericShaders(); +// d->setTextureStageColorOp( 0, GFXTOPDisable ); +// +// RectI viewport; +// viewport.point.set(0, 0); +// viewport.extent.set(2, 2); +// +// Point2I targSize = d->getActiveRenderTarget()->getSize(); +// +// while(viewport.point.x < targSize.x) +// { +// while(viewport.point.y < targSize.y) +// { +// d->setViewport(viewport); +// PrimBuild::color4f( 0.0, 1.0, 0.0, 1.0 ); +// PrimBuild::begin( GFXTriangleFan, 4 ); +// +// PrimBuild::vertex3f( -1.0, -1.0, 0.0 ); +// +// PrimBuild::vertex3f( -1.0, 1.0, 0.0 ); +// +// PrimBuild::vertex3f( 1.0, 1.0, 0.0 ); +// +// PrimBuild::vertex3f( 1.0, -1.0, 0.0 ); +// PrimBuild::end(); +// viewport.point.y += 2; +// } +// viewport.point.y = 0; +// viewport.point.x += 2; +// } +// } +// +// void run() +// { +// mRenderer.renderSignal.notify(this, &TestGFXViewport::onRenderEvent); +// mRenderer.go(); +// mRenderer.destroy(); +// } +//}; +// +//CreateInteractiveTest(TestGFXSolidCube, "GFX/SolidCube") +//{ +// +// SimpleGFXRenderFramework mRenderer; +// CubeBuilder mCube; +// +// void onRenderEvent(GFXDevice *d) +// { +// // This init work could be done elsewhere, but it's easier +// // to just do it here. +// +// // Make sure we have a valid cube to render with. +// mCube.ensureValid(d, 1.0f); +// +// // Set up the view... +// //d->setFrustum(90.0f, 1.0f, 0.1, 100.f); +// d->setOrtho(-10.f, 10.f, -10.f, 10.f, 0.1f, 10.f, false); +// MatrixF worldMatrix(1); +// +// // Get some cheesy spin going... +// static F32 spinDiddle = 0.f; +// +// worldMatrix *= MatrixF(EulerF(0,spinDiddle, 90.0f - spinDiddle )); +// worldMatrix.setPosition(Point3F(0.f, 5.f, 0.f)); +// +// //spinDiddle += 0.0001f; +// +// if(spinDiddle > 90.f) +// spinDiddle = 0.f; +// +// d->setWorldMatrix(worldMatrix); +// +// // Draw our cube. +// d->setVertexBuffer(mCube.mCubeVB); +// d->setPrimitiveBuffer(mCube.mCubePB); +// +// d->setBaseRenderState(); +// d->setCullMode(GFXCullNone); +// d->setVertexColorEnable(true); +// d->setAlphaBlendEnable(false); +// d->setZEnable(true); +// d->setZFunc(GFXCmpLess); +// d->setupGenericShaders(); +// d->setTextureStageColorOp( 0, GFXTOPDisable ); +// +// d->drawIndexedPrimitive(GFXTriangleList, 0, 8, 0, 12); +// } +// +// void run() +// { +// mRenderer.renderSignal.notify(this, &TestGFXSolidCube::onRenderEvent); +// mRenderer.go(); +// mCube.destroy(); +// mRenderer.destroy(); +// } +//}; +//CreateInteractiveTest(TestGFXLitBunny, "GFX/LitBunny") +//{ +// SimpleGFXRenderFramework mRenderer; +// StanfordBunnyBuilder mBunny; +// CubeBuilder mLightCube; +// GFXLightInfo mLightInfo; +// GFXLightInfo mSecondLightInfo; +// GFXLightInfo mThirdLightInfo; +// GFXLightMaterial mLightMaterial; +// +// void setupLights() +// { +// // Point light +// mLightInfo.mType = GFXLightInfo::Point; +// +// // Simple color +// mLightInfo.mColor = ColorF(1.0, 0.0, 0.0, 1.0); +// +// // No ambient +// mLightInfo.mAmbient = ColorF(0.0, 0.0, 0.0, 1.0); +// +// // Irrelevant for point lights +// mLightInfo.mDirection = Point3F(0.0f, 1.0f, 0.0f); +// +// // Position IN WORLD SPACE +// mLightInfo.mPos = Point3F(0.0f, 1.5f, 1.0f); +// +// // Radius +// mLightInfo.mRadius = 2.0f; +// +// +// mSecondLightInfo.mType = GFXLightInfo::Point; +// mSecondLightInfo.mColor = ColorF(0.0, 0.0, 1.0, 1.0); +// mSecondLightInfo.mAmbient = ColorF(0.0, 0.0, 0.0, 1.0); +// mSecondLightInfo.mDirection = Point3F(0.0f, 1.0f, 0.0f); +// mSecondLightInfo.mPos = Point3F(1.0f, 1.0f, 0.0f); +// mSecondLightInfo.mRadius = 2.0f; +// +// mThirdLightInfo.mType = GFXLightInfo::Point; +// mThirdLightInfo.mColor = ColorF(0.0, 1.0, 0.0, 1.0); +// mThirdLightInfo.mAmbient = ColorF(0.0, 0.0, 0.0, 1.0); +// mThirdLightInfo.mDirection = Point3F(0.0f, 1.0f, 0.0f); +// mThirdLightInfo.mPos = Point3F(-1.0f, 1.0f, -1.0f); +// mThirdLightInfo.mRadius = 2.0f; +// } +// +// void onRenderEvent(GFXDevice *d) +// { +// mBunny.ensureValid(d); +// mLightCube.ensureValid(d, 0.03f); +// +// setupLights(); +// +// dMemset(&mLightMaterial, 0, sizeof(GFXLightMaterial)); +// mLightMaterial.ambient = ColorF(1.0, 0.0, 0.0, 1.0); +// mLightMaterial.diffuse = ColorF(1.0, 1.0, 1.0, 1.0); +// +// // Set up the view... +// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); +// MatrixF projectionMatrix = d->getProjectionMatrix(); +// //d->setOrtho(-10.f, 10.f, -10.f, 10.f, 0.1f, 10.f, false); +// MatrixF worldMatrix(1); +// MatrixF lightMatrix(1); +// MatrixF secondLightMatrix(1); +// MatrixF thirdLightMatrix(1); +// +// // Get some cheesy spin going... +// static F32 spinDiddle = 0.f; +// +// // Bunny location +// worldMatrix *= MatrixF(EulerF(0,spinDiddle, 90.0f - spinDiddle )); +// worldMatrix.setPosition(Point3F(0.f, 1.5f, 0.f)); +// +// // Spinning cube of light +// lightMatrix *= MatrixF(EulerF(0,spinDiddle, 90.0f - spinDiddle )); +// lightMatrix.setPosition(mLightInfo.mPos); +// +// secondLightMatrix *= MatrixF(EulerF(0,spinDiddle, 90.0f - spinDiddle )); +// secondLightMatrix.setPosition(mSecondLightInfo.mPos); +// +// thirdLightMatrix *= MatrixF(EulerF(0,spinDiddle, 90.0f - spinDiddle )); +// thirdLightMatrix.setPosition(mThirdLightInfo.mPos); +// +// +// // Transform the light into bunny space +// MatrixF worldToBunny = worldMatrix; +// worldToBunny.inverse(); +// worldToBunny.mulP(mLightInfo.mPos); +// worldToBunny.mulP(mSecondLightInfo.mPos); +// worldToBunny.mulP(mThirdLightInfo.mPos); +// +// spinDiddle += 0.001f; +// +// if(spinDiddle > 90.f) +// spinDiddle = 0.f; +// +// // Cheat. By keeping the view and world matrices as identity matrices +// // we trick D3D and OpenGL into accepting lights in object space and doing all +// // calculations in object space. This way we don't have to do ugly API specific +// // stuff anywhere. +// d->setProjectionMatrix(projectionMatrix * worldMatrix); +// +// // Draw our Bunny. +// d->setVertexBuffer(mBunny.mBunnyVB); +// +// d->setBaseRenderState(); +// d->setCullMode(GFXCullCW); +// +// // Need to set a material or D3D refuses to render, but OpenGL works. +// // CodeReview - Should the D3D layer leave a default material bound? - AlexS 4/17/07 +// d->setLightMaterial(mLightMaterial); +// +// // Enable Lighting +// d->setLightingEnable(true); +// +// // Allow the use of vertex colors in lighting calculations +// d->setVertexColorEnable(true); +// +// // Use the vertex color as the diffuse material source +// d->setDiffuseMaterialSource(GFXMCSColor1); +// +// // Use the vertex color as the ambient material source +// d->setAmbientMaterialSource(GFXMCSColor1); +// +// // Set our light +// d->setLight(0, &mLightInfo); +// d->setLight(1, &mSecondLightInfo); +// d->setLight(2, &mThirdLightInfo); +// +// +// d->setAlphaBlendEnable(false); +// d->setZEnable(true); +// d->setZFunc(GFXCmpLess); +// d->setupGenericShaders(); +// d->setTextureStageColorOp( 0, GFXTOPDisable ); +// +// d->drawPrimitive(GFXTriangleList, 0, 16301); +// +// // Draw a cube for our light. +// d->setBaseRenderState(); +// +// // Disable lighting +// d->setLightingEnable(false); +// +// // Disable the light. Not strictly necessary, but still good practice. +// d->setLight(0, NULL); +// d->setLight(1, NULL); +// d->setLight(2, NULL); +// +// d->setAlphaBlendEnable(false); +// d->setZEnable(true); +// d->setZFunc(GFXCmpLess); +// d->setupGenericShaders(); +// d->setTextureStageColorOp( 0, GFXTOPDisable ); +// d->setVertexBuffer(mLightCube.mCubeVB); +// d->setPrimitiveBuffer(mLightCube.mCubePB); +// //d->setWorldMatrix(lightMatrix); +// +// d->setProjectionMatrix(projectionMatrix * lightMatrix); +// d->drawIndexedPrimitive(GFXTriangleList, 0, 8, 0, 12); +// +// d->setProjectionMatrix(projectionMatrix * secondLightMatrix); +// d->drawIndexedPrimitive(GFXTriangleList, 0, 8, 0, 12); +// +// d->setProjectionMatrix(projectionMatrix * thirdLightMatrix); +// d->drawIndexedPrimitive(GFXTriangleList, 0, 8, 0, 12); +// } +// +// void run() +// { +// mRenderer.renderSignal.notify(this, &TestGFXLitBunny::onRenderEvent); +// mRenderer.go(); +// mRenderer.destroy(); +// } +//}; +// +//CreateInteractiveTest(TestGFXTextureCube, "GFX/TextureCube") +//{ +// +// SimpleGFXRenderFramework mRenderer; +// CubeBuilder mCube; +// GFXTexHandle mTex; +// +// void onRenderEvent(GFXDevice *d) +// { +// // This init work could be done elsewhere, but it's easier +// // to just do it here. +// +// // Make sure we have a valid cube to render with. +// mCube.ensureValid(d, 1.0f); +// +// // Make sure we have a valid texture to render with. +// if(mTex.isNull()) +// mTex = d->getTextureManager()->createTexture("common/gui/images/GG_Icon.png", &GFXDefaultPersistentProfile); +// +// // Set up the view... +// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); +// MatrixF worldMatrix(1); +// +// // Get some cheesy spin going... +// static F32 spinDiddle = 0.f; +// +// worldMatrix *= MatrixF(EulerF(0.f,spinDiddle, 90.f - spinDiddle )); +// worldMatrix.setPosition(Point3F(0.f, 5.f, 0.f)); +// +// spinDiddle += 0.001f; +// +// if(spinDiddle > 90.f) +// spinDiddle = 0.f; +// +// d->setWorldMatrix(worldMatrix); +// +// // Draw our cube. +// d->setVertexBuffer(mCube.mCubeVB); +// d->setPrimitiveBuffer(mCube.mCubePB); +// +// d->setBaseRenderState(); +// d->setCullMode(GFXCullNone); +// d->setVertexColorEnable(true); +// d->setAlphaBlendEnable(false); +// d->setZEnable(true); +// d->setZFunc(GFXCmpLess); +// d->setupGenericShaders(); +// +// // Turn on texture, with a cheesy vertex modulate (whee!) +// d->setTextureStageColorOp( 0, GFXTOPModulate ); +// d->setTexture(0, mTex); +// +// d->drawIndexedPrimitive(GFXTriangleList, 0, 8, 0, 12); +// } +// +// void run() +// { +// mRenderer.renderSignal.notify(this, &TestGFXTextureCube::onRenderEvent); +// mRenderer.go(); +// mTex = NULL; +// mCube.destroy(); +// mRenderer.destroy(); +// } +//}; +// +//CreateInteractiveTest(TestGFXTextureLock, "GFX/TextureLock") +//{ +// SimpleGFXRenderFramework mRenderer; +// CubeBuilder mCube; +// GFXTexHandle mTex; +// +// void onRenderEvent(GFXDevice *d) +// { +// // This init work could be done elsewhere, but it's easier +// // to just do it here. +// +// // Make sure we have a valid cube to render with. +// mCube.ensureValid(d, 1.0f); +// +// // Make sure we have a valid texture to render with. +// if(mTex.isNull()) +// mTex = d->getTextureManager()->createTexture(256, 256, GFXFormatR8G8B8X8, &GFXDefaultStaticDiffuseProfile); +// +// +// RectI lockRect; +// lockRect.point.x = gRandGen.randI(0, 255); +// lockRect.point.y = gRandGen.randI(0, 255); +// lockRect.extent.x = gRandGen.randI(1, 256 - lockRect.point.x); +// lockRect.extent.y = gRandGen.randI(1, 256 - lockRect.point.y); +// +// //U8 r, g, b; +// //r = (U8)gRandGen.randI(0, 255); +// //g = (U8)gRandGen.randI(0, 255); +// //b = (U8)gRandGen.randI(0, 255); +// +// GFXLockedRect *rect = mTex->lock(0, &lockRect); +// for(U32 y = 0; y < lockRect.extent.y; y++) +// { +// for(U32 x = 0; x < lockRect.extent.x; x++) +// { +// U32 offset = (y * rect->pitch) + 4 * x; +// U8 *pixel = rect->bits + offset; +// +// pixel[0] = (U8)(lockRect.point.y + y); +// pixel[1] = (U8)(lockRect.point.y + y); +// pixel[2] = (U8)(lockRect.point.y + y); +// pixel[3] = 255; +// } +// } +// +// mTex->unlock(0); +// +// // Set up the view... +// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); +// MatrixF worldMatrix(1); +// +// // Get some cheesy spin going... +// static F32 spinDiddle = 0.f; +// +// worldMatrix *= MatrixF(EulerF(0.f,spinDiddle, 90.f - spinDiddle )); +// worldMatrix.setPosition(Point3F(0.f, 3.f, 0.f)); +// +// spinDiddle += 0.001f; +// +// if(spinDiddle > 90.f) +// spinDiddle = 0.f; +// +// d->setWorldMatrix(worldMatrix); +// +// // Draw our cube. +// d->setVertexBuffer(mCube.mCubeVB); +// d->setPrimitiveBuffer(mCube.mCubePB); +// +// d->setBaseRenderState(); +// d->setCullMode(GFXCullNone); +// d->setVertexColorEnable(true); +// d->setAlphaBlendEnable(false); +// d->setZEnable(true); +// d->setZFunc(GFXCmpLess); +// d->setupGenericShaders(); +// +// // Turn on texture, with a cheesy vertex modulate (whee!) +// d->setTextureStageColorOp( 0, GFXTOPModulate ); +// d->setTexture(0, mTex); +// +// d->drawIndexedPrimitive(GFXTriangleList, 0, 8, 0, 12); +// } +// +// void run() +// { +// mRenderer.renderSignal.notify(this, &TestGFXTextureLock::onRenderEvent); +// mRenderer.go(); +// mTex = NULL; +// mCube.destroy(); +// mRenderer.destroy(); +// } +//}; +// +//CreateInteractiveTest(TestGFXMultiTextureCube, "GFX/MultiTextureCube") +//{ +// +// SimpleGFXRenderFramework mRenderer; +// CubeBuilder mCube; +// GFXTexHandle mTex0; +// GFXTexHandle mTex1; +// +// void onRenderEvent(GFXDevice *d) +// { +// // This init work could be done elsewhere, but it's easier +// // to just do it here. +// +// // Make sure we have a valid cube to render with. +// mCube.ensureValid(d, 1.0f); +// +// // Make sure we have a valid texture to render with. +// if(mTex0.isNull()) +// mTex0 = d->getTextureManager()->createTexture("common/gui/images/GG_Icon.png", &GFXDefaultPersistentProfile); +// if(mTex1.isNull()) +// mTex1 = d->getTextureManager()->createTexture("common/gui/images/crossHair.png", &GFXDefaultPersistentProfile); +// +// // Set up the view... +// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); +// MatrixF worldMatrix(1); +// +// // Get some cheesy spin going... +// static F32 spinDiddle = 0.f; +// +// worldMatrix *= MatrixF(EulerF(0.f,spinDiddle, 90.f - spinDiddle )); +// worldMatrix.setPosition(Point3F(0.f, 5.f, 0.f)); +// +// spinDiddle += 0.001f; +// +// if(spinDiddle > 90.f) +// spinDiddle = 0.f; +// +// d->setWorldMatrix(worldMatrix); +// +// // Draw our cube. +// d->setVertexBuffer(mCube.mCubeVB); +// d->setPrimitiveBuffer(mCube.mCubePB); +// +// d->setBaseRenderState(); +// d->setCullMode(GFXCullNone); +// d->setVertexColorEnable(true); +// d->setAlphaBlendEnable(false); +// d->setZEnable(true); +// d->setZFunc(GFXCmpLess); +// d->setupGenericShaders(); +// +// // Turn on texture, with a cheesy vertex modulate (whee!) +// d->setTextureStageColorOp( 0, GFXTOPModulate); +// d->setTexture(0, mTex0); +// d->setTextureStageColorOp( 1, GFXTOPModulate ); +// d->setTexture(1, mTex1); +// +// d->drawIndexedPrimitive(GFXTriangleList, 0, 8, 0, 12); +// } +// +// void run() +// { +// mRenderer.renderSignal.notify(this, &TestGFXMultiTextureCube::onRenderEvent); +// mRenderer.go(); +// mTex0 = NULL; +// mTex1 = NULL; +// mCube.destroy(); +// mRenderer.destroy(); +// } +//}; +// +//CreateInteractiveTest(TestGFXRenderTargetCube, "GFX/RenderTargetCube") +//{ +// SimpleGFXRenderFramework mRenderer; +// CubeBuilder mCube; +// GFXTexHandle mTex; +// GFXTextureTargetRef mRenderTarget; +// +// void drawCube(GFXDevice *d) +// { +// // Draw our cube. +// d->setVertexBuffer(mCube.mCubeVB); +// d->setPrimitiveBuffer(mCube.mCubePB); +// +// d->setBaseRenderState(); +// d->setCullMode(GFXCullNone); +// d->setVertexColorEnable(true); +// d->setAlphaBlendEnable(false); +// d->setupGenericShaders(); +// +// // Turn on texture, with a cheesy vertex modulate (whee!) +// d->setTextureStageColorOp( 0, GFXTOPModulate ); +// +// d->drawIndexedPrimitive(GFXTriangleList, 0, 8, 0, 12); +// } +// +// void onRenderEvent(GFXDevice *d) +// { +// // This init work could be done elsewhere, but it's easier +// // to just do it here. +// +// // Make sure we have a valid cube to render with. +// mCube.ensureValid(d, 1.0f); +// +// // Make sure we have a valid texture to render with. +// if(mTex.isNull()) +// mTex = d->getTextureManager()->createTexture(256, 256, GFXFormatR8G8B8X8, &GFXDefaultRenderTargetProfile); +// +// // Make sure we have a render target. +// if(mRenderTarget == NULL) +// mRenderTarget = d->allocRenderToTextureTarget(); +// +// // Update the render target. +// { +// d->setTexture(0, NULL); +// +// mRenderTarget->attachTexture(GFXTextureTarget::Color0, mTex); +// mRenderTarget->attachTexture(GFXTextureTarget::DepthStencil, GFXTextureTarget::sDefaultDepthStencil); +// d->setActiveRenderTarget(mRenderTarget); +// d->clear( GFXClearZBuffer | GFXClearStencil | GFXClearTarget, ColorI( 0, 0, 0 ), 1.0f, 0 ); +// +// +// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); +// MatrixF worldMatrix(1); +// +// // Get some cheesy spin going... +// static F32 spinDiddle = 45.f; +// +// worldMatrix *= MatrixF(EulerF(0,spinDiddle, 90.f - spinDiddle )); +// worldMatrix.setPosition(Point3F(0, 3, 0)); +// +// spinDiddle += 0.001f; +// +// if(spinDiddle > 90.f) +// spinDiddle = 0.f; +// +// d->setWorldMatrix(worldMatrix); +// +// drawCube(d); +// +// // Detach the texture so we can continue on w/ rendering... +// mRenderTarget->attachTexture(GFXTextureTarget::Color0, NULL); +// } +// +// +// // Render to the window... +// { +// d->setActiveRenderTarget(mRenderer.mWindow->getGFXTarget()); +// d->setTexture(0, mTex); +// +// // Set up the view... +// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); +// MatrixF worldMatrix(1); +// +// // Get some cheesy spin going... +// static F32 spinDiddle = 0.f; +// +// worldMatrix *= MatrixF(EulerF(0,spinDiddle, 90.f - spinDiddle )); +// worldMatrix.setPosition(Point3F(0, 5, 0)); +// +// spinDiddle += 0.001f; +// +// if(spinDiddle > 90.f) +// spinDiddle = 0.f; +// +// d->setWorldMatrix(worldMatrix); +// d->clear( GFXClearZBuffer | GFXClearStencil | GFXClearTarget, ColorI( 0, 0, 0 ), 1.0f, 0 ); +// +// drawCube(d); +// } +// } +// +// void run() +// { +// mRenderTarget = NULL; +// mRenderer.renderSignal.notify(this, &TestGFXRenderTargetCube::onRenderEvent); +// mRenderer.go(); +// +// mTex = NULL; +// mRenderTarget = NULL; +// mCube.destroy(); +// mRenderer.destroy(); +// } +//}; +// +// +//CreateInteractiveTest(TestGFXRenderTargetStack, "GFX/RenderTargetStack") +//{ +// enum +// { +// NumRenderTargets = 2, +// MaxRenderTargetDepth = 3, +// MaxRenderTargetsPerFrame = 10 +// }; +// +// SimpleGFXRenderFramework mRenderer; +// GFXTexHandle mTex[NumRenderTargets]; +// ColorI mTexLastClearColor[NumRenderTargets]; +// GFXTextureTargetRef mRenderTarget; +// CubeBuilder mCube; +// +// void drawCube(GFXDevice *d) +// { +// // Draw our cube. +// d->setVertexBuffer(mCube.mCubeVB); +// d->setPrimitiveBuffer(mCube.mCubePB); +// +// d->setBaseRenderState(); +// d->setCullMode(GFXCullNone); +// d->setVertexColorEnable(true); +// d->setAlphaBlendEnable(false); +// d->setupGenericShaders(); +// +// // Turn on texture, with a cheesy vertex modulate (whee!) +// d->setTextureStageColorOp( 0, GFXTOPModulate ); +// +// d->drawIndexedPrimitive(GFXTriangleList, 0, 8, 0, 12); +// } +// +// void onRenderEvent(GFXDevice *d) +// { +// // Make sure cube is ready to go. +// mCube.ensureValid(d, 1.0f); +// +// // Make sure we have a render to texture target. +// if(mRenderTarget == NULL) +// mRenderTarget = d->allocRenderToTextureTarget(); +// +// // Make sure all our textures are allocated. +// for(S32 i=0; igetTextureManager()->createTexture(256, 256, GFXFormatR8G8B8X8, &GFXDefaultRenderTargetProfile); +// } +// +// // Render to our different target textures. +// d->pushActiveRenderTarget(); +// +// // Set a starting texture so we can bind w/o any nulls. +// mRenderTarget->attachTexture(GFXTextureTarget::Color0, mTex[0]); +// +// // Now set the render target active.. +// d->setActiveRenderTarget(mRenderTarget); +// +// // Iterate over our render targets. +// for(S32 i=0; iattachTexture(GFXTextureTarget::Color0, mTex[i]); +// d->clear( GFXClearTarget, ColorI( (i+1)*80, (i)*150, 0 ), 1.0f, 0 ); +// } +// +// // Unbind everything so we don't have dangling references. +// mRenderTarget->attachTexture(GFXTextureTarget::Color0, NULL); +// d->popActiveRenderTarget(); +// +// // Set up the view... +// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); +// +// // Cheesy little positional offset table. +// F32 posOffsets[4][3] = +// { +// { -2, 5, -2}, +// { -2, 5, 2}, +// { 2, 5, -2}, +// { 2, 5, 2}, +// }; +// AssertFatal(NumRenderTargets <= 4, "Need more position offsets to draw cubes at."); +// +// // Let's draw a cube for each RT. +// for(S32 i=0; i 90.f) +// spinDiddle = 0.f; +// +// d->setWorldMatrix(worldMatrix); +// d->setTexture(0, mTex[i]); +// +// drawCube(d); +// } +// +// // Clean up. +// d->setTexture(0, NULL); +// } +// +// void run() +// { +// mRenderTarget = NULL; +// +// mRenderer.renderSignal.notify(this, &TestGFXRenderTargetStack::onRenderEvent); +// mRenderer.go(); +// +// // Clean stuff up. +// mRenderTarget = NULL; +// +// for(S32 i=0; isetVertexBuffer(mCube->mCubeVB); +// d->setPrimitiveBuffer(mCube->mCubePB); +// +// d->setBaseRenderState(); +// d->setCullMode(GFXCullNone); +// d->setVertexColorEnable(true); +// d->setAlphaBlendEnable(false); +// d->setupGenericShaders(); +// +// // Turn on texture, with a cheesy vertex modulate (whee!) +// d->setTextureStageColorOp( 0, GFXTOPModulate ); +// +// d->drawIndexedPrimitive(GFXTriangleList, 0, 8, 0, 12); +// } +// +// void onRenderSignal(WindowId id) +// { +// mDevice->beginScene(); +// mDevice->setActiveRenderTarget(mWindow->getGFXTarget()); +// +// // Fill this in an interesting way... +// static U32 i=10; +// mDevice->clear( GFXClearZBuffer | GFXClearStencil | GFXClearTarget, ColorI( 0, i, 0 ), 1.0f, 0 ); +// i+=10; +// +// // Set up the view... +// mDevice->setFrustum(90.0f, 1.0f, 0.1f, 100.f); +// +// // Get some cheesy spin going... +// MatrixF worldMatrix(1); +// +// static F32 spinDiddle = 0.f; +// +// worldMatrix *= MatrixF(EulerF(0,spinDiddle, 90.f - spinDiddle )); +// worldMatrix.setPosition(Point3F(-2, 5, -2)); +// +// spinDiddle += 0.001f; +// +// if(spinDiddle > 90.f) +// spinDiddle = 0.f; +// +// mDevice->setWorldMatrix(worldMatrix); +// +// // set sampler if we have one (handle null device case) +// if (mDevice->getNumSamplers()) +// mDevice->setTexture(0, *mTex); +// +// // Draw our cube... +// drawCube(mDevice); +// +// // And swap. +// mDevice->endScene(); +// mWindow->getGFXTarget()->present(); +// } +// +// bool onAppSignal(WindowId d, S32 event) +// { +// if(event == WindowClose && d == mWindow->getWindowId()) +// Process::requestShutdown(); +// return true; +// } +// +// void run() +// { +// PlatformWindowManager *pwm = CreatePlatformWindowManager(); +// +// // Create a video mode to use. +// GFXVideoMode vm; +// vm.resolution.x = 400; +// vm.resolution.y = 400; +// +// // Query all the available devices and adapters. +// GFXInit::enumerateAdapters(); +// Vector adapters; +// GFXInit::getAdapters(&adapters); +// +// test(adapters.size() > 0, "Got zero adapters! Hard to run an adapter test with no adapters!"); +// +// // For each reported adapter... +// for(S32 i=0; imIndex, +// adapters[i]->mName, +// GFXInit::getAdapterNameFromType(adapters[i]->mType))); +// +// // Init the device. +// mDevice = GFXInit::createDevice(adapters[i]); +// test(mDevice, "Failed to create a device!"); +// +// if(!mDevice) +// continue; +// +// // Create the window... +// mWindow = pwm->createWindow(mDevice, vm); +// test(mWindow, "Failed to create a window for this device!"); +// +// if(!mWindow) +// { +// SAFE_DELETE(mDevice); +// continue; +// } +// +// // Create some representative items: +// // - a cube builder... +// mCube = new CubeBuilder(); +// mCube->ensureValid(mDevice, 1.0f); +// +// // - a texture +// mTex = new GFXTexHandle(); +// if((*mTex).isNull()) +// (*mTex) = mDevice->getTextureManager()->createTexture("common/gui/images/GG_Icon.png", +// &GFXDefaultPersistentProfile); +// +// // Hook in our events. +// // Setup our events. +// mWindow->signalRender.notify(this, &TestGFXDeviceSwitching::onRenderSignal); +// mWindow->signalApp.notify (this, &TestGFXDeviceSwitching::onAppSignal); +// +// // Render until the user gets bored. +// while(Process::processEvents()); +// +// // And clean up, so we can do it again. +// SAFE_DELETE(mTex); +// SAFE_DELETE(mCube); +// SAFE_DELETE(mDevice); +// SAFE_DELETE(mWindow); +// } +// +// // All done! +// SAFE_DELETE(pwm); +// } +//}; diff --git a/Engine/source/gfx/util/distanceField.cpp b/Engine/source/gfx/util/distanceField.cpp new file mode 100644 index 000000000..91069ee25 --- /dev/null +++ b/Engine/source/gfx/util/distanceField.cpp @@ -0,0 +1,206 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#include "math/mPoint2.h" +#include "gfx/bitmap/gBitmap.h" + +#include "gfx/util/distanceField.h" + +//----------------------------------------------------------------------------- +struct DistanceFieldSearchSpaceStruct +{ + S32 xOffset; + S32 yOffset; + F32 distance; +}; + +int QSORT_CALLBACK cmpSortDistanceFieldSearchSpaceStruct(const void* p1, const void* p2) +{ + const DistanceFieldSearchSpaceStruct* sp1 = (const DistanceFieldSearchSpaceStruct*)p1; + const DistanceFieldSearchSpaceStruct* sp2 = (const DistanceFieldSearchSpaceStruct*)p2; + + if (sp2->distance > sp1->distance) + return -1; + else if (sp2->distance == sp1->distance) + return 0; + else + return 1; +} + +//GBitmap * GFXUtil::DistanceField::makeDistanceField(GBitmap * sourceBmp, S32 targetSizeX, S32 targetSizeY, F32 rangePct) +//{ +// AssertFatal(sourceBmp->getFormat() == GFXFormatA8,"Use an alpha-only texture to create distance fields"); +// +// static Vector searchSpace; +// +// S32 sourceSizeX = sourceBmp->getWidth(); +// S32 sourceSizeY = sourceBmp->getHeight(); +// +// S32 targetToSourceScalarX = sourceSizeX / targetSizeX; +// S32 targetToSourceScalarY = sourceSizeY / targetSizeY; +// S32 targetToSourcePixOffsetX = targetToSourceScalarX / 2; +// S32 targetToSourcePixOffsetY = targetToSourceScalarY / 2; +// +// F32 range = getMin(sourceSizeX,sourceSizeY) * rangePct; +// F32 range2 = range * 2.f; +// +// { +// S32 intRange = mCeil(range); +// for(S32 spaceY = -intRange; spaceY < intRange; spaceY++) +// { +// for(S32 spaceX = -intRange; spaceX < intRange; spaceX++) +// { +// if(spaceX == 0 && spaceY == 0) +// continue; +// +// F32 distance = Point2F(spaceX,spaceY).len(); +// if(distance <= range) +// { +// searchSpace.increment(); +// searchSpace.last().distance = distance; +// searchSpace.last().xOffset = spaceX; +// searchSpace.last().yOffset = spaceY; +// } +// } +// } +// } +// dQsort(searchSpace.address(), searchSpace.size(), sizeof(DistanceFieldSearchSpaceStruct), cmpSortDistanceFieldSearchSpaceStruct); +// +// GBitmap * targetBmp = new GBitmap(targetSizeX,targetSizeY,false,GFXFormatA8); +// +// U8 * targetPixel = targetBmp->getWritableBits(); +// for(S32 y = 0; y < targetSizeY; y++) +// { +// for(S32 x = 0; x < targetSizeX; x++) +// { +// S32 sourceX = x * targetToSourceScalarX + targetToSourcePixOffsetX; +// S32 sourceY = y * targetToSourceScalarY + targetToSourcePixOffsetY; +// +// const U8 * thisPixel = sourceBmp->getAddress(sourceX,sourceY); +// +// bool thisPixelEmpty = *thisPixel == 0; +// +// F32 closestDist = F32_MAX; +// +// for(DistanceFieldSearchSpaceStruct * seachSpaceStructPtr = searchSpace.begin(); seachSpaceStructPtr <= searchSpace.end(); seachSpaceStructPtr++) +// { +// DistanceFieldSearchSpaceStruct & searchSpaceStruct = *seachSpaceStructPtr; +// S32 cx = sourceX + searchSpaceStruct.xOffset; +// if(cx < 0 || cx >= sourceSizeX) +// continue; +// +// S32 cy = sourceY + searchSpaceStruct.yOffset; +// if(cy < 0 || cy >= sourceSizeY) +// continue; +// +// const U8 * checkPixel = sourceBmp->getAddress(cx,cy); +// if((*checkPixel == 0) != thisPixelEmpty) +// { +// closestDist = searchSpaceStruct.distance; +// break; +// } +// } +// +// F32 diff = thisPixelEmpty ? getMax(-0.5f,-(closestDist / range2)) : getMin(0.5f,closestDist / range2); +// F32 targetValue = 0.5f + diff; +// +// *targetPixel = targetValue * 255; +// targetPixel++; +// } +// } +// +// searchSpace.clear(); +// +// return targetBmp; +//} + +void GFXUtil::DistanceField::makeDistanceField( const U8 * sourceData, S32 sourceSizeX, S32 sourceSizeY, U8 * targetData, S32 targetSizeX, S32 targetSizeY, F32 radius ) +{ + static Vector searchSpace; + + S32 targetToSourceScalarX = sourceSizeX / targetSizeX; + S32 targetToSourceScalarY = sourceSizeY / targetSizeY; + S32 targetToSourcePixOffsetX = targetToSourceScalarX / 2; + S32 targetToSourcePixOffsetY = targetToSourceScalarY / 2; + + F32 radius2 = radius * 2.f; + + { + S32 intRange = mCeil(radius); + for(S32 spaceY = -intRange; spaceY < intRange; spaceY++) + { + for(S32 spaceX = -intRange; spaceX < intRange; spaceX++) + { + if(spaceX == 0 && spaceY == 0) + continue; + + F32 distance = Point2F(spaceX,spaceY).len(); + if(distance <= radius) + { + searchSpace.increment(); + searchSpace.last().distance = distance; + searchSpace.last().xOffset = spaceX; + searchSpace.last().yOffset = spaceY; + } + } + } + } + dQsort(searchSpace.address(), searchSpace.size(), sizeof(DistanceFieldSearchSpaceStruct), cmpSortDistanceFieldSearchSpaceStruct); + + for(S32 y = 0; y < targetSizeY; y++) + { + for(S32 x = 0; x < targetSizeX; x++) + { + S32 sourceX = x * targetToSourceScalarX + targetToSourcePixOffsetX; + S32 sourceY = y * targetToSourceScalarY + targetToSourcePixOffsetY; + + bool thisPixelEmpty = sourceData[sourceY * sourceSizeX + sourceX] < 127; + + F32 closestDist = F32_MAX; + + for(DistanceFieldSearchSpaceStruct * seachSpaceStructPtr = searchSpace.begin(); seachSpaceStructPtr <= searchSpace.end(); seachSpaceStructPtr++) + { + DistanceFieldSearchSpaceStruct & searchSpaceStruct = *seachSpaceStructPtr; + S32 cx = sourceX + searchSpaceStruct.xOffset; + if(cx < 0 || cx >= sourceSizeX) + continue; + + S32 cy = sourceY + searchSpaceStruct.yOffset; + if(cy < 0 || cy >= sourceSizeY) + continue; + + if((sourceData[cy * sourceSizeX + cx] < 127) != thisPixelEmpty) + { + closestDist = searchSpaceStruct.distance; + break; + } + } + + F32 diff = thisPixelEmpty ? getMax(-0.5f,-(closestDist / radius2)) : getMin(0.5f,closestDist / radius2); + F32 targetValue = 0.5f + diff; + + *targetData = targetValue * 255; + targetData++; + } + } + + searchSpace.clear(); +} diff --git a/Engine/source/gfx/util/distanceField.h b/Engine/source/gfx/util/distanceField.h new file mode 100644 index 000000000..e695d4d1a --- /dev/null +++ b/Engine/source/gfx/util/distanceField.h @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _DISTANCE_FIELD_H_ +#define _DISTANCE_FIELD_H_ + +namespace GFXUtil +{ +namespace DistanceField +{ + //GBitmap * makeDistanceField(const GBitmap * sourceBmp, S32 targetSizeX, S32 targetSizeY, F32 rangePct); + void makeDistanceField(const U8 * sourceData, S32 sourceSizeX, S32 sourceSizeY, U8 * targetData, S32 targetSizeX, S32 targetSizeY, F32 radius); +} +} + +#endif // _DISTANCE_FIELD_H_ diff --git a/Engine/source/gfx/util/gfxFrustumSaver.cpp b/Engine/source/gfx/util/gfxFrustumSaver.cpp new file mode 100644 index 000000000..9d52a8d16 --- /dev/null +++ b/Engine/source/gfx/util/gfxFrustumSaver.cpp @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/util/gfxFrustumSaver.h" +#include "gfx/gfxDevice.h" + +GFXFrustumSaver::GFXFrustumSaver() +{ + mFrustum = GFX->getFrustum(); +} + +GFXFrustumSaver::~GFXFrustumSaver() +{ + GFX->setFrustum( mFrustum ); +} diff --git a/Engine/source/gfx/util/gfxFrustumSaver.h b/Engine/source/gfx/util/gfxFrustumSaver.h new file mode 100644 index 000000000..c45cda5ab --- /dev/null +++ b/Engine/source/gfx/util/gfxFrustumSaver.h @@ -0,0 +1,45 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef TORQUE_GFX_UTIL_GFXFRUSTUMSAVER_H_ +#define TORQUE_GFX_UTIL_GFXFRUSTUMSAVER_H_ + +#include "platform/types.h" + +#ifndef _MATHUTIL_FRUSTUM_H_ +#include "math/util/frustum.h" +#endif + +class GFXFrustumSaver +{ +public: + /// Saves the current frustum state. + GFXFrustumSaver(); + + /// Restores the saved frustum state. + ~GFXFrustumSaver(); + +private: + Frustum mFrustum; +}; + +#endif diff --git a/Engine/source/gfx/util/screenspace.cpp b/Engine/source/gfx/util/screenspace.cpp new file mode 100644 index 000000000..3ef64be48 --- /dev/null +++ b/Engine/source/gfx/util/screenspace.cpp @@ -0,0 +1,52 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/util/screenspace.h" + +// The conversion from screen space to the render target +// is made more complex because screen space is relative +// to the viewport. +void ScreenSpace::RenderTargetParameters(const Point3I &targetSize, const RectI &targetViewport, Point4F &rtParams) +{ + // Top->Down + Point2F targetOffset( (F32)targetViewport.point.x / (F32)targetSize.x, + (F32)targetViewport.point.y / (F32)targetSize.y ); + + // Bottom->Up + //Point2F targetOffset( (F32)targetViewport.point.x / (F32)targetSize.x, + // ( (F32)targetSize.y - (F32)(targetViewport.point.y + targetViewport.extent.y ) ) / (F32)targetSize.y ); + + + // Get the scale to convert from the + // screen space to the target size. + Point2F targetScale( (F32)targetViewport.extent.x / (F32)targetSize.x, + (F32)targetViewport.extent.y / (F32)targetSize.y ); + + // Get the target half pixel size. + const Point2F halfPixel( 0.5f / targetSize.x, + 0.5f / targetSize.y ); + + rtParams.set( targetOffset.x + halfPixel.x, + targetOffset.y + halfPixel.y, + targetScale.x, + targetScale.y ); +} \ No newline at end of file diff --git a/Engine/source/gfx/util/screenspace.h b/Engine/source/gfx/util/screenspace.h new file mode 100644 index 000000000..4274ae944 --- /dev/null +++ b/Engine/source/gfx/util/screenspace.h @@ -0,0 +1,35 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SCREENSPACE_UTILS_H_ +#define _SCREENSPACE_UTILS_H_ + +#include "math/mPoint3.h" +#include "math/mPoint4.h" +#include "math/mRect.h" + +namespace ScreenSpace +{ +void RenderTargetParameters(const Point3I &targetSize, const RectI &targetViewport, Point4F &rtParams); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/gfx/util/triListOpt.cpp b/Engine/source/gfx/util/triListOpt.cpp new file mode 100644 index 000000000..8ed7e4d12 --- /dev/null +++ b/Engine/source/gfx/util/triListOpt.cpp @@ -0,0 +1,398 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/util/triListOpt.h" +#include "core/frameAllocator.h" +#include "platform/profiler.h" +#include "math/mMathFn.h" + +namespace TriListOpt +{ + +void OptimizeTriangleOrdering(const dsize_t numVerts, const dsize_t numIndices, const U32 *indices, IndexType *outIndices) +{ + PROFILE_SCOPE(TriListOpt_OptimizeTriangleOrdering); + + if(numVerts == 0 || numIndices == 0) + { + dCopyArray(outIndices, indices, numIndices); + return; + } + + const U32 NumPrimitives = numIndices / 3; + AssertFatal(NumPrimitives == U32(mFloor(numIndices / 3.0f)), "Number of indicies not divisible by 3, not a good triangle list."); + + // + // Step 1: Run through the data, and initialize + // + FrameTemp vertexData(numVerts); + FrameTemp triangleData(NumPrimitives); + + U32 curIdx = 0; + for(int tri = 0; tri < NumPrimitives; tri++) + { + TriData &curTri = triangleData[tri]; + + for(int c = 0; c < 3; c++) + { + const U32 &curVIdx = indices[curIdx]; + AssertFatal(curVIdx < numVerts, "Out of range index."); + + // Add this vert to the list of verts that define the triangle + curTri.vertIdx[c] = curVIdx; + + VertData &curVert = vertexData[curVIdx]; + + // Increment the number of triangles that reference this vertex + curVert.numUnaddedReferences++; + + curIdx++; + } + } + + // Allocate per-vertex triangle lists, and calculate the starting score of + // each of the verts + for(int v = 0; v < numVerts; v++) + { + VertData &curVert = vertexData[v]; + curVert.triIndex = new S32[curVert.numUnaddedReferences]; + curVert.score = FindVertexScore::score(curVert); + } + + // These variables will be used later, but need to be declared now + S32 nextNextBestTriIdx = -1, nextBestTriIdx = -1; + F32 nextNextBestTriScore = -1.0f, nextBestTriScore = -1.0f; + +#define _VALIDATE_TRI_IDX(idx) if(idx > -1) { AssertFatal(idx < NumPrimitives, "Out of range triangle index."); AssertFatal(!triangleData[idx].isInList, "Triangle already in list, bad."); } +#define _CHECK_NEXT_NEXT_BEST(score, idx) { if(score > nextNextBestTriScore) { nextNextBestTriIdx = idx; nextNextBestTriScore = score; } } +#define _CHECK_NEXT_BEST(score, idx) { if(score > nextBestTriScore) { _CHECK_NEXT_NEXT_BEST(nextBestTriScore, nextBestTriIdx); nextBestTriIdx = idx; nextBestTriScore = score; } _VALIDATE_TRI_IDX(nextBestTriIdx); } + + // Fill-in per-vertex triangle lists, and sum the scores of each vertex used + // per-triangle, to get the starting triangle score + curIdx = 0; + for(int tri = 0; tri < NumPrimitives; tri++) + { + TriData &curTri = triangleData[tri]; + + for(int c = 0; c < 3; c++) + { + const U32 &curVIdx = indices[curIdx]; + AssertFatal(curVIdx < numVerts, "Out of range index."); + VertData &curVert = vertexData[curVIdx]; + + // Add triangle to triangle list + curVert.triIndex[curVert.numReferences++] = tri; + + // Add vertex score to triangle score + curTri.score += curVert.score; + + curIdx++; + } + + // This will pick the first triangle to add to the list in 'Step 2' + _CHECK_NEXT_BEST(curTri.score, tri); + _CHECK_NEXT_NEXT_BEST(curTri.score, tri); + } + + // + // Step 2: Start emitting triangles...this is the emit loop + // + LRUCacheModel lruCache; + for(int outIdx = 0; outIdx < numIndices; /* this space intentionally left blank */ ) + { + // If there is no next best triangle, than search for the next highest + // scored triangle that isn't in the list already + if(nextBestTriIdx < 0) + { + // TODO: Something better than linear performance here... + nextBestTriScore = nextNextBestTriScore = -1.0f; + nextBestTriIdx = nextNextBestTriIdx = -1; + + for(int tri = 0; tri < NumPrimitives; tri++) + { + TriData &curTri = triangleData[tri]; + + if(!curTri.isInList) + { + _CHECK_NEXT_BEST(curTri.score, tri); + _CHECK_NEXT_NEXT_BEST(curTri.score, tri); + } + } + } + AssertFatal(nextBestTriIdx > -1, "Ran out of 'nextBestTriangle' before I ran out of indices...not good."); + + // Emit the next best triangle + TriData &nextBestTri = triangleData[nextBestTriIdx]; + AssertFatal(!nextBestTri.isInList, "Next best triangle already in list, this is no good."); + for(int i = 0; i < 3; i++) + { + // Emit index + outIndices[outIdx++] = IndexType(nextBestTri.vertIdx[i]); + + // Update the list of triangles on the vert + VertData &curVert = vertexData[nextBestTri.vertIdx[i]]; + curVert.numUnaddedReferences--; + for(int t = 0; t < curVert.numReferences; t++) + { + if(curVert.triIndex[t] == nextBestTriIdx) + { + curVert.triIndex[t] = -1; + break; + } + } + + // Update cache + lruCache.useVertex(nextBestTri.vertIdx[i], &curVert); + } + nextBestTri.isInList = true; + + // Enforce cache size, this will update the cache position of all verts + // still in the cache. It will also update the score of the verts in the + // cache, and give back a list of triangle indicies that need updating. + Vector trisToUpdate; + lruCache.enforceSize(MaxSizeVertexCache, trisToUpdate); + + // Now update scores for triangles that need updates, and find the new best + // triangle score/index + nextBestTriIdx = -1; + nextBestTriScore = -1.0f; + for(Vector::iterator itr = trisToUpdate.begin(); itr != trisToUpdate.end(); itr++) + { + TriData &tri = triangleData[*itr]; + + // If this triangle isn't already emitted, re-score it + if(!tri.isInList) + { + tri.score = 0.0f; + + for(int i = 0; i < 3; i++) + tri.score += vertexData[tri.vertIdx[i]].score; + + _CHECK_NEXT_BEST(tri.score, *itr); + _CHECK_NEXT_NEXT_BEST(tri.score, *itr); + } + } + + // If there was no love finding a good triangle, than see if there is a + // next-next-best triangle, and if there isn't one of those...well than + // I guess we have to find one next time + if(nextBestTriIdx < 0 && nextNextBestTriIdx > -1) + { + if(!triangleData[nextNextBestTriIdx].isInList) + { + nextBestTriIdx = nextNextBestTriIdx; + nextBestTriScore = nextNextBestTriScore; + _VALIDATE_TRI_IDX(nextNextBestTriIdx); + } + + // Nuke the next-next best + nextNextBestTriIdx = -1; + nextNextBestTriScore = -1.0f; + } + + // Validate triangle we are marking as next-best + _VALIDATE_TRI_IDX(nextBestTriIdx); + } + +#undef _CHECK_NEXT_BEST +#undef _CHECK_NEXT_NEXT_BEST +#undef _VALIDATE_TRI_IDX + + // FrameTemp will call destructInPlace to clean up vertex lists +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +LRUCacheModel::~LRUCacheModel() +{ + for( LRUCacheEntry* entry = mCacheHead; entry != NULL; ) + { + LRUCacheEntry* next = entry->next; + delete entry; + entry = next; + } +} + +//------------------------------------------------------------------------------ + +void LRUCacheModel::useVertex(const U32 vIdx, VertData *vData) +{ + LRUCacheEntry *search = mCacheHead; + LRUCacheEntry *last = NULL; + + while(search != NULL) + { + if(search->vIdx == vIdx) + break; + + last = search; + search = search->next; + } + + // If this vertex wasn't found in the cache, create a new entry + if(search == NULL) + { + search = new LRUCacheEntry; + search->vIdx = vIdx; + search->vData = vData; + } + + if(search != mCacheHead) + { + // Unlink the entry from the linked list + if(last) + last->next = search->next; + + // Vertex that got passed in is now at the head of the cache + search->next = mCacheHead; + mCacheHead = search; + } +} + +//------------------------------------------------------------------------------ + +void LRUCacheModel::enforceSize(const dsize_t maxSize, Vector &outTrisToUpdate) +{ + // Clear list of triangles to update scores for + outTrisToUpdate.clear(); + + dsize_t length = 0; + LRUCacheEntry *next = mCacheHead; + LRUCacheEntry *last = NULL; + + // Run through list, up to the max size + while(next != NULL && length < MaxSizeVertexCache) + { + VertData &vData = *next->vData; + + // Update cache position on verts still in cache + vData.cachePosition = length++; + + for(int i = 0; i < vData.numReferences; i++) + { + const S32 &triIdx = vData.triIndex[i]; + if(triIdx > -1) + { + int j = 0; + for(; j < outTrisToUpdate.size(); j++) + if(outTrisToUpdate[j] == triIdx) + break; + if(j == outTrisToUpdate.size()) + outTrisToUpdate.push_back(triIdx); + } + } + + // Update score + vData.score = FindVertexScore::score(vData); + + last = next; + next = next->next; + } + + // NULL out the pointer to the next entry on the last valid entry + last->next = NULL; + + // If next != NULL, than we need to prune entries from the tail of the cache + while(next != NULL) + { + // Update cache position on verts which are going to get tossed from cache + next->vData->cachePosition = -1; + + LRUCacheEntry *curEntry = next; + next = next->next; + + delete curEntry; + } +} + +//------------------------------------------------------------------------------ + +S32 LRUCacheModel::getCachePosition(const U32 vIdx) +{ + dsize_t length = 0; + LRUCacheEntry *next = mCacheHead; + while(next != NULL) + { + if(next->vIdx == vIdx) + return length; + + length++; + next = next->next; + } + + return -1; +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +// http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html +namespace FindVertexScore +{ + +F32 score(const VertData &vertexData) +{ + // If nobody needs this vertex, return -1.0 + if(vertexData.numUnaddedReferences < 1) + return -1.0f; + + F32 Score = 0.0f; + + if(vertexData.cachePosition < 0) + { + // Vertex is not in FIFO cache - no score. + } + else + { + if(vertexData.cachePosition < 3) + { + // This vertex was used in the last triangle, + // so it has a fixed score, whichever of the three + // it's in. Otherwise, you can get very different + // answers depending on whether you add + // the triangle 1,2,3 or 3,1,2 - which is silly. + Score = FindVertexScore::LastTriScore; + } + else + { + AssertFatal(vertexData.cachePosition < MaxSizeVertexCache, "Out of range cache position for vertex"); + + // Points for being high in the cache. + const float Scaler = 1.0f / (MaxSizeVertexCache - 3); + Score = 1.0f - (vertexData.cachePosition - 3) * Scaler; + Score = mPow(Score, FindVertexScore::CacheDecayPower); + } + } + + + // Bonus points for having a low number of tris still to + // use the vert, so we get rid of lone verts quickly. + float ValenceBoost = mPow(vertexData.numUnaddedReferences, -FindVertexScore::ValenceBoostPower); + Score += FindVertexScore::ValenceBoostScale * ValenceBoost; + + return Score; +} + +} // namspace FindVertexScore + +} // namespace TriListOpt diff --git a/Engine/source/gfx/util/triListOpt.h b/Engine/source/gfx/util/triListOpt.h new file mode 100644 index 000000000..db454f010 --- /dev/null +++ b/Engine/source/gfx/util/triListOpt.h @@ -0,0 +1,100 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TRI_LIST_OPT_H_ +#define _TRI_LIST_OPT_H_ + +#include "core/util/tVector.h" + +namespace TriListOpt +{ + typedef U32 IndexType; + + const U32 MaxSizeVertexCache = 32; + + struct VertData + { + S32 cachePosition; + F32 score; + U32 numReferences; + U32 numUnaddedReferences; + S32 *triIndex; + + VertData() : cachePosition(-1), score(0.0f), numReferences(0), numUnaddedReferences(0), triIndex(NULL) {} + ~VertData() { if(triIndex != NULL) delete [] triIndex; triIndex = NULL; } + }; + + struct TriData + { + bool isInList; + F32 score; + U32 vertIdx[3]; + + TriData() : isInList(false), score(0.0f) { dMemset(vertIdx, 0, sizeof(vertIdx)); } + }; + + class LRUCacheModel + { + struct LRUCacheEntry + { + LRUCacheEntry *next; + U32 vIdx; + VertData *vData; + + LRUCacheEntry() : next(NULL), vIdx(0), vData(NULL) {} + }; + + LRUCacheEntry *mCacheHead; + + public: + LRUCacheModel() : mCacheHead(NULL) {} + ~LRUCacheModel(); + void enforceSize(const dsize_t maxSize, Vector &outTrisToUpdate); + void useVertex(const U32 vIdx, VertData *vData); + S32 getCachePosition(const U32 vIdx); + }; + + + /// This method will look at the index buffer for a triangle list, and generate + /// a new index buffer which is optimized using Tom Forsyth's paper: + /// "Linear-Speed Vertex Cache Optimization" + /// http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html + /// @param numVerts Number of vertices indexed by the 'indices' + /// @param numIndices Number of elements in both 'indices' and 'outIndices' + /// @param indices Input index buffer + /// @param outIndices Output index buffer + /// + /// @note Both 'indices' and 'outIndices' can point to the same memory. + void OptimizeTriangleOrdering(const dsize_t numVerts, const dsize_t numIndices, const U32 *indices, IndexType *outIndices); + + namespace FindVertexScore + { + const F32 CacheDecayPower = 1.5f; + const F32 LastTriScore = 0.75f; + const F32 ValenceBoostScale = 2.0f; + const F32 ValenceBoostPower = 0.5f; + + F32 score(const VertData &vertexData); + }; +}; + +#endif \ No newline at end of file diff --git a/Engine/source/gfx/video/theoraTexture.cpp b/Engine/source/gfx/video/theoraTexture.cpp new file mode 100644 index 000000000..95b513c26 --- /dev/null +++ b/Engine/source/gfx/video/theoraTexture.cpp @@ -0,0 +1,697 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_OGGTHEORA + +/// If defined, uses a separate file stream to read Vorbis sound data +/// from the Ogg stream. This removes both the contention caused by +/// concurrently streaming from a single master stream as well as the +/// coupling between Theora reads and Vorbis reads that arises from +/// multiplexing. +#define SPLIT_VORBIS + + +#include "theoraTexture.h" + +#include "sfx/sfxDescription.h" +#include "sfx/sfxSystem.h" +#include "sfx/sfxCommon.h" +#include "sfx/sfxMemoryStream.h" +#include "sfx/sfxSound.h" +#ifdef SPLIT_VORBIS + #include "sfx/media/sfxVorbisStream.h" +#endif + +#include "core/stream/fileStream.h" +#include "core/ogg/oggInputStream.h" +#include "core/ogg/oggTheoraDecoder.h" +#include "core/ogg/oggVorbisDecoder.h" +#include "core/util/safeDelete.h" +#include "core/util/rawData.h" +#include "console/console.h" +#include "math/mMath.h" +#include "gfx/bitmap/gBitmap.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxFormatUtils.h" +#include "gfx/gfxTextureManager.h" + + + +/// Profile for the video texture. +GFX_ImplementTextureProfile( GFXTheoraTextureProfile, + GFXTextureProfile::DiffuseMap, + GFXTextureProfile::NoMipmap | GFXTextureProfile::Dynamic, + GFXTextureProfile::None ); + + +//----------------------------------------------------------------------------- + +static const char* GetPixelFormatName( OggTheoraDecoder::EPixelFormat format ) +{ + switch( format ) + { + case OggTheoraDecoder::PIXEL_FORMAT_420: + return "4:2:0"; + case OggTheoraDecoder::PIXEL_FORMAT_422: + return "4:2:2"; + case OggTheoraDecoder::PIXEL_FORMAT_444: + return "4:4:4"; + + case OggTheoraDecoder::PIXEL_FORMAT_Unknown: ; + } + return "Unknown"; +} + +//============================================================================= +// TheoraTexture::FrameReadItem implementation. +//============================================================================= + +//----------------------------------------------------------------------------- + +TheoraTexture::FrameReadItem::FrameReadItem( AsyncBufferedInputStream< TheoraTextureFrame*, IInputStream< OggTheoraFrame* >* >* stream, ThreadContext* context ) + : Parent( context ), + mFrameStream( dynamic_cast< FrameStream* >( stream ) ) +{ + AssertFatal( mFrameStream != NULL, "TheoraTexture::FrameReadItem::FrameReadItem() - expecting stream of type 'FrameStream'" ); + + mAsyncState = mFrameStream->mAsyncState; + + // Assign a TheoraTextureFrame record to us. The nature of + // AsyncBufferedInputStream ensures that we are always serial + // here so this is thread-safe. + + mFrame = &mFrameStream->mFrames[ mFrameStream->mFrameIndex ]; + mFrameStream->mFrameIndex = ( mFrameStream->mFrameIndex + 1 ) % FrameStream::NUM_FRAME_RECORDS; +} + +//----------------------------------------------------------------------------- + +void TheoraTexture::FrameReadItem::execute() +{ + // Read Theora frame data. + + OggTheoraFrame* frame; + if( mFrameStream->getSourceStream()->read( &frame, 1 ) != 1 ) + return; + + // Copy the data into the texture. + + OggTheoraDecoder* decoder = mAsyncState->getTheora(); + const U32 height = decoder->getFrameHeight(); + const U32 framePitch = decoder->getFrameWidth() * 4; + + GFXLockedRect* rect = mFrame->mLockedRect; + if( rect ) + { + const U32 usePitch = getMin(framePitch, mFrame->mTexture->getWidth() * 4); + const U32 maxHeight = getMin(height, mFrame->mTexture->getHeight()); + if( (framePitch == rect->pitch) && (height == maxHeight) ) + dMemcpy( rect->bits, frame->data, rect->pitch * height ); + else + { + // Scanline length does not match. Copy line by line. + + U8* dst = rect->bits; + U8* src = ( U8* ) frame->data; + + // Center the video if it is too big for the texture mode + if ( height > maxHeight ) + src += framePitch * ((height - maxHeight) / 2); + if ( framePitch > usePitch ) + src += (framePitch - usePitch) / 2; + for( U32 i = 0; i < maxHeight; ++ i ) + { + dMemcpy( dst, src, usePitch ); + dst += rect->pitch; + src += framePitch; + } + } + } + #ifdef TORQUE_DEBUG + else + Platform::outputDebugString( "[TheoraTexture] texture not locked on frame %i", frame->mFrameNumber ); + #endif + + // Copy frame metrics. + + mFrame->mFrameNumber = frame->mFrameNumber; + mFrame->mFrameTime = frame->mFrameTime; + mFrame->mFrameDuration = frame->mFrameDuration; + + // Yield the frame packet back to the Theora decoder. + + decoder->reusePacket( frame ); + + // Buffer the frame. + + mFrameStream->_onArrival( mFrame ); +} + +//============================================================================= +// TheoraTexture::FrameStream implementation. +//============================================================================= + +//----------------------------------------------------------------------------- + +TheoraTexture::FrameStream::FrameStream( AsyncState* asyncState, bool looping ) + : Parent( asyncState->getTheora(), 0, FRAME_READ_AHEAD, looping ), + mAsyncState( asyncState ), + mFrameIndex( 0 ) +{ + // Create the textures. + + OggTheoraDecoder* theora = asyncState->getTheora(); + const U32 width = theora->getFrameWidth(); + const U32 height = theora->getFrameHeight(); + + for( U32 i = 0; i < NUM_FRAME_RECORDS; ++ i ) + { + mFrames[ i ].mTexture.set( + width, + height, + GFXFormatR8G8B8A8, + &GFXTheoraTextureProfile, + String::ToString( "Theora texture frame buffer %i (%s:%i)", i, __FILE__, __LINE__ ) + ); + } + + acquireTextureLocks(); +} + +//----------------------------------------------------------------------------- + +void TheoraTexture::FrameStream::acquireTextureLocks() +{ + for( U32 i = 0; i < NUM_FRAME_RECORDS; ++ i ) + if( !mFrames[ i ].mLockedRect ) + mFrames[ i ].mLockedRect = mFrames[ i ].mTexture.lock(); +} + +//----------------------------------------------------------------------------- + +void TheoraTexture::FrameStream::releaseTextureLocks() +{ + for( U32 i = 0; i < NUM_FRAME_RECORDS; ++ i ) + if( mFrames[ i ].mLockedRect ) + { + mFrames[ i ].mTexture.unlock(); + mFrames[ i ].mLockedRect = NULL; + } +} + +//============================================================================= +// TheoraTexture::AsyncState implementation. +//============================================================================= + +//----------------------------------------------------------------------------- + +TheoraTexture::AsyncState::AsyncState( const ThreadSafeRef< OggInputStream >& oggStream, bool looping ) + : mOggStream( oggStream ), + mTheoraDecoder( dynamic_cast< OggTheoraDecoder* >( oggStream->getDecoder( "Theora" ) ) ), + mVorbisDecoder( dynamic_cast< OggVorbisDecoder* >( oggStream->getDecoder( "Vorbis" ) ) ), + mCurrentTime( 0 ) +{ + if( mTheoraDecoder ) + { + mTheoraDecoder->setTimeSource( this ); + mFrameStream = new FrameStream( this, looping ); + } +} + +//----------------------------------------------------------------------------- + +TheoraTextureFrame* TheoraTexture::AsyncState::readNextFrame() +{ + TheoraTextureFrame* frame; + if( mFrameStream->read( &frame, 1 ) ) + return frame; + else + return NULL; +} + +//----------------------------------------------------------------------------- + +void TheoraTexture::AsyncState::start() +{ + mFrameStream->start(); +} + +//----------------------------------------------------------------------------- + +void TheoraTexture::AsyncState::stop() +{ + mFrameStream->stop(); +} + +//----------------------------------------------------------------------------- + +bool TheoraTexture::AsyncState::isAtEnd() +{ + return mOggStream->isAtEnd(); +} + +//============================================================================= +// TheoraTexture implementation. +//============================================================================= + +//----------------------------------------------------------------------------- + +TheoraTexture::TheoraTexture() + : mPlaybackQueue( NULL ), + mCurrentFrame( NULL ), + mIsPaused( true ) +{ + GFXTextureManager::addEventDelegate( this, &TheoraTexture::_onTextureEvent ); +} + +//----------------------------------------------------------------------------- + +TheoraTexture::~TheoraTexture() +{ + GFXTextureManager::removeEventDelegate( this, &TheoraTexture::_onTextureEvent ); + _reset(); +} + +//----------------------------------------------------------------------------- + +void TheoraTexture::_reset() +{ + // Stop the async streams. + + if( mAsyncState != NULL ) + mAsyncState->stop(); + + // Delete the playback queue. + + if( mPlaybackQueue ) + SAFE_DELETE( mPlaybackQueue ); + + // Kill the sound source. + + if( mSFXSource != NULL ) + { + mSFXSource->stop(); + SFX_DELETE( mSFXSource ); + mSFXSource = NULL; + } + + mLastFrameNumber = 0; + mNumDroppedFrames = 0; + mCurrentFrame = NULL; + mAsyncState = NULL; + mIsPaused = false; + mPlaybackTimer.reset(); +} + +//----------------------------------------------------------------------------- + +void TheoraTexture::_onTextureEvent( GFXTexCallbackCode code ) +{ + switch( code ) + { + case GFXZombify: + mCurrentFrame = NULL; + if( mAsyncState ) + { + // Blast out work items and then release all texture locks. + + ThreadPool::GLOBAL().flushWorkItems(); + mAsyncState->getFrameStream()->releaseTextureLocks(); + + // The Theora decoder does not implement seeking at the moment, + // so we absolutely want to make sure we don't fall behind too far or + // we may end up having the decoder go crazy trying to skip through + // Ogg packets (even just reading these undecoded packets takes a + // lot of time). So, for the time being, just pause playback when + // we go zombie. + + if( mSFXSource ) + mSFXSource->pause(); + else + mPlaybackTimer.pause(); + } + break; + + case GFXResurrect: + if( mAsyncState ) + { + // Reacquire texture locks. + + mAsyncState->getFrameStream()->acquireTextureLocks(); + + // Resume playback if we have paused it. + + if( !mIsPaused ) + { + if( mSFXSource ) + mSFXSource->play(); + else + mPlaybackTimer.start(); + } + } + break; + } +} + +//----------------------------------------------------------------------------- + +void TheoraTexture::_initVideo() +{ + OggTheoraDecoder* theora = _getTheora(); + + // Set the decoder's pixel output format to match + // the texture format. + + OggTheoraDecoder::PacketFormat format; + format.mFormat = GFXFormatR8G8B8A8; + format.mPitch = GFXFormatInfo( format.mFormat ).getBytesPerPixel() * theora->getFrameWidth(); + + theora->setPacketFormat( format ); +} + +//----------------------------------------------------------------------------- + +void TheoraTexture::_initAudio( const ThreadSafeRef< SFXStream >& stream ) +{ + // Create an SFXDescription if we don't have one. + + if( !mSFXDescription ) + { + SFXDescription* description = new SFXDescription; + description->mIsStreaming = true; + description->registerObject(); + description->setAutoDelete( true ); + + mSFXDescription = description; + } + + // Create an SFX memory stream that consumes the output + // of the Vorbis decoder. + + ThreadSafeRef< SFXStream > sfxStream = stream; + if( !sfxStream ) + { + OggVorbisDecoder* vorbis = _getVorbis(); + SFXFormat sfxFormat( vorbis->getNumChannels(), + vorbis->getNumChannels() * 16, + vorbis->getSamplesPerSecond() ); + + sfxStream = new SFXMemoryStream( sfxFormat, vorbis ); + } + + // Create the SFXSource. + + mSFXSource = SFX->createSourceFromStream( sfxStream, mSFXDescription ); +} + +//----------------------------------------------------------------------------- + +TheoraTexture::TimeSourceType* TheoraTexture::_getTimeSource() const +{ + if( mSFXSource != NULL ) + return mSFXSource; + else + return ( TimeSourceType* ) &mPlaybackTimer; +} + +//----------------------------------------------------------------------------- + +U32 TheoraTexture::getWidth() const +{ + return _getTheora()->getFrameWidth(); +} + +//----------------------------------------------------------------------------- + +U32 TheoraTexture::getHeight() const +{ + return _getTheora()->getFrameHeight(); +} + +//----------------------------------------------------------------------------- + +bool TheoraTexture::setFile( const String& filename, SFXDescription* desc ) +{ + _reset(); + + if( filename.isEmpty() ) + return true; + + // Check SFX profile. + + if( desc && !desc->mIsStreaming ) + { + Con::errorf( "TheoraTexture::setFile - Not a streaming SFXDescription" ); + return false; + } + mSFXDescription = desc; + + // Open the Theora file. + + Stream* stream = FileStream::createAndOpen( filename, Torque::FS::File::Read ); + if( !stream ) + { + Con::errorf( "TheoraTexture::setFile - Theora file '%s' not found.", filename.c_str() ); + return false; + } + + // Create the OGG stream. + + Con::printf( "TheoraTexture - Loading file '%s'", filename.c_str() ); + + ThreadSafeRef< OggInputStream > oggStream = new OggInputStream( stream ); + oggStream->addDecoder< OggTheoraDecoder >(); + #ifndef SPLIT_VORBIS + oggStream->addDecoder< OggVorbisDecoder >(); + #endif + + if( !oggStream->init() ) + { + Con::errorf( "TheoraTexture - Failed to initialize OGG stream" ); + return false; + } + + mFilename = filename; + mAsyncState = new AsyncState( oggStream, desc ? desc->mIsLooping : false ); + + // Set up video. + + OggTheoraDecoder* theoraDecoder = _getTheora(); + if( !theoraDecoder ) + { + Con::errorf( "TheoraTexture - '%s' is not a Theora file", filename.c_str() ); + mAsyncState = NULL; + return false; + } + + Con::printf( " - Theora: %ix%i pixels, %.02f fps, %s format", + theoraDecoder->getFrameWidth(), + theoraDecoder->getFrameHeight(), + theoraDecoder->getFramesPerSecond(), + GetPixelFormatName( theoraDecoder->getDecoderPixelFormat() ) ); + + _initVideo(); + + // Set up sound if we have it. For performance reasons, create + // a separate physical stream for the Vorbis sound data rather than + // using the bitstream from the multiplexed OGG master stream. The + // contention caused by the OGG page/packet feeding will otherwise + // slow us down significantly. + + #ifdef SPLIT_VORBIS + + stream = FileStream::createAndOpen( filename, Torque::FS::File::Read ); + if( stream ) + { + ThreadSafeRef< SFXStream > vorbisStream = SFXVorbisStream::create( stream ); + if( !vorbisStream ) + { + Con::errorf( "TheoraTexture - could not create Vorbis stream for '%s'", filename.c_str() ); + + // Stream is deleted by SFXVorbisStream. + } + else + { + Con::printf( " - Vorbis: %i channels, %i kHz", + vorbisStream->getFormat().getChannels(), + vorbisStream->getFormat().getSamplesPerSecond() / 1000 ); + + _initAudio( vorbisStream ); + } + } + + #else + + OggVorbisDecoder* vorbisDecoder = _getVorbis(); + if( vorbisDecoder ) + { + Con::printf( " - Vorbis: %i bits, %i channels, %i kHz", + vorbisDecoder->getNumChannels(), + vorbisDecoder->getSamplesPerSecond() / 1000 ); + + _initAudio(); + } + + #endif + + // Initiate the background request chain. + + mAsyncState->start(); + + return true; +} + +//----------------------------------------------------------------------------- + +bool TheoraTexture::isPlaying() const +{ + if( !mAsyncState || !mCurrentFrame ) + return false; + + if( mSFXSource ) + return mSFXSource->isPlaying(); + else + return mPlaybackTimer.isStarted(); +} + +//----------------------------------------------------------------------------- + +void TheoraTexture::play() +{ + if( isPlaying() ) + return; + + if( !mAsyncState ) + setFile( mFilename, mSFXDescription ); + + // Construct playback queue that sync's to our time source, + // writes to us, and drops outdated packets. + + if( !mPlaybackQueue ) + mPlaybackQueue = new PlaybackQueueType( 1, _getTimeSource(), this, 0, true ); + + // Start playback. + + if( mSFXSource ) + mSFXSource->play(); + else + mPlaybackTimer.start(); + + mIsPaused = false; +} + +//----------------------------------------------------------------------------- + +void TheoraTexture::pause() +{ + if( mSFXSource ) + mSFXSource->pause(); + else + mPlaybackTimer.pause(); + + mIsPaused = true; +} + +//----------------------------------------------------------------------------- + +void TheoraTexture::stop() +{ + _reset(); +} + +//----------------------------------------------------------------------------- + +void TheoraTexture::refresh() +{ + PROFILE_SCOPE( TheoraTexture_refresh ); + + if( !mAsyncState || !mPlaybackQueue ) + return; + + // Synchronize the async state to our current time. + // Unfortunately, we cannot set the Theora decoder to + // synchronize directly with us as our lifetime and the + // lifetime of our time sources isn't bound to the + // threaded state. + + mAsyncState->syncTime( _getTimeSource()->getPosition() ); + + // Update the texture, if necessary. + + bool haveFrame = false; + while( mPlaybackQueue->needPacket() ) + { + // Lock the current frame. + + if( mCurrentFrame && !mCurrentFrame->mLockedRect ) + mCurrentFrame->mLockedRect = mCurrentFrame->mTexture.lock(); + + // Try to read a new frame. + + TheoraTextureFrame* frame = mAsyncState->readNextFrame(); + if( !frame ) + break; + + // Submit frame to queue. + + mPlaybackQueue->submitPacket( + frame, + frame->mFrameDuration * 1000.f, + false, + frame->mFrameTime * 1000.f + ); + + // See if we have dropped frames. + + if( frame->mFrameNumber != mLastFrameNumber + 1 ) + mNumDroppedFrames += frame->mFrameNumber - mLastFrameNumber - 1; + mLastFrameNumber = frame->mFrameNumber; + + haveFrame = true; + } + + // Unlock current frame. + + if( mCurrentFrame && mCurrentFrame->mLockedRect ) + { + mCurrentFrame->mTexture.unlock(); + mCurrentFrame->mLockedRect = NULL; + } + + // Release async state if we have reached the + // end of the Ogg stream. + + if( mAsyncState->isAtEnd() && !haveFrame ) + _reset(); +} + +//----------------------------------------------------------------------------- + +void TheoraTexture::write( TheoraTextureFrame* const* frames, U32 num ) +{ + if( !num ) + return; + + mCurrentFrame = frames[ num - 1 ]; // Only used last. +} + +#endif // TORQUE_OGGTHEORA diff --git a/Engine/source/gfx/video/theoraTexture.h b/Engine/source/gfx/video/theoraTexture.h new file mode 100644 index 000000000..47289de3d --- /dev/null +++ b/Engine/source/gfx/video/theoraTexture.h @@ -0,0 +1,418 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _THEORATEXTURE_H_ +#define _THEORATEXTURE_H_ + +#ifdef TORQUE_OGGTHEORA + +#ifndef _PLATFORM_H_ + #include "platform/platform.h" +#endif +#ifndef _GFXTEXTUREHANDLE_H_ + #include "gfx/gfxTextureHandle.h" +#endif +#ifndef _ASYNCPACKETQUEUE_H_ + #include "platform/async/asyncPacketQueue.h" +#endif +#ifndef _ASYNCBUFFEREDSTREAM_H_ + #include "platform/async/asyncBufferedStream.h" +#endif +#ifndef _TIMESOURCE_H_ + #include "core/util/timeSource.h" +#endif +#ifndef _THREADSAFEREFCOUNT_H_ + #include "platform/threads/threadSafeRefCount.h" +#endif +#ifndef _RAWDATA_H_ + #include "core/util/rawData.h" +#endif +#ifndef _SIMOBJECT_H_ + #include "console/simObject.h" +#endif +#ifndef _SFXSTREAM_H_ + #include "sfx/sfxStream.h" +#endif +#ifndef _OGGTHEORADECODER_H_ + #include "core/ogg/oggTheoraDecoder.h" +#endif +#ifndef _TYPETRAITS_H_ + #include "platform/typetraits.h" +#endif + + + +class SFXDescription; +class SFXSound; +class SFXVorbisStream; + +class OggInputStream; +class OggVorbisDecoder; + +class Stream; + + +/// A single frame in the video frame stream. +/// +/// Frames are uploaded directly into textures by the asynchronous +/// streaming system. This offloads as much work as possible to the worker +/// threads and guarantees the smoothest possible playback. +/// +/// Frame records are re-used and are managed directly by the video frame +/// stream. The number of textures concurrently used by a Theora stream +/// is determined by its stream read-ahead. +class TheoraTextureFrame +{ + public: + + typedef void Parent; + + /// The texture containing the video frame. + GFXTexHandle mTexture; + + /// The locked rectangle, if the texture is currently locked. + /// Frames will remain in locked state except if currently displayed. + GFXLockedRect* mLockedRect; + + /// + U32 mFrameNumber; + + /// The play time in seconds at which to display this frame. + F32 mFrameTime; + + /// The duration in seconds to display this frame. + F32 mFrameDuration; + + TheoraTextureFrame() + : mLockedRect( NULL ) + { + } +}; + + +inline void destructSingle( TheoraTextureFrame* frame ) +{ + // Do nothing. +} + + +/// TheoraTexture decodes Ogg Theora files, and their audio. +/// +/// TheoraTexture objects can be used similarly to TextureObjects. Just +/// set the video, call play(), and then refresh every frame to get the +/// latest video. Audio happens automagically. +/// +/// @note Uses Theora and ogg libraries which are Copyright (C) Xiph.org Foundation +class TheoraTexture : private IOutputStream< TheoraTextureFrame* >, + public IPositionable< U32 > +{ + public: + + typedef void Parent; + + protected: + + typedef IPositionable< U32 > TimeSourceType; + typedef GenericTimeSource<> TimerType; + typedef AsyncPacketQueue< TheoraTextureFrame*, TimeSourceType*, IOutputStream< TheoraTextureFrame* >*, F32 > PlaybackQueueType; + + class FrameStream; + class AsyncState; + friend class GuiTheoraCtrl; // accesses OggTheoraDecoder to set transcoder + + /// Parameters for tuning streaming behavior. + enum + { + /// Number of textures to load in background. + FRAME_READ_AHEAD = 6, + }; + + /// WorkItem that reads a frame from a Theora decoder and uploads it into a TheoraTextureFrame. + /// + /// Loading directly into textures moves the costly uploads out of the main thread into worker + /// threads. The downside to this is that since we cannot do GFX work on the worker threads, + /// we need to expect textures to get to us in locked state. + class FrameReadItem : public ThreadWorkItem + { + public: + + typedef ThreadWorkItem Parent; + + protected: + + /// The asynchronous state we belong to. This reference + /// ensures that all our streaming state stays live for as long as our + /// work item is in the pipeline. + ThreadSafeRef< AsyncState > mAsyncState; + + /// + FrameStream* mFrameStream; + + /// The frame texture we are loading. + TheoraTextureFrame* mFrame; + + // WorkItem. + virtual void execute(); + + public: + + /// + FrameReadItem( AsyncBufferedInputStream< TheoraTextureFrame*, IInputStream< OggTheoraFrame* >* >* stream, + ThreadPool::Context* context ); + }; + + /// Stream filter that turns a stream of OggTheoraFrames into a buffered background stream of TheoraTextureFrame + /// records. + /// + /// This streams allocates a fixed amount 'M' of TheoraTextureFrames. Reading the n-th frame from the stream, will + /// automatically invalidate the (n-M)-th frame. + class FrameStream : public AsyncSingleBufferedInputStream< TheoraTextureFrame*, IInputStream< OggTheoraFrame* >*, FrameReadItem > + { + public: + + typedef AsyncSingleBufferedInputStream< TheoraTextureFrame*, IInputStream< OggTheoraFrame* >*, FrameReadItem > Parent; + + protected: + + friend class FrameReadItem; + + enum + { + /// Number of TheoraTextureFrame records to allocate. + /// + /// We need to pre-allocate TheoraTextureFrames as we cannot do GFX operations + /// on the fly on worker threads. This number corresponds to the length of the + /// buffering queue plus one record that will be returned to the user as the + /// current frame record. + NUM_FRAME_RECORDS = FRAME_READ_AHEAD + 1 + }; + + /// Asynchronous state of the texture object. + /// This is *NOT* a ThreadSafeRef to not create a cycle. + AsyncState* mAsyncState; + + /// Wrap-around index into "mFrames." + U32 mFrameIndex; + + /// The pre-allocated TheoraTextureFrames. + TheoraTextureFrame mFrames[ NUM_FRAME_RECORDS ]; + + public: + + /// + FrameStream( AsyncState* asyncState, bool looping = false ); + + /// + void acquireTextureLocks(); + + /// + void releaseTextureLocks(); + }; + + /// Encapsulation of compound asynchronous state. Allows releasing + /// the entire state in one go. + class AsyncState : public ThreadSafeRefCount< AsyncState >, + private IPositionable< U32 > + { + public: + + typedef void Parent; + friend class FrameStream; // mDecoderBufferStream + + protected: + + typedef AsyncSingleBufferedInputStream< OggTheoraFrame* > DecoderBufferStream; + + /// Last synchronization position in the video stream. This is what the + /// Theora decoder gets passed to see if frames are outdated. + U32 mCurrentTime; + + /// The Ogg master stream. + ThreadSafeRef< OggInputStream > mOggStream; + + /// The raw video decoding stream. + OggTheoraDecoder* mTheoraDecoder; + + /// The raw sound decoding stream; NULL if no Vorbis in video or if + /// Vorbis is streamed separately. + OggVorbisDecoder* mVorbisDecoder; + + /// The background-buffered frame stream. + ThreadSafeRef< FrameStream > mFrameStream; + + public: + + /// + AsyncState( const ThreadSafeRef< OggInputStream >& oggStream, bool looping = false ); + + /// Return the Theora decoder substream. + OggTheoraDecoder* getTheora() const { return mTheoraDecoder; } + + /// Return the Vorbis decoder substream. + /// @note If Vorbis streaming is split out into a separate physical substream, + /// this method will always return NULL even if Vorbis sound is being used. + OggVorbisDecoder* getVorbis() const { return mVorbisDecoder; } + + /// + const ThreadSafeRef< FrameStream >& getFrameStream() const { return mFrameStream; } + + /// + TheoraTextureFrame* readNextFrame(); + + /// + void start(); + + /// + void stop(); + + /// + bool isAtEnd(); + + /// + void syncTime( U32 ms ) { mCurrentTime = ms; } + + private: + + // IPositionable. + virtual U32 getPosition() const { return mCurrentTime; } + virtual void setPosition( U32 pos ) {} + }; + + /// The Theora video file. + String mFilename; + + /// The SFXDescription used for sound playback. Synthesized if not provided. + SimObjectPtr< SFXDescription > mSFXDescription; + + /// If there's a Vorbis stream, this is the sound source used for playback. + /// Playback will synchronize to this source then. + SimObjectPtr< SFXSound > mSFXSource; + + /// The current frame. + TheoraTextureFrame* mCurrentFrame; + + /// The queue that synchronizes the writing of frames to the TheoraTexture. + PlaybackQueueType* mPlaybackQueue; + + /// The timer for synchronizing playback when there is no audio stream + /// to synchronize to. + TimerType mPlaybackTimer; + + /// Our threaded state. + ThreadSafeRef< AsyncState > mAsyncState; + + /// + bool mIsPaused; + + /// + U32 mLastFrameNumber; + + /// Number of frames we have dropped so far. + U32 mNumDroppedFrames; + + /// Release all dynamic playback state. + void _reset(); + + /// Initialize video playback. + void _initVideo(); + + /// Initialize audio playback. + void _initAudio( const ThreadSafeRef< SFXStream >& stream = NULL ); + + /// Return the Theora decoder stream or NULL. + OggTheoraDecoder* _getTheora() const { return ( mAsyncState != NULL ? mAsyncState->getTheora() : NULL ); } + + /// Return the Vorbis decoder stream or NULL. + OggVorbisDecoder* _getVorbis() const { return ( mAsyncState != NULL ? mAsyncState->getVorbis() : NULL ); } + + /// Return the object that is acting as our time source. + TimeSourceType* _getTimeSource() const; + + /// + void _onTextureEvent( GFXTexCallbackCode code ); + + // IOutputStream. + virtual void write( TheoraTextureFrame* const* frames, U32 num ); + + public: + + /// + TheoraTexture(); + + ~TheoraTexture(); + + /// Return the width of a single video frame in pixels. + U32 getWidth() const; + + /// Return the height of a single video frame in pixels. + U32 getHeight() const; + + /// Return the filename of the Theora video file loaded by the player. + const String& getFilename() const { return mFilename; } + + /// Load the given Theora video file. Set up audio using the given SFXDescription (must have + /// streaming enabled). If no SFXDescription is provided, a default description is used. + bool setFile( const String& filename, SFXDescription* desc = NULL ); + + /// Start video playback. + void play(); + + /// Pause video playback. + void pause(); + + /// Stop video playback. + void stop(); + + /// Refresh the current frame. This should be called before getTexture() to ensure that + /// the texture returned by getTexture() contains up-to-date contents. + void refresh(); + + /// Return true if a video file has been loaded and is ready for playback. + bool isReady() const { return ( mAsyncState != NULL ); } + + /// Return true if the video is currently playing. + bool isPlaying() const; + + /// Return true if the video is currently paused. + bool isPaused() const { return mIsPaused; } + + /// Return the sequence number of the current frame. Starts at 0. + U32 getFrameNumber() const { return mCurrentFrame->mFrameNumber; } + + /// Return the playback position of the current frame. + F32 getFrameTime() const { return mCurrentFrame->mFrameTime; } + + /// Return the number of frames that have been dropped so far. + U32 getNumDroppedFrames() const { return mNumDroppedFrames; } + + /// Return the texture containing the current frame. Call refresh() first + /// to ensure the texture contents are up-to-date. + const GFXTexHandle& getTexture() const { return mCurrentFrame->mTexture; } + GFXTexHandle& getTexture() { return mCurrentFrame->mTexture; } + + // IPositionable. + virtual U32 getPosition() const { return _getTimeSource()->getPosition(); } + virtual void setPosition( U32 pos ) {} // Not (yet?) implemented. +}; + +#endif // TORQUE_OGGTHEORA +#endif // !_THEORATEXTURE_H_ diff --git a/Engine/source/gfx/video/videoCapture.cpp b/Engine/source/gfx/video/videoCapture.cpp new file mode 100644 index 000000000..25ba633dd --- /dev/null +++ b/Engine/source/gfx/video/videoCapture.cpp @@ -0,0 +1,359 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gfx/video/videoCapture.h" + +#include "console/console.h" +#include "core/strings/stringFunctions.h" +#include "core/util/journal/journal.h" +#include "core/module.h" +#include "gui/core/guiCanvas.h" +#include "gfx/gfxTextureManager.h" +#include "console/engineAPI.h" + + +Vector VideoCapture::mEncoderFactoryFnList; + +MODULE_BEGIN( VideoCapture ) + + MODULE_INIT_BEFORE( GFX ) + MODULE_SHUTDOWN_BEFORE( GFX ) + + MODULE_INIT + { + ManagedSingleton< VideoCapture >::createSingleton(); + } + + MODULE_SHUTDOWN + { + VIDCAP->end(); + ManagedSingleton< VideoCapture >::deleteSingleton(); + } + +MODULE_END; + +VideoCapture::VideoCapture() : + mEncoder(NULL), + mIsRecording(false), + mCanvas(NULL), + mFrameGrabber(NULL), + mWaitingForCanvas(false), + mResolution(0,0), + mFrameRate(30.0f), + mEncoderName("THEORA"), + mFileName(""), + mMsPerFrameError(0) +{ +} + +S32 VideoCapture::getMsPerFrame() +{ + //Add accumulated error to ms per frame before rounding + F32 roundTime = mFloor(mMsPerFrame + mMsPerFrameError + 0.5f); + + //Accumulate the rounding errors + mMsPerFrameError += mMsPerFrame - roundTime; + + return (S32)roundTime; +} + +void VideoCapture::begin( GuiCanvas* canvas ) +{ + // No longer waiting for a canvas + mWaitingForCanvas = false; + + // No specified file + if (mFileName.isEmpty()) + { + Con::errorf("VideoCapture: no file specified!"); + return; + } + + // No framegrabber, cannot capture + if (mFrameGrabber == NULL) + { + Con::errorf("VideoCapture: cannot capture without a VideoFrameGrabber! One should be created in the GFXDevice initialization!"); + return; + } + + // Set the active encoder + if (!initEncoder(mEncoderName)) + return; + + // Store the canvas, so we know which one to capture from + mCanvas = canvas; + + // If the resolution is zero, get the current video mode + if (mResolution.isZero()) + mResolution = mCanvas->getPlatformWindow()->getVideoMode().resolution; + + // Set the encoder file, framerate and resolution + mEncoder->setFile(mFileName); + mEncoder->setFramerate( &mFrameRate ); + mEncoder->setResolution( &mResolution ); + + // The frame grabber must know about the resolution as well, since it'll do the resizing for us + mFrameGrabber->setOutResolution( mResolution ); + + // Calculate the ms per frame + mMsPerFrame = 1000.0f / mFrameRate; + + // Start the encoder + if (!mEncoder->begin()) + return; + + // We're now recording + mIsRecording = true; + mNextFramePosition = 0.0f; +} + +void VideoCapture::end() +{ + if (!mIsRecording) + return; + + if (mEncoder && !mEncoder->end()) + Con::errorf("VideoCapture: an error has ocurred while closing the video stream"); + + // Garbage collect the processed bitmaps + deleteProcessedBitmaps(); + + delete mEncoder; + mEncoder = NULL; + + mIsRecording = false; +} + +void VideoCapture::capture() +{ + // If this is the first frame, capture and encode it right away + if (mNextFramePosition == 0.0f) + { + mVideoCaptureStartTime = Platform::getVirtualMilliseconds(); + mCapturedFramePos = -1.0f; + } + + // Calculate the frame position for this captured frame + U32 frameTimeMs = Platform::getVirtualMilliseconds() - mVideoCaptureStartTime; + F32 framePosition = (F32)frameTimeMs / mMsPerFrame; + + // Repeat until the current frame is captured + while (framePosition > mCapturedFramePos) + { + // If the frame position is closer to the next frame position + // than the previous one capture it + if ( mFabs(framePosition - mNextFramePosition) < mFabs(mCapturedFramePos - mNextFramePosition) ) + { + mFrameGrabber->captureBackBuffer(); + mCapturedFramePos = framePosition; + } + + // If the new frame position is greater or equal than the next frame time + // tell the framegrabber to make bitmaps out from the last captured backbuffer until the video catches up + while ( framePosition >= mNextFramePosition ) + { + mFrameGrabber->makeBitmap(); + mNextFramePosition++; + } + } + + // Fetch bitmaps from the framegrabber and encode them + GBitmap *bitmap = NULL; + while ( (bitmap = mFrameGrabber->fetchBitmap()) != NULL ) + { + //mEncoder->pushProcessedBitmap(bitmap); + if (!mEncoder->pushFrame(bitmap)) + { + Con::errorf("VideoCapture: an error occurred while encoding a frame. Recording aborted."); + end(); + break; + } + } + + // Garbage collect the processed bitmaps + deleteProcessedBitmaps(); +} + + +void VideoCapture::registerEncoder( const char* name, VideoEncoderFactoryFn factoryFn ) +{ + mEncoderFactoryFnList.increment(); + mEncoderFactoryFnList.last().name = name; + mEncoderFactoryFnList.last().factory = factoryFn; +} + + +bool VideoCapture::initEncoder( const char* name ) +{ + if ( mEncoder ) + { + Con::errorf("VideoCapture:: cannot change video encoder while capturing! Stop the capture first!"); + return false; + } + + // Try creating an encoder based on the name + for (U32 i=0; igetProcessedBitmap()) != NULL ) + mBitmapDeleteList.push_back(bitmap); + + //Now delete them (or not... se below) + while ( mBitmapDeleteList.size() ) + { + bitmap = mBitmapDeleteList[0]; + mBitmapDeleteList.pop_front(); + + // Delete the bitmap only if it's the different than the next one (or it's the last one). + // This is done because repeated frames re-use the same GBitmap object + // and thus their pointers will appearl multiple times in the list + if (mBitmapDeleteList.size() == 0 || bitmap != mBitmapDeleteList[0]) + delete bitmap; + } +} + +///---------------------------------------------------------------------- + +///---------------------------------------------------------------------- + +void VideoEncoder::setFile( const char* path ) +{ + mPath = path; +} + +GBitmap* VideoEncoder::getProcessedBitmap() +{ + GBitmap* bitmap = NULL; + if (mProcessedBitmaps.tryPopFront(bitmap)) + return bitmap; + return NULL; +} + +void VideoEncoder::pushProcessedBitmap( GBitmap* bitmap ) +{ + mProcessedBitmaps.pushBack(bitmap); +} + +///---------------------------------------------------------------------- + +///---------------------------------------------------------------------- + +GBitmap* VideoFrameGrabber::fetchBitmap() +{ + if (mBitmapList.size() == 0) + return NULL; + + GBitmap *bitmap = mBitmapList.first(); + mBitmapList.pop_front(); + return bitmap; +} + +VideoFrameGrabber::VideoFrameGrabber() +{ + GFXTextureManager::addEventDelegate( this, &VideoFrameGrabber::_onTextureEvent ); +} + +VideoFrameGrabber::~VideoFrameGrabber() +{ + GFXTextureManager::removeEventDelegate( this, &VideoFrameGrabber::_onTextureEvent ); +} + +void VideoFrameGrabber::_onTextureEvent(GFXTexCallbackCode code) +{ + if ( code == GFXZombify ) + releaseTextures(); +} + +///---------------------------------------------------------------------- + +///---------------------------------------------------------------------- + +DefineEngineFunction( startVideoCapture, void, + ( GuiCanvas *canvas, const char *filename, const char *encoder, F32 framerate, Point2I resolution ), + ( "THEORA", 30.0f, Point2I( 0, 0 ) ), + "Begins a video capture session.\n" + "@see stopVideoCapture\n" + "@ingroup Rendering\n" ) +{ + if ( !canvas ) + { + Con::errorf("startVideoCapture -Please specify a GuiCanvas object to record from!"); + return; + } + + VIDCAP->setFilename( filename ); + VIDCAP->setEncoderName( encoder ); + VIDCAP->setFramerate( framerate ); + + if ( !resolution.isZero() ) + VIDCAP->setResolution(resolution); + + VIDCAP->begin(canvas); +} + +DefineEngineFunction( stopVideoCapture, void, (),, + "Stops the video capture session.\n" + "@see startVideoCapture\n" + "@ingroup Rendering\n" ) +{ + VIDCAP->end(); +} + +DefineEngineFunction( playJournalToVideo, void, + ( const char *journalFile, const char *videoFile, const char *encoder, F32 framerate, Point2I resolution ), + ( NULL, "THEORA", 30.0f, Point2I( 0, 0 ) ), + "Load a journal file and capture it video.\n" + "@ingroup Rendering\n" ) +{ + if ( !videoFile ) + videoFile = journalFile; + + VIDCAP->setFilename( Torque::Path( videoFile ).getFileName() ); + VIDCAP->setEncoderName( encoder ); + VIDCAP->setFramerate( framerate ); + + if ( !resolution.isZero() ) + VIDCAP->setResolution(resolution); + + VIDCAP->waitForCanvas(); + + Journal::Play( journalFile ); +} \ No newline at end of file diff --git a/Engine/source/gfx/video/videoCapture.h b/Engine/source/gfx/video/videoCapture.h new file mode 100644 index 000000000..5ef80c836 --- /dev/null +++ b/Engine/source/gfx/video/videoCapture.h @@ -0,0 +1,260 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _VIDEOCAPTURE_H_ +#define _VIDEOCAPTURE_H_ + +#ifndef _TSINGLETON_H_ +#include "core/util/tSingleton.h" +#endif + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +#ifndef _TORQUE_STRING_H_ +#include "core/util/str.h" +#endif + +#ifndef _GFXTEXTUREHANDLE_H_ +#include "gfx/gfxTextureHandle.h" +#endif + +#ifndef _MPOINT2_H_ +#include "math/mPoint2.h" +#endif + +#ifndef _THREADSAFEDEQUE_H_ +#include "platform/threads/threadSafeDeque.h" +#endif + + +class GuiCanvas; +class VideoFrameGrabber; +class VideoEncoder; +class GBitmap; + +typedef VideoEncoder* (*VideoEncoderFactoryFn)(); + + +/// Abstract frame grabber class +/// implementation and initalization depends on video device +class VideoFrameGrabber +{ + friend class VideoCapture; +protected: + Point2I mResolution; // The resolution used to capture the back buffer (scaling will be used) + Vector mBitmapList; //List of bitmaps created from backbuffer captures + + /// Sets the output frame resolution + void setOutResolution( const Point2I& res ) { mResolution = res; } + + /// Pushes a fresh bitmap into our list + void pushNewBitmap( GBitmap* bitmap ) { mBitmapList.push_back(bitmap); } + + /// Returns one captured bitmap. Returns NULL if there are no more bitmaps. + GBitmap* fetchBitmap(); + + /// Texture event callback + void _onTextureEvent(GFXTexCallbackCode code); + + /// Captures the current backbuffer. If the last capture wasn't made into a bitmap, it will be overriden. + virtual void captureBackBuffer() = 0; + + /// Starts converting the last backbuffer capture to a bitmap + /// Depending on the VideoFrameGrabber implementation, this may not produce a bitmap right away. + virtual void makeBitmap() = 0; + + /// Releases internal textures + virtual void releaseTextures() {}; + +public: + VideoFrameGrabber(); + virtual ~VideoFrameGrabber(); +}; + + +/// Video capture interface class +class VideoCapture +{ +private: + struct EncoderFactory { + const char* name; + VideoEncoderFactoryFn factory; + }; + + /// List of encoder factory functions + static Vector mEncoderFactoryFnList; + + // The frame position of the latest backbuffer capture + F32 mCapturedFramePos; + + /// Our current video encoder + VideoEncoder* mEncoder; + + /// Our video frame grabber + VideoFrameGrabber* mFrameGrabber; + + /// The canvas we're recording from + GuiCanvas* mCanvas; + + /// True if we're recording + bool mIsRecording; + + /// Time when we captured the previous frame + U32 mVideoCaptureStartTime; + + /// Frame to be captured next + F32 mNextFramePosition; + + /// The per-frame time (in milliseconds) + F32 mMsPerFrame; + + /// The framerate we'll use to record + F32 mFrameRate; + + /// Accumulated error when converting the per-frame-time to integer + /// this is used to dither the value and keep overall time advancing + /// correct + F32 mMsPerFrameError; + + /// Name of the encoder we'll be using + String mEncoderName; + + /// The video output resolution + Point2I mResolution; + + /// Output filename + String mFileName; + + /// Tur if we're waiting for a canvas to bre created before capturing + bool mWaitingForCanvas; + + /// Vector with bitmaps to delete + Vector< GBitmap* > mBitmapDeleteList; + + /// Initializes our encoder + bool initEncoder( const char* name ); + + /// Deletes processed bitmaps + void deleteProcessedBitmaps(); + +public: + VideoCapture(); + + /// Start a video capture session + void begin( GuiCanvas* canvas ); + + /// Captures a new frame + void capture(); + + /// Ends a video capture + void end(); + + /// Sets the output filename + void setFilename( const char* filename ) { mFileName = filename; } + + /// Sets the encoder we'll use + void setEncoderName( const char* encoder ) { mEncoderName = encoder; } + + /// Sets the framerate + void setFramerate( F32 fps ) { mFrameRate = fps; } + + /// Sets the video output resolution + void setResolution(const Point2I& res) { mResolution = res; } + + /// Returns true if we're capturing + bool isRecording() { return mIsRecording; } + + /// Returns the number of milliseconds per frame + S32 getMsPerFrame(); + + /// Sets the video farme grabber (cannot record without one). + void setFrameGrabber( VideoFrameGrabber* grabber ) { mFrameGrabber = grabber; } + + /// This will make the video capture begin capturing + /// as soon as a GuiCanvas is created + void waitForCanvas() { mWaitingForCanvas = true; } + bool isWaitingForCanvas() { return mWaitingForCanvas; } + + /// Registers an encoder + static void registerEncoder( const char* name, VideoEncoderFactoryFn factoryFn ); + + // For ManagedSingleton. + static const char* getSingletonName() { return "VideoCapture"; } +}; + + + +/// Abstract video encoder class +class VideoEncoder +{ +protected: + // Video output file path + String mPath; + + // Video framerate + F32 mFramerate; + + // Video resolution + Point2I mResolution; + + // List with bitmaps which are done encoding + ThreadSafeDeque< GBitmap* > mProcessedBitmaps; +public: + // Stores an encoded bitmap to be dealt with later + void pushProcessedBitmap( GBitmap* bitmap ); + +public: + /// Sets the file the encoder will write to + void setFile( const char* path ); + + /// Sets the framerate (and fixes it if its invalid) + virtual void setFramerate( F32* framerate ) { mFramerate = *framerate; } + + /// Sets the output resolution (and fixes it if its invalid) + virtual void setResolution( Point2I* resolution ) { mResolution = *resolution; } + + /// Begins accepting frames for encoding + virtual bool begin() = 0; + + /// Pushes a new frame into the video stream + virtual bool pushFrame( GBitmap * bitmap ) = 0; + + /// Finishes the encoding and closes the video + virtual bool end() = 0; + + /// Returns an already encoded bitmap. Video capture will get these and manage their deletion + GBitmap* getProcessedBitmap(); +}; + +/// Returns the VideoCapture singleton. +#define VIDCAP ManagedSingleton::instance() + +//----------------------------------------- +/// VIDEO ENCODER REGISTRATION MACRO +#define REGISTER_VIDEO_ENCODER(ClassName, EncoderName) \ + VideoEncoder* EncoderFactory##EncoderName() { return new ClassName(); } \ + struct __VidEncReg##EncoderName { __VidEncReg##EncoderName() { VideoCapture::registerEncoder( #EncoderName, &EncoderFactory##EncoderName ); } }; \ + static __VidEncReg##EncoderName _gEncoderRegistration; + +#endif // !_VIDEOCAPTURE_H_ diff --git a/Engine/source/gfx/video/videoEncoderPNG.cpp b/Engine/source/gfx/video/videoEncoderPNG.cpp new file mode 100644 index 000000000..6909080cf --- /dev/null +++ b/Engine/source/gfx/video/videoEncoderPNG.cpp @@ -0,0 +1,76 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "videoCapture.h" +#include "core/stream/fileStream.h" +#include "console/console.h" +#include "gfx/bitmap/gBitmap.h" + +/// This is a very basic "encoder" that records the video as a series of numbered PNGs +/// Good if you're having problems with container-based formats and need extra flexibility. +class VideoEncoderPNG : public VideoEncoder +{ + U32 mCurrentFrame; + +public: + /// Begins accepting frames for encoding + bool begin() + { + mPath += "\\"; + mCurrentFrame = 0; + + return true; + } + + /// Pushes a new frame into the video stream + bool pushFrame( GBitmap * bitmap ) + { + FileStream fs; + String framePath = mPath + String::ToString("%.6u.png", mCurrentFrame); + if ( !fs.open( framePath, Torque::FS::File::Write ) ) + { + Con::errorf( "VideoEncoderPNG::pushFrame() - Failed to open output file '%s'!", framePath.c_str() ); + return false; + } + + //Increment + mCurrentFrame++; + + bool result = bitmap->writeBitmap("png", fs, 0); + pushProcessedBitmap(bitmap); + + return result; + } + + /// Finishes the encoding and closes the video + bool end() + { + return true; + } + + void setResolution( Point2I* resolution ) + { + mResolution = *resolution; + } +}; + +REGISTER_VIDEO_ENCODER(VideoEncoderPNG, PNG) \ No newline at end of file diff --git a/Engine/source/gfx/video/videoEncoderTheora.cpp b/Engine/source/gfx/video/videoEncoderTheora.cpp new file mode 100644 index 000000000..c0b094a52 --- /dev/null +++ b/Engine/source/gfx/video/videoEncoderTheora.cpp @@ -0,0 +1,461 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_OGGTHEORA + +#include "videoCapture.h" +#include "core/stream/fileStream.h" +#include "console/console.h" +#include "gfx/bitmap/gBitmap.h" +#include "gfx/bitmap/bitmapUtils.h" + +#include "platform/threads/thread.h" +#include "platform/threads/threadSafeDeque.h" +#include "platform/threads/semaphore.h" + +#include "theora/theoraenc.h" +#include "vorbis/codec.h" +#include "vorbis/vorbisenc.h" + +#include "platform/profiler.h" + +//#define THEORA_ENCODER_SINGLE_THREAD + +// These are the coefficient lookup tables, so we don't need to multiply +dALIGN( static S32 sYRGB[ 256 ][ 4 ] ); +dALIGN( static S32 sURGB[ 256 ][ 4 ] ); +dALIGN( static S32 sVRGB[ 256 ][ 4 ] ); + +/// Initialize the lookup tables used in the RGB-YUV transcoding +static void initLookupTables() +{ + static bool sGenerated = false; + if( !sGenerated ) + { + for( S32 i = 0; i < 256; ++ i ) + { + //Y = ( ( 66 * R + 129 * G + 25 * B + 128) >> 8) + 16 + //U = ( ( -38 * R - 74 * G + 112 * B + 128) >> 8) + 128 + //V = ( ( 112 * R - 94 * G - 18 * B + 128) >> 8) + 128 + + // Y coefficients + sYRGB[ i ][ 0 ] = 66 * i; + sYRGB[ i ][ 1 ] = 129 * i; + sYRGB[ i ][ 2 ] = 25 * i + 128 + (16 << 8); + + // U coefficients + sURGB[ i ][ 0 ] = -38 * i; + sURGB[ i ][ 1 ] = -74 * i; + sURGB[ i ][ 2 ] = 112 * i + 128 + (128 << 8); + + // V coefficients + sVRGB[ i ][ 0 ] = 112 * i; + sVRGB[ i ][ 1 ] = -94 * i; + sVRGB[ i ][ 2 ] = -18 * i + 128 + (128 << 8); + } + sGenerated = true; + } +} + +/// Theora video capture encoder +class VideoEncoderTheora : public VideoEncoder, public Thread +{ + U32 mCurrentFrame; + + ogg_stream_state to; // take physical pages, weld into a logical stream of packets + th_enc_ctx *td; // Theora encoder context + th_info ti; // Theora info structure + th_comment tc; // Theora comment structure + + FileStream mFile; // Output file + + th_ycbcr_buffer mBuffer; // YCbCr buffer + + GBitmap* mLastFrame; + + ThreadSafeDeque< GBitmap* > mFrameBitmapList; // List with unprocessed frame bitmaps + Semaphore mSemaphore; //Semaphore for preventing the encoder from being lagged behind the game + + bool mErrorStatus; //Status flag, true if OK, false if an error ocurred + + /// Sets our error status + bool setStatus(bool status) + { + mErrorStatus = status; + return mErrorStatus; + } + bool getStatus() { return mErrorStatus; } + + /// Encodes one frame + void encodeFrame( GBitmap* bitmap, bool isLast=false ) + { + PROFILE_SCOPE(Theora_encodeFrame); + + //Copy bitmap to YUV buffer + copyBitmapToYUV420(bitmap); + + PROFILE_START(th_encode_ycbcr_in); + //Submit frame for encoding + if (th_encode_ycbcr_in(td, mBuffer)) + { + Platform::outputDebugString("VideoEncoderTheora::encodeFrame() - The buffer size does not match the frame size the encoder was initialized with, or encoding has already completed. ."); + setStatus(false); + PROFILE_END(); + return; + } + PROFILE_END(); + + //Fetch the encoded packets + ogg_packet packet; + if (!th_encode_packetout(td, isLast, &packet)) + { + Platform::outputDebugString("VideoEncoderTheora::encodeFrame() - Internal Theora library error."); + setStatus(false); + return; + } + + //Insert packet into vorbis stream page + ogg_stream_packetin(&to,&packet); + + //Increment + mCurrentFrame++; + + //Is there a video page flushed? + ogg_page videopage; + while (ogg_stream_pageout(&to,&videopage)) + { + //Write the video page to disk + mFile.write(videopage.header_len, videopage.header); + mFile.write(videopage.body_len, videopage.body); + + F64 videotime = th_granule_time(td,ogg_page_granulepos(&videopage)); + if (videotime > 0) + { + int hundredths=(int)(videotime*100-(long)videotime*100); + int seconds=(long)videotime%60; + Platform::outputDebugString("Encoding time %g %02i.%02i", videotime, seconds, hundredths); + } + } + + mSemaphore.release(); + } + + bool process(bool ending) + { + if (!getStatus()) + return false; + + //Try getting a bitmap for encoding + GBitmap* bitmap = NULL; + if (mFrameBitmapList.tryPopFront(bitmap)) + { + encodeFrame(bitmap, false); + } + + //Delete previous bitmap + if (!ending && bitmap) + { + if (mLastFrame) + pushProcessedBitmap(mLastFrame); + mLastFrame = bitmap; + } + + //If we're stopping encoding, but didn't have a frame, re-encode the last frame + if (ending && !bitmap && mLastFrame) + { + encodeFrame(mLastFrame, true); + pushProcessedBitmap(mLastFrame); + mLastFrame = NULL; + } + + // We'll live while we have a last frame + return (mLastFrame != NULL); + } + +public: + VideoEncoderTheora() : + mLastFrame(NULL) + { + setStatus(false); + } + + virtual void run( void* arg ) + { + _setName( "TheoraEncoderThread" ); + while (!checkForStop()) + process(false); + + // Encode all pending frames and close the last one + while (process(true)); + } + + /// Begins accepting frames for encoding + bool begin() + { + mPath += ".ogv"; + mCurrentFrame = 0; + + //Try opening the file for writing + if ( !mFile.open( mPath, Torque::FS::File::Write ) ) + { + Platform::outputDebugString( "VideoEncoderTheora::begin() - Failed to open output file '%s'!", mPath.c_str() ); + return setStatus(false); + } + + ogg_stream_init(&to, Platform::getRandom() * S32_MAX); + + ogg_packet op; + + th_info_init(&ti); + ti.frame_width = mResolution.x; + ti.frame_height = mResolution.y; + ti.pic_width = mResolution.x; + ti.pic_height = mResolution.y; + ti.pic_x = 0; + ti.pic_y = 0; + + ti.fps_numerator = (int)(mFramerate * 1000.0f); + ti.fps_denominator = 1000; + + ti.aspect_numerator = 0; + ti.aspect_denominator = 0; + ti.colorspace = TH_CS_UNSPECIFIED; + + ti.target_bitrate = 0; + ti.quality = 63; + ti.pixel_fmt = TH_PF_420; + + td = th_encode_alloc(&ti); + if (td == NULL) + { + Platform::outputDebugString("VideoEncoderTheora::begin() - Theora initialization error."); + return setStatus(false); + } + + th_info_clear(&ti); + + // This is needed for youtube compatibility + int vp3_compatible = 1; + th_encode_ctl(td, TH_ENCCTL_SET_VP3_COMPATIBLE, &vp3_compatible, sizeof(vp3_compatible)); + + // Set the encoder to max speed + int speed_max; + int ret; + ret = th_encode_ctl(td, TH_ENCCTL_GET_SPLEVEL_MAX, &speed_max, sizeof(speed_max)); + if(ret<0){ + Platform::outputDebugString("VideoEncoderTheora::begin() - could not determine maximum speed level."); + speed_max = 0; + } + ret = th_encode_ctl(td, TH_ENCCTL_SET_SPLEVEL, &speed_max, sizeof(speed_max)); + + // write the bitstream header packets with proper page interleave + th_comment_init(&tc); + + // first packet will get its own page automatically + if(th_encode_flushheader(td,&tc,&op) <= 0) + { + Platform::outputDebugString("VideoEncoderTheora::begin() - Internal Theora library error."); + return setStatus(false); + } + + ogg_page og; + ogg_stream_packetin(&to,&op); + if(ogg_stream_pageout(&to,&og) != 1) + { + Platform::outputDebugString("VideoEncoderTheora::begin() - Internal Ogg library error."); + return setStatus(false); + } + mFile.write(og.header_len, og.header); + mFile.write(og.body_len, og.body); + + // create the remaining theora headers + while((ret = th_encode_flushheader(td,&tc,&op)) != 0) + { + if(ret < 0) + { + Platform::outputDebugString("VideoEncoderTheora::begin() - Internal Theora library error."); + return setStatus(false); + } + ogg_stream_packetin(&to,&op); + } + + // Flush the rest of our headers. This ensures + // the actual data in each stream will start + // on a new page, as per spec. + while((ret = ogg_stream_flush(&to,&og)) != 0) + { + if(ret < 0) + { + Platform::outputDebugString("VideoEncoderTheora::begin() - Internal Ogg library error."); + return setStatus(false); + } + mFile.write(og.header_len, og.header); + mFile.write(og.body_len, og.body); + } + + //Initialize the YUV buffer + S32 decimation[] = {0,1,1}; + for (U32 i=0; i<3; i++) + { + mBuffer[i].width = mResolution.x >> decimation[i]; + mBuffer[i].height = mResolution.y >> decimation[i]; + mBuffer[i].stride = mBuffer[i].width * sizeof(U8); + mBuffer[i].data = new U8[mBuffer[i].width*mBuffer[i].height*sizeof(U8)]; + } + + //Initialize the YUV coefficient lookup tables + initLookupTables(); + + setStatus(true); + +#ifndef THEORA_ENCODER_SINGLE_THREAD + start(); +#endif + + return getStatus(); + } + + /// Pushes a new frame into the video stream + bool pushFrame( GBitmap * bitmap ) + { + + // Push the bitmap into the frame list + mFrameBitmapList.pushBack( bitmap ); + + mSemaphore.acquire(); + +#ifdef THEORA_ENCODER_SINGLE_THREAD + process(false); +#endif + + return getStatus(); + } + + /// Finishes the encoding and closes the video + bool end() + { + //Let's wait the thread stop doing whatever it needs to do + stop(); + join(); + +#ifdef THEORA_ENCODER_SINGLE_THREAD + while (process(true)); +#endif + + th_encode_free(td); + ogg_stream_clear(&to); + th_comment_clear(&tc); + + mFile.close(); + return true; + } + + + void setResolution( Point2I* resolution ) + { + /* Theora has a divisible-by-sixteen restriction for the encoded frame size */ + /* scale the picture size up to the nearest /16 and calculate offsets */ + resolution->x = (resolution->x) + 15 & ~0xF; + resolution->y = (resolution->y) + 15 & ~0xF; + + mResolution = *resolution; + } + + /// Converts the bitmap to YUV420 and copies it into our internal buffer + void copyBitmapToYUV420( GBitmap* bitmap ) + { + PROFILE_SCOPE(copyBitmapToYUV420); + + // Convert luma + const U8* rgb = bitmap->getBits(); + + // Chroma planes are half width and half height + U32 w = mResolution.x / 2; + U32 h = mResolution.y / 2; + + // We'll update two luminance rows at once + U8* yuv_y0 = mBuffer[0].data; + U8* yuv_y1 = mBuffer[0].data + mBuffer[0].stride; + + // Get pointers to chroma planes + U8* yuv_u = mBuffer[1].data; + U8* yuv_v = mBuffer[2].data; + + // We'll also need to read two RGB rows at once + U32 rgbStride = mResolution.x * bitmap->getBytesPerPixel(); + const U8* row0 = rgb; + const U8* row1 = row0 + rgbStride; + + for(U32 y = 0; y < h; y++) + { + for(U32 x = 0; x < w; x++) + { + // Fetch two RGB samples from each RGB row (for downsampling the chroma) + U8 r0 = *row0++; + U8 g0 = *row0++; + U8 b0 = *row0++; + U8 r1 = *row0++; + U8 g1 = *row0++; + U8 b1 = *row0++; + U8 r2 = *row1++; + U8 g2 = *row1++; + U8 b2 = *row1++; + U8 r3 = *row1++; + U8 g3 = *row1++; + U8 b3 = *row1++; + + // Convert the four RGB samples into four luminance samples + *yuv_y0 = ( (sYRGB[r0][0] + sYRGB[g0][1] + sYRGB[b0][2]) >> 8); + yuv_y0++; + *yuv_y0 = ( (sYRGB[r1][0] + sYRGB[g1][1] + sYRGB[b1][2]) >> 8); + yuv_y0++; + *yuv_y1 = ( (sYRGB[r2][0] + sYRGB[g2][1] + sYRGB[b2][2]) >> 8); + yuv_y1++; + *yuv_y1 = ( (sYRGB[r3][0] + sYRGB[g3][1] + sYRGB[b3][2]) >> 8); + yuv_y1++; + + // Downsample the four RGB samples + U8 r = (r0 + r1 + r2 + r3) >> 2; + U8 g = (g0 + g1 + g2 + g3) >> 2; + U8 b = (b0 + b1 + b2 + b3) >> 2; + + // Convert downsampled RGB into chroma + *yuv_u = ( (sURGB[r][0] + sURGB[g][1] + sURGB[b][2]) >> 8); + *yuv_v = ( (sVRGB[r][0] + sVRGB[g][1] + sVRGB[b][2]) >> 8); + yuv_u++; + yuv_v++; + } + + //Next RGB rows + row0 += rgbStride; + row1 += rgbStride; + + //Next luminance rows + yuv_y0 += mBuffer[0].stride; + yuv_y1 += mBuffer[0].stride; + } + } +}; + +REGISTER_VIDEO_ENCODER(VideoEncoderTheora, THEORA) + +#endif \ No newline at end of file diff --git a/Engine/source/ggEndOfLineFix.txt b/Engine/source/ggEndOfLineFix.txt new file mode 100644 index 000000000..42ac0c042 --- /dev/null +++ b/Engine/source/ggEndOfLineFix.txt @@ -0,0 +1,3 @@ +find . -name "*.cc" -exec sh -c 'echo >> {}' \; +find . -name "*.cpp" -exec sh -c 'echo >> {}' \; +find . -name "*.h" -exec sh -c 'echo >> {}' \; diff --git a/Engine/source/gui/3d/guiTSControl.cpp b/Engine/source/gui/3d/guiTSControl.cpp new file mode 100644 index 000000000..e1d2ecc0b --- /dev/null +++ b/Engine/source/gui/3d/guiTSControl.cpp @@ -0,0 +1,497 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/3d/guiTSControl.h" + +#include "console/engineAPI.h" +#include "scene/sceneManager.h" +#include "lighting/lightManager.h" +#include "gfx/sim/debugDraw.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/screenshot.h" +#include "math/mathUtils.h" +#include "gui/core/guiCanvas.h" +#include "scene/reflectionManager.h" +#include "postFx/postEffectManager.h" +#include "gfx/gfxTransformSaver.h" + + +IMPLEMENT_CONOBJECT( GuiTSCtrl ); + +ConsoleDocClass( GuiTSCtrl, + "@brief Abstract base class for controls that render 3D scenes.\n\n" + + "GuiTSCtrl is the base class for controls that render 3D camera views in Torque. The class itself " + "does not implement a concrete scene rendering. Use GuiObjectView to display invidiual shapes in " + "the Gui and GameTSCtrl to render full scenes.\n\n" + + "@see GameTSCtrl\n" + "@see GuiObjectView\n" + "@ingroup Gui3D\n" +); + +U32 GuiTSCtrl::smFrameCount = 0; +Vector GuiTSCtrl::smAwakeTSCtrls; + + +//----------------------------------------------------------------------------- + +namespace +{ + void _drawLine( const Point3F &p0, const Point3F &p1, const ColorI &color, F32 width ) + { + F32 x1, x2, y1, y2, z1, z2; + + x1 = p0.x; + y1 = p0.y; + z1 = p0.z; + x2 = p1.x; + y2 = p1.y; + z2 = p1.z; + + // + // Convert Line a----------b + // + // Into Quad v0---------v1 + // a b + // v2---------v3 + // + + Point2F start(x1, y1); + Point2F end(x2, y2); + Point2F perp, lineVec; + + // handle degenerate case where point a = b + if(x1 == x2 && y1 == y2) + { + perp.set(0.0f, width * 0.5f); + lineVec.set(0.1f, 0.0f); + } + else + { + perp.set(start.y - end.y, end.x - start.x); + lineVec.set(end.x - start.x, end.y - start.y); + perp.normalize(width * 0.5f); + lineVec.normalize(0.1f); + } + start -= lineVec; + end += lineVec; + + GFXVertexBufferHandle verts(GFX, 4, GFXBufferTypeVolatile); + verts.lock(); + + verts[0].point.set( start.x+perp.x, start.y+perp.y, z1 ); + verts[1].point.set( end.x+perp.x, end.y+perp.y, z2 ); + verts[2].point.set( start.x-perp.x, start.y-perp.y, z1 ); + verts[3].point.set( end.x-perp.x, end.y-perp.y, z2 ); + + verts[0].color = color; + verts[1].color = color; + verts[2].color = color; + verts[3].color = color; + + verts.unlock(); + GFX->setVertexBuffer( verts ); + + GFXStateBlockDesc desc; + desc.setCullMode(GFXCullNone); + desc.setZReadWrite(false); + desc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha); + GFX->setStateBlockByDesc( desc ); + + GFX->drawPrimitive( GFXTriangleStrip, 0, 2 ); + } +} + +//----------------------------------------------------------------------------- + +GuiTSCtrl::GuiTSCtrl() +{ + mCameraZRot = 0; + mForceFOV = 0; + mReflectPriority = 1.0f; + + mSaveModelview.identity(); + mSaveProjection.identity(); + mSaveViewport.set( 0, 0, 10, 10 ); + mSaveWorldToScreenScale.set( 0, 0 ); + + mLastCameraQuery.cameraMatrix.identity(); + mLastCameraQuery.fov = 45.0f; + mLastCameraQuery.object = NULL; + mLastCameraQuery.farPlane = 10.0f; + mLastCameraQuery.nearPlane = 0.01f; + + mLastCameraQuery.ortho = false; +} + +//----------------------------------------------------------------------------- + +void GuiTSCtrl::initPersistFields() +{ + addGroup( "Camera" ); + + addField("cameraZRot", TypeF32, Offset(mCameraZRot, GuiTSCtrl), + "Z rotation angle of camera." ); + addField("forceFOV", TypeF32, Offset(mForceFOV, GuiTSCtrl), + "The vertical field of view in degrees or zero to use the normal camera FOV." ); + + endGroup( "Camera" ); + + addGroup( "Rendering" ); + + addField( "reflectPriority", TypeF32, Offset( mReflectPriority, GuiTSCtrl ), + "The share of the per-frame reflection update work this control's rendering should run.\n" + "The reflect update priorities of all visible GuiTSCtrls are added together and each control is assigned " + "a share of the per-frame reflection update time according to its percentage of the total priority value." ); + + endGroup( "Rendering" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +void GuiTSCtrl::consoleInit() +{ + Con::addVariable("$TSControl::frameCount", TypeS32, &smFrameCount, "The number of frames that have been rendered since this control was created.\n" + "@ingroup Rendering\n"); +} + +//----------------------------------------------------------------------------- + +bool GuiTSCtrl::onWake() +{ + if ( !Parent::onWake() ) + return false; + + // Add ourselves to the active viewport list. + AssertFatal( !smAwakeTSCtrls.contains( this ), + "GuiTSCtrl::onWake - This control is already in the awake list!" ); + smAwakeTSCtrls.push_back( this ); + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiTSCtrl::onSleep() +{ + Parent::onSleep(); + + AssertFatal( smAwakeTSCtrls.contains( this ), + "GuiTSCtrl::onSleep - This control is not in the awake list!" ); + smAwakeTSCtrls.remove( this ); +} + +//----------------------------------------------------------------------------- + +void GuiTSCtrl::onPreRender() +{ + setUpdate(); +} + +//----------------------------------------------------------------------------- + +bool GuiTSCtrl::processCameraQuery(CameraQuery *) +{ + return false; +} + +//----------------------------------------------------------------------------- + +void GuiTSCtrl::renderWorld(const RectI& /*updateRect*/) +{ +} + +//----------------------------------------------------------------------------- + +F32 GuiTSCtrl::projectRadius( F32 dist, F32 radius ) const +{ + // Fixup any negative or zero distance so we + // don't get a divide by zero. + dist = dist > 0.0f ? dist : 0.001f; + return ( radius / dist ) * mSaveWorldToScreenScale.y; +} + +//----------------------------------------------------------------------------- + +bool GuiTSCtrl::project( const Point3F &pt, Point3F *dest ) const +{ + return MathUtils::mProjectWorldToScreen(pt,dest,mSaveViewport,mSaveModelview,mSaveProjection); +} + +//----------------------------------------------------------------------------- + +bool GuiTSCtrl::unproject( const Point3F &pt, Point3F *dest ) const +{ + MathUtils::mProjectScreenToWorld(pt,dest,mSaveViewport,mSaveModelview,mSaveProjection,mLastCameraQuery.farPlane,mLastCameraQuery.nearPlane); + return true; +} + +//----------------------------------------------------------------------------- + +F32 GuiTSCtrl::calculateViewDistance(F32 radius) +{ + F32 fov = mLastCameraQuery.fov; + F32 wwidth; + F32 wheight; + F32 aspectRatio = F32(getWidth()) / F32(getHeight()); + + // Use the FOV to calculate the viewport height scale + // then generate the width scale from the aspect ratio. + if(!mLastCameraQuery.ortho) + { + wheight = mLastCameraQuery.nearPlane * mTan(mLastCameraQuery.fov / 2.0f); + wwidth = aspectRatio * wheight; + } + else + { + wheight = mLastCameraQuery.fov; + wwidth = aspectRatio * wheight; + } + + // Now determine if we should use the width + // fov or height fov. + // + // If the window is taller than it is wide, use the + // width fov to keep the object completely in view. + if (wheight > wwidth) + fov = mAtan( wwidth / mLastCameraQuery.nearPlane ) * 2.0f; + + return radius / mTan(fov / 2.0f); +} + +//----------------------------------------------------------------------------- + +void GuiTSCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + // Save the current transforms so we can restore + // it for child control rendering below. + GFXTransformSaver saver; + + if(!processCameraQuery(&mLastCameraQuery)) + { + // We have no camera, but render the GUI children + // anyway. This makes editing GuiTSCtrl derived + // controls easier in the GuiEditor. + renderChildControls( offset, updateRect ); + return; + } + + if ( mReflectPriority > 0 ) + { + // Get the total reflection priority. + F32 totalPriority = 0; + for ( U32 i=0; i < smAwakeTSCtrls.size(); i++ ) + if ( smAwakeTSCtrls[i]->isVisible() ) + totalPriority += smAwakeTSCtrls[i]->mReflectPriority; + + REFLECTMGR->update( mReflectPriority / totalPriority, + getExtent(), + mLastCameraQuery ); + } + + if(mForceFOV != 0) + mLastCameraQuery.fov = mDegToRad(mForceFOV); + + if(mCameraZRot) + { + MatrixF rotMat(EulerF(0, 0, mDegToRad(mCameraZRot))); + mLastCameraQuery.cameraMatrix.mul(rotMat); + } + + // set up the camera and viewport stuff: + F32 wwidth; + F32 wheight; + F32 aspectRatio = F32(getWidth()) / F32(getHeight()); + + // Use the FOV to calculate the viewport height scale + // then generate the width scale from the aspect ratio. + if(!mLastCameraQuery.ortho) + { + wheight = mLastCameraQuery.nearPlane * mTan(mLastCameraQuery.fov / 2.0f); + wwidth = aspectRatio * wheight; + } + else + { + wheight = mLastCameraQuery.fov; + wwidth = aspectRatio * wheight; + } + + F32 hscale = wwidth * 2.0f / F32(getWidth()); + F32 vscale = wheight * 2.0f / F32(getHeight()); + + F32 left = (updateRect.point.x - offset.x) * hscale - wwidth; + F32 right = (updateRect.point.x + updateRect.extent.x - offset.x) * hscale - wwidth; + F32 top = wheight - vscale * (updateRect.point.y - offset.y); + F32 bottom = wheight - vscale * (updateRect.point.y + updateRect.extent.y - offset.y); + + Frustum frustum; + frustum.set( mLastCameraQuery.ortho, left, right, top, bottom, mLastCameraQuery.nearPlane, mLastCameraQuery.farPlane ); + + // Manipulate the frustum for tiled screenshots + const bool screenShotMode = gScreenShot && gScreenShot->isPending(); + if ( screenShotMode ) + { + gScreenShot->tileFrustum( frustum ); + GFX->setViewMatrix(MatrixF::Identity); + } + + RectI tempRect = updateRect; + +#ifdef TORQUE_OS_MAC + Point2I screensize = getRoot()->getWindowSize(); + tempRect.point.y = screensize.y - (tempRect.point.y + tempRect.extent.y); +#endif + + GFX->setViewport( tempRect ); + + // Clear the zBuffer so GUI doesn't hose object rendering accidentally + GFX->clear( GFXClearZBuffer , ColorI(20,20,20), 1.0f, 0 ); + + GFX->setFrustum( frustum ); + if(mLastCameraQuery.ortho) + { + mOrthoWidth = frustum.getWidth(); + mOrthoHeight = frustum.getHeight(); + } + + // We're going to be displaying this render at size of this control in + // pixels - let the scene know so that it can calculate e.g. reflections + // correctly for that final display result. + gClientSceneGraph->setDisplayTargetResolution(getExtent()); + + // Set the GFX world matrix to the world-to-camera transform, but don't + // change the cameraMatrix in mLastCameraQuery. This is because + // mLastCameraQuery.cameraMatrix is supposed to contain the camera-to-world + // transform. In-place invert would save a copy but mess up any GUIs that + // depend on that value. + MatrixF worldToCamera = mLastCameraQuery.cameraMatrix; + worldToCamera.inverse(); + GFX->setWorldMatrix( worldToCamera ); + + mSaveProjection = GFX->getProjectionMatrix(); + mSaveModelview = GFX->getWorldMatrix(); + mSaveViewport = updateRect; + mSaveWorldToScreenScale = GFX->getWorldToScreenScale(); + mSaveFrustum = GFX->getFrustum(); + mSaveFrustum.setTransform( mLastCameraQuery.cameraMatrix ); + + // Set the default non-clip projection as some + // objects depend on this even in non-reflect cases. + gClientSceneGraph->setNonClipProjection( mSaveProjection ); + + // Give the post effect manager the worldToCamera, and cameraToScreen matrices + PFXMGR->setFrameMatrices( mSaveModelview, mSaveProjection ); + + renderWorld(updateRect); + DebugDrawer::get()->render(); + + // Restore the previous matrix state before + // we begin rendering the child controls. + saver.restore(); + + // Allow subclasses to render 2D elements. + GFX->setClipRect(updateRect); + renderGui( offset, updateRect ); + + renderChildControls(offset, updateRect); + smFrameCount++; +} + +//----------------------------------------------------------------------------- + +void GuiTSCtrl::drawLine( Point3F p0, Point3F p1, const ColorI &color, F32 width ) +{ + if ( !mSaveFrustum.clipSegment( p0, p1 ) ) + return; + + MathUtils::mProjectWorldToScreen( p0, &p0, mSaveViewport, mSaveModelview, mSaveProjection ); + MathUtils::mProjectWorldToScreen( p1, &p1, mSaveViewport, mSaveModelview, mSaveProjection ); + + p0.x = mClampF( p0.x, 0.0f, mSaveViewport.extent.x ); + p0.y = mClampF( p0.y, 0.0f, mSaveViewport.extent.y ); + p1.x = mClampF( p1.x, 0.0f, mSaveViewport.extent.x ); + p1.y = mClampF( p1.y, 0.0f, mSaveViewport.extent.y ); + p0.z = p1.z = 0.0f; + + _drawLine( p0, p1, color, width ); +} + +//----------------------------------------------------------------------------- + +void GuiTSCtrl::drawLineList( const Vector &points, const ColorI color, F32 width ) +{ + for ( S32 i = 0; i < points.size() - 1; i++ ) + drawLine( points[i], points[i+1], color, width ); +} + +//============================================================================= +// Console Methods. +//============================================================================= +// MARK: ---- Console Methods ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTSCtrl, unproject, Point3F, ( Point3F screenPosition ),, + "Transform 3D screen-space coordinates (x, y, depth) to world space.\n" + "This method can be, for example, used to find the world-space position relating to the current mouse cursor position.\n" + "@param screenPosition The x/y position on the screen plus the depth from the screen-plane outwards.\n" + "@return The world-space position corresponding to the given screen-space coordinates." ) +{ + Point3F worldPos; + object->unproject( screenPosition, &worldPos ); + return worldPos; +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTSCtrl, project, Point3F, ( Point3F worldPosition ),, + "Transform world-space coordinates to screen-space (x, y, depth) coordinates.\n" + "@param worldPosition The world-space position to transform to screen-space.\n" + "@return The " ) +{ + Point3F screenPos; + object->project( worldPosition, &screenPos ); + return screenPos; +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTSCtrl, getWorldToScreenScale, Point2F, (),, + "Get the ratio between world-space units and pixels.\n" + "@return The amount of world-space units covered by the extent of a single pixel." ) +{ + return object->getWorldToScreenScale(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTSCtrl, calculateViewDistance, float, ( float radius ),, + "Given the camera's current FOV, get the distance from the camera's viewpoint at which the given radius will fit in the render area.\n" + "@param radius Radius in world-space units which should fit in the view.\n" + "@return The distance from the viewpoint at which the given radius would be fully visible." ) +{ + return object->calculateViewDistance( radius ); +} diff --git a/Engine/source/gui/3d/guiTSControl.h b/Engine/source/gui/3d/guiTSControl.h new file mode 100644 index 000000000..809e49c78 --- /dev/null +++ b/Engine/source/gui/3d/guiTSControl.h @@ -0,0 +1,153 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUITSCONTROL_H_ +#define _GUITSCONTROL_H_ + +#ifndef _GUICONTAINER_H_ +#include "gui/containers/guiContainer.h" +#endif +#ifndef _MMATH_H_ +#include "math/mMath.h" +#endif + +struct CameraQuery +{ + SimObject* object; + F32 nearPlane; + F32 farPlane; + F32 fov; + bool ortho; + MatrixF cameraMatrix; +}; + +/// Abstract base class for 3D viewport GUIs. +class GuiTSCtrl : public GuiContainer +{ + typedef GuiContainer Parent; + + static U32 smFrameCount; + F32 mCameraZRot; + F32 mForceFOV; + +protected: + + /// A list of GuiTSCtrl which are awake and + /// most likely rendering. + static Vector smAwakeTSCtrls; + + /// A scalar which controls how much of the reflection + /// update timeslice for this viewport to get. + F32 mReflectPriority; + + F32 mOrthoWidth; + F32 mOrthoHeight; + + MatrixF mSaveModelview; + MatrixF mSaveProjection; + RectI mSaveViewport; + Frustum mSaveFrustum; + + /// The saved world to screen space scale. + /// @see getWorldToScreenScale + Point2F mSaveWorldToScreenScale; + + /// The last camera query set in onRender. + /// @see getLastCameraQuery + CameraQuery mLastCameraQuery; + +public: + + GuiTSCtrl(); + + void onPreRender(); + void onRender(Point2I offset, const RectI &updateRect); + virtual bool processCameraQuery(CameraQuery *query); + + /// Subclasses can override this to perform 3D rendering. + virtual void renderWorld(const RectI &updateRect); + + /// Subclasses can override this to perform 2D rendering. + virtual void renderGui(Point2I offset, const RectI &updateRect) {} + + static void initPersistFields(); + static void consoleInit(); + + virtual bool onWake(); + virtual void onSleep(); + + /// Returns the last World Matrix set in onRender. + const MatrixF& getLastWorldMatrix() const { return mSaveModelview; } + + /// Returns the last Projection Matrix set in onRender. + const MatrixF& getLastProjectionMatrix() const { return mSaveProjection; } + + /// Returns the last Viewport Rect set in onRender. + const RectI& getLastViewportRect() const { return mSaveViewport; } + + /// Returns the last Frustum set in onRender. + const Frustum& getLastFrustum() const { return mSaveFrustum; } + + /// Returns the scale for converting world space + /// units to screen space units... aka pixels. + /// @see GFXDevice::getWorldToScreenScale + const Point2F& getWorldToScreenScale() const { return mSaveWorldToScreenScale; } + + /// Returns the last camera query set in onRender. + const CameraQuery& getLastCameraQuery() const { return mLastCameraQuery; } + + /// Returns the screen space X,Y and Z for world space point. + /// The input z coord is depth, from 0 to 1. + bool project( const Point3F &pt, Point3F *dest ) const; + + /// Returns the world space point for X, Y and Z. The ouput + /// z coord is depth, from 0 to 1 + bool unproject( const Point3F &pt, Point3F *dest ) const; + + /// + F32 projectRadius( F32 dist, F32 radius ) const; + + /// Returns the distance required to fit the given + /// radius within the camera's view. + F32 calculateViewDistance(F32 radius); + + /// Takes Points in World Space representing a Line or LineList. + /// These will be projected into screen space and rendered with the requested + /// width in pixels. + /// + /// This is a 2D drawing operation and should not be called from within + /// renderScene without preparing the GFX for 2D rendering first. + /// + /// These methods are NOT optimized for performance in any way and are only + /// intended for debug rendering, editor rendering, or infrequent rendering. + /// + void drawLine( Point3F p0, Point3F p1, const ColorI &color, F32 width ); + void drawLineList( const Vector &points, const ColorI color, F32 width ); + + static const U32& getFrameCount() { return smFrameCount; } + + DECLARE_CONOBJECT(GuiTSCtrl); + DECLARE_CATEGORY( "Gui 3D" ); + DECLARE_DESCRIPTION( "Abstract base class for controls that render a 3D viewport." ); +}; + +#endif // _GUITSCONTROL_H_ diff --git a/Engine/source/gui/buttons/guiBitmapButtonCtrl.cpp b/Engine/source/gui/buttons/guiBitmapButtonCtrl.cpp new file mode 100644 index 000000000..8c0f452aa --- /dev/null +++ b/Engine/source/gui/buttons/guiBitmapButtonCtrl.cpp @@ -0,0 +1,552 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/buttons/guiBitmapButtonCtrl.h" +#include "core/util/path.h" +#include "console/console.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "gui/core/guiCanvas.h" +#include "gui/core/guiDefaultControlRender.h" +#include "gfx/gfxDrawUtil.h" + + +ImplementEnumType( GuiBitmapMode, + "Rendering behavior when placing bitmaps in controls.\n\n" + "@ingroup GuiImages" ) + { GuiBitmapButtonCtrl::BitmapStretched, "Stretched", "Stretch bitmap to fit control extents." }, + { GuiBitmapButtonCtrl::BitmapCentered, "Centered", "Center bitmap in control." }, +EndImplementEnumType; + + +//============================================================================= +// GuiBitmapButtonCtrl +//============================================================================= + +IMPLEMENT_CONOBJECT(GuiBitmapButtonCtrl); + +ConsoleDocClass( GuiBitmapButtonCtrl, + "@brief A button that renders its various states (mouse over, pushed, etc.) from separate bitmaps.\n\n" + + "A bitmapped button is a push button that uses one or more texture images for rendering its individual states.\n\n" + + "To find the individual textures associated with the button, a naming scheme is used. For each state " + "a suffix is appended to the texture file name given in the GuiBitmapButtonCtrl::bitmap field:\n" + + "- \"_n\": Normal state. This one will be active when no other state applies.\n" + "- \"_h\": Highlighted state. This applies when the mouse is hovering over the button.\n" + "- \"_d\": Depressed state. This applies when the left mouse button has been clicked on the button but not yet released.\n" + "- \"_i\": Inactive state. This applies when the button control has been deactivated (GuiControl::setActive())\n\n" + + "If a bitmap for a particular state cannot be found, the default bitmap will be used. To disable all state-based " + "bitmap functionality, set useStates to false which will make the control solely render from the bitmap specified " + "in the bitmap field.\n\n" + + "@section guibitmapbutton_modifiers Per-Modifier Button Actions\n" + + "If GuiBitmapButtonCtrl::useModifiers is set to true, per-modifier button actions and textures are enabled. This functionality " + "allows to associate different images and different actions with a button depending on which modifiers are pressed " + "on the keyboard by the user.\n\n" + + "When enabled, this functionality alters the texture lookup above by prepending the following strings to the " + "suffixes listed above:\n" + + "- \"\": Default. No modifier is pressed.\n" + "- \"_ctrl\": Image to use when CTRL/CMD is down.\n" + "- \"_alt\": Image to use when ALT is down.\n" + "- \"_shift\": Image to use when SHIFT is down\n\n" + + "When this functionality is enabled, a new set of callbacks is used:\n" + + "- onDefaultClick: Button was clicked without a modifier being presssed.\n" + "- onCtrlClick: Button was clicked with the CTRL/CMD key down.\n" + "- onAltClick: Button was clicked with the ALT key down.\n" + "- onShiftClick: Button was clicked with the SHIFT key down.\n\n" + + "GuiControl::command or GuiControl::onAction() still work as before when per-modifier functionality is enabled.\n\n" + + "Note that modifiers cannot be mixed. If two or more modifiers are pressed, a single one will take precedence over " + "the remaining modifiers. The order of precedence corresponds to the order listed above.\n\n" + + "@tsexample\n" + "// Create an OK button that will trigger an onOk() call on its parent when clicked:\n" + "%okButton = new GuiBitmapButtonCtrl()\n" + "{\n" + " bitmap = \"art/gui/okButton\";\n" + " autoFitExtents = true;\n" + " command = \"$ThisControl.getParent().onOk();\";\n" + "};\n" + "@endtsexample\n\n" + + "@ingroup GuiButtons" +); + +IMPLEMENT_CALLBACK( GuiBitmapButtonCtrl, onDefaultClick, void, (), (), + "Called when per-modifier functionality is enabled and the user clicks on the button without any modifier pressed.\n" + "@ref guibitmapbutton_modifiers" ); +IMPLEMENT_CALLBACK( GuiBitmapButtonCtrl, onCtrlClick, void, (), (), + "Called when per-modifier functionality is enabled and the user clicks on the button with the CTRL key pressed.\n" + "@ref guibitmapbutton_modifiers" ); +IMPLEMENT_CALLBACK( GuiBitmapButtonCtrl, onAltClick, void, (), (), + "Called when per-modifier functionality is enabled and the user clicks on the button with the ALT key pressed.\n" + "@ref guibitmapbutton_modifiers" ); +IMPLEMENT_CALLBACK( GuiBitmapButtonCtrl, onShiftClick, void, (), (), + "Called when per-modifier functionality is enabled and the user clicks on the button with the SHIFT key pressed.\n" + "@ref guibitmapbutton_modifiers" ); + +//----------------------------------------------------------------------------- + +GuiBitmapButtonCtrl::GuiBitmapButtonCtrl() +{ + mBitmapMode = BitmapStretched; + mAutoFitExtents = false; + mUseModifiers = false; + mUseStates = true; + setExtent( 140, 30 ); +} + +//----------------------------------------------------------------------------- + +void GuiBitmapButtonCtrl::initPersistFields() +{ + addGroup( "Bitmap" ); + + addProtectedField( "bitmap", TypeStringFilename, Offset( mBitmapName, GuiBitmapButtonCtrl ), + &_setBitmap, &defaultProtectedGetFn, + "Texture file to display on this button.\n" + "If useStates is false, this will be the file that renders on the control. Otherwise, this will " + "specify the default texture name to which the various state and modifier suffixes are appended " + "to find the per-state and per-modifier (if enabled) textures." ); + addField( "bitmapMode", TYPEID< BitmapMode >(), Offset( mBitmapMode, GuiBitmapButtonCtrl ), + "Behavior for fitting the bitmap to the control extents.\n" + "If set to 'Stretched', the bitmap will be stretched both verticall and horizontally to fit inside " + "the control's extents.\n\n" + "If set to 'Centered', the bitmap will stay at its original resolution centered in the control's " + "rectangle (getting clipped if the control is smaller than the texture)." ); + addProtectedField( "autoFitExtents", TypeBool, Offset( mAutoFitExtents, GuiBitmapButtonCtrl ), + &_setAutoFitExtents, &defaultProtectedGetFn, + "If true, the control's extents will be set to match the bitmap's extents when setting the bitmap.\n" + "The bitmap extents will always be taken from the default/normal bitmap (in case the extents of the various " + "bitmaps do not match up.)" ); + addField( "useModifiers", TypeBool, Offset( mUseModifiers, GuiBitmapButtonCtrl ), + "If true, per-modifier button functionality is enabled.\n" + "@ref guibitmapbutton_modifiers" ); + addField( "useStates", TypeBool, Offset( mUseStates, GuiBitmapButtonCtrl ), + "If true, per-mouse state button functionality is enabled.\n" + "Defaults to true.\n\n" + "If you do not use per-state images on this button set this to false to speed up the loading process " + "by inhibiting searches for the individual images." ); + + endGroup( "Bitmap" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +bool GuiBitmapButtonCtrl::onWake() +{ + if (! Parent::onWake()) + return false; + + setActive( true ); + setBitmap( mBitmapName ); + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiBitmapButtonCtrl::onSleep() +{ + if( dStricmp(mBitmapName, "texhandle") != 0 ) + for( U32 i = 0; i < NumModifiers; ++ i ) + { + mTextures[ i ].mTextureNormal = NULL; + mTextures[ i ].mTextureHilight = NULL; + mTextures[ i ].mTextureDepressed = NULL; + mTextures[ i ].mTextureInactive = NULL; + } + + Parent::onSleep(); +} + +//----------------------------------------------------------------------------- + +bool GuiBitmapButtonCtrl::_setAutoFitExtents( void *object, const char *index, const char *data ) +{ + GuiBitmapButtonCtrl* ctrl = reinterpret_cast< GuiBitmapButtonCtrl* >( object ); + ctrl->setAutoFitExtents( dAtob( data ) ); + return false; +} + +//----------------------------------------------------------------------------- + +bool GuiBitmapButtonCtrl::_setBitmap( void *object, const char *index, const char *data ) +{ + GuiBitmapButtonCtrl* ctrl = reinterpret_cast< GuiBitmapButtonCtrl* >( object ); + ctrl->setBitmap( data ); + return false; +} + +//----------------------------------------------------------------------------- + +// Legacy method. Can just assign to bitmap field. +DefineEngineMethod( GuiBitmapButtonCtrl, setBitmap, void, ( const char* path ),, + "Set the bitmap to show on the button.\n" + "@param path Path to the texture file in any of the supported formats.\n" ) +{ + object->setBitmap( path ); +} + +//----------------------------------------------------------------------------- + +void GuiBitmapButtonCtrl::inspectPostApply() +{ + Parent::inspectPostApply(); + + Torque::Path path( mBitmapName ); + const String& fileName = path.getFileName(); + + if( mUseStates ) + { + // If the filename points to a single state, automatically + // cut off the state part. Makes it easy to select files in + // the editor without having to go in and manually cut off the + // state parts all the time. + + static String s_n = "_n"; + static String s_d = "_d"; + static String s_h = "_h"; + static String s_i = "_i"; + + if( fileName.endsWith( s_n ) + || fileName.endsWith( s_d ) + || fileName.endsWith( s_h ) + || fileName.endsWith( s_i ) ) + { + path.setFileName( fileName.substr( 0, fileName.length() - 2 ) ); + path.setExtension( String::EmptyString ); + } + } + + setBitmap( path.getFullPath() ); + + // if the extent is set to (0,0) in the gui editor and appy hit, this control will + // set it's extent to be exactly the size of the normal bitmap (if present) + + if ((getWidth() == 0) && (getHeight() == 0) && mTextures[ 0 ].mTextureNormal) + { + setExtent( mTextures[ 0 ].mTextureNormal->getWidth(), mTextures[ 0 ].mTextureNormal->getHeight()); + } +} + +//----------------------------------------------------------------------------- + +void GuiBitmapButtonCtrl::setAutoFitExtents( bool state ) +{ + mAutoFitExtents = state; + if( mAutoFitExtents ) + setBitmap( mBitmapName ); +} + +//----------------------------------------------------------------------------- + +void GuiBitmapButtonCtrl::setBitmap( const String& name ) +{ + PROFILE_SCOPE( GuiBitmapButtonCtrl_setBitmap ); + + mBitmapName = name; + if( !isAwake() ) + return; + + if( !mBitmapName.isEmpty() ) + { + if( dStricmp( mBitmapName, "texhandle" ) != 0 ) + { + const U32 count = mUseModifiers ? NumModifiers : 1; + for( U32 i = 0; i < count; ++ i ) + { + static String modifiers[] = + { + "", + "_ctrl", + "_alt", + "_shift" + }; + + static String s_n = "_n"; + static String s_d = "_d"; + static String s_h = "_h"; + static String s_i = "_i"; + + String baseName = mBitmapName; + if( mUseModifiers ) + baseName += modifiers[ i ]; + + mTextures[ i ].mTextureNormal = GFXTexHandle( baseName, &GFXDefaultPersistentProfile, avar("%s() - mTextureNormal (line %d)", __FUNCTION__, __LINE__)); + + if( mUseStates ) + { + if( !mTextures[ i ].mTextureNormal ) + mTextures[ i ].mTextureNormal = GFXTexHandle( baseName + s_n, &GFXDefaultPersistentProfile, avar("%s() - mTextureNormal (line %d)", __FUNCTION__, __LINE__)); + + mTextures[ i ].mTextureHilight = GFXTexHandle( baseName + s_h, &GFXDefaultPersistentProfile, avar("%s() - mTextureHighlight (line %d)", __FUNCTION__, __LINE__)); + if( !mTextures[ i ].mTextureHilight ) + mTextures[ i ].mTextureHilight = mTextures[ i ].mTextureNormal; + + mTextures[ i ].mTextureDepressed = GFXTexHandle( baseName + s_d, &GFXDefaultPersistentProfile, avar("%s() - mTextureDepressed (line %d)", __FUNCTION__, __LINE__)); + if( !mTextures[ i ].mTextureDepressed ) + mTextures[ i ].mTextureDepressed = mTextures[ i ].mTextureHilight; + + mTextures[ i ].mTextureInactive = GFXTexHandle( baseName + s_i, &GFXDefaultPersistentProfile, avar("%s() - mTextureInactive (line %d)", __FUNCTION__, __LINE__)); + if( !mTextures[ i ].mTextureInactive ) + mTextures[ i ].mTextureInactive = mTextures[ i ].mTextureNormal; + } + + if( i == 0 && mTextures[ i ].mTextureNormal.isNull() && mTextures[ i ].mTextureHilight.isNull() && mTextures[ i ].mTextureDepressed.isNull() && mTextures[ i ].mTextureInactive.isNull() ) + { + Con::warnf( "GuiBitmapButtonCtrl::setBitmap - Unable to load texture: %s", mBitmapName.c_str() ); + this->setBitmap( "core/art/unavailable" ); + return; + } + } + } + + if( mAutoFitExtents && !mTextures[ 0 ].mTextureNormal.isNull() ) + setExtent( mTextures[ 0 ].mTextureNormal.getWidth(), mTextures[ 0 ].mTextureNormal.getHeight() ); + } + else + { + for( U32 i = 0; i < NumModifiers; ++ i ) + { + mTextures[ i ].mTextureNormal = NULL; + mTextures[ i ].mTextureHilight = NULL; + mTextures[ i ].mTextureDepressed = NULL; + mTextures[ i ].mTextureInactive = NULL; + } + } + + setUpdate(); +} + +//----------------------------------------------------------------------------- + +void GuiBitmapButtonCtrl::setBitmapHandles(GFXTexHandle normal, GFXTexHandle highlighted, GFXTexHandle depressed, GFXTexHandle inactive) +{ + const U32 count = mUseModifiers ? NumModifiers : 1; + for( U32 i = 0; i < count; ++ i ) + { + mTextures[ i ].mTextureNormal = normal; + mTextures[ i ].mTextureHilight = highlighted; + mTextures[ i ].mTextureDepressed = depressed; + mTextures[ i ].mTextureInactive = inactive; + + if (!mTextures[ i ].mTextureHilight) + mTextures[ i ].mTextureHilight = mTextures[ i ].mTextureNormal; + if (!mTextures[ i ].mTextureDepressed) + mTextures[ i ].mTextureDepressed = mTextures[ i ].mTextureHilight; + if (!mTextures[ i ].mTextureInactive) + mTextures[ i ].mTextureInactive = mTextures[ i ].mTextureNormal; + + if (mTextures[ i ].mTextureNormal.isNull() && mTextures[ i ].mTextureHilight.isNull() && mTextures[ i ].mTextureDepressed.isNull() && mTextures[ i ].mTextureInactive.isNull()) + { + Con::warnf("GuiBitmapButtonCtrl::setBitmapHandles() - Invalid texture handles"); + setBitmap("core/art/unavailable"); + + return; + } + } + + mBitmapName = "texhandle"; +} + +//------------------------------------------------------------------------------ + +GuiBitmapButtonCtrl::Modifier GuiBitmapButtonCtrl::getCurrentModifier() +{ + U8 modifierKeys = Input::getModifierKeys(); + + if( modifierKeys & SI_PRIMARY_CTRL ) + return ModifierCtrl; + else if( modifierKeys & SI_PRIMARY_ALT ) + return ModifierAlt; + else if( modifierKeys & SI_SHIFT ) + return ModifierShift; + + return ModifierNone; +} + +//------------------------------------------------------------------------------ + +GFXTexHandle& GuiBitmapButtonCtrl::getTextureForCurrentState() +{ + U32 index = ModifierNone; + if( mUseModifiers ) + index = getCurrentModifier(); + + if( !mUseStates ) + { + if( mTextures[ index ].mTextureNormal ) + return mTextures[ 0 ].mTextureNormal; + else + return mTextures[ index ].mTextureNormal; + } + + switch( getState() ) + { + case NORMAL: + if( !mTextures[ index ].mTextureNormal ) + return mTextures[ 0 ].mTextureNormal; + else + return mTextures[ index ].mTextureNormal; + + case HILIGHT: + if( !mTextures[ index ].mTextureHilight ) + return mTextures[ 0 ].mTextureHilight; + else + return mTextures[ index ].mTextureHilight; + + case DEPRESSED: + if( !mTextures[ index ].mTextureDepressed ) + return mTextures[ 0 ].mTextureDepressed; + else + return mTextures[ index ].mTextureDepressed; + + default: + if( !mTextures[ index ].mTextureInactive ) + return mTextures[ 0 ].mTextureInactive; + else + return mTextures[ index ].mTextureInactive; + } +} + +//------------------------------------------------------------------------------ + +void GuiBitmapButtonCtrl::onAction() +{ + Parent::onAction(); + + if( mUseModifiers ) + { + switch( getCurrentModifier() ) + { + case ModifierNone: + onDefaultClick_callback(); + break; + + case ModifierCtrl: + onCtrlClick_callback(); + break; + + case ModifierAlt: + onAltClick_callback(); + break; + + case ModifierShift: + onShiftClick_callback(); + break; + + default: + break; + } + } +} + +//------------------------------------------------------------------------------ + +void GuiBitmapButtonCtrl::onRender(Point2I offset, const RectI& updateRect) +{ + GFXTexHandle& texture = getTextureForCurrentState(); + if( texture ) + { + renderButton( texture, offset, updateRect ); + renderChildControls( offset, updateRect ); + } + else + Parent::onRender(offset, updateRect); +} + +//------------------------------------------------------------------------------ + +void GuiBitmapButtonCtrl::renderButton( GFXTexHandle &texture, const Point2I &offset, const RectI& updateRect ) +{ + GFX->getDrawUtil()->clearBitmapModulation(); + + switch( mBitmapMode ) + { + case BitmapStretched: + { + RectI rect( offset, getExtent() ); + GFX->getDrawUtil()->drawBitmapStretch( texture, rect ); + break; + } + + case BitmapCentered: + { + Point2I p = offset; + + p.x += getExtent().x / 2 - texture.getWidth() / 2; + p.y += getExtent().y / 2 - texture.getHeight() / 2; + + GFX->getDrawUtil()->drawBitmap( texture, p ); + break; + } + } +} + +//============================================================================= +// GuiBitmapButtonTextCtrl. +//============================================================================= + +IMPLEMENT_CONOBJECT( GuiBitmapButtonTextCtrl); + +ConsoleDocClass( GuiBitmapButtonTextCtrl, + "@brief An extension of GuiBitmapButtonCtrl that additionally renders a text label on the bitmapped button.\n\n" + + "The text for the label is taken from the GuiButtonBaseCtrl::text property.\n\n" + + "For rendering, the label is placed, relative to the control's upper left corner, at the text offset specified in the " + "control's profile (GuiControlProfile::textOffset) and justified according to the profile's setting (GuiControlProfile::justify).\n\n" + + "@see GuiControlProfile::textOffset\n" + "@see GuiControlProfile::justify\n" + "@ingroup GuiButtons" +); + +//----------------------------------------------------------------------------- + +void GuiBitmapButtonTextCtrl::renderButton( GFXTexHandle &texture, const Point2I &offset, const RectI& updateRect ) +{ + Parent::renderButton( texture, offset, updateRect ); + + Point2I textPos = offset; + if(mDepressed) + textPos += Point2I(1,1); + + // Make sure we take the profile's textOffset into account. + textPos += mProfile->mTextOffset; + + GFX->getDrawUtil()->setBitmapModulation( mProfile->mFontColor ); + renderJustifiedText(textPos, getExtent(), mButtonText); +} diff --git a/Engine/source/gui/buttons/guiBitmapButtonCtrl.h b/Engine/source/gui/buttons/guiBitmapButtonCtrl.h new file mode 100644 index 000000000..c42b4f3f4 --- /dev/null +++ b/Engine/source/gui/buttons/guiBitmapButtonCtrl.h @@ -0,0 +1,193 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIBITMAPBUTTON_H_ +#define _GUIBITMAPBUTTON_H_ + +#ifndef _GUIBUTTONCTRL_H_ + #include "gui/buttons/guiButtonCtrl.h" +#endif +#ifndef _GFXTEXTUREMANAGER_H_ + #include "gfx/gfxTextureManager.h" +#endif + + +/// A button control that uses bitmaps as its different button states. +/// +/// Set 'bitmap' console field to base name of bitmaps to use. This control will +/// +/// append '_n' for normal +/// append '_h' for highlighted +/// append '_d' for depressed +/// append '_i' for inactive +/// +/// If a bitmap cannot be found it will use the default bitmap to render. +/// +/// Additionally, a bitmap button can be made to react to keyboard modifiers. These can be +/// either CTRL/CMD, ALT, or SHIFT (but no combination of them.) To assign a different bitmap +/// for a modifier state, prepend "_ctrl", _"alt", or "_shift" to the state postfix. +/// +/// To implement different handlers for the modifier states, use the "onDefaultClick", +/// "onCtrlClick", "onAltClick", and "onShiftClick" methods. +/// +class GuiBitmapButtonCtrl : public GuiButtonCtrl +{ + public: + + typedef GuiButtonCtrl Parent; + + enum BitmapMode + { + BitmapStretched, + BitmapCentered, + }; + + protected: + + enum Modifier + { + ModifierNone, + ModifierCtrl, + ModifierAlt, + ModifierShift, + + NumModifiers + }; + + enum State + { + NORMAL, + HILIGHT, + DEPRESSED, + INACTIVE + }; + + struct Textures + { + /// Texture for normal state. + GFXTexHandle mTextureNormal; + + /// Texture for highlight state. + GFXTexHandle mTextureHilight; + + /// Texture for depressed state. + GFXTexHandle mTextureDepressed; + + /// Texture for inactive state. + GFXTexHandle mTextureInactive; + }; + + /// Make control extents equal to bitmap size. + bool mAutoFitExtents; + + /// Allow switching out images according to modifier presses. + bool mUseModifiers; + + /// Allow switching images according to mouse states. On by default. + /// Switch off when not needed as it otherwise results in a lot of costly + /// texture loads. + bool mUseStates; + + /// + BitmapMode mBitmapMode; + + /// File name for bitmap. + String mBitmapName; + + /// + Textures mTextures[ NumModifiers ]; + + virtual void renderButton( GFXTexHandle &texture, const Point2I& offset, const RectI& updateRect ); + + static bool _setAutoFitExtents( void *object, const char *index, const char *data ); + static bool _setBitmap( void *object, const char *index, const char *data ); + + State getState() const + { + if( mActive ) + { + if( mDepressed || mStateOn ) return DEPRESSED; + if( mMouseOver ) return HILIGHT; + return NORMAL; + } + else + return INACTIVE; + } + + Modifier getCurrentModifier(); + GFXTexHandle& getTextureForCurrentState(); + + /// @name Callbacks + /// @{ + + DECLARE_CALLBACK( void, onDefaultClick, () ); + DECLARE_CALLBACK( void, onCtrlClick, () ); + DECLARE_CALLBACK( void, onAltClick, () ); + DECLARE_CALLBACK( void, onShiftClick, () ); + + /// @} + + public: + + GuiBitmapButtonCtrl(); + + void setAutoFitExtents( bool state ); + void setBitmap( const String& name ); + void setBitmapHandles( GFXTexHandle normal, GFXTexHandle highlighted, GFXTexHandle depressed, GFXTexHandle inactive ); + + //Parent methods + virtual bool onWake(); + virtual void onSleep(); + virtual void onAction(); + virtual void inspectPostApply(); + + virtual void onRender(Point2I offset, const RectI &updateRect); + + static void initPersistFields(); + + DECLARE_CONOBJECT(GuiBitmapButtonCtrl); + DECLARE_DESCRIPTION( "A button control rendered entirely from bitmaps.\n" + "The individual button states are represented with separate bitmaps." ); +}; + +typedef GuiBitmapButtonCtrl::BitmapMode GuiBitmapMode; +DefineEnumType( GuiBitmapMode ); + +/// Extension of GuiBitmapButtonCtrl that also display a text label on the button. +class GuiBitmapButtonTextCtrl : public GuiBitmapButtonCtrl +{ + public: + + typedef GuiBitmapButtonCtrl Parent; + + protected: + + virtual void renderButton( GFXTexHandle &texture, const Point2I& offset, const RectI& updateRect ); + + public: + + DECLARE_CONOBJECT( GuiBitmapButtonTextCtrl ); + DECLARE_DESCRIPTION( "An extension of GuiBitmapButtonCtrl that also renders a text\n" + "label on the button." ); +}; + +#endif //_GUI_BITMAP_BUTTON_CTRL_H diff --git a/Engine/source/gui/buttons/guiBorderButton.cpp b/Engine/source/gui/buttons/guiBorderButton.cpp new file mode 100644 index 000000000..6efc5bc77 --- /dev/null +++ b/Engine/source/gui/buttons/guiBorderButton.cpp @@ -0,0 +1,95 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "gfx/gfxDevice.h" +#include "gfx/gfxDrawUtil.h" +#include "gui/core/guiCanvas.h" +#include "gui/buttons/guiButtonBaseCtrl.h" +#include "gui/core/guiDefaultControlRender.h" + + +class GuiBorderButtonCtrl : public GuiButtonBaseCtrl +{ + typedef GuiButtonBaseCtrl Parent; + +protected: +public: + DECLARE_CONOBJECT(GuiBorderButtonCtrl); + + void onRender(Point2I offset, const RectI &updateRect); +}; + +IMPLEMENT_CONOBJECT(GuiBorderButtonCtrl); + +ConsoleDocClass( GuiBorderButtonCtrl, + "@brief A push button that renders only a border.\n\n" + + "A border button consists of a border rendered along its extents according to the border thickness defined in its profile " + "(GuiControlProfile::border). For the border color, a color is selected from the profile according to current button state:\n" + + "- Default state: GuiControlProfile::borderColor\n" + "- Highlighted (mouse is over the button): GuiControlProfile::fontColorHL\n" + "- Depressed (mouse button down but not yet released): GuiControlProfile::fontColorSEL\n" + + "@ingroup GuiButtons\n" +); + +void GuiBorderButtonCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + if ( mProfile->mBorder > 0 ) + { + RectI bounds( offset, getExtent() ); + for ( S32 i=0; i < mProfile->mBorderThickness; i++ ) + { + GFX->getDrawUtil()->drawRect( bounds, mProfile->mBorderColor ); + bounds.inset( 1, 1 ); + } + } + + if ( mActive ) + { + if ( mStateOn || mDepressed ) + { + RectI bounds( offset, getExtent() ); + for ( S32 i=0; i < mProfile->mBorderThickness; i++ ) + { + GFX->getDrawUtil()->drawRect( bounds, mProfile->mFontColorSEL ); + bounds.inset( 1, 1 ); + } + } + + if ( mMouseOver ) + { + RectI bounds( offset, getExtent() ); + for ( S32 i=0; i < mProfile->mBorderThickness; i++ ) + { + GFX->getDrawUtil()->drawRect( bounds, mProfile->mFontColorHL ); + bounds.inset( 1, 1 ); + } + } + } + + renderChildControls( offset, updateRect ); +} + diff --git a/Engine/source/gui/buttons/guiButtonBaseCtrl.cpp b/Engine/source/gui/buttons/guiButtonBaseCtrl.cpp new file mode 100644 index 000000000..4621d5fea --- /dev/null +++ b/Engine/source/gui/buttons/guiButtonBaseCtrl.cpp @@ -0,0 +1,544 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/buttons/guiButtonBaseCtrl.h" + +#include "console/console.h" +#include "console/engineAPI.h" +#include "gfx/gfxDevice.h" +#include "gui/core/guiCanvas.h" +#include "i18n/lang.h" +#include "sfx/sfxSystem.h" +#include "sfx/sfxTrack.h" + + +IMPLEMENT_CONOBJECT( GuiButtonBaseCtrl ); + +ConsoleDocClass( GuiButtonBaseCtrl, + "@brief The base class for the various button controls.\n\n" + + "This is the base class for the various types of button controls. If no more specific functionality is required than " + "offered by this class, then it can be instantiated and used directly. Otherwise, its subclasses should be used:\n" + + "- GuiRadioCtrl (radio buttons)\n" + "- GuiCheckBoxCtrl (checkboxes)\n" + "- GuiButtonCtrl (push buttons with text labels)\n" + "- GuiBitmapButtonCtrl (bitmapped buttons)\n" + "- GuiBitmapButtonTextCtrl (bitmapped buttons with a text label)\n" + "- GuiToggleButtonCtrl (toggle buttons, i.e. push buttons with \"sticky\" behavior)\n" + "- GuiSwatchButtonCtrl (color swatch buttons)\n" + "- GuiBorderButtonCtrl (push buttons for surrounding child controls)\n\n" + + "@ingroup GuiButtons" +); + +IMPLEMENT_CALLBACK( GuiButtonBaseCtrl, onMouseDown, void, (), (), + "If #useMouseEvents is true, this is called when the left mouse button is pressed on an (active) " + "button." ); + +IMPLEMENT_CALLBACK( GuiButtonBaseCtrl, onMouseUp, void, (), (), + "If #useMouseEvents is true, this is called when the left mouse button is release over an (active) " + "button.\n\n" + "@note To trigger actions, better use onClick() since onMouseUp() will also be called when the mouse was " + "not originally pressed on the button." ); + +IMPLEMENT_CALLBACK( GuiButtonBaseCtrl, onClick, void, (), (), + "Called when the primary action of the button is triggered (e.g. by a left mouse click)." ); + +IMPLEMENT_CALLBACK( GuiButtonBaseCtrl, onDoubleClick, void, (), (), + "Called when the left mouse button is double-clicked on the button." ); + +IMPLEMENT_CALLBACK( GuiButtonBaseCtrl, onRightClick, void, (), (), + "Called when the right mouse button is clicked on the button." ); + +IMPLEMENT_CALLBACK( GuiButtonBaseCtrl, onMouseEnter, void, (), (), + "If #useMouseEvents is true, this is called when the mouse cursor moves over the button (only if the button " + "is the front-most visible control, though)." ); + +IMPLEMENT_CALLBACK( GuiButtonBaseCtrl, onMouseLeave, void, (), (), + "If #useMouseEvents is true, this is called when the mouse cursor moves off the button (only if the button " + "had previously received an onMouseEvent() event)." ); + +IMPLEMENT_CALLBACK( GuiButtonBaseCtrl, onMouseDragged, void, (), (), + "If #useMouseEvents is true, this is called when a left mouse button drag is detected, i.e. when the user " + "pressed the left mouse button on the control and then moves the mouse over a certain distance threshold with " + "the mouse button still pressed." ); + + +ImplementEnumType( GuiButtonType, + "Type of button control.\n\n" + "@ingroup GuiButtons" ) + { GuiButtonBaseCtrl::ButtonTypePush, "PushButton", "A button that triggers an action when clicked." }, + { GuiButtonBaseCtrl::ButtonTypeCheck, "ToggleButton", "A button that is toggled between on and off state." }, + { GuiButtonBaseCtrl::ButtonTypeRadio, "RadioButton", "A button placed in groups for presenting choices." }, +EndImplementEnumType; + + +//----------------------------------------------------------------------------- + +GuiButtonBaseCtrl::GuiButtonBaseCtrl() +{ + mDepressed = false; + mMouseOver = false; + mActive = true; + static StringTableEntry sButton = StringTable->insert( "Button" ); + mButtonText = sButton; + mButtonTextID = StringTable->EmptyString(); + mStateOn = false; + mRadioGroup = -1; + mButtonType = ButtonTypePush; + mUseMouseEvents = false; + mMouseDragged = false; +} + +//----------------------------------------------------------------------------- + +void GuiButtonBaseCtrl::initPersistFields() +{ + addGroup( "Button" ); + + addField( "text", TypeCaseString, Offset(mButtonText, GuiButtonBaseCtrl), + "Text label to display on button (if button class supports text labels)." ); + addField( "textID", TypeString, Offset(mButtonTextID, GuiButtonBaseCtrl), + "ID of string in string table to use for text label on button.\n\n" + "@see setTextID\n" + "@see GuiControl::langTableMod\n" + "@see LangTable\n\n" ); + addField( "groupNum", TypeS32, Offset(mRadioGroup, GuiButtonBaseCtrl), + "Radio button toggle group number. All radio buttons that are assigned the same #groupNum and that " + "are parented to the same control will synchronize their toggle state, i.e. if one radio button is toggled on " + "all other radio buttons in its group will be toggled off.\n\n" + "The default group is -1." ); + addField( "buttonType", TYPEID< ButtonType >(), Offset(mButtonType, GuiButtonBaseCtrl), + "Button behavior type.\n" ); + addField( "useMouseEvents", TypeBool, Offset(mUseMouseEvents, GuiButtonBaseCtrl), + "If true, mouse events will be passed on to script. Default is false.\n" ); + + endGroup( "Button" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +bool GuiButtonBaseCtrl::onWake() +{ + if(!Parent::onWake()) + return false; + + // is we have a script variable, make sure we're in sync + if ( mConsoleVariable[0] ) + mStateOn = Con::getBoolVariable( mConsoleVariable ); + if(mButtonTextID && *mButtonTextID != 0) + setTextID(mButtonTextID); + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiButtonBaseCtrl::setText( const char* text ) +{ + mButtonText = StringTable->insert(text, true); +} + +//----------------------------------------------------------------------------- + +void GuiButtonBaseCtrl::setTextID(const char *id) +{ + S32 n = Con::getIntVariable(id, -1); + if(n != -1) + { + mButtonTextID = StringTable->insert(id); + setTextID(n); + } +} + +//----------------------------------------------------------------------------- + +void GuiButtonBaseCtrl::setTextID(S32 id) +{ + const UTF8 *str = getGUIString(id); + if(str) + setText((const char*)str); + //mButtonTextID = id; +} + +//----------------------------------------------------------------------------- + +const char *GuiButtonBaseCtrl::getText() +{ + return mButtonText; +} + +//----------------------------------------------------------------------------- + +void GuiButtonBaseCtrl::setStateOn( bool bStateOn ) +{ + if(!mActive) + return; + + if(mButtonType == ButtonTypeCheck) + { + mStateOn = bStateOn; + } + else if(mButtonType == ButtonTypeRadio) + { + messageSiblings(mRadioGroup); + mStateOn = bStateOn; + } + setUpdate(); +} + +//----------------------------------------------------------------------------- + +void GuiButtonBaseCtrl::acceleratorKeyPress(U32) +{ + if( !mActive ) + return; + + //set the bool + mDepressed = true; + + if (mProfile->mTabable) + setFirstResponder(); +} + +//----------------------------------------------------------------------------- + +void GuiButtonBaseCtrl::acceleratorKeyRelease(U32) +{ + if (! mActive) + return; + + if (mDepressed) + { + //set the bool + mDepressed = false; + //perform the action + onAction(); + } + + //update + setUpdate(); +} + +//----------------------------------------------------------------------------- + +void GuiButtonBaseCtrl::onMouseDown(const GuiEvent &event) +{ + if (! mActive) + return; + + if (mProfile->mCanKeyFocus) + setFirstResponder(); + + if (mProfile->mSoundButtonDown) + SFX->playOnce(mProfile->mSoundButtonDown); + + mMouseDownPoint = event.mousePoint; + mMouseDragged = false; + + if( mUseMouseEvents ) + onMouseDown_callback(); + + //lock the mouse + mouseLock(); + mDepressed = true; + + // If we have a double click then execute the alt command. + if ( event.mouseClickCount == 2 ) + { + onDoubleClick_callback(); + execAltConsoleCallback(); + } + + //update + setUpdate(); +} + +//----------------------------------------------------------------------------- + +void GuiButtonBaseCtrl::onMouseEnter(const GuiEvent &event) +{ + setUpdate(); + + if( mUseMouseEvents ) + onMouseEnter_callback(); + + if(isMouseLocked()) + { + mDepressed = true; + mMouseOver = true; + } + else + { + if ( mActive && mProfile->mSoundButtonOver ) + SFX->playOnce(mProfile->mSoundButtonOver); + + mMouseOver = true; + } +} + +//----------------------------------------------------------------------------- + +void GuiButtonBaseCtrl::onMouseLeave(const GuiEvent &) +{ + setUpdate(); + + if( mUseMouseEvents ) + onMouseLeave_callback(); + if( isMouseLocked() ) + mDepressed = false; + mMouseOver = false; +} + +//----------------------------------------------------------------------------- + +void GuiButtonBaseCtrl::onMouseUp(const GuiEvent &event) +{ + mouseUnlock(); + + if( !mActive ) + return; + + setUpdate(); + + if( mUseMouseEvents ) + onMouseUp_callback(); + + //if we released the mouse within this control, perform the action + if( mDepressed ) + onAction(); + + mDepressed = false; + mMouseDragged = false; +} + +//----------------------------------------------------------------------------- + +void GuiButtonBaseCtrl::onRightMouseUp(const GuiEvent &event) +{ + onRightClick_callback(); + Parent::onRightMouseUp( event ); +} + +//----------------------------------------------------------------------------- + +void GuiButtonBaseCtrl::onMouseDragged( const GuiEvent& event ) +{ + if( mUseMouseEvents ) + { + // If we haven't started a drag yet, find whether we have moved past + // the tolerance value. + + if( !mMouseDragged ) + { + Point2I delta = mMouseDownPoint - event.mousePoint; + if( mAbs( delta.x ) > 2 || mAbs( delta.y ) > 2 ) + mMouseDragged = true; + } + + if( mMouseDragged ) + onMouseDragged_callback(); + } + + Parent::onMouseDragged( event ); +} + +//----------------------------------------------------------------------------- + +bool GuiButtonBaseCtrl::onKeyDown(const GuiEvent &event) +{ + //if the control is a dead end, kill the event + if (!mActive) + return true; + + //see if the key down is a return or space or not + if ((event.keyCode == KEY_RETURN || event.keyCode == KEY_SPACE) + && event.modifier == 0) + { + if ( mProfile->mSoundButtonDown ) + SFX->playOnce( mProfile->mSoundButtonDown); + + return true; + } + //otherwise, pass the event to it's parent + return Parent::onKeyDown(event); +} + +//----------------------------------------------------------------------------- + +bool GuiButtonBaseCtrl::onKeyUp(const GuiEvent &event) +{ + //if the control is a dead end, kill the event + if (!mActive) + return true; + + //see if the key down is a return or space or not + if (mDepressed && + (event.keyCode == KEY_RETURN || event.keyCode == KEY_SPACE) && + event.modifier == 0) + { + onAction(); + return true; + } + + //otherwise, pass the event to it's parent + return Parent::onKeyUp(event); +} + +//----------------------------------------------------------------------------- + +void GuiButtonBaseCtrl::setScriptValue(const char *value) +{ + mStateOn = dAtob(value); + + // Update the console variable: + if ( mConsoleVariable[0] ) + Con::setBoolVariable( mConsoleVariable, mStateOn ); + + setUpdate(); +} + +//----------------------------------------------------------------------------- + +const char *GuiButtonBaseCtrl::getScriptValue() +{ + return mStateOn ? "1" : "0"; +} + +//----------------------------------------------------------------------------- + +void GuiButtonBaseCtrl::onAction() +{ + if(!mActive) + return; + + if(mButtonType == ButtonTypeCheck) + { + mStateOn = mStateOn ? false : true; + } + else if(mButtonType == ButtonTypeRadio) + { + mStateOn = true; + messageSiblings(mRadioGroup); + } + setUpdate(); + + // Update the console variable: + if ( mConsoleVariable[0] ) + Con::setBoolVariable( mConsoleVariable, mStateOn ); + + onClick_callback(); + Parent::onAction(); +} + +//----------------------------------------------------------------------------- + +void GuiButtonBaseCtrl::onMessage( GuiControl *sender, S32 msg ) +{ + Parent::onMessage(sender, msg); + if( mRadioGroup == msg && mButtonType == ButtonTypeRadio ) + { + setUpdate(); + mStateOn = ( sender == this ); + + // Update the console variable: + if ( mConsoleVariable[0] ) + Con::setBoolVariable( mConsoleVariable, mStateOn ); + } +} + +//============================================================================= +// Console Methods. +//============================================================================= +// MARK: ---- Console Methods ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiButtonBaseCtrl, performClick, void, (),, + "Simulate a click on the button.\n" + "This method will trigger the button's action just as if the button had been pressed by the " + "user.\n\n" ) +{ + object->onAction(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiButtonBaseCtrl, setText, void, ( const char* text ),, + "Set the text displayed on the button's label.\n" + "@param text The text to display as the button's text label.\n" + "@note Not all buttons render text labels.\n\n" + "@see getText\n" + "@see setTextID\n" ) +{ + object->setText( text ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiButtonBaseCtrl, setTextID, void, ( const char* id ),, + "Set the text displayed on the button's label using a string from the string table " + "assigned to the control.\n\n" + "@param id Name of the variable that contains the integer string ID. Used to look up " + "string in table.\n\n" + "@note Not all buttons render text labels.\n\n" + "@see setText\n" + "@see getText\n" + "@see GuiControl::langTableMod\n" + "@see LangTable\n\n" + "@ref Gui_i18n" ) +{ + object->setTextID( id ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiButtonBaseCtrl, getText, const char*, (),, + "Get the text display on the button's label (if any).\n\n" + "@return The button's label." ) +{ + return object->getText( ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiButtonBaseCtrl, setStateOn, void, ( bool isOn ), ( true ), + "For toggle or radio buttons, set whether the button is currently activated or not. For radio buttons, " + "toggling a button on will toggle all other radio buttons in its group to off.\n\n" + "@param isOn If true, the button will be toggled on (if not already); if false, it will be toggled off.\n\n" + "@note Toggling the state of a button with this method will not not trigger the action associated with the " + "button. To do that, use performClick()." ) +{ + object->setStateOn( isOn ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiButtonBaseCtrl, resetState, void, (),, + "Reset the mousing state of the button.\n\n" + "This method should not generally be called." ) +{ + object->resetState(); +} diff --git a/Engine/source/gui/buttons/guiButtonBaseCtrl.h b/Engine/source/gui/buttons/guiButtonBaseCtrl.h new file mode 100644 index 000000000..0e3be53a7 --- /dev/null +++ b/Engine/source/gui/buttons/guiButtonBaseCtrl.h @@ -0,0 +1,127 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIBUTTONBASECTRL_H_ +#define _GUIBUTTONBASECTRL_H_ + +#ifndef _GUICONTROL_H_ + #include "gui/core/guiControl.h" +#endif + + +/// Base class for all button controls. Subclasses are mostly for specific +/// rendering types. +/// +class GuiButtonBaseCtrl : public GuiControl +{ + public: + + typedef GuiControl Parent; + + enum ButtonType + { + ButtonTypePush, + ButtonTypeCheck, + ButtonTypeRadio, + }; + + protected: + + StringTableEntry mButtonText; + StringTableEntry mButtonTextID; + bool mDepressed; + bool mMouseOver; + bool mStateOn; + S32 mButtonType; + S32 mRadioGroup; + bool mUseMouseEvents; + + /// Point where left mouse button was pressed down. Used to find when to start + /// a mouse drag. + Point2I mMouseDownPoint; + + /// + bool mMouseDragged; + + /// @name Callbacks + /// @{ + + DECLARE_CALLBACK( void, onMouseDown, () ); + DECLARE_CALLBACK( void, onMouseUp, () ); + DECLARE_CALLBACK( void, onClick, () ); + DECLARE_CALLBACK( void, onRightClick, () ); + DECLARE_CALLBACK( void, onDoubleClick, () ); + DECLARE_CALLBACK( void, onMouseEnter, () ); + DECLARE_CALLBACK( void, onMouseLeave, () ); + DECLARE_CALLBACK( void, onMouseDragged, () ); + + /// @} + + public: + + GuiButtonBaseCtrl(); + bool onWake(); + + DECLARE_CONOBJECT( GuiButtonBaseCtrl ); + DECLARE_CATEGORY( "Gui Buttons" ); + DECLARE_DESCRIPTION( "A basic button control." ); + + static void initPersistFields(); + + void setText(const char *text); + void setTextID(S32 id); + void setTextID(const char *id); + const char *getText(); + void setStateOn( bool bStateOn ); + bool getStateOn() const { return mStateOn; } + + void setDepressed( bool depressed ) { mDepressed = depressed; } + void resetState() {mDepressed = false; mMouseOver = false;} + + void acceleratorKeyPress(U32 index); + void acceleratorKeyRelease(U32 index); + + void onMouseDown(const GuiEvent &); + void onMouseUp(const GuiEvent &); + void onMouseDragged( const GuiEvent& event ); + void onRightMouseUp(const GuiEvent &); + + void onMouseEnter(const GuiEvent &); + void onMouseLeave(const GuiEvent &); + + bool onKeyDown(const GuiEvent &event); + bool onKeyUp(const GuiEvent &event); + + void setScriptValue(const char *value); + const char *getScriptValue(); + + void onMessage(GuiControl *,S32 msg); + void onAction(); + + bool usesMouseEvents() const { return mUseMouseEvents; } + void setUseMouseEvents( bool val ) { mUseMouseEvents = val; } +}; + +typedef GuiButtonBaseCtrl::ButtonType GuiButtonType; +DefineEnumType( GuiButtonType ); + +#endif diff --git a/Engine/source/gui/buttons/guiButtonCtrl.cpp b/Engine/source/gui/buttons/guiButtonCtrl.cpp new file mode 100644 index 000000000..14d6f1aa7 --- /dev/null +++ b/Engine/source/gui/buttons/guiButtonCtrl.cpp @@ -0,0 +1,124 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/buttons/guiButtonCtrl.h" + +#include "console/console.h" +#include "console/consoleTypes.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxDrawUtil.h" +#include "gui/core/guiCanvas.h" +#include "gui/core/guiDefaultControlRender.h" + +IMPLEMENT_CONOBJECT(GuiButtonCtrl); + +ConsoleDocClass( GuiButtonCtrl, + "@brief The most widely used button class.\n\n" + + "GuiButtonCtrl renders seperately of, but utilizes all of the functionality of GuiBaseButtonCtrl.\n" + "This grants GuiButtonCtrl the versatility to be either of the 3 button types.\n\n" + + "@tsexample\n" + "// Create a PushButton GuiButtonCtrl that calls randomFunction when clicked\n" + "%button = new GuiButtonCtrl()\n" + "{\n" + " profile = \"GuiButtonProfile\";\n" + " buttonType = \"PushButton\";\n" + " command = \"randomFunction();\";\n" + "};\n" + "@endtsexample\n\n" + + "@ingroup GuiButtons" +); + + +//----------------------------------------------------------------------------- + +GuiButtonCtrl::GuiButtonCtrl() +{ + setExtent(140, 30); + mButtonText = StringTable->EmptyString(); +} + +//----------------------------------------------------------------------------- + +bool GuiButtonCtrl::onWake() +{ + if( !Parent::onWake() ) + return false; + + // Button Theme? + if( mProfile->constructBitmapArray() >= 36 ) + mHasTheme = true; + else + mHasTheme = false; + + return true; + +} + +//----------------------------------------------------------------------------- + +void GuiButtonCtrl::onRender(Point2I offset, + const RectI& updateRect) +{ + bool highlight = mMouseOver; + bool depressed = mDepressed; + + ColorI fontColor = mActive ? ( highlight ? mProfile->mFontColorHL : mProfile->mFontColor ) : mProfile->mFontColorNA; + ColorI fillColor = mActive ? ( highlight ? mProfile->mFillColorHL : mProfile->mFillColor ) : mProfile->mFillColorNA; + ColorI borderColor = mActive ? ( highlight ? mProfile->mBorderColorHL : mProfile->mBorderColor ) : mProfile->mBorderColorNA; + + RectI boundsRect(offset, getExtent()); + + if( !mHasTheme ) + { + if( mProfile->mBorder != 0 ) + renderFilledBorder( boundsRect, borderColor, fillColor, mProfile->mBorderThickness ); + else + GFX->getDrawUtil()->drawRectFill( boundsRect, fillColor ); + } + else + { + S32 indexMultiplier = 1; + + if( !mActive ) + indexMultiplier = 4; + else if ( mDepressed || mStateOn ) + indexMultiplier = 2; + else if ( mMouseOver ) + indexMultiplier = 3; + + renderSizableBitmapBordersFilled( boundsRect, indexMultiplier, mProfile ); + } + + Point2I textPos = offset; + if( depressed ) + textPos += Point2I( 1, 1 ); + + GFX->getDrawUtil()->setBitmapModulation( fontColor ); + renderJustifiedText( textPos, getExtent(), mButtonText ); + + //render the children + renderChildControls( offset, updateRect); +} \ No newline at end of file diff --git a/Engine/source/gui/buttons/guiButtonCtrl.h b/Engine/source/gui/buttons/guiButtonCtrl.h new file mode 100644 index 000000000..475b16bd5 --- /dev/null +++ b/Engine/source/gui/buttons/guiButtonCtrl.h @@ -0,0 +1,42 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIBUTTONCTRL_H_ +#define _GUIBUTTONCTRL_H_ + +#ifndef _GUIBUTTONBASECTRL_H_ +#include "gui/buttons/guiButtonBaseCtrl.h" +#endif + +class GuiButtonCtrl : public GuiButtonBaseCtrl +{ + typedef GuiButtonBaseCtrl Parent; +protected: + bool mHasTheme; +public: + DECLARE_CONOBJECT(GuiButtonCtrl); + GuiButtonCtrl(); + bool onWake(); + void onRender(Point2I offset, const RectI &updateRect); +}; + +#endif //_GUI_BUTTON_CTRL_H diff --git a/Engine/source/gui/buttons/guiCheckBoxCtrl.cpp b/Engine/source/gui/buttons/guiCheckBoxCtrl.cpp new file mode 100644 index 000000000..d927552d0 --- /dev/null +++ b/Engine/source/gui/buttons/guiCheckBoxCtrl.cpp @@ -0,0 +1,207 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/buttons/guiCheckBoxCtrl.h" + +#include "console/console.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxDrawUtil.h" +#include "gui/core/guiCanvas.h" +#include "sfx/sfxSystem.h" +#include "sfx/sfxTrack.h" + + +IMPLEMENT_CONOBJECT( GuiCheckBoxCtrl ); + +ConsoleDocClass( GuiCheckBoxCtrl, + "@brief A named checkbox that can be toggled on and off.\n\n" + + "A GuiCheckBoxCtrl displays a text label next to a checkbox that can be toggled on and off by the user. " + "Checkboxes are usually used to present boolean choices like, for example, a switch to toggle fullscreen " + "video on and off.\n\n" + + "@tsexample\n" + "// Create a checkbox that allows to toggle fullscreen on and off.\n" + "new GuiCheckBoxCtrl( FullscreenToggle )\n" + "{\n" + " text = \"Fullscreen\";\n" + "};\n" + "\n" + "// Set the initial state to match the current fullscreen setting.\n" + "FullscreenToggle.setStateOn( Canvas.isFullscreen() );\n" + "\n" + "// Define function to be called when checkbox state is toggled.\n" + "function FullscreenToggle::onClick( %this )\n" + "{\n" + " Canvas.toggleFullscreen();\n" + "}\n" + "@endtsexample\n\n" + + "@ingroup GuiButtons" +); + + +//----------------------------------------------------------------------------- + +GuiCheckBoxCtrl::GuiCheckBoxCtrl() +{ + setExtent(140, 30); + mStateOn = false; + mIndent = 0; + mButtonType = ButtonTypeCheck; +} + +//----------------------------------------------------------------------------- + +bool GuiCheckBoxCtrl::onWake() +{ + if(!Parent::onWake()) + return false; + + // make sure there is a bitmap array for this control type + // if it is declared as such in the control + if( !mProfile->mBitmapArrayRects.size() && !mProfile->constructBitmapArray() ) + { + Con::errorf( "GuiCheckBoxCtrl::onWake - failed to create bitmap array from profile '%s'", mProfile->getName() ); + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiCheckBoxCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + // RLP/Sickhead NOTE: New/experimental code + // for notifying the GuiCheckBoxCtrl of changes + // to its mConsoleVariable's state. + if ( mConsoleVariable[0] ) + { + bool stateOn = Con::getBoolVariable( mConsoleVariable ); + if ( stateOn != mStateOn ) + mStateOn = stateOn; + } + + ColorI backColor = mActive ? mProfile->mFillColor : mProfile->mFillColorNA; + ColorI fontColor = mActive ? (mMouseOver ? mProfile->mFontColorHL : mProfile->mFontColor) : mProfile->mFontColorNA; + ColorI insideBorderColor = isFirstResponder() ? mProfile->mBorderColorHL : mProfile->mBorderColor; + + // just draw the check box and the text: + S32 xOffset = 0; + GFX->getDrawUtil()->clearBitmapModulation(); + if(mProfile->mBitmapArrayRects.size() >= 4) + { + S32 index = mStateOn; + if(!mActive) + { + if(mProfile->mBitmapArrayRects.size() >= 6) + { + // New style checkbox bitmap with 6 images + index = 4 + mStateOn; + } + else + { + // Old style checkbox bitmap + index = 2; + } + } + else if(mDepressed) + { + index += 2; + } + xOffset = mProfile->mBitmapArrayRects[0].extent.x + 2 + mIndent; + S32 y = (getHeight() - mProfile->mBitmapArrayRects[0].extent.y) / 2; + GFX->getDrawUtil()->drawBitmapSR(mProfile->mTextureObject, offset + Point2I(mIndent, y), mProfile->mBitmapArrayRects[index]); + } + + if(mButtonText[0] != '\0') + { + GFX->getDrawUtil()->setBitmapModulation( fontColor ); + renderJustifiedText(Point2I(offset.x + xOffset, offset.y), + Point2I(getWidth() - getHeight(), getHeight()), + mButtonText); + } + //render the children + renderChildControls(offset, updateRect); +} + +//----------------------------------------------------------------------------- + +void GuiCheckBoxCtrl::autoSize() +{ + U32 width, height; + U32 bmpArrayRect0Width = 0; + + if( !mAwake ) + { + mProfile->incLoadCount(); + + if( !mProfile->mBitmapArrayRects.size() ) + mProfile->constructBitmapArray(); + if( mProfile->mBitmapArrayRects.size() ) + bmpArrayRect0Width = mProfile->mBitmapArrayRects[ 0 ].extent.x; + } + + U32 bmpWidth = bmpArrayRect0Width + 2 + mIndent; + U32 strWidth = mProfile->mFont->getStrWidthPrecise( mButtonText ); + + width = bmpWidth + strWidth + 2; + + U32 bmpHeight = mProfile->mBitmapArrayRects[0].extent.y; + U32 fontHeight = mProfile->mFont->getHeight(); + + height = getMax( bmpHeight, fontHeight ) + 4; + + setExtent( width, height ); + + if( !mAwake ) + mProfile->decLoadCount(); +} + +//============================================================================= +// API. +//============================================================================= +// MARK: ---- API ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiCheckBoxCtrl, setStateOn, void, ( bool newState ),, + "Set whether the checkbox is ticked or not.\n" + "@param newState If true the box will be checked, if false, it will be unchecked.\n\n" + "@note This method will @b not trigger the command associated with the control. To toggle the " + "checkbox state as if the user had clicked the control, use performClick()." ) +{ + object->setStateOn( newState ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiCheckBoxCtrl, isStateOn, bool, (),, + "Test whether the checkbox is currently checked.\n" + "@return True if the checkbox is currently ticked, false otherwise.\n" ) +{ + return object->getStateOn(); +} diff --git a/Engine/source/gui/buttons/guiCheckBoxCtrl.h b/Engine/source/gui/buttons/guiCheckBoxCtrl.h new file mode 100644 index 000000000..d01ee65c4 --- /dev/null +++ b/Engine/source/gui/buttons/guiCheckBoxCtrl.h @@ -0,0 +1,58 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUICHECKBOXCTRL_H_ +#define _GUICHECKBOXCTRL_H_ + +#ifndef _GUIBUTTONBASECTRL_H_ + #include "gui/buttons/guiButtonBaseCtrl.h" +#endif + + +/// A checkbox button. +class GuiCheckBoxCtrl : public GuiButtonBaseCtrl +{ + public: + + typedef GuiButtonBaseCtrl Parent; + + protected: + + S32 mIndent; + + public: + + GuiCheckBoxCtrl(); + + S32 getIndent() const { return mIndent; } + void setIndent( S32 value ) { mIndent = value; } + + void onRender( Point2I offset, const RectI &updateRect ); + bool onWake(); + + void autoSize(); + + DECLARE_CONOBJECT( GuiCheckBoxCtrl ); + DECLARE_DESCRIPTION( "A toggle button that displays a text label and an on/off checkbox." ); +}; + +#endif //_GUI_CHECKBOX_CTRL_H diff --git a/Engine/source/gui/buttons/guiIconButtonCtrl.cpp b/Engine/source/gui/buttons/guiIconButtonCtrl.cpp new file mode 100644 index 000000000..55a0efb16 --- /dev/null +++ b/Engine/source/gui/buttons/guiIconButtonCtrl.cpp @@ -0,0 +1,439 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + + +//------------------------------------- +// +// Icon Button Control +// Draws the bitmap within a special button control. Only a single bitmap is used and the +// button will be drawn in a highlighted mode when the mouse hovers over it or when it +// has been clicked. +// +// Use mTextLocation to choose where within the button the text will be drawn, if at all. +// Use mTextMargin to set the text away from the button sides or from the bitmap. +// Use mButtonMargin to set everything away from the button sides. +// Use mErrorBitmapName to set the name of a bitmap to draw if the main bitmap cannot be found. +// Use mFitBitmapToButton to force the bitmap to fill the entire button extent. Usually used +// with no button text defined. +// +// + +#include "platform/platform.h" +#include "gui/buttons/guiIconButtonCtrl.h" + +#include "console/console.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxDrawUtil.h" +#include "console/consoleTypes.h" +#include "gui/core/guiCanvas.h" +#include "gui/core/guiDefaultControlRender.h" +#include "console/engineAPI.h" + +static const ColorI colorWhite(255,255,255); +static const ColorI colorBlack(0,0,0); + +IMPLEMENT_CONOBJECT(GuiIconButtonCtrl); + +ConsoleDocClass( GuiIconButtonCtrl, + "@brief Draws the bitmap within a special button control. Only a single bitmap is used and the\n" + "button will be drawn in a highlighted mode when the mouse hovers over it or when it\n" + "has been clicked.\n\n" + + "@tsexample\n" + "new GuiIconButtonCtrl(TestIconButton)\n" + "{\n" + " buttonMargin = \"4 4\";\n" + " iconBitmap = \"art/gui/lagIcon.png\";\n" + " iconLocation = \"Center\";\n" + " sizeIconToButton = \"0\";\n" + " makeIconSquare = \"1\";\n" + " textLocation = \"Bottom\";\n" + " textMargin = \"-2\";\n" + " autoSize = \"0\";\n" + " text = \"Lag Icon\";\n" + " textID = \"\"STR_LAG\"\";\n" + " buttonType = \"PushButton\";\n" + " profile = \"GuiIconButtonProfile\";\n" + "};\n" + "@endtsexample\n\n" + + "@see GuiControl\n" + "@see GuiButtonCtrl\n\n" + + "@ingroup GuiCore\n" +); + + +GuiIconButtonCtrl::GuiIconButtonCtrl() +{ + mBitmapName = StringTable->insert(""); + mTextLocation = TextLocLeft; + mIconLocation = IconLocLeft; + mTextMargin = 4; + mButtonMargin.set(4,4); + + mFitBitmapToButton = false; + mMakeIconSquare = false; + + mErrorBitmapName = StringTable->insert(""); + mErrorTextureHandle = NULL; + + mAutoSize = false; + + setExtent(140, 30); +} + +ImplementEnumType( GuiIconButtonTextLocation, + "\n\n" + "@ingroup GuiImages" ) + { GuiIconButtonCtrl::TextLocNone, "None" }, + { GuiIconButtonCtrl::TextLocBottom, "Bottom" }, + { GuiIconButtonCtrl::TextLocRight, "Right" }, + { GuiIconButtonCtrl::TextLocTop, "Top" }, + { GuiIconButtonCtrl::TextLocLeft, "Left" }, + { GuiIconButtonCtrl::TextLocCenter, "Center" }, +EndImplementEnumType; + +ImplementEnumType( GuiIconButtonIconLocation, + "\n\n" + "@ingroup GuiImages" ) + { GuiIconButtonCtrl::IconLocNone, "None" }, + { GuiIconButtonCtrl::IconLocLeft, "Left" }, + { GuiIconButtonCtrl::IconLocRight, "Right" }, + { GuiIconButtonCtrl::IconLocCenter, "Center" } +EndImplementEnumType; + +void GuiIconButtonCtrl::initPersistFields() +{ + addField( "buttonMargin", TypePoint2I, Offset( mButtonMargin, GuiIconButtonCtrl ),"Margin area around the button.\n"); + addField( "iconBitmap", TypeFilename, Offset( mBitmapName, GuiIconButtonCtrl ),"Bitmap file for the icon to display on the button.\n"); + addField( "iconLocation", TYPEID< IconLocation >(), Offset( mIconLocation, GuiIconButtonCtrl ),"Where to place the icon on the control. Options are 0 (None), 1 (Left), 2 (Right), 3 (Center).\n"); + addField( "sizeIconToButton", TypeBool, Offset( mFitBitmapToButton, GuiIconButtonCtrl ),"If true, the icon will be scaled to be the same size as the button.\n"); + addField( "makeIconSquare", TypeBool, Offset( mMakeIconSquare, GuiIconButtonCtrl ),"If true, will make sure the icon is square.\n"); + addField( "textLocation", TYPEID< TextLocation >(), Offset( mTextLocation, GuiIconButtonCtrl ),"Where to place the text on the control.\n" + "Options are 0 (None), 1 (Bottom), 2 (Right), 3 (Top), 4 (Left), 5 (Center).\n"); + addField( "textMargin", TypeS32, Offset( mTextMargin, GuiIconButtonCtrl ),"Margin between the icon and the text.\n"); + addField( "autoSize", TypeBool, Offset( mAutoSize, GuiIconButtonCtrl ),"If true, the text and icon will be automatically sized to the size of the control.\n"); + Parent::initPersistFields(); +} + +bool GuiIconButtonCtrl::onWake() +{ + if (! Parent::onWake()) + return false; + setActive(true); + + setBitmap(mBitmapName); + + if( mProfile ) + mProfile->constructBitmapArray(); + + return true; +} + +void GuiIconButtonCtrl::onSleep() +{ + mTextureNormal = NULL; + Parent::onSleep(); +} + +void GuiIconButtonCtrl::inspectPostApply() +{ + Parent::inspectPostApply(); +} + +void GuiIconButtonCtrl::onStaticModified(const char* slotName, const char* newValue) +{ + if ( isProperlyAdded() && !dStricmp(slotName, "autoSize") ) + resize( getPosition(), getExtent() ); +} + +bool GuiIconButtonCtrl::resize(const Point2I &newPosition, const Point2I &newExtent) +{ + if ( !mAutoSize || !mProfile->mFont ) + return Parent::resize( newPosition, newExtent ); + + Point2I autoExtent( mMinExtent ); + + if ( mIconLocation != IconLocNone ) + { + autoExtent.y = mTextureNormal.getHeight() + mButtonMargin.y * 2; + autoExtent.x = mTextureNormal.getWidth() + mButtonMargin.x * 2; + } + + if ( mTextLocation != TextLocNone && mButtonText && mButtonText[0] ) + { + U32 strWidth = mProfile->mFont->getStrWidthPrecise( mButtonText ); + + if ( mTextLocation == TextLocLeft || mTextLocation == TextLocRight ) + { + autoExtent.x += strWidth + mTextMargin * 2; + } + else // Top, Bottom, Center + { + strWidth += mTextMargin * 2; + if ( strWidth > autoExtent.x ) + autoExtent.x = strWidth; + } + } + + return Parent::resize( newPosition, autoExtent ); +} + +void GuiIconButtonCtrl::setBitmap(const char *name) +{ + mBitmapName = StringTable->insert(name); + if(!isAwake()) + return; + + if (*mBitmapName) + { + mTextureNormal = GFXTexHandle( name, &GFXDefaultPersistentProfile, avar("%s() - mTextureNormal (line %d)", __FUNCTION__, __LINE__) ); + } + else + { + mTextureNormal = NULL; + } + + // So that extent is recalculated if autoSize is set. + resize( getPosition(), getExtent() ); + + setUpdate(); +} + +void GuiIconButtonCtrl::onRender(Point2I offset, const RectI& updateRect) +{ + renderButton( offset, updateRect); +} + +void GuiIconButtonCtrl::renderButton( Point2I &offset, const RectI& updateRect ) +{ + bool highlight = mMouseOver; + bool depressed = mDepressed; + + ColorI fontColor = mActive ? (highlight ? mProfile->mFontColor : mProfile->mFontColor) : mProfile->mFontColorNA; + + RectI boundsRect(offset, getExtent()); + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + + if (mDepressed || mStateOn) + { + // If there is a bitmap array then render using it. + // Otherwise use a standard fill. + if(mProfile->mUseBitmapArray && mProfile->mBitmapArrayRects.size()) + renderBitmapArray(boundsRect, statePressed); + else + renderSlightlyLoweredBox(boundsRect, mProfile); + } + else if(mMouseOver && mActive) + { + // If there is a bitmap array then render using it. + // Otherwise use a standard fill. + if(mProfile->mUseBitmapArray && mProfile->mBitmapArrayRects.size()) + renderBitmapArray(boundsRect, stateMouseOver); + else + renderSlightlyRaisedBox(boundsRect, mProfile); + } + else + { + // If there is a bitmap array then render using it. + // Otherwise use a standard fill. + if(mProfile->mUseBitmapArray && mProfile->mBitmapArrayRects.size()) + { + if(mActive) + renderBitmapArray(boundsRect, stateNormal); + else + renderBitmapArray(boundsRect, stateDisabled); + } + else + { + drawer->drawRectFill(boundsRect, mProfile->mFillColorNA); + drawer->drawRect(boundsRect, mProfile->mBorderColorNA); + } + } + + Point2I textPos = offset; + if(depressed) + textPos += Point2I(1,1); + + RectI iconRect( 0, 0, 0, 0 ); + + // Render the icon + if ( mTextureNormal && mIconLocation != GuiIconButtonCtrl::IconLocNone ) + { + // Render the normal bitmap + drawer->clearBitmapModulation(); + + // Maintain the bitmap size or fill the button? + if ( !mFitBitmapToButton ) + { + Point2I textureSize( mTextureNormal->getWidth(), mTextureNormal->getHeight() ); + iconRect.set( offset + mButtonMargin, textureSize ); + + if ( mIconLocation == IconLocRight ) + { + iconRect.point.x = ( offset.x + getWidth() ) - ( mButtonMargin.x + textureSize.x ); + iconRect.point.y = offset.y + ( getHeight() - textureSize.y ) / 2; + } + else if ( mIconLocation == IconLocLeft ) + { + iconRect.point.x = offset.x + mButtonMargin.x; + iconRect.point.y = offset.y + ( getHeight() - textureSize.y ) / 2; + } + else if ( mIconLocation == IconLocCenter ) + { + iconRect.point.x = offset.x + ( getWidth() - textureSize.x ) / 2; + iconRect.point.y = offset.y + ( getHeight() - textureSize.y ) / 2; + } + + drawer->drawBitmapStretch( mTextureNormal, iconRect ); + + } + else + { + iconRect.set( offset + mButtonMargin, getExtent() - (mButtonMargin * 2) ); + + if ( mMakeIconSquare ) + { + // Square the icon to the smaller axis extent. + if ( iconRect.extent.x < iconRect.extent.y ) + iconRect.extent.y = iconRect.extent.x; + else + iconRect.extent.x = iconRect.extent.y; + } + + drawer->drawBitmapStretch( mTextureNormal, iconRect ); + } + } + + // Render text + if ( mTextLocation != TextLocNone ) + { + // Clip text to fit (appends ...), + // pad some space to keep it off our border + String text( mButtonText ); + S32 textWidth = clipText( text, getWidth() - 4 - mTextMargin ); + + drawer->setBitmapModulation( fontColor ); + + if ( mTextLocation == TextLocRight ) + { + Point2I start( mTextMargin, ( getHeight() - mProfile->mFont->getHeight() ) / 2 ); + if ( mTextureNormal && mIconLocation != IconLocNone ) + { + start.x = iconRect.extent.x + mButtonMargin.x + mTextMargin; + } + + drawer->drawText( mProfile->mFont, start + offset, text, mProfile->mFontColors ); + } + + if ( mTextLocation == TextLocLeft ) + { + Point2I start( mTextMargin, ( getHeight() - mProfile->mFont->getHeight() ) / 2 ); + + drawer->drawText( mProfile->mFont, start + offset, text, mProfile->mFontColors ); + } + + if ( mTextLocation == TextLocCenter ) + { + Point2I start; + if ( mTextureNormal && mIconLocation == IconLocLeft ) + { + start.set( ( getWidth() - textWidth - iconRect.extent.x ) / 2 + iconRect.extent.x, + ( getHeight() - mProfile->mFont->getHeight() ) / 2 ); + } + else + start.set( ( getWidth() - textWidth ) / 2, ( getHeight() - mProfile->mFont->getHeight() ) / 2 ); + drawer->setBitmapModulation( fontColor ); + drawer->drawText( mProfile->mFont, start + offset, text, mProfile->mFontColors ); + } + + if ( mTextLocation == TextLocBottom ) + { + Point2I start; + start.set( ( getWidth() - textWidth ) / 2, getHeight() - mProfile->mFont->getHeight() - mTextMargin ); + + // If the text is longer then the box size + // it will get clipped, force Left Justify + if( textWidth > getWidth() ) + start.x = 0; + + drawer->setBitmapModulation( fontColor ); + drawer->drawText( mProfile->mFont, start + offset, text, mProfile->mFontColors ); + } + } + + renderChildControls( offset, updateRect); +} + +// Draw the bitmap array's borders according to the button's state. +void GuiIconButtonCtrl::renderBitmapArray(RectI &bounds, S32 state) +{ + switch(state) + { + case stateNormal: + if(mProfile->mBorder == -2) + renderSizableBitmapBordersFilled(bounds, 1, mProfile); + else + renderFixedBitmapBordersFilled(bounds, 1, mProfile); + break; + + case stateMouseOver: + if(mProfile->mBorder == -2) + renderSizableBitmapBordersFilled(bounds, 2, mProfile); + else + renderFixedBitmapBordersFilled(bounds, 2, mProfile); + break; + + case statePressed: + if(mProfile->mBorder == -2) + renderSizableBitmapBordersFilled(bounds, 3, mProfile); + else + renderFixedBitmapBordersFilled(bounds, 3, mProfile); + break; + + case stateDisabled: + if(mProfile->mBorder == -2) + renderSizableBitmapBordersFilled(bounds, 4, mProfile); + else + renderFixedBitmapBordersFilled(bounds, 4, mProfile); + break; + } +} + +DefineEngineMethod( GuiIconButtonCtrl, setBitmap, void, (const char* buttonFilename),, + "@brief Set the bitmap to use for the button portion of this control.\n\n" + "@param buttonFilename Filename for the image\n" + "@tsexample\n" + "// Define the button filename\n" + "%buttonFilename = \"pearlButton\";\n\n" + "// Inform the GuiIconButtonCtrl control to update its main button graphic to the defined bitmap\n" + "%thisGuiIconButtonCtrl.setBitmap(%buttonFilename);\n" + "@endtsexample\n\n" + "@see GuiControl\n" + "@see GuiButtonCtrl\n\n") +{ + char* argBuffer = Con::getArgBuffer( 512 ); + Platform::makeFullPathName( buttonFilename, argBuffer, 512 ); + object->setBitmap( argBuffer ); +} \ No newline at end of file diff --git a/Engine/source/gui/buttons/guiIconButtonCtrl.h b/Engine/source/gui/buttons/guiIconButtonCtrl.h new file mode 100644 index 000000000..9767e7257 --- /dev/null +++ b/Engine/source/gui/buttons/guiIconButtonCtrl.h @@ -0,0 +1,125 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIICONBUTTON_H_ +#define _GUIICONBUTTON_H_ + +#ifndef _GUIBUTTONCTRL_H_ +#include "gui/buttons/guiButtonCtrl.h" +#endif +#ifndef GFX_Texture_Manager_H_ +#include "gfx/gfxTextureManager.h" +#endif + + +/// The GuiIconButtonCtrl draws an icon and text caption within a normal +/// button control with several layout options. +/// +class GuiIconButtonCtrl : public GuiButtonCtrl +{ +private: + typedef GuiButtonCtrl Parent; + +protected: + + StringTableEntry mBitmapName; + GFXTexHandle mTextureNormal; + S32 mIconLocation; + S32 mTextLocation; + S32 mTextMargin; + Point2I mButtonMargin; + + /// Make the bitmap fill the button extent. + bool mFitBitmapToButton; + + /// Keep a square aspect ration on the icon. + bool mMakeIconSquare; + + /// Calculate extent based on icon size, text width, and layout options. + bool mAutoSize; + + // Optional bitmap to be displayed when the proper bitmap cannot be found + StringTableEntry mErrorBitmapName; + GFXTexHandle mErrorTextureHandle; + + void renderButton( Point2I &offset, const RectI& updateRect); + + enum + { + stateNormal, + stateMouseOver, + statePressed, + stateDisabled, + }; + + void renderBitmapArray(RectI &bounds, S32 state); + +public: + enum TextLocation + { + TextLocNone, + TextLocBottom, + TextLocRight, + TextLocTop, + TextLocLeft, + TextLocCenter, + }; + + enum IconLocation + { + IconLocNone, + IconLocLeft, + IconLocRight, + IconLocCenter + }; + + + DECLARE_CONOBJECT(GuiIconButtonCtrl); + DECLARE_DESCRIPTION( "A button control that displays an icon on the button in addition\n" + "to the optional text label." ); + + GuiIconButtonCtrl(); + + static void initPersistFields(); + + //Parent methods + bool onWake(); + void onSleep(); + void inspectPostApply(); + void onStaticModified(const char* slotName, const char* newValue = NULL); + bool resize(const Point2I &newPosition, const Point2I &newExtent); + + void setBitmap(const char *name); + + // Used to set the optional error bitmap + void setErrorBitmap(const char *name); + + void onRender(Point2I offset, const RectI &updateRect); +}; + +typedef GuiIconButtonCtrl::TextLocation GuiIconButtonTextLocation; +typedef GuiIconButtonCtrl::IconLocation GuiIconButtonIconLocation; + +DefineEnumType( GuiIconButtonTextLocation ); +DefineEnumType( GuiIconButtonIconLocation ); + +#endif //_GUIICONBUTTON_H_ diff --git a/Engine/source/gui/buttons/guiRadioCtrl.cpp b/Engine/source/gui/buttons/guiRadioCtrl.cpp new file mode 100644 index 000000000..34764cd09 --- /dev/null +++ b/Engine/source/gui/buttons/guiRadioCtrl.cpp @@ -0,0 +1,61 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/buttons/guiRadioCtrl.h" + +#include "console/console.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "gfx/gfxDevice.h" +#include "gui/core/guiCanvas.h" + +IMPLEMENT_CONOBJECT(GuiRadioCtrl); + +ConsoleDocClass( GuiRadioCtrl, + "@brief A button based around the radio concept.\n\n" + + "GuiRadioCtrl's functionality is based around GuiButtonBaseCtrl's ButtonTypeRadio type.\n\n" + + "A button control with a radio box and a text label.\n" + "This control is used in groups where multiple radio buttons\n" + "present a range of options out of which one can be chosen.\n" + "A radio button automatically signals its siblings when it is\n" + "toggled on.\n\n" + + "@tsexample\n" + "// Create a GuiCheckBoxCtrl that calls randomFunction with its current value when clicked.\n" + "%radio = new GuiRadioCtrl()\n" + "{\n" + " profile = \"GuiRadioProfile\";\n" + "};\n" + "@endtsexample\n\n" + + "@ingroup GuiButtons" +); + +//----------------------------------------------------------------------------- + +GuiRadioCtrl::GuiRadioCtrl() +{ + mButtonType = ButtonTypeRadio; +} + diff --git a/Engine/source/gui/buttons/guiRadioCtrl.h b/Engine/source/gui/buttons/guiRadioCtrl.h new file mode 100644 index 000000000..5bcaf3247 --- /dev/null +++ b/Engine/source/gui/buttons/guiRadioCtrl.h @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIRADIOCTRL_H_ +#define _GUIRADIOCTRL_H_ + +#ifndef _GUICHECKBOXCTRLL_H_ +#include "gui/buttons/guiCheckBoxCtrl.h" +#endif + +// the radio button renders exactly the same as the check box +// the only difference is it sends messages to its siblings to +// turn themselves off. + +class GuiRadioCtrl : public GuiCheckBoxCtrl +{ + typedef GuiCheckBoxCtrl Parent; + +public: + DECLARE_CONOBJECT(GuiRadioCtrl); + DECLARE_DESCRIPTION( "A button control with a radio box and a text label.\n" + "This control is used in groups where multiple radio buttons\n" + "present a range of options out of which one can be chosen.\n" + "A radio button automatically signals its siblings when it is\n" + "toggled on." ); + GuiRadioCtrl(); +}; + +#endif //_GUI_RADIO_CTRL_H diff --git a/Engine/source/gui/buttons/guiSwatchButtonCtrl.cpp b/Engine/source/gui/buttons/guiSwatchButtonCtrl.cpp new file mode 100644 index 000000000..8067cd95a --- /dev/null +++ b/Engine/source/gui/buttons/guiSwatchButtonCtrl.cpp @@ -0,0 +1,123 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/buttons/guiSwatchButtonCtrl.h" + +#include "console/console.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxDrawUtil.h" +#include "gui/core/guiCanvas.h" +#include "gui/core/guiDefaultControlRender.h" + +IMPLEMENT_CONOBJECT( GuiSwatchButtonCtrl ); + +ConsoleDocClass( GuiSwatchButtonCtrl, + "@brief A button that is used to represent color; often used in correlation with a color picker.\n\n" + + "A swatch button is a push button that uses its color field to designate the color drawn over an image, on top of a button.\n\n" + + "The color itself is a float value stored inside the GuiSwatchButtonCtrl::color field. The texture path that represents\n" + "the image underlying the color is stored inside the GuiSwatchButtonCtrl::bitmap field.\n" + "The default value assigned toGuiSwatchButtonCtrl::color is \"1 1 1 1\"( White ). The default/fallback image assigned to \n" + "GuiSwatchButtonCtrl::bitmap is \"core/art/gui/images/transp_grid\".\n\n" + + "@tsexample\n" + "// Create a GuiSwatchButtonCtrl that calls randomFunction with its current color when clicked\n" + "%swatchButton = new GuiSwatchButtonCtrl()\n" + "{\n" + " profile = \"GuiInspectorSwatchButtonProfile\";\n" + " command = \"randomFunction( $ThisControl.color );\";\n" + "};\n" + "@endtsexample\n\n" + + "@ingroup GuiButtons" +); + +//----------------------------------------------------------------------------- + +GuiSwatchButtonCtrl::GuiSwatchButtonCtrl() + : mSwatchColor( 1, 1, 1, 1 ) +{ + mButtonText = StringTable->insert( "" ); + setExtent(140, 30); + + static StringTableEntry sProfile = StringTable->insert( "profile" ); + setDataField( sProfile, NULL, "GuiInspectorSwatchButtonProfile" ); +} + +void GuiSwatchButtonCtrl::initPersistFields() +{ + addField( "color", TypeColorF, Offset( mSwatchColor, GuiSwatchButtonCtrl ), "The foreground color of GuiSwatchButtonCtrl" ); + + Parent::initPersistFields(); +} + +bool GuiSwatchButtonCtrl::onWake() +{ + if ( !Parent::onWake() ) + return false; + + if ( mGrid.isNull() ) + mGrid.set( "core/art/gui/images/transp_grid", &GFXDefaultGUIProfile, avar("%s() - mGrid (line %d)", __FUNCTION__, __LINE__) ); + + return true; +} + +void GuiSwatchButtonCtrl::onRender( Point2I offset, const RectI &updateRect ) +{ + bool highlight = mMouseOver; + + ColorI backColor = mSwatchColor; + ColorI borderColor = mActive ? ( highlight ? mProfile->mBorderColorHL : mProfile->mBorderColor ) : mProfile->mBorderColorNA; + + RectI renderRect( offset, getExtent() ); + if ( !highlight ) + renderRect.inset( 1, 1 ); + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + drawer->clearBitmapModulation(); + + // Draw background transparency grid texture... + if ( mGrid.isValid() ) + drawer->drawBitmapStretch( mGrid, renderRect ); + + // Draw swatch color as fill... + drawer->drawRectFill( renderRect, mSwatchColor ); + + // Draw any borders... + drawer->drawRect( renderRect, borderColor ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiSwatchButtonCtrl, setColor, void, ( const char* newColor ),, + "Set the color of the swatch control.\n" + "@param newColor The new color string given to the swatch control in float format \"r g b a\".\n" + "@note It's also important to note that when setColor is called causes\n" + "the control's altCommand field to be executed." ) +{ + object->setField( "color", newColor ); + object->execAltConsoleCallback(); +} diff --git a/Engine/source/gui/buttons/guiSwatchButtonCtrl.h b/Engine/source/gui/buttons/guiSwatchButtonCtrl.h new file mode 100644 index 000000000..38b26c2fe --- /dev/null +++ b/Engine/source/gui/buttons/guiSwatchButtonCtrl.h @@ -0,0 +1,67 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUISWATCHBUTTONCTRL_H_ +#define _GUISWATCHBUTTONCTRL_H_ + +#ifndef _GUIBUTTONBASECTRL_H_ + #include "gui/buttons/guiButtonBaseCtrl.h" +#endif + + +/// A color swatch button. +/// +class GuiSwatchButtonCtrl : public GuiButtonBaseCtrl +{ + public: + + typedef GuiButtonBaseCtrl Parent; + + protected: + + /// The color to display on the button. + ColorF mSwatchColor; + + /// Background texture that will show through with transparent colors. + GFXTexHandle mGrid; + + public: + + GuiSwatchButtonCtrl(); + + /// Return the color displayed in the swatch. + ColorF getColor() { return mSwatchColor; } + + /// Set the color to display in the swatch. + void setColor( const ColorF &color ) { mSwatchColor = color; } + + // GuiButtonBaseCtrl + virtual bool onWake(); + virtual void onRender(Point2I offset, const RectI &updateRect); + + static void initPersistFields(); + + DECLARE_CONOBJECT( GuiSwatchButtonCtrl ); + DECLARE_DESCRIPTION( "A color swatch button." ); +}; + +#endif // _GUISWATCHBUTTONCTRL_H_ diff --git a/Engine/source/gui/buttons/guiToggleButtonCtrl.cpp b/Engine/source/gui/buttons/guiToggleButtonCtrl.cpp new file mode 100644 index 000000000..e5aa4b0f5 --- /dev/null +++ b/Engine/source/gui/buttons/guiToggleButtonCtrl.cpp @@ -0,0 +1,108 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/buttons/guiToggleButtonCtrl.h" + +#include "console/console.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxDrawUtil.h" +#include "gui/core/guiCanvas.h" +#include "gui/core/guiDefaultControlRender.h" + +IMPLEMENT_CONOBJECT(GuiToggleButtonCtrl); + +ConsoleDocClass( GuiToggleButtonCtrl, + "@brief Deprecated gui control.\n\n" + + "@deprecated GuiToggleButtonCtrl's functionality is solely based on GuiButtonBaseCtrl's ButtonTypeCheck type.\n\n" + + "@see GuiButtonCtrl\n" + "@see GuiCheckBoxCtrl\n" + + "@ingroup GuiButtons" +); + +//----------------------------------------------------------------------------- + +GuiToggleButtonCtrl::GuiToggleButtonCtrl() +{ + setExtent(140, 30); + mButtonText = StringTable->insert(""); + mStateOn = false; + mButtonType = ButtonTypeCheck; +} + +void GuiToggleButtonCtrl::onPreRender() +{ + Parent::onPreRender(); + + // If we have a script variable, make sure we're in sync + if ( mConsoleVariable[0] ) + mStateOn = Con::getBoolVariable( mConsoleVariable ); +} + +void GuiToggleButtonCtrl::onRender(Point2I offset, + const RectI& updateRect) +{ + bool highlight = mMouseOver; + bool depressed = mDepressed; + + ColorI fontColor = mActive ? ( highlight ? mProfile->mFontColorHL : mProfile->mFontColor ) : mProfile->mFontColorNA; + ColorI fillColor = mActive ? ( highlight ? mProfile->mFillColorHL : mProfile->mFillColor ) : mProfile->mFillColorNA; + ColorI borderColor = mActive ? ( highlight ? mProfile->mBorderColorHL : mProfile->mBorderColor ) : mProfile->mBorderColorNA; + + RectI boundsRect(offset, getExtent()); + + if( !mHasTheme ) + { + if( mProfile->mBorder != 0 ) + renderFilledBorder( boundsRect, borderColor, fillColor, mProfile->mBorderThickness ); + else + GFX->getDrawUtil()->drawRectFill( boundsRect, fillColor ); + } + else if( mHasTheme ) + { + S32 indexMultiplier = 1; + if ( !mActive ) + indexMultiplier = 4; + else if ( mDepressed || mStateOn ) + indexMultiplier = 2; + else if ( mMouseOver ) + indexMultiplier = 3; + + + renderSizableBitmapBordersFilled( boundsRect, indexMultiplier, mProfile ); + } + + Point2I textPos = offset; + if(depressed) + textPos += Point2I(1,1); + + GFX->getDrawUtil()->setBitmapModulation( fontColor ); + renderJustifiedText(textPos, getExtent(), mButtonText); + + //render the children + renderChildControls( offset, updateRect); +} diff --git a/Engine/source/gui/buttons/guiToggleButtonCtrl.h b/Engine/source/gui/buttons/guiToggleButtonCtrl.h new file mode 100644 index 000000000..6b379224e --- /dev/null +++ b/Engine/source/gui/buttons/guiToggleButtonCtrl.h @@ -0,0 +1,41 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUITOGGLEBUTTONCTRL_H_ +#define _GUITOGGLEBUTTONCTRL_H_ + +#ifndef _GUIBUTTONCTRL_H_ +#include "gui/buttons/guiButtonCtrl.h" +#endif + +class GuiToggleButtonCtrl : public GuiButtonCtrl +{ + typedef GuiButtonCtrl Parent; +public: + DECLARE_CONOBJECT(GuiToggleButtonCtrl); + GuiToggleButtonCtrl(); + + virtual void onPreRender(); + void onRender(Point2I offset, const RectI &updateRect); +}; + +#endif //_GUITOGGLEBUTTONCTRL_H_ diff --git a/Engine/source/gui/buttons/guiToolboxButtonCtrl.cpp b/Engine/source/gui/buttons/guiToolboxButtonCtrl.cpp new file mode 100644 index 000000000..a2a0b962a --- /dev/null +++ b/Engine/source/gui/buttons/guiToolboxButtonCtrl.cpp @@ -0,0 +1,227 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/buttons/guiToolboxButtonCtrl.h" + +#include "console/console.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxDrawUtil.h" +#include "console/consoleTypes.h" +#include "gui/core/guiCanvas.h" +#include "gui/core/guiDefaultControlRender.h" + + +IMPLEMENT_CONOBJECT(GuiToolboxButtonCtrl); + +ConsoleDocClass( GuiToolboxButtonCtrl, + "@brief Unimplemented GUI control meant to interact with Toolbox.\n\n" + "For Torque 3D editors only, soon to be deprecated\n\n" + "@internal" +); + +//------------------------------------- +GuiToolboxButtonCtrl::GuiToolboxButtonCtrl() +{ + mNormalBitmapName = StringTable->insert(""); + mLoweredBitmapName = StringTable->insert("sceneeditor/client/images/buttondown"); + mHoverBitmapName = StringTable->insert("sceneeditor/client/images/buttonup"); + setMinExtent(Point2I(16,16)); + setExtent(48, 48); + mButtonType = ButtonTypeRadio; + mTipHoverTime = 100; + +} + + +//------------------------------------- +void GuiToolboxButtonCtrl::initPersistFields() +{ + addField("normalBitmap", TypeFilename, Offset(mNormalBitmapName, GuiToolboxButtonCtrl)); + addField("loweredBitmap", TypeFilename, Offset(mLoweredBitmapName, GuiToolboxButtonCtrl)); + addField("hoverBitmap", TypeFilename, Offset(mHoverBitmapName, GuiToolboxButtonCtrl)); + Parent::initPersistFields(); +} + + +//------------------------------------- +bool GuiToolboxButtonCtrl::onWake() +{ + if (! Parent::onWake()) + return false; + + setActive( true ); + + setNormalBitmap( mNormalBitmapName ); + setLoweredBitmap( mLoweredBitmapName ); + setHoverBitmap( mHoverBitmapName ); + + return true; +} + + +//------------------------------------- +void GuiToolboxButtonCtrl::onSleep() +{ + mTextureNormal = NULL; + mTextureLowered = NULL; + mTextureHover = NULL; + Parent::onSleep(); +} + + +//------------------------------------- + +ConsoleMethod( GuiToolboxButtonCtrl, setNormalBitmap, void, 3, 3, "( filepath name ) sets the bitmap that shows when the button is active") +{ + object->setNormalBitmap(argv[2]); +} + +ConsoleMethod( GuiToolboxButtonCtrl, setLoweredBitmap, void, 3, 3, "( filepath name ) sets the bitmap that shows when the button is disabled") +{ + object->setLoweredBitmap(argv[2]); +} + +ConsoleMethod( GuiToolboxButtonCtrl, setHoverBitmap, void, 3, 3, "( filepath name ) sets the bitmap that shows when the button is disabled") +{ + object->setHoverBitmap(argv[2]); +} + +//------------------------------------- +void GuiToolboxButtonCtrl::inspectPostApply() +{ + // if the extent is set to (0,0) in the gui editor and appy hit, this control will + // set it's extent to be exactly the size of the normal bitmap (if present) + Parent::inspectPostApply(); + + if ((getWidth() == 0) && (getHeight() == 0) && mTextureNormal) + { + setExtent( mTextureNormal->getWidth(), mTextureNormal->getHeight()); + } +} + + +//------------------------------------- +void GuiToolboxButtonCtrl::setNormalBitmap( StringTableEntry bitmapName ) +{ + mNormalBitmapName = StringTable->insert( bitmapName ); + + if(!isAwake()) + return; + + if ( *mNormalBitmapName ) + mTextureNormal = GFXTexHandle( mNormalBitmapName, &GFXDefaultPersistentProfile, avar("%s() - mTextureNormal (line %d)", __FUNCTION__, __LINE__) ); + else + mTextureNormal = NULL; + + setUpdate(); +} + +void GuiToolboxButtonCtrl::setLoweredBitmap( StringTableEntry bitmapName ) +{ + mLoweredBitmapName = StringTable->insert( bitmapName ); + + if(!isAwake()) + return; + + if ( *mLoweredBitmapName ) + mTextureLowered = GFXTexHandle( mLoweredBitmapName, &GFXDefaultPersistentProfile, avar("%s() - mTextureLowered (line %d)", __FUNCTION__, __LINE__) ); + else + mTextureLowered = NULL; + + setUpdate(); +} + +void GuiToolboxButtonCtrl::setHoverBitmap( StringTableEntry bitmapName ) +{ + mHoverBitmapName = StringTable->insert( bitmapName ); + + if(!isAwake()) + return; + + if ( *mHoverBitmapName ) + mTextureHover = GFXTexHandle( mHoverBitmapName, &GFXDefaultPersistentProfile, avar("%s() - mTextureHover (line %d)", __FUNCTION__, __LINE__) ); + else + mTextureHover = NULL; + + setUpdate(); +} + + + +//------------------------------------- +void GuiToolboxButtonCtrl::onRender(Point2I offset, const RectI& updateRect) +{ + // Only render the state rect (hover/down) if we're active + if (mActive) + { + RectI r(offset, getExtent()); + if ( mDepressed || mStateOn ) + renderStateRect( mTextureLowered , r ); + else if ( mMouseOver ) + renderStateRect( mTextureHover , r ); + } + + // Now render the image + if( mTextureNormal ) + { + renderButton( mTextureNormal, offset, updateRect ); + return; + } + + Point2I textPos = offset; + if( mDepressed ) + textPos += Point2I(1,1); + + // Make sure we take the profile's textOffset into account. + textPos += mProfile->mTextOffset; + + GFX->getDrawUtil()->setBitmapModulation( mProfile->mFontColor ); + renderJustifiedText(textPos, getExtent(), mButtonText); + +} + +void GuiToolboxButtonCtrl::renderStateRect( GFXTexHandle &texture, const RectI& rect ) +{ + if (texture) + { + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapStretch( texture, rect ); + } +} + +//------------------------------------------------------------------------------ + +void GuiToolboxButtonCtrl::renderButton(GFXTexHandle &texture, Point2I &offset, const RectI& updateRect) +{ + if (texture) + { + Point2I finalOffset = offset; + + finalOffset.x += ( ( getWidth() / 2 ) - ( texture.getWidth() / 2 ) ); + finalOffset.y += ( ( getHeight() / 2 ) - ( texture.getHeight() / 2 ) ); + + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmap(texture, finalOffset); + renderChildControls( offset, updateRect); + } +} \ No newline at end of file diff --git a/Engine/source/gui/buttons/guiToolboxButtonCtrl.h b/Engine/source/gui/buttons/guiToolboxButtonCtrl.h new file mode 100644 index 000000000..f90cdfa87 --- /dev/null +++ b/Engine/source/gui/buttons/guiToolboxButtonCtrl.h @@ -0,0 +1,70 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUITOOLBOXBUTTON_H_ +#define _GUITOOLBOXBUTTON_H_ + +#ifndef _GUIBUTTONCTRL_H_ +#include "gui/buttons/guiButtonCtrl.h" +#endif +#ifndef GFX_Texture_Manager_H_ +#include "gfx/gfxTextureManager.h" +#endif + +class GuiToolboxButtonCtrl : public GuiButtonCtrl +{ +private: + typedef GuiButtonCtrl Parent; + +protected: + StringTableEntry mNormalBitmapName; + StringTableEntry mLoweredBitmapName; + StringTableEntry mHoverBitmapName; + + GFXTexHandle mTextureNormal; + GFXTexHandle mTextureLowered; + GFXTexHandle mTextureHover; + + void renderButton(GFXTexHandle &texture, Point2I &offset, const RectI& updateRect); + void renderStateRect( GFXTexHandle &texture, const RectI& rect ); + +public: + DECLARE_CONOBJECT(GuiToolboxButtonCtrl); + GuiToolboxButtonCtrl(); + + static void initPersistFields(); + + //Parent methods + bool onWake(); + void onSleep(); + void inspectPostApply(); + + void setNormalBitmap( StringTableEntry bitmapName ); + void setLoweredBitmap( StringTableEntry bitmapName ); + void setHoverBitmap( StringTableEntry bitmapName ); + + + void onRender(Point2I offset, const RectI &updateRect); +}; + + +#endif //_GUITOOLBOXBUTTON_H_ diff --git a/Engine/source/gui/containers/guiAutoScrollCtrl.cpp b/Engine/source/gui/containers/guiAutoScrollCtrl.cpp new file mode 100644 index 000000000..d37c0ea0f --- /dev/null +++ b/Engine/source/gui/containers/guiAutoScrollCtrl.cpp @@ -0,0 +1,358 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/containers/guiAutoScrollCtrl.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" + + +IMPLEMENT_CONOBJECT( GuiAutoScrollCtrl ); + +ConsoleDocClass( GuiAutoScrollCtrl, + "@brief A container that scrolls its child control up over time.\n\n" + + "This container can be used to scroll a single child control in either of the four directions.\n\n" + + "@tsexample\n" + "// Create a GuiAutoScrollCtrl that scrolls a long text of credits.\n" + "new GuiAutoScrollCtrl( CreditsScroller )\n" + "{\n" + " position = \"0 0\";\n" + " extent = Canvas.extent.x SPC Canvas.extent.y;\n" + "\n" + " scrollDirection = \"Up\"; // Scroll upwards.\n" + " startDelay = 4; // Wait 4 seconds before starting to scroll.\n" + " isLooping = false; // Don't loop the credits.\n" + " scrollOutOfSight = true; // Scroll up fully.\n" + "\n" + " new GuiMLTextCtrl()\n" + " {\n" + " text = $CREDITS;\n" + " };\n" + "};\n" + "\n" + "function CreditsScroller::onComplete( %this )\n" + "{\n" + " // Switch back to main menu after credits have rolled.\n" + " Canvas.setContent( MainMenu );\n" + "}\n" + "\n" + "// Start rolling credits.\n" + "Canvas.setContent( CreditsScroller );\n" + "@endtsexample\n\n" + + "@note Only the first child will be scrolled.\n\n" + + "@ingroup GuiContainers" +); + + +IMPLEMENT_CALLBACK( GuiAutoScrollCtrl, onTick, void, (), (), + "Called every 32ms on the control." ); +IMPLEMENT_CALLBACK( GuiAutoScrollCtrl, onStart, void, (), (), + "Called when the control starts to scroll." ); +IMPLEMENT_CALLBACK( GuiAutoScrollCtrl, onComplete, void, (), (), + "Called when the child control has been scrolled in entirety." ); +IMPLEMENT_CALLBACK( GuiAutoScrollCtrl, onReset, void, (), (), + "Called when the child control is reset to its initial position and the cycle starts again." ); + + +ImplementEnumType( GuiAutoScrollDirection, + "Direction in which to scroll the child control.\n\n" + "@ingroup GuiContainers" ) + { GuiAutoScrollCtrl::Up, "Up", "Scroll from bottom towards top." }, + { GuiAutoScrollCtrl::Down, "Down", "Scroll from top towards bottom." }, + { GuiAutoScrollCtrl::Left, "Left", "Scroll from right towards left." }, + { GuiAutoScrollCtrl::Right, "Right", "Scroll from left towards right." }, +EndImplementEnumType; + +//----------------------------------------------------------------------------- + +GuiAutoScrollCtrl::GuiAutoScrollCtrl() + : mDirection( Up ), + mIsLooping( true ), + mCurrentPhase( PhaseComplete ), + mCurrentTime( 0.f ), + mStartDelay( 3.f ), + mResetDelay( 5.f ), + mChildBorder( 10 ), + mScrollSpeed( 1.f ), + mScrollOutOfSight( false ) +{ + mIsContainer = true; +} + +//----------------------------------------------------------------------------- + +void GuiAutoScrollCtrl::initPersistFields() +{ + addGroup( "Scrolling" ); + + addField( "scrollDirection", TYPEID< Direction >(), Offset( mDirection, GuiAutoScrollCtrl ), + "Direction in which the child control is moved." ); + addField( "startDelay", TypeF32, Offset( mStartDelay, GuiAutoScrollCtrl ), + "Seconds to wait before starting to scroll." ); + addField( "resetDelay", TypeF32, Offset( mResetDelay, GuiAutoScrollCtrl ), + "Seconds to wait after scrolling completes before resetting and starting over.\n\n" + "@note Only takes effect if #isLooping is true." ); + addField( "childBorder", TypeS32, Offset( mChildBorder, GuiAutoScrollCtrl ), + "Padding to put around child control (in pixels)." ); + addField( "scrollSpeed", TypeF32, Offset( mScrollSpeed, GuiAutoScrollCtrl ), + "Scrolling speed in pixels per second." ); + addField( "isLooping", TypeBool, Offset( mIsLooping, GuiAutoScrollCtrl ), + "If true, the scrolling will reset to the beginning once completing a cycle." ); + addField( "scrollOutOfSight", TypeBool, Offset( mScrollOutOfSight, GuiAutoScrollCtrl ), + "If true, the child control will be completely scrolled out of sight; otherwise it will only scroll " + "until the other end becomes visible." ); + + endGroup( "Scrolling" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +bool GuiAutoScrollCtrl::onWake() +{ + if( !Parent::onWake() ) + return false; + + setProcessTicks( true ); + return true; +} + +//----------------------------------------------------------------------------- + +void GuiAutoScrollCtrl::onSleep() +{ + setProcessTicks( false ); + Parent::onSleep(); +} + +//----------------------------------------------------------------------------- + +void GuiAutoScrollCtrl::onChildAdded( GuiControl* control ) +{ + _reset( control ); + Parent::onChildAdded( control ); +} + +//----------------------------------------------------------------------------- + +void GuiAutoScrollCtrl::onChildRemoved( GuiControl* control ) +{ + mCurrentPhase = PhaseComplete; + Parent::onChildRemoved( control ); +} + +//----------------------------------------------------------------------------- + +bool GuiAutoScrollCtrl::_isScrollComplete() const +{ + if( empty() ) + return true; + + GuiControl* control = static_cast< GuiControl* >( at( 0 ) ); + U32 axis = _getScrollAxis(); + F32 amount = _getScrollAmount(); + + if( mScrollOutOfSight ) + { + // If scrolling out of sight, scrolling is complete when the control's rectangle + // does not intersect our own rectangle anymore. + + RectI thisRect( Point2I( 0, 0 ), getExtent() ); + return !( thisRect.overlaps( control->getBounds() ) ); + } + else + { + if( amount < 0 ) + return ( control->getPosition()[ axis ] + control->getExtent()[ axis ] ) < ( getExtent()[ axis ] - mChildBorder ); + else + return ( control->getPosition()[ axis ] >= mChildBorder ); + } +} + +//----------------------------------------------------------------------------- + +void GuiAutoScrollCtrl::_reset( GuiControl* control ) +{ + U32 axis = _getScrollAxis(); + U32 counterAxis = ( axis == 1 ? 0 : 1 ); + + Point2I newPosition( mChildBorder, mChildBorder ); + Point2I newExtent = control->getExtent(); + + // Fit control on axis that is not scrolled. + newExtent[ counterAxis ] = getExtent()[ counterAxis ] - mChildBorder * 2; + + // For the right and down scrolls, position the control away from the + // right/bottom edge of our control. + + if( mDirection == Right ) + newPosition.x = - ( newExtent.x - getExtent().x + mChildBorder ); + else if( mDirection == Down ) + newPosition.y = - ( newExtent.y - getExtent().y + mChildBorder ); + + // Set the child geometry. + + control->setPosition( newPosition ); + control->setExtent( newExtent ); + + // Reset counters. + + mCurrentTime = 0.0f; + mCurrentPhase = PhaseInitial; + mCurrentPosition = control->getPosition()[ axis ]; +} + +//----------------------------------------------------------------------------- + +void GuiAutoScrollCtrl::reset() +{ + if( !empty() ) + _reset( static_cast< GuiControl* >( at( 0 ) ) ); +} + +//----------------------------------------------------------------------------- + +bool GuiAutoScrollCtrl::resize( const Point2I &newPosition, const Point2I &newExtent ) +{ + if( !Parent::resize( newPosition, newExtent ) ) + return false; + + for( iterator i = begin(); i != end(); ++ i ) + { + GuiControl* control = static_cast< GuiControl* >( *i ); + if( control ) + _reset( control ); + } + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiAutoScrollCtrl::childResized( GuiControl* child ) +{ + Parent::childResized( child ); + _reset(child); +} + +//----------------------------------------------------------------------------- + +void GuiAutoScrollCtrl::processTick() +{ + onTick_callback(); +} + +//----------------------------------------------------------------------------- + +void GuiAutoScrollCtrl::advanceTime( F32 timeDelta ) +{ + if( mCurrentPhase == PhaseComplete ) + return; + + // Wait out initial delay. + + if( ( mCurrentTime + timeDelta ) < mStartDelay) + { + mCurrentTime += timeDelta; + return; + } + + // Start scrolling if we haven't already. + + if( mCurrentPhase == PhaseInitial ) + { + onStart_callback(); + mCurrentPhase = PhaseScrolling; + } + + GuiControl* control = static_cast< GuiControl* >( at( 0 ) ); + if( !control ) // Should not happen. + return; + + // If not yet complete, scroll some more. + + if( !_isScrollComplete() ) + { + U32 axis = _getScrollAxis(); + F32 amount = _getScrollAmount(); + + mCurrentPosition += amount * timeDelta; + Point2I newPosition = control->getPosition(); + newPosition[ axis ] = mCurrentPosition; + + control->setPosition( newPosition ); + } + else + { + mCurrentTime += timeDelta; + + if( mCurrentPhase != PhaseComplete && mCurrentPhase != PhaseWait ) + { + if( mCurrentPhase != PhaseWait ) + { + onComplete_callback(); + mCurrentPhase = PhaseComplete; + } + + mCompleteTime = mCurrentTime; + } + + // Reset, if looping. + + if( mIsLooping ) + { + // Wait out reset time and restart. + + mCurrentPhase = PhaseWait; + if( mCurrentTime > ( mCompleteTime + mResetDelay ) ) + { + onReset_callback(); + _reset( control ); + } + } + } +} + +//----------------------------------------------------------------------------- + +void GuiAutoScrollCtrl::inspectPostApply() +{ + Parent::inspectPostApply(); + reset(); +} + +//============================================================================= +// API. +//============================================================================= +// MARK: ---- API ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiAutoScrollCtrl, reset, void, (),, + "Reset scrolling." ) +{ + object->reset(); +} diff --git a/Engine/source/gui/containers/guiAutoScrollCtrl.h b/Engine/source/gui/containers/guiAutoScrollCtrl.h new file mode 100644 index 000000000..6e63fadf4 --- /dev/null +++ b/Engine/source/gui/containers/guiAutoScrollCtrl.h @@ -0,0 +1,162 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIAUTOSCROLLCTRL_H_ +#define _GUIAUTOSCROLLCTRL_H_ + +#ifndef _GUICONTROL_H_ + #include "gui/core/guiControl.h" +#endif +#ifndef _GUITICKCTRL_H_ + #include "gui/shiny/guiTickCtrl.h" +#endif + + +/// A control that automatically scrolls its child control upwards. +class GuiAutoScrollCtrl : public GuiTickCtrl +{ + public: + + typedef GuiTickCtrl Parent; + + /// Scrolling direction. + enum Direction + { + Up, + Down, + Left, + Right + }; + + protected: + + enum Phase + { + PhaseInitial, ///< Waiting to begin scrolling. + PhaseScrolling, ///< Currently scrolling. + PhaseComplete, ///< Scrolling complete. + PhaseWait ///< Wait before starting a new loop. + }; + + /// The direction in which to scroll. + Direction mDirection; + + /// If true, scrolling will start from the beginning once finished. + bool mIsLooping; + + /// Whether to scroll the child control completely out of sight. + bool mScrollOutOfSight; + + /// Current phase in the scrolling animation. + Phase mCurrentPhase; + + /// The current animation time. + F32 mCurrentTime; + + /// The time scrolling was completed. + F32 mCompleteTime; + + /// Current scrolling position. This is kept separate from the control's + /// current position value since we will receive time updates in increments + /// less than a second and thus need to have this value in floating-point. + F32 mCurrentPosition; + + /// Seconds to wait before starting to scroll. + F32 mStartDelay; + + /// Seconds to wait after scrolling is complete before reseting the control + /// to the initial state (only if #mIsLooping is true). + F32 mResetDelay; + + /// Border to put around scrolled child control. + S32 mChildBorder; + + /// Speed at which to scroll in pixels per second. + F32 mScrollSpeed; + + /// @name Callbacks + /// @{ + + DECLARE_CALLBACK( void, onTick, () ); + DECLARE_CALLBACK( void, onStart, () ); + DECLARE_CALLBACK( void, onComplete, () ); + DECLARE_CALLBACK( void, onReset, () ); + + /// @} + + void _reset( GuiControl* control ); + bool _isScrollComplete() const; + + U32 _getScrollAxis() const + { + switch( mDirection ) + { + case Up: return 1; + case Down: return 1; + case Left: return 0; + case Right: return 0; + } + return 0; + } + + F32 _getScrollAmount() const + { + switch( mDirection ) + { + case Up: return - mScrollSpeed; + case Down: return mScrollSpeed; + case Left: return - mScrollSpeed; + case Right: return mScrollSpeed; + } + return 0.f; + } + + public: + + GuiAutoScrollCtrl(); + + void reset(); + + virtual bool onWake(); + virtual void onSleep(); + + virtual void onChildAdded( GuiControl* control ); + virtual void onChildRemoved( GuiControl* control ); + virtual bool resize( const Point2I& newPosition, const Point2I& newExtent ); + virtual void childResized( GuiControl *child ); + + virtual void processTick(); + virtual void advanceTime( F32 timeDelta ); + virtual void inspectPostApply(); + + static void initPersistFields(); + + DECLARE_CONOBJECT( GuiAutoScrollCtrl ); + DECLARE_CATEGORY( "Gui Containers" ); + DECLARE_DESCRIPTION( "A container that automatically scrolls its child control upwards.\n" + "Can be used, for example, for credits screens." ); +}; + +typedef GuiAutoScrollCtrl::Direction GuiAutoScrollDirection; +DefineEnumType( GuiAutoScrollDirection ); + +#endif diff --git a/Engine/source/gui/containers/guiContainer.cpp b/Engine/source/gui/containers/guiContainer.cpp new file mode 100644 index 000000000..45bc09d93 --- /dev/null +++ b/Engine/source/gui/containers/guiContainer.cpp @@ -0,0 +1,439 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/containers/guiContainer.h" + +#include "gui/containers/guiPanel.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" + + +IMPLEMENT_CONOBJECT( GuiContainer ); + +ConsoleDocClass( GuiContainer, + "@brief Brief Desc.\n\n" + + "@tsexample\n" + "// Comment:\n" + "%okButton = new ClassObject()\n" + "instantiation\n" + "@endtsexample\n\n" + + "@ingroup GuiContainers" +); + +ImplementEnumType( GuiDockingType, + "\n\n" + "@ingroup GuiContainers" ) + { Docking::dockNone, "None" }, + { Docking::dockClient, "Client" }, + { Docking::dockTop, "Top" }, + { Docking::dockBottom, "Bottom" }, + { Docking::dockLeft, "Left" }, + { Docking::dockRight, "Right" } +EndImplementEnumType; + + +//----------------------------------------------------------------------------- + +GuiContainer::GuiContainer() +{ + mUpdateLayout = false; + mValidDockingMask = Docking::dockNone | Docking::dockBottom | + Docking::dockTop | Docking::dockClient | + Docking::dockLeft | Docking::dockRight; + mIsContainer = true; +} + +//----------------------------------------------------------------------------- + +GuiContainer::~GuiContainer() +{ +} + +//----------------------------------------------------------------------------- + +void GuiContainer::initPersistFields() +{ + Con::setIntVariable("$DOCKING_NONE", Docking::dockNone); + Con::setIntVariable("$DOCKING_CLIENT", Docking::dockClient); + Con::setIntVariable("$DOCKING_TOP", Docking::dockTop); + Con::setIntVariable("$DOCKING_BOTTOM", Docking::dockBottom); + Con::setIntVariable("$DOCKING_LEFT", Docking::dockLeft); + Con::setIntVariable("$DOCKING_RIGHT", Docking::dockRight); + + addGroup( "Layout" ); + + addProtectedField("docking", TYPEID< Docking::DockingType >(), Offset(mSizingOptions.mDocking, GuiContainer), &setDockingField, &defaultProtectedGetFn, "" ); + addField("margin", TypeRectSpacingI, Offset(mSizingOptions.mPadding, GuiContainer)); + addField("padding", TypeRectSpacingI, Offset(mSizingOptions.mInternalPadding, GuiContainer)); + addField("anchorTop", TypeBool, Offset(mSizingOptions.mAnchorTop, GuiContainer)); + addField("anchorBottom", TypeBool, Offset(mSizingOptions.mAnchorBottom, GuiContainer)); + addField("anchorLeft", TypeBool, Offset(mSizingOptions.mAnchorLeft, GuiContainer)); + addField("anchorRight", TypeBool, Offset(mSizingOptions.mAnchorRight, GuiContainer)); + + endGroup( "Layout" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +void GuiContainer::onChildAdded(GuiControl* control) +{ + Parent::onChildAdded( control ); + setUpdateLayout(); +} + +//----------------------------------------------------------------------------- + +void GuiContainer::onChildRemoved(GuiControl* control) +{ + Parent::onChildRemoved( control ); + setUpdateLayout(); +} + +//----------------------------------------------------------------------------- + +bool GuiContainer::reOrder(SimObject* obj, SimObject* target) +{ + if ( !Parent::reOrder(obj, target) ) + return false; + + setUpdateLayout(); + return true; +} + +//----------------------------------------------------------------------------- + +bool GuiContainer::resize( const Point2I &newPosition, const Point2I &newExtent ) +{ + RectI oldBounds = getBounds(); + Point2I minExtent = getMinExtent(); + + if( !Parent::resize( newPosition, newExtent ) ) + return false; + + RectI clientRect = getClientRect(); + layoutControls( clientRect ); + + GuiControl *parent = getParent(); + S32 docking = getDocking(); + if( parent && docking != Docking::dockNone && docking != Docking::dockInvalid ) + setUpdateLayout( updateParent ); + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiContainer::addObject(SimObject *obj) +{ + Parent::addObject(obj); + + setUpdateLayout(); +} + +//----------------------------------------------------------------------------- + +void GuiContainer::removeObject(SimObject *obj) +{ + Parent::removeObject(obj); + + setUpdateLayout(); +} + +//----------------------------------------------------------------------------- + +void GuiContainer::parentResized(const RectI &oldParentRect, const RectI &newParentRect) +{ + //if(!mCanResize) + // return; + + // If it's a control that specifies invalid docking, we'll just treat it as an old GuiControl + if( getDocking() & Docking::dockInvalid || getDocking() & Docking::dockNone) + return Parent::parentResized( oldParentRect, newParentRect ); + + S32 deltaX = newParentRect.extent.x - oldParentRect.extent.x; + S32 deltaY = newParentRect.extent.y - oldParentRect.extent.y; + + // Update Self + RectI oldThisRect = getBounds(); + anchorControl( this, Point2I( deltaX, deltaY ) ); + RectI newThisRect = getBounds(); + + // Update Deltas to pass on to children + deltaX = newThisRect.extent.x - oldThisRect.extent.x; + deltaY = newThisRect.extent.y - oldThisRect.extent.y; + + // Iterate over all children and update their anchors + iterator nI = begin(); + for( ; nI != end(); nI++ ) + { + // Sanity + GuiControl *control = dynamic_cast( (*nI) ); + if( control ) + control->parentResized( oldThisRect, newThisRect ); + } +} + +//----------------------------------------------------------------------------- + +void GuiContainer::childResized(GuiControl *child) +{ + Parent::childResized( child ); + setUpdateLayout(); +} + +//----------------------------------------------------------------------------- + +bool GuiContainer::layoutControls( RectI &clientRect ) +{ + // This variable is set to the first 'Client' docking + // control that is found. We defer client docking until + // after all other docks have been made since it will consume + // the remaining client area available. + GuiContainer *clientDocking = NULL; + + // Iterate over all children and perform docking + iterator nI = begin(); + for( ; nI != end(); nI++ ) + { + // Layout Content with proper docking (Client Default) + GuiControl *control = static_cast(*nI); + + // If we're invisible we don't get counted in docking + if( control == NULL || !control->isVisible() ) + continue; + + S32 dockingMode = Docking::dockNone; + GuiContainer *container = dynamic_cast(control); + if( container != NULL ) + dockingMode = container->getDocking(); + else + continue; + + // See above note about clientDocking pointer + if( dockingMode & Docking::dockClient && clientDocking == NULL ) + clientDocking = container; + + // Dock Appropriately + if( !(dockingMode & Docking::dockClient) ) + dockControl( container, dockingMode, clientRect ); + } + + // Do client dock + if( clientDocking != NULL ) + dockControl( clientDocking, Docking::dockClient, clientRect ); + + return true; +} + +//----------------------------------------------------------------------------- + +bool GuiContainer::dockControl( GuiContainer *control, S32 dockingMode, RectI &clientRect ) +{ + if( !control ) + return false; + + // Make sure this class support docking of this type + if( !(dockingMode & getValidDockingMask())) + return false; + + // If our client rect has run out of room, we can't dock any more + if( !clientRect.isValidRect() ) + return false; + + // Dock Appropriately + RectI dockRect; + RectSpacingI rectShrinker; + ControlSizing sizingOptions = control->getSizingOptions(); + switch( dockingMode ) + { + case Docking::dockClient: + + // Inset by padding + sizingOptions.mPadding.insetRect(clientRect); + + // Dock to entirety of client rectangle + control->resize( clientRect.point, clientRect.extent ); + + // Remove Client Rect, can only have one client dock + clientRect.set(0,0,0,0); + break; + case Docking::dockTop: + + dockRect = clientRect; + dockRect.extent.y = getMin( control->getHeight() + sizingOptions.mPadding.top + sizingOptions.mPadding.bottom , clientRect.extent.y ); + + // Subtract our rect + clientRect.point.y += dockRect.extent.y; + clientRect.extent.y -= dockRect.extent.y; + + // Inset by padding + sizingOptions.mPadding.insetRect(dockRect); + + // Resize + control->resize( dockRect.point, dockRect.extent ); + + break; + case Docking::dockBottom: + + dockRect = clientRect; + dockRect.extent.y = getMin( control->getHeight() + sizingOptions.mPadding.top + sizingOptions.mPadding.bottom, clientRect.extent.y ); + dockRect.point.y += clientRect.extent.y - dockRect.extent.y; + + // Subtract our rect + clientRect.extent.y -= dockRect.extent.y; + + // Inset by padding + sizingOptions.mPadding.insetRect(dockRect); + + // Resize + control->resize( dockRect.point, dockRect.extent ); + + break; + case Docking::dockLeft: + + dockRect = clientRect; + dockRect.extent.x = getMin( control->getWidth() + sizingOptions.mPadding.left + sizingOptions.mPadding.right, clientRect.extent.x ); + + // Subtract our rect + clientRect.point.x += dockRect.extent.x; + clientRect.extent.x -= dockRect.extent.x; + + // Inset by padding + sizingOptions.mPadding.insetRect(dockRect); + + // Resize + control->resize( dockRect.point, dockRect.extent ); + + break; + case Docking::dockRight: + + dockRect = clientRect; + dockRect.extent.x = getMin( control->getWidth() + sizingOptions.mPadding.left + sizingOptions.mPadding.right, clientRect.extent.x ); + dockRect.point.x += clientRect.extent.x - dockRect.extent.x; + + // Subtract our rect + clientRect.extent.x -= dockRect.extent.x; + + // Inset by padding + sizingOptions.mPadding.insetRect(dockRect); + + // Resize + control->resize( dockRect.point, dockRect.extent ); + + break; + case Docking::dockNone: + control->setUpdateLayout(); + break; + } + + return true; +} + +//----------------------------------------------------------------------------- + +bool GuiContainer::anchorControl( GuiControl *control, const Point2I &deltaParentExtent ) +{ + GuiContainer *container = dynamic_cast( control ); + if( !control || !container ) + return false; + + // If we're docked, we don't anchor to anything + if( (container->getDocking() & Docking::dockAny) || !(container->getDocking() & Docking::dockInvalid) ) + return false; + + if( deltaParentExtent.isZero() ) + return false; + + RectI oldRect = control->getBounds(); + RectI newRect = control->getBounds(); + + F32 deltaBottom = mSizingOptions.mAnchorBottom ? (F32)deltaParentExtent.y : 0.0f; + F32 deltaRight = mSizingOptions.mAnchorRight ? (F32)deltaParentExtent.x : 0.0f; + F32 deltaLeft = mSizingOptions.mAnchorLeft ? 0.0f : (F32)deltaParentExtent.x; + F32 deltaTop = mSizingOptions.mAnchorTop ? 0.0f : (F32)deltaParentExtent.y; + + // Apply Delta's to newRect + newRect.point.x += (S32)deltaLeft; + newRect.extent.x += (S32)(deltaRight - deltaLeft); + newRect.point.y += (S32)deltaTop; + newRect.extent.y += (S32)(deltaBottom - deltaTop); + + Point2I minExtent = control->getMinExtent(); + // Only resize if our minExtent is satisfied with it. + if( !( newRect.extent.x >= control->getMinExtent().x && newRect.extent.y >= control->getMinExtent().y ) ) + return false; + + if( newRect.point == oldRect.point && newRect.extent == oldRect.extent ) + return false; + + // Finally Size the control + control->resize( newRect.point, newRect.extent ); + + // We made changes + return true; +} + +//----------------------------------------------------------------------------- + +void GuiContainer::onPreRender() +{ + if( mUpdateLayout == updateNone ) + return; + + RectI clientRect = getClientRect(); + if( mUpdateLayout & updateSelf ) + layoutControls( clientRect ); + + GuiContainer *parent = dynamic_cast( getParent() ); + if( parent && ( mUpdateLayout & updateParent ) ) + parent->setUpdateLayout(); + + // Always set AFTER layoutControls call to prevent recursive calling of layoutControls - JDD + mUpdateLayout = updateNone; + + Parent::onPreRender(); +} + +//----------------------------------------------------------------------------- + +const RectI GuiContainer::getClientRect() +{ + RectI resRect = RectI( Point2I(0,0), getExtent() ); + + // Inset by padding + mSizingOptions.mInternalPadding.insetRect( resRect ); + + return resRect; +} + +//----------------------------------------------------------------------------- + +void GuiContainer::setDocking( S32 docking ) +{ + mSizingOptions.mDocking = docking; + setUpdateLayout( updateParent ); +} diff --git a/Engine/source/gui/containers/guiContainer.h b/Engine/source/gui/containers/guiContainer.h new file mode 100644 index 000000000..b441e2d50 --- /dev/null +++ b/Engine/source/gui/containers/guiContainer.h @@ -0,0 +1,155 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUICONTAINER_H_ +#define _GUICONTAINER_H_ + +#ifndef _GUICONTROL_H_ + #include "gui/core/guiControl.h" +#endif + + +/// Base class for controls that act as containers to other controls. +/// +/// @addtogroup gui_container_group Containers +/// +/// @ingroup gui_group Gui System +/// @{ +class GuiContainer : public GuiControl +{ + public: + + typedef GuiControl Parent; + + enum + { + updateSelf = BIT(1), + updateParent = BIT(2), + updateNone = 0 + }; + + protected: + + S32 mUpdateLayout; ///< Layout Update Mask + ControlSizing mSizingOptions; ///< Control Sizing Options + S32 mValidDockingMask; + + public: + + DECLARE_CONOBJECT(GuiContainer); + DECLARE_CATEGORY( "Gui Containers" ); + + GuiContainer(); + virtual ~GuiContainer(); + + static void initPersistFields(); + + /// @name Container Sizing + /// @{ + + /// Returns the Mask of valid docking modes supported by this container + inline S32 getValidDockingMask() { return mValidDockingMask; }; + + /// Docking Accessors + inline S32 getDocking() { return mSizingOptions.mDocking; }; + virtual void setDocking( S32 docking ); + + /// Docking Protected Field Setter + static bool setDockingField( void *object, const char *index, const char *data ) + { + GuiContainer *pContainer = static_cast(object); + pContainer->setUpdateLayout( updateParent ); + return true; + } + + inline bool getAnchorTop() const { return mSizingOptions.mAnchorTop; } + inline bool getAnchorBottom() const { return mSizingOptions.mAnchorBottom; } + inline bool getAnchorLeft() const { return mSizingOptions.mAnchorLeft; } + inline bool getAnchorRight() const { return mSizingOptions.mAnchorRight; } + inline void setAnchorTop(bool val) { mSizingOptions.mAnchorTop = val; } + inline void setAnchorBottom(bool val) { mSizingOptions.mAnchorBottom = val; } + inline void setAnchorLeft(bool val) { mSizingOptions.mAnchorLeft = val; } + inline void setAnchorRight(bool val) { mSizingOptions.mAnchorRight = val; } + + ControlSizing getSizingOptions() const { return mSizingOptions; } + void setSizingOptions(ControlSizing val) { mSizingOptions = val; } + + /// @} + + /// @name Sizing Constraints + /// @{ + virtual const RectI getClientRect(); + /// @} + + /// @name Control Layout Methods + /// @{ + + /// Called when the Layout for a Container needs to be updated because of a resize call or a call to setUpdateLayout + /// @param clientRect The Client Rectangle that is available for this Container to layout it's children in + virtual bool layoutControls( RectI &clientRect ); + + /// Set the layout flag to Dirty on a Container, triggering an update to it's layout on the next onPreRender call. + /// @attention This can be called without regard to whether the flag is already set, as setting it + /// does not actually cause an update, but rather tells the container it should update the next + /// chance it gets + /// @param updateType A Mask that indicates how the layout should be updated. + inline void setUpdateLayout( S32 updateType = updateSelf ) { mUpdateLayout |= updateType; }; + + /// @} + + /// @name Container Sizing Methods + /// @{ + + /// Dock a Control with the given docking mode inside the given client rect. + /// @attention The clientRect passed in will be modified by the docking of + /// the control. It will return the rect that remains after the docking operation. + virtual bool dockControl( GuiContainer *control, S32 dockingMode, RectI &clientRect ); + + /// Update a Controls Anchor based on a delta sizing of it's parents extent + /// This function should return true if the control was changed in size or position at all + virtual bool anchorControl( GuiControl *control, const Point2I &deltaParentExtent ); + + /// @} + + /// @name GuiControl Inherited + /// @{ + + virtual void onChildAdded(GuiControl* control); + virtual void onChildRemoved(GuiControl* control); + virtual bool resize( const Point2I &newPosition, const Point2I &newExtent ); + virtual void childResized(GuiControl *child); + virtual void addObject(SimObject *obj); + virtual void removeObject(SimObject *obj); + virtual bool reOrder(SimObject* obj, SimObject* target); + virtual void onPreRender(); + + /// GuiContainer deals with parentResized calls differently than GuiControl. It will + /// update the layout for all of it's non-docked child controls. parentResized calls + /// on the child controls will be handled by their default functions, but for our + /// purposes we want at least our immediate children to use the anchors that they have + /// set on themselves. - JDD [9/20/2006] + virtual void parentResized(const RectI &oldParentRect, const RectI &newParentRect); + + /// @} +}; +/// @} +#endif diff --git a/Engine/source/gui/containers/guiCtrlArrayCtrl.cpp b/Engine/source/gui/containers/guiCtrlArrayCtrl.cpp new file mode 100644 index 000000000..2e93a69cb --- /dev/null +++ b/Engine/source/gui/containers/guiCtrlArrayCtrl.cpp @@ -0,0 +1,190 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/containers/guiCtrlArrayCtrl.h" + +#include "console/engineAPI.h" + +IMPLEMENT_CONOBJECT(GuiControlArrayControl); + +ConsoleDocClass( GuiControlArrayControl, + "@brief Brief Desc.\n\n" + + "@tsexample\n" + "// Comment:\n" + "%okButton = new ClassObject()\n" + "instantiation\n" + "@endtsexample\n\n" + + "@ingroup GuiContainers" +); + +// One call back that the system isnt ready for yet in this class + +GuiControlArrayControl::GuiControlArrayControl() +{ + mResizing = false; + + mCols = 0; + mRowSize = 30; + mRowSpacing = 2; + mColSpacing = 0; + mIsContainer = true; +} + +void GuiControlArrayControl::initPersistFields() +{ + addGroup( "Array" ); + + addField( "colCount", TypeS32, Offset(mCols, GuiControlArrayControl), + "Number of colums in the array." ); + addField( "colSizes", TypeS32Vector, Offset(mColumnSizes, GuiControlArrayControl), + "Size of each individual column." ); + addField( "rowSize", TypeS32, Offset(mRowSize, GuiControlArrayControl), + "Heigth of a row in the array." ); + addField( "rowSpacing", TypeS32, Offset(mRowSpacing, GuiControlArrayControl), + "Padding to put between rows." ); + addField( "colSpacing", TypeS32, Offset(mColSpacing, GuiControlArrayControl), + "Padding to put between columns." ); + + endGroup( "Array" ); + + Parent::initPersistFields(); +} + +bool GuiControlArrayControl::onWake() +{ + if ( !Parent::onWake() ) + return false; + + return true; +} + +void GuiControlArrayControl::onSleep() +{ + Parent::onSleep(); +} + +void GuiControlArrayControl::inspectPostApply() +{ + Parent::inspectPostApply(); + + updateArray(); +} + +bool GuiControlArrayControl::resize(const Point2I &newPosition, const Point2I &newExtent) +{ + if( !Parent::resize(newPosition, newExtent) ) + return false; + + return updateArray(); +} + +void GuiControlArrayControl::addObject(SimObject *obj) +{ + Parent::addObject(obj); + + updateArray(); +} + +void GuiControlArrayControl::removeObject(SimObject *obj) +{ + Parent::removeObject(obj); + + updateArray(); +} + +bool GuiControlArrayControl::reOrder(SimObject* obj, SimObject* target) +{ + bool ret = Parent::reOrder(obj, target); + if (ret) + updateArray(); + + return ret; +} + +bool GuiControlArrayControl::updateArray() +{ + // Prevent recursion + if(mResizing) + return false; + + // Set Resizing. + mResizing = true; + + if(mCols < 1 || size() < 1) + { + mResizing = false; + return false; + } + + S32 *sizes = new S32[mCols]; + S32 *offsets = new S32[mCols]; + S32 totalSize = 0; + Point2I extent = getExtent(); + + // Calculate the column sizes + for(S32 i=0; i= mColumnSizes.size() ) + sizes[ i ] = 0; + else + sizes[i] = mColumnSizes[i]; + + offsets[i] = totalSize; + + // If it's an auto-size one, then... auto-size... + if(sizes[i] == -1) + { + sizes[i] = extent.x - totalSize; + break; + } + + totalSize += sizes[i] + mColSpacing; + } + + // Now iterate through the children and resize them to fit the grid... + for(S32 i=0; i(operator[](i)); + + // Get the current column and row... + S32 curCol = i % mCols; + S32 curRow = i / mCols; + + if(gc) + { + Point2I newPos(offsets[curCol], curRow * (mRowSize + mRowSpacing)); + Point2I newExtents(sizes[curCol], mRowSize); + + gc->resize(newPos, newExtents); + } + } + + // Clear Sizing Flag. + mResizing = false; + + delete [] sizes; + delete [] offsets; + + return true; +} diff --git a/Engine/source/gui/containers/guiCtrlArrayCtrl.h b/Engine/source/gui/containers/guiCtrlArrayCtrl.h new file mode 100644 index 000000000..c3bfbf425 --- /dev/null +++ b/Engine/source/gui/containers/guiCtrlArrayCtrl.h @@ -0,0 +1,68 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUICTRLARRAYCTRL_H_ +#define _GUICTRLARRAYCTRL_H_ + +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif + +#include "gfx/gfxDevice.h" +#include "console/console.h" +#include "console/consoleTypes.h" + +class GuiControlArrayControl : public GuiControl +{ +private: + typedef GuiControl Parent; + + bool mResizing; + + S32 mCols; + Vector mColumnSizes; + S32 mRowSize; + S32 mRowSpacing; + S32 mColSpacing; + +public: + GuiControlArrayControl(); + + bool resize(const Point2I &newPosition, const Point2I &newExtent); + + bool onWake(); + void onSleep(); + void inspectPostApply(); + + bool updateArray(); + + void addObject(SimObject *obj); + void removeObject(SimObject *obj); + + bool reOrder(SimObject* obj, SimObject* target = 0); + + static void initPersistFields(); + DECLARE_CONOBJECT(GuiControlArrayControl); + DECLARE_CATEGORY( "Gui Containers" ); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/gui/containers/guiDragAndDropCtrl.cpp b/Engine/source/gui/containers/guiDragAndDropCtrl.cpp new file mode 100644 index 000000000..027fda6c8 --- /dev/null +++ b/Engine/source/gui/containers/guiDragAndDropCtrl.cpp @@ -0,0 +1,271 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/containers/guiDragAndDropCtrl.h" +#include "gui/core/guiCanvas.h" +#include "console/engineAPI.h" + + +IMPLEMENT_CONOBJECT( GuiDragAndDropControl ); + +ConsoleDocClass( GuiDragAndDropControl, + "@brief A container control that can be used to implement drag&drop behavior.\n\n" + + "GuiDragAndDropControl is a special control that can be used to allow drag&drop behavior to be implemented where " + "GuiControls may be dragged across the canvas and the dropped on other GuiControls.\n\n" + + "To start a drag operation, construct a GuiDragAndDropControl and add the control that should be drag&dropped " + "as a child to it. Note that this must be a single child control. To drag multiple controls, wrap them in a new " + "GuiControl object as a temporary container.\n\n" + + "Then, to initiate the drag, add the GuiDragAndDropControl to the canvas and call startDragging(). You can optionally " + "supply an offset to better position the GuiDragAndDropControl on the mouse cursor.\n\n" + + "As the GuiDragAndDropControl is then moved across the canvas, it will call the onControlDragEnter(), onControlDragExit(), " + "onControlDragged(), and finally onControlDropped() callbacks on the visible topmost controls that it moves across. " + "onControlDropped() is called when the mouse button is released and the drag operation thus finished.\n\n" + + "@tsexample\n" + "// The following example implements drag&drop behavior for GuiSwatchButtonCtrl so that\n" + "// one color swatch may be dragged over the other to quickly copy its color.\n" + "//\n" + "// This code is taken from the stock scripts.\n" + "\n" + "//---------------------------------------------------------------------------------------------\n" + "\n" + "// With this method, we start the operation when the mouse is click-dragged away from a color swatch.\n" + "function GuiSwatchButtonCtrl::onMouseDragged( %this )\n" + "{\n" + " // First we construct a new temporary swatch button that becomes the payload for our\n" + " // drag operation and give it the properties of the swatch button we want to copy.\n" + "\n" + " %payload = new GuiSwatchButtonCtrl();\n" + " %payload.assignFieldsFrom( %this );\n" + " %payload.position = \"0 0\";\n" + " %payload.dragSourceControl = %this; // Remember where the drag originated from so that we don't copy a color swatch onto itself.\n" + "\n" + " // Calculate the offset of the GuiDragAndDropControl from the mouse cursor. Here we center\n" + " // it on the cursor.\n" + "\n" + " %xOffset = getWord( %payload.extent, 0 ) / 2;\n" + " %yOffset = getWord( %payload.extent, 1 ) / 2;\n" + "\n" + " // Compute the initial position of the GuiDragAndDrop control on the cavas based on the current\n" + " // mouse cursor position.\n" + "\n" + " %cursorpos = Canvas.getCursorPos();\n" + " %xPos = getWord( %cursorpos, 0 ) - %xOffset;\n" + " %yPos = getWord( %cursorpos, 1 ) - %yOffset;\n" + "\n" + " // Create the drag control.\n" + "\n" + " %ctrl = new GuiDragAndDropControl()\n" + " {\n" + " canSaveDynamicFields = \"0\";\n" + " Profile = \"GuiSolidDefaultProfile\";\n" + " HorizSizing = \"right\";\n" + " VertSizing = \"bottom\";\n" + " Position = %xPos SPC %yPos;\n" + " extent = %payload.extent;\n" + " MinExtent = \"4 4\";\n" + " canSave = \"1\";\n" + " Visible = \"1\";\n" + " hovertime = \"1000\";\n" + "\n" + " // Let the GuiDragAndDropControl delete itself on mouse-up. When the drag is aborted,\n" + " // this not only deletes the drag control but also our payload.\n" + " deleteOnMouseUp = true;\n" + "\n" + " // To differentiate drags, use the namespace hierarchy to classify them.\n" + " // This will allow a color swatch drag to tell itself apart from a file drag, for example.\n" + " class = \"GuiDragAndDropControlType_ColorSwatch\";\n" + " };\n" + "\n" + " // Add the temporary color swatch to the drag control as the payload.\n" + " %ctrl.add( %payload );\n" + "\n" + " // Start drag by adding the drag control to the canvas and then calling startDragging().\n" + "\n" + " Canvas.getContent().add( %ctrl );\n" + " %ctrl.startDragging( %xOffset, %yOffset );\n" + "}\n" + "\n" + "//---------------------------------------------------------------------------------------------\n" + "\n" + "// This method receives the drop when the mouse button is released over a color swatch control\n" + "// during a drag operation.\n" + "function GuiSwatchButtonCtrl::onControlDropped( %this, %payload, %position )\n" + "{\n" + " // Make sure this is a color swatch drag operation.\n" + " if( !%payload.parentGroup.isInNamespaceHierarchy( \"GuiDragAndDropControlType_ColorSwatch\" ) )\n" + " return;\n" + "\n" + " // If dropped on same button whence we came from,\n" + " // do nothing.\n" + "\n" + " if( %payload.dragSourceControl == %this )\n" + " return;\n" + "\n" + " // If a swatch button control is dropped onto this control,\n" + " // copy it's color.\n" + "\n" + " if( %payload.isMemberOfClass( \"GuiSwatchButtonCtrl\" ) )\n" + " {\n" + " // If the swatch button is part of a color-type inspector field,\n" + " // remember the inspector field so we can later set the color\n" + " // through it.\n" + "\n" + " if( %this.parentGroup.isMemberOfClass( \"GuiInspectorTypeColorI\" ) )\n" + " %this.parentGroup.apply( ColorFloatToInt( %payload.color ) );\n" + " else if( %this.parentGroup.isMemberOfClass( \"GuiInspectorTypeColorF\" ) )\n" + " %this.parentGroup.apply( %payload.color );\n" + " else\n" + " %this.setColor( %payload.color );\n" + " }\n" + "}\n" + "@endtsexample\n\n" + + "@see GuiControl::onControlDragEnter\n" + "@see GuiControl::onControlDragExit\n" + "@see GuiControl::onControlDragged\n" + "@see GuiControl::onControlDropped\n\n" + + "@ingroup GuiUtil" +); + + +//----------------------------------------------------------------------------- + +void GuiDragAndDropControl::initPersistFields() +{ + addField( "deleteOnMouseUp", TypeBool, Offset( mDeleteOnMouseUp, GuiDragAndDropControl ), + "If true, the control deletes itself when the left mouse button is released.\n\n" + "If at this point, the drag&drop control still contains its payload, it will be deleted along with the control." ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +void GuiDragAndDropControl::startDragging( Point2I offset ) +{ + GuiCanvas* canvas = getRoot(); + if( !canvas ) + { + Con::errorf( "GuiDragAndDropControl::startDragging - GuiDragAndDropControl wasn't added to the gui before the drag started." ); + if( mDeleteOnMouseUp ) + deleteObject(); + return; + } + + if( canvas->getMouseLockedControl() ) + { + GuiEvent event; + canvas->getMouseLockedControl()->onMouseLeave(event); + canvas->mouseUnlock( canvas->getMouseLockedControl() ); + } + canvas->mouseLock(this); + canvas->setFirstResponder(this); + mOffset = offset; + mLastTarget=NULL; +} + +//----------------------------------------------------------------------------- + +void GuiDragAndDropControl::onMouseDown( const GuiEvent& event ) +{ + startDragging( event.mousePoint - getPosition() ); +} + +//----------------------------------------------------------------------------- + +void GuiDragAndDropControl::onMouseDragged( const GuiEvent& event ) +{ + setPosition( event.mousePoint - mOffset ); + + // Allow the control under the drag to react to a potential drop + GuiControl* enterTarget = findDragTarget( event.mousePoint, "onControlDragEnter" ); + if( mLastTarget != enterTarget ) + { + if( mLastTarget ) + mLastTarget->onControlDragExit_callback( dynamic_cast< GuiControl* >( at( 0 ) ), getDropPoint() ); + if( enterTarget ) + enterTarget->onControlDragEnter_callback( dynamic_cast< GuiControl* >( at( 0 ) ), getDropPoint() ); + + mLastTarget = enterTarget; + } + + GuiControl* dragTarget = findDragTarget( event.mousePoint, "onControlDragged" ); + if( dragTarget ) + dragTarget->onControlDragged_callback( dynamic_cast< GuiControl* >( at( 0 ) ), getDropPoint() ); +} + +//----------------------------------------------------------------------------- + +void GuiDragAndDropControl::onMouseUp(const GuiEvent& event) +{ + mouseUnlock(); + + GuiControl* target = findDragTarget( event.mousePoint, "onControlDropped" ); + if( target ) + target->onControlDropped_callback( dynamic_cast< GuiControl* >( at( 0 ) ), getDropPoint() ); + + if( mDeleteOnMouseUp ) + deleteObject(); +} + +//----------------------------------------------------------------------------- + +GuiControl* GuiDragAndDropControl::findDragTarget( Point2I mousePoint, const char* method ) +{ + // If there are any children and we have a parent. + GuiControl* parent = getParent(); + if (size() && parent) + { + mVisible = false; + GuiControl* dropControl = parent->findHitControl(mousePoint); + mVisible = true; + while( dropControl ) + { + if (dropControl->isMethod(method)) + return dropControl; + else + dropControl = dropControl->getParent(); + } + } + return NULL; +} + +//============================================================================= +// Console Methods. +//============================================================================= +// MARK: ---- Console Methods ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiDragAndDropControl, startDragging, void, ( S32 x, S32 y ), ( 0, 0 ), + "Start the drag operation.\n\n" + "@param x X coordinate for the mouse pointer offset which the drag control should position itself.\n" + "@param y Y coordinate for the mouse pointer offset which the drag control should position itself.") +{ + object->startDragging( Point2I( x, y ) ); +} diff --git a/Engine/source/gui/containers/guiDragAndDropCtrl.h b/Engine/source/gui/containers/guiDragAndDropCtrl.h new file mode 100644 index 000000000..a8ef0e7b3 --- /dev/null +++ b/Engine/source/gui/containers/guiDragAndDropCtrl.h @@ -0,0 +1,86 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIDRAGANDDROPCTRL_H_ +#define _GUIDRAGANDDROPCTRL_H_ + +#ifndef _GUICONTROL_H_ + #include "gui/core/guiControl.h" +#endif +#ifndef _GFXDEVICE_H_ + #include "gfx/gfxDevice.h" +#endif +#ifndef _CONSOLE_H_ + #include "console/console.h" +#endif +#ifndef _CONSOLETYPES_H_ + #include "console/consoleTypes.h" +#endif + + +/// A special control that implements drag-and-drop behavior. +/// +class GuiDragAndDropControl : public GuiControl +{ + public: + + typedef GuiControl Parent; + + private: + + /// The mouse down offset from the upper left of the control. + Point2I mOffset; + + /// If true, the control deletes itself when the left mouse button is released. + bool mDeleteOnMouseUp; + + /// Controls may want to react when they are dragged over, entered or exited. + SimObjectPtr mLastTarget; + + GuiControl* findDragTarget(Point2I mousePoint, const char* method); + + Point2I getDropPoint() const + { + return getPosition() + (getExtent() / 2); + } + + public: + + GuiDragAndDropControl() {} + + void startDragging(Point2I offset = Point2I(0, 0)); + + // GuiControl. + virtual void onMouseDown(const GuiEvent& event); + virtual void onMouseDragged(const GuiEvent& event); + virtual void onMouseUp(const GuiEvent& event); + + static void initPersistFields(); + + DECLARE_CONOBJECT( GuiDragAndDropControl ); + DECLARE_CATEGORY( "Gui Other" ); + DECLARE_DESCRIPTION( "A special control that implements drag&drop behavior.\n" + "The control will notify other controls as it moves across the canvas.\n" + "Content can be attached through dynamic fields or child objects." ); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/gui/containers/guiDynamicCtrlArrayCtrl.cpp b/Engine/source/gui/containers/guiDynamicCtrlArrayCtrl.cpp new file mode 100644 index 000000000..f66a14bb0 --- /dev/null +++ b/Engine/source/gui/containers/guiDynamicCtrlArrayCtrl.cpp @@ -0,0 +1,303 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/engineAPI.h" +#include "platform/platform.h" +#include "gui/containers/guiDynamicCtrlArrayCtrl.h" + + +GuiDynamicCtrlArrayControl::GuiDynamicCtrlArrayControl() +{ + mCols = 0; + mColSize = 64; + mRows = 0; + mRowSize = 64; + mRowSpacing = 0; + mColSpacing = 0; + mIsContainer = true; + + mResizing = false; + + mSizeToChildren = false; + mAutoCellSize = false; + + mFrozen = false; + mDynamicSize = false; + mFillRowFirst = true; + + mPadding.set( 0, 0, 0, 0 ); +} + +GuiDynamicCtrlArrayControl::~GuiDynamicCtrlArrayControl() +{ +} + +IMPLEMENT_CONOBJECT(GuiDynamicCtrlArrayControl); + +ConsoleDocClass( GuiDynamicCtrlArrayControl, + "@brief A container that arranges children into a grid.\n\n" + + "This container maintains a 2D grid of GUI controls. If one is added, deleted, " + "or resized, then the grid is updated. The insertion order into the grid is " + "determined by the internal order of the children (ie. the order of addition).
      " + + "Children are added to the grid by row or column until they fill the assocated " + "GuiDynamicCtrlArrayControl extent (width or height). For example, a " + "GuiDynamicCtrlArrayControl with 15 children, and fillRowFirst set to " + "true may be arranged as follows:\n\n" + + "
      \n"
      +   "1  2  3  4  5  6\n"
      +   "7  8  9  10 11 12\n"
      +   "13 14 15\n"
      +   "
      \n" + + "If dynamicSize were set to true in this case, the GuiDynamicCtrlArrayControl " + "height would be calculated to fit the 3 rows of child controls.\n\n" + + "@tsexample\n" + "new GuiDynamicCtrlArrayControl()\n" + "{\n" + " colSize = \"128\";\n" + " rowSize = \"18\";\n" + " colSpacing = \"2\";\n" + " rowSpacing = \"2\";\n" + " frozen = \"0\";\n" + " autoCellSize = \"1\";\n" + " fillRowFirst = \"1\";\n" + " dynamicSize = \"1\";\n" + " padding = \"0 0 0 0\";\n" + " //Properties not specific to this control have been omitted from this example.\n" + "};\n" + "@endtsexample\n\n" + + "@ingroup GuiContainers" +); + +// ConsoleObject... + +void GuiDynamicCtrlArrayControl::initPersistFields() +{ + addField( "colCount", TypeS32, Offset( mCols, GuiDynamicCtrlArrayControl ), + "Number of columns the child controls have been arranged into. This " + "value is calculated automatically when children are added, removed or " + "resized; writing it directly has no effect." ); + + addField( "colSize", TypeS32, Offset( mColSize, GuiDynamicCtrlArrayControl ), + "Width of each column. If autoCellSize is set, this will be " + "calculated automatically from the widest child control" ); + + addField( "rowCount", TypeS32, Offset( mRows, GuiDynamicCtrlArrayControl ), + "Number of rows the child controls have been arranged into. This value " + "is calculated automatically when children are added, removed or resized; " + "writing it directly has no effect." ); + + addField( "rowSize", TypeS32, Offset( mRowSize, GuiDynamicCtrlArrayControl ), + "Height of each row. If autoCellSize is set, this will be " + "calculated automatically from the tallest child control" ); + + addField( "rowSpacing", TypeS32, Offset( mRowSpacing, GuiDynamicCtrlArrayControl ), + "Spacing between rows" ); + + addField( "colSpacing", TypeS32, Offset( mColSpacing, GuiDynamicCtrlArrayControl ), + "Spacing between columns" ); + + addField( "frozen", TypeBool, Offset( mFrozen, GuiDynamicCtrlArrayControl ), + "When true, the array will not update when new children are added or in " + "response to child resize events. This is useful to prevent unnecessary " + "resizing when adding, removing or resizing a number of child controls." ); + + addField( "autoCellSize", TypeBool, Offset( mAutoCellSize, GuiDynamicCtrlArrayControl ), + "When true, the cell size is set to the widest/tallest child control." ); + + addField( "fillRowFirst", TypeBool, Offset( mFillRowFirst, GuiDynamicCtrlArrayControl ), + "Controls whether rows or columns are filled first.\n\nIf true, controls are " + "added to the grid left-to-right (to fill a row); then rows are added " + "top-to-bottom as shown below:\n" + "
      1 2 3 4\n"
      +      "5 6 7 8
      \n" + "If false, controls are added to the grid top-to-bottom (to fill a column); " + "then columns are added left-to-right as shown below:\n" + "
      1 3 5 7\n"
      +      "2 4 6 8
      " ); + + addField( "dynamicSize", TypeBool, Offset( mDynamicSize, GuiDynamicCtrlArrayControl ), + "If true, the width or height of this control will be automatically " + "calculated based on the number of child controls (width if " + "fillRowFirst is false, height if fillRowFirst is true)." ); + + addField( "padding", TypeRectSpacingI, Offset( mPadding, GuiDynamicCtrlArrayControl ), + "Padding around the top, bottom, left, and right of this control. This " + "reduces the area available for child controls." ); + + Parent::initPersistFields(); +} + + +// SimObject... + +void GuiDynamicCtrlArrayControl::inspectPostApply() +{ + resize(getPosition(), getExtent()); + Parent::inspectPostApply(); +} + + +// SimSet... + +void GuiDynamicCtrlArrayControl::addObject(SimObject *obj) +{ + Parent::addObject(obj); + + if ( !mFrozen ) + refresh(); +} + + +// GuiControl... + +bool GuiDynamicCtrlArrayControl::resize(const Point2I &newPosition, const Point2I &newExtent) +{ + if ( size() == 0 ) + return Parent::resize( newPosition, newExtent ); + + if ( mResizing ) + return false; + + mResizing = true; + + // Calculate the cellSize based on our widest/tallest child control + // if the flag to do so is set. + if ( mAutoCellSize ) + { + mColSize = 1; + mRowSize = 1; + + for ( U32 i = 0; i < size(); i++ ) + { + GuiControl *child = dynamic_cast(operator [](i)); + if ( child && child->isVisible() ) + { + if ( mColSize < child->getWidth() ) + mColSize = child->getWidth(); + if ( mRowSize < child->getHeight() ) + mRowSize = child->getHeight(); + } + } + } + + // Count number of visible, children guiControls. + S32 numChildren = 0; + for ( U32 i = 0; i < size(); i++ ) + { + GuiControl *child = dynamic_cast(operator [](i)); + if ( child && child->isVisible() ) + numChildren++; + } + + // Calculate number of rows and columns. + if ( !mFillRowFirst ) + { + mRows = 1; + while ( ( ( mRows + 1 ) * mRowSize + mRows * mRowSpacing ) <= ( newExtent.y - ( mPadding.top + mPadding.bottom ) ) ) + mRows++; + + mCols = numChildren / mRows; + if ( numChildren % mRows > 0 ) + mCols++; + } + else + { + mCols = 1; + while ( ( ( mCols + 1 ) * mColSize + mCols * mColSpacing ) <= ( newExtent.x - ( mPadding.left + mPadding.right ) ) ) + mCols++; + + mRows = numChildren / mCols; + if ( numChildren % mCols > 0 ) + mRows++; + } + + // Place each child... + S32 childcount = 0; + for ( S32 i = 0; i < size(); i++ ) + { + // Place control + GuiControl *gc = dynamic_cast(operator [](i)); + + // Added check if child is visible. Invisible children don't take part + if ( gc && gc->isVisible() ) + { + S32 curCol, curRow; + + // Get the current column and row... + if ( mFillRowFirst ) + { + curCol = childcount % mCols; + curRow = childcount / mCols; + } + else + { + curCol = childcount / mRows; + curRow = childcount % mRows; + } + + // Reposition and resize + Point2I newPos( mPadding.left + curCol * ( mColSize + mColSpacing ), mPadding.top + curRow * ( mRowSize + mRowSpacing ) ); + gc->resize( newPos, Point2I( mColSize, mRowSize ) ); + + childcount++; + } + } + + Point2I realExtent( newExtent ); + + if ( mDynamicSize ) + { + if ( mFillRowFirst ) + realExtent.y = mRows * mRowSize + ( mRows - 1 ) * mRowSpacing + ( mPadding.top + mPadding.bottom ); + else + realExtent.x = mCols * mColSize + ( mCols - 1 ) * mColSpacing + ( mPadding.left + mPadding.right ); + } + + mResizing = false; + + return Parent::resize( newPosition, realExtent ); +} + +void GuiDynamicCtrlArrayControl::childResized(GuiControl *child) +{ + Parent::childResized(child); + + if ( !mFrozen ) + refresh(); +} + +void GuiDynamicCtrlArrayControl::refresh() +{ + resize( getPosition(), getExtent() ); +} + +DefineEngineMethod( GuiDynamicCtrlArrayControl, refresh, void, (),, + "Recalculates the position and size of this control and all its children." ) +{ + object->refresh(); +} diff --git a/Engine/source/gui/containers/guiDynamicCtrlArrayCtrl.h b/Engine/source/gui/containers/guiDynamicCtrlArrayCtrl.h new file mode 100644 index 000000000..8a39b1872 --- /dev/null +++ b/Engine/source/gui/containers/guiDynamicCtrlArrayCtrl.h @@ -0,0 +1,80 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIDYNAMICCTRLARRAYCTRL_H_ +#define _GUIDYNAMICCTRLARRAYCTRL_H_ + +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif + +#include "gfx/gfxDevice.h" +#include "console/console.h" +#include "console/consoleTypes.h" + +class GuiDynamicCtrlArrayControl : public GuiControl +{ + typedef GuiControl Parent; + +public: + + GuiDynamicCtrlArrayControl(); + virtual ~GuiDynamicCtrlArrayControl(); + + DECLARE_CONOBJECT(GuiDynamicCtrlArrayControl); + DECLARE_CATEGORY( "Gui Containers" ); + + // ConsoleObject + static void initPersistFields(); + + // SimObject + void inspectPostApply(); + + // SimSet + void addObject(SimObject *obj); + + // GuiControl + bool resize(const Point2I &newPosition, const Point2I &newExtent); + void childResized(GuiControl *child); + + // GuiDynamicCtrlArrayCtrl + void refresh(); + +protected: + + S32 mCols; + S32 mRows; + S32 mRowSize; + S32 mColSize; + S32 mRowSpacing; + S32 mColSpacing; + bool mResizing; + bool mSizeToChildren; + bool mAutoCellSize; + bool mFrozen; + bool mDynamicSize; + bool mFillRowFirst; + + RectSpacingI mPadding; +}; + +#endif // _GUIDYNAMICCTRLARRAYCTRL_H_ \ No newline at end of file diff --git a/Engine/source/gui/containers/guiFormCtrl.cpp b/Engine/source/gui/containers/guiFormCtrl.cpp new file mode 100644 index 000000000..f1b86f453 --- /dev/null +++ b/Engine/source/gui/containers/guiFormCtrl.cpp @@ -0,0 +1,413 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/engineAPI.h" +#include "platform/platform.h" +#include "gui/containers/guiFormCtrl.h" + +#include "gui/core/guiDefaultControlRender.h" +#include "gfx/gfxDrawUtil.h" + +#ifdef TORQUE_TOOLS + +IMPLEMENT_CONOBJECT(GuiFormCtrl); + +ConsoleDocClass( GuiFormCtrl, + "@brief A generic form control.\n\n" + "Currently editor use only.\n\n " + "@internal" +); + +IMPLEMENT_CALLBACK( GuiFormCtrl, onResize, void, (), (), + "Called when the control is resized." ); + +GuiFormCtrl::GuiFormCtrl() +{ + setMinExtent(Point2I(200,100)); + mActive = true; + mMouseOver = false; + mDepressed = false; + mCanMove = false; + mCaption = "[none]"; + mUseSmallCaption = false; + + mContentLibrary = StringTable->insert(""); + mContent = StringTable->insert(""); + + mCanSaveFieldDictionary = true; + mIsContainer = true; + + // The attached menu bar + mHasMenu = false; + mMenuBar = NULL; +} + +GuiFormCtrl::~GuiFormCtrl() +{ + // If we still have a menu bar, delete it. + + if( mMenuBar ) + mMenuBar->deleteObject(); +} + +bool GuiFormCtrl::_setHasMenu( void *object, const char *index, const char *data ) +{ + GuiFormCtrl* ctrl = reinterpret_cast< GuiFormCtrl* >( object ); + ctrl->setHasMenu( dAtob( data ) ); + return false; +} + +void GuiFormCtrl::initPersistFields() +{ + addField("caption", TypeRealString, Offset(mCaption, GuiFormCtrl)); + addField("contentLibrary",TypeString, Offset(mContentLibrary, GuiFormCtrl)); + addField("content", TypeString, Offset(mContent, GuiFormCtrl)); + addField("movable", TypeBool, Offset(mCanMove, GuiFormCtrl)); + + addProtectedField( "hasMenu", TypeBool, Offset(mHasMenu, GuiFormCtrl), + &_setHasMenu, &defaultProtectedGetFn, + "" ); + + Parent::initPersistFields(); +} + +void GuiFormCtrl::setHasMenu( bool value ) +{ + if( mHasMenu == value ) + return; + + if( !value ) + { + mMenuBar->deleteObject(); + mMenuBar = NULL; + } + else + { + if( !mMenuBar ) + { + mMenuBar = new GuiMenuBar(); + mMenuBar->setField( "profile", "GuiFormMenuBarProfile" ); + mMenuBar->setField( "horizSizing", "right" ); + mMenuBar->setField( "vertSizing", "bottom" ); + mMenuBar->setField( "extent", "16 16" ); + mMenuBar->setField( "minExtent", "16 16" ); + mMenuBar->setField( "position", "0 0" ); + mMenuBar->setField( "class", "FormMenuBarClass "); // Give a generic class to the menu bar so that one set of functions may be used for all of them. + + mMenuBar->registerObject(); + mMenuBar->setProcessTicks(true); // Activate the processing of ticks to track if the mouse pointer has been hovering within the menu + } + + addObject( mMenuBar ); // Add the menu bar to the form + } + + mHasMenu = value; +} + +bool GuiFormCtrl::onWake() +{ + if ( !Parent::onWake() ) + return false; + + mFont = mProfile->mFont; + AssertFatal(mFont, "GuiFormCtrl::onWake: invalid font in profile" ); + + mProfile->constructBitmapArray(); + + if(mProfile->mUseBitmapArray && mProfile->mBitmapArrayRects.size()) + { + mThumbSize.set( mProfile->mBitmapArrayRects[0].extent.x, mProfile->mBitmapArrayRects[0].extent.y ); + mThumbSize.setMax( mProfile->mBitmapArrayRects[1].extent ); + + if(mFont->getHeight() > mThumbSize.y) + mThumbSize.y = mFont->getHeight(); + } + else + { + mThumbSize.set(20, 20); + } + + return true; +} + + +void GuiFormCtrl::addObject(SimObject *newObj ) +{ + if( ( mHasMenu && size() > 1) || (!mHasMenu && size() > 0 ) ) + { + Con::warnf("GuiFormCtrl::addObject - Forms may only have one *direct* child - Placing on Parent!"); + + GuiControl* parent = getParent(); + if ( parent ) + parent->addObject( newObj ); + + return; + } + + GuiControl *newCtrl = dynamic_cast( newObj ); + GuiFormCtrl*formCtrl = dynamic_cast( newObj ); + + if( newCtrl && formCtrl ) + newCtrl->setCanSave( true ); + else if ( newCtrl ) + newCtrl->setCanSave( false ); + + Parent::addObject( newObj ); +} + +void GuiFormCtrl::removeObject( SimObject* object ) +{ + if( object == mMenuBar ) + { + mHasMenu = false; + mMenuBar = NULL; + } + + Parent::removeObject( object ); +} + +bool GuiFormCtrl::acceptsAsChild( SimObject* object ) const +{ + return Parent::acceptsAsChild( object ) && + ( ( mHasMenu && size() == 1 ) || ( !mHasMenu && !size() ) ); // Only accept a single child. +} + +void GuiFormCtrl::onSleep() +{ + Parent::onSleep(); + mFont = NULL; +} + +bool GuiFormCtrl::resize(const Point2I &newPosition, const Point2I &newExtent) +{ + if( !Parent::resize(newPosition, newExtent) ) + return false; + + if( !mAwake || !mProfile->mBitmapArrayRects.size() ) + return false; + + // Should the caption be modified because the title bar is too small? + S32 textWidth = mProfile->mFont->getStrWidth(mCaption); + S32 newTextArea = getWidth() - mThumbSize.x - mProfile->mBitmapArrayRects[4].extent.x; + if(newTextArea < textWidth) + { + static char buf[256]; + + mUseSmallCaption = true; + mSmallCaption = StringTable->insert(""); + + S32 strlen = dStrlen((const char*)mCaption); + for(S32 i=strlen; i>=0; --i) + { + dStrcpy(buf, ""); + dStrncat(buf, (const char*)mCaption, i); + dStrcat(buf, "..."); + + textWidth = mProfile->mFont->getStrWidth(buf); + + if(textWidth < newTextArea) + { + mSmallCaption = StringTable->insert(buf, true); + break; + } + } + + } else + { + mUseSmallCaption = false; + } + + onResize_callback(); + + return true; +} + +void GuiFormCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + // Fill in the control's child area + RectI boundsRect(offset, getExtent()); + boundsRect.point.y += mThumbSize.y; + boundsRect.extent.y -= mThumbSize.y; + + // draw the border of the form if specified + if (mProfile->mOpaque) + GFX->getDrawUtil()->drawRectFill(boundsRect, mProfile->mFillColor); + + if (mProfile->mBorder) + renderBorder(boundsRect, mProfile); + + // If we don't have a child, put some text in the child area + if( empty() ) + { + GFX->getDrawUtil()->setBitmapModulation(ColorI(0,0,0)); + renderJustifiedText(boundsRect.point, boundsRect.extent, "[none]"); + } + + S32 textWidth = 0; + + // Draw our little bar, too + if (mProfile->mBitmapArrayRects.size() >= 5) + { + GFX->getDrawUtil()->clearBitmapModulation(); + + S32 barStart = offset.x + textWidth; + S32 barTop = mThumbSize.y / 2 + offset.y - mProfile->mBitmapArrayRects[3].extent.y / 2; + + Point2I barOffset(barStart, barTop); + + // Draw the start of the bar... + GFX->getDrawUtil()->drawBitmapStretchSR(mProfile->mTextureObject ,RectI(barOffset, mProfile->mBitmapArrayRects[2].extent), mProfile->mBitmapArrayRects[2] ); + + // Now draw the middle... + barOffset.x += mProfile->mBitmapArrayRects[2].extent.x; + + S32 barMiddleSize = (getExtent().x - (barOffset.x - offset.x)) - mProfile->mBitmapArrayRects[4].extent.x + 1; + + if (barMiddleSize > 0) + { + // We have to do this inset to prevent nasty stretching artifacts + RectI foo = mProfile->mBitmapArrayRects[3]; + foo.inset(1,0); + + GFX->getDrawUtil()->drawBitmapStretchSR( + mProfile->mTextureObject, + RectI(barOffset, Point2I(barMiddleSize, mProfile->mBitmapArrayRects[3].extent.y)), + foo + ); + } + + // And the end + barOffset.x += barMiddleSize; + + GFX->getDrawUtil()->drawBitmapStretchSR( mProfile->mTextureObject, RectI(barOffset, mProfile->mBitmapArrayRects[4].extent), + mProfile->mBitmapArrayRects[4]); + + GFX->getDrawUtil()->setBitmapModulation((mMouseOver ? mProfile->mFontColorHL : mProfile->mFontColor)); + renderJustifiedText(Point2I(mThumbSize.x, 0) + offset, Point2I(getWidth() - mThumbSize.x - mProfile->mBitmapArrayRects[4].extent.x, mThumbSize.y), (mUseSmallCaption ? mSmallCaption : mCaption) ); + + } + + // Render the children + renderChildControls(offset, updateRect); +} + +void GuiFormCtrl::onMouseMove(const GuiEvent &event) +{ + Point2I localMove = globalToLocalCoord(event.mousePoint); + + // If we're clicking in the header then resize + mMouseOver = (localMove.y < mThumbSize.y); + if(isMouseLocked()) + mDepressed = mMouseOver; + +} + +void GuiFormCtrl::onMouseEnter(const GuiEvent &event) +{ + setUpdate(); + if(isMouseLocked()) + { + mDepressed = true; + mMouseOver = true; + } + else + { + mMouseOver = true; + } + +} + +void GuiFormCtrl::onMouseLeave(const GuiEvent &event) +{ + setUpdate(); + if(isMouseLocked()) + mDepressed = false; + mMouseOver = false; +} + +void GuiFormCtrl::onMouseDown(const GuiEvent &event) +{ + Point2I localClick = globalToLocalCoord(event.mousePoint); + + // If we're clicking in the header then resize + if(localClick.y < mThumbSize.y) + { + mouseLock(); + mDepressed = true; + mMouseMovingWin = mCanMove; + + //update + setUpdate(); + } + + mOrigBounds = getBounds(); + + mMouseDownPosition = event.mousePoint; + + if (mMouseMovingWin ) + { + mouseLock(); + } + else + { + GuiControl *ctrl = findHitControl(localClick); + if (ctrl && ctrl != this) + ctrl->onMouseDown(event); + } +} + +void GuiFormCtrl::onMouseUp(const GuiEvent &event) +{ + // Make sure we only get events we ought to be getting... + if (! mActive) + return; + + mouseUnlock(); + setUpdate(); + + Point2I localClick = globalToLocalCoord(event.mousePoint); + + // If we're clicking in the header then resize + //if(localClick.y < mThumbSize.y && mDepressed) + // setCollapsed(!mCollapsed); +} + +DefineEngineMethod( GuiFormCtrl, getMenuID, S32, (),, + "Get the ID of this form's menu.\n\n" + "@return The ID of the form menu\n" ) +{ + return object->getMenuBarID(); +} + +U32 GuiFormCtrl::getMenuBarID() +{ + return mMenuBar ? mMenuBar->getId() : 0; +} + +DefineEngineMethod( GuiFormCtrl, setCaption, void, ( const char* caption ),, + "Sets the title of the form.\n\n" + "@param caption Form caption\n" ) +{ + object->setCaption( caption ); +} + +#endif diff --git a/Engine/source/gui/containers/guiFormCtrl.h b/Engine/source/gui/containers/guiFormCtrl.h new file mode 100644 index 000000000..255053127 --- /dev/null +++ b/Engine/source/gui/containers/guiFormCtrl.h @@ -0,0 +1,121 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIFORMCTRL_H_ +#define _GUIFORMCTRL_H_ + +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif + +#ifndef _GUI_PANEL_H_ +#include "gui/containers/guiPanel.h" +#endif + +#ifndef _GUIMENUBAR_H_ +#include "gui/editor/guiMenuBar.h" +#endif + +#ifndef _GUICANVAS_H_ +#include "gui/core/guiCanvas.h" +#endif + +#include "console/console.h" +#include "console/consoleTypes.h" + +class GuiMenuBar; + + +/// @addtogroup gui_container_group Containers +/// +/// @ingroup gui_group Gui System +/// @{ +class GuiFormCtrl : public GuiPanel +{ +private: + typedef GuiPanel Parent; + + Resource mFont; + String mCaption; + bool mCanMove; + bool mUseSmallCaption; + String mSmallCaption; + StringTableEntry mContentLibrary; + StringTableEntry mContent; + + Point2I mThumbSize; + + bool mHasMenu; + GuiMenuBar* mMenuBar; + + bool mMouseMovingWin; + + Point2I mMouseDownPosition; + RectI mOrigBounds; + RectI mStandardBounds; + + RectI mCloseButton; + RectI mMinimizeButton; + RectI mMaximizeButton; + + bool mMouseOver; + bool mDepressed; + + static bool _setHasMenu( void *object, const char *index, const char *data ); + +protected: + /// @name Callbacks + /// @{ + DECLARE_CALLBACK( void, onResize, () ); + /// @} + +public: + GuiFormCtrl(); + virtual ~GuiFormCtrl(); + + void setCaption( const char* caption ) { mCaption = caption; } + void setHasMenu( bool value ); + + bool resize(const Point2I &newPosition, const Point2I &newExtent); + void onRender(Point2I offset, const RectI &updateRect); + + bool onWake(); + void onSleep(); + + virtual void addObject( SimObject *object ); + virtual void removeObject( SimObject* object ); + virtual bool acceptsAsChild( SimObject* object ) const; + + void onMouseDown(const GuiEvent &event); + void onMouseUp(const GuiEvent &event); + void onMouseMove(const GuiEvent &event); + void onMouseLeave(const GuiEvent &event); + void onMouseEnter(const GuiEvent &event); + + U32 getMenuBarID(); + + static void initPersistFields(); + DECLARE_CONOBJECT(GuiFormCtrl); +}; +/// @} + +#endif \ No newline at end of file diff --git a/Engine/source/gui/containers/guiFrameCtrl.cpp b/Engine/source/gui/containers/guiFrameCtrl.cpp new file mode 100644 index 000000000..a756ca560 --- /dev/null +++ b/Engine/source/gui/containers/guiFrameCtrl.cpp @@ -0,0 +1,1153 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/containers/guiFrameCtrl.h" + +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "gui/core/guiCanvas.h" +#include "gfx/gfxDrawUtil.h" + + +IMPLEMENT_CONOBJECT(GuiFrameSetCtrl); + +ConsoleDocClass( GuiFrameSetCtrl, + "@brief A gui control allowing a window to be subdivided into panes, each " + "of which displays a gui control child of the GuiFrameSetCtrl.\n\n" + + "Each gui control child will have an associated FrameDetail through which " + "frame-specific details can be assigned. Frame-specific values override the " + "values specified for the entire frameset.\n\n" + + "Note that it is possible to have more children than frames, or more frames " + "than children. In the former case, the extra children will not be visible " + "(they are moved beyond the visible extent of the frameset). In the latter " + "case, frames will be empty. For example, if a frameset had two columns and " + "two rows but only three gui control children they would be assigned to " + "frames as follows:\n" + + "
      \n"
      +   "                 1 | 2\n"
      +   "                 -----\n"
      +   "                 3 |\n"
      +   "
      \n" + + "The last frame would be blank.\n\n" + + "@tsexample\n" + "new GuiFrameSetCtrl()\n" + "{\n" + " columns = \"3\";\n" + " rows = \"2\";\n" + " borderWidth = \"1\";\n" + " borderColor = \"128 128 128\";\n" + " borderEnable = \"dynamic\";\n" + " borderMovable = \"dynamic\";\n" + " autoBalance = \"1\";\n" + " fudgeFactor = \"0\";\n" + " //Properties not specific to this control have been omitted from this example.\n" + "};\n" + "@endtsexample\n\n" + + "@ingroup GuiContainers" +); + +//----------------------------------------------------------------------------- + +ImplementEnumType( GuiFrameState, + "\n\n" + "@ingroup GuiContainers" ) + { GuiFrameSetCtrl::FRAME_STATE_ON, "alwaysOn" }, + { GuiFrameSetCtrl::FRAME_STATE_OFF, "alwaysOff" }, + { GuiFrameSetCtrl::FRAME_STATE_AUTO, "dynamic" } +EndImplementEnumType; + +//----------------------------------------------------------------------------- +void GuiFrameSetCtrl::initPersistFields() +{ + addField( "columns", TypeS32Vector, Offset(mColumnOffsets, GuiFrameSetCtrl), + "A vector of column offsets (determines the width of each column)." ); + + addField( "rows", TypeS32Vector, Offset(mRowOffsets, GuiFrameSetCtrl), + "A vector of row offsets (determines the height of each row)." ); + + addField( "borderWidth", TypeS32, Offset(mFramesetDetails.mBorderWidth, GuiFrameSetCtrl), + "Width of interior borders between cells in pixels." ); + + addField( "borderColor", TypeColorI, Offset(mFramesetDetails.mBorderColor, GuiFrameSetCtrl), + "Color of interior borders between cells." ); + + addField( "borderEnable", TYPEID< FrameState >(), Offset(mFramesetDetails.mBorderEnable, GuiFrameSetCtrl), + "Controls whether frame borders are enabled.\n\nFrames use this value " + "unless overridden for that frame using %ctrl.frameBorder(index)" ); + + addField( "borderMovable", TYPEID< FrameState >(), Offset(mFramesetDetails.mBorderMovable, GuiFrameSetCtrl), + "Controls whether borders can be dynamically repositioned with the mouse " + "by the user.\n\nFrames use this value unless overridden for that frame " + "using %ctrl.frameMovable(index)" ); + + addField( "autoBalance", TypeBool, Offset(mAutoBalance, GuiFrameSetCtrl), + "If true, row and column offsets are automatically scaled to match the " + "new extents when the control is resized." ); + + addField( "fudgeFactor", TypeS32, Offset(mFudgeFactor, GuiFrameSetCtrl), + "Offset for row and column dividers in pixels" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- +DefineEngineMethod( GuiFrameSetCtrl, frameBorder, void, ( S32 index, const char* state ), ( "dynamic" ), + "Override the borderEnable setting for this frame.\n\n" + "@param index Index of the frame to modify\n" + "@param state New borderEnable state: \"on\", \"off\" or \"dynamic\"\n" ) +{ + object->frameBorderEnable( index, state ); +} + +DefineEngineMethod( GuiFrameSetCtrl, frameMovable, void, ( S32 index, const char* state ), ( "dynamic" ), + "Override the borderMovable setting for this frame.\n\n" + "@param index Index of the frame to modify\n" + "@param state New borderEnable state: \"on\", \"off\" or \"dynamic\"\n" ) +{ + object->frameBorderMovable( index, state ); +} + +DefineEngineMethod( GuiFrameSetCtrl, frameMinExtent, void, ( S32 index, S32 width, S32 height ),, + "Set the minimum width and height for the frame. It will not be possible " + "for the user to resize the frame smaller than this.\n\n" + "@param index Index of the frame to modify\n" + "@param width Minimum width in pixels\n" + "@param height Minimum height in pixels\n" ) +{ + Point2I extent( getMax( 0, width ), getMax( 0, height ) ); + object->frameMinExtent( index, extent); +} + +DefineEngineMethod( GuiFrameSetCtrl, framePadding, void, ( S32 index, RectSpacingI padding ),, + "Set the padding for this frame. Padding introduces blank space on the inside " + "edge of the frame.\n\n" + "@param index Index of the frame to modify\n" + "@param padding Frame top, bottom, left, and right padding\n" ) +{ + object->framePadding( index, padding); +} + +DefineEngineMethod( GuiFrameSetCtrl, getFramePadding, RectSpacingI, ( S32 index ),, + "Get the padding for this frame.\n\n" + "@param index Index of the frame to query\n" ) +{ + return object->getFramePadding( index ); +} + +DefineEngineMethod( GuiFrameSetCtrl, addColumn, void, (),, + "Add a new column.\n\n" ) +{ + Vector * columns = object->columnOffsets(); + columns->push_back(0); + object->balanceFrames(); +} + +DefineEngineMethod( GuiFrameSetCtrl, addRow, void, (),, + "Add a new row.\n\n" ) +{ + Vector * rows = object->rowOffsets(); + rows->push_back(0); + object->balanceFrames(); +} + +DefineEngineMethod( GuiFrameSetCtrl, removeColumn, void, (),, + "Remove the last (rightmost) column.\n\n" ) +{ + Vector * columns = object->columnOffsets(); + + if(columns->size() > 0) + { + columns->setSize(columns->size() - 1); + object->balanceFrames(); + } + else + Con::errorf(ConsoleLogEntry::General, "No columns exist to remove"); +} + +DefineEngineMethod( GuiFrameSetCtrl, removeRow, void, (),, + "Remove the last (bottom) row.\n\n" ) +{ + Vector * rows = object->rowOffsets(); + + if(rows->size() > 0) + { + rows->setSize(rows->size() - 1); + object->balanceFrames(); + } + else + Con::errorf(ConsoleLogEntry::General, "No rows exist to remove"); +} + +DefineEngineMethod( GuiFrameSetCtrl, getColumnCount, S32, (),, + "Get the number of columns.\n\n" + "@return The number of columns\n" ) +{ + return(object->columnOffsets()->size()); +} + +DefineEngineMethod( GuiFrameSetCtrl, getRowCount, S32, (),, + "Get the number of rows.\n\n" + "@return The number of rows\n" ) +{ + return(object->rowOffsets()->size()); +} + +DefineEngineMethod( GuiFrameSetCtrl, getColumnOffset, S32, ( S32 index ),, + "Get the horizontal offset of a column.\n\n" + "@param index Index of the column to query\n" + "@return Column offset in pixels\n" ) +{ + if(index < 0 || index > object->columnOffsets()->size()) + { + Con::errorf(ConsoleLogEntry::General, "Column index out of range"); + return(0); + } + return((*object->columnOffsets())[index]); +} + +DefineEngineMethod( GuiFrameSetCtrl, getRowOffset, S32, ( S32 index ),, + "Get the vertical offset of a row.\n\n" + "@param index Index of the row to query\n" + "@return Row offset in pixels\n" ) +{ + if(index < 0 || index > object->rowOffsets()->size()) + { + Con::errorf(ConsoleLogEntry::General, "Row index out of range"); + return(0); + } + return((*object->rowOffsets())[index]); +} + +DefineEngineMethod( GuiFrameSetCtrl, setColumnOffset, void, ( S32 index, S32 offset ),, + "Set the horizontal offset of a column.\n\n" + "Note that column offsets must always be in increasing order, and therefore " + "this offset must be between the offsets of the colunns either side.\n" + "@param index Index of the column to modify\n" + "@param offset New column offset\n" ) +{ + Vector & columns = *(object->columnOffsets()); + + if(index < 0 || index > columns.size()) + { + Con::errorf(ConsoleLogEntry::General, "Column index out of range"); + return; + } + + // check the offset + if(((index > 0) && (offset < columns[index-1])) || + ((index < (columns.size() - 1)) && (offset > columns[index+1]))) + { + Con::errorf(ConsoleLogEntry::General, "Invalid column offset"); + return; + } + + columns[index] = offset; + object->updateSizes(); +} + +DefineEngineMethod( GuiFrameSetCtrl, setRowOffset, void, ( S32 index, S32 offset ),, + "Set the vertical offset of a row.\n\n" + "Note that row offsets must always be in increasing order, and therefore " + "this offset must be between the offsets of the rows either side.\n" + "@param index Index of the row to modify\n" + "@param offset New row offset\n" ) +{ + Vector & rows = *(object->rowOffsets()); + + if(index < 0 || index > rows.size()) + { + Con::errorf(ConsoleLogEntry::General, "Row index out of range"); + return; + } + + // check the offset + if(((index > 0) && (offset < rows[index-1])) || + ((index < (rows.size() - 1)) && (offset > rows[index+1]))) + { + Con::errorf(ConsoleLogEntry::General, "Invalid row offset"); + return; + } + + rows[index] = offset; + object->updateSizes(); +} + +DefineEngineMethod( GuiFrameSetCtrl, updateSizes, void, (),, + "Recalculates child control sizes." ) +{ + object->updateSizes(); +} + +//----------------------------------------------------------------------------- +GuiFrameSetCtrl::GuiFrameSetCtrl() +{ + VECTOR_SET_ASSOCIATION(mColumnOffsets); + VECTOR_SET_ASSOCIATION(mRowOffsets); + VECTOR_SET_ASSOCIATION(mFrameDetails); + + mAutoBalance = true; + mIsContainer = true; + + init(1, 1, NULL, NULL); +} + +//----------------------------------------------------------------------------- +GuiFrameSetCtrl::GuiFrameSetCtrl(U32 columns, U32 rows, const U32 columnOffsets[], const U32 rowOffsets[]) +{ + init(columns, rows, columnOffsets, rowOffsets); +} + +//----------------------------------------------------------------------------- +GuiFrameSetCtrl::~GuiFrameSetCtrl() +{ + while (mFrameDetails.size() > 0) + { + delete mFrameDetails.last(); + mFrameDetails.pop_back(); + } +} + +//----------------------------------------------------------------------------- + +void GuiFrameSetCtrl::addObject(SimObject *object) +{ + AssertFatal(object != NULL, "GuiFrameSetCtrl::addObject: NULL object"); + + // assign the object to a frame - give it default frame details + Parent::addObject(object); + GuiControl *gc = dynamic_cast(object); + if (gc != NULL) + { + FrameDetail *detail = new FrameDetail; + detail->mMinExtent = gc->getMinExtent(); + mFrameDetails.push_back(detail); + } + else + mFrameDetails.push_back(NULL); + // resize it to fit into the frame to which it is assigned (if no frame for it, don't bother resizing) + if(isAwake()) + computeSizes(); +} + +//----------------------------------------------------------------------------- +void GuiFrameSetCtrl::removeObject(SimObject *object) +{ + if (object != NULL) + { + VectorPtr::iterator soitr; + VectorPtr::iterator fditr = mFrameDetails.begin(); + for (soitr = begin(); soitr != end(); soitr++, fditr++) + { + if (*soitr == object) + { + delete *fditr; + mFrameDetails.erase(fditr); + break; + } + } + } + Parent::removeObject(object); + + if(isAwake()) + computeSizes(); +} + +//----------------------------------------------------------------------------- +bool GuiFrameSetCtrl::resize(const Point2I &newPos, const Point2I &newExtent) +{ + // rebalance before losing the old extent (if required) + if (mAutoBalance == true) + rebalance(newExtent); + + if( !Parent::resize(newPos, newExtent) ) + return false; + + // compute new sizing info for the frames - takes care of resizing the children + computeSizes( !mAutoBalance ); + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiFrameSetCtrl::getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent) +{ + GuiCanvas *pRoot = getRoot(); + if( !pRoot ) + return; + + Region curRegion = NONE; + // Determine the region by mouse position. + Point2I curMousePos = globalToLocalCoord(lastGuiEvent.mousePoint); + curRegion = pointInAnyRegion(curMousePos); + + PlatformWindow *pWindow = pRoot->getPlatformWindow(); + AssertFatal(pWindow != NULL,"GuiControl without owning platform window! This should not be possible."); + PlatformCursorController *pController = pWindow->getCursorController(); + AssertFatal(pController != NULL,"PlatformWindow without an owned CursorController!"); + + switch (curRegion) + { + case VERTICAL_DIVIDER: + // change to left-right cursor + if(pRoot->mCursorChanged != PlatformCursorController::curResizeVert) + { + //*** We've already changed the cursor, so set it back before we change it again. + if(pRoot->mCursorChanged != -1) + pController->popCursor(); + + //*** Now change the cursor shape + pController->pushCursor(PlatformCursorController::curResizeVert); + pRoot->mCursorChanged = PlatformCursorController::curResizeVert; + + } + break; + + case HORIZONTAL_DIVIDER: + // change to up-down cursor + if(pRoot->mCursorChanged != PlatformCursorController::curResizeHorz) + { + //*** We've already changed the cursor, so set it back before we change it again. + if(pRoot->mCursorChanged != -1) + pController->popCursor(); + + //*** Now change the cursor shape + pController->pushCursor(PlatformCursorController::curResizeHorz); + pRoot->mCursorChanged = PlatformCursorController::curResizeHorz; + } + break; + + case DIVIDER_INTERSECTION: + // change to move cursor + if(pRoot->mCursorChanged != PlatformCursorController::curResizeAll) + { + //*** We've already changed the cursor, so set it back before we change it again. + if(pRoot->mCursorChanged != -1) + pController->popCursor(); + + //*** Now change the cursor shape + pController->pushCursor(PlatformCursorController::curResizeAll); + pRoot->mCursorChanged = PlatformCursorController::curResizeAll; + + } + break; + + case NONE: + default: + if(pRoot->mCursorChanged != -1) + { + //*** We've already changed the cursor, so set it back before we change it again. + pController->popCursor(); + + pRoot->mCursorChanged = -1; + } + break; + } + +} + +//----------------------------------------------------------------------------- +void GuiFrameSetCtrl::onMouseDown(const GuiEvent &event) +{ + if (mFramesetDetails.mBorderEnable != FRAME_STATE_OFF && mFramesetDetails.mBorderMovable != FRAME_STATE_OFF) + { + // determine if a divider was hit + Point2I curMousePos = globalToLocalCoord(event.mousePoint); + findHitRegion(curMousePos); // sets mCurVerticalHit, mCurHorizontalHit, & mCurHitRegion + + if (mCurHitRegion != NONE) + { + mouseLock(); + setFirstResponder(); + setUpdate(); + } + } +} + +//----------------------------------------------------------------------------- +void GuiFrameSetCtrl::onMouseUp(const GuiEvent &event) +{ + TORQUE_UNUSED(event); + if (mCurHitRegion != NONE) + { + mCurHitRegion = NONE; + mCurVerticalHit = NO_HIT; + mCurHorizontalHit = NO_HIT; + mouseUnlock(); + setUpdate(); + } +} + +//----------------------------------------------------------------------------- +void GuiFrameSetCtrl::onMouseDragged(const GuiEvent &event) +{ + if (mCurHitRegion != NONE) + { + // identify the frames involved in the resizing, checking if they are resizable + S32 indexes[4]; + S32 activeFrames = findResizableFrames(indexes); + if (activeFrames > 0) + { + S32 range[4]; + // determine the range of movement, limiting as specified by individual frames + computeMovableRange(mCurHitRegion, mCurVerticalHit, mCurHorizontalHit, activeFrames, indexes, range); + Point2I curMousePos = globalToLocalCoord(event.mousePoint); + switch (mCurHitRegion) + { + case VERTICAL_DIVIDER: + mColumnOffsets[mCurVerticalHit] = getMin(getMax(range[0], curMousePos.x - mLocOnDivider.x), range[1]); + break; + case HORIZONTAL_DIVIDER: + mRowOffsets[mCurHorizontalHit] = getMin(getMax(range[0], curMousePos.y - mLocOnDivider.y), range[1]); + break; + case DIVIDER_INTERSECTION: + mColumnOffsets[mCurVerticalHit] = getMin(getMax(range[0], curMousePos.x - mLocOnDivider.x), range[1]); + mRowOffsets[mCurHorizontalHit] = getMin(getMax(range[2], curMousePos.y - mLocOnDivider.y), range[3]); + break; + default: + return; + } + computeSizes(); + } + } +} + +//----------------------------------------------------------------------------- +bool GuiFrameSetCtrl::onAdd() +{ + if (Parent::onAdd() == false) + return(false); + + return(true); +} + +bool GuiFrameSetCtrl::onWake() +{ + if (Parent::onWake() == false) + return(false); + + computeSizes(); + return(true); +} + +//----------------------------------------------------------------------------- +void GuiFrameSetCtrl::onRender(Point2I offset, const RectI &updateRect ) +{ + + Parent::onRender( offset, updateRect ); + + drawDividers(offset); + +} + +//----------------------------------------------------------------------------- +bool GuiFrameSetCtrl::init(U32 columns, U32 rows, const U32 columnOffsets[], const U32 rowOffsets[]) +{ + if (columns != 0 && rows != 0) + { + mColumnOffsets.clear(); + mRowOffsets.clear(); + U32 i; + for (i = 0; i < columns; i++) + { + if (columnOffsets == NULL) + mColumnOffsets.push_back(0); + else + { + AssertFatal(columnOffsets != NULL, "GuiFrameSetCtrl::init: NULL column offsets"); + mColumnOffsets.push_back((U32)columnOffsets[i]); + if (i > 0) + { + AssertFatal(mColumnOffsets[i - 1] < mColumnOffsets[i], "GuiFrameSetCtrl::init: column offsets must be monotonically increasing"); + mColumnOffsets.clear(); + return(false); + } + } + } + for (i = 0; i < rows; i++) + { + if (rowOffsets == NULL) + mRowOffsets.push_back(0); + else + { + AssertFatal(rowOffsets != NULL, "GuiFrameSetCtrl::init: NULL row offsets"); + mRowOffsets.push_back((U32)rowOffsets[i]); + if (i > 0) + { + AssertFatal(mRowOffsets[i - 1] < mRowOffsets[i], "GuiFrameSetCtrl::init: row offsets must be monotonically increasing"); + mRowOffsets.clear(); + return(false); + } + } + } + } + mFramesetDetails.mBorderWidth = DEFAULT_BORDER_WIDTH; + mFramesetDetails.mBorderEnable = FRAME_STATE_AUTO; + mFramesetDetails.mBorderMovable = FRAME_STATE_AUTO; + mAutoBalance = false; + mFudgeFactor = 0; + mCurHitRegion = NONE; + mCurVerticalHit = NO_HIT; + mCurHorizontalHit = NO_HIT; + return(true); +} + +//----------------------------------------------------------------------------- +// point is assumed to already be in local coordinates. +GuiFrameSetCtrl::Region GuiFrameSetCtrl::findHitRegion(const Point2I &point) +{ + Vector::iterator itr; + S32 i = 1; + // step through vertical dividers + for (itr = mColumnOffsets.begin() + 1; itr < mColumnOffsets.end(); itr++, i++) + { + if (hitVerticalDivider(*itr, point) == true) + { + mCurVerticalHit = i; + mLocOnDivider.x = point.x - (*itr); + break; + } + } + i = 1; + // step through horizontal dividers + for (itr = mRowOffsets.begin() + 1; itr < mRowOffsets.end(); itr++, i++) + { + if (hitHorizontalDivider(*itr, point) == true) + { + mCurHorizontalHit = i; + mLocOnDivider.y = point.y - (*itr); + break; + } + } + // now set type of hit... + if (mCurVerticalHit != NO_HIT) + { + if (mCurHorizontalHit != NO_HIT) + return(mCurHitRegion = DIVIDER_INTERSECTION); + else + return(mCurHitRegion = VERTICAL_DIVIDER); + } + else if (mCurHorizontalHit != NO_HIT) + return(mCurHitRegion = HORIZONTAL_DIVIDER); + else + return(mCurHitRegion = NONE); +} + +GuiFrameSetCtrl::Region GuiFrameSetCtrl::pointInAnyRegion(const Point2I &point) +{ + Vector::iterator itr; + S32 i = 1; + S32 curVertHit = NO_HIT, curHorzHit = NO_HIT; + Region result = NONE; + // step through vertical dividers + for (itr = mColumnOffsets.begin() + 1; itr < mColumnOffsets.end(); itr++, i++) + { + if (hitVerticalDivider(*itr, point) == true) + { + curVertHit = i; + break; + } + } + i = 1; + // step through horizontal dividers + for (itr = mRowOffsets.begin() + 1; itr < mRowOffsets.end(); itr++, i++) + { + if (hitHorizontalDivider(*itr, point) == true) + { + curHorzHit = i; + break; + } + } + // now select the type of region in which the point lies + if (curVertHit != NO_HIT) + { + if (curHorzHit != NO_HIT) + result = DIVIDER_INTERSECTION; + else + result = VERTICAL_DIVIDER; + } + else if (curHorzHit != NO_HIT) + result = HORIZONTAL_DIVIDER; + return(result); +} + +//----------------------------------------------------------------------------- +// indexes must have at least 4 entries. +// This *may* modify mCurVerticalHit, mCurHorizontalHit, and mCurHitRegion if it +// determines that movement is disabled by frame content. +// If it does make such a change, it also needs to do the reset performed by +// onMouseUp if it sets mCurHitRegion to NONE. +S32 GuiFrameSetCtrl::findResizableFrames(S32 indexes[]) +{ + AssertFatal(indexes != NULL, "GuiFrameSetCtrl::findResizableFrames: NULL indexes"); + + // first, find the column and row indexes of the affected columns/rows + S32 validIndexes = 0; + switch (mCurHitRegion) + { + case VERTICAL_DIVIDER: // columns + indexes[0] = mCurVerticalHit - 1; + indexes[1] = mCurVerticalHit; + validIndexes = 2; + break; + case HORIZONTAL_DIVIDER: // rows + indexes[0] = mCurHorizontalHit - 1; + indexes[1] = mCurHorizontalHit; + validIndexes = 2; + break; + case DIVIDER_INTERSECTION: // columns & rows + indexes[0] = mCurVerticalHit - 1; // columns + indexes[1] = mCurVerticalHit; + indexes[2] = mCurHorizontalHit - 1; // rows + indexes[3] = mCurHorizontalHit; + validIndexes = 4; + break; + default: + break; + } + // now, make sure these indexes are for movable frames + VectorPtr::iterator soitr; + VectorPtr::iterator fditr = mFrameDetails.begin(); + GuiControl *gc; + S32 column = 0; + S32 row = 0; + S32 columns = mColumnOffsets.size(); + S32 rows = mRowOffsets.size(); + for (soitr = begin(); soitr != end() && validIndexes > 0; soitr++, fditr++) + { + // don't continue if some of the frames are empty + if (fditr == mFrameDetails.end()) + break; + // otherwise, check the gui elements for move-restrictions + gc = dynamic_cast(*soitr); + if (gc != NULL) + { + if (column == columns) + { + column = 0; + row++; + } + if (row == rows) + break; + switch (mCurHitRegion) + { + case VERTICAL_DIVIDER: + if ((column == indexes[0] || column == indexes[1]) && (*fditr) && (*fditr)->mBorderMovable == FRAME_STATE_OFF) + validIndexes = 0; + break; + case HORIZONTAL_DIVIDER: + if ((row == indexes[0] || row == indexes[1]) && (*fditr) && (*fditr)->mBorderMovable == FRAME_STATE_OFF) + validIndexes = 0; + break; + case DIVIDER_INTERSECTION: + if ((column == indexes[0] || column == indexes[1]) && (*fditr) && (*fditr)->mBorderMovable == FRAME_STATE_OFF) + { + if ((row == indexes[2] || row == indexes[3]) && (*fditr) && (*fditr)->mBorderMovable == FRAME_STATE_OFF) + validIndexes = 0; + else + { + mCurHitRegion = HORIZONTAL_DIVIDER; + mCurVerticalHit = NO_HIT; + indexes[0] = indexes[2]; + indexes[1] = indexes[3]; + validIndexes = 2; + } + } + else if ((row == indexes[2] || row == indexes[3]) && (*fditr) && (*fditr)->mBorderMovable == FRAME_STATE_OFF) + { + mCurHitRegion = VERTICAL_DIVIDER; + mCurHorizontalHit = NO_HIT; + validIndexes = 2; + } + break; + default: + return(0); + } + column++; + } + } + if (validIndexes == 0) + { + mCurHitRegion = NONE; + mCurVerticalHit = NO_HIT; + mCurHorizontalHit = NO_HIT; + mouseUnlock(); + setUpdate(); + } + return(validIndexes); +} + +//----------------------------------------------------------------------------- +// This method locates the gui control and frame detail associated with a +// particular frame index. +bool GuiFrameSetCtrl::findFrameContents(S32 index, GuiControl **gc, FrameDetail **fd) +{ + AssertFatal(gc != NULL, "GuiFrameSetCtrl::findFrameContents: NULL gui control pointer"); + AssertFatal(fd != NULL, "GuiFrameSetCtrl::findFrameContents: NULL frame detail pointer"); + AssertFatal(*gc == NULL, "GuiFrameSetCtrl::findFrameContents: contents of gui control must be NULL"); + AssertFatal(*fd == NULL, "GuiFrameSetCtrl::findFrameContents: contents of frame detail must be NULL"); + + if (index >= 0 && index < size()) + { + VectorPtr::iterator soitr; + VectorPtr::iterator fditr = mFrameDetails.begin(); + for (soitr = begin(); soitr != end(); soitr++, fditr++, index--) + { + if (index == 0) + { + GuiControl *guiCtrl = dynamic_cast(*soitr); + if (guiCtrl != NULL) + { + *gc = guiCtrl; + *fd = *fditr; + return(true); + } + else + break; + } + } + } + return(false); +} + +//----------------------------------------------------------------------------- +void GuiFrameSetCtrl::computeSizes(bool balanceFrames) +{ + S32 columns = mColumnOffsets.size(); + S32 rows = mRowOffsets.size(); + S32 vDividers = columns - 1; + S32 hDividers = rows - 1; + + if ( !balanceFrames && mFrameDetails.size() == ( columns * rows ) ) + { + // This will do some goofy things if you allow this control to resize smaller than + // the additive minimum extents of its frames--so don't. + S32 index, delta; + + if ( columns > 1 ) + { + index = columns - 1; + delta = mFrameDetails[index]->mMinExtent.x - ( getWidth() - mColumnOffsets[index] ); + while ( delta > 0 ) + { + mColumnOffsets[index--] -= delta; + if ( index >= 0 ) + delta = mFrameDetails[index]->mMinExtent.x - ( mColumnOffsets[index + 1] - mColumnOffsets[index] ); + else + break; + } + } + + if ( rows > 1 ) + { + index = rows - 1; + delta = mFrameDetails[columns * index]->mMinExtent.y - ( getHeight() - mRowOffsets[index] ); + while ( delta > 0 ) + { + mRowOffsets[index--] -= delta; + if ( index >= 0 ) + delta = mFrameDetails[columns * index]->mMinExtent.y - ( mRowOffsets[index + 1] - mRowOffsets[index] ); + else + break; + } + } + } + + // first, update the divider placement if necessary + if (balanceFrames == true && mColumnOffsets.size() > 0 && mRowOffsets.size() > 0) + { + Vector::iterator itr; + F32 totWidth = F32(getWidth() - vDividers * mFramesetDetails.mBorderWidth); + F32 totHeight = F32(getHeight() - hDividers * mFramesetDetails.mBorderWidth); + F32 frameWidth = totWidth/(F32)columns; + F32 frameHeight = totHeight/(F32)rows; + F32 i = 0.; + for (itr = mColumnOffsets.begin(); itr != mColumnOffsets.end(); itr++, i++) + *itr = (S32)(i * (frameWidth + (F32)mFramesetDetails.mBorderWidth)); + i = 0.; + for (itr = mRowOffsets.begin(); itr != mRowOffsets.end(); itr++, i++) + *itr = (S32)(i * (frameHeight + (F32)mFramesetDetails.mBorderWidth)); + } + + // now, resize the contents of each frame (and move content w/o a frame beyond visible range) + VectorPtr::iterator soitr; + VectorPtr::iterator fditr = mFrameDetails.begin(); + GuiControl *gc; + S32 column = 0; + S32 row = 0; + Point2I newPos; + Point2I newExtent; + // step through all the children + for (soitr = begin(); soitr != end(); soitr++, fditr++) + { + // column and row track the current frame being resized + if (column == columns) + { + column = 0; + row++; + } + // resize the contents if its a gui control... + gc = dynamic_cast(*soitr); + if (gc != NULL) + { + if (row >= rows) + { + // no more visible frames + newPos = getExtent(); + newExtent.set(DEFAULT_MIN_FRAME_EXTENT, DEFAULT_MIN_FRAME_EXTENT); + gc->resize(newPos, newExtent); + continue; + } + else + { + // determine x components of new position & extent + newPos.x = mColumnOffsets[column]; + if (column == vDividers) + newExtent.x = getWidth() - mColumnOffsets[column]; // last column + else + newExtent.x = mColumnOffsets[column + 1] - mColumnOffsets[column] - mFramesetDetails.mBorderWidth; // any other column + // determine y components of new position & extent + newPos.y = mRowOffsets[row]; + if (row == hDividers) + newExtent.y = getHeight() - mRowOffsets[row]; // last row + else + newExtent.y = mRowOffsets[row + 1] - mRowOffsets[row] - mFramesetDetails.mBorderWidth; // any other row + + if ( *fditr ) + { + FrameDetail *fd = ( *fditr ); + + // Account for Padding. + newPos.x += fd->mPadding.left; + newPos.y += fd->mPadding.top; + newExtent.x -= ( fd->mPadding.left + fd->mPadding.right ); + newExtent.y -= ( fd->mPadding.top + fd->mPadding.bottom ); + } + + // apply the new position & extent + gc->resize(newPos, newExtent); + column++; + } + } + } +} + +//----------------------------------------------------------------------------- +// this method looks at the previous offsets, and uses them to redistribute +// the available height & width proportionally. +void GuiFrameSetCtrl::rebalance(const Point2I &newExtent) +{ + TORQUE_UNUSED(newExtent); + + // look at old_width and old_height - current extent + F32 widthScale = (F32)newExtent.x/(F32)getWidth(); + F32 heightScale = (F32)newExtent.y/(F32)getHeight(); + Vector::iterator itr; + // look at old width offsets + for (itr = mColumnOffsets.begin() + 1; itr < mColumnOffsets.end(); itr++) + // multiply each by new_width/old_width + *itr = S32(F32(*itr) * widthScale); + // look at old height offsets + for (itr = mRowOffsets.begin() + 1; itr < mRowOffsets.end(); itr++) + // multiply each by new_height/new_width + *itr = S32(F32(*itr) * heightScale); + +} + +//----------------------------------------------------------------------------- +void GuiFrameSetCtrl::computeMovableRange(Region hitRegion, S32 vertHit, S32 horzHit, S32 numIndexes, const S32 indexes[], S32 ranges[]) +{ + S32 hardRanges[4] = { 0 }; + switch (numIndexes) + { + case 2: + switch (hitRegion) + { + case VERTICAL_DIVIDER: + ranges[0] = hardRanges[0] = (vertHit <= 1) ? mFramesetDetails.mBorderWidth : mColumnOffsets[vertHit - 1] + mFramesetDetails.mBorderWidth; + ranges[1] = hardRanges[1] = (vertHit >= (mColumnOffsets.size() - 1)) ? getWidth() : mColumnOffsets[vertHit + 1] - mFramesetDetails.mBorderWidth; + break; + case HORIZONTAL_DIVIDER: + ranges[0] = hardRanges[0] = (horzHit <= 1) ? mFramesetDetails.mBorderWidth : mRowOffsets[horzHit - 1] + mFramesetDetails.mBorderWidth; + ranges[1] = hardRanges[1] = (horzHit >= (mRowOffsets.size() - 1)) ? getHeight() : mRowOffsets[horzHit + 1] - mFramesetDetails.mBorderWidth; + break; + default: + return; + } + break; + case 4: + if (hitRegion == DIVIDER_INTERSECTION) + { + ranges[0] = hardRanges[0] = (vertHit <= 1) ? mFramesetDetails.mBorderWidth : mColumnOffsets[vertHit - 1] + mFramesetDetails.mBorderWidth; + ranges[1] = hardRanges[1] = (vertHit >= (mColumnOffsets.size() - 1)) ? getWidth() : mColumnOffsets[vertHit + 1] - mFramesetDetails.mBorderWidth; + ranges[2] = hardRanges[2] = (horzHit <= 1) ? mFramesetDetails.mBorderWidth : mRowOffsets[horzHit - 1] + mFramesetDetails.mBorderWidth; + ranges[3] = hardRanges[3] = (horzHit >= (mRowOffsets.size() - 1)) ? getHeight() : mRowOffsets[horzHit + 1] - mFramesetDetails.mBorderWidth; + } + else + return; + break; + default: + return; + } + // now that we have the hard ranges, reduce ranges based on minimum frame extents + VectorPtr::iterator soitr; + VectorPtr::iterator fditr = mFrameDetails.begin(); + GuiControl *gc; + S32 column = 0; + S32 row = 0; + S32 columns = mColumnOffsets.size(); + S32 rows = mRowOffsets.size(); + for (soitr = begin(); soitr != end(); soitr++, fditr++) + { + // only worry about visible frames + if (column == columns) + { + column = 0; + row++; + } + if (row == rows) + return; + gc = dynamic_cast(*soitr); + if (gc != NULL) + { + // the gui control is in a visible frame, so look at its frame details + if ((*fditr) != NULL) + { + switch (hitRegion) + { + case VERTICAL_DIVIDER: + if (column == indexes[0]) + ranges[0] = getMax(ranges[0], hardRanges[0] + (*fditr)->mMinExtent.x); + if (column == indexes[1]) + ranges[1] = getMin(ranges[1], hardRanges[1] - (*fditr)->mMinExtent.x); + break; + case HORIZONTAL_DIVIDER: + if (row == indexes[0]) + ranges[0] = getMax(ranges[0], hardRanges[0] + (*fditr)->mMinExtent.y); + if (row == indexes[1]) + ranges[1] = getMin(ranges[1], hardRanges[1] - (*fditr)->mMinExtent.y); + break; + case DIVIDER_INTERSECTION: + if (column == indexes[0]) + ranges[0] = getMax(ranges[0], hardRanges[0] + (*fditr)->mMinExtent.x); + if (column == indexes[1]) + ranges[1] = getMin(ranges[1], hardRanges[1] - (*fditr)->mMinExtent.x); + if (row == indexes[2]) + ranges[2] = getMax(ranges[2], hardRanges[2] + (*fditr)->mMinExtent.y); + if (row == indexes[3]) + ranges[3] = getMin(ranges[3], hardRanges[3] - (*fditr)->mMinExtent.y); + break; + default: + return; + } + } + column++; + } + } +} + +//----------------------------------------------------------------------------- +void GuiFrameSetCtrl::drawDividers(const Point2I &offset) +{ + // draw the frame dividers, if they are enabled + if (mFramesetDetails.mBorderEnable != FRAME_STATE_OFF) + { + RectI r; + Vector::iterator itr; + for (itr = mColumnOffsets.begin() + 1; itr < mColumnOffsets.end(); itr++) + { + r.point = Point2I(*itr - mFramesetDetails.mBorderWidth, mFudgeFactor) + offset; + r.extent.set(mFramesetDetails.mBorderWidth, getHeight() - ( 2 * mFudgeFactor ) ); + GFX->getDrawUtil()->drawRectFill(r, mFramesetDetails.mBorderColor); + } + for (itr = mRowOffsets.begin() + 1; itr < mRowOffsets.end(); itr++) + { + r.point = Point2I(mFudgeFactor, *itr - mFramesetDetails.mBorderWidth) + offset; + r.extent.set(getWidth() - ( 2 * mFudgeFactor ), mFramesetDetails.mBorderWidth); + GFX->getDrawUtil()->drawRectFill(r, mFramesetDetails.mBorderColor); + } + } +} + +//----------------------------------------------------------------------------- +void GuiFrameSetCtrl::frameBorderEnable(S32 index, const char *state) +{ + GuiControl *gc = NULL; + FrameDetail *fd = NULL; + if (findFrameContents(index, &gc, &fd) == true && fd != NULL) + { + if (state != NULL) + fd->mBorderEnable = EngineUnmarshallData< FrameState >()( state ); + else + // defaults to AUTO if NULL passed in state + fd->mBorderEnable = FRAME_STATE_AUTO; + } +} + +//----------------------------------------------------------------------------- +void GuiFrameSetCtrl::frameBorderMovable(S32 index, const char *state) +{ + GuiControl *gc = NULL; + FrameDetail *fd = NULL; + if (findFrameContents(index, &gc, &fd) == true && fd != NULL) + { + if (state != NULL) + fd->mBorderMovable = EngineUnmarshallData< FrameState >()( state ); + else + // defaults to AUTO if NULL passed in state + fd->mBorderMovable = FRAME_STATE_AUTO; + } +} + +//----------------------------------------------------------------------------- +void GuiFrameSetCtrl::frameMinExtent(S32 index, const Point2I &extent) +{ + GuiControl *gc = NULL; + FrameDetail *fd = NULL; + if (findFrameContents(index, &gc, &fd) == true && fd != NULL) + fd->mMinExtent = extent; +} + +void GuiFrameSetCtrl::framePadding(S32 index, const RectSpacingI &padding) +{ + GuiControl *gc = NULL; + FrameDetail *fd = NULL; + if (findFrameContents(index, &gc, &fd) == true && fd != NULL) + fd->mPadding = padding; +} + +RectSpacingI GuiFrameSetCtrl::getFramePadding(S32 index) +{ + GuiControl *gc = NULL; + FrameDetail *fd = NULL; + if (findFrameContents(index, &gc, &fd) == true && fd != NULL) + return fd->mPadding; + + return RectSpacingI( 0, 0, 0, 0 ); +} \ No newline at end of file diff --git a/Engine/source/gui/containers/guiFrameCtrl.h b/Engine/source/gui/containers/guiFrameCtrl.h new file mode 100644 index 000000000..682946198 --- /dev/null +++ b/Engine/source/gui/containers/guiFrameCtrl.h @@ -0,0 +1,191 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIFRAMECTRL_H_ +#define _GUIFRAMECTRL_H_ + +#ifndef _GUICONTAINER_H_ + #include "gui/containers/guiContainer.h" +#endif + + +// for debugging porpoises... +#define GUI_FRAME_DEBUG +// ...save the porpoises + + +/// A gui control allowing a window to be subdivided into panes, +/// each of which displays a gui control child of the +/// GuiFrameSetCtrl. Each gui control child will have an associated +/// FrameDetail through which frame-specific details can be +/// assigned. Frame-specific values override the values specified +/// for the entire frameset. +/// +/// Note that it is possible to have more children than frames, +/// or more frames than children. In the former case, the extra +/// children will not be visible (they are moved beyond the +/// visible extent of the frameset). In the latter case, frames +/// will be empty. +/// +/// If a frameset had two columns and two rows but only three +/// gui control children they would be assigned to frames as +/// follows: +/// 1 | 2 +/// ----- +/// 3 | +/// +/// The last frame would be blank. +/// +class GuiFrameSetCtrl : public GuiContainer +{ +private: + typedef GuiContainer Parent; +public: + enum FrameState + { + FRAME_STATE_ON, // ON overrides OFF + FRAME_STATE_OFF, // OFF overrides AUTO + FRAME_STATE_AUTO, // AUTO == ON, unless overridden + + NO_HIT = -1, + + DEFAULT_BORDER_WIDTH = 4, + DEFAULT_COLUMNS = 1, + DEFAULT_ROWS = 1, + DEFAULT_MIN_FRAME_EXTENT = 64 + }; + enum Region + { + VERTICAL_DIVIDER, + HORIZONTAL_DIVIDER, + DIVIDER_INTERSECTION, + NONE + }; + struct FrameDetail + { + U32 mBorderWidth; + ColorI mBorderColor; + S32 mBorderEnable; + S32 mBorderMovable; + Point2I mMinExtent; + RectSpacingI mPadding; + FrameDetail() { mBorderWidth = DEFAULT_BORDER_WIDTH; mBorderEnable = FRAME_STATE_AUTO; mBorderMovable = FRAME_STATE_AUTO; mMinExtent.set(DEFAULT_MIN_FRAME_EXTENT, DEFAULT_MIN_FRAME_EXTENT); mPadding.setAll( 0 ); } + }; + DECLARE_CONOBJECT(GuiFrameSetCtrl); + DECLARE_DESCRIPTION( "A container that allows to subdivide its space into rows and columns.\n" + "Child controls are assigned to the cells row by row." ); + static void initPersistFields(); + + GuiFrameSetCtrl(); + GuiFrameSetCtrl(U32 columns, U32 rows, const U32 columnOffsets[] = NULL, const U32 rowOffsets[] = NULL); + virtual ~GuiFrameSetCtrl(); + + void addObject(SimObject *obj); + void removeObject(SimObject *obj); + + virtual bool resize(const Point2I &newPosition, const Point2I &newExtent); + + virtual void onMouseDown(const GuiEvent &event); + virtual void onMouseUp(const GuiEvent &event); + virtual void onMouseDragged(const GuiEvent &event); + + bool onAdd(); + void onRender(Point2I offset, const RectI &updateRect ); +protected: + /* member variables */ + Vector mColumnOffsets; + Vector mRowOffsets; + GuiCursor *mMoveCursor; + GuiCursor *mUpDownCursor; + GuiCursor *mLeftRightCursor; + GuiCursor *mDefaultCursor; + FrameDetail mFramesetDetails; + VectorPtr mFrameDetails; + bool mAutoBalance; + S32 mFudgeFactor; + + /* divider activation member variables */ + Region mCurHitRegion; + Point2I mLocOnDivider; + S32 mCurVerticalHit; + S32 mCurHorizontalHit; + + bool init(U32 columns, U32 rows, const U32 columnOffsets[], const U32 rowOffsets[]); + + Region findHitRegion(const Point2I &point); + Region pointInAnyRegion(const Point2I &point); + S32 findResizableFrames(S32 indexes[]); + bool hitVerticalDivider(S32 x, const Point2I &point); + bool hitHorizontalDivider(S32 y, const Point2I &point); + + virtual void getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent); + void rebalance(const Point2I &newExtent); + + void computeSizes(bool balanceFrames = false); + void computeMovableRange(Region hitRegion, S32 vertHit, S32 horzHit, S32 numIndexes, const S32 indexes[], S32 ranges[]); + + void drawDividers(const Point2I &offset); +public: + U32 columns() const { return(mColumnOffsets.size()); } + U32 rows() const { return(mRowOffsets.size()); } + U32 borderWidth() const { return(mFramesetDetails.mBorderWidth); } + Vector* columnOffsets() { return(&mColumnOffsets); } + Vector* rowOffsets() { return(&mRowOffsets); } + FrameDetail* framesetDetails() { return(&mFramesetDetails); } + + bool findFrameContents(S32 index, GuiControl **gc, FrameDetail **fd); + + void frameBorderEnable(S32 index, const char *state = NULL); + void frameBorderMovable(S32 index, const char *state = NULL); + void frameMinExtent(S32 index, const Point2I &extent); + void framePadding(S32 index, const RectSpacingI &padding); + RectSpacingI getFramePadding(S32 index); + + void balanceFrames() { computeSizes(true); } + void updateSizes() { computeSizes(); } + + bool onWake(); + +private: + GuiFrameSetCtrl(const GuiFrameSetCtrl &); + GuiFrameSetCtrl& operator=(const GuiFrameSetCtrl &); +}; + +typedef GuiFrameSetCtrl::FrameState GuiFrameState; +DefineEnumType( GuiFrameState ); + +//----------------------------------------------------------------------------- +// x is the first value inside the next column, so the divider x-coords +// precede x. +inline bool GuiFrameSetCtrl::hitVerticalDivider(S32 x, const Point2I &point) +{ + return((point.x >= S32(x - mFramesetDetails.mBorderWidth)) && (point.x < x) && (point.y >= 0) && (point.y < S32(getHeight()))); +} + +//----------------------------------------------------------------------------- +// y is the first value inside the next row, so the divider y-coords precede y. +inline bool GuiFrameSetCtrl::hitHorizontalDivider(S32 y, const Point2I &point) +{ + return((point.x >= 0) && (point.x < S32(getWidth())) && (point.y >= S32(y - mFramesetDetails.mBorderWidth)) && (point.y < y)); +} + +#endif // _GUI_FRAME_CTRL_H diff --git a/Engine/source/gui/containers/guiPaneCtrl.cpp b/Engine/source/gui/containers/guiPaneCtrl.cpp new file mode 100644 index 000000000..1637b0b30 --- /dev/null +++ b/Engine/source/gui/containers/guiPaneCtrl.cpp @@ -0,0 +1,396 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/engineAPI.h" +#include "platform/platform.h" +#include "gui/containers/guiPaneCtrl.h" + +#include "gfx/gfxDrawUtil.h" + + +IMPLEMENT_CONOBJECT(GuiPaneControl); + +ConsoleDocClass( GuiPaneControl, + "@brief A collapsable pane control.\n\n" + + "This class wraps a single child control and displays a header with caption " + "above it. If you click the header it will collapse or expand (if collapsable " + "is enabled). The control resizes itself based on its collapsed/expanded size.
      " + + "In the GUI editor, if you just want the header you can make collapsable " + "false. The caption field lets you set the caption; it expects a bitmap (from " + "the GuiControlProfile) that contains two images - the first is displayed when " + "the control is expanded and the second is displayed when it is collapsed. The " + "header is sized based on the first image.\n\n" + + "@tsexample\n" + "new GuiPaneControl()\n" + "{\n" + " caption = \"Example Pane\";\n" + " collapsable = \"1\";\n" + " barBehindText = \"1\";\n" + " //Properties not specific to this control have been omitted from this example.\n" + "};\n" + "@endtsexample\n\n" + + "@ingroup GuiContainers" +); + +//----------------------------------------------------------------------------- + +GuiPaneControl::GuiPaneControl() +{ + setMinExtent(Point2I(16,16)); + + mActive = true; + mCollapsable = true; + mCollapsed = false; + mBarBehindText = true; + mMouseOver = false; + mDepressed = false; + mCaption = "A Pane"; + mCaptionID = StringTable->insert(""); + mIsContainer = true; + + mOriginalExtents.set(10,10); +} + +//----------------------------------------------------------------------------- + +void GuiPaneControl::initPersistFields() +{ + addGroup( "Pane" ); + + addField("caption", TypeRealString, Offset(mCaption, GuiPaneControl), + "Text label to display as the pane header." ); + addField("captionID", TypeString, Offset(mCaptionID, GuiPaneControl), + "String table text ID to use as caption string (overrides 'caption')." ); + addField("collapsable", TypeBool, Offset(mCollapsable, GuiPaneControl), + "Whether the pane can be collapsed by clicking its header." ); + addField("barBehindText", TypeBool, Offset(mBarBehindText, GuiPaneControl), + "Whether to draw the bitmapped pane bar behind the header text, too." ); + + endGroup( "Pane" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +bool GuiPaneControl::onWake() +{ + if ( !Parent::onWake() ) + return false; + + if( !mProfile->mFont ) + { + Con::errorf( "GuiPaneControl::onWake - profile has no valid font" ); + return false; + } + + if(mCaptionID && *mCaptionID != 0) + setCaptionID(mCaptionID); + + mProfile->constructBitmapArray(); + if(mProfile->mUseBitmapArray && mProfile->mBitmapArrayRects.size()) + { + mThumbSize.set( mProfile->mBitmapArrayRects[0].extent.x, mProfile->mBitmapArrayRects[0].extent.y ); + mThumbSize.setMax( mProfile->mBitmapArrayRects[1].extent ); + + if( mProfile->mFont->getHeight() > mThumbSize.y ) + mThumbSize.y = mProfile->mFont->getHeight(); + } + else + { + mThumbSize.set(20, 20); + } + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiPaneControl::setCaptionID(const char *id) +{ + S32 n = Con::getIntVariable(id, -1); + if(n != -1) + { + mCaptionID = StringTable->insert(id); + setCaptionID(n); + } +} + +//----------------------------------------------------------------------------- + +void GuiPaneControl::setCaptionID(S32 id) +{ + mCaption = getGUIString(id); +} + +//----------------------------------------------------------------------------- + +bool GuiPaneControl::resize(const Point2I &newPosition, const Point2I &newExtent) +{ + // CodeReview WTF is going on here that we need to bypass parent sanity? + // Investigate this [7/1/2007 justind] + if( !Parent::resize( newPosition, newExtent ) ) + return false; + + mOriginalExtents.x = getWidth(); + + /* + GuiControl *parent = getParent(); + if (parent) + parent->childResized(this); + setUpdate(); + */ + + // Resize the child control if we're not collapsed + if(size() && !mCollapsed) + { + GuiControl *gc = dynamic_cast(operator[](0)); + + if(gc) + { + Point2I offset(0, mThumbSize.y); + + gc->resize(offset, newExtent - offset); + } + } + + // For now. + return true; +} + +//----------------------------------------------------------------------------- + +void GuiPaneControl::onRender(Point2I offset, const RectI &updateRect) +{ + // Render our awesome little doogong + if(mProfile->mBitmapArrayRects.size() >= 2 && mCollapsable) + { + S32 idx = mCollapsed ? 0 : 1; + + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapStretchSR( + mProfile->mTextureObject, + RectI(offset, mProfile->mBitmapArrayRects[idx].extent), + mProfile->mBitmapArrayRects[idx] + ); + + } + + S32 textWidth = 0; + + if(!mBarBehindText) + { + GFX->getDrawUtil()->setBitmapModulation((mMouseOver ? mProfile->mFontColorHL : mProfile->mFontColor)); + textWidth = GFX->getDrawUtil()->drawText( + mProfile->mFont, + Point2I(mThumbSize.x, 0) + offset, + mCaption, + mProfile->mFontColors + ); + } + + + // Draw our little bar, too + if(mProfile->mBitmapArrayRects.size() >= 5) + { + GFX->getDrawUtil()->clearBitmapModulation(); + + S32 barStart = mThumbSize.x + offset.x + textWidth; + S32 barTop = mThumbSize.y/2 + offset.y - mProfile->mBitmapArrayRects[3].extent.y /2; + + Point2I barOffset(barStart, barTop); + + // Draw the start of the bar... + GFX->getDrawUtil()->drawBitmapStretchSR( + mProfile->mTextureObject, + RectI(barOffset, mProfile->mBitmapArrayRects[2].extent), + mProfile->mBitmapArrayRects[2] + ); + + // Now draw the middle... + barOffset.x += mProfile->mBitmapArrayRects[2].extent.x; + + S32 barMiddleSize = (getExtent().x - (barOffset.x - offset.x)) - mProfile->mBitmapArrayRects[4].extent.x; + + if(barMiddleSize>0) + { + // We have to do this inset to prevent nasty stretching artifacts + RectI foo = mProfile->mBitmapArrayRects[3]; + foo.inset(1,0); + + GFX->getDrawUtil()->drawBitmapStretchSR( + mProfile->mTextureObject, + RectI(barOffset, Point2I(barMiddleSize, mProfile->mBitmapArrayRects[3].extent.y)), + foo + ); + } + + // And the end + barOffset.x += barMiddleSize; + + GFX->getDrawUtil()->drawBitmapStretchSR( + mProfile->mTextureObject, + RectI(barOffset, mProfile->mBitmapArrayRects[4].extent), + mProfile->mBitmapArrayRects[4] + ); + } + + if(mBarBehindText) + { + GFX->getDrawUtil()->setBitmapModulation((mMouseOver ? mProfile->mFontColorHL : mProfile->mFontColor)); + GFX->getDrawUtil()->drawText( + mProfile->mFont, + Point2I(mThumbSize.x, 0) + offset, + mCaption, + mProfile->mFontColors + ); + } + + // Draw child controls if appropriate + if(!mCollapsed) + renderChildControls(offset, updateRect); +} + +//----------------------------------------------------------------------------- + +void GuiPaneControl::setCollapsed(bool isCollapsed) +{ + // Get the child + if(size() == 0 || !mCollapsable) return; + + GuiControl *gc = dynamic_cast(operator[](0)); + + if(mCollapsed && !isCollapsed) + { + resize(getPosition(), mOriginalExtents); + mCollapsed = false; + + if(gc) + gc->setVisible(true); + } + else if(!mCollapsed && isCollapsed) + { + mCollapsed = true; + + mOriginalExtents = getExtent(); + resize(getPosition(), Point2I(getExtent().x, mThumbSize.y)); + + if(gc) + gc->setVisible(false); + } +} + +//----------------------------------------------------------------------------- + +void GuiPaneControl::onMouseMove(const GuiEvent &event) +{ + Point2I localMove = globalToLocalCoord(event.mousePoint); + + // If we're clicking in the header then resize + mMouseOver = (localMove.y < mThumbSize.y); + if(isMouseLocked()) + mDepressed = mMouseOver; + +} + +//----------------------------------------------------------------------------- + +void GuiPaneControl::onMouseEnter(const GuiEvent &event) +{ + setUpdate(); + if(isMouseLocked()) + { + mDepressed = true; + mMouseOver = true; + } + else + { + mMouseOver = true; + } + +} + +//----------------------------------------------------------------------------- + +void GuiPaneControl::onMouseLeave(const GuiEvent &event) +{ + setUpdate(); + if(isMouseLocked()) + mDepressed = false; + mMouseOver = false; +} + +//----------------------------------------------------------------------------- + +void GuiPaneControl::onMouseDown(const GuiEvent &event) +{ + if(!mCollapsable) + return; + + Point2I localClick = globalToLocalCoord(event.mousePoint); + + // If we're clicking in the header then resize + if(localClick.y < mThumbSize.y) + { + mouseLock(); + mDepressed = true; + + //update + setUpdate(); + } +} + +//----------------------------------------------------------------------------- + +void GuiPaneControl::onMouseUp(const GuiEvent &event) +{ + // Make sure we only get events we ought to be getting... + if (! mActive) + return; + + if(!mCollapsable) + return; + + mouseUnlock(); + setUpdate(); + + Point2I localClick = globalToLocalCoord(event.mousePoint); + + // If we're clicking in the header then resize + if(localClick.y < mThumbSize.y && mDepressed) + setCollapsed(!mCollapsed); +} + +//============================================================================= +// Console Methods. +//============================================================================= + +DefineEngineMethod( GuiPaneControl, setCollapsed, void, ( bool collapse ),, + "Collapse or un-collapse the control.\n\n" + "@param collapse True to collapse the control, false to un-collapse it\n" ) +{ + object->setCollapsed( collapse ); +} diff --git a/Engine/source/gui/containers/guiPaneCtrl.h b/Engine/source/gui/containers/guiPaneCtrl.h new file mode 100644 index 000000000..aaec12d29 --- /dev/null +++ b/Engine/source/gui/containers/guiPaneCtrl.h @@ -0,0 +1,120 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIPANECTRL_H_ +#define _GUIPANECTRL_H_ + +#ifndef _GUICONTROL_H_ + #include "gui/core/guiControl.h" +#endif +#ifndef _GFXDEVICE_H_ + #include "gfx/gfxDevice.h" +#endif +#ifndef _CONSOLE_H_ + #include "console/console.h" +#endif +#ifndef _CONSOLETYPES_H_ + #include "console/consoleTypes.h" +#endif + + +/// Collapsable pane control. +/// +/// This class wraps a single child control and displays a header with caption +/// above it. If you click the header it will collapse or expand. The control +/// resizes itself based on its collapsed/expanded size. +/// +/// In the GUI editor, if you just want the header you can make collapsable +/// false. The caption field lets you set the caption. It expects a bitmap +/// (from the GuiControlProfile) that contains two images - the first is +/// displayed when the control is expanded and the second is displayed when +/// it is collapsed. The header is sized based off of the first image. +class GuiPaneControl : public GuiControl +{ + public: + + typedef GuiControl Parent; + + protected: + + /// If true, the pane can be collapsed and extended by clicking its header. + bool mCollapsable; + + /// Whether the pane is currently collapsed. + bool mCollapsed; + + /// Whether to display the bitmapped pane bar behind the header text, too. + bool mBarBehindText; + + /// + Point2I mOriginalExtents; + + /// Text to display as the pane caption. + String mCaption; + + /// String table text ID to use as caption string. + StringTableEntry mCaptionID; + + /// + Point2I mThumbSize; + + /// + bool mMouseOver; + + /// + bool mDepressed; + + public: + + GuiPaneControl(); + + /// Return whether the pane is currently collapsed. + bool getCollapsed() { return mCollapsed; }; + + /// Collapse or expand the pane. + void setCollapsed(bool isCollapsed); + + virtual void setCaptionID(S32 id); + virtual void setCaptionID(const char *id); + + // GuiControl. + virtual bool onWake(); + + virtual void onMouseDown(const GuiEvent &event); + virtual void onMouseUp(const GuiEvent &event); + virtual void onMouseMove(const GuiEvent &event); + virtual void onMouseLeave(const GuiEvent &event); + virtual void onMouseEnter(const GuiEvent &event); + + virtual bool resize(const Point2I &newPosition, const Point2I &newExtent); + virtual void onRender(Point2I offset, const RectI &updateRect); + + static void initPersistFields(); + + DECLARE_CONOBJECT(GuiPaneControl); + DECLARE_CATEGORY( "Gui Containers" ); + DECLARE_DESCRIPTION( "A container that wraps a single child control displaying a header\n" + "with a caption above it. If enabled, the pane can be collapsed and expanded\n" + "by clicking the header." ); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/gui/containers/guiPanel.cpp b/Engine/source/gui/containers/guiPanel.cpp new file mode 100644 index 000000000..40b1de72f --- /dev/null +++ b/Engine/source/gui/containers/guiPanel.cpp @@ -0,0 +1,113 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/containers/guiPanel.h" + +#include "console/consoleTypes.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxDrawUtil.h" + + +//----------------------------------------------------------------------------- +// GuiPanel +//----------------------------------------------------------------------------- + +ConsoleDocClass( GuiPanel, + "@brief The GuiPanel panel is a container that when opaque will " + "draw a left to right gradient using its profile fill and " + "fill highlight colors.\n\n" + + "@tsexample\n" + "// Mandatory GuiDefaultProfile\n" + "// Contains the fill color information required by a GuiPanel\n" + "// Some values left out for sake of this example\n" + "new GuiControlProfile (GuiDefaultProfile)\n" + "{\n" + " // fill color\n" + " opaque = false;\n" + " fillColor = \"242 241 240\";\n" + " fillColorHL =\"228 228 235\";\n" + " fillColorSEL = \"98 100 137\";\n" + " fillColorNA = \"255 255 255 \";\n" + "};\n\n" + "new GuiPanel(TestPanel)\n" + "{\n" + " position = \"45 33\";\n" + " extent = \"342 379\";\n" + " minExtent = \"16 16\";\n" + " horizSizing = \"right\";\n" + " vertSizing = \"bottom\";\n" + " profile = \"GuiDefaultProfile\"; // Color fill info is in this profile\n" + " isContainer = \"1\";\n" + "};\n" + "@endtsexample\n\n" + + "@see GuiControlProfile\n" + + "@ingroup GuiContainers\n"); + +GuiPanel::GuiPanel() +{ + setMinExtent( Point2I( 16,16 ) ); + setDocking( Docking::dockNone ); + + mIsContainer = true; +} + +GuiPanel::~GuiPanel() +{ +} + +IMPLEMENT_CONOBJECT(GuiPanel); + + +void GuiPanel::onRender(Point2I offset, const RectI &updateRect) +{ + if ( !mProfile->mOpaque ) + { + RectI ctrlRect = getClientRect(); + ctrlRect.point += offset; + + // Draw border. + + if( mProfile->mBorder != 0 ) + { + GFX->getDrawUtil()->drawRectFill( ctrlRect, mProfile->mBorderColor ); + ctrlRect.inset( mProfile->mBorderThickness, mProfile->mBorderThickness ); + } + + // Draw a gradient left to right. + + PrimBuild::begin( GFXTriangleStrip, 4 ); + PrimBuild::color( mProfile->mFillColorHL ); + PrimBuild::vertex2i( ctrlRect.point.x, ctrlRect.point.y ); + PrimBuild::vertex2i( ctrlRect.point.x, ctrlRect.point.y + ctrlRect.extent.y ); + + PrimBuild::color( mProfile->mFillColor ); + PrimBuild::vertex2i( ctrlRect.point.x + ctrlRect.extent.x, ctrlRect.point.y); + PrimBuild::vertex2i( ctrlRect.point.x + ctrlRect.extent.x, ctrlRect.point.y + ctrlRect.extent.y ); + PrimBuild::end(); + } + + Parent::onRender( offset, updateRect ); +} \ No newline at end of file diff --git a/Engine/source/gui/containers/guiPanel.h b/Engine/source/gui/containers/guiPanel.h new file mode 100644 index 000000000..f0806b778 --- /dev/null +++ b/Engine/source/gui/containers/guiPanel.h @@ -0,0 +1,62 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _GUI_PANEL_H_ +#define _GUI_PANEL_H_ + +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif +#ifndef _GUITICKCTRL_H_ +#include "gui/shiny/guiTickCtrl.h" +#endif +#ifndef _GUICONTAINER_H_ +#include "gui/containers/guiContainer.h" +#endif + + +/// The GuiPanel panel is a container that when opaque will +/// draw a left to right gradient using its profile fill and +/// fill highlight colors. +/// +/// @addtogroup gui_container_group Containers +/// +/// @ingroup gui_group Gui System +/// @{ +class GuiPanel : public GuiContainer +{ +private: + typedef GuiContainer Parent; + +public: + // Constructor/Destructor/ConObject Declaration + GuiPanel(); + virtual ~GuiPanel(); + + DECLARE_CONOBJECT(GuiPanel); + + // GuiControl + void onRender(Point2I offset, const RectI &updateRect); + void setVisible(bool value) { Parent::setVisible(value); setUpdateLayout( updateParent ); } +}; +/// @} + +#endif // _GUI_PANEL_H_ \ No newline at end of file diff --git a/Engine/source/gui/containers/guiRolloutCtrl.cpp b/Engine/source/gui/containers/guiRolloutCtrl.cpp new file mode 100644 index 000000000..9044a1aaf --- /dev/null +++ b/Engine/source/gui/containers/guiRolloutCtrl.cpp @@ -0,0 +1,650 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/containers/guiRolloutCtrl.h" +#include "gui/containers/guiScrollCtrl.h" +#include "gfx/gfxDrawUtil.h" +#include "console/engineAPI.h" + + +IMPLEMENT_CONOBJECT( GuiRolloutCtrl ); + +ConsoleDocClass( GuiRolloutCtrl, + "@brief A container that shows a single child with an optional header bar that can be used to collapse and expand the rollout.\n\n" + + "A rollout is a container that can be collapsed and expanded using smooth animation. By default, rollouts will display a header " + "with a caption along the top edge of the control which can be clicked by the user to toggle the collapse state of the rollout.\n\n" + + "Rollouts will automatically size themselves to exactly fit around their child control. They will also automatically position their child " + "control in their upper left corner below the header (if present).\n\n" + + "@note GuiRolloutCtrls will only work correctly with a single child control. To put multiple controls in a rollout, put them " + "in their own group using a new GuiControl which then can be put inside the rollout.\n\n" + + "@ingroup GuiContainers" +); + +IMPLEMENT_CALLBACK( GuiRolloutCtrl, onHeaderRightClick, void, (), (), + "Called when the user right-clicks on the rollout's header. This is useful for implementing " + "context menus for rollouts." ); + +IMPLEMENT_CALLBACK( GuiRolloutCtrl, onExpanded, void, (), (), + "Called when the rollout is expanded." ); + +IMPLEMENT_CALLBACK( GuiRolloutCtrl, onCollapsed, void, (), (), + "Called when the rollout is collapsed." ); + +//----------------------------------------------------------------------------- + +GuiRolloutCtrl::GuiRolloutCtrl() +{ + mExpanded.set(0,0,200,60); + mCaption = StringTable->EmptyString(); + mIsExpanded = true; + mIsAnimating = false; + mCollapsing = false; + mAnimateDestHeight = 40; + mAnimateStep = 1; + mDefaultHeight = 40; + mHideHeader = false; + mMargin.set( 0, 0, 0, 0 ); + mIsContainer = true; + mCanCollapse = true; + mAutoCollapseSiblings = false; + // Make sure we receive our ticks. + setProcessTicks(); +} + +//----------------------------------------------------------------------------- + +GuiRolloutCtrl::~GuiRolloutCtrl() +{ +} + +//----------------------------------------------------------------------------- + +void GuiRolloutCtrl::initPersistFields() +{ + addGroup( "Rollout" ); + + addField( "caption", TypeRealString, Offset( mCaption, GuiRolloutCtrl ), + "Text label to display on the rollout header." ); + addField( "margin", TypeRectI, Offset( mMargin, GuiRolloutCtrl ), + "Margin to put around child control." ); + addField( "defaultHeight", TypeS32, Offset( mDefaultHeight, GuiRolloutCtrl ), + "Default height of the client area. This is used when no child control has been added to the rollout." ); + addProtectedField( "expanded", TypeBool, Offset( mIsExpanded, GuiRolloutCtrl), &setExpanded, &defaultProtectedGetFn, + "The current rollout expansion state." ); + addField( "clickCollapse", TypeBool, Offset( mCanCollapse, GuiRolloutCtrl ), + "Whether the rollout can be collapsed by clicking its header." ); + addField( "hideHeader", TypeBool, Offset( mHideHeader, GuiRolloutCtrl ), + "Whether to render the rollout header.\n\n" + "@note If this is false, the user cannot toggle the rollout state with the mouse." ); + addField( "autoCollapseSiblings", TypeBool, Offset( mAutoCollapseSiblings, GuiRolloutCtrl ), + "Whether to automatically collapse sibling rollouts.\n\n" + "If this is true, the rollout will automatically collapse all sibling rollout controls when it " + "is expanded. If this is false, the auto-collapse behavior can be triggered by CTRL (CMD on MAC) " + "clicking the rollout header. CTRL/CMD clicking also works if this is false, in which case the " + "auto-collapsing of sibling controls will be temporarily deactivated." ); + + endGroup( "Rollout" ); + + Parent::initPersistFields(); +} + +//============================================================================= +// Events. +//============================================================================= +// MARK: ---- Events ---- + +//----------------------------------------------------------------------------- + +bool GuiRolloutCtrl::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + mHasTexture = ( mProfile ? mProfile->constructBitmapArray() > 0 : false ); + if ( mHasTexture ) + mBitmapBounds = mProfile->mBitmapArrayRects.address(); + + // Calculate Heights for this control + calculateHeights(); + + return true; +} + +//----------------------------------------------------------------------------- + +bool GuiRolloutCtrl::onWake() +{ + if (! Parent::onWake()) + return false; + + if( !mIsAnimating && mIsExpanded ) + sizeToContents(); + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiRolloutCtrl::addObject( SimObject *obj ) +{ + // Call Parent. + Parent::addObject( obj ); + + sizeToContents(); +} + +//----------------------------------------------------------------------------- + +void GuiRolloutCtrl::removeObject( SimObject *obj ) +{ + // Call Parent. + Parent::removeObject( obj ); + + // Recalculate our rectangles. + calculateHeights(); +} + +//----------------------------------------------------------------------------- + +void GuiRolloutCtrl::onMouseDown( const GuiEvent &event ) +{ + Point2I localPoint = globalToLocalCoord( event.mousePoint ); + mouseLock(); +} + +//----------------------------------------------------------------------------- + +bool GuiRolloutCtrl::_onMouseUp( const GuiEvent &event, bool lockedMouse ) +{ + Point2I localPoint = globalToLocalCoord( event.mousePoint ); + if( mCanCollapse && mHeader.pointInRect( localPoint ) && !mIsAnimating && ( !lockedMouse || isMouseLocked() ) ) + { + // If Ctrl/Cmd-clicking a header, collapse all sibling GuiRolloutCtrls. + + if( ( mAutoCollapseSiblings && !mIsExpanded && !( event.modifier & SI_PRIMARY_CTRL ) + || ( !mAutoCollapseSiblings && event.modifier & SI_PRIMARY_CTRL ) ) ) + { + for( SimSet::iterator iter = getParent()->begin(); iter != getParent()->end(); ++ iter ) + { + GuiRolloutCtrl* ctrl = dynamic_cast< GuiRolloutCtrl* >( *iter ); + if( ctrl && ctrl != this && ctrl->mCanCollapse ) + ctrl->instantCollapse(); + } + + if( !mIsExpanded ) + expand(); + } + else + { + // Toggle expansion. + + toggleExpanded( false ); + } + + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- + +void GuiRolloutCtrl::onMouseUp( const GuiEvent &event ) +{ + _onMouseUp( event, true ); + if( isMouseLocked() ) + mouseUnlock(); +} + +//----------------------------------------------------------------------------- + +void GuiRolloutCtrl::onRightMouseUp( const GuiEvent& event ) +{ + Parent::onRightMouseUp( event ); + + Point2I localMouse = globalToLocalCoord( event.mousePoint ); + if( mHeader.pointInRect( localMouse ) ) + onHeaderRightClick_callback(); +} + +//----------------------------------------------------------------------------- + +bool GuiRolloutCtrl::onMouseUpEditor( const GuiEvent& event, Point2I offset ) +{ + return ( event.modifier & SI_PRIMARY_ALT && _onMouseUp( event, false ) ); +} + +//============================================================================= +// Sizing. +//============================================================================= +// MARK: ---- Sizing ---- + +//----------------------------------------------------------------------------- + +void GuiRolloutCtrl::calculateHeights() +{ + S32 barHeight = 20; + + if ( mHasTexture && mProfile && mProfile->mBitmapArrayRects.size() >= NumBitmaps ) + { + // Store Header Rectangle + mHeader.set( 0, 0, getWidth(), mProfile->mBitmapArrayRects[ CollapsedCenter ].extent.y ); + + // Bottom Bar Max + barHeight = mProfile->mBitmapArrayRects[ TopLeftHeader ].extent.y; + } + else + { + mHeader.set( 0, 0, getWidth(), barHeight ); + } + + if ( mHideHeader ) + { + barHeight = 0; + mHeader.extent.y = 0; + } + + GuiControl *content = static_cast( at(0) ); + if ( content != NULL ) + mExpanded.set( 0, 0, getWidth(), barHeight + content->getHeight() + ( mMargin.point.y + mMargin.extent.y ) ); + else + mExpanded.set( 0, 0, getWidth(), barHeight + mDefaultHeight ); +} + +//----------------------------------------------------------------------------- + +bool GuiRolloutCtrl::resize( const Point2I &newPosition, const Point2I &newExtent ) +{ + if ( !Parent::resize( newPosition, newExtent ) ) + return false; + + // Recalculate Heights and resize ourself appropriately. + calculateHeights(); + + GuiControl *content = dynamic_cast( at(0) ); + + // Size Content Properly?! + if ( mNotifyChildrenResized && content != NULL ) + { + S32 barHeight = ( mHideHeader ) ? 0 : 20; + if( !mHideHeader && mHasTexture && mProfile && mProfile->mBitmapArrayRects.size() >= NumBitmaps ) + { + barHeight = mProfile->mBitmapArrayRects[ TopLeftHeader ].extent.y; + } + + mChildRect.set( mMargin.point.x, + mHeader.extent.y + mMargin.point.y, + getWidth() - ( mMargin.point.x + mMargin.extent.x ), + getHeight() - ( barHeight + ( mMargin.point.y + mMargin.extent.y ) ) ); + + if ( content->resize( mChildRect.point, mChildRect.extent ) ) + return true; + } + + // Nothing sized + return false; +} + +//----------------------------------------------------------------------------- + +void GuiRolloutCtrl::sizeToContents() +{ + calculateHeights(); + + // Set destination height + if ( size() > 0 ) + instantExpand(); + else + instantCollapse(); +} + +//----------------------------------------------------------------------------- + +void GuiRolloutCtrl::instantExpand() +{ + mAnimateDestHeight = mExpanded.extent.y; + mCollapsing = false; + mIsExpanded = true; + mIsAnimating = false; + resize( getPosition() + mExpanded.point, mExpanded.extent ); + + onExpanded_callback(); +} + +//----------------------------------------------------------------------------- + +void GuiRolloutCtrl::instantCollapse() +{ + mAnimateDestHeight = mHeader.extent.y; + mCollapsing = false; + mIsExpanded = false; + mIsAnimating = false; + resize( getPosition() + mHeader.point, mHeader.extent ); + + onCollapsed_callback(); +} + +//----------------------------------------------------------------------------- + +void GuiRolloutCtrl::toggleExpanded( bool instant ) +{ + if ( mIsExpanded ) + { + if ( instant ) + instantCollapse(); + else + collapse(); + } + else + { + if ( instant ) + instantExpand(); + else + expand(); + } +} + +//----------------------------------------------------------------------------- + +void GuiRolloutCtrl::childResized( GuiControl *child ) +{ + Parent::childResized( child ); + + calculateHeights(); + + // While we are animating we are constantly resizing our children + // and therefore need to ignore this call to 'instantExpand' which would + // halt the animation in some crappy intermediate stage. + if ( mIsExpanded && !mIsAnimating ) + { + mNotifyChildrenResized = false; + instantExpand(); + mNotifyChildrenResized = true; + } +} + +//============================================================================= +// Animation. +//============================================================================= +// MARK: ---- Animation ---- + +//----------------------------------------------------------------------------- + +void GuiRolloutCtrl::animateTo( S32 height ) +{ + // We do nothing if we're already animating + if( mIsAnimating ) + return; + + bool collapsing = (bool)( getHeight() > height ); + + // If we're already at the destination height, bail + if ( getHeight() >= height && !collapsing ) + { + mIsExpanded = true; + return; + } + + // If we're already at the destination height, bail + if ( getHeight() <= height && collapsing ) + { + mIsExpanded = false; + return; + } + + // Set destination height + mAnimateDestHeight = height; + + // Set Animation Mode + mCollapsing = collapsing; + + // Set Animation Step (Increment) + if ( collapsing ) + mAnimateStep = (S32)mFloor( (F32)( getHeight() - height ) / 3.f ); + else + mAnimateStep = (S32)mFloor( (F32)( height - getHeight() ) / 3.f ); + + // Start our animation + mIsAnimating = true; +} + +//----------------------------------------------------------------------------- + +void GuiRolloutCtrl::processTick() +{ + // We do nothing here if we're NOT animating + if ( !mIsAnimating ) + return; + + // Sanity check to fix non collapsing panels. + if ( mAnimateStep == 0 ) + mAnimateStep = 1; + + S32 newHeight = getHeight(); + // We're collapsing ourself down (Hiding our contents) + if( mCollapsing ) + { + if ( newHeight < mAnimateDestHeight ) + newHeight = mAnimateDestHeight; + else if ( ( newHeight - mAnimateStep ) < mAnimateDestHeight ) + newHeight = mAnimateDestHeight; + + if ( newHeight == mAnimateDestHeight ) + mIsAnimating = false; + else + newHeight -= mAnimateStep; + + if( !mIsAnimating ) + { + mIsExpanded = false; + } + } + else // We're expanding ourself (Showing our contents) + { + if ( newHeight > mAnimateDestHeight ) + newHeight = mAnimateDestHeight; + else if ( ( newHeight + mAnimateStep ) > mAnimateDestHeight ) + newHeight = mAnimateDestHeight; + + if ( newHeight == mAnimateDestHeight ) + mIsAnimating = false; + else + newHeight += mAnimateStep; + + if ( !mIsAnimating ) + mIsExpanded = true; + } + + if ( newHeight != getHeight() ) + setHeight( newHeight ); + + if ( !mIsAnimating ) + { + if( mCollapsing ) + onCollapsed_callback(); + else if( !mCollapsing ) + onExpanded_callback(); + + calculateHeights(); + } + + GuiControl* parent = getParent(); + if ( parent ) + { + parent->childResized( this ); + // if our parent's parent is a scroll control, scrollvisible. + GuiScrollCtrl* scroll = dynamic_cast( parent->getParent() ); + if ( scroll ) + { + scroll->scrollRectVisible( getBounds() ); + } + } +} + +//============================================================================= +// Rendering. +//============================================================================= +// MARK: ---- Rendering ---- + +//----------------------------------------------------------------------------- + +void GuiRolloutCtrl::onRender( Point2I offset, const RectI &updateRect ) +{ + if( !mProfile || mProfile->mFont == NULL ) + return; + + // Calculate actual world bounds for rendering + RectI worldBounds( offset, getExtent() ); + + // if opaque, fill the update rect with the fill color + if ( mProfile->mOpaque ) + GFX->getDrawUtil()->drawRectFill( worldBounds, mProfile->mFillColor ); + + if ( mProfile->mBitmapArrayRects.size() >= NumBitmaps ) + { + GFX->getDrawUtil()->clearBitmapModulation(); + + // Draw Rollout From Skin + if ( !mIsExpanded && !mIsAnimating ) + renderFixedBitmapBordersFilled( worldBounds, 1, mProfile ); + else if ( mHideHeader ) + renderSizableBitmapBordersFilledIndex( worldBounds, MidPageLeft, mProfile ); + else + renderSizableBitmapBordersFilledIndex( worldBounds, TopLeftHeader, mProfile ); + } + + if ( !(mIsExpanded && mHideHeader ) ) + { + // Draw Caption ( Vertically Centered ) + ColorI currColor; + GFX->getDrawUtil()->getBitmapModulation( &currColor ); + Point2I textPosition = mHeader.point + offset + mProfile->mTextOffset; + GFX->getDrawUtil()->setBitmapModulation( mProfile->mFontColor ); + renderJustifiedText( textPosition, mHeader.extent, mCaption ); + GFX->getDrawUtil()->setBitmapModulation( currColor ); + } + + // If we're collapsed we contain the first child as our content + // thus we don't render it when collapsed. but to support modified + // rollouts with custom header buttons etc we still render our other + // children. -JDD + GuiControl *pChild = dynamic_cast( at(0) ); + if ( pChild ) + { + if ( !mIsExpanded && !mIsAnimating && pChild->isVisible() ) + { + pChild->setVisible( false ); + } + else if ( (mIsExpanded || mIsAnimating) && !pChild->isVisible() ) + { + pChild->setVisible( true ); + } + } + renderChildControls( offset, updateRect ); + + // Render our border should we have it specified in our profile. + renderBorder(worldBounds, mProfile); +} + +//============================================================================= +// Console Methods. +//============================================================================= +// MARK: ---- Console Methods ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiRolloutCtrl, isExpanded, bool, (),, + "Determine whether the rollout is currently expanded, i.e. whether the child control is visible.\n\n" + "@return True if the rollout is expanded, false if not." ) +{ + return object->isExpanded(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiRolloutCtrl, collapse, void, (),, + "Collapse the rollout if it is currently expanded. This will make the rollout's child control invisible.\n\n" + "@note The rollout will animate to collapsed state. To instantly collapse without animation, use instantCollapse()." ) +{ + object->collapse(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiRolloutCtrl, expand, void, (),, + "Expand the rollout if it is currently collapsed. This will make the rollout's child control visible.\n\n" + "@note The rollout will animate to expanded state. To instantly expand without animation, use instantExpand()." ) +{ + object->expand(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiRolloutCtrl, toggleCollapse, void, (),, + "Toggle the current collapse state of the rollout. If it is currently expanded, then collapse it. If it " + "is currently collapsed, then expand it." ) +{ + if( object->isExpanded() ) + object->collapse(); + else + object->expand(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiRolloutCtrl, toggleExpanded, void, ( bool instantly ), ( false ), + "Toggle the current expansion state of the rollout If it is currently expanded, then collapse it. If it " + "is currently collapsed, then expand it.\n\n" + "@param instant If true, the rollout will toggle its state without animation. Otherwise, the rollout will " + "smoothly slide into the opposite state." ) +{ + object->toggleExpanded( instantly ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiRolloutCtrl, instantCollapse, void, (),, + "Instantly collapse the rollout without animation. To smoothly slide the rollout to collapsed state, use collapse()." ) +{ + object->instantCollapse(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiRolloutCtrl, instantExpand, void, (),, + "Instantly expand the rollout without animation. To smoothly slide the rollout to expanded state, use expand()." ) +{ + object->instantExpand(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiRolloutCtrl, sizeToContents, void, (),, + "Resize the rollout to exactly fit around its child control. This can be used to manually trigger a recomputation of " + "the rollout size." ) +{ + object->sizeToContents(); +} diff --git a/Engine/source/gui/containers/guiRolloutCtrl.h b/Engine/source/gui/containers/guiRolloutCtrl.h new file mode 100644 index 000000000..dc11ab3dc --- /dev/null +++ b/Engine/source/gui/containers/guiRolloutCtrl.h @@ -0,0 +1,179 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _GUI_ROLLOUTCTRL_H_ +#define _GUI_ROLLOUTCTRL_H_ + +#ifndef _GUICONTROL_H_ + #include "gui/core/guiControl.h" +#endif +#ifndef _GUISTACKCTRL_H_ + #include "gui/containers/guiStackCtrl.h" +#endif +#ifndef _H_GUIDEFAULTCONTROLRENDER_ + #include "gui/core/guiDefaultControlRender.h" +#endif +#ifndef _GUITICKCTRL_H_ + #include "gui/shiny/guiTickCtrl.h" +#endif + + +/// A container with an optional header that allows its child control to +/// be collapsed using an animated effet. +class GuiRolloutCtrl : public GuiTickCtrl +{ + public: + + typedef GuiControl Parent; + + // Theme Support + enum + { + CollapsedLeft = 0, + CollapsedCenter, + CollapsedRight, + TopLeftHeader, + TopMidHeader, + TopRightHeader, + MidPageLeft, + MidPageCenter, + MidPageRight, + BottomLeftHeader, + BottomMidHeader, + BottomRightHeader, + NumBitmaps ///< Number of bitmaps in this array + }; + + protected: + + /// Label to display on rollout header. + String mCaption; + + RectI mHeader; + RectI mExpanded; + RectI mChildRect; + RectI mMargin; + bool mIsExpanded; + bool mIsAnimating; + bool mCollapsing; + S32 mAnimateDestHeight; + S32 mAnimateStep; + S32 mDefaultHeight; + + /// Whether the rollout can be collapsed. + bool mCanCollapse; + + /// Whether to hide the rollout header. + bool mHideHeader; + + /// Whether to automatically collapse sibling rollouts when this one + /// is expanded. + bool mAutoCollapseSiblings; + + GuiCursor* mDefaultCursor; + GuiCursor* mVertSizingCursor; + + /// Indicates whether we have a texture to render the tabs with. + bool mHasTexture; + + /// Array of rectangles identifying textures for rollout. + RectI *mBitmapBounds; + + // Property - "Expanded" + static bool setExpanded( void *object, const char *index, const char *data ) + { + bool expand = dAtob( data ); + if( expand ) + static_cast(object)->instantExpand(); + else + static_cast(object)->instantCollapse(); + return false; + }; + + bool _onMouseUp( const GuiEvent& event, bool lockedMouse ); + + /// @name Callbacks + /// @{ + DECLARE_CALLBACK( void, onHeaderRightClick, () ); + + DECLARE_CALLBACK( void, onExpanded, () ); + + DECLARE_CALLBACK( void, onCollapsed, () ); + /// @} + + public: + + GuiRolloutCtrl(); + ~GuiRolloutCtrl(); + + DECLARE_CONOBJECT(GuiRolloutCtrl); + DECLARE_CATEGORY( "Gui Containers" ); + DECLARE_DESCRIPTION( "A container that displays a header with a caption on top of its child control\n" + "that when clicked collapses/expands the control to/from just the header." ); + + // Persistence + static void initPersistFields(); + + // Control Events + bool onWake(); + void addObject(SimObject *obj); + void removeObject(SimObject *obj); + virtual void childResized(GuiControl *child); + + // Mouse Events + virtual void onMouseDown( const GuiEvent& event ); + virtual void onMouseUp( const GuiEvent& event ); + virtual void onRightMouseUp( const GuiEvent& event ); + virtual bool onMouseUpEditor( const GuiEvent& event, Point2I offset ); + + // Sizing Helpers + virtual void calculateHeights(); + virtual bool resize( const Point2I &newPosition, const Point2I &newExtent ); + virtual void sizeToContents(); + inline bool isExpanded() const { return mIsExpanded; } + + // Sizing Animation Functions + void animateTo( S32 height ); + virtual void processTick(); + + void collapse() { animateTo( mHeader.extent.y ); } + void expand() { animateTo( mExpanded.extent.y ); } + void instantCollapse(); + void instantExpand(); + void toggleExpanded( bool instant ); + + const String& getCaption() const { return mCaption; } + bool isHeaderHidden() const { return mHideHeader; } + bool canCollapse() const { return mCanCollapse; } + + void setCaption( const String& str ) { mCaption = str; } + void setMargin( const RectI& rect ) { mMargin = rect; } + void setMargin( S32 left, S32 top, S32 right, S32 bottom ) { setMargin( RectI( left, top, right, bottom ) ); } + + void setHeaderHidden( bool value ) { mHideHeader = value; } + void setCanCollapse( bool value ) { mCanCollapse = value; } + + // Control Rendering + virtual void onRender(Point2I offset, const RectI &updateRect); + bool onAdd(); +}; + +#endif // _GUI_ROLLOUTCTRL_H_ \ No newline at end of file diff --git a/Engine/source/gui/containers/guiScrollCtrl.cpp b/Engine/source/gui/containers/guiScrollCtrl.cpp new file mode 100644 index 000000000..c1c7a9305 --- /dev/null +++ b/Engine/source/gui/containers/guiScrollCtrl.cpp @@ -0,0 +1,1410 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "platform/typetraits.h" +#include "gui/containers/guiScrollCtrl.h" +#include "console/engineAPI.h" +#include "console/console.h" +#include "gfx/bitmap/gBitmap.h" +#include "platform/event.h" +#include "gui/core/guiDefaultControlRender.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxDrawUtil.h" +#include "gui/core/guiCanvas.h" + + +IMPLEMENT_CONOBJECT( GuiScrollCtrl ); + +ConsoleDocClass( GuiScrollCtrl, + "@brief A container that allows to view one or more possibly larger controls inside its area by " + "providing horizontal and/or vertical scroll bars.\n\n" + + "@ingroup GuiContainers" +); + +ImplementEnumType( GuiScrollBarBehavior, + "Display behavior of a scroll bar. Determines when a scrollbar will be visible.\n\n" + "@ingroup GuiContainers" ) + { GuiScrollCtrl::ScrollBarAlwaysOn, "alwaysOn", "Always visible." }, + { GuiScrollCtrl::ScrollBarAlwaysOff, "alwaysOff", "Never visible." }, + { GuiScrollCtrl::ScrollBarDynamic, "dynamic", "Only visible when actually needed, i.e. when the child control(s) exceed the visible space on the given axis." }, +EndImplementEnumType; + +IMPLEMENT_CALLBACK( GuiScrollCtrl, onScroll, void, (), (), + "Called each time the child controls are scrolled by some amount." ); + +//----------------------------------------------------------------------------- + +GuiScrollCtrl::GuiScrollCtrl() + : mChildMargin( 0, 0 ), + mBorderThickness( 1 ), + mScrollBarThickness( 16 ), + mScrollBarArrowBtnLength( 16 ), + mScrollBarDragTolerance( 130 ), + mStateDepressed( false ), + mHitRegion( None ), + mWillFirstRespond( true ), + mUseConstantHeightThumb( false ), + mForceVScrollBar( ScrollBarAlwaysOn ), + mForceHScrollBar( ScrollBarAlwaysOn ), + mLockHorizScroll( false ), + mLockVertScroll( false ), + mIgnoreChildResized( false ), + mAnimating( false ), + mScrollAnimSpeed( -1 ), + mScrollTargetPos( -1, -1 ), + mChildExt(0, 0), + mChildPos(0, 0) +{ + mIsContainer = true; + setExtent(200,200); +} + +//----------------------------------------------------------------------------- + +void GuiScrollCtrl::initPersistFields() +{ + addGroup( "Scolling" ); + + addField( "willFirstRespond", TypeBool, Offset(mWillFirstRespond, GuiScrollCtrl)); + addField( "hScrollBar", TYPEID< ScrollBarBehavior >(), Offset(mForceHScrollBar, GuiScrollCtrl), + "When to display the horizontal scrollbar."); + addField( "vScrollBar", TYPEID< ScrollBarBehavior >(), Offset(mForceVScrollBar, GuiScrollCtrl), + "When to display the vertical scrollbar."); + addField( "lockHorizScroll", TypeBool, Offset(mLockHorizScroll, GuiScrollCtrl), + "Horizontal scrolling not allowed if set."); + addField( "lockVertScroll", TypeBool, Offset(mLockVertScroll, GuiScrollCtrl), + "Vertical scrolling not allowed if set."); + addField( "constantThumbHeight", TypeBool, Offset(mUseConstantHeightThumb, GuiScrollCtrl)); + addField( "childMargin", TypePoint2I, Offset(mChildMargin, GuiScrollCtrl), + "Padding region to put around child contents." ); + addField( "mouseWheelScrollSpeed", TypeS32, Offset(mScrollAnimSpeed, GuiScrollCtrl), + "Pixels/Tick - if not positive then mousewheel scrolling occurs instantly (like other scrolling)."); + + endGroup( "Scrolling" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +bool GuiScrollCtrl::resize(const Point2I &newPos, const Point2I &newExt) +{ + if( !Parent::resize(newPos, newExt) ) + return false; + + computeSizes(); + return true; +} + +//----------------------------------------------------------------------------- + +void GuiScrollCtrl::childResized(GuiControl *child) +{ + if ( mIgnoreChildResized ) + return; + + Parent::childResized(child); + computeSizes(); +} + +//----------------------------------------------------------------------------- + +bool GuiScrollCtrl::onWake() +{ + if (! Parent::onWake()) + return false; + + mTextureObject = mProfile->mTextureObject; + if (mTextureObject && (mProfile->constructBitmapArray() >= BmpStates * BmpCount)) + { + mBitmapBounds = mProfile->mBitmapArrayRects.address(); + + //init + mBaseThumbSize = mBitmapBounds[BmpStates * BmpVThumbTopCap].extent.y + + mBitmapBounds[BmpStates * BmpVThumbBottomCap].extent.y; + mScrollBarThickness = mBitmapBounds[BmpStates * BmpVPage].extent.x; + mScrollBarArrowBtnLength = mBitmapBounds[BmpStates * BmpUp].extent.y; + computeSizes(); + } + else + { + Con::warnf("No texture loaded for scroll control named %s with profile %s", getName(), mProfile->getName()); + } + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiScrollCtrl::onSleep() +{ + // Reset the mouse tracking state of this control + // when it is put to sleep + mStateDepressed = false; + mHitRegion = None; + + Parent::onSleep(); + mTextureObject = NULL; +} + +//----------------------------------------------------------------------------- + +bool GuiScrollCtrl::calcChildExtents() +{ + // scroll control should deal well with multiple gui controls + if( !size() ) + return false; + + // Find size and relative position of the client rectangle. + + Point2I maxPos( TypeTraits< S32 >::MIN, TypeTraits< S32 >::MIN ); + Point2I minPos( TypeTraits< S32 >::MAX, TypeTraits< S32 >::MAX ); + + bool haveVisibleChild = false; + for( U32 i = 0; i < size(); i++ ) + { + GuiControl *ctrl = (GuiControl*)at(i); + if( ctrl->isVisible() ) + { + haveVisibleChild = true; + + minPos.x = getMin( ctrl->getPosition().x, minPos.x ); + minPos.y = getMin( ctrl->getPosition().y, minPos.y ); + + // This is +1 but the remaining code here all works with extents +1. + Point2I ctrlMax = ctrl->getPosition() + ctrl->getExtent(); + + maxPos.x = getMax( ctrlMax.x, maxPos.x ); + maxPos.y = getMax( ctrlMax.y, maxPos.y ); + } + } + + if( !haveVisibleChild ) + return false; + + mChildPos = minPos; + mChildExt = maxPos - minPos; + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiScrollCtrl::scrollRectVisible(RectI rect) +{ + // rect is passed in virtual client space + if(rect.extent.x > mContentExt.x) + rect.extent.x = mContentExt.x; + if(rect.extent.y > mContentExt.y) + rect.extent.y = mContentExt.y; + + // Determine the points bounding the requested rectangle + Point2I rectUpperLeft = rect.point; + Point2I rectLowerRight = rect.point + rect.extent; + + // Determine the points bounding the actual visible area... + Point2I visUpperLeft = mChildRelPos; + Point2I visLowerRight = mChildRelPos + mContentExt; + Point2I delta(0,0); + + // We basically try to make sure that first the top left of the given + // rect is visible, and if it is, then that the bottom right is visible. + + // Make sure the rectangle is visible along the X axis... + if(rectUpperLeft.x < visUpperLeft.x) + delta.x = rectUpperLeft.x - visUpperLeft.x; + else if(rectLowerRight.x > visLowerRight.x) + delta.x = rectLowerRight.x - visLowerRight.x; + + // Make sure the rectangle is visible along the Y axis... + if(rectUpperLeft.y < visUpperLeft.y) + delta.y = rectUpperLeft.y - visUpperLeft.y; + else if(rectLowerRight.y > visLowerRight.y) + delta.y = rectLowerRight.y - visLowerRight.y; + + // If we had any changes, scroll, otherwise don't. + if(delta.x || delta.y) + scrollDelta(delta.x, delta.y); +} + +//----------------------------------------------------------------------------- + +bool GuiScrollCtrl::isPointVisible( const Point2I& point ) +{ + return ( point.x >= mChildRelPos.x && point.x <= ( mChildRelPos.x + mContentExt.x ) ) + && ( point.y >= mChildRelPos.y && point.y <= ( mChildRelPos.y + mContentExt.y ) ); +} + +//----------------------------------------------------------------------------- + +bool GuiScrollCtrl::isRectCompletelyVisible(const RectI& rect) +{ + // rect is passed in virtual client space + // Determine the points bounding the requested rectangle + Point2I rectUpperLeft = rect.point; + Point2I rectLowerRight = rect.point + rect.extent; + + // Determine the points bounding the actual visible area... + Point2I visUpperLeft = mChildRelPos; + Point2I visLowerRight = mChildRelPos + mContentExt; + + // Make sure the rectangle is visible along the X axis... + if(rectUpperLeft.x < visUpperLeft.x) + return false; + else if(rectLowerRight.x > visLowerRight.x) + return false; + + // Make sure the rectangle is visible along the Y axis... + if(rectUpperLeft.y < visUpperLeft.y) + return false; + else if(rectLowerRight.y > visLowerRight.y) + return false; + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiScrollCtrl::addObject(SimObject *object) +{ + Parent::addObject(object); + computeSizes(); +} + +//----------------------------------------------------------------------------- + +GuiControl* GuiScrollCtrl::findHitControl(const Point2I &pt, S32 initialLayer) +{ + if(pt.x < mProfile->mBorderThickness || pt.y < mProfile->mBorderThickness) + return this; + if(pt.x >= getWidth() - mProfile->mBorderThickness - (mHasVScrollBar ? mScrollBarThickness : 0) || + pt.y >= getHeight() - mProfile->mBorderThickness - (mHasHScrollBar ? mScrollBarThickness : 0)) + return this; + return Parent::findHitControl(pt, initialLayer); +} + +//----------------------------------------------------------------------------- + +void GuiScrollCtrl::computeSizes() +{ + S32 thickness = (mProfile ? mProfile->mBorderThickness : 1); + Point2I borderExtent(thickness, thickness); + mContentPos = borderExtent + mChildMargin; + mContentExt = getExtent() - (mChildMargin * 2) + - (borderExtent * 2); + + Point2I childLowerRight; + + mHBarEnabled = false; + mVBarEnabled = false; + mHasVScrollBar = (mForceVScrollBar == ScrollBarAlwaysOn); + mHasHScrollBar = (mForceHScrollBar == ScrollBarAlwaysOn); + + setUpdate(); + + if (calcChildExtents()) + { + childLowerRight = mChildPos + mChildExt; + + if (mHasVScrollBar) + mContentExt.x -= mScrollBarThickness; + if (mHasHScrollBar) + mContentExt.y -= mScrollBarThickness; + if (mChildExt.x > mContentExt.x && (mForceHScrollBar == ScrollBarDynamic)) + { + mHasHScrollBar = true; + mContentExt.y -= mScrollBarThickness; + } + if (mChildExt.y > mContentExt.y && (mForceVScrollBar == ScrollBarDynamic)) + { + mHasVScrollBar = true; + mContentExt.x -= mScrollBarThickness; + + // If Extent X Changed, check Horiz Scrollbar. + if (mChildExt.x > mContentExt.x && !mHasHScrollBar && (mForceHScrollBar == ScrollBarDynamic)) + { + mHasHScrollBar = true; + mContentExt.y -= mScrollBarThickness; + } + } + Point2I contentLowerRight = mContentPos + mContentExt; + + // see if the child controls need to be repositioned (null space in control) + Point2I delta(0,0); + + if (mChildPos.x > mContentPos.x) + delta.x = mContentPos.x - mChildPos.x; + else if (contentLowerRight.x > childLowerRight.x) + { + S32 diff = contentLowerRight.x - childLowerRight.x; + delta.x = getMin(mContentPos.x - mChildPos.x, diff); + } + + //reposition the children if the child extent > the scroll content extent + if (mChildPos.y > mContentPos.y) + delta.y = mContentPos.y - mChildPos.y; + else if (contentLowerRight.y > childLowerRight.y) + { + S32 diff = contentLowerRight.y - childLowerRight.y; + delta.y = getMin(mContentPos.y - mChildPos.y, diff); + } + + // apply the deltas to the children... + if (delta.x || delta.y) + { + SimGroup::iterator i; + for(i = begin(); i != end();i++) + { + GuiControl *ctrl = (GuiControl *) (*i); + ctrl->setPosition( ctrl->getPosition() + delta ); + } + mChildPos += delta; + childLowerRight += delta; + } + // enable needed scroll bars + if (mChildExt.x > mContentExt.x) + mHBarEnabled = true; + if (mChildExt.y > mContentExt.y) + mVBarEnabled = true; + mChildRelPos = mContentPos - mChildPos; + } + + // Prevent resizing our children from recalling this function! + mIgnoreChildResized = true; + + if ( mLockVertScroll ) + { + // If vertical scroll is locked we size our child's height to our own + SimGroup::iterator i; + for(i = begin(); i != end();i++) + { + GuiControl *ctrl = (GuiControl *) (*i); + ctrl->setHeight( mContentExt.y ); + } + } + + if ( mLockHorizScroll ) + { + // If horizontal scroll is locked we size our child's width to our own + SimGroup::iterator i; + for(i = begin(); i != end();i++) + { + GuiControl *ctrl = (GuiControl *) (*i); + ctrl->setWidth( mContentExt.x ); + } + } + + mIgnoreChildResized = false; + + // build all the rectangles and such... + calcScrollRects(); + calcThumbs(); +} + +//----------------------------------------------------------------------------- + +void GuiScrollCtrl::calcScrollRects(void) +{ + S32 thickness = ( mProfile ? mProfile->mBorderThickness : 1 ); + if (mHasHScrollBar) + { + mLeftArrowRect.set(thickness, + getHeight() - thickness - mScrollBarThickness, + mScrollBarArrowBtnLength, + mScrollBarThickness); + + mRightArrowRect.set(getWidth() - thickness - (mHasVScrollBar ? mScrollBarThickness - 1 : 0) - mScrollBarArrowBtnLength, + getHeight() - thickness - mScrollBarThickness, + mScrollBarArrowBtnLength, + mScrollBarThickness); + mHTrackRect.set(mLeftArrowRect.point.x + mLeftArrowRect.extent.x, + mLeftArrowRect.point.y, + mRightArrowRect.point.x - (mLeftArrowRect.point.x + mLeftArrowRect.extent.x), + mScrollBarThickness); + } + if (mHasVScrollBar) + { + mUpArrowRect.set(getWidth() - thickness - mScrollBarThickness, + thickness, + mScrollBarThickness, + mScrollBarArrowBtnLength); + mDownArrowRect.set(getWidth() - thickness - mScrollBarThickness, + getHeight() - thickness - (mHasHScrollBar ? mScrollBarThickness - 1 : 0) - mScrollBarArrowBtnLength, + mScrollBarThickness, + mScrollBarArrowBtnLength); + mVTrackRect.set(mUpArrowRect.point.x, + mUpArrowRect.point.y + mUpArrowRect.extent.y, + mScrollBarThickness, + mDownArrowRect.point.y - (mUpArrowRect.point.y + mUpArrowRect.extent.y) ); + } +} + +//----------------------------------------------------------------------------- + +void GuiScrollCtrl::calcThumbs() +{ + if (mHBarEnabled && mChildExt.x > 0) + { + U32 trackSize = mHTrackRect.len_x(); + + if (mUseConstantHeightThumb) + mHThumbSize = mBaseThumbSize; + else + mHThumbSize = getMax(mBaseThumbSize, ( S32 )mCeil( ( F32 )( mContentExt.x * trackSize) / ( F32 )mChildExt.x ) ); + + mHThumbPos = mHTrackRect.point.x + (mChildRelPos.x * (trackSize - mHThumbSize)) / (mChildExt.x - mContentExt.x); + } + if (mVBarEnabled && mChildExt.y > 0) + { + U32 trackSize = mVTrackRect.len_y(); + + if (mUseConstantHeightThumb) + mVThumbSize = mBaseThumbSize; + else + mVThumbSize = getMax(mBaseThumbSize, ( S32 )mCeil( ( F32 )( mContentExt.y * trackSize ) / ( F32 )mChildExt.y ) ); + + mVThumbPos = mVTrackRect.point.y + (mChildRelPos.y * (trackSize - mVThumbSize)) / (mChildExt.y - mContentExt.y); + } +} + +//----------------------------------------------------------------------------- + +void GuiScrollCtrl::scrollDelta(S32 deltaX, S32 deltaY) +{ + scrollTo(mChildRelPos.x + deltaX, mChildRelPos.y + deltaY); +} + +//----------------------------------------------------------------------------- + +void GuiScrollCtrl::scrollDeltaAnimate(S32 x, S32 y) +{ + if ( !size() ) + return; + + if ( mAnimating ) + mScrollTargetPos += Point2I( x, y ); + else + mScrollTargetPos = mChildRelPos + Point2I( x, y ); + + setUpdate(); + + mScrollTargetPos.setMin( mChildExt - mContentExt ); + mScrollTargetPos.setMax( Point2I::Zero ); + + mAnimating = true; +} + +//----------------------------------------------------------------------------- + +void GuiScrollCtrl::scrollTo(S32 x, S32 y) +{ + if( !size() ) + return; + + if ( x == mChildRelPos.x && y == mChildRelPos.y ) + return; + + setUpdate(); + if (x > mChildExt.x - mContentExt.x) + x = mChildExt.x - mContentExt.x; + if (x < 0) + x = 0; + + if (y > mChildExt.y - mContentExt.y) + y = mChildExt.y - mContentExt.y; + if (y < 0) + y = 0; + + Point2I delta(x - mChildRelPos.x, y - mChildRelPos.y); + mChildRelPos += delta; + mChildPos -= delta; + + for(SimSet::iterator i = begin(); i != end();i++) + { + GuiControl *ctrl = (GuiControl *) (*i); + ctrl->setPosition( ctrl->getPosition() - delta ); + } + calcThumbs(); + + onScroll_callback(); +} + +//----------------------------------------------------------------------------- + +void GuiScrollCtrl::scrollToObject(GuiControl *targetControl) +{ + bool isValidChild = false; + Point2I relativePosition = targetControl->getPosition(); + GuiControl* parentControl = targetControl->getParent(); + while (parentControl) + { + GuiScrollCtrl* scrollControl = dynamic_cast(parentControl); + if (scrollControl == this) + { + relativePosition += scrollControl->getChildRelPos(); + isValidChild = true; + break; + } + + relativePosition += parentControl->getPosition(); + parentControl = parentControl->getParent(); + } + + if (isValidChild) + { + scrollRectVisible(RectI(relativePosition, targetControl->getExtent())); + } + else + { + Con::errorf("GuiScrollCtrl::scrollToObject() - Specified object is not a child of this scroll control (%d)!", targetControl->getId()); + } +} + +//----------------------------------------------------------------------------- + +GuiScrollCtrl::Region GuiScrollCtrl::findHitRegion(const Point2I &pt) +{ + if (mVBarEnabled && mHasVScrollBar) + { + if (mUpArrowRect.pointInRect(pt)) + return UpArrow; + else if (mDownArrowRect.pointInRect(pt)) + return DownArrow; + else if (mVTrackRect.pointInRect(pt)) + { + if (pt.y < mVThumbPos) + return UpPage; + else if (pt.y < mVThumbPos + mVThumbSize) + return VertThumb; + else + return DownPage; + } + } + if (mHBarEnabled && mHasHScrollBar) + { + if (mLeftArrowRect.pointInRect(pt)) + return LeftArrow; + else if (mRightArrowRect.pointInRect(pt)) + return RightArrow; + else if (mHTrackRect.pointInRect(pt)) + { + if (pt.x < mHThumbPos) + return LeftPage; + else if (pt.x < mHThumbPos + mHThumbSize) + return HorizThumb; + else + return RightPage; + } + } + return None; +} + +//----------------------------------------------------------------------------- + +bool GuiScrollCtrl::wantsTabListMembership() +{ + return true; +} + +//----------------------------------------------------------------------------- + +bool GuiScrollCtrl::loseFirstResponder() +{ + setUpdate(); + return true; +} + +//----------------------------------------------------------------------------- + +bool GuiScrollCtrl::becomeFirstResponder() +{ + setUpdate(); + return mWillFirstRespond; +} + +//----------------------------------------------------------------------------- + +bool GuiScrollCtrl::onKeyDown(const GuiEvent &event) +{ + if (mWillFirstRespond) + { + switch (event.keyCode) + { + case KEY_RIGHT: + scrollByRegion(RightArrow); + return true; + + case KEY_LEFT: + scrollByRegion(LeftArrow); + return true; + + case KEY_DOWN: + scrollByRegion(DownArrow); + return true; + + case KEY_UP: + scrollByRegion(UpArrow); + return true; + + case KEY_PAGE_UP: + scrollByRegion(UpPage); + return true; + + case KEY_PAGE_DOWN: + scrollByRegion(DownPage); + return true; + + default: + break; + } + } + return Parent::onKeyDown(event); +} + +//----------------------------------------------------------------------------- + +void GuiScrollCtrl::_onMouseDown( const GuiEvent &event, bool lockMouse ) +{ + if( lockMouse ) + { + mouseLock(); + mStateDepressed = true; + } + + setUpdate(); + + Point2I curMousePos = globalToLocalCoord(event.mousePoint); + mHitRegion = findHitRegion(curMousePos); + + // Set a 0.5 second delay before we start scrolling + mLastUpdated = Platform::getVirtualMilliseconds() + 500; + + scrollByRegion(mHitRegion); + + if (mHitRegion == VertThumb) + { + mChildRelPosAnchor = mChildRelPos; + mThumbMouseDelta = curMousePos.y - mVThumbPos; + } + else if (mHitRegion == HorizThumb) + { + mChildRelPosAnchor = mChildRelPos; + mThumbMouseDelta = curMousePos.x - mHThumbPos; + } +} + +//----------------------------------------------------------------------------- + +void GuiScrollCtrl::onMouseDown(const GuiEvent &event) +{ + _onMouseDown( event, true ); +} + +//----------------------------------------------------------------------------- + +bool GuiScrollCtrl::onMouseDownEditor( const GuiEvent& event, Point2I offset ) +{ + // If ALT is pressed while clicking on a horizontal or vertical scrollbar, + // do a scroll. + + if( event.modifier & SI_PRIMARY_ALT ) + { + Region hitRegion = findHitRegion( globalToLocalCoord( event.mousePoint ) ); + if( hitRegion != None ) + { + _onMouseDown( event, false ); + return true; + } + } + + return false; +} + +//----------------------------------------------------------------------------- + +void GuiScrollCtrl::onMouseUp(const GuiEvent &) +{ + mouseUnlock(); + + setUpdate(); + + mHitRegion = None; + mStateDepressed = false; +} + +//----------------------------------------------------------------------------- + +void GuiScrollCtrl::onMouseDragged(const GuiEvent &event) +{ + Point2I curMousePos = globalToLocalCoord(event.mousePoint); + setUpdate(); + + if ( (mHitRegion != VertThumb) && (mHitRegion != HorizThumb) ) + { + Region hit = findHitRegion(curMousePos); + if (hit != mHitRegion) + mStateDepressed = false; + else + mStateDepressed = true; + return; + } + + // ok... if the mouse is 'near' the scroll bar, scroll with it + // otherwise, snap back to the previous position. + + if (mHitRegion == VertThumb) + { + if (curMousePos.x >= mVTrackRect.point.x - mScrollBarDragTolerance && + curMousePos.x <= mVTrackRect.point.x + mVTrackRect.extent.x - 1 + mScrollBarDragTolerance && + curMousePos.y >= mVTrackRect.point.y - mScrollBarDragTolerance && + curMousePos.y <= mVTrackRect.point.y + mVTrackRect.extent.y - 1 + mScrollBarDragTolerance) + { + S32 newVThumbPos = curMousePos.y - mThumbMouseDelta; + if(newVThumbPos != mVThumbPos) + { + S32 newVPos = (newVThumbPos - mVTrackRect.point.y) * + (mChildExt.y - mContentExt.y) / + (mVTrackRect.extent.y - mVThumbSize); + + scrollTo(mChildRelPosAnchor.x, newVPos); + } + } + else + scrollTo(mChildRelPosAnchor.x, mChildRelPosAnchor.y); + } + else if (mHitRegion == HorizThumb) + { + if (curMousePos.x >= mHTrackRect.point.x - mScrollBarDragTolerance && + curMousePos.x <= mHTrackRect.point.x + mHTrackRect.extent.x - 1 + mScrollBarDragTolerance && + curMousePos.y >= mHTrackRect.point.y - mScrollBarDragTolerance && + curMousePos.y <= mHTrackRect.point.y + mHTrackRect.extent.y - 1 + mScrollBarDragTolerance) + { + S32 newHThumbPos = curMousePos.x - mThumbMouseDelta; + if(newHThumbPos != mHThumbPos) + { + S32 newHPos = (newHThumbPos - mHTrackRect.point.x) * + (mChildExt.x - mContentExt.x) / + (mHTrackRect.extent.x - mHThumbSize); + + scrollTo(newHPos, mChildRelPosAnchor.y); + } + } + else + scrollTo(mChildRelPosAnchor.x, mChildRelPosAnchor.y); + } +} + +//----------------------------------------------------------------------------- + +bool GuiScrollCtrl::onMouseWheelUp(const GuiEvent &event) +{ + if ( !mAwake || !mVisible ) + return false; + + scrollByMouseWheel( event ); + + return true; +} + +//----------------------------------------------------------------------------- + +bool GuiScrollCtrl::onMouseWheelDown(const GuiEvent &event) +{ + if ( !mAwake || !mVisible ) + return false; + + scrollByMouseWheel( event ); + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiScrollCtrl::updateChildMousePos() +{ + // We pass a fake GuiEvent to child controls onMouseMove + // since although the mouse has not moved 'they' have. + // + // Its possible this could cause problems if a GuiControl + // responds to more than just the mouse position in the onMouseMove + // event, like for example doing something different depending on + // a modifier key, which we aren't filling in to the structure! + + GuiEvent event; + event.mousePoint = getRoot()->getCursorPos(); + + iterator itr; + for ( itr = begin(); itr != end(); itr++ ) + { + GuiControl *child = static_cast( *itr ); + child->onMouseMove( event ); + } +} + +//----------------------------------------------------------------------------- + +void GuiScrollCtrl::onPreRender() +{ + Parent::onPreRender(); + + S32 currentTime = Platform::getVirtualMilliseconds(); + S32 deltaMs = currentTime - mLastPreRender; + mLastPreRender = currentTime; + + // Update mouse-wheel scroll animation if we are currently doing one... + + if ( mAnimating ) + { + //U32 frames = Con::getIntVariable( "$frames", 0 ); + //frames++; + //Con::setIntVariable( "$frames", frames ); + + F32 deltaTicks = deltaMs / 32.0f; + + if ( mScrollAnimSpeed <= 0 ) + { + scrollTo( mScrollTargetPos.x, mScrollTargetPos.y ); + } + else + { + S32 maxPixels = deltaTicks * mScrollAnimSpeed; + + Point2I toTarget = mScrollTargetPos - mChildRelPos; + S32 signx = toTarget.x > 0 ? 1 : -1; + S32 signy = toTarget.y > 0 ? 1 : -1; + + S32 deltaX = getMin( mAbs(toTarget.x), maxPixels ) * signx; + S32 deltaY = getMin( mAbs(toTarget.y), maxPixels ) * signy; + + scrollDelta( deltaX, deltaY ); + } + + if ( mChildRelPos == mScrollTargetPos ) + { + //Con::printf( "Animated Frames : %d", frames ); + //Con::setIntVariable( "$frames", 0 ); + mAnimating = false; + } + + updateChildMousePos(); + } + + // Now scroll in response to a 'depressed state' if appropriate... + + // Short circuit if not depressed to save cycles + if( mStateDepressed != true ) + return; + + //default to one second, though it shouldn't be necessary + U32 timeThreshold = 1000; + + // We don't want to scroll by pages at an interval the same as when we're scrolling + // using the arrow buttons, so adjust accordingly. + switch( mHitRegion ) + { + case UpPage: + case DownPage: + case LeftPage: + case RightPage: + timeThreshold = 200; + break; + case UpArrow: + case DownArrow: + case LeftArrow: + case RightArrow: + timeThreshold = 20; + break; + default: + // Neither a button or a page, don't scroll (shouldn't get here) + return; + break; + }; + + S32 timeElapsed = Platform::getVirtualMilliseconds() - mLastUpdated; + + if ( ( timeElapsed > 0 ) && ( timeElapsed > timeThreshold ) ) + { + mLastUpdated = Platform::getVirtualMilliseconds(); + scrollByRegion(mHitRegion); + } +} + +//----------------------------------------------------------------------------- + +void GuiScrollCtrl::scrollByRegion(Region reg) +{ + setUpdate(); + if(!size()) + return; + GuiControl *content = (GuiControl *) front(); + U32 rowHeight, columnWidth; + U32 pageHeight, pageWidth; + + content->getScrollLineSizes(&rowHeight, &columnWidth); + + if(rowHeight >= mContentExt.y) + pageHeight = 1; + else + pageHeight = mContentExt.y - rowHeight; + + if(columnWidth >= mContentExt.x) + pageWidth = 1; + else + pageWidth = mContentExt.x - columnWidth; + + if (mVBarEnabled) + { + switch(reg) + { + case UpPage: + scrollDelta(0, -(S32)pageHeight); + break; + case DownPage: + scrollDelta(0, pageHeight); + break; + case UpArrow: + scrollDelta(0, -(S32)rowHeight); + break; + case DownArrow: + scrollDelta(0, rowHeight); + break; + default: + break; + } + } + + if (mHBarEnabled) + { + switch(reg) + { + case LeftPage: + scrollDelta(-(S32)pageWidth, 0); + break; + case RightPage: + scrollDelta(pageWidth, 0); + break; + case LeftArrow: + scrollDelta(-(S32)columnWidth, 0); + break; + case RightArrow: + scrollDelta(columnWidth, 0); + break; + default: + break; + } + } +} + +//----------------------------------------------------------------------------- + +void GuiScrollCtrl::scrollByMouseWheel( const GuiEvent &event ) +{ + setUpdate(); + if ( !size() ) + return; + + if( event.mouseAxis == 1 ) + scrollDeltaAnimate( 0, -event.fval ); + else + scrollDeltaAnimate( -event.fval, 0 ); +} + +//----------------------------------------------------------------------------- + +void GuiScrollCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + // draw content controls + // create a rect to intersect with the updateRect + RectI contentRect(mContentPos.x + offset.x, mContentPos.y + offset.y, mContentExt.x, mContentExt.y); + contentRect.intersect(updateRect); + + // Always call parent + Parent::onRender(offset, contentRect); + + if( mTextureObject ) + { + // Reset the ClipRect as the parent call can modify it when rendering + // the child controls + GFX->setClipRect( updateRect ); + + //draw the scroll corner + if (mHasVScrollBar && mHasHScrollBar) + drawScrollCorner(offset); + + // draw scroll bars + if (mHasVScrollBar) + drawVScrollBar(offset); + + if (mHasHScrollBar) + drawHScrollBar(offset); + } +} + +//----------------------------------------------------------------------------- + +void GuiScrollCtrl::drawBorder( const Point2I &offset, bool /*isFirstResponder*/ ) +{ +} + +//----------------------------------------------------------------------------- + +void GuiScrollCtrl::drawVScrollBar(const Point2I &offset) +{ + if ( mTextureObject.isNull() ) + { + return; + } + + // Start Point. + Point2I pos = ( offset + mUpArrowRect.point ); + + // Up Arrow. + S32 upArrowBitmap = ( BmpStates * BmpUp ); + if ( !mVBarEnabled ) + { + upArrowBitmap += BmpDisabled; + } + else if ( mHitRegion == UpArrow && mStateDepressed ) + { + upArrowBitmap += BmpHilite; + } + + // Render Up Arrow. + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapSR( mTextureObject, pos, mBitmapBounds[upArrowBitmap] ); + + // Update Pos. + pos.y += mBitmapBounds[upArrowBitmap].extent.y; + + // Track. + S32 trackBitmap = ( BmpStates * BmpVPage ); + if ( !mVBarEnabled ) + { + trackBitmap += BmpDisabled; + } + else if ( mHitRegion == DownPage && mStateDepressed ) + { + trackBitmap += BmpHilite; + } + + // Determine the Track Rect. + RectI trackRect; + trackRect.point = pos; + trackRect.extent.x = mBitmapBounds[trackBitmap].extent.x; + trackRect.extent.y = ( offset.y + mDownArrowRect.point.y ) - pos.y; + + // Render Track? + if ( trackRect.extent.y > 0 ) + { + // Render Track. + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapStretchSR( mTextureObject, trackRect, mBitmapBounds[trackBitmap] ); + } + + // Update Pos. + pos.y += trackRect.extent.y; + + // Down Arrow. + S32 downArrowBitmap = ( BmpStates * BmpDown ); + if ( !mVBarEnabled ) + { + downArrowBitmap += BmpDisabled; + } + else if ( mHitRegion == DownArrow && mStateDepressed ) + { + downArrowBitmap += BmpHilite; + } + + // Render Down Arrow. + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapSR( mTextureObject, pos, mBitmapBounds[downArrowBitmap] ); + + // Render the Thumb? + if ( !mVBarEnabled ) + { + // Nope. + return; + } + + // Reset the Pos. + pos.y = ( offset.y + mVThumbPos ); + + // Determine the Bitmaps. + S32 thumbBitmapTop = ( BmpStates * BmpVThumbTopCap ); + S32 thumbBitmapMiddle = ( BmpStates * BmpVThumb ); + S32 thumbBitmapBottom = ( BmpStates * BmpVThumbBottomCap ); + + if ( mHitRegion == VertThumb && mStateDepressed ) + { + thumbBitmapTop += BmpHilite; + thumbBitmapMiddle += BmpHilite; + thumbBitmapBottom += BmpHilite; + } + + // Render Thumb Top. + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapSR( mTextureObject, pos, mBitmapBounds[thumbBitmapTop] ); + + // Update Pos. + pos.y += mBitmapBounds[thumbBitmapTop].extent.y; + + // Determine the Thumb Rect. + RectI thumbRect; + thumbRect.point = pos; + thumbRect.extent.x = mBitmapBounds[thumbBitmapMiddle].extent.x; + thumbRect.extent.y = mVThumbSize - ( mBitmapBounds[thumbBitmapTop].extent.y + mBitmapBounds[thumbBitmapBottom].extent.y ); + + // Render Thumb? + if ( thumbRect.extent.y > 0 ) + { + // Render Track. + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapStretchSR( mTextureObject, thumbRect, mBitmapBounds[thumbBitmapMiddle] ); + } + + // Update Pos. + pos.y += thumbRect.extent.y; + + // Render the Thumb Bottom. + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapSR( mTextureObject, pos, mBitmapBounds[thumbBitmapBottom] ); +} + +//----------------------------------------------------------------------------- + +void GuiScrollCtrl::drawHScrollBar(const Point2I &offset) +{ + if ( mTextureObject.isNull() ) + { + return; + } + + // Start Point. + Point2I pos = ( offset + mLeftArrowRect.point ); + + // Left Arrow. + S32 leftArrowBitmap = ( BmpStates * BmpLeft ); + if ( !mHBarEnabled ) + { + leftArrowBitmap += BmpDisabled; + } + else if ( mHitRegion == LeftArrow && mStateDepressed ) + { + leftArrowBitmap += BmpHilite; + } + + // Render Up Arrow. + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapSR( mTextureObject, pos, mBitmapBounds[leftArrowBitmap] ); + + // Update Pos. + pos.x += mBitmapBounds[leftArrowBitmap].extent.x; + + // Track. + S32 trackBitmap = ( BmpStates * BmpHPage ); + if ( !mHBarEnabled ) + { + trackBitmap += BmpDisabled; + } + else if ( mHitRegion == LeftPage && mStateDepressed ) + { + trackBitmap += BmpHilite; + } + + // Determine the Track Rect. + RectI trackRect; + trackRect.point = pos; + trackRect.extent.x = ( offset.x + mRightArrowRect.point.x ) - pos.x; + trackRect.extent.y = mBitmapBounds[trackBitmap].extent.y; + + // Render Track? + if ( trackRect.extent.x > 0 ) + { + // Render Track. + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapStretchSR( mTextureObject, trackRect, mBitmapBounds[trackBitmap] ); + } + + // Update Pos. + pos.x += trackRect.extent.x; + + // Right Arrow. + S32 rightArrowBitmap = ( BmpStates * BmpRight ); + if ( !mHBarEnabled ) + { + rightArrowBitmap += BmpDisabled; + } + else if ( mHitRegion == RightArrow && mStateDepressed ) + { + rightArrowBitmap += BmpHilite; + } + + // Render Right Arrow. + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapSR( mTextureObject, pos, mBitmapBounds[rightArrowBitmap] ); + + // Render the Thumb? + if ( !mHBarEnabled ) + { + // Nope. + return; + } + + // Reset the Pos. + pos.x = ( offset.x + mHThumbPos ); + + // Determine the Bitmaps. + S32 thumbBitmapLeft = ( BmpStates * BmpHThumbLeftCap ); + S32 thumbBitmapMiddle = ( BmpStates * BmpHThumb ); + S32 thumbBitmapRight = ( BmpStates * BmpHThumbRightCap ); + + if ( mHitRegion == HorizThumb && mStateDepressed ) + { + thumbBitmapLeft += BmpHilite; + thumbBitmapMiddle += BmpHilite; + thumbBitmapRight += BmpHilite; + } + + // Render Thumb Left. + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapSR( mTextureObject, pos, mBitmapBounds[thumbBitmapLeft] ); + + // Update Pos. + pos.x += mBitmapBounds[thumbBitmapLeft].extent.x; + + // Determine the Thumb Rect. + RectI thumbRect; + thumbRect.point = pos; + thumbRect.extent.x = mHThumbSize - ( mBitmapBounds[thumbBitmapLeft].extent.x + mBitmapBounds[thumbBitmapRight].extent.x ); + thumbRect.extent.y = mBitmapBounds[thumbBitmapMiddle].extent.y; + + // Render Thumb? + if ( thumbRect.extent.x > 0 ) + { + // Render Track. + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapStretchSR( mTextureObject, thumbRect, mBitmapBounds[thumbBitmapMiddle] ); + } + + // Update Pos. + pos.x += thumbRect.extent.x; + + // Render the Thumb Bottom. + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapSR( mTextureObject, pos, mBitmapBounds[thumbBitmapRight] ); +} + +//----------------------------------------------------------------------------- + +void GuiScrollCtrl::drawScrollCorner(const Point2I &offset) +{ + Point2I pos = offset; + pos.x += mRightArrowRect.point.x + mRightArrowRect.extent.x - 1; + pos.y += mRightArrowRect.point.y; + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapSR(mTextureObject, pos, mBitmapBounds[BmpStates * BmpResize]); +} + +//----------------------------------------------------------------------------- + +void GuiScrollCtrl::autoScroll(Region reg) +{ + scrollByRegion(reg); +} + +//============================================================================= +// API. +//============================================================================= +// MARK: ---- API ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiScrollCtrl, scrollToTop, void, (),, + "Scroll all the way to the top of the vertical and left of the horizontal scrollbar." ) +{ + object->scrollTo( 0, 0 ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiScrollCtrl, scrollToBottom, void, (),, + "Scroll all the way to the bottom of the vertical scrollbar and the left of the horizontal bar." ) +{ + object->scrollTo( 0, 0x7FFFFFFF ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiScrollCtrl, setScrollPosition, void, ( S32 x, S32 y ),, + "Set the position of the scrolled content.\n\n" + "@param x Position on X axis.\n" + "@param y Position on y axis.\n" ) +{ + object->scrollTo( x, y ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiScrollCtrl, scrollToObject, void, ( GuiControl* control ),, + "Scroll the control so that the given child @a control is visible.\n\n" + "@param control A child control." ) +{ + if( control ) + object->scrollToObject( control ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiScrollCtrl, getScrollPosition, Point2I, (),, + "Get the current coordinates of the scrolled content.\n\n" + "@return The current position of the scrolled content." ) +{ + return object->getChildRelPos(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiScrollCtrl, getScrollPositionX, S32, (),, + "Get the current X coordinate of the scrolled content.\n\n" + "@return The current X coordinate of the scrolled content." ) +{ + return object->getChildRelPos().x; +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiScrollCtrl, getScrollPositionY, S32, (),, + "Get the current Y coordinate of the scrolled content." + "@return The current Y coordinate of the scrolled content." ) +{ + return object->getChildRelPos().y; +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiScrollCtrl, computeSizes, void, (),, + "Refresh sizing and positioning of child controls." ) +{ + object->computeSizes(); +} diff --git a/Engine/source/gui/containers/guiScrollCtrl.h b/Engine/source/gui/containers/guiScrollCtrl.h new file mode 100644 index 000000000..6ca280d04 --- /dev/null +++ b/Engine/source/gui/containers/guiScrollCtrl.h @@ -0,0 +1,276 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUISCROLLCTRL_H_ +#define _GUISCROLLCTRL_H_ + +#ifndef _GUICONTAINER_H_ + #include "gui/containers/guiContainer.h" +#endif + + +/// A control providing a window inside a larger client area which can be +/// scrolled using scrollbars. +/// +class GuiScrollCtrl : public GuiContainer +{ + public: + + typedef GuiContainer Parent; + + enum Region + { + UpArrow, + DownArrow, + LeftArrow, + RightArrow, + UpPage, + DownPage, + LeftPage, + RightPage, + VertThumb, + HorizThumb, + None + }; + + enum ScrollBarBehavior + { + ScrollBarAlwaysOn = 0, + ScrollBarAlwaysOff = 1, + ScrollBarDynamic = 2 + }; + + protected: + + // the scroll control uses a bitmap array to draw all its + // graphics... these are the bitmaps it needs: + enum BitmapIndices + { + BmpUp, + BmpDown, + BmpVThumbTopCap, + BmpVThumb, + BmpVThumbBottomCap, + BmpVPage, + BmpLeft, + BmpRight, + BmpHThumbLeftCap, + BmpHThumb, + BmpHThumbRightCap, + BmpHPage, + BmpResize, + + BmpCount + }; + + enum BitmapStates + { + BmpDefault = 0, + BmpHilite, + BmpDisabled, + + BmpStates + }; + + RectI *mBitmapBounds; //bmp is [3*n], bmpHL is [3*n + 1], bmpNA is [3*n + 2] + GFXTexHandle mTextureObject; + + S32 mBorderThickness; // this gets set per class in the constructor + Point2I mChildMargin; // the thickness of the margin around the child controls + + // note - it is implicit in the scroll view that the buttons all have the same + // arrow length and that horizontal and vertical scroll bars have the + // same thickness + + S32 mScrollBarThickness; // determined by the width of the vertical page bmp + S32 mScrollBarArrowBtnLength; // determined by the height of the up arrow + S32 mScrollBarDragTolerance; // maximal distance from scrollbar at which a scrollbar drag is still valid + + bool mHBarEnabled; + bool mVBarEnabled; + bool mHasHScrollBar; + bool mHasVScrollBar; + + Point2I mContentPos; // the position of the content region in the control's coord system + Point2I mContentExt; // the extent of the content region + + Point2I mChildPos; // the position of the upper left corner of the child control(s) + Point2I mChildExt; + + Point2I mChildRelPos; // the relative position of the upper left content corner in + // the child's coordinate system - 0,0 if scrolled all the way to upper left. + + //-------------------------------------- + // for mouse dragging the thumb + Point2I mChildRelPosAnchor; // the original childRelPos when scrolling started + S32 mThumbMouseDelta; + + S32 mLastUpdated; + + S32 mHThumbSize; + S32 mHThumbPos; + + S32 mVThumbSize; + S32 mVThumbPos; + + S32 mBaseThumbSize; + + RectI mUpArrowRect; + RectI mDownArrowRect; + RectI mLeftArrowRect; + RectI mRightArrowRect; + RectI mHTrackRect; + RectI mVTrackRect; + + //-------------------------------------- + // for determining hit area + + bool mStateDepressed; ///< Is the mouse currently depressed on a scroll region + Region mHitRegion; ///< Which region is hit by the mouse + + S32 mForceHScrollBar; ///< Force showing the Horizontal scrollbar + S32 mForceVScrollBar; ///< Force showing the Vertical scrollbar + bool mLockHorizScroll; ///< Is horizontal scrolling disabled + bool mLockVertScroll; ///< Is vertical scrolling disabled + + bool mUseConstantHeightThumb; + bool mWillFirstRespond; // for automatically handling arrow keys + + /// Used internally to prevent infinite recursion. + bool mIgnoreChildResized; + + /// MouseWheel scroll animation + /// @{ + + /// Is currently performing a scroll animation. + bool mAnimating; + + /// Pixels moved per tick when performing a scroll animation. + S32 mScrollAnimSpeed; + + /// The target position when performing a scroll animation. + Point2I mScrollTargetPos; + + /// Platform time of the last call to onPreRender + S32 mLastPreRender; + + /// @} + + /// @name Callbacks + /// @{ + + DECLARE_CALLBACK( void, onScroll, () ); + + /// @} + + Region findHitRegion(const Point2I &); + + virtual bool calcChildExtents(); + virtual void calcScrollRects(void); + void calcThumbs(); + void scrollByRegion(Region reg); + void scrollByMouseWheel( const GuiEvent &event ); + + /// Tell the kids that the mouse moved (relatively) + void updateChildMousePos(); + + /// + void _onMouseDown( const GuiEvent& event, bool lockMouse ); + + public: + + GuiScrollCtrl(); + + void autoScroll(Region reg); + + void scrollDeltaAnimate(S32 x, S32 y); + void scrollTo(S32 x, S32 y); + void scrollToObject(GuiControl *targetControl); + void scrollDelta(S32 x, S32 y); + void scrollRectVisible(RectI rect); + + /// Is the given client space rect completely visible within the actual + /// visible area, or is some of it clipped. Returns true if it is + /// completely visible. + bool isRectCompletelyVisible(const RectI& rect); + bool isPointVisible( const Point2I& point ); + + void computeSizes(); + + // you can change the bitmap array dynamically. + void loadBitmapArray(); + + void addObject(SimObject *obj); + bool resize(const Point2I &newPosition, const Point2I &newExtent); + void childResized(GuiControl *child); + Point2I getChildPos() { return mChildPos; } + Point2I getChildRelPos() { return mChildRelPos; }; + Point2I getChildExtent() { return mChildExt; } + Point2I getContentExtent() { return mContentExt; } + Point2I getChildMargin() { return mChildMargin; } // Added to aid in sizing calculations + S32 getBorderThickness(void) { return mBorderThickness; } + S32 scrollBarThickness() const { return(mScrollBarThickness); } + S32 scrollBarArrowBtnLength() const { return(mScrollBarArrowBtnLength); } + bool hasHScrollBar() const { return(mHasHScrollBar); } + bool hasVScrollBar() const { return(mHasVScrollBar); } + bool enabledHScrollBar() const { return(mHBarEnabled); } + bool enabledVScrollBar() const { return(mVBarEnabled); } + + bool isScrolledToBottom() { return mChildPos.y + mChildExt.y <= mContentPos.y + mContentExt.y; } + + bool wantsTabListMembership(); + bool becomeFirstResponder(); + bool loseFirstResponder(); + + Region getCurHitRegion(void) { return mHitRegion; } + + // GuiControl + virtual bool onKeyDown(const GuiEvent &event); + virtual void onMouseDown(const GuiEvent &event); + virtual bool onMouseDownEditor( const GuiEvent& event, Point2I offset ); + virtual void onMouseUp(const GuiEvent &event); + virtual void onMouseDragged(const GuiEvent &event); + virtual bool onMouseWheelUp(const GuiEvent &event); + virtual bool onMouseWheelDown(const GuiEvent &event); + + virtual bool onWake(); + virtual void onSleep(); + + virtual void onPreRender(); + virtual void onRender(Point2I offset, const RectI &updateRect); + virtual void drawBorder(const Point2I &offset, bool isFirstResponder); + virtual void drawVScrollBar(const Point2I &offset); + virtual void drawHScrollBar(const Point2I &offset); + virtual void drawScrollCorner(const Point2I &offset); + virtual GuiControl* findHitControl(const Point2I &pt, S32 initialLayer = -1); + + static void initPersistFields(); + + DECLARE_CONOBJECT(GuiScrollCtrl); + DECLARE_DESCRIPTION( "A container that allows to view a larger GUI control inside its smaller area " + "by providing horizontal and/or vertical scroll bars." ); +}; + +typedef GuiScrollCtrl::ScrollBarBehavior GuiScrollBarBehavior; +DefineEnumType( GuiScrollBarBehavior ); + +#endif //_GUI_SCROLL_CTRL_H diff --git a/Engine/source/gui/containers/guiSplitContainer.cpp b/Engine/source/gui/containers/guiSplitContainer.cpp new file mode 100644 index 000000000..cf6cab46e --- /dev/null +++ b/Engine/source/gui/containers/guiSplitContainer.cpp @@ -0,0 +1,615 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/containers/guiSplitContainer.h" + +#include "gui/core/guiCanvas.h" +#include "console/consoleTypes.h" +#include "gfx/gfxDrawUtil.h" + + +IMPLEMENT_CONOBJECT( GuiSplitContainer ); + +ConsoleDocClass( GuiSplitContainer, + "@brief A container that splits its area between two child controls.\n\n" + + "A GuiSplitContainer can be used to dynamically subdivide an area between two child controls. " + "A splitter bar is placed between the two controls and allows to dynamically adjust the sizing " + "ratio between the two sides. Splitting can be either horizontal (subdividing top and bottom) " + "or vertical (subdividing left and right) depending on #orientation.\n\n" + + "By using #fixedPanel, one of the panels can be chosen to remain at a fixed size (#fixedSize)." + + "@tsexample\n" + "// Create a vertical splitter with a fixed-size left panel.\n" + "%splitter = new GuiSplitContainer()\n" + "{\n" + " orientation = \"Vertical\";\n" + " fixedPanel = \"FirstPanel\";\n" + " fixedSize = 100;\n" + "\n" + " new GuiScrollCtrl()\n" + " {\n" + " new GuiMLTextCtrl()\n" + " {\n" + " text = %longText;\n" + " };\n" + " };\n" + "\n" + " new GuiScrollCtrl()\n" + " {\n" + " new GuiMLTextCtrl()\n" + " {\n" + " text = %moreLongText;\n" + " };\n" + " };\n" + "};\n" + "@endtsexample\n\n" + + "@note The children placed inside GuiSplitContainers must be GuiContainers.\n\n" + + "@ingroup GuiContainers" +); + + +ImplementEnumType( GuiSplitOrientation, + "Axis along which to divide the container's space.\n\n" + "@ingroup GuiContainers" ) + { GuiSplitContainer::Vertical, "Vertical", "Divide vertically placing one child left and one child right." }, + { GuiSplitContainer::Horizontal, "Horizontal", "Divide horizontally placing one child on top and one child below." } +EndImplementEnumType; + +ImplementEnumType( GuiSplitFixedPanel, + "Which side of the splitter to keep at a fixed size (if any).\n\n" + "@ingroup GuiContainers" ) + { GuiSplitContainer::None, "None", "Allow both childs to resize (default)." }, + { GuiSplitContainer::FirstPanel, "FirstPanel", "Keep " }, + { GuiSplitContainer::SecondPanel, "SecondPanel" } +EndImplementEnumType; + + +//----------------------------------------------------------------------------- + +GuiSplitContainer::GuiSplitContainer() + : mFixedPanel( None ), + mFixedPanelSize( 100 ), + mOrientation( Vertical ), + mSplitterSize( 2 ), + mSplitPoint( 0, 0 ), + mSplitRect( 0, 0, mSplitterSize, mSplitterSize ), + mDragging( false ) +{ + setMinExtent( Point2I(64,64) ); + setExtent(200,200); + setDocking( Docking::dockNone ); + + mSplitPoint.set( 300, 100 ); + + // We only support client docked items in a split container + mValidDockingMask = Docking::dockClient; +} + +//----------------------------------------------------------------------------- + +void GuiSplitContainer::initPersistFields() +{ + addGroup( "Splitter", "Options to configure split panels contained by this control" ); + + addField( "orientation", TYPEID< Orientation >(), Offset( mOrientation, GuiSplitContainer), + "Whether to split between top and bottom (horizontal) or between left and right (vertical)." ); + addField( "splitterSize", TypeS32, Offset( mSplitterSize, GuiSplitContainer), + "Width of the splitter bar between the two sides. Default is 2." ); + addField( "splitPoint", TypePoint2I, Offset( mSplitPoint, GuiSplitContainer), + "Point on control through which the splitter goes.\n\n" + "Changed relatively if size of control changes." ); + addField( "fixedPanel", TYPEID< FixedPanel >(), Offset( mFixedPanel, GuiSplitContainer), + "Which (if any) side of the splitter to keep at a fixed size." ); + addField( "fixedSize", TypeS32, Offset( mFixedPanelSize, GuiSplitContainer), + "Width of the fixed panel specified by #fixedPanel (if any)." ); + + endGroup( "Splitter" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +bool GuiSplitContainer::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- + +bool GuiSplitContainer::onWake() +{ + if ( !Parent::onWake() ) + return false; + + // Create Panel 1 + if ( empty() ) + { + GuiPanel *newPanel = new GuiPanel(); + AssertFatal( newPanel, "GuiSplitContainer::onAdd - Cannot create subordinate panel #1!" ); + newPanel->registerObject(); + newPanel->setInternalName( "Panel1" ); + newPanel->setDocking( Docking::dockClient ); + addObject( (SimObject*)newPanel ); + } + else + { + GuiContainer *containerCtrl = dynamic_cast( at(0) ); + if ( containerCtrl ) + { + containerCtrl->setInternalName( "Panel1" ); + containerCtrl->setDocking( Docking::dockClient ); + } + } + + if ( size() == 1 ) + { + // Create Panel 2 + GuiPanel *newPanel = new GuiPanel(); + AssertFatal( newPanel, "GuiSplitContainer::onAdd - Cannot create subordinate panel #2!" ); + newPanel->registerObject(); + newPanel->setInternalName( "Panel2" ); + newPanel->setDocking( Docking::dockClient ); + addObject( (SimObject*)newPanel ); + } + else + { + GuiContainer *containerCtrl = dynamic_cast( at(1) ); + if ( containerCtrl ) + { + containerCtrl->setInternalName( "Panel2" ); + containerCtrl->setDocking( Docking::dockClient ); + } + } + + // Has FixedWidth been specified? + if ( mFixedPanelSize == 0 ) + { + // Nope, so try to guess as best we can + GuiContainer *firstPanel = dynamic_cast( at(0) ); + GuiContainer *secondPanel = dynamic_cast( at(1) ); + if ( mFixedPanel == FirstPanel ) + { + if ( mOrientation == Horizontal ) + mFixedPanelSize = firstPanel->getExtent().y; + else + mFixedPanelSize = firstPanel->getExtent().x; + + mSplitPoint = Point2I( mFixedPanelSize, mFixedPanelSize ); + } + else if ( mFixedPanel == SecondPanel ) + { + if ( mOrientation == Horizontal ) + mFixedPanelSize = getExtent().y - secondPanel->getExtent().y; + else + mFixedPanelSize = getExtent().x - secondPanel->getExtent().x; + + mSplitPoint = getExtent() - Point2I( mFixedPanelSize, mFixedPanelSize ); + } + + } + + setUpdateLayout(); + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiSplitContainer::onRender( Point2I offset, const RectI &updateRect ) +{ + Parent::onRender( offset, updateRect ); + + // Only render if we're dragging the splitter + if ( mDragging && mSplitRect.isValidRect() ) + { + // Splitter Rectangle (will adjust positioning only) + RectI splitterRect = mSplitRect; + + // Currently being dragged to Rect + Point2I splitterPoint = localToGlobalCoord( mSplitRect.point ); + splitterRect.point = localToGlobalCoord( mSplitPoint ); + + RectI clientRect = getClientRect(); + clientRect.point = localToGlobalCoord( clientRect.point ); + + if ( mOrientation == Horizontal ) + { + splitterRect.point.y -= mSplitterSize; + splitterRect.point.x = splitterPoint.x; + } + else + { + splitterRect.point.x -= mSplitterSize; + splitterRect.point.y = splitterPoint.y; + } + + RectI oldClip = GFX->getClipRect(); + GFX->setClipRect( clientRect ); + GFX->getDrawUtil()->drawRectFill( splitterRect, mProfile->mFillColorHL ); + GFX->setClipRect( oldClip ); + + } + else + { + RectI splitterRect = mSplitRect; + splitterRect.point += offset; + GFX->getDrawUtil()->drawRectFill( splitterRect, mProfile->mFillColor ); + } +} + +//----------------------------------------------------------------------------- + +Point2I GuiSplitContainer::getMinExtent() const +{ + GuiContainer *panelOne = dynamic_cast( at(0) ); + GuiContainer *panelTwo = dynamic_cast( at(1) ); + + if ( !panelOne || !panelTwo ) + return Parent::getMinExtent(); + + Point2I minExtent = Point2I(0,0); + Point2I panelOneMinExtent = panelOne->getMinExtent(); + Point2I panelTwoMinExtent = panelTwo->getMinExtent(); + + if ( mOrientation == Horizontal ) + { + minExtent.y = 2 * mSplitterSize + panelOneMinExtent.y + panelTwoMinExtent.y; + minExtent.x = getMax( panelOneMinExtent.x, panelTwoMinExtent.x ); + } + else + { + minExtent.x = 2 * mSplitterSize + panelOneMinExtent.x + panelTwoMinExtent.x; + minExtent.y = getMax( panelOneMinExtent.y, panelTwoMinExtent.y ); + } + + return minExtent; +} + +//----------------------------------------------------------------------------- + +void GuiSplitContainer::parentResized( const RectI &oldParentRect, const RectI &newParentRect ) +{ + Parent::parentResized( oldParentRect, newParentRect ); + return; + + // TODO: Is this right James? This isn't needed anymore? + /* + + // GuiSplitContainer overrides parentResized to make sure that the proper fixed frame's width/height + // is not compromised in the call + + if ( size() < 2 ) + return; + + GuiContainer *panelOne = dynamic_cast( at(0) ); + GuiContainer *panelTwo = dynamic_cast( at(1) ); + + AssertFatal( panelOne && panelTwo, "GuiSplitContainer::parentResized - Missing/Invalid Subordinate Controls! Split contained controls must derive from GuiContainer!" ); + + Point2I newDragPos; + if ( mFixedPanel == FirstPanel ) + { + newDragPos = panelOne->getExtent(); + newDragPos += Point2I( mSplitterSize, mSplitterSize ); + } + else if ( mFixedPanel == SecondPanel ) + { + newDragPos = getExtent() - panelTwo->getExtent(); + newDragPos -= Point2I( mSplitterSize, mSplitterSize ); + } + else // None + newDragPos.set( 1, 1); + + RectI clientRect = getClientRect(); + solvePanelConstraints( newDragPos, panelOne, panelTwo, clientRect ); + + setUpdateLayout(); + */ +} + +//----------------------------------------------------------------------------- + +bool GuiSplitContainer::resize( const Point2I &newPosition, const Point2I &newExtent ) +{ + // Save previous extent. + Point2I oldExtent = getExtent(); + + // Resize ourselves. + if ( !Parent::resize( newPosition, newExtent ) || size() < 2 ) + return false; + + GuiContainer *panelOne = dynamic_cast( at(0) ); + GuiContainer *panelTwo = dynamic_cast( at(1) ); + + // + AssertFatal( panelOne && panelTwo, "GuiSplitContainer::resize - Missing/Invalid Subordinate Controls! Split contained controls must derive from GuiContainer!" ); + + // We only need to update the split point if our second panel is fixed. + // If the first is fixed, then we can leave the split point alone because + // the remainder of the size will be added to or taken from the second panel + Point2I newDragPos; + if ( mFixedPanel == SecondPanel ) + { + S32 deltaX = newExtent.x - oldExtent.x; + S32 deltaY = newExtent.y - oldExtent.y; + + if( mOrientation == Horizontal ) + mSplitPoint.y += deltaY; + else + mSplitPoint.x += deltaX; + } + + // If we got here, parent returned true + return true; +} + +//----------------------------------------------------------------------------- + +bool GuiSplitContainer::layoutControls( RectI &clientRect ) +{ + if ( size() < 2 ) + return false; + + GuiContainer *panelOne = dynamic_cast( at(0) ); + GuiContainer *panelTwo = dynamic_cast( at(1) ); + + // + AssertFatal( panelOne && panelTwo, "GuiSplitContainer::layoutControl - Missing/Invalid Subordinate Controls! Split contained controls must derive from GuiContainer!" ); + + RectI panelOneRect = RectI( clientRect.point, Point2I( 0, 0 ) ); + RectI panelTwoRect; + RectI splitRect; + + solvePanelConstraints( getSplitPoint(), panelOne, panelTwo, clientRect ); + + switch( mOrientation ) + { + case Horizontal: + panelOneRect.extent = Point2I( clientRect.extent.x, getSplitPoint().y ); + panelTwoRect = panelOneRect; + panelTwoRect.intersect( clientRect ); + panelTwoRect.point.y = panelOneRect.extent.y; + panelTwoRect.extent.y = clientRect.extent.y - panelOneRect.extent.y; + + // Generate new Splitter Rectangle + splitRect = panelTwoRect; + splitRect.extent.y = 0; + splitRect.inset( 0, -mSplitterSize ); + + panelOneRect.extent.y -= mSplitterSize; + panelTwoRect.point.y += mSplitterSize; + panelTwoRect.extent.y -= mSplitterSize; + + break; + + case Vertical: + panelOneRect.extent = Point2I( getSplitPoint().x, clientRect.extent.y ); + panelTwoRect = panelOneRect; + panelTwoRect.intersect( clientRect ); + panelTwoRect.point.x = panelOneRect.extent.x; + panelTwoRect.extent.x = clientRect.extent.x - panelOneRect.extent.x; + + // Generate new Splitter Rectangle + splitRect = panelTwoRect; + splitRect.extent.x = 0; + splitRect.inset( -mSplitterSize, 0 ); + + panelOneRect.extent.x -= mSplitterSize; + panelTwoRect.point.x += mSplitterSize; + panelTwoRect.extent.x -= mSplitterSize; + + break; + } + + // Update Split Rect + mSplitRect = splitRect; + + // Dock Appropriately + if( !( mFixedPanel == FirstPanel && !panelOne->isVisible() ) ) + dockControl( panelOne, panelOne->getDocking(), panelOneRect ); + if( !( mFixedPanel == FirstPanel && !panelTwo->isVisible() ) ) + dockControl( panelTwo, panelOne->getDocking(), panelTwoRect ); + + // + return false; +} + +//----------------------------------------------------------------------------- + +void GuiSplitContainer::solvePanelConstraints( Point2I newDragPos, GuiContainer * firstPanel, GuiContainer * secondPanel, RectI clientRect ) +{ + if( !firstPanel || !secondPanel ) + return; + + if ( mOrientation == Horizontal ) + { + // Constrain based on Y axis (Horizontal Splitter) + + // This accounts for the splitter width + S32 splitterSize = (S32)(mSplitRect.extent.y * 0.5); + + // Collapsed fixed panel + if ( mFixedPanel == SecondPanel && !secondPanel->isVisible() ) + { + newDragPos = Point2I(mSplitPoint.x, getExtent().y - splitterSize ); + } + else if( mFixedPanel == SecondPanel && !firstPanel->isVisible() ) + { + newDragPos = Point2I(mSplitPoint.x, splitterSize ); + } + else // Normal constraints + { + //newDragPos.y -= splitterSize; + S32 newPosition = mClamp( newDragPos.y, + firstPanel->getMinExtent().y + splitterSize, + getExtent().y - secondPanel->getMinExtent().y - splitterSize ); + newDragPos = Point2I( mSplitPoint.x, newPosition ); + } + } + else + { + // Constrain based on X axis (Vertical Splitter) + + // This accounts for the splitter width + S32 splitterSize = (S32)(mSplitRect.extent.x * 0.5); + + // Collapsed fixed panel + if ( mFixedPanel == SecondPanel && !secondPanel->isVisible() ) + { + newDragPos = Point2I(getExtent().x - splitterSize, mSplitPoint.y ); + } + else if ( mFixedPanel == FirstPanel && !firstPanel->isVisible() ) + { + newDragPos = Point2I( splitterSize, mSplitPoint.x ); + } + else // Normal constraints + { + S32 newPosition = mClamp( newDragPos.x, firstPanel->getMinExtent().x + splitterSize, + getExtent().x - secondPanel->getMinExtent().x - splitterSize ); + newDragPos = Point2I( newPosition, mSplitPoint.y ); + } + } + + // Just in case, clamp to bounds of controls + newDragPos.x = mClamp( newDragPos.x, clientRect.point.x, clientRect.point.x + clientRect.extent.x ); + newDragPos.y = mClamp( newDragPos.y, clientRect.point.y, clientRect.point.y + clientRect.extent.y ); + + mSplitPoint = newDragPos; +} + +//----------------------------------------------------------------------------- + +void GuiSplitContainer::getCursor( GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent ) +{ + GuiCanvas *rootCtrl = getRoot(); + if ( !rootCtrl ) + return; + + S32 desiredCursor = 0; + RectI splitRect = getSplitRect(); + + // Figure out which cursor we want if we need one + if ( mOrientation == Horizontal ) + desiredCursor = PlatformCursorController::curResizeHorz; + else if ( mOrientation == Vertical ) + desiredCursor = PlatformCursorController::curResizeVert; + + PlatformWindow *platformWindow = static_cast(getRoot())->getPlatformWindow(); + AssertFatal( platformWindow != NULL,"GuiControl without owning platform window! This should not be possible." ); + + PlatformCursorController *cusrorController = platformWindow->getCursorController(); + AssertFatal( cusrorController != NULL,"PlatformWindow without an owned CursorController!" ); + + // Check to see if we need one or just the default... + + Point2I localPoint = Point2I( globalToLocalCoord( lastGuiEvent.mousePoint ) ); + if ( splitRect.pointInRect( localPoint ) || mDragging ) + { + // Do we need to change it or is it already set? + if ( rootCtrl->mCursorChanged != desiredCursor ) + { + // We've already changed the cursor, so set it back + if ( rootCtrl->mCursorChanged != -1 ) + cusrorController->popCursor(); + + // Now change the cursor shape + cusrorController->pushCursor( desiredCursor ); + rootCtrl->mCursorChanged = desiredCursor; + } + } + else if ( rootCtrl->mCursorChanged != -1 ) + { + // Just the default + cusrorController->popCursor(); + rootCtrl->mCursorChanged = -1; + } +} + +//----------------------------------------------------------------------------- + +void GuiSplitContainer::onMouseDown( const GuiEvent &event ) +{ + GuiContainer *firstPanel = dynamic_cast(at(0)); + GuiContainer *secondPanel = dynamic_cast(at(1)); + + // This function will constrain the panels to their minExtents and update the mSplitPoint + if ( firstPanel && secondPanel ) + { + mouseLock(); + mDragging = true; + + RectI clientRect = getClientRect(); + Point2I newDragPos = globalToLocalCoord( event.mousePoint ); + + solvePanelConstraints(newDragPos, firstPanel, secondPanel, clientRect); + } +} + +//----------------------------------------------------------------------------- + +void GuiSplitContainer::onMouseUp( const GuiEvent &event ) +{ + // If we've been dragging, we need to update the fixed panel extent. + // NOTE : This should ONLY ever happen in this function. the Fixed panel + // is to REMAIN FIXED unless the user changes it. + if ( mDragging ) + { + Point2I newSplitPoint = getSplitPoint(); + + // Update Fixed Panel Extent + if ( mFixedPanel == FirstPanel ) + mFixedPanelSize = ( mOrientation == Horizontal ) ? newSplitPoint.y : newSplitPoint.x; + else + mFixedPanelSize = ( mOrientation == Horizontal ) ? getExtent().y - newSplitPoint.y : getExtent().x - newSplitPoint.x; + + setUpdateLayout(); + } + + mDragging = false; + mouseUnlock(); +} + +//----------------------------------------------------------------------------- + +void GuiSplitContainer::onMouseDragged( const GuiEvent &event ) +{ + GuiContainer *firstPanel = dynamic_cast(at(0)); + GuiContainer *secondPanel = dynamic_cast(at(1)); + + // This function will constrain the panels to their minExtents and update the mSplitPoint + if ( mDragging && firstPanel && secondPanel ) + { + RectI clientRect = getClientRect(); + Point2I newDragPos = globalToLocalCoord( event.mousePoint ); + + solvePanelConstraints(newDragPos, firstPanel, secondPanel, clientRect); + } +} diff --git a/Engine/source/gui/containers/guiSplitContainer.h b/Engine/source/gui/containers/guiSplitContainer.h new file mode 100644 index 000000000..9e0bb0afe --- /dev/null +++ b/Engine/source/gui/containers/guiSplitContainer.h @@ -0,0 +1,110 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _GUI_SPLTCONTAINER_H_ +#define _GUI_SPLTCONTAINER_H_ + +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif +#ifndef _GUICONTAINER_H_ +#include "gui/containers/guiContainer.h" +#endif +#ifndef _GUI_PANEL_H_ +#include "gui/containers/guiPanel.h" +#endif +#ifndef _PLATFORMINPUT_H_ +#include "platform/platformInput.h" +#endif + + + +/// @addtogroup gui_container_group Containers +/// +/// @ingroup gui_group Gui System +/// @{ +class GuiSplitContainer : public GuiContainer +{ + typedef GuiContainer Parent; +public: + + enum Orientation + { + Vertical = 0, + Horizontal = 1 + }; + + enum FixedPanel + { + None = 0, + FirstPanel = 1, + SecondPanel + }; + + GuiSplitContainer(); + + DECLARE_CONOBJECT( GuiSplitContainer ); + DECLARE_DESCRIPTION( "A container that splits its area between two child controls.\n" + "The split ratio can be dynamically adjusted with a handle control between the two children.\n" + "Splitting can be either horizontal or vertical." ); + + // ConsoleObject + static void initPersistFields(); + virtual bool onAdd(); + + // GuiControl + virtual bool onWake(); + virtual void parentResized(const RectI &oldParentRect, const RectI &newParentRect); + virtual bool resize( const Point2I &newPosition, const Point2I &newExtent ); + virtual void onRender(Point2I offset, const RectI &updateRect); + virtual void onMouseDown(const GuiEvent &event); + virtual void onMouseUp(const GuiEvent &event); + virtual void onMouseDragged(const GuiEvent &event); + + virtual bool layoutControls( RectI &clientRect ); + virtual void getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent); + virtual inline Point2I getSplitPoint() { return mSplitPoint; }; + /// The Splitters entire Client Rectangle, this takes into account padding of this control + virtual inline RectI getSplitRect() { return mSplitRect; }; + virtual void solvePanelConstraints( Point2I newDragPos, GuiContainer * firstPanel, GuiContainer * secondPanel, RectI clientRect ); + virtual Point2I getMinExtent() const; + +protected: + + S32 mFixedPanel; + S32 mFixedPanelSize; + S32 mOrientation; + S32 mSplitterSize; + Point2I mSplitPoint; + RectI mSplitRect; + bool mDragging; + +}; + +typedef GuiSplitContainer::Orientation GuiSplitOrientation; +typedef GuiSplitContainer::FixedPanel GuiSplitFixedPanel; + +DefineEnumType( GuiSplitOrientation ); +DefineEnumType( GuiSplitFixedPanel ); + +/// @} + +#endif // _GUI_SPLTCONTAINER_H_ \ No newline at end of file diff --git a/Engine/source/gui/containers/guiStackCtrl.cpp b/Engine/source/gui/containers/guiStackCtrl.cpp new file mode 100644 index 000000000..3d587f8d1 --- /dev/null +++ b/Engine/source/gui/containers/guiStackCtrl.cpp @@ -0,0 +1,388 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/engineAPI.h" +#include "gui/containers/guiStackCtrl.h" + +IMPLEMENT_CONOBJECT(GuiStackControl); + +ConsoleDocClass( GuiStackControl, + "@brief A container that stacks its children horizontally or vertically.\n\n" + + "This container maintains a horizontal or vertical stack of GUI controls. If " + "one is added, deleted, or resized, then the stack is resized to fit. The " + "order of the stack is determined by the internal order of the children (ie. " + "the order of addition).
      " + + "@tsexample\n" + "new GuiStackControl()\n" + "{\n" + " stackingType = \"Dynamic\";\n" + " horizStacking = \"Left to Right\";\n" + " vertStacking = \"Top to Bottom\";\n" + " padding = \"4\";\n" + " dynamicSize = \"1\";\n" + " dynamicNonStackExtent = \"0\";\n" + " dynamicPos = \"0\";\n" + " changeChildSizeToFit = \"1\";\n" + " changeChildPosition = \"1\";\n" + " //Properties not specific to this control have been omitted from this example.\n" + "};\n" + "@endtsexample\n\n" + + "@ingroup GuiContainers" +); + +ImplementEnumType( GuiStackingType, + "Stacking method used to position child controls.\n\n" + "@ingroup GuiContainers" ) + { GuiStackControl::stackingTypeVert, "Vertical", "Stack children vertically by setting their Y position" }, + { GuiStackControl::stackingTypeHoriz,"Horizontal", "Stack children horizontall by setting their X position" }, + { GuiStackControl::stackingTypeDyn,"Dynamic", "Automatically switch between " + "Vertical and Horizontal stacking. Vertical stacking is chosen when the " + "stack control is taller than it is wide, horizontal stacking is chosen " + "when the stack control is wider than it is tall." } +EndImplementEnumType; + +ImplementEnumType( GuiHorizontalStackingType, + "Determines how child controls are stacked horizontally.\n\n" + "@ingroup GuiContainers" ) + { GuiStackControl::horizStackLeft, "Left to Right", "Child controls are positioned in order from left to right (left-most control is first)" }, + { GuiStackControl::horizStackRight,"Right to Left", "Child controls are positioned in order from right to left (right-most control is first)" } +EndImplementEnumType; + +ImplementEnumType( GuiVerticalStackingType, + "Determines how child controls are stacked vertically.\n\n" + "@ingroup GuiContainers" ) + { GuiStackControl::vertStackTop, "Top to Bottom", "Child controls are positioned in order from top to bottom (top-most control is first)" }, + { GuiStackControl::vertStackBottom,"Bottom to Top", "Child controls are positioned in order from bottom to top (bottom-most control is first)" } +EndImplementEnumType; + + +GuiStackControl::GuiStackControl() +{ + setMinExtent(Point2I(16,16)); + mResizing = false; + mStackingType = stackingTypeVert; + mStackVertSizing = vertStackTop; + mStackHorizSizing = horizStackLeft; + mPadding = 0; + mIsContainer = true; + mDynamicSize = true; + mDynamicNonStackExtent = false; + mDynamicPos = false; + mChangeChildSizeToFit = true; + mChangeChildPosition = true; +} + +void GuiStackControl::initPersistFields() +{ + + addGroup( "Stacking" ); + addField( "stackingType", TYPEID< StackingType >(), Offset(mStackingType, GuiStackControl), + "Determines the method used to position the child controls.\n\n" ); + + addField( "horizStacking", TYPEID< HorizontalType >(), Offset(mStackHorizSizing, GuiStackControl), + "Controls the type of horizontal stacking to use (Left to Right or " + "Right to Left)" ); + + addField( "vertStacking", TYPEID< VerticalType >(), Offset(mStackVertSizing, GuiStackControl), + "Controls the type of vertical stacking to use (Top to Bottom or " + "Bottom to Top)" ); + + addField( "padding", TypeS32, Offset(mPadding, GuiStackControl), + "Distance (in pixels) between stacked child controls." ); + + addField( "dynamicSize", TypeBool, Offset(mDynamicSize, GuiStackControl), + "Determines whether to resize the stack control along the stack axis (change " + "width for horizontal stacking, change height for vertical stacking).\n\n" + "If true, the stack width/height will be resized to the sum of the child control widths/heights. " + "If false, the stack will not be resized." ); + + addField( "dynamicNonStackExtent", TypeBool, Offset(mDynamicNonStackExtent, GuiStackControl), + "Determines whether to resize the stack control along the non-stack axis (change " + "height for horizontal stacking, change width for vertical stacking). No effect " + "if dynamicSize is false.\n\n" + "If true, the stack will be resized to the maximum of the child control widths/heights. " + "If false, the stack will not be resized." ); + + addField( "dynamicPos", TypeBool, Offset(mDynamicPos, GuiStackControl), + "Determines whether to reposition the stack along the stack axis when it is " + "auto-resized. No effect if dynamicSize is false.\n\n" + "If true, the stack will grow left for horizontal stacking, and grow up for vertical stacking.\n" + "If false, the stack will grow right for horizontal stacking, and grow down for vertical stacking.\n" ); + + addField( "changeChildSizeToFit", TypeBool, Offset(mChangeChildSizeToFit, GuiStackControl), + "Determines whether to resize child controls.\n\n" + "If true, horizontally stacked children keep their width, but have their " + "height set to the stack control height. Vertically stacked children keep " + "their height, but have their width set to the stack control width. If " + "false, child controls are not resized." ); + + addField( "changeChildPosition", TypeBool, Offset(mChangeChildPosition, GuiStackControl), + "Determines whether to reposition child controls.\n\n" + "If true, horizontally stacked children are aligned along the top edge of " + "the stack control. Vertically stacked children are aligned along the left " + "edge of the stack control. If false, horizontally stacked children retain " + "their Y position, and vertically stacked children retain their X position." ); + endGroup( "Stacking" ); + + Parent::initPersistFields(); +} + +DefineEngineMethod( GuiStackControl, isFrozen, bool, (),, + "Return whether or not this control is frozen" ) +{ + return object->isFrozen(); +} + +DefineEngineMethod( GuiStackControl, freeze, void, ( bool freeze ),, + "Prevents control from restacking - useful when adding or removing child controls\n" + "@param freeze True to freeze the control, false to unfreeze it\n\n" + "@tsexample\n" + "%stackCtrl.freeze(true);\n" + "// add controls to stack\n" + "%stackCtrl.freeze(false);\n" + "@endtsexample\n" ) +{ + object->freeze( freeze ); +} + +DefineEngineMethod( GuiStackControl, updateStack, void, (),, + "Restack the child controls.\n" ) +{ + object->updatePanes(); +} + +bool GuiStackControl::onWake() +{ + if ( !Parent::onWake() ) + return false; + + updatePanes(); + + return true; +} + +void GuiStackControl::onSleep() +{ + Parent::onSleep(); +} + +void GuiStackControl::updatePanes() +{ + // Prevent recursion + if(mResizing) + return; + + // Set Resizing. + mResizing = true; + + Point2I extent = getExtent(); + + // Do we need to stack horizontally? + if( ( extent.x > extent.y && mStackingType == stackingTypeDyn ) || mStackingType == stackingTypeHoriz ) + { + stackHorizontal( mStackHorizSizing == horizStackLeft ); + } + // Or, vertically? + else if( ( extent.y > extent.x && mStackingType == stackingTypeDyn ) || mStackingType == stackingTypeVert) + { + stackVertical( mStackVertSizing == vertStackTop ); + } + + // Clear Sizing Flag. + mResizing = false; +} + +void GuiStackControl::freeze(bool _shouldfreeze) +{ + mResizing = _shouldfreeze; +} + +void GuiStackControl::stackVertical(bool fromTop) +{ + if( empty() ) + return; + + S32 begin, end, step; + if ( fromTop ) + { + // Stack from Child0 at top to ChildN at bottom + begin = 0; + end = size(); + step = 1; + } + else + { + // Stack from ChildN at top to Child0 at bottom + begin = size()-1; + end = -1; + step = -1; + } + + // Place each visible child control + S32 maxWidth = 0; + Point2I curPos(0, 0); + for ( S32 i = begin; i != end; i += step ) + { + GuiControl * gc = dynamic_cast( at(i) ); + if ( gc && gc->isVisible() ) + { + // Add padding between controls + if ( curPos.y > 0 ) + curPos.y += mPadding; + + Point2I childPos = curPos; + if ( !mChangeChildPosition ) + childPos.x = gc->getLeft(); + + Point2I childSize( gc->getExtent() ); + if ( mChangeChildSizeToFit ) + childSize.x = getWidth(); + + gc->resize( childPos, childSize ); + + curPos.y += gc->getHeight(); + maxWidth = getMax( maxWidth, childPos.x + childSize.x ); + } + } + + if ( mDynamicSize ) + { + // Conform our size to the sum of the child sizes. + Point2I newPos( getPosition() ); + Point2I newSize( mDynamicNonStackExtent ? maxWidth : getWidth(), curPos.y ); + + newSize.setMax( getMinExtent() ); + + // Grow the stack up instead of down? + if ( mDynamicPos ) + newPos.y -= ( newSize.y - getHeight() ); + + resize( newPos, newSize ); + } +} + +void GuiStackControl::stackHorizontal(bool fromLeft) +{ + if( empty() ) + return; + + S32 begin, end, step; + if ( fromLeft ) + { + // Stack from Child0 at left to ChildN at right + begin = 0; + end = size(); + step = 1; + } + else + { + // Stack from ChildN at left to Child0 at right + begin = size()-1; + end = -1; + step = -1; + } + + // Place each visible child control + S32 maxHeight = 0; + Point2I curPos(0, 0); + for ( S32 i = begin; i != end; i += step ) + { + GuiControl * gc = dynamic_cast( at(i) ); + if ( gc && gc->isVisible() ) + { + // Add padding between controls + if ( curPos.x > 0 ) + curPos.x += mPadding; + + Point2I childPos = curPos; + if ( !mChangeChildPosition ) + childPos.y = gc->getTop(); + + Point2I childSize( gc->getExtent() ); + if ( mChangeChildSizeToFit ) + childSize.y = getHeight(); + + gc->resize( childPos, childSize ); + + curPos.x += gc->getWidth(); + maxHeight = getMax( maxHeight, childPos.y + childSize.y ); + } + } + + if ( mDynamicSize ) + { + // Conform our size to the sum of the child sizes. + Point2I newPos( getPosition() ); + Point2I newSize( curPos.x, mDynamicNonStackExtent ? maxHeight : getHeight() ); + + newSize.setMax( getMinExtent() ); + + // Grow the stack left instead of right? + if ( mDynamicPos ) + newPos.x -= ( newSize.x - getWidth() ); + + resize( newPos, newSize ); + } +} + +bool GuiStackControl::resize(const Point2I &newPosition, const Point2I &newExtent) +{ + if( !Parent::resize( newPosition, newExtent ) ) + return false; + + updatePanes(); + + // CodeReview This logic should be updated to correctly return true/false + // based on whether it sized it's children. [7/1/2007 justind] + return true; +} + +void GuiStackControl::addObject(SimObject *obj) +{ + Parent::addObject(obj); + + updatePanes(); +} + +void GuiStackControl::removeObject(SimObject *obj) +{ + Parent::removeObject(obj); + + updatePanes(); +} + +bool GuiStackControl::reOrder(SimObject* obj, SimObject* target) +{ + bool ret = Parent::reOrder(obj, target); + if (ret) + updatePanes(); + + return ret; +} + +void GuiStackControl::childResized(GuiControl *child) +{ + updatePanes(); +} \ No newline at end of file diff --git a/Engine/source/gui/containers/guiStackCtrl.h b/Engine/source/gui/containers/guiStackCtrl.h new file mode 100644 index 000000000..9b3209ddb --- /dev/null +++ b/Engine/source/gui/containers/guiStackCtrl.h @@ -0,0 +1,112 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUISTACKCTRL_H_ +#define _GUISTACKCTRL_H_ + +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif + +#include "gfx/gfxDevice.h" +#include "console/console.h" +#include "console/consoleTypes.h" + +/// A stack of GUI controls. +/// +/// This maintains a horizontal or vertical stack of GUI controls. If one is deleted, or +/// resized, then the stack is resized to fit. The order of the stack is +/// determined by the internal order of the children (ie, order of addition). +class GuiStackControl : public GuiControl +{ +protected: + typedef GuiControl Parent; + bool mResizing; + S32 mPadding; + S32 mStackHorizSizing; ///< Set from horizSizingOptions. + S32 mStackVertSizing; ///< Set from vertSizingOptions. + S32 mStackingType; + bool mDynamicSize; ///< Resize this control along the stack axis to fit the summed extent of the children (width or height depends on the stack type) + bool mDynamicNonStackExtent; ///< Resize this control along the non-stack axis to fit the max extent of the children (width or height depends on the stack type) + bool mDynamicPos; ///< Reposition this control along the stack axis when it is resized (by mDynamicSize) (left or up depends on the stack type) + bool mChangeChildSizeToFit; ///< Does the child resize to fit i.e. should a horizontal stack resize its children's height to fit? + bool mChangeChildPosition; ///< Do we reset the child's position in the opposite direction we are stacking? + +public: + GuiStackControl(); + + enum StackingType + { + stackingTypeVert, ///< Always stack vertically + stackingTypeHoriz, ///< Always stack horizontally + stackingTypeDyn ///< Dynamically switch based on width/height + }; + + enum HorizontalType + { + horizStackLeft = 0,///< Stack from left to right when horizontal + horizStackRight, ///< Stack from right to left when horizontal + }; + + enum VerticalType + { + vertStackTop, ///< Stack from top to bottom when vertical + vertStackBottom, ///< Stack from bottom to top when vertical + }; + + bool resize(const Point2I &newPosition, const Point2I &newExtent); + void childResized(GuiControl *child); + bool isFrozen() { return mResizing; }; + /// prevent resizing. useful when adding many items. + void freeze(bool); + + bool onWake(); + void onSleep(); + + void updatePanes(); + + virtual void stackVertical(bool fromTop); + virtual void stackHorizontal(bool fromLeft); + + S32 getCount() { return size(); }; /// Returns the number of children in the stack + + void addObject(SimObject *obj); + void removeObject(SimObject *obj); + + bool reOrder(SimObject* obj, SimObject* target = 0); + + static void initPersistFields(); + + DECLARE_CONOBJECT(GuiStackControl); + DECLARE_CATEGORY( "Gui Containers" ); + DECLARE_DESCRIPTION( "A container that stacks its children horizontally or vertically." ); +}; + +typedef GuiStackControl::StackingType GuiStackingType; +typedef GuiStackControl::HorizontalType GuiHorizontalStackingType; +typedef GuiStackControl::VerticalType GuiVerticalStackingType; + +DefineEnumType( GuiStackingType ); +DefineEnumType( GuiHorizontalStackingType ); +DefineEnumType( GuiVerticalStackingType ); + +#endif \ No newline at end of file diff --git a/Engine/source/gui/containers/guiTabBookCtrl.cpp b/Engine/source/gui/containers/guiTabBookCtrl.cpp new file mode 100644 index 000000000..59b9e7e71 --- /dev/null +++ b/Engine/source/gui/containers/guiTabBookCtrl.cpp @@ -0,0 +1,982 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/containers/guiTabBookCtrl.h" +#include "console/engineAPI.h" +#include "gui/core/guiCanvas.h" +#include "gui/editor/guiEditCtrl.h" +#include "gui/controls/guiPopUpCtrl.h" +#include "gui/core/guiDefaultControlRender.h" +#include "gfx/gfxDrawUtil.h" + + +IMPLEMENT_CONOBJECT( GuiTabBookCtrl ); + +ConsoleDocClass( GuiTabBookCtrl, + "@brief A container \n\n" + + "@tsexample\n" + "// Create \n" + "@endtsexample\n\n" + + "@note Only GuiTabPageCtrls must be added to GuiTabBookCtrls. If an object of a different " + "class is added to the control, it will be reassigned to either the active page or the " + "tab book's parent.\n\n" + + "@see GuiTabPageCtrl\n" + + "@ingroup GuiContainers" +); + +ImplementEnumType( GuiTabPosition, + "Where the control should put the tab headers for selecting individual pages.\n\n" + "@ingroup GuiContainers" ) + { GuiTabBookCtrl::AlignTop, "Top", "Tab headers on top edge." }, + { GuiTabBookCtrl::AlignBottom,"Bottom", "Tab headers on bottom edge." } +EndImplementEnumType; + +IMPLEMENT_CALLBACK( GuiTabBookCtrl, onTabSelected, void, ( const String& text, U32 index ), ( text, index ), + "Called when a new tab page is selected.\n\n" + "@param text Text of the page header for the tab that is being selected.\n" + "@param index Index of the tab page being selected." ); +IMPLEMENT_CALLBACK( GuiTabBookCtrl, onTabRightClick, void, ( const String& text, U32 index ), ( text, index ), + "Called when the user right-clicks on a tab page header.\n\n" + "@param text Text of the page header for the tab that is being selected.\n" + "@param index Index of the tab page being selected." ); + + +//----------------------------------------------------------------------------- + +GuiTabBookCtrl::GuiTabBookCtrl() +{ + VECTOR_SET_ASSOCIATION( mPages ); + + mTabHeight = 24; + mTabPosition = AlignTop; + mActivePage = NULL; + mHoverTab = NULL; + mHasTexture = false; + mBitmapBounds = NULL; + setExtent( 400, 300 ); + mPageRect = RectI(0,0,0,0); + mTabRect = RectI(0,0,0,0); + mFrontTabPadding = 0; + + mPages.reserve(12); + mTabMargin = 7; + mMinTabWidth = 64; + mIsContainer = true; + mSelectedPageNum = -1; + mDefaultPageNum = -1; + + mAllowReorder = false; + mDraggingTab = false; + mDraggingTabRect = false; + mIsFirstWake = true; +} + +//----------------------------------------------------------------------------- + +void GuiTabBookCtrl::initPersistFields() +{ + addGroup( "TabBook" ); + + addField( "tabPosition", TYPEID< TabPosition >(), Offset( mTabPosition, GuiTabBookCtrl ), + "Where to place the tab page headers." ); + addField( "tabMargin", TypeS32, Offset( mTabMargin, GuiTabBookCtrl ), + "Spacing to put between individual tab page headers." ); + addField( "minTabWidth", TypeS32, Offset( mMinTabWidth, GuiTabBookCtrl ), + "Minimum width allocated to a tab page header." ); + addField( "tabHeight", TypeS32, Offset( mTabHeight, GuiTabBookCtrl ), + "Height of tab page headers." ); + addField( "allowReorder", TypeBool, Offset( mAllowReorder, GuiTabBookCtrl ), + "Whether reordering tabs with the mouse is allowed." ); + addField( "defaultPage", TypeS32, Offset( mDefaultPageNum, GuiTabBookCtrl ), + "Index of page to select on first onWake() call (-1 to disable)." ); + + addProtectedField( "selectedPage", TypeS32, Offset( mSelectedPageNum, GuiTabBookCtrl ), + &_setSelectedPage, &defaultProtectedGetFn, + "Index of currently selected page." ); + + addField( "frontTabPadding", TypeS32, Offset( mFrontTabPadding, GuiTabBookCtrl ), + "X offset of first tab page header." ); + + endGroup( "TabBook" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +void GuiTabBookCtrl::onChildRemoved( GuiControl* child ) +{ + for (S32 i = 0; i < mPages.size(); i++ ) + { + GuiTabPageCtrl* tab = mPages[i].Page; + if( tab == child ) + { + mPages.erase( i ); + break; + } + } + + // Calculate Page Information + calculatePageTabs(); + + // Active Index. + mSelectedPageNum = getMin( mSelectedPageNum, mPages.size() - 1 ); + + if ( mSelectedPageNum != -1 ) + { + // Select Page. + selectPage( mSelectedPageNum ); + } +} + +//----------------------------------------------------------------------------- + +void GuiTabBookCtrl::onChildAdded( GuiControl *child ) +{ + GuiTabPageCtrl *page = dynamic_cast(child); + if( !page ) + { + Con::warnf("GuiTabBookCtrl::onChildAdded - attempting to add NON GuiTabPageCtrl as child page"); + SimObject *simObj = reinterpret_cast(child); + removeObject( simObj ); + if( mActivePage ) + { + mActivePage->addObject( simObj ); + } + else + { + Con::warnf("GuiTabBookCtrl::onChildAdded - unable to find active page to reassign ownership of new child control to, placing on parent"); + GuiControl *rent = getParent(); + if( rent ) + rent->addObject( simObj ); + } + return; + } + + TabHeaderInfo newPage; + + newPage.Page = page; + newPage.TabRow = -1; + newPage.TabColumn = -1; + + mPages.push_back( newPage ); + + // Calculate Page Information + calculatePageTabs(); + + if( page->getFitBook() ) + fitPage( page ); + + // Select this Page + selectPage( page ); +} + +//----------------------------------------------------------------------------- + +bool GuiTabBookCtrl::reOrder(SimObject* obj, SimObject* target) +{ + if ( !Parent::reOrder(obj, target) ) + return false; + + // Store the Selected Page. + GuiTabPageCtrl *selectedPage = NULL; + if ( mSelectedPageNum != -1 ) + selectedPage = mPages[mSelectedPageNum].Page; + + // Determine the Target Page Index. + S32 targetIndex = -1; + for( S32 i = 0; i < mPages.size(); i++ ) + { + const TabHeaderInfo &info = mPages[i]; + if ( info.Page == target ) + { + targetIndex = i; + break; + } + } + + if ( targetIndex == -1 ) + { + return false; + } + + for( S32 i = 0; i < mPages.size(); i++ ) + { + const TabHeaderInfo &info = mPages[i]; + if ( info.Page == obj ) + { + // Store Info. + TabHeaderInfo objPage = info; + + // Remove. + mPages.erase( i ); + // Insert. + mPages.insert( targetIndex, objPage ); + + break; + } + } + + // Update Tabs. + calculatePageTabs(); + + // Reselect Page. + selectPage( selectedPage ); + + return true; +} + +//----------------------------------------------------------------------------- + +bool GuiTabBookCtrl::acceptsAsChild( SimObject* object ) const +{ + // Only accept tab pages. + return ( dynamic_cast< GuiTabPageCtrl* >( object ) != NULL ); +} + +//----------------------------------------------------------------------------- + +bool GuiTabBookCtrl::onWake() +{ + if (! Parent::onWake()) + return false; + + mHasTexture = mProfile->constructBitmapArray() > 0; + if( mHasTexture ) + { + mBitmapBounds = mProfile->mBitmapArrayRects.address(); + mTabHeight = mBitmapBounds[TabSelected].extent.y; + } + + calculatePageTabs(); + + if( mIsFirstWake ) + { + // Awaken all pages, visible or not. We need to do this so + // any pages that make use of a language table for their label + // are correctly initialized. + for ( U32 i = 0; i < mPages.size(); ++i) + { + if ( !mPages[i].Page->isAwake() ) + { + mPages[i].Page->awaken(); + } + } + + if( mDefaultPageNum >= 0 && mDefaultPageNum < mPages.size() ) + selectPage( mDefaultPageNum ); + + mIsFirstWake = false; + } + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiTabBookCtrl::addNewPage( const char* text ) +{ + GuiTabPageCtrl* page = new GuiTabPageCtrl(); + + if( text ) + page->setText( text ); + + page->registerObject(); + addObject( page ); +} + +//----------------------------------------------------------------------------- + +bool GuiTabBookCtrl::resize(const Point2I &newPosition, const Point2I &newExtent) +{ + bool result = Parent::resize( newPosition, newExtent ); + + calculatePageTabs(); + + return result; +} + +//----------------------------------------------------------------------------- + +void GuiTabBookCtrl::childResized(GuiControl *child) +{ + Parent::childResized( child ); + + //child->resize( mPageRect.point, mPageRect.extent ); +} + +//----------------------------------------------------------------------------- + +void GuiTabBookCtrl::onMouseDown(const GuiEvent &event) +{ + mDraggingTab = false; + mDraggingTabRect = false; + Point2I localMouse = globalToLocalCoord( event.mousePoint ); + if( mTabRect.pointInRect( localMouse ) ) + { + GuiTabPageCtrl *tab = findHitTab( localMouse ); + if( tab != NULL ) + { + selectPage( tab ); + mDraggingTab = mAllowReorder; + } + else + { + mDraggingTabRect = true; + } + } +} + +//----------------------------------------------------------------------------- + +void GuiTabBookCtrl::onMouseUp(const GuiEvent &event) +{ + Parent::onMouseUp( event ); + + mDraggingTab = false; + mDraggingTabRect = false; +} + +//----------------------------------------------------------------------------- + +void GuiTabBookCtrl::onMouseDragged(const GuiEvent &event) +{ + Parent::onMouseDragged( event ); + + if ( !mDraggingTab ) + return; + + GuiTabPageCtrl *selectedPage = NULL; + if ( mSelectedPageNum != -1 ) + selectedPage = mPages[mSelectedPageNum].Page; + + if ( !selectedPage ) + return; + + Point2I localMouse = globalToLocalCoord( event.mousePoint ); + if( mTabRect.pointInRect( localMouse ) ) + { + GuiTabPageCtrl *tab = findHitTab( localMouse ); + if( tab != NULL && tab != selectedPage ) + { + S32 targetIndex = -1; + for( S32 i = 0; i < mPages.size(); i++ ) + { + if( mPages[i].Page == tab ) + { + targetIndex = i; + break; + } + } + + if ( targetIndex > mSelectedPageNum ) + { + reOrder( tab, selectedPage ); + } + else + { + reOrder( selectedPage, tab ); + } + } + } +} + +//----------------------------------------------------------------------------- + +void GuiTabBookCtrl::onMouseMove(const GuiEvent &event) +{ + Point2I localMouse = globalToLocalCoord( event.mousePoint ); + if( mTabRect.pointInRect( localMouse ) ) + { + GuiTabPageCtrl *tab = findHitTab( localMouse ); + if( tab != NULL && mHoverTab != tab ) + mHoverTab = tab; + else if ( !tab ) + mHoverTab = NULL; + } + Parent::onMouseMove( event ); +} + +//----------------------------------------------------------------------------- + +void GuiTabBookCtrl::onMouseLeave( const GuiEvent &event ) +{ + Parent::onMouseLeave( event ); + + mHoverTab = NULL; +} + +//----------------------------------------------------------------------------- + +bool GuiTabBookCtrl::onMouseDownEditor(const GuiEvent &event, Point2I offset) +{ + bool handled = false; + Point2I localMouse = globalToLocalCoord( event.mousePoint ); + + if( mTabRect.pointInRect( localMouse ) ) + { + GuiTabPageCtrl *tab = findHitTab( localMouse ); + if( tab != NULL ) + { + selectPage( tab ); + handled = true; + } + } + +#ifdef TORQUE_TOOLS + // This shouldn't be called if it's not design time, but check just incase + if ( GuiControl::smDesignTime ) + { + // If we clicked in the editor and our addset is the tab book + // ctrl, select the child ctrl so we can edit it's properties + GuiEditCtrl* edit = GuiControl::smEditorHandle; + if( edit && ( edit->getAddSet() == this ) && mActivePage != NULL ) + edit->select( mActivePage ); + } +#endif + + // Return whether we handled this or not. + return handled; + +} + +//----------------------------------------------------------------------------- + +void GuiTabBookCtrl::onRightMouseUp( const GuiEvent& event ) +{ + Point2I localMouse = globalToLocalCoord( event.mousePoint ); + if( mTabRect.pointInRect( localMouse ) ) + { + GuiTabPageCtrl* tab = findHitTab( localMouse ); + if( tab ) + onTabRightClick_callback( tab->getText(), getPageNum( tab ) ); + } +} + +//----------------------------------------------------------------------------- + +void GuiTabBookCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + RectI tabRect = mTabRect; + tabRect.point += offset; + RectI pageRect = mPageRect; + pageRect.point += offset; + + // We're so nice we'll store the old modulation before we clear it for our rendering! :) + ColorI oldModulation; + GFX->getDrawUtil()->getBitmapModulation( &oldModulation ); + + // Wipe it out + GFX->getDrawUtil()->clearBitmapModulation(); + + Parent::onRender(offset, updateRect); + + // Clip to tab area + RectI savedClipRect = GFX->getClipRect(); + GFX->setClipRect( tabRect ); + + // Render our tabs + renderTabs( offset, tabRect ); + + // Restore Rect. + GFX->setClipRect( savedClipRect ); + + // Restore old modulation + GFX->getDrawUtil()->setBitmapModulation( oldModulation ); +} + +//----------------------------------------------------------------------------- + +void GuiTabBookCtrl::renderTabs( const Point2I &offset, const RectI &tabRect ) +{ + // If the tab size is zero, don't render tabs, + // assuming it's a tab-less book + if( mPages.empty() || mTabHeight <= 0 ) + return; + + for( S32 i = 0; i < mPages.size(); i++ ) + { + const TabHeaderInfo ¤tTabInfo = mPages[i]; + RectI tabBounds = mPages[i].TabRect; + tabBounds.point += offset; + GuiTabPageCtrl *tab = mPages[i].Page; + if( tab != NULL ) + renderTab( tabBounds, tab ); + + // If we're on the last tab, draw the nice end piece + if( i + 1 == mPages.size() ) + { + Point2I tabEndPoint = Point2I(currentTabInfo.TabRect.point.x + currentTabInfo.TabRect.extent.x + offset.x, currentTabInfo.TabRect.point.y + offset.y); + Point2I tabEndExtent = Point2I((tabRect.point.x + tabRect.extent.x) - tabEndPoint.x, currentTabInfo.TabRect.extent.y); + RectI tabEndRect = RectI(tabEndPoint,tabEndExtent); + + GFX->setClipRect( tabEndRect ); + + // As it turns out the last tab can be outside the viewport in which + // case trying to render causes a DX assert. Could be better if + // setClipRect returned a bool. + if ( GFX->getViewport().isValidRect() ) + renderFixedBitmapBordersFilled( tabEndRect, TabEnds + 1, mProfile ); + } + } +} + +//----------------------------------------------------------------------------- + +void GuiTabBookCtrl::renderTab( RectI tabRect, GuiTabPageCtrl *tab ) +{ + StringTableEntry text = tab->getText(); + ColorI oldColor; + + GFX->getDrawUtil()->getBitmapModulation( &oldColor ); + + // Is this a skinned control? + if( mHasTexture && mProfile->mBitmapArrayRects.size() >= 9 ) + { + S32 indexMultiplier = 1; + switch( mTabPosition ) + { + case AlignTop: + case AlignBottom: + + if ( mActivePage == tab ) + indexMultiplier += TabSelected; + else if( mHoverTab == tab ) + indexMultiplier += TabHover; + else + indexMultiplier += TabNormal; + break; + } + + renderFixedBitmapBordersFilled( tabRect, indexMultiplier, mProfile ); + } + else + { + // If this isn't a skinned control or the bitmap is simply missing, handle it WELL + if ( mActivePage == tab ) + GFX->getDrawUtil()->drawRectFill(tabRect, mProfile->mFillColor); + else if( mHoverTab == tab ) + GFX->getDrawUtil()->drawRectFill(tabRect, mProfile->mFillColorHL); + else + GFX->getDrawUtil()->drawRectFill(tabRect, mProfile->mFillColorNA); + + } + + + GFX->getDrawUtil()->setBitmapModulation(mProfile->mFontColor); + + switch( mTabPosition ) + { + case AlignTop: + case AlignBottom: + renderJustifiedText( tabRect.point, tabRect.extent, text); + break; + } + + GFX->getDrawUtil()->setBitmapModulation( oldColor); + +} + +//----------------------------------------------------------------------------- + +void GuiTabBookCtrl::setUpdate() +{ + Parent::setUpdate(); + + setUpdateRegion(Point2I(0,0), getExtent()); + + calculatePageTabs(); +} + +//----------------------------------------------------------------------------- + +S32 GuiTabBookCtrl::calculatePageTabWidth( GuiTabPageCtrl *page ) +{ + if( !page ) + return mMinTabWidth; + + const char* text = page->getText(); + + if( !text || dStrlen(text) == 0 || mProfile->mFont == NULL ) + return mMinTabWidth; + + GFont *font = mProfile->mFont; + + return font->getStrNWidth( text, dStrlen(text) ); + +} + +//----------------------------------------------------------------------------- + +const RectI GuiTabBookCtrl::getClientRect() +{ + + if( !mProfile || mProfile->mBitmapArrayRects.size() < NumBitmaps ) + return Parent::getClientRect(); + + return mPageRect; +} + +//----------------------------------------------------------------------------- + +void GuiTabBookCtrl::calculatePageTabs() +{ + // Short Circuit. + // + // If the tab size is zero, don't render tabs, + // assuming it's a tab-less book + if( mPages.empty() || mTabHeight <= 0 ) + { + mPageRect.point.x = 0; + mPageRect.point.y = 0; + mPageRect.extent.x = getWidth(); + mPageRect.extent.y = getHeight(); + return; + } + + S32 currRow = 0; + S32 currColumn = 0; + S32 currX = mFrontTabPadding; + S32 maxWidth = 0; + + for( S32 i = 0; i < mPages.size(); i++ ) + { + // Fetch Tab Width + S32 tabWidth = calculatePageTabWidth( mPages[i].Page ) + ( mTabMargin * 2 ); + tabWidth = getMax( tabWidth, mMinTabWidth ); + TabHeaderInfo &info = mPages[i]; + switch( mTabPosition ) + { + case AlignTop: + case AlignBottom: + // If we're going to go outside our bounds + // with this tab move it down a row + if( currX + tabWidth > getWidth() ) + { + // Calculate and Advance State. + maxWidth = getMax( tabWidth, maxWidth ); + balanceRow( currRow, currX ); + info.TabRow = ++currRow; + // Reset Necessaries + info.TabColumn = currColumn = maxWidth = currX = 0; + } + else + { + info.TabRow = currRow; + info.TabColumn = currColumn++; + } + + // Calculate Tabs Bounding Rect + info.TabRect.point.x = currX; + info.TabRect.extent.x = tabWidth; + info.TabRect.extent.y = mTabHeight; + + // Adjust Y Point based on alignment + if( mTabPosition == AlignTop ) + info.TabRect.point.y = ( info.TabRow * mTabHeight ); + else + info.TabRect.point.y = getHeight() - ( ( 1 + info.TabRow ) * mTabHeight ); + + currX += tabWidth; + break; + }; + } + + currRow++; + currColumn++; + + Point2I localPoint = getExtent(); + + // Calculate + switch( mTabPosition ) + { + case AlignTop: + + localPoint.y -= getTop(); + + mTabRect.point.x = 0; + mTabRect.extent.x = localPoint.x; + mTabRect.point.y = 0; + mTabRect.extent.y = currRow * mTabHeight; + + mPageRect.point.x = 0; + mPageRect.point.y = mTabRect.extent.y; + mPageRect.extent.x = mTabRect.extent.x; + mPageRect.extent.y = getHeight() - mTabRect.extent.y; + + break; + case AlignBottom: + mTabRect.point.x = 0; + mTabRect.extent.x = localPoint.x; + mTabRect.extent.y = currRow * mTabHeight; + mTabRect.point.y = getHeight() - mTabRect.extent.y; + + mPageRect.point.x = 0; + mPageRect.point.y = 0; + mPageRect.extent.x = mTabRect.extent.x; + mPageRect.extent.y = localPoint.y - mTabRect.extent.y; + + break; + } +} + +//----------------------------------------------------------------------------- + +void GuiTabBookCtrl::balanceRow( S32 row, S32 totalTabWidth ) +{ + // Short Circuit. + // + // If the tab size is zero, don't render tabs, + // and assume it's a tab-less tab-book - JDD + if( mPages.empty() || mTabHeight <= 0 ) + return; + + Vector rowTemp; + rowTemp.clear(); + + for( S32 i = 0; i < mPages.size(); i++ ) + { + TabHeaderInfo &info = mPages[i]; + + if(info.TabRow == row ) + rowTemp.push_back( &mPages[i] ); + } + + if( rowTemp.empty() ) + return; + + // Balance the tabs across the remaining space + S32 spaceToDivide = getWidth() - totalTabWidth; + S32 pointDelta = 0; + for( S32 i = 0; i < rowTemp.size(); i++ ) + { + TabHeaderInfo &info = *rowTemp[i]; + S32 extraSpace = (S32)spaceToDivide / ( rowTemp.size() ); + info.TabRect.extent.x += extraSpace; + info.TabRect.point.x += pointDelta; + pointDelta += extraSpace; + } +} + +//----------------------------------------------------------------------------- + +GuiTabPageCtrl *GuiTabBookCtrl::findHitTab( const GuiEvent &event ) +{ + return findHitTab( event.mousePoint ); +} + +//----------------------------------------------------------------------------- + +GuiTabPageCtrl *GuiTabBookCtrl::findHitTab( Point2I hitPoint ) +{ + // Short Circuit. + // + // If the tab size is zero, don't render tabs, + // and assume it's a tab-less tab-book - JDD + if( mPages.empty() || mTabHeight <= 0 ) + return NULL; + + for( S32 i = 0; i < mPages.size(); i++ ) + { + if( mPages[i].TabRect.pointInRect( hitPoint ) ) + return mPages[i].Page; + } + return NULL; +} + +//----------------------------------------------------------------------------- + +void GuiTabBookCtrl::selectPage( S32 index ) +{ + if( mPages.empty() || index < 0 ) + return; + + if( mPages.size() <= index ) + index = mPages.size() - 1; + + // Select the page + selectPage( mPages[ index ].Page ); +} + +//----------------------------------------------------------------------------- + +void GuiTabBookCtrl::selectPage( GuiTabPageCtrl *page ) +{ + // Return if already selected. + if( mSelectedPageNum >= 0 && mSelectedPageNum < mPages.size() && mPages[ mSelectedPageNum ].Page == page ) + return; + + mSelectedPageNum = -1; + + Vector::iterator i = mPages.begin(); + for( S32 index = 0; i != mPages.end() ; i++, index++ ) + { + GuiTabPageCtrl *tab = (*i).Page; + if( page == tab ) + { + mActivePage = tab; + tab->setVisible( true ); + + mSelectedPageNum = index; + + // Notify User + onTabSelected_callback( tab->getText(), index ); + } + else + tab->setVisible( false ); + } + setUpdateLayout( updateSelf ); +} + +//----------------------------------------------------------------------------- + +bool GuiTabBookCtrl::_setSelectedPage( void *object, const char *index, const char *data ) +{ + GuiTabBookCtrl* book = reinterpret_cast< GuiTabBookCtrl* >( object ); + book->selectPage( dAtoi( data ) ); + return false; +} +//----------------------------------------------------------------------------- + +bool GuiTabBookCtrl::onKeyDown(const GuiEvent &event) +{ + // Tab = Next Page + // Ctrl-Tab = Previous Page + if( 0 && event.keyCode == KEY_TAB ) + { + if( event.modifier & SI_PRIMARY_CTRL ) + selectPrevPage(); + else + selectNextPage(); + + return true; + } + + return Parent::onKeyDown( event ); +} + +//----------------------------------------------------------------------------- + +void GuiTabBookCtrl::selectNextPage() +{ + if( mPages.empty() ) + return; + + if( mActivePage == NULL ) + mActivePage = mPages[0].Page; + + S32 nI = 0; + for( ; nI < mPages.size(); nI++ ) + { + GuiTabPageCtrl *tab = mPages[ nI ].Page; + if( tab == mActivePage ) + { + if( nI == ( mPages.size() - 1 ) ) + selectPage( 0 ); + else if ( nI + 1 <= ( mPages.size() - 1 ) ) + selectPage( nI + 1 ); + else + selectPage( 0 ); + return; + } + } +} + +//----------------------------------------------------------------------------- + +void GuiTabBookCtrl::selectPrevPage() +{ + if( mPages.empty() ) + return; + + if( mActivePage == NULL ) + mActivePage = mPages[0].Page; + + S32 nI = 0; + for( ; nI < mPages.size(); nI++ ) + { + GuiTabPageCtrl *tab = mPages[ nI ].Page; + if( tab == mActivePage ) + { + if( nI == 0 ) + selectPage( mPages.size() - 1 ); + else + selectPage( nI - 1 ); + return; + } + } +} + +//----------------------------------------------------------------------------- + +void GuiTabBookCtrl::fitPage( GuiTabPageCtrl* page ) +{ + page->resize( mPageRect.point, mPageRect.extent ); +} + +//----------------------------------------------------------------------------- + +S32 GuiTabBookCtrl::getPageNum( GuiTabPageCtrl* page ) const +{ + const U32 numPages = mPages.size(); + for( U32 i = 0; i < numPages; ++ i ) + if( mPages[ i ].Page == page ) + return i; + + return -1; +} + +//============================================================================= +// API. +//============================================================================= +// MARK: ---- API ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTabBookCtrl, addPage, void, ( const char* title ), ( "" ), + "Add a new tab page to the control.\n\n" + "@param title Title text for the tab page header." ) +{ + object->addNewPage( title ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTabBookCtrl, selectPage, void, ( S32 index ),, + "Set the selected tab page.\n\n" + "@param index Index of the tab page." ) +{ + object->selectPage( index ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTabBookCtrl, getSelectedPage, S32, (),, + "Get the index of the currently selected tab page.\n\n" + "@return Index of the selected tab page or -1 if no tab page is selected." ) +{ + return object->getSelectedPageNum(); +} diff --git a/Engine/source/gui/containers/guiTabBookCtrl.h b/Engine/source/gui/containers/guiTabBookCtrl.h new file mode 100644 index 000000000..b8c7f3787 --- /dev/null +++ b/Engine/source/gui/containers/guiTabBookCtrl.h @@ -0,0 +1,274 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _GUI_TABBOOKCTRL_H_ +#define _GUI_TABBOOKCTRL_H_ + +#ifndef _GUICONTAINER_H_ + #include "gui/containers/guiContainer.h" +#endif +#ifndef _GUITABPAGECTRL_H_ + #include "gui/controls/guiTabPageCtrl.h" +#endif + + +/// Tab Book Control for creation of tabbed dialogs +/// +/// @see GUI for an overview of the Torque GUI system. +/// +/// @section GuiTabBookCtrl_Intro Introduction +/// +/// GuiTabBookCtrl is a container class that holds children of type GuiTabPageCtrl +/// +/// GuiTabBookCtrl creates an easy to work with system for creating tabbed dialogs +/// allowing for creation of dialogs that store a lot of information in a small area +/// by separating the information into pages which are changeable by clicking their +/// page title on top or bottom of the control +/// +/// tabs may be aligned to be on top or bottom of the book and are changeable while +/// the GUI editor is open for quick switching between pages allowing multi-page dialogs +/// to be edited quickly and easily. +/// +/// The control may only contain children of type GuiTabPageCtrl. +/// If a control is added to the Book that is not of type GuiTabPageCtrl, it will be +/// removed and relocated to the currently active page of the control. +/// If there is no active page in the book, the child control will be relocated to the +/// parent of the book. +/// +/// @ref GUI has an overview of the GUI system. +/// +/// @nosubgrouping +class GuiTabBookCtrl : public GuiContainer +{ + public: + + typedef GuiContainer Parent; + + enum TabPosition + { + AlignTop, ///< Align the tabs on the top of the tab book control + AlignBottom ///< Align the tabs on the bottom of the tab book control + }; + + struct TabHeaderInfo + { + GuiTabPageCtrl *Page; + S32 TextWidth; + S32 TabRow; + S32 TabColumn; + RectI TabRect; + }; + + private: + + static bool _setSelectedPage( void *object, const char *index, const char *data ); + + protected: + + RectI mPageRect; ///< Rectangle of the tab page portion of the control + RectI mTabRect; ///< Rectangle of the tab portion of the control + Vector mPages; ///< Vector of pages contained by the control + GuiTabPageCtrl* mActivePage; ///< Pointer to the active (selected) tab page child control + GuiTabPageCtrl* mHoverTab; ///< Pointer to the tab page that currently has the mouse positioned ontop of its tab + S32 mMinTabWidth; ///< Minimum Width a tab will display as. + TabPosition mTabPosition; ///< Current tab position (see alignment) + S32 mTabHeight; ///< Current tab height + S32 mTabMargin; ///< Margin left/right of tab text in tab + S32 mSelectedPageNum; ///< Current selected tab position + S32 mDefaultPageNum; ///< Page to select on first wake. + bool mAllowReorder; ///< Allow the user to reorder tabs by dragging them + S32 mFrontTabPadding; ///< Padding to the Left of the first Tab + bool mDraggingTab; + bool mDraggingTabRect; + bool mIsFirstWake; + + enum + { + TabSelected = 0, ///< Index of selected tab texture + TabNormal, ///< Index of normal tab texture + TabHover, ///< Index of hover tab texture + TabEnds, ///< Index of end lines for horizontal tabs + NumBitmaps ///< Number of bitmaps in this array + }; + + bool mHasTexture; ///< Indicates whether we have a texture to render the tabs with + RectI *mBitmapBounds;///< Array of rectangles identifying textures for tab book + + /// @name Callbacks + /// @{ + + DECLARE_CALLBACK( void, onTabSelected, ( const String& text, U32 index ) ); + DECLARE_CALLBACK( void, onTabRightClick, ( const String& text, U32 index ) ); + + /// @} + + public: + + GuiTabBookCtrl(); + + DECLARE_CONOBJECT(GuiTabBookCtrl); + DECLARE_DESCRIPTION( "A control that allows to select from a set of tabbed pages." ); + + static void initPersistFields(); + + /// @name Control Events + /// @{ + + bool onWake(); + void onRender( Point2I offset, const RectI &updateRect ); + + /// @} + + /// @name Child events + /// @{ + + void onChildRemoved( GuiControl* child ); + void onChildAdded( GuiControl *child ); + bool reOrder(SimObject* obj, SimObject* target); + bool acceptsAsChild( SimObject* object ) const; + + /// @} + + /// @name Rendering methods + /// @{ + + /// Tab rendering routine, iterates through all tabs rendering one at a time + /// @param offset the render offset to accomodate global coords + /// @param tabRect the Rectangle in which these tabs are to be rendered + void renderTabs( const Point2I &offset, const RectI &tabRect ); + + /// Tab rendering subroutine, renders one tab with specified options + /// @param tabRect the rectangle to render the tab into + /// @param tab pointer to the tab page control for which to render the tab + void renderTab( RectI tabRect, GuiTabPageCtrl* tab ); + + /// @} + + /// @name Page Management + /// @{ + + /// Create a new tab page child in the book + /// + /// Pages created are not titled and appear with no text on their tab when created. + /// This may change in the future. + /// + /// @param text Tab page header text. + void addNewPage( const char* text = NULL ); + + /// Select a tab page based on an index + /// @param index The index in the list that specifies which page to select + void selectPage( S32 index ); + + /// Select a tab page by a pointer to that page + /// @param page A pointer to the GuiTabPageCtrl to select. This page must be a child of the tab book. + void selectPage( GuiTabPageCtrl *page ); + + /// Get the current selected tab number + S32 getSelectedPageNum(){ return mSelectedPageNum; }; + + /// Select the Next page in the tab book + void selectNextPage(); + + /// Select the Previous page in the tab book + void selectPrevPage(); + + /// Make the page fill the entire page space. + void fitPage( GuiTabPageCtrl* page ); + + /// Return the index for the given page. -1 if not a page in this book. + S32 getPageNum( GuiTabPageCtrl* page ) const; + + /// @} + + /// @name Internal Utility Functions + /// @{ + + /// Update ourselves by hooking common GuiControl functionality. + void setUpdate(); + + /// Balance a top/bottom tab row + void balanceRow( S32 row, S32 totalTabWidth ); + + /// Balance a left/right tab column + void balanceColumn( S32 row, S32 totalTabWidth ); + + /// Calculate the tab width of a page, given it's caption + S32 calculatePageTabWidth( GuiTabPageCtrl *page ); + + /// Calculate Page Header Information + void calculatePageTabs(); + + /// Get client area of tab book + virtual const RectI getClientRect(); + + /// Find the tab that was hit by the current event, if any + /// @param event The GuiEvent that caused this function call + GuiTabPageCtrl *findHitTab( const GuiEvent &event ); + + /// Find the tab that was hit, based on a point + /// @param hitPoint A Point2I that specifies where to search for a tab hit + GuiTabPageCtrl *findHitTab( Point2I hitPoint ); + + /// @} + + /// @name Sizing + /// @{ + + /// Rezize our control + /// This method is overridden so that we may handle resizing of our child tab + /// pages when we are resized. + /// This ensures we keep our sizing in sync when we are moved or sized. + /// + /// @param newPosition The new position of the control + /// @param newExtent The new extent of the control + bool resize(const Point2I &newPosition, const Point2I &newExtent); + + /// Called when a child page is resized + /// This method is overridden so that we may handle resizing of our child tab + /// pages when one of them is resized. + /// This ensures we keep our sizing in sync when we our children are sized or moved. + /// + /// @param child A pointer to the child control that has been resized + void childResized(GuiControl *child); + + /// @} + + virtual bool onKeyDown(const GuiEvent &event); + + + /// @name Mouse Events + /// @{ + + virtual void onMouseDown( const GuiEvent &event ); + virtual void onMouseUp( const GuiEvent &event ); + virtual void onMouseDragged( const GuiEvent &event ); + virtual void onMouseMove( const GuiEvent &event ); + virtual void onMouseLeave( const GuiEvent &event ); + virtual bool onMouseDownEditor( const GuiEvent &event, Point2I offset ); + virtual void onRightMouseUp( const GuiEvent& event ); + + /// @} +}; + +typedef GuiTabBookCtrl::TabPosition GuiTabPosition; +DefineEnumType( GuiTabPosition ); + +#endif //_GUI_TABBOOKCTRL_H_ diff --git a/Engine/source/gui/containers/guiWindowCollapseCtrl.cpp b/Engine/source/gui/containers/guiWindowCollapseCtrl.cpp new file mode 100644 index 000000000..639f25cc6 --- /dev/null +++ b/Engine/source/gui/containers/guiWindowCollapseCtrl.cpp @@ -0,0 +1,38 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/containers/guiWindowCollapseCtrl.h" + +IMPLEMENT_CONOBJECT( GuiWindowCollapseCtrl ); + +ConsoleDocClass( GuiWindowCollapseCtrl, + "@deprecated Use GuiWindowCtrl with GuiWindowCtrl::canCollapse = true.\n\n" + "@internal" +); + + +//----------------------------------------------------------------------------- + +GuiWindowCollapseCtrl::GuiWindowCollapseCtrl() +{ + mCanCollapse = true; +} diff --git a/Engine/source/gui/containers/guiWindowCollapseCtrl.h b/Engine/source/gui/containers/guiWindowCollapseCtrl.h new file mode 100644 index 000000000..278f9194f --- /dev/null +++ b/Engine/source/gui/containers/guiWindowCollapseCtrl.h @@ -0,0 +1,52 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIWINDOWCOLLAPSECTRL_H_ +#define _GUIWINDOWCOLLAPSECTRL_H_ + +#ifndef _GUIWINDOWCTRL_H_ + #include "gui/containers/guiWindowCtrl.h" +#endif + + +/// @addtogroup gui_container_group Containers +/// +/// Legacy control. Use GuiWindowCtrl with canCollapse=true. +/// +/// @deprecated Functionality moved into GuiWindowCtrl. +/// +/// @ingroup gui_group Gui System +/// @{ +class GuiWindowCollapseCtrl : public GuiWindowCtrl +{ + public: + + typedef GuiWindowCtrl Parent; + + GuiWindowCollapseCtrl(); + + DECLARE_CONOBJECT(GuiWindowCollapseCtrl); + DECLARE_DESCRIPTION( "Legacy control. Use GuiWindowCtrl with canCollapse=true." ); +}; +/// @} + +#endif //_GUI_WINDOWCOLLAPSE_CTRL_H diff --git a/Engine/source/gui/containers/guiWindowCtrl.cpp b/Engine/source/gui/containers/guiWindowCtrl.cpp new file mode 100644 index 000000000..724154910 --- /dev/null +++ b/Engine/source/gui/containers/guiWindowCtrl.cpp @@ -0,0 +1,1932 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/containers/guiWindowCtrl.h" + +#include "console/consoleTypes.h" +#include "console/console.h" +#include "console/engineAPI.h" +#include "gui/core/guiCanvas.h" +#include "gui/core/guiDefaultControlRender.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxDrawUtil.h" +#include "gui/containers/guiRolloutCtrl.h" + + +IMPLEMENT_CONOBJECT( GuiWindowCtrl ); + +ConsoleDocClass( GuiWindowCtrl, + "@brief A window with a title bar and an optional set of buttons.\n\n" + + "The GuiWindowCtrl class implements windows that can be freely placed within the render window. Additionally, " + "the windows can be resized and maximized/minimized.\n\n" + + "@tsexample\n" + "new GuiWindowCtrl( MyWindow )\n" + "{\n" + " text = \"My Window\"; // The text that is displayed on the title bar.\n" + " resizeWidth = true; // Allow horizontal resizing by user via mouse.\n" + " resizeHeight = true; // Allow vertical resizing by user via mouse.\n" + " canClose = true; // Display a close button in the title bar.\n" + " canMinimize = true; // Display a minimize button in the title bar.\n" + " canMaximize = true; // Display a maximize button in the title bar.\n" + "};\n" + "@endtsexample\n\n" + + "@ingroup GuiContainers" +); + +IMPLEMENT_CALLBACK( GuiWindowCtrl, onClose, void, (), (), + "Called when the close button has been pressed." ); +IMPLEMENT_CALLBACK( GuiWindowCtrl, onMinimize, void, (), (), + "Called when the window has been minimized." ); +IMPLEMENT_CALLBACK( GuiWindowCtrl, onMaximize, void, (), (), + "Called when the window has been maximized." ); +IMPLEMENT_CALLBACK( GuiWindowCtrl, onCollapse, void, (), (), + "Called when the window is collapsed by clicking its title bar." ); +IMPLEMENT_CALLBACK( GuiWindowCtrl, onRestore, void, (), (), + "Called when the window is restored from minimized, maximized, or collapsed state." ); + +//----------------------------------------------------------------------------- + +GuiWindowCtrl::GuiWindowCtrl() + : mResizeEdge(edgeNone), + mResizeWidth(true), + mResizeHeight(true), + mResizeMargin(2.f), + mCanMove(true), + mCanClose(true), + mCanMinimize(true), + mCanMaximize(true), + mCanDock(false), + mCanCollapse(false), + mEdgeSnap(true), + mCollapseGroup(-1), + mCollapseGroupNum(-1), + mIsCollapsed(false), + mIsMouseResizing(false) +{ + // mTitleHeight will change in instanciation most likely... + mTitleHeight = 24; + + mIsContainer = true; + + mCloseCommand = StringTable->EmptyString(); + + mMinimized = false; + mMaximized = false; + mMouseMovingWin = false; + mMouseResizeWidth = false; + mMouseResizeHeight = false; + setExtent(100, 200); + mMinimizeIndex = -1; + mTabIndex = -1; + mBitmapBounds = NULL; + + RectI closeRect(80, 2, 16, 16); + mCloseButton = closeRect; + closeRect.point.x -= 18; + mMaximizeButton = closeRect; + closeRect.point.x -= 18; + mMinimizeButton = closeRect; + + // Other defaults + mActive = true; + mCloseButtonPressed = false; + mMaximizeButtonPressed = false; + mMinimizeButtonPressed = false; + + mText = "New Window"; +} + +//----------------------------------------------------------------------------- + +void GuiWindowCtrl::initPersistFields() +{ + addGroup( "Window" ); + + addField( "text", TypeRealString, Offset( mText, GuiWindowCtrl ), + "Text label to display in titlebar." ); + addField( "resizeWidth", TypeBool, Offset( mResizeWidth, GuiWindowCtrl ), + "Whether the window can be resized horizontally." ); + addField( "resizeHeight", TypeBool, Offset( mResizeHeight, GuiWindowCtrl ), + "Whether the window can be resized vertically." ); + addField( "canMove", TypeBool, Offset( mCanMove, GuiWindowCtrl ), + "Whether the window can be moved by dragging its titlebar." ); + addField( "canClose", TypeBool, Offset( mCanClose, GuiWindowCtrl ), + "Whether the window has a close button." ); + addField( "canMinimize", TypeBool, Offset( mCanMinimize, GuiWindowCtrl ), + "Whether the window has a minimize button." ); + addField( "canMaximize", TypeBool, Offset( mCanMaximize, GuiWindowCtrl ), + "Whether the window has a maximize button." ); + addField( "canCollapse", TypeBool, Offset( mCanCollapse, GuiWindowCtrl ), + "Whether the window can be collapsed by clicking its title bar." ); + addField( "closeCommand", TypeString, Offset( mCloseCommand, GuiWindowCtrl ), + "Script code to execute when the window is closed." ); + addField( "edgeSnap", TypeBool, Offset( mEdgeSnap,GuiWindowCtrl ), + "If true, the window will snap to the edges of other windows when moved close to them." ); + + endGroup( "Window" ); + + Parent::initPersistFields(); +} + +//============================================================================= +// Collapsing. +//============================================================================= +// MARK: ---- Collapsing ---- + +//----------------------------------------------------------------------------- + +void GuiWindowCtrl::moveFromCollapseGroup() +{ + GuiControl *parent = getParent(); + if( !parent ) + return; + + S32 groupVec = mCollapseGroup; + S32 vecPos = mCollapseGroupNum; + S32 groupVecCount = parent->mCollapseGroupVec[groupVec].size() - 1; + + CollapseGroupNumVec collapseGroupNumVec; + + if( groupVecCount > vecPos ) + { + if (vecPos == 1) + { + CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[groupVec].begin(); + for(; iter != parent->mCollapseGroupVec[groupVec].end(); iter++ ) + { + if((*iter)->mCollapseGroupNum >= vecPos) + collapseGroupNumVec.push_back((*iter)); + } + + parent->mCollapseGroupVec[groupVec].first()->mCollapseGroup = -1; + parent->mCollapseGroupVec[groupVec].first()->mCollapseGroupNum = -1; + parent->mCollapseGroupVec[groupVec].erase(U32(0)); + parent->mCollapseGroupVec[groupVec].setSize(groupVecCount - 1); + parent->mCollapseGroupVec.erase(groupVec); + if(groupVec > 0) + parent->mCollapseGroupVec.setSize(groupVec); + + parent->mCollapseGroupVec.push_back( collapseGroupNumVec ); + } + else + { + // Iterate through the group i was once in, gather myself and the controls below me and store them in an array + CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[groupVec].begin(); + for(; iter != parent->mCollapseGroupVec[groupVec].end(); iter++ ) + { + if((*iter)->mCollapseGroupNum >= vecPos) + collapseGroupNumVec.push_back((*iter)); + } + + // Iterate through the newly created array; delete my references in the old group, create a new group and organize me accord. + S32 assignWindowNumber = 0; + CollapseGroupNumVec::iterator iter2 = collapseGroupNumVec.begin(); + for(; iter2 != collapseGroupNumVec.end(); iter2++ ) + { + parent->mCollapseGroupVec[groupVec].pop_back(); + parent->mCollapseGroupVec[groupVec].setSize(groupVecCount); + (*iter2)->mCollapseGroup = (parent->mCollapseGroupVec.size()); + (*iter2)->mCollapseGroupNum = assignWindowNumber; + + assignWindowNumber++; + groupVecCount--; + } + + parent->mCollapseGroupVec.push_back( collapseGroupNumVec ); + } + } + else + { + parent->mCollapseGroupVec[groupVec].erase(mCollapseGroupNum); + parent->mCollapseGroupVec[groupVec].setSize(groupVecCount); + mCollapseGroup = -1; + mCollapseGroupNum = -1; + + if(groupVecCount <= 1) + { + parent->mCollapseGroupVec[groupVec].first()->mCollapseGroup = -1; + parent->mCollapseGroupVec[groupVec].first()->mCollapseGroupNum = -1; + parent->mCollapseGroupVec[groupVec].erase(U32(0)); + parent->mCollapseGroupVec[groupVec].setSize(groupVecCount - 1); + parent->mCollapseGroupVec.erase(groupVec); + } + } + + // Re id collapse groups + refreshCollapseGroups(); +} + +//----------------------------------------------------------------------------- + +void GuiWindowCtrl::moveToCollapseGroup(GuiWindowCtrl* hitWindow, bool orientation ) +{ + // Orientation 0 - window in question is being connected to top of another window + // Orientation 1 - window in question is being connected to bottom of another window + + GuiControl *parent = getParent(); + if( !parent ) + return; + + S32 groupVec = mCollapseGroup; + S32 attatchedGroupVec = hitWindow->mCollapseGroup; + S32 vecPos = mCollapseGroupNum; + + if(mCollapseGroup == attatchedGroupVec && vecPos != -1) + return; + + CollapseGroupNumVec collapseGroupNumVec; + + // Window colliding with is not in a collapse group + if(hitWindow->mCollapseGroup < 0) + { + // We(the collider) are in a group of windows + if(mCollapseGroup >= 0) + { + S32 groupVecCount = parent->mCollapseGroupVec[groupVec].size() - 1; + + // Copy pointer window data in my array, and store in a temp array + CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[groupVec].begin(); + for(; iter != parent->mCollapseGroupVec[groupVec].end(); iter++ ) + { + if((*iter)->mCollapseGroupNum >= vecPos) + { + collapseGroupNumVec.push_back((*iter)); + groupVecCount--; + } + } + + // Kill my old array group and erase its footprints + if( vecPos <= 1 || groupVecCount == 0 ) + { + // Isn't covered in the renumbering of groups, so we it here + parent->mCollapseGroupVec[groupVec].first()->mCollapseGroup = -1; + parent->mCollapseGroupVec[groupVec].first()->mCollapseGroupNum = -1; + + parent->mCollapseGroupVec[groupVec].clear(); + parent->mCollapseGroupVec[groupVec].setSize(U32(0)); + parent->mCollapseGroupVec.erase(groupVec); + + if(groupVec > 0) + parent->mCollapseGroupVec.setSize(groupVec); + } + + // Push the collided window + if(orientation == 0) + collapseGroupNumVec.push_back(hitWindow); + else + collapseGroupNumVec.push_front(hitWindow); + + // Push the temp array in the guiControl array holder + parent->mCollapseGroupVec.push_back( collapseGroupNumVec ); + } + else + { + if(orientation == 0) + { + collapseGroupNumVec.push_front(hitWindow); + collapseGroupNumVec.push_front(this); + } + else + { + collapseGroupNumVec.push_front(this); + collapseGroupNumVec.push_front(hitWindow); + } + + parent->mCollapseGroupVec.push_back( collapseGroupNumVec ); + } + } + else // Window colliding with *IS* in a collapse group + { + + if(mCollapseGroup >= 0) + { + S32 groupVecCount = parent->mCollapseGroupVec[groupVec].size() - 1; + + CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[groupVec].begin(); + for(; iter != parent->mCollapseGroupVec[groupVec].end(); iter++ ) + { + if((*iter)->mCollapseGroupNum >= vecPos) + { + // Push back the pointer window controls to the collided array + parent->mCollapseGroupVec[attatchedGroupVec].push_back((*iter)); + groupVecCount--; + } + } + + if( vecPos <= 1 || groupVecCount == 0 ) + { + // Isn't covered in the renumbering of groups, so we it here + parent->mCollapseGroupVec[groupVec].first()->mCollapseGroup = -1; + parent->mCollapseGroupVec[groupVec].first()->mCollapseGroupNum = -1; + + parent->mCollapseGroupVec[groupVec].clear(); + parent->mCollapseGroupVec[groupVec].setSize(U32(0)); + parent->mCollapseGroupVec.erase(groupVec); + + if(groupVec > 0) + parent->mCollapseGroupVec.setSize(groupVec); + } + + // Since we killed my old array group, run in case the guiControl array moved me down a notch + if(attatchedGroupVec > groupVec ) + attatchedGroupVec--; + + } + else + { + S32 groupVec = hitWindow->mCollapseGroup; + + if(orientation == 0) + parent->mCollapseGroupVec[groupVec].push_front(this); + else + parent->mCollapseGroupVec[groupVec].push_back(this); + } + } + + // Re id collapse groups + refreshCollapseGroups(); + + // Select the current window and its collapse group + selectWindow(); +} + +//----------------------------------------------------------------------------- + +void GuiWindowCtrl::refreshCollapseGroups() +{ + GuiControl *parent = getParent(); + if( !parent ) + return; + + CollapseGroupNumVec collapseGroupNumVec; + + // iterate through the collided array, renumbering the windows pointers + S32 assignGroupNum = 0; + CollapseGroupVec::iterator iter = parent->mCollapseGroupVec.begin(); + for(; iter != parent->mCollapseGroupVec.end(); iter++ ) + { + S32 assignWindowNumber = 0; + CollapseGroupNumVec::iterator iter2 = parent->mCollapseGroupVec[assignGroupNum].begin(); + for(; iter2 != parent->mCollapseGroupVec[assignGroupNum].end(); iter2++ ) + { + (*iter2)->mCollapseGroup = assignGroupNum; + (*iter2)->mCollapseGroupNum = assignWindowNumber; + assignWindowNumber++; + } + + assignGroupNum++; + } +} + +//----------------------------------------------------------------------------- + +void GuiWindowCtrl::moveWithCollapseGroup(Point2I windowPosition) +{ + GuiControl *parent = getParent(); + if( !parent ) + return; + + Point2I newChildPosition(0, 0); + S32 addedPosition = getExtent().y; + + // Iterate through windows below this. Move them according to my position. + CollapseGroupNumVec collapseGroupNumVec; + CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[mCollapseGroup].begin(); + for(; iter != parent->mCollapseGroupVec[mCollapseGroup].end(); iter++ ) + { + if((*iter)->mCollapseGroupNum > mCollapseGroupNum) + { + newChildPosition.x = windowPosition.x; + newChildPosition.y = windowPosition.y + addedPosition; + + (*iter)->resize(newChildPosition, (*iter)->getExtent()); + addedPosition += (*iter)->getExtent().y; + } + } +} + +//----------------------------------------------------------------------------- + +void GuiWindowCtrl::setCollapseGroup(bool state) +{ + GuiControl *parent = getParent(); + if( !parent ) + return; + + if( mIsCollapsed != state ) + { + mIsCollapsed = state; + handleCollapseGroup(); + } +} + +//----------------------------------------------------------------------------- + +void GuiWindowCtrl::toggleCollapseGroup() +{ + GuiControl *parent = getParent(); + if( !parent ) + return; + + mIsCollapsed = !mIsCollapsed; + handleCollapseGroup(); +} + +//----------------------------------------------------------------------------- + +void GuiWindowCtrl::handleCollapseGroup() +{ + GuiControl *parent = getParent(); + if( !parent ) + return; + + CollapseGroupNumVec collapseGroupNumVec; + + if( mIsCollapsed ) // minimize window up to its header bar + { + //save settings + mPreCollapsedYExtent = getExtent().y; + mPreCollapsedYMinExtent = getMinExtent().y; + + //create settings for collapsed window to abide by + mResizeHeight = false; + setMinExtent( Point2I( getMinExtent().x, 24 ) ); + + iterator i; + for(i = begin(); i != end(); i++) + { + GuiControl *ctrl = static_cast(*i); + ctrl->setVisible(false); + ctrl->mCanResize = false; + } + + resize( getPosition(), Point2I( getExtent().x, 24 ) ); + + if(mCollapseGroup >= 0) + { + S32 moveChildYBy = (mPreCollapsedYExtent - 24); + + CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[mCollapseGroup].begin(); + for(; iter != parent->mCollapseGroupVec[mCollapseGroup].end(); iter++ ) + { + if((*iter)->mCollapseGroupNum > mCollapseGroupNum) + { + Point2I newChildPosition = (*iter)->getPosition(); + newChildPosition.y -= moveChildYBy; + (*iter)->resize(newChildPosition, (*iter)->getExtent()); + } + } + } + + onCollapse_callback(); + } + else // maximize the window to its previous position + { + //create and load settings + mResizeHeight = true; + setMinExtent( Point2I( getMinExtent().x, mPreCollapsedYMinExtent ) ); + + resize( getPosition(), Point2I( getExtent().x, mPreCollapsedYExtent ) ); + + iterator i; + for(i = begin(); i != end(); i++) + { + GuiControl *ctrl = static_cast(*i); + ctrl->setVisible(true); + ctrl->mCanResize = true; + } + + if(mCollapseGroup >= 0) + { + S32 moveChildYBy = (mPreCollapsedYExtent - 24); + + CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[mCollapseGroup].begin(); + for(; iter != parent->mCollapseGroupVec[mCollapseGroup].end(); iter++ ) + { + if((*iter)->mCollapseGroupNum > mCollapseGroupNum) + { + Point2I newChildPosition = (*iter)->getPosition(); + newChildPosition.y += moveChildYBy; + (*iter)->resize(newChildPosition, (*iter)->getExtent()); + } + } + } + + onRestore_callback(); + } +} + +//----------------------------------------------------------------------------- + +bool GuiWindowCtrl::resizeCollapseGroup(bool resizeX, bool resizeY, Point2I resizePos, Point2I resizeExtent) +{ + GuiControl *parent = getParent(); + if( !parent ) + return false; + + CollapseGroupNumVec collapseGroupNumVec; + + bool canResize = true; + CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[mCollapseGroup].begin(); + for(; iter != parent->mCollapseGroupVec[mCollapseGroup].end(); iter++ ) + { + if((*iter) == this) + continue; + + Point2I newChildPosition = (*iter)->getPosition(); + Point2I newChildExtent = (*iter)->getExtent(); + + if( resizeX == true ) + { + newChildPosition.x -= resizePos.x; + newChildExtent.x -= resizeExtent.x; + + } + if( resizeY == true ) + { + if((*iter)->mCollapseGroupNum > mCollapseGroupNum) + { + newChildPosition.y -= resizeExtent.y; + newChildPosition.y -= resizePos.y; + } + else if((*iter)->mCollapseGroupNum == mCollapseGroupNum - 1) + newChildExtent.y -= resizePos.y; + } + + // check is done for normal extent of windows. if false, check again in case its just giving false + // due to being a collapsed window. if your truly being forced passed your extent, return false + if( !(*iter)->mIsCollapsed && newChildExtent.y >= (*iter)->getMinExtent().y ) + (*iter)->resize( newChildPosition, newChildExtent); + else + { + if( (*iter)->mIsCollapsed ) + (*iter)->resize( newChildPosition, newChildExtent); + else + canResize = false; + } + } + return canResize; +} + +//----------------------------------------------------------------------------- +// Mouse Methods +//----------------------------------------------------------------------------- +S32 GuiWindowCtrl::findHitEdges( const Point2I &globalPoint ) +{ + // No Edges + S32 edgeMask = edgeNone; + + GuiControl *parent = getParent(); + if( !parent ) + return edgeMask; + + RectI bounds( getGlobalBounds() ); + + // Create an EdgeRectI structure that has four edges + // Left/Right/Top/Bottom + // Each Edge structure has a hit operation that will take + // another edge and test for a hit on the edge with a margin + // specified by the .margin scalar + EdgeRectI edges = EdgeRectI(bounds, mResizeMargin); + + // Get Cursor Edges + Edge cursorVertEdge = Edge( globalPoint, Point2F( 1.f, 0.f ) ); + Edge cursorHorzEdge = Edge( globalPoint, Point2F( 0.f, 1.f ) ); + + if( edges.left.hit( cursorVertEdge ) ) + edgeMask |= edgeLeft; + else if( edges.right.hit( cursorVertEdge ) ) + edgeMask |= edgeRight; + + if( edges.bottom.hit( cursorHorzEdge ) ) + edgeMask |= edgeBottom; + else if( edges.top.hit( cursorHorzEdge ) ) + { + // Only the top window in a collapse group can be extended from the top + if( mCanCollapse && mCollapseGroup >= 0 ) + { + if( parent->mCollapseGroupVec[mCollapseGroup].first() != this ) + return edgeMask; + } + + edgeMask |= edgeTop; + } + + // Return the resulting mask + return edgeMask; +} + +void GuiWindowCtrl::getSnappableWindows( Vector &windowOutVector, bool canCollapse ) +{ + GuiControl *parent = getParent(); + if( !parent ) + return; + + S32 parentSize = parent->size(); + for( S32 i = 0; i < parentSize; i++ ) + { + GuiWindowCtrl *childWindow = dynamic_cast(parent->at(i)); + if( !childWindow || !childWindow->isVisible() || childWindow == this || childWindow->mEdgeSnap == false) + continue; + + if( canCollapse && !childWindow->mCanCollapse ) + continue; + + windowOutVector.push_back(childWindow); + } + +} + +//============================================================================= +// Events. +//============================================================================= +// MARK: ---- Events ---- + +//----------------------------------------------------------------------------- + +bool GuiWindowCtrl::onWake() +{ + if (! Parent::onWake()) + return false; + + //get the texture for the close, minimize, and maximize buttons + mTextureObject = mProfile->mTextureObject; + bool result = mProfile->constructBitmapArray() >= NumBitmaps; + if( !result ) + { + Con::errorf( "GuiWindowCtrl::onWake - failed to create bitmap array from profile bitmap." ); + return false; + } + + mBitmapBounds = mProfile->mBitmapArrayRects.address(); + S32 buttonHeight = mBitmapBounds[BmpStates * BmpClose].extent.y; + + mTitleHeight = buttonHeight + 4; + + //set the button coords + positionButtons(); + + //set the tab index + mTabIndex = -1; + GuiControl *parent = getParent(); + if (parent && mFirstResponder) + { + mTabIndex = 0; + + //count the number of windows preceeding this one + iterator i; + for (i = parent->begin(); i != parent->end(); i++) + { + GuiWindowCtrl *ctrl = dynamic_cast(*i); + if (ctrl) + { + if (ctrl == this) break; + else if (ctrl->mFirstResponder) mTabIndex++; + } + } + } + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiWindowCtrl::onSleep() +{ + mTextureObject = NULL; + mMousePosition = Point2I(0,0); + Parent::onSleep(); +} + +//----------------------------------------------------------------------------- + +void GuiWindowCtrl::onMouseDown(const GuiEvent &event) +{ + setUpdate(); + + mOrigBounds = getBounds(); + + mMouseDownPosition = event.mousePoint; + Point2I localPoint = globalToLocalCoord(event.mousePoint); + + // Select this window - move it to the front, and set the first responder + selectWindow(); + + mMouseMovingWin = false; + + S32 hitEdges = findHitEdges( event.mousePoint ); + + mResizeEdge = edgeNone; + + // Set flag by default so we only clear it + // if we don't match either edge + mMouseResizeHeight = true; + + // Check Bottom/Top edges (Mutually Exclusive) + if( mResizeHeight && hitEdges & edgeBottom ) + mResizeEdge |= edgeBottom; + else if( mResizeHeight && hitEdges & edgeTop ) + mResizeEdge |= edgeTop; + else + mMouseResizeHeight = false; + + // Set flag by default so we only clear it + // if we don't match either edge + mMouseResizeWidth = true; + + // Check Left/Right edges (Mutually Exclusive) + if( mResizeWidth && hitEdges & edgeLeft ) + mResizeEdge |= edgeLeft; + else if( mResizeWidth && hitEdges & edgeRight ) + mResizeEdge |= edgeRight; + else + mMouseResizeWidth = false; + + + // If we clicked within the title bar + if ( !(mResizeEdge & ( edgeTop | edgeLeft | edgeRight ) ) && localPoint.y < mTitleHeight) + { + if (mCanClose && mCloseButton.pointInRect(localPoint)) + { + mCloseButtonPressed = mCanClose; + } + else if (mCanMaximize && mMaximizeButton.pointInRect(localPoint)) + { + mMaximizeButtonPressed = mCanMaximize; + } + else if (mCanMinimize && mMinimizeButton.pointInRect(localPoint)) + { + mMinimizeButtonPressed = mCanMinimize; + } + else // We clicked anywhere else within the title + { + S32 docking = getDocking(); + if( docking == Docking::dockInvalid || docking == Docking::dockNone ) + mMouseMovingWin = mCanMove; + + mMouseResizeWidth = false; + mMouseResizeHeight = false; + } + } + + + if (mMouseMovingWin || mResizeEdge != edgeNone || + mCloseButtonPressed || mMaximizeButtonPressed || mMinimizeButtonPressed) + { + mouseLock(); + } + else + { + + GuiControl *ctrl = findHitControl(localPoint); + if (ctrl && ctrl != this) + ctrl->onMouseDown(event); + } +} + +//----------------------------------------------------------------------------- + +void GuiWindowCtrl::onMouseDragged(const GuiEvent &event) +{ + GuiControl *parent = getParent(); + GuiCanvas *root = getRoot(); + if ( !root ) + return; + + mMousePosition = globalToLocalCoord(event.mousePoint); + Point2I deltaMousePosition = event.mousePoint - mMouseDownPosition; + + Point2I newPosition = getPosition(); + Point2I newExtent = getExtent(); + bool resizeX = false; + bool resizeY = false; + mResizeWindow = false; + mRepositionWindow = false; + + if (mMouseMovingWin && parent) + { + if( parent != root ) + { + newPosition.x = mOrigBounds.point.x + deltaMousePosition.x; + newPosition.y = getMax(0, mOrigBounds.point.y + deltaMousePosition.y ); + mRepositionWindow = true; + } + else + { + newPosition.x = getMax(0, getMin(parent->getWidth() - getWidth(), mOrigBounds.point.x + deltaMousePosition.x)); + newPosition.y = getMax(0, getMin(parent->getHeight() - getHeight(), mOrigBounds.point.y + deltaMousePosition.y)); + } + + // Check snapping to other windows + if( mEdgeSnap ) + { + RectI bounds = getGlobalBounds(); + bounds.point = mOrigBounds.point + deltaMousePosition; + EdgeRectI edges = EdgeRectI( bounds, mResizeMargin ); + + // Create a global-space rectangle that covers the snapping + // zone of this window. Double the space in which snapping occurs + // for top and bottom. + RectI snapZone = bounds; + snapZone.point.x -= SnapDistance; + snapZone.point.y -= SnapDistance; + snapZone.extent.x += SnapDistance + SnapDistance; + snapZone.extent.y += SnapDistance + SnapDistance; + + // Build valid snap and window vectors to compare against + Vector< GuiWindowCtrl* > windowList; + getSnappableWindows( windowList ); + + for( S32 i =0; i < windowList.size(); i++ ) + { + // Make sure the window is both horizontally and vertically + // within the snap zone for this window. + if( !snapZone.overlaps( windowList[i]->getGlobalBounds() ) ) + continue; + + // Build edges for snap detection + EdgeRectI snapRect( windowList[i]->getGlobalBounds(), mResizeMargin ); + + if( snapRect.right.position.x <= edges.left.position.x + SnapDistance && + snapRect.right.position.x >= edges.left.position.x - SnapDistance ) + { + newPosition.x = snapRect.right.position.x; + } + else if( snapRect.left.position.x <= edges.right.position.x + SnapDistance && + snapRect.left.position.x >= edges.right.position.x - SnapDistance ) + { + newPosition.x = snapRect.left.position.x - bounds.extent.x; + } + else if( snapRect.top.position.y <= edges.bottom.position.y + SnapDistance + SnapDistance && + snapRect.top.position.y >= edges.bottom.position.y - SnapDistance - SnapDistance ) + { + // Ensure that we're not snapping to the middle of collapse groups + if( (windowList[i]->mCanCollapse && windowList[i]->mCollapseGroup >= 0) || + (mCanCollapse && mCollapseGroup >= 0) ) + continue; + newPosition.x = snapRect.left.position.x; + newPosition.y = snapRect.top.position.y - bounds.extent.y; + } + else if( snapRect.bottom.position.y <= edges.top.position.y + SnapDistance + SnapDistance && + snapRect.bottom.position.y >= edges.top.position.y - SnapDistance - SnapDistance) + { + // Ensure that we're not snapping to the middle of collapse groups + // We are not in a group, or we are not in the same group + if( mCollapseGroup == -1 || ( mCollapseGroup >= 0 && mCollapseGroup != windowList[i]->mCollapseGroup ) ) + { + // If the window checked is in a group, if its anything but the last, its n/a + if( windowList[i]->mCollapseGroup >= 0 && + windowList[i] != parent->mCollapseGroupVec[windowList[i]->mCollapseGroup].last() ) + continue; + } + else // We are in the same group, we can't obviously be [0] + { + // If we are [-1/0] we have a serious problem. Also, we only allow connection to the window directly above us + if( mCollapseGroupNum <= 0 || + windowList[i] != parent->mCollapseGroupVec[mCollapseGroup][mCollapseGroupNum-1] ) + continue; + } + + newPosition.x = snapRect.left.position.x; + newPosition.y = snapRect.bottom.position.y; + } + } + } + } + else if(mCloseButtonPressed || mMaximizeButtonPressed || mMinimizeButtonPressed) + { + setUpdate(); + return; + } + else + { + if( ( !mMouseResizeHeight && !mMouseResizeWidth ) || !parent ) + return; + + mResizeWindow = true; + if( mResizeEdge & edgeBottom ) + { + newExtent.y = getMin(parent->getHeight(), mOrigBounds.extent.y + deltaMousePosition.y); + resizeY = true; + } + else if ( mResizeEdge & edgeTop ) + { + newExtent.y = getMin(parent->getHeight(), mOrigBounds.extent.y - deltaMousePosition.y); + if ( newExtent.y >= mMinExtent.y ) + { + // Standard reposition as we're not travelling into the min extent range + newPosition.y = mOrigBounds.point.y + deltaMousePosition.y; + } + else + { + // We're into the min extent, so adjust the position up to the min extent + // so the window doesn't appear to jump + newPosition.y = mOrigBounds.point.y + (mOrigBounds.extent.y - mMinExtent.y); + } + resizeY = true; + } + + if( mResizeEdge & edgeRight ) + { + newExtent.x = getMin(parent->getWidth(), mOrigBounds.extent.x + deltaMousePosition.x); + resizeX = true; + } + else if( mResizeEdge & edgeLeft ) + { + newExtent.x = getMin(parent->getWidth(), mOrigBounds.extent.x - deltaMousePosition.x); + if ( newExtent.x >= mMinExtent.x ) + { + // Standard reposition as we're not travelling into the min extent range + newPosition.x = mOrigBounds.point.x + deltaMousePosition.x; + } + else + { + // We're into the min extent, so adjust the position up to the min extent + // so the window doesn't appear to jump + newPosition.x = mOrigBounds.point.x + (mOrigBounds.extent.x - mMinExtent.x); + } + resizeX = true; + } + } + + // Resize this + Point2I pos = parent->localToGlobalCoord(getPosition()); + root->addUpdateRegion(pos, getExtent()); + + if(mCanCollapse && mCollapseGroup >= 0 && mRepositionWindow == true) + moveWithCollapseGroup(newPosition); + + if(mCanCollapse && mCollapseGroup >= 0 && mResizeWindow == true ) + { + // Resize the window if allowed + if( newExtent.y >= getMinExtent().y && newExtent.x >= getMinExtent().x) + { + mIsMouseResizing = true; + if( resizeCollapseGroup( resizeX, resizeY, (getPosition() - newPosition), (getExtent() - newExtent) ) ) + resize(newPosition, newExtent); + } + } + else // Normal window sizing functionality + resize(newPosition, newExtent); +} + +//----------------------------------------------------------------------------- + +void GuiWindowCtrl::onMouseUp(const GuiEvent &event) +{ + bool closing = mCloseButtonPressed; + bool maximizing = mMaximizeButtonPressed; + bool minimizing = mMinimizeButtonPressed; + + mCloseButtonPressed = false; + mMaximizeButtonPressed = false; + mMinimizeButtonPressed = false; + + TORQUE_UNUSED(event); + mouseUnlock(); + + mMouseMovingWin = false; + mMouseResizeWidth = false; + mMouseResizeHeight = false; + + GuiControl *parent = getParent(); + if (! parent) + return; + + if( mIsMouseResizing ) + { + mIsMouseResizing = false; + return; + } + + Point2I localPoint = globalToLocalCoord(event.mousePoint); + if (closing && mCloseButton.pointInRect(localPoint)) + { + // Here is where were going to put our other if statement + // if the window closes, and there were only 2 windows in the array, then just delete the array and default there params + // if not, delete the window from the array, default its params, and renumber the windows + + if( engineAPI::gUseConsoleInterop ) + evaluate( mCloseCommand ); + onClose_callback(); + } + else if (maximizing && mMaximizeButton.pointInRect(localPoint)) + { + if (mMaximized) + { + // Resize to the previous position and extent, bounded by the parent + resize(Point2I(getMax(0, getMin(parent->getWidth() - mStandardBounds.extent.x, mStandardBounds.point.x)), + getMax(0, getMin(parent->getHeight() - mStandardBounds.extent.y, mStandardBounds.point.y))), + mStandardBounds.extent); + // Set the flag + mMaximized = false; + + onRestore_callback(); + } + else + { + // Only save the position if we're not minimized + if (! mMinimized) + mStandardBounds = getBounds(); + else + mMinimized = false; + + // Resize to fit the parent + resize(Point2I(0, 0), parent->getExtent()); + + // Set the flag + mMaximized = true; + + onMaximize_callback(); + } + } + else if (minimizing && mMinimizeButton.pointInRect(localPoint)) + { + if (mMinimized) + { + // Resize to the previous position and extent, bounded by the parent + resize(Point2I(getMax(0, getMin(parent->getWidth() - mStandardBounds.extent.x, mStandardBounds.point.x)), + getMax(0, getMin(parent->getHeight() - mStandardBounds.extent.y, mStandardBounds.point.y))), + mStandardBounds.extent); + // Set the flag + mMinimized = false; + + onRestore_callback(); + } + else + { + if (parent->getWidth() < 100 || parent->getHeight() < mTitleHeight + 3) + return; + + // Only save the position if we're not maximized + if (! mMaximized) + { + mStandardBounds = getBounds(); + } + else + { + mMaximized = false; + } + + // First find the lowest unused minimized index up to 32 minimized windows + U32 indexMask = 0; + iterator i; + S32 count = 0; + for (i = parent->begin(); i != parent->end() && count < 32; i++) + { + count++; + S32 index; + GuiWindowCtrl *ctrl = dynamic_cast(*i); + if (ctrl && ctrl->isMinimized(index)) + { + indexMask |= (1 << index); + } + } + + // Now find the first unused bit + for (count = 0; count < 32; count++) + { + if (! (indexMask & (1 << count))) break; + } + + // If we have more than 32 minimized windows, use the first position + count = getMax(0, count); + + // This algorithm assumes all window have the same title height, and will minimize to 98 pix + Point2I newExtent(98, mTitleHeight); + + // First, how many can fit across + S32 numAcross = getMax(1, (parent->getWidth() / newExtent.x + 2)); + + // Find the new "mini position" + Point2I newPosition; + newPosition.x = (count % numAcross) * (newExtent.x + 2) + 2; + newPosition.y = parent->getHeight() - (((count / numAcross) + 1) * (newExtent.y + 2)) - 2; + + // Find the minimized position and extent + resize(newPosition, newExtent); + + // Set the index so other windows will not try to minimize to the same location + mMinimizeIndex = count; + + // Set the flag + mMinimized = true; + + onMinimize_callback(); + } + } + else if ( !(mResizeEdge & edgeTop) && localPoint.y < mTitleHeight && event.mousePoint == mMouseDownPosition) + { + if (mCanClose && mCloseButton.pointInRect(localPoint)) + return; + else if (mCanMaximize && mMaximizeButton.pointInRect(localPoint)) + return; + else if (mCanMinimize && mMinimizeButton.pointInRect(localPoint)) + return; + else if( mCanCollapse ) // If we clicked anywhere else on the title bar + toggleCollapseGroup(); + } + else if( mEdgeSnap && mCanCollapse ) + { + // Create storage pointer + GuiWindowCtrl* hitWindow = NULL; + S32 snapType = -1; + + Point2I deltaMousePosition = event.mousePoint - mMouseDownPosition; + Point2I newExtent = getExtent(); + + RectI bounds = getGlobalBounds(); + bounds.point = mOrigBounds.point + deltaMousePosition; + EdgeRectI edges = EdgeRectI( bounds, mResizeMargin ); + + RectI snapZone = bounds; + snapZone.point.x -= SnapDistance; + snapZone.point.y -= SnapDistance; + snapZone.extent.x += SnapDistance + SnapDistance; + snapZone.extent.y += SnapDistance + SnapDistance; + + Vector snapList; + Vector windowList; + + getSnappableWindows( windowList, true ); + + for( S32 i =0; i < windowList.size(); i++ ) + { + if( !snapZone.overlaps( windowList[i]->getGlobalBounds() ) ) + continue; + + // Build edges for snap detection + EdgeRectI snapRect( windowList[i]->getGlobalBounds(), mResizeMargin ); + + if( windowList[i]->mCollapseGroupNum == -1 ) + { + // BECOMES "PARENT" + if( snapRect.top.position.y <= edges.bottom.position.y + SnapDistance + SnapDistance && + snapRect.top.position.y >= edges.bottom.position.y - SnapDistance - SnapDistance ) + { + hitWindow = windowList[i]; + snapType = 0; + newExtent.x = snapRect.right.position.x - snapRect.left.position.x; + break; + } + } + + if( (windowList[i]->mCollapseGroupNum == -1) || (windowList[i]->mCollapseGroupNum == mCollapseGroupNum - 1) || + (!parent->mCollapseGroupVec.empty() && parent->mCollapseGroupVec[windowList[i]->mCollapseGroup].last() == windowList[i]) ) + { + // BECOMES "CHILD" + if( snapRect.bottom.position.y <= edges.top.position.y + SnapDistance + SnapDistance && + snapRect.bottom.position.y >= edges.top.position.y - SnapDistance - SnapDistance) + { + hitWindow = windowList[i]; + snapType = 1; + newExtent.x = snapRect.right.position.x - snapRect.left.position.x; + break; + } + } + } + + // We're either moving out of a collapse group or moving to another one + // Not valid for windows not previously in a group + if( mCollapseGroup >= 0 && + ( snapType == -1 || ( snapType >= 0 && mCollapseGroup != hitWindow->mCollapseGroup) ) ) + moveFromCollapseGroup(); + + // No window to connect to + if( !hitWindow ) + return; + + moveToCollapseGroup( hitWindow, snapType ); + resize( getPosition(), newExtent ); + } +} + +//----------------------------------------------------------------------------- + +void GuiWindowCtrl::onMouseMove(const GuiEvent &event) +{ + mMousePosition = globalToLocalCoord(event.mousePoint); +} + +bool GuiWindowCtrl::onKeyDown(const GuiEvent &event) +{ + // If this control is a dead end, kill the event + if ((! mVisible) || (! mActive) || (! mAwake)) return true; + + if ((event.keyCode == KEY_TAB) && (event.modifier & SI_PRIMARY_CTRL)) + { + // Find the next sibling window, and select it + GuiControl *parent = getParent(); + if (parent) + { + GuiWindowCtrl *firstWindow = NULL; + iterator i; + for (i = parent->begin(); i != parent->end(); i++) + { + GuiWindowCtrl *ctrl = dynamic_cast(*i); + if (ctrl && ctrl->getTabIndex() == mTabIndex + 1) + { + ctrl->selectWindow(); + return true; + } + else if (ctrl && ctrl->getTabIndex() == 0) + { + firstWindow = ctrl; + } + } + // Recycle from the beginning + if (firstWindow != this) + { + firstWindow->selectWindow(); + return true; + } + } + } + + return Parent::onKeyDown(event); +} + +//----------------------------------------------------------------------------- + +void GuiWindowCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + if( !mProfile || mProfile->mFont == NULL || mProfile->mBitmapArrayRects.size() < NumBitmaps ) + return Parent::onRender( offset, updateRect ); + + // Draw the outline + RectI winRect; + winRect.point = offset; + winRect.extent = getExtent(); + GuiCanvas *root = getRoot(); + GuiControl *firstResponder = root ? root->getFirstResponder() : NULL; + + bool isKey = (!firstResponder || controlIsChild(firstResponder)); + + U32 topBase = isKey ? BorderTopLeftKey : BorderTopLeftNoKey; + winRect.point.x += mBitmapBounds[BorderLeft].extent.x; + winRect.point.y += mBitmapBounds[topBase + 2].extent.y; + + winRect.extent.x -= mBitmapBounds[BorderLeft].extent.x + mBitmapBounds[BorderRight].extent.x; + winRect.extent.y -= mBitmapBounds[topBase + 2].extent.y + mBitmapBounds[BorderBottom].extent.y; + + winRect.extent.x += 1; + + GFX->getDrawUtil()->drawRectFill(winRect, mProfile->mFillColor); + + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapSR(mTextureObject, offset, mBitmapBounds[topBase]); + GFX->getDrawUtil()->drawBitmapSR(mTextureObject, Point2I(offset.x + getWidth() - mBitmapBounds[topBase+1].extent.x, offset.y), + mBitmapBounds[topBase + 1]); + + RectI destRect; + destRect.point.x = offset.x + mBitmapBounds[topBase].extent.x; + destRect.point.y = offset.y; + destRect.extent.x = getWidth() - mBitmapBounds[topBase].extent.x - mBitmapBounds[topBase + 1].extent.x; + destRect.extent.y = mBitmapBounds[topBase + 2].extent.y; + RectI stretchRect = mBitmapBounds[topBase + 2]; + stretchRect.inset(1,0); + GFX->getDrawUtil()->drawBitmapStretchSR(mTextureObject, destRect, stretchRect); + + destRect.point.x = offset.x; + destRect.point.y = offset.y + mBitmapBounds[topBase].extent.y; + destRect.extent.x = mBitmapBounds[BorderLeft].extent.x; + destRect.extent.y = getHeight() - mBitmapBounds[topBase].extent.y - mBitmapBounds[BorderBottomLeft].extent.y; + stretchRect = mBitmapBounds[BorderLeft]; + stretchRect.inset(0,1); + GFX->getDrawUtil()->drawBitmapStretchSR(mTextureObject, destRect, stretchRect); + + destRect.point.x = offset.x + getWidth() - mBitmapBounds[BorderRight].extent.x; + destRect.extent.x = mBitmapBounds[BorderRight].extent.x; + destRect.point.y = offset.y + mBitmapBounds[topBase + 1].extent.y; + destRect.extent.y = getHeight() - mBitmapBounds[topBase + 1].extent.y - mBitmapBounds[BorderBottomRight].extent.y; + + stretchRect = mBitmapBounds[BorderRight]; + stretchRect.inset(0,1); + GFX->getDrawUtil()->drawBitmapStretchSR(mTextureObject, destRect, stretchRect); + + GFX->getDrawUtil()->drawBitmapSR(mTextureObject, offset + Point2I(0, getHeight() - mBitmapBounds[BorderBottomLeft].extent.y), mBitmapBounds[BorderBottomLeft]); + GFX->getDrawUtil()->drawBitmapSR(mTextureObject, offset + getExtent() - mBitmapBounds[BorderBottomRight].extent, mBitmapBounds[BorderBottomRight]); + + destRect.point.x = offset.x + mBitmapBounds[BorderBottomLeft].extent.x; + destRect.extent.x = getWidth() - mBitmapBounds[BorderBottomLeft].extent.x - mBitmapBounds[BorderBottomRight].extent.x; + + destRect.point.y = offset.y + getHeight() - mBitmapBounds[BorderBottom].extent.y; + destRect.extent.y = mBitmapBounds[BorderBottom].extent.y; + stretchRect = mBitmapBounds[BorderBottom]; + stretchRect.inset(1,0); + + GFX->getDrawUtil()->drawBitmapStretchSR(mTextureObject, destRect, stretchRect); + + // Draw the title + // dhc addition: copied/modded from renderJustifiedText, since we enforce a + // different color usage here. NOTE: it currently CAN overdraw the controls + // if mis-positioned or 'scrunched' in a small width. + GFX->getDrawUtil()->setBitmapModulation(mProfile->mFontColor); + S32 textWidth = mProfile->mFont->getStrWidth((const UTF8 *)mText); + Point2I start(0,0); + + // Align the horizontal + if ( mProfile->mAlignment == GuiControlProfile::RightJustify ) + start.set( winRect.extent.x - textWidth, 0 ); + else if ( mProfile->mAlignment == GuiControlProfile::CenterJustify ) + start.set( ( winRect.extent.x - textWidth) / 2, 0 ); + else // GuiControlProfile::LeftJustify or garbage... ;) + start.set( 0, 0 ); + // If the text is longer then the box size, (it'll get clipped) so force Left Justify + if( textWidth > winRect.extent.x ) start.set( 0, 0 ); + // center the vertical +// start.y = ( winRect.extent.y - ( font->getHeight() - 2 ) ) / 2; + GFX->getDrawUtil()->drawText( mProfile->mFont, start + offset + mProfile->mTextOffset, mText ); + + // Deal with rendering the titlebar controls + AssertFatal(root, "Unable to get the root GuiCanvas."); + + // Draw the close button + Point2I tempUL; + Point2I tempLR; + S32 bmp = BmpStates * BmpClose; + + if( mCanClose ) { + if( mCloseButton.pointInRect( mMousePosition ) ) + { + if( mCloseButtonPressed ) + bmp += BmpDown; + else + bmp += BmpHilite; + } + + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapSR(mTextureObject, offset + mCloseButton.point, mBitmapBounds[bmp]); + } + + // Draw the maximize button + if( mMaximized ) + bmp = BmpStates * BmpNormal; + else + bmp = BmpStates * BmpMaximize; + + if( mCanMaximize ) { + if( mMaximizeButton.pointInRect( mMousePosition ) ) + { + if( mMaximizeButtonPressed ) + bmp += BmpDown; + else + bmp += BmpHilite; + } + + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapSR( mTextureObject, offset + mMaximizeButton.point, mBitmapBounds[bmp] ); + } + + // Draw the minimize button + if( mMinimized ) + bmp = BmpStates * BmpNormal; + else + bmp = BmpStates * BmpMinimize; + + if( mCanMinimize ) { + if( mMinimizeButton.pointInRect( mMousePosition ) ) + { + if( mMinimizeButtonPressed ) + bmp += BmpDown; + else + bmp += BmpHilite; + } + + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapSR( mTextureObject, offset + mMinimizeButton.point, mBitmapBounds[bmp] ); + } + + if( !mMinimized ) + { + // Render the children + renderChildControls( offset, updateRect ); + } +} + +//============================================================================= +// Misc. +//============================================================================= +// MARK: ---- Misc ---- + +//----------------------------------------------------------------------------- + +const RectI GuiWindowCtrl::getClientRect() +{ + if( !mProfile || mProfile->mBitmapArrayRects.size() < NumBitmaps ) + return Parent::getClientRect(); + + if( !mBitmapBounds ) + mBitmapBounds = mProfile->mBitmapArrayRects.address(); + + RectI winRect; + winRect.point.x = mBitmapBounds[BorderLeft].extent.x; + winRect.point.y = mBitmapBounds[BorderTopKey].extent.y; + + winRect.extent.y = getHeight() - ( winRect.point.y + mBitmapBounds[BorderBottom].extent.y ); + winRect.extent.x = getWidth() - ( winRect.point.x + mBitmapBounds[BorderRight].extent.x ); + + // Finally, inset it by padding + // Inset by padding. margin is specified for all t/b/l/r but + // uses only pointx pointy uniformly on both ends. This should be fixed. - JDD + // winRect.inset( mSizingOptions.mPadding.point.x, mSizingOptions.mPadding.point.y ); + + return winRect; +} + +//----------------------------------------------------------------------------- + +bool GuiWindowCtrl::isMinimized(S32 &index) +{ + index = mMinimizeIndex; + return mMinimized && mVisible; +} + +//----------------------------------------------------------------------------- + +void GuiWindowCtrl::positionButtons(void) +{ + if( !mBitmapBounds || !mAwake ) + return; + + S32 buttonWidth = mBitmapBounds[BmpStates * BmpClose].extent.x; + S32 buttonHeight = mBitmapBounds[BmpStates * BmpClose].extent.y; + Point2I mainOff = mProfile->mTextOffset; + + // Until a pref, if alignment is LEFT, put buttons RIGHT justified. + // ELSE, put buttons LEFT justified. + int closeLeft = mainOff.x, closeTop = mainOff.y, closeOff = buttonWidth + 2; + if ( mProfile->mAlignment == GuiControlProfile::LeftJustify ) + { + closeOff = -closeOff; + closeLeft = getWidth() - buttonWidth - mainOff.x; + } + RectI closeRect(closeLeft, closeTop, buttonHeight, buttonWidth); + mCloseButton = closeRect; + + // Always put Minimize on left side of Maximize. + closeRect.point.x += closeOff; + if (closeOff>0) + { + mMinimizeButton = closeRect; + closeRect.point.x += closeOff; + mMaximizeButton = closeRect; + } + else + { + mMaximizeButton = closeRect; + closeRect.point.x += closeOff; + mMinimizeButton = closeRect; + } +} + +//----------------------------------------------------------------------------- + +void GuiWindowCtrl::setCloseCommand(const char *newCmd) +{ + if (newCmd) + mCloseCommand = StringTable->insert(newCmd); + else + mCloseCommand = StringTable->insert(""); +} + +//----------------------------------------------------------------------------- + +GuiControl* GuiWindowCtrl::findHitControl(const Point2I &pt, S32 initialLayer) +{ + if (! mMinimized) + return Parent::findHitControl(pt, initialLayer); + else + return this; +} + +//----------------------------------------------------------------------------- + +bool GuiWindowCtrl::resize(const Point2I &newPosition, const Point2I &newExtent) +{ + if( !Parent::resize(newPosition, newExtent) ) + return false; + + // Set the button coords + positionButtons(); + + return true; +} + +//----------------------------------------------------------------------------- + +GuiControl *GuiWindowCtrl::findNextTabable(GuiControl *curResponder, bool firstCall) +{ + // Set the global if this is the first call (directly from the canvas) + if (firstCall) + { + GuiControl::smCurResponder = NULL; + } + + // If the window does not already contain the first responder, return false + // ie. Can't tab into or out of a window + if (! controlIsChild(curResponder)) + { + return NULL; + } + + // Loop through, checking each child to see if it is the one that follows the firstResponder + GuiControl *tabCtrl = NULL; + iterator i; + for (i = begin(); i != end(); i++) + { + GuiControl *ctrl = static_cast(*i); + tabCtrl = ctrl->findNextTabable(curResponder, false); + if (tabCtrl) break; + } + + // To ensure the tab cycles within the current window... + if (! tabCtrl) + { + tabCtrl = findFirstTabable(); + } + + mFirstResponder = tabCtrl; + return tabCtrl; +} + +//----------------------------------------------------------------------------- + +GuiControl *GuiWindowCtrl::findPrevTabable(GuiControl *curResponder, bool firstCall) +{ + if (firstCall) + { + GuiControl::smPrevResponder = NULL; + } + + // If the window does not already contain the first responder, return false + // ie. Can't tab into or out of a window + if (! controlIsChild(curResponder)) + { + return NULL; + } + + // Loop through, checking each child to see if it is the one that follows the firstResponder + GuiControl *tabCtrl = NULL; + iterator i; + for (i = begin(); i != end(); i++) + { + GuiControl *ctrl = static_cast(*i); + tabCtrl = ctrl->findPrevTabable(curResponder, false); + if (tabCtrl) break; + } + + // To ensure the tab cycles within the current window... + if (! tabCtrl) + { + tabCtrl = findLastTabable(); + } + + mFirstResponder = tabCtrl; + return tabCtrl; +} + +//----------------------------------------------------------------------------- + +void GuiWindowCtrl::selectWindow(void) +{ + // First make sure this window is the front most of its siblings + GuiControl *parent = getParent(); + if (parent && *parent->end() != this ) + { + // Valid collapse groups have to be selected together + if( mCanCollapse && mCollapseGroup >= 0 ) + { + CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[mCollapseGroup].begin(); + for(; iter != parent->mCollapseGroupVec[mCollapseGroup].end(); iter++ ) + { + parent->pushObjectToBack( (*iter) ); + } + } + else + { + parent->pushObjectToBack( this ); + } + } + + // Also set the first responder to be the one within this window + setFirstResponder(mFirstResponder); +} + +//----------------------------------------------------------------------------- + +void GuiWindowCtrl::getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent) +{ + GuiCanvas *pRoot = getRoot(); + if( !pRoot ) + return; + PlatformWindow *pWindow = static_cast(getRoot())->getPlatformWindow(); + AssertFatal(pWindow != NULL,"GuiControl without owning platform window! This should not be possible."); + PlatformCursorController *pController = pWindow->getCursorController(); + AssertFatal(pController != NULL,"PlatformWindow without an owned CursorController!"); + + S32 desiredCursor = PlatformCursorController::curArrow; + S32 hitEdges = findHitEdges( lastGuiEvent.mousePoint ); + + if( hitEdges & edgeBottom && hitEdges & edgeLeft && mResizeHeight ) + desiredCursor = PlatformCursorController::curResizeNESW; + else if( hitEdges & edgeBottom && hitEdges & edgeRight && mResizeHeight ) + desiredCursor = PlatformCursorController::curResizeNWSE; + else if( hitEdges & edgeBottom && mResizeHeight ) + desiredCursor = PlatformCursorController::curResizeHorz; + else if( hitEdges & edgeTop && hitEdges & edgeLeft && mResizeHeight ) + desiredCursor = PlatformCursorController::curResizeNWSE; + else if( hitEdges & edgeTop && hitEdges & edgeRight && mResizeHeight ) + desiredCursor = PlatformCursorController::curResizeNESW; + else if( hitEdges & edgeTop && mResizeHeight ) + desiredCursor = PlatformCursorController::curResizeHorz; + else if ( hitEdges & edgeLeft && mResizeWidth ) + desiredCursor = PlatformCursorController::curResizeVert; + else if( hitEdges & edgeRight && mResizeWidth ) + desiredCursor = PlatformCursorController::curResizeVert; + else + desiredCursor = PlatformCursorController::curArrow; + + // Bail if we're already at the desired cursor + if(pRoot->mCursorChanged == desiredCursor ) + return; + + // Now change the cursor shape + pController->popCursor(); + pController->pushCursor(desiredCursor); + pRoot->mCursorChanged = desiredCursor; +} + +//----------------------------------------------------------------------------- + +void GuiWindowCtrl::parentResized(const RectI &oldParentRect, const RectI &newParentRect) +{ + if(!mCanResize) + return; + + GuiControl *parent = getParent(); + if( !parent ) + return; + + // Bail if were not sized both by windowrelative + if( mHorizSizing != horizResizeWindowRelative || mHorizSizing != vertResizeWindowRelative ) + return Parent::parentResized( oldParentRect, newParentRect ); + + Point2I newPosition = getPosition(); + Point2I newExtent = getExtent(); + + bool doCollapse = false; + + S32 deltaX = newParentRect.extent.x - oldParentRect.extent.x; + S32 deltaY = newParentRect.extent.y - oldParentRect.extent.y;// + mProfile->mYPositionOffset; + + if( newPosition.x > ( oldParentRect.extent.x / 2 ) ) + newPosition.x = newPosition.x + deltaX; + + if (oldParentRect.extent.y != 0) + { + // Only if were apart of a group + if ( mCanCollapse && mCollapseGroup >= 0 ) + { + // Setup parsing mechanisms + CollapseGroupNumVec collapseGroupNumVec; + + // Lets grab the information we need (this should probably be already stored on each individual window object) + S32 groupNum = mCollapseGroup; + S32 groupMax = parent->mCollapseGroupVec[ groupNum ].size() - 1; + + // Set up vars that we're going to be using + S32 groupPos = 0; + S32 groupExtent = 0; + S32 tempGroupExtent = 0; + + // Set up the vector/iterator combo we need + collapseGroupNumVec = parent->mCollapseGroupVec[ groupNum ]; + CollapseGroupNumVec::iterator iter = collapseGroupNumVec.begin(); + + // Grab some more information we need later ( this info should also be located on each ind. window object ) + for( ; iter != collapseGroupNumVec.end(); iter++ ) + { + if((*iter)->getCollapseGroupNum() == 0) + { + groupPos = (*iter)->getPosition().y; + } + + groupExtent += (*iter)->getExtent().y; + } + + // Use the information we just gatherered; only enter this block if we need to + tempGroupExtent = groupPos + groupExtent; + if( tempGroupExtent > ( newParentRect.extent.y / 2 ) ) + { + // Lets size the collapse group + S32 windowParser = groupMax; + bool secondLoop = false; + while( tempGroupExtent >= newParentRect.extent.y ) + { + + if( windowParser == -1) + { + if( !secondLoop ) + { + secondLoop = true; + windowParser = groupMax; + } + else + break; + } + + GuiWindowCtrl *tempWindow = collapseGroupNumVec[windowParser]; + if(tempWindow->mIsCollapsed) + { + windowParser--; + continue; + } + + // Resizing block for the loop... if we can get away with just resizing the bottom window do it before + // resizing the whole block. else, go through the group and start setting min extents. if that doesnt work + // on the second go around, start collpsing the windows. + if( tempGroupExtent - ( tempWindow->getExtent().y - tempWindow->getMinExtent().y ) <= newParentRect.extent.y ) + { + if( this == tempWindow ) + newExtent.y = newExtent.y - ( tempGroupExtent - newParentRect.extent.y ); + + tempGroupExtent = tempGroupExtent - newParentRect.extent.y; + } + else + { + if( secondLoop ) + { + tempGroupExtent = tempGroupExtent - (tempWindow->getExtent().y + 32); + + if( this == tempWindow ) + doCollapse = true; + } + else + { + tempGroupExtent = tempGroupExtent - (tempWindow->getExtent().y - tempWindow->getMinExtent().y); + if( this == tempWindow ) + newExtent.y = tempWindow->getMinExtent().y; + } + } + windowParser--; + } + } + } + else if( newPosition.y > ( oldParentRect.extent.y / 2 ) ) + { + newPosition.y = newPosition.y + deltaY - 1; + } + } + + if( newExtent.x >= getMinExtent().x && newExtent.y >= getMinExtent().y ) + { + // If we are already outside the reach of the main window, lets not place ourselves + // further out; but if were trying to improve visibility, go for it + // note: small tolerance (4) added to keep guis that are very slightly outside + // the main window (like all of the editor windows!) from appearing to 'undock' + // when the resolution is changed. + if( (newPosition.x + newExtent.x) > newParentRect.extent.x + 4 ) + { + if( (newPosition.x + newExtent.x) > (getPosition().x + getExtent().x) ) + { + newPosition.x = getPosition().x; + newExtent.x = getExtent().x; + } + } + if( (newPosition.y + newExtent.y) > newParentRect.extent.y + 4) + { + if( (newPosition.y + newExtent.y) > (getPosition().y + getExtent().y) ) + { + newPosition.y = getPosition().y; + newExtent.y = getExtent().y; + } + } + + // Only for collpasing groups, if were not, then do it like normal windows + if( mCanCollapse && mCollapseGroup >= 0 ) + { + bool resizeMe = false; + + // Only the group window should control positioning + if( mCollapseGroupNum == 0 ) + { + resizeMe = resizeCollapseGroup( true, true, (getPosition() - newPosition), (getExtent() - newExtent) ); + if(resizeMe == true) + resize(newPosition, newExtent); + } + else if( getExtent() != newExtent) + { + resizeMe = resizeCollapseGroup( false, true, (getPosition() - newPosition), (getExtent() - newExtent) ); + if(resizeMe == true) + resize(getPosition(), newExtent); + } + } + else + { + resize(newPosition, newExtent); + } + } + + if( mCanCollapse && !mIsCollapsed && doCollapse ) + toggleCollapseGroup(); + + // If docking is invalid on this control, then bail out here + if( getDocking() & Docking::dockInvalid || getDocking() & Docking::dockNone) + return; + + // Update Self + RectI oldThisRect = getBounds(); + anchorControl( this, Point2I( deltaX, deltaY ) ); + RectI newThisRect = getBounds(); + + // Update Deltas to pass on to children + deltaX = newThisRect.extent.x - oldThisRect.extent.x; + deltaY = newThisRect.extent.y - oldThisRect.extent.y; + + // Iterate over all children and update their anchors + iterator nI = begin(); + for( ; nI != end(); nI++ ) + { + // Sanity + GuiControl *control = dynamic_cast( (*nI) ); + if( control ) + control->parentResized( oldThisRect, newThisRect ); + } +} + +//============================================================================= +// Console Methods. +//============================================================================= +// MARK: ---- Console Methods ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiWindowCtrl, selectWindow, void, (),, + "Bring the window to the front." ) +{ + object->selectWindow(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiWindowCtrl, setCollapseGroup, void, ( bool state ),, + "Set the window's collapsing state." ) +{ + object->setCollapseGroup(state); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiWindowCtrl, toggleCollapseGroup, void, (),, + "Toggle the window collapsing." ) +{ + object->toggleCollapseGroup(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiWindowCtrl, attachTo, void, ( GuiWindowCtrl* window ),, + "" ) +{ + object->moveToCollapseGroup( window, 1 ); +} + +//----------------------------------------------------------------------------- + +DefineEngineStaticMethod( GuiWindowCtrl, attach, void, ( GuiWindowCtrl* bottomWindow, GuiWindowCtrl* topWindow ),, + "Attach @a bottomWindow to @topWindow so that @a bottomWindow moves along with @a topWindow when it is dragged.\n\n" + "@param bottomWindow \n" + "@param topWindow " ) +{ + if(bottomWindow == NULL || topWindow == NULL) + { + Con::warnf("Warning: AttachWindows - could not find windows"); + return; + } + + bottomWindow->moveToCollapseGroup( topWindow, 1 ); +} diff --git a/Engine/source/gui/containers/guiWindowCtrl.h b/Engine/source/gui/containers/guiWindowCtrl.h new file mode 100644 index 000000000..ef1b61751 --- /dev/null +++ b/Engine/source/gui/containers/guiWindowCtrl.h @@ -0,0 +1,287 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIWINDOWCTRL_H_ +#define _GUIWINDOWCTRL_H_ + +#ifndef _GUICONTAINER_H_ + #include "gui/containers/guiContainer.h" +#endif + + +/// @addtogroup gui_container_group Containers +/// +/// @ingroup gui_group Gui System +/// @{ +class GuiWindowCtrl : public GuiContainer +{ + public: + + typedef GuiContainer Parent; + + protected: + + enum + { + /// Pixel distance across which snapping takes effect. + SnapDistance = 12 + }; + + /// Base indices for the button bitmap rects. Each button + /// bitmap is defined in each of the BitmapStates. + enum BitmapIndices + { + BmpClose, + BmpMaximize, + BmpNormal, + BmpMinimize, + + BmpCount + }; + + /// Button bitmap states. + enum BitmapStates + { + BmpDefault = 0, + BmpHilite, + BmpDown, + + BmpStates + }; + + /// Indices for non-button bitmap rects. + enum + { + BorderTopLeftKey = 12, + BorderTopRightKey, + BorderTopKey, + BorderTopLeftNoKey, + BorderTopRightNoKey, + BorderTopNoKey, + BorderLeft, + BorderRight, + BorderBottomLeft, + BorderBottom, + BorderBottomRight, + NumBitmaps + }; + + /// Window Edge Bit Masks + /// + /// Edges can be combined to create a mask of multiple edges. + /// This is used for hit detection throughout this class. + enum Edges + { + edgeNone = 0, ///< No Edge + edgeTop = BIT(1), ///< Top Edge + edgeLeft = BIT(2), ///< Left Edge + edgeRight = BIT(3), ///< Right Edge + edgeBottom = BIT(4) ///< Bottom Edge + }; + + /// @name Flags + /// @{ + + /// Allow resizing width of window. + bool mResizeWidth; + + /// Allow resizing height of window. + bool mResizeHeight; + + /// Allow moving window. + bool mCanMove; + + /// Display close button. + bool mCanClose; + + /// Display minimize button. + bool mCanMinimize; + + /// Display maximize button. + bool mCanMaximize; + + /// + bool mCanCollapse; + + bool mCanDock; ///< Show a docking button on the title bar? + bool mEdgeSnap; ///< Should this window snap to other windows edges? + + /// @} + + bool mCloseButtonPressed; + bool mMinimizeButtonPressed; + bool mMaximizeButtonPressed; + + bool mRepositionWindow; + bool mResizeWindow; + bool mSnapSignal; + + StringTableEntry mCloseCommand; + + /// Window title string. + String mText; + + S32 mResizeEdge; ///< Resizing Edges Mask (See Edges Enumeration) + + S32 mTitleHeight; + + F32 mResizeMargin; + + bool mMouseMovingWin; + bool mMouseResizeWidth; + bool mMouseResizeHeight; + bool mMinimized; + bool mMaximized; + + Point2I mMousePosition; + Point2I mMouseDownPosition; + RectI mOrigBounds; + RectI mStandardBounds; + + RectI mCloseButton; + RectI mMinimizeButton; + RectI mMaximizeButton; + S32 mMinimizeIndex; + S32 mTabIndex; + + void positionButtons(void); + + RectI *mBitmapBounds; //bmp is [3*n], bmpHL is [3*n + 1], bmpNA is [3*n + 2] + GFXTexHandle mTextureObject; + + /// @name Collapsing + /// @{ + + typedef Vector< GuiWindowCtrl *> CollapseGroupNumVec; + + S32 mCollapseGroup; + S32 mCollapseGroupNum; + S32 mPreCollapsedYExtent; + S32 mPreCollapsedYMinExtent; + + bool mIsCollapsed; + bool mIsMouseResizing; + + S32 getCollapseGroupNum() { return mCollapseGroupNum; } + + void moveFromCollapseGroup(); + void moveWithCollapseGroup(Point2I windowPosition); + + bool resizeCollapseGroup(bool resizeX, bool resizeY, Point2I resizePos, Point2I resizeWidth); + void refreshCollapseGroups(); + + void handleCollapseGroup(); + + /// @} + + /// @name Callbacks + /// @{ + + DECLARE_CALLBACK( void, onClose, () ); + DECLARE_CALLBACK( void, onMinimize, () ); + DECLARE_CALLBACK( void, onMaximize, () ); + DECLARE_CALLBACK( void, onCollapse, () ); + DECLARE_CALLBACK( void, onRestore, () ); + + /// @} + + public: + + GuiWindowCtrl(); + + bool isMinimized(S32 &index); + + virtual void getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent); + + void setFont(S32 fntTag); + + void setCloseCommand(const char *newCmd); + + GuiControl* findHitControl (const Point2I &pt, S32 initialLayer = -1 ); + S32 findHitEdges( const Point2I &globalPoint ); + void getSnappableWindows( Vector &windowOutVector, bool canCollapse = false ); + bool resize( const Point2I &newPosition, const Point2I &newExtent ); + + //only cycle tabs through the current window, so overwrite the method + GuiControl* findNextTabable(GuiControl *curResponder, bool firstCall = true); + GuiControl* findPrevTabable(GuiControl *curResponder, bool firstCall = true); + + S32 getTabIndex(void) { return mTabIndex; } + void selectWindow(void); + + //// + const RectI getClientRect(); + + /// Mutators for window properties from code. + /// Using setDataField is a bit overkill. + void setMobility( bool canMove, bool canClose, bool canMinimize, bool canMaximize, bool canDock, bool edgeSnap ) + { + mCanMove = canMove; + mCanClose = canClose; + mCanMinimize = canMinimize; + mCanMaximize = canMaximize; + mCanDock = canDock; + mEdgeSnap = edgeSnap; + } + + /// Mutators for window properties from code. + /// Using setDataField is a bit overkill. + void setCanResize( bool canResizeWidth, bool canResizeHeight ) + { + mResizeWidth = canResizeWidth; + mResizeHeight = canResizeHeight; + } + + /// Set the text on the window title bar. + void setText( const String& text ) + { + mText = text; + } + + /// @name Collapsing + /// @{ + + void setCollapseGroup( bool state ); + void toggleCollapseGroup(); + void moveToCollapseGroup( GuiWindowCtrl* hitWindow, bool orientation ); + + /// @} + + // GuiContainer. + virtual bool onWake(); + virtual void onSleep(); + virtual void parentResized(const RectI &oldParentRect, const RectI &newParentRect); + virtual void onMouseDown(const GuiEvent &event); + virtual void onMouseDragged(const GuiEvent &event); + virtual void onMouseUp(const GuiEvent &event); + virtual void onMouseMove(const GuiEvent &event); + virtual bool onKeyDown(const GuiEvent &event); + virtual void onRender(Point2I offset, const RectI &updateRect); + + DECLARE_CONOBJECT( GuiWindowCtrl ); + DECLARE_DESCRIPTION( "A control that shows an independent window inside the canvas." ); + + static void initPersistFields(); +}; +/// @} + +#endif //_GUI_WINDOW_CTRL_H diff --git a/Engine/source/gui/controls/guiBackgroundCtrl.cpp b/Engine/source/gui/controls/guiBackgroundCtrl.cpp new file mode 100644 index 000000000..8432d7aa3 --- /dev/null +++ b/Engine/source/gui/controls/guiBackgroundCtrl.cpp @@ -0,0 +1,54 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/console.h" +#include "gui/controls/guiBackgroundCtrl.h" + +IMPLEMENT_CONOBJECT(GuiBackgroundCtrl); + +ConsoleDocClass( GuiBackgroundCtrl, + "@brief Renders a background, so you can have a backdrop for your GUI.\n\n" + + "Deprecated\n\n" + + "@ingroup GuiImages\n" + + "@internal" +); + +//-------------------------------------------------------------------------- +GuiBackgroundCtrl::GuiBackgroundCtrl() : GuiControl() +{ + mDraw = false; + mIsContainer = true; +} + +//-------------------------------------------------------------------------- +void GuiBackgroundCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + if ( mDraw ) + Parent::onRender( offset, updateRect ); + + renderChildControls(offset, updateRect); +} + + diff --git a/Engine/source/gui/controls/guiBackgroundCtrl.h b/Engine/source/gui/controls/guiBackgroundCtrl.h new file mode 100644 index 000000000..931d8c246 --- /dev/null +++ b/Engine/source/gui/controls/guiBackgroundCtrl.h @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIBACKGROUNDCTRL_H_ +#define _GUIBACKGROUNDCTRL_H_ + +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif + +/// Renders a background, so you can have a backdrop for your GUI. +class GuiBackgroundCtrl : public GuiControl +{ +private: + typedef GuiControl Parent; + +public: + bool mDraw; + + //creation methods + DECLARE_CONOBJECT(GuiBackgroundCtrl); + DECLARE_CATEGORY( "Gui Containers" ); + + GuiBackgroundCtrl(); + + void onRender(Point2I offset, const RectI &updateRect); +}; + +#endif diff --git a/Engine/source/gui/controls/guiBitmapBorderCtrl.cpp b/Engine/source/gui/controls/guiBitmapBorderCtrl.cpp new file mode 100644 index 000000000..cf297c50a --- /dev/null +++ b/Engine/source/gui/controls/guiBitmapBorderCtrl.cpp @@ -0,0 +1,197 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "gui/core/guiControl.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxDrawUtil.h" + + +// [rene 11/09/09] Why does this not use the bitmap array from its profile? + + +/// Renders a skinned border. +class GuiBitmapBorderCtrl : public GuiControl +{ + typedef GuiControl Parent; + + enum { + BorderTopLeft, + BorderTopRight, + BorderTop, + BorderLeft, + BorderRight, + BorderBottomLeft, + BorderBottom, + BorderBottomRight, + NumBitmaps + }; + RectI *mBitmapBounds; ///< bmp is [3*n], bmpHL is [3*n + 1], bmpNA is [3*n + 2] + GFXTexHandle mTextureObject; +public: + bool onWake(); + void onSleep(); + void onRender(Point2I offset, const RectI &updateRect); + DECLARE_CONOBJECT(GuiBitmapBorderCtrl); + DECLARE_CATEGORY( "Gui Images" ); + DECLARE_DESCRIPTION( "A control that renders a skinned border." ); +}; + +IMPLEMENT_CONOBJECT(GuiBitmapBorderCtrl); + +ConsoleDocClass( GuiBitmapBorderCtrl, + "@brief A control that renders a skinned border specified in its profile.\n\n" + + "This control uses the bitmap specified in it's profile (GuiControlProfile::bitmapName). It takes this image and breaks up aspects of it " + "to skin the border of this control with. It is also important to set GuiControlProfile::hasBitmapArray to true on the profile as well.\n\n" + + "The bitmap referenced should be broken up into a 3 x 3 grid (using the top left color pixel as a border color between each of the images) " + "in which it will map to the following places:\n" + "1 = Top Left Corner\n" + "2 = Top Right Corner\n" + "3 = Top Center\n" + "4 = Left Center\n" + "5 = Right Center\n" + "6 = Bottom Left Corner\n" + "7 = Bottom Center\n" + "8 = Bottom Right Corner\n" + "0 = Nothing\n\n" + + "1 2 3\n" + "4 5 0\n" + "6 7 8\n\n" + + "@tsexample\n" + "singleton GuiControlProfile (BorderGUIProfile)\n" + "{\n" + " bitmap = \"core/art/gui/images/borderArray\";\n" + " hasBitmapArray = true;\n" + " opaque = false;\n" + "};\n\n" + + "new GuiBitmapBorderCtrl(BitmapBorderGUI)\n" + "{\n" + " profile = \"BorderGUIProfile\";\n" + " position = \"0 0\";\n" + " extent = \"400 40\";\n" + " visible = \"1\";\n" + "};" + "@endtsexample\n\n" + + "@see GuiControlProfile::bitmapName\n" + "@see GuiControlProfile::hasBitmapArray\n\n" + + "@ingroup GuiImages" +); + +bool GuiBitmapBorderCtrl::onWake() +{ + if (! Parent::onWake()) + return false; + + //get the texture for the close, minimize, and maximize buttons + mBitmapBounds = NULL; + mTextureObject = mProfile->mTextureObject; + if( mProfile->constructBitmapArray() >= NumBitmaps ) + mBitmapBounds = mProfile->mBitmapArrayRects.address(); + else + Con::errorf( "GuiBitmapBorderCtrl: Could not construct bitmap array for profile '%s'", mProfile->getName() ); + + return true; +} + +void GuiBitmapBorderCtrl::onSleep() +{ + mTextureObject = NULL; + mBitmapBounds = NULL; + + Parent::onSleep(); +} + +void GuiBitmapBorderCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + renderChildControls( offset, updateRect ); + + if( mBitmapBounds ) + { + GFX->setClipRect(updateRect); + + //draw the outline + RectI winRect; + winRect.point = offset; + winRect.extent = getExtent(); + + winRect.point.x += mBitmapBounds[BorderLeft].extent.x; + winRect.point.y += mBitmapBounds[BorderTop].extent.y; + + winRect.extent.x -= mBitmapBounds[BorderLeft].extent.x + mBitmapBounds[BorderRight].extent.x; + winRect.extent.y -= mBitmapBounds[BorderTop].extent.y + mBitmapBounds[BorderBottom].extent.y; + + if(mProfile->mOpaque) + GFX->getDrawUtil()->drawRectFill(winRect, mProfile->mFillColor); + + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapSR(mTextureObject, offset, mBitmapBounds[BorderTopLeft]); + GFX->getDrawUtil()->drawBitmapSR(mTextureObject, Point2I(offset.x + getWidth() - mBitmapBounds[BorderTopRight].extent.x, offset.y), + mBitmapBounds[BorderTopRight]); + + RectI destRect; + destRect.point.x = offset.x + mBitmapBounds[BorderTopLeft].extent.x; + destRect.point.y = offset.y; + destRect.extent.x = getWidth() - mBitmapBounds[BorderTopLeft].extent.x - mBitmapBounds[BorderTopRight].extent.x; + destRect.extent.y = mBitmapBounds[BorderTop].extent.y; + RectI stretchRect = mBitmapBounds[BorderTop]; + stretchRect.inset(1,0); + GFX->getDrawUtil()->drawBitmapStretchSR(mTextureObject, destRect, stretchRect); + + destRect.point.x = offset.x; + destRect.point.y = offset.y + mBitmapBounds[BorderTopLeft].extent.y; + destRect.extent.x = mBitmapBounds[BorderLeft].extent.x; + destRect.extent.y = getHeight() - mBitmapBounds[BorderTopLeft].extent.y - mBitmapBounds[BorderBottomLeft].extent.y; + stretchRect = mBitmapBounds[BorderLeft]; + stretchRect.inset(0,1); + GFX->getDrawUtil()->drawBitmapStretchSR(mTextureObject, destRect, stretchRect); + + destRect.point.x = offset.x + getWidth() - mBitmapBounds[BorderRight].extent.x; + destRect.extent.x = mBitmapBounds[BorderRight].extent.x; + destRect.point.y = offset.y + mBitmapBounds[BorderTopRight].extent.y; + destRect.extent.y = getHeight() - mBitmapBounds[BorderTopRight].extent.y - mBitmapBounds[BorderBottomRight].extent.y; + + stretchRect = mBitmapBounds[BorderRight]; + stretchRect.inset(0,1); + GFX->getDrawUtil()->drawBitmapStretchSR(mTextureObject, destRect, stretchRect); + + GFX->getDrawUtil()->drawBitmapSR(mTextureObject, offset + Point2I(0, getHeight() - mBitmapBounds[BorderBottomLeft].extent.y), mBitmapBounds[BorderBottomLeft]); + GFX->getDrawUtil()->drawBitmapSR(mTextureObject, offset + getExtent() - mBitmapBounds[BorderBottomRight].extent, mBitmapBounds[BorderBottomRight]); + + destRect.point.x = offset.x + mBitmapBounds[BorderBottomLeft].extent.x; + destRect.extent.x = getWidth() - mBitmapBounds[BorderBottomLeft].extent.x - mBitmapBounds[BorderBottomRight].extent.x; + + destRect.point.y = offset.y + getHeight() - mBitmapBounds[BorderBottom].extent.y; + destRect.extent.y = mBitmapBounds[BorderBottom].extent.y; + stretchRect = mBitmapBounds[BorderBottom]; + stretchRect.inset(1,0); + + GFX->getDrawUtil()->drawBitmapStretchSR(mTextureObject, destRect, stretchRect); + } +} diff --git a/Engine/source/gui/controls/guiBitmapCtrl.cpp b/Engine/source/gui/controls/guiBitmapCtrl.cpp new file mode 100644 index 000000000..29766d197 --- /dev/null +++ b/Engine/source/gui/controls/guiBitmapCtrl.cpp @@ -0,0 +1,266 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/controls/guiBitmapCtrl.h" + +#include "console/console.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxDrawUtil.h" + + +IMPLEMENT_CONOBJECT(GuiBitmapCtrl); + +ConsoleDocClass( GuiBitmapCtrl, + "@brief A gui control that is used to display an image.\n\n" + + "The image is stretched to the constraints of the control by default. However, the control can also\n" + "tile the image as well.\n\n" + + "The image itself is stored inside the GuiBitmapCtrl::bitmap field. The boolean value that decides\n" + "whether the image is stretched or tiled is stored inside the GuiBitmapCtrl::wrap field.\n" + + "@tsexample\n" + "// Create a tiling GuiBitmapCtrl that displays \"myImage.png\"\n" + "%bitmapCtrl = new GuiBitmapCtrl()\n" + "{\n" + " bitmap = \"myImage.png\";\n" + " wrap = \"true\";\n" + "};\n" + "@endtsexample\n\n" + + "@ingroup GuiControls" +); + +GuiBitmapCtrl::GuiBitmapCtrl(void) + : mBitmapName(), + mStartPoint( 0, 0 ), + mWrap( false ) +{ +} + +bool GuiBitmapCtrl::setBitmapName( void *object, const char *index, const char *data ) +{ + // Prior to this, you couldn't do bitmap.bitmap = "foo.jpg" and have it work. + // With protected console types you can now call the setBitmap function and + // make it load the image. + static_cast( object )->setBitmap( data ); + + // Return false because the setBitmap method will assign 'mBitmapName' to the + // argument we are specifying in the call. + return false; +} + +void GuiBitmapCtrl::initPersistFields() +{ + addGroup( "Bitmap" ); + + addProtectedField( "bitmap", TypeImageFilename, Offset( mBitmapName, GuiBitmapCtrl ), + &setBitmapName, &defaultProtectedGetFn, + "The bitmap file to display in the control." ); + addField( "wrap", TypeBool, Offset( mWrap, GuiBitmapCtrl ), + "If true, the bitmap is tiled inside the control rather than stretched to fit." ); + + endGroup( "Bitmap" ); + + Parent::initPersistFields(); +} + +bool GuiBitmapCtrl::onWake() +{ + if (! Parent::onWake()) + return false; + setActive(true); + setBitmap(mBitmapName); + return true; +} + +void GuiBitmapCtrl::onSleep() +{ + if ( !mBitmapName.equal("texhandle", String::NoCase) ) + mTextureObject = NULL; + + Parent::onSleep(); +} + +//------------------------------------- +void GuiBitmapCtrl::inspectPostApply() +{ + // if the extent is set to (0,0) in the gui editor and appy hit, this control will + // set it's extent to be exactly the size of the bitmap (if present) + Parent::inspectPostApply(); + + if (!mWrap && (getExtent().x == 0) && (getExtent().y == 0) && mTextureObject) + { + setExtent( mTextureObject->getWidth(), mTextureObject->getHeight()); + } +} + +void GuiBitmapCtrl::setBitmap( const char *name, bool resize ) +{ + mBitmapName = name; + if ( !isAwake() ) + return; + + if ( mBitmapName.isNotEmpty() ) + { + if ( !mBitmapName.equal("texhandle", String::NoCase) ) + mTextureObject.set( mBitmapName, &GFXDefaultGUIProfile, avar("%s() - mTextureObject (line %d)", __FUNCTION__, __LINE__) ); + + // Resize the control to fit the bitmap + if ( mTextureObject && resize ) + { + setExtent( mTextureObject->getWidth(), mTextureObject->getHeight() ); + updateSizing(); + } + } + else + mTextureObject = NULL; + + setUpdate(); +} + +void GuiBitmapCtrl::updateSizing() +{ + if(!getParent()) + return; + // updates our bounds according to our horizSizing and verSizing rules + RectI fakeBounds( getPosition(), getParent()->getExtent()); + parentResized( fakeBounds, fakeBounds); +} + +void GuiBitmapCtrl::setBitmapHandle(GFXTexHandle handle, bool resize) +{ + mTextureObject = handle; + + mBitmapName = String("texhandle"); + + // Resize the control to fit the bitmap + if (resize) + { + setExtent(mTextureObject->getWidth(), mTextureObject->getHeight()); + updateSizing(); + } +} + +void GuiBitmapCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + if (mTextureObject) + { + GFX->getDrawUtil()->clearBitmapModulation(); + if(mWrap) + { + // We manually draw each repeat because non power of two textures will + // not tile correctly when rendered with GFX->drawBitmapTile(). The non POT + // bitmap will be padded by the hardware, and we'll see lots of slack + // in the texture. So... lets do what we must: draw each repeat by itself: + GFXTextureObject* texture = mTextureObject; + RectI srcRegion; + RectI dstRegion; + float xdone = ((float)getExtent().x/(float)texture->mBitmapSize.x)+1; + float ydone = ((float)getExtent().y/(float)texture->mBitmapSize.y)+1; + + int xshift = mStartPoint.x%texture->mBitmapSize.x; + int yshift = mStartPoint.y%texture->mBitmapSize.y; + for(int y = 0; y < ydone; ++y) + for(int x = 0; x < xdone; ++x) + { + srcRegion.set(0,0,texture->mBitmapSize.x,texture->mBitmapSize.y); + dstRegion.set( ((texture->mBitmapSize.x*x)+offset.x)-xshift, + ((texture->mBitmapSize.y*y)+offset.y)-yshift, + texture->mBitmapSize.x, + texture->mBitmapSize.y); + GFX->getDrawUtil()->drawBitmapStretchSR(texture,dstRegion, srcRegion, GFXBitmapFlip_None, GFXTextureFilterLinear); + } + + } + else + { + RectI rect(offset, getExtent()); + GFX->getDrawUtil()->drawBitmapStretch(mTextureObject, rect, GFXBitmapFlip_None, GFXTextureFilterLinear, false); + } + } + + if (mProfile->mBorder || !mTextureObject) + { + RectI rect(offset.x, offset.y, getExtent().x, getExtent().y); + GFX->getDrawUtil()->drawRect(rect, mProfile->mBorderColor); + } + + renderChildControls(offset, updateRect); +} + +void GuiBitmapCtrl::setValue(S32 x, S32 y) +{ + if (mTextureObject) + { + x += mTextureObject->getWidth() / 2; + y += mTextureObject->getHeight() / 2; + } + while (x < 0) + x += 256; + mStartPoint.x = x % 256; + + while (y < 0) + y += 256; + mStartPoint.y = y % 256; +} + +DefineEngineMethod( GuiBitmapCtrl, setValue, void, ( S32 x, S32 y ),, + "Set the offset of the bitmap within the control.\n" + "@param x The x-axis offset of the image.\n" + "@param y The y-axis offset of the image.\n") +{ + object->setValue(x, y); +} + + +// + +static ConsoleDocFragment _sGuiBitmapCtrlSetBitmap1( + "@brief Assign an image to the control.\n\n" + "Child controls with resize according to their layout settings.\n" + "@param filename The filename of the image.\n" + "@param resize Optional parameter. If true, the GUI will resize to fit the image.", + "GuiBitmapCtrl", // The class to place the method in; use NULL for functions. + "void setBitmap( String filename, bool resize );" ); // The definition string. + +static ConsoleDocFragment _sGuiBitmapCtrlSetBitmap2( + "@brief Assign an image to the control.\n\n" + "Child controls will resize according to their layout settings.\n" + "@param filename The filename of the image.\n" + "@param resize A boolean value that decides whether the ctrl refreshes or not.", + "GuiBitmapCtrl", // The class to place the method in; use NULL for functions. + "void setBitmap( String filename );" ); // The definition string. + + +//"Set the bitmap displayed in the control. Note that it is limited in size, to 256x256." +ConsoleMethod( GuiBitmapCtrl, setBitmap, void, 3, 4, + "( String filename | String filename, bool resize ) Assign an image to the control.\n\n" + "@hide" ) +{ + char filename[1024]; + Con::expandScriptFilename(filename, sizeof(filename), argv[2]); + object->setBitmap(filename, argc > 3 ? dAtob( argv[3] ) : false ); +} diff --git a/Engine/source/gui/controls/guiBitmapCtrl.h b/Engine/source/gui/controls/guiBitmapCtrl.h new file mode 100644 index 000000000..0eb8af353 --- /dev/null +++ b/Engine/source/gui/controls/guiBitmapCtrl.h @@ -0,0 +1,78 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIBITMAPCTRL_H_ +#define _GUIBITMAPCTRL_H_ + +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif + +/// Renders a bitmap. +class GuiBitmapCtrl : public GuiControl +{ + public: + + typedef GuiControl Parent; + + protected: + + /// Name of the bitmap file. If this is 'texhandle' the bitmap is not loaded + /// from a file but rather set explicitly on the control. + String mBitmapName; + + /// Loaded texture. + GFXTexHandle mTextureObject; + + Point2I mStartPoint; + + /// If true, bitmap tiles inside control. Otherwise stretches. + bool mWrap; + + static bool setBitmapName( void *object, const char *index, const char *data ); + static const char *getBitmapName( void *obj, const char *data ); + + public: + + GuiBitmapCtrl(); + static void initPersistFields(); + + void setBitmap(const char *name,bool resize = false); + void setBitmapHandle(GFXTexHandle handle, bool resize = false); + + // GuiControl. + bool onWake(); + void onSleep(); + void inspectPostApply(); + + void updateSizing(); + + void onRender(Point2I offset, const RectI &updateRect); + void setValue(S32 x, S32 y); + + DECLARE_CONOBJECT( GuiBitmapCtrl ); + DECLARE_CATEGORY( "Gui Images" ); + DECLARE_DESCRIPTION( "A control that displays a single, static image from a file.\n" + "The bitmap can either be tiled or stretched inside the control." ); +}; + +#endif diff --git a/Engine/source/gui/controls/guiColorPicker.cpp b/Engine/source/gui/controls/guiColorPicker.cpp new file mode 100644 index 000000000..c8c34eebe --- /dev/null +++ b/Engine/source/gui/controls/guiColorPicker.cpp @@ -0,0 +1,548 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#include "console/console.h" +#include "gfx/gfxDevice.h" +#include "console/consoleTypes.h" +#include "gui/core/guiCanvas.h" +#include "gui/buttons/guiButtonCtrl.h" +#include "gui/core/guiDefaultControlRender.h" +#include "gui/controls/guiColorPicker.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxDrawUtil.h" + +/// @name Common colors we use +/// @{ +ColorF colorWhite(1.,1.,1.); +ColorF colorWhiteBlend(1.,1.,1.,.75); +ColorF colorBlack(.0,.0,.0); +ColorF colorAlpha(0.0f, 0.0f, 0.0f, 0.0f); +ColorF colorAlphaW(1.0f, 1.0f, 1.0f, 0.0f); + +ColorI GuiColorPickerCtrl::mColorRange[7] = { + ColorI(255,0,0), // Red + ColorI(255,0,255), // Pink + ColorI(0,0,255), // Blue + ColorI(0,255,255), // Light blue + ColorI(0,255,0), // Green + ColorI(255,255,0), // Yellow + ColorI(255,0,0), // Red +}; +/// @} + +IMPLEMENT_CONOBJECT(GuiColorPickerCtrl); + +ConsoleDocClass( GuiColorPickerCtrl, + "@brief Editor GUI used for picking a ColorF from a palette.\n\n" + "@note Editor use only.\n\n" + "@internal" +); + +//-------------------------------------------------------------------------- +GuiColorPickerCtrl::GuiColorPickerCtrl() +{ + setExtent(140, 30); + mDisplayMode = pPallet; + mBaseColor = ColorF(1.,.0,1.); + mPickColor = ColorF(.0,.0,.0); + mSelectorPos = Point2I(0,0); + mMouseDown = mMouseOver = false; + mActive = true; + mPositionChanged = false; + mSelectorGap = 1; + mActionOnMove = false; + mShowReticle = true; +} + +//-------------------------------------------------------------------------- + +ImplementEnumType( GuiColorPickMode, + "\n\n" + "@ingroup GuiUtil" + "@internal" ) + { GuiColorPickerCtrl::pPallet, "Pallete" }, + { GuiColorPickerCtrl::pHorizColorRange, "HorizColor"}, + { GuiColorPickerCtrl::pVertColorRange, "VertColor" }, + { GuiColorPickerCtrl::pHorizColorBrightnessRange, "HorizBrightnessColor"}, + { GuiColorPickerCtrl::pVertColorBrightnessRange, "VertBrightnessColor" }, + { GuiColorPickerCtrl::pBlendColorRange, "BlendColor"}, + { GuiColorPickerCtrl::pHorizAlphaRange, "HorizAlpha"}, + { GuiColorPickerCtrl::pVertAlphaRange, "VertAlpha" }, + { GuiColorPickerCtrl::pDropperBackground, "Dropper" }, +EndImplementEnumType; + +//-------------------------------------------------------------------------- +void GuiColorPickerCtrl::initPersistFields() +{ + addGroup("ColorPicker"); + + addField("baseColor", TypeColorF, Offset(mBaseColor, GuiColorPickerCtrl)); + addField("pickColor", TypeColorF, Offset(mPickColor, GuiColorPickerCtrl)); + addField("selectorGap", TypeS32, Offset(mSelectorGap, GuiColorPickerCtrl)); + addField("displayMode", TYPEID< PickMode >(), Offset(mDisplayMode, GuiColorPickerCtrl) ); + addField("actionOnMove", TypeBool,Offset(mActionOnMove, GuiColorPickerCtrl)); + addField("showReticle", TypeBool, Offset(mShowReticle, GuiColorPickerCtrl)); + + endGroup("ColorPicker"); + + Parent::initPersistFields(); +} + +//-------------------------------------------------------------------------- +// Function to draw a box which can have 4 different colors in each corner blended together +void GuiColorPickerCtrl::drawBlendBox(RectI &bounds, ColorF &c1, ColorF &c2, ColorF &c3, ColorF &c4) +{ + GFX->setStateBlock(mStateBlock); + + S32 l = bounds.point.x, r = bounds.point.x + bounds.extent.x; + S32 t = bounds.point.y, b = bounds.point.y + bounds.extent.y; + + //A couple of checks to determine if color blend + if(c1 == colorWhite && c3 == colorAlpha && c4 == colorBlack) + { + //Color + PrimBuild::begin( GFXTriangleFan, 4 ); + PrimBuild::color( c2 ); + PrimBuild::vertex2i( r, t ); + + PrimBuild::color( c2 ); + PrimBuild::vertex2i( r, b ); + + PrimBuild::color( c2 ); + PrimBuild::vertex2i( l, b ); + + PrimBuild::color( c2 ); + PrimBuild::vertex2i( l, t ); + PrimBuild::end(); + + //White + PrimBuild::begin( GFXTriangleFan, 4 ); + PrimBuild::color( colorAlphaW ); + PrimBuild::vertex2i( r, t ); + + PrimBuild::color( colorAlphaW ); + PrimBuild::vertex2i( r, b ); + + PrimBuild::color( c1 ); + PrimBuild::vertex2i( l, b ); + + PrimBuild::color( c1 ); + PrimBuild::vertex2i( l, t ); + PrimBuild::end(); + + //Black + PrimBuild::begin( GFXTriangleFan, 4 ); + PrimBuild::color( c3 ); + PrimBuild::vertex2i( r, t ); + + PrimBuild::color( c4 ); + PrimBuild::vertex2i( r, b ); + + PrimBuild::color( c4 ); + PrimBuild::vertex2i( l, b ); + + PrimBuild::color( c3 ); + PrimBuild::vertex2i( l, t ); + PrimBuild::end(); + } + else + { + PrimBuild::begin( GFXTriangleFan, 4 ); + PrimBuild::color( c1 ); + PrimBuild::vertex2i( l, t ); + + PrimBuild::color( c2 ); + PrimBuild::vertex2i( r, t ); + + PrimBuild::color( c3 ); + PrimBuild::vertex2i( r, b ); + + PrimBuild::color( c4 ); + PrimBuild::vertex2i( l, b ); + PrimBuild::end(); + } +} + +//-------------------------------------------------------------------------- +/// Function to draw a set of boxes blending throughout an array of colors +void GuiColorPickerCtrl::drawBlendRangeBox(RectI &bounds, bool vertical, U8 numColors, ColorI *colors) +{ + + GFX->setStateBlock(mStateBlock); + + S32 l = bounds.point.x, r = bounds.point.x + bounds.extent.x + 4; + S32 t = bounds.point.y, b = bounds.point.y + bounds.extent.y + 4; + + // Calculate increment value + S32 x_inc = int(mFloor((r - l) / F32(numColors-1))); + S32 y_inc = int(mFloor((b - t) / F32(numColors-1))); + + for( U16 i = 0;i < numColors - 1; i++ ) + { + // This is not efficent, but then again it doesn't really need to be. -pw + PrimBuild::begin( GFXTriangleFan, 4 ); + + if (!vertical) // Horizontal (+x) + { + // First color + PrimBuild::color( colors[i] ); + PrimBuild::vertex2i( l, t ); + PrimBuild::vertex2i( l, b ); + + // Second color + PrimBuild::color( colors[i+1] ); + PrimBuild::vertex2i( l + x_inc, b ); + PrimBuild::vertex2i( l + x_inc, t ); + l += x_inc; + } + else // Vertical (+y) + { + // First color + PrimBuild::color( colors[i] ); + PrimBuild::vertex2i( l, t ); + PrimBuild::vertex2i( r, t ); + + // Second color + PrimBuild::color( colors[i+1] ); + PrimBuild::vertex2i( r, t + y_inc ); + PrimBuild::vertex2i( l, t + y_inc ); + t += y_inc; + } + PrimBuild::end(); + } +} + +void GuiColorPickerCtrl::drawSelector(RectI &bounds, Point2I &selectorPos, SelectorMode mode) +{ + if( !mShowReticle ) + return; + + U16 sMax = mSelectorGap*2; + switch (mode) + { + case sVertical: + // Now draw the vertical selector + // Up -> Pos + if (selectorPos.y != bounds.point.y+1) + GFX->getDrawUtil()->drawLine(selectorPos.x, bounds.point.y, selectorPos.x, selectorPos.y-sMax-1, colorWhiteBlend); + // Down -> Pos + if (selectorPos.y != bounds.point.y+bounds.extent.y) + GFX->getDrawUtil()->drawLine(selectorPos.x, selectorPos.y + sMax, selectorPos.x, bounds.point.y + bounds.extent.y, colorWhiteBlend); + break; + case sHorizontal: + // Now draw the horizontal selector + // Left -> Pos + if (selectorPos.x != bounds.point.x) + GFX->getDrawUtil()->drawLine(bounds.point.x, selectorPos.y-1, selectorPos.x-sMax, selectorPos.y-1, colorWhiteBlend); + // Right -> Pos + if (selectorPos.x != bounds.point.x) + GFX->getDrawUtil()->drawLine(bounds.point.x+mSelectorPos.x+sMax, selectorPos.y-1, bounds.point.x + bounds.extent.x, selectorPos.y-1, colorWhiteBlend); + break; + } +} + +//-------------------------------------------------------------------------- +/// Function to invoke calls to draw the picker box and selector +void GuiColorPickerCtrl::renderColorBox(RectI &bounds) +{ + RectI pickerBounds; + pickerBounds.point.x = bounds.point.x+1; + pickerBounds.point.y = bounds.point.y+1; + pickerBounds.extent.x = bounds.extent.x-1; + pickerBounds.extent.y = bounds.extent.y-1; + + if (mProfile->mBorder) + GFX->getDrawUtil()->drawRect(bounds, mProfile->mBorderColor); + + Point2I selectorPos = Point2I(bounds.point.x+mSelectorPos.x+1, bounds.point.y+mSelectorPos.y+1); + + // Draw color box differently depending on mode + RectI blendRect; + switch (mDisplayMode) + { + case pHorizColorRange: + drawBlendRangeBox( pickerBounds, false, 7, mColorRange); + drawSelector( pickerBounds, selectorPos, sVertical ); + break; + case pVertColorRange: + drawBlendRangeBox( pickerBounds, true, 7, mColorRange); + drawSelector( pickerBounds, selectorPos, sHorizontal ); + break; + case pHorizColorBrightnessRange: + blendRect = pickerBounds; + blendRect.point.y++; + blendRect.extent.y -= 2; + drawBlendRangeBox( pickerBounds, false, 7, mColorRange); + // This is being drawn slightly offset from the larger rect so as to insure 255 and 0 + // can both be selected for every color. + drawBlendBox( blendRect, colorAlpha, colorAlpha, colorBlack, colorBlack ); + blendRect.point.y += blendRect.extent.y - 1; + blendRect.extent.y = 2; + GFX->getDrawUtil()->drawRect( blendRect, colorBlack); + drawSelector( pickerBounds, selectorPos, sHorizontal ); + drawSelector( pickerBounds, selectorPos, sVertical ); + break; + case pVertColorBrightnessRange: + drawBlendRangeBox( pickerBounds, true, 7, mColorRange); + drawBlendBox( pickerBounds, colorAlpha, colorBlack, colorBlack, colorAlpha ); + drawSelector( pickerBounds, selectorPos, sHorizontal ); + drawSelector( pickerBounds, selectorPos, sVertical ); + break; + case pHorizAlphaRange: + drawBlendBox( pickerBounds, colorBlack, colorWhite, colorWhite, colorBlack ); + drawSelector( pickerBounds, selectorPos, sVertical ); + break; + case pVertAlphaRange: + drawBlendBox( pickerBounds, colorBlack, colorBlack, colorWhite, colorWhite ); + drawSelector( pickerBounds, selectorPos, sHorizontal ); + break; + case pBlendColorRange: + drawBlendBox( pickerBounds, colorWhite, mBaseColor, colorAlpha, colorBlack ); + drawSelector( pickerBounds, selectorPos, sHorizontal ); + drawSelector( pickerBounds, selectorPos, sVertical ); + break; + case pDropperBackground: + break; + case pPallet: + default: + GFX->getDrawUtil()->drawRectFill( pickerBounds, mBaseColor ); + break; + } +} + +void GuiColorPickerCtrl::onRender(Point2I offset, const RectI& updateRect) +{ + if (mStateBlock.isNull()) + { + GFXStateBlockDesc desc; + desc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha); + desc.setZReadWrite(false); + desc.zWriteEnable = false; + desc.setCullMode(GFXCullNone); + mStateBlock = GFX->createStateBlock( desc ); + } + + RectI boundsRect(offset, getExtent()); + renderColorBox(boundsRect); + + if (mPositionChanged) + { + mPositionChanged = false; + Point2I extent = getRoot()->getExtent(); + // If we are anything but a pallete, change the pick color + if (mDisplayMode != pPallet) + { + Point2I resolution = getRoot()->getExtent(); + + U32 buf_x = offset.x + mSelectorPos.x + 1; + U32 buf_y = ( extent.y - ( offset.y + mSelectorPos.y + 1 ) ); + if(GFX->getAdapterType() != OpenGL) + buf_y = resolution.y - buf_y; + + GFXTexHandle bb( resolution.x, + resolution.y, + GFXFormatR8G8B8A8, &GFXDefaultRenderTargetProfile, avar("%s() - bb (line %d)", __FUNCTION__, __LINE__) ); + + Point2I tmpPt( buf_x, buf_y ); + + GFXTarget *targ = GFX->getActiveRenderTarget(); + targ->resolveTo( bb ); + + GBitmap bmp( bb.getWidth(), bb.getHeight() ); + + bb.copyToBmp( &bmp ); + + //bmp.writePNGDebug( "foo.png" ); + + ColorI tmp; + bmp.getColor( buf_x, buf_y, tmp ); + + mPickColor = (ColorF)tmp; + + // Now do onAction() if we are allowed + if (mActionOnMove) + onAction(); + } + + } + + //render the children + renderChildControls( offset, updateRect); +} + +//-------------------------------------------------------------------------- +void GuiColorPickerCtrl::setSelectorPos(const Point2I &pos) +{ + Point2I extent = getExtent(); + RectI rect; + if (mDisplayMode != pDropperBackground) + { + extent.x -= 3; + extent.y -= 2; + rect = RectI(Point2I(1,1), extent); + } + else + { + rect = RectI(Point2I(0,0), extent); + } + + if (rect.pointInRect(pos)) + { + mSelectorPos = pos; + mPositionChanged = true; + // We now need to update + setUpdate(); + } + + else + { + if ((pos.x > rect.point.x) && (pos.x < (rect.point.x + rect.extent.x))) + mSelectorPos.x = pos.x; + else if (pos.x <= rect.point.x) + mSelectorPos.x = rect.point.x; + else if (pos.x >= (rect.point.x + rect.extent.x)) + mSelectorPos.x = rect.point.x + rect.extent.x - 1; + + if ((pos.y > rect.point.y) && (pos.y < (rect.point.y + rect.extent.y))) + mSelectorPos.y = pos.y; + else if (pos.y <= rect.point.y) + mSelectorPos.y = rect.point.y; + else if (pos.y >= (rect.point.y + rect.extent.y)) + mSelectorPos.y = rect.point.y + rect.extent.y - 1; + + mPositionChanged = true; + setUpdate(); + } +} + +//-------------------------------------------------------------------------- +void GuiColorPickerCtrl::onMouseDown(const GuiEvent &event) +{ + if (!mActive) + return; + + if (mDisplayMode == pDropperBackground) + return; + + mouseLock(this); + + if (mProfile->mCanKeyFocus) + setFirstResponder(); + + if (mActive && (mDisplayMode != pDropperBackground)) + onAction(); + + // Update the picker cross position + if (mDisplayMode != pPallet) + setSelectorPos(globalToLocalCoord(event.mousePoint)); + + mMouseDown = true; +} + +//-------------------------------------------------------------------------- +void GuiColorPickerCtrl::onMouseDragged(const GuiEvent &event) +{ + if ((mActive && mMouseDown) || (mActive && (mDisplayMode == pDropperBackground))) + { + // Update the picker cross position + if (mDisplayMode != pPallet) + setSelectorPos(globalToLocalCoord(event.mousePoint)); + } + + if( !mActionOnMove ) + execAltConsoleCallback(); + +} + +//-------------------------------------------------------------------------- +void GuiColorPickerCtrl::onMouseMove(const GuiEvent &event) +{ + // Only for dropper mode + if (mActive && (mDisplayMode == pDropperBackground)) + setSelectorPos(globalToLocalCoord(event.mousePoint)); +} + +//-------------------------------------------------------------------------- +void GuiColorPickerCtrl::onMouseEnter(const GuiEvent &event) +{ + mMouseOver = true; +} + +//-------------------------------------------------------------------------- +void GuiColorPickerCtrl::onMouseLeave(const GuiEvent &) +{ + // Reset state + mMouseOver = false; +} + +//-------------------------------------------------------------------------- +void GuiColorPickerCtrl::onMouseUp(const GuiEvent &) +{ + //if we released the mouse within this control, perform the action + if (mActive && mMouseDown && (mDisplayMode != pDropperBackground)) + mMouseDown = false; + + if (mActive && (mDisplayMode == pDropperBackground)) + { + // In a dropper, the alt command executes the mouse up action (to signal stopping) + execAltConsoleCallback(); + } + + mouseUnlock(); +} + +//-------------------------------------------------------------------------- +const char *GuiColorPickerCtrl::getScriptValue() +{ + static char temp[256]; + ColorF color = getValue(); + dSprintf(temp,256,"%f %f %f %f",color.red, color.green, color.blue, color.alpha); + return temp; +} + +//-------------------------------------------------------------------------- +void GuiColorPickerCtrl::setScriptValue(const char *value) +{ + ColorF newValue; + dSscanf(value, "%f %f %f %f", &newValue.red, &newValue.green, &newValue.blue, &newValue.alpha); + setValue(newValue); +} + +ConsoleMethod(GuiColorPickerCtrl, getSelectorPos, const char*, 2, 2, "Gets the current position of the selector") +{ + char *temp = Con::getReturnBuffer(256); + Point2I pos; + pos = object->getSelectorPos(); + dSprintf(temp,256,"%d %d",pos.x, pos.y); + return temp; +} + +ConsoleMethod(GuiColorPickerCtrl, setSelectorPos, void, 3, 3, "Sets the current position of the selector") +{ + Point2I newPos; + dSscanf(argv[2], "%d %d", &newPos.x, &newPos.y); + object->setSelectorPos(newPos); +} + +ConsoleMethod(GuiColorPickerCtrl, updateColor, void, 2, 2, "Forces update of pick color") +{ + object->updateColor(); +} diff --git a/Engine/source/gui/controls/guiColorPicker.h b/Engine/source/gui/controls/guiColorPicker.h new file mode 100644 index 000000000..a9421b7f7 --- /dev/null +++ b/Engine/source/gui/controls/guiColorPicker.h @@ -0,0 +1,152 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _GUICOLORPICKER_H_ +#define _GUICOLORPICKER_H_ + +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif + +//---------------------------- +/// GuiColorPickerCtrl +/// +/// This control draws a box containing a color specified by mPickColor, +/// in a way according to one of the PickMode enum's, stored as mDisplayMode. +/// +/// The color the box represents is stored as mBaseColour (for pPallete, pBlendColorRange), +/// whilst the color chosen by the box is stored as mPickColor. +/// +/// Whenever the control is clicked, it will do one of many things : +/// +/// -# If its in pPallete mode, execute the regular "command" +/// -# If its in pBlendColorRange mode, update the selector position. The position will be re-read upon the next render. In addition, a cross will be drawn where the color has been selected from. As with 1, "command" will be executed. +/// -# If its in pHorizColorRange or pVertColorRange mode, it will function in a similar manner to 2, but the selector will resemble a horizontal or vertical bar. +/// -# If its in pHorizAlphaRange or pVertAlphaRange mode, it will also function the same way as 3 +/// -# If its in pDropperBackground mode, nothing will happen +/// +/// Colours are drawn in different ways according to mDisplayMode: +/// +/// -# With pPallete, a box with a blank color, mBaseColor is drawn. +/// -# With pHorizColorRange, a horizontal box with colors blending in the range, mColorRange. +/// -# With pVertColorRange, a vertical box with colors blending in the range, mColorRange. +/// -# With pBlendColorRange, a box, the bottom colors being black, but the top left being white, and the top right being mBaseColor. +/// -# With pHorizAlphaRange, a horizontal box with black blending with an alpha from 0 to 255 +/// -# With pVertAlphaRange, a vertical box with black blending with an apha from 0 to 255 +/// -# With pDropperBackground, nothing is drawn +class GuiColorPickerCtrl : public GuiControl +{ + typedef GuiControl Parent; + + public: + enum PickMode + { + pPallet = 0, ///< We just have a solid color; We just act like a pallet + pHorizColorRange, ///< We have a range of base colors going horizontally + pVertColorRange, ///< We have a range of base colors going vertically + pHorizColorBrightnessRange, ///< HorizColorRange with brightness + pVertColorBrightnessRange, ///< VertColorRange with brightness + pBlendColorRange, ///< We have a box which shows a range in brightness of the color + pHorizAlphaRange, ///< We have a box which shows a range in alpha going horizontally + pVertAlphaRange, ///< We have a box which shows a range in alpha going vertically + pDropperBackground ///< The control does not draw anything; Only does something when you click, or move the mouse (when active) + }; + + enum SelectorMode + { + sHorizontal = 0, ///< Horizontal selector with small gap + sVertical, ///< Vertical selector with small gap + }; + + protected: + + /// @name Core Rendering functions + /// @{ + void renderColorBox(RectI &bounds); ///< Function that draws the actual color box + void drawSelector(RectI &bounds, Point2I &selectorPos, SelectorMode mode); ///< Function that draws the selection indicator + void drawBlendBox(RectI &bounds, ColorF &c1, ColorF &c2, ColorF &c3, ColorF &c4); + void drawBlendRangeBox(RectI &bounds, bool vertical, U8 numColors, ColorI *colors); + /// @} + + /// @name Core Variables + /// @{ + ColorF mPickColor; ///< Color that has been picked from control + ColorF mBaseColor; ///< Colour we display (in case of pallet and blend mode) + PickMode mDisplayMode; ///< Current color display mode of the selector + + Point2I mSelectorPos; ///< Current position of the selector + bool mPositionChanged; ///< Current position has changed since last render? + bool mMouseOver; ///< Mouse is over? + bool mMouseDown; ///< Mouse button down? + bool mActionOnMove; ///< Perform onAction() when position has changed? + + + + S32 mSelectorGap; ///< The half-way "gap" between the selector pos and where the selector is allowed to draw. + + GFXStateBlockRef mStateBlock; + + static ColorI mColorRange[7]; ///< Color range for pHorizColorRange and pVertColorRange + /// @} + + public: + + DECLARE_CONOBJECT(GuiColorPickerCtrl); + DECLARE_CATEGORY( "Gui Editor" ); + + GuiColorPickerCtrl(); + + static void initPersistFields(); + void onRender(Point2I offset, const RectI &updateRect); + bool mShowReticle; ///< Show reticle on render + /// @name Color Value Functions + /// @{ + /// NOTE: setValue only sets baseColor, since setting pickColor wouldn't be useful + void setValue(ColorF &value) {mBaseColor = value;} + /// NOTE: getValue() returns baseColor if pallet (since pallet controls can't "pick" colours themselves) + ColorF getValue() {return mDisplayMode == pPallet ? mBaseColor : mPickColor;} + const char *getScriptValue(); + void setScriptValue(const char *value); + void updateColor() {mPositionChanged = true;} + /// @} + + /// @name Selector Functions + /// @{ + void setSelectorPos(const Point2I &pos); ///< Set new pos (in local coords) + Point2I getSelectorPos() {return mSelectorPos;} + /// @} + + /// @name Input Events + /// @{ + void onMouseDown(const GuiEvent &); + void onMouseUp(const GuiEvent &); + void onMouseMove(const GuiEvent &event); + void onMouseDragged(const GuiEvent &event); + + void onMouseEnter(const GuiEvent &); + void onMouseLeave(const GuiEvent &); + /// @} +}; + +typedef GuiColorPickerCtrl::PickMode GuiColorPickMode; +DefineEnumType( GuiColorPickMode ); + +#endif diff --git a/Engine/source/gui/controls/guiConsole.cpp b/Engine/source/gui/controls/guiConsole.cpp new file mode 100644 index 000000000..0a1e2459d --- /dev/null +++ b/Engine/source/gui/controls/guiConsole.cpp @@ -0,0 +1,173 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/console.h" +#include "gfx/gfxDrawUtil.h" +#include "gui/core/guiTypes.h" +#include "gui/core/guiControl.h" +#include "gui/controls/guiConsole.h" +#include "gui/containers/guiScrollCtrl.h" +#include "console/engineAPI.h" + +IMPLEMENT_CONOBJECT(GuiConsole); + +ConsoleDocClass( GuiConsole, + "@brief The on-screen, in-game console. Calls getLog() to get the on-screen console entries, then renders them as needed.\n\n" + + "@tsexample\n" + " new GuiConsole()\n" + " {\n" + " //Properties not specific to this control have been omitted from this example.\n" + " };\n" + "@endtsexample\n\n" + + "@see GuiControl\n\n" + + "@ingroup GuiCore" +); + +IMPLEMENT_CALLBACK( GuiConsole, onMessageSelected, void, ( ConsoleLogEntry::Level level, const char* message ), ( level, message ), + "Called when a message in the log is clicked.\n\n" + "@param level Diagnostic level of the message.\n" + "@param message Message text.\n" ); + + +//----------------------------------------------------------------------------- + +GuiConsole::GuiConsole() +{ + setExtent(64, 64); + mCellSize.set(1, 1); + mSize.set(1, 0); +} + +//----------------------------------------------------------------------------- + +bool GuiConsole::onWake() +{ + if (! Parent::onWake()) + return false; + + //get the font + mFont = mProfile->mFont; + + return true; +} + +//----------------------------------------------------------------------------- + +S32 GuiConsole::getMaxWidth(S32 startIndex, S32 endIndex) +{ + //sanity check + U32 size; + ConsoleLogEntry *log; + + Con::getLockLog(log, size); + + if(startIndex < 0 || (U32)endIndex >= size || startIndex > endIndex) + return 0; + + S32 result = 0; + for(S32 i = startIndex; i <= endIndex; i++) + result = getMax(result, (S32)(mFont->getStrWidth((const UTF8 *)log[i].mString))); + + Con::unlockLog(); + + return(result + 6); +} + +//----------------------------------------------------------------------------- + +void GuiConsole::onPreRender() +{ + //see if the size has changed + U32 prevSize = getHeight() / mCellSize.y; + U32 size; + ConsoleLogEntry *log; + + Con::getLockLog(log, size); + Con::unlockLog(); // we unlock immediately because we only use size here, not log. + + if(size != prevSize) + { + //first, find out if the console was scrolled up + bool scrolled = false; + GuiScrollCtrl *parent = dynamic_cast(getParent()); + + if(parent) + scrolled = parent->isScrolledToBottom(); + + //find the max cell width for the new entries + S32 newMax = getMaxWidth(prevSize, size - 1); + if(newMax > mCellSize.x) + mCellSize.set(newMax, mFont->getHeight()); + + //set the array size + mSize.set(1, size); + + //resize the control + setExtent( Point2I(mCellSize.x, mCellSize.y * size)); + + //if the console was not scrolled, make the last entry visible + if (scrolled) + scrollCellVisible(Point2I(0,mSize.y - 1)); + } +} + +//----------------------------------------------------------------------------- + +void GuiConsole::onRenderCell(Point2I offset, Point2I cell, bool /*selected*/, bool /*mouseOver*/) +{ + U32 size; + ConsoleLogEntry *log; + + Con::getLockLog(log, size); + + ConsoleLogEntry &entry = log[cell.y]; + switch (entry.mLevel) + { + case ConsoleLogEntry::Normal: GFX->getDrawUtil()->setBitmapModulation(mProfile->mFontColor); break; + case ConsoleLogEntry::Warning: GFX->getDrawUtil()->setBitmapModulation(mProfile->mFontColorHL); break; + case ConsoleLogEntry::Error: GFX->getDrawUtil()->setBitmapModulation(mProfile->mFontColorNA); break; + default: AssertFatal(false, "GuiConsole::onRenderCell - Unrecognized ConsoleLogEntry type, update this."); + } + GFX->getDrawUtil()->drawText(mFont, Point2I(offset.x + 3, offset.y), entry.mString, mProfile->mFontColors); + + Con::unlockLog(); +} + +//----------------------------------------------------------------------------- + +void GuiConsole::onCellSelected( Point2I cell ) +{ + Parent::onCellSelected( cell ); + + U32 size; + ConsoleLogEntry* log; + + Con::getLockLog(log, size); + + ConsoleLogEntry& entry = log[ cell.y ]; + onMessageSelected_callback( entry.mLevel, entry.mString ); + + Con::unlockLog(); +} diff --git a/Engine/source/gui/controls/guiConsole.h b/Engine/source/gui/controls/guiConsole.h new file mode 100644 index 000000000..8f67573e1 --- /dev/null +++ b/Engine/source/gui/controls/guiConsole.h @@ -0,0 +1,68 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUICONSOLE_H_ +#define _GUICONSOLE_H_ + +#ifndef _GUIARRAYCTRL_H_ +#include "gui/core/guiArrayCtrl.h" +#endif + +#ifndef _CONSOLE_LOGGER_H_ +#include "console/consoleLogger.h" +#endif + + +class GuiConsole : public GuiArrayCtrl +{ + private: + typedef GuiArrayCtrl Parent; + + Resource mFont; + + S32 getMaxWidth(S32 startIndex, S32 endIndex); + + protected: + + /// @name Callbacks + /// @{ + + DECLARE_CALLBACK( void, onMessageSelected, ( ConsoleLogEntry::Level level, const char* message ) ); + + /// @} + + // GuiArrayCtrl. + virtual void onCellSelected( Point2I cell ); + + public: + GuiConsole(); + DECLARE_CONOBJECT(GuiConsole); + DECLARE_CATEGORY( "Gui Editor" ); + DECLARE_DESCRIPTION( "Control that displays the console log text." ); + + // GuiArrayCtrl. + virtual bool onWake(); + virtual void onPreRender(); + virtual void onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver); +}; + +#endif diff --git a/Engine/source/gui/controls/guiConsoleEditCtrl.cpp b/Engine/source/gui/controls/guiConsoleEditCtrl.cpp new file mode 100644 index 000000000..2b281e18f --- /dev/null +++ b/Engine/source/gui/controls/guiConsoleEditCtrl.cpp @@ -0,0 +1,145 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/consoleTypes.h" +#include "console/console.h" +#include "gui/core/guiCanvas.h" +#include "gui/controls/guiConsoleEditCtrl.h" +#include "core/frameAllocator.h" + +IMPLEMENT_CONOBJECT(GuiConsoleEditCtrl); + +ConsoleDocClass( GuiConsoleEditCtrl, + "@brief Text entry element of a GuiConsole.\n\n" + "@tsexample\n" + "new GuiConsoleEditCtrl(ConsoleEntry)\n" + "{\n" + " profile = \"ConsoleTextEditProfile\";\n" + " horizSizing = \"width\";\n" + " vertSizing = \"top\";\n" + " position = \"0 462\";\n" + " extent = \"640 18\";\n" + " minExtent = \"8 8\";\n" + " visible = \"1\";\n" + " altCommand = \"ConsoleEntry::eval();\";\n" + " helpTag = \"0\";\n" + " maxLength = \"255\";\n" + " historySize = \"40\";\n" + " password = \"0\";\n" + " tabComplete = \"0\";\n" + " sinkAllKeyEvents = \"1\";\n" + " useSiblingScroller = \"1\";\n" + "};\n" + "@endtsexample\n\n" + "@ingroup GuiCore" +); + +GuiConsoleEditCtrl::GuiConsoleEditCtrl() +{ + mSinkAllKeyEvents = true; + mSiblingScroller = NULL; + mUseSiblingScroller = true; +} + +void GuiConsoleEditCtrl::initPersistFields() +{ + addGroup("GuiConsoleEditCtrl"); + addField("useSiblingScroller", TypeBool, Offset(mUseSiblingScroller, GuiConsoleEditCtrl)); + endGroup("GuiConsoleEditCtrl"); + + Parent::initPersistFields(); +} + +bool GuiConsoleEditCtrl::onKeyDown(const GuiEvent &event) +{ + setUpdate(); + + if (event.keyCode == KEY_TAB) + { + // Get a buffer that can hold the completed text... + FrameTemp tmpBuff(GuiTextCtrl::MAX_STRING_LENGTH); + // And copy the text to be completed into it. + mTextBuffer.getCopy8(tmpBuff, GuiTextCtrl::MAX_STRING_LENGTH); + + // perform the completion + bool forward = (event.modifier & SI_SHIFT) == 0; + mCursorPos = Con::tabComplete(tmpBuff, mCursorPos, GuiTextCtrl::MAX_STRING_LENGTH, forward); + + // place results in our buffer. + mTextBuffer.set(tmpBuff); + return true; + } + else if ((event.keyCode == KEY_PAGE_UP) || (event.keyCode == KEY_PAGE_DOWN)) + { + // See if there's some other widget that can scroll the console history. + if (mUseSiblingScroller) + { + if (mSiblingScroller) + { + return mSiblingScroller->onKeyDown(event); + } + else + { + // Let's see if we can find it... + SimGroup* pGroup = getGroup(); + if (pGroup) + { + // Find the first scroll control in the same group as us. + for (SimSetIterator itr(pGroup); *itr; ++itr) + { + mSiblingScroller = dynamic_cast(*itr); + if (mSiblingScroller != NULL) + { + return mSiblingScroller->onKeyDown(event); + } + } + } + + // No luck... so don't try, next time. + mUseSiblingScroller = false; + } + } + } + else if( event.keyCode == KEY_RETURN || event.keyCode == KEY_NUMPADENTER ) + { + if ( event.modifier & SI_SHIFT && + mTextBuffer.length() + dStrlen("echo();") <= GuiTextCtrl::MAX_STRING_LENGTH ) + { + // Wrap the text with echo( %s ); + + char buf[GuiTextCtrl::MAX_STRING_LENGTH]; + getText( buf ); + + String text( buf ); + text.replace( ";", "" ); + + text = String::ToString( "echo(%s);", text.c_str() ); + + setText( text ); + } + + return Parent::dealWithEnter(false); + } + + return Parent::onKeyDown(event); +} + diff --git a/Engine/source/gui/controls/guiConsoleEditCtrl.h b/Engine/source/gui/controls/guiConsoleEditCtrl.h new file mode 100644 index 000000000..af1648649 --- /dev/null +++ b/Engine/source/gui/controls/guiConsoleEditCtrl.h @@ -0,0 +1,56 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUICONSOLEEDITCTRL_H_ +#define _GUICONSOLEEDITCTRL_H_ + +#ifndef _GUITYPES_H_ +#include "gui/core/guiTypes.h" +#endif +#ifndef _GUITEXTEDITCTRL_H_ +#include "gui/controls/guiTextEditCtrl.h" +#endif +#ifndef _GUISCROLLCTRL_H_ +#include "gui/containers/guiScrollCtrl.h" +#endif + +class GuiConsoleEditCtrl : public GuiTextEditCtrl +{ +private: + typedef GuiTextEditCtrl Parent; + +protected: + bool mUseSiblingScroller; + GuiScrollCtrl* mSiblingScroller; + +public: + GuiConsoleEditCtrl(); + + DECLARE_CONOBJECT(GuiConsoleEditCtrl); + DECLARE_CATEGORY( "Gui Editor" ); + + static void initPersistFields(); + + bool onKeyDown(const GuiEvent &event); +}; + +#endif //_GUI_TEXTEDIT_CTRL_H diff --git a/Engine/source/gui/controls/guiConsoleTextCtrl.cpp b/Engine/source/gui/controls/guiConsoleTextCtrl.cpp new file mode 100644 index 000000000..617099646 --- /dev/null +++ b/Engine/source/gui/controls/guiConsoleTextCtrl.cpp @@ -0,0 +1,171 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/controls/guiConsoleTextCtrl.h" + +#include "console/consoleTypes.h" +#include "console/console.h" +#include "core/color.h" +#include "gfx/gfxDrawUtil.h" +#include "gui/core/guiDefaultControlRender.h" + +IMPLEMENT_CONOBJECT(GuiConsoleTextCtrl); + +ConsoleDocClass( GuiConsoleTextCtrl, + "@brief Used by GUIConsole system internally.\n\n" + "@internal" +); + +GuiConsoleTextCtrl::GuiConsoleTextCtrl() +{ +} + +GuiConsoleTextCtrl::~GuiConsoleTextCtrl() +{ +} + +void GuiConsoleTextCtrl::initPersistFields() +{ + addGroup("GuiConsoleTextCtrl"); + addField("expression", TypeRealString, Offset(mConsoleExpression, GuiConsoleTextCtrl)); + endGroup("GuiConsoleTextCtrl"); + Parent::initPersistFields(); +} + +bool GuiConsoleTextCtrl::onWake() +{ + if (! Parent::onWake()) + return false; + + mFont = mProfile->mFont; + return true; +} + +void GuiConsoleTextCtrl::onSleep() +{ + Parent::onSleep(); + mFont = NULL; +} + +void GuiConsoleTextCtrl::setText(const char *txt) +{ + //make sure we don't call this before onAdd(); + AssertFatal(mProfile, "Can't call setText() until setProfile() has been called."); + + if (txt) + mConsoleExpression = txt; + else + mConsoleExpression = String::EmptyString; + + // make sure we have a font + mProfile->incLoadCount(); + mFont = mProfile->mFont; + + setUpdate(); + + // decrement the profile reference + mProfile->decLoadCount(); +} + +void GuiConsoleTextCtrl::calcResize() +{ + if ( mResult.isEmpty() ) + return; + + // The width is the longest line. + U32 ctrlWidth = 0; + for ( U32 i = 0; i < mLineLen.size(); i++ ) + { + U32 width = mFont->getStrNWidth( mResult.c_str() + mStartLineOffset[i], mLineLen[i] ); + + if ( width > ctrlWidth ) + ctrlWidth = width; + } + + // The height is the number of lines times the height of the font. + U32 ctrlHeight = mLineLen.size() * mFont->getHeight(); + + setExtent( Point2I( ctrlWidth, ctrlHeight ) + mProfile->mTextOffset * 2 ); +} + + +void GuiConsoleTextCtrl::onPreRender() +{ + if ( mConsoleExpression.isNotEmpty() ) + { + mResult = Con::evaluatef( "$guiConsoleTextCtrlTemp = %s;", mConsoleExpression.c_str() ); + + // Of the resulting string we will be printing, + // Find the number of lines and length of each. + mProfile->mFont->wrapString( mResult, U32_MAX, mStartLineOffset, mLineLen ); + } + else + mResult = String::EmptyString; + + calcResize(); +} + +void GuiConsoleTextCtrl::onRender( Point2I offset, const RectI &updateRect ) +{ + RectI ctrlRect( offset, getExtent() ); + + // if opaque, fill the update rect with the fill color + if ( mProfile->mOpaque ) + GFX->getDrawUtil()->drawRectFill( ctrlRect, mProfile->mFillColor ); + + // if there's a border, draw the border + if ( mProfile->mBorder ) + renderBorder( ctrlRect, mProfile ); + + // If we have text to render. + if ( mResult.isNotEmpty() ) + { + GFont *font = mProfile->mFont; + + GFX->getDrawUtil()->setBitmapModulation( mProfile->mFontColor ); + + for ( U32 i = 0; i < mLineLen.size(); i++ ) + { + Point2I tempOffset = offset; + tempOffset += mProfile->mTextOffset; + tempOffset.y += i * font->getHeight(); + + const UTF8 *line = mResult.c_str() + mStartLineOffset[i]; + U32 lineLen = mLineLen[i]; + GFX->getDrawUtil()->drawTextN( font, tempOffset, line, lineLen, mProfile->mFontColors ); + } + } + + // render the child controlsmResult + renderChildControls(offset, updateRect); +} + +const char *GuiConsoleTextCtrl::getScriptValue() +{ + return getText(); +} + +void GuiConsoleTextCtrl::setScriptValue(const char *val) +{ + setText(val); +} diff --git a/Engine/source/gui/controls/guiConsoleTextCtrl.h b/Engine/source/gui/controls/guiConsoleTextCtrl.h new file mode 100644 index 000000000..689b62534 --- /dev/null +++ b/Engine/source/gui/controls/guiConsoleTextCtrl.h @@ -0,0 +1,82 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUICONSOLETEXTCTRL_H_ +#define _GUICONSOLETEXTCTRL_H_ + +#ifndef _GFONT_H_ +#include "gfx/gFont.h" +#endif +#ifndef _GUITYPES_H_ +#include "gui/core/guiTypes.h" +#endif +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif + +class GuiConsoleTextCtrl : public GuiControl +{ +private: + typedef GuiControl Parent; + +public: + enum Constants { MAX_STRING_LENGTH = 255 }; + + +protected: + + String mConsoleExpression; + String mResult; + Resource mFont; + + Vector mStartLineOffset; + Vector mLineLen; + +public: + + //creation methods + DECLARE_CONOBJECT(GuiConsoleTextCtrl); + DECLARE_CATEGORY( "Gui Editor" ); + + GuiConsoleTextCtrl(); + virtual ~GuiConsoleTextCtrl(); + static void initPersistFields(); + + //Parental methods + bool onWake(); + void onSleep(); + + //text methods + virtual void setText( const char *txt = NULL ); + const char* getText() { return mConsoleExpression.c_str(); } + + //rendering methods + void calcResize(); + void onPreRender(); // do special pre render processing + void onRender( Point2I offset, const RectI &updateRect ); + + //Console methods + const char* getScriptValue(); + void setScriptValue( const char *value ); +}; + +#endif //_GUI_TEXT_CONTROL_H_ diff --git a/Engine/source/gui/controls/guiDecoyCtrl.cpp b/Engine/source/gui/controls/guiDecoyCtrl.cpp new file mode 100644 index 000000000..f764c976a --- /dev/null +++ b/Engine/source/gui/controls/guiDecoyCtrl.cpp @@ -0,0 +1,238 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/controls/guiDecoyCtrl.h" +#include "gui/buttons/guiButtonBaseCtrl.h" + +#include "console/consoleTypes.h" +#include "gfx/primBuilder.h" + +//----------------------------------------------------------------------------- +// GuiDecoyCtrl +//----------------------------------------------------------------------------- + +/* +So far this control has been designed in mind solely for button controls. I'm pretty sure +it can be used for other things, but to do anything more in depth; it has to be extended. +Make sure you know a little about how guiCanvas hands out signals to gui controls before you tinker +in this class. + +Been thinking about this class a little more. I tried pretty hard to protect this class into being +guiControl like agnostic. But I ended up adding a check specifically for buttons in the +onMouseUp function. Its been protected with a dynamic_cast and a NULL check; but in the end, the only way +too solve the main problem, that GuiCanvas cannot process more than one mouse action for more than one +gui control at a time, is for it to get a rewrite. +*/ + +IMPLEMENT_CONOBJECT(GuiDecoyCtrl); + +ConsoleDocClass( GuiDecoyCtrl, + "@brief Designed soley for buttons, primarily used in editor.\n\n" + "Currently editor use only, no real application without extension.\n\n " + "@internal"); + +GuiDecoyCtrl::GuiDecoyCtrl() : mIsDecoy(true), + mMouseOver(false), + mDecoyReference(NULL) +{ +} + +GuiDecoyCtrl::~GuiDecoyCtrl() +{ +} + +void GuiDecoyCtrl::initPersistFields() +{ + addField("isDecoy", TypeBool, Offset(mIsDecoy, GuiDecoyCtrl), "Sets this control to decoy mode"); + + Parent::initPersistFields(); +} + +void GuiDecoyCtrl::onMouseUp(const GuiEvent &event) +{ + mouseUnlock(); + setUpdate(); + + //this code is pretty hacky. right now there is no way that guiCanvas will allow sending more than + //one signal to one gui control at a time. + if(mIsDecoy == true) + { + mVisible = false; + + GuiControl *parent = getParent(); + Point2I localPoint = parent->globalToLocalCoord(event.mousePoint); + GuiControl *tempControl = parent->findHitControl(localPoint); + + //the decoy control has the responsibility of keeping track of the decoyed controls status + if( mDecoyReference != NULL && tempControl == mDecoyReference) + tempControl->onMouseUp(event); + else if(mDecoyReference != NULL && tempControl != mDecoyReference) + { + //as explained earlier, this control was written in the mindset for buttons. + //nothing bad should ever happen if not a button due to the checks in this function though. + GuiButtonBaseCtrl *unCastCtrl = NULL; + unCastCtrl = dynamic_cast( mDecoyReference ); + if(unCastCtrl != NULL) + unCastCtrl->resetState(); + } + mVisible = true; + } +} + +void GuiDecoyCtrl::onMouseDown(const GuiEvent &event) +{ + if ( !mVisible || !mAwake ) + return; + + mouseLock(); + + if(mIsDecoy == true) + { + mVisible = false; + + GuiControl *parent = getParent(); + Point2I localPoint = parent->globalToLocalCoord(event.mousePoint); + + GuiControl *tempControl = parent->findHitControl(localPoint); + tempControl->onMouseDown(event); + + mVisible = true; + } + + execConsoleCallback(); + setUpdate(); +} + +void GuiDecoyCtrl::onMouseMove(const GuiEvent &event) +{ + //if this control is a dead end, make sure the event stops here + if ( !mVisible || !mAwake ) + return; + + //pass the event to the parent + GuiControl *parent = getParent(); + if ( parent ) + parent->onMouseMove( event ); + + Point2I localPoint = parent->globalToLocalCoord(event.mousePoint); + + //also pretty hacky. since guiCanvas, *NOT* GuiControl, distributes the calls for onMouseEnter + //and onMouseLeave, we simulate those calls here through a series of checks. + if(mIsDecoy == true) + { + mVisible = false; + GuiControl *parent = getParent(); + GuiControl *tempControl = parent->findHitControl(localPoint); + + //the decoy control has the responsibility of keeping track of the decoyed controls status + if(mMouseOverDecoy == false && mDecoyReference != NULL) + { + tempControl->onMouseEnter(event); + mMouseOverDecoy = true; + } + else if(tempControl != mDecoyReference && mDecoyReference != NULL) + { + mDecoyReference->onMouseLeave(event); + mMouseOverDecoy = false; + } + + mDecoyReference = tempControl; + mVisible = true; + } +} + +void GuiDecoyCtrl::onMouseDragged(const GuiEvent &event) +{ +} + +void GuiDecoyCtrl::onMouseEnter(const GuiEvent &event) +{ + if ( !mVisible || !mAwake ) + return; + + setUpdate(); + Con::executef( this , "onMouseEnter" ); + mMouseOver = true; +} + +void GuiDecoyCtrl::onMouseLeave(const GuiEvent &event) +{ + if ( !mVisible || !mAwake ) + return; + + setUpdate(); + Con::executef( this , "onMouseLeave" ); + mMouseOver = false; +} + +bool GuiDecoyCtrl::onMouseWheelUp( const GuiEvent &event ) +{ + //if this control is a dead end, make sure the event stops here + if ( !mVisible || !mAwake ) + return true; + + //pass the event to the parent + GuiControl *parent = getParent(); + if ( parent ) + return parent->onMouseWheelUp( event ); + else + return false; +} + +bool GuiDecoyCtrl::onMouseWheelDown( const GuiEvent &event ) +{ + //if this control is a dead end, make sure the event stops here + if ( !mVisible || !mAwake ) + return true; + + //pass the event to the parent + GuiControl *parent = getParent(); + if ( parent ) + return parent->onMouseWheelDown( event ); + else + return false; +} + +void GuiDecoyCtrl::onRightMouseDown(const GuiEvent &) +{ +} + +void GuiDecoyCtrl::onRightMouseUp(const GuiEvent &) +{ +} + +void GuiDecoyCtrl::onRightMouseDragged(const GuiEvent &) +{ +} + +void GuiDecoyCtrl::onMiddleMouseDown(const GuiEvent &) +{ +} + +void GuiDecoyCtrl::onMiddleMouseUp(const GuiEvent &) +{ +} + +void GuiDecoyCtrl::onMiddleMouseDragged(const GuiEvent &) +{ +} \ No newline at end of file diff --git a/Engine/source/gui/controls/guiDecoyCtrl.h b/Engine/source/gui/controls/guiDecoyCtrl.h new file mode 100644 index 000000000..d1d194b56 --- /dev/null +++ b/Engine/source/gui/controls/guiDecoyCtrl.h @@ -0,0 +1,69 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _GUIDECOYCTRL_H_ +#define _GUIDECOYCTRL_H_ + +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif + +class GuiDecoyCtrl : public GuiControl +{ +private: + typedef GuiControl Parent; + +public: + // Constructor/Destructor/ConObject Declaration + GuiDecoyCtrl(); + virtual ~GuiDecoyCtrl(); + + DECLARE_CONOBJECT(GuiDecoyCtrl); + DECLARE_CATEGORY( "Gui Other" ); + + static void initPersistFields(); + + bool mMouseOver; + bool mIsDecoy; + GuiControl* mDecoyReference; + bool mMouseOverDecoy; + Point2I mMouseDownPosition; + + + virtual void onMouseUp(const GuiEvent &event); + virtual void onMouseDown(const GuiEvent &event); + virtual void onMouseMove(const GuiEvent &event); + virtual void onMouseDragged(const GuiEvent &event); + virtual void onMouseEnter(const GuiEvent &event); + virtual void onMouseLeave(const GuiEvent &event); + + virtual bool onMouseWheelUp(const GuiEvent &event); + virtual bool onMouseWheelDown(const GuiEvent &event); + + virtual void onRightMouseDown(const GuiEvent &event); + virtual void onRightMouseUp(const GuiEvent &event); + virtual void onRightMouseDragged(const GuiEvent &event); + + virtual void onMiddleMouseDown(const GuiEvent &event); + virtual void onMiddleMouseUp(const GuiEvent &event); + virtual void onMiddleMouseDragged(const GuiEvent &event); +}; +#endif diff --git a/Engine/source/gui/controls/guiDirectoryFileListCtrl.cpp b/Engine/source/gui/controls/guiDirectoryFileListCtrl.cpp new file mode 100644 index 000000000..d5695b24d --- /dev/null +++ b/Engine/source/gui/controls/guiDirectoryFileListCtrl.cpp @@ -0,0 +1,222 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/engineAPI.h" +#include "core/strings/findMatch.h" +#include "gui/controls/guiDirectoryFileListCtrl.h" + + +IMPLEMENT_CONOBJECT( GuiDirectoryFileListCtrl ); + +ConsoleDocClass( GuiDirectoryFileListCtrl, + "@brief A control that displays a list of files from within a single directory " + "in the game file system.\n\n" + + "@tsexample\n\n" + "new GuiDirectoryFileListCtrl()\n" + "{\n" + " filePath = \"art/shapes\";\n" + " fileFilter = \"*.dts\" TAB \"*.dae\";\n" + " //Properties not specific to this control have been omitted from this example.\n" + "};\n" + "@endtsexample\n\n" + + "@ingroup GuiControls\n" +); + +GuiDirectoryFileListCtrl::GuiDirectoryFileListCtrl() +{ + mFilePath = StringTable->insert( "" ); + mFilter = StringTable->insert( "*.*" ); +} + +void GuiDirectoryFileListCtrl::initPersistFields() +{ + addProtectedField( "filePath", TypeString, Offset( mFilePath, GuiDirectoryFileListCtrl ), + &_setFilePath, &defaultProtectedGetFn, "Path in game directory from which to list files." ); + addProtectedField( "fileFilter", TypeString, Offset( mFilter, GuiDirectoryFileListCtrl ), + &_setFilter, &defaultProtectedGetFn, "Tab-delimited list of file name patterns. Only matched files will be displayed." ); + + Parent::initPersistFields(); +} + +bool GuiDirectoryFileListCtrl::onWake() +{ + if( !Parent::onWake() ) + return false; + + update(); + + return true; +} + +void GuiDirectoryFileListCtrl::onMouseDown(const GuiEvent &event) +{ + Parent::onMouseDown( event ); + + if( event.mouseClickCount == 2 ) + onDoubleClick_callback(); +} + + +void GuiDirectoryFileListCtrl::openDirectory() +{ + String path; + if( mFilePath && mFilePath[ 0 ] ) + path = String::ToString( "%s/%s", Platform::getMainDotCsDir(), mFilePath ); + else + path = Platform::getMainDotCsDir(); + + Vector fileVector; + Platform::dumpPath( path, fileVector, 0 ); + + // Clear the current file listing + clearItems(); + + // Does this dir have any files? + if( fileVector.empty() ) + return; + + // If so, iterate through and list them + Vector::iterator i = fileVector.begin(); + for( S32 j=0 ; i != fileVector.end(); i++, j++ ) + { + if( !mFilter[ 0 ] || FindMatch::isMatchMultipleExprs( mFilter, (*i).pFileName,false ) ) + addItem( (*i).pFileName ); + } +} + + +void GuiDirectoryFileListCtrl::setCurrentFilter( const char* filter ) +{ + if( !filter ) + filter = ""; + + mFilter = StringTable->insert( filter ); + + // Update our view + openDirectory(); +} + +DefineEngineMethod( GuiDirectoryFileListCtrl, setFilter, void, ( const char* filter ),, + "Set the file filter.\n\n" + "@param filter Tab-delimited list of file name patterns. Only matched files will be displayed.\n" ) +{ + object->setCurrentFilter( filter ); +} + +bool GuiDirectoryFileListCtrl::setCurrentPath( const char* path, const char* filter ) +{ + if( !path ) + return false; + + const U32 pathLen = dStrlen( path ); + if( pathLen > 0 && path[ pathLen - 1 ] == '/' ) + mFilePath = StringTable->insertn( path, pathLen - 1 ); + else + mFilePath = StringTable->insert( path ); + + if( filter ) + mFilter = StringTable->insert( filter ); + + // Update our view + openDirectory(); + + return true; +} + +DefineEngineMethod( GuiDirectoryFileListCtrl, reload, void, (),, + "Update the file list." ) +{ + object->update(); +} + +DefineEngineMethod( GuiDirectoryFileListCtrl, setPath, bool, ( const char* path, const char* filter ),, + "Set the search path and file filter.\n\n" + "@param path Path in game directory from which to list files.\n" + "@param filter Tab-delimited list of file name patterns. Only matched files will be displayed.\n" ) +{ + return object->setCurrentPath( path, filter ); +} + +DefineEngineMethod( GuiDirectoryFileListCtrl, getSelectedFiles, const char*, (),, + "Get the list of selected files.\n\n" + "@return A space separated list of selected files" ) +{ + Vector ItemVector; + object->getSelectedItems( ItemVector ); + + if( ItemVector.empty() ) + return StringTable->insert( "" ); + + // Get an adequate buffer + char itemBuffer[256]; + dMemset( itemBuffer, 0, 256 ); + + char* returnBuffer = Con::getReturnBuffer( ItemVector.size() * 64 ); + dMemset( returnBuffer, 0, ItemVector.size() * 64 ); + + // Fetch the first entry + StringTableEntry itemText = object->getItemText( ItemVector[0] ); + if( !itemText ) + return StringTable->lookup(""); + dSprintf( returnBuffer, ItemVector.size() * 64, "%s", itemText ); + + // If only one entry, return it. + if( ItemVector.size() == 1 ) + return returnBuffer; + + // Fetch the remaining entries + for( S32 i = 1; i < ItemVector.size(); i++ ) + { + StringTableEntry itemText = object->getItemText( ItemVector[i] ); + if( !itemText ) + continue; + + dMemset( itemBuffer, 0, 256 ); + dSprintf( itemBuffer, 256, " %s", itemText ); + dStrcat( returnBuffer, itemBuffer ); + } + + return returnBuffer; + +} + +StringTableEntry GuiDirectoryFileListCtrl::getSelectedFileName() +{ + S32 item = getSelectedItem(); + if( item == -1 ) + return StringTable->lookup(""); + + StringTableEntry itemText = getItemText( item ); + if( !itemText ) + return StringTable->lookup(""); + + return itemText; +} + +DefineEngineMethod( GuiDirectoryFileListCtrl, getSelectedFile, const char*, (),, + "Get the currently selected filename.\n\n" + "@return The filename of the currently selected file\n" ) +{ + return object->getSelectedFileName(); +} diff --git a/Engine/source/gui/controls/guiDirectoryFileListCtrl.h b/Engine/source/gui/controls/guiDirectoryFileListCtrl.h new file mode 100644 index 000000000..391ed591a --- /dev/null +++ b/Engine/source/gui/controls/guiDirectoryFileListCtrl.h @@ -0,0 +1,79 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUI_DIRECTORYFILELISTCTRL_H_ +#define _GUI_DIRECTORYFILELISTCTRL_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif + +#ifndef _GUI_LISTBOXCTRL_H_ +#include "gui/controls/guiListBoxCtrl.h" +#endif + +class GuiDirectoryFileListCtrl : public GuiListBoxCtrl +{ +private: + typedef GuiListBoxCtrl Parent; +protected: + StringTableEntry mFilePath; + StringTableEntry mFilter; + + void openDirectory(); + + static bool _setFilePath( void *object, const char *index, const char *data ) + { + GuiDirectoryFileListCtrl* ctrl = ( GuiDirectoryFileListCtrl* ) object; + ctrl->setCurrentPath( data, ctrl->mFilter ); + return false; + } + static bool _setFilter( void *object, const char *index, const char *data ) + { + GuiDirectoryFileListCtrl* ctrl = ( GuiDirectoryFileListCtrl* ) object; + ctrl->setCurrentFilter( data ); + return false; + } + +public: + GuiDirectoryFileListCtrl(); + + DECLARE_CONOBJECT(GuiDirectoryFileListCtrl); + DECLARE_DESCRIPTION( "A control that displays a list of files from within a single\n" + "directory in the game file system." ); + + static void initPersistFields(); + + void update() { openDirectory(); } + + /// Set the current path to grab files from + bool setCurrentPath( const char* path, const char* filter ); + void setCurrentFilter( const char* filter ); + + /// Get the currently selected file's name + StringTableEntry getSelectedFileName(); + + virtual void onMouseDown(const GuiEvent &event); + virtual bool onWake(); +}; + +#endif diff --git a/Engine/source/gui/controls/guiFileTreeCtrl.cpp b/Engine/source/gui/controls/guiFileTreeCtrl.cpp new file mode 100644 index 000000000..7fe7e3ba4 --- /dev/null +++ b/Engine/source/gui/controls/guiFileTreeCtrl.cpp @@ -0,0 +1,430 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/controls/guiFileTreeCtrl.h" +#include "core/strings/findMatch.h" +#include "core/frameAllocator.h" +#include "core/strings/stringUnit.h" +#include "console/consoleTypes.h" + + +IMPLEMENT_CONOBJECT(GuiFileTreeCtrl); + +ConsoleDocClass( GuiFileTreeCtrl, + "@brief A control that displays a hierarchical tree view of a path in the game file system.\n\n" + "@note Currently not used, most likely existed for editors. Possibly deprecated.\n\n" + "@internal" +); + + +static bool _isDirInMainDotCsPath(const char* dir) +{ + StringTableEntry cs = Platform::getMainDotCsDir(); + U32 len = dStrlen(cs) + dStrlen(dir) + 2; + FrameTemp fullpath(len); + dSprintf(fullpath, len, "%s/%s", cs, dir); + + return Platform::isDirectory(fullpath); +} + +static bool _hasChildren(const char* path) +{ + if( Platform::hasSubDirectory(path)) + return true; + + Vector dummy; + Platform::dumpDirectories( path, dummy, 0, true); + + return dummy.size() > 0; +} + +GuiFileTreeCtrl::GuiFileTreeCtrl() + : Parent() +{ + // Parent configuration + setBounds(0,0,200,100); + mDestroyOnSleep = false; + mSupportMouseDragging = false; + mMultipleSelections = false; + + mFileFilter = "*.cs *.gui *.ed.cs"; + _initFilters(); +} + +void GuiFileTreeCtrl::initPersistFields() +{ + addGroup( "File Tree" ); + addField( "rootPath", TypeRealString, Offset( mRootPath, GuiFileTreeCtrl ), "Path in game directory that should be displayed in the control." ); + addProtectedField( "fileFilter", TypeRealString, Offset( mFileFilter, GuiFileTreeCtrl ), + &_setFileFilterValue, &defaultProtectedGetFn, "Vector of file patterns. If not empty, only files matching the pattern will be shown in the control." ); + endGroup( "File Tree" ); + + Parent::initPersistFields(); +} + +static void _dumpFiles(const char *path, Vector &directoryVector, S32 depth = 0) +{ + Vector fileVec; + Platform::dumpPath( path, fileVec, depth); + + for(U32 i = 0; i < fileVec.size(); i++) + { + directoryVector.push_back( StringTable->insert(fileVec[i].pFileName) ); + } +} + +void GuiFileTreeCtrl::updateTree() +{ + // Kill off any existing items + _destroyTree(); + + // Here we're going to grab our system volumes from the platform layer and create them as roots + // + // Note : that we're passing a 1 as the last parameter to Platform::dumpDirectories, which tells it + // how deep to dump in recursion. This is an optimization to keep from dumping the whole file system + // to the tree. The tree will dump more paths as necessary when the virtual parents are expanded, + // much as windows does. + + // Determine the root path. + + String rootPath = Platform::getMainDotCsDir(); + if( !mRootPath.isEmpty() ) + rootPath = String::ToString( "%s/%s", rootPath.c_str(), mRootPath.c_str() ); + + // get the files in the main.cs dir + Vector pathVec; + Platform::dumpDirectories( rootPath, pathVec, 0, true); + _dumpFiles( rootPath, pathVec, 0); + if( ! pathVec.empty() ) + { + // get the last folder in the path. + char *dirname = dStrdup(rootPath); + U32 last = dStrlen(dirname)-1; + if(dirname[last] == '/') + dirname[last] = '\0'; + char* lastPathComponent = dStrrchr(dirname,'/'); + if(lastPathComponent) + *lastPathComponent++ = '\0'; + else + lastPathComponent = dirname; + + // Iterate through the returned paths and add them to the tree + Vector::iterator j = pathVec.begin(); + for( ; j != pathVec.end(); j++ ) + { + char fullModPathSub [512]; + dMemset( fullModPathSub, 0, 512 ); + dSprintf( fullModPathSub, 512, "%s/%s", lastPathComponent, (*j) ); + addPathToTree( *j ); + } + dFree(dirname); + } +} + +bool GuiFileTreeCtrl::onWake() +{ + if( !Parent::onWake() ) + return false; + + updateTree(); + + return true; +} + +bool GuiFileTreeCtrl::onVirtualParentExpand(Item *item) +{ + if( !item || !item->isExpanded() ) + return true; + + const char* pathToExpand = item->getValue(); + if( !pathToExpand ) + { + Con::errorf("GuiFileTreeCtrl::onVirtualParentExpand - Unable to retrieve item value!"); + return false; + } + + Vector pathVec; + _dumpFiles( pathToExpand, pathVec, 0 ); + Platform::dumpDirectories( pathToExpand, pathVec, 0, true); + if( ! pathVec.empty() ) + { + // Iterate through the returned paths and add them to the tree + Vector::iterator i = pathVec.begin(); + for( ; i != pathVec.end(); i++ ) + recurseInsert(item, (*i) ); + + item->setExpanded( true ); + } + + item->setVirtualParent( false ); + + // Update our tree view + buildVisibleTree(); + + return true; + +} + +void GuiFileTreeCtrl::addPathToTree( StringTableEntry path ) +{ + if( !path ) + { + Con::errorf("GuiFileTreeCtrl::addPathToTree - Invalid Path!"); + return; + } + + // Identify which root (volume) this path belongs to (if any) + S32 root = getFirstRootItem(); + StringTableEntry ourPath = &path[ dStrcspn( path, "/" ) + 1]; + StringTableEntry ourRoot = StringUnit::getUnit( path, 0, "/" ); + // There are no current roots, we can safely create one + if( root == 0 ) + { + recurseInsert( NULL, path ); + } + else + { + while( root != 0 ) + { + if( dStricmp( getItemValue( root ), ourRoot ) == 0 ) + { + recurseInsert( getItem( root ), ourPath ); + break; + } + root = this->getNextSiblingItem( root ); + } + // We found none so we'll create one + if ( root == 0 ) + { + recurseInsert( NULL, path ); + } + } +} + +void GuiFileTreeCtrl::onItemSelected( Item *item ) +{ + Con::executef( this, "onSelectPath", avar("%s",item->getValue()) ); + + mSelPath = item->getValue(); + if( _hasChildren( mSelPath ) ) + item->setVirtualParent( true ); +} + +bool GuiFileTreeCtrl::_setFileFilterValue( void *object, const char *index, const char *data ) +{ + GuiFileTreeCtrl* ctrl = ( GuiFileTreeCtrl* ) object; + + ctrl->mFileFilter = data; + ctrl->_initFilters(); + + return false; +} + +void GuiFileTreeCtrl::_initFilters() +{ + mFilters.clear(); + + U32 index = 0; + while( true ) + { + const char* pattern = StringUnit::getUnit( mFileFilter, index, " " ); + if( !pattern[ 0 ] ) + break; + + mFilters.push_back( pattern ); + ++ index; + } +} + +bool GuiFileTreeCtrl::matchesFilters(const char* filename) +{ + if( !mFilters.size() ) + return true; + + for(int i = 0; i < mFilters.size(); i++) + { + if(FindMatch::isMatch( mFilters[i], filename)) + return true; + } + return false; +} + +void GuiFileTreeCtrl::recurseInsert( Item* parent, StringTableEntry path ) +{ + if( !path ) + return; + + char szPathCopy [ 1024 ]; + dMemset( szPathCopy, 0, 1024 ); + dStrcpy( szPathCopy, path ); + + // Jump over the first character if it's a root / + char *curPos = szPathCopy; + if( *curPos == '/' ) + curPos++; + + char szValue[1024]; + dMemset( szValue, 0, 1024 ); + if( parent ) + { + dMemset( szValue, 0, sizeof( szValue ) ); + dSprintf( szValue, sizeof( szValue ), "%s/%s", parent->getValue(), curPos ); + } + else + { + dStrncpy( szValue, curPos, sizeof( szValue ) ); + szValue[ sizeof( szValue ) - 1 ] = 0; + } + + const U32 valueLen = dStrlen( szValue ); + char* value = new char[ valueLen + 1 ]; + dMemcpy( value, szValue, valueLen + 1 ); + + char *delim = dStrchr( curPos, '/' ); + if ( delim ) + { + // terminate our / and then move our pointer to the next character (rest of the path) + *delim = 0x00; + delim++; + } + S32 itemIndex = 0; + // only insert blindly if we have no root + if( !parent ) + itemIndex = insertItem( 0, curPos, curPos ); + else + { + bool allowed = (_isDirInMainDotCsPath(value) || matchesFilters(value)); + Item *exists = parent->findChildByValue( szValue ); + if( allowed && !exists && dStrcmp( curPos, "" ) != 0 ) + { + // Since we're adding a child this parent can't be a virtual parent, so clear that flag + parent->setVirtualParent( false ); + + itemIndex = insertItem( parent->getID(), curPos); + Item *newitem = getItem(itemIndex); + newitem->setValue( value ); + } + else + { + itemIndex = ( parent != NULL ) ? ( ( exists != NULL ) ? exists->getID() : -1 ) : -1; + } + } + + Item *newitem = getItem(itemIndex); + if(newitem) + { + newitem->setValue( value ); + if( _isDirInMainDotCsPath( value ) ) + { + newitem->setNormalImage( Icon_FolderClosed ); + newitem->setExpandedImage( Icon_Folder ); + newitem->setVirtualParent(true); + newitem->setExpanded(false); + } + else + { + newitem->setNormalImage( Icon_Doc ); + } + } + // since we're only dealing with volumes and directories, all end nodes will be virtual parents + // so if we are at the bottom of the rabbit hole, set the item to be a virtual parent + Item* item = getItem( itemIndex ); + if(item) + { + item->setExpanded(false); + if(parent && _isDirInMainDotCsPath(item->getValue()) && Platform::hasSubDirectory(item->getValue())) + item->setVirtualParent(true); + } + if( delim ) + { + if( ( dStrcmp( delim, "" ) == 0 ) && item ) + { + item->setExpanded( false ); + if( parent && _hasChildren( item->getValue() ) ) + item->setVirtualParent( true ); + } + } + else + { + if( item ) + { + item->setExpanded( false ); + if( parent && _hasChildren( item->getValue() ) ) + item->setVirtualParent( true ); + } + } + + // Down the rabbit hole we go + recurseInsert( getItem( itemIndex ), delim ); + +} + +ConsoleMethod( GuiFileTreeCtrl, getSelectedPath, const char*, 2, 2, "getSelectedPath() - returns the currently selected path in the tree") +{ + const String& path = object->getSelectedPath(); + return Con::getStringArg( path ); +} + +ConsoleMethod( GuiFileTreeCtrl, setSelectedPath, bool, 3, 3, "setSelectedPath(path) - expands the tree to the specified path") +{ + return object->setSelectedPath( argv[ 2 ] ); +} + +ConsoleMethod( GuiFileTreeCtrl, reload, void, 2, 2, "() - Reread the directory tree hierarchy." ) +{ + object->updateTree(); +} + +bool GuiFileTreeCtrl::setSelectedPath( const char* path ) +{ + if( !path ) + return false; + + // Since we only list one deep on paths, we need to add the path to the tree just incase it isn't already indexed in the tree + // or else we wouldn't be able to select a path we hadn't previously browsed to. :) + if( _isDirInMainDotCsPath( path ) ) + addPathToTree( path ); + + // see if we have a child that matches what we want + for(U32 i = 0; i < mItems.size(); i++) + { + if( dStricmp( mItems[i]->getValue(), path ) == 0 ) + { + Item* item = mItems[i]; + AssertFatal(item,"GuiFileTreeCtrl::setSelectedPath - Item Index Bad, Fatal Mistake!!!"); + item->setExpanded( true ); + clearSelection(); + setItemSelected( item->getID(), true ); + // make sure all of it's parents are expanded + S32 parent = getParentItem( item->getID() ); + while( parent != 0 ) + { + setItemExpanded( parent, true ); + parent = getParentItem( parent ); + } + // Rebuild our tree just incase we've oops'd + buildVisibleTree(); + scrollVisible( item ); + } + } + return false; +} diff --git a/Engine/source/gui/controls/guiFileTreeCtrl.h b/Engine/source/gui/controls/guiFileTreeCtrl.h new file mode 100644 index 000000000..e0e8c054e --- /dev/null +++ b/Engine/source/gui/controls/guiFileTreeCtrl.h @@ -0,0 +1,77 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUI_FILETREECTRL_H_ +#define _GUI_FILETREECTRL_H_ + +#include "platform/platform.h" +#include "gui/controls/guiTreeViewCtrl.h" + +class GuiFileTreeCtrl : public GuiTreeViewCtrl +{ +private: + + // Utility functions + void recurseInsert( Item* parent, StringTableEntry path ); + void addPathToTree( StringTableEntry path ); + +protected: + String mSelPath; + String mFileFilter; + String mRootPath; + Vector< String > mFilters; + + void _initFilters(); + + static bool _setFileFilterValue( void *object, const char *index, const char *data ); + +public: + + typedef GuiTreeViewCtrl Parent; + + enum + { + Icon_Folder = 1, + Icon_FolderClosed = 2, + Icon_Doc = 3 + }; + + GuiFileTreeCtrl(); + + bool onWake(); + bool onVirtualParentExpand(Item *item); + void onItemSelected( Item *item ); + const String& getSelectedPath() { return mSelPath; } + bool setSelectedPath( const char* path ); + + bool matchesFilters(const char* filename); + void updateTree(); + + DECLARE_CONOBJECT( GuiFileTreeCtrl ); + DECLARE_DESCRIPTION( "A control that displays a hierarchical tree view of a path in the game file system.\n" + "Note that to enable expanding/collapsing of directories, the control must be\n" + "placed inside a GuiScrollCtrl." ); + + static void initPersistFields(); +}; + +#endif //_GUI_FILETREECTRL_H_ diff --git a/Engine/source/gui/controls/guiGameListMenuCtrl.cpp b/Engine/source/gui/controls/guiGameListMenuCtrl.cpp new file mode 100644 index 000000000..d5cc8813d --- /dev/null +++ b/Engine/source/gui/controls/guiGameListMenuCtrl.cpp @@ -0,0 +1,877 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "guiGameListMenuCtrl.h" + +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "gfx/gfxDrawUtil.h" + +//----------------------------------------------------------------------------- +// GuiGameListMenuCtrl +//----------------------------------------------------------------------------- + +GuiGameListMenuCtrl::GuiGameListMenuCtrl() + : mSelected(NO_ROW), + mHighlighted(NO_ROW), + mDebugRender(false) +{ + VECTOR_SET_ASSOCIATION(mRows); + + // initialize the control callbacks + mCallbackOnA = StringTable->insert(""); + mCallbackOnB = mCallbackOnA; + mCallbackOnX = mCallbackOnA; + mCallbackOnY = mCallbackOnA; +} + +GuiGameListMenuCtrl::~GuiGameListMenuCtrl() +{ + for (S32 i = 0; i < mRows.size(); ++i) + { + delete mRows[i]; + } +} + +void GuiGameListMenuCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + GuiGameListMenuProfile * profile = (GuiGameListMenuProfile *) mProfile; + + F32 xScale = (float) getWidth() / profile->getRowWidth(); + + bool profileHasIcons = profile->hasArrows(); + + S32 rowHeight = profile->getRowHeight(); + + Point2I currentOffset = offset; + Point2I extent = getExtent(); + Point2I rowExtent(extent.x, rowHeight); + Point2I textOffset(profile->mTextOffset.x * xScale, profile->mTextOffset.y); + Point2I textExtent(extent.x - textOffset.x, rowHeight); + Point2I iconExtent, iconOffset(0.0f, 0.0f); + if (profileHasIcons) + { + iconExtent = profile->getIconExtent(); + + // icon is centered vertically plus any specified offset + S32 iconOffsetY = (rowHeight - iconExtent.y) >> 1; + iconOffsetY += profile->mIconOffset.y; + iconOffset = Point2I(profile->mIconOffset.x * xScale, iconOffsetY); + } + for (Vector::iterator row = mRows.begin(); row < mRows.end(); ++row) + { + if (row != mRows.begin()) + { + // rows other than the first can have padding above them + currentOffset.y += (*row)->mHeightPad; + currentOffset.y += rowHeight; + } + + // select appropriate colors and textures + ColorI fontColor; + U32 buttonTextureIndex; + S32 iconIndex = (*row)->mIconIndex; + bool useHighlightIcon = (*row)->mUseHighlightIcon; + if (! (*row)->mEnabled) + { + buttonTextureIndex = Profile::TEX_DISABLED; + fontColor = profile->mFontColorNA; + } + else if (row == &mRows[mSelected]) + { + if (iconIndex != NO_ICON) + { + iconIndex++; + } + buttonTextureIndex = Profile::TEX_SELECTED; + fontColor = profile->mFontColorSEL; + } + else if ((mHighlighted != NO_ROW) && (row == &mRows[mHighlighted])) + { + if (iconIndex != NO_ICON && useHighlightIcon) + { + iconIndex++; + } + buttonTextureIndex = Profile::TEX_HIGHLIGHT; + fontColor = profile->mFontColorHL; + } + else + { + buttonTextureIndex = Profile::TEX_NORMAL; + fontColor = profile->mFontColor; + } + + // render the row bitmap + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapStretchSR(profile->mTextureObject, RectI(currentOffset, rowExtent), profile->getBitmapArrayRect(buttonTextureIndex)); + + // render the row icon if it has one + if ((iconIndex != NO_ICON) && profileHasIcons && (! profile->getBitmapArrayRect((U32)iconIndex).extent.isZero())) + { + iconIndex += Profile::TEX_FIRST_ICON; + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapStretchSR(profile->mTextureObject, RectI(currentOffset + iconOffset, iconExtent), profile->getBitmapArrayRect(iconIndex)); + } + + // render the row text + GFX->getDrawUtil()->setBitmapModulation(fontColor); + renderJustifiedText(currentOffset + textOffset, textExtent, (*row)->mLabel); + } + + if (mDebugRender) + { + onDebugRender(offset); + } + + renderChildControls(offset, updateRect); +} + +void GuiGameListMenuCtrl::onDebugRender(Point2I offset) +{ + GuiGameListMenuProfile * profile = (GuiGameListMenuProfile *) mProfile; + + F32 xScale = (float) getWidth() / profile->getRowWidth(); + + ColorI controlBorderColor(200, 200, 200); // gray + ColorI rowBorderColor(255, 127, 255); // magenta + ColorI hitBorderColor(255, 0, 0); // red + Point2I shrinker(-1, -1); + Point2I extent = getExtent(); + + // render a border around the entire control + RectI borderRect(offset, extent + shrinker); + GFX->getDrawUtil()->drawRect(borderRect, controlBorderColor); + + S32 rowHeight = profile->getRowHeight(); + Point2I currentOffset(offset); + Point2I rowExtent(extent.x, rowHeight); + rowExtent += shrinker; + Point2I hitAreaExtent(profile->getHitAreaExtent()); + hitAreaExtent.x *= xScale; + hitAreaExtent += shrinker; + Point2I hitAreaOffset = profile->mHitAreaUpperLeft; + hitAreaOffset.x *= xScale; + Point2I upperLeft; + for (Vector::iterator row = mRows.begin(); row < mRows.end(); ++row) + { + // set the top of the current row + if (row != mRows.begin()) + { + // rows other than the first can have padding above them + currentOffset.y += (*row)->mHeightPad; + currentOffset.y += rowHeight; + } + + // draw the box around the whole row's extent + upperLeft = currentOffset; + borderRect.point = upperLeft; + borderRect.extent = rowExtent; + GFX->getDrawUtil()->drawRect(borderRect, rowBorderColor); + + // draw the box around the hit area of the row + upperLeft = currentOffset + hitAreaOffset; + borderRect.point = upperLeft; + borderRect.extent = hitAreaExtent; + GFX->getDrawUtil()->drawRect(borderRect, hitBorderColor); + } +} + +void GuiGameListMenuCtrl::addRow(const char* label, const char* callback, S32 icon, S32 yPad, bool useHighlightIcon, bool enabled) +{ + Row * row = new Row(); + addRow(row, label, callback, icon, yPad, useHighlightIcon, enabled); +} + +void GuiGameListMenuCtrl::addRow(Row * row, const char* label, const char* callback, S32 icon, S32 yPad, bool useHighlightIcon, bool enabled) +{ + row->mLabel = StringTable->insert(label, true); + row->mScriptCallback = (dStrlen(callback) > 0) ? StringTable->insert(callback, true) : NULL; + row->mIconIndex = (icon < 0) ? NO_ICON : icon; + row->mHeightPad = yPad; + row->mUseHighlightIcon = useHighlightIcon; + row->mEnabled = enabled; + + mRows.push_back(row); + + updateHeight(); + + if (mSelected == NO_ROW) + { + selectFirstEnabledRow(); + } +} + +Point2I GuiGameListMenuCtrl::getMinExtent() const +{ + Point2I parentMin = Parent::getMinExtent(); + + GuiGameListMenuProfile * profile = (GuiGameListMenuProfile *) mProfile; + + S32 minHeight = 0; + S32 rowHeight = profile->getRowHeight(); + + for (Vector::const_iterator row = mRows.begin(); row < mRows.end(); ++row) + { + minHeight += rowHeight; + if (row != mRows.begin()) + { + minHeight += (*row)->mHeightPad; + } + } + + if (minHeight > parentMin.y) + parentMin.y = minHeight; + + return parentMin; +} + +bool GuiGameListMenuCtrl::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + // If we have a non-GuiGameListMenuProfile profile, try to + // substitute it for DefaultListMenuProfile. + + if( !hasValidProfile() ) + { + GuiGameListMenuProfile* profile; + if( !Sim::findObject( "DefaultListMenuProfile", profile ) ) + { + Con::errorf( "GuiGameListMenuCtrl: %s can't be created with a profile of type %s. Please create it with a profile of type GuiGameListMenuProfile.", + getName(), mProfile->getClassName() ); + return false; + } + else + Con::warnf( "GuiGameListMenuCtrl: substituted non-GuiGameListMenuProfile in %s for DefaultListMenuProfile", getName() ); + + setControlProfile( profile ); + } + + return true; +} + +bool GuiGameListMenuCtrl::onWake() +{ + if( !Parent::onWake() ) + return false; + + if( !hasValidProfile() ) + return false; + + if( mRows.empty() ) + { + Con::errorf( "GuiGameListMenuCtrl: %s can't be woken up without any rows. Please use \"addRow\" to add at least one row to the control before pushing it to the canvas.", + getName() ); + return false; + } + + enforceConstraints(); + + selectFirstEnabledRow(); + + setFirstResponder(); + + mHighlighted = NO_ROW; + + return true; +} + +bool GuiGameListMenuCtrl::hasValidProfile() const +{ + GuiGameListMenuProfile * profile = dynamic_cast(mProfile); + return profile; +} + +void GuiGameListMenuCtrl::enforceConstraints() +{ + if( hasValidProfile() ) + { + ((GuiGameListMenuProfile *)mProfile)->enforceConstraints(); + } + updateHeight(); +} + +void GuiGameListMenuCtrl::updateHeight() +{ + S32 minHeight = getMinExtent().y; + if (getHeight() < minHeight) + { + setHeight(minHeight); + } +} + +void GuiGameListMenuCtrl::onMouseDown(const GuiEvent &event) +{ + S32 hitRow = getRow(event.mousePoint); + if (hitRow != NO_ROW) + { + S32 delta = (mSelected != NO_ROW) ? (hitRow - mSelected) : (mSelected + 1); + changeRow(delta); + } +} + +void GuiGameListMenuCtrl::onMouseLeave(const GuiEvent &event) +{ + mHighlighted = NO_ROW; +} + +void GuiGameListMenuCtrl::onMouseMove(const GuiEvent &event) +{ + S32 hitRow = getRow(event.mousePoint); + // allow mHighligetd to be set to NO_ROW so rows can be unhighlighted + mHighlighted = hitRow; +} + +void GuiGameListMenuCtrl::onMouseUp(const GuiEvent &event) +{ + S32 hitRow = getRow(event.mousePoint); + if ((hitRow != NO_ROW) && isRowEnabled(hitRow) && (hitRow == getSelected())) + { + activateRow(); + } +} + +void GuiGameListMenuCtrl::activateRow() +{ + S32 row = getSelected(); + if ((row != NO_ROW) && isRowEnabled(row) && (mRows[row]->mScriptCallback != NULL)) + { + setThisControl(); + if (Con::isFunction(mRows[row]->mScriptCallback)) + { + Con::executef(mRows[row]->mScriptCallback); + } + } +} + +S32 GuiGameListMenuCtrl::getRow(Point2I globalPoint) +{ + Point2I localPoint = globalToLocalCoord(globalPoint); + GuiGameListMenuProfile * profile = (GuiGameListMenuProfile *) mProfile; + + F32 xScale = (float) getWidth() / profile->getRowWidth(); + + S32 rowHeight = profile->getRowHeight(); + Point2I currentOffset(0, 0); + Point2I hitAreaUpperLeft = profile->mHitAreaUpperLeft; + hitAreaUpperLeft.x *= xScale; + Point2I hitAreaLowerRight = profile->mHitAreaLowerRight; + hitAreaLowerRight.x *= xScale; + + Point2I upperLeft, lowerRight; + for (Vector::iterator row = mRows.begin(); row < mRows.end(); ++row) + { + if (row != mRows.begin()) + { + // rows other than the first can have padding above them + currentOffset.y += (*row)->mHeightPad; + } + + upperLeft = currentOffset + hitAreaUpperLeft; + lowerRight = currentOffset + hitAreaLowerRight; + + if ((upperLeft.x <= localPoint.x) && (localPoint.x < lowerRight.x) && + (upperLeft.y <= localPoint.y) && (localPoint.y < lowerRight.y)) + { + return row - mRows.begin(); + } + + currentOffset.y += rowHeight; + } + + return NO_ROW; +} + +void GuiGameListMenuCtrl::setSelected(S32 index) +{ + if (index == NO_ROW) + { + // deselection + mSelected = NO_ROW; + return; + } + + if (! isValidRowIndex(index)) + { + return; + } + + if (! isRowEnabled(index)) + { + // row is disabled, it can't be selected + return; + } + + mSelected = mClamp(index, 0, mRows.size() - 1); +} + +bool GuiGameListMenuCtrl::isRowEnabled(S32 index) const +{ + if (! isValidRowIndex(index)) + { + return false; + } + + return mRows[index]->mEnabled; +} + +void GuiGameListMenuCtrl::setRowEnabled(S32 index, bool enabled) +{ + if (! isValidRowIndex(index)) + { + return; + } + + mRows[index]->mEnabled = enabled; + + if (getSelected() == index) + { + selectFirstEnabledRow(); + } +} + +bool GuiGameListMenuCtrl::isValidRowIndex(S32 index) const +{ + return ((0 <= index) && (index < mRows.size())); +} + +void GuiGameListMenuCtrl::selectFirstEnabledRow() +{ + setSelected(NO_ROW); + for (Vector::iterator row = mRows.begin(); row < mRows.end(); ++row) + { + if ((*row)->mEnabled) + { + setSelected(row - mRows.begin()); + return; + } + } +} + +bool GuiGameListMenuCtrl::onKeyDown(const GuiEvent &event) +{ + switch (event.keyCode) + { + case KEY_UP: + changeRow(-1); + return true; + + case KEY_DOWN: + changeRow(1); + return true; + + case KEY_A: + case KEY_RETURN: + case KEY_NUMPADENTER: + case KEY_SPACE: + case XI_A: + case XI_START: + doScriptCommand(mCallbackOnA); + return true; + + case KEY_B: + case KEY_ESCAPE: + case KEY_BACKSPACE: + case KEY_DELETE: + case XI_B: + case XI_BACK: + doScriptCommand(mCallbackOnB); + return true; + + case KEY_X: + case XI_X: + doScriptCommand(mCallbackOnX); + return true; + + case KEY_Y: + case XI_Y: + doScriptCommand(mCallbackOnY); + return true; + default: + break; + } + + return Parent::onKeyDown(event); +} + +bool GuiGameListMenuCtrl::onGamepadAxisUp(const GuiEvent &event) +{ + changeRow(-1); + return true; +} + +bool GuiGameListMenuCtrl::onGamepadAxisDown(const GuiEvent &event) +{ + changeRow(1); + return true; +} + +void GuiGameListMenuCtrl::doScriptCommand(StringTableEntry command) +{ + if (command && command[0]) + { + setThisControl(); + Con::evaluate(command, false, __FILE__); + } +} + +void GuiGameListMenuCtrl::changeRow(S32 delta) +{ + S32 oldRowIndex = getSelected(); + S32 newRowIndex = oldRowIndex; + do + { + newRowIndex += delta; + if (newRowIndex >= mRows.size()) + { + newRowIndex = 0; + } + else if (newRowIndex < 0) + { + newRowIndex = mRows.size() - 1; + } + } + while ((! mRows[newRowIndex]->mEnabled) && (newRowIndex != oldRowIndex)); + + setSelected(newRowIndex); + + // do the callback + onChange_callback(); +} + +void GuiGameListMenuCtrl::setThisControl() +{ + smThisControl = this; +} + +StringTableEntry GuiGameListMenuCtrl::getRowLabel(S32 rowIndex) const +{ + AssertFatal(isValidRowIndex(rowIndex), avar("GuiGameListMenuCtrl: You can't get the label from row %d of %s because it is not a valid row index. Please specify a valid row index in the range [0, %d).", rowIndex, getName(), getRowCount())); + if (! isValidRowIndex(rowIndex)) + { + // not a valid row index, don't do anything + return StringTable->insert(""); + } + return mRows[rowIndex]->mLabel; +} + +void GuiGameListMenuCtrl::setRowLabel(S32 rowIndex, const char * label) +{ + AssertFatal(isValidRowIndex(rowIndex), avar("GuiGameListMenuCtrl: You can't set the label on row %d of %s because it is not a valid row index. Please specify a valid row index in the range [0, %d).", rowIndex, getName(), getRowCount())); + if (! isValidRowIndex(rowIndex)) + { + // not a valid row index, don't do anything + return; + } + + mRows[rowIndex]->mLabel = StringTable->insert(label, true); +} + +//----------------------------------------------------------------------------- +// Console stuff (GuiGameListMenuCtrl) +//----------------------------------------------------------------------------- + +IMPLEMENT_CONOBJECT(GuiGameListMenuCtrl); + +ConsoleDocClass( GuiGameListMenuCtrl, + "@brief A base class for cross platform menu controls that are gamepad friendly.\n\n" + + "This class is used to build row-based menu GUIs that can be easily navigated " + "using the keyboard, mouse or gamepad. The desired row can be selected using " + "the mouse, or by navigating using the Up and Down buttons.\n\n" + + "@tsexample\n\n" + "new GuiGameListMenuCtrl()\n" + "{\n" + " debugRender = \"0\";\n" + " callbackOnA = \"applyOptions();\";\n" + " callbackOnB = \"Canvas.setContent(MainMenuGui);\";\n" + " callbackOnX = \"\";\n" + " callbackOnY = \"revertOptions();\";\n" + " //Properties not specific to this control have been omitted from this example.\n" + "};\n" + "@endtsexample\n\n" + + "@see GuiGameListMenuProfile\n\n" + + "@ingroup GuiGame" +); + +IMPLEMENT_CALLBACK( GuiGameListMenuCtrl, onChange, void, (), (), + "Called when the selected row changes." ); + +void GuiGameListMenuCtrl::initPersistFields() +{ + addField("debugRender", TypeBool, Offset(mDebugRender, GuiGameListMenuCtrl), + "Enable debug rendering" ); + + addField("callbackOnA", TypeString, Offset(mCallbackOnA, GuiGameListMenuCtrl), + "Script callback when the 'A' button is pressed. 'A' inputs are Keyboard: A, Return, Space; Gamepad: A, Start" ); + + addField("callbackOnB", TypeString, Offset(mCallbackOnB, GuiGameListMenuCtrl), + "Script callback when the 'B' button is pressed. 'B' inputs are Keyboard: B, Esc, Backspace, Delete; Gamepad: B, Back" ); + + addField("callbackOnX", TypeString, Offset(mCallbackOnX, GuiGameListMenuCtrl), + "Script callback when the 'X' button is pressed. 'X' inputs are Keyboard: X; Gamepad: X" ); + + addField("callbackOnY", TypeString, Offset(mCallbackOnY, GuiGameListMenuCtrl), + "Script callback when the 'Y' button is pressed. 'Y' inputs are Keyboard: Y; Gamepad: Y" ); + + Parent::initPersistFields(); +} + +DefineEngineMethod( GuiGameListMenuCtrl, addRow, void, + ( const char* label, const char* callback, S32 icon, S32 yPad, bool useHighlightIcon, bool enabled ), + ( -1, 0, true, true ), + "Add a row to the list control.\n\n" + "@param label The text to display on the row as a label.\n" + "@param callback Name of a script function to use as a callback when this row is activated.\n" + "@param icon [optional] Index of the icon to use as a marker.\n" + "@param yPad [optional] An extra amount of height padding before the row. Does nothing on the first row.\n" + "@param useHighlightIcon [optional] Does this row use the highlight icon?.\n" + "@param enabled [optional] If this row is initially enabled." ) +{ + object->addRow( label, callback, icon, yPad, useHighlightIcon, enabled ); +} + +DefineEngineMethod( GuiGameListMenuCtrl, isRowEnabled, bool, ( S32 row ),, + "Determines if the specified row is enabled or disabled.\n\n" + "@param row The row to set the enabled status of.\n" + "@return True if the specified row is enabled. False if the row is not enabled or the given index was not valid." ) +{ + return object->isRowEnabled( row ); +} + +DefineEngineMethod( GuiGameListMenuCtrl, setRowEnabled, void, ( S32 row, bool enabled ),, + "Sets a row's enabled status according to the given parameters.\n\n" + "@param row The index to check for validity.\n" + "@param enabled Indicate true to enable the row or false to disable it." ) +{ + object->setRowEnabled( row, enabled ); +} + +DefineEngineMethod( GuiGameListMenuCtrl, activateRow, void, (),, + "Activates the current row. The script callback of the current row will be called (if it has one)." ) +{ + object->activateRow(); +} + +DefineEngineMethod( GuiGameListMenuCtrl, getRowCount, S32, (),, + "Gets the number of rows on the control.\n\n" + "@return (int) The number of rows on the control." ) +{ + return object->getRowCount(); +} + +DefineEngineMethod( GuiGameListMenuCtrl, getRowLabel, const char *, ( S32 row ),, + "Gets the label displayed on the specified row.\n\n" + "@param row Index of the row to get the label of.\n" + "@return The label for the row." ) +{ + return object->getRowLabel( row ); +} + +DefineEngineMethod( GuiGameListMenuCtrl, setRowLabel, void, ( S32 row, const char* label ),, + "Sets the label on the given row.\n\n" + "@param row Index of the row to set the label on.\n" + "@param label Text to set as the label of the row.\n" ) +{ + object->setRowLabel( row, label ); +} + +DefineEngineMethod( GuiGameListMenuCtrl, setSelected, void, ( S32 row ),, + "Sets the selected row. Only rows that are enabled can be selected.\n\n" + "@param row Index of the row to set as selected." ) +{ + object->setSelected( row ); +} + +DefineEngineMethod( GuiGameListMenuCtrl, getSelectedRow, S32, (),, + "Gets the index of the currently selected row.\n\n" + "@return Index of the selected row." ) +{ + return object->getSelected(); +} + +//----------------------------------------------------------------------------- +// GuiGameListMenuProfile +//----------------------------------------------------------------------------- + +GuiGameListMenuProfile::GuiGameListMenuProfile() +: mHitAreaUpperLeft(0, 0), + mHitAreaLowerRight(0, 0), + mIconOffset(0, 0), + mRowSize(0, 0), + mRowScale(1.0f, 1.0f) +{ +} + +bool GuiGameListMenuProfile::onAdd() +{ + if (! Parent::onAdd()) + { + return false; + } + + // We can't call enforceConstraints() here because incRefCount initializes + // some of the things to enforce. Do a basic sanity check here instead. + + if( !dStrlen(mBitmapName) ) + { + Con::errorf( "GuiGameListMenuProfile: %s can't be created without a bitmap. Please add a 'Bitmap' property to the object definition.", getName() ); + return false; + } + + if( mRowSize.x < 0 ) + { + Con::errorf( "GuiGameListMenuProfile: %s can't have a negative row width. Please change the row width to be non-negative.", getName() ); + return false; + } + + if( mRowSize.y < 0 ) + { + Con::errorf( "GuiGameListMenuProfile: %s can't have a negative row height. Please change the row height to be non-negative.", getName() ); + return false; + } + + return true; +} + +void GuiGameListMenuProfile::enforceConstraints() +{ + if( getBitmapArrayRect(0).extent.isZero() ) + Con::errorf( "GuiGameListMenuCtrl: %s can't be created without a bitmap. Please add a bitmap to the profile's definition.", getName() ); + + if( mRowSize.x < 0 ) + Con::errorf( "GuiGameListMenuProfile: %s can't have a negative row width. Please change the row width to be non-negative.", getName() ); + mRowSize.x = getMax(mRowSize.x, 0); + + if( mRowSize.y < 0 ) + Con::errorf( "GuiGameListMenuProfile: %s can't have a negative row height. Please change the row height to be non-negative.", getName() ); + mRowSize.y = getMax(mRowSize.y, 0); + + Point2I rowTexExtent = getBitmapArrayRect(TEX_NORMAL).extent; + mRowScale.x = (float) getRowWidth() / rowTexExtent.x; + mRowScale.y = (float) getRowHeight() / rowTexExtent.y; +} + +Point2I GuiGameListMenuProfile::getIconExtent() +{ + Point2I iconExtent = getBitmapArrayRect(TEX_FIRST_ICON).extent; + + // scale both by y to keep the aspect ratio + iconExtent.x *= mRowScale.y; + iconExtent.y *= mRowScale.y; + + return iconExtent; +} + +Point2I GuiGameListMenuProfile::getArrowExtent() +{ + Point2I arrowExtent = getBitmapArrayRect(TEX_FIRST_ARROW).extent; + + // scale both by y to keep the aspect ratio + arrowExtent.x *= mRowScale.y; + arrowExtent.y *= mRowScale.y; + + return arrowExtent; +} + +Point2I GuiGameListMenuProfile::getHitAreaExtent() +{ + if (mHitAreaLowerRight == mHitAreaUpperLeft) + { + return mRowSize; + } + else + { + return mHitAreaLowerRight - mHitAreaUpperLeft; + } +} + +//----------------------------------------------------------------------------- +// Console stuff (GuiGameListMenuProfile) +//----------------------------------------------------------------------------- + +IMPLEMENT_CONOBJECT(GuiGameListMenuProfile); + +ConsoleDocClass( GuiGameListMenuProfile, + "@brief A GuiControlProfile with additional fields specific to GuiGameListMenuCtrl.\n\n" + + "@tsexample\n" + "new GuiGameListMenuProfile()\n" + "{\n" + " hitAreaUpperLeft = \"10 2\";\n" + " hitAreaLowerRight = \"190 18\";\n" + " iconOffset = \"10 2\";\n" + " rowSize = \"200 20\";\n" + " //Properties not specific to this control have been omitted from this example.\n" + "};\n" + "@endtsexample\n\n" + + "@ingroup GuiGame" +); + +void GuiGameListMenuProfile::initPersistFields() +{ + addField( "hitAreaUpperLeft", TypePoint2I, Offset(mHitAreaUpperLeft, GuiGameListMenuProfile), + "Position of the upper left corner of the row hit area (relative to row's top left corner)" ); + + addField( "hitAreaLowerRight", TypePoint2I, Offset(mHitAreaLowerRight, GuiGameListMenuProfile), + "Position of the lower right corner of the row hit area (relative to row's top left corner)" ); + + addField( "iconOffset", TypePoint2I, Offset(mIconOffset, GuiGameListMenuProfile), + "Offset from the row's top left corner at which to render the row icon" ); + + addField( "rowSize", TypePoint2I, Offset(mRowSize, GuiGameListMenuProfile), + "The base size (\"width height\") of a row" ); + + Parent::initPersistFields(); + + removeField("tab"); + removeField("mouseOverSelected"); + + removeField("modal"); + removeField("opaque"); + removeField("fillColor"); + removeField("fillColorHL"); + removeField("fillColorNA"); + removeField("border"); + removeField("borderThickness"); + removeField("borderColor"); + removeField("borderColorHL"); + removeField("borderColorNA"); + + removeField("bevelColorHL"); + removeField("bevelColorLL"); + + removeField("fontColorLink"); + removeField("fontColorLinkHL"); + + removeField("justify"); + removeField("returnTab"); + removeField("numbersOnly"); + removeField("cursorColor"); + + removeField("profileForChildren"); +} diff --git a/Engine/source/gui/controls/guiGameListMenuCtrl.h b/Engine/source/gui/controls/guiGameListMenuCtrl.h new file mode 100644 index 000000000..b496010b9 --- /dev/null +++ b/Engine/source/gui/controls/guiGameListMenuCtrl.h @@ -0,0 +1,354 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GuiGameListMenuCtrl_H_ +#define _GuiGameListMenuCtrl_H_ + +#include "gui/core/guiControl.h" + +class GuiGameListMenuProfile; + +/// \class GuiGameListMenuCtrl +/// A base class for cross platform menu controls that are gamepad friendly. +class GuiGameListMenuCtrl : public GuiControl +{ +public: + typedef GuiControl Parent; + typedef GuiGameListMenuProfile Profile; + +protected: + /// \struct Row + /// Internal data representation of a single row in the control. + struct Row + { + StringTableEntry mLabel; ///< Text to display in the row as a label + StringTableEntry mScriptCallback; ///< Script callback when row is activated + S32 mIconIndex; ///< Index of the icon to display on the row (-1 = no icon) + S32 mHeightPad; ///< Extra amount to pad above this row + bool mUseHighlightIcon; ///< Toggle the use of the highlight icon + bool mEnabled; ///< If this row is enabled or not (grayed out) + + virtual ~Row() {} + }; + +public: + /// \return The index of the highlighted row or NO_ROW if none of the rows + /// are currently highlighted. + virtual S32 getHighlighted() const { return mHighlighted; } + + /// \return The index of the selected row or NO_ROW if none of the rows are + /// currently selected. + virtual S32 getSelected() const { return mSelected; } + + /// Sets the selected row. Only rows that are enabled can be selected. Input is + /// clamped to [0, mRows.size()) + /// + /// \param index The index to set as selected. + virtual void setSelected(S32 index); + + /// Determines if the specified row is enabled or disabled. + /// + /// \param index Index of the row to check. + /// \return True if the specified row is enabled. False if the row is not + /// enabled or the given index was not valid. + virtual bool isRowEnabled(S32 index) const; + + /// Sets a row's enabled status according to the given parameters. + /// + /// \param index The row to set the enabled status of. + /// \param enabled Indicate true to enable the row or false to disable it. + virtual void setRowEnabled(S32 index, bool enabled); + + /// Gets the label displayed on the specified row. + /// + /// \param rowIndex Index of the row to get the label of. + /// \return The label for the row. + virtual StringTableEntry getRowLabel(S32 rowIndex) const; + + /// Sets the label on the given row. + /// + /// \param rowIndex Index of the row to set the label on. + /// \param label Text to set as the label of the row. + virtual void setRowLabel(S32 rowIndex, const char * label); + + /// Adds a row to the control. + /// + /// \param label The text to display on the row as a label. + /// \param callback Name of a script function to use as a callback when this + /// row is activated. + /// \param icon [optional] Index of the icon to use as a marker. Default -1 + /// means no icon will be shown on this row. + /// \param yPad [optional] An extra amount of height padding before the row. + /// \param enabled [optional] If this row is initially enabled. Default true. + virtual void addRow(const char* label, const char* callback, S32 icon = -1, S32 yPad = 0, bool useHighlightIcon = true, bool enabled = true); + + /// Activates the current row. The script callback of the current row will + /// be called (if it has one). + virtual void activateRow(); + + /// Gets the number of rows in the control. + /// + /// \return The number of rows in this control. + virtual S32 getRowCount() const { return mRows.size(); } + + GuiGameListMenuCtrl(); + ~GuiGameListMenuCtrl(); + + void onRender(Point2I offset, const RectI &updateRect); + + /// Callback when the object is registered with the sim. + /// + /// \return True if the profile was successfully added, false otherwise. + bool onAdd(); + + /// Callback when the control wakes up. + bool onWake(); + + /// Callback when a key is pressed. + /// + /// \param event The event that triggered this callback. + bool onKeyDown(const GuiEvent &event); + + /// Callback when a key is repeating. + /// + /// \param event The event that triggered this callback. + bool onKeyRepeat(const GuiEvent &event){ return onKeyDown(event); } + + /// Callback when the mouse button is clicked on the control. + /// + /// \param event A reference to the event that triggered the callback. + void onMouseDown(const GuiEvent &event); + + /// Callback when the mouse is dragged on the control. + /// + /// \param event A reference to the event that triggered the callback. + void onMouseDragged(const GuiEvent &event){ onMouseDown(event); } + + /// Callback when the mouse leaves the control. + /// + /// \param event A reference to the event that triggered the callback. + void onMouseLeave(const GuiEvent &event); + + /// Callback when the mouse is moving over this control + /// + /// \param event A reference to the event that triggered the callback. + void onMouseMove(const GuiEvent &event); + + /// Callback when the mouse button is released. + /// + /// \param event A reference to the event that triggered the callback. + void onMouseUp(const GuiEvent &event); + + /// Callback when the gamepad axis is activated. + /// + /// \param event A reference to the event that triggered the callback. + virtual bool onGamepadAxisUp(const GuiEvent & event); + + /// Callback when the gamepad axis is activated. + /// + /// \param event A reference to the event that triggered the callback. + virtual bool onGamepadAxisDown(const GuiEvent & event); + + DECLARE_CONOBJECT(GuiGameListMenuCtrl); + DECLARE_CATEGORY( "Gui Game" ); + DECLARE_DESCRIPTION( "Base class for cross platform menu controls that are gamepad friendly." ); + + /// Initializes fields accessible through the console. + static void initPersistFields(); + + static const S32 NO_ROW = -1; ///< Indicates a query result of no row found. + static const S32 NO_ICON = -1; ///< Indicates a row has no extra icon available + +protected: + /// Adds a row to the control. + /// + /// \param row A reference to the row object to fill. + /// \param label The text to display on the row as a label. + /// \param callback Name of a script function to use as a callback when this + /// row is activated. + /// \param icon [optional] Index of the icon to use as a marker. Default -1 + /// means no icon will be shown on this row. + /// \param yPad [optional] An extra amount of height padding before the row. + /// \param enabled [optional] If this row is initially enabled. Default true. + virtual void addRow(Row * row, const char* label, const char* callback, S32 icon, S32 yPad, bool useHighlightIcon, bool enabled); + + /// Determines if the given index is a valid row index. Any index pointing at + /// an existing row is valid. + /// + /// \param index The index to check for validity. + /// \return True if the index points at a valid row, false otherwise. + virtual bool isValidRowIndex(S32 index) const; + + /// Sets the script variable $ThisControl to reflect this control. + virtual void setThisControl(); + + /// Called to implement debug rendering which displays colored lines to + /// provide visual feedback on extents and hit zones. + virtual void onDebugRender(Point2I offset); + + /// Looks up the row having a hit area at the given global point. + /// + /// \param globalPoint The point we want to check for hitting a row. + /// \return The index of the hit row or NO_ROW if no row was hit. + virtual S32 getRow(Point2I globalPoint); + + /// Checks to make sure our control has a profile of the correct type. + /// + /// \return True if the profile is of type GuiGameListMenuProfile or false if + /// the profile is of any other type. + virtual bool hasValidProfile() const; + + /// Enforces the validity of the fields on this control and its profile (if + /// the profile is valid, see: hasValidProfile). + virtual void enforceConstraints(); + + /// @name Callbacks + /// @{ + DECLARE_CALLBACK( void, onChange, () ); + /// @} + + /// Evaluates some script. If the command is empty then nothing is evaluated. + /// + /// \param command The script to evaluate. + void doScriptCommand(StringTableEntry command); + + StringTableEntry mCallbackOnA; ///< Script callback when the 'A' button is pressed + StringTableEntry mCallbackOnB; ///< Script callback when the 'B' button is pressed + StringTableEntry mCallbackOnX; ///< Script callback when the 'X' button is pressed + StringTableEntry mCallbackOnY; ///< Script callback when the 'Y' button is pressed + + bool mDebugRender; ///< Determines when to show debug render lines + Vector mRows; ///< Holds data wrappers on all the rows we have + +private: + /// Recalculates the height of this control based on the stored row height and + /// and padding on the rows. + virtual Point2I getMinExtent() const; + + /// Makes sure the height will allow all rows to be displayed without being + /// truncated. + void updateHeight(); + + /// Sets the first enabled row as selected. If there are no enabled rows then + /// selected will be set to NO_ROW. + void selectFirstEnabledRow(); + + /// Changes the currently selected row. + /// + /// \param delta The amount to change the row selection by. Typically this will + /// be 1 or -1. + void changeRow(S32 delta); + + S32 mSelected; ///< index of the currently selected row + S32 mHighlighted; ///< index of the currently highlighted row +}; + +/// \class GuiGameListMenuProfile +/// A gui profile with additional fields specific to GuiGameListMenuCtrl. +class GuiGameListMenuProfile : public GuiControlProfile +{ + typedef GuiControlProfile Parent; + +public: + /// Enforces range constraints on all required fields. + virtual void enforceConstraints(); + + /// Get the height of rows in this profile. All rows are considered to be the + /// same base height. Rows can have an extra amount of y padding defined when + /// they are added to the control. + /// + /// \return The height of rows in this profile. + S32 getRowHeight() { return (mRowSize.y) ? mRowSize.y : getBitmapArrayRect(TEX_NORMAL).extent.y; } + + /// Get the width of rows in this profile. All rows are considered to be the + /// same width. + /// + /// \return The width of rows in this profile. + S32 getRowWidth() { return (mRowSize.x) ? mRowSize.x : getBitmapArrayRect(TEX_NORMAL).extent.x; } + + /// Row scale is the ratio between the defined row size and the raw size of + /// the bitmap. + /// + /// \return The row scale. + const Point2F & getRowScale() const { return mRowScale; } + + /// Gets the extent of icons for this profile. If there are no icons you will + /// get a point of (0, 0); + /// + /// \return The extent of icons or (0, 0) if there aren't any. + Point2I getIconExtent(); + + /// Gets the extent of arrows for this profile. If there are no arrows you + /// will get a point of (0, 0). + /// + /// \return The extent of icons or (0, 0) if there aren't any. + Point2I getArrowExtent(); + + /// Gets the extent of the defined hit area for this profile. If the hit area + /// is not defined then it defaults to the full size of a row. + /// + /// \return The extents of the defined hit area or the full size of the row. + Point2I getHitAreaExtent(); + + /// Determines if this profile has textures for the left and right arrows. + /// + /// \return True if the profile's bitmap has textures for the arrows, false + /// otherwise. + bool hasArrows(){ return (! getBitmapArrayRect(TEX_FIRST_ARROW).extent.isZero()); } + + /// Callback when the object is registered with the sim. + /// + /// \return True if the profile was successfully added, false otherwise. + bool onAdd(); + + Point2I mHitAreaUpperLeft; ///< Offset for the upper left corner of the hit area + Point2I mHitAreaLowerRight; ///< Offset for the lower right corner of the hit area + Point2I mIconOffset; ///< Offset for a row's extra icon + Point2I mRowSize; ///< The base size of a row + + GuiGameListMenuProfile(); + + DECLARE_CONOBJECT(GuiGameListMenuProfile); + + /// Initializes fields accessible through the console. + static void initPersistFields(); + + enum + { + TEX_NORMAL = 0, ///< texture index for a normal, unselected row + TEX_SELECTED = 1, ///< texture index for a selected row + TEX_HIGHLIGHT = 2, ///< texture index for a highlighted row (moused over, not selected) + TEX_DISABLED = 3, ///< texture index for a disabled row + TEX_L_ARROW_OFF = 4, ///< texture index for the left arrow of an unselected row + TEX_L_ARROW_ON = 5, ///< texture index for the left arrow of a selected row + TEX_R_ARROW_OFF = 6, ///< texture index for the right arrow of an unselected row + TEX_R_ARROW_ON = 7, ///< texture index for the right arrow of a selected row + + TEX_FIRST_ARROW = 4, ///< texture index for the first arrow + TEX_FIRST_ICON = 8, ///< texture index for the first row marker icon + }; + +private: + Point2F mRowScale; ///< Ratio of row size to actual bitmap size +}; + +#endif diff --git a/Engine/source/gui/controls/guiGameListOptionsCtrl.cpp b/Engine/source/gui/controls/guiGameListOptionsCtrl.cpp new file mode 100644 index 000000000..42eb38092 --- /dev/null +++ b/Engine/source/gui/controls/guiGameListOptionsCtrl.cpp @@ -0,0 +1,538 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "guiGameListOptionsCtrl.h" +#include "gfx/gfxDrawUtil.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "core/strings/stringUnit.h" + +//----------------------------------------------------------------------------- +// GuiGameListOptionsCtrl +//----------------------------------------------------------------------------- + +GuiGameListOptionsCtrl::GuiGameListOptionsCtrl() +{ +} + +GuiGameListOptionsCtrl::~GuiGameListOptionsCtrl() +{ +} + +bool GuiGameListOptionsCtrl::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + if( !hasValidProfile() ) + { + GuiGameListOptionsProfile* profile; + if( !Sim::findObject( "DefaultOptionsMenuProfile", profile ) ) + { + Con::errorf( "GuiGameListOptionsCtrl: %s can't be created with a profile of type %s. Please create it with a profile of type GuiGameListOptionsProfile.", + getName(), mProfile->getClassName() ); + return false; + } + else + Con::warnf( "GuiGameListOptionsCtrl: substituted non-GuiGameListOptionsProfile in %s for DefaultOptionsMenuProfile", getName() ); + + setControlProfile( profile ); + } + + return true; +} + +void GuiGameListOptionsCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + Parent::onRender(offset, updateRect); + GuiGameListOptionsProfile * profile = (GuiGameListOptionsProfile *) mProfile; + + F32 xScale = (float) getWidth() / profile->getRowWidth(); + + S32 rowHeight = profile->getRowHeight(); + + bool profileHasArrows = profile->hasArrows(); + Point2I arrowExtent; + S32 arrowOffsetY(0); + if (profileHasArrows) + { + arrowExtent = profile->getArrowExtent(); + + // icon is centered vertically + arrowOffsetY = (rowHeight - arrowExtent.y) >> 1; + } + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + + Point2I currentOffset = offset; + Point2I arrowOffset; + S32 columnSplit = profile->mColumnSplit * xScale; + S32 iconIndex; + for (Vector::iterator row = mRows.begin(); row < mRows.end(); ++row) + { + Row * myRow = (Row *) *row; + if (row != mRows.begin()) + { + // rows other than the first can have padding above them + currentOffset.y += myRow->mHeightPad; + currentOffset.y += rowHeight; + } + + bool hasOptions = (myRow->mOptions.size() > 0) && myRow->mSelectedOption > -1; + if (hasOptions) + { + bool isRowSelected = (getSelected() != NO_ROW) && (row == &mRows[getSelected()]); + bool isRowHighlighted = (getHighlighted() != NO_ROW) ? ((row == &mRows[getHighlighted()]) && ((*row)->mEnabled)) : false; + if (profileHasArrows) + { + // render the left arrow + bool arrowOnL = (isRowSelected || isRowHighlighted) && (myRow->mWrapOptions || (myRow->mSelectedOption > 0)); + iconIndex = (arrowOnL) ? Profile::TEX_L_ARROW_ON : Profile::TEX_L_ARROW_OFF; + arrowOffset.x = currentOffset.x + columnSplit; + arrowOffset.y = currentOffset.y + arrowOffsetY; + + drawer->clearBitmapModulation(); + drawer->drawBitmapStretchSR(profile->mTextureObject, RectI(arrowOffset, arrowExtent), profile->getBitmapArrayRect((U32)iconIndex)); + + // render the right arrow + bool arrowOnR = (isRowSelected || isRowHighlighted) && (myRow->mWrapOptions || (myRow->mSelectedOption < myRow->mOptions.size() - 1)); + iconIndex = (arrowOnR) ? Profile::TEX_R_ARROW_ON : Profile::TEX_R_ARROW_OFF; + arrowOffset.x = currentOffset.x + (profile->mHitAreaLowerRight.x - profile->mRightPad) * xScale - arrowExtent.x; + arrowOffset.y = currentOffset.y + arrowOffsetY; + + drawer->clearBitmapModulation(); + drawer->drawBitmapStretchSR(profile->mTextureObject, RectI(arrowOffset, arrowExtent), profile->getBitmapArrayRect((U32)iconIndex)); + } + + // get the appropriate font color + ColorI fontColor; + if (! myRow->mEnabled) + { + fontColor = profile->mFontColorNA; + } + else if (isRowSelected) + { + fontColor = profile->mFontColorSEL; + } + else if (isRowHighlighted) + { + fontColor = profile->mFontColorHL; + } + else + { + fontColor = profile->mFontColor; + } + + // calculate text to be at the center between the arrows + GFont * font = profile->mFont; + StringTableEntry text = myRow->mOptions[myRow->mSelectedOption]; + S32 textWidth = font->getStrWidth(text); + S32 columnWidth = profile->mHitAreaLowerRight.x * xScale - profile->mRightPad - columnSplit; + S32 columnCenter = columnSplit + (columnWidth >> 1); + S32 textStartX = columnCenter - (textWidth >> 1); + Point2I textOffset(textStartX, 0); + + // render the option text itself + Point2I textExtent(columnWidth, rowHeight); + drawer->setBitmapModulation(fontColor); + renderJustifiedText(currentOffset + textOffset, textExtent, text); + } + } +} + +void GuiGameListOptionsCtrl::onDebugRender(Point2I offset) +{ + Parent::onDebugRender(offset); + GuiGameListOptionsProfile * profile = (GuiGameListOptionsProfile *) mProfile; + + F32 xScale = (float) getWidth() / profile->getRowWidth(); + + ColorI column1Color(255, 255, 0); // yellow + ColorI column2Color(0, 255, 0); // green + Point2I shrinker(-1, -1); + + S32 rowHeight = profile->getRowHeight(); + Point2I currentOffset(offset); + Point2I rowExtent(getExtent().x, rowHeight); + Point2I hitAreaExtent(profile->getHitAreaExtent()); + hitAreaExtent.x *= xScale; + hitAreaExtent += shrinker; + Point2I column1Extent((profile->mColumnSplit - profile->mHitAreaUpperLeft.x) * xScale - 1, hitAreaExtent.y); + Point2I column2Extent(hitAreaExtent.x - column1Extent.x - 1, hitAreaExtent.y); + Point2I hitAreaOffset = profile->mHitAreaUpperLeft; + hitAreaOffset.x *= xScale; + + RectI borderRect; + Point2I upperLeft; + for (Vector::iterator row = mRows.begin(); row < mRows.end(); ++row) + { + // set the top of the current row + if (row != mRows.begin()) + { + // rows other than the first can have padding above them + currentOffset.y += (*row)->mHeightPad; + currentOffset.y += rowHeight; + } + + // draw the box around column 1 + upperLeft = currentOffset + hitAreaOffset; + borderRect.point = upperLeft; + borderRect.extent = column1Extent; + GFX->getDrawUtil()->drawRect(borderRect, column1Color); + + // draw the box around column 2 + upperLeft.x += column1Extent.x + 1; + borderRect.point = upperLeft; + borderRect.extent = column2Extent; + GFX->getDrawUtil()->drawRect(borderRect, column2Color); + } +} + +void GuiGameListOptionsCtrl::addRow(const char* label, const char* optionsList, bool wrapOptions, const char* callback, S32 icon, S32 yPad, bool enabled) +{ + static StringTableEntry DELIM = StringTable->insert("\t", true); + Row * row = new Row(); + Vector options( __FILE__, __LINE__ ); + S32 count = StringUnit::getUnitCount(optionsList, DELIM); + for (int i = 0; i < count; ++i) + { + const char * option = StringUnit::getUnit(optionsList, i, DELIM); + options.push_back(StringTable->insert(option, true)); + } + row->mOptions = options; + bool hasOptions = row->mOptions.size() > 0; + row->mSelectedOption = (hasOptions) ? 0 : NO_OPTION; + row->mWrapOptions = wrapOptions; + Parent::addRow(row, label, callback, icon, yPad, true, (hasOptions) ? enabled : false); +} + +void GuiGameListOptionsCtrl::setOptions(S32 rowIndex, const char * optionsList) +{ + static StringTableEntry DELIM = StringTable->insert("\t", true); + + if (! isValidRowIndex(rowIndex)) + { + return; + } + + Row * row = (Row *)mRows[rowIndex]; + + S32 count = StringUnit::getUnitCount(optionsList, DELIM); + row->mOptions.setSize(count); + for (int i = 0; i < count; ++i) + { + const char * option = StringUnit::getUnit(optionsList, i, DELIM); + row->mOptions[i] = StringTable->insert(option, true); + } + + if (row->mSelectedOption >= row->mOptions.size()) + { + row->mSelectedOption = row->mOptions.size() - 1; + } +} + +bool GuiGameListOptionsCtrl::hasValidProfile() const +{ + GuiGameListOptionsProfile * profile = dynamic_cast(mProfile); + return profile; +} + +void GuiGameListOptionsCtrl::enforceConstraints() +{ + Parent::enforceConstraints(); +} + +void GuiGameListOptionsCtrl::onMouseUp(const GuiEvent &event) +{ + S32 hitRow = getRow(event.mousePoint); + if ((hitRow != NO_ROW) && isRowEnabled(hitRow) && (hitRow == getSelected())) + { + S32 xPos = globalToLocalCoord(event.mousePoint).x; + clickOption((Row *) mRows[getSelected()], xPos); + } +} + +void GuiGameListOptionsCtrl::clickOption(Row * row, S32 xPos) +{ + GuiGameListOptionsProfile * profile = (GuiGameListOptionsProfile *) mProfile; + if (! profile->hasArrows()) + { + return; + } + + F32 xScale = (float) getWidth() / profile->getRowWidth(); + + S32 bitmapArrowWidth = mProfile->getBitmapArrayRect(Profile::TEX_FIRST_ARROW).extent.x; + + S32 leftArrowX1 = profile->mColumnSplit * xScale; + S32 leftArrowX2 = leftArrowX1 + bitmapArrowWidth; + + S32 rightArrowX2 = (profile->mHitAreaLowerRight.x - profile->mRightPad) * xScale; + S32 rightArrowX1 = rightArrowX2 - bitmapArrowWidth; + + if ((leftArrowX1 <= xPos) && (xPos <= leftArrowX2)) + { + changeOption(row, -1); + } + else if ((rightArrowX1 <= xPos) && (xPos <= rightArrowX2)) + { + changeOption(row, 1); + } +} + +bool GuiGameListOptionsCtrl::onKeyDown(const GuiEvent &event) +{ + switch (event.keyCode) + { + case KEY_LEFT: + changeOption(-1); + return true; + + case KEY_RIGHT: + changeOption(1); + return true; + + default: + break; + } + + return Parent::onKeyDown(event); +} + +bool GuiGameListOptionsCtrl::onGamepadAxisLeft( const GuiEvent &event ) +{ + changeOption(-1); + return true; +} + +bool GuiGameListOptionsCtrl::onGamepadAxisRight( const GuiEvent &event ) +{ + changeOption(1); + return true; +} + +void GuiGameListOptionsCtrl::changeOption(S32 delta) +{ + if (getSelected() != NO_ROW) + { + Row * row = (Row *) mRows[getSelected()]; + changeOption(row, delta); + } +} + +void GuiGameListOptionsCtrl::changeOption(Row * row, S32 delta) +{ + S32 optionCount = row->mOptions.size(); + + S32 newSelection = row->mSelectedOption + delta; + if (optionCount == 0) + { + newSelection = NO_OPTION; + } + else if (! row->mWrapOptions) + { + newSelection = mClamp(newSelection, 0, optionCount - 1); + } + else if (newSelection < 0) + { + newSelection = optionCount - 1; + } + else if (newSelection >= optionCount) + { + newSelection = 0; + } + row->mSelectedOption = newSelection; + + static StringTableEntry LEFT = StringTable->insert("LEFT", true); + static StringTableEntry RIGHT = StringTable->insert("RIGHT", true); + + if (row->mScriptCallback != NULL) + { + setThisControl(); + StringTableEntry direction = NULL; + if (delta < 0) + { + direction = LEFT; + } + else if (delta > 0) + { + direction = RIGHT; + } + if ((direction != NULL) && (Con::isFunction(row->mScriptCallback))) + { + Con::executef(row->mScriptCallback, direction); + } + } +} + +StringTableEntry GuiGameListOptionsCtrl::getCurrentOption(S32 rowIndex) const +{ + if (isValidRowIndex(rowIndex)) + { + Row * row = (Row *) mRows[rowIndex]; + if (row->mSelectedOption != NO_OPTION) + { + return row->mOptions[row->mSelectedOption]; + } + } + return StringTable->insert("", false); +} + +bool GuiGameListOptionsCtrl::selectOption(S32 rowIndex, const char * theOption) +{ + if (! isValidRowIndex(rowIndex)) + { + return false; + } + + Row * row = (Row *) mRows[rowIndex]; + + for (Vector::iterator anOption = row->mOptions.begin(); anOption < row->mOptions.end(); ++anOption) + { + if (dStrcmp(*anOption, theOption) == 0) + { + S32 newIndex = anOption - row->mOptions.begin(); + row->mSelectedOption = newIndex; + return true; + } + } + + return false; +} + +//----------------------------------------------------------------------------- +// Console stuff (GuiGameListOptionsCtrl) +//----------------------------------------------------------------------------- + +IMPLEMENT_CONOBJECT(GuiGameListOptionsCtrl); + +ConsoleDocClass( GuiGameListOptionsCtrl, + "@brief A control for showing pages of options that are gamepad friendly.\n\n" + + "Each row in this control allows the selection of one value from a set of " + "options using the keyboard, gamepad or mouse. The row is rendered as 2 " + "columns: the first column contains the row label, the second column " + "contains left and right arrows (for mouse picking) and the currently " + "selected value.\n\n" + + "@see GuiGameListOptionsProfile\n\n" + + "@ingroup GuiGame" +); + +void GuiGameListOptionsCtrl::initPersistFields() +{ + Parent::initPersistFields(); +} + +DefineEngineMethod( GuiGameListOptionsCtrl, addRow, void, + ( const char* label, const char* options, bool wrapOptions, const char* callback, S32 icon, S32 yPad, bool enabled ), + ( -1, 0, true ), + "Add a row to the list control.\n\n" + "@param label The text to display on the row as a label.\n" + "@param options A tab separated list of options.\n" + "@param wrapOptions Specify true to allow options to wrap at each end or false to prevent wrapping.\n" + "@param callback Name of a script function to use as a callback when this row is activated.\n" + "@param icon [optional] Index of the icon to use as a marker.\n" + "@param yPad [optional] An extra amount of height padding before the row. Does nothing on the first row.\n" + "@param enabled [optional] If this row is initially enabled." ) +{ + object->addRow( label, options, wrapOptions, callback, icon, yPad, enabled ); +} + +DefineEngineMethod( GuiGameListOptionsCtrl, getCurrentOption, const char *, ( S32 row ),, + "Gets the text for the currently selected option of the given row.\n\n" + "@param row Index of the row to get the option from.\n" + "@return A string representing the text currently displayed as the selected option on the given row. If there is no such displayed text then the empty string is returned." ) +{ + return object->getCurrentOption( row ); +} + +DefineEngineMethod( GuiGameListOptionsCtrl, selectOption, bool, ( S32 row, const char* option ),, + "Set the row's current option to the one specified\n\n" + "@param row Index of the row to set an option on.\n" + "@param option The option to be made active.\n" + "@return True if the row contained the option and was set, false otherwise." ) +{ + return object->selectOption( row, option ); +} + +DefineEngineMethod( GuiGameListOptionsCtrl, setOptions, void, ( S32 row, const char* optionsList ),, + "Sets the list of options on the given row.\n\n" + "@param row Index of the row to set options on." + "@param optionsList A tab separated list of options for the control." ) +{ + object->setOptions( row, optionsList ); +} + +//----------------------------------------------------------------------------- +// GuiGameListOptionsProfile +//----------------------------------------------------------------------------- + +GuiGameListOptionsProfile::GuiGameListOptionsProfile() +: mColumnSplit(0), + mRightPad(0) +{ +} + +void GuiGameListOptionsProfile::enforceConstraints() +{ + Parent::enforceConstraints(); + + if( mHitAreaUpperLeft.x > mColumnSplit || mColumnSplit > mHitAreaLowerRight.x ) + Con::errorf( "GuiGameListOptionsProfile: You can't create %s with a ColumnSplit outside the hit area. You set the split to %d. Please change the ColumnSplit to be in the range [%d, %d].", + getName(), mColumnSplit, mHitAreaUpperLeft.x, mHitAreaLowerRight.x); + + mColumnSplit = mClamp(mColumnSplit, mHitAreaUpperLeft.x, mHitAreaLowerRight.x); +} + +//----------------------------------------------------------------------------- +// Console stuff (GuiGameListOptionsProfile) +//----------------------------------------------------------------------------- + +IMPLEMENT_CONOBJECT(GuiGameListOptionsProfile); + +ConsoleDocClass( GuiGameListOptionsProfile, + "@brief A GuiControlProfile with additional fields specific to GuiGameListOptionsCtrl.\n\n" + + "@tsexample\n" + "new GuiGameListOptionsProfile()\n" + "{\n" + " columnSplit = \"100\";\n" + " rightPad = \"4\";\n" + " //Properties not specific to this control have been omitted from this example.\n" + "};\n" + "@endtsexample\n\n" + + "@ingroup GuiGame" +); + +void GuiGameListOptionsProfile::initPersistFields() +{ + addField( "columnSplit", TypeS32, Offset(mColumnSplit, GuiGameListOptionsProfile), + "Padding between the leftmost edge of the control, and the row's left arrow." ); + + addField( "rightPad", TypeS32, Offset(mRightPad, GuiGameListOptionsProfile), + "Padding between the rightmost edge of the control and the row's right arrow." ); + + Parent::initPersistFields(); +} diff --git a/Engine/source/gui/controls/guiGameListOptionsCtrl.h b/Engine/source/gui/controls/guiGameListOptionsCtrl.h new file mode 100644 index 000000000..d390e5e16 --- /dev/null +++ b/Engine/source/gui/controls/guiGameListOptionsCtrl.h @@ -0,0 +1,188 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GuiGameListOptionsCtrl_H_ +#define _GuiGameListOptionsCtrl_H_ + +#include "gui/controls/guiGameListMenuCtrl.h" + +/// \class GuiGameListOptionsCtrl +/// A control for showing pages of options that are gamepad friendly. +class GuiGameListOptionsCtrl : public GuiGameListMenuCtrl +{ + typedef GuiGameListMenuCtrl Parent; + +protected: + /// \struct Row + /// An extension to the parent's row, adding the ability to keep a collection + /// of options and track status related to them. + struct Row : public Parent::Row + { + Vector mOptions; ///< Collection of options available to display + S32 mSelectedOption; ///< Index into mOptions pointing at the selected option + bool mWrapOptions; ///< Determines if options should "wrap around" at the ends + + Row() + { + VECTOR_SET_ASSOCIATION( mOptions ); + } + }; + +public: + /// Gets the text for the currently selected option of the given row. + /// + /// \param rowIndex Index of the row to get the option from. + /// \return A string representing the text currently displayed as the selected + /// option on the given row. If there is no such displayed text then the empty + /// string is returned. + StringTableEntry getCurrentOption(S32 rowIndex) const; + + /// Attempts to set the given row to the specified selected option. The option + /// will only be set if the option exists in the control. + /// + /// \param rowIndex Index of the row to set an option on. + /// \param option The option to be made active. + /// \return True if the row contained the option and was set, false otherwise. + bool selectOption(S32 rowIndex, StringTableEntry option); + + /// Sets the list of options on the given row. + /// + /// \param rowIndex Index of the row to set options on. + /// \param optionsList A tab separated list of options for the control. + void setOptions(S32 rowIndex, const char * optionsList); + + /// Adds a row to the control. + /// + /// \param label The text to display on the row as a label. + /// \param optionsList A tab separated list of options for the control. + /// \param wrapOptions Specify true to allow options to wrap at the ends or + /// false to prevent wrapping. + /// \param callback [optional] Name of a script function to use as a callback + /// when this row is activated. Default NULL means no callback. + /// \param icon [optional] Index of the icon to use as a marker. Default -1 + /// means no icon will be shown on this row. + /// \param yPad [optional] An extra amount of height padding before the row. + /// \param enabled [optional] If this row is initially enabled. Default true. + void addRow(const char* label, const char* optionsList, bool wrapOptions, const char* callback, S32 icon = -1, S32 yPad = 0, bool enabled = true); + + void onRender(Point2I offset, const RectI &updateRect); + + /// Callback when the mouse button is released. + /// + /// \param event A reference to the event that triggered the callback. + void onMouseUp(const GuiEvent &event); + + /// Callback when a key is pressed. + /// + /// \param event The event that triggered this callback. + bool onKeyDown(const GuiEvent &event); + + /// Callback when a key is repeating. + /// + /// \param event The event that triggered this callback. + bool onKeyRepeat(const GuiEvent &event){ return onKeyDown(event); } + + /// Callback when the gamepad axis is activated. + /// + /// \param event A reference to the event that triggered the callback. + virtual bool onGamepadAxisLeft(const GuiEvent &event); + + /// Callback when the gamepad axis is activated. + /// + /// \param event A reference to the event that triggered the callback. + virtual bool onGamepadAxisRight(const GuiEvent &event); + + GuiGameListOptionsCtrl(); + ~GuiGameListOptionsCtrl(); + + DECLARE_CONOBJECT(GuiGameListOptionsCtrl); + DECLARE_DESCRIPTION( "A control for showing pages of options that are gamepad friendly." ); + + virtual bool onAdd(); + + /// Initializes fields accessible through the console. + static void initPersistFields(); + + static const S32 NO_OPTION = -1; ///< Indicates there is no option + +protected: + /// Checks to make sure our control has a profile of the correct type. + /// + /// \return True if the profile is of type GuiGameListOptionsProfile or false + /// if the profile is of any other type. + bool hasValidProfile() const; + + /// Enforces the validity of the fields on this control and its profile (if the + /// profile is valid, see: hasValidProfile). + void enforceConstraints(); + + /// Adds lines around the column divisions to the feedback already provided + /// in the Parent. + void onDebugRender(Point2I offset); + +private: + /// Performs a click on the current option row. The x position is used to + /// determine if the left or right arrow were clicked. If one was clicked, the + /// option will be changed. If neither was clicked, the option is unaffected. + /// This method should only be called when there is an actively selected row. + /// + /// \param row The row to perform the click on. + /// \param xPos The x position of the the click, relative to the control. + void clickOption(Row * row, S32 xPos); + + /// Changes the option on the currently selected row. If there is no row + /// selected, this method does nothing. + /// + /// \param delta The amount to change the option selection by. Typically this + /// will be 1 or -1. + void changeOption(S32 delta); + + /// Changes the option on the given row. + /// + /// \param row The row to change the option on. + /// \param delta The amount to change the option selection by. Typically this + /// will be 1 or -1. + void changeOption(Row * row, S32 delta); +}; + +/// \class GuiGameListOptionsProfile +/// A gui profile with additional fields specific to GuiGameListOptionsCtrl. +class GuiGameListOptionsProfile : public GuiGameListMenuProfile +{ + typedef GuiGameListMenuProfile Parent; + +public: + /// Enforces range constraints on all required fields. + void enforceConstraints(); + + GuiGameListOptionsProfile(); + + S32 mColumnSplit; ///< Absolute position of the split between columns + S32 mRightPad; ///< Extra padding between the right arrow and the hit area + + DECLARE_CONOBJECT(GuiGameListOptionsProfile); + + /// Initializes fields accessible through the console. + static void initPersistFields(); +}; + +#endif diff --git a/Engine/source/gui/controls/guiGradientCtrl.cpp b/Engine/source/gui/controls/guiGradientCtrl.cpp new file mode 100644 index 000000000..164a602b1 --- /dev/null +++ b/Engine/source/gui/controls/guiGradientCtrl.cpp @@ -0,0 +1,652 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#include "console/console.h" +#include "gfx/gfxDevice.h" +#include "console/consoleTypes.h" +#include "gui/core/guiCanvas.h" +#include "gui/buttons/guiButtonCtrl.h" +#include "gui/core/guiDefaultControlRender.h" +#include "gui/controls/guiGradientCtrl.h" +#include "gui/controls/guiColorPicker.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxDrawUtil.h" +#include "console/engineAPI.h" + +//----------------------------------------------------------------------------- +// GuiGradientSwatchCtrl + +IMPLEMENT_CONOBJECT(GuiGradientSwatchCtrl); + +ConsoleDocClass( GuiGradientSwatchCtrl, + "@brief Swatch selector that appears inside the GuiGradientCtrl object. These objects are automatically created by GuiGradientCtrl. \n\n" + "Currently only appears to be editor specific\n\n" + "@see GuiSwatchButtonCtrl\n" + "@see GuiGradientCtrl\n\n" + "@ingroup GuiCore\n" + "@internal" +); + +IMPLEMENT_CALLBACK( GuiGradientSwatchCtrl, onMouseDown, void, (),(), + "@brief Called whenever the left mouse button has entered the down state while in this control.\n\n" + "@tsexample\n" + "// The left mouse button is down on the control, causing the callback to occur.\n" + "GuiGradientSwatchCtrl::onMouseDown(%this)\n" + " {\n" + " // Code to run when the callback occurs\n" + " }\n" + "@endtsexample\n\n" + "@see GuiControl\n" + "@see GuiSwatchButtonCtrl\n\n" + "@internal" +); + +IMPLEMENT_CALLBACK( GuiGradientSwatchCtrl, onDoubleClick, void, (),(), + "@brief Called whenever the left mouse button performs a double click while in this control.\n\n" + "@tsexample\n" + "// The left mouse button has performed a double click on the control, causing the callback to occur.\n" + "GuiGradientSwatchCtrl::onDoubleClick(%this)\n" + " {\n" + " // Code to run when the callback occurs\n" + " }\n" + "@endtsexample\n\n" + "@see GuiControl\n" + "@see GuiSwatchButtonCtrl\n\n" + "@internal" +); + + +GuiGradientSwatchCtrl::GuiGradientSwatchCtrl() +{ + setPosition(0, 0); + setExtent(14, 14); + mMouseDownPosition = Point2I(0, 0); + mSwatchColor = ColorI( 1, 1, 1, 1 ); + mColorFunction = StringTable->insert("getColorF"); + setDataField( StringTable->insert("Profile"), NULL, "GuiInspectorSwatchButtonProfile" ); +} + +bool GuiGradientSwatchCtrl::onWake() +{ + if ( !Parent::onWake() ) + return false; + + if ( mPointer.isNull() ) + mPointer.set( "core/art/gui/images/arrowbtn_d", &GFXDefaultGUIProfile, avar("%s() - mGrid (line %d)", __FUNCTION__, __LINE__) ); + + char* altCommand = Con::getReturnBuffer(512); + dSprintf( altCommand, 512, "%s(%i.color, \"%i.setColor\");", mColorFunction, getId(), getId() ); + setField( "altCommand", altCommand ); + + return true; +} + +void GuiGradientSwatchCtrl::onRender( Point2I offset, const RectI &updateRect ) +{ + bool highlight = mMouseOver; + + ColorI backColor = mSwatchColor; + ColorI borderColor = mActive ? ( highlight ? mProfile->mBorderColorHL : mProfile->mBorderColor ) : mProfile->mBorderColorNA; + RectI renderRect( offset, getExtent() ); + + if ( !highlight ) + renderRect.inset( 1, 1 ); + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + drawer->clearBitmapModulation(); + + // Draw background transparency grid texture... + if ( mGrid.isValid() ) + drawer->drawBitmapStretch( mGrid, renderRect ); + + // Draw swatch color as fill... + drawer->drawRectFill( renderRect, mSwatchColor ); + + // Draw any borders... + drawer->drawRect( renderRect, borderColor ); +} + +void GuiGradientSwatchCtrl::onMouseDown(const GuiEvent &event) +{ + if (! mActive) + return; + + if (mProfile->mCanKeyFocus) + setFirstResponder(); + + //capture current bounds and mouse down position + mOrigBounds = getBounds(); + mMouseDownPosition = event.mousePoint; + + if(mUseMouseEvents) + onMouseDown_callback(); + + //lock the mouse + mouseLock(); + mDepressed = true; + + // If we have a double click then execute the alt command. + if ( event.mouseClickCount == 2 ) + { + onDoubleClick_callback(); + + execAltConsoleCallback(); + } + + setUpdate(); +} + +void GuiGradientSwatchCtrl::onMouseDragged(const GuiEvent &event) +{ + //gradientCtrl owns the y, x here however is regulated by the extent currently + //dirty, please fix + GuiGradientCtrl* parent = dynamic_cast(getParent()); + if( !parent ) + return; + + //use bounds and delta to move the ctrl + Point2I newPosition = mMouseDownPosition; + Point2I deltaMousePosition = event.mousePoint - mMouseDownPosition; + + newPosition.x = mOrigBounds.point.x + deltaMousePosition.x; + + // default position but it needs to be standard; currently using this cops out a static y value + newPosition.y = mOrigBounds.point.y; + + if( newPosition.x + parent->mSwatchFactor >= parent->mBlendRangeBox.point.x && + newPosition.x + parent->mSwatchFactor <= parent->mBlendRangeBox.extent.x ) + { + setPosition(newPosition); + if( parent ) + parent->sortColorRange(); + } +} + +void GuiGradientSwatchCtrl::onRightMouseDown(const GuiEvent &event) +{ + GuiGradientCtrl* parent = dynamic_cast(getParent()); + if( parent ) + parent->removeColorRange( this ); +} + +//----------------------------------------------------------------------------- +// GuiGradientCtrl + +static S32 QSORT_CALLBACK _numIncreasing( const void* a, const void* b ) +{ + GuiGradientCtrl::ColorRange *crA = (GuiGradientCtrl::ColorRange *) (a); + GuiGradientCtrl::ColorRange *crB = (GuiGradientCtrl::ColorRange *) (b); + S32 posA = crA->swatch->getPosition().x; + S32 posB = crB->swatch->getPosition().x; + return ( (posA < posB) ? -1 : ((posA > posB) ? 1 : 0) ); +} + +ImplementEnumType( GuiGradientPickMode, + "\n\n" + "@ingroup GuiCore" + "@internal") + { GuiGradientCtrl::pHorizColorRange, "HorizColor"}, + { GuiGradientCtrl::pHorizAlphaRange, "HorizAlpha"}, +EndImplementEnumType; + +IMPLEMENT_CONOBJECT(GuiGradientCtrl); + +ConsoleDocClass( GuiGradientCtrl, + "@brief Visual representation of color box used with the GuiColorPickerCtrl\n\n" + "Editor use only.\n\n" + "@internal" +); + + +GuiGradientCtrl::GuiGradientCtrl() +{ + setExtent(140, 30); + mDisplayMode = pHorizColorRange; + mSaveDisplayMode = pHorizColorRange; + mBaseColor = ColorF(1.,.0,1.); + mPickColor = ColorF(.0,.0,.0); + mMouseDown = mMouseOver = false; + mActive = true; + mPositionChanged = false; + mActionOnMove = false; + mShowReticle = true; + colorWhiteBlend = ColorF(1.,1.,1.,.75); + mSwatchFactor = 7; +} + +//-------------------------------------------------------------------------- +void GuiGradientCtrl::initPersistFields() +{ + addGroup("ColorPicker"); + addField("baseColor", TypeColorF, Offset(mBaseColor, GuiGradientCtrl)); + addField("pickColor", TypeColorF, Offset(mPickColor, GuiGradientCtrl)); + addField("displayMode", TYPEID< PickMode >(), Offset(mDisplayMode, GuiGradientCtrl) ); + addField("actionOnMove", TypeBool,Offset(mActionOnMove, GuiGradientCtrl)); + addField("showReticle", TypeBool, Offset(mShowReticle, GuiGradientCtrl)); + addField("swatchFactor", TypeS32, Offset(mSwatchFactor, GuiGradientCtrl)); + endGroup("ColorPicker"); + + Parent::initPersistFields(); +} + +bool GuiGradientCtrl::onAdd() +{ + Parent::onAdd(); + + S32 l = getBounds().point.x + mSwatchFactor, r = getBounds().point.x + getBounds().extent.x - mSwatchFactor; + S32 t = getBounds().point.y, b = getBounds().point.y + getBounds().extent.y - mSwatchFactor; + mBlendRangeBox = RectI( Point2I(l, t), Point2I(r, b) ); + + setupDefaultRange(); + reInitSwatches( mDisplayMode ); + return true; +} + +void GuiGradientCtrl::inspectPreApply() +{ + mSaveDisplayMode = mDisplayMode; +} + +void GuiGradientCtrl::inspectPostApply() +{ + if((mSaveDisplayMode != mDisplayMode) ) + reInitSwatches( mDisplayMode ); + + // Apply any transformations set in the editor + Parent::inspectPostApply(); +} + +void GuiGradientCtrl::onRender(Point2I offset, const RectI& updateRect) +{ + if (mStateBlock.isNull()) + { + GFXStateBlockDesc desc; + desc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha); + desc.setZReadWrite(false); + desc.zWriteEnable = false; + desc.setCullMode(GFXCullNone); + mStateBlock = GFX->createStateBlock( desc ); + } + + RectI boundsRect(offset, getExtent()); + renderColorBox(boundsRect); + + if (mPositionChanged) + { + mPositionChanged = false; + + // Now do onAction() if we are allowed + if (mActionOnMove) + onAction(); + } + + //render the children + renderChildControls( offset, updateRect); +} + +/// Function to invoke calls to draw the picker box and swatch controls +void GuiGradientCtrl::renderColorBox(RectI &bounds) +{ + // Draw color box differently depending on mode + if( mDisplayMode == pHorizColorRange ) + { + drawBlendRangeBox( bounds, false, mColorRange); + } + else if( mDisplayMode == pHorizAlphaRange ) + { + drawBlendRangeBox( bounds, false, mAlphaRange); + } +} + +/// Function to draw a set of boxes blending throughout an array of colors +void GuiGradientCtrl::drawBlendRangeBox(RectI &bounds, bool vertical, Vector colorRange) +{ + GFX->setStateBlock(mStateBlock); + + // Create new global dimensions + S32 l = bounds.point.x + mSwatchFactor, r = bounds.point.x + bounds.extent.x - mSwatchFactor; + S32 t = bounds.point.y, b = bounds.point.y + bounds.extent.y - mSwatchFactor; + + // Draw border using new global dimensions + if (mProfile->mBorder) + GFX->getDrawUtil()->drawRect( RectI( Point2I(l,t),Point2I(r,b) ), mProfile->mBorderColor); + + // Update local dimensions + mBlendRangeBox.point = globalToLocalCoord(Point2I(l, t)); + mBlendRangeBox.extent = globalToLocalCoord(Point2I(r, b)); + + if(colorRange.size() == 1) // Only one color to draw + { + PrimBuild::begin( GFXTriangleFan, 4 ); + + PrimBuild::color( colorRange.first().swatch->getColor() ); + PrimBuild::vertex2i( l, t ); + PrimBuild::vertex2i( l, b ); + + PrimBuild::color( colorRange.first().swatch->getColor() ); + PrimBuild::vertex2i( r, b ); + PrimBuild::vertex2i( r, t ); + + PrimBuild::end(); + } + else + { + PrimBuild::begin( GFXTriangleFan, 4 ); + + PrimBuild::color( colorRange.first().swatch->getColor() ); + PrimBuild::vertex2i( l, t ); + PrimBuild::vertex2i( l, b ); + + PrimBuild::color( colorRange.first().swatch->getColor() ); + PrimBuild::vertex2i( l + colorRange.first().swatch->getPosition().x, b ); + PrimBuild::vertex2i( l + colorRange.first().swatch->getPosition().x, t ); + + PrimBuild::end(); + + for( U16 i = 0;i < colorRange.size() - 1; i++ ) + { + PrimBuild::begin( GFXTriangleFan, 4 ); + if (!vertical) // Horizontal (+x) + { + // First color + PrimBuild::color( colorRange[i].swatch->getColor() ); + PrimBuild::vertex2i( l + colorRange[i].swatch->getPosition().x, t ); + PrimBuild::vertex2i( l + colorRange[i].swatch->getPosition().x, b ); + + // First color + PrimBuild::color( colorRange[i+1].swatch->getColor() ); + PrimBuild::vertex2i( l + colorRange[i+1].swatch->getPosition().x, b ); + PrimBuild::vertex2i( l + colorRange[i+1].swatch->getPosition().x, t ); + } + PrimBuild::end(); + } + + PrimBuild::begin( GFXTriangleFan, 4 ); + + PrimBuild::color( colorRange.last().swatch->getColor() ); + PrimBuild::vertex2i( l + colorRange.last().swatch->getPosition().x, t ); + PrimBuild::vertex2i( l + colorRange.last().swatch->getPosition().x, b ); + + PrimBuild::color( colorRange.last().swatch->getColor() ); + PrimBuild::vertex2i( r, b ); + PrimBuild::vertex2i( r, t ); + + PrimBuild::end(); + } +} + +void GuiGradientCtrl::onMouseDown(const GuiEvent &event) +{ + if (!mActive) + return; + + mouseLock(this); + + if (mProfile->mCanKeyFocus) + setFirstResponder(); + + if (mActive) + onAction(); + + Point2I extent = getRoot()->getExtent(); + Point2I resolution = getRoot()->getExtent(); + GFXTexHandle bb( resolution.x, + resolution.y, + GFXFormatR8G8B8A8, &GFXDefaultRenderTargetProfile, avar("%s() - bb (line %d)", __FUNCTION__, __LINE__) ); + + Point2I tmpPt( event.mousePoint.x, event.mousePoint.y ); + GFXTarget *targ = GFX->getActiveRenderTarget(); + targ->resolveTo( bb ); + GBitmap bmp( bb.getWidth(), bb.getHeight() ); + bb.copyToBmp( &bmp ); + ColorI tmp; + bmp.getColor( event.mousePoint.x, event.mousePoint.y, tmp ); + + addColorRange( globalToLocalCoord(event.mousePoint), ColorF(tmp) ); + + mMouseDown = true; +} + +void GuiGradientCtrl::onMouseUp(const GuiEvent &) +{ + //if we released the mouse within this control, perform the action + if (mActive && mMouseDown ) + mMouseDown = false; + + mouseUnlock(); +} + +void GuiGradientCtrl::onMouseEnter(const GuiEvent &event) +{ + mMouseOver = true; +} + +void GuiGradientCtrl::onMouseLeave(const GuiEvent &) +{ + // Reset state + mMouseOver = false; +} + +void GuiGradientCtrl::setupDefaultRange() +{ + S32 l = mBlendRangeBox.point.x - mSwatchFactor; + S32 r = mBlendRangeBox.extent.x - mSwatchFactor; + + //setup alpha range (white/black only) + ColorRange crW; + crW.pos = l; + crW.color = ColorI(255,255,255); + crW.swatch = NULL; + mAlphaRange.push_back( crW ); + + ColorRange crB; + crB.pos = r; + crB.color = ColorI(0,0,0); + crB.swatch = NULL; + mAlphaRange.push_back( crB ); + + //setup color range (only 1 color necessary) + ColorRange crD; + crD.pos = l; + crD.color = ColorI(255,0,0); + crD.swatch = NULL; + mColorRange.push_back( crD ); +} + +void GuiGradientCtrl::reInitSwatches( GuiGradientCtrl::PickMode ) +{ + //liable to crash in the guiEditor, needs fix + for( S32 i = 0;i < mColorRange.size(); i++ ) + { + if(mColorRange[i].swatch != NULL) + { + mColorRange[i].pos = mColorRange[i].swatch->getPosition().x; + mColorRange[i].color = mColorRange[i].swatch->getColor(); + mColorRange[i].swatch->deleteObject(); + mColorRange[i].swatch = NULL; + } + } + + for( S32 i = 0;i < mAlphaRange.size(); i++ ) + { + if(mAlphaRange[i].swatch != NULL) + { + mAlphaRange[i].pos = mAlphaRange[i].swatch->getPosition().x; + mAlphaRange[i].color = mAlphaRange[i].swatch->getColor(); + mAlphaRange[i].swatch->deleteObject(); + mAlphaRange[i].swatch = NULL; + } + } + + S32 b = mBlendRangeBox.extent.y - mSwatchFactor; + + if( mDisplayMode == pHorizColorRange ) + { + for( S32 i = 0;i < mColorRange.size(); i++ ) + { + mColorRange[i].swatch = new GuiGradientSwatchCtrl(); + mColorRange[i].swatch->registerObject(); + addObject(mColorRange[i].swatch); + mColorRange[i].swatch->setPosition( Point2I( mColorRange[i].pos, b ) );// needs to be adjusted + mColorRange[i].swatch->setColor(ColorF(mColorRange[i].color)); + } + } + + else if( mDisplayMode == pHorizAlphaRange ) + { + for( S32 i = 0;i < mAlphaRange.size(); i++ ) + { + mAlphaRange[i].swatch = new GuiGradientSwatchCtrl(); + mAlphaRange[i].swatch->registerObject(); + addObject(mAlphaRange[i].swatch); + mAlphaRange[i].swatch->setPosition( Point2I( mAlphaRange[i].pos, b ) );// needs to be adjusted + mAlphaRange[i].swatch->setColor(ColorF(mAlphaRange[i].color)); + } + } +} + +void GuiGradientCtrl::addColorRange( Point2I pos, ColorF color ) +{ + if( pos.x + mSwatchFactor < mBlendRangeBox.point.x && + pos.x + mSwatchFactor > mBlendRangeBox.extent.x ) + { + return; + } + + ColorRange range; + range.pos = pos.x - mSwatchFactor; + range.color = color; + + S32 b = mBlendRangeBox.extent.y - mSwatchFactor; + + range.swatch = new GuiGradientSwatchCtrl(); + range.swatch->registerObject(); + addObject( range.swatch ); + range.swatch->setPosition( pos.x - mSwatchFactor, b );//swatch factor and default location is going to have to be placed + range.swatch->setColor( color ); + + if( mDisplayMode == pHorizColorRange ) + { + mColorRange.push_back( range ); + S32 size = mColorRange.size(); + if( size > 0 ) + dQsort( mColorRange.address(), size, sizeof(ColorRange), _numIncreasing); + } + else if( mDisplayMode == pHorizAlphaRange ) + { + mAlphaRange.push_back( range ); + S32 size = mAlphaRange.size(); + if( size > 0 ) + dQsort( mAlphaRange.address(), size, sizeof(ColorRange), _numIncreasing); + } +} + +void GuiGradientCtrl::removeColorRange( GuiGradientSwatchCtrl* swatch ) +{ + if( mDisplayMode == pHorizColorRange ) + { + if( mColorRange.size() <= 1 ) + return; + + for( S32 i = 0;i < mColorRange.size(); i++ ) + { + if( mColorRange[i].swatch == swatch ) + { + mColorRange.erase( U32(i) ); + swatch->safeDeleteObject(); + break; + } + } + } + else if( mDisplayMode == pHorizAlphaRange ) + { + if( mAlphaRange.size() <= 1 ) + return; + + for( S32 i = 0;i < mAlphaRange.size(); i++ ) + { + if( mAlphaRange[i].swatch == swatch ) + { + mAlphaRange.erase( U32(i) ); + swatch->safeDeleteObject(); + break; + } + } + } +} + +void GuiGradientCtrl::sortColorRange() +{ + if( mDisplayMode == pHorizColorRange ) + dQsort( mColorRange.address(), mColorRange.size(), sizeof(ColorRange), _numIncreasing); + else if( mDisplayMode == pHorizAlphaRange ) + dQsort( mAlphaRange.address(), mAlphaRange.size(), sizeof(ColorRange), _numIncreasing); +} + +ConsoleMethod(GuiGradientCtrl, getColorCount, S32, 2, 2, "Get color count") +{ + if( object->getDisplayMode() == GuiGradientCtrl::pHorizColorRange ) + return object->mColorRange.size(); + else if( object->getDisplayMode() == GuiGradientCtrl::pHorizColorRange ) + return object->mColorRange.size(); + + return 0; +} + +ConsoleMethod(GuiGradientCtrl, getColor, const char*, 3, 3, "Get color value") +{ + S32 idx = dAtoi(argv[2]); + + if( object->getDisplayMode() == GuiGradientCtrl::pHorizColorRange ) + { + if ( idx >= 0 && idx < object->mColorRange.size() ) + { + char* rColor = Con::getReturnBuffer(256); + rColor[0] = 0; + + dSprintf(rColor, 256, "%f %f %f %f", + object->mColorRange[idx].swatch->getColor().red, + object->mColorRange[idx].swatch->getColor().green, + object->mColorRange[idx].swatch->getColor().blue, + object->mColorRange[idx].swatch->getColor().alpha); + + return rColor; + } + } + else if( object->getDisplayMode() == GuiGradientCtrl::pHorizColorRange ) + { + if ( idx >= 0 && idx < object->mAlphaRange.size() ) + { + char* rColor = Con::getReturnBuffer(256); + rColor[0] = 0; + + dSprintf(rColor, 256, "%f %f %f %f", + object->mAlphaRange[idx].swatch->getColor().red, + object->mAlphaRange[idx].swatch->getColor().green, + object->mAlphaRange[idx].swatch->getColor().blue, + object->mAlphaRange[idx].swatch->getColor().alpha); + + return rColor; + } + } + + return "1 1 1 1"; +} \ No newline at end of file diff --git a/Engine/source/gui/controls/guiGradientCtrl.h b/Engine/source/gui/controls/guiGradientCtrl.h new file mode 100644 index 000000000..8cbcd6a8b --- /dev/null +++ b/Engine/source/gui/controls/guiGradientCtrl.h @@ -0,0 +1,162 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _GUIGRADIENTCTRL_H_ +#define _GUIGRADIENTCTRL_H_ + +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif + +#ifndef _GUISWATCHBUTTONCTRL_H_ +#include "gui/buttons/guiSwatchButtonCtrl.h" +#endif + +class GuiGradientSwatchCtrl : public GuiSwatchButtonCtrl +{ +private: + typedef GuiSwatchButtonCtrl Parent; +private: + Point2I mMouseDownPosition; + RectI mOrigBounds; +public: + DECLARE_CONOBJECT(GuiGradientSwatchCtrl); + DECLARE_CALLBACK( void, onMouseDown, ()); + DECLARE_CALLBACK( void, onDoubleClick, ()); + GuiGradientSwatchCtrl(); + void onMouseDown(const GuiEvent &); + void onRightMouseDown(const GuiEvent &); + void onMouseDragged(const GuiEvent &event); + void onRender(Point2I offset, const RectI &updateRect); + bool onWake(); +protected: + GFXTexHandle mPointer; + StringTableEntry mColorFunction; +}; +//---------------------------- +/// GuiGradientCtrl + +class GuiGradientCtrl : public GuiControl +{ + typedef GuiControl Parent; + +public: + enum PickMode + { + pHorizColorRange, ///< We have a range of base colors going horizontally + pHorizAlphaRange, ///< We have a box which shows a range in alpha going horizontally + }; + + enum SelectorMode + { + sHorizontal = 0, ///< Horizontal selector with small gap + sVertical, ///< Vertical selector with small gap + }; + + struct ColorRange + { + GuiGradientSwatchCtrl* swatch; + S32 pos; + ColorF color; + }; + + Vector mColorRange; + Vector mAlphaRange; + S32 mSwatchFactor; + RectI mBlendRangeBox; + +private: + + /// @name Core Rendering functions + /// @{ + void renderColorBox(RectI &bounds); ///< Function that draws the actual color box + //void drawSelector(RectI &bounds, Point2I &selectorPos, SelectorMode mode); ///< Function that draws the selection indicator + void drawBlendRangeBox(RectI &bounds, bool vertical, Vector colorRange); + /// @} + + /// @name Core Variables + /// @{ + ColorF mPickColor; ///< Color that has been picked from control + ColorF mBaseColor; ///< Colour we display (in case of pallet and blend mode) + PickMode mDisplayMode; ///< Current color display mode of the selector + PickMode mSaveDisplayMode; + + bool mPositionChanged; ///< Current position has changed since last render? + bool mMouseOver; ///< Mouse is over? + bool mMouseDown; ///< Mouse button down? + bool mActionOnMove; ///< Perform onAction() when position has changed? + + GFXStateBlockRef mStateBlock; + + ColorF colorWhite; + ColorF colorWhiteBlend; + ColorF colorBlack; + ColorF colorAlpha; + ColorF colorAlphaW; + /// @} + String mColorFunction; + +public: + + DECLARE_CONOBJECT(GuiGradientCtrl); + DECLARE_CATEGORY( "Gui Editor" ); + + GuiGradientCtrl(); + + static void initPersistFields(); + void onRender(Point2I offset, const RectI &updateRect); + bool mShowReticle; ///< Show reticle on render + /// @name Color Value Functions + /// @{ + /// NOTE: setValue only sets baseColor, since setting pickColor wouldn't be useful + void setValue(ColorF &value) {mBaseColor = value;} + /// NOTE: getValue() returns baseColor if pallet (since pallet controls can't "pick" colours themselves) + ColorF getValue() {return mPickColor;} + void updateColor() {mPositionChanged = true;} + /// @} + + /// @name Input Events + /// @{ + void onMouseDown(const GuiEvent &); + void onMouseUp(const GuiEvent &); + + void onMouseEnter(const GuiEvent &); + void onMouseLeave(const GuiEvent &); + /// @} + + void addColorRange(ColorI color); + void setupDefaultRange(); + + bool onAdd(); + void inspectPreApply(); + void inspectPostApply(); + void reInitSwatches( GuiGradientCtrl::PickMode ); + void addColorRange( Point2I pos, ColorF color ); + void removeColorRange( GuiGradientSwatchCtrl* swatch ); + void sortColorRange(); + + PickMode getDisplayMode() { return mDisplayMode; } +}; + +typedef GuiGradientCtrl::PickMode GuiGradientPickMode; +DefineEnumType( GuiGradientPickMode ); + +#endif diff --git a/Engine/source/gui/controls/guiListBoxCtrl.cpp b/Engine/source/gui/controls/guiListBoxCtrl.cpp new file mode 100644 index 000000000..fda05f07c --- /dev/null +++ b/Engine/source/gui/controls/guiListBoxCtrl.cpp @@ -0,0 +1,1646 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#include "gui/controls/guiListBoxCtrl.h" +#include "gfx/gfxDrawUtil.h" +#include "console/engineAPI.h" + +IMPLEMENT_CONOBJECT(GuiListBoxCtrl); + +ConsoleDocClass( GuiListBoxCtrl, + "@brief A list of text items.\n\n" + + "A list of text items where each individual entry can have its own text value, text color and associated SimObject.\n\n" + + "@tsexample\n" + "new GuiListBoxCtrl(GuiMusicPlayerMusicList)\n" + "{\n" + " allowMultipleSelections = \"true\";\n" + " fitParentWidth = \"true\";\n" + " mirrorSet = \"AnotherGuiListBoxCtrl\";\n" + " makeNameCallback = \"\";\n" + " colorBullet = \"1\";\n" + " //Properties not specific to this control have been omitted from this example.\n" + "};\n" + "@endtsexample\n\n" + + "@see GuiControl\n\n" + + "@ingroup GuiCore\n" +); + +IMPLEMENT_CALLBACK( GuiListBoxCtrl, onMouseDragged, void, (),(), + "@brief Called whenever the mouse is dragged across the control.\n\n" + "@tsexample\n" + "// Mouse is dragged across the control, causing the callback to occur.\n" + "GuiListBoxCtrl::onMouseDragged(%this)\n" + " {\n" + " // Code to run whenever the mouse is dragged across the control\n" + " }\n" + "@endtsexample\n\n" + "@see GuiControl\n\n" +); + +IMPLEMENT_CALLBACK( GuiListBoxCtrl, onClearSelection, void, (),(), + "@brief Called whenever a selected item in the list is cleared.\n\n" + "@tsexample\n" + "// A selected item is cleared, causing the callback to occur.\n" + "GuiListBoxCtrl::onClearSelection(%this)\n" + " {\n" + " // Code to run whenever a selected item is cleared\n" + " }\n" + "@endtsexample\n\n" + "@see GuiControl\n\n" +); + +IMPLEMENT_CALLBACK( GuiListBoxCtrl, onUnSelect, void, ( const char* index, const char* itemText),( index, itemText ), + "@brief Called whenever a selected item in the list has been unselected.\n\n" + "@param index Index id of the item that was unselected\n" + "@param itemText Text for the list entry at the index id that was unselected\n\n" + "@tsexample\n" + "// A selected item is unselected, causing the callback to occur\n" + "GuiListBoxCtrl::onUnSelect(%this, %indexId, %itemText)\n" + " {\n" + " // Code to run whenever a selected list item is unselected\n" + " }\n" + "@endtsexample\n\n" + "@see GuiControl\n\n" +); + +IMPLEMENT_CALLBACK( GuiListBoxCtrl, onSelect, void, ( const char* index , const char* itemText ),( index, itemText ), + "@brief Called whenever an item in the list is selected.\n\n" + "@param index Index id for the item in the list that was selected.\n" + "@param itemText Text for the list item at the index that was selected.\n\n" + "@tsexample\n" + "// An item in the list is selected, causing the callback to occur\n" + "GuiListBoxCtrl::onSelect(%this, %index, %itemText)\n" + " {\n" + " // Code to run whenever an item in the list is selected\n" + " }\n" + "@endtsexample\n\n" + "@see GuiControl\n\n" +); + +IMPLEMENT_CALLBACK( GuiListBoxCtrl, onDoubleClick, void, (),(), + "@brief Called whenever an item in the list has been double clicked.\n\n" + "@tsexample\n" + "// An item in the list is double clicked, causing the callback to occur.\n" + "GuiListBoxCtrl::onDoubleClick(%this)\n" + " {\n" + " // Code to run whenever an item in the control has been double clicked\n" + " }\n" + "@endtsexample\n\n" + "@see GuiControl\n\n" +); + +IMPLEMENT_CALLBACK( GuiListBoxCtrl, onMouseUp, void, (const char* itemHit, const char* mouseClickCount),( itemHit,mouseClickCount), + "@brief Called whenever the mouse has previously been clicked down (onMouseDown) and has now been raised on the control.\n" + "If an item in the list was hit during the click cycle, then the index id of the clicked object along with how many clicks occured are passed\n" + "into the callback.\n\n" + "Detailed description\n\n" + "@param itemHit Index id for the list item that was hit\n" + "@param mouseClickCount How many mouse clicks occured on this list item\n\n" + "@tsexample\n" + "// Mouse was previously clicked down, and now has been released, causing the callback to occur.\n" + "GuiListBoxCtrl::onMouseUp(%this, %itemHit, %mouseClickCount)\n" + " {\n" + " // Code to call whenever the mouse has been clicked and released on the control\n" + " }\n" + "@endtsexample\n\n" + "@see GuiControl\n\n" +); + +IMPLEMENT_CALLBACK( GuiListBoxCtrl, onDeleteKey, void, (),(), + "@brief Called whenever the Delete key on the keyboard has been pressed while in this control.\n\n" + "@tsexample\n" + "// The delete key on the keyboard has been pressed while this control is in focus, causing the callback to occur.\n" + "GuiListBoxCtrl::onDeleteKey(%this)\n" + " {\n" + " // Code to call whenever the delete key is pressed\n" + " }\n" + "@endtsexample\n\n" + "@see GuiControl\n\n" +); + +IMPLEMENT_CALLBACK( GuiListBoxCtrl, isObjectMirrored, bool, ( const char* indexIdString ),( indexIdString ), + "@brief Checks if a list item at a defined index id is mirrored, and returns the result.\n\n" + "@param indexIdString Index id of the list to check.\n" + "@tsexample\n" + "// Engine has requested of the script level to determine if a list entry is mirrored or not.\n" + "GuiListBoxCtrl::isObjectMirrored(%this, %indexIdString)\n" + " {\n" + " // Perform code required to check and see if the list item at the index id is mirrored or not.\n" + " return %isMirrored;\n" + " }\n" + "@endtsexample\n\n" + "@return A boolean value on if the list item is mirrored or not.\n\n" + "@see GuiControl\n\n" +); + + +GuiListBoxCtrl::GuiListBoxCtrl() +{ + mItems.clear(); + mSelectedItems.clear(); + mMultipleSelections = true; + mFitParentWidth = true; + mColorBullet = true; + mItemSize = Point2I(10,20); + mLastClickItem = NULL; + + mRenderTooltipDelegate.bind( this, &GuiListBoxCtrl::renderTooltip ); +} + +GuiListBoxCtrl::~GuiListBoxCtrl() +{ + clearItems(); +} + +void GuiListBoxCtrl::initPersistFields() +{ + addField( "allowMultipleSelections", TypeBool, Offset( mMultipleSelections, GuiListBoxCtrl), "If true, will allow the selection of multiple items in the listbox.\n"); + addField( "fitParentWidth", TypeBool, Offset( mFitParentWidth, GuiListBoxCtrl), "If true, the width of the listbox will match the width of its parent control.\n"); + addField( "colorBullet", TypeBool, Offset( mColorBullet, GuiListBoxCtrl), "If true, colored items will render a colored rectangular bullet next to the item text.\n"); + + addField( "mirrorSet", TypeRealString, Offset( mMirrorSetName, GuiListBoxCtrl ), "If populated with the name of another GuiListBoxCtrl, then this list box will mirror the contents of the mirrorSet listbox.\n"); + addField( "makeNameCallback", TypeRealString, Offset( mMakeNameCallback, GuiListBoxCtrl ), "A script snippet to control what is displayed in the list for a SimObject. Within this snippet, $ThisControl is bound to the guiListBoxCtrl and $ThisObject to the contained object in question.\n"); + + Parent::initPersistFields(); +} + +bool GuiListBoxCtrl::onWake() +{ + if( !Parent::onWake() ) + return false; + + updateSize(); + + return true; +} + +//----------------------------------------------------------------------------- +// Item Accessors +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiListBoxCtrl, setMultipleSelection, void, (bool allowMultSelections),, + "@brief Enable or disable multiple selections for this GuiListBoxCtrl object.\n\n" + "@param allowMultSelections Boolean variable to set the use of multiple selections or not.\n" + "@tsexample\n" + "// Define the multiple selection use state.\n" + "%allowMultSelections = \"true\";\n\n" + "// Set the allow multiple selection state on the GuiListBoxCtrl object.\n" + "%thisGuiListBoxCtrl.setMultipleSelection(%allowMultSelections);\n" + "@endtsexample\n\n" + "@see GuiControl\n") +{ + object->setMultipleSelection( allowMultSelections ); +} + +DefineEngineMethod( GuiListBoxCtrl, clearItems, void, (),, + "@brief Clears all the items in the listbox.\n\n" + "@tsexample\n" + "// Inform the GuiListBoxCtrl object to clear all items from its list.\n" + "%thisGuiListBoxCtrl.clearItems();\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->clearItems(); +} + +void GuiListBoxCtrl::clearItems() +{ + // Free item list allocated memory + while( mItems.size() ) + deleteItem( 0 ); + + // Free our vector lists + mItems.clear(); + mSelectedItems.clear(); + mFilteredItems.clear(); +} + +DefineEngineMethod( GuiListBoxCtrl, clearSelection, void, (),, + "@brief Sets all currently selected items to unselected.\n\n" + "Detailed description\n\n" + "@tsexample\n" + "// Inform the GuiListBoxCtrl object to set all of its items to unselected./n" + "%thisGuiListBoxCtrl.clearSelection();\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->clearSelection(); +} + +void GuiListBoxCtrl::clearSelection() +{ + if( !mSelectedItems.size() ) + return; + + VectorPtr::iterator i = mSelectedItems.begin(); + for( ; i != mSelectedItems.end(); i++ ) + (*i)->isSelected = false; + + mSelectedItems.clear(); + + onClearSelection_callback(); +} + +DefineEngineMethod( GuiListBoxCtrl, setSelected, void, (S32 index, bool setSelected), (true), + "@brief Sets the item at the index specified to selected or not.\n\n" + "Detailed description\n\n" + "@param index Item index to set selected or unselected.\n" + "@param setSelected Boolean selection state to set the requested item index.\n" + "@tsexample\n" + "// Define the index\n" + "%index = \"5\";\n\n" + "// Define the selection state\n" + "%selected = \"true\"\n\n" + "// Inform the GuiListBoxCtrl object of the new selection state for the requested index entry.\n" + "%thisGuiListBoxCtrl.setSelected(%index,%selected);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + if( setSelected == true ) + object->addSelection( index ); + else + object->removeSelection( index ); +} + +void GuiListBoxCtrl::removeSelection( S32 index ) +{ + // Range Check + if( index >= mItems.size() || index < 0 ) + { + Con::warnf("GuiListBoxCtrl::removeSelection - index out of range!" ); + return; + } + + removeSelection( mItems[index], index ); +} +void GuiListBoxCtrl::removeSelection( LBItem *item, S32 index ) +{ + if( !mSelectedItems.size() ) + return; + + if( !item ) + return; + + for( S32 i = 0 ; i < mSelectedItems.size(); i++ ) + { + if( mSelectedItems[i] == item ) + { + mSelectedItems.erase( &mSelectedItems[i] ); + item->isSelected = false; + onUnSelect_callback(Con::getIntArg(index), item->itemText); + return; + } + } +} + +void GuiListBoxCtrl::addSelection( S32 index ) +{ + // Range Check + if( index >= mItems.size() || index < 0 ) + { + Con::warnf("GuiListBoxCtrl::addSelection- index out of range!" ); + return; + } + + addSelection( mItems[index], index ); + +} +void GuiListBoxCtrl::addSelection( LBItem *item, S32 index ) +{ + if( !mMultipleSelections ) + { + if( !mSelectedItems.empty() ) + { + LBItem* selItem = mSelectedItems.front(); + if( selItem != item ) + clearSelection(); + else + return; + } + } + else + { + if( !mSelectedItems.empty() ) + { + for( S32 i = 0; i < mSelectedItems.size(); i++ ) + { + if( mSelectedItems[ i ] == item ) + return; + } + } + } + + item->isSelected = true; + mSelectedItems.push_front( item ); + + onSelect_callback(Con::getIntArg( index ), item->itemText); +} + +S32 GuiListBoxCtrl::getItemIndex( LBItem *item ) +{ + if( mItems.empty() ) + return -1; + + // Lookup the index of an item in our list, by the pointer to the item + for( S32 i = 0; i < mItems.size(); i++ ) + if( mItems[i] == item ) + return i; + + return -1; +} + +DefineEngineMethod( GuiListBoxCtrl, getItemCount, S32, (),, + "@brief Returns the number of items in the list.\n\n" + "@tsexample\n" + "// Request the number of items in the list of the GuiListBoxCtrl object.\n" + "%listItemCount = %thisGuiListBoxCtrl.getItemCount();\n" + "@endtsexample\n\n" + "@return The number of items in the list.\n\n" + "@see GuiControl") +{ + return object->getItemCount(); +} + +S32 GuiListBoxCtrl::getItemCount() +{ + return mItems.size(); +} + +DefineEngineMethod( GuiListBoxCtrl, getSelCount, S32, (),, + "@brief Returns the number of items currently selected.\n\n" + "@tsexample\n" + "// Request the number of currently selected items\n" + "%selectedItemCount = %thisGuiListBoxCtrl.getSelCount();\n" + "@endtsexample\n\n" + "@return Number of currently selected items.\n\n" + "@see GuiControl") +{ + return object->getSelCount(); +} + +S32 GuiListBoxCtrl::getSelCount() +{ + return mSelectedItems.size(); +} + +DefineEngineMethod( GuiListBoxCtrl, getSelectedItem, S32, (),, + "@brief Returns the selected items index or -1 if none selected. If multiple selections exist it returns the first selected item. \n\n" + "@tsexample\n" + "// Request the index id of the currently selected item\n" + "%selectedItemId = %thisGuiListBoxCtrl.getSelectedItem();\n" + "@endtsexample\n\n" + "@return The selected items index or -1 if none selected.\n\n" + "@see GuiControl") +{ + return object->getSelectedItem(); +} + +S32 GuiListBoxCtrl::getSelectedItem() +{ + if( mSelectedItems.empty() || mItems.empty() ) + return -1; + + for( S32 i = 0 ; i < mItems.size(); i++ ) + if( mItems[i]->isSelected ) + return i; + + return -1; +} + +DefineEngineMethod( GuiListBoxCtrl, getSelectedItems, const char*, (),, + "@brief Returns a space delimited list of the selected items indexes in the list.\n\n" + "@tsexample\n" + "// Request a space delimited list of the items in the GuiListBoxCtrl object.\n" + "%selectionList = %thisGuiListBoxCtrl.getSelectedItems();\n" + "@endtsexample\n\n" + "@return Space delimited list of the selected items indexes in the list\n\n" + "@see GuiControl") +{ + S32 selCount = object->getSelCount(); + if( selCount == -1 || selCount == 0 ) + return StringTable->lookup("-1"); + else if( selCount == 1 ) + return Con::getIntArg(object->getSelectedItem()); + + Vector selItems; + object->getSelectedItems( selItems ); + + if( selItems.empty() ) + return StringTable->lookup("-1"); + + UTF8 *retBuffer = Con::getReturnBuffer( selItems.size() * 4 ); + dMemset( retBuffer, 0, selItems.size() * 4 ); + Vector::iterator i = selItems.begin(); + for( ; i != selItems.end(); i++ ) + { + UTF8 retFormat[12]; + dSprintf( retFormat, 12, "%d ", (*i) ); + dStrcat( retBuffer, retFormat ); + } + + return retBuffer; +} + +void GuiListBoxCtrl::getSelectedItems( Vector &Items ) +{ + // Clear our return vector + Items.clear(); + + // If there are no selected items, return an empty vector + if( mSelectedItems.empty() ) + return; + + for( S32 i = 0; i < mItems.size(); i++ ) + if( mItems[i]->isSelected ) + Items.push_back( i ); +} + +DefineEngineMethod( GuiListBoxCtrl, findItemText, S32, (const char* findText, bool bCaseSensitive), (false), + "@brief Returns index of item with matching text or -1 if none found.\n\n" + "@param findText Text in the list to find.\n" + "@param isCaseSensitive If true, the search will be case sensitive.\n" + "@tsexample\n" + "// Define the text we wish to find in the list.\n" + "%findText = \"Hickory Smoked Gideon\"/n/n" + "// Define if this is a case sensitive search or not.\n" + "%isCaseSensitive = \"false\";\n\n" + "// Ask the GuiListBoxCtrl object what item id in the list matches the requested text.\n" + "%matchingId = %thisGuiListBoxCtrl.findItemText(%findText,%isCaseSensitive);\n" + "@endtsexample\n\n" + "@return Index id of item with matching text or -1 if none found.\n\n" + "@see GuiControl") +{ + return object->findItemText( findText, bCaseSensitive ); +} + +S32 GuiListBoxCtrl::findItemText( StringTableEntry text, bool caseSensitive ) +{ + // Check Proper Arguments + if( !text || !text[0] || text == StringTable->lookup("") ) + { + Con::warnf("GuiListBoxCtrl::findItemText - No Text Specified!"); + return -1; + } + + // Check Items Exist. + if( mItems.empty() ) + return -1; + + // Lookup the index of an item in our list, by the pointer to the item + for( S32 i = 0; i < mItems.size(); i++ ) + { + // Case Sensitive Compare? + if( caseSensitive && ( dStrcmp( mItems[i]->itemText, text ) == 0 ) ) + return i; + else if (!caseSensitive && ( dStricmp( mItems[i]->itemText, text ) == 0 )) + return i; + } + + // Not Found! + return -1; +} + +DefineEngineMethod( GuiListBoxCtrl, setCurSel, void, (S32 indexId),, + "@brief Sets the currently selected item at the specified index.\n\n" + "@param indexId Index Id to set selected.\n" + "@tsexample\n" + "// Define the index id that we wish to select.\n" + "%selectId = \"4\";\n\n" + "// Inform the GuiListBoxCtrl object to set the requested index as selected.\n" + "%thisGuiListBoxCtrl.setCurSel(%selectId);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->setCurSel( indexId ); +} +void GuiListBoxCtrl::setCurSel( S32 index ) +{ + // Range Check + if( index >= mItems.size() ) + { + Con::warnf("GuiListBoxCtrl::setCurSel - index out of range!" ); + return; + } + + // If index -1 is specified, we clear the selection + if( index == -1 ) + { + mSelectedItems.clear(); + return; + } + + // Add the selection + addSelection( mItems[ index ], index ); + +} + +DefineEngineMethod( GuiListBoxCtrl, setCurSelRange, void, (S32 indexStart, S32 indexStop), (999999), + "@brief Sets the current selection range from index start to stop. If no stop is specified it sets from start index to the end of the list\n\n" + "@param indexStart Index Id to start selection.\n" + "@param indexStop Index Id to end selection.\n" + "@tsexample\n" + "// Set start id\n" + "%indexStart = \"3\";\n\n" + "// Set end id\n" + "%indexEnd = \"6\";\n\n" + "// Request the GuiListBoxCtrl object to select the defined range.\n" + "%thisGuiListBoxCtrl.setCurSelRange(%indexStart,%indexEnd);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->setCurSelRange( indexStart , indexStop ); +} + +void GuiListBoxCtrl::setCurSelRange( S32 start, S32 stop ) +{ + // Verify Selection Range + if( start < 0 ) + start = 0; + else if( start > mItems.size() ) + start = mItems.size(); + + if( stop < 0 ) + stop = 0; + else if( stop > mItems.size() ) + stop = mItems.size(); + + S32 iterStart = ( start < stop ) ? start : stop; + S32 iterStop = ( start < stop ) ? stop : start; + + for( ; iterStart <= iterStop; iterStart++ ) + addSelection( mItems[iterStart], iterStart ); +} + +DefineEngineMethod( GuiListBoxCtrl, addItem, S32, (const char* newItem, const char* color), ( "" ), + "@brief Adds an item to the end of the list with an optional color.\n\n" + "@param newItem New item to add to the list.\n" + "@param color Optional color parameter to add to the new item.\n" + "@tsexample\n" + "// Define the item to add to the list.\n" + "%newItem = \"Gideon's Blue Coat\";\n\n" + "// Define the optional color for the new list item.\n" + "%color = \"0.0 0.0 1.0\";\n\n" + "// Inform the GuiListBoxCtrl object to add the item to the end of the list with the defined color.\n" + "%thisGuiListBoxCtrl.addItem(%newItem,%color);\n" + "@endtsexample\n\n" + "@return If not void, return value and description\n\n" + "@see GuiControl\n" + "@hide") +{ + if(dStricmp(color,"") == 0) + { + return object->addItem( newItem ); + } + else + { + U32 elementCount = GuiListBoxCtrl::getStringElementCount(color); + + if(elementCount == 3) + { + F32 red, green, blue; + + red = dAtof(GuiListBoxCtrl::getStringElement( color, 0 )); + green = dAtof(GuiListBoxCtrl::getStringElement( color, 1 )); + blue = dAtof(GuiListBoxCtrl::getStringElement( color, 2 )); + + return object->addItemWithColor( newItem, ColorF(red, green, blue) ); + } + else if(elementCount == 1) + { + U32 objId = dAtoi( color ); + return object->addItem( newItem, (void*)objId ); + } + else + { + Con::warnf("GuiListBoxCtrl::addItem() - Invalid number of parameters for the color!"); + return -1; + } + } +} + + static ConsoleDocFragment sGuiControlSetExtent1( + "@brief Adds an item to the control with the specific text.\n\n" + "@param text Text item to add to the list.\n" + "GuiListBoxCtrl", // The class to place the method in; use NULL for functions. + "void addItem( const char* text );" ); + + +S32 GuiListBoxCtrl::addItem( StringTableEntry text, void *itemData ) +{ + // This just calls insert item at the end of the list + return insertItem( mItems.size(), text, itemData ); +} + +S32 GuiListBoxCtrl::addItemWithColor( StringTableEntry text, ColorF color, void *itemData ) +{ + // This just calls insert item at the end of the list + return insertItemWithColor( mItems.size(), text, color, itemData ); +} + +DefineEngineMethod( GuiListBoxCtrl, setItemColor, void, (S32 index, ColorF color),, + "@brief Sets the color of a single list entry at the specified index id.\n\n" + "@param index Index id to modify the color of in the list.\n" + "@param color Color value to set the list entry to.\n" + "@tsexample\n" + "// Define the index id value\n" + "%index = \"5\";\n\n" + "// Define the color value\n" + "%color = \"1.0 0.0 0.0\";\n\n" + "// Inform the GuiListBoxCtrl object to change the color of the requested index\n" + "%thisGuiListBoxCtrl.setItemColor(%index,%color);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->setItemColor( index, color ); +} + +void GuiListBoxCtrl::setItemColor( S32 index, ColorF color ) +{ + if ((index >= mItems.size()) || index < 0) + { + Con::warnf("GuiListBoxCtrl::setItemColor - invalid index"); + return; + } + + LBItem* item = mItems[index]; + item->hasColor = true; + item->color = color; +} + +DefineEngineMethod( GuiListBoxCtrl, clearItemColor, void, (S32 index),, + "@brief Removes any custom coloring from an item at the defined index id in the list.\n\n" + "@param index Index id for the item to clear any custom color from.\n" + "@tsexample\n" + "// Define the index id\n" + "%index = \"4\";\n\n" + "// Request the GuiListBoxCtrl object to remove any custom coloring from the defined index entry\n" + "%thisGuiListBoxCtrl.clearItemColor(%index);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->clearItemColor(index); +} + +void GuiListBoxCtrl::clearItemColor( S32 index ) +{ + if ((index >= mItems.size()) || index < 0) + { + Con::warnf("GuiListBoxCtrl::setItemColor - invalid index"); + return; + } + + LBItem* item = mItems[index]; + item->hasColor = false; +} + +DefineEngineMethod( GuiListBoxCtrl, insertItem, void, (const char* text, S32 index),, + "@brief Inserts an item into the list at the specified index and returns the index assigned or -1 on error.\n\n" + "@param text Text item to add.\n" + "@param index Index id to insert the list item text at.\n" + "@tsexample\n" + "// Define the text to insert\n" + "%text = \"Secret Agent Gideon\";\n\n" + "// Define the index entry to insert the text at\n" + "%index = \"14\";\n\n" + "// In form the GuiListBoxCtrl object to insert the text at the defined index.\n" + "%assignedId = %thisGuiListBoxCtrl.insertItem(%text,%index);\n" + "@endtsexample\n\n" + "@return If successful will return the index id assigned. If unsuccessful, will return -1.\n\n" + "@see GuiControl") +{ + object->insertItem( index, text ); +} + +S32 GuiListBoxCtrl::insertItem( S32 index, StringTableEntry text, void *itemData ) +{ + // If the index is greater than our list size, insert it at the end + if( index >= mItems.size() ) + index = mItems.size(); + + // Sanity checking + if( !text ) + { + Con::warnf("GuiListBoxCtrl::insertItem - cannot add NULL string" ); + return -1; + } + + LBItem *newItem = new LBItem; + if( !newItem ) + { + Con::warnf("GuiListBoxCtrl::insertItem - error allocating item memory!" ); + return -1; + } + + // Assign item data + newItem->itemText = StringTable->insert(text, true); + newItem->itemData = itemData; + newItem->isSelected = false; + newItem->hasColor = false; + + // Add to list + mItems.insert(index); + mItems[index] = newItem; + + // Resize our list to fit our items + updateSize(); + + // Return our index in list (last) + return index; + +} + +S32 GuiListBoxCtrl::insertItemWithColor( S32 index, StringTableEntry text, ColorF color, void *itemData ) +{ + // If the index is greater than our list size, insert it at the end + if( index >= mItems.size() ) + index = mItems.size(); + + // Sanity checking + if( !text ) + { + Con::warnf("GuiListBoxCtrl::insertItem - cannot add NULL string" ); + return -1; + } + + if( color == ColorF(-1, -1, -1) ) + { + Con::warnf("GuiListBoxCtrl::insertItem - cannot add NULL color" ); + return -1; + } + + LBItem *newItem = new LBItem; + if( !newItem ) + { + Con::warnf("GuiListBoxCtrl::insertItem - error allocating item memory!" ); + return -1; + } + + // Assign item data + newItem->itemText = StringTable->insert(text, true); + newItem->itemData = itemData; + newItem->isSelected = false; + newItem->hasColor = true; + newItem->color = color; + + // Add to list + mItems.insert(index); + mItems[index] = newItem; + + // Resize our list to fit our items + updateSize(); + + // Return our index in list (last) + return index; + +} + +DefineEngineMethod( GuiListBoxCtrl, deleteItem, void, (S32 itemIndex),, + "@brief Removes the list entry at the requested index id from the control and clears the memory associated with it.\n\n" + "@param itemIndex Index id location to remove the item from.\n" + "@tsexample\n" + "// Define the index id we want to remove from the list\n" + "%itemIndex = \"8\";\n\n" + "// Inform the GuiListBoxCtrl object to remove the item at the defined index id.\n" + "%thisGuiListBoxCtrl.deleteItem(%itemIndex);\n" + "@endtsexample\n\n" + "@see References") +{ + object->deleteItem( itemIndex ); +} + +void GuiListBoxCtrl::deleteItem( S32 index ) +{ + // Range Check + if( index >= mItems.size() || index < 0 ) + { + Con::warnf("GuiListBoxCtrl::deleteItem - index out of range!" ); + return; + } + + // Grab our item + LBItem* item = mItems[ index ]; + if( !item ) + { + Con::warnf("GuiListBoxCtrl::deleteItem - Bad Item Data!" ); + return; + } + + // Remove it from the selected list. + if( item->isSelected ) + { + for( VectorPtr::iterator i = mSelectedItems.begin(); i != mSelectedItems.end(); i++ ) + { + if( item == *i ) + { + mSelectedItems.erase_fast( i ); + break; + } + } + } + + // Remove it from the list + mItems.erase( &mItems[ index ] ); + + // Free the memory associated with it + delete item; +} + +DefineEngineMethod( GuiListBoxCtrl, getItemText, const char*, (S32 index),, + "@brief Returns the text of the item at the specified index.\n\n" + "@param index Index id to return the item text from.\n" + "@tsexample\n" + "// Define the index id entry to request the text from\n" + "%index = \"12\";\n\n" + "// Request the item id text from the GuiListBoxCtrl object.\n" + "%text = %thisGuiListBoxCtrl.getItemText(%index);\n" + "@endtsexample\n\n" + "@return The text of the requested index id.\n\n" + "@see GuiControl") +{ + return object->getItemText( index ); +} + +StringTableEntry GuiListBoxCtrl::getItemText( S32 index ) +{ + // Range Checking + if( index > mItems.size() || index < 0 ) + { + Con::warnf( "GuiListBoxCtrl::getItemText - index out of range!" ); + return StringTable->lookup(""); + } + + return mItems[ index ]->itemText; +} + +DefineEngineMethod( GuiListBoxCtrl, getItemObject, const char*, (S32 index),, + "@brief Returns the object associated with an item. This only makes sense if you are mirroring a simset.\n\n" + "@param index Index id to request the associated item from.\n" + "@tsexample\n" + "// Define the index id\n" + "%index = \"12\";\n\n" + "// Request the item from the GuiListBoxCtrl object\n" + "%object = %thisGuiListBoxCtrl.getItemObject(%index);\n" + "@endtsexample\n\n" + "@return The object associated with the item in the list.\n\n" + "@see References") +{ + SimObject *outObj = object->getItemObject( index ); + if ( !outObj ) + return NULL; + + return outObj->getIdString(); +} + +SimObject* GuiListBoxCtrl::getItemObject( S32 index ) +{ + // Range Checking + if( index > mItems.size() || index < 0 ) + { + Con::warnf( "GuiListBoxCtrl::getItemObject - index out of range!" ); + return NULL; + } + + SimObject *outObj; + Sim::findObject( (SimObjectId)(mItems[ index ]->itemData), outObj ); + + return outObj; +} + +DefineEngineMethod( GuiListBoxCtrl, setItemText, void, (S32 index, const char* newtext),, + "@brief Sets the items text at the specified index.\n\n" + "@param index Index id to set the item text at.\n" + "@param newtext Text to change the list item at index id to.\n" + "@tsexample\n" + "// Define the index id/n" + "%index = \"12\";\n\n" + "// Define the text to set the list item to\n" + "%newtext = \"Gideon's Fancy Goggles\";\n\n" + "// Inform the GuiListBoxCtrl object to change the text at the requested index\n" + "%thisGuiListBoxCtrl.setItemText(%index,%newText);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->setItemText(index, newtext ); +} + +void GuiListBoxCtrl::setItemText( S32 index, StringTableEntry text ) +{ + // Sanity Checking + if( !text ) + { + Con::warnf("GuiListBoxCtrl::setItemText - Invalid Text Specified!" ); + return; + } + // Range Checking + if( index > mItems.size() || index < 0 ) + { + Con::warnf( "GuiListBoxCtrl::getItemText - index out of range!" ); + return; + } + + mItems[ index ]->itemText = StringTable->insert( text, true ); +} + +DefineEngineMethod( GuiListBoxCtrl, setItemTooltip, void, (S32 index, const char* text),, + "@brief Set the tooltip text to display for the given list item.\n\n" + "@param index Index id to change the tooltip text\n" + "@param text Text for the tooltip.\n" + "@tsexample\n" + "// Define the index id\n" + "%index = \"12\";\n\n" + "// Define the tooltip text\n" + "%tooltip = \"Gideon's goggles can see through space and time.\"\n\n" + "// Inform the GuiListBoxCtrl object to set the tooltop for the item at the defined index id\n" + "%thisGuiListBoxCtrl.setItemToolTip(%index,%tooltip);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + if( index > object->mItems.size() || index < 0 ) + { + Con::errorf( "GuiListBoxCtrl::setItemTooltip - index '%i' out of range", index ); + return; + } + + object->mItems[ index ]->itemTooltip = text; +} + +DefineEngineMethod( GuiListBoxCtrl, getLastClickItem, S32, (),, + "@brief Request the item index for the item that was last clicked.\n\n" + "@tsexample\n" + "// Request the item index for the last clicked item in the list\n" + "%lastClickedIndex = %thisGuiListBoxCtrl.getLastClickItem();\n" + "@endtsexample\n\n" + "@return Index id for the last clicked item in the list.\n\n" + "@see GuiControl") +{ + GuiListBoxCtrl::LBItem *lastItem = object->mLastClickItem; + if ( !lastItem ) + return -1; + + return object->getItemIndex( lastItem ); +} + +//----------------------------------------------------------------------------- +// Sizing Functions +//----------------------------------------------------------------------------- +void GuiListBoxCtrl::updateSize() +{ + if( !mProfile || !mProfile->mFont ) + return; + + GFont *font = mProfile->mFont; + GuiScrollCtrl* parent = dynamic_cast(getParent()); + + if ( mFitParentWidth && parent ) + mItemSize.x = parent->getContentExtent().x; + else + { + // Find the maximum width cell: + S32 maxWidth = 1; + for ( U32 i = 0; i < mItems.size(); i++ ) + { + S32 width = font->getStrWidth( mItems[i]->itemText ); + if( width > maxWidth ) + maxWidth = width; + } + mItemSize.x = maxWidth + 6; + } + + mItemSize.y = font->getHeight() + 2; + + Point2I newExtent( mItemSize.x, mItemSize.y * mItems.size() ); + setExtent( newExtent ); + +} + +void GuiListBoxCtrl::parentResized(const RectI &oldParentRect, const RectI &newParentRect) +{ + Parent::parentResized( oldParentRect, newParentRect ); + + updateSize(); +} + +//----------------------------------------------------------------------------- +// Overrides +//----------------------------------------------------------------------------- +void GuiListBoxCtrl::onRender( Point2I offset, const RectI &updateRect ) +{ + RectI clipRect(updateRect.point, updateRect.extent); + + if( !mProfile ) + return; + + _mirror(); + + // Save our original clip rect + RectI oldClipRect = clipRect; + + for ( S32 i = 0; i < mItems.size(); i++) + { + S32 colorBoxSize = 0; + ColorI boxColor = ColorI(0, 0, 0); + // Only render visible items + if ((i + 1) * mItemSize.y + offset.y < updateRect.point.y) + continue; + + // Break our once we're no longer in visible item range + if( i * mItemSize.y + offset.y >= updateRect.point.y + updateRect.extent.y) + break; + + // Render color box if needed + if(mColorBullet && mItems[i]->hasColor) + { + // Set the size of the color box to be drawn next to the item text + colorBoxSize = 3; + boxColor = ColorI(mItems[i]->color); + // Draw the box first + ColorI black = ColorI(0, 0, 0); + drawBox( Point2I(offset.x + mProfile->mTextOffset.x + colorBoxSize, offset.y + ( i * mItemSize.y ) + 8), colorBoxSize, black, boxColor ); + } + + RectI itemRect = RectI( offset.x + mProfile->mTextOffset.x + (colorBoxSize * 3), offset.y + ( i * mItemSize.y ), mItemSize.x, mItemSize.y ); + + // Render our item + onRenderItem( itemRect, mItems[i] ); + } + + GFX->setClipRect( oldClipRect ); +} + +void GuiListBoxCtrl::onRenderItem( RectI itemRect, LBItem *item ) +{ + if( item->isSelected ) + GFX->getDrawUtil()->drawRectFill( itemRect, mProfile->mFillColorSEL ); + + GFX->getDrawUtil()->setBitmapModulation( item->hasColor ? (ColorI)item->color : mProfile->mFontColor); + renderJustifiedText(itemRect.point + Point2I( 2, 0 ), itemRect.extent, item->itemText); +} + +void GuiListBoxCtrl::drawBox(const Point2I &box, S32 size, ColorI &outlineColor, ColorI &boxColor) +{ + RectI r(box.x - size, box.y - size, 2 * size + 1, 2 * size + 1); + r.inset(1, 1); + GFX->getDrawUtil()->drawRectFill(r, boxColor); + r.inset(-1, -1); + GFX->getDrawUtil()->drawRect(r, outlineColor); +} + +bool GuiListBoxCtrl::renderTooltip( const Point2I &hoverPos, const Point2I& cursorPos, const char* tipText ) +{ + S32 hitItemIndex; + if( hitTest( hoverPos, hitItemIndex ) ) + tipText = mItems[ hitItemIndex ]->itemTooltip; + + return defaultTooltipRender( hoverPos, cursorPos, tipText ); +} + +//----------------------------------------------------------------------------- +// Hit Detection +//----------------------------------------------------------------------------- + +bool GuiListBoxCtrl::hitTest( const Point2I& point, S32& outItem ) +{ + Point2I localPoint = globalToLocalCoord( point ); + + S32 itemHit = ( localPoint.y < 0 ) ? -1 : (S32)mFloor( (F32)localPoint.y / (F32)mItemSize.y ); + if ( itemHit >= mItems.size() || itemHit == -1 ) + return false; + + LBItem *hitItem = mItems[ itemHit ]; + if ( hitItem == NULL ) + return false; + + outItem = itemHit; + return true; +} + +//----------------------------------------------------------------------------- +// Mouse Events +//----------------------------------------------------------------------------- + +void GuiListBoxCtrl::onMouseDragged(const GuiEvent &event) +{ + Parent::onMouseDragged(event); + + onMouseDragged_callback(); +} + +void GuiListBoxCtrl::onMouseDown( const GuiEvent &event ) +{ + S32 itemHit; + if( !hitTest( event.mousePoint, itemHit ) ) + return; + + LBItem* hitItem = mItems[ itemHit ]; + + // If we're not a multiple selection listbox, we simply select/unselect an item + if( !mMultipleSelections ) + { + // No current selection? Just select the cell and move on + S32 selItem = getSelectedItem(); + + if ( selItem != itemHit && selItem != -1 ) + clearSelection(); + + // Set the current selection + setCurSel( itemHit ); + + if( itemHit == selItem && event.mouseClickCount == 2 ) + onDoubleClick_callback(); + + // Store the clicked item + mLastClickItem = hitItem; + + // Evaluate the console command if we clicked the same item twice + if( selItem == itemHit && event.mouseClickCount > 1 ) + execAltConsoleCallback(); + + return; + + } + + // Deal with multiple selections + if( event.modifier & SI_MULTISELECT) + { + // Ctrl-Click toggles selection + if( hitItem->isSelected ) + { + removeSelection( hitItem, itemHit ); + + // We return here when we deselect an item because we don't store last clicked when we deselect + return; + } + else + addSelection( hitItem, itemHit ); + } + else if( event.modifier & SI_RANGESELECT ) + { + if( !mLastClickItem ) + addSelection( hitItem, itemHit ); + else + setCurSelRange( getItemIndex( mLastClickItem ), itemHit ); + } + else + { + if( getSelCount() != 0 ) + { + S32 selItem = getSelectedItem(); + if( selItem != -1 && mItems[selItem] != hitItem ) + clearSelection(); + } + addSelection( hitItem, itemHit ); + } + + if( hitItem == mLastClickItem && event.mouseClickCount == 2 ) + onDoubleClick_callback(); + + mLastClickItem = hitItem; +} + +void GuiListBoxCtrl::onMouseUp( const GuiEvent& event ) +{ + S32 itemHit = -1; + if( hitTest( event.mousePoint, itemHit ) ) + onMouseUp_callback(Con::getIntArg( itemHit ), Con::getIntArg( event.mouseClickCount ) ); + + // Execute console command + execConsoleCallback(); + + Parent::onMouseUp( event ); +} + +bool GuiListBoxCtrl::onKeyDown( const GuiEvent &event ) +{ + if ( event.keyCode == KEY_DELETE ) + { + onDeleteKey_callback(); + return true; + } + + return Parent::onKeyDown( event ); +} + +U32 GuiListBoxCtrl::getStringElementCount( const char* inString ) +{ + // Non-whitespace chars. + static const char* set = " \t\n"; + + // End of string. + if ( *inString == 0 ) + return 0; + + U32 wordCount = 0; + U8 search = 0; + + // Search String. + while( *inString ) + { + // Get string element. + search = *inString; + + // End of string? + if ( search == 0 ) + break; + + // Move to next element. + inString++; + + // Search for seperators. + for( U32 i = 0; set[i]; i++ ) + { + // Found one? + if( search == set[i] ) + { + // Yes... + search = 0; + break; + } + } + + // Found a seperator? + if ( search == 0 ) + continue; + + // We've found a non-seperator. + wordCount++; + + // Search for end of non-seperator. + while( 1 ) + { + // Get string element. + search = *inString; + + // End of string? + if ( search == 0 ) + break; + + // Move to next element. + inString++; + + // Search for seperators. + for( U32 i = 0; set[i]; i++ ) + { + // Found one? + if( search == set[i] ) + { + // Yes... + search = 0; + break; + } + } + + // Found Seperator? + if ( search == 0 ) + break; + } + + // End of string? + if ( *inString == 0 ) + { + // Bah! + break; + } + } + + // We've finished. + return wordCount; +} + +//------------------------------------------------------------------------------ +// Get String Element. +//------------------------------------------------------------------------------ +const char* GuiListBoxCtrl::getStringElement( const char* inString, const U32 index ) +{ + // Non-whitespace chars. + static const char* set = " \t\n"; + + U32 wordCount = 0; + U8 search = 0; + const char* pWordStart = NULL; + + // End of string? + if ( *inString != 0 ) + { + // No, so search string. + while( *inString ) + { + // Get string element. + search = *inString; + + // End of string? + if ( search == 0 ) + break; + + // Move to next element. + inString++; + + // Search for seperators. + for( U32 i = 0; set[i]; i++ ) + { + // Found one? + if( search == set[i] ) + { + // Yes... + search = 0; + break; + } + } + + // Found a seperator? + if ( search == 0 ) + continue; + + // Found are word? + if ( wordCount == index ) + { + // Yes, so mark it. + pWordStart = inString-1; + } + + // We've found a non-seperator. + wordCount++; + + // Search for end of non-seperator. + while( 1 ) + { + // Get string element. + search = *inString; + + // End of string? + if ( search == 0 ) + break; + + // Move to next element. + inString++; + + // Search for seperators. + for( U32 i = 0; set[i]; i++ ) + { + // Found one? + if( search == set[i] ) + { + // Yes... + search = 0; + break; + } + } + + // Found Seperator? + if ( search == 0 ) + break; + } + + // Have we found our word? + if ( pWordStart ) + { + // Yes, so we've got our word... + + // Result Buffer. + static char buffer[4096]; + + // Calculate word length. + const U32 length = inString - pWordStart - ((*inString)?1:0); + + // Copy Word. + dStrncpy( buffer, pWordStart, length); + buffer[length] = '\0'; + + // Return Word. + return buffer; + } + + // End of string? + if ( *inString == 0 ) + { + // Bah! + break; + } + } + } + + // Sanity! + AssertFatal( false, "t2dSceneObject::getStringElement() - Couldn't find specified string element!" ); + // Didn't find it + return " "; +} + +void GuiListBoxCtrl::_mirror() +{ + SimSet *mirrorSet; + if ( !Sim::findObject( mMirrorSetName, mirrorSet ) ) + return; + + // Allow script to filter out objects if desired. + + Vector workingSet; + + // If the method is not defined we assume user wants us to add + // all objects. + bool isObjMirroredDefined = isMethod( "isObjectMirrored" ); + + for ( S32 i = 0; i < mirrorSet->size(); i++ ) + { + bool addObj = true; + + if ( isObjMirroredDefined ) + addObj = isObjectMirrored_callback(mirrorSet->at(i)->getIdString()); + + if ( addObj ) + workingSet.push_back( mirrorSet->at(i)->getId() ); + } + + + // Remove existing items that are no longer in the SimSet. + // Or are no longer valid objects. + + SimObjectId curId; + SimObject *curObj; + + for ( U32 i = 0; i < mItems.size(); i++ ) + { + curId = (SimObjectId)mItems[i]->itemData; + + Sim::findObject( curId, curObj ); + + bool keep = false; + + if ( curObj ) + { + if ( workingSet.contains( curId ) ) + { + mItems[i]->itemText = _makeMirrorItemName( curObj ); + keep = true; + } + } + + if ( !keep ) + { + deleteItem( i ); + i--; + } + } + + + // Add items that are in the SimSet but not yet in the list. + + for ( U32 i = 0; i < workingSet.size(); i++ ) + { + curId = workingSet[i]; + Sim::findObject( curId, curObj ); + + bool found = false; + + for ( U32 j = 0; j < mItems.size(); j++ ) + { + if ( (SimObjectId)(mItems[j]->itemData) == curId ) + { + found = true; + break; + } + } + + for ( U32 j = 0; j < mFilteredItems.size(); j++ ) + { + if ( (SimObjectId)(mFilteredItems[j]->itemData) == curId ) + { + found = true; + break; + } + } + + if ( !found ) + { + addItem( _makeMirrorItemName( curObj ), (void*)curId ); + } + } +} + +StringTableEntry GuiListBoxCtrl::_makeMirrorItemName( SimObject *inObj ) +{ + StringTableEntry outName = StringTable->insert(""); + + if ( mMakeNameCallback.isNotEmpty() ) + { + Con::setIntVariable( "$ThisControl", getId() ); + Con::setIntVariable( "$ThisObject", inObj->getId() ); + + outName = StringTable->insert( Con::evaluate( mMakeNameCallback ), true ); + } + else if ( inObj->getName() ) + outName = StringTable->insert( inObj->getName() ); + + if ( !outName || !outName[0] ) + outName = StringTable->insert( "(no name)" ); + + return outName; +} + +DefineEngineMethod( GuiListBoxCtrl, doMirror, void, (),, + "@brief Informs the GuiListBoxCtrl object to mirror the contents of the GuiListBoxCtrl stored in the mirrorSet field.\n\n" + "@tsexample\n" + "\\ Inform the object to mirror the object located at %thisGuiListBox.mirrorSet\n" + "%thisGuiListBox.doMirror();\n" + "@endtsexample\n\n" + "@see GuiCore") +{ + object->_mirror(); +} + +DefineEngineMethod( GuiListBoxCtrl, addFilteredItem, void, (const char* newItem),, + "@brief Checks if there is an item with the exact text of what is passed in, and if so\n" + "the item is removed from the list and adds that item's data to the filtered list.\n\n" + "@param itemName Name of the item that we wish to add to the filtered item list of the GuiListBoxCtrl.\n" + "@tsexample\n" + "// Define the itemName that we wish to add to the filtered item list.\n" + "%itemName = \"This Item Name\";\n\n" + "// Add the item name to the filtered item list.\n" + "%thisGuiListBoxCtrl.addFilteredItem(%filteredItemName);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + String item(newItem); + if( item == String::EmptyString ) + return; + + object->addFilteredItem( item ); +} + +void GuiListBoxCtrl::addFilteredItem( String item ) +{ + // Delete from selected items list + for ( S32 i = 0; i < mSelectedItems.size(); i++ ) + { + String itemText = mSelectedItems[i]->itemText; + if ( dStrcmp( itemText.c_str(), item.c_str() ) == 0 ) + { + mSelectedItems.erase_fast( i ); + break; + } + } + + for ( S32 i = 0; i < mItems.size(); i++ ) + { + String itemText = mItems[i]->itemText; + if( dStrcmp( itemText.c_str(), item.c_str() ) == 0 ) + { + mItems[i]->isSelected = false; + mFilteredItems.push_front( mItems[i] ); + mItems.erase( &mItems[i] ); + break; + } + } +} + +DefineEngineMethod( GuiListBoxCtrl, removeFilteredItem, void, ( const char* itemName ),, + "@brief Removes an item of the entered name from the filtered items list.\n\n" + "@param itemName Name of the item to remove from the filtered list.\n" + "@tsexample\n" + "// Define the itemName that you wish to remove.\n" + "%itemName = \"This Item Name\";\n\n" + "// Remove the itemName from the GuiListBoxCtrl\n" + "%thisGuiListBoxCtrl.removeFilteredItem(%itemName);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + String item(itemName); + if( item == String::EmptyString ) + return; + + object->removeFilteredItem( item ); +} + +void GuiListBoxCtrl::removeFilteredItem( String item ) +{ + for ( S32 i = 0; i < mFilteredItems.size(); i++ ) + { + String itemText = mFilteredItems[i]->itemText; + if( dStrcmp( itemText.c_str(), item.c_str() ) == 0 ) + { + mItems.push_front( mFilteredItems[i] ); + mFilteredItems.erase( &mFilteredItems[i] ); + break; + } + } +} \ No newline at end of file diff --git a/Engine/source/gui/controls/guiListBoxCtrl.h b/Engine/source/gui/controls/guiListBoxCtrl.h new file mode 100644 index 000000000..9f8020247 --- /dev/null +++ b/Engine/source/gui/controls/guiListBoxCtrl.h @@ -0,0 +1,155 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _GUI_LISTBOXCTRL_H_ +#define _GUI_LISTBOXCTRL_H_ + +#ifndef _CONSOLETYPES_H_ +#include "console/consoleTypes.h" +#endif + +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif + +#ifndef _DGL_H_ +#include "gfx/gfxDevice.h" +#endif + +#ifndef _H_GUIDEFAULTCONTROLRENDER_ +#include "gui/core/guiDefaultControlRender.h" +#endif + +#ifndef _GUISCROLLCTRL_H_ +#include "gui/containers/guiScrollCtrl.h" +#endif + + +class GuiListBoxCtrl : public GuiControl +{ +private: + typedef GuiControl Parent; +public: + + GuiListBoxCtrl(); + ~GuiListBoxCtrl(); + DECLARE_CONOBJECT(GuiListBoxCtrl); + DECLARE_CATEGORY( "Gui Lists" ); + DECLARE_DESCRIPTION( "Linear list of text items." ); + + DECLARE_CALLBACK( void, onMouseDragged, ()); + DECLARE_CALLBACK( void, onClearSelection, ()); + DECLARE_CALLBACK( void, onUnSelect, ( const char* index, const char* itemText)); + DECLARE_CALLBACK( void, onSelect, ( const char* index , const char* itemText )); + DECLARE_CALLBACK( void, onDoubleClick, ()); + DECLARE_CALLBACK( void, onMouseUp, (const char* itemHit, const char* mouseClickCount)); + DECLARE_CALLBACK( void, onDeleteKey, ()); + DECLARE_CALLBACK( bool, isObjectMirrored, ( const char* indexIdString )); + + struct LBItem + { + StringTableEntry itemText; + String itemTooltip; + bool isSelected; + void* itemData; + ColorF color; + bool hasColor; + }; + + VectorPtr mItems; + VectorPtr mSelectedItems; + + VectorPtr mFilteredItems; + + bool mMultipleSelections; + Point2I mItemSize; + bool mFitParentWidth; + bool mColorBullet; + LBItem* mLastClickItem; + + // Persistence + static void initPersistFields(); + + // Item Accessors + S32 getItemCount(); + S32 getSelCount(); + S32 getSelectedItem(); + void getSelectedItems( Vector &Items ); + S32 getItemIndex( LBItem *item ); + StringTableEntry getItemText( S32 index ); + SimObject* getItemObject( S32 index ); + + void setCurSel( S32 index ); + void setCurSelRange( S32 start, S32 stop ); + void setItemText( S32 index, StringTableEntry text ); + + S32 addItem( StringTableEntry text, void *itemData = NULL ); + S32 addItemWithColor( StringTableEntry text, ColorF color = ColorF(-1, -1, -1), void *itemData = NULL); + S32 insertItem( S32 index, StringTableEntry text, void *itemData = NULL ); + S32 insertItemWithColor( S32 index, StringTableEntry text, ColorF color = ColorF(-1, -1, -1), void *itemData = NULL); + S32 findItemText( StringTableEntry text, bool caseSensitive = false ); + + void setItemColor(S32 index, ColorF color); + void clearItemColor(S32 index); + + void deleteItem( S32 index ); + void clearItems(); + void clearSelection(); + void removeSelection( LBItem *item, S32 index ); + void removeSelection( S32 index ); + void addSelection( LBItem *item, S32 index ); + void addSelection( S32 index ); + inline void setMultipleSelection( bool allowMultipleSelect = true ) { mMultipleSelections = allowMultipleSelect; }; + + bool hitTest( const Point2I& point, S32& outItem ); + + // Sizing + void updateSize(); + virtual void parentResized(const RectI& oldParentRect, const RectI& newParentRect); + virtual bool onWake(); + + // Rendering + virtual void onRender( Point2I offset, const RectI &updateRect ); + virtual void onRenderItem( RectI itemRect, LBItem *item ); + void drawBox( const Point2I &box, S32 size, ColorI &outlineColor, ColorI &boxColor ); + bool renderTooltip( const Point2I &hoverPos, const Point2I& cursorPos, const char* tipText ); + void addFilteredItem( String item ); + void removeFilteredItem( String item ); + // Mouse/Key Events + virtual void onMouseDown( const GuiEvent &event ); + virtual void onMouseDragged(const GuiEvent &event); + virtual void onMouseUp( const GuiEvent& event ); + virtual bool onKeyDown( const GuiEvent &event ); + + // String Utility + static U32 getStringElementCount( const char *string ); + static const char* getStringElement( const char* inString, const U32 index ); + + // SimSet Mirroring Stuff + void setMirrorObject( SimSet *inObj ); + void _mirror(); + StringTableEntry _makeMirrorItemName( SimObject *inObj ); + + String mMirrorSetName; + String mMakeNameCallback; +}; + +#endif \ No newline at end of file diff --git a/Engine/source/gui/controls/guiMLTextCtrl.cpp b/Engine/source/gui/controls/guiMLTextCtrl.cpp new file mode 100644 index 000000000..d026bfd92 --- /dev/null +++ b/Engine/source/gui/controls/guiMLTextCtrl.cpp @@ -0,0 +1,2379 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/controls/guiMLTextCtrl.h" +#include "gui/containers/guiScrollCtrl.h" +#include "console/consoleTypes.h" +#include "gui/core/guiCanvas.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxDrawUtil.h" +#include "core/frameAllocator.h" +#include "core/strings/unicode.h" +#include "sfx/sfxSystem.h" +#include "sfx/sfxTrack.h" +#include "sfx/sfxTypes.h" +#include "console/engineAPI.h" + +IMPLEMENT_CONOBJECT( GuiMLTextCtrl ); + +ConsoleDocClass( GuiMLTextCtrl, + "@brief A text control that uses the Gui Markup Language ('ML') tags to dynamically change the text.\n\n" + + "Example of dynamic changes include colors, styles, and/or hyperlinks. These changes can occur without " + "having to use separate text controls with separate text profiles.\n\n" + + "@tsexample\n" + "new GuiMLTextCtrl(CenterPrintText)\n" + "{\n" + " lineSpacing = \"2\";\n" + " allowColorChars = \"0\";\n" + " maxChars = \"-1\";\n" + " deniedSound = \"DeniedSoundProfile\";\n" + " text = \"The Text for This Control.\";\n" + " useURLMouseCursor = \"true\";\n" + " //Properties not specific to this control have been omitted from this example.\n" + "};\n" + "@endtsexample\n\n" + + "@see GuiControl\n\n" + + "@ingroup GuiCore\n" +); + + +IMPLEMENT_CALLBACK( GuiMLTextCtrl, onURL, void, ( const char* url ),( url ), + "@brief Called whenever a URL was clicked on within the control.\n\n" + "@param url The URL address that was clicked on.\n" + "@tsexample\n" + "// A URL address was clicked on in the control, causing the callback to occur.\n" + "GuiMLTextCtrl::onUrl(%this,%url)\n" + " {\n" + " // Code to run whenever a URL was clicked on\n" + " }\n" + "@endtsexample\n\n" + "@see GuiControl\n\n" +); + +IMPLEMENT_CALLBACK( GuiMLTextCtrl, onResize, void, ( const char* width, const char* maxY ),( width, maxY ), + "@brief Called whenever the control size changes.\n\n" + "@param width The new width value for the control\n" + "@param maxY The current maximum allowed Y value for the control\n\n" + "@tsexample\n" + "// Control size changed, causing the callback to occur.\n" + "GuiMLTextCtrl::onResize(%this,%width,%maxY)\n" + " {\n" + " // Code to call when the control size changes\n" + " }\n" + "@endtsexample\n\n" + "@see GuiControl\n\n" +); + +GFX_ImplementTextureProfile(GFXMLTextureProfile, + GFXTextureProfile::DiffuseMap, + GFXTextureProfile::PreserveSize | + GFXTextureProfile::Static, + GFXTextureProfile::None); + +const U32 GuiMLTextCtrl::csmTextBufferGrowthSize = 1024; + +DefineEngineMethod( GuiMLTextCtrl, setText, void, (const char* text),, + "@brief Set the text contained in the control.\n\n" + "@param text The text to display in the control.\n" + "@tsexample\n" + "// Define the text to display\n" + "%text = \"Nifty Control Text\";\n\n" + "// Set the text displayed within the control\n" + "%thisGuiMLTextCtrl.setText(%text);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->setText(text, dStrlen(text)); +} + +DefineEngineMethod( GuiMLTextCtrl, getText, const char*, (),, + "@brief Returns the text from the control, including TorqueML characters.\n\n" + "@tsexample\n" + "// Get the text displayed in the control\n" + "%controlText = %thisGuiMLTextCtrl.getText();\n" + "@endtsexample\n\n" + "@return Text string displayed in the control, including any TorqueML characters.\n\n" + "@see GuiControl") +{ + return( object->getTextContent() ); +} + +DefineEngineMethod( GuiMLTextCtrl, addText, void, ( const char* text, bool reformat),, + "@brief Appends the text in the control with additional text. Also .\n\n" + "@param text New text to append to the existing text.\n" + "@param reformat If true, the control will also be visually reset.\n" + "@tsexample\n" + "// Define new text to add\n" + "%text = \"New Text to Add\";\n\n" + "// Set reformat boolean\n" + "%reformat = \"true\";\n\n" + "// Inform the control to add the new text\n" + "%thisGuiMLTextCtrl.addText(%text,%reformat);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->addText(text, dStrlen(text), reformat); +} + +DefineEngineMethod( GuiMLTextCtrl, setCursorPosition, bool, (int newPos),, + "@brief Change the text cursor's position to a new defined offset within the text in the control.\n\n" + "@param newPos Offset to place cursor.\n" + "@tsexample\n" + "// Define cursor offset position\n" + "%position = \"23\";\n\n" + "// Inform the GuiMLTextCtrl object to move the cursor to the new position.\n" + "%thisGuiMLTextCtrl.setCursorPosition(%position);\n" + "@endtsexample\n\n" + "@return Returns true if the cursor position moved, or false if the position was not changed.\n\n" + "@see GuiControl") +{ + return object->setCursorPosition(newPos); +} + +DefineEngineMethod( GuiMLTextCtrl, scrollToTag, void, (int tagID),, + "@brief Scroll down to a specified tag.\n\n" + "Detailed description\n\n" + "@param tagID TagID to scroll the control to\n" + "@tsexample\n" + "// Define the TagID we want to scroll the control to\n" + "%tagId = \"4\";\n\n" + "// Inform the GuiMLTextCtrl to scroll to the defined TagID\n" + "%thisGuiMLTextCtrl.scrollToTag(%tagId);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->scrollToTag( tagID ); +} + +DefineEngineMethod( GuiMLTextCtrl, scrollToTop, void, ( S32 param1, S32 param2),, + "@brief Scroll to the top of the text.\n\n" + "@tsexample\n" + "// Inform GuiMLTextCtrl object to scroll to its top\n" + "%thisGuiMLTextCtrl.scrollToTop();\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->scrollToTop(); +} + +DefineEngineMethod( GuiMLTextCtrl, scrollToBottom, void, (),, + "@brief Scroll to the bottom of the text.\n\n" + "@tsexample\n" + "// Inform GuiMLTextCtrl object to scroll to its bottom\n" + "%thisGuiMLTextCtrl.scrollToBottom();\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->scrollToBottom(); +} + +DefineEngineFunction(StripMLControlChars, const char*, (const char* inString),, + "@brief Strip TorqueML control characters from the specified string, returning a 'clean' version.\n\n" + "@param inString String to strip TorqueML control characters from.\n" + "@tsexample\n" + "// Define the string to strip TorqueML control characters from\n" + "%string = \"How Now Brown Cow\";\n\n" + "// Request the stripped version of the string\n" + "%strippedString = StripMLControlChars(%string);\n" + "@endtsexample\n\n" + "@return Version of the inputted string with all TorqueML characters removed.\n\n" + "@see References\n\n" + "@ingroup GuiCore") +{ + return GuiMLTextCtrl::stripControlChars(inString); +} + +DefineEngineMethod( GuiMLTextCtrl, forceReflow, void, (),, + "@brief Forces the text control to reflow the text after new text is added, possibly resizing the control.\n\n" + "@tsexample\n" + "// Define new text to add\n" + "%newText = \"BACON!\";\n\n" + "// Add the new text to the control\n" + "%thisGuiMLTextCtrl.addText(%newText);\n\n" + "// Inform the GuiMLTextCtrl object to force a reflow to ensure the added text fits properly.\n" + "%thisGuiMLTextCtrl.forceReflow();\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + if(!object->isAwake()) + Con::errorf("GuiMLTextCtrl::forceReflow can only be called on visible controls."); + else + object->reflow(); +} + +//-------------------------------------------------------------------------- +GuiMLTextCtrl::GuiMLTextCtrl() +: mTabStops( NULL ), + mTabStopCount( 0 ), + mCurTabStop( 0 ), + mCurStyle( NULL ), + mCurLMargin( 0 ), + mCurRMargin( 100 ), + mCurJustify( LeftJustify ), + mCurDiv( 0 ), + mCurY( 0 ), + mCurClipX( 0 ), + mLineAtoms( NULL ), + mLineAtomPtr( &mLineAtoms ), + mLineList( NULL ), + mLineInsert( &mLineList ), + mScanPos( 0 ), + mCurX( 0 ), + mMaxY( 0 ), + mCurURL( NULL ), + mLineStart( 0 ), + mVertMoveAnchor( 0 ), + mVertMoveAnchorValid( false ), + mSelectionAnchor( 0 ), + mIsEditCtrl( false ), + mCursorPosition( false ), + mMaxBufferSize( -1 ), + mInitialText( StringTable->insert("") ), + mSelectionActive( false ), + mSelectionStart( 0 ), + mSelectionEnd( 0 ), + mLineSpacingPixels( 2 ), + mAllowColorChars( false ), + mUseURLMouseCursor( false ), + mBitmapList( 0 ), + mBitmapRefList( 0 ), + mDirty( true ), + mTagList( NULL ), + mHitURL( 0 ), + mAlpha( 1.0f ), + mFontList( NULL ) +{ + mActive = true; + //mInitialText = StringTable->insert(""); + Sim::findObject("InputDeniedSound", mDeniedSound); +} + +//-------------------------------------------------------------------------- +GuiMLTextCtrl::~GuiMLTextCtrl() +{ + mCursorPosition = 0; + + mSelectionActive = false; + mSelectionStart = 0; + mSelectionEnd = 0; + freeResources(); +} + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::initPersistFields() +{ + addGroup( "Text" ); + + addField("lineSpacing", TypeS32, Offset(mLineSpacingPixels, GuiMLTextCtrl), "The number of blank pixels to place between each line.\n"); + addField("allowColorChars", TypeBool, Offset(mAllowColorChars, GuiMLTextCtrl), "If true, the control will allow characters to have unique colors."); + addField("maxChars", TypeS32, Offset(mMaxBufferSize, GuiMLTextCtrl), "Maximum number of characters that the control will display."); + addField("deniedSound", TypeSFXTrackName, Offset(mDeniedSound, GuiMLTextCtrl), "If the text will not fit in the control, the deniedSound is played."); + addField("text", TypeCaseString, Offset( mInitialText, GuiMLTextCtrl ), "Text to display in this control."); + addField("useURLMouseCursor", TypeBool, Offset(mUseURLMouseCursor, GuiMLTextCtrl), "If true, the mouse cursor will turn into a hand cursor while over a link in the text.\n" + "This is dependant on the markup language used by the GuiMLTextCtrl\n"); + + endGroup( "Text" ); + + Parent::initPersistFields(); +} + +DefineEngineMethod( GuiMLTextCtrl, setAlpha, void, (F32 alphaVal),, + "@brief Sets the alpha value of the control.\n\n" + "@param alphaVal n - 1.0 floating value for the alpha\n" + "@tsexample\n" + "// Define the alphe value\n" + "%alphaVal = \"0.5\";\n\n" + "// Inform the control to update its alpha value.\n" + "%thisGuiMLTextCtrl.setAlpha(%alphaVal);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->setAlpha(alphaVal); +} + +//-------------------------------------------------------------------------- + +bool GuiMLTextCtrl::onAdd() +{ + if(!Parent::onAdd()) + return false; + + if (!mTextBuffer.length() && mInitialText[0] != 0) + setText(mInitialText, dStrlen(mInitialText)+1); + return true; +} + +//-------------------------------------------------------------------------- +bool GuiMLTextCtrl::onWake() +{ + if (Parent::onWake() == false) + return false; + + mDirty = true; + return true; +} + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::onPreRender() +{ + if(mDirty) + reflow(); +} + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::drawAtomText(bool sel, U32 start, U32 end, Atom *atom, Line *line, Point2I offset) +{ + GFont *font = atom->style->font->fontRes; + U32 xOff = 0; + if(start != atom->textStart) + { + const UTF16* buff = mTextBuffer.getPtr() + atom->textStart; + xOff += font->getStrNWidth(buff, start - atom->textStart); + } + + Point2I drawPoint(offset.x + atom->xStart + xOff, offset.y + atom->yStart); + + ColorI color; + if(atom->url) + { + if(atom->url->mouseDown) + color = atom->style->linkColorHL; + else + color = atom->style->linkColor; + } + else + color = atom->style->color; + + const UTF16* tmp = mTextBuffer.getPtr() + start; + U32 tmpLen = end-start; + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + + if(!sel) + { + if(atom->style->shadowOffset.x || atom->style->shadowOffset.y) + { + ColorI shadowColor = atom->style->shadowColor; + shadowColor.alpha = (S32)(mAlpha * shadowColor.alpha); + drawer->setBitmapModulation(shadowColor); + drawer->drawTextN(font, drawPoint + atom->style->shadowOffset, + tmp, tmpLen, mAllowColorChars ? mProfile->mFontColors : NULL); + } + + color.alpha = (S32)(mAlpha * color.alpha); + drawer->setBitmapModulation(color); + drawer->drawTextN(font, drawPoint, tmp, end-start, mAllowColorChars ? mProfile->mFontColors : NULL); + + //if the atom was "clipped", see if we need to draw a "..." at the end + if (atom->isClipped) + { + Point2I p2 = drawPoint; + p2.x += font->getStrNWidthPrecise(tmp, tmpLen); + drawer->drawTextN(font, p2, "...", 3, mAllowColorChars ? mProfile->mFontColors : NULL); + } + } + else + { + RectI rect; + rect.point.x = drawPoint.x; + rect.point.y = line->y + offset.y; + rect.extent.x = font->getStrNWidth(tmp, tmpLen) + 1; + rect.extent.y = line->height + 1; + + drawer->drawRectFill(rect, mProfile->mFillColorHL); + drawer->setBitmapModulation( mProfile->mFontColorHL ); // over-ride atom color: + drawer->drawTextN(font, drawPoint, tmp, tmpLen, mAllowColorChars ? mProfile->mFontColors : NULL); + + //if the atom was "clipped", see if we need to draw a "..." at the end + if (atom->isClipped) + { + Point2I p2 = drawPoint; + p2.x += font->getStrNWidthPrecise(tmp, end - atom->textStart); + drawer->drawTextN(font, p2, "...", 3, mAllowColorChars ? mProfile->mFontColors : NULL); + } + } + + if(atom->url && !atom->url->noUnderline) + { + drawPoint.y += atom->baseLine + 2; + Point2I p2 = drawPoint; + p2.x += font->getStrNWidthPrecise(tmp, end - atom->textStart); + drawer->drawLine(drawPoint, p2, color); + } +} + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::onRender(Point2I offset, const RectI& updateRect) +{ + Parent::onRender(offset, updateRect); + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + + // draw all the bitmaps + for(BitmapRef *walk = mBitmapRefList; walk; walk = walk->next) + { + RectI screenBounds = *walk; + screenBounds.point += offset; + if(!screenBounds.overlaps(updateRect)) + continue; + + drawer->clearBitmapModulation(); + drawer->drawBitmap(walk->bitmap->bitmapObject, screenBounds.point); + //GFX->drawRectFill(screenBounds, mProfile->mFillColor); + } + + offset += mProfile->mTextOffset; + + // draw all the text and dividerStyles + for(Line *lwalk = mLineList; lwalk; lwalk = lwalk->next) + { + RectI lineRect(offset.x, offset.y + lwalk->y, getWidth(), lwalk->height); + + if(!lineRect.overlaps(updateRect)) + continue; + + if(lwalk->divStyle) + drawer->drawRectFill(lineRect, mProfile->mFillColorHL); + + for(Atom *awalk = lwalk->atomList; awalk; awalk = awalk->next) + { + if(!mSelectionActive || mSelectionEnd < awalk->textStart || mSelectionStart >= awalk->textStart + awalk->len) + drawAtomText(false, awalk->textStart, awalk->textStart + awalk->len, awalk, lwalk, offset); + else + { + U32 selectionStart = getMax(awalk->textStart, mSelectionStart); + U32 selectionEnd = getMin(awalk->textStart + awalk->len, mSelectionEnd + 1); + + // draw some unselected text + if(selectionStart > awalk->textStart) + drawAtomText(false, awalk->textStart, selectionStart, awalk, lwalk, offset); + + // draw the selection + drawAtomText(true, selectionStart, selectionEnd, awalk, lwalk, offset); + + if(selectionEnd < awalk->textStart + awalk->len) + drawAtomText(false, selectionEnd, awalk->textStart + awalk->len, awalk, lwalk, offset); + } + } + } + + drawer->clearBitmapModulation(); +} + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::freeLineBuffers() +{ + mViewChunker.freeBlocks(); + mLineList = NULL; + mBitmapRefList = NULL; + mTagList = NULL; + mHitURL = 0; + mDirty = true; +} + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::freeResources() +{ + for(Font* walk = mFontList; walk; walk = walk->next) + { + walk->fontRes = NULL; + delete[] walk->faceName; + } + + for(Bitmap* bwalk = mBitmapList; bwalk; bwalk = bwalk->next) + bwalk->bitmapObject = 0; + + mFontList = NULL; + mBitmapList = NULL; + mResourceChunker.freeBlocks(); + mDirty = true; + + freeLineBuffers(); +} + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::onSleep() +{ + freeResources(); + Parent::onSleep(); +} + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::inspectPostApply() +{ + Parent::inspectPostApply(); + + setText(mInitialText, dStrlen(mInitialText)); + + if (mLineSpacingPixels < 0) + mLineSpacingPixels = 0; +} + + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::parentResized(const RectI& oldParentRect, const RectI& newParentRect) +{ + Parent::parentResized(oldParentRect, newParentRect); + mDirty = true; +} + +//-------------------------------------------------------------------------- +U32 GuiMLTextCtrl::getNumChars() const +{ + return mTextBuffer.length(); +} + +//-------------------------------------------------------------------------- +U32 GuiMLTextCtrl::getText(char* pBuffer, const U32 bufferSize) const +{ + mTextBuffer.getCopy8(pBuffer, bufferSize); + + return getMin(mTextBuffer.length(), bufferSize); +} + +//-------------------------------------------------------------------------- +const char* GuiMLTextCtrl::getTextContent() +{ + if ( mTextBuffer.length() > 0 ) + { + UTF8* returnString = Con::getReturnBuffer( mTextBuffer.getUTF8BufferSizeEstimate() ); + mTextBuffer.getCopy8(returnString, mTextBuffer.getUTF8BufferSizeEstimate() ); + return returnString; + } + + return( "" ); +} + +//-------------------------------------------------------------------------- +const char *GuiMLTextCtrl::getScriptValue() +{ + return getTextContent(); +} + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::setScriptValue(const char *newText) +{ + setText(newText, dStrlen(newText)); +} + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::setText(const char* textBuffer, const U32 numChars) +{ + U32 chars = numChars; + if(numChars >= mMaxBufferSize) + chars = mMaxBufferSize; + + // leaving this usage because we StringBuffer.set((UTF8*)) cannot take a numChars arg. + // perhaps it should? -paxorr + FrameTemp tmp(chars+1); + dStrncpy(tmp, textBuffer, chars); + tmp[chars] = 0; + + mTextBuffer.set(tmp); + + //after setting text, always set the cursor to the beginning + setCursorPosition(0); + clearSelection(); + mDirty = true; + scrollToTop(); +} + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::addText(const char* textBuffer, const U32 numChars, bool reformat) +{ + if(numChars >= mMaxBufferSize) + return; + + FrameTemp tmp(numChars+1); + dStrncpy(tmp, textBuffer, numChars); + tmp[numChars] = 0; + + mTextBuffer.append(tmp); + + //after setting text, always set the cursor to the beginning + if (reformat) + { + setCursorPosition(0); + clearSelection(); + mDirty = true; + scrollToTop(); + } +} + +//-------------------------------------------------------------------------- +bool GuiMLTextCtrl::setCursorPosition(const S32 newPosition) +{ + if (newPosition < 0) + { + mCursorPosition = 0; + return true; + } + else if (newPosition >= mTextBuffer.length()) + { + mCursorPosition = mTextBuffer.length(); + return true; + } + else + { + mCursorPosition = newPosition; + return false; + } +} + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::ensureCursorOnScreen() +{ + // If our parent isn't a scroll control, or we're not the only control + // in the content region, bail... + GuiControl* pParent = getParent(); + GuiScrollCtrl *sc = dynamic_cast(pParent); + if(!sc) + return; + + // Ok. Now we know that our parent is a scroll control. Let's find the + // top of the cursor, and it's bottom. We can then scroll the parent control + // if appropriate... + + Point2I cursorTopP, cursorBottomP; + ColorI color; + getCursorPositionAndColor(cursorTopP, cursorBottomP, color); + + sc->scrollRectVisible(RectI(cursorTopP.x, cursorTopP.y, 1, cursorBottomP.y - cursorTopP.y)); +} + +//-------------------------------------- +void GuiMLTextCtrl::getCursorPositionAndColor(Point2I &cursorTop, Point2I &cursorBottom, ColorI &color) +{ + S32 x = 0; + S32 y = 0; + S32 height = mProfile->mFont->getHeight(); + color = mProfile->mCursorColor; + for(Line *walk = mLineList; walk; walk = walk->next) + { + if ((mCursorPosition <= walk->textStart + walk->len) || (walk->next == NULL)) + { + // it's in the atoms on this line... + y = walk->y; + height = walk->height; + + for(Atom *awalk = walk->atomList; awalk; awalk = awalk->next) + { + + if(mCursorPosition < awalk->textStart) + { + x = awalk->xStart; + goto done; + } + + if(mCursorPosition > awalk->textStart + awalk->len) + { + x = awalk->xStart + awalk->width; + continue; + } + + // it's in the text block... + x = awalk->xStart; + GFont *font = awalk->style->font->fontRes; + + const UTF16* buff = mTextBuffer.getPtr() + awalk->textStart; + x += font->getStrNWidth(buff, mCursorPosition - awalk->textStart);// - 1); + + color = awalk->style->color; + goto done; + } + + //if it's within this walk's width, but we didn't find an atom, leave the cursor at the beginning of the line... + goto done; + } + } +done: + cursorTop.set(x, y); + cursorBottom.set(x, y + height); +} + +//-------------------------------------------------------------------------- +// Keyboard events... +bool GuiMLTextCtrl::onKeyDown(const GuiEvent& event) +{ + //only cut/copy work with this control... + if (event.modifier & SI_COPYPASTE) + { + switch(event.keyCode) + { + //copy + case KEY_C: + { + //make sure we actually have something selected + if (mSelectionActive) + { + copyToClipboard(mSelectionStart, mSelectionEnd); + setUpdate(); + } + return true; + } + + default: + break; + } + } + + // Otherwise, let the parent have the event... + return Parent::onKeyDown(event); +} + +//-------------------------------------------------------------------------- +// Mousing events... +void GuiMLTextCtrl::onMouseDown(const GuiEvent& event) +{ + if(!mActive) + return; + + Atom *hitAtom = findHitAtom(globalToLocalCoord(event.mousePoint)); + if(hitAtom && !mIsEditCtrl) + { + mouseLock(); + mHitURL = hitAtom->url; + if (mHitURL) + mHitURL->mouseDown = true; + } + + setFirstResponder(); + mouseLock(); + + mSelectionActive = false; + mSelectionAnchor = getTextPosition(globalToLocalCoord(event.mousePoint)); + mSelectionAnchorDropped = event.mousePoint; + if (mSelectionAnchor < 0) + mSelectionAnchor = 0; + else if (mSelectionAnchor >= mTextBuffer.length()) + mSelectionAnchor = mTextBuffer.length(); + + mVertMoveAnchorValid = false; + + setUpdate(); +} + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::onMouseDragged(const GuiEvent& event) +{ + if (!mActive || (getRoot()->getMouseLockedControl() != this)) + return; + + Atom *hitAtom = findHitAtom(globalToLocalCoord(event.mousePoint)); + bool down = false; + + //note mHitURL can't be set unless this is (!mIsEditCtrl) + if(hitAtom && hitAtom->url == mHitURL) + down = true; + + if(mHitURL && down != mHitURL->mouseDown) + mHitURL->mouseDown = down; + + if (!mHitURL) + { + S32 newSelection = 0; + newSelection = getTextPosition(globalToLocalCoord(event.mousePoint)); + if (newSelection < 0) + newSelection = 0; + else if (newSelection > mTextBuffer.length()) + newSelection = mTextBuffer.length(); + + if (newSelection == mSelectionAnchor) + { + mSelectionActive = false; + } + else if (newSelection > mSelectionAnchor) + { + mSelectionActive = true; + mSelectionStart = mSelectionAnchor; + mSelectionEnd = newSelection - 1; + } + else + { + mSelectionStart = newSelection; + mSelectionEnd = mSelectionAnchor - 1; + mSelectionActive = true; + } + setCursorPosition(newSelection); + mDirty = true; + } + + setUpdate(); +} + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::onMouseUp(const GuiEvent& event) +{ + if (!mActive || (getRoot()->getMouseLockedControl() != this)) + return; + + mouseUnlock(); + + //see if we've clicked on a URL + Atom *hitAtom = findHitAtom(globalToLocalCoord(event.mousePoint)); + if (mHitURL && hitAtom && hitAtom->url == mHitURL && mHitURL->mouseDown) + { + mHitURL->mouseDown = false; + + // Convert URL from UTF16 to UTF8. + UTF8* url = mTextBuffer.createSubstring8(mHitURL->textStart, mHitURL->len); + onURL_callback(url); + + delete[] url; + mHitURL = NULL; + + setUpdate(); + return; + } + + //else, update our selection + else + { + if ((event.mousePoint - mSelectionAnchorDropped).len() < 3) + mSelectionActive = false; + + setCursorPosition(getTextPosition(globalToLocalCoord(event.mousePoint))); + mVertMoveAnchorValid = false; + setUpdate(); + } +} + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent) +{ + if(!mUseURLMouseCursor) + { + Parent::getCursor(cursor, showCursor, lastGuiEvent); + return; + } + + GuiCanvas *pRoot = getRoot(); + if( !pRoot ) + return; + + PlatformWindow *pWindow = pRoot->getPlatformWindow(); + AssertFatal(pWindow != NULL,"GuiControl without owning platform window! This should not be possible."); + PlatformCursorController *pController = pWindow->getCursorController(); + AssertFatal(pController != NULL,"PlatformWindow without an owned CursorController!"); + + Atom *hitAtom = findHitAtom(globalToLocalCoord(lastGuiEvent.mousePoint)); + if(hitAtom && !mIsEditCtrl && hitAtom->url) + { + if(pRoot->mCursorChanged != PlatformCursorController::curHand) + { + // We've already changed the cursor, so set it back before we change it again. + if(pRoot->mCursorChanged != -1) + pController->popCursor(); + + // Now change the cursor shape + pController->pushCursor(PlatformCursorController::curHand); + pRoot->mCursorChanged = PlatformCursorController::curHand; + + } + } + else if(pRoot->mCursorChanged != -1) + { + // Restore the cursor we changed + pController->popCursor(); + pRoot->mCursorChanged = -1; + } +} + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::insertChars(const char* inputChars, + const U32 numInputChars, + const U32 position) +{ + AssertFatal(isSelectionActive() == false, "GuiMLTextCtrl::insertChars: don't use this function when there's a selection active!"); + AssertFatal(position <= mTextBuffer.length(), "GuiMLTextCtrl::insertChars: can't insert outside of current text!"); + + //make sure the text will fit... + S32 numCharsToInsert = numInputChars; + if (mMaxBufferSize > 0 && mTextBuffer.length() + numInputChars + 1 > mMaxBufferSize) + numCharsToInsert = mMaxBufferSize - mTextBuffer.length() - 1; + if (numCharsToInsert <= 0) + { + // Play the "Denied" sound: + if ( numInputChars > 0 && mDeniedSound ) + SFX->playOnce(mDeniedSound); + + return; + } + + mTextBuffer.insert(position, inputChars ); + + if (mCursorPosition >= position) + { + // Cursor was at or after the inserted text, move forward... + mCursorPosition += numCharsToInsert; + } + + AssertFatal(mCursorPosition <= mTextBuffer.length(), "GuiMLTextCtrl::insertChars: bad cursor position"); + mDirty = true; +} + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::deleteChars(const U32 rangeStart, + const U32 rangeEnd) +{ + AssertFatal(isSelectionActive() == false, "GuiMLTextCtrl::deleteChars: don't use this function when there's a selection active"); + AssertFatal(rangeStart <= mTextBuffer.length() && rangeEnd <= mTextBuffer.length(), + avar("GuiMLTextCtrl::deleteChars: can't delete outside of current text (%d, %d, %d)", + rangeStart, rangeEnd, mTextBuffer.length())); + AssertFatal(rangeStart <= rangeEnd, "GuiMLTextCtrl::deleteChars: invalid delete range"); + + // Currently deleting text doesn't resize the text buffer, perhaps this should + // change? + mTextBuffer.cut(rangeStart, rangeEnd - rangeStart); + + if (mCursorPosition <= rangeStart) + { + // Cursor placed before deleted text, ignore + } + else if (mCursorPosition > rangeStart && mCursorPosition <= rangeEnd) + { + // Cursor inside deleted text, set to start of range + mCursorPosition = rangeStart; + } + else + { + // Cursor after deleted text, decrement by number of chars deleted + mCursorPosition -= (rangeEnd - rangeStart) + 1; + } + + AssertFatal(mCursorPosition <= mTextBuffer.length(), "GuiMLTextCtrl::deleteChars: bad cursor position"); + mDirty = true; +} + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::copyToClipboard(const U32 rangeStart, const U32 rangeEnd) +{ + AssertFatal(rangeStart < mTextBuffer.length() && rangeEnd < mTextBuffer.length(), + avar("GuiMLTextCtrl::copyToClipboard: can't copy outside of current text (%d, %d, %d)", + rangeStart, rangeEnd, mTextBuffer.length())); + AssertFatal(rangeStart <= rangeEnd, "GuiMLTextCtrl::copyToClipboard: invalid copy range"); + + //copy the selection to the clipboard + + UTF8* selection = mTextBuffer.createSubstring8(rangeStart, rangeEnd-rangeStart+1); + Platform::setClipboard(selection); + delete[] selection; +} + +//-------------------------------------------------------------------------- +bool GuiMLTextCtrl::isSelectionActive() const +{ + return mSelectionActive; +} + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::clearSelection() +{ + mSelectionActive = false; + mSelectionStart = 0; + mSelectionEnd = 0; +} + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::scrollToTag( U32 id ) +{ + // If the parent control is not a GuiScrollContentCtrl, then this call is invalid: + GuiScrollCtrl *pappy = dynamic_cast(getParent()); + if ( !pappy ) + return; + + // Find the indicated tag: + LineTag* tag = NULL; + for ( tag = mTagList; tag; tag = tag->next ) + { + if ( tag->id == id ) + break; + } + + if ( !tag ) + { + Con::warnf( ConsoleLogEntry::General, "GuiMLTextCtrl::scrollToTag - tag id %d not found!", id ); + return; + } + pappy->scrollRectVisible(RectI(0, tag->y, 1, 1)); +} + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::scrollToTop() +{ + // If the parent control is not a GuiScrollContentCtrl, then this call is invalid: + GuiScrollCtrl *pappy = dynamic_cast(getParent()); + if ( !pappy ) + return; + pappy->scrollRectVisible(RectI(0,0,0,0)); +} + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::scrollToBottom() +{ + // If the parent control is not a GuiScrollContentCtrl, then this call is invalid: + GuiScrollCtrl *pappy = dynamic_cast(getParent()); + if ( !pappy ) + return; + + // Figure bounds for the bottom left corner + RectI cornerBounds (0, getPosition().y + getExtent().y, 1, 1); + pappy->scrollRectVisible(cornerBounds); +} + +//-------------------------------------------------------------------------- +GuiMLTextCtrl::Atom *GuiMLTextCtrl::findHitAtom(const Point2I localCoords) +{ + AssertFatal(mAwake, "Can't get the text position of a sleeping control."); + if(mDirty) + reflow(); + for(Line *walk = mLineList; walk; walk = walk->next) + { + if(localCoords.y < walk->y) + return NULL; + + if(localCoords.y >= walk->y && localCoords.y < walk->y + walk->height) + { + for(Atom *awalk = walk->atomList; awalk; awalk = awalk->next) + { + if(localCoords.x < awalk->xStart) + return NULL; + if(localCoords.x >= awalk->xStart + awalk->width) + continue; + return awalk; + } + } + } + return NULL; +} + +//-------------------------------------------------------------------------- +S32 GuiMLTextCtrl::getTextPosition(const Point2I& localCoords) +{ + AssertFatal(mAwake, "Can't get the text position of a sleeping control."); + if(mDirty) + reflow(); + + for(Line *walk = mLineList; walk; walk = walk->next) + { + if((S32)localCoords.y < (S32)walk->y) + return walk->textStart; + + if(localCoords.y >= walk->y && localCoords.y < walk->y + walk->height) + { + for(Atom *awalk = walk->atomList; awalk; awalk = awalk->next) + { + if(localCoords.x < awalk->xStart) + return awalk->textStart; + if(localCoords.x >= awalk->xStart + awalk->width) + continue; + // it's in the text block... + GFont *font = awalk->style->font->fontRes; + + const UTF16 *tmp16 = mTextBuffer.getPtr() + awalk->textStart; + U32 bp = font->getBreakPos(tmp16, awalk->len, localCoords.x - awalk->xStart, false); + return awalk->textStart + bp; + } + return walk->textStart + walk->len; + } + } + return mTextBuffer.length() - 1; +} + +//-------------------------------------------------------------------------- +GuiMLTextCtrl::Font *GuiMLTextCtrl::allocFont(const char *faceName, U32 faceNameLen, U32 size) +{ + // check if it's in the font list currently: + for(Font *walk = mFontList; walk; walk = walk->next) + if(faceNameLen == walk->faceNameLen && + !dStrncmp(walk->faceName, faceName, faceNameLen) && + size == walk->size) + return walk; + + // Create! + Font *ret; + ret = constructInPlace((Font *) mResourceChunker.alloc(sizeof(Font))); + ret->faceName = new char[faceNameLen+1]; + dStrncpy(ret->faceName, faceName, faceNameLen); + ret->faceName[faceNameLen] = '\0'; + ret->faceNameLen = faceNameLen; + ret->size = size; + ret->next = mFontList; + ret->fontRes = GFont::create(ret->faceName, size, GuiControlProfile::sFontCacheDirectory); + if(ret->fontRes != NULL) + { + ret->next = mFontList; + mFontList = ret; + return ret; + } + return NULL; +} + +//-------------------------------------------------------------------------- +GuiMLTextCtrl::Bitmap *GuiMLTextCtrl::allocBitmap(const char *bitmapName, U32 bitmapNameLen) +{ + for(Bitmap *walk = mBitmapList; walk; walk = walk->next) + if(bitmapNameLen == walk->bitmapNameLen && + !dStrncmp(walk->bitmapName, bitmapName, bitmapNameLen)) + return walk; + + Bitmap *ret = constructInPlace((Bitmap *) mResourceChunker.alloc(sizeof(Bitmap))); + const U32 BitmapNameSize = sizeof(ret->bitmapName); + dStrncpy(ret->bitmapName, bitmapName, getMin(bitmapNameLen,BitmapNameSize)); + if (bitmapNameLen < BitmapNameSize) + ret->bitmapName[bitmapNameLen] = 0; + else + ret->bitmapName[BitmapNameSize - 1] = 0; + ret->bitmapNameLen = bitmapNameLen; + + ret->bitmapObject.set(ret->bitmapName, &GFXMLTextureProfile, avar("%s() - ret->bitmapObject (line %d)", __FUNCTION__, __LINE__)); + //ret->bitmapObject.set(bitmapName, &GFXMLTextureProfile); + if( bool(ret->bitmapObject) ) + { + ret->next = mBitmapList; + mBitmapList = ret; + return ret; + } + return NULL; +} + +//-------------------------------------------------------------------------- +GuiMLTextCtrl::LineTag *GuiMLTextCtrl::allocLineTag(U32 id) +{ + for ( LineTag* walk = mTagList; walk; walk = walk->next ) + { + if ( walk->id == id ) + { + Con::warnf( ConsoleLogEntry::General, "GuiMLTextCtrl - can't add duplicate line tags!" ); + return( NULL ); + } + } + LineTag* newTag = (LineTag*) mViewChunker.alloc( sizeof( LineTag ) ); + newTag->id = id; + newTag->y = mCurY; + newTag->next = mTagList; + mTagList = newTag; + + return( newTag ); +} + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::emitNewLine(U32 textStart) +{ + //clear any clipping + mCurClipX = 0; + + Line *l = (Line *) mViewChunker.alloc(sizeof(Line)); + l->height = mCurStyle->font->fontRes->getHeight(); + l->y = mCurY; + l->textStart = mLineStart; + l->len = textStart - l->textStart; + mLineStart = textStart; + l->atomList = mLineAtoms; + l->next = 0; + l->divStyle = mCurDiv; + *mLineInsert = l; + mLineInsert = &(l->next); + mCurX = mCurLMargin; + mCurTabStop = 0; + + if(mLineAtoms) + { + // scan through the atoms in the line, get the largest height + U32 maxBaseLine = 0; + U32 maxDescent = 0; + Atom* walk; + + for(walk = mLineAtoms; walk; walk = walk->next) + { + if(walk->baseLine > maxBaseLine) + maxBaseLine = walk->baseLine; + if(walk->descent > maxDescent) + maxDescent = walk->descent; + if(!walk->next) + { + l->len = walk->textStart + walk->len - l->textStart; + mLineStart = walk->textStart + walk->len; + } + } + l->height = maxBaseLine + maxDescent; + + for(walk = mLineAtoms; walk; walk = walk->next) + walk->yStart = mCurY + maxBaseLine - walk->baseLine; + } + mCurY += l->height; + mLineAtoms = NULL; + mLineAtomPtr = &mLineAtoms; + + // clear out the blocker list + BitmapRef **blockList = &mBlockList; + while(*blockList) + { + BitmapRef *blk = *blockList; + if(blk->point.y + blk->extent.y <= mCurY) + *blockList = blk->nextBlocker; + else + blockList = &(blk->nextBlocker); + } + if(mCurY > mMaxY) + mMaxY = mCurY; +} + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::emitBitmapToken(GuiMLTextCtrl::Bitmap *bmp, U32 textStart, bool bitmapBreak) +{ + if(mCurRMargin <= mCurLMargin) + return; + if(mCurRMargin - mCurLMargin < bmp->bitmapObject->getWidth()) + return; + + BitmapRef *ref = (BitmapRef *) mViewChunker.alloc(sizeof(BitmapRef)); + ref->bitmap = bmp; + ref->next = mBitmapRefList; + mBitmapRefList = ref; + + // now we gotta insert it into the blocker list and figure out where it's spos to go... + ref->extent.x = bmp->bitmapObject->getBitmapWidth(); + ref->extent.y = bmp->bitmapObject->getBitmapHeight(); + + // find the first space in the blocker list that will fit this thats > curLMargin + + while(bitmapBreak && mBlockList != &mSentinel) + emitNewLine(textStart); + + for(;;) + { + // loop til we find a line that fits... + // we'll have to emitLine repeatedly to clear out the block lists... + + BitmapRef **walk = &mBlockList; + U32 minx = mCurX; + U32 maxx = mCurRMargin; + + while(*walk) + { + BitmapRef *blk = *walk; + + if(blk->point.x > minx) + { + U32 right = maxx; + if(blk->point.x < right) + right = blk->point.x; + U32 width = right - minx; + + if(right > minx && width >= ref->extent.x) // we've found the spot... + { + // insert it: + U32 x = minx; + if(mCurJustify == CenterJustify) + x += (width - ref->extent.x) >> 1; + else if(mCurJustify == RightJustify) + x += width - ref->extent.x; + ref->point.x = x; + ref->point.y = mCurY; + ref->nextBlocker = blk; + *walk = ref; + if(ref->point.y + ref->extent.y > mMaxY) + mMaxY = ref->point.y + ref->extent.y; + + return; + } + } + if(minx < blk->point.x + blk->extent.x) + minx = blk->point.x + blk->extent.x; + // move on to the next blocker... + walk = &(blk->nextBlocker); + } + // go to the next line... + emitNewLine(textStart); + } +} + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::emitTextToken(U32 textStart, U32 len) +{ + if(mCurRMargin <= mCurLMargin) + return; + + GFont *font = mCurStyle->font->fontRes; + Atom *a = (Atom *) mViewChunker.alloc(sizeof(Atom)); + a->url = mCurURL; + + a->style = mCurStyle; + mCurStyle->used = true; + + a->baseLine = font->getBaseline(); + a->descent = font->getDescent(); + a->textStart = textStart; + a->len = len; + a->isClipped = false; + a->next = NULL; + *mEmitAtomPtr = a; + mEmitAtomPtr = &(a->next); +} + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::processEmitAtoms() +{ + Atom *atomList = mEmitAtoms; + mEmitAtoms = NULL; + mEmitAtomPtr = &mEmitAtoms; + + bool bailout = false; + + while(atomList) + { + // split the tokenlist by space + // first find the first space that the text can go into: + BitmapRef *br = mBlockList; + //bool bailout = false; // Scoping error here? Moved up one scope. -pw + Atom *list = atomList; + + while(br && atomList) + { + // if the blocker is before the current x, ignore it. + if(br->point.x + br->extent.x <= mCurX) + { + br = br->nextBlocker; + continue; + } + // if cur x is in the middle of the blocker + // advance cur x to right edge of blocker. + if(mCurX >= br->point.x) + { + mCurX = br->point.x + br->extent.x; + br = br->nextBlocker; + continue; + } + // get the remaining width + U32 right = br->point.x; + if(right > mCurRMargin) + right = mCurRMargin; + + //if we're clipping text, readjust + if (mCurClipX > 0 && right > mCurClipX) + right = mCurClipX; + + // if there's no room, break to the next line... + if(right <= mCurX) + break; + + // we've got some space: + U32 width = right - mCurX; + atomList = splitAtomListEmit(atomList, width); + if(atomList) // there's more, so advance cur x + { + mCurX = br->point.x + br->extent.x; + br = br->nextBlocker; + } + } + if(mBlockList == &mSentinel && atomList == list) + { + if(bailout) + return; + else + bailout = true; + } + // is there more to process for the next line? + if(atomList) + emitNewLine(mScanPos); + } +} + +//-------------------------------------------------------------------------- +GuiMLTextCtrl::Atom *GuiMLTextCtrl::splitAtomListEmit(Atom *list, U32 width) +{ + U32 totalWidth = 0; + Atom *emitList = 0; + Atom **emitPtr = &emitList; + + bool adjustClipAtom = false; + Atom *clipAtom = NULL; + bool emitted = false; + + while(list) + { + GFont *font = list->style->font->fontRes; + U32 breakPos; + + const UTF16 *tmp16 = mTextBuffer.getPtr() + list->textStart; + + //if we're clipping the text, we don't break within an atom, we adjust the atom to only render + //the portion of text that does fit, and to ignore the rest... + if (mCurClipX > 0) + { + //find out how many character's fit within the given width + breakPos = font->getBreakPos(tmp16, list->len, width - totalWidth, false); + + //if there isn't room for even the first character... + if (breakPos == 0) + { + //set the atom's len and width to prevent it from being drawn + list->len = 0; + list->width = 0; + adjustClipAtom = true; + } + + //if our text doesn't fit within the clip region, add a "..." + else if (breakPos != list->len) + { + U32 etcWidth = font->getStrNWidthPrecise("...", 3); + breakPos = font->getBreakPos(tmp16, list->len, width - totalWidth - etcWidth, false); + + //again, if there isn't even room for a single character before the "...." + if (breakPos == 0) + { + //set the atom's len and width to prevent it from being drawn + list->len = 0; + list->width = 0; + adjustClipAtom = true; + } + else + { + //set the char len to the break pos, and the rest of the characters in this atom will be ignored + list->len = breakPos; + list->width = width - totalWidth; + + //mark this one as clipped + list->isClipped = true; + clipAtom = NULL; + } + } + + //otherwise no need to treat this atom any differently.. + else + { + //set the atom width == to the string length + list->width = font->getStrNWidthPrecise(tmp16, breakPos); + + //set the pointer to the last atom that fit within the clip region + clipAtom = list; + } + } + else + { + breakPos = font->getBreakPos(tmp16, list->len, width - totalWidth, true); + if(breakPos == 0 || (breakPos < list->len && mTextBuffer.getChar(list->textStart + breakPos - 1)!= ' ' && emitted)) + break; + + //set the atom width == to the string length + list->width = font->getStrNWidthPrecise(tmp16, breakPos); + } + + //update the total width + totalWidth += list->width; + + // see if this is the last atom that will fit: + Atom *emit = list; + + *emitPtr = emit; + emitPtr = &(emit->next); + emitted = true; + + //if we're clipping, don't split the atom, otherwise, see if it needs to be split + if(!list->isClipped && breakPos != list->len) + { + Atom *a = (Atom *) mViewChunker.alloc(sizeof(Atom)); + a->url = list->url; + a->textStart = list->textStart + breakPos; + a->len = list->len - breakPos; + a->next = list->next; + a->baseLine = list->baseLine; + a->descent = list->descent; + a->style = list->style; + a->isClipped = false; + + list = a; + emit->len = breakPos; + break; + } + list = list->next; + if(totalWidth > width) + break; + } + + //if we had to completely clip an atom(s), the last (partially) visible atom should be modified to include a "..." + if (adjustClipAtom && clipAtom) + { + GFont *font = clipAtom->style->font->fontRes; + U32 breakPos; + + U32 etcWidth = font->getStrNWidthPrecise("...", 3); + + const UTF16 *tmp16 = mTextBuffer.getPtr() + clipAtom->textStart; + + breakPos = font->getBreakPos(tmp16, clipAtom->len, clipAtom->width - etcWidth, false); + if (breakPos != 0) + { + clipAtom->isClipped = true; + clipAtom->len = breakPos; + } + } + + // terminate the emit list: + *emitPtr = 0; + // now emit it: + // going from mCurX to mCurX + width: + if(mCurJustify == CenterJustify) + { + if ( width > totalWidth ) + mCurX += (width - totalWidth) >> 1; + } + else if(mCurJustify == RightJustify) + { + if ( width > totalWidth ) + mCurX += width - totalWidth; + } + + + while(emitList) + { + emitList->xStart = mCurX; + mCurX += emitList->width; + Atom *temp = emitList->next; + *mLineAtomPtr = emitList; + emitList->next = 0; + mLineAtomPtr = &(emitList->next); + emitList = temp; + } + + return list; +} + +//-------------------------------------------------------------------------- +static bool scanforchar(const char *str, U32 &idx, char c) +{ + U32 startidx = idx; + while(str[idx] != c && str[idx] && str[idx] != ':' && str[idx] != '>' && str[idx] != '\n') + idx++; + return str[idx] == c && startidx != idx; +} + +//-------------------------------------------------------------------------- +static bool scanforURL(const char *str, U32 &idx, char c) +{ + U32 startidx = idx; + while(str[idx] != c && str[idx] && str[idx] != '>' && str[idx] != '\n') + idx++; + return str[idx] == c && startidx != idx; +} + +//-------------------------------------------------------------------------- +static S32 getHexVal(char c) +{ + if(c >= '0' && c <= '9') + return c - '0'; + else if(c >= 'A' && c <= 'Z') + return c - 'A' + 10; + else if(c >= 'a' && c <= 'z') + return c - 'a' + 10; + return -1; +} + +//-------------------------------------------------------------------------- +GuiMLTextCtrl::Style *GuiMLTextCtrl::allocStyle(GuiMLTextCtrl::Style *style) +{ + Style *ret = (Style *) mViewChunker.alloc(sizeof(Style)); + ret->used = false; + if(style) + { + ret->font = style->font; + ret->color = style->color; + ret->linkColor = style->linkColor; + ret->linkColorHL = style->linkColorHL; + ret->shadowColor = style->shadowColor; + ret->shadowOffset = style->shadowOffset; + ret->next = style->next; + } + else + { + ret->font = 0; + ret->next = 0; + } + return ret; +} + +//-------------------------------------------------------------------------- +void GuiMLTextCtrl::reflow() +{ + AssertFatal(mAwake, "Can't reflow a sleeping control."); + freeLineBuffers(); + mDirty = false; + mScanPos = 0; + + mLineList = NULL; + mLineInsert = &mLineList; + + mCurStyle = allocStyle(NULL); + mCurStyle->font = allocFont((char *) mProfile->mFontType, dStrlen(mProfile->mFontType), mProfile->mFontSize); + if(!mCurStyle->font) + return; + mCurStyle->color = mProfile->mFontColor; + mCurStyle->shadowColor = mProfile->mFontColor; + mCurStyle->shadowOffset.set(0,0); + mCurStyle->linkColor = mProfile->mFontColors[GuiControlProfile::ColorUser0]; + mCurStyle->linkColorHL = mProfile->mFontColors[GuiControlProfile::ColorUser1]; + + U32 width = getWidth(); + + mCurLMargin = 0; + mCurRMargin = width; + mCurJustify = LeftJustify; + mCurDiv = 0; + mCurY = 0; + mCurX = 0; + mCurClipX = 0; + mLineAtoms = NULL; + mLineAtomPtr = &mLineAtoms; + + mSentinel.point.x = width; + mSentinel.point.y = 0; + mSentinel.extent.x = 0; + mSentinel.extent.y = 0x7FFFFF; + mSentinel.nextBlocker = NULL; + mLineStart = 0; + mEmitAtoms = 0; + mMaxY = 0; + mEmitAtomPtr = &mEmitAtoms; + + mBlockList = &mSentinel; + + Font *nextFont; + LineTag *nextTag; + mTabStops = 0; + mCurTabStop = 0; + mTabStopCount = 0; + mCurURL = 0; + Style *newStyle; + + U32 textStart; + U32 len; + U32 idx; + U32 sizidx; + + for(;;) + { + UTF16 curChar = mTextBuffer.getChar(mScanPos); + + if(!curChar) + break; + + if(curChar == '\n') + { + textStart = mScanPos; + len = 1; + mScanPos++; + processEmitAtoms(); + emitNewLine(textStart); + mCurDiv = 0; + continue; + } + + if(curChar == '\t') + { + textStart = mScanPos; + len = 1; + mScanPos++; + processEmitAtoms(); + if(mTabStopCount) + { + if(mCurTabStop < mTabStopCount) + { + if(mCurX < mTabStops[mCurTabStop]) + mCurX = mTabStops[mCurTabStop]; + } + mCurTabStop++; + } + continue; + } + + if(curChar == '<') + { + // it's probably some kind of tag: + + // Get a pointer into the utf8 version of the buffer, + // because we're still scanning text in in utf8 mode. + const UTF8 *str = mTextBuffer.getPtr8(); + str = getNthCodepoint(str, mScanPos); + + // And go! + + if(!dStrnicmp(str + 1, "br>", 3)) + { + mScanPos += 4; + len = 4; + textStart = mScanPos + 4; + processEmitAtoms(); + emitNewLine(textStart); + mCurDiv = 0; + continue; + } + + if(!dStrnicmp(str + 1, "font:", 5)) + { + // scan for the second colon... + // at each level it should drop out to the text case below... + idx = 6; + if(!scanforchar(str, idx, ':')) + goto textemit; + + sizidx = idx + 1; + if(!scanforchar(str, sizidx, '>')) + goto textemit; + + U32 size = dAtoi(str + idx + 1); + if(!size || size > 64) + goto textemit; + textStart = mScanPos + 6; + len = idx - 6; + + mScanPos += sizidx + 1; + + nextFont = allocFont(str + 6, len, size); + if(nextFont) + { + if(mCurStyle->used) + mCurStyle = allocStyle(mCurStyle); + mCurStyle->font = nextFont; + } + continue; + } + + if ( !dStrnicmp( str + 1, "tag:", 4 ) ) + { + idx = 5; + if ( !scanforchar( str, idx, '>' ) ) + goto textemit; + U32 tagId = dAtoi( str + 5 ); + nextTag = allocLineTag( tagId ); + + mScanPos += idx + 1; + continue; + } + + if(!dStrnicmp(str +1, "color:", 6)) + { + idx = 7; + if(!scanforchar(str, idx, '>')) + goto textemit; + if(idx != 13 && idx != 15) + goto textemit; + ColorI color; + + color.red = getHexVal(str[7]) * 16 + getHexVal(str[8]); + color.green = getHexVal(str[9]) * 16 + getHexVal(str[10]); + color.blue = getHexVal(str[11]) * 16 + getHexVal(str[12]); + if(idx == 15) + color.alpha = getHexVal(str[13]) * 16 + getHexVal(str[14]); + else + color.alpha = 255; + mScanPos += idx + 1; + + if(mCurStyle->used) + mCurStyle = allocStyle(mCurStyle); + mCurStyle->color = color; + + continue; + } + + if(!dStrnicmp(str +1, "shadowcolor:", 12)) + { + idx = 13; + if(!scanforchar(str, idx, '>')) + goto textemit; + if(idx != 19 && idx != 21) + goto textemit; + ColorI color; + + color.red = getHexVal(str[13]) * 16 + getHexVal(str[14]); + color.green = getHexVal(str[15]) * 16 + getHexVal(str[16]); + color.blue = getHexVal(str[17]) * 16 + getHexVal(str[18]); + if(idx == 21) + color.alpha = getHexVal(str[19]) * 16 + getHexVal(str[20]); + else + color.alpha = 255; + mScanPos += idx + 1; + + if(mCurStyle->used) + mCurStyle = allocStyle(mCurStyle); + mCurStyle->shadowColor = color; + + continue; + } + + if(!dStrnicmp(str +1, "linkcolor:", 10)) + { + idx = 11; + if(!scanforchar(str, idx, '>')) + goto textemit; + if(idx != 17 && idx != 19) + goto textemit; + ColorI color; + + color.red = getHexVal(str[11]) * 16 + getHexVal(str[12]); + color.green = getHexVal(str[13]) * 16 + getHexVal(str[14]); + color.blue = getHexVal(str[15]) * 16 + getHexVal(str[16]); + if(idx == 19) + color.alpha = getHexVal(str[17]) * 16 + getHexVal(str[18]); + else + color.alpha = 255; + mScanPos += idx + 1; + + if(mCurStyle->used) + mCurStyle = allocStyle(mCurStyle); + mCurStyle->linkColor = color; + + continue; + } + + if(!dStrnicmp(str +1, "linkcolorhl:", 12)) + { + idx = 13; + if(!scanforchar(str, idx, '>')) + goto textemit; + if(idx != 19 && idx != 21) + goto textemit; + ColorI color; + + color.red = getHexVal(str[13]) * 16 + getHexVal(str[14]); + color.green = getHexVal(str[15]) * 16 + getHexVal(str[16]); + color.blue = getHexVal(str[17]) * 16 + getHexVal(str[18]); + if(idx == 21) + color.alpha = getHexVal(str[19]) * 16 + getHexVal(str[20]); + else + color.alpha = 255; + mScanPos += idx + 1; + + if(mCurStyle->used) + mCurStyle = allocStyle(mCurStyle); + mCurStyle->linkColorHL = color; + + continue; + } + if(!dStrnicmp(str +1, "shadow:", 7)) + { + idx = 8; + if(!scanforchar(str, idx, ':')) + goto textemit; + U32 yidx = idx + 1; + if(!scanforchar(str, yidx, '>')) + goto textemit; + mScanPos += yidx + 1; + Point2I offset; + offset.x = dAtoi(str + 8); + offset.y = dAtoi(str + idx + 1); + if(mCurStyle->used) + mCurStyle = allocStyle(mCurStyle); + mCurStyle->shadowOffset = offset; + continue; + } + if(!dStrnicmp(str +1, "bitmap", 6)) + { + S32 start = 8; + bool bitBrk = false; + if(str[7] == 'k' && str[8] == ':') + { + bitBrk = true; + start = 9; + } + else if(str[7] != ':') + goto textemit; + + idx = start; + if(!scanforchar(str, idx, '>')) + goto textemit; + textStart = mScanPos + start; + len = idx - start; + + mScanPos += idx + 1; + + processEmitAtoms(); + Bitmap *bmp; + bmp = allocBitmap(str + 8, len); + if(bmp) + emitBitmapToken(bmp, textStart, bitBrk); + continue; + } + + if(!dStrnicmp(str +1, "spush>", 6)) + { + mScanPos += 7; + newStyle = allocStyle(mCurStyle); // copy out all the attributes... + newStyle->next = mCurStyle; + mCurStyle = newStyle; + continue; + } + + if(!dStrnicmp(str +1, "spop>", 5)) + { + mScanPos += 6; + if(mCurStyle->next) + mCurStyle = mCurStyle->next; + continue; + } + + if(!dStrnicmp(str +1, "sbreak>", 7)) + { + mScanPos += 8; + processEmitAtoms(); + while(mBlockList != &mSentinel) + emitNewLine(mScanPos); + continue; + } + + if(!dStrnicmp(str +1, "just:left>", 10)) + { + processEmitAtoms(); + mCurJustify = LeftJustify; + mScanPos += 11; + continue; + } + + if(!dStrnicmp(str +1, "just:right>", 11)) + { + processEmitAtoms(); + mCurJustify = RightJustify; + mScanPos += 12; + continue; + } + + if(!dStrnicmp(str +1, "just:center>", 12)) + { + processEmitAtoms(); + mCurJustify = CenterJustify; + mScanPos += 13; + continue; + } + + if(!dStrnicmp(str +1, "a:", 2)) + { + idx = 3; + if(!scanforURL(str, idx, '>')) + goto textemit; + + mCurURL = (URL *) mViewChunker.alloc(sizeof(URL)); + mCurURL->mouseDown = false; + mCurURL->textStart = mScanPos + 3; + mCurURL->len = idx - 3; + mCurURL->noUnderline = false; + + //if the URL is a "gamelink", don't underline... + if (!dStrnicmp(str + 3, "gamelink", 8)) + mCurURL->noUnderline = true; + + mScanPos += idx + 1; + continue; + } + + if(!dStrnicmp(str+1, "/a>", 3)) + { + mCurURL = NULL; + mScanPos += 4; + continue; + } + + U32 margin; + + if(!dStrnicmp(str + 1, "lmargin%:", 9)) + { + idx = 10; + if(!scanforchar(str, idx, '>')) + goto textemit; + margin = (getWidth() * dAtoi(str + 10)) / 100; + mScanPos += idx + 1; + goto setleftmargin; + } + + if(!dStrnicmp(str + 1, "lmargin:", 8)) + { + idx = 9; + if(!scanforchar(str, idx, '>')) + goto textemit; + margin = dAtoi(str + 9); + mScanPos += idx + 1; +setleftmargin: + processEmitAtoms(); + U32 oldLMargin; + oldLMargin = mCurLMargin; + mCurLMargin = margin; + if(mCurLMargin >= width) + mCurLMargin = width - 1; + if(mCurX == oldLMargin) + mCurX = mCurLMargin; + if(mCurX < mCurLMargin) + mCurX = mCurLMargin; + continue; + } + + if(!dStrnicmp(str + 1, "rmargin%:", 9)) + { + idx = 10; + if(!scanforchar(str, idx, '>')) + goto textemit; + margin = (getWidth() * dAtoi(str + 10)) / 100; + mScanPos += idx + 1; + goto setrightmargin; + } + + if(!dStrnicmp(str + 1, "rmargin:", 8)) + { + idx = 9; + if(!scanforchar(str, idx, '>')) + goto textemit; + margin = dAtoi(str + 9); + mScanPos += idx + 1; +setrightmargin: + processEmitAtoms(); + mCurRMargin = margin; + if(mCurLMargin >= width) + mCurLMargin = width; + if (mCurClipX > mCurRMargin) + mCurClipX = mCurRMargin; + continue; + } + + if(!dStrnicmp(str + 1, "clip:", 5)) + { + U32 clipWidth = 0; + idx = 6; + if(!scanforchar(str, idx, '>')) + goto textemit; + clipWidth = dAtoi(str + 6); + mScanPos += idx + 1; + processEmitAtoms(); + if (clipWidth > 0) + mCurClipX = mCurX + clipWidth; + else + mCurClipX = 0; + if(mCurClipX > mCurRMargin) + mCurClipX = mCurRMargin; + continue; + } + + if(!dStrnicmp(str + 1, "/clip>", 6)) + { + processEmitAtoms(); + mCurClipX = 0; + mScanPos += 7; + continue; + } + + if(!dStrnicmp(str + 1, "div:", 4)) + { + idx = 5; + if(!scanforchar(str, idx, '>')) + goto textemit; + mScanPos += idx + 1; + mCurDiv = dAtoi(str + 5); + continue; + } + + if(!dStrnicmp(str + 1, "tab:", 4)) + { + idx = 5; + if(!scanforchar(str, idx, '>')) + goto textemit; + // scan for tab stops... + mTabStopCount = 1; + idx = 5; + while(scanforchar(str, idx, ',')) + { + idx++; + mTabStopCount++; + } + idx = 5; + mTabStops = (U32 *) mViewChunker.alloc(sizeof(U32) * mTabStopCount); + mTabStops[0] = dAtoi(str + idx); + U32 i = 1; + + while(scanforchar(str, idx, ',')) + { + idx++; + mTabStops[i] = dAtoi(str + idx); + i++; + } + mScanPos += idx + 1; + continue; + } + } + + // default case: +textemit: + textStart = mScanPos; + idx = 1; + while(mTextBuffer.getChar(mScanPos+idx) != '\t' && mTextBuffer.getChar(mScanPos+idx) != '<' && mTextBuffer.getChar(mScanPos+idx) != '\n' && mTextBuffer.getChar(mScanPos+idx)) + idx++; + len = idx; + mScanPos += idx; + emitTextToken(textStart, len); + } + processEmitAtoms(); + emitNewLine(mScanPos); + setHeight( mMaxY ); + onResize_callback(Con::getIntArg( getWidth() ), Con::getIntArg( mMaxY ) ); + + //make sure the cursor is still visible - this handles if we're a child of a scroll ctrl... + ensureCursorOnScreen(); +} + +//----------------------------------------------------------------------------- +char* GuiMLTextCtrl::stripControlChars(const char *inString) +{ + if (! bool(inString)) + return NULL; + U32 maxBufLength = 64; + char *strippedBuffer = Con::getReturnBuffer(maxBufLength); + char *stripBufPtr = &strippedBuffer[0]; + const char *bufPtr = (char *) inString; + U32 idx, sizidx; + + for(;;) + { + //if we've reached the end of the string, or run out of room in the stripped Buffer... + if(*bufPtr == '\0' || (U32(stripBufPtr - strippedBuffer) >= maxBufLength - 1)) + break; + + if (*bufPtr == '\n') + { + U32 walked; + oneUTF8toUTF32(bufPtr,&walked); + bufPtr += walked; + continue; + } + if(*bufPtr == '\t') + { + U32 walked; + oneUTF8toUTF32(bufPtr,&walked); + bufPtr += walked; + continue; + } + if(*bufPtr < 0x20 && *bufPtr >= 0) + { + U32 walked; + oneUTF8toUTF32(bufPtr,&walked); + bufPtr += walked; + continue; + } + + if(*bufPtr == '<') + { + // it's probably some kind of tag: + if(!dStrnicmp(bufPtr + 1, "font:", 5)) + { + // scan for the second colon... + // at each level it should drop out to the text case below... + idx = 6; + if(!scanforchar((char*)bufPtr, idx, ':')) + goto textemit; + + sizidx = idx + 1; + if(!scanforchar((char*)bufPtr, sizidx, '>')) + goto textemit; + + bufPtr += sizidx + 1; + continue; + } + + if (!dStrnicmp(bufPtr + 1, "tag:", 4 )) + { + idx = 5; + if ( !scanforchar((char*)bufPtr, idx, '>' )) + goto textemit; + + bufPtr += idx + 1; + continue; + } + + if(!dStrnicmp(bufPtr + 1, "color:", 6)) + { + idx = 7; + if(!scanforchar((char*)bufPtr, idx, '>')) + goto textemit; + if(idx != 13) + goto textemit; + + bufPtr += 14; + continue; + } + + if(!dStrnicmp(bufPtr +1, "bitmap:", 7)) + { + idx = 8; + if(!scanforchar((char*)bufPtr, idx, '>')) + goto textemit; + + bufPtr += idx + 1; + continue; + } + + if(!dStrnicmp(bufPtr +1, "spush>", 6)) + { + bufPtr += 7; + continue; + } + + if(!dStrnicmp(bufPtr +1, "spop>", 5)) + { + bufPtr += 6; + continue; + } + + if(!dStrnicmp(bufPtr +1, "sbreak>", 7)) + { + bufPtr += 8; + continue; + } + + if(!dStrnicmp(bufPtr +1, "just:left>", 10)) + { + bufPtr += 11; + continue; + } + + if(!dStrnicmp(bufPtr +1, "just:right>", 11)) + { + bufPtr += 12; + continue; + } + + if(!dStrnicmp(bufPtr +1, "just:center>", 12)) + { + bufPtr += 13; + continue; + } + + if(!dStrnicmp(bufPtr +1, "a:", 2)) + { + idx = 3; + if(!scanforchar((char*)bufPtr, idx, '>')) + goto textemit; + + bufPtr += idx + 1; + continue; + } + + if(!dStrnicmp(bufPtr+1, "/a>", 3)) + { + bufPtr += 4; + continue; + } + + if(!dStrnicmp(bufPtr + 1, "lmargin%:", 9)) + { + idx = 10; + if(!scanforchar((char*)bufPtr, idx, '>')) + goto textemit; + bufPtr += idx + 1; + goto setleftmargin; + } + + if(!dStrnicmp(bufPtr + 1, "lmargin:", 8)) + { + idx = 9; + if(!scanforchar((char*)bufPtr, idx, '>')) + goto textemit; + bufPtr += idx + 1; +setleftmargin: + continue; + } + + if(!dStrnicmp(bufPtr + 1, "rmargin%:", 9)) + { + idx = 10; + if(!scanforchar((char*)bufPtr, idx, '>')) + goto textemit; + bufPtr += idx + 1; + goto setrightmargin; + } + + if(!dStrnicmp(bufPtr + 1, "rmargin:", 8)) + { + idx = 9; + if(!scanforchar((char*)bufPtr, idx, '>')) + goto textemit; + bufPtr += idx + 1; +setrightmargin: + continue; + } + + if(!dStrnicmp(bufPtr + 1, "clip:", 5)) + { + idx = 6; + if(!scanforchar((char*)bufPtr, idx, '>')) + goto textemit; + bufPtr += idx + 1; + continue; + } + + if(!dStrnicmp(bufPtr + 1, "/clip>", 6)) + { + bufPtr += 7; + continue; + } + + if(!dStrnicmp(bufPtr + 1, "div:", 4)) + { + idx = 5; + if(!scanforchar((char*)bufPtr, idx, '>')) + goto textemit; + bufPtr += idx + 1; + continue; + } + + if(!dStrnicmp(bufPtr + 1, "tab:", 4)) + { + idx = 5; + if(!scanforchar((char*)bufPtr, idx, '>')) + goto textemit; + bufPtr += idx + 1; + continue; + } + } + + // default case: +textemit: + *stripBufPtr++ = *bufPtr++; + while(*bufPtr != '\t' && *bufPtr != '<' && *bufPtr != '\n' && (*bufPtr >= 0x20 || *bufPtr < 0)) + *stripBufPtr++ = *bufPtr++; + } + + //we're finished - terminate the string + *stripBufPtr = '\0'; + return strippedBuffer; +} + +//-------------------------------------------------------------------------- + +bool GuiMLTextCtrl::resize( const Point2I& newPosition, const Point2I& newExtent ) +{ + if( Parent::resize( newPosition, newExtent ) ) + { + mDirty = true; + return true; + } + + return false; +} diff --git a/Engine/source/gui/controls/guiMLTextCtrl.h b/Engine/source/gui/controls/guiMLTextCtrl.h new file mode 100644 index 000000000..f917a3dd9 --- /dev/null +++ b/Engine/source/gui/controls/guiMLTextCtrl.h @@ -0,0 +1,305 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIMLTEXTCTRL_H_ +#define _GUIMLTEXTCTRL_H_ + +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif + +#ifndef _STRINGBUFFER_H_ +#include "core/stringBuffer.h" +#endif + +class GFont; +class SFXTrack; + +GFX_DeclareTextureProfile(GFXMLTextureProfile); +class GuiMLTextCtrl : public GuiControl +{ + typedef GuiControl Parent; + + //-------------------------------------- Public interfaces... + public: + enum Justification + { + LeftJustify, + RightJustify, + CenterJustify, + }; + + struct Font { + char *faceName; + U32 faceNameLen; + U32 size; + Resource fontRes; + Font *next; + }; + + struct Bitmap { + char bitmapName[1024]; + U32 bitmapNameLen; + GFXTexHandle bitmapObject; + Bitmap *next; + }; + + struct URL + { + bool mouseDown; + U32 textStart; + U32 len; + bool noUnderline; + }; + + struct Style + { + ColorI color; + ColorI shadowColor; + ColorI linkColor; + ColorI linkColorHL; + Point2I shadowOffset; + Font *font; + bool used; + Style *next; + }; + + struct Atom + { + U32 textStart; + U32 len; + U32 xStart; + U32 yStart; + U32 width; + U32 baseLine; + U32 descent; + Style *style; + bool isClipped; + + URL *url; + Atom *next; + }; + + struct Line { + U32 y; + U32 height; + U32 divStyle; + U32 textStart; + U32 len; + Atom *atomList; + Line *next; + }; + + struct BitmapRef : public RectI + { + BitmapRef *nextBlocker; + U32 textStart; + U32 len; + Bitmap *bitmap; + BitmapRef *next; + }; + + struct LineTag { + U32 id; + S32 y; + LineTag *next; + }; + + GuiMLTextCtrl(); + ~GuiMLTextCtrl(); + + DECLARE_CALLBACK( void, onURL, (const char* url)); + DECLARE_CALLBACK( void, onResize, ( const char* width, const char* maxY )); + + // Text retrieval functions + U32 getNumChars() const; + U32 getText(char* pBuffer, const U32 bufferSize) const; + U32 getWrappedText(char* pBuffer, const U32 bufferSize) const; + const char* getTextContent(); + void insertChars(const char* inputChars, + const U32 numInputChars, + const U32 position); + + // Text substitution functions + void setText(const char* textBuffer, const U32 numChars); + void addText(const char* textBuffer, const U32 numChars, bool reformat); + + void setAlpha(F32 alpha) { mAlpha = alpha;} + + bool setCursorPosition(const S32); + void ensureCursorOnScreen(); + + // Scroll functions + void scrollToTag( U32 id ); + void scrollToTop(); + void scrollToBottom(); + + virtual void reflow(); + + DECLARE_CONOBJECT(GuiMLTextCtrl); + DECLARE_CATEGORY( "Gui Text" ); + DECLARE_DESCRIPTION( "A control that displays multiple lines of text." ); + + static void initPersistFields(); + + void setScriptValue(const char *value); + const char *getScriptValue(); + + static char *stripControlChars(const char *inString); + + //-------------------------------------- Protected Structures and constants + protected: + bool mIsEditCtrl; + + U32 *mTabStops; + U32 mTabStopCount; + U32 mCurTabStop; + + F32 mAlpha; + + DataChunker mViewChunker; + DataChunker mResourceChunker; + Line *mLineList; + Bitmap *mBitmapList; + BitmapRef *mBitmapRefList; + Font *mFontList; + LineTag *mTagList; + bool mDirty; + Style *mCurStyle; + + U32 mCurLMargin; + U32 mCurRMargin; + U32 mCurJustify; + U32 mCurDiv; + U32 mCurY; + U32 mCurClipX; + Atom *mLineAtoms; + Atom **mLineAtomPtr; + + Atom *mEmitAtoms; + Atom **mEmitAtomPtr; + + BitmapRef mSentinel; + Line **mLineInsert; + BitmapRef *mBlockList; + U32 mScanPos; + U32 mCurX; + U32 mMaxY; + URL *mCurURL; + + URL *mHitURL; + + void freeLineBuffers(); + void freeResources(); + + Bitmap *allocBitmap(const char *bitmapName, U32 bitmapNameLen); + Font *allocFont(const char *faceName, U32 faceNameLen, U32 size); + LineTag *allocLineTag(U32 id); + void emitNewLine(U32 textStart); + Atom *buildTextAtom(U32 start, U32 len, U32 left, U32 right, URL *url); + void emitTextToken(U32 textStart, U32 len); + void emitBitmapToken(Bitmap *bmp, U32 textStart, bool bitmapBreak); + void processEmitAtoms(); + Atom *splitAtomListEmit(Atom *list, U32 width); + void drawAtomText(bool sel, U32 start, U32 end, Atom *atom, Line *line, Point2I offset); + Atom *findHitAtom(const Point2I localCoords); + Style *allocStyle(Style *style); + + static const U32 csmTextBufferGrowthSize; + + //-------------------------------------- Data... + protected: + // Cursor position should always be <= mCurrTextSize + U32 mCursorPosition; + + // Actual text data. The line buffer is rebuilt from the linear text + // given a specific width. TextBuffer is /not/ \0 terminated + StringBuffer mTextBuffer; + U32 mLineStart; + S32 mMaxBufferSize; + StringTableEntry mInitialText; + + // Selection information + bool mSelectionActive; + U32 mSelectionStart; + U32 mSelectionEnd; + + U32 mVertMoveAnchor; + bool mVertMoveAnchorValid; + + S32 mSelectionAnchor; + Point2I mSelectionAnchorDropped; + + // Font resource + Resource mFont; + + // Console settable parameters + U32 mLineSpacingPixels; + bool mAllowColorChars; + bool mUseURLMouseCursor; + + // Too many chars sound: + SFXTrack* mDeniedSound; + + //-------------------------------------- Protected interface + protected: + // Inserting and deleting character blocks... + void deleteChars(const U32 rangeStart, + const U32 rangeEnd); + void copyToClipboard(const U32 rangeStart, + const U32 rangeEnd); + + // Selection maintainence + bool isSelectionActive() const; + void clearSelection(); + + // Pixel -> text position mappings + S32 getTextPosition(const Point2I& localPosition); + + // Gui control overrides + bool onWake(); + void onSleep(); + void onPreRender(); + void onRender(Point2I offset, const RectI &updateRect); + void getCursorPositionAndColor(Point2I &cursorTop, Point2I &cursorBottom, ColorI &color); + void inspectPostApply(); + void parentResized(const RectI& oldParentRect, const RectI& newParentRect); + bool onKeyDown(const GuiEvent& event); + void onMouseDown(const GuiEvent&); + void onMouseDragged(const GuiEvent&); + void onMouseUp(const GuiEvent&); + + virtual void getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent); + + public: + // Gui control overrides + bool onAdd(); + + void setSelectionStart( U32 start ) { clearSelection(); mSelectionStart = start; }; + void setSelectionEnd( U32 end ) { mSelectionEnd = end;}; + void setSelectionActive(bool active) { mSelectionActive = active; }; + S32 getCursorPosition() { return( mCursorPosition ); } + + virtual bool resize(const Point2I &newPosition, const Point2I &newExtent); +}; + +#endif // _H_GUIMLTEXTCTRL_ diff --git a/Engine/source/gui/controls/guiMLTextEditCtrl.cpp b/Engine/source/gui/controls/guiMLTextEditCtrl.cpp new file mode 100644 index 000000000..34e17f9f5 --- /dev/null +++ b/Engine/source/gui/controls/guiMLTextEditCtrl.cpp @@ -0,0 +1,468 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/controls/guiMLTextEditCtrl.h" +#include "gui/containers/guiScrollCtrl.h" +#include "gui/core/guiCanvas.h" +#include "console/consoleTypes.h" +#include "platform/event.h" +#include "core/frameAllocator.h" +#include "core/stringBuffer.h" +#include "gfx/gfxDrawUtil.h" +#include "console/engineAPI.h" + +IMPLEMENT_CONOBJECT(GuiMLTextEditCtrl); + +ConsoleDocClass( GuiMLTextEditCtrl, + "@brief A text entry control that accepts the Gui Markup Language ('ML') tags and multiple lines.\n\n" + + "@tsexample\n" + "new GuiMLTextEditCtrl()\n" + " {\n" + " lineSpacing = \"2\";\n" + " allowColorChars = \"0\";\n" + " maxChars = \"-1\";\n" + " deniedSound = \"DeniedSoundProfile\";\n" + " text = \"\";\n" + " escapeCommand = \"onEscapeScriptFunction();\";\n" + " //Properties not specific to this control have been omitted from this example.\n" + " };\n" + "@endtsexample\n\n" + + "@see GuiMLTextCtrl\n" + "@see GuiControl\n\n" + + "@ingroup GuiControls\n" +); + +//-------------------------------------------------------------------------- +GuiMLTextEditCtrl::GuiMLTextEditCtrl() +{ + mEscapeCommand = StringTable->insert( "" ); + + mIsEditCtrl = true; + + mActive = true; + + mVertMoveAnchorValid = false; +} + + +//-------------------------------------------------------------------------- +GuiMLTextEditCtrl::~GuiMLTextEditCtrl() +{ + +} + + +//-------------------------------------------------------------------------- +bool GuiMLTextEditCtrl::resize(const Point2I &newPosition, const Point2I &newExtent) +{ + // We don't want to get any smaller than our parent: + Point2I newExt = newExtent; + GuiControl* parent = getParent(); + if ( parent ) + newExt.y = getMax( parent->getHeight(), newExt.y ); + + return Parent::resize( newPosition, newExt ); +} + + +//-------------------------------------------------------------------------- +void GuiMLTextEditCtrl::initPersistFields() +{ + addField( "escapeCommand", TypeString, Offset( mEscapeCommand, GuiMLTextEditCtrl ), "Script function to run whenever the 'escape' key is pressed when this control is in focus.\n"); + Parent::initPersistFields(); +} + +//-------------------------------------------------------------------------- +void GuiMLTextEditCtrl::setFirstResponder() +{ + Parent::setFirstResponder(); + + GuiCanvas *root = getRoot(); + if (root != NULL) + { + root->enableKeyboardTranslation(); + + // If the native OS accelerator keys are not disabled + // then some key events like Delete, ctrl+V, etc may + // not make it down to us. + root->setNativeAcceleratorsEnabled( false ); + } +} + +void GuiMLTextEditCtrl::onLoseFirstResponder() +{ + GuiCanvas *root = getRoot(); + if (root != NULL) + { + root->setNativeAcceleratorsEnabled( true ); + root->disableKeyboardTranslation(); + } + + // Redraw the control: + setUpdate(); +} + +//-------------------------------------------------------------------------- +bool GuiMLTextEditCtrl::onWake() +{ + if( !Parent::onWake() ) + return false; + + getRoot()->enableKeyboardTranslation(); + return true; +} + +//-------------------------------------------------------------------------- +// Key events... +bool GuiMLTextEditCtrl::onKeyDown(const GuiEvent& event) +{ + if ( !isActive() ) + return false; + + setUpdate(); + //handle modifiers first... + if (event.modifier & SI_PRIMARY_CTRL) + { + switch(event.keyCode) + { + //copy/cut + case KEY_C: + case KEY_X: + { + //make sure we actually have something selected + if (mSelectionActive) + { + copyToClipboard(mSelectionStart, mSelectionEnd); + + //if we're cutting, also delete the selection + if (event.keyCode == KEY_X) + { + mSelectionActive = false; + deleteChars(mSelectionStart, mSelectionEnd); + mCursorPosition = mSelectionStart; + } + else + mCursorPosition = mSelectionEnd + 1; + } + return true; + } + + //paste + case KEY_V: + { + const char *clipBuf = Platform::getClipboard(); + if (dStrlen(clipBuf) > 0) + { + // Normal ascii keypress. Go ahead and add the chars... + if (mSelectionActive == true) + { + mSelectionActive = false; + deleteChars(mSelectionStart, mSelectionEnd); + mCursorPosition = mSelectionStart; + } + + insertChars(clipBuf, dStrlen(clipBuf), mCursorPosition); + } + return true; + } + + default: + break; + } + } + else if ( event.modifier & SI_SHIFT ) + { + switch ( event.keyCode ) + { + case KEY_TAB: + return( Parent::onKeyDown( event ) ); + default: + break; + } + } + else if ( event.modifier == 0 ) + { + switch (event.keyCode) + { + // Escape: + case KEY_ESCAPE: + if ( mEscapeCommand[0] ) + { + Con::evaluate( mEscapeCommand ); + return( true ); + } + return( Parent::onKeyDown( event ) ); + + // Deletion + case KEY_BACKSPACE: + case KEY_DELETE: + handleDeleteKeys(event); + return true; + + // Cursor movement + case KEY_LEFT: + case KEY_RIGHT: + case KEY_UP: + case KEY_DOWN: + case KEY_HOME: + case KEY_END: + handleMoveKeys(event); + return true; + + // Special chars... + case KEY_TAB: + // insert 3 spaces + if (mSelectionActive == true) + { + mSelectionActive = false; + deleteChars(mSelectionStart, mSelectionEnd); + mCursorPosition = mSelectionStart; + } + insertChars( "\t", 1, mCursorPosition ); + return true; + + case KEY_RETURN: + // insert carriage return + if (mSelectionActive == true) + { + mSelectionActive = false; + deleteChars(mSelectionStart, mSelectionEnd); + mCursorPosition = mSelectionStart; + } + insertChars( "\n", 1, mCursorPosition ); + return true; + + default: + break; + } + } + + if ( (mFont && mFont->isValidChar(event.ascii)) || (!mFont && event.ascii != 0) ) + { + // Normal ascii keypress. Go ahead and add the chars... + if (mSelectionActive == true) + { + mSelectionActive = false; + deleteChars(mSelectionStart, mSelectionEnd); + mCursorPosition = mSelectionStart; + } + + UTF8 *outString = NULL; + U32 outStringLen = 0; + +#ifdef TORQUE_UNICODE + + UTF16 inData[2] = { event.ascii, 0 }; + StringBuffer inBuff(inData); + + FrameTemp outBuff(4); + inBuff.getCopy8(outBuff, 4); + + outString = outBuff; + outStringLen = dStrlen(outBuff); +#else + char ascii = char(event.ascii); + outString = &ascii; + outStringLen = 1; +#endif + + insertChars(outString, outStringLen, mCursorPosition); + mVertMoveAnchorValid = false; + return true; + } + + // Otherwise, let the parent have the event... + return Parent::onKeyDown(event); +} + + +//-------------------------------------- +void GuiMLTextEditCtrl::handleDeleteKeys(const GuiEvent& event) +{ + if ( isSelectionActive() ) + { + mSelectionActive = false; + deleteChars(mSelectionStart, mSelectionEnd+1); + mCursorPosition = mSelectionStart; + } + else + { + switch ( event.keyCode ) + { + case KEY_BACKSPACE: + if (mCursorPosition != 0) + { + // delete one character left + deleteChars(mCursorPosition-1, mCursorPosition); + setUpdate(); + } + break; + + case KEY_DELETE: + if (mCursorPosition != mTextBuffer.length()) + { + // delete one character right + deleteChars(mCursorPosition, mCursorPosition+1); + setUpdate(); + } + break; + + default: + AssertFatal(false, "Unknown key code received!"); + } + } +} + + +//-------------------------------------- +void GuiMLTextEditCtrl::handleMoveKeys(const GuiEvent& event) +{ + if ( event.modifier & SI_SHIFT ) + return; + + mSelectionActive = false; + + switch ( event.keyCode ) + { + case KEY_LEFT: + mVertMoveAnchorValid = false; + // move one left + if ( mCursorPosition != 0 ) + { + mCursorPosition--; + setUpdate(); + } + break; + + case KEY_RIGHT: + mVertMoveAnchorValid = false; + // move one right + if ( mCursorPosition != mTextBuffer.length() ) + { + mCursorPosition++; + setUpdate(); + } + break; + + case KEY_UP: + case KEY_DOWN: + { + Line* walk; + for ( walk = mLineList; walk->next; walk = walk->next ) + { + if ( mCursorPosition <= ( walk->textStart + walk->len ) ) + break; + } + + if ( !walk ) + return; + + if ( event.keyCode == KEY_UP ) + { + if ( walk == mLineList ) + return; + } + else if ( walk->next == NULL ) + return; + + Point2I newPos; + newPos.set( 0, walk->y ); + + // Find the x-position: + if ( !mVertMoveAnchorValid ) + { + Point2I cursorTopP, cursorBottomP; + ColorI color; + getCursorPositionAndColor(cursorTopP, cursorBottomP, color); + mVertMoveAnchor = cursorTopP.x; + mVertMoveAnchorValid = true; + } + + newPos.x = mVertMoveAnchor; + + // Set the new y-position: + if (event.keyCode == KEY_UP) + newPos.y--; + else + newPos.y += (walk->height + 1); + + if (setCursorPosition(getTextPosition(newPos))) + mVertMoveAnchorValid = false; + break; + } + + case KEY_HOME: + case KEY_END: + { + mVertMoveAnchorValid = false; + Line* walk; + for (walk = mLineList; walk->next; walk = walk->next) + { + if (mCursorPosition <= (walk->textStart + walk->len)) + break; + } + + if (walk) + { + if (event.keyCode == KEY_HOME) + { + //place the cursor at the beginning of the first atom if there is one + if (walk->atomList) + mCursorPosition = walk->atomList->textStart; + else + mCursorPosition = walk->textStart; + } + else + { + mCursorPosition = walk->textStart; + mCursorPosition += walk->len; + } + setUpdate(); + } + break; + } + + default: + AssertFatal(false, "Unknown move key code was received!"); + } + + ensureCursorOnScreen(); +} + +//-------------------------------------------------------------------------- +void GuiMLTextEditCtrl::onRender(Point2I offset, const RectI& updateRect) +{ + Parent::onRender(offset, updateRect); + + // We are the first responder, draw our cursor in the appropriate position... + if (isFirstResponder()) + { + Point2I top, bottom; + ColorI color; + getCursorPositionAndColor(top, bottom, color); + GFX->getDrawUtil()->drawLine(top + offset, bottom + offset, mProfile->mCursorColor); + } +} + diff --git a/Engine/source/gui/controls/guiMLTextEditCtrl.h b/Engine/source/gui/controls/guiMLTextEditCtrl.h new file mode 100644 index 000000000..c4611cef8 --- /dev/null +++ b/Engine/source/gui/controls/guiMLTextEditCtrl.h @@ -0,0 +1,64 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIMLTEXTEDITCTRL_H_ +#define _GUIMLTEXTEDITCTRL_H_ + +#ifndef _GUIMLTEXTCTRL_H_ +#include "gui/controls/guiMLTextCtrl.h" +#endif + +class GuiMLTextEditCtrl : public GuiMLTextCtrl +{ + typedef GuiMLTextCtrl Parent; + + //-------------------------------------- Overrides + protected: + StringTableEntry mEscapeCommand; + + // Events + bool onKeyDown(const GuiEvent&event); + + // Event forwards + void handleMoveKeys(const GuiEvent&); + void handleDeleteKeys(const GuiEvent&); + + // rendering + void onRender(Point2I offset, const RectI &updateRect); + + public: + GuiMLTextEditCtrl(); + ~GuiMLTextEditCtrl(); + + virtual void setFirstResponder(); + virtual void onLoseFirstResponder(); + + bool onWake(); + bool resize(const Point2I &newPosition, const Point2I &newExtent); + + DECLARE_CONOBJECT(GuiMLTextEditCtrl); + DECLARE_DESCRIPTION( "A control that allows to edit multiple lines of text." ); + + static void initPersistFields(); +}; + +#endif // _H_GUIMLTEXTEDITCTRL_ diff --git a/Engine/source/gui/controls/guiMaterialCtrl.cpp b/Engine/source/gui/controls/guiMaterialCtrl.cpp new file mode 100644 index 000000000..124070b57 --- /dev/null +++ b/Engine/source/gui/controls/guiMaterialCtrl.cpp @@ -0,0 +1,173 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/controls/guiMaterialCtrl.h" + +#include "materials/baseMatInstance.h" +#include "materials/materialManager.h" +#include "materials/sceneData.h" +#include "core/util/safeDelete.h" +#include "console/console.h" +#include "console/consoleTypes.h" +#include "gfx/gfxDevice.h" +#include "math/util/matrixSet.h" +#include "scene/sceneRenderState.h" + +IMPLEMENT_CONOBJECT( GuiMaterialCtrl ); + +ConsoleDocClass( GuiMaterialCtrl, + "@brief Container for GuiMaterialPreview\n\n" + "Editor use only.\n\n" + "@internal" +); + +GuiMaterialCtrl::GuiMaterialCtrl() + : mMaterialInst( NULL ) +{ +} + +void GuiMaterialCtrl::initPersistFields() +{ + addGroup( "Material" ); + addProtectedField( "materialName", TypeStringFilename, Offset( mMaterialName, GuiMaterialCtrl ), &GuiMaterialCtrl::_setMaterial, &defaultProtectedGetFn, "" ); + endGroup( "Material" ); + + Parent::initPersistFields(); +} + +bool GuiMaterialCtrl::onWake() +{ + if ( !Parent::onWake() ) + return false; + + setActive( true ); + setMaterial( mMaterialName ); + + return true; +} + +void GuiMaterialCtrl::onSleep() +{ + SAFE_DELETE( mMaterialInst ); + + Parent::onSleep(); +} + +bool GuiMaterialCtrl::_setMaterial( void *object, const char *index, const char *data ) +{ + static_cast( object )->setMaterial( data ); + + // Return false to keep the caller from setting the field. + return false; +} + +bool GuiMaterialCtrl::setMaterial( const String &materialName ) +{ + SAFE_DELETE( mMaterialInst ); + mMaterialName = materialName; + + if ( mMaterialName.isNotEmpty() && isAwake() ) + mMaterialInst = MATMGR->createMatInstance( mMaterialName, getGFXVertexFormat() ); + + return true; +} + +void GuiMaterialCtrl::inspectPostApply() +{ + Parent::inspectPostApply(); +} + +void GuiMaterialCtrl::onRender( Point2I offset, const RectI &updateRect ) +{ + Parent::onRender( offset, updateRect ); + + if ( !mMaterialInst ) + return; + + // Draw a quad with the material assigned + GFXVertexBufferHandle verts( GFX, 4, GFXBufferTypeVolatile ); + verts.lock(); + + F32 screenLeft = updateRect.point.x; + F32 screenRight = (updateRect.point.x + updateRect.extent.x); + F32 screenTop = updateRect.point.y; + F32 screenBottom = (updateRect.point.y + updateRect.extent.y); + + const F32 fillConv = GFX->getFillConventionOffset(); + verts[0].point.set( screenLeft - fillConv, screenTop - fillConv, 0.f ); + verts[1].point.set( screenRight - fillConv, screenTop - fillConv, 0.f ); + verts[2].point.set( screenLeft - fillConv, screenBottom - fillConv, 0.f ); + verts[3].point.set( screenRight - fillConv, screenBottom - fillConv, 0.f ); + + verts[0].color = verts[1].color = verts[2].color = verts[3].color = ColorI( 255, 255, 255, 255 ); + + verts[0].texCoord.set( 0.0f, 0.0f ); + verts[1].texCoord.set( 1.0f, 0.0f ); + verts[2].texCoord.set( 0.0f, 1.0f ); + verts[3].texCoord.set( 1.0f, 1.0f ); + + verts.unlock(); + + GFX->setVertexBuffer( verts ); + + MatrixSet matSet; + matSet.setWorld(GFX->getWorldMatrix()); + matSet.setView(GFX->getViewMatrix()); + matSet.setProjection(GFX->getProjectionMatrix()); + + MatrixF cameraMatrix( true ); + F32 left, right, top, bottom, nearPlane, farPlane; + bool isOrtho; + GFX->getFrustum( &left, &right, &bottom, &top, &nearPlane, &farPlane, &isOrtho ); + Frustum frust( isOrtho, left, right, top, bottom, nearPlane, farPlane, cameraMatrix ); + + SceneRenderState state + ( + gClientSceneGraph, + SPT_Diffuse, + SceneCameraState( GFX->getViewport(), frust, GFX->getWorldMatrix(), GFX->getProjectionMatrix() ), + gClientSceneGraph->getDefaultRenderPass(), + false + ); + + SceneData sgData; + sgData.init( &state ); + sgData.wireframe = false; // Don't wireframe this. + + while( mMaterialInst->setupPass( &state, sgData ) ) + { + mMaterialInst->setSceneInfo( &state, sgData ); + mMaterialInst->setTransforms( matSet, &state ); + GFX->drawPrimitive( GFXTriangleStrip, 0, 2 ); + } + + // Clean up + GFX->setShader( NULL ); + GFX->setTexture( 0, NULL ); +} + +ConsoleMethod( GuiMaterialCtrl, setMaterial, bool, 3, 3, "( string materialName )" + "Set the material to be displayed in the control." ) +{ + return object->setMaterial( argv[2] ); +} diff --git a/Engine/source/gui/controls/guiMaterialCtrl.h b/Engine/source/gui/controls/guiMaterialCtrl.h new file mode 100644 index 000000000..6e8bc5df5 --- /dev/null +++ b/Engine/source/gui/controls/guiMaterialCtrl.h @@ -0,0 +1,67 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIMATERIALCTRL_H_ +#define _GUIMATERIALCTRL_H_ + +#ifndef _GUICONTAINER_H_ +#include "gui/containers/guiContainer.h" +#endif + +class BaseMatInstance; + + +/// +class GuiMaterialCtrl : public GuiContainer +{ +private: + typedef GuiContainer Parent; + +protected: + + String mMaterialName; + + BaseMatInstance *mMaterialInst; + + static bool _setMaterial( void *object, const char *index, const char *data ); + +public: + + GuiMaterialCtrl(); + + // ConsoleObject + static void initPersistFields(); + void inspectPostApply(); + + DECLARE_CONOBJECT(GuiMaterialCtrl); + DECLARE_CATEGORY( "Gui Editor" ); + + // GuiControl + bool onWake(); + void onSleep(); + + bool setMaterial( const String &materialName ); + + void onRender( Point2I offset, const RectI &updateRect ); +}; + +#endif // _GUIMATERIALCTRL_H_ diff --git a/Engine/source/gui/controls/guiPopUpCtrl.cpp b/Engine/source/gui/controls/guiPopUpCtrl.cpp new file mode 100644 index 000000000..e4b2903f7 --- /dev/null +++ b/Engine/source/gui/controls/guiPopUpCtrl.cpp @@ -0,0 +1,1535 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/core/guiCanvas.h" +#include "gui/controls/guiPopUpCtrl.h" +#include "console/consoleTypes.h" +#include "gui/core/guiDefaultControlRender.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxDrawUtil.h" +#include "console/engineAPI.h" + +static ColorI colorWhite(255,255,255); // Added + +// Function to return the number of columns in 'string' given delimeters in 'set' +static U32 getColumnCount(const char *string, const char *set) +{ + U32 count = 0; + U8 last = 0; + while(*string) + { + last = *string++; + + for(U32 i =0; set[i]; i++) + { + if(last == set[i]) + { + count++; + last = 0; + break; + } + } + } + if(last) + count++; + return count; +} + +// Function to return the 'index' column from 'string' given delimeters in 'set' +static const char *getColumn(const char *string, char* returnbuff, U32 index, const char *set) +{ + U32 sz; + while(index--) + { + if(!*string) + return ""; + sz = dStrcspn(string, set); + if (string[sz] == 0) + return ""; + string += (sz + 1); + } + sz = dStrcspn(string, set); + if (sz == 0) + return ""; + char *ret = returnbuff; + dStrncpy(ret, string, sz); + ret[sz] = '\0'; + return ret; +} + +GuiPopUpBackgroundCtrl::GuiPopUpBackgroundCtrl(GuiPopUpMenuCtrl *ctrl, GuiPopupTextListCtrl *textList) +{ + mPopUpCtrl = ctrl; + mTextList = textList; +} + +void GuiPopUpBackgroundCtrl::onMouseDown(const GuiEvent &event) +{ + mPopUpCtrl->mBackgroundCancel = true; // Set that the user didn't click within the text list. Replaces the line above. + mPopUpCtrl->closePopUp(); +} + +//------------------------------------------------------------------------------ +GuiPopupTextListCtrl::GuiPopupTextListCtrl() +{ + mPopUpCtrl = NULL; +} + + +//------------------------------------------------------------------------------ +GuiPopupTextListCtrl::GuiPopupTextListCtrl(GuiPopUpMenuCtrl *ctrl) +{ + mPopUpCtrl = ctrl; +} + +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +//void GuiPopUpTextListCtrl::onCellSelected( Point2I /*cell*/ ) +//{ +// // Do nothing, the parent control will take care of everything... +//} +void GuiPopupTextListCtrl::onCellSelected( Point2I cell ) +{ + // The old function is above. This new one will only call the the select + // functions if we were not cancelled by a background click. + + // Check if we were cancelled by the user clicking on the Background ie: anywhere + // other than within the text list. + if(mPopUpCtrl->mBackgroundCancel) + return; + + if( isMethod( "onSelect" ) ) + Con::executef(this, "onSelect", Con::getFloatArg(cell.x), Con::getFloatArg(cell.y)); + + //call the console function + execConsoleCallback(); + //if (mConsoleCommand[0]) + // Con::evaluate(mConsoleCommand, false); + +} + +//------------------------------------------------------------------------------ +bool GuiPopupTextListCtrl::onKeyDown(const GuiEvent &event) +{ + //if the control is a dead end, don't process the input: + if ( !mVisible || !mActive || !mAwake ) + return false; + + //see if the key down is a or not + if ( event.modifier == 0 ) + { + if ( event.keyCode == KEY_RETURN ) + { + mPopUpCtrl->closePopUp(); + return true; + } + else if ( event.keyCode == KEY_ESCAPE ) + { + mSelectedCell.set( -1, -1 ); + mPopUpCtrl->closePopUp(); + return true; + } + } + + //otherwise, pass the event to it's parent + return Parent::onKeyDown(event); +} + +void GuiPopupTextListCtrl::onMouseUp(const GuiEvent &event) +{ + Parent::onMouseUp( event ); + mPopUpCtrl->closePopUp(); +} + +//------------------------------------------------------------------------------ +void GuiPopupTextListCtrl::onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver) +{ + Point2I size; + getCellSize( size ); + + // Render a background color for the cell + if ( mouseOver ) + { + RectI cellR( offset.x, offset.y, size.x, size.y ); + GFX->getDrawUtil()->drawRectFill( cellR, mProfile->mFillColorHL ); + + } + else if ( selected ) + { + RectI cellR( offset.x, offset.y, size.x, size.y ); + GFX->getDrawUtil()->drawRectFill( cellR, mProfile->mFillColorSEL ); + } + + // Define the default x offset for the text + U32 textXOffset = offset.x + mProfile->mTextOffset.x; + + // Do we also draw a colored box beside the text? + ColorI boxColor; + bool drawbox = mPopUpCtrl->getColoredBox( boxColor, mList[cell.y].id); + if(drawbox) + { + Point2I coloredboxsize(15,10); + RectI r(offset.x + mProfile->mTextOffset.x, offset.y+2, coloredboxsize.x, coloredboxsize.y); + GFX->getDrawUtil()->drawRectFill( r, boxColor); + GFX->getDrawUtil()->drawRect( r, ColorI(0,0,0)); + + textXOffset += coloredboxsize.x + mProfile->mTextOffset.x; + } + + ColorI fontColor; + mPopUpCtrl->getFontColor( fontColor, mList[cell.y].id, selected, mouseOver ); + + GFX->getDrawUtil()->setBitmapModulation( fontColor ); + //GFX->drawText( mFont, Point2I( offset.x + 4, offset.y ), mList[cell.y].text ); + + // Get the number of columns in the cell + S32 colcount = getColumnCount(mList[cell.y].text, "\t"); + + // Are there two or more columns? + if(colcount >= 2) + { + char buff[256]; + + // Draw the first column + getColumn(mList[cell.y].text, buff, 0, "\t"); + GFX->getDrawUtil()->drawText( mFont, Point2I( textXOffset, offset.y ), buff ); // Used mTextOffset as a margin for the text list rather than the hard coded value of '4'. + + // Draw the second column to the right + getColumn(mList[cell.y].text, buff, 1, "\t"); + S32 txt_w = mFont->getStrWidth(buff); + + GFX->getDrawUtil()->drawText( mFont, Point2I( offset.x+size.x-mProfile->mTextOffset.x-txt_w, offset.y ), buff ); // Used mTextOffset as a margin for the text list rather than the hard coded value of '4'. + + } else + { + GFX->getDrawUtil()->drawText( mFont, Point2I( textXOffset, offset.y ), mList[cell.y].text ); // Used mTextOffset as a margin for the text list rather than the hard coded value of '4'. + } +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +IMPLEMENT_CONOBJECT(GuiPopUpMenuCtrl); + +ConsoleDocClass( GuiPopUpMenuCtrl, + "@brief A control that allows to select a value from a drop-down list.\n\n" + + "For a nearly identical GUI with additional features, use GuiPopUpMenuCtrlEx.\n\n" + + "@tsexample\n" + "new GuiPopUpMenuCtrl()\n" + "{\n" + " maxPopupHeight = \"200\";\n" + " sbUsesNAColor = \"0\";\n" + " reverseTextList = \"0\";\n" + " bitmapBounds = \"16 16\";\n" + " maxLength = \"1024\";\n" + " position = \"56 31\";\n" + " extent = \"64 64\";\n" + " minExtent = \"8 2\";\n" + " profile = \"GuiPopUpMenuProfile\";\n" + " tooltipProfile = \"GuiToolTipProfile\";\n" + "};\n" + "@endtsexample\n\n" + + "@note This is definitely going to be deprecated soon.\n\n" + + "@see GuiPopUpMenuCtrlEx for more features and better explanations.\n" + + "@ingroup GuiControls\n"); + +GuiPopUpMenuCtrl::GuiPopUpMenuCtrl(void) +{ + VECTOR_SET_ASSOCIATION(mEntries); + VECTOR_SET_ASSOCIATION(mSchemes); + + mSelIndex = -1; + mActive = true; + mMaxPopupHeight = 200; + mScrollDir = GuiScrollCtrl::None; + mScrollCount = 0; + mLastYvalue = 0; + mIncValue = 0; + mRevNum = 0; + mInAction = false; + mMouseOver = false; // Added + mRenderScrollInNA = false; // Added + mBackgroundCancel = false; // Added + mReverseTextList = false; // Added - Don't reverse text list if displaying up + mBitmapName = StringTable->insert(""); // Added + mBitmapBounds.set(16, 16); // Added + mIdMax = -1; +} + +//------------------------------------------------------------------------------ +GuiPopUpMenuCtrl::~GuiPopUpMenuCtrl() +{ +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrl::initPersistFields(void) +{ + addField("maxPopupHeight", TypeS32, Offset(mMaxPopupHeight, GuiPopUpMenuCtrl)); + addField("sbUsesNAColor", TypeBool, Offset(mRenderScrollInNA, GuiPopUpMenuCtrl)); + addField("reverseTextList", TypeBool, Offset(mReverseTextList, GuiPopUpMenuCtrl)); + addField("bitmap", TypeFilename, Offset(mBitmapName, GuiPopUpMenuCtrl)); + addField("bitmapBounds", TypePoint2I, Offset(mBitmapBounds, GuiPopUpMenuCtrl)); + + Parent::initPersistFields(); +} + +//------------------------------------------------------------------------------ +ConsoleMethod( GuiPopUpMenuCtrl, add, void, 3, 5, "(string name, int idNum, int scheme=0)") +{ + if ( argc == 4 ) + object->addEntry(argv[2],dAtoi(argv[3])); + if ( argc == 5 ) + object->addEntry(argv[2],dAtoi(argv[3]),dAtoi(argv[4])); + else + object->addEntry(argv[2]); +} + +ConsoleMethod( GuiPopUpMenuCtrl, addScheme, void, 6, 6, "(int id, ColorI fontColor, ColorI fontColorHL, ColorI fontColorSEL)") +{ + ColorI fontColor, fontColorHL, fontColorSEL; + U32 r, g, b; + char buf[64]; + + dStrcpy( buf, argv[3] ); + char* temp = dStrtok( buf, " \0" ); + r = temp ? dAtoi( temp ) : 0; + temp = dStrtok( NULL, " \0" ); + g = temp ? dAtoi( temp ) : 0; + temp = dStrtok( NULL, " \0" ); + b = temp ? dAtoi( temp ) : 0; + fontColor.set( r, g, b ); + + dStrcpy( buf, argv[4] ); + temp = dStrtok( buf, " \0" ); + r = temp ? dAtoi( temp ) : 0; + temp = dStrtok( NULL, " \0" ); + g = temp ? dAtoi( temp ) : 0; + temp = dStrtok( NULL, " \0" ); + b = temp ? dAtoi( temp ) : 0; + fontColorHL.set( r, g, b ); + + dStrcpy( buf, argv[5] ); + temp = dStrtok( buf, " \0" ); + r = temp ? dAtoi( temp ) : 0; + temp = dStrtok( NULL, " \0" ); + g = temp ? dAtoi( temp ) : 0; + temp = dStrtok( NULL, " \0" ); + b = temp ? dAtoi( temp ) : 0; + fontColorSEL.set( r, g, b ); + + object->addScheme( dAtoi( argv[2] ), fontColor, fontColorHL, fontColorSEL ); +} + +ConsoleMethod( GuiPopUpMenuCtrl, getText, const char*, 2, 2, "") +{ + return object->getText(); +} + +ConsoleMethod( GuiPopUpMenuCtrl, clear, void, 2, 2, "Clear the popup list.") +{ + object->clear(); +} + +//FIXME: clashes with SimSet.sort +ConsoleMethod(GuiPopUpMenuCtrl, sort, void, 2, 2, "Sort the list alphabetically.") +{ + object->sort(); +} + +// Added to sort the entries by ID +ConsoleMethod(GuiPopUpMenuCtrl, sortID, void, 2, 2, "Sort the list by ID.") +{ + object->sortID(); +} + +ConsoleMethod( GuiPopUpMenuCtrl, forceOnAction, void, 2, 2, "") +{ + object->onAction(); +} + +ConsoleMethod( GuiPopUpMenuCtrl, forceClose, void, 2, 2, "") +{ + object->closePopUp(); +} + +ConsoleMethod( GuiPopUpMenuCtrl, getSelected, S32, 2, 2, "") +{ + return object->getSelected(); +} + +ConsoleMethod( GuiPopUpMenuCtrl, setSelected, void, 3, 4, "(int id, [scriptCallback=true])") +{ + if( argc > 3 ) + object->setSelected( dAtoi( argv[2] ), dAtob( argv[3] ) ); + else + object->setSelected( dAtoi( argv[2] ) ); +} + +ConsoleMethod( GuiPopUpMenuCtrl, setFirstSelected, void, 2, 3, "([scriptCallback=true])") +{ + if( argc > 2 ) + object->setFirstSelected( dAtob( argv[2] ) ); + else + object->setFirstSelected(); +} + +ConsoleMethod( GuiPopUpMenuCtrl, setNoneSelected, void, 2, 2, "") +{ + object->setNoneSelected(); +} + +ConsoleMethod( GuiPopUpMenuCtrl, getTextById, const char*, 3, 3, "(int id)") +{ + return(object->getTextById(dAtoi(argv[2]))); +} + +ConsoleMethod( GuiPopUpMenuCtrl, changeTextById, void, 4, 4, "( int id, string text )" ) +{ + object->setEntryText( dAtoi( argv[ 2 ] ), argv[ 3 ] ); +} + +ConsoleMethod( GuiPopUpMenuCtrl, setEnumContent, void, 4, 4, "(string class, string enum)" + "This fills the popup with a classrep's field enumeration type info.\n\n" + "More of a helper function than anything. If console access to the field list is added, " + "at least for the enumerated types, then this should go away..") +{ + AbstractClassRep * classRep = AbstractClassRep::getClassList(); + + // walk the class list to get our class + while(classRep) + { + if(!dStricmp(classRep->getClassName(), argv[2])) + break; + classRep = classRep->getNextClass(); + } + + // get it? + if(!classRep) + { + Con::warnf(ConsoleLogEntry::General, "failed to locate class rep for '%s'", argv[2]); + return; + } + + // walk the fields to check for this one (findField checks StringTableEntry ptrs...) + U32 i; + for(i = 0; i < classRep->mFieldList.size(); i++) + if(!dStricmp(classRep->mFieldList[i].pFieldname, argv[3])) + break; + + // found it? + if(i == classRep->mFieldList.size()) + { + Con::warnf(ConsoleLogEntry::General, "failed to locate field '%s' for class '%s'", argv[3], argv[2]); + return; + } + + const AbstractClassRep::Field & field = classRep->mFieldList[i]; + ConsoleBaseType* conType = ConsoleBaseType::getType( field.type ); + + // check the type + if( !conType->getEnumTable() ) + { + Con::warnf(ConsoleLogEntry::General, "field '%s' is not an enumeration for class '%s'", argv[3], argv[2]); + return; + } + + // fill it + const EngineEnumTable& table = *( conType->getEnumTable() ); + const U32 numValues = table.getNumValues(); + + for(i = 0; i < numValues; i++) + object->addEntry( table[i].getName(), table[i] ); +} + +//------------------------------------------------------------------------------ +ConsoleMethod( GuiPopUpMenuCtrl, findText, S32, 3, 3, "(string text)" + "Returns the position of the first entry containing the specified text.") +{ + return( object->findText( argv[2] ) ); +} + +//------------------------------------------------------------------------------ +ConsoleMethod( GuiPopUpMenuCtrl, size, S32, 2, 2, "Get the size of the menu - the number of entries in it.") +{ + return( object->getNumEntries() ); +} + +//------------------------------------------------------------------------------ +ConsoleMethod( GuiPopUpMenuCtrl, replaceText, void, 3, 3, "(bool doReplaceText)") +{ + object->replaceText(dAtoi(argv[2])); +} + +//------------------------------------------------------------------------------ +// Added +bool GuiPopUpMenuCtrl::onWake() +{ + if ( !Parent::onWake() ) + return false; + + // Set the bitmap for the popup. + setBitmap( mBitmapName ); + + // Now update the Form Control's bitmap array, and possibly the child's too + mProfile->constructBitmapArray(); + + if ( mProfile->getChildrenProfile() ) + mProfile->getChildrenProfile()->constructBitmapArray(); + + return true; +} + +//------------------------------------------------------------------------------ +bool GuiPopUpMenuCtrl::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + mSelIndex = -1; + mReplaceText = true; + return true; +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrl::onSleep() +{ + mTextureNormal = NULL; // Added + mTextureDepressed = NULL; // Added + Parent::onSleep(); + closePopUp(); // Tests in function. +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrl::clear() +{ + mEntries.setSize(0); + setText(""); + mSelIndex = -1; + mRevNum = 0; + mIdMax = -1; +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrl::clearEntry( S32 entry ) +{ + if( entry == -1 ) + return; + + U32 i = 0; + for ( ; i < mEntries.size(); i++ ) + { + if ( mEntries[i].id == entry ) + break; + } + + mEntries.erase( i ); + + if( mEntries.size() <= 0 ) + { + mEntries.setSize(0); + setText(""); + mSelIndex = -1; + mRevNum = 0; + } + else + { + if (entry < mSelIndex) + { + mSelIndex--; + } + else if( entry == mSelIndex ) + { + setText(""); + mSelIndex = -1; + } + } +} + +//------------------------------------------------------------------------------ +ConsoleMethod( GuiPopUpMenuCtrl, clearEntry, void, 3, 3, "(S32 entry)") +{ + object->clearEntry(dAtoi(argv[2])); +} + +//------------------------------------------------------------------------------ +static S32 QSORT_CALLBACK textCompare(const void *a,const void *b) +{ + GuiPopUpMenuCtrl::Entry *ea = (GuiPopUpMenuCtrl::Entry *) (a); + GuiPopUpMenuCtrl::Entry *eb = (GuiPopUpMenuCtrl::Entry *) (b); + return (dStrnatcasecmp(ea->buf, eb->buf)); +} + +// Added to sort by entry ID +//------------------------------------------------------------------------------ +static S32 QSORT_CALLBACK idCompare(const void *a,const void *b) +{ + GuiPopUpMenuCtrl::Entry *ea = (GuiPopUpMenuCtrl::Entry *) (a); + GuiPopUpMenuCtrl::Entry *eb = (GuiPopUpMenuCtrl::Entry *) (b); + return ( (ea->id < eb->id) ? -1 : ((ea->id > eb->id) ? 1 : 0) ); +} + +//------------------------------------------------------------------------------ +// Added +void GuiPopUpMenuCtrl::setBitmap( const char *name ) +{ + mBitmapName = StringTable->insert( name ); + if ( !isAwake() ) + return; + + if ( *mBitmapName ) + { + char buffer[1024]; + char *p; + dStrcpy(buffer, name); + p = buffer + dStrlen(buffer); + + dStrcpy(p, "_n"); + mTextureNormal = GFXTexHandle( (StringTableEntry)buffer, &GFXDefaultGUIProfile, avar("%s() - mTextureNormal (line %d)", __FUNCTION__, __LINE__) ); + + dStrcpy(p, "_d"); + mTextureDepressed = GFXTexHandle( (StringTableEntry)buffer, &GFXDefaultGUIProfile, avar("%s() - mTextureDepressed (line %d)", __FUNCTION__, __LINE__) ); + if ( !mTextureDepressed ) + mTextureDepressed = mTextureNormal; + } + else + { + mTextureNormal = NULL; + mTextureDepressed = NULL; + } + setUpdate(); +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrl::sort() +{ + S32 selId = getSelected(); + + S32 size = mEntries.size(); + if( size > 0 ) + dQsort( mEntries.address(), size, sizeof(Entry), textCompare); + + if( selId != -1 ) + setSelected( selId, false ); +} + +// Added to sort by entry ID +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrl::sortID() +{ + S32 selId = getSelected(); + + S32 size = mEntries.size(); + if( size > 0 ) + dQsort( mEntries.address(), size, sizeof(Entry), idCompare); + + if( selId != -1 ) + setSelected( selId, false ); +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrl::addEntry( const char *buf, S32 id, U32 scheme ) +{ + if( !buf ) + { + //Con::printf( "GuiPopupMenuCtrlEx::addEntry - Invalid buffer!" ); + return; + } + + // Ensure that there are no other entries with exactly the same name + for ( U32 i = 0; i < mEntries.size(); i++ ) + { + if ( dStrcmp( mEntries[i].buf, buf ) == 0 ) + return; + } + + // If we don't give an id, create one from mIdMax + if( id == -1 ) + id = mIdMax + 1; + + // Increase mIdMax when an id is greater than it + if( id > mIdMax ) + mIdMax = id; + + Entry e; + dStrcpy( e.buf, buf ); + e.id = id; + e.scheme = scheme; + + // see if there is a shortcut key + char * cp = dStrchr( e.buf, '~' ); + e.ascii = cp ? cp[1] : 0; + + // See if there is a colour box defined with the text + char *cb = dStrchr( e.buf, '|' ); + if ( cb ) + { + e.usesColorBox = true; + cb[0] = '\0'; + + char* red = &cb[1]; + cb = dStrchr(red, '|'); + cb[0] = '\0'; + char* green = &cb[1]; + cb = dStrchr(green, '|'); + cb[0] = '\0'; + char* blue = &cb[1]; + + U32 r = dAtoi(red); + U32 g = dAtoi(green); + U32 b = dAtoi(blue); + + e.colorbox = ColorI(r,g,b); + + } + else + { + e.usesColorBox = false; + } + + mEntries.push_back(e); + + if ( mInAction && mTl ) + { + // Add the new entry: + mTl->addEntry( e.id, e.buf ); + repositionPopup(); + } +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrl::addScheme( U32 id, ColorI fontColor, ColorI fontColorHL, ColorI fontColorSEL ) +{ + if ( !id ) + return; + + Scheme newScheme; + newScheme.id = id; + newScheme.fontColor = fontColor; + newScheme.fontColorHL = fontColorHL; + newScheme.fontColorSEL = fontColorSEL; + + mSchemes.push_back( newScheme ); +} + +//------------------------------------------------------------------------------ +S32 GuiPopUpMenuCtrl::getSelected() +{ + if (mSelIndex == -1) + return 0; + return mEntries[mSelIndex].id; +} + +//------------------------------------------------------------------------------ +bool GuiPopUpMenuCtrl::setEntryText( S32 id, const char* buf ) +{ + const U32 numEntries = getNumEntries(); + for( U32 i = 0; i < numEntries; i++ ) + { + if( mEntries[ i ].id == id ) + { + Entry& entry = mEntries[ i ]; + dStrncpy( entry.buf, buf, sizeof( entry.buf ) ); + entry.buf[ sizeof( entry.buf ) - 1 ] = '\0'; + return true; + } + } + + return false; +} + +//------------------------------------------------------------------------------ +const char* GuiPopUpMenuCtrl::getTextById(S32 id) +{ + for ( U32 i = 0; i < mEntries.size(); i++ ) + { + if ( mEntries[i].id == id ) + return( mEntries[i].buf ); + } + + return( "" ); +} + +//------------------------------------------------------------------------------ +S32 GuiPopUpMenuCtrl::findText( const char* text ) +{ + for ( U32 i = 0; i < mEntries.size(); i++ ) + { + if ( dStrcmp( text, mEntries[i].buf ) == 0 ) + return( mEntries[i].id ); + } + return( -1 ); +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrl::setSelected(S32 id, bool bNotifyScript ) +{ + for( S32 i = 0; i < mEntries.size(); i++ ) + { + if( id == mEntries[i].id ) + { + i = ( mRevNum > i ) ? mRevNum - i : i; + mSelIndex = i; + + if( mReplaceText ) // Only change the displayed text if appropriate. + setText( mEntries[ i ].buf ); + + // Now perform the popup action: + + if( bNotifyScript ) + { + if( isMethod( "onSelect" ) ) + Con::executef( this, "onSelect", Con::getIntArg( mEntries[ mSelIndex ].id ), mEntries[mSelIndex].buf ); + + execConsoleCallback(); + } + + return; + } + } + + if( mReplaceText ) // Only change the displayed text if appropriate. + { + setText(""); + } + mSelIndex = -1; + + if( bNotifyScript && isMethod( "onCancel" ) ) + Con::executef( this, "onCancel" ); + + if( id == -1 ) + return; + + // Execute the popup console command: + if( bNotifyScript ) + execConsoleCallback(); +} + +//------------------------------------------------------------------------------ +// Added to set the first item as selected. +void GuiPopUpMenuCtrl::setFirstSelected( bool bNotifyScript ) +{ + if( mEntries.size() > 0 ) + { + mSelIndex = 0; + if ( mReplaceText ) // Only change the displayed text if appropriate. + { + setText( mEntries[0].buf ); + } + + // Execute the popup console command: + if( bNotifyScript ) + { + if ( isMethod( "onSelect" ) ) + Con::executef( this, "onSelect", Con::getIntArg( mEntries[ mSelIndex ].id ), mEntries[mSelIndex].buf ); + + execConsoleCallback(); + } + } + else + { + if ( mReplaceText ) // Only change the displayed text if appropriate. + setText(""); + + mSelIndex = -1; + + if( bNotifyScript ) + { + Con::executef( this, "onCancel" ); + execConsoleCallback(); + } + } +} + +//------------------------------------------------------------------------------ +// Added to set no items as selected. +void GuiPopUpMenuCtrl::setNoneSelected() +{ + if ( mReplaceText ) // Only change the displayed text if appropriate. + { + setText(""); + } + mSelIndex = -1; +} + +//------------------------------------------------------------------------------ +const char *GuiPopUpMenuCtrl::getScriptValue() +{ + return getText(); +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrl::onRender( Point2I offset, const RectI &updateRect ) +{ + TORQUE_UNUSED(updateRect); + Point2I localStart; + + if ( mScrollDir != GuiScrollCtrl::None ) + autoScroll(); + + RectI r( offset, getExtent() ); + if ( mInAction ) + { + S32 l = r.point.x, r2 = r.point.x + r.extent.x - 1; + S32 t = r.point.y, b = r.point.y + r.extent.y - 1; + + // Do we render a bitmap border or lines? + if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() ) + { + // Render the fixed, filled in border + renderFixedBitmapBordersFilled(r, 3, mProfile ); + + } + else + { + //renderSlightlyLoweredBox(r, mProfile); + GFX->getDrawUtil()->drawRectFill( r, mProfile->mFillColor ); + } + + // Draw a bitmap over the background? + if ( mTextureDepressed ) + { + RectI rect(offset, mBitmapBounds); + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapStretch( mTextureDepressed, rect ); + } + else if ( mTextureNormal ) + { + RectI rect(offset, mBitmapBounds); + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapStretch( mTextureNormal, rect ); + } + + // Do we render a bitmap border or lines? + if ( !( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() ) ) + { + GFX->getDrawUtil()->drawLine( l, t, l, b, colorWhite ); + GFX->getDrawUtil()->drawLine( l, t, r2, t, colorWhite ); + GFX->getDrawUtil()->drawLine( l + 1, b, r2, b, mProfile->mBorderColor ); + GFX->getDrawUtil()->drawLine( r2, t + 1, r2, b - 1, mProfile->mBorderColor ); + } + + } + else + // TODO: Implement + // TODO: Add onMouseEnter() and onMouseLeave() and a definition of mMouseOver (see guiButtonBaseCtrl) for this to work. + if ( mMouseOver ) + { + S32 l = r.point.x, r2 = r.point.x + r.extent.x - 1; + S32 t = r.point.y, b = r.point.y + r.extent.y - 1; + + // Do we render a bitmap border or lines? + if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() ) + { + // Render the fixed, filled in border + renderFixedBitmapBordersFilled( r, 2, mProfile ); + + } + else + { + GFX->getDrawUtil()->drawRectFill( r, mProfile->mFillColorHL ); + } + + // Draw a bitmap over the background? + if ( mTextureNormal ) + { + RectI rect( offset, mBitmapBounds ); + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapStretch( mTextureNormal, rect ); + } + + // Do we render a bitmap border or lines? + if ( !( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() ) ) + { + GFX->getDrawUtil()->drawLine( l, t, l, b, colorWhite ); + GFX->getDrawUtil()->drawLine( l, t, r2, t, colorWhite ); + GFX->getDrawUtil()->drawLine( l + 1, b, r2, b, mProfile->mBorderColor ); + GFX->getDrawUtil()->drawLine( r2, t + 1, r2, b - 1, mProfile->mBorderColor ); + } + } + else + { + // Do we render a bitmap border or lines? + if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() ) + { + // Render the fixed, filled in border + renderFixedBitmapBordersFilled( r, 1, mProfile ); + } + else + { + GFX->getDrawUtil()->drawRectFill( r, mProfile->mFillColorNA ); + } + + // Draw a bitmap over the background? + if ( mTextureNormal ) + { + RectI rect(offset, mBitmapBounds); + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapStretch( mTextureNormal, rect ); + } + + // Do we render a bitmap border or lines? + if ( !( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() ) ) + { + GFX->getDrawUtil()->drawRect( r, mProfile->mBorderColorNA ); + } + } + // renderSlightlyRaisedBox(r, mProfile); // Used to be the only 'else' condition to mInAction above. + + S32 txt_w = mProfile->mFont->getStrWidth(mText); + localStart.x = 0; + localStart.y = (getHeight() - (mProfile->mFont->getHeight())) / 2; + + // align the horizontal + switch (mProfile->mAlignment) + { + case GuiControlProfile::RightJustify: + if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() ) + { + // We're making use of a bitmap border, so take into account the + // right cap of the border. + RectI* mBitmapBounds = mProfile->mBitmapArrayRects.address(); + localStart.x = getWidth() - mBitmapBounds[2].extent.x - txt_w; + } + else + { + localStart.x = getWidth() - txt_w; + } + break; + case GuiControlProfile::CenterJustify: + if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() ) + { + // We're making use of a bitmap border, so take into account the + // right cap of the border. + RectI* mBitmapBounds = mProfile->mBitmapArrayRects.address(); + localStart.x = (getWidth() - mBitmapBounds[2].extent.x - txt_w) / 2; + + } else + { + localStart.x = (getWidth() - txt_w) / 2; + } + break; + default: + // GuiControlProfile::LeftJustify + if ( txt_w > getWidth() ) + { + // The width of the text is greater than the width of the control. + // In this case we will right justify the text and leave some space + // for the down arrow. + if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() ) + { + // We're making use of a bitmap border, so take into account the + // right cap of the border. + RectI* mBitmapBounds = mProfile->mBitmapArrayRects.address(); + localStart.x = getWidth() - mBitmapBounds[2].extent.x - txt_w; + } + else + { + localStart.x = getWidth() - txt_w - 12; + } + } + else + { + localStart.x = mProfile->mTextOffset.x; // Use mProfile->mTextOffset as a controlable margin for the control's text. + } + break; + } + + // Do we first draw a coloured box beside the text? + ColorI boxColor; + bool drawbox = getColoredBox( boxColor, mSelIndex); + if ( drawbox ) + { + Point2I coloredboxsize( 15, 10 ); + RectI r( offset.x + mProfile->mTextOffset.x, offset.y + ( (getHeight() - coloredboxsize.y ) / 2 ), coloredboxsize.x, coloredboxsize.y ); + GFX->getDrawUtil()->drawRectFill( r, boxColor); + GFX->getDrawUtil()->drawRect( r, ColorI(0,0,0)); + + localStart.x += coloredboxsize.x + mProfile->mTextOffset.x; + } + + // Draw the text + Point2I globalStart = localToGlobalCoord( localStart ); + ColorI fontColor = mActive ? ( mInAction ? mProfile->mFontColor : mProfile->mFontColorNA ) : mProfile->mFontColorNA; + GFX->getDrawUtil()->setBitmapModulation( fontColor ); // was: (mProfile->mFontColor); + + // Get the number of columns in the text + S32 colcount = getColumnCount( mText, "\t" ); + + // Are there two or more columns? + if ( colcount >= 2 ) + { + char buff[256]; + + // Draw the first column + getColumn( mText, buff, 0, "\t" ); + GFX->getDrawUtil()->drawText( mProfile->mFont, globalStart, buff, mProfile->mFontColors ); + + // Draw the second column to the right + getColumn( mText, buff, 1, "\t" ); + S32 txt_w = mProfile->mFont->getStrWidth( buff ); + if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() ) + { + // We're making use of a bitmap border, so take into account the + // right cap of the border. + RectI* mBitmapBounds = mProfile->mBitmapArrayRects.address(); + Point2I textpos = localToGlobalCoord( Point2I( getWidth() - txt_w - mBitmapBounds[2].extent.x, localStart.y ) ); + GFX->getDrawUtil()->drawText( mProfile->mFont, textpos, buff, mProfile->mFontColors ); + + } else + { + Point2I textpos = localToGlobalCoord( Point2I( getWidth() - txt_w - 12, localStart.y ) ); + GFX->getDrawUtil()->drawText( mProfile->mFont, textpos, buff, mProfile->mFontColors ); + } + + } else + { + GFX->getDrawUtil()->drawText( mProfile->mFont, globalStart, mText, mProfile->mFontColors ); + } + + // If we're rendering a bitmap border, then it will take care of the arrow. + if ( !(mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size()) ) + { + // Draw a triangle (down arrow) + S32 left = r.point.x + r.extent.x - 12; + S32 right = left + 8; + S32 middle = left + 4; + S32 top = r.extent.y / 2 + r.point.y - 4; + S32 bottom = top + 8; + + PrimBuild::color( mProfile->mFontColor ); + + PrimBuild::begin( GFXTriangleList, 3 ); + PrimBuild::vertex2fv( Point3F( (F32)left, (F32)top, 0.0f ) ); + PrimBuild::vertex2fv( Point3F( (F32)right, (F32)top, 0.0f ) ); + PrimBuild::vertex2fv( Point3F( (F32)middle, (F32)bottom, 0.0f ) ); + PrimBuild::end(); + } +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrl::closePopUp() +{ + if ( !mInAction ) + return; + + // Get the selection from the text list: + + if( !mBackgroundCancel ) + { + mSelIndex = mTl->getSelectedCell().y; + mSelIndex = ( mRevNum >= mSelIndex && mSelIndex != -1 ) ? mRevNum - mSelIndex : mSelIndex; + if ( mSelIndex != -1 ) + { + if ( mReplaceText ) + setText( mEntries[mSelIndex].buf ); + setIntVariable( mEntries[mSelIndex].id ); + } + } + + // Release the mouse: + mInAction = false; + mTl->mouseUnlock(); + + // Now perform the popup action: + if( mSelIndex != -1 && !mBackgroundCancel ) + { + if ( isMethod( "onSelect" ) ) + Con::executef( this, "onSelect", Con::getIntArg( mEntries[ mSelIndex ].id ), mEntries[mSelIndex].buf ); + + // Execute the popup console command: + execConsoleCallback(); + } + else if ( isMethod( "onCancel" ) ) + Con::executef( this, "onCancel" ); + + // Pop the background: + GuiCanvas *root = getRoot(); + if ( root ) + root->popDialogControl(mBackground); + + // Kill the popup: + mBackground->removeObject( mSc ); + mTl->deleteObject(); + mSc->deleteObject(); + mBackground->deleteObject(); + + mBackground = NULL; + mTl = NULL; + mSc = NULL; + + // Set this as the first responder: + setFirstResponder(); +} + +//------------------------------------------------------------------------------ +bool GuiPopUpMenuCtrl::onKeyDown(const GuiEvent &event) +{ + //if the control is a dead end, don't process the input: + if ( !mVisible || !mActive || !mAwake ) + return false; + + //see if the key down is a or not + if ( event.keyCode == KEY_RETURN && event.modifier == 0 ) + { + onAction(); + return true; + } + + //otherwise, pass the event to its parent + return Parent::onKeyDown( event ); +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrl::onAction() +{ + GuiControl *canCtrl = getParent(); + + addChildren(); + + GuiCanvas *root = getRoot(); + Point2I windowExt = root->getExtent(); + + mBackground->resize( Point2I(0,0), root->getExtent() ); + + S32 textWidth = 0, width = getWidth(); + const S32 textSpace = 2; + bool setScroll = false; + + for ( U32 i = 0; i < mEntries.size(); ++i ) + if ( S32(mProfile->mFont->getStrWidth( mEntries[i].buf )) > textWidth ) + textWidth = mProfile->mFont->getStrWidth( mEntries[i].buf ); + + S32 sbWidth = mSc->getControlProfile()->mBorderThickness * 2 + mSc->scrollBarThickness(); // Calculate the scroll bar width + if ( textWidth > ( getWidth() - sbWidth-mProfile->mTextOffset.x - mSc->getChildMargin().x * 2 ) ) // The text draw area to test against is the width of the drop-down minus the scroll bar width, the text margin and the scroll bar child margins. + { + textWidth +=sbWidth + mProfile->mTextOffset.x + mSc->getChildMargin().x * 2; // The new width is the width of the text plus the scroll bar width plus the text margin size plus the scroll bar child margins. + width = textWidth; + + // If a child margin is not defined for the scroll control, let's add + // some space between the text and scroll control for readability + if(mSc->getChildMargin().x == 0) + width += textSpace; + } + + mTl->setCellSize(Point2I(width, mProfile->mFont->getHeight() + textSpace)); + + for ( U32 j = 0; j < mEntries.size(); ++j ) + mTl->addEntry( mEntries[j].id, mEntries[j].buf ); + + if ( mSelIndex >= 0 ) + mTl->setSelectedCell( Point2I( 0, mSelIndex ) ); + + Point2I pointInGC = canCtrl->localToGlobalCoord( getPosition() ); + Point2I scrollPoint( pointInGC.x, pointInGC.y + getHeight() ); + + //Calc max Y distance, so Scroll Ctrl will fit on window + + S32 sbBorder = mSc->getControlProfile()->mBorderThickness * 2 + mSc->getChildMargin().y * 2; + S32 maxYdis = windowExt.y - pointInGC.y - getHeight() - sbBorder; + + //If scroll bars need to be added + mRevNum = 0; + if ( maxYdis < mTl->getHeight() + sbBorder ) + { + //Should we pop menu list above the button + if ( maxYdis < pointInGC.y ) + { + if(mReverseTextList) + reverseTextList(); + + maxYdis = pointInGC.y; + //Does the menu need a scroll bar + if ( maxYdis < mTl->getHeight() + sbBorder ) + { + setScroll = true; + } + //No scroll bar needed + else + { + maxYdis = mTl->getHeight() + sbBorder; + } + + // Added the next two lines + scrollPoint.set(pointInGC.x, pointInGC.y - maxYdis); // Used to have the following on the end: '-1);' + } + //Scroll bar needed but Don't pop above button + else + { + setScroll = true; + } + } + //No scroll bar needed + else + { + maxYdis = mTl->getHeight() + sbBorder; + } + + RectI newBounds = mSc->getBounds(); + + //offset it from the background so it lines up properly + newBounds.point = mBackground->globalToLocalCoord( scrollPoint ); + + if ( newBounds.point.x + width > mBackground->getWidth() ) + if ( width - getWidth() > 0 ) + newBounds.point.x -= width - getWidth(); + + newBounds.extent.set( width, maxYdis ); + mSc->setBounds( newBounds ); + + mSc->registerObject(); + mTl->registerObject(); + mBackground->registerObject(); + + mSc->addObject( mTl ); + mBackground->addObject( mSc ); + + mBackgroundCancel = false; // Setup check if user clicked on the background instead of the text list (ie: didn't want to change their current selection). + + root->pushDialogControl( mBackground, 99 ); + + if ( setScroll ) + { + // Resize the text list + Point2I cellSize; + mTl->getCellSize( cellSize ); + cellSize.x = width - mSc->scrollBarThickness() - sbBorder; + mTl->setCellSize( cellSize ); + mTl->setWidth( cellSize.x ); + + if ( mSelIndex ) + mTl->scrollCellVisible( Point2I( 0, mSelIndex ) ); + else + mTl->scrollCellVisible( Point2I( 0, 0 ) ); + } + + mTl->setFirstResponder(); + + mInAction = true; +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrl::addChildren() +{ + // Create Text List. + mTl = new GuiPopupTextListCtrl( this ); + AssertFatal( mTl, "Failed to create the GuiPopUpTextListCtrl for the PopUpMenu" ); + // Use the children's profile rather than the parent's profile, if it exists. + mTl->setControlProfile( mProfile->getChildrenProfile() ? mProfile->getChildrenProfile() : mProfile ); + mTl->setField("noDuplicates", "false"); + + mSc = new GuiScrollCtrl; + AssertFatal( mSc, "Failed to create the GuiScrollCtrl for the PopUpMenu" ); + GuiControlProfile *prof; + if ( Sim::findObject( "GuiScrollProfile", prof ) ) + { + mSc->setControlProfile( prof ); + } + else + { + // Use the children's profile rather than the parent's profile, if it exists. + mSc->setControlProfile( mProfile->getChildrenProfile() ? mProfile->getChildrenProfile() : mProfile ); + } + + mSc->setField( "hScrollBar", "AlwaysOff" ); + mSc->setField( "vScrollBar", "dynamic" ); + //if(mRenderScrollInNA) // Force the scroll control to render using fillColorNA rather than fillColor + // mSc->mUseNABackground = true; + + mBackground = new GuiPopUpBackgroundCtrl( this, mTl ); + AssertFatal( mBackground, "Failed to create the GuiBackgroundCtrl for the PopUpMenu" ); +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrl::repositionPopup() +{ + if ( !mInAction || !mSc || !mTl ) + return; + + // I'm not concerned with this right now... +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrl::reverseTextList() +{ + mTl->clear(); + for ( S32 i = mEntries.size()-1; i >= 0; --i ) + mTl->addEntry( mEntries[i].id, mEntries[i].buf ); + + // Don't lose the selected cell: + if ( mSelIndex >= 0 ) + mTl->setSelectedCell( Point2I( 0, mEntries.size() - mSelIndex - 1 ) ); + + mRevNum = mEntries.size() - 1; +} + +//------------------------------------------------------------------------------ +bool GuiPopUpMenuCtrl::getFontColor( ColorI &fontColor, S32 id, bool selected, bool mouseOver ) +{ + U32 i; + Entry* entry = NULL; + for ( i = 0; i < mEntries.size(); i++ ) + { + if ( mEntries[i].id == id ) + { + entry = &mEntries[i]; + break; + } + } + + if ( !entry ) + return( false ); + + if ( entry->scheme != 0 ) + { + // Find the entry's color scheme: + for ( i = 0; i < mSchemes.size(); i++ ) + { + if ( mSchemes[i].id == entry->scheme ) + { + fontColor = selected ? mSchemes[i].fontColorSEL : mouseOver ? mSchemes[i].fontColorHL : mSchemes[i].fontColor; + return( true ); + } + } + } + + // Default color scheme... + fontColor = selected ? mProfile->mFontColorSEL : mouseOver ? mProfile->mFontColorHL : mProfile->mFontColorNA; // Modified the final color choice from mProfile->mFontColor to mProfile->mFontColorNA + + return( true ); +} + +//------------------------------------------------------------------------------ +// Added +bool GuiPopUpMenuCtrl::getColoredBox( ColorI &fontColor, S32 id ) +{ + U32 i; + Entry* entry = NULL; + for ( i = 0; i < mEntries.size(); i++ ) + { + if ( mEntries[i].id == id ) + { + entry = &mEntries[i]; + break; + } + } + + if ( !entry ) + return false; + + if ( entry->usesColorBox == false ) + return false; + + fontColor = entry->colorbox; + + return true; +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrl::onMouseDown( const GuiEvent &event ) +{ + TORQUE_UNUSED(event); + + if( !mVisible || !mActive || !mAwake ) + return; + + onAction(); +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrl::onMouseUp( const GuiEvent &event ) +{ + TORQUE_UNUSED(event); +} + +//------------------------------------------------------------------------------ +// Added +void GuiPopUpMenuCtrl::onMouseEnter( const GuiEvent &event ) +{ + mMouseOver = true; +} + +//------------------------------------------------------------------------------ +// Added +void GuiPopUpMenuCtrl::onMouseLeave( const GuiEvent &event ) +{ + mMouseOver = false; +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrl::setupAutoScroll( const GuiEvent &event ) +{ + GuiControl *parent = getParent(); + if ( !parent ) + return; + + Point2I mousePt = mSc->globalToLocalCoord( event.mousePoint ); + + mEventSave = event; + + if ( mLastYvalue != mousePt.y ) + { + mScrollDir = GuiScrollCtrl::None; + if ( mousePt.y > mSc->getHeight() || mousePt.y < 0 ) + { + S32 topOrBottom = ( mousePt.y > mSc->getHeight() ) ? 1 : 0; + mSc->scrollTo( 0, topOrBottom ); + return; + } + + F32 percent = (F32)mousePt.y / (F32)mSc->getHeight(); + if ( percent > 0.7f && mousePt.y > mLastYvalue ) + { + mIncValue = percent - 0.5f; + mScrollDir = GuiScrollCtrl::DownArrow; + } + else if ( percent < 0.3f && mousePt.y < mLastYvalue ) + { + mIncValue = 0.5f - percent; + mScrollDir = GuiScrollCtrl::UpArrow; + } + mLastYvalue = mousePt.y; + } +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrl::autoScroll() +{ + mScrollCount += mIncValue; + + while ( mScrollCount > 1 ) + { + mSc->autoScroll( mScrollDir ); + mScrollCount -= 1; + } + mTl->onMouseMove( mEventSave ); +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrl::replaceText(S32 boolVal) +{ + mReplaceText = boolVal; +} diff --git a/Engine/source/gui/controls/guiPopUpCtrl.h b/Engine/source/gui/controls/guiPopUpCtrl.h new file mode 100644 index 000000000..741fc523a --- /dev/null +++ b/Engine/source/gui/controls/guiPopUpCtrl.h @@ -0,0 +1,174 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIPOPUPCTRL_H_ +#define _GUIPOPUPCTRL_H_ + +#ifndef _GUITEXTCTRL_H_ +#include "gui/controls/guiTextCtrl.h" +#endif +#ifndef _GUITEXTLISTCTRL_H_ +#include "gui/controls/guiTextListCtrl.h" +#endif +#ifndef _GUIBUTTONCTRL_H_ +#include "gui/buttons/guiButtonCtrl.h" +#endif +#ifndef _GUIBACKGROUNDCTRL_H_ +#include "gui/controls/guiBackgroundCtrl.h" +#endif +#ifndef _GUISCROLLCTRL_H_ +#include "gui/containers/guiScrollCtrl.h" +#endif +class GuiPopUpMenuCtrl; +class GuiPopupTextListCtrl; + +class GuiPopUpBackgroundCtrl : public GuiControl +{ +protected: + GuiPopUpMenuCtrl *mPopUpCtrl; + GuiPopupTextListCtrl *mTextList; +public: + GuiPopUpBackgroundCtrl(GuiPopUpMenuCtrl *ctrl, GuiPopupTextListCtrl* textList); + void onMouseDown(const GuiEvent &event); +}; + +class GuiPopupTextListCtrl : public GuiTextListCtrl +{ + private: + typedef GuiTextListCtrl Parent; + +protected: + GuiPopUpMenuCtrl *mPopUpCtrl; + +public: + GuiPopupTextListCtrl(); // for inheritance + GuiPopupTextListCtrl(GuiPopUpMenuCtrl *ctrl); + + // GuiArrayCtrl overload: + void onCellSelected(Point2I cell); + + // GuiControl overloads: + bool onKeyDown(const GuiEvent &event); + void onMouseUp(const GuiEvent &event); + void onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver); +}; + +class GuiPopUpMenuCtrl : public GuiTextCtrl +{ + typedef GuiTextCtrl Parent; + +public: + struct Entry + { + char buf[256]; + S32 id; + U16 ascii; + U16 scheme; + bool usesColorBox; // Added + ColorI colorbox; // Added + }; + + struct Scheme + { + U32 id; + ColorI fontColor; + ColorI fontColorHL; + ColorI fontColorSEL; + }; + + bool mBackgroundCancel; // Added + +protected: + GuiPopupTextListCtrl *mTl; + GuiScrollCtrl *mSc; + GuiPopUpBackgroundCtrl *mBackground; + Vector mEntries; + Vector mSchemes; + S32 mSelIndex; + S32 mMaxPopupHeight; + F32 mIncValue; + F32 mScrollCount; + S32 mLastYvalue; + GuiEvent mEventSave; + S32 mRevNum; + bool mInAction; + bool mReplaceText; + bool mMouseOver; // Added + bool mRenderScrollInNA; // Added + bool mReverseTextList; // Added - Should we reverse the text list if we display up? + StringTableEntry mBitmapName; // Added + Point2I mBitmapBounds; // Added + GFXTexHandle mTextureNormal; // Added + GFXTexHandle mTextureDepressed; // Added + S32 mIdMax; + + virtual void addChildren(); + virtual void repositionPopup(); + +public: + GuiPopUpMenuCtrl(void); + ~GuiPopUpMenuCtrl(); + GuiScrollCtrl::Region mScrollDir; + bool onWake(); // Added + bool onAdd(); + void onSleep(); + void setBitmap(const char *name); // Added + void sort(); + void sortID(); // Added + void addEntry(const char *buf, S32 id = -1, U32 scheme = 0); + void addScheme(U32 id, ColorI fontColor, ColorI fontColorHL, ColorI fontColorSEL); + void onRender(Point2I offset, const RectI &updateRect); + void onAction(); + virtual void closePopUp(); + void clear(); + void clearEntry( S32 entry ); // Added + void onMouseDown(const GuiEvent &event); + void onMouseUp(const GuiEvent &event); + void onMouseEnter(const GuiEvent &event); // Added + void onMouseLeave(const GuiEvent &); // Added + void setupAutoScroll(const GuiEvent &event); + void autoScroll(); + bool onKeyDown(const GuiEvent &event); + void reverseTextList(); + bool getFontColor(ColorI &fontColor, S32 id, bool selected, bool mouseOver); + bool getColoredBox(ColorI &boxColor, S32 id); // Added + bool setEntryText( S32 id, const char* buf ); + + S32 getSelected(); + void setSelected(S32 id, bool bNotifyScript = true); + void setFirstSelected(bool bNotifyScript = true); // Added + void setNoneSelected(); // Added + const char *getScriptValue(); + const char *getTextById(S32 id); + S32 findText( const char* text ); + S32 getNumEntries() { return( mEntries.size() ); } + void replaceText(S32); + + DECLARE_CONOBJECT( GuiPopUpMenuCtrl ); + DECLARE_CATEGORY( "Gui Lists" ); + DECLARE_DESCRIPTION( "A control that allows to select a value from a drop-down list." ); + + static void initPersistFields(void); + +}; + +#endif //_GUI_POPUPMENU_CTRL_H diff --git a/Engine/source/gui/controls/guiPopUpCtrlEx.cpp b/Engine/source/gui/controls/guiPopUpCtrlEx.cpp new file mode 100644 index 000000000..275faf75c --- /dev/null +++ b/Engine/source/gui/controls/guiPopUpCtrlEx.cpp @@ -0,0 +1,1728 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/core/guiCanvas.h" +#include "gui/controls/guiPopUpCtrlEx.h" +#include "console/consoleTypes.h" +#include "gui/core/guiDefaultControlRender.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxDrawUtil.h" +#include "console/engineAPI.h" + +ConsoleDocClass( GuiPopUpMenuCtrlEx, + "@brief A control that allows to select a value from a drop-down list.\n\n" + + "This is essentially a GuiPopUpMenuCtrl, but with quite a few more features.\n\n" + + "@tsexample\n" + "new GuiPopUpMenuCtrlEx()\n" + "{\n" + " maxPopupHeight = \"200\";\n" + " sbUsesNAColor = \"0\";\n" + " reverseTextList = \"0\";\n" + " bitmapBounds = \"16 16\";\n" + " hotTrackCallback = \"0\";\n" + " extent = \"64 64\";\n" + " profile = \"GuiDefaultProfile\";\n" + " tooltipProfile = \"GuiToolTipProfile\";\n" + "};\n" + "@endtsexample\n\n" + + "@see GuiPopUpMenuCtrl\n" + + "@ingroup GuiControls\n"); + +static ColorI colorWhite(255,255,255); // Added + +// Function to return the number of columns in 'string' given delimeters in 'set' +static U32 getColumnCount(const char *string, const char *set) +{ + U32 count = 0; + U8 last = 0; + while(*string) + { + last = *string++; + + for(U32 i =0; set[i]; i++) + { + if(last == set[i]) + { + count++; + last = 0; + break; + } + } + } + if(last) + count++; + return count; +} + +// Function to return the 'index' column from 'string' given delimeters in 'set' +static const char *getColumn(const char *string, char* returnbuff, U32 index, const char *set) +{ + U32 sz; + while(index--) + { + if(!*string) + return ""; + sz = dStrcspn(string, set); + if (string[sz] == 0) + return ""; + string += (sz + 1); + } + sz = dStrcspn(string, set); + if (sz == 0) + return ""; + char *ret = returnbuff; + dStrncpy(ret, string, sz); + ret[sz] = '\0'; + return ret; +} + +GuiPopUpBackgroundCtrlEx::GuiPopUpBackgroundCtrlEx(GuiPopUpMenuCtrlEx *ctrl, GuiPopupTextListCtrlEx *textList) +{ + mPopUpCtrl = ctrl; + mTextList = textList; +} + +void GuiPopUpBackgroundCtrlEx::onMouseDown(const GuiEvent &event) +{ + // mTextList->setSelectedCell(Point2I(-1,-1)); // Removed + mPopUpCtrl->mBackgroundCancel = true; // Set that the user didn't click within the text list. Replaces the line above. + mPopUpCtrl->closePopUp(); +} + +//------------------------------------------------------------------------------ +GuiPopupTextListCtrlEx::GuiPopupTextListCtrlEx() +{ + mPopUpCtrl = NULL; +} + + +//------------------------------------------------------------------------------ +GuiPopupTextListCtrlEx::GuiPopupTextListCtrlEx(GuiPopUpMenuCtrlEx *ctrl) +{ + mPopUpCtrl = ctrl; +} + +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +//void GuiPopUpTextListCtrl::onCellSelected( Point2I /*cell*/ ) +//{ +// // Do nothing, the parent control will take care of everything... +//} +void GuiPopupTextListCtrlEx::onCellSelected( Point2I cell ) +{ + // The old function is above. This new one will only call the the select + // functions if we were not cancelled by a background click. + + // Check if we were cancelled by the user clicking on the Background ie: anywhere + // other than within the text list. + if(mPopUpCtrl->mBackgroundCancel) + return; + + if( isMethod( "onSelect" ) ) + Con::executef(this, "onSelect", Con::getFloatArg(cell.x), Con::getFloatArg(cell.y)); + + //call the console function + execConsoleCallback(); + //if (mConsoleCommand[0]) + // Con::evaluate(mConsoleCommand, false); + +} + +bool GuiPopupTextListCtrlEx::hasCategories() +{ + for( S32 i = 0; i < mList.size(); i++) + { + if( mList[i].id == -1) + return true; + } + + return false; +} + +//------------------------------------------------------------------------------ +bool GuiPopupTextListCtrlEx::onKeyDown(const GuiEvent &event) +{ + //if the control is a dead end, don't process the input: + if ( !mVisible || !mActive || !mAwake ) + return false; + + //see if the key down is a or not + if ( event.modifier == 0 ) + { + if ( event.keyCode == KEY_RETURN ) + { + mPopUpCtrl->closePopUp(); + return true; + } + else if ( event.keyCode == KEY_ESCAPE ) + { + mSelectedCell.set( -1, -1 ); + mPopUpCtrl->closePopUp(); + return true; + } + } + + //otherwise, pass the event to it's parent + return Parent::onKeyDown(event); +} + +void GuiPopupTextListCtrlEx::onMouseUp(const GuiEvent &event) +{ + Point2I pt = globalToLocalCoord(event.mousePoint); + pt.x -= mHeaderDim.x; pt.y -= mHeaderDim.y; + Point2I cell( + (pt.x < 0 ? -1 : pt.x / mCellSize.x), + (pt.y < 0 ? -1 : pt.y / mCellSize.y) + ); + + if (cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y) + { + if (mList[cell.y].id == -1) + return; + } + + Parent::onMouseUp(event); + mPopUpCtrl->closePopUp(); +} + +void GuiPopupTextListCtrlEx::onMouseMove( const GuiEvent &event ) +{ + if( !mPopUpCtrl || !mPopUpCtrl->isMethod("onHotTrackItem") ) + return Parent::onMouseMove( event ); + + Point2I pt = globalToLocalCoord(event.mousePoint); + pt.x -= mHeaderDim.x; pt.y -= mHeaderDim.y; + Point2I cell( (pt.x < 0 ? -1 : pt.x / mCellSize.x), (pt.y < 0 ? -1 : pt.y / mCellSize.y) ); + + // Within Bounds? + if (cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y) + // Hot Track notification + Con::executef( mPopUpCtrl, "onHotTrackItem", Con::getIntArg(mList[cell.y].id) ); + else + // Hot Track -1 + Con::executef( mPopUpCtrl, "onHotTrackItem", Con::getIntArg(-1) ); + + // Call Parent + Parent::onMouseMove(event); +} + +//------------------------------------------------------------------------------ +void GuiPopupTextListCtrlEx::onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver) +{ + Point2I size; + getCellSize( size ); + + // Render a background color for the cell + if ( mouseOver ) + { + RectI cellR( offset.x, offset.y, size.x, size.y ); + GFX->getDrawUtil()->drawRectFill( cellR, mProfile->mFillColorHL ); + } + else if ( selected ) + { + RectI cellR( offset.x, offset.y, size.x, size.y ); + GFX->getDrawUtil()->drawRectFill( cellR, mProfile->mFillColorSEL ); + } + + // Define the default x offset for the text + U32 textXOffset = offset.x + mProfile->mTextOffset.x; + + // Do we also draw a colored box beside the text? + ColorI boxColor; + bool drawbox = mPopUpCtrl->getColoredBox( boxColor, mList[cell.y].id ); + if ( drawbox ) + { + Point2I coloredboxsize( 15, 10 ); + RectI r( offset.x + mProfile->mTextOffset.x, offset.y + 2, coloredboxsize.x, coloredboxsize.y ); + GFX->getDrawUtil()->drawRectFill( r, boxColor ); + GFX->getDrawUtil()->drawRect( r, ColorI( 0, 0, 0 ) ); + + textXOffset += coloredboxsize.x + mProfile->mTextOffset.x; + } + + // Render 'Group' background + if ( mList[cell.y].id == -1) + { + RectI cellR( offset.x, offset.y, size.x, size.y ); + GFX->getDrawUtil()->drawRectFill( cellR, mProfile->mFillColorHL ); + } + + ColorI fontColor; + mPopUpCtrl->getFontColor( fontColor, mList[cell.y].id, selected, mouseOver ); + + GFX->getDrawUtil()->setBitmapModulation( fontColor ); + //GFX->drawText( mFont, Point2I( offset.x + 4, offset.y ), mList[cell.y].text ); + + // Get the number of columns in the cell + S32 colcount = getColumnCount(mList[cell.y].text, "\t"); + + // Are there two or more columns? + if(colcount >= 2) + { + char buff[256]; + + // Draw the first column + getColumn(mList[cell.y].text, buff, 0, "\t"); + GFX->getDrawUtil()->drawText( mFont, Point2I( textXOffset, offset.y ), buff ); // Used mTextOffset as a margin for the text list rather than the hard coded value of '4'. + + // Draw the second column to the right + getColumn(mList[cell.y].text, buff, 1, "\t"); + S32 txt_w = mFont->getStrWidth(buff); + + GFX->getDrawUtil()->drawText( mFont, Point2I( offset.x+size.x-mProfile->mTextOffset.x-txt_w, offset.y ), buff ); // Used mTextOffset as a margin for the text list rather than the hard coded value of '4'. + + } else + { + if ((mList[cell.y].id == -1) || (!hasCategories())) + GFX->getDrawUtil()->drawText( mFont, Point2I( textXOffset, offset.y ), mList[cell.y].text ); // Used mTextOffset as a margin for the text list rather than the hard coded value of '4'. + else + GFX->getDrawUtil()->drawText( mFont, Point2I( textXOffset + 8, offset.y ), mList[cell.y].text ); // Used mTextOffset as a margin for the text list rather than the hard coded value of '4'. + } +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +IMPLEMENT_CONOBJECT(GuiPopUpMenuCtrlEx); + +GuiPopUpMenuCtrlEx::GuiPopUpMenuCtrlEx(void) +{ + VECTOR_SET_ASSOCIATION(mEntries); + VECTOR_SET_ASSOCIATION(mSchemes); + + mSelIndex = -1; + mActive = true; + mMaxPopupHeight = 200; + mScrollDir = GuiScrollCtrl::None; + mScrollCount = 0; + mLastYvalue = 0; + mIncValue = 0; + mRevNum = 0; + mInAction = false; + mMouseOver = false; // Added + mRenderScrollInNA = false; // Added + mBackgroundCancel = false; // Added + mReverseTextList = false; // Added - Don't reverse text list if displaying up + mBitmapName = StringTable->insert(""); // Added + mBitmapBounds.set(16, 16); // Added + mHotTrackItems = false; + mIdMax = -1; +} + +//------------------------------------------------------------------------------ +GuiPopUpMenuCtrlEx::~GuiPopUpMenuCtrlEx() +{ +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrlEx::initPersistFields(void) +{ + addField("maxPopupHeight", TypeS32, Offset(mMaxPopupHeight, GuiPopUpMenuCtrlEx), "Length of menu when it extends"); + addField("sbUsesNAColor", TypeBool, Offset(mRenderScrollInNA, GuiPopUpMenuCtrlEx), "Deprecated" "@internal"); + addField("reverseTextList", TypeBool, Offset(mReverseTextList, GuiPopUpMenuCtrlEx), "Reverses text list if popup extends up, instead of down"); + addField("bitmap", TypeFilename, Offset(mBitmapName, GuiPopUpMenuCtrlEx), "File name of bitmap to use"); + addField("bitmapBounds", TypePoint2I, Offset(mBitmapBounds, GuiPopUpMenuCtrlEx), "Boundaries of bitmap displayed"); + addField("hotTrackCallback", TypeBool, Offset(mHotTrackItems, GuiPopUpMenuCtrlEx), + "Whether to provide a 'onHotTrackItem' callback when a list item is hovered over"); + + Parent::initPersistFields(); +} + +//------------------------------------------------------------------------------ +ConsoleDocFragment _GuiPopUpMenuCtrlExAdd( + "@brief Adds an entry to the list\n\n" + "@param name String containing the name of the entry\n" + "@param idNum Numerical value assigned to the name\n" + "@param scheme Optional ID associated with a scheme " + "for font coloring, highlight coloring, and selection coloring\n\n", + "GuiPopUpMenuCtrlEx", + "void add(string name, S32 idNum, S32 scheme=0);" +); + +ConsoleMethod( GuiPopUpMenuCtrlEx, add, void, 3, 5, "(string name, int idNum, int scheme=0)") +{ + if ( argc == 4 ) + object->addEntry(argv[2],dAtoi(argv[3])); + if ( argc == 5 ) + object->addEntry(argv[2],dAtoi(argv[3]),dAtoi(argv[4])); + else + object->addEntry(argv[2]); +} + +DefineEngineMethod( GuiPopUpMenuCtrlEx, addCategory, void, (const char* text),, + "@brief Add a category to the list.\n\n" + + "Acts as a separator between entries, allowing for sub-lists\n\n" + + "@param text Name of the new category\n\n") +{ + object->addEntry(text, -1, 0); +} + +DefineEngineMethod( GuiPopUpMenuCtrlEx, addScheme, void, (S32 id, ColorI fontColor, ColorI fontColorHL, ColorI fontColorSEL),, + "@brief Create a new scheme and add it to the list of choices for when a new text entry is added.\n\n" + "@param id Numerical id associated with this scheme\n" + "@param fontColor The base text font color. Formatted as \"Red Green Blue\", each a numerical between 0 and 255.\n" + "@param fontColorHL Color of text when being highlighted. Formatted as \"Red Green Blue\", each a numerical between 0 and 255.\n" + "@param fontColorSel Color of text when being selected. Formatted as \"Red Green Blue\", each a numerical between 0 and 255.\n") +{ + /*ColorI fontColor, fontColorHL, fontColorSEL; + U32 r, g, b; + char buf[64]; + + dStrcpy( buf, argv[3] ); + char* temp = dStrtok( buf, " \0" ); + r = temp ? dAtoi( temp ) : 0; + temp = dStrtok( NULL, " \0" ); + g = temp ? dAtoi( temp ) : 0; + temp = dStrtok( NULL, " \0" ); + b = temp ? dAtoi( temp ) : 0; + fontColor.set( r, g, b ); + + dStrcpy( buf, argv[4] ); + temp = dStrtok( buf, " \0" ); + r = temp ? dAtoi( temp ) : 0; + temp = dStrtok( NULL, " \0" ); + g = temp ? dAtoi( temp ) : 0; + temp = dStrtok( NULL, " \0" ); + b = temp ? dAtoi( temp ) : 0; + fontColorHL.set( r, g, b ); + + dStrcpy( buf, argv[5] ); + temp = dStrtok( buf, " \0" ); + r = temp ? dAtoi( temp ) : 0; + temp = dStrtok( NULL, " \0" ); + g = temp ? dAtoi( temp ) : 0; + temp = dStrtok( NULL, " \0" ); + b = temp ? dAtoi( temp ) : 0; + fontColorSEL.set( r, g, b );*/ + + object->addScheme( id, fontColor, fontColorHL, fontColorSEL ); +} + +//ConsoleMethod( GuiPopUpMenuCtrlEx, addScheme, void, 6, 6, "(int id, ColorI fontColor, ColorI fontColorHL, ColorI fontColorSEL)") +//{ +// ColorI fontColor, fontColorHL, fontColorSEL; +// U32 r, g, b; +// char buf[64]; +// +// dStrcpy( buf, argv[3] ); +// char* temp = dStrtok( buf, " \0" ); +// r = temp ? dAtoi( temp ) : 0; +// temp = dStrtok( NULL, " \0" ); +// g = temp ? dAtoi( temp ) : 0; +// temp = dStrtok( NULL, " \0" ); +// b = temp ? dAtoi( temp ) : 0; +// fontColor.set( r, g, b ); +// +// dStrcpy( buf, argv[4] ); +// temp = dStrtok( buf, " \0" ); +// r = temp ? dAtoi( temp ) : 0; +// temp = dStrtok( NULL, " \0" ); +// g = temp ? dAtoi( temp ) : 0; +// temp = dStrtok( NULL, " \0" ); +// b = temp ? dAtoi( temp ) : 0; +// fontColorHL.set( r, g, b ); +// +// dStrcpy( buf, argv[5] ); +// temp = dStrtok( buf, " \0" ); +// r = temp ? dAtoi( temp ) : 0; +// temp = dStrtok( NULL, " \0" ); +// g = temp ? dAtoi( temp ) : 0; +// temp = dStrtok( NULL, " \0" ); +// b = temp ? dAtoi( temp ) : 0; +// fontColorSEL.set( r, g, b ); +// +// object->addScheme( dAtoi( argv[2] ), fontColor, fontColorHL, fontColorSEL ); +//} + +DefineEngineMethod( GuiPopUpMenuCtrlEx, setText, void, ( const char* text),, + "@brief Set the current text to a specified value.\n\n" + "@param text String containing new text to set\n\n") +{ + object->setText(text); +} + +DefineEngineMethod( GuiPopUpMenuCtrlEx, getText, const char*, (),, + "@brief Get the.\n\n" + + "Detailed description\n\n" + + "@param param Description\n\n" + + "@tsexample\n" + "// Comment\n" + "code();\n" + "@endtsexample\n\n" + + "@return Returns current text in string format") +{ + return object->getText(); +} + +DefineEngineMethod( GuiPopUpMenuCtrlEx, clear, void, (),, + "@brief Clear the popup list.\n\n") +{ + object->clear(); +} + +DefineEngineMethod( GuiPopUpMenuCtrlEx, sort, void, (),, + "@brief Sort the list alphabetically.\n\n") +{ + object->sort(); +} + +DefineEngineMethod( GuiPopUpMenuCtrlEx, sortID, void, (),, + "@brief Sort the list by ID.\n\n") +{ + object->sortID(); +} + +DefineEngineMethod( GuiPopUpMenuCtrlEx, forceOnAction, void, (),, + "@brief Manually for the onAction function, which updates everything in this control.\n\n") +{ + object->onAction(); +} + +DefineEngineMethod( GuiPopUpMenuCtrlEx, forceClose, void, (),, + "@brief Manually force this control to collapse and close.\n\n") +{ + object->closePopUp(); +} + +DefineEngineMethod( GuiPopUpMenuCtrlEx, getSelected, S32, (),, + "@brief Get the current selection of the menu.\n\n" + "@return Returns the ID of the currently selected entry") +{ + return object->getSelected(); +} + +ConsoleDocFragment _GuiPopUpMenuCtrlExsetSelected( + "brief Manually set an entry as selected int his control\n\n" + "@param id The ID of the entry to select\n" + "@param scripCallback Optional boolean that forces the script callback if true\n", + "GuiPopUpMenuCtrlEx", + "setSelected(int id, bool scriptCallback=true);" +); + +ConsoleMethod( GuiPopUpMenuCtrlEx, setSelected, void, 3, 4, "(int id, [scriptCallback=true])" + "@hide") +{ + if( argc > 3 ) + object->setSelected( dAtoi( argv[2] ), dAtob( argv[3] ) ); + else + object->setSelected( dAtoi( argv[2] ) ); +} + +ConsoleDocFragment _GuiPopUpMenuCtrlExsetFirstSelected( + "brief Manually set the selection to the first entry\n\n" + "@param scripCallback Optional boolean that forces the script callback if true\n", + "GuiPopUpMenuCtrlEx", + "setSelected(bool scriptCallback=true);" +); + + +ConsoleMethod( GuiPopUpMenuCtrlEx, setFirstSelected, void, 2, 3, "([scriptCallback=true])" + "@hide") +{ + if( argc > 2 ) + object->setFirstSelected( dAtob( argv[2] ) ); + else + object->setFirstSelected(); +} + +DefineEngineMethod( GuiPopUpMenuCtrlEx, setNoneSelected, void, ( S32 param),, + "@brief Clears selection in the menu.\n\n") +{ + object->setNoneSelected(); +} + + +DefineEngineMethod( GuiPopUpMenuCtrlEx, getTextById, const char*, (S32 id),, + "@brief Get the text of an entry based on an ID.\n\n" + "@param id The ID assigned to the entry being queried\n\n" + "@return String contained by the specified entry, NULL if empty or bad ID") +{ + return(object->getTextById(id)); +} + + +ConsoleMethod( GuiPopUpMenuCtrlEx, getColorById, const char*, 3, 3, + "@brief Get color of an entry's box\n\n" + "@param id ID number of entry to query\n\n" + "@return ColorI in the format of \"Red Green Blue Alpha\", each of with is a value between 0 - 255") +{ + ColorI color; + object->getColoredBox(color, dAtoi(argv[2])); + + char *strBuffer = Con::getReturnBuffer(512); + dSprintf(strBuffer, 512, "%d %d %d %d", color.red, color.green, color.blue, color.alpha); + return strBuffer; +} + +ConsoleMethod( GuiPopUpMenuCtrlEx, setEnumContent, void, 4, 4, + "@brief This fills the popup with a classrep's field enumeration type info.\n\n" + "More of a helper function than anything. If console access to the field list is added, " + "at least for the enumerated types, then this should go away.\n\n" + "@param class Name of the class containing the enum\n" + "@param enum Name of the enum value to acces\n") +{ + AbstractClassRep * classRep = AbstractClassRep::getClassList(); + + // walk the class list to get our class + while(classRep) + { + if(!dStricmp(classRep->getClassName(), argv[2])) + break; + classRep = classRep->getNextClass(); + } + + // get it? + if(!classRep) + { + Con::warnf(ConsoleLogEntry::General, "failed to locate class rep for '%s'", argv[2]); + return; + } + + // walk the fields to check for this one (findField checks StringTableEntry ptrs...) + U32 i; + for(i = 0; i < classRep->mFieldList.size(); i++) + if(!dStricmp(classRep->mFieldList[i].pFieldname, argv[3])) + break; + + // found it? + if(i == classRep->mFieldList.size()) + { + Con::warnf(ConsoleLogEntry::General, "failed to locate field '%s' for class '%s'", argv[3], argv[2]); + return; + } + + const AbstractClassRep::Field & field = classRep->mFieldList[i]; + ConsoleBaseType* conType = ConsoleBaseType::getType( field.type ); + + // check the type + if( !conType->getEnumTable() ) + { + Con::warnf(ConsoleLogEntry::General, "field '%s' is not an enumeration for class '%s'", argv[3], argv[2]); + return; + } + + // fill it + const EngineEnumTable& table = *( conType->getEnumTable() ); + const U32 numValues = table.getNumValues(); + + for(i = 0; i < numValues; i++) + object->addEntry( table[i].getName(), table[i] ); +} + +//------------------------------------------------------------------------------ +ConsoleMethod( GuiPopUpMenuCtrlEx, findText, S32, 3, 3, "(string text)" + "Returns the id of the first entry containing the specified text or -1 if not found." + "@param text String value used for the query\n\n" + "@return Numerical ID of entry containing the text.") +{ + return( object->findText( argv[2] ) ); +} + +//------------------------------------------------------------------------------ +ConsoleMethod( GuiPopUpMenuCtrlEx, size, S32, 2, 2, + "@brief Get the size of the menu\n\n" + "@return Number of entries in the menu\n") +{ + return( object->getNumEntries() ); +} + +//------------------------------------------------------------------------------ +ConsoleMethod( GuiPopUpMenuCtrlEx, replaceText, void, 3, 3, + "@brief Flag that causes each new text addition to replace the current entry\n\n" + "@param True to turn on replacing, false to disable it") +{ + object->replaceText(dAtoi(argv[2])); +} + +//------------------------------------------------------------------------------ +// Added +bool GuiPopUpMenuCtrlEx::onWake() +{ + if ( !Parent::onWake() ) + return false; + + // Set the bitmap for the popup. + setBitmap( mBitmapName ); + + // Now update the Form Control's bitmap array, and possibly the child's too + mProfile->constructBitmapArray(); + + if ( mProfile->getChildrenProfile() ) + mProfile->getChildrenProfile()->constructBitmapArray(); + + return true; +} + +//------------------------------------------------------------------------------ +bool GuiPopUpMenuCtrlEx::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + mSelIndex = -1; + mReplaceText = true; + return true; +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrlEx::onSleep() +{ + mTextureNormal = NULL; // Added + mTextureDepressed = NULL; // Added + Parent::onSleep(); + closePopUp(); // Tests in function. +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrlEx::clear() +{ + mEntries.setSize(0); + setText(""); + mSelIndex = -1; + mRevNum = 0; + mIdMax = -1; +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrlEx::clearEntry( S32 entry ) +{ + if( entry == -1 ) + return; + + U32 i = 0; + for ( ; i < mEntries.size(); i++ ) + { + if ( mEntries[i].id == entry ) + break; + } + + mEntries.erase( i ); + + if( mEntries.size() <= 0 ) + { + mEntries.setSize(0); + setText(""); + mSelIndex = -1; + mRevNum = 0; + } + else + { + if( entry == mSelIndex ) + { + setText(""); + mSelIndex = -1; + } + else + { + mSelIndex--; + } + } +} + +//------------------------------------------------------------------------------ +ConsoleMethod( GuiPopUpMenuCtrlEx, clearEntry, void, 3, 3, "(S32 entry)") +{ + object->clearEntry(dAtoi(argv[2])); +} + +//------------------------------------------------------------------------------ +static S32 QSORT_CALLBACK textCompare(const void *a,const void *b) +{ + GuiPopUpMenuCtrlEx::Entry *ea = (GuiPopUpMenuCtrlEx::Entry *) (a); + GuiPopUpMenuCtrlEx::Entry *eb = (GuiPopUpMenuCtrlEx::Entry *) (b); + return (dStrnatcasecmp(ea->buf, eb->buf)); +} + +// Added to sort by entry ID +//------------------------------------------------------------------------------ +static S32 QSORT_CALLBACK idCompare(const void *a,const void *b) +{ + GuiPopUpMenuCtrlEx::Entry *ea = (GuiPopUpMenuCtrlEx::Entry *) (a); + GuiPopUpMenuCtrlEx::Entry *eb = (GuiPopUpMenuCtrlEx::Entry *) (b); + return ( (ea->id < eb->id) ? -1 : ((ea->id > eb->id) ? 1 : 0) ); +} + +//------------------------------------------------------------------------------ +// Added +void GuiPopUpMenuCtrlEx::setBitmap(const char *name) +{ + mBitmapName = StringTable->insert( name ); + if ( !isAwake() ) + return; + + if ( *mBitmapName ) + { + char buffer[1024]; + char *p; + dStrcpy(buffer, name); + p = buffer + dStrlen(buffer); + + dStrcpy(p, "_n"); + mTextureNormal = GFXTexHandle( (StringTableEntry)buffer, &GFXDefaultGUIProfile, avar("%s() - mTextureNormal (line %d)", __FUNCTION__, __LINE__) ); + + dStrcpy(p, "_d"); + mTextureDepressed = GFXTexHandle( (StringTableEntry)buffer, &GFXDefaultGUIProfile, avar("%s() - mTextureDepressed (line %d)", __FUNCTION__, __LINE__) ); + if ( !mTextureDepressed ) + mTextureDepressed = mTextureNormal; + } + else + { + mTextureNormal = NULL; + mTextureDepressed = NULL; + } + setUpdate(); +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrlEx::sort() +{ + S32 size = mEntries.size(); + if( size > 0 ) + dQsort( mEntries.address(), size, sizeof(Entry), textCompare); + + // Entries need to re-Id themselves + for( U32 i = 0; i < mEntries.size(); i++ ) + mEntries[i].id = i; +} + +// Added to sort by entry ID +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrlEx::sortID() +{ + S32 size = mEntries.size(); + if( size > 0 ) + dQsort( mEntries.address(), size, sizeof(Entry), idCompare); + + // Entries need to re-Id themselves + for( U32 i = 0; i < mEntries.size(); i++ ) + mEntries[i].id = i; +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrlEx::addEntry(const char *buf, S32 id, U32 scheme) +{ + if( !buf ) + { + //Con::printf( "GuiPopupMenuCtrlEx::addEntry - Invalid buffer!" ); + return; + } + + // Ensure that there are no other entries with exactly the same name + for ( U32 i = 0; i < mEntries.size(); i++ ) + { + if ( dStrcmp( mEntries[i].buf, buf ) == 0 ) + return; + } + + // If we don't give an id, create one from mIdMax + if( id == -1 ) + id = mIdMax + 1; + + // Increase mIdMax when an id is greater than it + if( id > mIdMax ) + mIdMax = id; + + Entry e; + dStrcpy( e.buf, buf ); + e.id = id; + e.scheme = scheme; + + // see if there is a shortcut key + char * cp = dStrchr( e.buf, '~' ); + e.ascii = cp ? cp[1] : 0; + + // See if there is a colour box defined with the text + char *cb = dStrchr( e.buf, '|' ); + if ( cb ) + { + e.usesColorBox = true; + cb[0] = '\0'; + + char* red = &cb[1]; + cb = dStrchr(red, '|'); + cb[0] = '\0'; + char* green = &cb[1]; + cb = dStrchr(green, '|'); + cb[0] = '\0'; + char* blue = &cb[1]; + + U32 r = dAtoi(red); + U32 g = dAtoi(green); + U32 b = dAtoi(blue); + + e.colorbox = ColorI(r,g,b); + + } + else + { + e.usesColorBox = false; + } + + mEntries.push_back(e); + + if ( mInAction && mTl ) + { + // Add the new entry: + mTl->addEntry( e.id, e.buf ); + repositionPopup(); + } +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrlEx::addScheme( U32 id, ColorI fontColor, ColorI fontColorHL, ColorI fontColorSEL ) +{ + if ( !id ) + return; + + Scheme newScheme; + newScheme.id = id; + newScheme.fontColor = fontColor; + newScheme.fontColorHL = fontColorHL; + newScheme.fontColorSEL = fontColorSEL; + + mSchemes.push_back( newScheme ); +} + +//------------------------------------------------------------------------------ +S32 GuiPopUpMenuCtrlEx::getSelected() +{ + if (mSelIndex == -1) + return 0; + return mEntries[mSelIndex].id; +} + +//------------------------------------------------------------------------------ +const char* GuiPopUpMenuCtrlEx::getTextById(S32 id) +{ + for ( U32 i = 0; i < mEntries.size(); i++ ) + { + if ( mEntries[i].id == id ) + return( mEntries[i].buf ); + } + + return( "" ); +} + +//------------------------------------------------------------------------------ +S32 GuiPopUpMenuCtrlEx::findText( const char* text ) +{ + for ( U32 i = 0; i < mEntries.size(); i++ ) + { + if ( dStrcmp( text, mEntries[i].buf ) == 0 ) + return( mEntries[i].id ); + } + return( -1 ); +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrlEx::setSelected(S32 id, bool bNotifyScript ) +{ + S32 i; + for ( i = 0; U32(i) < mEntries.size(); i++ ) + { + if ( id == mEntries[i].id ) + { + i = ( mRevNum > i ) ? mRevNum - i : i; + mSelIndex = i; + if ( mReplaceText ) // Only change the displayed text if appropriate. + { + setText(mEntries[i].buf); + } + + // Now perform the popup action: + char idval[24]; + dSprintf( idval, sizeof(idval), "%d", mEntries[mSelIndex].id ); + if ( isMethod( "onSelect" ) && bNotifyScript ) + Con::executef( this, "onSelect", idval, mEntries[mSelIndex].buf ); + return; + } + } + + if ( mReplaceText ) // Only change the displayed text if appropriate. + { + setText(""); + } + mSelIndex = -1; + + if ( isMethod( "onCancel" ) && bNotifyScript ) + Con::executef( this, "onCancel" ); + + if ( id == -1 ) + return; + + // Execute the popup console command: + if ( bNotifyScript ) + execConsoleCallback(); + //if ( mConsoleCommand[0] && bNotifyScript ) + // Con::evaluate( mConsoleCommand, false ); +} + +//------------------------------------------------------------------------------ +// Added to set the first item as selected. +void GuiPopUpMenuCtrlEx::setFirstSelected( bool bNotifyScript ) +{ + if ( mEntries.size() > 0 ) + { + mSelIndex = 0; + if ( mReplaceText ) // Only change the displayed text if appropriate. + { + setText( mEntries[0].buf ); + } + + // Now perform the popup action: + char idval[24]; + dSprintf( idval, sizeof(idval), "%d", mEntries[mSelIndex].id ); + if ( isMethod( "onSelect" ) ) + Con::executef( this, "onSelect", idval, mEntries[mSelIndex].buf ); + + // Execute the popup console command: + if ( bNotifyScript ) + execConsoleCallback(); + } + else + { + if ( mReplaceText ) // Only change the displayed text if appropriate. + setText(""); + + mSelIndex = -1; + + if ( bNotifyScript ) + Con::executef( this, "onCancel" ); + } +} + +//------------------------------------------------------------------------------ +// Added to set no items as selected. +void GuiPopUpMenuCtrlEx::setNoneSelected() +{ + if ( mReplaceText ) // Only change the displayed text if appropriate. + { + setText(""); + } + mSelIndex = -1; +} + +//------------------------------------------------------------------------------ +const char *GuiPopUpMenuCtrlEx::getScriptValue() +{ + return getText(); +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrlEx::onRender(Point2I offset, const RectI &updateRect) +{ + TORQUE_UNUSED(updateRect); + Point2I localStart; + + if ( mScrollDir != GuiScrollCtrl::None ) + autoScroll(); + + RectI r( offset, getExtent() ); + if ( mInAction ) + { + S32 l = r.point.x, r2 = r.point.x + r.extent.x - 1; + S32 t = r.point.y, b = r.point.y + r.extent.y - 1; + + // Do we render a bitmap border or lines? + if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() ) + { + // Render the fixed, filled in border + renderFixedBitmapBordersFilled(r, 3, mProfile ); + + } + else + { + //renderSlightlyLoweredBox(r, mProfile); + GFX->getDrawUtil()->drawRectFill( r, mProfile->mFillColor ); + } + + // Draw a bitmap over the background? + if ( mTextureDepressed ) + { + RectI rect(offset, mBitmapBounds); + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapStretch( mTextureDepressed, rect ); + } + else if ( mTextureNormal ) + { + RectI rect(offset, mBitmapBounds); + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapStretch( mTextureNormal, rect ); + } + + // Do we render a bitmap border or lines? + if ( !( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() ) ) + { + GFX->getDrawUtil()->drawLine( l, t, l, b, colorWhite ); + GFX->getDrawUtil()->drawLine( l, t, r2, t, colorWhite ); + GFX->getDrawUtil()->drawLine( l + 1, b, r2, b, mProfile->mBorderColor ); + GFX->getDrawUtil()->drawLine( r2, t + 1, r2, b - 1, mProfile->mBorderColor ); + } + + } + else + // TODO: Implement + // TODO: Add onMouseEnter() and onMouseLeave() and a definition of mMouseOver (see guiButtonBaseCtrl) for this to work. + if ( mMouseOver ) + { + S32 l = r.point.x, r2 = r.point.x + r.extent.x - 1; + S32 t = r.point.y, b = r.point.y + r.extent.y - 1; + + // Do we render a bitmap border or lines? + if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() ) + { + // Render the fixed, filled in border + renderFixedBitmapBordersFilled( r, 2, mProfile ); + + } + else + { + GFX->getDrawUtil()->drawRectFill( r, mProfile->mFillColorHL ); + } + + // Draw a bitmap over the background? + if ( mTextureNormal ) + { + RectI rect( offset, mBitmapBounds ); + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapStretch( mTextureNormal, rect ); + } + + // Do we render a bitmap border or lines? + if ( !( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() ) ) + { + GFX->getDrawUtil()->drawLine( l, t, l, b, colorWhite ); + GFX->getDrawUtil()->drawLine( l, t, r2, t, colorWhite ); + GFX->getDrawUtil()->drawLine( l + 1, b, r2, b, mProfile->mBorderColor ); + GFX->getDrawUtil()->drawLine( r2, t + 1, r2, b - 1, mProfile->mBorderColor ); + } + } + else + { + // Do we render a bitmap border or lines? + if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() ) + { + // Render the fixed, filled in border + renderFixedBitmapBordersFilled( r, 1, mProfile ); + } + else + { + GFX->getDrawUtil()->drawRectFill( r, mProfile->mFillColorNA ); + } + + // Draw a bitmap over the background? + if ( mTextureNormal ) + { + RectI rect(offset, mBitmapBounds); + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapStretch( mTextureNormal, rect ); + } + + // Do we render a bitmap border or lines? + if ( !( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() ) ) + { + GFX->getDrawUtil()->drawRect( r, mProfile->mBorderColorNA ); + } + } + // renderSlightlyRaisedBox(r, mProfile); // Used to be the only 'else' condition to mInAction above. + + S32 txt_w = mProfile->mFont->getStrWidth(mText); + localStart.x = 0; + localStart.y = (getHeight() - (mProfile->mFont->getHeight())) / 2; + + // align the horizontal + switch (mProfile->mAlignment) + { + case GuiControlProfile::RightJustify: + if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() ) + { + // We're making use of a bitmap border, so take into account the + // right cap of the border. + RectI* mBitmapBounds = mProfile->mBitmapArrayRects.address(); + localStart.x = getWidth() - mBitmapBounds[2].extent.x - txt_w; + } + else + { + localStart.x = getWidth() - txt_w; + } + break; + case GuiControlProfile::CenterJustify: + if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() ) + { + // We're making use of a bitmap border, so take into account the + // right cap of the border. + RectI* mBitmapBounds = mProfile->mBitmapArrayRects.address(); + localStart.x = (getWidth() - mBitmapBounds[2].extent.x - txt_w) / 2; + + } else + { + localStart.x = (getWidth() - txt_w) / 2; + } + break; + default: + // GuiControlProfile::LeftJustify + if ( txt_w > getWidth() ) + { + // The width of the text is greater than the width of the control. + // In this case we will right justify the text and leave some space + // for the down arrow. + if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() ) + { + // We're making use of a bitmap border, so take into account the + // right cap of the border. + RectI* mBitmapBounds = mProfile->mBitmapArrayRects.address(); + localStart.x = getWidth() - mBitmapBounds[2].extent.x - txt_w; + } + else + { + localStart.x = getWidth() - txt_w - 12; + } + } + else + { + localStart.x = mProfile->mTextOffset.x; // Use mProfile->mTextOffset as a controlable margin for the control's text. + } + break; + } + + // Do we first draw a coloured box beside the text? + ColorI boxColor; + bool drawbox = getColoredBox( boxColor, mSelIndex); + if ( drawbox ) + { + Point2I coloredboxsize( 15, 10 ); + RectI r( offset.x + mProfile->mTextOffset.x, offset.y + ( (getHeight() - coloredboxsize.y ) / 2 ), coloredboxsize.x, coloredboxsize.y ); + GFX->getDrawUtil()->drawRectFill( r, boxColor); + GFX->getDrawUtil()->drawRect( r, ColorI(0,0,0)); + + localStart.x += coloredboxsize.x + mProfile->mTextOffset.x; + } + + // Draw the text + Point2I globalStart = localToGlobalCoord( localStart ); + ColorI fontColor = mActive ? ( mInAction ? mProfile->mFontColor : mProfile->mFontColorNA ) : mProfile->mFontColorNA; + GFX->getDrawUtil()->setBitmapModulation( fontColor ); // was: (mProfile->mFontColor); + + // Get the number of columns in the text + S32 colcount = getColumnCount( mText, "\t" ); + + // Are there two or more columns? + if ( colcount >= 2 ) + { + char buff[256]; + + // Draw the first column + getColumn( mText, buff, 0, "\t" ); + GFX->getDrawUtil()->drawText( mProfile->mFont, globalStart, buff, mProfile->mFontColors ); + + // Draw the second column to the right + getColumn( mText, buff, 1, "\t" ); + S32 txt_w = mProfile->mFont->getStrWidth( buff ); + if ( mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size() ) + { + // We're making use of a bitmap border, so take into account the + // right cap of the border. + RectI* mBitmapBounds = mProfile->mBitmapArrayRects.address(); + Point2I textpos = localToGlobalCoord( Point2I( getWidth() - txt_w - mBitmapBounds[2].extent.x, localStart.y ) ); + GFX->getDrawUtil()->drawText( mProfile->mFont, textpos, buff, mProfile->mFontColors ); + + } else + { + Point2I textpos = localToGlobalCoord( Point2I( getWidth() - txt_w - 12, localStart.y ) ); + GFX->getDrawUtil()->drawText( mProfile->mFont, textpos, buff, mProfile->mFontColors ); + } + + } else + { + GFX->getDrawUtil()->drawText( mProfile->mFont, globalStart, mText, mProfile->mFontColors ); + } + + // If we're rendering a bitmap border, then it will take care of the arrow. + if ( !(mProfile->getChildrenProfile() && mProfile->mBitmapArrayRects.size()) ) + { + // Draw a triangle (down arrow) + S32 left = r.point.x + r.extent.x - 12; + S32 right = left + 8; + S32 middle = left + 4; + S32 top = r.extent.y / 2 + r.point.y - 4; + S32 bottom = top + 8; + + PrimBuild::color( mProfile->mFontColor ); + + PrimBuild::begin( GFXTriangleList, 3 ); + PrimBuild::vertex2fv( Point3F( (F32)left, (F32)top, 0.0f ) ); + PrimBuild::vertex2fv( Point3F( (F32)right, (F32)top, 0.0f ) ); + PrimBuild::vertex2fv( Point3F( (F32)middle, (F32)bottom, 0.0f ) ); + PrimBuild::end(); + } +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrlEx::closePopUp() +{ + if ( !mInAction ) + return; + + // Get the selection from the text list: + mSelIndex = mTl->getSelectedCell().y; + mSelIndex = ( mRevNum >= mSelIndex && mSelIndex != -1 ) ? mRevNum - mSelIndex : mSelIndex; + if ( mSelIndex != -1 ) + { + if ( mReplaceText ) + setText( mEntries[mSelIndex].buf ); + setIntVariable( mEntries[mSelIndex].id ); + } + + // Release the mouse: + mInAction = false; + mTl->mouseUnlock(); + + // Commented out below and moved to the end of the function. See the + // note below for the reason why. + /* + // Pop the background: + getRoot()->popDialogControl(mBackground); + + // Kill the popup: + mBackground->removeObject( mSc ); + mTl->deleteObject(); + mSc->deleteObject(); + mBackground->deleteObject(); + + // Set this as the first responder: + setFocus(); + */ + + // Now perform the popup action: + if ( mSelIndex != -1 ) + { + char idval[24]; + dSprintf( idval, sizeof(idval), "%d", mEntries[mSelIndex].id ); + if ( isMethod( "onSelect" ) ) + Con::executef( this, "onSelect", idval, mEntries[mSelIndex].buf ); + } + else if ( isMethod( "onCancel" ) ) + Con::executef( this, "onCancel" ); + + // Execute the popup console command: + execConsoleCallback(); + //if ( mConsoleCommand[0] ) + // Con::evaluate( mConsoleCommand, false ); + + // Reordered this pop dialog to be after the script select callback. When the + // background was popped it was causing all sorts of focus issues when + // suddenly the first text edit control took the focus before it came back + // to this popup. + + // Pop the background: + GuiCanvas *root = getRoot(); + if ( root ) + root->popDialogControl(mBackground); + + // Kill the popup: + mBackground->removeObject( mSc ); + mTl->deleteObject(); + mSc->deleteObject(); + mBackground->deleteObject(); + + // Set this as the first responder: + setFirstResponder(); +} + +//------------------------------------------------------------------------------ +bool GuiPopUpMenuCtrlEx::onKeyDown(const GuiEvent &event) +{ + //if the control is a dead end, don't process the input: + if ( !mVisible || !mActive || !mAwake ) + return false; + + //see if the key down is a or not + if ( event.keyCode == KEY_RETURN && event.modifier == 0 ) + { + onAction(); + return true; + } + + //otherwise, pass the event to its parent + return Parent::onKeyDown( event ); +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrlEx::onAction() +{ + GuiControl *canCtrl = getParent(); + + addChildren(); + + GuiCanvas *root = getRoot(); + Point2I windowExt = root->getExtent(); + + mBackground->resize( Point2I(0,0), root->getExtent() ); + + S32 textWidth = 0, width = getWidth(); + //const S32 menuSpace = 5; // Removed as no longer used. + const S32 textSpace = 2; + bool setScroll = false; + + for ( U32 i = 0; i < mEntries.size(); ++i ) + if ( S32(mProfile->mFont->getStrWidth( mEntries[i].buf )) > textWidth ) + textWidth = mProfile->mFont->getStrWidth( mEntries[i].buf ); + + //if(textWidth > getWidth()) + S32 sbWidth = mSc->getControlProfile()->mBorderThickness * 2 + mSc->scrollBarThickness(); // Calculate the scroll bar width + if ( textWidth > ( getWidth() - sbWidth-mProfile->mTextOffset.x - mSc->getChildMargin().x * 2 ) ) // The text draw area to test against is the width of the drop-down minus the scroll bar width, the text margin and the scroll bar child margins. + { + //textWidth +=10; + textWidth +=sbWidth + mProfile->mTextOffset.x + mSc->getChildMargin().x * 2; // The new width is the width of the text plus the scroll bar width plus the text margin size plus the scroll bar child margins. + width = textWidth; + + // If a child margin is not defined for the scroll control, let's add + // some space between the text and scroll control for readability + if(mSc->getChildMargin().x == 0) + width += textSpace; + } + + //mTl->setCellSize(Point2I(width, mFont->getHeight()+3)); + mTl->setCellSize(Point2I(width, mProfile->mFont->getHeight() + textSpace)); // Modified the above line to use textSpace rather than the '3' as this is what is used below. + + for ( U32 j = 0; j < mEntries.size(); ++j ) + mTl->addEntry( mEntries[j].id, mEntries[j].buf ); + + if ( mSelIndex >= 0 ) + mTl->setSelectedCell( Point2I( 0, mSelIndex ) ); + + Point2I pointInGC = canCtrl->localToGlobalCoord( getPosition() ); + Point2I scrollPoint( pointInGC.x, pointInGC.y + getHeight() ); + + //Calc max Y distance, so Scroll Ctrl will fit on window + //S32 maxYdis = windowExt.y - pointInGC.y - getHeight() - menuSpace; + S32 sbBorder = mSc->getControlProfile()->mBorderThickness * 2 + mSc->getChildMargin().y * 2; // Added to take into account the border thickness and the margin of the child controls of the scroll control when figuring out the size of the contained text list control + S32 maxYdis = windowExt.y - pointInGC.y - getHeight() - sbBorder; // - menuSpace; // Need to remove the border thickness from the contained control maximum extent and got rid of the 'menuspace' variable + + //If scroll bars need to be added + mRevNum = 0; // Added here rather than within the following 'if' statements. + if ( maxYdis < mTl->getHeight() + sbBorder ) // Instead of adding sbBorder, it was: 'textSpace' + { + //Should we pop menu list above the button + if ( maxYdis < pointInGC.y ) // removed: '- menuSpace)' from check to see if there is more space above the control than below. + { + if(mReverseTextList) // Added this check if we should reverse the text list. + reverseTextList(); + + maxYdis = pointInGC.y; // Was at the end: '- menuSpace;' + //Does the menu need a scroll bar + if ( maxYdis < mTl->getHeight() + sbBorder ) // Instead of adding sbBorder, it was: 'textSpace' + { + // Removed width recalculation for scroll bar as the scroll bar is already being taken into account. + //Calc for the width of the scroll bar + // if(textWidth >= width) + // width += 20; + // mTl->setCellSize(Point2I(width,mFont->getHeight() + textSpace)); + + //Pop menu list above the button + // scrollPoint.set(pointInGC.x, menuSpace - 1); // Removed as calculated outside the 'if', and was wrong to begin with + setScroll = true; + } + //No scroll bar needed + else + { + maxYdis = mTl->getHeight() + sbBorder; // Instead of adding sbBorder, it was: 'textSpace' + // scrollPoint.set(pointInGC.x, pointInGC.y - maxYdis -1); // Removed as calculated outside the 'if' and the '-1' at the end is wrong + } + + // Added the next two lines + scrollPoint.set(pointInGC.x, pointInGC.y - maxYdis); // Used to have the following on the end: '-1);' + } + //Scroll bar needed but Don't pop above button + else + { + //mRevNum = 0; // Commented out as it has been added at the beginning of this function + + // Removed width recalculation for scroll bar as the scroll bar is already being taken into account. + //Calc for the width of the scroll bar + // if(textWidth >= width) + // width += 20; + // mTl->setCellSize(Point2I(width,mFont->getHeight() + textSpace)); + setScroll = true; + } + } + //No scroll bar needed + else + { + + //maxYdis = mTl->getHeight() + textSpace; + maxYdis = mTl->getHeight() + sbBorder; // Added in the border thickness of the scroll control and removed the addition of textSpace + } + + RectI newBounds = mSc->getBounds(); + + //offset it from the background so it lines up properly + newBounds.point = mBackground->globalToLocalCoord( scrollPoint ); + + if ( newBounds.point.x + width > mBackground->getWidth() ) + if ( width - getWidth() > 0 ) + newBounds.point.x -= width - getWidth(); + + //mSc->getExtent().set(width-1, maxYdis); + newBounds.extent.set( width, maxYdis ); + mSc->setBounds( newBounds ); // Not sure why the '-1' above. + + mSc->registerObject(); + mTl->registerObject(); + mBackground->registerObject(); + + mSc->addObject( mTl ); + mBackground->addObject( mSc ); + + mBackgroundCancel = false; // Setup check if user clicked on the background instead of the text list (ie: didn't want to change their current selection). + + root->pushDialogControl( mBackground, 99 ); + + if ( setScroll ) + { + // Resize the text list + Point2I cellSize; + mTl->getCellSize( cellSize ); + cellSize.x = width - mSc->scrollBarThickness() - sbBorder; + mTl->setCellSize( cellSize ); + mTl->setWidth( cellSize.x ); + + if ( mSelIndex ) + mTl->scrollCellVisible( Point2I( 0, mSelIndex ) ); + else + mTl->scrollCellVisible( Point2I( 0, 0 ) ); + } + + mTl->setFirstResponder(); + + mInAction = true; +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrlEx::addChildren() +{ + mTl = new GuiPopupTextListCtrlEx( this ); + AssertFatal( mTl, "Failed to create the GuiPopUpTextListCtrlEx for the PopUpMenu" ); + // Use the children's profile rather than the parent's profile, if it exists. + mTl->setControlProfile( mProfile->getChildrenProfile() ? mProfile->getChildrenProfile() : mProfile ); + mTl->setField("noDuplicates", "false"); + + mSc = new GuiScrollCtrl; + AssertFatal( mSc, "Failed to create the GuiScrollCtrl for the PopUpMenu" ); + GuiControlProfile *prof; + if ( Sim::findObject( "GuiScrollProfile", prof ) ) + { + mSc->setControlProfile( prof ); + } + else + { + // Use the children's profile rather than the parent's profile, if it exists. + mSc->setControlProfile( mProfile->getChildrenProfile() ? mProfile->getChildrenProfile() : mProfile ); + } + mSc->setField( "hScrollBar", "AlwaysOff" ); + mSc->setField( "vScrollBar", "dynamic" ); + //if(mRenderScrollInNA) // Force the scroll control to render using fillColorNA rather than fillColor + // mSc->mUseNABackground = true; + + mBackground = new GuiPopUpBackgroundCtrlEx( this, mTl ); + AssertFatal( mBackground, "Failed to create the GuiBackgroundCtrlEx for the PopUpMenu" ); +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrlEx::repositionPopup() +{ + if ( !mInAction || !mSc || !mTl ) + return; + + // I'm not concerned with this right now... +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrlEx::reverseTextList() +{ + mTl->clear(); + for ( S32 i = mEntries.size()-1; i >= 0; --i ) + mTl->addEntry( mEntries[i].id, mEntries[i].buf ); + + // Don't lose the selected cell: + if ( mSelIndex >= 0 ) + mTl->setSelectedCell( Point2I( 0, mEntries.size() - mSelIndex - 1 ) ); + + mRevNum = mEntries.size() - 1; +} + +//------------------------------------------------------------------------------ +bool GuiPopUpMenuCtrlEx::getFontColor( ColorI &fontColor, S32 id, bool selected, bool mouseOver ) +{ + U32 i; + Entry* entry = NULL; + for ( i = 0; i < mEntries.size(); i++ ) + { + if ( mEntries[i].id == id ) + { + entry = &mEntries[i]; + break; + } + } + + if ( !entry ) + return( false ); + + if ( entry->scheme != 0 ) + { + // Find the entry's color scheme: + for ( i = 0; i < mSchemes.size(); i++ ) + { + if ( mSchemes[i].id == entry->scheme ) + { + fontColor = selected ? mSchemes[i].fontColorSEL : mouseOver ? mSchemes[i].fontColorHL : mSchemes[i].fontColor; + return( true ); + } + } + } + + if(id == -1) + fontColor = mProfile->mFontColorHL; + else + // Default color scheme... + fontColor = selected ? mProfile->mFontColorSEL : mouseOver ? mProfile->mFontColorHL : mProfile->mFontColorNA; // Modified the final color choice from mProfile->mFontColor to mProfile->mFontColorNA + + return( true ); +} + +//------------------------------------------------------------------------------ +// Added +bool GuiPopUpMenuCtrlEx::getColoredBox( ColorI &fontColor, S32 id ) +{ + U32 i; + Entry* entry = NULL; + for ( i = 0; i < mEntries.size(); i++ ) + { + if ( mEntries[i].id == id ) + { + entry = &mEntries[i]; + break; + } + } + + if ( !entry ) + return false; + + if ( entry->usesColorBox == false ) + return false; + + fontColor = entry->colorbox; + + return true; +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrlEx::onMouseDown(const GuiEvent &event) +{ + TORQUE_UNUSED(event); + onAction(); +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrlEx::onMouseUp(const GuiEvent &event) +{ + TORQUE_UNUSED(event); +} + +//------------------------------------------------------------------------------ +// Added +void GuiPopUpMenuCtrlEx::onMouseEnter(const GuiEvent &event) +{ + mMouseOver = true; +} + +//------------------------------------------------------------------------------ +// Added +void GuiPopUpMenuCtrlEx::onMouseLeave(const GuiEvent &) +{ + mMouseOver = false; +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrlEx::setupAutoScroll(const GuiEvent &event) +{ + GuiControl *parent = getParent(); + if ( !parent ) + return; + + Point2I mousePt = mSc->globalToLocalCoord( event.mousePoint ); + + mEventSave = event; + + if ( mLastYvalue != mousePt.y ) + { + mScrollDir = GuiScrollCtrl::None; + if ( mousePt.y > mSc->getHeight() || mousePt.y < 0 ) + { + S32 topOrBottom = ( mousePt.y > mSc->getHeight() ) ? 1 : 0; + mSc->scrollTo( 0, topOrBottom ); + return; + } + + F32 percent = (F32)mousePt.y / (F32)mSc->getHeight(); + if ( percent > 0.7f && mousePt.y > mLastYvalue ) + { + mIncValue = percent - 0.5f; + mScrollDir = GuiScrollCtrl::DownArrow; + } + else if ( percent < 0.3f && mousePt.y < mLastYvalue ) + { + mIncValue = 0.5f - percent; + mScrollDir = GuiScrollCtrl::UpArrow; + } + mLastYvalue = mousePt.y; + } +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrlEx::autoScroll() +{ + mScrollCount += mIncValue; + + while ( mScrollCount > 1 ) + { + mSc->autoScroll( mScrollDir ); + mScrollCount -= 1; + } + mTl->onMouseMove( mEventSave ); +} + +//------------------------------------------------------------------------------ +void GuiPopUpMenuCtrlEx::replaceText(S32 boolVal) +{ + mReplaceText = boolVal; +} diff --git a/Engine/source/gui/controls/guiPopUpCtrlEx.h b/Engine/source/gui/controls/guiPopUpCtrlEx.h new file mode 100644 index 000000000..04fa7b813 --- /dev/null +++ b/Engine/source/gui/controls/guiPopUpCtrlEx.h @@ -0,0 +1,173 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIPOPUPCTRLEX_H_ +#define _GUIPOPUPCTRLEX_H_ + +#ifndef _GUITEXTCTRL_H_ +#include "gui/controls/guiTextCtrl.h" +#endif +#ifndef _GUITEXTLISTCTRL_H_ +#include "gui/controls/guiTextListCtrl.h" +#endif +#ifndef _GUIBUTTONCTRL_H_ +#include "gui/buttons/guiButtonCtrl.h" +#endif +#ifndef _GUISCROLLCTRL_H_ +#include "gui/containers/guiScrollCtrl.h" +#endif +class GuiPopUpMenuCtrlEx; +class GuiPopupTextListCtrlEx; + +class GuiPopUpBackgroundCtrlEx : public GuiControl +{ +protected: + GuiPopUpMenuCtrlEx *mPopUpCtrl; + GuiPopupTextListCtrlEx *mTextList; +public: + GuiPopUpBackgroundCtrlEx(GuiPopUpMenuCtrlEx *ctrl, GuiPopupTextListCtrlEx* textList); + void onMouseDown(const GuiEvent &event); +}; + +class GuiPopupTextListCtrlEx : public GuiTextListCtrl +{ + private: + typedef GuiTextListCtrl Parent; + + bool hasCategories(); + + protected: + GuiPopUpMenuCtrlEx *mPopUpCtrl; + + public: + GuiPopupTextListCtrlEx(); // for inheritance + GuiPopupTextListCtrlEx(GuiPopUpMenuCtrlEx *ctrl); + + // GuiArrayCtrl overload: + void onCellSelected(Point2I cell); + + // GuiControl overloads: + bool onKeyDown(const GuiEvent &event); + void onMouseUp(const GuiEvent &event); + void onMouseMove(const GuiEvent &event); + void onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver); +}; + +class GuiPopUpMenuCtrlEx : public GuiTextCtrl +{ + typedef GuiTextCtrl Parent; + + public: + struct Entry + { + char buf[256]; + S32 id; + U16 ascii; + U16 scheme; + bool usesColorBox; // Added + ColorI colorbox; // Added + }; + + struct Scheme + { + U32 id; + ColorI fontColor; + ColorI fontColorHL; + ColorI fontColorSEL; + }; + + bool mBackgroundCancel; // Added + + protected: + GuiPopupTextListCtrlEx *mTl; + GuiScrollCtrl *mSc; + GuiPopUpBackgroundCtrlEx *mBackground; + Vector mEntries; + Vector mSchemes; + S32 mSelIndex; + S32 mMaxPopupHeight; + F32 mIncValue; + F32 mScrollCount; + S32 mLastYvalue; + GuiEvent mEventSave; + S32 mRevNum; + bool mInAction; + bool mReplaceText; + bool mMouseOver; // Added + bool mRenderScrollInNA; // Added + bool mReverseTextList; // Added - Should we reverse the text list if we display up? + bool mHotTrackItems; + StringTableEntry mBitmapName; // Added + Point2I mBitmapBounds; // Added + GFXTexHandle mTextureNormal; // Added + GFXTexHandle mTextureDepressed; // Added + S32 mIdMax; + + virtual void addChildren(); + virtual void repositionPopup(); + + public: + GuiPopUpMenuCtrlEx(void); + ~GuiPopUpMenuCtrlEx(); + GuiScrollCtrl::Region mScrollDir; + bool onWake(); // Added + bool onAdd(); + void onSleep(); + void setBitmap(const char *name); // Added + void sort(); + void sortID(); // Added + void addEntry(const char *buf, S32 id = -1, U32 scheme = 0); + void addScheme(U32 id, ColorI fontColor, ColorI fontColorHL, ColorI fontColorSEL); + void onRender(Point2I offset, const RectI &updateRect); + void onAction(); + virtual void closePopUp(); + void clear(); + void clearEntry( S32 entry ); // Added + void onMouseDown(const GuiEvent &event); + void onMouseUp(const GuiEvent &event); + void onMouseEnter(const GuiEvent &event); // Added + void onMouseLeave(const GuiEvent &); // Added + void setupAutoScroll(const GuiEvent &event); + void autoScroll(); + bool onKeyDown(const GuiEvent &event); + void reverseTextList(); + bool getFontColor(ColorI &fontColor, S32 id, bool selected, bool mouseOver); + bool getColoredBox(ColorI &boxColor, S32 id); // Added + + S32 getSelected(); + void setSelected(S32 id, bool bNotifyScript = true); + void setFirstSelected(bool bNotifyScript = true); // Added + void setNoneSelected(); // Added + const char *getScriptValue(); + const char *getTextById(S32 id); + S32 findText( const char* text ); + S32 getNumEntries() { return( mEntries.size() ); } + void replaceText(S32); + + DECLARE_CONOBJECT(GuiPopUpMenuCtrlEx); + DECLARE_CATEGORY( "Gui Lists" ); + DECLARE_DESCRIPTION( "A control that allows to select a value from a drop-down list." ); + + static void initPersistFields(void); +}; + +#endif //_GUIIMPROVEDPOPUPCTRL_H_ diff --git a/Engine/source/gui/controls/guiSliderCtrl.cpp b/Engine/source/gui/controls/guiSliderCtrl.cpp new file mode 100644 index 000000000..47362dc9b --- /dev/null +++ b/Engine/source/gui/controls/guiSliderCtrl.cpp @@ -0,0 +1,584 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/console.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "gfx/gfxTextureManager.h" +#include "gui/controls/guiSliderCtrl.h" +#include "gui/core/guiDefaultControlRender.h" +#include "platform/event.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxDrawUtil.h" +#include "sfx/sfxSystem.h" +#include "sfx/sfxTrack.h" + + +IMPLEMENT_CONOBJECT( GuiSliderCtrl ); + +ConsoleDocClass( GuiSliderCtrl, + "@brief A control that displays a value between its minimal and maximal bounds using a slider placed on a vertical " + "or horizontal axis.\n\n" + + "A slider displays a value and allows that value to be changed by dragging a thumb control along the axis of the " + "slider. In this way, the value is changed between its allowed minimum and maximum.\n\n" + + "To hook up script code to the value changes of a slider, use the #command and #altCommand properties. #command is " + "executed once the thumb is released by the user whereas #altCommand is called any time the slider value changes. " + "When changing the slider value from script, however, trigger of #altCommand is suppressed by default.\n\n" + + "The orientation of a slider is automatically determined from the ratio of its width to its height. If a slider is " + "taller than it is wide, it will be rendered with a vertical orientation. If it is wider than it is tall, it will be " + "rendered with a horizontal orientation.\n\n" + + "The rendering of a slider depends on the bitmap in the slider's profile. This bitmap must be a bitmap array comprised " + "of at least five bitmap rectangles. The rectangles are used such that:\n\n" + + "- Rectangle #1: Left edge of slider\n" + "- Rectangle #2: Center piece of slider; this is stretched between the left and right edge\n" + "- Rectangle #3: Right edge of slider\n" + "- Rectangle #4: Thumb button in normal state\n" + "- Rectangle #5: Thumb button in highlighted (mouse-over) state\n\n" + + "@tsexample\n" + "// Create a sound source and a slider that changes the volume of the source.\n" + "\n" + "%source = sfxPlayOnce( \"art/sound/testing\", AudioLoop2D );\n" + "\n" + "new GuiSlider()\n" + "{\n" + " // Update the sound source volume when the slider is being dragged and released.\n" + " command = %source @ \".setVolume( $ThisControl.value );\";\n" + "\n" + " // Limit the range to 0..1 since that is the allowable range for sound volumes.\n" + " range = \"0 1\";\n" + "};\n" + "@endtsexample\n\n" + + "@see GuiTextEditSliderCtrl\n" + "@see GuiTextEditSliderBitmapCtrl\n\n" + + "@ingroup GuiValues" +); + + +IMPLEMENT_CALLBACK( GuiSliderCtrl, onMouseDragged, void, (), (), + "Called when the left mouse button is dragged across the slider." ); + + +//---------------------------------------------------------------------------- + +GuiSliderCtrl::GuiSliderCtrl() + : mRange( 0., 1.f ), + mTicks( 10 ), + mSnap( false ), + mValue( 0.5f ), + mThumbSize( 8, 20 ), + mShiftPoint( 5 ), + mShiftExtent( 10 ), + mIncAmount( 0.f ), + mDisplayValue( false ), + mMouseOver( false ), + mMouseDragged( false ), + mDepressed( false ) +{ +} + +//---------------------------------------------------------------------------- + +void GuiSliderCtrl::initPersistFields() +{ + addGroup( "Slider" ); + + addField( "range", TypePoint2F, Offset( mRange, GuiSliderCtrl ), + "Min and max values corresponding to left and right slider position." ); + addField( "ticks", TypeS32, Offset( mTicks, GuiSliderCtrl ), + "Spacing between tick marks in pixels. 0=off." ); + addField( "snap", TypeBool, Offset( mSnap, GuiSliderCtrl ), + "Whether to snap the slider to tick marks." ); + addProtectedField( "value", TypeF32, Offset( mValue, GuiSliderCtrl ), + _setValue, defaultProtectedGetFn, + "The value corresponding to the current slider position." ); + + endGroup( "Slider" ); + + Parent::initPersistFields(); +} + +//---------------------------------------------------------------------------- + +void GuiSliderCtrl::setValue(F32 val, bool doCallback) +{ + _updateThumb( val, mSnap, false, doCallback ); +} + +//---------------------------------------------------------------------------- + +void GuiSliderCtrl::setActive( bool value ) +{ + if( !value && mDepressed ) + { + // We're in the middle of a drag. Finish it here as once we've + // been deactivated, we are not going to see a mouse-up event. + + mDepressed = false; + mouseUnlock(); + execConsoleCallback(); + } + + Parent::setActive( value ); +} + +//---------------------------------------------------------------------------- + +bool GuiSliderCtrl::onWake() +{ + if( !Parent::onWake() ) + return false; + + mHasTexture = mProfile->constructBitmapArray() >= NumBitmaps; + if( mHasTexture ) + { + mBitmapBounds = mProfile->mBitmapArrayRects.address(); + mThumbSize = Point2I( mBitmapBounds[ SliderButtonNormal ].extent.x, mBitmapBounds[ SliderButtonNormal ].extent.y ); + } + + F32 value; + if( mConsoleVariable[ 0 ] ) + value = getFloatVariable(); + else + value = mValue; + + mValue = mClampF( value, mRange.x, mRange.y ); + + // mouse scroll increment percentage is 5% of the range + mIncAmount = ( ( mRange.y - mRange.x ) * 0.05 ); + + if( ( mThumbSize.y + mProfile->mFont->getHeight() - 4 ) <= getExtent().y ) + mDisplayValue = true; + else + mDisplayValue = false; + + _updateThumb( mValue, mSnap, true ); + + return true; +} + +//---------------------------------------------------------------------------- + +void GuiSliderCtrl::onMouseDown(const GuiEvent &event) +{ + if ( !mActive || !mAwake || !mVisible ) + return; + + mouseLock(); + setFirstResponder(); + mDepressed = true; + + Point2I curMousePos = globalToLocalCoord( event.mousePoint ); + F32 value; + if (getWidth() >= getHeight()) + value = F32(curMousePos.x-mShiftPoint) / F32(getWidth()-mShiftExtent)*(mRange.y-mRange.x) + mRange.x; + else + value = F32(curMousePos.y) / F32(getHeight())*(mRange.y-mRange.x) + mRange.x; + + _updateThumb( value, mSnap || ( event.modifier & SI_SHIFT ) ); +} + +//---------------------------------------------------------------------------- + +void GuiSliderCtrl::onMouseDragged( const GuiEvent &event ) +{ + if ( !mActive || !mAwake || !mVisible ) + return; + + mMouseDragged = true; + + F32 value = _getThumbValue( event ); + _updateThumb( value, mSnap || ( event.modifier & SI_SHIFT ) ); + + onMouseDragged_callback(); +} + +//---------------------------------------------------------------------------- + +void GuiSliderCtrl::onMouseUp( const GuiEvent& event ) +{ + if ( !mActive || !mAwake || !mVisible ) + return; + + mouseUnlock(); + + mDepressed = false; + mMouseDragged = false; + + _updateThumb( _getThumbValue( event ), event.modifier & SI_SHIFT ); + + execConsoleCallback(); +} + +//---------------------------------------------------------------------------- + +void GuiSliderCtrl::onMouseEnter(const GuiEvent &event) +{ + setUpdate(); + if( isMouseLocked() ) + { + mDepressed = true; + mMouseOver = true; + } + else + { + if( mActive && mProfile->mSoundButtonOver ) + { + //F32 pan = (F32(event.mousePoint.x)/F32(getRoot()->getWidth())*2.0f-1.0f)*0.8f; + SFX->playOnce( mProfile->mSoundButtonOver ); + } + + mMouseOver = true; + } +} + +//---------------------------------------------------------------------------- + +void GuiSliderCtrl::onMouseLeave(const GuiEvent &) +{ + setUpdate(); + if( isMouseLocked() ) + mDepressed = false; + mMouseOver = false; +} +//---------------------------------------------------------------------------- + +bool GuiSliderCtrl::onMouseWheelUp(const GuiEvent &event) +{ + if ( !mActive || !mAwake || !mVisible ) + return Parent::onMouseWheelUp(event); + + _updateThumb( mValue + mIncAmount, ( event.modifier & SI_SHIFT ) ); + execConsoleCallback(); + + return true; +} + +//---------------------------------------------------------------------------- + +bool GuiSliderCtrl::onMouseWheelDown(const GuiEvent &event) +{ + if ( !mActive || !mAwake || !mVisible ) + return Parent::onMouseWheelUp(event); + + _updateThumb( mValue - mIncAmount, ( event.modifier & SI_SHIFT ) ); + execConsoleCallback(); + + return true; +} + +//---------------------------------------------------------------------------- + +void GuiSliderCtrl::_updateThumb( F32 _value, bool snap, bool onWake, bool doCallback ) +{ + if( snap && mTicks > 0 ) + { + // If the shift key is held, snap to the nearest tick, if any are being drawn + + F32 tickStep = (mRange.y - mRange.x) / F32(mTicks + 1); + + F32 tickSteps = (_value - mRange.x) / tickStep; + S32 actualTick = S32(tickSteps + 0.5); + + _value = actualTick * tickStep + mRange.x; + } + + // Clamp the thumb to legal values. + + if( _value < mRange.x ) + _value = mRange.x; + if( _value > mRange.y ) + _value = mRange.y; + + // If value hasn't changed and this isn't the initial update on + // waking, do nothing. + + if( mValue == _value && !onWake ) + return; + + mValue = _value; + + Point2I ext = getExtent(); + ext.x -= ( mShiftExtent + mThumbSize.x ) / 2; + // update the bounding thumb rect + if (getWidth() >= getHeight()) + { // HORZ thumb + S32 mx = (S32)((F32(ext.x) * (mValue-mRange.x) / (mRange.y-mRange.x))); + S32 my = ext.y/2; + if(mDisplayValue) + my = mThumbSize.y/2; + + mThumb.point.x = mx - (mThumbSize.x/2); + mThumb.point.y = my - (mThumbSize.y/2); + mThumb.extent = mThumbSize; + } + else + { // VERT thumb + S32 mx = ext.x/2; + S32 my = (S32)((F32(ext.y) * (mValue-mRange.x) / (mRange.y-mRange.x))); + mThumb.point.x = mx - (mThumbSize.y/2); + mThumb.point.y = my - (mThumbSize.x/2); + mThumb.extent.x = mThumbSize.y; + mThumb.extent.y = mThumbSize.x; + } + + setFloatVariable(mValue); + setUpdate(); + + // Use the alt console command if you want to continually update: + if ( !onWake && doCallback ) + execAltConsoleCallback(); +} + +//---------------------------------------------------------------------------- + +void GuiSliderCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + Point2I pos(offset.x+mShiftPoint, offset.y); + Point2I ext(getWidth() - mShiftExtent, getHeight()); + RectI thumb = mThumb; + + if( mHasTexture ) + { + if(mTicks > 0) + { + // TODO: tick marks should be positioned based on the bitmap dimensions. + Point2I mid(ext.x, ext.y/2); + Point2I oldpos = pos; + pos += Point2I(1, 0); + + PrimBuild::color4f( 0.f, 0.f, 0.f, 1.f ); + PrimBuild::begin( GFXLineList, ( mTicks + 2 ) * 2 ); + // tick marks + for (U32 t = 0; t <= (mTicks+1); t++) + { + S32 x = (S32)(F32(mid.x+1)/F32(mTicks+1)*F32(t)) + pos.x; + S32 y = pos.y + mid.y; + PrimBuild::vertex2i(x, y + mShiftPoint); + PrimBuild::vertex2i(x, y + mShiftPoint*2 + 2); + } + PrimBuild::end(); + // TODO: it would be nice, if the primitive builder were a little smarter, + // so that we could change colors midstream. + PrimBuild::color4f(0.9f, 0.9f, 0.9f, 1.0f); + PrimBuild::begin( GFXLineList, ( mTicks + 2 ) * 2 ); + // tick marks + for (U32 t = 0; t <= (mTicks+1); t++) + { + S32 x = (S32)(F32(mid.x+1)/F32(mTicks+1)*F32(t)) + pos.x + 1; + S32 y = pos.y + mid.y + 1; + PrimBuild::vertex2i(x, y + mShiftPoint ); + PrimBuild::vertex2i(x, y + mShiftPoint * 2 + 3); + } + PrimBuild::end(); + pos = oldpos; + } + + S32 index = SliderButtonNormal; + if(mMouseOver) + index = SliderButtonHighlight; + GFX->getDrawUtil()->clearBitmapModulation(); + + //left border + GFX->getDrawUtil()->drawBitmapSR(mProfile->mTextureObject, Point2I(offset.x,offset.y), mBitmapBounds[SliderLineLeft]); + //right border + GFX->getDrawUtil()->drawBitmapSR(mProfile->mTextureObject, Point2I(offset.x + getWidth() - mBitmapBounds[SliderLineRight].extent.x, offset.y), mBitmapBounds[SliderLineRight]); + + + //draw our center piece to our slider control's border and stretch it + RectI destRect; + destRect.point.x = offset.x + mBitmapBounds[SliderLineLeft].extent.x; + destRect.extent.x = getWidth() - mBitmapBounds[SliderLineLeft].extent.x - mBitmapBounds[SliderLineRight].extent.x; + destRect.point.y = offset.y; + destRect.extent.y = mBitmapBounds[SliderLineCenter].extent.y; + + RectI stretchRect; + stretchRect = mBitmapBounds[SliderLineCenter]; + stretchRect.inset(1,0); + + GFX->getDrawUtil()->drawBitmapStretchSR(mProfile->mTextureObject, destRect, stretchRect); + + //draw our control slider button + thumb.point += pos; + GFX->getDrawUtil()->drawBitmapSR(mProfile->mTextureObject,Point2I(thumb.point.x,offset.y ),mBitmapBounds[index]); + + } + else if (getWidth() >= getHeight()) + { + Point2I mid(ext.x, ext.y/2); + if(mDisplayValue) + mid.set(ext.x, mThumbSize.y/2); + + PrimBuild::color4f( 0.f, 0.f, 0.f, 1.f ); + PrimBuild::begin( GFXLineList, ( mTicks + 2 ) * 2 + 2); + // horz rule + PrimBuild::vertex2i( pos.x, pos.y + mid.y ); + PrimBuild::vertex2i( pos.x + mid.x, pos.y + mid.y ); + + // tick marks + for( U32 t = 0; t <= ( mTicks + 1 ); t++ ) + { + S32 x = (S32)( F32( mid.x - 1 ) / F32( mTicks + 1 ) * F32( t ) ); + PrimBuild::vertex2i( pos.x + x, pos.y + mid.y - mShiftPoint ); + PrimBuild::vertex2i( pos.x + x, pos.y + mid.y + mShiftPoint ); + } + PrimBuild::end(); + } + else + { + Point2I mid(ext.x/2, ext.y); + + PrimBuild::color4f( 0.f, 0.f, 0.f, 1.f ); + PrimBuild::begin( GFXLineList, ( mTicks + 2 ) * 2 + 2); + // horz rule + PrimBuild::vertex2i( pos.x + mid.x, pos.y ); + PrimBuild::vertex2i( pos.x + mid.x, pos.y + mid.y ); + + // tick marks + for( U32 t = 0; t <= ( mTicks + 1 ); t++ ) + { + S32 y = (S32)( F32( mid.y - 1 ) / F32( mTicks + 1 ) * F32( t ) ); + PrimBuild::vertex2i( pos.x + mid.x - mShiftPoint, pos.y + y ); + PrimBuild::vertex2i( pos.x + mid.x + mShiftPoint, pos.y + y ); + } + PrimBuild::end(); + mDisplayValue = false; + } + // draw the thumb + thumb.point += pos; + renderRaisedBox(thumb, mProfile); + + if(mDisplayValue) + { + char buf[20]; + dSprintf(buf,sizeof(buf),"%0.3f",mValue); + + Point2I textStart = thumb.point; + + S32 txt_w = mProfile->mFont->getStrWidth((const UTF8 *)buf); + + textStart.x += (S32)((thumb.extent.x/2.0f)); + textStart.y += thumb.extent.y - 2; //19 + textStart.x -= (txt_w/2); + if(textStart.x < offset.x) + textStart.x = offset.x; + else if(textStart.x + txt_w > offset.x+getWidth()) + textStart.x -=((textStart.x + txt_w) - (offset.x+getWidth())); + + GFX->getDrawUtil()->setBitmapModulation(mProfile->mFontColor); + GFX->getDrawUtil()->drawText(mProfile->mFont, textStart, buf, mProfile->mFontColors); + } + renderChildControls(offset, updateRect); +} + +//---------------------------------------------------------------------------- + +bool GuiSliderCtrl::resize( const Point2I& newPosition, const Point2I& newSize ) +{ + if( !Parent::resize( newPosition, newSize ) ) + return false; + + _updateThumb( mValue, false, true, false ); + return true; +} + +//---------------------------------------------------------------------------- + +void GuiSliderCtrl::parentResized( const RectI& oldParentRect, const RectI& newParentRect ) +{ + Parent::parentResized( oldParentRect, newParentRect ); + + _updateThumb( mValue, false, true, false ); +} + +//---------------------------------------------------------------------------- + +F32 GuiSliderCtrl::_getThumbValue( const GuiEvent& event ) +{ + Point2I curMousePos = globalToLocalCoord( event.mousePoint ); + + F32 value; + if( getWidth() >= getHeight() ) + value = F32( curMousePos.x - mShiftPoint ) / F32( getWidth() - mShiftExtent ) * ( mRange.y - mRange.x ) + mRange.x; + else + value = F32( curMousePos.y ) / F32( getHeight() ) * ( mRange.y - mRange.x ) + mRange.x; + + if(value > mRange.y ) + value = mRange.y; + else if( value < mRange.x ) + value = mRange.x; + + if( mSnap || ( event.modifier & SI_SHIFT && mTicks >= 1 ) ) + { + // If the shift key is held, snap to the nearest tick, if any are being drawn + + F32 tickStep = ( mRange.y - mRange.x ) / F32( mTicks + 1 ); + + F32 tickSteps = (value - mRange.x ) / tickStep; + S32 actualTick = S32( tickSteps + 0.5 ); + + value = actualTick * tickStep + mRange.x; + AssertFatal( value <= mRange.y && value >= mRange.x, "Error, out of bounds value generated from shift-snap of slider" ); + } + + return value; +} + +//============================================================================= +// Console Methods. +//============================================================================= + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiSliderCtrl, getValue, F32, (),, + "Get the current value of the slider based on the position of the thumb.\n" + "@return Slider position (from range.x to range.y)." ) +{ + return object->getValue(); +} + +//---------------------------------------------------------------------------- + +DefineEngineMethod( GuiSliderCtrl, setValue, void, ( F32 pos, bool doCallback ), ( false ), + "Set position of the thumb on the slider.\n" + "@param pos New slider position (from range.x to range.y)\n" + "@param doCallback If true, the altCommand callback will be invoked\n" ) +{ + object->setValue( pos, doCallback ); +} + +//---------------------------------------------------------------------------- + +DefineEngineMethod( GuiSliderCtrl, isThumbBeingDragged, bool, (),, + "Returns true if the thumb is currently being dragged by the user. This method is mainly useful " + "for scrubbing type sliders where the slider position is sync'd to a changing value. When the " + "user is dragging the thumb, however, the sync'ing should pause and not get in the way of the user." ) +{ + return object->isThumbBeingDragged(); +} diff --git a/Engine/source/gui/controls/guiSliderCtrl.h b/Engine/source/gui/controls/guiSliderCtrl.h new file mode 100644 index 000000000..9a70c512e --- /dev/null +++ b/Engine/source/gui/controls/guiSliderCtrl.h @@ -0,0 +1,116 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUISLIDERCTRL_H_ +#define _GUISLIDERCTRL_H_ + +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif + + +/// A slider control that selects out of a floating-point value range. +class GuiSliderCtrl : public GuiControl +{ + public: + + typedef GuiControl Parent; + + protected: + + Point2F mRange; + U32 mTicks; + bool mSnap; + F32 mValue; + RectI mThumb; + Point2I mThumbSize; + S32 mShiftPoint; + S32 mShiftExtent; + F32 mIncAmount; + bool mDisplayValue; + bool mDepressed; + bool mMouseOver; + bool mMouseDragged; + bool mHasTexture; + + enum + { + SliderLineLeft = 0, + SliderLineCenter, + SliderLineRight, + SliderButtonNormal, + SliderButtonHighlight, + NumBitmaps + }; + RectI *mBitmapBounds; + + F32 _getThumbValue( const GuiEvent& event ); + void _updateThumb( F32 value, bool snap = true, bool onWake = false, bool doCallback = true ); + + /// @name Callbacks + /// @{ + + DECLARE_CALLBACK( void, onMouseDragged, () ); + + /// @} + + static bool _setValue( void* object, const char* index, const char* data ) { static_cast< GuiSliderCtrl* >( object )->setValue( dAtof( data ) ); return false; } + + public: + + GuiSliderCtrl(); + + bool isThumbBeingDragged() const { return mDepressed; } + + const Point2F& getRange() const { return mRange; } + + // GuiControl. + bool onWake(); + + void onMouseDown(const GuiEvent &event); + void onMouseDragged(const GuiEvent &event); + void onMouseUp(const GuiEvent &); + void onMouseLeave(const GuiEvent &); + void onMouseEnter(const GuiEvent &); + bool onMouseWheelUp(const GuiEvent &event); + bool onMouseWheelDown(const GuiEvent &event); + + void setActive( bool value ); + + F32 getValue() const { return mValue; } + void setScriptValue(const char *val) { setValue(dAtof(val)); } + void setValue(F32 val, bool doCallback=false); + + void onRender(Point2I offset, const RectI &updateRect); + + virtual bool resize( const Point2I& newSize, const Point2I& newExtent ); + virtual void parentResized( const RectI& oldParentRect, const RectI& newParentRect ); + + static void initPersistFields(); + + DECLARE_CONOBJECT(GuiSliderCtrl); + DECLARE_CATEGORY( "Gui Values" ); + DECLARE_DESCRIPTION( "A control that implements a horizontal or vertical slider to\n" + "select/represent values in a certain range." ) +}; + +#endif diff --git a/Engine/source/gui/controls/guiTabPageCtrl.cpp b/Engine/source/gui/controls/guiTabPageCtrl.cpp new file mode 100644 index 000000000..ada0a2712 --- /dev/null +++ b/Engine/source/gui/controls/guiTabPageCtrl.cpp @@ -0,0 +1,234 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/consoleTypes.h" +#include "console/console.h" +#include "console/engineAPI.h" +#include "gfx/gfxDevice.h" +#include "gui/core/guiCanvas.h" +#include "gui/controls/guiTabPageCtrl.h" +#include "gui/containers/guiTabBookCtrl.h" +#include "gui/core/guiDefaultControlRender.h" +#include "gui/editor/guiEditCtrl.h" + +IMPLEMENT_CONOBJECT(GuiTabPageCtrl); + +ConsoleDocClass( GuiTabPageCtrl, + "@brief A single page in a GuiTabBookCtrl.\n\n" + + "@tsexample\n\n" + "new GuiTabPageCtrl()\n" + "{\n" + " fitBook = \"1\";\n" + " //Properties not specific to this control have been omitted from this example.\n" + "};\n" + "@endtsexample\n\n" + + "@ingroup GuiContainers" +); + +GuiTabPageCtrl::GuiTabPageCtrl(void) +{ + setExtent(Point2I(100, 200)); + mFitBook = false; + dStrcpy(mText,(UTF8*)"TabPage"); + mActive = true; + mIsContainer = true; +} + +void GuiTabPageCtrl::initPersistFields() +{ + addField( "fitBook", TypeBool, Offset( mFitBook, GuiTabPageCtrl ), + "Determines whether to resize this page when it is added to the tab book. " + "If true, the page will be resized according to the tab book extents and " + "tabPosition property." ); + + Parent::initPersistFields(); +} + +bool GuiTabPageCtrl::onWake() +{ + if (! Parent::onWake()) + return false; + + return true; +} + +void GuiTabPageCtrl::onSleep() +{ + Parent::onSleep(); +} + +GuiControl* GuiTabPageCtrl::findHitControl(const Point2I &pt, S32 initialLayer) +{ + return Parent::findHitControl(pt, initialLayer); +} + +void GuiTabPageCtrl::onMouseDown(const GuiEvent &event) +{ + setUpdate(); + Point2I localPoint = globalToLocalCoord( event.mousePoint ); + + GuiControl *ctrl = findHitControl(localPoint); + if (ctrl && ctrl != this) + { + ctrl->onMouseDown(event); + } +} + +bool GuiTabPageCtrl::onMouseDownEditor(const GuiEvent &event, Point2I offset ) +{ +#ifdef TORQUE_TOOLS + // This shouldn't be called if it's not design time, but check just incase + if ( GuiControl::smDesignTime ) + { + GuiEditCtrl* edit = GuiControl::smEditorHandle; + if( edit ) + edit->select( this ); + } + + return Parent::onMouseDownEditor( event, offset ); +#else + return false; +#endif +} + + +GuiControl *GuiTabPageCtrl::findNextTabable(GuiControl *curResponder, bool firstCall) +{ + //set the global if this is the first call (directly from the canvas) + if (firstCall) + { + GuiControl::smCurResponder = NULL; + } + + //if the window does not already contain the first responder, return false + //ie. Can't tab into or out of a window + if (! controlIsChild(curResponder)) + { + return NULL; + } + + //loop through, checking each child to see if it is the one that follows the firstResponder + GuiControl *tabCtrl = NULL; + iterator i; + for (i = begin(); i != end(); i++) + { + GuiControl *ctrl = static_cast(*i); + tabCtrl = ctrl->findNextTabable(curResponder, false); + if (tabCtrl) break; + } + + //to ensure the tab cycles within the current window... + if (! tabCtrl) + { + tabCtrl = findFirstTabable(); + } + + mFirstResponder = tabCtrl; + return tabCtrl; +} + +GuiControl *GuiTabPageCtrl::findPrevTabable(GuiControl *curResponder, bool firstCall) +{ + if (firstCall) + { + GuiControl::smPrevResponder = NULL; + } + + //if the window does not already contain the first responder, return false + //ie. Can't tab into or out of a window + if (! controlIsChild(curResponder)) + { + return NULL; + } + + //loop through, checking each child to see if it is the one that follows the firstResponder + GuiControl *tabCtrl = NULL; + iterator i; + for (i = begin(); i != end(); i++) + { + GuiControl *ctrl = static_cast(*i); + tabCtrl = ctrl->findPrevTabable(curResponder, false); + if (tabCtrl) break; + } + + //to ensure the tab cycles within the current window... + if (! tabCtrl) + { + tabCtrl = findLastTabable(); + } + + mFirstResponder = tabCtrl; + return tabCtrl; +} + +void GuiTabPageCtrl::setText(const char *txt) +{ + Parent::setText( txt ); + + GuiControl *parent = getParent(); + if( parent ) + parent->setUpdate(); +}; + + +void GuiTabPageCtrl::selectWindow(void) +{ + //first make sure this window is the front most of its siblings + GuiControl *parent = getParent(); + if (parent) + { + parent->pushObjectToBack(this); + } + + //also set the first responder to be the one within this window + setFirstResponder(mFirstResponder); +} + +void GuiTabPageCtrl::onRender(Point2I offset,const RectI &updateRect) +{ + // Call directly into GuiControl to skip the GuiTextCtrl parent render + GuiControl::onRender( offset, updateRect ); +} + +void GuiTabPageCtrl::inspectPostApply() +{ + Parent::inspectPostApply(); + + if( mFitBook ) + { + GuiTabBookCtrl* book = dynamic_cast< GuiTabBookCtrl* >( getParent() ); + if( book ) + book->fitPage( this ); + } +} + +DefineEngineMethod( GuiTabPageCtrl, select, void, (),, + "Select this page in its tab book." ) +{ + GuiTabBookCtrl* book = dynamic_cast< GuiTabBookCtrl* >( object->getParent() ); + if( !book ) + return; + + book->selectPage( object ); +} diff --git a/Engine/source/gui/controls/guiTabPageCtrl.h b/Engine/source/gui/controls/guiTabPageCtrl.h new file mode 100644 index 000000000..8b10718fd --- /dev/null +++ b/Engine/source/gui/controls/guiTabPageCtrl.h @@ -0,0 +1,72 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUITABPAGECTRL_H_ +#define _GUITABPAGECTRL_H_ + +#ifndef _GUITEXTCTRL_H_ +#include "gui/controls/guiTextCtrl.h" +#endif + +class GuiTabPageCtrl : public GuiTextCtrl +{ + private: + typedef GuiTextCtrl Parent; + + bool mFitBook; ///< Resize to fit book when first added + S32 mTabIndex; + + public: + GuiTabPageCtrl(); + + DECLARE_CONOBJECT(GuiTabPageCtrl); + DECLARE_CATEGORY( "Gui Containers" ); + DECLARE_DESCRIPTION( "A page in a GuiTabBookCtrl." ); + + static void initPersistFields(); + + bool onWake(); ///< The page awakens (becomes active)! + void onSleep(); ///< The page sleeps (zzzzZZ - becomes inactive) + void inspectPostApply(); + + bool getFitBook() { return mFitBook; } + void setFitBook(bool state) { mFitBook = state; } + + GuiControl* findHitControl(const Point2I &pt, S32 initialLayer = -1); ///< Find which control is hit by the mouse starting at a specified layer + + void onMouseDown(const GuiEvent &event); ///< Called when a mouseDown event occurs + bool onMouseDownEditor(const GuiEvent &event, Point2I offset ); ///< Called when a mouseDown event occurs and the GUI editor is active + + S32 getTabIndex(void) { return mTabIndex; } ///< Get the tab index of this control + + //only cycle tabs through the current window, so overwrite the method + GuiControl* findNextTabable(GuiControl *curResponder, bool firstCall = true); + GuiControl* findPrevTabable(GuiControl *curResponder, bool firstCall = true); + + void selectWindow(void); ///< Select this window + + virtual void setText(const char *txt = NULL); ///< Override setText function to signal parent we need to update. + + void onRender(Point2I offset, const RectI &updateRect); ///< Called when it's time to render this page to the scene +}; + +#endif //_GUI_WINDOW_CTRL_H diff --git a/Engine/source/gui/controls/guiTextCtrl.cpp b/Engine/source/gui/controls/guiTextCtrl.cpp new file mode 100644 index 000000000..1b8c8e9e0 --- /dev/null +++ b/Engine/source/gui/controls/guiTextCtrl.cpp @@ -0,0 +1,246 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/controls/guiTextCtrl.h" + +#include "gui/core/guiDefaultControlRender.h" +#include "console/consoleTypes.h" +#include "console/console.h" +#include "core/color.h" +#include "i18n/lang.h" +#include "gfx/gfxDrawUtil.h" +#include "console/engineAPI.h" + +IMPLEMENT_CONOBJECT( GuiTextCtrl ); + +ConsoleDocClass( GuiTextCtrl, + "@brief GUI control object this displays a single line of text, without TorqueML.\n\n" + + "@tsexample\n" + " new GuiTextCtrl()\n" + " {\n" + " text = \"Hello World\";\n" + " textID = \"\"STR_HELLO\"\";\n" + " maxlength = \"1024\";\n" + " //Properties not specific to this control have been omitted from this example.\n" + " };\n" + "@endtsexample\n\n" + + "@see GuiControl\n" + "@see Localization\n\n" + "@ingroup GuiCore\n" +); + +GuiTextCtrl::GuiTextCtrl() +{ + //default fonts + mInitialText = StringTable->insert(""); + mInitialTextID = StringTable->insert(""); + mText[0] = '\0'; + mMaxStrLen = GuiTextCtrl::MAX_STRING_LENGTH; +} + +DefineEngineMethod( GuiTextCtrl, setText, void, (const char* text),, + "@brief Sets the text in the control.\n\n" + "@param text Text to display in the control.\n" + "@tsexample\n" + "// Set the text to show in the control\n" + "%text = \"Gideon - Destroyer of World\";\n\n" + "// Inform the GuiTextCtrl control to change its text to the defined value\n" + "%thisGuiTextCtrl.setText(%text);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->setText( text ); +} + +DefineEngineMethod( GuiTextCtrl, setTextID, void, (const char* textID),, + "@brief Maps the text ctrl to a variable used in localization, rather than raw text.\n\n" + "@param textID Name of variable text should be mapped to\n" + "@tsexample\n" + "// Inform the GuiTextCtrl control of the textID to use\n" + "%thisGuiTextCtrl.setTextID(\"STR_QUIT\");\n" + "@endtsexample\n\n" + "@see GuiControl" + "@see Localization") +{ + object->setTextID( textID ); +} + +void GuiTextCtrl::initPersistFields() +{ + addProtectedField("text", TypeCaseString, Offset(mInitialText, GuiTextCtrl), setText, getTextProperty, + "The text to show on the control."); + + addField( "textID", TypeString, Offset( mInitialTextID, GuiTextCtrl ), + "Maps the text of this control to a variable used in localization, rather than raw text."); + + addField( "maxLength", TypeS32, Offset( mMaxStrLen, GuiTextCtrl ), + "Defines the maximum length of the text. The default is 1024." ); + + Parent::initPersistFields(); +} + +bool GuiTextCtrl::onAdd() +{ + if(!Parent::onAdd()) + return false; + + dStrncpy(mText, (UTF8*)mInitialText, MAX_STRING_LENGTH); + mText[MAX_STRING_LENGTH] = '\0'; + + + return true; +} + +void GuiTextCtrl::inspectPostApply() +{ + Parent::inspectPostApply(); + if(mInitialTextID && *mInitialTextID != 0) + setTextID(mInitialTextID); + else if( mConsoleVariable[ 0 ] ) + setText( getVariable() ); + else + setText(mInitialText); +} + +bool GuiTextCtrl::onWake() +{ + if ( !Parent::onWake() ) + return false; + + if( !mProfile->mFont ) + { + Con::errorf( "GuiTextCtrl::onWake() - no valid font in profile '%s'", mProfile->getName() ); + return false; + } + if(mInitialTextID && *mInitialTextID != 0) + setTextID(mInitialTextID); + + if ( mConsoleVariable[0] ) + { + const char *txt = Con::getVariable( mConsoleVariable ); + if ( txt ) + { + if ( dStrlen( txt ) > mMaxStrLen ) + { + char* buf = new char[mMaxStrLen + 1]; + dStrncpy( buf, txt, mMaxStrLen ); + buf[mMaxStrLen] = 0; + setScriptValue( buf ); + delete [] buf; + } + else + setScriptValue( txt ); + } + } + + //resize + autoResize(); + + return true; +} + +void GuiTextCtrl::autoResize() +{ + if( mProfile->mAutoSizeWidth || mProfile->mAutoSizeHeight) + { + if( !mProfile->mFont ) + { + mProfile->loadFont(); + if( !mProfile->mFont ) + return; + } + + Point2I newExtents = getExtent(); + if ( mProfile->mAutoSizeWidth ) + newExtents.x = mProfile->mFont->getStrWidth((const UTF8 *) mText ); + if ( mProfile->mAutoSizeHeight ) + newExtents.y = mProfile->mFont->getHeight() + 4; + + setExtent( newExtents ); + } +} + +void GuiTextCtrl::setText(const char *txt) +{ + //make sure we don't call this before onAdd(); + if( !mProfile ) + return; + + if (txt) + dStrncpy(mText, (UTF8*)txt, MAX_STRING_LENGTH); + mText[MAX_STRING_LENGTH] = '\0'; + + setVariable((char*)mText); + setUpdate(); + + autoResize(); +} + +void GuiTextCtrl::setTextID(const char *id) +{ + S32 n = Con::getIntVariable(id, -1); + if(n != -1) + { + mInitialTextID = StringTable->insert(id); + setTextID(n); + } +} +void GuiTextCtrl::setTextID(S32 id) +{ + const UTF8 *str = getGUIString(id); + if(str) + setText((const char*)str); + //mInitialTextID = id; +} + +void GuiTextCtrl::onPreRender() +{ + Parent::onPreRender(); + + const char * var = getVariable(); + if(var && var[0] && dStricmp((char*)mText, var)) + setText(var); +} + +void GuiTextCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + renderBorder( RectI( offset, getExtent() ), mProfile ); + + GFX->getDrawUtil()->setBitmapModulation( mProfile->mFontColor ); + renderJustifiedText(offset, getExtent(), (char*)mText); + + //render the child controls + renderChildControls(offset, updateRect); +} + +const char *GuiTextCtrl::getScriptValue() +{ + return getText(); +} + +void GuiTextCtrl::setScriptValue(const char *val) +{ + setText(val); +} diff --git a/Engine/source/gui/controls/guiTextCtrl.h b/Engine/source/gui/controls/guiTextCtrl.h new file mode 100644 index 000000000..5f1ac25dd --- /dev/null +++ b/Engine/source/gui/controls/guiTextCtrl.h @@ -0,0 +1,92 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUITEXTCTRL_H_ +#define _GUITEXTCTRL_H_ + +#ifndef _GFONT_H_ +#include "gfx/gFont.h" +#endif +#ifndef _GUITYPES_H_ +#include "gui/core/guiTypes.h" +#endif +#ifndef _GUICONTAINER_H_ +#include "gui/containers/guiContainer.h" +#endif + +class GuiTextCtrl : public GuiContainer +{ +private: + typedef GuiContainer Parent; + +public: + enum Constants { MAX_STRING_LENGTH = 1024 }; + + +protected: + StringTableEntry mInitialText; + StringTableEntry mInitialTextID; + UTF8 mText[MAX_STRING_LENGTH + 1]; + S32 mMaxStrLen; // max string len, must be less then or equal to 255 + +public: + + //creation methods + DECLARE_CONOBJECT(GuiTextCtrl); + DECLARE_CATEGORY( "Gui Text" ); + DECLARE_DESCRIPTION( "A control that displays a single line of text." ); + + GuiTextCtrl(); + static void initPersistFields(); + + //Parental methods + bool onAdd(); + virtual bool onWake(); + + //text methods + virtual void setText(const char *txt = NULL); + virtual void setTextID(S32 id); + virtual void setTextID(const char *id); + const char *getText() { return (const char*)mText; } + + // Text Property Accessors + static bool setText(void *object, const char *index, const char *data) + { static_cast(object)->setText(data); return true; } + static const char* getTextProperty(void* obj, const char* data) + { return static_cast(obj)->getText(); } + + + void inspectPostApply(); + //rendering methods + void onPreRender(); + void onRender(Point2I offset, const RectI &updateRect); + void displayText( S32 xOffset, S32 yOffset ); + + // resizing + void autoResize(); + + //Console methods + const char *getScriptValue(); + void setScriptValue(const char *value); +}; + +#endif //_GUI_TEXT_CONTROL_H_ diff --git a/Engine/source/gui/controls/guiTextEditCtrl.cpp b/Engine/source/gui/controls/guiTextEditCtrl.cpp new file mode 100644 index 000000000..1748d5ce7 --- /dev/null +++ b/Engine/source/gui/controls/guiTextEditCtrl.cpp @@ -0,0 +1,1748 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/controls/guiTextEditCtrl.h" + +#include "console/consoleTypes.h" +#include "console/console.h" +#include "gui/core/guiCanvas.h" +#include "gui/controls/guiMLTextCtrl.h" +#include "gui/core/guiDefaultControlRender.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxDrawUtil.h" +#include "core/frameAllocator.h" +#include "sfx/sfxTrack.h" +#include "sfx/sfxTypes.h" +#include "sfx/sfxSystem.h" +#include "core/strings/unicode.h" +#include "console/engineAPI.h" + +IMPLEMENT_CONOBJECT(GuiTextEditCtrl); + +ConsoleDocClass( GuiTextEditCtrl, + "@brief A component that places a text entry box on the screen.\n\n" + + "Fonts and sizes are changed using profiles. The text value can be set or entered by a user.\n\n" + + "@tsexample\n" + " new GuiTextEditCtrl(MessageHud_Edit)\n" + " {\n" + " text = \"Hello World\";\n" + " validate = \"validateCommand();\"\n" + " escapeCommand = \"escapeCommand();\";\n" + " historySize = \"5\";\n" + " tabComplete = \"true\";\n" + " deniedSound = \"DeniedSoundProfile\";\n" + " sinkAllKeyEvents = \"true\";\n" + " password = \"true\";\n" + " passwordMask = \"*\";\n" + " //Properties not specific to this control have been omitted from this example.\n" + " };\n" + "@endtsexample\n\n" + + "@see GuiTextCtrl\n" + "@see GuiControl\n\n" + + "@ingroup GuiControls\n" +); + +IMPLEMENT_CALLBACK( GuiTextEditCtrl, onTabComplete, void, (const char* val),( val ), + "@brief Called if tabComplete is true, and the 'tab' key is pressed.\n\n" + "@param val Input to mimick the '1' sent by the actual tab key button press.\n" + "@tsexample\n" + "// Tab key has been pressed, causing the callback to occur.\n" + "GuiTextEditCtrl::onTabComplete(%this,%val)\n" + " {\n" + " //Code to run when the onTabComplete callback occurs\n" + " }\n" + "@endtsexample\n\n" + "@see GuiTextCtrl\n" + "@see GuiControl\n\n" +); + +IMPLEMENT_CALLBACK( GuiTextEditCtrl, onReturn, void, (),(), + "@brief Called when the 'Return' or 'Enter' key is pressed.\n\n" + "@tsexample\n" + "// Return or Enter key was pressed, causing the callback to occur.\n" + "GuiTextEditCtrl::onReturn(%this)\n" + " {\n" + " // Code to run when the onReturn callback occurs\n" + " }\n" + "@endtsexample\n\n" + "@see GuiTextCtrl\n" + "@see GuiControl\n\n" +); + +IMPLEMENT_CALLBACK( GuiTextEditCtrl, onValidate, void, (),(), + "@brief Called whenever the control is validated.\n\n" + "@tsexample\n" + "// The control gets validated, causing the callback to occur\n" + "GuiTextEditCtrl::onValidated(%this)\n" + " {\n" + " // Code to run when the control is validated\n" + " }\n" + "@endtsexample\n\n" + "@see GuiTextCtrl\n" + "@see GuiControl\n\n" +); + +GuiTextEditCtrl::GuiTextEditCtrl() +{ + mInsertOn = true; + mBlockStart = 0; + mBlockEnd = 0; + mCursorPos = 0; + mCursorOn = false; + mNumFramesElapsed = 0; + + mDragHit = false; + mTabComplete = false; + mScrollDir = 0; + + mUndoBlockStart = 0; + mUndoBlockEnd = 0; + mUndoCursorPos = 0; + mPasswordText = false; + + mSinkAllKeyEvents = false; + + mActive = true; + + mTextOffsetReset = true; + + mHistoryDirty = false; + mHistorySize = 0; + mHistoryLast = -1; + mHistoryIndex = 0; + mHistoryBuf = NULL; + +#if defined(__MACOSX__) + UTF8 bullet[4] = { 0xE2, 0x80, 0xA2, 0 }; + + mPasswordMask = StringTable->insert( bullet ); +#else + mPasswordMask = StringTable->insert( "*" ); +#endif + Sim::findObject( "InputDeniedSound", mDeniedSound ); +} + +GuiTextEditCtrl::~GuiTextEditCtrl() +{ + //delete the history buffer if it exists + if (mHistoryBuf) + { + for (S32 i = 0; i < mHistorySize; i++) + delete [] mHistoryBuf[i]; + + delete [] mHistoryBuf; + } +} + +void GuiTextEditCtrl::initPersistFields() +{ + addGroup( "Text Input" ); + + addField("validate", TypeRealString,Offset(mValidateCommand, GuiTextEditCtrl), "Script command to be called when the first validater is lost.\n"); + addField("escapeCommand", TypeRealString,Offset(mEscapeCommand, GuiTextEditCtrl), "Script command to be called when the Escape key is pressed.\n"); + addField("historySize", TypeS32, Offset(mHistorySize, GuiTextEditCtrl), "How large of a history buffer to maintain.\n"); + addField("tabComplete", TypeBool, Offset(mTabComplete, GuiTextEditCtrl), "If true, when the 'tab' key is pressed, it will act as if the Enter key was pressed on the control.\n"); + addField("deniedSound", TypeSFXTrackName, Offset(mDeniedSound, GuiTextEditCtrl), "If the attempted text cannot be entered, this sound effect will be played.\n"); + addField("sinkAllKeyEvents", TypeBool, Offset(mSinkAllKeyEvents, GuiTextEditCtrl), "If true, every key event will act as if the Enter key was pressed.\n"); + addField("password", TypeBool, Offset(mPasswordText, GuiTextEditCtrl), "If true, all characters entered will be stored in the control, however will display as the character stored in passwordMask.\n"); + addField("passwordMask", TypeString, Offset(mPasswordMask, GuiTextEditCtrl), "If 'password' is true, this is the character that will be used to mask the characters in the control.\n"); + + endGroup( "Text Input" ); + + Parent::initPersistFields(); +} + +bool GuiTextEditCtrl::onAdd() +{ + if ( ! Parent::onAdd() ) + return false; + + //create the history buffer + if ( mHistorySize > 0 ) + { + mHistoryBuf = new UTF16*[mHistorySize]; + for ( S32 i = 0; i < mHistorySize; i++ ) + { + mHistoryBuf[i] = new UTF16[GuiTextCtrl::MAX_STRING_LENGTH + 1]; + mHistoryBuf[i][0] = '\0'; + } + } + + if( mText && mText[0] ) + { + setText(mText); + } + + return true; +} + +void GuiTextEditCtrl::onStaticModified(const char* slotName, const char* newValue) +{ + if(!dStricmp(slotName, "text")) + setText(mText); +} + +void GuiTextEditCtrl::execConsoleCallback() +{ + // Execute the console command! + Parent::execConsoleCallback(); + + // Update the console variable: + if ( mConsoleVariable[0] ) + Con::setVariable( mConsoleVariable, mTextBuffer.getPtr8() ); +} + +void GuiTextEditCtrl::updateHistory( StringBuffer *inTxt, bool moveIndex ) +{ + if(!mHistorySize) + return; + + const UTF16* txt = inTxt->getPtr(); + + // Reject empty strings. + + if( !txt || !txt[ 0 ] ) + return; + + // see if it's already in + if(mHistoryLast == -1 || dStrcmp(txt, mHistoryBuf[mHistoryLast])) + { + if(mHistoryLast == mHistorySize-1) // we're at the history limit... shuffle the pointers around: + { + UTF16 *first = mHistoryBuf[0]; + for(U32 i = 0; i < mHistorySize - 1; i++) + mHistoryBuf[i] = mHistoryBuf[i+1]; + mHistoryBuf[mHistorySize-1] = first; + if(mHistoryIndex > 0) + mHistoryIndex--; + } + else + mHistoryLast++; + + inTxt->getCopy(mHistoryBuf[mHistoryLast], GuiTextCtrl::MAX_STRING_LENGTH); + mHistoryBuf[mHistoryLast][GuiTextCtrl::MAX_STRING_LENGTH] = '\0'; + } + + if(moveIndex) + mHistoryIndex = mHistoryLast + 1; +} + +void GuiTextEditCtrl::getText( char *dest ) +{ + if ( dest ) + mTextBuffer.getCopy8((UTF8*)dest, GuiTextCtrl::MAX_STRING_LENGTH+1); +} + +void GuiTextEditCtrl::getRenderText(char *dest) +{ + getText( dest ); +} + +void GuiTextEditCtrl::setText( const UTF8 *txt ) +{ + if(txt && txt[0] != 0) + { + Parent::setText(txt); + mTextBuffer.set( txt ); + } + else + mTextBuffer.set( "" ); + + mCursorPos = mTextBuffer.length(); +} + +void GuiTextEditCtrl::setText( const UTF16* txt) +{ + if(txt && txt[0] != 0) + { + UTF8* txt8 = convertUTF16toUTF8( txt ); + Parent::setText( txt8 ); + delete[] txt8; + mTextBuffer.set( txt ); + } + else + { + Parent::setText(""); + mTextBuffer.set(""); + } + + mCursorPos = mTextBuffer.length(); +} + +bool GuiTextEditCtrl::isAllTextSelected() +{ + if( mBlockStart == 0 && mBlockEnd == mTextBuffer.length() ) + return true; + else + return false; +} + +void GuiTextEditCtrl::selectAllText() +{ + mBlockStart = 0; + mBlockEnd = mTextBuffer.length(); + setUpdate(); +} + +void GuiTextEditCtrl::clearSelectedText() +{ + mBlockStart = 0; + mBlockEnd = 0; + setUpdate(); +} + +void GuiTextEditCtrl::forceValidateText() +{ + if( mValidateCommand.isNotEmpty() ) + evaluate( mValidateCommand ); +} + +void GuiTextEditCtrl::setCursorPos( const S32 newPos ) +{ + S32 charCount = mTextBuffer.length(); + S32 realPos = newPos > charCount ? charCount : newPos < 0 ? 0 : newPos; + if ( realPos != mCursorPos ) + { + mCursorPos = realPos; + setUpdate(); + } +} + +S32 GuiTextEditCtrl::calculateCursorPos( const Point2I &globalPos ) +{ + Point2I ctrlOffset = localToGlobalCoord( Point2I( 0, 0 ) ); + S32 charLength = 0; + S32 curX; + + curX = globalPos.x - ctrlOffset.x; + setUpdate(); + + //if the cursor is too far to the left + if ( curX < 0 ) + return -1; + + //if the cursor is too far to the right + if ( curX >= ctrlOffset.x + getExtent().x ) + return -2; + + curX = globalPos.x - mTextOffset.x; + S32 count=0; + if(mTextBuffer.length() == 0) + return 0; + + for(count=0; countmFont->isValidChar(c)) + continue; + + if(mPasswordText) + charLength += mProfile->mFont->getCharXIncrement( mPasswordMask[0] ); + else + charLength += mProfile->mFont->getCharXIncrement( c ); + + if ( charLength > curX ) + break; + } + + return count; +} + +void GuiTextEditCtrl::onMouseDown( const GuiEvent &event ) +{ + mDragHit = false; + + // If we have a double click, select all text. Otherwise + // act as before by clearing any selection. + bool doubleClick = (event.mouseClickCount > 1); + if(doubleClick) + { + selectAllText(); + + } else + { + //undo any block function + mBlockStart = 0; + mBlockEnd = 0; + } + + //find out where the cursor should be + S32 pos = calculateCursorPos( event.mousePoint ); + + // if the position is to the left + if ( pos == -1 ) + mCursorPos = 0; + else if ( pos == -2 ) //else if the position is to the right + mCursorPos = mTextBuffer.length(); + else //else set the mCursorPos + mCursorPos = pos; + + //save the mouseDragPos + mMouseDragStart = mCursorPos; + + // lock the mouse + mouseLock(); + + //set the drag var + mDragHit = true; + + //let the parent get the event + setFirstResponder(); +} + +void GuiTextEditCtrl::onMouseDragged( const GuiEvent &event ) +{ + S32 pos = calculateCursorPos( event.mousePoint ); + + // if the position is to the left + if ( pos == -1 ) + mScrollDir = -1; + else if ( pos == -2 ) // the position is to the right + mScrollDir = 1; + else // set the new cursor position + { + mScrollDir = 0; + mCursorPos = pos; + } + + // update the block: + mBlockStart = getMin( mCursorPos, mMouseDragStart ); + mBlockEnd = getMax( mCursorPos, mMouseDragStart ); + if ( mBlockStart < 0 ) + mBlockStart = 0; + + if ( mBlockStart == mBlockEnd ) + mBlockStart = mBlockEnd = 0; + + //let the parent get the event + Parent::onMouseDragged(event); +} + +void GuiTextEditCtrl::onMouseUp(const GuiEvent &event) +{ + TORQUE_UNUSED(event); + mDragHit = false; + mScrollDir = 0; + mouseUnlock(); +} + +void GuiTextEditCtrl::saveUndoState() +{ + //save the current state + mUndoText.set(&mTextBuffer); + mUndoBlockStart = mBlockStart; + mUndoBlockEnd = mBlockEnd; + mUndoCursorPos = mCursorPos; +} + +void GuiTextEditCtrl::onCopy(bool andCut) +{ + // Don't copy/cut password field! + if(mPasswordText) + return; + + if (mBlockEnd > 0) + { + //save the current state + saveUndoState(); + + //copy the text to the clipboard + UTF8* clipBuff = mTextBuffer.createSubstring8(mBlockStart, mBlockEnd - mBlockStart); + Platform::setClipboard(clipBuff); + delete[] clipBuff; + + //if we pressed the cut shortcut, we need to cut the selected text from the control... + if (andCut) + { + mTextBuffer.cut(mBlockStart, mBlockEnd - mBlockStart); + mCursorPos = mBlockStart; + } + + mBlockStart = 0; + mBlockEnd = 0; + } + +} + +void GuiTextEditCtrl::onPaste() +{ + //first, make sure there's something in the clipboard to copy... + const UTF8 *clipboard = Platform::getClipboard(); + if(dStrlen(clipboard) <= 0) + return; + + //save the current state + saveUndoState(); + + //delete anything hilited + if (mBlockEnd > 0) + { + mTextBuffer.cut(mBlockStart, mBlockEnd - mBlockStart); + mCursorPos = mBlockStart; + mBlockStart = 0; + mBlockEnd = 0; + } + + // We'll be converting to UTF16, and maybe trimming the string, + // so let's use a StringBuffer, for convinience. + StringBuffer pasteText(clipboard); + + // Space left after we remove the highlighted text + S32 stringLen = mTextBuffer.length(); + + // Trim down to fit in a buffer of size mMaxStrLen + S32 pasteLen = pasteText.length(); + + if(stringLen + pasteLen > mMaxStrLen) + { + pasteLen = mMaxStrLen - stringLen; + + pasteText.cut(pasteLen, pasteText.length() - pasteLen); + } + + if (mCursorPos == stringLen) + { + mTextBuffer.append(pasteText); + } + else + { + mTextBuffer.insert(mCursorPos, pasteText); + } + + mCursorPos += pasteLen; +} + +void GuiTextEditCtrl::onUndo() +{ + StringBuffer tempBuffer; + S32 tempBlockStart; + S32 tempBlockEnd; + S32 tempCursorPos; + + //save the current + tempBuffer.set(&mTextBuffer); + tempBlockStart = mBlockStart; + tempBlockEnd = mBlockEnd; + tempCursorPos = mCursorPos; + + //restore the prev + mTextBuffer.set(&mUndoText); + mBlockStart = mUndoBlockStart; + mBlockEnd = mUndoBlockEnd; + mCursorPos = mUndoCursorPos; + + //update the undo + mUndoText.set(&tempBuffer); + mUndoBlockStart = tempBlockStart; + mUndoBlockEnd = tempBlockEnd; + mUndoCursorPos = tempCursorPos; +} + +bool GuiTextEditCtrl::onKeyDown(const GuiEvent &event) +{ + if ( !isActive() || !isAwake() ) + return false; + + S32 stringLen = mTextBuffer.length(); + setUpdate(); + + // Ugly, but now I'm cool like MarkF. + if(event.keyCode == KEY_BACKSPACE) + goto dealWithBackspace; + + if ( event.modifier & SI_SHIFT ) + { + + // Added support for word jump selection. + + if ( event.modifier & SI_CTRL ) + { + switch ( event.keyCode ) + { + case KEY_LEFT: + { + S32 newpos = findPrevWord(); + + if ( mBlockStart == mBlockEnd ) + { + // There was not already a selection so start a new one. + mBlockStart = newpos; + mBlockEnd = mCursorPos; + } + else + { + // There was a selection already... + // In this case the cursor MUST be at either the + // start or end of that selection. + + if ( mCursorPos == mBlockStart ) + { + // We are at the start block and traveling left so + // just extend the start block farther left. + mBlockStart = newpos; + } + else + { + // We are at the end block BUT traveling left + // back towards the start block... + + if ( newpos > mBlockStart ) + { + // We haven't overpassed the existing start block + // so just trim back the end block. + mBlockEnd = newpos; + } + else if ( newpos == mBlockStart ) + { + // We are back at the start, so no more selection. + mBlockEnd = mBlockStart = 0; + } + else + { + // Only other option, we just backtracked PAST + // our original start block. + // So the new position becomes the start block + // and the old start block becomes the end block. + mBlockEnd = mBlockStart; + mBlockStart = newpos; + } + } + } + + mCursorPos = newpos; + + return true; + } + + case KEY_RIGHT: + { + S32 newpos = findNextWord(); + + if ( mBlockStart == mBlockEnd ) + { + // There was not already a selection so start a new one. + mBlockStart = mCursorPos; + mBlockEnd = newpos; + } + else + { + // There was a selection already... + // In this case the cursor MUST be at either the + // start or end of that selection. + + if ( mCursorPos == mBlockEnd ) + { + // We are at the end block and traveling right so + // just extend the end block farther right. + mBlockEnd = newpos; + } + else + { + // We are at the start block BUT traveling right + // back towards the end block... + + if ( newpos < mBlockEnd ) + { + // We haven't overpassed the existing end block + // so just trim back the start block. + mBlockStart = newpos; + } + else if ( newpos == mBlockEnd ) + { + // We are back at the end, so no more selection. + mBlockEnd = mBlockStart = 0; + } + else + { + // Only other option, we just backtracked PAST + // our original end block. + // So the new position becomes the end block + // and the old end block becomes the start block. + mBlockStart = mBlockEnd; + mBlockEnd = newpos; + } + } + } + + mCursorPos = newpos; + + return true; + } + + default: + break; + } + } + + // End support for word jump selection. + + + switch ( event.keyCode ) + { + case KEY_TAB: + if ( mTabComplete ) + { + onTabComplete_callback("1"); + return true; + } + break; // We don't want to fall through if we don't handle the TAB here. + + case KEY_HOME: + mBlockStart = 0; + mBlockEnd = mCursorPos; + mCursorPos = 0; + return true; + + case KEY_END: + mBlockStart = mCursorPos; + mBlockEnd = stringLen; + mCursorPos = stringLen; + return true; + + case KEY_LEFT: + if ((mCursorPos > 0) & (stringLen > 0)) + { + //if we already have a selected block + if (mCursorPos == mBlockEnd) + { + mCursorPos--; + mBlockEnd--; + if (mBlockEnd == mBlockStart) + { + mBlockStart = 0; + mBlockEnd = 0; + } + } + else { + mCursorPos--; + mBlockStart = mCursorPos; + + if (mBlockEnd == 0) + { + mBlockEnd = mCursorPos + 1; + } + } + } + return true; + + case KEY_RIGHT: + if (mCursorPos < stringLen) + { + if ((mCursorPos == mBlockStart) && (mBlockEnd > 0)) + { + mCursorPos++; + mBlockStart++; + if (mBlockStart == mBlockEnd) + { + mBlockStart = 0; + mBlockEnd = 0; + } + } + else + { + if (mBlockEnd == 0) + { + mBlockStart = mCursorPos; + mBlockEnd = mCursorPos; + } + mCursorPos++; + mBlockEnd++; + } + } + return true; + + case KEY_RETURN: + case KEY_NUMPADENTER: + + return dealWithEnter(false); + + default: + break; + } + } + else if (event.modifier & SI_CTRL) + { + switch(event.keyCode) + { +#if defined(TORQUE_OS_MAC) + // Added UNIX emacs key bindings - just a little hack here... + + // Ctrl-B - move one character back + case KEY_B: + { + GuiEvent new_event; + new_event.modifier = 0; + new_event.keyCode = KEY_LEFT; + return(onKeyDown(new_event)); + } + + // Ctrl-F - move one character forward + case KEY_F: + { + GuiEvent new_event; + new_event.modifier = 0; + new_event.keyCode = KEY_RIGHT; + return(onKeyDown(new_event)); + } + + // Ctrl-A - move to the beginning of the line + case KEY_A: + { + GuiEvent new_event; + new_event.modifier = 0; + new_event.keyCode = KEY_HOME; + return(onKeyDown(new_event)); + } + + // Ctrl-E - move to the end of the line + case KEY_E: + { + GuiEvent new_event; + new_event.modifier = 0; + new_event.keyCode = KEY_END; + return(onKeyDown(new_event)); + } + + // Ctrl-P - move backward in history + case KEY_P: + { + GuiEvent new_event; + new_event.modifier = 0; + new_event.keyCode = KEY_UP; + return(onKeyDown(new_event)); + } + + // Ctrl-N - move forward in history + case KEY_N: + { + GuiEvent new_event; + new_event.modifier = 0; + new_event.keyCode = KEY_DOWN; + return(onKeyDown(new_event)); + } + + // Ctrl-D - delete under cursor + case KEY_D: + { + GuiEvent new_event; + new_event.modifier = 0; + new_event.keyCode = KEY_DELETE; + return(onKeyDown(new_event)); + } + + case KEY_U: + { + GuiEvent new_event; + new_event.modifier = SI_CTRL; + new_event.keyCode = KEY_DELETE; + return(onKeyDown(new_event)); + } + + // End added UNIX emacs key bindings +#endif + + // Adding word jump navigation. + + case KEY_LEFT: + { + + mCursorPos = findPrevWord(); + mBlockStart = 0; + mBlockEnd = 0; + return true; + } + + case KEY_RIGHT: + { + mCursorPos = findNextWord(); + mBlockStart = 0; + mBlockEnd = 0; + return true; + } + +#if !defined(TORQUE_OS_MAC) + // Select all + case KEY_A: + { + selectAllText(); + return true; + } + + // windows style cut / copy / paste / undo keybinds + case KEY_C: + case KEY_X: + { + // copy, and cut the text if we hit ctrl-x + onCopy( event.keyCode==KEY_X ); + return true; + } + case KEY_V: + { + onPaste(); + + // Execute the console command! + execConsoleCallback(); + return true; + } + + case KEY_Z: + if (! mDragHit) + { + onUndo(); + return true; + } +#endif + + case KEY_DELETE: + case KEY_BACKSPACE: + //save the current state + saveUndoState(); + + //delete everything in the field + mTextBuffer.set(""); + mCursorPos = 0; + mBlockStart = 0; + mBlockEnd = 0; + + execConsoleCallback(); + + return true; + default: + break; + } + } +#if defined(TORQUE_OS_MAC) + // mac style cut / copy / paste / undo keybinds + else if (event.modifier & SI_ALT) + { + // Mac command key maps to alt in torque. + + // Added Mac cut/copy/paste/undo keys + switch(event.keyCode) + { + // Select all + case KEY_A: + { + selectAllText(); + return true; + } + case KEY_C: + case KEY_X: + { + // copy, and cut the text if we hit cmd-x + onCopy( event.keyCode==KEY_X ); + return true; + } + case KEY_V: + { + onPaste(); + + // Execute the console command! + execConsoleCallback(); + + return true; + } + + case KEY_Z: + if (! mDragHit) + { + onUndo(); + return true; + } + + default: + break; + } + } +#endif + else + { + switch(event.keyCode) + { + case KEY_ESCAPE: + if( mEscapeCommand.isNotEmpty() ) + { + evaluate( mEscapeCommand ); + return( true ); + } + return( Parent::onKeyDown( event ) ); + + case KEY_RETURN: + case KEY_NUMPADENTER: + + return dealWithEnter(true); + + case KEY_UP: + { + if( mHistorySize > 0 ) + { + if(mHistoryDirty) + { + updateHistory(&mTextBuffer, false); + mHistoryDirty = false; + } + + mHistoryIndex--; + + if(mHistoryIndex >= 0 && mHistoryIndex <= mHistoryLast) + setText(mHistoryBuf[mHistoryIndex]); + else if(mHistoryIndex < 0) + mHistoryIndex = 0; + } + + return true; + } + + case KEY_DOWN: + { + if( mHistorySize > 0 ) + { + if(mHistoryDirty) + { + updateHistory(&mTextBuffer, false); + mHistoryDirty = false; + } + mHistoryIndex++; + if(mHistoryIndex > mHistoryLast) + { + mHistoryIndex = mHistoryLast + 1; + setText(""); + } + else + setText(mHistoryBuf[mHistoryIndex]); + } + return true; + } + + case KEY_LEFT: + + // If we have a selection put the cursor to the left side of it. + if ( mBlockStart != mBlockEnd ) + { + mCursorPos = mBlockStart; + mBlockStart = mBlockEnd = 0; + } + else + { + mBlockStart = mBlockEnd = 0; + mCursorPos = getMax( mCursorPos - 1, 0 ); + } + + return true; + + case KEY_RIGHT: + + // If we have a selection put the cursor to the right side of it. + if ( mBlockStart != mBlockEnd ) + { + mCursorPos = mBlockEnd; + mBlockStart = mBlockEnd = 0; + } + else + { + mBlockStart = mBlockEnd = 0; + mCursorPos = getMin( mCursorPos + 1, stringLen ); + } + + return true; + + case KEY_BACKSPACE: +dealWithBackspace: + //save the current state + saveUndoState(); + + if (mBlockEnd > 0) + { + mTextBuffer.cut(mBlockStart, mBlockEnd-mBlockStart); + mCursorPos = mBlockStart; + mBlockStart = 0; + mBlockEnd = 0; + mHistoryDirty = true; + + // Execute the console command! + execConsoleCallback(); + + } + else if (mCursorPos > 0) + { + mTextBuffer.cut(mCursorPos-1, 1); + mCursorPos--; + mHistoryDirty = true; + + // Execute the console command! + execConsoleCallback(); + } + return true; + + case KEY_DELETE: + //save the current state + saveUndoState(); + + if (mBlockEnd > 0) + { + mHistoryDirty = true; + mTextBuffer.cut(mBlockStart, mBlockEnd-mBlockStart); + + mCursorPos = mBlockStart; + mBlockStart = 0; + mBlockEnd = 0; + + // Execute the console command! + execConsoleCallback(); + } + else if (mCursorPos < stringLen) + { + mHistoryDirty = true; + mTextBuffer.cut(mCursorPos, 1); + + // Execute the console command! + execConsoleCallback(); + } + return true; + + case KEY_INSERT: + mInsertOn = !mInsertOn; + return true; + + case KEY_HOME: + mBlockStart = 0; + mBlockEnd = 0; + mCursorPos = 0; + return true; + + case KEY_END: + mBlockStart = 0; + mBlockEnd = 0; + mCursorPos = stringLen; + return true; + + default: + break; + + } + } + + switch ( event.keyCode ) + { + case KEY_TAB: + if ( mTabComplete ) + { + onTabComplete_callback("0"); + return( true ); + } + case KEY_UP: + case KEY_DOWN: + case KEY_ESCAPE: + return Parent::onKeyDown( event ); + default: + break; + } + + // Handle character input events. + + if( mProfile->mFont->isValidChar( event.ascii ) ) + { + handleCharInput( event.ascii ); + return true; + } + + // Or eat it if that's appropriate. + if( mSinkAllKeyEvents ) + return true; + + // Not handled - pass the event to it's parent. + return Parent::onKeyDown( event ); +} + +bool GuiTextEditCtrl::dealWithEnter( bool clearResponder ) +{ + //first validate + if (mProfile->mReturnTab) + { + onLoseFirstResponder(); + } + + updateHistory(&mTextBuffer, true); + mHistoryDirty = false; + + //next exec the alt console command + execAltConsoleCallback(); + + // Notify of Return + onReturn_callback(); + + if (mProfile->mReturnTab) + { + GuiCanvas *root = getRoot(); + if (root) + { + root->tabNext(); + return true; + } + } + + if( clearResponder ) + clearFirstResponder(); + + return true; +} + +void GuiTextEditCtrl::setFirstResponder() +{ + Parent::setFirstResponder(); + + GuiCanvas *root = getRoot(); + if (root != NULL) + { + root->enableKeyboardTranslation(); + + + // If the native OS accelerator keys are not disabled + // then some key events like Delete, ctrl+V, etc may + // not make it down to us. + root->setNativeAcceleratorsEnabled( false ); + } +} + +void GuiTextEditCtrl::onLoseFirstResponder() +{ + GuiCanvas *root = getRoot(); + if( root ) + { + root->setNativeAcceleratorsEnabled( true ); + root->disableKeyboardTranslation(); + } + + //execute the validate command + if( mValidateCommand.isNotEmpty() ) + evaluate( mValidateCommand ); + + onValidate_callback(); + + // Redraw the control: + setUpdate(); + + // Lost Responder + Parent::onLoseFirstResponder(); +} + +void GuiTextEditCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + RectI ctrlRect( offset, getExtent() ); + + //if opaque, fill the update rect with the fill color + if ( mProfile->mOpaque ) + { + if(isFirstResponder()) + GFX->getDrawUtil()->drawRectFill( ctrlRect, mProfile->mFillColorHL ); + else + GFX->getDrawUtil()->drawRectFill( ctrlRect, mProfile->mFillColor ); + } + + //if there's a border, draw the border + if ( mProfile->mBorder ) + renderBorder( ctrlRect, mProfile ); + + drawText( ctrlRect, isFirstResponder() ); +} + +void GuiTextEditCtrl::onPreRender() +{ + if ( isFirstResponder() ) + { + U32 timeElapsed = Platform::getVirtualMilliseconds() - mTimeLastCursorFlipped; + mNumFramesElapsed++; + if ( ( timeElapsed > 500 ) && ( mNumFramesElapsed > 3 ) ) + { + mCursorOn = !mCursorOn; + mTimeLastCursorFlipped = Platform::getVirtualMilliseconds(); + mNumFramesElapsed = 0; + setUpdate(); + } + + //update the cursor if the text is scrolling + if ( mDragHit ) + { + if ( ( mScrollDir < 0 ) && ( mCursorPos > 0 ) ) + mCursorPos--; + else if ( ( mScrollDir > 0 ) && ( mCursorPos < (S32) mTextBuffer.length() ) ) + mCursorPos++; + } + } +} + +void GuiTextEditCtrl::drawText( const RectI &drawRect, bool isFocused ) +{ + StringBuffer textBuffer; + Point2I drawPoint = drawRect.point; + Point2I paddingLeftTop, paddingRightBottom; + + // Or else just copy it over. + char *renderText = Con::getReturnBuffer( GuiTextEditCtrl::MAX_STRING_LENGTH ); + getRenderText( renderText ); + + // Apply password masking (make the masking char optional perhaps?) + if(mPasswordText) + { + const U32 renderLen = dStrlen( renderText ); + for( U32 i = 0; i < renderLen; i++ ) + textBuffer.append(mPasswordMask); + } + else + { + textBuffer.set( renderText ); + } + + // Just a little sanity. + if(mCursorPos > textBuffer.length()) + mCursorPos = textBuffer.length(); + + paddingLeftTop.set(( mProfile->mTextOffset.x != 0 ? mProfile->mTextOffset.x : 3 ), mProfile->mTextOffset.y); + paddingRightBottom = paddingLeftTop; + + // Center vertically: + drawPoint.y += ( ( drawRect.extent.y - paddingLeftTop.y - paddingRightBottom.y - S32( mProfile->mFont->getHeight() ) ) / 2 ) + paddingLeftTop.y; + + // Align horizontally: + + S32 textWidth = mProfile->mFont->getStrNWidth(textBuffer.getPtr(), textBuffer.length()); + + switch( mProfile->mAlignment ) + { + case GuiControlProfile::RightJustify: + drawPoint.x += ( drawRect.extent.x - textWidth - paddingRightBottom.x ); + break; + case GuiControlProfile::CenterJustify: + drawPoint.x += ( ( drawRect.extent.x - textWidth ) / 2 ); + break; + default: + case GuiControlProfile::LeftJustify : + drawPoint.x += paddingLeftTop.x; + break; + } + + ColorI fontColor = mActive ? mProfile->mFontColor : mProfile->mFontColorNA; + + // now draw the text + Point2I cursorStart, cursorEnd; + mTextOffset.y = drawPoint.y; + mTextOffset.x = drawPoint.x; + + if ( drawRect.extent.x - paddingLeftTop.x > textWidth ) + mTextOffset.x = drawPoint.x; + else + { + // Alignment affects large text + if ( mProfile->mAlignment == GuiControlProfile::RightJustify + || mProfile->mAlignment == GuiControlProfile::CenterJustify ) + { + if ( mTextOffset.x + textWidth < (drawRect.point.x + drawRect.extent.x) - paddingRightBottom.x) + mTextOffset.x = (drawRect.point.x + drawRect.extent.x) - paddingRightBottom.x - textWidth; + } + } + + // calculate the cursor + if( isFocused && mActive ) + { + // Where in the string are we? + S32 cursorOffset=0, charWidth=0; + UTF16 tempChar = textBuffer.getChar(mCursorPos); + + // Alright, we want to terminate things momentarily. + if(mCursorPos > 0) + { + cursorOffset = mProfile->mFont->getStrNWidth(textBuffer.getPtr(), mCursorPos); + } + else + cursorOffset = 0; + + if( tempChar && mProfile->mFont->isValidChar( tempChar ) ) + charWidth = mProfile->mFont->getCharWidth( tempChar ); + else + charWidth = paddingRightBottom.x; + + if( mTextOffset.x + cursorOffset + 1 >= (drawRect.point.x + drawRect.extent.x) ) // +1 is for the cursor width + { + // Cursor somewhere beyond the textcontrol, + // skip forward roughly 25% of the total width (if possible) + S32 skipForward = drawRect.extent.x / 4 * 3; + + if ( cursorOffset + skipForward > textWidth ) + mTextOffset.x = (drawRect.point.x + drawRect.extent.x) - paddingRightBottom.x - textWidth; + else + { + //mTextOffset.x -= skipForward; + S32 mul = (S32)( mFloor( (cursorOffset-drawRect.extent.x) / skipForward ) ); + mTextOffset.x -= skipForward * mul + drawRect.extent.x - 1; // -1 is for the cursor width + } + } + else if( mTextOffset.x + cursorOffset < drawRect.point.x + paddingLeftTop.x ) + { + // Cursor somewhere before the textcontrol + // skip backward roughly 25% of the total width (if possible) + S32 skipBackward = drawRect.extent.x / 4 * 3; + + if ( cursorOffset - skipBackward < 0 ) + mTextOffset.x = drawRect.point.x + paddingLeftTop.x; + else + { + S32 mul = (S32)( mFloor( cursorOffset / skipBackward ) ); + mTextOffset.x += drawRect.point.x - mTextOffset.x - skipBackward * mul; + } + } + cursorStart.x = mTextOffset.x + cursorOffset; + +#ifdef TORQUE_OS_MAC + cursorStart.x += charWidth/2; +#endif + + cursorEnd.x = cursorStart.x; + + S32 cursorHeight = mProfile->mFont->getHeight(); + if ( cursorHeight < drawRect.extent.y ) + { + cursorStart.y = drawPoint.y; + cursorEnd.y = cursorStart.y + cursorHeight; + } + else + { + cursorStart.y = drawRect.point.y; + cursorEnd.y = cursorStart.y + drawRect.extent.y; + } + } + + //draw the text + if ( !isFocused ) + mBlockStart = mBlockEnd = 0; + + //also verify the block start/end + if ((mBlockStart > textBuffer.length() || (mBlockEnd > textBuffer.length()) || (mBlockStart > mBlockEnd))) + mBlockStart = mBlockEnd = 0; + + Point2I tempOffset = mTextOffset; + + //draw the portion before the highlight + if ( mBlockStart > 0 ) + { + GFX->getDrawUtil()->setBitmapModulation( fontColor ); + const UTF16* preString2 = textBuffer.getPtr(); + GFX->getDrawUtil()->drawText( mProfile->mFont, tempOffset, preString2, mProfile->mFontColors ); + tempOffset.x += mProfile->mFont->getStrNWidth(preString2, mBlockStart); + } + + //draw the highlighted portion + if ( mBlockEnd > 0 ) + { + const UTF16* highlightBuff = textBuffer.getPtr() + mBlockStart; + U32 highlightBuffLen = mBlockEnd-mBlockStart; + + S32 highlightWidth = mProfile->mFont->getStrNWidth(highlightBuff, highlightBuffLen); + + GFX->getDrawUtil()->drawRectFill( Point2I( tempOffset.x, drawRect.point.y ), + Point2I( tempOffset.x + highlightWidth, drawRect.point.y + drawRect.extent.y - 1), + mProfile->mFontColorSEL ); + + GFX->getDrawUtil()->setBitmapModulation( mProfile->mFontColorHL ); + GFX->getDrawUtil()->drawTextN( mProfile->mFont, tempOffset, highlightBuff, highlightBuffLen, mProfile->mFontColors ); + tempOffset.x += highlightWidth; + } + + //draw the portion after the highlight + if(mBlockEnd < textBuffer.length()) + { + const UTF16* finalBuff = textBuffer.getPtr() + mBlockEnd; + U32 finalBuffLen = textBuffer.length() - mBlockEnd; + + GFX->getDrawUtil()->setBitmapModulation( fontColor ); + GFX->getDrawUtil()->drawTextN( mProfile->mFont, tempOffset, finalBuff, finalBuffLen, mProfile->mFontColors ); + } + + //draw the cursor + if ( isFocused && mCursorOn ) + GFX->getDrawUtil()->drawLine( cursorStart, cursorEnd, mProfile->mCursorColor ); +} + +bool GuiTextEditCtrl::hasText() +{ + return (mTextBuffer.length()); +} + +void GuiTextEditCtrl::playDeniedSound() +{ + if ( mDeniedSound ) + SFX->playOnce( mDeniedSound ); +} + +const char *GuiTextEditCtrl::getScriptValue() +{ + return StringTable->insert(mTextBuffer.getPtr8()); +} + +void GuiTextEditCtrl::setScriptValue(const char *value) +{ + mTextBuffer.set(value); + mCursorPos = mTextBuffer.length(); +} + +void GuiTextEditCtrl::handleCharInput( U16 ascii ) +{ + S32 stringLen = mTextBuffer.length(); + + // Get the character ready to add to a UTF8 string. + UTF16 convertedChar[2] = { ascii, 0 }; + + //see if it's a number field + if ( mProfile->mNumbersOnly ) + { + if ( ascii == '-') + { + //a minus sign only exists at the beginning, and only a single minus sign + if ( mCursorPos != 0 && !isAllTextSelected() ) + { + playDeniedSound(); + return; + } + + if ( mInsertOn && ( mTextBuffer.getChar(0) == '-' ) ) + { + playDeniedSound(); + return; + } + } + // BJTODO: This is probably not unicode safe. + else if ( ascii != '.' && (ascii < '0' || ascii > '9') ) + { + playDeniedSound(); + return; + } + } + + //save the current state + saveUndoState(); + + bool alreadyCut = false; + + //delete anything highlighted + if ( mBlockEnd > 0 ) + { + mTextBuffer.cut(mBlockStart, mBlockEnd-mBlockStart); + mCursorPos = mBlockStart; + mBlockStart = 0; + mBlockEnd = 0; + + // We just changed the string length! + // Get its new value. + stringLen = mTextBuffer.length(); + + // If we already had text highlighted, we just want to cut that text. + // Don't cut the next character even if insert is not on. + alreadyCut = true; + } + + if ( ( mInsertOn && ( stringLen < mMaxStrLen ) ) || + ( !mInsertOn && ( mCursorPos < mMaxStrLen ) ) ) + { + if ( mCursorPos == stringLen ) + { + mTextBuffer.append(convertedChar); + mCursorPos++; + } + else + { + if ( mInsertOn || alreadyCut ) + { + mTextBuffer.insert(mCursorPos, convertedChar); + mCursorPos++; + } + else + { + mTextBuffer.cut(mCursorPos, 1); + mTextBuffer.insert(mCursorPos, convertedChar); + mCursorPos++; + } + } + } + else + playDeniedSound(); + + //reset the history index + mHistoryDirty = true; + + //execute the console command if it exists + execConsoleCallback(); +} + +S32 GuiTextEditCtrl::findPrevWord() +{ + // First the first word to the left of the current cursor position + // and return the positional index of its starting character. + + // We define the first character of a word as any non-whitespace + // character which has a non-alpha-numeric character to its immediate left. + + const UTF8* text = mTextBuffer.getPtr8(); + + for ( S32 i = mCursorPos - 1; i > 0; i-- ) + { + if ( !dIsspace( text[i] ) ) + { + if ( !dIsalnum( text[i-1] ) ) + { + return i; + } + } + } + + return 0; +} + +S32 GuiTextEditCtrl::findNextWord() +{ + // First the first word to the right of the current cursor position + // and return the positional index of its starting character. + + // We define the first character of a word as any non-whitespace + // character which has a non-alpha-numeric character to its immediate left. + + const UTF8* text = mTextBuffer.getPtr8(); + + for ( S32 i = mCursorPos + 1; i < mTextBuffer.length(); i++ ) + { + if ( !dIsspace( text[i] ) ) + { + if ( !dIsalnum( text[i-1] ) ) + { + return i; + } + } + } + + return mTextBuffer.length(); +} + +DefineEngineMethod( GuiTextEditCtrl, getText, const char*, (),, + "@brief Acquires the current text displayed in this control.\n\n" + "@tsexample\n" + "// Acquire the value of the text control.\n" + "%text = %thisGuiTextEditCtrl.getText();\n" + "@endtsexample\n\n" + "@return The current text within the control.\n\n" + "@see GuiControl") +{ + if( !object->hasText() ) + return StringTable->insert(""); + + char *retBuffer = Con::getReturnBuffer( GuiTextEditCtrl::MAX_STRING_LENGTH ); + object->getText( retBuffer ); + + return retBuffer; +} + +DefineEngineMethod( GuiTextEditCtrl, setText, void, (const char* text),, + "@brief Sets the text in the control.\n\n" + "@param text Text to place in the control.\n" + "@tsexample\n" + "// Define the text to display\n" + "%text = \"Text!\"\n\n" + "// Inform the GuiTextEditCtrl to display the defined text\n" + "%thisGuiTextEditCtrl.setText(%text);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->setText( text ); +} + +DefineEngineMethod( GuiTextEditCtrl, getCursorPos, S32, (),, + "@brief Returns the current position of the text cursor in the control.\n\n" + "@tsexample\n" + "// Acquire the cursor position in the control\n" + "%position = %thisGuiTextEditCtrl.getCursorPost();\n" + "@endtsexample\n\n" + "@return Text cursor position within the control.\n\n" + "@see GuiControl") +{ + return( object->getCursorPos() ); +} + +DefineEngineMethod( GuiTextEditCtrl, setCursorPos, void, (int position),, + "@brief Sets the text cursor at the defined position within the control.\n\n" + "@param position Text position to set the text cursor.\n" + "@tsexample\n" + "// Define the cursor position\n" + "%position = \"12\";\n\n" + "// Inform the GuiTextEditCtrl control to place the text cursor at the defined position\n" + "%thisGuiTextEditCtrl.setCursorPos(%position);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->setCursorPos( position ); +} + +DefineEngineMethod( GuiTextEditCtrl, isAllTextSelected, bool, (),, + "@brief Checks to see if all text in the control has been selected.\n\n" + "@tsexample\n" + "// Check to see if all text has been selected or not.\n" + "%allSelected = %thisGuiTextEditCtrl.isAllTextSelected();\n" + "@endtsexample\n\n" + "@return True if all text in the control is selected, otherwise false.\n\n" + "@see GuiControl") +{ + return object->isAllTextSelected(); +} + +DefineEngineMethod( GuiTextEditCtrl, selectAllText, void, (),, + "@brief Selects all text within the control.\n\n" + "@tsexample\n" + "// Inform the control to select all of its text.\n" + "%thisGuiTextEditCtrl.selectAllText();\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->selectAllText(); +} + +DefineEngineMethod( GuiTextEditCtrl, clearSelectedText, void, (),, + "@brief Unselects all selected text in the control.\n\n" + "@tsexample\n" + "// Inform the control to unselect all of its selected text\n" + "%thisGuiTextEditCtrl.clearSelectedText();\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->clearSelectedText(); +} + +DefineEngineMethod( GuiTextEditCtrl, forceValidateText, void, (),, + "@brief Force a validation to occur.\n\n" + "@tsexample\n" + "// Inform the control to force a validation of its text.\n" + "%thisGuiTextEditCtrl.forceValidateText();\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->forceValidateText(); +} diff --git a/Engine/source/gui/controls/guiTextEditCtrl.h b/Engine/source/gui/controls/guiTextEditCtrl.h new file mode 100644 index 000000000..1821ef182 --- /dev/null +++ b/Engine/source/gui/controls/guiTextEditCtrl.h @@ -0,0 +1,156 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUITEXTEDITCTRL_H_ +#define _GUITEXTEDITCTRL_H_ + +#ifndef _GUITYPES_H_ +#include "gui/core/guiTypes.h" +#endif +#ifndef _GUITEXTCTRL_H_ +#include "gui/controls/guiTextCtrl.h" +#endif +#ifndef _STRINGBUFFER_H_ +#include "core/stringBuffer.h" +#endif + +class SFXTrack; + +class GuiTextEditCtrl : public GuiTextCtrl +{ +private: + typedef GuiTextCtrl Parent; + +protected: + + DECLARE_CALLBACK( void, onTabComplete, (const char* val)); + DECLARE_CALLBACK( void, onReturn, ()); + DECLARE_CALLBACK( void, onValidate, ()); + + StringBuffer mTextBuffer; + + String mValidateCommand; + String mEscapeCommand; + SFXTrack* mDeniedSound; + + // for animating the cursor + S32 mNumFramesElapsed; + U32 mTimeLastCursorFlipped; + ColorI mCursorColor; + bool mCursorOn; + + bool mInsertOn; + S32 mMouseDragStart; + Point2I mTextOffset; + bool mTextOffsetReset; + bool mDragHit; + bool mTabComplete; + S32 mScrollDir; + + //undo members + StringBuffer mUndoText; + S32 mUndoBlockStart; + S32 mUndoBlockEnd; + S32 mUndoCursorPos; + void saveUndoState(); + + S32 mBlockStart; + S32 mBlockEnd; + S32 mCursorPos; + virtual S32 calculateCursorPos( const Point2I &globalPos ); + + bool mHistoryDirty; + S32 mHistoryLast; + S32 mHistoryIndex; + S32 mHistorySize; + bool mPasswordText; + StringTableEntry mPasswordMask; + + /// If set, any non-ESC key is handled here or not at all + bool mSinkAllKeyEvents; + UTF16 **mHistoryBuf; + void updateHistory(StringBuffer *txt, bool moveIndex); + + void playDeniedSound(); + void execConsoleCallback(); + + virtual void handleCharInput( U16 ascii ); + + S32 findNextWord(); + S32 findPrevWord(); + +public: + GuiTextEditCtrl(); + ~GuiTextEditCtrl(); + DECLARE_CONOBJECT(GuiTextEditCtrl); + DECLARE_DESCRIPTION( "A control that allows to edit a single line of text. "); + static void initPersistFields(); + + bool onAdd(); + + /// Get the contents of the control. + /// + /// dest should be of size GuiTextCtrl::MAX_STRING_LENGTH+1. + void getText(char *dest); + virtual void getRenderText(char *dest); + + void setText(S32 tag); + virtual void setText(const UTF8* txt); + virtual void setText(const UTF16* txt); + S32 getCursorPos() { return( mCursorPos ); } + void setCursorPos( const S32 newPos ); + + bool isAllTextSelected(); + void selectAllText(); + void clearSelectedText(); + + void forceValidateText(); + const char *getScriptValue(); + void setScriptValue(const char *value); + + bool getSinkAllKeys() { return mSinkAllKeyEvents; } + void setSinkAllKeys(bool state) { mSinkAllKeyEvents = state; } + + virtual bool onKeyDown(const GuiEvent &event); + virtual void onMouseDown(const GuiEvent &event); + virtual void onMouseDragged(const GuiEvent &event); + virtual void onMouseUp(const GuiEvent &event); + + void onCopy(bool andCut); + void onPaste(); + void onUndo(); + + virtual void setFirstResponder(); + virtual void onLoseFirstResponder(); + + bool hasText(); + + void onStaticModified(const char* slotName, const char* newValue = NULL); + + void onPreRender(); + void onRender(Point2I offset, const RectI &updateRect); + virtual void drawText( const RectI &drawRect, bool isFocused ); + + bool dealWithEnter( bool clearResponder ); +}; + +#endif //_GUI_TEXTEDIT_CTRL_H diff --git a/Engine/source/gui/controls/guiTextEditSliderBitmapCtrl.cpp b/Engine/source/gui/controls/guiTextEditSliderBitmapCtrl.cpp new file mode 100644 index 000000000..37ed5fe54 --- /dev/null +++ b/Engine/source/gui/controls/guiTextEditSliderBitmapCtrl.cpp @@ -0,0 +1,447 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/controls/guiTextEditSliderBitmapCtrl.h" + +#include "console/consoleTypes.h" +#include "console/console.h" +#include "gui/core/guiCanvas.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxDrawUtil.h" + + +IMPLEMENT_CONOBJECT(GuiTextEditSliderBitmapCtrl); + +ConsoleDocClass( GuiTextEditSliderBitmapCtrl, + "@brief GUI Control which displays a numerical value which can be increased " + "or decreased using a pair of bitmap up/down buttons. \n\n" + + "This control uses the bitmap specified in it's profile " + "(GuiControlProfile::bitmapName). It takes this image and breaks up aspects " + "of it to render the up and down arrows. It is also important to set " + "GuiControlProfile::hasBitmapArray to true on the profile as well.\n\n" + + "The bitmap referenced should be broken up into a 1 x 4 grid (using the top " + "left color pixel as a border color between each of the images) in which it " + "will map to the following places:\n" + "
        \n" + "
      1. Up arrow active
      2. \n" + "
      3. Up arrow inactive
      4. \n" + "
      5. Down arrow active
      6. \n" + "
      7. Down arrow inactive
      8. \n" + "
      \n\n" + + "
      \n"
      +   "1\n"
      +   "2\n"
      +   "3\n"
      +   "4
      \n\n" + + "@tsexample\n" + "singleton GuiControlProfile (SliderBitmapGUIProfile)\n" + "{\n" + " bitmap = \"core/art/gui/images/sliderArray\";\n" + " hasBitmapArray = true;\n" + " opaque = false;\n" + "};\n\n" + + "new GuiTextEditSliderBitmapCtrl()\n" + "{\n" + " profile = \"SliderBitmapGUIProfile\";\n" + " format = \"%3.2f\";\n" + " range = \"-1e+03 1e+03\";\n" + " increment = \"0.1\";\n" + " focusOnMouseWheel = \"0\";\n" + " bitmap = \"\";\n" + " //Properties not specific to this control have been omitted from this example.\n" + "};\n" + "@endtsexample\n\n" + + "@see GuiTextEditSliderCtrl\n\n" + "@see GuiTextEditCtrl\n\n" + + "@ingroup GuiCore\n" +); + + +GuiTextEditSliderBitmapCtrl::GuiTextEditSliderBitmapCtrl() +{ + mRange.set(0.0f, 1.0f); + mIncAmount = 1.0f; + mValue = 0.0f; + mMulInc = 0; + mIncCounter = 0.0f; + mFormat = StringTable->insert("%3.2f"); + mTextAreaHit = None; + mFocusOnMouseWheel = false; + mBitmapName = StringTable->insert( "" ); +} + +GuiTextEditSliderBitmapCtrl::~GuiTextEditSliderBitmapCtrl() +{ +} + +void GuiTextEditSliderBitmapCtrl::initPersistFields() +{ + addField("format", TypeString, Offset(mFormat, GuiTextEditSliderBitmapCtrl), "Character format type to place in the control.\n"); + addField("range", TypePoint2F, Offset(mRange, GuiTextEditSliderBitmapCtrl), "Maximum vertical and horizontal range to allow in the control.\n"); + addField("increment", TypeF32, Offset(mIncAmount, GuiTextEditSliderBitmapCtrl), "How far to increment the slider on each step.\n"); + addField("focusOnMouseWheel", TypeBool, Offset(mFocusOnMouseWheel, GuiTextEditSliderBitmapCtrl), "If true, the control will accept giving focus to the user when the mouse wheel is used.\n"); + addField("bitmap", TypeFilename,Offset(mBitmapName, GuiTextEditSliderBitmapCtrl), "Unused" ); + + Parent::initPersistFields(); +} + +void GuiTextEditSliderBitmapCtrl::getText(char *dest) +{ + Parent::getText(dest); +} + +void GuiTextEditSliderBitmapCtrl::setText(const char *txt) +{ + mValue = dAtof(txt); + checkRange(); + setValue(); +} + +bool GuiTextEditSliderBitmapCtrl::onKeyDown(const GuiEvent &event) +{ + return Parent::onKeyDown(event); +} + +void GuiTextEditSliderBitmapCtrl::checkRange() +{ + if(mValue < mRange.x) + mValue = mRange.x; + else if(mValue > mRange.y) + mValue = mRange.y; +} + +void GuiTextEditSliderBitmapCtrl::setValue() +{ + char buf[20]; + // For some reason this sprintf is failing to convert + // a floating point number to anything with %d, so cast it. + if( dStricmp( mFormat, "%d" ) == 0 ) + dSprintf(buf,sizeof(buf),mFormat, (S32)mValue); + else + dSprintf(buf,sizeof(buf),mFormat, mValue); + Parent::setText(buf); +} + +void GuiTextEditSliderBitmapCtrl::onMouseDown(const GuiEvent &event) +{ + // If we're not active then skip out. + if ( !mActive || !mAwake || !mVisible ) + { + Parent::onMouseDown(event); + return; + } + + char txt[20]; + Parent::getText(txt); + mValue = dAtof(txt); + + mMouseDownTime = Sim::getCurrentTime(); + GuiControl *parent = getParent(); + if(!parent) + return; + Point2I camPos = event.mousePoint; + Point2I point = parent->localToGlobalCoord(getPosition()); + + if(camPos.x > point.x + getExtent().x - 14) + { + if(camPos.y > point.y + (getExtent().y/2)) + { + mValue -=mIncAmount; + mTextAreaHit = ArrowDown; + mMulInc = -0.15f; + } + else + { + mValue +=mIncAmount; + mTextAreaHit = ArrowUp; + mMulInc = 0.15f; + } + + checkRange(); + setValue(); + mouseLock(); + + // We should get the focus and set the + // cursor to the start of the text to + // mimic the standard Windows behavior. + setFirstResponder(); + mCursorPos = mBlockStart = mBlockEnd = 0; + setUpdate(); + + return; + } + + Parent::onMouseDown(event); +} + +void GuiTextEditSliderBitmapCtrl::onMouseDragged(const GuiEvent &event) +{ + // If we're not active then skip out. + if ( !mActive || !mAwake || !mVisible ) + { + Parent::onMouseDragged(event); + return; + } + + if(mTextAreaHit == None || mTextAreaHit == Slider) + { + mTextAreaHit = Slider; + GuiControl *parent = getParent(); + if(!parent) + return; + Point2I camPos = event.mousePoint; + Point2I point = parent->localToGlobalCoord(getPosition()); + F32 maxDis = 100; + F32 val; + if(camPos.y < point.y) + { + if((F32)point.y < maxDis) + maxDis = (F32)point.y; + + val = point.y - maxDis; + + if(point.y > 0) + mMulInc= 1.0f-(((float)camPos.y - val) / maxDis); + else + mMulInc = 1.0f; + + checkIncValue(); + + return; + } + else if(camPos.y > point.y + getExtent().y) + { + GuiCanvas *root = getRoot(); + val = (F32)(root->getHeight() - (point.y + getHeight())); + if(val < maxDis) + maxDis = val; + if( val > 0) + mMulInc= -(F32)(camPos.y - (point.y + getHeight()))/maxDis; + else + mMulInc = -1.0f; + checkIncValue(); + return; + } + mTextAreaHit = None; + Parent::onMouseDragged(event); + } +} + +void GuiTextEditSliderBitmapCtrl::onMouseUp(const GuiEvent &event) +{ + // If we're not active then skip out. + if ( !mActive || !mAwake || !mVisible ) + { + Parent::onMouseUp(event); + return; + } + + mMulInc = 0.0f; + mouseUnlock(); + + if ( mTextAreaHit != None ) + + //if we released the mouse within this control, then the parent will call + //the mConsoleCommand other wise we have to call it. + Parent::onMouseUp(event); + + //if we didn't release the mouse within this control, then perform the action + // if (!cursorInControl()) + execConsoleCallback(); + + execAltConsoleCallback(); + + mTextAreaHit = None; +} + +bool GuiTextEditSliderBitmapCtrl::onMouseWheelUp(const GuiEvent &event) +{ + if ( !mActive || !mAwake || !mVisible ) + return Parent::onMouseWheelUp(event); + + if ( !isFirstResponder() && !mFocusOnMouseWheel ) + return false; + + mValue += mIncAmount; + + checkRange(); + setValue(); + + setFirstResponder(); + mCursorPos = mBlockStart = mBlockEnd = 0; + setUpdate(); + + return true; +} + +bool GuiTextEditSliderBitmapCtrl::onMouseWheelDown(const GuiEvent &event) +{ + if ( !mActive || !mAwake || !mVisible ) + return Parent::onMouseWheelDown(event); + + if ( !isFirstResponder() && !mFocusOnMouseWheel ) + return false; + + mValue -= mIncAmount; + + checkRange(); + setValue(); + + setFirstResponder(); + mCursorPos = mBlockStart = mBlockEnd = 0; + setUpdate(); + + return true; +} + +void GuiTextEditSliderBitmapCtrl::checkIncValue() +{ + if(mMulInc > 1.0f) + mMulInc = 1.0f; + else if(mMulInc < -1.0f) + mMulInc = -1.0f; +} + +void GuiTextEditSliderBitmapCtrl::timeInc(U32 elapseTime) +{ + S32 numTimes = elapseTime / 750; + if(mTextAreaHit != Slider && numTimes > 0) + { + if(mTextAreaHit == ArrowUp) + mMulInc = 0.15f * numTimes; + else + mMulInc = -0.15f * numTimes; + + checkIncValue(); + } +} +bool GuiTextEditSliderBitmapCtrl::onWake() +{ + if(!Parent::onWake()) + return false; + + mNumberOfBitmaps = mProfile->constructBitmapArray(); + + return true; +} + +void GuiTextEditSliderBitmapCtrl::onPreRender() +{ + if (isFirstResponder()) + { + U32 timeElapsed = Platform::getVirtualMilliseconds() - mTimeLastCursorFlipped; + mNumFramesElapsed++; + if ((timeElapsed > 500) && (mNumFramesElapsed > 3)) + { + mCursorOn = !mCursorOn; + mTimeLastCursorFlipped = Sim::getCurrentTime(); + mNumFramesElapsed = 0; + setUpdate(); + } + + //update the cursor if the text is scrolling + if (mDragHit) + { + if ((mScrollDir < 0) && (mCursorPos > 0)) + { + mCursorPos--; + } + else if ((mScrollDir > 0) && (mCursorPos < (S32)dStrlen(mText))) + { + mCursorPos++; + } + } + } +} + +void GuiTextEditSliderBitmapCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + if(mTextAreaHit != None) + { + U32 elapseTime = Sim::getCurrentTime() - mMouseDownTime; + if(elapseTime > 750 || mTextAreaHit == Slider) + { + timeInc(elapseTime); + mIncCounter += mMulInc; + if(mIncCounter >= 1.0f || mIncCounter <= -1.0f) + { + mValue = (mMulInc > 0.0f) ? mValue+mIncAmount : mValue-mIncAmount; + mIncCounter = (mIncCounter > 0.0f) ? mIncCounter-1 : mIncCounter+1; + checkRange(); + setValue(); + mCursorPos = 0; + } + } + } + + Parent::onRender(offset, updateRect); + + // Arrow placement coordinates + Point2I arrowUpStart(offset.x + getWidth() - 14, offset.y + 1 ); + Point2I arrowUpEnd(13, getExtent().y/2); + + Point2I arrowDownStart(offset.x + getWidth() - 14, offset.y + 1 + getExtent().y/2); + Point2I arrowDownEnd(13, getExtent().y/2); + + // Draw the line that splits the number and bitmaps + GFX->getDrawUtil()->drawLine(Point2I(offset.x + getWidth() - 14 -2, offset.y + 1 ), + Point2I(arrowUpStart.x -2, arrowUpStart.y + getExtent().y), + mProfile->mBorderColor); + + GFX->getDrawUtil()->clearBitmapModulation(); + + if(mNumberOfBitmaps == 0) + Con::warnf("No image provided for GuiTextEditSliderBitmapCtrl; do not render"); + else + { + // This control needs 4 images in order to render correctly + if(mTextAreaHit == ArrowUp) + GFX->getDrawUtil()->drawBitmapStretchSR( mProfile->mTextureObject, RectI(arrowUpStart,arrowUpEnd), mProfile->mBitmapArrayRects[0] ); + else + GFX->getDrawUtil()->drawBitmapStretchSR( mProfile->mTextureObject, RectI(arrowUpStart,arrowUpEnd), mProfile->mBitmapArrayRects[1] ); + + if(mTextAreaHit == ArrowDown) + GFX->getDrawUtil()->drawBitmapStretchSR( mProfile->mTextureObject, RectI(arrowDownStart,arrowDownEnd), mProfile->mBitmapArrayRects[2] ); + else + GFX->getDrawUtil()->drawBitmapStretchSR( mProfile->mTextureObject, RectI(arrowDownStart,arrowDownEnd), mProfile->mBitmapArrayRects[3] ); + } +} + +void GuiTextEditSliderBitmapCtrl::setBitmap(const char *name) +{ + bool awake = mAwake; + if(awake) + onSleep(); + + mBitmapName = StringTable->insert(name); + if(awake) + onWake(); + setUpdate(); +} \ No newline at end of file diff --git a/Engine/source/gui/controls/guiTextEditSliderBitmapCtrl.h b/Engine/source/gui/controls/guiTextEditSliderBitmapCtrl.h new file mode 100644 index 000000000..ebe35530a --- /dev/null +++ b/Engine/source/gui/controls/guiTextEditSliderBitmapCtrl.h @@ -0,0 +1,95 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUITEXTEDITSLIDERBITMAPCTRL_H_ +#define _GUITEXTEDITSLIDERBITMAPCTRL_H_ + +#ifndef _GUITYPES_H_ +#include "gui/core/guiTypes.h" +#endif +#ifndef _GUITEXTEDITCTRL_H_ +#include "gui/controls/guiTextEditCtrl.h" +#endif + +class GuiTextEditSliderBitmapCtrl : public GuiTextEditCtrl +{ + typedef GuiTextEditCtrl Parent; + +public: + + enum CtrlArea + { + None, + Slider, + ArrowUp, + ArrowDown + }; + + GuiTextEditSliderBitmapCtrl(); + ~GuiTextEditSliderBitmapCtrl(); + + DECLARE_CONOBJECT(GuiTextEditSliderBitmapCtrl); + DECLARE_CATEGORY( "Gui Values" ); + DECLARE_DESCRIPTION( "A text control that display a numeric value and bitmapped up/down sliders." ); + + static void initPersistFields(); + + virtual void getText(char *dest); // dest must be of size + // StructDes::MAX_STRING_LEN + 1 + + virtual void setText(const char *txt); + + void setValue(); + void checkRange(); + void checkIncValue(); + void timeInc(U32 elapseTime); + + virtual bool onKeyDown(const GuiEvent &event); + virtual void onMouseDown(const GuiEvent &event); + virtual void onMouseDragged(const GuiEvent &event); + virtual void onMouseUp(const GuiEvent &event); + virtual bool onMouseWheelUp(const GuiEvent &event); + virtual bool onMouseWheelDown(const GuiEvent &event); + + bool onWake(); + virtual void onPreRender(); + virtual void onRender(Point2I offset, const RectI &updateRect); + + void setBitmap(const char *name); + +protected: + + Point2F mRange; + F32 mIncAmount; + F32 mValue; + F32 mIncCounter; + F32 mMulInc; + StringTableEntry mFormat; + U32 mMouseDownTime; + bool mFocusOnMouseWheel; + S32 mNumberOfBitmaps; + StringTableEntry mBitmapName; + + CtrlArea mTextAreaHit; +}; + +#endif //_GUITEXTEDITSLIDERBITMAPCTRL_H_ diff --git a/Engine/source/gui/controls/guiTextEditSliderCtrl.cpp b/Engine/source/gui/controls/guiTextEditSliderCtrl.cpp new file mode 100644 index 000000000..32360e87b --- /dev/null +++ b/Engine/source/gui/controls/guiTextEditSliderCtrl.cpp @@ -0,0 +1,429 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/controls/guiTextEditSliderCtrl.h" + +#include "console/consoleTypes.h" +#include "console/console.h" +#include "gui/core/guiCanvas.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxDrawUtil.h" +#include "console/engineAPI.h" + +IMPLEMENT_CONOBJECT(GuiTextEditSliderCtrl); + +ConsoleDocClass( GuiTextEditSliderCtrl, + "@brief GUI Control which displays a numerical value which can be increased or " + "decreased using a pair of arrows.\n\n" + + "@tsexample\n" + "new GuiTextEditSliderCtrl()\n" + "{\n" + " format = \"%3.2f\";\n" + " range = \"-1e+03 1e+03\";\n" + " increment = \"0.1\";\n" + " focusOnMouseWheel = \"0\";\n" + " //Properties not specific to this control have been omitted from this example.\n" + "};\n" + "@endtsexample\n\n" + + "@see GuiTextEditCtrl\n\n" + + "@ingroup GuiCore\n" +); + +GuiTextEditSliderCtrl::GuiTextEditSliderCtrl() +{ + mRange.set(0.0f, 1.0f); + mIncAmount = 1.0f; + mValue = 0.0f; + mMulInc = 0; + mIncCounter = 0.0f; + mFormat = StringTable->insert("%3.2f"); + mTextAreaHit = None; + mFocusOnMouseWheel = false; +} + +GuiTextEditSliderCtrl::~GuiTextEditSliderCtrl() +{ +} + +void GuiTextEditSliderCtrl::initPersistFields() +{ + addField("format", TypeString, Offset(mFormat, GuiTextEditSliderCtrl), "Character format type to place in the control.\n"); + addField("range", TypePoint2F, Offset(mRange, GuiTextEditSliderCtrl), "Maximum vertical and horizontal range to allow in the control.\n"); + addField("increment", TypeF32, Offset(mIncAmount, GuiTextEditSliderCtrl), "How far to increment the slider on each step.\n"); + addField("focusOnMouseWheel", TypeBool, Offset(mFocusOnMouseWheel, GuiTextEditSliderCtrl), "If true, the control will accept giving focus to the user when the mouse wheel is used.\n"); + + Parent::initPersistFields(); +} + +void GuiTextEditSliderCtrl::getText(char *dest) +{ + Parent::getText(dest); +} + +void GuiTextEditSliderCtrl::setText(const char *txt) +{ + mValue = dAtof(txt); + checkRange(); + setValue(); +} + +bool GuiTextEditSliderCtrl::onKeyDown(const GuiEvent &event) +{ + return Parent::onKeyDown(event); +} + +void GuiTextEditSliderCtrl::checkRange() +{ + if(mValue < mRange.x) + mValue = mRange.x; + else if(mValue > mRange.y) + mValue = mRange.y; +} + +void GuiTextEditSliderCtrl::setValue() +{ + char buf[20]; + // For some reason this sprintf is failing to convert + // a floating point number to anything with %d, so cast it. + if( dStricmp( mFormat, "%d" ) == 0 ) + dSprintf(buf,sizeof(buf),mFormat, (S32)mValue); + else + dSprintf(buf,sizeof(buf),mFormat, mValue); + Parent::setText(buf); +} + +void GuiTextEditSliderCtrl::onMouseDown(const GuiEvent &event) +{ + // If we're not active then skip out. + if ( !mActive || !mAwake || !mVisible ) + { + Parent::onMouseDown(event); + return; + } + + char txt[20]; + Parent::getText(txt); + mValue = dAtof(txt); + + mMouseDownTime = Sim::getCurrentTime(); + GuiControl *parent = getParent(); + if(!parent) + return; + Point2I camPos = event.mousePoint; + Point2I point = parent->localToGlobalCoord(getPosition()); + + if(camPos.x > point.x + getExtent().x - 14) + { + if(camPos.y > point.y + (getExtent().y/2)) + { + mValue -=mIncAmount; + mTextAreaHit = ArrowDown; + mMulInc = -0.15f; + } + else + { + mValue +=mIncAmount; + mTextAreaHit = ArrowUp; + mMulInc = 0.15f; + } + + checkRange(); + setValue(); + mouseLock(); + + // We should get the focus and set the + // cursor to the start of the text to + // mimic the standard Windows behavior. + setFirstResponder(); + mCursorPos = mBlockStart = mBlockEnd = 0; + setUpdate(); + + return; + } + + Parent::onMouseDown(event); +} + +void GuiTextEditSliderCtrl::onMouseDragged(const GuiEvent &event) +{ + // If we're not active then skip out. + if ( !mActive || !mAwake || !mVisible ) + { + Parent::onMouseDragged(event); + return; + } + + if(mTextAreaHit == None || mTextAreaHit == Slider) + { + mTextAreaHit = Slider; + GuiControl *parent = getParent(); + if(!parent) + return; + Point2I camPos = event.mousePoint; + Point2I point = parent->localToGlobalCoord(getPosition()); + F32 maxDis = 100; + F32 val; + if(camPos.y < point.y) + { + if((F32)point.y < maxDis) + maxDis = (F32)point.y; + + val = point.y - maxDis; + + if(point.y > 0) + mMulInc= 1.0f-(((float)camPos.y - val) / maxDis); + else + mMulInc = 1.0f; + + checkIncValue(); + + return; + } + else if(camPos.y > point.y + getExtent().y) + { + GuiCanvas *root = getRoot(); + val = (F32)(root->getHeight() - (point.y + getHeight())); + if(val < maxDis) + maxDis = val; + if( val > 0) + mMulInc= -(F32)(camPos.y - (point.y + getHeight()))/maxDis; + else + mMulInc = -1.0f; + checkIncValue(); + return; + } + mTextAreaHit = None; + Parent::onMouseDragged(event); + } +} + +void GuiTextEditSliderCtrl::onMouseUp(const GuiEvent &event) +{ + // If we're not active then skip out. + if ( !mActive || !mAwake || !mVisible ) + { + Parent::onMouseUp(event); + return; + } + + mMulInc = 0.0f; + mouseUnlock(); + + if ( mTextAreaHit != None ) + selectAllText(); + + //if we released the mouse within this control, then the parent will call + //the mConsoleCommand other wise we have to call it. + Parent::onMouseUp(event); + + //if we didn't release the mouse within this control, then perform the action + // if (!cursorInControl()) + execConsoleCallback(); + execAltConsoleCallback(); + + //Set the cursor position to where the user clicked + mCursorPos = calculateCursorPos( event.mousePoint ); + + mTextAreaHit = None; +} + +bool GuiTextEditSliderCtrl::onMouseWheelUp(const GuiEvent &event) +{ + if ( !mActive || !mAwake || !mVisible ) + return Parent::onMouseWheelUp(event); + + if ( !isFirstResponder() && !mFocusOnMouseWheel ) + { + GuiControl *parent = getParent(); + if ( parent ) + return parent->onMouseWheelUp( event ); + + return false; + } + + mValue += mIncAmount; + + checkRange(); + setValue(); + + setFirstResponder(); + mCursorPos = mBlockStart = mBlockEnd = 0; + setUpdate(); + + return true; +} + +bool GuiTextEditSliderCtrl::onMouseWheelDown(const GuiEvent &event) +{ + if ( !mActive || !mAwake || !mVisible ) + return Parent::onMouseWheelDown(event); + + if ( !isFirstResponder() && !mFocusOnMouseWheel ) + { + GuiControl *parent = getParent(); + if ( parent ) + return parent->onMouseWheelUp( event ); + + return false; + } + + mValue -= mIncAmount; + + checkRange(); + setValue(); + + setFirstResponder(); + mCursorPos = mBlockStart = mBlockEnd = 0; + setUpdate(); + + return true; +} + +void GuiTextEditSliderCtrl::checkIncValue() +{ + if(mMulInc > 1.0f) + mMulInc = 1.0f; + else if(mMulInc < -1.0f) + mMulInc = -1.0f; +} + +void GuiTextEditSliderCtrl::timeInc(U32 elapseTime) +{ + S32 numTimes = elapseTime / 750; + if(mTextAreaHit != Slider && numTimes > 0) + { + if(mTextAreaHit == ArrowUp) + mMulInc = 0.15f * numTimes; + else + mMulInc = -0.15f * numTimes; + + checkIncValue(); + } +} + +void GuiTextEditSliderCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + if(mTextAreaHit != None) + { + U32 elapseTime = Sim::getCurrentTime() - mMouseDownTime; + if(elapseTime > 750 || mTextAreaHit == Slider) + { + timeInc(elapseTime); + mIncCounter += mMulInc; + if(mIncCounter >= 1.0f || mIncCounter <= -1.0f) + { + mValue = (mMulInc > 0.0f) ? mValue+mIncAmount : mValue-mIncAmount; + mIncCounter = (mIncCounter > 0.0f) ? mIncCounter-1 : mIncCounter+1; + checkRange(); + setValue(); + mCursorPos = 0; + } + } + } + Parent::onRender(offset, updateRect); + + Point2I start(offset.x + getWidth() - 14, offset.y); + Point2I midPoint(start.x + 7, start.y + (getExtent().y/2)); + + GFX->getDrawUtil()->drawRectFill(Point2I(start.x+1,start.y+1), Point2I(start.x+13,start.y+getExtent().y-1) , mProfile->mFillColor); + + GFX->getDrawUtil()->drawLine(start, Point2I(start.x, start.y+getExtent().y),mProfile->mFontColor); + GFX->getDrawUtil()->drawLine(Point2I(start.x,midPoint.y), + Point2I(start.x+14,midPoint.y), + mProfile->mFontColor); + + GFXVertexBufferHandle verts(GFX, 6, GFXBufferTypeVolatile); + verts.lock(); + + verts[0].color.set( 0, 0, 0 ); + verts[1].color.set( 0, 0, 0 ); + verts[2].color.set( 0, 0, 0 ); + verts[3].color.set( 0, 0, 0 ); + verts[4].color.set( 0, 0, 0 ); + verts[5].color.set( 0, 0, 0 ); + + if(mTextAreaHit == ArrowUp) + { + verts[0].point.set( (F32)midPoint.x, (F32)start.y + 1.0f, 0.0f ); + verts[1].point.set( (F32)start.x + 11.0f, (F32)midPoint.y - 2.0f, 0.0f ); + verts[2].point.set( (F32)start.x + 3.0f, (F32)midPoint.y - 2.0f, 0.0f ); + } + else + { + verts[0].point.set( (F32)midPoint.x, (F32)start.y + 2.0f, 0.0f ); + verts[1].point.set( (F32)start.x + 11.0f, (F32)midPoint.y - 1.0f, 0.0f ); + verts[2].point.set( (F32)start.x + 3.0f, (F32)midPoint.y - 1.0f, 0.0f ); + } + + if(mTextAreaHit == ArrowDown) + { + verts[3].point.set( (F32)midPoint.x, (F32)(start.y + getExtent().y - 1), 0.0f ); + verts[4].point.set( (F32)start.x + 11.0f, (F32)midPoint.y + 3.0f, 0.0f ); + verts[5].point.set( (F32)start.x + 3.0f, (F32)midPoint.y + 3.0f, 0.0f ); + } + else + { + verts[3].point.set( (F32)midPoint.x, (F32)(start.y + getExtent().y - 2), 0.0f ); + verts[4].point.set( (F32)start.x + 11.0f, (F32)midPoint.y + 2.0f, 0.0f ); + verts[5].point.set( (F32)start.x + 3.0f, (F32)midPoint.y + 2.0f, 0.0f ); + } + + verts.unlock(); + + GFX->setVertexBuffer( verts ); + GFX->drawPrimitive( GFXTriangleList, 0, 2 ); +} + +void GuiTextEditSliderCtrl::onPreRender() +{ + if (isFirstResponder()) + { + U32 timeElapsed = Platform::getVirtualMilliseconds() - mTimeLastCursorFlipped; + mNumFramesElapsed++; + if ((timeElapsed > 500) && (mNumFramesElapsed > 3)) + { + mCursorOn = !mCursorOn; + mTimeLastCursorFlipped = Sim::getCurrentTime(); + mNumFramesElapsed = 0; + setUpdate(); + } + + //update the cursor if the text is scrolling + if (mDragHit) + { + if ((mScrollDir < 0) && (mCursorPos > 0)) + { + mCursorPos--; + } + else if ((mScrollDir > 0) && (mCursorPos < (S32)dStrlen(mText))) + { + mCursorPos++; + } + } + } +} + + diff --git a/Engine/source/gui/controls/guiTextEditSliderCtrl.h b/Engine/source/gui/controls/guiTextEditSliderCtrl.h new file mode 100644 index 000000000..24986dd9b --- /dev/null +++ b/Engine/source/gui/controls/guiTextEditSliderCtrl.h @@ -0,0 +1,91 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUITEXTEDITSLIDERCTRL_H_ +#define _GUITEXTEDITSLIDERCTRL_H_ + +#ifndef _GUITYPES_H_ +#include "gui/core/guiTypes.h" +#endif +#ifndef _GUITEXTEDITCTRL_H_ +#include "gui/controls/guiTextEditCtrl.h" +#endif + +class GuiTextEditSliderCtrl : public GuiTextEditCtrl +{ + typedef GuiTextEditCtrl Parent; + +public: + + enum CtrlArea + { + None, + Slider, + ArrowUp, + ArrowDown + }; + + GuiTextEditSliderCtrl(); + ~GuiTextEditSliderCtrl(); + + DECLARE_CONOBJECT(GuiTextEditSliderCtrl); + DECLARE_CATEGORY( "Gui Values" ); + DECLARE_DESCRIPTION( "A text that shows a numeric value and up/down arrows to\n" + "increase/decrease the value." ); + + static void initPersistFields(); + + virtual void getText(char *dest); // dest must be of size + // StructDes::MAX_STRING_LEN + 1 + + virtual void setText(const char *txt); + + void setValue(); + void checkRange(); + void checkIncValue(); + void timeInc(U32 elapseTime); + + virtual bool onKeyDown(const GuiEvent &event); + virtual void onMouseDown(const GuiEvent &event); + virtual void onMouseDragged(const GuiEvent &event); + virtual void onMouseUp(const GuiEvent &event); + virtual bool onMouseWheelUp(const GuiEvent &event); + virtual bool onMouseWheelDown(const GuiEvent &event); + + virtual void onPreRender(); + virtual void onRender(Point2I offset, const RectI &updateRect); + +protected: + + Point2F mRange; + F32 mIncAmount; + F32 mValue; + F32 mIncCounter; + F32 mMulInc; + StringTableEntry mFormat; + U32 mMouseDownTime; + bool mFocusOnMouseWheel; + + CtrlArea mTextAreaHit; +}; + +#endif //_GUITEXTEDITSLIDERCTRL_H_ diff --git a/Engine/source/gui/controls/guiTextListCtrl.cpp b/Engine/source/gui/controls/guiTextListCtrl.cpp new file mode 100644 index 000000000..7c6b9602a --- /dev/null +++ b/Engine/source/gui/controls/guiTextListCtrl.cpp @@ -0,0 +1,846 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/controls/guiTextListCtrl.h" + +#include "console/consoleTypes.h" +#include "console/console.h" +#include "gui/containers/guiScrollCtrl.h" +#include "gui/core/guiDefaultControlRender.h" +#include "gfx/gfxDrawUtil.h" +#include "console/engineAPI.h" + +IMPLEMENT_CONOBJECT(GuiTextListCtrl); + +ConsoleDocClass( GuiTextListCtrl, + "@brief GUI control that displays a list of text. Text items in the list can be individually selected.\n\n" + + "@tsexample\n" + + " new GuiTextListCtrl(EndGameGuiList)\n" + " {\n" + " columns = \"0 256\";\n" + " fitParentWidth = \"1\";\n" + " clipColumnText = \"0\";\n" + " //Properties not specific to this control have been omitted from this example.\n" + " };\n" + "@endtsexample\n\n" + + "@see Reference\n\n" + + "@ingroup GuiControls\n" +); + + +IMPLEMENT_CALLBACK( GuiTextListCtrl, onSelect, void, (const char* cellid, const char* text),( cellid , text ), + "@brief Called whenever an item in the list is selected.\n\n" + "@param cellid The ID of the cell that was selected\n" + "@param text The text in the selected cel\n\n" + "@tsexample\n" + "// A cel in the control was selected, causing the callback to occur\n" + "GuiTextListCtrl::onSelect(%this,%callid,%text)\n" + " {\n" + " // Code to run when a cel item is selected\n" + " }\n" + "@endtsexample\n\n" + "@see GuiControl\n\n" +); + +IMPLEMENT_CALLBACK( GuiTextListCtrl, onDeleteKey, void, ( const char* id ),( id ), + "@brief Called when the delete key has been pressed.\n\n" + "@param id Id of the selected item in the list\n" + "@tsexample\n" + "// The delete key was pressed while the GuiTextListCtrl was in focus, causing the callback to occur.\n" + "GuiTextListCtrl::onDeleteKey(%this,%id)\n" + " {\n" + " // Code to run when the delete key is pressed\n" + " }\n" + "@endtsexample\n\n" + "@see GuiControl\n\n" +); + +static int sortColumn; +static bool sIncreasing; + +static const char *getColumn(const char *text) +{ + int ct = sortColumn; + while(ct--) + { + text = dStrchr(text, '\t'); + if(!text) + return ""; + text++; + } + return text; +} + +static S32 QSORT_CALLBACK textCompare( const void* a, const void* b ) +{ + GuiTextListCtrl::Entry *ea = (GuiTextListCtrl::Entry *) (a); + GuiTextListCtrl::Entry *eb = (GuiTextListCtrl::Entry *) (b); + S32 result = dStrnatcasecmp( getColumn( ea->text ), getColumn( eb->text ) ); + return ( sIncreasing ? result : -result ); +} + +static S32 QSORT_CALLBACK numCompare(const void *a,const void *b) +{ + GuiTextListCtrl::Entry *ea = (GuiTextListCtrl::Entry *) (a); + GuiTextListCtrl::Entry *eb = (GuiTextListCtrl::Entry *) (b); + const char* aCol = getColumn( ea->text ); + const char* bCol = getColumn( eb->text ); + F32 result = dAtof(aCol) - dAtof(bCol); + S32 res = result < 0 ? -1 : (result > 0 ? 1 : 0); + + return ( sIncreasing ? res : -res ); +} + +GuiTextListCtrl::GuiTextListCtrl() +{ + VECTOR_SET_ASSOCIATION(mList); + VECTOR_SET_ASSOCIATION(mColumnOffsets); + + mActive = true; + mSize.set(1, 0); + mColumnOffsets.push_back(0); + mFitParentWidth = true; + mClipColumnText = false; +} + +void GuiTextListCtrl::initPersistFields() +{ + addField("columns", TypeS32Vector, Offset(mColumnOffsets, GuiTextListCtrl), "A vector of column offsets. The number of values determines the number of columns in the table.\n" ); + addField("fitParentWidth", TypeBool, Offset(mFitParentWidth, GuiTextListCtrl), "If true, the width of this control will match the width of its parent.\n"); + addField("clipColumnText", TypeBool, Offset(mClipColumnText, GuiTextListCtrl), "If true, text exceeding a column's given width will get clipped.\n" ); + Parent::initPersistFields(); +} + +bool GuiTextListCtrl::onWake() +{ + if(!Parent::onWake()) + return false; + + setSize(mSize); + return true; +} + +U32 GuiTextListCtrl::getSelectedId() +{ + if (mSelectedCell.y == -1) + return InvalidId; + + return mList[mSelectedCell.y].id; +} + +U32 GuiTextListCtrl::getSelectedRow() +{ + return mSelectedCell.y; +} + +bool GuiTextListCtrl::cellSelected(Point2I cell) +{ + // Is the selection being cleared? + if( cell.x == -1 && cell.y == -1) + return Parent::cellSelected(cell); + + // Do not allow selection of inactive cells + if (cell.y >= 0 && cell.y < mSize.y && mList[cell.y].active) + return Parent::cellSelected(cell); + else + return false; +} + +void GuiTextListCtrl::onCellSelected(Point2I cell) +{ + onSelect_callback(Con::getIntArg(mList[cell.y].id), mList[cell.y].text); + execConsoleCallback(); +} + +void GuiTextListCtrl::onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver) +{ + if ( mList[cell.y].active ) + { + if (selected || (mProfile->mMouseOverSelected && mouseOver)) + { + RectI highlightRect = RectI(offset.x, offset.y, mCellSize.x, mCellSize.y); + highlightRect.inset( 0, -1 ); + renderFilledBorder( highlightRect, mProfile->mBorderColorHL, mProfile->mFillColorHL); + GFX->getDrawUtil()->setBitmapModulation(mProfile->mFontColorHL); + } + else + GFX->getDrawUtil()->setBitmapModulation(mouseOver ? mProfile->mFontColorHL : mProfile->mFontColor); + } + else + GFX->getDrawUtil()->setBitmapModulation( mProfile->mFontColorNA ); + + const char *text = mList[cell.y].text; + for(U32 index = 0; index < mColumnOffsets.size(); index++) + { + const char *nextCol = dStrchr(text, '\t'); + if(mColumnOffsets[index] >= 0) + { + dsize_t slen; + if(nextCol) + slen = nextCol - text; + else + slen = dStrlen(text); + + Point2I pos(offset.x + 4 + mColumnOffsets[index], offset.y); + + RectI saveClipRect; + bool clipped = false; + + if(mClipColumnText && (index != (mColumnOffsets.size() - 1))) + { + saveClipRect = GFX->getClipRect(); + + RectI clipRect(pos, Point2I(mColumnOffsets[index+1] - mColumnOffsets[index] - 4, mCellSize.y)); + if(clipRect.intersect(saveClipRect)) + { + clipped = true; + GFX->setClipRect( clipRect ); + } + } + + GFX->getDrawUtil()->drawTextN(mFont, pos, text, slen, mProfile->mFontColors); + + if(clipped) + GFX->setClipRect( saveClipRect ); + } + if(!nextCol) + break; + text = nextCol+1; + } +} + +U32 GuiTextListCtrl::getRowWidth(Entry *row) +{ + U32 width = 1; + const char *text = row->text; + for(U32 index = 0; index < mColumnOffsets.size(); index++) + { + const char *nextCol = dStrchr(text, '\t'); + U32 textWidth; + if(nextCol) + textWidth = mFont->getStrNWidth((const UTF8*)text, nextCol - text); + else + textWidth = mFont->getStrWidth((const UTF8*)text); + if(mColumnOffsets[index] >= 0) + width = getMax(width, mColumnOffsets[index] + textWidth); + if(!nextCol) + break; + text = nextCol+1; + } + return width; +} + +void GuiTextListCtrl::insertEntry(U32 id, const char *text, S32 index) +{ + Entry e; + e.text = dStrdup(text); + e.id = id; + e.active = true; + if(!mList.size()) + mList.push_back(e); + else + { + if(index > mList.size()) + index = mList.size(); + mList.insert(index); + mList[index] = e; + } + setSize(Point2I(1, mList.size())); +} + +void GuiTextListCtrl::addEntry(U32 id, const char *text) +{ + Entry e; + e.text = dStrdup(text); + e.id = id; + e.active = true; + mList.push_back(e); + setSize(Point2I(1, mList.size())); +} + +void GuiTextListCtrl::setEntry(U32 id, const char *text) +{ + S32 e = findEntryById(id); + if(e == -1) + addEntry(id, text); + else + { + dFree(mList[e].text); + mList[e].text = dStrdup(text); + + // Still have to call this to make sure cells are wide enough for new values: + setSize( Point2I( 1, mList.size() ) ); + } + setUpdate(); +} + +void GuiTextListCtrl::setEntryActive(U32 id, bool active) +{ + S32 index = findEntryById( id ); + if ( index == -1 ) + return; + + if ( mList[index].active != active ) + { + mList[index].active = active; + + // You can't have an inactive entry selected... + if ( !active && mSelectedCell.y >= 0 && mSelectedCell.y < mList.size() + && mList[mSelectedCell.y].id == id ) + setSelectedCell( Point2I( -1, -1 ) ); + + setUpdate(); + } +} + +S32 GuiTextListCtrl::findEntryById(U32 id) +{ + for(U32 i = 0; i < mList.size(); i++) + if(mList[i].id == id) + return i; + return -1; +} + +S32 GuiTextListCtrl::findEntryByText(const char *text) +{ + for(U32 i = 0; i < mList.size(); i++) + if(!dStricmp(mList[i].text, text)) + return i; + return -1; +} + +bool GuiTextListCtrl::isEntryActive(U32 id) +{ + S32 index = findEntryById( id ); + if ( index == -1 ) + return( false ); + + return( mList[index].active ); +} + +void GuiTextListCtrl::setSize(Point2I newSize) +{ + mSize = newSize; + + if ( bool( mFont ) ) + { + if ( mSize.x == 1 && mFitParentWidth ) + { + GuiScrollCtrl* parent = dynamic_cast(getParent()); + if ( parent ) + mCellSize.x = parent->getContentExtent().x; + } + else + { + // Find the maximum width cell: + S32 maxWidth = 1; + for ( U32 i = 0; i < mList.size(); i++ ) + { + U32 rWidth = getRowWidth( &mList[i] ); + if ( rWidth > maxWidth ) + maxWidth = rWidth; + } + + mCellSize.x = maxWidth + 8; + } + + mCellSize.y = mFont->getHeight() + 2; + } + + Point2I newExtent( newSize.x * mCellSize.x + mHeaderDim.x, newSize.y * mCellSize.y + mHeaderDim.y ); + setExtent( newExtent ); +} + +void GuiTextListCtrl::clear() +{ + while (mList.size()) + removeEntry(mList[0].id); + + mMouseOverCell.set( -1, -1 ); + setSelectedCell(Point2I(-1, -1)); +} + +void GuiTextListCtrl::sort(U32 column, bool increasing) +{ + if (getNumEntries() < 2) + return; + sortColumn = column; + sIncreasing = increasing; + dQsort((void *)&(mList[0]), mList.size(), sizeof(Entry), textCompare); +} + +void GuiTextListCtrl::sortNumerical( U32 column, bool increasing ) +{ + if ( getNumEntries() < 2 ) + return; + + sortColumn = column; + sIncreasing = increasing; + dQsort( (void*) &( mList[0] ), mList.size(), sizeof( Entry ), numCompare ); +} + +void GuiTextListCtrl::onRemove() +{ + clear(); + Parent::onRemove(); +} + +U32 GuiTextListCtrl::getNumEntries() +{ + return mList.size(); +} + +void GuiTextListCtrl::removeEntryByIndex(S32 index) +{ + if(index < 0 || index >= mList.size()) + return; + dFree(mList[index].text); + mList.erase(index); + + setSize(Point2I( 1, mList.size())); + setSelectedCell(Point2I(-1, -1)); +} + +void GuiTextListCtrl::removeEntry(U32 id) +{ + S32 index = findEntryById(id); + removeEntryByIndex(index); +} + +const char *GuiTextListCtrl::getSelectedText() +{ + if (mSelectedCell.y == -1) + return NULL; + + return mList[mSelectedCell.y].text; +} + +const char *GuiTextListCtrl::getScriptValue() +{ + return getSelectedText(); +} + +void GuiTextListCtrl::setScriptValue(const char *val) +{ + S32 e = findEntryByText(val); + if(e == -1) + setSelectedCell(Point2I(-1, -1)); + else + setSelectedCell(Point2I(0, e)); +} + +bool GuiTextListCtrl::onKeyDown( const GuiEvent &event ) +{ + //if this control is a dead end, make sure the event stops here + if ( !mVisible || !mActive || !mAwake ) + return true; + + S32 yDelta = 0; + switch( event.keyCode ) + { + case KEY_RETURN: + execAltConsoleCallback(); + break; + case KEY_LEFT: + case KEY_UP: + if ( mSelectedCell.y > 0 ) + { + mSelectedCell.y--; + yDelta = -mCellSize.y; + } + break; + case KEY_DOWN: + case KEY_RIGHT: + if ( mSelectedCell.y < ( mList.size() - 1 ) ) + { + mSelectedCell.y++; + yDelta = mCellSize.y; + } + break; + case KEY_HOME: + if ( mList.size() ) + { + mSelectedCell.y = 0; + yDelta = -(mCellSize.y * mList.size() + 1 ); + } + break; + case KEY_END: + if ( mList.size() ) + { + mSelectedCell.y = mList.size() - 1; + yDelta = (mCellSize.y * mList.size() + 1 ); + } + break; + case KEY_DELETE: + if ( mSelectedCell.y >= 0 && mSelectedCell.y < mList.size() ) + onDeleteKey_callback(Con::getIntArg( mList[mSelectedCell.y].id ) ); + break; + default: + return( Parent::onKeyDown( event ) ); + break; + }; + + GuiScrollCtrl* parent = dynamic_cast(getParent()); + if ( parent ) + parent->scrollDelta( 0, yDelta ); + + return ( true ); + + + +} + +//----------------------------------------------------------------------------- +// Console Methods +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTextListCtrl, getSelectedId, S32, (),, + "@brief Get the ID of the currently selected item.\n\n" + "@tsexample\n" + "// Acquire the ID of the selected item in the list.\n" + "%id = %thisGuiTextListCtrl.getSelectedId();\n" + "@endtsexample\n\n" + "@return The id of the selected item in the list.\n\n" + "@see GuiControl") +{ + return object->getSelectedId(); +} + +DefineEngineMethod( GuiTextListCtrl, setSelectedById, void, (int id),, + "@brief Finds the specified entry by id, then marks its row as selected.\n\n" + "@param id Entry within the text list to make selected.\n" + "@tsexample\n" + "// Define the id\n" + "%id = \"5\";\n\n" + "// Inform the GuiTextListCtrl control to set the defined id entry as selected\n" + "%thisGuiTextListCtrl.setSelectedById(%id);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + S32 index = object->findEntryById(id); + if(index < 0) + return ; + + object->setSelectedCell(Point2I(0, index)); +} + +DefineEngineMethod( GuiTextListCtrl, setSelectedRow, void, (int rowNum),, + "@briefSelects the specified row.\n\n" + "@param rowNum Row number to set selected.\n" + "@tsexample\n" + "// Define the row number to set selected\n" + "%rowNum = \"4\";\n\n" + "%guiTextListCtrl.setSelectedRow(%rowNum);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->setSelectedCell( Point2I( 0, rowNum ) ); +} + +DefineEngineMethod( GuiTextListCtrl, getSelectedRow, S32, (),, + "@brief Returns the selected row index (not the row ID).\n\n" + "@tsexample\n" + "// Acquire the selected row index\n" + "%rowIndex = %thisGuiTextListCtrl.getSelectedRow();\n" + "@endtsexample\n\n" + "@return Index of the selected row\n\n" + "@see GuiControl") +{ + return object->getSelectedRow(); +} + +DefineEngineMethod( GuiTextListCtrl, clearSelection, void, (),, + "@brief Set the selection to nothing.\n\n" + "@tsexample\n" + "// Deselect anything that is currently selected\n" + "%thisGuiTextListCtrl.clearSelection();\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->setSelectedCell(Point2I(-1, -1)); +} + +DefineEngineMethod( GuiTextListCtrl, addRow, S32, (int id, const char* text, int index),(0,"",-1), + "@brief Adds a new row at end of the list with the defined id and text.\n" + "If index is used, then the new row is inserted at the row location of 'index'.\n\n" + "@param id Id of the new row.\n" + "@param text Text to display at the new row.\n" + "@param index Index to insert the new row at. If not used, new row will be placed at the end of the list.\n" + "@tsexample\n" + "// Define the id\n" + "%id = \"4\";\n\n" + "// Define the text to display\n" + "%text = \"Display Text\"\n\n" + "// Define the index (optional)\n" + "%index = \"2\"\n\n" + "// Inform the GuiTextListCtrl control to add the new row with the defined information.\n" + "%rowIndex = %thisGuiTextListCtrl.addRow(%id,%text,%index);\n" + "@endtsexample\n\n" + "@return Returns the row index of the new row. If 'index' was defined, then this just returns the number of rows in the list.\n\n" + "@see References") +{ + S32 ret = object->mList.size(); + if(index == -1) + object->addEntry(id, text); + else + object->insertEntry(id, text, index); + + return ret; +} + +DefineEngineMethod( GuiTextListCtrl, setRowById, void, (int id, const char* text),, + "@brief Sets the text at the defined id.\n\n" + "@param id Id to change.\n" + "@param text Text to use at the Id.\n" + "@tsexample\n" + "// Define the id\n" + "%id = \"4\";\n\n" + "// Define the text\n" + "%text = \"Text To Display\";\n\n" + "// Inform the GuiTextListCtrl control to display the defined text at the defined id\n" + "%thisGuiTextListCtrl.setRowById(%id,%text);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->setEntry(id, text); +} + +DefineEngineMethod( GuiTextListCtrl, sort, void, ( int columnId, bool increasing ), ( true ), + "@brief Performs a standard (alphabetical) sort on the values in the specified column.\n\n" + "@param columnId Column ID to perform the sort on.\n" + "@param increasing If false, sort will be performed in reverse.\n" + "@tsexample\n" + "// Define the columnId\n" + "%id = \"1\";\n\n" + "// Define if we are increasing or not\n" + "%increasing = \"false\";\n\n" + "// Inform the GuiTextListCtrl to perform the sort operation\n" + "%thisGuiTextListCtrl.sort(%id,%increasing);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->sort( columnId, increasing ); +} + +DefineEngineMethod( GuiTextListCtrl, sortNumerical, void, (int columnID, bool increasing), ( true ), + "@brief Perform a numerical sort on the values in the specified column.\n\n" + "Detailed description\n\n" + "@param columnId Column ID to perform the sort on.\n" + "@param increasing If false, sort will be performed in reverse.\n" + "@tsexample\n" + "// Define the columnId\n" + "%id = \"1\";\n\n" + "// Define if we are increasing or not\n" + "%increasing = \"false\";\n\n" + "// Inform the GuiTextListCtrl to perform the sort operation\n" + "%thisGuiTextListCtrl.sortNumerical(%id,%increasing);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->sortNumerical( columnID, increasing ); +} + +DefineEngineMethod( GuiTextListCtrl, clear, void, (),, + "@brief Clear the list.\n\n" + "@tsexample\n" + "// Inform the GuiTextListCtrl control to clear its contents\n" + "%thisGuiTextListCtrl.clear();\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->clear(); +} + +DefineEngineMethod( GuiTextListCtrl, rowCount, S32, (),, + "@brief Get the number of rows.\n\n" + "@tsexample\n" + "// Get the number of rows in the list\n" + "%rowCount = %thisGuiTextListCtrl.rowCount();\n" + "@endtsexample\n\n" + "@return Number of rows in the list.\n\n" + "@see GuiControl") +{ + return object->getNumEntries(); +} + +DefineEngineMethod( GuiTextListCtrl, getRowId, S32, (int index),, + "@brief Get the row ID for an index.\n\n" + "@param index Index to get the RowID at\n" + "@tsexample\n" + "// Define the index\n" + "%index = \"3\";\n\n" + "// Request the row ID at the defined index\n" + "%rowId = %thisGuiTextListCtrl.getRowId(%index);\n" + "@endtsexample\n\n" + "@return RowId at the defined index.\n\n" + "@see GuiControl") +{ + if(index >= object->getNumEntries()) + return -1; + + return object->mList[index].id; +} + +DefineEngineMethod( GuiTextListCtrl, getRowTextById, const char*, (int id),, + "@brief Get the text of a row with the specified id.\n\n" + "@tsexample\n" + "// Define the id\n" + "%id = \"4\";\n\n" + "// Inform the GuiTextListCtrl control to return the text at the defined row id\n" + "%rowText = %thisGuiTextListCtrl.getRowTextById(%id);\n" + "@endtsexample\n\n" + "@return Row text at the requested row id.\n\n" + "@see GuiControl") +{ + S32 index = object->findEntryById(id); + if(index < 0) + return ""; + return object->mList[index].text; +} + +DefineEngineMethod( GuiTextListCtrl, getRowNumById, S32, (int id),, + "@brief Get the row number for a specified id.\n\n" + "@param id Id to get the row number at\n" + "@tsexample\n" + "// Define the id\n" + "%id = \"4\";\n\n" + "// Request the row number from the GuiTextListCtrl control at the defined id.\n" + "%rowNumber = %thisGuiTextListCtrl.getRowNumById(%id);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + S32 index = object->findEntryById(id); + if(index < 0) + return -1; + return index; +} + +DefineEngineMethod( GuiTextListCtrl, getRowText, const char*, (int index),, + "@brief Get the text of the row with the specified index.\n\n" + "@param index Row index to acquire the text at.\n" + "@tsexample\n" + "// Define the row index\n" + "%index = \"5\";\n\n" + "// Request the text from the row at the defined index\n" + "%rowText = %thisGuiTextListCtrl.getRowText(%index);\n" + "@endtsexample\n\n" + "@return Text at the defined row index.\n\n" + "@see GuiControl") +{ + if(index < 0 || index >= object->mList.size()) + return ""; + return object->mList[index].text; +} + +DefineEngineMethod( GuiTextListCtrl, removeRowById, void, (int id),, + "@brief Remove row with the specified id.\n\n" + "@param id Id to remove the row entry at\n" + "@tsexample\n" + "// Define the id\n" + "%id = \"4\";\n\n" + "// Inform the GuiTextListCtrl control to remove the row at the defined id\n" + "%thisGuiTextListCtrl.removeRowById(%id);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->removeEntry(id); +} + +DefineEngineMethod( GuiTextListCtrl, removeRow, void, (int index),, + "@brief Remove a row from the table, based on its index.\n\n" + "@param index Row index to remove from the list.\n" + "@tsexample\n" + "// Define the row index\n" + "%index = \"4\";\n\n" + "// Inform the GuiTextListCtrl control to remove the row at the defined row index\n" + "%thisGuiTextListCtrl.removeRow(%index);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->removeEntryByIndex(index); +} + +DefineEngineMethod( GuiTextListCtrl, scrollVisible, void, (int rowNum),, + "@brief Scroll so the specified row is visible\n\n" + "@param rowNum Row number to make visible\n" + "@tsexample\n" + "// Define the row number to make visible\n" + "%rowNum = \"4\";\n\n" + "// Inform the GuiTextListCtrl control to scroll the list so the defined rowNum is visible.\n" + "%thisGuiTextListCtrl.scrollVisible(%rowNum);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->scrollCellVisible(Point2I(0, rowNum)); +} + +DefineEngineMethod( GuiTextListCtrl, findTextIndex, S32, (const char* needle),, + "@brief Find needle in the list, and return the row number it was found in.\n\n" + "@param needle Text to find in the list.\n" + "@tsexample\n" + "// Define the text to find in the list\n" + "%needle = \"Text To Find\";\n\n" + "// Request the row number that contains the defined text to find\n\n" + "%rowNumber = %thisGuiTextListCtrl.findTextIndex(%needle);\n\n" + "@endtsexample\n\n" + "@return Row number that the defined text was found in,\n\n" + "@see GuiControl") +{ + return( object->findEntryByText(needle) ); +} + +DefineEngineMethod( GuiTextListCtrl, setRowActive, void, (int rowNum, bool active),, + "@brief Mark a specified row as active/not.\n\n" + "@param rowNum Row number to change the active state.\n" + "@param active Boolean active state to set the row number.\n" + "@tsexample\n" + "// Define the row number\n" + "%rowNum = \"4\";\n\n" + "// Define the boolean active state\n" + "%active = \"true\";\n\n" + "// Informthe GuiTextListCtrl control to set the defined active state at the defined row number.\n" + "%thisGuiTextListCtrl.setRowActive(%rowNum,%active);\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + object->setEntryActive( U32( rowNum ), active ); +} + +DefineEngineMethod( GuiTextListCtrl, isRowActive, bool, (int rowNum),, + "@brief Check if the specified row is currently active or not.\n\n" + "@param rowNum Row number to check the active state.\n" + "@tsexample\n" + "// Define the row number\n" + "%rowNum = \"5\";\n\n" + "// Request the active state of the defined row number from the GuiTextListCtrl control.\n" + "%rowActiveState = %thisGuiTextListCtrl.isRowActive(%rowNum);\n" + "@endtsexample\n\n" + "@return Active state of the defined row number.\n\n" + "@see GuiControl") +{ + return( object->isEntryActive( U32( rowNum ) ) ); +} diff --git a/Engine/source/gui/controls/guiTextListCtrl.h b/Engine/source/gui/controls/guiTextListCtrl.h new file mode 100644 index 000000000..45e156ee8 --- /dev/null +++ b/Engine/source/gui/controls/guiTextListCtrl.h @@ -0,0 +1,114 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUITEXTLISTCTRL_H_ +#define _GUITEXTLISTCTRL_H_ + +#ifndef _GUIARRAYCTRL_H_ +#include "gui/core/guiArrayCtrl.h" +#endif + +class GuiTextListCtrl : public GuiArrayCtrl +{ + private: + typedef GuiArrayCtrl Parent; + + public: + struct Entry + { + char *text; + U32 id; + bool active; + }; + + Vector mList; + + protected: + enum ScrollConst + { + UP = 0, + DOWN = 1 + }; + enum { + InvalidId = 0xFFFFFFFF + }; + Vector mColumnOffsets; + + bool mFitParentWidth; + bool mClipColumnText; + + U32 getRowWidth(Entry *row); + bool cellSelected(Point2I cell); + void onCellSelected(Point2I cell); + + public: + GuiTextListCtrl(); + + DECLARE_CONOBJECT(GuiTextListCtrl); + DECLARE_CATEGORY( "Gui Lists" ); + DECLARE_DESCRIPTION( "A control that displays text in tabular form." ); + + DECLARE_CALLBACK( void, onSelect, (const char* cellid, const char* text)); + DECLARE_CALLBACK( void, onDeleteKey, ( const char* id )); + + static void initPersistFields(); + + virtual void setCellSize( const Point2I &size ){ mCellSize = size; } + virtual void getCellSize( Point2I &size ){ size = mCellSize; } + + const char *getScriptValue(); + void setScriptValue(const char *value); + + U32 getNumEntries(); + + void clear(); + virtual void addEntry(U32 id, const char *text); + virtual void insertEntry(U32 id, const char *text, S32 index); + void setEntry(U32 id, const char *text); + void setEntryActive(U32 id, bool active); + S32 findEntryById(U32 id); + S32 findEntryByText(const char *text); + bool isEntryActive(U32 id); + + U32 getEntryId(U32 index); + + bool onWake(); + void removeEntry(U32 id); + virtual void removeEntryByIndex(S32 id); + virtual void sort(U32 column, bool increasing = true); + virtual void sortNumerical(U32 column, bool increasing = true); + + U32 getSelectedId(); + U32 getSelectedRow(); + const char *getSelectedText(); + + bool onKeyDown(const GuiEvent &event); + + virtual void onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver); + + void setSize(Point2I newSize); + void onRemove(); + void addColumnOffset(S32 offset) { mColumnOffsets.push_back(offset); } + void clearColumnOffsets() { mColumnOffsets.clear(); } +}; + +#endif //_GUI_TEXTLIST_CTRL_H diff --git a/Engine/source/gui/controls/guiTreeViewCtrl.cpp b/Engine/source/gui/controls/guiTreeViewCtrl.cpp new file mode 100644 index 000000000..4bd98b640 --- /dev/null +++ b/Engine/source/gui/controls/guiTreeViewCtrl.cpp @@ -0,0 +1,5397 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/controls/guiTreeViewCtrl.h" + +#include "core/frameAllocator.h" +#include "core/strings/findMatch.h" +#include "gui/containers/guiScrollCtrl.h" +#include "gui/worldEditor/editorIconRegistry.h" +#include "console/consoleTypes.h" +#include "console/console.h" +#include "gui/core/guiTypes.h" +#include "platform/event.h" +#include "gfx/gfxDrawUtil.h" +#include "gui/controls/guiTextEditCtrl.h" +#include "gui/editor/editorFunctions.h" +#include "console/engineAPI.h" + + +IMPLEMENT_CONOBJECT(GuiTreeViewCtrl); + +ConsoleDocClass( GuiTreeViewCtrl, + "@brief Hierarchical list of text items with optional icons.\n\n" + + "Can also be used to inspect SimObject hierarchies, primarily within editors.\n\n" + + "GuiTreeViewCtrls can either display arbitrary user-defined trees or can be used to display SimObject hierarchies where " + "each parent node in the tree is a SimSet or SimGroup and each leaf node is a SimObject.\n\n" + + "Each item in the tree has a text and a value. For trees that display SimObject hierarchies, the text for each item " + "is automatically derived from objects while the value for each item is the ID of the respective SimObject. For trees " + "that are not tied to SimObjects, both text and value of each item are set by the user.\n\n" + + "Additionally, items in the tree can have icons.\n\n" + + "Each item in the tree has a distinct numeric ID that is unique within its tree. The ID of the root item, which is always " + "present on a tree, is 0.\n\n" + + "@tsexample\n" + "new GuiTreeViewCtrl(DatablockEditorTree)\n" + "{\n" + " tabSize = \"16\";\n" + " textOffset = \"2\";\n" + " fullRowSelect = \"0\";\n" + " itemHeight = \"21\";\n" + " destroyTreeOnSleep = \"0\";\n" + " MouseDragging = \"0\";\n" + " MultipleSelections = \"1\";\n" + " DeleteObjectAllowed = \"1\";\n" + " DragToItemAllowed = \"0\";\n" + " ClearAllOnSingleSelection = \"1\";\n" + " showRoot = \"1\";\n" + " internalNamesOnly = \"0\";\n" + " objectNamesOnly = \"0\";\n" + " compareToObjectID = \"0\";\n" + " Profile = \"GuiTreeViewProfile\";\n" + " tooltipprofile = \"GuiToolTipProfile\";\n" + " hovertime = \"1000\";\n" + "};\n" + "@endtsexample\n\n" + + "@ingroup GuiContainers\n"); + +IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onDeleteObject, bool, ( SimObject* object ), ( object ), "" ); +IMPLEMENT_CALLBACK( GuiTreeViewCtrl, isValidDragTarget, bool, ( S32 id, const char* value ), ( id, value ), "" ); +IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onDefineIcons, void, (), (), "" ); +IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onAddGroupSelected, void, ( SimGroup* group ), ( group ), "" ); +IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onAddSelection, void, ( S32 itemOrObjectId, bool isLastSelection ), ( itemOrObjectId, isLastSelection ), "" ); +IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onSelect, void, ( S32 itemOrObjectId ), ( itemOrObjectId ), "" ); +IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onInspect, void, ( S32 itemOrObjectId ), ( itemOrObjectId ), "" ); +IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onRemoveSelection, void, ( S32 itemOrObjectId ), ( itemOrObjectId ), "" ); +IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onUnselect, void, ( S32 itemOrObjectId ), ( itemOrObjectId ), "" ); +IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onDeleteSelection, void, (), (), "" ); +IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onObjectDeleteCompleted, void, (), (), "" ); +IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onKeyDown, void, ( S32 modifier, S32 keyCode ), ( modifier, keyCode ), "" ); +IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onMouseUp, void, ( S32 hitItemId, S32 mouseClickCount ), ( hitItemId, mouseClickCount ), "" ); +IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onMouseDragged, void, (), (), "" ); +IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onRightMouseDown, void, ( S32 itemId, const Point2I& mousePos, SimObject* object ), ( itemId, mousePos, object ), "" ); +IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onRightMouseUp, void, ( S32 itemId, const Point2I& mousePos, SimObject* object ), ( itemId, mousePos, object ), "" ); +IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onBeginReparenting, void, (), (), "" ); +IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onEndReparenting, void, (), (), "" ); +IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onReparent, void, ( S32 itemOrObjectId, S32 oldParentItemOrObjectId, S32 newParentItemOrObjectId ), ( itemOrObjectId, oldParentItemOrObjectId, newParentItemOrObjectId ), "" ); +IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onDragDropped, void, (), (), "" ); +IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onAddMultipleSelectionBegin, void, (), (), "" ); +IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onAddMultipleSelectionEnd, void, (), (), "" ); +IMPLEMENT_CALLBACK( GuiTreeViewCtrl, canRenameObject, bool, ( SimObject* object ), ( object ), "" ); +IMPLEMENT_CALLBACK( GuiTreeViewCtrl, handleRenameObject, bool, ( const char* newName, SimObject* object ), ( newName, object ), "" ); +IMPLEMENT_CALLBACK( GuiTreeViewCtrl, onClearSelection, void, (), (), "" ); + + +static S32 QSORT_CALLBACK itemCompareCaseSensitive( const void *a, const void *b ) +{ + GuiTreeViewCtrl::Item* itemA = *( ( GuiTreeViewCtrl::Item** ) a ); + GuiTreeViewCtrl::Item* itemB = *( ( GuiTreeViewCtrl::Item** ) b ); + + char bufferA[ 1024 ]; + char bufferB[ 1024 ]; + + itemA->getDisplayText( sizeof( bufferA ), bufferA ); + itemB->getDisplayText( sizeof( bufferB ), bufferB ); + + return dStrnatcmp( bufferA, bufferB ); +} + +static S32 QSORT_CALLBACK itemCompareCaseInsensitive( const void *a, const void *b ) +{ + GuiTreeViewCtrl::Item* itemA = *( ( GuiTreeViewCtrl::Item** ) a ); + GuiTreeViewCtrl::Item* itemB = *( ( GuiTreeViewCtrl::Item** ) b ); + + char bufferA[ 1024 ]; + char bufferB[ 1024 ]; + + itemA->getDisplayText( sizeof( bufferA ), bufferA ); + itemB->getDisplayText( sizeof( bufferB ), bufferB ); + + return dStrnatcasecmp( bufferA, bufferB ); +} + +static void itemSortList( GuiTreeViewCtrl::Item*& firstChild, bool caseSensitive, bool traverseHierarchy, bool parentsFirst ) +{ + // Sort the children. + // Do this in a separate scope, so we release the buffers before + // recursing. + + { + Vector< GuiTreeViewCtrl::Item* > parents; + Vector< GuiTreeViewCtrl::Item* > items; + + // Put all items into the two vectors. + + for( GuiTreeViewCtrl::Item* item = firstChild; item != NULL; item = item->mNext ) + if( parentsFirst && item->isParent() ) + parents.push_back( item ); + else + items.push_back( item ); + + // Sort both vectors. + + dQsort( parents.address(), parents.size(), sizeof( GuiTreeViewCtrl::Item* ), caseSensitive ? itemCompareCaseSensitive : itemCompareCaseInsensitive ); + dQsort( items.address(), items.size(), sizeof( GuiTreeViewCtrl::Item* ), caseSensitive ? itemCompareCaseSensitive : itemCompareCaseInsensitive ); + + // Wipe current child chain then reconstruct it in reverse + // as we prepend items. + + firstChild = NULL; + + // Add child items. + + for( U32 i = items.size(); i > 0; -- i ) + { + GuiTreeViewCtrl::Item* child = items[ i - 1 ]; + + child->mNext = firstChild; + if( firstChild ) + firstChild->mPrevious = child; + + firstChild = child; + } + + // Add parent child items, if requested. + + for( U32 i = parents.size(); i > 0; -- i ) + { + GuiTreeViewCtrl::Item* child = parents[ i - 1 ]; + + child->mNext = firstChild; + if( firstChild ) + firstChild->mPrevious = child; + + firstChild = child; + } + + firstChild->mPrevious = NULL; + } + + // Traverse hierarchy, if requested. + + if( traverseHierarchy ) + { + GuiTreeViewCtrl::Item* child = firstChild; + while( child ) + { + if( child->isParent() ) + child->sort( caseSensitive, traverseHierarchy, parentsFirst ); + + child = child->mNext; + } + } +} + + +//============================================================================= +// GuiTreeViewCtrl::Item. +//============================================================================= +// MARK: ---- GuiTreeViewCtrl::Item ---- + +//----------------------------------------------------------------------------- + +GuiTreeViewCtrl::Item::Item( GuiTreeViewCtrl* parent, GuiControlProfile *pProfile ) +{ + AssertFatal( pProfile != NULL , "Cannot create a tree item without a valid tree and control profile!"); + mParentControl = parent; + mState = 0; + mId = -1; + mTabLevel = 0; + mIcon = 0; + mDataRenderWidth = 0; + mParent = NULL; + mChild = NULL; + mNext = NULL; + mPrevious = NULL; + mProfile = pProfile; + + mScriptInfo.mNormalImage = BmpCon; + mScriptInfo.mExpandedImage = BmpExp; + mScriptInfo.mText = NULL; + mScriptInfo.mValue = NULL; +} + +//----------------------------------------------------------------------------- + +GuiTreeViewCtrl::Item::~Item() +{ + _disconnectMonitors(); +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::Item::_connectMonitors() +{ + if( mInspectorInfo.mObject != NULL ) + { + SimSet* set = dynamic_cast< SimSet* >( mInspectorInfo.mObject.getPointer() ); + if( set ) + set->getSetModificationSignal().notify( mParentControl, &GuiTreeViewCtrl::_onInspectorSetObjectModified ); + } +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::Item::_disconnectMonitors() +{ + if( mInspectorInfo.mObject != NULL ) + { + SimSet* set = dynamic_cast< SimSet* >( mInspectorInfo.mObject.getPointer() ); + if( set ) + set->getSetModificationSignal().remove( mParentControl, &GuiTreeViewCtrl::_onInspectorSetObjectModified ); + } +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::Item::setNormalImage(S8 id) +{ + if(mState.test(InspectorData)) + { + Con::errorf("Tried to set normal image %d for item %d, which is InspectorData!", id, mId); + return; + } + + mScriptInfo.mNormalImage = id; +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::Item::setExpandedImage(S8 id) +{ + if(mState.test(InspectorData)) + { + Con::errorf("Tried to set expanded image %d for item %d, which is InspectorData!", id, mId); + return; + } + + mScriptInfo.mExpandedImage = id; +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::Item::setText(StringTableEntry txt) +{ + if(mState.test(InspectorData)) + { + Con::errorf("Tried to set text for item %d, which is InspectorData!", mId); + return; + } + + mScriptInfo.mText = txt; + + + // Update Render Data + if( !mProfile.isNull() ) + mDataRenderWidth = getDisplayTextWidth( mProfile->mFont ); + +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::Item::setValue(StringTableEntry val) +{ + if(mState.test(InspectorData)) + { + Con::errorf("Tried to set value for item %d, which is InspectorData!", mId); + return; + } + + mScriptInfo.mValue = const_cast(val); // mValue really ought to be a StringTableEntry + + // Update Render Data + if( !mProfile.isNull() ) + mDataRenderWidth = getDisplayTextWidth( mProfile->mFont ); + +} + +//----------------------------------------------------------------------------- + +S8 GuiTreeViewCtrl::Item::getNormalImage() const +{ + if(mState.test(InspectorData)) + { + Con::errorf("Tried to get the normal image for item %d, which is InspectorData!", mId); + return 0; // fail safe for width determinations + } + + return mScriptInfo.mNormalImage; +} + +//----------------------------------------------------------------------------- + +S8 GuiTreeViewCtrl::Item::getExpandedImage() const +{ + if(mState.test(InspectorData)) + { + Con::errorf("Tried to get the expanded image for item %d, which is InspectorData!", mId); + return 0; // fail safe for width determinations + } + + return mScriptInfo.mExpandedImage; +} + +//----------------------------------------------------------------------------- + +StringTableEntry GuiTreeViewCtrl::Item::getText() +{ + if(mState.test(InspectorData)) + { + Con::errorf("Tried to get the text for item %d, which is InspectorData!", mId); + return NULL; + } + + return ( mScriptInfo.mText ) ? mScriptInfo.mText : StringTable->EmptyString(); +} + +//----------------------------------------------------------------------------- + +StringTableEntry GuiTreeViewCtrl::Item::getValue() +{ + if(mState.test(InspectorData)) + { + Con::errorf("Tried to get the value for item %d, which is InspectorData!", mId); + return NULL; + } + + return ( mScriptInfo.mValue ) ? mScriptInfo.mValue : StringTable->EmptyString(); +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::Item::setObject(SimObject *obj) +{ + if(!mState.test(InspectorData)) + { + Con::errorf("Tried to set the object for item %d, which is not InspectorData!", mId); + return; + } + + _disconnectMonitors(); + mInspectorInfo.mObject = obj; + _connectMonitors(); + + // Update Render Data + if( !mProfile.isNull() ) + mDataRenderWidth = getDisplayTextWidth( mProfile->mFont ); +} + +//----------------------------------------------------------------------------- + +SimObject *GuiTreeViewCtrl::Item::getObject() +{ + if(!mState.test(InspectorData)) + { + Con::errorf("Tried to get the object for item %d, which is not InspectorData!", mId); + return NULL; + } + + return mInspectorInfo.mObject; +} + +//----------------------------------------------------------------------------- + +U32 GuiTreeViewCtrl::Item::getDisplayTextLength() +{ + if( mState.test( InspectorData ) ) + { + SimObject *obj = getObject(); + if( !obj ) + return 0; + + StringTableEntry name = obj->getName(); + StringTableEntry internalName = obj->getInternalName(); + StringTableEntry className = obj->getClassName(); + + if( showInternalNameOnly() ) + { + if( internalName && internalName[ 0 ] ) + return dStrlen( internalName ); + else + return dStrlen( "(none)" ); + } + else if( showObjectNameOnly() ) + { + if( name && name[ 0 ] ) + return dStrlen( name ); + else if( mState.test( ShowClassNameForUnnamed ) ) + return dStrlen( className ); + else + return dStrlen( "(none)" ); + } + + dsize_t len = 0; + if( mState.test( ShowObjectId ) ) + len += dStrlen( obj->getIdString() ) + 2; // ': ' + if( mState.test( ShowClassName ) ) + { + if( name && name[ 0 ] ) + len += dStrlen( className ) + 3; // ' - ' + else + len += dStrlen( className ); + } + if( mState.test( ShowObjectName ) ) + { + if( name && name[ 0 ] ) + len += dStrlen( name ); + else if( mState.test( ShowClassNameForUnnamed ) ) + len += dStrlen( className ); + } + if( mState.test( ShowInternalName ) ) + { + if( internalName && internalName[ 0 ] ) + len += dStrlen( internalName ) + 3; // ' []' + } + + return len; + } + + StringTableEntry pText = getText(); + if( pText == NULL ) + return 0; + + return dStrlen( pText ); +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::Item::getDisplayText(U32 bufLen, char *buf) +{ + FrameAllocatorMarker txtAlloc; + + if( mState.test( InspectorData ) ) + { + SimObject *pObject = getObject(); + if( pObject ) + { + const char* pObjName = pObject->getName(); + const char* pInternalName = pObject->getInternalName(); + + bool hasInternalName = pInternalName && pInternalName[0]; + bool hasObjectName = pObjName && pObjName[0]; + + const char* pClassName = pObject->getClassName(); + + if( showInternalNameOnly() ) + dSprintf( buf, bufLen, "%s", hasInternalName ? pInternalName : "(none)" ); + else if( showObjectNameOnly() ) + { + if( !hasObjectName && mState.test( ShowClassNameForUnnamed ) ) + dSprintf( buf, bufLen, "%s", pClassName ); + else + dSprintf( buf, bufLen, "%s", hasObjectName ? pObjName : "(none)" ); + } + else + { + char* ptr = buf; + int len = bufLen; + + if( mState.test( ShowObjectId ) ) + { + S32 n = dSprintf( ptr, len, "%d: ", pObject->getId() ); + ptr += n; + len -= n; + } + + if( mState.test( ShowClassName ) ) + { + S32 n; + if( hasObjectName && mState.test( ShowObjectName ) ) + n = dSprintf( ptr, len, "%s - ", pClassName ); + else + n = dSprintf( ptr, len, "%s", pClassName ); + + ptr += n; + len -= n; + } + + if( mState.test( ShowObjectName ) ) + { + S32 n = 0; + if( hasObjectName ) + n = dSprintf( ptr, len, "%s", pObjName ); + else if( mState.test( ShowClassNameForUnnamed ) ) + n = dSprintf( ptr, len, "%s", pClassName ); + + ptr += n; + len -= n; + } + + if( hasInternalName && mState.test( ShowInternalName ) ) + dSprintf( ptr, len, " [%s]", pInternalName ); + } + } + else + buf[ 0 ] = '\0'; + } + else + { + // Script data! (copy it in) + dStrncpy(buf, getText(), bufLen); + } +} + +//----------------------------------------------------------------------------- + +S32 GuiTreeViewCtrl::Item::getDisplayTextWidth(GFont *font) +{ + if( !font ) + return 0; + + FrameAllocatorMarker txtAlloc; + U32 bufLen = getDisplayTextLength(); + if( bufLen == 0 ) + return 0; + + // Add space for the string terminator + bufLen++; + + char *buf = (char*)txtAlloc.alloc(bufLen); + getDisplayText(bufLen, buf); + + return font->getStrWidth(buf); +} + +//----------------------------------------------------------------------------- + +bool GuiTreeViewCtrl::Item::hasObjectBasedTooltip() +{ + if(mState.test(Item::InspectorData)) + { + SimObject *pObject = getObject(); + if(pObject) + { + const char* pClassName = pObject->getClassName(); + + // Retrieve custom tooltip string + String method("GetTooltip"); + method += pClassName; + if(mParentControl->isMethod(method.c_str())) + { + return true; + } + } + } + + return false; +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::Item::getTooltipText(U32 bufLen, char *buf) +{ + getDisplayText(bufLen, buf); + + if(mState.test(Item::InspectorData)) + { + SimObject *pObject = getObject(); + if(pObject) + { + const char* pClassName = pObject->getClassName(); + + // Retrieve custom tooltip string + String method("GetTooltip"); + method += pClassName; + if(mParentControl->isMethod(method.c_str())) + { + const char* tooltip = Con::executef( mParentControl, method.c_str(), pObject->getIdString() ); + dsize_t len = dStrlen(buf); + S32 newBufLen = bufLen-len; + if(dStrlen(tooltip) > 0 && newBufLen > 0) + { + dSprintf(buf+len, newBufLen, "\n%s", tooltip); + } + } + } + } +} + +//----------------------------------------------------------------------------- + +bool GuiTreeViewCtrl::Item::isParent() const +{ + if(mState.test(VirtualParent)) + { + if( !isInspectorData() ) + return true; + + // Does our object have any children? + if(mInspectorInfo.mObject) + { + SimSet *pSimSet = dynamic_cast( (SimObject*)mInspectorInfo.mObject); + if ( pSimSet != NULL && pSimSet->size() > 0) + return pSimSet->size(); + } + } + + // Otherwise, just return whether the child list is populated. + return mChild; +} + +//----------------------------------------------------------------------------- + +bool GuiTreeViewCtrl::Item::isExpanded() const +{ + if(mState.test(InspectorData)) + return mInspectorInfo.mObject ? mInspectorInfo.mObject->isExpanded() : false; + else + return mState.test(Expanded); +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::Item::setExpanded(bool f) +{ + if( mState.test(InspectorData) ) + { + if( !mInspectorInfo.mObject.isNull() ) + mInspectorInfo.mObject->setExpanded(f); + } + else + mState.set(Expanded, f); +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::Item::setVirtualParent( bool value ) +{ + mState.set(VirtualParent, value); +} + +//----------------------------------------------------------------------------- + +GuiTreeViewCtrl::Item* GuiTreeViewCtrl::Item::findChildByName( const char* name ) +{ + Item* child = mChild; + while( child ) + { + if( dStricmp( child->mScriptInfo.mText, name ) == 0 ) + return child; + + child = child->mNext; + } + + return NULL; +} + +//----------------------------------------------------------------------------- + +GuiTreeViewCtrl::Item *GuiTreeViewCtrl::Item::findChildByValue(const SimObject *obj) +{ + // Iterate over our children and try to find the given + // SimObject + + Item *pResultObj = mChild; + + while(pResultObj) + { + // CodeReview this check may need to be removed + // if we want to use the tree for data that + // isn't related to SimObject based objects with + // arbitrary values associated with them [5/5/2007 justind] + + // Skip non-inspector data stuff. + if(pResultObj->mState.test(InspectorData)) + { + if(pResultObj->getObject() == obj) + break; // Whoa. + } + pResultObj = pResultObj->mNext; + } + // If the loop terminated we are NULL, otherwise we have the result in res. + return pResultObj; +} + +//----------------------------------------------------------------------------- + +GuiTreeViewCtrl::Item *GuiTreeViewCtrl::Item::findChildByValue( StringTableEntry Value ) +{ + // Iterate over our children and try to find the given Value + // Note : This is a case-insensitive search + Item *pResultObj = mChild; + + while(pResultObj) + { + + // check the script value of the item against the specified value + if( pResultObj->mScriptInfo.mValue != NULL && dStricmp( pResultObj->mScriptInfo.mValue, Value ) == 0 ) + return pResultObj; + pResultObj = pResultObj->mNext; + } + // If the loop terminated we didn't find an item with the specified script value + return NULL; +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::Item::sort( bool caseSensitive, bool traverseHierarchy, bool parentsFirst ) +{ + itemSortList( mChild, caseSensitive, traverseHierarchy, parentsFirst ); +} + +//============================================================================= +// GuiTreeViewCtrl. +//============================================================================= +// MARK: ---- GuiTreeViewCtrl ---- + +//----------------------------------------------------------------------------- + +GuiTreeViewCtrl::GuiTreeViewCtrl() +{ + VECTOR_SET_ASSOCIATION(mItems); + VECTOR_SET_ASSOCIATION(mVisibleItems); + VECTOR_SET_ASSOCIATION(mSelectedItems); + VECTOR_SET_ASSOCIATION(mSelected); + + mItemFreeList = NULL; + mRoot = NULL; + mItemCount = 0; + mSelectedItem = 0; + mStart = 0; + mPossibleRenameItem = NULL; + mRenamingItem = NULL; + mTempItem = NULL; + mRenameCtrl = NULL; + + mDraggedToItem = 0; + mCurrentDragCell = 0; + mPreviousDragCell = 0; + mDragMidPoint = NomDragMidPoint; + mMouseDragged = false; + mDebug = false; + + // persist info.. + mTabSize = 16; + mTextOffset = 2; + mFullRowSelect = false; + mItemHeight = 20; + + // + setSize(Point2I(1, 0)); + + // Set up default state + mFlags.set(ShowTreeLines); + mFlags.set(IsEditable, false); + + mDestroyOnSleep = true; + mSupportMouseDragging = true; + mMultipleSelections = true; + mDeleteObjectAllowed = true; + mDragToItemAllowed = true; + mShowRoot = true; + mUseInspectorTooltips = false; + mTooltipOnWidthOnly = false; + mCompareToObjectID = true; + mShowObjectIds = true; + mShowClassNames = true; + mShowObjectNames = true; + mShowInternalNames = true; + mShowClassNameForUnnamedObjects = false; + mFlags.set(RebuildVisible); + + mCanRenameObjects = true; + mRenameInternal = false; + + mClearAllOnSingleSelection = true; + + mBitmapBase = StringTable->insert(""); + mTexRollover = NULL; + mTexSelected = NULL; + + mRenderTooltipDelegate.bind( this, &GuiTreeViewCtrl::renderTooltip ); +} + +//----------------------------------------------------------------------------- + +GuiTreeViewCtrl::~GuiTreeViewCtrl() +{ + _destroyTree(); +} + +//------------------------------------------------------------------------------ + +void GuiTreeViewCtrl::initPersistFields() +{ + addGroup( "TreeView" ); + + addField( "tabSize", TypeS32, Offset(mTabSize, GuiTreeViewCtrl)); + addField( "textOffset", TypeS32, Offset(mTextOffset, GuiTreeViewCtrl)); + addField( "fullRowSelect", TypeBool, Offset(mFullRowSelect, GuiTreeViewCtrl)); + addField( "itemHeight", TypeS32, Offset(mItemHeight, GuiTreeViewCtrl)); + addField( "destroyTreeOnSleep", TypeBool, Offset(mDestroyOnSleep, GuiTreeViewCtrl), + "If true, the entire tree item hierarchy is deleted when the control goes to sleep." ); + addField( "mouseDragging", TypeBool, Offset(mSupportMouseDragging, GuiTreeViewCtrl)); + addField( "multipleSelections", TypeBool, Offset(mMultipleSelections, GuiTreeViewCtrl), + "If true, multiple items can be selected concurrently." ); + addField( "deleteObjectAllowed", TypeBool, Offset(mDeleteObjectAllowed, GuiTreeViewCtrl)); + addField( "dragToItemAllowed", TypeBool, Offset(mDragToItemAllowed, GuiTreeViewCtrl)); + addField( "clearAllOnSingleSelection", TypeBool, Offset(mClearAllOnSingleSelection, GuiTreeViewCtrl)); + addField( "showRoot", TypeBool, Offset(mShowRoot, GuiTreeViewCtrl), + "If true, the root item is shown in the tree." ); + addField( "useInspectorTooltips", TypeBool, Offset(mUseInspectorTooltips, GuiTreeViewCtrl)); + addField( "tooltipOnWidthOnly", TypeBool, Offset(mTooltipOnWidthOnly, GuiTreeViewCtrl)); + + endGroup( "TreeView" ); + + addGroup( "Inspector Trees" ); + + addField( "showObjectIds", TypeBool, Offset( mShowObjectIds, GuiTreeViewCtrl ), + "If true, item text labels for objects will include object IDs." ); + addField( "showClassNames", TypeBool, Offset( mShowClassNames, GuiTreeViewCtrl ), + "If true, item text labels for objects will include class names." ); + addField( "showObjectNames", TypeBool, Offset( mShowObjectNames, GuiTreeViewCtrl ), + "If true, item text labels for objects will include object names." ); + addField( "showInternalNames", TypeBool, Offset( mShowInternalNames, GuiTreeViewCtrl ), + "If true, item text labels for obje ts will include internal names." ); + addField( "showClassNameForUnnamedObjects", TypeBool, Offset( mShowClassNameForUnnamedObjects, GuiTreeViewCtrl ), + "If true, class names will be used as object names for unnamed objects." ); + addField( "compareToObjectID", TypeBool, Offset(mCompareToObjectID, GuiTreeViewCtrl)); + addField( "canRenameObjects", TypeBool, Offset(mCanRenameObjects, GuiTreeViewCtrl), + "If true clicking on a selected item ( that is an object and not the root ) will allow you to rename it." ); + addField( "renameInternal", TypeBool, Offset(mRenameInternal, GuiTreeViewCtrl), + "If true then object renaming operates on the internalName rather than the object name." ); + + endGroup( "Inspector Trees" ); + + Parent::initPersistFields(); +} + +//------------------------------------------------------------------------------ + +GuiTreeViewCtrl::Item * GuiTreeViewCtrl::getItem(S32 itemId) const +{ + if ( itemId > 0 && itemId <= mItems.size() ) + return mItems[itemId-1]; + return NULL; +} + +//------------------------------------------------------------------------------ + +GuiTreeViewCtrl::Item * GuiTreeViewCtrl::createItem(S32 icon) +{ + Item * pNewItem = NULL; + + // grab from the free list? + if( mItemFreeList ) + { + pNewItem = mItemFreeList; + mItemFreeList = pNewItem->mNext; + + // re-add to vector + mItems[ pNewItem->mId - 1 ] = pNewItem; + } + else + { + pNewItem = new Item( this, mProfile ); + + AssertFatal( pNewItem != NULL, "Fatal : unable to allocate tree item!"); + + mItems.push_back( pNewItem ); + + // set the id + pNewItem->mId = mItems.size(); + } + + // reset + if (icon) + pNewItem->mIcon = icon; + else + pNewItem->mIcon = Default; //default icon to stick next to an item + + pNewItem->mState = Item::ShowObjectId | Item::ShowClassName | Item::ShowObjectName | Item::ShowInternalName; + pNewItem->mTabLevel = 0; + + // Null out item pointers + pNewItem->mNext = 0; + pNewItem->mPrevious = 0; + pNewItem->mChild = 0; + pNewItem->mParent = 0; + + mItemCount++; + + return pNewItem; +} + +//------------------------------------------------------------------------------ + +void GuiTreeViewCtrl::_destroyChildren( Item* item, Item* parent, bool deleteObjects ) +{ + if ( !item || item == parent || !mItems[item->mId-1] ) + return; + + // destroy depth first, then siblings from last to first + if ( item->isParent() && item->mChild ) + _destroyChildren(item->mChild, item, deleteObjects); + if( item->mNext ) + _destroyChildren(item->mNext, parent, deleteObjects); + + // destroy the item + _destroyItem( item, deleteObjects ); +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::_destroyItem( Item* item, bool deleteObject ) +{ + if(!item) + return; + + if(item->isInspectorData()) + { + // make sure the SimObjectPtr is clean! + SimObject *pObject = item->getObject(); + if( pObject && pObject->isProperlyAdded() ) + { + bool skipDelete = !deleteObject; + + if( !skipDelete && isMethod( "onDeleteObject" ) ) + skipDelete = onDeleteObject_callback( pObject ); + + if ( !skipDelete ) + pObject->deleteObject(); + } + + item->setObject( NULL ); + } + + // Remove item from the selection + if (mSelectedItem == item->mId) + mSelectedItem = 0; + for ( S32 i = 0; i < mSelectedItems.size(); i++ ) + { + if ( mSelectedItems[i] == item ) + { + mSelectedItems.erase( i ); + break; + } + } + item->mState.clear(); + + // unlink + if( item->mPrevious ) + item->mPrevious->mNext = item->mNext; + if( item->mNext ) + item->mNext->mPrevious = item->mPrevious; + if( item->mParent && ( item->mParent->mChild == item ) ) + item->mParent->mChild = item->mNext; + + // remove from vector + mItems[item->mId-1] = 0; + + // set as root free item + item->mNext = mItemFreeList; + mItemFreeList = item; + mItemCount--; +} + +//------------------------------------------------------------------------------ + +void GuiTreeViewCtrl::_deleteItem(Item *item) +{ + removeItem(item->mId); +} + +//------------------------------------------------------------------------------ + +void GuiTreeViewCtrl::_destroyTree() +{ + // clear the item list + for(U32 i = 0; i < mItems.size(); i++) + { + Item *pFreeItem = mItems[ i ]; + if( pFreeItem != NULL ) + delete pFreeItem; + } + + mItems.clear(); + + // clear the free list + while(mItemFreeList) + { + Item *next = mItemFreeList->mNext; + delete mItemFreeList; + mItemFreeList = next; + } + + mVisibleItems.clear(); + mSelectedItems.clear(); + + // + mRoot = NULL; + mItemFreeList = NULL; + mItemCount = 0; + mSelectedItem = 0; + mDraggedToItem = 0; + + mRenamingItem = NULL; + mTempItem = NULL; + mPossibleRenameItem = NULL; +} + +//------------------------------------------------------------------------------ + +void GuiTreeViewCtrl::_onInspectorSetObjectModified( SetModification modification, SimSet* set, SimObject* object ) +{ + // Don't bother searching for the Item to see if it is actually visible and instead just + // mark our tree state as dirty so we get a rebuild on the next render. + + mFlags.set( RebuildVisible ); +} + +//------------------------------------------------------------------------------ + +GuiTreeViewCtrl::Item* GuiTreeViewCtrl::_findItemByAmbiguousId( S32 itemOrObjectId, bool buildVirtual ) +{ + Item* item = getItem( itemOrObjectId ); + if( item ) + return item; + + SimObject* object = Sim::findObject( itemOrObjectId ); + if( object ) + { + // If we should expand virtual trees in order to find the item, + // do so now. + if( buildVirtual ) + { + if( mFlags.test( RebuildVisible ) ) + buildVisibleTree(); + + SimGroup* group = object->getGroup(); + if( group ) + _expandObjectHierarchy( group ); + } + + if( objectSearch( object, &item ) ) + return item; + } + + return NULL; +} + +//------------------------------------------------------------------------------ + +void GuiTreeViewCtrl::_expandObjectHierarchy( SimGroup* group ) +{ + SimGroup* parent = group->getGroup(); + if( parent && !parent->isExpanded() ) + _expandObjectHierarchy( parent ); + + if( !group->isExpanded() ) + { + Item* item; + if( objectSearch( group, &item ) ) + { + item->setExpanded(); + onVirtualParentBuild( item, false ); + } + } +} + +//------------------------------------------------------------------------------ + +void GuiTreeViewCtrl::_buildItem( Item* item, U32 tabLevel, bool bForceFullUpdate ) +{ + if (!item || !mActive || !isVisible() || !mProfile ) + return; + + // If it's inspector data, make sure we still have it, if not, kill it. + if(item->isInspectorData() && !item->getObject() ) + { + removeItem(item->mId); + return; + } + + // If it's a virtual parent, give a chance to update itself... + if(item->mState.test( Item::VirtualParent) ) + { + // If it returns false the item has been removed. + + if( !onVirtualParentBuild( item, bForceFullUpdate ) ) + return; + } + + // If we have a filter pattern, sync the item's filtering status to it. + + if( !getFilterText().isEmpty() ) + { + // Determine the filtering status by looking for the filter + // text in the item's display text. + + char displayText[ 2048 ]; + item->getDisplayText( sizeof( displayText ), displayText ); + if( !dStristr( displayText, mFilterText ) ) + { + item->mState.set( Item::Filtered ); + + // If it's not a parent, we're done. Otherwise, there may be children + // that are not filtered so we need to process them first. + + if( !item->isParent() ) + return; + } + else + item->mState.clear( Item::Filtered ); + } + else + item->mState.clear( Item::Filtered ); + + // Is this the root item? + const bool isRoot = item == mRoot; + + // Add non-root items or the root if we're supposed to show it. + + if( ( mShowRoot || !isRoot ) && + !item->isFiltered() ) + { + item->mTabLevel = tabLevel; + mVisibleItems.push_back( item ); + + if( mProfile != NULL ) + { + mProfile->incLoadCount(); + + S32 width = mTextOffset + ( mTabSize * item->mTabLevel ) + getInspectorItemIconsWidth( item ) + item->getDisplayTextWidth( mProfile->mFont ); + + // check image + S32 image = BmpChild; + if ( item->isInspectorData() ) + image = item->isExpanded() ? BmpExp : BmpCon; + else + image = item->isExpanded() ? item->getExpandedImage() : item->getNormalImage(); + + if ( ( image >= 0 ) && ( image < mProfile->mBitmapArrayRects.size() ) ) + width += mProfile->mBitmapArrayRects[image].extent.x; + + if ( width > mMaxWidth ) + mMaxWidth = width; + + mProfile->decLoadCount(); + } + } + + // If expanded or a hidden root, add all the + // children items as well. + + if ( item->isExpanded() || + bForceFullUpdate || + ( isRoot && !mShowRoot ) ) + { + Item* child = item->mChild; + while ( child ) + { + // Bit of a hack so we can safely remove items as we + // traverse. + Item *pChildTemp = child; + child = child->mNext; + + _buildItem( pChildTemp, tabLevel + 1, bForceFullUpdate ); + } + } +} + +//------------------------------------------------------------------------------ + +void GuiTreeViewCtrl::buildVisibleTree(bool bForceFullUpdate) +{ + // Recursion Prevention. + if( mFlags.test( BuildingVisTree ) ) + return; + mFlags.set( BuildingVisTree, true ); + + if( mDebug ) + Con::printf( "Rebuilding visible tree" ); + + mMaxWidth = 0; + mVisibleItems.clear(); + + // If we're filtering, force a full update. + + if( !mFilterText.isEmpty() ) + bForceFullUpdate = true; + + // Update the flags. + mFlags.clear(RebuildVisible); + + // build the root items + Item *traverse = mRoot; + while(traverse) + { + _buildItem(traverse, 0, bForceFullUpdate); + traverse = traverse->mNext; + } + + // adjust the GuiArrayCtrl + mCellSize.set( mMaxWidth + mTextOffset, mItemHeight ); + setSize(Point2I(1, mVisibleItems.size())); + syncSelection(); + + // Done Recursing. + mFlags.clear( BuildingVisTree ); +} + +//------------------------------------------------------------------------------ + +bool GuiTreeViewCtrl::scrollVisible( S32 itemId ) +{ + Item* item = getItem(itemId); + if(item) + return scrollVisible(item); + + return false; +} + +//----------------------------------------------------------------------------- + +bool GuiTreeViewCtrl::scrollVisible( Item *item ) +{ + // Now, make sure it's visible (ie, all parents expanded) + Item *parent = item->mParent; + + if( !item->isInspectorData() && item->mState.test(Item::VirtualParent) ) + onVirtualParentExpand(item); + + while(parent) + { + parent->setExpanded(true); + + if( !parent->isInspectorData() && parent->mState.test(Item::VirtualParent) ) + onVirtualParentExpand(parent); + + parent = parent->mParent; + } + + // Get our scroll-pappy, if any. + GuiScrollCtrl *pScrollParent = dynamic_cast( getParent() ); + + if ( !pScrollParent ) + { + Con::warnf("GuiTreeViewCtrl::scrollVisible - parent control is not a GuiScrollCtrl!"); + return false; + } + + // And now, build the visible tree so we know where we have to scroll. + if( mFlags.test( RebuildVisible ) ) + buildVisibleTree(); + + // All done, let's figure out where we have to scroll... + for(S32 i=0; igetChildRelPos().x; + const S32 xWidth = ( mMaxWidth - xPos ); + + // Scroll to View the Item. + // Note: Delta X should be 0 so that we maintain the X axis position. + pScrollParent->scrollRectVisible( RectI( xPos, i * mItemHeight, xWidth, mItemHeight ) ); + return true; + } + } + + // If we got here, it's probably bad... + Con::errorf("GuiTreeViewCtrl::scrollVisible - was unable to find specified item in visible list!"); + return false; +} + +//------------------------------------------------------------------------------ + +S32 GuiTreeViewCtrl::insertItem(S32 parentId, const char * text, const char * value, const char * iconString, S16 normalImage, S16 expandedImage) +{ + if( ( parentId < 0 ) || ( parentId > mItems.size() ) ) + { + Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::insertItem: invalid parent id!"); + return 0; + } + + if((parentId != 0) && (mItems[parentId-1] == 0)) + { + Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::insertItem: parent item invalid!"); + return 0; + } + + + const char * pItemText = ( text != NULL ) ? text : ""; + const char * pItemValue = ( value != NULL ) ? value : ""; + + S32 icon = getIcon(iconString); + + // create an item (assigns id) + Item * pNewItem = createItem(icon); + if( pNewItem == NULL ) + return 0; + + pNewItem->setText( StringTable->insert( pItemText, true ) ); + pNewItem->setValue( StringTable->insert( pItemValue, true ) ); + + pNewItem->setNormalImage( normalImage ); + pNewItem->setExpandedImage( expandedImage ); + + // root level? + if(parentId == 0) + { + // insert back + if( mRoot != NULL ) + { + Item * pTreeTraverse = mRoot; + while( pTreeTraverse != NULL && pTreeTraverse->mNext != NULL ) + pTreeTraverse = pTreeTraverse->mNext; + + pTreeTraverse->mNext = pNewItem; + pNewItem->mPrevious = pTreeTraverse; + } + else + mRoot = pNewItem; + + mFlags.set(RebuildVisible); + } + else if( mItems.size() >= ( parentId - 1 ) ) + { + Item * pParentItem = mItems[parentId-1]; + + // insert back + if( pParentItem != NULL && pParentItem->mChild) + { + Item * pTreeTraverse = pParentItem->mChild; + while( pTreeTraverse != NULL && pTreeTraverse->mNext != NULL ) + pTreeTraverse = pTreeTraverse->mNext; + + pTreeTraverse->mNext = pNewItem; + pNewItem->mPrevious = pTreeTraverse; + } + else + pParentItem->mChild = pNewItem; + + pNewItem->mParent = pParentItem; + + if( pParentItem->isExpanded() ) + mFlags.set(RebuildVisible); + } + + return pNewItem->mId; +} + +//------------------------------------------------------------------------------ + +bool GuiTreeViewCtrl::removeItem( S32 itemId, bool deleteObjects ) +{ + if( isSelected( itemId ) ) + removeSelection( itemId ); + + // tree? + if(itemId == 0) + { + //RD: this does not delete objects and thus isn't coherent with the semantics of this method + + _destroyTree(); + return(true); + } + + Item * item = getItem(itemId); + if(!item) + { + //Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::removeItem: invalid item id!"); + return false; + } + + // root? + if(item == mRoot) + mRoot = item->mNext; + + // Dispose of any children... + if (item->mChild) + _destroyChildren( item->mChild, item, deleteObjects ); + + // Kill the item... + _destroyItem( item, deleteObjects ); + + // Update the rendered tree... + mFlags.set(RebuildVisible); + + return true; +} + + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::removeAllChildren(S32 itemId) +{ + Item * item = getItem(itemId); + if(item) + { + _destroyChildren(item->mChild, item); + } +} +//------------------------------------------------------------------------------ + +const S32 GuiTreeViewCtrl::getFirstRootItem() const +{ + return (mRoot ? mRoot->mId : 0); +} + +//------------------------------------------------------------------------------ + +S32 GuiTreeViewCtrl::getChildItem(S32 itemId) +{ + Item * item = getItem(itemId); + if(!item) + { + Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::getChild: invalid item id!"); + return(0); + } + + return(item->mChild ? item->mChild->mId : 0); +} + +//----------------------------------------------------------------------------- + +S32 GuiTreeViewCtrl::getParentItem(S32 itemId) +{ + Item * item = getItem(itemId); + if(!item) + { + Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::getParent: invalid item id!"); + return(0); + } + + return(item->mParent ? item->mParent->mId : 0); +} + +//----------------------------------------------------------------------------- + +S32 GuiTreeViewCtrl::getNextSiblingItem(S32 itemId) +{ + Item * item = getItem(itemId); + if(!item) + { + Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::getNextSibling: invalid item id!"); + return(0); + } + + return(item->mNext ? item->mNext->mId : 0); +} + +//----------------------------------------------------------------------------- + +S32 GuiTreeViewCtrl::getPrevSiblingItem(S32 itemId) +{ + Item * item = getItem(itemId); + if(!item) + { + Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::getPrevSibling: invalid item id!"); + return(0); + } + + return(item->mPrevious ? item->mPrevious->mId : 0); +} + +//------------------------------------------------------------------------------ + +bool GuiTreeViewCtrl::isValidDragTarget( Item* item ) +{ + bool isValid = true; + + // If this is inspector data, first make sure the item accepts all + // selected objects as children. This prevents bad surprises when + // certain SimSet subclasses reject children and start shoving them + // off to places of their own choosing. + + if( item->isInspectorData() ) + { + if( mDebug ) + Con::printf( "Checking %i:%s as drag-parent", + item->getObject()->getId(), item->getObject()->getClassName() ); + + SimSet* set = dynamic_cast< SimSet*>( item->getObject() ); + if( set ) + { + for( U32 i = 0; i < mSelectedItems.size(); ++ i ) + { + Item* selectedItem = mSelectedItems[ i ]; + + if( mDebug ) + Con::printf( "Checking %i:%s as drag-object", + selectedItem->getObject()->getId(), + selectedItem->getObject()->getClassName() ); + + if( selectedItem->isInspectorData() + && !set->acceptsAsChild( selectedItem->getObject() ) ) + return false; + } + } + } + + if( isMethod( "isValidDragTarget" ) ) + { + // We have a callback. Exclusively leave the decision whether + // the item is a valid drag target to it. + + isValid = isValidDragTarget_callback( item->mId, getItemValue( item->mId ) ); + } + else + { + // Make the item a valid drag target if it either already is + // a parent (including VirtualParents) or if dragging to non-parent + // items is explicitly allowed. + + isValid = item->isParent() || mDragToItemAllowed; + } + + return isValid; +} + +//------------------------------------------------------------------------------ + +S32 GuiTreeViewCtrl::getItemCount() +{ + return(mItemCount); +} + +//----------------------------------------------------------------------------- + +S32 GuiTreeViewCtrl::getSelectedItem() +{ + return mSelectedItem; +} + +//------------------------------------------------------------------------------ + +void GuiTreeViewCtrl::moveItemUp( S32 itemId ) +{ + GuiTreeViewCtrl::Item* pItem = getItem( itemId ); + if ( !pItem ) + { + Con::errorf( ConsoleLogEntry::General, "GuiTreeViewCtrl::moveItemUp: invalid item id!"); + return; + } + + Item * pParent = pItem->mParent; + Item * pPrevItem = pItem->mPrevious; + if ( pPrevItem == NULL || pParent == NULL ) + { + Con::errorf( ConsoleLogEntry::General, "GuiTreeViewCtrl::moveItemUp: Unable to move item up, bad data!"); + return; + } + + // Diddle the linked list! + if ( pPrevItem->mPrevious ) + pPrevItem->mPrevious->mNext = pItem; + else if ( pItem->mParent ) + pItem->mParent->mChild = pItem; + + if ( pItem->mNext ) + pItem->mNext->mPrevious = pPrevItem; + + pItem->mPrevious = pPrevItem->mPrevious; + pPrevItem->mNext = pItem->mNext; + pItem->mNext = pPrevItem; + pPrevItem->mPrevious = pItem; + + // Update SimObjects if Appropriate. + SimObject * pSimObject = NULL; + SimSet * pParentSet = NULL; + + // Fetch Current Add Set + if( pParent->isInspectorData() ) + pParentSet = dynamic_cast( pParent->getObject() ); + else + { + // parent is probably script data so we search up the tree for a + // set to put our object in + Item * pTraverse = pItem->mParent; + while ( pTraverse != NULL && !pTraverse->isInspectorData() ) + pTraverse = pTraverse->mParent; + + // found an ancestor who is an inspectorData? + if (pTraverse != NULL) + pParentSet = pTraverse->isInspectorData() ? dynamic_cast( pTraverse->getObject() ) : NULL; + } + + // Reorder the item and make sure that the children of the item get updated + // correctly prev item may be script... so find a prevItem if there is. + // We only need to reorder if there you move it above an inspector item. + if ( pSimObject != NULL && pParentSet != NULL ) + { + Item * pTraverse = pItem->mNext; + + while(pTraverse) + { + if (pTraverse->isInspectorData()) + break; + pTraverse = pTraverse->mNext; + } + + if (pTraverse && pItem->getObject() && pTraverse->getObject()) + pParentSet->reOrder(pItem->getObject(), pTraverse->getObject()); + } + + mFlags.set(RebuildVisible); +} +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::moveItemDown( S32 itemId ) +{ + GuiTreeViewCtrl::Item* item = getItem( itemId ); + if ( !item ) + { + Con::errorf( ConsoleLogEntry::General, "GuiTreeViewCtrl::moveItemDown: invalid item id!"); + return; + } + + Item* nextItem = item->mNext; + if ( !nextItem ) + { + Con::errorf( ConsoleLogEntry::General, "GuiTreeViewCtrl::moveItemDown: no next sibling?"); + return; + } + + // Diddle the linked list! + if ( nextItem->mNext ) + nextItem->mNext->mPrevious = item; + + if ( item->mPrevious ) + item->mPrevious->mNext = nextItem; + else if ( item->mParent ) + item->mParent->mChild = nextItem; + + item->mNext = nextItem->mNext; + nextItem->mPrevious = item->mPrevious; + item->mPrevious = nextItem; + nextItem->mNext = item; + + // And update the simobjects if apppropriate... + SimObject * simobj = NULL; + if (item->isInspectorData()) + simobj = item->getObject(); + + SimSet *parentSet = NULL; + + // grab the current parentSet if there is any... + if(item->mParent->isInspectorData()) + parentSet = dynamic_cast(item->mParent->getObject()); + else + { + // parent is probably script data so we search up the tree for a + // set to put our object in + Item * temp = item->mParent; + while (temp && !temp->isInspectorData()) + temp = temp->mParent; + + // found an ancestor who is an inspectorData? + parentSet = (temp && temp->isInspectorData()) ? dynamic_cast(temp->getObject()) : NULL; + } + + // Reorder the item and make sure that the children of the item get updated + // correctly prev item may be script... so find a prevItem if there is. + // We only need to reorder if there you move it above an inspector item. + if (simobj && parentSet) + { + Item * temp = item->mPrevious; + + while(temp) + { + if (temp->isInspectorData()) + break; + temp = temp->mPrevious; + } + + if (temp && item->getObject() && temp->getObject()) + parentSet->reOrder(temp->getObject(), item->getObject()); + } + + mFlags.set(RebuildVisible); +} + +//------------------------------------------------------------------------------ + +bool GuiTreeViewCtrl::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + // If we have dynamic fields, convert the "internalNamesOnly" and "objectNamesOnly" + // legacy fields. + + if( getFieldDictionary() ) + { + static StringTableEntry sInternalNamesOnly = StringTable->insert( "internalNamesOnly" ); + static StringTableEntry sObjectNamesOnly = StringTable->insert( "objectNamesOnly" ); + + const char* internalNamesOnly = getDataField( sInternalNamesOnly, NULL ); + if( internalNamesOnly && internalNamesOnly[ 0 ] && dAtob( internalNamesOnly ) ) + { + mShowObjectIds = false; + mShowClassNames = false; + mShowObjectNames = false; + mShowInternalNames = true; + } + + const char* objectNamesOnly = getDataField( sObjectNamesOnly, NULL ); + if( objectNamesOnly && objectNamesOnly[ 0 ] && dAtob( objectNamesOnly ) ) + { + mShowObjectIds = false; + mShowClassNames = false; + mShowObjectNames = true; + mShowInternalNames = false; + } + } + + return true; +} + +//------------------------------------------------------------------------------ + +bool GuiTreeViewCtrl::onWake() +{ + if(!Parent::onWake() || !mProfile->constructBitmapArray()) + return false; + + // If destroy on sleep, then we have to give things a chance to rebuild. + if(mDestroyOnSleep) + { + onDefineIcons_callback(); + } + + // Update the row height, if appropriate. + if(mProfile->mAutoSizeHeight) + { + // make sure it's big enough for both bitmap AND font... + mItemHeight = getMax((S32)mFont->getHeight(), (S32)mProfile->mBitmapArrayRects[0].extent.y); + } + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::onSleep() +{ + Parent::onSleep(); + + // If appropriate, blast the tree. (We probably rebuild it on wake.) + if( mDestroyOnSleep ) + _destroyTree(); + + if ( mRenameCtrl ) + { + mRenameCtrl->deleteObject(); + mRenameCtrl = NULL; + } +} + +//----------------------------------------------------------------------------- + +bool GuiTreeViewCtrl::buildIconTable(const char * icons) +{ + // Icons should be designated by the bitmap/png file names (minus the file extensions) + // and separated by colons (:). This list should be synchronized with the Icons enum. + + // Figure the size of the buffer we need... + const char* temp = dStrchr( icons, '\t' ); + U32 textLen = temp ? ( temp - icons ) : dStrlen( icons ); + + // Allocate temporary space. + FrameAllocatorMarker txtBuff; + char* drawText = (char*)txtBuff.alloc(sizeof(char) * (textLen + 4)); + dStrncpy( drawText, icons, textLen ); + drawText[textLen] = '\0'; + + U32 numIcons = 0; + char buf[ 1024 ]; + char* pos = drawText; + + // Count the number of icons and store them. + while( *pos && numIcons < MaxIcons ) + { + char* start = pos; + while( *pos && *pos != ':' ) + pos ++; + + const U32 len = pos - start; + if( len ) + { + dStrncpy( buf, start, getMin( sizeof( buf ) / sizeof( buf[ 0 ] ) - 1, len ) ); + buf[ len ] = '\0'; + + mIconTable[ numIcons ] = GFXTexHandle( buf, &GFXDefaultPersistentProfile, avar( "%s() - mIconTable[%d] (line %d)", __FUNCTION__, numIcons, __LINE__ ) ); + } + else + mIconTable[ numIcons ] = GFXTexHandle(); + + numIcons ++; + if( *pos ) + pos ++; + } + + return true; +} + +//------------------------------------------------------------------------------ + +void GuiTreeViewCtrl::onPreRender() +{ + Parent::onPreRender(); + + S32 nRootItemId = getFirstRootItem(); + if( nRootItemId == 0 ) + return; + + Item *pRootItem = getItem( nRootItemId ); + if( pRootItem == NULL ) + return; + + // Update every render in case new objects are added + if(mFlags.test(RebuildVisible)) + { + buildVisibleTree(); + mFlags.clear(RebuildVisible); + } +} + +//------------------------------------------------------------------------------ + +bool GuiTreeViewCtrl::_hitTest(const Point2I & pnt, Item* & item, BitSet32 & flags) +{ + // Initialize some things. + const Point2I pos = globalToLocalCoord(pnt); + flags.clear(); + item = 0; + + // get the hit cell + Point2I cell((pos.x < 0 ? -1 : pos.x / mCellSize.x), + (pos.y < 0 ? -1 : pos.y / mCellSize.y)); + + // valid? + if((cell.x < 0 || cell.x >= mSize.x) || + (cell.y < 0 || cell.y >= mSize.y)) + return false; + + flags.set(OnRow); + + // Grab the cell. + if (cell.y >= mVisibleItems.size()) + return false; //Invalid cell, so don't do anything + + item = mVisibleItems[cell.y]; + + S32 min = mTabSize * item->mTabLevel; + + // left of icon/text? + if(pos.x < min) + { + flags.set(OnIndent); + return true; + } + + // check image + S32 image = BmpChild; + + if(item->isInspectorData()) + image = item->isExpanded() ? BmpExp : BmpCon; + else + image = item->isExpanded() ? item->getExpandedImage() : item->getNormalImage(); + + if((image >= 0) && (image < mProfile->mBitmapArrayRects.size())) + min += mProfile->mBitmapArrayRects[image].extent.x; + + // Is it on the image? + if(pos.x < min) + { + flags.set(OnImage); + return(true); + } + + // Check the icon. + min += getInspectorItemIconsWidth( item ); + + if ( pos.x < min ) + { + flags.set(OnIcon); + return true; + } + + // Check the text. + + min += mProfile->mTextOffset.x; + + FrameAllocatorMarker txtAlloc; + U32 bufLen = item->getDisplayTextLength() + 1; + char *buf = (char*)txtAlloc.alloc(bufLen); + item->getDisplayText(bufLen, buf); + + min += mProfile->mFont->getStrWidth(buf); + if(pos.x < min) + flags.set(OnText); + + return true; +} + +//----------------------------------------------------------------------------- + +S32 GuiTreeViewCtrl::getInspectorItemIconsWidth(Item* & item) +{ + S32 width = 0; + + if( item->isInspectorData() ) + { + // Based on code in onRenderCell() + + S32 icon = Lock1; + S32 icon2 = Hidden; + + if (item->getObject() && item->getObject()->isLocked()) + { + if (mIconTable[icon]) + { + width += mIconTable[icon].getWidth(); + } + } + + if (item->getObject() && item->getObject()->isHidden()) + { + if (mIconTable[icon2]) + { + width += mIconTable[icon2].getWidth(); + } + } + + GFXTexHandle iconHandle; + if ( ( item->mIcon != -1 ) && mIconTable[item->mIcon] ) + iconHandle = mIconTable[item->mIcon]; + #ifdef TORQUE_TOOLS + else + iconHandle = gEditorIcons.findIcon( item->getObject() ); + #endif + + if ( iconHandle.isValid() ) + { + width += iconHandle.getWidth(); + } + } + else + { + S32 icon = item->isExpanded() ? item->mScriptInfo.mExpandedImage : item->mScriptInfo.mNormalImage; + if ( ( icon != -1 ) && mIconTable[icon] ) + { + width += mIconTable[icon].getWidth(); + } + } + + return width; +} + +//----------------------------------------------------------------------------- + +bool GuiTreeViewCtrl::setAddGroup(SimObject * obj) +{ + // make sure we're talking about a group. + SimGroup * grp = dynamic_cast(obj); + + if(grp) + { + onAddGroupSelected_callback( grp ); + return true; + } + return false; +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::syncSelection() +{ + // for each visible item check to see if it is on the mSelected list. + // if it is then make sure that it is on the mSelectedItems list as well. + for (S32 i = 0; i < mVisibleItems.size(); i++) + { + for (S32 j = 0; j < mSelected.size(); j++) + { + if (mVisibleItems[i]->mId == mSelected[j]) + { + // check to see if it is on the visible items list. + bool addToSelectedItems = true; + for (S32 k = 0; k < mSelectedItems.size(); k++) + { + if (mSelected[j] == mSelectedItems[k]->mId) + { + // don't add it + addToSelectedItems = false; + } + } + if (addToSelectedItems) + { + mVisibleItems[i]->mState.set(Item::Selected, true); + mSelectedItems.push_front(mVisibleItems[i]); + break; + } + } + else if (mVisibleItems[i]->isInspectorData()) + { + if(mCompareToObjectID) + { + if (mVisibleItems[i]->getObject() && mVisibleItems[i]->getObject()->getId() == mSelected[j]) + { + // check to see if it is on the visible items list. + bool addToSelectedItems = true; + for (S32 k = 0; k < mSelectedItems.size(); k++) + { + if (mSelectedItems[k]->isInspectorData() && mSelectedItems[k]->getObject() ) + { + if (mSelected[j] == mSelectedItems[k]->getObject()->getId()) + { + // don't add it + addToSelectedItems = false; + } + } + else + { + if (mSelected[j] == mSelectedItems[k]->mId) + { + // don't add it + addToSelectedItems = false; + } + } + } + if (addToSelectedItems) + { + mVisibleItems[i]->mState.set(Item::Selected, true); + mSelectedItems.push_front(mVisibleItems[i]); + break; + } + } + } + } + + } + + } +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::removeSelection( S32 itemOrObjectId ) +{ + if (mDebug) + Con::printf( "removeSelection %i", itemOrObjectId ); + + Item* item = _findItemByAmbiguousId( itemOrObjectId, false ); + if (!item) + return; + + // Make sure we have a true item ID even if we started with + // an object ID. + S32 itemId = item->getID(); + + S32 objectId = -1; + if ( item->isInspectorData() && item->getObject() ) + objectId = item->getObject()->getId(); + + // Remove from vector of selected object ids if it exists there + if ( objectId != -1 ) + { + for ( S32 i = 0; i < mSelected.size(); i++ ) + { + if ( objectId == mSelected[i] || itemId == mSelected[i] ) + { + mSelected.erase( i ); + break; + } + } + } + else + { + for ( S32 i = 0; i < mSelected.size(); i++ ) + { + if ( itemId == mSelected[i] ) + { + mSelected.erase( i ); + break; + } + } + } + + item->mState.set(Item::Selected, false); + + // Remove from vector of selected items if it exists there. + for ( S32 i = 0; i < mSelectedItems.size(); i++ ) + { + if ( mSelectedItems[i] == item ) + { + mSelectedItems.erase( i ); + break; + } + } + + // Callback. + onRemoveSelection( item ); +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::addSelection( S32 itemOrObjectId, bool update, bool isLastSelection ) +{ + if (mDebug) + Con::printf( "addSelection %i", itemOrObjectId ); + + Item* item = _findItemByAmbiguousId( itemOrObjectId ); + + // Add Item? + if ( !item || isSelected( item ) || !canAddSelection( item ) ) + { + // Nope. + return; + } + + const S32 itemId = item->getID(); + + // Ok, we have an item to select which isn't already selected.... + + // Do we want to allow more than one selected item? + if( !mMultipleSelections ) + clearSelection(); + + // Add this object id to the vector of selected objectIds + // if it is not already. + bool foundMatch = false; + for ( S32 i = 0; i < mSelected.size(); i++) + { + if ( mSelected[i] == itemId ) + foundMatch = true; + } + + if ( !foundMatch ) + mSelected.push_front(itemId); + + item->mState.set(Item::Selected, true); + + if( mSelected.size() == 1 ) + { + onItemSelected( item ); + } + + // Callback Start + // Set and add the selection to the selected items group + item->mState.set(Item::Selected, true); + mSelectedItems.push_front(item); + + if ( item->isInspectorData() && + item->getObject() ) + { + SimObject *obj = item->getObject(); + + onAddSelection_callback( obj->getId(), isLastSelection ); + } + else + { + onAddSelection_callback( item->mId, isLastSelection ); + } + // Callback end + + mFlags.set( RebuildVisible ); + if( update ) + { + // Also make it so we can see it if we didn't already. + scrollVisible( item ); + } +} + + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::onItemSelected( Item *item ) +{ + mSelectedItem = item->getID(); + + if (item->isInspectorData()) + { + SimObject* object = item->getObject(); + if( object ) + onSelect_callback( object->getId() ); + if( !item->isParent() && object ) + onInspect_callback( object->getId() ); + } + else + { + onSelect_callback( item->mId ); + if( !item->isParent() ) + onInspect_callback( item->mId ); + } +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::onRemoveSelection( Item *item ) +{ + S32 id = item->mId; + + if( item->isInspectorData() && + item->getObject() ) + { + SimObject* obj = item->getObject(); + id = obj->getId(); + //obj->setSelected( false ); + } + + if( isMethod( "onRemoveSelection" ) ) + onRemoveSelection_callback( id ); + else + onUnselect_callback( id ); +} + +//----------------------------------------------------------------------------- + +bool GuiTreeViewCtrl::setItemSelected(S32 itemId, bool select) +{ + Item * item = getItem(itemId); + if( isSelected( item ) == select ) + return true; + + if (select) + { + if (mDebug) Con::printf("setItemSelected called true"); + + mSelected.push_front(itemId); + } + else + { + if (mDebug) Con::printf("setItemSelected called false"); + + // remove it from the mSelected list + for (S32 j = 0; j isInspectorData()) + { + if (item->getObject()) + { + if(item->getObject()->getId() == mSelected[j]) + { + mSelected.erase(j); + break; + } + } + else + { + // Zombie, kill it! + mSelected.erase(j); + j--; + break; + } + } + } + + if (mSelected[j] == itemId) + { + mSelected.erase(j); + break; + } + } + } + + if(!item) + { + // maybe what we were passed wasn't an item id but an object id. + for (S32 i = 0; i isInspectorData()) + { + if (mItems[i]->getObject()) + { + if(mItems[i]->getObject()->getId() == itemId) + { + item = mItems[i]; + break; + } + } + else + { + // It's a zombie, blast it. + mItems.erase(i); + i--; + } + } + } + } + + if (!item) + { + //Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::setItemSelected: invalid item id! Perhaps it isn't visible yet."); + return(false); + } + } + + mFlags.set( RebuildVisible ); + + if(select) + { + addSelection( item->mId ); + onItemSelected( item ); + } + else + { + // deselect the item, if it's present. + item->mState.set(Item::Selected, false); + + if (item->isInspectorData() && item->getObject()) + onUnselect_callback( item->getObject()->getId() ); + else + onUnselect_callback( item->mId ); + + // remove it from the selected items list + for (S32 i = 0; i < mSelectedItems.size(); i++) + { + if (mSelectedItems[i] == item) + { + mSelectedItems.erase(i); + break; + } + } + } + + setUpdate(); + return(true); +} + +//----------------------------------------------------------------------------- + +// Given an item's index in the selection list, return its itemId +S32 GuiTreeViewCtrl::getSelectedItem(S32 index) +{ + if(index >= 0 && index < getSelectedItemsCount()) + { + return mSelectedItems[index]->mId; + } + + return -1; +} + +//----------------------------------------------------------------------------- + +bool GuiTreeViewCtrl::setItemExpanded(S32 itemId, bool expand) +{ + Item * item = getItem(itemId); + if(!item) + { + Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::setItemExpanded: invalid item id!"); + return(false); + } + + if(item->isExpanded() == expand) + return(true); + + // expand parents + if(expand) + { + while(item) + { + if(item->mState.test(Item::VirtualParent)) + onVirtualParentExpand(item); + + item->setExpanded(true); + item = item->mParent; + } + } + else + { + if(item->mState.test(Item::VirtualParent)) + onVirtualParentCollapse(item); + + item->setExpanded(false); + } + return(true); +} + + +//----------------------------------------------------------------------------- + +bool GuiTreeViewCtrl::setItemValue(S32 itemId, StringTableEntry Value) +{ + Item * item = getItem(itemId); + if(!item) + { + Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::setItemValue: invalid item id!"); + return(false); + } + + item->setValue( ( Value ) ? Value : "" ); + + return(true); +} + +//----------------------------------------------------------------------------- + +const char * GuiTreeViewCtrl::getItemText(S32 itemId) +{ + Item * item = getItem(itemId); + if(!item) + { + Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::getItemText: invalid item id!"); + return(""); + } + + return(item->getText() ? item->getText() : ""); +} + +//----------------------------------------------------------------------------- + +const char * GuiTreeViewCtrl::getItemValue(S32 itemId) +{ + Item * item = getItem(itemId); + if(!item) + { + Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::getItemValue: invalid item id!"); + return(""); + } + + if(item->mState.test(Item::InspectorData)) + { + // If it's InspectorData, we let people use this call to get an object reference. + return item->mInspectorInfo.mObject->getIdString(); + } + else + { + // Just return the script value... + return item->getValue(); + } +} + +//----------------------------------------------------------------------------- + +bool GuiTreeViewCtrl::editItem( S32 itemId, const char* newText, const char* newValue ) +{ + Item* item = getItem( itemId ); + if ( !item ) + { + Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::editItem: invalid item id: %d!", itemId); + return false; + } + + if ( item->mState.test(Item::InspectorData) ) + { + Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::editItem: item %d is inspector data and may not be modified!", itemId); + return false; + } + + item->setText( StringTable->insert( newText, true ) ); + item->setValue( StringTable->insert( newValue, true ) ); + + // Update the widths and such: + mFlags.set(RebuildVisible); + return true; +} + +//----------------------------------------------------------------------------- + +bool GuiTreeViewCtrl::markItem( S32 itemId, bool mark ) +{ + Item *item = getItem( itemId ); + if ( !item ) + { + Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::markItem: invalid item id: %d!", itemId); + return false; + } + + item->mState.set(Item::Marked, mark); + return true; +} + +//----------------------------------------------------------------------------- + +bool GuiTreeViewCtrl::isItemSelected( S32 itemId ) +{ + for( U32 i = 0, num = mSelectedItems.size(); i < num; ++ i ) + if( mSelectedItems[ i ]->mId == itemId ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::deleteSelection() +{ + onDeleteSelection_callback(); + + if (mSelectedItems.empty()) + { + for (S32 i = 0; i < mSelected.size(); i++) + { + S32 objectId = mSelected[i]; + + // find the object + SimObject* obj = Sim::findObject(objectId); + if ( !obj ) + continue; + + bool skipDelete = onDeleteObject_callback( obj ); + if ( !skipDelete ) + obj->deleteObject(); + } + } + else + { + Vector delSelection; + delSelection = mSelectedItems; + mSelectedItems.clear(); + while (!delSelection.empty()) + { + Item * item = delSelection.front(); + setItemSelected(item->mId,false); + if ( item->mParent ) + _deleteItem( item ); + + delSelection.pop_front(); + } + } + + mSelected.clear(); + mSelectedItems.clear(); + mSelectedItem = 0; + + onObjectDeleteCompleted_callback(); +} + +//------------------------------------------------------------------------------ + +// keyboard movement of items is restricted to just one item at a time +// if more than one item is selected then movement operations are not performed +bool GuiTreeViewCtrl::onKeyDown( const GuiEvent& event ) +{ + if ( !mVisible || !mActive || !mAwake ) + return false; + + // All the keyboard functionality requires a selected item, so if none exists... + + // Deal with enter and delete + if ( event.modifier == 0 ) + { + if ( event.keyCode == KEY_RETURN ) + { + execAltConsoleCallback(); + return true; + } + + if ( event.keyCode == KEY_DELETE && mDeleteObjectAllowed ) + { + // Don't delete the root! + if (mSelectedItems.empty()) + return true; + + //this may be fighting with the world editor delete + deleteSelection(); + return true; + } + + //call a generic bit of script that will let the subclass know that a key was pressed + onKeyDown_callback( event.modifier, event.keyCode ); + } + + // only do operations if only one item is selected + if ( mSelectedItems.empty() || (mSelectedItems.size() > 1)) + return false; + + Item* item = mSelectedItems.first(); + + if ( !item ) + return false; + + // The Alt key lets you move items around! + if ( mFlags.test(IsEditable) && event.modifier & SI_ALT ) + { + switch ( event.keyCode ) + { + case KEY_UP: + // Move us up. + if ( item->mPrevious ) + { + moveItemUp( item->mId ); + scrollVisible(item); + } + return true; + + case KEY_DOWN: + // Move the item under us up. + if ( item->mNext ) + { + moveItemUp( item->mNext->mId ); + scrollVisible(item); + } + return true; + + case KEY_LEFT: + if ( item->mParent ) + { + if ( item->mParent->mParent ) + { + // Ok, we have both an immediate parent, and a grandparent. + + // The goal of left-arrow alt is to become the child of our + // grandparent, ie, to become a sibling of our parent. + + // First, unlink item from its siblings. + if ( item->mPrevious ) + item->mPrevious->mNext = item->mNext; + else + item->mParent->mChild = item->mNext; + + if ( item->mNext ) + item->mNext->mPrevious = item->mPrevious; + + // Now, relink as the next sibling of our parent. + item->mPrevious = item->mParent; + item->mNext = item->mParent->mNext; + + // If there was already a next sibling, deal with that case. + if ( item->mNext ) + item->mNext->mPrevious = item; + item->mParent->mNext = item; + + // Snag the current parent set if any... + SimSet *parentSet = NULL; + + if(item->mParent->isInspectorData()) + parentSet = dynamic_cast(item->mParent->getObject()); + else + { + // parent is probably script data so we search up the tree for a + // set to put our object in + Item * temp = item->mParent; + while (!temp->isInspectorData()) + temp = temp->mParent; + // found a ancestor who is an inspectorData + if (temp->isInspectorData()) + parentSet = dynamic_cast(temp->getObject()); + else parentSet = NULL; + } + + // Get our active SimObject if any + SimObject *simObj = NULL; + if(item->isInspectorData()) + simObj = item->getObject(); + + // Remove from the old parentset... + if(simObj && parentSet) { + if (parentSet->size()>0) + { + SimObject *lastObject = parentSet->last(); + parentSet->removeObject(simObj); + parentSet->reOrder(lastObject); + } else + parentSet->removeObject(simObj); + } + + // And finally, update our item + item->mParent = item->mParent->mParent; + + // Snag the newparent set if any... + SimSet *newParentSet = NULL; + + if(item->mParent->isInspectorData()) + newParentSet = dynamic_cast(item->mParent->getObject()); + else + { + // parent is probably script data so we search up the tree for a + // set to put our object in + Item * temp = item->mParent; + while (!temp->isInspectorData()) + temp = temp->mParent; + // found a ancestor who is an inspectorData + if (temp->isInspectorData()) + newParentSet = dynamic_cast(temp->getObject()); + else newParentSet = NULL; + } + if(simObj && newParentSet) + { + + newParentSet->addObject(simObj); + Item * temp = item->mNext; + // item->mNext may be script, so find an inspector item to reorder with if any + + if (temp) { + do { + if (temp->isInspectorData()) + break; + temp = temp->mNext; + } while (temp); + if (temp && item->getObject() && temp->getObject()) //do we still have a item->mNext? If not then don't bother reordering + newParentSet->reOrder(item->getObject(), temp->getObject()); + } + + } else if (!simObj&&newParentSet) { + // our current item is script data. but it may have children who + // is inspector data who need an updated set + if (item->mChild) + inspectorSearch(item->mChild, item, parentSet, newParentSet); + + } + + // And update everything hurrah. + buildVisibleTree(); + scrollVisible(item); + } + } + return true; + + case KEY_RIGHT: + if ( item->mPrevious ) + { + // Make the item the last child of its previous sibling. + + // First, unlink from the current position in the list + item->mPrevious->mNext = item->mNext; + + if ( item->mNext ) + item->mNext->mPrevious = item->mPrevious; + + // Get the object we're poking with. + SimObject *simObj = NULL; + SimSet *parentSet = NULL; + if(item->isInspectorData()) + simObj = item->getObject(); + if(item->mParent->isInspectorData()) + parentSet = dynamic_cast(item->mParent->getObject()); + else { + // parent is probably script data so we search up the tree for a + // set to put our object in + Item * temp = item->mParent; + while (!temp->isInspectorData()) + temp = temp->mParent; + // found an ancestor who is an inspectorData + if (temp->isInspectorData()) + parentSet = dynamic_cast(temp->getObject()); + } + + // If appropriate, remove from the current SimSet. + if(parentSet && simObj) { + if (parentSet->size()>0) + { + SimObject *lastObject = parentSet->last(); + parentSet->removeObject(simObj); + parentSet->reOrder(lastObject); + } else + parentSet->removeObject(simObj); + } + + + // Now, make our previous sibling our parent... + item->mParent = item->mPrevious; + item->mNext = NULL; + + // And sink us down to the end of its siblings, if appropriate. + if ( item->mParent->mChild ) + { + Item* temp = item->mParent->mChild; + while ( temp->mNext ) + temp = temp->mNext; + + temp->mNext = item; + item->mPrevious = temp; + } + else + { + // only child... + item->mParent->mChild = item; + item->mPrevious = NULL; + } + + // Make sure the new parent is expanded: + if ( !item->mParent->mState.test( Item::Expanded ) ) + setItemExpanded( item->mParent->mId, true ); + + // Snag the new parent simset if any. + SimSet *newParentSet = NULL; + + // new parent might be script. so figure out what set we need to add it to. + if(item->mParent->isInspectorData()) + newParentSet = dynamic_cast(item->mParent->getObject()); + else + { + // parent is probably script data so we search up the tree for a + // set to put our object in + if (mDebug) Con::printf("oh nos my parent is script!"); + Item * temp = item->mParent; + while (!temp->isInspectorData()) + temp = temp->mParent; + // found a ancestor who is an inspectorData + if (temp->isInspectorData()) + newParentSet = dynamic_cast(temp->getObject()); + else newParentSet = NULL; + } + // Add the item's SimObject to the new parent simset, at the end. + if(newParentSet && simObj) + newParentSet->addObject(simObj); + else if (!simObj&&newParentSet&&parentSet) { + // our current item is script data. but it may have children who + // is inspector data who need an updated set + + if (item->mChild) { + inspectorSearch(item->mChild, item, parentSet, newParentSet); + } + + } + scrollVisible(item); + } + return true; + + default: + break; + } + } + + // Explorer-esque navigation... + switch( event.keyCode ) + { + case KEY_UP: + // Select previous visible item: + if ( item->mPrevious ) + { + item = item->mPrevious; + while ( item->isParent() && item->isExpanded() ) + { + item = item->mChild; + while ( item->mNext ) + item = item->mNext; + } + clearSelection(); + addSelection( item->mId ); + return true; + } + + // or select parent: + if ( item->mParent ) + { + clearSelection(); + addSelection( item->mParent->mId ); + return true; + } + + return false; + break; + + case KEY_DOWN: + // Selected child if it is visible: + if ( item->isParent() && item->isExpanded() ) + { + clearSelection(); + addSelection( item->mChild->mId ); + return true; + } + // or select next sibling (recursively): + do + { + if ( item->mNext ) + { + clearSelection(); + addSelection( item->mNext->mId ); + return true; + } + + item = item->mParent; + } while ( item ); + + return false; + break; + + case KEY_LEFT: + // Contract current menu: + if ( item->isExpanded() ) + { + setItemExpanded( item->mId, false ); + scrollVisible(item); + return true; + } + // or select parent: + if ( item->mParent ) + { + clearSelection(); + addSelection( item->mParent->mId ); + return true; + } + + return false; + break; + + case KEY_RIGHT: + // Expand selected item: + if ( item->isParent() ) + { + if ( !item->isExpanded() ) + { + setItemExpanded( item->mId, true ); + scrollVisible(item); + return true; + } + + // or select child: + clearSelection(); + addSelection( item->mChild->mId ); + return true; + } + + return false; + break; + + default: + break; + } + + // Not processed, so pass the event on: + return Parent::onKeyDown( event ); +} + +//------------------------------------------------------------------------------ + +// on mouse up look at the current item and check to see if it is valid +// to move the selected item(s) to it. +void GuiTreeViewCtrl::onMouseUp(const GuiEvent &event) +{ + if( !mActive || !mAwake || !mVisible ) + return; + + if( isMethod("onMouseUp") ) + { + BitSet32 hitFlags = 0; + Item* item; + + S32 hitItemId = -1; + if( _hitTest( event.mousePoint, item, hitFlags ) ) + hitItemId = item->mId; + + onMouseUp_callback( hitItemId, event.mouseClickCount ); + } + + mouseUnlock(); + + if ( mSelectedItems.empty()) + { + mDragMidPoint = NomDragMidPoint; + return; + } + + BitSet32 hitFlags = 0; + Item *item; + bool hitCheck = _hitTest( event.mousePoint, item, hitFlags ); + mRenamingItem = NULL; + + if( hitCheck ) + { + if ( event.mouseClickCount == 1 && !mMouseDragged && mPossibleRenameItem != NULL ) + { + if ( item == mPossibleRenameItem ) + showItemRenameCtrl( item ); + } + else // If mouseUp occurs on the same item as mouse down + { + bool wasSelected = isSelected( item ); + bool multiSelect = getSelectedItemsCount() > 1; + if( wasSelected && multiSelect && item == mTempItem ) + { + clearSelection(); + addSelection( item->mId ); + } + } + } + + mPossibleRenameItem = NULL; + + if (!mMouseDragged) + return; + + Item* newItem = NULL; + Item* newItem2 = NULL; + + if (mFlags.test(IsEditable)) + { + Parent::onMouseMove( event ); + + BitSet32 hitFlags = 0; + if( !_hitTest( event.mousePoint, newItem2, hitFlags ) ) + { + if( !mShowRoot ) + newItem2 = mRoot; + else + { + if( mDebug ) + Con::printf( "Nothing hit" ); + + mDragMidPoint = NomDragMidPoint; + return; + } + } + + newItem2->mState.clear(Item::MouseOverBmp | Item::MouseOverText ); + + // If the hit item is the visible root, make sure + // we don't allow dragging above. + + if( newItem2 == mRoot && mDragMidPoint == AbovemDragMidPoint ) + { + if( mDebug ) + Con::printf( "Rejecting to make child sibling of root" ); + + mDragMidPoint = NomDragMidPoint; + return; + } + + // if the newItem isn't in the mSelectedItemList then continue. + + Vector::iterator k; + for(k = mSelectedItems.begin(); k != mSelectedItems.end(); k++) + { + newItem = newItem2; + + if (*(k) == newItem) + { + mDragMidPoint = NomDragMidPoint; + return; + } + + Item * temp = *(k); + Item * grandpaTemp = newItem->mParent; + + // grandpa check, kick out if an item would be its own ancestor + while (grandpaTemp) + { + if (temp == grandpaTemp) + { + if (mDebug) + { + Con::printf("grandpa check"); + + if (temp->isInspectorData()) + Con::printf("temp's name: %s",temp->getObject()->getName()); + + if (grandpaTemp->isInspectorData()) + Con::printf("grandpa's name: %s",grandpaTemp->getObject()->getName()); + } + + mDragMidPoint = NomDragMidPoint; + return; + } + + grandpaTemp = grandpaTemp->mParent; + } + } + + // Notify script for undo. + + onBeginReparenting_callback(); + + // Reparent the items. + + for (S32 i = 0; i mState.clear(Item::MouseOverBmp | Item::MouseOverText ); + + // move the selected item to the newItem + Item* oldParent = item->mParent; + // Snag the current parent set if any for future reference + SimSet *parentSet = NULL; + + if(oldParent->isInspectorData()) + parentSet = dynamic_cast(oldParent->getObject()); + else + { + // parent is probably script data so we search up the tree for a + // set to put our object in + Item * temp = oldParent; + while (temp) + { + if (temp->isInspectorData()) + break; + temp = temp->mParent; + } + // found an ancestor who is an inspectorData + if (temp) + { + if (temp->isInspectorData()) + parentSet = dynamic_cast(temp->getObject()); + } + } + + // unlink from the current position in the list + unlinkItem(item); + + // update the parent's children + + // check if we an only child + if (item->mParent->mChild == item) + { + if (item->mNext) + item->mParent->mChild = item->mNext; + else + item->mParent->mChild = NULL; + } + + if (mDragMidPoint != NomDragMidPoint) + { + + //if it is below an expanded tree, place as last item in the tree + //if it is below a parent who isn't expanded put below it + + // position the item above or below another item + if (mDragMidPoint == AbovemDragMidPoint) + { + // easier to treat everything as "Below the mDragMidPoint" so make some adjustments + if (mDebug) Con::printf("adding item above mDragMidPoint"); + + // above the mid point of an item, so grab either the parent + // or the previous sibling + + // does the item have a previous sibling? + if (newItem->mPrevious) + { + newItem = newItem->mPrevious; + + if (mDebug) Con::printf("treating as if below an item that isn't expanded"); + + // otherwise add below that item as a sibling + item->mParent = newItem->mParent; + item->mPrevious = newItem; + item->mNext = newItem->mNext; + if (newItem->mNext) + newItem->mNext->mPrevious = item; + newItem->mNext = item; + } + else + { + if (mDebug) Con::printf("treating as if adding below the parent of the item"); + + // instead we add as the first item below the newItem's parent + item->mParent = newItem->mParent; + item->mNext = newItem; + item->mPrevious = NULL; + newItem->mPrevious = item; + item->mParent->mChild = item; + } + } + else if (mDragMidPoint == BelowmDragMidPoint) + { + if ((newItem->isParent())&&(newItem->isExpanded())) + { + if (mDebug) Con::printf("adding item to an expanded parent below the mDragMidPoint"); + + item->mParent = newItem; + + // then add the new item as a child + item->mNext = newItem->mChild; + if (newItem->mChild) + newItem->mChild->mPrevious = item; + item->mParent->mChild = item; + item->mPrevious = NULL; + } + else if ((!newItem->mNext)&&(newItem->mParent)&&(newItem->mParent->mParent)) + { + // add below it's parent. + if (mDebug) Con::printf("adding below a tree"); + + item->mParent = newItem->mParent->mParent; + item->mNext = newItem->mParent->mNext; + item->mPrevious = newItem->mParent; + + if (newItem->mParent->mNext) + newItem->mParent->mNext->mPrevious = item; + + newItem->mParent->mNext = item; + } + else + { + // adding below item not as a child + if (mDebug) Con::printf("adding item below the mDragMidPoint of an item"); + + item->mParent = newItem->mParent; + // otherwise the item is a sibling + if (newItem->mNext) + newItem->mNext->mPrevious = item; + item->mNext = newItem->mNext; + item->mPrevious = newItem; + newItem->mNext = item; + } + } + } + // if we're not allowed to add to items, then try to add to the parent of the hit item. + // if we are, just add to the item we hit. + else + { + if (mDebug) + { + if (item->isInspectorData() && item->getObject()) + Con::printf("Item: %i",item->getObject()->getId()); + if (newItem->isInspectorData() && newItem->getObject()) + Con::printf("Parent: %i",newItem->getObject()->getId()); + Con::printf("dragged onto an item"); + } + + // If the hit item is not a valid drag target, + // then try to add to the parent. + + if( !isValidDragTarget( newItem ) ) + { + // add to the item's parent. + if(!newItem->mParent || !newItem->mParent->isParent()) + { + if(mDebug) + Con::printf("could not find the parent of that item. dragging to an item is not allowed, kicking out."); + mDragMidPoint = NomDragMidPoint; + continue; + } + newItem = newItem->mParent; + } + + // new parent is the item in the current cell + item->mParent = newItem; + + // adjust children if any + if (newItem->mChild) + { + if (mDebug) Con::printf("not the first child"); + + // put it at the top of the list (easier to find if there are many children) + if (newItem->mChild) + newItem->mChild->mPrevious = item; + item->mNext = newItem->mChild; + newItem->mChild = item; + item->mPrevious = NULL; + } + else + { + if (mDebug) Con::printf("first child"); + + // only child + newItem->mChild = item; + item->mNext = NULL; + item->mPrevious = NULL; + } + } + + // expand the item we added to, if it isn't expanded already + if( !item->mParent->mState.test( Item::Expanded ) ) + setItemExpanded( item->mParent->mId, true ); + + //---------------------------------------------------------------- + // handle objects + + // Get our active SimObject if any + SimObject *simObj = NULL; + if(item->isInspectorData()) + { + simObj = item->getObject(); + } + + // Remove from the old parentset + if((simObj && parentSet)&&(oldParent != item->mParent)) + { + if (mDebug) Con::printf("removing item from old parentset"); + + // hack to get around the way removeObject takes the last item of the set + // and moves it into the place of the object we removed + if (parentSet->size()>0) + { + SimObject *lastObject = parentSet->last(); + parentSet->removeObject(simObj); + parentSet->reOrder(lastObject); + } + else + { + parentSet->removeObject(simObj); + } + } + + // Snag the newparent set if any... + SimSet *newParentSet = NULL; + + if(item->mParent->isInspectorData()) + { + if (mDebug) Con::printf("getting a new parent set"); + + SimObject * tmpObj = item->mParent->getObject(); + newParentSet = dynamic_cast(tmpObj); + } + else + { + // parent is probably script data so we search up the tree for a + // set to put our object in + if (mDebug) Con::printf("oh nos my parent is script!"); + + Item * temp = item->mParent; + while (temp) + { + if (temp->isInspectorData()) + break; + temp = temp->mParent; + } + + // found a ancestor who is an inspectorData + if (temp) + { + if (temp->isInspectorData()) + newParentSet = dynamic_cast(temp->getObject()); + } + else + { + newParentSet = NULL; + } + } + + if(simObj && newParentSet) + { + if (mDebug) Con::printf("simobj and new ParentSet"); + + if (oldParent != item->mParent) + newParentSet->addObject(simObj); + + //order the objects in the simset according to their + //order in the tree view control + if(!item->mNext) + { + if( item->mPrevious ) + { + //bring to the end of the set + SimObject *prevObject = item->mPrevious->getObject(); + if (prevObject && item->getObject()) + { + newParentSet->reOrder(item->getObject(), prevObject); + } + } + } + else + { + //reorder within the set + SimObject *nextObject = item->mNext->getObject(); + if(nextObject && item->getObject()) + { + newParentSet->reOrder(item->getObject(), nextObject); + } + } + } + else if (!simObj&&newParentSet) + { + // our current item is script data. but it may have children who + // is inspector data who need an updated set + if (mDebug) Con::printf("no simobj but new parentSet"); + if (item->mChild) + inspectorSearch(item->mChild, item, parentSet, newParentSet); + + } + else if (simObj&&!newParentSet) + { + if (mDebug) Con::printf("simobject and no new parent set"); + } + else + if (mDebug) Con::printf("no simobject and no new parent set"); + + // Notify script. + + if( item->isInspectorData() ) + onReparent_callback( + item->getObject()->getId(), + oldParent->getObject()->getId(), + item->mParent->getObject()->getId() + ); + else + onReparent_callback( + item->mId, + oldParent->mId, + item->mParent->mId + ); + } + + onEndReparenting_callback(); + + // And update everything. + scrollVisible(newItem); + + onDragDropped_callback(); + } + + mDragMidPoint = NomDragMidPoint; +} + +//------------------------------------------------------------------------------ + +void GuiTreeViewCtrl::onMouseDragged(const GuiEvent &event) +{ + if( mDragStartInSelection ) + onMouseDragged_callback(); + + if(!mSupportMouseDragging) + return; + + if( !mActive || !mAwake || !mVisible ) + return; + + if (mSelectedItems.size() == 0) + return; + + // Give us a little delta before we actually start a mouse drag so that + // if the user moves the mouse a little while clicking, he/she does not + // accidentally trigger a drag. + + if( mFabs( ( mMouseDownPoint - event.mousePoint ).len() ) <= 4.f ) + return; + + Point2I pt = globalToLocalCoord(event.mousePoint); + Parent::onMouseMove(event); + mouseLock(); + mMouseDragged = true; + + // If the drag is outside of our visible area, + // start scrolling. + + GuiScrollCtrl* scrollCtrl = dynamic_cast< GuiScrollCtrl* >( getParent() ); + if( scrollCtrl && !scrollCtrl->isPointVisible( pt ) ) + { + S32 widthDelta = 0; + S32 heightDelta = 0; + + if( pt.x < scrollCtrl->getChildRelPos().x ) + widthDelta = pt.x - scrollCtrl->getChildRelPos().x; + else if( pt.x > scrollCtrl->getChildRelPos().x + scrollCtrl->getContentExtent().x ) + widthDelta = pt.x - scrollCtrl->getChildRelPos().x - scrollCtrl->getContentExtent().x; + + if( pt.y < scrollCtrl->getChildRelPos().y ) + heightDelta = pt.y - scrollCtrl->getChildRelPos().y; + else if( pt.y > scrollCtrl->getChildRelPos().y + scrollCtrl->getContentExtent().y ) + heightDelta = pt.y - scrollCtrl->getChildRelPos().y - scrollCtrl->getContentExtent().y; + + const F32 SCROLL_RATIO = 0.5f; + scrollCtrl->scrollDelta( S32( F32( widthDelta ) * SCROLL_RATIO ), S32( F32( heightDelta ) * SCROLL_RATIO ) ); + } + + // whats our mDragMidPoint? + mCurrentDragCell = mMouseOverCell.y; + S32 midpCell = mCurrentDragCell * mItemHeight + (mItemHeight/2); + S32 currentY = pt.y; + S32 yDiff = currentY-midpCell; + S32 variance = (mItemHeight/5); + if( mPreviousDragCell >= 0 && mPreviousDragCell < mVisibleItems.size() ) + mVisibleItems[mPreviousDragCell]->mState.clear( Item::MouseOverBmp | Item::MouseOverText | Item::MouseOverIcon ); + + bool hoverItem = false; + + if (mAbs(yDiff) <= variance) + { + mDragMidPoint = NomDragMidPoint; + // highlight the current item + // hittest to detect whether we are on an item + // ganked from onMouseMouse + + // used for tracking what our last cell was so we can clear it. + mPreviousDragCell = mCurrentDragCell; + if (mCurrentDragCell >= 0) + { + + Item* item = NULL; + BitSet32 hitFlags = 0; + if ( !_hitTest( event.mousePoint, item, hitFlags ) ) + return; + + // If the item is a valid drag target, activate the item + // highlighting. + + if( isValidDragTarget( item ) ) + { + hoverItem = true; + + if ( hitFlags.test( OnImage ) ) + item->mState.set( Item::MouseOverBmp ); + + if ( hitFlags.test( OnText ) ) + item->mState.set( Item::MouseOverText ); + + if ( hitFlags.test( OnIcon ) ) + item->mState.set( Item::MouseOverIcon ); + + // Always redraw the entire mouse over item, since we are distinguishing + // between the bitmap and the text: + setUpdateRegion( Point2I( mMouseOverCell.x * mCellSize.x, mMouseOverCell.y * mCellSize.y ), mCellSize ); + } + } + } + + if ( !hoverItem ) + { + //above or below an item? + if (yDiff < 0) + mDragMidPoint = AbovemDragMidPoint; + else + mDragMidPoint = BelowmDragMidPoint; + } +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::onMiddleMouseDown(const GuiEvent & event) +{ + //for debugging items + if (mDebug) { + Item* item; + BitSet32 hitFlags = 0; + _hitTest( event.mousePoint, item, hitFlags ); + Con::printf("debugging %d", item->mId); + Point2I pt = globalToLocalCoord(event.mousePoint); + if (item->isInspectorData() && item->getObject()) { + Con::printf("object data:"); + Con::printf("name:%s",item->getObject()->getName()); + Con::printf("className:%s",item->getObject()->getClassName()); + } + Con::printf("contents of mSelectedItems:"); + for(S32 i = 0; i < mSelectedItems.size(); i++) { + if (mSelectedItems[i]->isInspectorData()) { + Con::printf("%d",mSelectedItems[i]->getObject()->getId()); + } else + Con::printf("wtf %d", mSelectedItems[i]); + } + Con::printf("contents of mSelected"); + for (S32 j = 0; j < mSelected.size(); j++) { + Con::printf("%d", mSelected[j]); + } + S32 mCurrentDragCell = mMouseOverCell.y; + S32 midpCell = (mCurrentDragCell) * mItemHeight + (mItemHeight/2); + S32 currentY = pt.y; + S32 yDiff = currentY-midpCell; + Con::printf("cell info: (%d,%d) mCurrentDragCell=%d est=(%d,%d,%d) ydiff=%d",pt.x,pt.y,mCurrentDragCell,mCurrentDragCell*mItemHeight, midpCell, (mCurrentDragCell+1)*mItemHeight,yDiff); + } +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::onMouseDown(const GuiEvent & event) +{ + if( !mActive || !mAwake || !mVisible ) + { + Parent::onMouseDown(event); + return; + } + if ( mProfile->mCanKeyFocus ) + setFirstResponder(); + + Item * item = 0; + BitSet32 hitFlags; + mDragMidPoint = NomDragMidPoint; + + mMouseDragged = false; + mMouseDownPoint = event.mousePoint; + + // + if(!_hitTest(event.mousePoint, item, hitFlags)) + return; + + mPossibleRenameItem = NULL; + mRenamingItem = NULL; + mTempItem = NULL; + + // + if( event.modifier & SI_MULTISELECT ) + { + bool selectFlag = item->mState.test(Item::Selected); + if (selectFlag == true) + { + // already selected, so unselect it and remove it + removeSelection(item->mId); + } + else + { + addSelection(item->mId); + } + } + else if( event.modifier & SI_RANGESELECT && mMultipleSelections ) + { + // is something already selected? + S32 firstSelectedIndex = 0; + Item * firstItem = NULL; + if (!mSelectedItems.empty()) + { + firstItem = mSelectedItems.front(); + for (S32 i = 0; i < mVisibleItems.size();i++) + { + if (mVisibleItems[i] == mSelectedItems.front()) + { + firstSelectedIndex = i; + break; + } + } + S32 mCurrentDragCell = mMouseOverCell.y; + if (mVisibleItems[firstSelectedIndex] != firstItem ) + { + /* + Con::printf("something isn't right..."); + if (mVisibleItems[firstSelectedIndex]->isInspectorData()) + Con::printf("visibleItem %s",mVisibleItems[firstSelectedIndex]->getObject()->getName()); + if (firstItem->isInspectorData()) + Con::printf("firstItem %s",firstItem->getObject()->getName()); + */ + } + else + { + // select the cells + onAddMultipleSelectionBegin_callback(); + if ((mCurrentDragCell) < firstSelectedIndex) + { + //select up + for (S32 j = (mCurrentDragCell); j < firstSelectedIndex; j++) + { + if( j != (firstSelectedIndex - 1) ) + addSelection(mVisibleItems[j]->mId, false, false); + else + addSelection(mVisibleItems[j]->mId, false); + } + } + else + { + // select down + for (S32 j = firstSelectedIndex+1; j < (mCurrentDragCell+1); j++) + { + if( j != mCurrentDragCell ) + addSelection(mVisibleItems[j]->mId, false, false); + else + addSelection(mVisibleItems[j]->mId, false); + } + } + + // Scroll to view the last selected cell. + scrollVisible( mVisibleItems[mCurrentDragCell] ); + + onAddMultipleSelectionEnd_callback(); + } + } + } + else if ( event.modifier & SI_PRIMARY_ALT ) + { + if ( item->isInspectorData() && item->getObject() ) + setAddGroup(item->getObject()); + } + else if ( !hitFlags.test(OnImage) ) + { + mTempItem = item; + + bool wasSelected = isSelected( item ); + bool multiSelect = getSelectedItemsCount() > 1; + + if( !wasSelected || !multiSelect ) + { + if ( mClearAllOnSingleSelection ) + clearSelection(); + + if ( !wasSelected || mClearAllOnSingleSelection ) + addSelection( item->mId ); + + if ( wasSelected && + !multiSelect && + mCanRenameObjects && + hitFlags.test(OnText) && + mFlags.test(IsEditable) && + item->isInspectorData() && + item->getObject() && + item->getObject()->isNameChangeAllowed() && + item != mRoot && + event.mouseClickCount == 1 ) + { + mPossibleRenameItem = item; + + if ( isMethod( "canRenameObject" ) ) + { + if( canRenameObject_callback( item->getObject() ) ) + mPossibleRenameItem = NULL; + } + } + } + + } + + if ( ( hitFlags.test( OnText ) || hitFlags.test( OnIcon ) ) && + event.mouseClickCount > 1 ) + execAltConsoleCallback(); + + // For dragging, note if hit is in selection. + + mDragStartInSelection = isItemSelected( item->mId ); + + if(!item->isParent()) + return; + + // + if ( mFullRowSelect || hitFlags.test( OnImage ) ) + { + item->setExpanded(!item->isExpanded()); + if( !item->isInspectorData() && item->mState.test(Item::VirtualParent) ) + onVirtualParentExpand(item); + + mFlags.set( RebuildVisible ); + scrollVisible(item); + } +} + +//------------------------------------------------------------------------------ + +void GuiTreeViewCtrl::onMouseMove( const GuiEvent &event ) +{ + if ( mMouseOverCell.y >= 0 && mVisibleItems.size() > mMouseOverCell.y) + mVisibleItems[mMouseOverCell.y]->mState.clear( Item::MouseOverBmp | Item::MouseOverText | Item::MouseOverIcon); + + Parent::onMouseMove( event ); + + if ( mMouseOverCell.y >= 0 ) + { + Item* item = NULL; + BitSet32 hitFlags = 0; + if ( !_hitTest( event.mousePoint, item, hitFlags ) ) + return; + + if ( hitFlags.test( OnImage ) ) + item->mState.set( Item::MouseOverBmp ); + + if ( hitFlags.test( OnText ) ) + item->mState.set( Item::MouseOverText ); + + if ( hitFlags.test( OnIcon ) ) + item->mState.set( Item::MouseOverIcon ); + + // Always redraw the entire mouse over item, since we are distinguishing + // between the bitmap and the text: + setUpdateRegion( Point2I( mMouseOverCell.x * mCellSize.x, mMouseOverCell.y * mCellSize.y ), mCellSize ); + } +} + +//------------------------------------------------------------------------------ + +void GuiTreeViewCtrl::onMouseEnter( const GuiEvent &event ) +{ + Parent::onMouseEnter( event ); + onMouseMove( event ); +} + +//------------------------------------------------------------------------------ + +void GuiTreeViewCtrl::onMouseLeave( const GuiEvent &event ) +{ + if ( mMouseOverCell.y >= 0 && mVisibleItems.size() > mMouseOverCell.y) + mVisibleItems[mMouseOverCell.y]->mState.clear( Item::MouseOverBmp | Item::MouseOverText | Item::MouseOverIcon ); + + Parent::onMouseLeave( event ); +} + +//------------------------------------------------------------------------------ + +void GuiTreeViewCtrl::onRightMouseDown(const GuiEvent & event) +{ + if(!mActive) + { + Parent::onRightMouseDown(event); + return; + } + + Item * item = NULL; + BitSet32 hitFlags; + + // + if(!_hitTest(event.mousePoint, item, hitFlags)) + return; + + // + + if (item->isInspectorData() && item->getObject()) + onRightMouseDown_callback( item->mId, event.mousePoint, item->getObject() ); + else + onRightMouseDown_callback( item->mId, event.mousePoint ); +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::onRightMouseUp(const GuiEvent & event) +{ + Item *item = NULL; + BitSet32 hitFlags; + + if ( !_hitTest( event.mousePoint, item, hitFlags ) ) + return; + + if ( hitFlags.test( OnText ) || hitFlags.test( OnIcon ) ) + { + if ( !isItemSelected( item->getID() ) ) + { + clearSelection(); + addSelection( item->getID() ); + } + + if (item->isInspectorData() && item->getObject()) + onRightMouseUp_callback( item->mId, event.mousePoint, item->getObject() ); + else + onRightMouseUp_callback( item->mId, event.mousePoint ); + } + else + { + clearSelection(); + } + + Parent::onRightMouseUp(event); +} + +//------------------------------------------------------------------------------ + +void GuiTreeViewCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + if ( !mRenamingItem && mRenameCtrl ) + { + mRenameCtrl->deleteObject(); + mRenameCtrl = NULL; + } + + // Get all our contents drawn! + Parent::onRender(offset,updateRect); + + // Deal with drawing the drag & drop line, if any... + GFX->setClipRect(updateRect); + + // only do it if we have a mDragMidPoint + if (mDragMidPoint == NomDragMidPoint || !mSupportMouseDragging ) + return; + + ColorF greyLine(0.5,0.5,0.5,1); + Point2F squarePt; + + // CodeReview: LineWidth is not supported in Direct3D. This is lame. [5/10/2007 Pat] + // draw mDragMidPoint lines with a diamond + if (mDragMidPoint == AbovemDragMidPoint) + { + S32 tempY = mItemHeight*mCurrentDragCell+offset.y ; + squarePt.y = (F32)tempY; + squarePt.x = 125.f+offset.x; + GFX->getDrawUtil()->drawLine(0+offset.x, tempY, 250+offset.x, tempY,greyLine); + GFX->getDrawUtil()->draw2DSquare(squarePt, 6, 90 ); + + } + if (mDragMidPoint == BelowmDragMidPoint) + { + S32 tempY2 = mItemHeight*(mCurrentDragCell+1) +offset.y; + squarePt.y = (F32)tempY2; + squarePt.x = 125.f+offset.x; + GFX->getDrawUtil()->drawLine(0+offset.x, tempY2, 250+offset.x, tempY2,greyLine); + GFX->getDrawUtil()->draw2DSquare(squarePt,6, 90 ); + } +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::onRenderCell(Point2I offset, Point2I cell, bool, bool ) +{ + if( !mVisibleItems.size() ) + return; + + // Do some sanity checking and data retrieval. + AssertFatal(cell.y < mVisibleItems.size(), "GuiTreeViewCtrl::onRenderCell: invalid cell"); + Item * item = mVisibleItems[cell.y]; + + // If there's no object, deal with it. + if(item->isInspectorData()) + if(!item->getObject()) + return; + + RectI drawRect( offset, mCellSize ); + GFXDrawUtil *drawer = GFX->getDrawUtil(); + drawer->clearBitmapModulation(); + + FrameAllocatorMarker txtBuff; + + // Ok, we have the item. There are a few possibilities at this point: + // - We need to draw inheritance lines and a treeview-chosen icon + // OR + // - We have to draw an item-dependent icon + // - If we're mouseover, we have to highlight it. + // + // - We have to draw the text for the item + // - Taking into account various mouseover states + // - Taking into account the value (set or not) + // - If it's an inspector data, we have to do some custom rendering + // - ADDED: If it is being renamed, we also have custom rendering. + + // Ok, first draw the tab and icon. + + // Do we draw the tree lines? + if( mFlags.test(ShowTreeLines) ) + { + drawRect.point.x += ( mTabSize * item->mTabLevel ); + Item* parent = item->mParent; + for ( S32 i = item->mTabLevel; ( parent && i > 0 ); i-- ) + { + drawRect.point.x -= mTabSize; + if ( parent->mNext ) + drawer->drawBitmapSR( mProfile->mTextureObject, drawRect.point, mProfile->mBitmapArrayRects[BmpLine] ); + + parent = parent->mParent; + } + } + + // Now, the icon... + drawRect.point.x = offset.x + mTabSize * item->mTabLevel; + + // First, draw the rollover glow, if it's an inner node. + if ( item->isParent() && item->mState.test( Item::MouseOverBmp ) ) + drawer->drawBitmapSR( mProfile->mTextureObject, drawRect.point, mProfile->mBitmapArrayRects[BmpGlow] ); + + // Now, do we draw a treeview-selected item or an item dependent one? + S32 newOffset = 0; // This is stored so we can render glow, then update render pos. + + S32 bitmap = 0; + + // Ok, draw the treeview lines as appropriate. + + bool drawBitmap = true; + if ( !item->isParent() ) + { + if( mFlags.test( ShowTreeLines ) ) + { + if( ( item->mNext && item->mPrevious ) + || ( item->mNext && item->mParent && ( !_isRootLevelItem( item ) || mShowRoot ) ) ) + bitmap = BmpChild; + else if( item->mNext && ( !item->mParent || !mShowRoot ) ) + bitmap = BmpFirstChild; + else if( item->mPrevious || ( item->mParent && !_isRootLevelItem( item ) ) ) + bitmap = BmpLastChild; + else + drawBitmap = false; + } + else + drawBitmap = false; + } + else + { + bitmap = item->isExpanded() ? BmpExp : BmpCon; + + if( mFlags.test( ShowTreeLines ) ) + { + // Shift indices to show versions with tree lines. + + if ( item->mParent || item->mPrevious ) + bitmap += ( item->mNext ? 3 : 2 ); + else + bitmap += ( item->mNext ? 1 : 0 ); + } + } + + if( ( bitmap >= 0 ) && ( bitmap < mProfile->mBitmapArrayRects.size() ) ) + { + if( drawBitmap ) + drawer->drawBitmapSR( mProfile->mTextureObject, drawRect.point, mProfile->mBitmapArrayRects[bitmap] ); + newOffset = mProfile->mBitmapArrayRects[bitmap].extent.x; + } + + if(item->isInspectorData()) + { + // draw lock icon if need be + S32 icon = Lock1; + S32 icon2 = Hidden; + + if (item->getObject() && item->getObject()->isLocked()) + { + if (mIconTable[icon]) + { + //drawRect.point.x = offset.x + mTabSize * item->mTabLevel + mIconTable[icon].getWidth(); + drawRect.point.x += mIconTable[icon].getWidth(); + drawer->drawBitmap( mIconTable[icon], drawRect.point ); + } + } + + if (item->getObject() && item->getObject()->isHidden()) + { + if (mIconTable[icon2]) + { + //drawRect.point.x = offset.x + mTabSize * item->mTabLevel + mIconTable[icon].getWidth(); + drawRect.point.x += mIconTable[icon2].getWidth(); + drawer->drawBitmap( mIconTable[icon2], drawRect.point ); + } + } + + SimObject * pObject = item->getObject(); + SimGroup * pGroup = ( pObject == NULL ) ? NULL : dynamic_cast( pObject ); + + // If this item is a VirtualParent we can use the generic SimGroup123 icons. + // However if there is already an icon in the EditorIconRegistry for this + // exact class (not counting parent class icons) we want to use that instead. + bool hasClassIcon = gEditorIcons.hasIconNoRecurse( pObject ); + + // draw the icon associated with the item + if ( !hasClassIcon && item->mState.test(Item::VirtualParent)) + { + if ( pGroup != NULL) + { + if (item->isExpanded()) + item->mIcon = SimGroup1; + else + item->mIcon = SimGroup2; + } + else + item->mIcon = SimGroup2; + } + + if ( !hasClassIcon && item->mState.test(Item::Marked)) + { + if (item->isInspectorData()) + { + if ( pGroup != NULL ) + { + if (item->isExpanded()) + item->mIcon = SimGroup3; + else + item->mIcon = SimGroup4; + } + } + } + + GFXTexHandle iconHandle; + + if ( ( item->mIcon != -1 ) && mIconTable[item->mIcon] ) + iconHandle = mIconTable[item->mIcon]; +#ifdef TORQUE_TOOLS + else + iconHandle = gEditorIcons.findIcon( item->getObject() ); +#endif + + if ( iconHandle.isValid() ) + { + S32 iconHeight = (mItemHeight - iconHandle.getHeight()) / 2; + S32 oldHeight = drawRect.point.y; + if(iconHeight > 0) + drawRect.point.y += iconHeight; + drawRect.point.x += iconHandle.getWidth(); + drawer->drawBitmap( iconHandle, drawRect.point ); + drawRect.point.y = oldHeight; + } + } + else + { + S32 icon = item->isExpanded() ? item->mScriptInfo.mExpandedImage : item->mScriptInfo.mNormalImage; + if ( icon ) + { + if (mIconTable[icon]) + { + S32 iconHeight = (mItemHeight - mIconTable[icon].getHeight()) / 2; + S32 oldHeight = drawRect.point.y; + if(iconHeight > 0) + drawRect.point.y += iconHeight; + drawRect.point.x += mIconTable[icon].getWidth(); + drawer->drawBitmap( mIconTable[icon], drawRect.point ); + drawRect.point.y = oldHeight; + } + } + } + + // Ok, update offset so we can render some text! + drawRect.point.x += newOffset; + + // Ok, now we're off to rendering the actual data for the treeview item. + + U32 bufLen = 1024; //item->mDataRenderWidth + 1; + char *displayText = (char *)txtBuff.alloc(bufLen); + displayText[bufLen-1] = 0; + item->getDisplayText(bufLen, displayText); + + // Draw the rollover/selected bitmap, if one was specified. + drawRect.extent.x = mProfile->mFont->getStrWidth( displayText ) + ( 2 * mTextOffset ); + if ( item->mState.test( Item::Selected ) && mTexSelected ) + drawer->drawBitmapStretch( mTexSelected, drawRect ); + else if ( item->mState.test( Item::MouseOverText ) && mTexRollover ) + drawer->drawBitmapStretch( mTexRollover, drawRect ); + + // Offset a bit so as to space text properly. + drawRect.point.x += mTextOffset; + + // Determine what color the font should be. + ColorI fontColor; + + fontColor = item->mState.test( Item::Selected ) ? mProfile->mFontColorSEL : + ( item->mState.test( Item::MouseOverText ) ? mProfile->mFontColorHL : mProfile->mFontColor ); + + if (item->mState.test(Item::Selected)) + { + drawer->drawRectFill(drawRect, mProfile->mFillColorSEL); + } + else if (item->mState.test(Item::MouseOverText)) + { + drawer->drawRectFill(drawRect, mProfile->mFillColorHL); + } + + if( item->mState.test(Item::MouseOverText) ) + { + fontColor = mProfile->mFontColorHL; + } + + drawer->setBitmapModulation( fontColor ); + + // Center the text horizontally. + S32 height = (mItemHeight - mProfile->mFont->getHeight()) / 2; + + if(height > 0) + drawRect.point.y += height; + + // JDD - offset by two pixels or so to keep the text from rendering RIGHT ONTOP of the outline + drawRect.point.x += 2; + + drawer->drawText( mProfile->mFont, drawRect.point, displayText, mProfile->mFontColors ); + + if ( mRenamingItem == item && mRenameCtrl ) + { + Point2I ctrPos = globalToLocalCoord( drawRect.point ); + ctrPos.y -= height; + ctrPos.x -= 2; + + Point2I ctrExtent( getWidth() - ctrPos.x, drawRect.extent.y ); + + mRenameCtrl->setPosition( ctrPos ); + mRenameCtrl->setExtent( ctrExtent ); + mRenameCtrl->setVisible( true ); + } +} + +//------------------------------------------------------------------------------ + +bool GuiTreeViewCtrl::renderTooltip( const Point2I &hoverPos, const Point2I& cursorPos, const char* tipText ) +{ + Item* item; + BitSet32 flags = 0; + char buf[ 2048 ]; + if( _hitTest( cursorPos, item, flags ) && (!item->mTooltip.isEmpty() || mUseInspectorTooltips) ) + { + bool render = true; + + if( mTooltipOnWidthOnly && !item->hasObjectBasedTooltip() ) + { + // Only render tooltip if the item's text is cut off with its + // parent scroll control, unless there is custom object-based + // tooltip information. + GuiScrollCtrl *pScrollParent = dynamic_cast( getParent() ); + if ( pScrollParent ) + { + Point2I textStart; + Point2I textExt; + + const Point2I pos = globalToLocalCoord(cursorPos); + textStart.y = pos.y / mCellSize.y; + textStart.y *= mCellSize.y; + + // The following is taken from _hitTest() + textStart.x = mTabSize * item->mTabLevel; + S32 image = BmpChild; + if((image >= 0) && (image < mProfile->mBitmapArrayRects.size())) + textStart.x += mProfile->mBitmapArrayRects[image].extent.x; + textStart.x += mTextOffset; + + textStart.x += getInspectorItemIconsWidth( item ); + + FrameAllocatorMarker txtAlloc; + U32 bufLen = item->getDisplayTextLength() + 1; + char *buf = (char*)txtAlloc.alloc(bufLen); + item->getDisplayText(bufLen, buf); + textExt.x = mProfile->mFont->getStrWidth(buf); + textExt.y = mProfile->mFont->getHeight(); + + if( pScrollParent->isRectCompletelyVisible(RectI(textStart, textExt)) ) + render = false; + } + } + + if( render ) + { + if( mUseInspectorTooltips ) + { + item->getTooltipText( sizeof( buf ), buf ); + tipText = buf; + } + else + { + tipText = item->mTooltip.c_str(); + } + } + } + + return defaultTooltipRender( cursorPos, cursorPos, tipText ); +} + +//------------------------------------------------------------------------------ + +void GuiTreeViewCtrl::clearSelection() +{ + if( mDebug ) Con::printf( "clearSelection called" ); + + while ( !mSelectedItems.empty() ) + { + removeSelection( mSelectedItems.last()->mId ); + } + + mSelectedItems.clear(); + mSelected.clear(); + + onClearSelection(); + onClearSelection_callback(); +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::lockSelection(bool lock) +{ + for(U32 i = 0; i < mSelectedItems.size(); i++) + { + if(mSelectedItems[i]->isInspectorData()) + mSelectedItems[i]->getObject()->setLocked(lock); + } +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::hideSelection(bool hide) +{ + for(U32 i = 0; i < mSelectedItems.size(); i++) + { + if(mSelectedItems[i]->isInspectorData()) + mSelectedItems[i]->getObject()->setHidden(hide); + } +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::toggleLockSelection() +{ + for(U32 i = 0; i < mSelectedItems.size(); i++) + { + if( mSelectedItems[i]->isInspectorData() ) + { + SimObject* object = mSelectedItems[ i ]->getObject(); + object->setLocked( !object->isLocked() ); + } + } +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::toggleHideSelection() +{ + for(U32 i = 0; i < mSelectedItems.size(); i++) + { + if( mSelectedItems[i]->isInspectorData() ) + { + SimObject* object = mSelectedItems[ i ]->getObject(); + object->setHidden( !object->isHidden() ); + } + } +} + +//------------------------------------------------------------------------------ + +// handles icon assignments +S32 GuiTreeViewCtrl::getIcon(const char * iconString) +{ + return -1; +} + +//----------------------------------------------------------------------------- + +GuiTreeViewCtrl::Item* GuiTreeViewCtrl::addInspectorDataItem(Item *parent, SimObject *obj) +{ + S32 icon = getIcon(obj->getClassName()); + Item *item = createItem(icon); + item->mState.set(Item::InspectorData); + + // Set the item text label flags. + + if( !mShowObjectIds ) + item->mState.clear( Item::ShowObjectId ); + else + item->mState.set( Item::ShowObjectId ); + if( !mShowClassNames ) + item->mState.clear( Item::ShowClassName ); + else + item->mState.set( Item::ShowClassName ); + if( !mShowObjectNames ) + item->mState.clear( Item::ShowObjectName ); + else + item->mState.set( Item::ShowObjectName ); + if( !mShowInternalNames ) + item->mState.clear( Item::ShowInternalName ); + else + item->mState.set( Item::ShowInternalName ); + if( mShowClassNameForUnnamedObjects ) + item->mState.set( Item::ShowClassNameForUnnamed ); + + // Deal with child objects... + if(dynamic_cast(obj)) + item->mState.set(Item::VirtualParent); + + // Actually store the data! + item->setObject(obj); + + // Now add us to the data structure... + if(parent) + { + // Add as child of parent. + if(parent->mChild) + { + Item * traverse = parent->mChild; + while(traverse->mNext) + traverse = traverse->mNext; + + traverse->mNext = item; + item->mPrevious = traverse; + } + else + parent->mChild = item; + + item->mParent = parent; + } + else + { + // If no parent, add to root. + item->mNext = mRoot; + mRoot = item; + item->mParent = NULL; + } + + mFlags.set(RebuildVisible); + return item; +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::unlinkItem(Item * item) +{ + if (item->mPrevious) + item->mPrevious->mNext = item->mNext; + + if (item->mNext) + item->mNext->mPrevious = item->mPrevious; +} + +//----------------------------------------------------------------------------- + +bool GuiTreeViewCtrl::childSearch(Item * item, SimObject *obj, bool yourBaby) +{ + Item * temp = item->mChild; + while (temp) + { + //do you have my baby? + if (temp->isInspectorData()) + { + if (temp->getObject() == obj) + yourBaby = false; //probably a child of an inner script + } + yourBaby = childSearch(temp,obj, yourBaby); + temp = temp->mNext; + } + return yourBaby; +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::inspectorSearch(Item * item, Item * parent, SimSet * parentSet, SimSet * newParentSet) +{ + if (!parentSet||!newParentSet) + return; + + if (item == parent->mNext) + return; + + if (item) + { + if (item->isInspectorData()) + { + // remove the object from the parentSet and add it to the newParentSet + SimObject* simObj = item->getObject(); + + if (parentSet->size()) + { + SimObject *lastObject = parentSet->last(); + parentSet->removeObject(simObj); + parentSet->reOrder(lastObject); + } + else + parentSet->removeObject(simObj); + + newParentSet->addObject(simObj); + + if (item->mNext) + { + inspectorSearch(item->mNext, parent, parentSet, newParentSet); + return; + } + else + { + // end of children so backing up + if (item->mParent == parent) + return; + else + { + inspectorSearch(item->mParent->mNext, parent, parentSet, newParentSet); + return; + } + } + } + + if (item->mChild) + { + inspectorSearch(item->mChild, parent, parentSet, newParentSet); + return; + } + + if (item->mNext) + { + inspectorSearch(item->mNext, parent, parentSet, newParentSet); + return; + } + } +} + +//----------------------------------------------------------------------------- + +bool GuiTreeViewCtrl::objectSearch( const SimObject *object, Item **item ) +{ + for ( U32 i = 0; i < mItems.size(); i++ ) + { + Item *pItem = mItems[i]; + + if ( !pItem ) + continue; + + SimObject *pObj = pItem->getObject(); + + if ( pObj && pObj == object ) + { + *item = pItem; + return true; + } + } + + return false; +} + +//----------------------------------------------------------------------------- + +bool GuiTreeViewCtrl::onVirtualParentBuild(Item *item, bool bForceFullUpdate) +{ + if(!item->mState.test(Item::InspectorData)) + return true; + + // Blast an item if it doesn't have a corresponding SimObject... + if(item->mInspectorInfo.mObject == NULL) + { + removeItem(item->mId); + return false; + } + + // Skip the next stuff unless we're expanded... + if(!item->isExpanded() && !bForceFullUpdate && !( item == mRoot && !mShowRoot ) ) + return true; + + // Verify that we have all the kids we should in here... + SimSet *srcObj = dynamic_cast(&(*item->mInspectorInfo.mObject)); + + // If it's not a SimSet... WTF are we doing here? + if(!srcObj) + return true; + + // This is slow but probably ok. + for( SimSet::iterator i = srcObj->begin(); i != srcObj->end(); ++ i ) + { + SimObject *obj = *i; + + // If we can't find it, add it. + // unless it has a parent that is a child that is a script + Item *res = item->findChildByValue(obj); + + bool foundChild = true; + + // search the children. if any of them are the parent of the object then don't add it. + foundChild = childSearch(item,obj,foundChild); + + if(!res && foundChild) + { + if (mDebug) Con::printf( "adding object %i to item %i", obj->getId(), item->mId ); + res = addInspectorDataItem(item, obj); + } + + if( res ) + res->mState.set( Item::RebuildVisited ); + } + + // Go through our items and purge those that have disappeared from + // the set. + + for( Item* ptr = item->mChild; ptr != NULL; ) + { + Item* next = ptr->mNext; + if( !ptr->mState.test( Item::RebuildVisited ) ) + { + if( mDebug ) Con::printf( "removing item %i for object %i that is no longer in the set", + ptr->mId, ptr->getObject()->getId() ); + + removeItem( ptr->mId, false ); + } + else + ptr->mState.clear( Item::RebuildVisited ); + + ptr = next; + } + + return true; +} + +//----------------------------------------------------------------------------- + +bool GuiTreeViewCtrl::onVirtualParentExpand(Item *item) +{ + // Do nothing... + return true; +} + +//----------------------------------------------------------------------------- + +bool GuiTreeViewCtrl::onVirtualParentCollapse(Item *item) +{ + // Do nothing... + return true; +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::inspectObject( SimObject* obj, bool okToEdit ) +{ + _destroyTree(); + + mFlags.set( IsEditable, okToEdit ); + mFlags.set( IsInspector ); + + onDefineIcons_callback(); + + addInspectorDataItem( NULL, obj ); +} + +//----------------------------------------------------------------------------- + +S32 GuiTreeViewCtrl::findItemByName(const char *name) +{ + for (S32 i = 0; i < mItems.size(); i++) + if (mItems[i] && dStrcmp(mItems[i]->getText(),name) == 0) + return mItems[i]->mId; + + return 0; +} + +//----------------------------------------------------------------------------- + +S32 GuiTreeViewCtrl::findItemByValue(const char *name) +{ + for (S32 i = 0; i < mItems.size(); i++) + if (mItems[i] && dStrcmp(mItems[i]->getValue(),name) == 0) + return mItems[i]->mId; + + return 0; +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::sortTree( bool caseSensitive, bool traverseHierarchy, bool parentsFirst ) +{ + itemSortList( mRoot, caseSensitive, traverseHierarchy, parentsFirst ); +} + +//----------------------------------------------------------------------------- + +StringTableEntry GuiTreeViewCtrl::getTextToRoot( S32 itemId, const char * delimiter ) +{ + Item * item = getItem(itemId); + + if(!item) + { + Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::getTextToRoot: invalid start item id!"); + return StringTable->insert(""); + } + + if(item->isInspectorData()) + { + Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::getTextToRoot: cannot get text to root of inspector data items"); + return StringTable->insert(""); + } + + char bufferOne[1024]; + char bufferTwo[1024]; + char bufferNodeText[128]; + + dMemset( bufferOne, 0, sizeof(bufferOne) ); + dMemset( bufferTwo, 0, sizeof(bufferTwo) ); + + dStrcpy( bufferOne, item->getText() ); + + Item *prevNode = item->mParent; + while ( prevNode ) + { + dMemset( bufferNodeText, 0, sizeof(bufferNodeText) ); + dStrcpy( bufferNodeText, prevNode->getText() ); + dSprintf( bufferTwo, 1024, "%s%s%s",bufferNodeText, delimiter, bufferOne ); + dStrcpy( bufferOne, bufferTwo ); + dMemset( bufferTwo, 0, sizeof(bufferTwo) ); + prevNode = prevNode->mParent; + } + + // Return the result, StringTable-ized. + return StringTable->insert( bufferOne, true ); +} + +//----------------------------------------------------------------------------- + +void GuiTreeViewCtrl::setFilterText( const String& text ) +{ + mFilterText = text; + + // Trigger rebuild. + mFlags.set( RebuildVisible ); +} + +//============================================================================= +// Console Methods. +//============================================================================= +// MARK: ---- Console Methods ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTreeViewCtrl, findItemByName, S32, ( const char* text ),, + "Get the ID of the item whose text matches the given @a text.\n\n" + "@param text Item text to match.\n" + "@return ID of the item or -1 if no item matches the given text." ) +{ + return object->findItemByName( text ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTreeViewCtrl, findItemByValue, S32, ( const char* value ),, + "Get the ID of the item whose value matches @a value.\n\n" + "@param value Value text to match.\n" + "@return ID of the item or -1 if no item has the given value." ) +{ + return object->findItemByValue( value ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTreeViewCtrl, findChildItemByName, S32, ( S32 parentId, const char* childName ),, + "Get the child item of the given parent item whose text matches @a childName.\n\n" + "@param parentId Item ID of the parent in which to look for the child.\n" + "@param childName Text of the child item to find.\n" + "@return ID of the child item or -1 if no child in @a parentId has the given text @a childName.\n\n" + "@note This method does not recurse, i.e. it only looks for direct children." ) +{ + if( parentId == 0 ) + { + if( !object->getRootItem() ) + return 0; + + GuiTreeViewCtrl::Item* root = object->getRootItem(); + while( root ) + { + if( dStricmp( root->getText(), childName ) == 0 ) + return root->getID(); + + root = root->mNext; + } + + return 0; + } + else + { + GuiTreeViewCtrl::Item* item = object->getItem( parentId ); + + if( !item ) + { + Con::errorf( "GuiTreeViewCtrl.findChildItemByName - invalid parent ID '%i'", parentId ); + return 0; + } + + GuiTreeViewCtrl::Item* child = item->findChildByName( childName ); + if( !child ) + return 0; + + return child->mId; + } +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTreeViewCtrl, insertItem, S32, ( S32 parentId, const char* text, const char* value, const char* icon, S32 normalImage, S32 expandedImage ), ( "", "", 0, 0 ), + "Add a new item to the tree.\n\n" + "@param parentId Item ID of parent to which to add the item as a child. 0 is root item.\n" + "@param text Text to display on the item in the tree.\n" + "@param value Behind-the-scenes value of the item.\n" + "@param icon\n" + "@param normalImage\n" + "@param expandedImage\n" + "@return The ID of the newly added item." ) +{ + return object->insertItem( parentId, text, value, icon, normalImage, expandedImage ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTreeViewCtrl, lockSelection, void, ( bool lock ), ( true ), + "Set whether the current selection can be changed by the user or not.\n\n" + "@param lock If true, the current selection is frozen and cannot be changed. If false, " + "the selection may be modified." ) +{ + object->lockSelection( lock ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTreeViewCtrl, hideSelection, void, ( bool state ), ( true ), + "Call SimObject::setHidden( @a state ) on all objects in the current selection.\n\n" + "@param state Visibility state to set objects in selection to." ) +{ + object->hideSelection( state ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTreeViewCtrl, toggleLockSelection, void, (),, + "Toggle the locked state of all objects in the current selection." ) +{ + object->toggleLockSelection(); + +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTreeViewCtrl, toggleHideSelection, void, (),, + "Toggle the hidden state of all objects in the current selection." ) +{ + object->toggleHideSelection(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTreeViewCtrl, clearSelection, void, (),, + "Unselect all currently selected items." ) +{ + object->clearSelection(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTreeViewCtrl, deleteSelection, void, (),, + "Delete all items/objects in the current selection." ) +{ + object->deleteSelection(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTreeViewCtrl, addSelection, void, ( S32 id, bool isLastSelection ), ( true ), + "Add an item/object to the current selection.\n\n" + "@param id ID of item/object to add to the selection.\n" + "@param isLastSelection Whether there are more pending items/objects to be added to the selection. If false, " + "the control will defer refreshing the tree and wait until addSelection() is called with this parameter set " + "to true." ) +{ + object->addSelection( id, isLastSelection, isLastSelection ); +} + +ConsoleMethod(GuiTreeViewCtrl, addChildSelectionByValue, void, 4, 4, "addChildSelectionByValue(TreeItemId parent, value)") +{ + S32 id = dAtoi(argv[2]); + GuiTreeViewCtrl::Item* parentItem = object->getItem(id); + GuiTreeViewCtrl::Item* child = parentItem->findChildByValue(argv[3]); + object->addSelection(child->getID()); +} + +ConsoleMethod(GuiTreeViewCtrl, removeSelection, void, 3, 3, "(deselects an item)") +{ + S32 id = dAtoi(argv[2]); + object->removeSelection(id); +} + +ConsoleMethod(GuiTreeViewCtrl, removeChildSelectionByValue, void, 4, 4, "removeChildSelectionByValue(TreeItemId parent, value)") +{ + S32 id = dAtoi(argv[2]); + GuiTreeViewCtrl::Item* parentItem = object->getItem(id); + if(parentItem) + { + GuiTreeViewCtrl::Item* child = parentItem->findChildByValue(argv[3]); + if(child) + { + object->removeSelection(child->getID()); + } + } +} + +ConsoleMethod(GuiTreeViewCtrl, selectItem, bool, 3, 4, "(TreeItemId item, bool select=true)") +{ + S32 id = dAtoi(argv[2]); + bool select = true; + if(argc == 4) + select = dAtob(argv[3]); + + return(object->setItemSelected(id, select)); +} + +ConsoleMethod(GuiTreeViewCtrl, expandItem, bool, 3, 4, "(TreeItemId item, bool expand=true)") +{ + S32 id = dAtoi(argv[2]); + bool expand = true; + if(argc == 4) + expand = dAtob(argv[3]); + return(object->setItemExpanded(id, expand)); +} + +ConsoleMethod(GuiTreeViewCtrl, markItem, bool, 3, 4, "(TreeItemId item, bool mark=true)") +{ + S32 id = dAtoi(argv[2]); + bool mark = true; + if(argc == 4) + mark = dAtob(argv[3]); + return object->markItem(id, mark); +} + +// Make the given item visible. +ConsoleMethod(GuiTreeViewCtrl, scrollVisible, void, 3, 3, "(TreeItemId item)") +{ + object->scrollVisible(dAtoi(argv[2])); +} + +ConsoleMethod(GuiTreeViewCtrl, buildIconTable, bool, 3,3, "(builds an icon table)") +{ + const char * icons = argv[2]; + return object->buildIconTable(icons); +} + +ConsoleMethod( GuiTreeViewCtrl, open, void, 3, 4, "(SimSet obj, bool okToEdit=true) Set the root of the tree view to the specified object, or to the root set.") +{ + SimSet *treeRoot = NULL; + SimObject* target = Sim::findObject(argv[2]); + + bool okToEdit = true; + + if (argc == 4) + okToEdit = dAtob(argv[3]); + + if (target) + treeRoot = dynamic_cast(target); + + if (! treeRoot) + Sim::findObject(RootGroupId, treeRoot); + + object->inspectObject(treeRoot,okToEdit); +} + +ConsoleMethod( GuiTreeViewCtrl, setItemTooltip, void, 4, 4, "( int id, string text ) - Set the tooltip to show for the given item." ) +{ + int id = dAtoi( argv[ 2 ] ); + + GuiTreeViewCtrl::Item* item = object->getItem( id ); + if( !item ) + { + Con::errorf( "GuiTreeViewCtrl::setTooltip() - invalid item id '%i'", id ); + return; + } + + item->mTooltip = argv[ 3 ]; +} + +ConsoleMethod( GuiTreeViewCtrl, setItemImages, void, 5, 5, "( int id, int normalImage, int expandedImage ) - Sets the normal and expanded images to show for the given item." ) +{ + int id = dAtoi( argv[ 2 ] ); + + GuiTreeViewCtrl::Item* item = object->getItem( id ); + if( !item ) + { + Con::errorf( "GuiTreeViewCtrl::setItemImages() - invalid item id '%i'", id ); + return; + } + + item->setNormalImage((S8)dAtoi(argv[3])); + item->setExpandedImage((S8)dAtoi(argv[4])); +} + +ConsoleMethod( GuiTreeViewCtrl, isParentItem, bool, 3, 3, "( int id ) - Returns true if the given item contains child items." ) +{ + int id = dAtoi( argv[ 2 ] ); + if( !id && object->getItemCount() ) + return true; + + GuiTreeViewCtrl::Item* item = object->getItem( id ); + if( !item ) + { + Con::errorf( "GuiTreeViewCtrl::isParentItem - invalid item id '%i'", id ); + return false; + } + + return item->isParent(); +} + +ConsoleMethod(GuiTreeViewCtrl, getItemText, const char *, 3, 3, "(TreeItemId item)") +{ + return(object->getItemText(dAtoi(argv[2]))); +} + +ConsoleMethod(GuiTreeViewCtrl, getItemValue, const char *, 3, 3, "(TreeItemId item)") +{ + return(object->getItemValue(dAtoi(argv[2]))); +} + +ConsoleMethod(GuiTreeViewCtrl, editItem, bool, 5, 5, "(TreeItemId item, string newText, string newValue)") +{ + return(object->editItem(dAtoi(argv[2]), argv[3], argv[4])); +} + +ConsoleMethod(GuiTreeViewCtrl, removeItem, bool, 3, 3, "(TreeItemId item)") +{ + return(object->removeItem(dAtoi(argv[2]))); +} + +ConsoleMethod(GuiTreeViewCtrl, removeAllChildren, void, 3, 3, "removeAllChildren(TreeItemId parent)") +{ + object->removeAllChildren(dAtoi(argv[2])); +} +ConsoleMethod(GuiTreeViewCtrl, clear, void, 2, 2, "() - empty tree") +{ + object->removeItem(0); +} + +ConsoleMethod(GuiTreeViewCtrl, getFirstRootItem, S32, 2, 2, "Get id for root item.") +{ + return(object->getFirstRootItem()); +} + +ConsoleMethod(GuiTreeViewCtrl, getChild, S32, 3, 3, "(TreeItemId item)") +{ + return(object->getChildItem(dAtoi(argv[2]))); +} + +ConsoleMethod(GuiTreeViewCtrl, buildVisibleTree, void, 2, 3, "Build the visible tree") +{ + bool forceFullUpdate = false; + if( argc > 2 ) + forceFullUpdate = dAtob( argv[ 2 ] ); + + object->buildVisibleTree( forceFullUpdate ); +} + +//FIXME: [rene 11/09/09 - This clashes with GuiControl.getParent(); bad thing; should be getParentItem] +ConsoleMethod(GuiTreeViewCtrl, getParent, S32, 3, 3, "(TreeItemId item)") +{ + return(object->getParentItem(dAtoi(argv[2]))); +} + +ConsoleMethod(GuiTreeViewCtrl, getNextSibling, S32, 3, 3, "(TreeItemId item)") +{ + return(object->getNextSiblingItem(dAtoi(argv[2]))); +} + +ConsoleMethod(GuiTreeViewCtrl, getPrevSibling, S32, 3, 3, "(TreeItemId item)") +{ + return(object->getPrevSiblingItem(dAtoi(argv[2]))); +} + +ConsoleMethod(GuiTreeViewCtrl, getItemCount, S32, 2, 2, "") +{ + return(object->getItemCount()); +} + +ConsoleMethod(GuiTreeViewCtrl, getSelectedItem, S32, 2, 3, "( int index=0 ) - Return the selected item at the given index.") +{ + S32 index = 0; + if( argc > 2 ) + index = dAtoi( argv[ 2 ] ); + + return ( object->getSelectedItem( index ) ); +} + +ConsoleMethod(GuiTreeViewCtrl, getSelectedObject, S32, 2, 3, "( int index=0 ) - Return the currently selected SimObject at the given index in inspector mode or -1") +{ + S32 index = 0; + if( argc > 2 ) + index = dAtoi( argv[ 2 ] ); + + GuiTreeViewCtrl::Item *item = object->getItem( object->getSelectedItem( index ) ); + if( item != NULL && item->isInspectorData() ) + { + SimObject *obj = item->getObject(); + if( obj != NULL ) + return obj->getId(); + } + + return -1; +} + +ConsoleMethod(GuiTreeViewCtrl, getSelectedObjectList, const char*, 2, 2, + "Returns a space sperated list of all selected object ids.") +{ + char* buff = Con::getReturnBuffer(1024); + dSprintf(buff,1024,""); + + const Vector< GuiTreeViewCtrl::Item* > selectedItems = object->getSelectedItems(); + for(int i = 0; i < selectedItems.size(); i++) + { + GuiTreeViewCtrl::Item *item = selectedItems[i]; + + if ( item->isInspectorData() && item->getObject() ) + { + S32 id = item->getObject()->getId(); + //get the current length of the buffer + U32 len = dStrlen(buff); + //the start of the buffer where we want to write + char* buffPart = buff+len; + //the size of the remaining buffer (-1 cause dStrlen doesn't count the \0) + S32 size = 1024-len-1; + //write it: + if(size < 1) + { + Con::errorf("GuiTreeViewCtrl::getSelectedItemList - Not enough room to return our object list"); + return buff; + } + + dSprintf(buffPart,size,"%d ", id); + } + } + + return buff; +} + +ConsoleMethod(GuiTreeViewCtrl, moveItemUp, void, 3, 3, "(TreeItemId item)") +{ + object->moveItemUp( dAtoi( argv[2] ) ); +} + +ConsoleMethod(GuiTreeViewCtrl, getSelectedItemsCount, S32, 2, 2, "") +{ + return ( object->getSelectedItemsCount() ); +} + + + +ConsoleMethod(GuiTreeViewCtrl, moveItemDown, void, 3, 3, "(TreeItemId item)") +{ + object->moveItemDown( dAtoi( argv[2] ) ); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod(GuiTreeViewCtrl, getTextToRoot, const char*,4,4,"(TreeItemId item,Delimiter=none) gets the text from the current node to the root, concatenating at each branch upward, with a specified delimiter optionally") +{ + if ( argc < 4 ) + { + Con::warnf("GuiTreeViewCtrl::getTextToRoot - Invalid number of arguments!"); + return (""); + } + S32 itemId = dAtoi( argv[2] ); + StringTableEntry delimiter = argv[3]; + + return object->getTextToRoot( itemId, delimiter ); +} + +ConsoleMethod(GuiTreeViewCtrl, getSelectedItemList,const char*, 2,2,"returns a space seperated list of mulitple item ids") +{ + char* buff = Con::getReturnBuffer(1024); + dSprintf(buff,1024,""); + + const Vector< S32 >& selected = object->getSelected(); + for(int i = 0; i < selected.size(); i++) + { + S32 id = selected[i]; + //get the current length of the buffer + U32 len = dStrlen(buff); + //the start of the buffer where we want to write + char* buffPart = buff+len; + //the size of the remaining buffer (-1 cause dStrlen doesn't count the \0) + S32 size = 1024-len-1; + //write it: + if(size < 1) + { + Con::errorf("GuiTreeViewCtrl::getSelectedItemList - Not enough room to return our object list"); + return buff; + } + + dSprintf(buffPart,size,"%d ", id); + } +//mSelected + + return buff; +} + +S32 GuiTreeViewCtrl::findItemByObjectId(S32 iObjId) +{ + for (S32 i = 0; i < mItems.size(); i++) + { + if ( !mItems[i] ) + continue; + + SimObject* pObj = mItems[i]->getObject(); + if( pObj && pObj->getId() == iObjId ) + return mItems[i]->mId; + } + + return -1; +} + +//------------------------------------------------------------------------------ +ConsoleMethod(GuiTreeViewCtrl, findItemByObjectId, S32, 3, 3, "(find item by object id and returns the mId)") +{ + return(object->findItemByObjectId(dAtoi(argv[2]))); +} + +//------------------------------------------------------------------------------ +bool GuiTreeViewCtrl::scrollVisibleByObjectId(S32 objID) +{ + S32 itemID = findItemByObjectId(objID); + + if(itemID == -1) + { + // we did not find the item in our current items + // we should try to find and show the parent of the item. + SimObject *obj = Sim::findObject(objID); + if(!obj || !obj->getGroup()) + return false; + + // if we can't show the parent, we fail. + if(! scrollVisibleByObjectId(obj->getGroup()->getId()) ) + return false; + + // get the parent. expand the parent. rebuild the tree. this ensures that + // we'll be able to find the child item we're targeting. + S32 parentID = findItemByObjectId(obj->getGroup()->getId()); + AssertFatal(parentID != -1, "We were able to show the parent, but could not then find the parent. This should not happen."); + Item *parentItem = getItem(parentID); + parentItem->setExpanded(true); + buildVisibleTree(); + + // NOW we should be able to find the object. if not... something's wrong. + itemID = findItemByObjectId(objID); + AssertWarn(itemID != -1,"GuiTreeViewCtrl::scrollVisibleByObjectId() found the parent, but can't find it's immediate child. This should not happen."); + if(itemID == -1) + return false; + } + + // ok, item found. scroll to it. + mFlags.set( RebuildVisible ); + scrollVisible(itemID); + + return true; +} + +//------------------------------------------------------------------------------ +ConsoleMethod(GuiTreeViewCtrl, scrollVisibleByObjectId, S32, 3, 3, "(show item by object id. returns true if sucessful.)") +{ + return(object->scrollVisibleByObjectId(dAtoi(argv[2]))); +} + +//------------------------------------------------------------------------------ + +//FIXME: this clashes with SimSet.sort() +ConsoleMethod( GuiTreeViewCtrl, sort, void, 2, 6, "( int parent, bool traverseHierarchy=false, bool parentsFirst=false, bool caseSensitive=true ) - Sorts all items of the given parent (or root). With 'hierarchy', traverses hierarchy." ) +{ + S32 parent = 0; + if( argc >= 3 ) + parent = dAtoi( argv[ 2 ] ); + + bool traverseHierarchy = false; + bool parentsFirst = false; + bool caseSensitive = true; + + if( argc >= 4 ) + traverseHierarchy = dAtob( argv[ 3 ] ); + if( argc >= 5 ) + parentsFirst = dAtob( argv[ 4 ] ); + if( argc >= 6 ) + caseSensitive = dAtob( argv[ 5 ] ); + + if( !parent ) + object->sortTree( caseSensitive, traverseHierarchy, parentsFirst ); + else + { + GuiTreeViewCtrl::Item* item = object->getItem( parent ); + if( !item ) + { + Con::errorf( "GuiTreeViewCtrl::sort - no item '%i' in tree", parent ); + return; + } + + item->sort( caseSensitive, traverseHierarchy, parentsFirst ); + } +} + +void GuiTreeViewCtrl::cancelRename() +{ + if ( !mRenamingItem || !mRenameCtrl ) + return; + + mRenamingItem = NULL; + + if ( mRenameCtrl ) + mRenameCtrl->clearFirstResponder(); +} + +void GuiTreeViewCtrl::onRenameValidate() +{ + if ( !mRenamingItem || !mRenameCtrl ) + return; + + char data[ GuiTextCtrl::MAX_STRING_LENGTH+1 ]; + mRenameCtrl->getText( data ); + + SimObject *obj = mRenamingItem->getObject(); + + mRenamingItem = NULL; + + // Object could have been deleted in the interum. + if ( !obj ) + return; + + if( isMethod( "handleRenameObject" ) && handleRenameObject_callback( data, obj ) ) + return; + + if ( mRenameInternal ) + obj->setInternalName( data ); + else if ( validateObjectName( data, obj ) ) + obj->assignName( data ); +} + +void GuiTreeViewCtrl::showItemRenameCtrl( Item* item ) +{ + SimObject *renameObj = item->getObject(); + + mRenamingItem = item; + + if ( !mRenameCtrl ) + { + mRenameCtrl = new GuiTextEditCtrl; + mRenameCtrl->registerObject(); + addObject( mRenameCtrl ); + + if ( mRenameInternal ) + mRenameCtrl->setText( renameObj->getInternalName() ); + else + mRenameCtrl->setText( renameObj->getName() ); + + mRenameCtrl->setFirstResponder(); + mRenameCtrl->setSinkAllKeys(true); + mRenameCtrl->selectAllText(); + mRenameCtrl->setCursorPos(0); + + char cmd[256]; + dSprintf( cmd, 256, "%i.onRenameValidate();", getId() ); + mRenameCtrl->setField( "validate", cmd ); + + dSprintf( cmd, 256, "%i.cancelRename();", getId() ); + mRenameCtrl->setField( "escapeCommand", cmd ); + } +} + +ConsoleMethod( GuiTreeViewCtrl, cancelRename, void, 2, 2, "For internal use." ) +{ + object->cancelRename(); +} + +ConsoleMethod( GuiTreeViewCtrl, onRenameValidate, void, 2, 2, "For internal use." ) +{ + object->onRenameValidate(); +} + +ConsoleMethod( GuiTreeViewCtrl, showItemRenameCtrl, void, 3, 3, "( TreeItemId id ) - Show the rename text field for the given item (only one at a time)." ) +{ + S32 id = dAtoi( argv[ 2 ] ); + GuiTreeViewCtrl::Item* item = object->getItem( id ); + if( !item ) + { + Con::errorf( "GuiTreeViewCtrl::showItemRenameCtrl - invalid item id '%i'", id ); + return; + } + + object->showItemRenameCtrl( item ); +} + +ConsoleMethod( GuiTreeViewCtrl, setDebug, void, 2, 3, "( bool value=true ) - Enable/disable debug output." ) +{ + bool value = true; + if( argc > 2 ) + value = dAtob( argv[ 2 ] ); + + object->setDebug( value ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTreeViewCtrl, isItemSelected, bool, ( S32 id ),, + "Check whether the given item is currently selected in the tree.\n\n" + "@param id Item/object ID.\n" + "@return True if the given item/object is currently selected in the tree." ) +{ + const Vector< GuiTreeViewCtrl::Item* >& selectedItems = object->getSelectedItems(); + for( S32 i = 0; i < selectedItems.size(); ++ i ) + if( selectedItems[ i ]->mId == id ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTreeViewCtrl, getFilterText, const char*, (),, + "Get the current filter expression. Only tree items whose text matches this expression " + "are displayed. By default, the expression is empty and all items are shown.\n\n" + "@return The current filter pattern or an empty string if no filter pattern is currently active.\n\n" + "@see setFilterText\n" + "@see clearFilterText" ) +{ + return object->getFilterText(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTreeViewCtrl, setFilterText, void, ( const char* pattern ),, + "Set the pattern by which to filter items in the tree. Only items in the tree whose text " + "matches this pattern are displayed.\n\n" + "@param pattern New pattern based on which visible items in the tree should be filtered. If empty, all items become visible.\n\n" + "@see getFilterText\n" + "@see clearFilterText" ) +{ + object->setFilterText( pattern ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTreeViewCtrl, clearFilterText, void, (),, + "Clear the current item filtering pattern.\n\n" + "@see setFilterText\n" + "@see getFilterText" ) +{ + object->clearFilterText(); +} diff --git a/Engine/source/gui/controls/guiTreeViewCtrl.h b/Engine/source/gui/controls/guiTreeViewCtrl.h new file mode 100644 index 000000000..f4dc5847f --- /dev/null +++ b/Engine/source/gui/controls/guiTreeViewCtrl.h @@ -0,0 +1,606 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUI_TREEVIEWCTRL_H +#define _GUI_TREEVIEWCTRL_H + +#include "core/bitSet.h" +#include "math/mRect.h" +#include "gfx/gFont.h" +#include "gui/core/guiControl.h" +#include "gui/core/guiArrayCtrl.h" + + +class GuiTextEditCtrl; + +//------------------------------------------------------------------------------ + +class GuiTreeViewCtrl : public GuiArrayCtrl +{ + private: + typedef GuiArrayCtrl Parent; + + public: + /// @section GuiControl_Intro Introduction + /// @nosubgrouping + + /// + class Item + { + public: + + enum ItemState + { + Selected = BIT( 0 ), + Expanded = BIT( 1 ), + Marked = BIT( 2 ), ///< Marked items are drawn with a border around them. This is + /// different than "Selected" because it can only be set by script. + Filtered = BIT( 3 ), ///< Whether the item is currently filtered out. + MouseOverBmp = BIT( 4 ), + MouseOverText = BIT( 5 ), + MouseOverIcon = BIT( 6 ), + InspectorData = BIT( 7 ), ///< Set if we're representing some inspector + /// info (ie, use mInspectorInfo, not mScriptInfo) + + VirtualParent = BIT( 8 ), ///< This indicates that we should be rendered as + /// a parent even though we don't have any children. + /// This is useful for preventing scenarios where + /// we might want to create thousands of + /// Items that might never be shown (for instance + /// if we're browsing the object hierarchy in + /// Torque, which might have thousands of objects). + + RebuildVisited = BIT( 9 ), ///< Rebuild traversal for virtual parents has visited and validated this item. + + ShowObjectId = BIT( 10 ), + ShowClassName = BIT( 11 ), + ShowObjectName = BIT( 12 ), + ShowInternalName = BIT( 13 ), + ShowClassNameForUnnamed = BIT( 14 ) + }; + + GuiTreeViewCtrl* mParentControl; + BitSet32 mState; + SimObjectPtr< GuiControlProfile > mProfile; + S16 mId; + U16 mTabLevel; + Item* mParent; + Item* mChild; + Item* mNext; + Item* mPrevious; + String mTooltip; + S32 mIcon; //stores the icon that will represent the item in the tree + S32 mDataRenderWidth; /// this stores the pixel width needed + /// to render the item's data in the + /// onRenderCell function to optimize + /// for speed. + + Item( GuiTreeViewCtrl* parent, GuiControlProfile *pProfile ); + ~Item(); + + struct ScriptTag + { + S8 mNormalImage; + S8 mExpandedImage; + StringTableEntry mText; + StringTableEntry mValue; + } mScriptInfo; + struct InspectorTag + { + SimObjectPtr mObject; + } mInspectorInfo; + + /// @name Get Methods + /// @{ + + /// + S8 getNormalImage() const; + S8 getExpandedImage() const; + StringTableEntry getText(); + StringTableEntry getValue(); + inline const S16 getID() const { return mId; }; + SimObject *getObject(); + U32 getDisplayTextLength(); + S32 getDisplayTextWidth(GFont *font); + void getDisplayText(U32 bufLen, char *buf); + bool hasObjectBasedTooltip(); + void getTooltipText(U32 bufLen, char *buf); + + /// @} + + + /// @name Set Methods + /// @{ + + /// Set whether an item is expanded or not (showing children or having them hidden) + void setExpanded( const bool f = true ); + /// Set the image to display when an item IS expanded + void setExpandedImage(const S8 id); + /// Set the image to display when an item is NOT expanded + void setNormalImage(const S8 id); + /// Assign a SimObject pointer to an inspector data item + void setObject(SimObject *obj); + /// Set the items displayable text (caption) + void setText(StringTableEntry txt); + /// Set the items script value (data) + void setValue(StringTableEntry val); + /// Set the items virtual parent flag + void setVirtualParent( bool value ); + /// Set whether the item is filtered out or not. + void setFiltered( bool value ) { mState.set( Filtered ); } + + /// @} + + + /// @name State Retrieval + /// @{ + + /// Returns true if this item is expanded. For + /// inspector objects, the expansion is stored + /// on the SimObject, for other things we use our + /// bit vector. + bool isExpanded() const; + + /// Return whether the item is current filtered out or not. + /// @note Parent items may be filtered and yet still be visible if they have + /// children that are not filtered. + bool isFiltered() const { return mState.test( Filtered ); } + + /// Returns true if an item is inspector data + /// or false if it's just an item. + bool isInspectorData() const { return mState.test(InspectorData); }; + + /// Returns true if we should show the expand art + /// and make the item interact with the mouse as if + /// it were a parent. + bool isParent() const; + + /// Return true if text label for inspector item should include internal name only. + bool showInternalNameOnly() const { return mState.test( ShowInternalName ) && !mState.test( ShowObjectName | ShowClassName | ShowObjectId ); } + + /// Return true if text label for inspector item should include object name only. + bool showObjectNameOnly() const { return mState.test( ShowObjectName ) && !mState.test( ShowInternalName | ShowClassName | ShowObjectId ); } + + /// @} + + /// @name Searching Methods + /// @{ + + /// Find a regular data item by it's script name. + Item* findChildByName( const char* name ); + + /// Find an inspector data item by it's SimObject pointer + Item* findChildByValue(const SimObject *obj); + + /// Find a regular data item by it's script value + Item* findChildByValue(StringTableEntry Value); + + /// @} + + /// Sort the childs of the item by their text. + /// + /// @param caseSensitive If true, sorting is case-sensitive. + /// @param traverseHierarchy If true, also triggers a sort() on all child items. + /// @param parentsFirst If true, parents are grouped before children in the resulting sort. + void sort( bool caseSensitive = true, bool traverseHierarchy = false, bool parentsFirst = false ); + + private: + void _connectMonitors(); + void _disconnectMonitors(); + }; + + friend class Item; // _onInspectorSetObjectModified + + /// @name Enums + /// @{ + + /// + enum TreeState + { + RebuildVisible = BIT(0), ///< Temporary flag, we have to rebuild the tree. + IsInspector = BIT(1), ///< We are mapping a SimObject hierarchy. + IsEditable = BIT(2), ///< We allow items to be moved around. + ShowTreeLines = BIT(3), ///< Should we render tree lines or just icons? + BuildingVisTree = BIT(4), ///< We are currently building the visible tree (prevent recursion) + }; + + protected: + + enum + { + MaxIcons = 32, + }; + + enum Icons + { + Default1 = 0, + SimGroup1, + SimGroup2, + SimGroup3, + SimGroup4, + Hidden, + Lock1, + Lock2, + Default, + Icon31, + Icon32 + }; + + enum mDragMidPointFlags + { + NomDragMidPoint, + AbovemDragMidPoint, + BelowmDragMidPoint + }; + + /// + enum HitFlags + { + OnIndent = BIT(0), + OnImage = BIT(1), + OnIcon = BIT(2), + OnText = BIT(3), + OnRow = BIT(4), + }; + + /// + enum BmpIndices + { + BmpFirstChild, + BmpLastChild, + BmpChild, + BmpExp, + BmpExpN, + BmpExpP, + BmpExpPN, + BmpCon, + BmpConN, + BmpConP, + BmpConPN, + BmpLine, + BmpGlow, + }; + + /// @} + + /// @name Callbacks + /// @{ + + DECLARE_CALLBACK( bool, onDeleteObject, ( SimObject* object ) ); + DECLARE_CALLBACK( bool, isValidDragTarget, ( S32 id, const char* value ) ); + DECLARE_CALLBACK( void, onDefineIcons, () ); + DECLARE_CALLBACK( void, onAddGroupSelected, ( SimGroup* group ) ); + DECLARE_CALLBACK( void, onAddSelection, ( S32 itemOrObjectId, bool isLastSelection ) ); + DECLARE_CALLBACK( void, onSelect, ( S32 itemOrObjectId ) ); + DECLARE_CALLBACK( void, onInspect, ( S32 itemOrObjectId ) ); + DECLARE_CALLBACK( void, onRemoveSelection, ( S32 itemOrObjectId ) ); + DECLARE_CALLBACK( void, onUnselect, ( S32 itemOrObjectId ) ); + DECLARE_CALLBACK( void, onDeleteSelection, () ); + DECLARE_CALLBACK( void, onObjectDeleteCompleted, () ); + DECLARE_CALLBACK( void, onKeyDown, ( S32 modifier, S32 keyCode ) ); + DECLARE_CALLBACK( void, onMouseUp, ( S32 hitItemId, S32 mouseClickCount ) ); + DECLARE_CALLBACK( void, onMouseDragged, () ); + DECLARE_CALLBACK( void, onRightMouseDown, ( S32 itemId, const Point2I& mousePos, SimObject* object = NULL ) ); + DECLARE_CALLBACK( void, onRightMouseUp, ( S32 itemId, const Point2I& mousePos, SimObject* object = NULL ) ); + DECLARE_CALLBACK( void, onBeginReparenting, () ); + DECLARE_CALLBACK( void, onEndReparenting, () ); + DECLARE_CALLBACK( void, onReparent, ( S32 itemOrObjectId, S32 oldParentItemOrObjectId, S32 newParentItemOrObjectId ) ); + DECLARE_CALLBACK( void, onDragDropped, () ); + DECLARE_CALLBACK( void, onAddMultipleSelectionBegin, () ); + DECLARE_CALLBACK( void, onAddMultipleSelectionEnd, () ); + DECLARE_CALLBACK( bool, canRenameObject, ( SimObject* object ) ); + DECLARE_CALLBACK( bool, handleRenameObject, ( const char* newName, SimObject* object ) ); + DECLARE_CALLBACK( void, onClearSelection, () ); + + /// @} + + /// + Vector mItems; + Vector mVisibleItems; + Vector mSelectedItems; + + /// Used for tracking stuff that was selected, but may not have been + /// created at time of selection. + Vector mSelected; + + S32 mItemCount; + + /// We do our own free list, as we we want to be able to recycle + /// item ids and do some other clever things. + Item* mItemFreeList; + + Item* mRoot; + S32 mMaxWidth; + S32 mSelectedItem; + S32 mDraggedToItem; + S32 mStart; + + /// A combination of TreeState flags. + BitSet32 mFlags; + + Item* mPossibleRenameItem; + Item* mRenamingItem; + Item* mTempItem; + GuiTextEditCtrl* mRenameCtrl; + + /// Current filter that determines which items in the tree are displayed and which are hidden. + String mFilterText; + + /// If true, a trace of actions taken by the control is logged to the console. Can + /// be turned on with the setDebug() script method. + bool mDebug; + + GFXTexHandle mIconTable[MaxIcons]; + + S32 mTabSize; + S32 mTextOffset; + bool mFullRowSelect; + S32 mItemHeight; + bool mDestroyOnSleep; + bool mSupportMouseDragging; + bool mMultipleSelections; + bool mDeleteObjectAllowed; + bool mDragToItemAllowed; + bool mClearAllOnSingleSelection; ///< When clicking on an already selected item, clear all other selections + bool mCompareToObjectID; + + /// Used to hide the root tree element, defaults to true. + bool mShowRoot; + + /// If true, object IDs will be included in inspector tree item labels. + bool mShowObjectIds; + + /// If true, class names will be included in inspector tree item labels. + bool mShowClassNames; + + /// If true, object names will be included in inspector tree item labels. + bool mShowObjectNames; + + /// If true, internal names will be included in inspector tree item labels. + bool mShowInternalNames; + + /// If true, class names will be used as object names for unnamed objects. + bool mShowClassNameForUnnamedObjects; + + /// If true then tooltips will be automatically + /// generated for all Inspector items + bool mUseInspectorTooltips; + + /// If true then only render item tooltips if the item + /// extends past the displayable width + bool mTooltipOnWidthOnly; + + /// If true clicking on a selected item ( that is an object ) + /// will allow you to rename it. + bool mCanRenameObjects; + + /// If true then object renaming operates on the internalName rather than + /// the object name. + bool mRenameInternal; + + S32 mCurrentDragCell; + S32 mPreviousDragCell; + S32 mDragMidPoint; + bool mMouseDragged; + bool mDragStartInSelection; + Point2I mMouseDownPoint; + + StringTableEntry mBitmapBase; + GFXTexHandle mTexRollover; + GFXTexHandle mTexSelected; + + ColorI mAltFontColor; + ColorI mAltFontColorHL; + ColorI mAltFontColorSE; + + SimObjectPtr mRootObject; + + void _destroyChildren( Item* item, Item* parent, bool deleteObjects=true); + void _destroyItem( Item* item, bool deleteObject=true); + void _destroyTree(); + + void _deleteItem(Item* item); + + void _buildItem(Item* item, U32 tabLevel, bool bForceFullUpdate = false); + + Item* _findItemByAmbiguousId( S32 itemOrObjectId, bool buildVirtual = true ); + + void _expandObjectHierarchy( SimGroup* group ); + + bool _hitTest(const Point2I & pnt, Item* & item, BitSet32 & flags); + + S32 getInspectorItemIconsWidth(Item* & item); + + virtual bool onVirtualParentBuild(Item *item, bool bForceFullUpdate = false); + virtual bool onVirtualParentExpand(Item *item); + virtual bool onVirtualParentCollapse(Item *item); + virtual void onItemSelected( Item *item ); + virtual void onRemoveSelection( Item *item ); + virtual void onClearSelection() {}; + + Item* addInspectorDataItem(Item *parent, SimObject *obj); + + virtual bool isValidDragTarget( Item* item ); + + bool _isRootLevelItem( Item* item ) const + { + return ( item == mRoot && mShowRoot ) || ( item->mParent == mRoot && !mShowRoot ); + } + + /// For inspector tree views, this is hooked to the SetModificationSignal of sets + /// so that the tree view knows when it needs to refresh. + void _onInspectorSetObjectModified( SetModification modification, SimSet* set, SimObject* object ); + + public: + GuiTreeViewCtrl(); + virtual ~GuiTreeViewCtrl(); + + /// Used for syncing the mSelected and mSelectedItems lists. + void syncSelection(); + + void lockSelection(bool lock); + void hideSelection(bool hide); + void toggleLockSelection(); + void toggleHideSelection(); + virtual bool canAddSelection( Item *item ) { return true; }; + void addSelection(S32 itemId, bool update = true, bool isLastSelection = true); + const Vector< Item* >& getSelectedItems() const { return mSelectedItems; } + const Vector< S32 >& getSelected() const { return mSelected; } + + bool isSelected(S32 itemId) + { + return isSelected( getItem( itemId ) ); + } + bool isSelected(Item *item) + { + if ( !item ) + return false; + return mSelectedItems.contains( item ); + } + + void setDebug( bool value ) { mDebug = value; } + + /// Should use addSelection and removeSelection when calling from script + /// instead of setItemSelected. Use setItemSelected when you want to select + /// something in the treeview as it has script call backs. + void removeSelection(S32 itemId); + + /// Sets the flag of the item with the matching itemId. + bool setItemSelected(S32 itemId, bool select); + bool setItemExpanded(S32 itemId, bool expand); + bool setItemValue(S32 itemId, StringTableEntry Value); + + const char * getItemText(S32 itemId); + const char * getItemValue(S32 itemId); + StringTableEntry getTextToRoot(S32 itemId, const char *delimiter = ""); + + Item* getRootItem() const { return mRoot; } + Item * getItem(S32 itemId) const; + Item * createItem(S32 icon); + bool editItem( S32 itemId, const char* newText, const char* newValue ); + + bool markItem( S32 itemId, bool mark ); + + bool isItemSelected( S32 itemId ); + + // insertion/removal + void unlinkItem(Item * item); + S32 insertItem(S32 parentId, const char * text, const char * value = "", const char * iconString = "", S16 normalImage = 0, S16 expandedImage = 1); + bool removeItem(S32 itemId, bool deleteObjects=true); + void removeAllChildren(S32 itemId); // Remove all children of the given item + + bool buildIconTable(const char * icons); + + bool setAddGroup(SimObject * obj); + + S32 getIcon(const char * iconString); + + // tree items + const S32 getFirstRootItem() const; + S32 getChildItem(S32 itemId); + S32 getParentItem(S32 itemId); + S32 getNextSiblingItem(S32 itemId); + S32 getPrevSiblingItem(S32 itemId); + S32 getItemCount(); + S32 getSelectedItem(); + S32 getSelectedItem(S32 index); // Given an item's index in the selection list, return its itemId + S32 getSelectedItemsCount() {return mSelectedItems.size();} // Returns the number of selected items + void moveItemUp( S32 itemId ); + void moveItemDown( S32 itemId ); + + // misc. + bool scrollVisible( Item *item ); + bool scrollVisible( S32 itemId ); + bool scrollVisibleByObjectId( S32 objID ); + + void deleteSelection(); + void clearSelection(); + + S32 findItemByName(const char *name); + S32 findItemByValue(const char *name); + S32 findItemByObjectId(S32 iObjId); + + void sortTree( bool caseSensitive, bool traverseHierarchy, bool parentsFirst ); + + /// @name Filtering + /// @{ + + /// Get the current filter expression. Only tree items whose text matches this expression + /// are displayed. By default, the expression is empty and all items are shown. + const String& getFilterText() const { return mFilterText; } + + /// Set the pattern by which to filter items in the tree. Only items in the tree whose text + /// matches this pattern are displayed. + void setFilterText( const String& text ); + + /// Clear the current item filtering pattern. + void clearFilterText() { setFilterText( String::EmptyString ); } + + /// @} + + // GuiControl + bool onAdd(); + bool onWake(); + void onSleep(); + void onPreRender(); + bool onKeyDown( const GuiEvent &event ); + void onMouseDown(const GuiEvent &event); + void onMiddleMouseDown(const GuiEvent &event); + void onMouseMove(const GuiEvent &event); + void onMouseEnter(const GuiEvent &event); + void onMouseLeave(const GuiEvent &event); + void onRightMouseDown(const GuiEvent &event); + void onRightMouseUp(const GuiEvent &event); + void onMouseDragged(const GuiEvent &event); + virtual void onMouseUp(const GuiEvent &event); + + /// Returns false if the object is a child of one of the inner items. + bool childSearch(Item * item, SimObject *obj, bool yourBaby); + + /// Find immediately available inspector items (eg ones that aren't children of other inspector items) + /// and then update their sets + void inspectorSearch(Item * item, Item * parent, SimSet * parentSet, SimSet * newParentSet); + + /// Find the Item associated with a sceneObject, returns true if it found one + bool objectSearch( const SimObject *object, Item **item ); + + // GuiArrayCtrl + void onRenderCell(Point2I offset, Point2I cell, bool, bool); + void onRender(Point2I offset, const RectI &updateRect); + + bool renderTooltip( const Point2I &hoverPos, const Point2I& cursorPos, const char* tipText ); + + static void initPersistFields(); + + void inspectObject(SimObject * obj, bool okToEdit); + void buildVisibleTree(bool bForceFullUpdate = false); + + void cancelRename(); + void onRenameValidate(); + void showItemRenameCtrl( Item* item ); + + DECLARE_CONOBJECT(GuiTreeViewCtrl); + DECLARE_CATEGORY( "Gui Lists" ); + DECLARE_DESCRIPTION( "Hierarchical list of text items with optional icons.\nCan also be used to inspect SimObject hierarchies." ); +}; + +#endif diff --git a/Engine/source/gui/core/guiArrayCtrl.cpp b/Engine/source/gui/core/guiArrayCtrl.cpp new file mode 100644 index 000000000..67a509e35 --- /dev/null +++ b/Engine/source/gui/core/guiArrayCtrl.cpp @@ -0,0 +1,569 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/core/guiArrayCtrl.h" + +#include "console/console.h" +#include "console/engineAPI.h" +#include "platform/event.h" +#include "gui/containers/guiScrollCtrl.h" +#include "gfx/gfxDrawUtil.h" +#include "gui/core/guiDefaultControlRender.h" + + +IMPLEMENT_CONOBJECT(GuiArrayCtrl); + +ConsoleDocClass( GuiArrayCtrl, + "@brief Abstract base class for controls that store and display multiple elements in a single view.\n\n" + + "You cannot actually instantiate this class. Instead you can use its childre:\n\n" + + "- GuiConsole\n" + "- GuiTextListCtrl\n" + "- GuiTreeViewCtrl\n" + "- DbgFileView\n" + "- CreatorTree\n" + + "This base class is primarily used by other internal classes or those dedicated to editors.\n\n" + + "@ingroup GuiCore\n" + + "@internal" +); + +IMPLEMENT_CALLBACK( GuiArrayCtrl, onCellSelected, void, ( const Point2I& cell ), ( cell ), + "Call when a cell in the array is selected (clicked).\n\n" + "@param @cell Coordinates of the cell" +); +IMPLEMENT_CALLBACK( GuiArrayCtrl, onCellHighlighted, void, ( const Point2I& cell ), ( cell ), + "Call when a cell in the array is highlighted (moused over).\n\n" + "@param @cell Coordinates of the cell" +); + +//----------------------------------------------------------------------------- + +GuiArrayCtrl::GuiArrayCtrl() +{ + mActive = true; + + mCellSize.set(80, 30); + mSize = Point2I(5, 30); + mSelectedCell.set(-1, -1); + mMouseOverCell.set(-1, -1); + mHeaderDim.set(0, 0); + mIsContainer = true; +} + +//----------------------------------------------------------------------------- + +bool GuiArrayCtrl::onWake() +{ + if (! Parent::onWake()) + return false; + + //get the font + mFont = mProfile->mFont; + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiArrayCtrl::onSleep() +{ + Parent::onSleep(); + mFont = NULL; +} + +//----------------------------------------------------------------------------- + +void GuiArrayCtrl::setSize(Point2I newSize) +{ + mSize = newSize; + Point2I newExtent(newSize.x * mCellSize.x + mHeaderDim.x, newSize.y * mCellSize.y + mHeaderDim.y); + + setExtent(newExtent); +} + +//----------------------------------------------------------------------------- + +void GuiArrayCtrl::getScrollDimensions(S32 &cell_size, S32 &num_cells) +{ + cell_size = mCellSize.y; + num_cells = mSize.y; +} + +//----------------------------------------------------------------------------- + +bool GuiArrayCtrl::cellSelected(Point2I cell) +{ + if (cell.x < 0 || cell.x >= mSize.x || cell.y < 0 || cell.y >= mSize.y) + { + mSelectedCell = Point2I(-1,-1); + return false; + } + + mSelectedCell = cell; + scrollSelectionVisible(); + onCellSelected(cell); + setUpdate(); + return true; +} + +//----------------------------------------------------------------------------- + +void GuiArrayCtrl::onCellSelected(Point2I cell) +{ + // [rene, 21-Jan-11 ] clashes with callbacks defined in derived classes + Con::executef(this, "onSelect", Con::getFloatArg(cell.x), Con::getFloatArg(cell.y)); + + onCellSelected_callback( cell ); + + //call the console function + execConsoleCallback(); +} + +//----------------------------------------------------------------------------- + +void GuiArrayCtrl::onCellHighlighted(Point2I cell) +{ + onCellHighlighted_callback( cell ); +} + +//----------------------------------------------------------------------------- + +void GuiArrayCtrl::setSelectedCell(Point2I cell) +{ + cellSelected(cell); +} + +//----------------------------------------------------------------------------- + +Point2I GuiArrayCtrl::getSelectedCell() +{ + return mSelectedCell; +} + +//----------------------------------------------------------------------------- + +void GuiArrayCtrl::scrollSelectionVisible() +{ + scrollCellVisible(mSelectedCell); +} + +//----------------------------------------------------------------------------- + +void GuiArrayCtrl::scrollCellVisible(Point2I cell) +{ + //make sure we have a parent + //make sure we have a valid cell selected + GuiScrollCtrl *parent = dynamic_cast(getParent()); + if(!parent || cell.x < 0 || cell.y < 0) + return; + + RectI cellBounds(cell.x * mCellSize.x, cell.y * mCellSize.y, mCellSize.x, mCellSize.y); + parent->scrollRectVisible(cellBounds); +} + +//----------------------------------------------------------------------------- + +void GuiArrayCtrl::onRenderColumnHeaders(Point2I offset, Point2I parentOffset, Point2I headerDim) +{ + if (mProfile->mBorder) + { + RectI cellR(offset.x + headerDim.x, parentOffset.y, getWidth() - headerDim.x, headerDim.y); + GFX->getDrawUtil()->drawRectFill(cellR, mProfile->mBorderColor); + } +} + +//----------------------------------------------------------------------------- + +void GuiArrayCtrl::onRenderRowHeader(Point2I offset, Point2I parentOffset, Point2I headerDim, Point2I cell) +{ + ColorI color; + RectI cellR; + if (cell.x % 2) + color.set(255, 0, 0, 255); + else + color.set(0, 255, 0, 255); + + cellR.point.set(parentOffset.x, offset.y); + cellR.extent.set(headerDim.x, mCellSize.y); + GFX->getDrawUtil()->drawRectFill(cellR, color); +} + +//----------------------------------------------------------------------------- + +void GuiArrayCtrl::onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver) +{ + ColorI color(255 * (cell.x % 2), 255 * (cell.y % 2), 255 * ((cell.x + cell.y) % 2), 255); + if (selected) + { + color.set(255, 0, 0, 255); + } + else if (mouseOver) + { + color.set(0, 0, 255, 255); + } + + //draw the cell + RectI cellR(offset.x, offset.y, mCellSize.x, mCellSize.y); + GFX->getDrawUtil()->drawRectFill(cellR, color); +} + +//----------------------------------------------------------------------------- + +void GuiArrayCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + // The unmodified offset which was passed into this method. + const Point2I inOffset( offset ); + + //Parent::onRender( offset, updateRect ); + + // We render our fill, borders, and child controls ourself. + // This allows us to render child controls after we render cells, + // so child controls appear on-top, as they should. + + // Render our fill and borders. + // This code from GuiControl::onRender(). + { + RectI ctrlRect(offset, getExtent()); + + //if opaque, fill the update rect with the fill color + if ( mProfile->mOpaque ) + GFX->getDrawUtil()->drawRectFill(ctrlRect, mProfile->mFillColor); + + //if there's a border, draw the border + if ( mProfile->mBorder ) + renderBorder(ctrlRect, mProfile); + } + + //make sure we have a parent + GuiControl *parent = getParent(); + if (! parent) + return; + + S32 i, j; + RectI headerClip; + RectI clipRect(updateRect.point, updateRect.extent); + + Point2I parentOffset = parent->localToGlobalCoord(Point2I(0, 0)); + + //if we have column headings + if (mHeaderDim.y > 0) + { + headerClip.point.x = parentOffset.x + mHeaderDim.x; + headerClip.point.y = parentOffset.y; + headerClip.extent.x = clipRect.extent.x;// - headerClip.point.x; // This seems to fix some strange problems with some Gui's, bug? -pw + headerClip.extent.y = mHeaderDim.y; + + if (headerClip.intersect(clipRect)) + { + GFX->setClipRect(headerClip); + + //now render the header + onRenderColumnHeaders(offset, parentOffset, mHeaderDim); + + clipRect.point.y = headerClip.point.y + headerClip.extent.y - 1; + } + offset.y += mHeaderDim.y; + } + + //if we have row headings + if (mHeaderDim.x > 0) + { + clipRect.point.x = getMax(clipRect.point.x, parentOffset.x + mHeaderDim.x); + offset.x += mHeaderDim.x; + } + + //save the original for clipping the row headers + RectI origClipRect = clipRect; + + for (j = 0; j < mSize.y; j++) + { + //skip until we get to a visible row + if ((j + 1) * mCellSize.y + offset.y < updateRect.point.y) + continue; + + //break once we've reached the last visible row + if(j * mCellSize.y + offset.y >= updateRect.point.y + updateRect.extent.y) + break; + + //render the header + if (mHeaderDim.x > 0) + { + headerClip.point.x = parentOffset.x; + headerClip.extent.x = mHeaderDim.x; + headerClip.point.y = offset.y + j * mCellSize.y; + headerClip.extent.y = mCellSize.y; + if (headerClip.intersect(origClipRect)) + { + GFX->setClipRect(headerClip); + + //render the row header + onRenderRowHeader(Point2I(0, offset.y + j * mCellSize.y), + Point2I(parentOffset.x, offset.y + j * mCellSize.y), + mHeaderDim, Point2I(0, j)); + } + } + + //render the cells for the row + for (i = 0; i < mSize.x; i++) + { + //skip past columns off the left edge + if ((i + 1) * mCellSize.x + offset.x < updateRect.point.x) + continue; + + //break once past the last visible column + if (i * mCellSize.x + offset.x >= updateRect.point.x + updateRect.extent.x) + break; + + S32 cellx = offset.x + i * mCellSize.x; + S32 celly = offset.y + j * mCellSize.y; + + RectI cellClip(cellx, celly, mCellSize.x, mCellSize.y); + + //make sure the cell is within the update region + if (cellClip.intersect(clipRect)) + { + //set the clip rect + GFX->setClipRect(cellClip); + + //render the cell + onRenderCell(Point2I(cellx, celly), Point2I(i, j), + i == mSelectedCell.x && j == mSelectedCell.y, + i == mMouseOverCell.x && j == mMouseOverCell.y); + } + } + } + + // Done rendering cells. + // Render child controls, if any, on top. + renderChildControls( inOffset, updateRect ); +} + +//----------------------------------------------------------------------------- + +void GuiArrayCtrl::onMouseDown( const GuiEvent &event ) +{ + if ( !mActive || !mAwake || !mVisible ) + return; +} + +//----------------------------------------------------------------------------- + +void GuiArrayCtrl::onMouseUp( const GuiEvent &event ) +{ + if ( !mActive || !mAwake || !mVisible ) + return; + + //let the guiControl method take care of the rest + Parent::onMouseUp(event); + + Point2I pt = globalToLocalCoord(event.mousePoint); + pt.x -= mHeaderDim.x; pt.y -= mHeaderDim.y; + Point2I cell( + (pt.x < 0 ? -1 : pt.x / mCellSize.x), + (pt.y < 0 ? -1 : pt.y / mCellSize.y) + ); + + if (cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y) + { + //store the previously selected cell + Point2I prevSelected = mSelectedCell; + + //select the new cell + cellSelected(Point2I(cell.x, cell.y)); + + //if we double clicked on the *same* cell, evaluate the altConsole Command + if ( ( event.mouseClickCount > 1 ) && ( prevSelected == mSelectedCell ) ) + execAltConsoleCallback(); + } +} + +//----------------------------------------------------------------------------- + +void GuiArrayCtrl::onMouseEnter(const GuiEvent &event) +{ + Point2I pt = globalToLocalCoord(event.mousePoint); + pt.x -= mHeaderDim.x; pt.y -= mHeaderDim.y; + + //get the cell + Point2I cell((pt.x < 0 ? -1 : pt.x / mCellSize.x), (pt.y < 0 ? -1 : pt.y / mCellSize.y)); + if (cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y) + { + mMouseOverCell = cell; + setUpdateRegion(Point2I(cell.x * mCellSize.x + mHeaderDim.x, + cell.y * mCellSize.y + mHeaderDim.y), mCellSize ); + onCellHighlighted(mMouseOverCell); + } +} + +//----------------------------------------------------------------------------- + +void GuiArrayCtrl::onMouseLeave(const GuiEvent & /*event*/) +{ + setUpdateRegion(Point2I(mMouseOverCell.x * mCellSize.x + mHeaderDim.x, + mMouseOverCell.y * mCellSize.y + mHeaderDim.y), mCellSize); + mMouseOverCell.set(-1,-1); + onCellHighlighted(mMouseOverCell); +} + +//----------------------------------------------------------------------------- + +void GuiArrayCtrl::onMouseDragged(const GuiEvent &event) +{ + // for the array control, the behavior of onMouseDragged is the same + // as on mouse moved - basically just recalc the current mouse over cell + // and set the update regions if necessary + GuiArrayCtrl::onMouseMove(event); +} + +//----------------------------------------------------------------------------- + +void GuiArrayCtrl::onMouseMove(const GuiEvent &event) +{ + Point2I pt = globalToLocalCoord(event.mousePoint); + pt.x -= mHeaderDim.x; pt.y -= mHeaderDim.y; + Point2I cell((pt.x < 0 ? -1 : pt.x / mCellSize.x), (pt.y < 0 ? -1 : pt.y / mCellSize.y)); + if (cell.x != mMouseOverCell.x || cell.y != mMouseOverCell.y) + { + if (mMouseOverCell.x != -1) + { + setUpdateRegion(Point2I(mMouseOverCell.x * mCellSize.x + mHeaderDim.x, + mMouseOverCell.y * mCellSize.y + mHeaderDim.y), mCellSize); + } + + if (cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y) + { + setUpdateRegion(Point2I(cell.x * mCellSize.x + mHeaderDim.x, + cell.y * mCellSize.y + mHeaderDim.y), mCellSize); + mMouseOverCell = cell; + } + else + mMouseOverCell.set(-1,-1); + } + onCellHighlighted(mMouseOverCell); +} + +//----------------------------------------------------------------------------- + +bool GuiArrayCtrl::onKeyDown(const GuiEvent &event) +{ + //if this control is a dead end, kill the event + if ((! mVisible) || (! mActive) || (! mAwake)) return true; + + //get the parent + S32 pageSize = 1; + GuiControl *parent = getParent(); + if (parent && mCellSize.y > 0) + { + pageSize = getMax(1, (parent->getHeight() / mCellSize.y) - 1); + } + + Point2I delta(0,0); + switch (event.keyCode) + { + case KEY_LEFT: + delta.set(-1, 0); + break; + case KEY_RIGHT: + delta.set(1, 0); + break; + case KEY_UP: + delta.set(0, -1); + break; + case KEY_DOWN: + delta.set(0, 1); + break; + case KEY_PAGE_UP: + delta.set(0, -pageSize); + break; + case KEY_PAGE_DOWN: + delta.set(0, pageSize); + break; + case KEY_HOME: + cellSelected( Point2I( 0, 0 ) ); + return( true ); + case KEY_END: + cellSelected( Point2I( 0, mSize.y - 1 ) ); + return( true ); + default: + return Parent::onKeyDown(event); + } + if (mSize.x < 1 || mSize.y < 1) + return true; + + //select the first cell if no previous cell was selected + if (mSelectedCell.x == -1 || mSelectedCell.y == -1) + { + cellSelected(Point2I(0,0)); + return true; + } + + //select the cell + Point2I cell = mSelectedCell; + cell.x = getMax(0, getMin(mSize.x - 1, cell.x + delta.x)); + cell.y = getMax(0, getMin(mSize.y - 1, cell.y + delta.y)); + cellSelected(cell); + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiArrayCtrl::onRightMouseDown(const GuiEvent &event) +{ + if ( !mActive || !mAwake || !mVisible ) + return; + + Parent::onRightMouseDown( event ); + + Point2I cell; + if( _findHitCell( event.mousePoint, cell ) ) + { + char buf[32]; + dSprintf( buf, sizeof( buf ), "%d %d", event.mousePoint.x, event.mousePoint.y ); + + // [rene, 21-Jan-11 ] clashes with callbacks defined in derived classes + + // Pass it to the console: + Con::executef(this, "onRightMouseDown", Con::getIntArg(cell.x), Con::getIntArg(cell.y), buf); + } +} + +//----------------------------------------------------------------------------- + +bool GuiArrayCtrl::_findHitCell( const Point2I& pos, Point2I& cellOut ) +{ + Point2I pt = globalToLocalCoord( pos ); + pt.x -= mHeaderDim.x; pt.y -= mHeaderDim.y; + Point2I cell( ( pt.x < 0 ? -1 : pt.x / mCellSize.x ), ( pt.y < 0 ? -1 : pt.y / mCellSize.y ) ); + if( cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y ) + { + cellOut = cell; + return true; + } + + return false; +} \ No newline at end of file diff --git a/Engine/source/gui/core/guiArrayCtrl.h b/Engine/source/gui/core/guiArrayCtrl.h new file mode 100644 index 000000000..2a7810b27 --- /dev/null +++ b/Engine/source/gui/core/guiArrayCtrl.h @@ -0,0 +1,108 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIARRAYCTRL_H_ +#define _GUIARRAYCTRL_H_ + +#ifndef _GUITYPES_H_ +#include "gui/core/guiTypes.h" +#endif +#ifndef _GUITEXTCTRL_H_ +#include "gui/controls/guiTextCtrl.h" +#endif + +/// Renders a grid of cells. +class GuiArrayCtrl : public GuiControl +{ + typedef GuiControl Parent; + +protected: + + Point2I mHeaderDim; + Point2I mSize; + Point2I mCellSize; + Point2I mSelectedCell; + Point2I mMouseOverCell; + + Resource mFont; + + virtual bool cellSelected(Point2I cell); + virtual void onCellSelected(Point2I cell); + virtual void onCellHighlighted(Point2I cell); + + bool _findHitCell( const Point2I& pos, Point2I& cellOut ); + + /// @name Callbacks + /// @{ + + DECLARE_CALLBACK( void, onCellHighlighted, ( const Point2I& cell ) ); + DECLARE_CALLBACK( void, onCellSelected, ( const Point2I& cell ) ); + + /// @} + +public: + + GuiArrayCtrl(); + DECLARE_CONOBJECT(GuiArrayCtrl); + + bool onWake(); + void onSleep(); + + /// @name Array attribute methods + /// @{ + Point2I getSize() { return mSize; } + virtual void setSize(Point2I size); + void setHeaderDim(const Point2I &dim) { mHeaderDim = dim; } + void getScrollDimensions(S32 &cell_size, S32 &num_cells); + /// @} + + /// @name Selected cell methods + /// @{ + void setSelectedCell(Point2I cell); + void deselectCells() { mSelectedCell.set(-1,-1); } + Point2I getSelectedCell(); + void scrollSelectionVisible(); + void scrollCellVisible(Point2I cell); + /// @} + + /// @name Rendering methods + /// @{ + virtual void onRenderColumnHeaders(Point2I offset, Point2I parentOffset, Point2I headerDim); + virtual void onRenderRowHeader(Point2I offset, Point2I parentOffset, Point2I headerDim, Point2I cell); + virtual void onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver); + void onRender(Point2I offset, const RectI &updateRect); + /// @} + + /// @name Mouse input methods + /// @{ + void onMouseDown( const GuiEvent &event ); + void onMouseUp( const GuiEvent &event ); + void onMouseMove( const GuiEvent &event ); + void onMouseDragged( const GuiEvent &event ); + void onMouseEnter( const GuiEvent &event ); + void onMouseLeave( const GuiEvent &event ); + bool onKeyDown( const GuiEvent &event ); + void onRightMouseDown( const GuiEvent &event ); + /// @} +}; + +#endif //_GUI_ARRAY_CTRL_H diff --git a/Engine/source/gui/core/guiCanvas.cpp b/Engine/source/gui/core/guiCanvas.cpp new file mode 100644 index 000000000..00a669646 --- /dev/null +++ b/Engine/source/gui/core/guiCanvas.cpp @@ -0,0 +1,2652 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/core/guiCanvas.h" + +#include "console/console.h" +#include "console/engineAPI.h" +#include "platform/profiler.h" +#include "platform/event.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxDrawUtil.h" +#include "gui/core/guiTypes.h" +#include "gui/core/guiControl.h" +#include "console/consoleTypes.h" +#include "gfx/screenshot.h" +#include "gfx/video/videoCapture.h" +#include "lighting/lightManager.h" +#include "core/strings/stringUnit.h" + +#ifndef TORQUE_TGB_ONLY +#include "scene/sceneObject.h" +#endif + +#include "gfx/gfxInit.h" +#include "core/util/journal/process.h" + +#ifdef TORQUE_GFX_STATE_DEBUG +#include "gfx/gfxDebugStateTracker.h" +#endif + +IMPLEMENT_CONOBJECT(GuiCanvas); + +ConsoleDocClass( GuiCanvas, + "@brief A canvas on which rendering occurs.\n\n" + + "@section GuiCanvas_contents What a GUICanvas Can Contain...\n\n" + + "@subsection GuiCanvas_content_contentcontrol Content Control\n" + "A content control is the top level GuiControl for a screen. This GuiControl " + "will be the parent control for all other GuiControls on that particular " + "screen.\n\n" + + "@subsection GuiCanvas_content_dialogs Dialogs\n\n" + + "A dialog is essentially another screen, only it gets overlaid on top of the " + "current content control, and all input goes to the dialog. This is most akin " + "to the \"Open File\" dialog box found in most operating systems. When you " + "choose to open a file, and the \"Open File\" dialog pops up, you can no longer " + "send input to the application, and must complete or cancel the open file " + "request. Torque keeps track of layers of dialogs. The dialog with the highest " + "layer is on top and will get all the input, unless the dialog is " + "modeless, which is a profile option.\n\n" + + "@see GuiControlProfile\n\n" + + "@section GuiCanvas_dirty Dirty Rectangles\n\n" + + "The GuiCanvas is based on dirty regions. " + "Every frame the canvas paints only the areas of the canvas that are 'dirty' " + "or need updating. In most cases, this only is the area under the mouse cursor. " + "This is why if you look in guiCanvas.cc the call to glClear is commented out. " + + "What you will see is a black screen, except in the dirty regions, where the " + "screen will be painted normally. If you are making an animated GuiControl " + "you need to add your control to the dirty areas of the canvas.\n\n" + + "@see GuiControl\n\n" + + "@ingroup GuiCore\n"); + +ColorI gCanvasClearColor( 255, 0, 255 ); ///< For GFX->clear + +extern InputModifiers convertModifierBits(const U32 in); + +//----------------------------------------------------------------------------- + +GuiCanvas::GuiCanvas(): GuiControl(), + mCursorEnabled(true), + mForceMouseToGUI(false), + mAlwaysHandleMouseButtons(false), + mClampTorqueCursor(true), + mShowCursor(true), + mLastCursorEnabled(false), + mMouseControl(NULL), + mMouseCapturedControl(NULL), + mMouseControlClicked(false), + mMouseButtonDown(false), + mMouseRightButtonDown(false), + mMouseMiddleButtonDown(false), + mDefaultCursor(NULL), + mLastCursor(NULL), + mLastCursorPt(0,0), + mCursorPt(0,0), + mLastMouseClickCount(0), + mLastMouseDownTime(0), + mPrevMouseTime(0), + mRenderFront(false), + mHoverControl(NULL), + mHoverPositionSet(false), + mHoverLeftControlTime(0), + mLeftMouseLast(false), + mMiddleMouseLast(false), + mRightMouseLast(false), + mPlatformWindow(NULL), + mLastRenderMs(0) +{ + setBounds(0, 0, 640, 480); + mAwake = true; + + mHoverControlStart = Platform::getRealMilliseconds(); + mHoverPosition = getCursorPos(); + + mFences = NULL; + mNextFenceIdx = -1; + +#ifndef _XBOX + mNumFences = Con::getIntVariable( "$pref::Video::defaultFenceCount", 0 ); +#else + mNumFences = 0; +#endif + +#ifdef TORQUE_DEMO_PURCHASE + mPurchaseScreen = NULL; +#endif +} + +GuiCanvas::~GuiCanvas() +{ + SAFE_DELETE(mPlatformWindow); + SAFE_DELETE_ARRAY( mFences ); + +#ifdef TORQUE_DEMO_PURCHASE + // if (mPurchaseScreen) + // { + // SAFE_DELETE(mPurchaseScreen); + // } +#endif +} + +//------------------------------------------------------------------------------ + +bool GuiCanvas::setProtectedNumFences( void *object, const char *index, const char *data) +{ + GuiCanvas *canvas = reinterpret_cast( object ); + canvas->mNumFences = dAtoi( data ); + canvas->setupFences(); + + return false; +} + +void GuiCanvas::initPersistFields() +{ + addGroup("Mouse Handling"); + addField("alwaysHandleMouseButtons", TypeBool, Offset(mAlwaysHandleMouseButtons, GuiCanvas), + "Deal with mouse buttons, even if the cursor is hidden." ); + endGroup("Mouse Handling"); + + addGroup("Canvas Rendering"); + addProtectedField( "numFences", TypeS32, Offset( mNumFences, GuiCanvas ), &setProtectedNumFences, &defaultProtectedGetFn, "The number of GFX fences to use." ); + endGroup("Canvas Rendering"); + + Parent::initPersistFields(); +} + +//------------------------------------------------------------------------------ + +bool GuiCanvas::onAdd() +{ + // ensure that we have a cursor + setCursor(dynamic_cast(Sim::findObject("DefaultCursor"))); + + // Enumerate things for GFX before we have an active device. + GFXInit::enumerateAdapters(); + + // Create a device. + GFXAdapter *a = GFXInit::getBestAdapterChoice(); + + // Do we have a global device already? (This is the site if you want + // to start rendering to multiple devices simultaneously) + GFXDevice *newDevice = GFX; + if(newDevice == NULL) + newDevice = GFXInit::createDevice(a); + + newDevice->setAllowRender( false ); + + // Disable starting a new journal recording or playback from here on + Journal::Disable(); + + // Initialize the window... + GFXVideoMode vm = GFXInit::getInitialVideoMode(); + + //If we're recording, store the intial video resolution + if (Journal::IsRecording()) + { + Journal::Write(vm.resolution.x); + Journal::Write(vm.resolution.y); + Journal::Write(vm.fullScreen); + } + + //If we're playing, read the intial video resolution from the journal + if (Journal::IsPlaying()) + { + Journal::Read(&vm.resolution.x); + Journal::Read(&vm.resolution.y); + Journal::Read(&vm.fullScreen); + } + + if (a && a->mType != NullDevice) + { + mPlatformWindow = WindowManager->createWindow(newDevice, vm); + + //Disable window resizing if recording ir playing a journal + if (Journal::IsRecording() || Journal::IsPlaying()) + mPlatformWindow->lockSize(true); + + // Set a minimum on the window size so people can't break us by resizing tiny. + mPlatformWindow->setMinimumWindowSize(Point2I(640,480)); + + // Now, we have to hook in our event callbacks so we'll get + // appropriate events from the window. + mPlatformWindow->resizeEvent .notify(this, &GuiCanvas::handleResize); + mPlatformWindow->appEvent .notify(this, &GuiCanvas::handleAppEvent); + mPlatformWindow->displayEvent.notify(this, &GuiCanvas::handlePaintEvent); + mPlatformWindow->setInputController( dynamic_cast(this) ); + } + + // Need to get painted, too! :) + Process::notify(this, &GuiCanvas::paint, PROCESS_RENDER_ORDER); + + // Set up the fences + setupFences(); + + // Make sure we're able to render. + newDevice->setAllowRender( true ); + + // Propagate add to parents. + // CodeReview - if GuiCanvas fails to add for whatever reason, what happens to + // all the event registration above? + bool parentRet = Parent::onAdd(); + + // Define the menu bar for this canvas (if any) + Con::executef(this, "onCreateMenu"); + +#ifdef TORQUE_DEMO_PURCHASE + mPurchaseScreen = new PurchaseScreen; + mPurchaseScreen->init(); + + mLastPurchaseHideTime = 0; +#endif + + return parentRet; +} + +void GuiCanvas::onRemove() +{ +#ifdef TORQUE_DEMO_PURCHASE + if (mPurchaseScreen && mPurchaseScreen->isAwake()) + removeObject(mPurchaseScreen); +#endif + + // And the process list + Process::remove(this, &GuiCanvas::paint); + + // Destroy the menu bar for this canvas (if any) + Con::executef(this, "onDestroyMenu"); + + Parent::onRemove(); +} + +void GuiCanvas::setWindowTitle(const char *newTitle) +{ + if (mPlatformWindow) + mPlatformWindow->setCaption(newTitle); +} + +void GuiCanvas::handleResize( WindowId did, S32 width, S32 height ) +{ + if (Journal::IsPlaying() && mPlatformWindow) + { + mPlatformWindow->lockSize(false); + mPlatformWindow->setSize(Point2I(width, height)); + mPlatformWindow->lockSize(true); + } + + // Notify the scripts + if ( isMethod( "onResize" ) ) + Con::executef( this, "onResize", Con::getIntArg( width ), Con::getIntArg( height ) ); +} + +void GuiCanvas::handlePaintEvent(WindowId did) +{ + bool canRender = mPlatformWindow->isVisible() && GFX->allowRender() && !GFX->canCurrentlyRender(); + + // Do the screenshot first. + if ( gScreenShot != NULL && gScreenShot->isPending() && canRender ) + gScreenShot->capture( this ); + + // If the video capture is waiting for a canvas, start the capture + if ( VIDCAP->isWaitingForCanvas() && canRender ) + VIDCAP->begin( this ); + + // Now capture the video + if ( VIDCAP->isRecording() && canRender ) + VIDCAP->capture(); + + renderFrame(false); +} + +void GuiCanvas::handleAppEvent( WindowId did, S32 event ) +{ + // Notify script if we gain or lose focus. + if(event == LoseFocus) + { + if(isMethod("onLoseFocus")) + Con::executef(this, "onLoseFocus"); + } + + if(event == GainFocus) + { + if(isMethod("onGainFocus")) + Con::executef(this, "onGainFocus"); + } + + if(event == WindowClose || event == WindowDestroy) + { + + if(isMethod("onWindowClose")) + { + // First see if there is a method on this window to handle + // it's closure + Con::executef(this,"onWindowClose"); + } + else if(Con::isFunction("onWindowClose")) + { + // otherwise check to see if there is a global function handling it + Con::executef("onWindowClose", getIdString()); + } + else + { + // Else just shutdown + Process::requestShutdown(); + } + } +} + +Point2I GuiCanvas::getWindowSize() +{ + // CodeReview Asserting on this breaks previous logic + // and code assumptions. It seems logical that we would + // handle this and return an error value rather than implementing + // if(!mPlatformWindow) whenever we need to call getWindowSize. + // This should help keep our API error free and easy to use, while + // cutting down on code duplication for sanity checking. [5/5/2007 justind] + if( !mPlatformWindow ) + return Point2I(-1,-1); + + return mPlatformWindow->getClientExtent(); +} + +void GuiCanvas::enableKeyboardTranslation() +{ + AssertISV(mPlatformWindow, "GuiCanvas::enableKeyboardTranslation - no window present!"); + mPlatformWindow->setKeyboardTranslation(true); +} + +void GuiCanvas::disableKeyboardTranslation() +{ + AssertISV(mPlatformWindow, "GuiCanvas::disableKeyboardTranslation - no window present!"); + mPlatformWindow->setKeyboardTranslation(false); +} + +void GuiCanvas::setNativeAcceleratorsEnabled( bool enabled ) +{ + AssertISV(mPlatformWindow, "GuiCanvas::setNativeAcceleratorsEnabled - no window present!"); + mPlatformWindow->setAcceleratorsEnabled( enabled ); +} + +void GuiCanvas::setForceMouseToGUI(bool onOff) +{ + mForceMouseToGUI = onOff; +} + +void GuiCanvas::setClampTorqueCursor(bool onOff) +{ + mClampTorqueCursor = onOff; +} + +void GuiCanvas::setCursor(GuiCursor *curs) +{ + mDefaultCursor = curs; +} + +void GuiCanvas::setCursorON(bool onOff) +{ + mCursorEnabled = onOff; + if(!mCursorEnabled) + mMouseControl = NULL; +} + +Point2I GuiCanvas::getCursorPos() +{ + Point2I p( 0, 0 ); + + if( mPlatformWindow ) + mPlatformWindow->getCursorPosition( p ); + + return p; +} + +void GuiCanvas::setCursorPos(const Point2I &pt) +{ + AssertISV(mPlatformWindow, "GuiCanvas::setCursorPos - no window present!"); + + if ( mPlatformWindow->isMouseLocked() ) + { + mCursorPt.x = F32( pt.x ); + mCursorPt.y = F32( pt.y ); + } + else + { + Point2I screenPt( mPlatformWindow->clientToScreen( pt ) ); + mPlatformWindow->setCursorPosition( screenPt.x, screenPt.y ); + } +} + +void GuiCanvas::showCursor(bool state) +{ + mShowCursor = state; + mPlatformWindow->setCursorVisible( state ); +} + +bool GuiCanvas::isCursorShown() +{ + if ( !mPlatformWindow->getCursorController() ) + { + return mShowCursor; + } + + return mPlatformWindow->isCursorVisible(); +} + +void GuiCanvas::addAcceleratorKey(GuiControl *ctrl, U32 index, U32 keyCode, U32 modifier) +{ + if (keyCode > 0 && ctrl) + { + AccKeyMap newMap; + newMap.ctrl = ctrl; + newMap.index = index; + newMap.keyCode = keyCode; + newMap.modifier = modifier; + mAcceleratorMap.push_back(newMap); + } +} + +bool GuiCanvas::tabNext(void) +{ + GuiControl *ctrl = static_cast(last()); + if (ctrl) + { + //save the old + GuiControl *oldResponder = mFirstResponder; + + GuiControl* newResponder = ctrl->findNextTabable(mFirstResponder); + if ( !newResponder ) + newResponder = ctrl->findFirstTabable(); + + if ( newResponder && newResponder != oldResponder ) + { + newResponder->setFirstResponder(); + + // CodeReview Can this get killed? Note tabPrev code. BJG - 3/25/07 +// if ( oldResponder ) +// oldResponder->onLoseFirstResponder(); + return true; + } + } + return false; +} + +bool GuiCanvas::tabPrev(void) +{ + GuiControl *ctrl = static_cast(last()); + if (ctrl) + { + //save the old + GuiControl *oldResponder = mFirstResponder; + + GuiControl* newResponder = ctrl->findPrevTabable(mFirstResponder); + if ( !newResponder ) + newResponder = ctrl->findLastTabable(); + + if ( newResponder && newResponder != oldResponder ) + { + newResponder->setFirstResponder(); + + // CodeReview As with tabNext() above, looks like this can now go. DAW - 7/05/09 + //if ( oldResponder ) + // oldResponder->onLoseFirstResponder(); + + return true; + } + } + return false; +} + +bool GuiCanvas::processInputEvent(InputEventInfo &inputEvent) +{ + // First call the general input handler (on the extremely off-chance that it will be handled): + if (mFirstResponder && mFirstResponder->onInputEvent(inputEvent)) + { + return(true); + } + + switch (inputEvent.deviceType) + { + case KeyboardDeviceType: + return processKeyboardEvent(inputEvent); + break; + + case GamepadDeviceType: + return processGamepadEvent(inputEvent); + break; + + case MouseDeviceType: + if (mCursorEnabled || mForceMouseToGUI || + (mAlwaysHandleMouseButtons && inputEvent.objType == SI_BUTTON) ) + { + return processMouseEvent(inputEvent); + } + break; + default: + break; + } + + return false; +} + +bool GuiCanvas::processKeyboardEvent(InputEventInfo &inputEvent) +{ + mLastEvent.ascii = inputEvent.ascii; + mLastEvent.modifier = inputEvent.modifier; + mLastEvent.keyCode = inputEvent.objInst; + + // Combine left/right shift bits - if one shift modifier key + // bit is set, then set the other one. This way we can simplify + // our processing logic by treating the keys identically. + U32 eventModifier = inputEvent.modifier; + if(eventModifier & SI_SHIFT) + { + eventModifier |= SI_SHIFT; + } + if(eventModifier & SI_CTRL) + { + eventModifier |= SI_CTRL; + } + if(eventModifier & SI_ALT) + { + eventModifier |= SI_ALT; + } + + if (inputEvent.action == SI_MAKE) + { + //see if we should now pass the event to the first responder + if (mFirstResponder) + { + if(mFirstResponder->onKeyDown(mLastEvent)) + return true; + } + + //see if we should tab next/prev + if ( isCursorON() && ( inputEvent.objInst == KEY_TAB ) ) + { + if (size() > 0) + { + if (inputEvent.modifier & SI_SHIFT) + { + if(tabPrev()) + return true; + } + else if (inputEvent.modifier == 0) + { + if(tabNext()) + return true; + } + } + } + + //if not handled, search for an accelerator + for (U32 i = 0; i < mAcceleratorMap.size(); i++) + { + if ((U32)mAcceleratorMap[i].keyCode == (U32)inputEvent.objInst && (U32)mAcceleratorMap[i].modifier == eventModifier) + { + mAcceleratorMap[i].ctrl->acceleratorKeyPress(mAcceleratorMap[i].index); + return true; + } + } + } + else if(inputEvent.action == SI_BREAK) + { + if(mFirstResponder && mFirstResponder->onKeyUp(mLastEvent)) + return true; + + //see if there's an accelerator + for (U32 i = 0; i < mAcceleratorMap.size(); i++) + { + if ((U32)mAcceleratorMap[i].keyCode == (U32)inputEvent.objInst && (U32)mAcceleratorMap[i].modifier == eventModifier) + { + mAcceleratorMap[i].ctrl->acceleratorKeyRelease(mAcceleratorMap[i].index); + return true; + } + } + } + else if(inputEvent.action == SI_REPEAT) + { + //if not handled, search for an accelerator + for (U32 i = 0; i < mAcceleratorMap.size(); i++) + { + if ((U32)mAcceleratorMap[i].keyCode == (U32)inputEvent.objInst && (U32)mAcceleratorMap[i].modifier == eventModifier) + { + mAcceleratorMap[i].ctrl->acceleratorKeyPress(mAcceleratorMap[i].index); + return true; + } + } + + if(mFirstResponder) + { + return mFirstResponder->onKeyRepeat(mLastEvent); + } + } + return false; +} + +bool GuiCanvas::processMouseEvent(InputEventInfo &inputEvent) +{ + // [rene 09/09/10] This custom mouse cursor tracking that is happening here is bad. It will frequently + // get ouf of step with where the cursor actually is. We really should *not* track the cursor; it's + // just another thing that can/will go wrong. Let the input system pass us absolute screen coordinates + // for every mouse event instead and work off that. + // + // 'mCursorPt' basically is an accumulation of errors and the number of bugs that have cropped up with + // the GUI clicking stuff where it is not supposed to are probably all to blame on this. + + // Need to query platform for specific things + AssertISV(mPlatformWindow, "GuiCanvas::processMouseEvent - no window present!"); + PlatformCursorController *pController = mPlatformWindow->getCursorController(); + AssertFatal(pController != NULL, "GuiCanvas::processInputEvent - No Platform Controller Found") + + //copy the modifier into the new event + mLastEvent.modifier = inputEvent.modifier; + + if(inputEvent.objType == SI_AXIS && + (inputEvent.objInst == SI_XAXIS || inputEvent.objInst == SI_YAXIS)) + { + + // Set the absolute position if we get an SI_MAKE on an axis + if( inputEvent.objInst == SI_XAXIS ) + { + if( inputEvent.action == SI_MAKE ) + mCursorPt.x = (S32)inputEvent.fValue; + else if( inputEvent.action == SI_MOVE ) + mCursorPt.x += (S32)inputEvent.fValue; + mCursorPt.x = getMax(0, getMin((S32)mCursorPt.x, getBounds().extent.x - 1)); + } + else if( inputEvent.objInst == SI_YAXIS ) + { + if( inputEvent.action == SI_MAKE ) + mCursorPt.y = (S32)inputEvent.fValue; + else if( inputEvent.action == SI_MOVE ) + mCursorPt.y += (S32)inputEvent.fValue; + mCursorPt.y = getMax(0, getMin((S32)mCursorPt.y, getBounds().extent.y - 1)); + } + + // Store new cursor position. + mLastEvent.mousePoint.x = S32(mCursorPt.x); + mLastEvent.mousePoint.y = S32(mCursorPt.y); + + // See if we need to invalidate a possible dbl click due to the cursor + // moving too much. + Point2F movement = mMouseDownPoint - mCursorPt; + + if ((mAbs((S32)movement.x) > pController->getDoubleClickWidth()) || (mAbs((S32)movement.y) > pController->getDoubleClickHeight() ) ) + { + mLeftMouseLast = false; + mMiddleMouseLast = false; + mRightMouseLast = false; + } + + if (mMouseButtonDown) + rootMouseDragged(mLastEvent); + else if (mMouseRightButtonDown) + rootRightMouseDragged(mLastEvent); + else if(mMouseMiddleButtonDown) + rootMiddleMouseDragged(mLastEvent); + else + rootMouseMove(mLastEvent); + return true; + } + else if ( inputEvent.objInst == SI_ZAXIS + || inputEvent.objInst == SI_RZAXIS ) + { + mLastEvent.mousePoint.x = S32( mCursorPt.x ); + mLastEvent.mousePoint.y = S32( mCursorPt.y ); + mLastEvent.fval = inputEvent.fValue; + + if( inputEvent.objInst == SI_ZAXIS ) + mLastEvent.mouseAxis = 1; + else + mLastEvent.mouseAxis = 0; + + if ( inputEvent.fValue < 0.0f ) + return rootMouseWheelDown( mLastEvent ); + else + return rootMouseWheelUp( mLastEvent ); + } + else if(inputEvent.objType == SI_BUTTON) + { + //copy the cursor point into the event + mLastEvent.mousePoint.x = S32(mCursorPt.x); + mLastEvent.mousePoint.y = S32(mCursorPt.y); + mMouseDownPoint = mCursorPt; + + if(inputEvent.objInst == KEY_BUTTON0) // left button + { + //see if button was pressed + if (inputEvent.action == SI_MAKE) + { + U32 curTime = Platform::getVirtualMilliseconds(); + + //if the last button pressed was the left... + if (mLeftMouseLast) + { + //if it was within the double click time count the clicks + if (curTime - mLastMouseDownTime <= pController->getDoubleClickTime()) + mLastMouseClickCount++; + else + mLastMouseClickCount = 1; + } + else + { + mLeftMouseLast = true; + mLastMouseClickCount = 1; + } + + mLastMouseDownTime = curTime; + mLastEvent.mouseClickCount = mLastMouseClickCount; + + rootMouseDown(mLastEvent); + } + //else button was released + else + { + rootMouseUp(mLastEvent); + } + + return true; + } + else if(inputEvent.objInst == KEY_BUTTON1) // right button + { + if(inputEvent.action == SI_MAKE) + { + U32 curTime = Platform::getVirtualMilliseconds(); + + //if the last button pressed was the right... + if (mRightMouseLast) + { + //if it was within the double click time count the clicks + if (curTime - mLastMouseDownTime <= pController->getDoubleClickTime()) + mLastMouseClickCount++; + else + mLastMouseClickCount = 1; + } + else + { + mRightMouseLast = true; + mLastMouseClickCount = 1; + } + + mLastMouseDownTime = curTime; + mLastEvent.mouseClickCount = mLastMouseClickCount; + + rootRightMouseDown(mLastEvent); + } + else // it was a mouse up + rootRightMouseUp(mLastEvent); + + return true; + } + else if(inputEvent.objInst == KEY_BUTTON2) // middle button + { + if(inputEvent.action == SI_MAKE) + { + U32 curTime = Platform::getVirtualMilliseconds(); + + //if the last button pressed was the right... + if (mMiddleMouseLast) + { + //if it was within the double click time count the clicks + if (curTime - mLastMouseDownTime <= pController->getDoubleClickTime()) + mLastMouseClickCount++; + else + mLastMouseClickCount = 1; + } + else + { + mMiddleMouseLast = true; + mLastMouseClickCount = 1; + } + + mLastMouseDownTime = curTime; + mLastEvent.mouseClickCount = mLastMouseClickCount; + + rootMiddleMouseDown(mLastEvent); + } + else // it was a mouse up + rootMiddleMouseUp(mLastEvent); + + return true; + } + } + return false; +} + +bool GuiCanvas::processGamepadEvent(InputEventInfo &inputEvent) +{ + if (! mFirstResponder) + { + // early out, no first responder to receive gamepad input + return false; + } + + if (inputEvent.deviceInst >= MAX_GAMEPADS) + { + // early out, we only support the first MAX_GAMEPADS gamepads + return false; + } + + mLastEvent.keyCode = inputEvent.objInst; + + if (inputEvent.objType == SI_BUTTON) + { + switch (inputEvent.action) + { + case SI_MAKE: + switch (inputEvent.objInst) + { + case SI_UPOV: + return mFirstResponder->onGamepadAxisUp(mLastEvent); + + case SI_DPOV: + return mFirstResponder->onGamepadAxisDown(mLastEvent); + + case SI_LPOV: + return mFirstResponder->onGamepadAxisLeft(mLastEvent); + + case SI_RPOV: + return mFirstResponder->onGamepadAxisRight(mLastEvent); + + default: + return mFirstResponder->onGamepadButtonDown(mLastEvent); + } + break; + + case SI_BREAK: + return mFirstResponder->onGamepadButtonUp(mLastEvent); + + default: + return false; + } + } + else if (inputEvent.objType == SI_AXIS) + { + F32 incomingValue = mFabs(inputEvent.fValue); + static const F32 DEAD_ZONE = 0.5f; + static const F32 MIN_CLICK_TIME = 500.0f; + static const F32 MAX_CLICK_TIME = 1000.0f; + static F32 xDecay [] = {1.0f, 1.0f, 1.0f, 1.0f}; + static F32 yDecay [] = {1.0f, 1.0f, 1.0f, 1.0f}; + static F32 zDecay [] = {1.0f, 1.0f, 1.0f, 1.0f}; + static U32 xLastClickTime [] = {0, 0, 0, 0}; + static U32 yLastClickTime [] = {0, 0, 0, 0}; + static U32 zLastClickTime [] = {0, 0, 0, 0}; + U32 curTime = Platform::getRealMilliseconds(); + F32 *decay; + U32 *lastClickTime; + + switch (inputEvent.objInst) + { + case SI_ZAXIS: + case XI_LEFT_TRIGGER: + case XI_RIGHT_TRIGGER: + decay = &zDecay[inputEvent.deviceInst]; + lastClickTime = &zLastClickTime[inputEvent.deviceInst]; + break; + + case SI_YAXIS: + case XI_THUMBLY: + case XI_THUMBRY: + decay = &yDecay[inputEvent.deviceInst]; + lastClickTime = &yLastClickTime[inputEvent.deviceInst]; + break; + + case SI_XAXIS: + case XI_THUMBLX: + case XI_THUMBRX: + default: + decay = &xDecay[inputEvent.deviceInst]; + lastClickTime = &xLastClickTime[inputEvent.deviceInst]; + break; + } + + if (incomingValue < DEAD_ZONE) + { + // early out, control movement is within the deadzone + *decay = 1.0f; + *lastClickTime = 0; + return false; + } + + // Rescales the input between 0.0 and 1.0 + incomingValue = (incomingValue - DEAD_ZONE) * (1.0f / (1.0f - DEAD_ZONE)); + + F32 clickTime = MIN_CLICK_TIME + (MAX_CLICK_TIME - MIN_CLICK_TIME) * (1.0f - incomingValue); + clickTime *= *decay; + + if (clickTime < (curTime - *lastClickTime)) + { + *decay *= 0.9f; + if (*decay < 0.2f) + { + *decay = 0.2f; + } + + *lastClickTime = curTime; + + bool negative = (inputEvent.fValue < 0.0f); + + switch (inputEvent.objInst) + { + case XI_LEFT_TRIGGER: + case XI_RIGHT_TRIGGER: + return mFirstResponder->onGamepadTrigger(mLastEvent); + + case SI_ZAXIS: + case SI_YAXIS: + case XI_THUMBLY: + case XI_THUMBRY: + if (negative) + { + return mFirstResponder->onGamepadAxisDown(mLastEvent); + } + else + { + return mFirstResponder->onGamepadAxisUp(mLastEvent); + } + + case SI_XAXIS: + case XI_THUMBLX: + case XI_THUMBRX: + default: + if (negative) + { + return mFirstResponder->onGamepadAxisLeft(mLastEvent); + } + else + { + return mFirstResponder->onGamepadAxisRight(mLastEvent); + } + } + } + } + return false; +} + +void GuiCanvas::rootMouseDown(const GuiEvent &event) +{ + mPrevMouseTime = Platform::getVirtualMilliseconds(); + mMouseButtonDown = true; + + //pass the event to the mouse locked control + if (bool(mMouseCapturedControl)) + mMouseCapturedControl->onMouseDown(event); + else + { + //else pass it to whoever is underneath the cursor + iterator i; + i = end(); + while (i != begin()) + { + i--; + GuiControl *ctrl = static_cast(*i); + GuiControl *controlHit = ctrl->findHitControl(event.mousePoint); + + //see if the controlHit is a modeless dialog... + if( !controlHit->getControlProfile()->mModal ) + continue; + else + { + controlHit->onMouseDown(event); + break; + } + } + } + + if (bool(mMouseControl)) + mMouseControlClicked = true; +} + +void GuiCanvas::findMouseControl(const GuiEvent &event) +{ + // Any children at all? + if(size() == 0) + { + mMouseControl = NULL; + return; + } + + // Otherwise, check the point and find the overlapped control. + GuiControl *controlHit = findHitControl(event.mousePoint); + if(controlHit != static_cast(mMouseControl)) + { + if(bool(mMouseControl)) + mMouseControl->onMouseLeave(event); + mMouseControl = controlHit; + mMouseControl->onMouseEnter(event); + } +} + +void GuiCanvas::refreshMouseControl() +{ + GuiEvent evt; + evt.mousePoint.x = S32(mCursorPt.x); + evt.mousePoint.y = S32(mCursorPt.y); + findMouseControl(evt); +} + +void GuiCanvas::checkLockMouseMove( const GuiEvent& event ) +{ + GuiControl* controlHit = findHitControl( event.mousePoint ); + if( controlHit != mMouseControl ) + { + if( mMouseControl == mMouseCapturedControl ) + mMouseCapturedControl->onMouseLeave( event ); + else if( controlHit == mMouseCapturedControl ) + mMouseCapturedControl->onMouseEnter( event ); + + mMouseControl = controlHit; + } +} + +void GuiCanvas::rootMouseUp(const GuiEvent &event) +{ + mPrevMouseTime = Platform::getVirtualMilliseconds(); + mMouseButtonDown = false; + + // pass the event to the mouse locked control + if (bool(mMouseCapturedControl)) + { + checkLockMouseMove( event ); + mMouseCapturedControl->onMouseUp(event); + } + else + { + findMouseControl(event); + if(bool(mMouseControl)) + mMouseControl->onMouseUp(event); + } +} + +void GuiCanvas::rootMouseDragged(const GuiEvent &event) +{ + //pass the event to the mouse locked control + if (bool(mMouseCapturedControl)) + { + checkLockMouseMove( event ); + mMouseCapturedControl->onMouseDragged(event); + } + else + { + findMouseControl(event); + if(bool(mMouseControl)) + mMouseControl->onMouseDragged(event); + } +} + +void GuiCanvas::rootMouseMove(const GuiEvent &event) +{ + if (bool(mMouseCapturedControl)) + { + mMouseCapturedControl->onMouseMove(event); + } + else + { + findMouseControl(event); + if(bool(mMouseControl)) + mMouseControl->onMouseMove(event); + } +} + +void GuiCanvas::rootRightMouseDown(const GuiEvent &event) +{ + mPrevMouseTime = Platform::getVirtualMilliseconds(); + mMouseRightButtonDown = true; + + if (bool(mMouseCapturedControl)) + { + checkLockMouseMove( event ); + mMouseCapturedControl->onRightMouseDown(event); + } + else + { + findMouseControl(event); + + if(bool(mMouseControl)) + { + mMouseControl->onRightMouseDown(event); + } + } +} + +void GuiCanvas::rootRightMouseUp(const GuiEvent &event) +{ + mPrevMouseTime = Platform::getVirtualMilliseconds(); + mMouseRightButtonDown = false; + + if (bool(mMouseCapturedControl)) + mMouseCapturedControl->onRightMouseUp(event); + else + { + findMouseControl(event); + + if(bool(mMouseControl)) + mMouseControl->onRightMouseUp(event); + } +} + +void GuiCanvas::rootRightMouseDragged(const GuiEvent &event) +{ + mPrevMouseTime = Platform::getVirtualMilliseconds(); + + if (bool(mMouseCapturedControl)) + { + mMouseCapturedControl->onRightMouseDragged(event); + } + else + { + findMouseControl(event); + + if(bool(mMouseControl)) + mMouseControl->onRightMouseDragged(event); + } +} + +void GuiCanvas::rootMiddleMouseDown(const GuiEvent &event) +{ + mPrevMouseTime = Platform::getVirtualMilliseconds(); + mMouseMiddleButtonDown = true; + + if (bool(mMouseCapturedControl)) + mMouseCapturedControl->onMiddleMouseDown(event); + else + { + findMouseControl(event); + + if(bool(mMouseControl)) + { + mMouseControl->onMiddleMouseDown(event); + } + } +} + +void GuiCanvas::rootMiddleMouseUp(const GuiEvent &event) +{ + mPrevMouseTime = Platform::getVirtualMilliseconds(); + mMouseMiddleButtonDown = false; + + if (bool(mMouseCapturedControl)) + mMouseCapturedControl->onMiddleMouseUp(event); + else + { + findMouseControl(event); + + if(bool(mMouseControl)) + mMouseControl->onMiddleMouseUp(event); + } +} + +void GuiCanvas::rootMiddleMouseDragged(const GuiEvent &event) +{ + mPrevMouseTime = Platform::getVirtualMilliseconds(); + + if (bool(mMouseCapturedControl)) + { + checkLockMouseMove( event ); + mMouseCapturedControl->onMiddleMouseDragged(event); + } + else + { + findMouseControl(event); + + if(bool(mMouseControl)) + mMouseControl->onMiddleMouseDragged(event); + } +} + +bool GuiCanvas::rootMouseWheelUp(const GuiEvent &event) +{ + if (bool(mMouseCapturedControl)) + return mMouseCapturedControl->onMouseWheelUp(event); + else + { + findMouseControl(event); + + if (bool(mMouseControl)) + return mMouseControl->onMouseWheelUp(event); + } + + return false; +} + +bool GuiCanvas::rootMouseWheelDown(const GuiEvent &event) +{ + if (bool(mMouseCapturedControl)) + return mMouseCapturedControl->onMouseWheelDown(event); + else + { + findMouseControl(event); + + if (bool(mMouseControl)) + return mMouseControl->onMouseWheelDown(event); + } + + return false; +} + +void GuiCanvas::setContentControl(GuiControl *gui) +{ +#ifdef TORQUE_DEMO_PURCHASE + if (mPurchaseScreen->isForceExit()) + return; +#endif + + // Skip out if we got passed NULL (why would that happen?) + if(!gui) + return; + + GuiControl *oldContent = getContentControl(); + if(oldContent) + Con::executef(oldContent, "onUnsetContent", Con::getIntArg(gui->getId())); + + //remove all dialogs on layer 0 + U32 index = 0; + while (size() > index) + { + GuiControl *ctrl = static_cast((*this)[index]); + if (ctrl == gui || ctrl->mLayer != 0) + index++; + + Sim::getGuiGroup()->addObject( ctrl ); + } + + // lose the first responder from the old GUI + GuiControl* responder = gui->findFirstTabable(); + if(responder) + responder->setFirstResponder(); + + //add the gui to the front + if(!size() || gui != (*this)[0]) + { + // automatically wakes objects in GuiControl::onWake + addObject(gui); + if (size() >= 2) + reOrder(gui, *begin()); + } + //refresh the entire gui + resetUpdateRegions(); + + //rebuild the accelerator map + mAcceleratorMap.clear(); + + for(iterator i = end(); i != begin() ; ) + { + i--; + GuiControl *ctrl = static_cast(*i); + ctrl->buildAcceleratorMap(); + + if (ctrl->getControlProfile()->mModal) + break; + } + refreshMouseControl(); + + // Force the canvas to update the sizing of the new content control + maintainSizing(); + + // Do this last so onWake gets called first + Con::executef(gui, "onSetContent", Con::getIntArg(oldContent ? oldContent->getId() : 0)); +} + +GuiControl *GuiCanvas::getContentControl() +{ + if(size() > 0) + return (GuiControl *) first(); + return NULL; +} + +void GuiCanvas::pushDialogControl(GuiControl *gui, S32 layer, bool center) +{ +#ifdef TORQUE_DEMO_PURCHASE + if (mPurchaseScreen->isForceExit()) + return; +#endif + + if( center ) + gui->setPosition( getExtent().x / 2 - gui->getExtent().x / 2, + getExtent().y / 2 - gui->getExtent().y / 2 ); + + //add the gui + gui->mLayer = layer; + + // GuiControl::addObject wakes the object + addObject(gui); + + //reorder it to the correct layer + iterator i; + for (i = begin(); i != end(); i++) + { + GuiControl *ctrl = static_cast(*i); + if (ctrl->mLayer > gui->mLayer) + { + reOrder(gui, ctrl); + break; + } + } + + //call the dialog push method + gui->onDialogPush(); + + //find the first responder + GuiControl* responder = gui->findFirstTabable(); + if(responder) + responder->setFirstResponder(); + + // call the 'onWake' method? + //if(wakedGui) + // Con::executef(gui, 1, "onWake"); + + //refresh the entire gui + resetUpdateRegions(); + + //rebuild the accelerator map + mAcceleratorMap.clear(); + if (size() > 0) + { + GuiControl *ctrl = static_cast(last()); + ctrl->buildAcceleratorMap(); + } + + refreshMouseControl(); + + // I don't see the purpose of this, and it's causing issues when showing, for instance the + // metrics dialog while in a 3d scene, causing the cursor to be shown even when the mouse + // is locked [4/25/2007 justind] + //if(gui->mProfile && gui->mProfile->mModal) + // mPlatformWindow->getCursorController()->pushCursor(PlatformCursorController::curArrow); +} + +void GuiCanvas::popDialogControl(GuiControl *gui) +{ + if (size() < 1) + return; + + //first, find the dialog, and call the "onDialogPop()" method + GuiControl *ctrl = NULL; + if (gui) + { + //make sure the gui really exists on the stack + iterator i; + bool found = false; + for(i = begin(); i != end(); i++) + { + GuiControl *check = static_cast(*i); + if (check == gui) + { + ctrl = check; + found = true; + } + } + + if (!found) + return; + } + else + ctrl = static_cast(last()); + + //call the "on pop" function + ctrl->onDialogPop(); + + //now pop the last child (will sleep if awake) + Sim::getGuiGroup()->addObject(ctrl); + + if (size() > 0) + { + GuiControl *ctrl = static_cast(last()); + if( ctrl->getFirstResponder() ) + ctrl->getFirstResponder()->setFirstResponder(); + } + else + { + setFirstResponder(NULL); + } + + //refresh the entire gui + resetUpdateRegions(); + + //rebuild the accelerator map + mAcceleratorMap.clear(); + + if (size() > 0) + { + GuiControl *ctrl = static_cast(last()); + ctrl->buildAcceleratorMap(); + } + refreshMouseControl(); +} + +void GuiCanvas::popDialogControl(S32 layer) +{ + if (size() < 1) + return; + + GuiControl *ctrl = NULL; + iterator i = end(); // find in z order (last to first) + while (i != begin()) + { + i--; + ctrl = static_cast(*i); + if (ctrl->mLayer == layer) + break; + } + if (ctrl) + popDialogControl(ctrl); +} + +void GuiCanvas::mouseLock(GuiControl *lockingControl) +{ + if (bool(mMouseCapturedControl)) + return; + + mMouseCapturedControl = lockingControl; + + if(mMouseControl && mMouseControl != mMouseCapturedControl) + { + GuiEvent evt; + evt.mousePoint.x = S32(mCursorPt.x); + evt.mousePoint.y = S32(mCursorPt.y); + + mMouseControl->onMouseLeave(evt); + } +} + +void GuiCanvas::mouseUnlock(GuiControl *lockingControl) +{ + if (static_cast(mMouseCapturedControl) != lockingControl) + return; + + GuiEvent evt; + evt.mousePoint.x = S32(mCursorPt.x); + evt.mousePoint.y = S32(mCursorPt.y); + + GuiControl * controlHit = findHitControl(evt.mousePoint); + if(controlHit != mMouseCapturedControl) + { + mMouseControl = controlHit; + mMouseControlClicked = false; + if(bool(mMouseControl)) + mMouseControl->onMouseEnter(evt); + } + mMouseCapturedControl = NULL; +} + +void GuiCanvas::paint() +{ + resetUpdateRegions(); + + // inhibit explicit refreshes in the case we're swapped out + if( mPlatformWindow && mPlatformWindow->isVisible() && GFX->allowRender()) + mPlatformWindow->displayEvent.trigger(mPlatformWindow->getWindowId()); +} + +void GuiCanvas::repaint(U32 elapsedMS) +{ + // Make sure we have a window. + if ( !mPlatformWindow ) + return; + + // Has enough time elapsed? + U32 elapsed = Platform::getRealMilliseconds() - mLastRenderMs; + if (elapsed < elapsedMS) + return; + + // Do the render. + resetUpdateRegions(); + handlePaintEvent(mPlatformWindow->getWindowId()); +} + +void GuiCanvas::maintainSizing() +{ + Point2I size = getWindowSize(); + + if(size.x == -1 || size.y == -1) + return; + + RectI screenRect(0, 0, size.x, size.y); + setBounds(screenRect); + + // all bottom level controls should be the same dimensions as the canvas + // this is necessary for passing mouse events accurately + iterator i; + for (i = begin(); i != end(); i++) + { + AssertFatal(static_cast((*i))->isAwake(), "GuiCanvas::maintainSizing - ctrl is not awake"); + GuiControl *ctrl = static_cast(*i); + Point2I ext = ctrl->getExtent(); + Point2I pos = ctrl->getPosition(); + + if(pos != screenRect.point || ext != screenRect.extent) + { + ctrl->resize(screenRect.point, screenRect.extent); + resetUpdateRegions(); + } + } + +} + +void GuiCanvas::setupFences() +{ + // Destroy old fences + SAFE_DELETE_ARRAY( mFences ); + + // Now create the new ones + if( mNumFences > 0 ) + { + mFences = new GFXFence*[mNumFences]; + + // Allocate the new fences + for( int i = 0; i < mNumFences; i++ ) + mFences[i] = GFX->createFence(); + } + + // Reset state + mNextFenceIdx = 0; +} + +void GuiCanvas::renderFrame(bool preRenderOnly, bool bufferSwap /* = true */) +{ + AssertISV(mPlatformWindow, "GuiCanvas::renderFrame - no window present!"); + if(!mPlatformWindow->isVisible() || !GFX->allowRender() || GFX->canCurrentlyRender()) + return; + + PROFILE_START(CanvasPreRender); + + // Set our window as the current render target so we can see outputs. + GFX->setActiveRenderTarget(mPlatformWindow->getGFXTarget()); + + if (!GFX->getActiveRenderTarget()) + { + PROFILE_END(); + return; + } + +#ifdef TORQUE_GFX_STATE_DEBUG + GFX->getDebugStateManager()->startFrame(); +#endif + + GFXTarget* renderTarget = GFX->getActiveRenderTarget(); + if (renderTarget == NULL) + { + PROFILE_END(); + return; + } + + // Make sure the root control is the size of the canvas. + Point2I size = renderTarget->getSize(); + + if(size.x == 0 || size.y == 0) + { + PROFILE_END(); + return; + } + + RectI screenRect(0, 0, size.x, size.y); + + maintainSizing(); + + //preRender (recursive) all controls + preRender(); + + PROFILE_END(); + + // Are we just doing pre-render? + if(preRenderOnly) + return; + + // Signal the interested parties. + GuiCanvas::getGuiCanvasFrameSignal().trigger(true); + + // Gross hack to make sure we don't end up with advanced lighting and msaa + // at the same time, which causes artifacts. At the same time we don't + // want to just throw the settings the user has chosen if the light manager + // changes at a later time. + + GFXVideoMode mode = mPlatformWindow->getVideoMode(); + if ( dStricmp( LIGHTMGR->getId(), "ADVLM" ) == 0 && mode.antialiasLevel > 0 ) + { + const char *pref = Con::getVariable( "$pref::Video::mode" ); + mode.parseFromString( pref ); + mode.antialiasLevel = 0; + mPlatformWindow->setVideoMode(mode); + + Con::printf( "AntiAliasing has been disabled; it is not compatible with AdvancedLighting." ); + } + else if ( dStricmp( LIGHTMGR->getId(), "BLM" ) == 0) + { + const char *pref = Con::getVariable( "$pref::Video::mode" ); + + U32 prefAA = dAtoi( StringUnit::getUnit(pref, 5, " ") ); + if ( prefAA != mode.antialiasLevel ) + { + mode.parseFromString( pref ); + mPlatformWindow->setVideoMode(mode); + + Con::printf( "AntiAliasing has been enabled while running BasicLighting." ); + } + } + + // for now, just always reset the update regions - this is a + // fix for FSAA on ATI cards + resetUpdateRegions(); + + PROFILE_START(CanvasRenderControls); + + // Draw the mouse + GuiCursor *mouseCursor = NULL; + bool cursorVisible = true; + + if(bool(mMouseCapturedControl)) + mMouseCapturedControl->getCursor(mouseCursor, cursorVisible, mLastEvent); + else if(bool(mMouseControl)) + mMouseControl->getCursor(mouseCursor, cursorVisible, mLastEvent); + + Point2I cursorPos((S32)mCursorPt.x, (S32)mCursorPt.y); + if(!mouseCursor) + mouseCursor = mDefaultCursor; + + if(mLastCursorEnabled && mLastCursor) + { + Point2I spot = mLastCursor->getHotSpot(); + Point2I cext = mLastCursor->getExtent(); + Point2I pos = mLastCursorPt - spot; + addUpdateRegion(pos - Point2I(2, 2), Point2I(cext.x + 4, cext.y + 4)); + } + + if(cursorVisible && mouseCursor) + { + Point2I spot = mouseCursor->getHotSpot(); + Point2I cext = mouseCursor->getExtent(); + Point2I pos = cursorPos - spot; + + addUpdateRegion(pos - Point2I(2, 2), Point2I(cext.x + 4, cext.y + 4)); + } + + mLastCursorEnabled = cursorVisible; + mLastCursor = mouseCursor; + mLastCursorPt = cursorPos; + + // Begin GFX + PROFILE_START(GFXBeginScene); + + bool beginSceneRes = GFX->beginScene(); + + PROFILE_END(); + + // Can't render if waiting for device to reset. + if ( !beginSceneRes ) + { + PROFILE_END(); // CanvasRenderControls + + // Since we already triggered the signal once for begin-of-frame, + // we should be consistent and trigger it again for end-of-frame. + GuiCanvas::getGuiCanvasFrameSignal().trigger(false); + + return; + } + + // Clear the current viewport area + GFX->setViewport( screenRect ); + GFX->clear( GFXClearZBuffer | GFXClearStencil | GFXClearTarget, gCanvasClearColor, 1.0f, 0 ); + + resetUpdateRegions(); + + // Make sure we have a clean matrix state + // before we start rendering anything! + GFX->setWorldMatrix( MatrixF::Identity ); + GFX->setViewMatrix( MatrixF::Identity ); + GFX->setProjectionMatrix( MatrixF::Identity ); + + // If we're taking a screenshot then let it have + // a chance at altering the view matrix. + if ( gScreenShot && gScreenShot->isPending() ) + gScreenShot->tileGui( size ); + + RectI updateUnion; + buildUpdateUnion(&updateUnion); + if (updateUnion.intersect(screenRect)) + { + // Render active GUI Dialogs + for(iterator i = begin(); i != end(); i++) + { + // Get the control + GuiControl *contentCtrl = static_cast(*i); + + GFX->setClipRect( updateUnion ); + GFX->setStateBlock(mDefaultGuiSB); + + contentCtrl->onRender(contentCtrl->getPosition(), updateUnion); + } + + // Fill Black if no Dialogs + if(this->size() == 0) + GFX->clear( GFXClearTarget, ColorI(0,0,0,0), 1.0f, 0 ); + + // Tooltip resource + if(bool(mMouseControl)) + { + U32 curTime = Platform::getRealMilliseconds(); + if(mHoverControl == mMouseControl) + { + if(mHoverPositionSet || (curTime - mHoverControlStart) >= mHoverControl->mTipHoverTime || (curTime - mHoverLeftControlTime) <= mHoverControl->mTipHoverTime) + { + if(!mHoverPositionSet) + { + mHoverPosition = cursorPos; + } + mHoverPositionSet = mMouseControl->mRenderTooltipDelegate( mHoverPosition, cursorPos, NULL ); + } + + } + else + { + if(mHoverPositionSet) + { + mHoverLeftControlTime = curTime; + mHoverPositionSet = false; + } + mHoverControl = mMouseControl; + mHoverControlStart = curTime; + } + } + + GFX->setClipRect( updateUnion ); + + // Draw an ugly box if we don't have a cursor available... + //if (mCursorEnabled && mShowCursor && !mouseCursor) + //{ + // GFX->drawRectFill( RectI( mCursorPt.x, mCursorPt.y, mCursorPt.x + 2, mCursorPt.y + 2 ), ColorI( 255, 0, 0 ) ); + //} + + + // CodeReview - Make sure our bitmap modulation is clear or else there's a black modulation + // that ruins rendering of textures at startup.. This was done in mouseCursor + // onRender and so at startup when it wasn't called the modulation was black, ruining + // the loading screen display. This fixes the issue, but is it only masking a deeper issue + // in GFX with regard to gui rendering? [5/3/2007 justind] + GFX->getDrawUtil()->clearBitmapModulation(); + + // Really draw the cursor. :) + // Only if the platform cursor controller is missing or the platform cursor + // isn't visible. + if (!mPlatformWindow->getCursorController() || (mCursorEnabled && mouseCursor && mShowCursor && + !mPlatformWindow->getCursorController()->isCursorVisible())) + { + Point2I pos((S32)mCursorPt.x, (S32)mCursorPt.y); + Point2I spot = mouseCursor->getHotSpot(); + + pos -= spot; + mouseCursor->render(pos); + } + } + + // Render all RTT end of frame updates HERE + //DynamicTexture::updateScreenTextures(); + //DynamicTexture::updateEndOfFrameTextures(); + // mPending is set when the console function "screenShot()" is called + // this situation is necessary because it needs to take the screenshot + // before the buffers swap + +#ifdef TORQUE_DEMO_TIMEOUT + checkTimeOut(); +#endif + + PROFILE_END(); + + // Fence logic here, because this is where endScene is called. + if( mNumFences > 0 ) + { + // Issue next fence + mFences[mNextFenceIdx]->issue(); + + mNextFenceIdx++; + + // Wrap the next fence around to first if we're maxxed + if( mNextFenceIdx >= mNumFences ) + mNextFenceIdx = 0; + + // Block on previous fence + mFences[mNextFenceIdx]->block(); + } + + PROFILE_START(GFXEndScene); + GFX->endScene(); + PROFILE_END(); + + swapBuffers(); + + GuiCanvas::getGuiCanvasFrameSignal().trigger(false); + +#ifdef TORQUE_GFX_STATE_DEBUG + GFX->getDebugStateManager()->endFrame(); +#endif + + // Keep track of the last time we rendered. + mLastRenderMs = Platform::getRealMilliseconds(); +} + +GuiCanvas::GuiCanvasFrameSignal& GuiCanvas::getGuiCanvasFrameSignal() +{ + static GuiCanvasFrameSignal theSignal; + return theSignal; +} + +void GuiCanvas::swapBuffers() +{ + AssertISV(mPlatformWindow, "GuiCanvas::swapBuffers - no window present!"); + if(!mPlatformWindow->isVisible()) + return; + + PROFILE_START(SwapBuffers); + mPlatformWindow->getGFXTarget()->present(); + PROFILE_END(); +} + +void GuiCanvas::buildUpdateUnion(RectI *updateUnion) +{ + *updateUnion = mOldUpdateRects[0]; + + //the update region should encompass the oldUpdateRects, and the curUpdateRect + Point2I upperL; + Point2I lowerR; + + upperL.x = getMin(mOldUpdateRects[0].point.x, mOldUpdateRects[1].point.x); + upperL.x = getMin(upperL.x, mCurUpdateRect.point.x); + + upperL.y = getMin(mOldUpdateRects[0].point.y, mOldUpdateRects[1].point.y); + upperL.y = getMin(upperL.y, mCurUpdateRect.point.y); + + lowerR.x = getMax(mOldUpdateRects[0].point.x + mOldUpdateRects[0].extent.x, mOldUpdateRects[1].point.x + mOldUpdateRects[1].extent.x); + lowerR.x = getMax(lowerR.x, mCurUpdateRect.point.x + mCurUpdateRect.extent.x); + + lowerR.y = getMax(mOldUpdateRects[0].point.y + mOldUpdateRects[0].extent.y, mOldUpdateRects[1].point.y + mOldUpdateRects[1].extent.y); + lowerR.y = getMax(lowerR.y, mCurUpdateRect.point.y + mCurUpdateRect.extent.y); + + updateUnion->point = upperL; + updateUnion->extent = lowerR - upperL; + + //shift the oldUpdateRects + mOldUpdateRects[0] = mOldUpdateRects[1]; + mOldUpdateRects[1] = mCurUpdateRect; + + mCurUpdateRect.point.set(0,0); + mCurUpdateRect.extent.set(0,0); +} + +void GuiCanvas::addUpdateRegion(Point2I pos, Point2I ext) +{ + if(mCurUpdateRect.extent.x == 0) + { + mCurUpdateRect.point = pos; + mCurUpdateRect.extent = ext; + } + else + { + Point2I upperL; + upperL.x = getMin(mCurUpdateRect.point.x, pos.x); + upperL.y = getMin(mCurUpdateRect.point.y, pos.y); + Point2I lowerR; + lowerR.x = getMax(mCurUpdateRect.point.x + mCurUpdateRect.extent.x, pos.x + ext.x); + lowerR.y = getMax(mCurUpdateRect.point.y + mCurUpdateRect.extent.y, pos.y + ext.y); + mCurUpdateRect.point = upperL; + mCurUpdateRect.extent = lowerR - upperL; + } +} + +void GuiCanvas::resetUpdateRegions() +{ + //DEBUG - get surface width and height + mOldUpdateRects[0] = getBounds(); + mOldUpdateRects[1] = mOldUpdateRects[0]; + mCurUpdateRect = mOldUpdateRects[0]; +} + +void GuiCanvas::setFirstResponder( GuiControl* newResponder ) +{ + GuiControl* oldResponder = mFirstResponder; + Parent::setFirstResponder( newResponder ); + + if( oldResponder == mFirstResponder ) + return; + + if( oldResponder && ( oldResponder != newResponder ) ) + oldResponder->onLoseFirstResponder(); + + if( newResponder && ( newResponder != oldResponder ) ) + newResponder->onGainFirstResponder(); +} + +DefineEngineMethod( GuiCanvas, getContent, S32, (),, + "@brief Get the GuiControl which is being used as the content.\n\n" + + "@tsexample\n" + "Canvas.getContent();\n" + "@endtsexample\n\n" + + "@return ID of current content control") +{ + GuiControl *ctrl = object->getContentControl(); + if(ctrl) + return ctrl->getId(); + return -1; +} + +DefineEngineMethod( GuiCanvas, setContent, void, (GuiControl* ctrl),, + "@brief Set the content of the canvas to a specified control.\n\n" + + "@param ctrl ID or name of GuiControl to set content to\n\n" + + "@tsexample\n" + "Canvas.setContent(PlayGui);\n" + "@endtsexample\n\n") +{ + // Not using old error reporting until we modify the engineAPI - mperry + + //GuiControl *gui = NULL; + // if(argv[2][0]) + // { + // if (!Sim::findObject(argv[2], gui)) + // { + // Con::printf("%s(): Invalid control: %s", argv[0], argv[2]); + // return; + // } + // } + + if(!ctrl) + { + Con::errorf("GuiCanvas::setContent - Invalid control specified')"); + return; + } + + //set the new content control + object->setContentControl(ctrl); +} + +ConsoleDocFragment _pushDialog( + "@brief Adds a dialog control onto the stack of dialogs\n\n" + "@param ctrl Dialog to add\n" + "@param layer Layer to put dialog on (optional)\n" + "@param center True to center dialog on canvas (optional)\n\n" + "@tsexample\n" + "Canvas.pushDialog(RecordingsDlg);\n" + "@endtsexample\n\n", + "GuiCanvas", + "void pushDialog( GuiControl ctrl, int layer=0, bool center=false);" +); + +ConsoleMethod( GuiCanvas, pushDialog, void, 3, 5, "(GuiControl ctrl, int layer=0, bool center=false)" + "@hide") +{ + GuiControl *gui; + + if (! Sim::findObject(argv[2], gui)) + { + Con::printf("%s(): Invalid control: %s", argv[0], argv[2]); + return; + } + + //find the layer + S32 layer = 0; + if( argc > 3 ) + layer = dAtoi( argv[ 3 ] ); + + bool center = false; + if( argc > 4 ) + center = dAtob( argv[ 4 ] ); + + //set the new content control + object->pushDialogControl(gui, layer, center); +} + +ConsoleDocFragment _popDialog1( + "@brief Removes a specific dialog control\n\n" + "@param ctrl Dialog to pop\n" + "@tsexample\n" + "Canvas.popDialog(RecordingsDlg);\n" + "@endtsexample\n\n", + "GuiCanvas", + "void popDialog( GuiControl ctrl);" +); + +ConsoleDocFragment _popDialog2( + "@brief Removes a dialog at the front most layer\n\n" + "@tsexample\n" + "// Pops whatever is on layer 0\n" + "Canvas.popDialog();\n" + "@endtsexample\n\n", + "GuiCanvas", + "void popDialog();" +); + +ConsoleMethod( GuiCanvas, popDialog, void, 2, 3, "(GuiControl ctrl=NULL)" + "@hide") +{ + GuiControl *gui = NULL; + if (argc == 3) + { + if (!Sim::findObject(argv[2], gui)) + { + Con::printf("%s(): Invalid control: %s", argv[0], argv[2]); + return; + } + } + + if (gui) + object->popDialogControl(gui); + else + object->popDialogControl(); +} + +ConsoleDocFragment _popLayer1( + "@brief Removes the top most layer of dialogs\n\n" + "@tsexample\n" + "Canvas.popLayer();\n" + "@endtsexample\n\n", + "GuiCanvas", + "void popLayer();" +); + +ConsoleDocFragment _popLayer2( + "@brief Removes a specified layer of dialogs\n\n" + "@param layer Number of the layer to pop\n\n" + "@tsexample\n" + "Canvas.popLayer(1);\n" + "@endtsexample\n\n", + "GuiCanvas", + "void popLayer(S32 layer);" +); + +ConsoleMethod( GuiCanvas, popLayer, void, 2, 3, "(int layer)" + "@hide") +{ + S32 layer = 0; + if (argc == 3) + layer = dAtoi(argv[2]); + + object->popDialogControl(layer); +} + +DefineEngineMethod( GuiCanvas, cursorOn, void, (),, + "@brief Turns on the mouse cursor.\n\n" + "@tsexample\n" + "Canvas.cursorOn();\n" + "@endtsexample\n\n") +{ + object->setCursorON(true); +} + +DefineEngineMethod( GuiCanvas, cursorOff, void, (),, + "@brief Turns on the mouse off.\n\n" + "@tsexample\n" + "Canvas.cursorOff();\n" + "@endtsexample\n\n") +{ + object->setCursorON(false); +} + + +DefineEngineMethod( GuiCanvas, setCursor, void, (GuiCursor* cursor),, + "@brief Sets the cursor for the canvas.\n\n" + + "@param cursor Name of the GuiCursor to use\n\n" + + "@tsexample\n" + "Canvas.setCursor(\"DefaultCursor\");\n" + "@endtsexample\n\n") +{ + if(!cursor) + { + Con::errorf("GuiCanvas::setCursor - Invalid GuiCursor name or ID"); + return; + } + object->setCursor(cursor); +} + +DefineEngineMethod( GuiCanvas, renderFront, void, ( bool enable ),, + "@brief This turns on/off front-buffer rendering.\n\n" + + "@param enable True if all rendering should be done to the front buffer\n\n" + + "@tsexample\n" + "Canvas.renderFront(false);\n" + "@endtsexample\n\n") +{ + object->setRenderFront(enable); +} + +DefineEngineMethod( GuiCanvas, showCursor, void, (),, + "@brief Enable rendering of the cursor.\n\n" + + "@tsexample\n" + "Canvas.showCursor();\n" + "@endtsexample\n\n") +{ + object->showCursor(true); +} + +DefineEngineMethod( GuiCanvas, hideCursor, void, (),, + "@brief Disable rendering of the cursor.\n\n" + + "@tsexample\n" + "Canvas.hideCursor();\n" + "@endtsexample\n\n") +{ + object->showCursor(false); +} + +DefineEngineMethod( GuiCanvas, isCursorOn, bool, (),, + "@brief Determines if mouse cursor is enabled.\n\n" + + "@tsexample\n" + "// Is cursor on?\n" + "if(Canvas.isCursorOn())\n" + " echo(\"Canvas cursor is on\");\n" + "@endtsexample\n\n" + "@return Returns true if the cursor is on.\n\n") +{ + return object->isCursorON(); +} + +DefineEngineMethod( GuiCanvas, isCursorShown, bool, (),, + "@brief Determines if mouse cursor is rendering.\n\n" + + "@tsexample\n" + "// Is cursor rendering?\n" + "if(Canvas.isCursorShown())\n" + " echo(\"Canvas cursor is rendering\");\n" + "@endtsexample\n\n" + "@return Returns true if the cursor is rendering.\n\n") +{ + return object->isCursorShown(); +} + +DefineEngineMethod( GuiCanvas, repaint, void, ( S32 elapsedMS ), (0), + "@brief Force canvas to redraw.\n" + "If the elapsed time is greater than the time since the last paint " + "then the repaint will be skipped.\n" + "@param elapsedMS The optional elapsed time in milliseconds.\n\n" + + "@tsexample\n" + "Canvas.repaint();\n" + "@endtsexample\n\n") +{ + object->repaint(elapsedMS < 0 ? 0 : elapsedMS); +} + +DefineEngineMethod( GuiCanvas, reset, void, (),, + "@brief Reset the update regions for the canvas.\n\n" + + "@tsexample\n" + "Canvas.reset();\n" + "@endtsexample\n\n") +{ + object->resetUpdateRegions(); +} + +DefineEngineMethod( GuiCanvas, getCursorPos, Point2I, (),, + "@brief Get the current position of the cursor.\n\n" + "@param param Description\n\n" + "@tsexample\n" + "%cursorPos = Canvas.getCursorPos();\n" + "@endtsexample\n\n" + "@return Screen coordinates of mouse cursor, in format \"X Y\"") +{ + return object->getCursorPos(); +} + +ConsoleDocFragment _setCursorPos1( + "@brief Sets the position of the cursor\n\n" + "@param pos Point, in screenspace for the cursor. Formatted as (\"x y\")\n\n" + "@tsexample\n" + "Canvas.setCursorPos(\"0 0\");\n" + "@endtsexample\n\n", + "GuiCanvas", + "bool setCursorPos( Point2I pos );" +); +ConsoleDocFragment _setCursorPos2( + "@brief Sets the position of the cursor\n\n" + "@param posX X-coordinate, in screenspace for the cursor.\n" + "@param posY Y-coordinate, in screenspace for the cursor.\n\n" + "@tsexample\n" + "Canvas.setCursorPos(0,0);\n" + "@endtsexample\n\n", + "GuiCanvas", + "bool setCursorPos( F32 posX, F32 posY);" +); + +ConsoleMethod( GuiCanvas, setCursorPos, void, 3, 4, "(Point2I pos)" + "@hide") +{ + Point2I pos(0,0); + + if(argc == 4) + pos.set(dAtoi(argv[2]), dAtoi(argv[3])); + else + dSscanf(argv[2], "%d %d", &pos.x, &pos.y); + + object->setCursorPos(pos); +} + +DefineEngineMethod( GuiCanvas, getMouseControl, S32, (),, + "@brief Gets the gui control under the mouse.\n\n" + "@tsexample\n" + "%underMouse = Canvas.getMouseControl();\n" + "@endtsexample\n\n" + + "@return ID of the gui control, if one was found. NULL otherwise") +{ + GuiControl* control = object->getMouseControl(); + if (control) + return control->getId(); + + return NULL; +} + +DefineEngineFunction(excludeOtherInstance, bool, (const char* appIdentifer),, + "@brief Used to exclude/prevent all other instances using the same identifier specified\n\n" + + "@note Not used on OSX, Xbox, or in Win debug builds\n\n" + + "@param appIdentifier Name of the app set up for exclusive use.\n" + + "@return False if another app is running that specified the same appIdentifier\n\n" + + "@ingroup Platform\n" + "@ingroup GuiCore") +{ + // mac/360 can only run one instance in general. +#if !defined(TORQUE_OS_MAC) && !defined(TORQUE_OS_XENON) && !defined(TORQUE_DEBUG) + return Platform::excludeOtherInstances(appIdentifer); +#else + // We can just return true if we get here. + return true; +#endif +} + +DefineEngineMethod( GuiCanvas, getExtent, Point2I, (),, + "@brief Returns the dimensions of the canvas\n\n" + + "@tsexample\n" + "%extent = Canvas.getExtent();\n" + "@endtsexample\n\n" + + "@return Width and height of canvas. Formatted as numerical values in a single string \"# #\"") +{ + return object->getExtent(); +} + + +DefineEngineMethod( GuiCanvas, setWindowTitle, void, ( const char* newTitle),, + "@brief Change the title of the OS window.\n\n" + + "@param newTitle String containing the new name\n\n" + + "@tsexample\n" + "Canvas.setWindowTitle(\"Documentation Rocks!\");\n" + "@endtsexample\n\n") +{ + object->setWindowTitle(newTitle); +} + + +DefineEngineMethod( GuiCanvas, getVideoMode, const char*, (),, + "@brief Gets the current screen mode as a string.\n\n" + + "The return string will contain 5 values (width, height, fullscreen, bitdepth, refreshRate). " + "You will need to parse out each one for individual use.\n\n" + + "@tsexample\n" + "%screenWidth = getWord(Canvas.getVideoMode(), 0);\n" + "%screenHeight = getWord(Canvas.getVideoMode(), 1);\n" + "%isFullscreen = getWord(Canvas.getVideoMode(), 2);\n" + "%bitdepth = getWord(Canvas.getVideoMode(), 3);\n" + "%refreshRate = getWord(Canvas.getVideoMode(), 4);\n" + "@endtsexample\n\n" + + "@return String formatted with screen width, screen height, screen mode, bit depth, and refresh rate.") +{ + // Grab the video mode. + if (!object->getPlatformWindow()) + return ""; + + GFXVideoMode vm = object->getPlatformWindow()->getVideoMode(); + char* buf = Con::getReturnBuffer(vm.toString()); + return buf; +} + + +DefineEngineMethod( GuiCanvas, getModeCount, S32, (),, + "@brief Gets the number of modes available on this device.\n\n" + + "@param param Description\n\n" + + "@tsexample\n" + "%modeCount = Canvas.getModeCount()\n" + "@endtsexample\n\n" + + "@return The number of video modes supported by the device") +{ + if (!object->getPlatformWindow()) + return 0; + + // Grab the available mode list from the device. + const Vector* const modeList = + object->getPlatformWindow()->getGFXDevice()->getVideoModeList(); + + // Return the number of resolutions. + return modeList->size(); +} + +DefineEngineMethod( GuiCanvas, getMode, const char*, (S32 modeId),, + "@brief Gets information on the specified mode of this device.\n\n" + "@param modeId Index of the mode to get data from.\n" + "@return A video mode string given an adapter and mode index.\n\n" + "@see GuiCanvas::getVideoMode()") +{ + if (!object->getPlatformWindow()) + return 0; + + // Grab the available mode list from the device. + const Vector* const modeList = + object->getPlatformWindow()->getGFXDevice()->getVideoModeList(); + + // Get the desired index and confirm it's valid. + S32 idx = modeId; + if((idx < 0) || (idx >= modeList->size())) + { + Con::errorf("GuiCanvas::getResolution - You requested an out of range index of %d. Please specify an index in the range [0, %d).", idx, modeList->size()); + return ""; + } + + // Great - we got something valid, so convert the videomode into a + // string and return to the user. + GFXVideoMode vm = (*modeList)[idx]; + + char *retString = Con::getReturnBuffer(vm.toString()); + return retString; +} + + +DefineEngineMethod( GuiCanvas, toggleFullscreen, void, (),, + "@brief toggle canvas from fullscreen to windowed mode or back.\n\n" + + "@tsexample\n" + "// If we are in windowed mode, the following will put is in fullscreen\n" + "Canvas.toggleFullscreen();" + "@endtsexample\n\n") +{ + if (Platform::getWebDeployment()) + return; + + if (!object->getPlatformWindow()) + return; + + if (Journal::IsRecording() || Journal::IsPlaying()) + return; + + // Get the window's video mode. + GFXVideoMode origMode = object->getPlatformWindow()->getVideoMode(); + + // And grab the device its using. + GFXDevice *device = object->getPlatformWindow()->getGFXDevice(); + + // Toggle the fullscreen bit. + GFXVideoMode newMode = origMode; + newMode.fullScreen = !origMode.fullScreen; + + // CodeReview Toggling might be better served by reading the fullscreen + // or windowed video mode pref and setting that instead [bjg 5/2/07] + + if(newMode.fullScreen == true) + { + // Are we going to fullscreen? If so find the first matching resolution that + // is equal to or bigger in size, and has same BPP - windows + // are often strangely sized and will need to be adjusted to a viable + // fullscreen res. + + for(S32 i=0; igetVideoModeList()->size(); i++) + { + const GFXVideoMode &newVm = (*(device->getVideoModeList()))[i]; + + if(newMode.resolution.x > newVm.resolution.x) + continue; + + if(newMode.resolution.y > newVm.resolution.y) + continue; + + if(newMode.bitDepth != newVm.bitDepth) + continue; + + // Great - got a match. + newMode = newVm; + newMode.fullScreen = true; + break; + } + } + + // Ok, we have new video mode. Set it! + object->getPlatformWindow()->setVideoMode(newMode); +} + + +DefineEngineMethod( GuiCanvas, clientToScreen, Point2I, ( Point2I coordinate ),, + "Translate a coordinate from canvas window-space to screen-space.\n" + "@param coordinate The coordinate in window-space.\n" + "@return The given coordinate translated to screen-space." ) +{ + if( !object->getPlatformWindow() ) + return coordinate; + + return object->getPlatformWindow()->clientToScreen( coordinate ); +} + +DefineEngineMethod( GuiCanvas, screenToClient, Point2I, ( Point2I coordinate ),, + "Translate a coordinate from screen-space to canvas window-space.\n" + "@param coordinate The coordinate in screen-space.\n" + "@return The given coordinate translated to window-space." ) +{ + if( !object->getPlatformWindow() ) + return coordinate; + + return object->getPlatformWindow()->screenToClient( coordinate ); +} + +DefineEngineMethod( GuiCanvas, getWindowPosition, Point2I, (),, + "Get the current position of the platform window associated with the canvas.\n" + "@return The window position of the canvas in screen-space." ) +{ + if( !object->getPlatformWindow() ) + return Point2I( 0, 0 ); + + return object->getPlatformWindow()->getPosition(); +} + +DefineEngineMethod( GuiCanvas, setWindowPosition, void, ( Point2I position ),, + "Set the position of the platform window associated with the canvas.\n" + "@param position The new position of the window in screen-space." ) +{ + if( !object->getPlatformWindow() ) + return; + + object->getPlatformWindow()->setPosition( position ); +} + +ConsoleMethod( GuiCanvas, isFullscreen, bool, 2, 2, "() - Is this canvas currently fullscreen?" ) +{ + if (Platform::getWebDeployment()) + return false; + + if (!object->getPlatformWindow()) + return false; + + return object->getPlatformWindow()->getVideoMode().fullScreen; +} + +ConsoleMethod( GuiCanvas, minimizeWindow, void, 2, 2, "() - minimize this canvas' window." ) +{ + PlatformWindow* window = object->getPlatformWindow(); + if ( window ) + window->minimize(); +} + +ConsoleMethod( GuiCanvas, isMinimized, bool, 2, 2, "()" ) +{ + PlatformWindow* window = object->getPlatformWindow(); + if ( window ) + return window->isMinimized(); + + return false; +} + +ConsoleMethod( GuiCanvas, isMaximized, bool, 2, 2, "()" ) +{ + PlatformWindow* window = object->getPlatformWindow(); + if ( window ) + return window->isMaximized(); + + return false; +} + +ConsoleMethod( GuiCanvas, maximizeWindow, void, 2, 2, "() - maximize this canvas' window." ) +{ + PlatformWindow* window = object->getPlatformWindow(); + if ( window ) + window->maximize(); +} + +ConsoleMethod( GuiCanvas, restoreWindow, void, 2, 2, "() - restore this canvas' window." ) +{ + PlatformWindow* window = object->getPlatformWindow(); + if( window ) + window->restore(); +} + +ConsoleMethod( GuiCanvas, setFocus, void, 2,2, "() - Claim OS input focus for this canvas' window.") +{ + PlatformWindow* window = object->getPlatformWindow(); + if( window ) + window->setFocus(); +} + +ConsoleMethod( GuiCanvas, setVideoMode, void, 5, 8, + "(int width, int height, bool fullscreen, [int bitDepth], [int refreshRate], [int antialiasLevel] )\n" + "Change the video mode of this canvas. This method has the side effect of setting the $pref::Video::mode to the new values.\n\n" + "\\param width The screen width to set.\n" + "\\param height The screen height to set.\n" + "\\param fullscreen Specify true to run fullscreen or false to run in a window\n" + "\\param bitDepth [optional] The desired bit-depth. Defaults to the current setting. This parameter is ignored if you are running in a window.\n" + "\\param refreshRate [optional] The desired refresh rate. Defaults to the current setting. This parameter is ignored if you are running in a window" + "\\param antialiasLevel [optional] The level of anti-aliasing to apply 0 = none" ) +{ + if (!object->getPlatformWindow()) + return; + + if (Journal::IsRecording() || Journal::IsPlaying()) + return; + + // Update the video mode and tell the window to reset. + GFXVideoMode vm = object->getPlatformWindow()->getVideoMode(); + + U32 width = dAtoi(argv[2]); + U32 height = dAtoi(argv[3]); + + bool changed = false; + if (width == 0 && height > 0) + { + // Our width is 0 but our height isn't... + // Try to find a matching width + for(S32 i=0; igetPlatformWindow()->getGFXDevice()->getVideoModeList()->size(); i++) + { + const GFXVideoMode &newVm = (*(object->getPlatformWindow()->getGFXDevice()->getVideoModeList()))[i]; + + if(newVm.resolution.y == height) + { + width = newVm.resolution.x; + changed = true; + break; + } + } + } + else if (height == 0 && width > 0) + { + // Our height is 0 but our width isn't... + // Try to find a matching height + for(S32 i=0; igetPlatformWindow()->getGFXDevice()->getVideoModeList()->size(); i++) + { + const GFXVideoMode &newVm = (*(object->getPlatformWindow()->getGFXDevice()->getVideoModeList()))[i]; + + if(newVm.resolution.x == width) + { + height = newVm.resolution.y; + changed = true; + break; + } + } + } + + if (width == 0 || height == 0) + { + // Got a bad size for both of our dimensions or one of our dimensions and + // didn't get a match for the other default back to our current resolution + width = vm.resolution.x; + height = vm.resolution.y; + + changed = true; + } + + if (changed) + Con::errorf("GuiCanvas::setVideoMode(): Error - Invalid resolution of (%d, %d) - attempting (%d, %d)", dAtoi(argv[2]), dAtoi(argv[3]), width, height); + + vm.resolution = Point2I(width, height); + vm.fullScreen = dAtob(argv[4]); + + if (Platform::getWebDeployment()) + vm.fullScreen = false; + + // These optional params are set to default at construction of vm. If they + // aren't specified, just leave them at whatever they were set to. + if ((argc > 5) && (dStrlen(argv[5]) > 0)) + { + vm.bitDepth = dAtoi(argv[5]); + } + if ((argc > 6) && (dStrlen(argv[6]) > 0)) + { + vm.refreshRate = dAtoi(argv[6]); + } + + if ((argc > 7) && (dStrlen(argv[7]) > 0)) + { + vm.antialiasLevel = dAtoi(argv[7]); + } + + object->getPlatformWindow()->setVideoMode(vm); + + // Store the new mode into a pref. + Con::setVariable( "$pref::Video::mode", vm.toString() ); +} diff --git a/Engine/source/gui/core/guiCanvas.h b/Engine/source/gui/core/guiCanvas.h new file mode 100644 index 000000000..b0d7c2fbd --- /dev/null +++ b/Engine/source/gui/core/guiCanvas.h @@ -0,0 +1,455 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUICANVAS_H_ +#define _GUICANVAS_H_ + +#ifndef _SIMBASE_H_ +#include "console/simBase.h" +#endif +#ifndef _EVENT_H_ +#include "platform/event.h" +#endif +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif +#ifndef _PLATFORMINPUT_H_ +#include "platform/platformInput.h" +#endif + +#include "component/interfaces/IProcessInput.h" +#include "windowManager/platformWindowMgr.h" +#include "gfx/gfxFence.h" + +#ifdef TORQUE_DEMO_PURCHASE +#ifndef _PURCHASESCREEN_H_ +#include "demo/purchase/purchaseScreen.h" +#endif +#endif + +/// A canvas on which rendering occurs. +/// +/// +/// @section GuiCanvas_contents What a GUICanvas Can Contain... +/// +/// @subsection GuiCanvas_content_contentcontrol Content Control +/// A content control is the top level GuiControl for a screen. This GuiControl +/// will be the parent control for all other GuiControls on that particular +/// screen. +/// +/// @subsection GuiCanvas_content_dialogs Dialogs +/// +/// A dialog is essentially another screen, only it gets overlaid on top of the +/// current content control, and all input goes to the dialog. This is most akin +/// to the "Open File" dialog box found in most operating systems. When you +/// choose to open a file, and the "Open File" dialog pops up, you can no longer +/// send input to the application, and must complete or cancel the open file +/// request. Torque keeps track of layers of dialogs. The dialog with the highest +/// layer is on top and will get all the input, unless the dialog is +/// modeless, which is a profile option. +/// +/// @see GuiControlProfile +/// +/// @section GuiCanvas_dirty Dirty Rectangles +/// +/// The GuiCanvas is based on dirty regions. +/// +/// Every frame the canvas paints only the areas of the canvas that are 'dirty' +/// or need updating. In most cases, this only is the area under the mouse cursor. +/// This is why if you look in guiCanvas.cc the call to glClear is commented out. +/// If you want a really good idea of what exactly dirty regions are and how they +/// work, un-comment that glClear line in the renderFrame method of guiCanvas.cc +/// +/// What you will see is a black screen, except in the dirty regions, where the +/// screen will be painted normally. If you are making an animated GuiControl +/// you need to add your control to the dirty areas of the canvas. +/// +class GuiCanvas : public GuiControl, public IProcessInput +{ + +protected: + typedef GuiControl Parent; + + /// @name Rendering + /// @{ + RectI mOldUpdateRects[2]; + RectI mCurUpdateRect; + U32 mLastRenderMs; + /// @} + + /// @name Cursor Properties + /// @{ + + bool mCursorEnabled; + bool mShowCursor; + bool mRenderFront; + Point2F mCursorPt; ///< Current cursor position in local coordinates. + Point2I mLastCursorPt; + GuiCursor *mDefaultCursor; + GuiCursor *mLastCursor; + bool mLastCursorEnabled; + bool mForceMouseToGUI; + bool mClampTorqueCursor; + bool mAlwaysHandleMouseButtons; + + /// @} + + /// @name Mouse Input + /// @{ + + SimObjectPtr mMouseCapturedControl; ///< All mouse events will go to this ctrl only + SimObjectPtr mMouseControl; ///< the control the mouse was last seen in unless some other one captured it + bool mMouseControlClicked; ///< whether the current ctrl has been clicked - used by helpctrl + U32 mPrevMouseTime; ///< this determines how long the mouse has been in the same control + bool mMouseButtonDown; ///< Flag to determine if the button is depressed + bool mMouseRightButtonDown; ///< bool to determine if the right button is depressed + bool mMouseMiddleButtonDown; ///< Middle button flag + GuiEvent mLastEvent; + + U8 mLastMouseClickCount; + S32 mLastMouseDownTime; + bool mLeftMouseLast; + bool mMiddleMouseLast; + bool mRightMouseLast; + Point2F mMouseDownPoint; + + /// Processes keyboard input events. Helper method for processInputEvent + /// + /// \param inputEvent Information on the input even to be processed. + /// \return True if the event was handled or false if it was not. + virtual bool processKeyboardEvent(InputEventInfo &inputEvent); + + /// Processes mouse input events. Helper method for processInputEvent + /// + /// \param inputEvent Information on the input even to be processed. + /// \return True if the event was handled or false if it was not. + virtual bool processMouseEvent(InputEventInfo &inputEvent); + + /// Processes gamepad input events. Helper method for processInputEvent + /// + /// \param inputEvent Information on the input even to be processed. + /// \return True if the event was handled or false if it was not. + virtual bool processGamepadEvent(InputEventInfo &inputEvent); + + virtual void findMouseControl(const GuiEvent &event); + virtual void refreshMouseControl(); + /// @} + + /// @name Keyboard Input + /// @{ + + /// Accelerator key map + struct AccKeyMap + { + GuiControl *ctrl; + U32 index; + U32 keyCode; + U32 modifier; + }; + Vector mAcceleratorMap; + + //for tooltip rendering + U32 mHoverControlStart; + GuiControl* mHoverControl; + Point2I mHoverPosition; + bool mHoverPositionSet; + U32 mHoverLeftControlTime; + + /// @} + + // Internal event handling callbacks for use with PlatformWindow. + void handleResize (WindowId did, S32 width, S32 height); + void handleAppEvent (WindowId did, S32 event); + void handlePaintEvent (WindowId did); + + PlatformWindow *mPlatformWindow; + GFXFence **mFences; + S32 mNextFenceIdx; + S32 mNumFences; + + static bool setProtectedNumFences( void *object, const char *index, const char *data ); + virtual void setupFences(); + + void checkLockMouseMove( const GuiEvent& event ); + +public: + DECLARE_CONOBJECT(GuiCanvas); + DECLARE_CATEGORY( "Gui Core" ); + + GuiCanvas(); + virtual ~GuiCanvas(); + + virtual bool onAdd(); + virtual void onRemove(); + + static void initPersistFields(); + + /// @name Rendering methods + /// + /// @{ + + /// Repaints the dirty regions of the canvas + /// @param preRenderOnly If set to true, only the onPreRender methods of all the GuiControls will be called + /// @param bufferSwap If set to true, it will swap buffers at the end. This is to support canvas-subclassing. + virtual void renderFrame(bool preRenderOnly, bool bufferSwap = true); + + /// Repaints the canvas by calling the platform window display event. + virtual void paint(); + + /// Repaints the canvas skipping rendering if the target time + /// has not yet elapsed. + /// @param elapsedMS The time since the last frame. + virtual void repaint(U32 elapsedMS); + + /// This signal is triggered at the beginning and end of each render frame + /// + /// @param beginFrame true at the beginning of the frame, false at the end + /// + typedef Signal GuiCanvasFrameSignal; + + static GuiCanvasFrameSignal& getGuiCanvasFrameSignal(); + + /// Adds a dirty area to the canvas so it will be updated on the next frame + /// @param pos Screen-coordinates of the upper-left hand corner of the dirty area + /// @param ext Width/height of the dirty area + virtual void addUpdateRegion(Point2I pos, Point2I ext); + + /// Resets the update regions so that the next call to renderFrame will + /// repaint the whole canvas + virtual void resetUpdateRegions(); + + /// Resizes the content control to match the canvas size. + void maintainSizing(); + + /// This builds a rectangle which encompasses all of the dirty regions to be + /// repainted + /// @param updateUnion (out) Rectangle which surrounds all dirty areas + virtual void buildUpdateUnion(RectI *updateUnion); + + /// This will swap the buffers at the end of renderFrame. It was added for canvas + /// sub-classes in case they wanted to do some custom code before the buffer + /// flip occured. + virtual void swapBuffers(); + + /// @} + + /// @name Canvas Content Management + /// @{ + + /// This returns the PlatformWindow owned by this Canvas + virtual PlatformWindow *getPlatformWindow() + { + return mPlatformWindow; + } + + /// This sets the content control to something different + /// @param gui New content control + virtual void setContentControl(GuiControl *gui); + + /// Returns the content control + virtual GuiControl *getContentControl(); + + /// Adds a dialog control onto the stack of dialogs + /// @param gui Dialog to add + /// @param layer Layer to put dialog on + /// @param center Center dialog on canvas. + virtual void pushDialogControl(GuiControl *gui, S32 layer = 0, bool center = false); + + /// Removes a specific layer of dialogs + /// @param layer Layer to pop off from + virtual void popDialogControl(S32 layer = 0); + + /// Removes a specific dialog control + /// @param gui Dialog to remove from the dialog stack + virtual void popDialogControl(GuiControl *gui); + ///@} + + /// This turns on/off front-buffer rendering + /// @param front True if all rendering should be done to the front buffer + virtual void setRenderFront(bool front) { mRenderFront = front; } + + /// @name Cursor commands + /// A cursor can be on, but not be shown. If a cursor is not on, than it does not + /// process input. + /// @{ + + /// Sets the cursor for the canvas. + /// @param cursor New cursor to use. + virtual void setCursor(GuiCursor *cursor); + S32 mCursorChanged; + + /// Returns true if the cursor is on. + virtual bool isCursorON() { return mCursorEnabled; } + + /// Sets if mouse events should be passed to the GUI even if the cursor is off. + /// @param onOff True if events should be passed to the GUI if the cursor is off + virtual void setForceMouseToGUI(bool onOff); + + /// Sets if the Torque cursor should be clamped to the window. + /// @param onOff True if the Torque cursor should be clamped against the window + virtual void setClampTorqueCursor(bool onOff); + + /// Returns if the Torque cursor is clamped to the window + virtual bool getClampTorqueCursor() { return mClampTorqueCursor; } + + /// Turns the cursor on or off. + /// @param onOff True if the cursor should be on. + virtual void setCursorON(bool onOff); + + /// Sets the position of the cursor + /// @param pt Point, in screenspace for the cursor + virtual void setCursorPos(const Point2I &pt); + + /// Returns the point, in screenspace, at which the cursor is located. + virtual Point2I getCursorPos(); + + /// Enable/disable rendering of the cursor. + /// @param state True if we should render cursor + virtual void showCursor(bool state); + + /// Returns true if the cursor is being rendered. + virtual bool isCursorShown(); + /// @} + + ///used by the tooltip resource + Point2I getCursorExtent() { return mDefaultCursor->getExtent(); } + + /// @name Input Processing + /// @{ + + /// Processes an input event + /// @see InputEvent + /// @param event Input event to process + virtual bool processInputEvent(InputEventInfo &inputEvent); + /// @} + + /// @name Mouse Methods + /// @{ + + /// When a control gets the mouse lock this means that that control gets + /// ALL mouse input and no other control receives any input. + /// @param lockingControl Control to lock mouse to + virtual void mouseLock(GuiControl *lockingControl); + + /// Unlocks the mouse from a control + /// @param lockingControl Control to unlock from + virtual void mouseUnlock(GuiControl *lockingControl); + + /// Returns the control which the mouse is over + virtual GuiControl* getMouseControl() { return mMouseControl; } + + /// Returns the control which the mouse is locked to if any + virtual GuiControl* getMouseLockedControl() { return mMouseCapturedControl; } + + /// Returns true if the left mouse button is down + virtual bool mouseButtonDown(void) { return mMouseButtonDown; } + + /// Returns true if the right mouse button is down + virtual bool mouseRightButtonDown(void) { return mMouseRightButtonDown; } + + /// @} + + /// @name Mouse input methods + /// These events process the events before passing them down to the + /// controls they effect. This allows for things such as the input + /// locking and such. + /// + /// Each of these methods corresponds to the action in it's method name + /// and processes the GuiEvent passed as a parameter + /// @{ + virtual void rootMouseUp(const GuiEvent &event); + virtual void rootMouseDown(const GuiEvent &event); + virtual void rootMouseMove(const GuiEvent &event); + virtual void rootMouseDragged(const GuiEvent &event); + + virtual void rootRightMouseDown(const GuiEvent &event); + virtual void rootRightMouseUp(const GuiEvent &event); + virtual void rootRightMouseDragged(const GuiEvent &event); + + virtual void rootMiddleMouseDown(const GuiEvent &event); + virtual void rootMiddleMouseUp(const GuiEvent &event); + virtual void rootMiddleMouseDragged(const GuiEvent &event); + + virtual bool rootMouseWheelUp(const GuiEvent &event); + virtual bool rootMouseWheelDown(const GuiEvent &event); + /// @} + + /// @name Keyboard input methods + /// First responders + /// + /// A first responder is a the GuiControl which responds first to input events + /// before passing them off for further processing. + /// @{ + + /// Moves the first responder to the next tabable controle + virtual bool tabNext(void); + + /// Moves the first responder to the previous tabable control + virtual bool tabPrev(void); + + /// Setups a keyboard accelerator which maps to a GuiControl. + /// + /// @param ctrl GuiControl to map to. + /// @param index + /// @param keyCode Key code. + /// @param modifier Shift, ctrl, etc. + virtual void addAcceleratorKey(GuiControl *ctrl, U32 index, U32 keyCode, U32 modifier); + + /// Sets the first responder. + /// @param firstResponder Control to designate as first responder + virtual void setFirstResponder(GuiControl *firstResponder); + + /// This is used to toggle processing of native OS accelerators, not + /// to be confused with the Torque accelerator key system, to keep them + /// from swallowing up keystrokes. Both GuiTextEditCtrl and GuiTextPadCtrl + /// use this method. + virtual void setNativeAcceleratorsEnabled( bool enabled ); + /// @} + + /// + virtual Point2I getWindowSize(); + + virtual void enableKeyboardTranslation(); + virtual void disableKeyboardTranslation(); + + virtual void setWindowTitle(const char *newTitle); + +private: + static const U32 MAX_GAMEPADS = 4; ///< The maximum number of supported gamepads + +#ifdef TORQUE_DEMO_PURCHASE +private: + PurchaseScreen* mPurchaseScreen; + U32 mLastPurchaseHideTime; + +public: + void showPurchaseScreen(bool show, bool startBlocker, const char* location, bool doExit); + void updatePurchaseScreen(const char* value); +#endif + +#ifdef TORQUE_DEMO_TIMEOUT +private: + void checkTimeOut(); +#endif +}; + +#endif diff --git a/Engine/source/gui/core/guiControl.cpp b/Engine/source/gui/core/guiControl.cpp new file mode 100644 index 000000000..2f5f41d99 --- /dev/null +++ b/Engine/source/gui/core/guiControl.cpp @@ -0,0 +1,2846 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/core/guiControl.h" + +#include "console/consoleTypes.h" +#include "console/console.h" +#include "console/consoleInternal.h" +#include "console/engineAPI.h" +#include "console/codeBlock.h" +#include "platform/event.h" +#include "gfx/bitmap/gBitmap.h" +#include "sim/actionMap.h" +#include "gui/core/guiCanvas.h" +#include "gui/core/guiDefaultControlRender.h" +#include "gui/editor/guiEditCtrl.h" +#include "gfx/gfxDrawUtil.h" + + +//#define DEBUG_SPEW + + +IMPLEMENT_CONOBJECT( GuiControl ); + +ConsoleDocClass( GuiControl, + "@brief Base class for all Gui control objects.\n\n" + + "GuiControl is the basis for the Gui system. It represents an individual control that can be placed on the canvas and with which " + "the mouse and keyboard can potentially interact with.\n\n" + + "@section GuiControl_Hierarchy Control Hierarchies\n" + + "GuiControls are arranged in a hierarchy. All children of a control are placed in their parent's coordinate space, i.e. their " + "coordinates are relative to the upper left corner of their immediate parent. When a control is moved, all its child controls " + "are moved along with it.\n\n" + + "Since GuiControl's are SimGroups, hierarchy also implies ownership. This means that if a control is destroyed, all its children " + "are destroyed along with it. It also means that a given control can only be part of a single GuiControl hierarchy. When adding a " + "control to another control, it will automatically be reparented from another control it may have previously been parented to.\n\n" + + "@section GuiControl_Layout Layout System\n" + + "GuiControls have a two-dimensional position and are rectangular in shape.\n\n" + + "@section GuiControl_Events Event System\n" + + "@section GuiControl_Profiles Control Profiles\n" + + "Common data accessed by GuiControls is stored in so-called \"Control Profiles.\" This includes font, color, and texture information. " + "By pooling this data in shared objects, the appearance of any number of controls can be changed quickly and easily by modifying " + "only the shared profile object.\n\n" + + "If not explicitly assigned a profile, a control will by default look for a profile object that matches its class name. This means " + "that the class GuiMyCtrl, for example, will look for a profile called 'GuiMyProfile'. If this profile cannot be found, the control " + "will fall back to GuiDefaultProfile which must be defined in any case for the Gui system to work.\n\n" + + "In addition to its primary profile, a control may be assigned a second profile called 'tooltipProfile' that will be used to render " + "tooltip popups for the control.\n\n" + + "@section GuiControl_Actions Triggered Actions\n" + + "@section GuiControl_FirstResponders First Responders\n" + + "At any time, a single control can be what is called the \"first responder\" on the GuiCanvas is placed on. This control " + "will be the first control to receive keyboard events not bound in the global ActionMap. If the first responder choses to " + "handle a particular keyboard event, \n\n" + + "@section GuiControl_Waking Waking and Sleeping\n" + + "@section GuiControl_VisibleActive Visibility and Activeness\n" + "By default, a GuiControl is active which means that it\n\n" + + "@see GuiCanvas\n" + "@see GuiControlProfile\n" + "@ingroup GuiCore\n" +); + +IMPLEMENT_CALLBACK( GuiControl, onAdd, void, (), (), + "Called when the control object is registered with the system after the control has been created." ); +IMPLEMENT_CALLBACK( GuiControl, onRemove, void, (), (), + "Called when the control object is removed from the system before it is deleted." ); +IMPLEMENT_CALLBACK( GuiControl, onWake, void, (), (), + "Called when the control is woken up.\n" + "@ref GuiControl_Waking" ); +IMPLEMENT_CALLBACK( GuiControl, onSleep, void, (), (), + "Called when the control is put to sleep.\n" + "@ref GuiControl_Waking" ); +IMPLEMENT_CALLBACK( GuiControl, onGainFirstResponder, void, (), (), + "Called when the control gains first responder status on the GuiCanvas.\n" + "@see setFirstResponder\n" + "@see makeFirstResponder\n" + "@see isFirstResponder\n" + "@ref GuiControl_FirstResponders" ); +IMPLEMENT_CALLBACK( GuiControl, onLoseFirstResponder, void, (), (), + "Called when the control loses first responder status on the GuiCanvas.\n" + "@see setFirstResponder\n" + "@see makeFirstResponder\n" + "@see isFirstResponder\n" + "@ref GuiControl_FirstResponders" ); +IMPLEMENT_CALLBACK( GuiControl, onAction, void, (), (), + "Called when the control's associated action is triggered and no 'command' is defined for the control.\n" + "@ref GuiControl_Actions" ); +IMPLEMENT_CALLBACK( GuiControl, onVisible, void, ( bool state ), ( state ), + "Called when the control changes its visibility state, i.e. when going from visible to invisible or vice versa.\n" + "@param state The new visibility state.\n" + "@see isVisible\n" + "@see setVisible\n" + "@ref GuiControl_VisibleActive" ); +IMPLEMENT_CALLBACK( GuiControl, onActive, void, ( bool state ), ( state ), + "Called when the control changes its activeness state, i.e. when going from active to inactive or vice versa.\n" + "@param stat The new activeness state.\n" + "@see isActive\n" + "@see setActive\n" + "@ref GuiControl_VisibleActive" ); +IMPLEMENT_CALLBACK( GuiControl, onDialogPush, void, (), (), + "Called when the control is pushed as a dialog onto the canvas.\n" + "@see GuiCanvas::pushDialog" ); +IMPLEMENT_CALLBACK( GuiControl, onDialogPop, void, (), (), + "Called when the control is removed as a dialog from the canvas.\n" + "@see GuiCanvas::popDialog" ); +IMPLEMENT_CALLBACK( GuiControl, onControlDragEnter, void, ( GuiControl* control, const Point2I& dropPoint ), ( control, dropPoint ), + "Called when a drag&drop operation through GuiDragAndDropControl has entered the control. This is only called for " + "topmost visible controls as the GuiDragAndDropControl moves over them.\n\n" + "@param control The payload of the drag operation.\n" + "@param dropPoint The point at which the payload would be dropped if it were released now. Relative to the canvas." ); +IMPLEMENT_CALLBACK( GuiControl, onControlDragExit, void, ( GuiControl* control, const Point2I& dropPoint ), ( control, dropPoint ), + "Called when a drag&drop operation through GuiDragAndDropControl has exited the control and moved over a different control. This is only called for " + "topmost visible controls as the GuiDragAndDropControl moves off of them.\n\n" + "@param control The payload of the drag operation.\n" + "@param dropPoint The point at which the payload would be dropped if it were released now. Relative to the canvas." ); +IMPLEMENT_CALLBACK( GuiControl, onControlDragged, void, ( GuiControl* control, const Point2I& dropPoint ), ( control, dropPoint ), + "Called when a drag&drop operation through GuiDragAndDropControl is moving across the control after it has entered it. This is only called for " + "topmost visible controls as the GuiDragAndDropControl moves across them.\n\n" + "@param control The payload of the drag operation.\n" + "@param dropPoint The point at which the payload would be dropped if it were released now. Relative to the canvas." ); +IMPLEMENT_CALLBACK( GuiControl, onControlDropped, void, ( GuiControl* control, const Point2I& dropPoint ), ( control, dropPoint ), + "Called when a drag&drop operation through GuiDragAndDropControl has completed and is dropping its payload onto the control. " + "This is only called for topmost visible controls as the GuiDragAndDropControl drops its payload on them.\n\n" + "@param control The control that is being dropped onto this control.\n" + "@param dropPoint The point at which the control is being dropped. Relative to the canvas." ); + + +GuiControl *GuiControl::smPrevResponder = NULL; +GuiControl *GuiControl::smCurResponder = NULL; +GuiEditCtrl*GuiControl::smEditorHandle = NULL; +bool GuiControl::smDesignTime = false; +GuiControl* GuiControl::smThisControl; + +IMPLEMENT_SCOPE( GuiAPI, Gui,, "" ); + +ImplementEnumType( GuiHorizontalSizing, + "Horizontal sizing behavior of a GuiControl.\n\n" + "@ingroup GuiCore" ) + { GuiControl::horizResizeRight, "right" }, + { GuiControl::horizResizeWidth, "width" }, + { GuiControl::horizResizeLeft, "left" }, + { GuiControl::horizResizeCenter, "center" }, + { GuiControl::horizResizeRelative, "relative" }, + { GuiControl::horizResizeWindowRelative, "windowRelative" } +EndImplementEnumType; + +ImplementEnumType( GuiVerticalSizing, + "Vertical sizing behavior of a GuiControl.\n\n" + "@ingroup GuiCore" ) + { GuiControl::vertResizeBottom, "bottom" }, + { GuiControl::vertResizeHeight, "height" }, + { GuiControl::vertResizeTop, "top" }, + { GuiControl::vertResizeCenter, "center" }, + { GuiControl::vertResizeRelative, "relative" }, + { GuiControl::vertResizeWindowRelative, "windowRelative" } +EndImplementEnumType; + +//----------------------------------------------------------------------------- + +GuiControl::GuiControl() : mAddGroup( NULL ), + mLayer(0), + mBounds(0,0,64,64), + mMinExtent(8,2), + mProfile(NULL), + mLangTable(NULL), + mFirstResponder(NULL), + mVisible(true), + mActive(true), + mAwake(false), + mHorizSizing(horizResizeRight), + mVertSizing(vertResizeBottom), + mTooltipProfile(NULL), + mTipHoverTime(1000), + mIsContainer(false), + mCanResize(true), + mCanHit( true ) +{ + mConsoleVariable = StringTable->EmptyString(); + mAcceleratorKey = StringTable->EmptyString(); + mLangTableName = StringTable->EmptyString(); + + mTooltip = StringTable->EmptyString(); + mRenderTooltipDelegate.bind( this, &GuiControl::defaultTooltipRender ); + + mCanSaveFieldDictionary = false; + mNotifyChildrenResized = true; +} + +//----------------------------------------------------------------------------- + +GuiControl::~GuiControl() +{ +} + +//----------------------------------------------------------------------------- + +void GuiControl::consoleInit() +{ + Con::addVariable( "$ThisControl", TYPEID< GuiControl >(), &smThisControl, + "The control for which a command is currently being evaluated. Only set during 'command' " + "and altCommand callbacks to the control for which the command or altCommand is invoked.\n" + "@ingroup GuiCore"); +} + +//----------------------------------------------------------------------------- + +void GuiControl::initPersistFields() +{ + addGroup( "Layout" ); + + addField("position", TypePoint2I, Offset(mBounds.point, GuiControl), + "The position relative to the parent control." ); + addField("extent", TypePoint2I, Offset(mBounds.extent, GuiControl), + "The width and height of the control." ); + addField("minExtent", TypePoint2I, Offset(mMinExtent, GuiControl), + "The minimum width and height of the control. The control will not be resized smaller than this." ); + addField("horizSizing", TYPEID< horizSizingOptions >(), Offset(mHorizSizing, GuiControl), + "The horizontal resizing behavior." ); + addField("vertSizing", TYPEID< vertSizingOptions >(), Offset(mVertSizing, GuiControl), + "The vertical resizing behavior." ); + + endGroup( "Layout" ); + + addGroup( "Control"); + + addProtectedField("profile", TYPEID< GuiControlProfile >(), Offset(mProfile, GuiControl), &setProfileProt, &defaultProtectedGetFn, + "The control profile that determines fill styles, font settings, etc." ); + + addProtectedField( "visible", TypeBool, Offset(mVisible, GuiControl), &_setVisible, &defaultProtectedGetFn, + "Whether the control is visible or hidden." ); + addProtectedField( "active", TypeBool, Offset( mActive, GuiControl ), &_setActive, &defaultProtectedGetFn, + "Whether the control is enabled for user interaction." ); + + addDeprecatedField("modal"); + addDeprecatedField("setFirstResponder"); + + addField("variable", TypeString, Offset(mConsoleVariable, GuiControl), + "Name of the variable to which the value of this control will be synchronized." ); + addField("command", TypeRealString, Offset(mConsoleCommand, GuiControl), + "Command to execute on the primary action of the control.\n\n" + "@note Within this script snippet, the control on which the #command is being executed is bound to " + "the global variable $ThisControl." ); + addField("altCommand", TypeRealString, Offset(mAltConsoleCommand, GuiControl), + "Command to execute on the secondary action of the control.\n\n" + "@note Within this script snippet, the control on which the #altCommand is being executed is bound to " + "the global variable $ThisControl." ); + addField("accelerator", TypeString, Offset(mAcceleratorKey, GuiControl), + "Key combination that triggers the control's primary action when the control is on the canvas." ); + + endGroup( "Control" ); + + addGroup( "ToolTip" ); + addProtectedField("tooltipProfile", TYPEID< GuiControlProfile >(), Offset(mTooltipProfile, GuiControl), &setTooltipProfileProt, &defaultProtectedGetFn, + "Control profile to use when rendering tooltips for this control." ); + addField("tooltip", TypeRealString, Offset(mTooltip, GuiControl), + "String to show in tooltip for this control." ); + addField("hovertime", TypeS32, Offset(mTipHoverTime, GuiControl), + "Time for mouse to hover over control until tooltip is shown (in milliseconds)." ); + endGroup( "ToolTip" ); + + addGroup( "Editing" ); + addField("isContainer", TypeBool, Offset(mIsContainer, GuiControl), + "If true, the control may contain child controls." ); + endGroup( "Editing" ); + + addGroup( "Localization" ); + addField("langTableMod", TypeString, Offset(mLangTableName, GuiControl), + "Name of string table to use for lookup of internationalized text." ); + endGroup( "Localization" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +bool GuiControl::processArguments(S32 argc, const char **argv) +{ + // argv[0] - The GuiGroup to add this control to when it's created. + // this is an optional parameter that may be specified at + // object creation time to organize a gui control into a + // subgroup of GuiControls and is useful in helping the + // gui editor group and sort the existent gui's in the Sim. + + // Specified group? + if( argc == 1 ) + { + StringTableEntry steIntName = StringTable->insert(argv[0]); + mAddGroup = dynamic_cast(Sim::getGuiGroup()->findObjectByInternalName( steIntName )); + if( mAddGroup == NULL ) + { + mAddGroup = new SimGroup(); + if( mAddGroup->registerObject() ) + { + mAddGroup->setInternalName( steIntName ); + Sim::getGuiGroup()->addObject( mAddGroup ); + } + else + { + SAFE_DELETE( mAddGroup ); + return false; + } + } + mAddGroup->addObject(this); + } + return true; +} + +//----------------------------------------------------------------------------- + +void GuiControl::awaken() +{ + PROFILE_SCOPE( GuiControl_awaken ); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[GuiControl] Waking up %i:%s (%s:%s) awake=%s", + getId(), getClassName(), getName(), getInternalName(), + mAwake ? "true" : "false" ); + #endif + + if( mAwake ) + return; + + // Wake up visible children. + + for( GuiControl::iterator iter = begin(); iter != end(); ++ iter ) + { + GuiControl* ctrl = static_cast< GuiControl* >( *iter ); + + if( ctrl->isVisible() && !ctrl->isAwake() ) + ctrl->awaken(); + } + + if( !mAwake && !onWake() ) + { + Con::errorf( ConsoleLogEntry::General, "GuiControl::awaken: failed onWake for obj: %i:%s (%s)", + getId(), getClassName(), getName() ); + mAwake = false; + } +} + +//----------------------------------------------------------------------------- + +void GuiControl::sleep() +{ + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[GuiControl] Putting to sleep %i:%s (%s:%s) awake=%s", + getId(), getClassName(), getName(), getInternalName(), + mAwake ? "true" : "false" ); + #endif + + if( !mAwake ) + return; + + iterator i; + for(i = begin(); i != end(); i++) + { + GuiControl *ctrl = static_cast(*i); + if( ctrl->isAwake() ) + ctrl->sleep(); + } + + if( mAwake ) + onSleep(); +} + +//============================================================================= +// Rendering. +//============================================================================= +// MARK: ---- Rendering ---- + +//----------------------------------------------------------------------------- + +void GuiControl::preRender() +{ + if( !mAwake ) + return; + + iterator i; + for(i = begin(); i != end(); i++) + { + GuiControl *ctrl = static_cast(*i); + ctrl->preRender(); + } + onPreRender(); +} + +//----------------------------------------------------------------------------- + +void GuiControl::onPreRender() +{ + // do nothing. +} + +//----------------------------------------------------------------------------- + +void GuiControl::onRender(Point2I offset, const RectI &updateRect) +{ + RectI ctrlRect(offset, getExtent()); + + //if opaque, fill the update rect with the fill color + if ( mProfile->mOpaque ) + GFX->getDrawUtil()->drawRectFill(ctrlRect, mProfile->mFillColor); + + //if there's a border, draw the border + if ( mProfile->mBorder ) + renderBorder(ctrlRect, mProfile); + + // Render Children + renderChildControls(offset, updateRect); +} + +//----------------------------------------------------------------------------- + +bool GuiControl::defaultTooltipRender( const Point2I &hoverPos, const Point2I &cursorPos, const char* tipText ) +{ + // Short Circuit. + if (!mAwake) + return false; + + if ( dStrlen( mTooltip ) == 0 && ( tipText == NULL || dStrlen( tipText ) == 0 ) ) + return false; + + String renderTip( mTooltip ); + if ( tipText != NULL ) + renderTip = tipText; + + // Need to have root. + GuiCanvas *root = getRoot(); + if ( !root ) + return false; + + GFont *font = mTooltipProfile->mFont; + + // Support for multi-line tooltip text... + + Vector startLineOffsets, lineLengths; + + font->wrapString( renderTip, U32_MAX, startLineOffsets, lineLengths ); + + // The width is the longest line. + U32 tipWidth = 0; + for ( U32 i = 0; i < lineLengths.size(); i++ ) + { + U32 width = font->getStrNWidth( renderTip.c_str() + startLineOffsets[i], lineLengths[i] ); + + if ( width > tipWidth ) + tipWidth = width; + } + + // The height is the number of lines times the height of the font. + U32 tipHeight = lineLengths.size() * font->getHeight(); + + // Vars used: + // Screensize (for position check) + // Offset to get position of cursor + // textBounds for text extent. + Point2I screensize = getRoot()->getWindowSize(); + Point2I offset = hoverPos; + Point2I textBounds; + + // Offset below cursor image + offset.y += 20; // TODO: Attempt to fix?: root->getCursorExtent().y; + + // Create text bounds... + + // Pixels above/below the text + const U32 vMargin = 2; + // Pixels left/right of the text + const U32 hMargin = 4; + + textBounds.x = tipWidth + hMargin * 2; + textBounds.y = tipHeight + vMargin * 2; + + // Check position/width to make sure all of the tooltip will be rendered + // 5 is given as a buffer against the edge + if ( screensize.x < offset.x + textBounds.x + 5 ) + offset.x = screensize.x - textBounds.x - 5; + + // And ditto for the height + if ( screensize.y < offset.y + textBounds.y + 5 ) + offset.y = hoverPos.y - textBounds.y - 5; + + RectI oldClip = GFX->getClipRect(); + + // Set rectangle for the box, and set the clip rectangle. + RectI rect( offset, textBounds ); + GFX->setClipRect( rect ); + + // Draw Filler bit, then border on top of that + GFX->getDrawUtil()->drawRectFill( rect, mTooltipProfile->mFillColor ); + GFX->getDrawUtil()->drawRect( rect, mTooltipProfile->mBorderColor ); + + // Draw the text centered in the tool tip box... + + GFX->getDrawUtil()->setBitmapModulation( mTooltipProfile->mFontColor ); + + for ( U32 i = 0; i < lineLengths.size(); i++ ) + { + Point2I start( hMargin, vMargin + i * font->getHeight() ); + const UTF8 *line = renderTip.c_str() + startLineOffsets[i]; + U32 lineLen = lineLengths[i]; + + GFX->getDrawUtil()->drawTextN( font, start + offset, line, lineLen, mProfile->mFontColors ); + } + + GFX->setClipRect( oldClip ); + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiControl::renderChildControls(Point2I offset, const RectI &updateRect) +{ + // Save the current clip rect + // so we can restore it at the end of this method. + RectI savedClipRect = GFX->getClipRect(); + + // offset is the upper-left corner of this control in screen coordinates + // updateRect is the intersection rectangle in screen coords of the control + // hierarchy. This can be set as the clip rectangle in most cases. + RectI clipRect = updateRect; + + iterator i; + for(i = begin(); i != end(); i++) + { + GuiControl *ctrl = static_cast(*i); + if (ctrl->mVisible) + { + Point2I childPosition = offset + ctrl->getPosition(); + RectI childClip(childPosition, ctrl->getExtent() + Point2I(1,1)); + + if (childClip.intersect(clipRect)) + { + GFX->setClipRect( childClip ); + GFX->setStateBlock(mDefaultGuiSB); + ctrl->onRender(childPosition, childClip); + } + } + } + + // Restore the clip rect to what it was at the start + // of this method. + GFX->setClipRect( savedClipRect ); +} + +//----------------------------------------------------------------------------- + +void GuiControl::setUpdateRegion(Point2I pos, Point2I ext) +{ + Point2I upos = localToGlobalCoord(pos); + GuiCanvas *root = getRoot(); + if (root) + { + root->addUpdateRegion(upos, ext); + } +} + +//----------------------------------------------------------------------------- + +void GuiControl::setUpdate() +{ + setUpdateRegion(Point2I(0,0), getExtent()); +} + +//----------------------------------------------------------------------------- + +void GuiControl::renderJustifiedText(Point2I offset, Point2I extent, const char *text) +{ + GFont *font = mProfile->mFont; + S32 textWidth = font->getStrWidthPrecise((const UTF8*)text); + U32 textHeight = font->getHeight(); + + Point2I start( 0, 0 ); + + // Align horizontal. + + if( textWidth > extent.x ) + { + // If the text is longer then the box size, (it'll get clipped) so + // force Left Justify + start.x = 0; + } + else + switch( mProfile->mAlignment ) + { + case GuiControlProfile::RightJustify: + start.x = extent.x - textWidth; + break; + + case GuiControlProfile::TopJustify: + case GuiControlProfile::BottomJustify: + case GuiControlProfile::CenterJustify: + start.x = ( extent.x - textWidth) / 2; + break; + + case GuiControlProfile::LeftJustify: + start.x = 0; + break; + } + + // Align vertical. + + if( textHeight > extent.y ) + { + start.y = 0 - ( textHeight - extent.y ) / 2; + } + else + switch( mProfile->mAlignment ) + { + case GuiControlProfile::TopJustify: + start.y = 0; + break; + + case GuiControlProfile::BottomJustify: + start.y = extent.y - textHeight - 1; + break; + + default: + // Center vertical. + start.y = ( extent.y - font->getHeight() ) / 2; + break; + } + + GFX->getDrawUtil()->drawText( font, start + offset, text, mProfile->mFontColors ); +} + +//============================================================================= +// Events. +//============================================================================= +// MARK: ---- Events ---- + +//----------------------------------------------------------------------------- + +bool GuiControl::onAdd() +{ + // Let Parent Do Work. + if ( !Parent::onAdd() ) + return false; + + // Grab the classname of this object + const char *cName = getClassName(); + + // if we're a pure GuiControl, then we're a container by default. + if ( dStrcmp( "GuiControl", cName ) == 0 ) + mIsContainer = true; + + // Add to root group. + if ( mAddGroup == NULL ) + mAddGroup = Sim::getGuiGroup(); + mAddGroup->addObject(this); + + // If we don't have a profile we must assign one now. + // Try assigning one based on the control's class name... + if ( !mProfile ) + { + String name = getClassName(); + + if ( name.isNotEmpty() ) + { + U32 pos = name.find( "Ctrl" ); + + if ( pos != -1 ) + name.replace( pos, 4, "Profile" ); + else + name += "Profile"; + + GuiControlProfile *profile = NULL; + if ( Sim::findObject( name, profile ) ) + setControlProfile( profile ); + } + } + + // Try assigning the default profile... + if ( !mProfile ) + { + GuiControlProfile *profile = NULL; + Sim::findObject( "GuiDefaultProfile", profile ); + + AssertISV( profile != NULL, avar("GuiControl::onAdd() unable to find specified profile and GuiDefaultProfile does not exist!") ); + + setControlProfile( profile ); + } + + // We must also assign a valid TooltipProfile... + if ( !mTooltipProfile ) + { + GuiControlProfile *profile = NULL; + Sim::findObject( "GuiTooltipProfile", profile ); + + AssertISV( profile != NULL, avar("GuiControl::onAdd() unable to find specified tooltip profile and GuiTooltipProfile does not exist!") ); + + setTooltipProfile( profile ); + } + + // Notify Script. + onAdd_callback(); + + GFXStateBlockDesc d; + + d.cullDefined = true; + d.cullMode = GFXCullNone; + d.zDefined = true; + d.zEnable = false; + + mDefaultGuiSB = GFX->createStateBlock( d ); + + // Return Success. + return true; +} + +//----------------------------------------------------------------------------- + +void GuiControl::onRemove() +{ + // If control is still awake, put it to sleep first. Otherwise, we'll see an + // onSleep() call triggered when we are removed from our parent. + + if( mAwake ) + sleep(); + + // Only invoke script callbacks if they can be received + onRemove_callback(); + + if ( mProfile ) + { + mProfile->decUseCount(); + mProfile = NULL; + } + + if ( mTooltipProfile ) + { + mTooltipProfile->decUseCount(); + mTooltipProfile = NULL; + } + + clearFirstResponder(); + + Parent::onRemove(); +} + +//----------------------------------------------------------------------------- + +void GuiControl::onDeleteNotify(SimObject *object) +{ + if( object == mProfile ) + { + GuiControlProfile* profile; + Sim::findObject( "GuiDefaultProfile", profile ); + + if ( profile == mProfile ) + mProfile = NULL; + else + setControlProfile( profile ); + } + if (object == mTooltipProfile) + { + GuiControlProfile* profile; + Sim::findObject( "GuiDefaultProfile", profile ); + + if ( profile == mTooltipProfile ) + mTooltipProfile = NULL; + else + setTooltipProfile( profile ); + } +} + +//----------------------------------------------------------------------------- + +bool GuiControl::onWake() +{ + PROFILE_SCOPE( GuiControl_onWake ); + + AssertFatal( !mAwake, "GuiControl::onWake() - control is already awake" ); + if( mAwake ) + return true; + + // [tom, 4/18/2005] Cause mLangTable to be refreshed in case it was changed + mLangTable = NULL; + + //set the flag + mAwake = true; + + //set the layer + GuiCanvas *root = getRoot(); + GuiControl *parent = getParent(); + if (parent && parent != root) + mLayer = parent->mLayer; + + //make sure the first responder exists + if (! mFirstResponder) + mFirstResponder = findFirstTabable(); + + //increment the profile + mProfile->incLoadCount(); + mTooltipProfile->incLoadCount(); + + // Only invoke script callbacks if we have a namespace in which to do so + // This will suppress warnings + onWake_callback(); + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiControl::onSleep() +{ + AssertFatal( mAwake, "GuiControl::onSleep() - control is already asleep" ); + if( !mAwake ) + return; + + clearFirstResponder(); + mouseUnlock(); + + // Only invoke script callbacks if we have a namespace in which to do so + // This will suppress warnings + onSleep_callback(); + + //decrement the profile reference + mProfile->decLoadCount(); + mTooltipProfile->decLoadCount(); + + // Set Flag + mAwake = false; +} + +//----------------------------------------------------------------------------- + +void GuiControl::onChildAdded( GuiControl *child ) +{ + // Base class does not make use of this +} + +//----------------------------------------------------------------------------- + +void GuiControl::onChildRemoved( GuiControl *child ) +{ + // Base does nothing with this +} + +//----------------------------------------------------------------------------- + +void GuiControl::onGroupRemove() +{ + // If we have a first responder in our hierarchy, + // make sure to kill it off. + + if( mFirstResponder ) + mFirstResponder->clearFirstResponder(); + else + { + GuiCanvas* root = getRoot(); + if( root ) + { + GuiControl* firstResponder = root->getFirstResponder(); + if( firstResponder && firstResponder->isChildOfGroup( this ) ) + firstResponder->clearFirstResponder(); + } + } + + // If we are awake, put us to sleep. + + if( isAwake() ) + sleep(); +} + +//----------------------------------------------------------------------------- + +bool GuiControl::onInputEvent(const InputEventInfo &event) +{ + // Do nothing by default... + return( false ); +} + +//----------------------------------------------------------------------------- + +bool GuiControl::onKeyDown(const GuiEvent &event) +{ + //pass the event to the parent + GuiControl *parent = getParent(); + if (parent) + return parent->onKeyDown(event); + else + return false; +} + +//----------------------------------------------------------------------------- + +bool GuiControl::onKeyRepeat(const GuiEvent &event) +{ + // default to just another key down. + return onKeyDown(event); +} + +//----------------------------------------------------------------------------- + +bool GuiControl::onKeyUp(const GuiEvent &event) +{ + //pass the event to the parent + GuiControl *parent = getParent(); + if (parent) + return parent->onKeyUp(event); + else + return false; +} + +//----------------------------------------------------------------------------- + +void GuiControl::onMouseUp(const GuiEvent &event) +{ +} + +//----------------------------------------------------------------------------- + +void GuiControl::onMouseDown(const GuiEvent &event) +{ + if ( !mVisible || !mAwake ) + return; + + execConsoleCallback(); +} + +//----------------------------------------------------------------------------- + +void GuiControl::onMouseMove(const GuiEvent &event) +{ + //if this control is a dead end, make sure the event stops here + if ( !mVisible || !mAwake ) + return; + + //pass the event to the parent + GuiControl *parent = getParent(); + if ( parent ) + parent->onMouseMove( event ); +} + +//----------------------------------------------------------------------------- + +void GuiControl::onMouseDragged(const GuiEvent &event) +{ +} + +//----------------------------------------------------------------------------- + +void GuiControl::onMouseEnter(const GuiEvent &event) +{ +} + +//----------------------------------------------------------------------------- + +void GuiControl::onMouseLeave(const GuiEvent &event) +{ +} + +//----------------------------------------------------------------------------- + +bool GuiControl::onMouseWheelUp( const GuiEvent &event ) +{ + //if this control is a dead end, make sure the event stops here + if ( !mVisible || !mAwake ) + return true; + + //pass the event to the parent + GuiControl *parent = getParent(); + if ( parent ) + return parent->onMouseWheelUp( event ); + else + return false; +} + +//----------------------------------------------------------------------------- + +bool GuiControl::onMouseWheelDown( const GuiEvent &event ) +{ + //if this control is a dead end, make sure the event stops here + if ( !mVisible || !mAwake ) + return true; + + //pass the event to the parent + GuiControl *parent = getParent(); + if ( parent ) + return parent->onMouseWheelDown( event ); + else + return false; +} + +//----------------------------------------------------------------------------- + +void GuiControl::onRightMouseDown(const GuiEvent &) +{ +} + +//----------------------------------------------------------------------------- + +void GuiControl::onRightMouseUp(const GuiEvent &) +{ +} + +//----------------------------------------------------------------------------- + +void GuiControl::onRightMouseDragged(const GuiEvent &) +{ +} + +//----------------------------------------------------------------------------- + +void GuiControl::onMiddleMouseDown(const GuiEvent &) +{ +} + +//----------------------------------------------------------------------------- + +void GuiControl::onMiddleMouseUp(const GuiEvent &) +{ +} + +//----------------------------------------------------------------------------- + +void GuiControl::onMiddleMouseDragged(const GuiEvent &) +{ +} + +//----------------------------------------------------------------------------- + +bool GuiControl::onGamepadButtonDown(const GuiEvent &event) +{ + return onKeyDown(event); +} + +//----------------------------------------------------------------------------- + +bool GuiControl::onGamepadButtonUp(const GuiEvent &event) +{ + return onKeyUp(event); +} + +//----------------------------------------------------------------------------- + +bool GuiControl::onGamepadAxisUp(const GuiEvent &event) +{ + //pass the event to the parent + GuiControl *parent = getParent(); + if (parent) + { + return parent->onGamepadAxisUp(event); + } + else + { + return false; + } +} + +//----------------------------------------------------------------------------- + +bool GuiControl::onGamepadAxisDown(const GuiEvent &event) +{ + //pass the event to the parent + GuiControl *parent = getParent(); + if (parent) + { + return parent->onGamepadAxisDown(event); + } + else + { + return false; + } +} + +//----------------------------------------------------------------------------- + +bool GuiControl::onGamepadAxisLeft(const GuiEvent &event) +{ + //pass the event to the parent + GuiControl *parent = getParent(); + if (parent) + { + return parent->onGamepadAxisLeft(event); + } + else + { + return false; + } +} + +//----------------------------------------------------------------------------- + +bool GuiControl::onGamepadAxisRight(const GuiEvent &event) +{ + //pass the event to the parent + GuiControl *parent = getParent(); + if (parent) + { + return parent->onGamepadAxisRight(event); + } + else + { + return false; + } +} + +//----------------------------------------------------------------------------- + +bool GuiControl::onGamepadTrigger(const GuiEvent &event) +{ + //pass the event to the parent + GuiControl *parent = getParent(); + if (parent) + { + return parent->onGamepadTrigger(event); + } + else + { + return false; + } +} + +//----------------------------------------------------------------------------- + +void GuiControl::onAction() +{ + if (! mActive) + return; + + //execute the console command + if( mConsoleCommand.isNotEmpty() ) + { + execConsoleCallback(); + } + else + onAction_callback(); +} + +//----------------------------------------------------------------------------- + +void GuiControl::onMessage( GuiControl* , S32 ) +{ +} + +//----------------------------------------------------------------------------- + +void GuiControl::onDialogPush() +{ + // Notify Script. + onDialogPush_callback(); +} + +//----------------------------------------------------------------------------- + +void GuiControl::onDialogPop() +{ + // Notify Script. + onDialogPop_callback(); +} + +//----------------------------------------------------------------------------- + +void GuiControl::inspectPreApply() +{ +} + +//----------------------------------------------------------------------------- + +void GuiControl::inspectPostApply() +{ + // To not require every control to hook onto inspectPostApply to make + // all changed properties take effect, just put controls through a fake + // sleep&wake sequence. This should generally get most property changes + // to show. + // + // Don't do this with the canvas. + + if( mAwake && !dynamic_cast< GuiCanvas* >( this ) ) + { + bool isContainer = mIsContainer; + + onSleep(); + onWake(); + + mIsContainer = isContainer; + } +} + +//============================================================================= +// Layout. +//============================================================================= +// MARK: ---- Layout ---- + +//----------------------------------------------------------------------------- + +void GuiControl::setSizing(S32 horz, S32 vert) +{ + mHorizSizing = horz; + mVertSizing = vert; +} + +//----------------------------------------------------------------------------- + +bool GuiControl::resize(const Point2I &newPosition, const Point2I &newExtent) +{ + const Point2I minExtent = getMinExtent(); + Point2I actualNewExtent = Point2I(getMax(minExtent.x, newExtent.x), + getMax(minExtent.y, newExtent.y)); + + // only do the child control resizing stuff if you really need to. + const RectI bounds = getBounds(); + + // If we didn't size anything, return false to indicate such + bool extentChanged = (actualNewExtent != bounds.extent); + bool positionChanged = (newPosition != bounds.point); + if (!extentChanged && !positionChanged ) + return false; + + // Update Position + if ( positionChanged ) + mBounds.point = newPosition; + + // Update Extent + if( extentChanged ) + { + //call set update both before and after + setUpdate(); + + mBounds.extent = actualNewExtent; + + // Obey the flag! + // Could be set if we are resizing in response to a child resizing! + if ( mNotifyChildrenResized ) + { + iterator i; + for(i = begin(); i != end(); i++) + { + GuiControl *ctrl = static_cast(*i); + ctrl->parentResized(RectI(bounds.point, bounds.extent), RectI(newPosition,actualNewExtent)); + } + } + + GuiControl *parent = getParent(); + if (parent) + parent->childResized(this); + setUpdate(); + } + + // We sized something + if( extentChanged ) + return true; + + // Note : We treat a repositioning as no sizing happening + // because parent's should really not need to know when they + // have moved, as it should not affect any child sizing since + // all child bounds are relative to the parent's 0,0 + return false; + +} + +//----------------------------------------------------------------------------- + +bool GuiControl::setPosition( const Point2I &newPosition ) +{ + return resize( newPosition, mBounds.extent ); +} + +//----------------------------------------------------------------------------- + +bool GuiControl::setExtent( const Point2I &newExtent ) +{ + return resize( mBounds.point, newExtent ); +} + +//----------------------------------------------------------------------------- + +bool GuiControl::setBounds( const RectI &newBounds ) +{ + return resize( newBounds.point, newBounds.extent ); +} + +//----------------------------------------------------------------------------- + +void GuiControl::setLeft( S32 newLeft ) +{ + resize( Point2I( newLeft, mBounds.point.y), mBounds.extent ); +} + +//----------------------------------------------------------------------------- + +void GuiControl::setTop( S32 newTop ) +{ + resize( Point2I( mBounds.point.x, newTop ), mBounds.extent ); +} + +//----------------------------------------------------------------------------- + +void GuiControl::setWidth( S32 newWidth ) +{ + resize( mBounds.point, Point2I( newWidth, mBounds.extent.y ) ); +} + +//----------------------------------------------------------------------------- + +void GuiControl::setHeight( S32 newHeight ) +{ + resize( mBounds.point, Point2I( mBounds.extent.x, newHeight ) ); +} + +//----------------------------------------------------------------------------- + +void GuiControl::childResized( GuiControl* ) +{ +} + +//----------------------------------------------------------------------------- + +void GuiControl::parentResized(const RectI &oldParentRect, const RectI &newParentRect) +{ + Point2I newPosition = getPosition(); + Point2I newExtent = getExtent(); + + S32 deltaX = newParentRect.extent.x - oldParentRect.extent.x; + S32 deltaY = newParentRect.extent.y - oldParentRect.extent.y; + + if (mHorizSizing == horizResizeCenter) + newPosition.x = (newParentRect.extent.x - getWidth()) >> 1; + else if (mHorizSizing == horizResizeWidth) + newExtent.x += deltaX; + else if (mHorizSizing == horizResizeLeft) + newPosition.x += deltaX; + else if (mHorizSizing == horizResizeRelative && oldParentRect.extent.x != 0) + { + S32 newLeft = mRoundToNearest( ( F32( newPosition.x ) / F32( oldParentRect.extent.x ) ) * F32( newParentRect.extent.x ) ); + S32 newWidth = mRoundToNearest( ( F32( newExtent.x ) / F32( oldParentRect.extent.x ) ) * F32( newParentRect.extent.x ) ); + + newPosition.x = newLeft; + newExtent.x = newWidth; + } + + if (mVertSizing == vertResizeCenter) + newPosition.y = (newParentRect.extent.y - getHeight()) >> 1; + else if (mVertSizing == vertResizeHeight) + newExtent.y += deltaY; + else if (mVertSizing == vertResizeTop) + newPosition.y += deltaY; + else if(mVertSizing == vertResizeRelative && oldParentRect.extent.y != 0) + { + S32 newTop = mRoundToNearest( ( F32( newPosition.y ) / F32( oldParentRect.extent.y ) ) * F32( newParentRect.extent.y ) ); + S32 newHeight = mRoundToNearest( ( F32( newExtent.y ) / F32( oldParentRect.extent.y ) ) * F32( newParentRect.extent.y ) ); + + newPosition.y = newTop; + newExtent.y = newHeight; + } + + // Resizing Re factor [9/18/2006] + // Only resize if our minExtent is satisfied with it. + Point2I minExtent = getMinExtent(); + if( newExtent.x >= minExtent.x && newExtent.y >= minExtent.y ) + resize(newPosition, newExtent); +} + +//----------------------------------------------------------------------------- + +Point2I GuiControl::localToGlobalCoord(const Point2I &src) +{ + Point2I ret = src; + ret += getPosition(); + GuiControl *walk = getParent(); + while(walk) + { + ret += walk->getPosition(); + walk = walk->getParent(); + } + return ret; +} + +//----------------------------------------------------------------------------- + +Point2I GuiControl::globalToLocalCoord(const Point2I &src) +{ + Point2I ret = src; + ret -= getPosition(); + GuiControl *walk = getParent(); + while(walk) + { + ret -= walk->getPosition(); + walk = walk->getParent(); + } + return ret; +} + +//----------------------------------------------------------------------------- + +bool GuiControl::cursorInControl() +{ + GuiCanvas *root = getRoot(); + if (! root) return false; + + Point2I pt = root->getCursorPos(); + Point2I extent = getExtent(); + Point2I offset = localToGlobalCoord(Point2I(0, 0)); + if (pt.x >= offset.x && pt.y >= offset.y && + pt.x < offset.x + extent.x && pt.y < offset.y + extent.y) + { + return true; + } + else + { + return false; + } +} + +//----------------------------------------------------------------------------- + +bool GuiControl::pointInControl(const Point2I& parentCoordPoint) +{ + const RectI &bounds = getBounds(); + S32 xt = parentCoordPoint.x - bounds.point.x; + S32 yt = parentCoordPoint.y - bounds.point.y; + return xt >= 0 && yt >= 0 && xt < bounds.extent.x && yt < bounds.extent.y; +} + +//============================================================================= +// Properties. +//============================================================================= +// MARK: ---- Properties ---- + +//----------------------------------------------------------------------------- + +void GuiControl::setTooltipProfile( GuiControlProfile *prof ) +{ + AssertFatal( prof, "GuiControl::setTooltipProfile: invalid profile" ); + + if ( prof == mTooltipProfile ) + return; + + bool skipAwaken = false; + + if ( mTooltipProfile == NULL ) + skipAwaken = true; + + if( mTooltipProfile ) + mTooltipProfile->decUseCount(); + + if ( mAwake && mTooltipProfile ) + mTooltipProfile->decLoadCount(); + + // Clear the delete notification we previously set up + if ( mTooltipProfile ) + clearNotify( mTooltipProfile ); + + mTooltipProfile = prof; + mTooltipProfile->incUseCount(); + if ( mAwake ) + mTooltipProfile->incLoadCount(); + + // Make sure that the new profile will notify us when it is deleted + if ( mTooltipProfile ) + deleteNotify( mTooltipProfile ); + + // force an update when the profile is changed + if ( mAwake && !skipAwaken ) + { + sleep(); + + if( !Sim::isShuttingDown() ) + awaken(); + } +} + +//----------------------------------------------------------------------------- + +void GuiControl::setControlProfile( GuiControlProfile *prof ) +{ + AssertFatal( prof, "GuiControl::setControlProfile: invalid profile" ); + + if ( prof == mProfile ) + return; + + bool skipAwaken = false; + + if ( mProfile == NULL ) + skipAwaken = true; + + if( mProfile ) + mProfile->decUseCount(); + + if ( mAwake && mProfile ) + mProfile->decLoadCount(); + + // Clear the delete notification we previously set up + if ( mProfile ) + clearNotify( mProfile ); + + mProfile = prof; + mProfile->incUseCount(); + if ( mAwake ) + mProfile->incLoadCount(); + + // Make sure that the new profile will notify us when it is deleted + if ( mProfile ) + deleteNotify( mProfile ); + + // force an update when the profile is changed + if ( mAwake && !skipAwaken ) + { + sleep(); + + if( !Sim::isShuttingDown() ) + awaken(); + } +} + +//----------------------------------------------------------------------------- + +bool GuiControl::setProfileProt( void *object, const char *index, const char *data ) +{ + GuiControl* ctrl = static_cast( object ); + GuiControlProfile *prof = dynamic_cast( Sim::findObject( data ) ); + if ( prof == NULL ) + return false; + + // filter through our setter, for consistency + ctrl->setControlProfile( prof ); + + // ask the console not to set the data, because we've already set it + return false; +} + +//----------------------------------------------------------------------------- + +bool GuiControl::setTooltipProfileProt( void *object, const char *index, const char *data ) +{ + GuiControl* ctrl = static_cast( object ); + GuiControlProfile *prof = dynamic_cast( Sim::findObject( data ) ); + if ( prof == NULL ) + return false; + + // filter through our setter, for consistency + ctrl->setTooltipProfile( prof ); + + // ask the console not to set the data, because we've already set it + return false; +} + +//----------------------------------------------------------------------------- + + +const char *GuiControl::getScriptValue() +{ + return NULL; +} + +//----------------------------------------------------------------------------- + +void GuiControl::setScriptValue( const char* ) +{ +} + +//----------------------------------------------------------------------------- + +void GuiControl::setConsoleVariable(const char *variable) +{ + if (variable) + { + mConsoleVariable = StringTable->insert(variable); + } + else + { + mConsoleVariable = StringTable->EmptyString(); + } +} + +//----------------------------------------------------------------------------- + +void GuiControl::setConsoleCommand( const String& newCmd ) +{ + mConsoleCommand = newCmd; +} + +//----------------------------------------------------------------------------- + +const char * GuiControl::getConsoleCommand() +{ + return mConsoleCommand; +} + +//----------------------------------------------------------------------------- + +void GuiControl::setVariable(const char *value) +{ + if (mConsoleVariable[0]) + Con::setVariable(mConsoleVariable, value); +} + +//----------------------------------------------------------------------------- + +void GuiControl::setIntVariable(S32 value) +{ + if (mConsoleVariable[0]) + Con::setIntVariable(mConsoleVariable, value); +} + +//----------------------------------------------------------------------------- + +void GuiControl::setFloatVariable(F32 value) +{ + if (mConsoleVariable[0]) + Con::setFloatVariable(mConsoleVariable, value); +} + +//----------------------------------------------------------------------------- + +const char * GuiControl::getVariable() +{ + if (mConsoleVariable[0]) + return Con::getVariable(mConsoleVariable); + else return NULL; +} + +//----------------------------------------------------------------------------- + +S32 GuiControl::getIntVariable() +{ + if (mConsoleVariable[0]) + return Con::getIntVariable(mConsoleVariable); + else return 0; +} + +//----------------------------------------------------------------------------- + +F32 GuiControl::getFloatVariable() +{ + if (mConsoleVariable[0]) + return Con::getFloatVariable(mConsoleVariable); + else return 0.0f; +} + +//----------------------------------------------------------------------------- + +void GuiControl::setVisible(bool value) +{ + mVisible = value; + + setUpdate(); + + for( iterator i = begin(); i != end(); i++) + { + GuiControl *ctrl = static_cast(*i); + ctrl->clearFirstResponder(); + } + + GuiControl *parent = getParent(); + if( parent ) + { + parent->childResized( this ); + + // If parent is visible and awake and this control has just + // become visible but was sleeping, wake it up now. + + if( parent->isVisible() && parent->isAwake() + && this->isVisible() && !this->isAwake() ) + awaken(); + } + + if( getNamespace() ) // May be called during construction. + onVisible_callback( value ); +} + +//----------------------------------------------------------------------------- + +void GuiControl::setActive( bool value ) +{ + if( mActive == value ) + return; + + mActive = value; + + if ( !mActive ) + clearFirstResponder(); + + if ( mVisible && mAwake ) + setUpdate(); + + if( getNamespace() ) // May be called during construction. + onActive_callback( value ); + + // Pass activation on to children. + + for( iterator iter = begin(); iter != end(); ++ iter ) + { + GuiControl* child = dynamic_cast< GuiControl* >( *iter ); + if( child ) + child->setActive( value ); + } +} + +//============================================================================= +// Persistence. +//============================================================================= +// MARK: ---- Persistence ---- + +//----------------------------------------------------------------------------- + +bool GuiControl::getCanSaveParent() +{ + GuiControl *walk = this; + while(walk) + { + if(!walk->getCanSave()) + return false; + + walk = walk->getParent(); + } + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiControl::write(Stream &stream, U32 tabStop, U32 flags) +{ + //note: this will return false if either we, or any of our parents, are non-save controls + bool bCanSave = ( flags & IgnoreCanSave ) || ( flags & NoCheckParentCanSave && getCanSave() ) || getCanSaveParent(); + StringTableEntry steName = mAddGroup->getInternalName(); + if(bCanSave && mAddGroup && (steName != NULL) && (steName != StringTable->insert("null")) && getName() ) + { + MutexHandle handle; + handle.lock(mMutex); + + // export selected only? + if((flags & SelectedOnly) && !isSelected()) + { + for(U32 i = 0; i < size(); i++) + (*this)[i]->write(stream, tabStop, flags); + + return; + + } + + stream.writeTabs(tabStop); + char buffer[1024]; + dSprintf(buffer, sizeof(buffer), "new %s(%s,%s) {\r\n", getClassName(), getName() ? getName() : "", mAddGroup->getInternalName()); + stream.write(dStrlen(buffer), buffer); + writeFields(stream, tabStop + 1); + + if(size()) + { + stream.write(2, "\r\n"); + for(U32 i = 0; i < size(); i++) + (*this)[i]->write(stream, tabStop + 1, flags); + } + + stream.writeTabs(tabStop); + stream.write(4, "};\r\n"); + } + else if (bCanSave) + Parent::write( stream, tabStop, flags ); + +} + +//============================================================================= +// Hierarchies. +//============================================================================= +// MARK: ---- Hierarchies ---- + +//----------------------------------------------------------------------------- + +void GuiControl::addObject(SimObject *object) +{ + GuiControl *ctrl = dynamic_cast(object); + if(object->getGroup() == this) + return; + + AssertFatal( ctrl, "GuiControl::addObject() - cannot add non-GuiControl as child of GuiControl" ); + + Parent::addObject(object); + + AssertFatal(!ctrl->isAwake(), "GuiControl::addObject: object is already awake before add"); + if( mAwake ) + ctrl->awaken(); + + // If we are a child, notify our parent that we've been removed + GuiControl *parent = ctrl->getParent(); + if( parent ) + parent->onChildAdded( ctrl ); +} + +//----------------------------------------------------------------------------- + +void GuiControl::removeObject(SimObject *object) +{ + GuiControl* ctrl = static_cast< GuiControl* >( object ); + if( mAwake && ctrl->isAwake() ) + ctrl->sleep(); + + onChildRemoved( ctrl ); + + Parent::removeObject(object); +} + +//----------------------------------------------------------------------------- + +GuiControl *GuiControl::getParent() +{ + SimObject *obj = getGroup(); + if (GuiControl* gui = dynamic_cast(obj)) + return gui; + return 0; +} + +//----------------------------------------------------------------------------- + +GuiCanvas *GuiControl::getRoot() +{ + GuiControl *root = NULL; + GuiControl *parent = getParent(); + while (parent) + { + root = parent; + parent = parent->getParent(); + } + if (root) + return dynamic_cast(root); + else + return NULL; +} + +//----------------------------------------------------------------------------- + +bool GuiControl::acceptsAsChild( SimObject* object ) const +{ + return ( dynamic_cast< GuiControl* >( object ) != NULL ); +} + +//----------------------------------------------------------------------------- + +GuiControl* GuiControl::findHitControl(const Point2I &pt, S32 initialLayer) +{ + iterator i = end(); // find in z order (last to first) + + while (i != begin()) + { + i--; + GuiControl *ctrl = static_cast(*i); + if (initialLayer >= 0 && ctrl->mLayer > initialLayer) + { + continue; + } + + else if (ctrl->mVisible && ctrl->mCanHit && ctrl->pointInControl(pt)) + { + Point2I ptemp = pt - ctrl->getPosition(); + GuiControl *hitCtrl = ctrl->findHitControl(ptemp); + + if ( hitCtrl->mProfile->mModal ) + return hitCtrl; + } + } + + if( mCanHit ) + return this; + return NULL; +} + +//----------------------------------------------------------------------------- + +bool GuiControl::findHitControls( const RectI& rect, Vector< GuiControl* >& outResult, U32 flags, S32 initialLayer, U32 depth ) +{ + if( !mVisible ) + return false; + else if( !mCanHit && flags & HIT_NoCanHitNoRecurse ) + return false; + + // Check for hit. If not full-box, always counts. + + bool isHit = mVisible; + if( flags & HIT_FullBoxOnly ) + { + RectI rectInParentSpace = rect; + rectInParentSpace.point += getPosition(); + + isHit &= rectInParentSpace.contains( getBounds() ); + } + else + isHit &= mCanHit; + + // If we have a hit and should not recurse into children, + // return us. + + if( isHit && flags & HIT_ParentPreventsChildHit && depth > 0 ) + { + outResult.push_back( this ); + return true; + } + + // Check child controls. + + bool haveFoundChild = false; + iterator i = end(); + + while( i != begin() ) + { + i --; + + GuiControl* ctrl = static_cast< GuiControl* >( *i ); + if( initialLayer >= 0 && ctrl->mLayer > initialLayer ) + continue; + + if( ctrl->getBounds().overlaps( rect ) ) + { + RectI transposedRect = rect; + transposedRect.point -= ctrl->getPosition(); + + if( ctrl->findHitControls( transposedRect, outResult, flags, -1, depth + 1 ) ) + haveFoundChild = true; + } + } + + if( ( !haveFoundChild || flags & HIT_AddParentHits ) && isHit ) + { + outResult.push_back( this ); + return true; + } + + return haveFoundChild; +} + +//----------------------------------------------------------------------------- + +GuiControl* GuiControl::findFirstTabable() +{ + // No tabbing if the control is disabled or hidden. + if ( !mAwake || !mVisible ) + return NULL; + + GuiControl *tabCtrl = NULL; + iterator i; + for (i = begin(); i != end(); i++) + { + GuiControl *ctrl = static_cast(*i); + tabCtrl = ctrl->findFirstTabable(); + if (tabCtrl) + { + mFirstResponder = tabCtrl; + return tabCtrl; + } + } + + //nothing was found, therefore, see if this ctrl is tabable + return ( mProfile != NULL ) ? ( ( mProfile->mTabable && mAwake && mVisible ) ? this : NULL ) : NULL; +} + +//----------------------------------------------------------------------------- + +GuiControl* GuiControl::findLastTabable(bool firstCall) +{ + // No tabbing if the control is disabled or hidden. + if ( !mAwake || !mVisible ) + return NULL; + + //if this is the first call, clear the global + if (firstCall) + smPrevResponder = NULL; + + //if this control is tabable, set the global + if (mProfile->mTabable) + smPrevResponder = this; + + iterator i; + for (i = begin(); i != end(); i++) + { + GuiControl *ctrl = static_cast(*i); + ctrl->findLastTabable(false); + } + + //after the entire tree has been traversed, return the last responder found + mFirstResponder = smPrevResponder; + return smPrevResponder; +} + +//----------------------------------------------------------------------------- + +GuiControl *GuiControl::findNextTabable(GuiControl *curResponder, bool firstCall) +{ + // No tabbing if the control is disabled or hidden. + if ( !mAwake || !mVisible ) + return NULL; + + //if this is the first call, clear the global + if (firstCall) + smCurResponder = NULL; + + //first find the current responder + if (curResponder == this) + smCurResponder = this; + + //if the first responder has been found, return the very next *tabable* control + else if ( smCurResponder && mProfile->mTabable && mAwake && mVisible && mActive ) + return( this ); + + //loop through, checking each child to see if it is the one that follows the firstResponder + GuiControl *tabCtrl = NULL; + iterator i; + for (i = begin(); i != end(); i++) + { + GuiControl *ctrl = static_cast(*i); + tabCtrl = ctrl->findNextTabable(curResponder, false); + if (tabCtrl) break; + } + mFirstResponder = tabCtrl; + return tabCtrl; +} + +//----------------------------------------------------------------------------- + +GuiControl *GuiControl::findPrevTabable(GuiControl *curResponder, bool firstCall) +{ + // No tabbing if the control is disabled or hidden. + if ( !mAwake || !mVisible ) + return NULL; + + if (firstCall) + smPrevResponder = NULL; + + //if this is the current reponder, return the previous one + if (curResponder == this) + return smPrevResponder; + + //else if this is a responder, store it in case the next found is the current responder + else if ( mProfile->mTabable && mAwake && mVisible && mActive ) + smPrevResponder = this; + + //loop through, checking each child to see if it is the one that follows the firstResponder + GuiControl *tabCtrl = NULL; + iterator i; + for (i = begin(); i != end(); i++) + { + GuiControl *ctrl = static_cast(*i); + tabCtrl = ctrl->findPrevTabable(curResponder, false); + if (tabCtrl) break; + } + mFirstResponder = tabCtrl; + return tabCtrl; +} + +//----------------------------------------------------------------------------- + +bool GuiControl::controlIsChild( GuiControl* child ) +{ + for( iterator i = begin(); i != end(); ++ i ) + { + GuiControl* ctrl = static_cast< GuiControl* >( *i ); + if( ctrl == child || ctrl->controlIsChild( child ) ) + return true; + } + + return false; +} + +//============================================================================= +// Event Handling. +//============================================================================= +// MARK: ---- Event Handling ---- + +//----------------------------------------------------------------------------- + +bool GuiControl::isFirstResponder() +{ + GuiCanvas *root = getRoot(); + return root && root->getFirstResponder() == this; +} + +//----------------------------------------------------------------------------- + +void GuiControl::makeFirstResponder(bool value) +{ + if ( value ) + //setFirstResponder(this); + setFirstResponder(); + else + clearFirstResponder(); +} + +//----------------------------------------------------------------------------- + +void GuiControl::setFirstResponder( GuiControl* firstResponder ) +{ + // If the control cannot have keyboard focus, refuse + // to make it first responder. + + if( firstResponder && !firstResponder->mProfile->mCanKeyFocus ) + return; + + mFirstResponder = firstResponder; + + if( getParent() ) + getParent()->setFirstResponder( firstResponder ); +} + +//----------------------------------------------------------------------------- + +void GuiControl::setFirstResponder() +{ + if( mAwake && mVisible ) + { + GuiControl *parent = getParent(); + if ( mProfile->mCanKeyFocus == true && parent != NULL ) + parent->setFirstResponder( this ); + } +} + +//----------------------------------------------------------------------------- + +void GuiControl::clearFirstResponder() +{ + if( !getParent() ) + return; + + if( isFirstResponder() ) + getParent()->setFirstResponder( NULL ); + else + for( GuiControl* parent = this; parent != NULL; parent = parent->getParent() ) + if( parent->mFirstResponder == this ) + parent->mFirstResponder = NULL; +} + +//----------------------------------------------------------------------------- + +void GuiControl::onLoseFirstResponder() +{ + // Since many controls have visual cues when they are the firstResponder... + setUpdate(); + + onLoseFirstResponder_callback(); +} + +//----------------------------------------------------------------------------- + +void GuiControl::onGainFirstResponder() +{ + // Since many controls have visual cues when they are the firstResponder... + this->setUpdate(); + + onGainFirstResponder_callback(); +} + +//----------------------------------------------------------------------------- + +void GuiControl::buildAcceleratorMap() +{ + //add my own accel key + addAcceleratorKey(); + + //add all my childrens keys + iterator i; + for(i = begin(); i != end(); i++) + { + GuiControl *ctrl = static_cast(*i); + ctrl->buildAcceleratorMap(); + } +} + +//----------------------------------------------------------------------------- + +void GuiControl::addAcceleratorKey() +{ + //see if we have an accelerator + if( mAcceleratorKey == StringTable->EmptyString() ) + return; + + EventDescriptor accelEvent; + ActionMap::createEventDescriptor(mAcceleratorKey, &accelEvent); + + //now we have a modifier, and a key, add them to the canvas + GuiCanvas *root = getRoot(); + if (root) + root->addAcceleratorKey(this, 0, accelEvent.eventCode, accelEvent.flags); +} + +//----------------------------------------------------------------------------- + +void GuiControl::acceleratorKeyPress( U32 ) +{ + onAction(); +} + +//----------------------------------------------------------------------------- + +void GuiControl::acceleratorKeyRelease( U32 ) +{ + //do nothing +} + +//----------------------------------------------------------------------------- + +bool GuiControl::isMouseLocked() +{ + GuiCanvas *root = getRoot(); + return root ? root->getMouseLockedControl() == this : false; +} + +//----------------------------------------------------------------------------- + +void GuiControl::mouseLock(GuiControl *lockingControl) +{ + GuiCanvas *root = getRoot(); + if (root) + root->mouseLock(lockingControl); +} + +//----------------------------------------------------------------------------- + +void GuiControl::mouseLock() +{ + GuiCanvas *root = getRoot(); + if (root) + root->mouseLock(this); +} + +//----------------------------------------------------------------------------- + +void GuiControl::mouseUnlock() +{ + GuiCanvas *root = getRoot(); + if (root) + root->mouseUnlock(this); +} + +//============================================================================= +// Misc. +//============================================================================= +// MARK: ---- Misc ---- + +//----------------------------------------------------------------------------- + +LangTable * GuiControl::getGUILangTable() +{ + if(mLangTable) + return mLangTable; + + if(mLangTableName && *mLangTableName) + { + mLangTable = (LangTable *)getModLangTable((const UTF8*)mLangTableName); + return mLangTable; + } + + GuiControl *parent = getParent(); + if(parent) + return parent->getGUILangTable(); + + return NULL; +} + +//----------------------------------------------------------------------------- + +const UTF8 * GuiControl::getGUIString(S32 id) +{ + LangTable *lt = getGUILangTable(); + if(lt) + return lt->getString(id); + + return NULL; +} + +//----------------------------------------------------------------------------- + +void GuiControl::messageSiblings(S32 message) +{ + GuiControl *parent = getParent(); + if (! parent) return; + GuiControl::iterator i; + for(i = parent->begin(); i != parent->end(); i++) + { + GuiControl *ctrl = dynamic_cast(*i); + if (ctrl != this) + ctrl->onMessage(this, message); + } +} + +//----------------------------------------------------------------------------- + +void GuiControl::getScrollLineSizes(U32 *rowHeight, U32 *columnWidth) +{ + // default to 10 pixels in y, 30 pixels in x + *columnWidth = 30; + *rowHeight = 30; +} + +//----------------------------------------------------------------------------- + +U32 GuiControl::clipText( String &text, U32 clipWidth ) const +{ + PROFILE_SCOPE( GuiControl_clipText ); + + U32 textWidth = mProfile->mFont->getStrWidthPrecise( text ); + + if ( textWidth <= clipWidth ) + return textWidth; + + // Start removing characters from the end of the string + // until the string width plus the elipsesWidth is less + // than clipWidth... + + // Note this would be more efficient without calling + // getStrWidthPrecise each loop iteration. eg. get the + // length of each char, store in a temporary U32 array, + // and then remove the number we need from the end all at once. + + String temp; + + while ( text.isNotEmpty() ) + { + text.erase( text.length() - 1, 1 ); + temp = text; + temp += "..."; + textWidth = mProfile->mFont->getStrWidthPrecise( temp ); + + if ( textWidth <= clipWidth ) + { + text = temp; + return textWidth; + } + } + + // Uh, not even the ellipses will fit in the passed width. + // Text should be an ellipses string now, + // which is the right thing to do in this case. + + return 0; +} + +//----------------------------------------------------------------------------- + +void GuiControl::getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent) +{ +#ifdef _XBOX + return; +#endif + + TORQUE_UNUSED(lastGuiEvent); + + if( !getRoot() ) + return; + + if(getRoot()->mCursorChanged != -1 && !isMouseLocked()) + { + // We've already changed the cursor, + // so set it back before we change it again. + + PlatformWindow *pWindow = static_cast(getRoot())->getPlatformWindow(); + AssertFatal(pWindow != NULL,"GuiControl without owning platform window! This should not be possible."); + PlatformCursorController *pController = pWindow->getCursorController(); + AssertFatal(pController != NULL,"PlatformWindow without an owned CursorController!"); + + pController->popCursor(); + + // We haven't changed it + getRoot()->mCursorChanged = -1; + } +} + +//----------------------------------------------------------------------------- + +const char* GuiControl::evaluate( const char* str ) +{ + smThisControl = this; + const char* result = Con::evaluate(str, false); + smThisControl = NULL; + + return result; +} + +//----------------------------------------------------------------------------- + +const char* GuiControl::execConsoleCallback() +{ + if( mConsoleCommand.isNotEmpty() ) + return evaluate( mConsoleCommand ); + + return ""; +} + +//----------------------------------------------------------------------------- + +const char* GuiControl::execAltConsoleCallback() +{ + if( mAltConsoleCommand.isNotEmpty() ) + return evaluate( mAltConsoleCommand ); + + return ""; +} + +//============================================================================= +// Console Methods. +//============================================================================= +// MARK: ---- Console Methods ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, findHitControl, GuiControl*, ( S32 x, S32 y ),, + "Find the topmost child control located at the given coordinates.\n" + "@note Only children that are both visible and have the 'modal' flag set in their profile will be considered in the search." + "@param x The X coordinate in the control's own coordinate space.\n" + "@param y The Y coordinate in the control's own coordinate space.\n" + "@return The topmost child control at the given coordintes or the control on which the method was called if no matching child could be found.\n" + "@see GuiControlProfile::modal\n" + "@see findHitControls" ) +{ + return object->findHitControl( Point2I( x, y ) ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, findHitControls, const char*, ( S32 x, S32 y, S32 width, S32 height ),, + "Find all visible child controls that intersect with the given rectangle.\n" + "@note Invisible child controls will not be included in the search.\n" + "@param x The X coordinate of the rectangle's upper left corner in the control's own coordinate space.\n" + "@param y The Y coordinate of the rectangle's upper left corner in the control's own coordinate space.\n" + "@param width The width of the search rectangle in pixels.\n" + "@param height The height of the search rectangle in pixels.\n" + "@return A space-separated list of the IDs of all visible control objects intersecting the given rectangle.\n\n" + "@tsexample\n" + "// Lock all controls in the rectangle at x=10 and y=10 and the extent width=100 and height=100.\n" + "foreach$( %ctrl in %this.findHitControls( 10, 10, 100, 100 ) )\n" + " %ctrl.setLocked( true );\n" + "@endtsexample\n" + "@see findHitControl" ) +{ + // Find hit controls. + + RectI bounds( x, y, width, height ); + Vector< GuiControl* > controls; + + if( !object->findHitControls( bounds, controls ) ) + return ""; + + // Create vector string. + + bool isFirst = true; + StringBuilder str; + for( U32 i = 0, num = controls.size(); i < num; ++ i ) + { + if( !isFirst ) + str.append( ' ' ); + + str.append( controls[ i ]->getIdString() ); + isFirst = false; + } + String s = str.end(); + + // Return result. + + if ( s.compare( object->getIdString() ) == 0 ) + return ""; + + char* buffer = Con::getReturnBuffer( s.size() ); + dStrcpy( buffer, s.c_str() ); + + return buffer; +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, controlIsChild, bool, ( GuiControl* control ),, + "Test whether the given control is a direct or indirect child to this control.\n" + "@param control The potential child control.\n" + "@return True if the given control is a direct or indirect child to this control." ) +{ + if( !control ) + return false; + + return object->controlIsChild( control ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, isFirstResponder, bool, (),, + "Test whether the control is the current first responder.\n" + "@return True if the control is the current first responder.\n" + "@see makeFirstResponder\n" + "@see setFirstResponder\n" + "@ref GuiControl_FirstResponders" ) +{ + return object->isFirstResponder(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, setFirstResponder, void, (),, + "Make this control the current first responder.\n" + "@note Only controls with a profile that has canKeyFocus enabled are able to become first responders.\n" + "@see GuiControlProfile::canKeyFocus\n" + "@see isFirstResponder\n" + "@ref GuiControl_FirstResponders" ) +{ + object->setFirstResponder(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, getFirstResponder, GuiControl*, (),, + "Get the first responder set on this GuiControl tree.\n" + "@return The first responder set on the control's subtree.\n" + "@see isFirstResponder\n" + "@see makeFirstResponder\n" + "@see setFirstResponder\n" + "@ref GuiControl_FirstResponders" ) +{ + return object->getFirstResponder(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, clearFirstResponder, void, ( bool ignored ), ( false ), + "Clear this control from being the first responder in its hierarchy chain.\n" + "@param ignored Ignored. Supported for backwards-compatibility.\n" ) +{ + object->clearFirstResponder(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, pointInControl, bool, ( S32 x, S32 y ),, + "Test whether the given point lies within the rectangle of the control.\n" + "@param x X coordinate of the point in parent-relative coordinates.\n" + "@param y Y coordinate of the point in parent-relative coordinates.\n" + "@return True if the point is within the control, false if not.\n" + "@see getExtent\n" + "@see getPosition\n" ) +{ + return object->pointInControl( Point2I( x, y ) ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, addGuiControl, void, ( GuiControl* control ),, + "Add the given control as a child to this control.\n" + "This is synonymous to calling SimGroup::addObject.\n" + "@param control The control to add as a child.\n" + "@note The control will retain its current position and size.\n" + "@see SimGroup::addObject\n" + "@ref GuiControl_Hierarchy\n" ) +{ + if( control ) + object->addObject( control ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, getRoot, GuiCanvas*, (),, + "Get the canvas on which the control is placed.\n" + "@return The canvas on which the control's hierarchy is currently placed or 0 if the control is not currently placed on a GuiCanvas.\n" + "@see GuiControl_Hierarchy\n" ) +{ + return object->getRoot(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, getParent, GuiControl*, (),, + "Get the immediate parent control of the control.\n" + "@return The immediate parent GuiControl or 0 if the control is not parented to a GuiControl.\n" ) +{ + return object->getParent(); +} + +//----------------------------------------------------------------------------- +DefineEngineMethod( GuiControl, isMouseLocked, bool, (),, + "Indicates if the mouse is locked in this control.\n" + "@return True if the mouse is currently locked.\n" ) +{ + return object->isMouseLocked(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, setValue, void, ( const char* value ),, + "Set the value associated with the control.\n" + "@param value The new value for the control.\n" ) +{ + object->setScriptValue( value ); +} + +ConsoleMethod( GuiControl, getValue, const char*, 2, 2, "") +{ + return object->getScriptValue(); +} + +ConsoleMethod( GuiControl, makeFirstResponder, void, 3, 3, "(bool isFirst)") +{ + object->makeFirstResponder(dAtob(argv[2])); +} + +ConsoleMethod( GuiControl, isActive, bool, 2, 2, "") +{ + return object->isActive(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, setActive, void, ( bool state ), ( true ), + "" ) +{ + object->setActive( state ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, isVisible, bool, (),, + "Test whether the control is currently set to be visible.\n" + "@return True if the control is currently set to be visible." + "@note This method does not tell anything about whether the control is actually visible to " + "the user at the moment.\n\n" + "@ref GuiControl_VisibleActive" ) +{ + return object->isVisible(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, setVisible, void, ( bool state ), ( true ), + "Set whether the control is visible or not.\n" + "@param state The new visiblity flag state for the control.\n" + "@ref GuiControl_VisibleActive" ) +{ + object->setVisible( state ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, isAwake, bool, (),, + "Test whether the control is currently awake.\n" + "If a control is awake it means that it is part of the GuiControl hierarchy of a GuiCanvas.\n" + "@return True if the control is awake." + "@ref GuiControl_Waking" ) +{ + return object->isAwake(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, setProfile, void, ( GuiControlProfile* profile ),, + "Set the control profile for the control to use.\n" + "The profile used by a control determines a great part of its behavior and appearance.\n" + "@param profile The new profile the control should use.\n" + "@ref GuiControl_Profiles" ) +{ + if( !profile ) + return; + + object->setControlProfile( profile ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, resize, void, ( S32 x, S32 y, S32 width, S32 height ),, + "Resize and reposition the control using the give coordinates and dimensions. Child controls " + "will resize according to their layout behaviors.\n" + "@param x The new X coordinate of the control in its parent's coordinate space.\n" + "@param y The new Y coordinate of the control in its parent's coordinate space.\n" + "@param width The new width to which the control should be resized.\n" + "@param height The new height to which the control should be resized." ) +{ + Point2I newPos( x, y ); + Point2I newExt( width, height ); + object->resize(newPos, newExt); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, getPosition, Point2I, (),, + "Get the control's current position relative to its parent.\n" + "@return The coordinate of the control in its parent's coordinate space." ) +{ + return object->getPosition(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, getCenter, Point2I, (),, + "Get the coordinate of the control's center point relative to its parent.\n" + "@return The coordinate of the control's center point in parent-relative coordinates." ) +{ + const Point2I pos = object->getPosition(); + const Point2I ext = object->getExtent(); + Point2I center( pos.x + ext.x / 2, pos.y + ext.y / 2 ); + + return center; +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, setCenter, void, ( S32 x, S32 y ),, + "Set the control's position by its center point.\n" + "@param x The X coordinate of the new center point of the control relative to the control's parent.\n" + "@param y The Y coordinate of the new center point of the control relative to the control's parent." ) +{ + const Point2I ext = object->getExtent(); + Point2I newpos( x - ext.x / 2, y - ext.y / 2 ); + object->setPosition( newpos ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, getGlobalCenter, Point2I, (),, + "Get the coordinate of the control's center point in coordinates relative to the root control in its control hierarchy.\n" + "@Return the center coordinate of the control in root-relative coordinates.\n" ) +{ + const Point2I tl( 0, 0 ); + Point2I pos = object->localToGlobalCoord( tl ); + const Point2I ext = object->getExtent(); + Point2I center( pos.x + ext.x / 2, pos.y + ext.y / 2 ); + + return center; +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, getGlobalPosition, Point2I, (),, + "Get the position of the control relative to the root of the GuiControl hierarchy it is contained in.\n" + "@return The control's current position in root-relative coordinates." ) +{ + const Point2I pos(0,0); + return object->localToGlobalCoord(pos); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, setPositionGlobal, void, ( S32 x, S32 y ),, + "Set position of the control relative to the root of the GuiControl hierarchy it is contained in.\n" + "@param x The new X coordinate of the control relative to the root's upper left corner.\n" + "@param y The new Y coordinate of the control relative to the root's upper left corner." ) +{ + //see if we can turn the x/y into ints directly, + Point2I lPosOffset = object->globalToLocalCoord( Point2I( x, y ) ); + + lPosOffset += object->getPosition(); + + object->setPosition( lPosOffset ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, setPosition, void, ( S32 x, S32 y ),, + "Position the control in the local space of the parent control.\n" + "@param x The new X coordinate of the control relative to its parent's upper left corner.\n" + "@param y The new Y coordinate of the control relative to its parent's upper left corner." ) +{ + object->setPosition( Point2I( x, y ) ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, getExtent, Point2I, (),, + "Get the width and height of the control.\n" + "@return A point structure containing the width of the control in x and the height in y." ) +{ + return object->getExtent(); +} + +//----------------------------------------------------------------------------- + +static ConsoleDocFragment _sGuiControlSetExtent1( + "@brief Resize the control to the given dimensions.\n\n" + "Child controls will resize according to their layout settings.\n" + "@param width The new width of the control in pixels.\n" + "@param height The new height of the control in pixels.", + "GuiControl", // The class to place the method in; use NULL for functions. + "void setExtent( S32 width, S32 height );" ); // The definition string. +static ConsoleDocFragment _sGuiControlSetExtent2( + "@brief Resize the control to the given dimensions.\n\n" + "Child controls with resize according to their layout settings.\n" + "@param p The new ( width, height ) extents of the control.", + "GuiControl", // The class to place the method in; use NULL for functions. + "void setExtent( Point2I p );" ); // The definition string. + +ConsoleMethod( GuiControl, setExtent, void, 3, 4, + "( Point2I p | int x, int y ) Set the width and height of the control.\n\n" + "@hide" ) +{ + if ( argc == 3 ) + { + // We scan for floats because its possible that math + // done on the extent can result in fractional values. + Point2F ext; + if ( dSscanf( argv[2], "%g %g", &ext.x, &ext.y ) == 2 ) + object->setExtent( (S32)ext.x, (S32)ext.y ); + else + Con::errorf( "GuiControl::setExtent, not enough parameters!" ); + } + else if ( argc == 4 ) + object->setExtent( dAtoi(argv[2]), dAtoi(argv[3]) ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, getMinExtent, Point2I, (),, + "Get the minimum allowed size of the control.\n" + "@return The minimum size to which the control can be shrunk.\n" + "@see minExtent" ) +{ + return object->getMinExtent(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiControl, getAspect, F32, (),, + "Get the aspect ratio of the control's extents.\n" + "@return The width of the control divided by its height.\n" + "@see getExtent" ) +{ + const Point2I &ext = object->getExtent(); + return (F32)ext.x / (F32)ext.y; +} diff --git a/Engine/source/gui/core/guiControl.h b/Engine/source/gui/core/guiControl.h new file mode 100644 index 000000000..0e798935f --- /dev/null +++ b/Engine/source/gui/core/guiControl.h @@ -0,0 +1,830 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUICONTROL_H_ +#define _GUICONTROL_H_ + +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _MRECT_H_ +#include "math/mRect.h" +#endif +#ifndef _COLOR_H_ +#include "core/color.h" +#endif +#ifndef _SIMBASE_H_ +#include "console/simBase.h" +#endif +#ifndef _GUITYPES_H_ +#include "gui/core/guiTypes.h" +#endif +#ifndef _EVENT_H_ +#include "platform/event.h" +#endif +#ifndef _UTIL_DELEGATE_H_ +#include "core/util/delegate.h" +#endif +#ifndef _LANG_H_ +#include "i18n/lang.h" +#endif + +class GuiCanvas; +class GuiEditCtrl; +class GuiWindowCtrl; + + +DECLARE_SCOPE( GuiAPI ); + + +/// A delegate used in tool tip rendering. +/// +/// @param hoverPos position to display the tip near +/// @param cursorPos the actual position of the cursor when the delegate is called +/// @param tipText optional alternate tip to be rendered +/// @return Returns true if the tooltip was rendered. +/// +/// @see GuiControl::mRenderTooltipDelegate +typedef Delegate RenderTooltipDelegate; + +/// @defgroup gui_group Gui System +/// The GUI system in Torque provides a powerful way of creating +/// WYSIWYG User Interfaces for your Game or Application written +/// in Torque. +/// +/// The GUI Provides a range of different controls that you may use +/// to arrange and layout your GUI's, including Buttons, Lists, Bitmaps +/// Windows, Containers, and HUD elements. +/// +/// The Base Control Class GuiControl provides a basis upon which to +/// write GuiControl's that may be specific to your particular type +/// of game. + + +/// @addtogroup gui_core_group Core +/// @section GuiControl_Intro Introduction +/// +/// GuiControl is the base class for GUI controls in Torque. It provides these +/// basic areas of functionality: +/// - Inherits from SimGroup, so that controls can have children. +/// - Interfacing with a GuiControlProfile. +/// - An abstraction from the details of handling user input +/// and so forth, providing friendly hooks like onMouseEnter(), onMouseMove(), +/// and onMouseLeave(), onKeyDown(), and so forth. +/// - An abstraction from the details of rendering and resizing. +/// - Helper functions to manipulate the mouse (mouseLock and +/// mouseUnlock), and convert coordinates (localToGlobalCoord() and +/// globalToLocalCoord()). +/// +/// @ref GUI has an overview of the GUI system. +/// +/// +/// @ingroup gui_group Gui System +/// @{ +class GuiControl : public SimGroup +{ + public: + + typedef SimGroup Parent; + + friend class GuiWindowCtrl; // mCollapseGroupVec + friend class GuiCanvas; + friend class GuiEditCtrl; + friend class GuiDragAndDropControl; // drag callbacks + + /// Additional write flags for GuiControls. + enum + { + NoCheckParentCanSave = BIT( 31 ), ///< Don't inherit mCanSave=false from parents. + }; + + enum horizSizingOptions + { + horizResizeRight = 0, ///< fixed on the left and width + horizResizeWidth, ///< fixed on the left and right + horizResizeLeft, ///< fixed on the right and width + horizResizeCenter, + horizResizeRelative, ///< resize relative + horizResizeWindowRelative ///< resize window relative + }; + enum vertSizingOptions + { + vertResizeBottom = 0, ///< fixed on the top and in height + vertResizeHeight, ///< fixed on the top and bottom + vertResizeTop, ///< fixed in height and on the bottom + vertResizeCenter, + vertResizeRelative, ///< resize relative + vertResizeWindowRelative ///< resize window relative + }; + + private: + + SimGroup *mAddGroup; ///< The internal name of a SimGroup child of the global GuiGroup in which to organize this gui on creation + RectI mBounds; ///< The internal bounds of this control + + protected: + + GuiControlProfile* mProfile; ///< The profile for this gui (data settings that are likely to be shared by multiple guis) + GuiControlProfile* mTooltipProfile; ///< The profile for any tooltips + + /// @name Control State + /// @{ + + static bool setProfileProt( void *object, const char *index, const char *data ); + static bool setTooltipProfileProt( void *object, const char *index, const char *data ); + + S32 mTipHoverTime; + + /// Delegate called to render a tooltip for this control. + /// By default this will be set to defaultTooltipRender. + RenderTooltipDelegate mRenderTooltipDelegate; + + /// The default tooltip rendering function. + /// @see RenderTooltipDelegate + bool defaultTooltipRender( const Point2I &hoverPos, const Point2I &cursorPos, const char* tipText = NULL ); + + bool mVisible; + bool mActive; + bool mAwake; + bool mSetFirstResponder; + bool mIsContainer; ///< if true, then the GuiEditor can drag other controls into this one. + bool mCanResize; + bool mCanHit; + + S32 mLayer; + Point2I mMinExtent; + StringTableEntry mLangTableName; + LangTable *mLangTable; + + bool mNotifyChildrenResized; + + // Contains array of windows located inside GuiControl + typedef Vector< Vector< GuiWindowCtrl *> > CollapseGroupVec; + CollapseGroupVec mCollapseGroupVec; + + static bool smDesignTime; ///< static GuiControl boolean that specifies if the GUI Editor is active + /// @} + + /// @name Design Time Editor Access + /// @{ + static GuiEditCtrl *smEditorHandle; ///< static GuiEditCtrl pointer that gives controls access to editor-NULL if editor is closed + /// @} + + /// @name Keyboard Input + /// @{ + GuiControl *mFirstResponder; + static GuiControl *smPrevResponder; + static GuiControl *smCurResponder; + /// @} + + /// @name Control State + /// @{ + + S32 mHorizSizing; ///< Set from horizSizingOptions. + S32 mVertSizing; ///< Set from vertSizingOptions. + + StringTableEntry mAcceleratorKey; + StringTableEntry mConsoleVariable; + + String mConsoleCommand; + String mAltConsoleCommand; + + String mTooltip; + + /// @} + + /// @name Console + /// The console variable collection of functions allows a console variable to be bound to the GUI control. + /// + /// This allows, say, an edit field to be bound to '$foo'. The value of the console + /// variable '$foo' would then be equal to the text inside the text field. Changing + /// either changes the other. + /// @{ + + /// $ThisControl variable for callback execution. + static GuiControl* smThisControl; + + /// Set $ThisControl and evaluate the given script code. + const char* evaluate( const char* str ); + + /// Sets the value of the console variable bound to this control + /// @param value String value to assign to control's console variable + void setVariable(const char *value); + + /// Sets the value of the console variable bound to this control + /// @param value Integer value to assign to control's console variable + void setIntVariable(S32 value); + + /// Sets the value of the console variable bound to this control + /// @param value Float value to assign to control's console variable + void setFloatVariable(F32 value); + + const char* getVariable(); ///< Returns value of control's bound variable as a string + S32 getIntVariable(); ///< Returns value of control's bound variable as a integer + F32 getFloatVariable(); ///< Returns value of control's bound variable as a float + + GFXStateBlockRef mDefaultGuiSB; + + /// @name Callbacks + /// @{ + + DECLARE_CALLBACK( void, onAdd, () ); + DECLARE_CALLBACK( void, onRemove, () ); + + DECLARE_CALLBACK( void, onWake, () ); + DECLARE_CALLBACK( void, onSleep, () ); + + DECLARE_CALLBACK( void, onLoseFirstResponder, () ); + DECLARE_CALLBACK( void, onGainFirstResponder, () ); + + DECLARE_CALLBACK( void, onAction, () ); + DECLARE_CALLBACK( void, onVisible, ( bool state ) ); + DECLARE_CALLBACK( void, onActive, ( bool state ) ); + + DECLARE_CALLBACK( void, onDialogPush, () ); + DECLARE_CALLBACK( void, onDialogPop, () ); + + DECLARE_CALLBACK( void, onControlDragEnter, ( GuiControl* control, const Point2I& dropPoint ) ); + DECLARE_CALLBACK( void, onControlDragExit, ( GuiControl* control, const Point2I& dropPoint ) ); + DECLARE_CALLBACK( void, onControlDragged, ( GuiControl* control, const Point2I& dropPoint ) ); + DECLARE_CALLBACK( void, onControlDropped, ( GuiControl* control, const Point2I& dropPoint ) ); + + /// @} + + public: + + /// Set the name of the console variable which this GuiObject is bound to + /// @param variable Variable name + void setConsoleVariable(const char *variable); + + /// Set the name of the console function bound to, such as a script function + /// a button calls when clicked. + /// @param newCmd Console function to attach to this GuiControl + void setConsoleCommand( const String& newCmd ); + const char * getConsoleCommand(); ///< Returns the name of the function bound to this GuiControl + LangTable *getGUILangTable(void); + const UTF8 *getGUIString(S32 id); + + /// @} + + /// @name Callbacks + /// @{ + /// Executes a console command, and returns the result. + /// + /// The global console variable $ThisControl is set to the id of the calling + /// control. WARNING: because multiple controls may set $ThisControl, at any time, + /// the value of $ThisControl should be stored in a local variable by the + /// callback code. The use of the $ThisControl variable is not thread safe. + + /// Executes mConsoleCommand, and returns the result. + const char* execConsoleCallback(); + /// Executes mAltConsoleCommand, and returns the result. + const char* execAltConsoleCallback(); + /// @} + + static bool _setVisible( void *object, const char *index, const char *data ) { static_cast(object)->setVisible( dAtob( data ) ); return false; }; + static bool _setActive( void *object, const char *index, const char *data ) { static_cast(object)->setActive( dAtob( data ) ); return false; }; + + /// @name Editor + /// These functions are used by the GUI Editor + /// @{ + + /// Sets the size of the GuiControl + /// @param horz Width of the control + /// @param vert Height of the control + void setSizing(S32 horz, S32 vert); + + /// Overrides Parent Serialization to allow specific controls to not be saved (Dynamic Controls, etc) + void write(Stream &stream, U32 tabStop, U32 flags); + + /// Returns boolean as to whether any parent of this control has the 'no serialization' flag set. + bool getCanSaveParent(); + + /// @} + + /// @name Initialization + /// @{ + + DECLARE_CONOBJECT(GuiControl); + DECLARE_CATEGORY( "Gui Core" ); + DECLARE_DESCRIPTION( "Base class for GUI controls. Can also be used as a generic container." ); + + GuiControl(); + virtual ~GuiControl(); + virtual bool processArguments(S32 argc, const char **argv); + + static void initPersistFields(); + static void consoleInit(); + + /// @} + + /// @name Accessors + /// @{ + + inline const Point2I& getPosition() const { return mBounds.point; } ///< Returns position of the control + inline const Point2I& getExtent() const { return mBounds.extent; } ///< Returns extents of the control + inline const RectI getBounds()const { return mBounds; } ///< Returns the bounds of the control + inline const RectI getGlobalBounds() ///< Returns the bounds of this object, in global coordinates + { + RectI retRect = getBounds(); + retRect.point = localToGlobalCoord( Point2I(0,0) ); + + return retRect; + }; + virtual Point2I getMinExtent() const { return mMinExtent; } ///< Returns minimum size the control can be + virtual void setMinExtent( const Point2I &newMinExtent ) { mMinExtent = newMinExtent; }; + inline const S32 getLeft() const { return mBounds.point.x; } ///< Returns the X position of the control + inline const S32 getTop() const { return mBounds.point.y; } ///< Returns the Y position of the control + inline const S32 getWidth() const { return mBounds.extent.x; } ///< Returns the width of the control + inline const S32 getHeight() const { return mBounds.extent.y; } ///< Returns the height of the control + + inline const S32 getHorizSizing() const { return mHorizSizing; } + inline const S32 getVertSizing() const { return mVertSizing; } + + /// @} + + /// @name Flags + /// @{ + + /// Sets the visibility of the control + /// @param value True if object should be visible + virtual void setVisible(bool value); + inline bool isVisible() const { return mVisible; } ///< Returns true if the object is visible + virtual bool isHidden() const { return !isVisible(); } + virtual void setHidden( bool state ) { setVisible( !state ); } + + void setCanHit( bool value ) { mCanHit = value; } + + /// Sets the status of this control as active and responding or inactive + /// @param value True if this is active + virtual void setActive(bool value); + bool isActive() { return mActive; } ///< Returns true if this control is active + + bool isAwake() { return mAwake; } ///< Returns true if this control is awake + + /// @} + + /// Get information about the size of a scroll line. + /// + /// @param rowHeight The height, in pixels, of a row + /// @param columnWidth The width, in pixels, of a column + virtual void getScrollLineSizes(U32 *rowHeight, U32 *columnWidth); + + /// Get information about the cursor. + /// @param cursor Cursor information will be stored here + /// @param showCursor Will be set to true if the cursor is visible + /// @param lastGuiEvent GuiEvent containing cursor position and modifier keys (ie ctrl, shift, alt etc) + virtual void getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent); + + /// @name Children + /// @{ + + /// Adds an object as a child of this object. + /// @param obj New child object of this control + void addObject(SimObject *obj); + + /// Removes a child object from this control. + /// @param obj Object to remove from this control + void removeObject(SimObject *obj); + + GuiControl *getParent(); ///< Returns the control which owns this one. + GuiCanvas *getRoot(); ///< Returns the root canvas of this control. + + virtual bool acceptsAsChild( SimObject* object ) const; + + virtual void onGroupRemove(); + + /// @} + + /// @name Coordinates + /// @{ + + /// Translates local coordinates (wrt this object) into global coordinates + /// + /// @param src Local coordinates to translate + Point2I localToGlobalCoord(const Point2I &src); + + /// Returns global coordinates translated into local space + /// + /// @param src Global coordinates to translate + Point2I globalToLocalCoord(const Point2I &src); + /// @} + + /// @name Resizing + /// @{ + + /// Changes the size and/or position of this control + /// @param newPosition New position of this control + /// @param newExtent New size of this control + virtual bool resize(const Point2I &newPosition, const Point2I &newExtent); + + /// Changes the position of this control + /// @param newPosition New position of this control + virtual bool setPosition( const Point2I &newPosition ); + inline void setPosition( const S32 x, const S32 y ) { setPosition(Point2I(x,y)); } + + /// Changes the size of this control + /// @param newExtent New size of this control + virtual bool setExtent( const Point2I &newExtent ); + inline void setExtent( const S32 width, const S32 height) { setExtent(Point2I(width, height)); } + + /// Changes the bounds of this control + /// @param newBounds New bounds of this control + virtual bool setBounds( const RectI &newBounds ); + inline void setBounds( const S32 left, const S32 top, + const S32 width, const S32 height) { setBounds(RectI(left, top, width, height)); } + + /// Changes the X position of this control + /// @param newXPosition New X Position of this control + virtual void setLeft( S32 newLeft ); + + /// Changes the Y position of this control + /// @param newYPosition New Y Position of this control + virtual void setTop( S32 newTop ); + + /// Changes the width of this control + /// @param newWidth New width of this control + virtual void setWidth( S32 newWidth ); + + /// Changes the height of this control + /// @param newHeight New Height of this control + virtual void setHeight( S32 newHeight ); + + /// Called when a child control of the object is resized + /// @param child Child object + virtual void childResized(GuiControl *child); + + /// Called when this objects parent is resized + /// @param oldParentRect The old rectangle of the parent object + /// @param newParentRect The new rectangle of the parent object + virtual void parentResized(const RectI &oldParentRect, const RectI &newParentRect); + /// @} + + /// @name Rendering + /// @{ + + /// Called when this control is to render itself + /// @param offset The location this control is to begin rendering + /// @param updateRect The screen area this control has drawing access to + virtual void onRender(Point2I offset, const RectI &updateRect); + + /// Called when this control should render its children + /// @param offset The location this control is to begin rendering + /// @param updateRect The screen area this control has drawing access to + void renderChildControls(Point2I offset, const RectI &updateRect); + + /// Sets the area (local coordinates) this control wants refreshed each frame + /// @param pos UpperLeft point on rectangle of refresh area + /// @param ext Extent of update rect + void setUpdateRegion(Point2I pos, Point2I ext); + + /// Sets the update area of the control to encompass the whole control + virtual void setUpdate(); + /// @} + + //child hierarchy calls + void awaken(); ///< Called when this control and its children have been wired up. + void sleep(); ///< Called when this control is no more. + void preRender(); ///< Pre-render this control and all its children. + + /// @name Events + /// + /// If you subclass these, make sure to call the Parent::'s versions. + /// + /// @{ + + /// Called when this object is asked to wake up returns true if it's actually awake at the end + virtual bool onWake(); + + /// Called when this object is asked to sleep + virtual void onSleep(); + + /// Do special pre-render processing + virtual void onPreRender(); + + /// Called when this object is removed + virtual void onRemove(); + + /// Called when one of this objects children is removed + virtual void onChildRemoved( GuiControl *child ); + + /// Called when this object is added to the scene + virtual bool onAdd(); + + /// Called when the mProfile or mToolTipProfile is deleted + virtual void onDeleteNotify(SimObject *object); + + /// Called when this object has a new child + virtual void onChildAdded( GuiControl *child ); + + /// @} + + /// @name Console + /// @{ + + /// Returns the value of the variable bound to this object + virtual const char *getScriptValue(); + + /// Sets the value of the variable bound to this object + virtual void setScriptValue(const char *value); + /// @} + + /// @name Input (Keyboard/Mouse) + /// @{ + + /// This function will return true if the provided coordinates (wrt parent object) are + /// within the bounds of this control + /// @param parentCoordPoint Coordinates to test + virtual bool pointInControl(const Point2I& parentCoordPoint); + + /// Returns true if the global cursor is inside this control + bool cursorInControl(); + + /// Returns the control which the provided point is under, with layering + /// @param pt Point to test + /// @param initialLayer Layer of gui objects to begin the search + virtual GuiControl* findHitControl(const Point2I &pt, S32 initialLayer = -1 ); + + enum EHitTestFlags + { + HIT_FullBoxOnly = BIT( 0 ), ///< Hit only counts if all of a control's bounds are within the hit rectangle. + HIT_ParentPreventsChildHit = BIT( 1 ), ///< A positive hit test on a parent control will prevent hit tests on children. + HIT_AddParentHits = BIT( 2 ), ///< Parent's that get hit should be added regardless of whether any of their children get hit, too. + HIT_NoCanHitNoRecurse = BIT( 3 ), ///< A hit-disabled control will not recurse into children. + }; + + /// + virtual bool findHitControls( const RectI& rect, Vector< GuiControl* >& outResult, U32 flags = 0, S32 initialLayer = -1, U32 depth = 0 ); + + /// Lock the mouse within the provided control + /// @param lockingControl Control to lock the mouse within + void mouseLock(GuiControl *lockingControl); + + /// Turn on mouse locking with last used lock control + void mouseLock(); + + /// Unlock the mouse + void mouseUnlock(); + + /// Returns true if the mouse is locked + bool isMouseLocked(); + /// @} + + + /// General input handler. + virtual bool onInputEvent(const InputEventInfo &event); + + /// @name Mouse Events + /// These functions are called when the input event which is + /// in the name of the function occurs. + /// @{ + virtual void onMouseUp(const GuiEvent &event); + virtual void onMouseDown(const GuiEvent &event); + virtual void onMouseMove(const GuiEvent &event); + virtual void onMouseDragged(const GuiEvent &event); + virtual void onMouseEnter(const GuiEvent &event); + virtual void onMouseLeave(const GuiEvent &event); + + virtual bool onMouseWheelUp(const GuiEvent &event); + virtual bool onMouseWheelDown(const GuiEvent &event); + + virtual void onRightMouseDown(const GuiEvent &event); + virtual void onRightMouseUp(const GuiEvent &event); + virtual void onRightMouseDragged(const GuiEvent &event); + + virtual void onMiddleMouseDown(const GuiEvent &event); + virtual void onMiddleMouseUp(const GuiEvent &event); + virtual void onMiddleMouseDragged(const GuiEvent &event); + /// @} + + /// @name Gamepad Events + /// These functions are called when the input event which is in the name of + /// the function occurs. + /// @{ + virtual bool onGamepadButtonDown(const GuiEvent &event); ///< Default behavior is call-through to onKeyDown + virtual bool onGamepadButtonUp(const GuiEvent &event); ///< Default behavior is call-through to onKeyUp + virtual bool onGamepadAxisUp(const GuiEvent &event); + virtual bool onGamepadAxisDown(const GuiEvent &event); + virtual bool onGamepadAxisLeft(const GuiEvent &event); + virtual bool onGamepadAxisRight(const GuiEvent &event); + virtual bool onGamepadTrigger(const GuiEvent &event); + /// @} + + /// @name Editor Mouse Events + /// + /// These functions are called when the input event which is + /// in the name of the function occurs. Conversely from normal + /// mouse events, these have a boolean return value that, if + /// they return true, the editor will NOT act on them or be able + /// to respond to this particular event. + /// + /// This is particularly useful for when writing controls so that + /// they may become aware of the editor and allow customization + /// of their data or appearance as if they were actually in use. + /// For example, the GuiTabBookCtrl catches on mouse down to select + /// a tab and NOT let the editor do any instant group manipulation. + /// + /// @{ + + /// Called when a mouseDown event occurs on a control and the GUI editor is active + /// @param event the GuiEvent which caused the call to this function + /// @param offset the offset which is representative of the units x and y that the editor takes up on screen + virtual bool onMouseDownEditor(const GuiEvent &event, Point2I offset) { return false; }; + + /// Called when a mouseUp event occurs on a control and the GUI editor is active + /// @param event the GuiEvent which caused the call to this function + /// @param offset the offset which is representative of the units x and y that the editor takes up on screen + virtual bool onMouseUpEditor(const GuiEvent &event, Point2I offset) { return false; }; + + /// Called when a rightMouseDown event occurs on a control and the GUI editor is active + /// @param event the GuiEvent which caused the call to this function + /// @param offset the offset which is representative of the units x and y that the editor takes up on screen + virtual bool onRightMouseDownEditor(const GuiEvent &event, Point2I offset) { return false; }; + + /// Called when a mouseDragged event occurs on a control and the GUI editor is active + /// @param event the GuiEvent which caused the call to this function + /// @param offset the offset which is representative of the units x and y that the editor takes up on screen + virtual bool onMouseDraggedEditor(const GuiEvent &event, Point2I offset) { return false; }; + + /// @} + + /// @name Tabs + /// @{ + + /// Find the first tab-accessible child of this control + virtual GuiControl* findFirstTabable(); + + /// Find the last tab-accessible child of this control + /// @param firstCall Set to true to clear the global previous responder + virtual GuiControl* findLastTabable(bool firstCall = true); + + /// Find previous tab-accessible control with respect to the provided one + /// @param curResponder Current control + /// @param firstCall Set to true to clear the global previous responder + virtual GuiControl* findPrevTabable(GuiControl *curResponder, bool firstCall = true); + + /// Find next tab-accessible control with regards to the provided control. + /// + /// @param curResponder Current control + /// @param firstCall Set to true to clear the global current responder + virtual GuiControl* findNextTabable(GuiControl *curResponder, bool firstCall = true); + /// @} + + /// Returns true if the provided control is a child (grandchild, or great-grandchild) of this one. + /// + /// @param child Control to test + virtual bool controlIsChild(GuiControl *child); + + /// @name First Responder + /// A first responder is the control which reacts first, in it's responder chain, to keyboard events + /// The responder chain is set for each parent and so there is only one first responder amongst it's + /// children. + /// @{ + + /// Sets the first responder for child controls + /// @param firstResponder First responder for this chain + virtual void setFirstResponder(GuiControl *firstResponder); + + /// Sets up this control to be the first in it's group to respond to an input event + /// @param value True if this should be a first responder + virtual void makeFirstResponder(bool value); + + /// Returns true if this control is a first responder + bool isFirstResponder(); + + /// Sets this object to be a first responder + virtual void setFirstResponder(); + + /// Clears the first responder for this chain + void clearFirstResponder(); + + /// Returns the first responder for this chain + GuiControl *getFirstResponder() { return mFirstResponder; } + + /// Occurs when the control gains first-responder status. + virtual void onGainFirstResponder(); + + /// Occurs when the control loses first-responder status. + virtual void onLoseFirstResponder(); + /// @} + + /// @name Keyboard Events + /// @{ + + /// Adds the accelerator key for this object to the canvas + void addAcceleratorKey(); + + /// Adds this control's accelerator key to the accelerator map, and + /// recursively tells all children to do the same. + virtual void buildAcceleratorMap(); + + /// Occurs when the accelerator key for this control is pressed + /// + /// @param index Index in the accelerator map of the key + virtual void acceleratorKeyPress(U32 index); + + /// Occurs when the accelerator key for this control is released + /// + /// @param index Index in the accelerator map of the key + virtual void acceleratorKeyRelease(U32 index); + + /// Happens when a key is depressed + /// @param event Event descriptor (which contains the key) + virtual bool onKeyDown(const GuiEvent &event); + + /// Happens when a key is released + /// @param event Event descriptor (which contains the key) + virtual bool onKeyUp(const GuiEvent &event); + + /// Happens when a key is held down, resulting in repeated keystrokes. + /// @param event Event descriptor (which contains the key) + virtual bool onKeyRepeat(const GuiEvent &event); + /// @} + + /// Return the delegate used to render tooltips on this control. + RenderTooltipDelegate& getRenderTooltipDelegate() { return mRenderTooltipDelegate; } + const RenderTooltipDelegate& getRenderTooltipDelegate() const { return mRenderTooltipDelegate; } + + /// Returns our tooltip profile (and finds the profile if it hasn't been set yet) + GuiControlProfile* getTooltipProfile() { return mTooltipProfile; } + + /// Sets the tooltip profile for this control. + /// + /// @see GuiControlProfile + /// @param prof Tooltip profile to apply + void setTooltipProfile(GuiControlProfile *prof); + + /// Returns our profile (and finds the profile if it hasn't been set yet) + GuiControlProfile* getControlProfile() { return mProfile; } + + /// Sets the control profile for this control. + /// + /// @see GuiControlProfile + /// @param prof Control profile to apply + void setControlProfile(GuiControlProfile *prof); + + /// Occurs when this control performs its "action" + virtual void onAction(); + + /// @name Peer Messaging + /// Used to send a message to other GUIControls which are children of the same parent. + /// + /// This is mostly used by radio controls. + /// @{ + void messageSiblings(S32 message); ///< Send a message to all siblings + virtual void onMessage(GuiControl *sender, S32 msg); ///< Receive a message from another control + /// @} + + /// @name Canvas Events + /// Functions called by the canvas + /// @{ + + /// Called if this object is a dialog, when it is added to the visible layers + virtual void onDialogPush(); + + /// Called if this object is a dialog, when it is removed from the visible layers + virtual void onDialogPop(); + /// @} + + /// Renders justified text using the profile. + /// + /// @note This should move into the graphics library at some point + void renderJustifiedText(Point2I offset, Point2I extent, const char *text); + + /// Returns text clipped to fit within a pixel width. The clipping + /// occurs on the right side and "..." is appended. It returns width + /// of the final clipped text in pixels. + U32 clipText( String &inOutText, U32 width ) const; + + void inspectPostApply(); + void inspectPreApply(); +}; + +typedef GuiControl::horizSizingOptions GuiHorizontalSizing; +typedef GuiControl::vertSizingOptions GuiVerticalSizing; + +DefineEnumType( GuiHorizontalSizing ); +DefineEnumType( GuiVerticalSizing ); + +/// @} + +#endif diff --git a/Engine/source/gui/core/guiDefaultControlRender.cpp b/Engine/source/gui/core/guiDefaultControlRender.cpp new file mode 100644 index 000000000..de148db73 --- /dev/null +++ b/Engine/source/gui/core/guiDefaultControlRender.cpp @@ -0,0 +1,547 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/core/guiDefaultControlRender.h" + +#include "gui/core/guiTypes.h" +#include "core/color.h" +#include "math/mRect.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxDrawUtil.h" + + +static ColorI colorLightGray(192, 192, 192); +static ColorI colorGray(128, 128, 128); +static ColorI colorDarkGray(64, 64, 64); +static ColorI colorWhite(255,255,255); +static ColorI colorBlack(0,0,0); + +void renderRaisedBox( const RectI &bounds, GuiControlProfile *profile ) +{ + S32 l = bounds.point.x, r = bounds.point.x + bounds.extent.x - 1; + S32 t = bounds.point.y, b = bounds.point.y + bounds.extent.y - 1; + + GFX->getDrawUtil()->drawRectFill( bounds, profile->mFillColor); + GFX->getDrawUtil()->drawLine(l, t, l, b - 1, colorWhite); + GFX->getDrawUtil()->drawLine(l, t, r - 1, t, colorWhite); + + GFX->getDrawUtil()->drawLine(l, b, r, b, colorBlack); + GFX->getDrawUtil()->drawLine(r, b - 1, r, t, colorBlack); + + GFX->getDrawUtil()->drawLine(l + 1, b - 1, r - 1, b - 1, profile->mBorderColor); + GFX->getDrawUtil()->drawLine(r - 1, b - 2, r - 1, t + 1, profile->mBorderColor); +} + +void renderSlightlyRaisedBox( const RectI &bounds, GuiControlProfile *profile ) +{ + S32 l = bounds.point.x + 1, r = bounds.point.x + bounds.extent.x - 1; + S32 t = bounds.point.y + 1, b = bounds.point.y + bounds.extent.y - 1; + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + drawer->drawRectFill( bounds, profile->mFillColor); + drawer->drawLine(l, t, l, b, profile->mBorderColor); + drawer->drawLine(l, t, r, t, profile->mBorderColor); + drawer->drawLine(l + 1, b, r, b, profile->mBorderColor); + drawer->drawLine(r, t + 1, r, b - 1, profile->mBorderColor); +} + +void renderLoweredBox( const RectI &bounds, GuiControlProfile *profile ) +{ + S32 l = bounds.point.x, r = bounds.point.x + bounds.extent.x - 1; + S32 t = bounds.point.y, b = bounds.point.y + bounds.extent.y - 1; + + GFX->getDrawUtil()->drawRectFill( bounds, profile->mFillColor); + + GFX->getDrawUtil()->drawLine(l, b, r, b, colorWhite); + GFX->getDrawUtil()->drawLine(r, b - 1, r, t, colorWhite); + + GFX->getDrawUtil()->drawLine(l, t, r - 1, t, colorBlack); + GFX->getDrawUtil()->drawLine(l, t + 1, l, b - 1, colorBlack); + + GFX->getDrawUtil()->drawLine(l + 1, t + 1, r - 2, t + 1, profile->mBorderColor); + GFX->getDrawUtil()->drawLine(l + 1, t + 2, l + 1, b - 2, profile->mBorderColor); +} + +void renderSlightlyLoweredBox( const RectI &bounds, GuiControlProfile *profile ) +{ + S32 l = bounds.point.x + 1, r = bounds.point.x + bounds.extent.x - 1; + S32 t = bounds.point.y + 1, b = bounds.point.y + bounds.extent.y - 1; + + GFX->getDrawUtil()->drawRectFill( bounds, profile->mFillColor); + GFX->getDrawUtil()->drawLine(l, b, r, b, profile->mBorderColor); + GFX->getDrawUtil()->drawLine(r, t, r, b - 1, profile->mBorderColor); + GFX->getDrawUtil()->drawLine(l, t, l, b - 1, profile->mBorderColor); + GFX->getDrawUtil()->drawLine(l + 1, t, r - 1, t, profile->mBorderColor); +} + +void renderBorder( const RectI &bounds, GuiControlProfile *profile ) +{ + S32 l = bounds.point.x, r = bounds.point.x + bounds.extent.x - 1; + S32 t = bounds.point.y, b = bounds.point.y + bounds.extent.y - 1; + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + + switch(profile->mBorder) + { + case 1: + drawer->drawRect(bounds, profile->mBorderColor); + break; + case 2: + drawer->drawLine(l + 1, t + 1, l + 1, b - 2, profile->mBevelColorHL); + drawer->drawLine(l + 2, t + 1, r - 2, t + 1, profile->mBevelColorHL); + drawer->drawLine(r, t, r, b, profile->mBevelColorHL); + drawer->drawLine(l, b, r - 1, b, profile->mBevelColorHL); + drawer->drawLine(l, t, r - 1, t, profile->mBorderColorNA); + drawer->drawLine(l, t + 1, l, b - 1, profile->mBorderColorNA); + drawer->drawLine(l + 1, b - 1, r - 1, b - 1, profile->mBorderColorNA); + drawer->drawLine(r - 1, t + 1, r - 1, b - 2, profile->mBorderColorNA); + break; + case 3: + drawer->drawLine(l, b, r, b, profile->mBevelColorHL); + drawer->drawLine(r, t, r, b - 1, profile->mBevelColorHL); + drawer->drawLine(l + 1, b - 1, r - 1, b - 1, profile->mFillColor); + drawer->drawLine(r - 1, t + 1, r - 1, b - 2, profile->mFillColor); + drawer->drawLine(l, t, l, b - 1, profile->mBorderColorNA); + drawer->drawLine(l + 1, t, r - 1, t, profile->mBorderColorNA); + drawer->drawLine(l + 1, t + 1, l + 1, b - 2, profile->mBevelColorLL); + drawer->drawLine(l + 2, t + 1, r - 2, t + 1, profile->mBevelColorLL); + break; + case 4: + drawer->drawLine(l, t, l, b - 1, profile->mBevelColorHL); + drawer->drawLine(l + 1, t, r, t, profile->mBevelColorHL); + drawer->drawLine(l, b, r, b, profile->mBevelColorLL); + drawer->drawLine(r, t + 1, r, b - 1, profile->mBevelColorLL); + drawer->drawLine(l + 1, b - 1, r - 1, b - 1, profile->mBorderColor); + drawer->drawLine(r - 1, t + 1, r - 1, b - 2, profile->mBorderColor); + break; + case 5: + renderFilledBorder( bounds, profile ); + break; + // + case -1: + // Draw a simple sizable border with corners + // Taken from the 'Skinnable GUI Controls in TGE' resource by Justin DuJardin + if(profile->mBitmapArrayRects.size() >= 8) + { + drawer->clearBitmapModulation(); + + RectI destRect; + RectI stretchRect; + RectI* mBitmapBounds = profile->mBitmapArrayRects.address(); + + // Indices into the bitmap array + enum + { + BorderTopLeft = 0, + BorderTop, + BorderTopRight, + BorderLeft, + //Fill, + BorderRight, + BorderBottomLeft, + BorderBottom, + BorderBottomRight, + NumBitmaps + }; + + // Draw all corners first. + + //top left border + drawer->drawBitmapSR(profile->mTextureObject,Point2I(bounds.point.x,bounds.point.y),mBitmapBounds[BorderTopLeft]); + //top right border + drawer->drawBitmapSR(profile->mTextureObject,Point2I(bounds.point.x + bounds.extent.x - mBitmapBounds[BorderTopRight].extent.x,bounds.point.y),mBitmapBounds[BorderTopRight]); + + //bottom left border + drawer->drawBitmapSR(profile->mTextureObject,Point2I(bounds.point.x,bounds.point.y + bounds.extent.y - mBitmapBounds[BorderBottomLeft].extent.y),mBitmapBounds[BorderBottomLeft]); + //bottom right border + drawer->drawBitmapSR(profile->mTextureObject,Point2I( + bounds.point.x + bounds.extent.x - mBitmapBounds[BorderBottomRight].extent.x, + bounds.point.y + bounds.extent.y - mBitmapBounds[BorderBottomRight].extent.y), + mBitmapBounds[BorderBottomRight]); + + // End drawing corners + + // Begin drawing sides and top stretched borders + + //start with top line stretch + destRect.point.x = bounds.point.x + mBitmapBounds[BorderTopLeft].extent.x; + destRect.extent.x = bounds.extent.x - mBitmapBounds[BorderTopRight].extent.x - mBitmapBounds[BorderTopLeft].extent.x; + destRect.extent.y = mBitmapBounds[BorderTop].extent.y; + destRect.point.y = bounds.point.y; + //stretch it + stretchRect = mBitmapBounds[BorderTop]; + stretchRect.inset(1,0); + //draw it + drawer->drawBitmapStretchSR(profile->mTextureObject,destRect,stretchRect); + //bottom line stretch + destRect.point.x = bounds.point.x + mBitmapBounds[BorderBottomLeft].extent.x; + destRect.extent.x = bounds.extent.x - mBitmapBounds[BorderBottomRight].extent.x - mBitmapBounds[BorderBottomLeft].extent.x; + destRect.extent.y = mBitmapBounds[BorderBottom].extent.y; + destRect.point.y = bounds.point.y + bounds.extent.y - mBitmapBounds[BorderBottom].extent.y; + //stretch it + stretchRect = mBitmapBounds[BorderBottom]; + stretchRect.inset(1,0); + //draw it + drawer->drawBitmapStretchSR(profile->mTextureObject,destRect,stretchRect); + //left line stretch + destRect.point.x = bounds.point.x; + destRect.extent.x = mBitmapBounds[BorderLeft].extent.x; + destRect.extent.y = bounds.extent.y - mBitmapBounds[BorderTopLeft].extent.y - mBitmapBounds[BorderBottomLeft].extent.y; + destRect.point.y = bounds.point.y + mBitmapBounds[BorderTopLeft].extent.y; + //stretch it + stretchRect = mBitmapBounds[BorderLeft]; + stretchRect.inset(0,1); + //draw it + drawer->drawBitmapStretchSR(profile->mTextureObject,destRect,stretchRect); + //right line stretch + destRect.point.x = bounds.point.x + bounds.extent.x - mBitmapBounds[BorderRight].extent.x; + destRect.extent.x = mBitmapBounds[BorderRight].extent.x; + destRect.extent.y = bounds.extent.y - mBitmapBounds[BorderTopRight].extent.y - mBitmapBounds[BorderBottomRight].extent.y; + destRect.point.y = bounds.point.y + mBitmapBounds[BorderTopRight].extent.y; + //stretch it + stretchRect = mBitmapBounds[BorderRight]; + stretchRect.inset(0,1); + //draw it + drawer->drawBitmapStretchSR(profile->mTextureObject,destRect,stretchRect); + + // End drawing sides and top stretched borders + break; + } + case -2: + // Draw a simple sizable border with corners that is filled in + renderSizableBitmapBordersFilled(bounds, 1, profile); + break; + case -3: + // Draw a simple fixed height border with center fill horizontally. + renderFixedBitmapBordersFilled( bounds, 1, profile ); + break; + + } +} + +void renderFilledBorder( const RectI &bounds, GuiControlProfile *profile ) +{ + renderFilledBorder( bounds, profile->mBorderColor, profile->mFillColor, profile->mBorderThickness ); +} + +void renderFilledBorder( const RectI &bounds, const ColorI &borderColor, const ColorI &fillColor, U32 thickness ) +{ + RectI fillBounds = bounds; + fillBounds.inset( thickness, thickness ); + + GFX->getDrawUtil()->drawRectFill( bounds, borderColor ); + GFX->getDrawUtil()->drawRectFill( fillBounds, fillColor ); +} + +// Render out the sizable bitmap borders based on a multiplier into the bitmap array +// Based on the 'Skinnable GUI Controls in TGE' resource by Justin DuJardin +void renderSizableBitmapBordersFilled( const RectI &bounds, S32 baseMultiplier, GuiControlProfile *profile) +{ + // Indices into the bitmap array + S32 numBitmaps = 9; + S32 borderTopLeft = numBitmaps * baseMultiplier - numBitmaps; + S32 borderTop = 1 + borderTopLeft; + S32 borderTopRight = 2 + borderTopLeft; + S32 borderLeft = 3 + borderTopLeft; + S32 fill = 4 + borderTopLeft; + S32 borderRight = 5 + borderTopLeft; + S32 borderBottomLeft = 6 + borderTopLeft; + S32 borderBottom = 7 + borderTopLeft; + S32 borderBottomRight = 8 + borderTopLeft; + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + + drawer->clearBitmapModulation(); + + if(profile->mBitmapArrayRects.size() >= (numBitmaps * baseMultiplier)) + { + RectI destRect; + RectI stretchRect; + RectI* mBitmapBounds = profile->mBitmapArrayRects.address(); + + // Draw all corners first. + + //top left border + drawer->drawBitmapSR(profile->mTextureObject,Point2I(bounds.point.x,bounds.point.y),mBitmapBounds[borderTopLeft]); + //top right border + drawer->drawBitmapSR(profile->mTextureObject,Point2I(bounds.point.x + bounds.extent.x - mBitmapBounds[borderTopRight].extent.x,bounds.point.y),mBitmapBounds[borderTopRight]); + + //bottom left border + drawer->drawBitmapSR(profile->mTextureObject,Point2I(bounds.point.x,bounds.point.y + bounds.extent.y - mBitmapBounds[borderBottomLeft].extent.y),mBitmapBounds[borderBottomLeft]); + //bottom right border + drawer->drawBitmapSR(profile->mTextureObject,Point2I( + bounds.point.x + bounds.extent.x - mBitmapBounds[borderBottomRight].extent.x, + bounds.point.y + bounds.extent.y - mBitmapBounds[borderBottomRight].extent.y), + mBitmapBounds[borderBottomRight]); + + // End drawing corners + + // Begin drawing sides and top stretched borders + + //start with top line stretch + destRect.point.x = bounds.point.x + mBitmapBounds[borderTopLeft].extent.x; + destRect.extent.x = bounds.extent.x - mBitmapBounds[borderTopRight].extent.x - mBitmapBounds[borderTopLeft].extent.x; + destRect.extent.y = mBitmapBounds[borderTop].extent.y; + destRect.point.y = bounds.point.y; + //stretch it + stretchRect = mBitmapBounds[borderTop]; + stretchRect.inset(1,0); + //draw it + drawer->drawBitmapStretchSR(profile->mTextureObject,destRect,stretchRect); + //bottom line stretch + destRect.point.x = bounds.point.x + mBitmapBounds[borderBottomLeft].extent.x; + destRect.extent.x = bounds.extent.x - mBitmapBounds[borderBottomRight].extent.x - mBitmapBounds[borderBottomLeft].extent.x; + destRect.extent.y = mBitmapBounds[borderBottom].extent.y; + destRect.point.y = bounds.point.y + bounds.extent.y - mBitmapBounds[borderBottom].extent.y; + //stretch it + stretchRect = mBitmapBounds[borderBottom]; + stretchRect.inset(1,0); + //draw it + drawer->drawBitmapStretchSR(profile->mTextureObject,destRect,stretchRect); + //left line stretch + destRect.point.x = bounds.point.x; + destRect.extent.x = mBitmapBounds[borderLeft].extent.x; + destRect.extent.y = bounds.extent.y - mBitmapBounds[borderTopLeft].extent.y - mBitmapBounds[borderBottomLeft].extent.y; + destRect.point.y = bounds.point.y + mBitmapBounds[borderTopLeft].extent.y; + //stretch it + stretchRect = mBitmapBounds[borderLeft]; + stretchRect.inset(0,1); + //draw it + drawer->drawBitmapStretchSR(profile->mTextureObject,destRect,stretchRect); + //right line stretch + destRect.point.x = bounds.point.x + bounds.extent.x - mBitmapBounds[borderRight].extent.x; + destRect.extent.x = mBitmapBounds[borderRight].extent.x; + destRect.extent.y = bounds.extent.y - mBitmapBounds[borderTopRight].extent.y - mBitmapBounds[borderBottomRight].extent.y; + destRect.point.y = bounds.point.y + mBitmapBounds[borderTopRight].extent.y; + //stretch it + stretchRect = mBitmapBounds[borderRight]; + stretchRect.inset(0,1); + //draw it + drawer->drawBitmapStretchSR(profile->mTextureObject,destRect,stretchRect); + //fill stretch + destRect.point.x = bounds.point.x + mBitmapBounds[borderLeft].extent.x; + destRect.extent.x = (bounds.extent.x) - mBitmapBounds[borderLeft].extent.x - mBitmapBounds[borderRight].extent.x; + destRect.extent.y = bounds.extent.y - mBitmapBounds[borderTop].extent.y - mBitmapBounds[borderBottom].extent.y; + destRect.point.y = bounds.point.y + mBitmapBounds[borderTop].extent.y; + //stretch it + stretchRect = mBitmapBounds[fill]; + stretchRect.inset(1,1); + //draw it + drawer->drawBitmapStretchSR(profile->mTextureObject,destRect,stretchRect); + + // End drawing sides and top stretched borders + } +} + + +// Render out the sizable bitmap borders based on a multiplier into the bitmap array +// Based on the 'Skinnable GUI Controls in TGE' resource by Justin DuJardin +void renderSizableBitmapBordersFilledIndex( const RectI &bounds, S32 startIndex, GuiControlProfile *profile ) +{ + // Indices into the bitmap array + S32 numBitmaps = 9; + S32 borderTopLeft = startIndex; + S32 borderTop = 1 + borderTopLeft; + S32 borderTopRight = 2 + borderTopLeft; + S32 borderLeft = 3 + borderTopLeft; + S32 fill = 4 + borderTopLeft; + S32 borderRight = 5 + borderTopLeft; + S32 borderBottomLeft = 6 + borderTopLeft; + S32 borderBottom = 7 + borderTopLeft; + S32 borderBottomRight = 8 + borderTopLeft; + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + + drawer->clearBitmapModulation(); + if(profile->mBitmapArrayRects.size() >= (startIndex + numBitmaps)) + { + RectI destRect; + RectI stretchRect; + RectI* mBitmapBounds = profile->mBitmapArrayRects.address(); + + // Draw all corners first. + + //top left border + drawer->drawBitmapSR(profile->mTextureObject,Point2I(bounds.point.x,bounds.point.y),mBitmapBounds[borderTopLeft]); + //top right border + drawer->drawBitmapSR(profile->mTextureObject,Point2I(bounds.point.x + bounds.extent.x - mBitmapBounds[borderTopRight].extent.x,bounds.point.y),mBitmapBounds[borderTopRight]); + + //bottom left border + drawer->drawBitmapSR(profile->mTextureObject,Point2I(bounds.point.x,bounds.point.y + bounds.extent.y - mBitmapBounds[borderBottomLeft].extent.y),mBitmapBounds[borderBottomLeft]); + //bottom right border + drawer->drawBitmapSR(profile->mTextureObject,Point2I( + bounds.point.x + bounds.extent.x - mBitmapBounds[borderBottomRight].extent.x, + bounds.point.y + bounds.extent.y - mBitmapBounds[borderBottomRight].extent.y), + mBitmapBounds[borderBottomRight]); + + // End drawing corners + + // Begin drawing sides and top stretched borders + + //start with top line stretch + destRect.point.x = bounds.point.x + mBitmapBounds[borderTopLeft].extent.x; + destRect.extent.x = bounds.extent.x - mBitmapBounds[borderTopRight].extent.x - mBitmapBounds[borderTopLeft].extent.x; + destRect.extent.y = mBitmapBounds[borderTop].extent.y; + destRect.point.y = bounds.point.y; + //stretch it + stretchRect = mBitmapBounds[borderTop]; + stretchRect.inset(1,0); + //draw it + drawer->drawBitmapStretchSR(profile->mTextureObject,destRect,stretchRect); + //bottom line stretch + destRect.point.x = bounds.point.x + mBitmapBounds[borderBottomLeft].extent.x; + destRect.extent.x = bounds.extent.x - mBitmapBounds[borderBottomRight].extent.x - mBitmapBounds[borderBottomLeft].extent.x; + destRect.extent.y = mBitmapBounds[borderBottom].extent.y; + destRect.point.y = bounds.point.y + bounds.extent.y - mBitmapBounds[borderBottom].extent.y; + //stretch it + stretchRect = mBitmapBounds[borderBottom]; + stretchRect.inset(1,0); + //draw it + drawer->drawBitmapStretchSR(profile->mTextureObject,destRect,stretchRect); + //left line stretch + destRect.point.x = bounds.point.x; + destRect.extent.x = mBitmapBounds[borderLeft].extent.x; + destRect.extent.y = bounds.extent.y - mBitmapBounds[borderTopLeft].extent.y - mBitmapBounds[borderBottomLeft].extent.y; + destRect.point.y = bounds.point.y + mBitmapBounds[borderTopLeft].extent.y; + //stretch it + stretchRect = mBitmapBounds[borderLeft]; + stretchRect.inset(0,1); + //draw it + drawer->drawBitmapStretchSR(profile->mTextureObject,destRect,stretchRect); + //left line stretch + destRect.point.x = bounds.point.x + bounds.extent.x - mBitmapBounds[borderRight].extent.x; + destRect.extent.x = mBitmapBounds[borderRight].extent.x; + destRect.extent.y = bounds.extent.y - mBitmapBounds[borderTopRight].extent.y - mBitmapBounds[borderBottomRight].extent.y; + destRect.point.y = bounds.point.y + mBitmapBounds[borderTopRight].extent.y; + //stretch it + stretchRect = mBitmapBounds[borderRight]; + stretchRect.inset(0,1); + //draw it + drawer->drawBitmapStretchSR(profile->mTextureObject,destRect,stretchRect); + //fill stretch + destRect.point.x = bounds.point.x + mBitmapBounds[borderLeft].extent.x; + destRect.extent.x = (bounds.extent.x) - mBitmapBounds[borderLeft].extent.x - mBitmapBounds[borderRight].extent.x; + destRect.extent.y = bounds.extent.y - mBitmapBounds[borderTop].extent.y - mBitmapBounds[borderBottom].extent.y; + destRect.point.y = bounds.point.y + mBitmapBounds[borderTop].extent.y; + //stretch it + stretchRect = mBitmapBounds[fill]; + stretchRect.inset(1,1); + //draw it + drawer->drawBitmapStretchSR(profile->mTextureObject,destRect,stretchRect); + + // End drawing sides and top stretched borders + } +} + + + +// Render out the fixed bitmap borders based on a multiplier into the bitmap array +// It renders left and right caps, with a sizable fill area in the middle to reach +// the x extent. It does not stretch in the y direction. +void renderFixedBitmapBordersFilled( const RectI &bounds, S32 baseMultiplier, GuiControlProfile *profile ) +{ + // Indices into the bitmap array + S32 numBitmaps = 3; + S32 borderLeft = numBitmaps * baseMultiplier - numBitmaps; + S32 fill = 1 + borderLeft; + S32 borderRight = 2 + borderLeft; + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + + drawer->clearBitmapModulation(); + if(profile->mBitmapArrayRects.size() >= (numBitmaps * baseMultiplier)) + { + RectI destRect; + RectI stretchRect; + RectI* mBitmapBounds = profile->mBitmapArrayRects.address(); + + // Draw all corners first. + + //left border + drawer->drawBitmapSR(profile->mTextureObject,Point2I(bounds.point.x,bounds.point.y),mBitmapBounds[borderLeft]); + //right border + drawer->drawBitmapSR(profile->mTextureObject,Point2I(bounds.point.x + bounds.extent.x - mBitmapBounds[borderRight].extent.x,bounds.point.y),mBitmapBounds[borderRight]); + + // End drawing corners + + // Begin drawing fill + + //fill stretch + destRect.point.x = bounds.point.x + mBitmapBounds[borderLeft].extent.x; + destRect.extent.x = (bounds.extent.x) - mBitmapBounds[borderLeft].extent.x - mBitmapBounds[borderRight].extent.x; + destRect.extent.y = mBitmapBounds[fill].extent.y; + destRect.point.y = bounds.point.y; + //stretch it + stretchRect = mBitmapBounds[fill]; + stretchRect.inset(1,0); + //draw it + drawer->drawBitmapStretchSR(profile->mTextureObject,destRect,stretchRect); + + // End drawing fill + } +} + +// Render out the fixed bitmap borders based on a multiplier into the bitmap array +// It renders left and right caps, with a sizable fill area in the middle to reach +// the x extent. It does not stretch in the y direction. +void renderFixedBitmapBordersFilledIndex( const RectI &bounds, S32 startIndex, GuiControlProfile *profile ) +{ + // Indices into the bitmap array + S32 numBitmaps = 3; + S32 borderLeft = startIndex; + S32 fill = 1 + startIndex; + S32 borderRight = 2 + startIndex; + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + drawer->clearBitmapModulation(); + if(profile->mBitmapArrayRects.size() >= (startIndex + numBitmaps)) + { + RectI destRect; + RectI stretchRect; + RectI* mBitmapBounds = profile->mBitmapArrayRects.address(); + + // Draw all corners first. + + //left border + drawer->drawBitmapSR(profile->mTextureObject,Point2I(bounds.point.x,bounds.point.y),mBitmapBounds[borderLeft]); + //right border + drawer->drawBitmapSR(profile->mTextureObject,Point2I(bounds.point.x + bounds.extent.x - mBitmapBounds[borderRight].extent.x,bounds.point.y),mBitmapBounds[borderRight]); + + // End drawing corners + + // Begin drawing fill + + //fill stretch + destRect.point.x = bounds.point.x + mBitmapBounds[borderLeft].extent.x; + destRect.extent.x = (bounds.extent.x) - mBitmapBounds[borderLeft].extent.x - mBitmapBounds[borderRight].extent.x; + destRect.extent.y = mBitmapBounds[fill].extent.y; + destRect.point.y = bounds.point.y; + //stretch it + stretchRect = mBitmapBounds[fill]; + stretchRect.inset(1,0); + //draw it + drawer->drawBitmapStretchSR(profile->mTextureObject,destRect,stretchRect); + + // End drawing fill + } +} diff --git a/Engine/source/gui/core/guiDefaultControlRender.h b/Engine/source/gui/core/guiDefaultControlRender.h new file mode 100644 index 000000000..9f07c0975 --- /dev/null +++ b/Engine/source/gui/core/guiDefaultControlRender.h @@ -0,0 +1,45 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _H_GUIDEFAULTCONTROLRENDER_ +#define _H_GUIDEFAULTCONTROLRENDER_ + +#ifndef _MRECT_H_ +#include "math/mRect.h" +#endif + +class GuiControlProfile; +class ColorI; + +void renderRaisedBox( const RectI &bounds, GuiControlProfile *profile); +void renderSlightlyRaisedBox( const RectI &bounds, GuiControlProfile *profile); +void renderLoweredBox( const RectI &bounds, GuiControlProfile *profile); +void renderSlightlyLoweredBox( const RectI &bounds, GuiControlProfile *profile); +void renderBorder( const RectI &bounds, GuiControlProfile *profile); +void renderFilledBorder( const RectI &bounds, GuiControlProfile *profile ); +void renderFilledBorder( const RectI &bounds, const ColorI &borderColor, const ColorI &fillColor, U32 thickness = 1 ); +void renderSizableBitmapBordersFilled( const RectI &bounds, S32 baseMultiplier, GuiControlProfile *profile); // Added +void renderSizableBitmapBordersFilledIndex( const RectI &bounds, S32 startIndex, GuiControlProfile *profile); +void renderFixedBitmapBordersFilled( const RectI &bounds, S32 baseMultiplier, GuiControlProfile *profile); // Added +void renderFixedBitmapBordersFilled( const RectI &bounds, S32 startIndex, GuiControlProfile *profile); + +#endif diff --git a/Engine/source/gui/core/guiScriptNotifyControl.cpp b/Engine/source/gui/core/guiScriptNotifyControl.cpp new file mode 100644 index 000000000..bd1cc63de --- /dev/null +++ b/Engine/source/gui/core/guiScriptNotifyControl.cpp @@ -0,0 +1,285 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/core/guiScriptNotifyControl.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" + +//------------------------------------------------------------------------------ + +IMPLEMENT_CONOBJECT(GuiScriptNotifyCtrl); + +ConsoleDocClass( GuiScriptNotifyCtrl, + "@brief A control which adds several reactions to other GUIs via callbacks.\n\n" + + "GuiScriptNotifyCtrl does not exist to render anything. When parented or made a child of " + "other controls, you can toggle flags on or off to make use of its specialized callbacks. " + "Normally these callbacks are used as utility functions by the GUI Editor, or other container " + "classes. However, for very fancy GUI work where controls interact with each other " + "constantly, this is a handy utility to make use of.\n\n " + + "@tsexample\n" + "// Common member fields left out for sake of example\n" + "new GuiScriptNotifyCtrl()\n" + "{\n" + " onChildAdded = \"0\";\n" + " onChildRemoved = \"0\";\n" + " onChildResized = \"0\";\n" + " onParentResized = \"0\";\n" + "};\n" + "@endtsexample\n\n" + + "@ingroup GuiUtil\n"); + +GuiScriptNotifyCtrl::GuiScriptNotifyCtrl() +{ + mOnChildAdded = false; + mOnChildRemoved = false; + mOnResize = false; + mOnChildResized = false; + mOnParentResized = false; +} + +GuiScriptNotifyCtrl::~GuiScriptNotifyCtrl() +{ +} + +void GuiScriptNotifyCtrl::initPersistFields() +{ + // Callbacks Group + addGroup("Callbacks"); + addField("onChildAdded", TypeBool, Offset( mOnChildAdded, GuiScriptNotifyCtrl ), "Enables/disables onChildAdded callback" ); + addField("onChildRemoved", TypeBool, Offset( mOnChildRemoved, GuiScriptNotifyCtrl ), "Enables/disables onChildRemoved callback" ); + addField("onChildResized", TypeBool, Offset( mOnChildResized, GuiScriptNotifyCtrl ), "Enables/disables onChildResized callback" ); + addField("onParentResized", TypeBool, Offset( mOnParentResized, GuiScriptNotifyCtrl ), "Enables/disables onParentResized callback" ); + addField("onResize", TypeBool, Offset( mOnResize, GuiScriptNotifyCtrl ), "Enables/disables onResize callback" ); + addField("onLoseFirstResponder", TypeBool, Offset( mOnLoseFirstResponder, GuiScriptNotifyCtrl ), "Enables/disables onLoseFirstResponder callback" ); + addField("onGainFirstResponder", TypeBool, Offset( mOnGainFirstResponder, GuiScriptNotifyCtrl ), "Enables/disables onGainFirstResponder callback" ); + endGroup("Callbacks"); + + Parent::initPersistFields(); +} + +IMPLEMENT_CALLBACK( GuiScriptNotifyCtrl, onResize, void, ( SimObjectId ID ), ( ID ), + "Called when this GUI is resized.\n\n" + "@param ID Unique object ID assigned when created (%this in script).\n" +); +IMPLEMENT_CALLBACK( GuiScriptNotifyCtrl, onChildAdded, void, ( SimObjectId ID, SimObjectId childID ), ( ID, childID ), + "Called when a child is added to this GUI.\n\n" + "@param ID Unique object ID assigned when created (%this in script).\n" + "@param childID Unique object ID of child being added.\n" +); +IMPLEMENT_CALLBACK( GuiScriptNotifyCtrl, onChildRemoved, void, ( SimObjectId ID, SimObjectId childID ), ( ID, childID ), + "Called when a child is removed from this GUI.\n\n" + "@param ID Unique object ID assigned when created (%this in script).\n" + "@param childID Unique object ID of child being removed.\n" +); +IMPLEMENT_CALLBACK( GuiScriptNotifyCtrl, onChildResized, void, ( SimObjectId ID, SimObjectId childID ), ( ID, childID ), + "Called when a child is of this GUI is being resized.\n\n" + "@param ID Unique object ID assigned when created (%this in script).\n" + "@param childID Unique object ID of child being resized.\n" +); +IMPLEMENT_CALLBACK( GuiScriptNotifyCtrl, onParentResized, void, ( SimObjectId ID ), ( ID ), + "Called when this GUI's parent is resized.\n\n" + "@param ID Unique object ID assigned when created (%this in script).\n" +); +IMPLEMENT_CALLBACK( GuiScriptNotifyCtrl, onLoseFirstResponder, void, ( SimObjectId ID ), ( ID ), + "Called when this GUI loses focus.\n\n" + "@param ID Unique object ID assigned when created (%this in script).\n" +); +IMPLEMENT_CALLBACK( GuiScriptNotifyCtrl, onGainFirstResponder, void, ( SimObjectId ID ), ( ID ), + "Called when this GUI gains focus.\n\n" + "@param ID Unique object ID assigned when created (%this in script).\n" +); + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // +void GuiScriptNotifyCtrl::onChildAdded( GuiControl *child ) +{ + Parent::onChildAdded( child ); + + // Call Script. + if( mOnChildAdded ) + onChildAdded_callback(getId(), child->getId()); +} + +void GuiScriptNotifyCtrl::onChildRemoved( GuiControl *child ) +{ + Parent::onChildRemoved( child ); + + // Call Script. + if( mOnChildRemoved ) + onChildRemoved_callback(getId(), child->getId()); +} +//---------------------------------------------------------------- + +bool GuiScriptNotifyCtrl::resize(const Point2I &newPosition, const Point2I &newExtent) +{ + if( !Parent::resize( newPosition, newExtent ) ) + return false; + + // Call Script. + if( mOnResize ) + onResize_callback(getId()); + + return true; +} + +void GuiScriptNotifyCtrl::childResized(GuiScriptNotifyCtrl *child) +{ + Parent::childResized( child ); + + // Call Script. + if( mOnChildResized ) + onChildResized_callback(getId(), child->getId()); +} + +void GuiScriptNotifyCtrl::parentResized(const RectI &oldParentRect, const RectI &newParentRect) +{ + Parent::parentResized( oldParentRect, newParentRect ); + + // Call Script. + if( mOnParentResized ) + onParentResized_callback(getId()); +} + +void GuiScriptNotifyCtrl::onLoseFirstResponder() +{ + Parent::onLoseFirstResponder(); + + // Call Script. + if( mOnLoseFirstResponder ) + onLoseFirstResponder_callback(getId()); +} + +void GuiScriptNotifyCtrl::setFirstResponder( GuiControl* firstResponder ) +{ + Parent::setFirstResponder( firstResponder ); + + // Call Script. + if( mOnGainFirstResponder && isFirstResponder() ) + onGainFirstResponder_callback(getId()); +} + +void GuiScriptNotifyCtrl::setFirstResponder() +{ + Parent::setFirstResponder(); + + // Call Script. + if( mOnGainFirstResponder && isFirstResponder() ) + onGainFirstResponder_callback(getId()); +} + +void GuiScriptNotifyCtrl::onMessage(GuiScriptNotifyCtrl *sender, S32 msg) +{ + Parent::onMessage( sender, msg ); +} + +void GuiScriptNotifyCtrl::onDialogPush() +{ + Parent::onDialogPush(); +} + +void GuiScriptNotifyCtrl::onDialogPop() +{ + Parent::onDialogPop(); +} + + +//void GuiScriptNotifyCtrl::onMouseUp(const GuiEvent &event) +//{ +//} +// +//void GuiScriptNotifyCtrl::onMouseDown(const GuiEvent &event) +//{ +//} +// +//void GuiScriptNotifyCtrl::onMouseMove(const GuiEvent &event) +//{ +//} +// +//void GuiScriptNotifyCtrl::onMouseDragged(const GuiEvent &event) +//{ +//} +// +//void GuiScriptNotifyCtrl::onMouseEnter(const GuiEvent &) +//{ +//} +// +//void GuiScriptNotifyCtrl::onMouseLeave(const GuiEvent &) +//{ +//} +// +//bool GuiScriptNotifyCtrl::onMouseWheelUp( const GuiEvent &event ) +//{ +//} +// +//bool GuiScriptNotifyCtrl::onMouseWheelDown( const GuiEvent &event ) +//{ +//} +// +//void GuiScriptNotifyCtrl::onRightMouseDown(const GuiEvent &) +//{ +//} +// +//void GuiScriptNotifyCtrl::onRightMouseUp(const GuiEvent &) +//{ +//} +// +//void GuiScriptNotifyCtrl::onRightMouseDragged(const GuiEvent &) +//{ +//} +// +//void GuiScriptNotifyCtrl::onMiddleMouseDown(const GuiEvent &) +//{ +//} +// +//void GuiScriptNotifyCtrl::onMiddleMouseUp(const GuiEvent &) +//{ +//} +// +//void GuiScriptNotifyCtrl::onMiddleMouseDragged(const GuiEvent &) +//{ +//} +//void GuiScriptNotifyCtrl::onMouseDownEditor(const GuiEvent &event, Point2I offset) +//{ +//} +//void GuiScriptNotifyCtrl::onRightMouseDownEditor(const GuiEvent &event, Point2I offset) +//{ +//} + +//bool GuiScriptNotifyCtrl::onKeyDown(const GuiEvent &event) +//{ +// if ( Parent::onKeyDown( event ) ) +// return true; +//} +// +//bool GuiScriptNotifyCtrl::onKeyRepeat(const GuiEvent &event) +//{ +// // default to just another key down. +// return onKeyDown(event); +//} +// +//bool GuiScriptNotifyCtrl::onKeyUp(const GuiEvent &event) +//{ +// if ( Parent::onKeyUp( event ) ) +// return true; +//} diff --git a/Engine/source/gui/core/guiScriptNotifyControl.h b/Engine/source/gui/core/guiScriptNotifyControl.h new file mode 100644 index 000000000..c94d3e283 --- /dev/null +++ b/Engine/source/gui/core/guiScriptNotifyControl.h @@ -0,0 +1,115 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUISCRIPTNOTIFYCTRL_H_ +#define _GUISCRIPTNOTIFYCTRL_H_ + +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif + +class GuiScriptNotifyCtrl : public GuiControl +{ +private: + typedef GuiControl Parent; +public: + + /// @name Event Callbacks + /// @{ + bool mOnChildAdded; ///< Script Notify : onAddObject(%object) + bool mOnChildRemoved; ///< Script Notify : onRemoveObject(%object) + bool mOnResize; ///< Script Notify : onResize() + bool mOnChildResized; ///< Script Notify : onChildResized(%child) + bool mOnParentResized; ///< Script Notify : onParentResized() + bool mOnLoseFirstResponder; ///< Script Notify : onLoseFirstResponder() + bool mOnGainFirstResponder; ///< Script Notify : onGainFirstResponder() + /// @} + +public: + /// @name Initialization + /// @{ + DECLARE_CONOBJECT(GuiScriptNotifyCtrl); + DECLARE_CATEGORY( "Gui Other Script" ); + DECLARE_DESCRIPTION( "A control that implements various script callbacks for\n" + "certain GUI events." ); + + GuiScriptNotifyCtrl(); + virtual ~GuiScriptNotifyCtrl(); + static void initPersistFields(); + + virtual bool resize(const Point2I &newPosition, const Point2I &newExtent); + virtual void childResized(GuiScriptNotifyCtrl *child); + virtual void parentResized(const RectI &oldParentRect, const RectI &newParentRect); + virtual void onChildRemoved( GuiControl *child ); + virtual void onChildAdded( GuiControl *child ); + + DECLARE_CALLBACK(void, onResize, (SimObjectId ID) ); + DECLARE_CALLBACK(void, onChildAdded, (SimObjectId ID, SimObjectId childID)); + DECLARE_CALLBACK(void, onChildRemoved, (SimObjectId ID, SimObjectId childID)); + DECLARE_CALLBACK(void, onChildResized, (SimObjectId ID, SimObjectId childID)); + DECLARE_CALLBACK(void, onParentResized, (SimObjectId ID)); + DECLARE_CALLBACK(void, onLoseFirstResponder, (SimObjectId ID)); + DECLARE_CALLBACK(void, onGainFirstResponder, (SimObjectId ID)); + + + + + //virtual void onMouseUp(const GuiEvent &event); + //virtual void onMouseDown(const GuiEvent &event); + //virtual void onMouseMove(const GuiEvent &event); + //virtual void onMouseDragged(const GuiEvent &event); + //virtual void onMouseEnter(const GuiEvent &event); + //virtual void onMouseLeave(const GuiEvent &event); + + //virtual bool onMouseWheelUp(const GuiEvent &event); + //virtual bool onMouseWheelDown(const GuiEvent &event); + + //virtual void onRightMouseDown(const GuiEvent &event); + //virtual void onRightMouseUp(const GuiEvent &event); + //virtual void onRightMouseDragged(const GuiEvent &event); + + //virtual void onMiddleMouseDown(const GuiEvent &event); + //virtual void onMiddleMouseUp(const GuiEvent &event); + //virtual void onMiddleMouseDragged(const GuiEvent &event); + + //virtual void onMouseDownEditor(const GuiEvent &event, Point2I offset); + //virtual void onRightMouseDownEditor(const GuiEvent &event, Point2I offset); + + virtual void setFirstResponder(GuiControl *firstResponder); + virtual void setFirstResponder(); + void clearFirstResponder(); + virtual void onLoseFirstResponder(); + + //virtual void acceleratorKeyPress(U32 index); + //virtual void acceleratorKeyRelease(U32 index); + //virtual bool onKeyDown(const GuiEvent &event); + //virtual bool onKeyUp(const GuiEvent &event); + //virtual bool onKeyRepeat(const GuiEvent &event); + + virtual void onMessage(GuiScriptNotifyCtrl *sender, S32 msg); ///< Receive a message from another control + + virtual void onDialogPush(); + virtual void onDialogPop(); + +}; + +#endif diff --git a/Engine/source/gui/core/guiTypes.cpp b/Engine/source/gui/core/guiTypes.cpp new file mode 100644 index 000000000..31929f78c --- /dev/null +++ b/Engine/source/gui/core/guiTypes.cpp @@ -0,0 +1,737 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "platform/types.h" +#include "console/consoleTypes.h" +#include "console/console.h" +#include "gui/core/guiTypes.h" +#include "gui/core/guiControl.h" +#include "gfx/gFont.h" +#include "gfx/bitmap/gBitmap.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxDrawUtil.h" +#include "sfx/sfxTrack.h" +#include "sfx/sfxTypes.h" +#include "console/engineAPI.h" + +//#define DEBUG_SPEW + + +// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // +IMPLEMENT_CONOBJECT(GuiCursor); + +ConsoleDocClass( GuiCursor, + "@brief Acts as a skin for the cursor, where each GuiCursor object can have its own look and click-zone.\n\n" + + "GuiCursors act as skins for the cursor in the game, where each individual GuiCursor can have its own defined imagemap,\n" + "click zone and render offset. This allows a game to easily support a wide range of cursors. The active cursor can de changed\n" + "for each Canvas using %canvasObj.setCursor(GuiCursor);." + + "@tsexample\n" + "new GuiCursor(DefaultCursor)\n" + "{\n" + " hotSpot = \"1 1\";\n" + " renderOffset = \"0 0\";\n" + " bitmapName = \"~/art/gui/images/defaultCursor\";\n" + "};\n" + "@endtsexample\n\n" + "@see GuiCanvas\n\n" + "@ingroup GuiCore\n" +); + +GFX_ImplementTextureProfile(GFXGuiCursorProfile, + GFXTextureProfile::DiffuseMap, + GFXTextureProfile::PreserveSize | + GFXTextureProfile::Static, + GFXTextureProfile::None); +GFX_ImplementTextureProfile(GFXDefaultGUIProfile, + GFXTextureProfile::DiffuseMap, + GFXTextureProfile::PreserveSize | + GFXTextureProfile::Static | + GFXTextureProfile::NoPadding, + GFXTextureProfile::None); + + +GuiCursor::GuiCursor() +{ + mHotSpot.set(0,0); + mRenderOffset.set(0.0f,0.0f); + mExtent.set(1,1); + mTextureObject = NULL; +} + +GuiCursor::~GuiCursor() +{ +} + +void GuiCursor::initPersistFields() +{ + addField("hotSpot", TypePoint2I, Offset(mHotSpot, GuiCursor), "The location of the cursor's hot spot (which pixel carries the click)."); + addField("renderOffset",TypePoint2F, Offset(mRenderOffset, GuiCursor), "Offset of the bitmap, where 0 signifies left edge of the bitmap, 1, the right. Similarly for the Y-component."); + addField("bitmapName", TypeFilename, Offset(mBitmapName, GuiCursor), "File name of the bitmap for the cursor."); + Parent::initPersistFields(); +} + +bool GuiCursor::onAdd() +{ + if(!Parent::onAdd()) + return false; + + Sim::getGuiDataGroup()->addObject(this); + + return true; +} + +void GuiCursor::onRemove() +{ + Parent::onRemove(); +} + +void GuiCursor::render(const Point2I &pos) +{ + if (!mTextureObject && mBitmapName && mBitmapName[0]) + { + mTextureObject.set( mBitmapName, &GFXGuiCursorProfile, avar("%s() - mTextureObject (line %d)", __FUNCTION__, __LINE__)); + if(!mTextureObject) + return; + mExtent.set(mTextureObject->getWidth(), mTextureObject->getHeight()); + } + + // Render the cursor centered according to dimensions of texture + S32 texWidth = mTextureObject.getWidth(); + S32 texHeight = mTextureObject.getHeight(); + + Point2I renderPos = pos; + renderPos.x -= (S32)( texWidth * mRenderOffset.x ); + renderPos.y -= (S32)( texHeight * mRenderOffset.y ); + + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmap(mTextureObject, renderPos); +} + +//------------------------------------------------------------------------------ +IMPLEMENT_CONOBJECT(GuiControlProfile); + +ConsoleDocClass( GuiControlProfile, + "@brief A collection of properties that determine control behavior and rendering.\n" + "@ingroup GuiCore\n" + + "" +); + +ImplementEnumType( GuiAlignmentType, + "\n\n" + "@ingroup GuiCore" ) + { GuiControlProfile::LeftJustify, "Left" }, + { GuiControlProfile::CenterJustify, "Center" }, + { GuiControlProfile::RightJustify, "Right" }, + { GuiControlProfile::TopJustify, "Top" }, + { GuiControlProfile::BottomJustify, "Bottom" } +EndImplementEnumType; + +ImplementEnumType( GuiFontCharset, + "\n\n" + "@ingroup GuiCore" ) + { TGE_ANSI_CHARSET, "ANSI" }, + { TGE_SYMBOL_CHARSET, "SYMBOL" }, + { TGE_SHIFTJIS_CHARSET, "SHIFTJIS" }, + { TGE_HANGEUL_CHARSET, "HANGEUL" }, + { TGE_HANGUL_CHARSET, "HANGUL" }, + { TGE_GB2312_CHARSET, "GB2312" }, + { TGE_CHINESEBIG5_CHARSET, "CHINESEBIG5" }, + { TGE_OEM_CHARSET, "OEM" }, + { TGE_JOHAB_CHARSET, "JOHAB" }, + { TGE_HEBREW_CHARSET, "HEBREW" }, + { TGE_ARABIC_CHARSET, "ARABIC" }, + { TGE_GREEK_CHARSET, "GREEK" }, + { TGE_TURKISH_CHARSET, "TURKISH" }, + { TGE_VIETNAMESE_CHARSET, "VIETNAMESE" }, + { TGE_THAI_CHARSET, "THAI" }, + { TGE_EASTEUROPE_CHARSET, "EASTEUROPE" }, + { TGE_RUSSIAN_CHARSET, "RUSSIAN" }, + { TGE_MAC_CHARSET, "MAC" }, + { TGE_BALTIC_CHARSET, "BALTIC" }, +EndImplementEnumType; + + +StringTableEntry GuiControlProfile::sFontCacheDirectory = ""; + +void GuiControlProfile::setBitmapHandle(GFXTexHandle handle) +{ + mTextureObject = handle; + + mBitmapName = StringTable->insert("texhandle"); +} + +bool GuiControlProfile::protectedSetBitmap( void *object, const char *index, const char *data ) +{ + GuiControlProfile *profile = static_cast( object ); + + profile->mBitmapName = StringTable->insert(data); + + if ( !profile->isProperlyAdded() ) + return false; + + if( profile->mLoadCount > 0 ) + { + profile->mBitmapArrayRects.clear(); + profile->mTextureObject = NULL; + + //verify the bitmap + if (profile->mBitmapName && profile->mBitmapName[0] && dStricmp(profile->mBitmapName, "texhandle") != 0 && + !profile->mTextureObject.set( profile->mBitmapName, &GFXDefaultPersistentProfile, avar("%s() - mTextureObject (line %d)", __FUNCTION__, __LINE__) )) + Con::errorf("Failed to load profile bitmap (%s)",profile->mBitmapName); + + // If we've got a special border, make sure it's usable. + //if( profile->mBorder == -1 || profile->mBorder == -2 ) + profile->constructBitmapArray(); + } + + return false; +} + +const char* GuiControlProfile::protectedGetSoundButtonDown( void* object, const char* data ) +{ + GuiControlProfile* profile = reinterpret_cast< GuiControlProfile* >( object ); + + SFXTrack* track = profile->mSoundButtonDown; + if( !track ) + return ""; + + return track->getName(); +} + +bool GuiControlProfile::protectedSetSoundButtonDown( void* object, const char* index, const char* data ) +{ + GuiControlProfile* profile = reinterpret_cast< GuiControlProfile* >( object ); + + SFXTrack* track = NULL; + if( data && data[ 0] && !Sim::findObject( data, track ) ) + { + Con::errorf( "GuiControlProfile::protectedSetSoundButtonDown - no SFXTrack '%s'", data ); + return false; + } + + profile->mSoundButtonDown = track; + + return false; +} + +const char* GuiControlProfile::protectedGetSoundButtonOver( void* object, const char* data ) +{ + GuiControlProfile* profile = reinterpret_cast< GuiControlProfile* >( object ); + + SFXTrack* track = profile->mSoundButtonOver; + if( !track ) + return ""; + + return track->getName(); +} + +bool GuiControlProfile::protectedSetSoundButtonOver( void* object, const char* index, const char* data ) +{ + GuiControlProfile* profile = reinterpret_cast< GuiControlProfile* >( object ); + + SFXTrack* track = NULL; + if( data && data[ 0] && !Sim::findObject( data, track ) ) + { + Con::errorf( "GuiControlProfile::protectedSetSoundButtonOver - no SFXTrack '%s'", data ); + return false; + } + + profile->mSoundButtonOver = track; + + return false; +} + +GuiControlProfile::GuiControlProfile(void) : + mFillColor(255,0,255,255), + mFillColorHL(255,0,255,255), + mFillColorNA(255,0,255,255), + mFillColorSEL(255,0,255,255), + mBorderColor(255,0,255,255), + mBorderColorHL(255,0,255,255), + mBorderColorNA(255,0,255,255), + mBevelColorHL(255,0,255,255), + mBevelColorLL(255,0,255,255), + // initialize these references to locations in the font colors array + // the array is initialized below. + mFontColor(mFontColors[BaseColor]), + mFontColorHL(mFontColors[ColorHL]), + mFontColorNA(mFontColors[ColorNA]), + mFontColorSEL(mFontColors[ColorSEL]), + mCursorColor(255,0,255,255), + mTextOffset(0,0), + mBitmapArrayRects(0) +{ + mLoadCount = 0; + mUseCount = 0; + + // event focus behavior + mTabable = false; + mCanKeyFocus = false; + mModal = false; + + // fill and border + mOpaque = false; + mBorder = 1; + mBorderThickness = 1; + + // font members + mFontType = "Arial"; + mFontSize = 10; + + for(U32 i = 0; i < 10; i++) + mFontColors[i].set(255,0,255,255); + + mFontCharset = TGE_ANSI_CHARSET; + + // sizing and alignment + mAlignment = LeftJustify; + mAutoSizeWidth = false; + mAutoSizeHeight= false; + mReturnTab = false; + mNumbersOnly = false; + mMouseOverSelected = false; + + // bitmap members + mBitmapName = NULL; + mUseBitmapArray = false; + mTextureObject = NULL; // initialized in incLoadCount() + + mChildrenProfileName = NULL; + mChildrenProfile = NULL; + + // inherit/copy values from GuiDefaultProfile + GuiControlProfile *def = dynamic_cast(Sim::findObject("GuiDefaultProfile")); + if (def) + { + mTabable = def->mTabable; + mCanKeyFocus = def->mCanKeyFocus; + + mOpaque = def->mOpaque; + mFillColor = def->mFillColor; + mFillColorHL = def->mFillColorHL; + mFillColorNA = def->mFillColorNA; + mFillColorSEL = def->mFillColorSEL; + + mBorder = def->mBorder; + mBorderThickness = def->mBorderThickness; + mBorderColor = def->mBorderColor; + mBorderColorHL = def->mBorderColorHL; + mBorderColorNA = def->mBorderColorNA; + + mBevelColorHL = def->mBevelColorHL; + mBevelColorLL = def->mBevelColorLL; + + // default font + mFontType = def->mFontType; + mFontSize = def->mFontSize; + mFontCharset = def->mFontCharset; + + for(U32 i = 0; i < 10; i++) + mFontColors[i] = def->mFontColors[i]; + + // default bitmap + mBitmapName = def->mBitmapName; + mUseBitmapArray = def->mUseBitmapArray; + mTextOffset = def->mTextOffset; + + // default sound + mSoundButtonDown = def->mSoundButtonDown; + mSoundButtonOver = def->mSoundButtonOver; + + //used by GuiTextCtrl + mModal = def->mModal; + mAlignment = def->mAlignment; + mAutoSizeWidth = def->mAutoSizeWidth; + mAutoSizeHeight= def->mAutoSizeHeight; + mReturnTab = def->mReturnTab; + mNumbersOnly = def->mNumbersOnly; + mCursorColor = def->mCursorColor; + mChildrenProfileName = def->mChildrenProfileName; + setChildrenProfile(def->mChildrenProfile); + } +} + +GuiControlProfile::~GuiControlProfile() +{ +} + + +void GuiControlProfile::initPersistFields() +{ + addGroup( "Behavior" ); + + addField( "tab", TypeBool, Offset(mTabable, GuiControlProfile)); + addField("canKeyFocus", TypeBool, Offset(mCanKeyFocus, GuiControlProfile), + "Whether the control can have the keyboard focus." ); + addField("mouseOverSelected", TypeBool, Offset(mMouseOverSelected, GuiControlProfile)); + addField("modal", TypeBool, Offset(mModal, GuiControlProfile)); + + endGroup( "Behavior" ); + + addGroup( "Appearance" ); + + addField("opaque", TypeBool, Offset(mOpaque, GuiControlProfile)); + addField("fillColor", TypeColorI, Offset(mFillColor, GuiControlProfile)); + addField("fillColorHL", TypeColorI, Offset(mFillColorHL, GuiControlProfile)); + addField("fillColorNA", TypeColorI, Offset(mFillColorNA, GuiControlProfile)); + addField("fillColorSEL", TypeColorI, Offset(mFillColorSEL, GuiControlProfile)); + addField("border", TypeS32, Offset(mBorder, GuiControlProfile), + "Border type (0=no border)." ); + addField("borderThickness",TypeS32, Offset(mBorderThickness, GuiControlProfile), + "Thickness of border in pixels." ); + addField("borderColor", TypeColorI, Offset(mBorderColor, GuiControlProfile), + "Color to draw border with." ); + addField("borderColorHL", TypeColorI, Offset(mBorderColorHL, GuiControlProfile)); + addField("borderColorNA", TypeColorI, Offset(mBorderColorNA, GuiControlProfile)); + + addField("bevelColorHL", TypeColorI, Offset(mBevelColorHL, GuiControlProfile)); + addField("bevelColorLL", TypeColorI, Offset(mBevelColorLL, GuiControlProfile)); + + endGroup( "Appearance" ); + + addGroup( "Text" ); + + addField("fontType", TypeString, Offset(mFontType, GuiControlProfile), + "Name of font family and typeface (e.g. \"Arial Bold\")." ); + addField("fontSize", TypeS32, Offset(mFontSize, GuiControlProfile), + "Font size in points." ); + addField("fontCharset", TYPEID< FontCharset >(), Offset(mFontCharset, GuiControlProfile) ); + addField("fontColors", TypeColorI, Offset(mFontColors, GuiControlProfile), 10, + "Font colors to use for different text types/states." ); + addField("fontColor", TypeColorI, Offset(mFontColors[BaseColor], GuiControlProfile), + "Font color for normal text (same as fontColors[0])." ); + addField("fontColorHL", TypeColorI, Offset(mFontColors[ColorHL], GuiControlProfile), + "Font color for highlighted text (same as fontColors[1])." ); + addField("fontColorNA", TypeColorI, Offset(mFontColors[ColorNA], GuiControlProfile), + "Font color when control is not active/disabled (same as fontColors[2])." ); + addField("fontColorSEL", TypeColorI, Offset(mFontColors[ColorSEL], GuiControlProfile), + "Font color for selected text (same as fontColors[3])." ); + addField("fontColorLink", TypeColorI, Offset(mFontColors[ColorUser0], GuiControlProfile), + "Font color for links in text (same as fontColors[4])." ); + addField("fontColorLinkHL", TypeColorI, Offset(mFontColors[ColorUser1], GuiControlProfile), + "Font color for highlighted links in text (same as fontColors[5])." ); + + addField( "justify", TYPEID< GuiControlProfile::AlignmentType >(), Offset(mAlignment, GuiControlProfile), + "Horizontal alignment for text." ); + addField( "textOffset", TypePoint2I, Offset(mTextOffset, GuiControlProfile)); + addField( "autoSizeWidth", TypeBool, Offset(mAutoSizeWidth, GuiControlProfile), + "Automatically adjust width of control to fit contents." ); + addField("autoSizeHeight",TypeBool, Offset(mAutoSizeHeight, GuiControlProfile), + "Automatically adjust height of control to fit contents." ); + addField("returnTab", TypeBool, Offset(mReturnTab, GuiControlProfile), + "Whether to add automatic tab event when return is pressed so focus moves on to next control (GuiTextEditCtrl)." ); + addField("numbersOnly", TypeBool, Offset(mNumbersOnly, GuiControlProfile), + "Whether control should only accept numerical data (GuiTextEditCtrl)." ); + addField("cursorColor", TypeColorI, Offset(mCursorColor, GuiControlProfile), + "Color to use for the text cursor." ); + + endGroup( "Text" ); + + addGroup( "Misc" ); + + addProtectedField( "bitmap", TypeFilename, Offset(mBitmapName, GuiControlProfile), + &GuiControlProfile::protectedSetBitmap, &defaultProtectedGetFn, + "Texture to use for rendering control." ); + addField("hasBitmapArray", TypeBool, Offset(mUseBitmapArray, GuiControlProfile), + "If true, 'bitmap' is an array of images." ); + + addProtectedField( "soundButtonDown", TypeSFXTrackName, Offset(mSoundButtonDown, GuiControlProfile), + &GuiControlProfile::protectedSetSoundButtonDown, &GuiControlProfile::protectedGetSoundButtonDown, + "Sound to play when mouse has been pressed on control." ); + addProtectedField( "soundButtonOver", TypeSFXTrackName, Offset(mSoundButtonOver, GuiControlProfile), + &GuiControlProfile::protectedSetSoundButtonOver, &GuiControlProfile::protectedGetSoundButtonOver, + "Sound to play when mouse is hovering over control." ); + addField("profileForChildren", TypeString, Offset(mChildrenProfileName, GuiControlProfile)); + + endGroup( "Misc" ); + + addField( "category", TypeRealString, Offset( mCategory, GuiControlProfile ), + "Category under which the profile will appear in the editor." + ); + + Parent::initPersistFields(); +} + +bool GuiControlProfile::onAdd() +{ + if(!Parent::onAdd()) + return false; + + Sim::getGuiDataGroup()->addObject(this); + + // Make sure we have an up-to-date children profile + getChildrenProfile(); + + return true; +} + +void GuiControlProfile::onStaticModified(const char* slotName, const char* newValue) +{ + if( mLoadCount > 0 ) + { + if ( !dStricmp(slotName, "fontType") || + !dStricmp(slotName, "fontCharset") || + !dStricmp(slotName, "fontSize" ) ) + { + // Reload the font + mFont = GFont::create(mFontType, mFontSize, sFontCacheDirectory, mFontCharset); + if ( mFont == NULL ) + Con::errorf("Failed to load/create profile font (%s/%d)", mFontType, mFontSize); + } + } +} + +void GuiControlProfile::onDeleteNotify(SimObject *object) +{ + if (object == mChildrenProfile) + mChildrenProfile = NULL; +} + +GuiControlProfile* GuiControlProfile::getChildrenProfile() +{ + // We can early out if we still have a valid profile + if (mChildrenProfile) + return mChildrenProfile; + + // Attempt to find the profile specified + if (mChildrenProfileName) + { + GuiControlProfile *profile = dynamic_cast(Sim::findObject( mChildrenProfileName )); + + if( profile ) + setChildrenProfile(profile); + } + + return mChildrenProfile; +} + +void GuiControlProfile::setChildrenProfile(GuiControlProfile *prof) +{ + if(prof == mChildrenProfile) + return; + + // Clear the delete notification we previously set up + if (mChildrenProfile) + clearNotify(mChildrenProfile); + + mChildrenProfile = prof; + + // Make sure that the new profile will notify us when it is deleted + if (mChildrenProfile) + deleteNotify(mChildrenProfile); +} + +RectI GuiControlProfile::getBitmapArrayRect(U32 i) +{ + if(!mBitmapArrayRects.size()) + constructBitmapArray(); + + if( i >= mBitmapArrayRects.size()) + return RectI(0,0,0,0); + + return mBitmapArrayRects[i]; +} + +S32 GuiControlProfile::constructBitmapArray() +{ + if(mBitmapArrayRects.size()) + return mBitmapArrayRects.size(); + + if( mTextureObject.isNull() ) + { + if ( !mBitmapName || !mBitmapName[0] || !mTextureObject.set( mBitmapName, &GFXDefaultPersistentProfile, avar("%s() - mTextureObject (line %d)", __FUNCTION__, __LINE__) )) + return 0; + } + + GBitmap *bmp = mTextureObject->getBitmap(); + + //get the separator color + ColorI sepColor; + if ( !bmp || !bmp->getColor( 0, 0, sepColor ) ) + { + Con::errorf("Failed to create bitmap array from %s for profile %s - couldn't ascertain seperator color!", mBitmapName, getName()); + AssertFatal( false, avar("Failed to create bitmap array from %s for profile %s - couldn't ascertain seperator color!", mBitmapName, getName())); + return 0; + } + + //now loop through all the scroll pieces, and find the bounding rectangle for each piece in each state + S32 curY = 0; + + // ascertain the height of this row... + ColorI color; + mBitmapArrayRects.clear(); + while(curY < bmp->getHeight()) + { + // skip any sep colors + bmp->getColor( 0, curY, color); + if(color == sepColor) + { + curY++; + continue; + } + // ok, process left to right, grabbing bitmaps as we go... + S32 curX = 0; + while(curX < bmp->getWidth()) + { + bmp->getColor(curX, curY, color); + if(color == sepColor) + { + curX++; + continue; + } + S32 startX = curX; + while(curX < bmp->getWidth()) + { + bmp->getColor(curX, curY, color); + if(color == sepColor) + break; + curX++; + } + S32 stepY = curY; + while(stepY < bmp->getHeight()) + { + bmp->getColor(startX, stepY, color); + if(color == sepColor) + break; + stepY++; + } + mBitmapArrayRects.push_back(RectI(startX, curY, curX - startX, stepY - curY)); + } + // ok, now skip to the next separation color on column 0 + while(curY < bmp->getHeight()) + { + bmp->getColor(0, curY, color); + if(color == sepColor) + break; + curY++; + } + } + return mBitmapArrayRects.size(); +} + +void GuiControlProfile::incLoadCount() +{ + if( !mLoadCount ) + { + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[GuiControlProfile] Loading profile %i:%s (%s:%s)", + getId(), getClassName(), getName(), getInternalName() ); + #endif + + sFontCacheDirectory = Con::getVariable( "$GUI::fontCacheDirectory" ); + + // Load font (if not already loaded). + + if( mFont == NULL ) + loadFont(); + + // + + if (mBitmapName && mBitmapName[0] && dStricmp(mBitmapName, "texhandle") != 0 && + !mTextureObject.set( mBitmapName, &GFXDefaultPersistentProfile, avar("%s() - mTextureObject (line %d)", __FUNCTION__, __LINE__) )) + Con::errorf("Failed to load profile bitmap (%s)",mBitmapName); + + constructBitmapArray(); + } + + mLoadCount ++; + + // Quick check to make sure our children profile is up-to-date + getChildrenProfile(); +} + +void GuiControlProfile::decLoadCount() +{ + AssertFatal( mLoadCount, "GuiControlProfile::decLoadCount - zero load count" ); + if(!mLoadCount) + return; + + -- mLoadCount; + if( !mLoadCount ) + { + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[GuiControlProfile] Unloading profile %i:%s (%s:%s)", + getId(), getClassName(), getName(), getInternalName() ); + #endif + + if( !mBitmapName || !mBitmapName[0] || dStricmp(mBitmapName, "texhandle") != 0 ) + mTextureObject = NULL; + } +} + +bool GuiControlProfile::loadFont() +{ + mFont = GFont::create( mFontType, mFontSize, sFontCacheDirectory, mFontCharset ); + if( mFont == NULL ) + { + Con::errorf( "GuiControlProfile::loadFont - Failed to load/create profile font (%s/%d)", mFontType, mFontSize ); + return false; + } + + return true; +} + +ConsoleMethod( GuiControlProfile, getStringWidth, S32, 3, 3, "( pString )" ) +{ + return object->mFont->getStrNWidth( argv[2], dStrlen( argv[2] ) ); +} + +//----------------------------------------------------------------------------- +// TypeRectSpacingI +//----------------------------------------------------------------------------- +IMPLEMENT_STRUCT( RectSpacingI, + RectSpacingI, GuiAPI, + "" ) + + FIELD( left, leftPadding, 1, "" ) + FIELD( right, rightPadding, 1, "" ) + FIELD( top, topPadding, 1, "" ) + FIELD( bottom, bottomPadding, 1, "" ) + +END_IMPLEMENT_STRUCT; + +ConsoleType( RectSpacingI, TypeRectSpacingI, RectSpacingI ) +ImplementConsoleTypeCasters( TypeRectSpacingI, RectSpacingI ) + +ConsoleGetType( TypeRectSpacingI ) +{ + RectSpacingI *rect = (RectSpacingI *) dptr; + char* returnBuffer = Con::getReturnBuffer(256); + dSprintf(returnBuffer, 256, "%d %d %d %d", rect->top, rect->bottom, + rect->left, rect->right); + return returnBuffer; +} + +ConsoleSetType( TypeRectSpacingI ) +{ + if(argc == 1) + dSscanf(argv[0], "%d %d %d %d", &((RectSpacingI *) dptr)->top, &((RectSpacingI *) dptr)->bottom, + &((RectSpacingI *) dptr)->left, &((RectSpacingI *) dptr)->right); + else if(argc == 4) + *((RectSpacingI *) dptr) = RectSpacingI(dAtoi(argv[0]), dAtoi(argv[1]), dAtoi(argv[2]), dAtoi(argv[3])); + else + Con::printf("RectSpacingI must be set as { t, b, l, r } or \"t b l r\""); +} diff --git a/Engine/source/gui/core/guiTypes.h b/Engine/source/gui/core/guiTypes.h new file mode 100644 index 000000000..acb1e743d --- /dev/null +++ b/Engine/source/gui/core/guiTypes.h @@ -0,0 +1,523 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUITYPES_H_ +#define _GUITYPES_H_ + +#ifndef _GFONT_H_ +#include "gfx/gFont.h" +#endif +#ifndef _COLOR_H_ +#include "core/color.h" +#endif +#ifndef _SIMBASE_H_ +#include "console/simBase.h" +#endif +#ifndef _DYNAMIC_CONSOLETYPES_H_ +#include "console/dynamicTypes.h" +#endif + + +#include "gfx/gfxDevice.h" +#include "platform/event.h" + +class GBitmap; +class SFXTrack; + +/// Represents a single GUI event. +/// +/// This is passed around to all the relevant controls so they know what's going on. +struct GuiEvent +{ + U16 ascii; ///< ascii character code 'a', 'A', 'b', '*', etc (if device==keyboard) - possibly a uchar or something + U8 modifier; ///< SI_LSHIFT, etc + InputObjectInstances keyCode; ///< for unprintables, 'tab', 'return', ... + Point2I mousePoint; ///< for mouse events + U8 mouseClickCount; ///< to determine double clicks, etc... + U8 mouseAxis; ///< mousewheel axis (0 == X, 1 == Y) + F32 fval; ///< used for mousewheel events + + GuiEvent() + : ascii( 0 ), + modifier( 0 ), + keyCode( KEY_NULL ), + mousePoint( 0, 0 ), + mouseClickCount( 0 ), + mouseAxis( 0 ), + fval( 0.f ) {} +}; + +/// Represent a mouse event with a 3D position and vector. +/// +/// This event used by the EditTSCtrl derived controls. +struct Gui3DMouseEvent : public GuiEvent +{ + Point3F vec; + Point3F pos; + + Gui3DMouseEvent() + : vec( 0.f, 0.f, 0.f ), + pos( 0.f, 0.f, 0.f ) {} +}; + + +/// @name Docking Flag +/// @{ +/// @brief Docking Options available to all GuiControl subclasses. +namespace Docking +{ + enum DockingType + { + dockNone = BIT(0), ///< Do not align this control to it's parent, let the control specify it's position/extent (default) + dockClient = BIT(1), ///< Align this control to the client area available in the parent + dockTop = BIT(2), ///< Align this control to the topmost border of it's parent (Width will be parent width) + dockBottom = BIT(3), ///< Align this control to the bottommost border of it's parent (Width will be parent width) + dockLeft = BIT(4), ///< Align this control to the leftmost border of it's parent (Height will be parent height) + dockRight = BIT(5), ///< Align this control to the rightmost border of it's parent (Height will be parent height) + dockInvalid = BIT(6), ///< Default NOT specified docking mode, this allows old sizing to takeover when needed by controls + dockAny = dockClient | dockTop | dockBottom | dockLeft | dockRight + }; +}; + +typedef Docking::DockingType GuiDockingType; +DefineEnumType( GuiDockingType ); + +/// @} + + +/// @name Margin Padding Structure +/// @{ +struct RectSpacingI +{ + S32 left; + S32 top; + S32 bottom; + S32 right; + RectSpacingI() { left = right = top = bottom = 0; }; + RectSpacingI( S32 in_top, S32 in_bottom, S32 in_left, S32 in_right ) + { + top = in_top; + bottom = in_bottom; + left = in_left; + right = in_right; + } + void setAll( S32 value ) { left = right = top = bottom = value; }; + void set( S32 in_top, S32 in_bottom, S32 in_left, S32 in_right ) + { + top = in_top; + bottom = in_bottom; + left = in_left; + right = in_right; + } + void insetRect( RectI &rectRef ) + { + // Inset by padding + rectRef.point.x += left; + rectRef.point.y += top; + rectRef.extent.x -= (left + right ); + rectRef.extent.y -= (bottom + top ); + } + void expandRect( RectI &rectRef ) + { + // Inset by padding + rectRef.point.x -= left; + rectRef.point.y -= top; + rectRef.extent.x += (left + right ); + rectRef.extent.y += (bottom + top ); + } + + +}; + +DECLARE_STRUCT( RectSpacingI ); +DefineConsoleType( TypeRectSpacingI, RectSpacingI ); +/// @} + + +/// @name Axis-Aligned Edge Structure +/// @{ +/// +struct Edge +{ + Point2F normal; ///< The Normal of this edge + Point2I position;///< The Position of the edge + Point2I extent; ///< The X/Y extents of the edge + F32 margin; ///< The Size of the edge + + Edge(): normal(0.f,0.f), + position(0,0), + extent(0,0), + margin(1.f){}; + Edge( const Point2I &inPoint, const Point2F &inNormal ) + { + normal = inNormal; + margin = 2.f; + + if( normal.x == 1.f || normal.x == -1.f ) + { + // Vertical Edge + position.x = inPoint.x; + position.y = 0; + + extent.x = 1; + extent.y = 1; + } + else if( normal.y == 1.f || normal.y == -1.f ) + { + // Horizontal Edge + position.y = inPoint.y; + position.x = 0; + + extent.x = 1; + extent.y = 1; + } + else + AssertFatal(false,"Edge point constructor cannot construct an Edge without an axis-aligned normal."); + } + + // Copy Constructor + Edge( const Edge &inEdge ) + { + normal = inEdge.normal; + position = inEdge.position; + extent = inEdge.extent; + margin = inEdge.margin; + } + + // RectI cast operator overload + operator const RectI() const + { + if( normal.x == 1.f || normal.x == -1.f ) + { + // Vertical Edge + RectI retRect = RectI( position.x, position.y, 1, position.y + extent.y ); + // Expand Rect by Margin along the X Axis + retRect.inset(-margin,0); + + return retRect; + } + else if( normal.y == 1.f || normal.y == -1.f ) + { + // Horizontal Edge + RectI retRect = RectI( position.x, position.y , position.x + extent.x, 1 ); + // Expand Rect by Margin along the Y Axis + retRect.inset(0,-margin); + return retRect; + } + + // CodeReview this code only deals with axis-aligned edges [6/8/2007 justind] + AssertFatal(false,"Edge cast operator cannot construct a Rect from an Edge that is not axis-aligned."); + return RectI( 0,0,0,0 ); + } + + inline bool hit( const Edge &inEdge ) const + { + const RectI thisRect = *this; + const RectI thatRect = inEdge; + + return thisRect.overlaps( thatRect ); + } +}; +/// @} + + +struct EdgeRectI +{ + Edge left; + Edge top; + Edge bottom; + Edge right; + + EdgeRectI(){ } + + EdgeRectI( const RectI &inRect, F32 inMargin ) + { + // Left Edge + left.normal = Point2F( -1.f, 0.f ); + left.position.x= inRect.point.x; + left.position.y= 0; + left.extent = Point2I(inRect.point.y, inRect.point.y + inRect.extent.y); + left.margin = inMargin; + + // Right Edge + right.normal = Point2F( 1.f, 0.f ); + right.position.x = inRect.point.x + inRect.extent.x; + right.position.y = 0; + right.extent = Point2I(inRect.point.y, inRect.point.y + inRect.extent.y); + right.margin = inMargin; + + // Top Edge + top.normal = Point2F( 0.f, 1.f ); + top.position.y = inRect.point.y; + top.position.x = 0; + top.extent = Point2I(inRect.point.x + inRect.extent.x, inRect.point.x); + top.margin = inMargin; + + // Bottom Edge + bottom.normal = Point2F( 0.f, -1.f ); + bottom.position.y= inRect.point.y + inRect.extent.y; + bottom.position.x=0; + bottom.extent = Point2I(inRect.point.x + inRect.extent.x, inRect.point.x); + bottom.margin = inMargin; + } + + // Copy constructor + EdgeRectI( const EdgeRectI &inEdgeRect ) + { + left = inEdgeRect.left; + right = inEdgeRect.right; + top = inEdgeRect.top; + bottom = inEdgeRect.bottom; + } +}; + + +/// Represents the Sizing Options for a GuiControl +struct ControlSizing +{ + ControlSizing() + { + mDocking = Docking::dockInvalid; + mPadding.setAll( 0 ); + mInternalPadding.setAll( 0 ); + + // Default anchors to full top/left + mAnchorBottom = false; + mAnchorLeft = true; + mAnchorTop = true; + mAnchorRight = false; + }; + + S32 mDocking; ///< Docking Flag + + RectSpacingI mPadding; ///< Padding for each side of the control to have as spacing between other controls + /// For example 1,1,1,1 would mean one pixel at least of spacing between this control and the + /// one next to it. + RectSpacingI mInternalPadding; ///< Interior Spacing of the control + + + /// @name Anchoring + /// @{ + /// @brief Anchors are applied to @b ONLY controls that are children of any derivative of a + /// GuiContainer control. Anchors are applied when a parent is resized and a child + /// element should be resized to accommodate the new parent extent + /// + /// Anchors are specified as true or false and control whether a certain edge of a control + /// will be locked to a certain edge of a parent, when the parent resizes. Anchors are specified + /// as a Mask and therefore you may lock any number of edges to a parent container and when the parent + /// is resized, any locked edges on a control will remain the same distance from the parent edge it + /// is locked to, after the resize happens. + /// + bool mAnchorTop; ///< Anchor to the Top edge of the parent when created + bool mAnchorBottom; ///< Anchor to the Bottom edge of the parent when created + bool mAnchorLeft; ///< Anchor to the Left edge of the parent when created + bool mAnchorRight; ///< Anchor to the Right edge of the parent when created + /// @} + +}; + +class GuiCursor : public SimObject +{ +private: + typedef SimObject Parent; + StringTableEntry mBitmapName; + + Point2I mHotSpot; + Point2F mRenderOffset; + Point2I mExtent; + GFXTexHandle mTextureObject; + +public: + Point2I getHotSpot() { return mHotSpot; } + Point2I getExtent() { return mExtent; } + + DECLARE_CONOBJECT(GuiCursor); + GuiCursor(void); + ~GuiCursor(void); + static void initPersistFields(); + + bool onAdd(void); + void onRemove(); + void render(const Point2I &pos); +}; + +/// A GuiControlProfile is used by every GuiObject and is akin to a +/// datablock. It is used to control information that does not change +/// or is unlikely to change during execution of a program. It is also +/// a level of abstraction between script and GUI control so that you can +/// use the same control, say a button, and have it look completly different +/// just with a different profile. +class GuiControlProfile : public SimObject +{ +private: + typedef SimObject Parent; + +public: + static StringTableEntry sFontCacheDirectory; ///< Directory where Torque will store font *.uft files. + + U32 mUseCount; ///< Total number of controls currently referencing this profile. + U32 mLoadCount; ///< Number of controls in woken state using this profile; resources for the profile are loaded when this is >0. + bool mTabable; ///< True if this object is accessable from using the tab key + + bool mCanKeyFocus; ///< True if the object can be given keyboard focus (in other words, made a first responder @see GuiControl) + bool mModal; ///< True if this is a Modeless dialog meaning it will pass input through instead of taking it all + + bool mOpaque; ///< True if this object is not translucent, and should draw a fill + ColorI mFillColor; ///< Fill color, this is used to fill the bounds of the control if it is opaque + ColorI mFillColorHL; ///< This is used instead of mFillColor if the object is highlighted + ColorI mFillColorNA; ///< This is used instead of mFillColor if the object is not active or disabled + ColorI mFillColorSEL; ///< This is used instead of mFillColor if the object is selected + + S32 mBorder; ///< For most controls, if mBorder is > 0 a border will be drawn, some controls use this to draw different types of borders however @see guiDefaultControlRender.cc + S32 mBorderThickness; ///< Border thickness + ColorI mBorderColor; ///< Border color, used to draw a border around the bounds if border is enabled + ColorI mBorderColorHL; ///< Used instead of mBorderColor when the object is highlighted + ColorI mBorderColorNA; ///< Used instead of mBorderColor when the object is not active or disabled + + ColorI mBevelColorHL; ///< Used for the high-light part of the bevel + ColorI mBevelColorLL; ///< Used for the low-light part of the bevel + + // font members + StringTableEntry mFontType; ///< Font face name for the control + S32 mFontSize; ///< Font size for the control + enum { + BaseColor = 0, + ColorHL, + ColorNA, + ColorSEL, + ColorUser0, + ColorUser1, + ColorUser2, + ColorUser3, + ColorUser4, + ColorUser5, + }; + ColorI mFontColors[10]; ///< Array of font colors used for drawText with escape characters for changing color mid-string + ColorI& mFontColor; ///< Main font color + ColorI& mFontColorHL; ///< Highlighted font color + ColorI& mFontColorNA; ///< Font color when object is not active/disabled + ColorI& mFontColorSEL; ///< Font color when object/text is selected + FontCharset mFontCharset; ///< Font character set + + Resource mFont; ///< Font resource + + enum AlignmentType + { + LeftJustify, + RightJustify, + CenterJustify, + TopJustify, + BottomJustify + }; + + AlignmentType mAlignment; ///< Horizontal text alignment + bool mAutoSizeWidth; ///< Auto-size the width-bounds of the control to fit it's contents + bool mAutoSizeHeight; ///< Auto-size the height-bounds of the control to fit it's contents + bool mReturnTab; ///< Used in GuiTextEditCtrl to specify if a tab-event should be simulated when return is pressed. + bool mNumbersOnly; ///< For text controls, true if this should only accept numerical data + bool mMouseOverSelected; ///< True if this object should be "selected" while the mouse is over it + ColorI mCursorColor; ///< Color for the blinking cursor in text fields (for example) + + Point2I mTextOffset; ///< Text offset for the control + + // bitmap members + StringTableEntry mBitmapName; ///< Bitmap file name for the bitmap of the control + bool mUseBitmapArray; ///< Flag to use the bitmap array or to fallback to non-array rendering + GFXTexHandle mTextureObject; + Vector mBitmapArrayRects; ///< Used for controls which use an array of bitmaps such as checkboxes + + // sound members + SimObjectPtr< SFXTrack > mSoundButtonDown; ///< Sound played when the object is "down" ie a button is pushed + SimObjectPtr< SFXTrack > mSoundButtonOver; ///< Sound played when the mouse is over the object + + StringTableEntry mChildrenProfileName; ///< The name of the profile to use for the children controls + + /// Returns our children profile (and finds the profile if it hasn't been set yet) + GuiControlProfile* getChildrenProfile(); + + /// Category name for editing in the Gui Editor. + String mCategory; + + /// Sets the children profile for this profile + /// + /// @see GuiControlProfile + /// @param prof Tooltip profile to apply + void setChildrenProfile(GuiControlProfile *prof); +protected: + GuiControlProfile* mChildrenProfile; ///< Profile used with children controls (such as the scroll bar on a popup menu) when defined. + + static bool protectedSetBitmap( void *object, const char *index, const char *data ); + static bool protectedSetSoundButtonDown( void* object, const char* index, const char* data ); + static bool protectedSetSoundButtonOver( void* object, const char* index, const char* data ); + static const char* protectedGetSoundButtonDown( void* object, const char* data ); + static const char* protectedGetSoundButtonOver( void* object, const char* data ); + +public: + DECLARE_CONOBJECT(GuiControlProfile); + GuiControlProfile(); + ~GuiControlProfile(); + static void initPersistFields(); + + bool onAdd(); + + void onStaticModified(const char* slotName, const char* newValue = NULL ); + + /// Called when mProfileForChildren is deleted + virtual void onDeleteNotify(SimObject *object); + + /// This method creates an array of bitmaps from one single bitmap with + /// separator color. The separator color is whatever color is in pixel 0,0 + /// of the bitmap. For an example see darkWindow.png and some of the other + /// UI textures. It returns the number of bitmaps in the array it created + /// It also stores the sizes in the mBitmapArrayRects vector. + S32 constructBitmapArray(); + + /// This method returns the ith bitmap array rect, first ensuring that i is a + /// valid index into mBitmapArrayRects. If the vector is empty, we call + /// constructBitmapArray() automatically. If it is still empty, we return a + /// zeroed RectI. + RectI getBitmapArrayRect(U32 i); + + /// + bool isInUse() const { return ( mUseCount != 0 ); } + + void incUseCount() { mUseCount ++; } + void decUseCount() { if( mUseCount > 0 ) mUseCount --; } + + void incLoadCount(); + void decLoadCount(); + + bool loadFont(); + + void setBitmapHandle(GFXTexHandle handle); +}; + +typedef GuiControlProfile::AlignmentType GuiAlignmentType; +DefineEnumType( GuiAlignmentType ); + +typedef FontCharset GuiFontCharset; +DefineEnumType( GuiFontCharset ); + +GFX_DeclareTextureProfile(GFXGuiCursorProfile); +GFX_DeclareTextureProfile(GFXDefaultGUIProfile); + +#endif //_GUITYPES_H diff --git a/Engine/source/gui/editor/editorFunctions.cpp b/Engine/source/gui/editor/editorFunctions.cpp new file mode 100644 index 000000000..41168758b --- /dev/null +++ b/Engine/source/gui/editor/editorFunctions.cpp @@ -0,0 +1,79 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/editor/editorFunctions.h" + +#include "console/simObject.h" + + +bool validateObjectName( const char *data, const SimObject *object ) +{ + if( !data || !data[ 0 ] ) + return true; + + bool isValidId = true; + if( !dIsalpha( data[ 0 ] ) && data[ 0 ] != '_' ) + isValidId = false; + else + { + for( U32 i = 1; data[ i ] != '\0'; ++ i ) + { + if( !dIsalnum( data[ i ] ) && data[ i ] != '_' ) + { + isValidId = false; + break; + } + } + } + + if( !isValidId ) + { + Platform::AlertOK( "Invalid Object Name", avar( "'%s' is not a valid " + "object name. Please choose a name that begins with a letter or " + "underscore and is otherwise comprised exclusively of letters, " + "digits, and/or underscores.", data ) ); + return false; + } + + SimObject *pTemp = NULL; + if ( Sim::findObject( data, pTemp ) && pTemp != object ) + { + const char* filename = pTemp->getFilename(); + if ( !filename || !filename[0] ) + filename = "an unknown file"; + + Platform::AlertOK( "Invalid Object Name", avar( "Object names must be unique, " + "and there is an existing %s object with the name '%s' (defined in %s). " + "Please choose another name.", pTemp->getClassName(), data, filename ) ); + return false; + } + + if ( AbstractClassRep::findClassRep( data ) ) + { + Platform::AlertOK( "Invalid Object Name", avar( "'%s' is the name of an " + "existing TorqueScript class. Please choose another name.", data ) ); + return false; + } + + return true; +} \ No newline at end of file diff --git a/Engine/source/gui/editor/editorFunctions.h b/Engine/source/gui/editor/editorFunctions.h new file mode 100644 index 000000000..d304412f6 --- /dev/null +++ b/Engine/source/gui/editor/editorFunctions.h @@ -0,0 +1,33 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _EDITORFUNCTIONS_H_ +#define _EDITORFUNCTIONS_H_ + +class SimObject; + +// Returns true if the passed name can be assigned to the passed object. +// This is true if that name is not already in use and it is a valid identifier, +// ( starts with a alpha character ). +bool validateObjectName( const char *name, const SimObject *object ); + +#endif // _EDITORFUNCTIONS_H_ \ No newline at end of file diff --git a/Engine/source/gui/editor/guiDebugger.cpp b/Engine/source/gui/editor/guiDebugger.cpp new file mode 100644 index 000000000..625704bee --- /dev/null +++ b/Engine/source/gui/editor/guiDebugger.cpp @@ -0,0 +1,705 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/editor/guiDebugger.h" + +#include "gui/core/guiCanvas.h" +#include "gfx/gfxDrawUtil.h" +#include "core/volume.h" + + +IMPLEMENT_CONOBJECT(DbgFileView); + +ConsoleDocClass( DbgFileView, + "@brief Remnant from ancient script debugger (TGE days?)\n\n" + "Possibly useful for an editor tooltip.\n\n" + "@internal" +); + +static const char* itoa(S32 i) +{ + static char buf[32]; + dSprintf(buf, sizeof(buf), "%d", i); + return buf; +} + +DbgFileView::~DbgFileView() +{ + clear(); +} + +DbgFileView::DbgFileView() +{ + VECTOR_SET_ASSOCIATION(mFileView); + + mFileName = NULL; + mPCFileName = NULL; + mPCCurrentLine = -1; + + mBlockStart = -1; + mBlockEnd = -1; + + mFindString[0] = '\0'; + mFindLineNumber = -1; + + mSize.set(1, 0); +} + +ConsoleMethod(DbgFileView, setCurrentLine, void, 4, 4, "(int line, bool selected)" + "Set the current highlighted line.") +{ + object->setCurrentLine(dAtoi(argv[2]), dAtob(argv[3])); +} + +ConsoleMethod(DbgFileView, getCurrentLine, const char *, 2, 2, "()" + "Get the currently executing file and line, if any.\n\n" + "@returns A string containing the file, a tab, and then the line number." + " Use getField() with this.") +{ + S32 lineNum; + const char *file = object->getCurrentLine(lineNum); + char* ret = Con::getReturnBuffer(256); + dSprintf(ret, sizeof(ret), "%s\t%d", file, lineNum); + return ret; +} + +ConsoleMethod(DbgFileView, open, bool, 3, 3, "(string filename)" + "Open a file for viewing.\n\n" + "@note This loads the file from the local system.") +{ + return object->openFile(argv[2]); +} + +ConsoleMethod(DbgFileView, clearBreakPositions, void, 2, 2, "()" + "Clear all break points in the current file.") +{ + object->clearBreakPositions(); +} + +ConsoleMethod(DbgFileView, setBreakPosition, void, 3, 3, "(int line)" + "Set a breakpoint at the specified line.") +{ + object->setBreakPosition(dAtoi(argv[2])); +} + +ConsoleMethod(DbgFileView, setBreak, void, 3, 3, "(int line)" + "Set a breakpoint at the specified line.") +{ + object->setBreakPointStatus(dAtoi(argv[2]), true); +} + +ConsoleMethod(DbgFileView, removeBreak, void, 3, 3, "(int line)" + "Remove a breakpoint from the specified line.") +{ + object->setBreakPointStatus(dAtoi(argv[2]), false); +} + +ConsoleMethod(DbgFileView, findString, bool, 3, 3, "(string findThis)" + "Find the specified string in the currently viewed file and " + "scroll it into view.") +{ + return object->findString(argv[2]); +} + +//this value is the offset used in the ::onRender() method... +static S32 gFileXOffset = 44; + +void DbgFileView::AdjustCellSize() +{ + if (! bool(mFont)) + return; + S32 maxWidth = 1; + for (U32 i = 0; i < mFileView.size(); i++) + { + S32 cellWidth = gFileXOffset + mFont->getStrWidth((const UTF8 *)mFileView[i].text); + maxWidth = getMax(maxWidth, cellWidth); + } + + mCellSize.set(maxWidth, mFont->getHeight() + 2); + setSize(mSize); +} + +bool DbgFileView::onWake() +{ + if (! Parent::onWake()) + return false; + + //clear the mouse over var + mMouseOverVariable[0] = '\0'; + mbMouseDragging = false; + + //adjust the cellwidth to the maximum line length + AdjustCellSize(); + mSize.set(1, mFileView.size()); + + return true; +} + +void DbgFileView::addLine(const char *string, U32 strLen) +{ + // first compute the size + U32 size = 1; // for null + for(U32 i = 0; i < strLen; i++) + { + if(string[i] == '\t') + size += 3; + else if(string[i] != '\r') + size++; + } + FileLine fl; + fl.breakPosition = false; + fl.breakOnLine = false; + if(size) + { + fl.text = (char *) dMalloc(size); + + U32 dstIndex = 0; + for(U32 i = 0; i < strLen; i++) + { + if(string[i] == '\t') + { + fl.text[dstIndex] = ' '; + fl.text[dstIndex + 1] = ' '; + fl.text[dstIndex + 2] = ' '; + dstIndex += 3; + } + else if(string[i] != '\r') + fl.text[dstIndex++] = string[i]; + } + fl.text[dstIndex] = 0; + } + else + fl.text = NULL; + mFileView.push_back(fl); +} + +void DbgFileView::clear() +{ + for(Vector::iterator i = mFileView.begin(); i != mFileView.end(); i++) + dFree(i->text); + mFileView.clear(); +} + +bool DbgFileView::findString(const char *text) +{ + //make sure we have a valid string to find + if (!text || !text[0]) + return false; + + //see which line we start searching from + S32 curLine = 0; + bool searchAgain = false; + if (mFindLineNumber >= 0 && !dStricmp(mFindString, text)) + { + searchAgain = true; + curLine = mFindLineNumber; + } + else + mFindLineNumber = -1; + + //copy the search text + dStrncpy(mFindString, text, 255); + S32 length = dStrlen(mFindString); + + //loop through looking for the next instance + while (curLine < mFileView.size()) + { + char *curText; + if (curLine == mFindLineNumber && mBlockStart >= 0) + curText = &mFileView[curLine].text[mBlockStart + 1]; + else + curText = &mFileView[curLine].text[0]; + + //search for the string (the hard way... - apparently dStrupr is broken... + char *found = NULL; + char *curTextPtr = curText; + while (*curTextPtr != '\0') + { + if (!dStrnicmp(mFindString, curTextPtr, length)) + { + found = curTextPtr; + break; + } + else + curTextPtr++; + } + + //did we find it? + if (found) + { + //scroll first + mFindLineNumber = curLine; + scrollToLine(mFindLineNumber + 1); + + //then hilite + mBlockStart = (S32)(found - &mFileView[curLine].text[0]); + mBlockEnd = mBlockStart + length; + + return true; + } + else + curLine++; + } + + //didn't find anything - reset the vars for the next search + mBlockStart = -1; + mBlockEnd = -1; + mFindLineNumber = -1; + + setSelectedCell(Point2I(-1, -1)); + return false; +} + +void DbgFileView::setCurrentLine(S32 lineNumber, bool setCurrentLine) +{ + //update the line number + if (setCurrentLine) + { + mPCFileName = mFileName; + mPCCurrentLine = lineNumber; + mBlockStart = -1; + mBlockEnd = -1; + if (lineNumber >= 0) + scrollToLine(mPCCurrentLine); + } + else + { + scrollToLine(lineNumber); + } +} + +const char* DbgFileView::getCurrentLine(S32 &lineNumber) +{ + lineNumber = mPCCurrentLine; + return mPCFileName; +} + +bool DbgFileView::openFile(const char *fileName) +{ + if ((! fileName) || (! fileName[0])) + return false; + + StringTableEntry newFile = StringTable->insert(fileName); + if (mFileName == newFile) + return true; + + void *data = NULL; + U32 dataSize = 0; + Torque::FS::ReadFile(fileName, data, dataSize, true); + if(data == NULL) + { + Con::printf("DbgFileView: unable to open file %s.", fileName); + return false; + } + + //copy the file name + mFileName = newFile; + + //clear the old mFileView + clear(); + setSize(Point2I(1, 0)); + + //begin reading and parsing at each '\n' + char *parsePtr = (char *)data; + for(;;) { + char *tempPtr = dStrchr(parsePtr, '\n'); + if(tempPtr) + addLine(parsePtr, tempPtr - parsePtr); + else if(parsePtr[0]) + addLine(parsePtr, dStrlen(parsePtr)); + if(!tempPtr) + break; + parsePtr = tempPtr + 1; + } + //delete the buffer + delete [] static_cast(data); + + //set the file size + AdjustCellSize(); + setSize(Point2I(1, mFileView.size())); + + return true; +} + +void DbgFileView::scrollToLine(S32 lineNumber) +{ + GuiControl *parent = getParent(); + if (! parent) + return; + + S32 yOffset = (lineNumber - 1) * mCellSize.y; + + //see if the line is already visible + if (! (yOffset + getPosition().y >= 0 && yOffset + getPosition().y < parent->getHeight() - mCellSize.y)) + { + //reposition the control + S32 newYOffset = getMin(0, getMax(parent->getHeight() - getHeight(), (mCellSize.y * 4) - yOffset)); + setPosition(Point2I(getPosition().x, newYOffset)); + } + + //hilite the line + cellSelected(Point2I(0, lineNumber - 1)); +} + +S32 DbgFileView::findMouseOverChar(const char *text, S32 stringPosition) +{ + static char tempBuf[512]; + char *bufPtr = &tempBuf[1]; + + // Handle the empty string correctly. + if (text[0] == '\0') { + return -1; + } + + // Copy the line's text into the scratch buffer. + dStrncpy(tempBuf, text, 512); + + // Make sure we have a null terminator. + tempBuf[511] = '\0'; + + // Loop over the characters... + bool found = false; + bool finished = false; + do { + // Note the current character, then replace it with EOL. + char c = *bufPtr; + *bufPtr = '\0'; + // Is the resulting string long enough to include the current + // mouse position? + if ((S32)mFont->getStrWidth((const UTF8 *)tempBuf) > stringPosition) { + // Yep. + // Restore the character. + *bufPtr = c; + // Set bufPtr to point to the char under the mouse. + bufPtr--; + // Bail. + found = true; + finished = true; + } + else { + // Nope. + // Restore the character. + *bufPtr = c; + // Move on to the next character. + bufPtr++; + // Bail if EOL. + if (*bufPtr == '\0') finished = true; + } + } while (!finished); + + // Did we find a character under the mouse? + if (found) { + // If so, return its position. + return bufPtr - tempBuf; + } + // If not, return -1. + else return -1; +} + +bool DbgFileView::findMouseOverVariable() +{ + GuiCanvas *root = getRoot(); + AssertFatal(root, "Unable to get the root Canvas."); + + Point2I curMouse = root->getCursorPos(); + Point2I pt = globalToLocalCoord(curMouse); + + //find out which cell was hit + Point2I cell((pt.x < 0 ? -1 : pt.x / mCellSize.x), (pt.y < 0 ? -1 : pt.y / mCellSize.y)); + if(cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y) + { + S32 stringPosition = pt.x - gFileXOffset; + char tempBuf[256], *varNamePtr = &tempBuf[1]; + dStrcpy(tempBuf, mFileView[cell.y].text); + + //find the current mouse over char + S32 charNum = findMouseOverChar(mFileView[cell.y].text, stringPosition); + if (charNum >= 0) + { + varNamePtr = &tempBuf[charNum]; + } + else + { + mMouseOverVariable[0] = '\0'; + mMouseOverValue[0] = '\0'; + return false; + } + + //now make sure we can go from the current cursor mPosition to the beginning of a var name + bool found = false; + while (varNamePtr >= &tempBuf[0]) + { + if (*varNamePtr == '%' || *varNamePtr == '$') + { + found = true; + break; + } + else if ((dToupper(*varNamePtr) >= 'A' && dToupper(*varNamePtr) <= 'Z') || + (*varNamePtr >= '0' && *varNamePtr <= '9') || *varNamePtr == '_' || *varNamePtr == ':') + { + varNamePtr--; + } + else + { + break; + } + } + + //mouse wasn't over a possible variable name + if (! found) + { + mMouseOverVariable[0] = '\0'; + mMouseOverValue[0] = '\0'; + return false; + } + + //set the var char start positions + mMouseVarStart = varNamePtr - tempBuf; + + //now copy the (possible) var name into the buf + char *tempPtr = &mMouseOverVariable[0]; + + //copy the leading '%' or '$' + *tempPtr++ = *varNamePtr++; + + //now copy letters and numbers until the end of the name + while ((dToupper(*varNamePtr) >= 'A' && dToupper(*varNamePtr) <= 'Z') || + (*varNamePtr >= '0' && *varNamePtr <= '9') || *varNamePtr == '_' || *varNamePtr == ':') + { + *tempPtr++ = *varNamePtr++; + } + *tempPtr = '\0'; + + //set the var char end positions + mMouseVarEnd = varNamePtr - tempBuf; + + return true; + } + return false; +} + +void DbgFileView::clearBreakPositions() +{ + for(Vector::iterator i = mFileView.begin(); i != mFileView.end(); i++) + { + i->breakPosition = false; + i->breakOnLine = false; + } +} + +void DbgFileView::setBreakPosition(U32 line) +{ + if(line > mFileView.size()) + return; + mFileView[line-1].breakPosition = true; +} + +void DbgFileView::setBreakPointStatus(U32 line, bool set) +{ + if(line > mFileView.size()) + return; + mFileView[line-1].breakOnLine = set; +} + +void DbgFileView::onPreRender() +{ + setUpdate(); + char oldVar[256]; + dStrcpy(oldVar, mMouseOverVariable); + bool found = findMouseOverVariable(); + if (found && mPCCurrentLine >= 0) + { + //send the query only when the var changes + if (dStricmp(oldVar, mMouseOverVariable)) + Con::executef("DbgSetCursorWatch", mMouseOverVariable); + } + else + Con::executef("DbgSetCursorWatch", ""); +} + +void DbgFileView::onMouseDown(const GuiEvent &event) +{ + if (! mActive) + { + Parent::onMouseDown(event); + return; + } + + Point2I pt = globalToLocalCoord(event.mousePoint); + bool doubleClick = (event.mouseClickCount > 1); + + //find out which cell was hit + Point2I cell((pt.x < 0 ? -1 : pt.x / mCellSize.x), (pt.y < 0 ? -1 : pt.y / mCellSize.y)); + if(cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y) + { + //if we clicked on the breakpoint mark + if (pt.x >= 0 && pt.x <= 12) + { + //toggle the break point + if (mFileView[cell.y].breakPosition) + { + if (mFileView[cell.y].breakOnLine) + Con::executef(this, "onRemoveBreakPoint", itoa(cell.y + 1)); + else + Con::executef(this, "onSetBreakPoint", itoa(cell.y + 1)); + } + } + else + { + Point2I prevSelected = mSelectedCell; + Parent::onMouseDown(event); + mBlockStart= -1; + mBlockEnd = -1; + + //open the file view + if (mSelectedCell.y == prevSelected.y && doubleClick && mMouseOverVariable[0]) + { + Con::executef(this, "onSetWatch", mMouseOverVariable); + mBlockStart = mMouseVarStart; + mBlockEnd = mMouseVarEnd; + } + else + { + S32 stringPosition = pt.x - gFileXOffset; + + //find which character we're over + S32 charNum = findMouseOverChar(mFileView[mSelectedCell.y].text, stringPosition); + if (charNum >= 0) + { + //lock the mouse + mouseLock(); + setFirstResponder(); + + //set the block hilite start and end + mbMouseDragging = true; + mMouseDownChar = charNum; + } + } + } + } + else + { + Parent::onMouseDown(event); + } +} + +void DbgFileView::onMouseDragged(const GuiEvent &event) +{ + if (mbMouseDragging) + { + Point2I pt = globalToLocalCoord(event.mousePoint); + S32 stringPosition = pt.x - gFileXOffset; + + //find which character we're over + S32 charNum = findMouseOverChar(mFileView[mSelectedCell.y].text, stringPosition); + if (charNum >= 0) + { + if (charNum < mMouseDownChar) + { + + mBlockEnd = mMouseDownChar + 1; + mBlockStart = charNum; + } + else + { + mBlockEnd = charNum + 1; + mBlockStart = mMouseDownChar; + } + } + + //otherwise, the cursor is past the end of the string + else + { + mBlockStart = mMouseDownChar; + mBlockEnd = dStrlen(mFileView[mSelectedCell.y].text) + 1; + } + } +} + +void DbgFileView::onMouseUp(const GuiEvent &) +{ + //unlock the mouse + mouseUnlock(); + + mbMouseDragging = false; +} + +void DbgFileView::onRenderCell(Point2I offset, Point2I cell, bool selected, bool) +{ + Point2I cellOffset = offset; + cellOffset.x += 4; + + //draw the break point marks + if (mFileView[cell.y].breakOnLine) + { + GFX->getDrawUtil()->setBitmapModulation(mProfile->mFontColorHL); + GFX->getDrawUtil()->drawText(mFont, cellOffset, "#"); + } + else if (mFileView[cell.y].breakPosition) + { + GFX->getDrawUtil()->setBitmapModulation(mProfile->mFontColor); + GFX->getDrawUtil()->drawText(mFont, cellOffset, "-"); + } + cellOffset.x += 8; + + //draw in the "current line" indicator + if (mFileName == mPCFileName && (cell.y + 1 == mPCCurrentLine)) + { + GFX->getDrawUtil()->setBitmapModulation(mProfile->mFontColorHL); + GFX->getDrawUtil()->drawText(mFont, cellOffset, "=>"); + } + + //by this time, the cellOffset has been incremented by 44 - the value of gFileXOffset + cellOffset.x += 32; + + //hilite the line if selected + if (selected) + { + if (mBlockStart == -1) + { + GFX->getDrawUtil()->drawRectFill(RectI(cellOffset.x - 2, cellOffset.y - 3, + mCellSize.x + 4, mCellSize.y + 6), mProfile->mFillColorHL); + } + else if (mBlockStart >= 0 && mBlockEnd > mBlockStart && mBlockEnd <= S32(dStrlen(mFileView[cell.y].text) + 1)) + { + S32 startPos, endPos; + char tempBuf[256]; + dStrcpy(tempBuf, mFileView[cell.y].text); + + //get the end coord + tempBuf[mBlockEnd] = '\0'; + endPos = mFont->getStrWidth((const UTF8 *)tempBuf); + + //get the start coord + tempBuf[mBlockStart] = '\0'; + startPos = mFont->getStrWidth((const UTF8 *)tempBuf); + + //draw the hilite + GFX->getDrawUtil()->drawRectFill(RectI(cellOffset.x + startPos, cellOffset.y - 3, endPos - startPos + 2, mCellSize.y + 6), mProfile->mFillColorHL); + } + } + + //draw the line of text + GFX->getDrawUtil()->setBitmapModulation(mFileView[cell.y].breakOnLine ? mProfile->mFontColorHL : mProfile->mFontColor); + GFX->getDrawUtil()->drawText(mFont, cellOffset, mFileView[cell.y].text); +} diff --git a/Engine/source/gui/editor/guiDebugger.h b/Engine/source/gui/editor/guiDebugger.h new file mode 100644 index 000000000..9255cc4e9 --- /dev/null +++ b/Engine/source/gui/editor/guiDebugger.h @@ -0,0 +1,101 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIDEBUGGER_H_ +#define _GUIDEBUGGER_H_ + +#ifndef _GUIARRAYCTRL_H_ +#include "gui/core/guiArrayCtrl.h" +#endif + +class DbgFileView : public GuiArrayCtrl +{ + private: + + typedef GuiArrayCtrl Parent; + + struct FileLine + { + bool breakPosition; + bool breakOnLine; + char *text; + }; + + Vector mFileView; + + StringTableEntry mFileName; + + void AdjustCellSize(); + + //used to display the program counter + StringTableEntry mPCFileName; + S32 mPCCurrentLine; + + //vars used to highlight the selected line segment for copying + bool mbMouseDragging; + S32 mMouseDownChar; + S32 mBlockStart; + S32 mBlockEnd; + + char mMouseOverVariable[256]; + char mMouseOverValue[256]; + S32 findMouseOverChar(const char *text, S32 stringPosition); + bool findMouseOverVariable(); + S32 mMouseVarStart; + S32 mMouseVarEnd; + + //find vars + char mFindString[256]; + S32 mFindLineNumber; + + public: + + DbgFileView(); + ~DbgFileView(); + + DECLARE_CONOBJECT(DbgFileView); + DECLARE_CATEGORY( "Gui Editor" ); + + bool onWake(); + + void clear(); + void clearBreakPositions(); + + void setCurrentLine(S32 lineNumber, bool setCurrentLine); + const char *getCurrentLine(S32 &lineNumber); + bool openFile(const char *fileName); + void scrollToLine(S32 lineNumber); + void setBreakPointStatus(U32 lineNumber, bool value); + void setBreakPosition(U32 line); + void addLine(const char *text, U32 textLen); + + bool findString(const char *text); + + void onMouseDown(const GuiEvent &event); + void onMouseDragged(const GuiEvent &event); + void onMouseUp(const GuiEvent &event); + + void onPreRender(); + void onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver); +}; + +#endif //_GUI_DEBUGGER_H diff --git a/Engine/source/gui/editor/guiEaseViewCtrl.cpp b/Engine/source/gui/editor/guiEaseViewCtrl.cpp new file mode 100644 index 000000000..87dff9d5e --- /dev/null +++ b/Engine/source/gui/editor/guiEaseViewCtrl.cpp @@ -0,0 +1,113 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/editor/guiEaseViewCtrl.h" +#include "console/consoleTypes.h" +#include "math/mMath.h" +#include "gfx/gfxDrawUtil.h" + + +IMPLEMENT_CONOBJECT( GuiEaseViewCtrl ); + +ConsoleDocClass( GuiEaseViewCtrl, + "@brief Control to visualize an EaseF.\n\n" + "Editor use only.\n\n" + "@see EaseF\n\n" + "@internal" +); + +//----------------------------------------------------------------------------- + +GuiEaseViewCtrl::GuiEaseViewCtrl() +{ + mEase.set(Ease::In, Ease::Cubic); + mAxisColor.set(1,1,1,1); + mEaseColor.set(1,1,1,1); + mEaseWidth = 4.0f; +} + +//----------------------------------------------------------------------------- + +void GuiEaseViewCtrl::initPersistFields() +{ + Parent::initPersistFields(); + + addField("ease", TypeEaseF, Offset( mEase, GuiEaseViewCtrl ) ); + addField("easeColor", TypeColorF, Offset( mEaseColor, GuiEaseViewCtrl ) ); + addField("easeWidth", TypeF32, Offset(mEaseWidth, GuiEaseViewCtrl ) ); + addField("axisColor", TypeColorF, Offset( mAxisColor, GuiEaseViewCtrl ) ); +} + +//----------------------------------------------------------------------------- + +bool GuiEaseViewCtrl::onWake() +{ + if (!Parent::onWake()) + return false; + setActive(true); + return true; +} + +//----------------------------------------------------------------------------- + +void GuiEaseViewCtrl::onSleep() +{ + setActive(false); + Parent::onSleep(); +} + +//----------------------------------------------------------------------------- + +void GuiEaseViewCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + const F32 W = getExtent().x; + const F32 H = getExtent().y; + const F32 plotW = W; + const F32 plotH = H; + const F32 zeroX = offset.x + 1.f; + const F32 zeroY = offset.y; + + // Draw axis. + + GFX->getDrawUtil()->drawLine( zeroX, zeroY + 0.0f, zeroX, zeroY + plotH, mAxisColor ); + GFX->getDrawUtil()->drawLine( zeroX, zeroY + plotH, zeroX + plotW, zeroY + plotH, mAxisColor ); + + F32 numPoints = W; + F32 lastX = zeroX; + F32 lastY = zeroY + plotH; + + // Draw curve. + + for( int i = 1; i <= numPoints; ++ i ) + { + F32 x = ( F32 ) i / ( F32 ) numPoints; + F32 y = mEase.getValue( x, 0, 1, 1 ); + + x = zeroX + x * plotW; + y = zeroY + plotH - y * plotH; + + GFX->getDrawUtil()->drawLine( lastX, lastY, x, y, mEaseColor ); + + lastX = x; + lastY = y; + } +} diff --git a/Engine/source/gui/editor/guiEaseViewCtrl.h b/Engine/source/gui/editor/guiEaseViewCtrl.h new file mode 100644 index 000000000..612ed4b14 --- /dev/null +++ b/Engine/source/gui/editor/guiEaseViewCtrl.h @@ -0,0 +1,63 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIEASEVIEWCTRL_H_ +#define _GUIEASEVIEWCTRL_H_ + +#ifndef _GUICONTROL_H_ + #include "gui/core/guiControl.h" +#endif +#ifndef _MEASE_H_ + #include "math/mEase.h" +#endif + + +/// Control to visualize an EaseF. +class GuiEaseViewCtrl : public GuiControl +{ + public: + + typedef GuiControl Parent; + + protected: + + EaseF mEase; // ease we are visualizing + ColorF mAxisColor; // color to draw axis in + ColorF mEaseColor; // color to draw ease in + F32 mEaseWidth; // width of lines + + public: + + GuiEaseViewCtrl(); + + bool onWake(); + void onSleep(); + + void onRender( Point2I, const RectI &); + static void initPersistFields(); + + DECLARE_CONOBJECT( GuiEaseViewCtrl ); + DECLARE_CATEGORY( "Gui Editor" ); + DECLARE_DESCRIPTION( "Control that display an EaseF curve." ); +}; + +#endif // !_GUIEASEVIEWCTRL_H_ diff --git a/Engine/source/gui/editor/guiEditCtrl.cpp b/Engine/source/gui/editor/guiEditCtrl.cpp new file mode 100644 index 000000000..584c9b9e2 --- /dev/null +++ b/Engine/source/gui/editor/guiEditCtrl.cpp @@ -0,0 +1,2967 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/editor/guiEditCtrl.h" + +#include "core/frameAllocator.h" +#include "core/stream/fileStream.h" +#include "core/stream/memStream.h" +#include "console/consoleTypes.h" +#include "gui/core/guiCanvas.h" +#include "gui/containers/guiScrollCtrl.h" +#include "core/strings/stringUnit.h" +#include "console/engineAPI.h" + + +IMPLEMENT_CONOBJECT( GuiEditCtrl ); + +ConsoleDocClass( GuiEditCtrl, + "@brief Native side of the GUI editor.\n\n" + "Editor use only.\n\n" + "@internal" +); + +IMPLEMENT_CALLBACK( GuiEditCtrl, onHierarchyChanged, void, (), (), + "" ); +IMPLEMENT_CALLBACK( GuiEditCtrl, onDelete, void, (), (), + "" ); +IMPLEMENT_CALLBACK( GuiEditCtrl, onPreEdit, void, ( SimSet* selection ), ( selection ), + "" ); +IMPLEMENT_CALLBACK( GuiEditCtrl, onPostEdit, void, ( SimSet* selection ), ( selection ), + "" ); +IMPLEMENT_CALLBACK( GuiEditCtrl, onClearSelected, void, (), (), + "" ) +IMPLEMENT_CALLBACK( GuiEditCtrl, onSelect, void, ( GuiControl* control ), ( control ), + "" ); +IMPLEMENT_CALLBACK( GuiEditCtrl, onAddSelected, void, ( GuiControl* control ), ( control ), + "" ); +IMPLEMENT_CALLBACK( GuiEditCtrl, onRemoveSelected, void, ( GuiControl* control ), ( control ), + "" ); +IMPLEMENT_CALLBACK( GuiEditCtrl, onPreSelectionNudged, void, ( SimSet* selection ), ( selection ), + "" ); +IMPLEMENT_CALLBACK( GuiEditCtrl, onPostSelectionNudged, void, ( SimSet* selection ), ( selection ), + "" ); +IMPLEMENT_CALLBACK( GuiEditCtrl, onSelectionMoved, void, ( GuiControl* control ), ( control ), + "" ); +IMPLEMENT_CALLBACK( GuiEditCtrl, onSelectionCloned, void, ( SimSet* selection ), ( selection ), + "" ); +IMPLEMENT_CALLBACK( GuiEditCtrl, onTrashSelection, void, ( SimSet* selection ), ( selection ), + "" ); +IMPLEMENT_CALLBACK( GuiEditCtrl, onAddNewCtrl, void, ( GuiControl* control ), ( control ), + "" ); +IMPLEMENT_CALLBACK( GuiEditCtrl, onAddNewCtrlSet, void, ( SimSet* set ), ( set ), + "" ); +IMPLEMENT_CALLBACK( GuiEditCtrl, onSelectionResized, void, ( GuiControl* control ), ( control ), + "" ); +IMPLEMENT_CALLBACK( GuiEditCtrl, onFitIntoParent, void, ( bool width, bool height ), ( width, height ), + "" ); +IMPLEMENT_CALLBACK( GuiEditCtrl, onMouseModeChange, void, (), (), + "" ); +IMPLEMENT_CALLBACK( GuiEditCtrl, onControlInspectPreApply, void, ( GuiControl* control ), ( control ), + "" ); +IMPLEMENT_CALLBACK( GuiEditCtrl, onControlInspectPostApply, void, ( GuiControl* control ), ( control ), + "" ); + + +StringTableEntry GuiEditCtrl::smGuidesPropertyName[ 2 ]; + + +//----------------------------------------------------------------------------- + +GuiEditCtrl::GuiEditCtrl() + : mCurrentAddSet( NULL ), + mContentControl( NULL ), + mGridSnap( 0, 0 ), + mDragBeginPoint( -1, -1 ), + mSnapToControls( true ), + mSnapToEdges( true ), + mSnapToCenters( true ), + mSnapToGuides( true ), + mSnapToCanvas( true ), + mSnapSensitivity( 2 ), + mFullBoxSelection( false ), + mDrawBorderLines( true ), + mDrawGuides( true ) +{ + VECTOR_SET_ASSOCIATION( mSelectedControls ); + VECTOR_SET_ASSOCIATION( mDragBeginPoints ); + VECTOR_SET_ASSOCIATION( mSnapHits[ 0 ] ); + VECTOR_SET_ASSOCIATION( mSnapHits[ 1 ] ); + + mActive = true; + mDotSB = NULL; + + mSnapped[ SnapVertical ] = false; + mSnapped[ SnapHorizontal ] = false; + + mDragGuide[ GuideVertical ] = false; + mDragGuide[ GuideHorizontal ] = false; + + if( !smGuidesPropertyName[ GuideVertical ] ) + smGuidesPropertyName[ GuideVertical ] = StringTable->insert( "guidesVertical" ); + if( !smGuidesPropertyName[ GuideHorizontal ] ) + smGuidesPropertyName[ GuideHorizontal ] = StringTable->insert( "guidesHorizontal" ); +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::initPersistFields() +{ + addGroup( "Snapping" ); + addField( "snapToControls", TypeBool, Offset( mSnapToControls, GuiEditCtrl ), + "If true, edge and center snapping will work against controls." ); + addField( "snapToGuides", TypeBool, Offset( mSnapToGuides, GuiEditCtrl ), + "If true, edge and center snapping will work against guides." ); + addField( "snapToCanvas", TypeBool, Offset( mSnapToCanvas, GuiEditCtrl ), + "If true, edge and center snapping will work against canvas (toplevel control)." ); + addField( "snapToEdges", TypeBool, Offset( mSnapToEdges, GuiEditCtrl ), + "If true, selection edges will snap into alignment when moved or resized." ); + addField( "snapToCenters", TypeBool, Offset( mSnapToCenters, GuiEditCtrl ), + "If true, selection centers will snap into alignment when moved or resized." ); + addField( "snapSensitivity", TypeS32, Offset( mSnapSensitivity, GuiEditCtrl ), + "Distance in pixels that edge and center snapping will work across." ); + endGroup( "Snapping" ); + + addGroup( "Selection" ); + addField( "fullBoxSelection", TypeBool, Offset( mFullBoxSelection, GuiEditCtrl ), + "If true, rectangle selection will only select controls fully inside the drag rectangle." ); + endGroup( "Selection" ); + + addGroup( "Rendering" ); + addField( "drawBorderLines", TypeBool, Offset( mDrawBorderLines, GuiEditCtrl ), + "If true, lines will be drawn extending along the edges of selected objects." ); + addField( "drawGuides", TypeBool, Offset( mDrawGuides, GuiEditCtrl ), + "If true, guides will be included in rendering." ); + endGroup( "Rendering" ); + + Parent::initPersistFields(); +} + +//============================================================================= +// Events. +//============================================================================= +// MARK: ---- Events ---- + +//----------------------------------------------------------------------------- + +bool GuiEditCtrl::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + mTrash = new SimGroup(); + mSelectedSet = new SimSet(); + + if( !mTrash->registerObject() ) + return false; + if( !mSelectedSet->registerObject() ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::onRemove() +{ + Parent::onRemove(); + + mDotSB = NULL; + + mTrash->deleteObject(); + mSelectedSet->deleteObject(); + + mTrash = NULL; + mSelectedSet = NULL; +} + +//----------------------------------------------------------------------------- + +bool GuiEditCtrl::onWake() +{ + if (! Parent::onWake()) + return false; + + // Set GUI Controls to DesignTime mode + GuiControl::smDesignTime = true; + GuiControl::smEditorHandle = this; + + setEditMode(true); + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::onSleep() +{ + // Set GUI Controls to run time mode + GuiControl::smDesignTime = false; + GuiControl::smEditorHandle = NULL; + + Parent::onSleep(); +} + +//----------------------------------------------------------------------------- + +bool GuiEditCtrl::onKeyDown(const GuiEvent &event) +{ + if (! mActive) + return Parent::onKeyDown(event); + + if (!(event.modifier & SI_PRIMARY_CTRL)) + { + switch(event.keyCode) + { + case KEY_BACKSPACE: + case KEY_DELETE: + deleteSelection(); + onDelete_callback(); + return true; + default: + break; + } + } + return false; +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::onMouseDown(const GuiEvent &event) +{ + if (! mActive) + { + Parent::onMouseDown(event); + return; + } + if(!mContentControl) + return; + + setFirstResponder(); + mouseLock(); + + mLastMousePos = globalToLocalCoord( event.mousePoint ); + + // Check whether we've hit a guide. If so, start a guide drag. + // Don't do this if SHIFT is down. + + if( !( event.modifier & SI_SHIFT ) ) + { + for( U32 axis = 0; axis < 2; ++ axis ) + { + const S32 guide = findGuide( ( guideAxis ) axis, event.mousePoint, 1 ); + if( guide != -1 ) + { + setMouseMode( DragGuide ); + + mDragGuide[ axis ] = true; + mDragGuideIndex[ axis ] = guide; + } + } + + if( mMouseDownMode == DragGuide ) + return; + } + + // Check whether we have hit a sizing knob on any of the currently selected + // controls. + + for( U32 i = 0, num = mSelectedControls.size(); i < num; ++ i ) + { + GuiControl* ctrl = mSelectedControls[ i ]; + + Point2I cext = ctrl->getExtent(); + Point2I ctOffset = globalToLocalCoord( ctrl->localToGlobalCoord( Point2I( 0, 0 ) ) ); + + RectI box( ctOffset.x, ctOffset.y, cext.x, cext.y ); + + if( ( mSizingMode = ( GuiEditCtrl::sizingModes ) getSizingHitKnobs( mLastMousePos, box ) ) != 0 ) + { + setMouseMode( SizingSelection ); + mLastDragPos = event.mousePoint; + + // undo + onPreEdit_callback( getSelectedSet() ); + return; + } + } + + // Find the control we have hit. + + GuiControl* ctrl = mContentControl->findHitControl( mLastMousePos, getCurrentAddSet()->mLayer ); + + // Give the control itself the opportunity to handle the event + // to implement custom editing logic. + + bool handledEvent = ctrl->onMouseDownEditor( event, localToGlobalCoord( Point2I(0,0) ) ); + if( handledEvent == true ) + { + // The Control handled the event and requested the edit ctrl + // *NOT* act on it. + return; + } + else if( event.modifier & SI_SHIFT ) + { + // Shift is down. Start rectangle selection in add mode + // no matter what we have hit. + + startDragRectangle( event.mousePoint ); + mDragAddSelection = true; + } + else if( selectionContains( ctrl ) ) + { + // We hit a selected control. If the multiselect key is pressed, + // deselect the control. Otherwise start a drag move. + + if( event.modifier & SI_MULTISELECT ) + { + removeSelection( ctrl ); + + //set the mode + setMouseMode( Selecting ); + } + else if( event.modifier & SI_PRIMARY_ALT ) + { + // Alt is down. Start a drag clone. + + startDragClone( event.mousePoint ); + } + else + { + startDragMove( event.mousePoint ); + } + } + else + { + // We clicked an unselected control. + + if( ctrl == getContentControl() ) + { + // Clicked in toplevel control. Start a rectangle selection. + + startDragRectangle( event.mousePoint ); + mDragAddSelection = false; + } + else if( event.modifier & SI_PRIMARY_ALT && ctrl != getContentControl() ) + { + // Alt is down. Start a drag clone. + + clearSelection(); + addSelection( ctrl ); + + startDragClone( event.mousePoint ); + } + else if( event.modifier & SI_MULTISELECT ) + addSelection( ctrl ); + else + { + // Clicked on child control. Start move. + + clearSelection(); + addSelection( ctrl ); + + startDragMove( event.mousePoint ); + } + } +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::onMouseUp(const GuiEvent &event) +{ + if (! mActive || !mContentControl || !getCurrentAddSet() ) + { + Parent::onMouseUp(event); + return; + } + + //find the control we clicked + GuiControl *ctrl = mContentControl->findHitControl(mLastMousePos, getCurrentAddSet()->mLayer); + + bool handledEvent = ctrl->onMouseUpEditor( event, localToGlobalCoord( Point2I(0,0) ) ); + if( handledEvent == true ) + { + // The Control handled the event and requested the edit ctrl + // *NOT* act on it. The dude abides. + return; + } + + //unlock the mouse + mouseUnlock(); + + // Reset Drag Axis Alignment Information + mDragBeginPoint.set(-1,-1); + mDragBeginPoints.clear(); + + mLastMousePos = globalToLocalCoord(event.mousePoint); + if( mMouseDownMode == DragGuide ) + { + // Check to see if the mouse has moved off the canvas. If so, + // remove the guides being dragged. + + for( U32 axis = 0; axis < 2; ++ axis ) + if( mDragGuide[ axis ] && !getContentControl()->getGlobalBounds().pointInRect( event.mousePoint ) ) + mGuides[ axis ].erase( mDragGuideIndex[ axis ] ); + } + else if( mMouseDownMode == DragSelecting ) + { + // If not multiselecting, clear the current selection. + + if( !( event.modifier & SI_MULTISELECT ) && !mDragAddSelection ) + clearSelection(); + + RectI rect; + getDragRect( rect ); + + // If the region is somewhere less than at least 2x2, count this as a + // normal, non-rectangular selection. + + if( rect.extent.x <= 2 && rect.extent.y <= 2 ) + addSelectControlAt( rect.point ); + else + { + // Use HIT_AddParentHits by default except if ALT is pressed. + // Use HIT_ParentPreventsChildHit if ALT+CTRL is pressed. + + U32 hitFlags = 0; + if( !( event.modifier & SI_PRIMARY_ALT ) ) + hitFlags |= HIT_AddParentHits; + if( event.modifier & SI_PRIMARY_ALT && event.modifier & SI_CTRL ) + hitFlags |= HIT_ParentPreventsChildHit; + + addSelectControlsInRegion( rect, hitFlags ); + } + } + else if( ctrl == getContentControl() && mMouseDownMode == Selecting ) + setCurrentAddSet( NULL, true ); + + // deliver post edit event if we've been editing + // note: paxorr: this may need to be moved earlier, if the selection has changed. + // undo + if( mMouseDownMode == SizingSelection || ( mMouseDownMode == MovingSelection && mDragMoveUndo ) ) + onPostEdit_callback( getSelectedSet() ); + + //reset the mouse mode + setFirstResponder(); + setMouseMode( Selecting ); + mSizingMode = sizingNone; + + // Clear snapping state. + + mSnapped[ SnapVertical ] = false; + mSnapped[ SnapHorizontal ] = false; + + mSnapTargets[ SnapVertical ] = NULL; + mSnapTargets[ SnapHorizontal ] = NULL; + + // Clear guide drag state. + + mDragGuide[ GuideVertical ] = false; + mDragGuide[ GuideHorizontal ] = false; +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::onMouseDragged( const GuiEvent &event ) +{ + if( !mActive || !mContentControl || !getCurrentAddSet() ) + { + Parent::onMouseDragged(event); + return; + } + + Point2I mousePoint = globalToLocalCoord( event.mousePoint ); + + //find the control we clicked + GuiControl *ctrl = mContentControl->findHitControl( mousePoint, getCurrentAddSet()->mLayer ); + + bool handledEvent = ctrl->onMouseDraggedEditor( event, localToGlobalCoord( Point2I(0,0) ) ); + if( handledEvent == true ) + { + // The Control handled the event and requested the edit ctrl + // *NOT* act on it. The dude abides. + return; + } + + // If we're doing a drag clone, see if we have crossed the move threshold. If so, + // clone the selection and switch to move mode. + + if( mMouseDownMode == DragClone ) + { + // If we haven't yet crossed the mouse delta to actually start the + // clone, check if we have now. + + S32 delta = mAbs( ( mousePoint - mDragBeginPoint ).len() ); + if( delta >= 4 ) + { + cloneSelection(); + mLastMousePos = mDragBeginPoint; + mDragMoveUndo = false; + + setMouseMode( MovingSelection ); + } + } + + if( mMouseDownMode == DragGuide ) + { + for( U32 axis = 0; axis < 2; ++ axis ) + if( mDragGuide[ axis ] ) + { + // Set the guide to the coordinate of the mouse cursor + // on the guide's axis. + + Point2I point = event.mousePoint; + point -= localToGlobalCoord( Point2I( 0, 0 ) ); + point[ axis ] = mClamp( point[ axis ], 0, getExtent()[ axis ] - 1 ); + + mGuides[ axis ][ mDragGuideIndex[ axis ] ] = point[ axis ]; + } + } + else if( mMouseDownMode == SizingSelection ) + { + // Snap the mouse cursor to grid if active. Do this on the mouse cursor so that we handle + // incremental drags correctly. + + Point2I mousePoint = event.mousePoint; + snapToGrid( mousePoint ); + + Point2I delta = mousePoint - mLastDragPos; + + // If CTRL is down, apply smart snapping. + + if( event.modifier & SI_CTRL ) + { + RectI selectionBounds = getSelectionBounds(); + + doSnapping( event, selectionBounds, delta ); + } + else + { + mSnapped[ SnapVertical ] = false; + mSnapped[ SnapHorizontal ] = false; + } + + // If ALT is down, do a move instead of a resize on the control + // knob's axis. Otherwise resize. + + if( event.modifier & SI_PRIMARY_ALT ) + { + if( !( mSizingMode & sizingLeft ) && !( mSizingMode & sizingRight ) ) + { + mSnapped[ SnapVertical ] = false; + delta.x = 0; + } + if( !( mSizingMode & sizingTop ) && !( mSizingMode & sizingBottom ) ) + { + mSnapped[ SnapHorizontal ] = false; + delta.y = 0; + } + + moveSelection( delta ); + } + else + resizeControlsInSelectionBy( delta, mSizingMode ); + + // Remember drag point. + + mLastDragPos = mousePoint; + } + else if (mMouseDownMode == MovingSelection && mSelectedControls.size()) + { + Point2I delta = mousePoint - mLastMousePos; + RectI selectionBounds = getSelectionBounds(); + + // Apply snaps. + + doSnapping( event, selectionBounds, delta ); + + //RDTODO: to me seems to be in need of revision + // Do we want to align this drag to the X and Y axes within a certain threshold? + if( event.modifier & SI_SHIFT && !( event.modifier & SI_PRIMARY_ALT ) ) + { + Point2I dragTotalDelta = event.mousePoint - localToGlobalCoord( mDragBeginPoint ); + if( dragTotalDelta.y < 10 && dragTotalDelta.y > -10 ) + { + for(S32 i = 0; i < mSelectedControls.size(); i++) + { + Point2I selCtrlPos = mSelectedControls[i]->getPosition(); + Point2I snapBackPoint( selCtrlPos.x, mDragBeginPoints[i].y); + // This is kind of nasty but we need to snap back if we're not at origin point with selection - JDD + if( selCtrlPos.y != mDragBeginPoints[i].y ) + mSelectedControls[i]->setPosition( snapBackPoint ); + } + delta.y = 0; + } + if( dragTotalDelta.x < 10 && dragTotalDelta.x > -10 ) + { + for(S32 i = 0; i < mSelectedControls.size(); i++) + { + Point2I selCtrlPos = mSelectedControls[i]->getPosition(); + Point2I snapBackPoint( mDragBeginPoints[i].x, selCtrlPos.y); + // This is kind of nasty but we need to snap back if we're not at origin point with selection - JDD + if( selCtrlPos.x != mDragBeginPoints[i].x ) + mSelectedControls[i]->setPosition( snapBackPoint ); + } + delta.x = 0; + } + } + + if( delta.x || delta.y ) + moveSelection( delta, mDragMoveUndo ); + + // find the current control under the mouse + + canHitSelectedControls( false ); + GuiControl *inCtrl = mContentControl->findHitControl(mousePoint, getCurrentAddSet()->mLayer); + canHitSelectedControls( true ); + + // find the nearest control up the heirarchy from the control the mouse is in + // that is flagged as a container. + while( !inCtrl->mIsContainer ) + inCtrl = inCtrl->getParent(); + + // if the control under the mouse is not our parent, move the selected controls + // into the new parent. + if(mSelectedControls[0]->getParent() != inCtrl && inCtrl->mIsContainer) + { + moveSelectionToCtrl( inCtrl, mDragMoveUndo ); + setCurrentAddSet( inCtrl, false ); + } + + mLastMousePos += delta; + } + else + mLastMousePos = mousePoint; +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::onRightMouseDown(const GuiEvent &event) +{ + if (! mActive || !mContentControl) + { + Parent::onRightMouseDown(event); + return; + } + setFirstResponder(); + + //search for the control hit in any layer below the edit layer + GuiControl *hitCtrl = mContentControl->findHitControl(globalToLocalCoord(event.mousePoint), mLayer - 1); + if (hitCtrl != getCurrentAddSet()) + { + setCurrentAddSet( hitCtrl ); + } + // select the parent if we right-click on the current add set + else if( getCurrentAddSet() != mContentControl) + { + setCurrentAddSet( hitCtrl->getParent() ); + select(hitCtrl); + } + + //Design time mouse events + GuiEvent designEvent = event; + designEvent.mousePoint = mLastMousePos; + hitCtrl->onRightMouseDownEditor( designEvent, localToGlobalCoord( Point2I(0,0) ) ); + +} + +//============================================================================= +// Rendering. +//============================================================================= +// MARK: ---- Rendering ---- + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::onPreRender() +{ + setUpdate(); +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + Point2I ctOffset; + Point2I cext; + bool keyFocused = isFirstResponder(); + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + + if (mActive) + { + if( getCurrentAddSet() != getContentControl() ) + { + // draw a white frame inset around the current add set. + cext = getCurrentAddSet()->getExtent(); + ctOffset = getCurrentAddSet()->localToGlobalCoord(Point2I(0,0)); + RectI box(ctOffset.x, ctOffset.y, cext.x, cext.y); + + box.inset( -5, -5 ); + drawer->drawRect( box, ColorI( 50, 101, 152, 128 ) ); + box.inset( 1, 1 ); + drawer->drawRect( box, ColorI( 50, 101, 152, 128 ) ); + box.inset( 1, 1 ); + drawer->drawRect( box, ColorI( 50, 101, 152, 128 ) ); + box.inset( 1, 1 ); + drawer->drawRect( box, ColorI( 50, 101, 152, 128 ) ); + box.inset( 1, 1 ); + drawer->drawRect( box, ColorI( 50, 101, 152, 128 ) ); + } + Vector::iterator i; + bool multisel = mSelectedControls.size() > 1; + for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) + { + GuiControl *ctrl = (*i); + cext = ctrl->getExtent(); + ctOffset = ctrl->localToGlobalCoord(Point2I(0,0)); + RectI box(ctOffset.x,ctOffset.y, cext.x, cext.y); + ColorI nutColor = multisel ? ColorI( 255, 255, 255, 100 ) : ColorI( 0, 0, 0, 100 ); + ColorI outlineColor = multisel ? ColorI( 0, 0, 0, 100 ) : ColorI( 255, 255, 255, 100 ); + if(!keyFocused) + nutColor.set( 128, 128, 128, 100 ); + + drawNuts(box, outlineColor, nutColor); + } + } + + renderChildControls(offset, updateRect); + + // Draw selection rectangle. + + if( mActive && mMouseDownMode == DragSelecting ) + { + RectI b; + getDragRect(b); + b.point += offset; + + // Draw outline. + + drawer->drawRect( b, ColorI( 100, 100, 100, 128 ) ); + + // Draw fill. + + b.inset( 1, 1 ); + drawer->drawRectFill( b, ColorI( 150, 150, 150, 128 ) ); + } + + // Draw grid. + + if( mActive && + ( mMouseDownMode == MovingSelection || mMouseDownMode == SizingSelection ) && + ( mGridSnap.x || mGridSnap.y ) ) + { + Point2I cext = getContentControl()->getExtent(); + Point2I coff = getContentControl()->localToGlobalCoord(Point2I(0,0)); + + // create point-dots + const Point2I& snap = mGridSnap; + U32 maxdot = (U32)(mCeil(cext.x / (F32)snap.x) - 1) * (U32)(mCeil(cext.y / (F32)snap.y) - 1); + + if( mDots.isNull() || maxdot != mDots->mNumVerts) + { + mDots.set(GFX, maxdot, GFXBufferTypeStatic); + + U32 ndot = 0; + mDots.lock(); + for(U32 ix = snap.x; ix < cext.x; ix += snap.x) + { + for(U32 iy = snap.y; ndot < maxdot && iy < cext.y; iy += snap.y) + { + mDots[ndot].color.set( 50, 50, 254, 100 ); + mDots[ndot].point.x = F32(ix + coff.x); + mDots[ndot].point.y = F32(iy + coff.y); + mDots[ndot].point.z = 0.0f; + ndot++; + } + } + mDots.unlock(); + AssertFatal(ndot <= maxdot, "dot overflow"); + AssertFatal(ndot == maxdot, "dot underflow"); + } + + if (!mDotSB) + { + GFXStateBlockDesc dotdesc; + dotdesc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha); + dotdesc.setCullMode( GFXCullNone ); + mDotSB = GFX->createStateBlock( dotdesc ); + } + + GFX->setStateBlock(mDotSB); + + // draw the points. + GFX->setVertexBuffer( mDots ); + GFX->drawPrimitive( GFXPointList, 0, mDots->mNumVerts ); + } + + // Draw snapping lines. + + if( mActive && getContentControl() ) + { + RectI bounds = getContentControl()->getGlobalBounds(); + + // Draw guide lines. + + if( mDrawGuides ) + { + for( U32 axis = 0; axis < 2; ++ axis ) + { + for( U32 i = 0, num = mGuides[ axis ].size(); i < num; ++ i ) + drawCrossSection( axis, mGuides[ axis ][ i ] + bounds.point[ axis ], + bounds, ColorI( 0, 255, 0, 100 ), drawer ); + } + } + + // Draw smart snap lines. + + for( U32 axis = 0; axis < 2; ++ axis ) + { + if( mSnapped[ axis ] ) + { + // Draw the snap line. + + drawCrossSection( axis, mSnapOffset[ axis ], + bounds, ColorI( 0, 0, 255, 100 ), drawer ); + + // Draw a border around the snap target control. + + if( mSnapTargets[ axis ] ) + { + RectI bounds = mSnapTargets[ axis ]->getGlobalBounds(); + drawer->drawRect( bounds, ColorF( .5, .5, .5, .5 ) ); + } + } + } + } +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::drawNuts(RectI &box, ColorI &outlineColor, ColorI &nutColor) +{ + GFXDrawUtil *drawer = GFX->getDrawUtil(); + + S32 lx = box.point.x, rx = box.point.x + box.extent.x - 1; + S32 cx = (lx + rx) >> 1; + S32 ty = box.point.y, by = box.point.y + box.extent.y - 1; + S32 cy = (ty + by) >> 1; + + if( mDrawBorderLines ) + { + ColorF lineColor( 0.7f, 0.7f, 0.7f, 0.25f ); + ColorF lightLineColor( 0.5f, 0.5f, 0.5f, 0.1f ); + + if(lx > 0 && ty > 0) + { + drawer->drawLine(0, ty, lx, ty, lineColor); // Left edge to top-left corner. + drawer->drawLine(lx, 0, lx, ty, lineColor); // Top edge to top-left corner. + } + + if(lx > 0 && by > 0) + drawer->drawLine(0, by, lx, by, lineColor); // Left edge to bottom-left corner. + + if(rx > 0 && ty > 0) + drawer->drawLine(rx, 0, rx, ty, lineColor); // Top edge to top-right corner. + + Point2I extent = localToGlobalCoord(getExtent()); + + if(lx < extent.x && by < extent.y) + drawer->drawLine(lx, by, lx, extent.y, lightLineColor); // Bottom-left corner to bottom edge. + if(rx < extent.x && by < extent.y) + { + drawer->drawLine(rx, by, rx, extent.y, lightLineColor); // Bottom-right corner to bottom edge. + drawer->drawLine(rx, by, extent.x, by, lightLineColor); // Bottom-right corner to right edge. + } + if(rx < extent.x && ty < extent.y) + drawer->drawLine(rx, ty, extent.x, ty, lightLineColor); // Top-right corner to right edge. + } + + // Adjust nuts, so they dont straddle the controls. + + lx -= NUT_SIZE + 1; + ty -= NUT_SIZE + 1; + rx += 1; + by += 1; + + // Draw nuts. + + drawNut( Point2I( lx - NUT_SIZE, ty - NUT_SIZE ), outlineColor, nutColor ); // Top left + drawNut( Point2I( lx - NUT_SIZE, cy - NUT_SIZE / 2 ), outlineColor, nutColor ); // Mid left + drawNut( Point2I( lx - NUT_SIZE, by ), outlineColor, nutColor ); // Bottom left + drawNut( Point2I( rx, ty - NUT_SIZE ), outlineColor, nutColor ); // Top right + drawNut( Point2I( rx, cy - NUT_SIZE / 2 ), outlineColor, nutColor ); // Mid right + drawNut( Point2I( rx, by ), outlineColor, nutColor ); // Bottom right + drawNut( Point2I( cx - NUT_SIZE / 2, ty - NUT_SIZE ), outlineColor, nutColor ); // Mid top + drawNut( Point2I( cx - NUT_SIZE / 2, by ), outlineColor, nutColor ); // Mid bottom +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::drawNut(const Point2I &nut, ColorI &outlineColor, ColorI &nutColor) +{ + RectI r( nut.x, nut.y, NUT_SIZE * 2, NUT_SIZE * 2 ); + GFX->getDrawUtil()->drawRect( r, outlineColor ); + r.inset( 1, 1 ); + GFX->getDrawUtil()->drawRectFill( r, nutColor ); +} + +//============================================================================= +// Selections. +//============================================================================= +// MARK: ---- Selections ---- + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::clearSelection(void) +{ + mSelectedControls.clear(); + onClearSelected_callback(); +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::setSelection(GuiControl *ctrl, bool inclusive) +{ + //sanity check + if( !ctrl ) + return; + + if( mSelectedControls.size() == 1 && mSelectedControls[ 0 ] == ctrl ) + return; + + if( !inclusive ) + clearSelection(); + + if( mContentControl == ctrl ) + setCurrentAddSet( ctrl, false ); + else + addSelection( ctrl ); +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::addSelection(S32 id) +{ + GuiControl * ctrl; + if( Sim::findObject( id, ctrl ) ) + addSelection( ctrl ); +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::addSelection( GuiControl* ctrl ) +{ + // Only add if this isn't the content control and the + // control isn't yet in the selection. + + if( ctrl != getContentControl() && !selectionContains( ctrl ) ) + { + mSelectedControls.push_back( ctrl ); + + if( mSelectedControls.size() == 1 ) + { + // Update the add set. + + if( ctrl->mIsContainer ) + setCurrentAddSet( ctrl, false ); + else + setCurrentAddSet( ctrl->getParent(), false ); + + // Notify script. + + onSelect_callback( ctrl ); + } + else + { + // Notify script. + + onAddSelected_callback( ctrl ); + } + } +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::removeSelection( S32 id ) +{ + GuiControl * ctrl; + if ( Sim::findObject( id, ctrl ) ) + removeSelection( ctrl ); +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::removeSelection( GuiControl* ctrl ) +{ + if( selectionContains( ctrl ) ) + { + Vector< GuiControl* >::iterator i = ::find( mSelectedControls.begin(), mSelectedControls.end(), ctrl ); + if ( i != mSelectedControls.end() ) + mSelectedControls.erase( i ); + + onRemoveSelected_callback( ctrl ); + } +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::canHitSelectedControls( bool state ) +{ + for( U32 i = 0, num = mSelectedControls.size(); i < num; ++ i ) + mSelectedControls[ i ]->setCanHit( state ); +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::moveSelectionToCtrl( GuiControl *newParent, bool callback ) +{ + for( U32 i = 0; i < mSelectedControls.size(); ++ i ) + { + GuiControl* ctrl = mSelectedControls[i]; + if( ctrl->getParent() == newParent + || ctrl->isLocked() + || selectionContainsParentOf( ctrl ) ) + continue; + + Point2I globalpos = ctrl->localToGlobalCoord(Point2I(0,0)); + newParent->addObject(ctrl); + Point2I newpos = ctrl->globalToLocalCoord(globalpos) + ctrl->getPosition(); + ctrl->setPosition(newpos); + } + + onHierarchyChanged_callback(); + + //TODO: undo +} + +//----------------------------------------------------------------------------- + +static Point2I snapPoint(Point2I point, Point2I delta, Point2I gridSnap) +{ + S32 snap; + if(gridSnap.x && delta.x) + { + snap = point.x % gridSnap.x; + point.x -= snap; + if(delta.x > 0 && snap != 0) + point.x += gridSnap.x; + } + if(gridSnap.y && delta.y) + { + snap = point.y % gridSnap.y; + point.y -= snap; + if(delta.y > 0 && snap != 0) + point.y += gridSnap.y; + } + return point; +} + +void GuiEditCtrl::moveAndSnapSelection( const Point2I &delta, bool callback ) +{ + // move / nudge gets a special callback so that multiple small moves can be + // coalesced into one large undo action. + // undo + + if( callback ) + onPreSelectionNudged_callback( getSelectedSet() ); + + Vector::iterator i; + Point2I newPos; + for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) + { + GuiControl* ctrl = *i; + if( !ctrl->isLocked() && !selectionContainsParentOf( ctrl ) ) + { + newPos = ctrl->getPosition() + delta; + newPos = snapPoint( newPos, delta, mGridSnap ); + ctrl->setPosition( newPos ); + } + } + + // undo + if( callback ) + onPostSelectionNudged_callback( getSelectedSet() ); + + // allow script to update the inspector + if( callback && mSelectedControls.size() > 0 ) + onSelectionMoved_callback( mSelectedControls[ 0 ] ); +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::moveSelection( const Point2I &delta, bool callback ) +{ + Vector::iterator i; + for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) + { + GuiControl* ctrl = *i; + if( !ctrl->isLocked() && !selectionContainsParentOf( ctrl ) ) + ctrl->setPosition( ctrl->getPosition() + delta ); + } + + // allow script to update the inspector + if( callback ) + onSelectionMoved_callback( mSelectedControls[ 0 ] ); +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::justifySelection( Justification j ) +{ + S32 minX, maxX; + S32 minY, maxY; + S32 extentX, extentY; + + if (mSelectedControls.size() < 2) + return; + + Vector::iterator i = mSelectedControls.begin(); + minX = (*i)->getLeft(); + maxX = minX + (*i)->getWidth(); + minY = (*i)->getTop(); + maxY = minY + (*i)->getHeight(); + extentX = (*i)->getWidth(); + extentY = (*i)->getHeight(); + i++; + for(;i != mSelectedControls.end(); i++) + { + minX = getMin(minX, (*i)->getLeft()); + maxX = getMax(maxX, (*i)->getLeft() + (*i)->getWidth()); + minY = getMin(minY, (*i)->getTop()); + maxY = getMax(maxY, (*i)->getTop() + (*i)->getHeight()); + extentX += (*i)->getWidth(); + extentY += (*i)->getHeight(); + } + S32 deltaX = maxX - minX; + S32 deltaY = maxY - minY; + switch(j) + { + case JUSTIFY_LEFT: + for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) + if( !( *i )->isLocked() ) + (*i)->setLeft( minX ); + break; + case JUSTIFY_TOP: + for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) + if( !( *i )->isLocked() ) + (*i)->setTop( minY ); + break; + case JUSTIFY_RIGHT: + for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) + if( !( *i )->isLocked() ) + (*i)->setLeft( maxX - (*i)->getWidth() + 1 ); + break; + case JUSTIFY_BOTTOM: + for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) + if( !( *i )->isLocked() ) + (*i)->setTop( maxY - (*i)->getHeight() + 1 ); + break; + case JUSTIFY_CENTER_VERTICAL: + for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) + if( !( *i )->isLocked() ) + (*i)->setLeft( minX + ((deltaX - (*i)->getWidth()) >> 1 )); + break; + case JUSTIFY_CENTER_HORIZONTAL: + for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) + if( !( *i )->isLocked() ) + (*i)->setTop( minY + ((deltaY - (*i)->getHeight()) >> 1 )); + break; + case SPACING_VERTICAL: + { + Vector sortedList; + Vector::iterator k; + for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) + { + for(k = sortedList.begin(); k != sortedList.end(); k++) + { + if ((*i)->getTop() < (*k)->getTop()) + break; + } + sortedList.insert(k, *i); + } + S32 space = (deltaY - extentY) / (mSelectedControls.size() - 1); + S32 curY = minY; + for(k = sortedList.begin(); k != sortedList.end(); k++) + { + if( !( *k )->isLocked() ) + (*k)->setTop( curY ); + curY += (*k)->getHeight() + space; + } + } + break; + case SPACING_HORIZONTAL: + { + Vector sortedList; + Vector::iterator k; + for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) + { + for(k = sortedList.begin(); k != sortedList.end(); k++) + { + if ((*i)->getLeft() < (*k)->getLeft()) + break; + } + sortedList.insert(k, *i); + } + S32 space = (deltaX - extentX) / (mSelectedControls.size() - 1); + S32 curX = minX; + for(k = sortedList.begin(); k != sortedList.end(); k++) + { + if( !( *k )->isLocked() ) + (*k)->setLeft( curX ); + curX += (*k)->getWidth() + space; + } + } + break; + } +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::cloneSelection() +{ + Vector< GuiControl* > newSelection; + + // Clone the controls in the current selection. + + const U32 numOldControls = mSelectedControls.size(); + for( U32 i = 0; i < numOldControls; ++ i ) + { + GuiControl* ctrl = mSelectedControls[ i ]; + + // If parent is in selection, too, skip to prevent multiple clones. + + if( ctrl->getParent() && selectionContains( ctrl->getParent() ) ) + continue; + + // Clone and add to set. + + GuiControl* clone = dynamic_cast< GuiControl* >( ctrl->deepClone() ); + if( clone ) + newSelection.push_back( clone ); + } + + // Exchange the selection set. + + clearSelection(); + const U32 numNewControls = newSelection.size(); + for( U32 i = 0; i < numNewControls; ++ i ) + addSelection( newSelection[ i ] ); + + // Callback for undo. + + onSelectionCloned_callback( getSelectedSet() ); +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::deleteSelection() +{ + // Notify script for undo. + + onTrashSelection_callback( getSelectedSet() ); + + // Move all objects in selection to trash. + + Vector< GuiControl* >::iterator i; + for( i = mSelectedControls.begin(); i != mSelectedControls.end(); i ++ ) + { + if( ( *i ) == getCurrentAddSet() ) + setCurrentAddSet( getContentControl(), false ); + + mTrash->addObject( *i ); + } + + clearSelection(); + + // Notify script it needs to update its views. + + onHierarchyChanged_callback(); +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::loadSelection( const char* filename ) +{ + // Set redefine behavior to rename. + + const char* oldRedefineBehavior = Con::getVariable( "$Con::redefineBehavior" ); + Con::setVariable( "$Con::redefineBehavior", "renameNew" ); + + // Exec the file or clipboard contents with the saved selection set. + + if( filename ) + Con::executef( "exec", filename ); + else + Con::evaluate( Platform::getClipboard() ); + + SimSet* set; + if( !Sim::findObject( "guiClipboard", set ) ) + { + if( filename ) + Con::errorf( "GuiEditCtrl::loadSelection() - could not find 'guiClipboard' in '%s'", filename ); + else + Con::errorf( "GuiEditCtrl::loadSelection() - could not find 'guiClipboard'" ); + return; + } + + // Restore redefine behavior. + + Con::setVariable( "$Con::redefineBehavior", oldRedefineBehavior ); + + // Add the objects in the set. + + if( set->size() ) + { + clearSelection(); + + GuiControlVector ctrls; + for( U32 i = 0, num = set->size(); i < num; ++ i ) + { + GuiControl *ctrl = dynamic_cast< GuiControl* >( ( *set )[ i ] ); + if( ctrl ) + { + getCurrentAddSet()->addObject( ctrl ); + ctrls.push_back( ctrl ); + } + } + + // Select all controls. We need to perform this here rather than in the + // loop above as addSelection() will modify the current add set. + for( U32 i = 0; i < ctrls.size(); ++ i ) + { + addSelection( ctrls[i] ); + } + + // Undo + onAddNewCtrlSet_callback( getSelectedSet() ); + + // Notify the script it needs to update its treeview. + + onHierarchyChanged_callback(); + } + set->deleteObject(); +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::saveSelection( const char* filename ) +{ + // If there are no selected objects, then don't save. + + if( mSelectedControls.size() == 0 ) + return; + + // Open the stream. + + Stream* stream; + if( filename ) + { + stream = FileStream::createAndOpen( filename, Torque::FS::File::Write ); + if( !stream ) + { + Con::errorf( "GuiEditCtrl::saveSelection - could not open '%s' for writing", filename ); + return; + } + } + else + stream = new MemStream( 4096 ); + + // Create a temporary SimSet. + + SimSet* clipboardSet = new SimSet; + clipboardSet->registerObject(); + Sim::getRootGroup()->addObject( clipboardSet, "guiClipboard" ); + + // Add the selected controls to the set. + + for( Vector< GuiControl* >::iterator i = mSelectedControls.begin(); + i != mSelectedControls.end(); ++ i ) + { + GuiControl* ctrl = *i; + if( !selectionContainsParentOf( ctrl ) ) + clipboardSet->addObject( ctrl ); + } + + // Write the SimSet. Use the IgnoreCanSave to ensure the controls + // get actually written out (also disables the default parent inheritance + // behavior for the flag). + + clipboardSet->write( *stream, 0, IgnoreCanSave ); + clipboardSet->deleteObject(); + + // If we were writing to a memory stream, copy to clipboard + // now. + + if( !filename ) + { + MemStream* memStream = static_cast< MemStream* >( stream ); + memStream->write( U8( 0 ) ); + Platform::setClipboard( ( const char* ) memStream->getBuffer() ); + } + + delete stream; +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::selectAll() +{ + GuiControl::iterator i; + + clearSelection(); + for(i = getCurrentAddSet()->begin(); i != getCurrentAddSet()->end(); i++) + { + GuiControl *ctrl = dynamic_cast(*i); + addSelection( ctrl ); + } +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::bringToFront() +{ + if( getNumSelected() != 1 ) + return; + + GuiControl* ctrl = mSelectedControls.first(); + ctrl->getParent()->pushObjectToBack( ctrl ); +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::pushToBack() +{ + if( getNumSelected() != 1 ) + return; + + GuiControl* ctrl = mSelectedControls.first(); + ctrl->getParent()->bringObjectToFront( ctrl ); +} + +//----------------------------------------------------------------------------- + +RectI GuiEditCtrl::getSelectionBounds() const +{ + Vector::const_iterator i = mSelectedControls.begin(); + + Point2I minPos = (*i)->localToGlobalCoord( Point2I( 0, 0 ) ); + Point2I maxPos = minPos; + + for(; i != mSelectedControls.end(); i++) + { + Point2I iPos = (**i).localToGlobalCoord( Point2I( 0 , 0 ) ); + + minPos.x = getMin( iPos.x, minPos.x ); + minPos.y = getMin( iPos.y, minPos.y ); + + Point2I iExt = ( **i ).getExtent(); + + iPos.x += iExt.x; + iPos.y += iExt.y; + + maxPos.x = getMax( iPos.x, maxPos.x ); + maxPos.y = getMax( iPos.y, maxPos.y ); + } + + minPos = getContentControl()->globalToLocalCoord( minPos ); + maxPos = getContentControl()->globalToLocalCoord( maxPos ); + + return RectI( minPos.x, minPos.y, ( maxPos.x - minPos.x ), ( maxPos.y - minPos.y ) ); +} + +//----------------------------------------------------------------------------- + +RectI GuiEditCtrl::getSelectionGlobalBounds() const +{ + Point2I minb( S32_MAX, S32_MAX ); + Point2I maxb( S32_MIN, S32_MIN ); + + for( U32 i = 0, num = mSelectedControls.size(); i < num; ++ i ) + { + // Min. + + Point2I pos = mSelectedControls[ i ]->localToGlobalCoord( Point2I( 0, 0 ) ); + + minb.x = getMin( minb.x, pos.x ); + minb.y = getMin( minb.y, pos.y ); + + // Max. + + const Point2I extent = mSelectedControls[ i ]->getExtent(); + + maxb.x = getMax( maxb.x, pos.x + extent.x ); + maxb.y = getMax( maxb.y, pos.y + extent.y ); + } + + RectI bounds( minb.x, minb.y, maxb.x - minb.x, maxb.y - minb.y ); + return bounds; +} + +//----------------------------------------------------------------------------- + +bool GuiEditCtrl::selectionContains( GuiControl *ctrl ) +{ + Vector::iterator i; + for (i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) + if (ctrl == *i) return true; + return false; +} + +//----------------------------------------------------------------------------- + +bool GuiEditCtrl::selectionContainsParentOf( GuiControl* ctrl ) +{ + GuiControl* parent = ctrl->getParent(); + while( parent && parent != getContentControl() ) + { + if( selectionContains( parent ) ) + return true; + + parent = parent->getParent(); + } + + return false; +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::select( GuiControl* ctrl ) +{ + clearSelection(); + addSelection( ctrl ); +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::updateSelectedSet() +{ + mSelectedSet->clear(); + Vector::iterator i; + for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) + { + mSelectedSet->addObject(*i); + } +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::addSelectControlsInRegion( const RectI& rect, U32 flags ) +{ + // Do a hit test on the content control. + + canHitSelectedControls( false ); + Vector< GuiControl* > hits; + + if( mFullBoxSelection ) + flags |= GuiControl::HIT_FullBoxOnly; + + getContentControl()->findHitControls( rect, hits, flags ); + canHitSelectedControls( true ); + + // Add all controls that got hit. + + for( U32 i = 0, num = hits.size(); i < num; ++ i ) + addSelection( hits[ i ] ); +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::addSelectControlAt( const Point2I& pos ) +{ + // Run a hit test. + + canHitSelectedControls( false ); + GuiControl* hit = getContentControl()->findHitControl( pos ); + canHitSelectedControls( true ); + + // Add to selection. + + if( hit ) + addSelection( hit ); +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::resizeControlsInSelectionBy( const Point2I& delta, U32 mode ) +{ + for( U32 i = 0, num = mSelectedControls.size(); i < num; ++ i ) + { + GuiControl *ctrl = mSelectedControls[ i ]; + if( ctrl->isLocked() ) + continue; + + Point2I minExtent = ctrl->getMinExtent(); + Point2I newPosition = ctrl->getPosition(); + Point2I newExtent = ctrl->getExtent(); + + if( mSizingMode & sizingLeft ) + { + newPosition.x += delta.x; + newExtent.x -= delta.x; + + if( newExtent.x < minExtent.x ) + { + newPosition.x -= minExtent.x - newExtent.x; + newExtent.x = minExtent.x; + } + } + else if( mSizingMode & sizingRight ) + { + newExtent.x += delta.x; + + if( newExtent.x < minExtent.x ) + newExtent.x = minExtent.x; + } + + if( mSizingMode & sizingTop ) + { + newPosition.y += delta.y; + newExtent.y -= delta.y; + + if( newExtent.y < minExtent.y ) + { + newPosition.y -= minExtent.y - newExtent.y; + newExtent.y = minExtent.y; + } + } + else if( mSizingMode & sizingBottom ) + { + newExtent.y += delta.y; + + if( newExtent.y < minExtent.y ) + newExtent.y = minExtent.y; + } + + ctrl->resize( newPosition, newExtent ); + } + + if( mSelectedControls.size() == 1 ) + onSelectionResized_callback( mSelectedControls[ 0 ] ); +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::fitIntoParents( bool width, bool height ) +{ + // Record undo. + + onFitIntoParent_callback( width, height ); + + // Fit. + + for( U32 i = 0; i < mSelectedControls.size(); ++ i ) + { + GuiControl* ctrl = mSelectedControls[ i ]; + GuiControl* parent = ctrl->getParent(); + + Point2I position = ctrl->getPosition(); + if( width ) + position.x = 0; + if( height ) + position.y = 0; + + Point2I extents = ctrl->getExtent(); + if( width ) + extents.x = parent->getWidth(); + if( height ) + extents.y = parent->getHeight(); + + ctrl->resize( position, extents ); + } +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::selectParents( bool addToSelection ) +{ + Vector< GuiControl* > parents; + + // Collect all parents. + + for( U32 i = 0; i < mSelectedControls.size(); ++ i ) + { + GuiControl* ctrl = mSelectedControls[ i ]; + if( ctrl != mContentControl && ctrl->getParent() != mContentControl ) + parents.push_back( mSelectedControls[ i ]->getParent() ); + } + + // If there's no parents to select, don't + // change the selection. + + if( parents.empty() ) + return; + + // Blast selection if need be. + + if( !addToSelection ) + clearSelection(); + + // Add the parents. + + for( U32 i = 0; i < parents.size(); ++ i ) + addSelection( parents[ i ] ); +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::selectChildren( bool addToSelection ) +{ + Vector< GuiControl* > children; + + // Collect all children. + + for( U32 i = 0; i < mSelectedControls.size(); ++ i ) + { + GuiControl* parent = mSelectedControls[ i ]; + for( GuiControl::iterator iter = parent->begin(); iter != parent->end(); ++ iter ) + { + GuiControl* child = dynamic_cast< GuiControl* >( *iter ); + if( child ) + children.push_back( child ); + } + } + + // If there's no children to select, don't + // change the selection. + + if( children.empty() ) + return; + + // Blast selection if need be. + + if( !addToSelection ) + clearSelection(); + + // Add the children. + + for( U32 i = 0; i < children.size(); ++ i ) + addSelection( children[ i ] ); +} + +//============================================================================= +// Guides. +//============================================================================= +// MARK: ---- Guides ---- + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::readGuides( guideAxis axis, GuiControl* ctrl ) +{ + // Read the guide indices from the vector stored on the respective dynamic + // property of the control. + + const char* guideIndices = ctrl->getDataField( smGuidesPropertyName[ axis ], NULL ); + if( guideIndices && guideIndices[ 0 ] ) + { + U32 index = 0; + while( true ) + { + const char* posStr = StringUnit::getUnit( guideIndices, index, " \t" ); + if( !posStr[ 0 ] ) + break; + + mGuides[ axis ].push_back( dAtoi( posStr ) ); + + index ++; + } + } +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::writeGuides( guideAxis axis, GuiControl* ctrl ) +{ + // Store the guide indices of the given axis in a vector on the respective + // dynamic property of the control. + + StringBuilder str; + bool isFirst = true; + for( U32 i = 0, num = mGuides[ axis ].size(); i < num; ++ i ) + { + if( !isFirst ) + str.append( ' ' ); + + char buffer[ 32 ]; + dSprintf( buffer, sizeof( buffer ), "%i", mGuides[ axis ][ i ] ); + + str.append( buffer ); + + isFirst = false; + } + + String value = str.end(); + ctrl->setDataField( smGuidesPropertyName[ axis ], NULL, value ); +} + +//----------------------------------------------------------------------------- + +S32 GuiEditCtrl::findGuide( guideAxis axis, const Point2I& point, U32 tolerance ) +{ + const S32 p = ( point - localToGlobalCoord( Point2I( 0, 0 ) ) )[ axis ]; + + for( U32 i = 0, num = mGuides[ axis ].size(); i < num; ++ i ) + { + const S32 g = mGuides[ axis ][ i ]; + if( p >= ( g - tolerance ) && + p <= ( g + tolerance ) ) + return i; + } + + return -1; +} + +//============================================================================= +// Snapping. +//============================================================================= +// MARK: ---- Snapping ---- + +//----------------------------------------------------------------------------- + +RectI GuiEditCtrl::getSnapRegion( snappingAxis axis, const Point2I& center ) const +{ + RectI rootBounds = getContentControl()->getBounds(); + + RectI rect; + if( axis == SnapHorizontal ) + rect = RectI( rootBounds.point.x, + center.y - mSnapSensitivity, + rootBounds.extent.x, + mSnapSensitivity * 2 ); + else // SnapVertical + rect = RectI( center.x - mSnapSensitivity, + rootBounds.point.y, + mSnapSensitivity * 2, + rootBounds.extent.y ); + + // Clip against root bounds. + + rect.intersect( rootBounds ); + + return rect; +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::registerSnap( snappingAxis axis, const Point2I& mousePoint, const Point2I& point, snappingEdges edge, GuiControl* ctrl ) +{ + bool takeNewSnap = false; + const Point2I globalPoint = getContentControl()->localToGlobalCoord( point ); + + // If we have no snap yet, just take this one. + + if( !mSnapped[ axis ] ) + takeNewSnap = true; + + // Otherwise see if this snap is the better one. + + else + { + // Compare deltas to pointer. + + S32 deltaCurrent = mAbs( mSnapOffset[ axis ] - mousePoint[ axis ] ); + S32 deltaNew = mAbs( globalPoint[ axis ] - mousePoint[ axis ] ); + + if( deltaCurrent > deltaNew ) + takeNewSnap = true; + } + + if( takeNewSnap ) + { + mSnapped[ axis ] = true; + mSnapOffset[ axis ] = globalPoint[ axis ]; + mSnapEdge[ axis ] = edge; + mSnapTargets[ axis ] = ctrl; + } +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::findSnaps( snappingAxis axis, const Point2I& mousePoint, const RectI& minRegion, const RectI& midRegion, const RectI& maxRegion ) +{ + // Find controls with edge in either minRegion, midRegion, or maxRegion + // (depending on snap settings). + + for( U32 i = 0, num = mSnapHits[ axis ].size(); i < num; ++ i ) + { + GuiControl* ctrl = mSnapHits[ axis ][ i ]; + if( ctrl == getContentControl() && !mSnapToCanvas ) + continue; + + RectI bounds = ctrl->getGlobalBounds(); + bounds.point = getContentControl()->globalToLocalCoord( bounds.point ); + + // Compute points on min, mid, and max lines of control. + + Point2I min = bounds.point; + Point2I max = min + bounds.extent - Point2I( 1, 1 ); + + Point2I mid = min; + mid.x += bounds.extent.x / 2; + mid.y += bounds.extent.y / 2; + + // Test edge snap cases. + + if( mSnapToEdges ) + { + // Min to min. + + if( minRegion.pointInRect( min ) ) + registerSnap( axis, mousePoint, min, SnapEdgeMin, ctrl ); + + // Max to max. + + if( maxRegion.pointInRect( max ) ) + registerSnap( axis, mousePoint, max, SnapEdgeMax, ctrl ); + + // Min to max. + + if( minRegion.pointInRect( max ) ) + registerSnap( axis, mousePoint, max, SnapEdgeMin, ctrl ); + + // Max to min. + + if( maxRegion.pointInRect( min ) ) + registerSnap( axis, mousePoint, min, SnapEdgeMax, ctrl ); + } + + // Test center snap cases. + + if( mSnapToCenters ) + { + // Mid to mid. + + if( midRegion.pointInRect( mid ) ) + registerSnap( axis, mousePoint, mid, SnapEdgeMid, ctrl ); + } + + // Test combined center+edge snap cases. + + if( mSnapToEdges && mSnapToCenters ) + { + // Min to mid. + + if( minRegion.pointInRect( mid ) ) + registerSnap( axis, mousePoint, mid, SnapEdgeMin, ctrl ); + + // Max to mid. + + if( maxRegion.pointInRect( mid ) ) + registerSnap( axis, mousePoint, mid, SnapEdgeMax, ctrl ); + + // Mid to min. + + if( midRegion.pointInRect( min ) ) + registerSnap( axis, mousePoint, min, SnapEdgeMid, ctrl ); + + // Mid to max. + + if( midRegion.pointInRect( max ) ) + registerSnap( axis, mousePoint, max, SnapEdgeMid, ctrl ); + } + } +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::doControlSnap( const GuiEvent& event, const RectI& selectionBounds, const RectI& selectionBoundsGlobal, Point2I& delta ) +{ + if( !mSnapToControls || ( !mSnapToEdges && !mSnapToCenters ) ) + return; + + // Allow restricting to just vertical (ALT+SHIFT) or just horizontal (ALT+CTRL) + // snaps. + + bool snapAxisEnabled[ 2 ]; + + if( event.modifier & SI_PRIMARY_ALT && event.modifier & SI_SHIFT ) + snapAxisEnabled[ SnapHorizontal ] = false; + else + snapAxisEnabled[ SnapHorizontal ] = true; + + if( event.modifier & SI_PRIMARY_ALT && event.modifier & SI_CTRL ) + snapAxisEnabled[ SnapVertical ] = false; + else + snapAxisEnabled[ SnapVertical ] = true; + + // Compute snap regions. There is one region centered on and aligned with + // each of the selection bounds edges plus two regions aligned on the selection + // bounds center. For the selection bounds origin, we use the point that the + // selection would be at, if we had already done the mouse drag. + + RectI snapRegions[ 2 ][ 3 ]; + Point2I projectedOrigin( selectionBounds.point + delta ); + dMemset( snapRegions, 0, sizeof( snapRegions ) ); + + for( U32 axis = 0; axis < 2; ++ axis ) + { + if( !snapAxisEnabled[ axis ] ) + continue; + + if( mSizingMode == sizingNone || + ( axis == 0 && mSizingMode & sizingLeft ) || + ( axis == 1 && mSizingMode & sizingTop ) ) + snapRegions[ axis ][ 0 ] = getSnapRegion( ( snappingAxis ) axis, projectedOrigin ); + + if( mSizingMode == sizingNone ) + snapRegions[ axis ][ 1 ] = getSnapRegion( ( snappingAxis ) axis, projectedOrigin + Point2I( selectionBounds.extent.x / 2, selectionBounds.extent.y / 2 ) ); + + if( mSizingMode == sizingNone || + ( axis == 0 && mSizingMode & sizingRight ) || + ( axis == 1 && mSizingMode & sizingBottom ) ) + snapRegions[ axis ][ 2 ] = getSnapRegion( ( snappingAxis ) axis, projectedOrigin + selectionBounds.extent - Point2I( 1, 1 ) ); + } + + // Find hit controls. + + canHitSelectedControls( false ); + + if( mSnapToEdges ) + { + for( U32 axis = 0; axis < 2; ++ axis ) + if( snapAxisEnabled[ axis ] ) + { + getContentControl()->findHitControls( snapRegions[ axis ][ 0 ], mSnapHits[ axis ], HIT_NoCanHitNoRecurse ); + getContentControl()->findHitControls( snapRegions[ axis ][ 2 ], mSnapHits[ axis ], HIT_NoCanHitNoRecurse ); + } + } + if( mSnapToCenters && mSizingMode == sizingNone ) + { + for( U32 axis = 0; axis < 2; ++ axis ) + if( snapAxisEnabled[ axis ] ) + getContentControl()->findHitControls( snapRegions[ axis ][ 1 ], mSnapHits[ axis ], HIT_NoCanHitNoRecurse ); + } + + canHitSelectedControls( true ); + + // Add the content control itself to the hit controls + // so we can always get a snap on it. + + if( mSnapToCanvas ) + { + mSnapHits[ 0 ].push_back( mContentControl ); + mSnapHits[ 1 ].push_back( mContentControl ); + } + + // Find snaps. + + for( U32 i = 0; i < 2; ++ i ) + if( snapAxisEnabled[ i ] ) + findSnaps( ( snappingAxis ) i, + event.mousePoint, + snapRegions[ i ][ 0 ], + snapRegions[ i ][ 1 ], + snapRegions[ i ][ 2 ] ); + + // Clean up. + + mSnapHits[ 0 ].clear(); + mSnapHits[ 1 ].clear(); +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::doGridSnap( const GuiEvent& event, const RectI& selectionBounds, const RectI& selectionBoundsGlobal, Point2I& delta ) +{ + delta += selectionBounds.point; + + if( mGridSnap.x ) + delta.x -= delta.x % mGridSnap.x; + if( mGridSnap.y ) + delta.y -= delta.y % mGridSnap.y; + + delta -= selectionBounds.point; +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::doGuideSnap( const GuiEvent& event, const RectI& selectionBounds, const RectI& selectionBoundsGlobal, Point2I& delta ) +{ + if( !mSnapToGuides ) + return; + + Point2I min = getContentControl()->localToGlobalCoord( selectionBounds.point + delta ); + Point2I mid = min + selectionBounds.extent / 2; + Point2I max = min + selectionBounds.extent - Point2I( 1, 1 ); + + for( U32 axis = 0; axis < 2; ++ axis ) + { + if( mSnapToEdges ) + { + S32 guideMin = -1; + S32 guideMax = -1; + + if( mSizingMode == sizingNone || + ( axis == 0 && mSizingMode & sizingLeft ) || + ( axis == 1 && mSizingMode & sizingTop ) ) + guideMin = findGuide( ( guideAxis ) axis, min, mSnapSensitivity ); + if( mSizingMode == sizingNone || + ( axis == 0 && mSizingMode & sizingRight ) || + ( axis == 1 && mSizingMode & sizingBottom ) ) + guideMax = findGuide( ( guideAxis ) axis, max, mSnapSensitivity ); + + Point2I pos( 0, 0 ); + + if( guideMin != -1 ) + { + pos[ axis ] = mGuides[ axis ][ guideMin ]; + registerSnap( ( snappingAxis ) axis, event.mousePoint, pos, SnapEdgeMin ); + } + + if( guideMax != -1 ) + { + pos[ axis ] = mGuides[ axis ][ guideMax ]; + registerSnap( ( snappingAxis ) axis, event.mousePoint, pos, SnapEdgeMax ); + } + } + + if( mSnapToCenters && mSizingMode == sizingNone ) + { + const S32 guideMid = findGuide( ( guideAxis ) axis, mid, mSnapSensitivity ); + if( guideMid != -1 ) + { + Point2I pos( 0, 0 ); + pos[ axis ] = mGuides[ axis ][ guideMid ]; + registerSnap( ( snappingAxis ) axis, event.mousePoint, pos, SnapEdgeMid ); + } + } + } +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::doSnapping( const GuiEvent& event, const RectI& selectionBounds, Point2I& delta ) +{ + // Clear snapping. If we have snapping on, we want to find a new best snap. + + mSnapped[ SnapVertical ] = false; + mSnapped[ SnapHorizontal ] = false; + + // Compute global bounds. + + RectI selectionBoundsGlobal = selectionBounds; + selectionBoundsGlobal.point = getContentControl()->localToGlobalCoord( selectionBoundsGlobal.point ); + + // Apply the snaps. + + doGridSnap( event, selectionBounds, selectionBoundsGlobal, delta ); + doGuideSnap( event, selectionBounds, selectionBoundsGlobal, delta ); + doControlSnap( event, selectionBounds, selectionBoundsGlobal, delta ); + + // If we have a horizontal snap, compute a delta. + + if( mSnapped[ SnapVertical ] ) + snapDelta( SnapVertical, mSnapEdge[ SnapVertical ], mSnapOffset[ SnapVertical ], selectionBoundsGlobal, delta ); + + // If we have a vertical snap, compute a delta. + + if( mSnapped[ SnapHorizontal ] ) + snapDelta( SnapHorizontal, mSnapEdge[ SnapHorizontal ], mSnapOffset[ SnapHorizontal ], selectionBoundsGlobal, delta ); +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::snapToGrid( Point2I& point ) +{ + if( mGridSnap.x ) + point.x -= point.x % mGridSnap.x; + if( mGridSnap.y ) + point.y -= point.y % mGridSnap.y; +} + +//============================================================================= +// Misc. +//============================================================================= +// MARK: ---- Misc ---- + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::setContentControl(GuiControl *root) +{ + mContentControl = root; + if( root != NULL ) + root->mIsContainer = true; + + setCurrentAddSet( root ); +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::setEditMode(bool value) +{ + mActive = value; + + clearSelection(); + if( mActive && mAwake ) + mCurrentAddSet = NULL; +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::setMouseMode( mouseModes mode ) +{ + if( mMouseDownMode != mode ) + { + mMouseDownMode = mode; + + onMouseModeChange_callback(); + } +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::setCurrentAddSet(GuiControl *ctrl, bool doclearSelection) +{ + if (ctrl != mCurrentAddSet) + { + if(doclearSelection) + clearSelection(); + + mCurrentAddSet = ctrl; + } +} + +//----------------------------------------------------------------------------- + +GuiControl* GuiEditCtrl::getCurrentAddSet() +{ + if( !mCurrentAddSet ) + setCurrentAddSet( mContentControl, false ); + + return mCurrentAddSet; +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::addNewControl(GuiControl *ctrl) +{ + getCurrentAddSet()->addObject(ctrl); + select( ctrl ); + + // undo + onAddNewCtrl_callback( ctrl ); +} + +//----------------------------------------------------------------------------- + +S32 GuiEditCtrl::getSizingHitKnobs(const Point2I &pt, const RectI &box) +{ + S32 lx = box.point.x, rx = box.point.x + box.extent.x - 1; + S32 cx = (lx + rx) >> 1; + S32 ty = box.point.y, by = box.point.y + box.extent.y - 1; + S32 cy = (ty + by) >> 1; + + // adjust nuts, so they dont straddle the controls + lx -= NUT_SIZE; + ty -= NUT_SIZE; + rx += NUT_SIZE; + by += NUT_SIZE; + + if (inNut(pt, lx, ty)) + return sizingLeft | sizingTop; + if (inNut(pt, cx, ty)) + return sizingTop; + if (inNut(pt, rx, ty)) + return sizingRight | sizingTop; + if (inNut(pt, lx, by)) + return sizingLeft | sizingBottom; + if (inNut(pt, cx, by)) + return sizingBottom; + if (inNut(pt, rx, by)) + return sizingRight | sizingBottom; + if (inNut(pt, lx, cy)) + return sizingLeft; + if (inNut(pt, rx, cy)) + return sizingRight; + return sizingNone; +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::getDragRect(RectI &box) +{ + box.point.x = getMin(mLastMousePos.x, mSelectionAnchor.x); + box.extent.x = getMax(mLastMousePos.x, mSelectionAnchor.x) - box.point.x + 1; + box.point.y = getMin(mLastMousePos.y, mSelectionAnchor.y); + box.extent.y = getMax(mLastMousePos.y, mSelectionAnchor.y) - box.point.y + 1; +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent) +{ + GuiCanvas *pRoot = getRoot(); + if( !pRoot ) + return; + + showCursor = false; + cursor = NULL; + + Point2I ctOffset; + Point2I cext; + GuiControl *ctrl; + + Point2I mousePos = globalToLocalCoord(lastGuiEvent.mousePoint); + + PlatformWindow *pWindow = static_cast(getRoot())->getPlatformWindow(); + AssertFatal(pWindow != NULL,"GuiControl without owning platform window! This should not be possible."); + PlatformCursorController *pController = pWindow->getCursorController(); + AssertFatal(pController != NULL,"PlatformWindow without an owned CursorController!"); + + S32 desiredCursor = PlatformCursorController::curArrow; + + // first see if we hit a sizing knob on the currently selected control... + if (mSelectedControls.size() == 1 ) + { + ctrl = mSelectedControls.first(); + cext = ctrl->getExtent(); + ctOffset = globalToLocalCoord(ctrl->localToGlobalCoord(Point2I(0,0))); + RectI box(ctOffset.x,ctOffset.y,cext.x, cext.y); + + GuiEditCtrl::sizingModes sizeMode = (GuiEditCtrl::sizingModes)getSizingHitKnobs(mousePos, box); + + if( mMouseDownMode == SizingSelection ) + { + if ( ( mSizingMode == ( sizingBottom | sizingRight ) ) || ( mSizingMode == ( sizingTop | sizingLeft ) ) ) + desiredCursor = PlatformCursorController::curResizeNWSE; + else if ( ( mSizingMode == ( sizingBottom | sizingLeft ) ) || ( mSizingMode == ( sizingTop | sizingRight ) ) ) + desiredCursor = PlatformCursorController::curResizeNESW; + else if ( mSizingMode == sizingLeft || mSizingMode == sizingRight ) + desiredCursor = PlatformCursorController::curResizeVert; + else if (mSizingMode == sizingTop || mSizingMode == sizingBottom ) + desiredCursor = PlatformCursorController::curResizeHorz; + } + else + { + // Check for current mouse position after checking for actual sizing mode + if ( ( sizeMode == ( sizingBottom | sizingRight ) ) || ( sizeMode == ( sizingTop | sizingLeft ) ) ) + desiredCursor = PlatformCursorController::curResizeNWSE; + else if ( ( sizeMode == ( sizingBottom | sizingLeft ) ) || ( sizeMode == ( sizingTop | sizingRight ) ) ) + desiredCursor = PlatformCursorController::curResizeNESW; + else if (sizeMode == sizingLeft || sizeMode == sizingRight ) + desiredCursor = PlatformCursorController::curResizeVert; + else if (sizeMode == sizingTop || sizeMode == sizingBottom ) + desiredCursor = PlatformCursorController::curResizeHorz; + } + } + + if( mMouseDownMode == MovingSelection && cursor == NULL ) + desiredCursor = PlatformCursorController::curResizeAll; + + if( pRoot->mCursorChanged != desiredCursor ) + { + // We've already changed the cursor, + // so set it back before we change it again. + if(pRoot->mCursorChanged != -1) + pController->popCursor(); + + // Now change the cursor shape + pController->pushCursor(desiredCursor); + pRoot->mCursorChanged = desiredCursor; + } +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::setSnapToGrid(U32 gridsize) +{ + if( gridsize != 0 ) + gridsize = getMax( gridsize, ( U32 ) MIN_GRID_SIZE ); + mGridSnap.set( gridsize, gridsize ); +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::controlInspectPreApply(GuiControl* object) +{ + // undo + onControlInspectPreApply_callback( object ); +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::controlInspectPostApply(GuiControl* object) +{ + // undo + onControlInspectPostApply_callback( object ); +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::startDragMove( const Point2I& startPoint ) +{ + mDragMoveUndo = true; + + // For calculating mouse delta + mDragBeginPoint = globalToLocalCoord( startPoint ); + + // Allocate enough space for our selected controls + mDragBeginPoints.reserve( mSelectedControls.size() ); + + // For snapping to origin + Vector::iterator i; + for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++) + mDragBeginPoints.push_back( (*i)->getPosition() ); + + // Set Mouse Mode + setMouseMode( MovingSelection ); + + // undo + onPreEdit_callback( getSelectedSet() ); +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::startDragRectangle( const Point2I& startPoint ) +{ + mSelectionAnchor = globalToLocalCoord( startPoint ); + setMouseMode( DragSelecting ); +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::startDragClone( const Point2I& startPoint ) +{ + mDragBeginPoint = globalToLocalCoord( startPoint ); + + setMouseMode( DragClone ); +} + +//----------------------------------------------------------------------------- + +void GuiEditCtrl::startMouseGuideDrag( guideAxis axis, U32 guideIndex, bool lockMouse ) +{ + mDragGuideIndex[ axis ] = guideIndex; + mDragGuide[ axis ] = true; + + setMouseMode( DragGuide ); + + // Grab the mouse. + + if( lockMouse ) + mouseLock(); +} + +//============================================================================= +// Console Methods. +//============================================================================= +// MARK: ---- Console Methods ---- + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiEditCtrl, getContentControl, S32, 2, 2, "() - Return the toplevel control edited inside the GUI editor." ) +{ + GuiControl* ctrl = object->getContentControl(); + if( ctrl ) + return ctrl->getId(); + else + return 0; +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiEditCtrl, setContentControl, void, 3, 3, "( GuiControl ctrl ) - Set the toplevel control to edit in the GUI editor." ) +{ + GuiControl *ctrl; + if(!Sim::findObject(argv[2], ctrl)) + return; + object->setContentControl(ctrl); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiEditCtrl, addNewCtrl, void, 3, 3, "(GuiControl ctrl)") +{ + GuiControl *ctrl; + if(!Sim::findObject(argv[2], ctrl)) + return; + object->addNewControl(ctrl); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiEditCtrl, addSelection, void, 3, 3, "selects a control.") +{ + S32 id = dAtoi(argv[2]); + object->addSelection(id); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiEditCtrl, removeSelection, void, 3, 3, "deselects a control.") +{ + S32 id = dAtoi(argv[2]); + object->removeSelection(id); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiEditCtrl, clearSelection, void, 2, 2, "Clear selected controls list.") +{ + object->clearSelection(); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiEditCtrl, select, void, 3, 3, "(GuiControl ctrl)") +{ + GuiControl *ctrl; + + if(!Sim::findObject(argv[2], ctrl)) + return; + + object->setSelection(ctrl, false); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiEditCtrl, setCurrentAddSet, void, 3, 3, "(GuiControl ctrl)") +{ + GuiControl *addSet; + + if (!Sim::findObject(argv[2], addSet)) + { + Con::printf("%s(): Invalid control: %s", argv[0], argv[2]); + return; + } + object->setCurrentAddSet(addSet); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiEditCtrl, getCurrentAddSet, S32, 2, 2, "Returns the set to which new controls will be added") +{ + const GuiControl* add = object->getCurrentAddSet(); + return add ? add->getId() : 0; +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiEditCtrl, toggle, void, 2, 2, "Toggle activation.") +{ + object->setEditMode( !object->isActive() ); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiEditCtrl, justify, void, 3, 3, "(int mode)" ) +{ + object->justifySelection((GuiEditCtrl::Justification)dAtoi(argv[2])); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiEditCtrl, bringToFront, void, 2, 2, "") +{ + object->bringToFront(); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiEditCtrl, pushToBack, void, 2, 2, "") +{ + object->pushToBack(); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiEditCtrl, deleteSelection, void, 2, 2, "() - Delete the selected controls.") +{ + object->deleteSelection(); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiEditCtrl, moveSelection, void, 4, 4, "(int dx, int dy) - Move all controls in the selection by (dx,dy) pixels.") +{ + object->moveAndSnapSelection(Point2I(dAtoi(argv[2]), dAtoi(argv[3]))); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiEditCtrl, saveSelection, void, 2, 3, "( string fileName=null ) - Save selection to file or clipboard.") +{ + const char* filename = NULL; + if( argc > 2 ) + filename = argv[ 2 ]; + + object->saveSelection( filename ); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiEditCtrl, loadSelection, void, 2, 3, "( string fileName=null ) - Load selection from file or clipboard.") +{ + const char* filename = NULL; + if( argc > 2 ) + filename = argv[ 2 ]; + + object->loadSelection( filename ); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiEditCtrl, selectAll, void, 2, 2, "()") +{ + object->selectAll(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiEditCtrl, getSelection, SimSet*, (),, + "Gets the set of GUI controls currently selected in the editor." ) +{ + return object->getSelectedSet(); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiEditCtrl, getNumSelected, S32, 2, 2, "() - Return the number of controls currently selected." ) +{ + return object->getNumSelected(); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiEditCtrl, getSelectionGlobalBounds, const char*, 2, 2, "() - Returns global bounds of current selection as vector 'x y width height'." ) +{ + RectI bounds = object->getSelectionGlobalBounds(); + String str = String::ToString( "%i %i %i %i", bounds.point.x, bounds.point.y, bounds.extent.x, bounds.extent.y ); + + char* buffer = Con::getReturnBuffer( str.length() ); + dStrcpy( buffer, str.c_str() ); + + return buffer; +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiEditCtrl, selectParents, void, 2, 3, "( bool addToSelection=false ) - Select parents of currently selected controls." ) +{ + bool addToSelection = false; + if( argc > 2 ) + addToSelection = dAtob( argv[ 2 ] ); + + object->selectParents( addToSelection ); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiEditCtrl, selectChildren, void, 2, 3, "( bool addToSelection=false ) - Select children of currently selected controls." ) +{ + bool addToSelection = false; + if( argc > 2 ) + addToSelection = dAtob( argv[ 2 ] ); + + object->selectChildren( addToSelection ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiEditCtrl, getTrash, SimGroup*, (),, + "Gets the GUI controls(s) that are currently in the trash.") +{ + return object->getTrash(); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod(GuiEditCtrl, setSnapToGrid, void, 3, 3, "GuiEditCtrl.setSnapToGrid(gridsize)") +{ + U32 gridsize = dAtoi(argv[2]); + object->setSnapToGrid(gridsize); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiEditCtrl, readGuides, void, 3, 4, "( GuiControl ctrl [, int axis ] ) - Read the guides from the given control." ) +{ + // Find the control. + + GuiControl* ctrl; + if( !Sim::findObject( argv[ 2 ], ctrl ) ) + { + Con::errorf( "GuiEditCtrl::readGuides - no control '%s'", argv[ 2 ] ); + return; + } + + // Read the guides. + + if( argc > 3 ) + { + S32 axis = dAtoi( argv[ 3 ] ); + if( axis < 0 || axis > 1 ) + { + Con::errorf( "GuiEditCtrl::readGuides - invalid axis '%s'", argv[ 3 ] ); + return; + } + + object->readGuides( ( GuiEditCtrl::guideAxis ) axis, ctrl ); + } + else + { + object->readGuides( GuiEditCtrl::GuideHorizontal, ctrl ); + object->readGuides( GuiEditCtrl::GuideVertical, ctrl ); + } +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiEditCtrl, writeGuides, void, 3, 4, "( GuiControl ctrl [, int axis ] ) - Write the guides to the given control." ) +{ + // Find the control. + + GuiControl* ctrl; + if( !Sim::findObject( argv[ 2 ], ctrl ) ) + { + Con::errorf( "GuiEditCtrl::writeGuides - no control '%i'", argv[ 2 ] ); + return; + } + + // Write the guides. + + if( argc > 3 ) + { + S32 axis = dAtoi( argv[ 3 ] ); + if( axis < 0 || axis > 1 ) + { + Con::errorf( "GuiEditCtrl::writeGuides - invalid axis '%s'", argv[ 3 ] ); + return; + } + + object->writeGuides( ( GuiEditCtrl::guideAxis ) axis, ctrl ); + } + else + { + object->writeGuides( GuiEditCtrl::GuideHorizontal, ctrl ); + object->writeGuides( GuiEditCtrl::GuideVertical, ctrl ); + } +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiEditCtrl, clearGuides, void, 2, 3, "( [ int axis ] ) - Clear all currently set guide lines." ) +{ + if( argc > 2 ) + { + S32 axis = dAtoi( argv[ 2 ] ); + if( axis < 0 || axis > 1 ) + { + Con::errorf( "GuiEditCtrl::clearGuides - invalid axis '%i'", axis ); + return; + } + + object->clearGuides( ( GuiEditCtrl::guideAxis ) axis ); + } + else + { + object->clearGuides( GuiEditCtrl::GuideHorizontal ); + object->clearGuides( GuiEditCtrl::GuideVertical ); + } +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiEditCtrl, fitIntoParents, void, 2, 4, "( bool width=true, bool height=true ) - Fit selected controls into their parents." ) +{ + bool width = true; + bool height = true; + + if( argc > 2 ) + width = dAtob( argv[ 2 ] ); + if( argc > 3 ) + height = dAtob( argv[ 3 ] ); + + object->fitIntoParents( width, height ); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiEditCtrl, getMouseMode, const char*, 2, 2, "() - Return the current mouse mode." ) +{ + switch( object->getMouseMode() ) + { + case GuiEditCtrl::Selecting: + return "Selecting"; + + case GuiEditCtrl::DragSelecting: + return "DragSelecting"; + + case GuiEditCtrl::MovingSelection: + return "MovingSelection"; + + case GuiEditCtrl::SizingSelection: + return "SizingSelection"; + + case GuiEditCtrl::DragGuide: + return "DragGuide"; + + case GuiEditCtrl::DragClone: + return "DragClone"; + + default: + return ""; + } +} + +//============================================================================= +// GuiEditorRuler. +//============================================================================= + +class GuiEditorRuler : public GuiControl +{ + public: + + typedef GuiControl Parent; + + protected: + + String mRefCtrlName; + String mEditCtrlName; + + GuiScrollCtrl* mRefCtrl; + GuiEditCtrl* mEditCtrl; + + public: + + enum EOrientation + { + ORIENTATION_Horizontal, + ORIENTATION_Vertical + }; + + GuiEditorRuler() + : mRefCtrl( 0 ), + mEditCtrl( 0 ) + { + } + + EOrientation getOrientation() const + { + if( getWidth() > getHeight() ) + return ORIENTATION_Horizontal; + else + return ORIENTATION_Vertical; + } + + bool onWake() + { + if( !Parent::onWake() ) + return false; + + if( !mEditCtrlName.isEmpty() && !Sim::findObject( mEditCtrlName, mEditCtrl ) ) + Con::errorf( "GuiEditorRuler::onWake() - no GuiEditCtrl '%s'", mEditCtrlName.c_str() ); + + if( !mRefCtrlName.isEmpty() && !Sim::findObject( mRefCtrlName, mRefCtrl ) ) + Con::errorf( "GuiEditorRuler::onWake() - no GuiScrollCtrl '%s'", mRefCtrlName.c_str() ); + + return true; + } + + void onPreRender() + { + setUpdate(); + } + + void onMouseDown( const GuiEvent& event ) + { + if( !mEditCtrl ) + return; + + // Determine the guide axis. + + GuiEditCtrl::guideAxis axis; + if( getOrientation() == ORIENTATION_Horizontal ) + axis = GuiEditCtrl::GuideHorizontal; + else + axis = GuiEditCtrl::GuideVertical; + + // Start dragging a new guide out in the editor. + + U32 guideIndex = mEditCtrl->addGuide( axis, 0 ); + mEditCtrl->startMouseGuideDrag( axis, guideIndex ); + } + + void onRender(Point2I offset, const RectI &updateRect) + { + GFX->getDrawUtil()->drawRectFill(updateRect, ColorF(1,1,1,1)); + + Point2I choffset(0,0); + if( mRefCtrl != NULL ) + choffset = mRefCtrl->getChildPos(); + + if( getOrientation() == ORIENTATION_Horizontal ) + { + // it's horizontal. + for(U32 i = 0; i < getWidth(); i++) + { + S32 x = offset.x + i; + S32 pos = i - choffset.x; + if(!(pos % 10)) + { + S32 start = 6; + if(!(pos %20)) + start = 4; + if(!(pos % 100)) + start = 1; + GFX->getDrawUtil()->drawLine(x, offset.y + start, x, offset.y + 10, ColorF(0,0,0,1)); + } + } + } + else + { + // it's vertical. + for(U32 i = 0; i < getHeight(); i++) + { + S32 y = offset.y + i; + S32 pos = i - choffset.y; + if(!(pos % 10)) + { + S32 start = 6; + if(!(pos %20)) + start = 4; + if(!(pos % 100)) + start = 1; + GFX->getDrawUtil()->drawLine(offset.x + start, y, offset.x + 10, y, ColorF(0,0,0,1)); + } + } + } + } + static void initPersistFields() + { + addField( "refCtrl", TypeRealString, Offset( mRefCtrlName, GuiEditorRuler ) ); + addField( "editCtrl", TypeRealString, Offset( mEditCtrlName, GuiEditorRuler ) ); + + Parent::initPersistFields(); + } + + DECLARE_CONOBJECT(GuiEditorRuler); + DECLARE_CATEGORY( "Gui Editor" ); +}; + +IMPLEMENT_CONOBJECT(GuiEditorRuler); + +ConsoleDocClass( GuiEditorRuler, + "@brief Visual representation of markers on top and left sides of GUI Editor\n\n" + "Editor use only.\n\n" + "@internal" +); diff --git a/Engine/source/gui/editor/guiEditCtrl.h b/Engine/source/gui/editor/guiEditCtrl.h new file mode 100644 index 000000000..7731545f1 --- /dev/null +++ b/Engine/source/gui/editor/guiEditCtrl.h @@ -0,0 +1,339 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIEDITCTRL_H_ +#define _GUIEDITCTRL_H_ + +#ifndef _GUICONTROL_H_ + #include "gui/core/guiControl.h" +#endif +#ifndef _UNDO_H_ + #include "util/undo.h" +#endif +#ifndef _GFX_GFXDRAWER_H_ + #include "gfx/gfxDrawUtil.h" +#endif + + +/// Native side of the GUI editor. +class GuiEditCtrl : public GuiControl +{ + public: + + typedef GuiControl Parent; + friend class GuiEditorRuler; + + enum Justification + { + JUSTIFY_LEFT, + JUSTIFY_CENTER_VERTICAL, + JUSTIFY_RIGHT, + JUSTIFY_TOP, + JUSTIFY_BOTTOM, + SPACING_VERTICAL, + SPACING_HORIZONTAL, + JUSTIFY_CENTER_HORIZONTAL, + }; + + enum guideAxis { GuideVertical, GuideHorizontal }; + + enum mouseModes { Selecting, MovingSelection, SizingSelection, DragSelecting, DragGuide, DragClone }; + enum sizingModes { sizingNone = 0, sizingLeft = 1, sizingRight = 2, sizingTop = 4, sizingBottom = 8 }; + + enum snappingAxis { SnapVertical, SnapHorizontal }; + enum snappingEdges { SnapEdgeMin, SnapEdgeMid, SnapEdgeMax }; + + protected: + + enum + { + NUT_SIZE = 4, + MIN_GRID_SIZE = 3 + }; + + typedef Vector< GuiControl* > GuiControlVector; + typedef SimObjectPtr< GuiControl > GuiControlPtr; + + bool mDrawBorderLines; + bool mDrawGuides; + bool mFullBoxSelection; + GuiControlVector mSelectedControls; + GuiControlPtr mCurrentAddSet; + GuiControlPtr mContentControl; + Point2I mLastMousePos; + Point2I mLastDragPos; + Point2I mSelectionAnchor; + Point2I mGridSnap; + Point2I mDragBeginPoint; + Vector mDragBeginPoints; + bool mDragAddSelection; + bool mDragMoveUndo; + + // Guides. + + bool mSnapToGuides; ///< If true, edge and center snapping will work against guides. + bool mDragGuide[ 2 ]; + U32 mDragGuideIndex[ 2 ];///< Indices of the guide's being dragged in DragGuide mouse mode. + Vector< U32 > mGuides[ 2 ]; ///< The guides defined on the current content control. + + // Snapping + + bool mSnapToControls; ///< If true, edge and center snapping will work against controls. + bool mSnapToCanvas; ///< If true, edge and center snapping will work against canvas (= content control). + bool mSnapToEdges; ///< If true, selection edges will snap to other control edges. + bool mSnapToCenters; ///< If true, selection centers will snap to other control centers. + S32 mSnapSensitivity; ///< Snap distance in pixels. + + bool mSnapped[ 2 ]; ///< Snap flag for each axis. If true, we are currently snapped in a drag. + S32 mSnapOffset[ 2 ]; ///< Reference point on snap line for each axis. + GuiControlVector mSnapHits[ 2 ]; ///< Hit testing lists for each axis. + snappingEdges mSnapEdge[ 2 ]; ///< Identification of edge being snapped for each axis. + GuiControlPtr mSnapTargets[ 2 ]; ///< Controls providing the snap reference lines on each axis. + + // Undo + SimGroup* mTrash; + SimSet* mSelectedSet; + + // grid drawing + GFXVertexBufferHandle mDots; + GFXStateBlockRef mDotSB; + + mouseModes mMouseDownMode; + sizingModes mSizingMode; + + static StringTableEntry smGuidesPropertyName[ 2 ]; + + // private methods + void updateSelectedSet(); + + S32 findGuide( guideAxis axis, const Point2I& point, U32 tolerance = 0 ); + + RectI getSnapRegion( snappingAxis axis, const Point2I& center ) const; + void findSnaps( snappingAxis axis, const Point2I& mousePoint, const RectI& minRegion, const RectI& midRegion, const RectI& maxRegion ); + void registerSnap( snappingAxis axis, const Point2I& mousePoint, const Point2I& point, snappingEdges edge, GuiControl* ctrl = NULL ); + + void doSnapping( const GuiEvent& event, const RectI& selectionBounds, Point2I& delta ); + void doGuideSnap( const GuiEvent& event, const RectI& selectionBounds, const RectI& selectionBoundsGlobal, Point2I& delta ); + void doControlSnap( const GuiEvent& event, const RectI& selectionBounds, const RectI& selectionBoundsGlobal, Point2I& delta ); + void doGridSnap( const GuiEvent& event, const RectI& selectionBounds, const RectI& selectionBoundsGlobal, Point2I& delta ); + void snapToGrid( Point2I& point ); + + void startDragMove( const Point2I& startPoint ); + void startDragRectangle( const Point2I& startPoint ); + void startDragClone( const Point2I& startPoint ); + void startMouseGuideDrag( guideAxis axis, U32 index, bool lockMouse = true ); + + void setMouseMode( mouseModes mode ); + + /// @name Callbacks + /// @{ + + DECLARE_CALLBACK( void, onHierarchyChanged, () ); + DECLARE_CALLBACK( void, onDelete, () ); + DECLARE_CALLBACK( void, onPreEdit, ( SimSet* selection ) ); + DECLARE_CALLBACK( void, onPostEdit, ( SimSet* selection ) ); + DECLARE_CALLBACK( void, onClearSelected, () ); + DECLARE_CALLBACK( void, onSelect, ( GuiControl* control ) ); + DECLARE_CALLBACK( void, onAddSelected, ( GuiControl* control ) ); + DECLARE_CALLBACK( void, onRemoveSelected, ( GuiControl* control ) ); + DECLARE_CALLBACK( void, onPreSelectionNudged, ( SimSet* selection ) ); + DECLARE_CALLBACK( void, onPostSelectionNudged, ( SimSet* selection ) ); + DECLARE_CALLBACK( void, onSelectionMoved, ( GuiControl* control ) ); //FIXME: this should be a selection SimSet + DECLARE_CALLBACK( void, onSelectionCloned, ( SimSet* selection ) ); + DECLARE_CALLBACK( void, onTrashSelection, ( SimSet* selection ) ); + DECLARE_CALLBACK( void, onAddNewCtrl, ( GuiControl* control ) ); + DECLARE_CALLBACK( void, onAddNewCtrlSet, ( SimSet* set ) ); + DECLARE_CALLBACK( void, onSelectionResized, ( GuiControl* control ) ); + DECLARE_CALLBACK( void, onFitIntoParent, ( bool width, bool height ) ); + DECLARE_CALLBACK( void, onMouseModeChange, () ); + DECLARE_CALLBACK( void, onControlInspectPreApply, ( GuiControl* control ) ); + DECLARE_CALLBACK( void, onControlInspectPostApply, ( GuiControl* control ) ); + + /// @} + + static bool inNut( const Point2I &pt, S32 x, S32 y ) + { + S32 dx = pt.x - x; + S32 dy = pt.y - y; + return dx <= NUT_SIZE && dx >= -NUT_SIZE && dy <= NUT_SIZE && dy >= -NUT_SIZE; + } + + static void drawCrossSection( U32 axis, S32 offset, const RectI& bounds, ColorI color, GFXDrawUtil* drawer ) + { + Point2I start; + Point2I end; + + if( axis == 0 ) + { + start.x = end.x = offset; + start.y = bounds.point.y; + end.y = bounds.point.y + bounds.extent.y - 1; + } + else + { + start.y = end.y = offset; + start.x = bounds.point.x; + end.x = bounds.point.x + bounds.extent.x - 1; + } + + drawer->drawLine( start, end, color ); + } + + static void snapDelta( snappingAxis axis, snappingEdges edge, S32 offset, const RectI& bounds, Point2I& delta ) + { + switch( axis ) + { + case SnapVertical: + switch( edge ) + { + case SnapEdgeMin: + delta.x = offset - bounds.point.x; + break; + + case SnapEdgeMid: + delta.x = offset - bounds.point.x - bounds.extent.x / 2; + break; + + case SnapEdgeMax: + delta.x = offset - bounds.point.x - bounds.extent.x + 1; + break; + } + break; + + case SnapHorizontal: + switch( edge ) + { + case SnapEdgeMin: + delta.y = offset - bounds.point.y; + break; + + case SnapEdgeMid: + delta.y = offset - bounds.point.y - bounds.extent.y / 2; + break; + + case SnapEdgeMax: + delta.y = offset - bounds.point.y - bounds.extent.y + 1; + break; + } + break; + } + } + + public: + + GuiEditCtrl(); + + DECLARE_CONOBJECT(GuiEditCtrl); + DECLARE_CATEGORY( "Gui Editor" ); + DECLARE_DESCRIPTION( "Implements the framework for the GUI editor." ); + + bool onWake(); + void onSleep(); + + static void initPersistFields(); + + GuiControl* getContentControl() const { return mContentControl; } + void setContentControl(GuiControl *ctrl); + void setEditMode(bool value); + S32 getSizingHitKnobs(const Point2I &pt, const RectI &box); + void getDragRect(RectI &b); + void drawNut(const Point2I &nut, ColorI &outlineColor, ColorI &nutColor); + void drawNuts(RectI &box, ColorI &outlineColor, ColorI &nutColor); + void onPreRender(); + void onRender(Point2I offset, const RectI &updateRect); + void addNewControl(GuiControl *ctrl); + void setCurrentAddSet(GuiControl *ctrl, bool clearSelection = true); + GuiControl* getCurrentAddSet(); + + // Selections. + + void select( GuiControl *ctrl ); + bool selectionContains( GuiControl *ctrl ); + bool selectionContainsParentOf( GuiControl* ctrl ); + void setSelection( GuiControl *ctrl, bool inclusive = false ); + RectI getSelectionBounds() const; + RectI getSelectionGlobalBounds() const; + void canHitSelectedControls( bool state = true ); + void justifySelection( Justification j ); + void moveSelection( const Point2I &delta, bool callback = true ); + void moveAndSnapSelection( const Point2I &delta, bool callback = true ); + void saveSelection( const char *filename ); + void loadSelection( const char *filename ); + void addSelection( S32 id ); + void addSelection( GuiControl* ctrl ); + void removeSelection( S32 id ); + void removeSelection( GuiControl* ctrl ); + void deleteSelection(); + void clearSelection(); + void selectAll(); + void bringToFront(); + void pushToBack(); + void moveSelectionToCtrl( GuiControl* ctrl, bool callback = true ); + void addSelectControlsInRegion( const RectI& rect, U32 hitTestFlags = 0 ); + void addSelectControlAt( const Point2I& pos ); + void resizeControlsInSelectionBy( const Point2I& delta, U32 mode ); + void fitIntoParents( bool width = true, bool height = true ); + void selectParents( bool addToSelection = false ); + void selectChildren( bool addToSelection = false ); + void cloneSelection(); + U32 getNumSelected() const { return mSelectedControls.size(); } + + // Guides. + + U32 addGuide( guideAxis axis, U32 offset ) { U32 index = mGuides[ axis ].size(); mGuides[ axis ].push_back( offset ); return index; } + void readGuides( guideAxis axis, GuiControl* ctrl ); + void writeGuides( guideAxis axis, GuiControl* ctrl ); + void clearGuides( guideAxis axis ) { mGuides[ axis ].clear(); } + + // Undo Access. + + void undo(); + void redo(); + + // When a control is changed by the inspector + void controlInspectPreApply(GuiControl* object); + void controlInspectPostApply(GuiControl* object); + + // Sizing Cursors + void getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent); + + U32 getSelectionSize() const { return mSelectedControls.size(); } + const Vector& getSelected() const { return mSelectedControls; } + SimSet* getSelectedSet() { updateSelectedSet(); return mSelectedSet; } + SimGroup* getTrash() { return mTrash; } + GuiControl* getAddSet() const { return mCurrentAddSet; }; //JDD + + bool onKeyDown(const GuiEvent &event); + void onMouseDown(const GuiEvent &event); + void onMouseUp(const GuiEvent &event); + void onMouseDragged(const GuiEvent &event); + void onRightMouseDown(const GuiEvent &event); + + mouseModes getMouseMode() const { return mMouseDownMode; } + + virtual bool onAdd(); + virtual void onRemove(); + + void setSnapToGrid(U32 gridsize); +}; + +#endif //_GUI_EDIT_CTRL_H diff --git a/Engine/source/gui/editor/guiFilterCtrl.cpp b/Engine/source/gui/editor/guiFilterCtrl.cpp new file mode 100644 index 000000000..4555f3ee5 --- /dev/null +++ b/Engine/source/gui/editor/guiFilterCtrl.cpp @@ -0,0 +1,293 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/editor/guiFilterCtrl.h" + +#include "console/console.h" +#include "console/consoleTypes.h" +#include "guiFilterCtrl.h" +#include "math/mMath.h" +#include "gfx/gfxDrawUtil.h" + + +IMPLEMENT_CONOBJECT(GuiFilterCtrl); + +ConsoleDocClass( GuiFilterCtrl, + "@brief A control that displays a Catmull-Rom spline through a number of control points.\n\n" + "Currently editor use only, no real application without extension.\n\n " + "@internal"); + +GuiFilterCtrl::GuiFilterCtrl() +{ + mControlPointRequest = 7; + mFilter.setSize(7); + mShowIdentity = true; + mIdentity.set( 0.0f, 1.0f ); + identity(); + mCurKnot = -1; +} + +void GuiFilterCtrl::initPersistFields() +{ + addField("controlPoints", TypeS32, Offset(mControlPointRequest, GuiFilterCtrl), + "Total number of control points in the spline curve." ); + addField("filter", TypeF32Vector, Offset(mFilter, GuiFilterCtrl), + "Vector of control points." ); + addField("showIdentity", TypeBool, Offset(mShowIdentity, GuiFilterCtrl), "@internal" ); + addField("identity", TypePoint2F, Offset(mIdentity, GuiFilterCtrl), "@internal"); + + Parent::initPersistFields(); +} + +ConsoleMethod( GuiFilterCtrl, getValue, const char*, 2, 2, "Return a tuple containing all the values in the filter." + "@internal") +{ + TORQUE_UNUSED(argv); + static char buffer[512]; + const Filter *filter = object->get(); + *buffer = 0; + + for (U32 i=0; i < filter->size(); i++) + { + char value[32]; + dSprintf(value, 31, "%1.5f ", *(filter->begin()+i) ); + dStrcat(buffer, value); + } + + return buffer; +} + +ConsoleMethod( GuiFilterCtrl, setValue, void, 3, 20, "(f1, f2, ...)" + "Reset the filter to use the specified points, spread equidistantly across the domain." + "@internal") +{ + Filter filter; + + argc -= 2; + argv += 2; + + filter.set(argc, argv); + object->set(filter); +} + +ConsoleMethod( GuiFilterCtrl, identity, void, 2, 2, "Reset the filtering." + "@internal") +{ + object->identity(); +} + +bool GuiFilterCtrl::onWake() +{ + if (!Parent::onWake()) + return false; + + if (U32(mControlPointRequest) != mFilter.size()) + { + mFilter.setSize(mControlPointRequest); + identity(); + } + + mCurKnot = -1; + + return true; +} + + +void GuiFilterCtrl::identity() +{ + S32 size = mFilter.size()-1; + for (U32 i=0; S32(i) <= size; i++) + { + F32 step = (F32)i/(F32)size; + mFilter[i] = mLerp( mIdentity.x, mIdentity.y, step ); + } +} + + +void GuiFilterCtrl::onMouseDown(const GuiEvent &event) +{ + mouseLock(); + setFirstResponder(); + + Point2I p = globalToLocalCoord(event.mousePoint); + + // determine which knot (offset same as in onRender) + F32 w = F32(getWidth()-4) / F32(mFilter.size()-1); + F32 val = (F32(p.x) + (w / 2.f)) / w; + mCurKnot = S32(val); + + mFilter[mCurKnot] = 1.0f - F32(getMin(getMax(0, p.y), getHeight())/(F32)getHeight()); + setUpdate(); +} + + +void GuiFilterCtrl::onMouseDragged(const GuiEvent &event) +{ + if ( mCurKnot < 0 ) + return; + + mouseLock(); + setFirstResponder(); + + Point2I p = globalToLocalCoord(event.mousePoint); + mFilter[mCurKnot] = 1.0f - F32(getMin(getMax(0, p.y), getHeight())/(F32)getHeight()); + setUpdate(); +} + +void GuiFilterCtrl::onMouseUp(const GuiEvent &) +{ + if ( mCurKnot < 0 ) + return; + + mouseUnlock(); + execConsoleCallback(); +} + +void GuiFilterCtrl::onPreRender() +{ + if(U32(mControlPointRequest) != mFilter.size()) + { + mFilter.setSize(mControlPointRequest); + identity(); + setUpdate(); + } +} + +void GuiFilterCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + Point2I pos = offset; + Point2I ext = getExtent(); + + RectI r(pos, ext); + GFX->getDrawUtil()->drawRectFill(r, ColorI(255,255,255)); + GFX->getDrawUtil()->drawRect(r, ColorI(0,0,0)); + + // shrink by 2 pixels + pos.x += 2; + pos.y += 2; + ext.x -= 4; + ext.y -= 4; + + // draw the identity line + if ( mShowIdentity ) + { + GFX->getDrawUtil()->drawLine( pos.x, pos.y + ( ext.y * ( 1.0f - mIdentity.x ) ), + pos.x + ext.x, pos.y + ( ext.y * ( 1.0f - mIdentity.y ) ), + ColorF( 0.9f, 0.9f, 0.9f ) ); + } + + // draw the curv + GFXVertexBufferHandle verts(GFX, ext.x, GFXBufferTypeVolatile); + + verts.lock(); + + F32 scale = 1.f / F32( ext.x ); + for ( U32 i = 0; i < ext.x; i++) + { + F32 index = F32(i) * scale; + S32 y = (S32)(ext.y*(1.0f-mFilter.getValue(index))); + + verts[i].point.set( (F32)(pos.x + i), (F32)(pos.y + y), 0.0f ); + verts[i].color = GFXVertexColor( ColorF( 0.4f, 0.4f, 0.4f ) ); + } + + verts.unlock(); + + GFX->setVertexBuffer( verts ); + GFX->drawPrimitive( GFXLineStrip, 0, ext.x - 1 ); + + // draw the knots + for (U32 k=0; k < mFilter.size(); k++) + { + RectI r; + r.point.x = (S32)(((F32)ext.x/(F32)(mFilter.size()-1)*(F32)k)); + r.point.y = (S32)(ext.y - ((F32)ext.y * mFilter[k])); + r.point += pos + Point2I(-2,-2); + r.extent = Point2I(5,5); + + GFX->getDrawUtil()->drawRectFill(r, ColorI(255,0,0)); + } + + renderChildControls(offset, updateRect); +} + + + +//-------------------------------------- +void Filter::set(S32 argc, const char *argv[]) +{ + setSize(0); + if (argc == 1) + { // in the form of one string "1.0 1.0 1.0" + char list[1024]; + dStrcpy(list, *argv); // strtok modifies the string so we need to copy it + char *value = dStrtok(list, " "); + while (value) + { + push_back(dAtof(value)); + value = dStrtok(NULL, " "); + } + } + else + { // in the form of seperate strings "1.0" "1.0" "1.0" + for (; argc ; argc--, argv++) + push_back(dAtof(*argv)); + } +} + + +//-------------------------------------- +F32 Filter::getValue(F32 x) const +{ + if (size() < 2) + return 0.0f; + + x = mClampF(x, 0.0f, 1.0f); + x *= F32(size()-1); + + F32 p0,p1,p2,p3; + S32 i1 = (S32)mFloor(x); + S32 i2 = i1+1; + F32 dt = x - F32(i1); + + p1 = *(begin()+i1); + p2 = *(begin()+i2); + + if (i1 == 0) + p0 = p1 + (p1 - p2); + else + p0 = *(begin()+i1-1); + + if (i2 == S32(size()-1)) + p3 = p2 + (p2 - p1); + else + p3 = *(begin()+i2+1); + + return mClampF( mCatmullrom(dt, p0, p1, p2, p3), 0.0f, 1.0f ); +} + + + + + + diff --git a/Engine/source/gui/editor/guiFilterCtrl.h b/Engine/source/gui/editor/guiFilterCtrl.h new file mode 100644 index 000000000..b40dee26b --- /dev/null +++ b/Engine/source/gui/editor/guiFilterCtrl.h @@ -0,0 +1,99 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIFILTERCTRL_H_ +#define _GUIFILTERCTRL_H_ + +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif + + +class Filter: public Vector +{ + public: + Filter() : Vector(__FILE__, __LINE__) { } + + void set(S32 argc, const char *argv[]); + F32 getValue(F32 t) const; +}; + + +class GuiFilterCtrl : public GuiControl +{ +protected: + + typedef GuiControl Parent; + + S32 mControlPointRequest; + + S32 mCurKnot; + + Filter mFilter; + + bool mShowIdentity; + + Point2F mIdentity; + +public: + + //creation methods + DECLARE_CONOBJECT(GuiFilterCtrl); + DECLARE_CATEGORY( "Gui Other" ); + DECLARE_DESCRIPTION( "A control that displays a Catmull-Rom spline through a number of control points\n" + "and allows to edit the Y-coordinates of the control points to adjust the curve." ); + + GuiFilterCtrl(); + static void initPersistFields(); + + //Parental methods + bool onWake(); + + void onMouseDown(const GuiEvent &event); + void onMouseDragged(const GuiEvent &event); + void onMouseUp(const GuiEvent &); + + F32 getValue(S32 n); + const Filter* get() { return &mFilter; } + void set(const Filter &f); + S32 getNumControlPoints() {return mFilter.size(); } + void identity(); + + void onPreRender(); + void onRender(Point2I offset, const RectI &updateRect ); +}; + + +inline F32 GuiFilterCtrl::getValue(S32 n) +{ + S32 index = getMin(getMax(n,0), (S32)mFilter.size()-1); + return mFilter[index]; +} + + +inline void GuiFilterCtrl::set(const Filter &f) +{ + mControlPointRequest = f.size(); + mFilter = f; +} + +#endif diff --git a/Engine/source/gui/editor/guiGraphCtrl.cpp b/Engine/source/gui/editor/guiGraphCtrl.cpp new file mode 100644 index 000000000..f056bb44d --- /dev/null +++ b/Engine/source/gui/editor/guiGraphCtrl.cpp @@ -0,0 +1,430 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/editor/guiGraphCtrl.h" + +#include "console/console.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxDrawUtil.h" +#include "gfx/primBuilder.h" + + +IMPLEMENT_CONOBJECT( GuiGraphCtrl ); + +ConsoleDocClass( GuiGraphCtrl, + "@brief A control that plots one or more curves in a chart.\n\n" + + "Up to 6 individual curves can be plotted in the graph. Each plotted curve can have its own display style including its " + "own charting style (#plotType) and color (#plotColor).\n\n" + + "The data points on each curve can be added in one of two ways:\n\n" + + "- Manually by calling addDatum(). This causes new data points to be added to the left end of the plotting curve.\n" + "- Automatically by letting the graph plot the values of a variable over time. This is achieved by calling addAutoPlot " + "and specifying the variable and update frequency.\n\n" + + "@tsexample\n" + "// Create a graph that plots a red polyline graph of the FPS counter in a 1 second (1000 milliseconds) interval.\n" + "new GuiGraphCtrl( FPSGraph )\n" + "{\n" + " plotType[ 0 ] = \"PolyLine\";\n" + " plotColor[ 0 ] = \"1 0 0\";\n" + " plotVariable[ 0 ] = \"fps::real\";\n" + " plotInterval[ 0 ] = 1000;\n" + "};\n" + "@endtsexample\n\n" + + "@note Each curve has a maximum number of 200 data points it can have at any one time. Adding more data points to a curve " + "will cause older data points to be removed.\n\n" + + "@ingroup GuiValues" +); + +ImplementEnumType( GuiGraphType, + "The charting style of a single plotting curve in a GuiGraphCtrl.\n\n" + "@ingroup GuiValues" ) + { GuiGraphCtrl::Bar, "Bar", "Plot the curve as a bar chart." }, + { GuiGraphCtrl::Filled, "Filled", "Plot a filled poly graph that connects the data points on the curve." }, + { GuiGraphCtrl::Point, "Point", "Plot each data point on the curve as a single dot." }, + { GuiGraphCtrl::Polyline , "PolyLine", "Plot straight lines through the data points of the curve." }, +EndImplementEnumType; + + +//----------------------------------------------------------------------------- + +GuiGraphCtrl::GuiGraphCtrl() + : mCenterY( 1.f ) +{ + dMemset( mAutoPlot, 0, sizeof( mAutoPlot ) ); + dMemset( mAutoPlotDelay, 0, sizeof( mAutoPlotDelay ) ); + dMemset( mGraphMax, 0, sizeof( mGraphMax ) ); + + for( S32 i = 0; i < MaxPlots; ++ i ) + { + mGraphType[ i ] = Polyline; + + VECTOR_SET_ASSOCIATION( mGraphData[ i ] ); + } + + AssertWarn( MaxPlots == 6, "Only 6 plot colors initialized. Update following code if you change MaxPlots." ); + + mGraphColor[ 0 ] = ColorF( 1.0, 1.0, 1.0 ); + mGraphColor[ 1 ] = ColorF( 1.0, 0.0, 0.0 ); + mGraphColor[ 2 ] = ColorF( 0.0, 1.0, 0.0 ); + mGraphColor[ 3 ] = ColorF( 0.0, 0.0, 1.0 ); + mGraphColor[ 4 ] = ColorF( 0.0, 1.0, 1.0 ); + mGraphColor[ 5 ] = ColorF( 0.0, 0.0, 0.0 ); +} + +//----------------------------------------------------------------------------- + +void GuiGraphCtrl::initPersistFields() +{ + addGroup( "Graph" ); + + addField( "centerY", TypeF32, Offset( mCenterY, GuiGraphCtrl ), + "Ratio of where to place the center coordinate of the graph on the Y axis. 0.5=middle height of control.\n\n" + "This allows to account for graphs that have only positive or only negative data points, for example." ); + + addField( "plotColor", TypeColorF, Offset( mGraphColor, GuiGraphCtrl ), MaxPlots, + "Color to use for the plotting curves in the graph." ); + + addField( "plotType", TYPEID< GuiGraphType >(), Offset( mGraphType, GuiGraphCtrl ), MaxPlots, + "Charting type of the plotting curves." ); + + addField( "plotVariable", TypeString, Offset( mAutoPlot, GuiGraphCtrl ), MaxPlots, + "Name of the variable to automatically plot on the curves. If empty, auto-plotting " + "is disabled for the respective curve." ); + + addField( "plotInterval", TypeS32, Offset( mAutoPlotDelay, GuiGraphCtrl ), MaxPlots, + "Interval between auto-plots of #plotVariable for the respective curve (in milliseconds)." ); + + endGroup( "Graph" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +void GuiGraphCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + if (mBlendSB.isNull()) + { + GFXStateBlockDesc desc; + + desc.setBlend(true, GFXBlendSrcColor, GFXBlendInvSrcColor); + mBlendSB = GFX->createStateBlock(desc); + + desc.setBlend(false, GFXBlendOne, GFXBlendZero); + mSolidSB = GFX->createStateBlock(desc); + + } + + GFX->setStateBlock( mBlendSB ); + + GFX->getDrawUtil()->drawRectFill( updateRect, mProfile->mFillColor ); + + GFX->setStateBlock( mSolidSB ); + + const Point2I globalPos = getGlobalBounds().point; + const F32 midPointY = F32( globalPos.y ) + F32( getExtent().y ) * mCenterY; + + for( S32 k = 0; k < MaxPlots; ++ k ) + { + // Check if there is an autoplot and the proper amount of time has passed, if so add datum. + if( mAutoPlot[ k ] && mAutoPlotDelay[ k ] < (Sim::getCurrentTime() - mAutoPlotLastDisplay[ k ] ) ) + { + mAutoPlotLastDisplay[ k ] = Sim::getCurrentTime(); + addDatum( k, Con::getFloatVariable( mAutoPlot[ k ] ) ); + } + + // Nothing to graph + if( mGraphData[ k ].size() == 0 ) + continue; + + // Adjust scale to max value + 5% so we can see high values + F32 Scale = F32( getExtent().y ) / F32( mGraphMax[ k ] * 1.05 ); + + const S32 numSamples = mGraphData[ k ].size(); + + switch( mGraphType[ k ] ) + { + case Bar: + { + F32 prevOffset = 0; + + for( S32 sample = 0; sample < numSamples; ++ sample ) + { + PrimBuild::begin( GFXTriangleFan, 4 ); + PrimBuild::color( mGraphColor[ k ] ); + + F32 offset = F32( getExtent().x ) / F32( MaxDataPoints ) * F32( sample + 1 ); + + PrimBuild::vertex2f( globalPos.x + prevOffset, + midPointY - ( getDatum( k, sample ) * Scale ) ); + + PrimBuild::vertex2f( globalPos.x + offset, + midPointY - ( getDatum( k, sample ) * Scale ) ); + + PrimBuild::vertex2f( globalPos.x + offset, + midPointY ); + + PrimBuild::vertex2f( globalPos.x + prevOffset, + midPointY ); + + prevOffset = offset; + + PrimBuild::end(); + } + + break; + } + + case Filled: + { + PrimBuild::begin( GFXTriangleStrip, numSamples * 2 ); // Max size correct? -pw + PrimBuild::color( mGraphColor[ k ] ); + + for( S32 sample = 0; sample < numSamples; ++ sample ) + { + F32 offset = F32( getExtent().x ) / F32( MaxDataPoints - 1 ) * F32( sample ); + + PrimBuild::vertex2f( globalPos.x + offset, + midPointY ); + + PrimBuild::vertex2f( globalPos.x + offset, + midPointY - ( getDatum( k, sample ) * Scale ) ); + } + + PrimBuild::end(); + break; + } + + case Point: + case Polyline: + { + if( mGraphType[ k ] == Point ) + PrimBuild::begin( GFXPointList, numSamples ); // Max size correct? -pw + else + PrimBuild::begin( GFXLineStrip, numSamples ); + + PrimBuild::color( mGraphColor[ k ] ); + + for( S32 sample = 0; sample < numSamples; ++ sample ) + { + F32 offset = F32( getExtent().x ) / F32( MaxDataPoints - 1 ) * F32( sample ); + + PrimBuild::vertex2f( globalPos.x + offset, + midPointY - ( getDatum( k, sample ) * Scale ) ); + } + + PrimBuild::end(); + break; + } + } + } + + if( mProfile->mBorder ) + { + RectI rect( offset.x, offset.y, getWidth(), getHeight() ); + GFX->getDrawUtil()->drawRect( rect, mProfile->mBorderColor ); + } + + renderChildControls( offset, updateRect ); +} + +//----------------------------------------------------------------------------- + +void GuiGraphCtrl::addDatum(S32 plotID, F32 v) +{ + AssertFatal(plotID > -1 && plotID < MaxPlots, "Invalid plot specified!"); + + // Add the data and trim the vector... + mGraphData[ plotID ].push_front( v ); + + if( mGraphData[ plotID ].size() > MaxDataPoints ) + mGraphData[ plotID ].pop_back(); + + // Keep record of maximum data value for scaling purposes. + if( v > mGraphMax[ plotID ] ) + mGraphMax[ plotID ] = v; +} + +//----------------------------------------------------------------------------- + +float GuiGraphCtrl::getDatum( int plotID, int sample) +{ + AssertFatal(plotID > -1 && plotID < MaxPlots, "Invalid plot specified!"); + AssertFatal(sample > -1 && sample < MaxDataPoints, "Invalid sample specified!"); + return mGraphData[ plotID ][sample]; +} + +//----------------------------------------------------------------------------- + +void GuiGraphCtrl::addAutoPlot( S32 plotID, const char* variable, S32 update ) +{ + mAutoPlot[ plotID ] = StringTable->insert( variable ); + mAutoPlotDelay[ plotID ] = update; +} + +//----------------------------------------------------------------------------- + +void GuiGraphCtrl::removeAutoPlot( S32 plotID ) +{ + mAutoPlot[ plotID ] = NULL; +} + +//----------------------------------------------------------------------------- + +void GuiGraphCtrl::setGraphType( S32 plotID, GraphType graphType ) +{ + AssertFatal( plotID > -1 && plotID < MaxPlots, "Invalid plot specified!" ); + mGraphType[ plotID ] = graphType; +} + +//============================================================================= +// Console Methods. +//============================================================================= +// MARK: ---- Console Methods ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiGraphCtrl, addDatum, void, ( S32 plotId, F32 value ),, + "Add a data point to the plot's curve.\n\n" + "@param plotId Index of the plotting curve to which to add the data point. Must be 0<=plotId<6.\n" + "@param value Value of the data point to add to the curve.\n\n" + "@note Data values are added to the @b left end of the plotting curve.\n\n" + "@note A maximum number of 200 data points can be added to any single plotting curve at any one time. If " + "this limit is exceeded, data points on the right end of the curve are culled." ) +{ + if( plotId > object->MaxPlots ) + { + Con::errorf( "GuiGraphCtrl::addDatum - 'plotId' out of range: %i", plotId ); + return; + } + + object->addDatum( plotId, value ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiGraphCtrl, getDatum, F32, ( S32 plotId, S32 index ),, + "Get a data point on the given plotting curve.\n\n" + "@param plotId Index of the plotting curve from which to fetch the data point. Must be 0<=plotId<6.\n" + "@param index Index of the data point on the curve.\n" + "@return The value of the data point or -1 if @a plotId or @a index are out of range." ) +{ + if( plotId > object->MaxPlots ) + { + Con::errorf( "GuiGraphCtrl::getDatum - 'plotId' out of range: %i", plotId ); + return -1.f; + } + + if( index > object->MaxDataPoints) + { + Con::errorf( "GuiGraphCtrl::getDatum - Data point index out of range: %i", index ); + return -1.f; + } + + return object->getDatum( plotId, index ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiGraphCtrl, addAutoPlot, void, ( S32 plotId, const char* variable, S32 updateFrequency ),, + "Sets up the given plotting curve to automatically plot the value of the @a variable with a " + "frequency of @a updateFrequency.\n" + "@param plotId Index of the plotting curve. Must be 0<=plotId<6.\n" + "@param variable Name of the global variable.\n" + "@param updateFrequency Frequency with which to add new data points to the plotting curve (in milliseconds).\n" + "@tsexample\n" + "// Plot FPS counter at 1 second intervals.\n" + "%graph.addAutoPlot( 0, \"fps::real\", 1000 );\n" + "@endtsexample" ) +{ + if( plotId > object->MaxPlots ) + { + Con::errorf( "GuiGraphCtrl::removeAutoPlot - 'plotId' out of range: %i", plotId ); + return; + } + + object->addAutoPlot( plotId, variable, updateFrequency ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiGraphCtrl, removeAutoPlot, void, ( S32 plotId ),, + "Stop automatic variable plotting for the given curve.\n" + "@param plotId Index of the plotting curve. Must be 0<=plotId<6.\n" ) +{ + if( plotId > object->MaxPlots ) + { + Con::errorf( "GuiGraphCtrl::removeAutoPlot - 'plotId' out of range: %i", plotId ); + return; + } + + object->removeAutoPlot( plotId ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiGraphCtrl, setGraphType, void, ( S32 plotId, GuiGraphType graphType ),, + "Change the charting type of the given plotting curve.\n" + "@param plotId Index of the plotting curve. Must be 0<=plotId<6.\n" + "@param graphType Charting type to use for the curve.\n" + "@note Instead of calling this method, you can directly assign to #plotType." ) +{ + if( plotId > object->MaxPlots ) + { + Con::errorf( "GuiGraphCtrl::setGraphType - 'plotId' out of range: %i", plotId ); + return; + } + + object->setGraphType( plotId, graphType ); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiGraphCtrl, matchScale, void, 3, GuiGraphCtrl::MaxPlots + 2, "( int plotID1, int plotID2, ... ) " + "Set the scale of all specified plots to the maximum scale among them.\n\n" + "@param plotID1 Index of plotting curve.\n" + "@param plotID2 Index of plotting curve." ) +{ + F32 max = 0; + for( S32 i = 0; i < ( argc - 2 ); ++ i ) + { + S32 plotID = dAtoi( argv[ 2 + i ] ); + if( plotID > object->MaxPlots ) + { + Con::errorf( "GuiGraphCtrl::matchScale - Plot ID out of range: %i", plotID ); + return; + } + + max = getMax( object->getMax( plotID ), max ); + } + + for( S32 i = 0; i < ( argc - 2 ); ++ i ) + object->setMax( dAtoi( argv[ 2 + i ] ), max ); +} diff --git a/Engine/source/gui/editor/guiGraphCtrl.h b/Engine/source/gui/editor/guiGraphCtrl.h new file mode 100644 index 000000000..7aca2c4ff --- /dev/null +++ b/Engine/source/gui/editor/guiGraphCtrl.h @@ -0,0 +1,88 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIGRAPHCTRL_H_ +#define _GUIGRAPHCTRL_H_ + +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif + +class GuiGraphCtrl : public GuiControl +{ + public: + + typedef GuiControl Parent; + + enum Constants + { + MaxPlots = 6, + MaxDataPoints = 200 + }; + + enum GraphType { + Point, + Polyline, + Filled, + Bar + }; + + protected: + + F32 mCenterY; + StringTableEntry mAutoPlot[ MaxPlots ]; + U32 mAutoPlotDelay[ MaxPlots ]; + SimTime mAutoPlotLastDisplay[ MaxPlots ]; + ColorF mGraphColor[ MaxPlots ]; + Vector< F32 > mGraphData[ MaxPlots ]; + F32 mGraphMax[ MaxPlots ]; + GraphType mGraphType[ MaxPlots ]; + + GFXStateBlockRef mSolidSB; + GFXStateBlockRef mBlendSB; + + public: + + GuiGraphCtrl(); + + void addDatum( S32 plotID, F32 v ); + F32 getDatum( S32 plotID, S32 samples ); + void addAutoPlot( S32 plotID, const char *variable, S32 update ); + void removeAutoPlot( S32 plotID); + void setGraphType( S32 plotID, GraphType graphType ); + F32 getMax( S32 plotID ) const { return mGraphMax[ plotID ]; } + void setMax( S32 plotID, F32 max ) { mGraphMax[ plotID ] = max; } + + // GuiControl. + virtual void onRender(Point2I offset, const RectI &updateRect); + + DECLARE_CONOBJECT(GuiGraphCtrl); + DECLARE_CATEGORY( "Gui Other" ); + DECLARE_DESCRIPTION( "A control that allows to plot curve graphs." ); + + static void initPersistFields(); +}; + +typedef GuiGraphCtrl::GraphType GuiGraphType; +DefineEnumType( GuiGraphType ); + +#endif diff --git a/Engine/source/gui/editor/guiImageList.cpp b/Engine/source/gui/editor/guiImageList.cpp new file mode 100644 index 000000000..18cb973ed --- /dev/null +++ b/Engine/source/gui/editor/guiImageList.cpp @@ -0,0 +1,259 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/consoleTypes.h" +#include "console/console.h" +#include "gfx/gfxDevice.h" +#include "gui/editor/guiImageList.h" +#include "console/engineAPI.h" + +IMPLEMENT_CONOBJECT(GuiImageList); + +ConsoleDocClass( GuiImageList, + "@brief GUI control which displays a list of images.\n\n" + "Used to be a part of an old editor system for previous Torque systems. " + "Doesn't appear to be used anymore, will most likely be deprecated.\n\n" + "@ingroup GuiCore\n" + "@internal"); + +GuiImageList::GuiImageList() +{ + VECTOR_SET_ASSOCIATION(mTextures); + mTextures.clear(); + mUniqueId = 0; +} + +U32 GuiImageList::Insert( const char* texturePath, GFXTextureProfile *Type ) +{ + TextureEntry *t = new TextureEntry; + + if ( ! t ) return -1; + + t->TexturePath = StringTable->insert(texturePath); + if ( *t->TexturePath ) + { + t->Handle = GFXTexHandle(t->TexturePath, Type, avar("%s() - t->Handle (line %d)", __FUNCTION__, __LINE__)); + + if ( t->Handle ) + { + t->id = ++mUniqueId; + + mTextures.push_back( t ); + + return t->id; + + } + } + + // Free Texture Entry. + delete t; + + // Return Failure. + return -1; + +} + +bool GuiImageList::Clear() +{ + while ( mTextures.size() ) + FreeTextureEntry( mTextures[0] ); + mTextures.clear(); + + mUniqueId = 0; + + return true; +} + +bool GuiImageList::FreeTextureEntry( U32 Index ) +{ + U32 Id = IndexFromId( Index ); + if ( Id != -1 ) + return FreeTextureEntry( mTextures[ Id ] ); + else + return false; +} + +bool GuiImageList::FreeTextureEntry( PTextureEntry Entry ) +{ + if ( ! Entry ) + return false; + + U32 id = IndexFromId( Entry->id ); + + delete Entry; + + mTextures.erase ( id ); + + return true; +} + +U32 GuiImageList::IndexFromId ( U32 Id ) +{ + if ( !mTextures.size() ) return -1; + Vector::iterator i = mTextures.begin(); + U32 j = 0; + for ( ; i != mTextures.end(); i++ ) + { + if ( i ) + { + if ( (*i)->id == Id ) + return j; + j++; + } + } + + return -1; +} + +U32 GuiImageList::IndexFromPath ( const char* Path ) +{ + if ( !mTextures.size() ) return -1; + Vector::iterator i = mTextures.begin(); + for ( ; i != mTextures.end(); i++ ) + { + if ( dStricmp( Path, (*i)->TexturePath ) == 0 ) + return (*i)->id; + } + + return -1; +} + +void GuiImageList::initPersistFields() +{ + Parent::initPersistFields(); +} + +DefineEngineMethod( GuiImageList, getImage, const char*, (int index),, + "@brief Get a path to the texture at the specified index.\n\n" + "@param index Index of the image in the list.\n" + "@tsexample\n" + "// Define the image index/n" + "%index = \"5\";\n\n" + "// Request the image path location from the control.\n" + "%imagePath = %thisGuiImageList.getImage(%index);\n" + "@endtsexample\n\n" + "@return File path to the image map for the specified index.\n\n" + "@see SimObject") +{ + return object->GetTexturePath(index); +} + +DefineEngineMethod(GuiImageList, clear, bool, (),, + "@brief Clears the imagelist\n\n" + "@tsexample\n" + "// Inform the GuiImageList control to clear itself.\n" + "%isFinished = %thisGuiImageList.clear();\n" + "@endtsexample\n\n" + "@return Returns true when finished.\n\n" + "@see SimObject") +{ + return object->Clear(); +} + +DefineEngineMethod( GuiImageList, count, S32, (),, + "@brief Gets the number of images in the list.\n\n" + "@tsexample\n" + "// Request the number of images from the GuiImageList control.\n" + "%imageCount = %thisGuiImageList.count();\n" + "@endtsexample\n\n" + "@return Number of images in the control.\n\n" + "@see SimObject") +{ + return object->Count(); +} + +DefineEngineMethod( GuiImageList, remove, bool, (int index),, + "@brief Removes an image from the list by index.\n\n" + "@param index Image index to remove.\n" + "@tsexample\n" + "// Define the image index.\n" + "%imageIndex = \"4\";\n\n" + "// Inform the GuiImageList control to remove the image at the defined index.\n" + "%wasSuccessful = %thisGuiImageList.remove(%imageIndex);\n" + "@endtsexample\n\n" + "@return True if the operation was successful, false if it was not.\n\n" + "@see SimObject") +{ + return object->FreeTextureEntry( index ); +} + +DefineEngineMethod( GuiImageList, getIndex, S32, (const char* imagePath),, + "@brief Retrieves the imageindex of a specified texture in the list.\n\n" + "@param imagePath Imagemap including filepath of image to search for\n" + "@tsexample\n" + "// Define the imagemap to search for\n" + "%imagePath = \"./game/client/data/images/thisImage\";\n\n" + "// Request the index entry for the defined imagemap\n" + "%imageIndex = %thisGuiImageList.getIndex(%imagePath);\n" + "@endtsexample\n\n" + "@return Index of the imagemap matching the defined image path.\n\n" + "@see SimObject") +{ + return object->IndexFromPath( imagePath ); +} + +DefineEngineMethod(GuiImageList, insert, S32, (const char* imagePath),, + "@brief Insert an image into imagelist- returns the image index or -1 for failure.\n\n" + "@param imagePath Imagemap, with path, to add to the list.\n" + "@tsexample\n" + "// Define the imagemap to add to the list\n" + "%imagePath = \"./game/client/data/images/thisImage\";\n\n" + "// Request the GuiImageList control to add the defined image to its list.\n" + "%imageIndex = %thisGuiImageList.insert(%imagePath);\n" + "@endtsexample\n\n" + "@return The index of the newly inserted imagemap, or -1 if the insertion failed.\n\n" + "@see SimObject") +{ + return object->Insert( imagePath ); +} + +GFXTexHandle GuiImageList::GetTextureHandle( U32 Index ) +{ + U32 ItemIndex = IndexFromId(Index); + if ( ItemIndex != -1 ) + return mTextures[ItemIndex]->Handle; + else + return NULL; + +} +GFXTexHandle GuiImageList::GetTextureHandle( const char* TexturePath ) +{ + Vector::iterator i = mTextures.begin(); + for ( ; i != mTextures.end(); i++ ) + { + if ( dStricmp( TexturePath, (*i)->TexturePath ) == 0 ) + return (*i)->Handle; + } + + return NULL; +} + + +const char *GuiImageList::GetTexturePath( U32 Index ) +{ + U32 ItemIndex = IndexFromId(Index); + if ( ItemIndex != -1 ) + return mTextures[ItemIndex]->TexturePath; + else + return ""; +} diff --git a/Engine/source/gui/editor/guiImageList.h b/Engine/source/gui/editor/guiImageList.h new file mode 100644 index 000000000..389d8cf58 --- /dev/null +++ b/Engine/source/gui/editor/guiImageList.h @@ -0,0 +1,78 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIIMAGELIST_H_ +#define _GUIIMAGELIST_H_ + +#include "console/simDatablock.h" + +#ifndef _GUITYPES_H_ +#include "gui/core/guiTypes.h" +#endif + + +class GuiImageList : public SimObject +{ + private: + typedef SimObject Parent; + + public: + typedef struct tag_TextureEntry + { + StringTableEntry TexturePath; + GFXTexHandle Handle; + U32 id; + }TextureEntry,*PTextureEntry; + + Vector mTextures; + + protected: + + + U32 mUniqueId; + + public: + GuiImageList(); + + DECLARE_CONOBJECT(GuiImageList); + + static void initPersistFields(); + + // Image managing functions + bool Clear(); + inline U32 Count() { return (U32)mTextures.size(); }; + U32 Insert( const char* texturePath , GFXTextureProfile *Type = &GFXDefaultGUIProfile ); + + bool FreeTextureEntry( U32 Index ); + bool FreeTextureEntry( PTextureEntry Entry ); + + GFXTexHandle GetTextureHandle( U32 Index ); + GFXTexHandle GetTextureHandle( const char* TexturePath ); + + const char * GetTexturePath( U32 Index ); + + U32 IndexFromId ( U32 Id ); + U32 IndexFromPath ( const char* Path ); + +}; + +#endif //_GUIIMAGELISTCTRL_H_ diff --git a/Engine/source/gui/editor/guiInspector.cpp b/Engine/source/gui/editor/guiInspector.cpp new file mode 100644 index 000000000..dab92cafb --- /dev/null +++ b/Engine/source/gui/editor/guiInspector.cpp @@ -0,0 +1,893 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/editor/guiInspector.h" +#include "gui/editor/inspector/field.h" +#include "gui/editor/inspector/group.h" +#include "gui/buttons/guiIconButtonCtrl.h" +#include "gui/editor/inspector/dynamicGroup.h" +#include "gui/containers/guiScrollCtrl.h" +#include "gui/editor/inspector/customField.h" + + +IMPLEMENT_CONOBJECT(GuiInspector); + +ConsoleDocClass( GuiInspector, + "@brief A control that allows to edit the properties of one or more SimObjects.\n\n" + "Editor use only.\n\n" + "@internal" +); + + +//#define DEBUG_SPEW + + +//----------------------------------------------------------------------------- + +GuiInspector::GuiInspector() + : mDividerPos( 0.35f ), + mDividerMargin( 5 ), + mOverDivider( false ), + mMovingDivider( false ), + mHLField( NULL ), + mShowCustomFields( true ) +{ + mPadding = 1; +} + +//----------------------------------------------------------------------------- + +GuiInspector::~GuiInspector() +{ + clearGroups(); +} + +//----------------------------------------------------------------------------- + +void GuiInspector::initPersistFields() +{ + addGroup( "Inspector" ); + + addField( "dividerMargin", TypeS32, Offset( mDividerMargin, GuiInspector ) ); + + addField( "groupFilters", TypeRealString, Offset( mGroupFilters, GuiInspector ), + "Specify groups that should be shown or not. Specifying 'shown' implicitly does 'not show' all other groups. Example string: +name -otherName" ); + + addField( "showCustomFields", TypeBool, Offset( mShowCustomFields, GuiInspector ), + "If false the custom fields Name, Id, and Source Class will not be shown." ); + + endGroup( "Inspector" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +void GuiInspector::onRemove() +{ + clearGroups(); + Parent::onRemove(); +} + +//----------------------------------------------------------------------------- + +void GuiInspector::onDeleteNotify( SimObject *object ) +{ + Parent::onDeleteNotify( object ); + + if( isInspectingObject( object ) ) + removeInspectObject( object ); +} + +//----------------------------------------------------------------------------- + +void GuiInspector::parentResized(const RectI &oldParentRect, const RectI &newParentRect) +{ + GuiControl *parent = getParent(); + if ( parent && dynamic_cast(parent) != NULL ) + { + GuiScrollCtrl *scroll = dynamic_cast(parent); + setWidth( ( newParentRect.extent.x - ( scroll->scrollBarThickness() + 4 ) ) ); + } + else + Parent::parentResized(oldParentRect,newParentRect); +} + +//----------------------------------------------------------------------------- + +bool GuiInspector::resize( const Point2I &newPosition, const Point2I &newExtent ) +{ + //F32 dividerPerc = (F32)getWidth() / (F32)mDividerPos; + + bool result = Parent::resize( newPosition, newExtent ); + + //mDividerPos = (F32)getWidth() * dividerPerc; + + updateDivider(); + + return result; +} + +//----------------------------------------------------------------------------- + +GuiControl* GuiInspector::findHitControl( const Point2I &pt, S32 initialLayer ) +{ + if ( mOverDivider || mMovingDivider ) + return this; + + return Parent::findHitControl( pt, initialLayer ); +} + +//----------------------------------------------------------------------------- + +void GuiInspector::getCursor( GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent ) +{ + GuiCanvas *pRoot = getRoot(); + if( !pRoot ) + return; + + S32 desiredCursor = mOverDivider ? PlatformCursorController::curResizeVert : PlatformCursorController::curArrow; + + // Bail if we're already at the desired cursor + if ( pRoot->mCursorChanged == desiredCursor ) + return; + + PlatformWindow *pWindow = static_cast(getRoot())->getPlatformWindow(); + AssertFatal(pWindow != NULL,"GuiControl without owning platform window! This should not be possible."); + PlatformCursorController *pController = pWindow->getCursorController(); + AssertFatal(pController != NULL,"PlatformWindow without an owned CursorController!"); + + // Now change the cursor shape + pController->popCursor(); + pController->pushCursor(desiredCursor); + pRoot->mCursorChanged = desiredCursor; +} + +//----------------------------------------------------------------------------- + +void GuiInspector::onMouseMove(const GuiEvent &event) +{ + if ( collideDivider( globalToLocalCoord( event.mousePoint ) ) ) + mOverDivider = true; + else + mOverDivider = false; +} + +//----------------------------------------------------------------------------- + +void GuiInspector::onMouseDown(const GuiEvent &event) +{ + if ( mOverDivider ) + { + mMovingDivider = true; + } +} + +//----------------------------------------------------------------------------- + +void GuiInspector::onMouseUp(const GuiEvent &event) +{ + mMovingDivider = false; +} + +//----------------------------------------------------------------------------- + +void GuiInspector::onMouseDragged(const GuiEvent &event) +{ + if ( !mMovingDivider ) + return; + + Point2I localPnt = globalToLocalCoord( event.mousePoint ); + + S32 inspectorWidth = getWidth(); + + // Distance from mouse/divider position in local space + // to the right edge of the inspector + mDividerPos = inspectorWidth - localPnt.x; + mDividerPos = mClamp( mDividerPos, 0, inspectorWidth ); + + // Divide that by the inspectorWidth to get a percentage + mDividerPos /= inspectorWidth; + + updateDivider(); +} + +//----------------------------------------------------------------------------- + +GuiInspectorGroup* GuiInspector::findExistentGroup( StringTableEntry groupName ) +{ + // If we have no groups, it couldn't possibly exist + if( mGroups.empty() ) + return NULL; + + // Attempt to find it in the group list + Vector::iterator i = mGroups.begin(); + + for( ; i != mGroups.end(); i++ ) + { + if( dStricmp( (*i)->getGroupName(), groupName ) == 0 ) + return *i; + } + + return NULL; +} + +//----------------------------------------------------------------------------- + +void GuiInspector::updateFieldValue( StringTableEntry fieldName, StringTableEntry arrayIdx ) +{ + // We don't know which group contains the field of this name, + // so ask each group in turn, and break when a group returns true + // signifying it contained and updated that field. + + Vector::iterator groupIter = mGroups.begin(); + + for( ; groupIter != mGroups.end(); groupIter++ ) + { + if ( (*groupIter)->updateFieldValue( fieldName, arrayIdx ) ) + break; + } +} + +//----------------------------------------------------------------------------- + +void GuiInspector::clearGroups() +{ + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[GuiInspector] Clearing %i (%s)", getId(), getName() ); + #endif + + // If we have no groups, there's nothing to clear! + if( mGroups.empty() ) + return; + + mHLField = NULL; + + if( isMethod( "onClear" ) ) + Con::executef( this, "onClear" ); + + Vector::iterator i = mGroups.begin(); + + freeze(true); + + // Delete Groups + for( ; i != mGroups.end(); i++ ) + { + if((*i) && (*i)->isProperlyAdded()) + (*i)->deleteObject(); + } + + mGroups.clear(); + + freeze(false); + updatePanes(); +} + +//----------------------------------------------------------------------------- + +bool GuiInspector::isInspectingObject( SimObject* object ) +{ + const U32 numTargets = mTargets.size(); + for( U32 i = 0; i < numTargets; ++ i ) + if( mTargets[ i ] == object ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- + +void GuiInspector::inspectObject( SimObject *object ) +{ + if( mTargets.size() > 1 || !isInspectingObject( object ) ) + clearInspectObjects(); + + addInspectObject( object ); +} + +//----------------------------------------------------------------------------- + +void GuiInspector::clearInspectObjects() +{ + const U32 numTargets = mTargets.size(); + for( U32 i = 0; i < numTargets; ++ i ) + clearNotify( mTargets[ i ] ); + + clearGroups(); + mTargets.clear(); +} + +//----------------------------------------------------------------------------- + +void GuiInspector::addInspectObject( SimObject* object, bool autoSync ) +{ + // If we are already inspecting the object, just update the groups. + + if( isInspectingObject( object ) ) + { + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[GuiInspector] Refreshing view of %i:%s (%s)", + object->getId(), object->getClassName(), object->getName() ); + #endif + + Vector::iterator i = mGroups.begin(); + for ( ; i != mGroups.end(); i++ ) + (*i)->updateAllFields(); + + return; + } + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[GuiInspector] Adding %i:%s (%s) to inspect set", + object->getId(), object->getClassName(), object->getName() ); + #endif + + // Give users a chance to customize fields on this object + if( object->isMethod("onDefineFieldTypes") ) + Con::executef( object, "onDefineFieldTypes" ); + + // Set Target + mTargets.push_back( object ); + deleteNotify( object ); + + if( autoSync ) + refresh(); +} + +//----------------------------------------------------------------------------- + +void GuiInspector::removeInspectObject( SimObject* object ) +{ + const U32 numTargets = mTargets.size(); + for( U32 i = 0; i < numTargets; ++ i ) + if( mTargets[ i ] == object ) + { + // Delete all inspector data *before* removing the target so that apply calls + // triggered by edit controls losing focus will not find the inspect object + // gone. + + clearGroups(); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[GuiInspector] Removing %i:%s (%s) from inspect set", + object->getId(), object->getClassName(), object->getName() ); + #endif + + mTargets.erase( i ); + clearNotify( object ); + + // Refresh the inspector except if the system is going down. + + if( !Sim::isShuttingDown() ) + refresh(); + + return; + } +} + +//----------------------------------------------------------------------------- + +void GuiInspector::setName( StringTableEntry newName ) +{ + if( mTargets.size() != 1 ) + return; + + StringTableEntry name = StringTable->insert(newName); + + // Only assign a new name if we provide one + mTargets[ 0 ]->assignName(name); +} + +//----------------------------------------------------------------------------- + +bool GuiInspector::collideDivider( const Point2I &localPnt ) +{ + RectI divisorRect( getWidth() - getWidth() * mDividerPos - mDividerMargin, 0, mDividerMargin * 2, getHeight() ); + + if ( divisorRect.pointInRect( localPnt ) ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- + +void GuiInspector::updateDivider() +{ + for ( U32 i = 0; i < mGroups.size(); i++ ) + for ( U32 j = 0; j < mGroups[i]->mChildren.size(); j++ ) + mGroups[i]->mChildren[j]->updateRects(); + + //setUpdate(); +} + +//----------------------------------------------------------------------------- + +void GuiInspector::getDivider( S32 &pos, S32 &margin ) +{ + pos = (F32)getWidth() * mDividerPos; + margin = mDividerMargin; +} + +//----------------------------------------------------------------------------- + +void GuiInspector::setHighlightField( GuiInspectorField *field ) +{ + if ( mHLField == field ) + return; + + if ( mHLField.isValid() ) + mHLField->setHLEnabled( false ); + mHLField = field; + + // We could have been passed a null field, meaning, set no field highlighted. + if ( mHLField.isNull() ) + return; + + mHLField->setHLEnabled( true ); +} + +//----------------------------------------------------------------------------- + +bool GuiInspector::isGroupFiltered( const char *groupName ) const +{ + // Internal and Ungrouped always filtered, we never show them. + if ( dStricmp( groupName, "Internal" ) == 0 || + dStricmp( groupName, "Ungrouped" ) == 0 || + dStricmp( groupName, "AdvCoordManipulation" ) == 0) + return true; + + // Normal case, determine if filtered by looking at the mGroupFilters string. + String searchStr; + + // Is this group explicitly show? Does it immediately follow a + char. + searchStr = String::ToString( "+%s", groupName ); + if ( mGroupFilters.find( searchStr ) != String::NPos ) + return false; + + // Were there any other + characters, if so, we are implicitly hidden. + if ( mGroupFilters.find( "+" ) != String::NPos ) + return true; + + // Is this group explicitly hidden? Does it immediately follow a - char. + searchStr = String::ToString( "-%s", groupName ); + if ( mGroupFilters.find( searchStr ) != String::NPos ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- + +bool GuiInspector::isGroupExplicitlyFiltered( const char *groupName ) const +{ + String searchStr; + + searchStr = String::ToString( "-%s", groupName ); + + if ( mGroupFilters.find( searchStr ) != String::NPos ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- + +void GuiInspector::setObjectField( const char *fieldName, const char *data ) +{ + GuiInspectorField *field; + + for ( S32 i = 0; i < mGroups.size(); i++ ) + { + field = mGroups[i]->findField( fieldName ); + + if( field ) + { + field->setData( data ); + return; + } + } +} + +//----------------------------------------------------------------------------- + +static SimObject *sgKeyObj = NULL; + +bool findInspectors( GuiInspector *obj ) +{ + if ( obj->isAwake() && obj->isInspectingObject( sgKeyObj ) ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- + +GuiInspector* GuiInspector::findByObject( SimObject *obj ) +{ + sgKeyObj = obj; + + Vector< GuiInspector* > found; + Sim::getGuiGroup()->findObjectByCallback( findInspectors, found ); + + if ( found.empty() ) + return NULL; + + return found.first(); +} + +//----------------------------------------------------------------------------- + +void GuiInspector::refresh() +{ + clearGroups(); + + // Remove any inspect object that happened to have + // already been killed. + + for( U32 i = 0; i < mTargets.size(); ++ i ) + if( !mTargets[ i ] ) + { + mTargets.erase( i ); + -- i; + } + + if( !mTargets.size() ) + return; + + // Special group for fields which should appear at the top of the + // list outside of a rollout control. + + GuiInspectorGroup *ungroup = NULL; + if( mTargets.size() == 1 ) + { + ungroup = new GuiInspectorGroup( "Ungrouped", this ); + ungroup->setHeaderHidden( true ); + ungroup->setCanCollapse( false ); + if( ungroup != NULL ) + { + ungroup->registerObject(); + mGroups.push_back( ungroup ); + addObject( ungroup ); + } + } + + // Put the 'transform' group first + GuiInspectorGroup *transform = new GuiInspectorGroup( "Transform", this ); + if( transform != NULL ) + { + transform->registerObject(); + mGroups.push_back( transform ); + addObject( transform ); + } + + // Always create the 'general' group (for fields without a group) + GuiInspectorGroup *general = new GuiInspectorGroup( "General", this ); + if( general != NULL ) + { + general->registerObject(); + mGroups.push_back( general ); + addObject( general ); + } + + // Create the inspector groups for static fields. + + for( TargetVector::iterator iter = mTargets.begin(); iter != mTargets.end(); ++ iter ) + { + AbstractClassRep::FieldList &fieldList = ( *iter )->getModifiableFieldList(); + + // Iterate through, identifying the groups and create necessary GuiInspectorGroups + for( AbstractClassRep::FieldList::iterator itr = fieldList.begin(); itr != fieldList.end(); itr++ ) + { + if ( itr->type == AbstractClassRep::StartGroupFieldType ) + { + GuiInspectorGroup* group = findExistentGroup( itr->pGroupname ); + + if( !group && !isGroupFiltered( itr->pGroupname ) ) + { + GuiInspectorGroup *group = new GuiInspectorGroup( itr->pGroupname, this ); + if( group != NULL ) + { + group->registerObject(); + if( !group->getNumFields() ) + { + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[GuiInspector] Removing empty group '%s'", + group->getCaption().c_str() ); + #endif + + // The group ended up having no fields. Remove it. + group->deleteObject(); + } + else + { + mGroups.push_back( group ); + addObject( group ); + } + } + } + } + } + } + + // Deal with dynamic fields + if ( !isGroupFiltered( "Dynamic Fields" ) ) + { + GuiInspectorGroup *dynGroup = new GuiInspectorDynamicGroup( "Dynamic Fields", this); + if( dynGroup != NULL ) + { + dynGroup->registerObject(); + mGroups.push_back( dynGroup ); + addObject( dynGroup ); + } + } + + if( mShowCustomFields && mTargets.size() == 1 ) + { + SimObject* object = mTargets.first(); + + // Add the SimObjectID field to the ungrouped group. + + GuiInspectorCustomField* field = new GuiInspectorCustomField(); + field->init( this, ungroup ); + + if( field->registerObject() ) + { + ungroup->mChildren.push_back( field ); + ungroup->mStack->addObject( field ); + + static StringTableEntry sId = StringTable->insert( "id" ); + + field->setCaption( sId ); + field->setData( object->getIdString() ); + field->setDoc( "SimObjectId of this object. [Read Only]" ); + } + else + delete field; + + // Add the Source Class field to the ungrouped group. + + field = new GuiInspectorCustomField(); + field->init( this, ungroup ); + + if( field->registerObject() ) + { + ungroup->mChildren.push_back( field ); + ungroup->mStack->addObject( field ); + + StringTableEntry sSourceClass = StringTable->insert( "Source Class", true ); + field->setCaption( sSourceClass ); + field->setData( object->getClassName() ); + + Namespace* ns = object->getClassRep()->getNameSpace(); + field->setToolTip( Con::getNamespaceList( ns ) ); + + field->setDoc( "Native class of this object. [Read Only]" ); + } + else + delete field; + } + + + // If the general group is still empty at this point ( or filtered ), kill it. + if ( isGroupFiltered( "General" ) || general->mStack->size() == 0 ) + { + for(S32 i=0; ideleteObject(); + updatePanes(); + + break; + } + } + } + + // If transform turns out to be empty or filtered, remove it + if( isGroupFiltered( "Transform" ) || transform->mStack->size() == 0 ) + { + for(S32 i=0; ideleteObject(); + updatePanes(); + + break; + } + } + } + + // If ungrouped is empty or explicitly filtered, remove it. + if( ungroup && ( isGroupExplicitlyFiltered( "Ungrouped" ) || ungroup->getNumFields() == 0 ) ) + { + for(S32 i=0; ideleteObject(); + updatePanes(); + + break; + } + } + } + + // If the object cannot be renamed, deactivate the name field if we have it. + + if( ungroup && getNumInspectObjects() == 1 && !getInspectObject()->isNameChangeAllowed() ) + { + GuiInspectorField* nameField = ungroup->findField( "name" ); + if( nameField ) + nameField->setActive( false ); + } +} + +//----------------------------------------------------------------------------- + +void GuiInspector::sendInspectPreApply() +{ + const U32 numObjects = getNumInspectObjects(); + for( U32 i = 0; i < numObjects; ++ i ) + getInspectObject( i )->inspectPreApply(); +} + +//----------------------------------------------------------------------------- + +void GuiInspector::sendInspectPostApply() +{ + const U32 numObjects = getNumInspectObjects(); + for( U32 i = 0; i < numObjects; ++ i ) + getInspectObject( i )->inspectPostApply(); +} + +//============================================================================= +// Console Methods. +//============================================================================= +// MARK: ---- Console Methods ---- + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiInspector, inspect, void, 3, 3, "Inspect(Object)") +{ + SimObject * target = Sim::findObject(argv[2]); + if(!target) + { + if(dAtoi(argv[2]) > 0) + Con::warnf("%s::inspect(): invalid object: %s", argv[0], argv[2]); + + object->clearInspectObjects(); + return; + } + + object->inspectObject(target); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiInspector, addInspect, void, 3, 4, "( id object, (bool autoSync = true) ) - Add the object to the list of objects being inspected." ) +{ + SimObject* obj; + if( !Sim::findObject( argv[ 2 ], obj ) ) + { + Con::errorf( "%s::addInspect(): invalid object: %s", argv[ 0 ], argv[ 2 ] ); + return; + } + + if( argc > 3 ) + object->addInspectObject( obj, false ); + else + object->addInspectObject( obj ); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiInspector, removeInspect, void, 3, 3, "( id object ) - Remove the object from the list of objects being inspected." ) +{ + SimObject* obj; + if( !Sim::findObject( argv[ 2 ], obj ) ) + { + Con::errorf( "%s::removeInspect(): invalid object: %s", argv[ 0 ], argv[ 2 ] ); + return; + } + + object->removeInspectObject( obj ); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiInspector, refresh, void, 2, 2, "Reinspect the currently selected object." ) +{ + if ( object->getNumInspectObjects() == 0 ) + return; + + SimObject *target = object->getInspectObject(); + if ( target ) + object->inspectObject( target ); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiInspector, getInspectObject, const char*, 2, 3, "getInspectObject( int index=0 ) - Returns currently inspected object" ) +{ + U32 index = 0; + if( argc > 2 ) + index = dAtoi( argv[ 2 ] ); + + if( index >= object->getNumInspectObjects() ) + { + Con::errorf( "GuiInspector::getInspectObject() - index out of range: %i", index ); + return ""; + } + + return object->getInspectObject( index )->getIdString(); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiInspector, getNumInspectObjects, S32, 2, 2, "() - Return the number of objects currently being inspected." ) +{ + return object->getNumInspectObjects(); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiInspector, setName, void, 3, 3, "setName(NewObjectName)") +{ + object->setName(argv[2]); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiInspector, apply, void, 2, 2, "apply() - Force application of inspected object's attributes" ) +{ + object->sendInspectPostApply(); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiInspector, setObjectField, void, 4, 4, + "setObjectField( fieldname, data ) - Set a named fields value on the inspected object if it exists. This triggers all the usual callbacks that would occur if the field had been changed through the gui." ) +{ + object->setObjectField( argv[2], argv[3] ); +} + +//----------------------------------------------------------------------------- + +ConsoleStaticMethod( GuiInspector, findByObject, S32, 2, 2, + "findByObject( SimObject ) - returns the id of an awake inspector that is inspecting the passed object if one exists." ) +{ + SimObject *obj; + if ( !Sim::findObject( argv[1], obj ) ) + return NULL; + + obj = GuiInspector::findByObject( obj ); + + if ( !obj ) + return NULL; + + return obj->getId(); +} diff --git a/Engine/source/gui/editor/guiInspector.h b/Engine/source/gui/editor/guiInspector.h new file mode 100644 index 000000000..01fa5c1b6 --- /dev/null +++ b/Engine/source/gui/editor/guiInspector.h @@ -0,0 +1,155 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUI_INSPECTOR_H_ +#define _GUI_INSPECTOR_H_ + +#ifndef _GUISTACKCTRL_H_ + #include "gui/containers/guiStackCtrl.h" +#endif + + +class GuiInspectorGroup; +class GuiInspectorField; +class GuiInspectorDatablockField; + + +/// A control that allows to edit the properties of one or more SimObjects. +class GuiInspector : public GuiStackControl +{ + typedef GuiStackControl Parent; + +public: + + GuiInspector(); + virtual ~GuiInspector(); + + DECLARE_CONOBJECT(GuiInspector); + DECLARE_CATEGORY( "Gui Editor" ); + DECLARE_DESCRIPTION( "A control that allows to edit the properties of one or more SimObjects." ); + + // Console Object + static void initPersistFields(); + + // SimObject + virtual void onRemove(); + virtual void onDeleteNotify( SimObject *object ); + + // GuiControl + virtual void parentResized( const RectI &oldParentRect, const RectI &newParentRect ); + virtual bool resize( const Point2I &newPosition, const Point2I &newExtent ); + virtual GuiControl* findHitControl( const Point2I &pt, S32 initialLayer ); + virtual void getCursor( GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent ); + virtual void onMouseMove( const GuiEvent &event ); + virtual void onMouseDown( const GuiEvent &event ); + virtual void onMouseUp( const GuiEvent &event ); + virtual void onMouseDragged( const GuiEvent &event ); + + // GuiInspector + + /// Return true if "object" is in the inspection set of this inspector. + bool isInspectingObject( SimObject* object ); + + /// Set the currently inspected object. + virtual void inspectObject( SimObject *object ); + + /// Add another object to the set of currently inspected objects. + virtual void addInspectObject( SimObject* object, bool autoSync = true ); + + /// Remove the given object from the set of inspected objects. + virtual void removeInspectObject( SimObject* object ); + + /// Remove all objects from the inspection set. + virtual void clearInspectObjects(); + + /// Get the currently inspected object + SimObject* getInspectObject( U32 index = 0 ) { return mTargets[ index ]; } + + /// Return the number of objects being inspected by this GuiInspector. + U32 getNumInspectObjects() const { return mTargets.size(); } + + /// Call inspectPreApply on all inspected objects. + void sendInspectPreApply(); + + /// Call inspectPostApply on all inspected objects. + void sendInspectPostApply(); + + /// Set the currently inspected object name + /// @note Only valid in single-object mode. + void setName( StringTableEntry newName ); + + /// Deletes all GuiInspectorGroups + void clearGroups(); + + /// Returns true if the named group exists + /// Helper for inspectObject + GuiInspectorGroup* findExistentGroup( StringTableEntry groupName ); + + /// Should there be a GuiInspectorField associated with this fieldName, + /// update it to reflect actual/current value of that field in the inspected object. + /// Added to support UndoActions + void updateFieldValue( StringTableEntry fieldName, const char* arrayIdx ); + + /// Divider position is interpreted as an offset + /// from the right edge of the field control. + /// Divider margin is an offset on both left and right + /// sides of the divider in which it can be selected + /// with the mouse. + void getDivider( S32 &pos, S32 &margin ); + + void updateDivider(); + + bool collideDivider( const Point2I &localPnt ); + + void setHighlightField( GuiInspectorField *field ); + + // If returns true that group will not be inspected. + bool isGroupFiltered( const char *groupName ) const; + + // Returns true only if the group name follows a minus symbol in the filters. + bool isGroupExplicitlyFiltered( const char *groupName ) const; + + void setObjectField( const char *fieldName, const char *data ); + + static GuiInspector* findByObject( SimObject *obj ); + +protected: + + typedef Vector< SimObjectPtr< SimObject > > TargetVector; + + Vector mGroups; + + /// Objects being inspected by this GuiInspector. + TargetVector mTargets; + + F32 mDividerPos; + S32 mDividerMargin; + bool mOverDivider; + bool mMovingDivider; + SimObjectPtr mHLField; + String mGroupFilters; + bool mShowCustomFields; + + void refresh(); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/gui/editor/guiInspectorTypes.cpp b/Engine/source/gui/editor/guiInspectorTypes.cpp new file mode 100644 index 000000000..e1533134f --- /dev/null +++ b/Engine/source/gui/editor/guiInspectorTypes.cpp @@ -0,0 +1,1715 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/editor/guiInspectorTypes.h" + +#include "gui/editor/inspector/group.h" +#include "gui/controls/guiTextEditSliderCtrl.h" +#include "gui/controls/guiTextEditSliderBitmapCtrl.h" +#include "gui/buttons/guiSwatchButtonCtrl.h" +#include "gui/containers/guiDynamicCtrlArrayCtrl.h" +#include "core/strings/stringUnit.h" +#include "materials/materialDefinition.h" +#include "materials/materialManager.h" +#include "materials/customMaterialDefinition.h" +#include "gfx/gfxDrawUtil.h" +#include "sfx/sfxTypes.h" +#include "sfx/sfxParameter.h" +#include "sfx/sfxState.h" +#include "sfx/sfxSource.h" +#include "gui/editor/editorFunctions.h" +#include "math/mEase.h" +#include "math/mathTypes.h" + + +//----------------------------------------------------------------------------- +// GuiInspectorTypeMenuBase +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(GuiInspectorTypeMenuBase); + +ConsoleDocClass( GuiInspectorTypeMenuBase, + "@brief Inspector field type for MenuBase\n\n" + "Editor use only.\n\n" + "@internal" +); + +GuiControl* GuiInspectorTypeMenuBase::constructEditControl() +{ + GuiControl* retCtrl = new GuiPopUpMenuCtrl(); + + // If we couldn't construct the control, bail! + if( retCtrl == NULL ) + return retCtrl; + + GuiPopUpMenuCtrl *menu = dynamic_cast(retCtrl); + + // Let's make it look pretty. + retCtrl->setDataField( StringTable->insert("profile"), NULL, "GuiPopUpMenuProfile" ); + _registerEditControl( retCtrl ); + + // Configure it to update our value when the popup is closed + char szBuffer[512]; + dSprintf( szBuffer, 512, "%d.apply( %d.getText() );", getId(), menu->getId() ); + menu->setField("Command", szBuffer ); + + //now add the entries, allow derived classes to override this + _populateMenu( menu ); + + // Select the active item, or just set the text field if that fails + S32 id = menu->findText(getData()); + if (id != -1) + menu->setSelected(id, false); + else + menu->setField("text", getData()); + + return retCtrl; +} + +void GuiInspectorTypeMenuBase::setValue( StringTableEntry newValue ) +{ + GuiPopUpMenuCtrl *ctrl = dynamic_cast( mEdit ); + if ( ctrl != NULL ) + ctrl->setText( newValue ); +} + +void GuiInspectorTypeMenuBase::_populateMenu( GuiPopUpMenuCtrl *menu ) +{ + // do nothing, child classes override this. +} + +//----------------------------------------------------------------------------- +// GuiInspectorTypeEnum +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(GuiInspectorTypeEnum); + +ConsoleDocClass( GuiInspectorTypeEnum, + "@brief Inspector field type for Enum\n\n" + "Editor use only.\n\n" + "@internal" +); + +void GuiInspectorTypeEnum::_populateMenu( GuiPopUpMenuCtrl *menu ) +{ + const EngineEnumTable* table = mField->table; + if( !table ) + { + ConsoleBaseType* type = ConsoleBaseType::getType( mField->type ); + if( type && type->getEnumTable() ) + table = type->getEnumTable(); + else + return; + } + + const EngineEnumTable& t = *table; + const U32 numEntries = t.getNumValues(); + + for( U32 i = 0; i < numEntries; ++ i ) + menu->addEntry( t[ i ].getName(), t[ i ] ); + + menu->sort(); +} + +void GuiInspectorTypeEnum::consoleInit() +{ + Parent::consoleInit(); + + // Set this to be the inspector type for all enumeration console types. + + for( ConsoleBaseType* type = ConsoleBaseType::getListHead(); type != NULL; type = type->getListNext() ) + if( type->getTypeInfo() && type->getTypeInfo()->isEnum() ) + type->setInspectorFieldType( "GuiInspectorTypeEnum" ); +} + +//----------------------------------------------------------------------------- +// GuiInspectorTypeCubemapName +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(GuiInspectorTypeCubemapName); + +ConsoleDocClass( GuiInspectorTypeCubemapName, + "@brief Inspector field type for Cubemap\n\n" + "Editor use only.\n\n" + "@internal" +); + +void GuiInspectorTypeCubemapName::_populateMenu( GuiPopUpMenuCtrl *menu ) +{ + PROFILE_SCOPE( GuiInspectorTypeCubemapName_populateMenu ); + + // This could be expensive looping through the whole RootGroup + // and performing string comparisons... Put a profile here + // to keep an eye on it. + + SimGroup *root = Sim::getRootGroup(); + + SimGroupIterator iter( root ); + for ( ; *iter; ++iter ) + { + if ( dStricmp( (*iter)->getClassName(), "CubemapData" ) == 0 ) + menu->addEntry( (*iter)->getName(), 0 ); + } + + menu->sort(); +} + +void GuiInspectorTypeCubemapName::consoleInit() +{ + Parent::consoleInit(); + + ConsoleBaseType::getType(TypeCubemapName)->setInspectorFieldType("GuiInspectorTypeCubemapName"); +} + +//----------------------------------------------------------------------------- +// GuiInspectorTypeMaterialName +//----------------------------------------------------------------------------- + +IMPLEMENT_CONOBJECT(GuiInspectorTypeMaterialName); + +ConsoleDocClass( GuiInspectorTypeMaterialName, + "@brief Inspector field type for Material\n\n" + "Editor use only.\n\n" + "@internal" +); + +GuiInspectorTypeMaterialName::GuiInspectorTypeMaterialName() + : mBrowseButton( NULL ) +{ +} + +void GuiInspectorTypeMaterialName::consoleInit() +{ + Parent::consoleInit(); + + ConsoleBaseType::getType(TypeMaterialName)->setInspectorFieldType("GuiInspectorTypeMaterialName"); +} + +GuiControl* GuiInspectorTypeMaterialName::construct(const char* command) +{ + GuiControl* retCtrl = new GuiTextEditCtrl(); + + retCtrl->setDataField( StringTable->insert("profile"), NULL, "GuiInspectorTextEditProfile" ); + + _registerEditControl( retCtrl ); + + char szBuffer[512]; + dSprintf( szBuffer, 512, "%d.apply(%d.getText());",getId(), retCtrl->getId() ); + retCtrl->setField("AltCommand", szBuffer ); + retCtrl->setField("Validate", szBuffer ); + + //return retCtrl; + mBrowseButton = new GuiBitmapButtonCtrl(); + + if ( mBrowseButton != NULL ) + { + RectI browseRect( Point2I( ( getLeft() + getWidth()) - 26, getTop() + 2), Point2I(20, getHeight() - 4) ); + + char szBuffer[512]; + dSprintf( szBuffer, 512, command, getId()); + mBrowseButton->setField( "Command", szBuffer ); + + //temporary static button name + char bitmapName[512] = "tools/materialEditor/gui/change-material-btn"; + mBrowseButton->setBitmap( bitmapName ); + + mBrowseButton->setDataField( StringTable->insert("Profile"), NULL, "GuiButtonProfile" ); + mBrowseButton->registerObject(); + addObject( mBrowseButton ); + + // Position + mBrowseButton->resize( browseRect.point, browseRect.extent ); + } + + return retCtrl; +} + +GuiControl* GuiInspectorTypeMaterialName::constructEditControl() +{ + return construct("materialSelector.showDialog(\"%d.apply\", \"name\");"); +} + +bool GuiInspectorTypeMaterialName::updateRects() +{ + Point2I fieldPos = getPosition(); + Point2I fieldExtent = getExtent(); + S32 dividerPos, dividerMargin; + mInspector->getDivider( dividerPos, dividerMargin ); + + mCaptionRect.set( 0, 0, fieldExtent.x - dividerPos - dividerMargin, fieldExtent.y ); + // Icon extent 17 x 17 + mBrowseRect.set( fieldExtent.x - 20, 2, 17, fieldExtent.y - 1 ); + mEditCtrlRect.set( fieldExtent.x - dividerPos + dividerMargin, 1, dividerPos - dividerMargin - 29, fieldExtent.y ); + + bool editResize = mEdit->resize( mEditCtrlRect.point, mEditCtrlRect.extent ); + bool browseResize = false; + + if ( mBrowseButton != NULL ) + { + browseResize = mBrowseButton->resize( mBrowseRect.point, mBrowseRect.extent ); + } + + return ( editResize || browseResize ); +} + +//----------------------------------------------------------------------------- +// GuiInspectorTypeTerrainMaterialIndex +//----------------------------------------------------------------------------- + +IMPLEMENT_CONOBJECT(GuiInspectorTypeTerrainMaterialIndex); + +ConsoleDocClass( GuiInspectorTypeTerrainMaterialIndex, + "@brief Inspector field type for TerrainMaterialIndex\n\n" + "Editor use only.\n\n" + "@internal" +); + +void GuiInspectorTypeTerrainMaterialIndex::consoleInit() +{ + Parent::consoleInit(); + + ConsoleBaseType::getType(TypeTerrainMaterialIndex)->setInspectorFieldType("GuiInspectorTypeTerrainMaterialIndex"); +} + +GuiControl* GuiInspectorTypeTerrainMaterialIndex::constructEditControl() +{ + return construct("materialSelector.showTerrainDialog(\"%d.apply\", \"index\");"); +} + +//----------------------------------------------------------------------------- +// GuiInspectorTypeTerrainMaterialName +//----------------------------------------------------------------------------- + +IMPLEMENT_CONOBJECT(GuiInspectorTypeTerrainMaterialName); + +ConsoleDocClass( GuiInspectorTypeTerrainMaterialName, + "@brief Inspector field type for TerrainMaterial\n\n" + "Editor use only.\n\n" + "@internal" +); + +void GuiInspectorTypeTerrainMaterialName::consoleInit() +{ + Parent::consoleInit(); + + ConsoleBaseType::getType(TypeTerrainMaterialName)->setInspectorFieldType("GuiInspectorTypeTerrainMaterialName"); +} + +GuiControl* GuiInspectorTypeTerrainMaterialName::construct(const char* command) +{ + GuiControl* retCtrl = new GuiTextEditCtrl(); + + retCtrl->setDataField( StringTable->insert("profile"), NULL, "GuiInspectorTextEditProfile" ); + + _registerEditControl( retCtrl ); + + char szBuffer[512]; + dSprintf( szBuffer, 512, "%d.apply(%d.getText());",getId(), retCtrl->getId() ); + retCtrl->setField("AltCommand", szBuffer ); + retCtrl->setField("Validate", szBuffer ); + + //return retCtrl; + mBrowseButton = new GuiBitmapButtonCtrl(); + + if ( mBrowseButton != NULL ) + { + RectI browseRect( Point2I( ( getLeft() + getWidth()) - 26, getTop() + 2), Point2I(20, getHeight() - 4) ); + + char szBuffer[512]; + dSprintf( szBuffer, 512, command, getId()); + mBrowseButton->setField( "Command", szBuffer ); + + //temporary static button name + char bitmapName[512] = "tools/gui/images/layers-btn"; + mBrowseButton->setBitmap( bitmapName ); + + mBrowseButton->setDataField( StringTable->insert("Profile"), NULL, "GuiButtonProfile" ); + mBrowseButton->registerObject(); + addObject( mBrowseButton ); + + // Position + mBrowseButton->resize( browseRect.point, browseRect.extent ); + } + + return retCtrl; +} + +GuiControl* GuiInspectorTypeTerrainMaterialName::constructEditControl() +{ + return construct("materialSelector.showTerrainDialog(\"%d.apply\", \"name\");"); +} + +//----------------------------------------------------------------------------- +// GuiInspectorTypeGuiProfile +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(GuiInspectorTypeGuiProfile); + +ConsoleDocClass( GuiInspectorTypeGuiProfile, + "@brief Inspector field type for GuiProfile\n\n" + "Editor use only.\n\n" + "@internal" +); + +void GuiInspectorTypeGuiProfile::_populateMenu( GuiPopUpMenuCtrl *menu ) +{ + // Check whether we should show profiles from the editor category. + + const bool showEditorProfiles = Con::getBoolVariable( "$pref::GuiEditor::showEditorProfiles", false ); + + // Add the control profiles to the menu. + + SimGroup *grp = Sim::getGuiDataGroup(); + SimSetIterator iter( grp ); + for ( ; *iter; ++iter ) + { + GuiControlProfile *profile = dynamic_cast(*iter); + if( !profile ) + continue; + + if( !showEditorProfiles && profile->mCategory.compare( "Editor", 0, String::NoCase ) == 0 ) + continue; + + menu->addEntry( profile->getName(), profile->getId() ); + } + + menu->sort(); +} + +void GuiInspectorTypeGuiProfile::consoleInit() +{ + Parent::consoleInit(); + + ConsoleBaseType::getType( TYPEID< GuiControlProfile >() )->setInspectorFieldType("GuiInspectorTypeGuiProfile"); +} + +//----------------------------------------------------------------------------- +// GuiInspectorTypeCheckBox +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(GuiInspectorTypeCheckBox); + +ConsoleDocClass( GuiInspectorTypeCheckBox, + "@brief Inspector field type for CheckBox\n\n" + "Editor use only.\n\n" + "@internal" +); + +GuiControl* GuiInspectorTypeCheckBox::constructEditControl() +{ + GuiControl* retCtrl = new GuiCheckBoxCtrl(); + + // If we couldn't construct the control, bail! + if( retCtrl == NULL ) + return retCtrl; + + GuiCheckBoxCtrl *check = dynamic_cast(retCtrl); + + // Let's make it look pretty. + retCtrl->setDataField( StringTable->insert("profile"), NULL, "InspectorTypeCheckboxProfile" ); + retCtrl->setField( "text", "" ); + + check->setIndent( 4 ); + + retCtrl->setScriptValue( getData() ); + + _registerEditControl( retCtrl ); + + // Configure it to update our value when the popup is closed + char szBuffer[512]; + dSprintf( szBuffer, 512, "%d.apply(%d.getValue());",getId(),check->getId() ); + check->setField("Command", szBuffer ); + + return retCtrl; +} + + +void GuiInspectorTypeCheckBox::consoleInit() +{ + Parent::consoleInit(); + + ConsoleBaseType::getType(TypeBool)->setInspectorFieldType("GuiInspectorTypeCheckBox"); +} + +void GuiInspectorTypeCheckBox::setValue( StringTableEntry newValue ) +{ + GuiButtonBaseCtrl *ctrl = dynamic_cast( mEdit ); + if ( ctrl != NULL ) + ctrl->setStateOn( dAtob(newValue) ); +} + +const char* GuiInspectorTypeCheckBox::getValue() +{ + GuiButtonBaseCtrl *ctrl = dynamic_cast( mEdit ); + if ( ctrl != NULL ) + return ctrl->getScriptValue(); + + return NULL; +} + +//----------------------------------------------------------------------------- +// GuiInspectorTypeFileName +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(GuiInspectorTypeFileName); + +ConsoleDocClass( GuiInspectorTypeFileName, + "@brief Inspector field type for FileName\n\n" + "Editor use only.\n\n" + "@internal" +); + +void GuiInspectorTypeFileName::consoleInit() +{ + Parent::consoleInit(); + + ConsoleBaseType::getType(TypeFilename)->setInspectorFieldType("GuiInspectorTypeFileName"); + ConsoleBaseType::getType(TypeStringFilename)->setInspectorFieldType("GuiInspectorTypeFileName"); +} + +GuiControl* GuiInspectorTypeFileName::constructEditControl() +{ + GuiControl* retCtrl = new GuiTextEditCtrl(); + + // If we couldn't construct the control, bail! + if( retCtrl == NULL ) + return retCtrl; + + // Let's make it look pretty. + retCtrl->setDataField( StringTable->insert("profile"), NULL, "GuiInspectorTextEditRightProfile" ); + retCtrl->setDataField( StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile" ); + retCtrl->setDataField( StringTable->insert("hovertime"), NULL, "1000" ); + + // Don't forget to register ourselves + _registerEditControl( retCtrl ); + + char szBuffer[512]; + dSprintf( szBuffer, 512, "%d.apply(%d.getText());",getId(),retCtrl->getId() ); + retCtrl->setField("AltCommand", szBuffer ); + retCtrl->setField("Validate", szBuffer ); + + mBrowseButton = new GuiButtonCtrl(); + + if( mBrowseButton != NULL ) + { + RectI browseRect( Point2I( ( getLeft() + getWidth()) - 26, getTop() + 2), Point2I(20, getHeight() - 4) ); + char szBuffer[512]; + dSprintf( szBuffer, 512, "getLoadFilename(\"*.*|*.*\", \"%d.apply\", %d.getData());", getId(), getId() ); + mBrowseButton->setField( "Command", szBuffer ); + mBrowseButton->setField( "text", "..." ); + mBrowseButton->setDataField( StringTable->insert("Profile"), NULL, "GuiInspectorButtonProfile" ); + mBrowseButton->registerObject(); + addObject( mBrowseButton ); + + // Position + mBrowseButton->resize( browseRect.point, browseRect.extent ); + } + + return retCtrl; +} + +bool GuiInspectorTypeFileName::resize( const Point2I &newPosition, const Point2I &newExtent ) +{ + if ( !Parent::resize( newPosition, newExtent ) ) + return false; + + if ( mEdit != NULL ) + { + return updateRects(); + } + + return false; +} + +bool GuiInspectorTypeFileName::updateRects() +{ + S32 dividerPos, dividerMargin; + mInspector->getDivider( dividerPos, dividerMargin ); + Point2I fieldExtent = getExtent(); + Point2I fieldPos = getPosition(); + + mCaptionRect.set( 0, 0, fieldExtent.x - dividerPos - dividerMargin, fieldExtent.y ); + mEditCtrlRect.set( fieldExtent.x - dividerPos + dividerMargin, 1, dividerPos - dividerMargin - 32, fieldExtent.y ); + + bool editResize = mEdit->resize( mEditCtrlRect.point, mEditCtrlRect.extent ); + bool browseResize = false; + + if ( mBrowseButton != NULL ) + { + mBrowseRect.set( fieldExtent.x - 20, 2, 14, fieldExtent.y - 4 ); + browseResize = mBrowseButton->resize( mBrowseRect.point, mBrowseRect.extent ); + } + + return ( editResize || browseResize ); +} + +void GuiInspectorTypeFileName::updateValue() +{ + if ( mField ) + { + Parent::updateValue(); + const char* data = getData(); + if(!data) + data = ""; + mEdit->setDataField( StringTable->insert("tooltip"), NULL, data ); + } +} + +ConsoleMethod( GuiInspectorTypeFileName, apply, void, 3,3, "apply(newValue);" ) +{ + String path( argv[2] ); + if ( path.isNotEmpty() ) + path = Platform::makeRelativePathName( path, Platform::getMainDotCsDir() ); + + object->setData( path.c_str() ); +} + + +//----------------------------------------------------------------------------- +// GuiInspectorTypeImageFileName +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(GuiInspectorTypeImageFileName); + +ConsoleDocClass( GuiInspectorTypeImageFileName, + "@brief Inspector field type for FileName\n\n" + "Editor use only.\n\n" + "@internal" +); + +void GuiInspectorTypeImageFileName::consoleInit() +{ + Parent::consoleInit(); + + ConsoleBaseType::getType(TypeImageFilename)->setInspectorFieldType("GuiInspectorTypeImageFileName"); +} + +GuiControl* GuiInspectorTypeImageFileName::constructEditControl() +{ + GuiControl *retCtrl = Parent::constructEditControl(); + + if ( retCtrl == NULL ) + return retCtrl; + + retCtrl->getRenderTooltipDelegate().bind( this, &GuiInspectorTypeImageFileName::renderTooltip ); + char szBuffer[512]; + + String extList = GBitmap::sGetExtensionList(); + extList += "dds"; + U32 extCount = StringUnit::getUnitCount( extList, " " ); + + String fileSpec; + + // building the fileSpec string + + fileSpec += "All Image Files|"; + + for ( U32 i = 0; i < extCount; i++ ) + { + fileSpec += "*."; + fileSpec += StringUnit::getUnit( extList, i, " " ); + + if ( i < extCount - 1 ) + fileSpec += ";"; + } + + fileSpec += "|"; + + for ( U32 i = 0; i < extCount; i++ ) + { + String ext = StringUnit::getUnit( extList, i, " " ); + fileSpec += ext; + fileSpec += "|*."; + fileSpec += ext; + + if ( i != extCount - 1 ) + fileSpec += "|"; + } + + dSprintf( szBuffer, 512, "getLoadFilename(\"%s\", \"%d.apply\", %d.getData());", fileSpec.c_str(), getId(), getId() ); + mBrowseButton->setField( "Command", szBuffer ); + + return retCtrl; +} + +bool GuiInspectorTypeImageFileName::renderTooltip( const Point2I &hoverPos, const Point2I &cursorPos, const char *tipText ) +{ + if ( !mAwake ) + return false; + + GuiCanvas *root = getRoot(); + if ( !root ) + return false; + + StringTableEntry filename = getData(); + if ( !filename || !filename[0] ) + return false; + + GFXTexHandle texture( filename, &GFXDefaultStaticDiffuseProfile, avar("%s() - tooltip texture (line %d)", __FUNCTION__, __LINE__) ); + if ( texture.isNull() ) + return false; + + // Render image at a reasonable screen size while + // keeping its aspect ratio... + Point2I screensize = getRoot()->getWindowSize(); + Point2I offset = hoverPos; + Point2I tipBounds; + + U32 texWidth = texture.getWidth(); + U32 texHeight = texture.getHeight(); + F32 aspect = (F32)texHeight / (F32)texWidth; + + const F32 newWidth = 150.0f; + F32 newHeight = aspect * newWidth; + + // Offset below cursor image + offset.y += 20; // TODO: Attempt to fix?: root->getCursorExtent().y; + tipBounds.x = newWidth; + tipBounds.y = newHeight; + + // Make sure all of the tooltip will be rendered width the app window, + // 5 is given as a buffer against the edge + if ( screensize.x < offset.x + tipBounds.x + 5 ) + offset.x = screensize.x - tipBounds.x - 5; + if ( screensize.y < offset.y + tipBounds.y + 5 ) + offset.y = hoverPos.y - tipBounds.y - 5; + + RectI oldClip = GFX->getClipRect(); + RectI rect( offset, tipBounds ); + GFX->setClipRect( rect ); + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + drawer->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapStretch( texture, rect ); + + GFX->setClipRect( oldClip ); + + return true; +} + + +//----------------------------------------------------------------------------- +// GuiInspectorTypePrefabFilename +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(GuiInspectorTypePrefabFilename); + +ConsoleDocClass( GuiInspectorTypePrefabFilename, + "@brief Inspector field type for PrefabFilename\n\n" + "Editor use only.\n\n" + "@internal" +); + +void GuiInspectorTypePrefabFilename::consoleInit() +{ + Parent::consoleInit(); + + ConsoleBaseType::getType(TypePrefabFilename)->setInspectorFieldType("GuiInspectorTypePrefabFilename"); +} + +GuiControl* GuiInspectorTypePrefabFilename::constructEditControl() +{ + GuiControl *retCtrl = Parent::constructEditControl(); + + if ( retCtrl == NULL ) + return retCtrl; + + const char *fileSpec = "Prefab Files (*.prefab)|*.prefab|All Files (*.*)|*.*|"; + + char szBuffer[512]; + dSprintf( szBuffer, 512, "getLoadFilename(\"%s\", \"%d.apply\", %d.getData());", fileSpec, getId(), getId() ); + + mBrowseButton->setField( "Command", szBuffer ); + + return retCtrl; +} + +//----------------------------------------------------------------------------- +// GuiInspectorTypeShapeFileName +//----------------------------------------------------------------------------- + +IMPLEMENT_CONOBJECT(GuiInspectorTypeShapeFilename); + +ConsoleDocClass( GuiInspectorTypeShapeFilename, + "@brief Inspector field type for Shapes\n\n" + "Editor use only.\n\n" + "@internal" +); + +void GuiInspectorTypeShapeFilename::consoleInit() +{ + Parent::consoleInit(); + + ConsoleBaseType::getType(TypeShapeFilename)->setInspectorFieldType("GuiInspectorTypeShapeFilename"); +} + +GuiControl* GuiInspectorTypeShapeFilename::constructEditControl() +{ + // Create base filename edit controls + GuiControl *retCtrl = Parent::constructEditControl(); + if ( retCtrl == NULL ) + return retCtrl; + + // Change filespec + char szBuffer[512]; + dSprintf( szBuffer, sizeof(szBuffer), "getLoadFilename(\"%s\", \"%d.apply\", %d.getData());", + "DTS Files (*.dts)|*.dts|COLLADA Files (*.dae)|*.dae|(All Files (*.*)|*.*|", getId(), getId() ); + mBrowseButton->setField( "Command", szBuffer ); + + // Create "Open in ShapeEditor" button + mShapeEdButton = new GuiBitmapButtonCtrl(); + if ( mShapeEdButton != NULL ) + { + char szBuffer[512]; + dSprintf( szBuffer, sizeof(szBuffer), "ShapeEditorPlugin.open(%d.getText());", retCtrl->getId() ); + mShapeEdButton->setField( "Command", szBuffer ); + + char bitmapName[512] = "tools/worldEditor/images/toolbar/shape-editor"; + mShapeEdButton->setBitmap( bitmapName ); + + mShapeEdButton->setDataField( StringTable->insert("Profile"), NULL, "GuiButtonProfile" ); + mShapeEdButton->setDataField( StringTable->insert("tooltipprofile"), NULL, "GuiToolTipProfile" ); + mShapeEdButton->setDataField( StringTable->insert("hovertime"), NULL, "1000" ); + mShapeEdButton->setDataField( StringTable->insert("tooltip"), NULL, "Open this file in the Shape Editor" ); + + mShapeEdButton->registerObject(); + addObject( mShapeEdButton ); + } + + return retCtrl; +} + +bool GuiInspectorTypeShapeFilename::updateRects() +{ + S32 dividerPos, dividerMargin; + mInspector->getDivider( dividerPos, dividerMargin ); + Point2I fieldExtent = getExtent(); + Point2I fieldPos = getPosition(); + + mCaptionRect.set( 0, 0, fieldExtent.x - dividerPos - dividerMargin, fieldExtent.y ); + mEditCtrlRect.set( fieldExtent.x - dividerPos + dividerMargin, 1, dividerPos - dividerMargin - 34, fieldExtent.y ); + + bool resized = mEdit->resize( mEditCtrlRect.point, mEditCtrlRect.extent ); + if ( mBrowseButton != NULL ) + { + mBrowseRect.set( fieldExtent.x - 32, 2, 14, fieldExtent.y - 4 ); + resized |= mBrowseButton->resize( mBrowseRect.point, mBrowseRect.extent ); + } + if ( mShapeEdButton != NULL ) + { + RectI shapeEdRect( fieldExtent.x - 16, 2, 14, fieldExtent.y - 4 ); + resized |= mShapeEdButton->resize( shapeEdRect.point, shapeEdRect.extent ); + } + + return resized; +} + +//----------------------------------------------------------------------------- +// GuiInspectorTypeCommand +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(GuiInspectorTypeCommand); + +ConsoleDocClass( GuiInspectorTypeCommand, + "@brief Inspector field type for Command\n\n" + "Editor use only.\n\n" + "@internal" +); + +void GuiInspectorTypeCommand::consoleInit() +{ + Parent::consoleInit(); + + ConsoleBaseType::getType(TypeCommand)->setInspectorFieldType("GuiInspectorTypeCommand"); +} + +GuiInspectorTypeCommand::GuiInspectorTypeCommand() +{ + mTextEditorCommand = StringTable->insert("TextPad"); +} + +GuiControl* GuiInspectorTypeCommand::constructEditControl() +{ + GuiButtonCtrl* retCtrl = new GuiButtonCtrl(); + + // If we couldn't construct the control, bail! + if( retCtrl == NULL ) + return retCtrl; + + // Let's make it look pretty. + retCtrl->setDataField( StringTable->insert("profile"), NULL, "GuiInspectorTextEditProfile" ); + + // Don't forget to register ourselves + _registerEditControl( retCtrl ); + + _setCommand( retCtrl, getData() ); + + return retCtrl; +} + +void GuiInspectorTypeCommand::setValue( StringTableEntry newValue ) +{ + GuiButtonCtrl *ctrl = dynamic_cast( mEdit ); + _setCommand( ctrl, newValue ); +} + +void GuiInspectorTypeCommand::_setCommand( GuiButtonCtrl *ctrl, StringTableEntry command ) +{ + if( ctrl != NULL ) + { + ctrl->setField( "text", command ); + + // expandEscape isn't length-limited, so while this _should_ work + // in most circumstances, it may still fail if getData() has lots of + // non-printable characters + S32 len = 2 * dStrlen(command) + 512; + + FrameTemp szBuffer(len); + + S32 written = dSprintf( szBuffer, len, "%s(\"", mTextEditorCommand ); + expandEscape(szBuffer.address() + written, command); + written = strlen(szBuffer); + dSprintf( szBuffer.address() + written, len - written, "\", \"%d.apply\", %d.getRoot());", getId(), getId() ); + + ctrl->setField( "Command", szBuffer ); + } +} + +//----------------------------------------------------------------------------- +// GuiInspectorTypeRectUV +//----------------------------------------------------------------------------- + +IMPLEMENT_CONOBJECT( GuiInspectorTypeRectUV ); + +ConsoleDocClass( GuiInspectorTypeRectUV, + "@brief Inspector field type for TypeRectUV.\n\n" + "Editor use only.\n\n" + "@internal" +); + +GuiInspectorTypeRectUV::GuiInspectorTypeRectUV() + : mBrowseButton( NULL ) +{ +} + + +void GuiInspectorTypeRectUV::consoleInit() +{ + Parent::consoleInit(); + + ConsoleBaseType::getType( TypeRectUV )->setInspectorFieldType( "GuiInspectorTypeRectUV" ); +} + +GuiControl* GuiInspectorTypeRectUV::constructEditControl() +{ + GuiControl* retCtrl = new GuiTextEditCtrl(); + + retCtrl->setDataField( StringTable->insert("profile"), NULL, "GuiInspectorTextEditProfile" ); + + _registerEditControl( retCtrl ); + + char szBuffer[512]; + dSprintf( szBuffer, 512, "%d.apply(%d.getText());",getId(), retCtrl->getId() ); + retCtrl->setField("AltCommand", szBuffer ); + retCtrl->setField("Validate", szBuffer ); + + //return retCtrl; + mBrowseButton = new GuiBitmapButtonCtrl(); + + if ( mBrowseButton != NULL ) + { + RectI browseRect( Point2I( ( getLeft() + getWidth()) - 26, getTop() + 2), Point2I(20, getHeight() - 4) ); + + char szBuffer[512]; + dSprintf( szBuffer, 512, "uvEditor.showDialog(\"%d.apply\", %d, %d.getText());", getId(), mInspector->getInspectObject()->getId(), retCtrl->getId()); + mBrowseButton->setField( "Command", szBuffer ); + + //temporary static button name + char bitmapName[512] = "tools/gui/images/uv-editor-btn"; + mBrowseButton->setBitmap( bitmapName ); + + mBrowseButton->setDataField( StringTable->insert("Profile"), NULL, "GuiButtonProfile" ); + mBrowseButton->registerObject(); + addObject( mBrowseButton ); + + // Position + mBrowseButton->resize( browseRect.point, browseRect.extent ); + } + + return retCtrl; +} + +bool GuiInspectorTypeRectUV::updateRects() +{ + Point2I fieldPos = getPosition(); + Point2I fieldExtent = getExtent(); + S32 dividerPos, dividerMargin; + mInspector->getDivider( dividerPos, dividerMargin ); + + mCaptionRect.set( 0, 0, fieldExtent.x - dividerPos - dividerMargin, fieldExtent.y ); + // Icon extent 17 x 17 + mBrowseRect.set( fieldExtent.x - 20, 2, 17, fieldExtent.y - 1 ); + mEditCtrlRect.set( fieldExtent.x - dividerPos + dividerMargin, 1, dividerPos - dividerMargin - 29, fieldExtent.y ); + + bool editResize = mEdit->resize( mEditCtrlRect.point, mEditCtrlRect.extent ); + bool browseResize = false; + + if ( mBrowseButton != NULL ) + { + browseResize = mBrowseButton->resize( mBrowseRect.point, mBrowseRect.extent ); + } + + return ( editResize || browseResize ); +} + +//----------------------------------------------------------------------------- +// GuiInspectorTypeEaseF +//----------------------------------------------------------------------------- + +IMPLEMENT_CONOBJECT( GuiInspectorTypeEaseF ); + +ConsoleDocClass( GuiInspectorTypeEaseF, + "@brief Inspector field type for TypeEaseF.\n\n" + "Editor use only.\n\n" + "@internal" +); + +GuiInspectorTypeEaseF::GuiInspectorTypeEaseF() + : mBrowseButton( NULL ) +{ +} + +void GuiInspectorTypeEaseF::consoleInit() +{ + Parent::consoleInit(); + + ConsoleBaseType::getType( TypeEaseF )->setInspectorFieldType( "GuiInspectorTypeEaseF" ); +} + +GuiControl* GuiInspectorTypeEaseF::constructEditControl() +{ + GuiControl* retCtrl = new GuiTextEditCtrl(); + + // Let's make it look pretty. + retCtrl->setDataField( StringTable->insert("profile"), NULL, "GuiInspectorTextEditProfile" ); + + // Don't forget to register ourselves + _registerEditControl( retCtrl ); + + char szBuffer[512]; + dSprintf( szBuffer, 512, "%d.apply(%d.getText());",getId(),retCtrl->getId() ); + retCtrl->setField("AltCommand", szBuffer ); + retCtrl->setField("Validate", szBuffer ); + + mBrowseButton = new GuiButtonCtrl(); + { + RectI browseRect( Point2I( ( getLeft() + getWidth()) - 26, getTop() + 2), Point2I(20, getHeight() - 4) ); + char szBuffer[512]; + dSprintf( szBuffer, sizeof( szBuffer ), "GetEaseF(%d.getText(), \"%d.apply\", %d.getRoot());", retCtrl->getId(), getId(), getId() ); + mBrowseButton->setField( "Command", szBuffer ); + mBrowseButton->setField( "text", "E" ); + mBrowseButton->setDataField( StringTable->insert("Profile"), NULL, "GuiInspectorButtonProfile" ); + mBrowseButton->registerObject(); + addObject( mBrowseButton ); + + // Position + mBrowseButton->resize( browseRect.point, browseRect.extent ); + } + + return retCtrl; +} + +bool GuiInspectorTypeEaseF::resize( const Point2I &newPosition, const Point2I &newExtent ) +{ + if ( !Parent::resize( newPosition, newExtent ) ) + return false; + + if ( mEdit != NULL ) + { + return updateRects(); + } + + return false; +} + +bool GuiInspectorTypeEaseF::updateRects() +{ + S32 dividerPos, dividerMargin; + mInspector->getDivider( dividerPos, dividerMargin ); + Point2I fieldExtent = getExtent(); + Point2I fieldPos = getPosition(); + + mCaptionRect.set( 0, 0, fieldExtent.x - dividerPos - dividerMargin, fieldExtent.y ); + mEditCtrlRect.set( fieldExtent.x - dividerPos + dividerMargin, 1, dividerPos - dividerMargin - 32, fieldExtent.y ); + + bool editResize = mEdit->resize( mEditCtrlRect.point, mEditCtrlRect.extent ); + bool browseResize = false; + + if ( mBrowseButton != NULL ) + { + mBrowseRect.set( fieldExtent.x - 20, 2, 14, fieldExtent.y - 4 ); + browseResize = mBrowseButton->resize( mBrowseRect.point, mBrowseRect.extent ); + } + + return ( editResize || browseResize ); +} + +//----------------------------------------------------------------------------- +// GuiInspectorTypeColor (Base for ColorI/ColorF) +//----------------------------------------------------------------------------- +GuiInspectorTypeColor::GuiInspectorTypeColor() + : mBrowseButton( NULL ) +{ +} + +IMPLEMENT_CONOBJECT(GuiInspectorTypeColor); + +ConsoleDocClass( GuiInspectorTypeColor, + "@brief Inspector field type for TypeColor\n\n" + "Editor use only.\n\n" + "@internal" +); + +GuiControl* GuiInspectorTypeColor::constructEditControl() +{ + GuiControl* retCtrl = new GuiTextEditCtrl(); + + // If we couldn't construct the control, bail! + if( retCtrl == NULL ) + return retCtrl; + + // Let's make it look pretty. + retCtrl->setDataField( StringTable->insert("profile"), NULL, "GuiInspectorTextEditProfile" ); + + // Don't forget to register ourselves + _registerEditControl( retCtrl ); + + char szBuffer[512]; + dSprintf( szBuffer, 512, "%d.apply(%d.getText());",getId(), retCtrl->getId() ); + retCtrl->setField("AltCommand", szBuffer ); + retCtrl->setField("Validate", szBuffer ); + + mBrowseButton = new GuiSwatchButtonCtrl(); + + if ( mBrowseButton != NULL ) + { + RectI browseRect( Point2I( ( getLeft() + getWidth()) - 26, getTop() + 2), Point2I(20, getHeight() - 4) ); + mBrowseButton->setDataField( StringTable->insert("Profile"), NULL, "GuiInspectorSwatchButtonProfile" ); + mBrowseButton->registerObject(); + addObject( mBrowseButton ); + + char szColor[512]; + if( _getColorConversionFunction() ) + dSprintf( szColor, 512, "%s( %d.color )", _getColorConversionFunction(), mBrowseButton->getId() ); + else + dSprintf( szColor, 512, "%d.color", mBrowseButton->getId() ); + + // If the inspector supports the alternate undo recording path, + // use this here. + + char szBuffer[2048]; + GuiInspector* inspector = getInspector(); + if( inspector->isMethod( "onInspectorPreFieldModification" ) ) + { + dSprintf( szBuffer, sizeof( szBuffer ), + "%d.onInspectorPreFieldModification(\"%s\",\"%s\"); %s(%s, \"%d.onInspectorPostFieldModification(); %d.applyWithoutUndo\", %d.getRoot(), \"%d.applyWithoutUndo\", \"%d.onInspectorDiscardFieldModification(); %%unused=\");", + inspector->getId(), getRawFieldName(), getArrayIndex(), + mColorFunction, szColor, inspector->getId(), getId(), + getId(), + getId(), + inspector->getId() + ); + } + else + dSprintf( szBuffer, sizeof( szBuffer ), + "%s(%s, \"%d.apply\", %d.getRoot());", + mColorFunction, szColor, getId(), getId() ); + + mBrowseButton->setConsoleCommand( szBuffer ); + mBrowseButton->setUseMouseEvents( true ); // Allow drag&drop. + + // Position + mBrowseButton->resize( browseRect.point, browseRect.extent ); + } + + return retCtrl; +} + +bool GuiInspectorTypeColor::resize( const Point2I &newPosition, const Point2I &newExtent ) +{ + if( !Parent::resize( newPosition, newExtent ) ) + return false; + + return false; +} + +bool GuiInspectorTypeColor::updateRects() +{ + Point2I fieldPos = getPosition(); + Point2I fieldExtent = getExtent(); + S32 dividerPos, dividerMargin; + mInspector->getDivider( dividerPos, dividerMargin ); + + mCaptionRect.set( 0, 0, fieldExtent.x - dividerPos - dividerMargin, fieldExtent.y ); + mBrowseRect.set( fieldExtent.x - 20, 2, 14, fieldExtent.y - 4 ); + mEditCtrlRect.set( fieldExtent.x - dividerPos + dividerMargin, 1, dividerPos - dividerMargin - 29, fieldExtent.y ); + + bool editResize = mEdit->resize( mEditCtrlRect.point, mEditCtrlRect.extent ); + bool browseResize = false; + + if ( mBrowseButton != NULL ) + { + browseResize = mBrowseButton->resize( mBrowseRect.point, mBrowseRect.extent ); + } + + return ( editResize || browseResize ); +} + + +//----------------------------------------------------------------------------- +// GuiInspectorTypeColorI +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(GuiInspectorTypeColorI); + +ConsoleDocClass( GuiInspectorTypeColorI, + "@brief Inspector field type for ColorI\n\n" + "Editor use only.\n\n" + "@internal" +); + +void GuiInspectorTypeColorI::consoleInit() +{ + Parent::consoleInit(); + + ConsoleBaseType::getType(TypeColorI)->setInspectorFieldType("GuiInspectorTypeColorI"); +} + +GuiInspectorTypeColorI::GuiInspectorTypeColorI() +{ + mColorFunction = StringTable->insert("getColorI"); +} + +void GuiInspectorTypeColorI::setValue( StringTableEntry newValue ) +{ + // Allow parent to set the edit-ctrl text to the new value. + Parent::setValue( newValue ); + + // Now we also set our color swatch button to the new color value. + if ( mBrowseButton ) + { + ColorI color(255,0,255,255); + S32 r,g,b,a; + dSscanf( newValue, "%d %d %d %d", &r, &g, &b, &a ); + color.red = r; + color.green = g; + color.blue = b; + color.alpha = a; + mBrowseButton->setColor( color ); + } +} + +//----------------------------------------------------------------------------- +// GuiInspectorTypeColorF +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(GuiInspectorTypeColorF); + +ConsoleDocClass( GuiInspectorTypeColorF, + "@brief Inspector field type for ColorF\n\n" + "Editor use only.\n\n" + "@internal" +); + +void GuiInspectorTypeColorF::consoleInit() +{ + Parent::consoleInit(); + + ConsoleBaseType::getType(TypeColorF)->setInspectorFieldType("GuiInspectorTypeColorF"); +} + +GuiInspectorTypeColorF::GuiInspectorTypeColorF() +{ + mColorFunction = StringTable->insert("getColorF"); +} + +void GuiInspectorTypeColorF::setValue( StringTableEntry newValue ) +{ + // Allow parent to set the edit-ctrl text to the new value. + Parent::setValue( newValue ); + + // Now we also set our color swatch button to the new color value. + if ( mBrowseButton ) + { + ColorF color(1,0,1,1); + dSscanf( newValue, "%f %f %f %f", &color.red, &color.green, &color.blue, &color.alpha ); + mBrowseButton->setColor( color ); + } +} + +//----------------------------------------------------------------------------- +// GuiInspectorTypeS32 +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(GuiInspectorTypeS32); + +ConsoleDocClass( GuiInspectorTypeS32, + "@brief Inspector field type for S32\n\n" + "Editor use only.\n\n" + "@internal" +); + +void GuiInspectorTypeS32::consoleInit() +{ + Parent::consoleInit(); + + ConsoleBaseType::getType(TypeS32)->setInspectorFieldType("GuiInspectorTypeS32"); +} + +GuiControl* GuiInspectorTypeS32::constructEditControl() +{ + GuiControl* retCtrl = new GuiTextEditSliderCtrl(); + + // If we couldn't construct the control, bail! + if( retCtrl == NULL ) + return retCtrl; + + retCtrl->setDataField( StringTable->insert("profile"), NULL, "GuiInspectorTextEditProfile" ); + + // Don't forget to register ourselves + _registerEditControl( retCtrl ); + + char szBuffer[512]; + dSprintf( szBuffer, 512, "%d.apply(%d.getText());",getId(), retCtrl->getId() ); + retCtrl->setField("AltCommand", szBuffer ); + retCtrl->setField("Validate", szBuffer ); + retCtrl->setField("increment","1"); + retCtrl->setField("format","%d"); + retCtrl->setField("range","-2147483648 2147483647"); + + return retCtrl; +} + +void GuiInspectorTypeS32::setValue( StringTableEntry newValue ) +{ + GuiTextEditSliderCtrl *ctrl = dynamic_cast( mEdit ); + if( ctrl != NULL ) + ctrl->setText( newValue ); +} + +//----------------------------------------------------------------------------- +// GuiInspectorTypeS32Mask +//----------------------------------------------------------------------------- + +GuiInspectorTypeBitMask32::GuiInspectorTypeBitMask32() + : mRollout( NULL ), + mArrayCtrl( NULL ), + mHelper( NULL ) +{ +} + +IMPLEMENT_CONOBJECT( GuiInspectorTypeBitMask32 ); + +ConsoleDocClass( GuiInspectorTypeBitMask32, + "@brief Inspector field type for TypeBitMask32.\n\n" + "Editor use only.\n\n" + "@internal" +); + +bool GuiInspectorTypeBitMask32::onAdd() +{ + // Skip our parent because we aren't using mEditCtrl + // and according to our parent that would be cause to fail onAdd. + if ( !Parent::Parent::onAdd() ) + return false; + + if ( !mInspector ) + return false; + + const EnumTable* table = mField->table; + if( !table ) + { + ConsoleBaseType* type = ConsoleBaseType::getType( mField->type ); + if( type && type->getEnumTable() ) + table = type->getEnumTable(); + else + return false; + } + + static StringTableEntry sProfile = StringTable->insert( "profile" ); + setDataField( sProfile, NULL, "GuiInspectorFieldProfile" ); + setBounds(0,0,100,18); + + // Allocate our children controls... + + mRollout = new GuiRolloutCtrl(); + mRollout->setMargin( 14, 0, 0, 0 ); + mRollout->setCanCollapse( false ); + mRollout->registerObject(); + addObject( mRollout ); + + mArrayCtrl = new GuiDynamicCtrlArrayControl(); + mArrayCtrl->setDataField( sProfile, NULL, "GuiInspectorBitMaskArrayProfile" ); + mArrayCtrl->setField( "autoCellSize", "true" ); + mArrayCtrl->setField( "fillRowFirst", "true" ); + mArrayCtrl->setField( "dynamicSize", "true" ); + mArrayCtrl->setField( "rowSpacing", "4" ); + mArrayCtrl->setField( "colSpacing", "1" ); + mArrayCtrl->setField( "frozen", "true" ); + mArrayCtrl->registerObject(); + + mRollout->addObject( mArrayCtrl ); + + GuiCheckBoxCtrl *pCheckBox = NULL; + + const EngineEnumTable& t = *table; + const U32 numValues = t.getNumValues(); + + for ( S32 i = 0; i < numValues; i++ ) + { + pCheckBox = new GuiCheckBoxCtrl(); + pCheckBox->setText( t[ i ].getName() ); + pCheckBox->registerObject(); + mArrayCtrl->addObject( pCheckBox ); + + pCheckBox->autoSize(); + + // Override the normal script callbacks for GuiInspectorTypeCheckBox + char szBuffer[512]; + dSprintf( szBuffer, 512, "%d.applyBit();", getId() ); + pCheckBox->setField( "Command", szBuffer ); + } + + mArrayCtrl->setField( "frozen", "false" ); + mArrayCtrl->refresh(); + + mHelper = new GuiInspectorTypeBitMask32Helper(); + mHelper->init( mInspector, mParent ); + mHelper->mParentRollout = mRollout; + mHelper->mParentField = this; + mHelper->setInspectorField( mField, mCaption, mFieldArrayIndex ); + mHelper->registerObject(); + mHelper->setExtent( pCheckBox->getExtent() ); + mHelper->setPosition( 0, 0 ); + mRollout->addObject( mHelper ); + + mRollout->sizeToContents(); + mRollout->instantCollapse(); + + updateValue(); + + return true; +} + +void GuiInspectorTypeBitMask32::consoleInit() +{ + Parent::consoleInit(); + + // Set this to be the inspector type for all bitfield console types. + + for( ConsoleBaseType* type = ConsoleBaseType::getListHead(); type != NULL; type = type->getListNext() ) + if( type->getTypeInfo() && type->getTypeInfo()->isBitfield() ) + type->setInspectorFieldType( "GuiInspectorTypeBitMask32" ); +} + +void GuiInspectorTypeBitMask32::childResized( GuiControl *child ) +{ + setExtent( mRollout->getExtent() ); +} + +bool GuiInspectorTypeBitMask32::resize(const Point2I &newPosition, const Point2I &newExtent) +{ + if ( !Parent::resize( newPosition, newExtent ) ) + return false; + + // Hack... height of 18 is hardcoded + return mHelper->resize( Point2I(0,0), Point2I( newExtent.x, 18 ) ); +} + +bool GuiInspectorTypeBitMask32::updateRects() +{ + if ( !mRollout ) + return false; + + bool result = mRollout->setExtent( getExtent() ); + + for ( U32 i = 0; i < mArrayCtrl->size(); i++ ) + { + GuiInspectorField *pField = dynamic_cast( mArrayCtrl->at(i) ); + if ( pField ) + if ( pField->updateRects() ) + result = true; + } + + if ( mHelper && mHelper->updateRects() ) + result = true; + + return result; +} + +StringTableEntry GuiInspectorTypeBitMask32::getValue() +{ + if ( !mRollout ) + return StringTable->insert( "" ); + + S32 mask = 0; + + for ( U32 i = 0; i < mArrayCtrl->size(); i++ ) + { + GuiCheckBoxCtrl *pCheckBox = dynamic_cast( mArrayCtrl->at(i) ); + + bool bit = pCheckBox->getStateOn(); + mask |= bit << i; + } + + return StringTable->insert( String::ToString(mask).c_str() ); +} + +void GuiInspectorTypeBitMask32::setValue( StringTableEntry value ) +{ + U32 mask = dAtoui( value, 0 ); + + for ( U32 i = 0; i < mArrayCtrl->size(); i++ ) + { + GuiCheckBoxCtrl *pCheckBox = dynamic_cast( mArrayCtrl->at(i) ); + + bool bit = mask & ( 1 << i ); + pCheckBox->setStateOn( bit ); + } + + mHelper->setValue( value ); +} + +void GuiInspectorTypeBitMask32::updateData() +{ + StringTableEntry data = getValue(); + setData( data ); +} + +ConsoleMethod( GuiInspectorTypeBitMask32, applyBit, void, 2,2, "apply();" ) +{ + object->updateData(); +} + +//------------------------------------------------------------------------------ +// GuiInspectorTypeS32MaskHelper +//------------------------------------------------------------------------------ + +GuiInspectorTypeBitMask32Helper::GuiInspectorTypeBitMask32Helper() +: mButton( NULL ), + mParentRollout( NULL ), + mParentField( NULL ) +{ +} + +IMPLEMENT_CONOBJECT( GuiInspectorTypeBitMask32Helper ); + +ConsoleDocClass( GuiInspectorTypeBitMask32Helper, + "@brief Inspector field type support for TypeBitMask32.\n\n" + "Editor use only.\n\n" + "@internal" +); + +GuiControl* GuiInspectorTypeBitMask32Helper::constructEditControl() +{ + GuiControl *retCtrl = new GuiTextEditCtrl(); + retCtrl->setDataField( StringTable->insert("profile"), NULL, "GuiInspectorTextEditProfile" ); + retCtrl->setField( "hexDisplay", "true" ); + + _registerEditControl( retCtrl ); + + char szBuffer[512]; + dSprintf( szBuffer, 512, "%d.apply(%d.getText());", mParentField->getId(), retCtrl->getId() ); + retCtrl->setField( "AltCommand", szBuffer ); + retCtrl->setField( "Validate", szBuffer ); + + mButton = new GuiBitmapButtonCtrl(); + + RectI browseRect( Point2I( ( getLeft() + getWidth()) - 26, getTop() + 2), Point2I(20, getHeight() - 4) ); + dSprintf( szBuffer, 512, "%d.toggleExpanded(false);", mParentRollout->getId() ); + mButton->setField( "Command", szBuffer ); + mButton->setField( "buttonType", "ToggleButton" ); + mButton->setDataField( StringTable->insert("Profile"), NULL, "GuiInspectorButtonProfile" ); + mButton->setBitmap( "core/art/gui/images/arrowBtn" ); + mButton->setStateOn( true ); + mButton->setExtent( 16, 16 ); + mButton->registerObject(); + addObject( mButton ); + + mButton->resize( browseRect.point, browseRect.extent ); + + return retCtrl; +} + +bool GuiInspectorTypeBitMask32Helper::resize(const Point2I &newPosition, const Point2I &newExtent) +{ + if ( !Parent::resize( newPosition, newExtent ) ) + return false; + + if ( mEdit != NULL ) + { + return updateRects(); + } + + return false; +} + +bool GuiInspectorTypeBitMask32Helper::updateRects() +{ + S32 dividerPos, dividerMargin; + mInspector->getDivider( dividerPos, dividerMargin ); + Point2I fieldExtent = getExtent(); + Point2I fieldPos = getPosition(); + + mCaptionRect.set( 0, 0, fieldExtent.x - dividerPos - dividerMargin, fieldExtent.y ); + mEditCtrlRect.set( fieldExtent.x - dividerPos + dividerMargin, 1, dividerPos - dividerMargin - 32, fieldExtent.y ); + + bool editResize = mEdit->resize( mEditCtrlRect.point, mEditCtrlRect.extent ); + bool buttonResize = false; + + if ( mButton != NULL ) + { + mButtonRect.set( fieldExtent.x - 26, 2, 16, 16 ); + buttonResize = mButton->resize( mButtonRect.point, mButtonRect.extent ); + } + + return ( editResize || buttonResize ); +} + +void GuiInspectorTypeBitMask32Helper::setValue( StringTableEntry newValue ) +{ + GuiTextEditCtrl *edit = dynamic_cast(mEdit); + edit->setText( newValue ); +} + + +//----------------------------------------------------------------------------- +// GuiInspectorTypeName +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(GuiInspectorTypeName); + +ConsoleDocClass( GuiInspectorTypeName, + "@brief Inspector field type for Name\n\n" + "Editor use only.\n\n" + "@internal" +); + +void GuiInspectorTypeName::consoleInit() +{ + Parent::consoleInit(); + + ConsoleBaseType::getType(TypeName)->setInspectorFieldType("GuiInspectorTypeName"); +} + +bool GuiInspectorTypeName::verifyData( StringTableEntry data ) +{ + return validateObjectName( data, mInspector->getInspectObject() ); +} + + +//----------------------------------------------------------------------------- +// GuiInspectorTypeSFXParameterName +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(GuiInspectorTypeSFXParameterName); + +ConsoleDocClass( GuiInspectorTypeSFXParameterName, + "@brief Inspector field type for SFXParameter\n\n" + "Editor use only.\n\n" + "@internal" +); + +void GuiInspectorTypeSFXParameterName::_populateMenu( GuiPopUpMenuCtrl *menu ) +{ + SimSet* set = Sim::getSFXParameterGroup(); + for( SimSet::iterator iter = set->begin(); iter != set->end(); ++ iter ) + { + SFXParameter* parameter = dynamic_cast< SFXParameter* >( *iter ); + if( parameter ) + menu->addEntry( parameter->getInternalName(), parameter->getId() ); + } + + menu->sort(); +} + +void GuiInspectorTypeSFXParameterName::consoleInit() +{ + Parent::consoleInit(); + + ConsoleBaseType::getType( TypeSFXParameterName )->setInspectorFieldType( "GuiInspectorTypeSFXParameterName" ); +} + + +//----------------------------------------------------------------------------- +// GuiInspectorTypeSFXStateName +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(GuiInspectorTypeSFXStateName); + +ConsoleDocClass( GuiInspectorTypeSFXStateName, + "@brief Inspector field type for SFXState\n\n" + "Editor use only.\n\n" + "@internal" +); + +void GuiInspectorTypeSFXStateName::_populateMenu( GuiPopUpMenuCtrl *menu ) +{ + menu->addEntry( "", 0 ); + + SimSet* set = Sim::getSFXStateSet(); + for( SimSet::iterator iter = set->begin(); iter != set->end(); ++ iter ) + { + SFXState* state = dynamic_cast< SFXState* >( *iter ); + if( state ) + menu->addEntry( state->getName(), state->getId() ); + } + + menu->sort(); +} + +void GuiInspectorTypeSFXStateName::consoleInit() +{ + Parent::consoleInit(); + + ConsoleBaseType::getType( TypeSFXStateName )->setInspectorFieldType( "GuiInspectorTypeSFXStateName" ); +} + + +//----------------------------------------------------------------------------- +// GuiInspectorTypeSFXSourceName +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(GuiInspectorTypeSFXSourceName); + +ConsoleDocClass( GuiInspectorTypeSFXSourceName, + "@brief Inspector field type for SFXSource\n\n" + "Editor use only.\n\n" + "@internal" +); + +void GuiInspectorTypeSFXSourceName::_populateMenu( GuiPopUpMenuCtrl *menu ) +{ + menu->addEntry( "", 0 ); + + SimSet* set = Sim::getSFXSourceSet(); + for( SimSet::iterator iter = set->begin(); iter != set->end(); ++ iter ) + { + SFXSource* source = dynamic_cast< SFXSource* >( *iter ); + if( source && source->getName() ) + menu->addEntry( source->getName(), source->getId() ); + } + + menu->sort(); +} + +void GuiInspectorTypeSFXSourceName::consoleInit() +{ + Parent::consoleInit(); + + ConsoleBaseType::getType( TypeSFXSourceName )->setInspectorFieldType( "GuiInspectorTypeSFXSourceName" ); +} diff --git a/Engine/source/gui/editor/guiInspectorTypes.h b/Engine/source/gui/editor/guiInspectorTypes.h new file mode 100644 index 000000000..eff813b34 --- /dev/null +++ b/Engine/source/gui/editor/guiInspectorTypes.h @@ -0,0 +1,580 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _GUI_INSPECTOR_TYPES_H_ +#define _GUI_INSPECTOR_TYPES_H_ + +#ifndef _GUI_INSPECTOR_H_ +#include "gui/editor/guiInspector.h" +#endif + +#ifndef _GUI_INSPECTOR_FIELD_H_ +#include "gui/editor/inspector/field.h" +#endif + +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif + +#ifndef _GUICHECKBOXCTRL_H_ +#include "gui/buttons/guiCheckBoxCtrl.h" +#endif + +#ifndef _GUIBITMAPBUTTON_H_ +#include "gui/buttons/guiBitmapButtonCtrl.h" +#endif + +class GuiPopUpMenuCtrl; + +/// A base class for other inspector field types which +/// wish to display a popup/dropdown menu. +class GuiInspectorTypeMenuBase : public GuiInspectorField +{ +private: + typedef GuiInspectorField Parent; +public: + + DECLARE_CONOBJECT(GuiInspectorTypeMenuBase); + + //----------------------------------------------------------------------------- + // Override able methods for custom edit fields + //----------------------------------------------------------------------------- + virtual GuiControl* constructEditControl(); + virtual void setValue( StringTableEntry newValue ); + virtual void _populateMenu( GuiPopUpMenuCtrl *menu ); +}; + +//----------------------------------------------------------------------------- +// TypeEnum GuiInspectorField Class +//----------------------------------------------------------------------------- +class GuiInspectorTypeEnum : public GuiInspectorTypeMenuBase +{ +private: + typedef GuiInspectorTypeMenuBase Parent; +public: + DECLARE_CONOBJECT(GuiInspectorTypeEnum); + static void consoleInit(); + + virtual void _populateMenu( GuiPopUpMenuCtrl *menu ); +}; + +//----------------------------------------------------------------------------- +// TypeCubemapName GuiInspectorField Class +//----------------------------------------------------------------------------- +class GuiInspectorTypeCubemapName : public GuiInspectorTypeMenuBase +{ +private: + typedef GuiInspectorTypeMenuBase Parent; +public: + DECLARE_CONOBJECT(GuiInspectorTypeCubemapName); + static void consoleInit(); + + virtual void _populateMenu( GuiPopUpMenuCtrl *menu ); +}; + +//-------------------------------------------------------------------------------- +// TypeMaterialName GuiInspectorField Class +//-------------------------------------------------------------------------------- +class GuiBitmapButtonCtrl; + +class GuiInspectorTypeMaterialName : public GuiInspectorField +{ + typedef GuiInspectorField Parent; + +public: + + GuiInspectorTypeMaterialName(); + + DECLARE_CONOBJECT(GuiInspectorTypeMaterialName); + static void consoleInit(); + + GuiBitmapButtonCtrl *mBrowseButton; + RectI mBrowseRect; + + GuiControl* construct(const char* command); + + //----------------------------------------------------------------------------- + // Override able methods for custom edit fields + //----------------------------------------------------------------------------- + virtual GuiControl* constructEditControl(); + virtual bool updateRects(); +}; + +class GuiInspectorTypeRegularMaterialName : public GuiInspectorTypeMaterialName +{ + typedef GuiInspectorTypeMaterialName Parent; +public: + GuiInspectorTypeRegularMaterialName() {} + DECLARE_CONOBJECT(GuiInspectorTypeRegularMaterialName); + static void consoleInit(); + virtual void _populateMenu( GuiPopUpMenuCtrl *menu ); +}; + +//-------------------------------------------------------------------------------- +// TypeTerrainMaterialIndex GuiInspectorField Class +//-------------------------------------------------------------------------------- +class GuiInspectorTypeTerrainMaterialIndex : public GuiInspectorTypeMaterialName +{ + typedef GuiInspectorTypeMaterialName Parent; + +public: + + GuiInspectorTypeTerrainMaterialIndex() {} + + DECLARE_CONOBJECT(GuiInspectorTypeTerrainMaterialIndex); + static void consoleInit(); + + //----------------------------------------------------------------------------- + // Override able methods for custom edit fields + //----------------------------------------------------------------------------- + virtual GuiControl* constructEditControl(); +}; + +//-------------------------------------------------------------------------------- +// TypeTerrainMaterialName GuiInspectorField Class +//-------------------------------------------------------------------------------- +class GuiInspectorTypeTerrainMaterialName : public GuiInspectorTypeMaterialName +{ + typedef GuiInspectorTypeMaterialName Parent; + +public: + + GuiInspectorTypeTerrainMaterialName() {} + + DECLARE_CONOBJECT(GuiInspectorTypeTerrainMaterialName); + static void consoleInit(); + GuiControl* construct(const char* command); + //----------------------------------------------------------------------------- + // Override able methods for custom edit fields + //----------------------------------------------------------------------------- + virtual GuiControl* constructEditControl(); +}; + +//----------------------------------------------------------------------------- +// GuiInspectorTypeGuiProfile Class +//----------------------------------------------------------------------------- +class GuiInspectorTypeGuiProfile : public GuiInspectorTypeMenuBase +{ +private: + typedef GuiInspectorTypeMenuBase Parent; +public: + DECLARE_CONOBJECT(GuiInspectorTypeGuiProfile); + static void consoleInit(); + + virtual void _populateMenu( GuiPopUpMenuCtrl *menu ); +}; + +//----------------------------------------------------------------------------- +// GuiInspectorTypeCheckBox Class +//----------------------------------------------------------------------------- +class GuiInspectorTypeCheckBox : public GuiInspectorField +{ +private: + typedef GuiInspectorField Parent; +public: + DECLARE_CONOBJECT(GuiInspectorTypeCheckBox); + static void consoleInit(); + + //----------------------------------------------------------------------------- + // Override able methods for custom edit fields + //----------------------------------------------------------------------------- + virtual GuiControl* constructEditControl(); + virtual void setValue( StringTableEntry newValue ); + virtual const char* getValue(); +}; + +//----------------------------------------------------------------------------- +// TypeCommand GuiInspectorField Class +//----------------------------------------------------------------------------- +class GuiInspectorTypeCommand : public GuiInspectorField +{ +private: + typedef GuiInspectorField Parent; + StringTableEntry mTextEditorCommand; + void _setCommand( GuiButtonCtrl *ctrl, StringTableEntry command ); +public: + DECLARE_CONOBJECT(GuiInspectorTypeCommand); + GuiInspectorTypeCommand(); + static void consoleInit(); + + //----------------------------------------------------------------------------- + // Override able methods for custom edit fields + //----------------------------------------------------------------------------- + virtual GuiControl* constructEditControl(); + virtual void setValue( StringTableEntry data ); +}; + +//----------------------------------------------------------------------------- +// TypeFileName GuiInspectorField Class +//----------------------------------------------------------------------------- +class GuiInspectorTypeFileName : public GuiInspectorField +{ +private: + typedef GuiInspectorField Parent; +public: + DECLARE_CONOBJECT(GuiInspectorTypeFileName); + static void consoleInit(); + + SimObjectPtr mBrowseButton; + RectI mBrowseRect; + + //----------------------------------------------------------------------------- + // Override able methods for custom edit fields + //----------------------------------------------------------------------------- + virtual GuiControl* constructEditControl(); + virtual bool resize(const Point2I &newPosition, const Point2I &newExtent); + virtual bool updateRects(); + virtual void updateValue(); +}; + + +//----------------------------------------------------------------------------- +// TypeImageFileName GuiInspectorField Class +//----------------------------------------------------------------------------- +class GuiInspectorTypeImageFileName : public GuiInspectorTypeFileName +{ + typedef GuiInspectorTypeFileName Parent; +public: + + DECLARE_CONOBJECT(GuiInspectorTypeImageFileName); + static void consoleInit(); + + virtual GuiControl* constructEditControl(); + bool renderTooltip( const Point2I &hoverPos, const Point2I &cursorPos, const char *tipText = NULL ); +}; + +//----------------------------------------------------------------------------- +// TypeRectUV GuiInspectorField Class +//----------------------------------------------------------------------------- +class GuiInspectorTypeRectUV : public GuiInspectorField +{ + typedef GuiInspectorField Parent; +public: + GuiBitmapButtonCtrl *mBrowseButton; + RectI mBrowseRect; + +public: + DECLARE_CONOBJECT(GuiInspectorTypeRectUV); + GuiInspectorTypeRectUV(); + static void consoleInit(); + + //----------------------------------------------------------------------------- + // Override able methods for custom edit fields + //----------------------------------------------------------------------------- + virtual GuiControl* constructEditControl(); + virtual bool updateRects(); +}; + +//----------------------------------------------------------------------------- +// TypeEaseF GuiInspectorField Class +//----------------------------------------------------------------------------- + +class GuiInspectorTypeEaseF : public GuiInspectorField +{ + public: + + typedef GuiInspectorField Parent; + + protected: + + SimObjectPtr mBrowseButton; + RectI mBrowseRect; + + public: + + GuiInspectorTypeEaseF(); + + DECLARE_CONOBJECT( GuiInspectorTypeEaseF ); + + static void consoleInit(); + + //----------------------------------------------------------------------------- + // Override able methods for custom edit fields + //----------------------------------------------------------------------------- + virtual GuiControl* constructEditControl(); + virtual bool resize(const Point2I &newPosition, const Point2I &newExtent); + virtual bool updateRects(); +}; + +//----------------------------------------------------------------------------- +// TypePrefabFilename GuiInspectorField Class +//----------------------------------------------------------------------------- +class GuiInspectorTypePrefabFilename : public GuiInspectorTypeFileName +{ + typedef GuiInspectorTypeFileName Parent; +public: + + DECLARE_CONOBJECT(GuiInspectorTypePrefabFilename); + static void consoleInit(); + + virtual GuiControl* constructEditControl(); +}; + +//----------------------------------------------------------------------------- +// TypeShapeFilename GuiInspectorField Class +//----------------------------------------------------------------------------- +class GuiInspectorTypeShapeFilename : public GuiInspectorTypeFileName +{ + typedef GuiInspectorTypeFileName Parent; +public: + + GuiBitmapButtonCtrl *mShapeEdButton; + + DECLARE_CONOBJECT(GuiInspectorTypeShapeFilename); + static void consoleInit(); + + virtual GuiControl* constructEditControl(); + virtual bool updateRects(); +}; + +//----------------------------------------------------------------------------- +// TypeColor GuiInspectorField Class (Base for ColorI/ColorF) +//----------------------------------------------------------------------------- + +class GuiSwatchButtonCtrl; + +class GuiInspectorTypeColor : public GuiInspectorField +{ + typedef GuiInspectorField Parent; + +protected: + + /// Return the name of a function that will be used to convert the + /// floating-point color of the swatch button to the form used by the + /// data field. + virtual const char* _getColorConversionFunction() const { return NULL; } + +public: + + GuiInspectorTypeColor(); + + DECLARE_CONOBJECT(GuiInspectorTypeColor); + + StringTableEntry mColorFunction; + GuiSwatchButtonCtrl *mBrowseButton; + RectI mBrowseRect; + + //----------------------------------------------------------------------------- + // Override able methods for custom edit fields + //----------------------------------------------------------------------------- + virtual GuiControl* constructEditControl(); + virtual bool resize(const Point2I &newPosition, const Point2I &newExtent); + virtual bool updateRects(); +}; + +//----------------------------------------------------------------------------- +// TypeColorI GuiInspectorField Class +//----------------------------------------------------------------------------- +class GuiInspectorTypeColorI : public GuiInspectorTypeColor +{ + typedef GuiInspectorTypeColor Parent; + +protected: + + virtual const char* _getColorConversionFunction() const { return "ColorFloatToInt"; } + +public: + + GuiInspectorTypeColorI(); + + DECLARE_CONOBJECT(GuiInspectorTypeColorI); + + static void consoleInit(); + void setValue( StringTableEntry newValue ); +}; + +//----------------------------------------------------------------------------- +// TypeColorF GuiInspectorField Class +//----------------------------------------------------------------------------- +class GuiInspectorTypeColorF : public GuiInspectorTypeColor +{ + typedef GuiInspectorTypeColor Parent; + +public: + + GuiInspectorTypeColorF(); + + DECLARE_CONOBJECT(GuiInspectorTypeColorF); + + static void consoleInit(); + void setValue( StringTableEntry newValue ); +}; + +/* NOTE: Evidently this isn't used anywhere (or implemented) so i commented it out +//------------------------------------------------------------------------------ +// TypeString GuiInspectorField class +//------------------------------------------------------------------------------ +class GuiInspectorTypeString : public GuiInspectorField +{ +private: + typedef GuiInspectorField Parent; +public: + DECLARE_CONOBJECT(GuiInspectorTypeString); + static void consoleInit(); + + SimObjectPtr mBrowseButton; + + virtual GuiControl* constructEditControl(); + virtual bool resize(const Point2I &newPosition, const Point2I &newExtent); + virtual bool updateRects(); +}; +*/ + + +//------------------------------------------------------------------------------ +// TypeS32 GuiInspectorField class +//------------------------------------------------------------------------------ +class GuiInspectorTypeS32 : public GuiInspectorField +{ +private: + typedef GuiInspectorField Parent; +public: + DECLARE_CONOBJECT(GuiInspectorTypeS32); + static void consoleInit(); + + virtual GuiControl* constructEditControl(); + virtual void setValue( StringTableEntry newValue ); +}; + + +//------------------------------------------------------------------------------ +// TypeBitMask32 GuiInspectorField class +//------------------------------------------------------------------------------ +class GuiInspectorTypeBitMask32Helper; +class GuiDynamicCtrlArrayControl; + +class GuiInspectorTypeBitMask32 : public GuiInspectorField +{ + typedef GuiInspectorField Parent; + +public: + + GuiInspectorTypeBitMask32(); + virtual ~GuiInspectorTypeBitMask32() {} + + DECLARE_CONOBJECT( GuiInspectorTypeBitMask32 ); + + // ConsoleObject + bool onAdd(); + static void consoleInit(); + + // GuiInspectorField + virtual void childResized( GuiControl *child ); + virtual bool resize( const Point2I &newPosition, const Point2I &newExtent ); + virtual bool updateRects(); + virtual void updateData(); + virtual StringTableEntry getValue(); + virtual void setValue( StringTableEntry value ); + +protected: + + GuiInspectorTypeBitMask32Helper *mHelper; + GuiRolloutCtrl *mRollout; + GuiDynamicCtrlArrayControl *mArrayCtrl; + Vector mChildren; +}; + +class GuiInspectorTypeBitMask32Helper : public GuiInspectorField +{ + typedef GuiInspectorField Parent; + +public: + + GuiInspectorTypeBitMask32Helper(); + + DECLARE_CONOBJECT( GuiInspectorTypeBitMask32Helper ); + + GuiBitmapButtonCtrl *mButton; + GuiRolloutCtrl *mParentRollout; + GuiInspectorTypeBitMask32 *mParentField; + RectI mButtonRect; + + //----------------------------------------------------------------------------- + // Override able methods for custom edit fields + //----------------------------------------------------------------------------- + virtual GuiControl* constructEditControl(); + virtual bool resize( const Point2I &newPosition, const Point2I &newExtent ); + virtual bool updateRects(); + virtual void setValue( StringTableEntry value ); +}; + + +//----------------------------------------------------------------------------- +// TypeName GuiInspectorField Class +//----------------------------------------------------------------------------- +class GuiInspectorTypeName : public GuiInspectorField +{ +private: + typedef GuiInspectorField Parent; +public: + DECLARE_CONOBJECT(GuiInspectorTypeName); + static void consoleInit(); + + virtual bool verifyData( StringTableEntry data ); +}; + + +//----------------------------------------------------------------------------- +// TypeSFXParameterName GuiInspectorField Class +//----------------------------------------------------------------------------- +class GuiInspectorTypeSFXParameterName : public GuiInspectorTypeMenuBase +{ +private: + typedef GuiInspectorTypeMenuBase Parent; +public: + DECLARE_CONOBJECT(GuiInspectorTypeSFXParameterName); + static void consoleInit(); + + virtual void _populateMenu( GuiPopUpMenuCtrl *menu ); +}; + + +//----------------------------------------------------------------------------- +// TypeSFXStateName GuiInspectorField Class +//----------------------------------------------------------------------------- +class GuiInspectorTypeSFXStateName : public GuiInspectorTypeMenuBase +{ +private: + typedef GuiInspectorTypeMenuBase Parent; +public: + DECLARE_CONOBJECT(GuiInspectorTypeSFXStateName); + static void consoleInit(); + + virtual void _populateMenu( GuiPopUpMenuCtrl *menu ); +}; + + +//----------------------------------------------------------------------------- +// TypeSFXSourceName GuiInspectorField Class +//----------------------------------------------------------------------------- +class GuiInspectorTypeSFXSourceName : public GuiInspectorTypeMenuBase +{ +private: + typedef GuiInspectorTypeMenuBase Parent; +public: + DECLARE_CONOBJECT(GuiInspectorTypeSFXSourceName); + static void consoleInit(); + + virtual void _populateMenu( GuiPopUpMenuCtrl *menu ); +}; + + +#endif // _GUI_INSPECTOR_TYPES_H_ \ No newline at end of file diff --git a/Engine/source/gui/editor/guiMenuBar.cpp b/Engine/source/gui/editor/guiMenuBar.cpp new file mode 100644 index 000000000..c2bdbb6a7 --- /dev/null +++ b/Engine/source/gui/editor/guiMenuBar.cpp @@ -0,0 +1,1943 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/editor/guiMenuBar.h" + +#include "console/consoleTypes.h" +#include "console/console.h" +#include "gui/core/guiCanvas.h" +#include "gui/core/guiDefaultControlRender.h" +#include "gui/controls/guiTextListCtrl.h" +#include "sim/actionMap.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxDrawUtil.h" +#include "gfx/primBuilder.h" +#include "console/engineAPI.h" + +// menu bar: +// basic idea - fixed height control bar at the top of a window, placed and sized in gui editor +// menu text for menus or menu items should not begin with a digit +// all menus can be removed via the clearMenus() console command +// each menu is added via the addMenu(menuText, menuId) console command +// each menu is added with a menu id +// menu items are added to menus via that addMenuItem(menu, menuItemText, menuItemId, accelerator, checkGroup) console command +// each menu item is added with a menu item id and an optional accelerator +// menu items are initially enabled, but can be disabled/re-enabled via the setMenuItemEnable(menu,menuItem,bool) +// menu text can be set via the setMenuText(menu, newMenuText) console method +// menu item text can be set via the setMenuItemText console method +// menu items can be removed via the removeMenuItem(menu, menuItem) console command +// menu items can be cleared via the clearMenuItems(menu) console command +// menus can be hidden or shown via the setMenuVisible(menu, bool) console command +// menu items can be hidden or shown via the setMenuItemVisible(menu, menuItem, bool) console command +// menu items can be check'd via the setMenuItemChecked(menu, menuItem, bool) console command +// if the bool is true, any other items in that menu item's check group become unchecked. +// +// menu items can have a bitmap set on them via the setMenuItemBitmap(menu, menuItem, bitmapIndex) +// passing -1 for the bitmap index will result in no bitmap being shown +// the index paramater is an index into the bitmap array of the associated profile +// this can be used, for example, to display a check next to a selected menu item +// bitmap indices are actually multiplied by 3 when indexing into the bitmap +// since bitmaps have normal, selected and disabled states. +// +// menus can be removed via the removeMenu console command +// specification arguments for menus and menu items can be either the id or the text of the menu or menu item +// adding the menu item "-" will add an un-selectable seperator to the menu +// callbacks: +// when a menu is clicked, before it is displayed, the menu calls its onMenuSelect(menuId, menuText) method - +// this allows the callback to enable/disable menu items, or add menu items in a context-sensitive way +// when a menu item is clicked, the menu removes itself from display, then calls onMenuItemSelect(menuId, menuText, menuItemId, menuItemText) + +// the initial implementation does not support: +// hierarchal menus +// keyboard accelerators on menu text (i.e. via alt-key combos) + +//------------------------------------------------------------------------------ + +IMPLEMENT_CONOBJECT(GuiMenuBar); + +ConsoleDocClass( GuiMenuBar, + "@brief GUI Control which displays a horizontal bar with individual drop-down menu items. Each menu item may also have submenu items.\n\n" + + "@tsexample\n" + "new GuiMenuBar(newMenuBar)\n" + "{\n" + " Padding = \"0\";\n" + " //Properties not specific to this control have been omitted from this example.\n" + "};\n\n" + "// Add a menu to the menu bar\n" + "newMenuBar.addMenu(0,\"New Menu\");\n\n" + "// Add a menu item to the New Menu\n" + "newMenuBar.addMenuItem(0,\"New Menu Item\",0,\"n\",-1);\n\n" + "// Add a submenu item to the New Menu Item\n" + "newMenuBar.addSubmenuItem(0,1,\"New Submenu Item\",0,\"s\",-1);\n" + "@endtsexample\n\n" + + "@see GuiTickCtrl\n\n" + + "@ingroup GuiCore\n" +); + +IMPLEMENT_CALLBACK( GuiMenuBar, onMouseInMenu, void, (bool isInMenu),( isInMenu ), + "@brief Called whenever the mouse enters, or persists is in the menu.\n\n" + "@param isInMenu True if the mouse has entered the menu, otherwise is false.\n" + "@note To receive this callback, call setProcessTicks(true) on the menu bar.\n" + "@tsexample\n" + "// Mouse enters or persists within the menu, causing the callback to occur.\n" + "GuiMenuBar::onMouseInMenu(%this,%hasLeftMenu)\n" + "{\n" + " // Code to run when the callback occurs\n" + "}\n" + "@endtsexample\n\n" + "@see GuiTickCtrl\n\n" +); + +IMPLEMENT_CALLBACK( GuiMenuBar, onMenuSelect, void, ( const char* menuId, const char* menuText ),( menuId , menuText ), + "@brief Called whenever a menu is selected.\n\n" + "@param menuId Index id of the clicked menu\n" + "@param menuText Text of the clicked menu\n\n" + "@tsexample\n" + "// A menu has been selected, causing the callback to occur.\n" + "GuiMenuBar::onMenuSelect(%this,%menuId,%menuText)\n" + "{\n" + " // Code to run when the callback occurs\n" + "}\n" + "@endtsexample\n\n" + "@see GuiTickCtrl\n\n" +); + +IMPLEMENT_CALLBACK( GuiMenuBar, onMenuItemSelect, void, ( const char* menuId, const char* menuText, const char* menuItemId, const char* menuItemText ), + ( menuId, menuText, menuItemId, menuItemText ), + "@brief Called whenever an item in a menu is selected.\n\n" + "@param menuId Index id of the menu which contains the selected menu item\n" + "@param menuText Text of the menu which contains the selected menu item\n\n" + "@param menuItemId Index id of the selected menu item\n" + "@param menuItemText Text of the selected menu item\n\n" + "@tsexample\n" + "// A menu item has been selected, causing the callback to occur.\n" + "GuiMenuBar::onMenuItemSelect(%this,%menuId,%menuText,%menuItemId,%menuItemText)\n" + "{\n" + " // Code to run when the callback occurs\n" + "}\n" + "@endtsexample\n\n" + "@see GuiTickCtrl\n\n" +); + +IMPLEMENT_CALLBACK( GuiMenuBar, onSubmenuSelect, void, ( const char* submenuId, const char* submenuText ),( submenuId, submenuText ), + "@brief Called whenever a submenu is selected.\n\n" + "@param submenuId Id of the selected submenu\n" + "@param submenuText Text of the selected submenu\n\n" + "@tsexample\n" + "GuiMenuBar::onSubmenuSelect(%this,%submenuId,%submenuText)\n" + "{\n" + " // Code to run when the callback occurs\n" + "}\n" + "@endtsexample\n\n" + "@see GuiTickCtrl\n\n" +); + +//------------------------------------------------------------------------------ +// console methods +//------------------------------------------------------------------------------ + +DefineEngineMethod( GuiMenuBar, clearMenus, void, ( S32 param1, S32 param2),, + "@brief Clears all the menus from the menu bar.\n\n" + "@tsexample\n" + "// Inform the GuiMenuBar control to clear all menus from itself.\n" + "%thisGuiMenuBar.clearMenus();\n" + "@endtsexample\n\n" + "@see GuiTickCtrl") +{ + object->clearMenus(); +} + +DefineEngineMethod( GuiMenuBar, setMenuMargins, void, (S32 horizontalMargin, S32 verticalMargin, S32 bitmapToTextSpacing),, + "@brief Sets the menu rendering margins: horizontal, vertical, bitmap spacing.\n\n" + "Detailed description\n\n" + "@param horizontalMargin Number of pixels on the left and right side of a menu's text.\n" + "@param verticalMargin Number of pixels on the top and bottom of a menu's text.\n" + "@param bitmapToTextSpacing Number of pixels between a menu's bitmap and text.\n" + "@tsexample\n" + "// Define the horizontalMargin\n" + "%horizontalMargin = \"5\";\n\n" + "// Define the verticalMargin\n" + "%verticalMargin = \"5\";\n\n" + "// Define the bitmapToTextSpacing\n" + "%bitmapToTextSpacing = \"12\";\n\n" + "// Inform the GuiMenuBar control to set its margins based on the defined values.\n" + "%thisGuiMenuBar.setMenuMargins(%horizontalMargin,%verticalMargin,%bitmapToTextSpacing);\n" + "@endtsexample\n\n" + "@see GuiTickCtrl") +{ + object->mHorizontalMargin = horizontalMargin; + object->mVerticalMargin = verticalMargin; + object->mBitmapMargin = bitmapToTextSpacing; +} + +DefineEngineMethod(GuiMenuBar, addMenu, void, (const char* menuText, int menuId),, + "@brief Adds a new menu to the menu bar.\n\n" + "@param menuText Text to display for the new menu item.\n" + "@param menuId ID for the new menu item.\n" + "@tsexample\n" + "// Define the menu text\n" + "%menuText = \"New Menu\";\n\n" + "// Define the menu ID.\n" + "%menuId = \"2\";\n\n" + "// Inform the GuiMenuBar control to add the new menu\n" + "%thisGuiMenuBar.addMenu(%menuText,%menuId);\n" + "@endtsexample\n\n" + "@see GuiTickCtrl") +{ + if(dIsdigit(menuText[0])) + { + Con::errorf("Cannot add menu %s (id = %s). First character of a menu's text cannot be a digit.", menuText, menuId); + return; + } + object->addMenu(menuText, menuId); +} + +DefineEngineMethod(GuiMenuBar, addMenuItem, void, (const char* targetMenu, const char* menuItemText, int menuItemId, const char* accelerator, int checkGroup), + ("","",0,NULL,-1), + "@brief Adds a menu item to the specified menu. The menu argument can be either the text of a menu or its id.\n\n" + "@param menu Menu name or menu Id to add the new item to.\n" + "@param menuItemText Text for the new menu item.\n" + "@param menuItemId Id for the new menu item.\n" + "@param accelerator Accelerator key for the new menu item.\n" + "@param checkGroup Check group to include this menu item in.\n" + "@tsexample\n" + "// Define the menu we wish to add the item to\n" + "%targetMenu = \"New Menu\"; or %menu = \"4\";\n\n" + "// Define the text for the new menu item\n" + "%menuItemText = \"Menu Item\";\n\n" + "// Define the id for the new menu item\n" + "%menuItemId = \"3\";\n\n" + "// Set the accelerator key to toggle this menu item with\n" + "%accelerator = \"n\";\n\n" + "// Define the Check Group that this menu item will be in, if we want it to be in a check group. -1 sets it in no check group.\n" + "%checkGroup = \"4\";\n\n" + "// Inform the GuiMenuBar control to add the new menu item with the defined fields\n" + "%thisGuiMenuBar.addMenuItem(%menu,%menuItemText,%menuItemId,%accelerator,%checkGroup);\n" + "@endtsexample\n\n" + "@see GuiTickCtrl") +{ + if(dIsdigit(menuItemText[0])) + { + Con::errorf("Cannot add menu item %s (id = %s). First character of a menu item's text cannot be a digit.", menuItemText, menuItemId); + return; + } + GuiMenuBar::Menu *menu = object->findMenu(targetMenu); + if(!menu) + { + Con::errorf("Cannot find menu %s for addMenuItem.", targetMenu); + return; + } + object->addMenuItem(menu, menuItemText, menuItemId, accelerator != NULL ? accelerator : "", checkGroup == -1 ? -1 : checkGroup); +} + +DefineEngineMethod(GuiMenuBar, setMenuItemEnable, void, (const char* menuTarget, const char* menuItemTarget, bool enabled),, + "@brief sets the menu item to enabled or disabled based on the enable parameter.\n" + "The specified menu and menu item can either be text or ids.\n\n" + "Detailed description\n\n" + "@param menuTarget Menu to work in\n" + "@param menuItemTarget The menu item inside of the menu to enable or disable\n" + "@param enabled Boolean enable / disable value.\n" + "@tsexample\n" + "// Define the menu\n" + "%menu = \"New Menu\"; or %menu = \"4\";\n\n" + "// Define the menu item\n" + "%menuItem = \"New Menu Item\"; or %menuItem = \"2\";\n\n" + "// Define the enabled state\n" + "%enabled = \"true\";\n\n" + "// Inform the GuiMenuBar control to set the enabled state of the requested menu item\n" + "%thisGuiMenuBar.setMenuItemEnable(%menu,%menuItme,%enabled);\n" + "@endtsexample\n\n" + "@see GuiTickCtrl") +{ + GuiMenuBar::Menu *menu = object->findMenu(menuTarget); + if(!menu) + { + Con::errorf("Cannot find menu %s for setMenuItemEnable.", menuTarget); + return; + } + GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, menuItemTarget); + if(!menuItem) + { + Con::errorf("Cannot find menu item %s for setMenuItemEnable.", menuItemTarget); + return; + } + menuItem->enabled = enabled; +} + +DefineEngineMethod(GuiMenuBar, setCheckmarkBitmapIndex, void, (S32 bitmapindex),, + "@brief Sets the menu bitmap index for the check mark image.\n\n" + "@param bitmapIndex Bitmap index for the check mark image.\n" + "@tsexample\n" + "// Define the bitmap index\n" + "%bitmapIndex = \"2\";\n\n" + "// Inform the GuiMenuBar control of the proper bitmap index for the check mark image\n" + "%thisGuiMenuBar.setCheckmarkBitmapIndex(%bitmapIndex);\n" + "@endtsexample\n\n" + "@see GuiTickCtrl") +{ + object->mCheckmarkBitmapIndex = bitmapindex; +} + +DefineEngineMethod(GuiMenuBar, setMenuItemChecked, void, (const char* menuTarget, const char* menuItemTarget, bool checked),, + "@brief Sets the menu item bitmap to a check mark, which by default is the first element in\n" + "the bitmap array (although this may be changed with setCheckmarkBitmapIndex()).\n" + "Any other menu items in the menu with the same check group become unchecked if they are checked.\n\n" + "@param menuTarget Menu to work in\n" + "@param menuItem Menu item to affect\n" + "@param checked Whether we are setting it to checked or not\n" + "@tsexample\n" + "" + "@endtsexample\n\n" + "@return If not void, return value and description\n\n" + "@see References") +{ + GuiMenuBar::Menu *menu = object->findMenu(menuTarget); + if(!menu) + { + Con::errorf("Cannot find menu %s for setMenuItemChecked.", menuTarget); + return; + } + GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, menuItemTarget); + if(!menuItem) + { + Con::errorf("Cannot find menu item %s for setMenuItemChecked.", menuItemTarget); + return; + } + if(checked && menuItem->checkGroup != -1) + { + // first, uncheck everything in the group: + for(GuiMenuBar::MenuItem *itemWalk = menu->firstMenuItem; itemWalk; itemWalk = itemWalk->nextMenuItem) + if(itemWalk->checkGroup == menuItem->checkGroup && itemWalk->bitmapIndex == object->mCheckmarkBitmapIndex) + itemWalk->bitmapIndex = -1; + } + menuItem->bitmapIndex = checked ? object->mCheckmarkBitmapIndex : -1; +} + +DefineEngineMethod(GuiMenuBar, setMenuText, void, (const char* menuTarget, const char* newMenuText),, + "@brief Sets the text of the specified menu to the new string.\n\n" + "@param menuTarget Menu to affect\n" + "@param newMenuText New menu text\n" + "@tsexample\n" + "// Define the menu to affect" + "%menu = \"New Menu\"; or %menu = \"3\";\n\n" + "// Define the text to change the menu to\n" + "%newMenuText = \"Still a New Menu\";\n\n" + "// Inform the GuiMenuBar control to change the defined menu to the defined text\n" + "%thisGuiMenuBar.setMenuText(%menu,%newMenuText);\n" + "@endtsexample\n\n" + "@see GuiTickCtrl") +{ + if(dIsdigit(menuTarget[0])) + { + Con::errorf("Cannot name menu %s to %s. First character of a menu's text cannot be a digit.", menuTarget, newMenuText); + return; + } + GuiMenuBar::Menu *menu = object->findMenu(menuTarget); + if(!menu) + { + Con::errorf("Cannot find menu %s for setMenuText.", menuTarget); + return; + } + dFree(menu->text); + menu->text = dStrdup(newMenuText); + object->menuBarDirty = true; +} + +DefineEngineMethod(GuiMenuBar, setMenuBitmapIndex, void, (const char* menuTarget, S32 bitmapindex, bool bitmaponly, bool drawborder),, + "@brief Sets the bitmap index for the menu and toggles rendering only the bitmap.\n\n" + "@param menuTarget Menu to affect\n" + "@param bitmapindex Bitmap index to set for the menu\n" + "@param bitmaponly If true, only the bitmap will be rendered\n" + "@param drawborder If true, a border will be drawn around the menu.\n" + "@tsexample\n" + "// Define the menuTarget to affect\n" + "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n" + "// Set the bitmap index\n" + "%bitmapIndex = \"5\";\n\n" + "// Set if we are only to render the bitmap or not\n" + "%bitmaponly = \"true\";\n\n" + "// Set if we are rendering a border or not\n" + "%drawborder = \"true\";\n\n" + "// Inform the GuiMenuBar of the bitmap and rendering changes\n" + "%thisGuiMenuBar.setMenuBitmapIndex(%menuTarget,%bitmapIndex,%bitmapOnly,%drawBorder);\n" + "@endtsexample\n\n" + "@see GuiTickCtrl") +{ + GuiMenuBar::Menu *menu = object->findMenu(menuTarget); + if(!menu) + { + Con::errorf("Cannot find menu %s for setMenuBitmapIndex.", menuTarget); + return; + } + + menu->bitmapIndex = bitmapindex; + menu->drawBitmapOnly = bitmaponly; + menu->drawBorder = drawborder; + + object->menuBarDirty = true; +} + +DefineEngineMethod(GuiMenuBar, setMenuVisible, void, (const char* menuTarget, bool visible),, + "@brief Sets the whether or not to display the specified menu.\n\n" + "@param menuTarget Menu item to affect\n" + "@param visible Whether the menu item will be visible or not\n" + "@tsexample\n" + "// Define the menu to work with\n" + "%menuTarget = \"New Menu\"; or %menuTarget = \"4\";\n\n" + "// Define if the menu should be visible or not\n" + "%visible = \"true\";\n\n" + "// Inform the GuiMenuBar control of the new visibility state for the defined menu\n" + "%thisGuiMenuBar.setMenuVisible(%menuTarget,%visible);\n" + "@endtsexample\n\n" + "@see GuiTickCtrl") +{ + GuiMenuBar::Menu *menu = object->findMenu(menuTarget); + if(!menu) + { + Con::errorf("Cannot find menu %s for setMenuVisible.", menuTarget); + return; + } + menu->visible = visible; + object->menuBarDirty = true; + object->setUpdate(); +} + +DefineEngineMethod(GuiMenuBar, setMenuItemText, void, (const char* menuTarget, const char* menuItemTarget, const char* newMenuItemText),, + "@brief Sets the text of the specified menu item to the new string.\n\n" + "@param menuTarget Menu to affect\n" + "@param menuItem Menu item in the menu to change the text at\n" + "@param newMenuItemText New menu text\n" + "@tsexample\n" + "// Define the menuTarget\n" + "%menuTarget = \"New Menu\"; or %menuTarget = \"4\";\n\n" + "// Define the menuItem\n" + "%menuItem = \"New Menu Item\"; or %menuItem = \"2\";\n\n" + "// Define the new text for the menu item\n" + "%newMenuItemText = \"Very New Menu Item\";\n\n" + "// Inform the GuiMenuBar control to change the defined menu item with the new text\n" + "%thisGuiMenuBar.setMenuItemText(%menuTarget,%menuItem,%newMenuItemText);\n" + "@endtsexample\n\n" + "@see GuiTickCtrl") +{ + if(dIsdigit(newMenuItemText[0])) + { + Con::errorf("Cannot name menu item %s to %s. First character of a menu item's text cannot be a digit.", menuItemTarget, newMenuItemText); + return; + } + GuiMenuBar::Menu *menu = object->findMenu(menuTarget); + if(!menu) + { + Con::errorf("Cannot find menu %s for setMenuItemText.", menuTarget); + return; + } + GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, menuItemTarget); + if(!menuItem) + { + Con::errorf("Cannot find menu item %s for setMenuItemText.", menuItemTarget); + return; + } + dFree(menuItem->text); + menuItem->text = dStrdup(newMenuItemText); +} + +DefineEngineMethod(GuiMenuBar, setMenuItemVisible, void, (const char* menuTarget, const char* menuItemTarget, bool isVisible),, + "@brief Brief Description.\n\n" + "Detailed description\n\n" + "@param menuTarget Menu to affect the menu item in\n" + "@param menuItem Menu item to affect\n" + "@param isVisible Visible state to set the menu item to.\n" + "@tsexample\n" + "// Define the menuTarget\n" + "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n" + "// Define the menuItem\n" + "%menuItem = \"New Menu Item\"; or %menuItem = \"2\";\n\n" + "// Define the visibility state\n" + "%isVisible = \"true\";\n\n" + "// Inform the GuiMenuBarControl of the visibility state of the defined menu item\n" + "%thisGuiMenuBar.setMenuItemVisible(%menuTarget,%menuItem,%isVisible);\n" + "@endtsexample\n\n" + "@see GuiTickCtrl") +{ + GuiMenuBar::Menu *menu = object->findMenu(menuTarget); + if(!menu) + { + Con::errorf("Cannot find menu %s for setMenuItemVisible.", menuTarget); + return; + } + GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, menuItemTarget); + if(!menuItem) + { + Con::errorf("Cannot find menu item %s for setMenuItemVisible.", menuItemTarget); + return; + } + menuItem->visible = isVisible; +} + +DefineEngineMethod(GuiMenuBar, setMenuItemBitmap, void, (const char* menuTarget, const char* menuItemTarget, int bitmapIndex),, + "@brief Sets the specified menu item bitmap index in the bitmap array. Setting the item's index to -1 will remove any bitmap.\n\n" + "@param menuTarget Menu to affect the menuItem in\n" + "@param menuItem Menu item to affect\n" + "@param bitmapIndex Bitmap index to set the menu item to\n" + "@tsexample\n" + "// Define the menuTarget\n" + "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n" + "// Define the menuItem\"\n" + "%menuItem = \"New Menu Item\"; or %menuItem = \"2\";\n\n" + "// Define the bitmapIndex\n" + "%bitmapIndex = \"6\";\n\n" + "// Inform the GuiMenuBar control to set the menu item to the defined bitmap\n" + "%thisGuiMenuBar.setMenuItemBitmap(%menuTarget,%menuItem,%bitmapIndex);\n" + "@endtsexample\n\n" + "@see GuiTickCtrl") +{ + GuiMenuBar::Menu *menu = object->findMenu(menuTarget); + if(!menu) + { + Con::errorf("Cannot find menu %s for setMenuItemBitmap.", menuTarget); + return; + } + GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, menuItemTarget); + if(!menuItem) + { + Con::errorf("Cannot find menu item %s for setMenuItemBitmap.", menuItemTarget); + return; + } + menuItem->bitmapIndex = bitmapIndex; +} + +DefineEngineMethod(GuiMenuBar, removeMenuItem, void, (const char* menuTarget, const char* menuItemTarget),, + "@brief Removes the specified menu item from the menu.\n\n" + "@param menuTarget Menu to affect the menu item in\n" + "@param menuItem Menu item to affect\n" + "@tsexample\n" + "// Define the menuTarget\n" + "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n" + "// Define the menuItem\n" + "%menuItem = \"New Menu Item\"; or %menuItem = \"5\";\n\n" + "// Request the GuiMenuBar control to remove the define menu item\n" + "%thisGuiMenuBar.removeMenuItem(%menuTarget,%menuItem);\n\n" + "@endtsexample\n\n" + "@see GuiTickCtrl") +{ + GuiMenuBar::Menu *menu = object->findMenu(menuTarget); + if(!menu) + { + Con::errorf("Cannot find menu %s for removeMenuItem.", menuTarget); + return; + } + GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, menuItemTarget); + if(!menuItem) + { + Con::errorf("Cannot find menu item %s for removeMenuItem.", menuItemTarget); + return; + } + object->removeMenuItem(menu, menuItem); +} + +DefineEngineMethod(GuiMenuBar, clearMenuItems, void, (const char* menuTarget),, + "@brief Removes all the menu items from the specified menu.\n\n" + "@param menuTarget Menu to remove all items from\n" + "@tsexample\n" + "// Define the menuTarget\n" + "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n" + "// Inform the GuiMenuBar control to clear all menu items from the defined menu\n" + "%thisGuiMenuBar.clearMenuItems(%menuTarget);\n" + "@endtsexample\n\n" + "@see GuiTickCtrl") +{ + GuiMenuBar::Menu *menu = object->findMenu(menuTarget); + if(!menu) + { + //Con::errorf("Cannot find menu %s for clearMenuItems.", menuTarget); + return; + } + object->clearMenuItems(menu); +} + +DefineEngineMethod( GuiMenuBar, removeMenu, void, (const char* menuTarget),, + "@brief Removes the specified menu from the menu bar.\n\n" + "@param menuTarget Menu to remove from the menu bar\n" + "@tsexample\n" + "// Define the menuTarget\n" + "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n" + "// Inform the GuiMenuBar to remove the defined menu from the menu bar\n" + "%thisGuiMenuBar.removeMenu(%menuTarget);\n" + "@endtsexample\n\n" + "@see GuiTickCtrl") +{ + GuiMenuBar::Menu *menu = object->findMenu(menuTarget); + if(!menu) + { + //Con::errorf("Cannot find menu %s for removeMenu.", menuTarget); + return; + } + object->clearMenuItems(menu); + object->menuBarDirty = true; +} + +//------------------------------------------------------------------------------ +// Submenu console methods +//------------------------------------------------------------------------------ + +DefineEngineMethod(GuiMenuBar, setMenuItemSubmenuState, void, (const char* menuTarget, const char* menuItem, bool isSubmenu),, + "@brief Sets the given menu item to be a submenu.\n\n" + "@param menuTarget Menu to affect a submenu in\n" + "@param menuItem Menu item to affect\n" + "@param isSubmenu Whether or not the menuItem will become a subMenu or not\n" + "@tsexample\n" + "// Define the menuTarget\n" + "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n" + "// Define the menuItem\n" + "%menuItem = \"New Menu Item\"; or %menuItem = \"5\";\n\n" + "// Define whether or not the Menu Item is a sub menu or not\n" + "%isSubmenu = \"true\";\n\n" + "// Inform the GuiMenuBar control to set the defined menu item to be a submenu or not.\n" + "%thisGuiMenuBar.setMenuItemSubmenuState(%menuTarget,%menuItem,%isSubmenu);\n" + "@endtsexample\n\n" + "@see GuiTickCtrl") +{ + GuiMenuBar::Menu *menu = object->findMenu(menuTarget); + if(!menu) + { + Con::errorf("Cannot find menu %s for setMenuItemSubmenuState.", menuTarget); + return; + } + + GuiMenuBar::MenuItem *menuitem = object->findMenuItem(menu, menuItem); + if(!menuitem) + { + Con::errorf("Cannot find menuitem %s for setMenuItemSubmenuState.", menuItem); + return; + } + + menuitem->isSubmenu = isSubmenu; +} + +DefineEngineMethod(GuiMenuBar, addSubmenuItem, void, (const char* menuTarget, const char* menuItem, const char* submenuItemText, + int submenuItemId, const char* accelerator, int checkGroup),, + "@brief Adds a menu item to the specified menu. The menu argument can be either the text of a menu or its id.\n\n" + "@param menuTarget Menu to affect a submenu in\n" + "@param menuItem Menu item to affect\n" + "@param submenuItemText Text to show for the new submenu\n" + "@param submenuItemId Id for the new submenu\n" + "@param accelerator Accelerator key for the new submenu\n" + "@param checkGroup Which check group the new submenu should be in, or -1 for none.\n" + "@tsexample\n" + "// Define the menuTarget\n" + "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n" + "// Define the menuItem\n" + "%menuItem = \"New Menu Item\"; or %menuItem = \"5\";\n\n" + "// Define the text for the new submenu\n" + "%submenuItemText = \"New Submenu Item\";\n\n" + "// Define the id for the new submenu\n" + "%submenuItemId = \"4\";\n\n" + "// Define the accelerator key for the new submenu\n" + "%accelerator = \"n\";\n\n" + "// Define the checkgroup for the new submenu\n" + "%checkgroup = \"7\";\n\n" + "// Request the GuiMenuBar control to add the new submenu with the defined information\n" + "%thisGuiMenuBar.addSubmenuItem(%menuTarget,%menuItem,%submenuItemText,%submenuItemId,%accelerator,%checkgroup);\n" + "@endtsexample\n\n" + "@see GuiTickCtrl\n") +{ + if(dIsdigit(submenuItemText[0])) + { + Con::errorf("Cannot add submenu item %s (id = %s). First character of a menu item's text cannot be a digit.", submenuItemText, submenuItemId); + return; + } + + GuiMenuBar::Menu *menu = object->findMenu(menuTarget); + if(!menu) + { + Con::errorf("Cannot find menu %s for addMenuItem.", menuTarget); + return; + } + + GuiMenuBar::MenuItem *menuitem = object->findMenuItem(menu, menuItem); + if(!menuitem) + { + Con::errorf("Cannot find menuitem %s for addSubmenuItem.", menuItem); + return; + } + + object->addSubmenuItem(menu, menuitem, submenuItemText, submenuItemId, !accelerator ? "" : accelerator, checkGroup == -1 ? -1 : checkGroup); +} + +DefineEngineMethod(GuiMenuBar, clearSubmenuItems, void, (const char* menuTarget, const char* menuItem),, + "@brief Removes all the menu items from the specified submenu.\n\n" + "@param menuTarget Menu to affect a submenu in\n" + "@param menuItem Menu item to affect\n" + "@tsexample\n" + "// Define the menuTarget\n" + "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n" + "// Define the menuItem\n" + "%menuItem = \"New Menu Item\"; or %menuItem = \"5\";\n\n" + "// Inform the GuiMenuBar to remove all submenu items from the defined menu item\n" + "%thisGuiMenuBar.clearSubmenuItems(%menuTarget,%menuItem);\n\n" + "@endtsexample\n\n" + "@see GuiControl") +{ + GuiMenuBar::Menu *menu = object->findMenu(menuTarget); + if(!menu) + { + Con::errorf("Cannot find menu %s for clearSubmenuItems.", menuTarget); + return; + } + + GuiMenuBar::MenuItem *menuitem = object->findMenuItem(menu, menuItem); + if(!menuitem) + { + Con::errorf("Cannot find menuitem %s for clearSubmenuItems.", menuItem); + return; + } + + object->clearSubmenuItems(menuitem); +} + +DefineEngineMethod(GuiMenuBar, setSubmenuItemChecked, void, (const char* menuTarget, const char* menuItemTarget, const char* submenuItemText, bool checked),, + "@brief Sets the menu item bitmap to a check mark, which by default is the first element in the\n" + "bitmap array (although this may be changed with setCheckmarkBitmapIndex()).\n" + "Any other menu items in the menu with the same check group become unchecked if they are checked.\n\n" + "@param menuTarget Menu to affect a submenu in\n" + "@param menuItem Menu item to affect\n" + "@param submenuItemText Text to show for submenu\n" + "@param checked Whether or not this submenu item will be checked.\n" + "@tsexample\n" + "// Define the menuTarget\n" + "%menuTarget = \"New Menu\"; or %menuTarget = \"3\";\n\n" + "// Define the menuItem\n" + "%menuItem = \"New Menu Item\"; or %menuItem = \"5\";\n\n" + "// Define the text for the new submenu\n" + "%submenuItemText = \"Submenu Item\";\n\n" + "// Define if this submenu item should be checked or not\n" + "%checked = \"true\";\n\n" + "// Inform the GuiMenuBar control to set the checked state of the defined submenu item\n" + "%thisGuiMenuBar.setSubmenuItemChecked(%menuTarget,%menuItem,%submenuItemText,%checked);\n" + "@endtsexample\n\n" + "@return If not void, return value and description\n\n" + "@see References") +{ + // Find the parent menu + GuiMenuBar::Menu *menu = object->findMenu(menuTarget); + if(!menu) + { + Con::errorf("Cannot find menu %s for setSubmenuItemChecked.", menuTarget); + return; + } + + // Find the parent menu item + GuiMenuBar::MenuItem *menuItem = object->findMenuItem(menu, menuItemTarget); + if(!menuItem) + { + Con::errorf("Cannot find menu item %s for setSubmenuItemChecked.", menuItemTarget); + return; + } + + // Find the submenu item + GuiMenuBar::MenuItem *submenuItem = object->findSubmenuItem(menu, menuItemTarget, submenuItemText); + if(!submenuItem) + { + Con::errorf("Cannot find submenu item %s for setSubmenuItemChecked.", submenuItemText); + return; + } + + if(checked && submenuItem->checkGroup != -1) + { + // first, uncheck everything in the group: + for(GuiMenuBar::MenuItem *itemWalk = menuItem->firstSubmenuItem; itemWalk; itemWalk = itemWalk->nextMenuItem) + if(itemWalk->checkGroup == submenuItem->checkGroup && itemWalk->bitmapIndex == object->mCheckmarkBitmapIndex) + itemWalk->bitmapIndex = -1; + } + submenuItem->bitmapIndex = checked ? object->mCheckmarkBitmapIndex : -1; +} + +//------------------------------------------------------------------------------ +// menu management methods +//------------------------------------------------------------------------------ + +void GuiMenuBar::addMenu(const char *menuText, U32 menuId) +{ + // allocate the menu + Menu *newMenu = new Menu; + newMenu->text = dStrdup(menuText); + newMenu->id = menuId; + newMenu->nextMenu = NULL; + newMenu->firstMenuItem = NULL; + newMenu->visible = true; + + // Menu bitmap variables + newMenu->bitmapIndex = -1; + newMenu->drawBitmapOnly = false; + newMenu->drawBorder = true; + + // add it to the menu list + menuBarDirty = true; + Menu **walk; + for(walk = &menuList; *walk; walk = &(*walk)->nextMenu) + ; + *walk = newMenu; +} + +GuiMenuBar::Menu *GuiMenuBar::findMenu(const char *menu) +{ + if(dIsdigit(menu[0])) + { + U32 id = dAtoi(menu); + for(Menu *walk = menuList; walk; walk = walk->nextMenu) + if(id == walk->id) + return walk; + return NULL; + } + else + { + for(Menu *walk = menuList; walk; walk = walk->nextMenu) + if(!dStricmp(menu, walk->text)) + return walk; + return NULL; + } +} + +GuiMenuBar::MenuItem *GuiMenuBar::findMenuItem(Menu *menu, const char *menuItem) +{ + if(dIsdigit(menuItem[0])) + { + U32 id = dAtoi(menuItem); + for(MenuItem *walk = menu->firstMenuItem; walk; walk = walk->nextMenuItem) + if(id == walk->id) + return walk; + return NULL; + } + else + { + for(MenuItem *walk = menu->firstMenuItem; walk; walk = walk->nextMenuItem) + if(!dStricmp(menuItem, walk->text)) + return walk; + return NULL; + } +} + +void GuiMenuBar::removeMenu(Menu *menu) +{ + menuBarDirty = true; + clearMenuItems(menu); + for(Menu **walk = &menuList; *walk; walk = &(*walk)->nextMenu) + { + if(*walk == menu) + { + *walk = menu->nextMenu; + break; + } + } + dFree(menu->text); + delete menu; +} + +void GuiMenuBar::removeMenuItem(Menu *menu, MenuItem *menuItem) +{ + for(MenuItem **walk = &menu->firstMenuItem; *walk; walk = &(*walk)->nextMenuItem) + { + if(*walk == menuItem) + { + *walk = menuItem->nextMenuItem; + break; + } + } + + // If this is a submenu, then be sure to clear the submenu's items + if(menuItem->isSubmenu) + { + clearSubmenuItems(menuItem); + } + + dFree(menuItem->text); + dFree(menuItem->accelerator); + delete menuItem; +} + +void GuiMenuBar::addMenuItem(Menu *menu, const char *text, U32 id, const char *accelerator, S32 checkGroup) +{ + // allocate the new menu item + MenuItem *newMenuItem = new MenuItem; + newMenuItem->text = dStrdup(text); + if(accelerator[0]) + newMenuItem->accelerator = dStrdup(accelerator); + else + newMenuItem->accelerator = NULL; + newMenuItem->id = id; + newMenuItem->checkGroup = checkGroup; + newMenuItem->nextMenuItem = NULL; + newMenuItem->acceleratorIndex = 0; + newMenuItem->enabled = text[0] != '-'; + newMenuItem->visible = true; + newMenuItem->bitmapIndex = -1; + + // Default to not having a submenu + newMenuItem->isSubmenu = false; + newMenuItem->firstSubmenuItem = NULL; + newMenuItem->submenuParentMenu = NULL; + + // link it into the menu's menu item list + MenuItem **walk = &menu->firstMenuItem; + while(*walk) + walk = &(*walk)->nextMenuItem; + *walk = newMenuItem; + +} + +void GuiMenuBar::clearMenuItems(Menu *menu) +{ + while(menu->firstMenuItem) + removeMenuItem(menu, menu->firstMenuItem); +} + +void GuiMenuBar::clearMenus() +{ + while(menuList) + removeMenu(menuList); +} + +//------------------------------------------------------------------------------ +// Submenu methods +//------------------------------------------------------------------------------ + +// This method will return the MenuItem class of of a submenu's menu item given +// its parent menu and parent menuitem. If the menuitem ID is used, then the submenu +// ID must also be used. +GuiMenuBar::MenuItem *GuiMenuBar::findSubmenuItem(Menu *menu, const char *menuItem, const char *submenuItem) +{ + if(dIsdigit(menuItem[0])) + { + // Search by ID + U32 id = dAtoi(menuItem); + for(MenuItem *walk = menu->firstMenuItem; walk; walk = walk->nextMenuItem) + if(id == walk->id) + { + if(walk->isSubmenu) + { + U32 subid = dAtoi(submenuItem); + for(MenuItem *subwalk = walk->firstSubmenuItem; subwalk; subwalk = subwalk->nextMenuItem) + { + if(subid == walk->id) + { + return subwalk; + } + } + } + return NULL; + } + return NULL; + } + else + { + // Search by name + for(MenuItem *walk = menu->firstMenuItem; walk; walk = walk->nextMenuItem) + if(!dStricmp(menuItem, walk->text)) + { + if(walk->isSubmenu) + { + for(MenuItem *subwalk = walk->firstSubmenuItem; subwalk; subwalk = subwalk->nextMenuItem) + { + if(!dStricmp(submenuItem, subwalk->text)) + return subwalk; + } + } + return NULL; + } + return NULL; + } +} + +// Add a menuitem to the given submenu +void GuiMenuBar::addSubmenuItem(Menu *menu, MenuItem *submenu, const char *text, U32 id, const char *accelerator, S32 checkGroup) +{ + // Check that the given menu item supports a submenu + if(submenu && !submenu->isSubmenu) + { + Con::errorf("GuiMenuBar::addSubmenuItem: Attempting to add menuitem '%s' to an invalid submenu",text); + return; + } + + // allocate the new menu item + MenuItem *newMenuItem = new MenuItem; + newMenuItem->text = dStrdup(text); + if(accelerator[0]) + newMenuItem->accelerator = dStrdup(accelerator); + else + newMenuItem->accelerator = NULL; + newMenuItem->id = id; + newMenuItem->checkGroup = checkGroup; + newMenuItem->nextMenuItem = NULL; + newMenuItem->acceleratorIndex = 0; + newMenuItem->enabled = text[0] != '-'; + newMenuItem->visible = true; + newMenuItem->bitmapIndex = -1; + + // Default to not having a submenu + newMenuItem->isSubmenu = false; + newMenuItem->firstSubmenuItem = NULL; + + // Point back to the submenu's menu + newMenuItem->submenuParentMenu = menu; + + // link it into the menu's menu item list + MenuItem **walk = &submenu->firstSubmenuItem; + while(*walk) + walk = &(*walk)->nextMenuItem; + *walk = newMenuItem; + +} + +// Remove a submenu item +void GuiMenuBar::removeSubmenuItem(MenuItem *menuItem, MenuItem *submenuItem) +{ + // Check that the given menu item supports a submenu + if(menuItem && !menuItem->isSubmenu) + { + Con::errorf("GuiMenuBar::removeSubmenuItem: Attempting to remove submenuitem '%s' from an invalid submenu",submenuItem->text); + return; + } + + for(MenuItem **subwalk = &menuItem->firstSubmenuItem; *subwalk; subwalk = &(*subwalk)->nextMenuItem) + { + if(*subwalk == submenuItem) + { + *subwalk = submenuItem->nextMenuItem; + break; + } + } + dFree(submenuItem->text); + dFree(submenuItem->accelerator); + delete submenuItem; +} + +// Clear all menuitems from a submenu +void GuiMenuBar::clearSubmenuItems(MenuItem *menuitem) +{ + // Check that the given menu item supports a submenu + if(menuitem && !menuitem->isSubmenu) + { + Con::errorf("GuiMenuBar::clearSubmenuItems: Attempting to clear an invalid submenu"); + return; + } + + while(menuitem->firstSubmenuItem) + removeSubmenuItem(menuitem, menuitem->firstSubmenuItem); +} + +//------------------------------------------------------------------------------ +// initialization, input and render methods +//------------------------------------------------------------------------------ + +GuiMenuBar::GuiMenuBar() +{ + menuList = NULL; + menuBarDirty = true; + mouseDownMenu = NULL; + mouseOverMenu = NULL; + mCurAcceleratorIndex = 0; + mBackground = NULL; + mPadding = 0; + + mCheckmarkBitmapIndex = 0; // Default to the first image in the bitmap array for the check mark + + mHorizontalMargin = 6; // Default number of pixels on the left and right side of a manu's text + mVerticalMargin = 1; // Default number of pixels on the top and bottom of a menu's text + mBitmapMargin = 2; // Default number of pixels between a menu's bitmap and text + + // Added: + mouseDownSubmenu = NULL; + mouseOverSubmenu = NULL; + mSubmenuBackground = NULL; + mSubmenuTextList = NULL; + mMouseOverCounter = 0; + mCountMouseOver = false; + mMouseHoverAmount = 30; + setProcessTicks(false); +} + +void GuiMenuBar::initPersistFields() +{ + addField("padding", TypeS32, Offset( mPadding, GuiMenuBar ),"Extra padding to add to the bounds of the control.\n"); + + Parent::initPersistFields(); +} + +bool GuiMenuBar::onWake() +{ + if(!Parent::onWake()) + return false; + mProfile->constructBitmapArray(); // if a bitmap was specified... + maxBitmapSize.set(0,0); + S32 numBitmaps = mProfile->mBitmapArrayRects.size(); + if(numBitmaps) + { + RectI *bitmapBounds = mProfile->mBitmapArrayRects.address(); + for(S32 i = 0; i < numBitmaps; i++) + { + if(bitmapBounds[i].extent.x > maxBitmapSize.x) + maxBitmapSize.x = bitmapBounds[i].extent.x; + if(bitmapBounds[i].extent.y > maxBitmapSize.y) + maxBitmapSize.y = bitmapBounds[i].extent.y; + } + } + return true; +} + +GuiMenuBar::Menu *GuiMenuBar::findHitMenu(Point2I mousePoint) +{ + Point2I pos = globalToLocalCoord(mousePoint); + + for(Menu *walk = menuList; walk; walk = walk->nextMenu) + if(walk->visible && walk->bounds.pointInRect(pos)) + return walk; + return NULL; +} + +void GuiMenuBar::onPreRender() +{ + Parent::onPreRender(); + if(menuBarDirty) + { + menuBarDirty = false; + U32 curX = mPadding; + for(Menu *walk = menuList; walk; walk = walk->nextMenu) + { + if(!walk->visible) + continue; + + // Bounds depends on if there is a bitmap to be drawn or not + if(walk->bitmapIndex == -1) + { + // Text only + walk->bounds.set(curX, 0, mProfile->mFont->getStrWidth(walk->text) + (mHorizontalMargin * 2), getHeight() - (mVerticalMargin * 2)); + + } else + { + // Will the bitmap and text be draw? + if(!walk->drawBitmapOnly) + { + // Draw the bitmap and the text + RectI *bitmapBounds = mProfile->mBitmapArrayRects.address(); + walk->bounds.set(curX, 0, bitmapBounds[walk->bitmapIndex].extent.x + mProfile->mFont->getStrWidth(walk->text) + (mHorizontalMargin * 2), getHeight() + (mVerticalMargin * 2)); + + } else + { + // Only the bitmap will be drawn + RectI *bitmapBounds = mProfile->mBitmapArrayRects.address(); + walk->bounds.set(curX, 0, bitmapBounds[walk->bitmapIndex].extent.x + mBitmapMargin + (mHorizontalMargin * 2), getHeight() + (mVerticalMargin * 2)); + } + } + + curX += walk->bounds.extent.x; + } + mouseOverMenu = NULL; + mouseDownMenu = NULL; + } +} + +void GuiMenuBar::checkMenuMouseMove(const GuiEvent &event) +{ + Menu *hit = findHitMenu(event.mousePoint); + if(hit && hit != mouseDownMenu) + { + // gotta close out the current menu... + mTextList->setSelectedCell(Point2I(-1, -1)); + closeMenu(); + mouseOverMenu = mouseDownMenu = hit; + setUpdate(); + onAction(); + } +} + +void GuiMenuBar::onMouseMove(const GuiEvent &event) +{ + Menu *hit = findHitMenu(event.mousePoint); + if(hit != mouseOverMenu) + { + // If we need to, reset the mouse over menu counter and indicate + // that we should track it. + if(hit) + mMouseOverCounter = 0; + if(!mCountMouseOver) + { + // We've never started the counter, so start it. + if(hit) + mCountMouseOver = true; + } + + mouseOverMenu = hit; + setUpdate(); + } +} + +void GuiMenuBar::onMouseLeave(const GuiEvent &event) +{ + if(mouseOverMenu) + setUpdate(); + mouseOverMenu = NULL; + + // As we've left the control, don't track how long the mouse has been + // within it. + if(mCountMouseOver && mMouseOverCounter >= mMouseHoverAmount) + { + onMouseInMenu_callback(false); // Last parameter indicates if we've entered or left the menu + } + mCountMouseOver = false; + mMouseOverCounter = 0; +} + +void GuiMenuBar::onMouseDragged(const GuiEvent &event) +{ + Menu *hit = findHitMenu(event.mousePoint); + + if(hit != mouseOverMenu) + { + // If we need to, reset the mouse over menu counter and indicate + // that we should track it. + if(hit) + mMouseOverCounter = 0; + if(!mCountMouseOver) + { + // We've never started the counter, so start it. + if(hit) + mCountMouseOver = true; + } + + mouseOverMenu = hit; + mouseDownMenu = hit; + setUpdate(); + onAction(); + } +} + +void GuiMenuBar::onMouseDown(const GuiEvent &event) +{ + mouseDownMenu = mouseOverMenu = findHitMenu(event.mousePoint); + setUpdate(); + onAction(); +} + +void GuiMenuBar::onMouseUp(const GuiEvent &event) +{ + mouseDownMenu = NULL; + setUpdate(); +} + +void GuiMenuBar::onRender(Point2I offset, const RectI &updateRect) +{ + + RectI ctrlRect(offset, getExtent()); + + //if opaque, fill the update rect with the fill color + if (mProfile->mOpaque) + GFX->getDrawUtil()->drawRectFill(RectI(offset, getExtent()), mProfile->mFillColor); + + //if there's a border, draw the border + if (mProfile->mBorder) + renderBorder(ctrlRect, mProfile); + + for(Menu *walk = menuList; walk; walk = walk->nextMenu) + { + if(!walk->visible) + continue; + ColorI fontColor = mProfile->mFontColor; + RectI bounds = walk->bounds; + bounds.point += offset; + + Point2I start; + + start.x = walk->bounds.point.x + mHorizontalMargin; + start.y = walk->bounds.point.y + ( walk->bounds.extent.y - mProfile->mFont->getHeight() ) / 2; + + // Draw the border + if(walk->drawBorder) + { + RectI highlightBounds = bounds; + highlightBounds.inset(1,1); + if(walk == mouseDownMenu) + renderFilledBorder(highlightBounds, mProfile->mBorderColorHL, mProfile->mFillColorHL ); + else if(walk == mouseOverMenu && mouseDownMenu == NULL) + renderFilledBorder(highlightBounds, mProfile->mBorderColor, mProfile->mFillColor ); + } + + // Do we draw a bitmap? + if(walk->bitmapIndex != -1) + { + S32 index = walk->bitmapIndex * 3; + if(walk == mouseDownMenu) + ++index; + else if(walk == mouseOverMenu && mouseDownMenu == NULL) + index += 2; + + RectI rect = mProfile->mBitmapArrayRects[index]; + + Point2I bitmapstart(start); + bitmapstart.y = walk->bounds.point.y + ( walk->bounds.extent.y - rect.extent.y ) / 2; + + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapSR( mProfile->mTextureObject, offset + bitmapstart, rect); + + // Should we also draw the text? + if(!walk->drawBitmapOnly) + { + start.x += mBitmapMargin; + GFX->getDrawUtil()->setBitmapModulation( fontColor ); + GFX->getDrawUtil()->drawText( mProfile->mFont, start + offset, walk->text, mProfile->mFontColors ); + } + } else + { + GFX->getDrawUtil()->setBitmapModulation( fontColor ); + GFX->getDrawUtil()->drawText( mProfile->mFont, start + offset, walk->text, mProfile->mFontColors ); + } + } + + renderChildControls( offset, updateRect ); +} + +void GuiMenuBar::buildAcceleratorMap() +{ + Parent::buildAcceleratorMap(); + // ok, accelerator map is cleared... + // add all our keys: + mCurAcceleratorIndex = 1; + + for(Menu *menu = menuList; menu; menu = menu->nextMenu) + { + for(MenuItem *item = menu->firstMenuItem; item; item = item->nextMenuItem) + { + if(!item->accelerator) + { + item->accelerator = 0; + continue; + } + EventDescriptor accelEvent; + ActionMap::createEventDescriptor(item->accelerator, &accelEvent); + + //now we have a modifier, and a key, add them to the canvas + GuiCanvas *root = getRoot(); + if (root) + root->addAcceleratorKey(this, mCurAcceleratorIndex, accelEvent.eventCode, accelEvent.flags); + item->acceleratorIndex = mCurAcceleratorIndex; + mCurAcceleratorIndex++; + } + } +} + +void GuiMenuBar::acceleratorKeyPress(U32 index) +{ + // loop through all the menus + // and find the item that corresponds to the accelerator index + for(Menu *menu = menuList; menu; menu = menu->nextMenu) + { + if(!menu->visible) + continue; + + for(MenuItem *item = menu->firstMenuItem; item; item = item->nextMenuItem) + { + if(item->acceleratorIndex == index) + { + // first, call the script callback for menu selection: + onMenuSelect_callback(Con::getIntArg(menu->id), menu->text); + + if(item->visible) + menuItemSelected(menu, item); + return; + } + } + } +} + +//------------------------------------------------------------------------------ +// Menu display class methods +//------------------------------------------------------------------------------ + +GuiMenuBackgroundCtrl::GuiMenuBackgroundCtrl(GuiMenuBar *ctrl, GuiMenuTextListCtrl *textList) +{ + mMenuBarCtrl = ctrl; + mTextList = textList; +} + +void GuiMenuBackgroundCtrl::onMouseDown(const GuiEvent &event) +{ + mTextList->setSelectedCell(Point2I(-1,-1)); + mMenuBarCtrl->closeMenu(); +} + +void GuiMenuBackgroundCtrl::onMouseMove(const GuiEvent &event) +{ + GuiCanvas *root = getRoot(); + GuiControl *ctrlHit = root->findHitControl(event.mousePoint, mLayer - 1); + if(ctrlHit == mMenuBarCtrl) // see if the current mouse over menu is right... + mMenuBarCtrl->checkMenuMouseMove(event); +} + +void GuiMenuBackgroundCtrl::onMouseDragged(const GuiEvent &event) +{ + GuiCanvas *root = getRoot(); + GuiControl *ctrlHit = root->findHitControl(event.mousePoint, mLayer - 1); + if(ctrlHit == mMenuBarCtrl) // see if the current mouse over menu is right... + mMenuBarCtrl->checkMenuMouseMove(event); +} + +GuiMenuTextListCtrl::GuiMenuTextListCtrl(GuiMenuBar *ctrl) +{ + mMenuBarCtrl = ctrl; + isSubMenu = false; // Added +} + +void GuiMenuTextListCtrl::onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver) +{ + if(dStrcmp(mList[cell.y].text + 3, "-\t")) // Was: dStrcmp(mList[cell.y].text + 2, "-\t")) but has been changed to take into account the submenu flag + Parent::onRenderCell(offset, cell, selected, mouseOver); + else + { + S32 yp = offset.y + mCellSize.y / 2; + GFX->getDrawUtil()->drawLine(offset.x, yp, offset.x + mCellSize.x, yp, ColorI(128,128,128)); + GFX->getDrawUtil()->drawLine(offset.x, yp+1, offset.x + mCellSize.x, yp+1, ColorI(255,255,255)); + } + // now see if there's a bitmap... + U8 idx = mList[cell.y].text[0]; + if(idx != 1) + { + // there's a bitmap... + U32 index = U32(idx - 2) * 3; + if(!mList[cell.y].active) + index += 2; + else if(selected || mouseOver) + index ++; + + RectI rect = mProfile->mBitmapArrayRects[index]; + Point2I off = mMenuBarCtrl->maxBitmapSize - rect.extent; + off /= 2; + + GFX->getDrawUtil()->clearBitmapModulation(); + GFX->getDrawUtil()->drawBitmapSR(mProfile->mTextureObject, offset + off, rect); + } + + // Check if this is a submenu + idx = mList[cell.y].text[1]; + if(idx != 1) + { + // This is a submenu, so draw an arrow + S32 left = offset.x + mCellSize.x - 12; + S32 right = left + 8; + S32 top = mCellSize.y / 2 + offset.y - 4; + S32 bottom = top + 8; + S32 middle = top + 4; + + PrimBuild::begin( GFXTriangleList, 3 ); + if( selected || mouseOver ) + PrimBuild::color( mProfile->mFontColorHL ); + else + PrimBuild::color( mProfile->mFontColor ); + + PrimBuild::vertex2i( left, top ); + PrimBuild::vertex2i( right, middle ); + PrimBuild::vertex2i( left, bottom ); + PrimBuild::end(); + } +} + +bool GuiMenuTextListCtrl::onKeyDown(const GuiEvent &event) +{ + //if the control is a dead end, don't process the input: + if ( !mVisible || !mActive || !mAwake ) + return false; + + //see if the key down is a or not + if ( event.modifier == 0 ) + { + if ( event.keyCode == KEY_RETURN ) + { + mMenuBarCtrl->closeMenu(); + return true; + } + else if ( event.keyCode == KEY_ESCAPE ) + { + mSelectedCell.set( -1, -1 ); + mMenuBarCtrl->closeMenu(); + return true; + } + } + + //otherwise, pass the event to it's parent + return Parent::onKeyDown(event); +} + +void GuiMenuTextListCtrl::onMouseDown(const GuiEvent &event) +{ + Parent::onMouseDown(event); +} + +void GuiMenuTextListCtrl::onMouseUp(const GuiEvent &event) +{ + Parent::onMouseUp(event); + mMenuBarCtrl->closeMenu(); +} + +void GuiMenuTextListCtrl::onCellHighlighted(Point2I cell) +{ + // If this text list control is part of a submenu, then don't worry about + // passing this along + if(!isSubMenu) + { + RectI globalbounds(getBounds()); + Point2I globalpoint = localToGlobalCoord(globalbounds.point); + globalbounds.point = globalpoint; + mMenuBarCtrl->highlightedMenuItem(cell.y, globalbounds, mCellSize); + } +} + +//------------------------------------------------------------------------------ +// Submenu display class methods +//------------------------------------------------------------------------------ + +GuiSubmenuBackgroundCtrl::GuiSubmenuBackgroundCtrl(GuiMenuBar *ctrl, GuiMenuTextListCtrl *textList) : GuiMenuBackgroundCtrl(ctrl, textList) +{ +} + +void GuiSubmenuBackgroundCtrl::onMouseDown(const GuiEvent &event) +{ + mTextList->setSelectedCell(Point2I(-1,-1)); + mMenuBarCtrl->closeMenu(); +} + +bool GuiSubmenuBackgroundCtrl::pointInControl(const Point2I& parentCoordPoint) +{ + S32 xt = parentCoordPoint.x - getLeft(); + S32 yt = parentCoordPoint.y - getTop(); + + if(findHitControl(Point2I(xt,yt)) == this) + return false; + else + return true; +// return xt >= 0 && yt >= 0 && xt < getWidth() && yt < getHeight(); +} + +//------------------------------------------------------------------------------ + +void GuiMenuBar::menuItemSelected(GuiMenuBar::Menu *menu, GuiMenuBar::MenuItem *item) +{ + if(item->enabled) + onMenuItemSelect_callback(Con::getIntArg(menu->id), menu->text, Con::getIntArg(item->id), item->text); +} + +void GuiMenuBar::onSleep() +{ + if(mBackground) // a menu is up? + { + mTextList->setSelectedCell(Point2I(-1, -1)); + closeMenu(); + } + Parent::onSleep(); +} + +void GuiMenuBar::closeMenu() +{ + // First close any open submenu + closeSubmenu(); + + // Get the selection from the text list: + S32 selectionIndex = mTextList->getSelectedCell().y; + + // Pop the background: + if( getRoot() ) + getRoot()->popDialogControl(mBackground); + else + return; + + // Kill the popup: + mBackground->deleteObject(); + mBackground = NULL; + + // Now perform the popup action: + if ( selectionIndex != -1 ) + { + MenuItem *list = mouseDownMenu->firstMenuItem; + + while(selectionIndex && list) + { + list = list->nextMenuItem; + selectionIndex--; + } + if(list) + menuItemSelected(mouseDownMenu, list); + } + mouseDownMenu = NULL; +} + +// Called when a menu item is highlighted by the mouse +void GuiMenuBar::highlightedMenuItem(S32 selectionIndex, RectI bounds, Point2I cellSize) +{ + S32 selstore = selectionIndex; + + // Now perform the popup action: + if ( selectionIndex != -1 ) + { + MenuItem *list = mouseDownMenu->firstMenuItem; + + while(selectionIndex && list) + { + list = list->nextMenuItem; + selectionIndex--; + } + + if(list) + { + // If the highlighted item has changed... + if(mouseOverSubmenu != list) + { + closeSubmenu(); + mouseOverSubmenu = NULL; + + // Check if this is a submenu. If so, open the submenu. + if(list->isSubmenu) + { + // If there are submenu items, then open the submenu + if(list->firstSubmenuItem) + { + mouseOverSubmenu = list; + onSubmenuAction(selstore, bounds, cellSize); + } + } + } + } + } +} + +//------------------------------------------------------------------------------ +void GuiMenuBar::onAction() +{ + if(!mouseDownMenu) + return; + + // first, call the script callback for menu selection: + onMenuSelect_callback(Con::getIntArg(mouseDownMenu->id), mouseDownMenu->text); + + MenuItem *visWalk = mouseDownMenu->firstMenuItem; + while(visWalk) + { + if(visWalk->visible) + break; + visWalk = visWalk->nextMenuItem; + } + if(!visWalk) + { + mouseDownMenu = NULL; + return; + } + + mTextList = new GuiMenuTextListCtrl(this); + mTextList->setControlProfile(mProfile); + + mBackground = new GuiMenuBackgroundCtrl(this, mTextList); + + GuiCanvas *root = getRoot(); + Point2I windowExt = root->getExtent(); + + mBackground->resize( Point2I(0,0), root->getExtent()); + S32 textWidth = 0, width = 0; + S32 acceleratorWidth = 0; + + GFont *font = mProfile->mFont; + + for(MenuItem *walk = mouseDownMenu->firstMenuItem; walk; walk = walk->nextMenuItem) + { + if(!walk->visible) + continue; + + S32 iTextWidth = font->getStrWidth(walk->text); + S32 iAcceleratorWidth = walk->accelerator ? font->getStrWidth(walk->accelerator) : 0; + + if(iTextWidth > textWidth) + textWidth = iTextWidth; + if(iAcceleratorWidth > acceleratorWidth) + acceleratorWidth = iAcceleratorWidth; + } + width = textWidth + acceleratorWidth + maxBitmapSize.x * 2 + 2 + 4; + + mTextList->setCellSize(Point2I(width, font->getHeight()+2)); + mTextList->clearColumnOffsets(); + mTextList->addColumnOffset(-1); // add an empty column in for the bitmap index. + mTextList->addColumnOffset(maxBitmapSize.x + 1); + mTextList->addColumnOffset(maxBitmapSize.x + 1 + textWidth + 4); + + U32 entryCount = 0; + + for(MenuItem *walk = mouseDownMenu->firstMenuItem; walk; walk = walk->nextMenuItem) + { + if(!walk->visible) + continue; + + char buf[512]; + + // If this menu item is a submenu, then set the isSubmenu to 2 to indicate + // an arrow should be drawn. Otherwise set the isSubmenu normally. + char isSubmenu = 1; + if(walk->isSubmenu) + isSubmenu = 2; + + char bitmapIndex = 1; + if(walk->bitmapIndex >= 0 && (walk->bitmapIndex * 3 <= mProfile->mBitmapArrayRects.size())) + bitmapIndex = walk->bitmapIndex + 2; + dSprintf(buf, sizeof(buf), "%c%c\t%s\t%s", bitmapIndex, isSubmenu, walk->text, walk->accelerator ? walk->accelerator : ""); + mTextList->addEntry(entryCount, buf); + + if(!walk->enabled) + mTextList->setEntryActive(entryCount, false); + + entryCount++; + } + Point2I menuPoint = localToGlobalCoord(mouseDownMenu->bounds.point); + menuPoint.y += mouseDownMenu->bounds.extent.y; // Used to have this at the end: + 2; + + GuiControl *ctrl = new GuiControl; + RectI ctrlBounds( menuPoint, mTextList->getExtent() + Point2I(6, 6)); + + ctrl->setControlProfile(mProfile); + mTextList->setPosition( mTextList->getPosition() + Point2I(3,3) ); + + // Make sure the menu doesn't go beyond the Canvas' bottom edge. + if((ctrlBounds.point.y + ctrlBounds.extent.y) > windowExt.y) + { + // Pop the menu above the menu bar + Point2I menuBar = localToGlobalCoord(mouseDownMenu->bounds.point); + ctrlBounds.point.y = menuBar.y - ctrl->getHeight(); + } + + ctrl->resize(ctrlBounds.point, ctrlBounds.extent); + //mTextList->setPosition(Point2I(3,3)); + + mTextList->registerObject(); + mBackground->registerObject(); + ctrl->registerObject(); + + mBackground->addObject( ctrl ); + ctrl->addObject( mTextList ); + + root->pushDialogControl(mBackground, mLayer + 1); + mTextList->setFirstResponder(); +} + +//------------------------------------------------------------------------------ +// Performs an action when a menu item that is a submenu is selected/highlighted +void GuiMenuBar::onSubmenuAction(S32 selectionIndex, RectI bounds, Point2I cellSize) +{ + if(!mouseOverSubmenu) + return; + + // first, call the script callback for menu selection: + onSubmenuSelect_callback(Con::getIntArg(mouseOverSubmenu->id), mouseOverSubmenu->text); + + MenuItem *visWalk = mouseOverSubmenu->firstSubmenuItem; + while(visWalk) + { + if(visWalk->visible) + break; + visWalk = visWalk->nextMenuItem; + } + if(!visWalk) + { + mouseOverSubmenu = NULL; + return; + } + + mSubmenuTextList = new GuiMenuTextListCtrl(this); + mSubmenuTextList->setControlProfile(mProfile); + mSubmenuTextList->isSubMenu = true; // Indicate that this text list is part of a submenu + + mSubmenuBackground = new GuiSubmenuBackgroundCtrl(this, mSubmenuTextList); + + GuiCanvas *root = getRoot(); + Point2I windowExt = root->getExtent(); + + mSubmenuBackground->resize( Point2I(0,0), root->getExtent()); + S32 textWidth = 0, width = 0; + S32 acceleratorWidth = 0; + + GFont *font = mProfile->mFont; + + for(MenuItem *walk = mouseOverSubmenu->firstSubmenuItem; walk; walk = walk->nextMenuItem) + { + if(!walk->visible) + continue; + + S32 iTextWidth = font->getStrWidth(walk->text); + S32 iAcceleratorWidth = walk->accelerator ? font->getStrWidth(walk->accelerator) : 0; + + if(iTextWidth > textWidth) + textWidth = iTextWidth; + if(iAcceleratorWidth > acceleratorWidth) + acceleratorWidth = iAcceleratorWidth; + } + width = textWidth + acceleratorWidth + maxBitmapSize.x * 2 + 2 + 4; + + mSubmenuTextList->setCellSize(Point2I(width, font->getHeight()+3)); + mSubmenuTextList->clearColumnOffsets(); + mSubmenuTextList->addColumnOffset(-1); // add an empty column in for the bitmap index. + mSubmenuTextList->addColumnOffset(maxBitmapSize.x + 1); + mSubmenuTextList->addColumnOffset(maxBitmapSize.x + 1 + textWidth + 4); + + U32 entryCount = 0; + + for(MenuItem *walk = mouseOverSubmenu->firstSubmenuItem; walk; walk = walk->nextMenuItem) + { + if(!walk->visible) + continue; + + char buf[512]; + + // Can't have submenus within submenus. + char isSubmenu = 1; + + char bitmapIndex = 1; + if(walk->bitmapIndex >= 0 && (walk->bitmapIndex * 3 <= mProfile->mBitmapArrayRects.size())) + bitmapIndex = walk->bitmapIndex + 2; + dSprintf(buf, sizeof(buf), "%c%c\t%s\t%s", bitmapIndex, isSubmenu, walk->text, walk->accelerator ? walk->accelerator : ""); + mSubmenuTextList->addEntry(entryCount, buf); + + if(!walk->enabled) + mSubmenuTextList->setEntryActive(entryCount, false); + + entryCount++; + } + Point2I menuPoint = bounds.point; //localToGlobalCoord(bounds.point); + menuPoint.x += bounds.extent.x; + menuPoint.y += cellSize.y * selectionIndex - 6; + + GuiControl *ctrl = new GuiControl; + RectI ctrlBounds(menuPoint, mSubmenuTextList->getExtent() + Point2I(6, 6)); + ctrl->setControlProfile(mProfile); + mSubmenuTextList->setPosition( getPosition() + Point2I(3,3)); + + // Make sure the menu doesn't go beyond the Canvas' bottom edge. + if((ctrlBounds.point.y + ctrlBounds.extent.y ) > windowExt.y) + { + // Pop the menu above the menu bar + ctrlBounds.point.y -= mSubmenuTextList->getHeight() - cellSize.y - 6 - 3; + } + + // And the same for the right edge + if((ctrlBounds.point.x + ctrlBounds.extent.x) > windowExt.x) + { + // Pop the submenu to the left of the menu + ctrlBounds.point.x -= mSubmenuTextList->getWidth() + cellSize.x + 6; + } + ctrl->resize(ctrlBounds.point, ctrlBounds.extent); + + //mSubmenuTextList->setPosition(Point2I(3,3)); + + mSubmenuTextList->registerObject(); + mSubmenuBackground->registerObject(); + ctrl->registerObject(); + + mSubmenuBackground->addObject( ctrl ); + ctrl->addObject( mSubmenuTextList ); + + root->pushDialogControl(mSubmenuBackground, mLayer + 1); + mSubmenuTextList->setFirstResponder(); +} + +// Close down the submenu controls +void GuiMenuBar::closeSubmenu() +{ + if(!mSubmenuBackground || !mSubmenuTextList) + return; + + // Get the selection from the text list: + S32 selectionIndex = mSubmenuTextList->getSelectedCell().y; + + // Pop the background: + if( getRoot() ) + getRoot()->popDialogControl(mSubmenuBackground); + + // Kill the popup: + mSubmenuBackground->deleteObject(); + mSubmenuBackground = NULL; + mSubmenuTextList = NULL; + + // Now perform the popup action: + if ( selectionIndex != -1 ) + { + MenuItem *list = NULL; + if(mouseOverSubmenu) + { + list = mouseOverSubmenu->firstSubmenuItem; + + while(selectionIndex && list) + { + list = list->nextMenuItem; + selectionIndex--; + } + } + if(list) + menuItemSelected(list->submenuParentMenu, list); + } + mouseOverSubmenu = NULL; +} + +// Find if the mouse pointer is within a menu item +GuiMenuBar::MenuItem *GuiMenuBar::findHitMenuItem(Point2I mousePoint) +{ + Point2I pos = globalToLocalCoord(mousePoint); + +// for(Menu *walk = menuList; walk; walk = walk->nextMenu) +// if(walk->visible && walk->bounds.pointInRect(pos)) +// return walk; + return NULL; +} + +// Checks if the mouse has been moved to a new menu item +void GuiMenuBar::checkSubmenuMouseMove(const GuiEvent &event) +{ + MenuItem *hit = findHitMenuItem(event.mousePoint); + if(hit && hit != mouseOverSubmenu) + { + // gotta close out the current menu... + mSubmenuTextList->setSelectedCell(Point2I(-1, -1)); +// closeSubmenu(); + setUpdate(); + } +} + +// Process a tick +void GuiMenuBar::processTick() +{ + // If we are to track a tick, then do so. + if(mCountMouseOver) + { + // If we're at a particular number of ticks, notify the script function + if(mMouseOverCounter < mMouseHoverAmount) + { + ++mMouseOverCounter; + + } else if(mMouseOverCounter == mMouseHoverAmount) + { + ++mMouseOverCounter; + onMouseInMenu_callback(true); // Last parameter indicates if we've entered or left the menu + } + } +} diff --git a/Engine/source/gui/editor/guiMenuBar.h b/Engine/source/gui/editor/guiMenuBar.h new file mode 100644 index 000000000..d852f93b8 --- /dev/null +++ b/Engine/source/gui/editor/guiMenuBar.h @@ -0,0 +1,222 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIMENUBAR_H_ +#define _GUIMENUBAR_H_ + +#ifndef _GUITEXTLISTCTRL_H_ +#include "gui/controls/guiTextListCtrl.h" +#endif +#ifndef _GUITICKCTRL_H_ +#include "gui/shiny/guiTickCtrl.h" +#endif + +class GuiMenuBar; +class GuiMenuTextListCtrl; + +class GuiMenuBackgroundCtrl : public GuiControl +{ + typedef GuiControl Parent; + +protected: + GuiMenuBar *mMenuBarCtrl; + GuiMenuTextListCtrl *mTextList; +public: + GuiMenuBackgroundCtrl(GuiMenuBar *ctrl, GuiMenuTextListCtrl* textList); + void onMouseDown(const GuiEvent &event); + void onMouseMove(const GuiEvent &event); + void onMouseDragged(const GuiEvent &event); +}; + +class GuiSubmenuBackgroundCtrl : public GuiMenuBackgroundCtrl +{ + typedef GuiMenuBackgroundCtrl Parent; + +public: + GuiSubmenuBackgroundCtrl(GuiMenuBar *ctrl, GuiMenuTextListCtrl* textList); + bool pointInControl(const Point2I & parentCoordPoint); + void onMouseDown(const GuiEvent &event); +}; + +//------------------------------------------------------------------------------ + +class GuiMenuTextListCtrl : public GuiTextListCtrl +{ + private: + typedef GuiTextListCtrl Parent; + + protected: + GuiMenuBar *mMenuBarCtrl; + + public: + bool isSubMenu; // Indicates that this text list is in a submenu + + GuiMenuTextListCtrl(); // for inheritance + GuiMenuTextListCtrl(GuiMenuBar *ctrl); + + // GuiControl overloads: + bool onKeyDown(const GuiEvent &event); + void onMouseDown(const GuiEvent &event); + void onMouseUp(const GuiEvent &event); + void onRenderCell(Point2I offset, Point2I cell, bool selected, bool mouseOver); + + virtual void onCellHighlighted(Point2I cell); // Added +}; + +//------------------------------------------------------------------------------ + +class GuiMenuBar : public GuiTickCtrl // Was: GuiControl +{ + typedef GuiTickCtrl Parent; // Was: GuiControl Parent; +public: + + struct Menu; + + struct MenuItem // an individual item in a pull-down menu + { + char *text; // the text of the menu item + U32 id; // a script-assigned identifier + char *accelerator; // the keyboard accelerator shortcut for the menu item + U32 acceleratorIndex; // index of this accelerator + bool enabled; // true if the menu item is selectable + bool visible; // true if the menu item is visible + S32 bitmapIndex; // index of the bitmap in the bitmap array + S32 checkGroup; // the group index of the item visa vi check marks - + // only one item in the group can be checked. + MenuItem *nextMenuItem; // next menu item in the linked list + + bool isSubmenu; // This menu item has a submenu that will be displayed + MenuItem *firstSubmenuItem; // The first menu item in the submenu + + Menu* submenuParentMenu; // For a submenu, this is the parent menu + }; + + struct Menu + { + char *text; + U32 id; + RectI bounds; + bool visible; + + S32 bitmapIndex; // Index of the bitmap in the bitmap array (-1 = no bitmap) + bool drawBitmapOnly; // Draw only the bitmap and not the text + bool drawBorder; // Should a border be drawn around this menu (usually if we only have a bitmap, we don't want a border) + + Menu *nextMenu; + MenuItem *firstMenuItem; + }; + + GuiMenuBackgroundCtrl *mBackground; + GuiMenuTextListCtrl *mTextList; + + GuiSubmenuBackgroundCtrl *mSubmenuBackground; // Background for a submenu + GuiMenuTextListCtrl *mSubmenuTextList; // Text list for a submenu + + Menu *menuList; + Menu *mouseDownMenu; + Menu *mouseOverMenu; + + MenuItem* mouseDownSubmenu; // Stores the menu item that is a submenu that has been selected + MenuItem* mouseOverSubmenu; // Stores the menu item that is a submenu that has been highlighted + + bool menuBarDirty; + U32 mCurAcceleratorIndex; + Point2I maxBitmapSize; + + S32 mCheckmarkBitmapIndex; // Index in the bitmap array to use for the check mark image + + S32 mPadding; + S32 mHorizontalMargin; // Left and right margin around the text of each menu + S32 mVerticalMargin; // Top and bottom margin around the text of each menu + S32 mBitmapMargin; // Margin between a menu's bitmap and text + + // Used to keep track of the amount of ticks that the mouse is hovering + // over a menu. + S32 mMouseOverCounter; + bool mCountMouseOver; + S32 mMouseHoverAmount; + + GuiMenuBar(); + bool onWake(); + void onSleep(); + + // internal menu handling functions + // these are used by the script manipulation functions to add/remove/change menu items + + void addMenu(const char *menuText, U32 menuId); + Menu *findMenu(const char *menu); // takes either a menu text or a string id + MenuItem *findMenuItem(Menu *menu, const char *menuItem); // takes either a menu text or a string id + void removeMenu(Menu *menu); + void removeMenuItem(Menu *menu, MenuItem *menuItem); + void addMenuItem(Menu *menu, const char *text, U32 id, const char *accelerator, S32 checkGroup); + void clearMenuItems(Menu *menu); + void clearMenus(); + + // Methods to deal with submenus + MenuItem* findSubmenuItem(Menu *menu, const char *menuItem, const char *submenuItem); + void addSubmenuItem(Menu *menu, MenuItem *submenu, const char *text, U32 id, const char *accelerator, S32 checkGroup); + void removeSubmenuItem(MenuItem *menuItem, MenuItem *submenuItem); + void clearSubmenuItems(MenuItem *menuitem); + void onSubmenuAction(S32 selectionIndex, RectI bounds, Point2I cellSize); + void closeSubmenu(); + void checkSubmenuMouseMove(const GuiEvent &event); + MenuItem *findHitMenuItem(Point2I mousePoint); + + void highlightedMenuItem(S32 selectionIndex, RectI bounds, Point2I cellSize); // Called whenever a menu item is highlighted by the mouse + + // display/mouse functions + + Menu *findHitMenu(Point2I mousePoint); + + // Called when the GUI theme changes and a bitmap arrary may need updating + // void onThemeChange(); + + void onPreRender(); + void onRender(Point2I offset, const RectI &updateRect); + + void checkMenuMouseMove(const GuiEvent &event); + void onMouseMove(const GuiEvent &event); + void onMouseLeave(const GuiEvent &event); + void onMouseDown(const GuiEvent &event); + void onMouseDragged(const GuiEvent &event); + void onMouseUp(const GuiEvent &event); + + void onAction(); + void closeMenu(); + void buildAcceleratorMap(); + void acceleratorKeyPress(U32 index); + + void menuItemSelected(Menu *menu, MenuItem *item); + + // Added to support 'ticks' + void processTick(); + + static void initPersistFields(); + + DECLARE_CONOBJECT(GuiMenuBar); + DECLARE_CALLBACK( void, onMouseInMenu, (bool hasLeftMenu)); + DECLARE_CALLBACK( void, onMenuSelect, (const char* menuId, const char* menuText)); + DECLARE_CALLBACK( void, onMenuItemSelect, ( const char* menuId, const char* menuText, const char* menuItemId, const char* menuItemText )); + DECLARE_CALLBACK( void, onSubmenuSelect, ( const char* submenuId, const char* submenuText)); +}; + +#endif diff --git a/Engine/source/gui/editor/guiParticleGraphCtrl.cpp b/Engine/source/gui/editor/guiParticleGraphCtrl.cpp new file mode 100644 index 000000000..37cc0365e --- /dev/null +++ b/Engine/source/gui/editor/guiParticleGraphCtrl.cpp @@ -0,0 +1,1453 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/gfxInit.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxDrawUtil.h" +#include "console/console.h" +#include "console/consoleTypes.h" +#include "gui/core/guiCanvas.h" + +#include "gui/editor/guiParticleGraphCtrl.h" + +IMPLEMENT_CONOBJECT(GuiParticleGraphCtrl); + +ConsoleDocClass( GuiParticleGraphCtrl, + "@brief Legacy remnant from old Torque particle editor\n\n" + "Editor use only.\n\n" + "@internal" +); + +GuiParticleGraphCtrl::GuiParticleGraphCtrl() +{ + + for(int i = 0; i < MaxPlots; i++) + { + mPlots[i].mGraphColor = ColorF(1.0, 1.0, 1.0); + VECTOR_SET_ASSOCIATION(mPlots[i].mGraphData); + mPlots[i].mGraphMax.x = 1; + mPlots[i].mGraphMax.y = 50; + mPlots[i].mGraphMin.x = 0; + mPlots[i].mGraphMin.y = 0; + mPlots[i].mGraphType = Polyline; + mPlots[i].mGraphName = StringTable->insert(""); + mPlots[i].mHidden = false; + mPlots[i].mGraphScale = 0.05f; + } + + mPlots[0].mGraphColor = ColorF(1.0f, 0.2f, 0.2f); + mPlots[1].mGraphColor = ColorF(1.0f, 0.5f, 0.5f); + mPlots[2].mGraphColor = ColorF(0.0f, 1.0f, 0.0f); + mPlots[3].mGraphColor = ColorF(0.0f, 0.0f, 1.0f); + mPlots[4].mGraphColor = ColorF(0.0f, 1.0f, 1.0f); + mPlots[5].mGraphColor = ColorF(0.0f, 0.0f, 0.0f); + mPlots[6].mGraphColor = ColorF(0.5f, 0.5f, 0.5f); + mPlots[7].mGraphColor = ColorF(0.5f, 0.0f, 0.0f); + mPlots[8].mGraphColor = ColorF(0.0f, 0.5f, 0.0f); + mPlots[9].mGraphColor = ColorF(0.0f, 0.0f, 0.5f); + mPlots[10].mGraphColor = ColorF(0.0f, 0.5f, 0.5f); + mPlots[11].mGraphColor = ColorF(0.25f, 0.25f, 0.25f); + mPlots[12].mGraphColor = ColorF(0.5f, 0.5f, 0.5f); + mPlots[13].mGraphColor = ColorF(0.5f, 0.0f, 0.0f); + mPlots[14].mGraphColor = ColorF(0.0f, 0.5f, 0.0f); + mPlots[15].mGraphColor = ColorF(0.0f, 0.0f, 0.5f); + mPlots[16].mGraphColor = ColorF(0.0f, 0.5f, 0.5f); + mPlots[17].mGraphColor = ColorF(0.25f, 0.25f, 0.25f); + mPlots[18].mGraphColor = ColorF(1.0f, 0.2f, 0.2f); + mPlots[19].mGraphColor = ColorF(1.0f, 0.5f, 0.5f); + mPlots[20].mGraphColor = ColorF(0.0f, 1.0f, 0.0f); + mPlots[21].mGraphColor = ColorF(0.0f, 0.0f, 1.0f); + mPlots[22].mGraphColor = ColorF(0.0f, 1.0f, 1.0f); + mPlots[23].mGraphColor = ColorF(0.0f, 0.0f, 0.0f); + mPlots[24].mGraphColor = ColorF(0.5f, 0.5f, 0.5f); + mPlots[25].mGraphColor = ColorF(0.5f, 0.0f, 0.0f); + mPlots[26].mGraphColor = ColorF(0.0f, 0.5f, 0.0f); + mPlots[27].mGraphColor = ColorF(0.0f, 0.0f, 0.5f); + mPlots[28].mGraphColor = ColorF(1.0f, 0.0f, 0.0f); + mPlots[29].mGraphColor = ColorF(0.0f, 1.0f, 0.0f); + mPlots[30].mGraphColor = ColorF(0.0f, 0.0f, 1.0f); + mPlots[31].mGraphColor = ColorF(0.5f, 0.0f, 0.0f); + + mVertexClickSize = 6; + mSelectedPlot = 0; + mSelectedPoint = -1; + mOriginalSelectedPoint = -1; + mLastSelectedPoint = -1; + mAddedPointIndex = -1; + mAutoMax = false; + mAutoRemove = false; + mRenderAllPoints = false; + mRenderGraphTooltip = true; + mPointWasAdded = false; + mPointXMovementClamped = false; + mOutlineColor = ColorI(1, 1, 1); + mCursorPos = Point2I(0, 0); +} + +ImplementEnumType( GuiParticleGraphType, + "\n\n" + "@ingroup GuiCore" + "@internal") + { GuiParticleGraphCtrl::Bar, "bar" }, + { GuiParticleGraphCtrl::Filled, "filled" }, + { GuiParticleGraphCtrl::Point, "point" }, + { GuiParticleGraphCtrl::Polyline , "polyline" }, +EndImplementEnumType; + +bool GuiParticleGraphCtrl::onWake() +{ + if (! Parent::onWake()) + return false; + setActive(true); + return true; +} + +void GuiParticleGraphCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + // Fetch Draw Utility. + GFXDrawUtil* pDrawUtil = GFX->getDrawUtil(); + + if (mProfile->mBorder) + { + const RectI bounds = getBounds(); + RectI rect(offset.x, offset.y, bounds.extent.x, bounds.extent.y); + pDrawUtil->drawRect(rect, mProfile->mBorderColor); + } + + GuiControlProfile* profile = dynamic_cast(Sim::findObject("GuiDefaultProfile")); + Resource font = profile->mFont; + GFXVideoMode videoMode = GFXInit::getDesktopResolution(); + + ColorF color(1.0f, 1.0f, 1.0f, 0.5f); + pDrawUtil->drawRectFill(updateRect, color); + + for (int k = 0; k < MaxPlots; k++) + { + // Nothing to graph + if ((mPlots[k].mGraphData.size() == 0) || (mPlots[k].mHidden == true)) + continue; + + Point2F graphExtent = getGraphExtent(k); + + // Adjust scale to max value + 5% so we can see high value + F32 ScaleX = (F32(getExtent().x) / (graphExtent.x*(1.00 + (mPlots[k].mGraphScale)))); + F32 ScaleY = (F32(getExtent().y) / (graphExtent.y*(1.00 + (mPlots[k].mGraphScale)))); + + if((mPlots[k].mGraphType == Point) || (mPlots[k].mGraphType == Polyline)) + { + S32 posX; + S32 posY; + S32 lastPosX = 0; + S32 lastPosY = 0; + Point2F plotPoint; + + S32 size = 32; + + for (S32 sample = 0; sample < mPlots[k].mGraphData.size(); sample++) + { + S32 temp; + + temp = (S32)(((F32)getExtent().x / (F32)mPlots[k].mGraphData.size()) * (F32)sample); + + // calculate the point positions + plotPoint = getPlotPoint(k, sample); + + posX = (S32)((plotPoint.x - mPlots[k].mGraphMin.x) * (ScaleX /(1.00 + mPlots[k].mGraphScale))); + posY = (getExtent().y) - (S32)((plotPoint.y - mPlots[k].mGraphMin.y) * ScaleY); + + posX += getExtent().x * (mPlots[k].mGraphScale); + posY /= (1.00 + (mPlots[k].mGraphScale)); + + posX = localToGlobalCoord(Point2I(posX, posY)).x; + posY = localToGlobalCoord(Point2I(posX, posY)).y; + + // check if this isn't our first loop through, if it is we won't have starting points + if(sample > 0) + { + pDrawUtil->drawLine( lastPosX, lastPosY , posX, posY , mPlots[k].mGraphColor ); + } else + { + mPlots[k].mNutList.clear(); + } + + mPlots[k].mNutList.push_back( Point2F(posX, posY) ); + + // store the last positions to be the starting points drawn into a line next loop + lastPosX = posX; + lastPosY = posY; + + //Con::printf("red %f green %f blue %f", mPlots[k].mGraphColor.red, mPlots[k].mGraphColor.green, mPlots[k].mGraphColor.blue); + + + + if(mSelectedPoint != -1) + { + mLastSelectedPoint = mSelectedPoint; + } + + ColorI nutColor (mPlots[k].mGraphColor); + + if(k == mSelectedPlot && sample == mLastSelectedPoint) + { + // grab the colors for the nut + F32 red = mPlots[k].mGraphColor.red; + F32 green = mPlots[k].mGraphColor.green; + F32 blue = mPlots[k].mGraphColor.blue; + + // invert the colors for the nut since this is a selected nut + red = 1.0 - red; + green = 1.0 - green; + blue = 1.0 - blue; + // nut color + + nutColor = ColorI(ColorF(red, green, blue)); + } + + // draw the seleciton nut + drawNut( Point2I(posX, posY), 3, mOutlineColor, nutColor ); + + if((mLastSelectedPoint != -1) || (mRenderAllPoints == true)) + { + if((k == mSelectedPlot && sample == mLastSelectedPoint) || (mRenderAllPoints == true)) + { + char number[32]; + + Point2I comparePos = localToGlobalCoord(Point2I(getPosition().x, getPosition().y)); + + dSprintf(number, 32, "%4.3f %4.3f", plotPoint.x, plotPoint.y); + + S32 textWidth = (S32)font->getStrWidth((const UTF8*)number);; + textWidth /= 2; + + if((((S32)posX - (textWidth/2)) < comparePos.x) || (((S32)posX - textWidth) <= 0)) + { + posX += (textWidth/1.5); + } else if((posX + (textWidth * 1.8)) > (comparePos.x + getExtent().x) || ((posX + textWidth) >= videoMode.resolution.x)) + { + posX -= (textWidth * 1.5); + } + + if((((S32)posY) < comparePos.y) || (((S32)posY - textWidth) <= 0)) + { + posY += 40; + } + + pDrawUtil->setBitmapModulation( profile->mFontColor ); + pDrawUtil->drawText( font, Point2I(posX, posY + 5) - Point2I(size >> 1, size), number ); + pDrawUtil->clearBitmapModulation(); + } + } + } + } + } + + if(mRenderNextGraphTooltip == true && mRenderGraphTooltip == true) + { + char argBuffer[1][32]; + + dSprintf(argBuffer[0], 32, "%s", getGraphName(mTooltipSelectedPlot)); + + renderGraphTooltip(mCursorPos, argBuffer[0]); + } +} + +S32 GuiParticleGraphCtrl::addPlotPoint(S32 plotID, Point2F v, bool setAdded) +{ + S32 mPlotIndex = 0; + bool plotChanged = false; + bool plotAdded = false; + + if(mAutoMax == false) + { + if(v.x < mPlots[plotID].mGraphMin.x) + { + v.x = mPlots[plotID].mGraphMin.x; + } + + if(v.x > mPlots[plotID].mGraphMax.x) + { + v.x = mPlots[plotID].mGraphMax.x; + } + + if(v.y < mPlots[plotID].mGraphMin.y) + { + v.y = mPlots[plotID].mGraphMin.y; + } + + if(v.y > mPlots[plotID].mGraphMax.y) + { + v.y = mPlots[plotID].mGraphMax.y; + } + } + + for(S32 i = 0; i < mPlots[plotID].mGraphData.size(); i++) + { + if(mFabs(v.x - mPlots[plotID].mGraphData[i].x) < 0.001) + { + if(mAutoRemove == true) + { + changePlotPoint(plotID, i, v); + plotChanged = true; + mPlotIndex = i; + } else + { + mPlotIndex = -1; + } + + plotAdded = true; + + break; + } else if(v.x < mPlots[plotID].mGraphData[i].x) + { + insertPlotPoint(plotID, i, v); + plotAdded = true; + mPlotIndex = i; + break; + } + } + + if(plotAdded == false) + { + mPlots[plotID].mGraphData.push_back( v ); + mPlotIndex = mPlots[plotID].mGraphData.size() - 1; + } + + if(mAutoMax == true) + { + // Keep record of maximum data value for scaling purposes. + if(v.y > mPlots[plotID].mGraphMax.y) + mPlots[plotID].mGraphMax.y = v.y; + + if(v.x > mPlots[plotID].mGraphMax.x) + mPlots[plotID].mGraphMax.x = v.x; + } + + if(plotChanged == true) + { + mPointWasAdded = false; + } else if(mPlotIndex != -1 && setAdded) + { + mPointWasAdded = true; + mAddedPoint = v; + mAddedPointIndex = mPlotIndex; + } + + return mPlotIndex; +} + +void GuiParticleGraphCtrl::insertPlotPoint(S32 plotID, S32 i, Point2F v) +{ + //AssertFatal(plotID > -1 && plotID < MaxPlots, "Invalid plot specified!"); + + // Add the data and trim the vector... + mPlots[plotID].mGraphData.insert( i ); + mPlots[plotID].mGraphData[ i ] = v; + + if(mAutoMax == true) + { + // Keep record of maximum data value for scaling purposes. + if(v.y > mPlots[plotID].mGraphMax.y) + mPlots[plotID].mGraphMax.y = v.y; + + if(v.x > mPlots[plotID].mGraphMax.x) + mPlots[plotID].mGraphMax.x = v.x; + } + + // Argument Buffer. + char argBuffer[2][32]; + + dSprintf(argBuffer[0], 32, "%d", plotID); + dSprintf(argBuffer[1], 32, "%f %f", v.x, v.y); + + // Call Scripts. + Con::executef(this, "onPlotPointInserted", argBuffer[0], argBuffer[1]); +} + +S32 GuiParticleGraphCtrl::changePlotPoint(S32 plotID, S32 i, Point2F v) +{ + //AssertFatal(plotID > -1 && plotID < MaxPlots, "Invalid plot specified!"); + + // Add the data and trim the vector... + S32 point = removePlotPoint(plotID, i); + + // Argument Buffer. + char argBuffer[3][32]; + + dSprintf(argBuffer[0], 32, "%d", mSelectedPlot); + dSprintf(argBuffer[1], 32, "%d", point); + dSprintf(argBuffer[2], 32, "%f %f", v.x, v.y); + + // Call Scripts. + Con::executef(this, "onPlotPointRemoved", argBuffer[0], argBuffer[1], argBuffer[2]); + + // call the insert function + S32 index = addPlotPoint(plotID, v); + + return index; +} + +S32 GuiParticleGraphCtrl::removePlotPoint(S32 plotID, S32 i) +{ + if((plotID < MaxPlots && plotID >= 0) && (i < mPlots[plotID].mGraphData.size())) + { + Point2F v = mPlots[plotID].mGraphData[i]; + + mPlots[plotID].mGraphData.erase(i); + + if(i == mPlots[plotID].mGraphData.size() && mSelectedPlot == plotID && mLastSelectedPoint == i) + { + setSelectedPoint(i-1); + } else if(i < mLastSelectedPoint) + { + setSelectedPoint(mLastSelectedPoint-1); + } + } + + return i; +} + +S32 GuiParticleGraphCtrl::getSelectedPlot() +{ + return mSelectedPlot; +} + +S32 GuiParticleGraphCtrl::getSelectedPoint() +{ + return mLastSelectedPoint; +} + +bool GuiParticleGraphCtrl::isExistingPoint(S32 plotID, S32 sample) +{ + if (((plotID < 0) || (plotID > MaxPlots)) || ((sample < 0) || (sample > MaxDataPoints)) || (sample >= mPlots[plotID].mGraphData.size())) + { + return false; + } else + { + return true; + } +} + +Point2F GuiParticleGraphCtrl::getPlotPoint(S32 plotID, S32 sample) +{ + Point2F val; + val.x = -1; + val.y = -1; + + if (((plotID < 0) || (plotID > MaxPlots)) || ((sample < 0) || (sample > MaxDataPoints))) + { + return val; + } + + return mPlots[plotID].mGraphData[sample]; +} + +S32 GuiParticleGraphCtrl::getPlotIndex(S32 plotID, F32 x, F32 y) +{ + if ((plotID < 0) || (plotID > MaxPlots)) + { + return -1; + } + + F32 compareX = 0.0; + F32 compareY = 0.0; + + for(S32 i = 0; i < mPlots[plotID].mGraphData.size(); i++) + { + compareX = mPlots[plotID].mGraphData[i].x; + compareY = mPlots[plotID].mGraphData[i].y; + + // + //if((x == compareX) && (y == compareY)) + if((mFabs(x - compareX) < 0.001) && (mFabs(y - compareY) < 0.001)) + return i; + } + + return -1; +} + +void GuiParticleGraphCtrl::setGraphType(S32 plotID, const char *graphType) +{ + AssertFatal(plotID > -1 && plotID < MaxPlots, "Invalid plot specified!"); + if(!dStricmp(graphType,"Bar")) + mPlots[plotID].mGraphType = Bar; + else if(!dStricmp(graphType,"Filled")) + mPlots[plotID].mGraphType = Filled; + else if(!dStricmp(graphType,"Point")) + mPlots[plotID].mGraphType = Point; + else if(!dStricmp(graphType,"Polyline")) + mPlots[plotID].mGraphType = Polyline; + else AssertWarn(true, "Invalid graph type!"); +} + +void GuiParticleGraphCtrl::setSelectedPlot(S32 plotID) +{ + mSelectedPlot = plotID; + + // Argument Buffer. + char argBuffer[32]; + + dSprintf(argBuffer, 32, "%d", plotID); + + // Call Scripts. + Con::executef(this, "onSetSelected", argBuffer); +} + +void GuiParticleGraphCtrl::setSelectedPoint(S32 point) +{ + if(point != mSelectedPoint && point < mPlots[mSelectedPlot].mGraphData.size() && point >= 0) + { + mSelectedPoint = point; + mLastSelectedPoint = point; + char argBuffer[32]; + + dSprintf(argBuffer, 32, "%d", point); + + // Call Scripts. + Con::executef(this, "onPlotPointSelected", argBuffer); + } +} + +void GuiParticleGraphCtrl::resetSelectedPoint() +{ + mSelectedPoint = -1; +} + +void GuiParticleGraphCtrl::setAutoGraphMax(bool autoMax) +{ + mAutoMax = autoMax; +} + +void GuiParticleGraphCtrl::setAutoRemove(bool autoRemove) +{ + mAutoRemove = autoRemove; +} + +void GuiParticleGraphCtrl::setGraphHidden(S32 plotID, bool isHidden) +{ + mPlots[plotID].mHidden = isHidden; +} + +void GuiParticleGraphCtrl::setRenderAll(bool renderAll) +{ + mRenderAllPoints = renderAll; +} + +void GuiParticleGraphCtrl::setPointXMovementClamped(bool clamped) +{ + mPointXMovementClamped = clamped; +} + +void GuiParticleGraphCtrl::setRenderGraphTooltip(bool renderGraphTooltip) +{ + mRenderGraphTooltip = renderGraphTooltip; +} + + +void GuiParticleGraphCtrl::drawNut(const Point2I &nut, S32 size, ColorI &outlineColor, ColorI &nutColor) +{ + // Fetch Draw Utility. + GFXDrawUtil* pDrawUtil = GFX->getDrawUtil(); + + //Con::printf("r %d g %d b %d", nutColor.red, nutColor.green, nutColor.blue); + S32 NUT_SIZE = size; + RectI r(nut.x - NUT_SIZE, nut.y - NUT_SIZE, 2 * NUT_SIZE + 1, 2 * NUT_SIZE + 1); + r.inset(1, 1); + pDrawUtil->drawRectFill(r, nutColor); + r.inset(-1, -1); + pDrawUtil->drawRect(r, outlineColor); +} + +bool GuiParticleGraphCtrl::inNut(const Point2I &pt, S32 x, S32 y) +{ + S32 dx = pt.x - x; + S32 dy = pt.y - y; + return dx <= mVertexClickSize && dx >= -mVertexClickSize && dy <= mVertexClickSize && dy >= -mVertexClickSize; +} + +Point2I GuiParticleGraphCtrl::findHitNut( Point2I hitPoint ) +{ + for(S32 i = 0; i < MaxPlots; i++) + { + if ( (mPlots[i].mGraphData.size() == 0) || (mPlots[i].mHidden == true)) + continue; + + for (S32 j = 0 ; j < mPlots[i].mNutList.size(); j++ ) + { + if( inNut (Point2I( mPlots[i].mNutList[j].x, mPlots[i].mNutList[j].y), hitPoint.x, hitPoint.y) ) + { + mTooltipSelectedPlot = i; + return Point2I(i,j); + } + } + } + + return Point2I(-1,-1); +} + +Point2F GuiParticleGraphCtrl::convertToGraphCoord(S32 plotID, Point2I v) +{ + Point2F resultV; + + v = globalToLocalCoord( v ); + v.y = getExtent().y - v.y; + resultV = Point2F(v.x, v.y); + resultV.x /= getExtent().x; + resultV.y /= getExtent().y; + resultV.x *= getGraphExtent(plotID).x; + resultV.y *= getGraphExtent(plotID).y; + resultV.x *= 1.00 + (mPlots[plotID].mGraphScale*2); + resultV.y *= 1.00 + (mPlots[plotID].mGraphScale*2); + resultV.x -= getGraphExtent(plotID).x * ((mPlots[plotID].mGraphScale)); + resultV.y -= getGraphExtent(plotID).y * ((mPlots[plotID].mGraphScale)); + resultV.x += mPlots[plotID].mGraphMin.x; + resultV.y += mPlots[plotID].mGraphMin.y; + + if(mAutoMax == false) + { + if(resultV.x < mPlots[plotID].mGraphMin.x) + { + resultV.x = mPlots[plotID].mGraphMin.x; + } + + if(resultV.x > mPlots[plotID].mGraphMax.x) + { + resultV.x = mPlots[plotID].mGraphMax.x; + } + + if(resultV.y < mPlots[plotID].mGraphMin.y) + { + resultV.y = mPlots[plotID].mGraphMin.y; + } + + if(resultV.y > mPlots[plotID].mGraphMax.y) + { + resultV.y = mPlots[plotID].mGraphMax.y; + } + } + + + + return resultV; +} + +Point2F GuiParticleGraphCtrl::getGraphExtent(S32 plotID) +{ + Point2F resultV; + + resultV.x = mPlots[plotID].mGraphMax.x - mPlots[plotID].mGraphMin.x; + resultV.y = mPlots[plotID].mGraphMax.y - mPlots[plotID].mGraphMin.y; + + return resultV; +} + +ColorF GuiParticleGraphCtrl::getGraphColor(S32 plotID) +{ + return mPlots[plotID].mGraphColor; +} + +Point2F GuiParticleGraphCtrl::getGraphMax(S32 plotID) +{ + return mPlots[plotID].mGraphMax; +} + +Point2F GuiParticleGraphCtrl::getGraphMin(S32 plotID) +{ + return mPlots[plotID].mGraphMin; +} + +void GuiParticleGraphCtrl::setGraphMin(S32 plotID, Point2F graphMin) +{ + mPlots[plotID].mGraphMin = graphMin; +} + +void GuiParticleGraphCtrl::setGraphMax(S32 plotID, Point2F graphMax) +{ + mPlots[plotID].mGraphMax = graphMax; +} + +void GuiParticleGraphCtrl::setGraphMinX(S32 plotID, F32 graphMinX) +{ + mPlots[plotID].mGraphMin.x = graphMinX; +} + +void GuiParticleGraphCtrl::setGraphMinY(S32 plotID, F32 graphMinY) +{ + mPlots[plotID].mGraphMin.y = graphMinY; +} + +void GuiParticleGraphCtrl::setGraphMaxX(S32 plotID, F32 graphMaxX) +{ + mPlots[plotID].mGraphMax.x = graphMaxX; +} + +void GuiParticleGraphCtrl::setGraphMaxY(S32 plotID, F32 graphMaxY) +{ + mPlots[plotID].mGraphMax.y = graphMaxY; +} + +void GuiParticleGraphCtrl::setGraphName(S32 plotID, StringTableEntry graphName) +{ + mPlots[plotID].mGraphName = StringTable->insert(graphName); +} + +StringTableEntry GuiParticleGraphCtrl::getGraphName(S32 plotID) +{ + return mPlots[plotID].mGraphName; +} + +void GuiParticleGraphCtrl::onMouseMove(const GuiEvent &event) +{ + mCursorPos = event.mousePoint; + + Point2I hitNut = findHitNut(event.mousePoint); + + if( hitNut != Point2I(-1,-1) ) + { + mRenderNextGraphTooltip = true; + } else + { + mRenderNextGraphTooltip = false; + } + + // Argument Buffer. + char argBuffer[32]; + + dSprintf(argBuffer, 32, "%f %f", convertToGraphCoord(mSelectedPlot, event.mousePoint).x, convertToGraphCoord(mSelectedPlot, event.mousePoint).y); + + + // Call Scripts. + Con::executef(this, "onMouseMove", argBuffer); +} + +void GuiParticleGraphCtrl::onMouseDown(const GuiEvent &event) +{ + Point2I hitNut = findHitNut(event.mousePoint); + + if( hitNut != Point2I(-1,-1) ) + { + if(event.mouseClickCount == 2) + { + Point2F plotPoint = getPlotPoint(hitNut.x, hitNut.y); + Point2F mousePos = convertToGraphCoord(mSelectedPlot, event.mousePoint); + S32 point = removePlotPoint(hitNut.x, hitNut.y); + + // Argument Buffer. + char argBuffer[3][32]; + + dSprintf(argBuffer[0], 32, "%d", mSelectedPlot); + dSprintf(argBuffer[1], 32, "%d", point); + dSprintf(argBuffer[2], 32, "%f %f", plotPoint.x, plotPoint.y); + + + + // Call Scripts. + Con::executef(this, "onPlotPointRemoved", argBuffer[0], argBuffer[1], argBuffer[2]); + } else + { + setSelectedPlot(hitNut.x); + setSelectedPoint(hitNut.y); + mOriginalSelectedPoint = hitNut.y; + + char argBuffer[32]; + + dSprintf(argBuffer, 32, "%d", hitNut.y); + + // Call Scripts. + Con::executef(this, "onPlotPointSelectedMouseDown", argBuffer); + } + } else if( mSelectedPlot != -1 ) + { + Point2F mousePos = convertToGraphCoord(mSelectedPlot, event.mousePoint); + mLastSelectedPoint = addPlotPoint(mSelectedPlot, mousePos); + + // Argument Buffer. + char argBuffer[32]; + + dSprintf(argBuffer, 32, "%f %f", convertToGraphCoord(mSelectedPlot, event.mousePoint).x, convertToGraphCoord(mSelectedPlot, event.mousePoint).y); + + // Call Scripts. + Con::executef(this, "onMouseDragged", argBuffer); + + return; + } +} + +void GuiParticleGraphCtrl::onMouseUp(const GuiEvent &event) +{ + if(mSelectedPoint != -1) + mLastSelectedPoint = mSelectedPoint; + + if(mPointWasAdded == true) + { + if(mSelectedPoint == -1) + { + // Argument Buffer. + char argBuffer[3][32]; + + dSprintf(argBuffer[0], 32, "%d", mSelectedPlot); + dSprintf(argBuffer[1], 32, "%f %f", mAddedPoint.x, mAddedPoint.y); + dSprintf(argBuffer[2], 32, "%d", mAddedPointIndex); + + // Call Scripts. + Con::executef(this, "onPlotPointAdded", argBuffer[0], argBuffer[1], argBuffer[2]); + } else + { + // Argument Buffer. + char argBuffer[4][32]; + + dSprintf(argBuffer[0], 32, "%d", mSelectedPlot); + dSprintf(argBuffer[1], 32, "%f %f", mAddedPoint.x, mAddedPoint.y); + dSprintf(argBuffer[2], 32, "%d", mOriginalSelectedPoint); + dSprintf(argBuffer[3], 32, "%d", mAddedPointIndex); + + // Call Scripts. + Con::executef(this, "onPlotPointChangedUp", argBuffer[0], argBuffer[1], argBuffer[2], argBuffer[3]); + } + } + + mPointWasAdded = false; + + mSelectedPoint = -1; +} + +void GuiParticleGraphCtrl::onMouseDragged(const GuiEvent &event) +{ + mRenderNextGraphTooltip = false; + + if(mSelectedPoint != -1) + { + Point2F mousePos = convertToGraphCoord(mSelectedPlot, event.mousePoint); + + if(mPointXMovementClamped == true) + { + F32 prevXPos = getPlotPoint(mSelectedPlot, mSelectedPoint).x; + if(mousePos.x != prevXPos) + { + mousePos.x = prevXPos; + } + } + + removePlotPoint(mSelectedPlot, mSelectedPoint); + S32 point = addPlotPoint(mSelectedPlot, mousePos); + if(point != -1) + { + setSelectedPoint(point); + mLastMousePos = mousePos; + + // Argument Buffer. + char argBuffer[3][32]; + + dSprintf(argBuffer[0], 32, "%d", mSelectedPlot); + dSprintf(argBuffer[1], 32, "%f %f", mAddedPoint.x, mAddedPoint.y); + dSprintf(argBuffer[2], 32, "%d", point); + + // Call Scripts. + Con::executef(this, "onPlotPointChangedMove", argBuffer[0], argBuffer[1], argBuffer[2]); + } else + { + point = addPlotPoint(mSelectedPlot, mLastMousePos); + } + } + + // Argument Buffer. + char argBuffer[32]; + + dSprintf(argBuffer, 32, "%f %f", convertToGraphCoord(mSelectedPlot, event.mousePoint).x, convertToGraphCoord(mSelectedPlot, event.mousePoint).y); + + // Call Scripts. + Con::executef(this, "onMouseDragged", argBuffer); +} + +void GuiParticleGraphCtrl::onRightMouseDown(const GuiEvent &event) +{ + Point2I hitNut = findHitNut(event.mousePoint); + + if( hitNut != Point2I(-1,-1) ) + { + Point2F plotPoint = getPlotPoint(hitNut.x, hitNut.y); + + Point2F mousePos = convertToGraphCoord(mSelectedPlot, event.mousePoint); + S32 point = removePlotPoint(hitNut.x, hitNut.y); + + // Argument Buffer. + char argBuffer[3][32]; + + dSprintf(argBuffer[0], 32, "%d", mSelectedPlot); + dSprintf(argBuffer[1], 32, "%d", point); + dSprintf(argBuffer[2], 32, "%f %f", plotPoint.x, plotPoint.y); + + // Call Scripts. + Con::executef(this, "onPlotPointRemoved", argBuffer[0], argBuffer[1], argBuffer[2]); + } +} + +void GuiParticleGraphCtrl::onRightMouseUp(const GuiEvent &event) +{ + +} + +void GuiParticleGraphCtrl::onRightMouseDragged(const GuiEvent &event) +{ + Point2I hitNut = findHitNut(event.mousePoint); + + if( hitNut != Point2I(-1,-1) ) + { + Point2F plotPoint = getPlotPoint(hitNut.x, hitNut.y); + + Point2F mousePos = convertToGraphCoord(mSelectedPlot, event.mousePoint); + S32 point = removePlotPoint(hitNut.x, hitNut.y); + + // Argument Buffer. + char argBuffer[3][32]; + + dSprintf(argBuffer[0], 32, "%d", mSelectedPlot); + dSprintf(argBuffer[1], 32, "%d", point); + dSprintf(argBuffer[2], 32, "%f %f", plotPoint.x, plotPoint.y); + + // Call Scripts. + Con::executef(this, "onPlotPointRemoved", argBuffer[0], argBuffer[1], argBuffer[2]); + } +} + +void GuiParticleGraphCtrl::clearAllGraphs() +{ + for(S32 i = 0;i < MaxPlots;i++) + { + clearGraph(i); + } +} + +void GuiParticleGraphCtrl::clearGraph(S32 plotID) +{ + mPlots[plotID].mGraphData.clear(); + mPlots[plotID].mNutList.clear(); +} + +bool GuiParticleGraphCtrl::renderGraphTooltip(Point2I cursorPos, StringTableEntry tooltip) +{ + // Short Circuit. + if (!mAwake) + return false; + if ( dStrlen( tooltip ) == 0 ) + return false; + + // Need to have root. + GuiCanvas *root = getRoot(); + if ( !root ) + return false; + + if (!mTooltipProfile) + setTooltipProfile( mProfile ); + + GFont *font = mTooltipProfile->mFont; + + // Fetch Canvas. + GuiCanvas *pCanvas = getRoot(); + + //Vars used: + //Screensize (for position check) + //Offset to get position of cursor + //textBounds for text extent. + Point2I screensize = pCanvas->getWindowSize(); + Point2I offset = cursorPos; + Point2I textBounds; + S32 textWidth = font->getStrWidth(tooltip); + + //Offset below cursor image + offset.y -= root->getCursorExtent().y; + //Create text bounds. + textBounds.x = textWidth+8; + textBounds.y = font->getHeight() + 4; + + // Check position/width to make sure all of the tooltip will be rendered + // 5 is given as a buffer against the edge + if (screensize.x < offset.x + textBounds.x + 5) + offset.x = screensize.x - textBounds.x - 5; + + // And ditto for the height + if(screensize.y < offset.y + textBounds.y + 5) + offset.y = cursorPos.y - textBounds.y - 5; + + // Set rectangle for the box, and set the clip rectangle. + RectI rect(offset, textBounds); + GFX->setClipRect(rect); + + // Fetch Draw Utility. + GFXDrawUtil* pDrawUtil = GFX->getDrawUtil(); + + // Draw Filler bit, then border on top of that + pDrawUtil->drawRectFill(rect, ColorI(mTooltipProfile->mFillColor.red, mTooltipProfile->mFillColor.green, mTooltipProfile->mFillColor.blue, 200) ); + pDrawUtil->drawRect( rect, mTooltipProfile->mBorderColor ); + + // Draw the text centered in the tool tip box + pDrawUtil->setBitmapModulation( mTooltipProfile->mFontColor ); + Point2I start; + start.set( ( textBounds.x - textWidth) / 2, ( textBounds.y - font->getHeight() ) / 2 ); + pDrawUtil->drawText( font, start + offset, tooltip, mProfile->mFontColors ); + pDrawUtil->clearBitmapModulation(); + + return true; +} + +ConsoleMethod(GuiParticleGraphCtrl, setSelectedPoint, void, 3, 3, "(int point)" + "Set the selected point on the graph.\n" + "@return No return value") +{ + S32 point = dAtoi(argv[2]); + if(point >= object->mPlots[object->mSelectedPlot].mGraphData.size() || point < 0) + { + Con::errorf("Invalid point to select."); + return; + } + object->setSelectedPoint( point ); +} + +ConsoleMethod(GuiParticleGraphCtrl, setSelectedPlot, void, 3, 3, "(int plotID)" + "Set the selected plot (a.k.a. graph)." + "@return No return value" ) +{ + S32 plotID = dAtoi(argv[2]); + if(plotID > object->MaxPlots) + { + Con::errorf("Invalid plotID."); + return; + } + object->setSelectedPlot( plotID ); +} + +ConsoleMethod(GuiParticleGraphCtrl, clearGraph, void, 3, 3, "(int plotID)" + "Clear the graph of the given plot." + "@return No return value") +{ + S32 plotID = dAtoi(argv[2]); + if(plotID > object->MaxPlots) + { + Con::errorf("Invalid plotID."); + return; + } + object->clearGraph( plotID ); +} + +ConsoleMethod(GuiParticleGraphCtrl, clearAllGraphs, void, 2, 2, "()" + "Clear all of the graphs." + "@return No return value") +{ + object->clearAllGraphs(); +} + +ConsoleMethod(GuiParticleGraphCtrl, addPlotPoint, const char*, 5, 6, "(int plotID, float x, float y, bool setAdded = true;)" + "Add a data point to the given plot." + "@return") +{ + S32 plotID = dAtoi(argv[2]); + S32 pointAdded = 0; + char *retBuffer = Con::getReturnBuffer(32); + + if(plotID > object->MaxPlots) + { + Con::errorf("Invalid plotID."); + dSprintf(retBuffer, 32, "%d", -2); + return retBuffer; + } + + if(argc == 5) + { + pointAdded = object->addPlotPoint( plotID, Point2F(dAtof(argv[3]), dAtof(argv[4]))); + } else if(argc == 6) + { + pointAdded = object->addPlotPoint( plotID, Point2F(dAtof(argv[3]), dAtof(argv[4])), dAtob(argv[5])); + } + + + dSprintf(retBuffer, 32, "%d", pointAdded); + + return retBuffer; +} + +ConsoleMethod(GuiParticleGraphCtrl, insertPlotPoint, void, 6, 6, "(int plotID, int i, float x, float y)\n" + "Insert a data point to the given plot and plot position.\n" + "@param plotID The plot you want to access\n" + "@param i The data point.\n" + "@param x,y The plot position.\n" + "@return No return value.") +{ + S32 plotID = dAtoi(argv[2]); + if(plotID > object->MaxPlots) + { + Con::errorf("Invalid plotID."); + return; + } + object->insertPlotPoint( plotID, dAtoi(argv[3]), Point2F(dAtof(argv[4]), dAtof(argv[5]))); +} + +ConsoleMethod(GuiParticleGraphCtrl, changePlotPoint, const char*, 6, 6, "(int plotID, int i, float x, float y)" + "Change a data point to the given plot and plot position.\n" + "@param plotID The plot you want to access\n" + "@param i The data point.\n" + "@param x,y The plot position.\n" + "@return No return value.") +{ + S32 plotID = dAtoi(argv[2]); + if(plotID > object->MaxPlots) + { + Con::errorf("Invalid plotID."); + + char *retBuffer = Con::getReturnBuffer(64); + const S32 index = -1; + dSprintf(retBuffer, 64, "%d", index); + return retBuffer; + } + + char *retBuffer = Con::getReturnBuffer(64); + const S32 index = object->changePlotPoint( plotID, dAtoi(argv[3]), Point2F(dAtof(argv[4]), dAtof(argv[5]))); + dSprintf(retBuffer, 64, "%d", index); + return retBuffer; +} + +ConsoleMethod(GuiParticleGraphCtrl, getSelectedPlot, const char*, 2, 2, "() " + "Gets the selected Plot (a.k.a. graph).\n" + "@return The plot's ID.") +{ + char *retBuffer = Con::getReturnBuffer(32); + const S32 plot = object->getSelectedPlot(); + dSprintf(retBuffer, 32, "%d", plot); + return retBuffer; +} + +ConsoleMethod(GuiParticleGraphCtrl, getSelectedPoint, const char*, 2, 2, "()" + "Gets the selected Point on the Plot (a.k.a. graph)." + "@return The last selected point ID") +{ + char *retBuffer = Con::getReturnBuffer(32); + const S32 point = object->getSelectedPoint(); + dSprintf(retBuffer, 32, "%d", point); + return retBuffer; +} + +ConsoleMethod(GuiParticleGraphCtrl, isExistingPoint, const char*, 4, 4, "(int plotID, int samples)" + "@return Returns true or false whether or not the point in the plot passed is an existing point.") +{ + S32 plotID = dAtoi(argv[2]); + S32 samples = dAtoi(argv[3]); + + if(plotID > object->MaxPlots) + { + Con::errorf("Invalid plotID."); + } + if(samples > object->MaxDataPoints) + { + Con::errorf("Invalid sample."); + } + + char *retBuffer = Con::getReturnBuffer(32); + const bool isPoint = object->isExistingPoint(plotID, samples); + dSprintf(retBuffer, 32, "%d", isPoint); + return retBuffer; +} + +ConsoleMethod(GuiParticleGraphCtrl, getPlotPoint, const char*, 4, 4, "(int plotID, int samples)" + "Get a data point from the plot specified, samples from the start of the graph." + "@return The data point ID") +{ + S32 plotID = dAtoi(argv[2]); + S32 samples = dAtoi(argv[3]); + + if(plotID > object->MaxPlots) + { + Con::errorf("Invalid plotID."); + } + if(samples > object->MaxDataPoints) + { + Con::errorf("Invalid sample."); + } + + char *retBuffer = Con::getReturnBuffer(64); + const Point2F &pos = object->getPlotPoint(plotID, samples); + dSprintf(retBuffer, 64, "%f %f", pos.x, pos.y); + return retBuffer; +} + +ConsoleMethod(GuiParticleGraphCtrl, getPlotIndex, const char*, 5, 5, "(int plotID, float x, float y)\n" + "Gets the index of the point passed on the plotID passed (graph ID).\n" + "@param plotID The plot you wish to check.\n" + "@param x,y The coordinates of the point to get.\n" + "@return Returns the index of the point.\n") +{ + S32 plotID = dAtoi(argv[2]); + F32 x = dAtof(argv[3]); + F32 y = dAtof(argv[4]); + + if(plotID > object->MaxPlots) + { + Con::errorf("Invalid plotID."); + } + + char *retBuffer = Con::getReturnBuffer(32); + const S32 &index = object->getPlotIndex(plotID, x, y); + dSprintf(retBuffer, 32, "%d", index); + return retBuffer; +} + +ConsoleMethod(GuiParticleGraphCtrl, getGraphColor, const char*, 3, 3, "(int plotID)" + "Get the color of the graph passed." + "@return Returns the color of the graph as a string of RGB values formatted as \"R G B\"") +{ + S32 plotID = dAtoi(argv[2]); + + if(plotID > object->MaxPlots) + { + Con::errorf("Invalid plotID."); + } + + char *retBuffer = Con::getReturnBuffer(64); + const ColorF &color = object->getGraphColor(plotID); + dSprintf(retBuffer, 64, "%f %f %f", color.red, color.green, color.blue); + return retBuffer; +} + +ConsoleMethod(GuiParticleGraphCtrl, getGraphMin, const char*, 3, 3, "(int plotID) " + "Get the minimum values of the graph ranges.\n" + "@return Returns the minimum of the range formatted as \"x-min y-min\"") +{ + S32 plotID = dAtoi(argv[2]); + + if(plotID > object->MaxPlots) + { + Con::errorf("Invalid plotID."); + } + + char *retBuffer = Con::getReturnBuffer(64); + const Point2F graphMin = object->getGraphMin(plotID); + dSprintf(retBuffer, 64, "%f %f", graphMin.x, graphMin.y); + return retBuffer; +} + +ConsoleMethod(GuiParticleGraphCtrl, getGraphMax, const char*, 3, 3, "(int plotID) " + "Get the maximum values of the graph ranges.\n" + "@return Returns the maximum of the range formatted as \"x-max y-max\"") +{ + S32 plotID = dAtoi(argv[2]); + + if(plotID > object->MaxPlots) + { + Con::errorf("Invalid plotID."); + } + + char *retBuffer = Con::getReturnBuffer(64); + const Point2F graphMax = object->getGraphMax(plotID); + dSprintf(retBuffer, 64, "%f %f", graphMax.x, graphMax.y); + return retBuffer; +} + +ConsoleMethod(GuiParticleGraphCtrl, getGraphName, const char*, 3, 3, "(int plotID) " + "Get the name of the graph passed.\n" + "@return Returns the name of the plot") +{ + S32 plotID = dAtoi(argv[2]); + + if(plotID > object->MaxPlots) + { + Con::errorf("Invalid plotID."); + } + + char *retBuffer = Con::getReturnBuffer(64); + const StringTableEntry graphName = object->getGraphName(plotID); + dSprintf(retBuffer, 64, "%s", graphName); + return retBuffer; +} + +ConsoleMethod(GuiParticleGraphCtrl, setGraphMin, void, 5, 5, "(int plotID, float minX, float minY) " + "Set the min values of the graph of plotID.\n" + "@param plotID The plot to modify\n" + "@param minX,minY The minimum bound of the value range.\n" + "@return No return value.") +{ + S32 plotID = dAtoi(argv[2]); + + if(plotID > object->MaxPlots) + { + Con::errorf("Invalid plotID."); + return; + } + + object->setGraphMin(dAtoi(argv[2]), Point2F(dAtof(argv[3]), dAtof(argv[4]))); +} + +ConsoleMethod(GuiParticleGraphCtrl, setGraphMinX, void, 4, 4, "(int plotID, float minX) " + "Set the min X value of the graph of plotID.\n" + "@param plotID The plot to modify.\n" + "@param minX The minimum x value.\n" + "@return No return Value.") +{ + S32 plotID = dAtoi(argv[2]); + + if(plotID > object->MaxPlots) + { + Con::errorf("Invalid plotID."); + return; + } + + object->setGraphMinX(dAtoi(argv[2]), dAtof(argv[3])); +} + +ConsoleMethod(GuiParticleGraphCtrl, setGraphMinY, void, 4, 4, "(int plotID, float minY) " + "Set the min Y value of the graph of plotID." + "@param plotID The plot to modify.\n" + "@param minY The minimum y value.\n" + "@return No return Value.") +{ + S32 plotID = dAtoi(argv[2]); + + if(plotID > object->MaxPlots) + { + Con::errorf("Invalid plotID."); + return; + } + + object->setGraphMinY(dAtoi(argv[2]), dAtof(argv[3])); +} + +ConsoleMethod(GuiParticleGraphCtrl, setGraphMax, void, 5, 5, "(int plotID, float maxX, float maxY) " + "Set the max values of the graph of plotID." + "@param plotID The plot to modify\n" + "@param maxX,maxY The maximum bound of the value range.\n" + "@return No return value.") +{ + S32 plotID = dAtoi(argv[2]); + + if(plotID > object->MaxPlots) + { + Con::errorf("Invalid plotID."); + return; + } + + object->setGraphMax(dAtoi(argv[2]), Point2F(dAtof(argv[3]), dAtof(argv[4]))); +} + +ConsoleMethod(GuiParticleGraphCtrl, setGraphMaxX, void, 4, 4, "(int plotID, float maxX)" + "Set the max X value of the graph of plotID." + "@param plotID The plot to modify.\n" + "@param maxX The maximum x value.\n" + "@return No return Value.") +{ + S32 plotID = dAtoi(argv[2]); + + if(plotID > object->MaxPlots) + { + Con::errorf("Invalid plotID."); + return; + } + + object->setGraphMaxX(dAtoi(argv[2]), dAtof(argv[3])); +} + +ConsoleMethod(GuiParticleGraphCtrl, setGraphMaxY, void, 4, 4, "(int plotID, float maxY)" + "Set the max Y value of the graph of plotID." + "@param plotID The plot to modify.\n" + "@param maxY The maximum y value.\n" + "@return No return Value.") +{ + S32 plotID = dAtoi(argv[2]); + + if(plotID > object->MaxPlots) + { + Con::errorf("Invalid plotID."); + return; + } + + object->setGraphMaxY(dAtoi(argv[2]), dAtof(argv[3])); +} + +ConsoleMethod(GuiParticleGraphCtrl, setGraphHidden, void, 4, 4, "(int plotID, bool isHidden)" + "Set whether the graph number passed is hidden or not." + "@return No return value.") +{ + S32 plotID = dAtoi(argv[2]); + + if(plotID > object->MaxPlots) + { + Con::errorf("Invalid plotID."); + return; + } + + object->setGraphHidden(dAtoi(argv[2]), dAtob(argv[3])); +} + +ConsoleMethod(GuiParticleGraphCtrl, setAutoGraphMax, void, 3, 3, "(bool autoMax) " + "Set whether the max will automatically be set when adding points " + "(ie if you add a value over the current max, the max is increased to that value).\n" + "@return No return value.") +{ + object->setAutoGraphMax(dAtob(argv[2])); +} + +ConsoleMethod(GuiParticleGraphCtrl, setAutoRemove, void, 3, 3, "(bool autoRemove) " + "Set whether or not a point should be deleted when you drag another one over it." + "@return No return value.") +{ + object->setAutoRemove(dAtob(argv[2])); +} + +ConsoleMethod(GuiParticleGraphCtrl, setRenderAll, void, 3, 3, "(bool renderAll)" + "Set whether or not a position should be rendered on every point or just the last selected." + "@return No return value.") +{ + object->setRenderAll(dAtob(argv[2])); +} + +ConsoleMethod(GuiParticleGraphCtrl, setPointXMovementClamped, void, 3, 3, "(bool clamped)" + "Set whether the x position of the selected graph point should be clamped" + "@return No return value.") +{ + object->setPointXMovementClamped(dAtob(argv[2])); +} + +ConsoleMethod(GuiParticleGraphCtrl, setRenderGraphTooltip, void, 3, 3, "(bool renderGraphTooltip)" + "Set whether or not to render the graph tooltip." + "@return No return value.") +{ + object->setRenderGraphTooltip(dAtob(argv[2])); +} + +ConsoleMethod(GuiParticleGraphCtrl, setGraphName, void, 4, 4, "(int plotID, string graphName) " + "Set the name of the given plot.\n" + "@param plotID The plot to modify.\n" + "@param graphName The name to set on the plot.\n" + "@return No return value.") +{ + S32 plotID = dAtoi(argv[2]); + + if(plotID > object->MaxPlots) + { + Con::errorf("Invalid plotID."); + return; + } + + object->setGraphName(dAtoi(argv[2]), argv[3]); +} + +ConsoleMethod(GuiParticleGraphCtrl, resetSelectedPoint, void, 2, 2, "()" + "This will reset the currently selected point to nothing." + "@return No return value.") +{ + object->resetSelectedPoint(); +} diff --git a/Engine/source/gui/editor/guiParticleGraphCtrl.h b/Engine/source/gui/editor/guiParticleGraphCtrl.h new file mode 100644 index 000000000..e7ec5e079 --- /dev/null +++ b/Engine/source/gui/editor/guiParticleGraphCtrl.h @@ -0,0 +1,175 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIPARTICLEGRAPHCTRL_H_ +#define _GUIPARTICLEGRAPHCTRL_H_ + +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif +#ifndef _STRINGTABLE_H_ +#include "core/stringTable.h" +#endif + + +class GuiParticleGraphCtrl : public GuiControl +{ +private: + typedef GuiControl Parent; + +protected: + /// The color of the nuts. + ColorI mNutColor; + /// The outline color of the nuts. + ColorI mOutlineColor; + /// The rectangle size to check for clicks on nuts (default 8) which is RectI( pointClick - 4, pointClick - 4, 8, 8 ) + S32 mVertexClickSize; + + Point2I findHitNut( Point2I hitPoint ); + +public: + enum Constants { + MaxPlots = 32, + MaxDataPoints = 200 + }; + + enum GraphType { + Point, + Polyline, + Filled, + Bar + }; + + struct PlotInfo + { + ColorF mGraphColor; + Vector mGraphData; + Vector mNutList; + Point2F mGraphMax; + Point2F mGraphMin; + GraphType mGraphType; + StringTableEntry mGraphName; + bool mHidden; + F32 mGraphScale; + }; + + S32 mSelectedPlot; + S32 mSelectedPoint; + S32 mOriginalSelectedPoint; + S32 mLastSelectedPoint; + S32 mTooltipSelectedPlot; + S32 mAddedPointIndex; + bool mAutoMax; + bool mAutoRemove; + bool mRenderAllPoints; + bool mPointWasAdded; + bool mRenderGraphTooltip; + bool mRenderNextGraphTooltip; + bool mPointXMovementClamped; + Point2F mAddedPoint; + Point2F mLastMousePos; + + Point2I mCursorPos; + + PlotInfo mPlots[MaxPlots]; + + //creation methods + DECLARE_CONOBJECT(GuiParticleGraphCtrl); + DECLARE_CATEGORY( "Gui Editor" ); + + GuiParticleGraphCtrl(); + virtual ~GuiParticleGraphCtrl() { }; + + void onMouseMove(const GuiEvent &event); + void onMouseDown( const GuiEvent &event ); + void onMouseUp( const GuiEvent &event ); + void onMouseDragged( const GuiEvent &event ); + void onRightMouseDown( const GuiEvent &event ); + void onRightMouseUp( const GuiEvent &event ); + void onRightMouseDragged( const GuiEvent &event ); + + //Parental methods + bool onWake(); + + void onRender(Point2I offset, const RectI &updateRect); + bool renderGraphTooltip(Point2I cursorPos, StringTableEntry tooltip); + + // Graph interface + S32 addPlotPoint(S32 plotID, Point2F v, bool setAdded = true); + void insertPlotPoint(S32 plotID, S32 i, Point2F v); + S32 changePlotPoint(S32 plotID, S32 i, Point2F v); + S32 removePlotPoint(S32 plotID, S32 i); + Point2F convertToGraphCoord(S32 plotID, Point2I v); + + // Set Graph specific functions + void setGraphType(S32 plotID, const char *graphType); + void setSelectedPlot(S32 plotID); + void setSelectedPoint(S32 point); + void resetSelectedPoint(); + void setGraphHidden(S32 plotID, bool isHidden); + + // Set Global Options + void setAutoGraphMax(bool autoMax); + void setAutoRemove(bool autoRemove); + void setRenderAll(bool renderAll); + void setRenderGraphTooltip(bool renderGraphTooltip); + void setPointXMovementClamped(bool clamped); + + // Get Functions + Point2F getGraphExtent(S32 plotID); + ColorF getGraphColor(S32 plotID); + S32 getSelectedPlot(); + S32 getSelectedPoint(); + Point2F getPlotPoint(S32 plotID, S32 samples); + S32 getPlotIndex(S32 plotID, F32 x, F32 y); + Point2F getGraphMax(S32 plotID); + Point2F getGraphMin(S32 plotID); + + bool isExistingPoint(S32 plotID, S32 point); + + StringTableEntry getGraphName(S32 plotID); + + // Set Graph Min and MaxFunctions + void setGraphMin(S32 plotID, Point2F graphMin); + void setGraphMinX(S32 plotID, F32 graphMinX); + void setGraphMinY(S32 plotID, F32 graphMinY); + void setGraphMax(S32 plotID, Point2F graphMax); + void setGraphMaxX(S32 plotID, F32 graphMaxX); + void setGraphMaxY(S32 plotID, F32 graphMaxY); + + // Other set functions for graphs + void setGraphName(S32 plotID, StringTableEntry graphName); + + + // Clear Functions + void clearGraph(S32 plotID); + void clearAllGraphs(); + + // Selection nuts. + bool inNut( const Point2I &pt, S32 x, S32 y ); + void drawNut( const Point2I &nut, S32 size, ColorI &outlineColor, ColorI &nutColor ); +}; + +typedef GuiParticleGraphCtrl::GraphType GuiParticleGraphType; +DefineEnumType( GuiParticleGraphType ); + +#endif diff --git a/Engine/source/gui/editor/guiRectHandles.cpp b/Engine/source/gui/editor/guiRectHandles.cpp new file mode 100644 index 000000000..fa98ab3b9 --- /dev/null +++ b/Engine/source/gui/editor/guiRectHandles.cpp @@ -0,0 +1,318 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/console.h" +#include "console/consoleTypes.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxDrawUtil.h" + +#include "gui/editor/guiRectHandles.h" + +IMPLEMENT_CONOBJECT(GuiRectHandles); + +ConsoleDocClass( GuiRectHandles, + "@brief Draws a box with handles for the user to manipulate.\n\n" + "Editor use only.\n\n" + "@internal" +); + +//-------------------------------------------------------------------------- +GuiRectHandles::GuiRectHandles() : GuiControl() +{ + mHandleRect.set(0.0f, 0.0f, 1.0f, 1.0f); + mHandleSize = 10; + mUseCustomColor = false; + mHandleColor.set(100,100,100); + mHitHandle = 0; +} + +GuiRectHandles::~GuiRectHandles() +{ +} + +//-------------------------------------------------------------------------- +void GuiRectHandles::initPersistFields() +{ + addField("handleRect", TypeRectF, Offset(mHandleRect, GuiRectHandles), "RectF of handle's box." ); + addField("handleSize", TypeS32, Offset(mHandleSize, GuiRectHandles), "Size of handles in pixels." ); + addField("useCustomColor", TypeBool, Offset(mUseCustomColor, GuiRectHandles), "Use given custom color for handles." ); + addField("handleColor", TypeColorI, Offset(mHandleColor, GuiRectHandles), "Use given custom color for handles." ); + + Parent::initPersistFields(); +} + +//-------------------------------------------------------------------------- + +void GuiRectHandles::onMouseUp(const GuiEvent &event) +{ + mHitHandle = 0; +} + +void GuiRectHandles::onMouseDown(const GuiEvent &event) +{ + // The handles range from 0-1, so scale to fit within the + // control's bounds. + const Point2I& extent = getExtent(); + Point2I pos(extent.x*mHandleRect.point.x, extent.y*mHandleRect.point.y); + Point2I size(extent.x*mHandleRect.extent.x, extent.y*mHandleRect.extent.y); + RectI box(pos, size); + + Point2I localMousePoint = globalToLocalCoord(event.mousePoint); + + // Check if mouse is within handle rect + if(!box.pointInRect(localMousePoint)) + { + mHitHandle = 0; + return; + } + + Point2I normalizedMouse = localMousePoint - pos; + Point2I halfSize = size / 2; + S32 halfHandleSize = mHandleSize / 2; + if(normalizedMouse.y < mHandleSize) + { + // Top handles + if(normalizedMouse.x < mHandleSize) + mHitHandle = 1; + else if(normalizedMouse.x >= (size.x-mHandleSize)) + mHitHandle = 3; + else if(normalizedMouse.x >= (halfSize.x-halfHandleSize) && normalizedMouse.x < (halfSize.x+halfHandleSize)) + mHitHandle = 2; + } + else if(normalizedMouse.y >= (size.y-mHandleSize)) + { + // Bottom handles + if(normalizedMouse.x < mHandleSize) + mHitHandle = 7; + else if(normalizedMouse.x >= (size.x-mHandleSize)) + mHitHandle = 5; + else if(normalizedMouse.x >= (halfSize.x-halfHandleSize) && normalizedMouse.x < (halfSize.x+halfHandleSize)) + mHitHandle = 6; + } + else if(normalizedMouse.y >= (halfSize.y-halfHandleSize) && normalizedMouse.y < (halfSize.y+halfHandleSize)) + { + // Middle handles + if(normalizedMouse.x < mHandleSize) + mHitHandle = 8; + else if(normalizedMouse.x >= (size.x-mHandleSize)) + mHitHandle = 4; + else if(normalizedMouse.x >= (halfSize.x-halfHandleSize) && normalizedMouse.x < (halfSize.x+halfHandleSize)) + mHitHandle = 9; + } + + mHitPoint = localMousePoint; +} + +void GuiRectHandles::onMouseDragged(const GuiEvent &event) +{ + if(mHitHandle == 0) + return; + + // The handles range from 0-1, so scale to fit within the + // control's bounds. + const Point2I& extent = getExtent(); + + Point2I localMousePoint = globalToLocalCoord(event.mousePoint); + + Point2I diffI = localMousePoint - mHitPoint; + Point2F diffF(diffI.x/F32(extent.x), diffI.y/F32(extent.y)); + + RectF box(mHandleRect); + bool postMoveExtentX = false; + bool postMoveExtentY = false; + bool keepExtent = false; + + switch(mHitHandle) + { + case 1: + { + // Top left + box.point += diffF; + postMoveExtentX = true; + postMoveExtentY = true; + break; + } + + case 2: + { + // Top middle + box.point.y += diffF.y; + postMoveExtentY = true; + break; + } + + case 3: + { + // Top right + box.point.y += diffF.y; + box.extent.x += diffF.x; + postMoveExtentY = true; + break; + } + + case 4: + { + // Middle right + box.extent.x += diffF.x; + break; + } + + case 5: + { + // Bottom right + box.extent += diffF; + break; + } + + case 6: + { + // Bottom middle + box.extent.y += diffF.y; + break; + } + + case 7: + { + // Bottom left + box.point.x += diffF.x; + box.extent.y += diffF.y; + postMoveExtentX = true; + break; + } + + case 8: + { + // Middle left + box.point.x += diffF.x; + postMoveExtentX = true; + break; + } + + case 9: + { + // Centre + box.point += diffF; + keepExtent = true; + break; + } + + default: + break; + } + + // Position limits + if(box.point.x < 0.0f) + box.point.x = 0.0f; + else if(box.point.x > 1.0f) + box.point.x = 1.0f; + + if(box.point.y < 0.0f) + box.point.y = 0.0f; + else if(box.point.y > 1.0f) + box.point.y = 1.0f; + + // Move any extent to counter a change in handle position. Do this + // after the limits above to make sure the extent doesn't accidentally + // grow when the position is clamped. + if(postMoveExtentX) + box.extent.x += mHandleRect.point.x - box.point.x; + if(postMoveExtentY) + box.extent.y += mHandleRect.point.y - box.point.y; + + // Extent limits + if(box.extent.x < 0.0f) + box.extent.x = 0.0f; + else if(box.extent.x > 1.0f) + box.extent.x = 1.0f; + if(box.point.x+box.extent.x > 1.0f) + { + if(keepExtent) + box.point.x = 1.0f-box.extent.x; + else + box.extent.x = 1.0f-box.point.x; + } + + if(box.extent.y < 0.0f) + box.extent.y = 0.0f; + else if(box.extent.y > 1.0f) + box.extent.y = 1.0f; + if(box.point.y+box.extent.y > 1.0f) + { + if(keepExtent) + box.point.y = 1.0f-box.extent.y; + else + box.extent.y = 1.0f-box.point.y; + } + + mHandleRect = box; + mHitPoint = localMousePoint; + + if( isMethod( "onHandleRectChange" ) ) + Con::executef(this, "onHandleRectChange" ); +} + +//-------------------------------------------------------------------------- +void GuiRectHandles::onRender(Point2I offset, const RectI &updateRect) +{ + Parent::onRender( offset, updateRect ); + + ColorI handleColor = mProfile->mBorderColor; + if(mUseCustomColor) + handleColor = mHandleColor; + + // The handles range from 0-1, so scale to fit within the + // control's bounds. + const Point2I& extent = getExtent(); + Point2I pos(extent.x*mHandleRect.point.x, extent.y*mHandleRect.point.y); + Point2I size(extent.x*mHandleRect.extent.x, extent.y*mHandleRect.extent.y); + RectI box(offset+pos, size); + + // Draw border + GFX->getDrawUtil()->drawRect(box, handleColor); + + // Draw each handle + Point2I handleSize(mHandleSize, mHandleSize); + RectI handleRect(box.point, handleSize); + GFX->getDrawUtil()->drawRectFill(handleRect, handleColor); // Upper left + handleRect.point = Point2I(box.point.x+size.x-handleSize.x, box.point.y); + GFX->getDrawUtil()->drawRectFill(handleRect, handleColor); // Upper right + handleRect.point = Point2I(box.point.x, box.point.y+size.y-handleSize.y); + GFX->getDrawUtil()->drawRectFill(handleRect, handleColor); // Lower left + handleRect.point = Point2I(box.point.x+size.x-handleSize.x, box.point.y+size.y-handleSize.y); + GFX->getDrawUtil()->drawRectFill(handleRect, handleColor); // Lower right + + Point2I halfSize = size / 2; + Point2I halfHandleSize = handleSize / 2; + handleRect.point = Point2I(box.point.x+halfSize.x-halfHandleSize.x, box.point.y); + GFX->getDrawUtil()->drawRectFill(handleRect, handleColor); // Upper middle + handleRect.point = Point2I(box.point.x+halfSize.x-halfHandleSize.x, box.point.y+size.y-handleSize.y); + GFX->getDrawUtil()->drawRectFill(handleRect, handleColor); // Lower middle + handleRect.point = Point2I(box.point.x, box.point.y+halfSize.y-halfHandleSize.y); + GFX->getDrawUtil()->drawRectFill(handleRect, handleColor); // Left middle + handleRect.point = Point2I(box.point.x+size.x-handleSize.x, box.point.y+halfSize.y-halfHandleSize.y); + GFX->getDrawUtil()->drawRectFill(handleRect, handleColor); // Right middle + + handleRect.point = Point2I(box.point.x+halfSize.x-halfHandleSize.x, box.point.y+halfSize.y-halfHandleSize.y); + GFX->getDrawUtil()->drawRectFill(handleRect, handleColor); // Middle + + renderChildControls(offset, updateRect); +} diff --git a/Engine/source/gui/editor/guiRectHandles.h b/Engine/source/gui/editor/guiRectHandles.h new file mode 100644 index 000000000..b9a3f7551 --- /dev/null +++ b/Engine/source/gui/editor/guiRectHandles.h @@ -0,0 +1,59 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _GUIRECTHANDLES_H_ +#define _GUIRECTHANDLES_H_ + +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif + +class GuiRectHandles : public GuiControl +{ +private: + typedef GuiControl Parent; + +protected: + RectF mHandleRect; + S32 mHandleSize; + bool mUseCustomColor; + ColorI mHandleColor; + S32 mHitHandle; // 0 = none, 1-8 = clockwise circle starting upper left, 9 = centre + Point2I mHitPoint; + +public: + DECLARE_CONOBJECT(GuiRectHandles); + DECLARE_CATEGORY( "Gui Other" ); + DECLARE_DESCRIPTION( "Draws a box with handles for the user to manipulate."); + + GuiRectHandles(); + virtual ~GuiRectHandles(); + + static void initPersistFields(); + + virtual void onMouseUp(const GuiEvent &event); + virtual void onMouseDown(const GuiEvent &event); + virtual void onMouseDragged(const GuiEvent &event); + + virtual void onRender(Point2I offset, const RectI &updateRect); +}; + +#endif diff --git a/Engine/source/gui/editor/guiSeparatorCtrl.cpp b/Engine/source/gui/editor/guiSeparatorCtrl.cpp new file mode 100644 index 000000000..98eb41761 --- /dev/null +++ b/Engine/source/gui/editor/guiSeparatorCtrl.cpp @@ -0,0 +1,142 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/editor/guiSeparatorCtrl.h" + +#include "gfx/gfxDevice.h" +#include "gfx/gfxDrawUtil.h" +#include "console/console.h" +#include "console/consoleTypes.h" +#include "gui/core/guiCanvas.h" +#include "gui/core/guiDefaultControlRender.h" + +IMPLEMENT_CONOBJECT(GuiSeparatorCtrl); + +ConsoleDocClass( GuiSeparatorCtrl, + "@brief A control that renders a horizontal or vertical separator with " + "an optional text label (horizontal only)\n\n" + + "@tsexample\n" + "new GuiSeparatorCtrl()\n" + "{\n" + " profile = \"GuiDefaultProfile\";\n" + " position = \"505 0\";\n" + " extent = \"10 17\";\n" + " minExtent = \"10 17\";\n" + " canSave = \"1\";\n" + " visible = \"1\";\n" + " horizSizing = \"left\";\n" + "};\n" + "@endtsexample\n\n" + + "@ingroup GuiControls\n"); + +ImplementEnumType( GuiSeparatorType, + "GuiSeparatorCtrl orientations\n\n" + "@ingroup GuiControls" ) + { GuiSeparatorCtrl::separatorTypeVertical, "Vertical" }, + { GuiSeparatorCtrl::separatorTypeHorizontal,"Horizontal" } +EndImplementEnumType; + + +//-------------------------------------------------------------------------- +GuiSeparatorCtrl::GuiSeparatorCtrl() : GuiControl() +{ + mInvisible = false; + mTextLeftMargin = 0; + mMargin = 2; + setExtent( 12, 35 ); + mSeparatorType = GuiSeparatorCtrl::separatorTypeVertical; +} + +//-------------------------------------------------------------------------- +void GuiSeparatorCtrl::initPersistFields() +{ + addField("caption", TypeRealString, Offset(mText, GuiSeparatorCtrl), + "Optional text label to display." ); + addField("type", TYPEID< separatorTypeOptions >(), Offset(mSeparatorType, GuiSeparatorCtrl), + "Orientation of separator." ); + addField("borderMargin", TypeS32, Offset(mMargin, GuiSeparatorCtrl)); + addField("invisible", TypeBool, Offset(mInvisible, GuiSeparatorCtrl));// Nonsense. Should use GuiControl's visibility. + addField("leftMargin", TypeS32, Offset(mTextLeftMargin, GuiSeparatorCtrl), + "Left margin of text label." ); + + Parent::initPersistFields(); +} + +//-------------------------------------------------------------------------- +void GuiSeparatorCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + Parent::onRender( offset, updateRect ); + + if( mInvisible ) + return; + + if( mText.isNotEmpty() && mSeparatorType != separatorTypeVertical ) + { + // If text is present and we have a left margin, then draw some separator, then the + // text, and then the rest of the separator. + + S32 posx = offset.x + mMargin; + S32 fontheight = mProfile->mFont->getHeight(); + S32 seppos = (fontheight - 2) / 2 + offset.y; + if( mTextLeftMargin > 0 ) + { + RectI rect( Point2I( posx, seppos ), Point2I( mTextLeftMargin, 2 ) ); + renderSlightlyLoweredBox(rect, mProfile); + posx += mTextLeftMargin; + } + + GFX->getDrawUtil()->setBitmapModulation( mProfile->mFontColor ); + posx = GFX->getDrawUtil()->drawText(mProfile->mFont, Point2I(posx,offset.y), mText, mProfile->mFontColors); + + RectI rect( Point2I( posx, seppos ), Point2I( getWidth() - posx + offset.x, 2 ) ); + + // Space text and separator a bit apart at right end. + + rect.point.x += 2; + rect.extent.x -= 2; + + if( rect.extent.x > 0 ) + renderSlightlyLoweredBox( rect, mProfile ); + } + else + { + if( mSeparatorType == separatorTypeHorizontal ) + { + S32 seppos = getHeight() / 2 + offset.y; + RectI rect(Point2I(offset.x + mMargin ,seppos),Point2I(getWidth() - (mMargin * 2),2)); + renderSlightlyLoweredBox(rect, mProfile); + } + else + { + S32 seppos = getWidth() / 2 + offset.x; + RectI rect(Point2I(seppos, offset.y + mMargin),Point2I(2, getHeight() - (mMargin * 2))); + renderSlightlyLoweredBox(rect, mProfile); + } + } + + renderChildControls(offset, updateRect); +} + + diff --git a/Engine/source/gui/editor/guiSeparatorCtrl.h b/Engine/source/gui/editor/guiSeparatorCtrl.h new file mode 100644 index 000000000..beca62033 --- /dev/null +++ b/Engine/source/gui/editor/guiSeparatorCtrl.h @@ -0,0 +1,67 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _GUISEPARATORCTRL_H_ +#define _GUISEPARATORCTRL_H_ + +#ifndef _GUICONTROL_H_ + #include "gui/core/guiControl.h" +#endif +#ifndef _DYNAMIC_CONSOLETYPES_H_ + #include "console/dynamicTypes.h" +#endif + + +/// Renders a separator line with optional text. +class GuiSeparatorCtrl : public GuiControl +{ +private: + typedef GuiControl Parent; + +public: + bool mInvisible; + String mText; + S32 mTextLeftMargin; + S32 mMargin; + S32 mSeparatorType; + + enum separatorTypeOptions + { + separatorTypeVertical = 0, ///< Draw Vertical Separator + separatorTypeHorizontal ///< Horizontal Separator + }; + + //creation methods + DECLARE_CONOBJECT(GuiSeparatorCtrl); + DECLARE_CATEGORY( "Gui Other" ); + DECLARE_DESCRIPTION( "A control that renders a horizontal or vertical separator with\n" + "an optional text label (horizontal only). "); + GuiSeparatorCtrl(); + + static void initPersistFields(); + + void onRender(Point2I offset, const RectI &updateRect); +}; + +typedef GuiSeparatorCtrl::separatorTypeOptions GuiSeparatorType; +DefineEnumType( GuiSeparatorType ); + +#endif diff --git a/Engine/source/gui/editor/guiShapeEdPreview.cpp b/Engine/source/gui/editor/guiShapeEdPreview.cpp new file mode 100644 index 000000000..128266f6d --- /dev/null +++ b/Engine/source/gui/editor/guiShapeEdPreview.cpp @@ -0,0 +1,1861 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/consoleTypes.h" +#include "console/console.h" +#include "console/engineAPI.h" +#include "gui/core/guiCanvas.h" +#include "gui/editor/guiShapeEdPreview.h" +#include "renderInstance/renderPassManager.h" +#include "lighting/lightManager.h" +#include "lighting/lightInfo.h" +#include "core/resourceManager.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxDrawUtil.h" +#include "collision/concretePolyList.h" + +#ifdef TORQUE_COLLADA + #include "collision/optimizedPolyList.h" + #include "ts/collada/colladaUtils.h" +#endif + +static const F32 sMoveScaler = 50.0f; +static const F32 sZoomScaler = 200.0f; +static const int sNodeRectSize = 16; + +IMPLEMENT_CONOBJECT( GuiShapeEdPreview ); + +ConsoleDocClass( GuiShapeEdPreview, + "@brief This control provides the 3D view for the Shape Editor tool, and is " + "not intended for general purpose use.\n" + + "@ingroup GuiControls\n" + "@internal" +); + +IMPLEMENT_CALLBACK( GuiShapeEdPreview, onThreadPosChanged, void, ( F32 pos, bool inTransition ), ( pos, inTransition), + "Called when the position of the active thread has changed, such as during " + "playback." ); + + +GuiShapeEdPreview::GuiShapeEdPreview() +: mOrbitDist( 5.0f ), + mMoveSpeed ( 1.0f ), + mZoomSpeed ( 1.0f ), + mModel( NULL ), + mGridDimension( 30, 30 ), + mRenderGhost( false ), + mRenderNodes( false ), + mRenderBounds( false ), + mRenderObjBox( false ), + mRenderMounts( true ), + mSunDiffuseColor( 255, 255, 255, 255 ), + mSunAmbientColor( 140, 140, 140, 255 ), + mSelectedNode( -1 ), + mHoverNode( -1 ), + mSelectedObject( -1 ), + mSelectedObjDetail( 0 ), + mUsingAxisGizmo( false ), + mGizmoDragID( 0 ), + mEditingSun( false ), + mTimeScale( 1.0f ), + mActiveThread( -1 ), + mLastRenderTime( 0 ), + mFakeSun( NULL ), + mSunRot( 45.0f, 0, 135.0f ), + mCameraRot( 0, 0, 3.9f ), + mRenderCameraAxes( false ), + mOrbitPos( 0, 0, 0 ), + mFixedDetail( true ), + mCurrentDL( 0 ), + mDetailSize( 0 ), + mDetailPolys( 0 ), + mPixelSize( 0 ), + mNumMaterials( 0 ), + mNumDrawCalls( 0 ), + mNumBones( 0 ), + mNumWeights( 0 ), + mColMeshes( 0 ), + mColPolys( 0 ) +{ + mActive = true; + + // By default don't do dynamic reflection + // updates for this viewport. + mReflectPriority = 0.0f; +} + +GuiShapeEdPreview::~GuiShapeEdPreview() +{ + SAFE_DELETE( mModel ); + SAFE_DELETE( mFakeSun ); +} + +void GuiShapeEdPreview::initPersistFields() +{ + addGroup( "Rendering" ); + addField( "editSun", TypeBool, Offset( mEditingSun, GuiShapeEdPreview ), + "If true, dragging the gizmo will rotate the sun direction" ); + addField( "selectedNode", TypeS32, Offset( mSelectedNode, GuiShapeEdPreview ), + "Index of the selected node, or -1 if none" ); + addField( "selectedObject", TypeS32, Offset( mSelectedObject, GuiShapeEdPreview ), + "Index of the selected object, or -1 if none" ); + addField( "selectedObjDetail", TypeS32, Offset( mSelectedObjDetail, GuiShapeEdPreview ), + "Index of the selected object detail mesh, or 0 if none" ); + addField( "gridDimension", TypePoint2I, Offset( mGridDimension, GuiShapeEdPreview ), + "Grid dimensions (number of rows and columns) in the form \"rows cols\"" ); + addField( "renderGrid", TypeBool, Offset( mRenderGridPlane, EditTSCtrl ), + "Flag indicating whether to draw the grid" ); + addField( "renderNodes", TypeBool, Offset( mRenderNodes, GuiShapeEdPreview ), + "Flag indicating whether to render the shape nodes" ); + addField( "renderGhost", TypeBool, Offset( mRenderGhost, GuiShapeEdPreview ), + "Flag indicating whether to render the shape in 'ghost' mode (transparent)" ); + addField( "renderBounds", TypeBool, Offset( mRenderBounds, GuiShapeEdPreview ), + "Flag indicating whether to render the shape bounding box" ); + addField( "renderObjBox", TypeBool, Offset( mRenderObjBox, GuiShapeEdPreview ), + "Flag indicating whether to render the selected object's bounding box" ); + addField( "renderColMeshes", TypeBool, Offset( mRenderColMeshes, GuiShapeEdPreview ), + "Flag indicating whether to render the shape's collision geometry" ); + addField( "renderMounts", TypeBool, Offset( mRenderMounts, GuiShapeEdPreview ), + "Flag indicating whether to render mounted objects" ); + endGroup( "Rendering" ); + + addGroup( "Sun" ); + addProtectedField( "sunDiffuse", TypeColorI, Offset( mSunDiffuseColor, GuiShapeEdPreview ), &setFieldSunDiffuse, &defaultProtectedGetFn, + "Ambient color for the sun" ); + addProtectedField( "sunAmbient", TypeColorI, Offset( mSunAmbientColor, GuiShapeEdPreview ), &setFieldSunAmbient, &defaultProtectedGetFn, + "Diffuse color for the sun" ); + addProtectedField( "sunAngleX", TypeF32, Offset( mSunRot.x, GuiShapeEdPreview ), &setFieldSunAngleX, &defaultProtectedGetFn, + "X-axis rotation angle for the sun" ); + addProtectedField( "sunAngleZ", TypeF32, Offset( mSunRot.z, GuiShapeEdPreview ), &setFieldSunAngleZ, &defaultProtectedGetFn, + "Z-axis rotation angle for the sun" ); + endGroup( "Sun" ); + + addGroup( "Animation" ); + addField( "activeThread", TypeS32, Offset( mActiveThread, GuiShapeEdPreview ), + "Index of the active thread, or -1 if none" ); + addProtectedField( "threadPos", TypeF32, NULL, &setFieldThreadPos, &getFieldThreadPos, + "Current position of the active thread (0-1)" ); + addProtectedField( "threadDirection", TypeS32, NULL, &setFieldThreadDir, &getFieldThreadDir, + "Playback direction of the active thread" ); + addProtectedField( "threadPingPong", TypeBool, NULL, &setFieldThreadPingPong, &getFieldThreadPingPong, + "'PingPong' mode of the active thread" ); + endGroup( "Animation" ); + + addGroup( "Detail Stats" ); + addField( "fixedDetail", TypeBool, Offset( mFixedDetail, GuiShapeEdPreview ), + "If false, the current detail is selected based on camera distance" ); + addField( "orbitDist", TypeF32, Offset( mOrbitDist, GuiShapeEdPreview ), + "The current distance from the camera to the model" ); + addProtectedField( "currentDL", TypeS32, Offset( mCurrentDL, GuiShapeEdPreview ), &setFieldCurrentDL, &defaultProtectedGetFn, + "The current detail level" ); + addProtectedField( "detailSize", TypeS32, Offset( mDetailSize, GuiShapeEdPreview ), &defaultProtectedSetFn, &defaultProtectedGetFn, + "The size of the current detail" ); + addProtectedField( "detailPolys", TypeS32, Offset( mDetailPolys, GuiShapeEdPreview ), &defaultProtectedSetFn, &defaultProtectedGetFn, + "Number of polygons in the current detail" ); + addProtectedField( "pixelSize", TypeF32, Offset( mPixelSize, GuiShapeEdPreview ), &defaultProtectedSetFn, &defaultProtectedGetFn, + "The current pixel size of the model" ); + addProtectedField( "numMaterials", TypeS32, Offset( mNumMaterials, GuiShapeEdPreview ), &defaultProtectedSetFn, &defaultProtectedGetFn, + "The number of materials in the current detail level" ); + addProtectedField( "numDrawCalls", TypeS32, Offset( mNumDrawCalls, GuiShapeEdPreview ), &defaultProtectedSetFn, &defaultProtectedGetFn, + "The number of draw calls in the current detail level" ); + addProtectedField( "numBones", TypeS32, Offset( mNumBones, GuiShapeEdPreview ), &defaultProtectedSetFn, &defaultProtectedGetFn, + "The number of bones in the current detail level (skins only)" ); + addProtectedField( "numWeights", TypeS32, Offset( mNumWeights, GuiShapeEdPreview ), &defaultProtectedSetFn, &defaultProtectedGetFn, + "The number of vertex weights in the current detail level (skins only)" ); + addProtectedField( "colMeshes", TypeS32, Offset( mColMeshes, GuiShapeEdPreview ), &defaultProtectedSetFn, &defaultProtectedGetFn, + "The number of collision meshes in the shape" ); + addProtectedField( "colPolys", TypeS32, Offset( mColPolys, GuiShapeEdPreview ), &defaultProtectedSetFn, &defaultProtectedGetFn, + "The total number of collision polygons (all meshes) in the shape" ); + endGroup( "Detail Stats" ); + + Parent::initPersistFields(); +} + +bool GuiShapeEdPreview::setFieldCurrentDL( void *object, const char *index, const char *data ) +{ + GuiShapeEdPreview* gui = static_cast( object ); + if ( gui ) + gui->setCurrentDetail( mFloor( dAtof( data ) + 0.5f ) ); + return false; +} + +bool GuiShapeEdPreview::setFieldSunDiffuse( void *object, const char *index, const char *data ) +{ + GuiShapeEdPreview* gui = static_cast( object ); + if ( gui ) + { + Con::setData( TypeColorI, &gui->mSunDiffuseColor, 0, 1, &data ); + gui->updateSun(); + } + return false; +} + +bool GuiShapeEdPreview::setFieldSunAmbient( void *object, const char *index, const char *data ) +{ + GuiShapeEdPreview* gui = static_cast( object ); + if ( gui ) + { + Con::setData( TypeColorI, &gui->mSunAmbientColor, 0, 1, &data ); + gui->updateSun(); + } + return false; +} + +bool GuiShapeEdPreview::setFieldSunAngleX( void *object, const char *index, const char *data ) +{ + GuiShapeEdPreview* gui = static_cast( object ); + if ( gui ) + { + Con::setData( TypeF32, &gui->mSunRot.x, 0, 1, &data ); + gui->updateSun(); + } + return false; +} + +bool GuiShapeEdPreview::setFieldSunAngleZ( void *object, const char *index, const char *data ) +{ + GuiShapeEdPreview* gui = static_cast( object ); + if ( gui ) + { + Con::setData( TypeF32, &gui->mSunRot.z, 0, 1, &data ); + gui->updateSun(); + } + return false; +} + +bool GuiShapeEdPreview::setFieldThreadPos( void *object, const char *index, const char *data ) +{ + GuiShapeEdPreview* gui = static_cast( object ); + if ( gui && ( gui->mActiveThread >= 0 ) && gui->mThreads[gui->mActiveThread].key ) + gui->mModel->setPos( gui->mThreads[gui->mActiveThread].key, dAtof( data ) ); + return false; +} + +const char *GuiShapeEdPreview::getFieldThreadPos( void *object, const char *data ) +{ + GuiShapeEdPreview* gui = static_cast( object ); + if ( gui && ( gui->mActiveThread >= 0 ) && gui->mThreads[gui->mActiveThread].key ) + return Con::getFloatArg( gui->mModel->getPos( gui->mThreads[gui->mActiveThread].key ) ); + else + return "0"; +} + +bool GuiShapeEdPreview::setFieldThreadDir( void *object, const char *index, const char *data ) +{ + GuiShapeEdPreview* gui = static_cast( object ); + if ( gui && ( gui->mActiveThread >= 0 ) ) + { + Thread& thread = gui->mThreads[gui->mActiveThread]; + Con::setData( TypeS32, &(thread.direction), 0, 1, &data ); + if ( thread.key ) + gui->mModel->setTimeScale( thread.key, gui->mTimeScale * thread.direction ); + } + return false; +} + +const char *GuiShapeEdPreview::getFieldThreadDir( void *object, const char *data ) +{ + GuiShapeEdPreview* gui = static_cast( object ); + if ( gui && ( gui->mActiveThread >= 0 ) ) + return Con::getIntArg( gui->mThreads[gui->mActiveThread].direction ); + else + return "0"; +} + +bool GuiShapeEdPreview::setFieldThreadPingPong( void *object, const char *index, const char *data ) +{ + GuiShapeEdPreview* gui = static_cast( object ); + if ( gui && ( gui->mActiveThread >= 0 ) ) + Con::setData( TypeBool, &(gui->mThreads[gui->mActiveThread].pingpong), 0, 1, &data ); + return false; +} + +const char *GuiShapeEdPreview::getFieldThreadPingPong( void *object, const char *data ) +{ + GuiShapeEdPreview* gui = static_cast( object ); + if ( gui && ( gui->mActiveThread >= 0 ) ) + return Con::getIntArg( gui->mThreads[gui->mActiveThread].pingpong ); + else + return "0"; +} + + +bool GuiShapeEdPreview::onWake() +{ + if (!Parent::onWake()) + return false; + + if (!mFakeSun ) + mFakeSun = LIGHTMGR->createLightInfo(); + + mFakeSun->setRange( 2000000.0f ); + updateSun(); + + mGizmoProfile->mode = MoveMode; + + return( true ); +} + +void GuiShapeEdPreview::setDisplayType( S32 type ) +{ + Parent::setDisplayType( type ); + mOrthoCamTrans.set( 0, 0, 0 ); +} + +//----------------------------------------------------------------------------- + +void GuiShapeEdPreview::setCurrentDetail(S32 dl) +{ + if ( mModel ) + { + S32 smallest = mModel->getShape()->mSmallestVisibleDL; + mModel->getShape()->mSmallestVisibleDL = mModel->getShape()->details.size()-1; + mModel->setCurrentDetail( dl ); + mModel->getShape()->mSmallestVisibleDL = smallest; + + // Match the camera distance to this detail if necessary + //@todo if ( !gui->mFixedDetail ) + } +} + +bool GuiShapeEdPreview::setObjectModel(const char* modelName) +{ + SAFE_DELETE( mModel ); + unmountAll(); + mThreads.clear(); + mActiveThread = -1; + + if (modelName && modelName[0]) + { + Resource model = ResourceManager::get().load( modelName ); + if (! bool( model )) + { + Con::warnf( avar("GuiShapeEdPreview: Failed to load model %s. Please check your model name and load a valid model.", modelName )); + return false; + } + + mModel = new TSShapeInstance( model, true ); + AssertFatal( mModel, avar("GuiShapeEdPreview: Failed to load model %s. Please check your model name and load a valid model.", modelName )); + + // Initialize camera values: + mOrbitPos = mModel->getShape()->center; + + // Set camera move and zoom speed according to model size + mMoveSpeed = mModel->getShape()->radius / sMoveScaler; + mZoomSpeed = mModel->getShape()->radius / sZoomScaler; + + // Reset node selection + mHoverNode = -1; + mSelectedNode = -1; + mSelectedObject = -1; + mSelectedObjDetail = 0; + mProjectedNodes.setSize( mModel->getShape()->nodes.size() ); + + // Reset detail stats + mCurrentDL = 0; + + // the first time recording + mLastRenderTime = Platform::getVirtualMilliseconds(); + } + + return true; +} + +void GuiShapeEdPreview::addThread() +{ + if ( mModel ) + { + mThreads.increment(); + if ( mActiveThread == -1 ) + mActiveThread = 0; + } +} + +void GuiShapeEdPreview::removeThread(S32 slot) +{ + if ( slot < mThreads.size() ) + { + if ( mThreads[slot].key ) + mModel->destroyThread( mThreads[slot].key ); + mThreads.erase( slot ); + + if ( mActiveThread >= mThreads.size() ) + mActiveThread = mThreads.size() - 1; + } +} + +void GuiShapeEdPreview::setTimeScale( F32 scale ) +{ + // Update time scale for all threads + mTimeScale = scale; + for ( S32 i = 0; i < mThreads.size(); i++ ) + { + if ( mThreads[i].key ) + mModel->setTimeScale( mThreads[i].key, mTimeScale * mThreads[i].direction ); + } +} + +void GuiShapeEdPreview::setActiveThreadSequence(const char* seqName, F32 duration, F32 pos, bool play) +{ + if ( mActiveThread == -1 ) + return; + + setThreadSequence(mThreads[mActiveThread], mModel, seqName, duration, pos, play); +} + +void GuiShapeEdPreview::setThreadSequence(GuiShapeEdPreview::Thread& thread, TSShapeInstance* shape, const char* seqName, F32 duration, F32 pos, bool play) +{ + thread.seqName = seqName; + + S32 seq = shape->getShape()->findSequence( thread.seqName ); + if ( thread.key && ( shape->getSequence(thread.key) == seq ) ) + return; + + if ( seq == -1 ) + { + // This thread is now set to an invalid sequence, so the key must be + // removed, but we keep the thread info around in case the user changes + // back to a valid sequence + if ( thread.key ) + { + shape->destroyThread( thread.key ); + thread.key = NULL; + } + } + else + { + // Add a TSThread key if one does not already exist + if ( !thread.key ) + { + thread.key = shape->addThread(); + shape->setTimeScale( thread.key, mTimeScale * thread.direction ); + } + + // Transition to slider or synched position? + if ( pos == -1.0f ) + pos = shape->getPos( thread.key ); + + if ( duration == 0.0f ) + { + // No transition => go straight to new sequence + shape->setSequence( thread.key, seq, pos ); + } + else + { + // Get the current position if transitioning to the sync position + shape->setTimeScale( thread.key, thread.direction >= 0 ? 1 : -1 ); + shape->transitionToSequence( thread.key, seq, pos, duration, play ); + shape->setTimeScale( thread.key, mTimeScale * thread.direction ); + } + } +} + +const char* GuiShapeEdPreview::getThreadSequence() const +{ + return ( mActiveThread >= 0 ) ? mThreads[mActiveThread].seqName : ""; +} + +void GuiShapeEdPreview::refreshThreadSequences() +{ + S32 oldActive = mActiveThread; + + for ( S32 i = 0; i < mThreads.size(); i++ ) + { + Thread& thread = mThreads[i]; + if ( !thread.key ) + continue; + + // Detect changed (or removed) sequence indices + if ( mModel->getSequence(thread.key) != mModel->getShape()->findSequence( thread.seqName ) ) + { + mActiveThread = i; + setThreadSequence( thread, mModel, thread.seqName, 0.0f, mModel->getPos( thread.key ), false ); + } + } + + mActiveThread = oldActive; +} + +//----------------------------------------------------------------------------- +// MOUNTING + +bool GuiShapeEdPreview::mountShape(const char* modelName, const char* nodeName, const char* mountType, S32 slot) +{ + if ( !modelName || !modelName[0] ) + return false; + + Resource model = ResourceManager::get().load( modelName ); + if ( !bool( model ) ) + return false; + + TSShapeInstance* tsi = new TSShapeInstance( model, true ); + if ( !tsi ) + return false; + + if ( slot == -1 ) + { + slot = mMounts.size(); + mMounts.push_back( new MountedShape ); + } + else + { + // Check if we are switching shapes + if ( mMounts[slot]->mShape->getShape() != tsi->getShape() ) + { + delete mMounts[slot]->mShape; + mMounts[slot]->mShape = NULL; + mMounts[slot]->mThread.init(); + } + else + { + // Keep using the existing shape + delete tsi; + tsi = mMounts[slot]->mShape; + } + } + + MountedShape* mount = mMounts[slot]; + mount->mShape = tsi; + + if ( dStrEqual( mountType, "Wheel" ) ) + mount->mType = MountedShape::Wheel; + else if ( dStrEqual( mountType, "Image" ) ) + mount->mType = MountedShape::Image; + else + mount->mType = MountedShape::Object; + + setMountNode( slot, nodeName); + + return true; +} + +void GuiShapeEdPreview::setMountNode(S32 mountSlot, const char* nodeName) +{ + if ( mountSlot < mMounts.size() ) + { + MountedShape* mount = mMounts[mountSlot]; + + mount->mNode = mModel ? mModel->getShape()->findNode( nodeName ) : -1; + mount->mTransform.identity(); + + switch ( mount->mType ) + { + case MountedShape::Image: + { + // Mount point is either the node called 'mountPoint' or the origin + S32 node = mount->mShape->getShape()->findNode( "mountPoint" ); + if ( node != -1 ) + { + mount->mShape->getShape()->getNodeWorldTransform( node, &mount->mTransform ); + mount->mTransform.inverse(); + } + } + break; + + case MountedShape::Wheel: + // Rotate shape according to node's x position (left or right) + { + F32 rotAngle = M_PI_F/2; + if ( mount->mNode != -1 ) + { + MatrixF hubMat; + mModel->getShape()->getNodeWorldTransform( mount->mNode, &hubMat ); + if ( hubMat.getPosition().x < 0 ) + rotAngle = -M_PI_F/2; + } + mount->mTransform.set( EulerF( 0, 0, rotAngle ) ); + } + break; + + default: + // No mount transform (use origin) + break; + } + } +} + +const char* GuiShapeEdPreview::getMountThreadSequence(S32 mountSlot) const +{ + if ( mountSlot < mMounts.size() ) + { + MountedShape* mount = mMounts[mountSlot]; + return mount->mThread.seqName; + } + else + return ""; +} + +void GuiShapeEdPreview::setMountThreadSequence(S32 mountSlot, const char* seqName) +{ + if ( mountSlot < mMounts.size() ) + { + MountedShape* mount = mMounts[mountSlot]; + setThreadSequence( mount->mThread, mount->mShape, seqName ); + } +} + +F32 GuiShapeEdPreview::getMountThreadPos(S32 mountSlot) const +{ + if ( mountSlot < mMounts.size() ) + { + MountedShape* mount = mMounts[mountSlot]; + if ( mount->mThread.key ) + return mount->mShape->getPos( mount->mThread.key ); + } + return 0; +} + +void GuiShapeEdPreview::setMountThreadPos(S32 mountSlot, F32 pos) +{ + if ( mountSlot < mMounts.size() ) + { + MountedShape* mount = mMounts[mountSlot]; + if ( mount->mThread.key ) + mount->mShape->setPos( mount->mThread.key, pos ); + } +} + +F32 GuiShapeEdPreview::getMountThreadDir(S32 mountSlot) const +{ + if ( mountSlot < mMounts.size() ) + { + MountedShape* mount = mMounts[mountSlot]; + return mount->mThread.direction; + } + return 0; +} + +void GuiShapeEdPreview::setMountThreadDir(S32 mountSlot, F32 dir) +{ + if ( mountSlot < mMounts.size() ) + { + MountedShape* mount = mMounts[mountSlot]; + mount->mThread.direction = dir; + if ( mount->mThread.key ) + mount->mShape->setTimeScale( mount->mThread.key, mTimeScale * mount->mThread.direction ); + } +} + +void GuiShapeEdPreview::unmountShape(S32 mountSlot) +{ + if ( mountSlot < mMounts.size() ) + { + delete mMounts[mountSlot]; + mMounts.erase( mountSlot ); + } +} + +void GuiShapeEdPreview::unmountAll() +{ + for ( S32 i = 0; i < mMounts.size(); i++) + delete mMounts[i]; + mMounts.clear(); +} + +void GuiShapeEdPreview::refreshShape() +{ + if ( mModel ) + { + // Nodes or details may have changed => refresh the shape instance + mModel->setMaterialList( mModel->mMaterialList ); + mModel->initNodeTransforms(); + mModel->initMeshObjects(); + + mProjectedNodes.setSize( mModel->getShape()->nodes.size() ); + + if ( mSelectedObject >= mModel->getShape()->objects.size() ) + { + mSelectedObject = -1; + mSelectedObjDetail = 0; + } + + // Re-compute the collision mesh stats + mColMeshes = 0; + mColPolys = 0; + for ( S32 i = 0; i < mModel->getShape()->details.size(); i++ ) + { + const TSShape::Detail& det = mModel->getShape()->details[i]; + const String& detName = mModel->getShape()->getName( det.nameIndex ); + if ( ( det.subShapeNum < 0 ) || !detName.startsWith( "collision-" ) ) + continue; + + mColPolys += det.polyCount; + + S32 od = det.objectDetailNum; + S32 start = mModel->getShape()->subShapeFirstObject[det.subShapeNum]; + S32 end = start + mModel->getShape()->subShapeNumObjects[det.subShapeNum]; + for ( S32 j = start; j < end; j++ ) + { + const TSShape::Object &obj = mModel->getShape()->objects[j]; + const TSMesh* mesh = ( od < obj.numMeshes ) ? mModel->getShape()->meshes[obj.startMeshIndex + od] : NULL; + if ( mesh ) + mColMeshes++; + } + } + } +} + +void GuiShapeEdPreview::updateSun() +{ + if ( mFakeSun ) + { + // Update sun colors + mFakeSun->setColor( mSunDiffuseColor ); + mFakeSun->setAmbient( mSunAmbientColor ); + + // Determine the new sun direction and position + Point3F vec; + MatrixF xRot, zRot; + xRot.set( EulerF( mDegToRad(mSunRot.x), 0.0f, 0.0f )); + zRot.set( EulerF( 0.0f, 0.0f, mDegToRad(mSunRot.z) )); + + zRot.mul( xRot ); + zRot.getColumn( 1, &vec ); + + mFakeSun->setDirection( vec ); + //mFakeSun->setPosition( vec * -10000.0f ); + } +} + +void GuiShapeEdPreview::updateNodeTransforms() +{ + if ( mModel ) + mModel->mDirtyFlags[0] |= TSShapeInstance::TransformDirty; +} + +bool GuiShapeEdPreview::getMeshHidden( const char* name ) const +{ + if ( mModel ) + { + S32 objIndex = mModel->getShape()->findObject( name ); + if ( objIndex != -1 ) + return mModel->mMeshObjects[objIndex].forceHidden; + } + return false; +} + +void GuiShapeEdPreview::setMeshHidden( const char* name, bool hidden ) +{ + if ( mModel ) + { + S32 objIndex = mModel->getShape()->findObject( name ); + if ( objIndex != -1 ) + mModel->setMeshForceHidden( objIndex, hidden ); + } +} + +void GuiShapeEdPreview::setAllMeshesHidden( bool hidden ) +{ + if ( mModel ) + { + for ( S32 i = 0; i < mModel->mMeshObjects.size(); i++ ) + mModel->setMeshForceHidden( i, hidden ); + } +} + +void GuiShapeEdPreview::get3DCursor( GuiCursor *&cursor, + bool &visible, + const Gui3DMouseEvent &event_ ) +{ + cursor = NULL; + visible = false; + + GuiCanvas *root = getRoot(); + if ( !root ) + return; + + S32 currCursor = PlatformCursorController::curArrow; + + if ( root->mCursorChanged == currCursor ) + return; + + PlatformWindow *window = root->getPlatformWindow(); + PlatformCursorController *controller = window->getCursorController(); + + // We've already changed the cursor, + // so set it back before we change it again. + if ( root->mCursorChanged != -1 ) + controller->popCursor(); + + // Now change the cursor shape + controller->pushCursor( currCursor ); + root->mCursorChanged = currCursor; +} + +void GuiShapeEdPreview::fitToShape() +{ + if ( !mModel ) + return; + + // Determine the shape bounding box given the current camera rotation + MatrixF camRotMatrix( smCamMatrix ); + camRotMatrix.setPosition( Point3F::Zero ); + camRotMatrix.inverse(); + + Box3F bounds; + computeSceneBounds( bounds ); + mOrbitPos = bounds.getCenter(); + + camRotMatrix.mul( bounds ); + + // Estimate the camera distance to fill the view by comparing the radii + // of the box and the viewport + F32 len_x = bounds.len_x(); + F32 len_z = bounds.len_z(); + F32 shapeRadius = mSqrt( len_x*len_x + len_z*len_z ) / 2; + F32 viewRadius = 0.45f * getMin( getExtent().x, getExtent().y ); + + // Set camera parameters + if ( mDisplayType == DisplayTypePerspective ) + { + mOrbitDist = ( shapeRadius / viewRadius ) * mSaveWorldToScreenScale.y; + } + else + { + mOrthoCamTrans.set( 0, 0, 0 ); + mOrthoFOV = shapeRadius * viewRadius / 320; + } +} + +void GuiShapeEdPreview::setOrbitPos( const Point3F& pos ) +{ + mOrbitPos = pos; +} + +void GuiShapeEdPreview::exportToCollada( const String& path ) +{ +#ifdef TORQUE_COLLADA + if ( mModel ) + { + MatrixF orientation( true ); + orientation.setPosition( mModel->getShape()->bounds.getCenter() ); + orientation.inverse(); + + OptimizedPolyList polyList; + polyList.setBaseTransform( orientation ); + + mModel->buildPolyList( &polyList, mCurrentDL ); + for ( S32 i = 0; i < mMounts.size(); i++ ) + { + MountedShape* mount = mMounts[i]; + + MatrixF mat( true ); + if ( mount->mNode != -1 ) + { + mat = mModel->mNodeTransforms[ mount->mNode ]; + mat *= mount->mTransform; + } + + polyList.setTransform( &mat, Point3F::One ); + mount->mShape->buildPolyList( &polyList, 0 ); + } + + // Use a ColladaUtils function to do the actual export to a Collada file + ColladaUtils::exportToCollada( path, polyList ); + } +#endif +} + +//----------------------------------------------------------------------------- +// Camera control and Node editing +// - moving the mouse over a node will highlight (but not select) it +// - left clicking on a node will select it, the gizmo will appear +// - left clicking on no node will unselect the current node +// - left dragging the gizmo will translate/rotate the node +// - middle drag translates the view +// - right drag rotates the view +// - mouse wheel zooms the view +// - holding shift while changing the view speeds them up + +void GuiShapeEdPreview::handleMouseDown(const GuiEvent& event, GizmoMode mode) +{ + if (!mActive || !mVisible || !mAwake ) + return; + + mouseLock(); + mLastMousePos = event.mousePoint; + + if ( mRenderNodes && ( mode == NoneMode ) ) + { + mGizmoDragID++; + make3DMouseEvent( mLastEvent, event ); + + // Check gizmo first + mUsingAxisGizmo = false; + if ( mSelectedNode != -1 ) + { + mGizmo->on3DMouseDown( mLastEvent ); + if ( mGizmo->getSelection() != Gizmo::None ) + { + mUsingAxisGizmo = true; + return; + } + } + + // Check if we have clicked on a node + S32 selected = collideNode( mLastEvent ); + if ( selected != mSelectedNode ) + { + mSelectedNode = selected; + Con::executef( this, "onNodeSelected", Con::getIntArg( mSelectedNode )); + } + } + + if ( mode == RotateMode ) + mRenderCameraAxes = true; +} + +void GuiShapeEdPreview::handleMouseUp(const GuiEvent& event, GizmoMode mode) +{ + mouseUnlock(); + mUsingAxisGizmo = false; + + if ( mRenderNodes && ( mode == NoneMode ) ) + { + make3DMouseEvent( mLastEvent, event ); + mGizmo->on3DMouseUp( mLastEvent ); + } + + if ( mode == RotateMode ) + mRenderCameraAxes = false; +} + +void GuiShapeEdPreview::handleMouseMove(const GuiEvent& event, GizmoMode mode) +{ + if ( mRenderNodes && ( mode == NoneMode ) ) + { + make3DMouseEvent( mLastEvent, event ); + if ( mSelectedNode != -1 ) + { + // Check if the mouse is hovering over an axis + mGizmo->on3DMouseMove( mLastEvent ); + if ( mGizmo->getSelection() != Gizmo::None ) + return; + } + + // Check if we are over another node + mHoverNode = collideNode( mLastEvent ); + } +} + +void GuiShapeEdPreview::handleMouseDragged(const GuiEvent& event, GizmoMode mode) +{ + // For non-perspective views, ignore rotation, and let EditTSCtrl handle + // translation + if ( mDisplayType != DisplayTypePerspective ) + { + if ( mode == MoveMode ) + { + Parent::onRightMouseDragged( event ); + return; + } + else if ( mode == RotateMode ) + return; + } + + Point2F delta( event.mousePoint.x - mLastMousePos.x, event.mousePoint.y - mLastMousePos.y ); + mLastMousePos = event.mousePoint; + + // Use shift to increase speed + delta.x *= ( event.modifier & SI_SHIFT ) ? 0.05f : 0.01f; + delta.y *= ( event.modifier & SI_SHIFT ) ? 0.05f : 0.01f; + + if ( mode == NoneMode ) + { + if ( mEditingSun ) + { + mSunRot.x += mRadToDeg( delta.y ); + mSunRot.z += mRadToDeg( delta.x ); + updateSun(); + } + else if ( mRenderNodes ) + { + make3DMouseEvent( mLastEvent, event ); + + if ( mUsingAxisGizmo ) + { + // Use gizmo to modify the transform of the selected node + mGizmo->on3DMouseDragged( mLastEvent ); + switch ( mGizmoProfile->mode ) + { + case MoveMode: + // Update node transform + if ( mSelectedNode != -1 ) + { + Point3F pos = mModel->mNodeTransforms[mSelectedNode].getPosition() + mGizmo->getOffset(); + mModel->mNodeTransforms[mSelectedNode].setPosition( pos ); + } + break; + + case RotateMode: + // Update node transform + if ( mSelectedNode != -1 ) + { + EulerF rot = mGizmo->getDeltaRot(); + mModel->mNodeTransforms[mSelectedNode].mul( MatrixF( rot ) ); + } + break; + default: + break; + } + + // Notify the change in node transform + const char* name = mModel->getShape()->getNodeName(mSelectedNode).c_str(); + const Point3F pos = mModel->mNodeTransforms[mSelectedNode].getPosition(); + AngAxisF aa(mModel->mNodeTransforms[mSelectedNode]); + char buffer[256]; + dSprintf(buffer, sizeof(buffer), "%g %g %g %g %g %g %g", + pos.x, pos.y, pos.z, aa.axis.x, aa.axis.y, aa.axis.z, aa.angle); + + Con::executef(this, "onEditNodeTransform", name, buffer, Con::getIntArg(mGizmoDragID)); + } + } + } + else + { + switch ( mode ) + { + case MoveMode: + { + VectorF offset(-delta.x, 0, delta.y ); + smCamMatrix.mulV( offset ); + mOrbitPos += offset * mMoveSpeed; + } + break; + + case RotateMode: + mCameraRot.x += delta.y; + mCameraRot.z += delta.x; + break; + + default: + break; + } + } +} + +void GuiShapeEdPreview::on3DMouseWheelUp(const Gui3DMouseEvent& event) +{ + if ( mDisplayType == DisplayTypePerspective ) + { + // Use shift and ctrl to increase speed + F32 mod = ( event.modifier & SI_SHIFT ) ? ( ( event.modifier & SI_CTRL ) ? 4.0 : 1.0 ) : 0.25f; + mOrbitDist -= mFabs(event.fval) * mZoomSpeed * mod; + } +} + +void GuiShapeEdPreview::on3DMouseWheelDown(const Gui3DMouseEvent& event) +{ + if ( mDisplayType == DisplayTypePerspective ) + { + // Use shift and ctrl to increase speed + F32 mod = ( event.modifier & SI_SHIFT ) ? ( ( event.modifier & SI_CTRL ) ? 4.0 : 1.0 ) : 0.25f; + mOrbitDist += mFabs(event.fval) * mZoomSpeed * mod; + } +} + +//----------------------------------------------------------------------------- +// NODE PICKING +void GuiShapeEdPreview::updateProjectedNodePoints() +{ + if ( mModel ) + { + // Project the 3D node position to get the 2D screen coordinates + for ( S32 i = 0; i < mModel->mNodeTransforms.size(); i++) + project( mModel->mNodeTransforms[i].getPosition(), &mProjectedNodes[i] ); + } +} + +S32 GuiShapeEdPreview::collideNode(const Gui3DMouseEvent& event) const +{ + // Check if the given position is inside the screen rectangle of + // any shape node + S32 nodeIndex = -1; + F32 minZ = 0; + for ( S32 i = 0; i < mProjectedNodes.size(); i++) + { + const Point3F& pt = mProjectedNodes[i]; + if ( pt.z > 1.0f ) + continue; + + RectI rect( pt.x - sNodeRectSize/2, pt.y - sNodeRectSize/2, sNodeRectSize, sNodeRectSize ); + if ( rect.pointInRect( event.mousePoint ) ) + { + if ( ( nodeIndex == -1 ) || ( pt.z < minZ ) ) + { + nodeIndex = i; + minZ = pt.z; + } + } + } + + return nodeIndex; +} + +//----------------------------------------------------------------------------- +// RENDERING +bool GuiShapeEdPreview::getCameraTransform(MatrixF* cameraMatrix) +{ + // Adjust the camera so that we are still facing the model + if ( mDisplayType == DisplayTypePerspective ) + { + Point3F vec; + MatrixF xRot, zRot; + xRot.set( EulerF( mCameraRot.x, 0.0f, 0.0f )); + zRot.set( EulerF( 0.0f, 0.0f, mCameraRot.z )); + + cameraMatrix->mul( zRot, xRot ); + cameraMatrix->getColumn( 1, &vec ); + cameraMatrix->setColumn( 3, mOrbitPos - vec*mOrbitDist ); + } + else + { + cameraMatrix->identity(); + if ( mModel ) + { + Point3F camPos = mModel->getShape()->bounds.getCenter(); + F32 offset = mModel->getShape()->bounds.len(); + + switch (mDisplayType) + { + case DisplayTypeTop: camPos.z += offset; break; + case DisplayTypeBottom: camPos.z -= offset; break; + case DisplayTypeFront: camPos.y += offset; break; + case DisplayTypeBack: camPos.y -= offset; break; + case DisplayTypeRight: camPos.x += offset; break; + case DisplayTypeLeft: camPos.x -= offset; break; + default: + break; + } + + cameraMatrix->setColumn( 3, camPos ); + } + } + + return true; +} + +void GuiShapeEdPreview::computeSceneBounds(Box3F& bounds) +{ + if ( mModel ) + mModel->computeBounds( mCurrentDL, bounds ); +} + +void GuiShapeEdPreview::updateDetailLevel(const SceneRenderState* state) +{ + // Make sure current detail is valid + if ( !mModel->getShape()->details.size() ) + return; + + if ( mModel->getCurrentDetail() >= mModel->getShape()->details.size() ) + setCurrentDetail( mModel->getShape()->details.size() - 1 ); + + // Convert between FOV and distance so zoom is consistent between Perspective + // and Orthographic views (conversion factor found by trial and error) + const F32 fov2dist = 1.3f; + if ( mDisplayType == DisplayTypePerspective ) + mOrthoFOV = mOrbitDist / fov2dist; + else + mOrbitDist = mOrthoFOV * fov2dist; + + // Use fixed distance in orthographic view (value found by trial + error) + F32 dist = ( mDisplayType == DisplayTypePerspective ) ? mOrbitDist : 0.1f; + + // Select the appropriate detail level, and update the detail stats + S32 currentDetail = mModel->getCurrentDetail(); + mModel->setDetailFromDistance( state, dist ); // need to call this to update smLastPixelSize + if ( mFixedDetail ) + setCurrentDetail( currentDetail ); + + if ( mModel->getCurrentDetail() < 0 ) + setCurrentDetail( 0 ); + + currentDetail = mModel->getCurrentDetail(); + const TSShape::Detail& det = mModel->getShape()->details[ currentDetail ]; + + mDetailPolys = det.polyCount; + mDetailSize = det.size; + mPixelSize = TSShapeInstance::smLastPixelSize; + + mNumMaterials = 0; + mNumDrawCalls = 0; + mNumBones = 0; + mNumWeights = 0; + + if ( det.subShapeNum < 0 ) + { + mNumMaterials = 1; + mNumDrawCalls = 1; + } + else + { + Vector usedMaterials; + + S32 start = mModel->getShape()->subShapeFirstObject[det.subShapeNum]; + S32 end = start + mModel->getShape()->subShapeNumObjects[det.subShapeNum]; + + for ( S32 iObj = start; iObj < end; iObj++ ) + { + const TSShape::Object& obj = mModel->getShape()->objects[iObj]; + + if ( obj.numMeshes <= currentDetail ) + continue; + + const TSMesh* mesh = mModel->getShape()->meshes[ obj.startMeshIndex + currentDetail ]; + if ( !mesh ) + continue; + + // Count the number of draw calls and materials + mNumDrawCalls += mesh->primitives.size(); + for ( S32 iPrim = 0; iPrim < mesh->primitives.size(); iPrim++ ) + usedMaterials.push_back_unique( mesh->primitives[iPrim].matIndex & TSDrawPrimitive::MaterialMask ); + + // For skinned meshes, count the number of bones and weights + if ( mesh->getMeshType() == TSMesh::SkinMeshType ) + { + const TSSkinMesh* skin = dynamic_cast(mesh); + mNumBones += skin->batchData.initialTransforms.size(); + mNumWeights += skin->weight.size(); + } + } + + mNumMaterials = usedMaterials.size(); + } + + // Detect changes in detail level + if ( mCurrentDL != currentDetail ) + { + mCurrentDL = currentDetail; + Con::executef( this, "onDetailChanged"); + } +} + +void GuiShapeEdPreview::updateThreads(F32 delta) +{ + // Advance time on all threads + for ( S32 i = 0; i < mThreads.size(); i++ ) + { + Thread& thread = mThreads[i]; + if ( !thread.key || !thread.direction ) + continue; + + // Make sure thread priority matches sequence priority (which may have changed) + mModel->setPriority( thread.key, mModel->getShape()->sequences[mModel->getSequence( thread.key )].priority ); + + // Handle ping-pong + if ( thread.pingpong && !mModel->isInTransition( thread.key ) ) + { + // Determine next position, then adjust if needed + F32 threadPos = mModel->getPos( thread.key ); + F32 nextPos = threadPos + ( mModel->getTimeScale( thread.key ) * delta / mModel->getDuration( thread.key ) ); + + if ( nextPos < 0 ) + { + // Reflect position and swap playback direction + nextPos = -nextPos; + mModel->setTimeScale( thread.key, -mModel->getTimeScale( thread.key ) ); + mModel->setPos( thread.key, nextPos ); + } + else if ( nextPos > 1.0f ) + { + // Reflect position and swap playback direction + nextPos = 2.0f - nextPos; + mModel->setTimeScale( thread.key, -mModel->getTimeScale( thread.key ) ); + mModel->setPos( thread.key, nextPos ); + } + else + { + // Advance time normally + mModel->advanceTime( delta, thread.key ); + } + } + else + { + // Advance time normally + mModel->advanceTime( delta, thread.key ); + } + + // Invoke script callback if active thread position has changed + if ( i == mActiveThread ) + { + F32 threadPos = mModel->getPos( thread.key ); + bool inTransition = mModel->isInTransition( thread.key ); + onThreadPosChanged_callback( threadPos, inTransition ); + } + } + + // Mark threads as dirty so they will be re-sorted, in case the user changed + // sequence priority or blend flags + mModel->setDirty( TSShapeInstance::ThreadDirty ); + + // Advance time on all mounted shape threads + for ( S32 i = 0; i < mMounts.size(); i++ ) + { + MountedShape* mount = mMounts[i]; + if ( mount->mThread.key ) + mount->mShape->advanceTime( delta, mount->mThread.key ); + } +} + +void GuiShapeEdPreview::renderWorld(const RectI &updateRect) +{ + if ( !mModel ) + return; + + mSaveFrustum = GFX->getFrustum(); + mSaveFrustum.setFarDist( 100000.0f ); + GFX->setFrustum( mSaveFrustum ); + mSaveFrustum.setTransform( smCamMatrix ); + + mSaveProjection = GFX->getProjectionMatrix(); + mSaveWorldToScreenScale = GFX->getWorldToScreenScale(); + + FogData savedFogData = gClientSceneGraph->getFogData(); + gClientSceneGraph->setFogData( FogData() ); // no fog in preview window + + SceneRenderState state + ( + gClientSceneGraph, + SPT_Diffuse, + SceneCameraState( GFX->getViewport(), mSaveFrustum, + GFX->getWorldMatrix(), GFX->getProjectionMatrix() ) + ); + + // Set up pass transforms + RenderPassManager *renderPass = state.getRenderPass(); + renderPass->assignSharedXform( RenderPassManager::View, GFX->getWorldMatrix() ); + renderPass->assignSharedXform( RenderPassManager::Projection, GFX->getProjectionMatrix() ); + + // Set up our TS render state here. + TSRenderState rdata; + rdata.setSceneState(&state); + + LIGHTMGR->unregisterAllLights(); + LIGHTMGR->setSpecialLight( LightManager::slSunLightType, mFakeSun ); + + // We might have some forward lit materials + // so pass down a query to gather lights. + LightQuery query; + query.init( SphereF( Point3F::Zero, 1 ) ); + rdata.setLightQuery( &query ); + + // Update projected node points (for mouse picking) + updateProjectedNodePoints(); + + // Determine time elapsed since last render (for animation playback) + S32 time = Platform::getVirtualMilliseconds(); + S32 dt = time - mLastRenderTime; + mLastRenderTime = time; + + if ( mModel ) + { + updateDetailLevel( &state ); + + // Render the grid + renderGrid(); + + // Animate the model + updateThreads( (F32)dt / 1000.f ); + mModel->animate(); + + // Render the shape + GFX->setStateBlock( mDefaultGuiSB ); + + if ( mRenderGhost ) + rdata.setFadeOverride( 0.5f ); + + GFX->pushWorldMatrix(); + GFX->setWorldMatrix( MatrixF::Identity ); + + mModel->render( rdata ); + + // Render mounted objects + if ( mRenderMounts ) + { + for ( S32 i = 0; i < mMounts.size(); i++ ) + { + MountedShape* mount = mMounts[i]; + + GFX->pushWorldMatrix(); + + if ( mount->mNode != -1 ) + { + GFX->multWorld( mModel->mNodeTransforms[ mount->mNode ] ); + GFX->multWorld( mount->mTransform ); + } + + mount->mShape->animate(); + mount->mShape->render( rdata ); + + GFX->popWorldMatrix(); + } + } + + GFX->popWorldMatrix(); + + renderPass->renderPass( &state ); + + // @todo: Model and other elements (bounds, grid etc) use different + // zBuffers, so at the moment, draw order determines what is on top + + // Render collision volumes + renderCollisionMeshes(); + + // Render the shape bounding box + if ( mRenderBounds ) + { + Point3F boxSize = mModel->getShape()->bounds.maxExtents - mModel->getShape()->bounds.minExtents; + + GFXStateBlockDesc desc; + desc.fillMode = GFXFillWireframe; + GFX->getDrawUtil()->drawCube( desc, boxSize, mModel->getShape()->center, ColorF::WHITE ); + } + + // Render the selected object bounding box + if ( mRenderObjBox && ( mSelectedObject != -1 ) ) + { + const TSShape::Object& obj = mModel->getShape()->objects[mSelectedObject]; + const TSMesh* mesh = ( mCurrentDL < obj.numMeshes ) ? mModel->getShape()->meshes[obj.startMeshIndex + mSelectedObjDetail] : NULL; + if ( mesh ) + { + GFX->pushWorldMatrix(); + if ( obj.nodeIndex != -1 ) + GFX->multWorld( mModel->mNodeTransforms[ obj.nodeIndex ] ); + + const Box3F& bounds = mesh->getBounds(); + GFXStateBlockDesc desc; + desc.fillMode = GFXFillWireframe; + GFX->getDrawUtil()->drawCube( desc, bounds.getExtents(), bounds.getCenter(), ColorF::RED ); + + GFX->popWorldMatrix(); + } + } + + // Render the sun direction if currently editing it + renderSunDirection(); + + // render the nodes in the model + renderNodes(); + + // use the gizmo to render the camera axes + if ( mRenderCameraAxes ) + { + GizmoMode savedMode = mGizmoProfile->mode; + mGizmoProfile->mode = MoveMode; + + Point3F pos; + Point2I screenCenter( updateRect.point + updateRect.extent/2 ); + unproject( Point3F( screenCenter.x, screenCenter.y, 0.5 ), &pos ); + + mGizmo->set( MatrixF::Identity, pos, Point3F::One); + mGizmo->renderGizmo( smCamMatrix ); + + mGizmoProfile->mode = savedMode; + } + } + + gClientSceneGraph->setFogData( savedFogData ); // restore fog setting +} + +void GuiShapeEdPreview::renderGui(Point2I offset, const RectI& updateRect) +{ + // Render the 2D stuff here + + // Render the names of the hovered and selected nodes + if ( mModel ) + { + if ( mRenderNodes && mHoverNode != -1 ) + renderNodeName( mHoverNode, ColorF::WHITE ); + if ( mSelectedNode != -1 ) + renderNodeName( mSelectedNode, ColorF::WHITE ); + } +} + +void GuiShapeEdPreview::renderGrid() +{ + if ( mRenderGridPlane ) + { + // Use EditTSCtrl to render the grid in non-perspective views + if ( mDisplayType != DisplayTypePerspective ) + { + Parent::renderGrid(); + return; + } + + // Round grid dimension up to a multiple of the minor ticks + Point2I dim(mGridDimension.x + mGridPlaneMinorTicks, mGridDimension.y + mGridPlaneMinorTicks); + dim /= ( mGridPlaneMinorTicks + 1 ); + dim *= ( mGridPlaneMinorTicks + 1 ); + + Point2F minorStep( mGridPlaneSize, mGridPlaneSize ); + Point2F size( minorStep.x * dim.x, minorStep.y * dim.y ); + Point2F majorStep( minorStep * ( mGridPlaneMinorTicks + 1 ) ); + + GFXStateBlockDesc desc; + desc.setBlend( true ); + desc.setZReadWrite( true, false ); + + GFX->getDrawUtil()->drawPlaneGrid( desc, Point3F::Zero, size, minorStep, mGridPlaneMinorTickColor ); + GFX->getDrawUtil()->drawPlaneGrid( desc, Point3F::Zero, size, majorStep, mGridPlaneColor ); + } +} + +void GuiShapeEdPreview::renderSunDirection() const +{ + if ( mEditingSun ) + { + // Render four arrows aiming in the direction of the sun's light + ColorI color( mFakeSun->getColor() ); + F32 length = mModel->getShape()->bounds.len() * 0.8f; + + // Get the sun's vectors + Point3F fwd = mFakeSun->getTransform().getForwardVector(); + Point3F up = mFakeSun->getTransform().getUpVector() * length / 8; + Point3F right = mFakeSun->getTransform().getRightVector() * length / 8; + + // Calculate the start and end points of the first arrow (bottom left) + Point3F start = mModel->getShape()->center - fwd * length - up/2 - right/2; + Point3F end = mModel->getShape()->center - fwd * length / 3 - up/2 - right/2; + + GFXStateBlockDesc desc; + desc.setZReadWrite( true, true ); + + GFX->getDrawUtil()->drawArrow( desc, start, end, color ); + GFX->getDrawUtil()->drawArrow( desc, start + up, end + up, color ); + GFX->getDrawUtil()->drawArrow( desc, start + right, end + right, color ); + GFX->getDrawUtil()->drawArrow( desc, start + up + right, end + up + right, color ); + } +} + +void GuiShapeEdPreview::renderNodes() const +{ + if ( mRenderNodes ) + { + // Render links between nodes + GFXStateBlockDesc desc; + desc.setZReadWrite( false, true ); + desc.setCullMode( GFXCullNone ); + GFX->setStateBlockByDesc( desc ); + + PrimBuild::color( ColorI::WHITE ); + PrimBuild::begin( GFXLineList, mModel->getShape()->nodes.size() * 2 ); + for ( S32 i = 0; i < mModel->getShape()->nodes.size(); i++) + { + const TSShape::Node& node = mModel->getShape()->nodes[i]; + if (node.parentIndex >= 0) + { + Point3F start(mModel->mNodeTransforms[i].getPosition()); + Point3F end(mModel->mNodeTransforms[node.parentIndex].getPosition()); + + PrimBuild::vertex3f( start.x, start.y, start.z ); + PrimBuild::vertex3f( end.x, end.y, end.z ); + } + } + PrimBuild::end(); + + // Render the node axes + for ( S32 i = 0; i < mModel->getShape()->nodes.size(); i++) + { + // Render the selected and hover nodes last (so they are on top) + if ( ( i == mSelectedNode ) || ( i == mHoverNode ) ) + continue; + + renderNodeAxes( i, ColorF::WHITE ); + } + + // Render the hovered node + if ( mHoverNode != -1 ) + renderNodeAxes( mHoverNode, ColorF::GREEN ); + } + + // Render the selected node (even if mRenderNodes is false) + if ( mSelectedNode != -1 ) + { + renderNodeAxes( mSelectedNode, ColorF::GREEN ); + + const MatrixF& nodeMat = mModel->mNodeTransforms[mSelectedNode]; + mGizmo->set( nodeMat, nodeMat.getPosition(), Point3F::One); + mGizmo->renderGizmo( smCamMatrix ); + } +} + +void GuiShapeEdPreview::renderNodeAxes(S32 index, const ColorF& nodeColor) const +{ + const Point3F xAxis( 1.0f, 0.15f, 0.15f ); + const Point3F yAxis( 0.15f, 1.0f, 0.15f ); + const Point3F zAxis( 0.15f, 0.15f, 1.0f ); + + GFXStateBlockDesc desc; + desc.setZReadWrite( false, true ); + desc.setCullMode( GFXCullNone ); + + // Render nodes the same size regardless of zoom + F32 scale = mOrbitDist / 60; + + GFX->pushWorldMatrix(); + GFX->multWorld( mModel->mNodeTransforms[index] ); + + GFX->getDrawUtil()->drawCube( desc, xAxis * scale, Point3F::Zero, nodeColor ); + GFX->getDrawUtil()->drawCube( desc, yAxis * scale, Point3F::Zero, nodeColor ); + GFX->getDrawUtil()->drawCube( desc, zAxis * scale, Point3F::Zero, nodeColor ); + + GFX->popWorldMatrix(); +} + +void GuiShapeEdPreview::renderNodeName(S32 index, const ColorF& textColor) const +{ + const TSShape::Node& node = mModel->getShape()->nodes[index]; + const String& nodeName = mModel->getShape()->getName( node.nameIndex ); + + Point2I pos( mProjectedNodes[index].x, mProjectedNodes[index].y + sNodeRectSize + 6 ); + + GFX->getDrawUtil()->setBitmapModulation( textColor ); + GFX->getDrawUtil()->drawText( mProfile->mFont, pos, nodeName.c_str() ); +} + +void GuiShapeEdPreview::renderCollisionMeshes() const +{ + if ( mRenderColMeshes ) + { + ConcretePolyList polylist; + polylist.setTransform( &MatrixF::Identity, Point3F::One ); + for ( S32 iDet = 0; iDet < mModel->getShape()->details.size(); iDet++ ) + { + const TSShape::Detail& det = mModel->getShape()->details[iDet]; + const String& detName = mModel->getShape()->getName( det.nameIndex ); + + // Ignore non-collision details + if ( detName.startsWith( "Collision-" ) ) + mModel->buildPolyList( &polylist, iDet ); + } + + polylist.render(); + } +} + +//----------------------------------------------------------------------------- +// Console methods (GuiShapeEdPreview) +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiShapeEdPreview, setOrbitPos, void, ( Point3F pos ),, + "Set the camera orbit position\n\n" + "@param pos Position in the form \"x y z\"\n" ) +{ + object->setOrbitPos( pos ); +} + +DefineEngineMethod( GuiShapeEdPreview, setModel, bool, ( const char* shapePath ),, + "Sets the model to be displayed in this control\n\n" + "@param shapeName Name of the model to display.\n" + "@return True if the model was loaded successfully, false otherwise.\n" ) +{ + return object->setObjectModel( shapePath ); +} + +DefineEngineMethod( GuiShapeEdPreview, fitToShape, void, (),, + "Adjust the camera position and zoom to fit the shape within the view.\n\n" ) +{ + object->fitToShape(); +} + +DefineEngineMethod( GuiShapeEdPreview, refreshShape, void, (),, + "Refresh the shape (used when the shape meshes or nodes have been added or removed)\n\n" ) +{ + object->refreshShape(); +} + +DefineEngineMethod( GuiShapeEdPreview, updateNodeTransforms, void, (),, + "Refresh the shape node transforms (used when a node transform has been modified externally)\n\n" ) +{ + object->updateNodeTransforms(); +} + +DefineEngineMethod( GuiShapeEdPreview, computeShapeBounds, Box3F, (),, + "Compute the bounding box of the shape using the current detail and node transforms\n\n" + "@return the bounding box \"min.x min.y min.z max.x max.y max.z\"" ) +{ + Box3F bounds; + object->computeSceneBounds(bounds); + return bounds; +} + +DefineEngineMethod( GuiShapeEdPreview, getMeshHidden, bool, ( const char* name ),, + "Return whether the named object is currently hidden\n\n" ) +{ + return object->getMeshHidden( name ); +} + +DefineEngineMethod( GuiShapeEdPreview, setMeshHidden, void, ( const char* name, bool hidden ),, + "Show or hide the named object in the shape\n\n" ) +{ + object->setMeshHidden( name, hidden ); +} + +DefineEngineMethod( GuiShapeEdPreview, setAllMeshesHidden, void, ( bool hidden ),, + "Show or hide all objects in the shape\n\n" ) +{ + object->setAllMeshesHidden( hidden ); +} + +DefineEngineMethod( GuiShapeEdPreview, exportToCollada, void, ( const char* path ),, + "Export the current shape and all mounted objects to COLLADA (.dae).\n" + "Note that animation is not exported, and all geometry is combined into a " + "single mesh.\n\n" + "@param path Destination filename\n" ) +{ + object->exportToCollada( path ); +} + +//----------------------------------------------------------------------------- +// THREADS +DefineEngineMethod( GuiShapeEdPreview, addThread, void, (),, + "Add a new thread (initially without any sequence set)\n\n" ) +{ + object->addThread(); +} + +DefineEngineMethod( GuiShapeEdPreview, removeThread, void, ( S32 slot ),, + "Removes the specifed thread\n\n" + "@param slot index of the thread to remove\n" ) +{ + object->removeThread( slot ); +} + +DefineEngineMethod( GuiShapeEdPreview, getThreadCount, S32, (),, + "Get the number of threads\n\n" + "@return the number of threads\n" ) +{ + return object->getThreadCount(); +} + +DefineEngineMethod( GuiShapeEdPreview, setTimeScale, void, ( F32 scale ),, + "Set the time scale of all threads\n\n" + "@param scale new time scale value\n" ) +{ + object->setTimeScale( scale ); +} + +DefineEngineMethod( GuiShapeEdPreview, setThreadSequence, void, ( const char* name, F32 duration, F32 pos, bool play ), ( 0, 0, false ), + "Sets the sequence to play for the active thread.\n\n" + "@param name name of the sequence to play\n" + "@param duration transition duration (0 for no transition)\n" + "@param pos position in the new sequence to transition to\n" + "@param play if true, the new sequence will play during the transition\n" ) +{ + object->setActiveThreadSequence( name, duration, pos, play ); +} + +DefineEngineMethod( GuiShapeEdPreview, getThreadSequence, const char*, (),, + "Get the name of the sequence assigned to the active thread" ) +{ + return object->getThreadSequence(); +} + +DefineEngineMethod( GuiShapeEdPreview, refreshThreadSequences, void, (),, + "Refreshes thread sequences (in case of removed/renamed sequences" ) +{ + object->refreshThreadSequences(); +} + +//----------------------------------------------------------------------------- +// Mounting +DefineEngineMethod( GuiShapeEdPreview, mountShape, bool, ( const char* shapePath, const char* nodeName, const char* type, S32 slot ),, + "Mount a shape onto the main shape at the specified node\n\n" + "@param shapePath path to the shape to mount\n" + "@param nodeName name of the node on the main shape to mount to\n" + "@param type type of mounting to use (Object, Image or Wheel)\n" + "@param slot mount slot\n" ) +{ + return object->mountShape( shapePath, nodeName, type, slot ); +} + +DefineEngineMethod( GuiShapeEdPreview, setMountNode, void, ( S32 slot, const char* nodeName ),, + "Set the node a shape is mounted to.\n\n" + "@param slot mounted shape slot\n" + "@param nodename name of the node to mount to\n" ) +{ + object->setMountNode( slot, nodeName ); +} + +DefineEngineMethod( GuiShapeEdPreview, getMountThreadSequence, const char*, ( S32 slot ),, + "Get the name of the sequence playing on this mounted shape\n" + "@param slot mounted shape slot\n" + "@return name of the sequence (if any)\n" ) +{ + return object->getMountThreadSequence( slot ); +} + +DefineEngineMethod( GuiShapeEdPreview, setMountThreadSequence, void, ( S32 slot, const char* name ),, + "Set the sequence to play for the shape mounted in the specified slot\n" + "@param slot mounted shape slot\n" + "@param name name of the sequence to play\n" ) +{ + object->setMountThreadSequence( slot, name ); +} + +DefineEngineMethod( GuiShapeEdPreview, getMountThreadPos, F32, ( S32 slot ),, + "Get the playback position of the sequence playing on this mounted shape\n" + "@param slot mounted shape slot\n" + "@return playback position of the sequence (0-1)\n" ) +{ + return object->getMountThreadPos( slot ); +} + +DefineEngineMethod( GuiShapeEdPreview, setMountThreadPos, void, ( S32 slot, F32 pos ),, + "Set the sequence position of the shape mounted in the specified slot\n" + "@param slot mounted shape slot\n" + "@param pos sequence position (0-1)\n" ) +{ + object->setMountThreadPos( slot, pos ); +} + +DefineEngineMethod( GuiShapeEdPreview, getMountThreadDir, F32, ( S32 slot ),, + "Get the playback direction of the sequence playing on this mounted shape\n" + "@param slot mounted shape slot\n" + "@return direction of the sequence (-1=reverse, 0=paused, 1=forward)\n" ) +{ + return object->getMountThreadDir( slot ); +} + +DefineEngineMethod( GuiShapeEdPreview, setMountThreadDir, void, ( S32 slot, F32 dir ),, + "Set the playback direction of the shape mounted in the specified slot\n" + "@param slot mounted shape slot\n" + "@param dir playback direction (-1=backwards, 0=paused, 1=forwards)\n" ) +{ + object->setMountThreadDir( slot, dir ); +} + +DefineEngineMethod( GuiShapeEdPreview, unmountShape, void, ( S32 slot ),, + "Unmount the shape in the specified slot\n" + "@param slot mounted shape slot\n" ) +{ + return object->unmountShape( slot ); +} + +DefineEngineMethod( GuiShapeEdPreview, unmountAll, void, (),, + "Unmount all shapes\n" ) +{ + return object->unmountAll(); +} diff --git a/Engine/source/gui/editor/guiShapeEdPreview.h b/Engine/source/gui/editor/guiShapeEdPreview.h new file mode 100644 index 000000000..63789aaac --- /dev/null +++ b/Engine/source/gui/editor/guiShapeEdPreview.h @@ -0,0 +1,262 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUISHAPEEDPREVIEW_H_ +#define _GUISHAPEEDPREVIEW_H_ + +#include "gui/worldEditor/editTSCtrl.h" +#include "ts/tsShapeInstance.h" + +class LightInfo; + +class GuiShapeEdPreview : public EditTSCtrl +{ + struct Thread + { + TSThread* key; //!< TSThread key + String seqName; //!< Name of the sequence for this thread + S32 direction; //!< Playback direction: -1 (reverse), 0 (paused), 1 (forward) + bool pingpong; //!< Pingpong flag (auto-reverse direction at start/end) + + Thread() + { + init(); + } + + void init() + { + key = NULL; + direction = 1.0f; + pingpong = false; + } + }; + + struct MountedShape + { + enum eMountType + { + Object, //!< Mount origin of shape to target node + Image, //!< Mount 'mountPoint' or origin of shape to target node + Wheel, //!< Mount origin of shape to target node, ignore target node rotation + }; + + TSShapeInstance* mShape; //!< The mounted shape instance + S32 mNode; //!< Index of the node this shape is mounted to + MatrixF mTransform; //!< Mount offset transform + eMountType mType; //!< Type of mount + Thread mThread; //!< Animation thread for the mounted shape + + MountedShape() : mShape(0), mNode(-1), mTransform(true), mType(Object) { } + ~MountedShape() { delete mShape; } + }; + +protected: + typedef EditTSCtrl Parent; + + // View and node selection + bool mUsingAxisGizmo; + bool mEditingSun; //!< True if editing the sun direction, false otherwise + + S32 mGizmoDragID; //!< Used to track transform changes within a single gizmo drag action + S32 mSelectedNode; //!< Index of the selected node + S32 mHoverNode; //!< Index of the node the mouse is over + Vector mProjectedNodes; //!< Projected screen positions of the model nodes + + S32 mSelectedObject; //!< Name of the selected object + S32 mSelectedObjDetail; //!< Detail mesh index of the selected object + + // Camera + EulerF mCameraRot; + bool mRenderCameraAxes; + Point3F mOrbitPos; + F32 mOrbitDist; + F32 mMoveSpeed; + F32 mZoomSpeed; + + // Current Detail + bool mFixedDetail; //!< If false, the detail level will be determined based on camera distance + S32 mCurrentDL; //!< Current detail level + S32 mDetailSize; //!< Size of current detail level + S32 mDetailPolys; //!< Number of polys (triangles) in current detail level + F32 mPixelSize; //!< Current pixel size + S32 mNumMaterials; //!< Number of materials in the current detail level + S32 mNumDrawCalls; //!< Number of draw calls in the current detail level + S32 mNumBones; //!< Number of bones in the current detail level (skins only) + S32 mNumWeights; //!< Number of vertex weights in the current detail level (skins only) + S32 mColMeshes; //!< Number of collision meshes + S32 mColPolys; //!< Number of collision polygons (all meshes) + + // Rendering + Point2I mGridDimension; //!< Dimension of grid in perspective view (eg. 30x30) + bool mRenderGhost; + bool mRenderNodes; + bool mRenderBounds; + bool mRenderObjBox; + bool mRenderColMeshes; + bool mRenderMounts; + TSShapeInstance* mModel; + + LightInfo* mFakeSun; + EulerF mSunRot; + ColorI mSunDiffuseColor; + ColorI mSunAmbientColor; + + // Animation and playback control + Vector mThreads; + F32 mTimeScale; + S32 mActiveThread; + S32 mLastRenderTime; + + // Mounted objects + Vector mMounts; + + static bool setFieldCurrentDL( void *object, const char *index, const char *data ); + static bool setFieldSunDiffuse( void *object, const char *index, const char *data ); + static bool setFieldSunAmbient( void *object, const char *index, const char *data ); + static bool setFieldSunAngleX( void *object, const char *index, const char *data ); + static bool setFieldSunAngleZ( void *object, const char *index, const char *data ); + + static bool setFieldThreadPos( void *object, const char *index, const char *data ); + static bool setFieldThreadIn( void *object, const char *index, const char *data ); + static bool setFieldThreadOut( void *object, const char *index, const char *data ); + static bool setFieldThreadDir( void *object, const char *index, const char *data ); + static bool setFieldThreadPingPong( void *object, const char *index, const char *data ); + + static const char *getFieldThreadPos( void *object, const char *data ); + static const char *getFieldThreadIn( void *object, const char *data ); + static const char *getFieldThreadOut( void *object, const char *data ); + static const char *getFieldThreadDir( void *object, const char *data ); + static const char *getFieldThreadPingPong( void *object, const char *data ); + + // Generic mouse event handlers + void handleMouseDown(const GuiEvent& event, GizmoMode mode); + void handleMouseUp(const GuiEvent& event, GizmoMode mode); + void handleMouseMove(const GuiEvent& event, GizmoMode mode); + void handleMouseDragged(const GuiEvent& event, GizmoMode mode); + + // Node picking + S32 collideNode(const Gui3DMouseEvent& event) const; + void updateProjectedNodePoints(); + + void updateSun(); + void updateDetailLevel(const SceneRenderState* state); + void updateThreads(F32 delta); + + // Rendering + void renderGrid(); + void renderNodes() const; + void renderNodeAxes(S32 index, const ColorF& nodeColor) const; + void renderNodeName(S32 index, const ColorF& textColor) const; + void renderSunDirection() const; + void renderCollisionMeshes() const; + +public: + bool onWake(); + + void setDisplayType(S32 type); + + /// @name Mouse event handlers + ///@{ + void onMouseDown(const GuiEvent& event) { handleMouseDown(event, NoneMode); } + void onMouseUp(const GuiEvent& event) { handleMouseUp(event, NoneMode); } + void onMouseMove(const GuiEvent& event) { handleMouseMove(event, NoneMode); } + void onMouseDragged(const GuiEvent& event) { handleMouseDragged(event, NoneMode); } + + void onMiddleMouseDown(const GuiEvent& event) { handleMouseDown(event, MoveMode); } + void onMiddleMouseUp(const GuiEvent& event) { handleMouseUp(event, MoveMode); } + void onMiddleMouseDragged(const GuiEvent& event) { handleMouseDragged(event, MoveMode); } + + void onRightMouseDown(const GuiEvent& event) { handleMouseDown(event, RotateMode); } + void onRightMouseUp(const GuiEvent& event) { handleMouseUp(event, RotateMode); } + void onRightMouseDragged(const GuiEvent& event) { handleMouseDragged(event, RotateMode); } + + void on3DMouseWheelUp(const Gui3DMouseEvent& event); + void on3DMouseWheelDown(const Gui3DMouseEvent& event); + ///@} + + // Setters/Getters + TSShapeInstance* getModel() { return mModel; } + + void setCurrentDetail(S32 dl); + bool setObjectModel(const char * modelName); + + /// @name Threads + ///@{ + void addThread(); + void removeThread(S32 slot); + S32 getThreadCount() const { return mThreads.size(); } + void setTimeScale(F32 scale); + void setActiveThreadSequence(const char* seqName, F32 duration, F32 pos, bool play); + void setThreadSequence(Thread& thread, TSShapeInstance* shape, const char * seqName, F32 duration=0.0f, F32 pos=0.0f, bool play=false); + const char* getThreadSequence() const; + void refreshThreadSequences(); + + DECLARE_CALLBACK( void, onThreadPosChanged, ( F32 pos, bool inTransition ) ); + ///@} + + /// @name Mounting + ///@{ + bool mountShape(const char* modelName, const char* nodeName, const char* mountType, S32 slot=-1); + void setMountNode(S32 mountSlot, const char* nodeName); + const char* getMountThreadSequence(S32 mountSlot) const; + void setMountThreadSequence(S32 mountSlot, const char* seqName); + F32 getMountThreadPos(S32 mountSlot) const; + void setMountThreadPos(S32 mountSlot, F32 pos); + F32 getMountThreadDir(S32 mountSlot) const; + void setMountThreadDir(S32 mountSlot, F32 dir); + void unmountShape(S32 mountSlot); + void unmountAll(); + ///@} + + void refreshShape(); + void updateNodeTransforms(); + + void get3DCursor(GuiCursor *& cursor, bool& visible, const Gui3DMouseEvent& event_); + + void fitToShape(); + void setOrbitPos( const Point3F& pos ); + + void exportToCollada( const String& path ); + + /// @name Rendering + ///@{ + bool getCameraTransform(MatrixF* cameraMatrix); + void computeSceneBounds(Box3F& bounds); + + bool getMeshHidden(const char* name) const; + void setMeshHidden(const char* name, bool hidden); + void setAllMeshesHidden(bool hidden); + + void renderWorld(const RectI& updateRect); + void renderGui(Point2I offset, const RectI& updateRect); + ///@} + + DECLARE_CONOBJECT(GuiShapeEdPreview); + DECLARE_CATEGORY( "Gui Editor" ); + + static void initPersistFields(); + + GuiShapeEdPreview(); + ~GuiShapeEdPreview(); +}; + +#endif diff --git a/Engine/source/gui/editor/inspector/customField.cpp b/Engine/source/gui/editor/inspector/customField.cpp new file mode 100644 index 000000000..8432c07a6 --- /dev/null +++ b/Engine/source/gui/editor/inspector/customField.cpp @@ -0,0 +1,129 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/editor/inspector/customField.h" +#include "gui/editor/guiInspector.h" + +//----------------------------------------------------------------------------- +// GuiInspectorCustomField - Child class of GuiInspectorField +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT( GuiInspectorCustomField ); + +ConsoleDocClass( GuiInspectorCustomField, + "@brief A control that allows to edit the custom properties (text) of one or more SimObjects.\n\n" + "Editor use only.\n\n" + "@internal" +); + +GuiInspectorCustomField::GuiInspectorCustomField( GuiInspector *inspector, + GuiInspectorGroup* parent, + SimFieldDictionary::Entry* field ) +{ + mInspector = inspector; + mParent = parent; + setBounds(0,0,100,20); +} + +GuiInspectorCustomField::GuiInspectorCustomField() +{ + mInspector = NULL; + mParent = NULL; +} + +void GuiInspectorCustomField::setData( const char* data, bool callbacks ) +{ + mCustomValue = data; + + // Force our edit to update + updateValue(); +} + +const char* GuiInspectorCustomField::getData( U32 inspectObjectIndex ) +{ + return mCustomValue; +} + +void GuiInspectorCustomField::updateValue() +{ + setValue( mCustomValue ); +} + +void GuiInspectorCustomField::setDoc( const char* doc ) +{ + mDoc = StringTable->insert( doc, true ); +} + +void GuiInspectorCustomField::setToolTip( StringTableEntry data ) +{ + static StringTableEntry sTooltipProfile = StringTable->insert( "tooltipProfile" ); + static StringTableEntry sHoverTime = StringTable->insert( "hovertime" ); + static StringTableEntry sTooltip = StringTable->insert( "tooltip" ); + + mEdit->setDataField( sTooltipProfile, NULL, "GuiToolTipProfile" ); + mEdit->setDataField( sHoverTime, NULL, "1000" ); + mEdit->setDataField( sTooltip, NULL, data ); +} + +bool GuiInspectorCustomField::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + return true; +} + +void GuiInspectorCustomField::setInspectorField( AbstractClassRep::Field *field, + StringTableEntry caption, + const char*arrayIndex ) +{ + // Override the base just to be sure it doesn't get called. + // We don't use an AbstractClassRep::Field... + +// mField = field; +// mCaption = StringTable->EmptyString(); +// mRenameCtrl->setText( getFieldName() ); +} + +GuiControl* GuiInspectorCustomField::constructEditControl() +{ + GuiControl* retCtrl = new GuiTextCtrl(); + + static StringTableEntry sProfile = StringTable->insert( "profile" ); + retCtrl->setDataField( sProfile, NULL, "GuiInspectorTextEditProfile" ); + + // Register the object + retCtrl->registerObject(); + + return retCtrl; +} + +void GuiInspectorCustomField::setValue( const char* newValue ) +{ + GuiTextCtrl *ctrl = dynamic_cast( mEdit ); + if( ctrl != NULL ) + ctrl->setText( newValue ); +} + +void GuiInspectorCustomField::_executeSelectedCallback() +{ + Con::executef( mInspector, "onFieldSelected", mCaption, ConsoleBaseType::getType(TypeCaseString)->getTypeName(), mDoc ); +} diff --git a/Engine/source/gui/editor/inspector/customField.h b/Engine/source/gui/editor/inspector/customField.h new file mode 100644 index 000000000..27cbd231b --- /dev/null +++ b/Engine/source/gui/editor/inspector/customField.h @@ -0,0 +1,69 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUI_INSPECTOR_CUSTOMFIELD_H_ +#define _GUI_INSPECTOR_CUSTOMFIELD_H_ + +#include "console/simFieldDictionary.h" +#include "gui/editor/inspector/field.h" + +class GuiInspectorCustomField : public GuiInspectorField +{ + typedef GuiInspectorField Parent; + +public: + + GuiInspectorCustomField( GuiInspector *inspector, GuiInspectorGroup* parent, SimFieldDictionary::Entry* field ); + GuiInspectorCustomField(); + ~GuiInspectorCustomField() {}; + + DECLARE_CONOBJECT( GuiInspectorCustomField ); + + virtual void setData( const char* data, bool callbacks = true ); + virtual const char* getData( U32 inspectObjectIndex = 0 ); + virtual void updateValue(); + virtual StringTableEntry getFieldName() { return StringTable->EmptyString(); } + + virtual void setDoc( const char* doc ); + virtual void setToolTip( StringTableEntry data ); + + virtual bool onAdd(); + + virtual void setInspectorField( AbstractClassRep::Field *field, + StringTableEntry caption = NULL, + const char *arrayIndex = NULL ); + + virtual GuiControl* constructEditControl(); + + virtual void setValue( const char* newValue ); + +protected: + + virtual void _executeSelectedCallback(); + +protected: + + String mCustomValue; + StringTableEntry mDoc; +}; + +#endif // _GUI_INSPECTOR_DYNAMICFIELD_H_ diff --git a/Engine/source/gui/editor/inspector/datablockField.cpp b/Engine/source/gui/editor/inspector/datablockField.cpp new file mode 100644 index 000000000..cce8cb443 --- /dev/null +++ b/Engine/source/gui/editor/inspector/datablockField.cpp @@ -0,0 +1,165 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/simBase.h" +#include "console/simDatablock.h" +#include "gui/editor/guiInspector.h" +#include "gui/editor/inspector/datablockField.h" +#include "gui/editor/inspector/group.h" +#include "gui/buttons/guiIconButtonCtrl.h" +#include "gui/editor/inspector/datablockField.h" +#include "sfx/sfxTypes.h" +#include "sfx/sfxDescription.h" +#include "sfx/sfxEnvironment.h" +#include "sfx/sfxAmbience.h" +#include "sfx/sfxTrack.h" + + +//----------------------------------------------------------------------------- +// GuiInspectorDatablockField +// Field construction for datablock types +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(GuiInspectorDatablockField); + +ConsoleDocClass( GuiInspectorDatablockField, + "@brief Custom field type for datablock enumeration.\n\n" + "Editor use only.\n\n" + "@internal" +); + + +GuiInspectorDatablockField::GuiInspectorDatablockField( StringTableEntry className ) +{ + setClassName( className ); +} + +void GuiInspectorDatablockField::setClassName( StringTableEntry className ) +{ + if( !className || !className[ 0 ] ) + mDesiredClass = NULL; + else + { + mDesiredClass = AbstractClassRep::findClassRep( className ); + if( !mDesiredClass ) + Con::errorf( "GuiInspectorDatablockField::setClassName - no class '%s' found!", className ); + } +} + +void GuiInspectorDatablockField::_populateMenu( GuiPopUpMenuCtrl* menu ) +{ + menu->addScheme( 1, ColorI( 80, 0, 0, 255 ), ColorI( 80, 0, 0, 255 ), ColorI( 80, 0, 0, 255 ) ); // For client-only coloring. + menu->addEntry( "", 0 ); // For unsetting. + + SimSet* set = _getDatablockSet(); + U32 id = 1; + + for( SimSet::iterator iter = set->begin(); iter != set->end(); ++ iter ) + { + SimDataBlock* datablock = dynamic_cast< SimDataBlock* >( *iter ); + + // Skip non-datablocks if we somehow encounter them. + if( !datablock ) + continue; + + // Ok, now we have to figure inheritance info. + if( datablock && ( !mDesiredClass || datablock->getClassRep()->isClass( mDesiredClass ) ) ) + menu->addEntry( datablock->getName(), id ++, datablock->isClientOnly() ? 1 : 0 ); + } + + menu->sort(); +} + + +//----------------------------------------------------------------------------- +// GuiInspectorTypeSFXDescriptionName +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(GuiInspectorTypeSFXDescriptionName); + +ConsoleDocClass( GuiInspectorTypeSFXDescriptionName, + "@brief Inspector field type for SFXDescriptionName\n\n" + "Editor use only.\n\n" + "@internal" +); + +void GuiInspectorTypeSFXDescriptionName::consoleInit() +{ + Parent::consoleInit(); + + ConsoleBaseType::getType( TypeSFXDescriptionName )->setInspectorFieldType( "GuiInspectorTypeSFXDescriptionName" ); +} + + +//----------------------------------------------------------------------------- +// GuiInspectorTypeSFXTrackName +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(GuiInspectorTypeSFXTrackName); + +ConsoleDocClass( GuiInspectorTypeSFXTrackName, + "@brief Inspector field type for SFXTrackName\n\n" + "Editor use only.\n\n" + "@internal" +); + +void GuiInspectorTypeSFXTrackName::consoleInit() +{ + Parent::consoleInit(); + + ConsoleBaseType::getType( TypeSFXTrackName )->setInspectorFieldType( "GuiInspectorTypeSFXTrackName" ); +} + + +//----------------------------------------------------------------------------- +// GuiInspectorTypeSFXEnvironmentName +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(GuiInspectorTypeSFXEnvironmentName); + +ConsoleDocClass( GuiInspectorTypeSFXEnvironmentName, + "@brief Inspector field type for SFXEnvironment\n\n" + "Editor use only.\n\n" + "@internal" +); + +void GuiInspectorTypeSFXEnvironmentName::consoleInit() +{ + Parent::consoleInit(); + + ConsoleBaseType::getType( TypeSFXEnvironmentName )->setInspectorFieldType( "GuiInspectorTypeSFXEnvironmentName" ); +} + + +//----------------------------------------------------------------------------- +// GuiInspectorTypeSFXAmbienceName +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(GuiInspectorTypeSFXAmbienceName); + +ConsoleDocClass( GuiInspectorTypeSFXAmbienceName, + "@brief Inspector field type for SFXAmbience\n\n" + "Editor use only.\n\n" + "@internal" +); + +void GuiInspectorTypeSFXAmbienceName::consoleInit() +{ + Parent::consoleInit(); + + ConsoleBaseType::getType( TypeSFXAmbienceName )->setInspectorFieldType( "GuiInspectorTypeSFXAmbienceName" ); +} diff --git a/Engine/source/gui/editor/inspector/datablockField.h b/Engine/source/gui/editor/inspector/datablockField.h new file mode 100644 index 000000000..eea65a1a5 --- /dev/null +++ b/Engine/source/gui/editor/inspector/datablockField.h @@ -0,0 +1,134 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUI_INSPECTOR_DATABLOCKFIELD_H_ +#define _GUI_INSPECTOR_DATABLOCKFIELD_H_ + +#include "gui/editor/guiInspectorTypes.h" + + +//----------------------------------------------------------------------------- +// GuiInspectorDatablockField - custom field type for datablock enumeration +//----------------------------------------------------------------------------- +class GuiInspectorDatablockField : public GuiInspectorTypeMenuBase +{ + public: + + typedef GuiInspectorTypeMenuBase Parent; + + protected: + + AbstractClassRep *mDesiredClass; + + virtual SimSet* _getDatablockSet() const { return Sim::getDataBlockGroup(); } + virtual void _populateMenu( GuiPopUpMenuCtrl* menu ); + + public: + + DECLARE_CONOBJECT(GuiInspectorDatablockField); + + GuiInspectorDatablockField( StringTableEntry className ); + GuiInspectorDatablockField() { mDesiredClass = NULL; }; + + void setClassName( StringTableEntry className ); +}; + +//----------------------------------------------------------------------------- +// TypeSFXDescriptionName GuiInspectorField Class +//----------------------------------------------------------------------------- +class GuiInspectorTypeSFXDescriptionName : public GuiInspectorDatablockField +{ + public: + + typedef GuiInspectorDatablockField Parent; + + protected: + + virtual SimSet* _getDatablockSet() const { return Sim::getSFXDescriptionSet(); } + + public: + + DECLARE_CONOBJECT(GuiInspectorTypeSFXDescriptionName); + static void consoleInit(); +}; + + +//----------------------------------------------------------------------------- +// TypeSFXTrackName GuiInspectorField Class +//----------------------------------------------------------------------------- +class GuiInspectorTypeSFXTrackName : public GuiInspectorDatablockField +{ + public: + + typedef GuiInspectorDatablockField Parent; + + protected: + + virtual SimSet* _getDatablockSet() const { return Sim::getSFXTrackSet(); } + + public: + + DECLARE_CONOBJECT(GuiInspectorTypeSFXTrackName); + static void consoleInit(); +}; + + +//----------------------------------------------------------------------------- +// TypeSFXEnvironmentName GuiInspectorField Class +//----------------------------------------------------------------------------- +class GuiInspectorTypeSFXEnvironmentName : public GuiInspectorDatablockField +{ + public: + + typedef GuiInspectorDatablockField Parent; + + protected: + + virtual SimSet* _getDatablockSet() const { return Sim::getSFXEnvironmentSet(); } + + public: + DECLARE_CONOBJECT(GuiInspectorTypeSFXEnvironmentName); + static void consoleInit(); +}; + + +//----------------------------------------------------------------------------- +// TypeSFXAmbienceName GuiInspectorField Class +//----------------------------------------------------------------------------- +class GuiInspectorTypeSFXAmbienceName : public GuiInspectorDatablockField +{ + public: + + typedef GuiInspectorDatablockField Parent; + + protected: + + virtual SimSet* _getDatablockSet() const { return Sim::getSFXAmbienceSet(); } + + public: + + DECLARE_CONOBJECT(GuiInspectorTypeSFXAmbienceName); + static void consoleInit(); +}; + + +#endif \ No newline at end of file diff --git a/Engine/source/gui/editor/inspector/dynamicField.cpp b/Engine/source/gui/editor/inspector/dynamicField.cpp new file mode 100644 index 000000000..13f77a797 --- /dev/null +++ b/Engine/source/gui/editor/inspector/dynamicField.cpp @@ -0,0 +1,321 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/editor/inspector/dynamicField.h" +#include "gui/editor/inspector/dynamicGroup.h" +#include "gui/editor/guiInspector.h" +#include "gui/buttons/guiIconButtonCtrl.h" + +//----------------------------------------------------------------------------- +// GuiInspectorDynamicField - Child class of GuiInspectorField +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT( GuiInspectorDynamicField ); + +ConsoleDocClass( GuiInspectorDynamicField, + "@brief Custom field type for dynamic variable modification on SimObjects.\n\n" + "Editor use only.\n\n" + "@internal" +); + +GuiInspectorDynamicField::GuiInspectorDynamicField( GuiInspector *inspector, + GuiInspectorGroup* parent, + SimFieldDictionary::Entry* field ) + : mRenameCtrl( NULL ), + mDeleteButton( NULL ) +{ + mInspector = inspector; + mParent = parent; + mDynField = field; + setBounds(0,0,100,20); +} + +void GuiInspectorDynamicField::setData( const char* data, bool callbacks ) +{ + if ( mDynField == NULL ) + return; + + const U32 numTargets = mInspector->getNumInspectObjects(); + if( callbacks && numTargets > 1 ) + Con::executef( mInspector, "beginCompoundUndo" ); + + // Setting an empty string will kill the field. + const bool isRemoval = !data[ 0 ]; + + for( U32 i = 0; i < numTargets; ++ i ) + { + SimObject* target = mInspector->getInspectObject( i ); + + // Callback on the inspector when the field is modified + // to allow creation of undo/redo actions. + const char *oldData = target->getDataField( mDynField->slotName, NULL ); + if ( !oldData ) + oldData = ""; + if ( dStrcmp( oldData, data ) != 0 ) + { + target->inspectPreApply(); + + if( callbacks ) + { + if( isRemoval ) + Con::executef( mInspector, "onFieldRemoved", target->getIdString(), mDynField->slotName ); + else + Con::executef( mInspector, "onInspectorFieldModified", target->getIdString(), mDynField->slotName, oldData, data ); + } + + target->setDataField( mDynField->slotName, NULL, data ); + + // give the target a chance to validate + target->inspectPostApply(); + } + } + + if( callbacks && numTargets > 1 ) + Con::executef( mInspector, "endCompoundUndo" ); + + // Force our edit to update + updateValue(); +} + +const char* GuiInspectorDynamicField::getData( U32 inspectObjectIndex ) +{ + if( mDynField == NULL ) + return ""; + + return mInspector->getInspectObject( inspectObjectIndex )->getDataField( mDynField->slotName, NULL ); +} + +void GuiInspectorDynamicField::renameField( const char* newFieldName ) +{ + newFieldName = StringTable->insert( newFieldName ); + + if ( mDynField == NULL || mParent == NULL || mEdit == NULL ) + { + Con::warnf("GuiInspectorDynamicField::renameField - No target object or dynamic field data found!" ); + return; + } + + if ( !newFieldName ) + { + Con::warnf("GuiInspectorDynamicField::renameField - Invalid field name specified!" ); + return; + } + + // Only proceed if the name has changed + if ( dStricmp( newFieldName, getFieldName() ) == 0 ) + return; + + // Grab a pointer to our parent and cast it to GuiInspectorDynamicGroup + GuiInspectorDynamicGroup *group = dynamic_cast(mParent); + if ( group == NULL ) + { + Con::warnf("GuiInspectorDynamicField::renameField - Unable to locate GuiInspectorDynamicGroup parent!" ); + return; + } + + const U32 numTargets = mInspector->getNumInspectObjects(); + if( numTargets > 1 ) + Con::executef( mInspector, "onBeginCompoundEdit" ); + + const char* oldFieldName = getFieldName(); + SimFieldDictionary::Entry* newEntry = NULL; + + for( U32 i = 0; i < numTargets; ++ i ) + { + SimObject* target = mInspector->getInspectObject( i ); + + // Make sure the new field is not already defined as a static field + // on the object. + + if( target->isField( newFieldName ) ) + { + // New field is already defined. If we can, let the scripts handle + // the error. Otherwise, just emit an error on the console and proceed. + + if( numTargets == 1 && mInspector->isMethod( "onFieldRenameAlreadyDefined" ) ) + Con::executef( mInspector, "onFieldRenameAlreadyDefined", target->getIdString(), oldFieldName, newFieldName ); + else + Con::errorf( "GuiInspectorDynamicField::renameField - field '%s' is already defined on %i:%s (%s)", + newFieldName, target->getId(), target->getClassName(), target->getName() ); + + // Reset the text entry. + + if( mRenameCtrl ) + mRenameCtrl->setText( oldFieldName ); + + continue; + } + + char currentValue[1024] = {0}; + // Grab our current dynamic field value (we use a temporary buffer as this gets corrupted upon Con::eval) + dSprintf( currentValue, sizeof( currentValue ), "%s", target->getDataField( oldFieldName, NULL ) ); + + // Unset the old field and set the new field. + + target->setDataField( oldFieldName, NULL, "" ); + target->setDataField( newFieldName, NULL, currentValue ); + + // Notify script. + + Con::executef( mInspector, "onFieldRenamed", target->getIdString(), oldFieldName, newFieldName ); + + // Look up the new SimFieldDictionary entry. + + if( !newEntry ) + { + newEntry = target->getFieldDictionary()->findDynamicField( newFieldName ); + if( !newEntry ) + { + Con::warnf( "GuiInspectorDynamicField::renameField - could not find new field '%s' on object %i:%s (%s)", + newFieldName, target->getId(), target->getClassName(), target->getName() ); + } + + mDynField = newEntry; + } + } + + if( numTargets > 1 ) + Con::executef( mInspector, "onEndCompoundEdit" ); + + // Lastly we need to reassign our validate field for our value edit control + char szBuffer[1024]; + dSprintf( szBuffer, sizeof( szBuffer ), "%d.apply(%d.getText());", getId(), mEdit->getId() ); + mEdit->setField("validate", szBuffer ); + + if( mDeleteButton ) + { + dSprintf(szBuffer, sizeof( szBuffer ), "%d.apply("");%d.inspectGroup();", getId(), newFieldName, group->getId()); + mDeleteButton->setField("Command", szBuffer); + } +} + +bool GuiInspectorDynamicField::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + //pushObjectToBack(mEdit); + + // Create our renaming field + mRenameCtrl = new GuiTextEditCtrl(); + mRenameCtrl->setDataField( StringTable->insert("profile"), NULL, "GuiInspectorDynamicFieldProfile" ); + + char szName[512]; + dSprintf( szName, 512, "IE_%s_%d_%s_Rename", mRenameCtrl->getClassName(), mInspector->getInspectObject()->getId(), getFieldName() ); + mRenameCtrl->registerObject( szName ); + + // Our command will evaluate to : + // + // if( (editCtrl).getText() !$= "" ) + // (field).renameField((editCtrl).getText()); + // + char szBuffer[1024]; + dSprintf( szBuffer, sizeof( szBuffer ), "if( %d.getText() !$= \"\" ) %d.renameField(%d.getText());", mRenameCtrl->getId(), getId(), mRenameCtrl->getId() ); + mRenameCtrl->setText( getFieldName() ); + mRenameCtrl->setField("Validate", szBuffer ); + addObject( mRenameCtrl ); + + // Resize the name control to fit in our caption rect + mRenameCtrl->resize( mCaptionRect.point, mCaptionRect.extent ); + + // Resize the value control to leave space for the delete button + mEdit->resize( mValueRect.point, mValueRect.extent); + + // Clear out any caption set from Parent::onAdd + // since we are rendering the fieldname with our 'rename' control. + mCaption = StringTable->insert( "" ); + + // Create delete button control + mDeleteButton = new GuiBitmapButtonCtrl(); + + SimObject* profilePtr = Sim::findObject("InspectorDynamicFieldButton"); + if( profilePtr != NULL ) + mDeleteButton->setControlProfile( dynamic_cast(profilePtr) ); + + dSprintf( szBuffer, sizeof( szBuffer ), + "%d.apply(\"\");%d.schedule(1,\"inspectGroup\");", + getId(), + mParent->getId() ); + + // FIXME Hardcoded image + mDeleteButton->setField( "Bitmap", "tools/gui/images/iconDelete" ); + mDeleteButton->setField( "Text", "X" ); + mDeleteButton->setField( "Command", szBuffer ); + mDeleteButton->setSizing( horizResizeLeft, vertResizeCenter ); + mDeleteButton->resize(Point2I(getWidth() - 20,2), Point2I(16, 16)); + mDeleteButton->registerObject(); + + addObject(mDeleteButton); + + return true; +} + +bool GuiInspectorDynamicField::updateRects() +{ + Point2I fieldExtent = getExtent(); + S32 dividerPos, dividerMargin; + mInspector->getDivider( dividerPos, dividerMargin ); + + S32 editWidth = dividerPos - dividerMargin; + + mEditCtrlRect.set( fieldExtent.x - dividerPos + dividerMargin, 1, editWidth, fieldExtent.y - 1 ); + mCaptionRect.set( 0, 0, fieldExtent.x - dividerPos - dividerMargin, fieldExtent.y ); + mValueRect.set( mEditCtrlRect.point, mEditCtrlRect.extent - Point2I( 20, 0 ) ); + mDeleteRect.set( fieldExtent.x - 20, 2, 16, fieldExtent.y - 4 ); + + // This is probably being called during Parent::onAdd + // so our special controls haven't been created yet but are just about to + // so we just need to calculate the extents. + if ( mRenameCtrl == NULL ) + return false; + + bool sized0 = mRenameCtrl->resize( mCaptionRect.point, mCaptionRect.extent ); + bool sized1 = mEdit->resize( mValueRect.point, mValueRect.extent ); + bool sized2 = mDeleteButton->resize(Point2I(getWidth() - 20,2), Point2I(16, 16)); + + return ( sized0 || sized1 || sized2 ); +} + +void GuiInspectorDynamicField::setInspectorField( AbstractClassRep::Field *field, + StringTableEntry caption, + const char*arrayIndex ) +{ + // Override the base just to be sure it doesn't get called. + // We don't use an AbstractClassRep::Field... + +// mField = field; +// mCaption = StringTable->EmptyString(); +// mRenameCtrl->setText( getFieldName() ); +} + +void GuiInspectorDynamicField::_executeSelectedCallback() +{ + ConsoleBaseType* type = mDynField->type; + if ( type ) + Con::executef( mInspector, "onFieldSelected", mDynField->slotName, type->getTypeName() ); + else + Con::executef( mInspector, "onFieldSelected", mDynField->slotName, "TypeDynamicField" ); +} + +ConsoleMethod( GuiInspectorDynamicField, renameField, void, 3,3, "field.renameField(newDynamicFieldName);" ) +{ + object->renameField( argv[ 2 ] ); +} diff --git a/Engine/source/gui/editor/inspector/dynamicField.h b/Engine/source/gui/editor/inspector/dynamicField.h new file mode 100644 index 000000000..405717295 --- /dev/null +++ b/Engine/source/gui/editor/inspector/dynamicField.h @@ -0,0 +1,72 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUI_INSPECTOR_DYNAMICFIELD_H_ +#define _GUI_INSPECTOR_DYNAMICFIELD_H_ + +#include "console/simFieldDictionary.h" +#include "gui/editor/inspector/field.h" + + +class GuiInspectorDynamicField : public GuiInspectorField +{ + typedef GuiInspectorField Parent; + +public: + + GuiInspectorDynamicField( GuiInspector *inspector, GuiInspectorGroup* parent, SimFieldDictionary::Entry* field ); + GuiInspectorDynamicField() {}; + ~GuiInspectorDynamicField() {}; + + DECLARE_CONOBJECT( GuiInspectorDynamicField ); + + virtual void setData( const char* data, bool callbacks = true ); + virtual const char* getData( U32 inspectObjectIndex = 0 ); + virtual StringTableEntry getFieldName() { return ( mDynField != NULL ) ? mDynField->slotName : StringTable->insert( "" ); } + virtual StringTableEntry getRawFieldName() { return getFieldName(); } + + virtual bool onAdd(); + + void renameField( const char* newFieldName ); + GuiControl* constructRenameControl(); + + virtual bool updateRects(); + virtual void setInspectorField( AbstractClassRep::Field *field, + StringTableEntry caption = NULL, + const char *arrayIndex = NULL ); + +protected: + + virtual void _executeSelectedCallback(); + +protected: + + /// Dynamic field dictionary entry for first target object only. + SimFieldDictionary::Entry* mDynField; + + SimObjectPtr mRenameCtrl; + GuiBitmapButtonCtrl* mDeleteButton; + RectI mDeleteRect; + RectI mValueRect; +}; + +#endif // _GUI_INSPECTOR_DYNAMICFIELD_H_ diff --git a/Engine/source/gui/editor/inspector/dynamicGroup.cpp b/Engine/source/gui/editor/inspector/dynamicGroup.cpp new file mode 100644 index 000000000..9f2285cfb --- /dev/null +++ b/Engine/source/gui/editor/inspector/dynamicGroup.cpp @@ -0,0 +1,258 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/buttons/guiIconButtonCtrl.h" +#include "gui/editor/guiInspector.h" +#include "gui/editor/inspector/dynamicGroup.h" +#include "gui/editor/inspector/dynamicField.h" + +IMPLEMENT_CONOBJECT(GuiInspectorDynamicGroup); + +ConsoleDocClass( GuiInspectorDynamicGroup, + "@brief Used to inspect an object's FieldDictionary (dynamic fields) instead " + "of regular persistent fields.\n\n" + "Editor use only.\n\n" + "@internal" +); + +//----------------------------------------------------------------------------- +// GuiInspectorDynamicGroup - add custom controls +//----------------------------------------------------------------------------- +bool GuiInspectorDynamicGroup::createContent() +{ + if(!Parent::createContent()) + return false; + + // encapsulate the button in a dummy control. + GuiControl* shell = new GuiControl(); + shell->setDataField( StringTable->insert("profile"), NULL, "GuiTransparentProfile" ); + if( !shell->registerObject() ) + { + delete shell; + return false; + } + + // add a button that lets us add new dynamic fields. + GuiBitmapButtonCtrl* addFieldBtn = new GuiBitmapButtonCtrl(); + { + SimObject* profilePtr = Sim::findObject("InspectorDynamicFieldButton"); + if( profilePtr != NULL ) + addFieldBtn->setControlProfile( dynamic_cast(profilePtr) ); + + // FIXME Hardcoded image + addFieldBtn->setBitmap("tools/gui/images/iconAdd.png"); + + char commandBuf[64]; + dSprintf(commandBuf, 64, "%d.addDynamicField();", this->getId()); + addFieldBtn->setField("command", commandBuf); + addFieldBtn->setSizing(horizResizeLeft,vertResizeCenter); + //addFieldBtn->setField("buttonMargin", "2 2"); + addFieldBtn->resize(Point2I(getWidth() - 20,2), Point2I(16, 16)); + addFieldBtn->registerObject("zAddButton"); + } + + shell->resize(Point2I(0,0), Point2I(getWidth(), 28)); + shell->addObject(addFieldBtn); + + // save off the shell control, so we can push it to the bottom of the stack in inspectGroup() + mAddCtrl = shell; + mStack->addObject(shell); + + return true; +} + +struct FieldEntry +{ + SimFieldDictionary::Entry* mEntry; + U32 mNumTargets; +}; + +static S32 QSORT_CALLBACK compareEntries(const void* a,const void* b) +{ + FieldEntry& fa = *((FieldEntry *)a); + FieldEntry& fb = *((FieldEntry *)b); + return dStrnatcmp(fa.mEntry->slotName, fb.mEntry->slotName); +} + +//----------------------------------------------------------------------------- +// GuiInspectorDynamicGroup - inspectGroup override +//----------------------------------------------------------------------------- +bool GuiInspectorDynamicGroup::inspectGroup() +{ + // clear the first responder if it's set + mStack->clearFirstResponder(); + + // Clearing the fields and recreating them will more than likely be more + // efficient than looking up existent fields, updating them, and then iterating + // over existent fields and making sure they still exist, if not, deleting them. + clearFields(); + + // Create a vector of the fields + Vector< FieldEntry > flist; + + const U32 numTargets = mParent->getNumInspectObjects(); + for( U32 i = 0; i < numTargets; ++ i ) + { + SimObject* target = mParent->getInspectObject( i ); + + // Then populate with fields + SimFieldDictionary * fieldDictionary = target->getFieldDictionary(); + for(SimFieldDictionaryIterator ditr(fieldDictionary); *ditr; ++ditr) + { + if( i == 0 ) + { + flist.increment(); + flist.last().mEntry = *ditr; + flist.last().mNumTargets = 1; + } + else + { + const U32 numFields = flist.size(); + for( U32 n = 0; n < numFields; ++ n ) + if( flist[ n ].mEntry->slotName == ( *ditr )->slotName ) + { + flist[ n ].mNumTargets ++; + break; + } + } + } + } + + dQsort( flist.address(), flist.size(), sizeof( FieldEntry ), compareEntries ); + + for(U32 i = 0; i < flist.size(); i++) + { + if( flist[ i ].mNumTargets != numTargets ) + continue; + + SimFieldDictionary::Entry* entry = flist[i].mEntry; + + // Create a dynamic field inspector. Can't reuse typed GuiInspectorFields as + // these rely on AbstractClassRep::Fields. + GuiInspectorDynamicField *field = new GuiInspectorDynamicField( mParent, this, entry ); + + // Register the inspector field and add it to our lists + if( field->registerObject() ) + { + mChildren.push_back( field ); + mStack->addObject( field ); + } + else + delete field; + } + + mStack->pushObjectToBack(mAddCtrl); + + setUpdate(); + + return true; +} + +void GuiInspectorDynamicGroup::updateAllFields() +{ + // We overload this to just reinspect the group. + inspectGroup(); +} + +ConsoleMethod(GuiInspectorDynamicGroup, inspectGroup, bool, 2, 2, "Refreshes the dynamic fields in the inspector.") +{ + return object->inspectGroup(); +} + +void GuiInspectorDynamicGroup::clearFields() +{ + // save mAddCtrl + Sim::getGuiGroup()->addObject(mAddCtrl); + // delete everything else + mStack->clear(); + // clear the mChildren list. + mChildren.clear(); + // and restore. + mStack->addObject(mAddCtrl); +} + +SimFieldDictionary::Entry* GuiInspectorDynamicGroup::findDynamicFieldInDictionary( StringTableEntry fieldName ) +{ + SimFieldDictionary * fieldDictionary = mParent->getInspectObject()->getFieldDictionary(); + + for(SimFieldDictionaryIterator ditr(fieldDictionary); *ditr; ++ditr) + { + SimFieldDictionary::Entry * entry = (*ditr); + + if( entry->slotName == fieldName ) + return entry; + } + + return NULL; +} + +void GuiInspectorDynamicGroup::addDynamicField() +{ + // We can't add a field without a target + if( !mStack ) + { + Con::warnf("GuiInspectorDynamicGroup::addDynamicField - no target SimObject to add a dynamic field to."); + return; + } + + // find a field name that is not in use. + // But we wont try more than 100 times to find an available field. + U32 uid = 1; + char buf[64] = "dynamicField"; + SimFieldDictionary::Entry* entry = findDynamicFieldInDictionary(buf); + while(entry != NULL && uid < 100) + { + dSprintf(buf, sizeof(buf), "dynamicField%03d", uid++); + entry = findDynamicFieldInDictionary(buf); + } + + const U32 numTargets = mParent->getNumInspectObjects(); + if( numTargets > 1 ) + Con::executef( mParent, "onBeginCompoundEdit" ); + + for( U32 i = 0; i < numTargets; ++ i ) + { + SimObject* target = mParent->getInspectObject( i ); + + Con::evaluatef( "%d.dynamicField = \"defaultValue\";", target->getId(), buf ); + + // Notify script. + + Con::executef( mParent, "onFieldAdded", target->getIdString(), buf ); + } + + if( numTargets > 1 ) + Con::executef( mParent, "onEndCompoundEdit" ); + + // now we simply re-inspect the object, to see the new field. + inspectGroup(); + instantExpand(); +} + +ConsoleMethod( GuiInspectorDynamicGroup, addDynamicField, void, 2, 2, "obj.addDynamicField();" ) +{ + object->addDynamicField(); +} + +ConsoleMethod( GuiInspectorDynamicGroup, removeDynamicField, void, 3, 3, "" ) +{ +} diff --git a/Engine/source/gui/editor/inspector/dynamicGroup.h b/Engine/source/gui/editor/inspector/dynamicGroup.h new file mode 100644 index 000000000..65708159d --- /dev/null +++ b/Engine/source/gui/editor/inspector/dynamicGroup.h @@ -0,0 +1,65 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUI_INSPECTOR_DYNAMICGROUP_H_ +#define _GUI_INSPECTOR_DYNAMICGROUP_H_ + +#include "gui/editor/inspector/group.h" + +#include "console/simFieldDictionary.h" + + +class GuiInspectorDynamicGroup : public GuiInspectorGroup +{ +private: + typedef GuiInspectorGroup Parent; + GuiControl* mAddCtrl; + + +public: + DECLARE_CONOBJECT(GuiInspectorDynamicGroup); + GuiInspectorDynamicGroup() { /*mNeedScroll=false;*/ }; + GuiInspectorDynamicGroup( StringTableEntry groupName, SimObjectPtr parent ) + : GuiInspectorGroup( groupName, parent) { /*mNeedScroll=false;*/}; + + //----------------------------------------------------------------------------- + // inspectGroup is overridden in GuiInspectorDynamicGroup to inspect an + // objects FieldDictionary (dynamic fields) instead of regular persistent + // fields. + bool inspectGroup(); + virtual void updateAllFields(); + + // For scriptable dynamic field additions + void addDynamicField(); + + // Clear our fields (delete them) + void clearFields(); + + // Find an already existent field by name in the dictionary + virtual SimFieldDictionary::Entry* findDynamicFieldInDictionary( StringTableEntry fieldName ); +protected: + // create our inner controls when we add + virtual bool createContent(); + +}; + +#endif // _GUI_INSPECTOR_DYNAMICGROUP_H_ diff --git a/Engine/source/gui/editor/inspector/field.cpp b/Engine/source/gui/editor/inspector/field.cpp new file mode 100644 index 000000000..64b434abc --- /dev/null +++ b/Engine/source/gui/editor/inspector/field.cpp @@ -0,0 +1,666 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/editor/inspector/field.h" +#include "gui/buttons/guiIconButtonCtrl.h" +#include "gui/editor/guiInspector.h" +#include "core/util/safeDelete.h" +#include "gfx/gfxDrawUtil.h" +#include "math/mathTypes.h" +#include "core/strings/stringUnit.h" + + +IMPLEMENT_CONOBJECT(GuiInspectorField); + +ConsoleDocClass( GuiInspectorField, + "@brief The GuiInspectorField control is a representation of a single abstract " + "field for a given ConsoleObject derived object.\n\n" + "Editor use only.\n\n" + "@internal" +); + +//----------------------------------------------------------------------------- + +GuiInspectorField::GuiInspectorField( GuiInspector* inspector, + GuiInspectorGroup* parent, + AbstractClassRep::Field* field ) + : mInspector( inspector ), + mParent( parent ), + mField( field ), + mFieldArrayIndex( NULL ), + mEdit( NULL ) +{ + if( field != NULL ) + mCaption = field->pFieldname; + else + mCaption = StringTable->EmptyString(); + + setCanSave( false ); + setBounds(0,0,100,18); + + if( field ) + _setFieldDocs( field->pFieldDocs ); +} + +//----------------------------------------------------------------------------- + +GuiInspectorField::GuiInspectorField() + : mInspector( NULL ), + mParent( NULL ), + mEdit( NULL ), + mField( NULL ), + mFieldArrayIndex( NULL ), + mCaption( StringTable->EmptyString() ), + mHighlighted( false ) +{ + setCanSave( false ); +} + +//----------------------------------------------------------------------------- + +GuiInspectorField::~GuiInspectorField() +{ +} + +//----------------------------------------------------------------------------- + +void GuiInspectorField::init( GuiInspector *inspector, GuiInspectorGroup *group ) +{ + mInspector = inspector; + mParent = group; +} + +//----------------------------------------------------------------------------- + +bool GuiInspectorField::onAdd() +{ + setInspectorProfile(); + + if ( !Parent::onAdd() ) + return false; + + if ( !mInspector ) + return false; + + mEdit = constructEditControl(); + if ( mEdit == NULL ) + return false; + + setBounds(0,0,100,18); + + // Add our edit as a child + addObject( mEdit ); + + // Calculate Caption and EditCtrl Rects + updateRects(); + + // Force our editField to set it's value + updateValue(); + + return true; +} + +//----------------------------------------------------------------------------- + +bool GuiInspectorField::resize( const Point2I &newPosition, const Point2I &newExtent ) +{ + if ( !Parent::resize( newPosition, newExtent ) ) + return false; + + return updateRects(); +} + +//----------------------------------------------------------------------------- + +void GuiInspectorField::onRender( Point2I offset, const RectI &updateRect ) +{ + RectI ctrlRect(offset, getExtent()); + + // Render fillcolor... + if ( mProfile->mOpaque ) + GFX->getDrawUtil()->drawRectFill(ctrlRect, mProfile->mFillColor); + + // Render caption... + if ( mCaption && mCaption[0] ) + { + // Backup current ClipRect + RectI clipBackup = GFX->getClipRect(); + + RectI clipRect = updateRect; + + // The rect within this control in which our caption must fit. + RectI rect( offset + mCaptionRect.point + mProfile->mTextOffset, mCaptionRect.extent + Point2I(1,1) - Point2I(5,0) ); + + // Now clipRect is the amount of our caption rect that is actually visible. + bool hit = clipRect.intersect( rect ); + + if ( hit ) + { + GFX->setClipRect( clipRect ); + GFXDrawUtil *drawer = GFX->getDrawUtil(); + + // Backup modulation color + ColorI currColor; + drawer->getBitmapModulation( &currColor ); + + // Draw caption background... + if( !isActive() ) + GFX->getDrawUtil()->drawRectFill( clipRect, mProfile->mFillColorNA ); + else if ( mHighlighted ) + GFX->getDrawUtil()->drawRectFill( clipRect, mProfile->mFillColorHL ); + + // Draw caption text... + + drawer->setBitmapModulation( !isActive() ? mProfile->mFontColorNA : mHighlighted ? mProfile->mFontColorHL : mProfile->mFontColor ); + + // Clip text with '...' if too long to fit + String clippedText( mCaption ); + clipText( clippedText, clipRect.extent.x ); + + renderJustifiedText( offset + mProfile->mTextOffset, getExtent(), clippedText ); + + // Restore modulation color + drawer->setBitmapModulation( currColor ); + + // Restore previous ClipRect + GFX->setClipRect( clipBackup ); + } + } + + // Render Children... + renderChildControls(offset, updateRect); + + // Render border... + if ( mProfile->mBorder ) + renderBorder(ctrlRect, mProfile); + + // Render divider... + Point2I worldPnt = mEditCtrlRect.point + offset; + GFX->getDrawUtil()->drawLine( worldPnt.x - 5, + worldPnt.y, + worldPnt.x - 5, + worldPnt.y + getHeight(), + !isActive() ? mProfile->mBorderColorNA : mHighlighted ? mProfile->mBorderColorHL : mProfile->mBorderColor ); +} + +//----------------------------------------------------------------------------- + +void GuiInspectorField::setFirstResponder( GuiControl *firstResponder ) +{ + Parent::setFirstResponder( firstResponder ); + + if ( firstResponder == this || firstResponder == mEdit ) + { + mInspector->setHighlightField( this ); + } +} + +//----------------------------------------------------------------------------- + +void GuiInspectorField::onMouseDown( const GuiEvent &event ) +{ + if ( mCaptionRect.pointInRect( globalToLocalCoord( event.mousePoint ) ) ) + { + if ( mEdit ) + //mEdit->onMouseDown( event ); + mInspector->setHighlightField( this ); + } + else + Parent::onMouseDown( event ); +} + +//----------------------------------------------------------------------------- + +void GuiInspectorField::onRightMouseUp( const GuiEvent &event ) +{ + if ( mCaptionRect.pointInRect( globalToLocalCoord( event.mousePoint ) ) ) + Con::executef( mInspector, "onFieldRightClick", getIdString() ); + else + Parent::onMouseDown( event ); +} + +//----------------------------------------------------------------------------- + +void GuiInspectorField::setData( const char* data, bool callbacks ) +{ + if( mField == NULL ) + return; + + if( verifyData( data ) ) + { + String strData = data; + const U32 numTargets = mInspector->getNumInspectObjects(); + + if( callbacks && numTargets > 1 ) + Con::executef( mInspector, "onBeginCompoundEdit" ); + + for( U32 i = 0; i < numTargets; ++ i ) + { + SimObject* target = mInspector->getInspectObject( i ); + + String oldValue = target->getDataField( mField->pFieldname, mFieldArrayIndex); + + // For numeric fields, allow input expressions. + + String newValue = strData; + S32 type= mField->type; + if( type == TypeS8 || type == TypeS32 || type == TypeF32 ) + { + char buffer[ 2048 ]; + expandEscape( buffer, newValue ); + newValue = Con::evaluatef( "%%f = \"%s\"; return ( %s );", oldValue.c_str(), buffer ); + } + else if( type == TypeS32Vector + || type == TypeF32Vector + || type == TypeColorI + || type == TypeColorF + || type == TypePoint2I + || type == TypePoint2F + || type == TypePoint3F + || type == TypePoint4F + || type == TypeRectI + || type == TypeRectF + || type == TypeMatrixPosition + || type == TypeMatrixRotation + || type == TypeBox3F + || type == TypeRectUV ) + { + //TODO: we should actually take strings into account and not chop things up between quotes + + U32 numNewUnits = StringUnit::getUnitCount( newValue, " \t\n\r" ); + + StringBuilder strNew; + bool isFirst = true; + for( U32 n = 0; n < numNewUnits; ++ n ) + { + char oldComponentVal[ 1024 ]; + StringUnit::getUnit( oldValue, n, " \t\n\r", oldComponentVal, sizeof( oldComponentVal ) ); + + char newComponentExpr[ 1024 ]; + StringUnit::getUnit( newValue, n, " \t\n\r", newComponentExpr, sizeof( newComponentExpr ) ); + + char buffer[ 2048 ]; + expandEscape( buffer, newComponentExpr ); + + const char* newComponentVal = Con::evaluatef( "%%f = \"%s\"; %%v = \"%s\"; return ( %s );", + oldComponentVal, oldValue.c_str(), buffer ); + + if( !isFirst ) + strNew.append( ' ' ); + strNew.append( newComponentVal ); + + isFirst = false; + } + + newValue = strNew.end(); + } + + target->inspectPreApply(); + + // Fire callback single-object undo. + + if( callbacks ) + Con::executef( mInspector, "onInspectorFieldModified", + target->getIdString(), + mField->pFieldname, + mFieldArrayIndex ? mFieldArrayIndex : "(null)", + oldValue.c_str(), + newValue.c_str() ); + + target->setDataField( mField->pFieldname, mFieldArrayIndex, newValue ); + + // Give the target a chance to validate. + target->inspectPostApply(); + } + + if( callbacks && numTargets > 1 ) + Con::executef( mInspector, "onEndCompoundEdit" ); + } + + // Force our edit to update + updateValue(); +} + +//----------------------------------------------------------------------------- + +const char* GuiInspectorField::getData( U32 inspectObjectIndex ) +{ + if( mField == NULL ) + return ""; + + return mInspector->getInspectObject( inspectObjectIndex )->getDataField( mField->pFieldname, mFieldArrayIndex ); +} + +//----------------------------------------------------------------------------- + +void GuiInspectorField::resetData() +{ + if( !mField ) + return; + + SimObject* inspectObject = getInspector()->getInspectObject(); + + SimObject* tempObject = static_cast< SimObject* >( inspectObject->getClassRep()->create() ); + setData( tempObject->getDataField( mField->pFieldname, mFieldArrayIndex ) ); + delete tempObject; +} + +//----------------------------------------------------------------------------- + +void GuiInspectorField::setInspectorField( AbstractClassRep::Field *field, StringTableEntry caption, const char*arrayIndex ) +{ + mField = field; + + if ( arrayIndex != NULL ) + mFieldArrayIndex = StringTable->insert( arrayIndex ); + + if ( !caption || !caption[0] ) + mCaption = getFieldName(); + else + mCaption = caption; + + _setFieldDocs( mField->pFieldDocs ); +} + +//----------------------------------------------------------------------------- + +StringTableEntry GuiInspectorField::getRawFieldName() +{ + if( !mField ) + return StringTable->EmptyString(); + + return mField->pFieldname; +} + +//----------------------------------------------------------------------------- + +StringTableEntry GuiInspectorField::getFieldName() +{ + // Sanity + if ( mField == NULL ) + return StringTable->EmptyString(); + + // Array element? + if( mFieldArrayIndex != NULL ) + { + S32 frameTempSize = dStrlen( mField->pFieldname ) + 32; + FrameTemp valCopy( frameTempSize ); + dSprintf( (char *)valCopy, frameTempSize, "%s[%s]", mField->pFieldname, mFieldArrayIndex ); + + // Return formatted element + return StringTable->insert( valCopy ); + } + + // Plain field name. + return mField->pFieldname; +} + +//----------------------------------------------------------------------------- + +StringTableEntry GuiInspectorField::getFieldType() +{ + if( !mField ) + return StringTable->EmptyString(); + + return ConsoleBaseType::getType( mField->type )->getTypeName(); +} + +//----------------------------------------------------------------------------- + +GuiControl* GuiInspectorField::constructEditControl() +{ + GuiControl* retCtrl = new GuiTextEditCtrl(); + + static StringTableEntry sProfile = StringTable->insert( "profile" ); + retCtrl->setDataField( sProfile, NULL, "GuiInspectorTextEditProfile" ); + + _registerEditControl( retCtrl ); + + char szBuffer[512]; + dSprintf( szBuffer, 512, "%d.apply(%d.getText());",getId(), retCtrl->getId() ); + + // Suffices to hook on to "validate" as regardless of whether we lose + // focus through the user pressing enter or clicking away on another + // keyboard control, we will see a validate call. + + retCtrl->setField("validate", szBuffer ); + + return retCtrl; +} + +//----------------------------------------------------------------------------- + +void GuiInspectorField::setInspectorProfile() +{ + GuiControlProfile *profile = NULL; + + if( mInspector->getNumInspectObjects() > 1 ) + { + if( !hasSameValueInAllObjects() ) + Sim::findObject( "GuiInspectorMultiFieldDifferentProfile", profile ); + else + Sim::findObject( "GuiInspectorMultiFieldProfile", profile ); + } + + if( !profile ) + Sim::findObject( "GuiInspectorFieldProfile", profile ); + + if( profile ) + setControlProfile( profile ); +} + +//----------------------------------------------------------------------------- + +void GuiInspectorField::setValue( StringTableEntry newValue ) +{ + GuiTextEditCtrl *ctrl = dynamic_cast( mEdit ); + if( ctrl != NULL ) + ctrl->setText( newValue ); +} + +//----------------------------------------------------------------------------- + +bool GuiInspectorField::updateRects() +{ + S32 dividerPos, dividerMargin; + mInspector->getDivider( dividerPos, dividerMargin ); + + Point2I fieldExtent = getExtent(); + Point2I fieldPos = getPosition(); + + S32 editWidth = dividerPos - dividerMargin; + + mEditCtrlRect.set( fieldExtent.x - dividerPos + dividerMargin, 1, editWidth, fieldExtent.y - 1 ); + mCaptionRect.set( 0, 0, fieldExtent.x - dividerPos - dividerMargin, fieldExtent.y ); + + if ( !mEdit ) + return false; + + return mEdit->resize( mEditCtrlRect.point, mEditCtrlRect.extent ); +} + +//----------------------------------------------------------------------------- + +void GuiInspectorField::updateValue() +{ + if( mInspector->getNumInspectObjects() > 1 ) + { + setInspectorProfile(); + + if( !hasSameValueInAllObjects() ) + setValue( StringTable->EmptyString() ); + else + setValue( getData() ); + } + else + setValue( getData() ); +} + +//----------------------------------------------------------------------------- + +void GuiInspectorField::setHLEnabled( bool enabled ) +{ + mHighlighted = enabled; + if ( mHighlighted ) + { + if ( mEdit && !mEdit->isFirstResponder() ) + { + mEdit->setFirstResponder(); + GuiTextEditCtrl *edit = dynamic_cast( mEdit ); + if ( edit ) + { + mouseUnlock(); + edit->mouseLock(); + edit->setCursorPos(0); + } + } + _executeSelectedCallback(); + } +} + +//----------------------------------------------------------------------------- + +bool GuiInspectorField::hasSameValueInAllObjects() +{ + char value1[ 2048 ]; + + // Get field value from first object. + + const char* data1 = getData( 0 ); + if( data1 ) + { + dStrncpy( value1, data1, sizeof( value1 ) ); + value1[ sizeof( value1 ) - 1 ] = 0; + } + else + value1[ 0 ] = 0; + + // Check if all other objects have the same value. + + const U32 numObjects = mInspector->getNumInspectObjects(); + for( U32 i = 1; i < numObjects; ++ i ) + { + const char* value2 = getData( i ); + if( !value2 ) + value2 = ""; + + if( dStrcmp( value1, value2 ) != 0 ) + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiInspectorField::_executeSelectedCallback() +{ + if( mField ) + Con::executef( mInspector, "onFieldSelected", mField->pFieldname, ConsoleBaseType::getType(mField->type)->getTypeName(), mFieldDocs.c_str() ); +} + +//----------------------------------------------------------------------------- + +void GuiInspectorField::_registerEditControl( GuiControl *ctrl ) +{ + char szName[512]; + dSprintf( szName, 512, "IE_%s_%d_%s_Field", ctrl->getClassName(), mInspector->getInspectObject()->getId(), mCaption); + + // Register the object + ctrl->registerObject( szName ); +} + +//----------------------------------------------------------------------------- + +void GuiInspectorField::_setFieldDocs( StringTableEntry docs ) +{ + mFieldDocs = String(); + if( docs && docs[ 0 ] ) + { + // Only accept first line of docs for brevity. + + const char* newline = dStrchr( docs, '\n' ); + if( newline ) + mFieldDocs = String( docs, newline - docs ); + else + mFieldDocs = docs; + } +} + +//============================================================================= +// Console Methods. +//============================================================================= +// MARK: ---- Console Methods ---- + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiInspectorField, getInspector, S32, 2, 2, "() - Return the GuiInspector to which this field belongs." ) +{ + return object->getInspector()->getId(); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiInspectorField, getInspectedFieldName, const char*, 2, 2, "() - Return the name of the field edited by this inspector field." ) +{ + return object->getFieldName(); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiInspectorField, getInspectedFieldType, const char*, 2, 2, "() - Return the type of the field edited by this inspector field." ) +{ + return object->getFieldType(); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiInspectorField, apply, void, 3, 4, "( string newValue, bool callbacks=true ) - Set the field's value. Suppress callbacks for undo if callbacks=false." ) +{ + bool callbacks = true; + if( argc > 3 ) + callbacks = dAtob( argv[ 3 ] ); + + object->setData( argv[ 2 ], callbacks ); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiInspectorField, applyWithoutUndo, void, 3, 3, "() - Set field value without recording undo (same as 'apply( value, false )')." ) +{ + object->setData( argv[ 2 ], false ); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiInspectorField, getData, const char*, 2, 2, "() - Return the value currently displayed on the field." ) +{ + return object->getData(); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( GuiInspectorField, reset, void, 2, 2, "() - Reset to default value." ) +{ + object->resetData(); +} diff --git a/Engine/source/gui/editor/inspector/field.h b/Engine/source/gui/editor/inspector/field.h new file mode 100644 index 000000000..2a4f8718b --- /dev/null +++ b/Engine/source/gui/editor/inspector/field.h @@ -0,0 +1,194 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUI_INSPECTOR_FIELD_H_ +#define _GUI_INSPECTOR_FIELD_H_ + +#include "gui/core/guiCanvas.h" +#include "gui/shiny/guiTickCtrl.h" +#include "gui/controls/guiTextEditCtrl.h" +#include "gui/buttons/guiBitmapButtonCtrl.h" +#include "gui/controls/guiPopUpCtrl.h" + +#include "gui/containers/guiRolloutCtrl.h" + +class GuiInspectorGroup; +class GuiInspector; + + +/// The GuiInspectorField control is a representation of a single abstract +/// field for a given ConsoleObject derived object. It handles creation +/// getting and setting of it's fields data and editing control. +/// +/// Creation of custom edit controls is done through this class and is +/// dependent upon the dynamic console type, which may be defined to be +/// custom for different types. +/// +/// @note GuiInspectorField controls must have a GuiInspectorGroup as their +/// parent. +class GuiInspectorField : public GuiControl +{ + public: + + typedef GuiControl Parent; + friend class GuiInspectorGroup; + + protected: + + /// The text to display as the field name. + StringTableEntry mCaption; + + /// The group to which this field belongs. + GuiInspectorGroup* mParent; + + /// The GuiInspector that the group is in to which this field belongs. + GuiInspector* mInspector; + + /// + AbstractClassRep::Field* mField; + + /// + StringTableEntry mFieldArrayIndex; + + /// + String mFieldDocs; + + /// + GuiControl* mEdit; + + /// + RectI mCaptionRect; + + /// + RectI mEditCtrlRect; + + /// + bool mHighlighted; + + virtual void _registerEditControl( GuiControl *ctrl ); + virtual void _executeSelectedCallback(); + + void _setFieldDocs( StringTableEntry docs ); + + public: + + explicit GuiInspectorField(); + + /// + GuiInspectorField( GuiInspector *inspector, GuiInspectorGroup* parent, AbstractClassRep::Field* field ); + + virtual ~GuiInspectorField(); + + /// + virtual void init( GuiInspector *inspector, GuiInspectorGroup *group ); + + /// + virtual void setInspectorField( AbstractClassRep::Field *field, + StringTableEntry caption = NULL, + const char *arrayIndex = NULL ); + + /// + virtual GuiControl* constructEditControl(); + + /// Chooses and sets the GuiControlProfile. + virtual void setInspectorProfile(); + + /// Sets this control's caption text, usually set within setInspectorField, + /// this is exposed in case someone wants to override the normal caption. + virtual void setCaption( StringTableEntry caption ) { mCaption = caption; } + + /// Returns pointer to this InspectorField's edit ctrl. + virtual GuiControl* getEditCtrl() { return mEdit; } + + /// Sets the value of this GuiInspectorField (not the actual field) + /// This means the EditCtrl unless overridden. + virtual void setValue( const char* newValue ); + + /// Get the currently value of this control (not the actual field) + virtual const char* getValue() { return NULL; } + + /// Update this controls value to reflect that of the inspected field. + virtual void updateValue(); + + /// Return the name of the field being edited. + virtual StringTableEntry getFieldName(); + + /// Return the name of the console type that this field uses. + virtual StringTableEntry getFieldType(); + + /// Return the name without the array index that may potentially be present. + virtual StringTableEntry getRawFieldName(); + + /// + StringTableEntry getArrayIndex() const { return mFieldArrayIndex; } + + /// Called from within setData to allow child classes + /// to perform their own verification. + virtual bool verifyData( StringTableEntry data ) { return true; } + + /// Set value of the field we are inspecting + virtual void setData( const char* data, bool callbacks = true ); + + /// Reset the field value to its default value based on default-constructed objects. + /// + /// @note If multiple objects are inspected, this will take the default value from + /// the first object and set all fields to this value. + virtual void resetData(); + + /// Get value of the field we are inspecting. + /// + /// @note The string returned by this method may be a transient string allocated + /// internally by the console. For any non-transient needs, this string has + /// to be copied to locally owned memory. + /// @note This method always returns the value of the field in the first + /// inspected object. + virtual const char* getData( U32 inspectObjectIndex = 0 ); + + /// Update the inspected field to match the value of this control. + virtual void updateData() {}; + + /// + virtual bool updateRects(); + + /// + virtual void setHLEnabled( bool enabled ); + + /// Return true if all inspected objects have the same value for this + /// field. + bool hasSameValueInAllObjects(); + + /// Return the inspector object that this field belongs to. + GuiInspector* getInspector() const { return mInspector; } + + // GuiControl. + virtual bool onAdd(); + virtual bool resize(const Point2I &newPosition, const Point2I &newExtent); + virtual void onRender(Point2I offset, const RectI &updateRect); + virtual void setFirstResponder( GuiControl *firstResponder ); + virtual void onMouseDown( const GuiEvent &event ); + virtual void onRightMouseUp( const GuiEvent &event ); + + DECLARE_CONOBJECT( GuiInspectorField ); + DECLARE_CATEGORY( "Gui Editor" ); +}; + +#endif // _GUI_INSPECTOR_FIELD_H_ diff --git a/Engine/source/gui/editor/inspector/group.cpp b/Engine/source/gui/editor/inspector/group.cpp new file mode 100644 index 000000000..7a52e7ca3 --- /dev/null +++ b/Engine/source/gui/editor/inspector/group.cpp @@ -0,0 +1,567 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/editor/guiInspector.h" +#include "gui/editor/inspector/group.h" +#include "gui/editor/inspector/dynamicField.h" +#include "gui/editor/inspector/datablockField.h" +#include "gui/buttons/guiIconButtonCtrl.h" + + +IMPLEMENT_CONOBJECT(GuiInspectorGroup); + +ConsoleDocClass( GuiInspectorGroup, + "@brief The GuiInspectorGroup control is a helper control that the inspector " + "makes use of which houses a collapsible pane type control for separating " + "inspected objects fields into groups.\n\n" + "Editor use only.\n\n" + "@internal" +); + +//#define DEBUG_SPEW + + +//----------------------------------------------------------------------------- + +GuiInspectorGroup::GuiInspectorGroup() + : mParent( NULL ), + mStack(NULL) +{ + setBounds(0,0,200,20); + + mChildren.clear(); + + setCanSave( false ); + + // Make sure we receive our ticks. + setProcessTicks(); + mMargin.set(0,0,5,0); +} + +//----------------------------------------------------------------------------- + +GuiInspectorGroup::GuiInspectorGroup( const String& groupName, + SimObjectPtr parent ) + : mParent( parent ), + mStack(NULL) +{ + + setBounds(0,0,200,20); + + mCaption = groupName; + setCanSave( false ); + + mChildren.clear(); + mMargin.set(0,0,4,0); +} + +//----------------------------------------------------------------------------- + +GuiInspectorGroup::~GuiInspectorGroup() +{ +} + +//----------------------------------------------------------------------------- + +bool GuiInspectorGroup::onAdd() +{ + setDataField( StringTable->insert("profile"), NULL, "GuiInspectorGroupProfile" ); + + if( !Parent::onAdd() ) + return false; + + // Create our inner controls. Allow subclasses to provide other content. + if(!createContent()) + return false; + + inspectGroup(); + + return true; +} + +//----------------------------------------------------------------------------- + +bool GuiInspectorGroup::createContent() +{ + // Create our field stack control + mStack = new GuiStackControl(); + + // Prefer GuiTransperantProfile for the stack. + mStack->setDataField( StringTable->insert("profile"), NULL, "GuiInspectorStackProfile" ); + if( !mStack->registerObject() ) + { + SAFE_DELETE( mStack ); + return false; + } + + addObject( mStack ); + mStack->setField( "padding", "0" ); + return true; +} + +//----------------------------------------------------------------------------- + +void GuiInspectorGroup::animateToContents() +{ + calculateHeights(); + if(size() > 0) + animateTo( mExpanded.extent.y ); + else + animateTo( mHeader.extent.y ); +} + +//----------------------------------------------------------------------------- + +GuiInspectorField* GuiInspectorGroup::constructField( S32 fieldType ) +{ + // See if we can construct a field of this type + ConsoleBaseType *cbt = ConsoleBaseType::getType(fieldType); + if( !cbt ) + return NULL; + + // Alright, is it a datablock? + if(cbt->isDatablock()) + { + // Default to GameBaseData + StringTableEntry typeClassName = cbt->getTypeClassName(); + + if( mParent->getNumInspectObjects() == 1 && !dStricmp(typeClassName, "GameBaseData") ) + { + // Try and setup the classname based on the object type + char className[256]; + dSprintf(className,256,"%sData", mParent->getInspectObject( 0 )->getClassName()); + // Walk the ACR list and find a matching class if any. + AbstractClassRep *walk = AbstractClassRep::getClassList(); + while(walk) + { + if(!dStricmp(walk->getClassName(), className)) + break; + + walk = walk->getNextClass(); + } + + // We found a valid class + if (walk) + typeClassName = walk->getClassName(); + + } + + + GuiInspectorDatablockField *dbFieldClass = new GuiInspectorDatablockField( typeClassName ); + if( dbFieldClass != NULL ) + { + // return our new datablock field with correct datablock type enumeration info + return dbFieldClass; + } + } + + // Nope, not a datablock. So maybe it has a valid inspector field override we can use? + if(!cbt->getInspectorFieldType()) + // Nothing, so bail. + return NULL; + + // Otherwise try to make it! + ConsoleObject *co = create(cbt->getInspectorFieldType()); + GuiInspectorField *gif = dynamic_cast(co); + + if(!gif) + { + // Wasn't appropriate type, bail. + delete co; + return NULL; + } + + return gif; +} + +//----------------------------------------------------------------------------- + +GuiInspectorField *GuiInspectorGroup::findField( const char *fieldName ) +{ + // If we don't have any field children we can't very well find one then can we? + if( mChildren.empty() ) + return NULL; + + Vector::iterator i = mChildren.begin(); + + for( ; i != mChildren.end(); i++ ) + { + if( (*i)->getFieldName() != NULL && dStricmp( (*i)->getFieldName(), fieldName ) == 0 ) + return (*i); + } + + return NULL; +} + +//----------------------------------------------------------------------------- + +void GuiInspectorGroup::clearFields() +{ + // Deallocates all field related controls. + mStack->clear(); + + // Then just cleanup our vectors which also point to children + // that we keep for our own convenience. + mArrayCtrls.clear(); + mChildren.clear(); +} + +//----------------------------------------------------------------------------- + +bool GuiInspectorGroup::inspectGroup() +{ + // We can't inspect a group without a target! + if( !mParent->getNumInspectObjects() ) + return false; + + // to prevent crazy resizing, we'll just freeze our stack for a sec.. + mStack->freeze(true); + + bool bNoGroup = false; + + // Un-grouped fields are all sorted into the 'general' group + if ( dStricmp( mCaption, "General" ) == 0 ) + bNoGroup = true; + + // Just delete all fields and recreate them (like the dynamicGroup) + // because that makes creating controls for array fields a lot easier + clearFields(); + + bool bNewItems = false; + bool bMakingArray = false; + GuiStackControl *pArrayStack = NULL; + GuiRolloutCtrl *pArrayRollout = NULL; + bool bGrabItems = false; + + AbstractClassRep* commonAncestorClass = findCommonAncestorClass(); + AbstractClassRep::FieldList& fieldList = commonAncestorClass->mFieldList; + for( AbstractClassRep::FieldList::iterator itr = fieldList.begin(); + itr != fieldList.end(); ++ itr ) + { + AbstractClassRep::Field* field = &( *itr ); + if( field->type == AbstractClassRep::StartGroupFieldType ) + { + // If we're dealing with general fields, always set grabItems to true (to skip them) + if( bNoGroup == true ) + bGrabItems = true; + else if( dStricmp( field->pGroupname, mCaption ) == 0 ) + bGrabItems = true; + continue; + } + else if ( field->type == AbstractClassRep::EndGroupFieldType ) + { + // If we're dealing with general fields, always set grabItems to false (to grab them) + if( bNoGroup == true ) + bGrabItems = false; + else if( dStricmp( field->pGroupname, mCaption ) == 0 ) + bGrabItems = false; + continue; + } + + // Skip field if it has the HideInInspectors flag set. + + if( field->flag.test( AbstractClassRep::FIELD_HideInInspectors ) ) + continue; + + if( ( bGrabItems == true || ( bNoGroup == true && bGrabItems == false ) ) && itr->type != AbstractClassRep::DeprecatedFieldType ) + { + if( bNoGroup == true && bGrabItems == true ) + continue; + + if ( field->type == AbstractClassRep::StartArrayFieldType ) + { + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[GuiInspectorGroup] Beginning array '%s'", + field->pFieldname ); + #endif + + // Starting an array... + // Create a rollout for the Array, give it the array's name. + GuiRolloutCtrl *arrayRollout = new GuiRolloutCtrl(); + GuiControlProfile *arrayRolloutProfile = dynamic_cast( Sim::findObject( "GuiInspectorRolloutProfile0" ) ); + + arrayRollout->setControlProfile(arrayRolloutProfile); + //arrayRollout->mCaption = StringTable->insert( String::ToString( "%s (%i)", field->pGroupname, field->elementCount ) ); + arrayRollout->setCaption( field->pGroupname ); + //arrayRollout->setMargin( 14, 0, 0, 0 ); + arrayRollout->registerObject(); + + GuiStackControl *arrayStack = new GuiStackControl(); + arrayStack->registerObject(); + arrayStack->freeze(true); + arrayRollout->addObject(arrayStack); + + // Allocate a rollout for each element-count in the array + // Give it the element count name. + for ( U32 i = 0; i < field->elementCount; i++ ) + { + GuiRolloutCtrl *elementRollout = new GuiRolloutCtrl(); + GuiControlProfile *elementRolloutProfile = dynamic_cast( Sim::findObject( "GuiInspectorRolloutProfile0" ) ); + + char buf[256]; + dSprintf( buf, 256, " [%i]", i ); + + elementRollout->setControlProfile(elementRolloutProfile); + elementRollout->setCaption(buf); + //elementRollout->setMargin( 14, 0, 0, 0 ); + elementRollout->registerObject(); + + GuiStackControl *elementStack = new GuiStackControl(); + elementStack->registerObject(); + elementRollout->addObject(elementStack); + elementRollout->instantCollapse(); + + arrayStack->addObject( elementRollout ); + } + + pArrayRollout = arrayRollout; + pArrayStack = arrayStack; + arrayStack->freeze(false); + pArrayRollout->instantCollapse(); + mStack->addObject(arrayRollout); + + bMakingArray = true; + continue; + } + else if ( field->type == AbstractClassRep::EndArrayFieldType ) + { + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[GuiInspectorGroup] Ending array '%s'", + field->pFieldname ); + #endif + + bMakingArray = false; + continue; + } + + if ( bMakingArray ) + { + // Add a GuiInspectorField for this field, + // for every element in the array... + for ( U32 i = 0; i < pArrayStack->size(); i++ ) + { + FrameTemp intToStr( 64 ); + dSprintf( intToStr, 64, "%d", i ); + + // The array stack should have a rollout for each element + // as children... + GuiRolloutCtrl *pRollout = dynamic_cast(pArrayStack->at(i)); + // And the each of those rollouts should have a stack for + // fields... + GuiStackControl *pStack = dynamic_cast(pRollout->at(0)); + + // And we add a new GuiInspectorField to each of those stacks... + GuiInspectorField *fieldGui = constructField( field->type ); + if ( fieldGui == NULL ) + fieldGui = new GuiInspectorField(); + + fieldGui->init( mParent, this ); + StringTableEntry caption = field->pFieldname; + fieldGui->setInspectorField( field, caption, intToStr ); + + if( fieldGui->registerObject() ) + { + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[GuiInspectorGroup] Adding array element '%s[%i]'", + field->pFieldname, i ); + #endif + + mChildren.push_back( fieldGui ); + pStack->addObject( fieldGui ); + } + else + delete fieldGui; + } + + continue; + } + + // This is weird, but it should work for now. - JDD + // We are going to check to see if this item is an array + // if so, we're going to construct a field for each array element + if( field->elementCount > 1 ) + { + // Make a rollout control for this array + // + GuiRolloutCtrl *rollout = new GuiRolloutCtrl(); + rollout->setDataField( StringTable->insert("profile"), NULL, "GuiInspectorRolloutProfile0" ); + rollout->setCaption(String::ToString( "%s (%i)", field->pFieldname, field->elementCount)); + rollout->setMargin( 14, 0, 0, 0 ); + rollout->registerObject(); + mArrayCtrls.push_back(rollout); + + // Put a stack control within the rollout + // + GuiStackControl *stack = new GuiStackControl(); + stack->setDataField( StringTable->insert("profile"), NULL, "GuiInspectorStackProfile" ); + stack->registerObject(); + stack->freeze(true); + rollout->addObject(stack); + + mStack->addObject(rollout); + + // Create each field and add it to the stack. + // + for (S32 nI = 0; nI < field->elementCount; nI++) + { + FrameTemp intToStr( 64 ); + dSprintf( intToStr, 64, "%d", nI ); + + // Construct proper ValueName[nI] format which is "ValueName0" for index 0, etc. + + String fieldName = String::ToString( "%s%d", field->pFieldname, nI ); + + // If the field already exists, just update it + GuiInspectorField *fieldGui = findField( fieldName ); + if( fieldGui != NULL ) + { + fieldGui->updateValue(); + continue; + } + + bNewItems = true; + + fieldGui = constructField( field->type ); + if ( fieldGui == NULL ) + fieldGui = new GuiInspectorField(); + + fieldGui->init( mParent, this ); + StringTableEntry caption = StringTable->insert( String::ToString(" [%i]",nI) ); + fieldGui->setInspectorField( field, caption, intToStr ); + + if ( fieldGui->registerObject() ) + { + mChildren.push_back( fieldGui ); + stack->addObject( fieldGui ); + } + else + delete fieldGui; + } + + stack->freeze(false); + stack->updatePanes(); + rollout->instantCollapse(); + } + else + { + // If the field already exists, just update it + GuiInspectorField *fieldGui = findField( field->pFieldname ); + if ( fieldGui != NULL ) + { + fieldGui->updateValue(); + continue; + } + + bNewItems = true; + + fieldGui = constructField( field->type ); + if ( fieldGui == NULL ) + fieldGui = new GuiInspectorField(); + + fieldGui->init( mParent, this ); + fieldGui->setInspectorField( field ); + + if( fieldGui->registerObject() ) + { + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[GuiInspectorGroup] Adding field '%s'", + field->pFieldname ); + #endif + + mChildren.push_back( fieldGui ); + mStack->addObject( fieldGui ); + } + else + { + SAFE_DELETE( fieldGui ); + } + } + } + } + mStack->freeze(false); + mStack->updatePanes(); + + // If we've no new items, there's no need to resize anything! + if( bNewItems == false && !mChildren.empty() ) + return true; + + sizeToContents(); + + setUpdate(); + + return true; +} + +//----------------------------------------------------------------------------- + +bool GuiInspectorGroup::updateFieldValue( StringTableEntry fieldName, StringTableEntry arrayIdx ) +{ + // Check if we contain a field of this name, + // if so update its value and return true. + Vector::iterator iter = mChildren.begin(); + + if( arrayIdx == StringTable->EmptyString() ) + arrayIdx = NULL; + + for( ; iter != mChildren.end(); iter++ ) + { + GuiInspectorField *field = (*iter); + if ( field->mField && + field->mField->pFieldname == fieldName && + field->mFieldArrayIndex == arrayIdx ) + { + field->updateValue(); + return true; + } + } + + return false; +} + +//----------------------------------------------------------------------------- + +void GuiInspectorGroup::updateAllFields() +{ + Vector::iterator iter = mChildren.begin(); + for( ; iter != mChildren.end(); iter++ ) + (*iter)->updateValue(); +} + +//----------------------------------------------------------------------------- + +AbstractClassRep* GuiInspectorGroup::findCommonAncestorClass() +{ + AbstractClassRep* classRep = getInspector()->getInspectObject( 0 )->getClassRep(); + const U32 numInspectObjects = getInspector()->getNumInspectObjects(); + + for( U32 i = 1; i < numInspectObjects; ++ i ) + { + SimObject* object = getInspector()->getInspectObject( i ); + while( !object->getClassRep()->isClass( classRep ) ) + { + classRep = classRep->getParentClass(); + AssertFatal( classRep, "GuiInspectorGroup::findcommonAncestorClass - Walked above ConsoleObject!" ); + } + } + + return classRep; +} diff --git a/Engine/source/gui/editor/inspector/group.h b/Engine/source/gui/editor/inspector/group.h new file mode 100644 index 000000000..782dbfd46 --- /dev/null +++ b/Engine/source/gui/editor/inspector/group.h @@ -0,0 +1,87 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUI_INSPECTOR_GROUP_H_ +#define _GUI_INSPECTOR_GROUP_H_ + +#include "gui/core/guiCanvas.h" +#include "gui/controls/guiTextEditCtrl.h" +#include "gui/buttons/guiBitmapButtonCtrl.h" +#include "gui/containers/guiRolloutCtrl.h" + +// Forward refs +class GuiInspector; +class GuiInspectorField; + + +/// The GuiInspectorGroup control is a helper control that the inspector +/// makes use of which houses a collapsible pane type control for separating +/// inspected objects fields into groups. The content of the inspector is +/// made up of zero or more GuiInspectorGroup controls inside of a GuiStackControl +/// +class GuiInspectorGroup : public GuiRolloutCtrl +{ +private: + typedef GuiRolloutCtrl Parent; +public: + // Members + SimObjectPtr mParent; + Vector mChildren; + GuiStackControl* mStack; + Vector mArrayCtrls; + + // Constructor/Destructor/Conobject Declaration + GuiInspectorGroup(); + GuiInspectorGroup( const String& groupName, SimObjectPtr parent ); + virtual ~GuiInspectorGroup(); + + DECLARE_CONOBJECT(GuiInspectorGroup); + DECLARE_CATEGORY( "Gui Editor" ); + + virtual GuiInspectorField* constructField( S32 fieldType ); + virtual GuiInspectorField* findField( const char *fieldName ); + + // Publicly Accessible Information about this group + const String& getGroupName() const { return mCaption; }; + SimObjectPtr getInspector() { return mParent; }; + + bool onAdd(); + virtual bool inspectGroup(); + + virtual void animateToContents(); + + void clearFields(); + + virtual bool updateFieldValue( StringTableEntry fieldName, const char *arrayIdx ); + virtual void updateAllFields(); + + U32 getNumFields() const { return mChildren.size(); } + +protected: + // overridable method that creates our inner controls. + virtual bool createContent(); + + /// Determine the class that is a common ancestor to all inspected objects. + AbstractClassRep* findCommonAncestorClass(); +}; + +#endif // _GUI_INSPECTOR_GROUP_H_ diff --git a/Engine/source/gui/editor/inspector/variableField.cpp b/Engine/source/gui/editor/inspector/variableField.cpp new file mode 100644 index 000000000..810ba45a7 --- /dev/null +++ b/Engine/source/gui/editor/inspector/variableField.cpp @@ -0,0 +1,120 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/editor/inspector/variableField.h" + +#include "gui/buttons/guiIconButtonCtrl.h" +#include "gui/editor/guiInspector.h" +#include "core/util/safeDelete.h" +#include "gfx/gfxDrawUtil.h" + +//----------------------------------------------------------------------------- +// GuiInspectorVariableField +//----------------------------------------------------------------------------- + +IMPLEMENT_CONOBJECT(GuiInspectorVariableField); + +ConsoleDocClass( GuiInspectorVariableField, + "@brief Inspector support for variables in a GuiVariableInspector.\n\n" + "Editor use only.\n\n" + "@internal" +); + +GuiInspectorVariableField::GuiInspectorVariableField() +{ +} + +GuiInspectorVariableField::~GuiInspectorVariableField() +{ +} + +bool GuiInspectorVariableField::onAdd() +{ + setInspectorProfile(); + + // Hack: skip our immediate parent + if ( !Parent::Parent::onAdd() ) + return false; + + { + GuiTextEditCtrl *edit = new GuiTextEditCtrl(); + + edit->setDataField( StringTable->insert("profile"), NULL, "GuiInspectorTextEditProfile" ); + + edit->registerObject(); + + char szBuffer[512]; + dSprintf( szBuffer, 512, "%d.apply(%d.getText());",getId(), edit->getId() ); + edit->setField("AltCommand", szBuffer ); + edit->setField("Validate", szBuffer ); + + mEdit = edit; + } + + setBounds(0,0,100,18); + + // Add our edit as a child + addObject( mEdit ); + + // Calculate Caption and EditCtrl Rects + updateRects(); + + // Force our editField to set it's value + updateValue(); + + return true; +} + +void GuiInspectorVariableField::setData( const char* data, bool callbacks ) +{ + if ( !mCaption || mCaption[0] == 0 ) + return; + + Con::setVariable( mCaption, data ); + + // Force our edit to update + updateValue(); +} + +const char* GuiInspectorVariableField::getData( U32 inspectObjectIndex ) +{ + if ( !mCaption || mCaption[0] == 0 ) + return ""; + + return Con::getVariable( mCaption ); +} + +void GuiInspectorVariableField::setValue( const char* newValue ) +{ + GuiTextEditCtrl *ctrl = dynamic_cast( mEdit ); + if( ctrl != NULL ) + ctrl->setText( newValue ); +} + +void GuiInspectorVariableField::updateValue() +{ + if ( !mCaption || mCaption[0] == 0 ) + return; + + setValue( getData() ); +} \ No newline at end of file diff --git a/Engine/source/gui/editor/inspector/variableField.h b/Engine/source/gui/editor/inspector/variableField.h new file mode 100644 index 000000000..ae720a7c4 --- /dev/null +++ b/Engine/source/gui/editor/inspector/variableField.h @@ -0,0 +1,62 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUI_INSPECTOR_VARIABLEFIELD_H_ +#define _GUI_INSPECTOR_VARIABLEFIELD_H_ + +#ifndef _GUI_INSPECTOR_FIELD_H_ +#include "gui/editor/inspector/field.h" +#endif + +class GuiInspectorGroup; +class GuiInspector; + + +class GuiInspectorVariableField : public GuiInspectorField +{ + friend class GuiInspectorField; + +public: + + typedef GuiInspectorField Parent; + + GuiInspectorVariableField(); + virtual ~GuiInspectorVariableField(); + + DECLARE_CONOBJECT( GuiInspectorVariableField ); + DECLARE_CATEGORY( "Gui Editor" ); + + virtual bool onAdd(); + + + virtual void setValue( const char* newValue ); + virtual const char* getValue() { return NULL; } + virtual void updateValue(); + virtual void setData( const char* data, bool callbacks = true ); + virtual const char* getData( U32 inspectObjectIndex = 0 ); + virtual void updateData() {}; + +protected: + +}; + +#endif // _GUI_INSPECTOR_VARIABLEFIELD_H_ diff --git a/Engine/source/gui/editor/inspector/variableGroup.cpp b/Engine/source/gui/editor/inspector/variableGroup.cpp new file mode 100644 index 000000000..6c1cca7ec --- /dev/null +++ b/Engine/source/gui/editor/inspector/variableGroup.cpp @@ -0,0 +1,109 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#include "platform/platform.h" +#include "gui/editor/inspector/variableGroup.h" +#include "gui/editor/inspector/variableField.h" +#include "gui/editor/guiInspector.h" +#include "gui/buttons/guiIconButtonCtrl.h" +#include "console/consoleInternal.h" + +extern ExprEvalState gEvalState; + +//----------------------------------------------------------------------------- +// GuiInspectorVariableGroup +//----------------------------------------------------------------------------- +// +// +IMPLEMENT_CONOBJECT(GuiInspectorVariableGroup); + +ConsoleDocClass( GuiInspectorVariableGroup, + "@brief Inspector support for variable groups in a GuiVariableInspector.\n\n" + "Editor use only.\n\n" + "@internal" +); + +GuiInspectorVariableGroup::GuiInspectorVariableGroup() +{ +} + +GuiInspectorVariableGroup::~GuiInspectorVariableGroup() +{ +} + +GuiInspectorField* GuiInspectorVariableGroup::constructField( S32 fieldType ) +{ + return NULL; +} + +bool GuiInspectorVariableGroup::inspectGroup() +{ + // to prevent crazy resizing, we'll just freeze our stack for a sec.. + mStack->freeze(true); + + clearFields(); + + Vector names; + + gEvalState.globalVars.exportVariables( mSearchString, &names, NULL ); + + bool bNewItems = false; + + for ( U32 i = 0; i < names.size(); i++ ) + { + const String &varName = names[i]; + + // If the field already exists, just update it + GuiInspectorVariableField *field = dynamic_cast( findField( varName ) ); + if ( field != NULL ) + { + field->updateValue(); + continue; + } + + bNewItems = true; + + field = new GuiInspectorVariableField(); + field->init( mParent, this ); + field->setInspectorField( NULL, StringTable->insert( varName ) ); + + if ( field->registerObject() ) + { + mChildren.push_back( field ); + mStack->addObject( field ); + } + else + delete field; + } + + mStack->freeze(false); + mStack->updatePanes(); + + // If we've no new items, there's no need to resize anything! + if( bNewItems == false && !mChildren.empty() ) + return true; + + sizeToContents(); + + setUpdate(); + + return true; +} diff --git a/Engine/source/gui/editor/inspector/variableGroup.h b/Engine/source/gui/editor/inspector/variableGroup.h new file mode 100644 index 000000000..fd0370c99 --- /dev/null +++ b/Engine/source/gui/editor/inspector/variableGroup.h @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUI_INSPECTOR_VARIABLEGROUP_H_ +#define _GUI_INSPECTOR_VARIABLEGROUP_H_ + +#ifndef _GUI_INSPECTOR_GROUP_H_ +#include "gui/editor/inspector/group.h" +#endif + +// Forward refs +class GuiInspector; +class GuiInspectorField; + +class GuiInspectorVariableGroup : public GuiInspectorGroup +{ +public: + + typedef GuiInspectorGroup Parent; + + String mSearchString; + + GuiInspectorVariableGroup(); + virtual ~GuiInspectorVariableGroup(); + + DECLARE_CONOBJECT(GuiInspectorVariableGroup); + DECLARE_CATEGORY( "Gui Editor" ); + + virtual GuiInspectorField* constructField( S32 fieldType ); + + virtual bool inspectGroup(); + +protected: +}; + +#endif // _GUI_INSPECTOR_VARIABLEGROUP_H_ diff --git a/Engine/source/gui/editor/inspector/variableInspector.cpp b/Engine/source/gui/editor/inspector/variableInspector.cpp new file mode 100644 index 000000000..62993d600 --- /dev/null +++ b/Engine/source/gui/editor/inspector/variableInspector.cpp @@ -0,0 +1,67 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/editor/inspector/variableInspector.h" +#include "gui/editor/inspector/variableGroup.h" + +GuiVariableInspector::GuiVariableInspector() +{ +} + +GuiVariableInspector::~GuiVariableInspector() +{ +} + +IMPLEMENT_CONOBJECT(GuiVariableInspector); + +ConsoleDocClass( GuiVariableInspector, + "@brief GUI dedicated to variable viewing/manipulation\n\n" + "Mostly used in console system, internal use only.\n\n" + "@internal" +); + +void GuiVariableInspector::loadVars( String searchStr ) +{ + clearGroups(); + + GuiInspectorVariableGroup *group = new GuiInspectorVariableGroup(); + + group->setHeaderHidden( true ); + group->setCanCollapse( false ); + group->mParent = this; + group->setCaption( "Global Variables" ); + group->mSearchString = searchStr; + + if( group != NULL ) + { + group->registerObject(); + mGroups.push_back( group ); + addObject( group ); + } + + //group->inspectGroup(); +} + +ConsoleMethod( GuiVariableInspector, loadVars, void, 3, 3, "loadVars( searchString )" ) +{ + object->loadVars( argv[2] ); +} \ No newline at end of file diff --git a/Engine/source/gui/editor/inspector/variableInspector.h b/Engine/source/gui/editor/inspector/variableInspector.h new file mode 100644 index 000000000..c28196429 --- /dev/null +++ b/Engine/source/gui/editor/inspector/variableInspector.h @@ -0,0 +1,52 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUI_VARIABLEINSPECTOR_H_ +#define _GUI_VARIABLEINSPECTOR_H_ + +#ifndef _GUI_INSPECTOR_H_ +#include "gui/editor/guiInspector.h" +#endif + + +class GuiVariableInspector : public GuiInspector +{ + typedef GuiInspector Parent; + +public: + + GuiVariableInspector(); + virtual ~GuiVariableInspector(); + + DECLARE_CONOBJECT( GuiVariableInspector ); + DECLARE_CATEGORY( "Gui Editor" ); + + virtual void inspectObject( SimObject *object ) {} + + virtual void loadVars( String searchString ); + + +protected: + +}; + +#endif // _GUI_VARIABLEINSPECTOR_H_ \ No newline at end of file diff --git a/Engine/source/gui/game/guiChunkedBitmapCtrl.cpp b/Engine/source/gui/game/guiChunkedBitmapCtrl.cpp new file mode 100644 index 000000000..636171609 --- /dev/null +++ b/Engine/source/gui/game/guiChunkedBitmapCtrl.cpp @@ -0,0 +1,201 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "console/console.h" +#include "console/consoleTypes.h" +#include "gfx/bitmap/gBitmap.h" +#include "gui/core/guiControl.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxTextureHandle.h" +#include "gfx/gfxDrawUtil.h" +#include "console/engineAPI.h" + + +class GuiChunkedBitmapCtrl : public GuiControl +{ +private: + typedef GuiControl Parent; + void renderRegion(const Point2I &offset, const Point2I &extent); + +protected: + StringTableEntry mBitmapName; + GFXTexHandle mTexHandle; + bool mUseVariable; + bool mTile; + +public: + //creation methods + DECLARE_CONOBJECT(GuiChunkedBitmapCtrl); + DECLARE_CATEGORY( "Gui Images" ); + + GuiChunkedBitmapCtrl(); + static void initPersistFields(); + + //Parental methods + bool onWake(); + void onSleep(); + + void setBitmap(const char *name); + + void onRender(Point2I offset, const RectI &updateRect); +}; + +IMPLEMENT_CONOBJECT(GuiChunkedBitmapCtrl); + +ConsoleDocClass( GuiChunkedBitmapCtrl, + "@brief This is a control that will render a specified bitmap or a bitmap specified in a referenced variable.\n\n" + + "This control allows you to either set a bitmap with the \"bitmap\" field or with the setBitmap method. You can also choose " + "to reference a variable in the \"variable\" field such as \"$image\" and then set \"useVariable\" to true. This will cause it to " + "synchronize the variable with the bitmap displayed (if the variable holds a valid image). You can then change the variable and " + "effectively changed the displayed image.\n\n" + + "@tsexample\n" + "$image = \"anotherbackground.png\";\n" + "new GuiChunkedBitmapCtrl(ChunkedBitmap)\n" + "{\n" + " bitmap = \"background.png\";\n" + " variable = \"$image\";\n" + " useVariable = false;\n" + "}\n\n" + "// This will result in the control rendering \"background.png\"\n" + "// If we now set the useVariable to true it will now render \"anotherbackground.png\"\n" + "ChunkedBitmap.useVariable = true;\n" + "@endtsexample\n\n" + + "@see GuiControl::variable\n\n" + + "@ingroup GuiImages\n" +); + + +void GuiChunkedBitmapCtrl::initPersistFields() +{ + addGroup("GuiChunkedBitmapCtrl"); + addField( "bitmap", TypeFilename, Offset( mBitmapName, GuiChunkedBitmapCtrl ), "This is the bitmap to render to the control." ); + addField( "useVariable", TypeBool, Offset( mUseVariable, GuiChunkedBitmapCtrl ), "This decides whether to use the \"bitmap\" file " + "or a bitmap stored in \"variable\""); + addField( "tile", TypeBool, Offset( mTile, GuiChunkedBitmapCtrl ), "This is no longer in use"); + endGroup("GuiChunkedBitmapCtrl"); + Parent::initPersistFields(); +} + +DefineEngineMethod( GuiChunkedBitmapCtrl, setBitmap, void, (const char* filename),, + "@brief Set the image rendered in this control.\n\n" + "@param filename The image name you want to set\n" + "@tsexample\n" + "ChunkedBitmap.setBitmap(\"images/background.png\");" + "@endtsexample\n\n") +{ + object->setBitmap( filename ); +} + +GuiChunkedBitmapCtrl::GuiChunkedBitmapCtrl() +{ + mBitmapName = StringTable->insert(""); + mUseVariable = false; + mTile = false; +} + +void GuiChunkedBitmapCtrl::setBitmap(const char *name) +{ + bool awake = mAwake; + if(awake) + onSleep(); + + mBitmapName = StringTable->insert(name); + if(awake) + onWake(); + setUpdate(); +} + +bool GuiChunkedBitmapCtrl::onWake() +{ + if(!Parent::onWake()) + return false; + + if( !mTexHandle + && ( ( mBitmapName && mBitmapName[ 0 ] ) + || ( mUseVariable && mConsoleVariable && mConsoleVariable[ 0 ] ) ) ) + { + if ( mUseVariable ) + mTexHandle.set( Con::getVariable( mConsoleVariable ), &GFXDefaultGUIProfile, avar("%s() - mTexHandle (line %d)", __FUNCTION__, __LINE__) ); + else + mTexHandle.set( mBitmapName, &GFXDefaultGUIProfile, avar("%s() - mTexHandle (line %d)", __FUNCTION__, __LINE__) ); + } + + return true; +} + +void GuiChunkedBitmapCtrl::onSleep() +{ + mTexHandle = NULL; + Parent::onSleep(); +} + +void GuiChunkedBitmapCtrl::renderRegion(const Point2I &offset, const Point2I &extent) +{ +/* + U32 widthCount = mTexHandle.getTextureCountWidth(); + U32 heightCount = mTexHandle.getTextureCountHeight(); + if(!widthCount || !heightCount) + return; + + F32 widthScale = F32(extent.x) / F32(mTexHandle.getWidth()); + F32 heightScale = F32(extent.y) / F32(mTexHandle.getHeight()); + GFX->setBitmapModulation(ColorF(1,1,1)); + for(U32 i = 0; i < widthCount; i++) + { + for(U32 j = 0; j < heightCount; j++) + { + GFXTexHandle t = mTexHandle.getSubTexture(i, j); + RectI stretchRegion; + stretchRegion.point.x = (S32)(i * 256 * widthScale + offset.x); + stretchRegion.point.y = (S32)(j * 256 * heightScale + offset.y); + if(i == widthCount - 1) + stretchRegion.extent.x = extent.x + offset.x - stretchRegion.point.x; + else + stretchRegion.extent.x = (S32)((i * 256 + t.getWidth() ) * widthScale + offset.x - stretchRegion.point.x); + if(j == heightCount - 1) + stretchRegion.extent.y = extent.y + offset.y - stretchRegion.point.y; + else + stretchRegion.extent.y = (S32)((j * 256 + t.getHeight()) * heightScale + offset.y - stretchRegion.point.y); + GFX->drawBitmapStretch(t, stretchRegion); + } + } +*/ +} + + +void GuiChunkedBitmapCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + + if( mTexHandle ) + { + RectI boundsRect( offset, getExtent()); + GFX->getDrawUtil()->drawBitmapStretch( mTexHandle, boundsRect, GFXBitmapFlip_None, GFXTextureFilterLinear ); + } + + renderChildControls(offset, updateRect); +} diff --git a/Engine/source/gui/game/guiFadeinBitmapCtrl.cpp b/Engine/source/gui/game/guiFadeinBitmapCtrl.cpp new file mode 100644 index 000000000..18181d040 --- /dev/null +++ b/Engine/source/gui/game/guiFadeinBitmapCtrl.cpp @@ -0,0 +1,201 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/game/guiFadeinBitmapCtrl.h" +#include "console/console.h" +#include "console/consoleTypes.h" +#include "gfx/gfxDrawUtil.h" +#include "math/mathTypes.h" +#include "console/engineAPI.h" + +IMPLEMENT_CONOBJECT( GuiFadeinBitmapCtrl ); + +ConsoleDocClass( GuiFadeinBitmapCtrl, + "@brief A GUI control which renders a black square over a bitmap image. The black square will fade out, then fade back in after a determined time.\n" + "This control is especially useful for transitions and splash screens.\n\n" + + "@tsexample\n" + "new GuiFadeinBitmapCtrl()\n" + " {\n" + " fadeinTime = \"1000\";\n" + " waitTime = \"2000\";\n" + " fadeoutTime = \"1000\";\n" + " done = \"1\";\n" + " // Additional GUI properties that are not specific to GuiFadeinBitmapCtrl have been omitted from this example.\n" + " };\n" + "@endtsexample\n\n" + + "@see GuiBitmapCtrl\n\n" + "@ingroup GuiCore\n" +); + +IMPLEMENT_CALLBACK( GuiFadeinBitmapCtrl, click, void, (),(), + "@brief Informs the script level that this object received a Click event from the cursor or keyboard.\n\n" + "@tsexample\n" + "GuiFadeInBitmapCtrl::click(%this)\n" + " {\n" + " // Code to run when click occurs\n" + " }\n" + "@endtsexample\n\n" + "@see GuiCore\n\n" +); + +IMPLEMENT_CALLBACK( GuiFadeinBitmapCtrl, onDone, void, (),(), + "@brief Informs the script level that this object has completed is fade cycle.\n\n" + "@tsexample\n" + "GuiFadeInBitmapCtrl::onDone(%this)\n" + " {\n" + " // Code to run when the fade cycle completes\n" + " }\n" + "@endtsexample\n\n" + "@see GuiCore\n\n" +); + +//----------------------------------------------------------------------------- + +GuiFadeinBitmapCtrl::GuiFadeinBitmapCtrl() + : mFadeColor( 0.f, 0.f, 0.f ), + mStartTime( 0 ), + mFadeInTime( 1000 ), + mWaitTime( 2000 ), + mFadeOutTime( 1000 ), + mDone( false ) +{ +} + +//----------------------------------------------------------------------------- + +void GuiFadeinBitmapCtrl::initPersistFields() +{ + addGroup( "Fading" ); + + addField( "fadeColor", TypeColorF, Offset( mFadeColor, GuiFadeinBitmapCtrl ), + "Color to fade in from and fade out to." ); + addField( "fadeInTime", TypeS32, Offset( mFadeInTime, GuiFadeinBitmapCtrl ), + "Milliseconds for the bitmap to fade in." ); + addField( "waitTime", TypeS32, Offset( mWaitTime, GuiFadeinBitmapCtrl ), + "Milliseconds to wait after fading in before fading out the bitmap." ); + addField( "fadeOutTime", TypeS32, Offset( mFadeOutTime, GuiFadeinBitmapCtrl ), + "Milliseconds for the bitmap to fade out." ); + addField( "fadeInEase", TypeEaseF, Offset( mFadeInEase, GuiFadeinBitmapCtrl ), + "Easing curve for fade-in." ); + addField( "fadeOutEase", TypeEaseF, Offset( mFadeOutEase, GuiFadeinBitmapCtrl ), + "Easing curve for fade-out." ); + addField( "done", TypeBool, Offset( mDone, GuiFadeinBitmapCtrl ), + "Whether the fade cycle has finished running." ); + + endGroup( "Fading" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +void GuiFadeinBitmapCtrl::onPreRender() +{ + Parent::onPreRender(); + setUpdate(); +} + +//----------------------------------------------------------------------------- + +void GuiFadeinBitmapCtrl::onMouseDown(const GuiEvent &) +{ + click_callback(); +} + +//----------------------------------------------------------------------------- + +bool GuiFadeinBitmapCtrl::onKeyDown(const GuiEvent &) +{ + click_callback(); + return true; +} + +//----------------------------------------------------------------------------- + +bool GuiFadeinBitmapCtrl::onWake() +{ + if( !Parent::onWake() ) + return false; + + // Reset reference time. + mStartTime = 0; + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiFadeinBitmapCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + Parent::onRender(offset, updateRect); + + // Set reference time if we haven't already. This is done here when rendering + // starts so that we begin counting from the time the control is actually + // visible rather than from its onWake() (which may be a considerable time + // before the control actually gets to render). + + if( !mStartTime ) + mStartTime = Platform::getRealMilliseconds(); + + // Compute overlay alpha. + + U32 elapsed = Platform::getRealMilliseconds() - mStartTime; + + U32 alpha; + if( elapsed < mFadeInTime ) + { + // fade-in + alpha = 255.f * ( 1.0f - mFadeInEase.getValue( elapsed, 0.f, 1.f, mFadeInTime ) ); + } + else if( elapsed < ( mFadeInTime + mWaitTime ) ) + { + // wait + alpha = 0; + } + else if( elapsed < ( mFadeInTime + mWaitTime + mFadeOutTime ) ) + { + // fade out + elapsed -= ( mFadeInTime + mWaitTime ); + alpha = mFadeOutEase.getValue( elapsed, 0.f, 255.f, mFadeOutTime ); + } + else + { + // done state + alpha = mFadeOutTime ? 255 : 0; + mDone = true; + + // Trigger onDone callback except when in Gui Editor. + + if( !smDesignTime ) + onDone_callback(); + } + + // Render overlay on top of bitmap. + + ColorI color = mFadeColor; + color.alpha = alpha; + + GFX->getDrawUtil()->drawRectFill( offset, getExtent() + offset, color ); +} diff --git a/Engine/source/gui/game/guiFadeinBitmapCtrl.h b/Engine/source/gui/game/guiFadeinBitmapCtrl.h new file mode 100644 index 000000000..d65b08dd1 --- /dev/null +++ b/Engine/source/gui/game/guiFadeinBitmapCtrl.h @@ -0,0 +1,89 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIFADEINBITMAPCTRL_H_ +#define _GUIFADEINBITMAPCTRL_H_ + +#ifndef _GUIBITMAPCTRL_H_ + #include "gui/controls/guiBitmapCtrl.h" +#endif +#ifndef _MEASE_H_ + #include "math/mEase.h" +#endif + + +/// A control that fades a bitmap in and out. +class GuiFadeinBitmapCtrl : public GuiBitmapCtrl +{ + public: + + typedef GuiBitmapCtrl Parent; + + protected: + + /// Color we fade in from and fade out to. + ColorF mFadeColor; + + /// Reference time on which to base all fade timings. + U32 mStartTime; + + /// Milliseconds for bitmap to fade in. + U32 mFadeInTime; + + /// Milliseconds to wait before fade-out. + U32 mWaitTime; + + /// Milliseconds for bitmap to fade out. + U32 mFadeOutTime; + + /// Easing curve for fade-in. + EaseF mFadeInEase; + + /// Easing curve for fade-out. + EaseF mFadeOutEase; + + /// Whether the fade cycle has run completely. + bool mDone; + + public: + + GuiFadeinBitmapCtrl(); + + // GuiControl. + virtual void onPreRender(); + virtual void onMouseDown(const GuiEvent &); + virtual bool onKeyDown(const GuiEvent &); + virtual bool onWake(); + virtual void onRender(Point2I offset, const RectI &updateRect); + + static void initPersistFields(); + + DECLARE_CONOBJECT( GuiFadeinBitmapCtrl ); + DECLARE_DESCRIPTION( "A control that shows a bitmap. It fades the bitmap in a set amount of time,\n" + "then waits a set amount of time, and finally fades the bitmap back out in\n" + "another set amount of time." ); + + DECLARE_CALLBACK( void, click, ()); + DECLARE_CALLBACK( void, onDone, ()); +}; + +#endif // !_GUIFADEINBITMAPCTRL_H_ diff --git a/Engine/source/gui/game/guiIdleCamFadeBitmapCtrl.cpp b/Engine/source/gui/game/guiIdleCamFadeBitmapCtrl.cpp new file mode 100644 index 000000000..641f629cb --- /dev/null +++ b/Engine/source/gui/game/guiIdleCamFadeBitmapCtrl.cpp @@ -0,0 +1,189 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/controls/guiBitmapCtrl.h" + +#include "console/console.h" +#include "console/consoleTypes.h" +#include "gfx/gfxDrawUtil.h" + + +class GuiIdleCamFadeBitmapCtrl : public GuiBitmapCtrl +{ + typedef GuiBitmapCtrl Parent; +public: + DECLARE_CONOBJECT(GuiIdleCamFadeBitmapCtrl); + DECLARE_CATEGORY( "Gui Images" ); + + U32 wakeTime; + bool done; + U32 fadeinTime; + U32 fadeoutTime; + bool doFadeIn; + bool doFadeOut; + + GuiIdleCamFadeBitmapCtrl() + { + wakeTime = 0; + fadeinTime = 1000; + fadeoutTime = 1000; + done = false; + + doFadeIn = false; + doFadeOut = false; + } + void onPreRender() + { + Parent::onPreRender(); + setUpdate(); + } + void onMouseDown(const GuiEvent &) + { + Con::executef(this, "click"); + } + bool onKeyDown(const GuiEvent &) + { + Con::executef(this, "click"); + return true; + } + bool onWake() + { + if(!Parent::onWake()) + return false; + wakeTime = Platform::getRealMilliseconds(); + return true; + } + + void fadeIn() + { + wakeTime = Platform::getRealMilliseconds(); + doFadeIn = true; + doFadeOut = false; + done = false; + } + + void fadeOut() + { + wakeTime = Platform::getRealMilliseconds(); + doFadeIn = false; + doFadeOut = true; + done = false; + } + + void onRender(Point2I offset, const RectI &updateRect) + { + U32 elapsed = Platform::getRealMilliseconds() - wakeTime; + + U32 alpha; + if (doFadeOut && elapsed < fadeoutTime) + { + // fade out + alpha = 255 - (255 * (F32(elapsed) / F32(fadeoutTime))); + } + else if (doFadeIn && elapsed < fadeinTime) + { + // fade in + alpha = 255 * F32(elapsed) / F32(fadeinTime); + } + else + { + // done state + alpha = doFadeIn ? 255 : 0; + done = true; + } + + ColorI color(255,255,255,alpha); + if (mTextureObject) + { + GFX->getDrawUtil()->setBitmapModulation(color); + + if(mWrap) + { + + GFXTextureObject* texture = mTextureObject; + RectI srcRegion; + RectI dstRegion; + float xdone = ((float)getExtent().x/(float)texture->mBitmapSize.x)+1; + float ydone = ((float)getExtent().y/(float)texture->mBitmapSize.y)+1; + + int xshift = mStartPoint.x%texture->mBitmapSize.x; + int yshift = mStartPoint.y%texture->mBitmapSize.y; + for(int y = 0; y < ydone; ++y) + for(int x = 0; x < xdone; ++x) + { + srcRegion.set(0,0,texture->mBitmapSize.x,texture->mBitmapSize.y); + dstRegion.set( ((texture->mBitmapSize.x*x)+offset.x)-xshift, + ((texture->mBitmapSize.y*y)+offset.y)-yshift, + texture->mBitmapSize.x, + texture->mBitmapSize.y); + GFX->getDrawUtil()->drawBitmapStretchSR(texture,dstRegion, srcRegion); + } + + } + else + { + RectI rect(offset, getExtent()); + GFX->getDrawUtil()->drawBitmapStretch(mTextureObject, rect); + } + } + + if (mProfile->mBorder || !mTextureObject) + { + RectI rect(offset.x, offset.y, getExtent().x, getExtent().y); + ColorI borderCol(mProfile->mBorderColor); + borderCol.alpha = alpha; + GFX->getDrawUtil()->drawRect(rect, borderCol); + } + + renderChildControls(offset, updateRect); + } + + static void initPersistFields() + { + addField("fadeinTime", TypeS32, Offset(fadeinTime, GuiIdleCamFadeBitmapCtrl)); + addField("fadeoutTime", TypeS32, Offset(fadeoutTime, GuiIdleCamFadeBitmapCtrl)); + addField("done", TypeBool, Offset(done, GuiIdleCamFadeBitmapCtrl)); + Parent::initPersistFields(); + } +}; + +IMPLEMENT_CONOBJECT(GuiIdleCamFadeBitmapCtrl); + +ConsoleDocClass( GuiIdleCamFadeBitmapCtrl, + "@brief GUI that will fade the current view in and out.\n\n" + "Main difference between this and FadeinBitmap is this appears to " + "fade based on the source texture.\n\n" + "This is going to be deprecated, and any useful code ported to FadeinBitmap\n\n" + "@internal"); + +ConsoleMethod(GuiIdleCamFadeBitmapCtrl, fadeIn, void, 2, 2, "()" + "@internal") +{ + object->fadeIn(); +} + +ConsoleMethod(GuiIdleCamFadeBitmapCtrl, fadeOut, void, 2, 2, "()" + "@internal") +{ + object->fadeOut(); +} diff --git a/Engine/source/gui/game/guiMessageVectorCtrl.cpp b/Engine/source/gui/game/guiMessageVectorCtrl.cpp new file mode 100644 index 000000000..9f5f45800 --- /dev/null +++ b/Engine/source/gui/game/guiMessageVectorCtrl.cpp @@ -0,0 +1,918 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/game/guiMessageVectorCtrl.h" + +#include "gui/utility/messageVector.h" +#include "console/consoleTypes.h" +#include "gui/containers/guiScrollCtrl.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxDrawUtil.h" +#include "console/engineAPI.h" + +IMPLEMENT_CONOBJECT(GuiMessageVectorCtrl); + +ConsoleDocClass( GuiMessageVectorCtrl, + "@brief A chat HUD control that displays messages from a MessageVector.\n\n" + + "This renders messages from a MessageVector; the important thing " + "here is that the MessageVector holds all the messages we care " + "about, while we can destroy and create these GUI controls as " + "needed.\n\n" + + "@tsexample\n" + "// Declare ChatHud, which is what will display the actual chat from a MessageVector\n" + "new GuiMessageVectorCtrl(ChatHud) {\n" + " profile = \"ChatHudMessageProfile\";\n" + " horizSizing = \"width\";\n" + " vertSizing = \"height\";\n" + " position = \"1 1\";\n" + " extent = \"252 16\";\n" + " minExtent = \"8 8\";\n" + " visible = \"1\";\n" + " helpTag = \"0\";\n" + " lineSpacing = \"0\";\n" + " lineContinuedIndex = \"10\";\n" + " matchColor = \"0 0 255 255\";\n" + " maxColorIndex = \"5\";\n" + "};\n\n" + "// All messages are stored in this HudMessageVector, the actual\n" + "// MainChatHud only displays the contents of this vector.\n" + "new MessageVector(HudMessageVector);\n\n" + "// Attach the MessageVector to the chat control\n" + "chatHud.attach(HudMessageVector);\n" + "@endtsexample\n\n" + + "@see MessageVector for more details on how this is used\n" + + "@ingroup GuiUtil\n"); + + +//-------------------------------------- Console functions +DefineEngineMethod( GuiMessageVectorCtrl, attach, bool, ( MessageVector* item),, + "@brief Push a line onto the back of the list.\n\n" + + "@param item The GUI element being pushed into the control\n\n" + + "@tsexample\n" + "// All messages are stored in this HudMessageVector, the actual\n" + "// MainChatHud only displays the contents of this vector.\n" + "new MessageVector(HudMessageVector);\n\n" + "// Attach the MessageVector to the chat control\n" + "chatHud.attach(HudMessageVector);\n" + "@endtsexample\n\n" + + "@return Value") +{ + if (item == NULL) + { + Con::errorf(ConsoleLogEntry::General, "Could not find MessageVector: %s", item); + return false; + } + + return object->attach(item); +} + +//ConsoleMethod(GuiMessageVectorCtrl, attach, bool, 3, 3, "(MessageVector item)" +// "Make this gui control display messages from the specified MessageVector") +//{ +// MessageVector* pMV = NULL; +// Sim::findObject(argv[2], pMV); +// if (pMV == NULL) { +// Con::errorf(ConsoleLogEntry::General, "Could not find MessageVector: %s", argv[2]); +// return false; +// } +// +// return object->attach(pMV); +//} + +DefineEngineMethod( GuiMessageVectorCtrl, detach, void, (),, + "@brief Stop listing messages from the MessageVector previously attached to, if any.\n\n" + + "Detailed description\n\n" + + "@param param Description\n\n" + + "@tsexample\n" + "// Deatch the MessageVector from HudMessageVector\n" + "// HudMessageVector will no longer render the text\n" + "chatHud.detach();\n" + "@endtsexample\n\n") +{ + if (object->isAttached() == false) + { + Con::warnf(ConsoleLogEntry::General, "GuiMessageVectorCtrl: double detach"); + return; + } + + object->detach(); +} + +//ConsoleMethod(GuiMessageVectorCtrl, detach, void, 2, 2, "()" +// "Stop listing messages from the MessageVector previously attached to, if any.") +//{ +// if (object->isAttached() == false) { +// Con::warnf(ConsoleLogEntry::General, "GuiMessageVectorCtrl: double detach"); +// return; +// } +// +// object->detach(); +//} + +struct TempLineBreak +{ + S32 start; + S32 end; +}; + +//-------------------------------------------------------------------------- +// Callback for messageVector +void sMVCtrlCallback(void * spectatorKey, + const MessageVector::MessageCode code, + const U32 argument) +{ + GuiMessageVectorCtrl* pMVC = reinterpret_cast(spectatorKey); + pMVC->callbackRouter(code, argument); +} + + +//-------------------------------------------------------------------------- +GuiMessageVectorCtrl::GuiMessageVectorCtrl() +{ + VECTOR_SET_ASSOCIATION(mLineWrappings); + VECTOR_SET_ASSOCIATION(mSpecialMarkers); + VECTOR_SET_ASSOCIATION(mLineElements); + + mMessageVector = NULL; + mLineSpacingPixels = 0; + mLineContinuationIndent = 10; + + mMouseDown = false; + mMouseSpecialLine = -1; + mMouseSpecialRef = -1; + + for (U32 i = 0; i < 16; i++) + mAllowedMatches[i] = ""; + mSpecialColor.set(0, 0, 255); + + mMaxColorIndex = 9; +} + + +//-------------------------------------------------------------------------- +GuiMessageVectorCtrl::~GuiMessageVectorCtrl() +{ + AssertFatal(mLineWrappings.size() == 0, "Error, line wrappings not properly cleared!"); + AssertFatal(mSpecialMarkers.size() == 0, "Error, special markers not properly cleared!"); + AssertFatal(mLineElements.size() == 0, "Error, line elements not properly cleared!"); +} + + +//-------------------------------------------------------------------------- +void GuiMessageVectorCtrl::initPersistFields() +{ + addField("lineSpacing", TypeS32, Offset(mLineSpacingPixels, GuiMessageVectorCtrl)); + addField("lineContinuedIndex", TypeS32, Offset(mLineContinuationIndent, GuiMessageVectorCtrl)); + addField("allowedMatches", TypeString, Offset(mAllowedMatches, GuiMessageVectorCtrl), 16); + addField("matchColor", TypeColorI, Offset(mSpecialColor, GuiMessageVectorCtrl)); + addField("maxColorIndex", TypeS32, Offset(mMaxColorIndex, GuiMessageVectorCtrl)); + Parent::initPersistFields(); +} + + +bool GuiMessageVectorCtrl::onAdd() +{ + return Parent::onAdd(); +} + + +void GuiMessageVectorCtrl::onRemove() +{ + Parent::onRemove(); +} + + +//-------------------------------------------------------------------------- +bool GuiMessageVectorCtrl::isAttached() const +{ + return (mMessageVector != NULL); +} + + +//-------------------------------------------------------------------------- +bool GuiMessageVectorCtrl::attach(MessageVector* newAttachment) +{ + AssertFatal(newAttachment, "No attachment!"); + if (newAttachment == NULL || !isAwake()) + return false; + + if (isAttached()) { + Con::warnf(ConsoleLogEntry::General, "GuiMessageVectorCtrl::attach: overriding attachment"); + detach(); + } + AssertFatal(mLineWrappings.size() == 0, "Error, line wrappings not properly cleared!"); + + mMessageVector = newAttachment; + mMessageVector->registerSpectator(sMVCtrlCallback, this); + + return true; +} + +//-------------------------------------------------------------------------- +void GuiMessageVectorCtrl::detach() +{ + if (isAttached() == false) { + Con::warnf(ConsoleLogEntry::General, "GuiMessageVectorCtrl::detach: not attached!"); + return; + } + + mMessageVector->unregisterSpectator(this); + mMessageVector = NULL; + AssertFatal(mLineWrappings.size() == 0, "Error, line wrappings not properly cleared!"); +} + + +//-------------------------------------------------------------------------- +void GuiMessageVectorCtrl::lineInserted(const U32 arg) +{ + AssertFatal(mMessageVector != NULL, "Should not be here unless we're attached!"); + + GuiScrollCtrl* pScroll = dynamic_cast(getParent()); + bool fullyScrolled = pScroll->isScrolledToBottom(); + + mSpecialMarkers.insert(arg); + createSpecialMarkers(mSpecialMarkers[arg], mMessageVector->getLine(arg).message); + + mLineWrappings.insert(arg); + createLineWrapping(mLineWrappings[arg], mMessageVector->getLine(arg).message); + + mLineElements.insert(arg); + createLineElement(mLineElements[arg], mLineWrappings[arg], mSpecialMarkers[arg]); + + U32 numLines = 0; + for (U32 i = 0; i < mLineWrappings.size(); i++) { + // We need to rebuild the physicalLineStart markers at the same time as + // we find out how many of them are left... + mLineElements[i].physicalLineStart = numLines; + + numLines += mLineWrappings[i].numLines; + } + + Point2I newExtent = getExtent(); + newExtent.y = (mProfile->mFont->getHeight() + mLineSpacingPixels) * getMax(numLines, U32(1)); + setExtent(newExtent); + if(fullyScrolled) + pScroll->scrollTo(0, 0x7FFFFFFF); +} + + +void GuiMessageVectorCtrl::lineDeleted(const U32 arg) +{ + AssertFatal(mMessageVector != NULL, "Should not be here unless we're attached!"); + AssertFatal(arg < mLineWrappings.size(), "Error, out of bounds line deleted!"); + + // It's a somewhat involved process to delete the lineelements... + LineElement& rElement = mLineElements[arg]; + + TextElement* walk = rElement.headLineElements; + while (walk != NULL) { + TextElement* lineWalk = walk->nextInLine; + while (lineWalk != NULL) { + TextElement* temp = lineWalk; + lineWalk = lineWalk->nextPhysicalLine; + delete temp; + } + + TextElement* temp = walk; + walk = walk->nextPhysicalLine; + delete temp; + } + rElement.headLineElements = NULL; + mLineElements.erase(arg); + + delete [] mLineWrappings[arg].startEndPairs; + mLineWrappings.erase(arg); + + delete [] mSpecialMarkers[arg].specials; + mSpecialMarkers.erase(arg); + + U32 numLines = 0; + for (U32 i = 0; i < mLineWrappings.size(); i++) { + // We need to rebuild the physicalLineStart markers at the same time as + // we find out how many of them are left... + mLineElements[i].physicalLineStart = numLines; + + numLines += mLineWrappings[i].numLines; + } + + U32 newHeight = (mProfile->mFont->getHeight() + mLineSpacingPixels) * getMax(numLines, U32(1)); + resize(getPosition(), Point2I(getWidth(), newHeight)); +} + + +void GuiMessageVectorCtrl::vectorDeleted() +{ + AssertFatal(mMessageVector != NULL, "Should not be here unless we're attached!"); + AssertFatal(mLineWrappings.size() == 0, "Error, line wrappings not properly cleared out!"); + + mMessageVector = NULL; + U32 newHeight = mProfile->mFont->getHeight() + mLineSpacingPixels; + resize(getPosition(), Point2I(getWidth(), newHeight)); +} + + +void GuiMessageVectorCtrl::callbackRouter(const MessageVector::MessageCode code, + const U32 arg) +{ + switch (code) { + case MessageVector::LineInserted: + lineInserted(arg); + break; + case MessageVector::LineDeleted: + lineDeleted(arg); + break; + case MessageVector::VectorDeletion: + vectorDeleted(); + break; + } +} + + +//-------------------------------------------------------------------------- +void GuiMessageVectorCtrl::createSpecialMarkers(SpecialMarkers& rSpecial, const char* string) +{ + // The first thing we need to do is create a version of the string with no uppercase + // chars for matching... + + String pLCCopyStr = String::ToLower( string ); + const char* pLCCopy = pLCCopyStr.c_str(); + + Vector tempSpecials(__FILE__, __LINE__); + Vector tempTypes(__FILE__, __LINE__); + + const char* pCurr = pLCCopy; + while (pCurr[0] != '\0') { + const char* pMinMatch = &pLCCopy[dStrlen(string)]; + U32 minMatchType = 0xFFFFFFFF; + AssertFatal(pMinMatch[0] == '\0', "Error, bad positioning of sentry..."); + + // Find the earliest match + for (U32 i = 0; i < 16; i++) { + if (mAllowedMatches[i][0] == '\0') + continue; + + const char* pMatch = dStrstr(pCurr, mAllowedMatches[i]); + if (pMatch != NULL && pMatch < pMinMatch) { + pMinMatch = pMatch; + minMatchType = i; + } + } + + if (pMinMatch[0] != '\0') { + AssertFatal(minMatchType != 0xFFFFFFFF, "Hm, that's bad"); + // Found a match => now find the end + U32 start = pMinMatch - pLCCopy; + U32 j; + for (j = 1; pLCCopy[start + j] != '\0'; j++) { + if (pLCCopy[start + j] == '\n' || + pLCCopy[start + j] == ' ' || + pLCCopy[start + j] == '\t') + break; + } + AssertFatal(j > 0, "Error, j must be > 0 at this point!"); + U32 end = start + j - 1; + + tempSpecials.increment(); + tempSpecials.last().start = start; + tempSpecials.last().end = end; + tempTypes.push_back(minMatchType); + + pCurr = &pLCCopy[end + 1]; + } else { + // No match. This will cause the while loop to terminate... + pCurr = pMinMatch; + } + } + + if ((rSpecial.numSpecials = tempSpecials.size()) != 0) { + rSpecial.specials = new SpecialMarkers::Special[tempSpecials.size()]; + for (U32 i = 0; i < tempSpecials.size(); i++) { + rSpecial.specials[i].start = tempSpecials[i].start; + rSpecial.specials[i].end = tempSpecials[i].end; + rSpecial.specials[i].specialType = tempTypes[i]; + } + } else { + rSpecial.specials = NULL; + } +} + + +//-------------------------------------------------------------------------- +void GuiMessageVectorCtrl::createLineWrapping(LineWrapping& rWrapping, const char* string) +{ + Vector tempBreaks(__FILE__, __LINE__); + + U32 i; + U32 currStart = 0; + U32 length = dStrlen(string); + if (length != 0) { + for (i = 0; i < length; i++) { + if (string[i] == '\n') { + tempBreaks.increment(); + tempBreaks.last().start = currStart; + tempBreaks.last().end = i-1; + currStart = i+1; + } else if (i == length - 1) { + tempBreaks.increment(); + tempBreaks.last().start = currStart; + tempBreaks.last().end = i; + currStart = i+1; + } + } + } else { + tempBreaks.increment(); + tempBreaks.last().start = 0; + tempBreaks.last().end = -1; + } + + U32 splitWidth = getWidth(); + U32 currLine = 0; + while (currLine < tempBreaks.size()) { + TempLineBreak& rLine = tempBreaks[currLine]; + if (rLine.start >= rLine.end) { + if (currLine == 0) + splitWidth -= mLineContinuationIndent; + currLine++; + continue; + } + + // Ok, there's some actual text in this line. How long is it? + U32 baseLength = mProfile->mFont->getStrNWidthPrecise((const UTF8 *)&string[rLine.start], rLine.end-rLine.start+1); + if (baseLength > splitWidth) { + // DMMNOTE: Replace with bin search eventually + U32 currPos = 0; + U32 breakPos = 0; + for (currPos = 0; currPos < rLine.end-rLine.start+1; currPos++) { + U32 currLength = mProfile->mFont->getStrNWidthPrecise((const UTF8 *)&string[rLine.start], currPos+1); + if (currLength > splitWidth) + { + // Make sure that the currPos has advanced, then set the breakPoint. + breakPos = currPos != 0 ? currPos - 1 : 0; + break; + } + } + if (currPos == rLine.end-rLine.start+1) { + AssertFatal(false, "Error, if the line must be broken, the position must be before this point!"); + currLine++; + continue; + } + + // Ok, the character at breakPos is the last valid char we can render. We + // want to scan back to the first whitespace character (which, in the bounds + // of the line, is guaranteed to be a space or a tab). + U32 originalBreak = breakPos; + while (true) { + if (string[rLine.start + breakPos] == ' ' || string[rLine.start + breakPos] == '\t') { + break; + } else { + AssertFatal(string[rLine.start + breakPos] != '\n', + "Bad characters in line range..."); + if (breakPos == 0) { + breakPos = originalBreak; + break; + } + breakPos--; + } + } + + // Ok, everything up to and including breakPos is in the currentLine. Insert + // a new line at this point, and put everything after in that line. + S32 oldStart = rLine.start; + S32 oldEnd = rLine.end; + rLine.end = rLine.start + breakPos; + + // Note that rLine is NOTNOTNOTNOT valid after this point! + tempBreaks.insert(currLine+1); + tempBreaks[currLine+1].start = oldStart + breakPos + 1; + tempBreaks[currLine+1].end = oldEnd; + } + + if (currLine == 0) + splitWidth -= mLineContinuationIndent; + currLine++; + } + + rWrapping.numLines = tempBreaks.size(); + rWrapping.startEndPairs = new LineWrapping::SEPair[tempBreaks.size()]; + for (i = 0; i < tempBreaks.size(); i++) { + rWrapping.startEndPairs[i].start = tempBreaks[i].start; + rWrapping.startEndPairs[i].end = tempBreaks[i].end; + } +} + +//-------------------------------------------------------------------------- +void GuiMessageVectorCtrl::createLineElement(LineElement& rElement, + LineWrapping& rWrapping, + SpecialMarkers& rSpecial) +{ + // First, do a straighforward translation of the wrapping... + TextElement** ppWalk = &rElement.headLineElements; + for (U32 i = 0; i < rWrapping.numLines; i++) { + *ppWalk = new TextElement; + + (*ppWalk)->nextInLine = NULL; + (*ppWalk)->nextPhysicalLine = NULL; + (*ppWalk)->specialReference = -1; + + (*ppWalk)->start = rWrapping.startEndPairs[i].start; + (*ppWalk)->end = rWrapping.startEndPairs[i].end; + + ppWalk = &((*ppWalk)->nextPhysicalLine); + } + + if (rSpecial.numSpecials != 0) { + // Ok. Now, walk down the lines, and split the elements into their constituent parts... + // + TextElement* walk = rElement.headLineElements; + while (walk) { + TextElement* walkAcross = walk; + while (walkAcross) { + S32 specialMatch = -1; + for (U32 i = 0; i < rSpecial.numSpecials; i++) { + if (walkAcross->start <= rSpecial.specials[i].end && + walkAcross->end >= rSpecial.specials[i].start) { + specialMatch = i; + break; + } + } + + if (specialMatch != -1) { + // We have a match here. Break it into the appropriate number of segments. + // this will vary depending on how the overlap is occuring... + if (walkAcross->start >= rSpecial.specials[specialMatch].start) { + if (walkAcross->end <= rSpecial.specials[specialMatch].end) { + // The whole thing is part of the special + AssertFatal(walkAcross->nextInLine == NULL, "Bad assumption!"); + walkAcross->specialReference = specialMatch; + } else { + // The first part is in the special, the tail is out + AssertFatal(walkAcross->nextInLine == NULL, "Bad assumption!"); + walkAcross->nextInLine = new TextElement; + walkAcross->nextInLine->nextInLine = NULL; + walkAcross->nextInLine->nextPhysicalLine = NULL; + walkAcross->nextInLine->specialReference = -1; + + walkAcross->specialReference = specialMatch; + walkAcross->nextInLine->end = walkAcross->end; + walkAcross->end = rSpecial.specials[specialMatch].end; + walkAcross->nextInLine->start = rSpecial.specials[specialMatch].end + 1; + + AssertFatal(walkAcross->end >= walkAcross->start && walkAcross->nextInLine->end >= walkAcross->nextInLine->start, "Bad textelements generated!"); + } + + walkAcross = walkAcross->nextInLine; + } else { + if (walkAcross->end <= rSpecial.specials[specialMatch].end) { + // The first part is out of the special, the second part in. + AssertFatal(walkAcross->nextInLine == NULL, "Bad assumption!"); + walkAcross->nextInLine = new TextElement; + walkAcross->nextInLine->nextInLine = NULL; + walkAcross->nextInLine->nextPhysicalLine = NULL; + walkAcross->nextInLine->specialReference = specialMatch; + + walkAcross->specialReference = -1; + walkAcross->nextInLine->end = walkAcross->end; + walkAcross->end = rSpecial.specials[specialMatch].start - 1; + walkAcross->nextInLine->start = rSpecial.specials[specialMatch].start; + + AssertFatal(walkAcross->end >= walkAcross->start && walkAcross->nextInLine->end >= walkAcross->nextInLine->start, "Bad textelements generated!"); + walkAcross = walkAcross->nextInLine; + } else { + // First out, middle in, last out. Oy. + AssertFatal(walkAcross->nextInLine == NULL, "Bad assumption!"); + walkAcross->nextInLine = new TextElement; + walkAcross->nextInLine->nextInLine = NULL; + walkAcross->nextInLine->nextPhysicalLine = NULL; + walkAcross->nextInLine->specialReference = specialMatch; + + walkAcross->nextInLine->nextInLine = new TextElement; + walkAcross->nextInLine->nextInLine->nextInLine = NULL; + walkAcross->nextInLine->nextInLine->nextPhysicalLine = NULL; + walkAcross->nextInLine->nextInLine->specialReference = -1; + + walkAcross->nextInLine->start = rSpecial.specials[specialMatch].start; + walkAcross->nextInLine->end = rSpecial.specials[specialMatch].end; + walkAcross->nextInLine->nextInLine->start = rSpecial.specials[specialMatch].end+1; + walkAcross->nextInLine->nextInLine->end = walkAcross->end; + walkAcross->end = walkAcross->nextInLine->start - 1; + AssertFatal((walkAcross->end >= walkAcross->start && + walkAcross->nextInLine->end >= walkAcross->nextInLine->start && + walkAcross->nextInLine->nextInLine->end >= walkAcross->nextInLine->nextInLine->start), + "Bad textelements generated!"); + walkAcross = walkAcross->nextInLine->nextInLine; + } + } + } else { + walkAcross = walkAcross->nextInLine; + } + } + + walk = walk->nextPhysicalLine; + } + } +} + + +//-------------------------------------------------------------------------- +bool GuiMessageVectorCtrl::onWake() +{ + if (Parent::onWake() == false) + return false; + + if (bool(mProfile->mFont) == false) + return false; + + mMinSensibleWidth = 1; + + for (U32 i = 0; i < 256; i++) { + if (mProfile->mFont->isValidChar(U8(i))) { + if (mProfile->mFont->getCharWidth(U8(i)) > mMinSensibleWidth) + mMinSensibleWidth = mProfile->mFont->getCharWidth(U8(i)); + } + } + + AssertFatal(mLineWrappings.size() == 0, "Error, line wrappings not properly cleared!"); + return true; +} + + +//-------------------------------------------------------------------------- +void GuiMessageVectorCtrl::onSleep() +{ + if (isAttached()) + detach(); + + Parent::onSleep(); +} + + +//-------------------------------------------------------------------------- +void GuiMessageVectorCtrl::onRender(Point2I offset, + const RectI& updateRect) +{ + GFXDrawUtil *drawer = GFX->getDrawUtil(); + + Parent::onRender(offset, updateRect); + if (isAttached()) { + U32 linePixels = mProfile->mFont->getHeight() + mLineSpacingPixels; + U32 currLine = 0; + ColorI lastColor = mProfile->mFontColor; + for (U32 i = 0; i < mMessageVector->getNumLines(); i++) { + + TextElement* pElement = mLineElements[i].headLineElements; + while (pElement != NULL) { + Point2I localStart(pElement == mLineElements[i].headLineElements ? 0 : mLineContinuationIndent, currLine * linePixels); + + Point2I globalCheck = localToGlobalCoord(localStart); + U32 globalRangeStart = globalCheck.y; + U32 globalRangeEnd = globalCheck.y + mProfile->mFont->getHeight(); + if (globalRangeStart > updateRect.point.y + updateRect.extent.y || + globalRangeEnd < updateRect.point.y) { + currLine++; + pElement = pElement->nextPhysicalLine; + continue; + } + + TextElement* walkAcross = pElement; + while (walkAcross) { + if (walkAcross->start > walkAcross->end) + break; + + Point2I globalStart = localToGlobalCoord(localStart); + + U32 strWidth; + if (walkAcross->specialReference == -1) { + drawer->setBitmapModulation(lastColor); + drawer->setTextAnchorColor(mProfile->mFontColor); + strWidth = drawer->drawTextN(mProfile->mFont, globalStart, &mMessageVector->getLine(i).message[walkAcross->start], + walkAcross->end - walkAcross->start + 1, mProfile->mFontColors, mMaxColorIndex); + drawer->getBitmapModulation(&lastColor); // in case an embedded color tag changed it + } else { + drawer->getBitmapModulation( &lastColor ); + drawer->setBitmapModulation(mSpecialColor); + drawer->setTextAnchorColor(mProfile->mFontColor); + strWidth = drawer->drawTextN(mProfile->mFont, globalStart, &mMessageVector->getLine(i).message[walkAcross->start], + walkAcross->end - walkAcross->start + 1); + + // in case we have 2 in a row... + drawer->setBitmapModulation(lastColor); + } + + // drawTextN returns the rightmost X coord, so subtract leftmost coord to get the width + strWidth -= globalStart.x; + + if (walkAcross->specialReference != -1) { + Point2I lineStart = localStart; + Point2I lineEnd = localStart; + lineStart.y += mProfile->mFont->getBaseline() + 1; + lineEnd.x += strWidth; + lineEnd.y += mProfile->mFont->getBaseline() + 1; + + drawer->drawLine(localToGlobalCoord(lineStart), + localToGlobalCoord(lineEnd), + mSpecialColor); + } + + localStart.x += strWidth; + walkAcross = walkAcross->nextInLine; + } + + currLine++; + pElement = pElement->nextPhysicalLine; + } + } + drawer->clearBitmapModulation(); + } +} + + +//-------------------------------------------------------------------------- +void GuiMessageVectorCtrl::inspectPostApply() +{ + Parent::inspectPostApply(); +} + + +//-------------------------------------------------------------------------- +void GuiMessageVectorCtrl::parentResized(const RectI& oldParentRect, const RectI& newParentRect) +{ + Parent::parentResized(oldParentRect, newParentRect); + + // If we have a MesssageVector, detach/reattach so we can reflow the text. + if (mMessageVector) + { + MessageVector *reflowme = mMessageVector; + + detach(); + attach(reflowme); + } +} + +//-------------------------------------------------------------------------- +void GuiMessageVectorCtrl::findSpecialFromCoord(const Point2I& point, S32* specialLine, S32* specialRef) +{ + if (mLineElements.size() == 0) { + *specialLine = -1; + *specialRef = -1; + return; + } + + U32 linePixels = mProfile->mFont->getHeight() + mLineSpacingPixels; + + if ((point.x < 0 || point.x >= getWidth()) || + (point.y < 0 || point.y >= getHeight())) { + *specialLine = -1; + *specialRef = -1; + return; + } + + // Ok, we have real work to do here. Let's determine the physical line that it's on... + U32 physLine = point.y / linePixels; + AssertFatal(physLine >= 0, "Bad physical line!"); + + // And now we find the LineElement that contains that physicalLine... + U32 elemIndex; + for (elemIndex = 0; elemIndex < mLineElements.size(); elemIndex++) { + if (mLineElements[elemIndex].physicalLineStart > physLine) { + // We've passed it. + AssertFatal(elemIndex != 0, "Error, bad elemIndex, check assumptions."); + elemIndex--; + break; + } + } + if (elemIndex == mLineElements.size()) { + // On the last line... + elemIndex = mLineElements.size() - 1; + } + + TextElement* line = mLineElements[elemIndex].headLineElements; + for (U32 i = 0; i < physLine - mLineElements[elemIndex].physicalLineStart; i++) + { + if (line->nextPhysicalLine == NULL) + { + *specialLine = -1; + *specialRef = -1; + return; + } + line = line->nextPhysicalLine; + AssertFatal(line != NULL, "Error, moved too far!"); + } + + // Ok, line represents the current line. We now need to find out which textelement + // the points x coord falls in. + U32 currX = 0; + if ((physLine - mLineElements[elemIndex].physicalLineStart) != 0) { + currX = mLineContinuationIndent; + // First, if this isn't the first line in this wrapping, we have to make sure + // that the coord isn't in the margin... + if (point.x < mLineContinuationIndent) { + *specialLine = -1; + *specialRef = -1; + return; + } + } + if (line->start > line->end) { + // Empty line special case... + *specialLine = -1; + *specialRef = -1; + return; + } + + while (line) { + U32 newX = currX + mProfile->mFont->getStrNWidth((const UTF8 *)mMessageVector->getLine(elemIndex).message, + line->end - line->start + 1); + if (point.x < newX) { + // That's the one! + *specialLine = elemIndex; + *specialRef = line->specialReference; + return; + } + + currX = newX; + line = line->nextInLine; + } + + // Off to the right. Aw... + *specialLine = -1; + *specialRef = -1; +} + + +void GuiMessageVectorCtrl::onMouseDown(const GuiEvent& event) +{ + Parent::onMouseDown(event); + mouseUnlock(); + + mMouseDown = true; + + // Find the special we are in, if any... + findSpecialFromCoord(globalToLocalCoord(event.mousePoint), + &mMouseSpecialLine, &mMouseSpecialRef); +} + +void GuiMessageVectorCtrl::onMouseUp(const GuiEvent& event) +{ + Parent::onMouseUp(event); + mouseUnlock(); + + // Is this an up from a dragged click? + if (mMouseDown == false) + return; + + // Find the special we are in, if any... + + S32 currSpecialLine; + S32 currSpecialRef; + findSpecialFromCoord(globalToLocalCoord(event.mousePoint), &currSpecialLine, &currSpecialRef); + + if (currSpecialRef != -1 && + (currSpecialLine == mMouseSpecialLine && + currSpecialRef == mMouseSpecialRef)) { + // Execute the callback + SpecialMarkers& rSpecial = mSpecialMarkers[currSpecialLine]; + S32 specialStart = rSpecial.specials[currSpecialRef].start; + S32 specialEnd = rSpecial.specials[currSpecialRef].end; + + char* copyURL = new char[specialEnd - specialStart + 2]; + dStrncpy(copyURL, &mMessageVector->getLine(currSpecialLine).message[specialStart], specialEnd - specialStart + 1); + copyURL[specialEnd - specialStart + 1] = '\0'; + + Con::executef(this, "urlClickCallback", copyURL); + delete [] copyURL; + } + + mMouseDown = false; + mMouseSpecialLine = -1; + mMouseSpecialRef = -1; +} + diff --git a/Engine/source/gui/game/guiMessageVectorCtrl.h b/Engine/source/gui/game/guiMessageVectorCtrl.h new file mode 100644 index 000000000..05d4ffc7c --- /dev/null +++ b/Engine/source/gui/game/guiMessageVectorCtrl.h @@ -0,0 +1,156 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIMESSAGEVECTORCTRL_H_ +#define _GUIMESSAGEVECTORCTRL_H_ + +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif +#ifndef _MESSAGEVECTOR_H_ +#include "gui/utility/messageVector.h" +#endif + +/// Render a MessageVector (chat HUD) +/// +/// This renders messages from a MessageVector; the important thing +/// here is that the MessageVector holds all the messages we care +/// about, while we can destroy and create these GUI controls as +/// needed. (For instance, Tribes 2 used seperate GuiMessageVectorCtrl +/// controls in the different HUD modes.) +class GuiMessageVectorCtrl : public GuiControl +{ + typedef GuiControl Parent; + + //-------------------------------------- Public interfaces... + public: + struct SpecialMarkers { + struct Special { + S32 specialType; + S32 start; + S32 end; + }; + + U32 numSpecials; + Special* specials; + }; + + GuiMessageVectorCtrl(); + ~GuiMessageVectorCtrl(); + + bool isAttached() const; + bool attach(MessageVector*); + void detach(); + + // Gui control overrides + protected: + bool onAdd(); + void onRemove(); + + bool onWake(); + void onSleep(); + void onRender(Point2I offset, const RectI &updateRect); + void inspectPostApply(); + void parentResized(const RectI& oldParentRect, const RectI& newParentRect); + + void onMouseUp(const GuiEvent &event); + void onMouseDown(const GuiEvent &event); +// void onMouseMove(const GuiEvent &event); + + // Overrideables + protected: + virtual void lineInserted(const U32); + virtual void lineDeleted(const U32); + virtual void vectorDeleted(); + + MessageVector* mMessageVector; + + // Font resource + protected: + U32 mMinSensibleWidth; + + U32 mLineSpacingPixels; + U32 mLineContinuationIndent; + + StringTableEntry mAllowedMatches[16]; + ColorI mSpecialColor; + + U32 mMaxColorIndex; + + bool mMouseDown; + S32 mMouseSpecialLine; + S32 mMouseSpecialRef; + + /// Derived classes must keep this set of variables consistent if they + /// use this classes onRender. Note that this has the fairly large + /// disadvantage of requiring lots of ugly, tiny mem allocs. A rewrite + /// to a more memory friendly version might be a good idea... + struct LineWrapping { + struct SEPair { + S32 start; + S32 end; + }; + + U32 numLines; + SEPair* startEndPairs; /// start[linex] = startEndPairs[linex*2+0] + /// end[linex] = startEndPairs[linex*2+1] + /// if end < start, line is empty... + }; + + struct TextElement { + TextElement* nextInLine; + TextElement* nextPhysicalLine; + + S32 specialReference; /// if (!= -1), indicates a special reference + + S32 start; + S32 end; + }; + struct LineElement { + U32 physicalLineStart; + + TextElement* headLineElements; + }; + + Vector mLineWrappings; + Vector mSpecialMarkers; + Vector mLineElements; + + void createLineWrapping(LineWrapping&, const char*); + void createSpecialMarkers(SpecialMarkers&, const char*); + void createLineElement(LineElement&, LineWrapping&, SpecialMarkers&); + + void findSpecialFromCoord(const Point2I&, S32*, S32*); + + public: + void callbackRouter(const MessageVector::MessageCode, const U32); + + public: + + DECLARE_CONOBJECT(GuiMessageVectorCtrl); + DECLARE_CATEGORY( "Gui Game" ); + DECLARE_DESCRIPTION( "A chat HUD control that displays messages from a MessageVector." ); + + static void initPersistFields(); +}; + +#endif // _H_GUIMESSAGEVECTORCTRL_ diff --git a/Engine/source/gui/game/guiProgressBitmapCtrl.cpp b/Engine/source/gui/game/guiProgressBitmapCtrl.cpp new file mode 100644 index 000000000..bbdf20316 --- /dev/null +++ b/Engine/source/gui/game/guiProgressBitmapCtrl.cpp @@ -0,0 +1,286 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/game/guiProgressBitmapCtrl.h" + +#include "console/console.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "gfx/gfxDrawUtil.h" + + +IMPLEMENT_CONOBJECT( GuiProgressBitmapCtrl ); + +ConsoleDocClass( GuiProgressBitmapCtrl, + "@brief A horizontal progress bar rendered from a repeating image.\n\n" + + "This class is used give progress feedback to the user. Unlike GuiProgressCtrl which simply " + "renders a filled rectangle, GuiProgressBitmapCtrl renders the bar using a bitmap.\n\n" + + "This bitmap can either be simple, plain image which is then stretched into the current extents of the bar " + "as it fills up or it can be a bitmap array with three entries. In the case of a bitmap array, the " + "first entry in the array is used to render the left cap of the bar and the third entry in the array " + "is used to render the right cap of the bar. The second entry is streched in-between the two caps.\n\n" + + "@tsexample\n" + "// This example shows one way to break down a long-running computation into phases\n" + "// and incrementally update a progress bar between the phases.\n" + "\n" + "new GuiProgressBitmapCtrl( Progress )\n" + "{\n" + " bitmap = \"core/art/gui/images/loading\";\n" + " extent = \"300 50\";\n" + " position = \"100 100\";\n" + "};\n" + "\n" + "// Put the control on the canvas.\n" + "%wrapper = new GuiControl();\n" + "%wrapper.addObject( Progress );\n" + "Canvas.pushDialog( %wrapper );\n" + "\n" + "// Start the computation.\n" + "schedule( 1, 0, \"phase1\" );\n" + "\n" + "function phase1()\n" + "{\n" + " Progress.setValue( 0 );\n" + "\n" + " // Perform some computation.\n" + " //...\n" + "\n" + " // Update progress.\n" + " Progress.setValue( 0.25 );\n" + "\n" + " // Schedule next phase. Don't call directly so engine gets a change to run refresh.\n" + " schedule( 1, 0, \"phase2\" );\n" + "}\n" + "\n" + "function phase2()\n" + "{\n" + " // Perform some computation.\n" + " //...\n" + "\n" + " // Update progress.\n" + " Progress.setValue( 0.7 );\n" + "\n" + " // Schedule next phase. Don't call directly so engine gets a change to run refresh.\n" + " schedule( 1, 0, \"phase3\" );\n" + "}\n" + "\n" + "function phase3()\n" + "{\n" + " // Perform some computation.\n" + " //...\n" + "\n" + " // Update progress.\n" + " Progress.setValue( 0.9 );\n" + "\n" + " // Schedule next phase. Don't call directly so engine gets a change to run refresh.\n" + " schedule( 1, 0, \"phase4\" );\n" + "}\n" + "\n" + "function phase4()\n" + "{\n" + " // Perform some computation.\n" + " //...\n" + "\n" + " // Final update of progress.\n" + " Progress.setValue( 1.0 );\n" + "}\n" + "@endtsexample\n\n" + + "@see GuiProgressCtrl\n\n" + + "@ingroup GuiValues" +); + + +//----------------------------------------------------------------------------- + +GuiProgressBitmapCtrl::GuiProgressBitmapCtrl() + : mProgress( 0.f ), + mBitmapName( StringTable->EmptyString() ), + mUseVariable( false ), + mTile( false ) +{ +} + +//----------------------------------------------------------------------------- + +void GuiProgressBitmapCtrl::initPersistFields() +{ + addProtectedField( "bitmap", TypeFilename, Offset( mBitmapName, GuiProgressBitmapCtrl ), + _setBitmap, defaultProtectedGetFn, + "~Path to the bitmap file to use for rendering the progress bar.\n\n" + "If the profile assigned to the control already has a bitmap assigned, this property need not be " + "set in which case the bitmap from the profile is used." + ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +void GuiProgressBitmapCtrl::setBitmap( const char* name ) +{ + bool awake = mAwake; + if( awake ) + onSleep(); + + mBitmapName = StringTable->insert( name ); + if( awake ) + onWake(); + + setUpdate(); +} + +//----------------------------------------------------------------------------- + +const char* GuiProgressBitmapCtrl::getScriptValue() +{ + char * ret = Con::getReturnBuffer(64); + dSprintf(ret, 64, "%g", mProgress); + return ret; +} + +//----------------------------------------------------------------------------- + +void GuiProgressBitmapCtrl::setScriptValue(const char *value) +{ + //set the value + if (! value) + mProgress = 0.0f; + else + mProgress = dAtof(value); + + //validate the value + mProgress = mClampF(mProgress, 0.f, 1.f); + setUpdate(); +} + +//----------------------------------------------------------------------------- + +void GuiProgressBitmapCtrl::onPreRender() +{ + const char * var = getVariable(); + if(var) + { + F32 value = mClampF(dAtof(var), 0.f, 1.f); + if(value != mProgress) + { + mProgress = value; + setUpdate(); + } + } +} + +//----------------------------------------------------------------------------- + +void GuiProgressBitmapCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + RectI ctrlRect(offset, getExtent()); + + //grab lowest dimension + if(getHeight() <= getWidth()) + mDim = getHeight(); + else + mDim = getWidth(); + + GFX->getDrawUtil()->clearBitmapModulation(); + + if(mNumberOfBitmaps == 1) + { + //draw the progress with image + S32 width = (S32)((F32)(getWidth()) * mProgress); + if (width > 0) + { + //drawing stretch bitmap + RectI progressRect = ctrlRect; + progressRect.extent.x = width; + GFX->getDrawUtil()->drawBitmapStretchSR(mProfile->mTextureObject, progressRect, mProfile->mBitmapArrayRects[0]); + } + } + else if(mNumberOfBitmaps >= 3) + { + //drawing left-end bitmap + RectI progressRectLeft(ctrlRect.point.x, ctrlRect.point.y, mDim, mDim); + GFX->getDrawUtil()->drawBitmapStretchSR(mProfile->mTextureObject, progressRectLeft, mProfile->mBitmapArrayRects[0]); + + //draw the progress with image + S32 width = (S32)((F32)(getWidth()) * mProgress); + if (width > mDim) + { + //drawing stretch bitmap + RectI progressRect = ctrlRect; + progressRect.point.x += mDim; + progressRect.extent.x = (width - mDim - mDim); + if (progressRect.extent.x < 0) + progressRect.extent.x = 0; + GFX->getDrawUtil()->drawBitmapStretchSR(mProfile->mTextureObject, progressRect, mProfile->mBitmapArrayRects[1]); + + //drawing right-end bitmap + RectI progressRectRight(progressRect.point.x + progressRect.extent.x, ctrlRect.point.y, mDim, mDim ); + GFX->getDrawUtil()->drawBitmapStretchSR(mProfile->mTextureObject, progressRectRight, mProfile->mBitmapArrayRects[2]); + } + } + else + Con::warnf("guiProgressBitmapCtrl only processes an array of bitmaps == 1 or >= 3"); + + //if there's a border, draw it + if (mProfile->mBorder) + GFX->getDrawUtil()->drawRect(ctrlRect, mProfile->mBorderColor); + + Parent::onRender( offset, updateRect ); + + //render the children + renderChildControls(offset, updateRect); + +} + +//----------------------------------------------------------------------------- + +bool GuiProgressBitmapCtrl::onWake() +{ + if(!Parent::onWake()) + return false; + + mNumberOfBitmaps = mProfile->constructBitmapArray(); + + return true; +} + +//============================================================================= +// Console Methods. +//============================================================================= +// MARK: ---- Console Methods ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiProgressBitmapCtrl, setBitmap, void, ( const char* filename ),, + "Set the bitmap to use for rendering the progress bar.\n\n" + "@param filename ~Path to the bitmap file.\n\n" + "@note Directly assign to #bitmap rather than using this method.\n\n" + "@see GuiProgressBitmapCtrl::setBitmap" ) +{ + object->setBitmap( filename ); +} diff --git a/Engine/source/gui/game/guiProgressBitmapCtrl.h b/Engine/source/gui/game/guiProgressBitmapCtrl.h new file mode 100644 index 000000000..6b538878a --- /dev/null +++ b/Engine/source/gui/game/guiProgressBitmapCtrl.h @@ -0,0 +1,83 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GuiProgressBitmapCtrl_H_ +#define _GuiProgressBitmapCtrl_H_ + +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif + +#ifndef _GUITEXTCTRL_H_ +#include "gui/controls/guiTextCtrl.h" +#endif + + +//FIXME: WTH is this derived from GuiTextCtrl?? should be a GuiControl + + +/// A control that renders a horizontal progress bar from a repeating bitmap image. +class GuiProgressBitmapCtrl : public GuiTextCtrl +{ + public: + + typedef GuiTextCtrl Parent; + + protected: + + F32 mProgress; + StringTableEntry mBitmapName; + bool mUseVariable; + bool mTile; + S32 mNumberOfBitmaps; + S32 mDim; + + static bool _setBitmap( void* object, const char* index, const char* data ) + { + static_cast< GuiProgressBitmapCtrl* >( object )->setBitmap( data ); + return false; + } + + public: + + GuiProgressBitmapCtrl(); + + void setBitmap( const char* name ); + + //console related methods + virtual const char *getScriptValue(); + virtual void setScriptValue(const char *value); + + // GuiTextCtrl. + virtual void onPreRender(); + virtual void onRender( Point2I offset, const RectI &updateRect ); + virtual bool onWake(); + + DECLARE_CONOBJECT( GuiProgressBitmapCtrl ); + DECLARE_CATEGORY( "Gui Values" ); + DECLARE_DESCRIPTION( "A control that shows a horizontal progress bar that is rendered\n" + "by repeating a bitmap." ); + + static void initPersistFields(); +}; + +#endif diff --git a/Engine/source/gui/game/guiProgressCtrl.cpp b/Engine/source/gui/game/guiProgressCtrl.cpp new file mode 100644 index 000000000..9b3901f84 --- /dev/null +++ b/Engine/source/gui/game/guiProgressCtrl.cpp @@ -0,0 +1,116 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/game/guiProgressCtrl.h" + +#include "console/console.h" +#include "console/consoleTypes.h" +#include "gfx/gfxDrawUtil.h" +#include "console/engineAPI.h" + +IMPLEMENT_CONOBJECT(GuiProgressCtrl); + +ConsoleDocClass( GuiProgressCtrl, + "@brief GUI Control which displays a horizontal bar which increases as the progress value of 0.0 - 1.0 increases.\n\n" + + "@tsexample\n" + " new GuiProgressCtrl(JS_statusBar)\n" + " {\n" + " //Properties not specific to this control have been omitted from this example.\n" + " };\n\n" + "// Define the value to set the progress bar" + "%value = \"0.5f\"\n\n" + "// Set the value of the progress bar, from 0.0 - 1.0\n" + "%thisGuiProgressCtrl.setValue(%value);\n" + "// Get the value of the progress bar.\n" + "%progress = %thisGuiProgressCtrl.getValue();\n" + "@endtsexample\n\n" + + "@see GuiTextCtrl\n" + "@see GuiControl\n\n" + + "@ingroup GuiValues\n" +); + +GuiProgressCtrl::GuiProgressCtrl() +{ + mProgress = 0.0f; +} + +const char* GuiProgressCtrl::getScriptValue() +{ + char * ret = Con::getReturnBuffer(64); + dSprintf(ret, 64, "%g", mProgress); + return ret; +} + +void GuiProgressCtrl::setScriptValue(const char *value) +{ + //set the value + if (! value) + mProgress = 0.0f; + else + mProgress = dAtof(value); + + //validate the value + mProgress = mClampF(mProgress, 0.f, 1.f); + setUpdate(); +} + +void GuiProgressCtrl::onPreRender() +{ + const char * var = getVariable(); + if(var) + { + F32 value = mClampF(dAtof(var), 0.f, 1.f); + if(value != mProgress) + { + mProgress = value; + setUpdate(); + } + } +} + +void GuiProgressCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + RectI ctrlRect(offset, getExtent()); + + //draw the progress + S32 width = (S32)((F32)(getWidth()) * mProgress); + if (width > 0) + { + RectI progressRect = ctrlRect; + progressRect.extent.x = width; + GFX->getDrawUtil()->drawRectFill(progressRect, mProfile->mFillColor); + } + + //now draw the border + if (mProfile->mBorder) + GFX->getDrawUtil()->drawRect(ctrlRect, mProfile->mBorderColor); + + Parent::onRender( offset, updateRect ); + + //render the children + renderChildControls(offset, updateRect); +} + diff --git a/Engine/source/gui/game/guiProgressCtrl.h b/Engine/source/gui/game/guiProgressCtrl.h new file mode 100644 index 000000000..9d1aacca2 --- /dev/null +++ b/Engine/source/gui/game/guiProgressCtrl.h @@ -0,0 +1,58 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIPROGRESSCTRL_H_ +#define _GUIPROGRESSCTRL_H_ + +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif + +#ifndef _GUITEXTCTRL_H_ +#include "gui/controls/guiTextCtrl.h" +#endif + + +class GuiProgressCtrl : public GuiTextCtrl +{ +private: + typedef GuiTextCtrl Parent; + + F32 mProgress; + +public: + //creation methods + DECLARE_CONOBJECT(GuiProgressCtrl); + DECLARE_CATEGORY( "Gui Values" ); + DECLARE_DESCRIPTION( "A control that display a horizontal progress bar. The bar is\n" + "rendered using as a filled rectangle (properties determined by profile)." ); + GuiProgressCtrl(); + + //console related methods + virtual const char *getScriptValue(); + virtual void setScriptValue(const char *value); + + void onPreRender(); + void onRender(Point2I offset, const RectI &updateRect); +}; + +#endif diff --git a/Engine/source/gui/shiny/guiTickCtrl.cpp b/Engine/source/gui/shiny/guiTickCtrl.cpp new file mode 100644 index 000000000..015e84d1e --- /dev/null +++ b/Engine/source/gui/shiny/guiTickCtrl.cpp @@ -0,0 +1,66 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#include "gui/shiny/guiTickCtrl.h" + +IMPLEMENT_CONOBJECT( GuiTickCtrl ); + +ConsoleDocClass( GuiTickCtrl, + "@brief Brief Description.\n\n" + "This Gui Control is designed to be subclassed to let people create controls " + "which want to receive update ticks at a constant interval. This class was " + "created to be the Parent class of a control which used a DynamicTexture " + "along with a VectorField to create warping effects much like the ones found " + "in visualization displays for iTunes or Winamp. Those displays are updated " + "at the framerate frequency. This works fine for those effects, however for " + "an application of the same type of effects for things like Gui transitions " + "the framerate-driven update frequency is not desirable because it does not " + "allow the developer to be able to have any idea of a consistent user-experience.\n\n" + + "Enter the ITickable interface. This lets the Gui control, in this case, update " + "the dynamic texture at a constant rate of once per tick, even though it gets " + "rendered every frame, thus creating a framerate-independent update frequency " + "so that the effects are at a consistent speed regardless of the specifics " + "of the system the user is on. This means that the screen-transitions will " + "occur in the same time on a machine getting 300fps in the Gui shell as a " + "machine which gets 150fps in the Gui shell.\n\n" + + "@ingroup GuiUtil\n"); + +//------------------------------------------------------------------------------ + +static ConsoleDocFragment _setProcessTicks( + "This will set this object to either be processing ticks or not.\n\n" + "@param tick (optional) True or nothing to enable ticking, false otherwise.\n\n" + "@tsexample\n" + "// Turn off ticking for a control, like a MenuBar (declared previously)\n" + "%sampleMenuBar.setProcessTicks(false);\n" + "@endtsexample\n\n", + "GuiTickCtrl", + "void setProcessTicks( bool tick )" +); +ConsoleMethod( GuiTickCtrl, setProcessTicks, void, 2, 3, "( [tick = true] ) - This will set this object to either be processing ticks or not" ) +{ + if( argc == 3 ) + object->setProcessTicks( dAtob( argv[2] ) ); + else + object->setProcessTicks(); +} \ No newline at end of file diff --git a/Engine/source/gui/shiny/guiTickCtrl.h b/Engine/source/gui/shiny/guiTickCtrl.h new file mode 100644 index 000000000..066a069a8 --- /dev/null +++ b/Engine/source/gui/shiny/guiTickCtrl.h @@ -0,0 +1,66 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUITICKCTRL_H_ +#define _GUITICKCTRL_H_ + +#include "gui/core/guiControl.h" +#include "core/iTickable.h" + +/// This Gui Control is designed to be subclassed to let people create controls +/// which want to receive update ticks at a constant interval. This class was +/// created to be the Parent class of a control which used a DynamicTexture +/// along with a VectorField to create warping effects much like the ones found +/// in visualization displays for iTunes or Winamp. Those displays are updated +/// at the framerate frequency. This works fine for those effects, however for +/// an application of the same type of effects for things like Gui transitions +/// the framerate-driven update frequency is not desirable because it does not +/// allow the developer to be able to have any idea of a consistent user-experience. +/// +/// Enter the ITickable interface. This lets the Gui control, in this case, update +/// the dynamic texture at a constant rate of once per tick, even though it gets +/// rendered every frame, thus creating a framerate-independent update frequency +/// so that the effects are at a consistent speed regardless of the specifics +/// of the system the user is on. This means that the screen-transitions will +/// occur in the same time on a machine getting 300fps in the Gui shell as a +/// machine which gets 150fps in the Gui shell. +/// @see ITickable +class GuiTickCtrl : public GuiControl, public virtual ITickable +{ + typedef GuiControl Parent; + +private: + +protected: + + // So this can be instantiated and not be a pure virtual class + virtual void interpolateTick( F32 delta ) {}; + virtual void processTick() {}; + virtual void advanceTime( F32 timeDelta ) {}; + +public: + DECLARE_CONOBJECT( GuiTickCtrl ); + DECLARE_CATEGORY( "Gui Other" ); +}; + + +#endif \ No newline at end of file diff --git a/Engine/source/gui/theora/guiTheoraCtrl.cpp b/Engine/source/gui/theora/guiTheoraCtrl.cpp new file mode 100644 index 000000000..145b3474c --- /dev/null +++ b/Engine/source/gui/theora/guiTheoraCtrl.cpp @@ -0,0 +1,314 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_OGGTHEORA + +#include "gui/core/guiControl.h" +#include "gui/theora/guiTheoraCtrl.h" +#include "console/consoleTypes.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxDrawUtil.h" +#include "console/engineAPI.h" + + +IMPLEMENT_CONOBJECT( GuiTheoraCtrl ); + +ConsoleDocClass( GuiTheoraCtrl, + "@brief A control to playing Theora videos.\n\n" + + "This control can be used to play videos in the Theora video format. The videos may include audio in Vorbis format. The " + "codecs for both formats are integrated with the engine and no codecs must be present on the user's machine.\n\n" + + "@tsexample\n" + "%video = new GuiTheoraCtrl()\n" + "{\n" + " theoraFile = \"videos/intro.ogv\";\n" + " playOnWake = false;\n" + " stopOnSleep = true;\n" + "}\n" + "\n" + "Canvas.setContent( %video );\n" + "%video.play();\n" + "@endtsexample\n\n" + + "@see http://www.theora.org\n\n" + "@ingroup GuiImages" +); + +ImplementEnumType( GuiTheoraTranscoder, + "Routine to use for converting Theora's Y'CbCr pixel format to RGB color space.\n\n" + "@ingroup GuiImages" ) + { OggTheoraDecoder::TRANSCODER_Auto, "Auto", "Automatically detect most appropriate setting." }, + { OggTheoraDecoder::TRANSCODER_Generic, "Generic", "Slower but beneric transcoder that can convert all Y'CbCr input formats to RGB or RGBA output." }, + { OggTheoraDecoder::TRANSCODER_SSE2420RGBA, "SSE2420RGBA", "Fast SSE2-based transcoder with fixed conversion from 4:2:0 Y'CbCr to RGBA." }, +EndImplementEnumType; + +//----------------------------------------------------------------------------- + +GuiTheoraCtrl::GuiTheoraCtrl() +{ + mFilename = StringTable->EmptyString(); + mDone = false; + mStopOnSleep = false; + mMatchVideoSize = true; + mPlayOnWake = true; + mRenderDebugInfo = false; + mTranscoder = OggTheoraDecoder::TRANSCODER_Auto; + + mBackgroundColor.set( 0, 0, 0, 255); +} + +//----------------------------------------------------------------------------- + +void GuiTheoraCtrl::initPersistFields() +{ + addGroup( "Playback"); + + addField( "theoraFile", TypeStringFilename, Offset( mFilename, GuiTheoraCtrl ), + "Theora video file to play." ); + addField( "backgroundColor", TypeColorI, Offset( mBackgroundColor, GuiTheoraCtrl ), + "Fill color when video is not playing." ); + addField( "playOnWake", TypeBool, Offset( mPlayOnWake, GuiTheoraCtrl ), + "Whether to start playing video when control is woken up." ); + addField( "stopOnSleep", TypeBool, Offset( mStopOnSleep, GuiTheoraCtrl ), + "Whether to stop video when control is set to sleep.\n\n" + "If this is not set to true, the video will be paused when the control is put to sleep. This is because there is no support " + "for seeking in the video stream in the player backend and letting the time source used to synchronize video (either audio " + "or a raw timer) get far ahead of frame decoding will cause possibly very long delays when the control is woken up again." ); + addField( "matchVideoSize", TypeBool, Offset( mMatchVideoSize, GuiTheoraCtrl ), + "Whether to automatically match control extents to the video size." ); + addField( "renderDebugInfo", TypeBool, Offset( mRenderDebugInfo, GuiTheoraCtrl ), + "If true, displays an overlay on top of the video with useful debugging information." ); + addField( "transcoder", TYPEID< OggTheoraDecoder::ETranscoder >(), Offset( mTranscoder, GuiTheoraCtrl ), + "The routine to use for Y'CbCr to RGB conversion." ); + + endGroup( "Playback" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +void GuiTheoraCtrl::setFile( const String& filename ) +{ + mDone = false; + mFilename = filename; + mTheoraTexture.setFile( filename ); + + if( mMatchVideoSize && mTheoraTexture.isReady() ) + setExtent( Point2I( mTheoraTexture.getWidth(), mTheoraTexture.getHeight() ) ); + + OggTheoraDecoder* decoder = mTheoraTexture._getTheora(); + if( decoder != NULL ) + decoder->setTranscoder( mTranscoder ); +} + +//----------------------------------------------------------------------------- + +void GuiTheoraCtrl::play() +{ + if( mFilename.isEmpty() ) + return; + + if( !mTheoraTexture.isPlaying() ) + { + mDone = false; + mTheoraTexture.play(); + } +} + +//----------------------------------------------------------------------------- + +void GuiTheoraCtrl::pause() +{ + if( !mTheoraTexture.isPlaying() ) + { + Con::errorf( "GuiTheoraCtrl::pause - not playing" ); + return; + } + + mTheoraTexture.pause(); +} + +//----------------------------------------------------------------------------- + +void GuiTheoraCtrl::stop() +{ + mTheoraTexture.stop(); + mDone = true; +} + +//----------------------------------------------------------------------------- + +bool GuiTheoraCtrl::onWake() +{ + if( !Parent::onWake() ) + return false; + + if( !mTheoraTexture.isReady() ) + setFile( mFilename ); + + if( mPlayOnWake && !mTheoraTexture.isPlaying() ) + play(); + + return true; +} + +//----------------------------------------------------------------------------- + +void GuiTheoraCtrl::onSleep() +{ + Parent::onSleep(); + + if( mTheoraTexture.isPlaying() ) + { + if( mStopOnSleep ) + stop(); + else + pause(); + } +} + +//----------------------------------------------------------------------------- + +void GuiTheoraCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + const RectI rect(offset, getBounds().extent); + + if( mTheoraTexture.isReady() ) + { + mTheoraTexture.refresh(); + if( mTheoraTexture.isPlaying() + || mTheoraTexture.isPaused() ) + { + // Draw the frame. + + GFXDrawUtil* drawUtil = GFX->getDrawUtil(); + drawUtil->clearBitmapModulation(); + drawUtil->drawBitmapStretch( mTheoraTexture.getTexture(), rect ); + + // Draw frame info, if requested. + + if( mRenderDebugInfo ) + { + String info = String::ToString( "Frame Number: %i | Frame Time: %.2fs | Playback Time: %.2fs | Dropped: %i", + mTheoraTexture.getFrameNumber(), + mTheoraTexture.getFrameTime(), + F32( mTheoraTexture.getPosition() ) / 1000.f, + mTheoraTexture.getNumDroppedFrames() ); + + drawUtil->setBitmapModulation( mProfile->mFontColors[ 0 ] ); + drawUtil->drawText( mProfile->mFont, localToGlobalCoord( Point2I( 0, 0 ) ), info, mProfile->mFontColors ); + } + } + else + mDone = true; + } + else + GFX->getDrawUtil()->drawRectFill(rect, mBackgroundColor); // black rect + + renderChildControls(offset, updateRect); +} + +//----------------------------------------------------------------------------- + +void GuiTheoraCtrl::inspectPostApply() +{ + if( !mTheoraTexture.getFilename().equal( mFilename, String::NoCase ) ) + { + stop(); + setFile( mFilename ); + + if( mPlayOnWake && !mTheoraTexture.isPlaying() ) + play(); + } + + if( mMatchVideoSize && mTheoraTexture.isReady() ) + setExtent( Point2I( mTheoraTexture.getWidth(), mTheoraTexture.getHeight() ) ); + + OggTheoraDecoder* decoder = mTheoraTexture._getTheora(); + if( decoder != NULL ) + decoder->setTranscoder( mTranscoder ); + + Parent::inspectPostApply(); +} + +//============================================================================= +// API. +//============================================================================= +// MARK: ---- API ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTheoraCtrl, setFile, void, ( const char* filename ),, + "Set the video file to play. If a video is already playing, playback is stopped and " + "the new video file is loaded.\n\n" + "@param filename The video file to load." ) +{ + object->setFile( filename ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTheoraCtrl, play, void, (),, + "Start playing the video. If the video is already playing, the call is ignored." ) +{ + object->play(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTheoraCtrl, pause, void, (),, + "Pause playback of the video. If the video is not currently playing, the call is ignored.\n\n" + "While stopped, the control displays the last frame." ) +{ + object->pause(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTheoraCtrl, stop, void, (),, + "Stop playback of the video. The next call to play() will then start playback from the beginning of the video.\n\n" + "While stopped, the control renders empty with just the background color." ) +{ + object->stop(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTheoraCtrl, getCurrentTime, F32, (),, + "Get the current playback time.\n\n" + "@return The elapsed playback time in seconds." ) +{ + return object->getCurrentTime(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( GuiTheoraCtrl, isPlaybackDone, bool, (),, + "Test whether the video has finished playing.\n\n" + "@return True if the video has finished playing, false otherwise." ) +{ + return object->isPlaybackDone(); +} + +#endif // TORQUE_OGGTHEORA diff --git a/Engine/source/gui/theora/guiTheoraCtrl.h b/Engine/source/gui/theora/guiTheoraCtrl.h new file mode 100644 index 000000000..e85ff6986 --- /dev/null +++ b/Engine/source/gui/theora/guiTheoraCtrl.h @@ -0,0 +1,127 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUITHEORACTRL_H_ +#define _GUITHEORACTRL_H_ + +#ifdef TORQUE_OGGTHEORA + +#ifndef _THEORATEXTURE_H_ + #include "gfx/video/theoraTexture.h" +#endif +#ifndef _OGGTHEORADECODER_H_ + #include "core/ogg/oggTheoraDecoder.h" +#endif +#ifndef _DYNAMIC_CONSOLETYPES_H_ + #include "console/dynamicTypes.h" +#endif + + +/// Control to play back a Theora video file. +class GuiTheoraCtrl : public GuiControl +{ + public: + + typedef GuiControl Parent; + + protected: + + /// The Theora file we should play. + String mFilename; + + /// Theora video player backend. + TheoraTexture mTheoraTexture; + + /// If true, the control's extents will be matched to the video size. + bool mMatchVideoSize; + + /// If true, playback will start automatically when the control receives its + /// onWake(). + bool mPlayOnWake; + + /// Which transcoder to use on the Theora decoder. This is mostly + /// meant as a development aid. + OggTheoraDecoder::ETranscoder mTranscoder; + + /// If true, stop video playback when the control goes to sleep. Otherwise, + /// the video will be paused. + /// + /// @note We do not currently support to keep video running in the background + /// as the Theora decoder does not yet support skipping through bulks of + /// outdated data. This means that when the Theora texture gets its next + /// refresh, the decoder will frantically try to wade through a huge amount + /// of outdated ogg_packets which even though the actual decoding does not + /// take place takes a lot of time. + bool mStopOnSleep; + + /// Are we done with playback? + bool mDone; + + /// If true, renders some text information into the frame. + bool mRenderDebugInfo; + + /// Our background color. + ColorI mBackgroundColor; + + public: + + GuiTheoraCtrl(); + + /// Load the given Theora video file. Does not start playback. + void setFile( const String& filename ); + + /// Start video playback. + void play(); + + /// Pause video playback. + void pause(); + + /// Stop video playback. + void stop(); + + /// Return true if the video has finished playing. + bool isPlaybackDone() const { return mDone; } + + /// Return the current playback position. + F32 getCurrentTime() + { + return F32( mTheoraTexture.getPosition() ) / 1000.f; + } + + // GuiControl. + virtual bool onWake(); + virtual void onSleep(); + virtual void onRender( Point2I offset, const RectI &updateRect ); + virtual void inspectPostApply(); + + static void initPersistFields(); + + DECLARE_CONOBJECT( GuiTheoraCtrl ); + DECLARE_CATEGORY( "Gui Images" ); + DECLARE_DESCRIPTION( "A control for playing Theora videos." ); +}; + +typedef OggTheoraDecoder::ETranscoder GuiTheoraTranscoder; +DefineEnumType( GuiTheoraTranscoder ); + +#endif // TORQUE_OGGTHEORA +#endif // !_GUITHEORACTRL_H_ diff --git a/Engine/source/gui/utility/guiBubbleTextCtrl.cpp b/Engine/source/gui/utility/guiBubbleTextCtrl.cpp new file mode 100644 index 000000000..3dbac6ddb --- /dev/null +++ b/Engine/source/gui/utility/guiBubbleTextCtrl.cpp @@ -0,0 +1,109 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/utility/guiBubbleTextCtrl.h" +#include "gui/core/guiCanvas.h" + +IMPLEMENT_CONOBJECT(GuiBubbleTextCtrl); + +ConsoleDocClass( GuiBubbleTextCtrl, + "@brief A single-line text control that displays its text in a multi-line popup when clicked.\n\n" + + "This control acts like a GuiTextCtrl (and inherits from it), when clicked it creates a GuiMLTextCtrl " + "roughly where you clicked with the same text in it. This allows you to have a single line text control " + "which upon clicking will display the entire text contained in a multi-line format.\n\n" + + "@tsexample\n" + "new GuiBubbleTextCtrl(BubbleTextGUI)\n" + "{\n" + " text = \"This is the first sentence. This second sentence can be sized outside of the default single " + "line view, upon clicking this will be displayed in a multi-line format.\";\n" + "};\n" + "@endtsexample\n\n" + + "@see GuiTextCtrl\n" + "@see GuiMLTextCtrl\n\n" + + "@ingroup GuiControls\n" +); + +//------------------------------------------------------------------------------ +void GuiBubbleTextCtrl::popBubble() +{ + // Release the mouse: + mInAction = false; + mouseUnlock(); + + // Pop the dialog + getRoot()->popDialogControl(mDlg); + + // Kill the popup + mDlg->removeObject(mPopup); + mPopup->removeObject(mMLText); + mMLText->deleteObject(); + mPopup->deleteObject(); + mDlg->deleteObject(); +} + +//------------------------------------------------------------------------------ +void GuiBubbleTextCtrl::onMouseDown(const GuiEvent &event) +{ + if (mInAction) + { + popBubble(); + + return; + } + + mDlg = new GuiControl(); + AssertFatal(mDlg, "Failed to create the GuiControl for the BubbleTextCtrl"); + mDlg->setDataField( StringTable->insert("profile"), NULL, "GuiModelessDialogProfile"); + mDlg->setField("horizSizing", "width"); + mDlg->setField("vertSizing", "height"); + mDlg->setField("extent", "640 480"); + + mPopup = new GuiControl(); + AssertFatal(mPopup, "Failed to create the GuiControl for the BubbleTextCtrl"); + mPopup->setDataField( StringTable->insert("profile"), NULL, "GuiBubblePopupProfile"); + + mMLText = new GuiMLTextCtrl(); + AssertFatal(mMLText, "Failed to create the GuiMLTextCtrl for the BubbleTextCtrl"); + mMLText->setDataField( StringTable->insert("profile"), NULL, "GuiBubbleTextProfile"); + mMLText->setField("position", "2 2"); + mMLText->setField("extent", "296 51"); + + mMLText->setText((char*)mText,dStrlen(mText)); + + mMLText->registerObject(); + mPopup->registerObject(); + mDlg->registerObject(); + + mPopup->addObject(mMLText); + mDlg->addObject(mPopup); + + mPopup->resize(event.mousePoint,Point2I(300,55)); + + getRoot()->pushDialogControl(mDlg,0); + mouseLock(); + + mInAction = true; +} diff --git a/Engine/source/gui/utility/guiBubbleTextCtrl.h b/Engine/source/gui/utility/guiBubbleTextCtrl.h new file mode 100644 index 000000000..909644c74 --- /dev/null +++ b/Engine/source/gui/utility/guiBubbleTextCtrl.h @@ -0,0 +1,60 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIBUBBLETEXTCTRL_H_ +#define _GUIBUBBLETEXTCTRL_H_ + +#ifndef _GUITEXTCTRL_H_ +#include "gui/controls/guiTextCtrl.h" +#endif +#ifndef _GUIMLTEXTCTRL_H_ +#include "gui/controls/guiMLTextCtrl.h" +#endif + +/// A single-line text control that displays its text in a multi-line popup when +/// clicked. +class GuiBubbleTextCtrl : public GuiTextCtrl +{ + private: + + typedef GuiTextCtrl Parent; + + protected: + bool mInAction; + GuiControl *mDlg; + GuiControl *mPopup; + GuiMLTextCtrl *mMLText; + + void popBubble(); + + public: + + DECLARE_CONOBJECT(GuiBubbleTextCtrl); + DECLARE_DESCRIPTION( "A single-line text control that displays its text in a multi-line\n" + "popup when clicked." ); + + GuiBubbleTextCtrl() { mInAction = false; } + + virtual void onMouseDown(const GuiEvent &event); +}; + +#endif /* _GUI_BUBBLE_TEXT_CONTROL_H_ */ diff --git a/Engine/source/gui/utility/guiInputCtrl.cpp b/Engine/source/gui/utility/guiInputCtrl.cpp new file mode 100644 index 000000000..c1799b43f --- /dev/null +++ b/Engine/source/gui/utility/guiInputCtrl.cpp @@ -0,0 +1,165 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/utility/guiInputCtrl.h" +#include "sim/actionMap.h" +#include "console/engineAPI.h" + +IMPLEMENT_CONOBJECT(GuiInputCtrl); + +ConsoleDocClass( GuiInputCtrl, + "@brief A control that locks the mouse and reports all keyboard input events to script.\n\n" + + "This is useful for implementing custom keyboard handling code, and most commonly " + "used in Torque for a menu that allows a user to remap their in-game controls\n\n " + + "@tsexample\n" + "new GuiInputCtrl(OptRemapInputCtrl)\n" + "{\n" + " lockMouse = \"0\";\n" + " position = \"0 0\";\n" + " extent = \"64 64\";\n" + " minExtent = \"8 8\";\n" + " horizSizing = \"center\";\n" + " vertSizing = \"bottom\";\n" + " profile = \"GuiInputCtrlProfile\";\n" + " visible = \"1\";\n" + " active = \"1\";\n" + " tooltipProfile = \"GuiToolTipProfile\";\n" + " hovertime = \"1000\";\n" + " isContainer = \"0\";\n" + " canSave = \"1\";\n" + " canSaveDynamicFields = \"0\";\n" + "};\n" + "@endtsexample\n\n" + + "@see GuiMouseEventCtrl\n" + + "@ingroup GuiUtil\n"); + +//------------------------------------------------------------------------------ + +void GuiInputCtrl::initPersistFields() +{ + + + Parent::initPersistFields(); +} + +//------------------------------------------------------------------------------ + +bool GuiInputCtrl::onWake() +{ + // Set the default profile on start-up: + if( !mProfile ) + { + GuiControlProfile* profile; + Sim::findObject( "GuiInputCtrlProfile", profile); + if( profile ) + setControlProfile( profile ); + } + + if ( !Parent::onWake() ) + return( false ); + + if( !smDesignTime ) + mouseLock(); + + setFirstResponder(); + + return( true ); +} + + +//------------------------------------------------------------------------------ +void GuiInputCtrl::onSleep() +{ + Parent::onSleep(); + mouseUnlock(); + clearFirstResponder(); +} + + +//------------------------------------------------------------------------------ +static bool isModifierKey( U16 keyCode ) +{ + switch ( keyCode ) + { + case KEY_LCONTROL: + case KEY_RCONTROL: + case KEY_LALT: + case KEY_RALT: + case KEY_LSHIFT: + case KEY_RSHIFT: + return( true ); + } + + return( false ); +} + +IMPLEMENT_CALLBACK( GuiInputCtrl, onInputEvent, void, (const char* device, const char* action, bool state ), + ( device, action, state), + "@brief Callback that occurs when an input is triggered on this control\n\n" + "@param device The device type triggering the input, such as keyboard, mouse, etc\n" + "@param action The actual event occuring, such as a key or button\n" + "@param state True if the action is being pressed, false if it is being release\n\n" +); + +//------------------------------------------------------------------------------ +bool GuiInputCtrl::onInputEvent( const InputEventInfo &event ) +{ + // TODO - add POV support... + if ( event.action == SI_MAKE ) + { + if ( event.objType == SI_BUTTON + || event.objType == SI_POV + || ( ( event.objType == SI_KEY ) && !isModifierKey( event.objInst ) ) ) + { + char deviceString[32]; + if ( !ActionMap::getDeviceName( event.deviceType, event.deviceInst, deviceString ) ) + return( false ); + + const char* actionString = ActionMap::buildActionString( &event ); + + //Con::executef( this, "onInputEvent", deviceString, actionString, "1" ); + onInputEvent_callback(deviceString, actionString, 1); + + return( true ); + } + } + else if ( event.action == SI_BREAK ) + { + if ( ( event.objType == SI_KEY ) && isModifierKey( event.objInst ) ) + { + char keyString[32]; + if ( !ActionMap::getKeyString( event.objInst, keyString ) ) + return( false ); + + //Con::executef( this, "onInputEvent", "keyboard", keyString, "0" ); + onInputEvent_callback("keyboard", keyString, 0); + + return( true ); + } + } + + return( false ); +} diff --git a/Engine/source/gui/utility/guiInputCtrl.h b/Engine/source/gui/utility/guiInputCtrl.h new file mode 100644 index 000000000..77d5d3e6c --- /dev/null +++ b/Engine/source/gui/utility/guiInputCtrl.h @@ -0,0 +1,57 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIINPUTCTRL_H_ +#define _GUIINPUTCTRL_H_ + +#ifndef _GUIMOUSEEVENTCTRL_H_ + #include "gui/utility/guiMouseEventCtrl.h" +#endif +#ifndef _EVENT_H_ + #include "platform/event.h" +#endif + + +/// A control that locks the mouse and reports all keyboard input events +/// to script. This is useful for implementing custom keyboard handling code. +class GuiInputCtrl : public GuiMouseEventCtrl +{ + public: + + typedef GuiMouseEventCtrl Parent; + + // GuiControl. + virtual bool onWake(); + virtual void onSleep(); + + virtual bool onInputEvent( const InputEventInfo &event ); + + static void initPersistFields(); + + DECLARE_CONOBJECT(GuiInputCtrl); + DECLARE_CATEGORY( "Gui Other Script" ); + DECLARE_DESCRIPTION( "A control that locks the mouse and reports all keyboard input events to script." ); + + DECLARE_CALLBACK( void, onInputEvent, ( const char* device, const char* action, bool state )); +}; + +#endif // _GUI_INPUTCTRL_H diff --git a/Engine/source/gui/utility/guiMouseEventCtrl.cpp b/Engine/source/gui/utility/guiMouseEventCtrl.cpp new file mode 100644 index 000000000..cde0359e9 --- /dev/null +++ b/Engine/source/gui/utility/guiMouseEventCtrl.cpp @@ -0,0 +1,373 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/utility/guiMouseEventCtrl.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" + +IMPLEMENT_CONOBJECT(GuiMouseEventCtrl); + +ConsoleDocClass( GuiMouseEventCtrl, + "@brief Used to overlaps a 'hot region' where you want to catch inputs with and have specific events occur based on individual callbacks.\n\n" + + "Mouse event callbacks supported by this control are: onMouseUp, onMouseDown, onMouseMove, onMouseDragged, onMouseEnter, onMouseLeave,\n" + "onRightMouseDown, onRightMouseUp and onRightMouseDragged.\n\n" + + "@tsexample\n" + "new GuiMouseEventCtrl()\n" + "{\n" + " lockMouse = \"0\";\n" + " //Properties not specific to this control have been omitted from this example.\n" + "};\n" + "@endtsexample\n\n" + + "@see GuiControl\n\n" + + "@ingroup GuiCore\n" +); + + +IMPLEMENT_CALLBACK( GuiMouseEventCtrl, onMouseDown, void, ( U8 modifier, Point2I mousePoint,U8 mouseClickCount ), + ( modifier, mousePoint, mouseClickCount ), + "@brief Callback that occurs whenever the mouse is pressed down while in this control.\n\n" + "@param modifier Key that was pressed during this callback. Values are:\n\n" + "$EventModifier::RSHIFT\n\n" + "$EventModifier::SHIFT\n\n" + "$EventModifier::LCTRL\n\n" + "$EventModifier::RCTRL\n\n" + "$EventModifier::CTRL\n\n" + "$EventModifier::CTRL\n\n" + "$EventModifier::RALT\n\n" + "$EventModifier::ALT\n\n" + "@param mousePoint X/Y location of the mouse point\n" + "@param mouseClickCount How many mouse clicks have occured for this event\n\n" + "@tsexample\n" + "// Mouse was pressed down in this control, causing the callback\n" + "GuiMouseEventCtrl::onMouseDown(%this,%modifier,%mousePoint,%mouseClickCount)\n" + "{\n" + " // Code to call when a mouse event occurs.\n" + "}\n" + "@endtsexample\n\n" + "@see GuiControl\n\n" +); + +IMPLEMENT_CALLBACK( GuiMouseEventCtrl, onMouseUp, void, ( U8 modifier, Point2I mousePoint,U8 mouseClickCount ), + ( modifier, mousePoint, mouseClickCount ), + "@brief Callback that occurs whenever the mouse is released while in this control.\n\n" + "@param modifier Key that was pressed during this callback. Values are:\n\n" + "$EventModifier::RSHIFT\n\n" + "$EventModifier::SHIFT\n\n" + "$EventModifier::LCTRL\n\n" + "$EventModifier::RCTRL\n\n" + "$EventModifier::CTRL\n\n" + "$EventModifier::CTRL\n\n" + "$EventModifier::RALT\n\n" + "$EventModifier::ALT\n\n" + "@param mousePoint X/Y location of the mouse point\n" + "@param mouseClickCount How many mouse clicks have occured for this event\n\n" + "@tsexample\n" + "// Mouse was released in this control, causing the callback\n" + "GuiMouseEventCtrl::onMouseUp(%this,%modifier,%mousePoint,%mouseClickCount)\n" + "{\n" + " // Code to call when a mouse event occurs.\n" + "}\n" + "@endtsexample\n\n" + "@see GuiControl\n\n" +); + +IMPLEMENT_CALLBACK( GuiMouseEventCtrl, onMouseMove, void, ( U8 modifier, Point2I mousePoint,U8 mouseClickCount ), + ( modifier, mousePoint, mouseClickCount ), + "@brief Callback that occurs whenever the mouse is moved (without dragging) while in this control.\n\n" + "@param modifier Key that was pressed during this callback. Values are:\n\n" + "$EventModifier::RSHIFT\n\n" + "$EventModifier::SHIFT\n\n" + "$EventModifier::LCTRL\n\n" + "$EventModifier::RCTRL\n\n" + "$EventModifier::CTRL\n\n" + "$EventModifier::CTRL\n\n" + "$EventModifier::RALT\n\n" + "$EventModifier::ALT\n\n" + "@param mousePoint X/Y location of the mouse point\n" + "@param mouseClickCount How many mouse clicks have occured for this event\n\n" + "@tsexample\n" + "// Mouse was moved in this control, causing the callback\n" + "GuiMouseEventCtrl::onMouseMove(%this,%modifier,%mousePoint,%mouseClickCount)\n" + "{\n" + " // Code to call when a mouse event occurs.\n" + "}\n" + "@endtsexample\n\n" + "@see GuiControl\n\n" +); + +IMPLEMENT_CALLBACK( GuiMouseEventCtrl, onMouseDragged, void, ( U8 modifier, Point2I mousePoint,U8 mouseClickCount ), + ( modifier, mousePoint, mouseClickCount ), + "@brief Callback that occurs whenever the mouse is dragged while in this control.\n\n" + "@param modifier Key that was pressed during this callback. Values are:\n\n" + "$EventModifier::RSHIFT\n\n" + "$EventModifier::SHIFT\n\n" + "$EventModifier::LCTRL\n\n" + "$EventModifier::RCTRL\n\n" + "$EventModifier::CTRL\n\n" + "$EventModifier::CTRL\n\n" + "$EventModifier::RALT\n\n" + "$EventModifier::ALT\n\n" + "@param mousePoint X/Y location of the mouse point\n" + "@param mouseClickCount How many mouse clicks have occured for this event\n\n" + "@tsexample\n" + "// Mouse was dragged in this control, causing the callback\n" + "GuiMouseEventCtrl::onMouseDragged(%this,%modifier,%mousePoint,%mouseClickCount)\n" + "{\n" + " // Code to call when a mouse event occurs.\n" + "}\n" + "@endtsexample\n\n" + "@see GuiControl\n\n" +); + +IMPLEMENT_CALLBACK( GuiMouseEventCtrl, onMouseEnter, void, ( U8 modifier, Point2I mousePoint,U8 mouseClickCount ), + ( modifier, mousePoint, mouseClickCount ), + "@brief Callback that occurs whenever the mouse enters this control.\n\n" + "@param modifier Key that was pressed during this callback. Values are:\n\n" + "$EventModifier::RSHIFT\n\n" + "$EventModifier::SHIFT\n\n" + "$EventModifier::LCTRL\n\n" + "$EventModifier::RCTRL\n\n" + "$EventModifier::CTRL\n\n" + "$EventModifier::CTRL\n\n" + "$EventModifier::RALT\n\n" + "$EventModifier::ALT\n\n" + "@param mousePoint X/Y location of the mouse point\n" + "@param mouseClickCount How many mouse clicks have occured for this event\n\n" + "@tsexample\n" + "// Mouse entered this control, causing the callback\n" + "GuiMouseEventCtrl::onMouseEnter(%this,%modifier,%mousePoint,%mouseClickCount)\n" + "{\n" + " // Code to call when a mouse event occurs.\n" + "}\n" + "@endtsexample\n\n" + "@see GuiControl\n\n" +); + +IMPLEMENT_CALLBACK( GuiMouseEventCtrl, onMouseLeave, void, ( U8 modifier, Point2I mousePoint,U8 mouseClickCount ), + ( modifier, mousePoint, mouseClickCount ), + "@brief Callback that occurs whenever the mouse leaves this control.\n\n" + "@param modifier Key that was pressed during this callback. Values are:\n\n" + "$EventModifier::RSHIFT\n\n" + "$EventModifier::SHIFT\n\n" + "$EventModifier::LCTRL\n\n" + "$EventModifier::RCTRL\n\n" + "$EventModifier::CTRL\n\n" + "$EventModifier::CTRL\n\n" + "$EventModifier::RALT\n\n" + "$EventModifier::ALT\n\n" + "@param mousePoint X/Y location of the mouse point\n" + "@param mouseClickCount How many mouse clicks have occured for this event\n\n" + "@tsexample\n" + "// Mouse left this control, causing the callback\n" + "GuiMouseEventCtrl::onMouseLeave(%this,%modifier,%mousePoint,%mouseClickCount)\n" + "{\n" + " // Code to call when a mouse event occurs.\n" + "}\n" + "@endtsexample\n\n" + "@see GuiControl\n\n" +); + +IMPLEMENT_CALLBACK( GuiMouseEventCtrl, onRightMouseDown, void, ( U8 modifier, Point2I mousePoint,U8 mouseClickCount ), + ( modifier, mousePoint, mouseClickCount ), + "@brief Callback that occurs whenever the right mouse button is pressed while in this control.\n\n" + "@param modifier Key that was pressed during this callback. Values are:\n\n" + "$EventModifier::RSHIFT\n\n" + "$EventModifier::SHIFT\n\n" + "$EventModifier::LCTRL\n\n" + "$EventModifier::RCTRL\n\n" + "$EventModifier::CTRL\n\n" + "$EventModifier::CTRL\n\n" + "$EventModifier::RALT\n\n" + "$EventModifier::ALT\n\n" + "@param mousePoint X/Y location of the mouse point\n" + "@param mouseClickCount How many mouse clicks have occured for this event\n\n" + "@tsexample\n" + "// Right mouse button was pressed in this control, causing the callback\n" + "GuiMouseEventCtrl::onRightMouseDown(%this,%modifier,%mousePoint,%mouseClickCount)\n" + "{\n" + " // Code to call when a mouse event occurs.\n" + "}\n" + "@endtsexample\n\n" + "@see GuiControl\n\n" +); + +IMPLEMENT_CALLBACK( GuiMouseEventCtrl, onRightMouseUp, void, ( U8 modifier, Point2I mousePoint,U8 mouseClickCount ), + ( modifier, mousePoint, mouseClickCount ), + "@brief Callback that occurs whenever the right mouse button is released while in this control.\n\n" + "@param modifier Key that was pressed during this callback. Values are:\n\n" + "$EventModifier::RSHIFT\n\n" + "$EventModifier::SHIFT\n\n" + "$EventModifier::LCTRL\n\n" + "$EventModifier::RCTRL\n\n" + "$EventModifier::CTRL\n\n" + "$EventModifier::CTRL\n\n" + "$EventModifier::RALT\n\n" + "$EventModifier::ALT\n\n" + "@param mousePoint X/Y location of the mouse point\n" + "@param mouseClickCount How many mouse clicks have occured for this event\n\n" + "@tsexample\n" + "// Right mouse button was released in this control, causing the callback\n" + "GuiMouseEventCtrl::onRightMouseUp(%this,%modifier,%mousePoint,%mouseClickCount)\n" + "{\n" + " // Code to call when a mouse event occurs.\n" + "}\n" + "@endtsexample\n\n" + "@see GuiControl\n\n" +); + +IMPLEMENT_CALLBACK( GuiMouseEventCtrl, onRightMouseDragged, void, ( U8 modifier, Point2I mousePoint,U8 mouseClickCount ), + ( modifier, mousePoint, mouseClickCount ), + "@brief Callback that occurs whenever the mouse is dragged in this control while the right mouse button is pressed.\n\n" + "@param modifier Key that was pressed during this callback. Values are:\n\n" + "$EventModifier::RSHIFT\n\n" + "$EventModifier::SHIFT\n\n" + "$EventModifier::LCTRL\n\n" + "$EventModifier::RCTRL\n\n" + "$EventModifier::CTRL\n\n" + "$EventModifier::CTRL\n\n" + "$EventModifier::RALT\n\n" + "$EventModifier::ALT\n\n" + "@param mousePoint X/Y location of the mouse point\n" + "@param mouseClickCount How many mouse clicks have occured for this event\n\n" + "@tsexample\n" + "// Right mouse button was dragged in this control, causing the callback\n" + "GuiMouseEventCtrl::onRightMouseDragged(%this,%modifier,%mousePoint,%mouseClickCount)\n" + "{\n" + " // Code to call when a mouse event occurs.\n" + "}\n" + "@endtsexample\n\n" + "@see GuiControl\n\n" +); + +GuiMouseEventCtrl::GuiMouseEventCtrl() +{ + mLockMouse = false; +} + +//------------------------------------------------------------------------------ +void GuiMouseEventCtrl::sendMouseEvent(const char * name, const GuiEvent & event) +{ + char buf[3][32]; + dSprintf(buf[0], 32, "%d", event.modifier); + dSprintf(buf[1], 32, "%d %d", event.mousePoint.x, event.mousePoint.y); + dSprintf(buf[2], 32, "%d", event.mouseClickCount); + + if(dStricmp(name,"onMouseDown") == 0) + onMouseDown_callback(event.modifier, event.mousePoint, event.mouseClickCount); + else if(dStricmp(name,"onMouseUp") == 0) + onMouseUp_callback(event.modifier, event.mousePoint, event.mouseClickCount); + else if(dStricmp(name,"onMouseMove") == 0) + onMouseMove_callback(event.modifier, event.mousePoint, event.mouseClickCount); + else if(dStricmp(name,"onMouseDragged") == 0) + onMouseDragged_callback(event.modifier, event.mousePoint, event.mouseClickCount); + else if(dStricmp(name,"onMouseEnter") == 0) + onMouseEnter_callback(event.modifier, event.mousePoint, event.mouseClickCount); + else if(dStricmp(name,"onMouseLeave") == 0) + onMouseLeave_callback(event.modifier, event.mousePoint, event.mouseClickCount); + else if(dStricmp(name,"onRightMouseDown") == 0) + onRightMouseDown_callback(event.modifier, event.mousePoint, event.mouseClickCount); + else if(dStricmp(name,"onRightMouseUp") == 0) + onRightMouseUp_callback(event.modifier, event.mousePoint, event.mouseClickCount); + else if(dStricmp(name,"onRightMouseDragged") == 0) + onRightMouseDragged_callback(event.modifier, event.mousePoint, event.mouseClickCount); +} + +//------------------------------------------------------------------------------ +void GuiMouseEventCtrl::initPersistFields() +{ + addGroup( "Input" ); + + addField("lockMouse", TypeBool, Offset(mLockMouse, GuiMouseEventCtrl), + "Whether the control should lock the mouse between up and down button events." ); + + endGroup( "Input" ); + + Parent::initPersistFields(); + + Con::setIntVariable("$EventModifier::LSHIFT", SI_LSHIFT); + Con::setIntVariable("$EventModifier::RSHIFT", SI_RSHIFT); + Con::setIntVariable("$EventModifier::SHIFT", SI_SHIFT); + Con::setIntVariable("$EventModifier::LCTRL", SI_LCTRL); + Con::setIntVariable("$EventModifier::RCTRL", SI_RCTRL); + Con::setIntVariable("$EventModifier::CTRL", SI_CTRL); + Con::setIntVariable("$EventModifier::LALT", SI_LALT); + Con::setIntVariable("$EventModifier::RALT", SI_RALT); + Con::setIntVariable("$EventModifier::ALT", SI_ALT); +} + +//------------------------------------------------------------------------------ +void GuiMouseEventCtrl::onMouseDown(const GuiEvent & event) +{ + if(mLockMouse) + mouseLock(); + sendMouseEvent("onMouseDown", event); +} + +void GuiMouseEventCtrl::onMouseUp(const GuiEvent & event) +{ + if(mLockMouse) + mouseUnlock(); + sendMouseEvent("onMouseUp", event); +} + +void GuiMouseEventCtrl::onMouseMove(const GuiEvent & event) +{ + sendMouseEvent("onMouseMove", event); +} + +void GuiMouseEventCtrl::onMouseDragged(const GuiEvent & event) +{ + sendMouseEvent("onMouseDragged", event); +} + +void GuiMouseEventCtrl::onMouseEnter(const GuiEvent & event) +{ + sendMouseEvent("onMouseEnter", event); +} + +void GuiMouseEventCtrl::onMouseLeave(const GuiEvent & event) +{ + sendMouseEvent("onMouseLeave", event); +} + +void GuiMouseEventCtrl::onRightMouseDown(const GuiEvent & event) +{ + if(mLockMouse) + mouseLock(); + sendMouseEvent("onRightMouseDown", event); +} + +void GuiMouseEventCtrl::onRightMouseUp(const GuiEvent & event) +{ + if(mLockMouse) + mouseUnlock(); + sendMouseEvent("onRightMouseUp", event); +} + +void GuiMouseEventCtrl::onRightMouseDragged(const GuiEvent & event) +{ + sendMouseEvent("onRightMouseDragged", event); +} \ No newline at end of file diff --git a/Engine/source/gui/utility/guiMouseEventCtrl.h b/Engine/source/gui/utility/guiMouseEventCtrl.h new file mode 100644 index 000000000..3162113b4 --- /dev/null +++ b/Engine/source/gui/utility/guiMouseEventCtrl.h @@ -0,0 +1,75 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIMOUSEEVENTCTRL_H_ +#define _GUIMOUSEEVENTCTRL_H_ + +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif +#ifndef _EVENT_H_ +#include "platform/event.h" +#endif + + +class GuiMouseEventCtrl : public GuiControl +{ + private: + typedef GuiControl Parent; + void sendMouseEvent(const char * name, const GuiEvent &); + + // field info + bool mLockMouse; + + public: + + GuiMouseEventCtrl(); + + DECLARE_CALLBACK( void, onMouseDown, ( U8 modifier, Point2I mousePoint,U8 mouseClickCount )); + DECLARE_CALLBACK( void, onMouseUp, ( U8 modifier, Point2I mousePoint,U8 mouseClickCount )); + DECLARE_CALLBACK( void, onMouseMove, ( U8 modifier, Point2I mousePoint,U8 mouseClickCount )); + DECLARE_CALLBACK( void, onMouseDragged, ( U8 modifier, Point2I mousePoint,U8 mouseClickCount )); + DECLARE_CALLBACK( void, onMouseEnter, ( U8 modifier, Point2I mousePoint,U8 mouseClickCount )); + DECLARE_CALLBACK( void, onMouseLeave, ( U8 modifier, Point2I mousePoint,U8 mouseClickCount )); + DECLARE_CALLBACK( void, onRightMouseDown, ( U8 modifier, Point2I mousePoint,U8 mouseClickCount )); + DECLARE_CALLBACK( void, onRightMouseUp, ( U8 modifier, Point2I mousePoint,U8 mouseClickCount )); + DECLARE_CALLBACK( void, onRightMouseDragged, ( U8 modifier, Point2I mousePoint,U8 mouseClickCount )); + + // GuiControl + void onMouseDown(const GuiEvent & event); + void onMouseUp(const GuiEvent & event); + void onMouseMove(const GuiEvent & event); + void onMouseDragged(const GuiEvent & event); + void onMouseEnter(const GuiEvent & event); + void onMouseLeave(const GuiEvent & event); + void onRightMouseDown(const GuiEvent & event); + void onRightMouseUp(const GuiEvent & event); + void onRightMouseDragged(const GuiEvent & event); + + static void initPersistFields(); + + DECLARE_CONOBJECT( GuiMouseEventCtrl ); + DECLARE_CATEGORY( "Gui Other Script" ); + DECLARE_DESCRIPTION( "A control that relays all mouse events to script." ); +}; + +#endif diff --git a/Engine/source/gui/utility/messageVector.cpp b/Engine/source/gui/utility/messageVector.cpp new file mode 100644 index 000000000..2c0c18fb4 --- /dev/null +++ b/Engine/source/gui/utility/messageVector.cpp @@ -0,0 +1,618 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/utility/messageVector.h" +#include "core/fileObject.h" +#include "console/engineAPI.h" + +IMPLEMENT_CONOBJECT(MessageVector); + +ConsoleDocClass( MessageVector, + "@brief Store a list of chat messages.\n\n" + + "This is responsible for managing messages which appear in the chat HUD, not the actual control rendered to the screen\n\n" + + "@tsexample\n" + "// Declare ChatHud, which is what will display the actual chat from a MessageVector\n" + "new GuiMessageVectorCtrl(ChatHud) {\n" + " profile = \"ChatHudMessageProfile\";\n" + " horizSizing = \"width\";\n" + " vertSizing = \"height\";\n" + " position = \"1 1\";\n" + " extent = \"252 16\";\n" + " minExtent = \"8 8\";\n" + " visible = \"1\";\n" + " helpTag = \"0\";\n" + " lineSpacing = \"0\";\n" + " lineContinuedIndex = \"10\";\n" + " matchColor = \"0 0 255 255\";\n" + " maxColorIndex = \"5\";\n" + "};\n\n" + "// All messages are stored in this HudMessageVector, the actual\n" + "// MainChatHud only displays the contents of this vector.\n" + "new MessageVector(HudMessageVector);\n\n" + "// Attach the MessageVector to the chat control\n" + "chatHud.attach(HudMessageVector);\n" + "@endtsexample\n\n" + + "@see GuiMessageVectorCtrl for more details on how this is used." + + "@ingroup GuiUtil\n" +); + +DefineEngineMethod( MessageVector, clear, void, (),, + "Clear all messages in the vector\n\n" + "@tsexample\n" + "HudMessageVector.clear();\n" + "@endtsexample\n\n") +{ + object->clear(); +} + +//ConsoleMethod( MessageVector, clear, void, 2, 2, "Clear the message vector.") +//{ +// object->clear(); +//} + +DefineEngineMethod( MessageVector, pushBackLine, void, ( const char* msg, S32 tag ),, + "Push a line onto the back of the list.\n\n" + "@param msg Text that makes up the message\n" + "@param tag Numerical value associated with this message, useful for searching.\n\n" + "@tsexample\n" + "// Add the message...\n" + "HudMessageVector.pushBackLine(\"Hello World\", 0);\n" + "@endtsexample\n\n") +{ + object->pushBackLine(msg, tag); +} + +//ConsoleMethod( MessageVector, pushBackLine, void, 3, 4, "(string msg, int tag=0)" +// "Push a line onto the back of the list.") +//{ +// U32 tag = 0; +// if (argc == 4) +// tag = dAtoi(argv[3]); +// +// object->pushBackLine(argv[2], tag); +//} + +DefineEngineMethod( MessageVector, popBackLine, bool, (),, + "Pop a line from the back of the list; destroys the line.\n\n" + "@tsexample\n" + "HudMessageVector.popBackLine();\n" + "@endtsexample\n\n" + "@return False if there are no lines to pop (underflow), true otherwise") +{ + if (object->getNumLines() == 0) { + Con::errorf(ConsoleLogEntry::General, "MessageVector::popBackLine(): underflow"); + return false; + } + + object->popBackLine(); + return true; +} + +//ConsoleMethod( MessageVector, popBackLine, bool, 2, 2, "()" +// "Pop a line from the back of the list; destroys the line.") +//{ +// if (object->getNumLines() == 0) { +// Con::errorf(ConsoleLogEntry::General, "MessageVector::popBackLine(): underflow"); +// return false; +// } +// +// object->popBackLine(); +// return true; +//} + +DefineEngineMethod( MessageVector, pushFrontLine, void, ( const char* msg, S32 tag ),, + "Push a line onto the front of the vector.\n\n" + "@param msg Text that makes up the message\n" + "@param tag Numerical value associated with this message, useful for searching.\n\n" + "@tsexample\n" + "// Add the message...\n" + "HudMessageVector.pushFrontLine(\"Hello World\", 0);\n" + "@endtsexample\n\n") +{ + object->pushFrontLine(msg, tag); +} + +//ConsoleMethod( MessageVector, pushFrontLine, void, 3, 4, "(string msg, int tag=0)" +// "Push a line onto the front of the vector.") +//{ +// U32 tag = 0; +// if (argc == 4) +// tag = dAtoi(argv[3]); +// +// object->pushFrontLine(argv[2], tag); +//} + +DefineEngineMethod( MessageVector, popFrontLine, bool, (),, + "Pop a line from the front of the vector, destroying the line.\n\n" + "@tsexample\n" + "HudMessageVector.popFrontLine();\n" + "@endtsexample\n\n" + "@return False if there are no lines to pop (underflow), true otherwise") +{ + if (object->getNumLines() == 0) { + Con::errorf(ConsoleLogEntry::General, "MessageVector::popFrontLine(): underflow"); + return false; + } + + object->popFrontLine(); + return true; +} + +//ConsoleMethod( MessageVector, popFrontLine, bool, 2, 2, +// "Pop a line from the front of the vector, destroying the line.") +//{ +// if (object->getNumLines() == 0) { +// Con::errorf(ConsoleLogEntry::General, "MessageVector::popFrontLine(): underflow"); +// return false; +// } +// +// object->popFrontLine(); +// return true; +//} + +DefineEngineMethod( MessageVector, insertLine, bool, ( S32 insertPos, const char* msg, S32 tag ),, + "Push a line onto the back of the list.\n\n" + "@param msg Text that makes up the message\n" + "@param tag Numerical value associated with this message, useful for searching.\n\n" + "@tsexample\n" + "// Add the message...\n" + "HudMessageVector.insertLine(1, \"Hello World\", 0);\n" + "@endtsexample\n\n" + "@return False if insertPos is greater than the number of lines in the current vector") +{ + if (insertPos > object->getNumLines()) + return false; + + object->insertLine(insertPos, msg, tag); + return true; +} + +//ConsoleMethod( MessageVector, insertLine, bool, 4, 5, "(int insertPos, string msg, int tag=0)" +// "Insert a new line into the vector at the specified position.") +//{ +// U32 pos = U32(dAtoi(argv[2])); +// if (pos > object->getNumLines()) +// return false; +// +// S32 tag = 0; +// if (argc == 5) +// tag = dAtoi(argv[4]); +// +// object->insertLine(pos, argv[3], tag); +// return true; +//} + +DefineEngineMethod( MessageVector, deleteLine, bool, ( S32 deletePos),, + "Delete the line at the specified position.\n\n" + "@param deletePos Position in the vector containing the line to be deleted\n" + "@tsexample\n" + "// Delete the first line (index 0) in the vector...\n" + "HudMessageVector.deleteLine(0);\n" + "@endtsexample\n\n" + "@return False if deletePos is greater than the number of lines in the current vector") +{ + if (deletePos >= object->getNumLines()) + return false; + + object->deleteLine(deletePos); + return true; +} + +//ConsoleMethod( MessageVector, deleteLine, bool, 3, 3, "(int deletePos)" +// "Delete the line at the specified position.") +//{ +// U32 pos = U32(dAtoi(argv[2])); +// if (pos >= object->getNumLines()) +// return false; +// +// object->deleteLine(pos); +// return true; +//} +static ConsoleDocFragment _MessageVectordump1( + "@brief Dump the message vector to a file without a header.\n\n" + + "@param filename Name and path of file to dump text to.\n" + + "@tsexample\n" + "// Dump the entire chat log to a text file\n" + "HudMessageVector.dump(\"./chatLog.txt\");\n" + "@endtsexample\n\n\n", + "MessageVector", + "void dump( string filename);"); + +static ConsoleDocFragment _MessageVectordump2( + "@brief Dump the message vector to a file with a header.\n\n" + + "@param filename Name and path of file to dump text to.\n" + "@param header Prefix information for write out\n\n" + + "@tsexample\n" + "// Arbitrary header data\n" + "%headerInfo = \"Ars Moriendi Chat Log\";\n\n" + "// Dump the entire chat log to a text file\n" + "HudMessageVector.dump(\"./chatLog.txt\", %headerInfo);\n" + "@endtsexample\n\n\n", + "MessageVector", + "void dump( string filename, string header);"); + +ConsoleMethod( MessageVector, dump, void, 3, 4, "(string filename, string header=NULL)" + "Dump the message vector to a file, optionally prefixing a header." + "@hide") +{ + + if ( argc == 4 ) + object->dump( argv[2], argv[3] ); + else + object->dump( argv[2] ); +} + +DefineEngineMethod( MessageVector, getNumLines, S32, (),, + "Get the number of lines in the vector.\n\n" + "@tsexample\n" + "// Find out how many lines have been stored in HudMessageVector\n" + "%chatLines = HudMessageVector.getNumLines();\n" + "echo(%chatLines);\n" + "@endtsexample\n\n") +{ + return object->getNumLines(); +} + +//ConsoleMethod( MessageVector, getNumLines, S32, 2, 2, "Get the number of lines in the vector.") +//{ +// return object->getNumLines(); +//} + +DefineEngineMethod( MessageVector, getLineTextByTag, const char*, ( S32 tag),, + "Scan through the lines in the vector, returning the first line that has a matching tag.\n\n" + "@param tag Numerical value assigned to a message when it was added or inserted\n" + "@tsexample\n" + "// Locate text in the vector tagged with the value \"1\", then print it\n" + "%taggedText = HudMessageVector.getLineTextByTag(1);\n" + "echo(%taggedText);\n" + "@endtsexample\n\n" + "@return Text from a line with matching tag, other wise \"\"") +{ + for(U32 i = 0; i < object->getNumLines(); i++) + if(object->getLine(i).messageTag == tag) + return object->getLine(i).message; + return ""; +} + +//ConsoleMethod( MessageVector, getLineTextByTag, const char*, 3, 3, "(int tag)" +// "Scan through the lines in the vector, returning the first line that has a matching tag.") +//{ +// U32 tag = dAtoi(argv[2]); +// +// for(U32 i = 0; i < object->getNumLines(); i++) +// if(object->getLine(i).messageTag == tag) +// return object->getLine(i).message; +// return ""; +//} + +DefineEngineMethod( MessageVector, getLineIndexByTag, S32, ( S32 tag),, + "Scan through the vector, returning the line number of the first line that matches the specified tag; else returns -1 if no match was found.\n\n" + "@param tag Numerical value assigned to a message when it was added or inserted\n" + "@tsexample\n" + "// Locate a line of text tagged with the value \"1\", then delete it.\n" + "%taggedLine = HudMessageVector.getLineIndexByTag(1);\n" + "HudMessageVector.deleteLine(%taggedLine);\n" + "@endtsexample\n\n" + "@return Line with matching tag, other wise -1") +{ + for(U32 i = 0; i < object->getNumLines(); i++) + if(object->getLine(i).messageTag == tag) + return i; + return -1; +} + +//ConsoleMethod( MessageVector, getLineIndexByTag, S32, 3, 3, "(int tag)" +// "Scan through the vector, returning the line number of the first line that matches the specified tag; else returns -1 if no match was found.") +//{ +// U32 tag = dAtoi(argv[2]); +// +// for(U32 i = 0; i < object->getNumLines(); i++) +// if(object->getLine(i).messageTag == tag) +// return i; +// return -1; +//} + +DefineEngineMethod( MessageVector, getLineText, const char*, ( S32 pos),, + "Get the text at a specified line.\n\n" + "@param pos Position in vector to grab text from\n" + "@tsexample\n" + "// Print a line of text at position 1.\n" + "%text = HudMessageVector.getLineText(1);\n" + "echo(%text);\n" + "@endtsexample\n\n" + "@return Text at specified line, if the position is greater than the number of lines return \"\"") +{ + if (pos >= object->getNumLines()) { + Con::errorf(ConsoleLogEntry::General, "MessageVector::getLineText(con): out of bounds line"); + return ""; + } + + return object->getLine(pos).message; +} + +//ConsoleMethod( MessageVector, getLineText, const char*, 3, 3, "(int line)" +// "Get the text at a specified line.") +//{ +// U32 pos = U32(dAtoi(argv[2])); +// if (pos >= object->getNumLines()) { +// Con::errorf(ConsoleLogEntry::General, "MessageVector::getLineText(con): out of bounds line"); +// return ""; +// } +// +// return object->getLine(pos).message; +//} + +DefineEngineMethod( MessageVector, getLineTag, S32, ( S32 pos),, + "Get the tag of a specified line.\n\n" + "@param pos Position in vector to grab tag from\n" + "@tsexample\n" + "// Remove all lines that do not have a tag value of 1.\n" + "while( HudMessageVector.getNumLines())\n" + "{\n" + " %tag = HudMessageVector.getLineTag(1);\n" + " if(%tag != 1)\n" + " %tag.delete();\n" + " HudMessageVector.popFrontLine();\n" + "}\n" + "@endtsexample\n\n" + "@return Tag value of a given line, if the position is greater than the number of lines return 0") +{ + if (pos >= object->getNumLines()) { + Con::errorf(ConsoleLogEntry::General, "MessageVector::getLineTag(con): out of bounds line"); + return 0; + } + + return object->getLine(pos).messageTag; +} + +//ConsoleMethod( MessageVector, getLineTag, S32, 3, 3, "(int line)" +// "Get the tag of a specified line.") +//{ +// U32 pos = U32(dAtoi(argv[2])); +// if (pos >= object->getNumLines()) { +// Con::errorf(ConsoleLogEntry::General, "MessageVector::getLineTag(con): out of bounds line"); +// return 0; +// } +// +// return object->getLine(pos).messageTag; +//} + +//-------------------------------------------------------------------------- +MessageVector::MessageVector() +{ + VECTOR_SET_ASSOCIATION(mMessageLines); + VECTOR_SET_ASSOCIATION(mSpectators); +} + + +//-------------------------------------------------------------------------- +MessageVector::~MessageVector() +{ + for (U32 i = 0; i < mMessageLines.size(); i++) { + char* pDelete = const_cast(mMessageLines[i].message); + delete [] pDelete; + mMessageLines[i].message = 0; + mMessageLines[i].messageTag = 0xFFFFFFFF; + } + mMessageLines.clear(); +} + + +//-------------------------------------------------------------------------- +void MessageVector::initPersistFields() +{ + Parent::initPersistFields(); +} + + + +//-------------------------------------------------------------------------- +bool MessageVector::onAdd() +{ + return Parent::onAdd(); +} + + +//-------------------------------------------------------------------------- +void MessageVector::onRemove() +{ + // Delete all the lines from the observers, and then forcibly detatch ourselves + // + for (S32 i = mMessageLines.size() - 1; i >= 0; i--) + spectatorMessage(LineDeleted, i); + spectatorMessage(VectorDeletion, 0); + mSpectators.clear(); + + Parent::onRemove(); +} + + +//-------------------------------------------------------------------------- +void MessageVector::pushBackLine(const char* newMessage, const S32 newMessageTag) +{ + insertLine(mMessageLines.size(), newMessage, newMessageTag); +} + + +void MessageVector::popBackLine() +{ + AssertFatal(mMessageLines.size() != 0, "MessageVector::popBackLine: nothing to pop!"); + if (mMessageLines.size() == 0) + return; + + deleteLine(mMessageLines.size() - 1); +} + +void MessageVector::clear() +{ + while(mMessageLines.size()) + deleteLine(mMessageLines.size() - 1); +} + +//-------------------------------------------------------------------------- +void MessageVector::pushFrontLine(const char* newMessage, const S32 newMessageTag) +{ + insertLine(0, newMessage, newMessageTag); +} + + +void MessageVector::popFrontLine() +{ + AssertFatal(mMessageLines.size() != 0, "MessageVector::popBackLine: nothing to pop!"); + if (mMessageLines.size() == 0) + return; + + deleteLine(0); +} + + +//-------------------------------------------------------------------------- +void MessageVector::insertLine(const U32 position, + const char* newMessage, + const S32 newMessageTag) +{ + AssertFatal(position >= 0 && position <= mMessageLines.size(), "MessageVector::insertLine: out of range position!"); + AssertFatal(newMessage != NULL, "Error, no message to insert!"); + + U32 len = dStrlen(newMessage) + 1; + char* copy = new char[len]; + dStrcpy(copy, newMessage); + + mMessageLines.insert(position); + mMessageLines[position].message = copy; + mMessageLines[position].messageTag = newMessageTag; + + // Notify of insert + spectatorMessage(LineInserted, position); +} + + +//-------------------------------------------------------------------------- +void MessageVector::deleteLine(const U32 position) +{ + AssertFatal(position >= 0 && position < mMessageLines.size(), "MessageVector::deleteLine: out of range position!"); + + char* pDelete = const_cast(mMessageLines[position].message); + delete [] pDelete; + mMessageLines[position].message = NULL; + mMessageLines[position].messageTag = 0xFFFFFFFF; + + mMessageLines.erase(position); + + // Notify of delete + spectatorMessage(LineDeleted, position); +} + + +//-------------------------------------------------------------------------- +bool MessageVector::dump( const char* filename, const char* header ) +{ + Con::printf( "Dumping message vector %s to %s...", getName(), filename ); + FileObject file; + if ( !file.openForWrite( filename ) ) + return( false ); + + // If passed a header line, write it out first: + if ( header ) + file.writeLine( (const U8*) header ); + + // First write out the record count: + char* lineBuf = (char*) dMalloc( 10 ); + dSprintf( lineBuf, 10, "%d", mMessageLines.size() ); + file.writeLine( (const U8*) lineBuf ); + + // Write all of the lines of the message vector: + U32 len; + for ( U32 i = 0; i < mMessageLines.size(); i++ ) + { + len = ( dStrlen( mMessageLines[i].message ) * 2 ) + 10; + lineBuf = (char*) dRealloc( lineBuf, len ); + dSprintf( lineBuf, len, "%d ", mMessageLines[i].messageTag ); + expandEscape( lineBuf + dStrlen( lineBuf ), mMessageLines[i].message ); + file.writeLine( (const U8*) lineBuf ); + } + + file.close(); + return( true ); +} + + +//-------------------------------------------------------------------------- +void MessageVector::registerSpectator(SpectatorCallback callBack, void *spectatorKey) +{ + AssertFatal(callBack != NULL, "Error, must have a callback!"); + + // First, make sure that this hasn't been registered already... + U32 i; + for (i = 0; i < mSpectators.size(); i++) { + AssertFatal(mSpectators[i].key != spectatorKey, "Error, spectator key already registered!"); + if (mSpectators[i].key == spectatorKey) + return; + } + + mSpectators.increment(); + mSpectators.last().callback = callBack; + mSpectators.last().key = spectatorKey; + + // Need to message this spectator of all the lines currently inserted... + for (i = 0; i < mMessageLines.size(); i++) { + (*mSpectators.last().callback)(mSpectators.last().key, + LineInserted, i); + } +} + +void MessageVector::unregisterSpectator(void * spectatorKey) +{ + for (U32 i = 0; i < mSpectators.size(); i++) { + if (mSpectators[i].key == spectatorKey) { + // Need to message this spectator of all the lines currently inserted... + for (S32 j = mMessageLines.size() - 1; j >= 0 ; j--) { + (*mSpectators[i].callback)(mSpectators[i].key, + LineDeleted, j); + } + + mSpectators.erase(i); + return; + } + } + + AssertFatal(false, "MessageVector::unregisterSpectator: tried to unregister a spectator that isn't subscribed!"); + Con::errorf(ConsoleLogEntry::General, + "MessageVector::unregisterSpectator: tried to unregister a spectator that isn't subscribed!"); +} + +void MessageVector::spectatorMessage(MessageCode code, const U32 arg) +{ + for (U32 i = 0; i < mSpectators.size(); i++) { + (*mSpectators[i].callback)(mSpectators[i].key, + code, arg); + } +} + diff --git a/Engine/source/gui/utility/messageVector.h b/Engine/source/gui/utility/messageVector.h new file mode 100644 index 000000000..2169dd04f --- /dev/null +++ b/Engine/source/gui/utility/messageVector.h @@ -0,0 +1,127 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MESSAGEVECTOR_H_ +#define _MESSAGEVECTOR_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _SIMBASE_H_ +#include "console/simBase.h" +#endif + +/// Store a list of chat messages. +/// +/// This is responsible for managing messages which appear in the chat HUD. +/// +/// @see GuiMessageVectorCtrl for more details on how this is used. +class MessageVector : public SimObject +{ + typedef SimObject Parent; + + //-------------------------------------- Public interface... + public: + MessageVector(); + ~MessageVector(); + + public: + struct MessageLine { + char* message; + S32 messageTag; + }; + + + // Spectator registration... + public: + enum MessageCode { + LineInserted = 0, + LineDeleted = 1, + + VectorDeletion = 2 + }; + + typedef void (*SpectatorCallback)(void * spectatorKey, + const MessageCode code, + const U32 argument); + + void registerSpectator(SpectatorCallback, void * spectatorKey); + void unregisterSpectator(void *spectatorKey); + + // Query functions + public: + U32 getNumLines() const; + const MessageLine& getLine(const U32 line) const; + + // Mutators + public: + void pushBackLine(const char*, const S32); + void popBackLine(); + void pushFrontLine(const char*, const S32); + void popFrontLine(); + void clear(); + + virtual void insertLine(const U32 position, const char*, const S32); + virtual void deleteLine(const U32); + + bool dump( const char* filename, const char* header = NULL ); + + + //-------------------------------------- Internal interface + protected: + bool onAdd(); + void onRemove(); + + private: + struct SpectatorRef { + SpectatorCallback callback; + void * key; + }; + + Vector mMessageLines; + + Vector mSpectators; + void spectatorMessage(MessageCode, const U32 arg); + + public: + DECLARE_CONOBJECT(MessageVector); + static void initPersistFields(); +}; + + +//-------------------------------------------------------------------------- +inline U32 MessageVector::getNumLines() const +{ + return mMessageLines.size(); +} + +//-------------------------------------------------------------------------- +inline const MessageVector::MessageLine& MessageVector::getLine(const U32 line) const +{ + AssertFatal(line < mMessageLines.size(), "MessageVector::getLine: out of bounds line index"); + return mMessageLines[line]; +} + +#endif // _H_GUICHATVECTOR_ diff --git a/Engine/source/gui/worldEditor/creator.cpp b/Engine/source/gui/worldEditor/creator.cpp new file mode 100644 index 000000000..f1d786fb0 --- /dev/null +++ b/Engine/source/gui/worldEditor/creator.cpp @@ -0,0 +1,475 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/worldEditor/creator.h" + +#include "gfx/gfxDrawUtil.h" + + +IMPLEMENT_CONOBJECT(CreatorTree); + +ConsoleDocClass( CreatorTree, + "@brief Creator tree from old editor. Not used in current editor.\n\n" + "@internal" +); + +//------------------------------------------------------------------------------ +// Class CreatorTree::Node +//------------------------------------------------------------------------------ + +CreatorTree::Node::Node() : + mFlags(0), + mParent(0), + mName(0), + mValue(0), + mId(0), + mTab(0) +{ + VECTOR_SET_ASSOCIATION(mChildren); +} + +CreatorTree::Node::~Node() +{ + for(U32 i = 0; i < mChildren.size(); i++) + delete mChildren[i]; +} + +//------------------------------------------------------------------------------ + +void CreatorTree::Node::expand(bool exp) +{ + if(exp) + { + if(mParent) + mParent->expand(exp); + mFlags.set(Node::Expanded); + } + else if(!isRoot()) + { + if(isGroup()) + for(U32 i = 0; i < mChildren.size(); i++) + mChildren[i]->expand(exp); + + mFlags.clear(Selected); + mFlags.clear(Expanded); + } +} + +//------------------------------------------------------------------------------ + +CreatorTree::Node * CreatorTree::Node::find(S32 id) +{ + if(mId == id) + return(this); + + if(!isGroup()) + return(0); + + for(U32 i = 0; i < mChildren.size(); i++) + { + Node * node = mChildren[i]->find(id); + if(node) + return(node); + } + + return(0); +} + +//------------------------------------------------------------------------------ + +bool CreatorTree::Node::isFirst() +{ + AssertFatal(!isRoot(), "CreatorTree::Node::isFirst - cannot call on root node"); + return(this == mParent->mChildren[0]); +} + +bool CreatorTree::Node::isLast() +{ + AssertFatal(!isRoot(), "CreatorTree::Node::isLast - cannot call on root node"); + return(this == mParent->mChildren[mParent->mChildren.size()-1]); +} + +bool CreatorTree::Node::hasChildItem() +{ + for(U32 i = 0; i < mChildren.size(); i++) + { + if(mChildren[i]->isGroup() && mChildren[i]->hasChildItem()) + return(true); + + if(!mChildren[i]->isGroup()) + return(true); + } + + return(false); +} + +S32 CreatorTree::Node::getSelected() +{ + for(U32 i = 0; i < mChildren.size(); i++) + { + if(mChildren[i]->isSelected()) + return(mChildren[i]->mId); + else if(mChildren[i]->isGroup()) + { + S32 ret = mChildren[i]->getSelected(); + if(ret != -1) + return(ret); + } + } + return(-1); +} + +//------------------------------------------------------------------------------ +// Class CreatorTree +//------------------------------------------------------------------------------ +CreatorTree::CreatorTree() : + mCurId(0), + mTxtOffset(5), + mRoot(0) +{ + VECTOR_SET_ASSOCIATION(mNodeList); + clear(); +} + +CreatorTree::~CreatorTree() +{ + delete mRoot; +} + +//------------------------------------------------------------------------------ + +CreatorTree::Node * CreatorTree::createNode(const char * name, const char * value, bool group, Node * parent) +{ + Node * node = new Node(); + node->mId = mCurId++; + node->mName = name ? StringTable->insert(name) : 0; + node->mValue = value ? StringTable->insert(value) : 0; + node->mFlags.set(Node::Group, group); + + // add to the parent group + if(parent) + { + node->mParent = parent; + if(!addNode(parent, node)) + { + delete node; + return(0); + } + } + + return(node); +} + +//------------------------------------------------------------------------------ + +void CreatorTree::clear() +{ + delete mRoot; + mCurId = 0; + mRoot = createNode(0, 0, true); + mRoot->mFlags.set(Node::Root | Node::Expanded); + mSize = Point2I(1,0); +} + +//------------------------------------------------------------------------------ + +bool CreatorTree::addNode(Node * parent, Node * node) +{ + if(!parent->isGroup()) + return(false); + + // + parent->mChildren.push_back(node); + return(true); +} + +//------------------------------------------------------------------------------ + +CreatorTree::Node * CreatorTree::findNode(S32 id) +{ + return(mRoot->find(id)); +} + +//------------------------------------------------------------------------------ + +void CreatorTree::sort() +{ + // groups then items by alpha +} + +//------------------------------------------------------------------------------ +ConsoleMethod( CreatorTree, addGroup, S32, 4, 4, "(string group, string name, string value)") +{ + CreatorTree::Node * grp = object->findNode(dAtoi(argv[2])); + + if(!grp || !grp->isGroup()) + return(-1); + + // return same named group if found... + for(U32 i = 0; i < grp->mChildren.size(); i++) + if(!dStricmp(argv[3], grp->mChildren[i]->mName)) + return(grp->mChildren[i]->mId); + + CreatorTree::Node * node = object->createNode(argv[3], 0, true, grp); + object->build(); + return(node ? node->getId() : -1); +} + +ConsoleMethod( CreatorTree, addItem, S32, 5, 5, "(Node group, string name, string value)") +{ + CreatorTree::Node * grp = object->findNode(dAtoi(argv[2])); + + if(!grp || !grp->isGroup()) + return -1; + + CreatorTree::Node * node = object->createNode(argv[3], argv[4], false, grp); + object->build(); + return(node ? node->getId() : -1); +} + +//------------------------------------------------------------------------------ +ConsoleMethod( CreatorTree, fileNameMatch, bool, 5, 5, "(string world, string type, string filename)"){ + // argv[2] - world short + // argv[3] - type short + // argv[4] - filename + + // interior filenames + // 0 - world short ('b', 'x', ...) + // 1-> - type short ('towr', 'bunk', ...) + U32 typeLen = dStrlen(argv[3]); + if(dStrlen(argv[4]) < (typeLen + 1)) + return(false); + + // world + if(dToupper(argv[4][0]) != dToupper(argv[2][0])) + return(false); + + return(!dStrnicmp(argv[4]+1, argv[3], typeLen)); +} + +ConsoleMethod( CreatorTree, getSelected, S32, 2, 2, "Return a handle to the currently selected item.") +{ + return(object->getSelected()); +} + +ConsoleMethod( CreatorTree, isGroup, bool, 3, 3, "(Group g)") +{ + CreatorTree::Node * node = object->findNode(dAtoi(argv[2])); + if(node && node->isGroup()) + return(true); + return(false); +} + +ConsoleMethod( CreatorTree, getName, const char*, 3, 3, "(Node item)") +{ + CreatorTree::Node * node = object->findNode(dAtoi(argv[2])); + return(node ? node->mName : 0); +} + +ConsoleMethod( CreatorTree, getValue, const char*, 3, 3, "(Node n)") +{ + CreatorTree::Node * node = object->findNode(dAtoi(argv[2])); + return(node ? node->mValue : 0); +} + +ConsoleMethod( CreatorTree, clear, void, 2, 2, "Clear the tree.") +{ + object->clear(); +} + +ConsoleMethod( CreatorTree, getParent, S32, 3, 3, "(Node n)") +{ + CreatorTree::Node * node = object->findNode(dAtoi(argv[2])); + if(node && node->mParent) + return(node->mParent->getId()); + + return(-1); +} +//------------------------------------------------------------------------------ + +void CreatorTree::buildNode(Node * node, U32 tab) +{ + if(node->isExpanded()) + for(U32 i = 0; i < node->mChildren.size(); i++) + { + Node * child = node->mChildren[i]; + child->mTab = tab; + child->select(false); + mNodeList.push_back(child); + + // grab width + if(bool(mProfile->mFont) && child->mName) + { + S32 width = (tab + 1) * mTabSize + mProfile->mFont->getStrWidth(child->mName) + mTxtOffset; + if(width > mMaxWidth) + mMaxWidth = width; + } + + if(node->mChildren[i]->isGroup()) + buildNode(node->mChildren[i], tab+1); + } +} + +//------------------------------------------------------------------------------ + +void CreatorTree::build() +{ + mMaxWidth = 0; + mNodeList.clear(); + buildNode(mRoot, 0); + mCellSize.set( mMaxWidth + 1, 11 ); + setSize(Point2I(1, mNodeList.size())); +} + +//------------------------------------------------------------------------------ +bool CreatorTree::onWake() +{ + if(!Parent::onWake()) + return(false); + + mTabSize = 11; + + + // + build(); + mCellSize.set( mMaxWidth + 1, 11 ); + setSize(Point2I(1, mNodeList.size())); + return true; +} + +//------------------------------------------------------------------------------ + +void CreatorTree::onMouseUp(const GuiEvent & event) +{ + onAction(); +} + +void CreatorTree::onMouseDown(const GuiEvent & event) +{ + Point2I pos = globalToLocalCoord(event.mousePoint); + + bool dblClick = event.mouseClickCount > 1; + + // determine cell + Point2I cell(pos.x < 0 ? -1 : pos.x / mCellSize.x, pos.y < 0 ? -1 : pos.y / mCellSize.y); + if(cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y) + { + Node * node = mNodeList[cell.y]; + S32 offset = mTabSize * node->mTab; + if(node->isGroup() && node->mChildren.size() && pos.x >= offset && pos.x <= (offset + mTabSize)) + { + node->expand(!node->isExpanded()); + build(); + dblClick = false; + } + + if(pos.x >= offset) + { + if(dblClick) + node->expand(!node->isExpanded()); + build(); + node->select(true); + } + } +} + +//------------------------------------------------------------------------------ + +void CreatorTree::onMouseDragged(const GuiEvent & event) +{ + TORQUE_UNUSED(event); +} + +//------------------------------------------------------------------------------ +void CreatorTree::onRenderCell(Point2I offset, Point2I cell, bool, bool) +{ + Point2I cellOffset = offset; + + Node *node = mNodeList[cell.y]; + + // Get our points + Point2I boxStart( cellOffset.x + mTabSize * node->mTab, cellOffset.y ); + + boxStart.x += 2; + boxStart.y += 1; + + Point2I boxEnd = Point2I( boxStart ); + + boxEnd.x += 8; + boxEnd.y += 8; + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + + // Start drawing stuff + if( node->isGroup() ) + { + // If we need a box... + drawer->drawRectFill( boxStart, boxEnd, mProfile->mFillColor ); // Box background + drawer->drawRect( boxStart, boxEnd, mProfile->mFontColor ); // Border + + // Cross line + drawer->drawLine( boxStart.x + 2, boxStart.y + 4, boxStart.x + 7, boxStart.y + 4, mProfile->mFontColor ); + + if( !node->isExpanded() ) // If it's a [+] draw down line + drawer->drawLine( boxStart.x + 4, boxStart.y + 2, boxStart.x + 4, boxStart.y + 7, mProfile->mFontColor ); + } + else + { + // Draw horizontal line + drawer->drawLine( boxStart.x + 4, boxStart.y + 4, boxStart.x + 9, boxStart.y + 4, mProfile->mFontColor ); + + if( !node->isLast() ) // If it's a continuing one, draw a long down line + drawer->drawLine( boxStart.x + 4, boxStart.y - 6, boxStart.x + 4, boxStart.y + 10, mProfile->mFontColor ); + else // Otherwise, just a small one + drawer->drawLine( boxStart.x + 4, boxStart.y - 2, boxStart.x + 4, boxStart.y + 4, mProfile->mFontColor ); + } + + //draw in all the required continuation lines + Node *parent = node->mParent; + + while( !parent->isRoot() ) + { + if( !parent->isLast() ) + { + drawer->drawLine( cellOffset.x + ( parent->mTab * mTabSize ) + 6, + cellOffset.y - 2, + cellOffset.x + ( parent->mTab * mTabSize ) + 6, + cellOffset.y + 11, + mProfile->mFontColor ); + } + parent = parent->mParent; + } + + ColorI fontColor = mProfile->mFontColor; + if( node->isSelected() ) + fontColor = mProfile->mFontColorHL; + else if( node->isGroup() && node->hasChildItem() ) + fontColor.set( 128, 0, 0 ); + else if( !node->isGroup() ) + fontColor.set( 0, 0, 128 ); + + drawer->setBitmapModulation(fontColor); //node->isSelected() ? mProfile->mFontColorHL : mProfile->mFontColor); + drawer->drawText( mProfile->mFont, + Point2I( offset.x + mTxtOffset + mTabSize * ( node->mTab + 1 ), offset.y ), + node->mName); +} diff --git a/Engine/source/gui/worldEditor/creator.h b/Engine/source/gui/worldEditor/creator.h new file mode 100644 index 000000000..074f34ad4 --- /dev/null +++ b/Engine/source/gui/worldEditor/creator.h @@ -0,0 +1,120 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _CREATOR_H_ +#define _CREATOR_H_ + +#ifndef _GUIARRAYCTRL_H_ +#include "gui/core/guiArrayCtrl.h" +#endif + + +/// Creator tree from old editor. Not used in current editor. +class CreatorTree : public GuiArrayCtrl +{ + typedef GuiArrayCtrl Parent; + public: + + class Node + { + public: + Node(); + ~Node(); + + enum { + Group = BIT(0), + Expanded = BIT(1), + Selected = BIT(2), + Root = BIT(3) + }; + + BitSet32 mFlags; + S32 mId; + U32 mTab; + Node * mParent; + Vector mChildren; + StringTableEntry mName; + StringTableEntry mValue; + + void expand(bool exp); + void select(bool sel){mFlags.set(Selected, sel);} + + Node * find(S32 id); + + // + bool isGroup(){return(mFlags.test(Group));} + bool isExpanded(){return(mFlags.test(Expanded));} + bool isSelected(){return(mFlags.test(Selected));} + bool isRoot(){return(mFlags.test(Root));} + S32 getId(){return(mId);} + bool hasChildItem(); + S32 getSelected(); + + // + bool isFirst(); + bool isLast(); + }; + + CreatorTree(); + ~CreatorTree(); + + // + S32 mCurId; + Node * mRoot; + Vector mNodeList; + + // + void buildNode(Node * node, U32 tab); + void build(); + + // + bool addNode(Node * parent, Node * node); + Node * createNode(const char * name, const char * value, bool group = false, Node * parent = 0); + Node * findNode(S32 id); + S32 getSelected(){return(mRoot->getSelected());} + + // + void expandNode(Node * node, bool expand); + void selectNode(Node * node, bool select); + + // + void sort(); + void clear(); + + S32 mTabSize; + S32 mMaxWidth; + S32 mTxtOffset; + + // GuiControl + void onMouseDown(const GuiEvent & event); + void onMouseDragged(const GuiEvent & event); + void onMouseUp(const GuiEvent & event); + bool onWake(); + + // GuiArrayCtrl + void onRenderCell(Point2I offset, Point2I cell, bool, bool); + + DECLARE_CONOBJECT(CreatorTree); + DECLARE_CATEGORY( "Gui Editor" ); +}; + +#endif diff --git a/Engine/source/gui/worldEditor/editTSCtrl.cpp b/Engine/source/gui/worldEditor/editTSCtrl.cpp new file mode 100644 index 000000000..123362123 --- /dev/null +++ b/Engine/source/gui/worldEditor/editTSCtrl.cpp @@ -0,0 +1,1395 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/worldEditor/editTSCtrl.h" + +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "T3D/gameBase/gameConnection.h" +#include "gui/worldEditor/editor.h" +#include "gui/core/guiCanvas.h" +#include "terrain/terrData.h" +#include "T3D/missionArea.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxDrawUtil.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/gfxDebugEvent.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "renderInstance/renderBinManager.h" + + +IMPLEMENT_CONOBJECT(EditTSCtrl); +ConsoleDocClass( EditTSCtrl, + "@brief 3D view control used specifically by Torque 3D's editors.\n\n" + + "For Torque 3D editors only, not for actual game development\n\n" + + "@ingroup Editors\n" + + "@internal" +); + +Point3F EditTSCtrl::smCamPos; +MatrixF EditTSCtrl::smCamMatrix; +bool EditTSCtrl::smCamOrtho = false; +F32 EditTSCtrl::smCamNearPlane; +F32 EditTSCtrl::smCamFOV = 75.0f; +F32 EditTSCtrl::smVisibleDistanceScale = 1.0f; +U32 EditTSCtrl::smSceneBoundsMask = EnvironmentObjectType | TerrainObjectType | WaterObjectType | CameraObjectType; +Point3F EditTSCtrl::smMinSceneBounds = Point3F(500.0f, 500.0f, 500.0f); + +EditTSCtrl::EditTSCtrl() +{ + mGizmoProfile = NULL; + mGizmo = NULL; + + mRenderMissionArea = true; + mMissionAreaFillColor.set(255,0,0,20); + mMissionAreaFrameColor.set(255,0,0,128); + mMissionAreaHeightAdjust = 5.0f; + + mConsoleFrameColor.set(255,0,0,255); + mConsoleFillColor.set(255,0,0,120); + mConsoleSphereLevel = 1; + mConsoleCircleSegments = 32; + mConsoleLineWidth = 1; + mRightMousePassThru = true; + mMiddleMousePassThru = true; + + mConsoleRendering = false; + + mDisplayType = DisplayTypePerspective; + mOrthoFOV = 50.0f; + mOrthoCamTrans.set(0.0f, 0.0f, 0.0f); + + mIsoCamAngle = mDegToRad(45.0f); + mIsoCamRot = EulerF(0, 0, 0); + + mRenderGridPlane = true; + mGridPlaneOriginColor = ColorI(255, 255, 255, 100); + mGridPlaneColor = ColorI(102, 102, 102, 100); + mGridPlaneMinorTickColor = ColorI(51, 51, 51, 100); + mGridPlaneMinorTicks = 9; + mGridPlaneSize = 1.0f; + mGridPlaneSizePixelBias = 10.0f; + + mLastMousePos.set(0, 0); + + mAllowBorderMove = false; + mMouseMoveBorder = 20; + mMouseMoveSpeed = 0.1f; + mLastBorderMoveTime = 0; + mLeftMouseDown = false; + mRightMouseDown = false; + mMiddleMouseDown = false; + mMiddleMouseTriggered = false; + mMouseLeft = false; + + mBlendSB = NULL; + +} + +EditTSCtrl::~EditTSCtrl() +{ + mBlendSB = NULL; +} + +//------------------------------------------------------------------------------ + +bool EditTSCtrl::onAdd() +{ + if(!Parent::onAdd()) + return(false); + + // give all derived access to the fields + setModStaticFields(true); + + GFXStateBlockDesc blenddesc; + blenddesc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha); + mBlendSB = GFX->createStateBlock( blenddesc ); + + if ( !mGizmoProfile ) + { + Con::errorf( "EditTSCtrl::onadd - gizmoProfile was not assigned, cannot create control!" ); + return false; + } + + mGizmo = new Gizmo(); + mGizmo->setProfile( mGizmoProfile ); + mGizmo->registerObject(); + + return true; +} + +void EditTSCtrl::onRemove() +{ + Parent::onRemove(); + + if ( mGizmo ) + mGizmo->deleteObject(); +} + +void EditTSCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + // Perform possible mouse border move... + if(mAllowBorderMove && smCamOrtho && !mLeftMouseDown && !mRightMouseDown && !mMouseLeft) + { + Point2I ext = getExtent(); + + U32 current = Platform::getRealMilliseconds(); + bool update = false; + F32 movex = 0.0f; + F32 movey = 0.0f; + + Point2I localMouse = globalToLocalCoord(mLastMousePos); + + if(localMouse.x <= mMouseMoveBorder || localMouse.x >= ext.x - mMouseMoveBorder) + { + if(mLastBorderMoveTime != 0) + { + U32 dt = current - mLastBorderMoveTime; + if(localMouse.x <= mMouseMoveBorder) + { + movex = mMouseMoveSpeed * dt; + } + else + { + movex = -mMouseMoveSpeed * dt; + } + } + update = true; + } + + if(localMouse.y <= mMouseMoveBorder || localMouse.y >= ext.y - mMouseMoveBorder) + { + if(mLastBorderMoveTime != 0) + { + U32 dt = current - mLastBorderMoveTime; + if(localMouse.y <= mMouseMoveBorder) + { + movey = mMouseMoveSpeed * dt; + } + else + { + movey = -mMouseMoveSpeed * dt; + } + } + update = true; + } + + if(update) + { + mLastBorderMoveTime = current; + calcOrthoCamOffset(movex, movey); + } + else + { + mLastBorderMoveTime = 0; + } + } + + updateGuiInfo(); + Parent::onRender(offset, updateRect); + +} + +//------------------------------------------------------------------------------ + +void EditTSCtrl::initPersistFields() +{ + addGroup( "Grid" ); + + addField( "gridSize", TypeF32, Offset( mGridPlaneSize, EditTSCtrl ) ); + addField( "gridColor", TypeColorI, Offset( mGridPlaneColor, EditTSCtrl ) ); + addField( "gridOriginColor", TypeColorI, Offset( mGridPlaneOriginColor, EditTSCtrl ) ); + addField( "gridMinorTickColor", TypeColorI, Offset( mGridPlaneMinorTickColor, EditTSCtrl ) ); + addField( "renderOrthoGrid", TypeBool, Offset( mRenderGridPlane, EditTSCtrl ), + "Whether to render the grid in orthographic axial projections." ); + addField( "renderOrthoGridPixelBias", TypeF32, Offset( mGridPlaneSizePixelBias, EditTSCtrl ), + "Grid patch pixel size below which to switch to coarser grid resolutions." ); + + endGroup( "Grid" ); + + addGroup("Mission Area"); + + addField("renderMissionArea", TypeBool, Offset(mRenderMissionArea, EditTSCtrl)); + addField("missionAreaFillColor", TypeColorI, Offset(mMissionAreaFillColor, EditTSCtrl)); + addField("missionAreaFrameColor", TypeColorI, Offset(mMissionAreaFrameColor, EditTSCtrl)); + addField("missionAreaHeightAdjust", TypeF32, Offset(mMissionAreaHeightAdjust, EditTSCtrl), + "How high above and below the terrain to render the mission area bounds." ); + + endGroup("Mission Area"); + + addGroup("BorderMovement"); + + addField("allowBorderMove", TypeBool, Offset(mAllowBorderMove, EditTSCtrl)); + addField("borderMovePixelSize", TypeS32, Offset(mMouseMoveBorder, EditTSCtrl)); + addField("borderMoveSpeed", TypeF32, Offset(mMouseMoveSpeed, EditTSCtrl)); + + endGroup("BorderMovement"); + + addGroup("Misc"); + + addField("consoleFrameColor", TypeColorI, Offset(mConsoleFrameColor, EditTSCtrl)); + addField("consoleFillColor", TypeColorI, Offset(mConsoleFillColor, EditTSCtrl)); + addField("consoleSphereLevel", TypeS32, Offset(mConsoleSphereLevel, EditTSCtrl)); + addField("consoleCircleSegments", TypeS32, Offset(mConsoleCircleSegments, EditTSCtrl)); + addField("consoleLineWidth", TypeS32, Offset(mConsoleLineWidth, EditTSCtrl)); + addField("gizmoProfile", TYPEID< GizmoProfile >(), Offset(mGizmoProfile, EditTSCtrl)); + + endGroup("Misc"); + + Parent::initPersistFields(); +} + +void EditTSCtrl::consoleInit() +{ + Con::addVariable("pref::WorldEditor::visibleDistanceScale", TypeF32, &EditTSCtrl::smVisibleDistanceScale, "Scale factor for the visible render distance.\n" + "@ingroup "); + + Con::addVariable("pref::WorldEditor::cameraFOV", TypeF32, &EditTSCtrl::smCamFOV, "Field of view for editor's perspective camera, in degrees.\n" + "@ingroup "); + + Con::setIntVariable( "$EditTsCtrl::DisplayTypeTop", DisplayTypeTop); + Con::setIntVariable( "$EditTsCtrl::DisplayTypeBottom", DisplayTypeBottom); + Con::setIntVariable( "$EditTsCtrl::DisplayTypeFront", DisplayTypeFront); + Con::setIntVariable( "$EditTsCtrl::DisplayTypeBack", DisplayTypeBack); + Con::setIntVariable( "$EditTsCtrl::DisplayTypeLeft", DisplayTypeLeft); + Con::setIntVariable( "$EditTsCtrl::DisplayTypeRight", DisplayTypeRight); + Con::setIntVariable( "$EditTsCtrl::DisplayTypePerspective", DisplayTypePerspective); + Con::setIntVariable( "$EditTsCtrl::DisplayTypeIsometric", DisplayTypeIsometric); +} + +//------------------------------------------------------------------------------ + +void EditTSCtrl::make3DMouseEvent(Gui3DMouseEvent & gui3DMouseEvent, const GuiEvent & event) +{ + (GuiEvent&)(gui3DMouseEvent) = event; + gui3DMouseEvent.mousePoint = event.mousePoint; + + if(!smCamOrtho) + { + // get the eye pos and the mouse vec from that... + Point3F screenPoint((F32)gui3DMouseEvent.mousePoint.x, (F32)gui3DMouseEvent.mousePoint.y, 1.0f); + + Point3F wp; + unproject(screenPoint, &wp); + + gui3DMouseEvent.pos = smCamPos; + gui3DMouseEvent.vec = wp - smCamPos; + gui3DMouseEvent.vec.normalizeSafe(); + } + else + { + // get the eye pos and the mouse vec from that... + Point3F screenPoint((F32)gui3DMouseEvent.mousePoint.x, (F32)gui3DMouseEvent.mousePoint.y, 0.0f); + + Point3F np, fp; + unproject(screenPoint, &np); + + gui3DMouseEvent.pos = np; + smCamMatrix.getColumn( 1, &(gui3DMouseEvent.vec) ); + } +} + +//------------------------------------------------------------------------------ + +TerrainBlock* EditTSCtrl::getActiveTerrain() +{ + // Find a terrain block + SimSet* scopeAlwaysSet = Sim::getGhostAlwaysSet(); + for(SimSet::iterator itr = scopeAlwaysSet->begin(); itr != scopeAlwaysSet->end(); itr++) + { + TerrainBlock* block = dynamic_cast(*itr); + if( block ) + return block; + } + + return NULL; +} + +//------------------------------------------------------------------------------ + +void EditTSCtrl::setDisplayType( S32 type ) +{ + mDisplayType = type; + + // Disable middle-mouse pass-thru in ortho views so we can + // use the middle mouse button for navigation. + + mMiddleMousePassThru = !isOrthoDisplayType(); + + if( mGizmo ) + { + // Disable gizmo's grid plane in the isometric views since + // they will render with the grid from EditTSCtrl. Also disable + // the move grid as it doesn't make sense in ortho views. + + if( type != DisplayTypePerspective ) + { + mGizmo->setGridPlaneEnabled( false ); + mGizmo->setMoveGridEnabled( false ); + } + else + { + mGizmo->setGridPlaneEnabled( true ); + mGizmo->setMoveGridEnabled( true ); + } + } +} + +//------------------------------------------------------------------------------ + +void EditTSCtrl::getCursor(GuiCursor *&cursor, bool &visible, const GuiEvent &event) +{ + make3DMouseEvent(mLastEvent, event); + get3DCursor(cursor, visible, mLastEvent); +} + +void EditTSCtrl::get3DCursor(GuiCursor *&cursor, bool &visible, const Gui3DMouseEvent &event) +{ + TORQUE_UNUSED(event); + cursor = NULL; + visible = false; +} + +void EditTSCtrl::onMouseUp(const GuiEvent & event) +{ + mLeftMouseDown = false; + make3DMouseEvent(mLastEvent, event); + on3DMouseUp(mLastEvent); +} + +void EditTSCtrl::onMouseDown(const GuiEvent & event) +{ + mLeftMouseDown = true; + mLastBorderMoveTime = 0; + make3DMouseEvent(mLastEvent, event); + on3DMouseDown(mLastEvent); + + setFirstResponder(); +} + +void EditTSCtrl::onMouseMove(const GuiEvent & event) +{ + make3DMouseEvent(mLastEvent, event); + on3DMouseMove(mLastEvent); + + mLastMousePos = event.mousePoint; +} + +void EditTSCtrl::onMouseDragged(const GuiEvent & event) +{ + make3DMouseEvent(mLastEvent, event); + on3DMouseDragged(mLastEvent); +} + +void EditTSCtrl::onMouseEnter(const GuiEvent & event) +{ + mMouseLeft = false; + make3DMouseEvent(mLastEvent, event); + on3DMouseEnter(mLastEvent); +} + +void EditTSCtrl::onMouseLeave(const GuiEvent & event) +{ + mMouseLeft = true; + mLastBorderMoveTime = 0; + make3DMouseEvent(mLastEvent, event); + on3DMouseLeave(mLastEvent); +} + +void EditTSCtrl::onRightMouseDown(const GuiEvent & event) +{ + // always process the right mouse event first... + + mRightMouseDown = true; + mLastBorderMoveTime = 0; + + make3DMouseEvent(mLastEvent, event); + on3DRightMouseDown(mLastEvent); + + if(!mLeftMouseDown && mRightMousePassThru && mProfile->mCanKeyFocus) + { + GuiCanvas *pCanvas = getRoot(); + if( !pCanvas ) + return; + + PlatformWindow *pWindow = static_cast(getRoot())->getPlatformWindow(); + if( !pWindow ) + return; + + PlatformCursorController *pController = pWindow->getCursorController(); + if( !pController ) + return; + + // ok, gotta disable the mouse + // script functions are lockMouse(true); Canvas.cursorOff(); + pWindow->setMouseLocked(true); + pCanvas->setCursorON( false ); + + if(mDisplayType != DisplayTypePerspective) + { + mouseLock(); + mLastMousePos = event.mousePoint; + pCanvas->setForceMouseToGUI(true); + mLastMouseClamping = pCanvas->getClampTorqueCursor(); + pCanvas->setClampTorqueCursor(false); + } + + if(mDisplayType == DisplayTypeIsometric) + { + // Store the screen center point on the terrain for a possible rotation + TerrainBlock* activeTerrain = getActiveTerrain(); + if( activeTerrain ) + { + F32 extx, exty; + if(event.modifier & SI_SHIFT) + { + extx = F32(event.mousePoint.x); + exty = F32(event.mousePoint.y); + } + else + { + extx = getExtent().x * 0.5; + exty = getExtent().y * 0.5; + } + Point3F sp(extx, exty, 0.0f); // Near plane projection + Point3F start; + unproject(sp, &start); + + Point3F end = start + mLastEvent.vec * 4000.0f; + Point3F tStartPnt, tEndPnt; + activeTerrain->getTransform().mulP(start, &tStartPnt); + activeTerrain->getTransform().mulP(end, &tEndPnt); + + RayInfo info; + bool result = activeTerrain->castRay(tStartPnt, tEndPnt, &info); + if(result) + { + info.point.interpolate(start, end, info.t); + mIsoCamRotCenter = info.point; + } + else + { + mIsoCamRotCenter = start; + } + } + else + { + F32 extx = getExtent().x * 0.5; + F32 exty = getExtent().y * 0.5; + Point3F sp(extx, exty, 0.0f); // Near plane projection + unproject(sp, &mIsoCamRotCenter); + } + } + + setFirstResponder(); + } +} + +void EditTSCtrl::onRightMouseUp(const GuiEvent & event) +{ + mRightMouseDown = false; + make3DMouseEvent(mLastEvent, event); + on3DRightMouseUp(mLastEvent); +} + +void EditTSCtrl::onRightMouseDragged(const GuiEvent & event) +{ + make3DMouseEvent(mLastEvent, event); + on3DRightMouseDragged(mLastEvent); + + // Handle zoom of orthographic views. + + if( isOrthoDisplayType() ) + { + orthoZoom( ( event.mousePoint.y - mLastMousePos.y ) * 0.5f ); + mLastMousePos = event.mousePoint; + } +} + +void EditTSCtrl::onMiddleMouseDown(const GuiEvent & event) +{ + mMiddleMouseDown = true; + mMiddleMouseTriggered = false; + mLastBorderMoveTime = 0; + + if(!mLeftMouseDown && !mRightMouseDown && mMiddleMousePassThru && mProfile->mCanKeyFocus) + { + GuiCanvas *pCanvas = getRoot(); + if( !pCanvas ) + return; + + PlatformWindow *pWindow = static_cast(getRoot())->getPlatformWindow(); + if( !pWindow ) + return; + + PlatformCursorController *pController = pWindow->getCursorController(); + if( !pController ) + return; + + // ok, gotta disable the mouse + // script functions are lockMouse(true); Canvas.cursorOff(); + pWindow->setMouseLocked(true); + pCanvas->setCursorON( false ); + + // Trigger 2 is used by the camera + MoveManager::mTriggerCount[2]++; + mMiddleMouseTriggered = true; + + setFirstResponder(); + } +} + +void EditTSCtrl::onMiddleMouseUp(const GuiEvent & event) +{ + // Trigger 2 is used by the camera + if( mMiddleMouseTriggered ) + { + MoveManager::mTriggerCount[2]++; + mMiddleMouseTriggered = false; + } + + mMiddleMouseDown = false; +} + +void EditTSCtrl::onMiddleMouseDragged(const GuiEvent & event) +{ + // Handle translation of orthographic views. + + if( isOrthoDisplayType() ) + { + calcOrthoCamOffset((event.mousePoint.x - mLastMousePos.x), (event.mousePoint.y - mLastMousePos.y), event.modifier); + mLastMousePos = event.mousePoint; + } +} + +bool EditTSCtrl::onMouseWheelUp( const GuiEvent &event ) +{ + // Looks like this should be zooming based on a factor of the GuiEvent.fval + if( isOrthoDisplayType() && !event.modifier ) + { + orthoZoom( -2.f ); + return true; + } + + make3DMouseEvent(mLastEvent, event); + on3DMouseWheelUp(mLastEvent); + + return false; +} + +bool EditTSCtrl::onMouseWheelDown( const GuiEvent &event ) +{ + // Looks like this should be zooming based on a factor of the GuiEvent.fval + if(mDisplayType != DisplayTypePerspective && !event.modifier) + { + orthoZoom( 2.f ); + return true; + } + + make3DMouseEvent(mLastEvent, event); + on3DMouseWheelDown(mLastEvent); + + return false; +} + + +bool EditTSCtrl::onInputEvent(const InputEventInfo & event) +{ + + if(mRightMousePassThru && event.deviceType == MouseDeviceType && + event.objInst == KEY_BUTTON1 && event.action == SI_BREAK) + { + // if the right mouse pass thru is enabled, + // we want to reactivate mouse on a right mouse button up + GuiCanvas *pCanvas = getRoot(); + if( !pCanvas ) + return false; + + PlatformWindow *pWindow = static_cast(getRoot())->getPlatformWindow(); + if( !pWindow ) + return false; + + PlatformCursorController *pController = pWindow->getCursorController(); + if( !pController ) + return false; + + pWindow->setMouseLocked(false); + pCanvas->setCursorON( true ); + + if(mDisplayType != DisplayTypePerspective) + { + mouseUnlock(); + pCanvas->setForceMouseToGUI(false); + pCanvas->setClampTorqueCursor(mLastMouseClamping); + } + } + + if(mMiddleMousePassThru && event.deviceType == MouseDeviceType && + event.objInst == KEY_BUTTON2 && event.action == SI_BREAK) + { + // if the middle mouse pass thru is enabled, + // we want to reactivate mouse on a middle mouse button up + GuiCanvas *pCanvas = getRoot(); + if( !pCanvas ) + return false; + + PlatformWindow *pWindow = static_cast(getRoot())->getPlatformWindow(); + if( !pWindow ) + return false; + + PlatformCursorController *pController = pWindow->getCursorController(); + if( !pController ) + return false; + + pWindow->setMouseLocked(false); + pCanvas->setCursorON( true ); + } + + // we return false so that the canvas can properly process the right mouse button up... + return false; +} + +//------------------------------------------------------------------------------ + +void EditTSCtrl::orthoZoom( F32 steps ) +{ + //TODO: this really should be proportional + + mOrthoFOV += steps; + + if( mOrthoFOV < 1.0f ) + mOrthoFOV = 1.0f; +} + +void EditTSCtrl::calcOrthoCamOffset(F32 mousex, F32 mousey, U8 modifier) +{ + F32 camScale = 0.01f; + + switch(mDisplayType) + { + case DisplayTypeTop: + mOrthoCamTrans.x -= mousex * mOrthoFOV * camScale; + mOrthoCamTrans.y += mousey * mOrthoFOV * camScale; + break; + + case DisplayTypeBottom: + mOrthoCamTrans.x -= mousex * mOrthoFOV * camScale; + mOrthoCamTrans.y -= mousey * mOrthoFOV * camScale; + break; + + case DisplayTypeFront: + mOrthoCamTrans.x += mousex * mOrthoFOV * camScale; + mOrthoCamTrans.z += mousey * mOrthoFOV * camScale; + break; + + case DisplayTypeBack: + mOrthoCamTrans.x -= mousex * mOrthoFOV * camScale; + mOrthoCamTrans.z += mousey * mOrthoFOV * camScale; + break; + + case DisplayTypeLeft: + mOrthoCamTrans.y += mousex * mOrthoFOV * camScale; + mOrthoCamTrans.z += mousey * mOrthoFOV * camScale; + break; + + case DisplayTypeRight: + mOrthoCamTrans.y -= mousex * mOrthoFOV * camScale; + mOrthoCamTrans.z += mousey * mOrthoFOV * camScale; + break; + + case DisplayTypeIsometric: + if(modifier & SI_PRIMARY_CTRL) + { + // NOTE: Maybe move the center of rotation code to right mouse down to avoid compound errors? + F32 rot = mDegToRad(mousex); + + Point3F campos = (mRawCamPos + mOrthoCamTrans) - mIsoCamRotCenter; + MatrixF mat(EulerF(0, 0, rot)); + mat.mulP(campos); + mOrthoCamTrans = (campos + mIsoCamRotCenter) - mRawCamPos; + mIsoCamRot.z += rot; + + } + else + { + mOrthoCamTrans.x -= mousex * mOrthoFOV * camScale * mCos(mIsoCamRot.z) - mousey * mOrthoFOV * camScale * mSin(mIsoCamRot.z); + mOrthoCamTrans.y += mousex * mOrthoFOV * camScale * mSin(mIsoCamRot.z) + mousey * mOrthoFOV * camScale * mCos(mIsoCamRot.z); + } + break; + } +} + +void EditTSCtrl::updateGizmo() +{ + mGizmoProfile->restoreDefaultState(); +} + +//------------------------------------------------------------------------------ + +void EditTSCtrl::renderWorld(const RectI & updateRect) +{ + // Make sure that whatever the editor does, it doesn't + // dirty the GFX transform state. + GFXTransformSaver saver; + + updateGizmo(); + + gClientSceneGraph->setDisplayTargetResolution(getExtent()); + + // Use a render instance to do editor 3D scene + // rendering after HDR is processed and while the depth + // buffer is still intact. + RenderPassManager *rpm = gClientSceneGraph->getDefaultRenderPass(); + ObjectRenderInst *inst = rpm->allocInst(); + inst->type = RenderPassManager::RIT_Editor; + inst->renderDelegate.bind(this, &EditTSCtrl::_renderScene); + rpm->addInst(inst); + + if( mDisplayType == DisplayTypePerspective ) + gClientSceneGraph->renderScene( SPT_Diffuse ); + else + { + // If we are in an orthographic mode, do a special render + // with AL, fog, and PostFX disabled. + + FogData savedFogData = gClientSceneGraph->getFogData(); + gClientSceneGraph->setFogData( FogData() ); + + SceneRenderState renderState + ( + gClientSceneGraph, + SPT_Diffuse + ); + + gClientSceneGraph->renderScene( &renderState ); + gClientSceneGraph->setFogData( savedFogData ); + } +} + +void EditTSCtrl::_renderScene( ObjectRenderInst*, SceneRenderState *state, BaseMatInstance* ) +{ + GFXTransformSaver saver; + + // render through console callbacks + SimSet * missionGroup = static_cast(Sim::findObject("MissionGroup")); + if(missionGroup) + { + mConsoleRendering = true; + + // [ rene, 27-Jan-10 ] This calls onEditorRender on the server objects instead + // of on the client objects which seems a bit questionable to me. + + for(SimSetIterator itr(missionGroup); *itr; ++itr) + { + SceneObject* object = dynamic_cast< SceneObject* >( *itr ); + if( object && object->isRenderEnabled() && !object->isHidden() ) + { + char buf[2][16]; + dSprintf(buf[0], 16, object->isSelected() ? "true" : "false"); + dSprintf(buf[1], 16, object->isExpanded() ? "true" : "false"); + + Con::executef( object, "onEditorRender", getIdString(), buf[0], buf[1] ); + } + } + + mConsoleRendering = false; + } + + // render the mission area... + renderMissionArea(); + + // Draw the grid + if ( mRenderGridPlane ) + renderGrid(); + + // render the editor stuff + renderScene(mSaveViewport); + + // Draw the camera axis + GFX->setClipRect(mSaveViewport); + GFX->setStateBlock(mDefaultGuiSB); + renderCameraAxis(); +} + +void EditTSCtrl::renderMissionArea() +{ + MissionArea* obj = MissionArea::getServerObject(); + if ( !obj ) + return; + + if ( !mRenderMissionArea && !obj->isSelected() ) + return; + + GFXDEBUGEVENT_SCOPE( Editor_renderMissionArea, ColorI::WHITE ); + + F32 minHeight = 0.0f; + F32 maxHeight = 0.0f; + + TerrainBlock* terrain = getActiveTerrain(); + if ( terrain ) + { + terrain->getMinMaxHeight( &minHeight, &maxHeight ); + Point3F pos = terrain->getPosition(); + + maxHeight += pos.z + mMissionAreaHeightAdjust; + minHeight += pos.z - mMissionAreaHeightAdjust; + } + + const RectI& area = obj->getArea(); + Box3F areaBox( area.point.x, + area.point.y, + minHeight, + area.point.x + area.extent.x, + area.point.y + area.extent.y, + maxHeight ); + + GFXDrawUtil* drawer = GFX->getDrawUtil(); + + GFXStateBlockDesc desc; + desc.setCullMode( GFXCullNone ); + desc.setBlend( true ); + desc.setZReadWrite( false, false ); + + desc.setFillModeSolid(); + drawer->drawCube( desc, areaBox, mMissionAreaFillColor ); + + desc.setFillModeWireframe(); + drawer->drawCube( desc, areaBox, mMissionAreaFrameColor ); +} + +void EditTSCtrl::renderCameraAxis() +{ + GFXDEBUGEVENT_SCOPE( Editor_renderCameraAxis, ColorI::WHITE ); + + static MatrixF sRotMat(EulerF( (M_PI_F / -2.0f), 0.0f, 0.0f)); + + MatrixF camMat = mLastCameraQuery.cameraMatrix; + camMat.mul(sRotMat); + camMat.inverse(); + + MatrixF axis; + axis.setColumn(0, Point3F(1, 0, 0)); + axis.setColumn(1, Point3F(0, 0, 1)); + axis.setColumn(2, Point3F(0, -1, 0)); + axis.mul(camMat); + + Point3F forwardVec, upVec, rightVec; + axis.getColumn( 2, &forwardVec ); + axis.getColumn( 1, &upVec ); + axis.getColumn( 0, &rightVec ); + + Point2I pos = getPosition(); + F32 offsetx = pos.x + 20.0; + F32 offsety = pos.y + getExtent().y - 42.0; // Take the status bar into account + F32 scale = 15.0; + + // Generate correct drawing order + ColorI c1(255,0,0); + ColorI c2(0,255,0); + ColorI c3(0,0,255); + ColorI tc; + Point3F *p1, *p2, *p3, *tp; + p1 = &rightVec; + p2 = &upVec; + p3 = &forwardVec; + if(p3->y > p2->y) + { + tp = p2; tc = c2; + p2 = p3; c2 = c3; + p3 = tp; c3 = tc; + } + if(p2->y > p1->y) + { + tp = p1; tc = c1; + p1 = p2; c1 = c2; + p2 = tp; c2 = tc; + } + + PrimBuild::begin( GFXLineList, 6 ); + //*** Axis 1 + PrimBuild::color(c1); + PrimBuild::vertex3f(offsetx, offsety, 0); + PrimBuild::vertex3f(offsetx+p1->x*scale, offsety-p1->z*scale, 0); + + //*** Axis 2 + PrimBuild::color(c2); + PrimBuild::vertex3f(offsetx, offsety, 0); + PrimBuild::vertex3f(offsetx+p2->x*scale, offsety-p2->z*scale, 0); + + //*** Axis 3 + PrimBuild::color(c3); + PrimBuild::vertex3f(offsetx, offsety, 0); + PrimBuild::vertex3f(offsetx+p3->x*scale, offsety-p3->z*scale, 0); + PrimBuild::end(); +} + +void EditTSCtrl::renderGrid() +{ + if( !isOrthoDisplayType() ) + return; + + GFXDEBUGEVENT_SCOPE( Editor_renderGrid, ColorI::WHITE ); + + // Calculate the displayed grid size based on view + F32 drawnGridSize = mGridPlaneSize; + F32 gridPixelSize = projectRadius(1.0f, mGridPlaneSize); + if(gridPixelSize < mGridPlaneSizePixelBias) + { + U32 counter = 1; + while(gridPixelSize < mGridPlaneSizePixelBias) + { + drawnGridSize = mGridPlaneSize * counter * 10.0f; + gridPixelSize = projectRadius(1.0f, drawnGridSize); + + ++counter; + + // No infinite loops here + if(counter > 1000) + break; + } + } + + F32 minorTickSize = 0; + F32 gridSize = drawnGridSize; + U32 minorTickMax = mGridPlaneMinorTicks + 1; + if(minorTickMax > 0) + { + minorTickSize = drawnGridSize; + gridSize = drawnGridSize * minorTickMax; + } + + // Build the view-based origin + VectorF dir; + smCamMatrix.getColumn( 1, &dir ); + + Point3F gridPlanePos = smCamPos + dir; + Point2F size(mOrthoWidth + 2 * gridSize, mOrthoHeight + 2 * gridSize); + + GFXStateBlockDesc desc; + desc.setBlend( true ); + desc.setZReadWrite( true, false ); + + GFXDrawUtil::Plane plane = GFXDrawUtil::PlaneXY; + switch( getDisplayType() ) + { + case DisplayTypeTop: + case DisplayTypeBottom: + plane = GFXDrawUtil::PlaneXY; + break; + + case DisplayTypeLeft: + case DisplayTypeRight: + plane = GFXDrawUtil::PlaneYZ; + break; + + case DisplayTypeFront: + case DisplayTypeBack: + plane = GFXDrawUtil::PlaneXZ; + break; + + default: + break; + } + + GFX->getDrawUtil()->drawPlaneGrid( desc, gridPlanePos, size, Point2F( minorTickSize, minorTickSize ), mGridPlaneMinorTickColor, plane ); + GFX->getDrawUtil()->drawPlaneGrid( desc, gridPlanePos, size, Point2F( gridSize, gridSize ), mGridPlaneColor, plane ); +} + +static void sceneBoundsCalcCallback(SceneObject* obj, void *key) +{ + // Early out for those objects that slipped through the mask check + // because they belong to more than one type. + if((obj->getTypeMask() & EditTSCtrl::smSceneBoundsMask) != 0) + return; + + if(obj->isGlobalBounds()) + return; + + Box3F* bounds = (Box3F*)key; + + Point3F min = obj->getWorldBox().minExtents; + Point3F max = obj->getWorldBox().maxExtents; + + #if 0 // Console messages in render loop lead to massive spam. + if (min.x <= -5000.0f || min.y <= -5000.0f || min.z <= -5000.0f || + min.x >= 5000.0f || min.y >= 5000.0f || min.z >= 5000.0f) + Con::errorf("SceneObject %d (%s : %s) has a bounds that could cause problems with a non-perspective view", obj->getId(), obj->getClassName(), obj->getName()); + #endif + + bounds->minExtents.setMin(min); + bounds->minExtents.setMin(max); + bounds->maxExtents.setMax(min); + bounds->maxExtents.setMax(max); +} + +bool EditTSCtrl::getCameraTransform(MatrixF* cameraMatrix) +{ + GameConnection* connection = dynamic_cast(NetConnection::getConnectionToServer()); + return (connection && connection->getControlCameraTransform(0.032f, cameraMatrix)); +} + +void EditTSCtrl::computeSceneBounds(Box3F& bounds) +{ + bounds.minExtents.set(1e10, 1e10, 1e10); + bounds.maxExtents.set(-1e10, -1e10, -1e10); + + // Calculate the scene bounds + gClientContainer.findObjects(~(smSceneBoundsMask), sceneBoundsCalcCallback, &bounds); +} + +bool EditTSCtrl::processCameraQuery(CameraQuery * query) +{ + if(mDisplayType == DisplayTypePerspective) + { + query->ortho = false; + } + else + { + query->ortho = true; + } + + if (getCameraTransform(&query->cameraMatrix)) + { + query->farPlane = gClientSceneGraph->getVisibleDistance() * smVisibleDistanceScale; + query->nearPlane = gClientSceneGraph->getNearClip(); + query->fov = mDegToRad(smCamFOV); + + if(query->ortho) + { + MatrixF camRot(true); + const F32 camBuffer = 1.0f; + Point3F camPos = query->cameraMatrix.getPosition(); + + F32 isocamplanedist = 0.0f; + if(mDisplayType == DisplayTypeIsometric) + { + const RectI& vp = GFX->getViewport(); + isocamplanedist = 0.25 * vp.extent.y * mSin(mIsoCamAngle); + } + + // Calculate the scene bounds + Box3F sceneBounds; + computeSceneBounds(sceneBounds); + + if(!sceneBounds.isValidBox()) + { + sceneBounds.maxExtents = camPos + smMinSceneBounds; + sceneBounds.minExtents = camPos - smMinSceneBounds; + } + else + { + query->farPlane = getMax(smMinSceneBounds.x * 2.0f, (sceneBounds.maxExtents - sceneBounds.minExtents).len() + camBuffer * 2.0f + isocamplanedist); + } + + mRawCamPos = camPos; + camPos += mOrthoCamTrans; + + switch(mDisplayType) + { + case DisplayTypeTop: + camRot.setColumn(0, Point3F(1.0, 0.0, 0.0)); + camRot.setColumn(1, Point3F(0.0, 0.0, -1.0)); + camRot.setColumn(2, Point3F(0.0, 1.0, 0.0)); + camPos.z = getMax(camPos.z + smMinSceneBounds.z, sceneBounds.maxExtents.z + camBuffer); + break; + + case DisplayTypeBottom: + camRot.setColumn(0, Point3F(1.0, 0.0, 0.0)); + camRot.setColumn(1, Point3F(0.0, 0.0, 1.0)); + camRot.setColumn(2, Point3F(0.0, -1.0, 0.0)); + camPos.z = getMin(camPos.z - smMinSceneBounds.z, sceneBounds.minExtents.z - camBuffer); + break; + + case DisplayTypeFront: + camRot.setColumn(0, Point3F(-1.0, 0.0, 0.0)); + camRot.setColumn(1, Point3F( 0.0, -1.0, 0.0)); + camRot.setColumn(2, Point3F( 0.0, 0.0, 1.0)); + camPos.y = getMax(camPos.y + smMinSceneBounds.y, sceneBounds.maxExtents.y + camBuffer); + break; + + case DisplayTypeBack: + camRot.setColumn(0, Point3F(1.0, 0.0, 0.0)); + camRot.setColumn(1, Point3F(0.0, 1.0, 0.0)); + camRot.setColumn(2, Point3F(0.0, 0.0, 1.0)); + camPos.y = getMin(camPos.y - smMinSceneBounds.y, sceneBounds.minExtents.y - camBuffer); + break; + + case DisplayTypeLeft: + camRot.setColumn(0, Point3F( 0.0, -1.0, 0.0)); + camRot.setColumn(1, Point3F( 1.0, 0.0, 0.0)); + camRot.setColumn(2, Point3F( 0.0, 0.0, 1.0)); + camPos.x = getMin(camPos.x - smMinSceneBounds.x, sceneBounds.minExtents.x - camBuffer); + break; + + case DisplayTypeRight: + camRot.setColumn(0, Point3F( 0.0, 1.0, 0.0)); + camRot.setColumn(1, Point3F(-1.0, 0.0, 0.0)); + camRot.setColumn(2, Point3F( 0.0, 0.0, 1.0)); + camPos.x = getMax(camPos.x + smMinSceneBounds.x, sceneBounds.maxExtents.x + camBuffer); + break; + + case DisplayTypeIsometric: + camPos.z = sceneBounds.maxExtents.z + camBuffer + isocamplanedist; + MatrixF angle(EulerF(mIsoCamAngle, 0, 0)); + MatrixF rot(mIsoCamRot); + camRot.mul(rot, angle); + break; + } + + query->cameraMatrix = camRot; + query->cameraMatrix.setPosition(camPos); + query->fov = mOrthoFOV; + } + + smCamMatrix = query->cameraMatrix; + smCamMatrix.getColumn(3,&smCamPos); + smCamOrtho = query->ortho; + smCamNearPlane = query->nearPlane; + + return true; + } + return false; +} + +//------------------------------------------------------------------------------ +DefineEngineMethod( EditTSCtrl, getDisplayType, S32, (),, "" ) +{ + return object->getDisplayType(); +} + +DefineEngineMethod( EditTSCtrl, setDisplayType, void, ( S32 displayType ),, "" ) +{ + object->setDisplayType( displayType ); +} + +DefineEngineMethod( EditTSCtrl, getOrthoFOV, F32, (),, + "Return the FOV for orthographic views." ) +{ + return object->getOrthoFOV(); +} + +DefineEngineMethod( EditTSCtrl, setOrthoFOV, void, ( F32 fov ),, + "Set the FOV for to use for orthographic views." ) +{ + object->setOrthoFOV( fov ); +} + +DefineEngineMethod( EditTSCtrl, renderBox, void, ( Point3F pos, Point3F size ),, "" ) +{ + if( !object->mConsoleRendering || !object->mConsoleFillColor.alpha ) + return; + + GFXStateBlockDesc desc; + desc.setBlend( true ); + + Box3F box; + box.set( size ); + box.setCenter( pos ); + + MatrixF camera = GFX->getWorldMatrix(); + camera.inverse(); + + if( box.isContained( camera.getPosition() ) ) + desc.setCullMode( GFXCullNone ); + + GFX->getDrawUtil()->drawCube( desc, size, pos, object->mConsoleFillColor ); +} + +DefineEngineMethod(EditTSCtrl, renderSphere, void, ( Point3F pos, F32 radius, S32 sphereLevel ), ( 0 ), "" ) +{ + if ( !object->mConsoleRendering || !object->mConsoleFillColor.alpha ) + return; + + // TODO: We need to support subdivision levels in GFXDrawUtil! + if ( sphereLevel <= 0 ) + sphereLevel = object->mConsoleSphereLevel; + + GFXStateBlockDesc desc; + desc.setBlend( true ); + + MatrixF camera = GFX->getWorldMatrix(); + camera.inverse(); + + SphereF sphere( pos, radius ); + if( sphere.isContained( camera.getPosition() ) ) + desc.setCullMode( GFXCullNone ); + + GFX->getDrawUtil()->drawSphere( desc, radius, pos, object->mConsoleFillColor ); +} + +DefineEngineMethod( EditTSCtrl, renderCircle, void, ( Point3F pos, Point3F normal, F32 radius, S32 segments ), ( 0 ), "" ) +{ + if(!object->mConsoleRendering) + return; + + if(!object->mConsoleFrameColor.alpha && !object->mConsoleFillColor.alpha) + return; + + if ( segments <= 0 ) + segments = object->mConsoleCircleSegments; + + normal.normalizeSafe(); + + AngAxisF aa; + + F32 dotUp = mDot( normal, Point3F(0,0,1) ); + if ( dotUp == 1.0f ) + aa.set( Point3F(0,0,1), 0.0f ); // normal is 0,0,1 + else if ( dotUp == -1.0f ) + aa.set( Point3F(1,0,0), M_PI_F ); // normal is 0,0,-1 + else + { + mCross( normal, Point3F(0,0,1), &aa.axis ); + aa.axis.normalizeSafe(); + aa.angle = mAcos( mClampF( dotUp, -1.f, 1.f ) ); + } + + MatrixF mat; + aa.setMatrix(&mat); + + F32 step = M_2PI / segments; + F32 angle = 0.f; + + Vector points(segments); + for(U32 i = 0; i < segments; i++) + { + Point3F pnt(mCos(angle), mSin(angle), 0.f); + + mat.mulP(pnt); + pnt *= radius; + pnt += pos; + + points.push_front(pnt); + angle += step; + } + + GFX->setStateBlock(object->mBlendSB); + + // framed + if(object->mConsoleFrameColor.alpha) + { + // TODO: Set GFX line width (when it exists) to the value of 'object->mConsoleLineWidth' + + PrimBuild::color( object->mConsoleFrameColor ); + + PrimBuild::begin( GFXLineStrip, points.size() + 1 ); + + for( int i = 0; i < points.size(); i++ ) + PrimBuild::vertex3fv( points[i] ); + + // GFX does not have a LineLoop primitive, so connect the last line + if( points.size() > 0 ) + PrimBuild::vertex3fv( points[0] ); + + PrimBuild::end(); + + // TODO: Reset GFX line width here + } + + // filled + if(object->mConsoleFillColor.alpha) + { + PrimBuild::color( object->mConsoleFillColor ); + + PrimBuild::begin( GFXTriangleFan, points.size() + 2 ); + + // Center point + PrimBuild::vertex3fv( pos ); + + // Edge verts + for( int i = 0; i < points.size(); i++ ) + PrimBuild::vertex3fv( points[i] ); + + PrimBuild::vertex3fv( points[0] ); + + PrimBuild::end(); + } +} + +DefineEngineMethod( EditTSCtrl, renderTriangle, void, ( Point3F a, Point3F b, Point3F c ),, "" ) +{ + if(!object->mConsoleRendering) + return; + + if(!object->mConsoleFrameColor.alpha && !object->mConsoleFillColor.alpha) + return; + + const Point3F* pnts[3] = { &a, &b, &c }; + + GFX->setStateBlock(object->mBlendSB); + + // frame + if( object->mConsoleFrameColor.alpha ) + { + PrimBuild::color( object->mConsoleFrameColor ); + + // TODO: Set GFX line width (when it exists) to the value of 'object->mConsoleLineWidth' + + PrimBuild::begin( GFXLineStrip, 4 ); + PrimBuild::vertex3fv( *pnts[0] ); + PrimBuild::vertex3fv( *pnts[1] ); + PrimBuild::vertex3fv( *pnts[2] ); + PrimBuild::vertex3fv( *pnts[0] ); + PrimBuild::end(); + + // TODO: Reset GFX line width here + } + + // fill + if( object->mConsoleFillColor.alpha ) + { + PrimBuild::color( object->mConsoleFillColor ); + + PrimBuild::begin( GFXTriangleList, 3 ); + PrimBuild::vertex3fv( *pnts[0] ); + PrimBuild::vertex3fv( *pnts[1] ); + PrimBuild::vertex3fv( *pnts[2] ); + PrimBuild::end(); + } +} + +DefineEngineMethod( EditTSCtrl, renderLine, void, ( Point3F start, Point3F end, F32 lineWidth ), ( 0 ), "" ) +{ + if ( !object->mConsoleRendering || !object->mConsoleFrameColor.alpha ) + return; + + // TODO: We don't support 3d lines with width... fix this! + if ( lineWidth <= 0 ) + lineWidth = object->mConsoleLineWidth; + + GFX->getDrawUtil()->drawLine( start, end, object->mConsoleFrameColor ); +} + +DefineEngineMethod( EditTSCtrl, getGizmo, S32, (),, "" ) +{ + return object->getGizmo()->getId(); +} + +DefineEngineMethod( EditTSCtrl, isMiddleMouseDown, bool, (),, "" ) +{ + return object->isMiddleMouseDown(); +} diff --git a/Engine/source/gui/worldEditor/editTSCtrl.h b/Engine/source/gui/worldEditor/editTSCtrl.h new file mode 100644 index 000000000..7fdc6782f --- /dev/null +++ b/Engine/source/gui/worldEditor/editTSCtrl.h @@ -0,0 +1,218 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _EDITTSCTRL_H_ +#define _EDITTSCTRL_H_ + +#ifndef _GUITSCONTROL_H_ +#include "gui/3d/guiTSControl.h" +#endif +#ifndef _GIZMO_H_ +#include "gizmo.h" +#endif + +class TerrainBlock; +class MissionArea; +class Gizmo; +class EditManager; +struct ObjectRenderInst; +class SceneRenderState; +class BaseMatInstance; + + +class EditTSCtrl : public GuiTSCtrl +{ + typedef GuiTSCtrl Parent; + + protected: + + void make3DMouseEvent(Gui3DMouseEvent & gui3Devent, const GuiEvent &event); + + // GuiControl + virtual void getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent); + virtual void onMouseUp(const GuiEvent & event); + virtual void onMouseDown(const GuiEvent & event); + virtual void onMouseMove(const GuiEvent & event); + virtual void onMouseDragged(const GuiEvent & event); + virtual void onMouseEnter(const GuiEvent & event); + virtual void onMouseLeave(const GuiEvent & event); + virtual void onRightMouseDown(const GuiEvent & event); + virtual void onRightMouseUp(const GuiEvent & event); + virtual void onRightMouseDragged(const GuiEvent & event); + virtual void onMiddleMouseDown(const GuiEvent & event); + virtual void onMiddleMouseUp(const GuiEvent & event); + virtual void onMiddleMouseDragged(const GuiEvent & event); + virtual bool onInputEvent(const InputEventInfo & event); + virtual bool onMouseWheelUp(const GuiEvent &event); + virtual bool onMouseWheelDown(const GuiEvent &event); + + + virtual void updateGuiInfo() {}; + virtual void renderScene(const RectI &){}; + void renderMissionArea(); + virtual void renderCameraAxis(); + virtual void renderGrid(); + + // GuiTSCtrl + void renderWorld(const RectI & updateRect); + + void _renderScene(ObjectRenderInst*, SceneRenderState *state, BaseMatInstance*); + + /// Zoom in/out in ortho views by "steps". + void orthoZoom( F32 steps ); + + protected: + enum DisplayType + { + DisplayTypeTop, + DisplayTypeBottom, + DisplayTypeFront, + DisplayTypeBack, + DisplayTypeLeft, + DisplayTypeRight, + DisplayTypePerspective, + DisplayTypeIsometric, + }; + + S32 mDisplayType; + F32 mOrthoFOV; + Point3F mOrthoCamTrans; + EulerF mIsoCamRot; + Point3F mIsoCamRotCenter; + F32 mIsoCamAngle; + Point3F mRawCamPos; + Point2I mLastMousePos; + bool mLastMouseClamping; + + bool mAllowBorderMove; + S32 mMouseMoveBorder; + F32 mMouseMoveSpeed; + U32 mLastBorderMoveTime; + + Gui3DMouseEvent mLastEvent; + bool mLeftMouseDown; + bool mRightMouseDown; + bool mMiddleMouseDown; + bool mMiddleMouseTriggered; + bool mMouseLeft; + + SimObjectPtr mGizmo; + GizmoProfile *mGizmoProfile; + + public: + + EditTSCtrl(); + ~EditTSCtrl(); + + // SimObject + bool onAdd(); + void onRemove(); + + // + bool mRenderMissionArea; + ColorI mMissionAreaFillColor; + ColorI mMissionAreaFrameColor; + F32 mMissionAreaHeightAdjust; + + // + ColorI mConsoleFrameColor; + ColorI mConsoleFillColor; + S32 mConsoleSphereLevel; + S32 mConsoleCircleSegments; + S32 mConsoleLineWidth; + + static void initPersistFields(); + static void consoleInit(); + + // + bool mConsoleRendering; + bool mRightMousePassThru; + bool mMiddleMousePassThru; + + // all editors will share a camera + static Point3F smCamPos; + static MatrixF smCamMatrix; + static bool smCamOrtho; + static F32 smCamNearPlane; + static F32 smCamFOV; + static F32 smVisibleDistanceScale; + + static U32 smSceneBoundsMask; + static Point3F smMinSceneBounds; + + bool mRenderGridPlane; + ColorI mGridPlaneColor; + F32 mGridPlaneSize; + F32 mGridPlaneSizePixelBias; + S32 mGridPlaneMinorTicks; + ColorI mGridPlaneMinorTickColor; + ColorI mGridPlaneOriginColor; + + GFXStateBlockRef mBlendSB; + + // GuiTSCtrl + virtual bool getCameraTransform(MatrixF* cameraMatrix); + virtual void computeSceneBounds(Box3F& bounds); + bool processCameraQuery(CameraQuery * query); + + // guiControl + virtual void onRender(Point2I offset, const RectI &updateRect); + virtual void on3DMouseUp(const Gui3DMouseEvent &){}; + virtual void on3DMouseDown(const Gui3DMouseEvent &){}; + virtual void on3DMouseMove(const Gui3DMouseEvent &){}; + virtual void on3DMouseDragged(const Gui3DMouseEvent &){}; + virtual void on3DMouseEnter(const Gui3DMouseEvent &){}; + virtual void on3DMouseLeave(const Gui3DMouseEvent &){}; + virtual void on3DRightMouseDown(const Gui3DMouseEvent &){}; + virtual void on3DRightMouseUp(const Gui3DMouseEvent &){}; + virtual void on3DRightMouseDragged(const Gui3DMouseEvent &){}; + virtual void on3DMouseWheelUp(const Gui3DMouseEvent &){}; + virtual void on3DMouseWheelDown(const Gui3DMouseEvent &){}; + virtual void get3DCursor(GuiCursor *&cursor, bool &visible, const Gui3DMouseEvent &); + + virtual bool isMiddleMouseDown() {return mMiddleMouseDown;} + + S32 getDisplayType() const {return mDisplayType;} + virtual void setDisplayType(S32 type); + + /// Return true if the current view is an ortho projection along one of the world axes. + bool isOrthoDisplayType() const { return ( mDisplayType != DisplayTypePerspective && mDisplayType != DisplayTypeIsometric ); } + + F32 getOrthoFOV() const { return mOrthoFOV; } + void setOrthoFOV( F32 fov ) { mOrthoFOV = fov; } + + virtual TerrainBlock* getActiveTerrain(); + + virtual void calcOrthoCamOffset(F32 mousex, F32 mousey, U8 modifier=0); + + Gizmo* getGizmo() { return mGizmo; } + + /// Set flags or other Gizmo state appropriate for the current situation. + /// For example derived classes may override this to disable certain + /// axes of modes of manipulation. + virtual void updateGizmo(); + + DECLARE_CONOBJECT(EditTSCtrl); + DECLARE_CATEGORY( "Gui Editor" ); +}; + +#endif // _EDITTSCTRL_H_ diff --git a/Engine/source/gui/worldEditor/editor.cpp b/Engine/source/gui/worldEditor/editor.cpp new file mode 100644 index 000000000..458b24d71 --- /dev/null +++ b/Engine/source/gui/worldEditor/editor.cpp @@ -0,0 +1,165 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/worldEditor/editor.h" +#include "console/console.h" +#include "console/consoleInternal.h" +#include "gui/controls/guiTextListCtrl.h" +#include "T3D/shapeBase.h" +#include "T3D/gameBase/gameConnection.h" + +#ifndef TORQUE_PLAYER +// See matching #ifdef in app/game.cpp +bool gEditingMission = false; +#endif + +//------------------------------------------------------------------------------ +// Class EditManager +//------------------------------------------------------------------------------ + +IMPLEMENT_CONOBJECT(EditManager); + +ConsoleDocClass( EditManager, + "@brief For Editor use only, deprecated\n\n" + "@internal" +); + +EditManager::EditManager() +{ + for(U32 i = 0; i < 10; i++) + mBookmarks[i] = MatrixF(true); +} + +EditManager::~EditManager() +{ +} + +//------------------------------------------------------------------------------ + +bool EditManager::onWake() +{ + if(!Parent::onWake()) + return(false); + + return(true); +} + +void EditManager::onSleep() +{ + Parent::onSleep(); +} + +//------------------------------------------------------------------------------ + +bool EditManager::onAdd() +{ + if(!Parent::onAdd()) + return(false); + + // hook the namespace + const char * name = getName(); + if(name && name[0] && getClassRep()) + { + Namespace * parent = getClassRep()->getNameSpace(); + Con::linkNamespaces(parent->mName, name); + mNameSpace = Con::lookupNamespace(name); + } + + return(true); +} + +//------------------------------------------------------------------------------ + +// NOTE: since EditManager is not actually used as a gui anymore, onWake/Sleep +// were never called, which broke anyone hooking into onEditorEnable/onEditorDisable +// and gEditingMission. So, moved these to happen in response to console methods +// which should be called at the appropriate time. +// +// This is a quick fix and this system is still "begging" for a remake. + +void EditManager::editorEnabled() +{ + for(SimGroupIterator itr(Sim::getRootGroup()); *itr; ++itr) + (*itr)->onEditorEnable(); + + gEditingMission = true; +} + +void EditManager::editorDisabled() +{ + for(SimGroupIterator itr(Sim::getRootGroup()); *itr; ++itr) + { + SimObject *so = *itr; + AssertFatal(so->isProperlyAdded() && !so->isRemoved(), "bad"); + so->onEditorDisable(); + } + + gEditingMission = false; +} + +//------------------------------------------------------------------------------ + +static GameBase * getControlObj() +{ + GameConnection * connection = GameConnection::getLocalClientConnection(); + ShapeBase* control = 0; + if(connection) + control = dynamic_cast(connection->getControlObject()); + return(control); +} + +ConsoleMethod( EditManager, setBookmark, void, 3, 3, "(int slot)") +{ + S32 val = dAtoi(argv[2]); + if(val < 0 || val > 9) + return; + + GameBase * control = getControlObj(); + if(control) + object->mBookmarks[val] = control->getTransform(); +} + +ConsoleMethod( EditManager, gotoBookmark, void, 3, 3, "(int slot)") +{ + S32 val = dAtoi(argv[2]); + if(val < 0 || val > 9) + return; + + GameBase * control = getControlObj(); + if(control) + control->setTransform(object->mBookmarks[val]); +} + +ConsoleMethod( EditManager, editorEnabled, void, 2, 2, "Perform the onEditorEnabled callback on all SimObjects and set gEditingMission true" ) +{ + object->editorEnabled(); +} + +ConsoleMethod( EditManager, editorDisabled, void, 2, 2, "Perform the onEditorDisabled callback on all SimObjects and set gEditingMission false" ) +{ + object->editorDisabled(); +} + +ConsoleMethod( EditManager, isEditorEnabled, bool, 2, 2, "Return the value of gEditingMission." ) +{ + return gEditingMission; +} \ No newline at end of file diff --git a/Engine/source/gui/worldEditor/editor.h b/Engine/source/gui/worldEditor/editor.h new file mode 100644 index 000000000..7ab4da857 --- /dev/null +++ b/Engine/source/gui/worldEditor/editor.h @@ -0,0 +1,69 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _EDITOR_H_ +#define _EDITOR_H_ + +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif + +class GameBase; + +//------------------------------------------------------------------------------ + +class EditManager : public GuiControl +{ + private: + typedef GuiControl Parent; + + public: + EditManager(); + ~EditManager(); + + bool onWake(); + void onSleep(); + + // SimObject + bool onAdd(); + + /// Perform the onEditorEnabled callback on all SimObjects + /// and set gEditingMission true. + void editorEnabled(); + + /// Perform the onEditorDisabled callback on all SimObjects + /// and set gEditingMission false. + void editorDisabled(); + + MatrixF mBookmarks[10]; + DECLARE_CONOBJECT(EditManager); + DECLARE_CATEGORY( "Gui Editor" ); +}; + +extern bool gEditingMission; + +//------------------------------------------------------------------------------ + +#endif diff --git a/Engine/source/gui/worldEditor/editorIconRegistry.cpp b/Engine/source/gui/worldEditor/editorIconRegistry.cpp new file mode 100644 index 000000000..331240bc8 --- /dev/null +++ b/Engine/source/gui/worldEditor/editorIconRegistry.cpp @@ -0,0 +1,225 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/worldEditor/editorIconRegistry.h" + +#include "console/console.h" +#include "console/simBase.h" + + +EditorIconRegistry gEditorIcons; + +ConsoleDoc( + "@class EditorIconRegistry\n" + "@brief This class is used to find the correct icon file path for different SimObject class types.\n\n" + "It is typically used by the editors, not intended for actual game development\n\n" + "@internal" +); + +EditorIconRegistry::EditorIconRegistry() +{ +} + +EditorIconRegistry::~EditorIconRegistry() +{ + clear(); +} + +void EditorIconRegistry::loadFromPath( const String &path, bool overwrite ) +{ + AbstractClassRep *classRep = AbstractClassRep::getClassList(); + while ( classRep ) + { + String iconFile = String::ToString( "%s%s", path.c_str(), classRep->getClassName() ); + add( classRep->getClassName(), iconFile.c_str(), overwrite ); + classRep = classRep->getNextClass(); + } + + String defaultIconFile = path + "default"; + + mDefaultIcon.set( defaultIconFile, + &GFXDefaultPersistentProfile, + avar("%s() - mIcons[] (line %d)", + __FUNCTION__, __LINE__) ); +} + +void EditorIconRegistry::add( const String &className, const String &imageFile, bool overwrite ) +{ + // First see if we can load the image. + GFXTexHandle icon( imageFile, &GFXDefaultPersistentProfile, + avar("%s() - mIcons[] (line %d)", __FUNCTION__, __LINE__) ); + if ( icon.isNull() ) + return; + + // Look it up in the map. + StringNoCase key( className ); + IconMap::Iterator iter = mIcons.find( key ); + if ( iter != mIcons.end() ) + { + if ( !overwrite ) + return; + + iter->value = icon; + } + else + mIcons.insertUnique( key, icon ); +} + +GFXTexHandle EditorIconRegistry::findIcon( AbstractClassRep *classRep ) +{ + while ( classRep ) + { + StringNoCase key( classRep->getClassName() ); + IconMap::Iterator icon = mIcons.find( key ); + + if ( icon != mIcons.end() && icon->value.isValid() ) + return icon->value; + + classRep = classRep->getParentClass(); + } + + return mDefaultIcon; +} + +GFXTexHandle EditorIconRegistry::findIcon( const SimObject *object ) +{ + if( object == NULL ) + return mDefaultIcon; + + AbstractClassRep *classRep = object->getClassRep(); + + return findIcon( classRep ); +} + +GFXTexHandle EditorIconRegistry::findIcon( const char *className ) +{ + // On the chance we have this className already in the map, + // check there first because its a lot faster... + + StringNoCase key( className ); + IconMap::Iterator icon = mIcons.find( key ); + + if ( icon != mIcons.end() && icon->value.isValid() ) + return icon->value; + + // Well, we could still have an icon for a parent class, + // so find the AbstractClassRep for the className. + // + // Unfortunately the only way to do this is looping through + // the AbstractClassRep linked list. + + bool found = false; + AbstractClassRep* pClassRep = AbstractClassRep::getClassList(); + + while ( pClassRep ) + { + if ( key.equal( pClassRep->getClassName(), String::NoCase ) ) + { + found = true; + break; + } + pClassRep = pClassRep->getNextClass(); + } + + if ( !found ) + { + Con::errorf( "EditorIconRegistry::findIcon, passed className %s was not an AbstractClassRep!", key.c_str() ); + return mDefaultIcon; + } + + // Now do a find by AbstractClassRep recursively up the class tree... + return findIcon( pClassRep ); +} + +bool EditorIconRegistry::hasIconNoRecurse( const SimObject *object ) +{ + AbstractClassRep *classRep = object->getClassRep(); + + StringNoCase key( classRep->getClassName() ); + + IconMap::Iterator icon = mIcons.find( key ); + + return icon != mIcons.end(); +} + +void EditorIconRegistry::clear() +{ + mIcons.clear(); + mDefaultIcon.free(); +} + +ConsoleStaticMethod( EditorIconRegistry, add, void, 3, 4, "( String className, String imageFile [, bool overwrite = true] )" + "@internal") +{ + bool overwrite = true; + if ( argc > 3 ) + overwrite = dAtob( argv[3] ); + + gEditorIcons.add( argv[1], argv[2], overwrite ); +} + +ConsoleStaticMethod( EditorIconRegistry, loadFromPath, void, 2, 3, "( String imagePath [, bool overwrite = true] )" + "@internal") +{ + bool overwrite = true; + if ( argc > 2 ) + overwrite = dAtob( argv[2] ); + + gEditorIcons.loadFromPath( argv[1], overwrite ); +} + +ConsoleStaticMethod( EditorIconRegistry, clear, void, 1, 1, "" + "@internal") +{ + gEditorIcons.clear(); +} + +ConsoleStaticMethod( EditorIconRegistry, findIconByClassName, const char*, 2, 2, "( String className )\n" + "Returns the file path to the icon file if found." + "@internal") +{ + GFXTexHandle icon = gEditorIcons.findIcon( argv[1] ); + if ( icon.isNull() ) + return NULL; + + return icon->mPath; +} + +ConsoleStaticMethod( EditorIconRegistry, findIconBySimObject, const char*, 2, 2, "( SimObject )\n" + "Returns the file path to the icon file if found." + "@internal") +{ + SimObject *obj = NULL; + if ( !Sim::findObject( argv[1], obj ) ) + { + Con::warnf( "EditorIconRegistry::findIcon, parameter %d was not a SimObject!", argv[1] ); + return NULL; + } + + GFXTexHandle icon = gEditorIcons.findIcon( obj ); + if ( icon.isNull() ) + return NULL; + + return icon->mPath; +} + diff --git a/Engine/source/gui/worldEditor/editorIconRegistry.h b/Engine/source/gui/worldEditor/editorIconRegistry.h new file mode 100644 index 000000000..35580d74c --- /dev/null +++ b/Engine/source/gui/worldEditor/editorIconRegistry.h @@ -0,0 +1,82 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _EDITORICONREGISTRY_H_ +#define _EDITORICONREGISTRY_H_ + +#ifndef _GFXTEXTUREHANDLE_H_ +#include "gfx/gfxTextureHandle.h" +#endif +#ifndef _TDICTIONARY_H_ +#include "core/util/tDictionary.h" +#endif + +class SimObject; +class AbstractClassRep; + + +/// This class is used to find the correct icon file +/// path for different SimObject class types. It is +/// typically used by the editors. +class EditorIconRegistry +{ +public: + + EditorIconRegistry(); + ~EditorIconRegistry(); + + /// Loops thru all the AbstractClassReps looking for icons in the path. + void loadFromPath( const String &path, bool overwrite ); + + /// Adds a single icon to the registry. + void add( const String &className, const String &imageFile, bool overwrite ); + + /// Clears all the icons from the registry. + void clear(); + + /// Looks up an icon given an AbstractClassRep. + /// Other findIcon methods redirect to this. + GFXTexHandle findIcon( AbstractClassRep *classRep ); + + /// Looks up an icon given a SimObject. + GFXTexHandle findIcon( const SimObject *object ); + + /// Looks up an icon given a className. + GFXTexHandle findIcon( const char *className ); + + /// Returns true if an icon is defined this object's class. + /// Does not recurse up the class hierarchy. + bool hasIconNoRecurse( const SimObject *object ); + +protected: + + typedef HashTable IconMap; + IconMap mIcons; + + /// The default icon returned when no matching icon is found. + GFXTexHandle mDefaultIcon; +}; + +/// The global registry of editor icons. +extern EditorIconRegistry gEditorIcons; + +#endif // _EDITORICONREGISTRY_H_ \ No newline at end of file diff --git a/Engine/source/gui/worldEditor/gizmo.cpp b/Engine/source/gui/worldEditor/gizmo.cpp new file mode 100644 index 000000000..7ae32519c --- /dev/null +++ b/Engine/source/gui/worldEditor/gizmo.cpp @@ -0,0 +1,2021 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/worldEditor/gizmo.h" + +#include "console/consoleTypes.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/util/gfxFrustumSaver.h" +#include "gfx/gfxDrawUtil.h" +#include "T3D/gameBase/gameConnection.h" +//#include "math/mathUtils.h" + +using namespace MathUtils; + + +// Developer Notes: + +// How to... Calculate the SelectionAxis index representing the normal +// of a plane, given a SelectionPlane index +// normal = axisVector[2 - (planeIdx - 3 )]; + +// How to... Get the two AxisVectors of a selected plane +// vec0 = mProjAxisVector[mAxisGizmoPlanarVectors[mSelectionIdx-3][0]]; +// vec1 = mProjAxisVector[mAxisGizmoPlanarVectors[mSelectionIdx-3][1]]; + +ImplementEnumType(GizmoAlignment, + "Whether the gizmo should be aligned with the world, or with the object.\n" + "@internal\n\n") + { World, "World", "Align the gizmo with the world.\n" }, + { Object, "Object", "Align the gizmo with the object.\n" } +EndImplementEnumType; + +ImplementEnumType( GizmoMode, + "@internal" ) + { NoneMode, "None" }, + { MoveMode, "Move" }, + { RotateMode, "Rotate" }, + { ScaleMode, "Scale" } +EndImplementEnumType; + + +//------------------------------------------------------------------------- +// Unnamed namespace for static data +//------------------------------------------------------------------------- + +namespace { + + static S32 sgAxisRemap[3][3] = { + {0, 1, 2}, + {2, 0, 1}, + {1, 2, 0}, + }; + + static VectorF sgAxisVectors[3] = { + VectorF(1.0f,0.0f,0.0f), + VectorF(0.0f,1.0f,0.0f), + VectorF(0.0f,0.0f,1.0f) + }; + + static U32 sgPlanarVectors[3][2] = { + { 0, 1 }, // XY + { 0, 2 }, // XZ + { 1, 2 } // YZ + }; + + static Point3F sgBoxPnts[] = { + Point3F(0.0f,0.0f,0.0f), + Point3F(0.0f,0.0f,1.0f), + Point3F(0.0f,1.0f,0.0f), + Point3F(0.0f,1.0f,1.0f), + Point3F(1.0f,0.0f,0.0f), + Point3F(1.0f,0.0f,1.0f), + Point3F(1.0f,1.0f,0.0f), + Point3F(1.0f,1.0f,1.0f) + }; + + static U32 sgBoxVerts[][4] = { + {0,2,3,1}, // -x + {7,6,4,5}, // +x + {0,1,5,4}, // -y + {3,2,6,7}, // +y + {0,4,6,2}, // -z + {3,7,5,1} // +z + }; + + static Point3F sgBoxNormals[] = { + Point3F(-1.0f, 0.0f, 0.0f), + Point3F( 1.0f, 0.0f, 0.0f), + Point3F( 0.0f,-1.0f, 0.0f), + Point3F( 0.0f, 1.0f, 0.0f), + Point3F( 0.0f, 0.0f,-1.0f), + Point3F( 0.0f, 0.0f, 1.0f) + }; + + static Point3F sgConePnts[] = { + Point3F(0.0f, 0.0f, 0.0f), + Point3F(-1.0f, 0.0f, -0.25f), + Point3F(-1.0f, -0.217f, -0.125f), + Point3F(-1.0f, -0.217f, 0.125f), + Point3F(-1.0f, 0.0f, 0.25f), + Point3F(-1.0f, 0.217f, 0.125f), + Point3F(-1.0f, 0.217f, -0.125f), + Point3F(-1.0f, 0.0f, 0.0f) + }; + + static U32 sgConeVerts[][3] = { + {0, 2, 1}, + {0, 3, 2}, + {0, 4, 3}, + {0, 5, 4}, + {0, 6, 5}, + {0, 1, 6}, + {7, 1, 6}, // Base + {7, 6, 5}, + {7, 5, 4}, + {7, 4, 3}, + {7, 3, 2}, + {7, 2, 1} + }; + + static Point3F sgCenterBoxPnts[] = { + Point3F(-0.5f, -0.5f, -0.5f), + Point3F(-0.5f, -0.5f, 0.5f), + Point3F(-0.5f, 0.5f, -0.5f), + Point3F(-0.5f, 0.5f, 0.5f), + Point3F( 0.5f, -0.5f, -0.5f), + Point3F( 0.5f, -0.5f, 0.5f), + Point3F( 0.5f, 0.5f, -0.5f), + Point3F( 0.5f, 0.5f, 0.5f) + }; + + static Point3F sgCirclePnts[] = { + Point3F(0.0f, 0.0f, -0.5f), + Point3F(0.0f, 0.354f, -0.354f), + Point3F(0.0f, 0.5f, 0), + Point3F(0.0f, 0.354f, 0.354f), + Point3F(0.0f, 0.0f, 0.5f), + Point3F(0.0f, -0.354f, 0.354f), + Point3F(0.0f, -0.5f, 0), + Point3F(0.0f, -0.354f, -0.354f), + Point3F(0.0f, 0.0f, -0.5f), + }; +} + + +//------------------------------------------------------------------------- +// GizmoProfile Class +//------------------------------------------------------------------------- + +GizmoProfile::GizmoProfile() +{ + mode = MoveMode; + alignment = World; + screenLen = 100; + renderWhenUsed = false; + renderInfoText = true; + renderPlane = true; + renderPlaneHashes = true; + renderSolid = false; + renderMoveGrid = true; + gridColor.set(255,255,255,20); + planeDim = 500.0f; + + gridSize.set(10,10,10); + snapToGrid = false; + allowSnapRotations = true; + rotationSnap = 15.0f; + allowSnapScale = true; + scaleSnap = 0.1f; + + rotateScalar = 0.8f; + scaleScalar = 0.8f; + + axisColors[0].set( 255, 0, 0 ); + axisColors[1].set( 0, 255, 0 ); + axisColors[2].set( 0, 0, 255 ); + + activeColor.set( 237, 219, 0 ); + inActiveColor.set( 170, 170, 170 ); + + centroidColor.set( 255, 255, 255 ); + centroidHighlightColor.set( 255, 0, 255 ); + + restoreDefaultState(); +} + +void GizmoProfile::restoreDefaultState() +{ + flags = U32_MAX; + flags &= ~CanRotateUniform; + allAxesScaleUniform = false; +} + +IMPLEMENT_CONOBJECT( GizmoProfile ); + +ConsoleDocClass( GizmoProfile, + "@brief This class contains behavior and rendering properties used " + "by the Gizmo class\n\n" + "Not intended for game development, for editors or internal use only.\n\n " + "@internal"); + +bool GizmoProfile::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + const char* fontCacheDirectory = Con::getVariable("$GUI::fontCacheDirectory"); + font = GFont::create( "Arial", 10, fontCacheDirectory, TGE_ANSI_CHARSET); + if ( !font ) + { + Con::errorf( "GizmoProfile::onAdd - failed to load font!" ); + return false; + } + + return true; +} + +void GizmoProfile::initPersistFields() +{ + addField( "alignment", TYPEID< GizmoAlignment >(), Offset(alignment, GizmoProfile ) ); + addField( "mode", TYPEID< GizmoMode >(), Offset(mode, GizmoProfile ) ); + + addField( "snapToGrid", TypeBool, Offset(snapToGrid, GizmoProfile) ); + addField( "allowSnapRotations", TypeBool, Offset(allowSnapRotations, GizmoProfile) ); + addField( "rotationSnap", TypeF32, Offset(rotationSnap, GizmoProfile) ); + addField( "allowSnapScale", TypeBool, Offset(allowSnapScale, GizmoProfile) ); + addField( "scaleSnap", TypeF32, Offset(scaleSnap, GizmoProfile) ); + addField( "renderWhenUsed", TypeBool, Offset(renderWhenUsed, GizmoProfile) ); + addField( "renderInfoText", TypeBool, Offset(renderInfoText, GizmoProfile) ); + addField( "renderPlane", TypeBool, Offset(renderPlane, GizmoProfile) ); + addField( "renderPlaneHashes", TypeBool, Offset(renderPlaneHashes, GizmoProfile) ); + addField( "renderSolid", TypeBool, Offset(renderSolid, GizmoProfile) ); + addField( "renderMoveGrid", TypeBool, Offset( renderMoveGrid, GizmoProfile ) ); + addField( "gridColor", TypeColorI, Offset(gridColor, GizmoProfile) ); + addField( "planeDim", TypeF32, Offset(planeDim, GizmoProfile) ); + addField( "gridSize", TypePoint3F, Offset(gridSize, GizmoProfile) ); + addField( "screenLength", TypeS32, Offset(screenLen, GizmoProfile) ); + addField( "rotateScalar", TypeF32, Offset(rotateScalar, GizmoProfile) ); + addField( "scaleScalar", TypeF32, Offset(scaleScalar, GizmoProfile) ); + addField( "flags", TypeS32, Offset(flags, GizmoProfile) ); +} + +void GizmoProfile::consoleInit() +{ + Parent::consoleInit(); + + Con::setIntVariable( "$GizmoFlag::CanRotate", CanRotate ); + Con::setIntVariable( "$GizmoFlag::CanRotateX", CanRotateX ); + Con::setIntVariable( "$GizmoFlag::CanRotateY", CanRotateY ); + Con::setIntVariable( "$GizmoFlag::CanRotateZ", CanRotateZ ); + Con::setIntVariable( "$GizmoFlag::CanRotateScreen", CanRotateScreen ); + Con::setIntVariable( "$GizmoFlag::CanRotateUniform", CanRotateUniform ); + Con::setIntVariable( "$GizmoFlag::CanScale", CanScale ); + Con::setIntVariable( "$GizmoFlag::CanScaleX", CanScaleX ); + Con::setIntVariable( "$GizmoFlag::CanScaleY", CanScaleY ); + Con::setIntVariable( "$GizmoFlag::CanScaleZ", CanScaleZ ); + Con::setIntVariable( "$GizmoFlag::CanScaleUniform", CanScaleUniform ); + Con::setIntVariable( "$GizmoFlag::CanTranslate", CanTranslate ); + Con::setIntVariable( "$GizmoFlag::CanTranslateX", CanTranslateX ); + Con::setIntVariable( "$GizmoFlag::CanTranslateY", CanTranslateY ); + Con::setIntVariable( "$GizmoFlag::CanTranslateZ", CanTranslateZ ); + Con::setIntVariable( "$GizmoFlag::CanTranslateUniform", CanTranslateUniform ); + Con::setIntVariable( "$GizmoFlag::PlanarHandlesOn", PlanarHandlesOn ); +} + +//------------------------------------------------------------------------- +// Gizmo Class +//------------------------------------------------------------------------- + +F32 Gizmo::smProjectDistance = 20000.0f; + +Gizmo::Gizmo() +: mProfile( NULL ), + mSelectionIdx( -1 ), + mCameraMat( true ), + mTransform( true ), + mObjectMat( true ), + mObjectMatInv( true ), + mCurrentTransform( true ), + mSavedTransform( true ), + mSavedScale( 0,0,0 ), + mDeltaScale( 0,0,0 ), + mDeltaRot( 0,0,0 ), + mDeltaPos( 0,0,0 ), + mDeltaTotalPos( 0,0,0 ), + mDeltaTotalRot( 0,0,0 ), + mDeltaTotalScale( 0,0,0 ), + mCurrentAlignment( World ), + mCurrentMode( MoveMode ), + mDirty( false ), + mMouseDownPos( -1,-1 ), + mMouseDown( false ), + mLastWorldMat( true ), + mLastProjMat( true ), + mLastViewport( 0, 0, 10, 10 ), + mLastCameraFOV( 1.f ), + mElipseCursorCollideVecSS( 1.0f, 0.0f, 0.0f ), + mElipseCursorCollidePntSS( 0.0f, 0.0f, 0.0f ), + mHighlightCentroidHandle( false ), + mHighlightAll( false ), + mGridPlaneEnabled( true ), + mMoveGridEnabled( true ), + mMoveGridSize( 20.f ), + mMoveGridSpacing( 1.f ) +{ + mUniformHandleEnabled = true; + mAxisEnabled[0] = mAxisEnabled[1] = mAxisEnabled[2] = true; +} + +Gizmo::~Gizmo() +{ +} + +IMPLEMENT_CONOBJECT( Gizmo ); + +ConsoleDocClass( Gizmo, + "@brief This class contains code for rendering and manipulating a 3D gizmo\n\n" + "It is usually used as a helper within a TSEdit-derived control. " + "Not intended for game development, for editors or internal use only.\n\n " + "@internal"); + +// SimObject Methods... + +bool Gizmo::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + if ( !mProfile ) + return false; + + mCurrentAlignment = mProfile->alignment; + mCurrentMode = mProfile->mode; + + return true; +} + +void Gizmo::onRemove() +{ + Parent::onRemove(); +} + +void Gizmo::initPersistFields() +{ + Parent::initPersistFields(); + + //addField( "profile",) +} + +// Gizmo Accessors and Mutators... + +void Gizmo::set( const MatrixF &objMat, const Point3F &worldPos, const Point3F &objScale ) +{ + if ( mMouseDown ) + return; + + mCurrentAlignment = _filteredAlignment(); + + if ( mCurrentAlignment == World ) + { + mTransform.identity(); + mTransform.setPosition( worldPos ); + mScale = objScale; + mObjectMat = objMat; + } + else + { + mTransform = objMat; + mTransform.setPosition( worldPos ); + mScale = objScale; + mObjectMat.identity(); + } + + mCurrentTransform = objMat; + mObjectMat.invertTo( &mObjectMatInv ); +} + +Gizmo::Selection Gizmo::getSelection() +{ + if ( mProfile->mode == NoneMode ) + return None; + + return (Selection)mSelectionIdx; +} + +VectorF Gizmo::selectionToAxisVector( Selection axis ) +{ + if ( axis < Axis_X || axis > Axis_Z ) + return VectorF(0,0,0); + + return sgAxisVectors[(U32)axis]; +} + +bool Gizmo::collideAxisGizmo( const Gui3DMouseEvent & event ) +{ + if ( mProfile->mode == NoneMode ) + return false; + + _calcAxisInfo(); + + // Early out if we are in a mode that is disabled. + if ( mProfile->mode == RotateMode && !(mProfile->flags & GizmoProfile::CanRotate ) ) + return false; + if ( mProfile->mode == MoveMode && !(mProfile->flags & GizmoProfile::CanTranslate ) ) + return false; + if ( mProfile->mode == ScaleMode && !(mProfile->flags & GizmoProfile::CanScale ) ) + return false; + + VectorF camPos; + if( GFX->isFrustumOrtho() ) + camPos = event.pos; + else + camPos = mCameraPos; + + VectorF toGizmoVec; + + // get the projected size... + + toGizmoVec = mOrigin - mCameraPos; + toGizmoVec.normalizeSafe(); + + PlaneF clipPlane( mOrigin, toGizmoVec ); + + mSelectionIdx = -1; + Point3F end = camPos + event.vec * smProjectDistance; + + if ( mProfile->mode == RotateMode ) + { + const Point3F mousePntSS( (F32)event.mousePoint.x, (F32)event.mousePoint.y, 0.0f ); + const F32 axisCollisionThresholdSS = 10.0f; + + + Point3F originSS; + MathUtils::mProjectWorldToScreen( mOrigin, &originSS, mLastViewport, mLastWorldMat, mLastProjMat ); + originSS.z = 0.0f; + + const F32 originDistSS = mAbs( ( mousePntSS - originSS ).len() ); + + // Check for camera facing axis rotation handle collision. + if ( mScreenRotateHandleEnabled ) + { + const F32 distSS = mAbs( ( (F32)mProfile->screenLen * 0.7f ) - originDistSS ); + + if ( distSS < axisCollisionThresholdSS ) + { + mSelectionIdx = Custom1; + + Point3F normal = mousePntSS - originSS; + normal.normalizeSafe(); + Point3F tangent = mCross( -normal, Point3F(0,0,1) ); + tangent.normalizeSafe(); + + mElipseCursorCollidePntSS = mousePntSS; + mElipseCursorCollideVecSS = tangent; + mElipseCursorCollideVecSS.z = 0.0f; + mElipseCursorCollideVecSS.normalizeSafe(); + + return true; + } + + } + + // Check for x/y/z axis ellipse handle collision. + // We do this as a screen-space pixel distance test between + // the cursor position and the ellipse handle projected to the screen + // as individual segments. + { + const F32 ellipseRadiusWS = mProjLen * 0.5f; + const U32 segments = 40; + const F32 stepRadians = mDegToRad(360.0f) / segments; + + U32 x,y,z; + F32 ang0, ang1, distSS; + Point3F temp, pnt0, pnt1, closestPntSS; + bool valid0, valid1; + + MatrixF worldToGizmo = mTransform; + worldToGizmo.inverse(); + PlaneF clipPlaneGS; // Clip plane in gizmo space. + mTransformPlane( worldToGizmo, Point3F(1,1,1), clipPlane, &clipPlaneGS ); + + for ( U32 i = 0; i < 3; i++ ) + { + if ( !mAxisEnabled[i] ) + continue; + + x = sgAxisRemap[i][0]; + y = sgAxisRemap[i][1]; + z = sgAxisRemap[i][2]; + + for ( U32 j = 1; j <= segments; j++ ) + { + ang0 = (j-1) * stepRadians; + ang1 = j * stepRadians; + + temp.x = 0.0f; + temp.y = mCos(ang0) * ellipseRadiusWS; + temp.z = mSin(ang0) * ellipseRadiusWS; + pnt0.set( temp[x], temp[y], temp[z] ); + + temp.x = 0.0f; + temp.y = mCos(ang1) * ellipseRadiusWS; + temp.z = mSin(ang1) * ellipseRadiusWS; + pnt1.set( temp[x], temp[y], temp[z] ); + + valid0 = ( clipPlaneGS.whichSide(pnt0) == PlaneF::Back ); + valid1 = ( clipPlaneGS.whichSide(pnt1) == PlaneF::Back ); + + if ( !valid0 || !valid1 ) + continue; + + // Transform points from gizmo space to world space. + + mTransform.mulP( pnt0 ); + mTransform.mulP( pnt1 ); + + // Transform points from gizmo space to screen space. + + valid0 = MathUtils::mProjectWorldToScreen( pnt0, &pnt0, mLastViewport, mLastWorldMat, mLastProjMat ); + valid1 = MathUtils::mProjectWorldToScreen( pnt1, &pnt1, mLastViewport, mLastWorldMat, mLastProjMat ); + + // Get distance from the cursor. + + closestPntSS = MathUtils::mClosestPointOnSegment( Point3F( pnt0.x, pnt0.y, 0.0f ), Point3F( pnt1.x, pnt1.y, 0.0f ), mousePntSS ); + distSS = ( closestPntSS - mousePntSS ).len(); + + if ( distSS < axisCollisionThresholdSS ) + { + mSelectionIdx = i; + mElipseCursorCollidePntSS = mousePntSS; + mElipseCursorCollideVecSS = pnt1 - pnt0; + mElipseCursorCollideVecSS.z = 0.0f; + mElipseCursorCollideVecSS.normalizeSafe(); + + return true; + } + } + } + } + + // Check for sphere surface collision + if ( originDistSS <= (F32)mProfile->screenLen * 0.5f ) + { + // If this style manipulation is desired it must also be implemented in onMouseDragged. + //mSelectionIdx = Custom2; + //return true; + } + } + + // Check if we've hit the uniform scale handle... + if ( mUniformHandleEnabled ) + { + F32 tipScale = mProjLen * 0.1f; + Point3F sp( tipScale, tipScale, tipScale ); + + Point3F min = mOrigin - sp; + Point3F max = mOrigin + sp; + Box3F uhandle(min, max); + if ( uhandle.collideLine( camPos, end ) ) + { + mSelectionIdx = Centroid; + return true; + } + } + + // Check if we've hit the planar handles... + if ( ( mProfile->mode == MoveMode || mProfile->mode == ScaleMode ) && + ( mProfile->flags & GizmoProfile::PlanarHandlesOn ) ) + { + for ( U32 i = 0; i < 3; i++ ) + { + Point3F p1 = mProjAxisVector[sgPlanarVectors[i][0]]; + Point3F p2 = mProjAxisVector[sgPlanarVectors[i][1]]; + VectorF normal; + mCross(p1, p2, &normal); + + if(normal.isZero()) + continue; + + PlaneF plane(mOrigin, normal); + + p1 *= mProjLen * 0.5f; + p2 *= mProjLen * 0.5f; + + F32 scale = 0.5f; + + Point3F poly [] = { + Point3F(mOrigin + p1 + p2 * scale), + Point3F(mOrigin + p1 + p2), + Point3F(mOrigin + p1 * scale + p2), + Point3F(mOrigin + (p1 + p2) * scale) + }; + + Point3F end = camPos + event.vec * smProjectDistance; + F32 t = plane.intersect(camPos, end); + if ( t >= 0 && t <= 1 ) + { + Point3F pos; + pos.interpolate(camPos, end, t); + + // check if inside our 'poly' of this axisIdx vector... + bool inside = true; + for(U32 j = 0; inside && (j < 4); j++) + { + U32 k = (j+1) % 4; + VectorF vec1 = poly[k] - poly[j]; + VectorF vec2 = pos - poly[k]; + + if(mDot(vec1, vec2) > 0.f) + inside = false; + } + + // + if ( inside ) + { + mSelectionIdx = i+3; + //mAxisGizmoSelPlane = plane; + //mAxisGizmoSelPlaneIndex = i; + //mAxisGizmoSelPlanePoint = pos; + //mAxisGizmoSelStart = camPos; + return true; + } + } + } + } + + if ( mCurrentMode == RotateMode ) + return false; + + // Check if we've hit an axis... + for ( U32 i = 0; i < 3; i++ ) + { + if ( !mAxisEnabled[i] ) + continue; + + VectorF up, normal; + mCross(toGizmoVec, mProjAxisVector[i], &up); + mCross(up, mProjAxisVector[i], &normal); + + if ( normal.isZero() ) + continue; + + PlaneF plane( mOrigin, normal ); + + // width of the axisIdx poly is 1/10 the run + Point3F a = up * mProjLen / 10; + Point3F b = mProjAxisVector[i] * mProjLen; + + Point3F poly [] = { + Point3F(mOrigin + a), + Point3F(mOrigin + a + b), + Point3F(mOrigin - a + b), + Point3F(mOrigin - a) + }; + + F32 t = plane.intersect(camPos, end); + if ( t >= 0 && t <= 1 ) + { + Point3F pos; + pos.interpolate(camPos, end, t); + + // check if inside our 'poly' of this axisIdx vector... + bool inside = true; + for ( U32 j = 0; inside && (j < 4); j++ ) + { + U32 k = (j+1) % 4; + VectorF vec1 = poly[k] - poly[j]; + VectorF vec2 = pos - poly[k]; + + if ( mDot(vec1, vec2) > 0.f ) + inside = false; + } + + // + if(inside) + { + mSelectionIdx = i; + + return true; + } + } + } + + return false; +} + +void Gizmo::on3DMouseDown( const Gui3DMouseEvent & event ) +{ + _updateState(); + + mMouseDown = true; + + if ( mProfile->mode == NoneMode ) + return; + + // Save the current transforms, need this for some + // operations that occur on3DMouseDragged. + + mSavedTransform = mTransform; + mSavedScale = mScale; + mSavedRot = mTransform.toEuler(); + + mMouseDownPos = event.mousePoint; + mLastAngle = 0.0f; + mLastScale = mScale; + mLastMouseEvent = event; + mSign = 0.0f; + + _calcAxisInfo(); + + // Calculate mMouseCollideLine and mMouseDownProjPnt + // which are used in on3DMouseDragged. + + if ( mProfile->mode == MoveMode || mProfile->mode == ScaleMode ) + { + if ( mSelectionIdx >= Axis_X && mSelectionIdx <= Axis_Z ) + { + MathUtils::Line clickLine; + clickLine.origin = event.pos; + clickLine.direction = event.vec; + + VectorF objectAxisVector = sgAxisVectors[mSelectionIdx]; + VectorF worldAxisVector = objectAxisVector; + mTransform.mulV( worldAxisVector ); + + MathUtils::Line axisLine; + axisLine.origin = mTransform.getPosition(); + axisLine.direction = worldAxisVector; + + mMouseCollideLine = axisLine; + + LineSegment segment; + mShortestSegmentBetweenLines( clickLine, axisLine, &segment ); + + mMouseDownProjPnt = segment.p1; + } + else if ( mSelectionIdx >= Plane_XY && mSelectionIdx <= Plane_YZ ) + { + VectorF objectPlaneNormal = sgAxisVectors[2 - (mSelectionIdx - 3 )]; + VectorF worldPlaneNormal = objectPlaneNormal; + mTransform.mulV( worldPlaneNormal ); + + PlaneF plane( mTransform.getPosition(), worldPlaneNormal ); + + mMouseCollidePlane = plane; + + Point3F intersectPnt; + if ( plane.intersect( event.pos, event.vec, &intersectPnt ) ) + { + mMouseDownProjPnt = intersectPnt; + } + + // We also calculate the line to be used later. + + VectorF objectAxisVector(0,0,0); + objectAxisVector += sgAxisVectors[sgPlanarVectors[mSelectionIdx-3][0]]; + objectAxisVector += sgAxisVectors[sgPlanarVectors[mSelectionIdx-3][1]]; + objectAxisVector.normalize(); + + VectorF worldAxisVector = objectAxisVector; + mTransform.mulV( worldAxisVector ); + + MathUtils::Line axisLine; + axisLine.origin = mTransform.getPosition(); + axisLine.direction = worldAxisVector; + + mMouseCollideLine = axisLine; + } + else if ( mSelectionIdx == Centroid ) + { + VectorF normal; + mCameraMat.getColumn(1,&normal); + normal = -normal; + + PlaneF plane( mOrigin, normal ); + + mMouseCollidePlane = plane; + + Point3F intersectPnt; + if ( plane.intersect( event.pos, event.vec, &intersectPnt ) ) + { + mMouseDownProjPnt = intersectPnt; + } + } + } + else if ( mProfile->mode == RotateMode ) + { + VectorF camPos; + if( GFX->isFrustumOrtho() ) + camPos = event.pos; + else + camPos = mCameraPos; + + Point3F end = camPos + event.vec * smProjectDistance; + + if ( 0 <= mSelectionIdx && mSelectionIdx <= 2 ) + { + // Nothing to do, we already have mElipseCursorCollidePntSS + // and mElipseCursorCollideVecSS set. + } + else if ( mSelectionIdx == Custom1 ) + { + // Nothing to do, we already have mElipseCursorCollidePntSS + // and mElipseCursorCollideVecSS set. + } + else if ( mSelectionIdx == Centroid ) + { + // The Centroid handle for rotation mode is not implemented to do anything. + // It can be handled by the class making use of the Gizmo. + } + } +} + +void Gizmo::on3DMouseUp( const Gui3DMouseEvent &event ) +{ + _updateState(); + mMouseDown = false; + mDeltaTotalPos.zero(); + mDeltaTotalScale.zero(); + mDeltaTotalRot.zero(); + + // Done with a drag operation, recenter our orientation to the world. + if ( mCurrentAlignment == World ) + { + Point3F pos = mTransform.getPosition(); + mTransform.identity(); + mTransform.setPosition( pos ); + } +} + +void Gizmo::on3DMouseMove( const Gui3DMouseEvent & event ) +{ + _updateState( false ); + + if ( mProfile->mode == NoneMode ) + return; + + collideAxisGizmo( event ); + + mLastMouseEvent = event; +} + +void Gizmo::on3DMouseDragged( const Gui3DMouseEvent & event ) +{ + _updateState( false ); + + if ( !mProfile || mProfile->mode == NoneMode || mSelectionIdx == None ) + return; + + // If we got a dragged event without the mouseDown flag the drag operation + // must have been canceled by a mode change, ignore further dragged events. + if ( !mMouseDown ) + return; + + _calcAxisInfo(); + + if ( mProfile->mode == MoveMode || mProfile->mode == ScaleMode ) + { + Point3F projPnt = mOrigin; + + // Project the mouse position onto the line/plane of manipulation... + + if ( mSelectionIdx >= 0 && mSelectionIdx <= 2 ) + { + MathUtils::Line clickLine; + clickLine.origin = event.pos; + clickLine.direction = event.vec; + + LineSegment segment; + mShortestSegmentBetweenLines( clickLine, mMouseCollideLine, &segment ); + + projPnt = segment.p1; + + // snap to the selected axisIdx, if required + Point3F snapPnt = _snapPoint(projPnt); + + if ( mSelectionIdx < 3 ) + { + projPnt[mSelectionIdx] = snapPnt[mSelectionIdx]; + } + else + { + projPnt[sgPlanarVectors[mSelectionIdx-3][0]] = snapPnt[sgPlanarVectors[mSelectionIdx-3][0]]; + projPnt[sgPlanarVectors[mSelectionIdx-3][1]] = snapPnt[sgPlanarVectors[mSelectionIdx-3][1]]; + } + } + else if ( 3 <= mSelectionIdx && mSelectionIdx <= 5 ) + { + if ( mProfile->mode == MoveMode ) + { + Point3F intersectPnt; + if ( mMouseCollidePlane.intersect( event.pos, event.vec, &intersectPnt ) ) + { + projPnt = intersectPnt; + + // snap to the selected axisIdx, if required + Point3F snapPnt = _snapPoint(projPnt); + projPnt[sgPlanarVectors[mSelectionIdx-3][0]] = snapPnt[sgPlanarVectors[mSelectionIdx-3][0]]; + projPnt[sgPlanarVectors[mSelectionIdx-3][1]] = snapPnt[sgPlanarVectors[mSelectionIdx-3][1]]; + } + } + else // ScaleMode + { + MathUtils::Line clickLine; + clickLine.origin = event.pos; + clickLine.direction = event.vec; + + LineSegment segment; + mShortestSegmentBetweenLines( clickLine, mMouseCollideLine, &segment ); + + projPnt = segment.p1; + } + } + else if ( mSelectionIdx == Centroid ) + { + Point3F intersectPnt; + if ( mMouseCollidePlane.intersect( event.pos, event.vec, &intersectPnt ) ) + { + projPnt = _snapPoint( intersectPnt ); + } + } + + // Perform the manipulation... + + if ( mProfile->mode == MoveMode ) + { + // Clear deltas we aren't using... + mDeltaRot.zero(); + mDeltaScale.zero(); + + Point3F newPosition; + if( mProfile->snapToGrid ) + { + Point3F snappedMouseDownProjPnt = _snapPoint( mMouseDownProjPnt ); + mDeltaTotalPos = projPnt - snappedMouseDownProjPnt; + newPosition = projPnt; + } + else + { + mDeltaTotalPos = projPnt - mMouseDownProjPnt; + newPosition = mSavedTransform.getPosition() + mDeltaTotalPos; + } + + mDeltaPos = newPosition - mTransform.getPosition(); + mTransform.setPosition( newPosition ); + + mCurrentTransform.setPosition( newPosition ); + } + else // ScaleMode + { + // This is the world-space axis we want to scale + //VectorF axis = sgAxisVectors[mSelectionIdx]; + + // Find its object-space components... + //MatrixF mat = mObjectMat; + //mat.inverse(); + //mat.mulV(axis); + + // Which needs to always be positive, this is a 'scale' transformation + // not really a 'vector' transformation. + //for ( U32 i = 0; i < 3; i++ ) + // axis[i] = mFabs(axis[i]); + + //axis.normalizeSafe(); + + // Clear deltas we aren't using... + mDeltaRot.zero(); + mDeltaPos.zero(); + + + // Calculate the deltaScale... + VectorF deltaScale(0,0,0); + + if ( 0 <= mSelectionIdx && mSelectionIdx <= 2 ) + { + // Are we above or below the starting position relative to this axis? + PlaneF plane( mMouseDownProjPnt, mProjAxisVector[mSelectionIdx] ); + F32 sign = ( plane.whichSide( projPnt ) == PlaneF::Front ) ? 1 : -1; + F32 diff = ( projPnt - mMouseDownProjPnt ).len(); + + if ( mProfile->allAxesScaleUniform ) + { + deltaScale.set(1,1,1); + deltaScale = deltaScale * sign * diff; + } + else + deltaScale[mSelectionIdx] = diff * sign; + } + else if ( 3 <= mSelectionIdx && mSelectionIdx <= 5 ) + { + PlaneF plane( mMouseDownProjPnt, mMouseCollideLine.direction ); + F32 sign = ( plane.whichSide( projPnt ) == PlaneF::Front ) ? 1 : -1; + F32 diff = ( projPnt - mMouseDownProjPnt ).len(); + + if ( mProfile->allAxesScaleUniform ) + { + deltaScale.set(1,1,1); + deltaScale = deltaScale * sign * diff; + } + else + { + deltaScale[sgPlanarVectors[mSelectionIdx-3][0]] = diff * sign; + deltaScale[sgPlanarVectors[mSelectionIdx-3][1]] = diff * sign; + } + } + else // mSelectionIdx == 6 + { + // Are we above or below the starting position relative to the camera? + VectorF normal; + mCameraMat.getColumn( 2, &normal ); + + PlaneF plane( mMouseDownProjPnt, normal ); + + F32 sign = ( plane.whichSide( projPnt ) == PlaneF::Front ) ? 1 : -1; + F32 diff = ( projPnt - mMouseDownProjPnt ).len(); + deltaScale.set(1,1,1); + deltaScale = deltaScale * sign * diff; + } + + // Save current scale, then set mDeltaScale + // to the amount it changes during this call. + mDeltaScale = mScale; + + mDeltaTotalScale = deltaScale; + + mScale = mSavedScale; + mScale += deltaScale * mProfile->scaleScalar; + + mDeltaScale = mScale - mDeltaScale; + + mScale.setMax( Point3F( 0.01f ) ); + } + + mDirty = true; + } + else if ( mProfile->mode == RotateMode && + mSelectionIdx != Centroid ) + { + // Clear deltas we aren't using... + mDeltaScale.zero(); + mDeltaPos.zero(); + + bool doScreenRot = ( mSelectionIdx == Custom1 ); + + U32 rotAxisIdx = ( doScreenRot ) ? 1 : mSelectionIdx; + + Point3F mousePntSS( event.mousePoint.x, event.mousePoint.y, 0.0f ); + + Point3F pntSS0 = mElipseCursorCollidePntSS + mElipseCursorCollideVecSS * 10000.0f; + Point3F pntSS1 = mElipseCursorCollidePntSS - mElipseCursorCollideVecSS * 10000.0f; + + Point3F closestPntSS = MathUtils::mClosestPointOnSegment( pntSS0, pntSS1, mousePntSS ); + + Point3F offsetDir = closestPntSS - mElipseCursorCollidePntSS; + F32 offsetDist = offsetDir.len(); + offsetDir.normalizeSafe(); + + F32 dot = mDot( mElipseCursorCollideVecSS, offsetDir ); + mSign = mIsZero( dot ) ? 0.0f : ( dot > 0.0f ) ? -1.0f : 1.0f; + + // The angle that we will rotate the (saved) gizmo transform by to + // generate the current gizmo transform. + F32 angle = offsetDist * mSign * mProfile->rotateScalar; + angle *= 0.02f; // scale down to not require rotate scalar to be microscopic + + // + if( mProfile->allowSnapRotations && event.modifier & SI_SHIFT ) + angle = mDegToRad( _snapFloat( mRadToDeg( angle ), mProfile->rotationSnap ) ); + + mDeltaAngle = angle - mLastAngle; + mLastAngle = angle; + + if ( doScreenRot ) + { + // Rotate relative to the camera. + // We rotate around the y/forward vector pointing from the camera + // to the gizmo. + + // NOTE: This does NOT work + + // Calculate mDeltaAngle and mDeltaTotalRot + //{ + // VectorF fvec( mOrigin - mCameraPos ); + // fvec.normalizeSafe(); + + // AngAxisF aa( fvec, mDeltaAngle ); + // MatrixF mat; + // aa.setMatrix( &mat ); + + // mDeltaRot = mat.toEuler(); + + // aa.set( fvec, mLastAngle ); + // aa.setMatrix( &mat ); + // mDeltaTotalRot = mat.toEuler(); + //} + + //MatrixF rotMat( mDeltaTotalRot ); + + //if ( mCurrentAlignment == World ) + //{ + // //aa.setMatrix( &rotMat ); + // mTransform = mSavedTransform * rotMat; + // mTransform.setPosition( mOrigin ); + + // rotMat.inverse(); + // mCurrentTransform = mObjectMatInv * rotMat; + // mCurrentTransform.inverse(); + // mCurrentTransform.setPosition( mOrigin ); + //} + //else + //{ + // rotMat.inverse(); + + // MatrixF m0; + // mSavedTransform.invertTo(&m0); + // + // mTransform = m0 * rotMat; + // mTransform.inverse(); + // mTransform.setPosition( mOrigin ); + + // mCurrentTransform = mTransform; + //} + } + else + { + // Normal rotation, eg, not screen relative. + + mDeltaRot.set(0,0,0); + mDeltaRot[rotAxisIdx] = mDeltaAngle; + + mDeltaTotalRot.set(0,0,0); + mDeltaTotalRot[rotAxisIdx] = angle; + + MatrixF rotMat( mDeltaTotalRot ); + + mTransform = mSavedTransform * rotMat; + mTransform.setPosition( mSavedTransform.getPosition() ); + + if ( mCurrentAlignment == World ) + { + MatrixF mat0 = mCurrentTransform; + + rotMat.inverse(); + mCurrentTransform = mObjectMatInv * rotMat; + mCurrentTransform.inverse(); + mCurrentTransform.setPosition( mOrigin ); + + MatrixF mat1 = mCurrentTransform; + mat0.inverse(); + MatrixF mrot; + mrot = mat0 * mat1; + mDeltaRot = mrot.toEuler(); + } + else + { + mCurrentTransform = mTransform; + } + } + + mDirty = true; + } + + mLastMouseEvent = event; +} + +//------------------------------------------------------------------------------ + +void Gizmo::renderGizmo(const MatrixF &cameraTransform, F32 cameraFOV ) +{ + mLastWorldMat = GFX->getWorldMatrix(); + mLastProjMat = GFX->getProjectionMatrix(); + mLastViewport = GFX->getViewport(); + mLastWorldToScreenScale = GFX->getWorldToScreenScale(); + mLastCameraFOV = cameraFOV; + + // Save the Camera transform matrix, used all over... + mCameraMat = cameraTransform; + mCameraPos = mCameraMat.getPosition(); + + GFXFrustumSaver fsaver; + + // Change the far plane distance so that the gizmo is always visible. + Frustum frustum = GFX->getFrustum(); + frustum.setFarDist( 100000.0f ); + GFX->setFrustum( frustum ); + + _updateEnabledAxices(); + + _updateState(); + + _calcAxisInfo(); + + if( mMouseDown ) + { + if( mProfile->renderMoveGrid && mMoveGridEnabled && mCurrentMode == MoveMode ) + { + GFXStateBlockDesc desc; + + desc.setBlend( true ); + desc.setZReadWrite( true, true ); + + GFXDrawUtil::Plane plane = GFXDrawUtil::PlaneXY; + ColorI color( 128, 128, 128, 200 ); + + switch( mSelectionIdx ) + { + case Axis_Z: + case Plane_XY: + plane = GFXDrawUtil::PlaneXY; + break; + + case Axis_X: + case Plane_YZ: + plane = GFXDrawUtil::PlaneYZ; + break; + + case Axis_Y: + case Plane_XZ: + plane = GFXDrawUtil::PlaneXZ; + break; + } + + GFX->getDrawUtil()->drawPlaneGrid( + desc, + mTransform.getPosition(), + Point2F( mMoveGridSize, mMoveGridSize ), + Point2F( mMoveGridSpacing, mMoveGridSpacing ), + color, + plane + ); + } + + if( !mProfile->renderWhenUsed ) + return; + } + + mHighlightAll = mProfile->allAxesScaleUniform && mSelectionIdx >= 0 && mSelectionIdx <= 3; + + // Render plane (if set to render) behind the gizmo + if ( mProfile->mode != NoneMode ) + _renderPlane(); + + _setStateBlock(); + + // Special case for NoneMode, + // we only render the primary axis with no tips. + if ( mProfile->mode == NoneMode ) + { + _renderPrimaryAxis(); + return; + } + + if ( mProfile->mode == RotateMode ) + { + PrimBuild::begin( GFXLineList, 6 ); + + // Render the primary axisIdx + for(U32 i = 0; i < 3; i++) + { + PrimBuild::color( ( mHighlightAll || i == mSelectionIdx ) ? mProfile->axisColors[i] : mProfile->inActiveColor ); + PrimBuild::vertex3fv( mOrigin ); + PrimBuild::vertex3fv( mOrigin + mProjAxisVector[i] * mProjLen * 0.25f ); + } + + PrimBuild::end(); + + _renderAxisCircles(); + } + else + { + // Both Move and Scale modes render basis vectors as + // large stick lines. + _renderPrimaryAxis(); + + // Render the tips based on current operation. + + GFXTransformSaver saver( true, false ); + + GFX->multWorld(mTransform); + + if ( mProfile->mode == ScaleMode ) + { + _renderAxisBoxes(); + } + else if ( mProfile->mode == MoveMode ) + { + _renderAxisArrows(); + } + + saver.restore(); + } + + // Render the planar handles... + + if ( mCurrentMode != RotateMode ) + { + Point3F midpnt[3]; + for(U32 i = 0; i < 3; i++) + midpnt[i] = mProjAxisVector[i] * mProjLen * 0.5f; + + PrimBuild::begin( GFXLineList, 12 ); + + for(U32 i = 0; i < 3; i++) + { + U32 axis0 = sgPlanarVectors[i][0]; + U32 axis1 = sgPlanarVectors[i][1]; + + const Point3F &p0 = midpnt[axis0]; + const Point3F &p1 = midpnt[axis1]; + + bool selected0 = false; + bool selected1 = false; + + if ( i + 3 == mSelectionIdx ) + selected0 = selected1 = true; + + bool inactive = !mAxisEnabled[axis0] || !mAxisEnabled[axis1] || !(mProfile->flags & GizmoProfile::PlanarHandlesOn); + + if ( inactive ) + PrimBuild::color( mProfile->hideDisabledAxes ? ColorI::ZERO : mProfile->inActiveColor ); + else + PrimBuild::color( selected0 ? mProfile->activeColor : mProfile->axisColors[axis0] ); + + PrimBuild::vertex3fv( mOrigin + p0 ); + PrimBuild::vertex3fv( mOrigin + p0 + p1 ); + + if ( inactive ) + PrimBuild::color( mProfile->hideDisabledAxes ? ColorI::ZERO : mProfile->inActiveColor ); + else + PrimBuild::color( selected1 ? mProfile->activeColor : mProfile->axisColors[axis1] ); + + PrimBuild::vertex3fv( mOrigin + p1 ); + PrimBuild::vertex3fv( mOrigin + p0 + p1 ); + } + + PrimBuild::end(); + + // Render planar handle as solid if selected. + + ColorI planeColorSEL( mProfile->activeColor ); + planeColorSEL.alpha = 75; + ColorI planeColorNA( 0, 0, 0, 0 ); + + PrimBuild::begin( GFXTriangleList, 18 ); + + for(U32 i = 0; i < 3; i++) + { + U32 axis0 = sgPlanarVectors[i][0]; + U32 axis1 = sgPlanarVectors[i][1]; + + const Point3F &p0 = midpnt[axis0]; + const Point3F &p1 = midpnt[axis1]; + + if ( i + 3 == mSelectionIdx ) + PrimBuild::color( planeColorSEL ); + else + PrimBuild::color( planeColorNA ); + + PrimBuild::vertex3fv( mOrigin ); + PrimBuild::vertex3fv( mOrigin + p0 ); + PrimBuild::vertex3fv( mOrigin + p0 + p1 ); + + PrimBuild::vertex3fv( mOrigin ); + PrimBuild::vertex3fv( mOrigin + p0 + p1 ); + PrimBuild::vertex3fv( mOrigin + p1 ); + } + + PrimBuild::end(); + } + + // Render Centroid Handle... + if ( mUniformHandleEnabled ) + { + F32 tipScale = mProjLen * 0.075f; + GFXTransformSaver saver; + GFX->multWorld( mTransform ); + + if ( mSelectionIdx == Centroid || mHighlightAll || mHighlightCentroidHandle ) + PrimBuild::color( mProfile->centroidHighlightColor ); + else + PrimBuild::color( mProfile->centroidColor ); + + for(U32 j = 0; j < 6; j++) + { + PrimBuild::begin( GFXTriangleFan, 4 ); + + PrimBuild::vertex3fv( sgCenterBoxPnts[sgBoxVerts[j][0]] * tipScale); + PrimBuild::vertex3fv( sgCenterBoxPnts[sgBoxVerts[j][1]] * tipScale); + PrimBuild::vertex3fv( sgCenterBoxPnts[sgBoxVerts[j][2]] * tipScale); + PrimBuild::vertex3fv( sgCenterBoxPnts[sgBoxVerts[j][3]] * tipScale); + + PrimBuild::end(); + } + } +} + +void Gizmo::renderText( const RectI &viewPort, const MatrixF &modelView, const MatrixF &projection ) +{ + if ( mProfile->mode == NoneMode ) + return; + + if ( mMouseDown && !mProfile->renderWhenUsed ) + return; + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + + _setStateBlock(); + + char axisText[] = "xyz"; + + F32 projLen = mProjLen * 1.05f; + if ( mProfile->mode == RotateMode ) + projLen *= 0.28f; + + for ( U32 i = 0; i < 3; i++ ) + { + if ( !mAxisEnabled[i] && mProfile->hideDisabledAxes ) + continue; + + const Point3F & centroid = mOrigin; + Point3F pos(centroid.x + mProjAxisVector[i].x * projLen, + centroid.y + mProjAxisVector[i].y * projLen, + centroid.z + mProjAxisVector[i].z * projLen); + + Point3F sPos; + + if ( MathUtils::mProjectWorldToScreen( pos, &sPos, viewPort, modelView, projection ) ) + { + ColorI textColor = ColorI(170,170,170); + + if ( mProfile->mode == RotateMode ) + { + textColor.set(170,170,170); + if ( i == mSelectionIdx ) + textColor = mProfile->axisColors[i]; + } + else + { + if ( i == mSelectionIdx || !mAxisEnabled[i] ) + textColor = mProfile->inActiveColor; + else + textColor = mProfile->axisColors[i]; + } + + char buf[2]; + buf[0] = axisText[i]; buf[1] = '\0'; + drawer->setBitmapModulation(textColor); + drawer->drawText( mProfile->font, Point2I((S32)sPos.x, (S32)sPos.y), buf ); + } + } +} + +// Gizmo Internal Methods... + +void Gizmo::_calcAxisInfo() +{ + mOrigin = mTransform.getPosition(); + + for ( U32 i = 0; i < 3; i++ ) + { + VectorF tmp; + mTransform.mulV(sgAxisVectors[i], &tmp); + mProjAxisVector[i] = tmp; + mProjAxisVector[i].normalizeSafe(); + } + + // get the projected size... + + mProjLen = _getProjectionLength( mProfile->screenLen ); +} + +void Gizmo::_renderPrimaryAxis() +{ + // Render the primary axis(s) + for ( U32 i = 0; i < 3; i++ ) + { + ColorI color = mProfile->axisColors[i]; + + if ( !mAxisEnabled[i] ) + { + color = mProfile->inActiveColor; + if ( mProfile->hideDisabledAxes ) + color.alpha = 0; + } + else + { + if ( 0 <= mSelectionIdx && mSelectionIdx <= 2 ) + { + if ( i == mSelectionIdx ) + color = mProfile->activeColor; + } + else if ( 3 <= mSelectionIdx && mSelectionIdx <= 5 ) + { + if ( i == sgPlanarVectors[mSelectionIdx-3][0] || + i == sgPlanarVectors[mSelectionIdx-3][1] ) + color = mProfile->activeColor; + } + else if ( mSelectionIdx == 6 ) + color = mProfile->activeColor; + } + + if ( mHighlightAll ) + { + // Previous logic is complex so do this outside. + // Don't change the alpha calculated previously but override + // the color to the activeColor. + U8 saveAlpha = color.alpha; + color = mProfile->activeColor; + color.alpha = saveAlpha; + } + + PrimBuild::begin( GFXLineList, 2 ); + PrimBuild::color( color ); + PrimBuild::vertex3fv( mOrigin ); + PrimBuild::vertex3fv( mOrigin + mProjAxisVector[i] * mProjLen ); + PrimBuild::end(); + } +} + +void Gizmo::_renderAxisArrows() +{ + F32 tipScale = mProjLen * 0.25; + S32 x, y, z; + Point3F pnt; + + for ( U32 axisIdx = 0; axisIdx < 3; axisIdx++ ) + { + if ( mProfile->hideDisabledAxes && !mAxisEnabled[axisIdx] ) + continue; + + PrimBuild::begin( GFXTriangleList, 12*3 ); + + if ( !mAxisEnabled[axisIdx] ) + PrimBuild::color( mProfile->inActiveColor ); + else + PrimBuild::color( mProfile->axisColors[axisIdx] ); + + x = sgAxisRemap[axisIdx][0]; + y = sgAxisRemap[axisIdx][1]; + z = sgAxisRemap[axisIdx][2]; + + for ( U32 i = 0; i < sizeof(sgConeVerts) / (sizeof(U32)*3); ++i ) + { + const Point3F& conePnt0 = sgConePnts[sgConeVerts[i][0]]; + pnt.set(conePnt0[x], conePnt0[y], conePnt0[z]); + PrimBuild::vertex3fv(pnt * tipScale + sgAxisVectors[axisIdx] * mProjLen); + + const Point3F& conePnt1 = sgConePnts[sgConeVerts[i][1]]; + pnt.set(conePnt1[x], conePnt1[y], conePnt1[z]); + PrimBuild::vertex3fv(pnt * tipScale + sgAxisVectors[axisIdx] * mProjLen); + + const Point3F& conePnt2 = sgConePnts[sgConeVerts[i][2]]; + pnt.set(conePnt2[x], conePnt2[y], conePnt2[z]); + PrimBuild::vertex3fv(pnt * tipScale + sgAxisVectors[axisIdx] * mProjLen); + } + + PrimBuild::end(); + } +} + +void Gizmo::_renderAxisBoxes() +{ + if ( mProfile->hideDisabledAxes && !( mProfile->flags & GizmoProfile::CanScale ) ) + return; + + F32 tipScale = mProjLen * 0.1; + F32 pos = mProjLen - 0.5 * tipScale; + + for( U32 axisIdx = 0; axisIdx < 3; ++axisIdx ) + { + if ( mProfile->hideDisabledAxes && !( mProfile->flags & ( GizmoProfile::CanScaleX << axisIdx ) ) ) + continue; + + if ( mAxisEnabled[axisIdx] ) + PrimBuild::color( mProfile->axisColors[axisIdx] ); + else + PrimBuild::color( mProfile->inActiveColor ); + + for(U32 j = 0; j < 6; j++) + { + PrimBuild::begin( GFXTriangleFan, 4 ); + + PrimBuild::vertex3fv( sgCenterBoxPnts[sgBoxVerts[j][0]] * tipScale + sgAxisVectors[axisIdx] * pos ); + PrimBuild::vertex3fv( sgCenterBoxPnts[sgBoxVerts[j][1]] * tipScale + sgAxisVectors[axisIdx] * pos ); + PrimBuild::vertex3fv( sgCenterBoxPnts[sgBoxVerts[j][2]] * tipScale + sgAxisVectors[axisIdx] * pos ); + PrimBuild::vertex3fv( sgCenterBoxPnts[sgBoxVerts[j][3]] * tipScale + sgAxisVectors[axisIdx] * pos ); + + PrimBuild::end(); + } + } +} + +void Gizmo::_renderAxisCircles() +{ + if ( mProfile->hideDisabledAxes && !( mProfile->flags & GizmoProfile::CanRotate ) ) + return; + + // Setup the WorldMatrix for rendering in camera space. + // Honestly not sure exactly why this works but it does... + GFX->pushWorldMatrix(); + MatrixF cameraXfm = GFX->getWorldMatrix(); + cameraXfm.inverse(); + const Point3F cameraPos = cameraXfm.getPosition(); + cameraXfm.setPosition( mOrigin ); + GFX->multWorld(cameraXfm); + + // Render the ScreenSpace rotation circle... + if ( !( mProfile->hideDisabledAxes && !mScreenRotateHandleEnabled ) ) + { + F32 radius = mProjLen * 0.7f; + U32 segments = 40; + F32 step = mDegToRad(360.0f)/ segments; + Point3F pnt; + + PrimBuild::color( ( mHighlightAll || mSelectionIdx == Custom1 ) ? mProfile->activeColor : mProfile->inActiveColor ); + PrimBuild::begin( GFXLineStrip, segments+1 ); + + for(U32 i = 0; i <= segments; i++) + { + F32 angle = i * step; + + pnt.x = mCos(angle) * radius; + pnt.y = 0.0f; + pnt.z = mSin(angle) * radius; + + PrimBuild::vertex3fv( pnt ); + } + + PrimBuild::end(); + } + + // Render the gizmo/sphere bounding circle... + { + F32 radius = mProjLen * 0.5f; + U32 segments = 40; + F32 step = mDegToRad(360.0f) / segments; + Point3F pnt; + + // Render as solid (with transparency) when the sphere is selected + if ( mSelectionIdx == Custom2 ) + { + ColorI color = mProfile->inActiveColor; + color.alpha = 100; + PrimBuild::color( color ); + PrimBuild::begin( GFXTriangleFan, segments+2 ); + + PrimBuild::vertex3fv( Point3F(0,0,0) ); + + for(U32 i = 0; i <= segments; i++) + { + F32 angle = i * step; + + pnt.x = mCos(angle) * radius; + pnt.y = 0.0f; + pnt.z = mSin(angle) * radius; + + PrimBuild::vertex3fv( pnt ); + } + + PrimBuild::end(); + } + else + { + PrimBuild::color( mProfile->inActiveColor ); + PrimBuild::begin( GFXLineStrip, segments+1 ); + + for(U32 i = 0; i <= segments; i++) + { + F32 angle = i * step; + + pnt.x = mCos(angle) * radius; + pnt.y = 0.0f; + pnt.z = mSin(angle) * radius; + + PrimBuild::vertex3fv( pnt ); + } + + PrimBuild::end(); + } + } + + // Done rendering in camera space. + GFX->popWorldMatrix(); + + // Setup WorldMatrix for Gizmo-Space rendering. + GFX->pushWorldMatrix(); + GFX->multWorld(mTransform); + + // Render the axis-manipulation ellipses... + { + F32 radius = mProjLen * 0.5f; + U32 segments = 40; + F32 step = mDegToRad(360.0f) / segments; + U32 x,y,z; + + VectorF planeNormal; + planeNormal = mOrigin - cameraPos; + planeNormal.normalize(); + PlaneF clipPlane( mOrigin, planeNormal ); + + MatrixF worldToGizmo = mTransform; + worldToGizmo.inverse(); + mTransformPlane( worldToGizmo, Point3F(1,1,1), clipPlane, &clipPlane ); + + for ( U32 axis = 0; axis < 3; axis++ ) + { + if ( mProfile->hideDisabledAxes && !( mProfile->flags & ( GizmoProfile::CanRotateX << axis ) ) ) + continue; + + if ( mAxisEnabled[axis] || mHighlightAll ) + PrimBuild::color( (axis == mSelectionIdx) ? mProfile->activeColor : mProfile->axisColors[axis] ); + else + PrimBuild::color( mProfile->inActiveColor ); + + x = sgAxisRemap[axis][0]; + y = sgAxisRemap[axis][1]; + z = sgAxisRemap[axis][2]; + + PrimBuild::begin( GFXLineList, (segments+1) * 2 ); + + for ( U32 i = 1; i <= segments; i++ ) + { + F32 ang0 = (i-1) * step; + F32 ang1 = i * step; + + Point3F temp; + + temp.x = 0.0f; + temp.y = mCos(ang0) * radius; + temp.z = mSin(ang0) * radius; + Point3F pnt0( temp[x], temp[y], temp[z] ); + + temp.x = 0.0f; + temp.y = mCos(ang1) * radius; + temp.z = mSin(ang1) * radius; + Point3F pnt1( temp[x], temp[y], temp[z] ); + + bool valid0 = ( clipPlane.whichSide(pnt0) == PlaneF::Back ); + bool valid1 = ( clipPlane.whichSide(pnt1) == PlaneF::Back ); + + //if ( !valid0 && !valid1 ) + // continue; + + if ( !valid0 || !valid1 ) + continue; + + PrimBuild::vertex3fv( pnt0 ); + PrimBuild::vertex3fv( pnt1 ); + } + + PrimBuild::end(); + } + } + + // Done rendering in Gizmo-Space. + GFX->popWorldMatrix(); + + // Render hint-arrows... + /* + if ( mMouseDown && mSelectionIdx != -1 ) + { + PrimBuild::begin( GFXLineList, 4 ); + + F32 hintArrowScreenLength = mProfile->screenLen * 0.5f; + F32 hintArrowTipScreenLength = mProfile->screenLen * 0.25; + + F32 worldZDist = ( mMouseCollideLine.origin - mCameraPos ).len(); + F32 hintArrowLen = ( hintArrowScreenLength * worldZDist ) / mLastWorldToScreenScale.y; + F32 hintArrowTipLen = ( hintArrowTipScreenLength * worldZDist ) / mLastWorldToScreenScale.y; + + Point3F p0 = mMouseCollideLine.origin - mMouseCollideLine.direction * hintArrowLen; + Point3F p1 = mMouseCollideLine.origin; + Point3F p2 = mMouseCollideLine.origin + mMouseCollideLine.direction * hintArrowLen; + + // For whatever reason, the sign is actually negative if we are on the + // positive size of the MouseCollideLine direction. + ColorI color0 = ( mSign > 0.0f ) ? mProfile->activeColor : mProfile->inActiveColor; + ColorI color1 = ( mSign < 0.0f ) ? mProfile->activeColor : mProfile->inActiveColor; + + PrimBuild::color( color0 ); + PrimBuild::vertex3fv( p1 ); + PrimBuild::vertex3fv( p0 ); + + PrimBuild::color( color1 ); + PrimBuild::vertex3fv( p1 ); + PrimBuild::vertex3fv( p2 ); + PrimBuild::end(); + + GFXStateBlockDesc desc; + desc.setBlend( true ); + desc.setZReadWrite( false, false ); + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + drawer->drawCone( desc, p0, p0 - mMouseCollideLine.direction * hintArrowTipLen, hintArrowTipLen * 0.5f, color0 ); + drawer->drawCone( desc, p2, p2 + mMouseCollideLine.direction * hintArrowTipLen, hintArrowTipLen * 0.5f, color1 ); + } + */ +} + +void Gizmo::_renderPlane() +{ + if( !mGridPlaneEnabled ) + return; + + Point2F size( mProfile->planeDim, mProfile->planeDim ); + + GFXStateBlockDesc desc; + desc.setBlend( true ); + desc.setZReadWrite( true, false ); + + GFXTransformSaver saver; + GFX->multWorld( mTransform ); + + if ( mProfile->renderPlane ) + GFX->getDrawUtil()->drawSolidPlane( desc, Point3F::Zero, size, mProfile->gridColor ); + + if ( mProfile->renderPlaneHashes ) + { + // TODO: This wasn't specified before... so it was + // rendering lines that were invisible. Maybe we need + // a new field for grid line color? + ColorI gridColor( mProfile->gridColor ); + gridColor.alpha *= 2; + + GFX->getDrawUtil()->drawPlaneGrid( desc, Point3F::Zero, size, Point2F( mProfile->gridSize.x, mProfile->gridSize.y ), gridColor ); + } +} + + +void Gizmo::_setStateBlock() +{ + if ( !mStateBlock ) + { + GFXStateBlockDesc sb; + sb.blendDefined = true; + sb.blendEnable = true; + sb.blendSrc = GFXBlendSrcAlpha; + sb.blendDest = GFXBlendInvSrcAlpha; + sb.zDefined = true; + sb.zEnable = false; + sb.cullDefined = true; + sb.cullMode = GFXCullNone; + mStateBlock = GFX->createStateBlock(sb); + + sb.setZReadWrite( true, false ); + mSolidStateBlock = GFX->createStateBlock(sb); + } + + //if ( mProfile->renderSolid ) + // GFX->setStateBlock( mSolidStateBlock ); + //else + GFX->setStateBlock( mStateBlock ); +} + +Point3F Gizmo::_snapPoint( const Point3F &pnt ) const +{ + if ( !mProfile->snapToGrid ) + return pnt; + + Point3F snap; + snap.x = _snapFloat( pnt.x, mProfile->gridSize.x ); + snap.y = _snapFloat( pnt.y, mProfile->gridSize.y ); + snap.z = _snapFloat( pnt.z, mProfile->gridSize.z ); + + return snap; +} + +F32 Gizmo::_snapFloat( const F32 &val, const F32 &snap ) const +{ + if ( snap == 0.0f ) + return val; + + F32 a = mFmod( val, snap ); + + F32 temp = val; + + if ( mFabs(a) > (snap / 2) ) + val < 0.0f ? temp -= snap : temp += snap; + + return(temp - a); +} + +GizmoAlignment Gizmo::_filteredAlignment() +{ + GizmoAlignment align = mProfile->alignment; + + // Special case in ScaleMode, always be in object. + if ( mProfile->mode == ScaleMode ) + align = Object; + + return align; +} + +void Gizmo::_updateState( bool collideGizmo ) +{ + if ( !mProfile ) + return; + + // Update mCurrentMode + + if ( mCurrentMode != mProfile->mode ) + { + // Changing the mode invalidates the prior selection since the gizmo + // has changed shape. + + mCurrentMode = mProfile->mode; + mSelectionIdx = -1; + + // Determine the new selection unless we have been told not to. + if ( collideGizmo ) + collideAxisGizmo( mLastMouseEvent ); + + // Also cancel any current dragging operation since it would only be + // valid if the mouse down event occurred first. + + mMouseDown = false; + } + + // Update mCurrentAlignment + + // Changing the alignment during a drag could be really bad. + // Haven't actually tested this though. + if ( mMouseDown ) + return; + + GizmoAlignment desired = _filteredAlignment(); + + if ( desired == World && + mCurrentAlignment == Object ) + { + mObjectMat = mTransform; + mTransform.identity(); + mTransform.setPosition( mObjectMat.getPosition() ); + } + else if ( desired == Object && + mCurrentAlignment == World ) + { + Point3F pos = mTransform.getPosition(); + mTransform = mObjectMat; + mTransform.setPosition( pos ); + mObjectMat.identity(); + mObjectMat.setPosition( pos ); + } + + mCurrentAlignment = desired; + + mObjectMat.invertTo( &mObjectMatInv ); +} + +void Gizmo::_updateEnabledAxices() +{ + if ( ( mProfile->mode == ScaleMode && mProfile->flags & GizmoProfile::CanScaleUniform ) || + ( mProfile->mode == MoveMode && mProfile->flags & GizmoProfile::CanTranslateUniform ) || + ( mProfile->mode == RotateMode && mProfile->flags & GizmoProfile::CanRotateUniform ) ) + mUniformHandleEnabled = true; + else + mUniformHandleEnabled = false; + + // Screen / camera relative rotation disabled until it functions properly + // + //if ( mProfile->mode == RotateMode && mProfile->flags & GizmoProfile::CanRotateScreen ) + // mScreenRotateHandleEnabled = true; + //else + mScreenRotateHandleEnabled = false; + + // Early out if we are in a mode that is disabled. + if ( mProfile->mode == RotateMode && !(mProfile->flags & GizmoProfile::CanRotate ) ) + { + mAxisEnabled[0] = mAxisEnabled[1] = mAxisEnabled[2] = false; + return; + } + if ( mProfile->mode == MoveMode && !(mProfile->flags & GizmoProfile::CanTranslate ) ) + { + mAxisEnabled[0] = mAxisEnabled[1] = mAxisEnabled[2] = false; + return; + } + if ( mProfile->mode == ScaleMode && !(mProfile->flags & GizmoProfile::CanScale ) ) + { + mAxisEnabled[0] = mAxisEnabled[1] = mAxisEnabled[2] = false; + return; + } + + for ( U32 i = 0; i < 3; i++ ) + { + mAxisEnabled[i] = false; + + // Some tricky enum math... x/y/z are sequential in the enum + if ( ( mProfile->mode == RotateMode ) && + !( mProfile->flags & ( GizmoProfile::CanRotateX << i ) ) ) + continue; + if ( ( mProfile->mode == MoveMode ) && + !( mProfile->flags & ( GizmoProfile::CanTranslateX << i ) ) ) + continue; + if ( ( mProfile->mode == ScaleMode ) && + !( mProfile->flags & ( GizmoProfile::CanScaleX << i ) ) ) + continue; + + mAxisEnabled[i] = true; + } +} diff --git a/Engine/source/gui/worldEditor/gizmo.h b/Engine/source/gui/worldEditor/gizmo.h new file mode 100644 index 000000000..d33a9957a --- /dev/null +++ b/Engine/source/gui/worldEditor/gizmo.h @@ -0,0 +1,418 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GIZMO_H_ +#define _GIZMO_H_ + +#ifndef _SIMBASE_H_ +#include "console/simBase.h" +#endif + +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif + +#ifndef _COLOR_H_ +#include "core/color.h" +#endif + +#ifndef _GUITYPES_H_ +#include "gui/core/guiTypes.h" +#endif + +#ifndef _MATHUTILS_H_ +#include "math/mathUtils.h" +#endif + +#ifndef _DYNAMIC_CONSOLETYPES_H_ +#include "console/dynamicTypes.h" +#endif + + +enum GizmoMode +{ + NoneMode = 0, + MoveMode, // 1 + RotateMode, // 2 + ScaleMode, // 3 + ModeEnumCount +}; + +enum GizmoAlignment +{ + World = 0, + Object, + AlignEnumCount +}; + +DefineEnumType( GizmoMode ); +DefineEnumType( GizmoAlignment ); + + +// +class GizmoProfile : public SimObject +{ + typedef SimObject Parent; + +public: + + GizmoProfile(); + virtual ~GizmoProfile() {} + + DECLARE_CONOBJECT( GizmoProfile ); + + virtual bool onAdd(); + + static void initPersistFields(); + static void consoleInit(); + + /// Set flags to default values. + void restoreDefaultState(); + + // Data Fields + + GizmoMode mode; + GizmoAlignment alignment; + + F32 rotateScalar; + F32 scaleScalar; + U32 screenLen; + ColorI axisColors[3]; + ColorI activeColor; + ColorI inActiveColor; + ColorI centroidColor; + ColorI centroidHighlightColor; + Resource font; + + bool snapToGrid; + F32 scaleSnap; + bool allowSnapScale; + F32 rotationSnap; + bool allowSnapRotations; + + bool renderWhenUsed; + bool renderInfoText; + + Point3F gridSize; + bool renderPlane; + bool renderPlaneHashes; + ColorI gridColor; + F32 planeDim; + bool renderSolid; + + /// Whether to render a transparent grid overlay when using the move gizmo. + bool renderMoveGrid; + + enum Flags { + CanRotate = 1 << 0, // 0 + CanRotateX = 1 << 1, + CanRotateY = 1 << 2, + CanRotateZ = 1 << 3, + CanRotateScreen = 1 << 4, + CanRotateUniform = 1 << 5, + CanScale = 1 << 6, + CanScaleX = 1 << 7, + CanScaleY = 1 << 8, + CanScaleZ = 1 << 9, + CanScaleUniform = 1 << 10, + CanTranslate = 1 << 11, + CanTranslateX = 1 << 12, + CanTranslateY = 1 << 13, + CanTranslateZ = 1 << 14, + CanTranslateUniform = 1 << 15, + PlanarHandlesOn = 1 << 16 + }; + + S32 flags; + + bool hideDisabledAxes; + + bool allAxesScaleUniform; +}; + + +// This class contains code for rendering and manipulating a 3D gizmo, it +// is usually used as a helper within a TSEdit-derived control. +// +// The Gizmo has a MatrixF transform and Point3F scale on which it will +// operate by passing it Gui3DMouseEvent(s). +// +// The idea is to set the Gizmo transform/scale to that of another 3D object +// which is being manipulated, pass mouse events into the Gizmo, read the +// new transform/scale out, and set it to onto the object. +// And of course the Gizmo can be rendered. +// +// Gizmo derives from SimObject only because this allows its properties +// to be initialized directly from script via fields. + +class Gizmo : public SimObject +{ + typedef SimObject Parent; + + friend class WorldEditor; + +public: + + enum Selection { + None = -1, + Axis_X = 0, + Axis_Y = 1, + Axis_Z = 2, + Plane_XY = 3, // Normal = Axis_Z + Plane_XZ = 4, // Normal = Axis_Y + Plane_YZ = 5, // Normal = Axis_X + Centroid = 6, + Custom1 = 7, // screen-aligned rotation + Custom2 = 8 + }; + + Gizmo(); + ~Gizmo(); + + DECLARE_CONOBJECT( Gizmo ); + + // SimObject + bool onAdd(); + void onRemove(); + static void initPersistFields(); + + // Mutators + void set( const MatrixF &objMat, const Point3F &worldPos, const Point3F &objScale ); + void setProfile( GizmoProfile *profile ) + { + AssertFatal( profile != NULL, "NULL passed to Gizmo::setProfile - Gizmo must always have a profile!" ); + mProfile = profile; + } + + // Accessors + + GizmoProfile* getProfile() { return mProfile; } + + GizmoMode getMode() const { return mCurrentMode; } + + GizmoAlignment getAlignment() const { return mCurrentAlignment; } + + /// Returns current object to world transform of the object being manipulated. + const MatrixF& getTransform() const { return mCurrentTransform; } + + Point3F getPosition() const { return mCurrentTransform.getPosition(); } + + const Point3F& getScale() const { return mScale; } + + + // Returns change in position in last call to on3DMouseDragged. + const Point3F& getOffset() const { return mDeltaPos; } + + // Returns change is position since on3DMouseDown. + const Point3F& getTotalOffset() const { return mDeltaTotalPos; } + + const Point3F& getDeltaScale() const { return mDeltaScale; } + + const Point3F& getDeltaTotalScale() const { return mDeltaTotalScale; } + + const Point3F& getDeltaRot() const { return mDeltaRot; } + + const Point3F& getDeltaTotalRot() const { return mDeltaTotalRot; } + + /// Set whether to render the grid plane. + void setGridPlaneEnabled( bool value ) { mGridPlaneEnabled = value; } + + /// Set whether to a transparent grid overlay when using the move gizmo. + void setMoveGridEnabled( bool value ) { mMoveGridEnabled = value; } + + /// Set the size of the move grid along one dimension. The total size of the + /// move grid is @a value * @a value. + void setMoveGridSize( F32 value ) { mMoveGridSize = value; } + + /// Set the spacing between grid lines on the move grid. + void setMoveGridSpacing( F32 value ) { mMoveGridSpacing = value; } + + // Gizmo Interface methods... + + // Set the current highlight mode on the gizmo's centroid handle + void setCentroidHandleHighlight( bool state ) { mHighlightCentroidHandle = state; } + + // Must be called before on3DMouseDragged to save state + void on3DMouseDown( const Gui3DMouseEvent &event ); + + // So Gizmo knows the current mouse button state. + void on3DMouseUp( const Gui3DMouseEvent &event ); + + // Test Gizmo for collisions and set the Gizmo Selection (the part under the cursor) + void on3DMouseMove( const Gui3DMouseEvent &event ); + + // Make changes to the Gizmo transform/scale (depending on mode) + void on3DMouseDragged( const Gui3DMouseEvent &event ); + + // Returns an enum describing the part of the Gizmo that is Selected + // ( under the cursor ). This should be called AFTER calling onMouseMove + // or collideAxisGizmo + // + // -1 None + // 0 Axis_X + // 1 Axis_Y + // 2 Axis_Z + // 3 Plane_XY + // 4 Plane_XZ + // 5 Plane_YZ + Selection getSelection(); + void setSelection( Selection sel ) { mSelectionIdx = sel; } + + // Returns the object space vector corresponding to a Selection. + Point3F selectionToAxisVector( Selection axis ); + + // These provide the user an easy way to check if the Gizmo's transform + // or scale have changed by calling markClean prior to calling + // on3DMouseDragged, and calling isDirty after. + bool isDirty() { return mDirty; } + void markClean() { mDirty = false; } + + // Renders the 3D Gizmo in the scene, GFX must be setup for proper + // 3D rendering before calling this! + // Calling this will change the GFXStateBlock! + void renderGizmo( const MatrixF &cameraTransform, F32 camerFOV = 1.5f ); + + // Renders text associated with the Gizmo, GFX must be setup for proper + // 2D rendering before calling this! + // Calling this will change the GFXStateBlock! + void renderText( const RectI &viewPort, const MatrixF &modelView, const MatrixF &projection ); + + // Returns true if the mouse event collides with any part of the Gizmo + // and sets the Gizmo's current Selection. + // You can call this or on3DMouseMove, they are identical + bool collideAxisGizmo( const Gui3DMouseEvent & event ); + +protected: + + void _calcAxisInfo(); + void _setStateBlock(); + void _renderPrimaryAxis(); + void _renderAxisArrows(); + void _renderAxisBoxes(); + void _renderAxisCircles(); + void _renderAxisText(); + void _renderPlane(); + Point3F _snapPoint( const Point3F &pnt ) const; + F32 _snapFloat( const F32 &val, const F32 &snap ) const; + GizmoAlignment _filteredAlignment(); + void _updateState( bool collideGizmo = true ); + void _updateEnabledAxices(); + + F32 _getProjectionLength( F32 dist ) const + { + if( GFX->isFrustumOrtho() ) + return mLastCameraFOV * dist * 0.002f; + else + { + Point3F dir = mOrigin - mCameraPos; + return ( dist * dir.len() ) / mLastWorldToScreenScale.y; + } + } + +protected: + + GizmoProfile *mProfile; + + MatrixF mObjectMat; + MatrixF mObjectMatInv; + MatrixF mTransform; + MatrixF mCurrentTransform; + MatrixF mSavedTransform; + + GizmoAlignment mCurrentAlignment; + GizmoMode mCurrentMode; + + MatrixF mCameraMat; + Point3F mCameraPos; + + Point3F mScale; + Point3F mSavedScale; + Point3F mDeltaScale; + Point3F mDeltaTotalScale; + Point3F mLastScale; + Point3F mScaleInfluence; + + EulerF mRot; + EulerF mSavedRot; + EulerF mDeltaRot; + EulerF mDeltaTotalRot; + F32 mDeltaAngle; + F32 mLastAngle; + Point2I mMouseDownPos; + Point3F mMouseDownProjPnt; + Point3F mDeltaPos; + Point3F mDeltaTotalPos; + Point3F mProjPnt; + Point3F mOrigin; + Point3F mProjAxisVector[3]; + F32 mProjLen; + S32 mSelectionIdx; + bool mDirty; + Gui3DMouseEvent mLastMouseEvent; + GFXStateBlockRef mStateBlock; + GFXStateBlockRef mSolidStateBlock; + + PlaneF mMouseCollidePlane; + MathUtils::Line mMouseCollideLine; + + bool mMouseDown; + + F32 mSign; + + /// If false, don't render the grid plane even if it is enabled in the profile. + bool mGridPlaneEnabled; + + /// If false, don't render a transparent grid overlay when using the move gizmo. + bool mMoveGridEnabled; + + /// Size of the move grid along one dimension. + F32 mMoveGridSize; + + /// Spacing between grid lines on the move grid. + U32 mMoveGridSpacing; + + bool mAxisEnabled[3]; + bool mUniformHandleEnabled; + bool mScreenRotateHandleEnabled; + + // Used to override rendering of handles. + bool mHighlightCentroidHandle; + bool mHighlightAll; + + // Initialized in renderGizmo and saved for later use when projecting + // to screen space for selection testing. + MatrixF mLastWorldMat; + MatrixF mLastProjMat; + RectI mLastViewport; + Point2F mLastWorldToScreenScale; + F32 mLastCameraFOV; + + // Screenspace cursor collision information used in rotation mode. + Point3F mElipseCursorCollidePntSS; + Point3F mElipseCursorCollideVecSS; + + /// A large hard coded distance used to test + /// gizmo axis selection. + static F32 smProjectDistance; +}; + +#endif // _GIZMO_H_ \ No newline at end of file diff --git a/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.cpp b/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.cpp new file mode 100644 index 000000000..f0d41f423 --- /dev/null +++ b/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.cpp @@ -0,0 +1,2214 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/worldEditor/guiConvexShapeEditorCtrl.h" + +#include "console/consoleTypes.h" +#include "T3D/convexShape.h" +#include "renderInstance/renderPassManager.h" +#include "collision/collision.h" +#include "math/util/frustum.h" +#include "math/mathUtils.h" +#include "gfx/gfxPrimitiveBuffer.h" +#include "gfx/gfxTextureHandle.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxDrawUtil.h" +#include "scene/sceneRenderState.h" +#include "scene/sceneManager.h" +#include "gui/core/guiCanvas.h" +#include "gui/buttons/guiButtonCtrl.h" +#include "gui/worldEditor/undoActions.h" +#include "T3D/gameBase/gameConnection.h" +#include "gfx/sim/debugDraw.h" +#include "collision/optimizedPolyList.h" +#include "core/volume.h" +#include "gui/worldEditor/worldEditor.h" +#include "T3D/prefab.h" + +IMPLEMENT_CONOBJECT( GuiConvexEditorCtrl ); + +ConsoleDocClass( GuiConvexEditorCtrl, + "@brief The base class for the sketch tool\n\n" + "Editor use only.\n\n" + "@internal" +); + + +GuiConvexEditorCtrl::GuiConvexEditorCtrl() + : mIsDirty( false ), + mFaceHL( -1 ), + mFaceSEL( -1 ), + mFaceSavedXfm( true ), + mSavedUndo( false ), + mDragging( false ), + mGizmoMatOffset( Point3F::Zero ), + mPivotPos( Point3F::Zero ), + mUsingPivot( false ), + mSettingPivot( false ), + mActiveTool( NULL ), + mCreateTool( NULL ), + mMouseDown( false ), + mUndoManager( NULL ), + mLastUndo( NULL ), + mHasCopied( false ), + mSavedGizmoFlags( -1 ), + mCtrlDown( false ) +{ + mMaterialName = StringTable->insert("Grid512_OrangeLines_Mat"); +} + +GuiConvexEditorCtrl::~GuiConvexEditorCtrl() +{ +} + +bool GuiConvexEditorCtrl::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + SceneManager::getPreRenderSignal().notify( this, &GuiConvexEditorCtrl::_prepRenderImage ); + + mCreateTool = new ConvexEditorCreateTool( this ); + + return true; +} + +void GuiConvexEditorCtrl::onRemove() +{ + SceneManager::getPreRenderSignal().remove( this, &GuiConvexEditorCtrl::_prepRenderImage ); + + SAFE_DELETE( mCreateTool ); + + Parent::onRemove(); +} + +void GuiConvexEditorCtrl::initPersistFields() +{ + addField( "isDirty", TypeBool, Offset( mIsDirty, GuiConvexEditorCtrl ) ); + addField( "materialName", TypeString, Offset(mMaterialName, GuiConvexEditorCtrl) ); + + Parent::initPersistFields(); +} + +bool GuiConvexEditorCtrl::onWake() +{ + if ( !Parent::onWake() ) + return false; + + SimGroup *missionGroup; + if ( !Sim::findObject( "MissionGroup", missionGroup ) ) + return true; + + SimGroup::iterator itr = missionGroup->begin(); + for ( ; itr != missionGroup->end(); itr++ ) + { + if ( dStrcmp( (*itr)->getClassName(), "ConvexShape" ) == 0 ) + { + mConvexSEL = static_cast( *itr ); + mGizmo->set( mConvexSEL->getTransform(), mConvexSEL->getPosition(), mConvexSEL->getScale() ); + return true; + } + } + + return true; +} + +void GuiConvexEditorCtrl::onSleep() +{ + Parent::onSleep(); + + mConvexSEL = NULL; + mConvexHL = NULL; +} + +void GuiConvexEditorCtrl::setVisible( bool val ) +{ + //ConvexShape::smRenderEdges = value; + + if ( isProperlyAdded() ) + { + if ( !val ) + { + mFaceHL = -1; + mConvexHL = NULL; + + setSelection( NULL, -1 ); + + if ( mSavedGizmoFlags != -1 ) + { + mGizmoProfile->flags = mSavedGizmoFlags; + mSavedGizmoFlags = -1; + } + } + else + { + mConvexHL = NULL; + mFaceHL = -1; + + setSelection( NULL, -1 ); + + WorldEditor *wedit; + if ( Sim::findObject( "EWorldEditor", wedit ) ) + { + S32 count = wedit->getSelectionSize(); + for ( S32 i = 0; i < count; i++ ) + { + S32 objId = wedit->getSelectObject(i); + ConvexShape *pShape; + if ( Sim::findObject( objId, pShape ) ) + { + mConvexSEL = pShape; + wedit->clearSelection(); + wedit->selectObject( String::ToString("%i",objId) ); + break; + } + } + } + updateGizmoPos(); + mSavedGizmoFlags = mGizmoProfile->flags; + } + } + + Parent::setVisible( val ); +} + +void GuiConvexEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event) +{ + mouseLock(); + + mMouseDown = true; + + if ( event.modifier & SI_ALT ) + { + setActiveTool( mCreateTool ); + mActiveTool->on3DMouseDown( event ); + return; + } + + if ( mConvexSEL && isShapeValid( mConvexSEL ) ) + mLastValidShape = mConvexSEL->mSurfaces; + + if ( mConvexSEL && + mFaceSEL != -1 && + mGizmo->getMode() == RotateMode && + mGizmo->getSelection() == Gizmo::Centroid ) + { + mSettingPivot = true; + mSavedPivotPos = mGizmo->getPosition(); + setPivotPos( mConvexSEL, mFaceSEL, event ); + updateGizmoPos(); + return; + } + + mGizmo->on3DMouseDown( event ); +} + +void GuiConvexEditorCtrl::on3DRightMouseDown(const Gui3DMouseEvent & event) +{ + return; + + /* + if ( mConvexSEL && mFaceSEL != -1 && mFaceSEL == mFaceHL ) + { + _submitUndo( "Split ConvexShape face." ); + + const MatrixF &surf = mConvexSEL->mSurfaces[mFaceSEL]; + + MatrixF newSurf( surf ); + + MatrixF rotMat( EulerF( 0.0f, mDegToRad( 2.0f ), 0.0f ) ); + + newSurf *= rotMat; + + mConvexSEL->mSurfaces.insert( mFaceSEL+1, newSurf ); + } + */ +} + +void GuiConvexEditorCtrl::on3DRightMouseUp(const Gui3DMouseEvent & event) +{ + //ConvexShape *hitShape; + //S32 hitFace; + //bool hit = _cursorCast( event, &hitShape, &hitFace ); + //Con::printf( hit ? "HIT" : "MISS" ); +} + +void GuiConvexEditorCtrl::on3DMouseUp(const Gui3DMouseEvent & event) +{ + mouseUnlock(); + + mMouseDown = false; + + mHasCopied = false; + mHasGeometry = false; + + if ( mActiveTool ) + { + ConvexEditorTool::EventResult result = mActiveTool->on3DMouseUp( event ); + + if ( result == ConvexEditorTool::Done ) + setActiveTool( NULL ); + + return; + } + + if ( !mSettingPivot && !mDragging && ( mGizmo->getSelection() == Gizmo::None || !mConvexSEL ) ) + { + if ( mConvexSEL != mConvexHL ) + { + setSelection( mConvexHL, -1 ); + } + else + { + if ( mFaceSEL != mFaceHL ) + setSelection( mConvexSEL, mFaceHL ); + else + setSelection( mConvexSEL, -1 ); + } + + mUsingPivot = false; + } + + mSettingPivot = false; + mSavedPivotPos = mGizmo->getPosition(); + mSavedUndo = false; + + mGizmo->on3DMouseUp( event ); + + if ( mDragging ) + { + mDragging = false; + + if ( mConvexSEL ) + { + Vector< U32 > removedPlanes; + mConvexSEL->cullEmptyPlanes( &removedPlanes ); + + // If a face has been removed we need to validate / remap + // our selected and highlighted faces. + if ( !removedPlanes.empty() ) + { + S32 prevFaceHL = mFaceHL; + S32 prevFaceSEL = mFaceSEL; + + if ( removedPlanes.contains( mFaceHL ) ) + prevFaceHL = mFaceHL = -1; + if ( removedPlanes.contains( mFaceSEL ) ) + prevFaceSEL = mFaceSEL = -1; + + for ( S32 i = 0; i < removedPlanes.size(); i++ ) + { + if ( (S32)removedPlanes[i] < prevFaceSEL ) + mFaceSEL--; + if ( (S32)removedPlanes[i] < prevFaceHL ) + mFaceHL--; + } + + setSelection( mConvexSEL, mFaceSEL ); + + // We need to reindex faces. + updateShape( mConvexSEL ); + } + } + } + + updateGizmoPos(); +} + +void GuiConvexEditorCtrl::on3DMouseMove(const Gui3DMouseEvent & event) +{ + if ( mActiveTool ) + { + // If we have an active tool pass this event to it. + // If it handled it, consume the event. + if ( mActiveTool->on3DMouseMove( event ) ) + return; + } + + ConvexShape *hitShape = NULL; + S32 hitFace = -1; + + _cursorCast( event, &hitShape, &hitFace ); + + if ( !mConvexSEL ) + { + mConvexHL = hitShape; + mFaceHL = -1; + } + else + { + if ( mConvexSEL == hitShape ) + { + mConvexHL = hitShape; + mFaceHL = hitFace; + } + else + { + // Mousing over a shape that is not the one currently selected. + + if ( mFaceSEL != -1 ) + { + mFaceHL = -1; + } + else + { + mConvexHL = hitShape; + mFaceHL = -1; + } + } + } + + if ( mConvexSEL ) + mGizmo->on3DMouseMove( event ); +} + +void GuiConvexEditorCtrl::on3DMouseDragged(const Gui3DMouseEvent & event) +{ + if ( mActiveTool ) + { + // If we have an active tool pass this event to it. + // If it handled it, consume the event. + if ( mActiveTool->on3DMouseDragged( event ) ) + return; + } + + //mGizmoProfile->rotateScalar = 0.55f; + //mGizmoProfile->scaleScalar = 0.55f; + + if ( !mConvexSEL ) + return; + + if ( mGizmo->getMode() == RotateMode && + mGizmo->getSelection() == Gizmo::Centroid ) + { + setPivotPos( mConvexSEL, mFaceSEL, event ); + mDragging = true; + return; + } + + mGizmo->on3DMouseDragged( event ); + + if ( event.modifier & SI_SHIFT && + ( mGizmo->getMode() == MoveMode || mGizmo->getMode() == RotateMode ) && + !mHasCopied ) + { + if ( mFaceSEL != -1 ) + { + ConvexShape *newShape = mCreateTool->extrudeShapeFromFace( mConvexSEL, mFaceSEL ); + //newShape->_updateGeometry(); + + submitUndo( CreateShape, newShape ); + setSelection( newShape, 0 ); + updateGizmoPos(); + + mGizmo->on3DMouseDown( event ); + + mHasCopied = true; + mSavedUndo = true; + } + else + { + ConvexShape *newShape = new ConvexShape(); + newShape->setTransform( mConvexSEL->getTransform() ); + newShape->setScale( mConvexSEL->getScale() ); + newShape->mSurfaces.clear(); + newShape->mSurfaces.merge( mConvexSEL->mSurfaces ); + + setupShape( newShape ); + + submitUndo( CreateShape, newShape ); + + setSelection( newShape, -1 ); + + updateGizmoPos(); + + mHasCopied = true; + mSavedUndo = true; + } + + return; + } + + if ( mGizmo->getMode() == RotateMode && + event.modifier & SI_CTRL && + !mHasCopied && + mFaceSEL != -1 ) + { + // Can must verify that splitting the face at the current angle + // ( of the gizmo ) will generate a valid shape. If not enough rotation + // has occurred we will have two faces that are coplanar and must wait + // until later in the drag to perform the split. + + //AssertFatal( isShapeValid( mConvexSEL ), "Shape was already invalid at beginning of split operation." ); + + if ( !isShapeValid( mConvexSEL ) ) + return; + + mLastValidShape = mConvexSEL->mSurfaces; + + Point3F rot = mGizmo->getDeltaTotalRot(); + rot.normalize(); + rot *= mDegToRad( 10.0f ); + + MatrixF rotMat( (EulerF)rot ); + + MatrixF worldToObj( mConvexSEL->getTransform() ); + worldToObj.scale( mConvexSEL->getScale() ); + worldToObj.inverse(); + + mConvexSEL->mSurfaces.increment(); + MatrixF &newSurf = mConvexSEL->mSurfaces.last(); + newSurf = mConvexSEL->mSurfaces[mFaceSEL] * rotMat; + + //worldToObj.mul( mGizmo->getTransform() ); + //Point3F pos( mPivotPos ); + //worldToObj.mulP( pos ); + //newSurf.setPosition( pos ); + + updateShape( mConvexSEL ); + + if ( !isShapeValid( mConvexSEL ) ) + { + mConvexSEL->mSurfaces = mLastValidShape; + updateShape( mConvexSEL ); + } + else + { + mHasCopied = true; + mSavedUndo = true; + + mLastValidShape = mConvexSEL->mSurfaces; + + submitUndo( ModifyShape, mConvexSEL ); + + setSelection( mConvexSEL, mConvexSEL->mSurfaces.size() - 1 ); + + updateGizmoPos(); + } + + return; + } + + // If we are dragging, but no gizmo selection... + // Then treat this like a regular mouse move, update the highlighted + // convex/face under the cursor and handle onMouseUp as we normally would + // to change the selection. + if ( mGizmo->getSelection() == Gizmo::None ) + { + ConvexShape *hitShape = NULL; + S32 hitFace = -1; + + _cursorCast( event, &hitShape, &hitFace ); + mFaceHL = hitFace; + mConvexHL = hitShape; + + return; + } + + mDragging = true; + + // Manipulating a face. + + if ( mFaceSEL != -1 ) + { + if ( !mSavedUndo ) + { + mSavedUndo = true; + submitUndo( ModifyShape, mConvexSEL ); + } + + if ( mGizmo->getMode() == ScaleMode ) + { + scaleFace( mConvexSEL, mFaceSEL, mGizmo->getScale() ); + } + else + { + // Why does this have to be so ugly. + if ( mGizmo->getMode() == RotateMode || + ( mGizmo->getMode() == MoveMode && + ( event.modifier & SI_CTRL || + ( mGizmo->getSelection() == Gizmo::Axis_Z && mHasCopied ) + ) + ) + ) + { + const MatrixF &gMat = mGizmo->getTransform(); + MatrixF surfMat; + surfMat.mul( mConvexSEL->mWorldToObj, gMat ); + + MatrixF worldToObj ( mConvexSEL->getTransform() ); + worldToObj.scale( mConvexSEL->getScale() ); + worldToObj.inverse(); + + Point3F newPos; + newPos = gMat.getPosition(); + + worldToObj.mulP( newPos ); + surfMat.setPosition( newPos ); + + // Clear out floating point errors. + cleanMatrix( surfMat ); + + mConvexSEL->mSurfaces[mFaceSEL] = surfMat; + + updateShape( mConvexSEL, mFaceSEL ); + } + else + { + // Translating a face in x/y/z + + translateFace( mConvexSEL, mFaceSEL, mGizmo->getTotalOffset() ); + } + } + + if ( isShapeValid( mConvexSEL ) ) + { + AssertFatal( mConvexSEL->mSurfaces.size() > mFaceSEL, "mFaceSEL out of range." ); + mLastValidShape = mConvexSEL->mSurfaces; + } + else + { + AssertFatal( mLastValidShape.size() > mFaceSEL, "mFaceSEL out of range." ); + mConvexSEL->mSurfaces = mLastValidShape; + updateShape( mConvexSEL ); + } + + return; + } + + // Manipulating a whole Convex. + + if ( !mSavedUndo ) + { + mSavedUndo = true; + submitUndo( ModifyShape, mConvexSEL ); + } + + if ( mGizmo->getMode() == MoveMode ) + { + mConvexSEL->setPosition( mGizmo->getPosition() ); + } + else if ( mGizmo->getMode() == RotateMode ) + { + mConvexSEL->setTransform( mGizmo->getTransform() ); + } + else + { + mConvexSEL->setScale( mGizmo->getScale() ); + } + + if ( mConvexSEL->getClientObject() ) + { + ConvexShape *clientObj = static_cast< ConvexShape* >( mConvexSEL->getClientObject() ); + clientObj->setTransform( mConvexSEL->getTransform() ); + clientObj->setScale( mConvexSEL->getScale() ); + } +} + +void GuiConvexEditorCtrl::on3DMouseEnter(const Gui3DMouseEvent & event) +{ + +} + +void GuiConvexEditorCtrl::on3DMouseLeave(const Gui3DMouseEvent & event) +{ + +} + +bool GuiConvexEditorCtrl::onKeyDown( const GuiEvent &evt ) +{ + bool handled = false; + + switch ( evt.keyCode ) + { + case KEY_ESCAPE: + handled = handleEscape(); + break; + case KEY_A: + if ( evt.modifier & SI_ALT ) + { + GizmoAlignment align = mGizmo->getProfile()->alignment; + if ( align == World ) + mGizmo->getProfile()->alignment = Object; + else + mGizmo->getProfile()->alignment = World; + handled = true; + } + break; + case KEY_LCONTROL: + //mCtrlDown = true; + break; + default: + break; + } + + return handled; +} + +bool GuiConvexEditorCtrl::onKeyUp( const GuiEvent &evt ) +{ + bool handled = false; + + switch ( evt.keyCode ) + { + case KEY_LCONTROL: + //mCtrlDown = false; + break; + default: + break; + } + + return handled; +} + +void GuiConvexEditorCtrl::get3DCursor( GuiCursor *&cursor, + bool &visible, + const Gui3DMouseEvent &event_ ) +{ + //cursor = mAddNodeCursor; + //visible = false; + + cursor = NULL; + visible = false; + + GuiCanvas *root = getRoot(); + if ( !root ) + return; + + S32 currCursor = PlatformCursorController::curArrow; + + if ( root->mCursorChanged == currCursor ) + return; + + PlatformWindow *window = root->getPlatformWindow(); + PlatformCursorController *controller = window->getCursorController(); + + // We've already changed the cursor, + // so set it back before we change it again. + if( root->mCursorChanged != -1) + controller->popCursor(); + + // Now change the cursor shape + controller->pushCursor(currCursor); + root->mCursorChanged = currCursor; +} + +void GuiConvexEditorCtrl::updateGizmo() +{ + mGizmoProfile->restoreDefaultState(); + + const GizmoMode &mode = mGizmoProfile->mode; + S32 &flags = mGizmoProfile->flags; + GizmoAlignment &align = mGizmoProfile->alignment; + + U8 keys = Input::getModifierKeys(); + + mCtrlDown = keys & ( SI_LCTRL | SI_LSHIFT ); + + bool altDown = keys & ( SI_LALT ); + + if ( altDown ) + { + flags = 0; + return; + } + + if ( mFaceSEL != -1 ) + { + align = Object; + flags |= GizmoProfile::CanRotateUniform; + flags &= ~GizmoProfile::CanRotateScreen; + } + else + { + flags &= ~GizmoProfile::CanRotateUniform; + flags |= GizmoProfile::CanRotateScreen; + } + + if ( mFaceSEL != -1 && mode == ScaleMode ) + flags &= ~GizmoProfile::CanScaleZ; + else + flags |= GizmoProfile::CanScaleZ; + + if ( mFaceSEL != -1 && mode == MoveMode ) + { + if ( mCtrlDown ) + flags &= ~( GizmoProfile::CanTranslateX | GizmoProfile::CanTranslateY | GizmoProfile::PlanarHandlesOn ); + else + flags |= ( GizmoProfile::CanTranslateX | GizmoProfile::CanTranslateY | GizmoProfile::PlanarHandlesOn ); + } +} + +void GuiConvexEditorCtrl::renderScene(const RectI & updateRect) +{ + // Synch selected ConvexShape with the WorldEditor. + + WorldEditor *wedit; + if ( Sim::findObject( "EWorldEditor", wedit) ) + { + S32 count = wedit->getSelectionSize(); + + if ( !mConvexSEL && count != 0 ) + wedit->clearSelection(); + else if ( mConvexSEL && count != 1 ) + { + wedit->clearSelection(); + wedit->selectObject( mConvexSEL->getIdString() ); + } + else if ( mConvexSEL && count == 1 ) + { + if ( wedit->getSelectObject(0) != mConvexSEL->getId() ) + { + wedit->clearSelection(); + wedit->selectObject( mConvexSEL->getIdString() ); + } + } + } + + // Update status bar text. + + SimObject *statusbar; + if ( Sim::findObject( "EditorGuiStatusBar", statusbar ) ) + { + String text( "Sketch Tool." ); + GizmoMode mode = mGizmo->getMode(); + + if ( mMouseDown && mGizmo->getSelection() != Gizmo::None && mConvexSEL ) + { + Point3F delta; + String qualifier; + + if ( mode == RotateMode ) + { + if ( mSettingPivot ) + delta = mGizmo->getPosition() - mSavedPivotPos; + else + delta = mGizmo->getDeltaTotalRot(); + } + else if ( mode == MoveMode ) + delta = mGizmo->getTotalOffset(); + else if ( mode == ScaleMode ) + delta = mGizmo->getDeltaTotalScale(); + + if ( mGizmo->getAlignment() == Object && + mode != ScaleMode ) + { + mConvexSEL->mWorldToObj.mulV( delta ); + if ( mFaceSEL != -1 && mode != RotateMode ) + { + MatrixF objToSurf( mConvexSEL->mSurfaces[ mFaceSEL ] ); + objToSurf.scale( mConvexSEL->getScale() ); + objToSurf.inverse(); + objToSurf.mulV( delta ); + } + } + + if ( mIsZero( delta.x, 0.0001f ) ) + delta.x = 0.0f; + if ( mIsZero( delta.y, 0.0001f ) ) + delta.y = 0.0f; + if ( mIsZero( delta.z, 0.0001f ) ) + delta.z = 0.0f; + + if ( mode == RotateMode ) + { + if ( mSettingPivot ) + text = String::ToString( "Delta position ( x: %4.2f, y: %4.2f, z: %4.2f ).", delta.x, delta.y, delta.z ); + else + { + delta.x = mRadToDeg( delta.x ); + delta.y = mRadToDeg( delta.y ); + delta.z = mRadToDeg( delta.z ); + text = String::ToString( "Delta angle ( x: %4.2f, y: %4.2f, z: %4.2f ).", delta.x, delta.y, delta.z ); + } + } + else if ( mode == MoveMode ) + text = String::ToString( "Delta position ( x: %4.2f, y: %4.2f, z: %4.2f ).", delta.x, delta.y, delta.z ); + else if ( mode == ScaleMode ) + text = String::ToString( "Delta scale ( x: %4.2f, y: %4.2f, z: %4.2f ).", delta.x, delta.y, delta.z ); + } + else + { + if ( !mConvexSEL ) + text = "Sketch Tool. ALT + Click-Drag to create a new ConvexShape."; + else if ( mFaceSEL == -1 ) + { + if ( mode == MoveMode ) + text = "Move selection. SHIFT while dragging duplicates objects."; + else if ( mode == RotateMode ) + text = "Rotate selection."; + else if ( mode == ScaleMode ) + text = "Scale selection."; + } + else + { + if ( mode == MoveMode ) + text = "Move face. SHIFT while beginning a drag EXTRUDES a new convex. Press CTRL for alternate translation mode."; + else if ( mode == RotateMode ) + text = "Rotate face. Gizmo/Pivot is draggable. CTRL while dragging splits/folds a new face. SHIFT while dragging extrudes a new convex."; + else if ( mode == ScaleMode ) + text = "Scale face."; + } + } + + Con::executef( statusbar, "setInfo", text.c_str() ); + + Con::executef( statusbar, "setSelectionObjectsByCount", Con::getIntArg( mConvexSEL == NULL ? 0 : 1 ) ); + } + + if ( mActiveTool ) + mActiveTool->renderScene( updateRect ); + + ColorI colorHL( 255, 50, 255, 255 ); + ColorI colorSEL( 255, 50, 255, 255 ); + ColorI colorNA( 255, 255, 255, 100 ); + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + + if ( mConvexSEL && !mDragging ) + { + if ( mFaceSEL == -1 ) + { + GFXStateBlockDesc desc; + desc.setBlend( true ); + desc.setZReadWrite( true, true ); + + Box3F objBox = mConvexSEL->getObjBox(); + objBox.scale( mConvexSEL->getScale() ); + + const MatrixF &objMat = mConvexSEL->getTransform(); + + Point3F boxPos = objBox.getCenter(); + objMat.mulP( boxPos ); + + drawer->drawObjectBox( desc, objBox.getExtents(), boxPos, objMat, ColorI::WHITE ); + } + else + { + mConvexSEL->renderFaceEdges( -1, colorNA ); + + drawFacePlane( mConvexSEL, mFaceSEL ); + } + + if ( mConvexHL == mConvexSEL && + mFaceHL != -1 && + mFaceHL != mFaceSEL && + mGizmo->getSelection() == Gizmo::None ) + { + mConvexSEL->renderFaceEdges( mFaceHL, colorHL ); + } + } + + if ( mConvexHL && mConvexHL != mConvexSEL ) + { + mConvexHL->renderFaceEdges( -1 ); + } + + if ( mGizmo->getMode() != RotateMode && mUsingPivot ) + { + mUsingPivot = false; + updateGizmoPos(); + } + + F32 gizmoAlpha = 1.0f; + if ( !mConvexSEL ) + gizmoAlpha = 0.0f; + + if ( mMouseDown && mGizmo->getSelection() != Gizmo::None && mConvexSEL ) + { + if ( mSettingPivot ) + gizmoAlpha = 1.0f; + else + gizmoAlpha = 0.0f; + } + + DebugDrawer::get()->render(); + + { + GFXTransformSaver saver; + // Now draw all the 2d stuff! + GFX->setClipRect(updateRect); + + if ( mConvexSEL && mFaceSEL != -1 ) + { + Vector< Point3F > lineList; + mConvexSEL->getSurfaceLineList( mFaceSEL, lineList ); + + MatrixF objToWorld( mConvexSEL->getTransform() ); + objToWorld.scale( mConvexSEL->getScale() ); + + for ( S32 i = 0; i < lineList.size(); i++ ) + objToWorld.mulP( lineList[i] ); + + for ( S32 i = 0; i < lineList.size() - 1; i++ ) + { + Point3F p0( lineList[i] ); + Point3F p1( lineList[i+1] ); + + drawLine( p0, p1, colorSEL, 3.0f ); + } + } + + if ( gizmoAlpha == 1.0f ) + { + if ( mGizmoProfile->mode != NoneMode ) + mGizmo->renderText( mSaveViewport, mSaveModelview, mSaveProjection ); + } + + if ( mActiveTool ) + mActiveTool->render2D(); + } + + if ( gizmoAlpha == 1.0f ) + mGizmo->renderGizmo( mLastCameraQuery.cameraMatrix, mLastCameraQuery.fov ); +} + +void GuiConvexEditorCtrl::drawFacePlane( ConvexShape *shape, S32 faceId ) +{ + // Build a vb of the face points ( in world space ) scaled outward in + // the surface space in x/y with uv coords. + + /* + Vector< Point3F > points; + Vector< Point2F > coords; + + shape->getSurfaceTriangles( faceId, &points, &coords, false ); + + if ( points.empty() ) + return; + + GFXVertexBufferHandle< GFXVertexPCT > vb; + vb.set( GFX, points.size(), GFXBufferTypeVolatile ); + GFXVertexPCT *vert = vb.lock(); + + for ( S32 i = 0; i < points.size(); i++ ) + { + vert->point = points[i]; + vert->color.set( 255, 255, 255, 200 ); + vert->texCoord = coords[i]; + vert++; + } + + vb.unlock(); + + GFXTransformSaver saver; + MatrixF renderMat( shape->getTransform() ); + renderMat.scale( shape->getScale() ); + GFX->multWorld( renderMat ); + + GFXStateBlockDesc desc; + desc.setBlend( true ); + desc.setCullMode( GFXCullNone ); + desc.setZReadWrite( true, false ); + desc.samplersDefined = true; + desc.samplers[0] = GFXSamplerStateDesc::getWrapLinear(); + GFX->setStateBlockByDesc( desc ); + + GFX->setVertexBuffer( vb ); + + GFXTexHandle tex( "core/art/grids/512_transp", &GFXDefaultStaticDiffuseProfile, "ConvexEditor_grid" ); + GFX->setTexture( 0, tex ); + GFX->setupGenericShaders(); + GFX->drawPrimitive( GFXTriangleList, 0, points.size() / 3 ); + */ +} + + +void GuiConvexEditorCtrl::scaleFace( ConvexShape *shape, S32 faceId, Point3F scale ) +{ + if ( !mHasGeometry ) + { + mHasGeometry = true; + + mSavedGeometry = shape->mGeometry; + mSavedSurfaces = shape->mSurfaces; + } + else + { + shape->mGeometry = mSavedGeometry; + shape->mSurfaces = mSavedSurfaces; + } + + if ( shape->mGeometry.faces.size() <= faceId ) + return; + + ConvexShape::Face &face = shape->mGeometry.faces[faceId]; + + Vector< Point3F > &pointList = shape->mGeometry.points; + + AssertFatal( shape->mSurfaces[ face.id ].isAffine(), "ConvexShapeEditor - surface not affine." ); + + Point3F projScale; + scale.z = 1.0f; + + const MatrixF &surfToObj = shape->mSurfaces[ face.id ]; + MatrixF objToSurf( surfToObj ); + objToSurf.inverse(); + + for ( S32 i = 0; i < face.points.size(); i++ ) + { + Point3F &pnt = pointList[ face.points[i] ]; + + objToSurf.mulP( pnt ); + pnt *= scale; + surfToObj.mulP( pnt ); + } + + updateModifiedFace( shape, faceId ); +} + +void GuiConvexEditorCtrl::translateFace( ConvexShape *shape, S32 faceId, const Point3F &displace ) +{ + if ( !mHasGeometry ) + { + mHasGeometry = true; + + mSavedGeometry = shape->mGeometry; + mSavedSurfaces = shape->mSurfaces; + } + else + { + shape->mGeometry = mSavedGeometry; + shape->mSurfaces = mSavedSurfaces; + } + + if ( shape->mGeometry.faces.size() <= faceId ) + return; + + ConvexShape::Face &face = shape->mGeometry.faces[faceId]; + + Vector< Point3F > &pointList = shape->mGeometry.points; + + AssertFatal( shape->mSurfaces[ face.id ].isAffine(), "ConvexShapeEditor - surface not affine." ); + + // Transform displacement into object space. + MatrixF worldToObj( shape->getTransform() ); + worldToObj.scale( shape->getScale() ); + worldToObj.inverse(); + + Point3F displaceOS; + worldToObj.mulV( displace, &displaceOS ); + + for ( S32 i = 0; i < face.points.size(); i++ ) + { + Point3F &pnt = pointList[ face.points[i] ]; + pnt += displaceOS; + } + + updateModifiedFace( shape, faceId ); +} + +void GuiConvexEditorCtrl::updateModifiedFace( ConvexShape *shape, S32 faceId ) +{ + if ( shape->mGeometry.faces.size() <= faceId ) + return; + + ConvexShape::Face &face = shape->mGeometry.faces[faceId]; + + Vector< Point3F > &pointList = shape->mGeometry.points; + + Vector< ConvexShape::Face > &faceList = shape->mGeometry.faces; + + for ( S32 i = 0; i < faceList.size(); i++ ) + { + ConvexShape::Face &curFace = faceList[i]; + MatrixF &curSurface = shape->mSurfaces[ curFace.id ]; + + U32 curPntCount = curFace.points.size(); + + if ( curPntCount < 3 ) + continue; + + // Does this face use any of the points which we have modified? + // Collect them in correct winding order. + + S32 pId0 = -1; + + for ( S32 j = 0; j < curFace.winding.size(); j++ ) + { + if ( face.points.contains( curFace.points[ curFace.winding[ j ] ] ) ) + { + pId0 = j; + break; + } + } + + if ( pId0 == -1 ) + continue; + + S32 pId1 = -1, pId2 = -1; + + pId1 = ( pId0 + 1 ) % curFace.winding.size(); + pId2 = ( pId0 + 2 ) % curFace.winding.size(); + + const Point3F &p0 = pointList[ curFace.points[ curFace.winding[ pId0 ] ] ]; + const Point3F &p1 = pointList[ curFace.points[ curFace.winding[ pId1 ] ] ]; + const Point3F &p2 = pointList[ curFace.points[ curFace.winding[ pId2 ] ] ]; + + PlaneF newPlane( p0, p1, p2 ); + Point3F uvec = newPlane.getNormal(); + Point3F fvec = curSurface.getForwardVector(); + Point3F rvec = curSurface.getRightVector(); + + F32 dt0 = mDot( uvec, fvec ); + F32 dt1 = mDot( uvec, rvec ); + + if ( mFabs( dt0 ) < mFabs( dt1 ) ) + { + rvec = mCross( fvec, uvec ); + rvec.normalizeSafe(); + fvec = mCross( uvec, rvec ); + fvec.normalizeSafe(); + } + else + { + fvec = mCross( uvec, rvec ); + fvec.normalizeSafe(); + rvec = mCross( fvec, uvec ); + rvec.normalizeSafe(); + } + + curSurface.setColumn( 0, rvec ); + curSurface.setColumn( 1, fvec ); + curSurface.setColumn( 2, uvec ); + curSurface.setPosition( newPlane.getPosition() ); + } + + updateShape( shape ); +} + +bool GuiConvexEditorCtrl::isShapeValid( ConvexShape *shape ) +{ + // Test for no-geometry. + if ( shape->mGeometry.points.empty() ) + return false; + + const Vector &pointList = shape->mGeometry.points; + const Vector &faceList = shape->mGeometry.faces; + + // Test that all points are shared by at least 3 faces. + + for ( S32 i = 0; i < pointList.size(); i++ ) + { + U32 counter = 0; + + for ( S32 j = 0; j < faceList.size(); j++ ) + { + if ( faceList[j].points.contains( i ) ) + counter++; + } + + if ( counter < 3 ) + return false; + } + + // Test for co-planar faces. + for ( S32 i = 0; i < shape->mPlanes.size(); i++ ) + { + for ( S32 j = i + 1; j < shape->mPlanes.size(); j++ ) + { + F32 d = mDot( shape->mPlanes[i], shape->mPlanes[j] ); + if ( d > 0.999f ) + return false; + } + } + + // Test for faces with zero or negative area. + for ( S32 i = 0; i < shape->mGeometry.faces.size(); i++ ) + { + if ( shape->mGeometry.faces[i].area < 0.0f ) + return false; + + if ( shape->mGeometry.faces[i].triangles.empty() ) + return false; + } + + return true; +} + +void GuiConvexEditorCtrl::setupShape( ConvexShape *shape ) +{ + shape->setField( "material", mMaterialName ); + shape->registerObject(); + updateShape( shape ); + + SimGroup *group; + if ( Sim::findObject( "missionGroup", group ) ) + group->addObject( shape ); +} + +void GuiConvexEditorCtrl::updateShape( ConvexShape *shape, S32 offsetFace ) +{ + shape->_updateGeometry( true ); + + /* + if ( offsetFace != -1 ) + { + shape->mSurfaces[ offsetFace ].setPosition( mPivotPos ); + }*/ + + synchClientObject( shape ); +} + +void GuiConvexEditorCtrl::synchClientObject( const ConvexShape *serverConvex ) +{ + if ( serverConvex->getClientObject() ) + { + ConvexShape *clientConvex = static_cast< ConvexShape* >( serverConvex->getClientObject() ); + clientConvex->setScale( serverConvex->getScale() ); + clientConvex->setTransform( serverConvex->getTransform() ); + clientConvex->mSurfaces.clear(); + clientConvex->mSurfaces.merge( serverConvex->mSurfaces ); + clientConvex->_updateGeometry(true); + } +} + +void GuiConvexEditorCtrl::updateGizmoPos() +{ + if ( mConvexSEL ) + { + if ( mFaceSEL != -1 ) + { + MatrixF surfMat = mConvexSEL->getSurfaceWorldMat( mFaceSEL ); + + MatrixF objToWorld( mConvexSEL->getTransform() ); + objToWorld.scale( mConvexSEL->getScale() ); + + Point3F gizmoPos(0,0,0); + + if ( mUsingPivot ) + { + gizmoPos = mPivotPos; + } + else + { + Point3F faceCenterPnt = mConvexSEL->mSurfaces[ mFaceSEL ].getPosition(); + objToWorld.mulP( faceCenterPnt ); + + mGizmoMatOffset = surfMat.getPosition() - faceCenterPnt; + + gizmoPos = faceCenterPnt; + } + + mGizmo->set( surfMat, gizmoPos, Point3F::One ); + } + else + { + mGizmoMatOffset = Point3F::Zero; + mGizmo->set( mConvexSEL->getTransform(), mConvexSEL->getPosition(), mConvexSEL->getScale() ); + } + } +} + +bool GuiConvexEditorCtrl::setActiveTool( ConvexEditorTool *tool ) +{ + if ( mActiveTool == tool ) + return false; + + ConvexEditorTool *prevTool = mActiveTool; + ConvexEditorTool *newTool = tool; + + if ( prevTool ) + prevTool->onDeactivated( newTool ); + + mActiveTool = newTool; + + if ( newTool ) + newTool->onActivated( prevTool ); + + return true; +} + +bool GuiConvexEditorCtrl::handleEscape() +{ + if ( mActiveTool ) + { + mActiveTool->onDeactivated( NULL ); + mActiveTool = NULL; + + return true; + } + + if ( mFaceSEL != -1 ) + { + setSelection( mConvexSEL, -1 ); + return true; + } + + if ( mConvexSEL ) + { + setSelection( NULL, -1 ); + return true; + } + + return false; +} + +bool GuiConvexEditorCtrl::handleDelete() +{ + if ( mActiveTool ) + { + mActiveTool->onDeactivated( NULL ); + mActiveTool = NULL; + } + + if ( mConvexSEL ) + { + if ( mFaceSEL != -1 ) + { + submitUndo( ModifyShape, mConvexSEL ); + + mConvexSEL->mSurfaces.erase_fast( mFaceSEL ); + updateShape( mConvexSEL ); + + if ( !isShapeValid( mConvexSEL ) ) + { + S32 selFace = mFaceSEL; + mLastUndo->undo(); + mFaceSEL = selFace; + updateShape( mConvexSEL ); + updateGizmoPos(); + } + else + { + setSelection( mConvexSEL, -1 ); + } + } + else + { + // Grab the mission editor undo manager. + UndoManager *undoMan = NULL; + if ( !Sim::findObject( "EUndoManager", undoMan ) ) + { + Con::errorf( "GuiConvexEditorCtrl::on3DMouseDown() - EUndoManager not found!" ); + } + else + { + // Create the UndoAction. + MEDeleteUndoAction *action = new MEDeleteUndoAction("Deleted ConvexShape"); + action->deleteObject( mConvexSEL ); + mIsDirty = true; + + mFaceHL = -1; + + setSelection( NULL, -1 ); + + // Submit it. + undoMan->addAction( action ); + } + } + } + + return true; +} + +bool GuiConvexEditorCtrl::hasSelection() const +{ + return mConvexSEL != NULL; +} + +void GuiConvexEditorCtrl::clearSelection() +{ + mFaceHL = -1; + mConvexHL = NULL; + setSelection( NULL, -1 ); +} + +void GuiConvexEditorCtrl::handleDeselect() +{ + if ( mActiveTool ) + { + mActiveTool->onDeactivated( NULL ); + mActiveTool = NULL; + } + + mFaceHL = -1; + mConvexHL = NULL; + setSelection( NULL, -1 ); +} + +void GuiConvexEditorCtrl::setSelection( ConvexShape *shape, S32 faceId ) +{ + mFaceSEL = faceId; + mConvexSEL = shape; + updateGizmoPos(); + + Con::executef( this, "onSelectionChanged", shape ? shape->getIdString() : "", Con::getIntArg(faceId) ); +} + +void GuiConvexEditorCtrl::_prepRenderImage( SceneManager* sceneGraph, const SceneRenderState* state ) +{ + if ( !isAwake() ) + return; + + /* + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->type = RenderPassManager::RIT_Editor; + ri->renderDelegate.bind( this, &GuiConvexEditorCtrl::_renderObject ); + ri->defaultKey = 100; + state->getRenderPass()->addInst( ri ); + */ +} + +void GuiConvexEditorCtrl::_renderObject( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *matInst ) +{ +} + +void GuiConvexEditorCtrl::submitUndo( UndoType type, ConvexShape *shape ) +{ + Vector< ConvexShape* > shapes; + shapes.push_back( shape ); + submitUndo( type, shapes ); +} + +void GuiConvexEditorCtrl::submitUndo( UndoType type, const Vector &shapes ) +{ + // Grab the mission editor undo manager. + Sim::findObject( "EUndoManager", mUndoManager ); + + if ( !mUndoManager ) + { + Con::errorf( "GuiConvexEditorCtrl::submitUndo() - EUndoManager not found!" ); + return; + } + + if ( type == ModifyShape ) + { + // Setup the action. + GuiConvexEditorUndoAction *action = new GuiConvexEditorUndoAction( "Modified a ConvexShape" ); + + ConvexShape *shape = shapes.first(); + + action->mObjId = shape->getId(); + action->mEditor = this; + action->mSavedObjToWorld = shape->getTransform(); + action->mSavedScale = shape->getScale(); + action->mSavedSurfaces.merge( shape->mSurfaces ); + action->mUndoManager = mUndoManager; + + mUndoManager->addAction( action ); + + mLastUndo = action; + } + else if ( type == CreateShape ) + { + MECreateUndoAction *action = new MECreateUndoAction( "Create ConvexShape" ); + + for ( S32 i = 0; i < shapes.size(); i++ ) + action->addObject( shapes[i] ); + + mUndoManager->addAction( action ); + + mLastUndo = action; + } + else if ( type == DeleteShape ) + { + MEDeleteUndoAction *action = new MEDeleteUndoAction( "Deleted ConvexShape" ); + + for ( S32 i = 0; i < shapes.size(); i++ ) + action->deleteObject( shapes[i] ); + + mUndoManager->addAction( action ); + + mLastUndo = action; + } + else if ( type == HollowShape ) + { + CompoundUndoAction *action = new CompoundUndoAction( "Hollow ConvexShape" ); + + MECreateUndoAction *createAction = new MECreateUndoAction(); + MEDeleteUndoAction *deleteAction = new MEDeleteUndoAction(); + + deleteAction->deleteObject( shapes.first() ); + + for ( S32 i = 1; i < shapes.size(); i++ ) + createAction->addObject( shapes[i] ); + + action->addAction( deleteAction ); + action->addAction( createAction ); + + mUndoManager->addAction( action ); + + mLastUndo = action; + } + + mIsDirty = true; +} + +bool GuiConvexEditorCtrl::_cursorCastCallback( RayInfo* ri ) +{ + // Reject anything that's not a ConvexShape. + return dynamic_cast< ConvexShape* >( ri->object ); +} + +bool GuiConvexEditorCtrl::_cursorCast( const Gui3DMouseEvent &event, ConvexShape **hitShape, S32 *hitFace ) +{ + RayInfo ri; + + if ( gServerContainer.castRay( event.pos, event.pos + event.vec * 10000.0f, StaticShapeObjectType, &ri, &GuiConvexEditorCtrl::_cursorCastCallback ) && + dynamic_cast< ConvexShape* >( ri.object ) ) + { + // Do not select or edit ConvexShapes that are within a Prefab. + if ( Prefab::getPrefabByChild( ri.object ) ) + return false; + + *hitShape = static_cast< ConvexShape* >( ri.object ); + *hitFace = ri.face; + mLastRayInfo = ri; + + return true; + } + + return false; +} + +void GuiConvexEditorCtrl::setPivotPos( ConvexShape *shape, S32 faceId, const Gui3DMouseEvent &event ) +{ + PlaneF plane; + mTransformPlane( shape->getTransform(), shape->getScale(), shape->mPlanes[ faceId ], &plane ); + + Point3F start( event.pos ); + Point3F end( start + event.vec * 10000.0f ); + + F32 t = plane.intersect( start, end ); + + if ( t >= 0.0f && t <= 1.0f ) + { + Point3F hitPos; + hitPos.interpolate( start, end, t ); + + mPivotPos = hitPos; + mUsingPivot = true; + + MatrixF worldToObj( shape->getTransform() ); + worldToObj.scale( shape->getScale() ); + worldToObj.inverse(); + + Point3F objPivotPos( mPivotPos ); + worldToObj.mulP( objPivotPos ); + + updateGizmoPos(); + } +} + +void GuiConvexEditorCtrl::cleanMatrix( MatrixF &mat ) +{ + if ( mat.isAffine() ) + return; + + VectorF col0 = mat.getColumn3F(0); + VectorF col1 = mat.getColumn3F(1); + VectorF col2 = mat.getColumn3F(2); + + col0.normalize(); + col1.normalize(); + col2.normalize(); + + col2 = mCross( col0, col1 ); + col2.normalize(); + col1 = mCross( col2, col0 ); + col1.normalize(); + col0 = mCross( col1, col2 ); + col0.normalize(); + + mat.setColumn(0,col0); + mat.setColumn(1,col1); + mat.setColumn(2,col2); + + AssertFatal( mat.isAffine(), "GuiConvexEditorCtrl::cleanMatrix, non-affine matrix" ); +} + +S32 GuiConvexEditorCtrl::getEdgeByPoints( ConvexShape *shape, S32 faceId, S32 p0, S32 p1 ) +{ + const ConvexShape::Face &face = shape->mGeometry.faces[faceId]; + + for ( S32 i = 0; i < face.edges.size(); i++ ) + { + const ConvexShape::Edge &edge = face.edges[i]; + + if ( edge.p0 != p0 && edge.p0 != p1 ) + continue; + if ( edge.p1 != p0 && edge.p1 != p1 ) + continue; + + return i; + } + + return -1; +} + +bool GuiConvexEditorCtrl::getEdgesTouchingPoint( ConvexShape *shape, S32 faceId, S32 pId, Vector< U32 > &edgeIdxList, S32 excludeEdge ) +{ + const ConvexShape::Face &face = shape->mGeometry.faces[faceId]; + const Vector< ConvexShape::Edge > &edgeList = face.edges; + + for ( S32 i = 0; i < edgeList.size(); i++ ) + { + if ( i == excludeEdge ) + continue; + + const ConvexShape::Edge &curEdge = edgeList[i]; + + if ( curEdge.p0 == pId || curEdge.p1 == pId ) + edgeIdxList.push_back(i); + } + + return !edgeIdxList.empty(); +} + +void GuiConvexEditorUndoAction::undo() +{ + ConvexShape *object = NULL; + if ( !Sim::findObject( mObjId, object ) ) + return; + + // Temporarily save the ConvexShape current data. + Vector< MatrixF > tempSurfaces; + tempSurfaces.merge( object->mSurfaces ); + MatrixF tempObjToWorld( object->getTransform() ); + Point3F tempScale( object->getScale() ); + + // Restore the Object to the UndoAction state. + object->mSurfaces.clear(); + object->mSurfaces.merge( mSavedSurfaces ); + object->setScale( mSavedScale ); + object->setTransform( mSavedObjToWorld ); + + // Regenerate the ConvexShape and synch the client object. + object->_updateGeometry(); + GuiConvexEditorCtrl::synchClientObject( object ); + + // If applicable set the selected ConvexShape and face + // on the editor. + mEditor->setSelection( object, -1 ); + mEditor->updateGizmoPos(); + + // Now save the previous ConvexShape data in this UndoAction + // since an undo action must become a redo action and vice-versa + + mSavedObjToWorld = tempObjToWorld; + mSavedScale = tempScale; + mSavedSurfaces.clear(); + mSavedSurfaces.merge( tempSurfaces ); +} + +ConvexEditorCreateTool::ConvexEditorCreateTool( GuiConvexEditorCtrl *editor ) + : Parent( editor ), + mStage( -1 ), + mNewConvex( NULL ) +{ +} + +void ConvexEditorCreateTool::onActivated( ConvexEditorTool *prevTool ) +{ + mEditor->clearSelection(); + mStage = -1; + mNewConvex = NULL; +} + +void ConvexEditorCreateTool::onDeactivated( ConvexEditorTool *newTool ) +{ + if ( mNewConvex ) + mNewConvex->deleteObject(); + + mStage = -1; + mNewConvex = NULL; + mEditor->mouseUnlock(); +} + +ConvexEditorTool::EventResult ConvexEditorCreateTool::on3DMouseDown( const Gui3DMouseEvent &event ) +{ + if ( mStage == -1 ) + { + mEditor->setFirstResponder(); + mEditor->mouseLock(); + + Point3F start( event.pos ); + Point3F end( event.pos + event.vec * 10000.0f ); + RayInfo ri; + + bool hit = gServerContainer.castRay( event.pos, end, STATIC_COLLISION_TYPEMASK, &ri ); + + MatrixF objMat( true ); + + // Calculate the orientation matrix of the new ConvexShape + // based on what has been clicked. + + if ( !hit ) + { + objMat.setPosition( event.pos + event.vec * 100.0f ); + } + else + { + if ( dynamic_cast< ConvexShape* >( ri.object ) ) + { + ConvexShape *hitShape = static_cast< ConvexShape* >( ri.object ); + objMat = hitShape->getSurfaceWorldMat( ri.face ); + objMat.setPosition( ri.point ); + } + else + { + Point3F rvec; + Point3F fvec( mEditor->getCameraMat().getForwardVector() ); + Point3F uvec( ri.normal ); + + rvec = mCross( fvec, uvec ); + + if ( rvec.isZero() ) + { + fvec = mEditor->getCameraMat().getRightVector(); + rvec = mCross( fvec, uvec ); + } + + rvec.normalizeSafe(); + fvec = mCross( uvec, rvec ); + fvec.normalizeSafe(); + uvec = mCross( rvec, fvec ); + uvec.normalizeSafe(); + + objMat.setColumn( 0, rvec ); + objMat.setColumn( 1, fvec ); + objMat.setColumn( 2, uvec ); + + objMat.setPosition( ri.point ); + } + } + + mNewConvex = new ConvexShape(); + + mNewConvex->setTransform( objMat ); + + mNewConvex->setField( "material", Parent::mEditor->mMaterialName ); + + mNewConvex->registerObject(); + mPlaneSizes.set( 0.1f, 0.1f, 0.1f ); + mNewConvex->resizePlanes( mPlaneSizes ); + mEditor->updateShape( mNewConvex ); + + mTransform = objMat; + + mCreatePlane.set( objMat.getPosition(), objMat.getUpVector() ); + } + else if ( mStage == 0 ) + { + // Handle this on mouseUp + } + + return Handled; +} + +ConvexEditorTool::EventResult ConvexEditorCreateTool::on3DMouseUp( const Gui3DMouseEvent &event ) +{ + if ( mNewConvex && mStage == -1 ) + { + mStage = 0; + + mCreatePlane = PlaneF( mNewConvex->getPosition(), mNewConvex->getTransform().getForwardVector() ); + + mTransform.setPosition( mNewConvex->getPosition() ); + + return Handled; + } + else if ( mStage == 0 ) + { + SimGroup *mg; + Sim::findObject( "MissionGroup", mg ); + + mg->addObject( mNewConvex ); + + mStage = -1; + + // Grab the mission editor undo manager. + UndoManager *undoMan = NULL; + if ( !Sim::findObject( "EUndoManager", undoMan ) ) + { + Con::errorf( "ConvexEditorCreateTool::on3DMouseDown() - EUndoManager not found!" ); + mNewConvex = NULL; + return Failed; + } + + // Create the UndoAction. + MECreateUndoAction *action = new MECreateUndoAction("Create ConvexShape"); + action->addObject( mNewConvex ); + + // Submit it. + undoMan->addAction( action ); + + mEditor->setField( "isDirty", "1" ); + + mEditor->setSelection( mNewConvex, -1 ); + + mNewConvex = NULL; + + mEditor->mouseUnlock(); + + return Done; + } + + return Done; +} + +ConvexEditorTool::EventResult ConvexEditorCreateTool::on3DMouseMove( const Gui3DMouseEvent &event ) +{ + if ( mStage == 0 ) + { + Point3F start( event.pos ); + Point3F end( start + event.vec * 10000.0f ); + + F32 t = mCreatePlane.intersect( start, end ); + + Point3F hitPos; + + if ( t < 0.0f || t > 1.0f ) + return Handled; + + hitPos.interpolate( start, end, t ); + + MatrixF worldToObj( mTransform ); + worldToObj.inverse(); + worldToObj.mulP( hitPos ); + + F32 delta = ( hitPos.z ); + + mPlaneSizes.z = getMax( 0.1f, delta ); + + mNewConvex->resizePlanes( mPlaneSizes ); + + mEditor->updateShape( mNewConvex ); + + Point3F pos( mTransform.getPosition() ); + pos += mPlaneSizes.z * 0.5f * mTransform.getUpVector(); + mNewConvex->setPosition( pos ); + } + + return Handled; +} + +ConvexEditorTool::EventResult ConvexEditorCreateTool::on3DMouseDragged( const Gui3DMouseEvent &event ) +{ + if ( !mNewConvex || mStage != -1 ) + return Handled; + + Point3F start( event.pos ); + Point3F end( event.pos + event.vec * 10000.0f ); + + F32 t = mCreatePlane.intersect( start, end ); + + if ( t < 0.0f || t > 1.0f ) + return Handled; + + Point3F hitPos; + hitPos.interpolate( start, end, t ); + + MatrixF xfm( mTransform ); + xfm.inverse(); + xfm.mulP( hitPos); + + Point3F scale; + scale.x = getMax( mFabs( hitPos.x ), 0.1f ); + scale.y = getMax( mFabs( hitPos.y ), 0.1f ); + scale.z = 0.1f; + + mNewConvex->resizePlanes( scale ); + mPlaneSizes = scale; + mEditor->updateShape( mNewConvex ); + + Point3F pos( mTransform.getPosition() ); + pos += mTransform.getRightVector() * hitPos.x * 0.5f; + pos += mTransform.getForwardVector() * hitPos.y * 0.5f; + + mNewConvex->setPosition( pos ); + + return Handled; +} + +void ConvexEditorCreateTool::renderScene( const RectI &updateRect ) +{ + +} + +ConvexShape* ConvexEditorCreateTool::extrudeShapeFromFace( ConvexShape *inShape, S32 inFaceId ) +{ + ConvexShape::Geometry &inShapeGeometry = inShape->getGeometry(); + ConvexShape::Face &inFace = inShapeGeometry.faces[inFaceId]; + Vector< Point3F > &inShapePointList = inShapeGeometry.points; + Vector< MatrixF > &inShapeSurfaces = inShape->getSurfaces(); + + S32 shapeFaceCount = inFace.edges.size() + 2; + + MatrixF inShapeToWorld( inShape->getTransform() ); + inShapeToWorld.scale( inShape->getScale() ); + //MatrixF inWorldToShape( inShapeToWorld ); + //inWorldToShape.inverse(); + + MatrixF shapeToWorld; + shapeToWorld.mul( inShape->getTransform(), inShapeSurfaces[inFaceId] ); + Point3F tmp( inShapeSurfaces[inFaceId].getPosition() ); + inShapeToWorld.mulP( tmp ); + shapeToWorld.setPosition( tmp ); + MatrixF worldToShape( shapeToWorld ); + worldToShape.inverse(); + + MatrixF inShapeToNewShape; + inShapeToNewShape.mul( inShapeToWorld, worldToShape ); + + ConvexShape *newShape = new ConvexShape; + newShape->setTransform( shapeToWorld ); + + Vector< MatrixF > &shapeSurfaces = newShape->getSurfaces(); + shapeSurfaces.setSize( shapeFaceCount ); + //shapeSurfaces.setSize( 2 ); + + const Point3F &shapePos = shapeToWorld.getPosition(); + + shapeSurfaces[0].identity(); + shapeSurfaces[1].identity(); + shapeSurfaces[1].setColumn( 0, -shapeSurfaces[1].getColumn3F(0) ); + shapeSurfaces[1].setColumn( 2, -shapeSurfaces[1].getColumn3F(2) ); + + for ( S32 i = 0; i < inFace.winding.size(); i++ ) + { + Point3F p0 = inShapePointList[ inFace.points[ inFace.winding[ i ] ] ]; + Point3F p1; + + if ( i+1 < inFace.winding.size() ) + p1 = inShapePointList[ inFace.points[ inFace.winding[ i+1 ] ] ]; + else + p1 = inShapePointList[ inFace.points[ inFace.winding[ 0 ] ] ]; + + inShapeToWorld.mulP( p0 ); + inShapeToWorld.mulP( p1 ); + + Point3F newPos = MathUtils::mClosestPointOnSegment( p0, p1, shapePos ); + + Point3F rvec = p0 - p1; + rvec.normalizeSafe(); + + Point3F fvec = shapeToWorld.getUpVector(); + + Point3F uvec = mCross( rvec, fvec ); + + if ( i + 2 >= shapeSurfaces.size() ) + continue; + + //F32 dt = mDot( shapeToWorld.getUpVector(), rvec ); + //AssertFatal( mIsZero( dt ), "bad" ); + + MatrixF &surf = shapeSurfaces[i+2]; + surf.identity(); + surf.setColumn( 0, rvec ); + surf.setColumn( 1, fvec ); + surf.setColumn( 2, uvec ); + surf.setPosition( newPos ); + + surf.mulL( worldToShape ); + } + + newShape->setField( "material", Parent::mEditor->mMaterialName ); + + newShape->registerObject(); + mEditor->updateShape( newShape ); + + SimGroup *group; + if ( Sim::findObject( "missionGroup", group ) ) + group->addObject( newShape ); + + return newShape; +} + +void GuiConvexEditorCtrl::hollowShape( ConvexShape *shape, F32 thickness ) +{ + // Create a new Convex for each face of the original shape. + // This is the same as an extrude from face operation going inward by the thickness + // for every face. + + Vector< ConvexShape* > convexList; + + for ( S32 i = 0; i < shape->mGeometry.faces.size(); i++ ) + { + ConvexShape *faceShape = mCreateTool->extrudeShapeFromFace( shape, i ); + MatrixF &inwardFace = faceShape->mSurfaces[1]; + //MatrixF &outwardFace = faceShape->mSurfaces[0]; + + Point3F invec = inwardFace.getUpVector(); + + inwardFace.setPosition( inwardFace.getPosition() + invec * thickness ); + + updateShape( faceShape ); + + convexList.push_back( faceShape ); + } + + convexList.push_front( shape ); + submitUndo( HollowShape, convexList ); +} + +void GuiConvexEditorCtrl::hollowSelection() +{ + if ( mConvexSEL ) + { + hollowShape( mConvexSEL, 0.15f ); + setSelection( NULL, -1 ); + } +} + +void GuiConvexEditorCtrl::recenterSelection() +{ + if ( mConvexSEL ) + { + recenterShape( mConvexSEL ); + updateGizmoPos(); + } +} + +void GuiConvexEditorCtrl::recenterShape( ConvexShape *shape ) +{ + submitUndo( ModifyShape, shape ); + shape->recenter(); + synchClientObject( shape ); +} + +void GuiConvexEditorCtrl::dropSelectionAtScreenCenter() +{ + // This code copied from WorldEditor. + // All the dropping code would be moved to somewhere common, but its not. + + if ( !mConvexSEL ) + return; + + // Calculate the center of the screen (in global screen coordinates) + Point2I offset = localToGlobalCoord(Point2I(0,0)); + Point3F sp(F32(offset.x + F32(getExtent().x / 2)), F32(offset.y + (getExtent().y / 2)), 1.0f); + + // Calculate the view distance to fit the selection + // within the camera's view. + const Box3F bounds = mConvexSEL->getWorldBox(); + F32 radius = bounds.len()*0.5f; + F32 viewdist = calculateViewDistance(radius); + + // Be careful of infinite sized objects, or just large ones in general. + if(viewdist > 100.0f ) + viewdist = 100.0f; + + // Position the selection + mConvexSEL->setPosition( smCamPos + smCamMatrix.getForwardVector() * viewdist ); + + synchClientObject( mConvexSEL ); + + updateGizmoPos(); +} + +void GuiConvexEditorCtrl::splitSelectedFace() +{ + if ( !mConvexSEL || mFaceSEL == -1 ) + return; + + if ( !isShapeValid( mConvexSEL ) ) + return; + + mLastValidShape = mConvexSEL->mSurfaces; + + const F32 radians = mDegToRad( 15.0f ); + Point3F rot( 0, 0, 0 ); + MatrixF rotMat( true ); + + mConvexSEL->mSurfaces.increment(); + MatrixF &dstMat = mConvexSEL->mSurfaces.last(); + const MatrixF &srcMat = mConvexSEL->mSurfaces[mFaceSEL]; + + for ( S32 i = 0; i < 6; i++ ) + { + F32 sign = i > 2 ? -1.0f : 1.0f; + U32 idx = i % 3; + + rot.zero(); + rot[idx] = sign * radians; + rotMat.set( (EulerF)rot ); + + dstMat = srcMat * rotMat; + + updateShape( mConvexSEL ); + + if ( isShapeValid( mConvexSEL ) ) + { + mSavedSurfaces = mConvexSEL->mSurfaces; + mConvexSEL->mSurfaces = mLastValidShape; + + submitUndo( ModifyShape, mConvexSEL ); + + mConvexSEL->mSurfaces = mSavedSurfaces; + mLastValidShape = mSavedSurfaces; + + setSelection( mConvexSEL, mConvexSEL->mSurfaces.size() - 1 ); + + return; + } + } + + mConvexSEL->mSurfaces = mLastValidShape; + updateShape( mConvexSEL ); + updateGizmoPos(); +} + +ConsoleMethod( GuiConvexEditorCtrl, hollowSelection, void, 2, 2, "" ) +{ + object->hollowSelection(); +} + +ConsoleMethod( GuiConvexEditorCtrl, recenterSelection, void, 2, 2, "" ) +{ + object->recenterSelection(); +} + +ConsoleMethod( GuiConvexEditorCtrl, hasSelection, S32, 2, 2, "" ) +{ + return object->hasSelection(); +} + +ConsoleMethod( GuiConvexEditorCtrl, handleDelete, void, 2, 2, "" ) +{ + object->handleDelete(); +} + +ConsoleMethod( GuiConvexEditorCtrl, handleDeselect, void, 2, 2, "" ) +{ + object->handleDeselect(); +} + +ConsoleMethod( GuiConvexEditorCtrl, dropSelectionAtScreenCenter, void, 2, 2, "" ) +{ + object->dropSelectionAtScreenCenter(); +} + +ConsoleMethod( GuiConvexEditorCtrl, selectConvex, void, 3, 3, "( ConvexShape )" ) +{ + ConvexShape *convex; + if ( Sim::findObject( argv[2], convex ) ) + object->setSelection( convex, -1 ); +} + +ConsoleMethod( GuiConvexEditorCtrl, splitSelectedFace, void, 2, 2, "" ) +{ + object->splitSelectedFace(); +} \ No newline at end of file diff --git a/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.h b/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.h new file mode 100644 index 000000000..0bb00c32f --- /dev/null +++ b/Engine/source/gui/worldEditor/guiConvexShapeEditorCtrl.h @@ -0,0 +1,294 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUICONVEXSHAPEEDITORCTRL_H_ +#define _GUICONVEXSHAPEEDITORCTRL_H_ + +#ifndef _EDITTSCTRL_H_ +#include "gui/worldEditor/editTSCtrl.h" +#endif +#ifndef _UNDO_H_ +#include "util/undo.h" +#endif +#ifndef _GIZMO_H_ +#include "gui/worldEditor/gizmo.h" +#endif +#ifndef _CONVEXSHAPE_H_ +#include "T3D/convexShape.h" +#endif + +class GameBase; +class GuiConvexEditorUndoAction; +class ConvexEditorTool; +class ConvexEditorCreateTool; + +class GuiConvexEditorCtrl : public EditTSCtrl +{ + typedef EditTSCtrl Parent; + + friend class GuiConvexEditorUndoAction; + +public: + + GuiConvexEditorCtrl(); + virtual ~GuiConvexEditorCtrl(); + + DECLARE_CONOBJECT( GuiConvexEditorCtrl ); + + // SimObject + virtual bool onAdd(); + virtual void onRemove(); + static void initPersistFields(); + + // GuiControl + virtual bool onWake(); + virtual void onSleep(); + virtual void setVisible(bool value); + + // EditTSCtrl + bool onKeyDown( const GuiEvent &event ); + bool onKeyUp( const GuiEvent &event ); + void get3DCursor( GuiCursor *&cursor, bool &visible, const Gui3DMouseEvent &event_ ); + void on3DMouseDown( const Gui3DMouseEvent &event ); + void on3DMouseUp( const Gui3DMouseEvent &event ); + void on3DMouseMove( const Gui3DMouseEvent &event ); + void on3DMouseDragged( const Gui3DMouseEvent &event ); + void on3DMouseEnter( const Gui3DMouseEvent &event ); + void on3DMouseLeave( const Gui3DMouseEvent &event ); + void on3DRightMouseDown( const Gui3DMouseEvent &event ); + void on3DRightMouseUp( const Gui3DMouseEvent &event ); + void renderScene(const RectI & updateRect); + void updateGizmo(); + + void updateShape( ConvexShape *shape, S32 offsetFace = -1 ); + static void synchClientObject( const ConvexShape *serverConvex ); + + void updateGizmoPos(); + + bool setActiveTool( ConvexEditorTool *tool ); + + void drawFacePlane( ConvexShape *shape, S32 faceId ); + + void scaleFace( ConvexShape *shape, S32 faceId, Point3F scale ); + + void translateFace( ConvexShape *shape, S32 faceId, const Point3F &displace ); + + void updateModifiedFace( ConvexShape *shape, S32 faceId ); + + bool isShapeValid( ConvexShape *shape ); + + void setupShape( ConvexShape *shape ); + + void setPivotPos( ConvexShape *shape, S32 faceId, const Gui3DMouseEvent &event ); + + void cleanMatrix( MatrixF &mat ); + + S32 getEdgeByPoints( ConvexShape *shape, S32 faceId, S32 pId0, S32 pId1 ); + + bool getEdgesTouchingPoint( ConvexShape *shape, S32 faceId, S32 pId, Vector< U32 > &edgeIdxList, S32 excludeEdge = -1 ); + + void hollowSelection(); + void hollowShape( ConvexShape *shape, F32 thickness ); + + void recenterSelection(); + void recenterShape( ConvexShape *shape ); + void dropSelectionAtScreenCenter(); + void splitSelectedFace(); + + /// Interface with Tools. + /// @{ + + MatrixF getCameraMat() const { return mLastCameraQuery.cameraMatrix; } + + enum UndoType + { + ModifyShape = 0, + CreateShape, + DeleteShape, + HollowShape + }; + + void submitUndo( UndoType type, ConvexShape *shape ); + void submitUndo( UndoType type, const Vector< ConvexShape* > &shape ); + + /// @} + + bool hasSelection() const; + void clearSelection(); + void setSelection( ConvexShape *shape, S32 faceId ); + void handleDeselect(); + bool handleEscape(); + bool handleDelete(); + bool handleTab(); +public: + + StringTableEntry mMaterialName; +protected: + + void _prepRenderImage( SceneManager* sceneGraph, const SceneRenderState* sceneState ); + void _renderObject( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *matInst ); + + bool _cursorCast( const Gui3DMouseEvent &event, ConvexShape **hitShape, S32 *hitFace ); + static bool _cursorCastCallback( RayInfo* ri ); + +protected: + + bool mIsDirty; + + U32 mSavedGizmoFlags; + + /// The selected ConvexShape. + SimObjectPtr mConvexSEL; + + /// The highlighted ConvexShape ( mouse over ). + SimObjectPtr mConvexHL; + + S32 mFaceSEL; + S32 mFaceHL; + + MatrixF mFaceSavedXfm; + + ConvexShape::Geometry mSavedGeometry; + Vector< MatrixF > mSavedSurfaces; + Vector< MatrixF > mLastValidShape; + + Point3F mSavedPivotPos; + + bool mCtrlDown; + bool mSavedUndo; + bool mHasGeometry; + bool mDragging; + bool mMouseDown; + bool mHasCopied; + RayInfo mLastRayInfo; + + Gui3DMouseEvent mMouseDownEvent; + + Point3F mGizmoMatOffset; + + Point3F mPivotPos; + bool mUsingPivot; + bool mSettingPivot; + + UndoAction *mLastUndo; + UndoManager *mUndoManager; + + ConvexEditorTool *mActiveTool; + ConvexEditorCreateTool *mCreateTool; +}; + +class GuiConvexEditorUndoAction : public UndoAction +{ + friend class GuiConvexEditorCtrl; +public: + + GuiConvexEditorUndoAction( const UTF8* actionName ) : UndoAction( actionName ) + { + } + + GuiConvexEditorCtrl *mEditor; + + SimObjectId mObjId; + + Vector< MatrixF > mSavedSurfaces; + MatrixF mSavedObjToWorld; + Point3F mSavedScale; + + virtual void undo(); + virtual void redo() { undo(); } +}; + +class ConvexEditorTool +{ +public: + + enum EventResult + { + NotHandled = 0, + Handled = 1, + Done = 2, + Failed = 3 + }; + + ConvexEditorTool( GuiConvexEditorCtrl *editor ) + : mEditor( editor ), mDone( false ) {} + virtual ~ConvexEditorTool() {} + + virtual void onActivated( ConvexEditorTool *prevTool ) {} + virtual void onDeactivated( ConvexEditorTool *newTool ) {} + + virtual EventResult onKeyDown( const GuiEvent &event ) { return NotHandled; } + virtual EventResult on3DMouseDown( const Gui3DMouseEvent &event ) { return NotHandled; } + virtual EventResult on3DMouseUp( const Gui3DMouseEvent &event ) { return NotHandled; } + virtual EventResult on3DMouseMove( const Gui3DMouseEvent &event ) { return NotHandled; } + virtual EventResult on3DMouseDragged( const Gui3DMouseEvent &event ) { return NotHandled; } + virtual EventResult on3DMouseEnter( const Gui3DMouseEvent &event ) { return NotHandled; } + virtual EventResult on3DMouseLeave( const Gui3DMouseEvent &event ) { return NotHandled; } + virtual EventResult on3DRightMouseDown( const Gui3DMouseEvent &event ) { return NotHandled; } + virtual EventResult on3DRightMouseUp( const Gui3DMouseEvent &event ) { return NotHandled; } + + virtual void renderScene(const RectI & updateRect) {} + virtual void render2D() {} + + bool isDone() { return mDone; } + +public: + GuiConvexEditorCtrl *mEditor; +protected: + bool mDone; +}; + +class ConvexEditorCreateTool : public ConvexEditorTool +{ + typedef ConvexEditorTool Parent; +public: + ConvexEditorCreateTool( GuiConvexEditorCtrl *editor ); + virtual ~ConvexEditorCreateTool() {} + + virtual void onActivated( ConvexEditorTool *prevTool ); + virtual void onDeactivated( ConvexEditorTool *newTool ); + + virtual EventResult on3DMouseDown( const Gui3DMouseEvent &event ); + virtual EventResult on3DMouseUp( const Gui3DMouseEvent &event ); + virtual EventResult on3DMouseMove( const Gui3DMouseEvent &event ); + virtual EventResult on3DMouseDragged( const Gui3DMouseEvent &event ); + + virtual void renderScene(const RectI & updateRect); + + ConvexShape* extrudeShapeFromFace( ConvexShape *shape, S32 face ); + +protected: + + S32 mStage; + ConvexShape *mNewConvex; + PlaneF mCreatePlane; + + MatrixF mTransform; + Point3F mStart; + Point3F mEnd; + Point3F mPlaneSizes; +}; + +#endif // _GUICONVEXSHAPEEDITORCTRL_H_ + + + diff --git a/Engine/source/gui/worldEditor/guiDecalEditorCtrl.cpp b/Engine/source/gui/worldEditor/guiDecalEditorCtrl.cpp new file mode 100644 index 000000000..d90e3f00b --- /dev/null +++ b/Engine/source/gui/worldEditor/guiDecalEditorCtrl.cpp @@ -0,0 +1,1244 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef TORQUE_TGB_ONLY + +#include "guiDecalEditorCtrl.h" +#include "platform/platform.h" + +#include "console/consoleTypes.h" +#include "scene/sceneManager.h" +#include "collision/collision.h" +#include "math/util/frustum.h" +#include "gfx/gfxPrimitiveBuffer.h" +#include "gfx/gfxTextureHandle.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxDrawUtil.h" +#include "gui/core/guiCanvas.h" +#include "gui/buttons/guiButtonCtrl.h" +#include "gui/worldEditor/gizmo.h" +#include "T3D/decal/decalManager.h" +#include "T3D/decal/decalInstance.h" +#include "gui/worldEditor/undoActions.h" + +IMPLEMENT_CONOBJECT(GuiDecalEditorCtrl); + +ConsoleDocClass( GuiDecalEditorCtrl, + "@brief The base class for the Decal Editor tool\n\n" + "Editor use only.\n\n" + "@internal" +); + +bool GuiDecalEditorCtrl::smRenderDecalPixelSize = false; + +GuiDecalEditorCtrl::GuiDecalEditorCtrl() +{ + mSELDecal = NULL; + mHLDecal = NULL; + mCurrentDecalData = NULL; + mMode = "AddDecalMode"; + mPerformedDragCopy = false; +} + +GuiDecalEditorCtrl::~GuiDecalEditorCtrl() +{ + // nothing to do +} + +bool GuiDecalEditorCtrl::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + return true; +} + +void GuiDecalEditorCtrl::initPersistFields() +{ + addField( "currentDecalData", TYPEID< DecalData >(), Offset( mCurrentDecalData, GuiDecalEditorCtrl ) ); + + Parent::initPersistFields(); +} + +void GuiDecalEditorCtrl::consoleInit() +{ + Con::addVariable( "$DecalEditor::renderPixelSize", TypeBool, &smRenderDecalPixelSize, + "Set true to render the pixel size as on overlay on the selected decal instance. " + "This is the value used to fade distant decals and is intended to help the user adjust " + "the values of DecalData::pixelSizeStartFade and pixelSizeEndFade.\n\n" + "@internal" ); + + Parent::consoleInit(); +} + +void GuiDecalEditorCtrl::onEditorDisable() +{ + // Tools are not deleted/recreated between missions, but decals instances + // ARE. So we must release any references. + mSELDecal = NULL; + mHLDecal = NULL; +} + +bool GuiDecalEditorCtrl::onWake() +{ + if ( !Parent::onWake() ) + return false; + + + + return true; +} + +void GuiDecalEditorCtrl::onSleep() +{ + Parent::onSleep(); +} + +void GuiDecalEditorCtrl::get3DCursor( GuiCursor *&cursor, + bool &visible, + const Gui3DMouseEvent &event_ ) +{ + cursor = NULL; + visible = false; + + GuiCanvas *root = getRoot(); + if ( !root ) + return; + + S32 currCursor = PlatformCursorController::curArrow; + + if ( root->mCursorChanged == currCursor ) + return; + + PlatformWindow *window = root->getPlatformWindow(); + PlatformCursorController *controller = window->getCursorController(); + + // We've already changed the cursor, + // so set it back before we change it again. + if( root->mCursorChanged != -1) + controller->popCursor(); + + // Now change the cursor shape + controller->pushCursor(currCursor); + root->mCursorChanged = currCursor; +} + +void GuiDecalEditorCtrl::on3DMouseDown(const Gui3DMouseEvent & event) +{ + mPerformedDragCopy = false; + + if ( !isFirstResponder() ) + setFirstResponder(); + + bool dblClick = ( event.mouseClickCount > 1 ); + + // Gather selected decal information + RayInfo ri; + bool hit = getRayInfo( event, &ri ); + + Point3F start = event.pos; + Point3F end = start + event.vec * 3000.0f; // use visible distance here?? + + DecalInstance *pDecal = gDecalManager->raycast( start, end ); + + if( mMode.compare("AddDecalMode") != 0 ) + { + if ( mSELDecal ) + { + // If our click hit the gizmo we are done. + if ( mGizmo->getSelection() != Gizmo::None ) + { + mGizmo->on3DMouseDown( event ); + + char returnBuffer[256]; + dSprintf(returnBuffer, sizeof(returnBuffer), "%f %f %f %f %f %f %f", + mSELDecal->mPosition.x, mSELDecal->mPosition.y, mSELDecal->mPosition.z, + mSELDecal->mTangent.x, mSELDecal->mTangent.y, mSELDecal->mTangent.z, + mSELDecal->mSize); + + Con::executef( this, "prepGizmoTransform", Con::getIntArg(mSELDecal->mId), returnBuffer ); + + return; + } + } + + if ( mHLDecal && pDecal == mHLDecal ) + { + mHLDecal = NULL; + selectDecal( pDecal ); + + if ( isMethod( "onSelectInstance" ) ) + { + char idBuf[512]; + dSprintf(idBuf, 512, "%i", pDecal->mId); + Con::executef( this, "onSelectInstance", String(idBuf).c_str(), pDecal->mDataBlock->lookupName.c_str() ); + } + + return; + } + else if ( hit && !pDecal) + { + if ( dblClick ) + setMode( String("AddDecalMode"), true ); + + return; + } + } + else + { + // If we accidently hit a decal, then bail(probably an accident). If the use hits the decal twice, + // then boot them into selection mode and select the decal. + if ( mHLDecal && pDecal == mHLDecal ) + { + if ( dblClick ) + { + mHLDecal = NULL; + selectDecal( pDecal ); + + if ( isMethod( "onSelectInstance" ) ) + { + char idBuf[512]; + dSprintf(idBuf, 512, "%i", pDecal->mId); + Con::executef( this, "onSelectInstance", String(idBuf).c_str(), pDecal->mDataBlock->lookupName.c_str() ); + } + setMode( String("SelectDecalMode"), true ); + } + return; + } + + if ( hit && mCurrentDecalData ) // Create a new decal... + { + U8 flags = PermanentDecal | SaveDecal; + + DecalInstance *decalInst = gDecalManager->addDecal( ri.point, ri.normal, 0.0f, mCurrentDecalData, 1.0f, -1, flags ); + + if ( decalInst ) + { + // Give the decal an id + decalInst->mId = gDecalManager->mDecalInstanceVec.size(); + gDecalManager->mDecalInstanceVec.push_back(decalInst); + + selectDecal( decalInst ); + + // Grab the mission editor undo manager. + UndoManager *undoMan = NULL; + if ( !Sim::findObject( "EUndoManager", undoMan ) ) + { + Con::errorf( "GuiMeshRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" ); + return; + } + + // Create the UndoAction. + DICreateUndoAction *action = new DICreateUndoAction("Create Decal"); + action->addDecal( *decalInst ); + + action->mEditor = this; + // Submit it. + undoMan->addAction( action ); + + if ( isMethod( "onCreateInstance" ) ) + { + char buffer[512]; + dSprintf(buffer, 512, "%i", decalInst->mId); + Con::executef( this, "onCreateInstance", buffer, decalInst->mDataBlock->lookupName.c_str()); + } + } + + return; + } + } + + if ( !mSELDecal ) + return; +} + +void GuiDecalEditorCtrl::on3DRightMouseDown(const Gui3DMouseEvent & event) +{ + //mIsPanning = true; + //mGizmo->on3DRightMouseDown( event ); +} + +void GuiDecalEditorCtrl::on3DRightMouseUp(const Gui3DMouseEvent & event) +{ + //mIsPanning = false; +// mGizmo->on3DRightMouseUp( event ); +} + +void GuiDecalEditorCtrl::on3DMouseUp(const Gui3DMouseEvent & event) +{ + if ( mSELDecal ) + { + if ( mGizmo->isDirty() ) + { + char returnBuffer[256]; + dSprintf(returnBuffer, sizeof(returnBuffer), "%f %f %f %f %f %f %f", + mSELDecal->mPosition.x, mSELDecal->mPosition.y, mSELDecal->mPosition.z, + mSELDecal->mTangent.x, mSELDecal->mTangent.y, mSELDecal->mTangent.z, + mSELDecal->mSize); + + Con::executef( this, "completeGizmoTransform", Con::getIntArg(mSELDecal->mId), returnBuffer ); + + mGizmo->markClean(); + } + + mGizmo->on3DMouseUp( event ); + } +} + +void GuiDecalEditorCtrl::on3DMouseMove(const Gui3DMouseEvent & event) +{ + if ( mSELDecal ) + mGizmo->on3DMouseMove( event ); + + RayInfo ri; + if ( !getRayInfo( event, &ri ) ) + return; + + Point3F start = event.pos; + Point3F end = start + event.vec * 3000.0f; // use visible distance here?? + + DecalInstance *pDecal = gDecalManager->raycast( start, end ); + + if ( pDecal && pDecal != mSELDecal ) + mHLDecal = pDecal; + else if ( !pDecal ) + mHLDecal = NULL; +} + +void GuiDecalEditorCtrl::on3DMouseDragged(const Gui3DMouseEvent & event) +{ + if ( !mSELDecal ) + return; + + // Doing a drag copy of the decal? + if ( event.modifier & SI_SHIFT && !mPerformedDragCopy ) + { + mPerformedDragCopy = true; + + DecalInstance *newDecal = gDecalManager->addDecal( mSELDecal->mPosition, + mSELDecal->mNormal, + 0.0f, + mSELDecal->mDataBlock, + 1.0f, + -1, + PermanentDecal | SaveDecal ); + + newDecal->mTangent = mSELDecal->mTangent; + newDecal->mSize = mSELDecal->mSize; + newDecal->mTextureRectIdx = mSELDecal->mTextureRectIdx; + + // TODO: This is crazy... we should move this sort of tracking + // inside of the decal manager... IdDecal flag maybe or just a + // byproduct of PermanentDecal? + // + newDecal->mId = gDecalManager->mDecalInstanceVec.size(); + gDecalManager->mDecalInstanceVec.push_back( newDecal ); + + selectDecal( newDecal ); + + // Grab the mission editor undo manager. + UndoManager *undoMan = NULL; + if ( Sim::findObject( "EUndoManager", undoMan ) ) + { + // Create the UndoAction. + DICreateUndoAction *action = new DICreateUndoAction("Create Decal"); + action->addDecal( *mSELDecal ); + action->mEditor = this; + undoMan->addAction( action ); + + if ( isMethod( "onCreateInstance" ) ) + { + char buffer[512]; + dSprintf( buffer, 512, "%i", mSELDecal->mId ); + Con::executef( this, "onCreateInstance", buffer, mSELDecal->mDataBlock->lookupName.c_str()); + } + } + } + + // Update the Gizmo. + if (mGizmo->getSelection() != Gizmo::None) + { + mGizmo->on3DMouseDragged( event ); + + // Pull out the Gizmo transform + // and position. + const MatrixF &gizmoMat = mGizmo->getTransform(); + const Point3F &gizmoPos = gizmoMat.getPosition(); + + // Get the new projection vector. + VectorF upVec, rightVec; + gizmoMat.getColumn( 0, &rightVec ); + gizmoMat.getColumn( 2, &upVec ); + + const Point3F &scale = mGizmo->getScale(); + + // Assign the appropriate changed value back to the decal. + if ( mGizmo->getMode() == ScaleMode ) + mSELDecal->mSize = (scale.x + scale.y) * 0.5f; + else if ( mGizmo->getMode() == MoveMode ) + mSELDecal->mPosition = gizmoPos; + else if ( mGizmo->getMode() == RotateMode ) + { + mSELDecal->mNormal = upVec; + mSELDecal->mTangent = rightVec; + } + + gDecalManager->notifyDecalModified( mSELDecal ); + + Con::executef( this, "syncNodeDetails" ); + } +} + +void GuiDecalEditorCtrl::on3DMouseEnter(const Gui3DMouseEvent & event) +{ + // nothing to do +} + +void GuiDecalEditorCtrl::on3DMouseLeave(const Gui3DMouseEvent & event) +{ + // nothing to do +} + +void GuiDecalEditorCtrl::updateGuiInfo() +{ + // nothing to do +} + +void GuiDecalEditorCtrl::onRender( Point2I offset, const RectI &updateRect ) +{ + Parent::onRender( offset, updateRect ); +} + +void GuiDecalEditorCtrl::renderGui( Point2I offset, const RectI &updateRect ) +{ + Parent::renderGui( offset, updateRect ); + + PROFILE_SCOPE( GuiDecalEditorCtrl_renderGui ); + + // Show the pixelSize of the selected decal as a text overlay. + if ( smRenderDecalPixelSize && mSELDecal != NULL ) + { + const F32 pixelSize = mSELDecal->calcPixelSize( mSaveViewport.extent.y, mLastCameraQuery.cameraMatrix.getPosition(), mSaveWorldToScreenScale.y ); + + // Find position onscreen to render the text. + Point3F screenPos; + bool onScreen = project( mSELDecal->mPosition, &screenPos ); + + if ( onScreen ) + { + // It is extremely annoying to require the GuiProfile to have a font + // or to create one within the decal editor for only this single use, + // so we instead rely on the fact that we already have a Gizmo, that + // all Gizmo's have a GizmoProfile, and that GizmoProfile has a font. + GFont *font = mGizmo->getProfile()->font; + + // Might as well use some colors defined in GizmoProfile too instead + // of just hardcoding it here. + const ColorI bgColor = mGizmo->getProfile()->inActiveColor; + const ColorI textColor = mGizmo->getProfile()->activeColor; + + // Note: This mostly mirrors the way WorldEditor renders popupText for + // the gizmo during a drag operation, consider unifying this into a utility method. + + char buf[256]; + dSprintf( buf, 256, "%0.3f", pixelSize ); + + const U32 width = font->getStrWidth((const UTF8 *)buf);; + const Point2I posi( (U32)screenPos.x, (U32)screenPos.y + 12 ); + const Point2I minPt(posi.x - width / 2 - 2, posi.y - 1); + const Point2I maxPt(posi.x + width / 2 + 2, posi.y + font->getHeight() + 1); + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + drawer->drawRectFill( minPt, maxPt, bgColor ); + GFX->getDrawUtil()->setBitmapModulation( textColor ); + GFX->getDrawUtil()->drawText( mProfile->mFont, Point2I( posi.x - width / 2, posi.y ), buf ); + } + } +} + +void GuiDecalEditorCtrl::renderScene(const RectI & updateRect) +{ + PROFILE_SCOPE( GuiDecalEditorCtrl_renderScene ); + + GFXTransformSaver saver; + + RectI bounds = getBounds(); + + ColorI hlColor(0,255,0,255); + ColorI regColor(255,0,0,255); + ColorI selColor(0,0,255,255); + ColorI color; + + GFXDrawUtil *drawUtil = GFX->getDrawUtil(); + + GFXStateBlockDesc desc; + desc.setBlend( true ); + desc.setZReadWrite( true, false ); + + // Draw 3D stuff here. + if ( mSELDecal ) + { + mGizmo->renderGizmo( mLastCameraQuery.cameraMatrix, mLastCameraQuery.fov ); + + mSELEdgeVerts.clear(); + if ( gDecalManager->clipDecal( mSELDecal, &mSELEdgeVerts ) ) + _renderDecalEdge( mSELEdgeVerts, ColorI( 255, 255, 255, 255 ) ); + + const F32 &decalSize = mSELDecal->mSize; + Point3F boxSize( decalSize, decalSize, decalSize ); + + MatrixF worldMat( true ); + mSELDecal->getWorldMatrix( &worldMat, true ); + + drawUtil->drawObjectBox( desc, boxSize, mSELDecal->mPosition, worldMat, ColorI( 255, 255, 255, 255 ) ); + } + + if ( mHLDecal ) + { + mHLEdgeVerts.clear(); + if ( gDecalManager->clipDecal( mHLDecal, &mHLEdgeVerts ) ) + _renderDecalEdge( mHLEdgeVerts, ColorI( 255, 255, 255, 255 ) ); + + const F32 &decalSize = mHLDecal->mSize; + Point3F boxSize( decalSize, decalSize, decalSize ); + + MatrixF worldMat( true ); + mHLDecal->getWorldMatrix( &worldMat, true ); + + drawUtil->drawObjectBox( desc, boxSize, mHLDecal->mPosition, worldMat, ColorI( 255, 255, 255, 255 ) ); + } +} + +void GuiDecalEditorCtrl::forceRedraw( DecalInstance * decalInstance ) +{ + // This should be redundant because the decal is already reclipped + // on each frame. Also it is not possible execute rendering code like + // this in response to UI events from script. + /* + if ( !decalInstance ) + return; + + GFXDrawUtil *drawUtil = GFX->getDrawUtil(); + GFXStateBlockDesc desc; + desc.setBlend( true ); + desc.setZReadWrite( true, false ); + + Vector verts; + verts.clear(); + if ( gDecalManager->clipDecal( decalInstance, &verts ) ) + if ( gDecalManager->clipDecal( decalInstance, &verts ) ) + _renderDecalEdge( verts, ColorI( 255, 255, 255, 255 ) ); + + const F32 &decalSize = decalInstance->mSize; + Point3F boxSize( decalSize, decalSize, decalSize ); + + MatrixF worldMat( true ); + decalInstance->getWorldMatrix( &worldMat, true ); + + drawUtil->drawObjectBox( desc, boxSize, decalInstance->mPosition, worldMat, ColorI( 255, 255, 255, 255 ) ); + */ +} + +void GuiDecalEditorCtrl::_renderDecalEdge( const Vector &verts, const ColorI &color ) +{ + U32 vertCount = verts.size(); + + GFXTransformSaver saver; + + PrimBuild::color( color ); + + Point3F endPt( 0, 0, 0 ); + for ( U32 i = 0; i < vertCount; i++ ) + { + const Point3F &vert = verts[i]; + if ( i + 1 < vertCount ) + endPt = verts[i + 1]; + else + break; + + PrimBuild::begin( GFXLineList, 2 ); + + PrimBuild::vertex3f( vert.x, vert.y, vert.z ); + PrimBuild::vertex3f( endPt.x, endPt.y, endPt.z ); + + PrimBuild::end(); + } +} + +bool GuiDecalEditorCtrl::getRayInfo( const Gui3DMouseEvent & event, RayInfo *rInfo ) +{ + Point3F startPnt = event.pos; + Point3F endPnt = event.pos + event.vec * 3000.0f; + + bool hit; + + hit = gServerContainer.castRayRendered( startPnt, endPnt, STATIC_COLLISION_TYPEMASK, rInfo ); + + return hit; +} + +void GuiDecalEditorCtrl::selectDecal( DecalInstance *decalInst ) +{ + // If id is zero or invalid we set the selected decal to null + // which is correct. + mSELDecal = decalInst; + + if ( decalInst ) + setGizmoFocus( decalInst ); +} + +void GuiDecalEditorCtrl::deleteSelectedDecal() +{ + if ( !mSELDecal ) + return; + + // Grab the mission editor undo manager. + UndoManager *undoMan = NULL; + if ( !Sim::findObject( "EUndoManager", undoMan ) ) + { + Con::errorf( "GuiMeshRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" ); + return; + } + + // Create the UndoAction. + DIDeleteUndoAction *action = new DIDeleteUndoAction("Delete Decal"); + action->deleteDecal( *mSELDecal ); + + action->mEditor = this; + // Submit it. + undoMan->addAction( action ); + + if ( isMethod( "onDeleteInstance" ) ) + { + char buffer[512]; + dSprintf(buffer, 512, "%i", mSELDecal->mId); + Con::executef( this, "onDeleteInstance", String(buffer).c_str(), mSELDecal->mDataBlock->lookupName.c_str() ); + } + + gDecalManager->removeDecal( mSELDecal ); + mSELDecal = NULL; +} + +void GuiDecalEditorCtrl::deleteDecalDatablock( String lookupName ) +{ + DecalData * datablock = dynamic_cast ( Sim::findObject(lookupName.c_str()) ); + if( !datablock ) + return; + + // Grab the mission editor undo manager. + UndoManager *undoMan = NULL; + if ( !Sim::findObject( "EUndoManager", undoMan ) ) + { + Con::errorf( "GuiMeshRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" ); + return; + } + + // Create the UndoAction. + DBDeleteUndoAction *action = new DBDeleteUndoAction("Delete Decal Datablock"); + action->mEditor = this; + action->mDatablockId = datablock->getId(); + + Vector mDecalQueue; + Vector::iterator iter; + mDecalQueue.clear(); + const Vector &grid = gDecalManager->getDecalDataFile()->getSphereList(); + + for ( U32 i = 0; i < grid.size(); i++ ) + { + const DecalSphere *decalSphere = grid[i]; + mDecalQueue.merge( decalSphere->mItems ); + } + + for ( iter = mDecalQueue.begin();iter != mDecalQueue.end();iter++ ) + { + if( !(*iter) ) + continue; + + if( (*iter)->mDataBlock->lookupName.compare( lookupName ) == 0 ) + { + if( (*iter)->mId != -1 ) + { + //make sure to call onDeleteInstance as well + if ( isMethod( "onDeleteInstance" ) ) + { + char buffer[512]; + dSprintf(buffer, 512, "%i", (*iter)->mId); + Con::executef( this, "onDeleteInstance", String(buffer).c_str(), (*iter)->mDataBlock->lookupName.c_str() ); + } + + action->deleteDecal( *(*iter) ); + + if( mSELDecal == (*iter) ) + mSELDecal = NULL; + + if( mHLDecal == (*iter) ) + mHLDecal = NULL; + } + gDecalManager->removeDecal( (*iter) ); + } + } + + undoMan->addAction( action ); + + mCurrentDecalData = NULL; +} + +void GuiDecalEditorCtrl::retargetDecalDatablock( String dbFrom, String dbTo ) +{ + DecalData * ptrFrom = dynamic_cast ( Sim::findObject(dbFrom.c_str()) ); + DecalData * ptrTo = dynamic_cast ( Sim::findObject(dbTo.c_str()) ); + + if( !ptrFrom || !ptrTo ) + return; + + // Grab the mission editor undo manager. + UndoManager *undoMan = NULL; + if ( !Sim::findObject( "EUndoManager", undoMan ) ) + { + Con::errorf( "GuiMeshRoadEditorCtrl::on3DMouseDown() - EUndoManager not found!" ); + return; + } + + // Create the UndoAction. + DBRetargetUndoAction *action = new DBRetargetUndoAction("Retarget Decal Datablock"); + action->mEditor = this; + action->mDBFromId = ptrFrom->getId(); + action->mDBToId = ptrTo->getId(); + + Vector mDecalQueue; + Vector::iterator iter; + mDecalQueue.clear(); + const Vector &grid = gDecalManager->getDecalDataFile()->getSphereList(); + for ( U32 i = 0; i < grid.size(); i++ ) + { + const DecalSphere *decalSphere = grid[i]; + mDecalQueue.merge( decalSphere->mItems ); + } + + for ( iter = mDecalQueue.begin();iter != mDecalQueue.end();iter++ ) + { + if( !(*iter) ) + continue; + + if( (*iter)->mDataBlock->lookupName.compare( dbFrom ) == 0 ) + { + if( (*iter)->mId != -1 ) + { + action->retargetDecal((*iter)); + (*iter)->mDataBlock = ptrTo; + forceRedraw((*iter)); + } + } + } + + undoMan->addAction( action ); +} + +void GuiDecalEditorCtrl::setMode( String mode, bool sourceShortcut = false ) +{ + if( mode.compare("SelectDecalMode") == 0) + mGizmo->getProfile()->mode = NoneMode; + else if( mode.compare("AddDecalMode") == 0) + mGizmo->getProfile()->mode = NoneMode; + else if( mode.compare("MoveDecalMode") == 0) + mGizmo->getProfile()->mode = MoveMode; + else if( mode.compare("RotateDecalMode") == 0) + mGizmo->getProfile()->mode = RotateMode; + else if( mode.compare("ScaleDecalMode") == 0) + mGizmo->getProfile()->mode = ScaleMode; + + mMode = mode; + + if( sourceShortcut ) + Con::executef( this, "paletteSync", mMode ); +} + +ConsoleMethod( GuiDecalEditorCtrl, deleteSelectedDecal, void, 2, 2, "deleteSelectedDecal()" ) +{ + object->deleteSelectedDecal(); +} + +ConsoleMethod( GuiDecalEditorCtrl, deleteDecalDatablock, void, 3, 3, "deleteSelectedDecalDatablock( String datablock )" ) +{ + String lookupName( argv[2] ); + if( lookupName == String::EmptyString ) + return; + + object->deleteDecalDatablock( lookupName ); +} + +ConsoleMethod( GuiDecalEditorCtrl, setMode, void, 3, 3, "setMode( String mode )()" ) +{ + String newMode = ( argv[2] ); + object->setMode( newMode ); +} + +ConsoleMethod( GuiDecalEditorCtrl, getMode, const char*, 2, 2, "getMode()" ) +{ + return object->mMode; +} + +ConsoleMethod( GuiDecalEditorCtrl, getDecalCount, S32, 2, 2, "getDecalCount()" ) +{ + return gDecalManager->mDecalInstanceVec.size(); +} + +ConsoleMethod( GuiDecalEditorCtrl, getDecalTransform, const char*, 3, 3, "getDecalTransform()" ) +{ + DecalInstance *decalInstance = gDecalManager->mDecalInstanceVec[dAtoi(argv[2])]; + if( decalInstance == NULL ) + return ""; + + char* returnBuffer = Con::getReturnBuffer(256); + returnBuffer[0] = 0; + + if ( decalInstance ) + { + dSprintf(returnBuffer, 256, "%f %f %f %f %f %f %f", + decalInstance->mPosition.x, decalInstance->mPosition.y, decalInstance->mPosition.z, + decalInstance->mTangent.x, decalInstance->mTangent.y, decalInstance->mTangent.z, + decalInstance->mSize); + } + + return returnBuffer; +} + +ConsoleMethod( GuiDecalEditorCtrl, getDecalLookupName, const char*, 3, 3, "getDecalLookupName( S32 )()" ) +{ + DecalInstance *decalInstance = gDecalManager->mDecalInstanceVec[dAtoi(argv[2])]; + if( decalInstance == NULL ) + return "invalid"; + + return decalInstance->mDataBlock->lookupName; +} + +ConsoleMethod( GuiDecalEditorCtrl, selectDecal, void, 3, 3, "selectDecal( S32 )()" ) +{ + DecalInstance *decalInstance = gDecalManager->mDecalInstanceVec[dAtoi(argv[2])]; + if( decalInstance == NULL ) + return; + + object->selectDecal( decalInstance ); +} + +ConsoleMethod( GuiDecalEditorCtrl, editDecalDetails, void, 4, 4, "editDecalDetails( S32 )()" ) +{ + DecalInstance *decalInstance = gDecalManager->mDecalInstanceVec[ dAtoi(argv[2]) ]; + if( decalInstance == NULL ) + return; + + Point3F pos; + Point3F tan; + F32 size; + + S32 count = dSscanf( argv[3], "%f %f %f %f %f %f %f", + &pos.x, &pos.y, &pos.z, &tan.x, &tan.y, &tan.z, &size); + + if ( (count != 7) ) + { + Con::printf("Failed to parse decal information \"px py pz tx ty tz s\" from '%s'", argv[3]); + return; + } + + decalInstance->mPosition = pos; + decalInstance->mTangent = tan; + decalInstance->mSize = size; + + if ( decalInstance == object->mSELDecal ) + object->setGizmoFocus( decalInstance ); + + object->forceRedraw( decalInstance ); + + gDecalManager->notifyDecalModified( decalInstance ); +} + +ConsoleMethod( GuiDecalEditorCtrl, getSelectionCount, S32, 2, 2, "" ) +{ + if ( object->mSELDecal != NULL ) + return 1; + return 0; +} + +ConsoleMethod( GuiDecalEditorCtrl, retargetDecalDatablock, void, 4, 4, "" ) +{ + if( dStrcmp( argv[2], "" ) != 0 && dStrcmp( argv[3], "" ) != 0 ) + object->retargetDecalDatablock( argv[2], argv[3] ); +} + +void GuiDecalEditorCtrl::setGizmoFocus( DecalInstance * decalInstance ) +{ + const F32 &size = decalInstance->mSize; + MatrixF worldMat( true ); + decalInstance->getWorldMatrix( &worldMat, true ); + worldMat.setPosition( Point3F( 0, 0, 0 ) ); + mGizmo->set( worldMat, decalInstance->mPosition, Point3F( size, size, size ) ); +} + +//Decal Instance Create Undo Actions +IMPLEMENT_CONOBJECT( DICreateUndoAction ); + +ConsoleDocClass( DICreateUndoAction, + "@brief Decal Instance Create Undo Actions\n\n" + "Not intended for game development, for editors or internal use only.\n\n " + "@internal"); + +DICreateUndoAction::DICreateUndoAction( const UTF8* actionName ) + : UndoAction( actionName ) +{ +} + +DICreateUndoAction::~DICreateUndoAction() +{ +} + +void DICreateUndoAction::initPersistFields() +{ + Parent::initPersistFields(); +} + +void DICreateUndoAction::addDecal( DecalInstance decal ) +{ + mDecalInstance = decal; + mDatablockId = decal.mDataBlock->getId(); +} + +void DICreateUndoAction::undo() +{ + Vector::iterator iter; + for(iter = gDecalManager->mDecalInstanceVec.begin();iter != gDecalManager->mDecalInstanceVec.end();iter++) + { + if( !(*iter) ) + continue; + + if( (*iter)->mId != mDecalInstance.mId ) + continue; + + if ( mEditor->isMethod( "onDeleteInstance" ) ) + { + char buffer[512]; + dSprintf(buffer, 512, "%i", (*iter)->mId); + Con::executef( mEditor, "onDeleteInstance", String(buffer).c_str(), (*iter)->mDataBlock->lookupName.c_str() ); + } + + // Decal manager handles clearing the vector if the decal contains a valid id + if( mEditor->mSELDecal == (*iter) ) + mEditor->mSELDecal = NULL; + + if( mEditor->mHLDecal == (*iter) ) + mEditor->mHLDecal = NULL; + + gDecalManager->removeDecal( (*iter) ); + break; + } +} + +void DICreateUndoAction::redo() +{ + //Reinstate the valid datablock pointer + mDecalInstance.mDataBlock = dynamic_cast( Sim::findObject( mDatablockId ) ); + + DecalInstance * decal = gDecalManager->addDecal( mDecalInstance.mPosition, + mDecalInstance.mNormal, + mDecalInstance.mTangent, + mDecalInstance.mDataBlock, + ( mDecalInstance.mSize / mDecalInstance.mDataBlock->size ), + mDecalInstance.mTextureRectIdx, + mDecalInstance.mFlags ); + + decal->mId = mDecalInstance.mId; + + // Override the rectIdx regardless of random decision in addDecal + decal->mTextureRectIdx = mDecalInstance.mTextureRectIdx; + + // We take care of filling in the vector space that was once there + gDecalManager->mDecalInstanceVec[decal->mId] = decal; + + if ( mEditor->isMethod( "onCreateInstance" ) ) + { + char buffer[512]; + dSprintf(buffer, 512, "%i", decal->mId); + Con::executef( mEditor, "onCreateInstance", buffer, decal->mDataBlock->lookupName.c_str()); + } + mEditor->selectDecal( decal ); +} + +//Decal Instance Delete Undo Actions +IMPLEMENT_CONOBJECT( DIDeleteUndoAction ); + +ConsoleDocClass( DIDeleteUndoAction, + "@brief Decal Instance Delete Undo Actions\n\n" + "Not intended for game development, for editors or internal use only.\n\n " + "@internal"); + +DIDeleteUndoAction::DIDeleteUndoAction( const UTF8 *actionName ) + : UndoAction( actionName ) +{ +} + +DIDeleteUndoAction::~DIDeleteUndoAction() +{ +} + +void DIDeleteUndoAction::initPersistFields() +{ + Parent::initPersistFields(); +} + +void DIDeleteUndoAction::deleteDecal( DecalInstance decal ) +{ + mDecalInstance = decal; + mDatablockId = decal.mDataBlock->getId(); +} + +void DIDeleteUndoAction::undo() +{ + //Reinstate the valid datablock pointer + mDecalInstance.mDataBlock = dynamic_cast( Sim::findObject( mDatablockId ) ); + + DecalInstance * decal = gDecalManager->addDecal( mDecalInstance.mPosition, + mDecalInstance.mNormal, + mDecalInstance.mTangent, + mDecalInstance.mDataBlock, + ( mDecalInstance.mSize / mDecalInstance.mDataBlock->size ), + mDecalInstance.mTextureRectIdx, + mDecalInstance.mFlags ); + + decal->mId = mDecalInstance.mId; + + // Override the rectIdx regardless of random decision in addDecal + decal->mTextureRectIdx = mDecalInstance.mTextureRectIdx; + + // We take care of filling in the vector space that was once there + gDecalManager->mDecalInstanceVec[decal->mId] = decal; + + if ( mEditor->isMethod( "onCreateInstance" ) ) + { + char buffer[512]; + dSprintf(buffer, 512, "%i", decal->mId); + Con::executef( mEditor, "onCreateInstance", buffer, decal->mDataBlock->lookupName.c_str()); + } + mEditor->selectDecal( decal ); +} + +void DIDeleteUndoAction::redo() +{ + Vector::iterator iter; + for(iter = gDecalManager->mDecalInstanceVec.begin();iter != gDecalManager->mDecalInstanceVec.end();iter++) + { + if( !(*iter) ) + continue; + + if( (*iter)->mId != mDecalInstance.mId ) + continue; + + if ( mEditor->isMethod( "onDeleteInstance" ) ) + { + char buffer[512]; + dSprintf(buffer, 512, "%i", (*iter)->mId); + Con::executef( mEditor, "onDeleteInstance", String(buffer).c_str(), (*iter)->mDataBlock->lookupName.c_str() ); + } + + // Decal manager handles clearing the vector if the decal contains a valid id + if( mEditor->mSELDecal == (*iter) ) + mEditor->mSELDecal = NULL; + + if( mEditor->mHLDecal == (*iter) ) + mEditor->mHLDecal = NULL; + + gDecalManager->removeDecal( (*iter) ); + break; + } +} + +//Decal Datablock Delete Undo Actions +IMPLEMENT_CONOBJECT( DBDeleteUndoAction ); + +ConsoleDocClass( DBDeleteUndoAction, + "@brief Decal Datablock Delete Undo Actions\n\n" + "Not intended for game development, for editors or internal use only.\n\n " + "@internal"); + +DBDeleteUndoAction::DBDeleteUndoAction( const UTF8 *actionName ) + : UndoAction( actionName ) +{ +} + +DBDeleteUndoAction::~DBDeleteUndoAction() +{ +} + +void DBDeleteUndoAction::initPersistFields() +{ + Parent::initPersistFields(); +} + +void DBDeleteUndoAction::deleteDecal( DecalInstance decal ) +{ + mDecalInstanceVec.increment(); + mDecalInstanceVec.last() = decal; +} + +void DBDeleteUndoAction::undo() +{ + DecalData * datablock = dynamic_cast( Sim::findObject( mDatablockId ) ); + if ( mEditor->isMethod( "undoDeleteDecalDatablock" ) ) + Con::executef( mEditor, "undoDeleteDecalDatablock", datablock->lookupName.c_str()); + + // Create and restore the decal instances + for ( S32 i= mDecalInstanceVec.size()-1; i >= 0; i-- ) + { + DecalInstance vecInstance = mDecalInstanceVec[i]; + + //Reinstate the valid datablock pointer + vecInstance.mDataBlock = datablock; + + DecalInstance * decalInstance = gDecalManager->addDecal( vecInstance.mPosition, + vecInstance.mNormal, + vecInstance.mTangent, + vecInstance.mDataBlock, + ( vecInstance.mSize / vecInstance.mDataBlock->size ), + vecInstance.mTextureRectIdx, + vecInstance.mFlags ); + + decalInstance->mId = vecInstance.mId; + + // Override the rectIdx regardless of random decision in addDecal + decalInstance->mTextureRectIdx = vecInstance.mTextureRectIdx; + + // We take care of filling in the vector space that was once there + gDecalManager->mDecalInstanceVec[decalInstance->mId] = decalInstance; + + if ( mEditor->isMethod( "onCreateInstance" ) ) + { + char buffer[512]; + dSprintf(buffer, 512, "%i", decalInstance->mId); + Con::executef( mEditor, "onCreateInstance", buffer, decalInstance->mDataBlock->lookupName.c_str()); + } + } + +} + +void DBDeleteUndoAction::redo() +{ + for ( S32 i=0; i < mDecalInstanceVec.size(); i++ ) + { + DecalInstance vecInstance = mDecalInstanceVec[i]; + + Vector::iterator iter; + for(iter = gDecalManager->mDecalInstanceVec.begin();iter != gDecalManager->mDecalInstanceVec.end();iter++) + { + DecalInstance * decalInstance = (*iter); + if( !decalInstance ) + continue; + + if( decalInstance->mId != vecInstance.mId ) + continue; + + if ( mEditor->isMethod( "onDeleteInstance" ) ) + { + char buffer[512]; + dSprintf(buffer, 512, "%i", decalInstance->mId); + Con::executef( mEditor, "onDeleteInstance", String(buffer).c_str(), decalInstance->mDataBlock->lookupName.c_str() ); + } + + // Decal manager handles clearing the vector if the decal contains a valid id + if( mEditor->mSELDecal == decalInstance ) + mEditor->mSELDecal = NULL; + + if( mEditor->mHLDecal == decalInstance ) + mEditor->mHLDecal = NULL; + + gDecalManager->removeDecal( decalInstance ); + break; + } + } + + DecalData * datablock = dynamic_cast( Sim::findObject( mDatablockId ) ); + if ( mEditor->isMethod( "redoDeleteDecalDatablock" ) ) + Con::executef( mEditor, "redoDeleteDecalDatablock", datablock->lookupName.c_str()); +} + +//------------------------------ +//Decal Datablock Retarget Undo Actions +IMPLEMENT_CONOBJECT( DBRetargetUndoAction ); + +ConsoleDocClass( DBRetargetUndoAction, + "@brief Decal Datablock Retarget Undo Actions\n\n" + "Not intended for game development, for editors or internal use only.\n\n " + "@internal"); + +DBRetargetUndoAction::DBRetargetUndoAction( const UTF8 *actionName ) + : UndoAction( actionName ) +{ +} + +DBRetargetUndoAction::~DBRetargetUndoAction() +{ +} + +void DBRetargetUndoAction::initPersistFields() +{ + Parent::initPersistFields(); +} + +void DBRetargetUndoAction::retargetDecal( DecalInstance* decal ) +{ + mDecalInstanceVec.increment(); + mDecalInstanceVec.last() = decal; +} + +void DBRetargetUndoAction::undo() +{ + DecalData * ptrFrom = dynamic_cast ( Sim::findObject(mDBFromId) ); + + if( !ptrFrom ) + return; + + Vector::iterator iter; + for(iter = mDecalInstanceVec.begin();iter != mDecalInstanceVec.end();iter++) + { + (*iter)->mDataBlock = ptrFrom; + mEditor->forceRedraw((*iter)); + } + if ( mEditor->isMethod( "rebuildInstanceTree" ) ) + Con::executef( mEditor, "rebuildInstanceTree" ); +} + +void DBRetargetUndoAction::redo() +{ + DecalData * ptrTo = dynamic_cast ( Sim::findObject(mDBToId) ); + + if( !ptrTo ) + return; + + Vector::iterator iter; + for(iter = mDecalInstanceVec.begin();iter != mDecalInstanceVec.end();iter++) + { + (*iter)->mDataBlock = ptrTo; + mEditor->forceRedraw((*iter)); + } + + if ( mEditor->isMethod( "rebuildInstanceTree" ) ) + Con::executef( mEditor, "rebuildInstanceTree" ); +} +#endif \ No newline at end of file diff --git a/Engine/source/gui/worldEditor/guiDecalEditorCtrl.h b/Engine/source/gui/worldEditor/guiDecalEditorCtrl.h new file mode 100644 index 000000000..043634a5d --- /dev/null +++ b/Engine/source/gui/worldEditor/guiDecalEditorCtrl.h @@ -0,0 +1,231 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIDECALEDITORCTRL_H_ +#define _GUIDECALEDITORCTRL_H_ + +#ifndef _EDITTSCTRL_H_ +#include "gui/worldEditor/editTSCtrl.h" +#endif +#ifndef _UNDO_H_ +#include "util/undo.h" +#endif +#ifndef _DECALINSTANCE_H_ +#include "T3D/decal/decalInstance.h" +#endif + +class GameBase; +class Gizmo; +struct RayInfo; +class DecalInstance; +class DecalData; + +class GuiDecalEditorCtrl : public EditTSCtrl +{ + typedef EditTSCtrl Parent; + + public: + + GuiDecalEditorCtrl(); + ~GuiDecalEditorCtrl(); + + DECLARE_CONOBJECT(GuiDecalEditorCtrl); + + // SimObject + bool onAdd(); + static void initPersistFields(); + static void consoleInit(); + void onEditorDisable(); + + // GuiControl + virtual bool onWake(); + virtual void onSleep(); + virtual void onRender(Point2I offset, const RectI &updateRect); + + // EditTSCtrl + void get3DCursor( GuiCursor *&cursor, bool &visible, const Gui3DMouseEvent &event_ ); + void on3DMouseDown(const Gui3DMouseEvent & event); + void on3DMouseUp(const Gui3DMouseEvent & event); + void on3DMouseMove(const Gui3DMouseEvent & event); + void on3DMouseDragged(const Gui3DMouseEvent & event); + void on3DMouseEnter(const Gui3DMouseEvent & event); + void on3DMouseLeave(const Gui3DMouseEvent & event); + void on3DRightMouseDown(const Gui3DMouseEvent & event); + void on3DRightMouseUp(const Gui3DMouseEvent & event); + void updateGuiInfo(); + void renderScene(const RectI & updateRect); + void renderGui(Point2I offset, const RectI &updateRect); + + /// Find clicked point on "static collision" objects. + bool getRayInfo( const Gui3DMouseEvent &event, RayInfo *rInfo ); + + void selectDecal( DecalInstance *inst ); + void deleteSelectedDecal(); + void deleteDecalDatablock( String lookupName ); + void retargetDecalDatablock( String dbFrom, String dbTo ); + void setMode( String mode, bool sourceShortcut ); + + void forceRedraw( DecalInstance * decalInstance ); + void setGizmoFocus( DecalInstance * decalInstance ); + + public: + + String mMode; + DecalInstance *mSELDecal; + DecalInstance *mHLDecal; + + static bool smRenderDecalPixelSize; + + protected: + + bool mPerformedDragCopy; + + DecalData *mCurrentDecalData; + + Vector mSELEdgeVerts; + Vector mHLEdgeVerts; + + void _renderDecalEdge( const Vector &verts, const ColorI &color ); +}; + +//Decal Instance Create Undo Actions +class DICreateUndoAction : public UndoAction +{ + typedef UndoAction Parent; + +public: + GuiDecalEditorCtrl *mEditor; + +protected: + + /// The captured object state. + DecalInstance mDecalInstance; + S32 mDatablockId; + +public: + + DECLARE_CONOBJECT( DICreateUndoAction ); + static void initPersistFields(); + + DICreateUndoAction( const UTF8* actionName = "Create Decal " ); + virtual ~DICreateUndoAction(); + + void addDecal( DecalInstance decal ); + + // UndoAction + virtual void undo(); + virtual void redo(); +}; + +//Decal Instance Delete Undo Actions +class DIDeleteUndoAction : public UndoAction +{ + typedef UndoAction Parent; + +public: + GuiDecalEditorCtrl *mEditor; + +protected: + + /// The captured object state. + DecalInstance mDecalInstance; + S32 mDatablockId; + +public: + + DECLARE_CONOBJECT( DIDeleteUndoAction ); + static void initPersistFields(); + + DIDeleteUndoAction( const UTF8* actionName = "Delete Decal" ); + virtual ~DIDeleteUndoAction(); + + /// + void deleteDecal( DecalInstance decal ); + + // UndoAction + virtual void undo(); + virtual void redo(); +}; + +//Decal Datablock Delete Undo Actions +class DBDeleteUndoAction : public UndoAction +{ + typedef UndoAction Parent; + +public: + GuiDecalEditorCtrl *mEditor; + S32 mDatablockId; + +protected: + + // The captured decalInstance states + Vector mDecalInstanceVec; + +public: + + DECLARE_CONOBJECT( DBDeleteUndoAction ); + static void initPersistFields(); + + DBDeleteUndoAction( const UTF8* actionName = "Delete Decal Datablock" ); + virtual ~DBDeleteUndoAction(); + + void deleteDecal( DecalInstance decal ); + + // UndoAction + virtual void undo(); + virtual void redo(); +}; + +//Decal Datablock Retarget Undo Actions +class DBRetargetUndoAction : public UndoAction +{ + typedef UndoAction Parent; + +public: + GuiDecalEditorCtrl *mEditor; + S32 mDBFromId; + S32 mDBToId; + +protected: + + // The captured decalInstance states + Vector mDecalInstanceVec; + +public: + + DECLARE_CONOBJECT( DBRetargetUndoAction ); + static void initPersistFields(); + + DBRetargetUndoAction( const UTF8* actionName = "Retarget Decal Datablock" ); + virtual ~DBRetargetUndoAction(); + + void retargetDecal( DecalInstance* decal ); + + // UndoAction + virtual void undo(); + virtual void redo(); +}; + +#endif // _GUIDECALEDITORCTRL_H_ + + + diff --git a/Engine/source/gui/worldEditor/guiMissionArea.cpp b/Engine/source/gui/worldEditor/guiMissionArea.cpp new file mode 100644 index 000000000..91aefc800 --- /dev/null +++ b/Engine/source/gui/worldEditor/guiMissionArea.cpp @@ -0,0 +1,720 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/worldEditor/guiMissionArea.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "gfx/gfxDrawUtil.h" +#include "gfx/primBuilder.h" +#include "gfx/bitmap/gBitmap.h" +#include "gui/3d/guiTSControl.h" +#include "T3D/gameFunctions.h" +#include "terrain/terrData.h" + +namespace { + F32 round_local(F32 val) + { + if(val >= 0.f) + { + F32 floor = mFloor(val); + if((val - floor) >= 0.5f) + return(floor + 1.f); + return(floor); + } + else + { + F32 ceil = mCeil(val); + if((val - ceil) <= -0.5f) + return(ceil - 1.f); + return(ceil); + } + } +} + +IMPLEMENT_CONOBJECT(GuiMissionAreaCtrl); + +ConsoleDocClass( GuiMissionAreaCtrl, + "@brief Visual representation of Mission Area Editor.\n\n" + "@internal" +); + +GuiMissionAreaCtrl::GuiMissionAreaCtrl() +{ + mHandleBitmap = StringTable->EmptyString(); + mHandleTexture = NULL; + mHandleTextureSize = Point2I::Zero; + mHandleTextureHalfSize = Point2F::Zero; + + mSquareBitmap = true; + + mMissionArea = 0; + mTerrainBlock = 0; + + mMissionBoundsColor.set(255,0,0); + mCameraColor.set(255,0,0); + + mBlendStateBlock = NULL; + mSolidStateBlock = NULL; + + mLastHitMode = Handle_None; + mSavedDrag = false; +} + +GuiMissionAreaCtrl::~GuiMissionAreaCtrl() +{ +} + +//------------------------------------------------------------------------------ + +void GuiMissionAreaCtrl::initPersistFields() +{ + addField( "squareBitmap", TypeBool, Offset(mSquareBitmap, GuiMissionAreaCtrl)); + + addField( "handleBitmap", TypeFilename, Offset( mHandleBitmap, GuiMissionAreaCtrl ), + "Bitmap file for the mission area handles.\n"); + + addField( "missionBoundsColor", TypeColorI, Offset(mMissionBoundsColor, GuiMissionAreaCtrl)); + addField( "cameraColor", TypeColorI, Offset(mCameraColor, GuiMissionAreaCtrl)); + + Parent::initPersistFields(); +} + +//------------------------------------------------------------------------------ + +bool GuiMissionAreaCtrl::onAdd() +{ + if(!Parent::onAdd()) + return(false); + + GFXStateBlockDesc desc; + desc.setCullMode(GFXCullNone); + desc.setZReadWrite(false); + desc.setBlend(false, GFXBlendOne, GFXBlendZero); + mSolidStateBlock = GFX->createStateBlock( desc ); + + desc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha); + mBlendStateBlock = GFX->createStateBlock( desc ); + + if (*mHandleBitmap) + { + mHandleTexture = GFXTexHandle( mHandleBitmap, &GFXDefaultPersistentProfile, avar("%s() - mHandleTexture (line %d)", __FUNCTION__, __LINE__) ); + mHandleTextureSize = Point2I( mHandleTexture->getWidth(), mHandleTexture->getHeight() ); + mHandleTextureHalfSize = Point2F(mHandleTextureSize.x, mHandleTextureSize.y) * 0.5f; + } + else + { + mHandleTexture = NULL; + mHandleTextureSize = Point2I::Zero; + mHandleTextureHalfSize = Point2F::Zero; + } + + return(true); +} + +bool GuiMissionAreaCtrl::onWake() +{ + if(!Parent::onWake()) + return(false); + + //mMissionArea = const_cast(MissionArea::getServerObject()); + //if(!bool(mMissionArea)) + // Con::warnf(ConsoleLogEntry::General, "GuiMissionAreaCtrl::onWake: no MissionArea object."); + + //mTerrainBlock = getTerrainObj(); + //if(!bool(mTerrainBlock)) + // Con::warnf(ConsoleLogEntry::General, "GuiMissionAreaCtrl::onWake: no TerrainBlock object."); + + //if ( !bool(mMissionArea) || !bool(mTerrainBlock) ) + // return true; + + updateTerrainBitmap(); + + // make sure mission area is clamped + setArea(getArea()); + + //onUpdate(); + setActive(true); + + return(true); +} + +void GuiMissionAreaCtrl::onSleep() +{ + mTextureObject = NULL; + mMissionArea = 0; + mTerrainBlock = 0; + + Parent::onSleep(); +} + +//------------------------------------------------------------------------------ + +void GuiMissionAreaCtrl::onMouseUp(const GuiEvent & event) +{ + if(!bool(mMissionArea)) + return; + + RectI box; + getScreenMissionArea(box); + S32 hit = getHitHandles(event.mousePoint, box); + + // set the current cursor + //updateCursor(hit); + mLastHitMode = hit; + + if(mSavedDrag) + { + // Let the script get a chance at it. + Con::executef( this, "onMissionAreaModified" ); + } + mSavedDrag = false; +} + +void GuiMissionAreaCtrl::onMouseDown(const GuiEvent & event) +{ + if(!bool(mMissionArea)) + return; + + RectI box; + getScreenMissionArea(box); + + mLastHitMode = getHitHandles(event.mousePoint, box); + //if(mLastHitMode == Handle_Middle) + // setCursor(GrabCursor); + mLastMousePoint = event.mousePoint; +} + +void GuiMissionAreaCtrl::onMouseMove(const GuiEvent & event) +{ + if(!bool(mMissionArea)) + return; + + RectI box; + getScreenMissionArea(box); + S32 hit = getHitHandles(event.mousePoint, box); + + // set the current cursor... + //updateCursor(hit); + mLastHitMode = hit; +} + +void GuiMissionAreaCtrl::onMouseDragged(const GuiEvent & event) +{ + if(!bool(mMissionArea)) + return; + + if(mLastHitMode == Handle_None) + return; + + // If we haven't already saved, + // save an undo action to get back to this state, + // before we make any modifications. + if ( !mSavedDrag ) + { + submitUndo( "Modify Node" ); + mSavedDrag = true; + } + + RectI box; + getScreenMissionArea(box); + Point2I mouseDiff(event.mousePoint.x - mLastMousePoint.x, + event.mousePoint.y - mLastMousePoint.y); + + // what we drag'n? + RectI area = getArea(); + Point2I wp = screenDeltaToWorldDelta(mouseDiff); + + if (mLastHitMode == Handle_Middle) + { + area.point += wp; + } + + if (mLastHitMode & Handle_Left) + { + if ((area.extent.x - wp.x) >= 1) + { + area.point.x += wp.x; + area.extent.x -= wp.x; + } + } + + if (mLastHitMode & Handle_Right) + { + if ((area.extent.x + wp.x) >= 1) + { + area.extent.x += wp.x; + } + } + + if (mLastHitMode & Handle_Bottom) + { + if ((area.extent.y - wp.y) >= 1) + { + area.point.y += wp.y; + area.extent.y -= wp.y; + } + } + + if (mLastHitMode & Handle_Top) + { + if ((area.extent.y + wp.y) >= 1) + { + area.extent.y += wp.y; + } + } + + setArea(area); + mLastMousePoint = event.mousePoint; +} + +void GuiMissionAreaCtrl::onMouseEnter(const GuiEvent &) +{ + mLastHitMode = Handle_None; + //setCursor(DefaultCursor); +} + +void GuiMissionAreaCtrl::onMouseLeave(const GuiEvent &) +{ + mLastHitMode = Handle_None; + //setCursor(DefaultCursor); +} + +//------------------------------------------------------------------------------ + +void GuiMissionAreaCtrl::submitUndo( const UTF8 *name ) +{ + // Grab the mission editor undo manager. + UndoManager *undoMan = NULL; + if ( !Sim::findObject( "EUndoManager", undoMan ) ) + { + Con::errorf( "GuiRiverEditorCtrl::submitUndo() - EUndoManager not found!" ); + return; + } + + // Setup the action. + GuiMissionAreaUndoAction *action = new GuiMissionAreaUndoAction( name ); + + action->mMissionAreaEditor = this; + + action->mObjId = mMissionArea->getId(); + action->mArea = mMissionArea->getArea(); + + undoMan->addAction( action ); +} + +//------------------------------------------------------------------------------ + +void GuiMissionAreaCtrl::updateTerrain() +{ + mTerrainBlock = getTerrainObj(); + updateTerrainBitmap(); +} + +TerrainBlock * GuiMissionAreaCtrl::getTerrainObj() +{ + SimSet * scopeAlwaysSet = Sim::getGhostAlwaysSet(); + for(SimSet::iterator itr = scopeAlwaysSet->begin(); itr != scopeAlwaysSet->end(); itr++) + { + TerrainBlock * terrain = dynamic_cast(*itr); + if(terrain) + return(terrain); + } + return(0); +} + +void GuiMissionAreaCtrl::updateTerrainBitmap() +{ + GBitmap * bitmap = createTerrainBitmap(); + if( bitmap ) + setBitmapHandle( GFXTexHandle( bitmap, &GFXDefaultGUIProfile, true, String("Terrain Bitmap Update") ) ); + else + setBitmap( "" ); +} + +GBitmap * GuiMissionAreaCtrl::createTerrainBitmap() +{ + if(!mTerrainBlock) + return NULL; + + GBitmap * bitmap = new GBitmap(mTerrainBlock->getBlockSize(), mTerrainBlock->getBlockSize(), false, GFXFormatR8G8B8 ); + + if(!bitmap) + return NULL; + + // get the min/max + F32 min, max; + mTerrainBlock->getMinMaxHeight(&min, &max); + + F32 diff = max - min; + F32 colRange = 255.0f / diff; + + // This method allocates it's bitmap above, and does all assignment + // in the following loop. It is not subject to 24-bit -> 32-bit conversion + // problems, because the texture handle creation is where the conversion would + // occur, if it occurs. Since the data in the texture is never read back, and + // the bitmap is deleted after texture-upload, this is not a problem. + for(S32 y = 0; y < mTerrainBlock->getBlockSize() ; y++) + { + for(S32 x = 0; x < mTerrainBlock->getBlockSize(); x++) + { + F32 height; + height = mTerrainBlock->getHeight(Point2I(x, y)); + + U8 col = U8((height - min) * colRange); + ColorI color(col, col, col); + bitmap->setColor(x, y, color); + + } + } + + return(bitmap); +} + +//------------------------------------------------------------------------------ + +void GuiMissionAreaCtrl::setMissionArea( MissionArea* area ) +{ + mMissionArea = area; + if( mMissionArea ) + { + setArea(getArea()); + } +} + +const RectI & GuiMissionAreaCtrl::getArea() +{ + if( !bool(mMissionArea) ) + return(MissionArea::smMissionArea); + + return(mMissionArea->getArea()); +} + +void GuiMissionAreaCtrl::setArea(const RectI & area) +{ + if( bool(mMissionArea) ) + { + mMissionArea->setArea(area); + mMissionArea->inspectPostApply(); + //onUpdate(); + } +} + +//------------------------------------------------------------------------------ + +void GuiMissionAreaCtrl::drawHandle(const Point2F & pos) +{ + Point2F pnt(pos.x-mHandleTextureHalfSize.x, pos.y-mHandleTextureHalfSize.y); + GFX->getDrawUtil()->drawBitmap(mHandleTexture, pnt); +} + +void GuiMissionAreaCtrl::drawHandles(RectI & box) +{ + F32 fillOffset = GFX->getFillConventionOffset(); + + F32 lx = box.point.x + fillOffset, rx = box.point.x + box.extent.x + fillOffset; + F32 cx = (lx + rx) * 0.5f; + F32 by = box.point.y + fillOffset, ty = box.point.y + box.extent.y + fillOffset; + F32 cy = (ty + by) * 0.5f; + + GFX->getDrawUtil()->clearBitmapModulation(); + drawHandle(Point2F(lx, ty)); + drawHandle(Point2F(lx, cy)); + drawHandle(Point2F(lx, by)); + drawHandle(Point2F(rx, ty)); + drawHandle(Point2F(rx, cy)); + drawHandle(Point2F(rx, by)); + drawHandle(Point2F(cx, ty)); + drawHandle(Point2F(cx, by)); +} + +bool GuiMissionAreaCtrl::testWithinHandle(const Point2I & testPoint, S32 handleX, S32 handleY) +{ + S32 dx = testPoint.x - handleX; + S32 dy = testPoint.y - handleY; + return dx <= Handle_Pixel_Size && dx >= -Handle_Pixel_Size && dy <= Handle_Pixel_Size && dy >= -Handle_Pixel_Size; +} + +S32 GuiMissionAreaCtrl::getHitHandles(const Point2I & mousePnt, const RectI & box) +{ + S32 lx = box.point.x, rx = box.point.x + box.extent.x - 1; + S32 cx = (lx + rx) >> 1; + S32 by = box.point.y, ty = box.point.y + box.extent.y - 1; + S32 cy = (ty + by) >> 1; + + if (testWithinHandle(mousePnt, lx, ty)) + return Handle_Left | Handle_Top; + if (testWithinHandle(mousePnt, cx, ty)) + return Handle_Top; + if (testWithinHandle(mousePnt, rx, ty)) + return Handle_Right | Handle_Top; + if (testWithinHandle(mousePnt, lx, by)) + return Handle_Left | Handle_Bottom; + if (testWithinHandle(mousePnt, cx, by)) + return Handle_Bottom; + if (testWithinHandle(mousePnt, rx, by)) + return Handle_Right | Handle_Bottom; + if (testWithinHandle(mousePnt, lx, cy)) + return Handle_Left; + if (testWithinHandle(mousePnt, rx, cy)) + return Handle_Right; + if(mousePnt.x >= lx && mousePnt.x <= rx && + mousePnt.y >= ty && mousePnt.y <= by) + return(Handle_Middle); + + return Handle_None; +} + +//------------------------------------------------------------------------------ + +Point2F GuiMissionAreaCtrl::worldToScreen(const Point2F & pos) +{ + return(Point2F(mCenterPos.x + (pos.x * mScale.x), mCenterPos.y + (pos.y * mScale.y))); +} + +Point2I GuiMissionAreaCtrl::worldToScreen(const Point2I &pos) +{ + return(Point2I(S32(mCenterPos.x + (pos.x * mScale.x)), S32(mCenterPos.y + (pos.y * mScale.y)))); +} + +Point2F GuiMissionAreaCtrl::screenToWorld(const Point2F & pos) +{ + return(Point2F((pos.x - mCenterPos.x) / mScale.x, (pos.y - mCenterPos.y) / mScale.y)); +} + +Point2I GuiMissionAreaCtrl::screenToWorld(const Point2I &pos) +{ + return(Point2I(S32((pos.x - mCenterPos.x) / mScale.x), S32((pos.y - mCenterPos.y) / mScale.y))); +} + +Point2I GuiMissionAreaCtrl::screenDeltaToWorldDelta(const Point2I &screenPoint) +{ + return(Point2I(S32(screenPoint.x / mScale.x), S32(screenPoint.y / mScale.y))); +} + +void GuiMissionAreaCtrl::setupScreenTransform(const Point2I & offset) +{ + const MatrixF & terrMat = mTerrainBlock->getTransform(); + Point3F terrPos; + terrMat.getColumn(3, &terrPos); + terrPos.z = 0; + + F32 terrDim = mTerrainBlock->getWorldBlockSize(); + + const Point2I& extenti = getExtent( ); + Point2F extent( static_cast( extenti.x ), static_cast( extenti.y ) ); + + if(mSquareBitmap) + extent.x > extent.y ? extent.x = extent.y : extent.y = extent.x; + + // We need to negate the y-axis so we are correctly oriented with + // positive y increase up the screen. + mScale.set(extent.x / terrDim, -extent.y / terrDim, 0); + + Point3F terrOffset = -terrPos; + terrOffset.convolve(mScale); + + // We need to add the y extent so we start from the bottom left of the control + // rather than the top left. + mCenterPos.set(terrOffset.x + F32(offset.x), terrOffset.y + F32(offset.y) + extent.y); +} + +void GuiMissionAreaCtrl::getScreenMissionArea(RectI & rect) +{ + RectI area = mMissionArea->getArea(); + Point2F pos = worldToScreen(Point2F(F32(area.point.x), F32(area.point.y))); + Point2F end = worldToScreen(Point2F(F32(area.point.x + area.extent.x), F32(area.point.y + area.extent.y))); + + // + rect.point.x = S32(round_local(pos.x)); + rect.point.y = S32(round_local(pos.y)); + rect.extent.x = S32(round_local(end.x - pos.x)); + rect.extent.y = S32(round_local(end.y - pos.y)); +} + +void GuiMissionAreaCtrl::getScreenMissionArea(RectF & rect) +{ + RectI area = mMissionArea->getArea(); + Point2F pos = worldToScreen(Point2F(F32(area.point.x), F32(area.point.y))); + Point2F end = worldToScreen(Point2F(F32(area.point.x + area.extent.x), F32(area.point.y + area.extent.y))); + + // + rect.point.x = pos.x; + rect.point.y = pos.y; + rect.extent.x = end.x - pos.x; + rect.extent.y = end.y - pos.y; +} + +Point2I GuiMissionAreaCtrl::convertOrigin(const Point2I &pos) +{ + // Convert screen point to our bottom left origin + Point2I pnt = globalToLocalCoord(pos); + const Point2I& extent = getExtent( ); + pnt.y = extent.y - pnt.y; + Point2I pt = localToGlobalCoord(pnt); + return pt; +} + +void GuiMissionAreaCtrl::onRender(Point2I offset, const RectI & updateRect) +{ + + RectI rect(offset, getExtent()); + F32 fillOffset = GFX->getFillConventionOffset(); + + setUpdate(); + + // draw an x + if(!bool(mMissionArea) || !bool(mTerrainBlock)) + { + GFX->setStateBlock(mSolidStateBlock); + PrimBuild::color3i( 0, 0, 0 ); + PrimBuild::begin( GFXLineList, 4 ); + + PrimBuild::vertex2f( rect.point.x + fillOffset, updateRect.point.y + fillOffset ); + PrimBuild::vertex2f( rect.point.x + updateRect.extent.x + fillOffset, updateRect.point.y + updateRect.extent.y + fillOffset ); + PrimBuild::vertex2f( rect.point.x + fillOffset, updateRect.point.y + updateRect.extent.y + fillOffset ); + PrimBuild::vertex2f( rect.point.x + updateRect.extent.x + fillOffset, updateRect.point.y + fillOffset ); + + PrimBuild::end(); + + return; + } + + // + setupScreenTransform(offset); + + // draw the terrain + if(mSquareBitmap) + rect.extent.x > rect.extent.y ? rect.extent.x = rect.extent.y : rect.extent.y = rect.extent.x; + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + drawer->clearBitmapModulation(); + drawer->drawBitmapStretch(mTextureObject, rect, GFXBitmapFlip_Y, GFXTextureFilterLinear, false); + + GFX->setStateBlock(mSolidStateBlock); + drawer->clearBitmapModulation(); + + // draw the reference axis + PrimBuild::begin( GFXLineList, 4 ); + PrimBuild::color3i( 255, 0, 0 ); + PrimBuild::vertex2f( rect.point.x + 5 + fillOffset, rect.point.y + rect.extent.y - 5 + fillOffset ); + PrimBuild::vertex2f( rect.point.x + 25 + fillOffset, rect.point.y + rect.extent.y - 5 + fillOffset ); + PrimBuild::color3i( 0, 255, 0 ); + PrimBuild::vertex2f( rect.point.x + 5 + fillOffset, rect.point.y + rect.extent.y - 5 + fillOffset ); + PrimBuild::vertex2f( rect.point.x + 5 + fillOffset, rect.point.y + rect.extent.y - 25 + fillOffset ); + PrimBuild::end(); + + RectF area; + getScreenMissionArea(area); + + // render the mission area box + PrimBuild::color( mMissionBoundsColor ); + PrimBuild::begin( GFXLineStrip, 5 ); + PrimBuild::vertex2f(area.point.x + fillOffset, area.point.y + fillOffset); + PrimBuild::vertex2f(area.point.x + area.extent.x + fillOffset, area.point.y + fillOffset); + PrimBuild::vertex2f(area.point.x + area.extent.x + fillOffset, area.point.y + area.extent.y + fillOffset); + PrimBuild::vertex2f(area.point.x + fillOffset, area.point.y + area.extent.y + fillOffset); + PrimBuild::vertex2f(area.point.x + fillOffset, area.point.y + fillOffset); + PrimBuild::end(); + + // render the camera + //if(mRenderCamera) + { + CameraQuery camera; + GameProcessCameraQuery(&camera); + + // farplane too far, 90' looks wrong... + camera.fov = mDegToRad(60.f); + camera.farPlane = 500.f; + + // + F32 rot = camera.fov / 2; + + // + VectorF ray; + VectorF projRayA, projRayB; + + ray.set(camera.farPlane * -mSin(rot), camera.farPlane * mCos(rot), 0); + camera.cameraMatrix.mulV(ray, &projRayA); + + ray.set(camera.farPlane * -mSin(-rot), camera.farPlane * mCos(-rot), 0); + camera.cameraMatrix.mulV(ray, &projRayB); + + Point3F camPos; + camera.cameraMatrix.getColumn(3, &camPos); + + Point2F s = worldToScreen(Point2F(camPos.x, camPos.y)); + Point2F e1 = worldToScreen(Point2F(camPos.x + projRayA.x, camPos.y + projRayA.y)); + Point2F e2 = worldToScreen(Point2F(camPos.x + projRayB.x, camPos.y + projRayB.y)); + + PrimBuild::color( mCameraColor ); + PrimBuild::begin( GFXLineList, 4 ); + PrimBuild::vertex2f( s.x + fillOffset, s.y + fillOffset ); + PrimBuild::vertex2f( e1.x + fillOffset, e1.y + fillOffset ); + PrimBuild::vertex2f( s.x + fillOffset, s.y + fillOffset ); + PrimBuild::vertex2f( e2.x + fillOffset, e2.y + fillOffset ); + PrimBuild::end(); + } + + // render the handles + RectI iArea; + getScreenMissionArea(iArea); + drawHandles(iArea); + + renderChildControls(offset, updateRect); +} + +//------------------------------------------------------------------------------ + +DefineEngineMethod( GuiMissionAreaCtrl, setMissionArea, void, ( MissionArea* area ),, + "@brief Set the MissionArea to edit.\n\n") +{ + object->setMissionArea( area ); +} + +DefineEngineMethod( GuiMissionAreaCtrl, updateTerrain, void, ( ),, + "@brief Update the terrain bitmap.\n\n") +{ + object->updateTerrain(); +} + +//------------------------------------------------------------------------------ + +void GuiMissionAreaUndoAction::undo() +{ + MissionArea *ma = NULL; + if ( !Sim::findObject( mObjId, ma ) ) + return; + + // Temporarily save the MissionArea's current data. + RectI area = ma->getArea(); + + // Restore the MissionArea properties saved in the UndoAction + ma->setArea( mArea ); + ma->inspectPostApply(); + + // Now save the previous Mission data in this UndoAction + // since an undo action must become a redo action and vice-versa + mArea = area; + + // Let the script get a chance at it. + Con::executef( mMissionAreaEditor, "onUndo" ); +} diff --git a/Engine/source/gui/worldEditor/guiMissionArea.h b/Engine/source/gui/worldEditor/guiMissionArea.h new file mode 100644 index 000000000..fb1d1cd85 --- /dev/null +++ b/Engine/source/gui/worldEditor/guiMissionArea.h @@ -0,0 +1,158 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIMISSIONAREA_H_ +#define _GUIMISSIONAREA_H_ + +#ifndef _GUIBITMAPCTRL_H_ +#include "gui/controls/guiBitmapCtrl.h" +#endif +#ifndef _GUITYPES_H_ +#include "gui/guiTypes.h" +#endif +#ifndef _MISSIONAREA_H_ +#include "T3D/missionArea.h" +#endif +#ifndef _UNDO_H_ +#include "util/undo.h" +#endif + +class GBitmap; +class TerrainBlock; + +class GuiMissionAreaCtrl : public GuiBitmapCtrl +{ + typedef GuiBitmapCtrl Parent; + +protected: + enum HandleInfo + { + // Handle in use + Handle_None = 0, + Handle_Left = BIT(0), + Handle_Right = BIT(1), + Handle_Top = BIT(2), + Handle_Bottom = BIT(3), + Handle_Middle = BIT(4), // Used to drag the whole area + + Handle_Pixel_Size = 3, + }; + + SimObjectPtr mMissionArea; + SimObjectPtr mTerrainBlock; + + GFXStateBlockRef mBlendStateBlock; + GFXStateBlockRef mSolidStateBlock; + + StringTableEntry mHandleBitmap; + GFXTexHandle mHandleTexture; + Point2I mHandleTextureSize; + Point2F mHandleTextureHalfSize; + + ColorI mMissionBoundsColor; + ColorI mCameraColor; + + bool mSquareBitmap; + + VectorF mScale; + Point2F mCenterPos; + + S32 mLastHitMode; + Point2I mLastMousePoint; + bool mSavedDrag; + + void submitUndo( const UTF8 *name = "Action" ); + + TerrainBlock * getTerrainObj(); + GBitmap * createTerrainBitmap(); + void updateTerrainBitmap(); + + //void onUpdate(); + + void setupScreenTransform(const Point2I & offset); + + Point2F worldToScreen(const Point2F &); + Point2F screenToWorld(const Point2F &); + + Point2I worldToScreen(const Point2I &); + Point2I screenToWorld(const Point2I &); + + Point2I screenDeltaToWorldDelta(const Point2I &screenPoint); + + void getScreenMissionArea(RectI & rect); + void getScreenMissionArea(RectF & rect); + + Point2I convertOrigin(const Point2I &); + + void drawHandle(const Point2F & pos); + void drawHandles(RectI & box); + + bool testWithinHandle(const Point2I & testPoint, S32 handleX, S32 handleY); + S32 getHitHandles(const Point2I & mousePnt, const RectI & box); + +public: + GuiMissionAreaCtrl(); + virtual ~GuiMissionAreaCtrl(); + + DECLARE_CONOBJECT(GuiMissionAreaCtrl); + + // SimObject + bool onAdd(); + static void initPersistFields(); + + // GuiControl + void onRender(Point2I offset, const RectI &updateRect); + bool onWake(); + void onSleep(); + + virtual void onMouseUp(const GuiEvent & event); + virtual void onMouseDown(const GuiEvent & event); + virtual void onMouseMove(const GuiEvent & event); + virtual void onMouseDragged(const GuiEvent & event); + virtual void onMouseEnter(const GuiEvent & event); + virtual void onMouseLeave(const GuiEvent & event); + + void setMissionArea( MissionArea* area ); + void updateTerrain(); + + const RectI & getArea(); + void setArea(const RectI & area); +}; + +class GuiMissionAreaUndoAction : public UndoAction +{ + public: + + GuiMissionAreaUndoAction( const UTF8* actionName ) : UndoAction( actionName ) + { + } + + GuiMissionAreaCtrl *mMissionAreaEditor; + + SimObjectId mObjId; + RectI mArea; + + virtual void undo(); + virtual void redo() { undo(); } +}; + +#endif // _GUIMISSIONAREA_H_ diff --git a/Engine/source/gui/worldEditor/guiMissionAreaEditor.cpp b/Engine/source/gui/worldEditor/guiMissionAreaEditor.cpp new file mode 100644 index 000000000..5253a6fb4 --- /dev/null +++ b/Engine/source/gui/worldEditor/guiMissionAreaEditor.cpp @@ -0,0 +1,116 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/worldEditor/guiMissionAreaEditor.h" +#include "gui/core/guiCanvas.h" + +IMPLEMENT_CONOBJECT(GuiMissionAreaEditorCtrl); + +ConsoleDocClass( GuiMissionAreaEditorCtrl, + "@brief Specialized GUI used for editing the MissionArea in a level\n\n" + "Editor use only.\n\n" + "@internal" +); + +GuiMissionAreaEditorCtrl::GuiMissionAreaEditorCtrl() +{ +} + +GuiMissionAreaEditorCtrl::~GuiMissionAreaEditorCtrl() +{ +} + +bool GuiMissionAreaEditorCtrl::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + return true; +} + +void GuiMissionAreaEditorCtrl::initPersistFields() +{ + Parent::initPersistFields(); +} + +void GuiMissionAreaEditorCtrl::get3DCursor( GuiCursor *&cursor, + bool &visible, + const Gui3DMouseEvent &event_ ) +{ + //cursor = mAddNodeCursor; + //visible = false; + + cursor = NULL; + visible = false; + + GuiCanvas *root = getRoot(); + if ( !root ) + return; + + S32 currCursor = PlatformCursorController::curArrow; + + if ( root->mCursorChanged == currCursor ) + return; + + PlatformWindow *window = root->getPlatformWindow(); + PlatformCursorController *controller = window->getCursorController(); + + // We've already changed the cursor, + // so set it back before we change it again. + if( root->mCursorChanged != -1) + controller->popCursor(); + + // Now change the cursor shape + controller->pushCursor(currCursor); + root->mCursorChanged = currCursor; +} + +void GuiMissionAreaEditorCtrl::setSelectedMissionArea( MissionArea *missionArea ) +{ + mSelMissionArea = missionArea; + + if ( mSelMissionArea != NULL ) + Con::executef( this, "onMissionAreaSelected", missionArea->getIdString() ); + else + Con::executef( this, "onMissionAreaSelected" ); +} + +ConsoleMethod( GuiMissionAreaEditorCtrl, setSelectedMissionArea, void, 2, 3, "" ) +{ + if ( argc == 2 ) + object->setSelectedMissionArea(NULL); + else + { + MissionArea *missionArea = NULL; + if ( Sim::findObject( argv[2], missionArea ) ) + object->setSelectedMissionArea(missionArea); + } +} + +ConsoleMethod( GuiMissionAreaEditorCtrl, getSelectedMissionArea, const char*, 2, 2, "" ) +{ + MissionArea *missionArea = object->getSelectedMissionArea(); + if ( !missionArea ) + return NULL; + + return missionArea->getIdString(); +} diff --git a/Engine/source/gui/worldEditor/guiMissionAreaEditor.h b/Engine/source/gui/worldEditor/guiMissionAreaEditor.h new file mode 100644 index 000000000..c934897a6 --- /dev/null +++ b/Engine/source/gui/worldEditor/guiMissionAreaEditor.h @@ -0,0 +1,57 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUIMISSIONAREAEDITORCTRL_H_ +#define _GUIMISSIONAREAEDITORCTRL_H_ + +#ifndef _EDITTSCTRL_H_ +#include "gui/worldEditor/editTSCtrl.h" +#endif +#ifndef _MISSIONAREA_H_ +#include "T3D/missionArea.h" +#endif + +class GuiMissionAreaEditorCtrl : public EditTSCtrl +{ + typedef EditTSCtrl Parent; + +protected: + SimObjectPtr mSelMissionArea; + +public: + GuiMissionAreaEditorCtrl(); + virtual ~GuiMissionAreaEditorCtrl(); + + DECLARE_CONOBJECT(GuiMissionAreaEditorCtrl); + + // SimObject + bool onAdd(); + static void initPersistFields(); + + // EditTSCtrl + void get3DCursor( GuiCursor *&cursor, bool &visible, const Gui3DMouseEvent &event_ ); + + void setSelectedMissionArea( MissionArea *missionArea ); + MissionArea* getSelectedMissionArea() { return mSelMissionArea; }; +}; + +#endif // _GUIMISSIONAREAEDITORCTRL_H_ diff --git a/Engine/source/gui/worldEditor/guiTerrPreviewCtrl.cpp b/Engine/source/gui/worldEditor/guiTerrPreviewCtrl.cpp new file mode 100644 index 000000000..3477d974e --- /dev/null +++ b/Engine/source/gui/worldEditor/guiTerrPreviewCtrl.cpp @@ -0,0 +1,366 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/console.h" +#include "console/consoleTypes.h" +#include "terrain/terrData.h" +#include "gui/worldEditor/guiTerrPreviewCtrl.h" +#include "gfx/primBuilder.h" +#include "T3D/gameFunctions.h" + +IMPLEMENT_CONOBJECT(GuiTerrPreviewCtrl); + +ConsoleDocClass( GuiTerrPreviewCtrl, + "@brief Very old GUI used for terrain preview\n\n" + "Deprecated\n\n" + "@internal" +); + +GuiTerrPreviewCtrl::GuiTerrPreviewCtrl(void) : mTerrainEditor(NULL), mTerrainSize(2048.0f) +{ + mRoot.set( 0, 0 ); + mOrigin.set( 0, 0 ); + mWorldScreenCenter.set( mTerrainSize*0.5f, mTerrainSize*0.5f ); + mControlsStateBlock = NULL; + mTerrainBitmapStateBlock = NULL; +} + +bool GuiTerrPreviewCtrl::onAdd() +{ + if(Parent::onAdd() == false) + { + return false; + } + + SimObject* inTerrEditor = Sim::findObject("ETerrainEditor"); + if(!inTerrEditor) + { + Con::errorf(ConsoleLogEntry::General, "TerrainEditor::onAdd: failed to load Terrain Editor"); + return false; + } + + mTerrainEditor = dynamic_cast(inTerrEditor); + + GFXStateBlockDesc desc; + + desc.setBlend(false, GFXBlendOne, GFXBlendZero); + + desc.samplersDefined = true; + desc.samplers[0].addressModeU = GFXAddressWrap; + desc.samplers[0].addressModeV = GFXAddressWrap; + desc.samplers[0].textureColorOp = GFXTOPSelectARG1; + desc.samplers[0].colorArg1 = GFXTATexture; + desc.setCullMode(GFXCullNone); + desc.setZReadWrite(false); + + mTerrainBitmapStateBlock = GFX->createStateBlock(desc); + + desc.samplers[0].textureColorOp = GFXTOPDisable; + + mControlsStateBlock = GFX->createStateBlock(desc); + + return true; +} + +void GuiTerrPreviewCtrl::initPersistFields() +{ + Parent::initPersistFields(); +} + + +ConsoleMethod( GuiTerrPreviewCtrl, reset, void, 2, 2, "Reset the view of the terrain.") +{ + object->reset(); +} + +ConsoleMethod( GuiTerrPreviewCtrl, setRoot, void, 2, 2, "Add the origin to the root and reset the origin.") +{ + object->setRoot(); +} + +ConsoleMethod( GuiTerrPreviewCtrl, getRoot, const char *, 2, 2, "Return a Point2F representing the position of the root.") +{ + Point2F p = object->getRoot(); + + static char rootbuf[32]; + dSprintf(rootbuf,sizeof(rootbuf),"%g %g", p.x, -p.y); + return rootbuf; +} + +ConsoleMethod( GuiTerrPreviewCtrl, setOrigin, void, 4, 4, "(float x, float y)" + "Set the origin of the view.") +{ + object->setOrigin( Point2F( dAtof(argv[2]), -dAtof(argv[3]) ) ); +} + +ConsoleMethod( GuiTerrPreviewCtrl, getOrigin, const char*, 2, 2, "Return a Point2F containing the position of the origin.") +{ + Point2F p = object->getOrigin(); + + static char originbuf[32]; + dSprintf(originbuf,sizeof(originbuf),"%g %g", p.x, -p.y); + return originbuf; +} + +ConsoleMethod( GuiTerrPreviewCtrl, getValue, const char*, 2, 2, "Returns a 4-tuple containing: root_x root_y origin_x origin_y") +{ + Point2F r = object->getRoot(); + Point2F o = object->getOrigin(); + + static char valuebuf[64]; + dSprintf(valuebuf,sizeof(valuebuf),"%g %g %g %g", r.x, -r.y, o.x, -o.y); + return valuebuf; +} + +ConsoleMethod( GuiTerrPreviewCtrl, setValue, void, 3, 3, "Accepts a 4-tuple in the same form as getValue returns.\n\n" + "@see GuiTerrPreviewCtrl::getValue()") +{ + Point2F r,o; + dSscanf(argv[2],"%g %g %g %g", &r.x, &r.y, &o.x, &o.y); + r.y = -r.y; + o.y = -o.y; + object->reset(); + object->setRoot(r); + object->setOrigin(o); +} + +bool GuiTerrPreviewCtrl::onWake() +{ + if (! Parent::onWake()) + return false; + + return true; +} + +void GuiTerrPreviewCtrl::onSleep() +{ + Parent::onSleep(); +} + +void GuiTerrPreviewCtrl::setBitmap(const GFXTexHandle &handle) +{ + mTextureHandle = handle; +} + + +void GuiTerrPreviewCtrl::reset() +{ + mRoot.set(0,0); + mOrigin.set(0,0); +} + +void GuiTerrPreviewCtrl::setRoot() +{ + mRoot += mOrigin; + mOrigin.set(0,0); +} + +void GuiTerrPreviewCtrl::setRoot(const Point2F &p) +{ + mRoot = p; +} + +void GuiTerrPreviewCtrl::setOrigin(const Point2F &p) +{ + mOrigin = p; +} + + +Point2F& GuiTerrPreviewCtrl::wrap(const Point2F &p) +{ + static Point2F result; + result = p; + + while (result.x < 0.0f) + result.x += mTerrainSize; + while (result.x > mTerrainSize) + result.x -= mTerrainSize; + while (result.y < 0.0f) + result.y += mTerrainSize; + while (result.y > mTerrainSize) + result.y -= mTerrainSize; + + return result; +} + +Point2F& GuiTerrPreviewCtrl::worldToTexture(const Point2F &p) +{ + static Point2F result; + result = wrap( p + mRoot ) / mTerrainSize; + return result; +} + + +Point2F& GuiTerrPreviewCtrl::worldToCtrl(const Point2F &p) +{ + static Point2F result; + result = wrap( p - mCamera - mWorldScreenCenter ); + result *= getWidth() / mTerrainSize; + return result; +} + + +void GuiTerrPreviewCtrl::onPreRender() +{ + setUpdate(); +} + +void GuiTerrPreviewCtrl::onRender(Point2I offset, const RectI &updateRect) +{ + CameraQuery query; + GameProcessCameraQuery(&query); + Point3F cameraRot; + TerrainBlock *terrBlock = NULL; + + MatrixF matrix = query.cameraMatrix; + matrix.getColumn(3,&cameraRot); // get Camera translation + mCamera.set(cameraRot.x, -cameraRot.y); + matrix.getRow(1,&cameraRot); // get camera rotation + + if (mTerrainEditor != NULL) + terrBlock = mTerrainEditor->getActiveTerrain(); + + if (!terrBlock) + return; + + for(U32 i = 0; i < GFX->getNumSamplers(); i++) + GFX->setTexture(i, NULL); + + GFX->disableShaders(); + + Point2F terrPos(terrBlock->getPosition().x, terrBlock->getPosition().y); + + mTerrainSize = terrBlock->getWorldBlockSize(); + + + //----------------------------------------- RENDER the Terrain Bitmap + if (mTextureHandle) + { + + GFXTextureObject *texture = (GFXTextureObject*)mTextureHandle; + if (texture) + { + //GFX->setLightingEnable(false); + GFX->setStateBlock(mTerrainBitmapStateBlock); + GFX->setTexture(0, texture); + + Point2F screenP1(offset.x - 0.5f, offset.y + 0.5f); + Point2F screenP2(offset.x + getWidth() - 0.5f, offset.y + getWidth() + 0.5f); + Point2F textureP1( worldToTexture( mCamera - terrPos ) - Point2F(0.5f, 0.5f)); + Point2F textureP2(textureP1 + Point2F(1.0f, 1.0f)); + + // the texture if flipped horz to reflect how the terrain is really drawn + PrimBuild::color3f(1.0f, 1.0f, 1.0f); + PrimBuild::begin(GFXTriangleFan, 4); + PrimBuild::texCoord2f(textureP1.x, textureP2.y); + PrimBuild::vertex2f(screenP1.x, screenP2.y); // left bottom + + + PrimBuild::texCoord2f(textureP2.x, textureP2.y); + PrimBuild::vertex2f(screenP2.x, screenP2.y); // right bottom + PrimBuild::texCoord2f(textureP2.x, textureP1.y); + PrimBuild::vertex2f(screenP2.x, screenP1.y); // right top + + PrimBuild::texCoord2f(textureP1.x, textureP1.y); + PrimBuild::vertex2f(screenP1.x, screenP1.y); // left top + PrimBuild::end(); + } + } + //Draw blank texture + else + { + RectI rect(offset.x, offset.y, getWidth(), getHeight()); + GFX->getDrawUtil()->drawRect(rect, ColorI(0,0,0)); + } + + GFX->setStateBlock(mControlsStateBlock); + + //----------------------------------------- RENDER the '+' at the center of the Block + + PrimBuild::color4f(1.0f, 1.0f, 1.0f, 1.0f); + Point2F center( worldToCtrl(terrPos + Point2F(mTerrainSize * 0.5f, mTerrainSize * 0.5f)) ); + S32 y; + for (y=-1; y<=1; y++) + { + F32 yoffset = offset.y + y*256.0f; + for (S32 x=-1; x<=1; x++) + { + F32 xoffset = offset.x + x*256.0f; + PrimBuild::begin(GFXLineList, 4); + PrimBuild::vertex2f(xoffset + center.x, yoffset + center.y-5); + PrimBuild::vertex2f(xoffset + center.x, yoffset + center.y+6); + PrimBuild::vertex2f(xoffset + center.x-5, yoffset + center.y); + PrimBuild::vertex2f(xoffset + center.x+6, yoffset + center.y); + PrimBuild::end(); + } + } + + //----------------------------------------- RENDER the Block Corners + Point2F cornerf( worldToCtrl(terrPos) + Point2F(0.125f, 0.125f)); + Point2I corner=Point2I((S32)cornerf.x,(S32)cornerf.y); + for (y=-1; y<=1; y++) + { + S32 yoffset = offset.y + y*256; + for (S32 x=-1; x<=1; x++) + { + S32 xoffset = offset.x + x*256; + PrimBuild::begin(GFXLineStrip, 3); + PrimBuild::color4f(1.0f, 1.0f, 1.0f, 0.3f); + PrimBuild::vertex2i(xoffset + corner.x, yoffset + corner.y-128); + PrimBuild::color4f(1.0f, 1.0f, 1.0f, 0.7f); + PrimBuild::vertex2i(xoffset + corner.x, yoffset + corner.y); + PrimBuild::color4f(1.0f, 1.0f, 1.0f, 0.3f); + PrimBuild::vertex2i(xoffset + corner.x+128, yoffset + corner.y); + PrimBuild::end(); + PrimBuild::begin(GFXLineStrip, 3); + PrimBuild::color4f(1.0f, 1.0f, 1.0f, 0.3f); + PrimBuild::vertex2i(xoffset + corner.x, yoffset + corner.y+128); + PrimBuild::color4f(1.0f, 1.0f, 1.0f, 0.7f); + PrimBuild::vertex2i(xoffset + corner.x, yoffset + corner.y); + PrimBuild::color4f(1.0f, 1.0f, 1.0f, 0.3f); + PrimBuild::vertex2i(xoffset + corner.x-128, yoffset + corner.y); + PrimBuild::end(); + } + } + + //----------------------------------------- RENDER the Viewcone + Point2F pointA(cameraRot.x * -40, cameraRot.y * -40); + Point2F pointB(-pointA.y, pointA.x); + + F32 tann = mTan(0.5f); + Point2F point1( pointA + pointB * tann ); + Point2F point2( pointA - pointB * tann ); + + center.set((F32)(offset.x + getWidth() / 2), (F32)(offset.y + getHeight() / 2 )); + PrimBuild::begin(GFXLineStrip, 3); + PrimBuild::color4f(1.0f, 0.0f, 0.0f, 0.7f); + PrimBuild::vertex2i((S32)(center.x + point1.x), (S32)(center.y + point1.y)); + PrimBuild::color4f(1.0f, 0.0f, 0.0f, 1.0f); + PrimBuild::vertex2i((S32)center.x,(S32)center.y); + PrimBuild::color4f(1.0f, 0.0f, 0.0f, 0.7f); + PrimBuild::vertex2i((S32)(center.x + point2.x), (S32)(center.y + point2.y)); + PrimBuild::end(); + + + renderChildControls(offset, updateRect); +} + diff --git a/Engine/source/gui/worldEditor/guiTerrPreviewCtrl.h b/Engine/source/gui/worldEditor/guiTerrPreviewCtrl.h new file mode 100644 index 000000000..68f670f82 --- /dev/null +++ b/Engine/source/gui/worldEditor/guiTerrPreviewCtrl.h @@ -0,0 +1,88 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUITERRPREVIEWCTRL_H_ +#define _GUITERRPREVIEWCTRL_H_ + +#ifndef _GUICONTROL_H_ +#include "gui/core/guiControl.h" +#endif +#ifndef _GUITSCONTROL_H_ +#include "gui/3d/guiTSControl.h" +#endif +#ifndef _GFX_GFXDRAWER_H_ +#include "gfx/gfxDrawUtil.h" +#endif +#ifndef _TERRAINEDITOR_H_ +#include "gui/worldEditor/terrainEditor.h" +#endif + + +class GuiTerrPreviewCtrl : public GuiControl +{ +private: + typedef GuiControl Parent; + GFXTexHandle mTextureHandle; + GFXStateBlockRef mTerrainBitmapStateBlock; + GFXStateBlockRef mControlsStateBlock; + Point2F mRoot; + Point2F mOrigin; + Point2F mWorldScreenCenter; + Point2F mCamera; + F32 mTerrainSize; + + TerrainEditor* mTerrainEditor; + + Point2F& wrap(const Point2F &p); + Point2F& worldToTexture(const Point2F &p); + Point2F& worldToCtrl(const Point2F &p); + + +public: + //creation methods + DECLARE_CONOBJECT(GuiTerrPreviewCtrl); + DECLARE_CATEGORY( "Gui Editor" ); + GuiTerrPreviewCtrl(); + static void initPersistFields(); + + //Parental methods + bool onWake(); + void onSleep(); + bool onAdd(); + + void setBitmap(const GFXTexHandle&); + + void reset(); + void setRoot(); + void setRoot(const Point2F &root); + void setOrigin(const Point2F &origin); + const Point2F& getRoot() { return mRoot; } + const Point2F& getOrigin() { return mOrigin; } + + //void setValue(const Point2F *center, const Point2F *camera); + //const char *getScriptValue(); + void onPreRender(); + void onRender(Point2I offset, const RectI &updateRect); +}; + + +#endif diff --git a/Engine/source/gui/worldEditor/tSelection.cpp b/Engine/source/gui/worldEditor/tSelection.cpp new file mode 100644 index 000000000..508a3be7c --- /dev/null +++ b/Engine/source/gui/worldEditor/tSelection.cpp @@ -0,0 +1,25 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/worldEditor/tSelection.h" + diff --git a/Engine/source/gui/worldEditor/tSelection.h b/Engine/source/gui/worldEditor/tSelection.h new file mode 100644 index 000000000..e42ce648a --- /dev/null +++ b/Engine/source/gui/worldEditor/tSelection.h @@ -0,0 +1,91 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TSELECTION_H_ +#define _TSELECTION_H_ + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif + + +template +class Selection : public Vector +{ +public: + + // Use explicit specialization to define these for your type. + MatrixF getOrientation() { return MatrixF(); } + Point3F getOrigin() { return Point3F(); } + Point3F getScale() { return Point3F(); } + + void offset( const Point3F &delta ); + void rotate( const EulerF &delta ); + void scale( const Point3F &delta ); + +protected: + + // Use explicit specialization to define these for your type. + virtual void offsetObject( T &object, const Point3F &delta ) {} + virtual void rotateObject( T &object, const EulerF &delta, const Point3F &origin ) {} + virtual void scaleObject( T &object, const Point3F &delta ) {} + +protected: + + //Point3F mCentroid; + //Point3F mBoxCentroid; + //Box3F mBoxBounds; + //bool mCentroidValid; +}; + + +template inline void Selection::offset( const Point3F &delta ) +{ + typename Selection::iterator itr = this->begin(); + + for ( ; itr != this->end(); itr++ ) + offsetObject( *itr, delta ); +} + +template inline void Selection::rotate( const EulerF &delta ) +{ + typename Selection::iterator itr = this->begin(); + Point3F origin = getOrigin(); + + for ( ; itr != this->end(); itr++ ) + rotateObject( *itr, delta, origin ); +} + +template inline void Selection::scale( const Point3F &delta ) +{ + // Can only scale a single selection. + if ( this->size() != 1 ) + return; + + scaleObject( this->mArray[0], delta ); +} + +#endif // _TSELECTION_H_ \ No newline at end of file diff --git a/Engine/source/gui/worldEditor/terrainActions.cpp b/Engine/source/gui/worldEditor/terrainActions.cpp new file mode 100644 index 000000000..f05847b8c --- /dev/null +++ b/Engine/source/gui/worldEditor/terrainActions.cpp @@ -0,0 +1,795 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/worldEditor/terrainActions.h" + +#include "gui/core/guiCanvas.h" + +//------------------------------------------------------------------------------ + +void SelectAction::process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type) +{ + if(sel == mTerrainEditor->getCurrentSel()) + return; + + if(type == Process) + return; + + if(selChanged) + { + if(event.modifier & SI_MULTISELECT) + { + for(U32 i = 0; i < sel->size(); i++) + mTerrainEditor->getCurrentSel()->remove((*sel)[i]); + } + else + { + for(U32 i = 0; i < sel->size(); i++) + { + GridInfo gInfo; + if(mTerrainEditor->getCurrentSel()->getInfo((*sel)[i].mGridPoint.gridPos, gInfo)) + { + if(!gInfo.mPrimarySelect) + gInfo.mPrimarySelect = (*sel)[i].mPrimarySelect; + + if(gInfo.mWeight < (*sel)[i].mWeight) + gInfo.mWeight = (*sel)[i].mWeight; + + mTerrainEditor->getCurrentSel()->setInfo(gInfo); + } + else + mTerrainEditor->getCurrentSel()->add((*sel)[i]); + } + } + } +} + +void DeselectAction::process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type) +{ + if(sel == mTerrainEditor->getCurrentSel()) + return; + + if(type == Process) + return; + + if(selChanged) + { + for(U32 i = 0; i < sel->size(); i++) + mTerrainEditor->getCurrentSel()->remove((*sel)[i]); + } +} + +//------------------------------------------------------------------------------ + +void SoftSelectAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type type) +{ + TerrainBlock *terrBlock = mTerrainEditor->getActiveTerrain(); + if ( !terrBlock ) + return; + + // allow process of current selection + Selection tmpSel; + if(sel == mTerrainEditor->getCurrentSel()) + { + tmpSel = *sel; + sel = &tmpSel; + } + + if(type == Begin || type == Process) + mFilter.set(1, &mTerrainEditor->mSoftSelectFilter); + + // + if(selChanged) + { + F32 radius = mTerrainEditor->mSoftSelectRadius; + if(radius == 0.f) + return; + + S32 squareSize = terrBlock->getSquareSize(); + U32 offset = U32(radius / F32(squareSize)) + 1; + + for(U32 i = 0; i < sel->size(); i++) + { + GridInfo & info = (*sel)[i]; + + info.mPrimarySelect = true; + info.mWeight = mFilter.getValue(0); + + if(!mTerrainEditor->getCurrentSel()->add(info)) + mTerrainEditor->getCurrentSel()->setInfo(info); + + Point2F infoPos((F32)info.mGridPoint.gridPos.x, (F32)info.mGridPoint.gridPos.y); + + // + for(S32 x = info.mGridPoint.gridPos.x - offset; x < info.mGridPoint.gridPos.x + (offset << 1); x++) + for(S32 y = info.mGridPoint.gridPos.y - offset; y < info.mGridPoint.gridPos.y + (offset << 1); y++) + { + // + Point2F pos((F32)x, (F32)y); + + F32 dist = Point2F(pos - infoPos).len() * F32(squareSize); + + if(dist > radius) + continue; + + F32 weight = mFilter.getValue(dist / radius); + + // + GridInfo gInfo; + GridPoint gridPoint = info.mGridPoint; + gridPoint.gridPos.set(x, y); + + if(mTerrainEditor->getCurrentSel()->getInfo(Point2I(x, y), gInfo)) + { + if(gInfo.mPrimarySelect) + continue; + + if(gInfo.mWeight < weight) + { + gInfo.mWeight = weight; + mTerrainEditor->getCurrentSel()->setInfo(gInfo); + } + } + else + { + Vector gInfos; + mTerrainEditor->getGridInfos(gridPoint, gInfos); + + for (U32 z = 0; z < gInfos.size(); z++) + { + gInfos[z].mWeight = weight; + gInfos[z].mPrimarySelect = false; + mTerrainEditor->getCurrentSel()->add(gInfos[z]); + } + } + } + } + } +} + +//------------------------------------------------------------------------------ + +void OutlineSelectAction::process(Selection * sel, const Gui3DMouseEvent & event, bool, Type type) +{ + TORQUE_UNUSED(sel); TORQUE_UNUSED(event); TORQUE_UNUSED(type); + switch(type) + { + case Begin: + if(event.modifier & SI_SHIFT) + break; + + mTerrainEditor->getCurrentSel()->reset(); + break; + + case End: + case Update: + + default: + return; + } + + mLastEvent = event; +} + +//------------------------------------------------------------------------------ + +void PaintMaterialAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type) +{ + S32 mat = mTerrainEditor->getPaintMaterialIndex(); + if ( !selChanged || mat < 0 ) + return; + + const bool slopeLimit = mTerrainEditor->mSlopeMinAngle > 0.0f || mTerrainEditor->mSlopeMaxAngle < 90.0f; + const F32 minSlope = mSin( mDegToRad( 90.0f - mTerrainEditor->mSlopeMinAngle ) ); + const F32 maxSlope = mSin( mDegToRad( 90.0f - mTerrainEditor->mSlopeMaxAngle ) ); + + const TerrainBlock *terrain = mTerrainEditor->getActiveTerrain(); + const F32 squareSize = terrain->getSquareSize(); + + Point2F p; + Point3F norm; + + + for( U32 i = 0; i < sel->size(); i++ ) + { + GridInfo &inf = (*sel)[i]; + + if ( slopeLimit ) + { + p.x = inf.mGridPoint.gridPos.x * squareSize; + p.y = inf.mGridPoint.gridPos.y * squareSize; + if ( !terrain->getNormal( p, &norm, true ) ) + continue; + + if ( norm.z > minSlope || + norm.z < maxSlope ) + continue; + } + + // If grid is already set to our material, or it is an + // empty grid spot, then skip painting. + if ( inf.mMaterial == mat || inf.mMaterial == U8_MAX ) + continue; + + if ( mRandF() > mTerrainEditor->getBrushPressure() ) + continue; + + inf.mMaterialChanged = true; + mTerrainEditor->getUndoSel()->add(inf); + + // Painting is really simple now... set the one mat index. + inf.mMaterial = mat; + mTerrainEditor->setGridInfo(inf, true); + } + + mTerrainEditor->scheduleMaterialUpdate(); +} + +//------------------------------------------------------------------------------ + +void ClearMaterialsAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type) +{ + if(selChanged) + { + for(U32 i = 0; i < sel->size(); i++) + { + GridInfo &inf = (*sel)[i]; + + mTerrainEditor->getUndoSel()->add(inf); + inf.mMaterialChanged = true; + + // Reset to the first texture layer. + inf.mMaterial = 0; + mTerrainEditor->setGridInfo(inf); + } + mTerrainEditor->scheduleMaterialUpdate(); + } +} + +//------------------------------------------------------------------------------ + +void RaiseHeightAction::process( Selection *sel, const Gui3DMouseEvent &evt, bool selChanged, Type type ) +{ + // ok the raise height action is our "dirt pour" action + // only works on brushes... + + Brush *brush = dynamic_cast(sel); + if ( !brush ) + return; + + if ( type == End ) + return; + + Point2I brushPos = brush->getPosition(); + Point2I brushSize = brush->getSize(); + GridPoint brushGridPoint = brush->getGridPoint(); + + Vector cur; // the height at the brush position + mTerrainEditor->getGridInfos(brushGridPoint, cur); + + if ( cur.size() == 0 ) + return; + + // we get 30 process actions per second (at least) + F32 heightAdjust = mTerrainEditor->mAdjustHeightVal / 30; + // nothing can get higher than the current brush pos adjusted height + + F32 maxHeight = cur[0].mHeight + heightAdjust; + + for ( U32 i = 0; i < sel->size(); i++ ) + { + mTerrainEditor->getUndoSel()->add((*sel)[i]); + if ( (*sel)[i].mHeight < maxHeight ) + { + (*sel)[i].mHeight += heightAdjust * (*sel)[i].mWeight; + if ( (*sel)[i].mHeight > maxHeight ) + (*sel)[i].mHeight = maxHeight; + } + mTerrainEditor->setGridInfo((*sel)[i]); + } + + mTerrainEditor->scheduleGridUpdate(); +} + +//------------------------------------------------------------------------------ + +void LowerHeightAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type type) +{ + // ok the lower height action is our "dirt dig" action + // only works on brushes... + + Brush *brush = dynamic_cast(sel); + if(!brush) + return; + + if ( type == End ) + return; + + Point2I brushPos = brush->getPosition(); + Point2I brushSize = brush->getSize(); + GridPoint brushGridPoint = brush->getGridPoint(); + + Vector cur; // the height at the brush position + mTerrainEditor->getGridInfos(brushGridPoint, cur); + + if (cur.size() == 0) + return; + + // we get 30 process actions per second (at least) + F32 heightAdjust = -mTerrainEditor->mAdjustHeightVal / 30; + // nothing can get higher than the current brush pos adjusted height + + F32 maxHeight = cur[0].mHeight + heightAdjust; + if(maxHeight < 0) + maxHeight = 0; + + for(U32 i = 0; i < sel->size(); i++) + { + mTerrainEditor->getUndoSel()->add((*sel)[i]); + if((*sel)[i].mHeight > maxHeight) + { + (*sel)[i].mHeight += heightAdjust * (*sel)[i].mWeight; + if((*sel)[i].mHeight < maxHeight) + (*sel)[i].mHeight = maxHeight; + } + mTerrainEditor->setGridInfo((*sel)[i]); + } + + mTerrainEditor->scheduleGridUpdate(); +} + +//------------------------------------------------------------------------------ + +void SetHeightAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type) +{ + if(selChanged) + { + for(U32 i = 0; i < sel->size(); i++) + { + mTerrainEditor->getUndoSel()->add((*sel)[i]); + (*sel)[i].mHeight = mTerrainEditor->mSetHeightVal; + mTerrainEditor->setGridInfo((*sel)[i]); + } + mTerrainEditor->scheduleGridUpdate(); + } +} + +//------------------------------------------------------------------------------ + +void SetEmptyAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type) +{ + if ( !selChanged ) + return; + + mTerrainEditor->setMissionDirty(); + + for ( U32 i = 0; i < sel->size(); i++ ) + { + GridInfo &inf = (*sel)[i]; + + // Skip already empty blocks. + if ( inf.mMaterial == U8_MAX ) + continue; + + // The change flag needs to be set on the undo + // so that it knows to restore materials. + inf.mMaterialChanged = true; + mTerrainEditor->getUndoSel()->add( inf ); + + // Set the material to empty. + inf.mMaterial = -1; + mTerrainEditor->setGridInfo( inf ); + } + + mTerrainEditor->scheduleGridUpdate(); +} + +//------------------------------------------------------------------------------ + +void ClearEmptyAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type) +{ + if ( !selChanged ) + return; + + mTerrainEditor->setMissionDirty(); + + for ( U32 i = 0; i < sel->size(); i++ ) + { + GridInfo &inf = (*sel)[i]; + + // Skip if not empty. + if ( inf.mMaterial != U8_MAX ) + continue; + + // The change flag needs to be set on the undo + // so that it knows to restore materials. + inf.mMaterialChanged = true; + mTerrainEditor->getUndoSel()->add( inf ); + + // Set the material + inf.mMaterial = 0; + mTerrainEditor->setGridInfo( inf ); + } + + mTerrainEditor->scheduleGridUpdate(); +} + +//------------------------------------------------------------------------------ + +void ScaleHeightAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type) +{ + if(selChanged) + { + for(U32 i = 0; i < sel->size(); i++) + { + mTerrainEditor->getUndoSel()->add((*sel)[i]); + (*sel)[i].mHeight *= mTerrainEditor->mScaleVal; + mTerrainEditor->setGridInfo((*sel)[i]); + } + mTerrainEditor->scheduleGridUpdate(); + } +} + +void BrushAdjustHeightAction::process(Selection * sel, const Gui3DMouseEvent & event, bool, Type type) +{ + if(type == Process) + return; + + TerrainBlock *terrBlock = mTerrainEditor->getActiveTerrain(); + if ( !terrBlock ) + return; + + if(type == Begin) + { + mTerrainEditor->lockSelection(true); + mTerrainEditor->getRoot()->mouseLock(mTerrainEditor); + + // the way this works is: + // construct a plane that goes through the collision point + // with one axis up the terrain Z, and horizontally parallel to the + // plane of projection + + // the cross of the camera ffdv and the terrain up vector produces + // the cross plane vector. + + // all subsequent mouse actions are collided against the plane and the deltaZ + // from the previous position is used to delta the selection up and down. + Point3F cameraDir; + + EditTSCtrl::smCamMatrix.getColumn(1, &cameraDir); + terrBlock->getTransform().getColumn(2, &mTerrainUpVector); + + // ok, get the cross vector for the plane: + Point3F planeCross; + mCross(cameraDir, mTerrainUpVector, &planeCross); + + planeCross.normalize(); + Point3F planeNormal; + + Point3F intersectPoint; + mTerrainEditor->collide(event, intersectPoint); + + mCross(mTerrainUpVector, planeCross, &planeNormal); + mIntersectionPlane.set(intersectPoint, planeNormal); + + // ok, we have the intersection point... + // project the collision point onto the up vector of the terrain + + mPreviousZ = mDot(mTerrainUpVector, intersectPoint); + + // add to undo + // and record the starting heights + for(U32 i = 0; i < sel->size(); i++) + { + mTerrainEditor->getUndoSel()->add((*sel)[i]); + (*sel)[i].mStartHeight = (*sel)[i].mHeight; + } + } + else if(type == Update) + { + // ok, collide the ray from the event with the intersection plane: + + Point3F intersectPoint; + Point3F start = event.pos; + Point3F end = start + event.vec * 1000; + + F32 t = mIntersectionPlane.intersect(start, end); + + m_point3F_interpolate( start, end, t, intersectPoint); + F32 currentZ = mDot(mTerrainUpVector, intersectPoint); + + F32 diff = currentZ - mPreviousZ; + + for(U32 i = 0; i < sel->size(); i++) + { + (*sel)[i].mHeight = (*sel)[i].mStartHeight + diff * (*sel)[i].mWeight; + + // clamp it + if((*sel)[i].mHeight < 0.f) + (*sel)[i].mHeight = 0.f; + if((*sel)[i].mHeight > 2047.f) + (*sel)[i].mHeight = 2047.f; + + mTerrainEditor->setGridInfoHeight((*sel)[i]); + } + mTerrainEditor->scheduleGridUpdate(); + } + else if(type == End) + { + mTerrainEditor->getRoot()->mouseUnlock(mTerrainEditor); + } +} + +//------------------------------------------------------------------------------ + +AdjustHeightAction::AdjustHeightAction(TerrainEditor * editor) : + BrushAdjustHeightAction(editor) +{ + mCursor = 0; +} + +void AdjustHeightAction::process(Selection *sel, const Gui3DMouseEvent & event, bool b, Type type) +{ + Selection * curSel = mTerrainEditor->getCurrentSel(); + BrushAdjustHeightAction::process(curSel, event, b, type); +} + +//------------------------------------------------------------------------------ +// flatten the primary selection then blend in the rest... + +void FlattenHeightAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type) +{ + if(!sel->size()) + return; + + if(selChanged) + { + F32 average = 0.f; + + // get the average height + U32 cPrimary = 0; + for(U32 k = 0; k < sel->size(); k++) + if((*sel)[k].mPrimarySelect) + { + cPrimary++; + average += (*sel)[k].mHeight; + } + + average /= cPrimary; + + // set it + for(U32 i = 0; i < sel->size(); i++) + { + mTerrainEditor->getUndoSel()->add((*sel)[i]); + + // + if((*sel)[i].mPrimarySelect) + (*sel)[i].mHeight = average; + else + { + F32 h = average - (*sel)[i].mHeight; + (*sel)[i].mHeight += (h * (*sel)[i].mWeight); + } + + mTerrainEditor->setGridInfo((*sel)[i]); + } + mTerrainEditor->scheduleGridUpdate(); + } +} + +//------------------------------------------------------------------------------ + +void SmoothHeightAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type) +{ + if(!sel->size()) + return; + + if(selChanged) + { + F32 avgHeight = 0.f; + for(U32 k = 0; k < sel->size(); k++) + { + mTerrainEditor->getUndoSel()->add((*sel)[k]); + avgHeight += (*sel)[k].mHeight; + } + + avgHeight /= sel->size(); + + // clamp the terrain smooth factor... + if(mTerrainEditor->mSmoothFactor < 0.f) + mTerrainEditor->mSmoothFactor = 0.f; + if(mTerrainEditor->mSmoothFactor > 1.f) + mTerrainEditor->mSmoothFactor = 1.f; + + // linear + for(U32 i = 0; i < sel->size(); i++) + { + (*sel)[i].mHeight += (avgHeight - (*sel)[i].mHeight) * mTerrainEditor->mSmoothFactor * (*sel)[i].mWeight; + mTerrainEditor->setGridInfo((*sel)[i]); + } + mTerrainEditor->scheduleGridUpdate(); + } +} + +void PaintNoiseAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type type) +{ + // If this is the ending + // mouse down event, then + // update the noise values. + if ( type == Begin ) + { + mNoise.setSeed( Sim::getCurrentTime() ); + mNoise.fBm( &mNoiseData, mNoiseSize, 12, 1.0f, 5.0f ); + mNoise.getMinMax( &mNoiseData, &mMinMaxNoise.x, &mMinMaxNoise.y, mNoiseSize ); + + mScale = 1.5f / ( mMinMaxNoise.x - mMinMaxNoise.y); + } + + if( selChanged ) + { + for( U32 i = 0; i < sel->size(); i++ ) + { + mTerrainEditor->getUndoSel()->add((*sel)[i]); + + const Point2I &gridPos = (*sel)[i].mGridPoint.gridPos; + + const F32 noiseVal = mNoiseData[ ( gridPos.x % mNoiseSize ) + + ( ( gridPos.y % mNoiseSize ) * mNoiseSize ) ]; + + (*sel)[i].mHeight += (noiseVal - mMinMaxNoise.y * mScale) * (*sel)[i].mWeight * mTerrainEditor->mNoiseFactor; + + mTerrainEditor->setGridInfo((*sel)[i]); + } + + mTerrainEditor->scheduleGridUpdate(); + } +} +/* +void ThermalErosionAction::process(Selection * sel, const Gui3DMouseEvent &, bool selChanged, Type) +{ + if( selChanged ) + { + TerrainBlock *tblock = mTerrainEditor->getActiveTerrain(); + if ( !tblock ) + return; + + F32 height = 0; + F32 maxHeight = 0; + U32 shift = getBinLog2( TerrainBlock::BlockSize ); + + for ( U32 x = 0; x < TerrainBlock::BlockSize; x++ ) + { + for ( U32 y = 0; y < TerrainBlock::BlockSize; y++ ) + { + height = fixedToFloat( tblock->getHeight( x, y ) ); + mTerrainHeights[ x + (y << 8)] = height; + + if ( height > maxHeight ) + maxHeight = height; + } + } + + //mNoise.erodeThermal( &mTerrainHeights, &mNoiseData, 30.0f, 5.0f, 5, TerrainBlock::BlockSize, tblock->getSquareSize(), maxHeight ); + + mNoise.erodeHydraulic( &mTerrainHeights, &mNoiseData, 1, TerrainBlock::BlockSize ); + + F32 heightDiff = 0; + + for( U32 i = 0; i < sel->size(); i++ ) + { + mTerrainEditor->getUndoSel()->add((*sel)[i]); + + const Point2I &gridPos = (*sel)[i].mGridPoint.gridPos; + + // Need to get the height difference + // between the current height and the + // erosion height to properly apply the + // softness and pressure settings of the brush + // for this selection. + heightDiff = (*sel)[i].mHeight - mNoiseData[ gridPos.x + (gridPos.y << shift)]; + + (*sel)[i].mHeight -= (heightDiff * (*sel)[i].mWeight); + + mTerrainEditor->setGridInfo((*sel)[i]); + } + + mTerrainEditor->gridUpdateComplete(); + } +} +*/ + + +IMPLEMENT_CONOBJECT( TerrainSmoothAction ); + +ConsoleDocClass( TerrainSmoothAction, + "@brief Terrain action used for leveling varying terrain heights smoothly.\n\n" + "Editor use only.\n\n" + "@internal" +); + +TerrainSmoothAction::TerrainSmoothAction() + : UndoAction( "Terrain Smoothing" ) +{ +} + +void TerrainSmoothAction::initPersistFields() +{ + Parent::initPersistFields(); +} + +void TerrainSmoothAction::smooth( TerrainBlock *terrain, F32 factor, U32 steps ) +{ + AssertFatal( terrain, "TerrainSmoothAction::smooth() - Got null object!" ); + + // Store our input parameters. + mTerrainId = terrain->getId(); + mSteps = steps; + mFactor = factor; + + // The redo can do the rest. + redo(); +} + +ConsoleMethod( TerrainSmoothAction, smooth, void, 5, 5, "( TerrainBlock obj, F32 factor, U32 steps )") +{ + TerrainBlock *terrain = NULL; + if ( Sim::findObject( argv[2], terrain ) && terrain ) + object->smooth( terrain, dAtof( argv[3] ), mClamp( dAtoi( argv[4] ), 1, 13 ) ); +} + +void TerrainSmoothAction::undo() +{ + // First find the terrain from the id. + TerrainBlock *terrain; + if ( !Sim::findObject( mTerrainId, terrain ) || !terrain ) + return; + + // Get the terrain file. + TerrainFile *terrFile = terrain->getFile(); + + // Copy our stored heightmap to the file. + terrFile->setHeightMap( mUnsmoothedHeights, false ); + + // Tell the terrain to update itself. + terrain->updateGrid( Point2I::Zero, Point2I::Max, true ); +} + +void TerrainSmoothAction::redo() +{ + // First find the terrain from the id. + TerrainBlock *terrain; + if ( !Sim::findObject( mTerrainId, terrain ) || !terrain ) + return; + + // Get the terrain file. + TerrainFile *terrFile = terrain->getFile(); + + // First copy the heightmap state. + mUnsmoothedHeights = terrFile->getHeightMap(); + + // Do the smooth. + terrFile->smooth( mFactor, mSteps, false ); + + // Tell the terrain to update itself. + terrain->updateGrid( Point2I::Zero, Point2I::Max, true ); +} \ No newline at end of file diff --git a/Engine/source/gui/worldEditor/terrainActions.h b/Engine/source/gui/worldEditor/terrainActions.h new file mode 100644 index 000000000..d091ea5f9 --- /dev/null +++ b/Engine/source/gui/worldEditor/terrainActions.h @@ -0,0 +1,351 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TERRAINACTIONS_H_ +#define _TERRAINACTIONS_H_ + +#ifndef _TERRAINEDITOR_H_ +#include "gui/worldEditor/terrainEditor.h" +#endif +#ifndef _GUIFILTERCTRL_H_ +#include "gui/editor/guiFilterCtrl.h" +#endif +#ifndef _UNDO_H_ +#include "util/undo.h" +#endif +#ifndef _NOISE2D_H_ +#include "util/noise2d.h" +#endif + +class TerrainAction +{ + protected: + TerrainEditor * mTerrainEditor; + + public: + + virtual ~TerrainAction(){}; + TerrainAction(TerrainEditor * editor) : mTerrainEditor(editor){} + + virtual StringTableEntry getName() = 0; + + enum Type { + Begin = 0, + Update, + End, + Process + }; + + // + virtual void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type) = 0; + virtual bool useMouseBrush() { return(true); } +}; + +//------------------------------------------------------------------------------ + +class SelectAction : public TerrainAction +{ + public: + SelectAction(TerrainEditor * editor) : TerrainAction(editor){}; + StringTableEntry getName(){return("select");} + + void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type); +}; + +class DeselectAction : public TerrainAction +{ + public: + DeselectAction(TerrainEditor * editor) : TerrainAction(editor){}; + StringTableEntry getName(){return("deselect");} + + void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type); +}; + +class ClearAction : public TerrainAction +{ + public: + ClearAction(TerrainEditor * editor) : TerrainAction(editor){}; + StringTableEntry getName(){return("clear");} + + void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type) {}; + bool useMouseBrush() { mTerrainEditor->getCurrentSel()->reset(); return true; } +}; + + +class SoftSelectAction : public TerrainAction +{ + public: + SoftSelectAction(TerrainEditor * editor) : TerrainAction(editor){}; + StringTableEntry getName(){return("softSelect");} + + void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type); + + Filter mFilter; +}; + +//------------------------------------------------------------------------------ + +class OutlineSelectAction : public TerrainAction +{ + public: + OutlineSelectAction(TerrainEditor * editor) : TerrainAction(editor){}; + StringTableEntry getName(){return("outlineSelect");} + + void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type); + bool useMouseBrush() { return(false); } + + private: + + Gui3DMouseEvent mLastEvent; +}; + +//------------------------------------------------------------------------------ + +class PaintMaterialAction : public TerrainAction +{ + public: + PaintMaterialAction(TerrainEditor * editor) : TerrainAction(editor){} + StringTableEntry getName(){return("paintMaterial");} + + void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type); +}; + +//------------------------------------------------------------------------------ + +class ClearMaterialsAction : public TerrainAction +{ +public: + ClearMaterialsAction(TerrainEditor * editor) : TerrainAction(editor){} + StringTableEntry getName(){return("clearMaterials");} + + void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type); +}; + +//------------------------------------------------------------------------------ + +class RaiseHeightAction : public TerrainAction +{ + public: + RaiseHeightAction(TerrainEditor * editor) : TerrainAction(editor){} + StringTableEntry getName(){return("raiseHeight");} + + void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type); +}; + +//------------------------------------------------------------------------------ + +class LowerHeightAction : public TerrainAction +{ + public: + LowerHeightAction(TerrainEditor * editor) : TerrainAction(editor){} + StringTableEntry getName(){return("lowerHeight");} + + void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type); +}; + +//------------------------------------------------------------------------------ + +class SetHeightAction : public TerrainAction +{ + public: + SetHeightAction(TerrainEditor * editor) : TerrainAction(editor){} + StringTableEntry getName(){return("setHeight");} + + void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type); +}; + +//------------------------------------------------------------------------------ + +class SetEmptyAction : public TerrainAction +{ + public: + SetEmptyAction(TerrainEditor * editor) : TerrainAction(editor){} + StringTableEntry getName(){return("setEmpty");} + + void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type); +}; + +//------------------------------------------------------------------------------ + +class ClearEmptyAction : public TerrainAction +{ + public: + ClearEmptyAction(TerrainEditor * editor) : TerrainAction(editor){} + StringTableEntry getName(){return("clearEmpty");} + + void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type); +}; + +//------------------------------------------------------------------------------ + +class ScaleHeightAction : public TerrainAction +{ + public: + ScaleHeightAction(TerrainEditor * editor) : TerrainAction(editor){} + StringTableEntry getName(){return("scaleHeight");} + + void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type); +}; + +//------------------------------------------------------------------------------ + +class BrushAdjustHeightAction : public TerrainAction +{ + public: + BrushAdjustHeightAction(TerrainEditor * editor) : TerrainAction(editor){} + StringTableEntry getName(){return("brushAdjustHeight");} + + void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type); + + private: + PlaneF mIntersectionPlane; + Point3F mTerrainUpVector; + F32 mPreviousZ; +}; + +class AdjustHeightAction : public BrushAdjustHeightAction +{ + public: + AdjustHeightAction(TerrainEditor * editor); + StringTableEntry getName(){return("adjustHeight");} + + void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type); + bool useMouseBrush() { return(false); } + + private: + // + Point3F mHitPos; + Point3F mLastPos; + SimObjectPtr mCursor; +}; + +//------------------------------------------------------------------------------ + +class FlattenHeightAction : public TerrainAction +{ + public: + FlattenHeightAction(TerrainEditor * editor) : TerrainAction(editor){} + StringTableEntry getName(){return("flattenHeight");} + + void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type); +}; + +class SmoothHeightAction : public TerrainAction +{ + public: + SmoothHeightAction(TerrainEditor * editor) : TerrainAction(editor){} + StringTableEntry getName(){return("smoothHeight");} + + void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type); +}; + +class PaintNoiseAction : public TerrainAction +{ + public: + + PaintNoiseAction( TerrainEditor *editor ) + : TerrainAction( editor ), + mNoiseSize( 256 ) + { + mNoise.setSeed( 5342219 ); + mNoiseData.setSize( mNoiseSize * mNoiseSize ); + mNoise.fBm( &mNoiseData, mNoiseSize, 12, 1.0f, 5.0f ); + //Vector scratch = mNoiseData; + //mNoise.rigidMultiFractal( &mNoiseData, &scratch, TerrainBlock::BlockSize, 12, 1.0f, 5.0f ); + mNoise.getMinMax( &mNoiseData, &mMinMaxNoise.x, &mMinMaxNoise.y, mNoiseSize ); + + mScale = 1.5f / ( mMinMaxNoise.x - mMinMaxNoise.y); + } + + StringTableEntry getName() { return "paintNoise"; } + + void process( Selection *sel, const Gui3DMouseEvent &event, bool selChanged, Type type ); + + protected: + + const U32 mNoiseSize; + + Noise2D mNoise; + + Vector mNoiseData; + + Point2F mMinMaxNoise; + + F32 mScale; +}; + +/* +class ThermalErosionAction : public TerrainAction +{ + public: + ThermalErosionAction(TerrainEditor * editor) + : TerrainAction(editor) + { + mNoise.setSeed( 1 );//Sim::getCurrentTime() ); + mNoiseData.setSize( TerrainBlock::BlockSize * TerrainBlock::BlockSize ); + mTerrainHeights.setSize( TerrainBlock::BlockSize * TerrainBlock::BlockSize ); + } + + StringTableEntry getName(){return("thermalErode");} + + void process(Selection * sel, const Gui3DMouseEvent & event, bool selChanged, Type type); + + Noise2D mNoise; + Vector mNoiseData; + Vector mTerrainHeights; +}; +*/ + +/// An undo action used to perform terrain wide smoothing. +class TerrainSmoothAction : public UndoAction +{ + typedef UndoAction Parent; + +protected: + + SimObjectId mTerrainId; + + U32 mSteps; + + F32 mFactor; + + Vector mUnsmoothedHeights; + +public: + + TerrainSmoothAction(); + + // SimObject + DECLARE_CONOBJECT( TerrainSmoothAction ); + static void initPersistFields(); + + // UndoAction + virtual void undo(); + virtual void redo(); + + /// Performs the initial smoothing and stores + /// the heighfield state for later undo. + void smooth( TerrainBlock *terrain, F32 factor, U32 steps ); +}; + + +#endif // _TERRAINACTIONS_H_ diff --git a/Engine/source/gui/worldEditor/terrainEditor.cpp b/Engine/source/gui/worldEditor/terrainEditor.cpp new file mode 100644 index 000000000..1b2239911 --- /dev/null +++ b/Engine/source/gui/worldEditor/terrainEditor.cpp @@ -0,0 +1,2935 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/worldEditor/terrainEditor.h" + +#include "core/frameAllocator.h" +#include "core/strings/stringUnit.h" +#include "console/consoleTypes.h" +#include "console/simEvents.h" +#include "sim/netConnection.h" +#include "math/mathUtils.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxDrawUtil.h" +#include "gui/core/guiCanvas.h" +#include "gui/worldEditor/terrainActions.h" +#include "interior/interiorInstance.h" +#include "terrain/terrMaterial.h" + + + +IMPLEMENT_CONOBJECT(TerrainEditor); + +ConsoleDocClass( TerrainEditor, + "@brief The base Terrain Editor tool\n\n" + "Editor use only.\n\n" + "@internal" +); + +Selection::Selection() : + Vector(__FILE__, __LINE__), + mName(0), + mUndoFlags(0), + mHashListSize(1024) +{ + VECTOR_SET_ASSOCIATION(mHashLists); + + // clear the hash list + mHashLists.setSize(mHashListSize); + reset(); +} + +Selection::~Selection() +{ +} + +void Selection::reset() +{ + PROFILE_SCOPE( TerrainEditor_Selection_Reset ); + + for(U32 i = 0; i < mHashListSize; i++) + mHashLists[i] = -1; + clear(); +} + +bool Selection::validate() +{ + PROFILE_SCOPE( TerrainEditor_Selection_Validate ); + + // scan all the hashes and verify that the heads they point to point back to them + U32 hashesProcessed = 0; + for(U32 i = 0; i < mHashLists.size(); i++) + { + U32 entry = mHashLists[i]; + if(entry == -1) + continue; + + GridInfo info = (*this)[entry]; + U32 hashIndex = getHashIndex(info.mGridPoint.gridPos); + + if( entry != mHashLists[hashIndex] ) + { + AssertFatal(false, "Selection hash lists corrupted"); + return false; + } + hashesProcessed++; + } + + // scan all the entries and verify that anything w/ a prev == -1 is correctly in the hash table + U32 headsProcessed = 0; + for(U32 i = 0; i < size(); i++) + { + GridInfo info = (*this)[i]; + if(info.mPrev != -1) + continue; + + U32 hashIndex = getHashIndex(info.mGridPoint.gridPos); + + if(mHashLists[hashIndex] != i) + { + AssertFatal(false, "Selection list heads corrupted"); + return false; + } + headsProcessed++; + } + AssertFatal(headsProcessed == hashesProcessed, "Selection's number of hashes and number of list heads differ."); + return true; +} + +U32 Selection::getHashIndex(const Point2I & pos) +{ + PROFILE_SCOPE( TerrainEditor_Selection_GetHashIndex ); + + Point2F pnt = Point2F((F32)pos.x, (F32)pos.y) + Point2F(1.3f,3.5f); + return( (U32)(mFloor(mHashLists.size() * mFmod(pnt.len() * 0.618f, 1.0f))) ); +} + +S32 Selection::lookup(const Point2I & pos) +{ + PROFILE_SCOPE( TerrainEditor_Selection_Lookup ); + + U32 index = getHashIndex(pos); + + S32 entry = mHashLists[index]; + + while(entry != -1) + { + if((*this)[entry].mGridPoint.gridPos == pos) + return(entry); + + entry = (*this)[entry].mNext; + } + + return(-1); +} + +void Selection::insert(GridInfo info) +{ + PROFILE_SCOPE( TerrainEditor_Selection_Insert ); + + //validate(); + // get the index into the hash table + U32 index = getHashIndex(info.mGridPoint.gridPos); + + // if there is an existing linked list, make it our next + info.mNext = mHashLists[index]; + info.mPrev = -1; + + // if there is an existing linked list, make us it's prev + U32 indexOfNewEntry = size(); + if(info.mNext != -1) + (*this)[info.mNext].mPrev = indexOfNewEntry; + + // the hash table holds the heads of the linked lists. make us the head of this list. + mHashLists[index] = indexOfNewEntry; + + // copy us into the vector + push_back(info); + //validate(); +} + +bool Selection::remove(const GridInfo &info) +{ + PROFILE_SCOPE( TerrainEditor_Selection_Remove ); + + if(size() < 1) + return false; + + //AssertFatal( validate(), "Selection hashLists corrupted before Selection.remove()"); + + U32 hashIndex = getHashIndex(info.mGridPoint.gridPos); + S32 listHead = mHashLists[hashIndex]; + //AssertFatal(listHead < size(), "A Selection's hash table is corrupt."); + + if(listHead == -1) + return(false); + + const S32 victimEntry = lookup(info.mGridPoint.gridPos); + if( victimEntry == -1 ) + return(false); + + const GridInfo victim = (*this)[victimEntry]; + const S32 vicPrev = victim.mPrev; + const S32 vicNext = victim.mNext; + + // remove us from the linked list, if there is one. + if(vicPrev != -1) + (*this)[vicPrev].mNext = vicNext; + if(vicNext != -1) + (*this)[vicNext].mPrev = vicPrev; + + // if we were the head of the list, make our next the new head in the hash table. + if(vicPrev == -1) + mHashLists[hashIndex] = vicNext; + + // if we're not the last element in the vector, copy the last element to our position. + if(victimEntry != size() - 1) + { + // copy last into victim, and re-cache next & prev + const GridInfo lastEntry = last(); + const S32 lastPrev = lastEntry.mPrev; + const S32 lastNext = lastEntry.mNext; + (*this)[victimEntry] = lastEntry; + + // update the new element's next and prev, to reestablish it in it's linked list. + if(lastPrev != -1) + (*this)[lastPrev].mNext = victimEntry; + if(lastNext != -1) + (*this)[lastNext].mPrev = victimEntry; + + // if it was the head of it's list, update the hash table with its new position. + if(lastPrev == -1) + { + const U32 lastHash = getHashIndex(lastEntry.mGridPoint.gridPos); + AssertFatal(mHashLists[lastHash] == size() - 1, "Selection hashLists corrupted during Selection.remove() (oldmsg)"); + mHashLists[lastHash] = victimEntry; + } + } + + // decrement the vector, we're done here + pop_back(); + //AssertFatal( validate(), "Selection hashLists corrupted after Selection.remove()"); + return true; +} + +bool Selection::add(const GridInfo &info) +{ + PROFILE_SCOPE( TerrainEditor_Selection_Add ); + + S32 index = lookup(info.mGridPoint.gridPos); + if(index != -1) + return(false); + + insert(info); + return(true); +} + +bool Selection::getInfo(Point2I pos, GridInfo & info) +{ + PROFILE_SCOPE( TerrainEditor_Selection_GetInfo ); + + S32 index = lookup(pos); + if(index == -1) + return(false); + + info = (*this)[index]; + return(true); +} + +bool Selection::setInfo(GridInfo & info) +{ + PROFILE_SCOPE( TerrainEditor_Selection_SetInfo ); + + S32 index = lookup(info.mGridPoint.gridPos); + if(index == -1) + return(false); + + S32 next = (*this)[index].mNext; + S32 prev = (*this)[index].mPrev; + + (*this)[index] = info; + (*this)[index].mNext = next; + (*this)[index].mPrev = prev; + + return(true); +} + +F32 Selection::getAvgHeight() +{ + PROFILE_SCOPE( TerrainEditor_Selection_GetAvgHeight ); + + if(!size()) + return(0); + + F32 avg = 0.f; + for(U32 i = 0; i < size(); i++) + avg += (*this)[i].mHeight; + + return(avg / size()); +} + +F32 Selection::getMinHeight() +{ + PROFILE_SCOPE( TerrainEditor_Selection_GetMinHeight ); + + if(!size()) + return(0); + + F32 minHeight = (*this)[0].mHeight; + for(U32 i = 1; i < size(); i++) + minHeight = getMin(minHeight, (*this)[i].mHeight); + + return minHeight; +} + +F32 Selection::getMaxHeight() +{ + PROFILE_SCOPE( TerrainEditor_Selection_GetMaxHeight ); + + if(!size()) + return(0); + + F32 maxHeight = (*this)[0].mHeight; + for(U32 i = 1; i < size(); i++) + maxHeight = getMax(maxHeight, (*this)[i].mHeight); + + return maxHeight; +} + +Brush::Brush(TerrainEditor * editor) : + mTerrainEditor(editor) +{ + mSize = mTerrainEditor->getBrushSize(); +} + +const Point2I & Brush::getPosition() +{ + return(mGridPoint.gridPos); +} + +const GridPoint & Brush::getGridPoint() +{ + return mGridPoint; +} + +void Brush::setPosition(const Point3F & pos) +{ + PROFILE_SCOPE( TerrainEditor_Brush_SetPosition_Point3F ); + + mTerrainEditor->worldToGrid(pos, mGridPoint); + update(); +} + +void Brush::setPosition(const Point2I & pos) +{ + PROFILE_SCOPE( TerrainEditor_Brush_SetPosition_Point2I ); + + mGridPoint.gridPos = pos; + update(); +} + +void Brush::update() +{ + PROFILE_SCOPE( TerrainEditor_Brush_update ); + + if ( mGridPoint.terrainBlock ) + rebuild(); +} + +void Brush::render() +{ + PROFILE_SCOPE( TerrainEditor_Brush_Render ); + + // Render the brush's outline via the derived brush class. + _renderOutline(); + + // Render the brush's interior grid points. + + const U32 pointCount = mSize.x * mSize.y; + if ( pointCount == 0 ) + return; + + if ( mRenderList.empty() || empty() ) + return; + + Vector pointList; + pointList.reserve( pointCount ); + + for(S32 x = 0; x < mSize.x; x++) + { + for(S32 y = 0; y < mSize.y; y++) + { + S32 id = mRenderList[x*mSize.x+y]; + if ( id == -1 ) + continue; + + const GridInfo &gInfo = (*this)[ id ]; + + Point3F pos; + mTerrainEditor->gridToWorld( gInfo.mGridPoint.gridPos, pos, gInfo.mGridPoint.terrainBlock ); + + if ( !mTerrainEditor->project( pos, &pos ) ) + continue; + + pointList.increment(); + GFXVertexPCT &pointInfo = pointList.last(); + + pointInfo.point = pos; + + pointInfo.color.set( 255, 0, 255, gInfo.mWeight * 255 ); + + pointInfo.texCoord.set( 1.0f, 0.0f ); + } + } + + mTerrainEditor->renderPoints( pointList ); +} + +void BoxBrush::rebuild() +{ + PROFILE_SCOPE( TerrainEditor_BoxBrush_Rebuild ); + + reset(); + + const F32 squareSize = mGridPoint.terrainBlock->getSquareSize(); + + mRenderList.setSize(mSize.x*mSize.y); + + Point3F center( F32(mSize.x - 1) / 2.0f * squareSize, F32(mSize.y - 1) / 2.0f * squareSize, 0.0f ); + + Filter filter; + filter.set(1, &mTerrainEditor->mSoftSelectFilter); + + const Point3F mousePos = mTerrainEditor->getMousePos(); + + F32 xFactorScale = center.x / ( center.x + 0.5f ); + F32 yFactorScale = center.y / ( center.y + 0.5f ); + + const F32 softness = mTerrainEditor->getBrushSoftness(); + const F32 pressure = mTerrainEditor->getBrushPressure(); + + Point3F posw( 0,0,0 ); + Point2I posg( 0,0 ); + Vector infos; + + for ( S32 x = 0; x < mSize.x; x++ ) + { + for(S32 y = 0; y < mSize.y; y++) + { + F32 xFactor = 0.0f; + if ( center.x > 0 ) + xFactor = mAbs( center.x - x ) / center.x * xFactorScale; + + F32 yFactor = 0.0f; + if ( center.y > 0 ) + yFactor = mAbs( center.y - y ) / center.y * yFactorScale; + + S32 &rl = mRenderList[x*mSize.x+y]; + + posw.x = mousePos.x + (F32)x * squareSize - center.x; + posw.y = mousePos.y + (F32)y * squareSize - center.y; + // round to grid coords + GridPoint gridPoint = mGridPoint; + mTerrainEditor->worldToGrid( posw, gridPoint ); + + // Check that the grid point is valid within the terrain. This assumes + // that there is no wrap around past the edge of the terrain. + if(!mTerrainEditor->isPointInTerrain(gridPoint)) + { + rl = -1; + continue; + } + + infos.clear(); + mTerrainEditor->getGridInfos( gridPoint, infos ); + + for (U32 z = 0; z < infos.size(); z++) + { + infos[z].mWeight = pressure * + mLerp( infos[z].mWeight, filter.getValue(xFactor > yFactor ? xFactor : yFactor), softness ); + + push_back(infos[z]); + } + + rl = size()-1; + } + } +} + +void BoxBrush::_renderOutline() +{ + F32 squareSize = mGridPoint.terrainBlock->getSquareSize(); + + RayInfo ri; + Point3F start( 0, 0, 5000.0f ); + Point3F end( 0, 0, -5000.0f ); + bool hit; + + Vector pointList; + pointList.reserve( 64 ); + + const ColorI col( 255, 0, 255, 255 ); + + const Point3F &mousePos = mTerrainEditor->getMousePos(); + + static const Point2F offsetArray [5] = + { + Point2F( -1, -1 ), + Point2F( 1, -1 ), + Point2F( 1, 1 ), + Point2F( -1, 1 ), + Point2F( -1, -1 ) // repeat of offset[0] + }; + + // 64 total steps, 4 sides to the box, 16 steps per side. + // 64 / 4 = 16 + const U32 steps = 16; + + for ( S32 i = 0; i < 4; i++ ) + { + const Point2F &offset = offsetArray[i]; + const Point2F &next = offsetArray[i+1]; + + for ( S32 j = 0; j < steps; j++ ) + { + F32 frac = (F32)j / ( (F32)steps - 1.0f ); + + Point2F tmp; + tmp.interpolate( offset, next, frac ); + + start.x = end.x = mousePos.x + tmp.x * squareSize * 0.5f * (F32)mSize.x; + start.y = end.y = mousePos.y + tmp.y * squareSize * 0.5f * (F32)mSize.y; + + hit = gServerContainer.castRay( start, end, TerrainObjectType, &ri ); + + if ( hit ) + pointList.push_back( ri.point ); + } + } + + mTerrainEditor->drawLineList( pointList, col, 1.0f ); +} + +void EllipseBrush::rebuild() +{ + PROFILE_SCOPE( TerrainEditor_EllipseBrush_Rebuild ); + + reset(); + + const F32 squareSize = mGridPoint.terrainBlock->getSquareSize(); + + mRenderList.setSize(mSize.x*mSize.y); + + Point3F center( F32(mSize.x - 1) / 2.0f * squareSize, F32(mSize.y - 1) / 2.0f * squareSize, 0.0f ); + + Filter filter; + filter.set(1, &mTerrainEditor->mSoftSelectFilter); + + const Point3F mousePos = mTerrainEditor->getMousePos(); + + // a point is in a circle if: + // x^2 + y^2 <= r^2 + // a point is in an ellipse if: + // (ax)^2 + (by)^2 <= 1 + // where a = 1/halfEllipseWidth and b = 1/halfEllipseHeight + + // for a soft-selected ellipse, + // the factor is simply the filtered: ((ax)^2 + (by)^2) + + F32 a = 1.0f / (F32(mSize.x) * squareSize * 0.5f); + F32 b = 1.0f / (F32(mSize.y) * squareSize * 0.5f); + + const F32 softness = mTerrainEditor->getBrushSoftness(); + const F32 pressure = mTerrainEditor->getBrushPressure(); + + Point3F posw( 0,0,0 ); + Point2I posg( 0,0 ); + Vector infos; + + for ( S32 x = 0; x < mSize.x; x++ ) + { + for ( S32 y = 0; y < mSize.y; y++ ) + { + F32 xp = center.x - x * squareSize; + F32 yp = center.y - y * squareSize; + + F32 factor = (a * a * xp * xp) + (b * b * yp * yp); + if ( factor > 1 ) + { + mRenderList[x*mSize.x+y] = -1; + continue; + } + + S32 &rl = mRenderList[x*mSize.x+y]; + + posw.x = mousePos.x + (F32)x * squareSize - center.x; + posw.y = mousePos.y + (F32)y * squareSize - center.y; + + // round to grid coords + GridPoint gridPoint = mGridPoint; + mTerrainEditor->worldToGrid( posw, gridPoint ); + + // Check that the grid point is valid within the terrain. This assumes + // that there is no wrap around past the edge of the terrain. + if ( !mTerrainEditor->isPointInTerrain( gridPoint ) ) + { + rl = -1; + continue; + } + + infos.clear(); + mTerrainEditor->getGridInfos( gridPoint, infos ); + + for ( U32 z = 0; z < infos.size(); z++ ) + { + infos[z].mWeight = pressure * mLerp( infos[z].mWeight, filter.getValue( factor ), softness ); + push_back(infos[z]); + } + + rl = size()-1; + } + } +} + +void EllipseBrush::_renderOutline() +{ + F32 squareSize = mGridPoint.terrainBlock->getSquareSize(); + + RayInfo ri; + Point3F start( 0, 0, 5000.0f ); + Point3F end( 0, 0, -5000.0f ); + bool hit; + + Vector pointList; + + ColorI col( 255, 0, 255, 255 ); + + const U32 steps = 64; + + const Point3F &mousePos = mTerrainEditor->getMousePos(); + + for ( S32 i = 0; i < steps; i++ ) + { + F32 radians = (F32)i / (F32)(steps-1) * M_2PI_F; + VectorF vec(0,1,0); + MathUtils::vectorRotateZAxis( vec, radians ); + + start.x = end.x = mousePos.x + vec.x * squareSize * (F32)mSize.x * 0.5f; + start.y = end.y = mousePos.y + vec.y * squareSize * (F32)mSize.y * 0.5f; + + hit = gServerContainer.castRay( start, end, TerrainObjectType, &ri ); + + if ( hit ) + pointList.push_back( ri.point ); + } + + mTerrainEditor->drawLineList( pointList, col, 1.0f ); +} + +SelectionBrush::SelectionBrush(TerrainEditor * editor) : + Brush(editor) +{ + //... grab the current selection +} + +void SelectionBrush::rebuild() +{ + reset(); + //... move the selection +} + +void SelectionBrush::render(Vector & vertexBuffer, S32 & verts, S32 & elems, S32 & prims, const ColorF & inColorFull, const ColorF & inColorNone, const ColorF & outColorFull, const ColorF & outColorNone) const +{ + //... render the selection +} + +TerrainEditor::TerrainEditor() : + mActiveTerrain(0), + mMousePos(0,0,0), + mMouseBrush(0), + mInAction(false), + mUndoSel(0), + mGridUpdateMin( S32_MAX, S32_MAX ), + mGridUpdateMax( 0, 0 ), + mMaxBrushSize(48,48), + mNeedsGridUpdate( false ), + mNeedsMaterialUpdate( false ), + mMouseDown( false ) +{ + VECTOR_SET_ASSOCIATION(mActions); + + // + resetCurrentSel(); + + // + mBrushPressure = 1.0f; + mBrushSize.set(1,1); + mBrushSoftness = 1.0f; + mBrushChanged = true; + mMouseBrush = new BoxBrush(this); + mMouseDownSeq = 0; + mIsDirty = false; + mIsMissionDirty = false; + mPaintIndex = -1; + + // add in all the actions here.. + mActions.push_back(new SelectAction(this)); + mActions.push_back(new DeselectAction(this)); + mActions.push_back(new ClearAction(this)); + mActions.push_back(new SoftSelectAction(this)); + mActions.push_back(new OutlineSelectAction(this)); + mActions.push_back(new PaintMaterialAction(this)); + mActions.push_back(new ClearMaterialsAction(this)); + mActions.push_back(new RaiseHeightAction(this)); + mActions.push_back(new LowerHeightAction(this)); + mActions.push_back(new SetHeightAction(this)); + mActions.push_back(new SetEmptyAction(this)); + mActions.push_back(new ClearEmptyAction(this)); + mActions.push_back(new ScaleHeightAction(this)); + mActions.push_back(new BrushAdjustHeightAction(this)); + mActions.push_back(new AdjustHeightAction(this)); + mActions.push_back(new FlattenHeightAction(this)); + mActions.push_back(new SmoothHeightAction(this)); + mActions.push_back(new PaintNoiseAction(this)); + //mActions.push_back(new ThermalErosionAction(this)); + + + // set the default action + mCurrentAction = mActions[0]; + mRenderBrush = mCurrentAction->useMouseBrush(); + + // persist data defaults + mRenderBorder = true; + mBorderHeight = 10; + mBorderFillColor.set(0,255,0,20); + mBorderFrameColor.set(0,255,0,128); + mBorderLineMode = false; + mSelectionHidden = false; + mRenderVertexSelection = false; + mRenderSolidBrush = false; + mProcessUsesBrush = false; + + // + mAdjustHeightVal = 10; + mSetHeightVal = 100; + mScaleVal = 1; + mSmoothFactor = 0.1f; + mNoiseFactor = 1.0f; + mMaterialGroup = 0; + mSoftSelectRadius = 50.f; + mAdjustHeightMouseScale = 0.1f; + + mSoftSelectDefaultFilter = StringTable->insert("1.000000 0.833333 0.666667 0.500000 0.333333 0.166667 0.000000"); + mSoftSelectFilter = mSoftSelectDefaultFilter; + + mSlopeMinAngle = 0.0f; + mSlopeMaxAngle = 90.0f; +} + +TerrainEditor::~TerrainEditor() +{ + // mouse + delete mMouseBrush; + + // terrain actions + U32 i; + for(i = 0; i < mActions.size(); i++) + delete mActions[i]; + + // undo stuff + delete mUndoSel; +} + +TerrainAction * TerrainEditor::lookupAction(const char * name) +{ + for(U32 i = 0; i < mActions.size(); i++) + if(!dStricmp(mActions[i]->getName(), name)) + return(mActions[i]); + return(0); +} + +bool TerrainEditor::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + GFXStateBlockDesc desc; + desc.setZReadWrite( false ); + desc.zWriteEnable = false; + desc.setCullMode( GFXCullNone ); + desc.setBlend( true, GFXBlendSrcAlpha, GFXBlendDestAlpha ); + mStateBlock = GFX->createStateBlock( desc ); + + return true; +} + +bool TerrainEditor::onWake() +{ + if ( !Parent::onWake() ) + return false; + + // Push our default cursor on here once. + GuiCanvas *root = getRoot(); + if ( root ) + { + S32 currCursor = PlatformCursorController::curArrow; + + PlatformWindow *window = root->getPlatformWindow(); + PlatformCursorController *controller = window->getCursorController(); + controller->pushCursor( currCursor ); + } + + return true; +} + +void TerrainEditor::onSleep() +{ + // Pop our default cursor off. + GuiCanvas *root = getRoot(); + if ( root ) + { + PlatformWindow *window = root->getPlatformWindow(); + PlatformCursorController *controller = window->getCursorController(); + controller->popCursor(); + } + + Parent::onSleep(); +} + +void TerrainEditor::get3DCursor( GuiCursor *&cursor, + bool &visible, + const Gui3DMouseEvent &event_ ) +{ + cursor = NULL; + visible = false; + + GuiCanvas *root = getRoot(); + if ( !root ) + return; + + S32 currCursor = PlatformCursorController::curArrow; + + if ( root->mCursorChanged == currCursor ) + return; + + PlatformWindow *window = root->getPlatformWindow(); + PlatformCursorController *controller = window->getCursorController(); + + // We've already changed the cursor, + // so set it back before we change it again. + if( root->mCursorChanged != -1) + controller->popCursor(); + + // Now change the cursor shape + controller->pushCursor(currCursor); + root->mCursorChanged = currCursor; +} + +void TerrainEditor::onDeleteNotify(SimObject * object) +{ + Parent::onDeleteNotify(object); + + if (dynamic_cast(object) == mActiveTerrain) + mActiveTerrain = NULL; +} + +TerrainBlock* TerrainEditor::getClientTerrain( TerrainBlock *serverTerrain ) const +{ + if ( !serverTerrain ) + serverTerrain = mActiveTerrain; + + return serverTerrain ? dynamic_cast( serverTerrain->getClientObject() ) : NULL; +} + +bool TerrainEditor::isMainTile(const GridPoint & gPoint) const +{ + const S32 blockSize = (S32)gPoint.terrainBlock->getBlockSize(); + + Point2I testPos = gPoint.gridPos; + if (!dStrcmp(getCurrentAction(),"paintMaterial")) + { + if (testPos.x == blockSize) + testPos.x--; + if (testPos.y == blockSize) + testPos.y--; + } + + return (testPos.x >= 0 && testPos.x < blockSize && testPos.y >= 0 && testPos.y < blockSize); +} + +TerrainBlock* TerrainEditor::getTerrainUnderWorldPoint(const Point3F & wPos) +{ + PROFILE_SCOPE( TerrainEditor_GetTerrainUnderWorldPoint ); + + // Cast a ray straight down from the world position and see which + // Terrain is the closest to our starting point + Point3F startPnt = wPos; + Point3F endPnt = wPos + Point3F(0.0f, 0.0f, -1000.0f); + + S32 blockIndex = -1; + F32 nearT = 1.0f; + + for (U32 i = 0; i < mTerrainBlocks.size(); i++) + { + Point3F tStartPnt, tEndPnt; + + mTerrainBlocks[i]->getWorldTransform().mulP(startPnt, &tStartPnt); + mTerrainBlocks[i]->getWorldTransform().mulP(endPnt, &tEndPnt); + + RayInfo ri; + if (mTerrainBlocks[i]->castRayI(tStartPnt, tEndPnt, &ri, true)) + { + if (ri.t < nearT) + { + blockIndex = i; + nearT = ri.t; + } + } + } + + if (blockIndex > -1) + return mTerrainBlocks[blockIndex]; + + return NULL; +} + +bool TerrainEditor::gridToWorld(const GridPoint & gPoint, Point3F & wPos) +{ + PROFILE_SCOPE( TerrainEditor_GridToWorld ); + + const MatrixF & mat = gPoint.terrainBlock->getTransform(); + Point3F origin; + mat.getColumn(3, &origin); + + wPos.x = gPoint.gridPos.x * gPoint.terrainBlock->getSquareSize() + origin.x; + wPos.y = gPoint.gridPos.y * gPoint.terrainBlock->getSquareSize() + origin.y; + wPos.z = getGridHeight(gPoint) + origin.z; + + return isMainTile(gPoint); +} + +bool TerrainEditor::gridToWorld(const Point2I & gPos, Point3F & wPos, TerrainBlock* terrain) +{ + GridPoint gridPoint; + gridPoint.gridPos = gPos; + gridPoint.terrainBlock = terrain; + + return gridToWorld(gridPoint, wPos); +} + +bool TerrainEditor::worldToGrid(const Point3F & wPos, GridPoint & gPoint) +{ + PROFILE_SCOPE( TerrainEditor_WorldToGrid ); + + // If the grid point TerrainBlock is NULL then find the closest Terrain underneath that + // point - pad a little upward in case our incoming point already lies exactly on the terrain + if (!gPoint.terrainBlock) + gPoint.terrainBlock = getTerrainUnderWorldPoint(wPos + Point3F(0.0f, 0.0f, 0.05f)); + + if (gPoint.terrainBlock == NULL) + return false; + + gPoint.gridPos = gPoint.terrainBlock->getGridPos(wPos); + return isMainTile(gPoint); +} + +bool TerrainEditor::worldToGrid(const Point3F & wPos, Point2I & gPos, TerrainBlock* terrain) +{ + GridPoint gridPoint; + gridPoint.terrainBlock = terrain; + + bool ret = worldToGrid(wPos, gridPoint); + + gPos = gridPoint.gridPos; + + return ret; +} + +bool TerrainEditor::gridToCenter(const Point2I & gPos, Point2I & cPos) const +{ + // TODO: What is this for... megaterrain or tiled terrains? + cPos.x = gPos.x; // & TerrainBlock::BlockMask; + cPos.y = gPos.y;// & TerrainBlock::BlockMask; + + //if (gPos.x == TerrainBlock::BlockSize) + // cPos.x = gPos.x; + //if (gPos.y == TerrainBlock::BlockSize) + // cPos.y = gPos.y; + + //return isMainTile(gPos); + return true; +} + +//------------------------------------------------------------------------------ + +//bool TerrainEditor::getGridInfo(const Point3F & wPos, GridInfo & info) +//{ +// Point2I gPos; +// worldToGrid(wPos, gPos); +// return getGridInfo(gPos, info); +//} + +bool TerrainEditor::getGridInfo(const GridPoint & gPoint, GridInfo & info) +{ + // + info.mGridPoint = gPoint; + info.mMaterial = getGridMaterial(gPoint); + info.mHeight = getGridHeight(gPoint); + info.mWeight = 1.f; + info.mPrimarySelect = true; + info.mMaterialChanged = false; + + Point2I cPos; + gridToCenter(gPoint.gridPos, cPos); + + return isMainTile(gPoint); +} + +bool TerrainEditor::getGridInfo(const Point2I & gPos, GridInfo & info, TerrainBlock* terrain) +{ + GridPoint gridPoint; + gridPoint.gridPos = gPos; + gridPoint.terrainBlock = terrain; + + return getGridInfo(gridPoint, info); +} + +void TerrainEditor::getGridInfos(const GridPoint & gPoint, Vector& infos) +{ + PROFILE_SCOPE( TerrainEditor_GetGridInfos ); + + // First we test against the brush terrain so that we can + // favor it (this should be the same as the active terrain) + bool foundBrush = false; + + GridInfo baseInfo; + if (getGridInfo(gPoint, baseInfo)) + { + infos.push_back(baseInfo); + + foundBrush = true; + } + + // We are going to need the world position to test against + Point3F wPos; + gridToWorld(gPoint, wPos); + + // Now loop through our terrain blocks and decide which ones hit the point + // If we already found a hit against our brush terrain we only add points + // that are relatively close to the found point + for (U32 i = 0; i < mTerrainBlocks.size(); i++) + { + // Skip if we've already found the point on the brush terrain + if (foundBrush && mTerrainBlocks[i] == baseInfo.mGridPoint.terrainBlock) + continue; + + // Get our grid position + Point2I gPos; + worldToGrid(wPos, gPos, mTerrainBlocks[i]); + + GridInfo info; + if (getGridInfo(gPos, info, mTerrainBlocks[i])) + { + // Skip adding this if we already found a GridInfo from the brush terrain + // and the resultant world point isn't equivalent + if (foundBrush) + { + // Convert back to world (since the height can be different) + // Possibly use getHeight() here? + Point3F testWorldPt; + gridToWorld(gPos, testWorldPt, mTerrainBlocks[i]); + + if (mFabs( wPos.z - testWorldPt.z ) > 4.0f ) + continue; + } + + infos.push_back(info); + } + } +} + +void TerrainEditor::setGridInfo(const GridInfo & info, bool checkActive) +{ + PROFILE_SCOPE( TerrainEditor_SetGridInfo ); + + setGridHeight(info.mGridPoint, info.mHeight); + setGridMaterial(info.mGridPoint, info.mMaterial); +} + +F32 TerrainEditor::getGridHeight(const GridPoint & gPoint) +{ + PROFILE_SCOPE( TerrainEditor_GetGridHeight ); + + Point2I cPos; + gridToCenter( gPoint.gridPos, cPos ); + const TerrainFile *file = gPoint.terrainBlock->getFile(); + return fixedToFloat( file->getHeight( cPos.x, cPos.y ) ); +} + +void TerrainEditor::gridUpdateComplete( bool materialChanged ) +{ + PROFILE_SCOPE( TerrainEditor_GridUpdateComplete ); + + // TODO: This updates all terrains and not just the ones + // that were changed. We should keep track of the mGridUpdate + // in world space and transform it into terrain space. + + if(mGridUpdateMin.x <= mGridUpdateMax.x) + { + for (U32 i = 0; i < mTerrainBlocks.size(); i++) + { + TerrainBlock *clientTerrain = getClientTerrain( mTerrainBlocks[i] ); + if ( materialChanged ) + clientTerrain->updateGridMaterials(mGridUpdateMin, mGridUpdateMax); + + mTerrainBlocks[i]->updateGrid(mGridUpdateMin, mGridUpdateMax); + clientTerrain->updateGrid(mGridUpdateMin, mGridUpdateMax); + } + } + + mGridUpdateMin.set( S32_MAX, S32_MAX ); + mGridUpdateMax.set( 0, 0 ); + mNeedsGridUpdate = false; +} + +void TerrainEditor::materialUpdateComplete() +{ + PROFILE_SCOPE( TerrainEditor_MaterialUpdateComplete ); + + if(mActiveTerrain && (mGridUpdateMin.x <= mGridUpdateMax.x)) + { + TerrainBlock * clientTerrain = getClientTerrain(mActiveTerrain); + clientTerrain->updateGridMaterials(mGridUpdateMin, mGridUpdateMax); + } + mGridUpdateMin.set( S32_MAX, S32_MAX ); + mGridUpdateMax.set( 0, 0 ); + mNeedsMaterialUpdate = false; +} + +void TerrainEditor::setGridHeight(const GridPoint & gPoint, const F32 height) +{ + PROFILE_SCOPE( TerrainEditor_SetGridHeight ); + + Point2I cPos; + gridToCenter(gPoint.gridPos, cPos); + + mGridUpdateMin.setMin( cPos ); + mGridUpdateMax.setMax( cPos ); + + gPoint.terrainBlock->setHeight(cPos, height); +} + +U8 TerrainEditor::getGridMaterial( const GridPoint &gPoint ) const +{ + PROFILE_SCOPE( TerrainEditor_GetGridMaterial ); + + Point2I cPos; + gridToCenter( gPoint.gridPos, cPos ); + const TerrainFile *file = gPoint.terrainBlock->getFile(); + return file->getLayerIndex( cPos.x, cPos.y ); +} + +void TerrainEditor::setGridMaterial( const GridPoint &gPoint, U8 index ) +{ + PROFILE_SCOPE( TerrainEditor_SetGridMaterial ); + + Point2I cPos; + gridToCenter( gPoint.gridPos, cPos ); + TerrainFile *file = gPoint.terrainBlock->getFile(); + + // If we changed the empty state then we need + // to do a grid update as well. + U8 currIndex = file->getLayerIndex( cPos.x, cPos.y ); + if ( ( currIndex == (U8)-1 && index != (U8)-1 ) || + ( currIndex != (U8)-1 && index == (U8)-1 ) ) + { + mGridUpdateMin.setMin( cPos ); + mGridUpdateMax.setMax( cPos ); + mNeedsGridUpdate = true; + } + + file->setLayerIndex( cPos.x, cPos.y, index ); +} + +//------------------------------------------------------------------------------ + +TerrainBlock* TerrainEditor::collide(const Gui3DMouseEvent & evt, Point3F & pos) +{ + PROFILE_SCOPE( TerrainEditor_Collide ); + + if (mTerrainBlocks.size() == 0) + return NULL; + + if ( mMouseDown && !dStrcmp(getCurrentAction(),"paintMaterial") ) + { + if ( !mActiveTerrain ) + return NULL; + + Point3F tpos, tvec; + + tpos = evt.pos; + tvec = evt.vec; + + mMousePlane.intersect( evt.pos, evt.vec, &pos ); + + return mActiveTerrain; + } + + const U32 mask = TerrainObjectType; + + Point3F start( evt.pos ); + Point3F end( evt.pos + ( evt.vec * 10000.0f ) ); + + RayInfo rinfo; + bool hit = gServerContainer.castRay( start, end, mask, &rinfo ); + + if ( !hit ) + return NULL; + + pos = rinfo.point; + + return (TerrainBlock*)(rinfo.object); + + // + //// call the terrain block's ray collision routine directly + //Point3F startPnt = event.pos; + //Point3F endPnt = event.pos + event.vec * 1000.0f; + + //S32 blockIndex = -1; + //F32 nearT = 1.0f; + + //for (U32 i = 0; i < mTerrainBlocks.size(); i++) + //{ + // Point3F tStartPnt, tEndPnt; + + // mTerrainBlocks[i]->getWorldTransform().mulP(startPnt, &tStartPnt); + // mTerrainBlocks[i]->getWorldTransform().mulP(endPnt, &tEndPnt); + + // RayInfo ri; + // if (mTerrainBlocks[i]->castRayI(tStartPnt, tEndPnt, &ri, true)) + // { + // if (ri.t < nearT) + // { + // blockIndex = i; + // nearT = ri.t; + // } + // } + //} + + //if (blockIndex > -1) + //{ + // pos.interpolate(startPnt, endPnt, nearT); + + // return mTerrainBlocks[blockIndex]; + //} + + //return NULL; +} + +//------------------------------------------------------------------------------ + +void TerrainEditor::updateGuiInfo() +{ + PROFILE_SCOPE( TerrainEditor_UpdateGuiInfo ); + + char buf[128]; + + // mouse num grids + // mouse avg height + // selection num grids + // selection avg height + dSprintf(buf, sizeof(buf), "%d %g %g %g %d %g", + mMouseBrush->size(), mMouseBrush->getMinHeight(), + mMouseBrush->getAvgHeight(), mMouseBrush->getMaxHeight(), + mDefaultSel.size(), mDefaultSel.getAvgHeight()); + Con::executef(this, "onGuiUpdate", buf); + + // If the brush setup has changed send out + // a notification of that! + if ( mBrushChanged && isMethod( "onBrushChanged" ) ) + { + mBrushChanged = false; + Con::executef( this, "onBrushChanged" ); + } +} + +//------------------------------------------------------------------------------ + +void TerrainEditor::onPreRender() +{ + PROFILE_SCOPE( TerrainEditor_OnPreRender ); + + if ( mNeedsGridUpdate ) + gridUpdateComplete( mNeedsMaterialUpdate ); + else if ( mNeedsMaterialUpdate ) + materialUpdateComplete(); + + Parent::onPreRender(); +} + +void TerrainEditor::renderScene(const RectI &) +{ + PROFILE_SCOPE( TerrainEditor_RenderScene ); + + if(mTerrainBlocks.size() == 0) + return; + + if(!mSelectionHidden) + renderSelection(mDefaultSel, ColorF::RED, ColorF::GREEN, ColorF::BLUE, ColorF::BLUE, true, false); + + if(mRenderBrush && mMouseBrush->size()) + renderBrush(*mMouseBrush, ColorF::GREEN, ColorF::RED, ColorF::BLUE, ColorF::BLUE, false, true); + + if(mRenderBorder) + renderBorder(); +} + +//------------------------------------------------------------------------------ + +void TerrainEditor::renderGui( Point2I offset, const RectI &updateRect ) +{ + PROFILE_SCOPE( TerrainEditor_RenderGui ); + + if ( !mActiveTerrain ) + return; + + // Just in case... + if ( mMouseBrush->getGridPoint().terrainBlock != mActiveTerrain ) + mMouseBrush->setTerrain( mActiveTerrain ); + + mMouseBrush->render(); +} + +void TerrainEditor::renderPoints( const Vector &pointList ) +{ + PROFILE_SCOPE( TerrainEditor_RenderPoints ); + + const U32 pointCount = pointList.size(); + const U32 vertCount = pointCount * 6; + + GFXStateBlockDesc desc; + desc.setBlend( true ); + desc.setZReadWrite( false, false ); + GFX->setupGenericShaders(); + GFX->setStateBlockByDesc( desc ); + + U32 vertsLeft = vertCount; + U32 offset = 0; + + while ( vertsLeft > 0 ) + { + U32 vertsThisDrawCall = getMin( (U32)vertsLeft, (U32)MAX_DYNAMIC_VERTS ); + vertsLeft -= vertsThisDrawCall; + + GFXVertexBufferHandle vbuff( GFX, vertsThisDrawCall, GFXBufferTypeVolatile ); + GFXVertexPC *vert = vbuff.lock(); + + const U32 loops = vertsThisDrawCall / 6; + + for ( S32 i = 0; i < loops; i++ ) + { + const GFXVertexPCT &pointInfo = pointList[i + offset]; + + vert[0].color = vert[1].color = vert[2].color = vert[3].color = vert[4].color = vert[5].color = pointInfo.color; + + + const F32 halfSize = pointInfo.texCoord.x * 0.5f; + const Point3F &pos = pointInfo.point; + + Point3F p0( pos.x - halfSize, pos.y - halfSize, 0.0f ); + Point3F p1( pos.x + halfSize, pos.y - halfSize, 0.0f ); + Point3F p2( pos.x + halfSize, pos.y + halfSize, 0.0f ); + Point3F p3( pos.x - halfSize, pos.y + halfSize, 0.0f ); + + vert[0].point = p0; + vert[1].point = p1; + vert[2].point = p2; + + vert[3].point = p0; + vert[4].point = p2; + vert[5].point = p3; + + vert += 6; + } + + vbuff.unlock(); + + GFX->setVertexBuffer( vbuff ); + + GFX->drawPrimitive( GFXTriangleList, 0, vertsThisDrawCall / 3 ); + + offset += loops; + } +} + + +//------------------------------------------------------------------------------ + +void TerrainEditor::renderSelection( const Selection & sel, const ColorF & inColorFull, const ColorF & inColorNone, const ColorF & outColorFull, const ColorF & outColorNone, bool renderFill, bool renderFrame ) +{ + PROFILE_SCOPE( TerrainEditor_RenderSelection ); + + // Draw nothing if nothing selected. + if(sel.size() == 0) + return; + + Vector vertexBuffer; + ColorF color; + ColorI iColor; + + vertexBuffer.setSize(sel.size() * 5); + + F32 squareSize = ( mActiveTerrain ) ? mActiveTerrain->getSquareSize() : 1; + + // 'RenderVertexSelection' looks really bad so just always use the good one. + if( false && mRenderVertexSelection) + { + + for(U32 i = 0; i < sel.size(); i++) + { + Point3F wPos; + bool center = gridToWorld(sel[i].mGridPoint, wPos); + + F32 weight = sel[i].mWeight; + + if(center) + { + if ( weight < 0.f || weight > 1.f ) + color = inColorFull; + else + color.interpolate( inColorNone, inColorFull, weight ); + } + else + { + if ( weight < 0.f || weight > 1.f) + color = outColorFull; + else + color.interpolate( outColorFull, outColorNone, weight ); + } + // + iColor = color; + + GFXVertexPC *verts = &(vertexBuffer[i * 5]); + + verts[0].point = wPos + Point3F(-squareSize, -squareSize, 0); + verts[0].color = iColor; + verts[1].point = wPos + Point3F( squareSize, -squareSize, 0); + verts[1].color = iColor; + verts[2].point = wPos + Point3F( squareSize, squareSize, 0); + verts[2].color = iColor; + verts[3].point = wPos + Point3F(-squareSize, squareSize, 0); + verts[3].color = iColor; + verts[4].point = verts[0].point; + verts[4].color = iColor; + } + } + else + { + // walk the points in the selection + for(U32 i = 0; i < sel.size(); i++) + { + Point2I gPos = sel[i].mGridPoint.gridPos; + + GFXVertexPC *verts = &(vertexBuffer[i * 5]); + + bool center = gridToWorld(sel[i].mGridPoint, verts[0].point); + gridToWorld(Point2I(gPos.x + 1, gPos.y), verts[1].point, sel[i].mGridPoint.terrainBlock); + gridToWorld(Point2I(gPos.x + 1, gPos.y + 1), verts[2].point, sel[i].mGridPoint.terrainBlock); + gridToWorld(Point2I(gPos.x, gPos.y + 1), verts[3].point, sel[i].mGridPoint.terrainBlock); + verts[4].point = verts[0].point; + + F32 weight = sel[i].mWeight; + + if( !mRenderSolidBrush ) + { + if ( center ) + { + if ( weight < 0.f || weight > 1.f ) + color = inColorFull; + else + color.interpolate(inColorNone, inColorFull, weight ); + } + else + { + if( weight < 0.f || weight > 1.f ) + color = outColorFull; + else + color.interpolate(outColorFull, outColorNone, weight ); + } + + iColor = color; + } + else + { + if ( center ) + { + iColor = inColorNone; + } + else + { + iColor = outColorFull; + } + } + + verts[0].color = iColor; + verts[1].color = iColor; + verts[2].color = iColor; + verts[3].color = iColor; + verts[4].color = iColor; + } + } + + // Render this bad boy, by stuffing everything into a volatile buffer + // and rendering... + GFXVertexBufferHandle selectionVB(GFX, vertexBuffer.size(), GFXBufferTypeStatic); + + selectionVB.lock(0, vertexBuffer.size()); + + // Copy stuff + dMemcpy((void*)&selectionVB[0], (void*)&vertexBuffer[0], sizeof(GFXVertexPC) * vertexBuffer.size()); + + selectionVB.unlock(); + + GFX->setupGenericShaders(); + GFX->setStateBlock( mStateBlock ); + GFX->setVertexBuffer(selectionVB); + + if(renderFill) + for(U32 i=0; i < sel.size(); i++) + GFX->drawPrimitive( GFXTriangleFan, i*5, 4); + + if(renderFrame) + for(U32 i=0; i < sel.size(); i++) + GFX->drawPrimitive( GFXLineStrip , i*5, 4); +} + +void TerrainEditor::renderBrush( const Brush & brush, const ColorF & inColorFull, const ColorF & inColorNone, const ColorF & outColorFull, const ColorF & outColorNone, bool renderFill, bool renderFrame ) +{ +} + +void TerrainEditor::renderBorder() +{ + // TODO: Disabled rendering the terrain borders... it was + // very annoying getting a fullscreen green tint on things. + // + // We should consider killing this all together or coming + // up with a new technique. + /* + Point2I pos(0,0); + Point2I dir[4] = { + Point2I(1,0), + Point2I(0,1), + Point2I(-1,0), + Point2I(0,-1) + }; + + GFX->setStateBlock( mStateBlock ); + + // + if(mBorderLineMode) + { + PrimBuild::color(mBorderFrameColor); + + PrimBuild::begin( GFXLineStrip, TerrainBlock::BlockSize * 4 + 1 ); + for(U32 i = 0; i < 4; i++) + { + for(U32 j = 0; j < TerrainBlock::BlockSize; j++) + { + Point3F wPos; + gridToWorld(pos, wPos, mActiveTerrain); + PrimBuild::vertex3fv( wPos ); + pos += dir[i]; + } + } + + Point3F wPos; + gridToWorld(Point2I(0,0), wPos, mActiveTerrain); + PrimBuild::vertex3fv( wPos ); + PrimBuild::end(); + } + else + { + GridSquare * gs = mActiveTerrain->findSquare(TerrainBlock::BlockShift, Point2I(0,0)); + F32 height = F32(gs->maxHeight) * 0.03125f + mBorderHeight; + + const MatrixF & mat = mActiveTerrain->getTransform(); + Point3F pos; + mat.getColumn(3, &pos); + + Point2F min(pos.x, pos.y); + Point2F max(pos.x + TerrainBlock::BlockSize * mActiveTerrain->getSquareSize(), + pos.y + TerrainBlock::BlockSize * mActiveTerrain->getSquareSize()); + + ColorI & a = mBorderFillColor; + ColorI & b = mBorderFrameColor; + + for(U32 i = 0; i < 2; i++) + { + // + if(i){ PrimBuild::color(a); PrimBuild::begin( GFXTriangleFan, 4 ); } else { PrimBuild::color(b); PrimBuild::begin( GFXLineStrip, 5 ); } + + PrimBuild::vertex3f(min.x, min.y, 0); + PrimBuild::vertex3f(max.x, min.y, 0); + PrimBuild::vertex3f(max.x, min.y, height); + PrimBuild::vertex3f(min.x, min.y, height); + if(!i) PrimBuild::vertex3f( min.x, min.y, 0.f ); + PrimBuild::end(); + + // + if(i){ PrimBuild::color(a); PrimBuild::begin( GFXTriangleFan, 4 ); } else { PrimBuild::color(b); PrimBuild::begin( GFXLineStrip, 5 ); } + PrimBuild::vertex3f(min.x, max.y, 0); + PrimBuild::vertex3f(max.x, max.y, 0); + PrimBuild::vertex3f(max.x, max.y, height); + PrimBuild::vertex3f(min.x, max.y, height); + if(!i) PrimBuild::vertex3f( min.x, min.y, 0.f ); + PrimBuild::end(); + + // + if(i){ PrimBuild::color(a); PrimBuild::begin( GFXTriangleFan, 4 ); } else { PrimBuild::color(b); PrimBuild::begin( GFXLineStrip, 5 ); } + PrimBuild::vertex3f(min.x, min.y, 0); + PrimBuild::vertex3f(min.x, max.y, 0); + PrimBuild::vertex3f(min.x, max.y, height); + PrimBuild::vertex3f(min.x, min.y, height); + if(!i) PrimBuild::vertex3f( min.x, min.y, 0.f ); + PrimBuild::end(); + + // + if(i){ PrimBuild::color(a); PrimBuild::begin( GFXTriangleFan, 4 ); } else { PrimBuild::color(b); PrimBuild::begin( GFXLineStrip, 5 ); } + PrimBuild::vertex3f(max.x, min.y, 0); + PrimBuild::vertex3f(max.x, max.y, 0); + PrimBuild::vertex3f(max.x, max.y, height); + PrimBuild::vertex3f(max.x, min.y, height); + if(!i) PrimBuild::vertex3f( min.x, min.y, 0.f ); + PrimBuild::end(); + } + } + */ +} + +void TerrainEditor::submitUndo( Selection *sel ) +{ + // Grab the mission editor undo manager. + UndoManager *undoMan = NULL; + if ( !Sim::findObject( "EUndoManager", undoMan ) ) + { + Con::errorf( "TerrainEditor::submitUndo() - EUndoManager not found!" ); + return; + } + + // Create and submit the action. + TerrainEditorUndoAction *action = new TerrainEditorUndoAction( "Terrain Editor Action" ); + action->mSel = sel; + action->mTerrainEditor = this; + undoMan->addAction( action ); + + // Mark the editor as dirty! + setDirty(); +} + +void TerrainEditor::TerrainEditorUndoAction::undo() +{ + // NOTE: This function also handles TerrainEditorUndoAction::redo(). + + bool materialChanged = false; + + for (U32 i = 0; i < mSel->size(); i++) + { + // Grab the current grid info for this point. + GridInfo info; + mTerrainEditor->getGridInfo( (*mSel)[i].mGridPoint, info ); + info.mMaterialChanged = (*mSel)[i].mMaterialChanged; + + materialChanged |= info.mMaterialChanged; + + // Restore the previous grid info. + mTerrainEditor->setGridInfo( (*mSel)[i] ); + + // Save the old grid info so we can + // restore it later. + (*mSel)[i] = info; + } + + // Mark the editor as dirty! + mTerrainEditor->setDirty(); + mTerrainEditor->gridUpdateComplete( materialChanged ); + mTerrainEditor->mMouseBrush->update(); +} + +void TerrainEditor::submitMaterialUndo( String actionName ) +{ + // Grab the mission editor undo manager. + UndoManager *undoMan = NULL; + if ( !Sim::findObject( "EUndoManager", undoMan ) ) + { + Con::errorf( "TerrainEditor::submitMaterialUndo() - EUndoManager not found!" ); + return; + } + + TerrainBlock *terr = getClientTerrain(); + + // Create and submit the action. + TerrainMaterialUndoAction *action = new TerrainMaterialUndoAction( actionName ); + action->mTerrain = terr; + action->mMaterials = terr->getMaterials(); + action->mLayerMap = terr->getLayerMap(); + action->mEditor = this; + + undoMan->addAction( action ); + + // Mark the editor as dirty! + setDirty(); +} + +void TerrainEditor::onMaterialUndo( TerrainBlock *terr ) +{ + setDirty(); + scheduleMaterialUpdate(); + setGridUpdateMinMax(); + + terr->mDetailsDirty = true; + terr->mLayerTexDirty = true; + + Con::executef( this, "onMaterialUndo" ); +} + +void TerrainEditor::TerrainMaterialUndoAction::undo() +{ + Vector tempMaterials = mTerrain->getMaterials(); + Vector tempLayers = mTerrain->getLayerMap(); + + mTerrain->setMaterials(mMaterials); + mTerrain->setLayerMap(mLayerMap); + + mMaterials = tempMaterials; + mLayerMap = tempLayers; + + mEditor->onMaterialUndo( mTerrain ); +} + +void TerrainEditor::TerrainMaterialUndoAction::redo() +{ + undo(); +} + +class TerrainProcessActionEvent : public SimEvent +{ + U32 mSequence; +public: + TerrainProcessActionEvent(U32 seq) + { + mSequence = seq; + } + void process(SimObject *object) + { + ((TerrainEditor *) object)->processActionTick(mSequence); + } +}; + +void TerrainEditor::processActionTick(U32 sequence) +{ + if(mMouseDownSeq == sequence) + { + Sim::postEvent(this, new TerrainProcessActionEvent(mMouseDownSeq), Sim::getCurrentTime() + 30); + mCurrentAction->process(mMouseBrush, mLastEvent, false, TerrainAction::Update); + } +} + +bool TerrainEditor::onInputEvent(const InputEventInfo & event) +{ + /* + if ( mRightMousePassThru && + event.deviceType == KeyboardDeviceType && + event.objType == SI_KEY && + event.objInst == KEY_TAB && + event.action == SI_MAKE ) + { + if ( isMethod( "onToggleToolWindows" ) ) + Con::executef( this, "onToggleToolWindows" ); + } + */ + + return Parent::onInputEvent( event ); +} + +void TerrainEditor::on3DMouseDown(const Gui3DMouseEvent & event) +{ + getRoot()->showCursor( false ); + + if(mTerrainBlocks.size() == 0) + return; + + if (!dStrcmp(getCurrentAction(),"paintMaterial")) + { + Point3F pos; + TerrainBlock* hitTerrain = collide(event, pos); + + if(!hitTerrain) + return; + + // Set the active terrain + bool changed = mActiveTerrain != hitTerrain; + mActiveTerrain = hitTerrain; + + if (changed) + { + Con::executef(this, "onActiveTerrainChange", Con::getIntArg(hitTerrain->getId())); + mMouseBrush->setTerrain(mActiveTerrain); + //if(mRenderBrush) + //mCursorVisible = false; + mMousePos = pos; + + mMouseBrush->setPosition(mMousePos); + + return; + } + } + else if ((event.modifier & SI_ALT) && !dStrcmp(getCurrentAction(),"setHeight")) + { + // Set value to terrain height at mouse position + GridInfo info; + getGridInfo(mMouseBrush->getGridPoint(), info); + mSetHeightVal = info.mHeight; + mBrushChanged = true; + return; + } + + mMousePlane.set( mMousePos, Point3F(0,0,1) ); + mMouseDown = true; + + mSelectionLocked = false; + + mouseLock(); + mMouseDownSeq++; + mUndoSel = new Selection; + mCurrentAction->process(mMouseBrush, event, true, TerrainAction::Begin); + // process on ticks - every 30th of a second. + Sim::postEvent(this, new TerrainProcessActionEvent(mMouseDownSeq), Sim::getCurrentTime() + 30); +} + +void TerrainEditor::on3DMouseMove(const Gui3DMouseEvent & event) +{ + PROFILE_SCOPE( TerrainEditor_On3DMouseMove ); + + if(mTerrainBlocks.size() == 0) + return; + + Point3F pos; + TerrainBlock* hitTerrain = collide(event, pos); + + if(!hitTerrain) + { + mMouseBrush->reset(); + } + else + { + // We do not change the active terrain as the mouse moves when + // in painting mode. This is because it causes the material + // window to change as you cursor over to it. + if ( dStrcmp(getCurrentAction(),"paintMaterial") != 0 ) + { + // Set the active terrain + bool changed = mActiveTerrain != hitTerrain; + mActiveTerrain = hitTerrain; + + if (changed) + Con::executef(this, "onActiveTerrainChange", Con::getIntArg(hitTerrain->getId())); + } + + mMousePos = pos; + + mMouseBrush->setTerrain(mActiveTerrain); + mMouseBrush->setPosition(mMousePos); + } +} + +void TerrainEditor::on3DMouseDragged(const Gui3DMouseEvent & event) +{ + PROFILE_SCOPE( TerrainEditor_On3DMouseDragged ); + + if ( mTerrainBlocks.empty() ) + return; + + if ( !isMouseLocked() ) + return; + + Point3F pos; + + if ( !mSelectionLocked ) + { + if ( !collide( event, pos) ) + mMouseBrush->reset(); + } + + // check if the mouse has actually moved in grid space + bool selChanged = false; + if ( !mSelectionLocked ) + { + Point2I gMouse; + Point2I gLastMouse; + worldToGrid( pos, gMouse ); + worldToGrid( mMousePos, gLastMouse ); + + mMousePos = pos; + mMouseBrush->setPosition( mMousePos ); + + selChanged = gMouse != gLastMouse; + } + + mCurrentAction->process( mMouseBrush, event, true, TerrainAction::Update ); +} + +void TerrainEditor::on3DMouseUp(const Gui3DMouseEvent & event) +{ + mMouseDown = false; + getRoot()->showCursor( true ); + + if ( mTerrainBlocks.size() == 0 ) + return; + + if ( isMouseLocked() ) + { + mouseUnlock(); + mMouseDownSeq++; + mCurrentAction->process( mMouseBrush, event, false, TerrainAction::End ); + + if ( mUndoSel->size() ) + submitUndo( mUndoSel ); + else + delete mUndoSel; + + mUndoSel = 0; + mInAction = false; + } +} + +bool TerrainEditor::onMouseWheelDown( const GuiEvent & event ) +{ + if ( event.modifier & SI_PRIMARY_CTRL && event.modifier & SI_SHIFT ) + { + setBrushPressure( mBrushPressure - 0.1f ); + return true; + } + else if ( event.modifier & SI_SHIFT ) + { + setBrushSoftness( mBrushSoftness + 0.05f ); + return true; + } + else if ( event.modifier & SI_PRIMARY_CTRL ) + { + Point2I newBrush = getBrushSize() - Point2I(1,1); + setBrushSize( newBrush.x, newBrush.y ); + return true; + } + + return Parent::onMouseWheelDown( event ); +} + +bool TerrainEditor::onMouseWheelUp( const GuiEvent & event ) +{ + if ( event.modifier & SI_PRIMARY_CTRL && event.modifier & SI_SHIFT ) + { + setBrushPressure( mBrushPressure + 0.1f ); + return true; + } + else if ( event.modifier & SI_SHIFT ) + { + setBrushSoftness( mBrushSoftness - 0.05f ); + return true; + } + else if( event.modifier & SI_PRIMARY_CTRL ) + { + Point2I newBrush = getBrushSize() + Point2I(1,1); + setBrushSize( newBrush.x, newBrush.y ); + return true; + } + + return Parent::onMouseWheelUp( event ); +} + +//------------------------------------------------------------------------------ +// any console function which depends on a terrainBlock attached to the editor +// should call this +bool checkTerrainBlock(TerrainEditor * object, const char * funcName) +{ + if(!object->terrainBlockValid()) + { + Con::errorf(ConsoleLogEntry::Script, "TerrainEditor::%s: not attached to a terrain block!", funcName); + return(false); + } + return(true); +} + +void TerrainEditor::attachTerrain(TerrainBlock *terrBlock) +{ + mActiveTerrain = terrBlock; + mTerrainBlocks.push_back_unique(terrBlock); +} + +void TerrainEditor::detachTerrain(TerrainBlock *terrBlock) +{ + if (mActiveTerrain == terrBlock) + mActiveTerrain = NULL; //do we want to set this to an existing terrain? + + if (mMouseBrush->getGridPoint().terrainBlock == terrBlock) + mMouseBrush->setTerrain(NULL); + + // reset the brush as its gridinfos may still have references to the old terrain + mMouseBrush->reset(); + + mTerrainBlocks.remove(terrBlock); +} + +TerrainBlock* TerrainEditor::getTerrainBlock(S32 index) +{ + if(index < 0 || index >= mTerrainBlocks.size()) + return NULL; + + return mTerrainBlocks[index]; +} + +void TerrainEditor::getTerrainBlocksMaterialList(Vector& list) +{ + for(S32 i=0; igetMaterialCount(); ++m) + { + TerrainMaterial* mat = tb->getMaterial(m); + if (mat) + list.push_back_unique(mat->getInternalName()); + } + } +} + +void TerrainEditor::setBrushType( const char *type ) +{ + if ( mMouseBrush && dStrcmp( mMouseBrush->getType(), type ) == 0 ) + return; + + if(!dStricmp(type, "box")) + { + delete mMouseBrush; + mMouseBrush = new BoxBrush(this); + mBrushChanged = true; + } + else if(!dStricmp(type, "ellipse")) + { + delete mMouseBrush; + mMouseBrush = new EllipseBrush(this); + mBrushChanged = true; + } + else if(!dStricmp(type, "selection")) + { + delete mMouseBrush; + mMouseBrush = new SelectionBrush(this); + mBrushChanged = true; + } + else {} +} + +const char* TerrainEditor::getBrushType() const +{ + if ( mMouseBrush ) + return mMouseBrush->getType(); + + return ""; +} + +void TerrainEditor::setBrushSize( S32 w, S32 h ) +{ + w = mClamp( w, 1, mMaxBrushSize.x ); + h = mClamp( h, 1, mMaxBrushSize.y ); + + if ( w == mBrushSize.x && h == mBrushSize.y ) + return; + + mBrushSize.set( w, h ); + mBrushChanged = true; + + if ( mMouseBrush ) + { + mMouseBrush->setSize( mBrushSize ); + + if ( mMouseBrush->getGridPoint().terrainBlock ) + mMouseBrush->rebuild(); + } +} + +void TerrainEditor::setBrushPressure( F32 pressure ) +{ + pressure = mClampF( pressure, 0.01f, 1.0f ); + + if ( mBrushPressure == pressure ) + return; + + mBrushPressure = pressure; + mBrushChanged = true; + + if ( mMouseBrush && mMouseBrush->getGridPoint().terrainBlock ) + mMouseBrush->rebuild(); +} + +void TerrainEditor::setBrushSoftness( F32 softness ) +{ + softness = mClampF( softness, 0.01f, 1.0f ); + + if ( mBrushSoftness == softness ) + return; + + mBrushSoftness = softness; + mBrushChanged = true; + + if ( mMouseBrush && mMouseBrush->getGridPoint().terrainBlock ) + mMouseBrush->rebuild(); +} + +const char* TerrainEditor::getBrushPos() +{ + AssertFatal(mMouseBrush!=NULL, "TerrainEditor::getBrushPos: no mouse brush!"); + + Point2I pos = mMouseBrush->getPosition(); + char * ret = Con::getReturnBuffer(32); + dSprintf(ret, 32, "%d %d", pos.x, pos.y); + return(ret); +} + +void TerrainEditor::setBrushPos(Point2I pos) +{ + AssertFatal(mMouseBrush!=NULL, "TerrainEditor::setBrushPos: no mouse brush!"); + mMouseBrush->setPosition(pos); +} + +void TerrainEditor::setAction(const char* action) +{ + for(U32 i = 0; i < mActions.size(); i++) + { + if(!dStricmp(mActions[i]->getName(), action)) + { + mCurrentAction = mActions[i]; + + // + mRenderBrush = mCurrentAction->useMouseBrush(); + return; + } + } +} + +const char* TerrainEditor::getActionName(U32 index) +{ + if(index >= mActions.size()) + return(""); + return(mActions[index]->getName()); +} + +const char* TerrainEditor::getCurrentAction() const +{ + return(mCurrentAction->getName()); +} + +S32 TerrainEditor::getNumActions() +{ + return(mActions.size()); +} + +void TerrainEditor::resetSelWeights(bool clear) +{ + // + if(!clear) + { + for(U32 i = 0; i < mDefaultSel.size(); i++) + { + mDefaultSel[i].mPrimarySelect = false; + mDefaultSel[i].mWeight = 1.f; + } + return; + } + + Selection sel; + + U32 i; + for(i = 0; i < mDefaultSel.size(); i++) + { + if(mDefaultSel[i].mPrimarySelect) + { + mDefaultSel[i].mWeight = 1.f; + sel.add(mDefaultSel[i]); + } + } + + mDefaultSel.reset(); + + for(i = 0; i < sel.size(); i++) + mDefaultSel.add(sel[i]); +} + +void TerrainEditor::clearSelection() +{ + mDefaultSel.reset(); +} + +void TerrainEditor::processAction(const char* sAction) +{ + if(!checkTerrainBlock(this, "processAction")) + return; + + TerrainAction * action = mCurrentAction; + if (dStrcmp(sAction, "") != 0) + { + action = lookupAction(sAction); + + if(!action) + { + Con::errorf(ConsoleLogEntry::General, "TerrainEditor::cProcessAction: invalid action name '%s'.", sAction); + return; + } + } + + if(!getCurrentSel()->size() && !mProcessUsesBrush) + return; + + mUndoSel = new Selection; + + Gui3DMouseEvent event; + if(mProcessUsesBrush) + action->process(mMouseBrush, event, true, TerrainAction::Process); + else + action->process(getCurrentSel(), event, true, TerrainAction::Process); + + // check if should delete the undo + if(mUndoSel->size()) + submitUndo( mUndoSel ); + else + delete mUndoSel; + + mUndoSel = 0; +} + +S32 TerrainEditor::getNumTextures() +{ + if(!checkTerrainBlock(this, "getNumTextures")) + return(0); + + // walk all the possible material lists and count them.. + U32 count = 0; + for (U32 t = 0; t < mTerrainBlocks.size(); t++) + count += mTerrainBlocks[t]->getMaterialCount(); + + return count; +} + +void TerrainEditor::markEmptySquares() +{ + if(!checkTerrainBlock(this, "markEmptySquares")) + return; + + // TODO! + /* + // build a list of all the marked interiors + Vector interiors; + U32 mask = InteriorObjectType; + gServerContainer.findObjects(mask, findObjectsCallback, &interiors); + + // walk the terrains and empty any grid which clips to an interior + for (U32 i = 0; i < mTerrainBlocks.size(); i++) + { + for(U32 x = 0; x < TerrainBlock::BlockSize; x++) + { + for(U32 y = 0; y < TerrainBlock::BlockSize; y++) + { + TerrainBlock::Material * material = mTerrainBlocks[i]->getMaterial(x,y); + material->flags |= ~(TerrainBlock::Material::Empty); + + Point3F a, b; + gridToWorld(Point2I(x,y), a, mTerrainBlocks[i]); + gridToWorld(Point2I(x+1,y+1), b, mTerrainBlocks[i]); + + Box3F box; + box.minExtents = a; + box.maxExtents = b; + + box.minExtents.setMin(b); + box.maxExtents.setMax(a); + + const MatrixF & terrOMat = mTerrainBlocks[i]->getTransform(); + const MatrixF & terrWMat = mTerrainBlocks[i]->getWorldTransform(); + + terrWMat.mulP(box.minExtents); + terrWMat.mulP(box.maxExtents); + + for(U32 i = 0; i < interiors.size(); i++) + { + MatrixF mat = interiors[i]->getWorldTransform(); + mat.scale(interiors[i]->getScale()); + mat.mul(terrOMat); + + U32 waterMark = FrameAllocator::getWaterMark(); + U16* zoneVector = (U16*)FrameAllocator::alloc(interiors[i]->getDetailLevel(0)->getNumZones()); + U32 numZones = 0; + interiors[i]->getDetailLevel(0)->scanZones(box, mat, + zoneVector, &numZones); + if (numZones != 0) + { + Con::printf("%d %d", x, y); + material->flags |= TerrainBlock::Material::Empty; + FrameAllocator::setWaterMark(waterMark); + break; + } + FrameAllocator::setWaterMark(waterMark); + } + } + } + } + + // rebuild stuff.. + for (U32 i = 0; i < mTerrainBlocks.size(); i++) + { + mTerrainBlocks[i]->buildGridMap(); + mTerrainBlocks[i]->rebuildEmptyFlags(); + mTerrainBlocks[i]->packEmptySquares(); + } + */ +} + +void TerrainEditor::mirrorTerrain(S32 mirrorIndex) +{ + if(!checkTerrainBlock(this, "mirrorTerrain")) + return; + + // TODO! + /* + TerrainBlock * terrain = mActiveTerrain; + setDirty(); + + // + enum { + top = BIT(0), + bottom = BIT(1), + left = BIT(2), + right = BIT(3) + }; + + U32 sides[8] = + { + bottom, + bottom | left, + left, + left | top, + top, + top | right, + right, + bottom | right + }; + + U32 n = TerrainBlock::BlockSize; + U32 side = sides[mirrorIndex % 8]; + bool diag = mirrorIndex & 0x01; + + Point2I src((side & right) ? (n - 1) : 0, (side & bottom) ? (n - 1) : 0); + Point2I dest((side & left) ? (n - 1) : 0, (side & top) ? (n - 1) : 0); + Point2I origSrc(src); + Point2I origDest(dest); + + // determine the run length + U32 minStride = ((side & top) || (side & bottom)) ? n : n / 2; + U32 majStride = ((side & left) || (side & right)) ? n : n / 2; + + Point2I srcStep((side & right) ? -1 : 1, (side & bottom) ? -1 : 1); + Point2I destStep((side & left) ? -1 : 1, (side & top) ? -1 : 1); + + // + U16 * heights = terrain->getHeightAddress(0,0); + U8 * baseMaterials = terrain->getBaseMaterialAddress(0,0); + TerrainBlock::Material * materials = terrain->getMaterial(0,0); + + // create an undo selection + Selection * undo = new Selection; + + // walk through all the positions + for(U32 i = 0; i < majStride; i++) + { + for(U32 j = 0; j < minStride; j++) + { + // skip the same position + if(src != dest) + { + U32 si = src.x + (src.y << TerrainBlock::BlockShift); + U32 di = dest.x + (dest.y << TerrainBlock::BlockShift); + + // add to undo selection + GridInfo info; + getGridInfo(dest, info, terrain); + undo->add(info); + + //... copy info... (height, basematerial, material) + heights[di] = heights[si]; + baseMaterials[di] = baseMaterials[si]; + materials[di] = materials[si]; + } + + // get to the new position + src.x += srcStep.x; + diag ? (dest.y += destStep.y) : (dest.x += destStep.x); + } + + // get the next position for a run + src.y += srcStep.y; + diag ? (dest.x += destStep.x) : (dest.y += destStep.y); + + // reset the minor run + src.x = origSrc.x; + diag ? (dest.y = origDest.y) : (dest.x = origDest.x); + + // shorten the run length for diag runs + if(diag) + minStride--; + } + + // rebuild stuff.. + terrain->buildGridMap(); + terrain->rebuildEmptyFlags(); + terrain->packEmptySquares(); + + // add undo selection + submitUndo( undo ); + */ +} + +bool TerrainEditor::isPointInTerrain( const GridPoint & gPoint) +{ + PROFILE_SCOPE( TerrainEditor_IsPointInTerrain ); + + Point2I cPos; + gridToCenter( gPoint.gridPos, cPos ); + const TerrainFile *file = gPoint.terrainBlock->getFile(); + return file->isPointInTerrain( cPos.x, cPos.y ); +} + +void TerrainEditor::reorderMaterial( S32 index, S32 orderPos ) +{ + TerrainBlock *terr = getClientTerrain(); + Vector layerMap = terr->getLayerMap(); + Vector materials = terr->getMaterials(); + + TerrainMaterial *pMat = materials[index]; + + submitMaterialUndo( String::ToString( "Reordered %s Material", terr->getMaterialName(index) ) ); + + materials.erase( index ); + materials.insert( orderPos, pMat ); + + Vector::iterator itr = layerMap.begin(); + for ( ; itr != layerMap.end(); itr++ ) + { + // Was previous material, set to new index. + if ( *itr == index ) + *itr = orderPos; + else + { + // We removed a Material prior to this one, bump it down. + if ( *itr > index ) + (*itr)--; + // We added a Material prior to this one, bump it up. + if ( *itr >= orderPos ) + (*itr)++; + } + } + + terr->setMaterials( materials ); + terr->setLayerMap( layerMap ); + + // We didn't really just "undo" but it happens to do everything we + // need to update the materials and gui. + onMaterialUndo( terr ); +} + +//------------------------------------------------------------------------------ + +ConsoleMethod( TerrainEditor, attachTerrain, void, 2, 3, "(TerrainBlock terrain)") +{ + SimSet * missionGroup = dynamic_cast(Sim::findObject("MissionGroup")); + if (!missionGroup) + { + Con::errorf(ConsoleLogEntry::Script, "TerrainEditor::attach: no mission group found"); + return; + } + + VectorPtr terrains; + + // attach to first found terrainBlock + if (argc == 2) + { + for(SimSetIterator itr(missionGroup); *itr; ++itr) + { + TerrainBlock* terrBlock = dynamic_cast(*itr); + + if (terrBlock) + terrains.push_back(terrBlock); + } + + //if (terrains.size() == 0) + // Con::errorf(ConsoleLogEntry::Script, "TerrainEditor::attach: no TerrainBlock objects found!"); + } + else // attach to named object + { + TerrainBlock* terrBlock = dynamic_cast(Sim::findObject(argv[2])); + + if (terrBlock) + terrains.push_back(terrBlock); + + if(terrains.size() == 0) + Con::errorf(ConsoleLogEntry::Script, "TerrainEditor::attach: failed to attach to object '%s'", argv[2]); + } + + if (terrains.size() > 0) + { + for (U32 i = 0; i < terrains.size(); i++) + { + if (!terrains[i]->isServerObject()) + { + terrains[i] = NULL; + + Con::errorf(ConsoleLogEntry::Script, "TerrainEditor::attach: cannot attach to client TerrainBlock"); + } + } + } + + for (U32 i = 0; i < terrains.size(); i++) + { + if (terrains[i]) + object->attachTerrain(terrains[i]); + } +} + +ConsoleMethod( TerrainEditor, getTerrainBlockCount, S32, 2, 2, "()") +{ + return object->getTerrainBlockCount(); +} + +ConsoleMethod( TerrainEditor, getTerrainBlock, S32, 3, 3, "(S32 index)") +{ + TerrainBlock* tb = object->getTerrainBlock(dAtoi(argv[2])); + if(!tb) + return 0; + else + return tb->getId(); +} + +ConsoleMethod(TerrainEditor, getTerrainBlocksMaterialList, const char *, 2, 2, "() gets the list of current terrain materials for all terrain blocks.") +{ + Vector list; + object->getTerrainBlocksMaterialList(list); + + if(list.size() == 0) + return ""; + + // Calculate the size of the return buffer + S32 size = 0; + for(U32 i = 0; i < list.size(); ++i) + { + size += dStrlen(list[i]); + ++size; + } + ++size; + + // Copy the material names + char *ret = Con::getReturnBuffer(size); + ret[0] = 0; + for(U32 i = 0; i < list.size(); ++i) + { + dStrcat( ret, list[i] ); + dStrcat( ret, "\n" ); + } + + return ret; +} + +ConsoleMethod( TerrainEditor, setBrushType, void, 3, 3, "(string type)" + "One of box, ellipse, selection.") +{ + object->setBrushType(argv[2]); +} + +ConsoleMethod( TerrainEditor, getBrushType, const char*, 2, 2, "()") +{ + return object->getBrushType(); +} + +ConsoleMethod( TerrainEditor, setBrushSize, void, 3, 4, "(int w [, int h])") +{ + S32 w = dAtoi(argv[2]); + S32 h = argc > 3 ? dAtoi(argv[3]) : w; + object->setBrushSize( w, h ); +} + +ConsoleMethod( TerrainEditor, getBrushSize, const char*, 2, 2, "()") +{ + Point2I size = object->getBrushSize(); + + char * ret = Con::getReturnBuffer(32); + dSprintf(ret, 32, "%d %d", size.x, size.y); + return ret; +} + +ConsoleMethod( TerrainEditor, setBrushPressure, void, 3, 3, "(float pressure)") +{ + object->setBrushPressure( dAtof( argv[2] ) ); +} + +ConsoleMethod( TerrainEditor, getBrushPressure, F32, 2, 2, "()") +{ + return object->getBrushPressure(); +} + +ConsoleMethod( TerrainEditor, setBrushSoftness, void, 3, 3, "(float softness)") +{ + object->setBrushSoftness( dAtof( argv[2] ) ); +} + +ConsoleMethod( TerrainEditor, getBrushSoftness, F32, 2, 2, "()") +{ + return object->getBrushSoftness(); +} + +ConsoleMethod( TerrainEditor, getBrushPos, const char*, 2, 2, "Returns a Point2I.") +{ + return object->getBrushPos(); +} + +ConsoleMethod( TerrainEditor, setBrushPos, void, 3, 4, "(int x, int y)") +{ + // + Point2I pos; + if(argc == 3) + dSscanf(argv[2], "%d %d", &pos.x, &pos.y); + else + { + pos.x = dAtoi(argv[2]); + pos.y = dAtoi(argv[3]); + } + + object->setBrushPos(pos); +} + +ConsoleMethod( TerrainEditor, setAction, void, 3, 3, "(string action_name)") +{ + object->setAction(argv[2]); +} + +ConsoleMethod( TerrainEditor, getActionName, const char*, 3, 3, "(int num)") +{ + return (object->getActionName(dAtoi(argv[2]))); +} + +ConsoleMethod( TerrainEditor, getNumActions, S32, 2, 2, "") +{ + return(object->getNumActions()); +} + +ConsoleMethod( TerrainEditor, getCurrentAction, const char*, 2, 2, "") +{ + return object->getCurrentAction(); +} + +ConsoleMethod( TerrainEditor, resetSelWeights, void, 3, 3, "(bool clear)") +{ + object->resetSelWeights(dAtob(argv[2])); +} + +ConsoleMethod( TerrainEditor, clearSelection, void, 2, 2, "") +{ + object->clearSelection(); +} + +ConsoleMethod( TerrainEditor, processAction, void, 2, 3, "(string action=NULL)") +{ + if(argc == 3) + object->processAction(argv[2]); + else object->processAction(""); +} + +ConsoleMethod( TerrainEditor, getActiveTerrain, S32, 2, 2, "") +{ + S32 ret = 0; + + TerrainBlock* terrain = object->getActiveTerrain(); + + if (terrain) + ret = terrain->getId(); + + return ret; +} + +ConsoleMethod( TerrainEditor, getNumTextures, S32, 2, 2, "") +{ + return object->getNumTextures(); +} + +ConsoleMethod( TerrainEditor, markEmptySquares, void, 2, 2, "") +{ + object->markEmptySquares(); +} + +ConsoleMethod( TerrainEditor, mirrorTerrain, void, 3, 3, "") +{ + object->mirrorTerrain(dAtoi(argv[2])); +} + +ConsoleMethod(TerrainEditor, setTerraformOverlay, void, 3, 3, "(bool overlayEnable) - sets the terraformer current heightmap to draw as an overlay over the current terrain.") +{ + // XA: This one needs to be implemented :) +} + +ConsoleMethod(TerrainEditor, updateMaterial, bool, 4, 4, + "( int index, string matName )\n" + "Changes the material name at the index." ) +{ + TerrainBlock *terr = object->getClientTerrain(); + if ( !terr ) + return false; + + U32 index = dAtoi( argv[2] ); + if ( index >= terr->getMaterialCount() ) + return false; + + terr->updateMaterial( index, argv[3] ); + + object->setDirty(); + + return true; +} + +ConsoleMethod(TerrainEditor, addMaterial, S32, 3, 3, + "( string matName )\n" + "Adds a new material." ) +{ + TerrainBlock *terr = object->getClientTerrain(); + if ( !terr ) + return false; + + terr->addMaterial( argv[2] ); + + object->setDirty(); + + return true; +} + +ConsoleMethod( TerrainEditor, removeMaterial, void, 3, 3, "( int index ) - Remove the material at the given index." ) +{ + TerrainBlock *terr = object->getClientTerrain(); + if ( !terr ) + return; + + S32 index = dAtoi( argv[ 2 ] ); + if ( index < 0 || index >= terr->getMaterialCount() ) + { + Con::errorf( "TerrainEditor::removeMaterial - index out of range!" ); + return; + } + + if ( terr->getMaterialCount() == 1 ) + { + Con::errorf( "TerrainEditor::removeMaterial - cannot remove material, there is only one!" ); + return; + } + + const char *matName = terr->getMaterialName( index ); + + object->submitMaterialUndo( String::ToString( "Remove TerrainMaterial %s", matName ) ); + + terr->removeMaterial( index ); + + object->setDirty(); + object->scheduleMaterialUpdate(); + object->setGridUpdateMinMax(); +} + +ConsoleMethod(TerrainEditor, getMaterialCount, S32, 2, 2, + "Returns the current material count." ) +{ + TerrainBlock *terr = object->getClientTerrain(); + if ( terr ) + return terr->getMaterialCount(); + + return 0; +} + +ConsoleMethod(TerrainEditor, getMaterials, const char *, 2, 2, "() gets the list of current terrain materials.") +{ + TerrainBlock *terr = object->getClientTerrain(); + if ( !terr ) + return ""; + + char *ret = Con::getReturnBuffer(4096); + ret[0] = 0; + for(U32 i = 0; i < terr->getMaterialCount(); i++) + { + dStrcat( ret, terr->getMaterialName(i) ); + dStrcat( ret, "\n" ); + } + + return ret; +} + +ConsoleMethod( TerrainEditor, getMaterialName, const char*, 3, 3, "( int index ) - Returns the name of the material at the given index." ) +{ + TerrainBlock *terr = object->getClientTerrain(); + if ( !terr ) + return ""; + + S32 index = dAtoi( argv[ 2 ] ); + if( index < 0 || index >= terr->getMaterialCount() ) + { + Con::errorf( "TerrainEditor::getMaterialName - index out of range!" ); + return ""; + } + + const char* name = terr->getMaterialName( index ); + return Con::getReturnBuffer( name ); +} + +ConsoleMethod( TerrainEditor, getMaterialIndex, S32, 3, 3, "( string name ) - Returns the index of the material with the given name or -1." ) +{ + TerrainBlock *terr = object->getClientTerrain(); + if ( !terr ) + return -1; + + const char* name = argv[ 2 ]; + const U32 count = terr->getMaterialCount(); + + for( U32 i = 0; i < count; ++ i ) + if( dStricmp( name, terr->getMaterialName( i ) ) == 0 ) + return i; + + return -1; +} + +ConsoleMethod( TerrainEditor, reorderMaterial, void, 4, 4, "( int index, int order ) " + "- Reorder material at the given index to the new position, changing the order in which it is rendered / blended." ) +{ + object->reorderMaterial( dAtoi( argv[2] ), dAtoi( argv[3] ) ); +} + +ConsoleMethod(TerrainEditor, getTerrainUnderWorldPoint, S32, 3, 5, "(x/y/z) Gets the terrain block that is located under the given world point.\n" + "@param x/y/z The world coordinates (floating point values) you wish to query at. " + "These can be formatted as either a string (\"x y z\") or separately as (x, y, z)\n" + "@return Returns the ID of the requested terrain block (0 if not found).\n\n") +{ + TerrainEditor *tEditor = (TerrainEditor *) object; + if(tEditor == NULL) + return 0; + Point3F pos; + if(argc == 3) + dSscanf(argv[2], "%f %f %f", &pos.x, &pos.y, &pos.z); + else if(argc == 5) + { + pos.x = dAtof(argv[2]); + pos.y = dAtof(argv[3]); + pos.z = dAtof(argv[4]); + } + + else + { + Con::errorf("TerrainEditor.getTerrainUnderWorldPoint(): Invalid argument count! Valid arguments are either \"x y z\" or x,y,z\n"); + return 0; + } + + TerrainBlock* terrain = tEditor->getTerrainUnderWorldPoint(pos); + if(terrain != NULL) + { + return terrain->getId(); + } + + return 0; +} + +//------------------------------------------------------------------------------ + +void TerrainEditor::initPersistFields() +{ + addGroup("Misc"); + addField("isDirty", TypeBool, Offset(mIsDirty, TerrainEditor)); + addField("isMissionDirty", TypeBool, Offset(mIsMissionDirty, TerrainEditor)); + addField("renderBorder", TypeBool, Offset(mRenderBorder, TerrainEditor)); ///< Not currently used + addField("borderHeight", TypeF32, Offset(mBorderHeight, TerrainEditor)); ///< Not currently used + addField("borderFillColor", TypeColorI, Offset(mBorderFillColor, TerrainEditor)); ///< Not currently used + addField("borderFrameColor", TypeColorI, Offset(mBorderFrameColor, TerrainEditor)); ///< Not currently used + addField("borderLineMode", TypeBool, Offset(mBorderLineMode, TerrainEditor)); ///< Not currently used + addField("selectionHidden", TypeBool, Offset(mSelectionHidden, TerrainEditor)); + addField("renderVertexSelection", TypeBool, Offset(mRenderVertexSelection, TerrainEditor)); ///< Not currently used + addField("renderSolidBrush", TypeBool, Offset(mRenderSolidBrush, TerrainEditor)); + addField("processUsesBrush", TypeBool, Offset(mProcessUsesBrush, TerrainEditor)); + addField("maxBrushSize", TypePoint2I, Offset(mMaxBrushSize, TerrainEditor)); + + // action values... + addField("adjustHeightVal", TypeF32, Offset(mAdjustHeightVal, TerrainEditor)); ///< RaiseHeightAction and LowerHeightAction + addField("setHeightVal", TypeF32, Offset(mSetHeightVal, TerrainEditor)); ///< SetHeightAction + addField("scaleVal", TypeF32, Offset(mScaleVal, TerrainEditor)); ///< ScaleHeightAction + addField("smoothFactor", TypeF32, Offset(mSmoothFactor, TerrainEditor)); ///< SmoothHeightAction + addField("noiseFactor", TypeF32, Offset(mNoiseFactor, TerrainEditor)); ///< PaintNoiseAction + addField("materialGroup", TypeS32, Offset(mMaterialGroup, TerrainEditor)); ///< Not currently used + addField("softSelectRadius", TypeF32, Offset(mSoftSelectRadius, TerrainEditor)); ///< SoftSelectAction + addField("softSelectFilter", TypeString, Offset(mSoftSelectFilter, TerrainEditor)); ///< SoftSelectAction brush filtering + addField("softSelectDefaultFilter", TypeString, Offset(mSoftSelectDefaultFilter, TerrainEditor)); ///< SoftSelectAction brush filtering + addField("adjustHeightMouseScale", TypeF32, Offset(mAdjustHeightMouseScale, TerrainEditor)); ///< Not currently used + addField("paintIndex", TypeS32, Offset(mPaintIndex, TerrainEditor)); ///< PaintMaterialAction + endGroup("Misc"); + + Parent::initPersistFields(); +} + +ConsoleMethod( TerrainEditor, getSlopeLimitMinAngle, F32, 2, 2, 0) +{ + return object->mSlopeMinAngle; +} + +ConsoleMethod( TerrainEditor, setSlopeLimitMinAngle, F32, 3, 3, 0) +{ + F32 angle = dAtof( argv[2] ); + if ( angle < 0.0f ) + angle = 0.0f; + if ( angle > object->mSlopeMaxAngle ) + angle = object->mSlopeMaxAngle; + + object->mSlopeMinAngle = angle; + return angle; +} + +ConsoleMethod( TerrainEditor, getSlopeLimitMaxAngle, F32, 2, 2, 0) +{ + return object->mSlopeMaxAngle; +} + +ConsoleMethod( TerrainEditor, setSlopeLimitMaxAngle, F32, 3, 3, 0) +{ + F32 angle = dAtof( argv[2] ); + if ( angle > 90.0f ) + angle = 90.0f; + if ( angle < object->mSlopeMinAngle ) + angle = object->mSlopeMinAngle; + + object->mSlopeMaxAngle = angle; + return angle; +} diff --git a/Engine/source/gui/worldEditor/terrainEditor.h b/Engine/source/gui/worldEditor/terrainEditor.h new file mode 100644 index 000000000..03bfe3058 --- /dev/null +++ b/Engine/source/gui/worldEditor/terrainEditor.h @@ -0,0 +1,497 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TERRAINEDITOR_H_ +#define _TERRAINEDITOR_H_ + +#ifndef _EDITTSCTRL_H_ +#include "gui/worldEditor/editTSCtrl.h" +#endif +#ifndef _TERRDATA_H_ +#include "terrain/terrData.h" +#endif +#ifndef _UNDO_H_ +#include "util/undo.h" +#endif + + + +// Each 2D grid position must be associated with a terrainBlock +struct GridPoint +{ + Point2I gridPos; + TerrainBlock* terrainBlock; + + GridPoint() { gridPos.set(0, 0); terrainBlock = NULL; }; +}; + +class GridInfo +{ +public: + + GridPoint mGridPoint; + U8 mMaterial; + F32 mHeight; + F32 mWeight; + F32 mStartHeight; + + bool mPrimarySelect; + bool mMaterialChanged; + + // hash table + S32 mNext; + S32 mPrev; +}; + + +class Selection : public Vector +{ +private: + + StringTableEntry mName; + BitSet32 mUndoFlags; + + // hash table + S32 lookup(const Point2I & pos); + void insert(GridInfo info); + U32 getHashIndex(const Point2I & pos); + bool validate(); + + Vector mHashLists; + U32 mHashListSize; + +public: + + Selection(); + virtual ~Selection(); + + void reset(); + + /// add unique grid info into the selection - test uniqueness by grid position + bool add(const GridInfo &info); + bool getInfo(Point2I pos, GridInfo & info); + bool setInfo(GridInfo & info); + bool remove(const GridInfo &info); + void setName(StringTableEntry name); + StringTableEntry getName(){return(mName);} + F32 getAvgHeight(); + F32 getMinHeight(); + F32 getMaxHeight(); +}; + + +class TerrainEditor; + +class Brush : public Selection +{ +protected: + + TerrainEditor * mTerrainEditor; + Point2I mSize; + GridPoint mGridPoint; + Vector mRenderList; + +public: + + enum { MaxBrushDim = 40 }; + + Brush(TerrainEditor * editor); + virtual ~Brush(){}; + + virtual const char *getType() const = 0; + + // Brush appears to intentionally bypass Selection's hash table, so we + // override validate() here. + bool validate() { return true; } + void setPosition(const Point3F & pos); + void setPosition(const Point2I & pos); + const Point2I & getPosition(); + const GridPoint & getGridPoint(); + void setTerrain(TerrainBlock* terrain) { mGridPoint.terrainBlock = terrain; }; + Point2I getSize() const {return(mSize);} + virtual void setSize(const Point2I & size){mSize = size;} + + void update(); + void render(); + + virtual void rebuild() = 0; + virtual void _renderOutline() = 0; +}; + +class BoxBrush : public Brush +{ +public: + + BoxBrush(TerrainEditor * editor) : Brush(editor){} + + const char *getType() const { return "box"; } + void rebuild(); + +protected: + + void _renderOutline(); +}; + +class EllipseBrush : public Brush +{ +public: + + EllipseBrush(TerrainEditor * editor) : Brush(editor){} + + const char *getType() const { return "ellipse"; } + void rebuild(); + +protected: + + void _renderOutline(); +}; + +class SelectionBrush : public Brush +{ +public: + + SelectionBrush(TerrainEditor * editor); + + const char *getType() const { return "selection"; } + void rebuild(); + void render(Vector & vertexBuffer, S32 & verts, S32 & elems, S32 & prims, const ColorF & inColorFull, const ColorF & inColorNone, const ColorF & outColorFull, const ColorF & outColorNone) const; + void setSize(const Point2I &){} + +protected: + + void _renderOutline() {} +}; + + +class TerrainAction; + +class TerrainEditor : public EditTSCtrl +{ + // XA: This methods where added to replace the friend consoleMethods. + public: + void attachTerrain(TerrainBlock *terrBlock); + void detachTerrain(TerrainBlock *terrBlock); + + S32 getTerrainBlockCount() {return mTerrainBlocks.size();} + TerrainBlock* getTerrainBlock(S32 index); + void getTerrainBlocksMaterialList(Vector& list); // Returns consolidated list of all materials used on all terrain blocks + + void setBrushType(const char* type); + const char* getBrushType() const; + + void setBrushSize(S32 w, S32 h); + const char* getBrushPos(); + void setBrushPos(Point2I pos); + + void setAction(const char* action); + const char* getActionName(U32 index); + const char* getCurrentAction() const; + S32 getNumActions(); + void processAction(const char* sAction); + + void resetSelWeights(bool clear); + void clearSelection(); + + S32 getNumTextures(); + + void markEmptySquares(); + + void mirrorTerrain(S32 mirrorIndex); + + TerrainBlock* getActiveTerrain() { return mActiveTerrain; }; + + void scheduleGridUpdate() { mNeedsGridUpdate = true; } + void scheduleMaterialUpdate() { mNeedsMaterialUpdate = true; } + void setGridUpdateMinMax() + { + mGridUpdateMax.set( S32_MAX, S32_MAX ); + mGridUpdateMin.set( 0, 0 ); + } + + void submitMaterialUndo( String actionName ); + void onMaterialUndo( TerrainBlock *terr ); + + private: + + typedef EditTSCtrl Parent; + + TerrainBlock* mActiveTerrain; + + // A list of all of the TerrainBlocks this editor can edit + VectorPtr mTerrainBlocks; + + Point2I mGridUpdateMin; + Point2I mGridUpdateMax; + U32 mMouseDownSeq; + + /// If one of these flags when the terrainEditor goes to render + /// an appropriate update method will be called on the terrain. + /// This prevents unnecessary work from happening from directly + /// within an editor event's process method. + bool mNeedsGridUpdate; + bool mNeedsMaterialUpdate; + + bool mMouseDown; + PlaneF mMousePlane; + Point3F mMousePos; + Brush * mMouseBrush; + bool mBrushChanged; + bool mRenderBrush; + F32 mBrushPressure; + Point2I mBrushSize; + F32 mBrushSoftness; + Vector mActions; + TerrainAction * mCurrentAction; + bool mInAction; + Selection mDefaultSel; + bool mSelectionLocked; + + S32 mPaintIndex; + + Selection * mCurrentSel; + + class TerrainEditorUndoAction : public UndoAction + { + public: + + TerrainEditorUndoAction( const UTF8* actionName ) + : UndoAction( actionName ), + mTerrainEditor( NULL ), + mSel( NULL ) + { + } + + virtual ~TerrainEditorUndoAction() + { + delete mSel; + } + + TerrainEditor *mTerrainEditor; + + Selection *mSel; + + virtual void undo(); + virtual void redo() { undo(); } + }; + + void submitUndo( Selection *sel ); + + Selection *mUndoSel; + + class TerrainMaterialUndoAction : public UndoAction + { + public: + + TerrainMaterialUndoAction( const UTF8 *actionName ) + : UndoAction( actionName ), + mEditor( NULL ), + mTerrain( NULL ) + { + } + + TerrainEditor *mEditor; + TerrainBlock *mTerrain; + Vector mLayerMap; + Vector mMaterials; + + virtual void undo(); + virtual void redo(); + }; + + bool mIsDirty; // dirty flag for writing terrain. + bool mIsMissionDirty; // dirty flag for writing mission. + + GFXStateBlockRef mStateBlock; + + public: + + TerrainEditor(); + ~TerrainEditor(); + + // conversion functions + // Returns true if the grid position is on the main tile + bool isMainTile(const GridPoint & gPoint) const; + + // Takes a world point and find the "highest" terrain underneath it + // Returns true if the returned GridPoint includes a valid terrain and grid position + TerrainBlock* getTerrainUnderWorldPoint(const Point3F & wPos); + + // Converts a GridPoint to a world position + bool gridToWorld(const GridPoint & gPoint, Point3F & wPos); + bool gridToWorld(const Point2I & gPos, Point3F & wPos, TerrainBlock* terrain); + + // Converts a world position to a grid point + // If the grid point does not have a TerrainBlock already it will find the nearest + // terrian under the world position + bool worldToGrid(const Point3F & wPos, GridPoint & gPoint); + bool worldToGrid(const Point3F & wPos, Point2I & gPos, TerrainBlock* terrain = NULL); + + // Converts any point that is off of the main tile to its equivalent on the main tile + // Returns true if the point was already on the main tile + bool gridToCenter(const Point2I & gPos, Point2I & cPos) const; + + //bool getGridInfo(const Point3F & wPos, GridInfo & info); + // Gets the grid info for a point on a TerrainBlock's grid + bool getGridInfo(const GridPoint & gPoint, GridInfo & info); + bool getGridInfo(const Point2I & gPos, GridInfo & info, TerrainBlock* terrain); + + // Returns a list of infos for all points on the terrain that are at that point in space + void getGridInfos(const GridPoint & gPoint, Vector& infos); + + void setGridInfo(const GridInfo & info, bool checkActive = false); + void setGridInfoHeight(const GridInfo & info); + void gridUpdateComplete( bool materialChanged = false ); + void materialUpdateComplete(); + void processActionTick(U32 sequence); + + TerrainBlock* collide(const Gui3DMouseEvent & event, Point3F & pos); + void lockSelection(bool lock) { mSelectionLocked = lock; }; + + Selection * getUndoSel(){return(mUndoSel);} + Selection * getCurrentSel(){return(mCurrentSel);} + void setCurrentSel(Selection * sel) { mCurrentSel = sel; } + void resetCurrentSel() {mCurrentSel = &mDefaultSel; } + + S32 getPaintMaterialIndex() const { return mPaintIndex; } + + void setBrushPressure( F32 pressure ); + F32 getBrushPressure() const { return mBrushPressure; } + + void setBrushSoftness( F32 softness ); + F32 getBrushSoftness() const { return mBrushSoftness; } + + Point2I getBrushSize() { return(mBrushSize); } + + TerrainBlock* getTerrainBlock() const { return mActiveTerrain; } + TerrainBlock* getClientTerrain( TerrainBlock *serverTerrain = NULL ) const; + bool terrainBlockValid() { return(mActiveTerrain ? true : false); } + void setDirty() { mIsDirty = true; } + void setMissionDirty() { mIsMissionDirty = true; } + + TerrainAction * lookupAction(const char * name); + + private: + + + // terrain interface functions + // Returns the height at a grid point + F32 getGridHeight(const GridPoint & gPoint); + // Sets a height at a grid point + void setGridHeight(const GridPoint & gPoint, const F32 height); + + /// + U8 getGridMaterial( const GridPoint &gPoint ) const; + + /// + void setGridMaterial( const GridPoint & gPoint, U8 index ); + + // Gets the material group of a specific spot on a TerrainBlock's grid + U8 getGridMaterialGroup(const GridPoint & gPoint); + + // Sets a material group for a spot on a TerrainBlock's grid + void setGridMaterialGroup(const GridPoint & gPoint, U8 group); + + // + void updateBrush(Brush & brush, const Point2I & gPos); + + // + void renderSelection(const Selection & sel, const ColorF & inColorFull, const ColorF & inColorNone, const ColorF & outColorFull, const ColorF & outColorNone, bool renderFill, bool renderFrame); + void renderBrush(const Brush & brush, const ColorF & inColorFull, const ColorF & inColorNone, const ColorF & outColorFull, const ColorF & outColorNone, bool renderFill, bool renderFrame); + void renderBorder(); + + public: + + // persist field data - these are dynamic + bool mRenderBorder; + F32 mBorderHeight; + ColorI mBorderFillColor; + ColorI mBorderFrameColor; + bool mBorderLineMode; + bool mSelectionHidden; + bool mRenderVertexSelection; + bool mRenderSolidBrush; + bool mProcessUsesBrush; + + // + F32 mAdjustHeightVal; + F32 mSetHeightVal; + F32 mScaleVal; + F32 mSmoothFactor; + F32 mNoiseFactor; + S32 mMaterialGroup; + F32 mSoftSelectRadius; + StringTableEntry mSoftSelectFilter; + StringTableEntry mSoftSelectDefaultFilter; + F32 mAdjustHeightMouseScale; + Point2I mMaxBrushSize; + + F32 mSlopeMinAngle; + F32 mSlopeMaxAngle; + + public: + + // SimObject + bool onAdd(); + void onDeleteNotify(SimObject * object); + + static void initPersistFields(); + + // GuiControl + bool onWake(); + void onSleep(); + + // EditTSCtrl + bool onInputEvent( const InputEventInfo & evt ); + void on3DMouseUp( const Gui3DMouseEvent & evt ); + void on3DMouseDown( const Gui3DMouseEvent & evt ); + void on3DMouseMove( const Gui3DMouseEvent & evt ); + void on3DMouseDragged( const Gui3DMouseEvent & evt ); + bool onMouseWheelUp( const GuiEvent & evt ); + bool onMouseWheelDown( const GuiEvent & evt ); + void get3DCursor( GuiCursor *&cursor, bool &visible, const Gui3DMouseEvent &evt ); + void onPreRender(); + void renderScene(const RectI & updateRect); + void renderGui( Point2I offset, const RectI &updateRect ); + void updateGuiInfo(); + + // Determine if the given grid point is valid within a non-wrap + // around terrain. + bool isPointInTerrain( const GridPoint & gPoint); + + /// Reorder material at the given index to the new position, changing the order in which it is rendered / blended + void reorderMaterial( S32 index, S32 orderPos ); + + // + Point3F getMousePos(){return(mMousePos);}; + + void renderPoints( const Vector &pointList ); + + + DECLARE_CONOBJECT(TerrainEditor); +}; + +inline void TerrainEditor::setGridInfoHeight(const GridInfo & info) +{ + setGridHeight(info.mGridPoint, info.mHeight); +} + +#endif diff --git a/Engine/source/gui/worldEditor/undoActions.cpp b/Engine/source/gui/worldEditor/undoActions.cpp new file mode 100644 index 000000000..aebd1fd49 --- /dev/null +++ b/Engine/source/gui/worldEditor/undoActions.cpp @@ -0,0 +1,272 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/worldEditor/undoActions.h" + +#include "gui/editor/inspector/field.h" +#include "gui/editor/guiInspector.h" +#include "console/consoleTypes.h" + + +IMPLEMENT_CONOBJECT( MECreateUndoAction ); + +ConsoleDocClass( MECreateUndoAction, + "@brief Material Editor create undo instance\n\n" + "Not intended for game development, for editors or internal use only.\n\n " + "@internal"); + +MECreateUndoAction::MECreateUndoAction( const UTF8* actionName ) + : UndoAction( actionName ) +{ +} + +MECreateUndoAction::~MECreateUndoAction() +{ +} + +void MECreateUndoAction::initPersistFields() +{ + Parent::initPersistFields(); +} + +void MECreateUndoAction::addObject( SimObject *object ) +{ + AssertFatal( object, "MECreateUndoAction::addObject() - Got null object!" ); + + mObjects.increment(); + mObjects.last().id = object->getId(); +} + +ConsoleMethod( MECreateUndoAction, addObject, void, 3, 3, "( SimObject obj )") +{ + SimObject *obj = NULL; + if ( Sim::findObject( argv[2], obj ) && obj ) + object->addObject( obj ); +} + +void MECreateUndoAction::undo() +{ + for ( S32 i= mObjects.size()-1; i >= 0; i-- ) + { + ObjectState &state = mObjects[i]; + + SimObject *object = Sim::findObject( state.id ); + if ( !object ) + continue; + + // Save the state. + if ( !state.memento.hasState() ) + state.memento.save( object ); + + // Store the group. + SimGroup *group = object->getGroup(); + if ( group ) + state.groupId = group->getId(); + + // We got what we need... delete it. + object->deleteObject(); + } + + Con::executef( this, "onUndone" ); +} + +void MECreateUndoAction::redo() +{ + for ( S32 i=0; i < mObjects.size(); i++ ) + { + const ObjectState &state = mObjects[i]; + + // Create the object. + SimObject::setForcedId(state.id); // Restore the object's Id + SimObject *object = state.memento.restore(); + if ( !object ) + continue; + + // Now restore its group. + SimGroup *group; + if ( Sim::findObject( state.groupId, group ) ) + group->addObject( object ); + } + + Con::executef( this, "onRedone" ); +} + + +IMPLEMENT_CONOBJECT( MEDeleteUndoAction ); + +ConsoleDocClass( MEDeleteUndoAction, + "@brief Material Editor delete undo instance\n\n" + "Not intended for game development, for editors or internal use only.\n\n " + "@internal"); + +MEDeleteUndoAction::MEDeleteUndoAction( const UTF8 *actionName ) + : UndoAction( actionName ) +{ +} + +MEDeleteUndoAction::~MEDeleteUndoAction() +{ +} + +void MEDeleteUndoAction::initPersistFields() +{ + Parent::initPersistFields(); +} + +void MEDeleteUndoAction::deleteObject( SimObject *object ) +{ + AssertFatal( object, "MEDeleteUndoAction::deleteObject() - Got null object!" ); + AssertFatal( object->isProperlyAdded(), + "MEDeleteUndoAction::deleteObject() - Object should be registered!" ); + + mObjects.increment(); + ObjectState& state = mObjects.last(); + + // Capture the object id. + state.id = object->getId(); + + // Save the state. + state.memento.save( object ); + + // Store the group. + SimGroup *group = object->getGroup(); + if ( group ) + state.groupId = group->getId(); + + // Now delete the object. + object->deleteObject(); +} + +void MEDeleteUndoAction::deleteObject( const Vector &objectList ) +{ + for ( S32 i = 0; i < objectList.size(); i++ ) + deleteObject( objectList[i] ); +} + +ConsoleMethod( MEDeleteUndoAction, deleteObject, void, 3, 3, "( SimObject obj )") +{ + SimObject *obj = NULL; + if ( Sim::findObject( argv[2], obj ) && obj ) + object->deleteObject( obj ); +} + +void MEDeleteUndoAction::undo() +{ + for ( S32 i= mObjects.size()-1; i >= 0; i-- ) + { + const ObjectState &state = mObjects[i]; + + // Create the object. + SimObject::setForcedId(state.id); // Restore the object's Id + SimObject *object = state.memento.restore(); + if ( !object ) + continue; + + // Now restore its group. + SimGroup *group; + if ( Sim::findObject( state.groupId, group ) ) + group->addObject( object ); + } + + Con::executef( this, "onUndone" ); +} + +void MEDeleteUndoAction::redo() +{ + for ( S32 i=0; i < mObjects.size(); i++ ) + { + const ObjectState& state = mObjects[i]; + SimObject *object = Sim::findObject( state.id ); + if ( object ) + object->deleteObject(); + } + + Con::executef( this, "onRedone" ); +} + +IMPLEMENT_CONOBJECT( InspectorFieldUndoAction ); + +ConsoleDocClass( InspectorFieldUndoAction, + "@brief Inspector Field undo action instance\n\n" + "Not intended for game development, for editors or internal use only.\n\n " + "@internal"); + +InspectorFieldUndoAction::InspectorFieldUndoAction() +{ + mObjId = 0; + mField = NULL; + mSlotName = StringTable->insert(""); + mArrayIdx = StringTable->insert(""); +} + +InspectorFieldUndoAction::InspectorFieldUndoAction( const UTF8 *actionName ) +: UndoAction( actionName ) +{ + mInspector = NULL; + mObjId = 0; + mField = NULL; + mSlotName = StringTable->insert(""); + mArrayIdx = StringTable->insert(""); +} + +void InspectorFieldUndoAction::initPersistFields() +{ + addField( "inspectorGui", TYPEID< GuiInspector >(), Offset( mInspector, InspectorFieldUndoAction ) ); + addField( "objectId", TypeS32, Offset( mObjId, InspectorFieldUndoAction ) ); + addField( "fieldName", TypeString, Offset( mSlotName, InspectorFieldUndoAction ) ); + addField( "fieldValue", TypeRealString, Offset( mData, InspectorFieldUndoAction ) ); + addField( "arrayIndex", TypeString, Offset( mArrayIdx, InspectorFieldUndoAction ) ); + + Parent::initPersistFields(); +} + +void InspectorFieldUndoAction::undo() +{ + SimObject *obj = NULL; + if ( !Sim::findObject( mObjId, obj ) ) + return; + + if ( mArrayIdx && dStricmp( mArrayIdx, "(null)" ) == 0 ) + mArrayIdx = NULL; + + // Grab the current data. + String data = obj->getDataField( mSlotName, mArrayIdx ); + + // Call this to mirror the way field changes are done through the inspector. + obj->inspectPreApply(); + + // Restore the data from the UndoAction + obj->setDataField( mSlotName, mArrayIdx, mData.c_str() ); + + // Call this to mirror the way field changes are done through the inspector. + obj->inspectPostApply(); + + // If the affected object is still being inspected, + // update the InspectorField to reflect the changed value. + if ( mInspector && mInspector->getNumInspectObjects() > 0 && mInspector->getInspectObject() == obj ) + mInspector->updateFieldValue( mSlotName, mArrayIdx ); + + // Now save the previous data in this UndoAction + // since an undo action must become a redo action and vice-versa + mData = data; +} \ No newline at end of file diff --git a/Engine/source/gui/worldEditor/undoActions.h b/Engine/source/gui/worldEditor/undoActions.h new file mode 100644 index 000000000..66e8df5ef --- /dev/null +++ b/Engine/source/gui/worldEditor/undoActions.h @@ -0,0 +1,135 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GUI_WORLDEDITOR_UNDOACTIONS_H_ +#define _GUI_WORLDEDITOR_UNDOACTIONS_H_ + +#ifndef _UNDO_H_ +#include "util/undo.h" +#endif +#ifndef _CONSOLE_SIMOBJECTMEMENTO_H_ +#include "console/simObjectMemento.h" +#endif + +class GuiInspectorField; +class GuiInspector; + +class MECreateUndoAction : public UndoAction +{ + typedef UndoAction Parent; + +protected: + + struct ObjectState + { + /// The object we created and will delete in undo. + SimObjectId id; + + /// The captured object state. + SimObjectMemento memento; + + /// Keep track of the parent group. + SimObjectId groupId; + }; + + /// All the objects that were created. + Vector mObjects; + +public: + + DECLARE_CONOBJECT( MECreateUndoAction ); + static void initPersistFields(); + + MECreateUndoAction( const UTF8* actionName = " " ); + virtual ~MECreateUndoAction(); + + void addObject( SimObject *object ); + + // UndoAction + virtual void undo(); + virtual void redo(); +}; + + +class MEDeleteUndoAction : public UndoAction +{ + typedef UndoAction Parent; + +protected: + + struct ObjectState + { + /// The object we deleted and will restore in undo. + SimObjectId id; + + /// The captured object state. + SimObjectMemento memento; + + /// Keep track of the parent group. + SimObjectId groupId; + }; + + /// All the objects we're deleting. + Vector mObjects; + +public: + + DECLARE_CONOBJECT( MEDeleteUndoAction ); + static void initPersistFields(); + + MEDeleteUndoAction( const UTF8* actionName = "Delete Object" ); + virtual ~MEDeleteUndoAction(); + + /// + void deleteObject( SimObject *object ); + void deleteObject( const Vector &objectList ); + + // UndoAction + virtual void undo(); + virtual void redo(); +}; + +class InspectorFieldUndoAction : public UndoAction +{ + typedef UndoAction Parent; + +public: + + InspectorFieldUndoAction(); + InspectorFieldUndoAction( const UTF8* actionName ); + + DECLARE_CONOBJECT( InspectorFieldUndoAction ); + static void initPersistFields(); + + GuiInspector *mInspector; + SimObjectId mObjId; + SimObjectPtr mField; + StringTableEntry mSlotName; + StringTableEntry mArrayIdx; + String mData; + + // UndoAction + virtual void undo(); + virtual void redo() { undo(); } +}; + +#endif // _GUI_WORLDEDITOR_UNDOACTIONS_H_ diff --git a/Engine/source/gui/worldEditor/worldEditor.cpp b/Engine/source/gui/worldEditor/worldEditor.cpp new file mode 100644 index 000000000..9f4566944 --- /dev/null +++ b/Engine/source/gui/worldEditor/worldEditor.cpp @@ -0,0 +1,3941 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "gui/worldEditor/worldEditor.h" + +#include "gui/worldEditor/gizmo.h" +#include "gui/worldEditor/undoActions.h" +#include "gui/worldEditor/editorIconRegistry.h" +#include "gui/worldEditor/worldEditorSelection.h" +#include "core/stream/memStream.h" +#include "scene/simPath.h" +#include "scene/mixin/scenePolyhedralObject.h" +#include "gui/core/guiCanvas.h" +#include "T3D/gameBase/gameConnection.h" +#include "T3D/groundPlane.h" +#include "collision/earlyOutPolyList.h" +#include "collision/concretePolyList.h" +#include "console/consoleInternal.h" +#include "console/engineAPI.h" +#include "T3D/shapeBase.h" +#include "T3D/cameraSpline.h" +#include "T3D/convexShape.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/gfxDrawUtil.h" +#include "gfx/gfxDebugEvent.h" +#include "platform/typetraits.h" +#include "T3D/prefab.h" +#include "math/mEase.h" + + + +IMPLEMENT_CONOBJECT( WorldEditor ); + +ConsoleDocClass( WorldEditor, + "@brief The main World Editor tool class\n\n" + "Editor use only.\n\n" + "@internal" +); + +ImplementEnumType( WorldEditorDropType, + "How to drop objects when placed or dropped in the world.\n" + "@internal\n\n") + { WorldEditor::DropAtOrigin, "atOrigin", "Place at the scene origin (usually 0,0,0)\n" }, + { WorldEditor::DropAtCamera, "atCamera", "Places at the same position as the camera, without rotation.\n" }, + { WorldEditor::DropAtCameraWithRot, "atCameraRot", "Places at the same position as the camera, with the camera's rotation.\n" }, + { WorldEditor::DropBelowCamera, "belowCamera", "Places below the camera.\n" }, + { WorldEditor::DropAtScreenCenter, "screenCenter", "Places at a position projected outwards from the screen's center.\n" }, + { WorldEditor::DropAtCentroid, "atCentroid", "Places at the center position of the current centroid.\n" }, + { WorldEditor::DropToTerrain, "toTerrain", "Places on the terrain.\n" }, + { WorldEditor::DropBelowSelection, "belowSelection", "Places at a position below the selected object.\n" } +EndImplementEnumType; + +ImplementEnumType( WorldEditorAlignmentType, + "How to snap when snapping is enabled.\n" + "@internal\n\n") + { WorldEditor::AlignNone, "None", "No alignement type.\n" }, + { WorldEditor::AlignPosX, "+X", "Snap towards the higher position on the X plane.\n" }, + { WorldEditor::AlignPosY, "+Y", "Snap towards the higher position on the Y plane.\n" }, + { WorldEditor::AlignPosZ, "+Z", "Snap towards the higher position on the Z plane.\n" }, + { WorldEditor::AlignNegX, "-X", "Snap towards the lower position on the X plane.\n" }, + { WorldEditor::AlignNegY, "-Y", "Snap towards the lower position on the Y plane.\n" }, + { WorldEditor::AlignNegZ, "-Z", "Snap towards the lower position on the Z plane.\n" }, +EndImplementEnumType; + + +// unnamed namespace for static data +namespace { + + static VectorF axisVector[3] = { + VectorF(1.0f,0.0f,0.0f), + VectorF(0.0f,1.0f,0.0f), + VectorF(0.0f,0.0f,1.0f) + }; + + static Point3F BoxPnts[] = { + Point3F(0.0f,0.0f,0.0f), + Point3F(0.0f,0.0f,1.0f), + Point3F(0.0f,1.0f,0.0f), + Point3F(0.0f,1.0f,1.0f), + Point3F(1.0f,0.0f,0.0f), + Point3F(1.0f,0.0f,1.0f), + Point3F(1.0f,1.0f,0.0f), + Point3F(1.0f,1.0f,1.0f) + }; + + static U32 BoxVerts[][4] = { + {0,2,3,1}, // -x + {7,6,4,5}, // +x + {0,1,5,4}, // -y + {3,2,6,7}, // +y + {0,4,6,2}, // -z + {3,7,5,1} // +z + }; + + // + U32 getBoxNormalIndex(const VectorF & normal) + { + const F32 * pNormal = ((const F32 *)normal); + + F32 max = 0; + S32 index = -1; + + for(U32 i = 0; i < 3; i++) + if(mFabs(pNormal[i]) >= mFabs(max)) + { + max = pNormal[i]; + index = i*2; + } + + AssertFatal(index >= 0, "Failed to get best normal"); + if(max > 0.f) + index++; + + return(index); + } + + // + Point3F getBoundingBoxCenter(SceneObject * obj) + { + Box3F box = obj->getObjBox(); + MatrixF mat = obj->getTransform(); + VectorF scale = obj->getScale(); + + Point3F center(0,0,0); + Point3F projPnts[8]; + + for(U32 i = 0; i < 8; i++) + { + Point3F pnt; + pnt.set(BoxPnts[i].x ? box.maxExtents.x : box.minExtents.x, + BoxPnts[i].y ? box.maxExtents.y : box.minExtents.y, + BoxPnts[i].z ? box.maxExtents.z : box.minExtents.z); + + // scale it + pnt.convolve(scale); + mat.mulP(pnt, &projPnts[i]); + center += projPnts[i]; + } + + center /= 8; + return(center); + } + + // + const char * parseObjectFormat(SimObject * obj, const char * format) + { + static char buf[1024]; + + U32 curPos = 0; + U32 len = dStrlen(format); + + for(U32 i = 0; i < len; i++) + { + if(format[i] == '$') + { + U32 j; + for(j = i+1; j < len; j++) + if(format[j] == '$') + break; + + if(j == len) + break; + + char token[80]; + + AssertFatal((j - i) < (sizeof(token) - 1), "token too long"); + dStrncpy(token, &format[i+1], (j - i - 1)); + token[j-i-1] = 0; + + U32 remaining = sizeof(buf) - curPos - 1; + + // look at the token + if(!dStricmp(token, "id")) + curPos += dSprintf(buf + curPos, remaining, "%d", obj->getId()); + else if( dStricmp( token, "name|class" ) == 0 ) + { + const char* str; + if( obj->getName() && obj->getName()[ 0 ] ) + str = obj->getName(); + else + str = obj->getClassName(); + + curPos += dSprintf( buf + curPos, remaining, "%s", str ); + } + else if(!dStricmp(token, "name|internal")) + { + if( obj->getName() || !obj->getInternalName() ) + curPos += dSprintf(buf + curPos, remaining, "%s", obj->getName()); + else + curPos += dSprintf(buf + curPos, remaining, "[%s]", obj->getInternalName()); + } + else if(!dStricmp(token, "name")) + curPos += dSprintf(buf + curPos, remaining, "%s", obj->getName()); + else if(!dStricmp(token, "class")) + curPos += dSprintf(buf + curPos, remaining, "%s", obj->getClassName()); + else if(!dStricmp(token, "namespace") && obj->getNamespace()) + curPos += dSprintf(buf + curPos, remaining, "%s", obj->getNamespace()->mName); + + // + i = j; + } + else + buf[curPos++] = format[i]; + } + + buf[curPos] = 0; + return(buf); + } + + // + F32 snapFloat(F32 val, F32 snap) + { + if(snap == 0.f) + return(val); + + F32 a = mFmod(val, snap); + + if(mFabs(a) > (snap / 2)) + val < 0.f ? val -= snap : val += snap; + + return(val - a); + } + + // + EulerF extractEuler(const MatrixF & matrix) + { + const F32 * mat = (const F32*)matrix; + + EulerF r; + r.x = mAsin(mat[MatrixF::idx(2,1)]); + + if(mCos(r.x) != 0.f) + { + r.y = mAtan2(-mat[MatrixF::idx(2,0)], mat[MatrixF::idx(2,2)]); + r.z = mAtan2(-mat[MatrixF::idx(0,1)], mat[MatrixF::idx(1,1)]); + } + else + { + r.y = 0.f; + r.z = mAtan2(mat[MatrixF::idx(1,0)], mat[MatrixF::idx(0,0)]); + } + + return(r); + } +} + +F32 WorldEditor::smProjectDistance = 20000.0f; + +SceneObject* WorldEditor::getClientObj(SceneObject * obj) +{ + AssertFatal(obj->isServerObject(), "WorldEditor::getClientObj: not a server object!"); + + NetConnection * toServer = NetConnection::getConnectionToServer(); + NetConnection * toClient = NetConnection::getLocalClientConnection(); + if (!toServer || !toClient) + return NULL; + + S32 index = toClient->getGhostIndex(obj); + if(index == -1) + return(0); + + return(dynamic_cast(toServer->resolveGhost(index))); +} + +void WorldEditor::markAsSelected( SimObject* object, bool state ) +{ + object->setSelected( state ); + + if( dynamic_cast< SceneObject* >( object ) ) + { + SceneObject* clientObj = WorldEditor::getClientObj( static_cast< SceneObject* >( object ) ); + if( clientObj ) + clientObj->setSelected( state ); + } +} + +void WorldEditor::setClientObjInfo(SceneObject * obj, const MatrixF & mat, const VectorF & scale) +{ + SceneObject * clientObj = getClientObj(obj); + if(!clientObj) + return; + + clientObj->setTransform(mat); + clientObj->setScale(scale); +} + +void WorldEditor::updateClientTransforms(Selection* sel) +{ + for( U32 i = 0; i < sel->size(); ++ i ) + { + SceneObject* serverObj = dynamic_cast< SceneObject* >( ( *sel )[ i ] ); + if( !serverObj ) + continue; + + SceneObject* clientObj = getClientObj( serverObj ); + if(!clientObj) + continue; + + clientObj->setTransform(serverObj->getTransform()); + clientObj->setScale(serverObj->getScale()); + } +} + +void WorldEditor::addUndoState() +{ + submitUndo( mSelected ); +} + +void WorldEditor::submitUndo( Selection* sel, const UTF8* label ) +{ + // Grab the world editor undo manager. + UndoManager *undoMan = NULL; + if ( !Sim::findObject( "EUndoManager", undoMan ) ) + { + Con::errorf( "WorldEditor::createUndo() - EUndoManager not found!" ); + return; + } + + // Setup the action. + WorldEditorUndoAction *action = new WorldEditorUndoAction( label ); + for(U32 i = 0; i < sel->size(); i++) + { + SceneObject* object = dynamic_cast< SceneObject* >( ( *sel )[ i ] ); + if( !object ) + continue; + + WorldEditorUndoAction::Entry entry; + + entry.mMatrix = object->getTransform(); + entry.mScale = object->getScale(); + entry.mObjId = object->getId(); + action->mEntries.push_back( entry ); + } + + // Submit it. + action->mWorldEditor = this; + undoMan->addAction( action ); + + // Mark the world editor as dirty! + setDirty(); +} + +void WorldEditor::WorldEditorUndoAction::undo() +{ + // NOTE: This function also handles WorldEditorUndoAction::redo(). + + MatrixF oldMatrix; + VectorF oldScale; + for( U32 i = 0; i < mEntries.size(); i++ ) + { + SceneObject *obj; + if ( !Sim::findObject( mEntries[i].mObjId, obj ) ) + continue; + + mWorldEditor->setClientObjInfo( obj, mEntries[i].mMatrix, mEntries[i].mScale ); + + // Grab the current state. + oldMatrix = obj->getTransform(); + oldScale = obj->getScale(); + + // Restore the saved state. + obj->setTransform( mEntries[i].mMatrix ); + obj->setScale( mEntries[i].mScale ); + + // Store the previous state so the next time + // we're called we can restore it. + mEntries[i].mMatrix = oldMatrix; + mEntries[i].mScale = oldScale; + } + + // Mark the world editor as dirty! + mWorldEditor->setDirty(); + mWorldEditor->mSelected->invalidateCentroid(); + + // Let the script get a chance at it. + Con::executef( mWorldEditor, "onWorldEditorUndo" ); +} + +//------------------------------------------------------------------------------ +// edit stuff + +bool WorldEditor::cutSelection(Selection* sel) +{ + if ( !sel->size() ) + return false; + + // First copy the selection. + copySelection( sel ); + + // Grab the world editor undo manager. + UndoManager *undoMan = NULL; + if ( !Sim::findObject( "EUndoManager", undoMan ) ) + { + Con::errorf( "WorldEditor::cutSelection() - EUndoManager not found!" ); + return false; + } + + // Setup the action. + MEDeleteUndoAction *action = new MEDeleteUndoAction(); + while ( sel->size() ) + action->deleteObject( ( *sel )[0] ); + undoMan->addAction( action ); + + // Mark the world editor as dirty! + setDirty(); + + return true; +} + +bool WorldEditor::copySelection(Selection* sel) +{ + mCopyBuffer.clear(); + + for( U32 i = 0; i < sel->size(); i++ ) + { + mCopyBuffer.increment(); + mCopyBuffer.last().save( ( *sel )[i] ); + } + + return true; +} + +bool WorldEditor::pasteSelection( bool dropSel ) +{ + clearSelection(); + + // Grab the world editor undo manager. + UndoManager *undoMan = NULL; + if ( !Sim::findObject( "EUndoManager", undoMan ) ) + { + Con::errorf( "WorldEditor::pasteSelection() - EUndoManager not found!" ); + return false; + } + + SimGroup *missionGroup = NULL; + if( isMethod( "getNewObjectGroup" ) ) + { + const char* targetGroupName = Con::executef( this, "getNewObjectGroup" ); + if( targetGroupName && targetGroupName[ 0 ] && !Sim::findObject( targetGroupName, missionGroup ) ) + Con::errorf( "WorldEditor::pasteSelection() - no SimGroup called '%s'", targetGroupName ); + } + + if( !missionGroup ) + { + if( !Sim::findObject( "MissionGroup", missionGroup ) ) + { + Con::errorf( "WorldEditor::pasteSelection() - MissionGroup not found" ); + return false; + } + } + + // Setup the action. + MECreateUndoAction *action = new MECreateUndoAction( "Paste" ); + + for( U32 i = 0; i < mCopyBuffer.size(); i++ ) + { + SimObject* obj = mCopyBuffer[i].restore(); + if ( !obj ) + continue; + + if ( missionGroup ) + missionGroup->addObject( obj ); + + action->addObject( obj ); + + if ( !mSelectionLocked ) + { + mSelected->addObject( obj ); + Con::executef( this, "onSelect", obj->getIdString() ); + } + } + + // Its safe to submit the action before the selection + // is dropped below because the state of the objects + // are not stored until they are first undone. + undoMan->addAction( action ); + + // drop it ... + if ( dropSel ) + dropSelection( mSelected ); + + if ( mSelected->size() ) + { + char buf[16]; + dSprintf( buf, sizeof(buf), "%d", ( *mSelected )[0]->getId() ); + + SimObject *obj = NULL; + if(mRedirectID) + obj = Sim::findObject(mRedirectID); + Con::executef(obj ? obj : this, "onClick", buf); + } + + // Mark the world editor as dirty! + setDirty(); + + return true; +} + +//------------------------------------------------------------------------------ + +WorldEditorSelection* WorldEditor::getActiveSelectionSet() const +{ + return mSelected; +} + +void WorldEditor::makeActiveSelectionSet( WorldEditorSelection* selection ) +{ + Selection* oldSelection = getActiveSelectionSet(); + Selection* newSelection = selection; + + if( oldSelection == newSelection ) + return; + + // Unset the selection set so that calling onSelect/onUnselect callbacks + // on the editor object will not affect the sets we have. + + mSelected = NULL; + + // Go through all objects in the old selection and for each + // one that is not also in the new selection, signal an + // unselect. + + if( oldSelection ) + { + for( Selection::iterator iter = oldSelection->begin(); iter != oldSelection->end(); ++ iter ) + if( !newSelection || !newSelection->objInSet( *iter ) ) + { + Con::executef( this, "onUnselect", ( *iter )->getIdString() ); + markAsSelected( *iter, false ); + } + + oldSelection->setAutoSelect( false ); + } + + // Go through all objects in the new selection and for each + // one that is not also in the old selection, signal a + // select. + + if( newSelection ) + { + for( Selection::iterator iter = newSelection->begin(); iter != newSelection->end(); ++ iter ) + if( !oldSelection || !oldSelection->objInSet( *iter ) ) + { + markAsSelected( *iter, true ); + Con::executef( this, "onSelect", ( *iter )->getIdString() ); + } + + newSelection->setAutoSelect( true ); + } + + // Install the new selection set. + + mSelected = newSelection; + + if( isMethod( "onSelectionSetChanged" ) ) + Con::executef( this, "onSelectionSetChanged" ); +} + +//------------------------------------------------------------------------------ + +void WorldEditor::hideObject(SceneObject* serverObj, bool hide) +{ + // client + SceneObject * clientObj = getClientObj( serverObj ); + if( clientObj ) + clientObj->setHidden( hide ); + + // server + serverObj->setHidden( hide ); +} + +void WorldEditor::hideSelection(bool hide) +{ + SimGroup* pGroup = dynamic_cast(Sim::findObject("MissionGroup")); + + // set server/client objects hide field + for(U32 i = 0; i < mSelected->size(); i++) + { + SceneObject* serverObj = dynamic_cast< SceneObject* >( ( *mSelected )[ i ] ); + if( !serverObj ) + continue; + + // Prevent non-mission group objects (i.e. Player) from being hidden. + // Otherwise it is difficult to show them again. + if(!serverObj->isChildOfGroup(pGroup)) + continue; + + hideObject(serverObj, hide); + } +} + +void WorldEditor::lockSelection(bool lock) +{ + // + for(U32 i = 0; i < mSelected->size(); i++) + ( *mSelected )[i]->setLocked(lock); +} + +//------------------------------------------------------------------------------ +// the centroid get's moved to the drop point... +void WorldEditor::dropSelection(Selection* sel) +{ + if(!sel->size()) + return; + + setDirty(); + + Point3F centroid = mObjectsUseBoxCenter ? sel->getBoxCentroid() : sel->getCentroid(); + + switch(mDropType) + { + case DropAtCentroid: + // already there + break; + + case DropAtOrigin: + { + if(mDropAtBounds && !sel->containsGlobalBounds()) + { + const Point3F& boxCenter = sel->getBoxCentroid(); + const Box3F& bounds = sel->getBoxBounds(); + Point3F offset = -boxCenter; + offset.z += bounds.len_z() * 0.5f; + + sel->offset( offset, mGridSnap ? mGridPlaneSize : 0.f ); + } + else + sel->offset( Point3F( -centroid ), mGridSnap ? mGridPlaneSize : 0.f ); + + break; + } + + case DropAtCameraWithRot: + { + Point3F center = centroid; + if(mDropAtBounds && !sel->containsGlobalBounds()) + center = sel->getBoxBottomCenter(); + + sel->offset( Point3F( smCamPos - center ), mGridSnap ? mGridPlaneSize : 0.f ); + sel->orient(smCamMatrix, center); + break; + } + + case DropAtCamera: + { + Point3F center = centroid; + if(mDropAtBounds && !sel->containsGlobalBounds()) + sel->getBoxBottomCenter(); + + sel->offset( Point3F( smCamPos - center ), mGridSnap ? mGridPlaneSize : 0.f ); + break; + } + + case DropBelowCamera: + { + Point3F center = centroid; + if(mDropAtBounds && !sel->containsGlobalBounds()) + center = sel->getBoxBottomCenter(); + + Point3F offset = smCamPos - center; + offset.z -= mDropBelowCameraOffset; + sel->offset( offset, mGridSnap ? mGridPlaneSize : 0.f ); + break; + } + + case DropAtScreenCenter: + { + // Use the center of the selection bounds + Point3F center = sel->getBoxCentroid(); + + Gui3DMouseEvent event; + event.pos = smCamPos; + + // Calculate the center of the sceen (in global screen coordinates) + Point2I offset = localToGlobalCoord(Point2I(0,0)); + Point3F sp(F32(offset.x + F32(getExtent().x / 2)), F32(offset.y + (getExtent().y / 2)), 1.0f); + + // Calculate the view distance to fit the selection + // within the camera's view. + const Box3F bounds = sel->getBoxBounds(); + F32 radius = bounds.len()*0.5f; + F32 viewdist = calculateViewDistance(radius) * mDropAtScreenCenterScalar; + + // Be careful of infinite sized objects, or just large ones in general. + if(viewdist > mDropAtScreenCenterMax) + viewdist = mDropAtScreenCenterMax; + + // Position the selection + Point3F wp; + unproject(sp, &wp); + event.vec = wp - smCamPos; + event.vec.normalizeSafe(); + event.vec *= viewdist; + sel->offset( Point3F( event.pos - center ) += event.vec, mGridSnap ? mGridPlaneSize : 0.f ); + + break; + } + + case DropToTerrain: + { + terrainSnapSelection(sel, 0, mGizmo->getPosition(), true); + break; + } + + case DropBelowSelection: + { + dropBelowSelection(sel, centroid, mDropAtBounds); + break; + } + } + + // + updateClientTransforms(sel); +} + +void WorldEditor::dropBelowSelection(Selection* sel, const Point3F & centroid, bool useBottomBounds) +{ + if(!sel->size()) + return; + + Point3F start; + if(useBottomBounds && !sel->containsGlobalBounds()) + start = sel->getBoxBottomCenter(); + else + start = centroid; + + Point3F end = start; + end.z -= 4000.f; + + sel->disableCollision(); // Make sure we don't hit ourselves. + + RayInfo ri; + bool hit = gServerContainer.castRay(start, end, STATIC_COLLISION_TYPEMASK, &ri); + + sel->enableCollision(); + + if( hit ) + sel->offset( ri.point - start, mGridSnap ? mGridPlaneSize : 0.f ); +} + +//------------------------------------------------------------------------------ + +void WorldEditor::terrainSnapSelection(Selection* sel, U8 modifier, Point3F gizmoPos, bool forceStick) +{ + mStuckToGround = false; + + if ( !mStickToGround && !forceStick ) + return; + + if(!sel->size()) + return; + + if(sel->containsGlobalBounds()) + return; + + Point3F centroid; + if(mDropAtBounds && !sel->containsGlobalBounds()) + centroid = sel->getBoxBottomCenter(); + else + centroid = mObjectsUseBoxCenter ? sel->getBoxCentroid() : sel->getCentroid(); + + Point3F start = centroid; + Point3F end = start; + start.z -= 2000; + end.z += 2000.f; + + sel->disableCollision(); // Make sure we don't hit ourselves. + + RayInfo ri; + bool hit; + if(mBoundingBoxCollision) + hit = gServerContainer.collideBox(start, end, TerrainObjectType, &ri); + else + hit = gServerContainer.castRay(start, end, TerrainObjectType, &ri); + + sel->enableCollision(); + + if( hit ) + { + mStuckToGround = true; + + sel->offset( ri.point - centroid, mGridSnap ? mGridPlaneSize : 0.f ); + + if(mTerrainSnapAlignment != AlignNone) + { + EulerF rot(0.0f, 0.0f, 0.0f); // Equivalent to AlignPosY + switch(mTerrainSnapAlignment) + { + case AlignPosX: + rot.set(0.0f, 0.0f, mDegToRad(-90.0f)); + break; + + case AlignPosZ: + rot.set(mDegToRad(90.0f), 0.0f, mDegToRad(180.0f)); + break; + + case AlignNegX: + rot.set(0.0f, 0.0f, mDegToRad(90.0f)); + break; + + case AlignNegY: + rot.set(0.0f, 0.0f, mDegToRad(180.0f)); + break; + + case AlignNegZ: + rot.set(mDegToRad(-90.0f), 0.0f, mDegToRad(180.0f)); + break; + } + + MatrixF mat = MathUtils::createOrientFromDir(ri.normal); + MatrixF rotMat(rot); + + sel->orient(mat.mul(rotMat), Point3F::Zero); + } + } +} + +void WorldEditor::softSnapSelection(Selection* sel, U8 modifier, Point3F gizmoPos) +{ + mSoftSnapIsStuck = false; + mSoftSnapActivated = false; + + // If soft snap is activated, holding CTRL will temporarily deactivate it. + // Conversely, if soft snapping is deactivated, holding CTRL will activate it. + if( (mSoftSnap && (modifier & SI_PRIMARY_CTRL)) || (!mSoftSnap && !(modifier & SI_PRIMARY_CTRL)) ) + return; + + if(!sel->size()) + return; + + if(sel->containsGlobalBounds()) + return; + + mSoftSnapActivated = true; + + Point3F centroid = mObjectsUseBoxCenter ? sel->getBoxCentroid() : sel->getCentroid(); + + // Find objects we may stick against + Vector foundobjs; + + SceneObject *controlObj = getControlObject(); + if ( controlObj ) + controlObj->disableCollision(); + + sel->disableCollision(); + + if(mSoftSnapSizeByBounds) + { + mSoftSnapBounds = sel->getBoxBounds(); + mSoftSnapBounds.setCenter(centroid); + } + else + { + mSoftSnapBounds.set(Point3F(mSoftSnapSize, mSoftSnapSize, mSoftSnapSize)); + mSoftSnapBounds.setCenter(centroid); + } + + mSoftSnapPreBounds = mSoftSnapBounds; + mSoftSnapPreBounds.setCenter(gizmoPos); + + SphereF sphere(gizmoPos, mSoftSnapPreBounds.len()*0.5f); + + gServerContainer.findObjectList(mSoftSnapPreBounds, 0xFFFFFFFF, &foundobjs); + + sel->enableCollision(); + + if ( controlObj ) + controlObj->enableCollision(); + + ConcretePolyList polys; + for(S32 i=0; igetTransform()), so->getScale()); + polys.setObject(so); + so->buildPolyList(PLC_Selection, &polys, mSoftSnapPreBounds, sphere); + } + + // Calculate sticky point + bool found = false; + F32 foundDist = mSoftSnapPreBounds.len(); + Point3F foundPoint(0.0f, 0.0f, 0.0f); + PlaneF foundPlane; + MathUtils::IntersectInfo info; + + if(mSoftSnapDebugRender) + { + mSoftSnapDebugPoint.set(0.0f, 0.0f, 0.0f); + mSoftSnapDebugTriangles.clear(); + } + + F32 backfaceToleranceSize = mSoftSnapBackfaceTolerance*mSoftSnapSize; + for(S32 i=0; i= 3) + { + S32 vertind[3]; + vertind[0] = polys.mIndexList[p.vertexStart]; + vertind[1] = polys.mIndexList[p.vertexStart + 1]; + vertind[2] = polys.mIndexList[p.vertexStart + 2]; + + // Distance to the triangle + F32 d = MathUtils::mTriangleDistance(polys.mVertexList[vertind[0]], polys.mVertexList[vertind[1]], polys.mVertexList[vertind[2]], gizmoPos, &info); + + // Cull backface polys that are not within tolerance + if(p.plane.whichSide(gizmoPos) == PlaneF::Back && d > backfaceToleranceSize) + continue; + + bool changed = false; + if(d < foundDist) + { + changed = true; + found = true; + foundDist = d; + foundPoint = info.segment.p1; + foundPlane = p.plane; + + if(mSoftSnapRenderTriangle) + { + mSoftSnapTriangle.p0 = polys.mVertexList[vertind[0]]; + mSoftSnapTriangle.p1 = polys.mVertexList[vertind[1]]; + mSoftSnapTriangle.p2 = polys.mVertexList[vertind[2]]; + } + } + + if(mSoftSnapDebugRender) + { + Triangle debugTri; + debugTri.p0 = polys.mVertexList[vertind[0]]; + debugTri.p1 = polys.mVertexList[vertind[1]]; + debugTri.p2 = polys.mVertexList[vertind[2]]; + mSoftSnapDebugTriangles.push_back(debugTri); + + if(changed) + { + mSoftSnapDebugSnapTri = debugTri; + mSoftSnapDebugPoint = foundPoint; + } + } + } + } + + if(found) + { + // Align selection to foundPlane normal + if(mSoftSnapAlignment != AlignNone) + { + EulerF rot(0.0f, 0.0f, 0.0f); // Equivalent to AlignPosY + switch(mSoftSnapAlignment) + { + case AlignPosX: + rot.set(0.0f, 0.0f, mDegToRad(-90.0f)); + break; + + case AlignPosZ: + rot.set(mDegToRad(90.0f), 0.0f, mDegToRad(180.0f)); + break; + + case AlignNegX: + rot.set(0.0f, 0.0f, mDegToRad(90.0f)); + break; + + case AlignNegY: + rot.set(0.0f, 0.0f, mDegToRad(180.0f)); + break; + + case AlignNegZ: + rot.set(mDegToRad(-90.0f), 0.0f, mDegToRad(180.0f)); + break; + } + + MatrixF mat = MathUtils::createOrientFromDir(foundPlane.getNormal()); + MatrixF rotMat(rot); + + sel->orient(mat.mul(rotMat), Point3F::Zero); + } + + // Cast ray towards the selection to find how close to move it to the foundPoint + F32 rayLength = mSoftSnapBounds.len() * 2; + Point3F start = sel->getCentroid() - foundPlane.getNormal() * rayLength / 2; + Point3F end = start + foundPlane.getNormal() * rayLength; + + RayInfo ri; + F32 minT = TypeTraits< F32 >::MAX; + + for( U32 i = 0; i < sel->size(); ++ i ) + { + SceneObject* object = dynamic_cast< SceneObject* >( ( *sel )[ i ] ); + if( !object ) + continue; + + // Convert start and end points to object space + Point3F s, e; + MatrixF mat = object->getTransform(); + mat.inverse(); + mat.mulP( start, &s ); + mat.mulP( end, &e ); + + if ( object->castRayRendered( s, e, &ri ) ) + minT = getMin( minT, ri.t ); + } + + if ( minT <= 1.0f ) + foundPoint += ( end - start ) * (0.5f - minT); + + sel->offset( foundPoint - sel->getCentroid(), mGridSnap ? mGridPlaneSize : 0.f ); + } + + mSoftSnapIsStuck = found; +} + +//------------------------------------------------------------------------------ + +SceneObject * WorldEditor::getControlObject() +{ + GameConnection * connection = GameConnection::getLocalClientConnection(); + if(connection) + return(dynamic_cast(connection->getControlObject())); + return(0); +} + +bool WorldEditor::collide( const Gui3DMouseEvent &event, SceneObject **hitObj ) +{ + // Collide against the screen-space class icons... + + S32 collidedIconIdx = -1; + F32 collidedIconDist = F32_MAX; + + for ( U32 i = 0; i < mIcons.size(); i++ ) + { + const IconObject &icon = mIcons[i]; + + if ( icon.rect.pointInRect( event.mousePoint ) && + icon.dist < collidedIconDist ) + { + collidedIconIdx = i; + collidedIconDist = icon.dist; + } + } + + if ( collidedIconIdx != -1 ) + { + *hitObj = mIcons[collidedIconIdx].object; + return true; + } + + if ( mBoundingBoxCollision ) + { + // Raycast against sceneObject bounding boxes... + + SceneObject *controlObj = getControlObject(); + if ( controlObj ) + controlObj->disableCollision(); + + Point3F startPnt = event.pos; + Point3F endPnt = event.pos + event.vec * smProjectDistance; + RayInfo ri; + + bool hit = gServerContainer.collideBox(startPnt, endPnt, 0xFFFFFFFF, &ri); + + if ( controlObj ) + controlObj->enableCollision(); + + if ( hit ) + { + // If we hit an object that is in a Prefab... + // we really want to hit / select that Prefab. + Prefab *prefab = Prefab::getPrefabByChild( ri.object ); + + if ( prefab ) + *hitObj = prefab; + else + *hitObj = ri.object; + + return true; + } + } + + // No icon hit so check against the mesh + if ( mObjectMeshCollision ) + { + SceneObject *controlObj = getControlObject(); + if ( controlObj ) + controlObj->disableCollision(); + + Point3F startPnt = event.pos; + Point3F endPnt = event.pos + event.vec * smProjectDistance; + RayInfo ri; + + bool hit = gServerContainer.castRayRendered(startPnt, endPnt, 0xFFFFFFFF, &ri); + if(hit && ri.object && ( ri.object->getTypeMask() & (TerrainObjectType) || dynamic_cast< GroundPlane* >( ri.object ))) + { + // We don't want to mesh select terrain + hit = false; + } + + if ( controlObj ) + controlObj->enableCollision(); + + if ( hit ) + { + // If we hit an object that is in a Prefab... + // we really want to hit / select that Prefab. + Prefab *prefab = Prefab::getPrefabByChild( ri.object ); + + if ( prefab ) + *hitObj = prefab; + else + *hitObj = ri.object; + + return true; + } + } + + return false; +} + +//------------------------------------------------------------------------------ +// main render functions + +void WorldEditor::renderSelectionWorldBox(Selection* sel) +{ + if( !mRenderSelectionBox || !sel->size() ) + return; + + // Compute the world bounds of the selection. + + Box3F selBox( TypeTraits< F32 >::MAX, TypeTraits< F32 >::MAX, TypeTraits< F32 >::MAX, + TypeTraits< F32 >::MIN, TypeTraits< F32 >::MIN, TypeTraits< F32 >::MIN ); + + for( U32 i = 0; i < sel->size(); ++ i ) + { + SceneObject* object = dynamic_cast< SceneObject* >( ( *sel )[ i ] ); + if( !object ) + continue; + + const Box3F & wBox = object->getWorldBox(); + selBox.minExtents.setMin(wBox.minExtents); + selBox.maxExtents.setMax(wBox.maxExtents); + } + + // Set up the render state block, if we haven't done so + // already. + + if ( mRenderSelectionBoxSB.isNull() ) + { + GFXStateBlockDesc desc; + + desc.setCullMode( GFXCullNone ); + desc.alphaDefined = true; + desc.alphaTestEnable = true; + desc.setZReadWrite( true, true ); + mRenderSelectionBoxSB = GFX->createStateBlock( desc ); + } + + GFX->setStateBlock(mRenderSelectionBoxSB); + + PrimBuild::color( mSelectionBoxColor ); + + // create the box points + Point3F projPnts[8]; + for( U32 i = 0; i < 8; ++ i ) + { + Point3F pnt; + pnt.set(BoxPnts[i].x ? selBox.maxExtents.x : selBox.minExtents.x, + BoxPnts[i].y ? selBox.maxExtents.y : selBox.minExtents.y, + BoxPnts[i].z ? selBox.maxExtents.z : selBox.minExtents.z); + projPnts[i] = pnt; + } + + // do the box + for(U32 j = 0; j < 6; j++) + { + PrimBuild::begin( GFXLineStrip, 4 ); + for(U32 k = 0; k < 4; k++) + { + PrimBuild::vertex3fv( projPnts[BoxVerts[j][k]] ); + } + PrimBuild::end(); + } +} + +void WorldEditor::renderObjectBox( SceneObject *obj, const ColorI &color ) +{ + if ( mRenderObjectBoxSB.isNull() ) + { + GFXStateBlockDesc desc; + desc.setCullMode( GFXCullNone ); + desc.setZReadWrite( true, true ); + mRenderObjectBoxSB = GFX->createStateBlock( desc ); + } + + GFX->setStateBlock(mRenderObjectBoxSB); + + GFXTransformSaver saver; + + Box3F objBox = obj->getObjBox(); + Point3F objScale = obj->getScale(); + Point3F boxScale = objBox.getExtents(); + Point3F boxCenter = obj->getWorldBox().getCenter(); + + MatrixF objMat = obj->getTransform(); + objMat.scale(objScale); + objMat.scale(boxScale); + objMat.setPosition(boxCenter); + + //GFX->multWorld( objMat ); + + PrimBuild::color( ColorI(255,255,255,255) ); + PrimBuild::begin( GFXLineList, 48 ); + + //Box3F objBox = obj->getObjBox(); + //Point3F size = objBox.getExtents(); + //Point3F halfSize = size * 0.5f; + + static const Point3F cubePoints[8] = + { + Point3F(-0.5, -0.5, -0.5), Point3F(-0.5, -0.5, 0.5), Point3F(-0.5, 0.5, -0.5), Point3F(-0.5, 0.5, 0.5), + Point3F( 0.5, -0.5, -0.5), Point3F( 0.5, -0.5, 0.5), Point3F( 0.5, 0.5, -0.5), Point3F( 0.5, 0.5, 0.5) + }; + + // 8 corner points of the box + for ( U32 i = 0; i < 8; i++ ) + { + //const Point3F &start = cubePoints[i]; + + // 3 lines per corner point + for ( U32 j = 0; j < 3; j++ ) + { + Point3F start = cubePoints[i]; + Point3F end = start; + end[j] *= 0.8f; + + objMat.mulP(start); + PrimBuild::vertex3fv(start); + objMat.mulP(end); + PrimBuild::vertex3fv(end); + } + } + + PrimBuild::end(); +} + +void WorldEditor::renderObjectFace(SceneObject * obj, const VectorF & normal, const ColorI & col) +{ + if ( mRenderObjectFaceSB.isNull() ) + { + GFXStateBlockDesc desc; + desc.setCullMode( GFXCullNone ); + desc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha); + desc.setZReadWrite( false ); + mRenderObjectFaceSB = GFX->createStateBlock( desc ); + } + + GFX->setStateBlock(mRenderObjectFaceSB); + + // get the normal index + VectorF objNorm; + obj->getWorldTransform().mulV(normal, &objNorm); + + U32 normI = getBoxNormalIndex(objNorm); + + // + Box3F box = obj->getObjBox(); + MatrixF mat = obj->getTransform(); + VectorF scale = obj->getScale(); + + Point3F projPnts[4]; + for(U32 i = 0; i < 4; i++) + { + Point3F pnt; + pnt.set(BoxPnts[BoxVerts[normI][i]].x ? box.maxExtents.x : box.minExtents.x, + BoxPnts[BoxVerts[normI][i]].y ? box.maxExtents.y : box.minExtents.y, + BoxPnts[BoxVerts[normI][i]].z ? box.maxExtents.z : box.minExtents.z); + + // scale it + pnt.convolve(scale); + mat.mulP(pnt, &projPnts[i]); + } + + PrimBuild::color( col ); + + PrimBuild::begin( GFXTriangleFan, 4 ); + for(U32 k = 0; k < 4; k++) + { + PrimBuild::vertex3f(projPnts[k].x, projPnts[k].y, projPnts[k].z); + } + PrimBuild::end(); +} + +void WorldEditor::renderMousePopupInfo() +{ + if ( !mMouseDragged ) + return; + + + if ( mGizmoProfile->mode == NoneMode || !mGizmoProfile->renderInfoText ) + return; + + char buf[256]; + + switch ( mGizmoProfile->mode ) + { + case MoveMode: + { + if ( !bool(mSelected)|| !mSelected->size() ) + return; + + Point3F pos = getSelectionCentroid(); + dSprintf(buf, sizeof(buf), "x: %0.3f, y: %0.3f, z: %0.3f", pos.x, pos.y, pos.z); + + break; + } + + case RotateMode: + { + if ( !bool(mHitObject) || !bool(mSelected) || (mSelected->size() != 1) ) + return; + + // print out the angle-axis + AngAxisF aa(mHitObject->getTransform()); + + dSprintf(buf, sizeof(buf), "x: %0.3f, y: %0.3f, z: %0.3f, a: %0.3f", + aa.axis.x, aa.axis.y, aa.axis.z, mRadToDeg(aa.angle)); + + break; + } + + case ScaleMode: + { + if ( !bool(mHitObject) || !bool(mSelected) || (mSelected->size() != 1) ) + return; + + VectorF scale = mHitObject->getScale(); + + Box3F box = mHitObject->getObjBox(); + box.minExtents.convolve(scale); + box.maxExtents.convolve(scale); + + box.maxExtents -= box.minExtents; + dSprintf(buf, sizeof(buf), "w: %0.3f, h: %0.3f, d: %0.3f", box.maxExtents.x, box.maxExtents.y, box.maxExtents.z); + + break; + } + + default: + return; + } + + U32 width = mProfile->mFont->getStrWidth((const UTF8 *)buf); + Point2I posi( mLastMouseEvent.mousePoint.x, mLastMouseEvent.mousePoint.y + 12 ); + + if ( mRenderPopupBackground ) + { + Point2I minPt(posi.x - width / 2 - 2, posi.y - 1); + Point2I maxPt(posi.x + width / 2 + 2, posi.y + mProfile->mFont->getHeight() + 1); + + GFX->getDrawUtil()->drawRectFill(minPt, maxPt, mPopupBackgroundColor); + } + + GFX->getDrawUtil()->setBitmapModulation(mPopupTextColor); + GFX->getDrawUtil()->drawText(mProfile->mFont, Point2I(posi.x - width / 2, posi.y), buf); +} + +void WorldEditor::renderPaths(SimObject *obj) +{ + if (obj == NULL) + return; + bool selected = false; + + // Loop through subsets + if (SimSet *set = dynamic_cast(obj)) + for(SimSetIterator itr(set); *itr; ++itr) { + renderPaths(*itr); + if ((*itr)->isSelected()) + selected = true; + } + + // Render the path if it, or any of it's immediate sub-objects, is selected. + if (SimPath::Path *path = dynamic_cast(obj)) + if (selected || path->isSelected()) + renderSplinePath(path); +} + + +void WorldEditor::renderSplinePath(SimPath::Path *path) +{ + // at the time of writing the path properties are not part of the path object + // so we don't know to render it looping, splined, linear etc. + // for now we render all paths splined+looping + + Vector positions; + Vector rotations; + + path->sortMarkers(); + CameraSpline spline; + + for(SimSetIterator itr(path); *itr; ++itr) + { + Marker *pathmarker = dynamic_cast(*itr); + if (!pathmarker) + continue; + Point3F pos; + pathmarker->getTransform().getColumn(3, &pos); + + QuatF rot; + rot.set(pathmarker->getTransform()); + CameraSpline::Knot::Type type; + switch (pathmarker->mKnotType) + { + case Marker::KnotTypePositionOnly: type = CameraSpline::Knot::POSITION_ONLY; break; + case Marker::KnotTypeKink: type = CameraSpline::Knot::KINK; break; + case Marker::KnotTypeNormal: + default: type = CameraSpline::Knot::NORMAL; break; + + } + + CameraSpline::Knot::Path path; + switch (pathmarker->mSmoothingType) + { + case Marker::SmoothingTypeLinear: path = CameraSpline::Knot::LINEAR; break; + case Marker::SmoothingTypeSpline: + default: path = CameraSpline::Knot::SPLINE; break; + + } + + spline.push_back(new CameraSpline::Knot(pos, rot, 1.0f, type, path)); + } + + F32 t = 0.0f; + S32 size = spline.size(); + if (size <= 1) + return; + + // DEBUG + //spline.renderTimeMap(); + + if (mSplineSB.isNull()) + { + GFXStateBlockDesc desc; + + desc.setCullMode( GFXCullNone ); + desc.setBlend( true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha); + desc.samplersDefined = true; + desc.samplers[0].textureColorOp = GFXTOPDisable; + + mSplineSB = GFX->createStateBlock( desc ); + } + + GFX->setStateBlock(mSplineSB); + + + if (path->isLooping()) + { + CameraSpline::Knot *front = new CameraSpline::Knot(*spline.front()); + CameraSpline::Knot *back = new CameraSpline::Knot(*spline.back()); + spline.push_back(front); + spline.push_front(back); + t = 1.0f; + size += 2; + } + + VectorF a(-0.45f, -0.55f, 0.0f); + VectorF b( 0.0f, 0.55f, 0.0f); + VectorF c( 0.45f, -0.55f, 0.0f); + + U32 vCount=0; + + F32 tmpT = t; + while (tmpT < size - 1) + { + tmpT = spline.advanceDist(tmpT, 4.0f); + vCount++; + } + + // Build vertex buffer + + U32 batchSize = vCount; + + if(vCount > 4000) + batchSize = 4000; + + GFXVertexBufferHandle vb; + vb.set(GFX, 3*batchSize, GFXBufferTypeVolatile); + vb.lock(); + + U32 vIdx=0; + + while (t < size - 1) + { + CameraSpline::Knot k; + spline.value(t, &k); + t = spline.advanceDist(t, 4.0f); + + k.mRotation.mulP(a, &vb[vIdx+0].point); + k.mRotation.mulP(b, &vb[vIdx+1].point); + k.mRotation.mulP(c, &vb[vIdx+2].point); + + vb[vIdx+0].point += k.mPosition; + vb[vIdx+1].point += k.mPosition; + vb[vIdx+2].point += k.mPosition; + + vb[vIdx+0].color.set(0, 255, 0, 100); + vb[vIdx+1].color.set(0, 255, 0, 100); + vb[vIdx+2].color.set(0, 255, 0, 100); + + // vb[vIdx+3] = vb[vIdx+1]; + + vIdx+=3; + + // Do we have to knock it out? + if(vIdx > 3 * batchSize - 10) + { + vb.unlock(); + + // Render the buffer + GFX->setVertexBuffer(vb); + GFX->drawPrimitive(GFXTriangleList,0,vIdx/3); + + // Reset for next pass... + vIdx = 0; + vb.lock(); + } + } + + vb.unlock(); + + // Render the buffer + GFX->setVertexBuffer(vb); + //GFX->drawPrimitive(GFXLineStrip,0,3); + + if(vIdx) + GFX->drawPrimitive(GFXTriangleList,0,vIdx/3); +} + +void WorldEditor::renderScreenObj( SceneObject *obj, const Point3F& projPos, const Point3F& wPos ) +{ + // Do not render control objects, hidden objects, + // or objects that are within a prefab. + if(obj == getControlObject() || obj->isHidden() || Prefab::getPrefabByChild(obj)) + return; + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + + // Lookup the ClassIcon - TextureHandle + GFXTexHandle classIcon = gEditorIcons.findIcon( obj ); + + if ( classIcon.isNull() ) + classIcon = mDefaultClassEntry.mDefaultHandle; + + U32 iconWidth = classIcon->getWidth(); + U32 iconHeight = classIcon->getHeight(); + + bool isHighlight = ( obj == mHitObject || mDragSelected->objInSet(obj) ); + + if ( isHighlight ) + { + iconWidth += 0; + iconHeight += 0; + } + + Point2I sPos( (S32)projPos.x, (S32)projPos.y ); + //if ( obj->isSelected() ) + // sPos.y += 4; + Point2I renderPos = sPos; + renderPos.x -= iconWidth / 2; + renderPos.y -= iconHeight / 2; + + Point2I iconSize( iconWidth, iconHeight ); + + RectI renderRect( renderPos, iconSize ); + + // Render object icon, except if the object is the + // only selected object. Do render the icon when there are + // multiple selection as otherwise, objects like lights are + // difficult to place. + + if( mRenderObjHandle && ( !obj->isSelected() || getSelectionSize() > 1 ) ) + { + // Compute icon fade. + + S32 iconAlpha = 255; + if( mFadeIcons && getDisplayType() == DisplayTypePerspective ) + { + Point3F objDist = smCamPos - wPos; + F32 dist = objDist.len(); + + if( dist > mFadeIconsDist ) + { + F32 iconDist = dist - mFadeIconsDist; + iconAlpha = mClampF( 255 - ( 255 * ( iconDist / 10.f ) ), 0.f, 255.f ); + } + } + + if ( isHighlight ) + drawer->setBitmapModulation( ColorI(255,255,255, mClamp(iconAlpha + 50, 0, 255)) ); + else + drawer->setBitmapModulation( ColorI(255,255,255,iconAlpha) ); + + drawer->drawBitmapStretch( classIcon, renderRect ); + drawer->clearBitmapModulation(); + + if ( obj->isLocked() ) + drawer->drawBitmap( mDefaultClassEntry.mLockedHandle, renderPos ); + + // Save an IconObject for performing icon-click testing later. + + mIcons.increment(); + mIcons.last().object = obj; + mIcons.last().rect = renderRect; + mIcons.last().dist = projPos.z; + mIcons.last().alpha = iconAlpha; + } + + // + if ( mRenderObjText && ( obj == mHitObject || obj->isSelected() ) ) + { + const char * str = parseObjectFormat(obj, mObjTextFormat); + + Point2I extent(mProfile->mFont->getStrWidth((const UTF8 *)str), mProfile->mFont->getHeight()); + + Point2I pos(sPos); + + if(mRenderObjHandle) + { + pos.x += (classIcon->getWidth() / 2) - (extent.x / 2); + pos.y += (classIcon->getHeight() / 2) + 3; + } + + + if(mGizmoProfile->mode == NoneMode){ + + drawer->drawBitmapStretch( classIcon, renderRect ); + drawer->setBitmapModulation( ColorI(255,255,255,255) ); + drawer->drawText(mProfile->mFont, pos, str); + if ( obj->isLocked() ) + drawer->drawBitmap( mDefaultClassEntry.mLockedHandle, renderPos ); + + // Save an IconObject for performing icon-click testing later. + { + IconObject icon; + icon.object = obj; + icon.rect = renderRect; + icon.dist = projPos.z; + mIcons.push_back( icon ); + } + }else{ + drawer->setBitmapModulation(mObjectTextColor); + drawer->drawText(mProfile->mFont, pos, str); + }; + } +} + +//------------------------------------------------------------------------------ +// ClassInfo stuff + +WorldEditor::ClassInfo::~ClassInfo() +{ + for(U32 i = 0; i < mEntries.size(); i++) + delete mEntries[i]; +} + +bool WorldEditor::objClassIgnored(const SimObject * obj) +{ + ClassInfo::Entry * entry = getClassEntry(obj); + if(mToggleIgnoreList) + return(!(entry ? entry->mIgnoreCollision : false)); + else + return(entry ? entry->mIgnoreCollision : false); +} + +WorldEditor::ClassInfo::Entry * WorldEditor::getClassEntry(StringTableEntry name) +{ + AssertFatal(name, "WorldEditor::getClassEntry - invalid args"); + for(U32 i = 0; i < mClassInfo.mEntries.size(); i++) + if(!dStricmp(name, mClassInfo.mEntries[i]->mName)) + return(mClassInfo.mEntries[i]); + return(0); +} + +WorldEditor::ClassInfo::Entry * WorldEditor::getClassEntry(const SimObject * obj) +{ + AssertFatal(obj, "WorldEditor::getClassEntry - invalid args"); + return(getClassEntry(obj->getClassName())); +} + +bool WorldEditor::addClassEntry(ClassInfo::Entry * entry) +{ + AssertFatal(entry, "WorldEditor::addClassEntry - invalid args"); + if(getClassEntry(entry->mName)) + return(false); + + mClassInfo.mEntries.push_back(entry); + return(true); +} + +//------------------------------------------------------------------------------ +// Mouse cursor stuff +void WorldEditor::setCursor(U32 cursor) +{ + mCurrentCursor = cursor; +} + +//------------------------------------------------------------------------------ +Signal WorldEditor::smRenderSceneSignal; + +WorldEditor::WorldEditor() + : mCurrentCursor(PlatformCursorController::curArrow) +{ + VECTOR_SET_ASSOCIATION( mIcons ); + + // init the field data + mDropType = DropAtScreenCenter; + mBoundingBoxCollision = true; + mObjectMeshCollision = true; + mRenderPopupBackground = true; + mPopupBackgroundColor.set(100,100,100); + mPopupTextColor.set(255,255,0); + mSelectHandle = StringTable->insert("tools/worldEditor/images/SelectHandle"); + mDefaultHandle = StringTable->insert("tools/worldEditor/images/DefaultHandle"); + mLockedHandle = StringTable->insert("tools/worldEditor/images/LockedHandle"); + mObjectTextColor.set(255,255,255); + mObjectsUseBoxCenter = true; + + mObjSelectColor.set(255,0,0,200); + mObjMultiSelectColor.set(128,0,0,200); + mObjMouseOverSelectColor.set(0,0,255); + mObjMouseOverColor.set(0,255,0); + mShowMousePopupInfo = true; + mDragRectColor.set(255,255,0); + mRenderObjText = true; + mRenderObjHandle = true; + mObjTextFormat = StringTable->insert("$id$: $name|internal$"); + mFaceSelectColor.set(0,0,100,100); + mRenderSelectionBox = true; + mSelectionBoxColor.set(255,255,0,100); + mSelectionLocked = false; + + mToggleIgnoreList = false; + + mIsDirty = false; + + mRedirectID = 0; + + // + mHitObject = NULL; + + // + //mDefaultMode = mCurrentMode = Move; + mMouseDown = false; + mDragSelect = false; + + mStickToGround = false; + mStuckToGround = false; + mTerrainSnapAlignment = AlignNone; + mDropAtBounds = false; + mDropBelowCameraOffset = 15.0f; + mDropAtScreenCenterScalar = 1.0f; + mDropAtScreenCenterMax = 100.0f; + + // Create the drag selection set. + + mDragSelected = new Selection(); + mDragSelected->registerObject( "EWorldEditorDragSelection" ); + Sim::getRootGroup()->addObject( mDragSelected ); + mDragSelected->setAutoSelect(false); + + // + mSoftSnap = false; + mSoftSnapActivated = false; + mSoftSnapIsStuck = false; + mSoftSnapAlignment = AlignNone; + mSoftSnapRender = true; + mSoftSnapRenderTriangle = false; + mSoftSnapSizeByBounds = false; + mSoftSnapSize = 2.0f; + mSoftSnapBackfaceTolerance = 0.5f; + mSoftSnapDebugRender = false; + mSoftSnapDebugPoint.set(0.0f, 0.0f, 0.0f); + + mGridSnap = false; + + mFadeIcons = true; + mFadeIconsDist = 8.f; +} + +WorldEditor::~WorldEditor() +{ +} + +//------------------------------------------------------------------------------ + +bool WorldEditor::onAdd() +{ + if(!Parent::onAdd()) + return(false); + + // create the default class entry + mDefaultClassEntry.mName = 0; + mDefaultClassEntry.mIgnoreCollision = false; + mDefaultClassEntry.mDefaultHandle = GFXTexHandle(mDefaultHandle, &GFXDefaultStaticDiffuseProfile, avar("%s() - mDefaultClassEntry.mDefaultHandle (line %d)", __FUNCTION__, __LINE__)); + mDefaultClassEntry.mSelectHandle = GFXTexHandle(mSelectHandle, &GFXDefaultStaticDiffuseProfile, avar("%s() - mDefaultClassEntry.mSelectHandle (line %d)", __FUNCTION__, __LINE__)); + mDefaultClassEntry.mLockedHandle = GFXTexHandle(mLockedHandle, &GFXDefaultStaticDiffuseProfile, avar("%s() - mDefaultClassEntry.mLockedHandle (line %d)", __FUNCTION__, __LINE__)); + + if(!(mDefaultClassEntry.mDefaultHandle && mDefaultClassEntry.mSelectHandle && mDefaultClassEntry.mLockedHandle)) + return false; + + //mGizmo = new Gizmo(); + //mGizmo->registerObject("WorldEditorGizmo"); + mGizmo->assignName("WorldEditorGizmo"); + + return true; +} + +//------------------------------------------------------------------------------ + +void WorldEditor::onEditorEnable() +{ + // go through and copy the hidden field to the client objects... + for(SimSetIterator itr(Sim::getRootGroup()); *itr; ++itr) + { + SceneObject * obj = dynamic_cast(*itr); + if(!obj) + continue; + + // only work with a server obj... + if(obj->isClientObject()) + continue; + + // grab the client object + SceneObject * clientObj = getClientObj(obj); + if(!clientObj) + continue; + + // + clientObj->setHidden(obj->isHidden()); + } +} + +//------------------------------------------------------------------------------ + +void WorldEditor::get3DCursor(GuiCursor *&cursor, bool &visible, const Gui3DMouseEvent &event) +{ + TORQUE_UNUSED(event); + cursor = NULL; + visible = false; + + GuiCanvas *pRoot = getRoot(); + if( !pRoot ) + return Parent::get3DCursor(cursor,visible,event); + + if(pRoot->mCursorChanged != mCurrentCursor) + { + PlatformWindow *pWindow = static_cast(getRoot())->getPlatformWindow(); + AssertFatal(pWindow != NULL,"GuiControl without owning platform window! This should not be possible."); + PlatformCursorController *pController = pWindow->getCursorController(); + AssertFatal(pController != NULL,"PlatformWindow without an owned CursorController!"); + + // We've already changed the cursor, + // so set it back before we change it again. + if(pRoot->mCursorChanged != -1) + pController->popCursor(); + + // Now change the cursor shape + pController->pushCursor(mCurrentCursor); + pRoot->mCursorChanged = mCurrentCursor; + } +} + +//TODO: [rene 03/10 -- The entire event handling code here needs cleanup] + +void WorldEditor::on3DMouseMove(const Gui3DMouseEvent & event) +{ + setCursor(PlatformCursorController::curArrow); + mHitObject = NULL; + + // + mUsingAxisGizmo = false; + + if ( bool(mSelected) && mSelected->size() > 0 ) + { + mGizmo->on3DMouseMove( event ); + + if ( mGizmo->getSelection() != Gizmo::None ) + { + mUsingAxisGizmo = true; + mHitObject = dynamic_cast< SceneObject* >( ( *mSelected )[0] ); + } + } + + if ( !mHitObject ) + { + SceneObject *hitObj = NULL; + if ( collide(event, &hitObj) && hitObj->isSelectionEnabled() && !objClassIgnored(hitObj) ) + { + mHitObject = hitObj; + } + } + + mLastMouseEvent = event; +} + +void WorldEditor::on3DMouseDown(const Gui3DMouseEvent & event) +{ + mMouseDown = true; + mMouseDragged = false; + mPerformedDragCopy = false; + mDragGridSnapToggle = false; + mLastMouseDownEvent = event; + + mouseLock(); + + // check gizmo first + mUsingAxisGizmo = false; + mNoMouseDrag = false; + + if ( bool(mSelected) && mSelected->size() > 0 ) + { + // Update the move grid settings depending on the + // bounds of the current selection. + + const Box3F& selBounds = getActiveSelectionSet()->getBoxBounds(); + const F32 maxDim = getMax( selBounds.len_x(), getMax( selBounds.len_y(), selBounds.len_z() ) ); + const F32 size = mCeil( maxDim + 10.f ); + const F32 spacing = mCeil( size / 20.f ); + + if( dynamic_cast< SceneObject* >( ( *mSelected )[0] )) + { + + if (size > 0) + { + mGizmo->setMoveGridSize( size ); + mGizmo->setMoveGridSpacing( spacing ); + } + + // Let the gizmo handle the event. + + mGizmo->on3DMouseDown( event ); + } + + if ( mGizmo->getSelection() != Gizmo::None ) + { + mUsingAxisGizmo = true; + mHitObject = dynamic_cast< SceneObject* >( ( *mSelected )[0] ); + + return; + } + } + + SceneObject *hitObj = NULL; + if ( collide( event, &hitObj ) && hitObj->isSelectionEnabled() && !objClassIgnored( hitObj ) ) + { + mPossibleHitObject = hitObj; + mNoMouseDrag = true; + } + else if ( !mSelectionLocked ) + { + if ( !(event.modifier & ( SI_RANGESELECT | SI_MULTISELECT ) ) ) + clearSelection(); + + mDragSelect = true; + mDragSelected->clear(); + mDragRect.set( Point2I(event.mousePoint), Point2I(0,0) ); + mDragStart = event.mousePoint; + } + + mLastMouseEvent = event; +} + +void WorldEditor::on3DMouseUp( const Gui3DMouseEvent &event ) +{ + const bool wasUsingAxisGizmo = mUsingAxisGizmo; + + mMouseDown = false; + mStuckToGround = false; + mSoftSnapIsStuck = false; + mSoftSnapActivated = false; + mUsingAxisGizmo = false; + mGizmo->on3DMouseUp(event); + + // Restore grid snap if we temporarily toggled it. + + if( mDragGridSnapToggle ) + { + mDragGridSnapToggle = false; + const bool snapToGrid = !mGridSnap; + mGridSnap = snapToGrid; + mGizmo->getProfile()->snapToGrid = snapToGrid; + } + + // check if selecting objects.... + if ( mDragSelect ) + { + mDragSelect = false; + mPossibleHitObject = NULL; + + const bool addToSelection = ( event.modifier & ( SI_RANGESELECT | SI_MULTISELECT ) ); + + // add all the objects from the drag selection into the normal selection + if( !addToSelection ) + clearSelection(); + + if ( mDragSelected->size() > 1 ) + { + for ( U32 i = 0; i < mDragSelected->size(); i++ ) + mSelected->addObject( ( *mDragSelected )[i] ); + + Con::executef( this, "onMultiSelect", mDragSelected->getIdString(), addToSelection ? "1" : "0" ); + mDragSelected->clear(); + + SimObject *obj = NULL; + if ( mRedirectID ) + obj = Sim::findObject( mRedirectID ); + Con::executef( obj ? obj : this, "onClick", ( *mSelected )[ 0 ]->getIdString() ); + } + else if ( mDragSelected->size() == 1 ) + { + mSelected->addObject( ( *mDragSelected )[0] ); + Con::executef( this, "onSelect", ( *mDragSelected )[ 0 ]->getIdString() ); + mDragSelected->clear(); + + SimObject *obj = NULL; + if ( mRedirectID ) + obj = Sim::findObject( mRedirectID ); + Con::executef( obj ? obj : this, "onClick", ( *mSelected )[ 0 ]->getIdString() ); + } + + mouseUnlock(); + return; + } + else if( mPossibleHitObject.isValid() && !wasUsingAxisGizmo ) + { + if ( !mSelectionLocked ) + { + if ( event.modifier & ( SI_RANGESELECT | SI_MULTISELECT ) ) + { + mNoMouseDrag = true; + if ( mSelected->objInSet( mPossibleHitObject ) ) + { + mSelected->removeObject( mPossibleHitObject ); + mSelected->storeCurrentCentroid(); + Con::executef( this, "onUnSelect", mPossibleHitObject->getIdString() ); + } + else + { + mSelected->addObject( mPossibleHitObject ); + mSelected->storeCurrentCentroid(); + Con::executef( this, "onSelect", mPossibleHitObject->getIdString() ); + } + } + else + { + if ( bool(mSelected) && !mSelected->objInSet( mPossibleHitObject ) ) + { + mNoMouseDrag = true; + + // Call onUnSelect. Because of the whole treeview<->selection synchronization, + // this may actually cause things to disappear from mSelected so do the loop + // in reverse. This will make the loop work even if items are removed as + // we go along. + for( S32 i = mSelected->size() - 1; i >= 0; -- i ) + Con::executef( this, "onUnSelect", ( *mSelected )[ i ]->getIdString() ); + + mSelected->clear(); + mSelected->addObject( mPossibleHitObject ); + mSelected->storeCurrentCentroid(); + Con::executef( this, "onSelect", mPossibleHitObject->getIdString() ); + } + } + } + + if ( event.mouseClickCount > 1 ) + { + // + char buf[16]; + dSprintf(buf, sizeof(buf), "%d", mPossibleHitObject->getId()); + + SimObject *obj = NULL; + if ( mRedirectID ) + obj = Sim::findObject( mRedirectID ); + Con::executef( obj ? obj : this, "onDblClick", buf ); + } + else + { + char buf[16]; + dSprintf( buf, sizeof(buf), "%d", mPossibleHitObject->getId() ); + + SimObject *obj = NULL; + if ( mRedirectID ) + obj = Sim::findObject( mRedirectID ); + Con::executef( obj ? obj : this, "onClick", buf ); + } + + mHitObject = mPossibleHitObject; + } + + if ( bool(mSelected) && mSelected->hasCentroidChanged() ) + { + Con::executef( this, "onSelectionCentroidChanged"); + } + + if ( mMouseDragged && bool(mSelected) && mSelected->size() ) + { + if ( mSelected->size() ) + { + if ( isMethod("onEndDrag") ) + { + SimObject * obj = 0; + if ( mRedirectID ) + obj = Sim::findObject( mRedirectID ); + Con::executef( obj ? obj : this, "onEndDrag", ( *mSelected )[ 0 ]->getIdString() ); + } + } + } + + //if ( mHitObject ) + // mHitObject->inspectPostApply(); + //mHitObject = NULL; + + // + //mHitObject = hitObj; + mouseUnlock(); +} + +void WorldEditor::on3DMouseDragged(const Gui3DMouseEvent & event) +{ + if ( !mMouseDown ) + return; + + if ( mNoMouseDrag && !mUsingAxisGizmo ) + { + // Perhaps we should start the drag after all + if( mAbs(mLastMouseDownEvent.mousePoint.x - event.mousePoint.x) > 2 || mAbs(mLastMouseDownEvent.mousePoint.y - event.mousePoint.y) > 2 ) + { + if ( !(event.modifier & ( SI_RANGESELECT | SI_MULTISELECT ) ) ) + clearSelection(); + + mDragSelect = true; + mDragSelected->clear(); + mDragRect.set( Point2I(mLastMouseDownEvent.mousePoint), Point2I(0,0) ); + mDragStart = mLastMouseDownEvent.mousePoint; + + mNoMouseDrag = false; + mHitObject = NULL; + } + else + { + return; + } + } + + // + if ( !mMouseDragged ) + { + if ( !mUsingAxisGizmo ) + { + // vert drag on new object.. reset hit offset + if ( mHitObject && bool(mSelected) &&!mSelected->objInSet( mHitObject ) ) + { + if ( !mSelectionLocked ) + mSelected->addObject( mHitObject ); + } + } + + // create and add an undo state + if ( !mDragSelect ) + submitUndo( mSelected ); + + mMouseDragged = true; + } + + // update the drag selection + if ( mDragSelect ) + { + // build the drag selection on the renderScene method - make sure no neg extent! + mDragRect.point.x = (event.mousePoint.x < mDragStart.x) ? event.mousePoint.x : mDragStart.x; + mDragRect.extent.x = (event.mousePoint.x > mDragStart.x) ? event.mousePoint.x - mDragStart.x : mDragStart.x - event.mousePoint.x; + mDragRect.point.y = (event.mousePoint.y < mDragStart.y) ? event.mousePoint.y : mDragStart.y; + mDragRect.extent.y = (event.mousePoint.y > mDragStart.y) ? event.mousePoint.y - mDragStart.y : mDragStart.y - event.mousePoint.y; + return; + } + + if ( !mUsingAxisGizmo && ( !mHitObject || !mSelected->objInSet( mHitObject ) ) ) + return; + + // anything locked? + for ( U32 i = 0; i < mSelected->size(); i++ ) + if ( ( *mSelected )[i]->isLocked() ) + return; + + if ( mUsingAxisGizmo ) + mGizmo->on3DMouseDragged( event ); + + switch ( mGizmoProfile->mode ) + { + case MoveMode: + + // grabbed axis gizmo? + if ( mUsingAxisGizmo ) + { + // Check if a copy should be made + if ( event.modifier & SI_SHIFT && !mPerformedDragCopy ) + { + mPerformedDragCopy = true; + mPossibleHitObject = NULL; + + copySelection( mSelected ); + pasteSelection( false ); + } + + // Check for grid snap toggle with ALT. + + if( event.modifier & SI_PRIMARY_ALT ) + { + if( !mDragGridSnapToggle ) + { + mDragGridSnapToggle = true; + const bool snapToGrid = !mGridSnap; + mGridSnap = snapToGrid; + mGizmo->getProfile()->snapToGrid = snapToGrid; + } + } + else if( mDragGridSnapToggle ) + { + mDragGridSnapToggle = false; + const bool snapToGrid = !mGridSnap; + mGridSnap = snapToGrid; + mGizmo->getProfile()->snapToGrid = snapToGrid; + } + + mSelected->offset( mGizmo->getOffset() ); + + // Handle various sticking + terrainSnapSelection( mSelected, event.modifier, mGizmo->getPosition() ); + softSnapSelection( mSelected, event.modifier, mGizmo->getPosition() ); + + updateClientTransforms( mSelected ); + } + break; + + case ScaleMode: + if ( mUsingAxisGizmo ) + { + Point3F scale = mGizmo->getScale() / (mGizmo->getScale() - mGizmo->getDeltaScale()); + + // Can scale each object independently around its own origin, or scale + // the selection as a group around the centroid + if ( mObjectsUseBoxCenter ) + mSelected->scale( scale, getSelectionCentroid() ); + else + mSelected->scale( scale ); + + updateClientTransforms(mSelected); + } + + break; + + case RotateMode: + { + Point3F centroid = getSelectionCentroid(); + EulerF rot = mGizmo->getDeltaRot(); + + mSelected->rotate(rot, centroid); + updateClientTransforms(mSelected); + + break; + } + + default: + break; + } + + mLastMouseEvent = event; +} + +void WorldEditor::on3DMouseEnter(const Gui3DMouseEvent &) +{ +} + +void WorldEditor::on3DMouseLeave(const Gui3DMouseEvent &) +{ +} + +void WorldEditor::on3DRightMouseDown(const Gui3DMouseEvent & event) +{ +} + +void WorldEditor::on3DRightMouseUp(const Gui3DMouseEvent & event) +{ +} + +//------------------------------------------------------------------------------ + +void WorldEditor::updateGuiInfo() +{ + SimObject * obj = 0; + if ( mRedirectID ) + obj = Sim::findObject( mRedirectID ); + + char buf[] = ""; + Con::executef( obj ? obj : this, "onGuiUpdate", buf ); +} + +//------------------------------------------------------------------------------ + +static void findObjectsCallback( SceneObject *obj, void *val ) +{ + Vector * list = (Vector*)val; + list->push_back(obj); +} + +struct DragMeshCallbackData { + WorldEditor* mWorldEditor; + Box3F mBounds; + SphereF mSphereBounds; + Vector mObjects; + EarlyOutPolyList mPolyList; + MatrixF mStandardMat; + Point3F mStandardScale; + + DragMeshCallbackData(WorldEditor* we, Box3F &bounds, SphereF &sphereBounds) + { + mWorldEditor = we; + mBounds = bounds; + mSphereBounds = sphereBounds; + mStandardMat.identity(); + mStandardScale.set(1.0f, 1.0f, 1.0f); + } +}; + +static Frustum gDragFrustum; +static void findDragMeshCallback( SceneObject *obj, void *data ) +{ + DragMeshCallbackData* dragData = reinterpret_cast(data); + + if ( dragData->mWorldEditor->objClassIgnored( obj ) || + !obj->isSelectionEnabled() || + obj->getTypeMask() & ( TerrainObjectType | ProjectileObjectType ) || + dynamic_cast< GroundPlane* >( obj ) || + Prefab::getPrefabByChild( obj ) ) + { + return; + } + + // Reset the poly list for us + dragData->mPolyList.clear(); + dragData->mPolyList.setTransform(&(dragData->mStandardMat), dragData->mStandardScale); + + // Do the work + obj->buildPolyList(PLC_Selection, &(dragData->mPolyList), dragData->mBounds, dragData->mSphereBounds); + if (!dragData->mPolyList.isEmpty()) + { + dragData->mObjects.push_back(obj); + } +} + +void WorldEditor::renderScene( const RectI &updateRect ) +{ + GFXDEBUGEVENT_SCOPE( Editor_renderScene, ColorI::RED ); + + smRenderSceneSignal.trigger(this); + + // Grab this before anything here changes it. + Frustum frustum; + { + F32 left, right, top, bottom, nearPlane, farPlane; + bool isOrtho = false; + GFX->getFrustum( &left, &right, &bottom, &top, &nearPlane, &farPlane, &isOrtho ); + + MatrixF cameraMat = GFX->getWorldMatrix(); + cameraMat.inverse(); + + frustum.set( isOrtho, left, right, top, bottom, nearPlane, farPlane, cameraMat ); + } + + // Render the paths + renderPaths(Sim::findObject("MissionGroup")); + + // walk selected + Selection* selection = getActiveSelectionSet(); + if( selection ) + { + bool isMultiSelection = mSelected->size() > 1; + for( U32 i = 0; i < mSelected->size(); i++ ) + { + if ( (const SceneObject *)mHitObject == ( *mSelected )[i] ) + continue; + SceneObject* object = dynamic_cast< SceneObject* >( ( *mSelected )[ i ] ); + if( object && mRenderSelectionBox ) + renderObjectBox( object, isMultiSelection ? mObjMultiSelectColor : mObjSelectColor ); + } + } + + // do the drag selection + for ( U32 i = 0; i < mDragSelected->size(); i++ ) + { + SceneObject* object = dynamic_cast< SceneObject* >( ( *mDragSelected )[ i ] ); + if( object && mRenderSelectionBox ) + renderObjectBox(object, mObjSelectColor); + } + + if( selection ) + { + // draw the mouse over obj + if ( bool(mHitObject) && mRenderSelectionBox ) + { + ColorI & col = selection->objInSet(mHitObject) ? mObjMouseOverSelectColor : mObjMouseOverColor; + renderObjectBox(mHitObject, col); + } + + // stuff to do if there is a selection + if ( selection->size() ) + { + if ( mRenderSelectionBox && selection->size() > 1 ) + renderSelectionWorldBox(selection); + + SceneObject* singleSelectedSceneObject = NULL; + if ( selection->size() == 1 ) + singleSelectedSceneObject = dynamic_cast< SceneObject* >( ( *selection )[ 0 ] ); + + MatrixF objMat(true); + if( singleSelectedSceneObject ) + objMat = singleSelectedSceneObject->getTransform(); + + Point3F worldPos = getSelectionCentroid(); + + Point3F objScale = singleSelectedSceneObject ? singleSelectedSceneObject->getScale() : Point3F(1,1,1); + + mGizmo->set( objMat, worldPos, objScale ); + + // Change the gizmo's centroid highlight based on soft sticking state + if( mSoftSnapIsStuck || mStuckToGround ) + mGizmo->setCentroidHandleHighlight( true ); + + mGizmo->renderGizmo( mLastCameraQuery.cameraMatrix, mLastCameraQuery.fov ); + + // Reset any highlighting + if( mSoftSnapIsStuck || mStuckToGround ) + mGizmo->setCentroidHandleHighlight( false ); + + // Soft snap box rendering + if( (mSoftSnapRender || mSoftSnapRenderTriangle) && mSoftSnapActivated) + { + GFXDrawUtil *drawUtil = GFX->getDrawUtil(); + ColorI color; + + GFXStateBlockDesc desc; + + if(mSoftSnapRenderTriangle && mSoftSnapIsStuck) + { + desc.setBlend( false ); + desc.setZReadWrite( false, false ); + desc.fillMode = GFXFillWireframe; + desc.cullMode = GFXCullNone; + + color.set( 255, 255, 128, 255 ); + drawUtil->drawTriangle( desc, mSoftSnapTriangle.p0, mSoftSnapTriangle.p1, mSoftSnapTriangle.p2, color ); + } + + if(mSoftSnapRender) + { + desc.setBlend( true ); + desc.blendSrc = GFXBlendOne; + desc.blendDest = GFXBlendOne; + desc.blendOp = GFXBlendOpAdd; + desc.setZReadWrite( true, false ); + desc.cullMode = GFXCullCCW; + + color.set( 64, 64, 0, 255 ); + + desc.fillMode = GFXFillWireframe; + drawUtil->drawCube(desc, mSoftSnapPreBounds, color); + + desc.fillMode = GFXFillSolid; + drawUtil->drawSphere(desc, mSoftSnapPreBounds.len()*0.05f, mSoftSnapPreBounds.getCenter(), color); + } + } + } + } + + // Debug rendering of the soft stick + if(mSoftSnapDebugRender) + { + GFXDrawUtil *drawUtil = GFX->getDrawUtil(); + ColorI color( 255, 0, 0, 255 ); + + GFXStateBlockDesc desc; + desc.setBlend( false ); + desc.setZReadWrite( false, false ); + + if(mSoftSnapIsStuck) + { + drawUtil->drawArrow( desc, getSelectionCentroid(), mSoftSnapDebugPoint, color ); + + color.set(255, 255, 255); + desc.fillMode = GFXFillWireframe; + for(S32 i=0; idrawTriangle( desc, mSoftSnapDebugTriangles[i].p0, mSoftSnapDebugTriangles[i].p1, mSoftSnapDebugTriangles[i].p2, color ); + } + + color.set(255, 255, 0); + desc.fillMode = GFXFillSolid; + desc.cullMode = GFXCullNone; + drawUtil->drawTriangle( desc, mSoftSnapDebugSnapTri.p0, mSoftSnapDebugSnapTri.p1, mSoftSnapDebugSnapTri.p2, color ); + } + + } + + // Now do the 2D stuff... + // icons and text + GFX->setClipRect(updateRect); + + // update what is in the selection + if ( mDragSelect ) + mDragSelected->clear(); + + // Determine selected objects based on the drag box touching + // a mesh if a drag operation has begun. + if( mDragSelect && mDragRect.extent.x > 1 && mDragRect.extent.y > 1 ) + { + // Build the drag frustum based on the rect + F32 wwidth; + F32 wheight; + F32 aspectRatio = F32(getWidth()) / F32(getHeight()); + + if(!mLastCameraQuery.ortho) + { + wheight = mLastCameraQuery.nearPlane * mTan(mLastCameraQuery.fov / 2); + wwidth = aspectRatio * wheight; + } + else + { + wheight = mLastCameraQuery.fov; + wwidth = aspectRatio * wheight; + } + + F32 hscale = wwidth * 2 / F32(getWidth()); + F32 vscale = wheight * 2 / F32(getHeight()); + + F32 left = (mDragRect.point.x - getPosition().x) * hscale - wwidth; + F32 right = (mDragRect.point.x - getPosition().x + mDragRect.extent.x) * hscale - wwidth; + F32 top = wheight - vscale * (mDragRect.point.y - getPosition().y); + F32 bottom = wheight - vscale * (mDragRect.point.y - getPosition().y + mDragRect.extent.y); + gDragFrustum.set(mLastCameraQuery.ortho, left, right, top, bottom, mLastCameraQuery.nearPlane, mLastCameraQuery.farPlane, mLastCameraQuery.cameraMatrix ); + + // Create the search bounds and callback data + Box3F bounds = gDragFrustum.getBounds(); + SphereF sphere; + sphere.center = bounds.getCenter(); + sphere.radius = (bounds.maxExtents - sphere.center).len(); + DragMeshCallbackData data(this, bounds, sphere); + + // Set up the search normal and planes + Point3F vec; + mLastCameraQuery.cameraMatrix.getColumn(1,&vec); + vec.neg(); + data.mPolyList.mNormal.set(vec); + const PlaneF* planes = gDragFrustum.getPlanes(); + for( U32 i=0; iisFirstPerson() : false; + if( isFirstPerson ) + GameConnection::getLocalClientConnection()->getControlObject()->disableCollision(); + + // Find objects in the region. + + gServerContainer.findObjects( gDragFrustum, 0xFFFFFFFF, findDragMeshCallback, &data); + for ( U32 i = 0; i < data.mObjects.size(); i++ ) + { + SceneObject *obj = data.mObjects[i]; + + // Filter out unwanted objects. + + if( objClassIgnored( obj ) + || !obj->isSelectionEnabled() + || ( obj->getTypeMask() & ( TerrainObjectType | ProjectileObjectType ) ) + || ( obj->getTypeMask() & StaticShapeObjectType && dynamic_cast< GroundPlane* >( obj ) ) ) + continue; + + // Add the object to the drag selection. + + mDragSelected->addObject(obj); + } + + // Re-enable collision on control object when in first-person view. + + if( isFirstPerson ) + GameConnection::getLocalClientConnection()->getControlObject()->enableCollision(); + } + + // Clear the vector of onscreen icons, will populate this below + // Necessary for performing click testing efficiently + mIcons.clear(); + + // Cull Objects and perform icon rendering + Vector objects; + gServerContainer.findObjects( frustum, 0xFFFFFFFF, findObjectsCallback, &objects); + for ( U32 i = 0; i < objects.size(); i++ ) + { + SceneObject *obj = objects[i]; + if( objClassIgnored(obj) || !obj->isSelectionEnabled() ) + continue; + + Point3F wPos; + if ( obj->isGlobalBounds() || !mObjectsUseBoxCenter ) + obj->getTransform().getColumn(3, &wPos); + else + wPos = getBoundingBoxCenter(obj); + + Point3F sPos; + if ( project(wPos, &sPos) ) + { + Point2I sPosI( (S32)sPos.x,(S32)sPos.y ); + if ( !updateRect.pointInRect(sPosI) ) + continue; + + // check if object needs to be added into the regions select + + // Probably should test the entire icon screen-rect instead of just the centerpoint + // but would need to move some code from renderScreenObj to here. + if ( mDragSelect ) + if ( mDragRect.pointInRect(sPosI) && !selection->objInSet(obj) ) + mDragSelected->addObject(obj); + + // + renderScreenObj( obj, sPos, wPos ); + } + } + + //// Debug render rect around icons + //for ( U32 i = 0; i < mIcons.size(); i++ ) + // GFX->getDrawUtil()->drawRect( mIcons[i].rect, ColorI(255,255,255,255) ); + + if ( mShowMousePopupInfo && mMouseDown ) + renderMousePopupInfo(); + + if ( mDragSelect && mDragRect.extent.x > 1 && mDragRect.extent.y > 1 ) + GFX->getDrawUtil()->drawRect( mDragRect, mDragRectColor ); + + if ( selection && selection->size() ) + mGizmo->renderText( mSaveViewport, mSaveModelview, mSaveProjection ); +} + +//------------------------------------------------------------------------------ +// Console stuff + +void WorldEditor::initPersistFields() +{ + addGroup( "Grid" ); + + addField( "gridSnap", TypeBool, Offset( mGridSnap, WorldEditor ), + "If true, transform operations will snap to the grid." ); + + endGroup( "Grid" ); + + addGroup( "Dropping" ); + + addField( "dropAtBounds", TypeBool, Offset(mDropAtBounds, WorldEditor) ); + addField( "dropBelowCameraOffset", TypeF32, Offset(mDropBelowCameraOffset, WorldEditor) ); + addField( "dropAtScreenCenterScalar",TypeF32, Offset(mDropAtScreenCenterScalar, WorldEditor) ); + addField( "dropAtScreenCenterMax", TypeF32, Offset(mDropAtScreenCenterMax, WorldEditor) ); + addField( "dropType", TYPEID< DropType >(), Offset(mDropType, WorldEditor) ); + + endGroup( "Dropping" ); + + addGroup( "Colors" ); + + addField( "popupBackgroundColor", TypeColorI, Offset(mPopupBackgroundColor, WorldEditor) ); + addField( "popupTextColor", TypeColorI, Offset(mPopupTextColor, WorldEditor) ); + addField( "objectTextColor", TypeColorI, Offset(mObjectTextColor, WorldEditor) ); + addField( "selectionBoxColor", TypeColorI, Offset(mSelectionBoxColor, WorldEditor) ); + addField( "objSelectColor", TypeColorI, Offset(mObjSelectColor, WorldEditor) ); + addField( "objMouseOverSelectColor",TypeColorI, Offset(mObjMouseOverSelectColor, WorldEditor) ); + addField( "objMouseOverColor", TypeColorI, Offset(mObjMouseOverColor, WorldEditor) ); + addField( "dragRectColor", TypeColorI, Offset(mDragRectColor, WorldEditor) ); + addField( "faceSelectColor", TypeColorI, Offset(mFaceSelectColor, WorldEditor) ); + + endGroup( "Colors" ); + + addGroup( "Selections" ); + + addField( "boundingBoxCollision", TypeBool, Offset(mBoundingBoxCollision, WorldEditor) ); + addField( "objectMeshCollision", TypeBool, Offset(mObjectMeshCollision, WorldEditor) ); + addField( "selectionLocked", TypeBool, Offset(mSelectionLocked, WorldEditor) ); + addProtectedField( "objectsUseBoxCenter", TypeBool, Offset(mObjectsUseBoxCenter, WorldEditor), &setObjectsUseBoxCenter, &defaultProtectedGetFn, "" ); + + endGroup( "Selections" ); + + addGroup( "Rendering" ); + + addField( "objTextFormat", TypeString, Offset(mObjTextFormat, WorldEditor) ); + addField( "renderPopupBackground", TypeBool, Offset(mRenderPopupBackground, WorldEditor) ); + addField( "showMousePopupInfo", TypeBool, Offset(mShowMousePopupInfo, WorldEditor) ); + addField( "renderObjText", TypeBool, Offset(mRenderObjText, WorldEditor) ); + addField( "renderObjHandle", TypeBool, Offset(mRenderObjHandle, WorldEditor) ); + addField( "renderSelectionBox", TypeBool, Offset(mRenderSelectionBox, WorldEditor) ); + addField( "selectHandle", TypeFilename, Offset(mSelectHandle, WorldEditor) ); + addField( "defaultHandle", TypeFilename, Offset(mDefaultHandle, WorldEditor) ); + addField( "lockedHandle", TypeFilename, Offset(mLockedHandle, WorldEditor) ); + + endGroup( "Rendering" ); + + addGroup( "Rendering: Icons" ); + + addField( "fadeIcons", TypeBool, Offset( mFadeIcons, WorldEditor ), + "Whether object icons should fade out with distance to camera pos." ); + addField( "fadeIconsDist", TypeF32, Offset( mFadeIconsDist, WorldEditor ), + "Distance from camera pos at which to start fading out icons." ); + + endGroup( "Rendering: Icons" ); + + addGroup( "Misc" ); + + addField( "isDirty", TypeBool, Offset(mIsDirty, WorldEditor) ); + addField( "stickToGround", TypeBool, Offset(mStickToGround, WorldEditor) ); + //addField("sameScaleAllAxis", TypeBool, Offset(mSameScaleAllAxis, WorldEditor)); + addField( "toggleIgnoreList", TypeBool, Offset(mToggleIgnoreList, WorldEditor) ); + + endGroup( "Misc" ); + + Parent::initPersistFields(); +} + +//------------------------------------------------------------------------------ +// These methods are needed for the console interfaces. + +void WorldEditor::ignoreObjClass( U32 argc, const char **argv ) +{ + for(S32 i = 2; i < argc; i++) + { + ClassInfo::Entry * entry = getClassEntry(argv[i]); + if(entry) + entry->mIgnoreCollision = true; + else + { + entry = new ClassInfo::Entry; + entry->mName = StringTable->insert(argv[i]); + entry->mIgnoreCollision = true; + if(!addClassEntry(entry)) + delete entry; + } + } +} + +void WorldEditor::clearIgnoreList() +{ + for(U32 i = 0; i < mClassInfo.mEntries.size(); i++) + mClassInfo.mEntries[i]->mIgnoreCollision = false; +} + +void WorldEditor::setObjectsUseBoxCenter(bool state) +{ + mObjectsUseBoxCenter = state; + if( getActiveSelectionSet() && isMethod( "onSelectionCentroidChanged" ) ) + Con::executef( this, "onSelectionCentroidChanged" ); +} + +void WorldEditor::clearSelection() +{ + if( mSelectionLocked || !mSelected ) + return; + + // Call onUnSelect. Because of the whole treeview<->selection synchronization, + // this may actually cause things to disappear from mSelected so do the loop + // in reverse. This will make the loop work even if items are removed as + // we go along. + for( S32 i = mSelected->size() - 1; i >= 0; -- i ) + Con::executef( this, "onUnSelect", ( *mSelected )[ i ]->getIdString() ); + + Con::executef(this, "onClearSelection"); + mSelected->clear(); +} + +void WorldEditor::selectObject( SimObject *obj ) +{ + if ( mSelectionLocked || !mSelected ) + return; + + // Don't check isSelectionEnabled of SceneObjects here as we + // want to still allow manual selection in treeviews. + + if ( !objClassIgnored( obj ) && !mSelected->objInSet( obj ) ) + { + mSelected->addObject( obj ); + Con::executef( this, "onSelect", obj->getIdString() ); + } +} + +void WorldEditor::selectObject( const char* obj ) +{ + SimObject *select; + + if ( Sim::findObject( obj, select ) ) + selectObject( select ); +} + +void WorldEditor::unselectObject( SimObject *obj ) +{ + if ( mSelectionLocked || !mSelected ) + return; + + if ( !objClassIgnored( obj ) && mSelected->objInSet( obj ) ) + { + mSelected->removeObject( obj ); + Con::executef( this, "onUnSelect", obj->getIdString() ); + } +} + +void WorldEditor::unselectObject( const char *obj ) +{ + SimObject *select; + + if ( Sim::findObject( obj, select ) ) + unselectObject( select ); +} + +S32 WorldEditor::getSelectionSize() +{ + if( !mSelected ) + return 0; + + return mSelected->size(); +} + +S32 WorldEditor::getSelectObject(S32 index) +{ + AssertFatal( mSelected != NULL, "WorldEditor::getSelectedObject - no active selection set!" ); + + // Return the object's id + return ( *mSelected )[index]->getId(); +} + +const Point3F& WorldEditor::getSelectionCentroid() +{ + if( !mSelected ) + return Point3F::Zero; + + if( mSelected->containsGlobalBounds() ) + { + return mSelected->getCentroid(); + } + + return mObjectsUseBoxCenter ? mSelected->getBoxCentroid() : mSelected->getCentroid(); +} + +const char* WorldEditor::getSelectionCentroidText() +{ + const Point3F & centroid = getSelectionCentroid(); + char * ret = Con::getReturnBuffer(100); + dSprintf(ret, 100, "%g %g %g", centroid.x, centroid.y, centroid.z); + return ret; +} + +const Box3F& WorldEditor::getSelectionBounds() +{ + return mSelected->getBoxBounds(); +} + +Point3F WorldEditor::getSelectionExtent() +{ + const Box3F& box = getSelectionBounds(); + return box.getExtents(); +} + +F32 WorldEditor::getSelectionRadius() +{ + const Box3F box = getSelectionBounds(); + return box.len() * 0.5f; +} + +void WorldEditor::dropCurrentSelection( bool skipUndo ) +{ + if ( !bool(mSelected) || !mSelected->size() ) + return; + + if ( !skipUndo ) + submitUndo( mSelected ); + + dropSelection( mSelected ); +} + +void WorldEditor::redirectConsole( S32 objID ) +{ + mRedirectID = objID; +} + +//------------------------------------------------------------------------------ + +bool WorldEditor::alignByBounds( S32 boundsAxis ) +{ + if(boundsAxis < 0 || boundsAxis > 5) + return false; + + if(mSelected->size() < 2) + return true; + + S32 axis = boundsAxis >= 3 ? boundsAxis-3 : boundsAxis; + bool useMax = boundsAxis >= 3 ? false : true; + + // Find out which selected object has its bounds the farthest out + F32 pos; + S32 baseObj = 0; + if(useMax) + pos = TypeTraits< F32 >::MIN; + else + pos = TypeTraits< F32 >::MAX; + + for(S32 i=1; isize(); ++i) + { + SceneObject* object = dynamic_cast< SceneObject* >( ( *mSelected )[ i ] ); + if( !object ) + continue; + + const Box3F& bounds = object->getWorldBox(); + + if(useMax) + { + if(bounds.maxExtents[axis] > pos) + { + pos = bounds.maxExtents[axis]; + baseObj = i; + } + } + else + { + if(bounds.minExtents[axis] < pos) + { + pos = bounds.minExtents[axis]; + baseObj = i; + } + } + } + + submitUndo( mSelected, "Align By Bounds" ); + + // Move all selected objects to align with the calculated bounds + for(S32 i=0; isize(); ++i) + { + if(i == baseObj) + continue; + + SceneObject* object = dynamic_cast< SceneObject* >( ( *mSelected )[ i ] ); + if( !object ) + continue; + + const Box3F& bounds = object->getWorldBox(); + F32 delta; + if(useMax) + delta = pos - bounds.maxExtents[axis]; + else + delta = pos - bounds.minExtents[axis]; + + Point3F objPos = object->getPosition(); + objPos[axis] += delta; + object->setPosition(objPos); + } + + return true; +} + +bool WorldEditor::alignByAxis( S32 axis ) +{ + if(axis < 0 || axis > 2) + return false; + + if(mSelected->size() < 2) + return true; + + SceneObject* object = dynamic_cast< SceneObject* >( ( *mSelected )[ 0 ] ); + if( !object ) + return false; + + submitUndo( mSelected, "Align By Axis" ); + + // All objects will be repositioned to line up with the + // first selected object + Point3F pos = object->getPosition(); + + for(S32 i=0; isize(); ++i) + { + SceneObject* object = dynamic_cast< SceneObject* >( ( *mSelected )[ i ] ); + if( !object ) + continue; + + Point3F objPos = object->getPosition(); + objPos[axis] = pos[axis]; + object->setPosition(objPos); + } + + return true; +} + +//------------------------------------------------------------------------------ + +void WorldEditor::transformSelection(bool position, Point3F& p, bool relativePos, bool rotate, EulerF& r, bool relativeRot, bool rotLocal, S32 scaleType, Point3F& s, bool sRelative, bool sLocal) +{ + if(mSelected->size() == 0) + return; + + submitUndo( mSelected, "Transform Selection" ); + + if( position ) + { + if( relativePos ) + { + mSelected->offset( p, mGridSnap ? mGridPlaneSize : 0.f ); + } + else + { + mSelected->setCentroidPosition(mObjectsUseBoxCenter, p); + } + } + + if( rotate ) + { + Point3F centroid; + if( mSelected->containsGlobalBounds() ) + { + centroid = mSelected->getCentroid(); + } + else + { + centroid = mObjectsUseBoxCenter ? mSelected->getBoxCentroid() : mSelected->getCentroid(); + } + + if( relativeRot ) + { + if( rotLocal ) + { + mSelected->rotate(r); + } + else + { + mSelected->rotate(r, centroid); + } + } + else if( rotLocal ) + { + // Can only do absolute rotation for multiple objects about + // object center + mSelected->setRotate(r); + } + } + + if( scaleType == 1 ) + { + // Scale + + Point3F centroid; + if( mSelected->containsGlobalBounds() ) + { + centroid = mSelected->getCentroid(); + } + else + { + centroid = mObjectsUseBoxCenter ? mSelected->getBoxCentroid() : mSelected->getCentroid(); + } + + if( sRelative ) + { + if( sLocal ) + { + mSelected->scale(s); + } + else + { + mSelected->scale(s, centroid); + } + } + else + { + if( sLocal ) + { + mSelected->setScale(s); + } + else + { + mSelected->setScale(s, centroid); + } + } + } + else if( scaleType == 2 ) + { + // Size + + if( mSelected->containsGlobalBounds() ) + return; + + if( sRelative ) + { + // Size is always local/object based + mSelected->addSize(s); + } + else + { + // Size is always local/object based + mSelected->setSize(s); + } + } + + updateClientTransforms(mSelected); + + if(mSelected->hasCentroidChanged()) + { + Con::executef( this, "onSelectionCentroidChanged"); + } + + if ( isMethod("onEndDrag") ) + { + SimObject * obj = 0; + if ( mRedirectID ) + obj = Sim::findObject( mRedirectID ); + Con::executef( obj ? obj : this, "onEndDrag", ( *mSelected )[ 0 ]->getIdString() ); + } +} + +//------------------------------------------------------------------------------ + +void WorldEditor::resetSelectedRotation() +{ + for(S32 i=0; isize(); ++i) + { + SceneObject* object = dynamic_cast< SceneObject* >( ( *mSelected )[ i ] ); + if( !object ) + continue; + + MatrixF mat(true); + mat.setPosition(object->getPosition()); + object->setTransform(mat); + } +} + +void WorldEditor::resetSelectedScale() +{ + for(S32 i=0; isize(); ++i) + { + SceneObject* object = dynamic_cast< SceneObject* >( ( *mSelected )[ i ] ); + if( object ) + object->setScale(Point3F(1,1,1)); + } +} + +//------------------------------------------------------------------------------ + +ConsoleMethod( WorldEditor, ignoreObjClass, void, 3, 0, "(string class_name, ...)") +{ + object->ignoreObjClass(argc, argv); +} + +ConsoleMethod( WorldEditor, clearIgnoreList, void, 2, 2, "") +{ + object->clearIgnoreList(); +} + +ConsoleMethod( WorldEditor, clearSelection, void, 2, 2, "") +{ + object->clearSelection(); +} + +ConsoleMethod( WorldEditor, getActiveSelection, S32, 2, 2, "() - Return the currently active WorldEditorSelection object." ) +{ + if( !object->getActiveSelectionSet() ) + return 0; + + return object->getActiveSelectionSet()->getId(); +} + +ConsoleMethod( WorldEditor, setActiveSelection, void, 3, 3, "( id set ) - Set the currently active WorldEditorSelection object." ) +{ + WorldEditorSelection* selection; + if( !Sim::findObject( argv[ 2 ], selection ) ) + { + Con::errorf( "WorldEditor::setActiveSelectionSet - no selection set '%s'", argv[ 2 ] ); + return; + } + + object->makeActiveSelectionSet( selection ); +} + +ConsoleMethod( WorldEditor, selectObject, void, 3, 3, "(SimObject obj)") +{ + object->selectObject(argv[2]); +} + +ConsoleMethod( WorldEditor, unselectObject, void, 3, 3, "(SimObject obj)") +{ + object->unselectObject(argv[2]); +} + +ConsoleMethod( WorldEditor, invalidateSelectionCentroid, void, 2, 2, "") +{ + WorldEditor::Selection* sel = object->getActiveSelectionSet(); + if(sel) + sel->invalidateCentroid(); +} + +ConsoleMethod( WorldEditor, getSelectionSize, S32, 2, 2, "() - Return the number of objects currently selected in the editor.") +{ + return object->getSelectionSize(); +} + +ConsoleMethod( WorldEditor, getSelectedObject, S32, 3, 3, "(int index)") +{ + S32 index = dAtoi(argv[2]); + if(index < 0 || index >= object->getSelectionSize()) + { + Con::errorf(ConsoleLogEntry::General, "WorldEditor::getSelectedObject: invalid object index"); + return(-1); + } + + return(object->getSelectObject(index)); +} + +ConsoleMethod( WorldEditor, getSelectionRadius, F32, 2, 2, "") +{ + return object->getSelectionRadius(); +} + +ConsoleMethod( WorldEditor, getSelectionCentroid, const char *, 2, 2, "") +{ + return object->getSelectionCentroidText(); +} + +ConsoleMethod( WorldEditor, getSelectionExtent, const char *, 2, 2, "") +{ + Point3F bounds = object->getSelectionExtent(); + char * ret = Con::getReturnBuffer(100); + dSprintf(ret, 100, "%g %g %g", bounds.x, bounds.y, bounds.z); + return ret; +} + +ConsoleMethod( WorldEditor, dropSelection, void, 2, 3, "( bool skipUndo = false )") +{ + bool skipUndo = false; + if ( argc > 2 ) + skipUndo = dAtob( argv[2] ); + + object->dropCurrentSelection( skipUndo ); +} + +void WorldEditor::cutCurrentSelection() +{ + cutSelection(mSelected); +} + +void WorldEditor::copyCurrentSelection() +{ + copySelection(mSelected); +} + +ConsoleMethod( WorldEditor, cutSelection, void, 2, 2, "") +{ + object->cutCurrentSelection(); +} + +ConsoleMethod( WorldEditor, copySelection, void, 2, 2, "") +{ + object->copyCurrentSelection(); +} + +ConsoleMethod( WorldEditor, pasteSelection, void, 2, 2, "") +{ + object->pasteSelection(); +} + +bool WorldEditor::canPasteSelection() +{ + return mCopyBuffer.empty() != true; +} + +ConsoleMethod( WorldEditor, canPasteSelection, bool, 2, 2, "") +{ + return object->canPasteSelection(); +} + +ConsoleMethod( WorldEditor, hideObject, void, 4, 4, "(Object obj, bool hide)") +{ + SceneObject *obj; + if ( !Sim::findObject( argv[2], obj ) ) + return; + + object->hideObject(obj, dAtob(argv[3])); +} + +ConsoleMethod( WorldEditor, hideSelection, void, 3, 3, "(bool hide)") +{ + object->hideSelection(dAtob(argv[2])); +} + +ConsoleMethod( WorldEditor, lockSelection, void, 3, 3, "(bool lock)") +{ + object->lockSelection(dAtob(argv[2])); +} + +ConsoleMethod( WorldEditor, alignByBounds, void, 3, 3, "(int boundsAxis)" + "Align all selected objects against the given bounds axis.") +{ + if(!object->alignByBounds(dAtoi(argv[2]))) + Con::warnf(ConsoleLogEntry::General, avar("worldEditor.alignByBounds: invalid bounds axis '%s'", argv[2])); +} + +ConsoleMethod( WorldEditor, alignByAxis, void, 3, 3, "(int axis)" + "Align all selected objects along the given axis.") +{ + if(!object->alignByAxis(dAtoi(argv[2]))) + Con::warnf(ConsoleLogEntry::General, avar("worldEditor.alignByAxis: invalid axis '%s'", argv[2])); +} + +ConsoleMethod( WorldEditor, resetSelectedRotation, void, 2, 2, "") +{ + object->resetSelectedRotation(); +} + +ConsoleMethod( WorldEditor, resetSelectedScale, void, 2, 2, "") +{ + object->resetSelectedScale(); +} + +ConsoleMethod( WorldEditor, redirectConsole, void, 3, 3, "( int objID )") +{ + object->redirectConsole(dAtoi(argv[2])); +} + +ConsoleMethod( WorldEditor, addUndoState, void, 2, 2, "") +{ + object->addUndoState(); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( WorldEditor, getSoftSnap, bool, 2, 2, "getSoftSnap()\n" + "Is soft snapping always on?") +{ + return object->mSoftSnap; +} + +ConsoleMethod( WorldEditor, setSoftSnap, void, 3, 3, "setSoftSnap(bool)\n" + "Allow soft snapping all of the time.") +{ + object->mSoftSnap = dAtob(argv[2]); +} + +ConsoleMethod( WorldEditor, getSoftSnapSize, F32, 2, 2, "getSoftSnapSize()\n" + "Get the absolute size to trigger a soft snap.") +{ + return object->mSoftSnapSize; +} + +ConsoleMethod( WorldEditor, setSoftSnapSize, void, 3, 3, "setSoftSnapSize(F32)\n" + "Set the absolute size to trigger a soft snap.") +{ + object->mSoftSnapSize = dAtof(argv[2]); +} + +DefineEngineMethod( WorldEditor, getSoftSnapAlignment, WorldEditor::AlignmentType, (),, + "Get the soft snap alignment." ) +{ + return object->mSoftSnapAlignment; +} + +DefineEngineMethod( WorldEditor, setSoftSnapAlignment, void, ( WorldEditor::AlignmentType type ),, + "Set the soft snap alignment." ) +{ + object->mSoftSnapAlignment = type; +} + +ConsoleMethod( WorldEditor, softSnapSizeByBounds, void, 3, 3, "softSnapSizeByBounds(bool)\n" + "Use selection bounds size as soft snap bounds.") +{ + object->mSoftSnapSizeByBounds = dAtob(argv[2]); +} + +ConsoleMethod( WorldEditor, getSoftSnapBackfaceTolerance, F32, 2, 2, "getSoftSnapBackfaceTolerance()\n" + "The fraction of the soft snap radius that backfaces may be included.") +{ + return object->mSoftSnapBackfaceTolerance; +} + +ConsoleMethod( WorldEditor, setSoftSnapBackfaceTolerance, void, 3, 3, "setSoftSnapBackfaceTolerance(F32 with range of 0..1)\n" + "The fraction of the soft snap radius that backfaces may be included.") +{ + object->mSoftSnapBackfaceTolerance = dAtof(argv[2]); +} + +ConsoleMethod( WorldEditor, softSnapRender, void, 3, 3, "softSnapRender(bool)\n" + "Render the soft snapping bounds.") +{ + object->mSoftSnapRender = dAtob(argv[2]); +} + +ConsoleMethod( WorldEditor, softSnapRenderTriangle, void, 3, 3, "softSnapRenderTriangle(bool)\n" + "Render the soft snapped triangle.") +{ + object->mSoftSnapRenderTriangle = dAtob(argv[2]); +} + +ConsoleMethod( WorldEditor, softSnapDebugRender, void, 3, 3, "softSnapDebugRender(bool)\n" + "Toggle soft snapping debug rendering.") +{ + object->mSoftSnapDebugRender = dAtob(argv[2]); +} + +DefineEngineMethod( WorldEditor, getTerrainSnapAlignment, WorldEditor::AlignmentType, (),, + "Get the terrain snap alignment. " ) +{ + return object->mTerrainSnapAlignment; +} + +DefineEngineMethod( WorldEditor, setTerrainSnapAlignment, void, ( WorldEditor::AlignmentType alignment ),, + "Set the terrain snap alignment." ) +{ + object->mTerrainSnapAlignment = alignment; +} + +ConsoleMethod( WorldEditor, transformSelection, void, 13, 13, "transformSelection(...)\n" + "Transform selection by given parameters.") +{ + bool position = dAtob(argv[2]); + Point3F p(0.0f, 0.0f, 0.0f); + dSscanf(argv[3], "%g %g %g", &p.x, &p.y, &p.z); + bool relativePos = dAtob(argv[4]); + + bool rotate = dAtob(argv[5]); + EulerF r(0.0f, 0.0f, 0.0f); + dSscanf(argv[6], "%g %g %g", &r.x, &r.y, &r.z); + bool relativeRot = dAtob(argv[7]); + bool rotLocal = dAtob(argv[8]); + + S32 scaleType = dAtoi(argv[9]); + Point3F s(1.0f, 1.0f, 1.0f); + dSscanf(argv[10], "%g %g %g", &s.x, &s.y, &s.z); + bool sRelative = dAtob(argv[11]); + bool sLocal = dAtob(argv[12]); + + object->transformSelection(position, p, relativePos, rotate, r, relativeRot, rotLocal, scaleType, s, sRelative, sLocal); +} + +#include "core/strings/stringUnit.h" +#include "collision/optimizedPolyList.h" +#include "core/volume.h" +#ifdef TORQUE_COLLADA + #include "ts/collada/colladaUtils.h" +#endif + +void WorldEditor::colladaExportSelection( const String &path ) +{ +#ifdef TORQUE_COLLADA + + Vector< SceneObject* > objectList; + + for ( S32 i = 0; i < mSelected->size(); i++ ) + { + SceneObject *pObj = dynamic_cast< SceneObject* >( ( *mSelected )[i] ); + if ( pObj ) + objectList.push_back( pObj ); + } + + if ( objectList.empty() ) + return; + + Point3F centroid; + MatrixF orientation; + + if ( objectList.size() == 1 ) + { + orientation = objectList[0]->getTransform(); + centroid = objectList[0]->getPosition(); + } + else + { + orientation.identity(); + centroid.zero(); + + S32 count = 0; + + for ( S32 i = 0; i < objectList.size(); i++ ) + { + SceneObject *pObj = objectList[i]; + if ( pObj->isGlobalBounds() ) + continue; + + centroid += pObj->getPosition(); + count++; + } + + centroid /= count; + } + + orientation.setPosition( centroid ); + orientation.inverse(); + + OptimizedPolyList polyList; + polyList.setBaseTransform( orientation ); + + for ( S32 i = 0; i < objectList.size(); i++ ) + { + SceneObject *pObj = objectList[i]; + if ( !pObj->buildPolyList( PLC_Export, &polyList, pObj->getWorldBox(), pObj->getWorldSphere() ) ) + Con::warnf( "colladaExportObjectList() - object %i returned no geometry.", pObj->getId() ); + } + + // Use a ColladaUtils function to do the actual export to a Collada file + ColladaUtils::exportToCollada( path, polyList ); + +#endif +} + +ConsoleMethod( WorldEditor, colladaExportSelection, void, 3, 3, + "( String path ) - Export the combined geometry of all selected objects to the specified path in collada format." ) +{ + object->colladaExportSelection( argv[2] ); +} + +void WorldEditor::makeSelectionPrefab( const char *filename ) +{ + if ( mSelected->size() == 0 ) + { + Con::errorf( "WorldEditor::makeSelectionPrefab - Nothing selected." ); + return; + } + + SimGroup *missionGroup; + if ( !Sim::findObject( "MissionGroup", missionGroup ) ) + { + Con::errorf( "WorldEditor::makeSelectionPrefab - Could not find MissionGroup." ); + return; + } + + Vector< SimObject* > stack; + Vector< SimObject* > found; + + for ( S32 i = 0; i < mSelected->size(); i++ ) + { + SimObject *obj = ( *mSelected )[i]; + stack.push_back( obj ); + } + + Vector< SimGroup* > cleanup; + + while ( !stack.empty() ) + { + SimObject *obj = stack.last(); + SimGroup *grp = dynamic_cast< SimGroup* >( obj ); + + stack.pop_back(); + + if ( grp ) + { + for ( S32 i = 0; i < grp->size(); i++ ) + stack.push_back( grp->at(i) ); + cleanup.push_back( grp ); + } + else + { + if ( Prefab::isValidChild( obj, true ) ) + found.push_back( obj ); + } + } + + if ( found.empty() ) + { + Con::warnf( "WorldEditor::makeSelectionPrefab - No valid objects selected." ); + return; + } + + // SimGroup we collect prefab objects into. + SimGroup *group = new SimGroup(); + group->registerObject(); + + // Transform from World to Prefab space. + MatrixF fabMat(true); + fabMat.setPosition( mSelected->getCentroid() ); + fabMat.inverse(); + + MatrixF objMat; + SimObject *obj = NULL; + SceneObject *sObj = NULL; + + for ( S32 i = 0; i < found.size(); i++ ) + { + obj = found[i]; + sObj = dynamic_cast< SceneObject* >( obj ); + + obj->assignName( "" ); + + if ( sObj ) + { + objMat.mul( fabMat, sObj->getTransform() ); + sObj->setTransform( objMat ); + } + + group->addObject( obj ); + } + + // Save out .prefab file. + group->save( filename, false, "$ThisPrefab = " ); + + // Allocate Prefab object and add to level. + Prefab *fab = new Prefab(); + fab->setFile( filename ); + fabMat.inverse(); + fab->setTransform( fabMat ); + fab->registerObject(); + missionGroup->addObject( fab ); + + // Select it, mark level as dirty. + clearSelection(); + selectObject( fab ); + setDirty(); + + // Delete original objects and temporary SimGroup. + group->deleteObject(); + for ( S32 i = 0; i < cleanup.size(); i++ ) + cleanup[i]->deleteObject(); +} + +void WorldEditor::explodeSelectedPrefab() +{ + Vector prefabList; + + for ( S32 i = 0; i < mSelected->size(); i++ ) + { + Prefab *obj = dynamic_cast( ( *mSelected )[i] ); + if ( obj ) + prefabList.push_back(obj); + } + + if ( prefabList.empty() ) + return; + + UndoManager *undoMan = NULL; + if ( !Sim::findObject( "EUndoManager", undoMan ) ) + { + Con::errorf( "WorldEditor::createUndo() - EUndoManager not found!" ); + return; + } + + CompoundUndoAction *action = new CompoundUndoAction("Explode Prefab"); + + clearSelection(); + + for ( S32 i = 0; i < prefabList.size(); i++ ) + { + Prefab *prefab = prefabList[i]; + + ExplodePrefabUndoAction *explodeAction = new ExplodePrefabUndoAction(prefab); + action->addAction( explodeAction ); + + selectObject( explodeAction->mGroup ); + + MEDeleteUndoAction *delAction = new MEDeleteUndoAction(); + delAction->deleteObject( prefab ); + + action->addAction( delAction ); + } + + undoMan->addAction( action ); + + setDirty(); +} + +ConsoleMethod( WorldEditor, makeSelectionPrefab, void, 3, 3, "( string filename ) - Save selected objects to a .prefab file and replace them in the level with a Prefab object." ) +{ + object->makeSelectionPrefab( argv[2] ); +} + +ConsoleMethod( WorldEditor, explodeSelectedPrefab, void, 2, 2, "() - Replace selected Prefab objects with a SimGroup containing all children objects defined in the .prefab." ) +{ + object->explodeSelectedPrefab(); +} + +ConsoleMethod( WorldEditor, mountRelative, void, 4, 4, "( Object A, Object B )" ) +{ + SceneObject *objA; + if ( !Sim::findObject( argv[2], objA ) ) + return; + + SceneObject *objB; + if ( !Sim::findObject( argv[3], objB ) ) + return; + + MatrixF xfm = objB->getTransform(); + MatrixF mat = objA->getWorldTransform(); + xfm.mul( mat ); + + Point3F pos = objB->getPosition(); + MatrixF temp = objA->getTransform(); + temp.scale( objA->getScale() ); + temp.inverse(); + temp.mulP( pos ); + + xfm.setPosition( pos ); + + + objA->mountObject( objB, -1, xfm ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( WorldEditor, createPolyhedralObject, SceneObject*, ( const char* className, SceneObject* geometryProvider ),, + "Grab the geometry from @a geometryProvider, create a @a className object, and assign it the extracted geometry." ) +{ + if( !geometryProvider ) + { + Con::errorf( "WorldEditor::createPolyhedralObject - Invalid geometry provider!" ); + return NULL; + } + + if( !className || !className[ 0 ] ) + { + Con::errorf( "WorldEditor::createPolyhedralObject - Invalid class name" ); + return NULL; + } + + AbstractClassRep* classRep = AbstractClassRep::findClassRep( className ); + if( !classRep ) + { + Con::errorf( "WorldEditor::createPolyhedralObject - No such class: %s", className ); + return NULL; + } + + // We don't want the extracted poly list to be affected by the object's + // current transform and scale so temporarily reset them. + + MatrixF savedTransform = geometryProvider->getTransform(); + Point3F savedScale = geometryProvider->getScale(); + + geometryProvider->setTransform( MatrixF::Identity ); + geometryProvider->setScale( Point3F( 1.f, 1.f, 1.f ) ); + + // Extract the geometry. Use the object-space bounding volumes + // as we have moved the object to the origin for the moment. + + OptimizedPolyList polyList; + if( !geometryProvider->buildPolyList( PLC_Export, &polyList, geometryProvider->getObjBox(), geometryProvider->getObjBox().getBoundingSphere() ) ) + { + Con::errorf( "WorldEditor::createPolyhedralObject - Failed to extract geometry!" ); + return NULL; + } + + // Restore the object's original transform. + + geometryProvider->setTransform( savedTransform ); + geometryProvider->setScale( savedScale ); + + // Create the object. + + SceneObject* object = dynamic_cast< SceneObject* >( classRep->create() ); + if( !Object ) + { + Con::errorf( "WorldEditor::createPolyhedralObject - Could not create SceneObject with class '%s'", className ); + return NULL; + } + + // Convert the polylist to a polyhedron. + + Polyhedron polyhedron = polyList.toPolyhedron(); + + // Add the vertex data. + + const U32 numPoints = polyhedron.getNumPoints(); + const Point3F* points = polyhedron.getPoints(); + + for( U32 i = 0; i < numPoints; ++ i ) + { + static StringTableEntry sPoint = StringTable->insert( "point" ); + object->setDataField( sPoint, NULL, EngineMarshallData( points[ i ] ) ); + } + + // Add the plane data. + + const U32 numPlanes = polyhedron.getNumPlanes(); + const PlaneF* planes = polyhedron.getPlanes(); + + for( U32 i = 0; i < numPlanes; ++ i ) + { + static StringTableEntry sPlane = StringTable->insert( "plane" ); + const PlaneF& plane = planes[ i ]; + + char buffer[ 1024 ]; + dSprintf( buffer, sizeof( buffer ), "%g %g %g %g", plane.x, plane.y, plane.z, plane.d ); + + object->setDataField( sPlane, NULL, buffer ); + } + + // Add the edge data. + + const U32 numEdges = polyhedron.getNumEdges(); + const Polyhedron::Edge* edges = polyhedron.getEdges(); + + for( U32 i = 0; i < numEdges; ++ i ) + { + static StringTableEntry sEdge = StringTable->insert( "edge" ); + const Polyhedron::Edge& edge = edges[ i ]; + + char buffer[ 1024 ]; + dSprintf( buffer, sizeof( buffer ), "%i %i %i %i ", + edge.face[ 0 ], edge.face[ 1 ], + edge.vertex[ 0 ], edge.vertex[ 1 ] + ); + + object->setDataField( sEdge, NULL, buffer ); + } + + // Set the transform. + + object->setTransform( savedTransform ); + object->setScale( savedScale ); + + // Register and return the object. + + if( !object->registerObject() ) + { + Con::errorf( "WorldEditor::createPolyhedralObject - Failed to register object!" ); + delete object; + return NULL; + } + + return object; +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( WorldEditor, createConvexShapeFrom, ConvexShape*, ( SceneObject* polyObject ),, + "Create a ConvexShape from the given polyhedral object." ) +{ + if( !polyObject ) + { + Con::errorf( "WorldEditor::createConvexShapeFrom - Invalid object" ); + return NULL; + } + + IScenePolyhedralObject* iPoly = dynamic_cast< IScenePolyhedralObject* >( polyObject ); + if( !iPoly ) + { + Con::errorf( "WorldEditor::createConvexShapeFrom - Not a polyhedral object!" ); + return NULL; + } + + // Get polyhedron. + + AnyPolyhedron polyhedron = iPoly->ToAnyPolyhedron(); + const U32 numPlanes = polyhedron.getNumPlanes(); + if( !numPlanes ) + { + Con::errorf( "WorldEditor::createConvexShapeFrom - Object returned no valid polyhedron" ); + return NULL; + } + + // Create a ConvexShape. + + ConvexShape* shape = new ConvexShape(); + + // Add all planes. + + for( U32 i = 0; i < numPlanes; ++ i ) + { + const PlaneF& plane = polyhedron.getPlanes()[ i ]; + + // Polyhedron planes are facing inwards so we need to + // invert the normal here. + + Point3F normal = plane.getNormal(); + normal.neg(); + + // Turn the orientation of the plane into a quaternion. + // The normal is our up vector (that's what's expected + // by ConvexShape for the surface orientation). + + MatrixF orientation( true ); + MathUtils::getMatrixFromUpVector( normal, &orientation ); + const QuatF quat( orientation ); + + // Get the plane position. + + const Point3F position = plane.getPosition(); + + // Turn everything into a "surface" property for the ConvexShape. + + char buffer[ 1024 ]; + dSprintf( buffer, sizeof( buffer ), "%g %g %g %g %g %g %g", + quat.x, quat.y, quat.z, quat.w, + position.x, position.y, position.z + ); + + // Add the surface. + + static StringTableEntry sSurface = StringTable->insert( "surface" ); + shape->setDataField( sSurface, NULL, buffer ); + } + + // Copy the transform. + + shape->setTransform( polyObject->getTransform() ); + shape->setScale( polyObject->getScale() ); + + // Register the shape. + + if( !shape->registerObject() ) + { + Con::errorf( "WorldEditor::createConvexShapeFrom - Could not register ConvexShape!" ); + delete shape; + return NULL; + } + + return shape; +} diff --git a/Engine/source/gui/worldEditor/worldEditor.h b/Engine/source/gui/worldEditor/worldEditor.h new file mode 100644 index 000000000..387a2d810 --- /dev/null +++ b/Engine/source/gui/worldEditor/worldEditor.h @@ -0,0 +1,419 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _WORLDEDITOR_H_ +#define _WORLDEDITOR_H_ + +#ifndef _EDITTSCTRL_H_ +#include "gui/worldEditor/editTSCtrl.h" +#endif + +#ifndef _CONSOLETYPES_H_ +#include "console/consoleTypes.h" +#endif + +#ifndef _GFXTEXTUREHANDLE_H_ +#include "gfx/gfxTextureHandle.h" +#endif + +#ifndef _TSIGNAL_H_ +#include "core/util/tSignal.h" +#endif + +#ifndef _CONSOLE_SIMOBJECTMEMENTO_H_ +#include "console/simObjectMemento.h" +#endif + +#ifndef _UNDO_H_ +#include "util/undo.h" +#endif + +#ifndef _SIMPATH_H_ +#include "scene/simPath.h" +#endif + +#ifndef _DYNAMIC_CONSOLETYPES_H_ +#include "console/dynamicTypes.h" +#endif + + +class SceneObject; +class WorldEditorSelection; + + +/// +class WorldEditor : public EditTSCtrl +{ + typedef EditTSCtrl Parent; + + public: + + typedef WorldEditorSelection Selection; + + struct Triangle + { + Point3F p0; + Point3F p1; + Point3F p2; + }; + + void ignoreObjClass(U32 argc, const char** argv); + void clearIgnoreList(); + + static bool setObjectsUseBoxCenter( void *object, const char *index, const char *data ) { static_cast(object)->setObjectsUseBoxCenter( dAtob( data ) ); return false; }; + void setObjectsUseBoxCenter(bool state); + bool getObjectsUseBoxCenter() { return mObjectsUseBoxCenter; } + + void clearSelection(); + void selectObject(SimObject *obj); + void selectObject(const char* obj); + void unselectObject(SimObject *obj); + void unselectObject(const char* obj); + + S32 getSelectionSize(); + S32 getSelectObject(S32 index); + const Point3F& getSelectionCentroid(); + const char* getSelectionCentroidText(); + const Box3F& getSelectionBounds(); + Point3F getSelectionExtent(); + F32 getSelectionRadius(); + + void dropCurrentSelection( bool skipUndo ); + void copyCurrentSelection(); + void cutCurrentSelection(); + bool canPasteSelection(); + + bool alignByBounds(S32 boundsAxis); + bool alignByAxis(S32 axis); + + void transformSelection(bool position, Point3F& p, bool relativePos, bool rotate, EulerF& r, bool relativeRot, bool rotLocal, S32 scaleType, Point3F& s, bool sRelative, bool sLocal); + + void resetSelectedRotation(); + void resetSelectedScale(); + + void addUndoState(); + void redirectConsole(S32 objID); + + void colladaExportSelection( const String &path ); + + void makeSelectionPrefab( const char *filename ); + void explodeSelectedPrefab(); + + // + static SceneObject* getClientObj(SceneObject *); + static void markAsSelected( SimObject* object, bool state ); + static void setClientObjInfo(SceneObject *, const MatrixF &, const VectorF &); + static void updateClientTransforms(Selection* ); + + protected: + + class WorldEditorUndoAction : public UndoAction + { + public: + + WorldEditorUndoAction( const UTF8* actionName ) : UndoAction( actionName ) + { + } + + WorldEditor *mWorldEditor; + + struct Entry + { + MatrixF mMatrix; + VectorF mScale; + + // validation + U32 mObjId; + U32 mObjNumber; + }; + + Vector mEntries; + + virtual void undo(); + virtual void redo() { undo(); } + }; + + void submitUndo( Selection* sel, const UTF8* label="World Editor Action" ); + + public: + + /// The objects currently in the copy buffer. + Vector mCopyBuffer; + + bool cutSelection(Selection* sel); + bool copySelection(Selection* sel); + bool pasteSelection(bool dropSel=true); + void dropSelection(Selection* sel); + void dropBelowSelection(Selection* sel, const Point3F & centroid, bool useBottomBounds=false); + + void terrainSnapSelection(Selection* sel, U8 modifier, Point3F gizmoPos, bool forceStick=false); + void softSnapSelection(Selection* sel, U8 modifier, Point3F gizmoPos); + + Selection* getActiveSelectionSet() const; + void makeActiveSelectionSet( Selection* sel ); + + void hideObject(SceneObject* obj, bool hide); + + // work off of mSelected + void hideSelection(bool hide); + void lockSelection(bool lock); + + public: + bool objClassIgnored(const SimObject * obj); + void renderObjectBox(SceneObject * obj, const ColorI & col); + + private: + SceneObject * getControlObject(); + bool collide(const Gui3DMouseEvent & event, SceneObject **hitObj ); + + // gfx methods + //void renderObjectBox(SceneObject * obj, const ColorI & col); + void renderObjectFace(SceneObject * obj, const VectorF & normal, const ColorI & col); + void renderSelectionWorldBox(Selection* sel); + + void renderPlane(const Point3F & origin); + void renderMousePopupInfo(); + void renderScreenObj( SceneObject * obj, const Point3F& sPos, const Point3F& wPos ); + + void renderPaths(SimObject *obj); + void renderSplinePath(SimPath::Path *path); + + // axis gizmo + bool mUsingAxisGizmo; + + GFXStateBlockRef mRenderSelectionBoxSB; + GFXStateBlockRef mRenderObjectBoxSB; + GFXStateBlockRef mRenderObjectFaceSB; + GFXStateBlockRef mSplineSB; + + // + bool mIsDirty; + + // + bool mMouseDown; + SimObjectPtr< Selection > mSelected; + + SimObjectPtr< Selection > mDragSelected; + bool mDragSelect; + RectI mDragRect; + Point2I mDragStart; + + // modes for when dragging a selection + enum { + Move = 0, + Rotate, + Scale + }; + + // + //U32 mCurrentMode; + //U32 mDefaultMode; + + S32 mRedirectID; + + /// @name Object Icons + /// @{ + + struct IconObject + { + SceneObject *object; + F32 dist; + RectI rect; + U32 alpha; + }; + + Vector< IconObject > mIcons; + + /// If true, icons fade out with distance to mouse cursor. + bool mFadeIcons; + + /// Distance at which to start fading out icons. + F32 mFadeIconsDist; + + /// @} + + SimObjectPtr mHitObject; + SimObjectPtr mPossibleHitObject; + bool mMouseDragged; + Gui3DMouseEvent mLastMouseEvent; + Gui3DMouseEvent mLastMouseDownEvent; + + // + class ClassInfo + { + public: + ~ClassInfo(); + + struct Entry + { + StringTableEntry mName; + bool mIgnoreCollision; + GFXTexHandle mDefaultHandle; + GFXTexHandle mSelectHandle; + GFXTexHandle mLockedHandle; + }; + + Vector mEntries; + }; + + + ClassInfo mClassInfo; + ClassInfo::Entry mDefaultClassEntry; + + ClassInfo::Entry * getClassEntry(StringTableEntry name); + ClassInfo::Entry * getClassEntry(const SimObject * obj); + bool addClassEntry(ClassInfo::Entry * entry); + + // persist field data + public: + + enum DropType + { + DropAtOrigin = 0, + DropAtCamera, + DropAtCameraWithRot, + DropBelowCamera, + DropAtScreenCenter, + DropAtCentroid, + DropToTerrain, + DropBelowSelection + }; + + // Snapping alignment mode + enum AlignmentType + { + AlignNone = 0, + AlignPosX, + AlignPosY, + AlignPosZ, + AlignNegX, + AlignNegY, + AlignNegZ + }; + + /// A large hard coded distance used to test + /// object and icon selection. + static F32 smProjectDistance; + + S32 mDropType; + bool mBoundingBoxCollision; + bool mObjectMeshCollision; + bool mRenderPopupBackground; + ColorI mPopupBackgroundColor; + ColorI mPopupTextColor; + StringTableEntry mSelectHandle; + StringTableEntry mDefaultHandle; + StringTableEntry mLockedHandle; + ColorI mObjectTextColor; + bool mObjectsUseBoxCenter; + ColorI mObjSelectColor; + ColorI mObjMultiSelectColor; + ColorI mObjMouseOverSelectColor; + ColorI mObjMouseOverColor; + bool mShowMousePopupInfo; + ColorI mDragRectColor; + bool mRenderObjText; + bool mRenderObjHandle; + StringTableEntry mObjTextFormat; + ColorI mFaceSelectColor; + bool mRenderSelectionBox; + ColorI mSelectionBoxColor; + bool mSelectionLocked; + bool mPerformedDragCopy; + bool mDragGridSnapToggle; ///< Grid snap setting temporarily toggled during drag. + bool mToggleIgnoreList; + bool mNoMouseDrag; + bool mDropAtBounds; + F32 mDropBelowCameraOffset; + F32 mDropAtScreenCenterScalar; + F32 mDropAtScreenCenterMax; + + bool mGridSnap; + bool mStickToGround; + bool mStuckToGround; ///< Selection is stuck to the ground + AlignmentType mTerrainSnapAlignment; ///< How does the stickied object align to the terrain + + bool mSoftSnap; ///< Allow soft snapping all of the time + bool mSoftSnapActivated; ///< Soft snap has been activated by the user and allowed by the current rules + bool mSoftSnapIsStuck; ///< Are we snapping? + AlignmentType mSoftSnapAlignment; ///< How does the snapped object align to the snapped surface + bool mSoftSnapRender; ///< Render the soft snapping bounds + bool mSoftSnapRenderTriangle; ///< Render the soft snapped triangle + Triangle mSoftSnapTriangle; ///< The triangle we are snapping to + bool mSoftSnapSizeByBounds; ///< Use the selection bounds for the size + F32 mSoftSnapSize; ///< If not the selection bounds, use this size + Box3F mSoftSnapBounds; ///< The actual bounds used for the soft snap + Box3F mSoftSnapPreBounds; ///< The bounds prior to any soft snapping (used when rendering the snap bounds) + F32 mSoftSnapBackfaceTolerance; ///< Fraction of mSoftSnapSize for backface polys to have an influence + + bool mSoftSnapDebugRender; ///< Activates debug rendering + Point3F mSoftSnapDebugPoint; ///< The point we're attempting to snap to + Triangle mSoftSnapDebugSnapTri; ///< The triangle we are snapping to + Vector mSoftSnapDebugTriangles; ///< The triangles that are considered for snapping + + protected: + + S32 mCurrentCursor; + void setCursor(U32 cursor); + void get3DCursor(GuiCursor *&cursor, bool &visible, const Gui3DMouseEvent &event); + + public: + + WorldEditor(); + ~WorldEditor(); + + void setDirty() { mIsDirty = true; } + + // SimObject + virtual bool onAdd(); + virtual void onEditorEnable(); + + // EditTSCtrl + void on3DMouseMove(const Gui3DMouseEvent & event); + void on3DMouseDown(const Gui3DMouseEvent & event); + void on3DMouseUp(const Gui3DMouseEvent & event); + void on3DMouseDragged(const Gui3DMouseEvent & event); + void on3DMouseEnter(const Gui3DMouseEvent & event); + void on3DMouseLeave(const Gui3DMouseEvent & event); + void on3DRightMouseDown(const Gui3DMouseEvent & event); + void on3DRightMouseUp(const Gui3DMouseEvent & event); + + void updateGuiInfo(); + + // + void renderScene(const RectI & updateRect); + + static void initPersistFields(); + + DECLARE_CONOBJECT(WorldEditor); + + static Signal smRenderSceneSignal; +}; + +typedef WorldEditor::DropType WorldEditorDropType; +typedef WorldEditor::AlignmentType WorldEditorAlignmentType; + +DefineEnumType( WorldEditorDropType ); +DefineEnumType( WorldEditorAlignmentType ); + +#endif // _WORLDEDITOR_H_ + diff --git a/Engine/source/gui/worldEditor/worldEditorSelection.cpp b/Engine/source/gui/worldEditor/worldEditorSelection.cpp new file mode 100644 index 000000000..ed2e1f5a3 --- /dev/null +++ b/Engine/source/gui/worldEditor/worldEditorSelection.cpp @@ -0,0 +1,708 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gui/worldEditor/worldEditorSelection.h" +#include "gui/worldEditor/worldEditor.h" +#include "scene/sceneObject.h" + + +IMPLEMENT_CONOBJECT( WorldEditorSelection ); + +ConsoleDocClass( WorldEditorSelection, + "@brief Specialized simset that stores the objects selected by the World Editor\n\n" + "Editor use only.\n\n" + "@internal" +); + +//----------------------------------------------------------------------------- + +WorldEditorSelection::WorldEditorSelection() + : mCentroidValid(false), + mAutoSelect(false), + mPrevCentroid(0.0f, 0.0f, 0.0f), + mContainsGlobalBounds(false) +{ + // Selections are transient by default. + setCanSave( false ); + setEditorOnly( true ); +} + +//----------------------------------------------------------------------------- + +WorldEditorSelection::~WorldEditorSelection() +{ +} + +//----------------------------------------------------------------------------- + +void WorldEditorSelection::initPersistFields() +{ + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +void WorldEditorSelection::setCanSave( bool value ) +{ + if( getCanSave() == value ) + return; + + Parent::setCanSave( value ); + + // If we went from being transient to being persistent, + // make sure all objects in the selection have persistent IDs. + + if( getCanSave() ) + for( iterator iter = begin(); iter != end(); ++ iter ) + ( *iter )->getOrCreatePersistentId(); +} + +//----------------------------------------------------------------------------- + +bool WorldEditorSelection::objInSet( SimObject* obj ) +{ + if( !mIsResolvingPIDs ) + resolvePIDs(); + + lock(); + + bool result = false; + for( iterator iter = begin(); iter != end(); ++ iter ) + { + if( obj == *iter ) + { + result = true; + break; + } + + WorldEditorSelection* set = dynamic_cast< WorldEditorSelection* >( *iter ); + if( set && set->objInSet( obj ) ) + { + result = true; + break; + } + } + + unlock(); + + return result; +} + +//----------------------------------------------------------------------------- + +void WorldEditorSelection::addObject( SimObject* obj ) +{ + // Return if object is already in selection. + + if( objInSet( obj ) ) + return; + + // Refuse to add object if this selection is locked. + + if( isLocked() ) + return; + + // Prevent adding us to ourselves. + + if( obj == this ) + return; + + // If the object is itself a selection set, make sure we + // don't create a cycle. + + WorldEditorSelection* selection = dynamic_cast< WorldEditorSelection* >( obj ); + if( selection && !selection->objInSet( this ) ) + return; + + // Refuse to add any of our parents. + + for( SimGroup* group = getGroup(); group != NULL; group = group->getGroup() ) + if( obj == group ) + return; + + invalidateCentroid(); + + Parent::addObject( obj ); + + if( mAutoSelect ) + WorldEditor::markAsSelected( obj, true ); + + return; +} + +//----------------------------------------------------------------------------- + +void WorldEditorSelection::removeObject( SimObject* obj ) +{ + if( !objInSet( obj ) ) + return; + + // Refuse to remove object if this selection is locked. + + if( isLocked() ) + return; + + invalidateCentroid(); + + Parent::removeObject( obj ); + + if( mAutoSelect ) + WorldEditor::markAsSelected( obj, false ); + + return; +} + +//----------------------------------------------------------------------------- + +bool WorldEditorSelection::containsGlobalBounds() +{ + updateCentroid(); + return mContainsGlobalBounds; +} + +//----------------------------------------------------------------------------- + +void WorldEditorSelection::updateCentroid() +{ + if( mCentroidValid ) + return; + + resolvePIDs(); + + mCentroidValid = true; + + mCentroid.set(0,0,0); + mBoxCentroid = mCentroid; + mBoxBounds.minExtents.set(1e10, 1e10, 1e10); + mBoxBounds.maxExtents.set(-1e10, -1e10, -1e10); + + mContainsGlobalBounds = false; + + if( empty() ) + return; + + // + for( SimSet::iterator iter = begin(); iter != end(); ++ iter ) + { + SceneObject* obj = dynamic_cast( *iter ); + if( !obj ) + continue; + + const MatrixF & mat = obj->getTransform(); + Point3F wPos; + mat.getColumn(3, &wPos); + + // + mCentroid += wPos; + + // + const Box3F& bounds = obj->getWorldBox(); + mBoxBounds.minExtents.setMin(bounds.minExtents); + mBoxBounds.maxExtents.setMax(bounds.maxExtents); + + if(obj->isGlobalBounds()) + mContainsGlobalBounds = true; + } + + mCentroid /= (F32) size(); + mBoxCentroid = mBoxBounds.getCenter(); +} + +//----------------------------------------------------------------------------- + +const Point3F & WorldEditorSelection::getCentroid() +{ + updateCentroid(); + return(mCentroid); +} + +//----------------------------------------------------------------------------- + +const Point3F & WorldEditorSelection::getBoxCentroid() +{ + updateCentroid(); + return(mBoxCentroid); +} + +//----------------------------------------------------------------------------- + +const Box3F & WorldEditorSelection::getBoxBounds() +{ + updateCentroid(); + return(mBoxBounds); +} + +//----------------------------------------------------------------------------- + +Point3F WorldEditorSelection::getBoxBottomCenter() +{ + updateCentroid(); + + Point3F bottomCenter = mBoxCentroid; + bottomCenter.z -= mBoxBounds.len_z() * 0.5f; + + return bottomCenter; +} + +//----------------------------------------------------------------------------- + +void WorldEditorSelection::enableCollision() +{ + for( iterator iter = begin(); iter != end(); ++ iter ) + { + SceneObject* object = dynamic_cast( *iter ); + if( object ) + object->enableCollision(); + } +} + +//----------------------------------------------------------------------------- + +void WorldEditorSelection::disableCollision() +{ + for( iterator iter = begin(); iter != end(); ++ iter ) + { + SceneObject* object = dynamic_cast< SceneObject* >( *iter ); + if( object ) + object->disableCollision(); + } +} + +//----------------------------------------------------------------------------- + +void WorldEditorSelection::offset( const Point3F& offset, F32 gridSnap ) +{ + for( iterator iter = begin(); iter != end(); ++ iter ) + { + SceneObject* obj = dynamic_cast( *iter ); + if( !obj ) + continue; + + MatrixF mat = obj->getTransform(); + Point3F wPos; + mat.getColumn(3, &wPos); + + // adjust + wPos += offset; + + if( gridSnap != 0.f ) + { + wPos.x -= mFmod( wPos.x, gridSnap ); + wPos.y -= mFmod( wPos.y, gridSnap ); + wPos.z -= mFmod( wPos.z, gridSnap ); + } + + mat.setColumn(3, wPos); + obj->setTransform(mat); + } + + mCentroidValid = false; +} + +//----------------------------------------------------------------------------- + +void WorldEditorSelection::setPosition(const Point3F & pos) +{ + for( iterator iter = begin(); iter != end(); ++ iter ) + { + SceneObject* object = dynamic_cast( *iter ); + if( object ) + object->setPosition(pos); + } + + mCentroidValid = false; +} + +//----------------------------------------------------------------------------- + +void WorldEditorSelection::setCentroidPosition(bool useBoxCenter, const Point3F & pos) +{ + Point3F centroid; + if( containsGlobalBounds() ) + { + centroid = getCentroid(); + } + else + { + centroid = useBoxCenter ? getBoxCentroid() : getCentroid(); + } + + offset(pos - centroid); +} + +//----------------------------------------------------------------------------- + +void WorldEditorSelection::orient(const MatrixF & rot, const Point3F & center) +{ + // Orient all the selected objects to the given rotation + for( iterator iter = begin(); iter != end(); ++ iter ) + { + SceneObject* object = dynamic_cast< SceneObject* >( *iter ); + if( !object ) + continue; + + MatrixF mat = rot; + mat.setPosition( object->getPosition() ); + object->setTransform(mat); + } + + mCentroidValid = false; +} + +//----------------------------------------------------------------------------- + +void WorldEditorSelection::rotate(const EulerF &rot) +{ + for( iterator iter = begin(); iter != end(); ++ iter ) + { + SceneObject* object = dynamic_cast< SceneObject* >( *iter ); + if( !object ) + continue; + + MatrixF mat = object->getTransform(); + + MatrixF transform(rot); + mat.mul(transform); + + object->setTransform(mat); + } +} + +//----------------------------------------------------------------------------- + +void WorldEditorSelection::rotate(const EulerF & rot, const Point3F & center) +{ + // single selections will rotate around own axis, multiple about world + if(size() == 1) + { + SceneObject* object = dynamic_cast< SceneObject* >( at( 0 ) ); + if( object ) + { + MatrixF mat = object->getTransform(); + + Point3F pos; + mat.getColumn(3, &pos); + + // get offset in obj space + Point3F offset = pos - center; + MatrixF wMat = object->getWorldTransform(); + wMat.mulV(offset); + + // + MatrixF transform(EulerF(0,0,0), -offset); + transform.mul(MatrixF(rot)); + transform.mul(MatrixF(EulerF(0,0,0), offset)); + mat.mul(transform); + + object->setTransform(mat); + } + } + else + { + for( iterator iter = begin(); iter != end(); ++ iter ) + { + SceneObject* object = dynamic_cast< SceneObject* >( *iter ); + if( !object ) + continue; + + MatrixF mat = object->getTransform(); + + Point3F pos; + mat.getColumn(3, &pos); + + // get offset in obj space + Point3F offset = pos - center; + + MatrixF transform(rot); + Point3F wOffset; + transform.mulV(offset, &wOffset); + + MatrixF wMat = object->getWorldTransform(); + wMat.mulV(offset); + + // + transform.set(EulerF(0,0,0), -offset); + + mat.setColumn(3, Point3F(0,0,0)); + wMat.setColumn(3, Point3F(0,0,0)); + + transform.mul(wMat); + transform.mul(MatrixF(rot)); + transform.mul(mat); + mat.mul(transform); + + mat.normalize(); + mat.setColumn(3, wOffset + center); + + object->setTransform(mat); + } + } + + mCentroidValid = false; +} + +//----------------------------------------------------------------------------- + +void WorldEditorSelection::setRotate(const EulerF & rot) +{ + for( iterator iter = begin(); iter != end(); ++ iter ) + { + SceneObject* object = dynamic_cast< SceneObject* >( *iter ); + if( !object ) + continue; + + MatrixF mat = object->getTransform(); + Point3F pos; + mat.getColumn(3, &pos); + + MatrixF rmat(rot); + rmat.setPosition(pos); + + object->setTransform(rmat); + } +} + +//----------------------------------------------------------------------------- + +void WorldEditorSelection::scale(const VectorF & scale) +{ + for( iterator iter = begin(); iter != end(); ++ iter ) + { + SceneObject* object = dynamic_cast< SceneObject* >( *iter ); + if( !object ) + continue; + + VectorF current = object->getScale(); + current.convolve(scale); + + // clamp scale to sensible limits + current.setMax( Point3F( 0.01f ) ); + current.setMin( Point3F( 1000.0f ) ); + + object->setScale(current); + } + + mCentroidValid = false; +} + +//----------------------------------------------------------------------------- + +void WorldEditorSelection::scale(const VectorF & scale, const Point3F & center) +{ + for( iterator iter = begin(); iter != end(); ++ iter ) + { + SceneObject* object = dynamic_cast< SceneObject* >( *iter ); + if( !object ) + continue; + + VectorF current = object->getScale(); + current.convolve(scale); + + // clamp scale to sensible limits + current.setMax( Point3F( 0.01f ) ); + current.setMin( Point3F( 1000.0f ) ); + + // Apply the scale first. If the object's scale doesn't change with + // this operation then this object doesn't scale. In this case + // we don't want to continue with the offset operation. + VectorF prevScale = object->getScale(); + object->setScale(current); + if( !object->getScale().equal(current) ) + continue; + + // determine the actual scale factor to apply to the object offset + // need to account for the scale limiting above to prevent offsets + // being reduced to 0 which then cannot be restored by unscaling + VectorF adjustedScale = current / prevScale; + + MatrixF mat = object->getTransform(); + + Point3F pos; + mat.getColumn(3, &pos); + + Point3F offset = pos - center; + offset *= adjustedScale; + + object->setPosition(offset + center); + } +} + +//----------------------------------------------------------------------------- + +void WorldEditorSelection::setScale(const VectorF & scale) +{ + for( iterator iter = begin(); iter != end(); ++ iter ) + { + SceneObject* object = dynamic_cast< SceneObject* >( *iter ); + if( object ) + object->setScale( scale ); + } + + mCentroidValid = false; +} + +//----------------------------------------------------------------------------- + +void WorldEditorSelection::setScale(const VectorF & scale, const Point3F & center) +{ + for( iterator iter = begin(); iter != end(); ++ iter ) + { + SceneObject* object = dynamic_cast< SceneObject* >( *iter ); + if( !object ) + continue; + + MatrixF mat = object->getTransform(); + + Point3F pos; + mat.getColumn(3, &pos); + + Point3F offset = pos - center; + offset *= scale; + + object->setPosition(offset + center); + object->setScale(scale); + } +} + +//----------------------------------------------------------------------------- + +void WorldEditorSelection::addSize(const VectorF & newsize) +{ + for( iterator iter = begin(); iter != end(); ++ iter ) + { + SceneObject* object = dynamic_cast< SceneObject* >( *iter ); + if( !object ) + continue; + + if( object->isGlobalBounds() ) + continue; + + const Box3F& bounds = object->getObjBox(); + VectorF extent = bounds.getExtents(); + VectorF scaledextent = object->getScale() * extent; + + VectorF scale = (newsize + scaledextent) / scaledextent; + object->setScale( object->getScale() * scale ); + } +} + +//----------------------------------------------------------------------------- + +void WorldEditorSelection::setSize(const VectorF & newsize) +{ + for( iterator iter = begin(); iter != end(); ++ iter ) + { + SceneObject* object = dynamic_cast< SceneObject* >( *iter ); + if( !object ) + continue; + + if( object->isGlobalBounds() ) + continue; + + const Box3F& bounds = object->getObjBox(); + VectorF extent = bounds.getExtents(); + + VectorF scale = newsize / extent; + object->setScale( scale ); + } +} + +//============================================================================= +// Console Methods. +//============================================================================= +// MARK: ---- Console Methods ---- + +//----------------------------------------------------------------------------- + +ConsoleMethod( WorldEditorSelection, containsGlobalBounds, bool, 2, 2, "() - True if an object with global bounds is contained in the selection." ) +{ + return object->containsGlobalBounds(); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( WorldEditorSelection, getCentroid, const char*, 2, 2, "() - Return the median of all object positions in the selection." ) +{ + char* buffer = Con::getReturnBuffer( 256 ); + const Point3F& centroid = object->getCentroid(); + + dSprintf( buffer, 256, "%g %g %g", centroid.x, centroid.y, centroid.z ); + return buffer; +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( WorldEditorSelection, getBoxCentroid, const char*, 2, 2, "() - Return the center of the bounding box around the selection." ) +{ + char* buffer = Con::getReturnBuffer( 256 ); + const Point3F& boxCentroid = object->getBoxCentroid(); + + dSprintf( buffer, 256, "%g %g %g", boxCentroid.x, boxCentroid.y, boxCentroid.z ); + return buffer; +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( WorldEditorSelection, offset, void, 3, 4, "( vector delta, float gridSnap=0 ) - Move all objects in the selection by the given delta." ) +{ + F32 x, y, z; + dSscanf( argv[ 3 ], "%g %g %g", &x, &y, &z ); + + F32 gridSnap = 0.f; + if( argc > 3 ) + gridSnap = dAtof( argv[ 3 ] ); + + object->offset( Point3F( x, y, z ), gridSnap ); + WorldEditor::updateClientTransforms( object ); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( WorldEditorSelection, union, void, 3, 3, "( SimSet set ) - Add all objects in the given set to this selection." ) +{ + SimSet* selection; + if( !Sim::findObject( argv[ 2 ], selection ) ) + { + Con::errorf( "WorldEditorSelection::union - no SimSet '%s'", argv[ 2 ] ); + return; + } + + const U32 numObjects = selection->size(); + for( U32 i = 0; i < numObjects; ++ i ) + object->addObject( selection->at( i ) ); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( WorldEditorSelection, subtract, void, 3, 3, "( SimSet ) - Remove all objects in the given set from this selection." ) +{ + SimSet* selection; + if( !Sim::findObject( argv[ 2 ], selection ) ) + { + Con::errorf( "WorldEditorSelection::subtract - no SimSet '%s'", argv[ 2 ] ); + return; + } + + const U32 numObjects = selection->size(); + for( U32 i = 0; i < numObjects; ++ i ) + object->removeObject( selection->at( i ) ); +} diff --git a/Engine/source/gui/worldEditor/worldEditorSelection.h b/Engine/source/gui/worldEditor/worldEditorSelection.h new file mode 100644 index 000000000..a2ff89c42 --- /dev/null +++ b/Engine/source/gui/worldEditor/worldEditorSelection.h @@ -0,0 +1,150 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _WORLDEDITORSELECTION_H_ +#define _WORLDEDITORSELECTION_H_ + +#ifndef _SIMPERSISTSET_H_ + #include "console/simPersistSet.h" +#endif +#ifndef _MPOINT3_H_ + #include "math/mPoint3.h" +#endif +#ifndef _MMATRIX_H_ + #include "math/mMatrix.h" +#endif +#ifndef _TVECTOR_H_ + #include "core/util/tVector.h" +#endif + + +class SceneObject; + + +/// A selection set in the World Editor. +/// +/// Selections are by default transient objects, but can be made persistent and +/// saved to disk. +/// +class WorldEditorSelection : public SimPersistSet +{ + public: + + typedef SimPersistSet Parent; + + private: + + /// The averaged positions of all objects in the selection. + Point3F mCentroid; + + /// The center point of the bounding box around the selection. + Point3F mBoxCentroid; + + /// The bounding box around the selection. + Box3F mBoxBounds; + + /// + MatrixF mTransform; + + /// If false, the selection has been modified and bounding box values + /// and center points need to be recomputed. + bool mCentroidValid; + + /// If true, the selection contains one or more objects that have + /// global bounds enabled. + bool mContainsGlobalBounds; + + bool mAutoSelect; + Point3F mPrevCentroid; + + void updateCentroid(); + + public: + + WorldEditorSelection(); + ~WorldEditorSelection(); + + /// Return true if "object" is contained in the selection. + bool objInSet( SimObject* object ); + + void storeCurrentCentroid() { mPrevCentroid = getCentroid(); } + bool hasCentroidChanged() { return (mPrevCentroid != getCentroid()); } + + bool containsGlobalBounds(); + + /// @name Transforms + /// + /// Note that these methods do not update transforms of client objects. + /// Use WorldEditor::updateClientTransforms to do that. + /// + /// @{ + + /// + const Point3F& getCentroid(); + const Point3F& getBoxCentroid(); + const Box3F& getBoxBounds(); + Point3F getBoxBottomCenter(); + const MatrixF& getTransform(); + + // + void offset(const Point3F& delta, F32 gridSnap = 0.f ); + void setPosition(const Point3F & pos); + void setCentroidPosition(bool useBoxCenter, const Point3F & pos); + + void orient(const MatrixF &, const Point3F &); + void rotate(const EulerF &); + void rotate(const EulerF &, const Point3F &); + void setRotate(const EulerF &); + + void scale(const VectorF &); + void scale(const VectorF &, const Point3F &); + void setScale(const VectorF &); + void setScale(const VectorF &, const Point3F &); + + void addSize(const VectorF &); + void setSize(const VectorF &); + + /// @} + + /// Enable collision for all objects in the selection. + void enableCollision(); + + /// Disable collision for all objects in the selection. + void disableCollision(); + + // + void setAutoSelect(bool b) { mAutoSelect = b; } + void invalidateCentroid() { mCentroidValid = false; } + + // SimSet. + virtual void addObject( SimObject* ); + virtual void removeObject( SimObject* ); + virtual void setCanSave( bool value ); + + static void initPersistFields(); + + DECLARE_CONOBJECT( WorldEditorSelection ); + DECLARE_CATEGORY( "Editor World" ); + DECLARE_DESCRIPTION( "A selection set for the World Editor." ); +}; + +#endif // !_WORLDEDITORSELECTION_H_ diff --git a/Engine/source/i18n/i18n.cpp b/Engine/source/i18n/i18n.cpp new file mode 100644 index 000000000..a5cfaf9de --- /dev/null +++ b/Engine/source/i18n/i18n.cpp @@ -0,0 +1,84 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/stream/stream.h" +#include "core/stream/fileStream.h" +#include "console/console.h" + +#include "i18n/i18n.h" +#include "i18n/lang.h" + +//----------------------------------------------------------------------------- +// Globals +//----------------------------------------------------------------------------- + +// [tom, 3/17/2005] Note: This is created in script +static LangTable *gCoreLangTable = NULL; + +// [tom, 3/17/2005] Defined in CoreStringsDefaults.cc, which is generated by langc +//extern const UTF8 *gI18NDefaultStrings[]; + +// [tom, 5/2/2005] Note: Temporary kludge to keep this compilable while +// the core localization isn't finished. +static const UTF8 *gI18NDefaultStrings[] = +{ + NULL +}; + +//----------------------------------------------------------------------------- + +const UTF8 *getCoreString(S32 id) +{ + if(gCoreLangTable) + return gCoreLangTable->getString(id); + else + return gI18NDefaultStrings[id]; +} + +//----------------------------------------------------------------------------- + +ConsoleFunction(getCoreLangTable, S32, 1, 1, "()" + "@brief Gets the primary LangTable used by the game\n\n" + "@return ID of the core LangTable\n" + "@ingroup Localization") +{ + if(gCoreLangTable) + return gCoreLangTable->getId(); + else + return 0; +} + +ConsoleFunction(setCoreLangTable, void, 2, 2, "(string LangTable)" + "@brief Sets the primary LangTable used by the game\n\n" + "@param LangTable ID of the core LangTable\n" + "@ingroup Localization") +{ + LangTable *lt; + + if(Sim::findObject(argv[1], lt)) + gCoreLangTable = lt; + else + Con::errorf("setCoreLangTable - Unable to find LanTable '%s'", argv[1]); +} + +//----------------------------------------------------------------------------- diff --git a/Engine/source/i18n/i18n.h b/Engine/source/i18n/i18n.h new file mode 100644 index 000000000..945cb04f3 --- /dev/null +++ b/Engine/source/i18n/i18n.h @@ -0,0 +1,39 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#if 0 + +// Core strings are out of date so this code is currently disabled. + +#include "i18n/CoreStrings.h" + +#ifndef _I18N_H_ +#define _I18N_H_ + +#define _L(x) (getCoreString(x)) + +// Protos +extern const UTF8 *getCoreString(S32 id); + +#endif // _I18N_H_ + +#endif \ No newline at end of file diff --git a/Engine/source/i18n/lang.cpp b/Engine/source/i18n/lang.cpp new file mode 100644 index 000000000..3f09e6b20 --- /dev/null +++ b/Engine/source/i18n/lang.cpp @@ -0,0 +1,481 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/stream/stream.h" +#include "core/stream/fileStream.h" +#include "console/console.h" +#include "console/consoleInternal.h" +#include "console/ast.h" +#include "console/compiler.h" +#include "core/util/safeDelete.h" +#include "console/engineAPI.h" + +#include "i18n/lang.h" + +//----------------------------------------------------------------------------- +// LangFile Class +//----------------------------------------------------------------------------- + +LangFile::LangFile(const UTF8 *langName /* = NULL */) +{ + VECTOR_SET_ASSOCIATION( mStringTable ); + + if(langName) + { + mLangName = new UTF8 [dStrlen(langName) + 1]; + dStrcpy(mLangName, langName); + } + else + mLangName = NULL; + + mLangFile = NULL; +} + +LangFile::~LangFile() +{ + // [tom, 3/1/2005] Note: If this is freed in FreeTable() then when the file + // is loaded, the language name will be blitzed. + // Programming after 36 hours without sleep != good. + + SAFE_DELETE_ARRAY(mLangName); + SAFE_DELETE_ARRAY(mLangFile); + freeTable(); +} + +void LangFile::freeTable() +{ + U32 i; + for(i = 0;i < mStringTable.size();i++) + { + if(mStringTable[i]) + delete [] mStringTable[i]; + } + mStringTable.clear(); +} + +bool LangFile::load(const UTF8 *filename) +{ + FileStream * stream; + if((stream = FileStream::createAndOpen( filename, Torque::FS::File::Read )) == NULL) + return false; + + bool ret = load(stream); + delete stream; + return ret; +} + +bool LangFile::load(Stream *s) +{ + freeTable(); + + while(s->getStatus() != Stream::EOS) + { + char buf[256]; + s->readString(buf); + addString((const UTF8*)buf); + } + return true; +} + +bool LangFile::save(const UTF8 *filename) +{ + FileStream *fs; + + if(!isLoaded()) + return false; + + if((fs = FileStream::createAndOpen( filename, Torque::FS::File::Write )) == NULL) + return false; + + bool ret = save(fs); + delete fs; + + return ret; +} + +bool LangFile::save(Stream *s) +{ + if(!isLoaded()) + return false; + + U32 i; + for(i = 0;i < mStringTable.size();i++) + { + s->writeString((char*)mStringTable[i]); + } + return true; +} + +const UTF8 * LangFile::getString(U32 id) +{ + if(id == LANG_INVALID_ID || id >= mStringTable.size()) + return NULL; + return mStringTable[id]; +} + +U32 LangFile::addString(const UTF8 *str) +{ + UTF8 *newstr = new UTF8 [dStrlen(str) + 1]; + dStrcpy(newstr, str); + mStringTable.push_back(newstr); + return mStringTable.size() - 1; +} + +void LangFile::setString(U32 id, const UTF8 *str) +{ + if(id >= mStringTable.size()) + { + U32 oldsize = mStringTable.size(); + mStringTable.setSize(id+1); + for( U32 i=oldsize; isetLangName(name); + + mLangTable.push_back(lang); + + if(mDefaultLang == -1) + setDefaultLanguage(mLangTable.size() - 1); + if(mCurrentLang == -1) + setCurrentLanguage(mLangTable.size() - 1); + + return mLangTable.size() - 1; +} + +S32 LangTable::addLanguage(const UTF8 *filename, const UTF8 *name /* = NULL */) +{ + LangFile * lang = new LangFile(name); + if(lang != NULL) + { + if(Torque::FS::IsFile(filename)) + { + lang->setLangFile(filename); + + S32 ret = addLanguage(lang); + if(ret >= 0) + return ret; + } + delete lang; + } + + return -1; +} + +const UTF8 *LangTable::getString(const U32 id) const +{ + const UTF8 *s = NULL; + + if(mCurrentLang >= 0) + s = mLangTable[mCurrentLang]->getString(id); + if(s == NULL && mDefaultLang >= 0 && mDefaultLang != mCurrentLang) + s = mLangTable[mDefaultLang]->getString(id); + + return s; +} + +const U32 LangTable::getStringLength(const U32 id) const +{ + const UTF8 *s = getString(id); + if(s) + return dStrlen(s); + + return 0; +} + +void LangTable::setDefaultLanguage(S32 langid) +{ + if(langid >= 0 && langid < mLangTable.size()) + { + if(mLangTable[langid]->activateLanguage()) + { + if(mDefaultLang >= 0) + mLangTable[mDefaultLang]->deactivateLanguage(); + + mDefaultLang = langid; + } + } +} + +void LangTable::setCurrentLanguage(S32 langid) +{ + if(langid >= 0 && langid < mLangTable.size()) + { + if(mLangTable[langid]->activateLanguage()) + { + Con::printf("Language %s [%s] activated.", mLangTable[langid]->getLangName(), mLangTable[langid]->getLangFile()); + + if(mCurrentLang >= 0 && mCurrentLang != mDefaultLang) + { + mLangTable[mCurrentLang]->deactivateLanguage(); + Con::printf("Language %s [%s] deactivated.", mLangTable[mCurrentLang]->getLangName(), mLangTable[mCurrentLang]->getLangFile()); + } + mCurrentLang = langid; + } + } +} + +//----------------------------------------------------------------------------- +// LangTable Console Methods +//----------------------------------------------------------------------------- + + + +ConsoleMethod(LangTable, addLanguage, S32, 3, 4, + "(string filename, [string languageName])" + "@brief Adds a language to the table\n\n" + "@param filename Name and path to the language file\n" + "@param languageName Optional name to assign to the new language entry\n\n" + "@return True If file was successfully found and language created\n" + ) +{ + UTF8 scriptFilenameBuffer[1024]; + + Con::expandScriptFilename((char*)scriptFilenameBuffer, sizeof(scriptFilenameBuffer), argv[2]); + return object->addLanguage(scriptFilenameBuffer, argc == 4 ? (const UTF8*)argv[3] : NULL); +} + +ConsoleMethod(LangTable, getString, const char *, 3, 3, + "(string filename)" + "@brief Grabs a string from the specified table\n\n" + "If an invalid is passed, the function will attempt to " + "to grab from the default table\n\n" + "@param filename Name of the language table to access\n\n" + "@return Text from the specified language table, \"\" if ID was invalid and default table is not set") +{ + const char * str = (const char*)object->getString(dAtoi(argv[2])); + if(str != NULL) + { + char * ret = Con::getReturnBuffer(dStrlen(str) + 1); + dStrcpy(ret, str); + return ret; + } + + return ""; +} + +ConsoleMethod(LangTable, setDefaultLanguage, void, 3, 3, "(int language)" + "@brief Sets the default language table\n\n" + "@param language ID of the table\n") +{ + object->setDefaultLanguage(dAtoi(argv[2])); +} + +ConsoleMethod(LangTable, setCurrentLanguage, void, 3, 3, + "(int language)" + "@brief Sets the current language table for grabbing text\n\n" + "@param language ID of the table\n") +{ + object->setCurrentLanguage(dAtoi(argv[2])); +} + +ConsoleMethod(LangTable, getCurrentLanguage, S32, 2, 2, "()" + "@brief Get the ID of the current language table\n\n" + "@return Numerical ID of the current language table") +{ + return object->getCurrentLanguage(); +} + +ConsoleMethod(LangTable, getLangName, const char *, 3, 3, "(int language)" + "@brief Return the readable name of the language table\n\n" + "@param language Numerical ID of the language table to access\n\n" + "@return String containing the name of the table, NULL if ID was invalid or name was never specified") +{ + const char * str = (const char*)object->getLangName(dAtoi(argv[2])); + if(str != NULL) + { + char * ret = Con::getReturnBuffer(dStrlen(str) + 1); + dStrcpy(ret, str); + return ret; + } + + return ""; +} + +ConsoleMethod(LangTable, getNumLang, S32, 2, 2, "()" + "@brief Used to find out how many languages are in the table\n\n" + "@return Size of the vector containing the languages, numerical") +{ + return object->getNumLang(); +} + +//----------------------------------------------------------------------------- +// Support Functions +//----------------------------------------------------------------------------- + +UTF8 *sanitiseVarName(const UTF8 *varName, UTF8 *buffer, U32 bufsize) +{ + if(! varName || bufsize < 10) // [tom, 3/3/2005] bufsize check gives room to be lazy below + { + *buffer = 0; + return NULL; + } + + dStrcpy(buffer, (const UTF8*)"I18N::"); + + UTF8 *dptr = buffer + 6; + const UTF8 *sptr = varName; + while(*sptr) + { + if(dIsalnum(*sptr)) + *dptr++ = *sptr++; + else + { + if(*(dptr - 1) != '_') + *dptr++ = '_'; + sptr++; + } + + if((dptr - buffer) >= (bufsize - 1)) + break; + } + *dptr = 0; + + return buffer; +} + +UTF8 *getCurrentModVarName(UTF8 *buffer, U32 bufsize) +{ + char varName[256]; + StringTableEntry cbName = CodeBlock::getCurrentCodeBlockName(); + + const UTF8 *slash = (const UTF8*)dStrchr(cbName, '/'); + if (slash == NULL) + { + Con::errorf("Illegal CodeBlock path detected in sanitiseVarName() (no mod directory): %s", cbName); + return NULL; + } + + dStrncpy(varName, cbName, slash - (const UTF8*)cbName); + varName[slash - (const UTF8*)cbName] = 0; + + return sanitiseVarName((UTF8*)varName, buffer, bufsize); +} + +const LangTable *getCurrentModLangTable() +{ + UTF8 saneVarName[256]; + + if(getCurrentModVarName(saneVarName, sizeof(saneVarName))) + { + const LangTable *lt = dynamic_cast(Sim::findObject(Con::getIntVariable((const char*)saneVarName))); + return lt; + } + return NULL; +} + +const LangTable *getModLangTable(const UTF8 *mod) +{ + UTF8 saneVarName[256]; + + if(sanitiseVarName(mod, saneVarName, sizeof(saneVarName))) + { + const LangTable *lt = dynamic_cast(Sim::findObject(Con::getIntVariable((const char*)saneVarName))); + return lt; + } + return NULL; +} diff --git a/Engine/source/i18n/lang.h b/Engine/source/i18n/lang.h new file mode 100644 index 000000000..edd031930 --- /dev/null +++ b/Engine/source/i18n/lang.h @@ -0,0 +1,120 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// \file lang.h +/// \brief Header for language support +//----------------------------------------------------------------------------- + +#include "console/simBase.h" +#include "core/util/tVector.h" + +#ifndef _LANG_H_ +#define _LANG_H_ + +#define LANG_INVALID_ID 0xffffffff ///!< Invalid ID. Used for returning failure + +//----------------------------------------------------------------------------- +/// \brief Class for working with language files +//----------------------------------------------------------------------------- +class LangFile +{ +protected: + Vector mStringTable; + UTF8 * mLangName; + UTF8 * mLangFile; + + void freeTable(); + +public: + LangFile(const UTF8 *langName = NULL); + virtual ~LangFile(); + + bool load(const UTF8 *filename); + bool save(const UTF8 *filename); + + bool load(Stream *s); + bool save(Stream *s); + + const UTF8 * getString(U32 id); + U32 addString(const UTF8 *str); + + // [tom, 4/22/2005] setString() added to help the language compiler a bit + void setString(U32 id, const UTF8 *str); + + void setLangName(const UTF8 *newName); + const UTF8 *getLangName(void) { return mLangName; } + const UTF8 *getLangFile(void) { return mLangFile; } + + void setLangFile(const UTF8 *langFile); + bool activateLanguage(void); + void deactivateLanguage(void); + + bool isLoaded(void) { return mStringTable.size() > 0; } + + S32 getNumStrings(void) { return mStringTable.size(); } +}; + +//----------------------------------------------------------------------------- +/// \brief Language file table +//----------------------------------------------------------------------------- +class LangTable : public SimObject +{ + typedef SimObject Parent; + +protected: + Vector mLangTable; + S32 mDefaultLang; + S32 mCurrentLang; + +public: + DECLARE_CONOBJECT(LangTable); + + LangTable(); + virtual ~LangTable(); + + S32 addLanguage(LangFile *lang, const UTF8 *name = NULL); + S32 addLanguage(const UTF8 *filename, const UTF8 *name = NULL); + + void setDefaultLanguage(S32 langid); + void setCurrentLanguage(S32 langid); + S32 getCurrentLanguage(void) { return mCurrentLang; } + + const UTF8 * getLangName(const S32 langid) const + { + if(langid < 0 || langid >= mLangTable.size()) + return NULL; + return mLangTable[langid]->getLangName(); + } + + const S32 getNumLang(void) const { return mLangTable.size(); } + + const UTF8 * getString(const U32 id) const; + const U32 getStringLength(const U32 id) const; +}; + +extern UTF8 *sanitiseVarName(const UTF8 *varName, UTF8 *buffer, U32 bufsize); +extern UTF8 *getCurrentModVarName(UTF8 *buffer, U32 bufsize); +extern const LangTable *getCurrentModLangTable(); +extern const LangTable *getModLangTable(const UTF8 *mod); + +#endif // _LANG_H_ diff --git a/Engine/source/interior/forceField.cpp b/Engine/source/interior/forceField.cpp new file mode 100644 index 000000000..1ef09ad57 --- /dev/null +++ b/Engine/source/interior/forceField.cpp @@ -0,0 +1,469 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "interior/forceField.h" + +#include "core/stream/stream.h" +#include "math/mathIO.h" +#include "console/console.h" +#include "collision/abstractPolyList.h" +#include "scene/sceneObject.h" + + +ForceField::ForceField() +{ + VECTOR_SET_ASSOCIATION( mTriggers ); + VECTOR_SET_ASSOCIATION( mPlanes ); + VECTOR_SET_ASSOCIATION( mPoints ); + VECTOR_SET_ASSOCIATION( mBSPNodes ); + VECTOR_SET_ASSOCIATION( mBSPSolidLeaves ); + VECTOR_SET_ASSOCIATION( mSolidLeafSurfaces ); + VECTOR_SET_ASSOCIATION( mWindings ); + VECTOR_SET_ASSOCIATION( mSurfaces ); + + mPreppedForRender = false; +} + +ForceField::~ForceField() +{ + mPreppedForRender = false; +} + + +bool ForceField::prepForRendering() +{ + if (mPreppedForRender == true) + return true; + + mPreppedForRender = true; + return true; +} + + +void ForceField::render(const ColorF& rColor, const F32 fade) +{ +} + + +//-------------------------------------------------------------------------- +//-------------------------------------- Persistence interfaces +// +const U32 ForceField::smFileVersion = 0; + +bool ForceField::read(Stream& stream) +{ + AssertFatal(stream.hasCapability(Stream::StreamRead), "ForceField::read: non-read capable stream passed"); + AssertFatal(stream.getStatus() == Stream::Ok, "ForceField::read: Error, stream in inconsistent state"); + + U32 i; + + // Version this stream + U32 fileVersion; + stream.read(&fileVersion); + if (fileVersion != smFileVersion) { + Con::errorf(ConsoleLogEntry::General, "ForceField::read: incompatible file version found."); + return false; + } + + mName = stream.readSTString(); + U32 numTriggers; + stream.read(&numTriggers); + mTriggers.setSize(numTriggers); + for (i = 0; i < mTriggers.size(); i++) + mTriggers[i] = stream.readSTString(); + + // Geometry factors... + mathRead(stream, &mBoundingBox); + mathRead(stream, &mBoundingSphere); + + // Now read in our data vectors. + U32 vectorSize; + // mPlanes + readPlaneVector(stream); + + // mPoints + stream.read(&vectorSize); + mPoints.setSize(vectorSize); + for (i = 0; i < mPoints.size(); i++) + mathRead(stream, &mPoints[i]); + + // mBSPNodes; + stream.read(&vectorSize); + mBSPNodes.setSize(vectorSize); + for (i = 0; i < mBSPNodes.size(); i++) { + stream.read(&mBSPNodes[i].planeIndex); + stream.read(&mBSPNodes[i].frontIndex); + stream.read(&mBSPNodes[i].backIndex); + } + + // mBSPSolidLeaves + stream.read(&vectorSize); + mBSPSolidLeaves.setSize(vectorSize); + for (i = 0; i < mBSPSolidLeaves.size(); i++) { + stream.read(&mBSPSolidLeaves[i].surfaceIndex); + stream.read(&mBSPSolidLeaves[i].surfaceCount); + } + + // mWindings + stream.read(&vectorSize); + mWindings.setSize(vectorSize); + for (i = 0; i < mWindings.size(); i++) { + stream.read(&mWindings[i]); + } + + // mSurfaces + stream.read(&vectorSize); + mSurfaces.setSize(vectorSize); + for (i = 0; i < mSurfaces.size(); i++) { + stream.read(&mSurfaces[i].windingStart); + stream.read(&mSurfaces[i].windingCount); + stream.read(&mSurfaces[i].planeIndex); + stream.read(&mSurfaces[i].surfaceFlags); + stream.read(&mSurfaces[i].fanMask); + } + + // mSolidLeafSurfaces + stream.read(&vectorSize); + mSolidLeafSurfaces.setSize(vectorSize); + for (i = 0; i < mSolidLeafSurfaces.size(); i++) { + stream.read(&mSolidLeafSurfaces[i]); + } + + stream.read(&mColor); + + return stream.getStatus() == Stream::Ok; +} + +bool ForceField::write(Stream& stream) const +{ + AssertFatal(stream.hasCapability(Stream::StreamWrite), "Interior::write: non-write capable stream passed"); + AssertFatal(stream.getStatus() == Stream::Ok, "Interior::write: Error, stream in inconsistent state"); + + U32 i; + + // Version this stream + stream.write(smFileVersion); + + stream.writeString(mName); + stream.write(mTriggers.size()); + for (i = 0; i < mTriggers.size(); i++) + stream.writeString(mTriggers[i]); + + mathWrite(stream, mBoundingBox); + mathWrite(stream, mBoundingSphere); + + // Now write out our data vectors. Remember, for cross-platform capability, no + // structure writing is allowed... + + // mPlanes + writePlaneVector(stream); + + // mPoints + stream.write(mPoints.size()); + for (i = 0; i < mPoints.size(); i++) + mathWrite(stream, mPoints[i]); + + // mBSPNodes; + stream.write(mBSPNodes.size()); + for (i = 0; i < mBSPNodes.size(); i++) { + stream.write(mBSPNodes[i].planeIndex); + stream.write(mBSPNodes[i].frontIndex); + stream.write(mBSPNodes[i].backIndex); + } + + // mBSPSolidLeaves + stream.write(mBSPSolidLeaves.size()); + for (i = 0; i < mBSPSolidLeaves.size(); i++) { + stream.write(mBSPSolidLeaves[i].surfaceIndex); + stream.write(mBSPSolidLeaves[i].surfaceCount); + } + + // mWindings + stream.write(mWindings.size()); + for (i = 0; i < mWindings.size(); i++) { + stream.write(mWindings[i]); + } + + // mSurfaces + stream.write(mSurfaces.size()); + for (i = 0; i < mSurfaces.size(); i++) { + stream.write(mSurfaces[i].windingStart); + stream.write(mSurfaces[i].windingCount); + stream.write(mSurfaces[i].planeIndex); + stream.write(mSurfaces[i].surfaceFlags); + stream.write(mSurfaces[i].fanMask); + } + + // mSolidLeafSurfaces + stream.write(mSolidLeafSurfaces.size()); + for (i = 0; i < mSolidLeafSurfaces.size(); i++) { + stream.write(mSolidLeafSurfaces[i]); + } + + stream.write(mColor); + + return stream.getStatus() == Stream::Ok; +} + +bool ForceField::writePlaneVector(Stream& stream) const +{ + // This is pretty slow, but who cares? + // + Vector uniqueNormals(mPlanes.size()); + Vector uniqueIndices(mPlanes.size()); + + U32 i; + + for (i = 0; i < mPlanes.size(); i++) { + bool inserted = false; + for (U32 j = 0; j < uniqueNormals.size(); j++) { + if (mPlanes[i] == uniqueNormals[j]) { + // Hah! Already have this one... + uniqueIndices.push_back(j); + inserted = true; + break; + } + } + + if (inserted == false) { + // Gotta do it ourselves... + uniqueIndices.push_back(uniqueNormals.size()); + uniqueNormals.push_back(Point3F(mPlanes[i].x, mPlanes[i].y, mPlanes[i].z)); + } + } + + // Ok, what we have now, is a list of unique normals, a set of indices into + // that vector, and the distances that we still have to write out by hand. + // Hop to it! + stream.write(uniqueNormals.size()); + for (i = 0; i < uniqueNormals.size(); i++) + mathWrite(stream, uniqueNormals[i]); + + stream.write(mPlanes.size()); + for (i = 0; i < mPlanes.size(); i++) { + stream.write(uniqueIndices[i]); + stream.write(mPlanes[i].d); + } + + return (stream.getStatus() == Stream::Ok); +} + +bool ForceField::readPlaneVector(Stream& stream) +{ + Vector normals; + U32 vectorSize; + + stream.read(&vectorSize); + normals.setSize(vectorSize); + U32 i; + for (i = 0; i < normals.size(); i++) + mathRead(stream, &normals[i]); + + U16 index; + stream.read(&vectorSize); + mPlanes.setSize(vectorSize); + for (i = 0; i < mPlanes.size(); i++) { + stream.read(&index); + stream.read(&mPlanes[i].d); + mPlanes[i].x = normals[index].x; + mPlanes[i].y = normals[index].y; + mPlanes[i].z = normals[index].z; + } + + return (stream.getStatus() == Stream::Ok); +} + + +//-------------------------------------------------------------------------- +//-------------------------------------- Collision support. Essentially +// copied from the interiorCollision +// +void ForceField::collisionFanFromSurface(const Surface& rSurface, U32* fanIndices, U32* numIndices) const +{ + U32 tempIndices[32]; + + tempIndices[0] = 0; + U32 idx = 1; + U32 i; + for (i = 1; i < rSurface.windingCount; i += 2) + tempIndices[idx++] = i; + for (i = ((rSurface.windingCount - 1) & (~0x1)); i > 0; i -= 2) + tempIndices[idx++] = i; + + idx = 0; + for (i = 0; i < rSurface.windingCount; i++) { + if (rSurface.fanMask & (1 << i)) { + fanIndices[idx++] = mWindings[rSurface.windingStart + tempIndices[i]]; + } + } + *numIndices = idx; +} + +bool ForceField::castRay(const Point3F& s, const Point3F& e, RayInfo* info) +{ + bool hit = castRay_r(0, s, e, info); + if (hit) { + Point3F vec = e - s; + F32 len = vec.len(); + vec /= len; + info->t = mDot(info->point - s, vec) / len; + } + + return hit; +} + +bool ForceField::castRay_r(const U16 node, + const Point3F& s, + const Point3F& e, + RayInfo* info) +{ + if (isBSPLeafIndex(node) == false) { + const IBSPNode& rNode = mBSPNodes[node]; + const PlaneF& rPlane = getPlane(rNode.planeIndex); + + PlaneF::Side sSide = rPlane.whichSide(s); + PlaneF::Side eSide = rPlane.whichSide(e); + + switch (PlaneSwitchCode(sSide, eSide)) { + case PlaneSwitchCode(PlaneF::Front, PlaneF::Front): + case PlaneSwitchCode(PlaneF::Front, PlaneF::On): + case PlaneSwitchCode(PlaneF::On, PlaneF::Front): + return castRay_r(rNode.frontIndex, s, e, info); + break; + + case PlaneSwitchCode(PlaneF::On, PlaneF::Back): + case PlaneSwitchCode(PlaneF::Back, PlaneF::On): + case PlaneSwitchCode(PlaneF::Back, PlaneF::Back): + return castRay_r(rNode.backIndex, s, e, info); + break; + + case PlaneSwitchCode(PlaneF::On, PlaneF::On): + // Line lies on the plane + if (isBSPLeafIndex(rNode.backIndex) == false) { + if (castRay_r(rNode.backIndex, s, e, info)) + return true; + } + if (isBSPLeafIndex(rNode.frontIndex) == false) { + if (castRay_r(rNode.frontIndex, s, e, info)) + return true; + } + return false; + break; + + case PlaneSwitchCode(PlaneF::Front, PlaneF::Back): { + Point3F ip; + F32 intersectT = rPlane.intersect(s, e); + AssertFatal(intersectT != PARALLEL_PLANE, "Error, this should never happen in this case!"); + ip.interpolate(s, e, intersectT); + if (castRay_r(rNode.frontIndex, s, ip, info)) + return true; + return castRay_r(rNode.backIndex, ip, e, info); + } + break; + + case PlaneSwitchCode(PlaneF::Back, PlaneF::Front): { + Point3F ip; + F32 intersectT = rPlane.intersect(s, e); + AssertFatal(intersectT != PARALLEL_PLANE, "Error, this should never happen in this case!"); + ip.interpolate(s, e, intersectT); + if (castRay_r(rNode.backIndex, s, ip, info)) + return true; + return castRay_r(rNode.frontIndex, ip, e, info); + } + break; + + default: + AssertFatal(false, "Misunderstood switchCode in ForceField::castRay_r"); + return false; + } + } + + if (isBSPSolidLeaf(node)) { + // DMM: Set material info here + info->point = s; + return true; + } + return false; +} + +void ForceField::buildPolyList_r(const U16 node, Vector& collPlanes, AbstractPolyList* list, SphereF& s) +{ + if (isBSPLeafIndex(node) == false) { + const IBSPNode& rNode = mBSPNodes[node]; + const PlaneF& rPlane = getPlane(rNode.planeIndex); + + F32 dist = rPlane.distToPlane(s.center); + if (mFabs(dist) <= s.radius) { + // Have to do both, and push the plane back on the list... + collPlanes.push_back(rNode.planeIndex); + buildPolyList_r(rNode.frontIndex, collPlanes, list, s); + buildPolyList_r(rNode.backIndex, collPlanes, list, s); + collPlanes.pop_back(); + } else if (dist > 0.0f) { + buildPolyList_r(rNode.frontIndex, collPlanes, list, s); + } else { + buildPolyList_r(rNode.backIndex, collPlanes, list, s); + } + return; + } + + if (isBSPSolidLeaf(node)) { + const IBSPLeafSolid& rLeaf = mBSPSolidLeaves[getBSPSolidLeafIndex(node)]; + for (U32 i = 0; i < rLeaf.surfaceCount; i++) { + U32 surfaceIndex = mSolidLeafSurfaces[rLeaf.surfaceIndex + i]; + const Surface& rSurface = mSurfaces[surfaceIndex]; + for (U32 j = 0; j < collPlanes.size(); j++) { + if (areEqualPlanes(rSurface.planeIndex, collPlanes[j]) == true) { + + U32 fanVerts[32]; + U32 numVerts; + collisionFanFromSurface(rSurface, fanVerts, &numVerts); + + // DMM: Material here + list->begin(0, rSurface.planeIndex); + + U32 vertStart = list->addPoint(mPoints[fanVerts[0]]); + list->vertex(vertStart); + for (U32 k = 1; k < numVerts; k++) { + list->addPoint(mPoints[fanVerts[k]]); + list->vertex(vertStart + k); + } + list->plane(vertStart, vertStart + 1, vertStart + 2); + list->end(); + + break; + } + } + + } + } +} + +bool ForceField::buildPolyList(AbstractPolyList* list, SphereF& sphere) +{ + Vector planes; + buildPolyList_r(0, planes, list, sphere); + AssertFatal(planes.size() == 0, "Error, unbalanced plane stack!"); + + return !list->isEmpty(); +} diff --git a/Engine/source/interior/forceField.h b/Engine/source/interior/forceField.h new file mode 100644 index 000000000..b849df01d --- /dev/null +++ b/Engine/source/interior/forceField.h @@ -0,0 +1,195 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FORCEFIELD_H_ +#define _FORCEFIELD_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _MBOX_H_ +#include "math/mBox.h" +#endif +#ifndef _MSPHERE_H_ +#include "math/mSphere.h" +#endif +#ifndef _MPLANE_H_ +#include "math/mPlane.h" +#endif +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _COLOR_H_ +#include "core/color.h" +#endif + +//-------------------------------------- forward decls. +class EditGeometry; +class InteriorInstance; +class Stream; +class AbstractPolyList; +struct RayInfo; + +//-------------------------------------------------------------------------- +class ForceField +{ + static const U32 smFileVersion; + friend class EditGeometry; + friend class InteriorInstance; + + //-------------------------------------- Public interfaces + public: + ForceField(); + ~ForceField(); + + bool prepForRendering(); + void render(const ColorF& color, const F32 fadeLevel); + const Box3F& getBoundingBox() const; + + bool castRay(const Point3F&, const Point3F&, RayInfo*); + bool buildPolyList(AbstractPolyList*, SphereF&); + //-------------------------------------- Persistence interface + public: + bool read(Stream& stream); + bool write(Stream& stream) const; + + public: + static U16 getPlaneIndex(U16 index); + static bool planeIsFlipped(U16 index); + + //-------------------------------------- BSP Structures + private: + struct IBSPNode { + U16 planeIndex; + U16 frontIndex; + U16 backIndex; + U16 __padding__; + }; + struct IBSPLeafSolid { + U32 surfaceIndex; + U16 surfaceCount; + U16 __padding__; + }; + + bool isBSPLeafIndex(U16 index) const; + bool isBSPSolidLeaf(U16 index) const; + bool isBSPEmptyLeaf(U16 index) const; + U16 getBSPSolidLeafIndex(U16 index) const; + + bool writePlaneVector(Stream&) const; + bool readPlaneVector(Stream&); + + private: + const PlaneF& getPlane(U16 index) const; + bool areEqualPlanes(U16, U16) const; + + struct Surface { + U32 windingStart; + U32 fanMask; + + U16 planeIndex; + U8 windingCount; + U8 surfaceFlags; + }; + + protected: + StringTableEntry mName; + ColorF mColor; + Vector mTriggers; + + Box3F mBoundingBox; + SphereF mBoundingSphere; + Vector mPlanes; + Vector mPoints; + + Vector mBSPNodes; + Vector mBSPSolidLeaves; + Vector mSolidLeafSurfaces; + + bool mPreppedForRender; + + Vector mWindings; + Vector mSurfaces; + + protected: + bool castRay_r(const U16, const Point3F&, const Point3F&, RayInfo*); + void buildPolyList_r(const U16, Vector&, AbstractPolyList*, SphereF&); + void collisionFanFromSurface(const Surface&, U32* fan, U32* numIndices) const; +}; + +//------------------------------------------------------------------------------ +inline bool ForceField::isBSPLeafIndex(U16 index) const +{ + return (index & 0x8000) != 0; +} + +inline bool ForceField::isBSPSolidLeaf(U16 index) const +{ + AssertFatal(isBSPLeafIndex(index) == true, "Error, only call for leaves!"); + return (index & 0x4000) != 0; +} + +inline bool ForceField::isBSPEmptyLeaf(U16 index) const +{ + AssertFatal(isBSPLeafIndex(index) == true, "Error, only call for leaves!"); + return (index & 0x4000) == 0; +} + +inline U16 ForceField::getBSPSolidLeafIndex(U16 index) const +{ + AssertFatal(isBSPSolidLeaf(index) == true, "Error, only call for leaves!"); + return (index & ~0xC000); +} + +inline const PlaneF& ForceField::getPlane(U16 index) const +{ + AssertFatal(U32(index & ~0x8000) < mPlanes.size(), + "ForceField::getPlane: planeIndex out of range"); + + return mPlanes[index & ~0x8000]; +} + +inline U16 ForceField::getPlaneIndex(U16 index) +{ + return index & ~0x8000; +} + +inline bool ForceField::planeIsFlipped(U16 index) +{ + return (index & 0x8000) != 0; +} + +inline bool ForceField::areEqualPlanes(U16 o, U16 t) const +{ + return (o & ~0x8000) == (t & ~0x8000); +} + +inline const Box3F& ForceField::getBoundingBox() const +{ + return mBoundingBox; +} + +#endif // _H_FORCEFIELD_ + diff --git a/Engine/source/interior/interior.cpp b/Engine/source/interior/interior.cpp new file mode 100644 index 000000000..2a722c31c --- /dev/null +++ b/Engine/source/interior/interior.cpp @@ -0,0 +1,2645 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "interior/interior.h" + +#include "scene/sceneRenderState.h" +#include "scene/sceneManager.h" +#include "gfx/bitmap/gBitmap.h" +#include "math/mMatrix.h" +#include "math/mRect.h" +#include "core/bitVector.h" +#include "core/frameAllocator.h" +#include "scene/sgUtil.h" +#include "platform/profiler.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxTextureHandle.h" +#include "materials/materialList.h" +#include "materials/matInstance.h" +#include "materials/materialManager.h" +#include "renderInstance/renderPassManager.h" +#include "materials/processedMaterial.h" +#include "materials/materialFeatureTypes.h" + +U32 Interior::smRenderMode = 0; +bool Interior::smFocusedDebug = false; +bool Interior::smUseVertexLighting = false; +bool Interior::smLightingCastRays = false; +bool Interior::smLightingBuildPolyList = false; + +// These are setup by setupActivePolyList +U16* sgActivePolyList = NULL; +U32 sgActivePolyListSize = 0; +U16* sgEnvironPolyList = NULL; +U32 sgEnvironPolyListSize = 0; +U16* sgFogPolyList = NULL; +U32 sgFogPolyListSize = 0; +bool sgFogActive = false; + +// Always the same size as the mPoints array +Point2F* sgFogTexCoords = NULL; + +class PlaneRange +{ +public: + U32 start; + U32 count; +}; + +namespace { + +struct PortalRenderInfo +{ + bool render; + + F64 frustum[4]; + RectI viewport; +}; + +//-------------------------------------- Rendering state variables. +Point3F sgCamPoint; +F64 sgStoredFrustum[6]; +RectI sgStoredViewport; + +Vector sgZoneRenderInfo(__FILE__, __LINE__); + +// Takes OS coords to clip space... +MatrixF sgWSToOSMatrix; +MatrixF sgProjMatrix; + +PlaneF sgOSPlaneFar; +PlaneF sgOSPlaneXMin; +PlaneF sgOSPlaneXMax; +PlaneF sgOSPlaneYMin; +PlaneF sgOSPlaneYMax; + +struct ZoneRect { + RectD rect; + bool active; +}; + +Vector sgZoneRects(__FILE__, __LINE__); + +//-------------------------------------- Little utility functions +RectD outlineRects(const Vector& rects) +{ + F64 minx = 1e10; + F64 maxx = -1e10; + F64 miny = 1e10; + F64 maxy = -1e10; + + for (S32 i = 0; i < rects.size(); i++) + { + if (rects[i].point.x < minx) + minx = rects[i].point.x; + if (rects[i].point.y < miny) + miny = rects[i].point.y; + + if (rects[i].point.x + rects[i].extent.x > maxx) + maxx = rects[i].point.x + rects[i].extent.x; + if (rects[i].point.y + rects[i].extent.y > maxy) + maxy = rects[i].point.y + rects[i].extent.y; + } + + return RectD(minx, miny, maxx - minx, maxy - miny); +} + +void insertZoneRects(ZoneRect& rZoneRect, const RectD* rects, const U32 numRects) +{ + F64 minx = 1e10; + F64 maxx = -1e10; + F64 miny = 1e10; + F64 maxy = -1e10; + + for (U32 i = 0; i < numRects; i++) { + if (rects[i].point.x < minx) + minx = rects[i].point.x; + if (rects[i].point.y < miny) + miny = rects[i].point.y; + + if (rects[i].point.x + rects[i].extent.x > maxx) + maxx = rects[i].point.x + rects[i].extent.x; + if (rects[i].point.y + rects[i].extent.y > maxy) + maxy = rects[i].point.y + rects[i].extent.y; + } + + if (rZoneRect.active == false && numRects != 0) { + rZoneRect.rect = RectD(minx, miny, maxx - minx, maxy - miny); + rZoneRect.active = true; + } else { + if (rZoneRect.rect.point.x < minx) + minx = rZoneRect.rect.point.x; + if (rZoneRect.rect.point.y < miny) + miny = rZoneRect.rect.point.y; + + if (rZoneRect.rect.point.x + rZoneRect.rect.extent.x > maxx) + maxx = rZoneRect.rect.point.x + rZoneRect.rect.extent.x; + if (rZoneRect.rect.point.y + rZoneRect.rect.extent.y > maxy) + maxy = rZoneRect.rect.point.y + rZoneRect.rect.extent.y; + + rZoneRect.rect = RectD(minx, miny, maxx - minx, maxy - miny); + } +} + + + +void fixupViewport(PortalRenderInfo& rInfo) +{ + F64 widthV = rInfo.frustum[1] - rInfo.frustum[0]; + F64 heightV = rInfo.frustum[3] - rInfo.frustum[2]; + + F64 fx0 = (rInfo.frustum[0] - sgStoredFrustum[0]) / (sgStoredFrustum[1] - sgStoredFrustum[0]); + F64 fx1 = (sgStoredFrustum[1] - rInfo.frustum[1]) / (sgStoredFrustum[1] - sgStoredFrustum[0]); + + F64 dV0 = F64(sgStoredViewport.point.x) + fx0 * F64(sgStoredViewport.extent.x); + F64 dV1 = F64(sgStoredViewport.point.x + + sgStoredViewport.extent.x) - fx1 * F64(sgStoredViewport.extent.x); + + F64 fdV0 = getMax(mFloorD(dV0), F64(sgStoredViewport.point.x)); + F64 cdV1 = getMin(mCeilD(dV1), F64(sgStoredViewport.point.x + sgStoredViewport.extent.x)); + + // If the width is 1 pixel, we need to widen it up a bit... + if ((cdV1 - fdV0) <= 1.0) + cdV1 = fdV0 + 1; + AssertFatal((fdV0 >= sgStoredViewport.point.x && + cdV1 <= sgStoredViewport.point.x + sgStoredViewport.extent.x), + "Out of bounds viewport bounds"); + + F64 new0 = rInfo.frustum[0] - ((dV0 - fdV0) * (widthV / F64(sgStoredViewport.extent.x))); + F64 new1 = rInfo.frustum[1] + ((cdV1 - dV1) * (widthV / F64(sgStoredViewport.extent.x))); + + rInfo.frustum[0] = new0; + rInfo.frustum[1] = new1; + + rInfo.viewport.point.x = S32(fdV0); + rInfo.viewport.extent.x = S32(cdV1) - rInfo.viewport.point.x; + + F64 fy0 = (sgStoredFrustum[3] - rInfo.frustum[3]) / (sgStoredFrustum[3] - sgStoredFrustum[2]); + F64 fy1 = (rInfo.frustum[2] - sgStoredFrustum[2]) / (sgStoredFrustum[3] - sgStoredFrustum[2]); + + dV0 = F64(sgStoredViewport.point.y) + fy0 * F64(sgStoredViewport.extent.y); + dV1 = F64(sgStoredViewport.point.y + + sgStoredViewport.extent.y) - fy1 * F64(sgStoredViewport.extent.y); + fdV0 = getMax(mFloorD(dV0), F64(sgStoredViewport.point.y)); + cdV1 = getMin(mCeilD(dV1), F64(sgStoredViewport.point.y + sgStoredViewport.extent.y)); + + // If the width is 1 pixel, we need to widen it up a bit... + if ((cdV1 - fdV0) <= 1.0) + cdV1 = fdV0 + 1; + // GFX2_RENDER_MERGE + // Need to fix this properly but for now *HACK* +#ifndef TORQUE_OS_MAC + AssertFatal((fdV0 >= sgStoredViewport.point.y && + cdV1 <= sgStoredViewport.point.y + sgStoredViewport.extent.y), + "Out of bounds viewport bounds"); +#endif + + new0 = rInfo.frustum[2] - ((cdV1 - dV1) * (heightV / F64(sgStoredViewport.extent.y))); + new1 = rInfo.frustum[3] + ((dV0 - fdV0) * (heightV / F64(sgStoredViewport.extent.y))); + rInfo.frustum[2] = new0; + rInfo.frustum[3] = new1; + + rInfo.viewport.point.y = S32(fdV0); + rInfo.viewport.extent.y = S32(cdV1) - rInfo.viewport.point.y; +} + +RectD convertToRectD(const F64 inResult[4]) +{ + F64 minx = ((inResult[0] + 1.0f) / 2.0f) * (sgStoredFrustum[1] - sgStoredFrustum[0]) + sgStoredFrustum[0]; + F64 maxx = ((inResult[2] + 1.0f) / 2.0f) * (sgStoredFrustum[1] - sgStoredFrustum[0]) + sgStoredFrustum[0]; + + F64 miny = ((inResult[1] + 1.0f) / 2.0f) * (sgStoredFrustum[3] - sgStoredFrustum[2]) + sgStoredFrustum[2]; + F64 maxy = ((inResult[3] + 1.0f) / 2.0f) * (sgStoredFrustum[3] - sgStoredFrustum[2]) + sgStoredFrustum[2]; + + return RectD(minx, miny, (maxx - minx), (maxy - miny)); +} + +void convertToFrustum(PortalRenderInfo& zrInfo, const RectD& finalRect) +{ + zrInfo.frustum[0] = finalRect.point.x; // left + zrInfo.frustum[1] = finalRect.point.x + finalRect.extent.x; // right + zrInfo.frustum[2] = finalRect.point.y; // bottom + zrInfo.frustum[3] = finalRect.point.y + finalRect.extent.y; // top + + fixupViewport(zrInfo); +} + +} // namespace {} + + +//------------------------------------------------------------------------------ +//-------------------------------------- IMPLEMENTATION +// +Interior::Interior() +{ + mMaterialList = NULL; + + mHasTranslucentMaterials = false; + + mLMHandle = LM_HANDLE(-1); + + // By default, no alarm state, no animated light states + mHasAlarmState = false; + + mNumLightStateEntries = 0; + mNumTriggerableLights = 0; + + mPreppedForRender = false;; + mSearchTag = 0; + + mLightMapBorderSize = 0; + +#ifndef TORQUE_SHIPPING + mDebugShader = NULL; + + mDebugShaderModelViewSC = NULL; + mDebugShaderShadeColorSC = NULL; +#endif + + // Bind our vectors + VECTOR_SET_ASSOCIATION(mPlanes); + VECTOR_SET_ASSOCIATION(mPoints); + VECTOR_SET_ASSOCIATION(mBSPNodes); + VECTOR_SET_ASSOCIATION(mBSPSolidLeaves); + VECTOR_SET_ASSOCIATION(mWindings); + VECTOR_SET_ASSOCIATION(mTexGenEQs); + VECTOR_SET_ASSOCIATION(mLMTexGenEQs); + VECTOR_SET_ASSOCIATION(mWindingIndices); + VECTOR_SET_ASSOCIATION(mSurfaces); + VECTOR_SET_ASSOCIATION(mNullSurfaces); + VECTOR_SET_ASSOCIATION(mSolidLeafSurfaces); + VECTOR_SET_ASSOCIATION(mZones); + VECTOR_SET_ASSOCIATION(mZonePlanes); + VECTOR_SET_ASSOCIATION(mZoneSurfaces); + VECTOR_SET_ASSOCIATION(mZonePortalList); + VECTOR_SET_ASSOCIATION(mPortals); + //VECTOR_SET_ASSOCIATION(mSubObjects); + VECTOR_SET_ASSOCIATION(mLightmaps); + VECTOR_SET_ASSOCIATION(mLightmapKeep); + VECTOR_SET_ASSOCIATION(mNormalLMapIndices); + VECTOR_SET_ASSOCIATION(mAlarmLMapIndices); + VECTOR_SET_ASSOCIATION(mAnimatedLights); + VECTOR_SET_ASSOCIATION(mLightStates); + VECTOR_SET_ASSOCIATION(mStateData); + VECTOR_SET_ASSOCIATION(mStateDataBuffer); + VECTOR_SET_ASSOCIATION(mNameBuffer); + VECTOR_SET_ASSOCIATION(mConvexHulls); + VECTOR_SET_ASSOCIATION(mConvexHullEmitStrings); + VECTOR_SET_ASSOCIATION(mHullIndices); + VECTOR_SET_ASSOCIATION(mHullEmitStringIndices); + VECTOR_SET_ASSOCIATION(mHullSurfaceIndices); + VECTOR_SET_ASSOCIATION(mHullPlaneIndices); + VECTOR_SET_ASSOCIATION(mPolyListPlanes); + VECTOR_SET_ASSOCIATION(mPolyListPoints); + VECTOR_SET_ASSOCIATION(mPolyListStrings); + VECTOR_SET_ASSOCIATION(mCoordBinIndices); + + VECTOR_SET_ASSOCIATION(mVehicleConvexHulls); + VECTOR_SET_ASSOCIATION(mVehicleConvexHullEmitStrings); + VECTOR_SET_ASSOCIATION(mVehicleHullIndices); + VECTOR_SET_ASSOCIATION(mVehicleHullEmitStringIndices); + VECTOR_SET_ASSOCIATION(mVehicleHullSurfaceIndices); + VECTOR_SET_ASSOCIATION(mVehicleHullPlaneIndices); + VECTOR_SET_ASSOCIATION(mVehiclePolyListPlanes); + VECTOR_SET_ASSOCIATION(mVehiclePolyListPoints); + VECTOR_SET_ASSOCIATION(mVehiclePolyListStrings); + VECTOR_SET_ASSOCIATION(mVehiclePoints); + VECTOR_SET_ASSOCIATION(mVehicleNullSurfaces); + VECTOR_SET_ASSOCIATION(mVehiclePlanes); +} + +Interior::~Interior() +{ + U32 i; + delete mMaterialList; + mMaterialList = NULL; + + if(mLMHandle != LM_HANDLE(-1)) + gInteriorLMManager.removeInterior(mLMHandle); + + for(i = 0; i < mLightmaps.size(); i++) + { + delete mLightmaps[i]; + mLightmaps[i] = NULL; + } + + for(i = 0; i < mMatInstCleanupList.size(); i++) + { + delete mMatInstCleanupList[i]; + mMatInstCleanupList[i] = NULL; + } + + for(S32 i=0; i &matNames = mMaterialList->getMaterialNameList(); + Vector originalNames = matNames; + + for (U32 i = 0; i < matNames.size(); i++) + { + if (matNames[i].equal("NULL", String::NoCase) || + matNames[i].equal("ORIGIN", String::NoCase) || + matNames[i].equal("TRIGGER", String::NoCase) || + matNames[i].equal("FORCEFIELD", String::NoCase) || + matNames[i].equal("EMITTER", String::NoCase) ) + { + mMaterialList->setMaterialName(i, String()); + } + } + + String relPath = Platform::makeRelativePathName(path, Platform::getCurrentDirectory()); + + // Load the material list + mMaterialList->setTextureLookupPath(relPath); + mMaterialList->mapMaterials(); + + // Grab all the static meshes and load any textures that didn't originate + // from inside the DIF. + for(S32 i=0; imaterialList->setTextureLookupPath(relPath); + + // Now restore the material names since someone later may + // count on the special texture names being present. + for (U32 i = 0; i < originalNames.size(); i++) + mMaterialList->setMaterialName(i, originalNames[i]); + + + fillSurfaceTexMats(); + createZoneVBs(); + cloneMatInstances(); + createReflectPlanes(); + initMatInstances(); + + // lightmap manager steals the lightmaps here... + gInteriorLMManager.addInterior(mLMHandle, mLightmaps.size(), this); + AssertFatal(!mLightmaps.size(), "Failed to process lightmaps"); + + for(U32 i=0; iprepForRendering(relPath); + + GFXStateBlockDesc sh; + +#ifndef TORQUE_SHIPPING + // First create a default state block with + // texturing turned off + mInteriorDebugNoneSB = GFX->createStateBlock(sh); + + // Create a state block for portal rendering that + // doesn't have backface culling enabled + sh.cullDefined = true; + sh.cullMode = GFXCullNone; + + mInteriorDebugPortalSB = GFX->createStateBlock(sh); + + // Reset our cull mode to the default + sh.cullMode = GFXCullCCW; +#endif + + // Next turn on the first texture channel + sh.samplersDefined = true; + sh.samplers[0].textureColorOp = GFXTOPModulate; + +#ifndef TORQUE_SHIPPING + mInteriorDebugTextureSB = GFX->createStateBlock(sh); + + sh.samplers[1].textureColorOp = GFXTOPModulate; + + mInteriorDebugTwoTextureSB = GFX->createStateBlock(sh); +#endif + + // Lastly create a standard rendering state block + sh.samplers[2].textureColorOp = GFXTOPModulate; + + sh.samplers[0].magFilter = GFXTextureFilterLinear; + sh.samplers[0].minFilter = GFXTextureFilterLinear; + + mInteriorSB = GFX->createStateBlock(sh); + + mPreppedForRender = true; + + return true; +} + + +void Interior::setupAveTexGenLength() +{ +/* + F32 len = 0; + for (U32 i = 0; i < mSurfaces.size(); i++) + { + // We're going to assume that most textures don't have separate scales for + // x and y... + F32 lenx = mTexGenEQs[mSurfaces[i].texGenIndex].planeX.len(); + len += F32((*mMaterialList)[mSurfaces[i].textureIndex].getWidth()) * lenx; + } + len /= F32(mSurfaces.size()); + mAveTexGenLength = len; +*/ +} + + +//-------------------------------------------------------------------------- +bool Interior::traverseZones(SceneCullingState* state, + const Frustum& frustum, + S32 containingZone, + S32 baseZone, + U32 zoneOffset, + const MatrixF& OSToWS, + const Point3F& objScale, + const bool dontRestrictOutside, + const bool flipClipPlanes, + Frustum& outFrustum) +{ + // Store off the viewport and frustum + sgStoredViewport = state->getCameraState().getViewport(); + if( dontRestrictOutside ) + { + sgStoredFrustum[0] = state->getFrustum().getNearLeft(); + sgStoredFrustum[1] = state->getFrustum().getNearRight(); + sgStoredFrustum[2] = state->getFrustum().getNearBottom(); + sgStoredFrustum[3] = state->getFrustum().getNearTop(); + sgStoredFrustum[4] = state->getFrustum().getNearDist(); + sgStoredFrustum[5] = state->getFrustum().getFarDist(); + } + else + { + sgStoredFrustum[0] = frustum.getNearLeft(); + sgStoredFrustum[1] = frustum.getNearRight(); + sgStoredFrustum[2] = frustum.getNearBottom(); + sgStoredFrustum[3] = frustum.getNearTop(); + sgStoredFrustum[4] = frustum.getNearDist(); + sgStoredFrustum[5] = frustum.getFarDist(); + } + + sgProjMatrix = state->getCameraState().getProjectionMatrix(); + + MatrixF finalModelView = state->getCameraState().getWorldViewMatrix(); + finalModelView.mul(OSToWS); + finalModelView.scale(Point3F(objScale.x, objScale.y, objScale.z)); + sgProjMatrix.mul(finalModelView); + + finalModelView.inverse(); + finalModelView.mulP(Point3F(0, 0, 0), &sgCamPoint); + sgWSToOSMatrix = finalModelView; + + // do the zone traversal + sgZoneRenderInfo.setSize(mZones.size()); + zoneTraversal(baseZone, flipClipPlanes); + + // Copy out the information for all zones but the outside zone. + for(U32 i = 1; i < mZones.size(); i++) + { + AssertFatal(zoneOffset != 0xFFFFFFFF, "Error, this should never happen!"); + U32 globalIndex = i + zoneOffset - 1; + + if( sgZoneRenderInfo[ i ].render ) + { + state->addCullingVolumeToZone( + globalIndex, + SceneCullingVolume::Includer, + Frustum( + frustum.isOrtho(), + sgZoneRenderInfo[ i ].frustum[ 0 ], + sgZoneRenderInfo[ i ].frustum[ 1 ], + sgZoneRenderInfo[ i ].frustum[ 3 ], + sgZoneRenderInfo[ i ].frustum[ 2 ], + frustum.getNearDist(), + frustum.getFarDist(), + frustum.getTransform() + ) + ); + } + } + + destroyZoneRectVectors(); + + // If zone 0 is rendered, then we return true... + bool continueOut = sgZoneRenderInfo[ 0 ].render; + if( continueOut ) + outFrustum = Frustum( + frustum.isOrtho(), + sgZoneRenderInfo[ 0 ].frustum[ 0 ], + sgZoneRenderInfo[ 0 ].frustum[ 1 ], + sgZoneRenderInfo[ 0 ].frustum[ 3 ], + sgZoneRenderInfo[ 0 ].frustum[ 2 ], + frustum.getNearDist(), + frustum.getFarDist(), + frustum.getTransform() + ); + + return sgZoneRenderInfo[0].render; +} + + +//------------------------------------------------------------------------------ +S32 Interior::getZoneForPoint(const Point3F& rPoint) const +{ + const IBSPNode* pNode = &mBSPNodes[0]; + + while (true) { + F32 dist = getPlane(pNode->planeIndex).distToPlane(rPoint); + if (planeIsFlipped(pNode->planeIndex)) + dist = -dist; + + U32 traverseIndex; + if (dist >= 0) + traverseIndex = pNode->frontIndex; + else + traverseIndex = pNode->backIndex; + + if (isBSPLeafIndex(traverseIndex)) { + if (isBSPSolidLeaf(traverseIndex)) { + return -1; + } else { + U16 zone = getBSPEmptyLeafZone(traverseIndex); + if (zone == 0x0FFF) + return -1; + else + return zone; + } + } + + pNode = &mBSPNodes[traverseIndex]; + } +} + + +//-------------------------------------------------------------------------- +static void itrClipToPlane(Point3F* points, U32& rNumPoints, const PlaneF& rPlane) +{ + S32 start = -1; + for(U32 i = 0; i < rNumPoints; i++) + { + if(rPlane.whichSide(points[i]) == PlaneF::Front) + { + start = i; + break; + } + } + + // Nothing was in front of the plane... + if(start == -1) + { + rNumPoints = 0; + return; + } + + static Point3F finalPoints[128]; + U32 numFinalPoints = 0; + + U32 baseStart = start; + U32 end = (start + 1) % rNumPoints; + + while(end != baseStart) + { + const Point3F& rStartPoint = points[start]; + const Point3F& rEndPoint = points[end]; + + PlaneF::Side fSide = rPlane.whichSide(rStartPoint); + PlaneF::Side eSide = rPlane.whichSide(rEndPoint); + + S32 code = fSide * 3 + eSide; + switch(code) + { + case 4: // f f + case 3: // f o + case 1: // o f + case 0: // o o + // No Clipping required + finalPoints[numFinalPoints++] = points[start]; + start = end; + end = (end + 1) % rNumPoints; + break; + + + case 2: // f b + { + // In this case, we emit the front point, Insert the intersection, + // and advancing to point to first point that is in front or on... + // + finalPoints[numFinalPoints++] = points[start]; + + Point3F vector = rEndPoint - rStartPoint; + F32 t = -(rPlane.distToPlane(rStartPoint) / mDot(rPlane, vector)); + + Point3F intersection = rStartPoint + (vector * t); + finalPoints[numFinalPoints++] = intersection; + + U32 endSeek = (end + 1) % rNumPoints; + while(rPlane.whichSide(points[endSeek]) == PlaneF::Back) + endSeek = (endSeek + 1) % rNumPoints; + + end = endSeek; + start = (end + (rNumPoints - 1)) % rNumPoints; + + const Point3F& rNewStartPoint = points[start]; + const Point3F& rNewEndPoint = points[end]; + + vector = rNewEndPoint - rNewStartPoint; + t = -(rPlane.distToPlane(rNewStartPoint) / mDot(rPlane, vector)); + + intersection = rNewStartPoint + (vector * t); + points[start] = intersection; + } + break; + + case -1: // o b + { + // In this case, we emit the front point, and advance to point to first + // point that is in front or on... + // + finalPoints[numFinalPoints++] = points[start]; + + U32 endSeek = (end + 1) % rNumPoints; + while(rPlane.whichSide(points[endSeek]) == PlaneF::Back) + endSeek = (endSeek + 1) % rNumPoints; + + end = endSeek; + start = (end + (rNumPoints - 1)) % rNumPoints; + + const Point3F& rNewStartPoint = points[start]; + const Point3F& rNewEndPoint = points[end]; + + Point3F vector = rNewEndPoint - rNewStartPoint; + F32 t = -(rPlane.distToPlane(rNewStartPoint) / mDot(rPlane, vector)); + + Point3F intersection = rNewStartPoint + (vector * t); + points[start] = intersection; + } + break; + + case -2: // b f + case -3: // b o + case -4: // b b + // In the algorithm used here, this should never happen... + AssertISV(false, "CSGPlane::clipWindingToPlaneFront: error in polygon clipper"); + break; + + default: + AssertFatal(false, "CSGPlane::clipWindingToPlaneFront: bad outcode"); + break; + } + + } + + // Emit the last point. + finalPoints[numFinalPoints++] = points[start]; + AssertFatal(numFinalPoints >= 3, avar("Error, this shouldn't happen! Invalid winding in itrClipToPlane: %d", numFinalPoints)); + + // Copy the new rWinding, and we're set! + // + dMemcpy(points, finalPoints, numFinalPoints * sizeof(Point3F)); + rNumPoints = numFinalPoints; + AssertISV(rNumPoints <= 128, "Increase maxWindingPoints. Talk to DMoore"); +} + +bool Interior::projectClipAndBoundFan(U32 fanIndex, F64* pResult) +{ + const TriFan& rFan = mWindingIndices[fanIndex]; + + static Point3F windingPoints[128]; + U32 numPoints = rFan.windingCount; + U32 i; + for(i = 0; i < numPoints; i++) + windingPoints[i] = mPoints[mWindings[rFan.windingStart + i]].point; + + itrClipToPlane(windingPoints, numPoints, sgOSPlaneFar); + if(numPoints != 0) + itrClipToPlane(windingPoints, numPoints, sgOSPlaneXMin); + if(numPoints != 0) + itrClipToPlane(windingPoints, numPoints, sgOSPlaneXMax); + if(numPoints != 0) + itrClipToPlane(windingPoints, numPoints, sgOSPlaneYMin); + if(numPoints != 0) + itrClipToPlane(windingPoints, numPoints, sgOSPlaneYMax); + + if(numPoints == 0) + { + pResult[0] = + pResult[1] = + pResult[2] = + pResult[3] = 0.0f; + return false; + } + + F32 minX = 1e10; + F32 maxX = -1e10; + F32 minY = 1e10; + F32 maxY = -1e10; + static Point4F projPoints[128]; + for(i = 0; i < numPoints; i++) + { + projPoints[i].set(windingPoints[i].x, windingPoints[i].y, windingPoints[i].z, 1.0); + sgProjMatrix.mul(projPoints[i]); + + AssertFatal(projPoints[i].w != 0.0, "Error, that's bad!"); + projPoints[i].x /= projPoints[i].w; + projPoints[i].y /= projPoints[i].w; + + if(projPoints[i].x < minX) + minX = projPoints[i].x; + if(projPoints[i].x > maxX) + maxX = projPoints[i].x; + if(projPoints[i].y < minY) + minY = projPoints[i].y; + if(projPoints[i].y > maxY) + maxY = projPoints[i].y; + } + + if(minX < -1.0f) minX = -1.0f; + if(minY < -1.0f) minY = -1.0f; + if(maxX > 1.0f) maxX = 1.0f; + if(maxY > 1.0f) maxY = 1.0f; + + pResult[0] = minX; + pResult[1] = minY; + pResult[2] = maxX; + pResult[3] = maxY; + return true; +} + +void Interior::createZoneRectVectors() +{ + sgZoneRects.setSize(mZones.size()); + for(U32 i = 0; i < mZones.size(); i++) + sgZoneRects[i].active = false; +} + +void Interior::destroyZoneRectVectors() +{ + +} + +void Interior::traverseZone(const RectD* inputRects, const U32 numInputRects, U32 currZone, Vector& zoneStack) +{ + PROFILE_START(InteriorTraverseZone); + // First, we push onto our rect list all the inputRects... + insertZoneRects(sgZoneRects[currZone], inputRects, numInputRects); + + // A portal is a valid traversal if the camera point is on the + // same side of it's plane as the zone. It must then pass the + // clip/project test. + U32 i; + const Zone& rZone = mZones[currZone]; + for(i = rZone.portalStart; i < U32(rZone.portalStart + rZone.portalCount); i++) + { + const Portal& rPortal = mPortals[mZonePortalList[i]]; + AssertFatal(U32(rPortal.zoneFront) == currZone || U32(rPortal.zoneBack) == currZone, + "Portal doesn't reference this zone?"); + + S32 camSide = getPlane(rPortal.planeIndex).whichSide(sgCamPoint); + if(planeIsFlipped(rPortal.planeIndex)) + camSide = -camSide; + S32 zoneSide = (U32(rPortal.zoneFront) == currZone) ? 1 : -1; + U16 otherZone = (U32(rPortal.zoneFront) == currZone) ? rPortal.zoneBack : rPortal.zoneFront; + + // Make sure this isn't a free floating portal... + if(otherZone == currZone) + continue; + + // Make sure we haven't encountered this zone already in this traversal + bool onStack = false; + for(U32 i = 0; i < zoneStack.size(); i++) + { + if(otherZone == zoneStack[i]) + { + onStack = true; + break; + } + } + if(onStack == true) + continue; + + if(camSide == zoneSide) + { + // Can traverse. Note: special case PlaneF::On + // here to prevent possible w == 0 problems and infinite recursion +// Vector newRects; +// VECTOR_SET_ASSOCIATION(newRects); + + // We're abusing the heck out of the allocator here. + U32 waterMark = FrameAllocator::getWaterMark(); + RectD* newRects = (RectD*)FrameAllocator::alloc(1); + U32 numNewRects = 0; + + for(S32 j = 0; j < rPortal.triFanCount; j++) + { + F64 result[4]; + if(projectClipAndBoundFan(rPortal.triFanStart + j, result)) + { + // Have a good rect from this. + RectD possible = convertToRectD(result); + + for(U32 k = 0; k < numInputRects; k++) + { + RectD copy = possible; + if(copy.intersect(inputRects[k])) + newRects[numNewRects++] = copy; + } + } + } + + if(numNewRects != 0) + { + FrameAllocator::alloc((sizeof(RectD) * numNewRects) - 1); + + U32 prevStackSize = zoneStack.size(); + zoneStack.push_back(currZone); + traverseZone(newRects, numNewRects, otherZone, zoneStack); + zoneStack.pop_back(); + AssertFatal(zoneStack.size() == prevStackSize, "Error, stack size changed!"); + } + FrameAllocator::setWaterMark(waterMark); + } + else if(camSide == PlaneF::On) + { + U32 waterMark = FrameAllocator::getWaterMark(); + RectD* newRects = (RectD*)FrameAllocator::alloc(numInputRects * sizeof(RectD)); + dMemcpy(newRects, inputRects, sizeof(RectD) * numInputRects); + + U32 prevStackSize = zoneStack.size(); + zoneStack.push_back(currZone); + traverseZone(newRects, numInputRects, otherZone, zoneStack); + zoneStack.pop_back(); + AssertFatal(zoneStack.size() == prevStackSize, "Error, stack size changed!"); + FrameAllocator::setWaterMark(waterMark); + } + } + PROFILE_END(); +} + +void Interior::zoneTraversal(S32 baseZone, const bool flipClip) +{ + PROFILE_START(InteriorZoneTraversal); + // If we're in solid, render everything... + if(baseZone == -1) + { + for(U32 i = 0; i < mZones.size(); i++) + { + sgZoneRenderInfo[i].render = true; + + sgZoneRenderInfo[i].frustum[0] = sgStoredFrustum[0]; + sgZoneRenderInfo[i].frustum[1] = sgStoredFrustum[1]; + sgZoneRenderInfo[i].frustum[2] = sgStoredFrustum[2]; + sgZoneRenderInfo[i].frustum[3] = sgStoredFrustum[3]; + sgZoneRenderInfo[i].viewport = sgStoredViewport; + } + PROFILE_END(); + return; + } + + // Otherwise, we're going to have to do some work... + createZoneRectVectors(); + U32 i; + for(i = 0; i < mZones.size(); i++) + sgZoneRenderInfo[i].render = false; + + // Create the object space clipping planes... + sgComputeOSFrustumPlanes(sgStoredFrustum, + sgWSToOSMatrix, + sgCamPoint, + sgOSPlaneFar, + sgOSPlaneXMin, + sgOSPlaneXMax, + sgOSPlaneYMin, + sgOSPlaneYMax); + + if(flipClip == true) + { + sgOSPlaneXMin.neg(); + sgOSPlaneXMax.neg(); + sgOSPlaneYMin.neg(); + sgOSPlaneYMax.neg(); + } + // First, the current zone gets the full clipRect, and marked as rendering... + static const F64 fullResult[4] = { -1, -1, 1, 1}; + + static Vector zoneStack; + zoneStack.clear(); + VECTOR_SET_ASSOCIATION(zoneStack); + + RectD baseRect = convertToRectD(fullResult); + traverseZone(&baseRect, 1, baseZone, zoneStack); + + for(i = 0; i < mZones.size(); i++) + { + if(sgZoneRects[i].active == true) + { + sgZoneRenderInfo[i].render = true; + convertToFrustum(sgZoneRenderInfo[i], sgZoneRects[i].rect); + } + } + PROFILE_END(); +} + + +void mergeSurfaceVectors(const U16* from0, + const U32 size0, + const U16* from1, + const U32 size1, + U16* output, + U32* outputSize) +{ + U32 pos0 = 0; + U32 pos1 = 0; + U32 outputCount = 0; + while(pos0 < size0 && pos1 < size1) + { + if(from0[pos0] < from1[pos1]) + { + output[outputCount++] = from0[pos0++]; + } + else if(from0[pos0] == from1[pos1]) + { + // Equal, output one, and inc both counts + output[outputCount++] = from0[pos0++]; + pos1++; + } + else + { + output[outputCount++] = from1[pos1++]; + } + } + AssertFatal(pos0 == size0 || pos1 == size1, "Error, one of these must have reached the end!"); + + // Copy the dregs... + if(pos0 != size0) + { + dMemcpy(&output[outputCount], &from0[pos0], sizeof(U16) * (size0 - pos0)); + outputCount += size0 - pos0; + } + else if(pos1 != size1) + { + dMemcpy(&output[outputCount], &from1[pos1], sizeof(U16) * (size1 - pos1)); + outputCount += size1 - pos1; + } + + *outputSize = outputCount; +} + +// Remove any collision hulls, interval trees, etc... +// +void Interior::purgeLODData() +{ + mConvexHulls.clear(); + mHullIndices.clear(); + mHullEmitStringIndices.clear(); + mHullSurfaceIndices.clear(); + mCoordBinIndices.clear(); + mConvexHullEmitStrings.clear(); + for(U32 i = 0; i < NumCoordBins * NumCoordBins; i++) + { + mCoordBins[i].binStart = 0; + mCoordBins[i].binCount = 0; + } +} + +// Build an OptimizedPolyList that represents this Interior's mesh +void Interior::buildExportPolyList(OptimizedPolyList& polys, MatrixF* mat, Point3F* scale) +{ + MatrixF saveMat; + Point3F saveScale; + polys.getTransform(&saveMat, &saveScale); + + if (mat) + { + if (scale) + polys.setTransform(mat, *scale); + else + polys.setTransform(mat, Point3F(1.0f, 1.0f, 1.0f)); + } + + // Create one TSMesh per zone + for (U32 i = 0; i < mZones.size(); i++) + { + const Interior::Zone& zone = mZones[i]; + + // Gather some data + for (U32 j = 0; j < zone.surfaceCount; j++) + { + U32 sdx = mZoneSurfaces[zone.surfaceStart + j]; + + const Interior::Surface& surface = mSurfaces[sdx]; + + // Snag the MaterialInstance + BaseMatInstance *matInst = mMaterialList->getMaterialInst( surface.textureIndex ); + + // Start a poly + polys.begin(matInst, j, OptimizedPolyList::TriangleStrip); + + // Set its plane + PlaneF plane = getFlippedPlane(surface.planeIndex); + polys.plane(plane); + + // Get its texGen so that we can calculate uvs + Interior::TexGenPlanes texGens = mTexGenEQs[surface.texGenIndex]; + texGens.planeY.invert(); + + // Loop through and add the verts and uvs + for (U32 k = 0; k < surface.windingCount; k++) + { + // Get our point + U32 vdx = mWindings[surface.windingStart + k]; + const Point3F& pt = mPoints[vdx].point; + + // Get our uv + Point2F uv; + uv.x = texGens.planeX.distToPlane(pt); + uv.y = texGens.planeY.distToPlane(pt); + + Point3F normal = getPointNormal(sdx, k); + + polys.vertex(pt, normal, uv); + } + + polys.end(); + } + } + + polys.setTransform(&saveMat, saveScale); +} + + +struct TempProcSurface +{ + U32 numPoints; + U32 pointIndices[32]; + U16 planeIndex; + U8 mask; +}; + +struct PlaneGrouping +{ + U32 numPlanes; + U16 planeIndices[32]; + U8 mask; +}; + + +//-------------------------------------------------------------------------- +void Interior::processHullPolyLists() +{ + Vector planeIndices(256, __FILE__, __LINE__); + Vector pointIndices(256, __FILE__, __LINE__); + Vector pointMasks(256, __FILE__, __LINE__); + Vector planeMasks(256, __FILE__, __LINE__); + Vector tempSurfaces(128, __FILE__, __LINE__); + Vector planeGroups(32, __FILE__, __LINE__); + + // Reserve space in the vectors + { + mPolyListStrings.setSize(0); + mPolyListStrings.reserve(128 << 10); + + mPolyListPoints.setSize(0); + mPolyListPoints.reserve(32 << 10); + + mPolyListPlanes.setSize(0); + mPolyListPlanes.reserve(16 << 10); + } + + for(U32 i = 0; i < mConvexHulls.size(); i++) + { + U32 j, k, l, m; + + ConvexHull& rHull = mConvexHulls[i]; + + planeIndices.setSize(0); + pointIndices.setSize(0); + tempSurfaces.setSize(0); + + // Extract all the surfaces from this hull into our temporary processing format + { + for(j = 0; j < rHull.surfaceCount; j++) + { + tempSurfaces.increment(); + TempProcSurface& temp = tempSurfaces.last(); + + U32 surfaceIndex = mHullSurfaceIndices[j + rHull.surfaceStart]; + if(isNullSurfaceIndex(surfaceIndex)) + { + const NullSurface& rSurface = mNullSurfaces[getNullSurfaceIndex(surfaceIndex)]; + + temp.planeIndex = rSurface.planeIndex; + temp.numPoints = rSurface.windingCount; + for(k = 0; k < rSurface.windingCount; k++) + temp.pointIndices[k] = mWindings[rSurface.windingStart + k]; + } + else + { + const Surface& rSurface = mSurfaces[surfaceIndex]; + + temp.planeIndex = rSurface.planeIndex; + collisionFanFromSurface(rSurface, temp.pointIndices, &temp.numPoints); + } + } + } + + // First order of business: extract all unique planes and points from + // the list of surfaces... + { + for(j = 0; j < tempSurfaces.size(); j++) + { + const TempProcSurface& rSurface = tempSurfaces[j]; + + bool found = false; + for(k = 0; k < planeIndices.size() && !found; k++) + { + if(rSurface.planeIndex == planeIndices[k]) + found = true; + } + if(!found) + planeIndices.push_back(rSurface.planeIndex); + + for(k = 0; k < rSurface.numPoints; k++) + { + found = false; + for(l = 0; l < pointIndices.size(); l++) + { + if(pointIndices[l] == rSurface.pointIndices[k]) + found = true; + } + if(!found) + pointIndices.push_back(rSurface.pointIndices[k]); + } + } + } + + // Now that we have all the unique points and planes, remap the surfaces in + // terms of the offsets into the unique point list... + { + for(j = 0; j < tempSurfaces.size(); j++) + { + TempProcSurface& rSurface = tempSurfaces[j]; + + // Points + for(k = 0; k < rSurface.numPoints; k++) + { + bool found = false; + for(l = 0; l < pointIndices.size(); l++) + { + if(pointIndices[l] == rSurface.pointIndices[k]) + { + rSurface.pointIndices[k] = l; + found = true; + break; + } + } + AssertISV(found, "Error remapping point indices in interior collision processing"); + } + } + } + + // Ok, at this point, we have a list of unique points, unique planes, and the + // surfaces all remapped in those terms. We need to check our error conditions + // that will make sure that we can properly encode this hull: + { + AssertISV(planeIndices.size() < 256, "Error, > 256 planes on an interior hull"); + AssertISV(pointIndices.size() < 63356, "Error, > 65536 points on an interior hull"); + AssertISV(tempSurfaces.size() < 256, "Error, > 256 surfaces on an interior hull"); + } + + // Now we group the planes together, and merge the closest groups until we're left + // with <= 8 groups + { + planeGroups.setSize(planeIndices.size()); + for(j = 0; j < planeIndices.size(); j++) + { + planeGroups[j].numPlanes = 1; + planeGroups[j].planeIndices[0] = planeIndices[j]; + } + + while(planeGroups.size() > 8) + { + // Find the two closest groups. If mdp(i, j) is the value of the + // largest pairwise dot product that can be computed from the vectors + // of group i, and group j, then the closest group pair is the one + // with the smallest value of mdp. + F32 currmin = 2; + S32 firstGroup = -1; + S32 secondGroup = -1; + + for(j = 0; j < planeGroups.size(); j++) + { + PlaneGrouping& first = planeGroups[j]; + for(k = j + 1; k < planeGroups.size(); k++) + { + PlaneGrouping& second = planeGroups[k]; + + F32 max = -2; + for(l = 0; l < first.numPlanes; l++) + { + for(m = 0; m < second.numPlanes; m++) + { + Point3F firstNormal = getPlane(first.planeIndices[l]); + if(planeIsFlipped(first.planeIndices[l])) + firstNormal.neg(); + Point3F secondNormal = getPlane(second.planeIndices[m]); + if(planeIsFlipped(second.planeIndices[m])) + secondNormal.neg(); + + F32 dot = mDot(firstNormal, secondNormal); + if(dot > max) + max = dot; + } + } + + if(max < currmin) + { + currmin = max; + firstGroup = j; + secondGroup = k; + } + } + } + AssertFatal(firstGroup != -1 && secondGroup != -1, "Error, unable to find a suitable pairing?"); + + // Merge first and second + PlaneGrouping& to = planeGroups[firstGroup]; + PlaneGrouping& from = planeGroups[secondGroup]; + while(from.numPlanes != 0) + { + to.planeIndices[to.numPlanes++] = from.planeIndices[from.numPlanes - 1]; + from.numPlanes--; + } + + // And remove the merged group + planeGroups.erase(secondGroup); + } + AssertFatal(planeGroups.size() <= 8, "Error, too many plane groupings!"); + + + // Assign a mask to each of the plane groupings + for(j = 0; j < planeGroups.size(); j++) + planeGroups[j].mask = (1 << j); + } + + // Now, assign the mask to each of the temp polys + { + for(j = 0; j < tempSurfaces.size(); j++) + { + bool assigned = false; + for(k = 0; k < planeGroups.size() && !assigned; k++) + { + for(l = 0; l < planeGroups[k].numPlanes; l++) + { + if(planeGroups[k].planeIndices[l] == tempSurfaces[j].planeIndex) + { + tempSurfaces[j].mask = planeGroups[k].mask; + assigned = true; + break; + } + } + } + AssertFatal(assigned, "Error, missed a plane somewhere in the hull poly list!"); + } + } + + // Copy the appropriate group mask to the plane masks + { + planeMasks.setSize(planeIndices.size()); + dMemset(planeMasks.address(), 0, planeMasks.size() * sizeof(U8)); + + for(j = 0; j < planeIndices.size(); j++) + { + bool found = false; + for(k = 0; k < planeGroups.size() && !found; k++) + { + for(l = 0; l < planeGroups[k].numPlanes; l++) + { + if(planeGroups[k].planeIndices[l] == planeIndices[j]) + { + planeMasks[j] = planeGroups[k].mask; + found = true; + break; + } + } + } + AssertFatal(planeMasks[j] != 0, "Error, missing mask for plane!"); + } + } + + // And whip through the points, constructing the total mask for that point + { + pointMasks.setSize(pointIndices.size()); + dMemset(pointMasks.address(), 0, pointMasks.size() * sizeof(U8)); + + for(j = 0; j < pointIndices.size(); j++) + { + for(k = 0; k < tempSurfaces.size(); k++) + { + for(l = 0; l < tempSurfaces[k].numPoints; l++) + { + if(tempSurfaces[k].pointIndices[l] == j) + { + pointMasks[j] |= tempSurfaces[k].mask; + break; + } + } + } + AssertFatal(pointMasks[j] != 0, "Error, point must exist in at least one surface!"); + } + } + + // Create the emit strings, and we're done! + { + // Set the range of planes + rHull.polyListPlaneStart = mPolyListPlanes.size(); + mPolyListPlanes.setSize(rHull.polyListPlaneStart + planeIndices.size()); + for(j = 0; j < planeIndices.size(); j++) + mPolyListPlanes[j + rHull.polyListPlaneStart] = planeIndices[j]; + + // Set the range of points + rHull.polyListPointStart = mPolyListPoints.size(); + mPolyListPoints.setSize(rHull.polyListPointStart + pointIndices.size()); + for(j = 0; j < pointIndices.size(); j++) + mPolyListPoints[j + rHull.polyListPointStart] = pointIndices[j]; + + // Now the emit string. The emit string goes like: (all fields are bytes) + // NumPlanes (PLMask) * NumPlanes + // NumPointsHi NumPointsLo (PtMask) * NumPoints + // NumSurfaces + // (NumPoints SurfaceMask PlOffset (PtOffsetHi PtOffsetLo) * NumPoints) * NumSurfaces + // + U32 stringLen = 1 + planeIndices.size(); + stringLen += 2 + pointIndices.size(); + stringLen += 1; + for(j = 0; j < tempSurfaces.size(); j++) + stringLen += 1 + 1 + 1 + (tempSurfaces[j].numPoints * 2); + + rHull.polyListStringStart = mPolyListStrings.size(); + mPolyListStrings.setSize(rHull.polyListStringStart + stringLen); + + U8* pString = &mPolyListStrings[rHull.polyListStringStart]; + U32 currPos = 0; + + // Planes + pString[currPos++] = planeIndices.size(); + for(j = 0; j < planeIndices.size(); j++) + pString[currPos++] = planeMasks[j]; + + // Points + pString[currPos++] = (pointIndices.size() >> 8) & 0xFF; + pString[currPos++] = (pointIndices.size() >> 0) & 0xFF; + for(j = 0; j < pointIndices.size(); j++) + pString[currPos++] = pointMasks[j]; + + // Surfaces + pString[currPos++] = tempSurfaces.size(); + for(j = 0; j < tempSurfaces.size(); j++) + { + pString[currPos++] = tempSurfaces[j].numPoints; + pString[currPos++] = tempSurfaces[j].mask; + + bool found = false; + for(k = 0; k < planeIndices.size(); k++) + { + if(planeIndices[k] == tempSurfaces[j].planeIndex) + { + pString[currPos++] = k; + found = true; + break; + } + } + AssertFatal(found, "Error, missing planeindex!"); + + for(k = 0; k < tempSurfaces[j].numPoints; k++) + { + pString[currPos++] = (tempSurfaces[j].pointIndices[k] >> 8) & 0xFF; + pString[currPos++] = (tempSurfaces[j].pointIndices[k] >> 0) & 0xFF; + } + } + AssertFatal(currPos == stringLen, "Error, mismatched string length!"); + } + } // for (i = 0; i < mConvexHulls.size(); i++) + + // Compact the used vectors + { + mPolyListStrings.compact(); + mPolyListPoints.compact(); + mPolyListPlanes.compact(); + } +} + +//-------------------------------------------------------------------------- +void Interior::processVehicleHullPolyLists() +{ + Vector planeIndices(256, __FILE__, __LINE__); + Vector pointIndices(256, __FILE__, __LINE__); + Vector pointMasks(256, __FILE__, __LINE__); + Vector planeMasks(256, __FILE__, __LINE__); + Vector tempSurfaces(128, __FILE__, __LINE__); + Vector planeGroups(32, __FILE__, __LINE__); + + // Reserve space in the vectors + { + mVehiclePolyListStrings.setSize(0); + mVehiclePolyListStrings.reserve(128 << 10); + + mVehiclePolyListPoints.setSize(0); + mVehiclePolyListPoints.reserve(32 << 10); + + mVehiclePolyListPlanes.setSize(0); + mVehiclePolyListPlanes.reserve(16 << 10); + } + + for(U32 i = 0; i < mVehicleConvexHulls.size(); i++) + { + U32 j, k, l, m; + + ConvexHull& rHull = mVehicleConvexHulls[i]; + + planeIndices.setSize(0); + pointIndices.setSize(0); + tempSurfaces.setSize(0); + + // Extract all the surfaces from this hull into our temporary processing format + { + for(j = 0; j < rHull.surfaceCount; j++) + { + tempSurfaces.increment(); + TempProcSurface& temp = tempSurfaces.last(); + + U32 surfaceIndex = mVehicleHullSurfaceIndices[j + rHull.surfaceStart]; + const NullSurface& rSurface = mVehicleNullSurfaces[getVehicleNullSurfaceIndex(surfaceIndex)]; + + temp.planeIndex = rSurface.planeIndex; + temp.numPoints = rSurface.windingCount; + for(k = 0; k < rSurface.windingCount; k++) + temp.pointIndices[k] = mVehicleWindings[rSurface.windingStart + k]; + } + } + + // First order of business: extract all unique planes and points from + // the list of surfaces... + { + for(j = 0; j < tempSurfaces.size(); j++) + { + const TempProcSurface& rSurface = tempSurfaces[j]; + + bool found = false; + for(k = 0; k < planeIndices.size() && !found; k++) + { + if(rSurface.planeIndex == planeIndices[k]) + found = true; + } + if(!found) + planeIndices.push_back(rSurface.planeIndex); + + for(k = 0; k < rSurface.numPoints; k++) + { + found = false; + for(l = 0; l < pointIndices.size(); l++) + { + if(pointIndices[l] == rSurface.pointIndices[k]) + found = true; + } + if(!found) + pointIndices.push_back(rSurface.pointIndices[k]); + } + } + } + + // Now that we have all the unique points and planes, remap the surfaces in + // terms of the offsets into the unique point list... + { + for(j = 0; j < tempSurfaces.size(); j++) + { + TempProcSurface& rSurface = tempSurfaces[j]; + + // Points + for(k = 0; k < rSurface.numPoints; k++) + { + bool found = false; + for(l = 0; l < pointIndices.size(); l++) + { + if(pointIndices[l] == rSurface.pointIndices[k]) + { + rSurface.pointIndices[k] = l; + found = true; + break; + } + } + AssertISV(found, "Error remapping point indices in interior collision processing"); + } + } + } + + // Ok, at this point, we have a list of unique points, unique planes, and the + // surfaces all remapped in those terms. We need to check our error conditions + // that will make sure that we can properly encode this hull: + { + AssertISV(planeIndices.size() < 256, "Error, > 256 planes on an interior hull"); + AssertISV(pointIndices.size() < 63356, "Error, > 65536 points on an interior hull"); + AssertISV(tempSurfaces.size() < 256, "Error, > 256 surfaces on an interior hull"); + } + + // Now we group the planes together, and merge the closest groups until we're left + // with <= 8 groups + { + planeGroups.setSize(planeIndices.size()); + for(j = 0; j < planeIndices.size(); j++) + { + planeGroups[j].numPlanes = 1; + planeGroups[j].planeIndices[0] = planeIndices[j]; + } + + while(planeGroups.size() > 8) + { + // Find the two closest groups. If mdp(i, j) is the value of the + // largest pairwise dot product that can be computed from the vectors + // of group i, and group j, then the closest group pair is the one + // with the smallest value of mdp. + F32 currmin = 2; + S32 firstGroup = -1; + S32 secondGroup = -1; + + for(j = 0; j < planeGroups.size(); j++) + { + PlaneGrouping& first = planeGroups[j]; + for(k = j + 1; k < planeGroups.size(); k++) + { + PlaneGrouping& second = planeGroups[k]; + + F32 max = -2; + for(l = 0; l < first.numPlanes; l++) + { + for(m = 0; m < second.numPlanes; m++) + { + Point3F firstNormal = mVehiclePlanes[first.planeIndices[l]]; + Point3F secondNormal = mVehiclePlanes[second.planeIndices[m]]; + + F32 dot = mDot(firstNormal, secondNormal); + if(dot > max) + max = dot; + } + } + + if(max < currmin) + { + currmin = max; + firstGroup = j; + secondGroup = k; + } + } + } + AssertFatal(firstGroup != -1 && secondGroup != -1, "Error, unable to find a suitable pairing?"); + + // Merge first and second + PlaneGrouping& to = planeGroups[firstGroup]; + PlaneGrouping& from = planeGroups[secondGroup]; + while(from.numPlanes != 0) + { + to.planeIndices[to.numPlanes++] = from.planeIndices[from.numPlanes - 1]; + from.numPlanes--; + } + + // And remove the merged group + planeGroups.erase(secondGroup); + } + AssertFatal(planeGroups.size() <= 8, "Error, too many plane groupings!"); + + + // Assign a mask to each of the plane groupings + for(j = 0; j < planeGroups.size(); j++) + planeGroups[j].mask = (1 << j); + } + + // Now, assign the mask to each of the temp polys + { + for(j = 0; j < tempSurfaces.size(); j++) + { + bool assigned = false; + for(k = 0; k < planeGroups.size() && !assigned; k++) + { + for(l = 0; l < planeGroups[k].numPlanes; l++) + { + if(planeGroups[k].planeIndices[l] == tempSurfaces[j].planeIndex) + { + tempSurfaces[j].mask = planeGroups[k].mask; + assigned = true; + break; + } + } + } + AssertFatal(assigned, "Error, missed a plane somewhere in the hull poly list!"); + } + } + + // Copy the appropriate group mask to the plane masks + { + planeMasks.setSize(planeIndices.size()); + dMemset(planeMasks.address(), 0, planeMasks.size() * sizeof(U8)); + + for(j = 0; j < planeIndices.size(); j++) + { + bool found = false; + for(k = 0; k < planeGroups.size() && !found; k++) + { + for(l = 0; l < planeGroups[k].numPlanes; l++) + { + if(planeGroups[k].planeIndices[l] == planeIndices[j]) + { + planeMasks[j] = planeGroups[k].mask; + found = true; + break; + } + } + } + AssertFatal(planeMasks[j] != 0, "Error, missing mask for plane!"); + } + } + + // And whip through the points, constructing the total mask for that point + { + pointMasks.setSize(pointIndices.size()); + dMemset(pointMasks.address(), 0, pointMasks.size() * sizeof(U8)); + + for(j = 0; j < pointIndices.size(); j++) + { + for(k = 0; k < tempSurfaces.size(); k++) + { + for(l = 0; l < tempSurfaces[k].numPoints; l++) + { + if(tempSurfaces[k].pointIndices[l] == j) + { + pointMasks[j] |= tempSurfaces[k].mask; + break; + } + } + } + AssertFatal(pointMasks[j] != 0, "Error, point must exist in at least one surface!"); + } + } + + // Create the emit strings, and we're done! + { + // Set the range of planes + rHull.polyListPlaneStart = mVehiclePolyListPlanes.size(); + mVehiclePolyListPlanes.setSize(rHull.polyListPlaneStart + planeIndices.size()); + for(j = 0; j < planeIndices.size(); j++) + mVehiclePolyListPlanes[j + rHull.polyListPlaneStart] = planeIndices[j]; + + // Set the range of points + rHull.polyListPointStart = mVehiclePolyListPoints.size(); + mVehiclePolyListPoints.setSize(rHull.polyListPointStart + pointIndices.size()); + for(j = 0; j < pointIndices.size(); j++) + mVehiclePolyListPoints[j + rHull.polyListPointStart] = pointIndices[j]; + + // Now the emit string. The emit string goes like: (all fields are bytes) + // NumPlanes (PLMask) * NumPlanes + // NumPointsHi NumPointsLo (PtMask) * NumPoints + // NumSurfaces + // (NumPoints SurfaceMask PlOffset (PtOffsetHi PtOffsetLo) * NumPoints) * NumSurfaces + // + U32 stringLen = 1 + planeIndices.size(); + stringLen += 2 + pointIndices.size(); + stringLen += 1; + for(j = 0; j < tempSurfaces.size(); j++) + stringLen += 1 + 1 + 1 + (tempSurfaces[j].numPoints * 2); + + rHull.polyListStringStart = mVehiclePolyListStrings.size(); + mVehiclePolyListStrings.setSize(rHull.polyListStringStart + stringLen); + + U8* pString = &mVehiclePolyListStrings[rHull.polyListStringStart]; + U32 currPos = 0; + + // Planes + pString[currPos++] = planeIndices.size(); + for(j = 0; j < planeIndices.size(); j++) + pString[currPos++] = planeMasks[j]; + + // Points + pString[currPos++] = (pointIndices.size() >> 8) & 0xFF; + pString[currPos++] = (pointIndices.size() >> 0) & 0xFF; + for(j = 0; j < pointIndices.size(); j++) + pString[currPos++] = pointMasks[j]; + + // Surfaces + pString[currPos++] = tempSurfaces.size(); + for(j = 0; j < tempSurfaces.size(); j++) + { + pString[currPos++] = tempSurfaces[j].numPoints; + pString[currPos++] = tempSurfaces[j].mask; + + bool found = false; + for(k = 0; k < planeIndices.size(); k++) + { + if(planeIndices[k] == tempSurfaces[j].planeIndex) + { + pString[currPos++] = k; + found = true; + break; + } + } + AssertFatal(found, "Error, missing planeindex!"); + + for(k = 0; k < tempSurfaces[j].numPoints; k++) + { + pString[currPos++] = (tempSurfaces[j].pointIndices[k] >> 8) & 0xFF; + pString[currPos++] = (tempSurfaces[j].pointIndices[k] >> 0) & 0xFF; + } + } + AssertFatal(currPos == stringLen, "Error, mismatched string length!"); + } + } // for (i = 0; i < mConvexHulls.size(); i++) + + // Compact the used vectors + { + mVehiclePolyListStrings.compact(); + mVehiclePolyListPoints.compact(); + mVehiclePolyListPlanes.compact(); + } +} + + + + +//-------------------------------------------------------------------------- +void ZoneVisDeterminer::runFromState(SceneRenderState* state, U32 offset, U32 parentZone) +{ + mMode = FromState; + mState = state; + mZoneRangeOffset = offset; + mParentZone = parentZone; +} + +void ZoneVisDeterminer::runFromRects(SceneRenderState* state, U32 offset, U32 parentZone) +{ + mMode = FromRects; + mState = state; + mZoneRangeOffset = offset; + mParentZone = parentZone; +} + +bool ZoneVisDeterminer::isZoneVisible(const U32 zone) const +{ + if(zone == 0) + return mState->getCullingState().getZoneState(mParentZone).isZoneVisible(); + + if(mMode == FromState) + { + return mState->getCullingState().getZoneState(zone + mZoneRangeOffset - 1).isZoneVisible(); + } + else + { + return sgZoneRenderInfo[zone].render; + } +} + + + +//-------------------------------------------------------------------------- +// storeSurfaceVerts - +// Need to store the verts for every surface because the uv mapping changes +// per vertex per surface. +//-------------------------------------------------------------------------- +void Interior::storeSurfVerts( Vector &masterIndexList, + Vector &tempIndexList, + Vector &verts, + U32 numIndices, + Surface &surface, + U32 surfaceIndex ) +{ + U32 startIndex = tempIndexList.size() - numIndices; + U32 startVert = verts.size(); + + Vector vertMap; + + for( U32 i=0; i &primInfoList, + Vector &indexList, + Vector &verts, + U32 &startIndex, + U32 &startVert ) +{ + + GFXPrimitive pnfo; + + if( !node.matInst ) + { + String name = mMaterialList->getMaterialName( node.baseTexIndex ); + + if (!name.equal("NULL", String::NoCase) && + !name.equal("ORIGIN", String::NoCase) && + !name.equal("TRIGGER", String::NoCase) && + !name.equal("FORCEFIELD", String::NoCase) && + !name.equal("EMITTER", String::NoCase) ) + { + Con::errorf( "material unmapped: %s", name.c_str() ); + } + + node.matInst = MATMGR->getWarningMatInstance(); + } + + + // find min index + pnfo.minIndex = U32(-1); + + for( U32 i=startIndex; i 0 ) + { + primInfoList.push_back( pnfo ); + + node.primInfoIndex = primInfoList.size() - 1; + RNList.renderNodeList.push_back( node ); + } + +} + +//-------------------------------------------------------------------------- +// fill vertex +//-------------------------------------------------------------------------- +void Interior::fillVertex( GFXVertexPNTTB &vert, Surface &surface, U32 surfaceIndex ) +{ + TexGenPlanes texPlanes = mTexGenEQs[surface.texGenIndex]; + + vert.texCoord.x = texPlanes.planeX.x * vert.point.x + + texPlanes.planeX.y * vert.point.y + + texPlanes.planeX.z * vert.point.z + + texPlanes.planeX.d; + + + vert.texCoord.y = texPlanes.planeY.x * vert.point.x + + texPlanes.planeY.y * vert.point.y + + texPlanes.planeY.z * vert.point.z + + texPlanes.planeY.d; + + texPlanes = mLMTexGenEQs[surfaceIndex]; + + vert.texCoord2.x = texPlanes.planeX.x * vert.point.x + + texPlanes.planeX.y * vert.point.y + + texPlanes.planeX.z * vert.point.z + + texPlanes.planeX.d; + + + vert.texCoord2.y = texPlanes.planeY.x * vert.point.x + + texPlanes.planeY.y * vert.point.y + + texPlanes.planeY.z * vert.point.z + + texPlanes.planeY.d; + + // vert normal and N already set + vert.T = surface.T - vert.normal * mDot(vert.normal, surface.T); + vert.T.normalize(); + + mCross(vert.normal, vert.T, &vert.B); + vert.B *= (mDot(vert.B, surface.B) < 0.0F) ? -1.0F : 1.0F; +} + +//-------------------------------------------------------------------------- +// Create vertex (and index) buffers for each zone +//-------------------------------------------------------------------------- +void Interior::createZoneVBs() +{ + if( mVertBuff ) + { + return; + } + + // create one big-ass vertex buffer to contain all verts + // drawIndexedPrimitive() calls can render subsets of the big-ass buffer + + Vector verts; + Vector indices; + + U32 startIndex = 0; + U32 startVert = 0; + + + Vector primInfoList; + + + // fill index list first, then fill verts + for( U32 i=0; i tempIndices; + tempIndices.setSize(0); + + + + for( U32 j=0; jgetMaterialInst( surface.textureIndex ); + Material* pMat = dynamic_cast(matInst->getMaterial()); + if( pMat && pMat->mPlanarReflection ) continue; + + node.exterior = surface.surfaceFlags & SurfaceOutsideVisible; + + // fill in node info on first time through + if( j==0 ) + { + node.baseTexIndex = surface.textureIndex; + node.matInst = matInst; + curTexIndex = node.baseTexIndex; + node.lightMapIndex = mNormalLMapIndices[surfaceIndex]; + curLightMapIndex = node.lightMapIndex; + } + + // check for material change + if( surface.textureIndex != curTexIndex || + mNormalLMapIndices[surfaceIndex] != curLightMapIndex ) + { + storeRenderNode( node, RNList, primInfoList, indices, verts, startIndex, startVert ); + + tempIndices.setSize( 0 ); + + // set new material info + U16 baseTex = surface.textureIndex; + U8 lmIndex = mNormalLMapIndices[surfaceIndex]; + + if( baseTex != curTexIndex ) + { + node.baseTexIndex = baseTex; + node.matInst = mMaterialList->getMaterialInst( baseTex ); + } + else + { + node.baseTexIndex = NULL; + } + + + node.lightMapIndex = lmIndex; + + curTexIndex = baseTex; + curLightMapIndex = lmIndex; + + } + + + // NOTE, can put this in storeSurfVerts() + U32 tempStartIndex = tempIndices.size(); + + U32 nPrim = 0; + U32 last = 2; + while(last < surface.windingCount) + { + // First + tempIndices.push_back( VertexBufferTempIndex(surfIndices[last-2], getPointNormal(surfaceIndex, last-2)) ); + tempIndices.push_back( VertexBufferTempIndex(surfIndices[last-1], getPointNormal(surfaceIndex, last-1)) ); + tempIndices.push_back( VertexBufferTempIndex(surfIndices[last-0], getPointNormal(surfaceIndex, last)) ); + last++; + nPrim++; + + if(last == surface.windingCount) + break; + + // Second + tempIndices.push_back( VertexBufferTempIndex(surfIndices[last-1], getPointNormal(surfaceIndex, last-1)) ); + tempIndices.push_back( VertexBufferTempIndex(surfIndices[last-2], getPointNormal(surfaceIndex, last-2)) ); + tempIndices.push_back( VertexBufferTempIndex(surfIndices[last-0], getPointNormal(surfaceIndex, last)) ); + last++; + nPrim++; + } + + U32 dStartVert = verts.size(); + GFXPrimitive* p = &surface.surfaceInfo; + p->startIndex = indices.size(); //tempStartIndex; + + // Normal render info + storeSurfVerts( indices, tempIndices, verts, tempIndices.size() - tempStartIndex, + surface, surfaceIndex ); + + // Debug render info + p->type = GFXTriangleList; + p->numVertices = verts.size() - dStartVert; + p->numPrimitives = nPrim; + p->minIndex = indices[p->startIndex]; + for (U32 i = p->startIndex; i < p->startIndex + nPrim * 3; i++) { + if (indices[i] < p->minIndex) { + p->minIndex = indices[i]; + } + } + } + + // store remaining index list + storeRenderNode( node, RNList, primInfoList, indices, verts, startIndex, startVert ); + + mZoneRNList.push_back( RNList ); + } + + // It is possible that we have no zones or have no surfaces in our zones (static meshes only) + if (verts.size() == 0) + return; + + // create vertex buffer + mVertBuff.set(GFX, verts.size(), GFXBufferTypeStatic); + GFXVertexPNTTB *vbVerts = mVertBuff.lock(); + + dMemcpy( vbVerts, verts.address(), verts.size() * sizeof( GFXVertexPNTTB ) ); + + mVertBuff.unlock(); + + // create primitive buffer + U16 *ibIndices; + GFXPrimitive *piInput; + mPrimBuff.set(GFX, indices.size(), primInfoList.size(), GFXBufferTypeStatic); + mPrimBuff.lock(&ibIndices, &piInput); + + dMemcpy( ibIndices, indices.address(), indices.size() * sizeof(U16) ); + dMemcpy( piInput, primInfoList.address(), primInfoList.size() * sizeof(GFXPrimitive) ); + + mPrimBuff.unlock(); + +} + + +#define SMALL_FLOAT (1e-12) + +//-------------------------------------------------------------------------- +// Get the texture space matrice for a point on a surface +//-------------------------------------------------------------------------- +void Interior::getTexMat(U32 surfaceIndex, U32 pointOffset, Point3F& T, Point3F& N, Point3F& B) +{ + Surface& surface = mSurfaces[surfaceIndex]; + + if (mFileVersion >= 11) + { + // There is a one-to-one mapping of mWindings and mTexMatIndices + U32 texMatIndex = mTexMatIndices[surface.windingStart + pointOffset]; + TexMatrix& texMat = mTexMatrices[texMatIndex]; + + T = mNormals[texMat.T]; + N = mNormals[texMat.N]; + B = mNormals[texMat.B]; + } + else + { + T = surface.T - surface.N * mDot(surface.N, surface.T); + N = surface.N; + + mCross(surface.N, T, &B); + B *= (mDot(B, surface.B) < 0.0F) ? -1.0f : 1.0f; + } + + return; +} + +//-------------------------------------------------------------------------- +// Fill in texture space matrices for each surface +//-------------------------------------------------------------------------- +void Interior::fillSurfaceTexMats() +{ + for( U32 i=0; i SMALL_FLOAT ) + { + S.x = -cp.y / cp.x; + T.x = -cp.z / cp.x; + } + + edge1.set( pts[1].point.y - pts[0].point.y, pts[1].texCoord.x - pts[0].texCoord.x, pts[1].texCoord.y - pts[0].texCoord.y ); + edge2.set( pts[2].point.y - pts[0].point.y, pts[2].texCoord.x - pts[0].texCoord.x, pts[2].texCoord.y - pts[0].texCoord.y ); + + mCross( edge1, edge2, &cp ); + if( fabs(cp.x) > SMALL_FLOAT ) + { + S.y = -cp.y / cp.x; + T.y = -cp.z / cp.x; + } + + edge1.set( pts[1].point.z - pts[0].point.z, pts[1].texCoord.x - pts[0].texCoord.x, pts[1].texCoord.y - pts[0].texCoord.y ); + edge2.set( pts[2].point.z - pts[0].point.z, pts[2].texCoord.x - pts[0].texCoord.x, pts[2].texCoord.y - pts[0].texCoord.y ); + + mCross( edge1, edge2, &cp ); + if( fabs(cp.x) > SMALL_FLOAT ) + { + S.z = -cp.y / cp.x; + T.z = -cp.z / cp.x; + } + + S.normalizeSafe(); + T.normalizeSafe(); + mCross( S, T, &SxT ); + + + if( mDot( SxT, planeNorm ) < 0.0 ) + { + SxT = -SxT; + } + + surface.T = S; + surface.B = T; + surface.N = SxT; + surface.normal = planeNorm; + } + +} + +//-------------------------------------------------------------------------- +// Clone material instances - if a texture (material) exists on both the +// inside and outside of an interior, it needs to create two material +// instances - one for the inside, and one for the outside. The reason is +// that the light direction maps only exist on the inside of the interior. +//-------------------------------------------------------------------------- +void Interior::cloneMatInstances() +{ + Vector< BaseMatInstance *> outsideMats; + Vector< BaseMatInstance *> insideMats; + + // store pointers to mat lists + for( U32 i=0; i(outsideMats[i]->getMaterial()); + + if (mat) + { + BaseMatInstance *newMat = mat->createMatInstance(); + mMatInstCleanupList.push_back( newMat ); + + // go through and find the inside version and replace it + // with the new one. + for( U32 k=0; kgetFeaturesDelegate().bind( &Interior::_enableLightMapFeature ); + mat->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat()); + + // We need to know if we have non-zwrite translucent materials + // so that we can make the extra renderimage pass for them. + Material* pMat = dynamic_cast(mat->getMaterial()); + if ( pMat ) + mHasTranslucentMaterials |= pMat->mTranslucent && !pMat->mTranslucentZWrite; + } + + } + + for( U32 j=0; jinit( MATMGR->getDefaultFeatures(), getGFXVertexFormat()); + + // We need to know if we have non-zwrite translucent materials + // so that we can make the extra renderimage pass for them. + Material* pMat = dynamic_cast(mat->getMaterial()); + if ( pMat ) + mHasTranslucentMaterials |= pMat->mTranslucent && !pMat->mTranslucentZWrite; + } + } + } +} + +void Interior::_enableLightMapFeature( ProcessedMaterial *mat, + U32 stageNum, + MaterialFeatureData &fd, + const FeatureSet &features ) +{ + if ( mat->getMaterial() ) + { + fd.features.addFeature( MFT_LightMap ); + fd.features.removeFeature( MFT_ToneMap ); + } +} + +//-------------------------------------------------------------------------- +// Create the reflect plane list and the nodes of geometry necessary to +// render the reflective surfaces. +//-------------------------------------------------------------------------- +void Interior::createReflectPlanes() +{ + Vector verts; + Vector primInfoList; + Vector indices; + + U32 startIndex = 0; + U32 startVert = 0; + + + + for( U32 i=0; igetMaterialInst( surface.textureIndex ); + Material* pMat = dynamic_cast(matInst->getMaterial()); + if( !pMat || !pMat->mPlanarReflection ) continue; + + U32 *surfIndices = &mWindings[surface.windingStart]; + + // create / fill in GFXPrimitve, verts, indices + // going to need a new render node + ReflectRenderNode node; + node.exterior = surface.surfaceFlags & SurfaceOutsideVisible; + node.matInst = mMaterialList->getMaterialInst( surface.textureIndex ); + node.lightMapIndex = mNormalLMapIndices[surfaceIndex]; + + + PlaneF plane; + plane = getPlane( surface.planeIndex ); + if( planeIsFlipped( surface.planeIndex ) ) + { + plane.x = -plane.x; + plane.y = -plane.y; + plane.z = -plane.z; + plane.d = -plane.d; + } + + // check if coplanar with existing reflect plane + //-------------------------------------------------- + S32 rPlaneIdx = -1; + for( U32 a=0; a 0.999 ) + { + if( fabs( plane.d - mReflectPlanes[a].d ) < 0.001 ) + { + rPlaneIdx = a; + break; + } + } + } + + PlaneF refPlane; + refPlane = plane; + + if( rPlaneIdx < 0 ) + { + mReflectPlanes.push_back( refPlane ); + node.reflectPlaneIndex = mReflectPlanes.size() - 1; + } + else + { + node.reflectPlaneIndex = rPlaneIdx; + } + + // store the indices for the surface + //-------------------------------------------------- + Vector tempIndices; + tempIndices.setSize( 0 ); + + U32 tempStartIndex = tempIndices.size(); + + U32 last = 2; + while(last < surface.windingCount) + { + // First + tempIndices.push_back( VertexBufferTempIndex(surfIndices[last-2], getPointNormal(surfaceIndex, last-2)) ); + tempIndices.push_back( VertexBufferTempIndex(surfIndices[last-1], getPointNormal(surfaceIndex, last-1)) ); + tempIndices.push_back( VertexBufferTempIndex(surfIndices[last-0], getPointNormal(surfaceIndex, last)) ); + last++; + + if(last == surface.windingCount) + break; + + // Second + tempIndices.push_back( VertexBufferTempIndex(surfIndices[last-1], getPointNormal(surfaceIndex, last-1)) ); + tempIndices.push_back( VertexBufferTempIndex(surfIndices[last-2], getPointNormal(surfaceIndex, last-2)) ); + tempIndices.push_back( VertexBufferTempIndex(surfIndices[last-0], getPointNormal(surfaceIndex, last)) ); + last++; + } + + storeSurfVerts( indices, tempIndices, verts, tempIndices.size() - tempStartIndex, + surface, surfaceIndex ); + + + // store render node and GFXPrimitive + // each node is a different reflective surface + // --------------------------------------------------- + + // find min index + GFXPrimitive pnfo; + pnfo.minIndex = U32(-1); + for( U32 k=startIndex; kgetMaterialNameList().size(); + + if(mapToNameIndex < 0 || mapToNameIndex >= targetCount) + return String::EmptyString; + + return mMaterialList->getMaterialNameList()[mapToNameIndex]; +} + +S32 Interior::getTargetCount() const +{ + if(!this) + return -1; + + return mMaterialList->getMaterialNameList().size(); + +} diff --git a/Engine/source/interior/interior.h b/Engine/source/interior/interior.h new file mode 100644 index 000000000..b3d36ab41 --- /dev/null +++ b/Engine/source/interior/interior.h @@ -0,0 +1,1172 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _INTERIOR_H_ +#define _INTERIOR_H_ + +#ifndef _COLOR_H_ +#include "core/color.h" +#endif +#ifndef _COLLISION_H_ +#include "collision/collision.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _MPLANE_H_ +#include "math/mPlane.h" +#endif +#ifndef _MBOX_H_ +#include "math/mBox.h" +#endif +#ifndef _MSPHERE_H_ +#include "math/mSphere.h" +#endif +#ifndef _CONVEX_H_ +#include "collision/convex.h" +#endif +#ifndef _INTERIORLMMANAGER_H_ +#include "interior/interiorLMManager.h" +#endif +#ifndef _INTERIORSIMPLEMESH_H_ +#include "interior/interiorSimpleMesh.h" +#endif +#ifndef _OPTIMIZEDPOLYLIST_H_ +#include "collision/optimizedPolyList.h" +#endif +#ifndef _SCENEDATA_H_ +#include "materials/sceneData.h" +#endif + +//-------------------------------------- Forward declarations +class MatInstance; +class Stream; +class EditGeometry; +class InteriorInstance; +class GBitmap; +class RectD; +class SphereF; +class MatrixF; +class SceneRenderState; +class MaterialList; +class AbstractPolyList; +class InteriorSubObject; +class TranslucentSubObject; +class BitVector; +struct RayInfo; +struct EdgeList; +class SurfaceHash; +class InteriorPolytope; +class LightInfo; +class PlaneRange; +class EditInteriorResource; +class GFXVertexBuffer; +class GFXPrimitiveBuffer; +struct RenderInst; +struct GFXVertexPNTTB; +struct GFXPrimitiveInfo; +struct MaterialFeatureData; + + +//-------------------------------------------------------------------------- +class InteriorConvex : public Convex +{ + typedef Convex Parent; + friend class Interior; + friend class InteriorInstance; + + protected: + Interior* pInterior; + public: + S32 hullId; + Box3F box; + + public: + InteriorConvex() { mType = InteriorConvexType; } + InteriorConvex(const InteriorConvex& cv) + { + mObject = cv.mObject; + pInterior = cv.pInterior; + hullId = cv.hullId; + box = box; + } + + Box3F getBoundingBox() const; + Box3F getBoundingBox(const MatrixF& mat, const Point3F& scale) const; + Point3F support(const VectorF& v) const; + void getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf); + void getPolyList(AbstractPolyList* list); +}; + +class ZoneVisDeterminer +{ + enum Mode + { + FromState, + FromRects + }; + + Mode mMode; + + SceneRenderState* mState; + U32 mZoneRangeOffset; + U32 mParentZone; + + public: + ZoneVisDeterminer() : mMode(FromRects), mState(NULL) { } + + void runFromState(SceneRenderState*, U32, U32); + void runFromRects(SceneRenderState*, U32, U32); + + bool isZoneVisible(const U32) const; +}; + + +struct ItrPaddedPoint +{ + Point3F point; + union + { + F32 fogCoord; + U8 fogColor[4]; + }; +}; + + +//------------------------------------------------------------------------------ +//-------------------------------------- CLASS NOTES +// Interior: Base for all interior geometries. Contains all lighting, poly, +// portal zone, bsp info, etc. to render an interior. +// +// Internal Structure Notes: +// IBSPNode: +// planeIndex: Obv. +// frontIndex/backIndex: Top bit indicates if children are leaves. +// Next bit indicates if leaf children are solid. +// +// IBSPLeafSolid: +// planeIndex: obv. +// surfaceIndex/surfaceCount: Polys that are on the faces of this leaf. Only +// used for collision/surface info detection. +// +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +class Interior +{ + friend class EditGeometry; + friend class InteriorInstance; + friend class InteriorProxy; + friend class blInteriorProxy; + friend class TranslucentSubObject; + friend class MirrorSubObject; + friend class InteriorConvex; + friend class EditInteriorResource; + friend class InteriorLMManager; + friend class TSShape; + friend class SceneLighting; + + //-------------------------------------- Public interfaces + public: + Interior(); + ~Interior(); + + +private: + U32 mLightMapBorderSize; + +public: + Vector surfaceZones; + U32 getLightMapBorderSize() const {return mLightMapBorderSize;} + void setLightMapBorderSize(U32 value) {mLightMapBorderSize = value;} + void buildSurfaceZones(); + //void sgSetupLighting(InteriorInstance *intInst, SceneData &sgData); + //bool sgRenderLights(InteriorInstance *intInst, SceneData &sgData); + + // Interior Render StateBlock + GFXStateBlockRef mInteriorSB; + + MaterialList* mMaterialList; + + // Misc + U32 getDetailLevel() const; + U32 getMinPixels() const; + const Box3F& getBoundingBox() const; + S32 getNumZones() const; + + // Rendering + bool prepForRendering(const char* path); + + bool traverseZones( SceneCullingState* state, + const Frustum& frustum, + S32 containingZone, + S32 baseZone, + U32 zoneOffset, + const MatrixF& OSToWS, + const Point3F& objScale, + const bool dontRestrictOutside, + const bool flipClipPlanes, + Frustum& outFrustum ); + + void prepBatchRender( InteriorInstance *intInst, SceneRenderState *state ); + + void setupRenderStates(); + + + bool scopeZones(const S32 baseZone, + const Point3F& interiorRoot, + bool* interiorScopingState); + + ZoneVisDeterminer setupZoneVis( InteriorInstance *intInst, SceneRenderState *state ); + + SceneData setupSceneGraphInfo( InteriorInstance *intInst, + SceneRenderState *state ); + + void setupRender( InteriorInstance *intInst, + SceneRenderState *state, + MeshRenderInst *coreRi, + const MatrixF* worldToCamera); + + + //-------------------------------------- Collision Interface and zone scans + public: + bool scanZones(const Box3F&, const MatrixF&, U16* zones, U32* numZones); + bool castRay(const Point3F&, const Point3F&, RayInfo*); + bool buildPolyList(AbstractPolyList*, const Box3F&, const MatrixF&, const Point3F&); + + bool getIntersectingHulls(const Box3F&, U16* hulls, U32* numHulls); + bool getIntersectingVehicleHulls(const Box3F&, U16* hulls, U32* numHulls); + +protected: + bool castRay_r(const U32, const U16, const Point3F&, const Point3F&, RayInfo*); + void buildPolyList_r(InteriorPolytope& polytope, + SurfaceHash& hash); + void scanZone_r(const U32 node, + const Point3F& center, + const Point3F& axisx, + const Point3F& axisy, + const Point3F& axisz, + U16* zones, + U32* numZones); + void scanZoneNew(InteriorPolytope& polytope, + U16* zones, + U32* numZones); + + + /// @name Lightmap Support + /// @{ + static void _enableLightMapFeature( ProcessedMaterial *mat, + U32 stageNum, + MaterialFeatureData &fd, + const FeatureSet &features ); + /// @} + + //-------------------------------------- Global rendering control +public: + enum RenderModes + { + NormalRender = 0, + NormalRenderLines = 1, + ShowDetail = 2, + ShowAmbiguous = 3, + ShowOrphan = 4, + ShowLightmaps = 5, + ShowTexturesOnly = 6, + ShowPortalZones = 7, + ShowOutsideVisible = 8, + ShowCollisionFans = 9, + ShowStrips = 10, + ShowNullSurfaces = 11, + ShowLargeTextures = 12, + ShowHullSurfaces = 13, + ShowVehicleHullSurfaces = 14, + ShowVertexColors = 15, + ShowDetailLevel = 16 + }; + + enum Constants + { + NumCoordBins = 16, + + BinsXY = 0, + BinsXZ = 1, + BinsYZ = 2 + }; + + static U32 smRenderMode; + static bool smFocusedDebug; + static bool smUseVertexLighting; + static U32 smFileVersion; + static bool smLightingCastRays; + static bool smLightingBuildPolyList; + + //-------------------------------------- Persistence interface + public: + bool read(Stream& stream); + bool write(Stream& stream) const; + + bool readVehicleCollision(Stream& stream); + bool writeVehicleCollision(Stream& stream) const; + + private: + bool writePlaneVector(Stream&) const; + bool readPlaneVector(Stream&); + bool readLMapTexGen(Stream&, PlaneF&, PlaneF&); + bool writeLMapTexGen(Stream&, const PlaneF&, const PlaneF&) const; + void setupTexCoords(); + void setupZonePlanes(); + + //-------------------------------------- For morian only... + public: + void processHullPolyLists(); + void processVehicleHullPolyLists(); + + //-------------------------------------- BSP Structures + private: + struct IBSPNode + { + U16 planeIndex; + U32 frontIndex; + U32 backIndex; + + U16 terminalZone; // if high bit set, then the lower 15 bits are the zone + // of any of the subsidiary nodes. Note that this is + // going to overestimate some, since an object could be + // completely contained in solid, but it's probably + // going to turn out alright. + }; + struct IBSPLeafSolid + { + U32 surfaceIndex; + U16 surfaceCount; + }; + + bool isBSPLeafIndex(U32 index) const; + bool isBSPSolidLeaf(U32 index) const; + bool isBSPEmptyLeaf(U32 index) const; + U16 getBSPSolidLeafIndex(U32 index) const; + U16 getBSPEmptyLeafZone(U32 index) const; + + void setupAveTexGenLength(); + + void truncateZoneTree(); + void truncateZoneNode(const U32); + bool getUnifiedZone(const U32, S32*); + + public: + static U16 getPlaneIndex(U16 index); + static bool planeIsFlipped(U16 index); + const PlaneF& getPlane(U16 index) const; + PlaneF getFlippedPlane(const U16 index) const; + + const Point3F getPointNormal(const U32 surfaceIndex, const U32 pointOffset) const; + + private: + bool areEqualPlanes(U16, U16) const; + + bool isNullSurfaceIndex(const U32 index) const; + bool isVehicleNullSurfaceIndex(const U32 index) const; + U32 getNullSurfaceIndex(const U32 index) const; + U32 getVehicleNullSurfaceIndex(const U32 index) const; + + //-------------------------------------- Portals and Zone structures + private: + struct Zone + { + U16 portalStart; + U16 portalCount; + + U32 surfaceStart; + U32 planeStart; + + U16 surfaceCount; + U16 planeCount; + + U32 staticMeshStart; + U32 staticMeshCount; + + U16 flags; + U16 zoneId; // This is ephemeral, not persisted out. + }; + + struct Portal + { + U16 planeIndex; + + U16 triFanCount; + U32 triFanStart; // portals can have multiple windings + + U16 zoneFront; + U16 zoneBack; + }; + + //-------------------------------------- Poly/Surface structures +public: + enum SurfaceFlags + { + SurfaceDetail = BIT(0), + SurfaceAmbiguous = BIT(1), + SurfaceOrphan = BIT(2), + SurfaceSharedLMaps = BIT(3), // Indicates that the alarm and normal states share a lightmap (for mission lighter) + SurfaceOutsideVisible = BIT(4), + SurfaceStaticMesh = BIT(5), // This surface belongs to a static mesh collision hull + SurfaceFlagMask = (SurfaceDetail | + SurfaceAmbiguous | + SurfaceOrphan | + SurfaceSharedLMaps | + SurfaceOutsideVisible | + SurfaceStaticMesh) + }; + enum ZoneFlags + { + ZoneInside = BIT(0) + }; + + const bool isSurfaceOutsideVisible(U32 surface) const; + + public: + struct TexMatrix + { + S32 T; + S32 N; + S32 B; + + TexMatrix() + : T( -1 ), + N( -1 ), + B( -1 ) + {}; + }; + + struct Edge + { + S32 vertexes[2]; + S32 faces[2]; + }; + + struct TexGenPlanes + { + PlaneF planeX; + PlaneF planeY; + }; + + struct TriFan + { + U32 windingStart; + U32 windingCount; + }; + + struct Surface + { + U32 windingStart; // 1 + + U16 planeIndex; // 2 + U16 textureIndex; + + U32 texGenIndex; // 3 + + U16 lightCount; // 4 + U8 surfaceFlags; + U32 windingCount; + + U32 fanMask; // 5 + + U32 lightStateInfoStart; // 6 + + U32 mapOffsetX; // 7 + U32 mapOffsetY; + U32 mapSizeX; + U32 mapSizeY; + + Point3F T,B,N; + Point3F normal; + + //U32 VBIndexStart; + //U32 primIndex; + GFXPrimitive surfaceInfo; + + bool unused; + }; + + struct NullSurface + { + U32 windingStart; + + U16 planeIndex; + U8 surfaceFlags; + U32 windingCount; + }; + + //-------------------------------------- Animated lighting structures + enum LightFlags + { + AnimationAmbient = BIT(0), + AnimationLoop = BIT(1), + AnimationFlicker = BIT(2), + AnimationTypeMask = BIT(3) - 1, + + AlarmLight = BIT(3) + }; + + enum LightType + { + AmbientLooping = AnimationAmbient | AnimationLoop, + AmbientFlicker = AnimationAmbient | AnimationFlicker, + + TriggerableLoop = AnimationLoop, + TriggerableFlicker = AnimationFlicker, + TriggerableRamp = 0 + }; + +private: + bool readSurface(Stream&, Surface&, TexGenPlanes&, const bool); + + public: + // this is public because tools/Morian needs this defination + struct AnimatedLight { + U32 nameIndex; // Light's name + U32 stateIndex; // start point in the state list + + U16 stateCount; // number of states in this light + U16 flags; // flags (Apply AnimationTypeMask to get type) + + U32 duration; // total duration of animation (ms) + }; + private: + struct LightState { + U8 red; // state's color + U8 green; + U8 blue; + U8 _color_padding_; + + U32 activeTime; // Time (ms) at which this state becomes active + + U32 dataIndex; // StateData count and index for this state + U16 dataCount; + + U16 __32bit_padding__; + }; + struct LightStateData { + U32 surfaceIndex; // Surface affected by this data + U32 mapIndex; // Index into StateDataBuffer (0xFFFFFFFF indicates none) + U16 lightStateIndex; // Entry to modify in InteriorInstance + U16 __32bit_padding__; + }; + + // convex hull collision structures... + protected: + struct ConvexHull + { + F32 minX; + F32 maxX; + F32 minY; + F32 maxY; + + F32 minZ; + F32 maxZ; + U32 hullStart; + U32 surfaceStart; + + U32 planeStart; + U16 hullCount; + U16 surfaceCount; + U32 polyListPlaneStart; + + U32 polyListPointStart; + U32 polyListStringStart; + U16 searchTag; + bool staticMesh; + }; + + struct CoordBin + { + U32 binStart; + U32 binCount; + }; + + struct RenderNode + { + bool exterior; + U16 baseTexIndex; + U8 lightMapIndex; + S32 primInfoIndex; + BaseMatInstance *matInst; + + RenderNode() + { + exterior = true; + baseTexIndex = 0; + lightMapIndex = U8(-1); + primInfoIndex = -1; + matInst = NULL; + } + }; + + + // public because InteriorInstance needs access + public: + Vector< PlaneF > mReflectPlanes; + + protected: + struct ReflectRenderNode + { + bool exterior; + S32 reflectPlaneIndex; + S32 primInfoIndex; + U8 lightMapIndex; + + BaseMatInstance *matInst; + + + ReflectRenderNode() + { + exterior = true; + reflectPlaneIndex = -1; + lightMapIndex = U8(-1); + primInfoIndex = -1; + matInst = NULL; + } + }; + + + struct ZoneRNList + { + Vector renderNodeList; + }; + + struct ZoneReflectRNList + { + Vector reflectList; + }; + + // needs to be exposed in order for on the fly changes + public: + Vector mZoneRNList; + + private: + + // reflective plane data + GFXVertexBufferHandle mReflectVertBuff; + GFXPrimitiveBufferHandle mReflectPrimBuff; + Vector mZoneReflectRNList; + + // standard interior data + GFXVertexBufferHandle mVertBuff; + GFXPrimitiveBufferHandle mPrimBuff; + + private: + LM_HANDLE mLMHandle; + public: + LM_HANDLE getLMHandle() {return(mLMHandle);} + + public: + + // SceneLighting::InteriorProxy interface + const Surface & getSurface(const U32 surface) const; + const U32 getSurfaceCount() const; + const U32 getNormalLMapIndex(const U32 surface) const; + const U32 getAlarmLMapIndex(const U32 surface) const; + const U32 getStaticMeshCount() const; + const InteriorSimpleMesh *getStaticMesh(const U32 index) const; + const U32 getWinding(const U32 index) const; + const Point3F & getPoint(const U32 index) const; + const TexGenPlanes & getLMTexGenEQ(const U32 index) const; + const TexGenPlanes & getTexGenEQ(const U32 index) const; + bool hasAlarmState() const; + const U32 getWindingCount() const; + S32 getTargetCount() const; + const String& getTargetName( S32 mapToNameIndex ) const; + + //-------------------------------------- Instance Data Members + private: + U32 mFileVersion; + U32 mDetailLevel; + U32 mMinPixels; + F32 mAveTexGenLength; // Set in Interior::read after loading the texgen planes. + Box3F mBoundingBox; + SphereF mBoundingSphere; + + Vector mPlanes; + Vector mPoints; + Vector mPointVisibility; + + Vector mNormals; + Vector mTexMatrices; + Vector mTexMatIndices; + + ColorF mBaseAmbient; + ColorF mAlarmAmbient; + + Vector mBSPNodes; + Vector mBSPSolidLeaves; + + bool mPreppedForRender; + + bool mHasTranslucentMaterials; + + Vector mWindings; + + Vector mTexGenEQs; + Vector mLMTexGenEQs; + + Vector mWindingIndices; + Vector mSurfaces; + Vector mNullSurfaces; + Vector mSolidLeafSurfaces; + + Vector mEdges; + + // Portals and zones + Vector mZones; + Vector mZonePlanes; + Vector mZoneSurfaces; + Vector mZonePortalList; + Vector mPortals; + Vector mZoneStaticMeshes; + + // Subobjects: Doors, translucencies, mirrors, etc. + Vector mSubObjects; + + // Lighting info + bool mHasAlarmState; + U32 mNumLightStateEntries; + + Vector mLightmaps; + Vector mLightmapKeep; + Vector mNormalLMapIndices; + Vector mAlarmLMapIndices; + + U32 mNumTriggerableLights; // Note: not persisted + + // Persistent animated light structures + Vector mAnimatedLights; + Vector mLightStates; + Vector mStateData; + Vector mStateDataBuffer; + + Vector mNameBuffer; + + Vector mConvexHulls; + Vector mConvexHullEmitStrings; + Vector mHullIndices; + Vector mHullEmitStringIndices; + Vector mHullSurfaceIndices; + Vector mHullPlaneIndices; + Vector mPolyListPlanes; + Vector mPolyListPoints; + Vector mPolyListStrings; + CoordBin mCoordBins[NumCoordBins * NumCoordBins]; + Vector mCoordBinIndices; + U32 mCoordBinMode; + + Vector mVehicleConvexHulls; + Vector mVehicleConvexHullEmitStrings; + Vector mVehicleHullIndices; + Vector mVehicleHullEmitStringIndices; + Vector mVehicleHullSurfaceIndices; + Vector mVehicleHullPlaneIndices; + Vector mVehiclePolyListPlanes; + Vector mVehiclePolyListPoints; + Vector mVehiclePolyListStrings; + Vector mVehiclePoints; + Vector mVehicleNullSurfaces; + Vector mVehiclePlanes; + Vector mVehicleWindings; + Vector mVehicleWindingIndices; + + VectorPtr mStaticMeshes; + + U16 mSearchTag; + Vector mMatInstCleanupList; + + //-------------------------------------- Private interface + private: + +#ifndef TORQUE_SHIPPING + GFXShaderRef mDebugShader; + GFXTexHandle mDebugTexture; + + GFXStateBlockRef mInteriorDebugNoneSB; + GFXStateBlockRef mInteriorDebugPortalSB; + GFXStateBlockRef mInteriorDebugTextureSB; + GFXStateBlockRef mInteriorDebugTwoTextureSB; + + // Debug Render ConstantBuffers + GFXShaderConstBufferRef mDebugShaderConsts; + + GFXShaderConstHandle* mDebugShaderModelViewSC; + GFXShaderConstHandle* mDebugShaderShadeColorSC; +#endif + + const char* getName(const U32 nameIndex) const; + static const char* getLightTypeString(const LightType); + S32 getZoneForPoint(const Point3F&) const; + +#ifndef TORQUE_SHIPPING + void debugRender(const ZoneVisDeterminer& zoneVis, SceneData &sgData, InteriorInstance *intInst, MatrixF& modelview); + // render debug utility functions + void preDebugRender(); + void debugDefaultRender(const ZoneVisDeterminer& zoneVis, SceneData &sgData, InteriorInstance *intInst); + // show all surfaces with flag set as color c + void debugShowSurfaceFlag(const ZoneVisDeterminer& zoneVis, const U32 flag, const ColorF& c); + // render brushes + void debugNormalRenderLines(const ZoneVisDeterminer& zoneVis); + // next 4 use debugShowSurfaceFlag to show surface info + void debugShowDetail(const ZoneVisDeterminer& zoneVis); + void debugShowAmbiguous(const ZoneVisDeterminer& zoneVis); + void debugShowOrphan(const ZoneVisDeterminer& zoneVis); + void debugShowOutsideVisible(const ZoneVisDeterminer& zoneVis); + void debugShowPortalZones(const ZoneVisDeterminer& zoneVis); + void debugRenderPortals(); + void debugShowCollisionFans(const ZoneVisDeterminer& zoneVis); + void debugShowStrips(const ZoneVisDeterminer& zoneVis); + void debugShowDetailLevel(const ZoneVisDeterminer& zoneVis); + void debugShowHullSurfaces(); + void debugShowNullSurfaces(const ZoneVisDeterminer& zoneVis, SceneData &sgData, InteriorInstance *intInst); + void debugShowVehicleHullSurfaces(const ZoneVisDeterminer& zoneVis, SceneData &sgData, InteriorInstance *intInst); + void debugShowTexturesOnly(const ZoneVisDeterminer& zoneVis, SceneData &sgData, InteriorInstance *intInst); + void debugShowLightmaps(const ZoneVisDeterminer& zoneVis, SceneData &sgData, InteriorInstance *intInst); + void debugShowLargeTextures(const ZoneVisDeterminer& zoneVis, SceneData &sgData, InteriorInstance *intInst); +// void debugShowVertexColors(MaterialList* pMaterials); +#endif + + public: + void collisionFanFromSurface(const Surface&, U32* fan, U32* numIndices) const; + void fillSurfaceTexMats(); + void createZoneVBs(); + void cloneMatInstances(); + void initMatInstances(); + void createReflectPlanes(); + + private: + void fullWindingFromSurface(const Surface&, U32* fan, U32* numIndices) const; + bool projectClipAndBoundFan(U32 fanIndex, F64* pResult); + void zoneTraversal(S32 baseZone, const bool flipClipPlanes); + void createZoneRectVectors(); + void destroyZoneRectVectors(); + void traverseZone(const RectD* inRects, const U32 numInputRects, U32 currZone, Vector& zoneStack); + + void fillVertex( GFXVertexPNTTB &vert, Surface &surface, U32 surfaceIndex ); + void getTexMat(U32 surfaceIndex, U32 pointOffset, Point3F& T, Point3F& N, Point3F& B); + + + void storeRenderNode( RenderNode &node, + ZoneRNList &RNList, + Vector &primInfoList, + Vector &indexList, + Vector &verts, + U32 &startIndex, + U32 &startVert ); + + void renderZoneNode( SceneRenderState *state, + RenderNode &node, + InteriorInstance *intInst, + SceneData &sgData, + MeshRenderInst *coreRi ); + + void renderReflectNode( SceneRenderState *state, + ReflectRenderNode &node, + InteriorInstance *intInst, + SceneData &sgData, + MeshRenderInst *coreRi ); + + void renderLights(SceneRenderState* state, InteriorInstance *intInst, SceneData &sgData, MeshRenderInst *coreRi, const ZoneVisDeterminer &zonevis); + + + /// Used to maintain point and normal relationship (which use + /// dissimilar index lookups) for verts when building vertex buffer. + struct VertexBufferTempIndex + { + U16 index; + Point3F normal; + + VertexBufferTempIndex() + { + index = 0; + normal.set(0.0f, 0.0f, 0.0f); + } + VertexBufferTempIndex(const U16 ind, const Point3F &norm) + { + index = ind; + normal = norm; + } + }; + + void storeSurfVerts( Vector &masterIndexList, + Vector &tempIndexList, + Vector &verts, + U32 numIndices, + Surface &surface, + U32 surfaceIndex ); + + public: + void purgeLODData(); + + void buildExportPolyList(OptimizedPolyList& polys, MatrixF* mat = NULL, Point3F* scale = NULL); +}; + +//------------------------------------------------------------------------------ +inline bool Interior::isBSPLeafIndex(U32 index) const +{ + if (mFileVersion >= 14) + return (index & 0x80000) != 0; + else + return (index & 0x8000) != 0; +} + +inline bool Interior::isBSPSolidLeaf(U32 index) const +{ + AssertFatal(isBSPLeafIndex(index) == true, "Error, only call for leaves!"); + + if (mFileVersion >= 14) + return (index & 0x40000) != 0; + else + return (index & 0x4000) != 0; +} + +inline bool Interior::isBSPEmptyLeaf(U32 index) const +{ + AssertFatal(isBSPLeafIndex(index) == true, "Error, only call for leaves!"); + + if (mFileVersion >= 14) + return (index & 0x40000) == 0; + else + return (index & 0x4000) == 0; +} + +inline U16 Interior::getBSPSolidLeafIndex(U32 index) const +{ + AssertFatal(isBSPSolidLeaf(index) == true, "Error, only call for leaves!"); + + if (mFileVersion >= 14) + return U16(index & ~0xC0000); + else + return U16(index & ~0xC000); +} + +inline U16 Interior::getBSPEmptyLeafZone(U32 index) const +{ + AssertFatal(isBSPEmptyLeaf(index) == true, "Error, only call for leaves!"); + + if (mFileVersion >= 14) + return U16(index & ~0xC0000); + else + return U16(index & ~0xC000); +} + +inline const PlaneF& Interior::getPlane(U16 index) const +{ + AssertFatal(U32(index & ~0x8000) < mPlanes.size(), + "Interior::getPlane: planeIndex out of range"); + + return mPlanes[index & ~0x8000]; +} + +inline PlaneF Interior::getFlippedPlane(const U16 index) const +{ + PlaneF plane = getPlane(index); + if(Interior::planeIsFlipped(index)) + plane.neg(); + + return plane; +} + +inline U16 Interior::getPlaneIndex(U16 index) +{ + return U16(index & ~0x8000); +} + +inline bool Interior::planeIsFlipped(U16 index) +{ + return (index >> 15)!=0; +} + +inline bool Interior::areEqualPlanes(U16 o, U16 t) const +{ + return (o & ~0x8000) == (t & ~0x8000); +} + +inline const Point3F Interior::getPointNormal(const U32 surfaceIndex, const U32 pointOffset) const +{ + Surface rSurface = mSurfaces[surfaceIndex]; + + Point3F normal(0.0f, 0.0f, 0.0f); + + if (mFileVersion >= 11) + { + U32 texMatIndex = mTexMatIndices[rSurface.windingStart + pointOffset]; + TexMatrix texMat = mTexMatrices[texMatIndex]; + + if (texMat.N > -1) + normal = mNormals[texMat.N]; + } + else + normal = getFlippedPlane(rSurface.planeIndex); + + return normal; +} + +inline U32 Interior::getDetailLevel() const +{ + return mDetailLevel; +} + +inline U32 Interior::getMinPixels() const +{ + return mMinPixels; +} + +inline const Box3F& Interior::getBoundingBox() const +{ + return mBoundingBox; +} + +inline S32 Interior::getNumZones() const +{ + return mZones.size(); +} + +inline bool Interior::isNullSurfaceIndex(const U32 index) const +{ + return (index & 0x80000000) != 0; +} + +inline bool Interior::isVehicleNullSurfaceIndex(const U32 index) const +{ + return (index & 0x40000000) != 0; +} + +inline U32 Interior::getNullSurfaceIndex(const U32 index) const +{ + AssertFatal(isNullSurfaceIndex(index), "Not a proper index!"); + AssertFatal(!isVehicleNullSurfaceIndex(index), "Not a proper index"); + return (index & ~0x80000000); +} + +inline U32 Interior::getVehicleNullSurfaceIndex(const U32 index) const +{ + AssertFatal(isVehicleNullSurfaceIndex(index), "Not a proper index!"); + return (index & ~(0x80000000 | 0x40000000)); +} + +inline const char* Interior::getLightTypeString(const LightType type) +{ + switch (type) { + case AmbientLooping: + return "AmbientLooping"; + case AmbientFlicker: + return "AmbientFlicker"; + case TriggerableLoop: + return "TriggerableLoop"; + case TriggerableFlicker: + return "TriggerableFlicker"; + case TriggerableRamp: + return "TriggerableRamp"; + + default: + return ""; + } +} + +inline const char* Interior::getName(const U32 nameIndex) const +{ + return &mNameBuffer[nameIndex]; +} + +inline const U32 Interior::getSurfaceCount() const +{ + return(mSurfaces.size()); +} + +inline const Interior::Surface & Interior::getSurface(const U32 surface) const +{ + AssertFatal(surface < mSurfaces.size(), "invalid index"); + return(mSurfaces[surface]); +} + +inline const U32 Interior::getStaticMeshCount() const +{ + return mStaticMeshes.size(); +} + +inline const InteriorSimpleMesh *Interior::getStaticMesh(const U32 index) const +{ + AssertFatal(index < mStaticMeshes.size(), "invalid index"); + return mStaticMeshes[index]; +} + +inline const U32 Interior::getNormalLMapIndex(const U32 surface) const +{ + AssertFatal(surface < mNormalLMapIndices.size(), "invalid index"); + return(mNormalLMapIndices[surface]); +} + +inline const U32 Interior::getAlarmLMapIndex(const U32 surface) const +{ + AssertFatal(surface < mAlarmLMapIndices.size(), "invalid index"); + return(mAlarmLMapIndices[surface]); +} + +inline const U32 Interior::getWinding(const U32 index) const +{ + AssertFatal(index < mWindings.size(), "invalid index"); + return(mWindings[index]); +} + +inline const Point3F & Interior::getPoint(const U32 index) const +{ + AssertFatal(index < mPoints.size(), "invalid index"); + return(mPoints[index].point); +} + +inline const Interior::TexGenPlanes & Interior::getLMTexGenEQ(const U32 index) const +{ + AssertFatal(index < mLMTexGenEQs.size(), "invalid index"); + return(mLMTexGenEQs[index]); +} + +inline const Interior::TexGenPlanes & Interior::getTexGenEQ(const U32 index) const +{ + AssertFatal(index < mTexGenEQs.size(), "invalid index"); + return(mTexGenEQs[index]); +} + +inline bool Interior::hasAlarmState() const +{ + return(mHasAlarmState); +} + +inline const bool Interior::isSurfaceOutsideVisible(U32 surface) const +{ + AssertFatal(surface < mSurfaces.size(), "Interior::isSurfaceOutsideVisible: Invalid surface index"); + return ((mSurfaces[surface].surfaceFlags & SurfaceOutsideVisible)!=0); +} + +inline const U32 Interior::getWindingCount() const +{ + return(mWindings.size()); +} + +#endif //_INTERIOR_H_ diff --git a/Engine/source/interior/interiorCollision.cpp b/Engine/source/interior/interiorCollision.cpp new file mode 100644 index 000000000..1e9260c8c --- /dev/null +++ b/Engine/source/interior/interiorCollision.cpp @@ -0,0 +1,1775 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "interior/interior.h" +#include "math/mSphere.h" +#include "scene/sceneObject.h" +#include "collision/abstractPolyList.h" + +#include "core/frameAllocator.h" +#include "platform/profiler.h" + +namespace { + +//-------------------------------------- +// Custom on plane version to reduce numerical inaccuracies in the GJK stuff. +// copied from terrCollision.cc +inline bool isOnPlane(const Point3F& p, const PlaneF& plane) +{ + F32 dist = mDot(plane,p) + plane.d; + return mFabs(dist) < 0.1f; +} + +} // namespace {} + + +//-------------------------------------------------------------------------- +//-------------------------------------- SurfaceHash +const U32 csgHashSize = 4096; +class SurfaceHash +{ + private: + U32 hash(U32 i) { return i & (csgHashSize - 1); } + + public: + U32 mSurfaceArray[csgHashSize]; + U32 mNumSurfaces; + + U32 mHashTable[csgHashSize]; + + public: + SurfaceHash() + { + AssertFatal(isPow2(csgHashSize), "Error, size must be power of 2"); + } + + void clear() + { + dMemset(mHashTable, 0xFF, sizeof(mHashTable)); + mNumSurfaces = 0; + } + + void insert(U32 surface); +}; + +inline void SurfaceHash::insert(U32 surface) +{ + AssertFatal(surface != 0xFFFFFFFF, "Hm, bad assumption. 0xFFFFFFFF is a valid surface index?"); + if (mNumSurfaces >= csgHashSize) + { + AssertFatal(false, "Error, exceeded surfaceCount restriction!"); + return; + } + + U32 probe = hash(surface); + U32 initialProbe = probe; + while (mHashTable[probe] != 0xFFFFFFFF) + { + // If it's already in the table, bail. + if (mHashTable[probe] == surface) + return; + + probe = (probe + 1) % csgHashSize; + AssertFatal(probe != initialProbe, "Hm, wraparound?"); + } + + // If we're here, then the probe failed, and the index isn't in the hash + // table + mHashTable[probe] = surface; + mSurfaceArray[mNumSurfaces++] = surface; +} + + +class InteriorPolytope +{ + // Convex Polyhedron + public: + struct Vertex + { + Point3F point; + // Temp BSP clip info + S32 side; + }; + struct Edge + { + S32 vertex[2]; + S32 face[2]; + S32 next; + }; + struct Face + { + S32 original; + // Temp BSP clip info + S32 vertex; + }; + struct Volume + { + S32 edgeList; + }; + struct StackElement + { + S32 edgeList; + U32 popCount; + U32 nodeIndex; + }; + + typedef Vector EdgeList; + typedef Vector FaceList; + typedef Vector VertexList; + typedef Vector VolumeList; + typedef Vector VolumeStack; + + // + S32 sideCount; + EdgeList mEdgeList; + FaceList mFaceList; + VertexList mVertexList; + VolumeList mVolumeList; + + public: + // + InteriorPolytope(); + void clear(); + bool intersect(const PlaneF& plane,const Point3F& sp,const Point3F& ep); + void buildBox(const Box3F& box, const MatrixF& transform, const Point3F& scale); + inline bool didIntersect() { return mVolumeList.size() > 1; } +}; + +//---------------------------------------------------------------------------- +// Box should be axis aligned in the transform space provided. + +InteriorPolytope::InteriorPolytope() +{ + mVertexList.reserve(256); + mFaceList.reserve(200); + mEdgeList.reserve(100); + mVolumeList.reserve(20); + sideCount = 0; +} + +void InteriorPolytope::clear() +{ + mVertexList.clear(); + mFaceList.clear(); + mEdgeList.clear(); + mVolumeList.clear(); + sideCount = 0; +} + +void InteriorPolytope::buildBox(const Box3F& box, const MatrixF& transform, const Point3F&) +{ + // Initial vertices + mVertexList.setSize(8); + mVertexList[0].point = Point3F(box.minExtents.x, box.minExtents.y, box.minExtents.z); + mVertexList[1].point = Point3F(box.minExtents.x, box.maxExtents.y, box.minExtents.z); + mVertexList[2].point = Point3F(box.maxExtents.x, box.maxExtents.y, box.minExtents.z); + mVertexList[3].point = Point3F(box.maxExtents.x, box.minExtents.y, box.minExtents.z); + mVertexList[4].point = Point3F(box.minExtents.x, box.minExtents.y, box.maxExtents.z); + mVertexList[5].point = Point3F(box.minExtents.x, box.maxExtents.y, box.maxExtents.z); + mVertexList[6].point = Point3F(box.maxExtents.x, box.maxExtents.y, box.maxExtents.z); + mVertexList[7].point = Point3F(box.maxExtents.x, box.minExtents.y, box.maxExtents.z); + S32 i; + for (i = 0; i < 8; i++) + { + transform.mulP(mVertexList[i].point); + mVertexList[i].side = 0; + } + + // Initial faces + mFaceList.setSize(6); + for (S32 f = 0; f < 6; f++) + { + Face& face = mFaceList[f]; + face.original = true; + face.vertex = 0; + } + + // Initial edges + mEdgeList.setSize(12); + Edge* edge = mEdgeList.begin(); + S32 nextEdge = 0; + for (i = 0; i < 4; i++) + { + S32 n = (i == 3)? 0: i + 1; + S32 p = (i == 0)? 3: i - 1; + edge->vertex[0] = i; + edge->vertex[1] = n; + edge->face[0] = i; + edge->face[1] = 4; + edge->next = ++nextEdge; + edge++; + edge->vertex[0] = 4 + i; + edge->vertex[1] = 4 + n; + edge->face[0] = i; + edge->face[1] = 5; + edge->next = ++nextEdge; + edge++; + edge->vertex[0] = i; + edge->vertex[1] = 4 + i; + edge->face[0] = i; + edge->face[1] = p; + edge->next = ++nextEdge; + edge++; + } + edge[-1].next = -1; + + // Volume + mVolumeList.setSize(1); + Volume& volume = mVolumeList.last(); + volume.edgeList = 0; + sideCount = 0; +} + +bool InteriorPolytope::intersect(const PlaneF& plane,const Point3F& sp,const Point3F& ep) +{ + // If den == 0 then the line and plane are parallel. + F32 den; + Point3F dt = ep - sp; + if ((den = plane.x * dt.x + plane.y * dt.y + plane.z * dt.z) == 0.0f) + return false; + + // Save the values in sp since the memory may go away after the increment + F32 sp_x = sp.x; + F32 sp_y = sp.y; + F32 sp_z = sp.z; + + mVertexList.increment(); + Vertex& v = mVertexList.last(); + F32 s = -(plane.x * sp_x + plane.y * sp_y + plane.z * sp_z + plane.d) / den; + v.point.x = sp_x + dt.x * s; + v.point.y = sp_y + dt.y * s; + v.point.z = sp_z + dt.z * s; + v.side = 0; + return true; +} + + +//-------------------------------------------------------------------------- +void Interior::collisionFanFromSurface(const Surface& rSurface, U32* fanIndices, U32* numIndices) const +{ + U32 tempIndices[32]; + + tempIndices[0] = 0; + U32 idx = 1; + U32 i; + + for (i = 1; i < rSurface.windingCount; i += 2) + tempIndices[idx++] = i; + + for (i = ((rSurface.windingCount - 1) & (~0x1)); i > 0; i -= 2) + tempIndices[idx++] = i; + + idx = 0; + for (i = 0; i < rSurface.windingCount; i++) + { + if (rSurface.fanMask & (1 << i)) + { + fanIndices[idx++] = mWindings[rSurface.windingStart + tempIndices[i]]; + } + } + *numIndices = idx; +} + +void Interior::fullWindingFromSurface(const Surface& rSurface, U32* fanIndices, U32* numIndices) const +{ + U32 tempIndices[32]; + + tempIndices[0] = 0; + U32 idx = 1; + U32 i; + + for (i = 1; i < rSurface.windingCount; i += 2) + tempIndices[idx++] = i; + + for (i = ((rSurface.windingCount - 1) & (~0x1)); i > 0; i -= 2) + tempIndices[idx++] = i; + + idx = 0; + for (i = 0; i < rSurface.windingCount; i++) + fanIndices[idx++] = mWindings[rSurface.windingStart + tempIndices[i]]; + + *numIndices = idx; +} + + +bool Interior::castRay_r(const U32 node, + const U16 planeIndex, + const Point3F& s, + const Point3F& e, + RayInfo* info) +{ + if (isBSPLeafIndex(node) == false) + { + const IBSPNode& rNode = mBSPNodes[node]; + const PlaneF& rPlane = getPlane(rNode.planeIndex); + + const PlaneF::Side sSide = rPlane.whichSide(s); + const PlaneF::Side eSide = rPlane.whichSide(e); + + switch (PlaneSwitchCode(sSide, eSide)) + { + case PlaneSwitchCode(PlaneF::Front, PlaneF::Front): + case PlaneSwitchCode(PlaneF::Front, PlaneF::On): + case PlaneSwitchCode(PlaneF::On, PlaneF::Front): + return castRay_r(rNode.frontIndex, planeIndex, s, e, info); + break; + + case PlaneSwitchCode(PlaneF::On, PlaneF::Back): + case PlaneSwitchCode(PlaneF::Back, PlaneF::On): + case PlaneSwitchCode(PlaneF::Back, PlaneF::Back): + return castRay_r(rNode.backIndex, planeIndex, s, e, info); + break; + + case PlaneSwitchCode(PlaneF::On, PlaneF::On): + // Line lies on the plane + if (isBSPLeafIndex(rNode.backIndex) == false) + { + if (castRay_r(rNode.backIndex, planeIndex, s, e, info)) + return true; + } + + if (isBSPLeafIndex(rNode.frontIndex) == false) + { + if (castRay_r(rNode.frontIndex, planeIndex, s, e, info)) + return true; + } + + return false; + break; + + case PlaneSwitchCode(PlaneF::Front, PlaneF::Back): + { + Point3F ip; + F32 intersectT = rPlane.intersect(s, e); + AssertFatal(intersectT != PARALLEL_PLANE, "Error, this should never happen in this case!"); + ip.interpolate(s, e, intersectT); + if (castRay_r(rNode.frontIndex, planeIndex, s, ip, info)) + return true; + return castRay_r(rNode.backIndex, rNode.planeIndex, ip, e, info); + } + break; + + case PlaneSwitchCode(PlaneF::Back, PlaneF::Front): + { + Point3F ip; + F32 intersectT = rPlane.intersect(s, e); + AssertFatal(intersectT != PARALLEL_PLANE, "Error, this should never happen in this case!"); + ip.interpolate(s, e, intersectT); + if (castRay_r(rNode.backIndex, planeIndex, s, ip, info)) + return true; + return castRay_r(rNode.frontIndex, rNode.planeIndex, ip, e, info); + } + break; + + default: + AssertFatal(false, "Misunderstood switchCode in Interior::castRay_r"); + return false; + } + } + + if (isBSPSolidLeaf(node)) + { + // DMM: Set material info here.. material info? hahaha + + info->point = s; + + if (planeIndex != U16(-1)) + { + const PlaneF& rPlane = getPlane(planeIndex); + info->normal = rPlane; + if (planeIsFlipped(planeIndex)) + { + info->normal.neg(); + if (rPlane.whichSide(e) == PlaneF::Back) + info->normal.neg(); + } + else + { + if (rPlane.whichSide(e) == PlaneF::Front) + info->normal.neg(); + } + } + else + { + // Point started in solid; + if (s == e) + { + info->normal.set(0.0f, 0.0f, 1.0f); + } + else + { + info->normal = s - e; + info->normal.normalize(); + } + } + + // ok.. let's get it on! get the face that was hit + info->face = U32(-1); + + U32 numStaticSurfaces = 0; + + const IBSPLeafSolid & rLeaf = mBSPSolidLeaves[getBSPSolidLeafIndex(node)]; + for(U32 i = 0; i < rLeaf.surfaceCount; i++) + { + U32 surfaceIndex = mSolidLeafSurfaces[rLeaf.surfaceIndex + i]; + if(isNullSurfaceIndex(surfaceIndex)) + { + const NullSurface & rSurface = mNullSurfaces[getNullSurfaceIndex(surfaceIndex)]; + if (rSurface.surfaceFlags & SurfaceStaticMesh) + numStaticSurfaces++; + + continue; + } + + const Surface & rSurface = mSurfaces[surfaceIndex]; + if(!areEqualPlanes(rSurface.planeIndex, planeIndex)) + continue; + + PlaneF plane = getPlane(rSurface.planeIndex); + if(planeIsFlipped(rSurface.planeIndex)) + plane.neg(); + + // unfan this surface + U32 winding[32]; + U32 windingCount; + collisionFanFromSurface(rSurface, winding, &windingCount); + + // inside this surface? + bool inside = true; + for(U32 j = 0; inside && (j < windingCount); j++) + { + U32 k = (j+1) % windingCount; + + Point3F vec1 = mPoints[winding[k]].point - mPoints[winding[j]].point; + Point3F vec2 = info->point - mPoints[winding[j]].point; + + Point3F cross; + mCross(vec2, vec1, &cross); + + if(mDot(plane, cross) < 0.f) + inside = false; + } + + if(inside) + { + info->face = surfaceIndex; + info->material = mMaterialList->getMaterialInst( rSurface.textureIndex ); + break; + } + } + + if (Interior::smLightingCastRays && numStaticSurfaces == rLeaf.surfaceCount && numStaticSurfaces > 0) + return false; + + return true; + } + return false; +} + +bool Interior::castRay(const Point3F& s, const Point3F& e, RayInfo* info) +{ + // DMM: Going to need normal here eventually. + bool hit = castRay_r(0, U16(-1), s, e, info); + if (hit) + { + Point3F vec = e - s; + F32 len = vec.len(); + if (len < 1e-6f) + { + info->t = 0.0f; + } + else + { + vec /= len; + info->t = mDot(info->point - s, vec) / len; + } + } + + AssertFatal(!hit || (info->normal.z == info->normal.z), "NaN returned from castRay.\n\nPlease talk to DMM if you are running this in the debugger"); + + return hit; +} + +void Interior::buildPolyList_r(InteriorPolytope& polytope, + SurfaceHash& hash) +{ + // Submit the first volume of the poly tope to the bsp tree + static InteriorPolytope::VolumeStack stack; + stack.reserve(256); + stack.setSize(1); + stack.last().edgeList = polytope.mVolumeList[0].edgeList; + stack.last().nodeIndex = 0; + stack.last().popCount = 0; + + static Vector collPlanes; + collPlanes.reserve(64); + collPlanes.clear(); + + while (stack.empty() == false) + { + InteriorPolytope::StackElement volume = stack.last(); + stack.pop_back(); + + if (isBSPLeafIndex(volume.nodeIndex)) + { + if (isBSPSolidLeaf(volume.nodeIndex)) + { + // Export the polys from this node. + const IBSPLeafSolid& rLeaf = mBSPSolidLeaves[getBSPSolidLeafIndex(volume.nodeIndex)]; + for (U32 i = 0; i < rLeaf.surfaceCount; i++) + { + U32 surfaceIndex = mSolidLeafSurfaces[rLeaf.surfaceIndex + i]; + if (isNullSurfaceIndex(surfaceIndex)) + { + // Is a NULL surface + const NullSurface& rSurface = mNullSurfaces[getNullSurfaceIndex(surfaceIndex)]; + for (U32 j = 0; j < collPlanes.size(); j++) + { + if (areEqualPlanes(rSurface.planeIndex, collPlanes[j]) == true) + { + hash.insert(surfaceIndex); + break; + } + } + } + else + { + const Surface& rSurface = mSurfaces[surfaceIndex]; + for (U32 j = 0; j < collPlanes.size(); j++) + { + if (areEqualPlanes(rSurface.planeIndex, collPlanes[j]) == true) + { + hash.insert(surfaceIndex); + break; + } + } + } + } + } + + if (volume.popCount) + for (U32 i = 0; i < volume.popCount; i++) + collPlanes.pop_back(); + + continue; + } + + const IBSPNode& rNode = mBSPNodes[volume.nodeIndex]; + + // New front and back faces + polytope.mFaceList.increment(2); + InteriorPolytope::Face& frontFace = polytope.mFaceList[polytope.mFaceList.size() - 1]; + InteriorPolytope::Face& backFace = polytope.mFaceList[polytope.mFaceList.size() - 2]; + + backFace.original = frontFace.original = false; + backFace.vertex = frontFace.vertex = 0; + + // New front and back volumes + InteriorPolytope::StackElement frontVolume,backVolume; + frontVolume.edgeList = backVolume.edgeList = -1; + + PlaneF plane = getPlane(rNode.planeIndex); + if (planeIsFlipped(rNode.planeIndex)) + plane.neg(); + + S32 startVertex = polytope.mVertexList.size(); + + // Test & clip all the edges + S32 sideBase = ++polytope.sideCount << 1; + for (S32 i = volume.edgeList; i >= 0; i = polytope.mEdgeList[i].next) + { + + // Copy into tmp first to avoid problems with the array. + InteriorPolytope::Edge edge = polytope.mEdgeList[i]; + + InteriorPolytope::Vertex& v0 = polytope.mVertexList[edge.vertex[0]]; + if (v0.side < sideBase) + v0.side = sideBase + ((plane.distToPlane(v0.point) >= 0)? 0: 1); + + InteriorPolytope::Vertex& v1 = polytope.mVertexList[edge.vertex[1]]; + if (v1.side < sideBase) + v1.side = sideBase + ((plane.distToPlane(v1.point) >= 0)? 0: 1); + + if (v0.side != v1.side) + { + S32 s = v0.side - sideBase; + polytope.intersect(plane,v0.point,v1.point); + + // Split the edge into each volume + polytope.mEdgeList.increment(2); + InteriorPolytope::Edge& e0 = polytope.mEdgeList.last(); + e0.next = frontVolume.edgeList; + frontVolume.edgeList = polytope.mEdgeList.size() - 1; + + InteriorPolytope::Edge& e1 = *(&e0 - 1); + e1.next = backVolume.edgeList; + backVolume.edgeList = frontVolume.edgeList - 1; + + e0.vertex[0] = edge.vertex[s]; + e1.vertex[0] = edge.vertex[s ^ 1]; + e0.vertex[1] = e1.vertex[1] = polytope.mVertexList.size() - 1; + e0.face[0] = e1.face[0] = edge.face[0]; + e0.face[1] = e1.face[1] = edge.face[1]; + + // Add new edges on the plane, one to each volume + for (S32 f = 0; f < 2; f++) + { + InteriorPolytope::Face& face = polytope.mFaceList[edge.face[f]]; + if (face.vertex < startVertex) + { + face.vertex = polytope.mVertexList.size() - 1; + } + else + { + polytope.mEdgeList.increment(2); + InteriorPolytope::Edge& e0 = polytope.mEdgeList.last(); + e0.next = frontVolume.edgeList; + frontVolume.edgeList = polytope.mEdgeList.size() - 1; + + InteriorPolytope::Edge& e1 = *(&e0 - 1); + e1.next = backVolume.edgeList; + backVolume.edgeList = frontVolume.edgeList - 1; + + e1.vertex[0] = e0.vertex[0] = face.vertex; + e1.vertex[1] = e0.vertex[1] = polytope.mVertexList.size() - 1; + e1.face[0] = e0.face[0] = edge.face[f]; + e1.face[1] = polytope.mFaceList.size() - 1; + e0.face[1] = e1.face[1] - 1; + } + } + } + else + { + if (v0.side == sideBase) + { + polytope.mEdgeList.push_back(edge); + InteriorPolytope::Edge& ne = polytope.mEdgeList.last(); + ne.next = frontVolume.edgeList; + frontVolume.edgeList = polytope.mEdgeList.size() - 1; + } + else + { + polytope.mEdgeList.push_back(edge); + InteriorPolytope::Edge& ne = polytope.mEdgeList.last(); + ne.next = backVolume.edgeList; + backVolume.edgeList = polytope.mEdgeList.size() - 1; + } + } + } + + // Push the front and back nodes + if (frontVolume.edgeList >= 0 && backVolume.edgeList >= 0) + { + collPlanes.push_back(rNode.planeIndex); + + frontVolume.nodeIndex = rNode.frontIndex; + frontVolume.popCount = volume.popCount + 1; + stack.push_back(frontVolume); + + backVolume.nodeIndex = rNode.backIndex; + backVolume.popCount = 0; + stack.push_back(backVolume); + } + else if (frontVolume.edgeList >= 0) + { + frontVolume.nodeIndex = rNode.frontIndex; + frontVolume.popCount = volume.popCount; + stack.push_back(frontVolume); + } + else if (backVolume.edgeList >= 0) + { + backVolume.nodeIndex = rNode.backIndex; + backVolume.popCount = volume.popCount; + stack.push_back(backVolume); + } + else + { + // Pop off our own planes... + if (volume.popCount) + for (U32 i = 0; i < volume.popCount; i++) + collPlanes.pop_back(); + } + } + + AssertFatal(collPlanes.size() == 0, "Unbalanced stack!"); +} + +bool Interior::buildPolyList(AbstractPolyList* list, + const Box3F& box, + const MatrixF& transform, + const Point3F& scale) +{ + Box3F testBox; + MatrixF toItr; + if (!list->getMapping(&toItr,&testBox)) + { + // this list doesn't do this, use world space box and transform + testBox = box; + toItr = transform; + } + + // construct an interior space box from testBox and toItr + // source space may be world space, or may be something else... + // that's up to the list + // Note: transform maps to interior, but scale is itr -> world... + // that is why we divide by scale below... + F32 * f = toItr; + + F32 xx = mFabs(f[0]); F32 xy = mFabs(f[4]); F32 xz = mFabs(f[8]); + F32 yx = mFabs(f[1]); F32 yy = mFabs(f[5]); F32 yz = mFabs(f[9]); + F32 zx = mFabs(f[2]); F32 zy = mFabs(f[6]); F32 zz = mFabs(f[10]); + + F32 xlen = testBox.maxExtents.x - testBox.minExtents.x; + F32 ylen = testBox.maxExtents.y - testBox.minExtents.y; + F32 zlen = testBox.maxExtents.z - testBox.minExtents.z; + + F32 invScalex = 1.0f/scale.x; + F32 invScaley = 1.0f/scale.y; + F32 invScalez = 1.0f/scale.z; + + F32 xrad = (xx * xlen + yx * ylen + zx * zlen) * invScalex; + F32 yrad = (xy * xlen + yy * ylen + zy * zlen) * invScaley; + F32 zrad = (xz * xlen + yz * ylen + zz * zlen) * invScalez; + + Box3F interiorBox; + testBox.getCenter(&interiorBox.minExtents); + toItr.mulP(interiorBox.minExtents); + + interiorBox.minExtents.x *= invScalex; + interiorBox.minExtents.y *= invScaley; + interiorBox.minExtents.z *= invScalez; + + interiorBox.maxExtents = interiorBox.minExtents; + + interiorBox.minExtents.x -= xrad; + interiorBox.minExtents.y -= yrad; + interiorBox.minExtents.z -= zrad; + + interiorBox.maxExtents.x += xrad; + interiorBox.maxExtents.y += yrad; + interiorBox.maxExtents.z += zrad; + + U32 waterMark = FrameAllocator::getWaterMark(); + + U16* hulls = (U16*)FrameAllocator::alloc(mConvexHulls.size() * sizeof(U16)); + U32 numHulls = 0; + + getIntersectingHulls(interiorBox,hulls, &numHulls); + + if (numHulls == 0) + { + FrameAllocator::setWaterMark(waterMark); + return false; + } + + // we've found all the hulls that intersect the lists interior space bounding box... + // now cull out those hulls which don't intersect the oriented bounding box... + + Point3F radii = testBox.maxExtents - testBox.minExtents; + radii *= 0.5f; + radii.x *= invScalex; + radii.y *= invScaley; + radii.z *= invScalez; + + // adjust toItr transform so that origin of source space is box center + // Note: center of interior box will be = to transformed center of testBox + Point3F center = interiorBox.minExtents + interiorBox.maxExtents; + center *= 0.5f; + toItr.setColumn(3,center); // (0,0,0) now goes where box center used to... + + for (S32 i=0; ibegin(0, rSurface.planeIndex); + for (U32 k = 0; k < rSurface.windingCount; k++) + { + array[k] = list->addPoint(mPoints[mWindings[rSurface.windingStart + k]].point); + list->vertex(array[k]); + } + + list->plane(getFlippedPlane(rSurface.planeIndex)); + list->end(); + } + else + { + const Interior::Surface& rSurface = mSurfaces[surfaceIndex]; + U32 array[32]; + U32 fanVerts[32]; + U32 numVerts; + + collisionFanFromSurface(rSurface, fanVerts, &numVerts); + + list->begin(0, rSurface.planeIndex); + for (U32 k = 0; k < numVerts; k++) + { + array[k] = list->addPoint(mPoints[fanVerts[k]].point); + list->vertex(array[k]); + } + list->plane(getFlippedPlane(rSurface.planeIndex)); + list->end(); + } + } + } + + FrameAllocator::setWaterMark(waterMark); + return !list->isEmpty(); +} + + +//--------------------------------------------------------------------------------- +//-------------------------------------- Zone scan. Not really collision, but hey. +// +struct Edge +{ + U16 p1; + U16 p2; + Edge(U16 o, U16 t) : p1(o), p2(t) { } +}; + +struct EdgeList +{ + + Vector points; + Vector edges; +}; + +void Interior::scanZoneNew(InteriorPolytope& polytope, + U16* zones, + U32* numZones) +{ + PROFILE_START(InteriorScanZoneNew); + // Submit the first volume of the poly tope to the bsp tree + static InteriorPolytope::VolumeStack stack; + stack.reserve(128); + stack.setSize(1); + stack.last().edgeList = polytope.mVolumeList[0].edgeList; + stack.last().nodeIndex = 0; + stack.last().popCount = 0; + + static Vector collPlanes; + collPlanes.reserve(64); + collPlanes.clear(); + + while (stack.empty() == false) + { + + InteriorPolytope::StackElement volume = stack.last(); + stack.pop_back(); + + if (isBSPLeafIndex(volume.nodeIndex)) + { + if (isBSPEmptyLeaf(volume.nodeIndex)) + { + U16 zone = getBSPEmptyLeafZone(volume.nodeIndex); + if (zone != 0x0FFF) + { + bool insert = true; + for (U32 i = 0; i < *numZones; i++) + { + if (zones[i] == zone) + { + insert = false; + break; + } + } + if (insert) + { + zones[*numZones] = zone; + (*numZones)++; + } + } + } + + if (volume.popCount) + for (U32 i = 0; i < volume.popCount; i++) + collPlanes.pop_back(); + + continue; + } + + const IBSPNode& rNode = mBSPNodes[volume.nodeIndex]; + if ((rNode.terminalZone & U16(0x8000)) != 0) + { + // Hah! we don't need to search any further + U16 zone = rNode.terminalZone & (~0x8000); + if (zone != 0x7FFF && zone != 0x0FFF) + { + bool insert = true; + for (U32 i = 0; i < *numZones; i++) + { + if (zones[i] == zone) + { + insert = false; + break; + } + } + if (insert) + { + zones[*numZones] = zone; + (*numZones)++; + } + } + + if (volume.popCount) + for (U32 i = 0; i < volume.popCount; i++) + collPlanes.pop_back(); + + continue; + } + + // New front and back faces + polytope.mFaceList.increment(2); + InteriorPolytope::Face& frontFace = polytope.mFaceList.last(); + InteriorPolytope::Face& backFace = *(&frontFace - 1); + + backFace.original = frontFace.original = false; + backFace.vertex = frontFace.vertex = 0; + + // New front and back volumes + InteriorPolytope::StackElement frontVolume,backVolume; + frontVolume.edgeList = backVolume.edgeList = -1; + + PlaneF plane = getFlippedPlane(rNode.planeIndex); + + S32 startVertex = polytope.mVertexList.size(); + + // Test & clip all the edges + S32 sideBase = ++polytope.sideCount << 1; + AssertFatal(sideBase != 0, "Well, crap."); + for (S32 i = volume.edgeList; i >= 0; i = polytope.mEdgeList[i].next) + { + // Copy into tmp first to avoid problems with the array. + InteriorPolytope::Edge edge = polytope.mEdgeList[i]; + + InteriorPolytope::Vertex& v0 = polytope.mVertexList[edge.vertex[0]]; + if (v0.side < sideBase) + v0.side = sideBase + ((plane.distToPlane(v0.point) >= 0)? 0: 1); + InteriorPolytope::Vertex& v1 = polytope.mVertexList[edge.vertex[1]]; + if (v1.side < sideBase) + v1.side = sideBase + ((plane.distToPlane(v1.point) >= 0)? 0: 1); + + if (v0.side != v1.side) + { + S32 s = v0.side - sideBase; + polytope.intersect(plane,v0.point,v1.point); + + // Split the edge into each volume + polytope.mEdgeList.increment(2); + InteriorPolytope::Edge& e0 = polytope.mEdgeList.last(); + e0.next = frontVolume.edgeList; + frontVolume.edgeList = polytope.mEdgeList.size() - 1; + + InteriorPolytope::Edge& e1 = *(&e0 - 1); + e1.next = backVolume.edgeList; + backVolume.edgeList = frontVolume.edgeList - 1; + + e0.vertex[0] = edge.vertex[s]; + e1.vertex[0] = edge.vertex[s ^ 1]; + e0.vertex[1] = e1.vertex[1] = polytope.mVertexList.size() - 1; + e0.face[0] = e1.face[0] = edge.face[0]; + e0.face[1] = e1.face[1] = edge.face[1]; + + // Add new edges on the plane, one to each volume + for (S32 f = 0; f < 2; f++) + { + InteriorPolytope::Face& face = polytope.mFaceList[edge.face[f]]; + if (face.vertex < startVertex) + { + face.vertex = polytope.mVertexList.size() - 1; + } + else + { + polytope.mEdgeList.increment(2); + InteriorPolytope::Edge& e0 = polytope.mEdgeList.last(); + e0.next = frontVolume.edgeList; + frontVolume.edgeList = polytope.mEdgeList.size() - 1; + + InteriorPolytope::Edge& e1 = *(&e0 - 1); + e1.next = backVolume.edgeList; + backVolume.edgeList = frontVolume.edgeList - 1; + + e1.vertex[0] = e0.vertex[0] = face.vertex; + e1.vertex[1] = e0.vertex[1] = polytope.mVertexList.size() - 1; + e1.face[0] = e0.face[0] = edge.face[f]; + e1.face[1] = polytope.mFaceList.size() - 1; + e0.face[1] = e1.face[1] - 1; + } + } + } + else + { + if (v0.side == sideBase) + { + polytope.mEdgeList.push_back(edge); + InteriorPolytope::Edge& ne = polytope.mEdgeList.last(); + ne.next = frontVolume.edgeList; + frontVolume.edgeList = polytope.mEdgeList.size() - 1; + } + else + { + polytope.mEdgeList.push_back(edge); + InteriorPolytope::Edge& ne = polytope.mEdgeList.last(); + ne.next = backVolume.edgeList; + backVolume.edgeList = polytope.mEdgeList.size() - 1; + } + } + } + + // Push the front and back nodes + if (frontVolume.edgeList >= 0 && backVolume.edgeList >= 0) + { + collPlanes.push_back(rNode.planeIndex); + + frontVolume.nodeIndex = rNode.frontIndex; + frontVolume.popCount = volume.popCount + 1; + stack.push_back(frontVolume); + + backVolume.nodeIndex = rNode.backIndex; + backVolume.popCount = 0; + stack.push_back(backVolume); + } + else if (frontVolume.edgeList >= 0) + { + frontVolume.nodeIndex = rNode.frontIndex; + frontVolume.popCount = volume.popCount; + stack.push_back(frontVolume); + } + else if (backVolume.edgeList >= 0) + { + backVolume.nodeIndex = rNode.backIndex; + backVolume.popCount = volume.popCount; + stack.push_back(backVolume); + } + else + { + // Pop off our own planes... + if (volume.popCount) + for (U32 i = 0; i < volume.popCount; i++) + collPlanes.pop_back(); + } + } + + AssertFatal(collPlanes.size() == 0, "Unbalanced stack!"); + PROFILE_END(); +} + +void Interior::scanZone_r(const U32 node, + const Point3F& center, + const Point3F& axisx, + const Point3F& axisy, + const Point3F& axisz, + U16* zones, + U32* numZones) +{ + if (isBSPLeafIndex(node) == false) + { + const IBSPNode& rNode = mBSPNodes[node]; + const PlaneF& rPlane = getPlane(rNode.planeIndex); + + PlaneF::Side side = rPlane.whichSideBox(center, axisx, axisy, axisz); + if (planeIsFlipped(rNode.planeIndex)) + side = PlaneF::Side(-S32(side)); + + switch (side) + { + case PlaneF::Front: + scanZone_r(rNode.frontIndex, center, axisx, axisy, axisz, zones, numZones); + break; + + case PlaneF::Back: + scanZone_r(rNode.backIndex, center, axisx, axisy, axisz, zones, numZones); + break; + + case PlaneF::On: + scanZone_r(rNode.frontIndex, center, axisx, axisy, axisz, zones, numZones); + scanZone_r(rNode.backIndex, center, axisx, axisy, axisz, zones, numZones); + break; + + default: + AssertFatal(false, "Misunderstood switchCode in Interior::zoneRay_r"); + } + + return; + } + + if (isBSPEmptyLeaf(node)) + { + U16 zone = getBSPEmptyLeafZone(node); + if (zone != 0x0FFF) + { + for (U32 i = 0; i < *numZones; i++) + { + if (zones[i] == zone) + return; + } + + zones[*numZones] = zone; + (*numZones)++; + } + } +} + +bool Interior::scanZones(const Box3F& box, + const MatrixF& transform, + U16* zones, + U32* numZones) +{ + // We don't need an exact answer, so let's just blast a box through + // the planes and see what we intersect. scanZoneNew is good if you + // have an exact volume to clip. + + Point3F center; + box.getCenter(¢er); + + Point3F xRad((box.maxExtents.x - box.minExtents.x) * 0.5f, 0.0f, 0.0f); + Point3F yRad(0.0f, (box.maxExtents.y - box.minExtents.y) * 0.5f, 0.0f); + Point3F zRad(0.0f, 0.0f, (box.maxExtents.z - box.minExtents.z) * 0.5f); + + transform.mulP(center); + transform.mulV(xRad); + transform.mulV(yRad); + transform.mulV(zRad); + + scanZone_r(0, center, xRad, yRad, zRad, zones, numZones); + + bool outsideToo = false; + if (*numZones != 0) + { + for (U32 i = 0; i < *numZones; /**/) + { + if (zones[i]) + { + i++; + continue; + } + + outsideToo = true; + + zones[i] = zones[(*numZones) - 1]; + (*numZones)--; + } + } + else + { + // If it ain't in us, it's outside us. + outsideToo = true; + } + + return outsideToo; +} + + +bool Interior::getIntersectingHulls(const Box3F& query, U16* hulls, U32* numHulls) +{ + AssertFatal(*numHulls == 0, "Error, some stuff in the hull vector already!"); + + // This is paranoia, and I probably wouldn't do it if the tag was 32 bits, but + // a possible collision every 65k searches is just a little too small for comfort + // DMM + if (mSearchTag == 0) + { + for (U32 i = 0; i < mConvexHulls.size(); i++) + mConvexHulls[i].searchTag = 0; + mSearchTag = 1; + } + else + { + mSearchTag++; + } + + F32 xBinSize = mBoundingBox.len_x() / F32(NumCoordBins); + F32 yBinSize = mBoundingBox.len_y() / F32(NumCoordBins); + + F32 queryMinX = getMax(mBoundingBox.minExtents.x, query.minExtents.x); + F32 queryMinY = getMax(mBoundingBox.minExtents.y, query.minExtents.y); + F32 queryMaxX = getMin(mBoundingBox.maxExtents.x, query.maxExtents.x); + F32 queryMaxY = getMin(mBoundingBox.maxExtents.y, query.maxExtents.y); + + S32 startX = S32(mFloor((queryMinX - mBoundingBox.minExtents.x) / xBinSize)); + S32 endX = S32( mCeil((queryMaxX - mBoundingBox.minExtents.x) / xBinSize)); + S32 startY = S32(mFloor((queryMinY - mBoundingBox.minExtents.y) / yBinSize)); + S32 endY = S32( mCeil((queryMaxY - mBoundingBox.minExtents.y) / yBinSize)); + + AssertFatal(startX >= 0, "Error, bad startx"); + AssertFatal(startY >= 0, "Error, bad starty"); + AssertFatal(endX <= NumCoordBins, "Error, bad endX"); + AssertFatal(endY <= NumCoordBins, "Error, bad endY"); + + // Handle non-debug case + startX = getMax(startX, 0); + endX = getMin(endX, NumCoordBins); + startY = getMax(startY, 0); + endY = getMin(endY, NumCoordBins); + + for (S32 i = startX; i < endX; i++) + { + for (S32 j = startY; j < endY; j++) + { + const CoordBin& rBin = mCoordBins[i * NumCoordBins + j]; + for (U32 k = rBin.binStart; k < rBin.binStart + rBin.binCount; k++) + { + U16 hullIndex = mCoordBinIndices[k]; + ConvexHull& rHull = mConvexHulls[hullIndex]; + + // Don't check twice! (We're not Santa Claus.) + if (rHull.searchTag == mSearchTag) + continue; + rHull.searchTag = mSearchTag; + + Box3F qb(rHull.minX, rHull.minY, rHull.minZ, rHull.maxX, rHull.maxY, rHull.maxZ); + if (query.isOverlapped(qb)) + { + hulls[*numHulls] = hullIndex; + (*numHulls)++; + } + } + } + } + + return *numHulls != 0; +} + + +bool Interior::getIntersectingVehicleHulls(const Box3F& query, U16* hulls, U32* numHulls) +{ + AssertFatal(*numHulls == 0, "Error, some stuff in the hull vector already!"); + + for (U16 i = 0; i < mVehicleConvexHulls.size(); i++) + { + ConvexHull& rHull = mVehicleConvexHulls[i]; + Box3F qb(rHull.minX, rHull.minY, rHull.minZ, rHull.maxX, rHull.maxY, rHull.maxZ); + if (query.isOverlapped(qb)) + { + hulls[*numHulls] = i; + (*numHulls)++; + } + } + + return *numHulls != 0; +} + +//-------------------------------------------------------------------------- +Box3F InteriorConvex::getBoundingBox() const +{ + return getBoundingBox(mObject->getTransform(), mObject->getScale()); +} + +Box3F InteriorConvex::getBoundingBox(const MatrixF& mat, const Point3F& scale) const +{ + Box3F newBox = box; + newBox.minExtents.convolve(scale); + newBox.maxExtents.convolve(scale); + mat.mul(newBox); + return newBox; +} + +Point3F InteriorConvex::support(const VectorF& v) const +{ + FrameAllocatorMarker fam; + + if (hullId >= 0) + { + AssertFatal(hullId < pInterior->mConvexHulls.size(), "Out of bounds hull!"); + + const Interior::ConvexHull& rHull = pInterior->mConvexHulls[hullId]; + + F32* pDots = (F32*)fam.alloc(sizeof(F32) * rHull.hullCount); + m_point3F_bulk_dot_indexed(&v.x, + &pInterior->mPoints[0].point.x, + rHull.hullCount, + sizeof(ItrPaddedPoint), + &pInterior->mHullIndices[rHull.hullStart], + pDots); + + U32 index = 0; + for (U32 i = 1; i < rHull.hullCount; i++) + { + if (pDots[i] > pDots[index]) + index = i; + } + + return pInterior->mPoints[pInterior->mHullIndices[rHull.hullStart + index]].point; + } + else + { + S32 actualId = -(hullId + 1); + AssertFatal(actualId < pInterior->mVehicleConvexHulls.size(), "Out of bounds hull!"); + + const Interior::ConvexHull& rHull = pInterior->mVehicleConvexHulls[actualId]; + + F32* pDots = (F32*)fam.alloc(sizeof(F32) * rHull.hullCount); + m_point3F_bulk_dot_indexed(&v.x, + &pInterior->mVehiclePoints[0].point.x, + rHull.hullCount, + sizeof(ItrPaddedPoint), + &pInterior->mVehicleHullIndices[rHull.hullStart], + pDots); + + U32 index = 0; + for (U32 i = 1; i < rHull.hullCount; i++) + { + if (pDots[i] > pDots[index]) + index = i; + } + + return pInterior->mVehiclePoints[pInterior->mVehicleHullIndices[rHull.hullStart + index]].point; + } +} + + +void InteriorConvex::getFeatures(const MatrixF& mat, const VectorF& n, ConvexFeature* cf) +{ + S32 i; + + cf->material = 0; + cf->object = mObject; + + if (hullId >= 0) + { + // We find the support ourselves here since we want the index too... + const Interior::ConvexHull& rHull = pInterior->mConvexHulls[hullId]; + U32 spIndex = 0; + F32 maxSp = mDot(pInterior->mPoints[pInterior->mHullIndices[rHull.hullStart + spIndex]].point, n); + + for (i = 1; i < rHull.hullCount; i++) + { + U32 index = pInterior->mHullIndices[rHull.hullStart + i]; + const Point3F& rPoint = pInterior->mPoints[index].point; + + F32 dot = mDot(rPoint, n); + if (dot > maxSp) + { + spIndex = i; + maxSp = dot; + } + } + + // Ok, now we have the support point, let's extract the emission string for this + // vertex... + U32 currPos = 0; + const U8* pString = &pInterior->mConvexHullEmitStrings[pInterior->mHullEmitStringIndices[rHull.hullStart + spIndex]]; + + FrameAllocatorMarker fam; + U32* pRemaps = (U32*)fam.alloc(256 * sizeof(U32)); + + // Ok, this is a piece of cake. Lets dump the points first... + U32 numPoints = pString[currPos++]; + for (i = 0; i < numPoints; i++) + { + U32 index = pString[currPos++]; + + pRemaps[i] = cf->mVertexList.size(); + cf->mVertexList.increment(); + + const Point3F& rPoint = pInterior->mPoints[pInterior->mHullIndices[rHull.hullStart + index]].point; + mat.mulP(rPoint, &cf->mVertexList.last()); + } + + // Then the edges... + U32 numEdges = pString[currPos++]; + for (i = 0; i < numEdges; i++) + { + U32 index0 = pString[currPos++]; + U32 index1 = pString[currPos++]; + + cf->mEdgeList.increment(); + cf->mEdgeList.last().vertex[0] = pRemaps[index0]; + cf->mEdgeList.last().vertex[1] = pRemaps[index1]; + } + + // Then the polys... + U32 numPolys = pString[currPos++]; + for (i = 0; i < numPolys; i++) + { + U32 vertexCount = pString[currPos++]; + U32 planeIndex = pString[currPos++]; + + U32 verts[3]; + verts[0] = pString[currPos++]; + verts[1] = pString[currPos++]; + + AssertFatal( verts[0] < numPoints, "InteriorConvex::getFeatures verts out of range" ); + + for (U32 j = 2; j < vertexCount; j++) + { + verts[2] = pString[currPos++]; + + // Emit this poly + cf->mFaceList.increment(); + cf->mFaceList.last().normal = pInterior->getPlane(pInterior->mHullPlaneIndices[rHull.planeStart + planeIndex]); + + AssertFatal( verts[1] < numPoints, "InteriorConvex::getFeatures verts out of range" ); + AssertFatal( verts[2] < numPoints, "InteriorConvex::getFeatures verts out of range" ); + + cf->mFaceList.last().vertex[0] = pRemaps[verts[0]]; + cf->mFaceList.last().vertex[1] = pRemaps[verts[1]]; + cf->mFaceList.last().vertex[2] = pRemaps[verts[2]]; + PlaneF plane(cf->mVertexList[pRemaps[verts[0]]], + cf->mVertexList[pRemaps[verts[1]]], + cf->mVertexList[pRemaps[verts[2]]]); + cf->mFaceList.last().normal = plane; + + // Shift the fan over + verts[1] = verts[2]; + } + } + } + else + { + S32 actualId = -(hullId + 1); + // We find the support ourselves here since we want the index too... + const Interior::ConvexHull& rHull = pInterior->mVehicleConvexHulls[actualId]; + U32 spIndex = 0; + F32 maxSp = mDot(pInterior->mVehiclePoints[pInterior->mVehicleHullIndices[rHull.hullStart + spIndex]].point, n); + + for (i = 1; i < rHull.hullCount; i++) + { + U32 index = pInterior->mVehicleHullIndices[rHull.hullStart + i]; + const Point3F& rPoint = pInterior->mVehiclePoints[index].point; + + F32 dot = mDot(rPoint, n); + if (dot > maxSp) + { + spIndex = i; + maxSp = dot; + } + } + + // Ok, now we have the support point, let's extract the emission string for this + // vertex... + U32 currPos = 0; + const U8* pString = &pInterior->mVehicleConvexHullEmitStrings[ + pInterior->mVehicleHullEmitStringIndices[rHull.hullStart + spIndex] + ]; + + FrameAllocatorMarker fam; + U32* pRemaps = (U32*)fam.alloc(256 * sizeof(U32)); + + // Ok, this is a piece of cake. Lets dump the points first... + U32 numPoints = pString[currPos++]; + for (i = 0; i < numPoints; i++) + { + U32 index = pString[currPos++]; + + pRemaps[i] = cf->mVertexList.size(); + cf->mVertexList.increment(); + + const Point3F& rPoint = pInterior->mVehiclePoints[pInterior->mVehicleHullIndices[rHull.hullStart + index]].point; + mat.mulP(rPoint, &cf->mVertexList.last()); + } + + // Then the edges... + U32 numEdges = pString[currPos++]; + for (i = 0; i < numEdges; i++) + { + U32 index0 = pString[currPos++]; + U32 index1 = pString[currPos++]; + + cf->mEdgeList.increment(); + cf->mEdgeList.last().vertex[0] = pRemaps[index0]; + cf->mEdgeList.last().vertex[1] = pRemaps[index1]; + } + + // Then the polys... + U32 numPolys = pString[currPos++]; + for (i = 0; i < numPolys; i++) + { + U32 vertexCount = pString[currPos++]; + U32 planeIndex = pString[currPos++]; + + U32 verts[3]; + verts[0] = pString[currPos++]; + verts[1] = pString[currPos++]; + + for (U32 j = 2; j < vertexCount; j++) + { + verts[2] = pString[currPos++]; + + // Emit this poly + cf->mFaceList.increment(); + + cf->mFaceList.last().vertex[0] = pRemaps[verts[0]]; + cf->mFaceList.last().vertex[1] = pRemaps[verts[1]]; + cf->mFaceList.last().vertex[2] = pRemaps[verts[2]]; + cf->mFaceList.last().normal = pInterior->getPlane(planeIndex); + + // Shift the fan over + verts[1] = verts[2]; + } + } + } +} + +void InteriorConvex::getPolyList(AbstractPolyList* list) +{ + // Setup collision state data + { + list->setTransform(&mObject->getTransform(), mObject->getScale()); + list->setObject(mObject); + } + + if (hullId >= 0) + { + // Get our hull + const Interior::ConvexHull& rHull = pInterior->mConvexHulls[hullId]; + + // Build up the lists of points and strings + const U8* pString = &pInterior->mPolyListStrings[rHull.polyListStringStart]; + + U32 currPos = 0; + U32 numPlanes = pString[currPos++]; + + // It can happen that a hull has no collision surfaces. In that case, just bail out + // here... + if (numPlanes == 0) + return; + + const U8* planeString = &pString[currPos]; + currPos += numPlanes; + + U32 numPoints = pString[currPos++] << 8; + numPoints |= pString[currPos++]; + const U8* pointString = &pString[currPos]; + currPos += numPoints; + + U32 numSurfaces = pString[currPos++]; + + const U16* planeIndices = &pInterior->mPolyListPlanes[rHull.polyListPlaneStart]; + const U32* pointIndices = &pInterior->mPolyListPoints[rHull.polyListPointStart]; + + //-------------------------------------- + // At this point, currPos is pointing to the first surface in the string + //-------------------------------------- + + // First thing to do: build the interest mask, by seeing if the list is interested + // in our planes... + U8 interestMask = 0; + U32 remappedPlaneBase; + { + U16 planeIndex = planeIndices[0]; + PlaneF plane = pInterior->getPlane(planeIndex); + if (Interior::planeIsFlipped(planeIndex)) + plane.neg(); + + remappedPlaneBase = list->addPlane(plane); + + if (list->isInterestedInPlane(remappedPlaneBase)) + interestMask |= planeString[0]; + + for (U32 i = 1; i < numPlanes; i++) + { + planeIndex = planeIndices[i]; + plane = pInterior->getPlane(planeIndex); + if (Interior::planeIsFlipped(planeIndex)) + plane.neg(); + + list->addPlane(plane); + if (list->isInterestedInPlane(remappedPlaneBase + i)) + interestMask |= planeString[i]; + } + } + + // Now, whip through the points, and build up the remap table, adding only + // those points that the list is interested in. Note that we use the frameAllocator + // to get enoughMemory to deal with the variable sized remap array + FrameAllocatorMarker fam; + U32* pointRemapTable = reinterpret_cast(fam.alloc(numPoints * sizeof(U32))); + { + for (U32 i = 0; i < numPoints; i++) + { + if ((interestMask & pointString[i]) != 0) + { + const Point3F& rPoint = pInterior->mPoints[pointIndices[i]].point; + pointRemapTable[i] = list->addPoint(rPoint); + } + } + } + + // Now, whip through the surfaces, checking to make sure that we're interested in + // that poly as we go. At this point, currPos should point to the first surface. + // The format of the surface string can be found in interior.cc, in the + // processHullPolyLists function + { + for (U32 i = 0; i < numSurfaces; i++) + { + U32 snPoints = pString[currPos++]; + U32 sMask = pString[currPos++]; + U32 sPlane = pString[currPos++]; + + if ((interestMask & sMask) != 0) + { + // Add the poly + // + list->begin(0, planeIndices[sPlane]); + for (U32 j = 0; j < snPoints; j++) + { + U16 remappedIndex = pString[currPos++] << 8; + remappedIndex |= pString[currPos++]; + remappedIndex = pointRemapTable[remappedIndex]; + list->vertex(remappedIndex); + } + list->plane(remappedPlaneBase + sPlane); + + list->end(); + } + else + { + // Superflous poly, just skip past the points + currPos += snPoints * 2; + } + } + } + } + else + { + S32 actualId = -(hullId + 1); + + // Get our hull + const Interior::ConvexHull& rHull = pInterior->mVehicleConvexHulls[actualId]; + + // Build up the lists of points and strings + const U8* pString = &pInterior->mVehiclePolyListStrings[rHull.polyListStringStart]; + U32 currPos = 0; + + U32 numPlanes = pString[currPos++]; + // It can happen that a hull has no collision surfaces. In that case, just bail out + // here... + if (numPlanes == 0) + return; + + const U8* planeString = &pString[currPos]; + currPos += numPlanes; + + U32 numPoints = pString[currPos++] << 8; + numPoints |= pString[currPos++]; + const U8* pointString = &pString[currPos]; + currPos += numPoints; + + U32 numSurfaces = pString[currPos++]; + + const U16* planeIndices = &pInterior->mVehiclePolyListPlanes[rHull.polyListPlaneStart]; + const U32* pointIndices = &pInterior->mVehiclePolyListPoints[rHull.polyListPointStart]; + + //-------------------------------------- + // At this point, currPos is pointing to the first surface in the string + //-------------------------------------- + + // First thing to do: build the interest mask, by seeing if the list is interested + // in our planes... + U8 interestMask = 0; + U32 remappedPlaneBase; + { + U16 planeIndex = planeIndices[0]; + PlaneF plane = pInterior->getPlane(planeIndex); + if (Interior::planeIsFlipped(planeIndex)) + plane.neg(); + + remappedPlaneBase = list->addPlane(plane); + + if (list->isInterestedInPlane(remappedPlaneBase)) + interestMask |= planeString[0]; + + for (U32 i = 1; i < numPlanes; i++) + { + planeIndex = planeIndices[i]; + plane = pInterior->getPlane(planeIndex); + if (Interior::planeIsFlipped(planeIndex)) + plane.neg(); + + list->addPlane(plane); + if (list->isInterestedInPlane(remappedPlaneBase + i)) + interestMask |= planeString[i]; + } + } + + // Now, whip through the points, and build up the remap table, adding only + // those points that the list is interested in. Note that we use the frameAllocator + // to get enoughMemory to deal with the variable sized remap array + FrameAllocatorMarker fam; + U32* pointRemapTable = reinterpret_cast(fam.alloc(numPoints * sizeof(U32))); + { + for (U32 i = 0; i < numPoints; i++) + { + if ((interestMask & pointString[i]) != 0) + { + const Point3F& rPoint = pInterior->mVehiclePoints[pointIndices[i]].point; + pointRemapTable[i] = list->addPoint(rPoint); + } + } + } + + // Now, whip through the surfaces, checking to make sure that we're interested in + // that poly as we go. At this point, currPos should point to the first surface. + // The format of the surface string can be found in interior.cc, in the + // processHullPolyLists function + { + for (U32 i = 0; i < numSurfaces; i++) + { + U32 snPoints = pString[currPos++]; + U32 sMask = pString[currPos++]; + U32 sPlane = pString[currPos++]; + + if ((interestMask & sMask) != 0) + { + // Add the poly + // + list->begin(0, planeIndices[sPlane]); + for (U32 j = 0; j < snPoints; j++) + { + U16 remappedIndex = pString[currPos++] << 8; + remappedIndex |= pString[currPos++]; + remappedIndex = pointRemapTable[remappedIndex]; + list->vertex(remappedIndex); + } + list->plane(remappedPlaneBase + sPlane); + + list->end(); + } + else + { + // Superflous poly, just skip past the points + currPos += snPoints * 2; + } + } + } + } +} + + diff --git a/Engine/source/interior/interiorDebug.cpp b/Engine/source/interior/interiorDebug.cpp new file mode 100644 index 000000000..1d0db508d --- /dev/null +++ b/Engine/source/interior/interiorDebug.cpp @@ -0,0 +1,711 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "torqueConfig.h" + +#ifndef TORQUE_SHIPPING + +#include "interior/interior.h" +#include "interior/interiorInstance.h" +#include "console/console.h" +#include "core/color.h" +#include "math/mMatrix.h" +#include "gfx/bitmap/gBitmap.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxShader.h" +#include "materials/matInstance.h" +#include "materials/materialList.h" +#include "materials/shaderData.h" +#include "renderInstance/renderPassManager.h" +#include "shaderGen/shaderGenVars.h" + +static U8 interiorDebugColors[14][3] = +{ + { 0xFF, 0xFF, 0xFF }, + { 0x00, 0x00, 0xFF }, + { 0x00, 0xFF, 0x00 }, + { 0xFF, 0x00, 0x00 }, + { 0xFF, 0xFF, 0x00 }, + { 0xFF, 0x00, 0xFF }, + { 0x00, 0xFF, 0xFF }, + { 0x80, 0x80, 0x80 }, + { 0xFF, 0x80, 0x80 }, + { 0x80, 0xFF, 0x80 }, + { 0x80, 0x80, 0xFF }, + { 0x80, 0xFF, 0xFF }, + { 0xFF, 0x80, 0xFF }, + { 0xFF, 0x80, 0x80 } +}; + + +namespace +{ + +void lineLoopFromStrip(Vector& points, + Vector& windings, + U32 windingStart, + U32 windingCount) +{ + PrimBuild::begin(GFXLineStrip, windingCount + 1); + PrimBuild::vertex3fv(points[windings[windingStart]].point); + S32 skip = windingStart + 1; + while (skip < (windingStart + windingCount)) + { + PrimBuild::vertex3fv(points[windings[skip]].point); + skip += 2; + } + + skip -= 1; + while (skip > windingStart) + { + if (skip < (windingStart + windingCount)) + PrimBuild::vertex3fv(points[windings[skip]].point); + + skip -= 2; + } + PrimBuild::vertex3fv(points[windings[windingStart]].point); + PrimBuild::end(); +} + +void lineStrip(Vector& points, + Vector& windings, + U32 windingStart, + U32 windingCount) +{ + U32 end = 2; + + while (end < windingCount) + { + // Even + PrimBuild::begin(GFXLineStrip, 4); + PrimBuild::vertex3fv(points[windings[windingStart + end - 2]].point); + PrimBuild::vertex3fv(points[windings[windingStart + end - 1]].point); + PrimBuild::vertex3fv(points[windings[windingStart + end - 0]].point); + PrimBuild::vertex3fv(points[windings[windingStart + end - 2]].point); + PrimBuild::end(); + + end++; + if (end >= windingCount) + break; + + // Odd + PrimBuild::begin(GFXLineStrip, 4); + PrimBuild::vertex3fv(points[windings[windingStart + end - 1]].point); + PrimBuild::vertex3fv(points[windings[windingStart + end - 2]].point); + PrimBuild::vertex3fv(points[windings[windingStart + end - 0]].point); + PrimBuild::vertex3fv(points[windings[windingStart + end - 1]].point); + PrimBuild::end(); + + end++; + } +} + +} // namespace {} + +void Interior::debugRender(const ZoneVisDeterminer& zoneVis, SceneData &sgData, InteriorInstance *intInst, MatrixF& modelview) +{ + // We use this shader to color things + if ( mDebugShader == NULL ) + { + ShaderData *shaderData = NULL; + AssertFatal( Sim::findObject( "_DebugInterior_", shaderData ), "Unable to find ShaderData _DebugInterior_" ); + + mDebugShader = shaderData ? shaderData->getShader() : NULL; + + if ( mDebugShader ) + { + mDebugShaderConsts = mDebugShader->allocConstBuffer(); + mDebugShaderModelViewSC = mDebugShader->getShaderConstHandle(ShaderGenVars::modelview); + mDebugShaderShadeColorSC = mDebugShader->getShaderConstHandle("$shadeColor"); + } + } + + // We use this to override the texture that the interior defines. + if (mDebugTexture.isNull()) + { + // Allocate a small white bitmap + GBitmap temp(16, 16); + mDebugTexture.set(&temp, &GFXDefaultStaticDiffuseProfile, false, "Blank Texture"); + } + + // Set up our buffers + GFX->setVertexBuffer( mVertBuff ); + GFX->setPrimitiveBuffer( mPrimBuff ); + + // Set the modelview matrix for the shaders + mDebugShaderConsts->setSafe(mDebugShaderModelViewSC, modelview); + + GFX->disableShaders(); + + switch (smRenderMode) + { + case NormalRenderLines: + debugNormalRenderLines(zoneVis); + break; + + case ShowDetail: + debugShowDetail(zoneVis); + break; + + case ShowAmbiguous: + debugShowAmbiguous(zoneVis); + break; + + case ShowLightmaps: + debugShowLightmaps(zoneVis, sgData, intInst); + break; + + case ShowPortalZones: + debugShowPortalZones(zoneVis); + break; + + case ShowCollisionFans: + debugShowCollisionFans(zoneVis); + break; + + case ShowOrphan: + debugShowOrphan(zoneVis); + break; + + case ShowStrips: + debugShowStrips(zoneVis); + break; + + case ShowTexturesOnly: + debugShowTexturesOnly(zoneVis, sgData, intInst); + break; + + case ShowNullSurfaces: + debugShowNullSurfaces(zoneVis, sgData, intInst); + break; + + case ShowLargeTextures: + debugShowLargeTextures(zoneVis, sgData, intInst); + break; + + case ShowOutsideVisible: + debugShowOutsideVisible(zoneVis); + break; + + case ShowHullSurfaces: + debugShowHullSurfaces(); + break; + + case ShowVehicleHullSurfaces: + debugShowVehicleHullSurfaces(zoneVis, sgData, intInst); + break; + + case ShowDetailLevel: + debugShowDetailLevel(zoneVis); + break; + + case ShowVertexColors: +// debugShowVertexColors(pMaterials); + break; + + default: + AssertWarn(false, "Warning! Misunderstood debug render mode. Defaulting to ShowDetail"); + debugShowDetail(zoneVis); + break; + } +} + +void Interior::preDebugRender() +{ + // Set up our rendering states. + if( mDebugShader ) + { + // Set our shader + GFX->setShader( mDebugShader ); + + // Set a "blank" texture + GFX->setTexture( 0, mDebugTexture ); + + // Set a state block to enable our texture + GFX->setStateBlock(mInteriorDebugTextureSB); + } +} + +void Interior::debugNormalRenderLines(const ZoneVisDeterminer& zoneVis) +{ + // Set our "base debug states" (no texture) + GFX->setStateBlock(mInteriorDebugNoneSB); + + for (U32 i = 0; i < mZones.size(); i++) + { + if (zoneVis.isZoneVisible(i) == false) + continue; + + for( U32 j=0; jsetSafe(mDebugShaderShadeColorSC, col); + + GFX->setShaderConstBuffer(mDebugShaderConsts); + + GFXPrimitive* info = &rSurface.surfaceInfo; + GFX->drawIndexedPrimitive( info->type, info->startVertex, info->minIndex, info->numVertices, info->startIndex, info->numPrimitives ); + } + } + + GFX->disableShaders(); + debugNormalRenderLines(zoneVis); +} + +void Interior::debugShowDetail(const ZoneVisDeterminer& zoneVis) +{ + debugShowSurfaceFlag(zoneVis, SurfaceDetail, ColorF(1.0f, 0.0f, 0.0f)); +} + +void Interior::debugShowAmbiguous(const ZoneVisDeterminer& zoneVis) +{ + debugShowSurfaceFlag(zoneVis, SurfaceAmbiguous, ColorF(0.0f, 1.0f, 0.0f)); +} + +void Interior::debugShowOrphan(const ZoneVisDeterminer& zoneVis) +{ + debugShowSurfaceFlag(zoneVis, SurfaceOrphan, ColorF(0.0f, 0.0f, 1.0f)); +} + +void Interior::debugShowOutsideVisible(const ZoneVisDeterminer& zoneVis) +{ + debugShowSurfaceFlag(zoneVis, SurfaceOutsideVisible, ColorF(1.0f, 0.0f, 0.0f)); +} + +void Interior::debugShowPortalZones(const ZoneVisDeterminer& zoneVis) +{ + preDebugRender(); + + for (U32 i = 0; i < mZones.size(); i++) + { + U8* color; + if (i == 0) + color = interiorDebugColors[0]; + else + color = interiorDebugColors[(i % 13) + 1]; + + for (U32 j = mZones[i].surfaceStart; j < mZones[i].surfaceStart + mZones[i].surfaceCount; j++) + { + Surface& rSurface = mSurfaces[mZoneSurfaces[j]]; + ColorF c((F32) color[0] / 255.0f, (F32) color[1] / 255.0f, (F32) color[2] / 255.0f); + + mDebugShaderConsts->setSafe(mDebugShaderShadeColorSC, c); + + GFX->setShaderConstBuffer(mDebugShaderConsts); + + GFXPrimitive* info = &rSurface.surfaceInfo; + GFX->drawIndexedPrimitive( info->type, info->startVertex, info->minIndex, info->numVertices, info->startIndex, info->numPrimitives ); + } + } + + GFX->disableShaders(); + debugRenderPortals(); + debugNormalRenderLines(zoneVis); +} + +// Render portals +void Interior::debugRenderPortals() +{ + // Set our portal rendering state block + GFX->setStateBlock(mInteriorDebugPortalSB); + + for (U32 i = 0; i < mPortals.size(); i++) + { + const Portal& rPortal = mPortals[i]; + + for (U16 j = 0; j < rPortal.triFanCount; j++) + { + const TriFan& rFan = mWindingIndices[rPortal.triFanStart + j]; + U32 k; + + PrimBuild::color4f(0.75f, 0.5f, 0.75f, 0.45f); + PrimBuild::begin(GFXTriangleFan, rFan.windingCount); + + for (k = 0; k < rFan.windingCount; k++) + PrimBuild::vertex3fv(mPoints[mWindings[rFan.windingStart + k]].point); + + PrimBuild::end(); + + PrimBuild::color4f(0, 0, 1, 1); + PrimBuild::begin(GFXLineStrip, rFan.windingCount+1); + + for (k = 0; k < rFan.windingCount; k++) + PrimBuild::vertex3fv(mPoints[mWindings[rFan.windingStart + k]].point); + + PrimBuild::vertex3fv(mPoints[mWindings[rFan.windingStart]].point); + + PrimBuild::end(); + } + } +} + +void Interior::debugShowCollisionFans(const ZoneVisDeterminer& zoneVis) +{ + // Set our "base debug states" (no texture) + GFX->setStateBlock(mInteriorDebugNoneSB); + + for (U32 i = 0; i < mZones.size(); i++) + { + if (zoneVis.isZoneVisible(i) == false) + continue; + + for( U32 j=0; j 0) + PrimBuild::vertex3fv(mPoints[fanIndices[0]].point); + PrimBuild::end(); + + // Normal + PrimBuild::color3f(1, 0, 0); + PrimBuild::begin(GFXLineList, numIndices * 2); + for (U32 j = 0; j < numIndices; j++) + { + Point3F up = mPoints[fanIndices[j]].point; + Point3F norm = getPlane(rSurface.planeIndex); + if (planeIsFlipped(rSurface.planeIndex)) + up -= norm * 0.4f; + else + up += norm * 0.4f; + + PrimBuild::vertex3fv(mPoints[fanIndices[j]].point); + PrimBuild::vertex3fv(up); + } + PrimBuild::end(); + } + } +} + +// This doesn't show strip (they don't go to the card that way) +// But it does show the batches of primitives we send. +void Interior::debugShowStrips(const ZoneVisDeterminer& zoneVis) +{ + // Set up our rendering states. + preDebugRender(); + + for (U32 i = 0; i < mZones.size(); i++) + { + if (zoneVis.isZoneVisible(i) == false) + continue; + + for( U32 j=0; jsetSafe(mDebugShaderShadeColorSC, col); + + GFX->setShaderConstBuffer(mDebugShaderConsts); + + GFX->drawPrimitive(node.primInfoIndex); + } + } + + GFX->disableShaders(); + debugNormalRenderLines(zoneVis); +} + +void Interior::debugShowDetailLevel(const ZoneVisDeterminer& zoneVis) +{ + // Set up our rendering states. + preDebugRender(); + + U32 index = getDetailLevel(); + ColorF col((F32)interiorDebugColors[index][0] / 255.0f, (F32)interiorDebugColors[index][1] / 255.0f, + (F32)interiorDebugColors[index][2] / 255.0f); + + mDebugShaderConsts->setSafe(mDebugShaderShadeColorSC, col); + + GFX->setShaderConstBuffer(mDebugShaderConsts); + + for (U32 i = 0; i < mZones.size(); i++) + { + if (zoneVis.isZoneVisible(i) == false) + continue; + + for( U32 j=0; jdrawPrimitive(node.primInfoIndex); + } + } + + GFX->disableShaders(); + debugNormalRenderLines(zoneVis); +} + +void Interior::debugShowHullSurfaces() +{ + // Set our "base debug states" (no texture) + GFX->setStateBlock(mInteriorDebugNoneSB); + + for (U32 i = 0; i < mConvexHulls.size(); i++) + { + const ConvexHull& rHull = mConvexHulls[i]; + + for (U32 j = rHull.surfaceStart; j < rHull.surfaceCount + rHull.surfaceStart; j++) + { + U32 index = mHullSurfaceIndices[j]; + + if (!isNullSurfaceIndex(index)) + { + const Interior::Surface& rSurface = mSurfaces[index]; + U32 fanVerts[32]; + U32 numVerts; + collisionFanFromSurface(rSurface, fanVerts, &numVerts); + + PrimBuild::color3i(interiorDebugColors[(i%13)+1][0], interiorDebugColors[(i%13)+1][1], + interiorDebugColors[(i%13)+1][2]); + Point3F center(0, 0, 0); + PrimBuild::begin(GFXTriangleFan, numVerts); + for (U32 k = 0; k < numVerts; k++) + { + PrimBuild::vertex3fv(mPoints[fanVerts[k]].point); + center += mPoints[fanVerts[k]].point; + } + PrimBuild::end(); + center /= F32(numVerts); + PrimBuild::color3f(0, 0, 0); + lineLoopFromStrip(mPoints, mWindings, rSurface.windingStart, rSurface.windingCount); + + PlaneF plane; + plane.set(mPoints[fanVerts[0]].point, mPoints[fanVerts[1]].point, mPoints[fanVerts[2]].point); + PrimBuild::begin(GFXLineList, 2); + PrimBuild::vertex3fv(center); + PrimBuild::vertex3fv(center + (plane * 0.25)); + PrimBuild::end(); + } + } + } +} + +void Interior::debugDefaultRender(const ZoneVisDeterminer& zoneVis, SceneData &sgData, InteriorInstance *intInst) +{ + // Set up a state block with two textures enabled + GFX->setStateBlock(mInteriorDebugTwoTextureSB); + + for( U32 i=0; igetLMHandle(), node.lightMapIndex ); + + GFX->setTexture( 0, mMaterialList->getDiffuseTexture( curBaseTexIndex )); + GFX->setTexture(1, sgData.lightmap); + GFX->drawPrimitive( node.primInfoIndex ); + } + } +} + +void Interior::debugShowNullSurfaces(const ZoneVisDeterminer& zoneVis, SceneData &sgData, InteriorInstance *intInst) +{ + debugDefaultRender(zoneVis, sgData, intInst); + + PrimBuild::color3f(1.0f, 0.0f, 0.0f); + for (U32 i = 0; i < mNullSurfaces.size(); i++) + { + const NullSurface& rSurface = mNullSurfaces[i]; + + PrimBuild::begin(GFXTriangleFan, rSurface.windingCount); + for (U32 k = 0; k < rSurface.windingCount; k++) + { + PrimBuild::vertex3fv(mPoints[mWindings[rSurface.windingStart+k]].point); + } + PrimBuild::end(); + } +} + +void Interior::debugShowVehicleHullSurfaces(const ZoneVisDeterminer& zoneVis, SceneData &sgData, + InteriorInstance *intInst) +{ + debugDefaultRender(zoneVis, sgData, intInst); + + PrimBuild::color3f(1.0f, 0.0f, 0.0f); + for (U32 i = 0; i < mVehicleNullSurfaces.size(); i++) + { + const NullSurface& rSurface = mNullSurfaces[i]; + PrimBuild::begin(GFXTriangleFan, rSurface.windingCount); + for (U32 k = 0; k < rSurface.windingCount; k++) + { + PrimBuild::vertex3fv(mPoints[mWindings[rSurface.windingStart+k]].point); + } + } +} + +void Interior::debugShowTexturesOnly(const ZoneVisDeterminer& zoneVis, SceneData &sgData, + InteriorInstance *intInst) +{ + // Set up a state block with one texture unit enabled + GFX->setStateBlock(mInteriorDebugTextureSB); + + for( U32 i=0; isetTexture( 0, mMaterialList->getDiffuseTexture( curBaseTexIndex )); + GFX->drawPrimitive( node.primInfoIndex ); + } + } +} + +void Interior::debugShowLargeTextures(const ZoneVisDeterminer& zoneVis, SceneData &sgData, + InteriorInstance *intInst) +{ + preDebugRender(); + + for( U32 i=0; igetDiffuseTexture( curBaseTexIndex ); + ColorF texSizeColor(1.0f, 1.0f, 1.0f, 1.0f); + if (t) + { + U32 width = t.getWidth(); + U32 height = t.getHeight(); + + if (width <= 256 && height <= 256) + texSizeColor = ColorF(0.25f, 0.25f, 1.0f); // small texture + else if (width <= 512 && height <= 512) + texSizeColor = ColorF(0.25f, 1.0f, 0.25f); // medium texture + else + texSizeColor = ColorF(1.0f, 0.25f, 0.25f); // large texture + } + + mDebugShaderConsts->setSafe(mDebugShaderShadeColorSC, texSizeColor); + + GFX->setShaderConstBuffer(mDebugShaderConsts); + + GFX->setTexture( 0, t); + GFX->drawPrimitive( node.primInfoIndex ); + } + } +} + +void Interior::debugShowLightmaps(const ZoneVisDeterminer& zoneVis, SceneData &sgData, InteriorInstance *intInst) +{ + GFX->setTexture(0, mDebugTexture); + + // Set up a state block with two textures enabled + GFX->setStateBlock(mInteriorDebugTwoTextureSB); + + for( U32 i=0; igetLMHandle(), node.lightMapIndex ); + + GFX->setTexture(1, sgData.lightmap); + GFX->drawPrimitive( node.primInfoIndex ); + } + } +} + +#endif diff --git a/Engine/source/interior/interiorIO.cpp b/Engine/source/interior/interiorIO.cpp new file mode 100644 index 000000000..323951cba --- /dev/null +++ b/Engine/source/interior/interiorIO.cpp @@ -0,0 +1,1669 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "interior/interior.h" +#include "core/bitVector.h" +#include "core/stream/stream.h" +#include "math/mathIO.h" +#include "gfx/bitmap/gBitmap.h" +#include "interior/interiorSubObject.h" +#include "console/console.h" +#include "core/frameAllocator.h" +#include "materials/materialList.h" + +int QSORT_CALLBACK cmpU32(const void* p1, const void* p2) +{ + return S32(*((U32*)p1)) - S32(*((U32*)p2)); +} + +//------------------------------------------------------------------------------ +//-------------------------------------- PERSISTENCE IMPLEMENTATION +// +U32 Interior::smFileVersion = 14; + +bool Interior::read(Stream& stream) +{ + AssertFatal(stream.hasCapability(Stream::StreamRead), "Interior::read: non-read capable stream passed"); + AssertFatal(stream.getStatus() == Stream::Ok, "Interior::read: Error, stream in inconsistent state"); + + S32 i; + + // Version this stream. We only load stream of the current version + U32 fileVersion; + stream.read(&fileVersion); + + // We need to store the version in case there is any post processing that + // needs to take place that is dependent on the file version + mFileVersion = fileVersion; + + if (fileVersion > smFileVersion) + { + Con::errorf(ConsoleLogEntry::General, "Interior::read: incompatible file version found."); + return false; + } + + // Geometry factors... + stream.read(&mDetailLevel); + + stream.read(&mMinPixels); + mathRead(stream, &mBoundingBox); + mathRead(stream, &mBoundingSphere); + stream.read(&mHasAlarmState); + stream.read(&mNumLightStateEntries); + + // Now read in our data vectors. + S32 vectorSize; + + // mPlanes + readPlaneVector(stream); + + // mPoints + stream.read(&vectorSize); + mPoints.setSize(vectorSize); + for (i = 0; i < mPoints.size(); i++) + mathRead(stream, &mPoints[i].point); + + // mPointVisibility + stream.read(&vectorSize); + mPointVisibility.setSize(vectorSize); + stream.read(vectorSize, mPointVisibility.address()); + + // mTexGenEQs + stream.read(&vectorSize); + mTexGenEQs.setSize(vectorSize); + for(i = 0; i < mTexGenEQs.size(); i++) + { + mathRead(stream, &mTexGenEQs[i].planeX); + mathRead(stream, &mTexGenEQs[i].planeY); + } + + // mBSPNodes; + stream.read(&vectorSize); + mBSPNodes.setSize(vectorSize); + for(i = 0; i < mBSPNodes.size(); i++) + { + stream.read(&mBSPNodes[i].planeIndex); + + if (fileVersion >= 14) + { + stream.read(&mBSPNodes[i].frontIndex); + stream.read(&mBSPNodes[i].backIndex); + } + else + { + U16 frontIndex, backIndex; + stream.read(&frontIndex); + stream.read(&backIndex); + + mBSPNodes[i].frontIndex = U32(frontIndex); + mBSPNodes[i].backIndex = U32(backIndex); + } + } + + // mBSPSolidLeaves + stream.read(&vectorSize); + mBSPSolidLeaves.setSize(vectorSize); + for(i = 0; i < mBSPSolidLeaves.size(); i++) + { + stream.read(&mBSPSolidLeaves[i].surfaceIndex); + stream.read(&mBSPSolidLeaves[i].surfaceCount); + } + + // MaterialList + if(mMaterialList != NULL) + delete mMaterialList; + mMaterialList = new MaterialList; + mMaterialList->read(stream); + + + // mWindings + stream.read(&vectorSize); + mWindings.setSize(vectorSize); + for(i = 0; i < mWindings.size(); i++) + { + stream.read(&mWindings[i]); + } + + // mWindingIndices + stream.read(&vectorSize); + mWindingIndices.setSize(vectorSize); + for(i = 0; i < mWindingIndices.size(); i++) + { + stream.read(&mWindingIndices[i].windingStart); + stream.read(&mWindingIndices[i].windingCount); + } + + // mEdges + if (fileVersion >= 12) + { + stream.read(&vectorSize); + + mEdges.setSize(vectorSize); + for (i = 0; i < mEdges.size(); i++) + { + stream.read(&mEdges[i].vertexes[0]); + stream.read(&mEdges[i].vertexes[1]); + stream.read(&mEdges[i].faces[0]); + stream.read(&mEdges[i].faces[1]); + } + } + + // mZones + stream.read(&vectorSize); + mZones.setSize(vectorSize); + for(i = 0; i < mZones.size(); i++) + { + stream.read(&mZones[i].portalStart); + stream.read(&mZones[i].portalCount); + stream.read(&mZones[i].surfaceStart); + stream.read(&mZones[i].surfaceCount); + + if (fileVersion >= 12) + { + stream.read(&mZones[i].staticMeshStart); + stream.read(&mZones[i].staticMeshCount); + } + else + { + mZones[i].staticMeshStart = 0; + mZones[i].staticMeshCount = 0; + } + + stream.read(&mZones[i].flags); + mZones[i].zoneId = 0; + } + + // Zone surfaces + stream.read(&vectorSize); + mZoneSurfaces.setSize(vectorSize); + for(i = 0; i < mZoneSurfaces.size(); i++) + stream.read(&mZoneSurfaces[i]); + + // Zone static meshes + if (fileVersion >= 12) + { + stream.read(&vectorSize); + + mZoneStaticMeshes.setSize(vectorSize); + for (i = 0; i < mZoneStaticMeshes.size(); i++) + stream.read(&mZoneStaticMeshes[i]); + } + + // mZonePortalList; + stream.read(&vectorSize); + mZonePortalList.setSize(vectorSize); + for(i = 0; i < mZonePortalList.size(); i++) + stream.read(&mZonePortalList[i]); + + // mPortals + stream.read(&vectorSize); + mPortals.setSize(vectorSize); + for(i = 0; i < mPortals.size(); i++) + { + stream.read(&mPortals[i].planeIndex); + stream.read(&mPortals[i].triFanCount); + stream.read(&mPortals[i].triFanStart); + stream.read(&mPortals[i].zoneFront); + stream.read(&mPortals[i].zoneBack); + } + + // mSurfaces + stream.read(&vectorSize); + mSurfaces.setSize(vectorSize); + mLMTexGenEQs.setSize(vectorSize); + + // Couple of hoops to *attempt* to detect that we are loading + // a TGE version 0 Interior and not a TGEA verison 0 + U32 surfacePos = stream.getPosition(); + bool tgeInterior = false; + + // First attempt to read this as though it isn't a TGE version 0 Interior + for(i = 0; i < mSurfaces.size(); i++) + { + // If we end up reading any invalid data in this loop then odds + // are that we are no longer correctly reading from the stream + // and have gotten off because this is a TGE version 0 Interior + + Surface& surface = mSurfaces[i]; + + if (readSurface(stream, surface, mLMTexGenEQs[i], false) == false) + { + tgeInterior = true; + break; + } + } + + // If this is a version 0 Interior and we failed to read it as a + // TGEA version 0 Interior then attempt to read it as a TGE version 0 + if (fileVersion == 0 && tgeInterior) + { + // Set our stream position back to the start of the surfaces + stream.setPosition(surfacePos); + + // Try reading in the surfaces again + for(i = 0; i < mSurfaces.size(); i++) + { + Surface& surface = mSurfaces[i]; + + // If we fail on any of the surfaces then bail + if (readSurface(stream, surface, mLMTexGenEQs[i], true) == false) + return false; + } + } + // If we failed to read but this isn't a version 0 Interior + // then something has gone horribly wrong + else if (fileVersion != 0 && tgeInterior) + return false; + + // Edges + if (fileVersion == 5) + { + stream.read(&vectorSize); + mEdges.setSize(vectorSize); + for (i = 0; i < mEdges.size(); i++) + { + stream.read(&mEdges[i].vertexes[0]); + stream.read(&mEdges[i].vertexes[1]); + U32 normals[2]; + stream.read(&normals[0]); + stream.read(&normals[1]); + + if (fileVersion > 2) // version 3 is where surface id's get added + { + stream.read(&mEdges[i].faces[0]); + stream.read(&mEdges[i].faces[1]); + } + } + } + + // mNormals + if (fileVersion == 5) + { + stream.read(&vectorSize); + Vector normals; + normals.setSize(vectorSize); + for(i = 0; i < normals.size(); i++) + mathRead(stream, &normals[i]); + + // mNormalIndices + stream.read(&vectorSize); + Vector normalIndices; + normalIndices.setSize(vectorSize); + for (i = 0; i < normalIndices.size(); i++) + stream.read(&normalIndices[i]); + } + + // NormalLMapIndices + stream.read(&vectorSize); + mNormalLMapIndices.setSize(vectorSize); + for (U32 i = 0; i < mNormalLMapIndices.size(); i++) + { + if (fileVersion >= 13) + stream.read(&mNormalLMapIndices[i]); + else + { + U8 index = 0; + stream.read(&index); + + mNormalLMapIndices[i] = (U32)index; + } + } + + // AlarmLMapIndices + stream.read(&vectorSize); + mAlarmLMapIndices.setSize(vectorSize); + for (U32 i = 0; i < mAlarmLMapIndices.size(); i++) + { + if (fileVersion >= 13) + stream.read(&mAlarmLMapIndices[i]); + else + { + U8 index = 0; + stream.read(&index); + + mAlarmLMapIndices[i] = (U32)index; + } + } + + // mNullSurfaces + stream.read(&vectorSize); + mNullSurfaces.setSize(vectorSize); + for(i = 0; i < mNullSurfaces.size(); i++) + { + stream.read(&mNullSurfaces[i].windingStart); + stream.read(&mNullSurfaces[i].planeIndex); + stream.read(&mNullSurfaces[i].surfaceFlags); + + if (fileVersion >= 13) + stream.read(&mNullSurfaces[i].windingCount); + else + { + U8 count; + stream.read(&count); + mNullSurfaces[i].windingCount = (U32)count; + } + } + + // mLightmaps + stream.read(&vectorSize); + mLightmaps.setSize(vectorSize); + mLightmapKeep.setSize(vectorSize); + GBitmap dummyBmp; + for(i = 0; i < mLightmaps.size(); i++) + { + mLightmaps[i] = new GBitmap; + mLightmaps[i]->readBitmap("png",stream); + + if (!tgeInterior && (fileVersion == 0 || fileVersion == 5 || fileVersion >= 12)) + { + // The "light normal maps" or "light direction maps" were + // removed from Torque 3D... this just reads and throws + // them away. + dummyBmp.readBitmap("png",stream); + } + + stream.read(&mLightmapKeep[i]); + } + + // mSolidLeafSurfaces + stream.read(&vectorSize); + mSolidLeafSurfaces.setSize(vectorSize); + for(i = 0; i < mSolidLeafSurfaces.size(); i++) + { + stream.read(&mSolidLeafSurfaces[i]); + } + + // mAnimatedLights + mNumTriggerableLights = 0; + stream.read(&vectorSize); + mAnimatedLights.setSize(vectorSize); + for(i = 0; i < mAnimatedLights.size(); i++) + { + stream.read(&mAnimatedLights[i].nameIndex); + stream.read(&mAnimatedLights[i].stateIndex); + stream.read(&mAnimatedLights[i].stateCount); + stream.read(&mAnimatedLights[i].flags); + stream.read(&mAnimatedLights[i].duration); + + if((mAnimatedLights[i].flags & AnimationAmbient) == 0) + mNumTriggerableLights++; + } + + // mLightStates + stream.read(&vectorSize); + mLightStates.setSize(vectorSize); + for(i = 0; i < mLightStates.size(); i++) + { + stream.read(&mLightStates[i].red); + stream.read(&mLightStates[i].green); + stream.read(&mLightStates[i].blue); + stream.read(&mLightStates[i].activeTime); + stream.read(&mLightStates[i].dataIndex); + stream.read(&mLightStates[i].dataCount); + } + + // mStateData + stream.read(&vectorSize); + mStateData.setSize(vectorSize); + for(i = 0; i < mStateData.size(); i++) + { + stream.read(&mStateData[i].surfaceIndex); + stream.read(&mStateData[i].mapIndex); + stream.read(&mStateData[i].lightStateIndex); + } + + // mStateDataBuffer + stream.read(&vectorSize); + mStateDataBuffer.setSize(vectorSize); + U32 flags; + stream.read(&flags); + stream.read(mStateDataBuffer.size(), mStateDataBuffer.address()); + + // mNameBuffer + stream.read(&vectorSize); + mNameBuffer.setSize(vectorSize); + stream.read(mNameBuffer.size(), mNameBuffer.address()); + + // mSubObjects + stream.read(&vectorSize); + mSubObjects.setSize(vectorSize); + for (i = 0; i < mSubObjects.size(); i++) + { + InteriorSubObject* iso = InteriorSubObject::readISO(stream); + AssertFatal(iso != NULL, "Error, bad sub object in stream!"); + mSubObjects[i] = iso; + } + + // Convex hulls + stream.read(&vectorSize); + mConvexHulls.setSize(vectorSize); + for(i = 0; i < mConvexHulls.size(); i++) + { + stream.read(&mConvexHulls[i].hullStart); + stream.read(&mConvexHulls[i].hullCount); + stream.read(&mConvexHulls[i].minX); + stream.read(&mConvexHulls[i].maxX); + stream.read(&mConvexHulls[i].minY); + stream.read(&mConvexHulls[i].maxY); + stream.read(&mConvexHulls[i].minZ); + stream.read(&mConvexHulls[i].maxZ); + stream.read(&mConvexHulls[i].surfaceStart); + stream.read(&mConvexHulls[i].surfaceCount); + stream.read(&mConvexHulls[i].planeStart); + stream.read(&mConvexHulls[i].polyListPlaneStart); + stream.read(&mConvexHulls[i].polyListPointStart); + stream.read(&mConvexHulls[i].polyListStringStart); + + if (fileVersion >= 12) + stream.read(&mConvexHulls[i].staticMesh); + else + mConvexHulls[i].staticMesh = false; + } + + // Convex hull emit strings + stream.read(&vectorSize); + mConvexHullEmitStrings.setSize(vectorSize); + stream.read(mConvexHullEmitStrings.size(), mConvexHullEmitStrings.address()); + + // Hull indices + stream.read(&vectorSize); + mHullIndices.setSize(vectorSize); + for(i = 0; i < mHullIndices.size(); i++) + stream.read(&mHullIndices[i]); + + // Hull plane indices + stream.read(&vectorSize); + mHullPlaneIndices.setSize(vectorSize); + for(i = 0; i < mHullPlaneIndices.size(); i++) + stream.read(&mHullPlaneIndices[i]); + + // Hull emit string indices + stream.read(&vectorSize); + mHullEmitStringIndices.setSize(vectorSize); + for(i = 0; i < mHullEmitStringIndices.size(); i++) + stream.read(&mHullEmitStringIndices[i]); + + // Hull surface indices + stream.read(&vectorSize); + mHullSurfaceIndices.setSize(vectorSize); + for(i = 0; i < mHullSurfaceIndices.size(); i++) + stream.read(&mHullSurfaceIndices[i]); + + // PolyList planes + stream.read(&vectorSize); + mPolyListPlanes.setSize(vectorSize); + for(i = 0; i < mPolyListPlanes.size(); i++) + stream.read(&mPolyListPlanes[i]); + + // PolyList points + stream.read(&vectorSize); + mPolyListPoints.setSize(vectorSize); + for(i = 0; i < mPolyListPoints.size(); i++) + stream.read(&mPolyListPoints[i]); + + // PolyList strings + stream.read(&vectorSize); + mPolyListStrings.setSize(vectorSize); + for(i = 0; i < mPolyListStrings.size(); i++) + stream.read(&mPolyListStrings[i]); + + // Coord bins + for(i = 0; i < NumCoordBins * NumCoordBins; i++) + { + stream.read(&mCoordBins[i].binStart); + stream.read(&mCoordBins[i].binCount); + } + + // Coord bin indices + stream.read(&vectorSize); + mCoordBinIndices.setSize(vectorSize); + for(i = 0; i < mCoordBinIndices.size(); i++) + stream.read(&mCoordBinIndices[i]); + + // Coord bin mode + stream.read(&mCoordBinMode); + + // Ambient colors + stream.read(&mBaseAmbient); + stream.read(&mAlarmAmbient); + + if (fileVersion >= 10) + { + // Static meshes + stream.read(&vectorSize); + + mStaticMeshes.setSize(vectorSize); + for (i = 0; i < mStaticMeshes.size(); i++) + { + mStaticMeshes[i] = new InteriorSimpleMesh; + mStaticMeshes[i]->read(stream); + } + } + + if (fileVersion >= 11) + { + // Normals + stream.read(&vectorSize); + + mNormals.setSize(vectorSize); + for (i = 0; i < mNormals.size(); i++) + mathRead(stream, &mNormals[i]); + + // TexMatrices + stream.read(&vectorSize); + + mTexMatrices.setSize(vectorSize); + for (i = 0; i < mTexMatrices.size(); i++) + { + stream.read(&mTexMatrices[i].T); + stream.read(&mTexMatrices[i].N); + stream.read(&mTexMatrices[i].B); + } + + // TexMatIndices + stream.read(&vectorSize); + + mTexMatIndices.setSize(vectorSize); + for (i = 0; i < mTexMatIndices.size(); i++) + stream.read(&mTexMatIndices[i]); + } + + // For future expandability + U32 dummy; + if (fileVersion < 10) + { + stream.read(&dummy); if (dummy != 0) return false; + } + if (fileVersion < 11) + { + stream.read(&dummy); if (dummy != 0) return false; + stream.read(&dummy); if (dummy != 0) return false; + } + + // + // Support for interior light map border sizes. + // + U32 extendedlightmapdata; + stream.read(&extendedlightmapdata); + if(extendedlightmapdata == 1) + { + stream.read(&mLightMapBorderSize); + + //future expansion under current block (avoid using too + //many of the above expansion slots by allowing nested + //blocks)... + stream.read(&dummy); if (dummy != 0) return false; + } + + // Setup the zone planes + setupZonePlanes(); + truncateZoneTree(); + + buildSurfaceZones(); + + return (stream.getStatus() == Stream::Ok); +} + +bool Interior::write(Stream& stream) const +{ + AssertFatal(stream.hasCapability(Stream::StreamWrite), "Interior::write: non-write capable stream passed"); + AssertFatal(stream.getStatus() == Stream::Ok, "Interior::write: Error, stream in inconsistent state"); + + U32 i; + + // Version this stream + stream.write(smFileVersion); + + stream.write(mDetailLevel); + stream.write(mMinPixels); + mathWrite(stream, mBoundingBox); + mathWrite(stream, mBoundingSphere); + + stream.write(mHasAlarmState); + stream.write(mNumLightStateEntries); + + // Now write out our data vectors. Remember, for cross-platform capability, no + // structure writing is allowed... + + // mPlanes + writePlaneVector(stream); + + // mPoints + stream.write(mPoints.size()); + for (i = 0; i < mPoints.size(); i++) + mathWrite(stream, mPoints[i].point); + + // mPointVisibility + stream.write(mPointVisibility.size()); + stream.write(mPointVisibility.size(), mPointVisibility.address()); + + // mTexGenEQs + stream.write(mTexGenEQs.size()); + for (i = 0; i < mTexGenEQs.size(); i++) + { + mathWrite(stream, mTexGenEQs[i].planeX); + mathWrite(stream, mTexGenEQs[i].planeY); + } + + // mBSPNodes; + stream.write(mBSPNodes.size()); + for (i = 0; i < mBSPNodes.size(); i++) + { + stream.write(mBSPNodes[i].planeIndex); + + if (smFileVersion < 14) + { + stream.write(U16(mBSPNodes[i].frontIndex)); + stream.write(U16(mBSPNodes[i].backIndex)); + } + else + { + stream.write(mBSPNodes[i].frontIndex); + stream.write(mBSPNodes[i].backIndex); + } + } + + // mBSPSolidLeaves + stream.write(mBSPSolidLeaves.size()); + for (i = 0; i < mBSPSolidLeaves.size(); i++) + { + stream.write(mBSPSolidLeaves[i].surfaceIndex); + stream.write(mBSPSolidLeaves[i].surfaceCount); + } + + // MaterialList + mMaterialList->write(stream); + + // mWindings + stream.write(mWindings.size()); + for (i = 0; i < mWindings.size(); i++) + { + stream.write(mWindings[i]); + } + + // mWindingIndices + stream.write(mWindingIndices.size()); + for (i = 0; i < mWindingIndices.size(); i++) + { + stream.write(mWindingIndices[i].windingStart); + stream.write(mWindingIndices[i].windingCount); + } + + // mEdges + if (smFileVersion >= 12) + { + stream.write(mEdges.size()); + for (i = 0; i < mEdges.size(); i++) + { + stream.write(mEdges[i].vertexes[0]); + stream.write(mEdges[i].vertexes[1]); + stream.write(mEdges[i].faces[0]); + stream.write(mEdges[i].faces[1]); + } + } + + // mZones + stream.write(mZones.size()); + for (i = 0; i < mZones.size(); i++) + { + stream.write(mZones[i].portalStart); + stream.write(mZones[i].portalCount); + stream.write(mZones[i].surfaceStart); + stream.write(mZones[i].surfaceCount); + + if (smFileVersion >= 12) + { + stream.write(mZones[i].staticMeshStart); + stream.write(mZones[i].staticMeshCount); + } + + stream.write(mZones[i].flags); + } + + // Zone surfaces + stream.write(mZoneSurfaces.size()); + for (i = 0; i < mZoneSurfaces.size(); i++) + stream.write(mZoneSurfaces[i]); + + // Zone static meshes + if (smFileVersion >= 12) + { + stream.write(mZoneStaticMeshes.size()); + for (i = 0; i < mZoneStaticMeshes.size(); i++) + stream.write(mZoneStaticMeshes[i]); + } + + // mZonePortalList; + stream.write(mZonePortalList.size()); + for (i = 0; i < mZonePortalList.size(); i++) + stream.write(mZonePortalList[i]); + + // mPortals + stream.write(mPortals.size()); + for (i = 0; i < mPortals.size(); i++) + { + stream.write(mPortals[i].planeIndex); + stream.write(mPortals[i].triFanCount); + stream.write(mPortals[i].triFanStart); + stream.write(mPortals[i].zoneFront); + stream.write(mPortals[i].zoneBack); + } + + // mSurfaces + stream.write(mSurfaces.size()); + for (i = 0; i < mSurfaces.size(); i++) + { + stream.write(mSurfaces[i].windingStart); + + if (smFileVersion >= 13) + stream.write(mSurfaces[i].windingCount); + else + { + U8 count = (U8)mSurfaces[i].windingCount; + stream.write(count); + } + + stream.write(mSurfaces[i].planeIndex); + stream.write(mSurfaces[i].textureIndex); + stream.write(mSurfaces[i].texGenIndex); + stream.write(mSurfaces[i].surfaceFlags); + stream.write(mSurfaces[i].fanMask); + writeLMapTexGen(stream, mLMTexGenEQs[i].planeX, mLMTexGenEQs[i].planeY); + + stream.write(mSurfaces[i].lightCount); + stream.write(mSurfaces[i].lightStateInfoStart); + + if (smFileVersion >= 13) + { + stream.write(mSurfaces[i].mapOffsetX); + stream.write(mSurfaces[i].mapOffsetY); + stream.write(mSurfaces[i].mapSizeX); + stream.write(mSurfaces[i].mapSizeY); + } + else + { + U8 offX, offY, sizeX, sizeY; + offX = (U8)mSurfaces[i].mapOffsetX; + offY = (U8)mSurfaces[i].mapOffsetY; + sizeX = (U8)mSurfaces[i].mapSizeX; + sizeY = (U8)mSurfaces[i].mapSizeY; + + stream.write(offX); + stream.write(offY); + stream.write(sizeX); + stream.write(sizeY); + } + + if (smFileVersion == 0 || smFileVersion >= 12) + stream.write(mSurfaces[i].unused); + } + // NormalLMapIndices + stream.write(mNormalLMapIndices.size()); + for (U32 i = 0; i < mNormalLMapIndices.size(); i++) + { + if (smFileVersion >= 13) + stream.write(mNormalLMapIndices[i]); + else + { + U8 index = (U8)mNormalLMapIndices[i]; + stream.write(index); + } + } + + // AlarmLMapIndices + stream.write(mAlarmLMapIndices.size()); + for (U32 i = 0; i < mAlarmLMapIndices.size(); i++) + { + if (smFileVersion >= 13) + stream.write(mAlarmLMapIndices[i]); + else + { + U8 index = (U8)mAlarmLMapIndices[i]; + stream.write(index); + } + } + + + // mNullSurfaces + stream.write(mNullSurfaces.size()); + for(i = 0; i < mNullSurfaces.size(); i++) + { + stream.write(mNullSurfaces[i].windingStart); + stream.write(mNullSurfaces[i].planeIndex); + stream.write(mNullSurfaces[i].surfaceFlags); + + if (smFileVersion >= 13) + stream.write(mNullSurfaces[i].windingCount); + else + { + U8 count = (U8)mNullSurfaces[i].windingCount; + stream.write(count); + } + } + + // mLightmaps + stream.write(mLightmaps.size()); + for(i = 0; i < mLightmaps.size(); i++) + { + mLightmaps[i]->writeBitmap("png",stream); + + if (smFileVersion == 0 || smFileVersion >= 12) + { + // The "light normal maps" or "light direction maps" were + // removed from Torque 3D... this just writes a dummy 2x2 + // texture so that the read/write functions don't change. + GBitmap dummyBmp( 2, 2 ); + dummyBmp.writeBitmap("png",stream); + } + + stream.write(mLightmapKeep[i]); + } + + // mSolidLeafSurfaces + stream.write(mSolidLeafSurfaces.size()); + for(i = 0; i < mSolidLeafSurfaces.size(); i++) + { + stream.write(mSolidLeafSurfaces[i]); + } + + + // Animated lights + stream.write(mAnimatedLights.size()); + for(i = 0; i < mAnimatedLights.size(); i++) + { + stream.write(mAnimatedLights[i].nameIndex); + stream.write(mAnimatedLights[i].stateIndex); + stream.write(mAnimatedLights[i].stateCount); + stream.write(mAnimatedLights[i].flags); + stream.write(mAnimatedLights[i].duration); + } + + stream.write(mLightStates.size()); + for(i = 0; i < mLightStates.size(); i++) + { + stream.write(mLightStates[i].red); + stream.write(mLightStates[i].green); + stream.write(mLightStates[i].blue); + stream.write(mLightStates[i].activeTime); + stream.write(mLightStates[i].dataIndex); + stream.write(mLightStates[i].dataCount); + } + + // mStateData + stream.write(mStateData.size()); + for(i = 0; i < mStateData.size(); i++) + { + stream.write(mStateData[i].surfaceIndex); + stream.write(mStateData[i].mapIndex); + stream.write(mStateData[i].lightStateIndex); + } + + // mStateDataBuffer: Note: superfluous 0 is for flags in future versions. + // that may add compression. This way, we can maintain + // compatability with previous versions. + stream.write(mStateDataBuffer.size()); + stream.write(U32(0)); + stream.write(mStateDataBuffer.size(), mStateDataBuffer.address()); + + // mNameBuffer + stream.write(mNameBuffer.size()); + stream.write(mNameBuffer.size(), mNameBuffer.address()); + + // mSubObjects + stream.write(mSubObjects.size()); + for (i = 0; i < mSubObjects.size(); i++) + { + bool writeSuccess = mSubObjects[i]->writeISO(stream); + AssertFatal(writeSuccess == true, "Error writing sub object to stream!"); + } + + // Convex hulls + stream.write(mConvexHulls.size()); + for(i = 0; i < mConvexHulls.size(); i++) + { + stream.write(mConvexHulls[i].hullStart); + stream.write(mConvexHulls[i].hullCount); + stream.write(mConvexHulls[i].minX); + stream.write(mConvexHulls[i].maxX); + stream.write(mConvexHulls[i].minY); + stream.write(mConvexHulls[i].maxY); + stream.write(mConvexHulls[i].minZ); + stream.write(mConvexHulls[i].maxZ); + stream.write(mConvexHulls[i].surfaceStart); + stream.write(mConvexHulls[i].surfaceCount); + stream.write(mConvexHulls[i].planeStart); + stream.write(mConvexHulls[i].polyListPlaneStart); + stream.write(mConvexHulls[i].polyListPointStart); + stream.write(mConvexHulls[i].polyListStringStart); + + if (smFileVersion >= 12) + stream.write(mConvexHulls[i].staticMesh); + } + + stream.write(mConvexHullEmitStrings.size()); + stream.write(mConvexHullEmitStrings.size(), mConvexHullEmitStrings.address()); + + stream.write(mHullIndices.size()); + for(i = 0; i < mHullIndices.size(); i++) + stream.write(mHullIndices[i]); + + stream.write(mHullPlaneIndices.size()); + for(i = 0; i < mHullPlaneIndices.size(); i++) + stream.write(mHullPlaneIndices[i]); + + stream.write(mHullEmitStringIndices.size()); + for(i = 0; i < mHullEmitStringIndices.size(); i++) + stream.write(mHullEmitStringIndices[i]); + + stream.write(mHullSurfaceIndices.size()); + for(i = 0; i < mHullSurfaceIndices.size(); i++) + stream.write(mHullSurfaceIndices[i]); + + stream.write(mPolyListPlanes.size()); + for(i = 0; i < mPolyListPlanes.size(); i++) + stream.write(mPolyListPlanes[i]); + + stream.write(mPolyListPoints.size()); + for(i = 0; i < mPolyListPoints.size(); i++) + stream.write(mPolyListPoints[i]); + + stream.write(mPolyListStrings.size()); + for(i = 0; i < mPolyListStrings.size(); i++) + stream.write(mPolyListStrings[i]); + + // Coord bins + for(i = 0; i < NumCoordBins * NumCoordBins; i++) + { + stream.write(mCoordBins[i].binStart); + stream.write(mCoordBins[i].binCount); + } + stream.write(mCoordBinIndices.size()); + for(i = 0; i < mCoordBinIndices.size(); i++) + stream.write(mCoordBinIndices[i]); + stream.write(mCoordBinMode); + + // Ambient colors... + stream.write(mBaseAmbient); + stream.write(mAlarmAmbient); + + if (smFileVersion >= 10) + { + // Static meshes + stream.write(mStaticMeshes.size()); + for (i = 0; i < mStaticMeshes.size(); i++) + mStaticMeshes[i]->write(stream); + } + else + stream.write(U32(0)); + + if (smFileVersion >= 11) + { + // Normals + stream.write(mNormals.size()); + for (i = 0; i < mNormals.size(); i++) + mathWrite(stream, mNormals[i]); + + // TexMatrices + stream.write(mTexMatrices.size()); + for (i = 0; i < mTexMatrices.size(); i++) + { + stream.write(mTexMatrices[i].T); + stream.write(mTexMatrices[i].N); + stream.write(mTexMatrices[i].B); + } + + // TexMatIndices + stream.write(mTexMatIndices.size()); + for (i = 0; i < mTexMatIndices.size(); i++) + stream.write(mTexMatIndices[i]); + } + else + { + stream.write(U32(0)); + stream.write(U32(0)); + } + + // + // Support for interior light map border sizes. + // + if(mLightMapBorderSize > 0) + { + stream.write(U32(1));//flag new block... + stream.write(U32(mLightMapBorderSize));//new block data.. + + //future expansion under current block (avoid using too + //many of the above expansion slots by allowing nested + //blocks)... + stream.write(U32(0)); + } + else + { + stream.write(U32(0)); + } + + return stream.getStatus() == Stream::Ok; +} + +bool Interior::readVehicleCollision(Stream& stream) +{ + AssertFatal(stream.hasCapability(Stream::StreamRead), "Interior::read: non-read capable stream passed"); + AssertFatal(stream.getStatus() == Stream::Ok, "Interior::read: Error, stream in inconsistent state"); + + S32 i; + + // Version this stream. We only load stream of the current version + U32 fileVersion; + stream.read(&fileVersion); + if (fileVersion > smFileVersion) + { + Con::errorf(ConsoleLogEntry::General, "Interior::read: incompatible file version found."); + return false; + } + + U32 vectorSize; + + // Convex hulls + stream.read(&vectorSize); + mVehicleConvexHulls.setSize(vectorSize); + for(i = 0; i < mVehicleConvexHulls.size(); i++) + { + stream.read(&mVehicleConvexHulls[i].hullStart); + stream.read(&mVehicleConvexHulls[i].hullCount); + stream.read(&mVehicleConvexHulls[i].minX); + stream.read(&mVehicleConvexHulls[i].maxX); + stream.read(&mVehicleConvexHulls[i].minY); + stream.read(&mVehicleConvexHulls[i].maxY); + stream.read(&mVehicleConvexHulls[i].minZ); + stream.read(&mVehicleConvexHulls[i].maxZ); + stream.read(&mVehicleConvexHulls[i].surfaceStart); + stream.read(&mVehicleConvexHulls[i].surfaceCount); + stream.read(&mVehicleConvexHulls[i].planeStart); + stream.read(&mVehicleConvexHulls[i].polyListPlaneStart); + stream.read(&mVehicleConvexHulls[i].polyListPointStart); + stream.read(&mVehicleConvexHulls[i].polyListStringStart); + } + + stream.read(&vectorSize); + mVehicleConvexHullEmitStrings.setSize(vectorSize); + stream.read(mVehicleConvexHullEmitStrings.size(), mVehicleConvexHullEmitStrings.address()); + + stream.read(&vectorSize); + mVehicleHullIndices.setSize(vectorSize); + for(i = 0; i < mVehicleHullIndices.size(); i++) + stream.read(&mVehicleHullIndices[i]); + + stream.read(&vectorSize); + mVehicleHullPlaneIndices.setSize(vectorSize); + for(i = 0; i < mVehicleHullPlaneIndices.size(); i++) + stream.read(&mVehicleHullPlaneIndices[i]); + + stream.read(&vectorSize); + mVehicleHullEmitStringIndices.setSize(vectorSize); + for(i = 0; i < mVehicleHullEmitStringIndices.size(); i++) + stream.read(&mVehicleHullEmitStringIndices[i]); + + stream.read(&vectorSize); + mVehicleHullSurfaceIndices.setSize(vectorSize); + for(i = 0; i < mVehicleHullSurfaceIndices.size(); i++) + stream.read(&mVehicleHullSurfaceIndices[i]); + + stream.read(&vectorSize); + mVehiclePolyListPlanes.setSize(vectorSize); + for(i = 0; i < mVehiclePolyListPlanes.size(); i++) + stream.read(&mVehiclePolyListPlanes[i]); + + stream.read(&vectorSize); + mVehiclePolyListPoints.setSize(vectorSize); + for(i = 0; i < mVehiclePolyListPoints.size(); i++) + stream.read(&mVehiclePolyListPoints[i]); + + stream.read(&vectorSize); + mVehiclePolyListStrings.setSize(vectorSize); + for(i = 0; i < mVehiclePolyListStrings.size(); i++) + stream.read(&mVehiclePolyListStrings[i]); + + stream.read(&vectorSize); + mVehicleNullSurfaces.setSize(vectorSize); + for(i = 0; i < mVehicleNullSurfaces.size(); i++) + { + stream.read(&mVehicleNullSurfaces[i].windingStart); + stream.read(&mVehicleNullSurfaces[i].planeIndex); + stream.read(&mVehicleNullSurfaces[i].surfaceFlags); + stream.read(&mVehicleNullSurfaces[i].windingCount); + } + + stream.read(&vectorSize); + mVehiclePoints.setSize(vectorSize); + for(i = 0; i < mVehiclePoints.size(); i++) + mathRead(stream, &mVehiclePoints[i].point); + + stream.read(&vectorSize); + mVehiclePlanes.setSize(vectorSize); + for(i = 0; i < mVehiclePlanes.size(); i++) + mathRead(stream, &mVehiclePlanes[i]); + + stream.read(&vectorSize); + mVehicleWindings.setSize(vectorSize); + for(i = 0; i < mVehicleWindings.size(); i++) + { + stream.read(&mVehicleWindings[i]); + } + + stream.read(&vectorSize); + mVehicleWindingIndices.setSize(vectorSize); + for(i = 0; i < mVehicleWindingIndices.size(); i++) + { + stream.read(&mVehicleWindingIndices[i].windingStart); + stream.read(&mVehicleWindingIndices[i].windingCount); + } + + return true; +} + +bool Interior::writeVehicleCollision(Stream& stream) const +{ + AssertFatal(stream.hasCapability(Stream::StreamWrite), "Interior::write: non-write capable stream passed"); + AssertFatal(stream.getStatus() == Stream::Ok, "Interior::write: Error, stream in inconsistent state"); + + U32 i; + + // Version this stream + stream.write(smFileVersion); + + // Convex hulls + stream.write(mVehicleConvexHulls.size()); + for(i = 0; i < mVehicleConvexHulls.size(); i++) + { + stream.write(mVehicleConvexHulls[i].hullStart); + stream.write(mVehicleConvexHulls[i].hullCount); + stream.write(mVehicleConvexHulls[i].minX); + stream.write(mVehicleConvexHulls[i].maxX); + stream.write(mVehicleConvexHulls[i].minY); + stream.write(mVehicleConvexHulls[i].maxY); + stream.write(mVehicleConvexHulls[i].minZ); + stream.write(mVehicleConvexHulls[i].maxZ); + stream.write(mVehicleConvexHulls[i].surfaceStart); + stream.write(mVehicleConvexHulls[i].surfaceCount); + stream.write(mVehicleConvexHulls[i].planeStart); + stream.write(mVehicleConvexHulls[i].polyListPlaneStart); + stream.write(mVehicleConvexHulls[i].polyListPointStart); + stream.write(mVehicleConvexHulls[i].polyListStringStart); + } + + stream.write(mVehicleConvexHullEmitStrings.size()); + stream.write(mVehicleConvexHullEmitStrings.size(), mVehicleConvexHullEmitStrings.address()); + + stream.write(mVehicleHullIndices.size()); + for(i = 0; i < mVehicleHullIndices.size(); i++) + stream.write(mVehicleHullIndices[i]); + + stream.write(mVehicleHullPlaneIndices.size()); + for(i = 0; i < mVehicleHullPlaneIndices.size(); i++) + stream.write(mVehicleHullPlaneIndices[i]); + + stream.write(mVehicleHullEmitStringIndices.size()); + for(i = 0; i < mVehicleHullEmitStringIndices.size(); i++) + stream.write(mVehicleHullEmitStringIndices[i]); + + stream.write(mVehicleHullSurfaceIndices.size()); + for(i = 0; i < mVehicleHullSurfaceIndices.size(); i++) + stream.write(mVehicleHullSurfaceIndices[i]); + + stream.write(mVehiclePolyListPlanes.size()); + for(i = 0; i < mVehiclePolyListPlanes.size(); i++) + stream.write(mVehiclePolyListPlanes[i]); + + stream.write(mVehiclePolyListPoints.size()); + for(i = 0; i < mVehiclePolyListPoints.size(); i++) + stream.write(mVehiclePolyListPoints[i]); + + stream.write(mVehiclePolyListStrings.size()); + for(i = 0; i < mVehiclePolyListStrings.size(); i++) + stream.write(mVehiclePolyListStrings[i]); + + stream.write(mVehicleNullSurfaces.size()); + for(i = 0; i < mVehicleNullSurfaces.size(); i++) + { + stream.write(mVehicleNullSurfaces[i].windingStart); + stream.write(mVehicleNullSurfaces[i].planeIndex); + stream.write(mVehicleNullSurfaces[i].surfaceFlags); + stream.write(mVehicleNullSurfaces[i].windingCount); + } + + stream.write(mVehiclePoints.size()); + for(i = 0; i < mVehiclePoints.size(); i++) + mathWrite(stream, mVehiclePoints[i].point); + + stream.write(mVehiclePlanes.size()); + for(i = 0; i < mVehiclePlanes.size(); i++) + mathWrite(stream, mVehiclePlanes[i]); + + stream.write(mVehicleWindings.size()); + for(i = 0; i < mVehicleWindings.size(); i++) + stream.write(mVehicleWindings[i]); + + stream.write(mVehicleWindingIndices.size()); + for(i = 0; i < mVehicleWindingIndices.size(); i++) + { + stream.write(mVehicleWindingIndices[i].windingStart); + stream.write(mVehicleWindingIndices[i].windingCount); + } + + return true; +} + +bool Interior::readSurface(Stream& stream, Surface& surface, TexGenPlanes& texgens, const bool tgeInterior) +{ + // If we end up reading any invalid data then odds are that we + // are no longer correctly reading from the stream and have gotten + // off because this is a TGE version 0 Interior so we bail. + // That is why you will see checks all the way through + stream.read(&surface.windingStart); + + if (surface.windingStart >= mWindings.size()) + return false; + + if (mFileVersion >= 13) + stream.read(&surface.windingCount); + else + { + U8 count; + stream.read(&count); + surface.windingCount = (U32)count; + } + + if (surface.windingStart + surface.windingCount > mWindings.size()) + return false; + + stream.read(&surface.planeIndex); + + if (U32(surface.planeIndex & ~0x8000) >= mPlanes.size()) + return false; + + stream.read(&surface.textureIndex); + + if (surface.textureIndex >= mMaterialList->size()) + return false; + + stream.read(&surface.texGenIndex); + + if (surface.texGenIndex >= mTexGenEQs.size()) + return false; + + stream.read(&surface.surfaceFlags); + stream.read(&surface.fanMask); + + // If reading the lightmap texgen fails then most likely this is a + // TGE version 0 Interior (it gets offset by the "unused" read below + if (readLMapTexGen(stream, texgens.planeX, texgens.planeY) == false) + return false; + + stream.read(&surface.lightCount); + stream.read(&surface.lightStateInfoStart); + + if (mFileVersion >= 13) + { + stream.read(&surface.mapOffsetX); + stream.read(&surface.mapOffsetY); + stream.read(&surface.mapSizeX); + stream.read(&surface.mapSizeY); + } + else + { + U8 offX, offY, sizeX, sizeY; + stream.read(&offX); + stream.read(&offY); + stream.read(&sizeX); + stream.read(&sizeY); + + surface.mapOffsetX = (U32)offX; + surface.mapOffsetY = (U32)offY; + surface.mapSizeX = (U32)sizeX; + surface.mapSizeY = (U32)sizeY; + } + + if (!tgeInterior && (mFileVersion == 0 || mFileVersion == 5 || mFileVersion >= 12)) + stream.read(&surface.unused); + + if (mFileVersion == 5) + { + U32 brushId; + stream.read(&brushId); + } + + return true; +} + +bool Interior::readLMapTexGen(Stream& stream, PlaneF& planeX, PlaneF& planeY) +{ + F32 genX[4]; + F32 genY[4]; + + for(U32 i = 0; i < 4; i++) + { + genX[i] = 0.0f; + genY[i] = 0.0f; + } + + U16 finalWord; + stream.read(&finalWord); + stream.read(&genX[3]); + stream.read(&genY[3]); + + // Unpack the final word. + U32 logScaleY = (finalWord >> 0) & ((1 << 6) - 1); + U32 logScaleX = (finalWord >> 6) & ((1 << 6) - 1); + U16 stEnc = (finalWord >> 13) & 7; + + S32 sc, tc; + switch(stEnc) + { + case 0: sc = 0; tc = 1; break; + case 1: sc = 0; tc = 2; break; + case 2: sc = 1; tc = 0; break; + case 3: sc = 1; tc = 2; break; + case 4: sc = 2; tc = 0; break; + case 5: sc = 2; tc = 1; break; + + default: + sc = tc = -1; + // This is potentially an invalid st coord encoding however *most* times + // this is caused by attempting to load a TGE version 0 Interior + return false; + } + + U32 invScaleX = 1 << logScaleX; + U32 invScaleY = 1 << logScaleY; + + genX[sc] = F32(1.0 / F64(invScaleX)); + genY[tc] = F32(1.0 / F64(invScaleY)); + + planeX.x = genX[0]; + planeX.y = genX[1]; + planeX.z = genX[2]; + planeX.d = genX[3]; + planeY.x = genY[0]; + planeY.y = genY[1]; + planeY.z = genY[2]; + planeY.d = genY[3]; + + return stream.getStatus() == Stream::Ok; +} + +bool Interior::writeLMapTexGen(Stream& stream, const PlaneF& planeX, const PlaneF& planeY) const +{ + F32 genX[4], genY[4]; + + genX[0] = planeX.x; + genX[1] = planeX.y; + genX[2] = planeX.z; + genX[3] = planeX.d; + genY[0] = planeY.x; + genY[1] = planeY.y; + genY[2] = planeY.z; + genY[3] = planeY.d; + + // The tex gen for lmaps is a special case. + // there are only 4 parameters that matter, + // an inverse power of 2 in the x and y, and the + // fp offsets in x and y. We can encode the + // scales completely in U16 and we'll just write out + // the offsets. First, determine which coords we're + // writing... + // + S32 sc = -1; + S32 tc = -1; + if(genX[0] != 0.0) sc = 0; + else if(genX[1] != 0.0) sc = 1; + else if(genX[2] != 0.0) sc = 2; + + if(genY[0] != 0.0) tc = 0; + else if(genY[1] != 0.0) tc = 1; + else if(genY[2] != 0.0) tc = 2; + AssertFatal(sc != -1 && tc != -1 && sc != tc, "Hm, something wrong here."); + + U32 invScaleX = U32((1.0f / genX[sc]) + 0.5); + U32 invScaleY = U32((1.0f / genY[tc]) + 0.5); + AssertISV(invScaleX && isPow2(invScaleX) && invScaleY && isPow2(invScaleY), "Not a power of 2? Something wrong"); + + U32 logScaleX = getBinLog2(invScaleX); + U32 logScaleY = getBinLog2(invScaleY); + AssertFatal(logScaleX < 63 && logScaleY < 63, "Error, you've set the lightmap scale WAAYYY to high!"); + + // We need 3 bits to encode sc and tc, which leaves us 6 bits for logScaleX + // and logScaleY + S16 stEnc = -1; + if(sc == 0 && tc == 1) stEnc = 0; + else if(sc == 0 && tc == 2) stEnc = 1; + else if(sc == 1 && tc == 0) stEnc = 2; + else if(sc == 1 && tc == 2) stEnc = 3; + else if(sc == 2 && tc == 0) stEnc = 4; + else if(sc == 2 && tc == 1) stEnc = 5; + AssertFatal(stEnc != -1, avar("Hm. This should never happen. (%d, %d)", sc, tc)); + + U16 finalWord = U16(stEnc) << 13; + finalWord |= logScaleX << 6; + finalWord |= logScaleY << 0; + + stream.write(finalWord); + stream.write(genX[3]); + stream.write(genY[3]); + + return stream.getStatus() == Stream::Ok; +} + +bool Interior::writePlaneVector(Stream& stream) const +{ + // This is pretty slow, but who cares? + // + Vector uniqueNormals(mPlanes.size()); + Vector uniqueIndices(mPlanes.size()); + + U32 i; + + for(i = 0; i < mPlanes.size(); i++) + { + bool inserted = false; + for(U32 j = 0; j < uniqueNormals.size(); j++) + { + if(mPlanes[i] == uniqueNormals[j]) + { + // Hah! Already have this one... + uniqueIndices.push_back(j); + inserted = true; + break; + } + } + + if(inserted == false) + { + // Gotta do it ourselves... + uniqueIndices.push_back(uniqueNormals.size()); + uniqueNormals.push_back(Point3F(mPlanes[i].x, mPlanes[i].y, mPlanes[i].z)); + } + } + + // Ok, what we have now, is a list of unique normals, a set of indices into + // that vector, and the distances that we still have to write out by hand. + // Hop to it! + stream.write(uniqueNormals.size()); + for(i = 0; i < uniqueNormals.size(); i++) + mathWrite(stream, uniqueNormals[i]); + + stream.write(mPlanes.size()); + for(i = 0; i < mPlanes.size(); i++) + { + stream.write(uniqueIndices[i]); + stream.write(mPlanes[i].d); + } + + return(stream.getStatus() == Stream::Ok); +} + +bool Interior::readPlaneVector(Stream& stream) +{ + U32 vectorSize; + + stream.read(&vectorSize); + Point3F* normals = new Point3F[vectorSize]; + U32 i; + for(i = 0; i < vectorSize; i++) + mathRead(stream, &normals[i]); + + U16 index; + stream.read(&vectorSize); + mPlanes.setSize(vectorSize); + for(i = 0; i < mPlanes.size(); i++) + { + stream.read(&index); + stream.read(&mPlanes[i].d); + mPlanes[i].x = normals[index].x; + mPlanes[i].y = normals[index].y; + mPlanes[i].z = normals[index].z; + } + + delete [] normals; + + return(stream.getStatus() == Stream::Ok); +} + + +bool Interior::getUnifiedZone(const U32 index, S32* zone) +{ + if(isBSPLeafIndex(index)) + { + if(isBSPSolidLeaf(index)) + *zone = -1; + else + *zone = S32(getBSPEmptyLeafZone(index)); + return true; + } + else + { + S32 frontZone, backZone; + bool frontUnified = getUnifiedZone(mBSPNodes[index].frontIndex, &frontZone); + bool backUnified = getUnifiedZone(mBSPNodes[index].backIndex, &backZone); + if(frontUnified && backUnified) + { + if(frontZone == backZone) + { + *zone = frontZone; + return true; + } + else + { + if(frontZone == -1 || backZone == -1) + { + // DMMFIX: Once the interior file format is able to distinguish + // between structural and detail nodes in the runtime bsp, + // we can make this work a little better. + return false; + } + else + { + // Not equal, and neither is -1, no unified zone possible + return false; + } + } + } + else + { + return false; + } + } +} + +void Interior::truncateZoneNode(const U32 index) +{ + S32 unifiedZone; + bool unified = getUnifiedZone(index, &unifiedZone); + if(unified) + { + // Aha! + if(isBSPLeafIndex(index)) + return; + + if(unifiedZone == -1) + mBSPNodes[index].terminalZone = U16(0xFFFF); + else + mBSPNodes[index].terminalZone = U16(0x8000) | U16(unifiedZone); + } + else + { + // Sigh. + if(isBSPLeafIndex(mBSPNodes[index].frontIndex) == false) + truncateZoneNode(mBSPNodes[index].frontIndex); + if(isBSPLeafIndex(mBSPNodes[index].backIndex) == false) + truncateZoneNode(mBSPNodes[index].backIndex); + } +} + +void Interior::truncateZoneTree() +{ + for(U32 i = 0; i < mBSPNodes.size(); i++) + { + mBSPNodes[i].terminalZone = 0; + } + + if(mBSPNodes.size() > 0) + truncateZoneNode(0); +} + + +void Interior::setupZonePlanes() +{ + U16* temp = new U16[mPlanes.size() * mZones.size()]; + U32 tempSize = 0; + + for(U32 i = 0; i < mZones.size(); i++) + { + Zone& rZone = mZones[i]; + + BitVector usedPlanes; + usedPlanes.setSize(mPlanes.size()); + usedPlanes.clear(); + + U32 j; + for(j = 0; j < rZone.surfaceCount; j++) + { + Surface& rSurface = mSurfaces[mZoneSurfaces[rZone.surfaceStart + j]]; + usedPlanes.set(getPlaneIndex(rSurface.planeIndex)); + } + + rZone.planeStart = tempSize; + for(j = 0; j < mPlanes.size(); j++) + { + if(usedPlanes.test(j)) + { + AssertFatal(tempSize < mPlanes.size() * mZones.size(), "Error, out of bounds plane list!"); + temp[tempSize++] = j; + } + } + rZone.planeCount = tempSize - rZone.planeStart; + } + + mZonePlanes.setSize(tempSize); + for(U32 j = 0; j < tempSize; j++) + mZonePlanes[j] = temp[j]; + + delete [] temp; +} diff --git a/Engine/source/interior/interiorInstance.cpp b/Engine/source/interior/interiorInstance.cpp new file mode 100644 index 000000000..3bacfde4a --- /dev/null +++ b/Engine/source/interior/interiorInstance.cpp @@ -0,0 +1,1547 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "interior/interiorInstance.h" + +#include "interior/interior.h" +#include "console/consoleTypes.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "scene/zones/sceneTraversalState.h" +#include "scene/zones/sceneRootZone.h" +#include "core/stream/bitStream.h" +#include "core/stream/fileStream.h" +#include "gfx/bitmap/gBitmap.h" +#include "math/mathIO.h" +#include "gui/worldEditor/editor.h" +#include "interior/interiorResObjects.h" +#include "scene/simPath.h" +#include "interior/forceField.h" +#include "lighting/lightManager.h" +#include "collision/convex.h" +#include "sfx/sfxProfile.h" +#include "sfx/sfxEnvironment.h" +#include "core/frameAllocator.h" +#include "sim/netConnection.h" +#include "platform/profiler.h" +#include "gui/3d/guiTSControl.h" +#include "math/mathUtils.h" +#include "renderInstance/renderPassManager.h" +#include "core/resourceManager.h" +#include "materials/materialManager.h" +#include "materials/materialFeatureTypes.h" +#include "materials/matInstance.h" +#include "collision/concretePolyList.h" +#include "T3D/physics/physicsPlugin.h" +#include "T3D/physics/physicsBody.h" +#include "T3D/physics/physicsCollision.h" +#include "console/engineAPI.h" + +#ifdef TORQUE_COLLADA +#include "ts/collada/colladaUtils.h" +#endif + + +IMPLEMENT_CO_NETOBJECT_V1(InteriorInstance); + +ConsoleDocClass( InteriorInstance, + "@brief Object used to represent buildings and other architectural structures (legacy).\n\n" + + "Interiors are made up entirely from convex hulls or, as they are more commonly known as by game " + "artists, brushes. So what you see is what you collide against. There is no difference between the " + "visible meshes and the collision meshes.\n\n" + + "Unlike a DTS or COLLADA mesh, interiors do not support any animation. They also do not support " + "transparent textures. If you need animation or transparency then you are forced to use other model objects.\n\n" + + "It is important to note that interiors are no longer the preferred format for large structures. It is an " + "old format, which does not have much to offer above DTS or COLLADA. They are still included in Torque 3D " + "for the sake of backwards compatibility for developers porting older TGE or TGEA projects. It will be " + "deprecated soon.\n\n" + + "@ingroup gameObjects" +); + + +static const U32 csgMaxZoneSize = 256; +static bool sgScopeBoolArray[256]; + + +bool InteriorInstance::smDontRestrictOutside = false; +F32 InteriorInstance::smDetailModification = 1.0f; + + + +//----------------------------------------------------------------------------- + +InteriorInstance::InteriorInstance() +{ + mAlarmState = false; + + mInteriorFileName = NULL; + mTypeMask |= InteriorObjectType | StaticObjectType | StaticShapeObjectType; + mZoneFlags.clear( ZoneFlag_IsClosedOffSpace ); // Interiors are open spaces. + + mForcedDetailLevel = -1; + + mConvexList = new Convex; + mCRC = 0; + + mPhysicsRep = NULL; +} + +//----------------------------------------------------------------------------- + +InteriorInstance::~InteriorInstance() +{ + delete mConvexList; + mConvexList = NULL; + + // GFX2_RENDER_MERGE + //for (U32 i = 0; i < mReflectPlanes.size(); i++) + // mReflectPlanes[i].clearTextures(); +} + +//----------------------------------------------------------------------------- + +void InteriorInstance::inspectPostApply() +{ + // Apply any transformations set in the editor + Parent::inspectPostApply(); + + // Update the Transform on Editor Apply. + setMaskBits(TransformMask); +} + +//----------------------------------------------------------------------------- + +void InteriorInstance::initPersistFields() +{ + addGroup("Media"); + + addProtectedField( "interiorFile", TypeFilename, Offset( mInteriorFileName, InteriorInstance ), + &_setInteriorFile, &defaultProtectedGetFn, + "Path and filename of the Interior file (.DIF) to load for this InteriorInstance."); + + endGroup("Media"); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +void InteriorInstance::consoleInit() +{ + //-------------------------------------- Class level variables + Con::addVariable( "pref::Interior::VertexLighting", TypeBool, &Interior::smUseVertexLighting, + "Forces all InteriorInstances to not render their lightmaps.\n" + "@ingroup Interior" ); + Con::addVariable( "pref::Interior::detailAdjust", TypeF32, &InteriorInstance::smDetailModification, + "Forces all InteriorInstance rendering to a particular detail level.\n" + "@ingroup Interior" ); + + // DEBUG ONLY!!! +#ifndef TORQUE_SHIPPING + Con::addVariable( "Interior::DontRestrictOutside", TypeBool, &smDontRestrictOutside, + "Render only the outside zone of all InteriorInstances.\n" + "@ingroup Interior" ); +#endif +} + +//----------------------------------------------------------------------------- + +#ifdef TORQUE_COLLADA + +void InteriorInstance::exportToCollada(bool bakeTransform) +{ + if (mInteriorRes->getNumDetailLevels() == 0) + { + Con::errorf("InteriorInstance::exportToCollada() called an InteriorInstance with no Interior"); + return; + } + + // For now I am only worrying about the highest lod + Interior* pInterior = mInteriorRes->getDetailLevel(0); + + if (!pInterior) + { + Con::errorf("InteriorInstance::exportToCollada() called an InteriorInstance with an invalid Interior"); + return; + } + + // Get an optimized version of our mesh + OptimizedPolyList interiorMesh; + + if (bakeTransform) + { + MatrixF mat = getTransform(); + Point3F scale = getScale(); + + pInterior->buildExportPolyList(interiorMesh, &mat, &scale); + } + else + pInterior->buildExportPolyList(interiorMesh); + + // Get our export path + Torque::Path colladaFile = mInteriorRes.getPath(); + + // Make sure to set our Collada extension + colladaFile.setExtension("dae"); + + // Use the InteriorInstance name if possible + String meshName = getName(); + + // Otherwise use the DIF's file name + if (meshName.isEmpty()) + meshName = colladaFile.getFileName(); + + // If we are baking the transform then append + // a CRC version of the transform to the mesh/file name + if (bakeTransform) + { + F32 trans[19]; + + const MatrixF& mat = getTransform(); + const Point3F& scale = getScale(); + + // Copy in the transform + for (U32 i = 0; i < 4; i++) + { + for (U32 j = 0; j < 4; j++) + { + trans[i * 4 + j] = mat(i, j); + } + } + + // Copy in the scale + trans[16] = scale.x; + trans[17] = scale.y; + trans[18] = scale.z; + + U32 crc = CRC::calculateCRC(trans, sizeof(F32) * 19); + + meshName += String::ToString("_%x", crc); + } + + // Set the file name as the meshName + colladaFile.setFileName(meshName); + + // Use a ColladaUtils function to do the actual export to a Collada file + ColladaUtils::exportToCollada(colladaFile, interiorMesh, meshName); +} +#endif + +//----------------------------------------------------------------------------- + +bool InteriorInstance::onAdd() +{ + if(! _loadInterior()) + return false; + + if(!Parent::onAdd()) + return false; + + addToScene(); + + if ( PHYSICSMGR && mInteriorRes && mInteriorRes->getNumDetailLevels() > 0 ) + { + // TODO: We need to cache the collision by resource name + // and reuse it across multiple instances. + + // Get the interior collision geometry. + ConcretePolyList polylist; + mInteriorRes->getDetailLevel(0)->buildPolyList( &polylist, Box3F(999999.0f), MatrixF::Identity, getScale() ); + polylist.triangulate(); + + // Look out... this could possibly happen! + if ( !polylist.isEmpty() ) + { + // Use a triangle mesh for collision. + PhysicsCollision *colShape = PHYSICSMGR->createCollision(); + colShape->addTriangleMesh( polylist.mVertexList.address(), + polylist.mVertexList.size(), + polylist.mIndexList.address(), + polylist.mIndexList.size() / 3, + MatrixF::Identity ); + + PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" ); + mPhysicsRep = PHYSICSMGR->createBody(); + mPhysicsRep->init( colShape, 0, 0, this, world ); + mPhysicsRep->setTransform( getTransform() ); + } + } + + return true; +} + +//----------------------------------------------------------------------------- + +void InteriorInstance::onRemove() +{ + SAFE_DELETE( mPhysicsRep ); + + _unloadInterior(); + + removeFromScene(); + + Parent::onRemove(); +} + +//----------------------------------------------------------------------------- + +bool InteriorInstance::_loadInterior() +{ + U32 i; + + // Load resource + mInteriorRes = ResourceManager::get().load(mInteriorFileName); + if (bool(mInteriorRes) == false) { + Con::errorf(ConsoleLogEntry::General, "Unable to load interior: %s", mInteriorFileName); + NetConnection::setLastError("Unable to load interior: %s", mInteriorFileName); + return false; + } + if(isClientObject()) + { + if(mCRC != mInteriorRes.getChecksum()) + { + NetConnection::setLastError("Local interior file '%s' does not match version on server.", mInteriorFileName); + return false; + } + for (i = 0; i < mInteriorRes->getNumDetailLevels(); i++) { + // ok, if the material list load failed... + // if this is a local connection, we'll assume that's ok + // and just have white textures... + // otherwise we want to return false. + Interior* pInterior = mInteriorRes->getDetailLevel(i); + if(!pInterior->prepForRendering(mInteriorRes.getPath().getFullPath().c_str()) ) + { + if(!bool(mServerObject)) + { + return false; + } + } + } + + // copy planar reflect list from top detail level - for now + Interior* pInterior = mInteriorRes->getDetailLevel(0); + if( pInterior->mReflectPlanes.size() ) + { + for ( i = 0; i < pInterior->mReflectPlanes.size(); i++ ) + { + mPlaneReflectors.increment(); + PlaneReflector &plane = mPlaneReflectors.last(); + + plane.refplane = pInterior->mReflectPlanes[i]; + plane.objectSpace = true; + plane.registerReflector( this, &mReflectorDesc ); + } + } + + } + else + mCRC = mInteriorRes.getChecksum(); + + // Ok, everything's groovy! Let's cache our hashed filename for renderimage sorting... + mInteriorFileHash = _StringTable::hashString(mInteriorFileName); + + // Setup bounding information + mObjBox = mInteriorRes->getDetailLevel(0)->getBoundingBox(); + resetWorldBox(); + setRenderTransform(mObjToWorld); + + + // Do any handle loading, etc. required. + + if (isClientObject()) { + + for (i = 0; i < mInteriorRes->getNumDetailLevels(); i++) { + Interior* pInterior = mInteriorRes->getDetailLevel(i); + + // Force the lightmap manager to download textures if we're + // running the mission editor. Normally they are only + // downloaded after the whole scene is lit. + gInteriorLMManager.addInstance(pInterior->getLMHandle(), mLMHandle, this); + if (gEditingMission) { + gInteriorLMManager.useBaseTextures(pInterior->getLMHandle(), mLMHandle); + gInteriorLMManager.downloadGLTextures(pInterior->getLMHandle()); + } + + // Install material list + // mMaterialMaps.push_back(new MaterialList(pInterior->mMaterialList)); + } + + } else { + + } + + setMaskBits(0xffffffff); + return true; +} + +//----------------------------------------------------------------------------- + +void InteriorInstance::_unloadInterior() +{ + mConvexList->nukeList(); + delete mConvexList; + mConvexList = new Convex; + + if(isClientObject()) + { + if(bool(mInteriorRes) && mLMHandle != 0xFFFFFFFF) + { + for(U32 i = 0; i < mInteriorRes->getNumDetailLevels(); i++) + { + Interior * pInterior = mInteriorRes->getDetailLevel(i); + if (pInterior->getLMHandle() != 0xFFFFFFFF) + gInteriorLMManager.removeInstance(pInterior->getLMHandle(), mLMHandle); + } + } + + if( mPlaneReflectors.size() ) + { + for ( U32 i = 0; i < mPlaneReflectors.size(); i++ ) + { + mPlaneReflectors[i].unregisterReflector(); + } + mPlaneReflectors.clear(); + } + } +} + +//----------------------------------------------------------------------------- + +bool InteriorInstance::onSceneAdd() +{ + AssertFatal(mInteriorRes, "Error, should not have been added to the scene if there's no interior!"); + + if (Parent::onSceneAdd() == false) + return false; + + U32 maxNumZones = 0; + + for (U32 i = 0; i < mInteriorRes->getNumDetailLevels(); i++) + { + if (mInteriorRes->getDetailLevel(i)->mZones.size() > maxNumZones) + maxNumZones = mInteriorRes->getDetailLevel(i)->mZones.size(); + } + + if( maxNumZones > 1 ) + { + SceneZoneSpaceManager* zoneManager = getSceneManager()->getZoneManager(); + if( zoneManager ) + { + zoneManager->registerZones(this, (maxNumZones - 1)); + + // Connect to outdoor zone. + zoneManager->getRootZone()->connectZoneSpace( this ); + connectZoneSpace( zoneManager->getRootZone() ); + } + } + + return true; +} + +//----------------------------------------------------------------------------- + +void InteriorInstance::onSceneRemove() +{ + // Disconnect from root zone in case we have connected. + + SceneZoneSpaceManager* zoneManager = getSceneManager()->getZoneManager(); + if( zoneManager ) + zoneManager->getRootZone()->disconnectZoneSpace( this ); + + Parent::onSceneRemove(); +} + +//----------------------------------------------------------------------------- + +bool InteriorInstance::_getOverlappingZones( const Box3F& aabb, const MatrixF& transform, const Point3F& scale, U32* outZones, U32& outNumZones ) +{ + MatrixF xForm(true); + Point3F invScale(1.0f / getScale().x, + 1.0f / getScale().y, + 1.0f / getScale().z); + xForm.scale(invScale); + xForm.mul(getWorldTransform()); + xForm.mul(transform); + xForm.scale(scale); + + U32 waterMark = FrameAllocator::getWaterMark(); + + U16* zoneVector = (U16*)FrameAllocator::alloc(mInteriorRes->getDetailLevel(0)->mZones.size() * sizeof(U16)); + U32 numRetZones = 0; + + bool outsideToo = mInteriorRes->getDetailLevel(0)->scanZones(aabb, + xForm, + zoneVector, + &numRetZones + ); + + if (numRetZones > SceneObject::MaxObjectZones) + { + Con::warnf(ConsoleLogEntry::General, "Too many zones returned for query on %s. Returning first %d", + mInteriorFileName, SceneObject::MaxObjectZones); + } + + for (U32 i = 0; i < getMin(numRetZones, U32(SceneObject::MaxObjectZones)); i++) + outZones[i] = zoneVector[i] + mZoneRangeStart - 1; + outNumZones = numRetZones; + + FrameAllocator::setWaterMark(waterMark); + + return outsideToo; +} + +//----------------------------------------------------------------------------- + +bool InteriorInstance::getOverlappingZones( const Box3F& aabb, U32* outZones, U32& outNumZones ) +{ + return _getOverlappingZones( aabb, MatrixF::Identity, Point3F( 1.f, 1.f, 1.f ), outZones, outNumZones ); +} + +//----------------------------------------------------------------------------- + +bool InteriorInstance::getOverlappingZones( SceneObject* obj, U32* outZones, U32& outNumZones ) +{ + return _getOverlappingZones( obj->getObjBox(), obj->getTransform(), obj->getScale(), outZones, outNumZones ); +} + +//----------------------------------------------------------------------------- + +U32 InteriorInstance::getPointZone(const Point3F& p) +{ + AssertFatal(mInteriorRes, "Error, no interior!"); + + Point3F osPoint = p; + mWorldToObj.mulP(osPoint); + osPoint.convolveInverse(mObjScale); + + S32 zone = mInteriorRes->getDetailLevel(0)->getZoneForPoint(osPoint); + + // If we're in solid (-1) or outside, we need to return 0 + if (zone == -1 || zone == 0) + return SceneZoneSpaceManager::InvalidZoneId; + + return (zone-1) + mZoneRangeStart; +} + +//----------------------------------------------------------------------------- + +// does a hack check to determine how much a point is 'inside'.. should have +// portals prebuilt with the transfer energy to each other portal in the zone +// from the neighboring zone.. these values can be used to determine the factor +// from within an individual zone.. also, each zone could be marked with +// average material property for eax environment audio +// ~0: outside -> 1: inside +bool InteriorInstance::getPointInsideScale(const Point3F & pos, F32 * pScale) +{ + AssertFatal(mInteriorRes, "InteriorInstance::getPointInsideScale: no interior"); + + Interior * interior = mInteriorRes->getDetailLevel(0); + + Point3F p = pos; + mWorldToObj.mulP(p); + p.convolveInverse(mObjScale); + + U32 zoneIndex = interior->getZoneForPoint(p); + if(zoneIndex == -1) // solid? + { + *pScale = 1.f; + return(true); + } + else if(zoneIndex == 0) // outside? + { + *pScale = 0.f; + return(true); + } + + U32 waterMark = FrameAllocator::getWaterMark(); + const Interior::Portal** portals = (const Interior::Portal**)FrameAllocator::alloc(256 * sizeof(const Interior::Portal*)); + U32 numPortals = 0; + + Interior::Zone & zone = interior->mZones[zoneIndex]; + + U32 i; + for(i = 0; i < zone.portalCount; i++) + { + const Interior::Portal & portal = interior->mPortals[interior->mZonePortalList[zone.portalStart + i]]; + if(portal.zoneBack == 0 || portal.zoneFront == 0) { + AssertFatal(numPortals < 256, "Error, overflow in temporary portal buffer!"); + portals[numPortals++] = &portal; + } + } + + // inside? + if(numPortals == 0) + { + *pScale = 1.f; + + FrameAllocator::setWaterMark(waterMark); + return(true); + } + + Point3F* portalCenters = (Point3F*)FrameAllocator::alloc(numPortals * sizeof(Point3F)); + U32 numPortalCenters = 0; + + // scale using the distances to the portals in this zone... + for(i = 0; i < numPortals; i++) + { + const Interior::Portal * portal = portals[i]; + if(!portal->triFanCount) + continue; + + Point3F center(0, 0, 0); + for(U32 j = 0; j < portal->triFanCount; j++) + { + const Interior::TriFan & fan = interior->mWindingIndices[portal->triFanStart + j]; + U32 numPoints = fan.windingCount; + + if(!numPoints) + continue; + + for(U32 k = 0; k < numPoints; k++) + { + const Point3F & a = interior->mPoints[interior->mWindings[fan.windingStart + k]].point; + center += a; + } + + center /= (F32)numPoints; + portalCenters[numPortalCenters++] = center; + } + } + + // 'magic' check here... + F32 magic = Con::getFloatVariable("Interior::insideDistanceFalloff", 10.f); + + F32 val = 0.f; + for(i = 0; i < numPortalCenters; i++) + val += 1.f - mClampF(Point3F(portalCenters[i] - p).len() / magic, 0.f, 1.f); + + *pScale = 1.f - mClampF(val, 0.f, 1.f); + + FrameAllocator::setWaterMark(waterMark); + return(true); +} + +//----------------------------------------------------------------------------- + +void InteriorInstance::_renderObject( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* overrideMat ) +{ +#ifndef TORQUE_SHIPPING + if (Interior::smRenderMode == 0) + return; + + if (overrideMat) + return; + + if(gEditingMission && isHidden()) + return; + + U32 detailLevel = 0; + detailLevel = _calcDetailLevel(state, state->getCameraPosition()); + + Interior* pInterior = mInteriorRes->getDetailLevel( detailLevel ); + + if (!pInterior) + return; + + PROFILE_START( IRO_DebugRender ); + + GFX->pushWorldMatrix(); + + // setup world matrix - for fixed function + MatrixF world = GFX->getWorldMatrix(); + world.mul( getRenderTransform() ); + world.scale( getScale() ); + GFX->setWorldMatrix( world ); + + // setup world matrix - for shaders + MatrixF proj = GFX->getProjectionMatrix(); + proj.mul(world); + + SceneData sgData; + + sgData = pInterior->setupSceneGraphInfo( this, state ); + ZoneVisDeterminer zoneVis = pInterior->setupZoneVis( this, state ); + pInterior->debugRender( zoneVis, sgData, this, proj ); + + GFX->popWorldMatrix(); + + PROFILE_END(); +#endif +} + +//----------------------------------------------------------------------------- + +U32 InteriorInstance::_calcDetailLevel(SceneRenderState* state, const Point3F& wsPoint) +{ + AssertFatal(mInteriorRes, "Error, should not try to calculate the deatil level without a resource to work with!"); + AssertFatal(_getNumCurrZones() > 0, "Error, must belong to a zone for this to work"); + + if (smDetailModification < 0.3f) + smDetailModification = 0.3f; + if (smDetailModification > 1.0f) + smDetailModification = 1.0f; + + // Early out for simple interiors + if (mInteriorRes->getNumDetailLevels() == 1) + return 0; + + if((mForcedDetailLevel >= 0) && (mForcedDetailLevel < mInteriorRes->getNumDetailLevels())) + return(mForcedDetailLevel); + + Point3F osPoint = wsPoint; + mRenderWorldToObj.mulP(osPoint); + osPoint.convolveInverse(mObjScale); + + // First, see if the point is in the object space bounding box of the highest detail + // If it is, then the detail level is zero. + if (mObjBox.isContained(osPoint)) + return 0; + + // Otherwise, we're going to have to do some ugly trickery to get the projection. + // I've stolen the worldToScreenScale from dglMatrix, we'll have to calculate the + // projection of the bounding sphere of the lowest detail level. + // worldToScreenScale = (near * view.extent.x) / (right - left) + F32 worldToScreenScale = state->getWorldToScreenScale().x; + const SphereF& lowSphere = mInteriorRes->getDetailLevel(mInteriorRes->getNumDetailLevels() - 1)->mBoundingSphere; + F32 dist = (lowSphere.center - osPoint).len(); + F32 projRadius = (lowSphere.radius / dist) * worldToScreenScale; + + // Scale the projRadius based on the objects maximum scale axis + projRadius *= getMax(mFabs(mObjScale.x), getMax(mFabs(mObjScale.y), mFabs(mObjScale.z))); + + // Multiply based on detail preference... + projRadius *= smDetailModification; + + // Ok, now we have the projected radius, we need to search through the interiors to + // find the largest interior that will support this projection. + U32 final = mInteriorRes->getNumDetailLevels() - 1; + for (U32 i = 0; i< mInteriorRes->getNumDetailLevels() - 1; i++) { + Interior* pDetail = mInteriorRes->getDetailLevel(i); + + if (pDetail->mMinPixels < projRadius) { + final = i; + break; + } + } + + // Ok, that's it. + return final; +} + +//----------------------------------------------------------------------------- + +void InteriorInstance::traverseZones( SceneTraversalState* state ) +{ + U32 startZone = getPointZone( state->getCullingState()->getCameraState().getViewPosition() ); + if( startZone != SceneZoneSpaceManager::InvalidZoneId ) + startZone = startZone - mZoneRangeStart + 1; + else + startZone = SceneZoneSpaceManager::RootZoneId; + + traverseZones( state, startZone ); +} + +//----------------------------------------------------------------------------- + +void InteriorInstance::traverseZones( SceneTraversalState* state, U32 startZoneId ) +{ + PROFILE_SCOPE( InteriorInstance_traverseZones ); + + SceneCullingState* cullingState = state->getCullingState(); + + // [rene, 23-Mar-11] This is a really gross hack. It effectively renders all zoning in interiors + // ineffective and just lets them render with the root frustum. It's just that after untangling + // DMM's mess in the sceneGraph system, I just don't have the energy anymore to also dig through + // the ungodly mess that is the interior code and since this is all quasi-deprecated-and-soon-to-die + // anyway it would just be wasted effort. + + for( U32 i = getZoneRangeStart(); i < ( getZoneRangeStart() + getZoneRange() ); ++ i ) + cullingState->addCullingVolumeToZone( i, state->getCurrentCullingVolume() ); + +#if 0 + U32 baseZoneForPrep = getCurrZone( 0 ); + bool multipleZones = ( getNumCurrZones() > 1 ); + + Frustum outFrustum; + bool continueOut = mInteriorRes->getDetailLevel( 0 )->traverseZones( + renderState, + frustum, + baseZoneForPrep, + startZoneId, + mZoneRangeStart, + mRenderObjToWorld, + mObjScale, + smDontRestrictOutside | multipleZones, + renderState->isInvertedWorld(), + outFrustum + ); + + if( smDontRestrictOutside ) + continueOut = true; +#endif + + if( true )// continueOut ) + { + state->pushZone( startZoneId ); + _traverseConnectedZoneSpaces( state ); + state->popZone(); + } +} + +//----------------------------------------------------------------------------- + +void InteriorInstance::prepRenderImage( SceneRenderState* state ) +{ + PROFILE_SCOPE( InteriorInstance_prepRenderImage ); + + U32 detailLevel = _calcDetailLevel( state, state->getCameraPosition() ); + Interior* pInterior = getResource()->getDetailLevel( detailLevel ); + pInterior->prepBatchRender( this, state ); +} + +//----------------------------------------------------------------------------- + +bool InteriorInstance::castRay(const Point3F& s, const Point3F& e, RayInfo* info) +{ + info->object = this; + return mInteriorRes->getDetailLevel(0)->castRay(s, e, info); +} + +//----------------------------------------------------------------------------- + +bool InteriorInstance::buildPolyList(PolyListContext context, AbstractPolyList* list, const Box3F& wsBox, const SphereF&) +{ + if (bool(mInteriorRes) == false) + return false; + + // Setup collision state data + list->setTransform(&getTransform(), getScale()); + list->setObject(this); + + return mInteriorRes->getDetailLevel(0)->buildPolyList(list, wsBox, mWorldToObj, getScale()); +} + +//----------------------------------------------------------------------------- + +void InteriorInstance::buildConvex(const Box3F& box, Convex* convex) +{ + if (bool(mInteriorRes) == false) + return; + + mConvexList->collectGarbage(); + + Box3F realBox = box; + mWorldToObj.mul(realBox); + realBox.minExtents.convolveInverse(mObjScale); + realBox.maxExtents.convolveInverse(mObjScale); + + if (realBox.isOverlapped(getObjBox()) == false) + return; + + U32 waterMark = FrameAllocator::getWaterMark(); + + if ((convex->getObject()->getTypeMask() & VehicleObjectType) && + mInteriorRes->getDetailLevel(0)->mVehicleConvexHulls.size() > 0) + { + // Can never have more hulls than there are hulls in the interior... + U16* hulls = (U16*)FrameAllocator::alloc(mInteriorRes->getDetailLevel(0)->mVehicleConvexHulls.size() * sizeof(U16)); + U32 numHulls = 0; + + Interior* pInterior = mInteriorRes->getDetailLevel(0); + if (pInterior->getIntersectingVehicleHulls(realBox, hulls, &numHulls) == false) { + FrameAllocator::setWaterMark(waterMark); + return; + } + + for (U32 i = 0; i < numHulls; i++) { + // See if this hull exists in the working set already... + Convex* cc = 0; + CollisionWorkingList& wl = convex->getWorkingList(); + for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) { + if (itr->mConvex->getType() == InteriorConvexType && + (static_cast(itr->mConvex)->getObject() == this && + static_cast(itr->mConvex)->hullId == -S32(hulls[i] + 1))) { + cc = itr->mConvex; + break; + } + } + if (cc) + continue; + + // Create a new convex. + InteriorConvex* cp = new InteriorConvex; + mConvexList->registerObject(cp); + convex->addToWorkingList(cp); + cp->mObject = this; + cp->pInterior = pInterior; + cp->hullId = -S32(hulls[i] + 1); + cp->box.minExtents.x = pInterior->mVehicleConvexHulls[hulls[i]].minX; + cp->box.minExtents.y = pInterior->mVehicleConvexHulls[hulls[i]].minY; + cp->box.minExtents.z = pInterior->mVehicleConvexHulls[hulls[i]].minZ; + cp->box.maxExtents.x = pInterior->mVehicleConvexHulls[hulls[i]].maxX; + cp->box.maxExtents.y = pInterior->mVehicleConvexHulls[hulls[i]].maxY; + cp->box.maxExtents.z = pInterior->mVehicleConvexHulls[hulls[i]].maxZ; + } + } + else + { + // Can never have more hulls than there are hulls in the interior... + U16* hulls = (U16*)FrameAllocator::alloc(mInteriorRes->getDetailLevel(0)->mConvexHulls.size() * sizeof(U16)); + U32 numHulls = 0; + + Interior* pInterior = mInteriorRes->getDetailLevel(0); + if (pInterior->getIntersectingHulls(realBox, hulls, &numHulls) == false) { + FrameAllocator::setWaterMark(waterMark); + return; + } + + for (U32 i = 0; i < numHulls; i++) { + // See if this hull exists in the working set already... + Convex* cc = 0; + CollisionWorkingList& wl = convex->getWorkingList(); + for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) { + if (itr->mConvex->getType() == InteriorConvexType && + (static_cast(itr->mConvex)->getObject() == this && + static_cast(itr->mConvex)->hullId == hulls[i])) { + cc = itr->mConvex; + break; + } + } + if (cc) + continue; + + // Create a new convex. + InteriorConvex* cp = new InteriorConvex; + mConvexList->registerObject(cp); + convex->addToWorkingList(cp); + cp->mObject = this; + cp->pInterior = pInterior; + cp->hullId = hulls[i]; + cp->box.minExtents.x = pInterior->mConvexHulls[hulls[i]].minX; + cp->box.minExtents.y = pInterior->mConvexHulls[hulls[i]].minY; + cp->box.minExtents.z = pInterior->mConvexHulls[hulls[i]].minZ; + cp->box.maxExtents.x = pInterior->mConvexHulls[hulls[i]].maxX; + cp->box.maxExtents.y = pInterior->mConvexHulls[hulls[i]].maxY; + cp->box.maxExtents.z = pInterior->mConvexHulls[hulls[i]].maxZ; + } + } + FrameAllocator::setWaterMark(waterMark); +} + +//----------------------------------------------------------------------------- + +U32 InteriorInstance::packUpdate(NetConnection* c, U32 mask, BitStream* stream) +{ + U32 retMask = Parent::packUpdate(c, mask, stream); + + if (stream->writeFlag((mask & InitMask) != 0)) { + // Initial update, write the whole kit and kaboodle + stream->write(mCRC); + + stream->writeString(mInteriorFileName); + + // Write the alarm state + stream->writeFlag(mAlarmState); + } + else + { + stream->writeFlag(mAlarmState); + } + + return retMask; +} + +//----------------------------------------------------------------------------- + +void InteriorInstance::unpackUpdate(NetConnection* c, BitStream* stream) +{ + Parent::unpackUpdate(c, stream); + + MatrixF temp; + Point3F tempScale; + + if (stream->readFlag()) { + bool isNewUpdate(mInteriorRes); + + if(isNewUpdate) + _unloadInterior(); + + // Initial Update + // CRC + stream->read(&mCRC); + + // File + mInteriorFileName = stream->readSTString(); + + // Alarm state: Note that we handle this ourselves on the initial update + // so that the state is always full on or full off... + mAlarmState = stream->readFlag(); + + if(isNewUpdate) + { + if(! _loadInterior()) + Con::errorf("InteriorInstance::unpackUpdate - Unable to load new interior"); + } + } + else + { + setAlarmMode(stream->readFlag()); + } +} + +//----------------------------------------------------------------------------- + +Interior* InteriorInstance::getDetailLevel(const U32 level) +{ + return mInteriorRes->getDetailLevel(level); +} + +//----------------------------------------------------------------------------- + +U32 InteriorInstance::getNumDetailLevels() +{ + return mInteriorRes->getNumDetailLevels(); +} + +//----------------------------------------------------------------------------- + +void InteriorInstance::setAlarmMode(const bool alarm) +{ + if (mInteriorRes->getDetailLevel(0)->mHasAlarmState == false) + return; + + if (mAlarmState == alarm) + return; + + mAlarmState = alarm; + if (isServerObject()) + { + setMaskBits(AlarmMask); + } + else + { + // DMMTODO: Invalidate current light state + } +} + +//----------------------------------------------------------------------------- + +void InteriorInstance::_createTriggerTransform(const InteriorResTrigger* trigger, MatrixF* transform) +{ + Point3F offset; + MatrixF xform = getTransform(); + xform.getColumn(3, &offset); + + Point3F triggerOffset = trigger->mOffset; + triggerOffset.convolve(mObjScale); + getTransform().mulV(triggerOffset); + offset += triggerOffset; + xform.setColumn(3, offset); + + *transform = xform; +} + +//----------------------------------------------------------------------------- + +bool InteriorInstance::readLightmaps(GBitmap**** lightmaps) +{ + AssertFatal(mInteriorRes, "Error, no interior loaded!"); + AssertFatal(lightmaps, "Error, no lightmaps or numdetails result pointers"); + AssertFatal(*lightmaps == NULL, "Error, already have a pointer in the lightmaps result field!"); + + // Load resource + FileStream* pStream; + if((pStream = FileStream::createAndOpen( mInteriorFileName, Torque::FS::File::Read )) == NULL) + { + Con::errorf(ConsoleLogEntry::General, "Unable to load interior: %s", mInteriorFileName); + return false; + } + + InteriorResource* pResource = new InteriorResource; + bool success = pResource->read(*pStream); + delete pStream; + + if (success == false) + { + delete pResource; + return false; + } + AssertFatal(pResource->getNumDetailLevels() == mInteriorRes->getNumDetailLevels(), + "Mismatched detail levels!"); + + *lightmaps = new GBitmap**[mInteriorRes->getNumDetailLevels()]; + + for (U32 i = 0; i < pResource->getNumDetailLevels(); i++) + { + Interior* pInterior = pResource->getDetailLevel(i); + (*lightmaps)[i] = new GBitmap*[pInterior->mLightmaps.size()]; + for (U32 j = 0; j < pInterior->mLightmaps.size(); j++) + { + ((*lightmaps)[i])[j] = pInterior->mLightmaps[j]; + pInterior->mLightmaps[j] = NULL; + } + pInterior->mLightmaps.clear(); + } + + delete pResource; + return true; +} + +//----------------------------------------------------------------------------- + +S32 InteriorInstance::getSurfaceZone(U32 surfaceindex, Interior *detail) +{ + AssertFatal(((surfaceindex >= 0) && (surfaceindex < detail->surfaceZones.size())), "Bad surface index!"); + S32 zone = detail->surfaceZones[surfaceindex]; + if(zone > -1) + return zone + mZoneRangeStart; + return _getCurrZone(0); +} + +//----------------------------------------------------------------------------- + +bool InteriorInstance::_setInteriorFile( void *object, const char *, const char *data ) +{ + if(data == NULL) + return true; + + InteriorInstance *inst = static_cast(object); + + if(inst->isProperlyAdded()) + inst->_unloadInterior(); + + inst->mInteriorFileName = StringTable->insert(data); + + if(inst->isProperlyAdded()) + { + if(! inst->_loadInterior()) + Con::errorf("InteriorInstance::setInteriorFile - Unable to load new interior"); + } + + return false; +} + +//============================================================================= +// Console API. +//============================================================================= +// MARK: ---- Console API ---- + +ConsoleFunctionGroupBegin(Interiors, ""); + +//----------------------------------------------------------------------------- + +#ifndef TORQUE_SHIPPING + +DefineEngineFunction( setInteriorRenderMode, void, ( S32 mode ),, + "Globally changes how InteriorInstances are rendered. Useful for debugging geometry and rendering artifacts\n" + + "@note This does not work in shipping mode\n\n" + + "@param mode The render mode can be one of the following numbers:\n\n" + "NormalRender = 0,\n\n" + "NormalRenderLines = 1,\n\n" + "ShowDetail = 2,\n\n" + "ShowAmbiguous = 3,\n\n" + "ShowOrphan = 4,\n\n" + "ShowLightmaps = 5,\n\n" + "ShowTexturesOnly = 6,\n\n" + "ShowPortalZones = 7,\n\n" + "ShowOutsideVisible = 8,\n\n" + "ShowCollisionFans = 9,\n\n" + "ShowStrips = 10,\n\n" + "ShowNullSurfaces = 11,\n\n" + "ShowLargeTextures = 12,\n\n" + "ShowHullSurfaces = 13,\n\n" + "ShowVehicleHullSurfaces = 14,\n\n" + "ShowVertexColors = 15,\n\n" + "ShowDetailLevel = 16\n\n" + + "@ingroup Game" ) +{ + if (mode < 0 || mode > Interior::ShowDetailLevel) + mode = 0; + + Interior::smRenderMode = mode; +} + +//----------------------------------------------------------------------------- + +ConsoleFunction( setInteriorFocusedDebug, void, 2, 2, "(bool enable)" + "@brief No longer properly supported\n\n" + "@internal") +{ + if (dAtob(argv[1])) { + Interior::smFocusedDebug = true; + } else { + Interior::smFocusedDebug = false; + } +} + +#endif + +//----------------------------------------------------------------------------- + +ConsoleDocFragment _isPointInside1( + "@brief Check to see if a point in world space is inside of an interior.\n\n" + + "@param position The position to check in world space.\n\n" + + "@tsexample\n" + "// Check to see if a point is inside any interior\n" + "%point = \"100 100 100\";\n" + "%isInside = isPointInside(%point);\n" + "@endtsexample\n\n" + + "@ingroup Game", + NULL, + "bool isPointInside( Point3F position );" +); +ConsoleDocFragment _isPointInside2( + "Check to see if a set of coordinates in world space are inside of an interior.\n\n" + "@param x X-coordinate for position in world space.\n" + "@param y Y-coordinate for position in world space.\n" + "@param z Z-coordinate for position in world space.\n" + "@tsexample\n\n" + "// Check to see if a point is inside any interior\n" + "%isInside = isPointInside(100, 100, 100);\n" + "@endtsexample\n\n" + "@ingroup Game", + NULL, + "bool isPointInside( F32 x, F32 y, F32 z );" +); + +ConsoleFunction( isPointInside, bool, 2, 4, "Check to see if a point in world space is inside of an interior." + "@hide") +{ + static bool lastValue = false; + + if(!(argc == 2 || argc == 4)) + { + Con::errorf(ConsoleLogEntry::General, "cIsPointInside: invalid parameters"); + return(lastValue); + } + + Point3F pos; + if(argc == 2) + dSscanf(argv[1], "%g %g %g", &pos.x, &pos.y, &pos.z); + else + { + pos.x = dAtof(argv[1]); + pos.y = dAtof(argv[2]); + pos.z = dAtof(argv[3]); + } + + RayInfo collision; + if(gClientContainer.castRay(pos, Point3F(pos.x, pos.y, pos.z - 2000.f), InteriorObjectType, &collision)) + { + if(collision.face == -1) + Con::errorf(ConsoleLogEntry::General, "cIsPointInside: failed to find hit face on interior"); + else + { + InteriorInstance * interior = dynamic_cast(collision.object); + if(interior) + lastValue = !interior->getDetailLevel(0)->isSurfaceOutsideVisible(collision.face); + else + Con::errorf(ConsoleLogEntry::General, "cIsPointInside: invalid interior on collision"); + } + } + + return(lastValue); +} + + +ConsoleFunctionGroupEnd(Interiors); + +//----------------------------------------------------------------------------- + +#ifdef TORQUE_COLLADA + +DefineEngineMethod( InteriorInstance, exportToCollada, void, ( bool bakeTransform ),, + "@brief Exports the Interior to a Collada file\n\n" + + "@param bakeTransform Bakes the InteriorInstance's transform into the vertex positions\n\n" + + "@tsexample\n" + "// Export to COLLADA, do not bakeTransform\n" + "%interiorObject.exportToCollada(0);\n" + "@endtsexample\n\n") +{ + object->exportToCollada(bakeTransform); +} +#endif + +//----------------------------------------------------------------------------- + +DefineEngineMethod( InteriorInstance, setAlarmMode, void, ( const char* alarmMode),, + "@brief This sets the alarm mode of the interior\n\n" + + "The alarm mode is used when debugging bad geometry for an interior. When on, the the bad verties " + "will be rendered a different color.\n\n" + + "@param alarmMode If true the interior will be in an alarm state next frame. Options are \'On\' or \'Off\'.\n\n" + + "@tsexample\n" + "// Turn on alarm mode debugging for interior\n" + "%interiorObject.setAlarmMode(\"On\");\n" + "@endtsexample\n\n") +{ + AssertFatal(dynamic_cast(object) != NULL, + "Error, how did a non-interior get here?"); + + bool alarm; + if (dStricmp(alarmMode, "On") == 0) + alarm = true; + else + alarm = false; + + InteriorInstance* interior = static_cast(object); + if (interior->isClientObject()) { + Con::errorf(ConsoleLogEntry::General, "InteriorInstance: client objects may not receive console commands. Ignored"); + return; + } + + interior->setAlarmMode(alarm); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( InteriorInstance, getNumDetailLevels, S32, (),, + "@brief Get the number of detail levels interior was created with\n\n" + + "@tsexample\n" + "%numLODs = %interiorObject.getNumDetailLevels();\n" + "echo(%numLODs);\n" + "@endtsexample\n\n") +{ + InteriorInstance * instance = static_cast(object); + return(instance->getNumDetailLevels()); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( InteriorInstance, setDetailLevel, void, (S32 level),, + "@brief Manually changes the current detail level, rather than automatically via view distance\n\n" + + "@param level Detail level to force.\n\n" + + "@tsexample\n" + "%interiorObject.setDetailLevel(2);\n" + "@endtsexample\n\n") +{ + InteriorInstance * instance = static_cast(object); + if(instance->isServerObject()) + { + NetConnection * toServer = NetConnection::getConnectionToServer(); + NetConnection * toClient = NetConnection::getLocalClientConnection(); + if(!toClient || !toServer) + return; + + S32 index = toClient->getGhostIndex(instance); + if(index == -1) + return; + + InteriorInstance * clientInstance = dynamic_cast(toServer->resolveGhost(index)); + if(clientInstance) + clientInstance->setDetailLevel(level); + } + else + instance->setDetailLevel(level); +} + +//----------------------------------------------------------------------------- + +//These functions are duplicated in tsStatic, shapeBase, and interiorInstance. +//They each function a little differently; but achieve the same purpose of gathering +//target names/counts without polluting simObject. + +DefineEngineMethod( InteriorInstance, getTargetName, const char*, (S32 detailLevel, S32 targetNum),, + "@brief Get the name of the indexed shape material\n\n" + + "@param detailLevel Target LOD\n" + "@param targetNum Index mapped to the target\n\n" + + "@return The name of the target (material) at the specified detail level and index\n\n" + + "@tsexample\n" + "// First level of detail, top of the index map\n" + "%targetName = %interiorObject.getTargetName(1, 0);\n" + "echo(%targetName);\n" + "@endtsexample\n\n") +{ + Interior* obj = object->getDetailLevel(detailLevel); + + if(obj) + return obj->getTargetName(targetNum); + + return ""; +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( InteriorInstance, getTargetCount, S32, (U32 detailLevel),, + "@brief Get the number of materials used by interior\n\n" + + "@param detailLevel Interior level of detail to scan\n" + + "@return The number of materials used by the interior at a specified detail level\n\n" + + "@tsexample\n" + "// Find materials used at first level of detail\n" + "%targetCount = %interiorObject.getTargetCount(1);\n" + "echo(%targetCount);\n" + "@endtsexample\n\n") +{ + Interior* obj = object->getDetailLevel(detailLevel); + if(obj) + return obj->getTargetCount(); + + return -1; +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( InteriorInstance, changeMaterial, void, (const char* mapTo, Material* oldMat, Material* newMat),, + "@brief Change one of the materials on the shape.\n\n" + + "This method changes materials per mapTo with others. The material that " + "is being replaced is mapped to unmapped_mat as a part of this transition.\n\n" + + "@note Warning, right now this only sort of works. It doesn't do a live " + "update like it should.\n\b" + + "@param mapTo The name of the material target to remap (from getTargetName)\n" + "@param oldMat The old Material that was mapped \n" + "@param newMat The new Material to map\n\n" + + "@tsexample\n" + "// remap the first material in the shape\n" + "%mapTo = %interiorObject.getTargetName( 0 );\n" + "%interiorObject.changeMaterial( %mapTo, 0, MyMaterial );\n" + "@endtsexample\n" ) +{ + // if no valid new material, theres no reason for doing this + if( !newMat ) + { + Con::errorf("InteriorInstance::changeMaterial failed: New material does not exist!"); + return; + } + + // simple parsing through the interiors detail levels looking for the correct mapto. + // break when we find the correct detail level to depend on. + U32 level; + S32 matIndex = -1; + for( level = 0; level < object->getNumDetailLevels(); level++ ) + { + matIndex = object->getDetailLevel(level)->mMaterialList->getMaterialNameList().find_next(String(mapTo)); + if (matIndex >= 0) + break; + } + + if (matIndex == -1) + { + Con::errorf("InteriorInstance::changeMaterial failed: Invalid mapTo name '%s'", mapTo); + return; + } + + // initilize server/client versions + Interior *serverObj = object->getDetailLevel( level ); + + InteriorInstance * instanceClientObj = dynamic_cast< InteriorInstance* > ( object->getClientObject() ); + Interior *clientObj = instanceClientObj ? instanceClientObj->getDetailLevel( level ) : NULL; + + if(serverObj) + { + // Lets remap the old material off, so as to let room for our current material room to claim its spot + if( oldMat ) + oldMat->mMapTo = String("unmapped_mat"); + + newMat->mMapTo = mapTo; + + // Map the material in the in the matmgr + MATMGR->mapMaterial( mapTo, newMat->mMapTo ); + + // Replace instances with the new material being traded in. Lets make sure that we only + // target the specific targets per inst. This technically is only done here for interiors for + // safe keeping. The remapping that truly matters most (for on the fly changes) are done in the node lists + delete serverObj->mMaterialList->mMatInstList[matIndex]; + serverObj->mMaterialList->mMatInstList[matIndex] = newMat->createMatInstance(); + + // Finishing the safekeeping + const GFXVertexFormat *flags = getGFXVertexFormat(); + FeatureSet features = MATMGR->getDefaultFeatures(); + serverObj->mMaterialList->getMaterialInst(matIndex)->init( features, flags ); + + if (clientObj) + { + delete clientObj->mMaterialList->mMatInstList[matIndex]; + clientObj->mMaterialList->mMatInstList[matIndex] = newMat->createMatInstance(); + clientObj->mMaterialList->getMaterialInst(matIndex)->init( features, flags ); + + // These loops are referenced in interior.cpp's initMatInstances + // Made a couple of alterations to tailor specifically towards one changing one instance + for( U32 i=0; igetNumZones(); i++ ) + { + for( U32 j=0; jmZoneRNList[i].renderNodeList.size(); j++ ) + { + BaseMatInstance *matInst = clientObj->mZoneRNList[i].renderNodeList[j].matInst; + Material* refMat = dynamic_cast(matInst->getMaterial()); + + if(refMat == oldMat) + { + clientObj->mZoneRNList[i].renderNodeList[j].matInst = newMat->createMatInstance(); + clientObj->mZoneRNList[i].renderNodeList[j].matInst->init(MATMGR->getDefaultFeatures(), getGFXVertexFormat()); + + //if ( pMat ) + //mHasTranslucentMaterials |= pMat->mTranslucent && !pMat->mTranslucentZWrite; + } + } + } + + // Lets reset the clientObj settings in order to accomodate the new material + clientObj->fillSurfaceTexMats(); + clientObj->createZoneVBs(); + clientObj->cloneMatInstances(); + clientObj->createReflectPlanes(); + clientObj->initMatInstances(); + } + } +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( InteriorInstance, getModelFile, const char*, (),, + "@brief Get the interior file name\n\n" + + + "@return The name of the interior's model file in .DIF.\n\n" + + "@tsexample\n" + "%interiorObject.getModelFile();\n" + "@endtsexample\n\n") +{ + return object->getInteriorFileName(); +} diff --git a/Engine/source/interior/interiorInstance.h b/Engine/source/interior/interiorInstance.h new file mode 100644 index 000000000..69f0d27c3 --- /dev/null +++ b/Engine/source/interior/interiorInstance.h @@ -0,0 +1,254 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _INTERIORINSTANCE_H_ +#define _INTERIORINSTANCE_H_ + +#ifndef _SCENEZONESPACE_H_ +#include "scene/zones/sceneZoneSpace.h" +#endif + +#ifndef _INTERIORRES_H_ +#include "interior/interiorRes.h" +#endif + +#ifndef _INTERIORLMMANAGER_H_ +#include "interior/interiorLMManager.h" +#endif + +#ifndef _BITVECTOR_H_ +#include "core/bitVector.h" +#endif + +#ifndef _COLOR_H_ +#include "core/color.h" +#endif + +#ifndef _INTERIOR_H_ +#include "interior.h" +#endif + +#ifndef _REFLECTOR_H_ +#include "scene/reflector.h" +#endif + +#ifndef __RESOURCE_H__ +#include "core/resource.h" +#endif + + +class AbstractPolyList; +class InteriorSubObject; +class InteriorResTrigger; +class MaterialList; +class TextureObject; +class Convex; +class SFXProfile; +class SFXEnvironment; +class PhysicsBody; + + +/// Instance of a DIF interior. +class InteriorInstance : public SceneZoneSpace +{ + public: + + friend class Interior; + + typedef SceneZoneSpace Parent; + + static bool smDontRestrictOutside; + static F32 smDetailModification; + + protected: + + enum UpdateMaskBits + { + InitMask = Parent::NextFreeMask << 0, + AlarmMask = Parent::NextFreeMask << 1, + + // Reserved for light updates (8 bits for now) + _lightupdate0 = Parent::NextFreeMask << 2, + _lightupdate1 = Parent::NextFreeMask << 3, + _lightupdate2 = Parent::NextFreeMask << 4, + _lightupdate3 = Parent::NextFreeMask << 5, + _lightupdate4 = Parent::NextFreeMask << 6, + _lightupdate5 = Parent::NextFreeMask << 7, + _lightupdate6 = Parent::NextFreeMask << 8, + _lightupdate7 = Parent::NextFreeMask << 9, + + SkinBaseMask = Parent::NextFreeMask << 10, + NextFreeMask = Parent::NextFreeMask << 11, + }; + + enum + { + LightUpdateBitStart = 3, + LightUpdateBitEnd = 10 + }; + + enum AlarmState { + Normal = 0, + Alarm = 1 + }; + + /// Alarm state of the interior + bool mAlarmState; + + /// File name of the interior this instance encapuslates + StringTableEntry mInteriorFileName; + + /// Hash for interior file name, used for sorting + U32 mInteriorFileHash; + + /// Interior managed by resource manager + Resource mInteriorRes; + + /// Forced LOD, if -1 auto LOD + S32 mForcedDetailLevel; + + /// CRC for the interior + U32 mCRC; + + /// Handle to the light manager + LM_HANDLE mLMHandle; + + Convex* mConvexList; + + PhysicsBody* mPhysicsRep; + + Vector< PlaneReflector > mPlaneReflectors; + ReflectorDesc mReflectorDesc; + + U32 _calcDetailLevel( SceneRenderState* state, const Point3F& wsPoint ); + bool _loadInterior(); + void _unloadInterior(); + void _renderObject( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* ); + bool _getOverlappingZones( const Box3F& aabb, const MatrixF& transform, const Point3F& scale, U32* outZones, U32& outNumZones ); + + /// Creates a transform based on an trigger area + /// @param trigger Trigger to create a transform for + /// @param transform Transform generated (out) + void _createTriggerTransform(const InteriorResTrigger *trigger, MatrixF *transform); + + // SceneObject. + virtual bool onSceneAdd(); + virtual void onSceneRemove(); + + public: + + InteriorInstance(); + virtual ~InteriorInstance(); + + StringTableEntry getInteriorFileName() { return mInteriorFileName; } + + S32 getSurfaceZone(U32 surfaceindex, Interior *detail); + + /// Exports the interior to a Collada file + /// @param bakeTransform Bakes the InteriorInstance's transform into the vertex positions + void exportToCollada(bool bakeTransform = false); + + /// Returns the Light Manager handle + LM_HANDLE getLMHandle() { return(mLMHandle); } + + /// Reads the lightmaps of the interior into the provided pointer + /// @param lightmaps Lightmaps in the interior (out) + bool readLightmaps(GBitmap ****lightmaps); + + /// This is used to determine just how 'inside' a point is in an interior. + /// This is used by the environmental audio code for audio properties and the + /// function always returns true. + /// @param pos Point to test + /// @param pScale How inside is the point 0 = totally outside, 1 = totally inside (out) + bool getPointInsideScale(const Point3F & pos, F32 * pScale); // ~0: outside -> 1: inside + + /// Returns the interior resource + Resource & getResource() {return(mInteriorRes);} // SceneLighting::InteriorProxy interface + + /// Returns the CRC for validation + U32 getCRC() { return(mCRC); } + + /// @name Alarm States + /// @{ + + /// This returns true if the interior is in an alarm state. Alarm state + /// will put different lighting into the interior and also possibly + /// have an audio element also. + bool inAlarmState() {return(mAlarmState);} + + /// This sets the alarm mode of the interior. + /// @param alarm If true the interior will be in an alarm state next frame + void setAlarmMode(const bool alarm); + + /// @} + + /// @name Subobject access interface + /// @{ + + /// Returns the number of detail levels for an object + U32 getNumDetailLevels(); + + /// Gets the interior associated with a particular detail level + /// @param level Detail level + Interior* getDetailLevel(const U32 level); + + /// Sets the detail level to render manually + /// @param level Detail level to force + void setDetailLevel(S32 level = -1) { mForcedDetailLevel = level; } + + /// @} + + // SimObject. + DECLARE_CONOBJECT( InteriorInstance ); + + virtual bool onAdd(); + virtual void onRemove(); + virtual void inspectPostApply(); + + static void initPersistFields(); + static void consoleInit(); + + // NetObject. + virtual U32 packUpdate( NetConnection* conn, U32 mask, BitStream* stream ); + virtual void unpackUpdate( NetConnection* conn, BitStream* stream ); + + // SceneObject. + virtual bool buildPolyList(PolyListContext context, AbstractPolyList *polyList, const Box3F &box, const SphereF &sphere); + virtual bool castRay(const Point3F &start, const Point3F &end, RayInfo *info); + virtual void buildConvex(const Box3F& box,Convex* convex); + virtual void prepRenderImage( SceneRenderState *state ); + + // SceneZoneSpace. + virtual void traverseZones( SceneTraversalState* state ); + virtual void traverseZones( SceneTraversalState* state, U32 startZoneId ); + virtual U32 getPointZone( const Point3F& p ); + virtual bool getOverlappingZones( const Box3F& aabb, U32* outZones, U32& outNumZones ); + virtual bool getOverlappingZones( SceneObject* obj, U32* outZones, U32& outNumZones ); + + private: + + // Protected field accessors + static bool _setInteriorFile( void *object, const char *, const char *data ); +}; + +#endif //_INTERIORBLOCK_H_ + diff --git a/Engine/source/interior/interiorLMManager.cpp b/Engine/source/interior/interiorLMManager.cpp new file mode 100644 index 000000000..93f465756 --- /dev/null +++ b/Engine/source/interior/interiorLMManager.cpp @@ -0,0 +1,384 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "interior/interiorLMManager.h" +#include "gfx/gfxTextureManager.h" +#include "gfx/bitmap/gBitmap.h" +#include "interior/interiorRes.h" +#include "interior/interiorInstance.h" +#include "interior/interior.h" + +//------------------------------------------------------------------------------ +// Globals +InteriorLMManager gInteriorLMManager; + +//------------------------------------------------------------------------------ + +InteriorLMManager::InteriorLMManager() +{ + VECTOR_SET_ASSOCIATION( mInteriors ); +} + +InteriorLMManager::~InteriorLMManager() +{ + for(U32 i = 0; i < mInteriors.size(); i++) + removeInterior(LM_HANDLE(i)); +} + +//------------------------------------------------------------------------------ +void InteriorLMManager::addInterior(LM_HANDLE & interiorHandle, U32 numLightmaps, Interior * interior) +{ + interiorHandle = mInteriors.size(); + mInteriors.increment(); + mInteriors.last() = new InteriorLMInfo; + + mInteriors.last()->mInterior = interior; + mInteriors.last()->mHandlePtr = &interiorHandle; + mInteriors.last()->mNumLightmaps = numLightmaps; + + // create base instance + addInstance(interiorHandle, mInteriors.last()->mBaseInstanceHandle, 0); + AssertFatal(mInteriors.last()->mBaseInstanceHandle == LM_HANDLE(0), "InteriorLMManager::addInterior: invalid base instance handle"); + + // steal the lightmaps from the interior + Vector& texHandles = getHandles(interiorHandle, 0); + for(U32 i = 0; i < interior->mLightmaps.size(); i++) + { + AssertFatal(interior->mLightmaps[i], "InteriorLMManager::addInterior: interior missing lightmap"); + texHandles[i].set(interior->mLightmaps[i], &GFXDefaultPersistentProfile, true, String("Interior Lightmap")); + } + + interior->mLightmaps.clear(); +} + +void InteriorLMManager::removeInterior(LM_HANDLE interiorHandle) +{ + AssertFatal(interiorHandle < mInteriors.size(), "InteriorLMManager::removeInterior: invalid interior handle"); + AssertFatal(mInteriors[interiorHandle]->mInstances.size() == 1, "InteriorLMManager::removeInterior: cannot remove base interior"); + + // remove base instance + removeInstance(interiorHandle, 0); + + *mInteriors[interiorHandle]->mHandlePtr = LM_HANDLE(-1); + + delete mInteriors[interiorHandle]; + + // last one? otherwise move it + if((mInteriors.size()-1) != interiorHandle) + { + mInteriors[interiorHandle] = mInteriors.last(); + *(mInteriors[interiorHandle]->mHandlePtr) = interiorHandle; + } + + mInteriors.decrement(); +} + +//------------------------------------------------------------------------------ +void InteriorLMManager::addInstance(LM_HANDLE interiorHandle, LM_HANDLE & instanceHandle, InteriorInstance * instance) +{ + AssertFatal(interiorHandle < mInteriors.size(), "InteriorLMManager::addInstance: invalid interior handle"); + AssertFatal(interiorHandle == *(mInteriors[interiorHandle]->mHandlePtr), "InteriorLMManager::addInstance: invalid handle value"); + + InteriorLMInfo * interiorInfo = mInteriors[interiorHandle]; + + // create the instance info and fill + InstanceLMInfo * instanceInfo = new InstanceLMInfo; + + instanceInfo->mInstance = instance; + instanceInfo->mHandlePtr = &instanceHandle; + instanceHandle = interiorInfo->mInstances.size(); + + interiorInfo->mInstances.push_back(instanceInfo); + + // create/clear list + instanceInfo->mLightmapHandles.setSize(interiorInfo->mNumLightmaps); + + for(U32 i = 0; i < instanceInfo->mLightmapHandles.size(); i++) + instanceInfo->mLightmapHandles[i] = NULL; +} + +void InteriorLMManager::removeInstance(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle) +{ + AssertFatal(interiorHandle < mInteriors.size(), "InteriorLMManager::removeInstance: invalid interior handle"); + AssertFatal(instanceHandle < mInteriors[interiorHandle]->mInstances.size(), "InteriorLMManager::removeInstance: invalid instance handle"); + AssertFatal(!(instanceHandle == mInteriors[interiorHandle]->mBaseInstanceHandle && + mInteriors[interiorHandle]->mInstances.size() > 1), "InteriorLMManager::removeInstance: invalid base instance"); + + InteriorLMInfo * itrInfo = mInteriors[interiorHandle]; + + // kill it + InstanceLMInfo * instInfo = itrInfo->mInstances[instanceHandle]; + for(U32 i = 0; i < instInfo->mLightmapHandles.size(); i++) + instInfo->mLightmapHandles[i] = NULL; + + // reset on last instance removal only (multi detailed shapes share the same instance handle) + if(itrInfo->mInstances.size() == 1) + *instInfo->mHandlePtr = LM_HANDLE(-1); + + delete instInfo; + + // last one? otherwise move it + if((itrInfo->mInstances.size()-1) != instanceHandle) + { + itrInfo->mInstances[instanceHandle] = itrInfo->mInstances.last(); + *(itrInfo->mInstances[instanceHandle]->mHandlePtr) = instanceHandle; + } + + itrInfo->mInstances.decrement(); +} + +void InteriorLMManager::useBaseTextures(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle) +{ + AssertFatal(interiorHandle < mInteriors.size(), "InteriorLMManager::useBaseTextures: invalid interior handle"); + AssertFatal(interiorHandle == *(mInteriors[interiorHandle]->mHandlePtr), "InteriorLMManager::useBaseTextures: invalid handle value"); + + // Make sure the base light maps are loaded + loadBaseLightmaps(interiorHandle,instanceHandle); + + // Install base lightmaps for this instance... + Vector& baseHandles = getHandles(interiorHandle, 0); + Vector& texHandles = getHandles(interiorHandle, instanceHandle); + for(U32 i = 0; i < baseHandles.size(); i++) + texHandles[i] = baseHandles[i]; +} + +//------------------------------------------------------------------------------ +void InteriorLMManager::destroyBitmaps() +{ + for(S32 i = mInteriors.size() - 1; i >= 0; i--) + { + InteriorLMInfo * interiorInfo = mInteriors[i]; + for(S32 j = interiorInfo->mInstances.size() - 1; j >= 0; j--) + { + InstanceLMInfo * instanceInfo = interiorInfo->mInstances[j]; + for(S32 k = instanceInfo->mLightmapHandles.size() - 1; k >= 0; k--) + { + if(!instanceInfo->mLightmapHandles[k]) + continue; + + GFXTextureObject * texObj = instanceInfo->mLightmapHandles[k]; + if(!texObj || !texObj->mBitmap) + continue; + + // don't remove 'keep' bitmaps + if(!interiorInfo->mInterior->mLightmapKeep[k]) + { +// SAFE_DELETE(texObj->mBitmap); +// texObj->bitmap = 0; + } + } + } + } +} + +void InteriorLMManager::destroyTextures() +{ + for(S32 i = mInteriors.size() - 1; i >= 0; i--) + { + InteriorLMInfo * interiorInfo = mInteriors[i]; + for(S32 j = interiorInfo->mInstances.size() - 1; j >= 0; j--) + { + InstanceLMInfo * instanceInfo = interiorInfo->mInstances[j]; + for(S32 k = interiorInfo->mNumLightmaps - 1; k >= 0; k--) + { + // will want to remove the vector here eventually... so dont clear + instanceInfo->mLightmapHandles[k] = NULL; + } + } + } +} + +void InteriorLMManager::downloadGLTextures() +{ + for(S32 i = mInteriors.size() - 1; i >= 0; i--) + downloadGLTextures(i); +} + +void InteriorLMManager::downloadGLTextures(LM_HANDLE interiorHandle) +{ + + AssertFatal(interiorHandle < mInteriors.size(), "InteriorLMManager::downloadGLTextures: invalid interior handle"); + InteriorLMInfo * interiorInfo = mInteriors[interiorHandle]; + + // The bit vector is used to keep track of which lightmap sets need + // to be loaded from the shared "base" instance. Every instance + // can have it's own lightmap set due to mission lighting. + BitVector needTexture; + needTexture.setSize(interiorInfo->mNumLightmaps); + needTexture.clear(); + + for(S32 j = interiorInfo->mInstances.size() - 1; j >= 0; j--) + { + InstanceLMInfo * instanceInfo = interiorInfo->mInstances[j]; + for(S32 k = instanceInfo->mLightmapHandles.size() - 1; k >= 0; k--) + { + // All instances can share the base instances static lightmaps. + // Test here to see if we need to load those lightmaps. + if ((j == 0) && !needTexture.test(k)) + continue; + if (!instanceInfo->mLightmapHandles[k]) + { + needTexture.set(k); + continue; + } + GFXTexHandle texObj = instanceInfo->mLightmapHandles[k]; + if (!texObj || !texObj->mBitmap) + { + needTexture.set(k); + continue; + } + + instanceInfo->mLightmapHandles[k].set( texObj->mBitmap, &GFXDefaultPersistentProfile, false, String("Interior Lightmap Handle") ); + } + } +} + +bool InteriorLMManager::loadBaseLightmaps(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle) +{ + AssertFatal(interiorHandle < mInteriors.size(), "InteriorLMManager::loadBaseLightmaps: invalid interior handle"); + AssertFatal(instanceHandle < mInteriors[interiorHandle]->mInstances.size(), "InteriorLMManager::loadBaseLightmaps: invalid instance handle"); + + // must use a valid instance handle + if(!instanceHandle) + return(false); + + InteriorLMInfo * interiorInfo = mInteriors[interiorHandle]; + if(!interiorInfo->mNumLightmaps) + return(false); + + InstanceLMInfo * baseInstanceInfo = interiorInfo->mInstances[0]; + + // already loaded? (if any bitmap is present, then assumed that all will be) + GFXTexHandle texture (baseInstanceInfo->mLightmapHandles[0]); + if(texture.isValid() && texture.getBitmap()) + return(true); + + InstanceLMInfo * instanceInfo = interiorInfo->mInstances[instanceHandle]; + + Resource & interiorRes = instanceInfo->mInstance->getResource(); + if(!bool(interiorRes)) + return(false); + + GBitmap *** pBitmaps = 0; + if(!instanceInfo->mInstance->readLightmaps(&pBitmaps)) + return(false); + + for(U32 i = 0; i < interiorRes->getNumDetailLevels(); i++) + { + Interior * interior = interiorRes->getDetailLevel(i); + AssertFatal(interior, "InteriorLMManager::loadBaseLightmaps: invalid detail level in resource"); + AssertFatal(interior->getLMHandle() != LM_HANDLE(-1), "InteriorLMManager::loadBaseLightmaps: interior not added to manager"); + AssertFatal(interior->getLMHandle() < mInteriors.size(), "InteriorLMManager::loadBaseLightmaps: invalid interior"); + + InteriorLMInfo * interiorInfo = mInteriors[interior->getLMHandle()]; + InstanceLMInfo * baseInstanceInfo = interiorInfo->mInstances[0]; + + for(U32 j = 0; j < interiorInfo->mNumLightmaps; j++) + { + AssertFatal(pBitmaps[i][j], "InteriorLMManager::loadBaseLightmaps: invalid bitmap"); + + if (baseInstanceInfo->mLightmapHandles[j]) + { + GFXTextureObject * texObj = baseInstanceInfo->mLightmapHandles[j]; + texObj->mBitmap = pBitmaps[i][j]; + } + else + baseInstanceInfo->mLightmapHandles[j].set( pBitmaps[i][j], &GFXDefaultPersistentProfile, false, String("Interior Lightmap Handle") ); + } + } + + delete [] pBitmaps; + return(true); +} + +//------------------------------------------------------------------------------ +GFXTexHandle &InteriorLMManager::getHandle(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle, U32 index) +{ + AssertFatal(interiorHandle < mInteriors.size(), "InteriorLMManager::getHandle: invalid interior handle"); + AssertFatal(instanceHandle < mInteriors[interiorHandle]->mInstances.size(), "InteriorLMManager::getHandle: invalid instance handle"); + AssertFatal(index < mInteriors[interiorHandle]->mNumLightmaps, "InteriorLMManager::getHandle: invalid texture index"); + + // valid? if not, then get base lightmap handle + if(!mInteriors[interiorHandle]->mInstances[instanceHandle]->mLightmapHandles[index]) + { + AssertFatal(mInteriors[interiorHandle]->mInstances[0]->mLightmapHandles[index], "InteriorLMManager::getHandle: invalid base texture handle"); + return(mInteriors[interiorHandle]->mInstances[0]->mLightmapHandles[index]); + } + return(mInteriors[interiorHandle]->mInstances[instanceHandle]->mLightmapHandles[index]); +} + +GBitmap * InteriorLMManager::getBitmap(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle, U32 index) +{ + AssertFatal(interiorHandle < mInteriors.size(), "InteriorLMManager::getBitmap: invalid interior handle"); + AssertFatal(instanceHandle < mInteriors[interiorHandle]->mInstances.size(), "InteriorLMManager::getBitmap: invalid instance handle"); + AssertFatal(index < mInteriors[interiorHandle]->mNumLightmaps, "InteriorLMManager::getBitmap: invalid texture index"); + + if(!mInteriors[interiorHandle]->mInstances[instanceHandle]->mLightmapHandles[index]) + { + AssertFatal(mInteriors[interiorHandle]->mInstances[0]->mLightmapHandles[index], "InteriorLMManager::getBitmap: invalid base texture handle"); + return(mInteriors[interiorHandle]->mInstances[0]->mLightmapHandles[index]->getBitmap()); + } + + return(mInteriors[interiorHandle]->mInstances[instanceHandle]->mLightmapHandles[index]->getBitmap()); +} + +Vector & InteriorLMManager::getHandles(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle) +{ + AssertFatal(interiorHandle < mInteriors.size(), "InteriorLMManager::getHandles: invalid interior handle"); + AssertFatal(instanceHandle < mInteriors[interiorHandle]->mInstances.size(), "InteriorLMManager::getHandles: invalid instance handle"); + return(mInteriors[interiorHandle]->mInstances[instanceHandle]->mLightmapHandles); +} + +//------------------------------------------------------------------------------ + +void InteriorLMManager::clearLightmaps(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle) +{ + AssertFatal(interiorHandle < mInteriors.size(), "InteriorLMManager::clearLightmaps: invalid interior handle"); + AssertFatal(instanceHandle < mInteriors[interiorHandle]->mInstances.size(), "InteriorLMManager::clearLightmaps: invalid instance handle"); + + for(U32 i = 0; i < mInteriors[interiorHandle]->mNumLightmaps; i++) + mInteriors[interiorHandle]->mInstances[instanceHandle]->mLightmapHandles[i] = 0; +} + +//------------------------------------------------------------------------------ +GFXTexHandle &InteriorLMManager::duplicateBaseLightmap(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle, U32 index) +{ + AssertFatal(interiorHandle < mInteriors.size(), "InteriorLMManager::duplicateBaseLightmap: invalid interior handle"); + AssertFatal(instanceHandle < mInteriors[interiorHandle]->mInstances.size(), "InteriorLMManager::duplicateBaseLightmap: invalid instance handle"); + AssertFatal(index < mInteriors[interiorHandle]->mNumLightmaps, "InteriorLMManager::duplicateBaseLightmap: invalid texture index"); + + // already exists? + GFXTexHandle texHandle = mInteriors[interiorHandle]->mInstances[instanceHandle]->mLightmapHandles[index]; + if(texHandle && texHandle->getBitmap() ) + return mInteriors[interiorHandle]->mInstances[instanceHandle]->mLightmapHandles[index]; + + AssertFatal(mInteriors[interiorHandle]->mInstances[0]->mLightmapHandles[index], "InteriorLMManager::duplicateBaseLightmap: invalid base handle"); + + // copy it + GBitmap * src = mInteriors[interiorHandle]->mInstances[0]->mLightmapHandles[index]->getBitmap(); + GBitmap * dest = new GBitmap(*src); + + // don't want this texture to be downloaded yet (SceneLighting will take care of that) + GFXTexHandle tHandle( dest, &GFXDefaultPersistentProfile, true, String("Interior Lightmap Handle 2") ); + mInteriors[interiorHandle]->mInstances[instanceHandle]->mLightmapHandles[index] = tHandle; + return mInteriors[interiorHandle]->mInstances[instanceHandle]->mLightmapHandles[index]; +} diff --git a/Engine/source/interior/interiorLMManager.h b/Engine/source/interior/interiorLMManager.h new file mode 100644 index 000000000..09a50b827 --- /dev/null +++ b/Engine/source/interior/interiorLMManager.h @@ -0,0 +1,92 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _INTERIORLMMANAGER_H_ +#define _INTERIORLMMANAGER_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +#include "gfx/gfxTextureHandle.h" + +class GBitmap; +class Interior; +class InteriorInstance; + +typedef U32 LM_HANDLE; + +class InteriorLMManager +{ + private: + + struct InstanceLMInfo { + InteriorInstance * mInstance; + LM_HANDLE * mHandlePtr; + Vector mLightmapHandles; + }; + + struct InteriorLMInfo { + Interior * mInterior; + LM_HANDLE * mHandlePtr; + U32 mNumLightmaps; + LM_HANDLE mBaseInstanceHandle; + Vector mInstances; + }; + + Vector mInteriors; + + public: + + InteriorLMManager(); + ~InteriorLMManager(); + + void destroyBitmaps(); + void destroyTextures(); + + void downloadGLTextures(); + void downloadGLTextures(LM_HANDLE interiorHandle); + bool loadBaseLightmaps(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle); + + void addInterior(LM_HANDLE & interiorHandle, U32 numLightmaps, Interior * interior); + void removeInterior(LM_HANDLE interiorHandle); + + void addInstance(LM_HANDLE interiorHandle, LM_HANDLE & instanceHandle, InteriorInstance * instance); + void removeInstance(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle); + void useBaseTextures(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle); + + void clearLightmaps(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle); + + GFXTexHandle &getHandle(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle, U32 index); + Vector & getHandles(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle); + + // helper's + GFXTexHandle &duplicateBaseLightmap(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle, U32 index); + GBitmap * getBitmap(LM_HANDLE interiorHandle, LM_HANDLE instanceHandle, U32 index); +}; + +extern InteriorLMManager gInteriorLMManager; + +#endif diff --git a/Engine/source/interior/interiorRender.cpp b/Engine/source/interior/interiorRender.cpp new file mode 100644 index 000000000..9f0309b8b --- /dev/null +++ b/Engine/source/interior/interiorRender.cpp @@ -0,0 +1,359 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "interior/interior.h" +#include "scene/sceneRenderState.h" +#include "scene/sceneManager.h" +#include "lighting/lightManager.h" + +#include "gfx/bitmap/gBitmap.h" +#include "math/mMatrix.h" +#include "math/mRect.h" +#include "core/bitVector.h" +#include "platform/profiler.h" +#include "gfx/gfxDevice.h" +#include "interior/interiorInstance.h" +#include "gfx/gfxTextureHandle.h" +#include "materials/materialList.h" +#include "materials/sceneData.h" +#include "materials/matInstance.h" +#include "materials/materialFeatureTypes.h" +#include "math/mathUtils.h" +#include "renderInstance/renderPassManager.h" +#include "core/frameAllocator.h" + +extern bool sgFogActive; +extern U16* sgActivePolyList; +extern U32 sgActivePolyListSize; +extern U16* sgFogPolyList; +extern U32 sgFogPolyListSize; +extern U16* sgEnvironPolyList; +extern U32 sgEnvironPolyListSize; + +Point3F sgOSCamPosition; + + +U32 sgRenderIndices[2048]; +U32 csgNumAllowedPoints = 256; + +extern "C" { + F32 texGen0[8]; + F32 texGen1[8]; + Point2F *fogCoordinatePointer; +} + + +//------------------------------------------------------------------------------ +// Set up render states for interor rendering +//------------------------------------------------------------------------------ +void Interior::setupRenderStates() +{ + // GFX2_RENDER_MERGE + // I suspect we don't need this anymore - MDF + GFX->setStateBlock(mInteriorSB); +} + +//------------------------------------------------------------------------------ +// Setup zone visibility +//------------------------------------------------------------------------------ +ZoneVisDeterminer Interior::setupZoneVis( InteriorInstance *intInst, SceneRenderState *state ) +{ + + U32 zoneOffset = intInst->getZoneRangeStart() != 0xFFFFFFFF ? intInst->getZoneRangeStart() : 0; + + U32 baseZone = 0xFFFFFFFF; + + if (intInst->_getNumCurrZones() == 1) + { + baseZone = intInst->_getCurrZone(0); + } + else + { + for (U32 i = 0; i < intInst->_getNumCurrZones(); i++) + { + if (state->getCullingState().getZoneState(intInst->_getCurrZone(i)).isZoneVisible()) + { + if (baseZone == 0xFFFFFFFF) { + baseZone = intInst->_getCurrZone(i); + break; + } + } + } + if (baseZone == 0xFFFFFFFF) + { + baseZone = intInst->_getCurrZone(0); + } + } + + ZoneVisDeterminer zoneVis; + zoneVis.runFromState(state, zoneOffset, baseZone); + return zoneVis; +} + +//------------------------------------------------------------------------------ +// Setup scenegraph data structure for materials +//------------------------------------------------------------------------------ +SceneData Interior::setupSceneGraphInfo( InteriorInstance *intInst, + SceneRenderState *state ) +{ + SceneData sgData; + sgData.init( state ); + + // TODO: This sucks... interiors only get sunlight? + sgData.lights[0] = LIGHTMGR->getSpecialLight( LightManager::slSunLightType ); + sgData.objTrans = &intInst->getTransform(); + //sgData.backBuffTex = GFX->getSfxBackBuffer(); // NOTICE: SFXBB is removed and refraction is disabled! + + return sgData; +} + +//------------------------------------------------------------------------------ +// Render zone RenderNode +//------------------------------------------------------------------------------ +void Interior::renderZoneNode( SceneRenderState *state, + RenderNode &node, + InteriorInstance *intInst, + SceneData &sgData, + MeshRenderInst *coreRi ) +{ + BaseMatInstance *matInst = state->getOverrideMaterial( node.matInst ); + if ( !matInst ) + return; + + MeshRenderInst *ri = state->getRenderPass()->allocInst(); + *ri = *coreRi; + + ri->lights[0] = LIGHTMGR->getSpecialLight( LightManager::slSunLightType ); + + // setup lightmap + if (dStricmp(LIGHTMGR->getId(), "BLM") == 0) + { + if ( node.lightMapIndex != U8(-1) && + matInst->getFeatures().hasFeature( MFT_LightMap ) ) + ri->lightmap = gInteriorLMManager.getHandle(mLMHandle, intInst->getLMHandle(), node.lightMapIndex ); + } + + ri->matInst = matInst; + ri->primBuffIndex = node.primInfoIndex; + + if ( matInst->getMaterial()->isTranslucent() ) + { + ri->type = RenderPassManager::RIT_Translucent; + ri->translucentSort = true; + ri->sortDistSq = intInst->getRenderWorldBox().getSqDistanceToPoint( state->getCameraPosition() ); + } + + // Sort by the material then the normal map or vertex buffer! + ri->defaultKey = matInst->getStateHint(); + ri->defaultKey2 = ri->lightmap ? (U32)ri->lightmap : (U32)ri->vertBuff; + + state->getRenderPass()->addInst( ri ); +} + +//------------------------------------------------------------------------------ +// Render zone RenderNode +//------------------------------------------------------------------------------ +void Interior::renderReflectNode( SceneRenderState *state, + ReflectRenderNode &node, + InteriorInstance *intInst, + SceneData &sgData, + MeshRenderInst *coreRi ) +{ + BaseMatInstance *matInst = state->getOverrideMaterial( node.matInst ); + if ( !matInst ) + return; + + MeshRenderInst *ri = state->getRenderPass()->allocInst(); + *ri = *coreRi; + + ri->vertBuff = &mReflectVertBuff; + ri->primBuff = &mReflectPrimBuff; + + // use sgData.backBuffer to transfer the reflect texture to the materials + PlaneReflector *rp = &intInst->mPlaneReflectors[ node.reflectPlaneIndex ]; + ri->reflectTex = rp->reflectTex; + ri->reflective = true; + + // setup lightmap + if (dStricmp(LIGHTMGR->getId(), "BLM") == 0) + { + if ( node.lightMapIndex != U8(-1) && + matInst->getFeatures().hasFeature( MFT_LightMap ) ) + ri->lightmap = gInteriorLMManager.getHandle(mLMHandle, intInst->getLMHandle(), node.lightMapIndex ); + } + + ri->matInst = matInst; + ri->primBuffIndex = node.primInfoIndex; + + // Sort by the material then the normal map or vertex buffer! + ri->defaultKey = matInst->getStateHint(); + ri->defaultKey2 = ri->lightmap ? (U32)ri->lightmap : (U32)ri->vertBuff; + + state->getRenderPass()->addInst( ri ); +} + + +//------------------------------------------------------------------------------ +// Setup the rendering +//------------------------------------------------------------------------------ +void Interior::setupRender( InteriorInstance *intInst, + SceneRenderState *state, + MeshRenderInst *coreRi, const MatrixF* worldToCamera ) +{ + // Set the vertex and primitive buffers + coreRi->vertBuff = &mVertBuff; + coreRi->primBuff = &mPrimBuff; + + // Grab our render transform and scale it + MatrixF objectToWorld = intInst->getRenderTransform(); + objectToWorld.scale( intInst->getScale() ); + + coreRi->objectToWorld = state->getRenderPass()->allocUniqueXform(objectToWorld); + coreRi->worldToCamera = state->getRenderPass()->allocUniqueXform(*worldToCamera); // This is handed down from SceneRenderState::renderCurrentImages() + coreRi->projection = state->getRenderPass()->allocSharedXform(RenderPassManager::Projection); + + coreRi->type = RenderPassManager::RIT_Interior; + + // NOTICE: SFXBB is removed and refraction is disabled! + //coreRi->backBuffTex = GFX->getSfxBackBuffer(); +} + + +//------------------------------------------------------------------------------ +// Render +//------------------------------------------------------------------------------ +void Interior::prepBatchRender( InteriorInstance *intInst, SceneRenderState *state ) +{ + // coreRi - used as basis for subsequent interior ri allocations + MeshRenderInst *coreRi = state->getRenderPass()->allocInst(); + SceneData sgData; + sgData.init( state ); + setupRender( intInst, state, coreRi, &state->getWorldViewMatrix() ); + ZoneVisDeterminer zoneVis = setupZoneVis( intInst, state ); + +// GFX2_RENDER_MERGE +#ifndef TORQUE_SHIPPING + if( smRenderMode != 0 ) + { + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind(intInst, &InteriorInstance::_renderObject); + ri->type = RenderPassManager::RIT_Object; + state->getRenderPass()->addInst( ri ); + return; + } +#endif + + // render zones + for( U32 i=0; iisReflectPass() ) + continue; + + for( U32 j=0; jrender( state, *coreRi, getLMHandle(), + intInst->getLMHandle(), intInst ); + + // render reflective surfaces + if ( !state->isReflectPass() ) + { + renderLights(state, intInst, sgData, coreRi, zoneVis); + + for( U32 i=0; iinteriorInstInit(intInst)) + return; + + // build the render instances... + for(U32 z=0; z -1) + zoneid += intInst->getZoneRangeStart();// only zone managers... + else + zoneid = intInst->_getCurrZone(0);// if not what zone is it in... + + if (!smLightPlugin->zoneInit(zoneid)) + continue; + + static Vector sRenderList; + sRenderList.clear(); + + for(U32 j=0; jgetRenderPass()->allocInst(); + *ri = *coreRi; + ri->type = RenderPassManager::RIT_InteriorDynamicLighting; + ri->matInst = node.matInst; + ri->primBuffIndex = node.primInfoIndex; + + sRenderList.push_back(ri); + } + smLightPlugin->processRI(state, sRenderList); + } + */ +} + + diff --git a/Engine/source/interior/interiorRes.cpp b/Engine/source/interior/interiorRes.cpp new file mode 100644 index 000000000..4e1d2cb3a --- /dev/null +++ b/Engine/source/interior/interiorRes.cpp @@ -0,0 +1,355 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + + +#include "platform/platform.h" +#include "interior/interiorRes.h" + +#include "console/console.h" +#include "core/stream/fileStream.h" +#include "interior/interior.h" +#include "interior/interiorResObjects.h" +#include "gfx/bitmap/gBitmap.h" +#include "interior/forceField.h" + + +const U32 InteriorResource::smFileVersion = 44; + +//-------------------------------------------------------------------------- +InteriorResource::InteriorResource() +{ + VECTOR_SET_ASSOCIATION(mDetailLevels); + VECTOR_SET_ASSOCIATION(mSubObjects); + VECTOR_SET_ASSOCIATION(mTriggers); + //VECTOR_SET_ASSOCIATION(mPaths); + VECTOR_SET_ASSOCIATION(mInteriorPathFollowers); + VECTOR_SET_ASSOCIATION(mForceFields); + VECTOR_SET_ASSOCIATION(mAISpecialNodes); + + mPreviewBitmap = NULL; +} + +InteriorResource::~InteriorResource() +{ + U32 i; + + for (i = 0; i < mDetailLevels.size(); i++) + delete mDetailLevels[i]; + for (i = 0; i < mSubObjects.size(); i++) + delete mSubObjects[i]; + for (i = 0; i < mTriggers.size(); i++) + delete mTriggers[i]; + for (i = 0; i < mInteriorPathFollowers.size(); i++) + delete mInteriorPathFollowers[i]; + for (i = 0; i < mForceFields.size(); i++) + delete mForceFields[i]; + for (i = 0; i < mAISpecialNodes.size(); i++) + delete mAISpecialNodes[i]; + for (i = 0; i < mGameEntities.size(); i++) + delete mGameEntities[i]; + + delete mPreviewBitmap; + mPreviewBitmap = NULL; +} + +bool InteriorResource::read(Stream& stream) +{ + AssertFatal(stream.hasCapability(Stream::StreamRead), "Interior::read: non-read capable stream passed"); + AssertFatal(stream.getStatus() == Stream::Ok, "Interior::read: Error, stream in inconsistent state"); + + U32 i; + + // Version this stream + U32 fileVersion; + stream.read(&fileVersion); + if (fileVersion != smFileVersion) { + Con::errorf(ConsoleLogEntry::General, "InteriorResource::read: incompatible file version found."); + return false; + } + + // Handle preview + bool previewIncluded = false; + stream.read(&previewIncluded); + if (previewIncluded) { + GBitmap bmp; + bmp.readBitmap("png",stream); + } + + // Details + U32 numDetailLevels; + stream.read(&numDetailLevels); + mDetailLevels.setSize(numDetailLevels); + for (i = 0; i < mDetailLevels.size(); i++) + mDetailLevels[i] = NULL; + + for (i = 0; i < mDetailLevels.size(); i++) { + mDetailLevels[i] = new Interior; + if (mDetailLevels[i]->read(stream) == false) { + Con::errorf(ConsoleLogEntry::General, "Unable to read detail level %d in interior resource", i); + return false; + } + } + + // Subobjects: mirrors, translucencies + U32 numSubObjects; + stream.read(&numSubObjects); + mSubObjects.setSize(numSubObjects); + for (i = 0; i < mSubObjects.size(); i++) + mSubObjects[i] = NULL; + + for (i = 0; i < mSubObjects.size(); i++) { + mSubObjects[i] = new Interior; + if (mSubObjects[i]->read(stream) == false) { + AssertISV(false, avar("Unable to read subobject %d in interior resource", i)); + return false; + } + } + + // Triggers + U32 numTriggers; + stream.read(&numTriggers); + mTriggers.setSize(numTriggers); + for (i = 0; i < mTriggers.size(); i++) + mTriggers[i] = NULL; + + for (i = 0; i < mTriggers.size(); i++) { + mTriggers[i] = new InteriorResTrigger; + if (mTriggers[i]->read(stream) == false) { + AssertISV(false, avar("Unable to read trigger %d in interior resource", i)); + return false; + } + } + + U32 numChildren; + stream.read(&numChildren); + mInteriorPathFollowers.setSize(numChildren); + for (i = 0; i < mInteriorPathFollowers.size(); i++) + mInteriorPathFollowers[i] = NULL; + + for (i = 0; i < mInteriorPathFollowers.size(); i++) { + mInteriorPathFollowers[i] = new InteriorPathFollower; + if (mInteriorPathFollowers[i]->read(stream) == false) { + AssertISV(false, avar("Unable to read child %d in interior resource", i)); + return false; + } + } + + U32 numFields; + stream.read(&numFields); + mForceFields.setSize(numFields); + for (i = 0; i < mForceFields.size(); i++) + mForceFields[i] = NULL; + + for (i = 0; i < mForceFields.size(); i++) { + mForceFields[i] = new ForceField; + if (mForceFields[i]->read(stream) == false) { + AssertISV(false, avar("Unable to read field %d in interior resource", i)); + return false; + } + } + + U32 numSpecNodes; + stream.read(&numSpecNodes); + mAISpecialNodes.setSize(numSpecNodes); + for (i = 0; i < mAISpecialNodes.size(); i++) + mAISpecialNodes[i] = NULL; + + for (i = 0; i < mAISpecialNodes.size(); i++) { + mAISpecialNodes[i] = new AISpecialNode; + if (mAISpecialNodes[i]->read(stream) == false) { + AssertISV(false, avar("Unable to read SpecNode %d in interior resource", i)); + return false; + } + } + + U32 dummyInt; + stream.read(&dummyInt); + if (dummyInt == 1) + { + if (mDetailLevels.size() != 0) + getDetailLevel(0)->readVehicleCollision(stream); + } + + // For expansion purposes + stream.read(&dummyInt); + if(dummyInt == 2) + { + U32 numGameEnts; + stream.read(&numGameEnts); + mGameEntities.setSize(numGameEnts); + for (i = 0; i < numGameEnts; i++) + mGameEntities[i] = new ItrGameEntity; + + for (i = 0; i < numGameEnts; i++) { + if (mGameEntities[i]->read(stream) == false) { + AssertISV(false, avar("Unable to read SpecNode %d in interior resource", i)); + return false; + } + } + stream.read(&dummyInt); + } + + return (stream.getStatus() == Stream::Ok); +} + +bool InteriorResource::write(Stream& stream) const +{ + AssertFatal(stream.hasCapability(Stream::StreamWrite), "Interior::write: non-write capable stream passed"); + AssertFatal(stream.getStatus() == Stream::Ok, "Interior::write: Error, stream in inconsistent state"); + + // Version the stream + stream.write(smFileVersion); + + // Handle preview + // + if (mPreviewBitmap != NULL) { + stream.write(bool(true)); + mPreviewBitmap->writeBitmap("png",stream); + } else { + stream.write(bool(false)); + } + + // Write out the interiors + stream.write(mDetailLevels.size()); + U32 i; + for (i = 0; i < mDetailLevels.size(); i++) { + if (mDetailLevels[i]->write(stream) == false) { + AssertISV(false, "Unable to write detail level to stream"); + return false; + } + } + + stream.write(mSubObjects.size()); + for (i = 0; i < mSubObjects.size(); i++) { + if (mSubObjects[i]->write(stream) == false) { + AssertISV(false, "Unable to write subobject to stream"); + return false; + } + } + + stream.write(mTriggers.size()); + for (i = 0; i < mTriggers.size(); i++) { + if (mTriggers[i]->write(stream) == false) { + AssertISV(false, "Unable to write trigger to stream"); + return false; + } + } + + stream.write(mInteriorPathFollowers.size()); + for (i = 0; i < mInteriorPathFollowers.size(); i++) { + if (mInteriorPathFollowers[i]->write(stream) == false) { + AssertISV(false, avar("Unable to write child %d in interior resource", i)); + return false; + } + } + + stream.write(mForceFields.size()); + for (i = 0; i < mForceFields.size(); i++) { + if (mForceFields[i]->write(stream) == false) { + AssertISV(false, avar("Unable to write field %d in interior resource", i)); + return false; + } + } + + stream.write(mAISpecialNodes.size()); + for (i = 0; i < mAISpecialNodes.size(); i++) { + if (mAISpecialNodes[i]->write(stream) == false) { + AssertISV(false, avar("Unable to write SpecNode %d in interior resource", i)); + return false; + } + } + + stream.write(U32(1)); + if (mDetailLevels.size() != 0) + const_cast(mDetailLevels[0])->writeVehicleCollision(stream); + + // For expansion purposes + if (mGameEntities.size()) + { + stream.write(U32(2)); + stream.write(mGameEntities.size()); + for(i = 0; i < mGameEntities.size(); i++) + { + if (mGameEntities[i]->write(stream) == false) { + AssertISV(false, avar("Unable to write GameEnt %d in interior resource", i)); + return false; + } + } + } + stream.write(U32(0)); + + return (stream.getStatus() == Stream::Ok); +} + +GBitmap* InteriorResource::extractPreview(Stream& stream) +{ + AssertFatal(stream.hasCapability(Stream::StreamRead), "Interior::read: non-read capable stream passed"); + AssertFatal(stream.getStatus() == Stream::Ok, "Interior::read: Error, stream in inconsistent state"); + + // Version this stream + U32 fileVersion; + stream.read(&fileVersion); + if (fileVersion != smFileVersion) { + Con::errorf(ConsoleLogEntry::General, "InteriorResource::read: incompatible file version found."); + return NULL; + } + + // Handle preview + bool previewIncluded = false; + stream.read(&previewIncluded); + if (previewIncluded) { + GBitmap* pBmp = new GBitmap; + if (pBmp->readBitmap("png",stream) == true) + return pBmp; + + delete pBmp; + } + + return NULL; +} + +//------------------------------------------------------------------------------ +//-------------------------------------- Interior Resource constructor +template<> void *Resource::create(const Torque::Path &path) +{ + FileStream stream; + + stream.open( path.getFullPath(), Torque::FS::File::Read ); + + if ( stream.getStatus() != Stream::Ok ) + return NULL; + + InteriorResource* pResource = new InteriorResource; + + if (pResource->read(stream) == true) + return pResource; + else + { + delete pResource; + return NULL; + } +} + +template<> ResourceBase::Signature Resource::signature() +{ + return MakeFourCC('t','d','i','f'); +} diff --git a/Engine/source/interior/interiorRes.h b/Engine/source/interior/interiorRes.h new file mode 100644 index 000000000..27f57bc39 --- /dev/null +++ b/Engine/source/interior/interiorRes.h @@ -0,0 +1,170 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _INTERIORRES_H_ +#define _INTERIORRES_H_ + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + + +class Stream; +class Interior; +class GBitmap; +class InteriorResTrigger; +class InteriorPath; +class InteriorPathFollower; +class ForceField; +class AISpecialNode; +class ItrGameEntity; + +class InteriorResource +{ + static const U32 smFileVersion; + + protected: + Vector mDetailLevels; + Vector mSubObjects; + Vector mTriggers; + Vector mInteriorPathFollowers; + Vector mForceFields; + Vector mAISpecialNodes; + Vector mGameEntities; + + GBitmap* mPreviewBitmap; + + public: + InteriorResource(); + ~InteriorResource(); + + bool read(Stream& stream); + bool write(Stream& stream) const; + static GBitmap* extractPreview(Stream&); + + S32 getNumDetailLevels() const; + S32 getNumSubObjects() const; + S32 getNumTriggers() const; + S32 getNumInteriorPathFollowers() const; + S32 getNumForceFields() const; + S32 getNumSpecialNodes() const; + S32 getNumGameEntities() const; + + Interior* getDetailLevel(const U32); + Interior* getSubObject(const U32); + InteriorResTrigger* getTrigger(const U32); + InteriorPathFollower* getInteriorPathFollower(const U32); + ForceField* getForceField(const U32); + AISpecialNode* getSpecialNode(const U32); + ItrGameEntity* getGameEntity(const U32); +}; + +//-------------------------------------------------------------------------- +inline S32 InteriorResource::getNumDetailLevels() const +{ + return mDetailLevels.size(); +} + +inline S32 InteriorResource::getNumSubObjects() const +{ + return mSubObjects.size(); +} + +inline S32 InteriorResource::getNumTriggers() const +{ + return mTriggers.size(); +} + +inline S32 InteriorResource::getNumSpecialNodes() const +{ + return mAISpecialNodes.size(); +} + +inline S32 InteriorResource::getNumGameEntities() const +{ + return mGameEntities.size(); +} + +inline S32 InteriorResource::getNumInteriorPathFollowers() const +{ + return mInteriorPathFollowers.size(); +} + +inline S32 InteriorResource::getNumForceFields() const +{ + return mForceFields.size(); +} + +inline Interior* InteriorResource::getDetailLevel(const U32 idx) +{ + AssertFatal(idx < getNumDetailLevels(), "Error, out of bounds detail level!"); + + if (idx < getNumDetailLevels()) + return mDetailLevels[idx]; + else + return NULL; +} + +inline Interior* InteriorResource::getSubObject(const U32 idx) +{ + AssertFatal(idx < getNumSubObjects(), "Error, out of bounds subObject!"); + + return mSubObjects[idx]; +} + +inline InteriorResTrigger* InteriorResource::getTrigger(const U32 idx) +{ + AssertFatal(idx < getNumTriggers(), "Error, out of bounds trigger!"); + + return mTriggers[idx]; +} + +inline InteriorPathFollower* InteriorResource::getInteriorPathFollower(const U32 idx) +{ + AssertFatal(idx < getNumInteriorPathFollowers(), "Error, out of bounds path follower!"); + + return mInteriorPathFollowers[idx]; +} + +inline ForceField* InteriorResource::getForceField(const U32 idx) +{ + AssertFatal(idx < getNumForceFields(), "Error, out of bounds force field!"); + + return mForceFields[idx]; +} + +inline AISpecialNode* InteriorResource::getSpecialNode(const U32 idx) +{ + AssertFatal(idx < getNumSpecialNodes(), "Error, out of bounds Special Nodes!"); + + return mAISpecialNodes[idx]; +} + +inline ItrGameEntity* InteriorResource::getGameEntity(const U32 idx) +{ + AssertFatal(idx < getNumGameEntities(), "Error, out of bounds Game ENts!"); + + return mGameEntities[idx]; +} + +#endif // _H_INTERIORRES_ + diff --git a/Engine/source/interior/interiorResObjects.cpp b/Engine/source/interior/interiorResObjects.cpp new file mode 100644 index 000000000..591dc4517 --- /dev/null +++ b/Engine/source/interior/interiorResObjects.cpp @@ -0,0 +1,246 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "interior/interiorResObjects.h" +#include "core/stream/stream.h" +#include "math/mathIO.h" + +//-------------------------------------------------------------------------- +//-------------------------------------- +// + +void InteriorDict::read(Stream &stream) +{ + U32 sz; + stream.read(&sz); + setSize(sz); + for(U32 i = 0; i < sz; i++) + { + InteriorDictEntry e; + stream.readString(e.name); + stream.readString(e.value); + (*this)[i] = e; + } +} + +void InteriorDict::write(Stream &stream) const +{ + U32 sz = size(); + stream.write(sz); + for(U32 i = 0; i < sz; i++) + { + stream.writeString((*this)[i].name); + stream.writeString((*this)[i].value); + } +} + +bool InteriorResTrigger::read(Stream& stream) +{ + U32 i, size; + stream.readString(mName); + mDataBlock = stream.readSTString(); + mDictionary.read(stream); + + // Read the polyhedron + stream.read(&size); + mPolyhedron.pointList.setSize(size); + for (i = 0; i < mPolyhedron.pointList.size(); i++) + mathRead(stream, &mPolyhedron.pointList[i]); + + stream.read(&size); + mPolyhedron.planeList.setSize(size); + for (i = 0; i < mPolyhedron.planeList.size(); i++) + mathRead(stream, &mPolyhedron.planeList[i]); + + stream.read(&size); + mPolyhedron.edgeList.setSize(size); + for (i = 0; i < mPolyhedron.edgeList.size(); i++) { + Polyhedron::Edge& rEdge = mPolyhedron.edgeList[i]; + + stream.read(&rEdge.face[0]); + stream.read(&rEdge.face[1]); + stream.read(&rEdge.vertex[0]); + stream.read(&rEdge.vertex[1]); + } + + // And the offset + mathRead(stream, &mOffset); + + return (stream.getStatus() == Stream::Ok); +} + +bool InteriorResTrigger::write(Stream& stream) const +{ + U32 i; + + stream.writeString(mName); + stream.writeString(mDataBlock); + mDictionary.write(stream); + + // Write the polyhedron + stream.write(mPolyhedron.pointList.size()); + for (i = 0; i < mPolyhedron.pointList.size(); i++) + mathWrite(stream, mPolyhedron.pointList[i]); + + stream.write(mPolyhedron.planeList.size()); + for (i = 0; i < mPolyhedron.planeList.size(); i++) + mathWrite(stream, mPolyhedron.planeList[i]); + + stream.write(mPolyhedron.edgeList.size()); + for (i = 0; i < mPolyhedron.edgeList.size(); i++) { + const Polyhedron::Edge& rEdge = mPolyhedron.edgeList[i]; + + stream.write(rEdge.face[0]); + stream.write(rEdge.face[1]); + stream.write(rEdge.vertex[0]); + stream.write(rEdge.vertex[1]); + } + + // And the offset + mathWrite(stream, mOffset); + + return (stream.getStatus() == Stream::Ok); +} + +InteriorPathFollower::InteriorPathFollower() +{ + VECTOR_SET_ASSOCIATION( mTriggerIds ); + VECTOR_SET_ASSOCIATION( mWayPoints ); + + mName = ""; + mPathIndex = 0; + mOffset.set(0, 0, 0); +} + +InteriorPathFollower::~InteriorPathFollower() +{ + +} + +bool InteriorPathFollower::read(Stream& stream) +{ + mName = stream.readSTString(); + mDataBlock = stream.readSTString(); + stream.read(&mInteriorResIndex); + mathRead(stream, &mOffset); + mDictionary.read(stream); + + U32 numTriggers; + stream.read(&numTriggers); + mTriggerIds.setSize(numTriggers); + for (U32 i = 0; i < mTriggerIds.size(); i++) + stream.read(&mTriggerIds[i]); + + U32 numWayPoints; + stream.read(&numWayPoints); + mWayPoints.setSize(numWayPoints); + for(U32 i = 0; i < numWayPoints; i++) + { + mathRead(stream, &mWayPoints[i].pos); + mathRead(stream, &mWayPoints[i].rot); + stream.read(&mWayPoints[i].msToNext); + stream.read(&mWayPoints[i].smoothingType); + } + stream.read(&mTotalMS); + return (stream.getStatus() == Stream::Ok); +} + +bool InteriorPathFollower::write(Stream& stream) const +{ + stream.writeString(mName); + stream.writeString(mDataBlock); + stream.write(mInteriorResIndex); + mathWrite(stream, mOffset); + mDictionary.write(stream); + + stream.write(mTriggerIds.size()); + for (U32 i = 0; i < mTriggerIds.size(); i++) + stream.write(mTriggerIds[i]); + + stream.write(U32(mWayPoints.size())); + for (U32 i = 0; i < mWayPoints.size(); i++) { + mathWrite(stream, mWayPoints[i].pos); + mathWrite(stream, mWayPoints[i].rot); + stream.write(mWayPoints[i].msToNext); + stream.write(mWayPoints[i].smoothingType); + } + stream.write(mTotalMS); + + return (stream.getStatus() == Stream::Ok); +} + +AISpecialNode::AISpecialNode() +{ + mName = ""; + mPos.set(0, 0, 0); +} + +AISpecialNode::~AISpecialNode() +{ +} + +bool AISpecialNode::read(Stream& stream) +{ + mName = stream.readSTString(); + mathRead(stream, &mPos); + + return (stream.getStatus() == Stream::Ok); +} + +bool AISpecialNode::write(Stream& stream) const +{ + stream.writeString(mName); + mathWrite(stream, mPos); + + return (stream.getStatus() == Stream::Ok); +} + +ItrGameEntity::ItrGameEntity() +{ + mDataBlock = ""; + mGameClass = ""; + mPos.set(0, 0, 0); +} + +ItrGameEntity::~ItrGameEntity() +{ +} + +bool ItrGameEntity::read(Stream& stream) +{ + mDataBlock = stream.readSTString(); + mGameClass = stream.readSTString(); + mathRead(stream, &mPos); + mDictionary.read(stream); + + return (stream.getStatus() == Stream::Ok); +} + +bool ItrGameEntity::write(Stream& stream) const +{ + stream.writeString(mDataBlock); + stream.writeString(mGameClass); + mathWrite(stream, mPos); + mDictionary.write(stream); + + return (stream.getStatus() == Stream::Ok); +} diff --git a/Engine/source/interior/interiorResObjects.h b/Engine/source/interior/interiorResObjects.h new file mode 100644 index 000000000..0307dd99f --- /dev/null +++ b/Engine/source/interior/interiorResObjects.h @@ -0,0 +1,151 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _INTERIORRESOBJECTS_H_ +#define _INTERIORRESOBJECTS_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _MBOX_H_ +#include "math/mBox.h" +#endif +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _MPOLYHEDRON_H_ +#include "math/mPolyhedron.h" +#endif +#ifndef _MQUAT_H_ +#include "math/mQuat.h" +#endif + +class Stream; + +struct InteriorDictEntry +{ + char name[256]; + char value[256]; +}; + +class InteriorDict : public Vector +{ +public: + void read(Stream& stream); + void write(Stream& stream) const; +}; + +class InteriorResTrigger +{ + public: + enum Constants { + MaxNameChars = 255 + }; + + char mName[MaxNameChars+1]; + StringTableEntry mDataBlock; + InteriorDict mDictionary; + + Point3F mOffset; + Polyhedron mPolyhedron; + + public: + InteriorResTrigger() { } + + bool read(Stream& stream); + bool write(Stream& stream) const; +}; + +class InteriorPathFollower +{ + public: + struct WayPoint { + Point3F pos; + QuatF rot; + U32 msToNext; + U32 smoothingType; + }; + StringTableEntry mName; + StringTableEntry mDataBlock; + U32 mInteriorResIndex; + U32 mPathIndex; + Point3F mOffset; + Vector mTriggerIds; + Vector mWayPoints; + U32 mTotalMS; + InteriorDict mDictionary; + + public: + InteriorPathFollower(); + ~InteriorPathFollower(); + + bool read(Stream& stream); + bool write(Stream& stream) const; +}; + + +class AISpecialNode +{ + public: + enum + { + chute = 0, + }; + + public: + StringTableEntry mName; + Point3F mPos; + //U32 mType; + + public: + AISpecialNode(); + ~AISpecialNode(); + + bool read(Stream& stream); + bool write(Stream& stream) const; + +}; + +class ItrGameEntity +{ + public: + StringTableEntry mDataBlock; + StringTableEntry mGameClass; + Point3F mPos; + InteriorDict mDictionary; + + public: + ItrGameEntity(); + ~ItrGameEntity(); + + bool read(Stream& stream); + bool write(Stream& stream) const; + +}; + +#endif // _H_INTERIORRESOBJECTS_ diff --git a/Engine/source/interior/interiorSimpleMesh.cpp b/Engine/source/interior/interiorSimpleMesh.cpp new file mode 100644 index 000000000..d8684acc5 --- /dev/null +++ b/Engine/source/interior/interiorSimpleMesh.cpp @@ -0,0 +1,644 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "interior/interiorSimpleMesh.h" + +#include "interior/interiorLMManager.h" +#include "interior/interior.h" +#include "console/console.h" +#include "scene/sceneObject.h" +#include "math/mathIO.h" +#include "materials/matInstance.h" +#include "materials/materialManager.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "ts/tsShape.h" +#include "gfx/bitmap/gBitmap.h" + + +Vector *InteriorSimpleMesh::renderInstList = new Vector(); + + +// Checks for polygon level collision with given planes +U32 _whichSide(PlaneF pln, Point3F* verts) +{ + Point3F currv, nextv; + S32 csd, nsd; + + // Find out which side the first vert is on + U32 side = PlaneF::On; + currv = verts[0]; + csd = pln.whichSide(currv); + if(csd != PlaneF::On) + side = csd; + + for(U32 k = 1; k < 3; k++) + { + nextv = verts[k]; + nsd = pln.whichSide(nextv); + if((csd == PlaneF::Back && nsd == PlaneF::Front) || + (csd == PlaneF::Front && nsd == PlaneF::Back)) + return 2; + else if (nsd != PlaneF::On) + side = nsd; + currv = nextv; + csd = nsd; + } + + // Loop back to the first vert + nextv = verts[0]; + nsd = pln.whichSide(nextv); + if((csd == PlaneF::Back && nsd == PlaneF::Front) || + (csd == PlaneF::Front && nsd == PlaneF::Back)) + return 2; + else if(nsd != PlaneF::On) + side = nsd; + return side; + +} + + +//bool InteriorSimpleMesh::castRay(const Point3F &start, const Point3F &end, RayInfo* info) +//{ +// bool found = false; +// F32 best_t = F32_MAX; +// Point3F best_normal = Point3F(0, 0, 1); +// Point3F dir = end - start; +// +// for(U32 p=0; pt = best_t; +// info->normal = best_normal; +// info->material = 0; +// } +// +// return found; +//} + +bool InteriorSimpleMesh::castPlanes(PlaneF left, PlaneF right, PlaneF top, PlaneF bottom) +{ + for(U32 p=0; pmPrimitiveCount == packedPrimitives.size()), "Primitive mismatch"); + + renderInstList->clear(); + + for(S32 i=0; igetRenderPass()->allocInst(); + *inst = copyinst; + + inst->matInst = materialList->getMaterialInst(draw.diffuseIndex); + if(!inst->matInst) + inst->matInst = MATMGR->getWarningMatInstance(); + if(!inst->matInst) + continue; + + inst->primBuffIndex = i; + inst->primBuff = &primBuff; + inst->vertBuff = &vertBuff; + + if(draw.alpha) + { + inst->translucentSort = true; + inst->type = RenderPassManager::RIT_Translucent; + } + + inst->lightmap = gInteriorLMManager.getHandle(interiorlmhandle, instancelmhandle, draw.lightMapIndex); + + state->getRenderPass()->addInst(inst); + renderInstList->push_back(inst); + } + + if(lightingplugin && renderInstList->size() > 0) + { + if(lightingplugin->interiorInstInit(intInst, this)) + { + if(lightingplugin->allZoneInit()) + { + Vector &list = *renderInstList; + + // clone the origial instances to avoid damaging the originals' data + for(int i=0; isize(); i++) + { + MeshRenderInst *inst = state->getRenderPass()->allocInst(); + const MeshRenderInst *oldinst = list[i]; + *inst = *oldinst; + list[i] = inst; + } + + lightingplugin->processRI(state, list); + } + } + } + */ +} + +bool InteriorSimpleMesh::read(Stream& stream) +{ + // Simple serialization + S32 vectorSize = 0; + + // Primitives + stream.read(&vectorSize); + primitives.setSize(vectorSize); + for (U32 i = 0; i < primitives.size(); i++) + { + stream.read(&primitives[i].alpha); + stream.read(&primitives[i].texS); + stream.read(&primitives[i].texT); + stream.read(&primitives[i].diffuseIndex); + stream.read(&primitives[i].lightMapIndex); + stream.read(&primitives[i].start); + stream.read(&primitives[i].count); + + mathRead(stream, &primitives[i].lightMapEquationX); + mathRead(stream, &primitives[i].lightMapEquationY); + mathRead(stream, &primitives[i].lightMapOffset); + mathRead(stream, &primitives[i].lightMapSize); + } + + // Indices + stream.read(&vectorSize); + indices.setSize(vectorSize); + for (U32 i = 0; i < indices.size(); i++) + stream.read(&indices[i]); + + // Vertices + stream.read(&vectorSize); + verts.setSize(vectorSize); + for (U32 i = 0; i < verts.size(); i++) + mathRead(stream, &verts[i]); + + // Normals + stream.read(&vectorSize); + norms.setSize(vectorSize); + for (U32 i = 0; i < norms.size(); i++) + mathRead(stream, &norms[i]); + + // Diffuse UVs + stream.read(&vectorSize); + diffuseUVs.setSize(vectorSize); + for (U32 i = 0; i < diffuseUVs.size(); i++) + mathRead(stream, &diffuseUVs[i]); + + // Lightmap UVs + stream.read(&vectorSize); + lightmapUVs.setSize(vectorSize); + for (U32 i = 0; i < lightmapUVs.size(); i++) + mathRead(stream, &lightmapUVs[i]); + + // Material list + bool hasMaterialList = false; + stream.read(&hasMaterialList); + if (hasMaterialList) + { + // Since we are doing this externally to a TSShape read we need to + // make sure that our read version is the same as our write version. + // It is possible that it was changed along the way by a loaded TSShape. + TSShape::smReadVersion = 25; + + if (materialList) + delete materialList; + + materialList = new TSMaterialList; + materialList->read(stream); + } + else + materialList = NULL; + + // Diffuse bitmaps + stream.read(&vectorSize); + for (U32 i = 0; i < vectorSize; i++) + { + // need to read these + bool hasBitmap = false; + stream.read(&hasBitmap); + if(hasBitmap) + { + GBitmap* bitMap = new GBitmap; + bitMap->readBitmap("png",stream); + delete bitMap; + } + } + + // Misc data + stream.read(&hasSolid); + stream.read(&hasTranslucency); + mathRead(stream, &bounds); + mathRead(stream, &transform); + mathRead(stream, &scale); + + calculateBounds(); + buildBuffers(); + + return true; +} + +bool InteriorSimpleMesh::write(Stream& stream) const +{ + // Simple serialization + // Primitives + stream.write(primitives.size()); + for (U32 i = 0; i < primitives.size(); i++) + { + stream.write(primitives[i].alpha); + stream.write(primitives[i].texS); + stream.write(primitives[i].texT); + stream.write(primitives[i].diffuseIndex); + stream.write(primitives[i].lightMapIndex); + stream.write(primitives[i].start); + stream.write(primitives[i].count); + + mathWrite(stream, primitives[i].lightMapEquationX); + mathWrite(stream, primitives[i].lightMapEquationY); + mathWrite(stream, primitives[i].lightMapOffset); + mathWrite(stream, primitives[i].lightMapSize); + } + + // Indices + stream.write(indices.size()); + for (U32 i = 0; i < indices.size(); i++) + stream.write(indices[i]); + + // Vertices + stream.write(verts.size()); + for (U32 i = 0; i < verts.size(); i++) + mathWrite(stream, verts[i]); + + // Normals + stream.write(norms.size()); + for (U32 i = 0; i < norms.size(); i++) + mathWrite(stream, norms[i]); + + // Diffuse UVs + stream.write(diffuseUVs.size()); + for (U32 i = 0; i < diffuseUVs.size(); i++) + mathWrite(stream, diffuseUVs[i]); + + // Lightmap UVs + stream.write(lightmapUVs.size()); + for (U32 i = 0; i < lightmapUVs.size(); i++) + mathWrite(stream, lightmapUVs[i]); + + // Material list + if (materialList) + { + stream.write(true); + materialList->write(stream); + } + else + stream.write(false); + + // Diffuse bitmaps + if (!materialList) + stream.write(0); + else + { + stream.write(materialList->size()); + + for (U32 i = 0; i < materialList->size(); i++) + { + GFXTexHandle handle(materialList->getDiffuseTexture(i)); + + if (handle.isValid()) + { + GBitmap* bitMap = handle.getBitmap(); + + if (bitMap) + { + stream.write(true); + bitMap->writeBitmap("png",stream); + } + else + stream.write(false); + } + else + stream.write(false); + } + } + + // Misc data + stream.write(hasSolid); + stream.write(hasTranslucency); + mathWrite(stream, bounds); + mathWrite(stream, transform); + mathWrite(stream, scale); + + return true; +} + +void InteriorSimpleMesh::buildBuffers() +{ + bool flipped = false; + + MatrixF trans = transform; + trans.scale(scale); + + Point3F r0, r1, r2; + trans.getRow(0, &r0); + trans.getRow(1, &r1); + trans.getRow(2, &r2); + F32 det = r0.x * (r1.y * r2.z - r1.z * r2.y) - + r0.y * (r1.x * r2.z - r1.z * r2.x) + + r0.z * (r1.x * r2.y - r1.y * r2.x); + flipped = det < 0.0f; + + // setup the repack vectors + packedIndices.clear(); + packedPrimitives.clear(); + packedIndices.reserve(indices.size() * 2); + packedPrimitives.reserve(primitives.size()); + + Vector addedprim; + addedprim.setSize(primitives.size()); + dMemset(addedprim.address(), 0, (addedprim.size() * sizeof(bool))); + + Vector tang; + Vector binorm; + tang.setSize(verts.size()); + binorm.setSize(verts.size()); + dMemset(tang.address(), 0, (tang.size() * sizeof(Point3F))); + dMemset(binorm.address(), 0, (binorm.size() * sizeof(Point3F))); + + // fill the repack vectors + for(U32 p=0; p packedprims; + packedprims.setSize(packedPrimitives.size()); + + for(U32 i=0; i packedIndices[prim.start + ii]) + p.minIndex = packedIndices[prim.start + ii]; + if(maxindex < packedIndices[prim.start + ii]) + maxindex = packedIndices[prim.start + ii]; + } + + // D3D voodoo - not the actual numverts, only the max span (maxindex - minindex) - this needs a better variable name... + p.numVertices = (maxindex - p.minIndex) + 1; + } + + // create vb style sysmem buffer + Vector packedverts; + packedverts.setSize(verts.size()); + + // fill it + for(U32 i=0; i &tang, Vector &binorm) +{ + const Point3F& va = verts[i0]; + const Point3F& vb = verts[i1]; + const Point3F& vc = verts[i2]; + const Point2F& uva = diffuseUVs[i0]; + const Point2F& uvb = diffuseUVs[i1]; + const Point2F& uvc = diffuseUVs[i2]; + + float x1 = vb.x - va.x; + float x2 = vc.x - va.x; + float y1 = vb.y - va.y; + float y2 = vc.y - va.y; + float z1 = vb.z - va.z; + float z2 = vc.z - va.z; + float s1 = uvb.x - uva.x; + float s2 = uvc.x - uva.x; + float t1 = uvb.y - uva.y; + float t2 = uvc.y - uva.y; + + F32 denom = (s1 * t2 - s2 * t1); + if(fabs(denom) < 0.0001) + return; + + float r = 1.0F / denom; + Point3F s((t2 * x1 - t1 * x2) * r, + (t2 * y1 - t1 * y2) * r, + (t2 * z1 - t1 * z2) * r); + Point3F t((s1 * x2 - s2 * x1) * r, + (s1 * y2 - s2 * y1) * r, + (s1 * z2 - s2 * z1) * r); + + tang[i0] += s; + tang[i1] += s; + tang[i2] += s; + binorm[i0] += t; + binorm[i1] += t; + binorm[i2] += t; +} + +void InteriorSimpleMesh::packPrimitive(primitive &primnew, const primitive &primold, Vector &indicesnew, + bool flipped, Vector &tang, Vector &binorm) +{ + // convert from strip to list and add to primnew + for(U32 p=2; pload(InteriorTexture, path, false); + materialList->mapMaterials(); + + // GFX2_RENDER_MERGE + materialList->initMatInstances( MATMGR->getDefaultFeatures(), getGFXVertexFormat() ); + + return true; +} diff --git a/Engine/source/interior/interiorSimpleMesh.h b/Engine/source/interior/interiorSimpleMesh.h new file mode 100644 index 000000000..762074d3c --- /dev/null +++ b/Engine/source/interior/interiorSimpleMesh.h @@ -0,0 +1,208 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _INTERIORSIMPLEMESH_H_ +#define _INTERIORSIMPLEMESH_H_ + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _MBOX_H_ +#include "math/mBox.h" +#endif +#ifndef _TSMATERIALLIST_H_ +#include "ts/tsMaterialList.h" +#endif +#ifndef _RENDERPASSMANAGER_H_ +#include "renderInstance/renderPassManager.h" +#endif +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif +#ifndef _GFXVERTEXBUFFER_H_ +#include "gfx/gfxVertexBuffer.h" +#endif + +class InteriorInstance; + + +class InteriorSimpleMesh +{ +public: + class primitive + { + public: + bool alpha; + U32 texS; + U32 texT; + S32 diffuseIndex; + S32 lightMapIndex; + U32 start; + U32 count; + + // used to relight the surface in-engine... + PlaneF lightMapEquationX; + PlaneF lightMapEquationY; + Point2I lightMapOffset; + Point2I lightMapSize; + + primitive() + { + alpha = false; + texS = GFXAddressWrap; + texT = GFXAddressWrap; + diffuseIndex = 0; + lightMapIndex = 0; + start = 0; + count = 0; + + lightMapEquationX = PlaneF(0, 0, 0, 0); + lightMapEquationY = PlaneF(0, 0, 0, 0); + lightMapOffset = Point2I(0, 0); + lightMapSize = Point2I(0, 0); + } + }; + + InteriorSimpleMesh() + { + VECTOR_SET_ASSOCIATION( packedIndices ); + VECTOR_SET_ASSOCIATION( packedPrimitives ); + VECTOR_SET_ASSOCIATION( indices ); + VECTOR_SET_ASSOCIATION( verts ); + VECTOR_SET_ASSOCIATION( norms ); + VECTOR_SET_ASSOCIATION( diffuseUVs ); + VECTOR_SET_ASSOCIATION( lightmapUVs ); + + materialList = NULL; + clear(); + } + ~InteriorSimpleMesh(){clear();} + void clear(bool wipeMaterials = true) + { + vertBuff = NULL; + primBuff = NULL; + packedIndices.clear(); + packedPrimitives.clear(); + + hasSolid = false; + hasTranslucency = false; + bounds = Box3F(-1, -1, -1, 1, 1, 1); + transform.identity(); + scale.set(1.0f, 1.0f, 1.0f); + + primitives.clear(); + indices.clear(); + verts.clear(); + norms.clear(); + diffuseUVs.clear(); + lightmapUVs.clear(); + + if(wipeMaterials && materialList) + delete materialList; + + if (wipeMaterials) + materialList = NULL; + } + + void render( SceneRenderState* state, + const MeshRenderInst ©inst, + U32 interiorlmhandle, + U32 instancelmhandle, + InteriorInstance* intInst ); + + void calculateBounds() + { + bounds = Box3F(F32_MAX, F32_MAX, F32_MAX, -F32_MAX, -F32_MAX, -F32_MAX); + for(U32 i=0; i packedIndices; + Vector packedPrimitives;/// tri-list instead of strips + GFXVertexBufferHandle vertBuff; + GFXPrimitiveBufferHandle primBuff; + void buildBuffers(); + void buildTangent(U32 i0, U32 i1, U32 i2, Vector &tang, Vector &binorm); + void packPrimitive(primitive &primnew, const primitive &primold, Vector &indicesnew, + bool flipped, Vector &tang, Vector &binorm); + bool prepForRendering(const char *path); + + bool hasSolid; + bool hasTranslucency; + Box3F bounds; + MatrixF transform; + Point3F scale; + + Vector primitives; + + // same index relationship... + Vector indices; + Vector verts; + Vector norms; + Vector diffuseUVs; + Vector lightmapUVs; + + TSMaterialList *materialList; + + bool containsPrimitiveType(bool translucent) + { + for(U32 i=0; i *renderInstList; +}; + +#endif //_INTERIORSIMPLEMESH_H_ + diff --git a/Engine/source/interior/interiorSubObject.cpp b/Engine/source/interior/interiorSubObject.cpp new file mode 100644 index 000000000..6cd003aea --- /dev/null +++ b/Engine/source/interior/interiorSubObject.cpp @@ -0,0 +1,108 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/stream/stream.h" +#include "interior/interiorInstance.h" + +#include "interior/interiorSubObject.h" +#include "interior/mirrorSubObject.h" + + +InteriorSubObject::InteriorSubObject() +{ + mInteriorInstance = NULL; +} + +InteriorSubObject::~InteriorSubObject() +{ + mInteriorInstance = NULL; +} + +InteriorSubObject* InteriorSubObject::readISO(Stream& stream) +{ + U32 soKey; + stream.read(&soKey); + + InteriorSubObject* pObject = NULL; + switch (soKey) { + case MirrorSubObjectKey: + pObject = new MirrorSubObject; + break; + + default: + Con::errorf(ConsoleLogEntry::General, "Bad key in subObject stream!"); + return NULL; + }; + + if (pObject) { + bool readSuccess = pObject->_readISO(stream); + if (readSuccess == false) { + delete pObject; + pObject = NULL; + } + } + + return pObject; +} + +bool InteriorSubObject::writeISO(Stream& stream) const +{ + stream.write(getSubObjectKey()); + return _writeISO(stream); +} + +bool InteriorSubObject::_readISO(Stream& stream) +{ + return (stream.getStatus() == Stream::Ok); +} + +bool InteriorSubObject::_writeISO(Stream& stream) const +{ + return (stream.getStatus() == Stream::Ok); +} + +const MatrixF& InteriorSubObject::getSOTransform() const +{ + static const MatrixF csBadMatrix(true); + + if (mInteriorInstance != NULL) { + return mInteriorInstance->getTransform(); + } else { + AssertWarn(false, "Returning bad transform for subobject"); + return csBadMatrix; + } +} + +const Point3F& InteriorSubObject::getSOScale() const +{ + return mInteriorInstance->getScale(); +} + +InteriorInstance* InteriorSubObject::getInstance() +{ + return mInteriorInstance; +} + +void InteriorSubObject::noteTransformChange() +{ + // +} diff --git a/Engine/source/interior/interiorSubObject.h b/Engine/source/interior/interiorSubObject.h new file mode 100644 index 000000000..51588150c --- /dev/null +++ b/Engine/source/interior/interiorSubObject.h @@ -0,0 +1,80 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _INTERIORSUBOBJECT_H_ +#define _INTERIORSUBOBJECT_H_ + +#ifndef _SCENERENDERSTATE_H_ +#include "scene/sceneRenderState.h" +#endif +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif + +class InteriorInstance; + +//class SubObjectRenderImage : public SceneRenderImage +//{ +// public: +// U32 mDetailLevel; +//}; + +class InteriorSubObject : public SceneObject +{ + typedef SceneObject Parent; + + protected: + InteriorInstance* mInteriorInstance; // Should NOT be set by derived except in clone + + protected: + enum SubObjectKeys { + TranslucentSubObjectKey = 0, + MirrorSubObjectKey = 1 + }; + + virtual U32 getSubObjectKey() const = 0; + virtual bool _readISO(Stream&); + virtual bool _writeISO(Stream&) const; + + InteriorInstance* getInstance(); + const MatrixF& getSOTransform() const; + const Point3F& getSOScale() const; + + public: + InteriorSubObject(); + virtual ~InteriorSubObject(); + + // Render control. A sub-object should return false from renderDetailDependant if + // it exists only at the level-0 detail level, ie, doors, elevators, etc., true + // if should only render at the interiors detail, ie, translucencies. + //virtual SubObjectRenderImage* getRenderImage(SceneState*, const Point3F& osPoint) = 0; + virtual bool renderDetailDependant() const = 0; + virtual U32 getZone() const = 0; + + virtual void noteTransformChange(); + virtual InteriorSubObject* clone(InteriorInstance*) const = 0; + + static InteriorSubObject* readISO(Stream&); + bool writeISO(Stream&) const; +}; + +#endif // _H_INTERIORSUBOBJECT_ diff --git a/Engine/source/interior/mirrorSubObject.cpp b/Engine/source/interior/mirrorSubObject.cpp new file mode 100644 index 000000000..f503fa93b --- /dev/null +++ b/Engine/source/interior/mirrorSubObject.cpp @@ -0,0 +1,286 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "interior/mirrorSubObject.h" +#include "interior/interiorInstance.h" +#include "interior/interior.h" +#include "materials/materialList.h" +#include "core/stream/stream.h" +#include "scene/sgUtil.h" + +IMPLEMENT_CONOBJECT(MirrorSubObject); + +ConsoleDocClass( MirrorSubObject, + "@deprecated Dysfunctional" + "@internal" +); + +//-------------------------------------------------------------------------- +MirrorSubObject::MirrorSubObject() +{ + mTypeMask = StaticObjectType; + + mInitialized = false; + mWhite = NULL; +} + +MirrorSubObject::~MirrorSubObject() +{ + delete mWhite; + mWhite = NULL; +} + +//-------------------------------------------------------------------------- +void MirrorSubObject::initPersistFields() +{ + Parent::initPersistFields(); + + // +} + +//-------------------------------------------------------------------------- +/* +void MirrorSubObject::renderObject(SceneRenderState* state, SceneRenderImage* image) +{ +} +*/ + +//-------------------------------------------------------------------------- +void MirrorSubObject::transformModelview(const U32 portalIndex, const MatrixF& oldMV, MatrixF* pNewMV) +{ + AssertFatal(isInitialized() == true, "Error, we should have been initialized by this point!"); + AssertFatal(portalIndex == 0, "Error, we only have one portal!"); + + *pNewMV = oldMV; + pNewMV->mul(mReflectionMatrix); +} + + +//-------------------------------------------------------------------------- +void MirrorSubObject::transformPosition(const U32 portalIndex, Point3F& ioPosition) +{ + AssertFatal(isInitialized() == true, "Error, we should have been initialized by this point!"); + AssertFatal(portalIndex == 0, "Error, we only have one portal!"); + + mReflectionMatrix.mulP(ioPosition); +} + + +//-------------------------------------------------------------------------- +bool MirrorSubObject::computeNewFrustum(const U32 portalIndex, + const Frustum &oldFrustum, + const F64 nearPlane, + const F64 farPlane, + const RectI& oldViewport, + F64 *newFrustum, + RectI& newViewport, + const bool flippedMatrix) +{ + AssertFatal(isInitialized() == true, "Error, we should have been initialized by this point!"); + AssertFatal(portalIndex == 0, "Error, mirrortests only have one portal!"); + + Interior* interior = getInstance()->getDetailLevel(mDetailLevel); + + static Vector mirrorWindings; + mirrorWindings.setSize(surfaceCount); + + for (U32 i = 0; i < surfaceCount; i++) { + SGWinding& rSGWinding = mirrorWindings[i]; + const Interior::Surface& rSurface = interior->mSurfaces[surfaceStart + i]; + + U32 fanIndices[32]; + U32 numFanIndices = 0; + interior->collisionFanFromSurface(rSurface, fanIndices, &numFanIndices); + + for (U32 j = 0; j < numFanIndices; j++) + rSGWinding.points[j] = interior->mPoints[fanIndices[j]].point; + rSGWinding.numPoints = numFanIndices; + } + + MatrixF finalModelView; + finalModelView.mul(getSOTransform()); + finalModelView.scale(getSOScale()); + + return sgComputeNewFrustum(oldFrustum, nearPlane, farPlane, + oldViewport, + mirrorWindings.address(), mirrorWindings.size(), + finalModelView, + newFrustum, newViewport, + flippedMatrix); +} + + +//-------------------------------------------------------------------------- +void MirrorSubObject::openPortal(const U32 portalIndex, + SceneRenderState* pCurrState, + SceneRenderState* pParentState) +{ + +} + + +//-------------------------------------------------------------------------- +void MirrorSubObject::closePortal(const U32 portalIndex, + SceneRenderState* pCurrState, + SceneRenderState* pParentState) +{ +} + + +//-------------------------------------------------------------------------- +void MirrorSubObject::getWSPortalPlane(const U32 portalIndex, PlaneF* pPlane) +{ + AssertFatal(portalIndex == 0, "Error, mirrortests only have one portal!"); + + Interior* interior = getInstance()->getDetailLevel(mDetailLevel); + const Interior::Surface& rSurface = interior->mSurfaces[surfaceStart]; + + PlaneF temp = interior->getPlane(rSurface.planeIndex); + if (Interior::planeIsFlipped(rSurface.planeIndex)) + temp.neg(); + + mTransformPlane(getSOTransform(), getSOScale(), temp, pPlane); +} + + +//-------------------------------------------------------------------------- +U32 MirrorSubObject::getSubObjectKey() const +{ + return InteriorSubObject::MirrorSubObjectKey; +} + + +bool MirrorSubObject::_readISO(Stream& stream) +{ + AssertFatal(isInitialized() == false, "Error, should not be initialized here!"); + + if (Parent::_readISO(stream) == false) + return false; + + stream.read(&mDetailLevel); + stream.read(&mZone); + stream.read(&mAlphaLevel); + stream.read(&surfaceCount); + stream.read(&surfaceStart); + + stream.read(&mCentroid.x); + stream.read(&mCentroid.y); + stream.read(&mCentroid.z); + + return true; +} + + +bool MirrorSubObject::_writeISO(Stream& stream) const +{ + if (Parent::_writeISO(stream) == false) + return false; + + stream.write(mDetailLevel); + stream.write(mZone); + stream.write(mAlphaLevel); + stream.write(surfaceCount); + stream.write(surfaceStart); + + stream.write(mCentroid.x); + stream.write(mCentroid.y); + stream.write(mCentroid.z); + + return true; +} + +bool MirrorSubObject::renderDetailDependant() const +{ + return true; +} + + +U32 MirrorSubObject::getZone() const +{ + return mZone; +} + + +void MirrorSubObject::setupTransforms() +{ + mInitialized = true; + + // This is really bad, but it's just about the only good place for this... + if (getInstance()->isClientObject() && mWhite == NULL) + mWhite = new GFXTexHandle("special/whiteAlpha0", &GFXDefaultStaticDiffuseProfile, avar("%s() - mWhite (line %d)", __FUNCTION__, __LINE__)); + + Interior* interior = getInstance()->getDetailLevel(mDetailLevel); + const Interior::Surface& rSurface = interior->mSurfaces[surfaceStart]; + + PlaneF plane = interior->getPlane(rSurface.planeIndex); + if (Interior::planeIsFlipped(rSurface.planeIndex)) + plane.neg(); + + Point3F n(plane.x, plane.y, plane.z); + Point3F q = n; + q *= -plane.d; + + MatrixF t(true); + t.scale(getSOScale()); + t.mul(getSOTransform()); + + t.mulV(n); + t.mulP(q); + + F32* ra = mReflectionMatrix; + + ra[0] = 1.0f - 2.0f*(n.x*n.x); ra[1] = 0.0f - 2.0f*(n.x*n.y); ra[2] = 0.0f - 2.0f*(n.x*n.z); ra[3] = 0.0f; + ra[4] = 0.0f - 2.0f*(n.y*n.x); ra[5] = 1.0f - 2.0f*(n.y*n.y); ra[6] = 0.0f - 2.0f*(n.y*n.z); ra[7] = 0.0f; + ra[8] = 0.0f - 2.0f*(n.z*n.x); ra[9] = 0.0f - 2.0f*(n.z*n.y); ra[10] = 1.0f - 2.0f*(n.z*n.z); ra[11] = 0.0f; + + Point3F qnn = n * mDot(n, q); + + ra[12] = qnn.x * 2.0f; + ra[13] = qnn.y * 2.0f; + ra[14] = qnn.z * 2.0f; + ra[15] = 1.0f; + + // Now, the GGems series (as of v1) uses row vectors (arg) + mReflectionMatrix.transpose(); +} + +void MirrorSubObject::noteTransformChange() +{ + setupTransforms(); + Parent::noteTransformChange(); +} + +InteriorSubObject* MirrorSubObject::clone(InteriorInstance* instance) const +{ + MirrorSubObject* pClone = new MirrorSubObject; + + pClone->mDetailLevel = mDetailLevel; + pClone->mZone = mZone; + pClone->mAlphaLevel = mAlphaLevel; + pClone->mCentroid = mCentroid; + pClone->surfaceCount = surfaceCount; + pClone->surfaceStart = surfaceStart; + + pClone->mInteriorInstance = instance; + + return pClone; +} diff --git a/Engine/source/interior/mirrorSubObject.h b/Engine/source/interior/mirrorSubObject.h new file mode 100644 index 000000000..59b345ee4 --- /dev/null +++ b/Engine/source/interior/mirrorSubObject.h @@ -0,0 +1,104 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MIRRORSUBOBJECT_H_ +#define _MIRRORSUBOBJECT_H_ + +#ifndef _INTERIORSUBOBJECT_H_ +#include "interior/interiorSubObject.h" +#endif + +#include "gfx/gfxTextureHandle.h" + +class TextureHandle; + +class MirrorSubObject : public InteriorSubObject +{ + typedef InteriorSubObject Parent; + + public: + U32 mDetailLevel; + U32 mZone; + + F32 mAlphaLevel; + Point3F mCentroid; + + U32 surfaceCount; + U32 surfaceStart; + + private: + bool mInitialized; + GFXTexHandle* mWhite; + MatrixF mReflectionMatrix; + + bool isInitialized() const { return mInitialized; } + void setupTransforms(); + + + // ISO overrides + protected: + U32 getSubObjectKey() const; + bool _readISO(Stream&); + bool _writeISO(Stream&) const; + + // Render control. A sub-object should return false from renderDetailDependant if + // it exists only at the level-0 detail level, ie, doors, elevators, etc., true + // if should only render at the interiors detail, ie, translucencies. + //SubObjectRenderImage* getRenderImage(SceneRenderState*, const Point3F&); + bool renderDetailDependant() const; + U32 getZone() const; + void noteTransformChange(); + + InteriorSubObject* clone(InteriorInstance*) const; + + // Rendering + protected: + //void renderObject(SceneRenderState*, SceneRenderImage*); + void transformModelview(const U32, const MatrixF&, MatrixF*); + void transformPosition(const U32, Point3F&); + bool computeNewFrustum( const U32 portalIndex, + const Frustum &oldFrustum, + const F64 nearPlane, + const F64 farPlane, + const RectI& oldViewport, + F64 *newFrustum, + RectI& newViewport, + const bool flippedMatrix ); + void openPortal(const U32 portalIndex, + SceneRenderState* pCurrState, + SceneRenderState* pParentState); + void closePortal(const U32 portalIndex, + SceneRenderState* pCurrState, + SceneRenderState* pParentState); + void getWSPortalPlane(const U32 portalIndex, PlaneF*); + + + public: + MirrorSubObject(); + ~MirrorSubObject(); + + DECLARE_CONOBJECT(MirrorSubObject); + static void initPersistFields(); +}; + +#endif // _H_MIRRORSUBOBJECT + diff --git a/Engine/source/interior/pathedInterior.cpp b/Engine/source/interior/pathedInterior.cpp new file mode 100644 index 000000000..081fd0c2f --- /dev/null +++ b/Engine/source/interior/pathedInterior.cpp @@ -0,0 +1,586 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "interior/pathedInterior.h" +#include "core/stream/stream.h" +#include "console/consoleTypes.h" +#include "scene/sceneRenderState.h" +#include "math/mathIO.h" +#include "core/stream/bitStream.h" +#include "interior/interior.h" +#include "scene/simPath.h" +#include "scene/pathManager.h" +#include "core/frameAllocator.h" +#include "scene/sceneManager.h" +#include "sfx/sfxSystem.h" +#include "sfx/sfxProfile.h" +#include "sfx/sfxSource.h" +#include "core/resourceManager.h" + +IMPLEMENT_CO_NETOBJECT_V1(PathedInterior); +IMPLEMENT_CO_DATABLOCK_V1(PathedInteriorData); + +ConsoleDocClass( PathedInterior, + "@brief Legacy interior related class, soon to be deprecated.\n\n" + "Not intended for game development, for editors or internal use only.\n\n " + "@internal"); + +ConsoleDocClass( PathedInteriorData, + "@brief Legacy interior related class, soon to be deprecated.\n\n" + "Not intended for game development, for editors or internal use only.\n\n " + "@internal"); + +//-------------------------------------------------------------------------- + +PathedInteriorData::PathedInteriorData() +{ + for(U32 i = 0; i < MaxSounds; i++) + sound[i] = NULL; +} + +void PathedInteriorData::initPersistFields() +{ + addField("StartSound", TYPEID< SFXProfile >(), Offset(sound[StartSound], PathedInteriorData)); + addField("SustainSound", TYPEID< SFXProfile >(), Offset(sound[SustainSound], PathedInteriorData)); + addField("StopSound", TYPEID< SFXProfile >(), Offset(sound[StopSound], PathedInteriorData)); + + Parent::initPersistFields(); +} + +void PathedInteriorData::packData(BitStream *stream) +{ + for (S32 i = 0; i < MaxSounds; i++) + { + if (stream->writeFlag(sound[i])) + stream->writeRangedU32(packed? SimObjectId(sound[i]): + sound[i]->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast); + } + Parent::packData(stream); +} + +void PathedInteriorData::unpackData(BitStream* stream) +{ + for (S32 i = 0; i < MaxSounds; i++) { + sound[i] = NULL; + if (stream->readFlag()) + sound[i] = (SFXProfile*)stream->readRangedU32(DataBlockObjectIdFirst, + DataBlockObjectIdLast); + } + Parent::unpackData(stream); +} + +bool PathedInteriorData::preload(bool server, String &errorStr) +{ + if(!Parent::preload(server, errorStr)) + return false; + + // Resolve objects transmitted from server + if (!server) + { + for (S32 i = 0; i < MaxSounds; i++) + if (sound[i]) + Sim::findObject(SimObjectId(sound[i]),sound[i]); + } + return true; +} + +PathedInterior::PathedInterior() +{ + mNetFlags.set(Ghostable); + mTypeMask = InteriorObjectType; + + mCurrentPosition = 0; + mTargetPosition = 0; + mPathKey = 0xFFFFFFFF; + mStopped = false; + + mSustainSound = NULL; +} + +PathedInterior::~PathedInterior() +{ + // +} + +PathedInterior *PathedInterior::mClientPathedInteriors = NULL; +//-------------------------------------------------------------------------- +void PathedInterior::initPersistFields() +{ + addField("interiorResource", TypeFilename, Offset(mInteriorResName, PathedInterior)); + addField("interiorIndex", TypeS32, Offset(mInteriorResIndex, PathedInterior)); + addField("basePosition", TypeMatrixPosition, Offset(mBaseTransform, PathedInterior)); + addField("baseRotation", TypeMatrixRotation, Offset(mBaseTransform, PathedInterior)); + addField("baseScale", TypePoint3F, Offset(mBaseScale, PathedInterior)); + + Parent::initPersistFields(); +} + + +//-------------------------------------------------------------------------- +bool PathedInterior::onAdd() +{ + if(!Parent::onAdd()) + return false; + + // Load the interior resource and extract the interior that is us. + mInteriorRes = ResourceManager::get().load(mInteriorResName); + if (bool(mInteriorRes) == false) + return false; + mInterior = mInteriorRes->getSubObject(mInteriorResIndex); + if (mInterior == NULL) + return false; + + // Setup bounding information + mObjBox = mInterior->getBoundingBox(); + resetWorldBox(); + + setScale(mBaseScale); + setTransform(mBaseTransform); + + if (isClientObject()) { + mNextClientPI = mClientPathedInteriors; + mClientPathedInteriors = this; + mInterior->prepForRendering(mInteriorRes.getPath().getFullPath().c_str()); +// gInteriorLMManager.addInstance(mInterior->getLMHandle(), mLMHandle, NULL, this); + } + + if(isClientObject()) + { + Point3F initialPos( 0.0, 0.0, 0.0 ); + mBaseTransform.getColumn(3, &initialPos); + Point3F pathPos( 0.0, 0.0, 0.0 ); + //gClientPathManager->getPathPosition(mPathKey, 0, pathPos); + mOffset = initialPos - pathPos; + //gClientPathManager->getPathPosition(mPathKey, mCurrentPosition, pathPos); + MatrixF mat = getTransform(); + mat.setColumn(3, pathPos + mOffset); + setTransform(mat); + } + + addToScene(); + + return true; +} + +bool PathedInterior::onNewDataBlock( GameBaseData *dptr, bool reload ) +{ + mDataBlock = dynamic_cast(dptr); + + if ( isClientObject() ) + { + SFX_DELETE( mSustainSound ); + + if ( mDataBlock->sound[PathedInteriorData::SustainSound] ) + mSustainSound = SFX->createSource( mDataBlock->sound[PathedInteriorData::SustainSound], &getTransform() ); + + if ( mSustainSound ) + mSustainSound->play(); + } + + return Parent::onNewDataBlock(dptr,reload); +} + +void PathedInterior::onRemove() +{ + if(isClientObject()) + { + SFX_DELETE( mSustainSound ); + + PathedInterior **walk = &mClientPathedInteriors; + while(*walk) + { + if(*walk == this) + { + *walk = mNextClientPI; + break; + } + walk = &((*walk)->mNextClientPI); + } +/* if(bool(mInteriorRes) && mLMHandle != 0xFFFFFFFF) + { + if (mInterior->getLMHandle() != 0xFFFFFFFF) + gInteriorLMManager.removeInstance(mInterior->getLMHandle(), mLMHandle); + }*/ + } + removeFromScene(); + Parent::onRemove(); +} + +//------------------------------------------------------------------------------ +bool PathedInterior::buildPolyList(AbstractPolyList* list, const Box3F& wsBox, const SphereF&) +{ + if (bool(mInteriorRes) == false) + return false; + + // Setup collision state data + list->setTransform(&getTransform(), getScale()); + list->setObject(this); + + return mInterior->buildPolyList(list, wsBox, mWorldToObj, getScale()); +} + + +//-------------------------------------------------------------------------- +void PathedInterior::prepRenderImage( SceneRenderState* state ) +{ + if (mPathKey == SimPath::Path::NoPathIndex) + return; + +/* + SceneRenderImage* image = new SceneRenderImage; + image->obj = this; + state->insertRenderImage(image); +*/ +} + +extern ColorF gInteriorFogColor; + +void PathedInterior::renderObject(SceneRenderState* state) +{ +} + +void PathedInterior::resolvePathKey() +{ + if(mPathKey == 0xFFFFFFFF && !isGhost()) + { + mPathKey = getPathKey(); + Point3F pathPos( 0.0, 0.0, 0.0 ); + Point3F initialPos( 0.0, 0.0, 0.0 ); + mBaseTransform.getColumn(3, &initialPos); + //gServerPathManager->getPathPosition(mPathKey, 0, pathPos); + mOffset = initialPos - pathPos; + } +} + + +//-------------------------------------------------------------------------- +U32 PathedInterior::packUpdate(NetConnection* con, U32 mask, BitStream* stream) +{ + U32 retMask = Parent::packUpdate(con, mask, stream); + resolvePathKey(); + + if (stream->writeFlag(mask & InitialUpdateMask)) + { + // Inital update... + stream->writeString(mInteriorResName); + stream->write(mInteriorResIndex); + + stream->writeAffineTransform(mBaseTransform); + mathWrite(*stream, mBaseScale); + + stream->write(mPathKey); + } + if(stream->writeFlag((mask & NewPositionMask) && mPathKey != SimPath::Path::NoPathIndex)) + stream->writeInt(S32(mCurrentPosition), gServerPathManager->getPathTimeBits(mPathKey)); + if(stream->writeFlag((mask & NewTargetMask) && mPathKey != SimPath::Path::NoPathIndex)) + { + if(stream->writeFlag(mTargetPosition < 0)) + { + stream->writeFlag(mTargetPosition == -1); + } + else + stream->writeInt(S32(mTargetPosition), gServerPathManager->getPathTimeBits(mPathKey)); + } + return retMask; +} + +void PathedInterior::unpackUpdate(NetConnection* con, BitStream* stream) +{ + Parent::unpackUpdate(con, stream); + + MatrixF tempXForm; + Point3F tempScale; + + if (stream->readFlag()) + { + // Initial + mInteriorResName = stream->readSTString(); + stream->read(&mInteriorResIndex); + + stream->readAffineTransform(&tempXForm); + mathRead(*stream, &tempScale); + mBaseTransform = tempXForm; + mBaseScale = tempScale; + + stream->read(&mPathKey); + } + if(stream->readFlag()) + { + Point3F pathPos(0.0f, 0.0f, 0.0f); + mCurrentPosition = stream->readInt(gClientPathManager->getPathTimeBits(mPathKey)); + if(isProperlyAdded()) + { + //gClientPathManager->getPathPosition(mPathKey, mCurrentPosition, pathPos); + MatrixF mat = getTransform(); + mat.setColumn(3, pathPos + mOffset); + setTransform(mat); + } + } + if(stream->readFlag()) + { + if(stream->readFlag()) + { + mTargetPosition = stream->readFlag() ? -1 : -2; + } + else + mTargetPosition = stream->readInt(gClientPathManager->getPathTimeBits(mPathKey)); + } +} + +void PathedInterior::processTick(const Move* move) +{ + if(isServerObject()) + { + S32 timeMs = 32; + if(mCurrentPosition != mTargetPosition) + { + S32 delta; + if(mTargetPosition == -1) + delta = timeMs; + else if(mTargetPosition == -2) + delta = -timeMs; + else + { + delta = mTargetPosition - (S32)mCurrentPosition; + if(delta < -timeMs) + delta = -timeMs; + else if(delta > timeMs) + delta = timeMs; + } + mCurrentPosition += delta; + U32 totalTime = gClientPathManager->getPathTotalTime(mPathKey); + while(mCurrentPosition > totalTime) + mCurrentPosition -= totalTime; + while(mCurrentPosition < 0) + mCurrentPosition += totalTime; + } + } +} + +void PathedInterior::computeNextPathStep(U32 timeDelta) +{ + S32 timeMs = timeDelta; + mStopped = false; + + if(mCurrentPosition == mTargetPosition) + { + mExtrudedBox = getWorldBox(); + mCurrentVelocity.set(0,0,0); + } + else + { + S32 delta = 0; + if(mTargetPosition < 0) + { + if(mTargetPosition == -1) + delta = timeMs; + else if(mTargetPosition == -2) + delta = -timeMs; + + mCurrentPosition += delta; + + U32 totalTime = gClientPathManager->getPathTotalTime(mPathKey); + + while(mCurrentPosition >= totalTime) + mCurrentPosition -= totalTime; + + while(mCurrentPosition < 0) + mCurrentPosition += totalTime; + } + else + { + delta = mTargetPosition - (S32)mCurrentPosition; + if(delta < -timeMs) + delta = -timeMs; + else if(delta > timeMs) + delta = timeMs; + mCurrentPosition += delta; + } + + Point3F curPoint; + Point3F newPoint( 0.0, 0.0, 0.0 ); + MatrixF mat = getTransform(); + mat.getColumn(3, &curPoint); + //gClientPathManager->getPathPosition(mPathKey, mCurrentPosition, newPoint); + newPoint += mOffset; + + Point3F displaceDelta = newPoint - curPoint; + mExtrudedBox = getWorldBox(); + + if(displaceDelta.x < 0) + mExtrudedBox.minExtents.x += displaceDelta.x; + else + mExtrudedBox.maxExtents.x += displaceDelta.x; + if(displaceDelta.y < 0) + mExtrudedBox.minExtents.y += displaceDelta.y; + else + mExtrudedBox.maxExtents.y += displaceDelta.y; + if(displaceDelta.z < 0) + mExtrudedBox.minExtents.z += displaceDelta.z; + else + mExtrudedBox.maxExtents.z += displaceDelta.z; + + mCurrentVelocity = displaceDelta * 1000 / F32(timeDelta); + } + Point3F pos; + mExtrudedBox.getCenter(&pos); + MatrixF mat = getTransform(); + mat.setColumn(3, pos); + + if ( mSustainSound ) + { + mSustainSound->setTransform( mat ); + mSustainSound->setVelocity( getVelocity() ); + } +} + +Point3F PathedInterior::getVelocity() +{ + return mCurrentVelocity; +} + +void PathedInterior::advance(F64 timeDelta) +{ + if(mStopped) + return; + + if(mCurrentVelocity.len() == 0) + { +// if(mSustainHandle) +// { +// alxStop(mSustainHandle); +// mSustainHandle = 0; +// } + return; + } + MatrixF mat = getTransform(); + Point3F newPoint; + mat.getColumn(3, &newPoint); + newPoint += mCurrentVelocity * timeDelta / 1000.0f; + //gClientPathManager->getPathPosition(mPathKey, mCurrentPosition, newPoint); + mat.setColumn(3, newPoint);// + mOffset); + setTransform(mat); + setRenderTransform(mat); +} + +U32 PathedInterior::getPathKey() +{ + AssertFatal(isServerObject(), "Error, must be a server object to call this..."); + + SimGroup* myGroup = getGroup(); + AssertFatal(myGroup != NULL, "No group for this object?"); + + for (SimGroup::iterator itr = myGroup->begin(); itr != myGroup->end(); itr++) { + SimPath::Path* pPath = dynamic_cast(*itr); + if (pPath != NULL) { + U32 pathKey = pPath->getPathIndex(); + AssertFatal(pathKey != SimPath::Path::NoPathIndex, "Error, path must have event over at this point..."); + return pathKey; + } + } + + return SimPath::Path::NoPathIndex; +} + +void PathedInterior::setPathPosition(S32 newPosition) +{ + resolvePathKey(); + if(newPosition < 0) + newPosition = 0; + if(newPosition > S32(gServerPathManager->getPathTotalTime(mPathKey))) + newPosition = S32(gServerPathManager->getPathTotalTime(mPathKey)); + mCurrentPosition = mTargetPosition = newPosition; + setMaskBits(NewPositionMask | NewTargetMask); +} + +void PathedInterior::setTargetPosition(S32 newPosition) +{ + resolvePathKey(); + if(newPosition < -2) + newPosition = 0; + if(newPosition > S32(gServerPathManager->getPathTotalTime(mPathKey))) + newPosition = gServerPathManager->getPathTotalTime(mPathKey); + if(mTargetPosition != newPosition) + { + mTargetPosition = newPosition; + setMaskBits(NewTargetMask); + } +} + +ConsoleMethod(PathedInterior, setPathPosition, void, 3, 3, "") +{ + ((PathedInterior *) object)->setPathPosition(dAtoi(argv[2])); +} + +ConsoleMethod(PathedInterior, setTargetPosition, void, 3, 3, "") +{ + ((PathedInterior *) object)->setTargetPosition(dAtoi(argv[2])); +} + + +//-------------------------------------------------------------------------- +bool PathedInterior::readPI(Stream& stream) +{ + mName = stream.readSTString(); + mInteriorResName = stream.readSTString(); + stream.read(&mInteriorResIndex); + stream.read(&mPathIndex); + mathRead(stream, &mOffset); + + U32 numTriggers; + stream.read(&numTriggers); + mTriggers.setSize(numTriggers); + for (S32 i = 0; i < mTriggers.size(); i++) + mTriggers[i] = stream.readSTString(); + + return (stream.getStatus() == Stream::Ok); +} + +bool PathedInterior::writePI(Stream& stream) const +{ + stream.writeString(mName); + stream.writeString(mInteriorResName); + stream.write(mInteriorResIndex); + stream.write(mPathIndex); + mathWrite(stream, mOffset); + + stream.write(mTriggers.size()); + for (S32 i = 0; i < mTriggers.size(); i++) + stream.writeString(mTriggers[i]); + + return (stream.getStatus() == Stream::Ok); +} + +PathedInterior* PathedInterior::clone() const +{ + PathedInterior* pClone = new PathedInterior; + + pClone->mName = mName; + pClone->mInteriorResName = mInteriorResName; + pClone->mInteriorResIndex = mInteriorResIndex; + pClone->mPathIndex = mPathIndex; + pClone->mOffset = mOffset; + + return pClone; +} + + diff --git a/Engine/source/interior/pathedInterior.h b/Engine/source/interior/pathedInterior.h new file mode 100644 index 000000000..1f8b3672c --- /dev/null +++ b/Engine/source/interior/pathedInterior.h @@ -0,0 +1,157 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _H_PATHEDINTERIOR +#define _H_PATHEDINTERIOR + +#ifndef _INTERIOR_H_ +#include "interior/interior.h" +#endif +#ifndef _GAMEBASE_H_ +#include "T3D/gameBase/gameBase.h" +#endif +#ifndef _INTERIORRES_H_ +#include "interior/interiorRes.h" +#endif +#ifndef __RESOURCE_H__ +#include "core/resource.h" +#endif + +class InteriorInstance; +class EditGeometry; +class EditInteriorResource; +class SFXProfile; +class SFXSource; + +struct PathedInteriorData : public GameBaseData { + typedef GameBaseData Parent; +public: + enum Sounds { + StartSound, + SustainSound, + StopSound, + MaxSounds + }; + SFXProfile *sound[MaxSounds]; + static void initPersistFields(); + virtual void packData(BitStream* stream); + virtual void unpackData(BitStream* stream); + bool preload(bool server, String &errorStr); + PathedInteriorData(); + + DECLARE_CONOBJECT(PathedInteriorData); +}; + +class PathedInterior : public GameBase +{ + typedef GameBase Parent; + friend class InteriorInstance; + friend class EditGeometry; + friend class EditInteriorResource; + + PathedInteriorData *mDataBlock; + +public: + enum UpdateMasks { + NewTargetMask = Parent::NextFreeMask, + NewPositionMask = Parent::NextFreeMask << 1, + NextFreeMask = Parent::NextFreeMask << 2, + }; +private: + + U32 getPathKey(); // only used on the server + + // Persist fields + protected: + StringTableEntry mName; + S32 mPathIndex; + Vector mTriggers; + Point3F mOffset; + Box3F mExtrudedBox; + bool mStopped; + + // Loaded resources and fields + protected: + static PathedInterior *mClientPathedInteriors; + + SFXSource* mSustainSound; + + StringTableEntry mInteriorResName; + S32 mInteriorResIndex; + Resource mInteriorRes; + Interior* mInterior; + Vector mVertexColorsNormal; + Vector mVertexColorsAlarm; + + MatrixF mBaseTransform; + Point3F mBaseScale; + + U32 mPathKey; // only used on the client + F64 mCurrentPosition; + S32 mTargetPosition; + Point3F mCurrentVelocity; + + PathedInterior *mNextClientPI; + + // Rendering + protected: + void prepRenderImage( SceneRenderState *state ); + void renderObject( SceneRenderState *state ); + void renderShadowVolumes( SceneRenderState *state ); + + protected: + bool onAdd(); + void onRemove(); + + public: + PathedInterior(); + ~PathedInterior(); + + PathedInterior *getNext() { return mNextClientPI; } + + static PathedInterior *getClientPathedInteriors() { return mClientPathedInteriors; } + + void processTick(const Move* move); + void setStopped() { mStopped = true; } + void resolvePathKey(); + + bool onNewDataBlock( GameBaseData *dptr, bool reload ); + bool buildPolyList(AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere); + bool readPI(Stream&); + bool writePI(Stream&) const; + PathedInterior* clone() const; + + DECLARE_CONOBJECT(PathedInterior); + static void initPersistFields(); + void setPathPosition(S32 newPosition); + void setTargetPosition(S32 targetPosition); + void computeNextPathStep(U32 timeDelta); + Box3F getExtrudedBox() { return mExtrudedBox; } + Point3F getVelocity(); + void advance(F64 timeDelta); + + U32 packUpdate(NetConnection *conn, U32 mask, BitStream* stream); + void unpackUpdate(NetConnection *conn, BitStream* stream); +}; + +#endif // _H_PATHEDINTERIOR + diff --git a/Engine/source/lighting/advanced/advancedLightBinManager.cpp b/Engine/source/lighting/advanced/advancedLightBinManager.cpp new file mode 100644 index 000000000..1aeef56ff --- /dev/null +++ b/Engine/source/lighting/advanced/advancedLightBinManager.cpp @@ -0,0 +1,855 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/advanced/advancedLightBinManager.h" + +#include "lighting/advanced/advancedLightManager.h" +#include "lighting/advanced/advancedLightBufferConditioner.h" +#include "lighting/shadowMap/shadowMapManager.h" +#include "lighting/shadowMap/shadowMapPass.h" +#include "lighting/shadowMap/lightShadowMap.h" +#include "lighting/common/lightMapParams.h" +#include "renderInstance/renderPrePassMgr.h" +#include "gfx/gfxTransformSaver.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "materials/materialManager.h" +#include "materials/sceneData.h" +#include "core/util/safeDelete.h" +#include "core/util/rgb2luv.h" +#include "gfx/gfxDebugEvent.h" +#include "math/util/matrixSet.h" +#include "console/consoleTypes.h" + + +const RenderInstType AdvancedLightBinManager::RIT_LightInfo( "LightInfo" ); +const String AdvancedLightBinManager::smBufferName( "lightinfo" ); + +ShadowFilterMode AdvancedLightBinManager::smShadowFilterMode = ShadowFilterMode_SoftShadowHighQuality; +bool AdvancedLightBinManager::smPSSMDebugRender = false; +bool AdvancedLightBinManager::smUseSSAOMask = false; + + +ImplementEnumType( ShadowFilterMode, + "The shadow filtering modes for Advanced Lighting shadows.\n" + "@ingroup AdvancedLighting" ) + + { ShadowFilterMode_None, "None", + "@brief Simple point sampled filtering.\n" + "This is the fastest and lowest quality mode." }, + + { ShadowFilterMode_SoftShadow, "SoftShadow", + "@brief A variable tap rotated poisson disk soft shadow filter.\n" + "It performs 4 taps to classify the point as in shadow, out of shadow, or along a " + "shadow edge. Samples on the edge get an additional 8 taps to soften them." }, + + { ShadowFilterMode_SoftShadowHighQuality, "SoftShadowHighQuality", + "@brief A 12 tap rotated poisson disk soft shadow filter.\n" + "It performs all the taps for every point without any early rejection." }, + +EndImplementEnumType; + +// NOTE: The order here matches that of the LightInfo::Type enum. +const String AdvancedLightBinManager::smLightMatNames[] = +{ + "AL_PointLightMaterial", // LightInfo::Point + "AL_SpotLightMaterial", // LightInfo::Spot + "AL_VectorLightMaterial", // LightInfo::Vector + "", // LightInfo::Ambient +}; + +// NOTE: The order here matches that of the LightInfo::Type enum. +const GFXVertexFormat* AdvancedLightBinManager::smLightMatVertex[] = +{ + getGFXVertexFormat(), // LightInfo::Point + getGFXVertexFormat(), // LightInfo::Spot + getGFXVertexFormat(), // LightInfo::Vector + NULL, // LightInfo::Ambient +}; + +// NOTE: The order here matches that of the ShadowType enum. +const String AdvancedLightBinManager::smShadowTypeMacro[] = +{ + "", // ShadowType_Spot + "", // ShadowType_PSSM, + "SHADOW_PARABOLOID", // ShadowType_Paraboloid, + "SHADOW_DUALPARABOLOID_SINGLE_PASS", // ShadowType_DualParaboloidSinglePass, + "SHADOW_DUALPARABOLOID", // ShadowType_DualParaboloid, + "SHADOW_CUBE", // ShadowType_CubeMap, +}; + +AdvancedLightBinManager::RenderSignal &AdvancedLightBinManager::getRenderSignal() +{ + static RenderSignal theSignal; + return theSignal; +} + +IMPLEMENT_CONOBJECT(AdvancedLightBinManager); + +ConsoleDocClass( AdvancedLightBinManager, + "@brief Rendering Manager responsible for lighting, shadows, and global variables affecing both.\n\n" + + "Should not be exposed to TorqueScript as a game object, meant for internal use only\n\n" + + "@ingroup Lighting" +); + +AdvancedLightBinManager::AdvancedLightBinManager( AdvancedLightManager *lm /* = NULL */, + ShadowMapManager *sm /* = NULL */, + GFXFormat lightBufferFormat /* = GFXFormatR8G8B8A8 */ ) + : RenderTexTargetBinManager( RIT_LightInfo, 1.0f, 1.0f, lightBufferFormat ), + mNumLightsCulled(0), + mLightManager(lm), + mShadowManager(sm), + mConditioner(NULL) +{ + // Create an RGB conditioner + mConditioner = new AdvancedLightBufferConditioner( getTargetFormat(), + AdvancedLightBufferConditioner::RGB ); + mNamedTarget.setConditioner( mConditioner ); + mNamedTarget.registerWithName( smBufferName ); + + // We want a full-resolution buffer + mTargetSizeType = RenderTexTargetBinManager::WindowSize; + + mMRTLightmapsDuringPrePass = false; + + Con::NotifyDelegate callback( this, &AdvancedLightBinManager::_deleteLightMaterials ); + Con::addVariableNotify( "$pref::Shadows::filterMode", callback ); + Con::addVariableNotify( "$AL::PSSMDebugRender", callback ); + Con::addVariableNotify( "$AL::UseSSAOMask", callback ); +} + + +AdvancedLightBinManager::~AdvancedLightBinManager() +{ + _deleteLightMaterials(); + + SAFE_DELETE(mConditioner); + + Con::NotifyDelegate callback( this, &AdvancedLightBinManager::_deleteLightMaterials ); + Con::removeVariableNotify( "$pref::shadows::filterMode", callback ); + Con::removeVariableNotify( "$AL::PSSMDebugRender", callback ); + Con::removeVariableNotify( "$AL::UseSSAOMask", callback ); +} + +void AdvancedLightBinManager::consoleInit() +{ + Parent::consoleInit(); + + Con::addVariable( "$pref::shadows::filterMode", + TYPEID(), &smShadowFilterMode, + "The filter mode to use for shadows.\n" + "@ingroup AdvancedLighting\n" ); + + Con::addVariable( "$AL::UseSSAOMask", TypeBool, &smUseSSAOMask, + "Used by the SSAO PostEffect to toggle the sampling of ssaomask " + "texture by the light shaders.\n" + "@ingroup AdvancedLighting\n" ); + + Con::addVariable( "$AL::PSSMDebugRender", TypeBool, &smPSSMDebugRender, + "Enables debug rendering of the PSSM shadows.\n" + "@ingroup AdvancedLighting\n" ); +} + +bool AdvancedLightBinManager::setTargetSize(const Point2I &newTargetSize) +{ + bool ret = Parent::setTargetSize( newTargetSize ); + + // We require the viewport to match the default. + mNamedTarget.setViewport( GFX->getViewport() ); + + return ret; +} + +void AdvancedLightBinManager::addLight( LightInfo *light ) +{ + // Get the light type. + const LightInfo::Type lightType = light->getType(); + + AssertFatal( lightType == LightInfo::Point || + lightType == LightInfo::Spot, "Bogus light type." ); + + // Find a shadow map for this light, if it has one + ShadowMapParams *lsp = light->getExtended(); + LightShadowMap *lsm = lsp->getShadowMap(); + + // Get the right shadow type. + ShadowType shadowType = ShadowType_None; + if ( light->getCastShadows() && + lsm && lsm->hasShadowTex() && + !ShadowMapPass::smDisableShadows ) + shadowType = lsm->getShadowType(); + + // Add the entry + LightBinEntry lEntry; + lEntry.lightInfo = light; + lEntry.shadowMap = lsm; + lEntry.lightMaterial = _getLightMaterial( lightType, shadowType, lsp->hasCookieTex() ); + + if( lightType == LightInfo::Spot ) + lEntry.vertBuffer = mLightManager->getConeMesh( lEntry.numPrims, lEntry.primBuffer ); + else + lEntry.vertBuffer = mLightManager->getSphereMesh( lEntry.numPrims, lEntry.primBuffer ); + + // If it's a point light, push front, spot + // light, push back. This helps batches. + Vector &curBin = mLightBin; + if ( light->getType() == LightInfo::Point ) + curBin.push_front( lEntry ); + else + curBin.push_back( lEntry ); +} + +void AdvancedLightBinManager::clear() +{ + Con::setIntVariable("lightMetrics::activeLights", mLightBin.size()); + Con::setIntVariable("lightMetrics::culledLights", mNumLightsCulled); + + mLightBin.clear(); + mNumLightsCulled = 0; +} + +void AdvancedLightBinManager::render( SceneRenderState *state ) +{ + PROFILE_SCOPE( AdvancedLightManager_Render ); + + // Take a look at the SceneRenderState and see if we should skip drawing the pre-pass + if( state->disableAdvancedLightingBins() ) + return; + + // Automagically save & restore our viewport and transforms. + GFXTransformSaver saver; + + if( !mLightManager ) + return; + + // Get the sunlight. If there's no sun, and no lights in the bins, no draw + LightInfo *sunLight = mLightManager->getSpecialLight( LightManager::slSunLightType ); + if( !sunLight && mLightBin.empty() ) + return; + + GFXDEBUGEVENT_SCOPE( AdvancedLightBinManager_Render, ColorI::RED ); + + // Tell the superclass we're about to render + if ( !_onPreRender( state ) ) + return; + + // Clear as long as there isn't MRT population of light buffer with lightmap data + if ( !MRTLightmapsDuringPrePass() ) + GFX->clear(GFXClearTarget, ColorI(0, 0, 0, 0), 1.0f, 0); + + // Restore transforms + MatrixSet &matrixSet = getRenderPass()->getMatrixSet(); + matrixSet.restoreSceneViewProjection(); + + const MatrixF &worldToCameraXfm = matrixSet.getWorldToCamera(); + + // Set up the SG Data + SceneData sgData; + sgData.init( state ); + + // There are cases where shadow rendering is disabled. + const bool disableShadows = state->isReflectPass() || ShadowMapPass::smDisableShadows; + + // Pick the right material for rendering the sunlight... we only + // cast shadows when its enabled and we're not in a reflection. + LightMaterialInfo *vectorMatInfo; + if ( sunLight && + sunLight->getCastShadows() && + !disableShadows && + sunLight->getExtended() ) + vectorMatInfo = _getLightMaterial( LightInfo::Vector, ShadowType_PSSM, false ); + else + vectorMatInfo = _getLightMaterial( LightInfo::Vector, ShadowType_None, false ); + + // Initialize and set the per-frame parameters after getting + // the vector light material as we use lazy creation. + _setupPerFrameParameters( state ); + + // Draw sunlight/ambient + if ( sunLight && vectorMatInfo ) + { + GFXDEBUGEVENT_SCOPE( AdvancedLightBinManager_Render_Sunlight, ColorI::RED ); + + // Set up SG data + setupSGData( sgData, state, sunLight ); + vectorMatInfo->setLightParameters( sunLight, state, worldToCameraXfm ); + + // Set light holds the active shadow map. + mShadowManager->setLightShadowMapForLight( sunLight ); + + // Set geometry + GFX->setVertexBuffer( mFarFrustumQuadVerts ); + GFX->setPrimitiveBuffer( NULL ); + + // Render the material passes + while( vectorMatInfo->matInstance->setupPass( state, sgData ) ) + { + vectorMatInfo->matInstance->setSceneInfo( state, sgData ); + vectorMatInfo->matInstance->setTransforms( matrixSet, state ); + GFX->drawPrimitive( GFXTriangleFan, 0, 2 ); + } + } + + // Blend the lights in the bin to the light buffer + for( LightBinIterator itr = mLightBin.begin(); itr != mLightBin.end(); itr++ ) + { + LightBinEntry& curEntry = *itr; + LightInfo *curLightInfo = curEntry.lightInfo; + LightMaterialInfo *curLightMat = curEntry.lightMaterial; + const U32 numPrims = curEntry.numPrims; + const U32 numVerts = curEntry.vertBuffer->mNumVerts; + + // Skip lights which won't affect the scene. + if ( !curLightMat || curLightInfo->getBrightness() <= 0.001f ) + continue; + + GFXDEBUGEVENT_SCOPE( AdvancedLightBinManager_Render_Light, ColorI::RED ); + + setupSGData( sgData, state, curLightInfo ); + curLightMat->setLightParameters( curLightInfo, state, worldToCameraXfm ); + mShadowManager->setLightShadowMap( curEntry.shadowMap ); + + // Let the shadow know we're about to render from it. + if ( curEntry.shadowMap ) + curEntry.shadowMap->preLightRender(); + + // Set geometry + GFX->setVertexBuffer( curEntry.vertBuffer ); + GFX->setPrimitiveBuffer( curEntry.primBuffer ); + + // Render the material passes + while( curLightMat->matInstance->setupPass( state, sgData ) ) + { + // Set transforms + matrixSet.setWorld(*sgData.objTrans); + curLightMat->matInstance->setTransforms(matrixSet, state); + curLightMat->matInstance->setSceneInfo(state, sgData); + + if(curEntry.primBuffer) + GFX->drawIndexedPrimitive(GFXTriangleList, 0, 0, numVerts, 0, numPrims); + else + GFX->drawPrimitive(GFXTriangleList, 0, numPrims); + } + + // Tell it we're done rendering. + if ( curEntry.shadowMap ) + curEntry.shadowMap->postLightRender(); + } + + // Set NULL for active shadow map (so nothing gets confused) + mShadowManager->setLightShadowMap(NULL); + GFX->setVertexBuffer( NULL ); + GFX->setPrimitiveBuffer( NULL ); + + // Fire off a signal to let others know that light-bin rendering is ending now + getRenderSignal().trigger(state, this); + + // Finish up the rendering + _onPostRender(); +} + +AdvancedLightBinManager::LightMaterialInfo* AdvancedLightBinManager::_getLightMaterial( LightInfo::Type lightType, + ShadowType shadowType, + bool useCookieTex ) +{ + PROFILE_SCOPE( AdvancedLightBinManager_GetLightMaterial ); + + // Build the key. + const LightMatKey key( lightType, shadowType, useCookieTex ); + + // See if we've already built this one. + LightMatTable::Iterator iter = mLightMaterials.find( key ); + if ( iter != mLightMaterials.end() ) + return iter->value; + + // If we got here we need to build a material for + // this light+shadow combination. + + LightMaterialInfo *info = NULL; + + // First get the light material name and make sure + // this light has a material in the first place. + const String &lightMatName = smLightMatNames[ lightType ]; + if ( lightMatName.isNotEmpty() ) + { + Vector shadowMacros; + + // Setup the shadow type macros for this material. + if ( shadowType == ShadowType_None ) + shadowMacros.push_back( GFXShaderMacro( "NO_SHADOW" ) ); + else + { + shadowMacros.push_back( GFXShaderMacro( smShadowTypeMacro[ shadowType ] ) ); + + // Do we need to do shadow filtering? + if ( smShadowFilterMode != ShadowFilterMode_None ) + { + shadowMacros.push_back( GFXShaderMacro( "SOFTSHADOW" ) ); + + const F32 SM = GFX->getPixelShaderVersion(); + if ( SM >= 3.0f && smShadowFilterMode == ShadowFilterMode_SoftShadowHighQuality ) + shadowMacros.push_back( GFXShaderMacro( "SOFTSHADOW_HIGH_QUALITY" ) ); + } + } + + if ( useCookieTex ) + shadowMacros.push_back( GFXShaderMacro( "USE_COOKIE_TEX" ) ); + + // Its safe to add the PSSM debug macro to all the materials. + if ( smPSSMDebugRender ) + shadowMacros.push_back( GFXShaderMacro( "PSSM_DEBUG_RENDER" ) ); + + // If its a vector light see if we can enable SSAO. + if ( lightType == LightInfo::Vector && smUseSSAOMask ) + shadowMacros.push_back( GFXShaderMacro( "USE_SSAO_MASK" ) ); + + // Now create the material info object. + info = new LightMaterialInfo( lightMatName, smLightMatVertex[ lightType ], shadowMacros ); + } + + // Push this into the map and return it. + mLightMaterials.insertUnique( key, info ); + return info; +} + +void AdvancedLightBinManager::_deleteLightMaterials() +{ + LightMatTable::Iterator iter = mLightMaterials.begin(); + for ( ; iter != mLightMaterials.end(); iter++ ) + delete iter->value; + + mLightMaterials.clear(); +} + +void AdvancedLightBinManager::_setupPerFrameParameters( const SceneRenderState *state ) +{ + PROFILE_SCOPE( AdvancedLightBinManager_SetupPerFrameParameters ); + const Frustum &frustum = state->getFrustum(); + + MatrixF invCam( frustum.getTransform() ); + invCam.inverse(); + + const Point3F *wsFrustumPoints = frustum.getPoints(); + const Point3F& cameraPos = frustum.getPosition(); + + // Now build the quad for drawing full-screen vector light + // passes.... this is a volatile VB and updates every frame. + FarFrustumQuadVert verts[4]; + { + verts[0].point.set( wsFrustumPoints[Frustum::FarBottomLeft] - cameraPos ); + invCam.mulP( wsFrustumPoints[Frustum::FarBottomLeft], &verts[0].normal ); + verts[0].texCoord.set( -1.0, -1.0 ); + + verts[1].point.set( wsFrustumPoints[Frustum::FarTopLeft] - cameraPos ); + invCam.mulP( wsFrustumPoints[Frustum::FarTopLeft], &verts[1].normal ); + verts[1].texCoord.set( -1.0, 1.0 ); + + verts[2].point.set( wsFrustumPoints[Frustum::FarTopRight] - cameraPos ); + invCam.mulP( wsFrustumPoints[Frustum::FarTopRight], &verts[2].normal ); + verts[2].texCoord.set( 1.0, 1.0 ); + + verts[3].point.set( wsFrustumPoints[Frustum::FarBottomRight] - cameraPos ); + invCam.mulP( wsFrustumPoints[Frustum::FarBottomRight], &verts[3].normal ); + verts[3].texCoord.set( 1.0, -1.0 ); + } + mFarFrustumQuadVerts.set( GFX, 4 ); + dMemcpy( mFarFrustumQuadVerts.lock(), verts, sizeof( verts ) ); + mFarFrustumQuadVerts.unlock(); + + PlaneF farPlane(wsFrustumPoints[Frustum::FarBottomLeft], wsFrustumPoints[Frustum::FarTopLeft], wsFrustumPoints[Frustum::FarTopRight]); + PlaneF vsFarPlane(verts[0].normal, verts[1].normal, verts[2].normal); + + // Parameters calculated, assign them to the materials + LightMatTable::Iterator iter = mLightMaterials.begin(); + for ( ; iter != mLightMaterials.end(); iter++ ) + { + if ( iter->value ) + iter->value->setViewParameters( frustum.getNearDist(), + frustum.getFarDist(), + frustum.getPosition(), + farPlane, + vsFarPlane); + } +} + +void AdvancedLightBinManager::setupSGData( SceneData &data, const SceneRenderState* state, LightInfo *light ) +{ + PROFILE_SCOPE( AdvancedLightBinManager_setupSGData ); + + data.lights[0] = light; + data.ambientLightColor = state->getAmbientLightColor(); + data.objTrans = &MatrixF::Identity; + + if ( light ) + { + if ( light->getType() == LightInfo::Point ) + { + // The point light volume gets some flat spots along + // the perimiter mostly visible in the constant and + // quadradic falloff modes. + // + // To account for them slightly increase the scale + // instead of greatly increasing the polycount. + + mLightMat = light->getTransform(); + mLightMat.scale( light->getRange() * 1.01f ); + data.objTrans = &mLightMat; + } + else if ( light->getType() == LightInfo::Spot ) + { + mLightMat = light->getTransform(); + + // Rotate it to face down the -y axis. + MatrixF scaleRotateTranslate( EulerF( M_PI_F / -2.0f, 0.0f, 0.0f ) ); + + // Calculate the radius based on the range and angle. + F32 range = light->getRange().x; + F32 radius = range * mSin( mDegToRad( light->getOuterConeAngle() ) * 0.5f ); + + // NOTE: This fudge makes the cone a little bigger + // to remove the facet egde of the cone geometry. + radius *= 1.1f; + + // Use the scale to distort the cone to + // match our radius and range. + scaleRotateTranslate.scale( Point3F( radius, radius, range ) ); + + // Apply the transform and set the position. + mLightMat *= scaleRotateTranslate; + mLightMat.setPosition( light->getPosition() ); + + data.objTrans = &mLightMat; + } + } +} + +void AdvancedLightBinManager::MRTLightmapsDuringPrePass( bool val ) +{ + // Do not enable if the GFX device can't do MRT's + if ( GFX->getNumRenderTargets() < 2 ) + val = false; + + if ( mMRTLightmapsDuringPrePass != val ) + { + mMRTLightmapsDuringPrePass = val; + + // Reload materials to cause a feature recalculation on prepass materials + if(mLightManager->isActive()) + MATMGR->flushAndReInitInstances(); + + RenderPrePassMgr *prepass; + if ( Sim::findObject( "AL_PrePassBin", prepass ) && prepass->getTargetTexture( 0 ) ) + prepass->updateTargets(); + } +} + +AdvancedLightBinManager::LightMaterialInfo::LightMaterialInfo( const String &matName, + const GFXVertexFormat *vertexFormat, + const Vector ¯os ) +: matInstance(NULL), + zNearFarInvNearFar(NULL), + farPlane(NULL), + vsFarPlane(NULL), + negFarPlaneDotEye(NULL), + lightPosition(NULL), + lightDirection(NULL), + lightColor(NULL), + lightAttenuation(NULL), + lightRange(NULL), + lightAmbient(NULL), + lightTrilight(NULL), + lightSpotParams(NULL) +{ + Material *mat = MATMGR->getMaterialDefinitionByName( matName ); + if ( !mat ) + return; + + matInstance = new LightMatInstance( *mat ); + + for ( U32 i=0; i < macros.size(); i++ ) + matInstance->addShaderMacro( macros[i].name, macros[i].value ); + + matInstance->init( MATMGR->getDefaultFeatures(), vertexFormat ); + + lightDirection = matInstance->getMaterialParameterHandle("$lightDirection"); + lightAmbient = matInstance->getMaterialParameterHandle("$lightAmbient"); + lightTrilight = matInstance->getMaterialParameterHandle("$lightTrilight"); + lightSpotParams = matInstance->getMaterialParameterHandle("$lightSpotParams"); + lightAttenuation = matInstance->getMaterialParameterHandle("$lightAttenuation"); + lightRange = matInstance->getMaterialParameterHandle("$lightRange"); + lightPosition = matInstance->getMaterialParameterHandle("$lightPosition"); + farPlane = matInstance->getMaterialParameterHandle("$farPlane"); + vsFarPlane = matInstance->getMaterialParameterHandle("$vsFarPlane"); + negFarPlaneDotEye = matInstance->getMaterialParameterHandle("$negFarPlaneDotEye"); + zNearFarInvNearFar = matInstance->getMaterialParameterHandle("$zNearFarInvNearFar"); + lightColor = matInstance->getMaterialParameterHandle("$lightColor"); + lightBrightness = matInstance->getMaterialParameterHandle("$lightBrightness"); +} + +AdvancedLightBinManager::LightMaterialInfo::~LightMaterialInfo() +{ + SAFE_DELETE(matInstance); +} + +void AdvancedLightBinManager::LightMaterialInfo::setViewParameters( const F32 _zNear, + const F32 _zFar, + const Point3F &_eyePos, + const PlaneF &_farPlane, + const PlaneF &_vsFarPlane) +{ + MaterialParameters *matParams = matInstance->getMaterialParameters(); + + matParams->setSafe( farPlane, *((const Point4F *)&_farPlane) ); + + matParams->setSafe( vsFarPlane, *((const Point4F *)&_vsFarPlane) ); + + if ( negFarPlaneDotEye->isValid() ) + { + // -dot( farPlane, eyePos ) + const F32 negFarPlaneDotEyeVal = -( mDot( *((const Point3F *)&_farPlane), _eyePos ) + _farPlane.d ); + matParams->set( negFarPlaneDotEye, negFarPlaneDotEyeVal ); + } + + matParams->setSafe( zNearFarInvNearFar, Point4F( _zNear, _zFar, 1.0f / _zNear, 1.0f / _zFar ) ); +} + +void AdvancedLightBinManager::LightMaterialInfo::setLightParameters( const LightInfo *lightInfo, const SceneRenderState* renderState, const MatrixF &worldViewOnly ) +{ + MaterialParameters *matParams = matInstance->getMaterialParameters(); + + // Set color in the right format, set alpha to the luminance value for the color. + ColorF col = lightInfo->getColor(); + + // TODO: The specularity control of the light + // is being scaled by the overall lumiance. + // + // Not sure if this may be the source of our + // bad specularity results maybe? + // + + const Point3F colorToLumiance( 0.3576f, 0.7152f, 0.1192f ); + F32 lumiance = mDot(*((const Point3F *)&lightInfo->getColor()), colorToLumiance ); + col.alpha *= lumiance; + + matParams->setSafe( lightColor, col ); + matParams->setSafe( lightBrightness, lightInfo->getBrightness() ); + + switch( lightInfo->getType() ) + { + case LightInfo::Vector: + { + VectorF lightDir = lightInfo->getDirection(); + worldViewOnly.mulV(lightDir); + lightDir.normalize(); + matParams->setSafe( lightDirection, lightDir ); + + // Set small number for alpha since it represents existing specular in + // the vector light. This prevents a divide by zero. + ColorF ambientColor = renderState->getAmbientLightColor(); + ambientColor.alpha = 0.00001f; + matParams->setSafe( lightAmbient, ambientColor ); + + // If no alt color is specified, set it to the average of + // the ambient and main color to avoid artifacts. + // + // TODO: Trilight disabled until we properly implement it + // in the light info! + // + //ColorF lightAlt = lightInfo->getAltColor(); + ColorF lightAlt( ColorF::BLACK ); // = lightInfo->getAltColor(); + if ( lightAlt.red == 0.0f && lightAlt.green == 0.0f && lightAlt.blue == 0.0f ) + lightAlt = (lightInfo->getColor() + renderState->getAmbientLightColor()) / 2.0f; + + ColorF trilightColor = lightAlt; + matParams->setSafe(lightTrilight, trilightColor); + } + break; + + case LightInfo::Spot: + { + const F32 outerCone = lightInfo->getOuterConeAngle(); + const F32 innerCone = getMin( lightInfo->getInnerConeAngle(), outerCone ); + const F32 outerCos = mCos( mDegToRad( outerCone / 2.0f ) ); + const F32 innerCos = mCos( mDegToRad( innerCone / 2.0f ) ); + Point4F spotParams( outerCos, + innerCos - outerCos, + mCos( mDegToRad( outerCone ) ), + 0.0f ); + + matParams->setSafe( lightSpotParams, spotParams ); + + VectorF lightDir = lightInfo->getDirection(); + worldViewOnly.mulV(lightDir); + lightDir.normalize(); + matParams->setSafe( lightDirection, lightDir ); + } + // Fall through + + case LightInfo::Point: + { + const F32 radius = lightInfo->getRange().x; + matParams->setSafe( lightRange, radius ); + + Point3F lightPos; + worldViewOnly.mulP(lightInfo->getPosition(), &lightPos); + matParams->setSafe( lightPosition, lightPos ); + + // Get the attenuation falloff ratio and normalize it. + Point3F attenRatio = lightInfo->getExtended()->attenuationRatio; + F32 total = attenRatio.x + attenRatio.y + attenRatio.z; + if ( total > 0.0f ) + attenRatio /= total; + + Point2F attenParams( ( 1.0f / radius ) * attenRatio.y, + ( 1.0f / ( radius * radius ) ) * attenRatio.z ); + + matParams->setSafe( lightAttenuation, attenParams ); + break; + } + + default: + AssertFatal( false, "Bad light type!" ); + break; + } +} + +bool LightMatInstance::setupPass( SceneRenderState *state, const SceneData &sgData ) +{ + // Go no further if the material failed to initialize properly. + if ( !mProcessedMaterial || + mProcessedMaterial->getNumPasses() == 0 ) + return false; + + // Fetch the lightmap params + const LightMapParams *lmParams = sgData.lights[0]->getExtended(); + + // If no Lightmap params, let parent handle it + if(lmParams == NULL) + return Parent::setupPass(state, sgData); + + // Defaults + bool bRetVal = true; + + // What render pass is this... + if(mCurPass == -1) + { + // First pass, reset this flag + mInternalPass = false; + + // Pass call to parent + bRetVal = Parent::setupPass(state, sgData); + } + else + { + // If this light is represented in a lightmap, it has already done it's + // job for non-lightmapped geometry. Now render the lightmapped geometry + // pass (specular + shadow-darkening) + if(!mInternalPass && lmParams->representedInLightmap) + mInternalPass = true; + else + return Parent::setupPass(state, sgData); + } + + // Set up the shader constants we need to... + if(mLightMapParamsSC->isValid()) + { + // If this is an internal pass, special case the parameters + if(mInternalPass) + { + AssertFatal( lmParams->shadowDarkenColor.alpha == -1.0f, "Assumption failed, check unpack code!" ); + getMaterialParameters()->set( mLightMapParamsSC, lmParams->shadowDarkenColor ); + } + else + getMaterialParameters()->set( mLightMapParamsSC, ColorF::WHITE ); + } + + // Now override stateblock with our own + if(!mInternalPass) + { + // If this is not an internal pass, and this light is represented in lightmaps + // than only effect non-lightmapped geometry for this pass + if(lmParams->representedInLightmap) + GFX->setStateBlock(mLitState[StaticLightNonLMGeometry]); + else // This is a normal, dynamic light. + GFX->setStateBlock(mLitState[DynamicLight]); + + } + else // Internal pass, this is the add-specular/multiply-darken-color pass + GFX->setStateBlock(mLitState[StaticLightLMGeometry]); + + return bRetVal; +} + +bool LightMatInstance::init( const FeatureSet &features, const GFXVertexFormat *vertexFormat ) +{ + bool success = Parent::init(features, vertexFormat); + + // If the initialization failed don't continue. + if ( !success || !mProcessedMaterial || mProcessedMaterial->getNumPasses() == 0 ) + return false; + + mLightMapParamsSC = getMaterialParameterHandle("$lightMapParams"); + + // Grab the state block for the first render pass (since this mat instance + // inserts a pass after the first pass) + AssertFatal(mProcessedMaterial->getNumPasses() > 0, "No passes created! Ohnoes"); + const RenderPassData *rpd = mProcessedMaterial->getPass(0); + AssertFatal(rpd, "No render pass data!"); + AssertFatal(rpd->mRenderStates[0], "No render state 0!"); + + // Get state block desc for normal (not wireframe, not translucent, not glow, etc) + // render state + GFXStateBlockDesc litState = rpd->mRenderStates[0]->getDesc(); + + // Create state blocks for each of the 3 possible combos in setupPass + + //DynamicLight State: This will effect lightmapped and non-lightmapped geometry + // in the same way. + litState.separateAlphaBlendDefined = true; + litState.separateAlphaBlendEnable = false; + litState.stencilMask = RenderPrePassMgr::OpaqueDynamicLitMask | RenderPrePassMgr::OpaqueStaticLitMask; + mLitState[DynamicLight] = GFX->createStateBlock(litState); + + // StaticLightNonLMGeometry State: This will treat non-lightmapped geometry + // in the usual way, but will not effect lightmapped geometry. + litState.separateAlphaBlendDefined = true; + litState.separateAlphaBlendEnable = false; + litState.stencilMask = RenderPrePassMgr::OpaqueDynamicLitMask; + mLitState[StaticLightNonLMGeometry] = GFX->createStateBlock(litState); + + // StaticLightLMGeometry State: This will add specular information (alpha) but + // multiply-darken color information. + litState.blendDest = GFXBlendSrcColor; + litState.blendSrc = GFXBlendZero; + litState.stencilMask = RenderPrePassMgr::OpaqueStaticLitMask; + litState.separateAlphaBlendDefined = true; + litState.separateAlphaBlendEnable = true; + litState.separateAlphaBlendSrc = GFXBlendOne; + litState.separateAlphaBlendDest = GFXBlendOne; + litState.separateAlphaBlendOp = GFXBlendOpAdd; + mLitState[StaticLightLMGeometry] = GFX->createStateBlock(litState); + + return true; +} diff --git a/Engine/source/lighting/advanced/advancedLightBinManager.h b/Engine/source/lighting/advanced/advancedLightBinManager.h new file mode 100644 index 000000000..a8aa20659 --- /dev/null +++ b/Engine/source/lighting/advanced/advancedLightBinManager.h @@ -0,0 +1,234 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _ADVANCEDLIGHTBINMANAGER_H_ +#define _ADVANCEDLIGHTBINMANAGER_H_ + +#ifndef _TEXTARGETBIN_MGR_H_ +#include "renderInstance/renderTexTargetBinManager.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _LIGHTINFO_H_ +#include "lighting/lightInfo.h" +#endif +#ifndef _MATHUTIL_FRUSTUM_H_ +#include "math/util/frustum.h" +#endif +#ifndef _MATINSTANCE_H_ +#include "materials/matInstance.h" +#endif +#ifndef _SHADOW_COMMON_H_ +#include "lighting/shadowMap/shadowCommon.h" +#endif + + +class AdvancedLightManager; +class ShadowMapManager; +class LightShadowMap; +class AdvancedLightBufferConditioner; +class LightMapParams; + + +class LightMatInstance : public MatInstance +{ + typedef MatInstance Parent; +protected: + MaterialParameterHandle *mLightMapParamsSC; + bool mInternalPass; + + enum + { + DynamicLight = 0, + StaticLightNonLMGeometry, + StaticLightLMGeometry, + NUM_LIT_STATES + }; + GFXStateBlockRef mLitState[NUM_LIT_STATES]; + +public: + LightMatInstance(Material &mat) : Parent(mat), mLightMapParamsSC(NULL), mInternalPass(false) {} + + virtual bool init( const FeatureSet &features, const GFXVertexFormat *vertexFormat ); + virtual bool setupPass( SceneRenderState *state, const SceneData &sgData ); +}; + + +class AdvancedLightBinManager : public RenderTexTargetBinManager +{ + typedef RenderTexTargetBinManager Parent; + +public: + + // Light info Render Inst Type + static const RenderInstType RIT_LightInfo; + + // registered buffer name + static const String smBufferName; + + /// The shadow filter mode to use on shadowed light materials. + static ShadowFilterMode smShadowFilterMode; + + /// Used to toggle the PSSM debug rendering mode. + static bool smPSSMDebugRender; + + /// Set by the SSAO post effect to tell the vector + /// light to compile in the SSAO mask. + static bool smUseSSAOMask; + + // Used for console init + AdvancedLightBinManager( AdvancedLightManager *lm = NULL, + ShadowMapManager *sm = NULL, + GFXFormat lightBufferFormat = GFXFormatR8G8B8A8 ); + virtual ~AdvancedLightBinManager(); + + // ConsoleObject + static void consoleInit(); + + // RenderBinManager + virtual void render(SceneRenderState *); + virtual void clear(); + virtual void sort() {} + + // Add a light to the bins + void addLight( LightInfo *light ); + + virtual bool setTargetSize(const Point2I &newTargetSize); + + // ConsoleObject interface + DECLARE_CONOBJECT(AdvancedLightBinManager); + + bool MRTLightmapsDuringPrePass() const { return mMRTLightmapsDuringPrePass; } + void MRTLightmapsDuringPrePass(bool val); + + + typedef Signal RenderSignal; + static RenderSignal &getRenderSignal(); + + AdvancedLightManager *getManager() { return mLightManager; } + +protected: + + /// Frees all the currently allocated light materials. + void _deleteLightMaterials(); + + // Track a light material and associated data + struct LightMaterialInfo + { + LightMatInstance *matInstance; + + // { zNear, zFar, 1/zNear, 1/zFar } + MaterialParameterHandle *zNearFarInvNearFar; + + // Far frustum plane (World Space) + MaterialParameterHandle *farPlane; + + // Far frustum plane (View Space) + MaterialParameterHandle *vsFarPlane; + + // -dot( farPlane, eyePos ) + MaterialParameterHandle *negFarPlaneDotEye; + + // Light Parameters + MaterialParameterHandle *lightPosition; + MaterialParameterHandle *lightDirection; + MaterialParameterHandle *lightColor; + MaterialParameterHandle *lightBrightness; + MaterialParameterHandle *lightAttenuation; + MaterialParameterHandle *lightRange; + MaterialParameterHandle *lightAmbient; + MaterialParameterHandle *lightTrilight; + MaterialParameterHandle *lightSpotParams; + + LightMaterialInfo( const String &matName, + const GFXVertexFormat *vertexFormat, + const Vector ¯os = Vector() ); + + virtual ~LightMaterialInfo(); + + + void setViewParameters( const F32 zNear, + const F32 zFar, + const Point3F &eyePos, + const PlaneF &farPlane, + const PlaneF &_vsFarPlane ); + + void setLightParameters( const LightInfo *light, const SceneRenderState* renderState, const MatrixF &worldViewOnly ); + }; + +protected: + + struct LightBinEntry + { + LightInfo* lightInfo; + LightShadowMap* shadowMap; + LightMaterialInfo* lightMaterial; + GFXPrimitiveBuffer* primBuffer; + GFXVertexBuffer* vertBuffer; + U32 numPrims; + }; + + Vector mLightBin; + typedef Vector::iterator LightBinIterator; + + bool mMRTLightmapsDuringPrePass; + + /// Used in setupSGData to set the object transform. + MatrixF mLightMat; + + U32 mNumLightsCulled; + AdvancedLightManager *mLightManager; + ShadowMapManager *mShadowManager; + + static const String smLightMatNames[LightInfo::Count]; + + static const String smShadowTypeMacro[ShadowType_Count]; + + static const GFXVertexFormat* smLightMatVertex[LightInfo::Count]; + + typedef CompoundKey3 LightMatKey; + + typedef HashTable LightMatTable; + + /// The fixed table of light material info. + LightMatTable mLightMaterials; + + LightMaterialInfo* _getLightMaterial( LightInfo::Type lightType, ShadowType shadowType, bool useCookieTex ); + + /// + void _onShadowFilterChanged(); + + AdvancedLightBufferConditioner *mConditioner; + + typedef GFXVertexPNT FarFrustumQuadVert; + GFXVertexBufferHandle mFarFrustumQuadVerts; + + + //void _createMaterials(); + + void _setupPerFrameParameters( const SceneRenderState *state ); + + void setupSGData( SceneData &data, const SceneRenderState* state, LightInfo *light ); +}; + +#endif // _ADVANCEDLIGHTBINMANAGER_H_ diff --git a/Engine/source/lighting/advanced/advancedLightBufferConditioner.cpp b/Engine/source/lighting/advanced/advancedLightBufferConditioner.cpp new file mode 100644 index 000000000..02919e26a --- /dev/null +++ b/Engine/source/lighting/advanced/advancedLightBufferConditioner.cpp @@ -0,0 +1,183 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/advanced/advancedLightBufferConditioner.h" + +#include "shaderGen/shaderOp.h" +#include "gfx/gfxDevice.h" +#include "core/util/safeDelete.h" + + +AdvancedLightBufferConditioner::~AdvancedLightBufferConditioner() +{ +} + +Var *AdvancedLightBufferConditioner::_conditionOutput( Var *unconditionedOutput, MultiLine *meta ) +{ + Var *conditionedOutput = new Var; + + if(GFX->getAdapterType() == OpenGL) + conditionedOutput->setType("vec4"); + else + conditionedOutput->setType("float4"); + + DecOp *outputDecl = new DecOp(conditionedOutput); + if(mColorFormat == RGB) + { + conditionedOutput->setName("rgbLightInfoOut"); + + // If this is a 16 bit integer format, scale up/down the values. All other + // formats just write out the full 0..1 + if(getBufferFormat() == GFXFormatR16G16B16A16) + meta->addStatement( new GenOp( " @ = max(4.0, (float4(lightColor, specular) * NL_att + float4(bufferSample.rgb, 0.0)) / 4.0);\r\n", outputDecl ) ); + else + meta->addStatement( new GenOp( " @ = float4(lightColor, specular) * NL_att + float4(bufferSample.rgb, 0.0);\r\n", outputDecl ) ); + } + else + { + // Input u'v' assumed to be scaled + conditionedOutput->setName("luvLightInfoOut"); + meta->addStatement( new GenOp( " @ = float4( lerp(bufferSample.xy, lightColor.xy, saturate(NL_att / bufferSample.z) * 0.5),\r\n", outputDecl ) ); + meta->addStatement( new GenOp( " bufferSample.z + NL_att, bufferSample.w + saturate(specular * NL_att) );\r\n" ) ); + } + + return conditionedOutput; +} + +Var *AdvancedLightBufferConditioner::_unconditionInput( Var *conditionedInput, MultiLine *meta ) +{ + if(mColorFormat == RGB) + { + if(getBufferFormat() == GFXFormatR16G16B16A16) + meta->addStatement( new GenOp( " lightColor = @.rgb * 4.0;\r\n", conditionedInput ) ); + else + meta->addStatement( new GenOp( " lightColor = @.rgb;\r\n", conditionedInput ) ); + meta->addStatement( new GenOp( " NL_att = dot(@.rgb, float3(0.3576, 0.7152, 0.1192));\r\n", conditionedInput ) ); + } + else + { + meta->addStatement( new GenOp( " // TODO: This clamps HDR values.\r\n" ) ); + meta->addStatement( new GenOp( " NL_att = @.b;\r\n", conditionedInput ) ); + meta->addStatement( new GenOp( " lightColor = DecodeLuv(float3(saturate(NL_att), @.rg * 0.62));\r\n", conditionedInput ) ); + } + meta->addStatement( new GenOp( " specular = max(@.a / NL_att, 0.00001f);\r\n", conditionedInput ) ); + + return NULL; +} + +Var *AdvancedLightBufferConditioner::printMethodHeader( MethodType methodType, const String &methodName, Stream &stream, MultiLine *meta ) +{ + Var *methodVar = new Var; + methodVar->setName(methodName); + DecOp *methodDecl = new DecOp(methodVar); + + Var *lightColor = new Var; + lightColor->setName("lightColor"); + DecOp *lightColorDecl = new DecOp(lightColor); + + Var *NLAtt = new Var; + NLAtt->setName("NL_att"); + DecOp *NLAttDecl = new DecOp(NLAtt); + + Var *specular = new Var; + specular->setName("specular"); + DecOp *specularDecl = new DecOp(specular); + + Var *bufferSample = new Var; + bufferSample->setName("bufferSample"); + DecOp *bufferSampleDecl = new DecOp(bufferSample); + + const bool isCondition = ( methodType == ConditionerFeature::ConditionMethod ); + + if(GFX->getAdapterType() == OpenGL) + { + methodVar->setType(avar("%s", isCondition ? "vec4" : "void")); + lightColor->setType(avar("%s vec3", isCondition ? "in" : "out")); + NLAtt->setType(avar("%s float", isCondition ? "in" : "out")); + specular->setType(avar("%s float", isCondition ? "in" : "out")); + bufferSample->setType("in vec4"); + } + else + { + methodVar->setType(avar("inline %s", isCondition ? "float4" : "void")); + lightColor->setType(avar("%s float3", isCondition ? "in" : "out")); + NLAtt->setType(avar("%s float", isCondition ? "in" : "out")); + specular->setType(avar("%s float", isCondition ? "in" : "out")); + bufferSample->setType("in float4"); + } + + // If this is LUV, print methods to convert RGB<->LUV as needed + if(mColorFormat == LUV) + { + if(!isCondition) + { + meta->addStatement( new GenOp( "float3 DecodeLuv(float3 Luv)\r\n{\r\n" ) ); + meta->addStatement( new GenOp( " float2 xy = float2(9.0f, 4.0f) * Luv.yz / (dot(Luv.yz, float2(6.0f, -16.0f)) + 12.0f);\r\n" ) ); + meta->addStatement( new GenOp( " float Ld = Luv.x;\r\n" ) ); + meta->addStatement( new GenOp( " float3 XYZ = float3(xy.x, Ld, 1.0f - xy.x - xy.y);\r\n" ) ); + meta->addStatement( new GenOp( " XYZ.xz = XYZ.xz * Ld / xy.y;\r\n" ) ); + meta->addStatement( new GenOp( " const float3x3 XYZ2RGB =\r\n" ) ); + meta->addStatement( new GenOp( " {\r\n" ) ); + meta->addStatement( new GenOp( " 2.5651f, -1.1665f, -0.3986f,\r\n" ) ); + meta->addStatement( new GenOp( " -1.0217f, 1.9777f, 0.0439f,\r\n" ) ); + meta->addStatement( new GenOp( " 0.0753f, -0.2543f, 1.1892f\r\n" ) ); + meta->addStatement( new GenOp( " };\r\n" ) ); + meta->addStatement( new GenOp( " return mul(XYZ2RGB, XYZ);\r\n" ) ); + meta->addStatement( new GenOp( "}\r\n\r\n" ) ); + } + else + { + // Shouldn't need this + } + } + + // Method header and opening bracket + if(isCondition) + { + // All parameters are input parameters, and the return value is float4. + // If this is an LUV buffer format, than the previous pixel value is needed + // for interpolation. + meta->addStatement( new GenOp( "@(@, @, @, @)\r\n", methodDecl, lightColorDecl, NLAttDecl, specularDecl, bufferSampleDecl ) ); + } + else + { + // Sample as input, parameters as output. Void return. + meta->addStatement( new GenOp( "@(@, @, @, @)\r\n", methodDecl, bufferSampleDecl, lightColorDecl, NLAttDecl, specularDecl ) ); + } + + meta->addStatement( new GenOp( "{\r\n" ) ); + + // We don't use this way of passing var's around, so this should cause a crash + // if something uses this improperly + return ( isCondition ? NULL : bufferSample ); +} + +void AdvancedLightBufferConditioner::printMethodFooter( ConditionerFeature::MethodType methodType, Var *retVar, Stream &stream, MultiLine *meta ) +{ + // Return and closing bracket + if(methodType == ConditionerFeature::ConditionMethod) + meta->addStatement( new GenOp( "\r\n return @;\r\n", retVar ) ); + + // Uncondition will assign output parameters + meta->addStatement( new GenOp( "}\r\n" ) ); +} \ No newline at end of file diff --git a/Engine/source/lighting/advanced/advancedLightBufferConditioner.h b/Engine/source/lighting/advanced/advancedLightBufferConditioner.h new file mode 100644 index 000000000..26e32c2b8 --- /dev/null +++ b/Engine/source/lighting/advanced/advancedLightBufferConditioner.h @@ -0,0 +1,65 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _ADVANCED_LIGHTBUFFER_CONDITIONER_H_ +#define _ADVANCED_LIGHTBUFFER_CONDITIONER_H_ + +#ifndef _CONDITIONER_BASE_H_ +#include "shaderGen/conditionerFeature.h" +#endif + + +class AdvancedLightBufferConditioner : public ConditionerFeature +{ + typedef ConditionerFeature Parent; + +public: + enum ColorFormat + { + RGB, + LUV + }; + +public: + + AdvancedLightBufferConditioner(const GFXFormat bufferFormat, const ColorFormat colorFormat) + : Parent(bufferFormat), mColorFormat(colorFormat) + { + + } + + virtual ~AdvancedLightBufferConditioner(); + + virtual String getName() + { + return String("Light Buffer Conditioner ") + String( mColorFormat == RGB ? "[RGB]" : "[LUV]" ); + } + +protected: + ColorFormat mColorFormat; + + virtual Var *_conditionOutput( Var *unconditionedOutput, MultiLine *meta ); + virtual Var *_unconditionInput( Var *conditionedInput, MultiLine *meta ); + virtual Var *printMethodHeader( MethodType methodType, const String &methodName, Stream &stream, MultiLine *meta ); + virtual void printMethodFooter( MethodType methodType, Var *retVar, Stream &stream, MultiLine *meta ); +}; + +#endif // _ADVANCED_LIGHTBUFFER_CONDITIONER_H_ \ No newline at end of file diff --git a/Engine/source/lighting/advanced/advancedLightManager.cpp b/Engine/source/lighting/advanced/advancedLightManager.cpp new file mode 100644 index 000000000..36be8db78 --- /dev/null +++ b/Engine/source/lighting/advanced/advancedLightManager.cpp @@ -0,0 +1,671 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/advanced/advancedLightManager.h" + +#include "lighting/advanced/advancedLightBinManager.h" +#include "lighting/advanced/advancedLightingFeatures.h" +#include "lighting/shadowMap/shadowMapManager.h" +#include "lighting/shadowMap/lightShadowMap.h" +#include "lighting/common/sceneLighting.h" +#include "lighting/common/lightMapParams.h" +#include "core/util/safeDelete.h" +#include "renderInstance/renderPrePassMgr.h" +#include "materials/materialManager.h" +#include "math/util/sphereMesh.h" +#include "console/consoleTypes.h" +#include "scene/sceneRenderState.h" + + +ImplementEnumType( ShadowType, + "\n\n" + "@ingroup AdvancedLighting" ) + { ShadowType_Spot, "Spot" }, + { ShadowType_PSSM, "PSSM" }, + { ShadowType_Paraboloid, "Paraboloid" }, + { ShadowType_DualParaboloidSinglePass, "DualParaboloidSinglePass" }, + { ShadowType_DualParaboloid, "DualParaboloid" }, + { ShadowType_CubeMap, "CubeMap" }, +EndImplementEnumType; + + +AdvancedLightManager AdvancedLightManager::smSingleton; + + +AdvancedLightManager::AdvancedLightManager() + : LightManager( "Advanced Lighting", "ADVLM" ) +{ + mLightBinManager = NULL; + mLastShader = NULL; + mAvailableSLInterfaces = NULL; +} + +AdvancedLightManager::~AdvancedLightManager() +{ + mLastShader = NULL; + mLastConstants = NULL; + + for (LightConstantMap::Iterator i = mConstantLookup.begin(); i != mConstantLookup.end(); i++) + { + if (i->value) + SAFE_DELETE(i->value); + } + mConstantLookup.clear(); +} + +bool AdvancedLightManager::isCompatible() const +{ + // TODO: We need at least 3.0 shaders at the moment + // but this should be relaxed to 2.0 soon. + if ( GFX->getPixelShaderVersion() < 3.0 ) + return false; + + // TODO: Test for the necessary texture formats! + + return true; +} + +void AdvancedLightManager::activate( SceneManager *sceneManager ) +{ + Parent::activate( sceneManager ); + + GFXShader::addGlobalMacro( "TORQUE_ADVANCED_LIGHTING" ); + + sceneManager->setPostEffectFog( true ); + + SHADOWMGR->activate(); + + // Find a target format that supports blending... + // we prefer the floating point format if it works. + Vector formats; + formats.push_back( GFXFormatR16G16B16A16F ); + formats.push_back( GFXFormatR16G16B16A16 ); + GFXFormat blendTargetFormat = GFX->selectSupportedFormat( &GFXDefaultRenderTargetProfile, + formats, + true, + true, + false ); + + mLightBinManager = new AdvancedLightBinManager( this, SHADOWMGR, blendTargetFormat ); + mLightBinManager->assignName( "AL_LightBinMgr" ); + + // First look for the prepass bin... + RenderPrePassMgr *prePassBin = _findPrePassRenderBin(); + + // If we didn't find the prepass bin then add one. + if ( !prePassBin ) + { + prePassBin = new RenderPrePassMgr( true, blendTargetFormat ); + prePassBin->assignName( "AL_PrePassBin" ); + prePassBin->registerObject(); + getSceneManager()->getDefaultRenderPass()->addManager( prePassBin ); + mPrePassRenderBin = prePassBin; + } + + // Tell the material manager that prepass is enabled. + MATMGR->setPrePassEnabled( true ); + + // Insert our light bin manager. + mLightBinManager->setRenderOrder( prePassBin->getRenderOrder() + 0.01f ); + getSceneManager()->getDefaultRenderPass()->addManager( mLightBinManager ); + + AdvancedLightingFeatures::registerFeatures(mPrePassRenderBin->getTargetFormat(), mLightBinManager->getTargetFormat()); + + // Last thing... let everyone know we're active. + smActivateSignal.trigger( getId(), true ); +} + +void AdvancedLightManager::deactivate() +{ + Parent::deactivate(); + + GFXShader::removeGlobalMacro( "TORQUE_ADVANCED_LIGHTING" ); + + // Release our bin manager... it will take care of + // removing itself from the render passes. + if( mLightBinManager ) + { + mLightBinManager->MRTLightmapsDuringPrePass(false); + mLightBinManager->deleteObject(); + } + mLightBinManager = NULL; + + if ( mPrePassRenderBin ) + mPrePassRenderBin->deleteObject(); + mPrePassRenderBin = NULL; + + SHADOWMGR->deactivate(); + + mLastShader = NULL; + mLastConstants = NULL; + + for (LightConstantMap::Iterator i = mConstantLookup.begin(); i != mConstantLookup.end(); i++) + { + if (i->value) + SAFE_DELETE(i->value); + } + mConstantLookup.clear(); + + mSphereGeometry = NULL; + mSphereIndices = NULL; + mConeGeometry = NULL; + mConeIndices = NULL; + + AdvancedLightingFeatures::unregisterFeatures(); + + // Now let everyone know we've deactivated. + smActivateSignal.trigger( getId(), false ); +} + +void AdvancedLightManager::_addLightInfoEx( LightInfo *lightInfo ) +{ + lightInfo->addExtended( new ShadowMapParams( lightInfo ) ); + lightInfo->addExtended( new LightMapParams( lightInfo ) ); +} + + +void AdvancedLightManager::_initLightFields() +{ + #define DEFINE_LIGHT_FIELD( var, type, enum_ ) \ + static inline const char* _get##var##Field( void *obj, const char *data ) \ + { \ + ShadowMapParams *p = _getShadowMapParams( obj ); \ + if ( p ) \ + return Con::getData( type, &p->var, 0, enum_ ); \ + else \ + return ""; \ + } \ + \ + static inline bool _set##var##Field( void *object, const char *index, const char *data ) \ + { \ + ShadowMapParams *p = _getShadowMapParams( object ); \ + if ( p ) \ + { \ + Con::setData( type, &p->var, 0, 1, &data, enum_ ); \ + p->_validate(); \ + } \ + return false; \ + } + + #define DEFINE_LIGHTMAP_FIELD( var, type, enum_ ) \ + static inline const char* _get##var##Field( void *obj, const char *data ) \ + { \ + LightMapParams *p = _getLightMapParams( obj ); \ + if ( p ) \ + return Con::getData( type, &p->var, 0, enum_ ); \ + else \ + return ""; \ + } \ + \ + static inline bool _set##var##Field( void *object, const char *index, const char *data ) \ + { \ + LightMapParams *p = _getLightMapParams( object ); \ + if ( p ) \ + { \ + Con::setData( type, &p->var, 0, 1, &data, enum_ ); \ + } \ + return false; \ + } + + #define ADD_LIGHT_FIELD( field, type, var, desc ) \ + ConsoleObject::addProtectedField( field, type, 0, \ + &Dummy::_set##var##Field, &Dummy::_get##var##Field, desc ) + + // Our dummy adaptor class which we hide in here + // to keep from poluting the global namespace. + class Dummy + { + protected: + + static inline ShadowMapParams* _getShadowMapParams( void *obj ) + { + ISceneLight *sceneLight = dynamic_cast( (SimObject*)obj ); + if ( sceneLight ) + { + LightInfo *lightInfo = sceneLight->getLight(); + if ( lightInfo ) + return lightInfo->getExtended(); + } + return NULL; + } + + static inline LightMapParams* _getLightMapParams( void *obj ) + { + ISceneLight *sceneLight = dynamic_cast( (SimObject*)obj ); + if ( sceneLight ) + { + LightInfo *lightInfo = sceneLight->getLight(); + if ( lightInfo ) + return lightInfo->getExtended(); + } + return NULL; + } + + public: + + DEFINE_LIGHT_FIELD( attenuationRatio, TypePoint3F, NULL ); + DEFINE_LIGHT_FIELD( shadowType, TYPEID< ShadowType >(), ConsoleBaseType::getType( TYPEID< ShadowType >() )->getEnumTable() ); + DEFINE_LIGHT_FIELD( texSize, TypeS32, NULL ); + DEFINE_LIGHT_FIELD( cookie, TypeStringFilename, NULL ); + DEFINE_LIGHT_FIELD( numSplits, TypeS32, NULL ); + DEFINE_LIGHT_FIELD( logWeight, TypeF32, NULL ); + DEFINE_LIGHT_FIELD( overDarkFactor, TypePoint4F, NULL); + DEFINE_LIGHT_FIELD( shadowDistance, TypeF32, NULL ); + DEFINE_LIGHT_FIELD( shadowSoftness, TypeF32, NULL ); + DEFINE_LIGHT_FIELD( fadeStartDist, TypeF32, NULL ); + DEFINE_LIGHT_FIELD( lastSplitTerrainOnly, TypeBool, NULL ); + + DEFINE_LIGHTMAP_FIELD( representedInLightmap, TypeBool, NULL ); + DEFINE_LIGHTMAP_FIELD( shadowDarkenColor, TypeColorF, NULL ); + DEFINE_LIGHTMAP_FIELD( includeLightmappedGeometryInShadow, TypeBool, NULL ); + }; + + ConsoleObject::addGroup( "Advanced Lighting" ); + + ADD_LIGHT_FIELD( "attenuationRatio", TypePoint3F, attenuationRatio, + "The proportions of constant, linear, and quadratic attenuation to use for " + "the falloff for point and spot lights." ); + + ADD_LIGHT_FIELD( "shadowType", TYPEID< ShadowType >(), shadowType, + "The type of shadow to use on this light." ); + + ADD_LIGHT_FIELD( "cookie", TypeStringFilename, cookie, + "A custom pattern texture which is projected from the light." ); + + ADD_LIGHT_FIELD( "texSize", TypeS32, texSize, + "The texture size of the shadow map." ); + + ADD_LIGHT_FIELD( "overDarkFactor", TypePoint4F, overDarkFactor, + "The ESM shadow darkening factor"); + + ADD_LIGHT_FIELD( "shadowDistance", TypeF32, shadowDistance, + "The distance from the camera to extend the PSSM shadow." ); + + ADD_LIGHT_FIELD( "shadowSoftness", TypeF32, shadowSoftness, + "" ); + + ADD_LIGHT_FIELD( "numSplits", TypeS32, numSplits, + "The logrithmic PSSM split distance factor." ); + + ADD_LIGHT_FIELD( "logWeight", TypeF32, logWeight, + "The logrithmic PSSM split distance factor." ); + + ADD_LIGHT_FIELD( "fadeStartDistance", TypeF32, fadeStartDist, + "Start fading shadows out at this distance. 0 = auto calculate this distance."); + + ADD_LIGHT_FIELD( "lastSplitTerrainOnly", TypeBool, lastSplitTerrainOnly, + "This toggles only terrain being rendered to the last split of a PSSM shadow map."); + + ConsoleObject::endGroup( "Advanced Lighting" ); + + ConsoleObject::addGroup( "Advanced Lighting Lightmap" ); + + ADD_LIGHT_FIELD( "representedInLightmap", TypeBool, representedInLightmap, + "This light is represented in lightmaps (static light, default: false)"); + + ADD_LIGHT_FIELD( "shadowDarkenColor", TypeColorF, shadowDarkenColor, + "The color that should be used to multiply-blend dynamic shadows onto lightmapped geometry (ignored if 'representedInLightmap' is false)"); + + ADD_LIGHT_FIELD( "includeLightmappedGeometryInShadow", TypeBool, includeLightmappedGeometryInShadow, + "This light should render lightmapped geometry during its shadow-map update (ignored if 'representedInLightmap' is false)"); + + ConsoleObject::endGroup( "Advanced Lighting Lightmap" ); + + #undef DEFINE_LIGHT_FIELD + #undef ADD_LIGHT_FIELD +} + +void AdvancedLightManager::setLightInfo( ProcessedMaterial *pmat, + const Material *mat, + const SceneData &sgData, + const SceneRenderState *state, + U32 pass, + GFXShaderConstBuffer *shaderConsts) +{ + // Skip this if we're rendering from the prepass bin. + if ( sgData.binType == SceneData::PrePassBin ) + return; + + PROFILE_SCOPE(AdvancedLightManager_setLightInfo); + + LightingShaderConstants *lsc = getLightingShaderConstants(shaderConsts); + + LightShadowMap *lsm = SHADOWMGR->getCurrentShadowMap(); + + LightInfo *light; + if ( lsm ) + light = lsm->getLightInfo(); + else + { + light = sgData.lights[0]; + if ( !light ) + light = getDefaultLight(); + } + + // NOTE: If you encounter a crash from this point forward + // while setting a shader constant its probably because the + // mConstantLookup has bad shaders/constants in it. + // + // This is a known crash bug that can occur if materials/shaders + // are reloaded and the light manager is not reset. + // + // We should look to fix this by clearing the table. + + // Update the forward shading light constants. + _update4LightConsts( sgData, + lsc->mLightPositionSC, + lsc->mLightDiffuseSC, + lsc->mLightAmbientSC, + lsc->mLightInvRadiusSqSC, + lsc->mLightSpotDirSC, + lsc->mLightSpotAngleSC, + lsc->mLightSpotFalloffSC, + shaderConsts ); + + if ( lsm && light->getCastShadows() ) + { + if ( lsc->mWorldToLightProjSC->isValid() ) + shaderConsts->set( lsc->mWorldToLightProjSC, + lsm->getWorldToLightProj(), + lsc->mWorldToLightProjSC->getType() ); + + if ( lsc->mViewToLightProjSC->isValid() ) + { + // TODO: Should probably cache these results and + // not do this mul here on every material that needs + // this transform. + + shaderConsts->set( lsc->mViewToLightProjSC, + lsm->getWorldToLightProj() * state->getCameraTransform(), + lsc->mViewToLightProjSC->getType() ); + } + + shaderConsts->setSafe( lsc->mShadowMapSizeSC, 1.0f / (F32)lsm->getTexSize() ); + + // Do this last so that overrides can properly override parameters previously set + lsm->setShaderParameters(shaderConsts, lsc); + } + else + { + if ( lsc->mViewToLightProjSC->isValid() ) + { + // TODO: Should probably cache these results and + // not do this mul here on every material that needs + // this transform. + MatrixF proj; + light->getWorldToLightProj( &proj ); + + shaderConsts->set( lsc->mViewToLightProjSC, + proj * state->getCameraTransform(), + lsc->mViewToLightProjSC->getType() ); + } + } +} + +void AdvancedLightManager::registerGlobalLight(LightInfo *light, SimObject *obj) +{ + Parent::registerGlobalLight( light, obj ); + + // Pass the volume lights to the bin manager. + if ( mLightBinManager && + ( light->getType() == LightInfo::Point || + light->getType() == LightInfo::Spot ) ) + mLightBinManager->addLight( light ); +} + +void AdvancedLightManager::unregisterAllLights() +{ + Parent::unregisterAllLights(); + + if ( mLightBinManager ) + mLightBinManager->clear(); +} + +bool AdvancedLightManager::setTextureStage( const SceneData &sgData, + const U32 currTexFlag, + const U32 textureSlot, + GFXShaderConstBuffer *shaderConsts, + ShaderConstHandles *handles ) +{ + LightShadowMap* lsm = SHADOWMGR->getCurrentShadowMap(); + + // Assign Shadowmap, if it exists + LightingShaderConstants* lsc = getLightingShaderConstants(shaderConsts); + if ( !lsc ) + return false; + + if ( lsm && lsm->getLightInfo()->getCastShadows() ) + return lsm->setTextureStage( currTexFlag, lsc ); + + + if ( currTexFlag == Material::DynamicLight ) + { + S32 reg = lsc->mShadowMapSC->getSamplerRegister(); + if ( reg != -1 ) + GFX->setTexture( reg, GFXTexHandle::ONE ); + + return true; + } + else if ( currTexFlag == Material::DynamicLightMask ) + { + S32 reg = lsc->mCookieMapSC->getSamplerRegister(); + if ( reg != -1 && sgData.lights[0] ) + { + ShadowMapParams *p = sgData.lights[0]->getExtended(); + + if ( lsc->mCookieMapSC->getType() == GFXSCT_SamplerCube ) + GFX->setCubeTexture( reg, p->getCookieCubeTex() ); + else + GFX->setTexture( reg, p->getCookieTex() ); + } + + return true; + } + + return false; +} + +LightingShaderConstants* AdvancedLightManager::getLightingShaderConstants(GFXShaderConstBuffer* buffer) +{ + if ( !buffer ) + return NULL; + + PROFILE_SCOPE( AdvancedLightManager_GetLightingShaderConstants ); + + GFXShader* shader = buffer->getShader(); + + // Check to see if this is the same shader, we'll get hit repeatedly by + // the same one due to the render bin loops. + if ( mLastShader.getPointer() != shader ) + { + LightConstantMap::Iterator iter = mConstantLookup.find(shader); + if ( iter != mConstantLookup.end() ) + { + mLastConstants = iter->value; + } + else + { + LightingShaderConstants* lsc = new LightingShaderConstants(); + mConstantLookup[shader] = lsc; + + mLastConstants = lsc; + } + + // Set our new shader + mLastShader = shader; + } + + // Make sure that our current lighting constants are initialized + if (!mLastConstants->mInit) + mLastConstants->init(shader); + + return mLastConstants; +} + +GFXVertexBufferHandle AdvancedLightManager::getSphereMesh(U32 &outNumPrimitives, GFXPrimitiveBuffer *&outPrimitives) +{ + static SphereMesh sSphereMesh; + + if( mSphereGeometry.isNull() ) + { + const SphereMesh::TriangleMesh * sphereMesh = sSphereMesh.getMesh(3); + S32 numPoly = sphereMesh->numPoly; + mSpherePrimitiveCount = 0; + mSphereGeometry.set(GFX, numPoly*3, GFXBufferTypeStatic); + mSphereGeometry.lock(); + S32 vertexIndex = 0; + + for (S32 i=0; ipoly[i].pnt[0]; + mSphereGeometry[vertexIndex].color = ColorI::WHITE; + vertexIndex++; + + mSphereGeometry[vertexIndex].point = sphereMesh->poly[i].pnt[1]; + mSphereGeometry[vertexIndex].color = ColorI::WHITE; + vertexIndex++; + + mSphereGeometry[vertexIndex].point = sphereMesh->poly[i].pnt[2]; + mSphereGeometry[vertexIndex].color = ColorI::WHITE; + vertexIndex++; + } + mSphereGeometry.unlock(); + } + + outNumPrimitives = mSpherePrimitiveCount; + outPrimitives = NULL; // For now + return mSphereGeometry; +} + +GFXVertexBufferHandle AdvancedLightManager::getConeMesh(U32 &outNumPrimitives, GFXPrimitiveBuffer *&outPrimitives ) +{ + static const Point2F circlePoints[] = + { + Point2F(0.707107f, 0.707107f), + Point2F(0.923880f, 0.382683f), + Point2F(1.000000f, 0.000000f), + Point2F(0.923880f, -0.382684f), + Point2F(0.707107f, -0.707107f), + Point2F(0.382683f, -0.923880f), + Point2F(0.000000f, -1.000000f), + Point2F(-0.382683f, -0.923880f), + Point2F(-0.707107f, -0.707107f), + Point2F(-0.923880f, -0.382684f), + Point2F(-1.000000f, 0.000000f), + Point2F(-0.923879f, 0.382684f), + Point2F(-0.707107f, 0.707107f), + Point2F(-0.382683f, 0.923880f), + Point2F(0.000000f, 1.000000f), + Point2F(0.382684f, 0.923879f) + }; + const S32 numPoints = sizeof(circlePoints)/sizeof(Point2F); + + if ( mConeGeometry.isNull() ) + { + mConeGeometry.set(GFX, numPoints + 1, GFXBufferTypeStatic); + mConeGeometry.lock(); + + mConeGeometry[0].point = Point3F(0.0f,0.0f,0.0f); + + for (S32 i=1; i numPoints ) ? 1 : i + 1; + } + + // Build the bottom of the cone (reverse winding order) + for( int i = 1; i < numPoints - 1; i++ ) + { + idx[idxIdx++] = 1; + idx[idxIdx++] = i + 2; + idx[idxIdx++] = i + 1; + } + mConeIndices.unlock(); + } + + outNumPrimitives = mConePrimitiveCount; + outPrimitives = mConeIndices.getPointer(); + return mConeGeometry; +} + +LightShadowMap* AdvancedLightManager::findShadowMapForObject( SimObject *object ) +{ + if ( !object ) + return NULL; + + ISceneLight *sceneLight = dynamic_cast( object ); + if ( !sceneLight || !sceneLight->getLight() ) + return NULL; + + return sceneLight->getLight()->getExtended()->getShadowMap(); +} + +ConsoleFunction( setShadowVizLight, const char*, 2, 2, "" ) +{ + static const String DebugTargetName( "AL_ShadowVizTexture" ); + + NamedTexTarget *target = NamedTexTarget::find( DebugTargetName ); + if ( target ) + target->unregister(); + + AdvancedLightManager *lm = dynamic_cast( LIGHTMGR ); + if ( !lm ) + return 0; + + SimObject *object; + Sim::findObject( argv[1], object ); + LightShadowMap *lightShadowMap = lm->findShadowMapForObject( object ); + if ( !lightShadowMap || !lightShadowMap->getTexture() ) + return 0; + + lightShadowMap->setDebugTarget( DebugTargetName ); + + GFXTextureObject *texObject = lightShadowMap->getTexture(); + const Point3I &size = texObject->getSize(); + F32 aspect = (F32)size.x / (F32)size.y; + + char *result = Con::getReturnBuffer( 64 ); + dSprintf( result, 64, "%d %d %g", size.x, size.y, aspect ); + return result; +} + diff --git a/Engine/source/lighting/advanced/advancedLightManager.h b/Engine/source/lighting/advanced/advancedLightManager.h new file mode 100644 index 000000000..efdf4fa32 --- /dev/null +++ b/Engine/source/lighting/advanced/advancedLightManager.h @@ -0,0 +1,143 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _ADVANCEDLIGHTMANAGER_H_ +#define _ADVANCEDLIGHTMANAGER_H_ + +#ifndef _SIMOBJECT_H_ +#include "console/simObject.h" +#endif +#ifndef _LIGHTMANAGER_H_ +#include "lighting/lightManager.h" +#endif +#ifndef _LIGHTINFO_H_ +#include "lighting/lightInfo.h" +#endif +#ifndef _GFXTEXTUREHANDLE_H_ +#include "gfx/gfxTextureHandle.h" +#endif +#ifndef _GFXTARGET_H_ +#include "gfx/gfxTarget.h" +#endif +#ifndef _TDICTIONARY_H_ +#include "core/util/tDictionary.h" +#endif +#ifndef _LIGHTSHADOWMAP_H_ +#include "lighting/shadowMap/lightShadowMap.h" +#endif +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif +#ifndef _GFXVERTEXBUFFER_H_ +#include "gfx/gfxVertexBuffer.h" +#endif + + +class AvailableSLInterfaces; +class AdvancedLightBinManager; +class RenderPrePassMgr; +class BaseMatInstance; +class MaterialParameters; +class MaterialParameterHandle; +class GFXShader; +class GFXShaderConstHandle; +class ShadowMapManager; + + +class AdvancedLightManager : public LightManager +{ + typedef LightManager Parent; + +public: + + /// Return the lightBinManager for this light manager. + AdvancedLightBinManager* getLightBinManager() { return mLightBinManager; } + + // LightManager + virtual bool isCompatible() const; + virtual void activate( SceneManager *sceneManager ); + virtual void deactivate(); + virtual void registerGlobalLight(LightInfo *light, SimObject *obj); + virtual void unregisterAllLights(); + virtual void setLightInfo( ProcessedMaterial *pmat, + const Material *mat, + const SceneData &sgData, + const SceneRenderState *state, + U32 pass, + GFXShaderConstBuffer *shaderConsts ); + virtual bool setTextureStage( const SceneData &sgData, + const U32 currTexFlag, + const U32 textureSlot, + GFXShaderConstBuffer *shaderConsts, + ShaderConstHandles *handles ); + + typedef GFXVertexPC LightVertex; + + GFXVertexBufferHandle getSphereMesh(U32 &outNumPrimitives, GFXPrimitiveBuffer *&outPrimitives ); + GFXVertexBufferHandle getConeMesh(U32 &outNumPrimitives, GFXPrimitiveBuffer *&outPrimitives ); + + LightShadowMap* findShadowMapForObject( SimObject *object ); + +protected: + + // LightManager + virtual void _addLightInfoEx( LightInfo *lightInfo ); + virtual void _initLightFields(); + + /// A simple protected singleton. Use LightManager::findByName() + /// to access this light manager. + /// @see LightManager::findByName() + static AdvancedLightManager smSingleton; + + // These are protected because we're a singleton and + // no one else should be creating us! + AdvancedLightManager(); + virtual ~AdvancedLightManager(); + + SimObjectPtr mLightBinManager; + + SimObjectPtr mPrePassRenderBin; + + LightConstantMap mConstantLookup; + + GFXShaderRef mLastShader; + + LightingShaderConstants* mLastConstants; + + // Convex geometry for lights + GFXVertexBufferHandle mSphereGeometry; + + GFXPrimitiveBufferHandle mSphereIndices; + + U32 mSpherePrimitiveCount; + + GFXVertexBufferHandle mConeGeometry; + + GFXPrimitiveBufferHandle mConeIndices; + + U32 mConePrimitiveCount; + + LightingShaderConstants* getLightingShaderConstants(GFXShaderConstBuffer* shader); + +}; + +#endif // _ADVANCEDLIGHTMANAGER_H_ diff --git a/Engine/source/lighting/advanced/advancedLightingFeatures.cpp b/Engine/source/lighting/advanced/advancedLightingFeatures.cpp new file mode 100644 index 000000000..159eb113a --- /dev/null +++ b/Engine/source/lighting/advanced/advancedLightingFeatures.cpp @@ -0,0 +1,101 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/advanced/advancedLightingFeatures.h" + +#include "shaderGen/featureMgr.h" +#include "gfx/gfxStringEnumTranslate.h" +#include "materials/materialParameters.h" +#include "materials/materialFeatureTypes.h" +#include "materials/matTextureTarget.h" +#include "gfx/gfxDevice.h" +#include "core/util/safeDelete.h" + +#ifndef TORQUE_OS_MAC +# include "lighting/advanced/hlsl/gBufferConditionerHLSL.h" +# include "lighting/advanced/hlsl/advancedLightingFeaturesHLSL.h" +#else +# include "lighting/advanced/glsl/gBufferConditionerGLSL.h" +# include "lighting/advanced/glsl/advancedLightingFeaturesGLSL.h" +#endif + + + +bool AdvancedLightingFeatures::smFeaturesRegistered = false; + +void AdvancedLightingFeatures::registerFeatures( const GFXFormat &prepassTargetFormat, const GFXFormat &lightInfoTargetFormat ) +{ + AssertFatal( !smFeaturesRegistered, "AdvancedLightingFeatures::registerFeatures() - Features already registered. Bad!" ); + + // If we ever need this... + TORQUE_UNUSED(lightInfoTargetFormat); + + ConditionerFeature *cond = NULL; + + if(GFX->getAdapterType() == OpenGL) + { +#ifdef TORQUE_OS_MAC + cond = new GBufferConditionerGLSL( prepassTargetFormat ); + FEATUREMGR->registerFeature(MFT_PrePassConditioner, cond); + FEATUREMGR->registerFeature(MFT_RTLighting, new DeferredRTLightingFeatGLSL()); + FEATUREMGR->registerFeature(MFT_NormalMap, new DeferredBumpFeatGLSL()); + FEATUREMGR->registerFeature(MFT_PixSpecular, new DeferredPixelSpecularGLSL()); + FEATUREMGR->registerFeature(MFT_MinnaertShading, new DeferredMinnaertGLSL()); + FEATUREMGR->registerFeature(MFT_SubSurface, new DeferredSubSurfaceGLSL()); +#endif + } + else + { +#ifndef TORQUE_OS_MAC + cond = new GBufferConditionerHLSL( prepassTargetFormat, GBufferConditionerHLSL::ViewSpace ); + FEATUREMGR->registerFeature(MFT_PrePassConditioner, cond); + FEATUREMGR->registerFeature(MFT_RTLighting, new DeferredRTLightingFeatHLSL()); + FEATUREMGR->registerFeature(MFT_NormalMap, new DeferredBumpFeatHLSL()); + FEATUREMGR->registerFeature(MFT_PixSpecular, new DeferredPixelSpecularHLSL()); + FEATUREMGR->registerFeature(MFT_MinnaertShading, new DeferredMinnaertHLSL()); + FEATUREMGR->registerFeature(MFT_SubSurface, new DeferredSubSurfaceHLSL()); +#endif + } + + NamedTexTarget *target = NamedTexTarget::find( "prepass" ); + if ( target ) + target->setConditioner( cond ); + + smFeaturesRegistered = true; +} + +void AdvancedLightingFeatures::unregisterFeatures() +{ + NamedTexTarget *target = NamedTexTarget::find( "prepass" ); + if ( target ) + target->setConditioner( NULL ); + + FEATUREMGR->unregisterFeature(MFT_PrePassConditioner); + FEATUREMGR->unregisterFeature(MFT_RTLighting); + FEATUREMGR->unregisterFeature(MFT_NormalMap); + FEATUREMGR->unregisterFeature(MFT_PixSpecular); + FEATUREMGR->unregisterFeature(MFT_MinnaertShading); + FEATUREMGR->unregisterFeature(MFT_SubSurface); + + smFeaturesRegistered = false; +} diff --git a/Engine/source/lighting/advanced/advancedLightingFeatures.h b/Engine/source/lighting/advanced/advancedLightingFeatures.h new file mode 100644 index 000000000..7cbfe1761 --- /dev/null +++ b/Engine/source/lighting/advanced/advancedLightingFeatures.h @@ -0,0 +1,42 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _ADVANCEDLIGHTINGFEATURES_H_ +#define _ADVANCEDLIGHTINGFEATURES_H_ + +#ifndef _GFXENUMS_H_ +#include "gfx/gfxEnums.h" +#endif + + +class AdvancedLightingFeatures +{ +public: + + static void registerFeatures( const GFXFormat &prepassTargetFormat, const GFXFormat &lightInfoTargetFormat ); + static void unregisterFeatures(); + +private: + + static bool smFeaturesRegistered; +}; + +#endif // _ADVANCEDLIGHTINGFEATURES_H_ diff --git a/Engine/source/lighting/advanced/glsl/advancedLightingFeaturesGLSL.cpp b/Engine/source/lighting/advanced/glsl/advancedLightingFeaturesGLSL.cpp new file mode 100644 index 000000000..b46f46cb6 --- /dev/null +++ b/Engine/source/lighting/advanced/glsl/advancedLightingFeaturesGLSL.cpp @@ -0,0 +1,725 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/advanced/glsl/advancedLightingFeaturesGLSL.h" + +#include "lighting/advanced/advancedLightBinManager.h" +#include "shaderGen/langElement.h" +#include "shaderGen/shaderOp.h" +#include "shaderGen/conditionerFeature.h" +#include "renderInstance/renderPrePassMgr.h" +#include "materials/processedMaterial.h" +#include "materials/materialFeatureTypes.h" + + +void DeferredRTLightingFeatGLSL::processPixMacros( Vector ¯os, + const MaterialFeatureData &fd ) +{ + /// TODO: This needs to be done via some sort of material + /// feature and not just allow all translucent elements to + /// read from the light prepass. + /* + if ( fd.features[MFT_IsTranslucent] ) + { + Parent::processPixMacros( macros, fd ); + return; + } + */ + + // Pull in the uncondition method for the light info buffer + NamedTexTarget *texTarget = NamedTexTarget::find( AdvancedLightBinManager::smBufferName ); + if ( texTarget && texTarget->getConditioner() ) + { + ConditionerMethodDependency *unconditionMethod = texTarget->getConditioner()->getConditionerMethodDependency(ConditionerFeature::UnconditionMethod); + unconditionMethod->createMethodMacro( String::ToLower( AdvancedLightBinManager::smBufferName ) + "Uncondition", macros ); + addDependency(unconditionMethod); + } +} + +void DeferredRTLightingFeatGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + /// TODO: This needs to be done via some sort of material + /// feature and not just allow all translucent elements to + /// read from the light prepass. + /* + if ( fd.features[MFT_IsTranslucent] ) + { + Parent::processVert( componentList, fd ); + return; + } + */ + + // Pass screen space position to pixel shader to compute a full screen buffer uv + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *ssPos = connectComp->getElement( RT_TEXCOORD ); + ssPos->setName( "screenspacePos" ); + ssPos->setType( "vec4" ); + +// Var *outPosition = (Var*) LangElement::find( "hpos" ); +// AssertFatal( outPosition, "No hpos, ohnoes." ); + + output = new GenOp( " @ = gl_Position;\r\n", ssPos ); +} + +void DeferredRTLightingFeatGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + /// TODO: This needs to be done via some sort of material + /// feature and not just allow all translucent elements to + /// read from the light prepass. + /* + if ( fd.features[MFT_IsTranslucent] ) + { + Parent::processPix( componentList, fd ); + return; + } + */ + + MultiLine *meta = new MultiLine; + + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *ssPos = connectComp->getElement( RT_TEXCOORD ); + ssPos->setName( "screenspacePos" ); + ssPos->setType( "vec4" ); + + Var *uvScene = new Var; + uvScene->setType( "vec2" ); + uvScene->setName( "uvScene" ); + LangElement *uvSceneDecl = new DecOp( uvScene ); + + Var *rtParams = (Var*) LangElement::find( "renderTargetParams" ); + if( !rtParams ) + { + rtParams = new Var; + rtParams->setType( "vec4" ); + rtParams->setName( "renderTargetParams" ); + rtParams->uniform = true; + rtParams->constSortPos = cspPass; + } + + meta->addStatement( new GenOp( " @ = @.xy / @.w;\r\n", uvSceneDecl, ssPos, ssPos ) ); // get the screen coord... its -1 to +1 + meta->addStatement( new GenOp( " @ = ( @ + 1.0 ) / 2.0;\r\n", uvScene, uvScene ) ); // get the screen coord to 0 to 1 + meta->addStatement( new GenOp( " @ = ( @ * @.zw ) + @.xy;\r\n", uvScene, uvScene, rtParams, rtParams) ); // scale it down and offset it to the rt size + + Var *lightInfoSamp = new Var; + lightInfoSamp->setType( "vec4" ); + lightInfoSamp->setName( "lightInfoSample" ); + + // create texture var + Var *lightInfoBuffer = new Var; + lightInfoBuffer->setType( "sampler2D" ); + lightInfoBuffer->setName( "lightInfoBuffer" ); + lightInfoBuffer->uniform = true; + lightInfoBuffer->sampler = true; + lightInfoBuffer->constNum = Var::getTexUnitNum(); // used as texture unit num here + + String unconditionLightInfo = String::ToLower( AdvancedLightBinManager::smBufferName ) + "Uncondition"; + + meta->addStatement( new GenOp( " vec3 d_lightcolor;\r\n" ) ); + meta->addStatement( new GenOp( " float d_NL_Att;\r\n" ) ); + meta->addStatement( new GenOp( " float d_specular;\r\n" ) ); + meta->addStatement( new GenOp( avar( " %s(texture2D(@, @), d_lightcolor, d_NL_Att, d_specular);\r\n", unconditionLightInfo.c_str() ), + lightInfoBuffer, uvScene ) ); + + Var *rtShading = new Var; + rtShading->setType( "vec4" ); + rtShading->setName( "rtShading" ); + LangElement *rtShadingDecl = new DecOp( rtShading ); + meta->addStatement( new GenOp( " @ = vec4( d_lightcolor, 1.0 );\r\n", rtShadingDecl ) ); + + // This is kind of weak sauce + if( !fd.features[MFT_SubSurface] && !fd.features[MFT_ToneMap] && !fd.features[MFT_LightMap] ) + meta->addStatement( new GenOp( " @;\r\n", assignColor( rtShading, Material::Mul ) ) ); + + output = meta; +} + +ShaderFeature::Resources DeferredRTLightingFeatGLSL::getResources( const MaterialFeatureData &fd ) +{ + /// TODO: This needs to be done via some sort of material + /// feature and not just allow all translucent elements to + /// read from the light prepass. + /* + if( fd.features[MFT_IsTranslucent] ) + return Parent::getResources( fd ); + */ + + Resources res; + res.numTex = 1; + res.numTexReg = 1; + return res; +} + +void DeferredRTLightingFeatGLSL::setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ) +{ + /// TODO: This needs to be done via some sort of material + /// feature and not just allow all translucent elements to + /// read from the light prepass. + /* + if( fd.features[MFT_IsTranslucent] ) + { + Parent::setTexData( stageDat, fd, passData, texIndex ); + return; + } + */ + + NamedTexTarget *texTarget = NamedTexTarget::find( AdvancedLightBinManager::smBufferName ); + if( texTarget ) + { + passData.mTexType[ texIndex ] = Material::TexTarget; + passData.mTexSlot[ texIndex++ ].texTarget = texTarget; + } +} + + +void DeferredBumpFeatGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + if( fd.features[MFT_PrePassConditioner] ) + { + // There is an output conditioner active, so we need to supply a transform + // to the pixel shader. + MultiLine *meta = new MultiLine; + + // setup texture space matrix + Var *texSpaceMat = (Var*) LangElement::find( "objToTangentSpace" ); + if( !texSpaceMat ) + { + LangElement * texSpaceSetup = setupTexSpaceMat( componentList, &texSpaceMat ); + meta->addStatement( texSpaceSetup ); + texSpaceMat = (Var*) LangElement::find( "objToTangentSpace" ); + } + + // turn obj->tangent into world->tangent + Var *worldToTangent = new Var; + worldToTangent->setType( "mat3" ); + worldToTangent->setName( "worldToTangent" ); + LangElement *worldToTangentDecl = new DecOp( worldToTangent ); + + // Get the world->obj transform + Var *worldToObj = new Var; + worldToObj->setType( "mat4" ); + worldToObj->setName( "worldToObj" ); + worldToObj->uniform = true; + worldToObj->constSortPos = cspPrimitive; + + Var *mat3Conversion = new Var; + mat3Conversion->setType( "mat3" ); + mat3Conversion->setName( "worldToObjMat3" ); + LangElement* mat3Lang = new DecOp(mat3Conversion); + meta->addStatement( new GenOp( " @ = mat3(@[0].xyz, @[1].xyz, @[2].xyz);\r\n ", mat3Lang, worldToObj, worldToObj, worldToObj) ); + + // assign world->tangent transform + meta->addStatement( new GenOp( " @ = @ * @;\r\n", worldToTangentDecl, texSpaceMat, mat3Conversion ) ); + + // send transform to pixel shader + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + Var *worldToTangentR1 = connectComp->getElement( RT_TEXCOORD ); + worldToTangentR1->setName( "worldToTangentR1" ); + worldToTangentR1->setType( "vec3" ); + meta->addStatement( new GenOp( " @ = @[0];\r\n", worldToTangentR1, worldToTangent ) ); + + Var *worldToTangentR2 = connectComp->getElement( RT_TEXCOORD ); + worldToTangentR2->setName( "worldToTangentR2" ); + worldToTangentR2->setType( "vec3" ); + meta->addStatement( new GenOp( " @ = @[1];\r\n", worldToTangentR2, worldToTangent ) ); + + Var *worldToTangentR3 = connectComp->getElement( RT_TEXCOORD ); + worldToTangentR3->setName( "worldToTangentR3" ); + worldToTangentR3->setType( "vec3" ); + meta->addStatement( new GenOp( " @ = @[2];\r\n", worldToTangentR3, worldToTangent ) ); + + // Make sure there are texcoords + if( !fd.features[MFT_DiffuseMap] ) + { + // find incoming texture var + Var *inTex = getVertTexCoord( "texCoord" ); + + // grab connector texcoord register + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outTex = connectComp->getElement( RT_TEXCOORD ); + outTex->setName( "outTexCoord" ); + outTex->setType( "vec2" ); + outTex->mapsToSampler = true; + + if( fd.features[MFT_TexAnim] ) + { + inTex->setType( "vec4" ); + + // create texture mat var + Var *texMat = new Var; + texMat->setType( "mat4" ); + texMat->setName( "texMat" ); + texMat->uniform = true; + texMat->constSortPos = cspPotentialPrimitive; + + meta->addStatement( new GenOp( " @ = @ * @;\r\n", outTex, texMat, inTex ) ); + } + else + { + // setup language elements to output incoming tex coords to output + meta->addStatement( new GenOp( " @ = @;\r\n", outTex, inTex ) ); + } + } + + output = meta; + } + else if ( fd.materialFeatures[MFT_NormalsOut] || + fd.features[MFT_IsTranslucent] || + !fd.features[MFT_RTLighting] ) + { + Parent::processVert( componentList, fd ); + return; + } + else + { + output = NULL; + } +} + +void DeferredBumpFeatGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // NULL output in case nothing gets handled + output = NULL; + + if( fd.features[MFT_PrePassConditioner] ) + { + MultiLine *meta = new MultiLine; + + // Pull the world->tangent transform from the vertex shader + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + Var *worldToTangentR1 = connectComp->getElement( RT_TEXCOORD ); + worldToTangentR1->setName( "worldToTangentR1" ); + worldToTangentR1->setType( "vec3" ); + + Var *worldToTangentR2 = connectComp->getElement( RT_TEXCOORD ); + worldToTangentR2->setName( "worldToTangentR2" ); + worldToTangentR2->setType( "vec3" ); + + Var *worldToTangentR3 = connectComp->getElement( RT_TEXCOORD ); + worldToTangentR3->setName( "worldToTangentR3" ); + worldToTangentR3->setType( "vec3" ); + + Var *worldToTangent = new Var; + worldToTangent->setType( "mat3" ); + worldToTangent->setName( "worldToTangent" ); + LangElement *worldToTangentDecl = new DecOp( worldToTangent ); + + // Build world->tangent matrix + meta->addStatement( new GenOp( " @;\r\n", worldToTangentDecl ) ); + meta->addStatement( new GenOp( " @[0] = @;\r\n", worldToTangent, worldToTangentR1 ) ); + meta->addStatement( new GenOp( " @[1] = @;\r\n", worldToTangent, worldToTangentR2 ) ); + meta->addStatement( new GenOp( " @[2] = @;\r\n", worldToTangent, worldToTangentR3 ) ); + + // create texture var + Var *bumpMap = new Var; + bumpMap->setType( "sampler2D" ); + bumpMap->setName( "bumpMap" ); + bumpMap->uniform = true; + bumpMap->sampler = true; + bumpMap->constNum = Var::getTexUnitNum(); // used as texture unit num here + + Var *texCoord = (Var*) LangElement::find( "outTexCoord" ); + if( !texCoord ) + { + // grab connector texcoord register + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + texCoord = connectComp->getElement( RT_TEXCOORD ); + texCoord->setName( "outTexCoord" ); + texCoord->setType( "vec2" ); + texCoord->mapsToSampler = true; + } + + LangElement * texOp = new GenOp( "texture2D(@, @)", bumpMap, texCoord ); + + // create bump normal + Var *bumpNorm = new Var; + bumpNorm->setName( "bumpNormal" ); + bumpNorm->setType( "vec4" ); + + LangElement *bumpNormDecl = new DecOp( bumpNorm ); + meta->addStatement( expandNormalMap( texOp, bumpNormDecl, bumpNorm, fd ) ); + + // This var is read from GBufferConditionerHLSL and + // used in the prepass output. + Var *gbNormal = new Var; + gbNormal->setName( "gbNormal" ); + gbNormal->setType( "vec3" ); + LangElement *gbNormalDecl = new DecOp( gbNormal ); + + // Normalize is done later... + // Note: The reverse mul order is intentional. Affine matrix. + meta->addStatement( new GenOp( " @ = @.xyz * @;\r\n", gbNormalDecl, bumpNorm, worldToTangent ) ); + + output = meta; + return; + } + else if ( fd.materialFeatures[MFT_NormalsOut] || + fd.features[MFT_IsTranslucent] || + !fd.features[MFT_RTLighting] ) + { + Parent::processPix( componentList, fd ); + return; + } + else if ( fd.features[MFT_PixSpecular] ) + { + Var *bumpSample = (Var *)LangElement::find( "bumpSample" ); + if( bumpSample == NULL ) + { + Var *texCoord = (Var*) LangElement::find( "outTexCoord" ); + if( !texCoord ) + { + // grab connector texcoord register + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + texCoord = connectComp->getElement( RT_TEXCOORD ); + texCoord->setName( "outTexCoord" ); + texCoord->setType( "vec2" ); + texCoord->mapsToSampler = true; + } + + Var *bumpMap = new Var; + bumpMap->setType( "sampler2D" ); + bumpMap->setName( "bumpMap" ); + bumpMap->uniform = true; + bumpMap->sampler = true; + bumpMap->constNum = Var::getTexUnitNum(); // used as texture unit num here + + bumpSample = new Var; + bumpSample->setType( "vec4" ); + bumpSample->setName( "bumpSample" ); + LangElement *bumpSampleDecl = new DecOp( bumpSample ); + + output = new GenOp( " @ = texture2D(@, @);\r\n", bumpSampleDecl, bumpMap, texCoord ); + return; + } + } + + output = NULL; +} + +ShaderFeature::Resources DeferredBumpFeatGLSL::getResources( const MaterialFeatureData &fd ) +{ + if ( fd.materialFeatures[MFT_NormalsOut] || + fd.features[MFT_IsTranslucent] || + fd.features[MFT_Parallax] || + !fd.features[MFT_RTLighting] ) + return Parent::getResources( fd ); + + Resources res; + if(!fd.features[MFT_SpecularMap]) + { + res.numTex = 1; + res.numTexReg = 1; + } + return res; +} + +void DeferredBumpFeatGLSL::setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ) +{ + if ( fd.materialFeatures[MFT_NormalsOut] || + fd.features[MFT_IsTranslucent] || + !fd.features[MFT_RTLighting] ) + { + Parent::setTexData( stageDat, fd, passData, texIndex ); + return; + } + + GFXTextureObject *normalMap = stageDat.getTex( MFT_NormalMap ); + if ( !fd.features[MFT_Parallax] && !fd.features[MFT_SpecularMap] && + ( fd.features[MFT_PrePassConditioner] || + fd.features[MFT_PixSpecular] ) && + normalMap ) + { + passData.mTexType[ texIndex ] = Material::Bump; + passData.mTexSlot[ texIndex++ ].texObject = normalMap; + } +} + + +void DeferredPixelSpecularGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + if( fd.features[MFT_IsTranslucent] || !fd.features[MFT_RTLighting] ) + { + Parent::processVert( componentList, fd ); + return; + } + output = NULL; +} + +void DeferredPixelSpecularGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + if( fd.features[MFT_IsTranslucent] || !fd.features[MFT_RTLighting] ) + { + Parent::processPix( componentList, fd ); + return; + } + + MultiLine *meta = new MultiLine; + + Var *specular = new Var; + specular->setType( "float" ); + specular->setName( "specular" ); + LangElement * specDecl = new DecOp( specular ); + + Var *specCol = (Var*)LangElement::find( "specularColor" ); + if(specCol == NULL) + { + specCol = new Var; + specCol->setType( "vec4" ); + specCol->setName( "specularColor" ); + specCol->uniform = true; + specCol->constSortPos = cspPotentialPrimitive; + } + + Var *specPow = new Var; + specPow->setType( "float" ); + specPow->setName( "specularPower" ); + + // If the gloss map flag is set, than the specular power is in the alpha + // channel of the specular map + if( fd.features[ MFT_GlossMap ] ) + meta->addStatement( new GenOp( " @ = @.a * 255;\r\n", new DecOp( specPow ), specCol ) ); + else + { + specPow->uniform = true; + specPow->constSortPos = cspPotentialPrimitive; + } + + Var *constSpecPow = new Var; + constSpecPow->setType( "float" ); + constSpecPow->setName( "constantSpecularPower" ); + constSpecPow->uniform = true; + constSpecPow->constSortPos = cspPass; + + Var *lightInfoSamp = (Var *)LangElement::find( "lightInfoSample" ); + AssertFatal( lightInfoSamp, "Something hosed the deferred features! Can't find lightInfoSample" ); + + // (a^m)^n = a^(m*n) + meta->addStatement( new GenOp( " @ = pow(d_specular, ceil(@ / @)) * d_NL_Att;\r\n", specDecl, specPow, constSpecPow ) ); + + LangElement *specMul = new GenOp( "@ * @", specCol, specular ); + LangElement *final = specMul; + + // We we have a normal map then mask the specular + if( !fd.features[MFT_SpecularMap] && fd.features[MFT_NormalMap] ) + { + Var *bumpSample = (Var*)LangElement::find( "bumpSample" ); + final = new GenOp( "@ * @.a", final, bumpSample ); + } + + // add to color + meta->addStatement( new GenOp( " @;\r\n", assignColor( final, Material::Add ) ) ); + + output = meta; +} + +ShaderFeature::Resources DeferredPixelSpecularGLSL::getResources( const MaterialFeatureData &fd ) +{ + if( fd.features[MFT_IsTranslucent] || !fd.features[MFT_RTLighting] ) + return Parent::getResources( fd ); + + Resources res; + return res; +} + + +ShaderFeature::Resources DeferredMinnaertGLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + if( !fd.features[MFT_IsTranslucent] && fd.features[MFT_RTLighting] ) + { + res.numTex = 1; + res.numTexReg = 1; + } + return res; +} + +void DeferredMinnaertGLSL::setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ) +{ + if( !fd.features[MFT_IsTranslucent] && fd.features[MFT_RTLighting] ) + { + NamedTexTarget *texTarget = NamedTexTarget::find(RenderPrePassMgr::BufferName); + if ( texTarget ) + { + passData.mTexType[ texIndex ] = Material::TexTarget; + passData.mTexSlot[ texIndex++ ].texTarget = texTarget; + } + } +} + +void DeferredMinnaertGLSL::processPixMacros( Vector ¯os, + const MaterialFeatureData &fd ) +{ + if( !fd.features[MFT_IsTranslucent] && fd.features[MFT_RTLighting] ) + { + // Pull in the uncondition method for the g buffer + NamedTexTarget *texTarget = NamedTexTarget::find( RenderPrePassMgr::BufferName ); + if ( texTarget && texTarget->getConditioner() ) + { + ConditionerMethodDependency *unconditionMethod = texTarget->getConditioner()->getConditionerMethodDependency(ConditionerFeature::UnconditionMethod); + unconditionMethod->createMethodMacro( String::ToLower(RenderPrePassMgr::BufferName) + "Uncondition", macros ); + addDependency(unconditionMethod); + } + } +} + +void DeferredMinnaertGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // If there is no deferred information, bail on this feature + if( fd.features[MFT_IsTranslucent] || !fd.features[MFT_RTLighting] ) + { + output = NULL; + return; + } + + // grab incoming vert position + Var *inVertPos = (Var*) LangElement::find( "position" ); + AssertFatal( inVertPos, "Something went bad with ShaderGen. The vertex position should be already defined." ); + + // grab output for gbuffer normal + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outWSEyeVec= connectComp->getElement( RT_TEXCOORD ); + outWSEyeVec->setName( "outWSViewVec" ); + outWSEyeVec->setType( "vec4" ); + + // create objToWorld variable + Var *objToWorld = (Var*) LangElement::find( "objTrans" ); + if( !objToWorld ) + { + objToWorld = new Var; + objToWorld->setType( "mat4x4" ); + objToWorld->setName( "objTrans" ); + objToWorld->uniform = true; + objToWorld->constSortPos = cspPrimitive; + } + + // Eye Pos world + Var *eyePosWorld = (Var*) LangElement::find( "eyePosWorld" ); + if( !eyePosWorld ) + { + eyePosWorld = new Var; + eyePosWorld->setType( "vec3" ); + eyePosWorld->setName( "eyePosWorld" ); + eyePosWorld->uniform = true; + eyePosWorld->constSortPos = cspPass; + } + + // Kick out the world-space normal + LangElement *statement = new GenOp( " @ = vec4(@, @) - vec4(@, 0.0);\r\n", + outWSEyeVec, objToWorld, inVertPos, eyePosWorld ); + output = statement; +} + +void DeferredMinnaertGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // If there is no deferred information, bail on this feature + if( fd.features[MFT_IsTranslucent] || !fd.features[MFT_RTLighting] ) + { + output = NULL; + return; + } + + Var *minnaertConstant = new Var; + minnaertConstant->setType( "float" ); + minnaertConstant->setName( "minnaertConstant" ); + minnaertConstant->uniform = true; + minnaertConstant->constSortPos = cspPotentialPrimitive; + + // create texture var + Var *prepassBuffer = new Var; + prepassBuffer->setType( "sampler2D" ); + prepassBuffer->setName( "prepassBuffer" ); + prepassBuffer->uniform = true; + prepassBuffer->sampler = true; + prepassBuffer->constNum = Var::getTexUnitNum(); // used as texture unit num here + + // Texture coord + Var *uvScene = (Var*) LangElement::find( "uvScene" ); + AssertFatal(uvScene != NULL, "Unable to find UVScene, no RTLighting feature?"); + + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *wsViewVec = (Var*) LangElement::find( "wsPos" ); + if( !wsViewVec ) + { + wsViewVec = connectComp->getElement( RT_TEXCOORD ); + wsViewVec->setName( "outWSViewVec" ); + wsViewVec->setType( "vec4" ); + wsViewVec->mapsToSampler = false; + wsViewVec->uniform = false; + } + + String unconditionPrePassMethod = String::ToLower(RenderPrePassMgr::BufferName) + "Uncondition"; + + MultiLine *meta = new MultiLine; + meta->addStatement( new GenOp( avar( " vec4 normalDepth = %s(texture2D(@, @));\r\n", unconditionPrePassMethod.c_str() ), prepassBuffer, uvScene ) ); + meta->addStatement( new GenOp( " vec3 worldViewVec = normalize(@.xyz / @.w);\r\n", wsViewVec, wsViewVec ) ); + meta->addStatement( new GenOp( " float vDotN = dot(normalDepth.xyz, worldViewVec);\r\n" ) ); + meta->addStatement( new GenOp( " float Minnaert = pow(d_NL_Att, @) * pow(vDotN, 1.0 - @);\r\n", minnaertConstant, minnaertConstant ) ); + meta->addStatement( new GenOp( " @;\r\n", assignColor( new GenOp( "vec4(Minnaert, Minnaert, Minnaert, 1.0)" ), Material::Mul ) ) ); + + output = meta; +} + + +void DeferredSubSurfaceGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // If there is no deferred information, bail on this feature + if( fd.features[MFT_IsTranslucent] || !fd.features[MFT_RTLighting] ) + { + output = NULL; + return; + } + + Var *subSurfaceParams = new Var; + subSurfaceParams->setType( "vec4" ); + subSurfaceParams->setName( "subSurfaceParams" ); + subSurfaceParams->uniform = true; + subSurfaceParams->constSortPos = cspPotentialPrimitive; + + Var *inColor = (Var*) LangElement::find( "rtShading" ); + + MultiLine *meta = new MultiLine; + meta->addStatement( new GenOp( " float subLamb = smoothstep(-@.a, 1.0, d_NL_Att) - smoothstep(0.0, 1.0, d_NL_Att);\r\n", subSurfaceParams ) ); + meta->addStatement( new GenOp( " subLamb = max(0.0, subLamb);\r\n" ) ); + meta->addStatement( new GenOp( " @;\r\n", assignColor( new GenOp( "vec4(@.rgb + (subLamb * @.rgb), 1.0)", inColor, subSurfaceParams ), Material::Mul ) ) ); + + output = meta; +} diff --git a/Engine/source/lighting/advanced/glsl/advancedLightingFeaturesGLSL.h b/Engine/source/lighting/advanced/glsl/advancedLightingFeaturesGLSL.h new file mode 100644 index 000000000..fb37848a8 --- /dev/null +++ b/Engine/source/lighting/advanced/glsl/advancedLightingFeaturesGLSL.h @@ -0,0 +1,158 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _DEFERREDFEATURESGLSL_H_ +#define _DEFERREDFEATURESGLSL_H_ + +#include "shaderGen/GLSL/shaderFeatureGLSL.h" +#include "shaderGen/GLSL/bumpGLSL.h" +#include "shaderGen/GLSL/pixSpecularGLSL.h" + +class ConditionerMethodDependency; + + +/// Lights the pixel by sampling from the light prepass buffer. It will +/// fall back to default vertex lighting functionality if +class DeferredRTLightingFeatGLSL : public RTLightingFeatGLSL +{ + typedef RTLightingFeatGLSL Parent; + +public: + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPixMacros( Vector ¯os, + const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp(){ return Material::None; } + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual void setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ); + + virtual String getName() + { + return "Deferred RT Lighting Feature"; + } +}; + + +/// Used to write the normals during the depth/normal prepass. +class DeferredBumpFeatGLSL : public BumpFeatGLSL +{ + typedef BumpFeatGLSL Parent; + +public: + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp() { return Material::LerpAlpha; } + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual void setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ); + + virtual String getName() + { + return "Bumpmap [Deferred]"; + } +}; + + +/// Generates specular highlights in the forward pass +/// from the light prepass buffer. +class DeferredPixelSpecularGLSL : public PixelSpecularGLSL +{ + typedef PixelSpecularGLSL Parent; + +public: + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual String getName() + { + return "Pixel Specular [Deferred]"; + } +}; + + +/// +class DeferredMinnaertGLSL : public ShaderFeatureGLSL +{ + typedef ShaderFeatureGLSL Parent; + +public: + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPixMacros( Vector ¯os, + const MaterialFeatureData &fd ); + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual void setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ); + + virtual String getName() + { + return "Minnaert Shading [Deferred]"; + } +}; + + +/// +class DeferredSubSurfaceGLSL : public ShaderFeatureGLSL +{ + typedef ShaderFeatureGLSL Parent; + +public: + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual String getName() + { + return "Sub-Surface Approximation [Deferred]"; + } +}; + +#endif // _DEFERREDFEATURESGLSL_H_ \ No newline at end of file diff --git a/Engine/source/lighting/advanced/glsl/gBufferConditionerGLSL.cpp b/Engine/source/lighting/advanced/glsl/gBufferConditionerGLSL.cpp new file mode 100644 index 000000000..fd3b27d85 --- /dev/null +++ b/Engine/source/lighting/advanced/glsl/gBufferConditionerGLSL.cpp @@ -0,0 +1,320 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/advanced/glsl/gBufferConditionerGLSL.h" + +#include "shaderGen/featureMgr.h" +#include "gfx/gfxStringEnumTranslate.h" +#include "materials/materialFeatureTypes.h" +#include "materials/materialFeatureData.h" + + +GBufferConditionerGLSL::GBufferConditionerGLSL( const GFXFormat bufferFormat ) : + Parent( bufferFormat ) +{ + // Figure out how we should store the normal data. These are the defaults. + mCanWriteNegativeValues = false; + mNormalStorageType = CartesianXYZ; + + // Note: We clear to a depth 1 (the w component) so + // that the unrendered parts of the scene end up + // farthest to the camera. + + switch(bufferFormat) + { + case GFXFormatR8G8B8A8: + // TODO: Some kind of logic here. Spherical is better, but is more + // expensive. + mNormalStorageType = Spherical; + mBitsPerChannel = 8; + break; + + case GFXFormatR16G16B16A16F: + // Floating point buffers don't need to encode negative values + mCanWriteNegativeValues = true; + mNormalStorageType = Spherical; + mBitsPerChannel = 16; + break; + + // Store a 32bit depth with a sperical normal in the + // integer 16 format. This gives us perfect depth + // precision and high quality normals within a 64bit + // buffer format. + case GFXFormatR16G16B16A16: + mNormalStorageType = Spherical; + mBitsPerChannel = 16; + break; + + case GFXFormatR32G32B32A32F: + mCanWriteNegativeValues = true; + mNormalStorageType = CartesianXYZ; + mBitsPerChannel = 32; + break; + + default: + AssertFatal(false, "Unsupported G-Buffer format"); + } +} + +GBufferConditionerGLSL::~GBufferConditionerGLSL() +{ +} + +void GBufferConditionerGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + output = NULL; + + if( !fd.features[MFT_NormalMap] ) + { + // grab incoming vert normal + Var *inNormal = (Var*) LangElement::find( "normal" ); + AssertFatal( inNormal, "Something went bad with ShaderGen. The normal should be already defined." ); + + // grab output for gbuffer normal + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outNormal = connectComp->getElement( RT_TEXCOORD ); + outNormal->setName( "gbNormal" ); + outNormal->setType( "vec3" ); + + // create objToWorld variable + Var *objToWorld = (Var*) LangElement::find( "objTrans" ); + if( !objToWorld ) + { + objToWorld = new Var; + objToWorld->setType( "mat4" ); + objToWorld->setName( "objTrans" ); + objToWorld->uniform = true; + objToWorld->constSortPos = cspPrimitive; + } + + // Kick out the world-space normal + LangElement *statement = new GenOp( " @ = vec3(@ * vec4(normalize(@), 0.0));\r\n", outNormal, objToWorld, inNormal ); + output = statement; + } +} + +void GBufferConditionerGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // sanity + AssertFatal( fd.features[MFT_EyeSpaceDepthOut], "No depth-out feature enabled! Bad news!" ); + + MultiLine *meta = new MultiLine; + + // grab connector normal + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *gbNormal = (Var*) LangElement::find( "gbNormal" ); + if( !gbNormal ) + { + gbNormal = connectComp->getElement( RT_TEXCOORD ); + gbNormal->setName( "gbNormal" ); + gbNormal->setType( "vec3" ); + gbNormal->mapsToSampler = false; + gbNormal->uniform = false; + } + + // find depth + ShaderFeature *depthFeat = FEATUREMGR->getByType( MFT_EyeSpaceDepthOut ); + AssertFatal( depthFeat != NULL, "No eye space depth feature found!" ); + + Var *depth = (Var*) LangElement::find(depthFeat->getOutputVarName()); + AssertFatal( depth, "Something went bad with ShaderGen. The depth should be already generated by the EyeSpaceDepthOut feature." ); + + + Var *unconditionedOut = new Var; + unconditionedOut->setType("vec4"); + unconditionedOut->setName("normal_depth"); + + LangElement *outputDecl = new DecOp( unconditionedOut ); + + // NOTE: We renormalize the normal here as they + // will not stay normalized during interpolation. + meta->addStatement( new GenOp(" @ = @;", outputDecl, new GenOp( "vec4(normalize(@), @)", gbNormal, depth ) ) ); + meta->addStatement( assignOutput( unconditionedOut ) ); + + output = meta; +} + +ShaderFeature::Resources GBufferConditionerGLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + + // Passing from VS->PS: + // - world space normal (gbNormal) + res.numTexReg = 1; + + return res; +} + +Var* GBufferConditionerGLSL::printMethodHeader( MethodType methodType, const String &methodName, Stream &stream, MultiLine *meta ) +{ + const bool isCondition = ( methodType == ConditionerFeature::ConditionMethod ); + + Var *retVal = NULL; + + // The uncondition method inputs are changed + if( isCondition ) + retVal = Parent::printMethodHeader( methodType, methodName, stream, meta ); + else + { + Var *methodVar = new Var; + methodVar->setName(methodName); + methodVar->setType("vec4"); + DecOp *methodDecl = new DecOp(methodVar); + + Var *prepassSampler = new Var; + prepassSampler->setName("prepassSamplerVar"); + prepassSampler->setType("sampler2D"); + DecOp *prepassSamplerDecl = new DecOp(prepassSampler); + + Var *screenUV = new Var; + screenUV->setName("screenUVVar"); + screenUV->setType("vec2"); + DecOp *screenUVDecl = new DecOp(screenUV); + + Var *bufferSample = new Var; + bufferSample->setName("bufferSample"); + bufferSample->setType("vec4"); + DecOp *bufferSampleDecl = new DecOp(bufferSample); + + meta->addStatement( new GenOp( "@(@, @)\r\n", methodDecl, prepassSamplerDecl, screenUVDecl ) ); + + meta->addStatement( new GenOp( "{\r\n" ) ); + + meta->addStatement( new GenOp( " // Sampler g-buffer\r\n" ) ); + + // The gbuffer has no mipmaps, so use tex2dlod when + // so that the shader compiler can optimize. + meta->addStatement( new GenOp( " @ = texture2DLod(@, @, 0.0);\r\n", bufferSampleDecl, prepassSampler, screenUV ) ); + + // We don't use this way of passing var's around, so this should cause a crash + // if something uses this improperly + retVal = bufferSample; + } + + return retVal; +} + +GenOp* GBufferConditionerGLSL::_posnegEncode( GenOp *val ) +{ + return mCanWriteNegativeValues ? val : new GenOp("0.5 * (@ + 1.0)", val); +} + +GenOp* GBufferConditionerGLSL::_posnegDecode( GenOp *val ) +{ + return mCanWriteNegativeValues ? val : new GenOp("@ * 2.0 - 1.0", val); +} + +Var* GBufferConditionerGLSL::_conditionOutput( Var *unconditionedOutput, MultiLine *meta ) +{ + Var *retVar = new Var; + retVar->setType("vec4"); + retVar->setName("_gbConditionedOutput"); + LangElement *outputDecl = new DecOp( retVar ); + + switch(mNormalStorageType) + { + case CartesianXYZ: + meta->addStatement( new GenOp( " // g-buffer conditioner: vec4(normal.xyz, depth)\r\n" ) ); + meta->addStatement( new GenOp( " @ = vec4(@, @.a);\r\n", outputDecl, + _posnegEncode(new GenOp("@.xyz", unconditionedOutput)), unconditionedOutput ) ); + break; + + case CartesianXY: + meta->addStatement( new GenOp( " // g-buffer conditioner: vec4(normal.xy, depth Hi + z-sign, depth Lo)\r\n" ) ); + meta->addStatement( new GenOp( " @ = vec4(@, @.a);", outputDecl, + _posnegEncode(new GenOp("vec3(@.xy, sign(@.z))", unconditionedOutput, unconditionedOutput)), unconditionedOutput ) ); + break; + + case Spherical: + meta->addStatement( new GenOp( " // g-buffer conditioner: vec4(normal.theta, normal.phi, depth Hi, depth Lo)\r\n" ) ); + meta->addStatement( new GenOp( " @ = vec4(@, 0.0, @.a);\r\n", outputDecl, + _posnegEncode(new GenOp("vec2(atan2(@.y, @.x) / 3.14159265358979323846f, @.z)", unconditionedOutput, unconditionedOutput, unconditionedOutput ) ), + unconditionedOutput ) ); + break; + } + + // Encode depth into two channels + if(mNormalStorageType != CartesianXYZ) + { + const U64 maxValPerChannel = 1 << mBitsPerChannel; + const U64 extraVal = (maxValPerChannel * maxValPerChannel - 1) - (maxValPerChannel - 1) * 2; + meta->addStatement( new GenOp( " \r\n // Encode depth into hi/lo\r\n" ) ); + meta->addStatement( new GenOp( avar( " vec3 _tempDepth = fract(@.a * vec3(1.0, %llu.0, %llu.0));\r\n", maxValPerChannel - 1, extraVal ), + unconditionedOutput ) ); + meta->addStatement( new GenOp( avar( " @.zw = _tempDepth.xy - _tempDepth.yz * vec2(1.0/%llu.0, 1.0/%llu.0);\r\n\r\n", maxValPerChannel - 1, maxValPerChannel - 1 ), + retVar ) ); + } + + AssertFatal( retVar != NULL, avar( "Cannot condition output to buffer format: %s", GFXStringTextureFormat[getBufferFormat()] ) ); + return retVar; +} + +Var* GBufferConditionerGLSL::_unconditionInput( Var *conditionedInput, MultiLine *meta ) +{ + Var *retVar = new Var; + retVar->setType("vec4"); + retVar->setName("_gbUnconditionedInput"); + LangElement *outputDecl = new DecOp( retVar ); + + switch(mNormalStorageType) + { + case CartesianXYZ: + meta->addStatement( new GenOp( " // g-buffer unconditioner: vec4(normal.xyz, depth)\r\n" ) ); + meta->addStatement( new GenOp( " @ = vec4(@, @.a);\r\n", outputDecl, + _posnegDecode(new GenOp("@.xyz", conditionedInput)), conditionedInput ) ); + break; + + case CartesianXY: + meta->addStatement( new GenOp( " // g-buffer unconditioner: vec4(normal.xy, depth Hi + z-sign, depth Lo)\r\n" ) ); + meta->addStatement( new GenOp( " @ = vec4(@, @.a);\r\n", outputDecl, + _posnegDecode(new GenOp("@.xyz", conditionedInput)), conditionedInput ) ); + meta->addStatement( new GenOp( " @.z *= sqrt(1.0 - dot(@.xy, @.xy));\r\n", retVar, retVar, retVar ) ); + break; + + case Spherical: + meta->addStatement( new GenOp( " // g-buffer unconditioner: vec4(normal.theta, normal.phi, depth Hi, depth Lo)\r\n" ) ); + meta->addStatement( new GenOp( " vec2 spGPUAngles = @;\r\n", _posnegDecode(new GenOp("@.xy", conditionedInput)) ) ); + meta->addStatement( new GenOp( " vec2 sincosTheta;\r\n" ) ); + meta->addStatement( new GenOp( " sincosTheta.x = sin(spGPUAngles.x * 3.14159265358979323846);\r\n" ) ); + meta->addStatement( new GenOp( " sincosTheta.y = cos(spGPUAngles.x * 3.14159265358979323846);\r\n" ) ); + meta->addStatement( new GenOp( " vec2 sincosPhi = vec2(sqrt(1.0 - spGPUAngles.y * spGPUAngles.y), spGPUAngles.y);\r\n" ) ); + meta->addStatement( new GenOp( " @ = vec4(sincosTheta.y * sincosPhi.x, sincosTheta.x * sincosPhi.x, sincosPhi.y, @.a);\r\n", outputDecl, conditionedInput ) ); + break; + } + + // Recover depth from encoding + if(mNormalStorageType != CartesianXYZ) + { + const U64 maxValPerChannel = 1 << mBitsPerChannel; + meta->addStatement( new GenOp( " \r\n // Decode depth\r\n" ) ); + meta->addStatement( new GenOp( avar( " @.w = dot( @.zw, vec2(1.0, 1.0/%llu.0));\r\n", maxValPerChannel - 1 ), + retVar, conditionedInput ) ); + } + + + AssertFatal( retVar != NULL, avar( "Cannot uncondition input from buffer format: %s", GFXStringTextureFormat[getBufferFormat()] ) ); + return retVar; +} + diff --git a/Engine/source/lighting/advanced/glsl/gBufferConditionerGLSL.h b/Engine/source/lighting/advanced/glsl/gBufferConditionerGLSL.h new file mode 100644 index 000000000..9e9364c88 --- /dev/null +++ b/Engine/source/lighting/advanced/glsl/gBufferConditionerGLSL.h @@ -0,0 +1,74 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GBUFFER_CONDITIONER_GLSL_H_ +#define _GBUFFER_CONDITIONER_GLSL_H_ + +#ifndef _CONDITIONER_BASE_H_ +#include "shaderGen/conditionerFeature.h" +#endif +#ifndef _SHADEROP_H_ +#include "shaderGen/shaderOp.h" +#endif + + +/// +class GBufferConditionerGLSL : public ConditionerFeature +{ + typedef ConditionerFeature Parent; + +public: + enum NormalStorage + { + CartesianXYZ, + CartesianXY, + Spherical, + }; + +protected: + + NormalStorage mNormalStorageType; + bool mCanWriteNegativeValues; + U32 mBitsPerChannel; + +public: + + GBufferConditionerGLSL( const GFXFormat bufferFormat ); + virtual ~GBufferConditionerGLSL(); + + + virtual void processVert( Vector &componentList, const MaterialFeatureData &fd ); + virtual void processPix( Vector &componentList, const MaterialFeatureData &fd ); + virtual Resources getResources( const MaterialFeatureData &fd ); + virtual String getName() { return "GBuffer Conditioner"; } + +protected: + + virtual Var *printMethodHeader( MethodType methodType, const String &methodName, Stream &stream, MultiLine *meta ); + + virtual GenOp* _posnegEncode( GenOp *val ); + virtual GenOp* _posnegDecode( GenOp *val ); + virtual Var* _conditionOutput( Var *unconditionedOutput, MultiLine *meta ); + virtual Var* _unconditionInput( Var *conditionedInput, MultiLine *meta ); +}; + +#endif // _GBUFFER_CONDITIONER_GLSL_H_ diff --git a/Engine/source/lighting/advanced/hlsl/advancedLightingFeaturesHLSL.cpp b/Engine/source/lighting/advanced/hlsl/advancedLightingFeaturesHLSL.cpp new file mode 100644 index 000000000..d9893c8cd --- /dev/null +++ b/Engine/source/lighting/advanced/hlsl/advancedLightingFeaturesHLSL.cpp @@ -0,0 +1,636 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/advanced/hlsl/advancedLightingFeaturesHLSL.h" + +#include "lighting/advanced/advancedLightBinManager.h" +#include "shaderGen/langElement.h" +#include "shaderGen/shaderOp.h" +#include "shaderGen/conditionerFeature.h" +#include "renderInstance/renderPrePassMgr.h" +#include "materials/processedMaterial.h" +#include "materials/materialFeatureTypes.h" + + +void DeferredRTLightingFeatHLSL::processPixMacros( Vector ¯os, + const MaterialFeatureData &fd ) +{ + // Skip deferred features, and use forward shading instead + if ( fd.features[MFT_ForwardShading] ) + { + Parent::processPixMacros( macros, fd ); + return; + } + + // Pull in the uncondition method for the light info buffer + NamedTexTarget *texTarget = NamedTexTarget::find( AdvancedLightBinManager::smBufferName ); + if ( texTarget && texTarget->getConditioner() ) + { + ConditionerMethodDependency *unconditionMethod = texTarget->getConditioner()->getConditionerMethodDependency(ConditionerFeature::UnconditionMethod); + unconditionMethod->createMethodMacro( String::ToLower( AdvancedLightBinManager::smBufferName ) + "Uncondition", macros ); + addDependency(unconditionMethod); + } +} + +void DeferredRTLightingFeatHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // Skip deferred features, and use forward shading instead + if ( fd.features[MFT_ForwardShading] ) + { + Parent::processVert( componentList, fd ); + return; + } + + // Pass screen space position to pixel shader to compute a full screen buffer uv + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *ssPos = connectComp->getElement( RT_TEXCOORD ); + ssPos->setName( "screenspacePos" ); + ssPos->setStructName( "OUT" ); + ssPos->setType( "float4" ); + + Var *outPosition = (Var*) LangElement::find( "hpos" ); + AssertFatal( outPosition, "No hpos, ohnoes." ); + + output = new GenOp( " @ = @;\r\n", ssPos, outPosition ); +} + +void DeferredRTLightingFeatHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // Skip deferred features, and use forward shading instead + if ( fd.features[MFT_ForwardShading] ) + { + Parent::processPix( componentList, fd ); + return; + } + + MultiLine *meta = new MultiLine; + + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *ssPos = connectComp->getElement( RT_TEXCOORD ); + ssPos->setName( "screenspacePos" ); + ssPos->setStructName( "IN" ); + ssPos->setType( "float4" ); + + Var *uvScene = new Var; + uvScene->setType( "float2" ); + uvScene->setName( "uvScene" ); + LangElement *uvSceneDecl = new DecOp( uvScene ); + + String rtParamName = String::ToString( "rtParams%d", mLastTexIndex ); + Var *rtParams = (Var*) LangElement::find( rtParamName ); + if( !rtParams ) + { + rtParams = new Var; + rtParams->setType( "float4" ); + rtParams->setName( rtParamName ); + rtParams->uniform = true; + rtParams->constSortPos = cspPass; + } + + meta->addStatement( new GenOp( " @ = @.xy / @.w;\r\n", uvSceneDecl, ssPos, ssPos ) ); // get the screen coord... its -1 to +1 + meta->addStatement( new GenOp( " @ = ( @ + 1.0 ) / 2.0;\r\n", uvScene, uvScene ) ); // get the screen coord to 0 to 1 + meta->addStatement( new GenOp( " @.y = 1.0 - @.y;\r\n", uvScene, uvScene ) ); // flip the y axis + meta->addStatement( new GenOp( " @ = ( @ * @.zw ) + @.xy;\r\n", uvScene, uvScene, rtParams, rtParams) ); // scale it down and offset it to the rt size + + Var *lightInfoSamp = new Var; + lightInfoSamp->setType( "float4" ); + lightInfoSamp->setName( "lightInfoSample" ); + + // create texture var + Var *lightInfoBuffer = new Var; + lightInfoBuffer->setType( "sampler2D" ); + lightInfoBuffer->setName( "lightInfoBuffer" ); + lightInfoBuffer->uniform = true; + lightInfoBuffer->sampler = true; + lightInfoBuffer->constNum = Var::getTexUnitNum(); // used as texture unit num here + + // Declare the RTLighting variables in this feature, they will either be assigned + // in this feature, or in the tonemap/lightmap feature + Var *d_lightcolor = new Var( "d_lightcolor", "float3" ); + meta->addStatement( new GenOp( " @;\r\n", new DecOp( d_lightcolor ) ) ); + + Var *d_NL_Att = new Var( "d_NL_Att", "float" ); + meta->addStatement( new GenOp( " @;\r\n", new DecOp( d_NL_Att ) ) ); + + Var *d_specular = new Var( "d_specular", "float" ); + meta->addStatement( new GenOp( " @;\r\n", new DecOp( d_specular ) ) ); + + + // Perform the uncondition here. + String unconditionLightInfo = String::ToLower( AdvancedLightBinManager::smBufferName ) + "Uncondition"; + meta->addStatement( new GenOp( avar( " %s(tex2D(@, @), @, @, @);\r\n", + unconditionLightInfo.c_str() ), lightInfoBuffer, uvScene, d_lightcolor, d_NL_Att, d_specular ) ); + + // If this has an interlaced pre-pass, do averaging here + if( fd.features[MFT_InterlacedPrePass] ) + { + Var *oneOverTargetSize = (Var*) LangElement::find( "oneOverTargetSize" ); + if( !oneOverTargetSize ) + { + oneOverTargetSize = new Var; + oneOverTargetSize->setType( "float2" ); + oneOverTargetSize->setName( "oneOverTargetSize" ); + oneOverTargetSize->uniform = true; + oneOverTargetSize->constSortPos = cspPass; + } + + meta->addStatement( new GenOp( " float id_NL_Att, id_specular;\r\n float3 id_lightcolor;\r\n" ) ); + meta->addStatement( new GenOp( avar( " %s(tex2D(@, @ + float2(0.0, @.y)), id_lightcolor, id_NL_Att, id_specular);\r\n", + unconditionLightInfo.c_str() ), lightInfoBuffer, uvScene, oneOverTargetSize ) ); + + meta->addStatement( new GenOp(" @ = lerp(@, id_lightcolor, 0.5);\r\n", d_lightcolor, d_lightcolor ) ); + meta->addStatement( new GenOp(" @ = lerp(@, id_NL_Att, 0.5);\r\n", d_NL_Att, d_NL_Att ) ); + meta->addStatement( new GenOp(" @ = lerp(@, id_specular, 0.5);\r\n", d_specular, d_specular ) ); + } + + // This is kind of weak sauce + if( !fd.features[MFT_VertLit] && !fd.features[MFT_ToneMap] && !fd.features[MFT_LightMap] && !fd.features[MFT_SubSurface] ) + meta->addStatement( new GenOp( " @;\r\n", assignColor( new GenOp( "float4(@, 1.0)", d_lightcolor ), Material::Mul ) ) ); + + output = meta; +} + +ShaderFeature::Resources DeferredRTLightingFeatHLSL::getResources( const MaterialFeatureData &fd ) +{ + // Skip deferred features, and use forward shading instead + if ( fd.features[MFT_ForwardShading] ) + return Parent::getResources( fd ); + + // HACK: See DeferredRTLightingFeatHLSL::setTexData. + mLastTexIndex = 0; + + Resources res; + res.numTex = 1; + res.numTexReg = 1; + return res; +} + +void DeferredRTLightingFeatHLSL::setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ) +{ + // Skip deferred features, and use forward shading instead + if ( fd.features[MFT_ForwardShading] ) + { + Parent::setTexData( stageDat, fd, passData, texIndex ); + return; + } + + NamedTexTarget *texTarget = NamedTexTarget::find( AdvancedLightBinManager::smBufferName ); + if( texTarget ) + { + // HACK: We store this for use in DeferredRTLightingFeatHLSL::processPix() + // which cannot deduce the texture unit itself. + mLastTexIndex = texIndex; + + passData.mTexType[ texIndex ] = Material::TexTarget; + passData.mTexSlot[ texIndex++ ].texTarget = texTarget; + } +} + + +void DeferredBumpFeatHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + if( fd.features[MFT_PrePassConditioner] ) + { + // There is an output conditioner active, so we need to supply a transform + // to the pixel shader. + MultiLine *meta = new MultiLine; + + // We need the view to tangent space transform in the pixel shader. + getOutViewToTangent( componentList, meta, fd ); + + // Make sure there are texcoords + if( !fd.features[MFT_Parallax] && !fd.features[MFT_DiffuseMap] ) + { + const bool useTexAnim = fd.features[MFT_TexAnim]; + + getOutTexCoord( "texCoord", + "float2", + true, + useTexAnim, + meta, + componentList ); + + if ( fd.features.hasFeature( MFT_DetailNormalMap ) ) + addOutDetailTexCoord( componentList, + meta, + useTexAnim ); + } + + output = meta; + } + else if ( fd.materialFeatures[MFT_NormalsOut] || + fd.features[MFT_ForwardShading] || + !fd.features[MFT_RTLighting] ) + { + Parent::processVert( componentList, fd ); + return; + } + else + { + output = NULL; + } +} + +void DeferredBumpFeatHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // NULL output in case nothing gets handled + output = NULL; + + if( fd.features[MFT_PrePassConditioner] ) + { + MultiLine *meta = new MultiLine; + + Var *viewToTangent = getInViewToTangent( componentList ); + + // create texture var + Var *bumpMap = getNormalMapTex(); + Var *texCoord = getInTexCoord( "texCoord", "float2", true, componentList ); + LangElement *texOp = new GenOp( "tex2D(@, @)", bumpMap, texCoord ); + + // create bump normal + Var *bumpNorm = new Var; + bumpNorm->setName( "bumpNormal" ); + bumpNorm->setType( "float4" ); + + LangElement *bumpNormDecl = new DecOp( bumpNorm ); + meta->addStatement( expandNormalMap( texOp, bumpNormDecl, bumpNorm, fd ) ); + + // If we have a detail normal map we add the xy coords of + // it to the base normal map. This gives us the effect we + // want with few instructions and minial artifacts. + if ( fd.features.hasFeature( MFT_DetailNormalMap ) ) + { + bumpMap = new Var; + bumpMap->setType( "sampler2D" ); + bumpMap->setName( "detailBumpMap" ); + bumpMap->uniform = true; + bumpMap->sampler = true; + bumpMap->constNum = Var::getTexUnitNum(); + + texCoord = getInTexCoord( "detCoord", "float2", true, componentList ); + texOp = new GenOp( "tex2D(@, @)", bumpMap, texCoord ); + + Var *detailBump = new Var; + detailBump->setName( "detailBump" ); + detailBump->setType( "float4" ); + meta->addStatement( expandNormalMap( texOp, new DecOp( detailBump ), detailBump, fd ) ); + + Var *detailBumpScale = new Var; + detailBumpScale->setType( "float" ); + detailBumpScale->setName( "detailBumpStrength" ); + detailBumpScale->uniform = true; + detailBumpScale->constSortPos = cspPass; + meta->addStatement( new GenOp( " @.xy += @.xy * @;\r\n", bumpNorm, detailBump, detailBumpScale ) ); + } + + // This var is read from GBufferConditionerHLSL and + // used in the prepass output. + // + // By using the 'half' type here we get a bunch of partial + // precision optimized code on further operations on the normal + // which helps alot on older Geforce cards. + // + Var *gbNormal = new Var; + gbNormal->setName( "gbNormal" ); + gbNormal->setType( "half3" ); + LangElement *gbNormalDecl = new DecOp( gbNormal ); + + // Normalize is done later... + // Note: The reverse mul order is intentional. Affine matrix. + meta->addStatement( new GenOp( " @ = (half3)mul( @.xyz, @ );\r\n", gbNormalDecl, bumpNorm, viewToTangent ) ); + + output = meta; + return; + } + else if ( fd.materialFeatures[MFT_NormalsOut] || + fd.features[MFT_ForwardShading] || + !fd.features[MFT_RTLighting] ) + { + Parent::processPix( componentList, fd ); + return; + } + else if ( fd.features[MFT_PixSpecular] && !fd.features[MFT_SpecularMap] ) + { + Var *bumpSample = (Var *)LangElement::find( "bumpSample" ); + if( bumpSample == NULL ) + { + Var *texCoord = getInTexCoord( "texCoord", "float2", true, componentList ); + + Var *bumpMap = getNormalMapTex(); + + bumpSample = new Var; + bumpSample->setType( "float4" ); + bumpSample->setName( "bumpSample" ); + LangElement *bumpSampleDecl = new DecOp( bumpSample ); + + output = new GenOp( " @ = tex2D(@, @);\r\n", bumpSampleDecl, bumpMap, texCoord ); + return; + } + } + + output = NULL; +} + +ShaderFeature::Resources DeferredBumpFeatHLSL::getResources( const MaterialFeatureData &fd ) +{ + if ( fd.materialFeatures[MFT_NormalsOut] || + fd.features[MFT_ForwardShading] || + fd.features[MFT_Parallax] || + !fd.features[MFT_RTLighting] ) + return Parent::getResources( fd ); + + Resources res; + if(!fd.features[MFT_SpecularMap]) + { + res.numTex = 1; + res.numTexReg = 1; + + if ( fd.features[MFT_PrePassConditioner] && + fd.features.hasFeature( MFT_DetailNormalMap ) ) + { + res.numTex += 1; + if ( !fd.features.hasFeature( MFT_DetailMap ) ) + res.numTexReg += 1; + } + } + + return res; +} + +void DeferredBumpFeatHLSL::setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ) +{ + if ( fd.materialFeatures[MFT_NormalsOut] || + fd.features[MFT_ForwardShading] || + !fd.features[MFT_RTLighting] ) + { + Parent::setTexData( stageDat, fd, passData, texIndex ); + return; + } + + if ( !fd.features[MFT_Parallax] && !fd.features[MFT_SpecularMap] && + ( fd.features[MFT_PrePassConditioner] || + fd.features[MFT_PixSpecular] ) ) + { + passData.mTexType[ texIndex ] = Material::Bump; + passData.mTexSlot[ texIndex++ ].texObject = stageDat.getTex( MFT_NormalMap ); + + if ( fd.features[MFT_PrePassConditioner] && + fd.features.hasFeature( MFT_DetailNormalMap ) ) + { + passData.mTexType[ texIndex ] = Material::DetailBump; + passData.mTexSlot[ texIndex++ ].texObject = stageDat.getTex( MFT_DetailNormalMap ); + } + } +} + + +void DeferredPixelSpecularHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + if( fd.features[MFT_ForwardShading] || !fd.features[MFT_RTLighting] ) + { + Parent::processVert( componentList, fd ); + return; + } + output = NULL; +} + +void DeferredPixelSpecularHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + if( fd.features[MFT_ForwardShading] || !fd.features[MFT_RTLighting] ) + { + Parent::processPix( componentList, fd ); + return; + } + + MultiLine *meta = new MultiLine; + + Var *specular = new Var; + specular->setType( "float" ); + specular->setName( "specular" ); + LangElement * specDecl = new DecOp( specular ); + + Var *specCol = (Var*)LangElement::find( "specularColor" ); + if(specCol == NULL) + { + specCol = new Var; + specCol->setType( "float4" ); + specCol->setName( "specularColor" ); + specCol->uniform = true; + specCol->constSortPos = cspPotentialPrimitive; + } + + Var *specPow = new Var; + specPow->setType( "float" ); + specPow->setName( "specularPower" ); + + // If the gloss map flag is set, than the specular power is in the alpha + // channel of the specular map + if( fd.features[ MFT_GlossMap ] ) + meta->addStatement( new GenOp( " @ = @.a * 255;\r\n", new DecOp( specPow ), specCol ) ); + else + { + specPow->uniform = true; + specPow->constSortPos = cspPotentialPrimitive; + } + + Var *lightInfoSamp = (Var *)LangElement::find( "lightInfoSample" ); + Var *d_specular = (Var*)LangElement::find( "d_specular" ); + Var *d_NL_Att = (Var*)LangElement::find( "d_NL_Att" ); + + AssertFatal( lightInfoSamp && d_specular && d_NL_Att, + "DeferredPixelSpecularHLSL::processPix - Something hosed the deferred features!" ); + + // (a^m)^n = a^(m*n) + meta->addStatement( new GenOp( " @ = pow( @, ceil(@ / AL_ConstantSpecularPower)) * @;\r\n", + specDecl, d_specular, specPow, d_NL_Att ) ); + + LangElement *specMul = new GenOp( "float4( @.rgb, 0 ) * @", specCol, specular ); + LangElement *final = specMul; + + // We we have a normal map then mask the specular + if( !fd.features[MFT_SpecularMap] && fd.features[MFT_NormalMap] ) + { + Var *bumpSample = (Var*)LangElement::find( "bumpSample" ); + final = new GenOp( "@ * @.a", final, bumpSample ); + } + + // add to color + meta->addStatement( new GenOp( " @;\r\n", assignColor( final, Material::Add ) ) ); + + output = meta; +} + +ShaderFeature::Resources DeferredPixelSpecularHLSL::getResources( const MaterialFeatureData &fd ) +{ + if( fd.features[MFT_ForwardShading] || !fd.features[MFT_RTLighting] ) + return Parent::getResources( fd ); + + Resources res; + return res; +} + + +ShaderFeature::Resources DeferredMinnaertHLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + if( !fd.features[MFT_ForwardShading] && fd.features[MFT_RTLighting] ) + { + res.numTex = 1; + res.numTexReg = 1; + } + return res; +} + +void DeferredMinnaertHLSL::setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ) +{ + if( !fd.features[MFT_ForwardShading] && fd.features[MFT_RTLighting] ) + { + NamedTexTarget *texTarget = NamedTexTarget::find(RenderPrePassMgr::BufferName); + if ( texTarget ) + { + passData.mTexType[ texIndex ] = Material::TexTarget; + passData.mTexSlot[ texIndex++ ].texTarget = texTarget; + } + } +} + +void DeferredMinnaertHLSL::processPixMacros( Vector ¯os, + const MaterialFeatureData &fd ) +{ + if( !fd.features[MFT_ForwardShading] && fd.features[MFT_RTLighting] ) + { + // Pull in the uncondition method for the g buffer + NamedTexTarget *texTarget = NamedTexTarget::find( RenderPrePassMgr::BufferName ); + if ( texTarget && texTarget->getConditioner() ) + { + ConditionerMethodDependency *unconditionMethod = texTarget->getConditioner()->getConditionerMethodDependency(ConditionerFeature::UnconditionMethod); + unconditionMethod->createMethodMacro( String::ToLower(RenderPrePassMgr::BufferName) + "Uncondition", macros ); + addDependency(unconditionMethod); + } + } +} + +void DeferredMinnaertHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // If there is no deferred information, bail on this feature + if( fd.features[MFT_ForwardShading] || !fd.features[MFT_RTLighting] ) + { + output = NULL; + return; + } + + // Make sure we pass the world space position to the + // pixel shader so we can calculate a view vector. + MultiLine *meta = new MultiLine; + addOutWsPosition( componentList, fd.features[MFT_UseInstancing], meta ); + output = meta; +} + +void DeferredMinnaertHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // If there is no deferred information, bail on this feature + if( fd.features[MFT_ForwardShading] || !fd.features[MFT_RTLighting] ) + { + output = NULL; + return; + } + + Var *minnaertConstant = new Var; + minnaertConstant->setType( "float" ); + minnaertConstant->setName( "minnaertConstant" ); + minnaertConstant->uniform = true; + minnaertConstant->constSortPos = cspPotentialPrimitive; + + // create texture var + Var *prepassBuffer = new Var; + prepassBuffer->setType( "sampler2D" ); + prepassBuffer->setName( "prepassBuffer" ); + prepassBuffer->uniform = true; + prepassBuffer->sampler = true; + prepassBuffer->constNum = Var::getTexUnitNum(); // used as texture unit num here + + // Texture coord + Var *uvScene = (Var*) LangElement::find( "uvScene" ); + AssertFatal(uvScene != NULL, "Unable to find UVScene, no RTLighting feature?"); + + MultiLine *meta = new MultiLine; + + // Get the world space view vector. + Var *wsViewVec = getWsView( getInWsPosition( componentList ), meta ); + + String unconditionPrePassMethod = String::ToLower(RenderPrePassMgr::BufferName) + "Uncondition"; + + Var *d_NL_Att = (Var*)LangElement::find( "d_NL_Att" ); + + meta->addStatement( new GenOp( avar( " float4 normalDepth = %s(@, @);\r\n", unconditionPrePassMethod.c_str() ), prepassBuffer, uvScene ) ); + meta->addStatement( new GenOp( " float vDotN = dot(normalDepth.xyz, @);\r\n", wsViewVec ) ); + meta->addStatement( new GenOp( " float Minnaert = pow( @, @) * pow(vDotN, 1.0 - @);\r\n", d_NL_Att, minnaertConstant, minnaertConstant ) ); + meta->addStatement( new GenOp( " @;\r\n", assignColor( new GenOp( "float4(Minnaert, Minnaert, Minnaert, 1.0)" ), Material::Mul ) ) ); + + output = meta; +} + + +void DeferredSubSurfaceHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // If there is no deferred information, bail on this feature + if( fd.features[MFT_ForwardShading] || !fd.features[MFT_RTLighting] ) + { + output = NULL; + return; + } + + Var *subSurfaceParams = new Var; + subSurfaceParams->setType( "float4" ); + subSurfaceParams->setName( "subSurfaceParams" ); + subSurfaceParams->uniform = true; + subSurfaceParams->constSortPos = cspPotentialPrimitive; + + Var *d_lightcolor = (Var*)LangElement::find( "d_lightcolor" ); + Var *d_NL_Att = (Var*)LangElement::find( "d_NL_Att" ); + + MultiLine *meta = new MultiLine; + meta->addStatement( new GenOp( " float subLamb = smoothstep(-@.a, 1.0, @) - smoothstep(0.0, 1.0, @);\r\n", subSurfaceParams, d_NL_Att, d_NL_Att ) ); + meta->addStatement( new GenOp( " subLamb = max(0.0, subLamb);\r\n" ) ); + meta->addStatement( new GenOp( " @;\r\n", assignColor( new GenOp( "float4(@ + (subLamb * @.rgb), 1.0)", d_lightcolor, subSurfaceParams ), Material::Mul ) ) ); + + output = meta; +} diff --git a/Engine/source/lighting/advanced/hlsl/advancedLightingFeaturesHLSL.h b/Engine/source/lighting/advanced/hlsl/advancedLightingFeaturesHLSL.h new file mode 100644 index 000000000..d1035398c --- /dev/null +++ b/Engine/source/lighting/advanced/hlsl/advancedLightingFeaturesHLSL.h @@ -0,0 +1,170 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _DEFERREDFEATURESHLSL_H_ +#define _DEFERREDFEATURESHLSL_H_ + +#include "shaderGen/HLSL/shaderFeatureHLSL.h" +#include "shaderGen/HLSL/bumpHLSL.h" +#include "shaderGen/HLSL/pixSpecularHLSL.h" + +class ConditionerMethodDependency; + + +/// Lights the pixel by sampling from the light prepass +/// buffer. It will fall back to forward lighting +/// functionality for non-deferred rendered surfaces. +/// +/// Also note that this feature is only used in the +/// forward rendering pass. It is not used during the +/// prepass step. +/// +class DeferredRTLightingFeatHLSL : public RTLightingFeatHLSL +{ + typedef RTLightingFeatHLSL Parent; + +protected: + + /// @see DeferredRTLightingFeatHLSL::processPix() + U32 mLastTexIndex; + +public: + + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPixMacros( Vector ¯os, + const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp(){ return Material::None; } + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual void setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ); + + virtual String getName() + { + return "Deferred RT Lighting"; + } +}; + + +/// This is used during the +class DeferredBumpFeatHLSL : public BumpFeatHLSL +{ + typedef BumpFeatHLSL Parent; + +public: + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp() { return Material::LerpAlpha; } + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual void setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ); + + virtual String getName() + { + return "Bumpmap [Deferred]"; + } +}; + + +/// Generates specular highlights in the forward pass +/// from the light prepass buffer. +class DeferredPixelSpecularHLSL : public PixelSpecularHLSL +{ + typedef PixelSpecularHLSL Parent; + +public: + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual String getName() + { + return "Pixel Specular [Deferred]"; + } +}; + + +/// +class DeferredMinnaertHLSL : public ShaderFeatureHLSL +{ + typedef ShaderFeatureHLSL Parent; + +public: + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPixMacros( Vector ¯os, + const MaterialFeatureData &fd ); + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual void setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ); + + virtual String getName() + { + return "Minnaert Shading [Deferred]"; + } +}; + + +/// +class DeferredSubSurfaceHLSL : public ShaderFeatureHLSL +{ + typedef ShaderFeatureHLSL Parent; + +public: + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual String getName() + { + return "Sub-Surface Approximation [Deferred]"; + } +}; + +#endif // _DEFERREDFEATURESHLSL_H_ \ No newline at end of file diff --git a/Engine/source/lighting/advanced/hlsl/gBufferConditionerHLSL.cpp b/Engine/source/lighting/advanced/hlsl/gBufferConditionerHLSL.cpp new file mode 100644 index 000000000..d4d30f9ad --- /dev/null +++ b/Engine/source/lighting/advanced/hlsl/gBufferConditionerHLSL.cpp @@ -0,0 +1,404 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/advanced/hlsl/gBufferConditionerHLSL.h" + +#include "shaderGen/featureMgr.h" +#include "gfx/gfxStringEnumTranslate.h" +#include "materials/materialFeatureTypes.h" +#include "materials/materialFeatureData.h" +#include "shaderGen/hlsl/shaderFeatureHLSL.h" + + +GBufferConditionerHLSL::GBufferConditionerHLSL( const GFXFormat bufferFormat, const NormalSpace nrmSpace ) : + Parent( bufferFormat ) +{ + // Figure out how we should store the normal data. These are the defaults. + mCanWriteNegativeValues = false; + mNormalStorageType = CartesianXYZ; + + // Note: We clear to a depth 1 (the w component) so + // that the unrendered parts of the scene end up + // farthest to the camera. + const NormalStorage &twoCmpNrmStorageType = ( nrmSpace == WorldSpace ? Spherical : LambertAzimuthal ); + switch(bufferFormat) + { + case GFXFormatR8G8B8A8: + mNormalStorageType = twoCmpNrmStorageType; + mBitsPerChannel = 8; + break; + + case GFXFormatR16G16B16A16F: + // Floating point buffers don't need to encode negative values + mCanWriteNegativeValues = true; + mNormalStorageType = twoCmpNrmStorageType; + mBitsPerChannel = 16; + break; + + // Store a 32bit depth with a sperical normal in the + // integer 16 format. This gives us perfect depth + // precision and high quality normals within a 64bit + // buffer format. + case GFXFormatR16G16B16A16: + mNormalStorageType = twoCmpNrmStorageType; + mBitsPerChannel = 16; + break; + + case GFXFormatR32G32B32A32F: + mCanWriteNegativeValues = true; + mNormalStorageType = CartesianXYZ; + mBitsPerChannel = 32; + break; + + default: + AssertFatal(false, "Unsupported G-Buffer format"); + } +} + +GBufferConditionerHLSL::~GBufferConditionerHLSL() +{ +} + +void GBufferConditionerHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // If we have a normal map then that feature will + // take care of passing gbNormal to the pixel shader. + if ( fd.features[MFT_NormalMap] ) + return; + + MultiLine *meta = new MultiLine; + output = meta; + + // grab incoming vert normal + Var *inNormal = (Var*) LangElement::find( "normal" ); + AssertFatal( inNormal, "Something went bad with ShaderGen. The normal should be already defined." ); + + // grab output for gbuffer normal + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outNormal = connectComp->getElement( RT_TEXCOORD ); + outNormal->setName( "gbNormal" ); + outNormal->setStructName( "OUT" ); + outNormal->setType( "float3" ); + + if( !fd.features[MFT_ParticleNormal] ) + { + // Kick out the view-space normal + + // TODO: Total hack because Conditioner is directly derived + // from ShaderFeature and not from ShaderFeatureHLSL. + NamedFeatureHLSL dummy( String::EmptyString ); + dummy.mInstancingFormat = mInstancingFormat; + Var *worldViewOnly = dummy.getWorldView( componentList, fd.features[MFT_UseInstancing], meta ); + + meta->addStatement( new GenOp(" @ = mul(@, float4( normalize(@), 0.0 ) ).xyz;\r\n", + outNormal, worldViewOnly, inNormal ) ); + } + else + { + // Assume the particle normal generator has already put this in view space + // and normalized it + meta->addStatement( new GenOp( " @ = @;\r\n", outNormal, inNormal ) ); + } +} + +void GBufferConditionerHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // sanity + AssertFatal( fd.features[MFT_EyeSpaceDepthOut], "No depth-out feature enabled! Bad news!" ); + + MultiLine *meta = new MultiLine; + + // grab connector normal + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *gbNormal = (Var*) LangElement::find( "gbNormal" ); + if( !gbNormal ) + { + gbNormal = connectComp->getElement( RT_TEXCOORD ); + gbNormal->setName( "gbNormal" ); + gbNormal->setStructName( "IN" ); + gbNormal->setType( "float3" ); + gbNormal->mapsToSampler = false; + gbNormal->uniform = false; + } + + // find depth + ShaderFeature *depthFeat = FEATUREMGR->getByType( MFT_EyeSpaceDepthOut ); + AssertFatal( depthFeat != NULL, "No eye space depth feature found!" ); + + Var *depth = (Var*) LangElement::find(depthFeat->getOutputVarName()); + AssertFatal( depth, "Something went bad with ShaderGen. The depth should be already generated by the EyeSpaceDepthOut feature." ); + + + Var *unconditionedOut = new Var; + unconditionedOut->setType("float4"); + unconditionedOut->setName("normal_depth"); + + LangElement *outputDecl = new DecOp( unconditionedOut ); + + // If we're doing prepass blending then we need + // to steal away the alpha channel before the + // conditioner stomps on it. + Var *alphaVal = NULL; + if ( fd.features[ MFT_IsTranslucentZWrite ] ) + { + alphaVal = new Var( "outAlpha", "float" ); + meta->addStatement( new GenOp( " @ = OUT.col.a; // MFT_IsTranslucentZWrite\r\n", new DecOp( alphaVal ) ) ); + } + + // If using interlaced normals, invert the normal + if(fd.features[MFT_InterlacedPrePass]) + { + // NOTE: Its safe to not call ShaderFeatureHLSL::addOutVpos() in the vertex + // shader as for SM 3.0 nothing is needed there. + Var *Vpos = ShaderFeatureHLSL::getInVpos( meta, componentList ); + + Var *iGBNormal = new Var( "interlacedGBNormal", "float3" ); + meta->addStatement(new GenOp(" @ = (frac(@.y * 0.5) < 0.1 ? reflect(@, float3(0.0, -1.0, 0.0)) : @);\r\n", new DecOp(iGBNormal), Vpos, gbNormal, gbNormal)); + gbNormal = iGBNormal; + } + + // NOTE: We renormalize the normal here as they + // will not stay normalized during interpolation. + meta->addStatement( new GenOp(" @ = @;", outputDecl, new GenOp( "float4(normalize(@), @)", gbNormal, depth ) ) ); + meta->addStatement( assignOutput( unconditionedOut ) ); + + // If we have an alpha var then we're doing prepass lerp blending. + if ( alphaVal ) + { + Var *outColor = (Var*)LangElement::find( getOutputTargetVarName( DefaultTarget ) ); + meta->addStatement( new GenOp( " @.ba = float2( 0, @ ); // MFT_IsTranslucentZWrite\r\n", outColor, alphaVal ) ); + } + + output = meta; +} + +ShaderFeature::Resources GBufferConditionerHLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + + // Passing from VS->PS: + // - world space normal (gbNormal) + res.numTexReg = 1; + + return res; +} + +Var* GBufferConditionerHLSL::printMethodHeader( MethodType methodType, const String &methodName, Stream &stream, MultiLine *meta ) +{ + const bool isCondition = ( methodType == ConditionerFeature::ConditionMethod ); + + Var *retVal = NULL; + + // The uncondition method inputs are changed + if( isCondition ) + retVal = Parent::printMethodHeader( methodType, methodName, stream, meta ); + else + { + Var *methodVar = new Var; + methodVar->setName(methodName); + methodVar->setType("inline float4"); + DecOp *methodDecl = new DecOp(methodVar); + + Var *prepassSampler = new Var; + prepassSampler->setName("prepassSamplerVar"); + prepassSampler->setType("sampler2D"); + DecOp *prepassSamplerDecl = new DecOp(prepassSampler); + + Var *screenUV = new Var; + screenUV->setName("screenUVVar"); + screenUV->setType("float2"); + DecOp *screenUVDecl = new DecOp(screenUV); + + Var *bufferSample = new Var; + bufferSample->setName("bufferSample"); + bufferSample->setType("float4"); + DecOp *bufferSampleDecl = new DecOp(bufferSample); + + meta->addStatement( new GenOp( "@(@, @)\r\n", methodDecl, prepassSamplerDecl, screenUVDecl ) ); + + meta->addStatement( new GenOp( "{\r\n" ) ); + + meta->addStatement( new GenOp( " // Sampler g-buffer\r\n" ) ); + +#ifdef TORQUE_OS_XENON + meta->addStatement( new GenOp( " @;\r\n", bufferSampleDecl ) ); + meta->addStatement( new GenOp( " asm { tfetch2D @, @, @, MagFilter = point, MinFilter = point, MipFilter = point };\r\n", bufferSample, screenUV, prepassSampler ) ); +#else + // The gbuffer has no mipmaps, so use tex2dlod when + // possible so that the shader compiler can optimize. + meta->addStatement( new GenOp( " #if TORQUE_SM >= 30\r\n" ) ); + meta->addStatement( new GenOp( " @ = tex2Dlod(@, float4(@,0,0));\r\n", bufferSampleDecl, prepassSampler, screenUV ) ); + meta->addStatement( new GenOp( " #else\r\n" ) ); + meta->addStatement( new GenOp( " @ = tex2D(@, @);\r\n", bufferSampleDecl, prepassSampler, screenUV ) ); + meta->addStatement( new GenOp( " #endif\r\n\r\n" ) ); +#endif + + // We don't use this way of passing var's around, so this should cause a crash + // if something uses this improperly + retVal = bufferSample; + } + + return retVal; +} + +GenOp* GBufferConditionerHLSL::_posnegEncode( GenOp *val ) +{ + if(mNormalStorageType == LambertAzimuthal) + return mCanWriteNegativeValues ? val : new GenOp(avar("(%f * (@ + %f))", 1.0f/(M_SQRT2_F * 2.0f), M_SQRT2_F), val); + else + return mCanWriteNegativeValues ? val : new GenOp("(0.5 * (@ + 1.0))", val); +} + +GenOp* GBufferConditionerHLSL::_posnegDecode( GenOp *val ) +{ + if(mNormalStorageType == LambertAzimuthal) + return mCanWriteNegativeValues ? val : new GenOp(avar("(@ * %f - %f)", M_SQRT2_F * 2.0f, M_SQRT2_F), val); + else + return mCanWriteNegativeValues ? val : new GenOp("(@ * 2.0 - 1.0)", val); +} + +Var* GBufferConditionerHLSL::_conditionOutput( Var *unconditionedOutput, MultiLine *meta ) +{ + Var *retVar = new Var; + retVar->setType("float4"); + retVar->setName("_gbConditionedOutput"); + LangElement *outputDecl = new DecOp( retVar ); + + switch(mNormalStorageType) + { + case CartesianXYZ: + meta->addStatement( new GenOp( " // g-buffer conditioner: float4(normal.xyz, depth)\r\n" ) ); + meta->addStatement( new GenOp( " @ = float4(@, @.a);\r\n", outputDecl, + _posnegEncode(new GenOp("@.xyz", unconditionedOutput)), unconditionedOutput ) ); + break; + + case CartesianXY: + meta->addStatement( new GenOp( " // g-buffer conditioner: float4(normal.xy, depth Hi + z-sign, depth Lo)\r\n" ) ); + meta->addStatement( new GenOp( " @ = float4(@, @.a);", outputDecl, + _posnegEncode(new GenOp("float3(@.xy, sign(@.z))", unconditionedOutput, unconditionedOutput)), unconditionedOutput ) ); + break; + + case Spherical: + meta->addStatement( new GenOp( " // g-buffer conditioner: float4(normal.theta, normal.phi, depth Hi, depth Lo)\r\n" ) ); + meta->addStatement( new GenOp( " @ = float4(@, 0.0, @.a);\r\n", outputDecl, + _posnegEncode(new GenOp("float2(atan2(@.y, @.x) / 3.14159265358979323846f, @.z)", unconditionedOutput, unconditionedOutput, unconditionedOutput ) ), + unconditionedOutput ) ); + + // HACK: This fixes the noise present when using a floating point + // gbuffer on Geforce cards and the "flat areas unlit" issues. + // + // We need work around atan2() above to fix this issue correctly + // without the extra overhead of this test. + // + meta->addStatement( new GenOp( " if ( abs( dot( @.xyz, float3( 0.0, 0.0, 1.0 ) ) ) > 0.999f ) @ = float4( 0, 1 * sign( @.z ), 0, @.a );\r\n", + unconditionedOutput, retVar, unconditionedOutput, unconditionedOutput ) ); + break; + + case LambertAzimuthal: + //http://en.wikipedia.org/wiki/Lambert_azimuthal_equal-area_projection + // + // Note we're casting to half to use partial precision + // sqrt which is much faster on older Geforces while + // still being acceptable for normals. + // + meta->addStatement( new GenOp( " // g-buffer conditioner: float4(normal.X, normal.Y, depth Hi, depth Lo)\r\n" ) ); + meta->addStatement( new GenOp( " @ = float4(@, 0.0, @.a);\r\n", outputDecl, + _posnegEncode(new GenOp("sqrt(half(2.0/(1.0 - @.y))) * half2(@.xz)", unconditionedOutput, unconditionedOutput)), + unconditionedOutput ) ); + break; + } + + // Encode depth into two channels + if(mNormalStorageType != CartesianXYZ) + { + const U64 maxValPerChannel = 1 << mBitsPerChannel; + meta->addStatement( new GenOp( " \r\n // Encode depth into hi/lo\r\n" ) ); + meta->addStatement( new GenOp( avar( " float2 _tempDepth = frac(@.a * float2(1.0, %llu.0));\r\n", maxValPerChannel - 1 ), + unconditionedOutput ) ); + meta->addStatement( new GenOp( avar( " @.zw = _tempDepth.xy - _tempDepth.yy * float2(1.0/%llu.0, 0.0);\r\n\r\n", maxValPerChannel - 1 ), + retVar ) ); + } + + AssertFatal( retVar != NULL, avar( "Cannot condition output to buffer format: %s", GFXStringTextureFormat[getBufferFormat()] ) ); + return retVar; +} + +Var* GBufferConditionerHLSL::_unconditionInput( Var *conditionedInput, MultiLine *meta ) +{ + Var *retVar = new Var; + retVar->setType("float4"); + retVar->setName("_gbUnconditionedInput"); + LangElement *outputDecl = new DecOp( retVar ); + + switch(mNormalStorageType) + { + case CartesianXYZ: + meta->addStatement( new GenOp( " // g-buffer unconditioner: float4(normal.xyz, depth)\r\n" ) ); + meta->addStatement( new GenOp( " @ = float4(@, @.a);\r\n", outputDecl, + _posnegDecode(new GenOp("@.xyz", conditionedInput)), conditionedInput ) ); + break; + + case CartesianXY: + meta->addStatement( new GenOp( " // g-buffer unconditioner: float4(normal.xy, depth Hi + z-sign, depth Lo)\r\n" ) ); + meta->addStatement( new GenOp( " @ = float4(@, @.a);\r\n", outputDecl, + _posnegDecode(new GenOp("@.xyz", conditionedInput)), conditionedInput ) ); + meta->addStatement( new GenOp( " @.z *= sqrt(1.0 - dot(@.xy, @.xy));\r\n", retVar, retVar, retVar ) ); + break; + + case Spherical: + meta->addStatement( new GenOp( " // g-buffer unconditioner: float4(normal.theta, normal.phi, depth Hi, depth Lo)\r\n" ) ); + meta->addStatement( new GenOp( " float2 spGPUAngles = @;\r\n", _posnegDecode(new GenOp("@.xy", conditionedInput)) ) ); + meta->addStatement( new GenOp( " float2 sincosTheta;\r\n" ) ); + meta->addStatement( new GenOp( " sincos(spGPUAngles.x * 3.14159265358979323846f, sincosTheta.x, sincosTheta.y);\r\n" ) ); + meta->addStatement( new GenOp( " float2 sincosPhi = float2(sqrt(1.0 - spGPUAngles.y * spGPUAngles.y), spGPUAngles.y);\r\n" ) ); + meta->addStatement( new GenOp( " @ = float4(sincosTheta.y * sincosPhi.x, sincosTheta.x * sincosPhi.x, sincosPhi.y, @.a);\r\n", outputDecl, conditionedInput ) ); + break; + + case LambertAzimuthal: + // Note we're casting to half to use partial precision + // sqrt which is much faster on older Geforces while + // still being acceptable for normals. + // + meta->addStatement( new GenOp( " // g-buffer unconditioner: float4(normal.X, normal.Y, depth Hi, depth Lo)\r\n" ) ); + meta->addStatement( new GenOp( " float2 _inpXY = @;\r\n", _posnegDecode(new GenOp("@.xy", conditionedInput)) ) ); + meta->addStatement( new GenOp( " float _xySQ = dot(_inpXY, _inpXY);\r\n" ) ); + meta->addStatement( new GenOp( " @ = float4( sqrt(half(1.0 - (_xySQ / 4.0))) * _inpXY, -1.0 + (_xySQ / 2.0), @.a).xzyw;\r\n", outputDecl, conditionedInput ) ); + break; + } + + // Recover depth from encoding + if(mNormalStorageType != CartesianXYZ) + { + const U64 maxValPerChannel = 1 << mBitsPerChannel; + meta->addStatement( new GenOp( " \r\n // Decode depth\r\n" ) ); + meta->addStatement( new GenOp( avar( " @.w = dot( @.zw, float2(1.0, 1.0/%llu.0));\r\n", maxValPerChannel - 1 ), + retVar, conditionedInput ) ); + } + + + AssertFatal( retVar != NULL, avar( "Cannot uncondition input from buffer format: %s", GFXStringTextureFormat[getBufferFormat()] ) ); + return retVar; +} + diff --git a/Engine/source/lighting/advanced/hlsl/gBufferConditionerHLSL.h b/Engine/source/lighting/advanced/hlsl/gBufferConditionerHLSL.h new file mode 100644 index 000000000..b28a05d79 --- /dev/null +++ b/Engine/source/lighting/advanced/hlsl/gBufferConditionerHLSL.h @@ -0,0 +1,81 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _GBUFFER_CONDITIONER_HLSL_H_ +#define _GBUFFER_CONDITIONER_HLSL_H_ + +#ifndef _CONDITIONER_BASE_H_ +#include "shaderGen/conditionerFeature.h" +#endif +#ifndef _SHADEROP_H_ +#include "shaderGen/shaderOp.h" +#endif + + +/// +class GBufferConditionerHLSL : public ConditionerFeature +{ + typedef ConditionerFeature Parent; + +public: + enum NormalStorage + { + CartesianXYZ, + CartesianXY, + Spherical, + LambertAzimuthal, + }; + + enum NormalSpace + { + WorldSpace, + ViewSpace, + }; + +protected: + + NormalStorage mNormalStorageType; + bool mCanWriteNegativeValues; + U32 mBitsPerChannel; + +public: + + GBufferConditionerHLSL( const GFXFormat bufferFormat, const NormalSpace nrmSpace ); + virtual ~GBufferConditionerHLSL(); + + + virtual void processVert( Vector &componentList, const MaterialFeatureData &fd ); + virtual void processPix( Vector &componentList, const MaterialFeatureData &fd ); + virtual Resources getResources( const MaterialFeatureData &fd ); + virtual String getName() { return "GBuffer Conditioner"; } + +protected: + + virtual Var *printMethodHeader( MethodType methodType, const String &methodName, Stream &stream, MultiLine *meta ); + + virtual GenOp* _posnegEncode( GenOp *val ); + virtual GenOp* _posnegDecode( GenOp *val ); + virtual Var* _conditionOutput( Var *unconditionedOutput, MultiLine *meta ); + virtual Var* _unconditionInput( Var *conditionedInput, MultiLine *meta ); +}; + +#endif // _GBUFFER_CONDITIONER_HLSL_H_ diff --git a/Engine/source/lighting/basic/basicLightManager.cpp b/Engine/source/lighting/basic/basicLightManager.cpp new file mode 100644 index 000000000..18db77cb6 --- /dev/null +++ b/Engine/source/lighting/basic/basicLightManager.cpp @@ -0,0 +1,412 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/basic/basicLightManager.h" + +#include "platform/platformTimer.h" +#include "console/simSet.h" +#include "console/consoleTypes.h" +#include "core/module.h" +#include "core/util/safeDelete.h" +#include "materials/processedMaterial.h" +#include "shaderGen/shaderFeature.h" +#include "lighting/basic/basicSceneObjectLightingPlugin.h" +#include "shaderGen/shaderGenVars.h" +#include "gfx/gfxShader.h" +#include "materials/sceneData.h" +#include "materials/materialParameters.h" +#include "materials/materialManager.h" +#include "materials/materialFeatureTypes.h" +#include "math/util/frustum.h" +#include "scene/sceneObject.h" +#include "renderInstance/renderPrePassMgr.h" +#include "shaderGen/featureMgr.h" +#include "shaderGen/HLSL/shaderFeatureHLSL.h" +#include "shaderGen/HLSL/bumpHLSL.h" +#include "shaderGen/HLSL/pixSpecularHLSL.h" +#include "lighting/basic/blInteriorSystem.h" +#include "lighting/basic/blTerrainSystem.h" +#include "lighting/common/projectedShadow.h" + + +#ifdef TORQUE_OS_MAC +#include "shaderGen/GLSL/shaderFeatureGLSL.h" +#include "shaderGen/GLSL/bumpGLSL.h" +#include "shaderGen/GLSL/pixSpecularGLSL.h" +#endif + + +MODULE_BEGIN( BasicLightManager ) + + MODULE_SHUTDOWN_AFTER( Scene ) + + MODULE_INIT + { + ManagedSingleton< BasicLightManager >::createSingleton(); + } + + MODULE_SHUTDOWN + { + ManagedSingleton< BasicLightManager >::deleteSingleton(); + } + +MODULE_END; + + +U32 BasicLightManager::smActiveShadowPlugins = 0; +U32 BasicLightManager::smShadowsUpdated = 0; +U32 BasicLightManager::smElapsedUpdateMs = 0; + +F32 BasicLightManager::smProjectedShadowFilterDistance = 40.0f; + +static S32 QSORT_CALLBACK comparePluginScores( const void *a, const void *b ) +{ + const BasicSceneObjectLightingPlugin *A = *((BasicSceneObjectLightingPlugin**)a); + const BasicSceneObjectLightingPlugin *B = *((BasicSceneObjectLightingPlugin**)b); + + F32 dif = B->getScore() - A->getScore(); + return (S32)mFloor( dif ); +} + +BasicLightManager::BasicLightManager() + : LightManager( "Basic Lighting", "BLM" ), + mLastShader(NULL), + mLastConstants(NULL) +{ + mTimer = PlatformTimer::create(); + + mInteriorSystem = new blInteriorSystem; + mTerrainSystem = new blTerrainSystem; + + getSceneLightingInterface()->registerSystem( mInteriorSystem ); + getSceneLightingInterface()->registerSystem( mTerrainSystem ); + + Con::addVariable( "$BasicLightManagerStats::activePlugins", + TypeS32, &smActiveShadowPlugins, + "The number of active Basic Lighting SceneObjectLightingPlugin objects this frame.\n" + "@ingroup BasicLighting\n" ); + + Con::addVariable( "$BasicLightManagerStats::shadowsUpdated", + TypeS32, &smShadowsUpdated, + "The number of Basic Lighting shadows updated this frame.\n" + "@ingroup BasicLighting\n" ); + + Con::addVariable( "$BasicLightManagerStats::elapsedUpdateMs", + TypeS32, &smElapsedUpdateMs, + "The number of milliseconds spent this frame updating Basic Lighting shadows.\n" + "@ingroup BasicLighting\n" ); + + Con::addVariable( "$BasicLightManager::shadowFilterDistance", + TypeF32, &smProjectedShadowFilterDistance, + "The maximum distance in meters that projected shadows will get soft filtering.\n" + "@ingroup BasicLighting\n" ); + + Con::addVariable( "$pref::ProjectedShadow::fadeStartPixelSize", + TypeF32, &ProjectedShadow::smFadeStartPixelSize, + "A size in pixels at which BL shadows begin to fade out. " + "This should be a larger value than fadeEndPixelSize.\n" + "@see DecalData\n" + "@ingroup BasicLighting\n" ); + + Con::addVariable( "$pref::ProjectedShadow::fadeEndPixelSize", + TypeF32, &ProjectedShadow::smFadeEndPixelSize, + "A size in pixels at which BL shadows are fully faded out. " + "This should be a smaller value than fadeStartPixelSize.\n" + "@see DecalData\n" + "@ingroup BasicLighting\n" ); +} + +BasicLightManager::~BasicLightManager() +{ + mLastShader = NULL; + mLastConstants = NULL; + + for (LightConstantMap::Iterator i = mConstantLookup.begin(); i != mConstantLookup.end(); i++) + { + if (i->value) + SAFE_DELETE(i->value); + } + mConstantLookup.clear(); + + if (mTimer) + SAFE_DELETE( mTimer ); + + SAFE_DELETE( mTerrainSystem ); + SAFE_DELETE( mInteriorSystem ); +} + +bool BasicLightManager::isCompatible() const +{ + // As long as we have some shaders this works. + return GFX->getPixelShaderVersion() > 1.0; +} + +void BasicLightManager::activate( SceneManager *sceneManager ) +{ + Parent::activate( sceneManager ); + + if( GFX->getAdapterType() == OpenGL ) + { + #ifdef TORQUE_OS_MAC + FEATUREMGR->registerFeature( MFT_LightMap, new LightmapFeatGLSL ); + FEATUREMGR->registerFeature( MFT_ToneMap, new TonemapFeatGLSL ); + FEATUREMGR->registerFeature( MFT_NormalMap, new BumpFeatGLSL ); + FEATUREMGR->registerFeature( MFT_RTLighting, new RTLightingFeatGLSL ); + FEATUREMGR->registerFeature( MFT_PixSpecular, new PixelSpecularGLSL ); + #endif + } + else + { + #ifndef TORQUE_OS_MAC + FEATUREMGR->registerFeature( MFT_LightMap, new LightmapFeatHLSL ); + FEATUREMGR->registerFeature( MFT_ToneMap, new TonemapFeatHLSL ); + FEATUREMGR->registerFeature( MFT_NormalMap, new BumpFeatHLSL ); + FEATUREMGR->registerFeature( MFT_RTLighting, new RTLightingFeatHLSL ); + FEATUREMGR->registerFeature( MFT_PixSpecular, new PixelSpecularHLSL ); + #endif + } + + FEATUREMGR->unregisterFeature( MFT_MinnaertShading ); + FEATUREMGR->unregisterFeature( MFT_SubSurface ); + + // First look for the prepass bin... + RenderPrePassMgr *prePassBin = _findPrePassRenderBin(); + + /* + // If you would like to use forward shading, and have a linear depth pre-pass + // than un-comment this code block. + if ( !prePassBin ) + { + Vector formats; + formats.push_back( GFXFormatR32F ); + formats.push_back( GFXFormatR16F ); + formats.push_back( GFXFormatR8G8B8A8 ); + GFXFormat linearDepthFormat = GFX->selectSupportedFormat( &GFXDefaultRenderTargetProfile, + formats, + true, + false ); + + // Uncomment this for a no-color-write z-fill pass. + //linearDepthFormat = GFXFormat_COUNT; + + prePassBin = new RenderPrePassMgr( linearDepthFormat != GFXFormat_COUNT, linearDepthFormat ); + prePassBin->registerObject(); + rpm->addManager( prePassBin ); + } + */ + mPrePassRenderBin = prePassBin; + + // If there is a prepass bin + MATMGR->setPrePassEnabled( mPrePassRenderBin.isValid() ); + sceneManager->setPostEffectFog( mPrePassRenderBin.isValid() && mPrePassRenderBin->getTargetChainLength() > 0 ); + + // Tell the material manager that we don't use prepass. + MATMGR->setPrePassEnabled( false ); + + GFXShader::addGlobalMacro( "TORQUE_BASIC_LIGHTING" ); + + // Hook into the SceneManager prerender signal. + sceneManager->getPreRenderSignal().notify( this, &BasicLightManager::_onPreRender ); + + // Last thing... let everyone know we're active. + smActivateSignal.trigger( getId(), true ); +} + +void BasicLightManager::deactivate() +{ + Parent::deactivate(); + + mLastShader = NULL; + mLastConstants = NULL; + + for (LightConstantMap::Iterator i = mConstantLookup.begin(); i != mConstantLookup.end(); i++) + { + if (i->value) + SAFE_DELETE(i->value); + } + mConstantLookup.clear(); + + if ( mPrePassRenderBin ) + mPrePassRenderBin->deleteObject(); + mPrePassRenderBin = NULL; + + GFXShader::removeGlobalMacro( "TORQUE_BASIC_LIGHTING" ); + + // Remove us from the prerender signal. + getSceneManager()->getPreRenderSignal().remove( this, &BasicLightManager::_onPreRender ); + + // Now let everyone know we've deactivated. + smActivateSignal.trigger( getId(), false ); +} + +void BasicLightManager::_onPreRender( SceneManager *sceneManger, const SceneRenderState *state ) +{ + // Update all our shadow plugins here! + Vector *pluginInsts = BasicSceneObjectLightingPlugin::getPluginInstances(); + + Vector::const_iterator pluginIter = (*pluginInsts).begin(); + for ( ; pluginIter != (*pluginInsts).end(); pluginIter++ ) + { + BasicSceneObjectLightingPlugin *plugin = *pluginIter; + plugin->updateShadow( (SceneRenderState*)state ); + } + + U32 pluginCount = (*pluginInsts).size(); + + // Sort them by the score. + dQsort( (*pluginInsts).address(), pluginCount, sizeof(BasicSceneObjectLightingPlugin*), comparePluginScores ); + + mTimer->getElapsedMs(); + mTimer->reset(); + U32 numUpdated = 0; + U32 targetMs = 5; + + S32 updateMs = 0; + + pluginIter = (*pluginInsts).begin(); + for ( ; pluginIter != (*pluginInsts).end(); pluginIter++ ) + { + BasicSceneObjectLightingPlugin *plugin = *pluginIter; + + // If we run out of update time then stop. + updateMs = mTimer->getElapsedMs(); + if ( updateMs >= targetMs ) + break; + + // NOTE! Fix this all up to past const SceneRenderState! + plugin->renderShadow( (SceneRenderState*)state ); + numUpdated++; + } + + smShadowsUpdated = numUpdated; + smActiveShadowPlugins = pluginCount; + smElapsedUpdateMs = updateMs; +} + +BasicLightManager::LightingShaderConstants::LightingShaderConstants() + : mInit( false ), + mShader( NULL ), + mLightPosition( NULL ), + mLightDiffuse( NULL ), + mLightAmbient( NULL ), + mLightInvRadiusSq( NULL ), + mLightSpotDir( NULL ), + mLightSpotAngle( NULL ), + mLightSpotFalloff( NULL ) +{ +} + +BasicLightManager::LightingShaderConstants::~LightingShaderConstants() +{ + if (mShader.isValid()) + { + mShader->getReloadSignal().remove( this, &LightingShaderConstants::_onShaderReload ); + mShader = NULL; + } +} + +void BasicLightManager::LightingShaderConstants::init(GFXShader* shader) +{ + if (mShader.getPointer() != shader) + { + if (mShader.isValid()) + mShader->getReloadSignal().remove( this, &LightingShaderConstants::_onShaderReload ); + + mShader = shader; + mShader->getReloadSignal().notify( this, &LightingShaderConstants::_onShaderReload ); + } + + mLightPosition = shader->getShaderConstHandle( ShaderGenVars::lightPosition ); + mLightDiffuse = shader->getShaderConstHandle( ShaderGenVars::lightDiffuse); + mLightInvRadiusSq = shader->getShaderConstHandle( ShaderGenVars::lightInvRadiusSq ); + mLightAmbient = shader->getShaderConstHandle( ShaderGenVars::lightAmbient ); + mLightSpotDir = shader->getShaderConstHandle( ShaderGenVars::lightSpotDir ); + mLightSpotAngle = shader->getShaderConstHandle( ShaderGenVars::lightSpotAngle ); + mLightSpotFalloff = shader->getShaderConstHandle( ShaderGenVars::lightSpotFalloff ); + + mInit = true; +} + +void BasicLightManager::LightingShaderConstants::_onShaderReload() +{ + if (mShader.isValid()) + init( mShader ); +} + +void BasicLightManager::setLightInfo( ProcessedMaterial* pmat, + const Material* mat, + const SceneData& sgData, + const SceneRenderState *state, + U32 pass, + GFXShaderConstBuffer* shaderConsts ) +{ + PROFILE_SCOPE( BasicLightManager_SetLightInfo ); + + GFXShader *shader = shaderConsts->getShader(); + + // Check to see if this is the same shader. Since we + // sort by material we should get hit repeatedly by the + // same one. This optimization should save us many + // hash table lookups. + if ( mLastShader.getPointer() != shader ) + { + LightConstantMap::Iterator iter = mConstantLookup.find(shader); + if ( iter != mConstantLookup.end() ) + { + mLastConstants = iter->value; + } + else + { + LightingShaderConstants* lsc = new LightingShaderConstants(); + mConstantLookup[shader] = lsc; + + mLastConstants = lsc; + } + + // Set our new shader + mLastShader = shader; + } + + // Make sure that our current lighting constants are initialized + if (!mLastConstants->mInit) + mLastConstants->init(shader); + + // NOTE: If you encounter a crash from this point forward + // while setting a shader constant its probably because the + // mConstantLookup has bad shaders/constants in it. + // + // This is a known crash bug that can occur if materials/shaders + // are reloaded and the light manager is not reset. + // + // We should look to fix this by clearing the table. + + _update4LightConsts( sgData, + mLastConstants->mLightPosition, + mLastConstants->mLightDiffuse, + mLastConstants->mLightAmbient, + mLastConstants->mLightInvRadiusSq, + mLastConstants->mLightSpotDir, + mLastConstants->mLightSpotAngle, + mLastConstants->mLightSpotFalloff, + shaderConsts ); +} diff --git a/Engine/source/lighting/basic/basicLightManager.h b/Engine/source/lighting/basic/basicLightManager.h new file mode 100644 index 000000000..4da1972be --- /dev/null +++ b/Engine/source/lighting/basic/basicLightManager.h @@ -0,0 +1,134 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _BASICLIGHTMANAGER_H_ +#define _BASICLIGHTMANAGER_H_ + +#ifndef _LIGHTMANAGER_H_ +#include "lighting/lightManager.h" +#endif +#ifndef _TDICTIONARY_H_ +#include "core/util/tDictionary.h" +#endif +#ifndef _GFXSHADER_H_ +#include "gfx/gfxShader.h" +#endif +#ifndef _SIMOBJECT_H_ +#include "console/simObject.h" +#endif +#ifndef _TSINGLETON_H_ +#include "core/util/tSingleton.h" +#endif + +class AvailableSLInterfaces; +class GFXShaderConstHandle; +class RenderPrePassMgr; +class PlatformTimer; + +class blInteriorSystem; +class blTerrainSystem; + +class BasicLightManager : public LightManager +{ + typedef LightManager Parent; + + // For access to protected constructor. + friend class ManagedSingleton; + +public: + + // LightManager + virtual bool isCompatible() const; + virtual void activate( SceneManager *sceneManager ); + virtual void deactivate(); + virtual void setLightInfo(ProcessedMaterial* pmat, const Material* mat, const SceneData& sgData, const SceneRenderState *state, U32 pass, GFXShaderConstBuffer* shaderConsts); + virtual bool setTextureStage(const SceneData& sgData, const U32 currTexFlag, const U32 textureSlot, GFXShaderConstBuffer* shaderConsts, ShaderConstHandles* handles) { return false; } + + static F32 getShadowFilterDistance() { return smProjectedShadowFilterDistance; } + +protected: + + // LightManager + virtual void _addLightInfoEx( LightInfo *lightInfo ) { } + virtual void _initLightFields() { } + + void _onPreRender( SceneManager *sceneManger, const SceneRenderState *state ); + + // These are protected because we're a singleton and + // no one else should be creating us! + BasicLightManager(); + virtual ~BasicLightManager(); + + SimObjectPtr mPrePassRenderBin; + + struct LightingShaderConstants + { + bool mInit; + + GFXShaderRef mShader; + + GFXShaderConstHandle *mLightPosition; + GFXShaderConstHandle *mLightDiffuse; + GFXShaderConstHandle *mLightAmbient; + GFXShaderConstHandle *mLightInvRadiusSq; + GFXShaderConstHandle *mLightSpotDir; + GFXShaderConstHandle *mLightSpotAngle; + GFXShaderConstHandle *mLightSpotFalloff; + + LightingShaderConstants(); + ~LightingShaderConstants(); + + void init( GFXShader *shader ); + + void _onShaderReload(); + }; + + typedef Map LightConstantMap; + + LightConstantMap mConstantLookup; + GFXShaderRef mLastShader; + LightingShaderConstants* mLastConstants; + + /// Statics used for light manager/projected shadow metrics. + static U32 smActiveShadowPlugins; + static U32 smShadowsUpdated; + static U32 smElapsedUpdateMs; + + /// This is used to determine the distance + /// at which the shadow filtering PostEffect + /// will be enabled for ProjectedShadow. + static F32 smProjectedShadowFilterDistance; + + /// A timer used for tracking update time. + PlatformTimer *mTimer; + + blInteriorSystem* mInteriorSystem; + blTerrainSystem* mTerrainSystem; + +public: + // For ManagedSingleton. + static const char* getSingletonName() { return "BasicLightManager"; } +}; + +#define BLM ManagedSingleton::instance() + +#endif // _BASICLIGHTMANAGER_H_ diff --git a/Engine/source/lighting/basic/basicSceneObjectLightingPlugin.cpp b/Engine/source/lighting/basic/basicSceneObjectLightingPlugin.cpp new file mode 100644 index 000000000..b475d15a4 --- /dev/null +++ b/Engine/source/lighting/basic/basicSceneObjectLightingPlugin.cpp @@ -0,0 +1,230 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/basic/basicSceneObjectLightingPlugin.h" + +#include "lighting/lightManager.h" +#include "lighting/shadowMap/shadowMapPass.h" +#include "lighting/shadowMap/shadowMapManager.h" +#include "lighting/common/projectedShadow.h" +#include "T3D/shapeBase.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxTransformSaver.h" +#include "ts/tsRenderState.h" +#include "gfx/sim/cubemapData.h" +#include "scene/reflector.h" +#include "T3D/decal/decalManager.h" +#include "core/module.h" + + +MODULE_BEGIN( BasicSceneObjectLightingPlugin ) + + MODULE_INIT + { + BasicSceneObjectPluginFactory::createSingleton(); + } + + MODULE_SHUTDOWN + { + BasicSceneObjectPluginFactory::deleteSingleton(); + } + +MODULE_END; + + +static const U32 shadowObjectTypeMask = PlayerObjectType | CorpseObjectType | ItemObjectType | VehicleObjectType; +Vector BasicSceneObjectLightingPlugin::smPluginInstances( __FILE__, __LINE__ ); + +BasicSceneObjectLightingPlugin::BasicSceneObjectLightingPlugin(SceneObject* parent) + : mParentObject( parent ) +{ + mShadow = NULL; + + // Stick us on the list. + smPluginInstances.push_back( this ); +} + +BasicSceneObjectLightingPlugin::~BasicSceneObjectLightingPlugin() +{ + SAFE_DELETE( mShadow ); + + // Delete us from the list. + smPluginInstances.remove( this ); +} + +void BasicSceneObjectLightingPlugin::reset() +{ + SAFE_DELETE( mShadow ); +} + +void BasicSceneObjectLightingPlugin::cleanupPluginInstances() +{ + for (U32 i = 0; i < smPluginInstances.size(); i++) + { + BasicSceneObjectLightingPlugin *plug = smPluginInstances[i]; + smPluginInstances.remove( plug ); + delete plug; + i--; + } + + smPluginInstances.clear(); +} + +void BasicSceneObjectLightingPlugin::resetAll() +{ + for( U32 i = 0, num = smPluginInstances.size(); i < num; ++ i ) + smPluginInstances[ i ]->reset(); +} + +const F32 BasicSceneObjectLightingPlugin::getScore() const +{ + return mShadow ? mShadow->getScore() : 0.0f; +} + +void BasicSceneObjectLightingPlugin::updateShadow( SceneRenderState *state ) +{ + if ( !mShadow ) + mShadow = new ProjectedShadow( mParentObject ); + + mShadow->update( state ); +} + +void BasicSceneObjectLightingPlugin::renderShadow( SceneRenderState *state ) +{ + // hack until new scenegraph in place + GFXTransformSaver ts; + + TSRenderState rstate; + rstate.setSceneState(state); + + F32 camDist = (state->getCameraPosition() - mParentObject->getRenderPosition()).len(); + + // Make sure the shadow wants to be rendered + if( mShadow->shouldRender( state ) ) + { + // Render! (and note the time) + mShadow->render( camDist, rstate ); + } +} + +BasicSceneObjectPluginFactory::BasicSceneObjectPluginFactory() + : mEnabled( false ) +{ + LightManager::smActivateSignal.notify( this, &BasicSceneObjectPluginFactory::_onLMActivate ); + + ShadowMapManager::smShadowDeactivateSignal.notify( this, &BasicSceneObjectPluginFactory::_setEnabled ); +} + +BasicSceneObjectPluginFactory::~BasicSceneObjectPluginFactory() +{ + LightManager::smActivateSignal.remove( this, &BasicSceneObjectPluginFactory::_onLMActivate ); + + ShadowMapManager::smShadowDeactivateSignal.remove( this, &BasicSceneObjectPluginFactory::_setEnabled ); +} + +void BasicSceneObjectPluginFactory::_onLMActivate( const char *lm, bool enable ) +{ + _setEnabled(); +} + +void BasicSceneObjectPluginFactory::_setEnabled() +{ + bool enable = false; + + // Enabled if using basic lighting. + LightManager *lm = LightManager::getActiveLM(); + if ( lm && dStricmp( lm->getName(), "Basic Lighting" ) == 0 ) + enable = true; + + // Disabled if all shadows are explictly disabled. + if ( ShadowMapPass::smDisableShadows ) + enable = false; + + // Already at the desired state. + if ( enable == mEnabled ) + return; + + if ( enable ) + { + SceneObject::smSceneObjectAdd.notify(this, &BasicSceneObjectPluginFactory::addLightPlugin); + SceneObject::smSceneObjectRemove.notify(this, &BasicSceneObjectPluginFactory::removeLightPlugin); + + if( gDecalManager ) + gDecalManager->getClearDataSignal().notify( this, &BasicSceneObjectPluginFactory::_onDecalManagerClear ); + + addToExistingObjects(); + } + else + { + SceneObject::smSceneObjectAdd.remove(this, &BasicSceneObjectPluginFactory::addLightPlugin); + SceneObject::smSceneObjectRemove.remove(this, &BasicSceneObjectPluginFactory::removeLightPlugin); + + if( gDecalManager ) + gDecalManager->getClearDataSignal().remove( this, &BasicSceneObjectPluginFactory::_onDecalManagerClear ); + + BasicSceneObjectLightingPlugin::cleanupPluginInstances(); + } + + mEnabled = enable; +} + +void BasicSceneObjectPluginFactory::_onDecalManagerClear() +{ + BasicSceneObjectLightingPlugin::resetAll(); +} + +void BasicSceneObjectPluginFactory::removeLightPlugin( SceneObject *obj ) +{ + // Grab the plugin instance. + SceneObjectLightingPlugin *lightPlugin = obj->getLightingPlugin(); + + // Delete it, which will also remove it + // from the static list of plugin instances. + if ( lightPlugin ) + { + delete lightPlugin; + obj->setLightingPlugin( NULL ); + } +} + +void BasicSceneObjectPluginFactory::addLightPlugin(SceneObject* obj) +{ + bool serverObj = obj->isServerObject(); + bool isShadowType = (obj->getTypeMask() & shadowObjectTypeMask); + + if ( !isShadowType || serverObj ) + return; + + obj->setLightingPlugin(new BasicSceneObjectLightingPlugin(obj)); +} + +// Some objects may not get cleaned up during mission load/free, so add our +// plugin to existing scene objects +void BasicSceneObjectPluginFactory::addToExistingObjects() +{ + SimpleQueryList sql; + gClientContainer.findObjects( shadowObjectTypeMask, SimpleQueryList::insertionCallback, &sql); + for (SceneObject** i = sql.mList.begin(); i != sql.mList.end(); i++) + addLightPlugin(*i); +} + diff --git a/Engine/source/lighting/basic/basicSceneObjectLightingPlugin.h b/Engine/source/lighting/basic/basicSceneObjectLightingPlugin.h new file mode 100644 index 000000000..ee7cac6fb --- /dev/null +++ b/Engine/source/lighting/basic/basicSceneObjectLightingPlugin.h @@ -0,0 +1,99 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _BASICSCENEOBJECTLIGHTINGPLUGIN_H_ +#define _BASICSCENEOBJECTLIGHTINGPLUGIN_H_ + +#ifndef _SCENEOBJECTLIGHTINGPLUGIN_H_ +#include "scene/sceneObjectLightingPlugin.h" +#endif + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif + +#ifndef _TSINGLETON_H_ +#include "core/util/tSingleton.h" +#endif + + +class ShadowBase; + +class BasicSceneObjectLightingPlugin : public SceneObjectLightingPlugin +{ +private: + + ShadowBase* mShadow; + SceneObject* mParentObject; + + static Vector smPluginInstances; + +public: + BasicSceneObjectLightingPlugin(SceneObject* parent); + ~BasicSceneObjectLightingPlugin(); + + static Vector* getPluginInstances() { return &smPluginInstances; } + + static void cleanupPluginInstances(); + static void resetAll(); + + const F32 getScore() const; + + // Called from BasicLightManager + virtual void updateShadow( SceneRenderState *state ); + virtual void renderShadow( SceneRenderState *state ); + + // Called by statics + virtual U32 packUpdate(SceneObject* obj, U32 checkMask, NetConnection *conn, U32 mask, BitStream *stream) { return 0; } + virtual void unpackUpdate(SceneObject* obj, NetConnection *conn, BitStream *stream) { } + + virtual void reset(); +}; + +class BasicSceneObjectPluginFactory : public ManagedSingleton< BasicSceneObjectPluginFactory > +{ +protected: + + /// Called from the light manager on activation. + /// @see LightManager::addActivateCallback + void _onLMActivate( const char *lm, bool enable ); + + void _onDecalManagerClear(); + + void removeLightPlugin(SceneObject* obj); + void addLightPlugin(SceneObject* obj); + void addToExistingObjects(); + + bool mEnabled; + +public: + + BasicSceneObjectPluginFactory(); + ~BasicSceneObjectPluginFactory(); + + // For ManagedSingleton. + static const char* getSingletonName() { return "BasicSceneObjectPluginFactory"; } + + void _setEnabled(); +}; + +#endif // !_BASICSCENEOBJECTLIGHTINGPLUGIN_H_ diff --git a/Engine/source/lighting/basic/blInteriorSystem.cpp b/Engine/source/lighting/basic/blInteriorSystem.cpp new file mode 100644 index 000000000..041f1ec8e --- /dev/null +++ b/Engine/source/lighting/basic/blInteriorSystem.cpp @@ -0,0 +1,1472 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "lighting/basic/blInteriorSystem.h" +#include "lighting/lightingInterfaces.h" +#include "lighting/common/shadowVolumeBSP.h" +#include "interior/interiorInstance.h" +#include "lighting/common/sceneLightingGlobals.h" +#include "lighting/basic/basicLightManager.h" +#include "gfx/bitmap/gBitmap.h" + +//#define SET_COLORS + + +bool blInteriorSystem::smUseVertexLighting = false; + + +//------------------------------------------------------------------------------ +// Class SceneLighting::PersistInfo::InteriorChunk +//------------------------------------------------------------------------------ +struct blInteriorChunk : public PersistInfo::PersistChunk +{ + typedef PersistChunk Parent; + + blInteriorChunk(); + ~blInteriorChunk(); + + Vector sgNormalLightMaps; + + Vector mDetailLightmapCount; + Vector mDetailLightmapIndices; + Vector mLightmaps; + + bool mHasAlarmState; + Vector mDetailVertexCount; + Vector mVertexColorsNormal; + Vector mVertexColorsAlarm; + + bool read(Stream &); + bool write(Stream &); +}; + +blInteriorChunk::blInteriorChunk() +{ + mChunkType = PersistChunk::InteriorChunkType; +} + +blInteriorChunk::~blInteriorChunk() +{ + for(U32 i = 0; i < mLightmaps.size(); i++) + delete mLightmaps[i]; +} + +//------------------------------------------------------------------------------ +// - always read in vertex lighting, lightmaps may not be needed +bool blInteriorChunk::read(Stream & stream) +{ + if(!Parent::read(stream)) + return(false); + + U32 size; + U32 i; + + // lightmaps->vertex-info + // BTRTODO: FIX ME + if (true) + //if(!SceneLighting::smUseVertexLighting) + { + // size of this minichunk + if(!stream.read(&size)) + return(false); + + // lightmaps + stream.read(&size); + mDetailLightmapCount.setSize(size); + for(i = 0; i < size; i++) + if(!stream.read(&mDetailLightmapCount[i])) + return(false); + + stream.read(&size); + mDetailLightmapIndices.setSize(size); + for(i = 0; i < size; i++) + if(!stream.read(&mDetailLightmapIndices[i])) + return(false); + + if(!stream.read(&size)) + return(false); + mLightmaps.setSize(size); + + for(i = 0; i < size; i++) + { + mLightmaps[i] = new GBitmap; + if(!mLightmaps[i]->readBitmap("png",stream)) + return(false); + } + } + else + { + // step past the lightmaps + if(!stream.read(&size)) + return(false); + if(!stream.setPosition(stream.getPosition() + size)) + return(false); + } + + // size of the vertex lighting: need to reset stream position after zipStream reading + U32 zipStreamEnd; + if(!stream.read(&zipStreamEnd)) + return(false); + zipStreamEnd += stream.getPosition(); + + /* + // vertex lighting + ZipSubRStream zipStream; + if(!zipStream.attachStream(&stream)) + return(false); + + if(!zipStream.read(&size)) + return(false); + mHasAlarmState = bool(size); + + if(!zipStream.read(&size)) + return(false); + mDetailVertexCount.setSize(size); + for(i = 0; i < size; i++) + if(!zipStream.read(&mDetailVertexCount[i])) + return(false); + + size = 0; + for(i = 0; i < mDetailVertexCount.size(); i++) + size += mDetailVertexCount[i]; + + mVertexColorsNormal.setSize(size); + + if(mHasAlarmState) + mVertexColorsAlarm.setSize(size); + + U32 curPos = 0; + for(i = 0; i < mDetailVertexCount.size(); i++) + { + U32 count = mDetailVertexCount[i]; + for(U32 j = 0; j < count; j++) + if(!zipStream.read(&mVertexColorsNormal[curPos + j])) + return(false); + + // read in the alarm info + if(mHasAlarmState) + { + // same? + if(!zipStream.read(&size)) + return(false); + if(bool(size)) + dMemcpy(&mVertexColorsAlarm[curPos], &mVertexColorsNormal[curPos], count * sizeof(ColorI)); + else + { + for(U32 j = 0; j < count; j++) + if(!zipStream.read(&mVertexColorsAlarm[curPos + j])) + return(false); + } + } + + curPos += count; + } + + zipStream.detachStream(); + */ + + // since there is no resizeFilterStream the zipStream happily reads + // off the end of the compressed block... reset the position + stream.setPosition(zipStreamEnd); + + return(true); +} + +bool blInteriorChunk::write(Stream & stream) +{ + if(!Parent::write(stream)) + return(false); + + // lightmaps + U32 startPos = stream.getPosition(); + if(!stream.write(U32(0))) + return(false); + + U32 i; + if(!stream.write(U32(mDetailLightmapCount.size()))) + return(false); + for(i = 0; i < mDetailLightmapCount.size(); i++) + if(!stream.write(mDetailLightmapCount[i])) + return(false); + + if(!stream.write(U32(mDetailLightmapIndices.size()))) + return(false); + for(i = 0; i < mDetailLightmapIndices.size(); i++) + if(!stream.write(mDetailLightmapIndices[i])) + return(false); + + if(!stream.write(U32(mLightmaps.size()))) + return(false); + for(i = 0; i < mLightmaps.size(); i++) + { + AssertFatal(mLightmaps[i], "SceneLighting::blInteriorChunk::Write: Invalid bitmap!"); + if(!mLightmaps[i]->writeBitmap("png",stream)) + return(false); + } + + // write out the lightmap portions size + U32 endPos = stream.getPosition(); + if(!stream.setPosition(startPos)) + return(false); + + // don't include the offset in the size + if(!stream.write(U32(endPos - startPos - sizeof(U32)))) + return(false); + if(!stream.setPosition(endPos)) + return(false); + + + // vertex lighting: needs the size of the vertex info because the + // zip stream may read off the end of the chunk + startPos = stream.getPosition(); + if(!stream.write(U32(0))) + return(false); + + // write out the vertex lighting portions size + endPos = stream.getPosition(); + if(!stream.setPosition(startPos)) + return(false); + + // don't include the offset in the size + if(!stream.write(U32(endPos - startPos - sizeof(U32)))) + return(false); + if(!stream.setPosition(endPos)) + return(false); + + return(true); +} + +// +// InteriorProxy (definition) +// +class blInteriorProxy : public SceneLighting::ObjectProxy +{ +private: + typedef ObjectProxy Parent; + + bool isShadowedBy(blInteriorProxy *); + ShadowVolumeBSP::SVPoly * buildInteriorPoly(ShadowVolumeBSP * shadowVolumeBSP, + Interior * detail, U32 surfaceIndex, LightInfo * light, + bool createSurfaceInfo); +public: + + blInteriorProxy(SceneObject * obj); + ~blInteriorProxy(); + InteriorInstance * operator->() {return(static_cast(static_cast(mObj)));} + InteriorInstance * getObject() {return(static_cast(static_cast(mObj)));} + + // current light info + ShadowVolumeBSP * mBoxShadowBSP; + Vector mLitBoxSurfaces; + Vector mOppositeBoxPlanes; + Vector mTerrainTestPlanes; + + + struct sgSurfaceInfo + { + const Interior::Surface *sgSurface; + U32 sgIndex; + Interior *sgDetail; + bool sgHasAlarm; + }; + U32 sgCurrentSurfaceIndex; + U32 sgSurfacesPerPass; + InteriorInstance *sgInterior; + Vector sgLights; + Vector sgSurfaces; + + void sgAddLight(LightInfo *light, InteriorInstance *interior); + //void sgLightUniversalPoint(LightInfo *light); + void sgProcessSurface(const Interior::Surface &surface, U32 i, Interior *detail, bool hasAlarm); + + + // lighting interface + bool loadResources(); + void init(); + //bool tgePreLight(LightInfo* light); + bool preLight(LightInfo *); + void light(LightInfo *); + void postLight(bool lastLight); + + //virtual void processLightingStart(); + //virtual bool processStartObjectLightingEvent(SceneLighting::ObjectProxy* objproxy, U32 current, U32 max); + //virtual void processTGELightProcessEvent(U32 curr, U32 max, LightInfo*); + + virtual bool supportsShadowVolume(); + virtual void getClipPlanes(Vector& planes); + virtual void addToShadowVolume(ShadowVolumeBSP * shadowVolume, LightInfo * light, S32 level); + + // persist + U32 getResourceCRC(); + bool setPersistInfo(PersistInfo::PersistChunk *); + bool getPersistInfo(PersistInfo::PersistChunk *); +}; + +//------------------------------------------------------------------------------ +// Class SceneLighting::InteriorProxy: +//------------------------------------------------------------------------------ +blInteriorProxy::blInteriorProxy(SceneObject * obj) : +Parent(obj) +{ + mBoxShadowBSP = 0; +} + +blInteriorProxy::~blInteriorProxy() +{ + delete mBoxShadowBSP; +} + +bool blInteriorProxy::loadResources() +{ + InteriorInstance * interior = getObject(); + if(!interior) + return(false); + + Resource & interiorRes = interior->getResource(); + if(!bool(interiorRes)) + return(false); + + return(true); +} + +void blInteriorProxy::init() +{ + InteriorInstance * interior = getObject(); + if(!interior) + return; +} + +bool blInteriorProxy::supportsShadowVolume() +{ + return true; +} + +void blInteriorProxy::getClipPlanes(Vector& planes) +{ + for(U32 i = 0; i < mLitBoxSurfaces.size(); i++) + planes.push_back(mLitBoxSurfaces[i]->mPlane); +} + +ShadowVolumeBSP::SVPoly * blInteriorProxy::buildInteriorPoly(ShadowVolumeBSP * shadowVolumeBSP, + Interior * detail, U32 surfaceIndex, LightInfo * light, + bool createSurfaceInfo) +{ + InteriorInstance* interior = dynamic_cast(getObject()); + if (!interior) + return NULL; + + // transform and add the points... + const MatrixF & transform = interior->getTransform(); + const VectorF & scale = interior->getScale(); + + const Interior::Surface & surface = detail->mSurfaces[surfaceIndex]; + + ShadowVolumeBSP::SVPoly * poly = shadowVolumeBSP->createPoly(); + + poly->mWindingCount = surface.windingCount; + + // project these points + for(U32 j = 0; j < poly->mWindingCount; j++) + { + Point3F iPnt = detail->mPoints[detail->mWindings[surface.windingStart + j]].point; + Point3F tPnt; + iPnt.convolve(scale); + transform.mulP(iPnt, &tPnt); + poly->mWinding[j] = tPnt; + } + + // convert from fan + U32 tmpIndices[ShadowVolumeBSP::SVPoly::MaxWinding]; + Point3F fanIndices[ShadowVolumeBSP::SVPoly::MaxWinding]; + + tmpIndices[0] = 0; + + U32 idx = 1; + U32 i; + for(i = 1; i < poly->mWindingCount; i += 2) + tmpIndices[idx++] = i; + for(i = ((poly->mWindingCount - 1) & (~0x1)); i > 0; i -= 2) + tmpIndices[idx++] = i; + + idx = 0; + for(i = 0; i < poly->mWindingCount; i++) + if(surface.fanMask & (1 << i)) + fanIndices[idx++] = poly->mWinding[tmpIndices[i]]; + + // set the data + poly->mWindingCount = idx; + for(i = 0; i < poly->mWindingCount; i++) + poly->mWinding[i] = fanIndices[i]; + + // flip the plane - shadow volumes face inwards + PlaneF plane = detail->getPlane(surface.planeIndex); + if(!Interior::planeIsFlipped(surface.planeIndex)) + plane.neg(); + + // transform the plane + mTransformPlane(transform, scale, plane, &poly->mPlane); + shadowVolumeBSP->buildPolyVolume(poly, light); + + // do surface info? + if(createSurfaceInfo) + { + ShadowVolumeBSP::SurfaceInfo * surfaceInfo = new ShadowVolumeBSP::SurfaceInfo; + shadowVolumeBSP->mSurfaces.push_back(surfaceInfo); + + // fill it + surfaceInfo->mSurfaceIndex = surfaceIndex; + surfaceInfo->mShadowVolume = shadowVolumeBSP->getShadowVolume(poly->mShadowVolume); + + // POLY and POLY node gets it too + ShadowVolumeBSP::SVNode * traverse = shadowVolumeBSP->getShadowVolume(poly->mShadowVolume); + while(traverse->mFront) + { + traverse->mSurfaceInfo = surfaceInfo; + traverse = traverse->mFront; + } + + // get some info from the poly node + poly->mSurfaceInfo = traverse->mSurfaceInfo = surfaceInfo; + surfaceInfo->mPlaneIndex = traverse->mPlaneIndex; + } + + return(poly); +} + + +void blInteriorProxy::addToShadowVolume(ShadowVolumeBSP * shadowVolume, LightInfo * light, S32 level) +{ + if(light->getType() != LightInfo::Vector) + return; + + ColorF ambient = light->getAmbient(); + + bool shadowedTree = true; + + InteriorInstance* interior = dynamic_cast(getObject()); + if (!interior) + return; + Resource mInteriorRes = interior->getResource(); + + // check if just getting shadow detail + if(level == SceneLighting::SHADOW_DETAIL) + { + shadowedTree = false; + level = mInteriorRes->getNumDetailLevels() - 1; + } + + Interior * detail = mInteriorRes->getDetailLevel(level); + bool hasAlarm = detail->hasAlarmState(); + + // make sure surfaces do not get processed more than once + BitVector surfaceProcessed; + surfaceProcessed.setSize(detail->mSurfaces.size()); + surfaceProcessed.clear(); + + ColorI color = light->getAmbient(); + + // go through the zones of the interior and grab outside visible surfaces + for(U32 i = 0; i < detail->getNumZones(); i++) + { + Interior::Zone & zone = detail->mZones[i]; + for(U32 j = 0; j < zone.surfaceCount; j++) + { + U32 surfaceIndex = detail->mZoneSurfaces[zone.surfaceStart + j]; + + // dont reprocess a surface + if(surfaceProcessed.test(surfaceIndex)) + continue; + surfaceProcessed.set(surfaceIndex); + + Interior::Surface & surface = detail->mSurfaces[surfaceIndex]; + + // outside visible? + if(!(surface.surfaceFlags & Interior::SurfaceOutsideVisible)) + continue; + + // good surface? + PlaneF plane = detail->getPlane(surface.planeIndex); + if(Interior::planeIsFlipped(surface.planeIndex)) + plane.neg(); + + // project the plane + PlaneF projPlane; + mTransformPlane(interior->getTransform(), interior->getScale(), plane, &projPlane); + + // fill with ambient? (need to do here, because surface will not be + // added to the SVBSP tree) + F32 dot = mDot(projPlane, light->getDirection()); + if(dot > -gParellelVectorThresh)// && !(GFX->getPixelShaderVersion() > 0.0) ) + { + if(shadowedTree) + { + // alarm lighting + GFXTexHandle normHandle = gInteriorLMManager.duplicateBaseLightmap(detail->getLMHandle(), interior->getLMHandle(), detail->getNormalLMapIndex(surfaceIndex)); + GFXTexHandle alarmHandle; + + GBitmap * normLightmap = normHandle->getBitmap(); + GBitmap * alarmLightmap = 0; + + // check if they share the lightmap + if(hasAlarm) + { + if(detail->getNormalLMapIndex(surfaceIndex) != detail->getAlarmLMapIndex(surfaceIndex)) + { + alarmHandle = gInteriorLMManager.duplicateBaseLightmap(detail->getLMHandle(), interior->getLMHandle(), detail->getAlarmLMapIndex(surfaceIndex)); + alarmLightmap = alarmHandle->getBitmap(); + } + } + + // + // Support for interior light map border sizes. + // + U32 xlen, ylen, xoff, yoff; + U32 lmborder = detail->getLightMapBorderSize(); + xlen = surface.mapSizeX + (lmborder * 2); + ylen = surface.mapSizeY + (lmborder * 2); + xoff = surface.mapOffsetX - lmborder; + yoff = surface.mapOffsetY - lmborder; + + // attemp to light normal and alarm lighting + for(U32 c = 0; c < 2; c++) + { + GBitmap * lightmap = (c == 0) ? normLightmap : alarmLightmap; + if(!lightmap) + continue; + + // fill it + for(U32 y = 0; y < ylen; y++) + { + for(U32 x = 0; x < xlen; x++) + { + ColorI outColor(255, 0, 0, 255); + +#ifndef SET_COLORS + ColorI lmColor(0, 0, 0, 255); + lightmap->getColor(xoff + x, yoff + y, lmColor); + + U32 _r = static_cast( color.red ) + static_cast( lmColor.red ); + U32 _g = static_cast( color.green ) + static_cast( lmColor.green ); + U32 _b = static_cast( color.blue ) + static_cast( lmColor.blue ); + + outColor.red = mClamp(_r, 0, 255); + outColor.green = mClamp(_g, 0, 255); + outColor.blue = mClamp(_b, 0, 255); +#endif + + lightmap->setColor(xoff + x, yoff + y, outColor); + } + } + } + } + continue; + } + + ShadowVolumeBSP::SVPoly * poly = buildInteriorPoly(shadowVolume, detail, + surfaceIndex, light, shadowedTree); + + // insert it into the SVBSP tree + shadowVolume->insertPoly(poly); + } + } +} + +bool blInteriorProxy::preLight(LightInfo * light) +{ + // create shadow volume of the bounding box of this object + InteriorInstance * interior = getObject(); + if(!interior) + return(false); + + if(light->getType() != LightInfo::Vector) + return(false); + + // reset + mLitBoxSurfaces.clear(); + + const Box3F & objBox = interior->getObjBox(); + const MatrixF & objTransform = interior->getTransform(); + const VectorF & objScale = interior->getScale(); + + // grab the surfaces which form the shadow volume + U32 numPlanes = 0; + PlaneF testPlanes[3]; + U32 planeIndices[3]; + + // grab the bounding planes which face the light + U32 i; + for(i = 0; (i < 6) && (numPlanes < 3); i++) + { + PlaneF plane; + plane.x = BoxNormals[i].x; + plane.y = BoxNormals[i].y; + plane.z = BoxNormals[i].z; + + if(i&1) + plane.d = (((const float*)objBox.minExtents)[(i-1)>>1]); + else + plane.d = -(((const float*)objBox.maxExtents)[i>>1]); + + // project + mTransformPlane(objTransform, objScale, plane, &testPlanes[numPlanes]); + + planeIndices[numPlanes] = i; + + if(mDot(testPlanes[numPlanes], light->getDirection()) < gParellelVectorThresh) + numPlanes++; + } + AssertFatal(numPlanes, "blInteriorProxy::preLight: no planes found"); + + // project the points + Point3F projPnts[8]; + for(i = 0; i < 8; i++) + { + Point3F pnt; + pnt.set(BoxPnts[i].x ? objBox.maxExtents.x : objBox.minExtents.x, + BoxPnts[i].y ? objBox.maxExtents.y : objBox.minExtents.y, + BoxPnts[i].z ? objBox.maxExtents.z : objBox.minExtents.z); + + // scale it + pnt.convolve(objScale); + objTransform.mulP(pnt, &projPnts[i]); + } + + mBoxShadowBSP = new ShadowVolumeBSP; + + // insert the shadow volumes for the surfaces + for(i = 0; i < numPlanes; i++) + { + ShadowVolumeBSP::SVPoly * poly = mBoxShadowBSP->createPoly(); + poly->mWindingCount = 4; + + U32 j; + for(j = 0; j < 4; j++) + poly->mWinding[j] = projPnts[BoxVerts[planeIndices[i]][j]]; + + testPlanes[i].neg(); + poly->mPlane = testPlanes[i]; + + mBoxShadowBSP->buildPolyVolume(poly, light); + mLitBoxSurfaces.push_back(mBoxShadowBSP->copyPoly(poly)); + mBoxShadowBSP->insertPoly(poly); + + // create the opposite planes for testing against terrain + Point3F pnts[3]; + for(j = 0; j < 3; j++) + pnts[j] = projPnts[BoxVerts[planeIndices[i]^1][j]]; + PlaneF plane(pnts[2], pnts[1], pnts[0]); + mOppositeBoxPlanes.push_back(plane); + } + + // grab the unique planes for terrain checks + for(i = 0; i < numPlanes; i++) + { + U32 mask = 0; + for(U32 j = 0; j < numPlanes; j++) + mask |= BoxSharedEdgeMask[planeIndices[i]][planeIndices[j]]; + + ShadowVolumeBSP::SVNode * traverse = mBoxShadowBSP->getShadowVolume(mLitBoxSurfaces[i]->mShadowVolume); + while(traverse->mFront) + { + if(!(mask & 1)) + mTerrainTestPlanes.push_back(mBoxShadowBSP->getPlane(traverse->mPlaneIndex)); + + mask >>= 1; + traverse = traverse->mFront; + } + } + + // there will be 2 duplicate node planes if there were only 2 planes lit + if(numPlanes == 2) + { + for(S32 i = 0; i < mTerrainTestPlanes.size(); i++) + for(U32 j = 0; j < mTerrainTestPlanes.size(); j++) + { + if(i == j) + continue; + + if((mDot(mTerrainTestPlanes[i], mTerrainTestPlanes[j]) > gPlaneNormThresh) && + (mFabs(mTerrainTestPlanes[i].d - mTerrainTestPlanes[j].d) < gPlaneDistThresh)) + { + mTerrainTestPlanes.erase(i); + i--; + break; + } + } + } + + return(true); +} + +bool blInteriorProxy::isShadowedBy(blInteriorProxy * test) +{ + // add if overlapping world box + if((*this)->getWorldBox().isOverlapped((*test)->getWorldBox())) + return(true); + + // test the box shadow volume + for(U32 i = 0; i < mLitBoxSurfaces.size(); i++) + { + ShadowVolumeBSP::SVPoly * poly = mBoxShadowBSP->copyPoly(mLitBoxSurfaces[i]); + if(test->mBoxShadowBSP->testPoly(poly)) + return(true); + } + + return(false); +} + +void blInteriorProxy::light(LightInfo * light) +{ + Platform::setMathControlStateKnown(); + + InteriorInstance * interior = getObject(); + if(!interior) + return; + + ColorF ambient = light->getAmbient(); + + S32 time = Platform::getRealMilliseconds(); + + // create own shadow volume + ShadowVolumeBSP shadowVolume; + + // add the other objects lit surfaces into shadow volume + for(ObjectProxy ** itr = gLighting->mLitObjects.begin(); itr != gLighting->mLitObjects.end(); itr++) + { + if(!(*itr)->getObject()) + continue; + + ObjectProxy* obj = *itr; + if (obj != this) + { + if (obj->supportsShadowVolume()) + obj->addToShadowVolume(&shadowVolume, light, SceneLighting::SHADOW_DETAIL); + } + +/* + // insert the terrain squares + if(gLighting->isTerrain((*itr)->mObj)) + { + TerrainProxy * terrain = static_cast(*itr); + + Vector clipPlanes; + clipPlanes = mTerrainTestPlanes; + for(U32 i = 0; i < mOppositeBoxPlanes.size(); i++) + clipPlanes.push_back(mOppositeBoxPlanes[i]); + + Vector shadowList; + if(terrain->getShadowedSquares(clipPlanes, shadowList)) + { + TerrainBlock * block = static_cast((*itr)->getObject()); + Point3F offset; + block->getTransform().getColumn(3, &offset); + + F32 squareSize = block->getSquareSize(); + + for(U32 j = 0; j < shadowList.size(); j++) + { + Point2I pos(shadowList[j] & TerrainBlock::BlockMask, shadowList[j] >> TerrainBlock::BlockShift); + Point2F wPos(pos.x * squareSize + offset.x, + pos.y * squareSize + offset.y); + + Point3F pnts[4]; + pnts[0].set(wPos.x, wPos.y, fixedToFloat(block->getHeight(pos.x, pos.y))); + pnts[1].set(wPos.x + squareSize, wPos.y, fixedToFloat(block->getHeight(pos.x + 1, pos.y))); + pnts[2].set(wPos.x + squareSize, wPos.y + squareSize, fixedToFloat(block->getHeight(pos.x + 1, pos.y + 1))); + pnts[3].set(wPos.x, wPos.y + squareSize, fixedToFloat(block->getHeight(pos.x, pos.y + 1))); + + GridSquare * gs = block->findSquare(0, pos); + + U32 squareIdx = (gs->flags & GridSquare::Split45) ? 0 : 2; + + for(U32 k = squareIdx; k < (squareIdx + 2); k++) + { + // face plane inwards + PlaneF plane(pnts[TerrainSquareIndices[k][2]], + pnts[TerrainSquareIndices[k][1]], + pnts[TerrainSquareIndices[k][0]]); + + if(mDot(plane, light->mDirection) > gParellelVectorThresh) + { + ShadowVolumeBSP::SVPoly * poly = shadowVolume.createPoly(); + poly->mWindingCount = 3; + + poly->mWinding[0] = pnts[TerrainSquareIndices[k][0]]; + poly->mWinding[1] = pnts[TerrainSquareIndices[k][1]]; + poly->mWinding[2] = pnts[TerrainSquareIndices[k][2]]; + poly->mPlane = plane; + + // create the shadow volume for this and insert + shadowVolume.buildPolyVolume(poly, light); + shadowVolume.insertPoly(poly); + } + } + } + } + }*/ + } + + // light all details + for(U32 i = 0; i < interior->getResource()->getNumDetailLevels(); i++) + { + // clear lightmaps + Interior * detail = interior->getResource()->getDetailLevel(i); + gInteriorLMManager.clearLightmaps(detail->getLMHandle(), interior->getLMHandle()); + + // clear out the last inserted interior + shadowVolume.removeLastInterior(); + + bool hasAlarm = detail->hasAlarmState(); + + addToShadowVolume(&shadowVolume, light, i); + + //gLighting->addInterior(&shadowVolume, *this, light, i); + + for(U32 j = 0; j < shadowVolume.mSurfaces.size(); j++) + { + ShadowVolumeBSP::SurfaceInfo * surfaceInfo = shadowVolume.mSurfaces[j]; + + U32 surfaceIndex = surfaceInfo->mSurfaceIndex; + + const Interior::Surface & surface = detail->getSurface(surfaceIndex); + + // alarm lighting + GFXTexHandle normHandle = gInteriorLMManager.duplicateBaseLightmap(detail->getLMHandle(), interior->getLMHandle(), detail->getNormalLMapIndex(surfaceIndex)); + GFXTexHandle alarmHandle; + + GBitmap * normLightmap = normHandle->getBitmap(); + GBitmap * alarmLightmap = 0; + + // check if the lightmaps are shared + if(hasAlarm) + { + if(detail->getNormalLMapIndex(surfaceIndex) != detail->getAlarmLMapIndex(surfaceIndex)) + { + alarmHandle = gInteriorLMManager.duplicateBaseLightmap(detail->getLMHandle(), interior->getLMHandle(), detail->getAlarmLMapIndex(surfaceIndex)); + alarmLightmap = alarmHandle->getBitmap(); + } + } + + // points right way? + PlaneF plane = detail->getPlane(surface.planeIndex); + if(Interior::planeIsFlipped(surface.planeIndex)) + plane.neg(); + + const MatrixF & transform = interior->getTransform(); + const Point3F & scale = interior->getScale(); + + // + PlaneF projPlane; + mTransformPlane(transform, scale, plane, &projPlane); + + F32 dot = mDot(projPlane, -light->getDirection()); + + // cancel out lambert dot product and ambient lighting on hardware + // with pixel shaders + if( GFX->getPixelShaderVersion() > 0.0 ) + { + dot = 1.0f; + ambient.set( 0.0f, 0.0f, 0.0f ); + } + + // shadowed? + if(!surfaceInfo->mShadowed.size()) + { + // calc the color and convert to U8 rep + ColorF tmp = (light->getColor() * dot) + ambient; + tmp.clamp(); + ColorI color = tmp; + + // attempt to light both the normal and the alarm states + for(U32 c = 0; c < 2; c++) + { + GBitmap * lightmap = (c == 0) ? normLightmap : alarmLightmap; + if(!lightmap) + continue; + + // + // Support for interior light map border sizes. + // + U32 xlen, ylen, xoff, yoff; + U32 lmborder = detail->getLightMapBorderSize(); + xlen = surface.mapSizeX + (lmborder * 2); + ylen = surface.mapSizeY + (lmborder * 2); + xoff = surface.mapOffsetX - lmborder; + yoff = surface.mapOffsetY - lmborder; + + // fill it + for(U32 y = 0; y < ylen; y++) + { + for(U32 x = 0; x < xlen; x++) + { + ColorI outColor(0, 255, 0, 255); + +#ifndef SET_COLORS + ColorI lmColor(0, 0, 0, 255); + lightmap->getColor(xoff + x, yoff + y, lmColor); + + U32 _r = static_cast( color.red ) + static_cast( lmColor.red ); + U32 _g = static_cast( color.green ) + static_cast( lmColor.green ); + U32 _b = static_cast( color.blue ) + static_cast( lmColor.blue ); + + outColor.red = mClamp(_r, 0, 255); + outColor.green = mClamp(_g, 0, 255); + outColor.blue = mClamp(_b, 0, 255); +#endif + + lightmap->setColor(xoff + x, yoff + y, outColor); + } + } + } + if(!surfaceInfo->mShadowed.size()) + continue; + } + + // + // Support for interior light map border sizes. + // + U32 xlen, ylen, xoff, yoff; + U32 lmborder = detail->getLightMapBorderSize(); + xlen = surface.mapSizeX + (lmborder * 2); + ylen = surface.mapSizeY + (lmborder * 2); + xoff = surface.mapOffsetX - lmborder; + yoff = surface.mapOffsetY - lmborder; + + // get the lmagGen... + const Interior::TexGenPlanes & lmTexGenEQ = detail->getLMTexGenEQ(surfaceIndex); + + const F32 * const lGenX = lmTexGenEQ.planeX; + const F32 * const lGenY = lmTexGenEQ.planeY; + + AssertFatal((lGenX[0] * lGenX[1] == 0.f) && + (lGenX[0] * lGenX[2] == 0.f) && + (lGenX[1] * lGenX[2] == 0.f), "Bad lmTexGen!"); + AssertFatal((lGenY[0] * lGenY[1] == 0.f) && + (lGenY[0] * lGenY[2] == 0.f) && + (lGenY[1] * lGenY[2] == 0.f), "Bad lmTexGen!"); + + // get the axis index for the texgens (could be swapped) + S32 si=0; + S32 ti=0; + S32 axis = -1; + + // + if(lGenX[0] == 0.f && lGenY[0] == 0.f) // YZ + { + axis = 0; + if(lGenX[1] == 0.f) { // swapped? + si = 2; + ti = 1; + } else { + si = 1; + ti = 2; + } + } + else if(lGenX[1] == 0.f && lGenY[1] == 0.f) // XZ + { + axis = 1; + if(lGenX[0] == 0.f) { // swapped? + si = 2; + ti = 0; + } else { + si = 0; + ti = 2; + } + } + else if(lGenX[2] == 0.f && lGenY[2] == 0.f) // XY + { + axis = 2; + if(lGenX[0] == 0.f) { // swapped? + si = 1; + ti = 0; + } else { + si = 0; + ti = 1; + } + } + AssertFatal(!(axis == -1), "SceneLighting::lightInterior: bad TexGen!"); + + const F32 * pNormal = ((const F32*)plane); + + Point3F start; + F32 * pStart = ((F32*)start); + + F32 lumelScale = 1.0 / (lGenX[si] * normLightmap->getWidth()); + + // get the start point on the lightmap + pStart[si] = (((xoff * lumelScale) / (1.0 / lGenX[si])) - lGenX[3] ) / lGenX[si]; + pStart[ti] = (((yoff * lumelScale) / (1.0 / lGenY[ti])) - lGenY[3] ) / lGenY[ti]; + pStart[axis] = ((pNormal[si] * pStart[si]) + (pNormal[ti] * pStart[ti]) + plane.d) / -pNormal[axis]; + + start.convolve(scale); + transform.mulP(start); + + // get the s/t vecs oriented on the surface + Point3F sVec; + Point3F tVec; + + F32 * pSVec = ((F32*)sVec); + F32 * pTVec = ((F32*)tVec); + + F32 angle; + Point3F planeNormal; + + // s + pSVec[si] = 1.f; + pSVec[ti] = 0.f; + + planeNormal = plane; + ((F32*)planeNormal)[ti] = 0.f; + planeNormal.normalize(); + + angle = mAcos(mClampF(((F32*)planeNormal)[axis], -1.f, 1.f)); + pSVec[axis] = (((F32*)planeNormal)[si] < 0.f) ? mTan(angle) : -mTan(angle); + + // t + pTVec[ti] = 1.f; + pTVec[si] = 0.f; + + planeNormal = plane; + ((F32*)planeNormal)[si] = 0.f; + planeNormal.normalize(); + + angle = mAcos(mClampF(((F32*)planeNormal)[axis], -1.f, 1.f)); + pTVec[axis] = (((F32*)planeNormal)[ti] < 0.f) ? mTan(angle) : -mTan(angle); + + // scale the vectors + + sVec *= lumelScale; + tVec *= lumelScale; + + // project vecs + transform.mulV(sVec); + sVec.convolve(scale); + + transform.mulV(tVec); + tVec.convolve(scale); + + Point3F & curPos = start; + Point3F sRun = sVec * xlen; + + // get the lexel area + Point3F cross; + mCross(sVec, tVec, &cross); + F32 maxLexelArea = cross.len(); + + const PlaneF & surfacePlane = shadowVolume.getPlane(surfaceInfo->mPlaneIndex); + + // get the world coordinate for each lexel + for(U32 y = 0; y < ylen; y++) + { + for(U32 x = 0; x < xlen; x++) + { + ShadowVolumeBSP::SVPoly * poly = shadowVolume.createPoly(); + poly->mPlane = surfacePlane; + poly->mWindingCount = 4; + + // set the poly indices + poly->mWinding[0] = curPos; + poly->mWinding[1] = curPos + sVec; + poly->mWinding[2] = curPos + sVec + tVec; + poly->mWinding[3] = curPos + tVec; + + //// insert poly which has been clipped to own shadow volume + //ShadowVolumeBSP::SVPoly * store = 0; + //shadowVolume.clipToSelf(surfaceInfo->mShadowVolume, &store, poly); + + //if(!store) + // continue; + + //F32 lexelArea = shadowVolume.getPolySurfaceArea(store); + //F32 area = shadowVolume.getLitSurfaceArea(store, surfaceInfo); + + F32 area = shadowVolume.getLitSurfaceArea(poly, surfaceInfo); + F32 shadowScale = mClampF(area / maxLexelArea, 0.f, 1.f); + + // get the color into U8 + ColorF tmp = (light->getColor() * dot * shadowScale) + ambient; + tmp.clamp(); + ColorI color = tmp; + + // attempt to light both normal and alarm lightmaps + for(U32 c = 0; c < 2; c++) + { + GBitmap * lightmap = (c == 0) ? normLightmap : alarmLightmap; + if(!lightmap) + continue; + + ColorI outColor(0, 0, 255, 255); + +#ifndef SET_COLORS + ColorI lmColor(0, 0, 0, 255); + lightmap->getColor(xoff + x, yoff + y, lmColor); + + U32 _r = static_cast( color.red ) + static_cast( lmColor.red ); + U32 _g = static_cast( color.green ) + static_cast( lmColor.green ); + U32 _b = static_cast( color.blue ) + static_cast( lmColor.blue ); + + outColor.red = mClamp(_r, 0, 255); + outColor.green = mClamp(_g, 0, 255); + outColor.blue = mClamp(_b, 0, 255); +#endif + + lightmap->setColor(xoff + x, yoff + y, outColor); + } + + curPos += sVec; + } + + curPos -= sRun; + curPos += tVec; + } + } + } + + Con::printf(" = interior lit in %3.3f seconds", (Platform::getRealMilliseconds()-time)/1000.f); +} + +void blInteriorProxy::postLight(bool lastLight) +{ + delete mBoxShadowBSP; + mBoxShadowBSP = 0; + + InteriorInstance * interior = getObject(); + if(!interior) + return; +} + +//------------------------------------------------------------------------------ +U32 blInteriorProxy::getResourceCRC() +{ + InteriorInstance * interior = getObject(); + if(!interior) + return(0); + return(interior->getCRC()); +} + +//------------------------------------------------------------------------------ +bool blInteriorProxy::setPersistInfo(PersistInfo::PersistChunk * info) +{ + + if(!Parent::setPersistInfo(info)) + return(false); + + blInteriorChunk * chunk = dynamic_cast(info); + AssertFatal(chunk, "blInteriorProxy::setPersistInfo: invalid info chunk!"); + + InteriorInstance * interior = getObject(); + if(!interior) + return(false); + + U32 numDetails = interior->getNumDetailLevels(); + + // check the lighting method +// BTRTODO: Restore +// AssertFatal(SceneLighting::smUseVertexLighting == Interior::smUseVertexLighting, "blInteriorProxy::setPersistInfo: invalid vertex lighting state"); +// if(SceneLighting::smUseVertexLighting != Interior::smUseVertexLighting) +// return(false); + + /* + // process the vertex lighting... + if(chunk->mDetailVertexCount.size() != numDetails) + return(false); + + AssertFatal(chunk->mVertexColorsNormal.size(), "blInteriorProxy::setPersistInfo: invalid chunk info"); + AssertFatal(!chunk->mHasAlarmState || chunk->mVertexColorsAlarm.size(), "blInteriorProxy::setPersistInfo: invalid chunk info"); + AssertFatal(!(chunk->mHasAlarmState ^ interior->getDetailLevel(0)->hasAlarmState()), "blInteriorProxy::setPersistInfo: invalid chunk info"); + + + U32 curPos = 0; + for(U32 i = 0; i < numDetails; i++) + { + U32 count = chunk->mDetailVertexCount[i]; + Vector* normal = interior->getVertexColorsNormal(i); + Vector* alarm = interior->getVertexColorsAlarm(i); + AssertFatal(normal != NULL && alarm != NULL, "Error, bad vectors returned!"); + + normal->setSize(count); + dMemcpy(normal->address(), &chunk->mVertexColorsNormal[curPos], count * sizeof(ColorI)); + + if(chunk->mHasAlarmState) + { + alarm->setSize(count); + dMemcpy(alarm->address(), &chunk->mVertexColorsAlarm[curPos], count * sizeof(ColorI)); + } + + curPos += count; + } + */ + // need lightmaps? + //if(!SceneLighting::smUseVertexLighting) + // BTRTODO: Fix me + if (!false) + { + if(chunk->mDetailLightmapCount.size() != numDetails) + return(false); + + LM_HANDLE instanceHandle = interior->getLMHandle(); + U32 idx = 0; + + for(U32 i = 0; i < numDetails; i++) + { + Interior * detail = interior->getDetailLevel(i); + + LM_HANDLE interiorHandle = detail->getLMHandle(); + Vector & baseHandles = gInteriorLMManager.getHandles(interiorHandle, 0); + + if(chunk->mDetailLightmapCount[i] > baseHandles.size()) + return(false); + + for(U32 j = 0; j < chunk->mDetailLightmapCount[i]; j++) + { + U32 baseIndex = chunk->mDetailLightmapIndices[idx]; + if(baseIndex >= baseHandles.size()) + return(false); + + AssertFatal(chunk->mLightmaps[idx], "blInteriorProxy::setPersistInfo: bunk bitmap!"); + if(chunk->mLightmaps[idx]->getWidth() != baseHandles[baseIndex]->getWidth() || + chunk->mLightmaps[idx]->getHeight() != baseHandles[baseIndex]->getHeight()) + return(false); + + GFXTexHandle tHandle = gInteriorLMManager.duplicateBaseLightmap(interiorHandle, instanceHandle, baseIndex); + + // create the diff bitmap + tHandle->getBitmap()->combine( baseHandles[baseIndex]->getBitmap(), + chunk->mLightmaps[idx], + GFXTOPAdd ); + + idx++; + } + } + } + + return(true); +} + +bool blInteriorProxy::getPersistInfo(PersistInfo::PersistChunk * info) +{ + if(!Parent::getPersistInfo(info)) + return(false); + + blInteriorChunk* chunk = dynamic_cast(info); + AssertFatal(chunk, "blInteriorProxy::getPersistInfo: invalid info chunk!"); + + InteriorInstance * interior = getObject(); + if(!interior) + return(false); + + LM_HANDLE instanceHandle = interior->getLMHandle(); + + AssertFatal(!chunk->mDetailLightmapCount.size(), "blInteriorProxy::getPersistInfo: invalid array!"); + AssertFatal(!chunk->mDetailLightmapIndices.size(), "blInteriorProxy::getPersistInfo: invalid array!"); + AssertFatal(!chunk->mLightmaps.size(), "blInteriorProxy::getPersistInfo: invalid array!"); + + U32 numDetails = interior->getNumDetailLevels(); + U32 i; + for(i = 0; i < numDetails; i++) + { + Interior * detail = interior->getDetailLevel(i); + LM_HANDLE interiorHandle = detail->getLMHandle(); + + Vector & baseHandles = gInteriorLMManager.getHandles(interiorHandle, 0); + Vector & instanceHandles = gInteriorLMManager.getHandles(interiorHandle, instanceHandle); + + U32 litCount = 0; + + // walk all the instance lightmaps and grab diff lighting from them + for(U32 j = 0; j < instanceHandles.size(); j++) + { + if(!instanceHandles[j]) + continue; + + litCount++; + chunk->mDetailLightmapIndices.push_back(j); + + GBitmap * baseBitmap = baseHandles[j]->getBitmap(); + GBitmap * instanceBitmap = instanceHandles[j]->getBitmap(); + + Point2I extent(baseBitmap->getWidth(), baseBitmap->getHeight()); + + GBitmap * diffLightmap = new GBitmap(extent.x, extent.y, false); + + // diffLightmap = instanceBitmap - baseBitmap + diffLightmap->combine( instanceBitmap, baseBitmap, GFXTOPSubtract ); + + chunk->mLightmaps.push_back(diffLightmap); + } + + chunk->mDetailLightmapCount.push_back(litCount); + } + + // process the vertex lighting... + AssertFatal(!chunk->mDetailVertexCount.size(), "blInteriorProxy::getPersistInfo: invalid chunk info"); + AssertFatal(!chunk->mVertexColorsNormal.size(), "blInteriorProxy::getPersistInfo: invalid chunk info"); + AssertFatal(!chunk->mVertexColorsAlarm.size(), "blInteriorProxy::getPersistInfo: invalid chunk info"); + + chunk->mHasAlarmState = interior->getDetailLevel(0)->hasAlarmState(); + chunk->mDetailVertexCount.setSize(numDetails); + + U32 size = 0; + for(i = 0; i < numDetails; i++) + { + Interior * detail = interior->getDetailLevel(i); + + U32 count = detail->getWindingCount(); + chunk->mDetailVertexCount[i] = count; + size += count; + } + + /* + chunk->mVertexColorsNormal.setSize(size); + if(chunk->mHasAlarmState) + chunk->mVertexColorsAlarm.setSize(size); + + U32 curPos = 0; + for(i = 0; i < numDetails; i++) + { + Vector* normal = interior->getVertexColorsNormal(i); + Vector* alarm = interior->getVertexColorsAlarm(i); + AssertFatal(normal != NULL && alarm != NULL, "Error, no normal or alarm vertex colors!"); + + U32 count = chunk->mDetailVertexCount[i]; + dMemcpy(&chunk->mVertexColorsNormal[curPos], normal->address(), count * sizeof(ColorI)); + + if(chunk->mHasAlarmState) + dMemcpy(&chunk->mVertexColorsAlarm[curPos], alarm->address(), count * sizeof(ColorI)); + + curPos += count; + } + */ + + return(true); +} + + +SceneLighting::ObjectProxy* blInteriorSystem::createObjectProxy(SceneObject* obj, SceneLighting::ObjectProxyList* sceneObjects) +{ + if ((obj->getTypeMask() & InteriorObjectType) != 0) + { + return new blInteriorProxy(obj); + } else { + return NULL; + } +} + +PersistInfo::PersistChunk* blInteriorSystem::createPersistChunk(const U32 chunkType) +{ + if (chunkType == PersistInfo::PersistChunk::InteriorChunkType) + { + return new blInteriorChunk; + } else { + return NULL; + } +} + +bool blInteriorSystem::createPersistChunkFromProxy(SceneLighting::ObjectProxy* objproxy, PersistInfo::PersistChunk **ret) +{ + if ((objproxy->mObj->getTypeMask() & InteriorObjectType) != 0) + { + *ret = new blInteriorChunk; + return true; + } else { + return false; + } +} + +void blInteriorSystem::init() +{ + +} + +U32 blInteriorSystem::addObjectType() +{ + return InteriorObjectType; +} + +U32 blInteriorSystem::addToClippingMask() +{ + return InteriorObjectType; +} + +void blInteriorSystem::processLightingBegin() +{ + // Store the vertex lighting state when we being lighting, we compare this when we finish lighting + smUseVertexLighting = Interior::smUseVertexLighting; +} + +void blInteriorSystem::processLightingCompleted(bool success) +{ + if(success) + { + AssertFatal(smUseVertexLighting == Interior::smUseVertexLighting, "SceneLighting::completed: vertex lighting state changed during scene light"); + + // cannot do anything if vertex state has changed (since we only load in what is needed) + if(smUseVertexLighting == Interior::smUseVertexLighting) + { + if(!smUseVertexLighting) + { + gInteriorLMManager.downloadGLTextures(); + gInteriorLMManager.destroyBitmaps(); + } + else + gInteriorLMManager.destroyTextures(); + } + } +} + +// Given a ray, this will return the color from the lightmap of this object, return true if handled +bool blInteriorSystem::getColorFromRayInfo(RayInfo collision, ColorF& result) +{ + InteriorInstance* interior = dynamic_cast(collision.object); + if (interior == NULL) + return false; + + interior->getRenderWorldTransform().mulP(collision.point); + Interior *detail = interior->getDetailLevel(0); + AssertFatal((detail), "SceneObject::getLightingAmbientColor: invalid interior"); + if(collision.face < detail->getSurfaceCount()) + { + const Interior::Surface &surface = detail->getSurface(collision.face); + const Interior::TexGenPlanes &texgen = detail->getLMTexGenEQ(collision.face); + + GBitmap* lightmap = gInteriorLMManager.getHandle(detail->getLMHandle(), + interior->getLMHandle(), detail->getNormalLMapIndex(collision.face)).getBitmap(); + if (!lightmap) + return false; + + Point2F uv; + uv.x = mDot(texgen.planeX, collision.point) + texgen.planeX.d; + uv.y = mDot(texgen.planeY, collision.point) + texgen.planeY.d; + + U32 size = (U32)(uv.x * F32(lightmap->getWidth())); + size = mClamp(size, surface.mapOffsetX, (surface.mapOffsetX + surface.mapSizeX)); + uv.x = F32(size) / F32(lightmap->getWidth()); + + size = (U32)(uv.y * F32(lightmap->getHeight())); + size = mClamp(size, surface.mapOffsetY, (surface.mapOffsetY + surface.mapSizeY)); + uv.y = F32(size) / F32(lightmap->getHeight()); + + result = lightmap->sampleTexel(uv.x, uv.y); + return true; + } + return false; +} diff --git a/Engine/source/lighting/basic/blInteriorSystem.h b/Engine/source/lighting/basic/blInteriorSystem.h new file mode 100644 index 000000000..604963d88 --- /dev/null +++ b/Engine/source/lighting/basic/blInteriorSystem.h @@ -0,0 +1,57 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _BLINTERIOSYSTEM_H_ +#define _BLINTERIOSYSTEM_H_ + +#ifndef _SCENELIGHTING_H_ + #include "lighting/common/sceneLighting.h" +#endif +#ifndef _SG_SYSTEM_INTERFACE_H + #include "lighting/lightingInterfaces.h" +#endif + + +// +// Lighting system interface +// +class blInteriorSystem : public SceneLightingInterface +{ +public: + static bool smUseVertexLighting; + + virtual SceneLighting::ObjectProxy* createObjectProxy(SceneObject* obj, SceneLighting::ObjectProxyList* sceneObjects); + virtual PersistInfo::PersistChunk* createPersistChunk(const U32 chunkType); + virtual bool createPersistChunkFromProxy(SceneLighting::ObjectProxy* objproxy, PersistInfo::PersistChunk **ret); + + virtual void init(); + virtual U32 addObjectType(); + virtual U32 addToClippingMask(); + + virtual void processLightingBegin(); + virtual void processLightingCompleted(bool success); + + // Given a ray, this will return the color from the lightmap of this object, return true if handled + virtual bool getColorFromRayInfo(RayInfo collision, ColorF& result); +}; + +#endif // !_BLINTERIOSYSTEM_H_ diff --git a/Engine/source/lighting/basic/blTerrainSystem.cpp b/Engine/source/lighting/basic/blTerrainSystem.cpp new file mode 100644 index 000000000..4e972977c --- /dev/null +++ b/Engine/source/lighting/basic/blTerrainSystem.cpp @@ -0,0 +1,715 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/basic/blTerrainSystem.h" + +#include "core/bitVector.h" +#include "lighting/common/shadowVolumeBSP.h" +#include "lighting/lightingInterfaces.h" +#include "terrain/terrData.h" +#include "lighting/basic/basicLightManager.h" +#include "lighting/common/sceneLighting.h" +#include "gfx/bitmap/gBitmap.h" +#include "collision/collision.h" + +extern SceneLighting* gLighting; + + +struct blTerrainChunk : public PersistInfo::PersistChunk +{ + typedef PersistInfo::PersistChunk Parent; + + blTerrainChunk(); + ~blTerrainChunk(); + + GBitmap *mLightmap; + + bool read(Stream &); + bool write(Stream &); +}; + +//------------------------------------------------------------------------------ +// Class SceneLighting::TerrainChunk +//------------------------------------------------------------------------------ +blTerrainChunk::blTerrainChunk() +{ + mChunkType = PersistChunk::TerrainChunkType; + mLightmap = NULL; +} + +blTerrainChunk::~blTerrainChunk() +{ + if(mLightmap) + delete mLightmap; +} + +//------------------------------------------------------------------------------ + +bool blTerrainChunk::read(Stream & stream) +{ + if(!Parent::read(stream)) + return(false); + + mLightmap = new GBitmap(); + return mLightmap->readBitmap("png",stream); +} + +bool blTerrainChunk::write(Stream & stream) +{ + if(!Parent::write(stream)) + return(false); + + if(!mLightmap) + return(false); + + if(!mLightmap->writeBitmap("png",stream)) + return(false); + + return(true); +} + +class blTerrainProxy : public SceneLighting::ObjectProxy +{ +protected: + + typedef ObjectProxy Parent; + + BitVector mShadowMask; + ShadowVolumeBSP * mShadowVolume; + ColorF * mLightmap; + + /// The dimension of the lightmap in pixels. + const U32 mLightMapSize; + + /// The dimension of the terrain height map sample array. + const U32 mTerrainBlockSize; + + ColorF *sgBakedLightmap; + Vector sgLights; + bool sgMarkStaticShadow(void *terrainproxy, SceneObject *sceneobject, LightInfo *light); + //void postLight(bool lastLight); + + void lightVector(LightInfo *); + + struct SquareStackNode + { + U8 mLevel; + U16 mClipFlags; + Point2I mPos; + }; + + S32 testSquare(const Point3F &, const Point3F &, S32, F32, const Vector &); + bool markObjectShadow(ObjectProxy *); + bool sgIsCorrectStaticObjectType(SceneObject *obj); + + inline ColorF _getValue( S32 row, S32 column ); + +public: + + blTerrainProxy(SceneObject * obj); + ~blTerrainProxy(); + TerrainBlock * operator->() {return(static_cast(static_cast(mObj)));} + TerrainBlock * getObject() {return(static_cast(static_cast(mObj)));} + + bool getShadowedSquares(const Vector &, Vector &); + + // lighting + void init(); + bool preLight(LightInfo *); + void light(LightInfo *); + + // persist + U32 getResourceCRC(); + bool setPersistInfo(PersistInfo::PersistChunk *); + bool getPersistInfo(PersistInfo::PersistChunk *); + + virtual bool supportsShadowVolume(); + virtual void getClipPlanes(Vector& planes); + virtual void addToShadowVolume(ShadowVolumeBSP * shadowVolume, LightInfo * light, S32 level); + + // events + //virtual void processTGELightProcessEvent(U32 curr, U32 max, LightInfo* currlight); + //virtual void processSGObjectProcessEvent(LightInfo* currLight); +}; + +//------------------------------------------------------------------------------- +// Class SceneLighting::TerrainProxy: +//------------------------------------------------------------------------------- +blTerrainProxy::blTerrainProxy( SceneObject *obj ) : + Parent( obj ), + mLightMapSize( getObject()->getLightMapSize() ), + mTerrainBlockSize( getObject()->getBlockSize() ), + mLightmap( NULL ) +{ +} + +blTerrainProxy::~blTerrainProxy() +{ + delete [] mLightmap; +} + +//------------------------------------------------------------------------------- +void blTerrainProxy::init() +{ + mLightmap = new ColorF[ mLightMapSize * mLightMapSize ]; + dMemset(mLightmap, 0, mLightMapSize * mLightMapSize * sizeof(ColorF)); + mShadowMask.setSize( mTerrainBlockSize * mTerrainBlockSize ); +} + +bool blTerrainProxy::preLight(LightInfo * light) +{ + if(!bool(mObj)) + return(false); + + if(light->getType() != LightInfo::Vector) + return(false); + + mShadowMask.clear(); + return(true); +} + +inline ColorF blTerrainProxy::_getValue( S32 row, S32 column ) +{ + while( row < 0 ) + row += mLightMapSize; + row = row % mLightMapSize; + + while( column < 0 ) + column += mLightMapSize; + column = column % mLightMapSize; + + U32 offset = row * mLightMapSize + column; + + return mLightmap[offset]; +} + +bool blTerrainProxy::markObjectShadow(ObjectProxy * proxy) +{ + if (!proxy->supportsShadowVolume()) + return false; + + // setup the clip planes + Vector clipPlanes; + proxy->getClipPlanes(clipPlanes); + + Vector shadowList; + if(!getShadowedSquares(clipPlanes, shadowList)) + return(false); + + // set the correct bit + for(U32 i = 0; i < shadowList.size(); i++) + mShadowMask.set(shadowList[i]); + + return(true); +} + +void blTerrainProxy::light(LightInfo * light) +{ + // If we don't have terrain or its not a directional + // light then skip processing. + TerrainBlock * terrain = getObject(); + if ( !terrain || light->getType() != LightInfo::Vector ) + return; + + S32 time = Platform::getRealMilliseconds(); + + // reset + mShadowVolume = new ShadowVolumeBSP; + + // build interior shadow volume + for(ObjectProxy ** itr = gLighting->mLitObjects.begin(); itr != gLighting->mLitObjects.end(); itr++) + { + ObjectProxy* objproxy = *itr; + if (markObjectShadow(objproxy)) + objproxy->addToShadowVolume(mShadowVolume, light, SceneLighting::SHADOW_DETAIL); + } + + lightVector(light); + + // set the lightmap... + terrain->clearLightMap(); + + // Blur... + F32 kernel[3][3] = { {1, 2, 1}, + {2, 3, 2}, + {1, 2, 1} }; + + F32 modifier = 1; + F32 divisor = 0; + + + for( U32 i=0; i<3; i++ ) + { + for( U32 j=0; j<3; j++ ) + { + if( i==1 && j==1 ) + { + kernel[i][j] = 1 + kernel[i][j] * modifier; + } + else + { + kernel[i][j] = kernel[i][j] * modifier; + } + + divisor += kernel[i][j]; + } + } + + for( U32 i=0; i < mLightMapSize; i++ ) + { + for( U32 j=0; j < mLightMapSize; j++ ) + { + + ColorF val; + val = _getValue( i-1, j-1 ) * kernel[0][0]; + val += _getValue( i-1, j ) * kernel[0][1]; + val += _getValue( i-1, j+1 ) * kernel[0][2]; + val += _getValue( i, j-1 ) * kernel[1][0]; + val += _getValue( i, j ) * kernel[1][1]; + val += _getValue( i, j+1 ) * kernel[1][2]; + val += _getValue( i+1, j-1 ) * kernel[2][0]; + val += _getValue( i+1, j ) * kernel[2][1]; + val += _getValue( i+1, j+1 ) * kernel[2][2]; + + U32 edge = 0; + + if( j == 0 || j == mLightMapSize - 1 ) + edge++; + + if( i == 0 || i == mLightMapSize - 1 ) + edge++; + + if( !edge ) + val = val / divisor; + else + val = mLightmap[ i * mLightMapSize + j ]; + + // clamp values + mLightmap[ i * mLightMapSize + j ]= val; + } + } + + // And stuff it into the texture... + GBitmap *terrLightMap = terrain->getLightMap(); + for(U32 y = 0; y < mLightMapSize; y++) + { + for(U32 x = 0; x < mLightMapSize; x++) + { + ColorI color(255, 255, 255, 255); + + color.red = mLightmap[x + y * mLightMapSize].red * 255; + color.green = mLightmap[x + y * mLightMapSize].green * 255; + color.blue = mLightmap[x + y * mLightMapSize].blue * 255; + + terrLightMap->setColor(x, y, color); + } + } + + /* + // This handles matching up the outer edges of the terrain + // lightmap when it has neighbors + if (!terrain->isTiling()) + { + for (S32 y = 0; y < terrLightMap->getHeight(); y++) + { + ColorI c; + if (terrain->getFile()->mEdgeTerrainFiles[0]) + { + terrLightMap->getColor(terrLightMap->getWidth()-1,y,c); + terrLightMap->setColor(0,y,c); + terrLightMap->setColor(1,y,c); + } + else + { + terrLightMap->getColor(0,y,c); + terrLightMap->setColor(terrLightMap->getWidth()-1,y,c); + terrLightMap->setColor(terrLightMap->getWidth()-2,y,c); + } + } + + for (S32 x = 0; x < terrLightMap->getHeight(); x++) + { + ColorI c; + if (terrain->getFile()->mEdgeTerrainFiles[1]) + { + terrLightMap->getColor(x,terrLightMap->getHeight()-1,c); + terrLightMap->setColor(x,0,c); + terrLightMap->setColor(x,1,c); + } + else + { + terrLightMap->getColor(x,0,c); + terrLightMap->setColor(x,terrLightMap->getHeight()-1,c); + terrLightMap->setColor(x,terrLightMap->getHeight()-2,c); + } + } + } + */ + + delete mShadowVolume; + + Con::printf(" = terrain lit in %3.3f seconds", (Platform::getRealMilliseconds()-time)/1000.f); +} + +//------------------------------------------------------------------------------ +S32 blTerrainProxy::testSquare(const Point3F & min, const Point3F & max, S32 mask, F32 expand, const Vector & clipPlanes) +{ + expand = 0; + S32 retMask = 0; + Point3F minPoint, maxPoint; + for(S32 i = 0; i < clipPlanes.size(); i++) + { + if(mask & (1 << i)) + { + if(clipPlanes[i].x > 0) + { + maxPoint.x = max.x; + minPoint.x = min.x; + } + else + { + maxPoint.x = min.x; + minPoint.x = max.x; + } + if(clipPlanes[i].y > 0) + { + maxPoint.y = max.y; + minPoint.y = min.y; + } + else + { + maxPoint.y = min.y; + minPoint.y = max.y; + } + if(clipPlanes[i].z > 0) + { + maxPoint.z = max.z; + minPoint.z = min.z; + } + else + { + maxPoint.z = min.z; + minPoint.z = max.z; + } + F32 maxDot = mDot(maxPoint, clipPlanes[i]); + F32 minDot = mDot(minPoint, clipPlanes[i]); + F32 planeD = clipPlanes[i].d; + if(maxDot <= -(planeD + expand)) + return(U16(-1)); + if(minDot <= -planeD) + retMask |= (1 << i); + } + } + return(retMask); +} + +bool blTerrainProxy::getShadowedSquares(const Vector & clipPlanes, Vector & shadowList) +{ + TerrainBlock *terrain = getObject(); + if ( !terrain ) + return false; + + // TODO: Fix me for variable terrain sizes! + return true; + + /* + SquareStackNode stack[TerrainBlock::BlockShift * 4]; + + stack[0].mLevel = TerrainBlock::BlockShift; + stack[0].mClipFlags = 0xff; + stack[0].mPos.set(0,0); + + U32 stackSize = 1; + + Point3F blockPos; + terrain->getTransform().getColumn(3, &blockPos); + S32 squareSize = terrain->getSquareSize(); + F32 floatSquareSize = (F32)squareSize; + + bool marked = false; + + // push through all the levels of the quadtree + while(stackSize) + { + SquareStackNode * node = &stack[stackSize - 1]; + + S32 clipFlags = node->mClipFlags; + Point2I pos = node->mPos; + GridSquare * sq = terrain->findSquare(node->mLevel, pos); + + Point3F minPoint, maxPoint; + minPoint.set(squareSize * pos.x + blockPos.x, + squareSize * pos.y + blockPos.y, + fixedToFloat(sq->minHeight)); + maxPoint.set(minPoint.x + (squareSize << node->mLevel), + minPoint.y + (squareSize << node->mLevel), + fixedToFloat(sq->maxHeight)); + + // test the square against the current level + if(clipFlags) + { + clipFlags = testSquare(minPoint, maxPoint, clipFlags, floatSquareSize, clipPlanes); + if(clipFlags == U16(-1)) + { + stackSize--; + continue; + } + } + + // shadowed? + if(node->mLevel == 0) + { + marked = true; + shadowList.push_back(pos.x + (pos.y << TerrainBlock::BlockShift)); + stackSize--; + continue; + } + + // setup the next level of squares + U8 nextLevel = node->mLevel - 1; + S32 squareHalfSize = 1 << nextLevel; + + for(U32 i = 0; i < 4; i++) + { + node[i].mLevel = nextLevel; + node[i].mClipFlags = clipFlags; + } + + node[3].mPos = pos; + node[2].mPos.set(pos.x + squareHalfSize, pos.y); + node[1].mPos.set(pos.x, pos.y + squareHalfSize); + node[0].mPos.set(pos.x + squareHalfSize, pos.y + squareHalfSize); + + stackSize += 3; + } + + return marked; + */ +} + +void blTerrainProxy::lightVector(LightInfo * light) +{ + // Grab our terrain object + TerrainBlock* terrain = getObject(); + if (!terrain) + return; + + // Get the direction to the light (the inverse of the direction + // the light is pointing) + Point3F lightDir = -light->getDirection(); + lightDir.normalize(); + + // Get the ratio between the light map pixel and world space (used below) + F32 lmTerrRatio = (F32)mTerrainBlockSize / (F32) mLightMapSize; + lmTerrRatio *= terrain->getSquareSize(); + + // Get the terrain position + Point3F terrPos( terrain->getTransform().getPosition() ); + + U32 i = 0; + for (U32 y = 0; y < mLightMapSize; y++) + { + for (U32 x = 0; x < mLightMapSize; x++) + { + // Get the relative pixel position and scale it + // by the ratio between lightmap and world space + Point2F pixelPos(x, y); + pixelPos *= lmTerrRatio; + + // Start with a default normal of straight up + Point3F normal(0.0f, 0.0f, 1.0f); + + // Try to get the actual normal from the terrain. + // Note: this won't change the default normal if + // it can't find a normal. + terrain->getNormal(pixelPos, &normal); + + // The terrain lightmap only contains shadows. + F32 shadowed = 0.0f; + + // Get the height at the lightmap pixel's position + F32 height = 0.0f; + terrain->getHeight(pixelPos, &height); + + // Calculate the 3D position of the pixel + Point3F pixelPos3F(pixelPos.x, pixelPos.y, height); + + // Translate that position by the terrain's transform + terrain->getTransform().mulP(pixelPos3F); + + // Offset slighting along the normal so that we don't + // raycast into ourself + pixelPos3F += (normal * 0.1f); + + // Calculate the light's position. + // If it is a vector light like the sun (no position + // just direction) then translate along that direction + // a reasonable distance to get a point sufficiently + // far away + Point3F lightPos = light->getPosition(); + if(light->getType() == LightInfo::Vector) + { + lightPos = 1000.f * lightDir; + lightPos = pixelPos3F + lightPos; + } + + // Cast a ray from the world space position of the lightmap pixel to the light source. + // If we hit something then we are in shadow. This allows us to be shadowed by anything + // that supports a castRay operation. + RayInfo info; + if(terrain->getContainer()->castRay(pixelPos3F, lightPos, STATIC_COLLISION_TYPEMASK, &info)) + { + // Shadow the pixel. + shadowed = 1.0f; + } + + // Set the final lightmap color. + mLightmap[i++] += ColorF::WHITE * mClampF( 1.0f - shadowed, 0.0f, 1.0f ); + } + } +} + +//-------------------------------------------------------------------------- +U32 blTerrainProxy::getResourceCRC() +{ + TerrainBlock * terrain = getObject(); + if(!terrain) + return(0); + return(terrain->getCRC()); +} + +//-------------------------------------------------------------------------- +bool blTerrainProxy::setPersistInfo(PersistInfo::PersistChunk * info) +{ + if(!Parent::setPersistInfo(info)) + return(false); + + blTerrainChunk * chunk = dynamic_cast(info); + AssertFatal(chunk, "blTerrainProxy::setPersistInfo: invalid info chunk!"); + + TerrainBlock * terrain = getObject(); + if(!terrain || !terrain->getLightMap()) + return(false); + + terrain->setLightMap( new GBitmap( *chunk->mLightmap) ); + + return(true); +} + +bool blTerrainProxy::getPersistInfo(PersistInfo::PersistChunk * info) +{ + if(!Parent::getPersistInfo(info)) + return(false); + + blTerrainChunk * chunk = dynamic_cast(info); + AssertFatal(chunk, "blTerrainProxy::getPersistInfo: invalid info chunk!"); + + TerrainBlock * terrain = getObject(); + if(!terrain || !terrain->getLightMap()) + return(false); + + if(chunk->mLightmap) delete chunk->mLightmap; + + chunk->mLightmap = new GBitmap(*terrain->getLightMap()); + + return(true); +} + +bool blTerrainProxy::supportsShadowVolume() +{ + return false; +} + +void blTerrainProxy::getClipPlanes(Vector& planes) +{ + +} + +void blTerrainProxy::addToShadowVolume(ShadowVolumeBSP * shadowVolume, LightInfo * light, S32 level) +{ + +} + + + +void blTerrainSystem::init() +{ +} + +U32 blTerrainSystem::addObjectType() +{ + return TerrainObjectType; +} + +SceneLighting::ObjectProxy* blTerrainSystem::createObjectProxy(SceneObject* obj, SceneLighting::ObjectProxyList* sceneObjects) +{ + if ((obj->getTypeMask() & TerrainObjectType) != 0) + return new blTerrainProxy(obj); + else + return NULL; +} + +PersistInfo::PersistChunk* blTerrainSystem::createPersistChunk(const U32 chunkType) +{ + if (chunkType == PersistInfo::PersistChunk::TerrainChunkType) + return new blTerrainChunk(); + else + return NULL; +} + +bool blTerrainSystem::createPersistChunkFromProxy(SceneLighting::ObjectProxy* objproxy, PersistInfo::PersistChunk **ret) +{ + if (dynamic_cast(objproxy) != NULL) + { + *ret = new blTerrainChunk(); + return true; + } else { + return NULL; + } +} + +// Given a ray, this will return the color from the lightmap of this object, return true if handled +bool blTerrainSystem::getColorFromRayInfo(const RayInfo & collision, ColorF& result) const +{ + TerrainBlock *terrain = dynamic_cast(collision.object); + if (!terrain) + return false; + + Point2F uv; + F32 terrainlength = (F32)terrain->getBlockSize(); + Point3F pos = terrain->getPosition(); + uv.x = (collision.point.x - pos.x) / terrainlength; + uv.y = (collision.point.y - pos.y) / terrainlength; + + // similar to x = x & width... + uv.x = uv.x - F32(U32(uv.x)); + uv.y = uv.y - F32(U32(uv.y)); + const GBitmap* lightmap = terrain->getLightMap(); + if (!lightmap) + return false; + + result = lightmap->sampleTexel(uv.x, uv.y); + // terrain lighting is dim - look into this (same thing done in shaders)... + result *= 2.0f; + return true; +} diff --git a/Engine/source/lighting/basic/blTerrainSystem.h b/Engine/source/lighting/basic/blTerrainSystem.h new file mode 100644 index 000000000..9de7c4fe4 --- /dev/null +++ b/Engine/source/lighting/basic/blTerrainSystem.h @@ -0,0 +1,49 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _BLTERRAINSYSTEM_H_ +#define _BLTERRAINSYSTEM_H_ + +#ifndef _SCENELIGHTING_H_ + #include "lighting/common/sceneLighting.h" +#endif +#ifndef _SG_SYSTEM_INTERFACE_H + #include "lighting/lightingInterfaces.h" +#endif + +// +// Lighting system interface +// +class blTerrainSystem : public SceneLightingInterface +{ +public: + virtual void init(); + virtual U32 addObjectType(); + virtual SceneLighting::ObjectProxy* createObjectProxy(SceneObject* obj, SceneLighting::ObjectProxyList* sceneObjects); + virtual PersistInfo::PersistChunk* createPersistChunk(const U32 chunkType); + virtual bool createPersistChunkFromProxy(SceneLighting::ObjectProxy* objproxy, PersistInfo::PersistChunk **ret); + + // Given a ray, this will return the color from the lightmap of this object, return true if handled + virtual bool getColorFromRayInfo(const RayInfo & collision, ColorF& result) const; +}; + +#endif // !_BLTERRAINSYSTEM_H_ diff --git a/Engine/source/lighting/common/blobShadow.cpp b/Engine/source/lighting/common/blobShadow.cpp new file mode 100644 index 000000000..ce1397cd1 --- /dev/null +++ b/Engine/source/lighting/common/blobShadow.cpp @@ -0,0 +1,352 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/common/blobShadow.h" + +#include "gfx/primBuilder.h" +#include "gfx/gfxTextureManager.h" +#include "gfx/bitmap/gBitmap.h" +#include "math/mathUtils.h" +#include "lighting/lightInfo.h" +#include "lighting/lightingInterfaces.h" +#include "T3D/shapeBase.h" +#include "scene/sceneManager.h" +#include "lighting/lightManager.h" +#include "ts/tsMesh.h" + +DepthSortList BlobShadow::smDepthSortList; +GFXTexHandle BlobShadow::smGenericShadowTexture = NULL; +S32 BlobShadow::smGenericShadowDim = 32; +U32 BlobShadow::smShadowMask = TerrainObjectType | InteriorObjectType; +F32 BlobShadow::smGenericRadiusSkew = 0.4f; // shrink radius of shape when it always uses generic shadow... + +Box3F gBlobShadowBox; +SphereF gBlobShadowSphere; +Point3F gBlobShadowPoly[4]; + +//-------------------------------------------------------------- + +BlobShadow::BlobShadow(SceneObject* parentObject, LightInfo* light, TSShapeInstance* shapeInstance) +{ + mParentObject = parentObject; + mShapeBase = dynamic_cast(parentObject); + mParentLight = light; + mShapeInstance = shapeInstance; + mRadius = 0.0f; + mLastRenderTime = 0; + mDepthBias = -0.0002f; + + generateGenericShadowBitmap(smGenericShadowDim); + setupStateBlocks(); +} + +void BlobShadow::setupStateBlocks() +{ + GFXStateBlockDesc sh; + sh.cullDefined = true; + sh.cullMode = GFXCullNone; + sh.zDefined = true; + sh.zEnable = true; + sh.zWriteEnable = false; + + sh.zBias = mDepthBias; + sh.blendDefined = true; + sh.blendEnable = true; + sh.blendSrc = GFXBlendSrcAlpha; + sh.blendDest = GFXBlendInvSrcAlpha; + sh.alphaDefined = true; + sh.alphaTestEnable = true; + sh.alphaTestFunc = GFXCmpGreater; + sh.alphaTestRef = 0; + sh.samplersDefined = true; + sh.samplers[0] = GFXSamplerStateDesc::getClampLinear(); + mShadowSB = GFX->createStateBlock(sh); +} + +BlobShadow::~BlobShadow() +{ + mShadowBuffer = NULL; +} + +bool BlobShadow::shouldRender(F32 camDist) +{ + Point3F lightDir; + + if (mShapeBase && mShapeBase->getFadeVal() < TSMesh::VISIBILITY_EPSILON) + return false; + + F32 shadowLen = 10.0f * mShapeInstance->getShape()->radius; + Point3F pos = mShapeInstance->getShape()->center; + + // this is a bit of a hack...move generic shadows towards feet/base of shape + pos *= 0.5f; + pos.convolve(mParentObject->getScale()); + mParentObject->getRenderTransform().mulP(pos); + if(mParentLight->getType() == LightInfo::Vector) + { + lightDir = mParentLight->getDirection(); + } + else + { + lightDir = pos - mParentLight->getPosition(); + lightDir.normalize(); + } + + // pos is where shadow will be centered (in world space) + setRadius(mShapeInstance, mParentObject->getScale()); + bool render = prepare(pos, lightDir, shadowLen); + return render; +} + +void BlobShadow::generateGenericShadowBitmap(S32 dim) +{ + if(smGenericShadowTexture) + return; + GBitmap * bitmap = new GBitmap(dim,dim,false,GFXFormatR8G8B8A8); + U8 * bits = bitmap->getWritableBits(); + dMemset(bits, 0, dim*dim*4); + S32 center = dim >> 1; + F32 invRadiusSq = 1.0f / (F32)(center*center); + F32 tmpF; + for (S32 i=0; i0.99f ? 0 : (U8)(180.0f*(1.0f-tmpF)); // 180 out of 255 max + bits[(i*dim*4)+(j*4)+3] = val; + } + } + + smGenericShadowTexture.set( bitmap, &GFXDefaultStaticDiffuseProfile, true, "BlobShadow" ); +} + +//-------------------------------------------------------------- + +void BlobShadow::setLightMatrices(const Point3F & lightDir, const Point3F & pos) +{ + AssertFatal(mDot(lightDir,lightDir)>0.0001f,"BlobShadow::setLightDir: light direction must be a non-zero vector."); + + // construct light matrix + Point3F x,z; + if (mFabs(lightDir.z)>0.001f) + { + // mCross(Point3F(1,0,0),lightDir,&z); + z.x = 0.0f; + z.y = lightDir.z; + z.z = -lightDir.y; + z.normalize(); + mCross(lightDir,z,&x); + } + else + { + mCross(lightDir,Point3F(0,0,1),&x); + x.normalize(); + mCross(x,lightDir,&z); + } + + mLightToWorld.identity(); + mLightToWorld.setColumn(0,x); + mLightToWorld.setColumn(1,lightDir); + mLightToWorld.setColumn(2,z); + mLightToWorld.setColumn(3,pos); + + mWorldToLight = mLightToWorld; + mWorldToLight.inverse(); +} + +void BlobShadow::setRadius(F32 radius) +{ + mRadius = radius; +} + +void BlobShadow::setRadius(TSShapeInstance * shapeInstance, const Point3F & scale) +{ + const Box3F & bounds = shapeInstance->getShape()->bounds; + F32 dx = 0.5f * (bounds.maxExtents.x-bounds.minExtents.x) * scale.x; + F32 dy = 0.5f * (bounds.maxExtents.y-bounds.minExtents.y) * scale.y; + F32 dz = 0.5f * (bounds.maxExtents.z-bounds.minExtents.z) * scale.z; + mRadius = mSqrt(dx*dx+dy*dy+dz*dz); +} + + +//-------------------------------------------------------------- + +bool BlobShadow::prepare(const Point3F & pos, Point3F lightDir, F32 shadowLen) +{ + if (mPartition.empty()) + { + // -------------------------------------- + // 1. + F32 dirMult = (1.0f) * (1.0f); + if (dirMult < 0.99f) + { + lightDir.z *= dirMult; + lightDir.z -= 1.0f - dirMult; + } + lightDir.normalize(); + shadowLen *= (1.0f) * (1.0f); + + // -------------------------------------- + // 2. get polys + F32 radius = mRadius; + radius *= smGenericRadiusSkew; + buildPartition(pos,lightDir,radius,shadowLen); + } + if (mPartition.empty()) + // no need to draw shadow if nothing to cast it onto + return false; + + return true; +} + +//-------------------------------------------------------------- + +void BlobShadow::buildPartition(const Point3F & p, const Point3F & lightDir, F32 radius, F32 shadowLen) +{ + setLightMatrices(lightDir,p); + + Point3F extent(2.0f*radius,shadowLen,2.0f*radius); + smDepthSortList.clear(); + smDepthSortList.set(mWorldToLight,extent); + smDepthSortList.setInterestNormal(lightDir); + + if (shadowLen<1.0f) + // no point in even this short of a shadow... + shadowLen = 1.0f; + mInvShadowDistance = 1.0f / shadowLen; + + // build world space box and sphere around shadow + + Point3F x,y,z; + mLightToWorld.getColumn(0,&x); + mLightToWorld.getColumn(1,&y); + mLightToWorld.getColumn(2,&z); + x *= radius; + y *= shadowLen; + z *= radius; + gBlobShadowBox.maxExtents.set(mFabs(x.x)+mFabs(y.x)+mFabs(z.x), + mFabs(x.y)+mFabs(y.y)+mFabs(z.y), + mFabs(x.z)+mFabs(y.z)+mFabs(z.z)); + y *= 0.5f; + gBlobShadowSphere.radius = gBlobShadowBox.maxExtents.len(); + gBlobShadowSphere.center = p + y; + gBlobShadowBox.minExtents = y + p - gBlobShadowBox.maxExtents; + gBlobShadowBox.maxExtents += y + p; + + // get polys + + gClientContainer.findObjects(STATIC_COLLISION_TYPEMASK, BlobShadow::collisionCallback, this); + + // setup partition list + gBlobShadowPoly[0].set(-radius,0,-radius); + gBlobShadowPoly[1].set(-radius,0, radius); + gBlobShadowPoly[2].set( radius,0, radius); + gBlobShadowPoly[3].set( radius,0,-radius); + + mPartition.clear(); + mPartitionVerts.clear(); + smDepthSortList.depthPartition(gBlobShadowPoly,4,mPartition,mPartitionVerts); + + if(mPartitionVerts.empty()) + return; + + // Find the rough distance of the shadow verts + // from the object position and use that to scale + // the visibleAlpha so that the shadow fades out + // the further away from you it gets + F32 dist = 0.0f; + + // Calculate the center of the partition verts + Point3F shadowCenter(0.0f, 0.0f, 0.0f); + for (U32 i = 0; i < mPartitionVerts.size(); i++) + shadowCenter += mPartitionVerts[i]; + + shadowCenter /= mPartitionVerts.size(); + + mLightToWorld.mulP(shadowCenter); + + dist = (p - shadowCenter).len(); + + // now set up tverts & colors + mShadowBuffer.set(GFX, mPartitionVerts.size(), GFXBufferTypeVolatile); + mShadowBuffer.lock(); + + F32 visibleAlpha = 255.0f; + if (mShapeBase && mShapeBase->getFadeVal()) + visibleAlpha = mClampF(255.0f * mShapeBase->getFadeVal(), 0, 255); + visibleAlpha *= 1.0f - (dist / gBlobShadowSphere.radius); + F32 invRadius = 1.0f / radius; + for (S32 i=0; igetWorldBox().isOverlapped(gBlobShadowBox)) + { + // only interiors clip... + ClippedPolyList::allowClipping = (obj->getTypeMask() & LIGHTMGR->getSceneLightingInterface()->mClippingMask) != 0; + obj->buildPolyList(PLC_Collision,&smDepthSortList,gBlobShadowBox,gBlobShadowSphere); + ClippedPolyList::allowClipping = true; + } +} + +//-------------------------------------------------------------- + +void BlobShadow::render( F32 camDist, const TSRenderState &rdata ) +{ + mLastRenderTime = Platform::getRealMilliseconds(); + GFX->pushWorldMatrix(); + MatrixF world = GFX->getWorldMatrix(); + world.mul(mLightToWorld); + GFX->setWorldMatrix(world); + + GFX->disableShaders(); + + GFX->setStateBlock(mShadowSB); + GFX->setTexture(0, smGenericShadowTexture); + GFX->setVertexBuffer(mShadowBuffer); + + for(U32 p=0; pdrawPrimitive(GFXTriangleFan, mPartition[p].vertexStart, (mPartition[p].vertexCount - 2)); + + // This is a bad nasty hack which forces the shadow to reconstruct itself every frame. + mPartition.clear(); + + GFX->popWorldMatrix(); +} + +void BlobShadow::deleteGenericShadowBitmap() +{ + smGenericShadowTexture = NULL; +} diff --git a/Engine/source/lighting/common/blobShadow.h b/Engine/source/lighting/common/blobShadow.h new file mode 100644 index 000000000..a56f51dab --- /dev/null +++ b/Engine/source/lighting/common/blobShadow.h @@ -0,0 +1,87 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _BLOBSHADOW_H_ +#define _BLOBSHADOW_H_ + +#include "collision/depthSortList.h" +#include "scene/sceneObject.h" +#include "ts/tsShapeInstance.h" +#include "lighting/common/shadowBase.h" + +class ShapeBase; +class LightInfo; + +class BlobShadow : public ShadowBase +{ + F32 mRadius; + F32 mInvShadowDistance; + MatrixF mLightToWorld; + MatrixF mWorldToLight; + + Vector mPartition; + Vector mPartitionVerts; + GFXVertexBufferHandle mShadowBuffer; + + static U32 smShadowMask; + + static DepthSortList smDepthSortList; + static GFXTexHandle smGenericShadowTexture; + static F32 smGenericRadiusSkew; + static S32 smGenericShadowDim; + + U32 mLastRenderTime; + + static void collisionCallback(SceneObject*,void *); + +private: + SceneObject* mParentObject; + ShapeBase* mShapeBase; + LightInfo* mParentLight; + TSShapeInstance* mShapeInstance; + GFXStateBlockRef mShadowSB; + F32 mDepthBias; + + void setupStateBlocks(); + void setLightMatrices(const Point3F & lightDir, const Point3F & pos); + void buildPartition(const Point3F & p, const Point3F & lightDir, F32 radius, F32 shadowLen); +public: + + BlobShadow(SceneObject* parentobject, LightInfo* light, TSShapeInstance* shapeinstance); + ~BlobShadow(); + + void setRadius(F32 radius); + void setRadius(TSShapeInstance *, const Point3F & scale); + + bool prepare(const Point3F & pos, Point3F lightDir, F32 shadowLen); + + bool shouldRender(F32 camDist); + + void update( const SceneRenderState *state ) {} + void render( F32 camDist, const TSRenderState &rdata ); + U32 getLastRenderTime() const { return mLastRenderTime; } + + static void generateGenericShadowBitmap(S32 dim); + static void deleteGenericShadowBitmap(); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/lighting/common/lightMapParams.cpp b/Engine/source/lighting/common/lightMapParams.cpp new file mode 100644 index 000000000..bc161cb32 --- /dev/null +++ b/Engine/source/lighting/common/lightMapParams.cpp @@ -0,0 +1,61 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "lighting/common/lightMapParams.h" +#include "core/stream/bitStream.h" + +const LightInfoExType LightMapParams::Type( "LightMapParams" ); + +LightMapParams::LightMapParams( LightInfo *light ) : + representedInLightmap(false), + includeLightmappedGeometryInShadow(false), + shadowDarkenColor(0.0f, 0.0f, 0.0f, -1.0f) +{ + +} + +LightMapParams::~LightMapParams() +{ + +} + +void LightMapParams::set( const LightInfoEx *ex ) +{ + // TODO: Do we even need this? +} + +void LightMapParams::packUpdate( BitStream *stream ) const +{ + stream->writeFlag(representedInLightmap); + stream->writeFlag(includeLightmappedGeometryInShadow); + stream->write(shadowDarkenColor); +} + +void LightMapParams::unpackUpdate( BitStream *stream ) +{ + representedInLightmap = stream->readFlag(); + includeLightmappedGeometryInShadow = stream->readFlag(); + stream->read(&shadowDarkenColor); + + // Always make sure that the alpha value of the shadowDarkenColor is -1.0 + shadowDarkenColor.alpha = -1.0f; +} \ No newline at end of file diff --git a/Engine/source/lighting/common/lightMapParams.h b/Engine/source/lighting/common/lightMapParams.h new file mode 100644 index 000000000..fa83c063c --- /dev/null +++ b/Engine/source/lighting/common/lightMapParams.h @@ -0,0 +1,54 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _LIGHTMAPPARAMS_H_ +#define _LIGHTMAPPARAMS_H_ + +#ifndef _LIGHTINFO_H_ +#include "lighting/lightInfo.h" +#endif + +class LightMapParams : public LightInfoEx +{ +public: + LightMapParams( LightInfo *light ); + virtual ~LightMapParams(); + + /// The LightInfoEx hook type. + static const LightInfoExType Type; + + // LightInfoEx + virtual void set( const LightInfoEx *ex ); + virtual const LightInfoExType& getType() const { return Type; } + virtual void packUpdate( BitStream *stream ) const; + virtual void unpackUpdate( BitStream *stream ); + +public: + // We're leaving these public for easy access + // for console protected fields. + + bool representedInLightmap; ///< This light is represented in lightmaps (static light, default: false) + ColorF shadowDarkenColor; ///< The color that should be used to multiply-blend dynamic shadows onto lightmapped geometry (ignored if 'representedInLightmap' is false) + bool includeLightmappedGeometryInShadow; ///< This light should render lightmapped geometry during its shadow-map update (ignored if 'representedInLightmap' is false) +}; + +#endif diff --git a/Engine/source/lighting/common/projectedShadow.cpp b/Engine/source/lighting/common/projectedShadow.cpp new file mode 100644 index 000000000..de3a31f8f --- /dev/null +++ b/Engine/source/lighting/common/projectedShadow.cpp @@ -0,0 +1,582 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/common/projectedShadow.h" + +#include "gfx/primBuilder.h" +#include "gfx/gfxTextureManager.h" +#include "gfx/bitmap/gBitmap.h" +#include "gfx/gfxDebugEvent.h" +#include "math/mathUtils.h" +#include "lighting/lightInfo.h" +#include "lighting/lightingInterfaces.h" +#include "T3D/shapeBase.h" +#include "scene/sceneManager.h" +#include "lighting/lightManager.h" +#include "ts/tsMesh.h" +#include "T3D/decal/decalManager.h" +#include "T3D/decal/decalInstance.h" +#include "renderInstance/renderPassManager.h" +#include "renderInstance/renderMeshMgr.h" +#include "gfx/gfxTransformSaver.h" +#include "materials/customMaterialDefinition.h" +#include "materials/materialFeatureTypes.h" +#include "console/console.h" +#include "postFx/postEffect.h" +#include "lighting/basic/basicLightManager.h" +#include "lighting/shadowMap/shadowMatHook.h" +#include "materials/materialManager.h" +#include "lighting/shadowMap/lightShadowMap.h" + + +SimObjectPtr ProjectedShadow::smRenderPass = NULL; +SimObjectPtr ProjectedShadow::smShadowFilter = NULL; +F32 ProjectedShadow::smDepthAdjust = 10.0f; + +float ProjectedShadow::smFadeStartPixelSize = 200.0f; +float ProjectedShadow::smFadeEndPixelSize = 35.0f; + + +GFX_ImplementTextureProfile( BLProjectedShadowProfile, + GFXTextureProfile::DiffuseMap, + GFXTextureProfile::PreserveSize | + GFXTextureProfile::RenderTarget | + GFXTextureProfile::Pooled, + GFXTextureProfile::None ); + +GFX_ImplementTextureProfile( BLProjectedShadowZProfile, + GFXTextureProfile::DiffuseMap, + GFXTextureProfile::PreserveSize | + GFXTextureProfile::ZTarget | + GFXTextureProfile::Pooled, + GFXTextureProfile::None ); + + +ProjectedShadow::ProjectedShadow( SceneObject *object ) +{ + mParentObject = object; + mShapeBase = dynamic_cast( object ); + + mRadius = 0; + mLastRenderTime = 0; + mUpdateTexture = false; + + mShadowLength = 10.0f; + + mDecalData = new DecalData; + mDecalData->skipVertexNormals = true; + + mDecalInstance = NULL; + + mLastLightDir.set( 0, 0, 0 ); + mLastObjectPosition.set( object->getRenderPosition() ); + mLastObjectScale.set( object->getScale() ); + + CustomMaterial *customMat = NULL; + Sim::findObject( "BL_ProjectedShadowMaterial", customMat ); + if ( customMat ) + { + mDecalData->material = customMat; + mDecalData->matInst = customMat->createMatInstance(); + } + else + mDecalData->matInst = MATMGR->createMatInstance( "WarningMaterial" ); + + mDecalData->matInst->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat() ); + + mCasterPositionSC = NULL; + mShadowLengthSC = NULL; +} + +ProjectedShadow::~ProjectedShadow() +{ + if ( mDecalInstance ) + gDecalManager->removeDecal( mDecalInstance ); + + delete mDecalData; + + mShadowTexture = NULL; + mRenderTarget = NULL; +} + +bool ProjectedShadow::shouldRender( const SceneRenderState *state ) +{ + // Don't render if our object has been removed from the + // scene graph. + + if( !mParentObject->getSceneManager() ) + return false; + + // Don't render if the ShapeBase + // object's fade value is greater + // than the visibility epsilon. + bool shapeFade = mShapeBase && mShapeBase->getFadeVal() < TSMesh::VISIBILITY_EPSILON; + + // Get the shapebase datablock if we have one. + ShapeBaseData *data = NULL; + if ( mShapeBase ) + data = static_cast( mShapeBase->getDataBlock() ); + + // Also don't render if + // the camera distance is greater + // than the shadow length. + if ( shapeFade || !mDecalData || + ( mDecalInstance && + mDecalInstance->calcPixelSize( state->getViewport().extent.y, state->getCameraPosition(), state->getWorldToScreenScale().y ) < mDecalInstance->mDataBlock->fadeEndPixelSize ) ) + { + // Release our shadow texture + // so that others can grab it out + // of the pool. + mShadowTexture = NULL; + + return false; + } + + return true; +} + +bool ProjectedShadow::_updateDecal( const SceneRenderState *state ) +{ + PROFILE_SCOPE( ProjectedShadow_UpdateDecal ); + + if ( !LIGHTMGR ) + return false; + + // Get the position of the decal first. + const Box3F &objBox = mParentObject->getObjBox(); + const Point3F boxCenter = objBox.getCenter(); + Point3F decalPos = boxCenter; + const MatrixF &renderTransform = mParentObject->getRenderTransform(); + { + // Set up the decal position. + // We use the object space box center + // multiplied by the render transform + // of the object to ensure we benefit + // from interpolation. + MatrixF t( renderTransform ); + t.setColumn(2,Point3F::UnitZ); + t.mulP( decalPos ); + } + + if ( mDecalInstance ) + { + mDecalInstance->mPosition = decalPos; + if ( !shouldRender( state ) ) + return false; + } + + // Get the sunlight for the shadow projection. + // We want the LightManager to return NULL if it can't + // get the "real" sun, so we specify false for the useDefault parameter. + LightInfo *lights[4] = {0}; + LightQuery query; + query.init( mParentObject->getWorldSphere() ); + query.getLights( lights, 4 ); + + Point3F pos = renderTransform.getPosition(); + + Point3F lightDir( 0, 0, 0 ); + Point3F tmp( 0, 0, 0 ); + F32 weight = 0; + F32 range = 0; + U32 lightCount = 0; + F32 dist = 0; + F32 fade = 0; + for ( U32 i = 0; i < 4; i++ ) + { + // If we got a NULL light, + // we're at the end of the list. + if ( !lights[i] ) + break; + + if ( !lights[i]->getCastShadows() ) + continue; + + if ( lights[i]->getType() != LightInfo::Point ) + tmp = lights[i]->getDirection(); + else + tmp = pos - lights[i]->getPosition(); + + range = lights[i]->getRange().x; + dist = ( (tmp.lenSquared()) / ((range * range) * 0.5f)); + weight = mClampF( 1.0f - ( tmp.lenSquared() / (range * range)), 0.00001f, 1.0f ); + + if ( lights[i]->getType() == LightInfo::Vector ) + fade = getMax( fade, 1.0f ); + else + fade = getMax( fade, mClampF( 1.0f - dist, 0.00001f, 1.0f ) ); + + lightDir += tmp * weight; + lightCount++; + } + + lightDir.normalize(); + + // No light... no shadow. + if ( !lights[0] ) + return false; + + // Has the light direction + // changed since last update? + bool lightDirChanged = !mLastLightDir.equal( lightDir ); + + // Has the parent object moved + // or scaled since the last update? + bool hasMoved = !mLastObjectPosition.equal( mParentObject->getRenderPosition() ); + bool hasScaled = !mLastObjectScale.equal( mParentObject->getScale() ); + + // Set the last light direction + // to the current light direction. + mLastLightDir = lightDir; + mLastObjectPosition = mParentObject->getRenderPosition(); + mLastObjectScale = mParentObject->getScale(); + + + // Temps used to generate + // tangent vector for DecalInstance below. + VectorF right( 0, 0, 0 ); + VectorF fwd( 0, 0, 0 ); + VectorF tmpFwd( 0, 0, 0 ); + + U32 idx = lightDir.getLeastComponentIndex(); + + tmpFwd[idx] = 1.0f; + + right = mCross( tmpFwd, lightDir ); + fwd = mCross( lightDir, right ); + right = mCross( fwd, lightDir ); + + right.normalize(); + + // Set up the world to light space + // matrix, along with proper position + // and rotation to be used as the world + // matrix for the render to texture later on. + static MatrixF sRotMat(EulerF( 0.0f, -(M_PI_F/2.0f), 0.0f)); + mWorldToLight.identity(); + MathUtils::getMatrixFromForwardVector( lightDir, &mWorldToLight ); + mWorldToLight.setPosition( ( pos + boxCenter ) - ( ( (mRadius * smDepthAdjust) + 0.001f ) * lightDir ) ); + mWorldToLight.mul( sRotMat ); + mWorldToLight.inverse(); + + // Get the shapebase datablock if we have one. + ShapeBaseData *data = NULL; + if ( mShapeBase ) + data = static_cast( mShapeBase->getDataBlock() ); + + // We use the object box's extents multiplied + // by the object's scale divided by 2 for the radius + // because the object's worldsphere radius is not + // rotationally invariant. + mRadius = (objBox.getExtents() * mParentObject->getScale()).len() * 0.5f; + + if ( data ) + mRadius *= data->shadowSphereAdjust; + + // Create the decal if we don't have one yet. + if ( !mDecalInstance ) + mDecalInstance = gDecalManager->addDecal( decalPos, + lightDir, + right, + mDecalData, + 1.0f, + 0, + PermanentDecal | ClipDecal | CustomDecal ); + + if ( !mDecalInstance ) + return false; + + mDecalInstance->mVisibility = fade; + + // Setup decal parameters. + mDecalInstance->mSize = mRadius * 2.0f; + mDecalInstance->mNormal = -lightDir; + mDecalInstance->mTangent = -right; + mDecalInstance->mRotAroundNormal = 0; + mDecalInstance->mPosition = decalPos; + mDecalInstance->mDataBlock = mDecalData; + + // If the position of the world + // space box center is the same + // as the decal's position, and + // the light direction has not + // changed, we don't need to clip. + bool shouldClip = lightDirChanged || hasMoved || hasScaled; + + // Now, check and see if the object is visible. + const Frustum &frust = state->getFrustum(); + if ( frust.isCulled( SphereF( mDecalInstance->mPosition, mDecalInstance->mSize * mDecalInstance->mSize ) ) && !shouldClip ) + return false; + + F32 shadowLen = 10.0f; + if ( data ) + shadowLen = data->shadowProjectionDistance; + + const Point3F &boxExtents = objBox.getExtents(); + + + mShadowLength = shadowLen * mParentObject->getScale().z; + + // Set up clip depth, and box half + // offset for decal clipping. + Point2F clipParams( mShadowLength, (boxExtents.x + boxExtents.y) * 0.25f ); + + bool render = false; + bool clipSucceeded = true; + + // Clip! + if ( shouldClip ) + { + clipSucceeded = gDecalManager->clipDecal( mDecalInstance, + NULL, + &clipParams ); + } + + // If the clip failed, + // we'll return false in + // order to keep from + // unnecessarily rendering + // into the texture. If + // there was no reason to clip + // on this update, we'll assume we + // should update the texture. + render = clipSucceeded; + + // Tell the DecalManager we've changed this decal. + gDecalManager->notifyDecalModified( mDecalInstance ); + + return render; +} + +void ProjectedShadow::_calcScore( const SceneRenderState *state ) +{ + if ( !mDecalInstance ) + return; + + F32 pixRadius = mDecalInstance->calcPixelSize( state->getViewport().extent.y, state->getCameraPosition(), state->getWorldToScreenScale().y ); + + F32 pct = pixRadius / mDecalInstance->mDataBlock->fadeStartPixelSize; + + U32 msSinceLastRender = Platform::getVirtualMilliseconds() - getLastRenderTime(); + + ShapeBaseData *data = NULL; + if ( mShapeBase ) + data = static_cast( mShapeBase->getDataBlock() ); + + // For every 1s this shadow hasn't been + // updated we'll add 10 to the score. + F32 secs = mFloor( (F32)msSinceLastRender / 1000.0f ); + + mScore = pct + secs; + mClampF( mScore, 0.0f, 2000.0f ); +} + +void ProjectedShadow::update( const SceneRenderState *state ) +{ + mUpdateTexture = true; + + // Set the decal lod settings. + mDecalData->fadeStartPixelSize = smFadeStartPixelSize; + mDecalData->fadeEndPixelSize = smFadeEndPixelSize; + + // Update our decal before + // we render to texture. + // If it fails, something bad happened + // (no light to grab/failed clip) and we should return. + if ( !_updateDecal( state ) ) + { + // Release our shadow texture + // so that others can grab it out + // of the pool. + mShadowTexture = NULL; + mUpdateTexture = false; + return; + } + + _calcScore( state ); + + if ( !mCasterPositionSC || !mCasterPositionSC->isValid() ) + mCasterPositionSC = mDecalData->matInst->getMaterialParameterHandle( "$shadowCasterPosition" ); + + if ( !mShadowLengthSC || !mShadowLengthSC->isValid() ) + mShadowLengthSC = mDecalData->matInst->getMaterialParameterHandle( "$shadowLength" ); + + MaterialParameters *matParams = mDecalData->matInst->getMaterialParameters(); + + matParams->setSafe( mCasterPositionSC, mParentObject->getRenderPosition() ); + matParams->setSafe( mShadowLengthSC, mShadowLength / 4.0f ); +} + +void ProjectedShadow::render( F32 camDist, const TSRenderState &rdata ) +{ + if ( !mUpdateTexture ) + return; + + // Do the render to texture, + // DecalManager handles rendering + // the shadow onto the world. + _renderToTexture( camDist, rdata ); +} + +BaseMatInstance* ProjectedShadow::_getShadowMaterial( BaseMatInstance *inMat ) +{ + // See if we have an existing material hook. + ShadowMaterialHook *hook = static_cast( inMat->getHook( ShadowMaterialHook::Type ) ); + if ( !hook ) + { + // Create a hook and initialize it using the incoming material. + hook = new ShadowMaterialHook; + hook->init( inMat ); + inMat->addHook( hook ); + } + + return hook->getShadowMat( ShadowType_Spot ); +} + +void ProjectedShadow::_renderToTexture( F32 camDist, const TSRenderState &rdata ) +{ + PROFILE_SCOPE( ProjectedShadow_RenderToTexture ); + + GFXDEBUGEVENT_SCOPE( ProjectedShadow_RenderToTexture, ColorI( 255, 0, 0 ) ); + + RenderPassManager *renderPass = _getRenderPass(); + if ( !renderPass ) + return; + + GFXTransformSaver saver; + + // NOTE: GFXTransformSaver does not save/restore the frustum + // so we must save it here before we modify it. + F32 l, r, b, t, n, f; + bool ortho; + GFX->getFrustum( &l, &r, &b, &t, &n, &f, &ortho ); + + // Set the orthographic projection + // matrix up, to be based on the radius + // generated based on our shape. + GFX->setOrtho( -mRadius, mRadius, -mRadius, mRadius, 0.001f, (mRadius * 2) * smDepthAdjust, true ); + + // Set the world to light space + // matrix set up in shouldRender(). + GFX->setWorldMatrix( mWorldToLight ); + + // Get the shapebase datablock if we have one. + ShapeBaseData *data = NULL; + if ( mShapeBase ) + data = static_cast( mShapeBase->getDataBlock() ); + + // Init or update the shadow texture size. + if ( mShadowTexture.isNull() || ( data && data->shadowSize != mShadowTexture.getWidth() ) ) + { + U32 texSize = getNextPow2( data ? data->shadowSize : 256 * LightShadowMap::smShadowTexScalar ); + mShadowTexture.set( texSize, texSize, GFXFormatR8G8B8A8, &PostFxTargetProfile, "BLShadow" ); + } + + GFX->pushActiveRenderTarget(); + + if ( !mRenderTarget ) + mRenderTarget = GFX->allocRenderToTextureTarget(); + + mRenderTarget->attachTexture( GFXTextureTarget::DepthStencil, _getDepthTarget( mShadowTexture->getWidth(), mShadowTexture->getHeight() ) ); + mRenderTarget->attachTexture( GFXTextureTarget::Color0, mShadowTexture ); + GFX->setActiveRenderTarget( mRenderTarget ); + + GFX->clear( GFXClearZBuffer | GFXClearStencil | GFXClearTarget, ColorI( 0, 0, 0, 0 ), 1.0f, 0 ); + + const SceneRenderState *diffuseState = rdata.getSceneState(); + SceneManager *sceneManager = diffuseState->getSceneManager(); + + SceneRenderState baseState + ( + sceneManager, + SPT_Shadow, + SceneCameraState::fromGFXWithViewport( diffuseState->getViewport() ), + renderPass + ); + + baseState.getMaterialDelegate().bind( &ProjectedShadow::_getShadowMaterial ); + baseState.setDiffuseCameraTransform( diffuseState->getCameraTransform() ); + baseState.setWorldToScreenScale( diffuseState->getWorldToScreenScale() ); + baseState.getCullingState().disableZoneCulling( true ); + + mParentObject->prepRenderImage( &baseState ); + renderPass->renderPass( &baseState ); + + // Delete the SceneRenderState we allocated. + mRenderTarget->resolve(); + GFX->popActiveRenderTarget(); + + // If we're close enough then filter the shadow. + if ( camDist < BasicLightManager::getShadowFilterDistance() ) + { + if ( !smShadowFilter ) + { + PostEffect *filter = NULL; + + if ( !Sim::findObject( "BL_ShadowFilterPostFx", filter ) ) + Con::errorf( "ProjectedShadow::_renderToTexture() - 'BL_ShadowFilterPostFx' not found!" ); + + smShadowFilter = filter; + } + + if ( smShadowFilter ) + smShadowFilter->process( NULL, mShadowTexture ); + } + + // Restore frustum + if (!ortho) + GFX->setFrustum(l, r, b, t, n, f); + else + GFX->setOrtho(l, r, b, t, n, f); + + // Set the last render time. + mLastRenderTime = Platform::getVirtualMilliseconds(); + + // HACK: Will remove in future release! + mDecalInstance->mCustomTex = &mShadowTexture; +} + +RenderPassManager* ProjectedShadow::_getRenderPass() +{ + if ( smRenderPass.isNull() ) + { + SimObject* renderPass = NULL; + + if ( !Sim::findObject( "BL_ProjectedShadowRPM", renderPass ) ) + Con::errorf( "ProjectedShadow::init() - 'BL_ProjectedShadowRPM' not initialized" ); + else + smRenderPass = dynamic_cast(renderPass); + } + + return smRenderPass; +} + +GFXTextureObject* ProjectedShadow::_getDepthTarget( U32 width, U32 height ) +{ + // Get a depth texture target from the pooled profile + // which is returned as a temporary. + GFXTexHandle depthTex( width, height, GFXFormatD24S8, &BLProjectedShadowZProfile, + "ProjectedShadow::_getDepthTarget()" ); + + return depthTex; +} diff --git a/Engine/source/lighting/common/projectedShadow.h b/Engine/source/lighting/common/projectedShadow.h new file mode 100644 index 000000000..7b519f3ab --- /dev/null +++ b/Engine/source/lighting/common/projectedShadow.h @@ -0,0 +1,127 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PROJECTEDSHADOW_H_ +#define _PROJECTEDSHADOW_H_ + +#ifndef _DEPTHSORTLIST_H_ +#include "collision/depthSortList.h" +#endif +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _TSSHAPEINSTANCE_H_ +#include "ts/tsShapeInstance.h" +#endif +#ifndef _LIGHTINGSYSTEM_SHADOWBASE_H_ +#include "lighting/common/shadowBase.h" +#endif + +class ShapeBase; +class LightInfo; +class DecalData; +class DecalInstance; +class RenderPassManager; +class PostEffect; +class RenderMeshMgr; +class CustomMaterial; +class BaseMatInstance; +class MaterialParameterHandle; + + +GFX_DeclareTextureProfile( BLProjectedShadowProfile ); +GFX_DeclareTextureProfile( BLProjectedShadowZProfile ); + +class ProjectedShadow : public ShadowBase +{ + +protected: + + + /// This parameter is used to + /// adjust the far plane out for our + /// orthographic render in order to + /// force our object towards one end of the + /// the eye space depth range. + static F32 smDepthAdjust; + + F32 mRadius; + MatrixF mWorldToLight; + U32 mLastRenderTime; + + F32 mShadowLength; + + F32 mScore; + bool mUpdateTexture; + + Point3F mLastObjectScale; + Point3F mLastObjectPosition; + VectorF mLastLightDir; + + DecalData *mDecalData; + DecalInstance *mDecalInstance; + + SceneObject *mParentObject; + ShapeBase *mShapeBase; + + MaterialParameterHandle *mCasterPositionSC; + MaterialParameterHandle *mShadowLengthSC; + + static SimObjectPtr smRenderPass; + + static SimObjectPtr smShadowFilter; + + static RenderPassManager* _getRenderPass(); + + GFXTexHandle mShadowTexture; + GFXTextureTargetRef mRenderTarget; + + GFXTextureObject* _getDepthTarget( U32 width, U32 height ); + void _renderToTexture( F32 camDist, const TSRenderState &rdata ); + + bool _updateDecal( const SceneRenderState *sceneState ); + + void _calcScore( const SceneRenderState *state ); + + /// Returns a spotlight shadow material for use when + /// rendering meshes into the projected shadow. + static BaseMatInstance* _getShadowMaterial( BaseMatInstance *inMat ); + +public: + + /// @see DecalData + static float smFadeStartPixelSize; + static float smFadeEndPixelSize; + + ProjectedShadow( SceneObject *object ); + virtual ~ProjectedShadow(); + + bool shouldRender( const SceneRenderState *state ); + + void update( const SceneRenderState *state ); + void render( F32 camDist, const TSRenderState &rdata ); + U32 getLastRenderTime() const { return mLastRenderTime; } + const F32 getScore() const { return mScore; } + +}; + +#endif // _PROJECTEDSHADOW_H_ \ No newline at end of file diff --git a/Engine/source/lighting/common/sceneLighting.cpp b/Engine/source/lighting/common/sceneLighting.cpp new file mode 100644 index 000000000..fe73f0086 --- /dev/null +++ b/Engine/source/lighting/common/sceneLighting.cpp @@ -0,0 +1,1110 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/common/sceneLighting.h" + +#include "T3D/gameBase/gameConnection.h" +#include "console/consoleTypes.h" +#include "scene/sceneManager.h" +#include "lighting/common/shadowVolumeBSP.h" +#include "T3D/shapeBase.h" +#include "gui/core/guiCanvas.h" +#include "ts/tsShape.h" +#include "ts/tsShapeInstance.h" +#include "T3D/staticShape.h" +#include "T3D/tsStatic.h" +#include "collision/concretePolyList.h" +#include "lighting/lightingInterfaces.h" +#include "terrain/terrData.h" +#include "platform/platformVolume.h" +#include "core/stream/fileStream.h" +#include "core/crc.h" + +//#define DUMP_LIGHTMAPS + +#ifdef DUMP_LIGHTMAPS +#include "interior/interiorInstance.h" +#include "core/volume.h" +#endif + + +namespace +{ + bool gTerminateLighting = false; + F32 gLightingProgress = 0.0f; + char * gCompleteCallback = NULL; + U32 gConnectionMissionCRC = 0xffffffff; +} + +SceneLighting *gLighting = NULL; +F32 gParellelVectorThresh = 0.01f; +F32 gPlaneNormThresh = 0.999f; +F32 gPlaneDistThresh = 0.001f; + + +void SceneLighting::sgNewEvent(U32 light, S32 object, U32 event) +{ + Sim::postEvent(this, new sgSceneLightingProcessEvent(light, object, event), Sim::getTargetTime() + 1); + // Paint canvas here? +} + +//----------------------------------------------- +/* +* Called once per scenelighting - entry point for event system +*/ +void SceneLighting::sgLightingStartEvent() +{ + Con::printf(""); + Con::printf("Starting scene lighting..."); + + sgTimeTemp2 = Platform::getRealMilliseconds(); + + // clear interior light maps + for(ObjectProxy **proxyItr = mSceneObjects.begin(); proxyItr != mSceneObjects.end(); proxyItr++) + { + ObjectProxy* objprox; + objprox = *proxyItr; + // is there an object? + if(!objprox->getObject()) + { + AssertFatal(0, "SceneLighting:: missing sceneobject on light start"); + Con::errorf(ConsoleLogEntry::General, " SceneLighting:: missing sceneobject on light start"); + continue; + } + + objprox->processLightingStart(); + } + + + sgNewEvent(0, 0, sgSceneLightingProcessEvent::sgTGEPassSetupEventType); + //sgNewEvent(0, 0, sgSceneLightingProcessEvent::sgSGPassSetupEventType); +} + +/* +* Called once per scenelighting - exit from event system +*/ +void SceneLighting::sgLightingCompleteEvent() +{ + Vector terrBlocks; + + // initialize the objects for lighting + for(ObjectProxy ** proxyItr = mSceneObjects.begin(); proxyItr != mSceneObjects.end(); proxyItr++) + { + ObjectProxy* objprox = *proxyItr; + TerrainBlock *terr = dynamic_cast(objprox->getObject()); + if (terr) + terrBlocks.push_back(terr); + } + + for (S32 i = 0; i < terrBlocks.size(); i++) + terrBlocks[i]->postLight(terrBlocks); + + // save out the lighting? + if(Con::getBoolVariable("$sceneLighting::cacheLighting", true)) + { + if(!savePersistInfo(mFileName)) + Con::errorf(ConsoleLogEntry::General, "SceneLighting::light: unable to persist lighting!"); + else + Con::printf("Successfully saved mission lighting file: '%s'", mFileName); + } + + Con::printf("Scene lighting complete (%3.3f seconds)", (Platform::getRealMilliseconds()-sgTimeTemp2)/1000.f); + Con::printf("//-----------------------------------------------"); + Con::printf(""); + + + completed(true); + deleteObject(); +} + +//----------------------------------------------- +/* +* Called once per scenelighting - used for prepping the +* event system for TGE style scenelighting +*/ +void SceneLighting::sgTGEPassSetupEvent() +{ + Con::printf(" Starting TGE based scene lighting..."); + + + sgNewEvent(0, 0, sgSceneLightingProcessEvent::sgTGELightStartEventType); +} + +/* +* Called once per light - used for calling preLight on all objects +* Only TGE lights call prelight and continue on to the process event +*/ +void SceneLighting::sgTGELightStartEvent(U32 light) +{ + // catch bad light index and jump to complete event + if(light >= mLights.size()) + { + sgNewEvent(light, 0, sgSceneLightingProcessEvent::sgTGELightCompleteEventType); + return; + } + + // can we use the light? + if(mLights[light]->getType() != LightInfo::Vector) + { + sgNewEvent((light+1), 0, sgSceneLightingProcessEvent::sgTGELightStartEventType); + return; + } + + // process pre-lighting + Con::printf(" Lighting with light #%d (TGE vector light)...", (light+1)); + LightInfo *lightobj = mLights[light]; + mLitObjects.clear(); + + for(ObjectProxy **proxyItr = mSceneObjects.begin(); proxyItr != mSceneObjects.end(); proxyItr++) + { + ObjectProxy* objprox = *proxyItr; + + // is there an object? + if(!objprox->getObject()) + { + AssertFatal(0, "SceneLighting:: missing sceneobject on light start"); + Con::errorf(ConsoleLogEntry::General, " SceneLighting:: missing sceneobject on light start"); + continue; + } + + if (objprox->tgePreLight(lightobj)) + mLitObjects.push_back(objprox); + } + + // kick off lighting + + sgNewEvent(light, 0, sgSceneLightingProcessEvent::sgTGELightProcessEventType); +} + +/* +* Called once for each TGE light and object - used for calling light on an object +*/ +void SceneLighting::sgTGELightProcessEvent(U32 light, S32 object) +{ + // catch bad light or object index + if((light >= mLights.size()) || (object >= mLitObjects.size())) + { + sgNewEvent(light, 0, sgSceneLightingProcessEvent::sgTGELightCompleteEventType); + return; + } + + //process object and light + S32 time = Platform::getRealMilliseconds(); + // light object + LightInfo* li = mLights[light]; + mLitObjects[object]->processTGELightProcessEvent(object, mLitObjects.size(), li); + + sgTGESetProgress(light, object); + Con::printf(" Object lighting complete (%3.3f seconds)", (Platform::getRealMilliseconds()-time)/1000.f); + + + // kick off next object event + + sgNewEvent(light, (object+1), sgSceneLightingProcessEvent::sgTGELightProcessEventType); +} + +/* +* Called once per TGE light - used for calling postLight on all objects +*/ +void SceneLighting::sgTGELightCompleteEvent(U32 light) +{ + // catch bad light index and move to the next pass event + if(light >= mLights.size()) + { + sgTGESetProgress(mLights.size(), mLitObjects.size()); + Con::printf(" TGE based scene lighting complete (%3.3f seconds)", (Platform::getRealMilliseconds()-sgTimeTemp2)/1000.f); + + sgNewEvent(0, 0, sgSceneLightingProcessEvent::sgSGPassSetupEventType); + //sgNewEvent(0, 0, sgSceneLightingProcessEvent::sgLightingCompleteEventType); + return; + } + + // process post-lighting + // don't do this, SG lighting events will copy terrain light map... + /*bool islast = (light == (mLights.size() - 1)); + for(U32 o=0; o(mLitObjects[o])) + mLitObjects[o]->postLight(islast); + }*/ + + // kick off next light event + + sgNewEvent((light+1), 0, sgSceneLightingProcessEvent::sgTGELightStartEventType); +} + +void SceneLighting::sgTGESetProgress(U32 light, S32 object) +{ + // TGE is light based... + F32 val = (F32)(light * mLitObjects.size()) + object; + F32 total = (F32)(mLights.size() * mLitObjects.size()); + + if(total == 0.0f) + return; + + val = getMin(val, total); + + // two passes... + total *= 2.0f; + + gLightingProgress = val / total; +} + +//----------------------------------------------- +/* +* Called once per scenelighting - used for prepping the +* event system for SG style scenelighting +*/ +void SceneLighting::sgSGPassSetupEvent() +{ + mLitObjects.clear(); + for(ObjectProxy **proxyItr = mSceneObjects.begin(); proxyItr != mSceneObjects.end(); proxyItr++) + { + // is there an object? + if(!(*proxyItr)->getObject()) + { + AssertFatal(0, "SceneLighting:: missing sceneobject on light start"); + Con::errorf(ConsoleLogEntry::General, " SceneLighting:: missing sceneobject on light start"); + continue; + } + + // add all lights + mLitObjects.push_back(*proxyItr); + } + + sgNewEvent(0, 0, sgSceneLightingProcessEvent::sgSGObjectStartEventType); +} + +/* +* Called once per object - used for calling preLight on all SG lights +*/ +void SceneLighting::sgSGObjectStartEvent(S32 object) +{ + // catch bad light index and jump to complete event + if(object >= mLitObjects.size()) + { + sgNewEvent(0, object, sgSceneLightingProcessEvent::sgSGObjectCompleteEventType); + return; + } + + ObjectProxy *obj = mLitObjects[object]; + bool bHandled = obj->processStartObjectLightingEvent(object, mLitObjects.size()); + if (!bHandled) + { + Con::printf(" Lighting object %d of %d... %s: %s", (object+1), mLitObjects.size(), obj->getObject()->getClassName(), obj->getObject()->getName()); + } + + for(U32 i=0; imType == LightInfo::SGStaticPoint) || (lightobj->mType == LightInfo::SGStaticSpot)) + obj->preLight(lightobj); + } + + sgTimeTemp = Platform::getRealMilliseconds(); + + // kick off lighting + + + // this is slow with multiple objects... + //sgNewEvent(0, object, sgSceneLightingProcessEvent::sgSGObjectProcessEventType); + // jump right to the method... + sgSGObjectProcessEvent(0, object); +} + +/* +* Called once per object and SG light - used for calling light on an object +*/ +void SceneLighting::sgSGObjectProcessEvent(U32 light, S32 object) +{ + // catch bad light or object index + if((light >= mLights.size()) || (object >= mLitObjects.size())) + { + // this is slow with multiple objects... + //sgNewEvent(0, object, sgSceneLightingProcessEvent::sgSGObjectCompleteEventType); + // jump right to the method... + sgSGObjectCompleteEvent(object); + return; + } + + // avoid the event overhead... + // 80 lights == 0.6 seconds an interior without ANY lighting (events only)... + U32 time = Platform::getRealMilliseconds(); + ObjectProxy* objprox = mLitObjects[object]; + while((light < mLights.size()) && ((Platform::getRealMilliseconds() - time) < 500)) + { + // can we use the light? + LightInfo *lightobj = mLights[light]; + + objprox->processSGObjectProcessEvent(lightobj); + + sgSGSetProgress(light, object); + + light++; + } + + light--; + + // kick off next light event + + sgNewEvent((light+1), object, sgSceneLightingProcessEvent::sgSGObjectProcessEventType); +} + +/* +* Called once per object - used for calling postLight on all SG lights +*/ +void SceneLighting::sgSGObjectCompleteEvent(S32 object) +{ + // catch bad light index and move to the next pass event + if(object >= mLitObjects.size()) + { + sgSGSetProgress(mLights.size(), mLitObjects.size()); + + sgNewEvent(0, 0, sgSceneLightingProcessEvent::sgLightingCompleteEventType); + return; + } + + // process post-lighting + Con::printf(" Object lighting complete (%3.3f seconds)", (Platform::getRealMilliseconds()-sgTimeTemp)/1000.f); + + // in case Atlas turned off rendering... + GFX->setAllowRender(true); + + // only the last light does something + mLitObjects[object]->postLight(true); + +#ifdef DUMP_LIGHTMAPS + InteriorInstance *interiorinst = dynamic_cast(mLitObjects[object]->getObject()); + if(interiorinst) + { + Interior *detail = interiorinst->getDetailLevel(0); + for(U32 i=0; imNormalLMapIndices.size(); i++) + { + GFXTexHandle normHandle = gInteriorLMManager.duplicateBaseLightmap(detail->getLMHandle(), + interiorinst->getLMHandle(), detail->getNormalLMapIndex(i)); + GBitmap *normLightmap = normHandle->getBitmap(); + + FileStream output; + output.open(avar("lightmaps/lm_%d_%d.png", object, i), Torque::FS::File::Write); + normLightmap->writeBitmap("png",output); + } + } +#endif + + /*ObjectProxy *obj = mLitObjects[object]; + for(U32 i=0; imType == LightInfo::SGStaticPoint) || (lightobj->mType == LightInfo::SGStaticSpot)) + obj->postLight((i == (mLights.size() - 1))); + }*/ + + // kick off next light event + + + // this is slow with multiple objects... + //sgNewEvent(0, (object+1), sgSceneLightingProcessEvent::sgSGObjectStartEventType); + // jump right to the method... + sgSGObjectStartEvent((object+1)); +} + +void SceneLighting::sgSGSetProgress(U32 light, S32 object) +{ + // SG is object based... + F32 val = (F32)((object * mLights.size()) + light); + F32 total = (F32)(mLights.size() * mLitObjects.size()); + + if(total == 0.0f) + return; + + val = getMin(val, total); + + // two passes... + total *= 2.0f; + + gLightingProgress = (val / total) + 0.5f; +} + +//----------------------------------------------- + +void SceneLighting::processEvent(U32 light, S32 object) +{ + sgNewEvent(light, object, sgSceneLightingProcessEvent::sgLightingStartEventType); +} + + +//----------------------------------------------- + + +SceneLighting::SceneLighting(AvailableSLInterfaces* lightingInterfaces) +{ + mLightingInterfaces = lightingInterfaces; + mStartTime = 0; + mFileName[0] = '\0'; + mSceneManager = NULL; + + // Registering vars more than once doesn't hurt anything. + Con::addVariable("$sceneLighting::terminateLighting", TypeBool, &gTerminateLighting); + Con::addVariable("$sceneLighting::lightingProgress", TypeF32, &gLightingProgress); + + mLightingInterfaces->initInterfaces(); +} + +SceneLighting::~SceneLighting() +{ + gLighting = NULL; + gLightingProgress = 0.0f; + + ObjectProxy ** proxyItr; + for(proxyItr = mSceneObjects.begin(); proxyItr != mSceneObjects.end(); proxyItr++) + delete *proxyItr; +} + +void SceneLighting::getMLName(const char* misName, const U32 missionCRC, const U32 buffSize, char* filenameBuffer) +{ + dSprintf(filenameBuffer, buffSize, "%s_%x.ml", misName, missionCRC); +} + +bool SceneLighting::light(BitSet32 flags) +{ + if(!mSceneManager) + return(false); + + mStartTime = Platform::getRealMilliseconds(); + + // Register static lights + if (!LIGHTMGR) + return false; // This world doesn't need lighting. + + LIGHTMGR->registerGlobalLights(NULL,true); + + // Notify each system factory that we are beginning to light + for(SceneLightingInterface** sitr = mLightingInterfaces->mAvailableSystemInterfaces.begin(); sitr != mLightingInterfaces->mAvailableSystemInterfaces.end(); sitr++) + { + SceneLightingInterface* si = (*sitr); + si->processLightingBegin(); + } + + // grab all the lights + mLights.clear(); + LIGHTMGR->getAllUnsortedLights(&mLights); + LIGHTMGR->unregisterAllLights(); + + if(!mLights.size()) + return(false); + + // get all the objects and create proxy's for them + SimpleQueryList objects; + gClientContainer.findObjects(mLightingInterfaces->mAvailableObjectTypes, &SimpleQueryList::insertionCallback, &objects); + + for(SceneObject ** itr = objects.mList.begin(); itr != objects.mList.end(); itr++) + { + ObjectProxy * proxy = NULL; + SceneObject* obj = *itr; + if (!obj) + continue; + + // Create the right chunk for the system + for(SceneLightingInterface** sitr = mLightingInterfaces->mAvailableSystemInterfaces.begin(); + sitr != mLightingInterfaces->mAvailableSystemInterfaces.end() && proxy == NULL; sitr++) + { + SceneLightingInterface* si = (*sitr); + proxy = si->createObjectProxy(obj, &mSceneObjects); + } + + if (proxy) + { + if(!proxy->calcValidation()) + { + Con::errorf(ConsoleLogEntry::General, "Failed to calculate validation info for object. Skipped."); + delete proxy; + continue; + } + + if(!proxy->loadResources()) + { + Con::errorf(ConsoleLogEntry::General, "Failed to load resources for object. Skipped."); + delete proxy; + continue; + } + + mSceneObjects.push_back(proxy); + } + } + + if(!mSceneObjects.size()) + return(false); + + // grab the missions crc + U32 missionCRC = calcMissionCRC(); + + // remove the '.mis' extension from the mission name + char misName[256]; + dSprintf(misName, sizeof(misName), "%s", Con::getVariable("$Client::MissionFile")); + char * dot = dStrstr((const char*)misName, ".mis"); + if(dot) + *dot = '\0'; + + // get the mission name + getMLName(misName, missionCRC, 1023, mFileName); + + // check for some persisted data, check if being forced.. + if(!flags.test(ForceAlways|ForceWritable)) + { + if(loadPersistInfo(mFileName)) + { + Con::printf(" Successfully loaded mission lighting file: '%s'", mFileName); + + // touch this file... + if(!Platform::FS::Touch(mFileName)) + Con::warnf(" Failed to touch file '%s'. File may be read only.", mFileName); + + return(false); + } + + // texture manager must have lighting complete now + if(flags.test(LoadOnly)) + { + Con::errorf(ConsoleLogEntry::General, "Failed to load mission lighting!"); + return(false); + } + } + + // don't light if file is read-only? + if(!flags.test(ForceAlways)) + { + FileStream stream; + + stream.open( mFileName, Torque::FS::File::Write ); + + if(stream.getStatus() != Stream::Ok) + { + Con::errorf(ConsoleLogEntry::General, "SceneLighting::Light: Failed to light mission. File '%s' cannot be written to.", mFileName); + return(false); + } + } + + // initialize the objects for lighting + for(ObjectProxy ** proxyItr = mSceneObjects.begin(); proxyItr != mSceneObjects.end(); proxyItr++) + (*proxyItr)->init(); + + // get things started + Sim::postEvent(this, new sgSceneLightingProcessEvent(0, -1, + sgSceneLightingProcessEvent::sgLightingStartEventType), Sim::getTargetTime() + 1); + + return(true); +} + +void SceneLighting::completed(bool success) +{ + // process the cached lighting files + processCache(); + + // Notify each system factory that we are have lit! + for(SceneLightingInterface** sitr = mLightingInterfaces->mAvailableSystemInterfaces.begin(); sitr != mLightingInterfaces->mAvailableSystemInterfaces.end(); sitr++) + { + SceneLightingInterface* si = (*sitr); + si->processLightingCompleted(success); + } + + if(gCompleteCallback && gCompleteCallback[0]) + Con::executef(gCompleteCallback); + + dFree(gCompleteCallback); + gCompleteCallback = NULL; +} + +//------------------------------------------------------------------------------ +// Static access method: there can be only one SceneLighting object +bool SceneLighting::lightScene(const char * callback, BitSet32 flags) +{ + if(gLighting) + { + Con::errorf(ConsoleLogEntry::General, "Lighting is already in progress!"); + return false; + } + + // register the object + if(!registerObject()) + { + AssertFatal(0, "SceneLighting:: Unable to register SceneLighting object!"); + Con::errorf(ConsoleLogEntry::General, "SceneLighting:: Unable to register SceneLighting object!"); + delete this; + return(false); + } + + // could have interior resources but no instances (hey, got this far didnt we...) + GameConnection * con = dynamic_cast(NetConnection::getConnectionToServer()); + if(!con) + { + Con::errorf(ConsoleLogEntry::General, "SceneLighting:: no GameConnection"); + return(false); + } + con->addObject(this); + + // set the globals + gLighting = this; + gTerminateLighting = false; + gLightingProgress = 0.0f; + if (gCompleteCallback) + dFree(gCompleteCallback); + gCompleteCallback = dStrdup(callback); + gConnectionMissionCRC = con->getMissionCRC(); + + // assumes we are in the world that needs lighting... + mSceneManager = gClientSceneGraph; + + if(!light(flags)) + { + completed(true); + deleteObject(); + return(false); + } + return(true); +} + +bool SceneLighting::isLighting() +{ + return(bool(gLighting)); +} + +/// adds TSStatic objects as shadow casters. +void SceneLighting::addStatic(ShadowVolumeBSP *shadowVolume, + SceneObject *sceneobject, LightInfo *light, S32 level) +{ + if (!sceneobject) + return; + + if(light->getType() != LightInfo::Vector) + return; + + ConcretePolyList polylist; + const Box3F box; + const SphereF sphere; + sceneobject->buildPolyList(PLC_Collision, &polylist, box, sphere); + + // retrieve the poly list (uses the collision mesh)... + //sobj->sgAdvancedStaticOptionsData.sgBuildPolyList(sobj, &polylist); + + S32 i, count, vertind[3]; + ConcretePolyList::Poly *poly; + + count = polylist.mPolyList.size(); + + // add the polys to the shadow volume... + for(i=0; ivertexCount == 3), "Hmmm... vert count is greater than 3."); + + vertind[0] = polylist.mIndexList[poly->vertexStart]; + vertind[1] = polylist.mIndexList[poly->vertexStart + 1]; + vertind[2] = polylist.mIndexList[poly->vertexStart + 2]; + + if(mDot(PlaneF(polylist.mVertexList[vertind[0]], polylist.mVertexList[vertind[1]], + polylist.mVertexList[vertind[2]]), light->getDirection()) < gParellelVectorThresh) + { + ShadowVolumeBSP::SVPoly *svpoly = shadowVolume->createPoly(); + svpoly->mWindingCount = 3; + + svpoly->mWinding[0].set(polylist.mVertexList[vertind[0]]); + svpoly->mWinding[1].set(polylist.mVertexList[vertind[1]]); + svpoly->mWinding[2].set(polylist.mVertexList[vertind[2]]); + svpoly->mPlane = PlaneF(svpoly->mWinding[0], svpoly->mWinding[1], svpoly->mWinding[2]); + svpoly->mPlane.neg(); + + shadowVolume->buildPolyVolume(svpoly, light); + shadowVolume->insertPoly(svpoly); + } + } +} + +//------------------------------------------------------------------------------ +bool SceneLighting::verifyMissionInfo(PersistInfo::PersistChunk * chunk) +{ + PersistInfo::MissionChunk * info = dynamic_cast(chunk); + if(!info) + return(false); + + PersistInfo::MissionChunk curInfo; + if(!getMissionInfo(&curInfo)) + return(false); + + return(curInfo.mChunkCRC == info->mChunkCRC); +} + +bool SceneLighting::getMissionInfo(PersistInfo::PersistChunk * chunk) +{ + PersistInfo::MissionChunk * info = dynamic_cast(chunk); + if(!info) + return(false); + + info->mChunkCRC = gConnectionMissionCRC ^ PersistInfo::smFileVersion; + return(true); +} + +//------------------------------------------------------------------------------ +bool SceneLighting::loadPersistInfo(const char * fileName) +{ + FileStream stream; + + stream.open( fileName, Torque::FS::File::Read ); + + if(stream.getStatus() != Stream::Ok) + return false; + + PersistInfo persistInfo; + bool success = persistInfo.read(stream); + stream.close(); + if(!success) + return(false); + + // verify the mission chunk + if(!verifyMissionInfo(persistInfo.mChunks[0])) + return(false); + + // Create the right chunk for the system + for(SceneLightingInterface** sitr = mLightingInterfaces->mAvailableSystemInterfaces.begin(); sitr != mLightingInterfaces->mAvailableSystemInterfaces.end(); sitr++) + { + SceneLightingInterface* si = (*sitr); + if (!si->postProcessLoad(&persistInfo, &mSceneObjects)) + { + return false; + } + } + + if(mSceneObjects.size() != (persistInfo.mChunks.size() - 1)) + return(false); + + Vector chunks; + + // ensure that the scene objects are in the same order as the chunks + // - different instances will depend on this + U32 i; + for(i = 0; i < mSceneObjects.size(); i++) + { + // 0th chunk is the mission chunk + U32 chunkIdx = i+1; + if(chunkIdx >= persistInfo.mChunks.size()) + return(false); + + if(!mSceneObjects[i]->isValidChunk(persistInfo.mChunks[chunkIdx])) + return(false); + chunks.push_back(persistInfo.mChunks[chunkIdx]); + } + + // get the objects to load in the persisted chunks + for(i = 0; i < mSceneObjects.size(); i++) + if(!mSceneObjects[i]->setPersistInfo(chunks[i])) + return(false); + + return(true); +} + +bool SceneLighting::savePersistInfo(const char * fileName) +{ + // open the file + FileStream file; + file.open( fileName, Torque::FS::File::Write ); + + if(file.getStatus() != Stream::Ok) + return false; + + PersistInfo persistInfo; + + // add in the mission chunk + persistInfo.mChunks.push_back(new PersistInfo::MissionChunk); + + // get the mission info, will return false when there are 0 lights + if(!getMissionInfo(persistInfo.mChunks[0])) + return(false); + + // get all the persist chunks + bool bChunkFound; + for(U32 i = 0; i < mSceneObjects.size(); i++) + { + bChunkFound = false; + // Create the right chunk for the system + for(SceneLightingInterface** sitr = mLightingInterfaces->mAvailableSystemInterfaces.begin(); sitr != mLightingInterfaces->mAvailableSystemInterfaces.end() && !bChunkFound; sitr++) + { + SceneLightingInterface* si = (*sitr); + PersistInfo::PersistChunk* chunk; + if (si->createPersistChunkFromProxy(mSceneObjects[i], &chunk)) + { + if (chunk) + { + persistInfo.mChunks.push_back(chunk); + bChunkFound = true; + } + } + } + + // Make sure the chunk worked. + if (!mSceneObjects[i]->getPersistInfo(persistInfo.mChunks.last())) + return false; + } + + if(!persistInfo.write(file)) + return(false); + + file.close(); + + return(true); +} + +struct CacheEntry { + Torque::FS::FileNodeRef mFileObject; + const char *mFileName; + + CacheEntry() { + mFileName = 0; + }; +}; + +// object list sort methods: want list in reverse +static int QSORT_CALLBACK minSizeSort(const void * p1, const void * p2) +{ + const CacheEntry * entry1 = (const CacheEntry *)p1; + const CacheEntry * entry2 = (const CacheEntry *)p2; + + return(entry2->mFileObject->getSize() - entry1->mFileObject->getSize()); +} + +static int QSORT_CALLBACK maxSizeSort(const void * p1, const void * p2) +{ + const CacheEntry * entry1 = (const CacheEntry *)p1; + const CacheEntry * entry2 = (const CacheEntry *)p2; + + return(entry1->mFileObject->getSize() - entry2->mFileObject->getSize()); +} + +static int QSORT_CALLBACK lastCreatedSort(const void * p1, const void * p2) +{ + const CacheEntry * entry1 = (const CacheEntry *)p1; + const CacheEntry * entry2 = (const CacheEntry *)p2; + + FileTime create[2]; + FileTime modify; + + bool ret[2]; + + ret[0] = Platform::getFileTimes(entry1->mFileName, &create[0], &modify); + ret[1] = Platform::getFileTimes(entry2->mFileName, &create[1], &modify); + + // check return values + if(!ret[0] && !ret[1]) + return(0); + if(!ret[0]) + return(1); + if(!ret[1]) + return(-1); + + return(Platform::compareFileTimes(create[1], create[0])); +} + +static int QSORT_CALLBACK lastModifiedSort(const void * p1, const void * p2) +{ + const CacheEntry * entry1 = (const CacheEntry *)p1; + const CacheEntry * entry2 = (const CacheEntry *)p2; + + FileTime create; + FileTime modify[2]; + + bool ret[2]; + + ret[0] = Platform::getFileTimes(entry1->mFileName, &create, &modify[0]); + ret[1] = Platform::getFileTimes(entry2->mFileName, &create, &modify[1]); + + // check return values + if(!ret[0] && !ret[1]) + return(0); + if(!ret[0]) + return(1); + if(!ret[1]) + return(-1); + + return(Platform::compareFileTimes(modify[1], modify[0])); +} + +void SceneLighting::processCache() +{ + // get size in kb + S32 quota = Con::getIntVariable("$sceneLighting::cacheSize", -1); + + Vector files; + + Vector fileNames; + Torque::FS::FindByPattern(Torque::Path(Platform::getMainDotCsDir()), "*.ml", true, fileNames); + + S32 curCacheSize = 0; + + for(S32 i = 0;i < fileNames.size();++i) + { + if(! Torque::FS::IsFile(fileNames[i])) + continue; + + Torque::FS::FileNodeRef fileNode = Torque::FS::GetFileNode(fileNames[i]); + if(fileNode == NULL) + continue; + + if(dStrstr(fileNames[i], mFileName) == 0) + { + // Don't allow the current file to be removed + CacheEntry entry; + entry.mFileObject = fileNode; + entry.mFileName = StringTable->insert(fileNames[i]); + files.push_back(entry); + } + else + curCacheSize += fileNode->getSize(); + } + + // remove old files + for(S32 i = files.size() - 1; i >= 0; i--) + { + FileStream *stream; + if((stream = FileStream::createAndOpen( files[i].mFileObject->getName(), Torque::FS::File::Read )) == NULL) + continue; + + // read in the version + U32 version; + bool ok = (stream->read(&version) && (version == PersistInfo::smFileVersion)); + delete stream; + + // ok? + if(ok) + continue; + + // no sneaky names + if(!dStrstr(files[i].mFileName, "..")) + { + Con::warnf("Removing old lighting file '%s'.", files[i].mFileName); + dFileDelete(files[i].mFileName); + } + + files.pop_back(); + } + + // no size restriction? + if(quota == -1 || !files.size()) + return; + + for(U32 i = 0; i < files.size(); i++) + curCacheSize += files[i].mFileObject->getSize(); + + // need to remove? + if(quota > (curCacheSize >> 10)) + return; + + // sort the entries by the correct method + const char * purgeMethod = Con::getVariable("$sceneLighting::purgeMethod"); + if(!purgeMethod) + purgeMethod = ""; + + // determine the method (default to least recently used) + if(!dStricmp(purgeMethod, "minSize")) + dQsort(files.address(), files.size(), sizeof(CacheEntry), minSizeSort); + else if(!dStricmp(purgeMethod, "maxSize")) + dQsort(files.address(), files.size(), sizeof(CacheEntry), maxSizeSort); + else if(!dStricmp(purgeMethod, "lastCreated")) + dQsort(files.address(), files.size(), sizeof(CacheEntry), lastCreatedSort); + else + dQsort(files.address(), files.size(), sizeof(CacheEntry), lastModifiedSort); + + // go through and remove the best candidate first (sorted reverse) + while(((curCacheSize >> 10) > quota) && files.size()) + { + curCacheSize -= files.last().mFileObject->getSize(); + + // no sneaky names + if(!dStrstr(files.last().mFileName, "..")) + { + Con::warnf("Removing lighting file '%s'.", files.last().mFileName); + dFileDelete(files.last().mFileName); + } + + files.pop_back(); + } +} + +static S32 QSORT_CALLBACK compareS32(const void * a, const void * b) +{ + return(*((S32 *)a) - *((S32 *)b)); +} + +U32 SceneLighting::calcMissionCRC() +{ + // all the objects + mission chunk + Vector crc; + + // grab the object crcs + for(U32 i = 0; i < mSceneObjects.size(); i++) + crc.push_back( mSceneObjects[i]->mChunkCRC ); + + // grab the missions crc + PersistInfo::MissionChunk curInfo; + getMissionInfo(&curInfo); + crc.push_back(curInfo.mChunkCRC); + + // sort them (order may not have been preserved) + dQsort(crc.address(), crc.size(), sizeof(U32), compareS32); + +#ifdef TORQUE_BIG_ENDIAN + // calculateCRC operates on 8-bit chunks of memory. The memory is a vector + // of U32's, and so the result will be different on big/little endian hardware. + // To fix this, swap endians on the CRC's in the vector. This must be done + // _after_ the qsort. + for( int i = 0; i < crc.size(); i++ ) + crc[i] = endianSwap( crc[i] ); +#endif + + return(CRC::calculateCRC(crc.address(), sizeof(U32) * crc.size(), 0xffffffff)); +} + +bool SceneLighting::ObjectProxy::calcValidation() +{ + mChunkCRC = getResourceCRC(); + if(!mChunkCRC) + return(false); + + return(true); +} + +bool SceneLighting::ObjectProxy::isValidChunk(PersistInfo::PersistChunk * chunk) +{ + return(chunk->mChunkCRC == mChunkCRC); +} + +bool SceneLighting::ObjectProxy::getPersistInfo(PersistInfo::PersistChunk * chunk) +{ + chunk->mChunkCRC = mChunkCRC; + return(true); +} + +bool SceneLighting::ObjectProxy::setPersistInfo(PersistInfo::PersistChunk * chunk) +{ + mChunkCRC = chunk->mChunkCRC; + return(true); +} \ No newline at end of file diff --git a/Engine/source/lighting/common/sceneLighting.h b/Engine/source/lighting/common/sceneLighting.h new file mode 100644 index 000000000..31bed91b5 --- /dev/null +++ b/Engine/source/lighting/common/sceneLighting.h @@ -0,0 +1,254 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SCENELIGHTING_H_ +#define _SCENELIGHTING_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _SGSCENEPERSIST_H_ +#include "lighting/common/scenePersist.h" +#endif +#ifndef _LIGHTINFO_H_ +#include "lighting/lightInfo.h" +#endif + +class ShadowVolumeBSP; +class LightInfo; +class AvailableSLInterfaces; + +class SceneLighting : public SimObject +{ + typedef SimObject Parent; +protected: + AvailableSLInterfaces* mLightingInterfaces; + virtual void getMLName(const char* misName, const U32 missionCRC, const U32 buffSize, char* filenameBuffer); +public: + S32 sgTimeTemp; + S32 sgTimeTemp2; + + virtual void sgNewEvent(U32 light, S32 object, U32 event); + + virtual void sgLightingStartEvent(); + virtual void sgLightingCompleteEvent(); + + virtual void sgTGEPassSetupEvent(); + virtual void sgTGELightStartEvent(U32 light); + virtual void sgTGELightProcessEvent(U32 light, S32 object); + virtual void sgTGELightCompleteEvent(U32 light); + virtual void sgTGESetProgress(U32 light, S32 object); + + virtual void sgSGPassSetupEvent(); + virtual void sgSGObjectStartEvent(S32 object); + virtual void sgSGObjectProcessEvent(U32 light, S32 object); + virtual void sgSGObjectCompleteEvent(S32 object); + virtual void sgSGSetProgress(U32 light, S32 object); + + // 'sg' prefix omitted to conform with existing 'addInterior' method... + void addStatic(ShadowVolumeBSP *shadowVolume, SceneObject *sceneobject, LightInfo *light, S32 level); + + // persist objects moved to 'sgScenePersist.h' for clarity... + // everything below this line should be original code... + + U32 calcMissionCRC(); + + bool verifyMissionInfo(PersistInfo::PersistChunk *); + bool getMissionInfo(PersistInfo::PersistChunk *); + + bool loadPersistInfo(const char *); + bool savePersistInfo(const char *); + + class ObjectProxy; + + enum { + SHADOW_DETAIL = -1 + }; + + //------------------------------------------------------------------------------ + /// Create a proxy for each object to store data. + class ObjectProxy + { + public: + SimObjectPtr mObj; + U32 mChunkCRC; + + ObjectProxy(SceneObject * obj) : mObj(obj){mChunkCRC = 0;} + virtual ~ObjectProxy(){} + SceneObject * operator->() {return(mObj);} + SceneObject * getObject() {return(mObj);} + + /// @name Lighting Interface + /// @{ + virtual bool loadResources() {return(true);} + virtual void init() {} + virtual bool tgePreLight(LightInfo* light) { return preLight(light); } + virtual bool preLight(LightInfo *) {return(false);} + virtual void light(LightInfo *) {} + virtual void postLight(bool lastLight) {} + /// @} + + /// @name Lighting events + /// @{ + // Called when the lighting process begins + virtual void processLightingStart() {} + // Called when a TGELight event is started, return true if status has been reported to console + virtual void processTGELightProcessEvent(U32 curr, U32 max, LightInfo*) { Con::printf(" Lighting object %d of %d...", (curr+1), max); } + // Called for lighting kit lights + virtual bool processStartObjectLightingEvent(U32 current, U32 max) { Con::printf(" Lighting object %d of %d... %s: %s", (current+1), max, mObj->getClassName(), mObj->getName()); return true; } + // Called once per object and SG light - used for calling light on an object + virtual void processSGObjectProcessEvent(LightInfo* currLight) { light(currLight); }; + /// @} + + /// @name Persistence + /// + /// We cache lighting information to cut down on load times. + /// + /// There are flags such as ForceAlways and LoadOnly which allow you + /// to control this behaviour. + /// @{ + bool calcValidation(); + bool isValidChunk(PersistInfo::PersistChunk *); + + virtual U32 getResourceCRC() = 0; + virtual bool setPersistInfo(PersistInfo::PersistChunk *); + virtual bool getPersistInfo(PersistInfo::PersistChunk *); + /// @} + + // Called to figure out if this object should be added to the shadow volume + virtual bool supportsShadowVolume() { return false; } + // Called to retrieve the clip planes of the object. Currently used for terrain lighting, but could be used to speed up other + // lighting calculations. + virtual void getClipPlanes(Vector& planes) { } + // Called to add the object to the shadow volume + virtual void addToShadowVolume(ShadowVolumeBSP * shadowVolume, LightInfo * light, S32 level) { } ; + }; + + typedef Vector ObjectProxyList; + + ObjectProxyList mSceneObjects; + ObjectProxyList mLitObjects; + + LightInfoList mLights; + + SceneLighting(AvailableSLInterfaces* lightingInterfaces); + ~SceneLighting(); + + enum Flags { + ForceAlways = BIT(0), ///< Regenerate the scene lighting no matter what. + ForceWritable = BIT(1), ///< Regenerate the scene lighting only if we can write to the lighting cache files. + LoadOnly = BIT(2), ///< Just load cached lighting data. + }; + bool lightScene(const char *, BitSet32 flags = 0); + bool isLighting(); + + S32 mStartTime; + char mFileName[1024]; + SceneManager * mSceneManager; + + bool light(BitSet32); + void completed(bool success); + void processEvent(U32 light, S32 object); + void processCache(); +}; + +class sgSceneLightingProcessEvent : public SimEvent +{ +private: + U32 sgLightIndex; + S32 sgObjectIndex; + U32 sgEvent; + +public: + enum sgEventTypes + { + sgLightingStartEventType, + sgLightingCompleteEventType, + + sgSGPassSetupEventType, + sgSGObjectStartEventType, + sgSGObjectCompleteEventType, + sgSGObjectProcessEventType, + + sgTGEPassSetupEventType, + sgTGELightStartEventType, + sgTGELightCompleteEventType, + sgTGELightProcessEventType + }; + + sgSceneLightingProcessEvent(U32 lightIndex, S32 objectIndex, U32 event) + { + sgLightIndex = lightIndex; + sgObjectIndex = objectIndex; + sgEvent = event; + } + void process(SimObject * object) + { + AssertFatal(object, "SceneLightingProcessEvent:: null event object!"); + if(!object) + return; + + SceneLighting *sl = static_cast(object); + switch(sgEvent) + { + case sgLightingStartEventType: + sl->sgLightingStartEvent(); + break; + case sgLightingCompleteEventType: + sl->sgLightingCompleteEvent(); + break; + + case sgTGEPassSetupEventType: + sl->sgTGEPassSetupEvent(); + break; + case sgTGELightStartEventType: + sl->sgTGELightStartEvent(sgLightIndex); + break; + case sgTGELightProcessEventType: + sl->sgTGELightProcessEvent(sgLightIndex, sgObjectIndex); + break; + case sgTGELightCompleteEventType: + sl->sgTGELightCompleteEvent(sgLightIndex); + break; + + case sgSGPassSetupEventType: + sl->sgSGPassSetupEvent(); + break; + case sgSGObjectStartEventType: + sl->sgSGObjectStartEvent(sgObjectIndex); + break; + case sgSGObjectProcessEventType: + sl->sgSGObjectProcessEvent(sgLightIndex, sgObjectIndex); + break; + case sgSGObjectCompleteEventType: + sl->sgSGObjectCompleteEvent(sgObjectIndex); + break; + + default: + return; + }; + }; +}; + +extern SceneLighting *gLighting; + +#endif//_SGSCENELIGHTING_H_ diff --git a/Engine/source/lighting/common/sceneLightingGlobals.h b/Engine/source/lighting/common/sceneLightingGlobals.h new file mode 100644 index 000000000..b9f05f521 --- /dev/null +++ b/Engine/source/lighting/common/sceneLightingGlobals.h @@ -0,0 +1,66 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +static const Point3F BoxNormals[] = +{ + Point3F( 1, 0, 0), + Point3F(-1, 0, 0), + Point3F( 0, 1, 0), + Point3F( 0,-1, 0), + Point3F( 0, 0, 1), + Point3F( 0, 0,-1) +}; + +static U32 BoxVerts[][4] = { + {7,6,4,5}, // +x + {0,2,3,1}, // -x + {7,3,2,6}, // +y + {0,1,5,4}, // -y + {7,5,1,3}, // +z + {0,4,6,2} // -z +}; + +static U32 BoxSharedEdgeMask[][6] = { + {0, 0, 1, 4, 8, 2}, + {0, 0, 2, 8, 4, 1}, + {8, 2, 0, 0, 1, 4}, + {4, 1, 0, 0, 2, 8}, + {1, 4, 8, 2, 0, 0}, + {2, 8, 4, 1, 0, 0} +}; + +static Point3F BoxPnts[] = { + Point3F(0,0,0), + Point3F(0,0,1), + Point3F(0,1,0), + Point3F(0,1,1), + Point3F(1,0,0), + Point3F(1,0,1), + Point3F(1,1,0), + Point3F(1,1,1) +}; + +extern SceneLighting *gLighting; +extern F32 gParellelVectorThresh; +extern F32 gPlaneNormThresh; +extern F32 gPlaneDistThresh; + diff --git a/Engine/source/lighting/common/scenePersist.cpp b/Engine/source/lighting/common/scenePersist.cpp new file mode 100644 index 000000000..1f4d164bd --- /dev/null +++ b/Engine/source/lighting/common/scenePersist.cpp @@ -0,0 +1,144 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/common/scenePersist.h" + +#include "lighting/lightingInterfaces.h" +#include "scene/sceneManager.h" +#include "lighting/lightManager.h" + + +U32 PersistInfo::smFileVersion = 0x11; + +PersistInfo::~PersistInfo() +{ + for(U32 i = 0; i < mChunks.size(); i++) + delete mChunks[i]; +} + +//------------------------------------------------------------------------------ +bool PersistInfo::read(Stream & stream) +{ + U32 version; + if(!stream.read(&version) || version != smFileVersion) + return(false); + + U32 numChunks; + if(!stream.read(&numChunks)) + return(false); + + if(numChunks == 0) + return(false); + + // read in all the chunks + for(U32 i = 0; i < numChunks; i++) + { + U32 chunkType; + if(!stream.read(&chunkType)) + return(false); + + // MissionChunk must be first chunk + if(i == 0 && chunkType != PersistChunk::MissionChunkType) + return(false); + if(i != 0 && chunkType == PersistChunk::MissionChunkType) + return(false); + + // Create the right chunk for the system + bool bChunkFound = false; + SceneLightingInterfaces sli = LIGHTMGR->getSceneLightingInterface()->mAvailableSystemInterfaces; + for(SceneLightingInterface** itr = sli.begin(); itr != sli.end() && !bChunkFound; itr++) + { + PersistInfo::PersistChunk* pc = (*itr)->createPersistChunk(chunkType); + if (pc != NULL) + { + mChunks.push_back(pc); + bChunkFound = true; + } + } + + if (!bChunkFound) + { + // create the chunk + switch(chunkType) + { + case PersistChunk::MissionChunkType: + mChunks.push_back(new PersistInfo::MissionChunk); + break; + + default: + return(false); + break; + } + } + + // load the chunk info + if(!mChunks[i]->read(stream)) + return(false); + } + + return(true); +} + +bool PersistInfo::write(Stream & stream) +{ + if(!stream.write(smFileVersion)) + return(false); + + if(!stream.write((U32)mChunks.size())) + return(false); + + for(U32 i = 0; i < mChunks.size(); i++) + { + if(!stream.write(mChunks[i]->mChunkType)) + return(false); + if(!mChunks[i]->write(stream)) + return(false); + } + + return(true); +} + +//------------------------------------------------------------------------------ +// Class SceneLighting::PersistInfo::PersistChunk +//------------------------------------------------------------------------------ +bool PersistInfo::PersistChunk::read(Stream & stream) +{ + if(!stream.read(&mChunkCRC)) + return(false); + return(true); +} + +bool PersistInfo::PersistChunk::write(Stream & stream) +{ + if(!stream.write(mChunkCRC)) + return(false); + return(true); +} + +//------------------------------------------------------------------------------ +// Class SceneLighting::PersistInfo::MissionChunk +//------------------------------------------------------------------------------ +PersistInfo::MissionChunk::MissionChunk() +{ + mChunkType = PersistChunk::MissionChunkType; +} \ No newline at end of file diff --git a/Engine/source/lighting/common/scenePersist.h b/Engine/source/lighting/common/scenePersist.h new file mode 100644 index 000000000..d02513b7f --- /dev/null +++ b/Engine/source/lighting/common/scenePersist.h @@ -0,0 +1,68 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SGSCENEPERSIST_H_ +#define _SGSCENEPERSIST_H_ + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +class Stream; + +struct PersistInfo +{ + struct PersistChunk + { + enum { + MissionChunkType = 0, + InteriorChunkType, + TerrainChunkType, + AtlasLightMapChunkType + }; + + U32 mChunkType; + U32 mChunkCRC; + + virtual ~PersistChunk() {} + + virtual bool read(Stream &); + virtual bool write(Stream &); + }; + + struct MissionChunk : public PersistChunk + { + typedef PersistChunk Parent; + MissionChunk(); + }; + + ~PersistInfo(); + + Vector mChunks; + static U32 smFileVersion; + + bool read(Stream &); + bool write(Stream &); +}; + + +#endif//_SGSCENEPERSIST_H_ diff --git a/Engine/source/lighting/common/shadowBase.h b/Engine/source/lighting/common/shadowBase.h new file mode 100644 index 000000000..3656435e8 --- /dev/null +++ b/Engine/source/lighting/common/shadowBase.h @@ -0,0 +1,40 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _LIGHTINGSYSTEM_SHADOWBASE_H_ +#define _LIGHTINGSYSTEM_SHADOWBASE_H_ + +class TSRenderState; + +class ShadowBase +{ +public: + virtual ~ShadowBase() {} + virtual bool shouldRender( const SceneRenderState *state ) = 0; + + virtual void update( const SceneRenderState *state ) = 0; + virtual void render(F32 camDist, const TSRenderState &rdata ) = 0; + virtual U32 getLastRenderTime() const = 0; + virtual const F32 getScore() const = 0; +}; + +#endif \ No newline at end of file diff --git a/Engine/source/lighting/common/shadowVolumeBSP.cpp b/Engine/source/lighting/common/shadowVolumeBSP.cpp new file mode 100644 index 000000000..190b8cdf1 --- /dev/null +++ b/Engine/source/lighting/common/shadowVolumeBSP.cpp @@ -0,0 +1,731 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/common/shadowVolumeBSP.h" + +#include "lighting/lightInfo.h" +#include "math/mPlane.h" + + +ShadowVolumeBSP::ShadowVolumeBSP() : + mSVRoot(0), + mNodeStore(0), + mPolyStore(0), + mFirstInteriorNode(0) +{ +} + +ShadowVolumeBSP::~ShadowVolumeBSP() +{ + for(U32 i = 0; i < mSurfaces.size(); i++) + delete mSurfaces[i]; +} + +void ShadowVolumeBSP::insertShadowVolume(SVNode ** root, U32 volume) +{ + SVNode * traverse = mShadowVolumes[volume]; + + // insert 'em + while(traverse) + { + // copy it + *root = createNode(); + (*root)->mPlaneIndex = traverse->mPlaneIndex; + (*root)->mSurfaceInfo = traverse->mSurfaceInfo; + (*root)->mShadowVolume = traverse->mShadowVolume; + + // do the next + root = &(*root)->mFront; + traverse = traverse->mFront; + } +} + +ShadowVolumeBSP::SVNode::Side ShadowVolumeBSP::whichSide(SVPoly * poly, const PlaneF & plane) const +{ + bool front = false; + bool back = false; + + for(U32 i = 0; i < poly->mWindingCount; i++) + { + switch(plane.whichSide(poly->mWinding[i])) + { + case PlaneF::Front: + if(back) + return(SVNode::Split); + front = true; + break; + + case PlaneF::Back: + if(front) + return(SVNode::Split); + back = true; + break; + + default: + break; + } + } + + AssertFatal(!(front && back), "ShadowVolumeBSP::whichSide - failed to classify poly"); + + if(!front && !back) + return(SVNode::On); + + return(front ? SVNode::Front : SVNode::Back); +} + +void ShadowVolumeBSP::splitPoly(SVPoly * poly, const PlaneF & plane, SVPoly ** front, SVPoly ** back) +{ + PlaneF::Side sides[SVPoly::MaxWinding]; + + U32 i; + for(i = 0; i < poly->mWindingCount; i++) + sides[i] = plane.whichSide(poly->mWinding[i]); + + // create the polys + (*front) = createPoly(); + (*back) = createPoly(); + + // copy the info + (*front)->mWindingCount = (*back)->mWindingCount = 0; + (*front)->mPlane = (*back)->mPlane = poly->mPlane; + (*front)->mTarget = (*back)->mTarget = poly->mTarget; + (*front)->mSurfaceInfo = (*back)->mSurfaceInfo = poly->mSurfaceInfo; + (*front)->mShadowVolume = (*back)->mShadowVolume = poly->mShadowVolume; + + // + for(i = 0; i < poly->mWindingCount; i++) + { + U32 j = (i+1) % poly->mWindingCount; + + if(sides[i] == PlaneF::On) + { + (*front)->mWinding[(*front)->mWindingCount++] = poly->mWinding[i]; + (*back)->mWinding[(*back)->mWindingCount++] = poly->mWinding[i]; + } + else if(sides[i] == PlaneF::Front) + { + (*front)->mWinding[(*front)->mWindingCount++] = poly->mWinding[i]; + + if(sides[j] == PlaneF::Back) + { + const Point3F & a = poly->mWinding[i]; + const Point3F & b = poly->mWinding[j]; + + F32 t = plane.intersect(a, b); + AssertFatal(t >=0 && t <= 1, "ShadowVolumeBSP::splitPoly - bad plane intersection"); + + Point3F pos; + pos.interpolate(a, b, t); + + // + (*front)->mWinding[(*front)->mWindingCount++] = + (*back)->mWinding[(*back)->mWindingCount++] = pos; + } + } + else if(sides[i] == PlaneF::Back) + { + (*back)->mWinding[(*back)->mWindingCount++] = poly->mWinding[i]; + + if(sides[j] == PlaneF::Front) + { + const Point3F & a = poly->mWinding[i]; + const Point3F & b = poly->mWinding[j]; + + F32 t = plane.intersect(a, b); + AssertFatal(t >=0 && t <= 1, "ShadowVolumeBSP::splitPoly - bad plane intersection"); + + Point3F pos; + pos.interpolate(a, b, t); + + (*front)->mWinding[(*front)->mWindingCount++] = + (*back)->mWinding[(*back)->mWindingCount++] = pos; + } + } + } + + AssertFatal((*front)->mWindingCount && (*back)->mWindingCount, "ShadowVolume::split - invalid split"); +} + +void ShadowVolumeBSP::addUniqueVolume(SurfaceInfo * surfaceInfo, U32 volume) +{ + if(!surfaceInfo) + return; + + for(U32 i = 0; i < surfaceInfo->mShadowed.size(); i++) + if(surfaceInfo->mShadowed[i] == volume) + return; + + // add it + surfaceInfo->mShadowed.push_back(volume); +} + +void ShadowVolumeBSP::insertPoly(SVNode ** root, SVPoly * poly) +{ + if(!(*root)) + { + insertShadowVolume(root, poly->mShadowVolume); + + if(poly->mSurfaceInfo && !mFirstInteriorNode) + mFirstInteriorNode = mShadowVolumes[poly->mShadowVolume]; + + if(poly->mTarget) + addUniqueVolume(poly->mTarget->mSurfaceInfo, poly->mShadowVolume); + + recyclePoly(poly); + return; + } + + const PlaneF & plane = getPlane((*root)->mPlaneIndex); + + // + switch(whichSide(poly, plane)) + { + case SVNode::On: + case SVNode::Front: + insertPolyFront(root, poly); + break; + + case SVNode::Back: + insertPolyBack(root, poly); + break; + + case SVNode::Split: + { + SVPoly * front; + SVPoly * back; + + splitPoly(poly, plane, &front, &back); + recyclePoly(poly); + + insertPolyFront(root, front); + insertPolyBack(root, back); + + break; + } + } +} + +void ShadowVolumeBSP::insertPolyFront(SVNode ** root, SVPoly * poly) +{ + // POLY type node? + if(!(*root)->mFront) + { + if(poly->mSurfaceInfo && !mFirstInteriorNode) + mFirstInteriorNode = mShadowVolumes[poly->mShadowVolume]; + addUniqueVolume(poly->mSurfaceInfo, (*root)->mShadowVolume); + recyclePoly(poly); + } + else + insertPoly(&(*root)->mFront, poly); +} + +void ShadowVolumeBSP::insertPolyBack(SVNode ** root, SVPoly * poly) +{ + // list of nodes where an interior has been added + if(poly->mSurfaceInfo && !(*root)->mSurfaceInfo && !(*root)->mBack) + { + if(!mFirstInteriorNode) + mFirstInteriorNode = mShadowVolumes[poly->mShadowVolume]; + mParentNodes.push_back(*root); + } + + // POLY type node? + if(!(*root)->mFront) + { + poly->mTarget = (*root); + insertPoly(&(*root)->mBack, poly); + } + else + insertPoly(&(*root)->mBack, poly); +} + +//------------------------------------------------------------------------------ + +ShadowVolumeBSP::SVNode * ShadowVolumeBSP::createNode() +{ + SVNode * node; + if(mNodeStore) + { + node = mNodeStore; + mNodeStore = mNodeStore->mFront; + } + else + node = mNodeChunker.alloc(); + + // + node->mFront = node->mBack = 0; + node->mShadowVolume = 0; + node->mSurfaceInfo = 0; + + return(node); +} + +void ShadowVolumeBSP::recycleNode(SVNode * node) +{ + if(!node) + return; + + recycleNode(node->mFront); + recycleNode(node->mBack); + + // + node->mFront = mNodeStore; + node->mBack = 0; + mNodeStore = node; +} + +ShadowVolumeBSP::SVPoly * ShadowVolumeBSP::createPoly() +{ + SVPoly * poly; + + if(mPolyStore) + { + poly = mPolyStore; + mPolyStore = mPolyStore->mNext; + } + else + poly = mPolyChunker.alloc(); + + // + poly->mNext = 0; + poly->mTarget = 0; + poly->mSurfaceInfo = 0; + poly->mShadowVolume = 0; + poly->mWindingCount = 0; + + for (U32 i = 0; i < SVPoly::MaxWinding; i++) + poly->mWinding[i] = Point3F(0.0f, 0.0f, 0.0f); + + return(poly); +} + +void ShadowVolumeBSP::recyclePoly(SVPoly * poly) +{ + if(!poly) + return; + + recyclePoly(poly->mNext); + + // + poly->mNext = mPolyStore; + mPolyStore = poly; +} + +U32 ShadowVolumeBSP::insertPlane(const PlaneF & plane) +{ + mPlanes.push_back(plane); + return(mPlanes.size() - 1); +} + +const PlaneF & ShadowVolumeBSP::getPlane(U32 index) const +{ + AssertFatal(index < mPlanes.size(), "ShadowVolumeBSP::getPlane - index out of range"); + return(mPlanes[index]); +} + +ShadowVolumeBSP::SVNode * ShadowVolumeBSP::getShadowVolume(U32 index) +{ + AssertFatal(index < mShadowVolumes.size(), "ShadowVolumeBSP::getShadowVolume - index out of range"); + return(mShadowVolumes[index]); +} + +bool ShadowVolumeBSP::testPoint(SVNode * root, const Point3F & pnt) +{ + const PlaneF & plane = getPlane(root->mPlaneIndex); + switch(plane.whichSide(pnt)) + { + case PlaneF::On: + + if(!root->mFront) + return(true); + else + { + if(testPoint(root->mFront, pnt)) + return(true); + else + { + if(!root->mBack) + return(false); + else + return(testPoint(root->mBack, pnt)); + } + } + break; + + // + case PlaneF::Front: + if(root->mFront) + return(testPoint(root->mFront, pnt)); + else + return(true); + break; + + // + case PlaneF::Back: + if(root->mBack) + return(testPoint(root->mBack, pnt)); + else + return(false); + break; + } + + return(false); +} + +//------------------------------------------------------------------------------ +bool ShadowVolumeBSP::testPoly(SVNode * root, SVPoly * poly) +{ + const PlaneF & plane = getPlane(root->mPlaneIndex); + switch(whichSide(poly, plane)) + { + case SVNode::On: + case SVNode::Front: + if(root->mFront) + return(testPoly(root->mFront, poly)); + + recyclePoly(poly); + return(true); + + case SVNode::Back: + if(root->mBack) + return(testPoly(root->mBack, poly)); + recyclePoly(poly); + break; + + case SVNode::Split: + { + if(!root->mFront) + { + recyclePoly(poly); + return(true); + } + + SVPoly * front; + SVPoly * back; + + splitPoly(poly, plane, &front, &back); + recyclePoly(poly); + + if(testPoly(root->mFront, front)) + { + recyclePoly(back); + return(true); + } + + if(root->mBack) + return(testPoly(root->mBack, back)); + + recyclePoly(back); + break; + } + } + return(false); +} + +//------------------------------------------------------------------------------ +void ShadowVolumeBSP::buildPolyVolume(SVPoly * poly, LightInfo * light) +{ + if(light->getType() != LightInfo::Vector) + return; + + // build the poly + Point3F pointOffset = light->getDirection() * 10.f; + + // create the shadow volume + mShadowVolumes.increment(); + SVNode ** traverse = &mShadowVolumes.last(); + U32 shadowVolumeIndex = mShadowVolumes.size() - 1; + + for(U32 i = 0; i < poly->mWindingCount; i++) + { + U32 j = (i + 1) % poly->mWindingCount; + if(poly->mWinding[i] == poly->mWinding[j]) + continue; + + (*traverse) = createNode(); + Point3F & a = poly->mWinding[i]; + Point3F & b = poly->mWinding[j]; + Point3F c = b + pointOffset; + + (*traverse)->mPlaneIndex = insertPlane(PlaneF(a,b,c)); + (*traverse)->mShadowVolume = shadowVolumeIndex; + + traverse = &(*traverse)->mFront; + } + + // do the poly node + (*traverse) = createNode(); + (*traverse)->mPlaneIndex = insertPlane(poly->mPlane); + (*traverse)->mShadowVolume = poly->mShadowVolume = shadowVolumeIndex; +} + +ShadowVolumeBSP::SVPoly * ShadowVolumeBSP::copyPoly(SVPoly * src) +{ + SVPoly * poly = createPoly(); + dMemcpy(poly, src, sizeof(SVPoly)); + + poly->mTarget = 0; + poly->mNext = 0; + + return(poly); +} + +//------------------------------------------------------------------------------ +void ShadowVolumeBSP::addToPolyList(SVPoly ** store, SVPoly * poly) const +{ + poly->mNext = *store; + *store = poly; +} + +//------------------------------------------------------------------------------ +void ShadowVolumeBSP::clipPoly(SVNode * root, SVPoly ** store, SVPoly * poly) +{ + if(!root) + { + recyclePoly(poly); + return; + } + + const PlaneF & plane = getPlane(root->mPlaneIndex); + + switch(whichSide(poly, plane)) + { + case SVNode::On: + case SVNode::Back: + if(root->mBack) + clipPoly(root->mBack, store, poly); + else + addToPolyList(store, poly); + break; + + case SVNode::Front: + // encountered POLY node? + if(!root->mFront) + { + recyclePoly(poly); + return; + } + else + clipPoly(root->mFront, store, poly); + break; + + case SVNode::Split: + { + SVPoly * front; + SVPoly * back; + + splitPoly(poly, plane, &front, &back); + AssertFatal(front && back, "ShadowVolumeBSP::clipPoly: invalid split"); + recyclePoly(poly); + + // front + if(!root->mFront) + { + recyclePoly(front); + return; + } + else + clipPoly(root->mFront, store, front); + + // back + if(root->mBack) + clipPoly(root->mBack, store, back); + else + addToPolyList(store, back); + break; + } + } +} + +// clip a poly to it's own shadow volume +void ShadowVolumeBSP::clipToSelf(SVNode * root, SVPoly ** store, SVPoly * poly) +{ + if(!root) + { + addToPolyList(store, poly); + return; + } + + const PlaneF & plane = getPlane(root->mPlaneIndex); + + switch(whichSide(poly, plane)) + { + case SVNode::Front: + clipToSelf(root->mFront, store, poly); + break; + + case SVNode::On: + addToPolyList(store, poly); + break; + + case SVNode::Back: + recyclePoly(poly); + break; + + case SVNode::Split: + { + SVPoly * front = 0; + SVPoly * back = 0; + splitPoly(poly, plane, &front, &back); + AssertFatal(front && back, "ShadowVolumeBSP::clipToSelf: invalid split"); + + recyclePoly(poly); + recyclePoly(back); + + clipToSelf(root->mFront, store, front); + break; + } + } +} + +//------------------------------------------------------------------------------ +F32 ShadowVolumeBSP::getPolySurfaceArea(SVPoly * poly) const +{ + if(!poly) + return(0.f); + + Point3F areaNorm(0,0,0); + for(U32 i = 0; i < poly->mWindingCount; i++) + { + U32 j = (i + 1) % poly->mWindingCount; + + Point3F tmp; + mCross(poly->mWinding[i], poly->mWinding[j], &tmp); + + areaNorm += tmp; + } + + F32 area = mDot(poly->mPlane, areaNorm); + + if(area < 0.f) + area *= -0.5f; + else + area *= 0.5f; + + if(poly->mNext) + area += getPolySurfaceArea(poly->mNext); + + return(area); +} + +//------------------------------------------------------------------------------ +F32 ShadowVolumeBSP::getClippedSurfaceArea(SVNode * root, SVPoly * poly) +{ + SVPoly * store = 0; + clipPoly(root, &store, poly); + + F32 area = getPolySurfaceArea(store); + recyclePoly(store); + return(area); +} + + +//------------------------------------------------------------------------------- +// Class SceneLighting::ShadowVolumeBSP +//------------------------------------------------------------------------------- + +void ShadowVolumeBSP::movePolyList(SVPoly ** dest, SVPoly * list) const +{ + while(list) + { + SVPoly * next = list->mNext; + addToPolyList(dest, list); + list = next; + } +} + + +F32 ShadowVolumeBSP::getLitSurfaceArea(SVPoly * poly, SurfaceInfo * surfaceInfo) +{ + // clip the poly to the shadow volumes + SVPoly * polyStore = poly; + + for(U32 i = 0; polyStore && (i < surfaceInfo->mShadowed.size()); i++) + { + SVPoly * polyList = 0; + SVPoly * traverse = polyStore; + + while(traverse) + { + SVPoly * next = traverse->mNext; + traverse->mNext = 0; + + SVPoly * currentStore = 0; + + clipPoly(mShadowVolumes[surfaceInfo->mShadowed[i]], ¤tStore, traverse); + + if(currentStore) + movePolyList(&polyList, currentStore); + + traverse = next; + } + polyStore = polyList; + } + + // get the lit area + F32 area = getPolySurfaceArea(polyStore); + recyclePoly(polyStore); + return(area); +} + +//------------------------------------------------------------------------------ +void ShadowVolumeBSP::removeLastInterior() +{ + if(!mSVRoot || !mFirstInteriorNode) + return; + + AssertFatal(mFirstInteriorNode->mSurfaceInfo, "No surface info for first interior node!"); + + // reset the planes + mPlanes.setSize(mFirstInteriorNode->mPlaneIndex); + + U32 i; + + // flush the shadow volumes + for(i = mFirstInteriorNode->mShadowVolume; i < mShadowVolumes.size(); i++) + recycleNode(mShadowVolumes[i]); + mShadowVolumes.setSize(mFirstInteriorNode->mShadowVolume); + + // flush the interior nodes + if(!mParentNodes.size() && (mFirstInteriorNode->mShadowVolume == mSVRoot->mShadowVolume)) + { + recycleNode(mSVRoot); + mSVRoot = 0; + } + else + { + for(i = 0; i < mParentNodes.size(); i++) + { + recycleNode(mParentNodes[i]->mBack); + mParentNodes[i]->mBack = 0; + } + } + + // flush the surfaces + for(i = 0; i < mSurfaces.size(); i++) + delete mSurfaces[i]; + mSurfaces.clear(); + + mFirstInteriorNode = 0; +} diff --git a/Engine/source/lighting/common/shadowVolumeBSP.h b/Engine/source/lighting/common/shadowVolumeBSP.h new file mode 100644 index 000000000..da84f5b42 --- /dev/null +++ b/Engine/source/lighting/common/shadowVolumeBSP.h @@ -0,0 +1,154 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SHADOWVOLUMEBSP_H_ +#define _SHADOWVOLUMEBSP_H_ + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _MMATH_H_ +#include "math/mMath.h" +#endif +#ifndef _DATACHUNKER_H_ +#include "core/dataChunker.h" +#endif +#ifndef _LIGHTMANAGER_H_ +#include "lighting/lightManager.h" +#endif + +/// Used to calculate shadows. +class ShadowVolumeBSP +{ + public: + ShadowVolumeBSP(); + ~ShadowVolumeBSP(); + + struct SVNode; + struct SurfaceInfo + { + U32 mSurfaceIndex; + U32 mPlaneIndex; + Vector mShadowed; + SVNode * mShadowVolume; + }; + + struct SVNode + { + enum Side + { + Front = 0, + Back = 1, + On = 2, + Split = 3 + }; + + SVNode * mFront; + SVNode * mBack; + U32 mPlaneIndex; + U32 mShadowVolume; + + /// Used with shadowed interiors. + SurfaceInfo * mSurfaceInfo; + }; + + struct SVPoly + { + enum { + MaxWinding = 32 + }; + + U32 mWindingCount; + Point3F mWinding[MaxWinding]; + + PlaneF mPlane; + SVNode * mTarget; + U32 mShadowVolume; + SVPoly * mNext; + SurfaceInfo * mSurfaceInfo; + }; + + void insertPoly(SVNode **, SVPoly *); + void insertPolyFront(SVNode **, SVPoly *); + void insertPolyBack(SVNode **, SVPoly *); + + void splitPoly(SVPoly *, const PlaneF &, SVPoly **, SVPoly **); + void insertShadowVolume(SVNode **, U32); + void addUniqueVolume(SurfaceInfo *, U32); + + SVNode::Side whichSide(SVPoly *, const PlaneF &) const; + + // + bool testPoint(SVNode *, const Point3F &); + bool testPoly(SVNode *, SVPoly *); + void addToPolyList(SVPoly **, SVPoly *) const; + void clipPoly(SVNode *, SVPoly **, SVPoly *); + void clipToSelf(SVNode *, SVPoly **, SVPoly *); + F32 getPolySurfaceArea(SVPoly *) const; + F32 getClippedSurfaceArea(SVNode *, SVPoly *); + void movePolyList(SVPoly **, SVPoly *) const; + F32 getLitSurfaceArea(SVPoly *, SurfaceInfo *); + + Vector mSurfaces; + + Chunker mNodeChunker; + Chunker mPolyChunker; + + SVNode * createNode(); + void recycleNode(SVNode *); + + SVPoly * createPoly(); + void recyclePoly(SVPoly *); + + U32 insertPlane(const PlaneF &); + const PlaneF & getPlane(U32) const; + + // + SVNode * mSVRoot; + Vector mShadowVolumes; + SVNode * getShadowVolume(U32); + + Vector mPlanes; + SVNode * mNodeStore; + SVPoly * mPolyStore; + + // used to remove the last inserted interior from the tree + Vector mParentNodes; + SVNode * mFirstInteriorNode; + void removeLastInterior(); + + /// @name Access functions + /// @{ + void insertPoly(SVPoly * poly) {insertPoly(&mSVRoot, poly);} + bool testPoint(Point3F & pnt) {return(testPoint(mSVRoot, pnt));} + bool testPoly(SVPoly * poly) {return(testPoly(mSVRoot, poly));} + F32 getClippedSurfaceArea(SVPoly * poly) {return(getClippedSurfaceArea(mSVRoot, poly));} + /// @} + + /// @name Helpers + /// @{ + void buildPolyVolume(SVPoly *, LightInfo *); + SVPoly * copyPoly(SVPoly *); + /// @} +}; + +#endif diff --git a/Engine/source/lighting/lightInfo.cpp b/Engine/source/lighting/lightInfo.cpp new file mode 100644 index 000000000..1759ba24b --- /dev/null +++ b/Engine/source/lighting/lightInfo.cpp @@ -0,0 +1,221 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/lightInfo.h" + +#include "math/mMath.h" +#include "core/color.h" +#include "gfx/gfxCubemap.h" +#include "console/simObject.h" +#include "math/mathUtils.h" + + +LightInfoExType::LightInfoExType( const char *type ) +{ + TypeMap::Iterator iter = getTypeMap().find( type ); + if ( iter == getTypeMap().end() ) + iter = getTypeMap().insertUnique( type, getTypeMap().size() ); + + mTypeIndex = iter->value; +} + + +LightInfo::LightInfo() + : mTransform( true ), + mColor( 0.0f, 0.0f, 0.0f, 1.0f ), + mBrightness( 1.0f ), + mAmbient( 0.0f, 0.0f, 0.0f, 1.0f ), + mRange( 1.0f, 1.0f, 1.0f ), + mInnerConeAngle( 90.0f ), + mOuterConeAngle( 90.0f ), + mType( Vector ), + mCastShadows( false ), + mPriority( 1.0f ), + mScore( 0.0f ), + mDebugRender( false ) +{ +} + +LightInfo::~LightInfo() +{ + deleteAllLightInfoEx(); +} + +void LightInfo::set( const LightInfo *light ) +{ + mTransform = light->mTransform; + mColor = light->mColor; + mBrightness = light->mBrightness; + mAmbient = light->mAmbient; + mRange = light->mRange; + mInnerConeAngle = light->mInnerConeAngle; + mOuterConeAngle = light->mOuterConeAngle; + mType = light->mType; + mCastShadows = light->mCastShadows; + + for ( U32 i=0; i < mExtended.size(); i++ ) + { + LightInfoEx *ex = light->mExtended[ i ]; + if ( ex ) + mExtended[i]->set( ex ); + else + { + delete mExtended[i]; + mExtended[i] = NULL; + } + } +} + +void LightInfo::setGFXLight( GFXLightInfo *outLight ) +{ + switch( getType() ) + { + case LightInfo::Point : + outLight->mType = GFXLightInfo::Point; + break; + case LightInfo::Spot : + outLight->mType = GFXLightInfo::Spot; + break; + case LightInfo::Vector: + outLight->mType = GFXLightInfo::Vector; + break; + case LightInfo::Ambient: + outLight->mType = GFXLightInfo::Ambient; + break; + default: + break; + } + + outLight->mPos = getPosition(); + outLight->mDirection = getDirection(); + outLight->mColor = mColor * mBrightness; + outLight->mAmbient = mAmbient; + outLight->mRadius = mRange.x; + outLight->mInnerConeAngle = mInnerConeAngle; + outLight->mOuterConeAngle = mOuterConeAngle; +} + +void LightInfo::setDirection( const VectorF &dir ) +{ + MathUtils::getMatrixFromForwardVector( mNormalize( dir ), &mTransform ); +} + +void LightInfo::deleteExtended( const LightInfoExType& type ) +{ + if ( type >= mExtended.size() ) + return; + + SAFE_DELETE( mExtended[ type ] ); +} + +void LightInfo::deleteAllLightInfoEx() +{ + for ( U32 i = 0; i < mExtended.size(); i++ ) + delete mExtended[ i ]; + + mExtended.clear(); +} + +LightInfoEx* LightInfo::getExtended( const LightInfoExType &type ) const +{ + if ( type >= mExtended.size() ) + return NULL; + + return mExtended[ type ]; +} + +void LightInfo::addExtended( LightInfoEx *lightInfoEx ) +{ + AssertFatal( lightInfoEx, "LightInfo::addExtended() - Got null extended light info!" ); + + const LightInfoExType &type = lightInfoEx->getType(); + + while ( mExtended.size() <= type ) + mExtended.push_back( NULL ); + + delete mExtended[type]; + mExtended[type] = lightInfoEx; +} + +void LightInfo::packExtended( BitStream *stream ) const +{ + for ( U32 i = 0; i < mExtended.size(); i++ ) + if ( mExtended[ i ] ) + mExtended[ i ]->packUpdate( stream ); +} + +void LightInfo::unpackExtended( BitStream *stream ) +{ + for ( U32 i = 0; i < mExtended.size(); i++ ) + if ( mExtended[ i ] ) + mExtended[ i ]->unpackUpdate( stream ); +} + +void LightInfo::getWorldToLightProj( MatrixF *outMatrix ) const +{ + if ( mType == Spot ) + { + // For spots we need to include the cone projection. + F32 fov = mDegToRad( getOuterConeAngle() ); + F32 range = getRange().x; + MatrixF proj; + MathUtils::makeProjection( &proj, fov, 1.0f, range * 0.01f, range, true ); + + MatrixF light = getTransform(); + light.inverse(); + + *outMatrix = proj * light; + return; + } + else + { + // The other lights just use the light transform. + *outMatrix = getTransform(); + outMatrix->inverse(); + } +} + +void LightInfoList::registerLight( LightInfo *light ) +{ + if(!light) + return; + // just add the light, we'll try to scan for dupes later... + push_back(light); +} + +void LightInfoList::unregisterLight( LightInfo *light ) +{ + // remove all of them... + LightInfoList &list = *this; + for(U32 i=0; i TypeMap; + + /// Returns the map of all the info types. We create + /// it as a method static so that its available to other + /// statics regardless of initialization order. + static inline TypeMap& getTypeMap() + { + static TypeMap smTypeMap; + return smTypeMap; + } + + /// The info type index for this type. + U32 mTypeIndex; + +public: + + LightInfoExType( const char *type ); + + inline LightInfoExType( const LightInfoExType &type ) + : mTypeIndex( type.mTypeIndex ) + { + } + + inline operator U32 () const { return mTypeIndex; } +}; + +/// This is the base class for extended lighting info +/// that lies outside of the normal info stored in LightInfo. +class LightInfoEx +{ +public: + + /// Basic destructor so we can delete the extended info + /// without knowing the concrete type. + virtual ~LightInfoEx() { } + + /// + virtual const LightInfoExType& getType() const = 0; + + /// Copy the values from the other LightInfoEx. + virtual void set( const LightInfoEx *ex ) {} + + /// + virtual void packUpdate( BitStream *stream ) const {} + + /// + virtual void unpackUpdate( BitStream *stream ) {} +}; + + +/// This is the base light information class that will be tracked by the +/// engine. Should basically contain a bounding volume and methods to interact +/// with the rest of the system (for example, setting GFX fixed function lights). +class LightInfo +{ +public: + + enum Type + { + Point = 0, + Spot = 1, + Vector = 2, + Ambient = 3, + Count = 4, + }; + +protected: + + Type mType; + + /// The primary light color. + ColorF mColor; + + F32 mBrightness; + + ColorF mAmbient; + + MatrixF mTransform; + + Point3F mRange; + + F32 mInnerConeAngle; + + F32 mOuterConeAngle; + + bool mCastShadows; + + ::Vector mExtended; + + /// The priority of this light used for + /// light and shadow scoring. + F32 mPriority; + + /// A temporary which holds the score used + /// when prioritizing lights for rendering. + F32 mScore; + + /// Whether to render debugging visualizations + /// for this light. + bool mDebugRender; + +public: + + LightInfo(); + ~LightInfo(); + + // Copies data passed in from light + void set( const LightInfo *light ); + + // Sets a fixed function GFXLight with our properties + void setGFXLight( GFXLightInfo *light ); + + // Accessors + Type getType() const { return mType; } + void setType( Type val ) { mType = val; } + + const MatrixF& getTransform() const { return mTransform; } + void setTransform( const MatrixF &xfm ) { mTransform = xfm; } + + Point3F getPosition() const { return mTransform.getPosition(); } + void setPosition( const Point3F &pos ) { mTransform.setPosition( pos ); } + + VectorF getDirection() const { return mTransform.getForwardVector(); } + void setDirection( const VectorF &val ); + + const ColorF& getColor() const { return mColor; } + void setColor( const ColorF &val ) { mColor = val; } + + F32 getBrightness() const { return mBrightness; } + void setBrightness( F32 val ) { mBrightness = val; } + + const ColorF& getAmbient() const { return mAmbient; } + void setAmbient( const ColorF &val ) { mAmbient = val; } + + const Point3F& getRange() const { return mRange; } + void setRange( const Point3F &range ) { mRange = range; } + void setRange( F32 range ) { mRange.set( range, range, range ); } + + F32 getInnerConeAngle() const { return mInnerConeAngle; } + void setInnerConeAngle( F32 val ) { mInnerConeAngle = val; } + + F32 getOuterConeAngle() const { return mOuterConeAngle; } + void setOuterConeAngle( F32 val ) { mOuterConeAngle = val; } + + bool getCastShadows() const { return mCastShadows; } + void setCastShadows( bool castShadows ) { mCastShadows = castShadows; } + + void setPriority( F32 priority ) { mPriority = priority; } + F32 getPriority() const { return mPriority; } + + void setScore( F32 score ) { mScore = score; } + F32 getScore() const { return mScore; } + + bool isDebugRenderingEnabled() const { return mDebugRender; } + void enableDebugRendering( bool value ) { mDebugRender = value; } + + /// Helper function for getting the extended light info. + /// @see getExtended + template + inline ExClass* getExtended() const { return (ExClass*)getExtended( ExClass::Type ); } + + /// Returns the extended light info for the selected type. + LightInfoEx* getExtended( const LightInfoExType &type ) const; + + /// Adds the extended info to the light deleting the + /// existing extended info if it has one. + void addExtended( LightInfoEx *lightInfoEx ); + + /// Delete all registered LightInfoEx instances of the given + /// type. + void deleteExtended( const LightInfoExType& type ); + + /// + void deleteAllLightInfoEx(); + + // Builds the world to light view projection used for + // shadow texture and cookie lookups. + void getWorldToLightProj( MatrixF *outMatrix ) const; + + /// + void packExtended( BitStream *stream ) const; + + /// + void unpackExtended( BitStream *stream ); +}; + + +/// +class LightInfoList : public Vector +{ +public: + void registerLight( LightInfo *light ); + void unregisterLight( LightInfo *light ); +}; + + +/// When the scene is queried for lights, the light manager will get +/// this interface to trigger a register light call. +class ISceneLight +{ +public: + + virtual ~ISceneLight() {} + + /// Submit lights to the light manager passed in. + virtual void submitLights( LightManager *lm, bool staticLighting ) = 0; + + /// + virtual LightInfo* getLight() = 0; +}; + +#endif // _LIGHTINFO_H_ diff --git a/Engine/source/lighting/lightManager.cpp b/Engine/source/lighting/lightManager.cpp new file mode 100644 index 000000000..fb7660c04 --- /dev/null +++ b/Engine/source/lighting/lightManager.cpp @@ -0,0 +1,499 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/lightManager.h" + +#include "console/console.h" +#include "console/consoleTypes.h" +#include "core/util/safeDelete.h" +#include "console/sim.h" +#include "console/simSet.h" +#include "scene/sceneManager.h" +#include "materials/materialManager.h" +#include "materials/sceneData.h" +#include "lighting/lightInfo.h" +#include "lighting/lightingInterfaces.h" +#include "T3D/gameBase/gameConnection.h" +#include "gfx/gfxStringEnumTranslate.h" +#include "console/engineAPI.h" +#include "renderInstance/renderPrePassMgr.h" + + +Signal LightManager::smActivateSignal; +LightManager *LightManager::smActiveLM = NULL; + + +LightManager::LightManager( const char *name, const char *id ) + : mName( name ), + mId( id ), + mIsActive( false ), + mSceneManager( NULL ), + mDefaultLight( NULL ), + mAvailableSLInterfaces( NULL ), + mCullPos( Point3F::Zero ) +{ + _getLightManagers().insert( mName, this ); + + dMemset( &mSpecialLights, 0, sizeof( mSpecialLights ) ); +} + +LightManager::~LightManager() +{ + _getLightManagers().erase( mName ); + SAFE_DELETE( mAvailableSLInterfaces ); + SAFE_DELETE( mDefaultLight ); +} + +LightManagerMap& LightManager::_getLightManagers() +{ + static LightManagerMap lightManagerMap; + return lightManagerMap; +} + +LightManager* LightManager::findByName( const char *name ) +{ + LightManagerMap &lightManagers = _getLightManagers(); + + LightManagerMap::Iterator iter = lightManagers.find( name ); + if ( iter != lightManagers.end() ) + return iter->value; + + return NULL; +} + +void LightManager::getLightManagerNames( String *outString ) +{ + LightManagerMap &lightManagers = _getLightManagers(); + LightManagerMap::Iterator iter = lightManagers.begin(); + for ( ; iter != lightManagers.end(); iter++ ) + *outString += iter->key + "\t"; + + // TODO! + //outString->rtrim(); +} + +LightInfo* LightManager::createLightInfo(LightInfo* light /* = NULL */) +{ + LightInfo *outLight = (light != NULL) ? light : new LightInfo; + + LightManagerMap &lightManagers = _getLightManagers(); + LightManagerMap::Iterator iter = lightManagers.begin(); + for ( ; iter != lightManagers.end(); iter++ ) + { + LightManager *lm = iter->value; + lm->_addLightInfoEx( outLight ); + } + + return outLight; +} + +void LightManager::initLightFields() +{ + LightManagerMap &lightManagers = _getLightManagers(); + + LightManagerMap::Iterator iter = lightManagers.begin(); + for ( ; iter != lightManagers.end(); iter++ ) + { + LightManager *lm = iter->value; + lm->_initLightFields(); + } +} + +IMPLEMENT_GLOBAL_CALLBACK( onLightManagerActivate, void, ( const char *name ), ( name ), + "A callback called by the engine when a light manager is activated.\n" + "@param name The name of the light manager being activated.\n" + "@ingroup Lighting\n" ); + +void LightManager::activate( SceneManager *sceneManager ) +{ + AssertFatal( sceneManager, "LightManager::activate() - Got null scene manager!" ); + AssertFatal( mIsActive == false, "LightManager::activate() - Already activated!" ); + AssertFatal( smActiveLM == NULL, "LightManager::activate() - A previous LM is still active!" ); + + mIsActive = true; + mSceneManager = sceneManager; + smActiveLM = this; + + onLightManagerActivate_callback( getName() ); +} + +IMPLEMENT_GLOBAL_CALLBACK( onLightManagerDeactivate, void, ( const char *name ), ( name ), + "A callback called by the engine when a light manager is deactivated.\n" + "@param name The name of the light manager being deactivated.\n" + "@ingroup Lighting\n" ); + +void LightManager::deactivate() +{ + AssertFatal( mIsActive == true, "LightManager::deactivate() - Already deactivated!" ); + AssertFatal( smActiveLM == this, "LightManager::activate() - This isn't the active light manager!" ); + + if( Sim::getRootGroup() ) // To protect against shutdown. + onLightManagerDeactivate_callback( getName() ); + + mIsActive = false; + mSceneManager = NULL; + smActiveLM = NULL; + + // Just in case... make sure we're all clear. + unregisterAllLights(); +} + +LightInfo* LightManager::getDefaultLight() +{ + // The sun is always our default light when + // when its registered. + if ( mSpecialLights[ LightManager::slSunLightType ] ) + return mSpecialLights[ LightManager::slSunLightType ]; + + // Else return a dummy special light. + if ( !mDefaultLight ) + mDefaultLight = createLightInfo(); + return mDefaultLight; +} + +LightInfo* LightManager::getSpecialLight( LightManager::SpecialLightTypesEnum type, bool useDefault ) +{ + if ( mSpecialLights[type] ) + return mSpecialLights[type]; + + if ( useDefault ) + return getDefaultLight(); + + return NULL; +} + +void LightManager::setSpecialLight( LightManager::SpecialLightTypesEnum type, LightInfo *light ) +{ + if ( light && type == slSunLightType ) + { + // The sun must be specially positioned and ranged + // so that it can be processed like a point light + // in the stock light shader used by Basic Lighting. + + light->setPosition( mCullPos - ( light->getDirection() * 10000.0f ) ); + light->setRange( 2000000.0f ); + } + + mSpecialLights[type] = light; + registerGlobalLight( light, NULL ); +} + +void LightManager::registerGlobalLights( const Frustum *frustum, bool staticLighting ) +{ + PROFILE_SCOPE( LightManager_RegisterGlobalLights ); + + // TODO: We need to work this out... + // + // 1. Why do we register and unregister lights on every + // render when they don't often change... shouldn't we + // just register once and keep them? + // + // 2. If we do culling of lights should this happen as part + // of registration or somewhere else? + // + + // Grab the lights to process. + Vector activeLights; + const U32 lightMask = LightObjectType; + + if ( staticLighting || !frustum ) + { + // We're processing static lighting or want all the lights + // in the container registerd... so no culling. + getSceneManager()->getContainer()->findObjectList( lightMask, &activeLights ); + } + else + { + // Cull the lights using the frustum. + getSceneManager()->getContainer()->findObjectList( *frustum, lightMask, &activeLights ); + + // Store the culling position for sun placement + // later... see setSpecialLight. + mCullPos = frustum->getPosition(); + + // HACK: Make sure the control object always gets + // processed as lights mounted to it don't change + // the shape bounds and can often get culled. + + GameConnection *conn = GameConnection::getConnectionToServer(); + if ( conn->getControlObject() ) + { + GameBase *conObject = conn->getControlObject(); + activeLights.push_back_unique( conObject ); + } + } + + // Let the lights register themselves. + for ( U32 i = 0; i < activeLights.size(); i++ ) + { + ISceneLight *lightInterface = dynamic_cast( activeLights[i] ); + if ( lightInterface ) + lightInterface->submitLights( this, staticLighting ); + } +} + +void LightManager::registerGlobalLight( LightInfo *light, SimObject *obj ) +{ + AssertFatal( !mRegisteredLights.contains( light ), + "LightManager::registerGlobalLight - This light is already registered!" ); + + mRegisteredLights.push_back( light ); +} + +void LightManager::unregisterGlobalLight( LightInfo *light ) +{ + mRegisteredLights.unregisterLight( light ); + + // If this is the sun... clear the special light too. + if ( light == mSpecialLights[slSunLightType] ) + dMemset( mSpecialLights, 0, sizeof( mSpecialLights ) ); +} + +void LightManager::registerLocalLight( LightInfo *light ) +{ + // TODO: What should we do here? +} + +void LightManager::unregisterLocalLight( LightInfo *light ) +{ + // TODO: What should we do here? +} + +void LightManager::unregisterAllLights() +{ + dMemset( mSpecialLights, 0, sizeof( mSpecialLights ) ); + mRegisteredLights.clear(); +} + +void LightManager::getAllUnsortedLights( Vector *list ) const +{ + list->merge( mRegisteredLights ); +} + +void LightManager::_update4LightConsts( const SceneData &sgData, + GFXShaderConstHandle *lightPositionSC, + GFXShaderConstHandle *lightDiffuseSC, + GFXShaderConstHandle *lightAmbientSC, + GFXShaderConstHandle *lightInvRadiusSqSC, + GFXShaderConstHandle *lightSpotDirSC, + GFXShaderConstHandle *lightSpotAngleSC, + GFXShaderConstHandle *lightSpotFalloffSC, + GFXShaderConstBuffer *shaderConsts ) +{ + PROFILE_SCOPE( LightManager_Update4LightConsts ); + + // Skip over gathering lights if we don't have to! + if ( lightPositionSC->isValid() || + lightDiffuseSC->isValid() || + lightInvRadiusSqSC->isValid() || + lightSpotDirSC->isValid() || + lightSpotAngleSC->isValid() || + lightSpotFalloffSC->isValid() ) + { + PROFILE_SCOPE( LightManager_Update4LightConsts_setLights ); + + // NOTE: We haven't ported the lighting shaders on OSX + // to the optimized HLSL versions. + #ifdef TORQUE_OS_MAC + static AlignedArray lightPositions( 4, sizeof( Point4F ) ); + #else + static AlignedArray lightPositions( 3, sizeof( Point4F ) ); + static AlignedArray lightSpotDirs( 3, sizeof( Point4F ) ); + #endif + static AlignedArray lightColors( 4, sizeof( Point4F ) ); + static Point4F lightInvRadiusSq; + static Point4F lightSpotAngle; + static Point4F lightSpotFalloff; + F32 range; + + // Need to clear the buffers so that we don't leak + // lights from previous passes or have NaNs. + dMemset( lightPositions.getBuffer(), 0, lightPositions.getBufferSize() ); + dMemset( lightColors.getBuffer(), 0, lightColors.getBufferSize() ); + lightInvRadiusSq = Point4F::Zero; + lightSpotAngle.set( -1.0f, -1.0f, -1.0f, -1.0f ); + lightSpotFalloff.set( F32_MAX, F32_MAX, F32_MAX, F32_MAX ); + + // Gather the data for the first 4 lights. + const LightInfo *light; + for ( U32 i=0; i < 4; i++ ) + { + light = sgData.lights[i]; + if ( !light ) + break; + + #ifdef TORQUE_OS_MAC + + lightPositions[i] = light->getPosition(); + + #else + + // The light positions and spot directions are + // in SoA order to make optimal use of the GPU. + const Point3F &lightPos = light->getPosition(); + lightPositions[0][i] = lightPos.x; + lightPositions[1][i] = lightPos.y; + lightPositions[2][i] = lightPos.z; + + const VectorF &lightDir = light->getDirection(); + lightSpotDirs[0][i] = lightDir.x; + lightSpotDirs[1][i] = lightDir.y; + lightSpotDirs[2][i] = lightDir.z; + + if ( light->getType() == LightInfo::Spot ) + { + lightSpotAngle[i] = mCos( mDegToRad( light->getOuterConeAngle() / 2.0f ) ); + lightSpotFalloff[i] = 1.0f / getMax( F32_MIN, mCos( mDegToRad( light->getInnerConeAngle() / 2.0f ) ) - lightSpotAngle[i] ); + } + + #endif + + // Prescale the light color by the brightness to + // avoid doing this in the shader. + lightColors[i] = Point4F(light->getColor()) * light->getBrightness(); + + // We need 1 over range^2 here. + range = light->getRange().x; + lightInvRadiusSq[i] = 1.0f / ( range * range ); + } + + shaderConsts->setSafe( lightPositionSC, lightPositions ); + shaderConsts->setSafe( lightDiffuseSC, lightColors ); + shaderConsts->setSafe( lightInvRadiusSqSC, lightInvRadiusSq ); + + #ifndef TORQUE_OS_MAC + + shaderConsts->setSafe( lightSpotDirSC, lightSpotDirs ); + shaderConsts->setSafe( lightSpotAngleSC, lightSpotAngle ); + shaderConsts->setSafe( lightSpotFalloffSC, lightSpotFalloff ); + + #endif + } + + // Setup the ambient lighting from the first + // light which is the directional light if + // one exists at all in the scene. + if ( lightAmbientSC->isValid() ) + shaderConsts->set( lightAmbientSC, sgData.ambientLightColor ); +} + +AvailableSLInterfaces* LightManager::getSceneLightingInterface() +{ + if ( !mAvailableSLInterfaces ) + mAvailableSLInterfaces = new AvailableSLInterfaces(); + + return mAvailableSLInterfaces; +} + +bool LightManager::lightScene( const char* callback, const char* param ) +{ + BitSet32 flags = 0; + + if ( param ) + { + if ( !dStricmp( param, "forceAlways" ) ) + flags.set( SceneLighting::ForceAlways ); + else if ( !dStricmp(param, "forceWritable" ) ) + flags.set( SceneLighting::ForceWritable ); + else if ( !dStricmp(param, "loadOnly" ) ) + flags.set( SceneLighting::LoadOnly ); + } + + // The SceneLighting object will delete itself + // once the lighting process is complete. + SceneLighting* sl = new SceneLighting( getSceneLightingInterface() ); + return sl->lightScene( callback, flags ); +} + +RenderPrePassMgr* LightManager::_findPrePassRenderBin() +{ + RenderPassManager* rpm = getSceneManager()->getDefaultRenderPass(); + for( U32 i = 0; i < rpm->getManagerCount(); i++ ) + { + RenderBinManager *bin = rpm->getManager( i ); + if( bin->getRenderInstType() == RenderPrePassMgr::RIT_PrePass ) + { + return ( RenderPrePassMgr* ) bin; + } + } + + return NULL; +} + +DefineEngineFunction( setLightManager, bool, ( const char *name ),, + "Finds and activates the named light manager.\n" + "@return Returns true if the light manager is found and activated.\n" + "@ingroup Lighting\n" ) +{ + return gClientSceneGraph->setLightManager( name ); +} + +DefineEngineFunction( lightScene, bool, ( const char *completeCallbackFn, const char *mode ), ( NULL, NULL ), + "Will generate static lighting for the scene if supported by the active light manager.\n\n" + "If mode is \"forceAlways\", the lightmaps will be regenerated regardless of whether " + "lighting cache files can be written to. If mode is \"forceWritable\", then the lightmaps " + "will be regenerated only if the lighting cache files can be written.\n" + "@param completeCallbackFn The name of the function to execute when the lighting is complete.\n" + "@param mode One of \"forceAlways\", \"forceWritable\" or \"loadOnly\".\n" + "@return Returns true if the scene lighting process was started.\n" + "@ingroup Lighting\n" ) +{ + if ( !LIGHTMGR ) + return false; + + return LIGHTMGR->lightScene( completeCallbackFn, mode ); +} + +DefineEngineFunction( getLightManagerNames, String, (),, + "Returns a tab seperated list of light manager names.\n" + "@ingroup Lighting\n" ) +{ + String names; + LightManager::getLightManagerNames( &names ); + return names; +} + +DefineEngineFunction( getActiveLightManager, const char*, (),, + "Returns the active light manager name.\n" + "@ingroup Lighting\n" ) +{ + if ( !LIGHTMGR ) + return NULL; + + return LIGHTMGR->getName(); +} + +DefineEngineFunction( resetLightManager, void, (),, + "@brief Deactivates and then activates the currently active light manager." + "This causes most shaders to be regenerated and is often used when global " + "rendering changes have occured.\n" + "@ingroup Lighting\n" ) +{ + LightManager *lm = LIGHTMGR; + if ( !lm ) + return; + + SceneManager *sm = lm->getSceneManager(); + lm->deactivate(); + lm->activate( sm ); +} diff --git a/Engine/source/lighting/lightManager.h b/Engine/source/lighting/lightManager.h new file mode 100644 index 000000000..a0ba1cb70 --- /dev/null +++ b/Engine/source/lighting/lightManager.h @@ -0,0 +1,232 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _LIGHTMANAGER_H_ +#define _LIGHTMANAGER_H_ + +#ifndef _TORQUE_STRING_H_ +#include "core/util/str.h" +#endif +#ifndef _TSIGNAL_H_ +#include "core/util/tSignal.h" +#endif +#ifndef _LIGHTINFO_H_ +#include "lighting/lightInfo.h" +#endif +#ifndef _LIGHTQUERY_H_ +#include "lighting/lightQuery.h" +#endif + +class SimObject; +class LightManager; +class Material; +class ProcessedMaterial; +class SceneManager; +struct SceneData; +class Point3F; +class AvailableSLInterfaces; +class SceneObject; +class GFXShaderConstBuffer; +class GFXShaderConstHandle; +class ShaderConstHandles; +class SceneRenderState; +class RenderPrePassMgr; +class Frustum; + +/// +typedef Map LightManagerMap; + + +class LightManager +{ +public: + + enum SpecialLightTypesEnum + { + slSunLightType, + slSpecialLightTypesCount + }; + + LightManager( const char *name, const char *id ); + + virtual ~LightManager(); + + /// + static void initLightFields(); + + /// + static LightInfo* createLightInfo(LightInfo* light = NULL); + + /// + static LightManager* findByName( const char *name ); + + /// Returns a tab seperated list of available light managers. + static void getLightManagerNames( String *outString ); + + /// The light manager activation signal. + static Signal smActivateSignal; + + /// Returns the active LM. + static inline LightManager* getActiveLM() { return smActiveLM; } + + /// Return an id string used to load different versions of light manager + /// specific assets. It shoud be short, contain no spaces, and be safe + /// for filename use. + const char* getName() const { return mName.c_str(); } + + /// Return an id string used to load different versions of light manager + /// specific assets. It shoud be short, contain no spaces, and be safe + /// for filename use. + const char* getId() const { return mId.c_str(); } + + // Returns the scene manager passed at activation. + SceneManager* getSceneManager() { return mSceneManager; } + + // Should return true if this light manager is compatible + // on the current platform and GFX device. + virtual bool isCompatible() const = 0; + + // Called when the lighting manager should become active + virtual void activate( SceneManager *sceneManager ); + + // Called when we don't want the light manager active (should clean up) + virtual void deactivate(); + + // Returns the active scene lighting interface for this light manager. + virtual AvailableSLInterfaces* getSceneLightingInterface(); + + // Returns a "default" light info that callers should not free. Used for instances where we don't actually care about + // the light (for example, setting default data for SceneData) + virtual LightInfo* getDefaultLight(); + + /// Returns the special light or the default light if useDefault is true. + /// @see getDefaultLight + virtual LightInfo* getSpecialLight( SpecialLightTypesEnum type, + bool useDefault = true ); + + /// Set a special light type. + virtual void setSpecialLight( SpecialLightTypesEnum type, LightInfo *light ); + + // registered before scene traversal... + virtual void registerGlobalLight( LightInfo *light, SimObject *obj ); + virtual void unregisterGlobalLight( LightInfo *light ); + + // registered per object... + virtual void registerLocalLight( LightInfo *light ); + virtual void unregisterLocalLight( LightInfo *light ); + + virtual void registerGlobalLights( const Frustum *frustum, bool staticlighting ); + virtual void unregisterAllLights(); + + /// Returns all unsorted and un-scored lights (both global and local). + void getAllUnsortedLights( Vector *list ) const; + + /// Sets shader constants / textures for light infos + virtual void setLightInfo( ProcessedMaterial *pmat, + const Material *mat, + const SceneData &sgData, + const SceneRenderState *state, + U32 pass, + GFXShaderConstBuffer *shaderConsts ) = 0; + + /// Allows us to set textures during the Material::setTextureStage call, return true if we've done work. + virtual bool setTextureStage( const SceneData &sgData, + const U32 currTexFlag, + const U32 textureSlot, + GFXShaderConstBuffer *shaderConsts, + ShaderConstHandles *handles ) = 0; + + /// Called when the static scene lighting (aka lightmaps) should be computed. + virtual bool lightScene( const char* callback, const char* param ); + + /// Returns true if this light manager is active + virtual bool isActive() const { return mIsActive; } + +protected: + + /// The current active light manager. + static LightManager *smActiveLM; + + /// Find the pre-pass render bin on the scene's default render pass. + RenderPrePassMgr* _findPrePassRenderBin(); + + /// This helper function sets the shader constansts + /// for the stock 4 light forward lighting code. + static void _update4LightConsts( const SceneData &sgData, + GFXShaderConstHandle *lightPositionSC, + GFXShaderConstHandle *lightDiffuseSC, + GFXShaderConstHandle *lightAmbientSC, + GFXShaderConstHandle *lightInvRadiusSqSC, + GFXShaderConstHandle *lightSpotDirSC, + GFXShaderConstHandle *lightSpotAngleSC, + GFXShaderConstHandle *lightSpotFalloffSC, + GFXShaderConstBuffer *shaderConsts ); + + /// A dummy default light used when no lights + /// happen to be registered with the manager. + LightInfo *mDefaultLight; + + /// The list of global registered lights which is + /// initialized before the scene is rendered. + LightInfoList mRegisteredLights; + + /// The registered special light list. + LightInfo *mSpecialLights[slSpecialLightTypesCount]; + + /// The root culling position used for + /// special sun light placement. + /// @see setSpecialLight + Point3F mCullPos; + + /// The scene lighting interfaces for + /// lightmap generation. + AvailableSLInterfaces *mAvailableSLInterfaces; + + /// Attaches any LightInfoEx data for this manager + /// to the light info object. + virtual void _addLightInfoEx( LightInfo *lightInfo ) = 0; + + /// + virtual void _initLightFields() = 0; + + /// Returns the static light manager map. + static LightManagerMap& _getLightManagers(); + + /// The constant light manager name initialized + /// in the constructor. + const String mName; + + /// The constant light manager identifier initialized + /// in the constructor. + const String mId; + + /// Is true if this light manager has been activated. + bool mIsActive; + + /// The scene graph the light manager is associated with. + SceneManager *mSceneManager; +}; + +/// Returns the current active light manager. +#define LIGHTMGR LightManager::getActiveLM() + +#endif // _LIGHTMANAGER_H_ diff --git a/Engine/source/lighting/lightQuery.cpp b/Engine/source/lighting/lightQuery.cpp new file mode 100644 index 000000000..fa62235f5 --- /dev/null +++ b/Engine/source/lighting/lightQuery.cpp @@ -0,0 +1,171 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/lightQuery.h" + +#include "lighting/lightManager.h" +#include "platform/profiler.h" + + +LightQuery::LightQuery( U32 maxLights ) + : mMaxLights( maxLights ) +{ +} + +LightQuery::~LightQuery() +{ +} + +void LightQuery::init( const Point3F &cameraPos, + const Point3F &cameraDir, + F32 viewDist ) +{ + mVolume.center = cameraPos; + mVolume.radius = viewDist; + mLights.clear(); +} + +void LightQuery::init( const SphereF &bounds ) +{ + mVolume = bounds; + mLights.clear(); +} + +void LightQuery::init( const Box3F &bounds ) +{ + bounds.getCenter( &mVolume.center ); + mVolume.radius = ( bounds.maxExtents - mVolume.center ).len(); + mLights.clear(); +} + +void LightQuery::getLights( LightInfo** outLights, U32 maxLights ) +{ + PROFILE_SCOPE( LightQuery_getLights ); + + // Gather lights if we haven't already. + if ( mLights.empty() ) + _scoreLights(); + + U32 lightCount = getMin( (U32)mLights.size(), getMin( mMaxLights, maxLights ) ); + + // Copy them over. + for ( U32 i = 0; i < lightCount; i++ ) + { + LightInfo *light = mLights[i]; + + // If the score reaches zero then we got to + // the end of the valid lights for this object. + if ( light->getScore() <= 0.0f ) + break; + + outLights[i] = light; + } +} + +void LightQuery::_scoreLights() +{ + PROFILE_SCOPE( LightQuery_scoreLights ); + + if ( !LIGHTMGR ) + return; + + // Get all the lights. + LIGHTMGR->getAllUnsortedLights( &mLights ); + LightInfo *sun = LIGHTMGR->getSpecialLight( LightManager::slSunLightType ); + + const Point3F lumDot( 0.2125f, 0.7154f, 0.0721f ); + + Vector::iterator iter = mLights.begin(); + for ( ; iter != mLights.end(); iter++ ) + { + // Get the light. + LightInfo *light = (*iter); + + F32 luminace = 0.0f; + F32 dist = 0.0f; + F32 weight = 0.0f; + + const bool isSpot = light->getType() == LightInfo::Spot; + const bool isPoint = light->getType() == LightInfo::Point; + + if ( isPoint || isSpot ) + { + // Get the luminocity. + luminace = mDot( light->getColor(), lumDot ) * light->getBrightness(); + + // Get the distance to the light... score it 1 to 0 near to far. + F32 lenSq = ( mVolume.center - light->getPosition() ).lenSquared(); + + F32 radiusSq = mSquared( light->getRange().x + mVolume.radius ); + F32 distSq = radiusSq - lenSq; + + if ( distSq > 0.0f ) + dist = mClampF( distSq / ( 1000.0f * 1000.0f ), 0.0f, 1.0f ); + + // TODO: This culling is broken... it culls spotlights + // that are actually visible. + if ( false && isSpot && dist > 0.0f ) + { + // TODO: I cannot test to see if we're within + // the cone without a more detailed test... so + // just reject if we're behind the spot direction. + + Point3F toCenter = mVolume.center - light->getPosition(); + F32 angDot = mDot( toCenter, light->getDirection() ); + if ( angDot < 0.0f ) + dist = 0.0f; + } + + weight = light->getPriority(); + } + else + { + // The sun always goes first + // regardless of the settings. + if ( light == sun ) + { + weight = F32_MAX; + dist = 1.0f; + luminace = 1.0f; + } + else + { + // TODO: When we have multiple directional + // lights we should score them here. + } + } + + // TODO: Manager ambient lights here too! + + light->setScore( luminace * weight * dist ); + } + + // Sort them! + mLights.sort( _lightScoreCmp ); +} + +S32 LightQuery::_lightScoreCmp( LightInfo* const *a, LightInfo* const *b ) +{ + F32 diff = (*a)->getScore() - (*b)->getScore(); + return diff < 0 ? 1 : diff > 0 ? -1 : 0; +} diff --git a/Engine/source/lighting/lightQuery.h b/Engine/source/lighting/lightQuery.h new file mode 100644 index 000000000..7fdeb1e9a --- /dev/null +++ b/Engine/source/lighting/lightQuery.h @@ -0,0 +1,82 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _LIGHTQUERY_H_ +#define _LIGHTQUERY_H_ + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +#ifndef _MSPHERE_H_ +#include "math/mSphere.h" +#endif + +#ifndef _MBOX_H_ +#include "math/mBox.h" +#endif + + +class LightManager; +class LightInfo; + + +/// Used to gather an score lights for rendering. +class LightQuery +{ +public: + + LightQuery( U32 maxLights = 4 ); + ~LightQuery(); + + /// Set the query volume from a camera position and direction. + void init( const Point3F &cameraPos, + const Point3F &cameraDir, + F32 viewDist ); + + /// Set the query volume from a sphere. + void init( const SphereF &bounds ); + + /// Set the query volume from a box. + void init( const Box3F &bounds ); + + /// This returns the best lights based on the query volume. + void getLights( LightInfo** outLights, U32 maxLights ); + +protected: + + void _scoreLights(); + + static S32 _lightScoreCmp( LightInfo* const *a, LightInfo* const *b ); + + /// The maximum lights to return from the query. + const U32 mMaxLights; + + /// The sorted list of best lights. + Vector mLights; + + /// The sphere used to query for lights. + SphereF mVolume; +}; + + +#endif // _LIGHTQUERY_H_ diff --git a/Engine/source/lighting/lightingInterfaces.cpp b/Engine/source/lighting/lightingInterfaces.cpp new file mode 100644 index 000000000..13ab76137 --- /dev/null +++ b/Engine/source/lighting/lightingInterfaces.cpp @@ -0,0 +1,51 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/lightingInterfaces.h" + + +void AvailableSLInterfaces::registerSystem(SceneLightingInterface* si) +{ + mAvailableSystemInterfaces.push_back(si); + mDirty = true; +} + +void AvailableSLInterfaces::initInterfaces() +{ + if ( !mDirty ) + return; + + mAvailableObjectTypes = mClippingMask = mZoneLightSkipMask = 0; + + SceneLightingInterface** sitr = mAvailableSystemInterfaces.begin(); + for ( ; sitr != mAvailableSystemInterfaces.end(); sitr++ ) + { + SceneLightingInterface* si = (*sitr); + si->init(); + mAvailableObjectTypes |= si->addObjectType(); + mClippingMask |= si->addToClippingMask(); + mZoneLightSkipMask |= si->addToZoneLightSkipMask(); + } + + mDirty = false; +} \ No newline at end of file diff --git a/Engine/source/lighting/lightingInterfaces.h b/Engine/source/lighting/lightingInterfaces.h new file mode 100644 index 000000000..54f8f7aea --- /dev/null +++ b/Engine/source/lighting/lightingInterfaces.h @@ -0,0 +1,123 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _SG_SYSTEM_INTERFACE_H +#define _SG_SYSTEM_INTERFACE_H + +#ifndef _SGSCENEPERSIST_H_ +#include "lighting/common/scenePersist.h" +#endif + +#ifndef _SCENELIGHTING_H_ +#include "lighting/common/sceneLighting.h" +#endif + +class ObjectProxy; +class ObjectProxyList; +class SceneLightingInterface; + +template class Vector; +typedef Vector SceneLightingInterfaces; + +// List of available "systems" that the lighting kit can use +class AvailableSLInterfaces +{ +protected: + + bool mDirty; + +public: + AvailableSLInterfaces() + : mAvailableObjectTypes( 0 ), + mClippingMask( 0 ), + mZoneLightSkipMask( 0 ), + mDirty( true ) + { + VECTOR_SET_ASSOCIATION( mAvailableSystemInterfaces ); + } + + // Register a system + void registerSystem(SceneLightingInterface* si); + + // Init the interfaces + void initInterfaces(); + + // The actual list of SceneLightingInterfaces + SceneLightingInterfaces mAvailableSystemInterfaces; + + // Object types that are registered with the system + U32 mAvailableObjectTypes; + + // Clipping typemask + U32 mClippingMask; + + // Object types that we should skip zone lighting for + U32 mZoneLightSkipMask; +}; + +// This object is responsible for returning PersistChunk and ObjectProxy classes for the lighting system to use +// We may want to eventually split this into scene lighting vs. dynamic lighting. getColorFromRayInfo is a dynamic +// lighting thing. +class SceneLightingInterface +{ +public: + SceneLightingInterface() + { + } + virtual ~SceneLightingInterface() { } + + virtual void init() { } + + // + // Scene lighting methods + // + // Creates an object proxy for obj + virtual SceneLighting::ObjectProxy* createObjectProxy(SceneObject* obj, SceneLighting::ObjectProxyList* sceneObjects) = 0; + + // Creates a PersistChunk based on the chunkType flag + virtual PersistInfo::PersistChunk* createPersistChunk(const U32 chunkType) = 0; + + // Creates a PersistChunk if needed for a proxy, returns true if it's "handled" by the system and ret contains the PersistChunk if needed. + virtual bool createPersistChunkFromProxy(SceneLighting::ObjectProxy* proxy, PersistInfo::PersistChunk** ret) = 0; + + // Returns which object type flag this system supports (used to query scene graph for objects to light) + virtual U32 addObjectType() = 0; + + // Add an object type flag to the "allow clipping mask" (used for blob shadows) + virtual U32 addToClippingMask() { return 0; } + + // Add an object type flag to skip zone lighting + virtual U32 addToZoneLightSkipMask() { return 0; } + + // Allows for processing/validating of the scene list after loading cached persistant info, return false if a relight is required or true if the data looks good. + virtual bool postProcessLoad(PersistInfo* pi, SceneLighting::ObjectProxyList* sceneObjects) { return true; } + + virtual void processLightingBegin() { } + virtual void processLightingCompleted(bool success) { } + + // + // Runtime / dynamic methods + // + // Given a ray, this will return the color from the lightmap of this object, return true if handled + virtual bool getColorFromRayInfo(const RayInfo & collision, ColorF& result) const { return false; } +}; + +#endif diff --git a/Engine/source/lighting/shadowManager.cpp b/Engine/source/lighting/shadowManager.cpp new file mode 100644 index 000000000..7c353f2ab --- /dev/null +++ b/Engine/source/lighting/shadowManager.cpp @@ -0,0 +1,79 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/shadowManager.h" + +#include "scene/sceneManager.h" +#include "materials/materialManager.h" + +const String ShadowManager::ManagerTypeName("ShadowManager"); + +//------------------------------------------------------------------------------ + +bool ShadowManager::canActivate() +{ + return true; +} + +//------------------------------------------------------------------------------ + +void ShadowManager::activate() +{ + mSceneManager = gClientSceneGraph; //;getWorld()->findWorldManager(); +} + +//------------------------------------------------------------------------------ + +SceneManager* ShadowManager::getSceneManager() +{ + return mSceneManager; +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +// Runtime switching of shadow systems. Requires correct world to be pushed at console. +ConsoleFunction( setShadowManager, bool, 1, 3, "string sShadowSystemName" ) +{ + /* + // Make sure this new one exists + ShadowManager * newSM = dynamic_cast(ConsoleObject::create(argv[1])); + if (!newSM) + return false; + + // Cleanup current + ShadowManager * currentSM = world->findWorldManager(); + if (currentSM) + { + currentSM->deactivate(); + world->removeWorldManager(currentSM); + delete currentSM; + } + + // Add to world and init. + world->addWorldManager(newSM); + newSM->activate(); + MaterialManager::get()->reInitInstances(); + */ + return true; +} \ No newline at end of file diff --git a/Engine/source/lighting/shadowManager.h b/Engine/source/lighting/shadowManager.h new file mode 100644 index 000000000..74aa308bc --- /dev/null +++ b/Engine/source/lighting/shadowManager.h @@ -0,0 +1,66 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SHADOWMANAGER_H_ +#define _SHADOWMANAGER_H_ + +#ifndef _TSIGNAL_H_ +#include "core/util/tSignal.h" +#endif +#ifndef _TORQUE_STRING_H_ +#include "core/util/str.h" +#endif + +class SceneManager; + +class ShadowManager +{ +public: + ShadowManager() : mSceneManager(NULL) {} + virtual ~ShadowManager() { } + + // Called when the shadow manager should become active + virtual void activate(); + + // Called when we don't want the shadow manager active (should clean up) + virtual void deactivate() { } + + // Return an "id" that other systems can use to load different versions of assets (custom shaders, etc.) + // Should be short and contain no spaces and safe for filename use. + //virtual const char* getId() const = 0; + + // SceneManager manager + virtual SceneManager* getSceneManager(); + + // Called to find out if it is valid to activate this shadow system. If not, we should print out + // a console warning explaining why. + virtual bool canActivate(); + + // SimWorldManager + static const String ManagerTypeName; + const String & getManagerTypeName() const { return ManagerTypeName; } + +private: + SceneManager* mSceneManager; +}; + +#endif // _SHADOWMANAGER_H_ diff --git a/Engine/source/lighting/shadowMap/cubeLightShadowMap.cpp b/Engine/source/lighting/shadowMap/cubeLightShadowMap.cpp new file mode 100644 index 000000000..5ab65b357 --- /dev/null +++ b/Engine/source/lighting/shadowMap/cubeLightShadowMap.cpp @@ -0,0 +1,202 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/shadowMap/cubeLightShadowMap.h" + +#include "lighting/shadowMap/shadowMapManager.h" +#include "lighting/common/lightMapParams.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/gfxDebugEvent.h" +#include "renderInstance/renderPassManager.h" +#include "materials/materialDefinition.h" +#include "gfx/util/gfxFrustumSaver.h" +#include "math/mathUtils.h" + + +CubeLightShadowMap::CubeLightShadowMap( LightInfo *light ) + : Parent( light ) +{ +} + +bool CubeLightShadowMap::setTextureStage( U32 currTexFlag, LightingShaderConstants* lsc ) +{ + if ( currTexFlag == Material::DynamicLight ) + { + S32 reg = lsc->mShadowMapSC->getSamplerRegister(); + if ( reg != -1 ) + GFX->setCubeTexture( reg, mCubemap ); + + return true; + } + + return false; +} + +void CubeLightShadowMap::setShaderParameters( GFXShaderConstBuffer *params, + LightingShaderConstants *lsc ) +{ + if ( lsc->mTapRotationTexSC->isValid() ) + GFX->setTexture( lsc->mTapRotationTexSC->getSamplerRegister(), + SHADOWMGR->getTapRotationTex() ); + + ShadowMapParams *p = mLight->getExtended(); + + if ( lsc->mLightParamsSC->isValid() ) + { + Point4F lightParams( mLight->getRange().x, + p->overDarkFactor.x, + 0.0f, + 0.0f ); + params->set(lsc->mLightParamsSC, lightParams); + } + + // The softness is a factor of the texel size. + params->setSafe( lsc->mShadowSoftnessConst, p->shadowSoftness * ( 1.0f / mTexSize ) ); +} + +void CubeLightShadowMap::releaseTextures() +{ + Parent::releaseTextures(); + mCubemap = NULL; +} + +void CubeLightShadowMap::_render( RenderPassManager* renderPass, + const SceneRenderState *diffuseState ) +{ + PROFILE_SCOPE( CubeLightShadowMap_Render ); + + const LightMapParams *lmParams = mLight->getExtended(); + const bool bUseLightmappedGeometry = lmParams ? !lmParams->representedInLightmap || lmParams->includeLightmappedGeometryInShadow : true; + + const U32 texSize = getBestTexSize(); + + if ( mCubemap.isNull() || + mTexSize != texSize ) + { + mTexSize = texSize; + mCubemap = GFX->createCubemap(); + mCubemap->initDynamic( mTexSize, LightShadowMap::ShadowMapFormat ); + } + + // Setup the world to light projection which is used + // in the shader to transform the light vector for the + // shadow lookup. + mWorldToLightProj = mLight->getTransform(); + mWorldToLightProj.inverse(); + + // Set up frustum and visible distance + GFXFrustumSaver fsaver; + GFXTransformSaver saver; + { + F32 left, right, top, bottom; + MathUtils::makeFrustum( &left, &right, &top, &bottom, M_HALFPI_F, 1.0f, 0.1f ); + GFX->setFrustum( left, right, bottom, top, 0.1f, mLight->getRange().x ); + } + + // Render the shadowmap! + GFX->pushActiveRenderTarget(); + + for( U32 i = 0; i < 6; i++ ) + { + // Standard view that will be overridden below. + VectorF vLookatPt(0.0f, 0.0f, 0.0f), vUpVec(0.0f, 0.0f, 0.0f), vRight(0.0f, 0.0f, 0.0f); + + switch( i ) + { + case 0 : // D3DCUBEMAP_FACE_POSITIVE_X: + vLookatPt = VectorF(1.0f, 0.0f, 0.0f); + vUpVec = VectorF(0.0f, 1.0f, 0.0f); + break; + case 1 : // D3DCUBEMAP_FACE_NEGATIVE_X: + vLookatPt = VectorF(-1.0f, 0.0f, 0.0f); + vUpVec = VectorF(0.0f, 1.0f, 0.0f); + break; + case 2 : // D3DCUBEMAP_FACE_POSITIVE_Y: + vLookatPt = VectorF(0.0f, 1.0f, 0.0f); + vUpVec = VectorF(0.0f, 0.0f,-1.0f); + break; + case 3 : // D3DCUBEMAP_FACE_NEGATIVE_Y: + vLookatPt = VectorF(0.0f, -1.0f, 0.0f); + vUpVec = VectorF(0.0f, 0.0f, 1.0f); + break; + case 4 : // D3DCUBEMAP_FACE_POSITIVE_Z: + vLookatPt = VectorF(0.0f, 0.0f, 1.0f); + vUpVec = VectorF(0.0f, 1.0f, 0.0f); + break; + case 5: // D3DCUBEMAP_FACE_NEGATIVE_Z: + vLookatPt = VectorF(0.0f, 0.0f, -1.0f); + vUpVec = VectorF(0.0f, 1.0f, 0.0f); + break; + } + + GFXDEBUGEVENT_START( CubeLightShadowMap_Render_Face, ColorI::RED ); + + // create camera matrix + VectorF cross = mCross(vUpVec, vLookatPt); + cross.normalizeSafe(); + + MatrixF lightMatrix(true); + lightMatrix.setColumn(0, cross); + lightMatrix.setColumn(1, vLookatPt); + lightMatrix.setColumn(2, vUpVec); + lightMatrix.setPosition( mLight->getPosition() ); + lightMatrix.inverse(); + + GFX->setWorldMatrix( lightMatrix ); + + mTarget->attachTexture(GFXTextureTarget::Color0, mCubemap, i); + mTarget->attachTexture(GFXTextureTarget::DepthStencil, _getDepthTarget( mTexSize, mTexSize )); + GFX->setActiveRenderTarget(mTarget); + GFX->clear( GFXClearTarget | GFXClearStencil | GFXClearZBuffer, ColorI(255,255,255,255), 1.0f, 0 ); + + // Create scene state, prep it + SceneManager* sceneManager = diffuseState->getSceneManager(); + + SceneRenderState shadowRenderState + ( + sceneManager, + SPT_Shadow, + SceneCameraState::fromGFXWithViewport( diffuseState->getViewport() ), + renderPass + ); + + shadowRenderState.getMaterialDelegate().bind( this, &LightShadowMap::getShadowMaterial ); + shadowRenderState.renderNonLightmappedMeshes( true ); + shadowRenderState.renderLightmappedMeshes( bUseLightmappedGeometry ); + shadowRenderState.setDiffuseCameraTransform( diffuseState->getCameraTransform() ); + shadowRenderState.setWorldToScreenScale( diffuseState->getWorldToScreenScale() ); + + sceneManager->renderSceneNoLights( &shadowRenderState, SHADOW_TYPEMASK ); + + _debugRender( &shadowRenderState ); + + // Resolve this face + mTarget->resolve(); + + GFXDEBUGEVENT_END(); + } + GFX->popActiveRenderTarget(); +} diff --git a/Engine/source/lighting/shadowMap/cubeLightShadowMap.h b/Engine/source/lighting/shadowMap/cubeLightShadowMap.h new file mode 100644 index 000000000..5b30a8d89 --- /dev/null +++ b/Engine/source/lighting/shadowMap/cubeLightShadowMap.h @@ -0,0 +1,57 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _CUBELIGHTSHADOWMAP_H_ +#define _CUBELIGHTSHADOWMAP_H_ + +#ifndef _LIGHTSHADOWMAP_H_ +#include "lighting/shadowMap/lightShadowMap.h" +#endif +#ifndef _GFXCUBEMAP_H_ +#include "gfx/gfxCubemap.h" +#endif + + +class CubeLightShadowMap : public LightShadowMap +{ + typedef LightShadowMap Parent; + +public: + + CubeLightShadowMap( LightInfo *light ); + + // LightShadowMap + virtual bool hasShadowTex() const { return mCubemap.isValid(); } + virtual ShadowType getShadowType() const { return ShadowType_CubeMap; } + virtual void _render( RenderPassManager* renderPass, const SceneRenderState *diffuseState ); + virtual void setShaderParameters( GFXShaderConstBuffer* params, LightingShaderConstants* lsc ); + virtual void releaseTextures(); + virtual bool setTextureStage( U32 currTexFlag, LightingShaderConstants* lsc ); + +protected: + + /// The shadow cubemap. + GFXCubemapHandle mCubemap; + +}; + +#endif // _CUBELIGHTSHADOWMAP_H_ diff --git a/Engine/source/lighting/shadowMap/dualParaboloidLightShadowMap.cpp b/Engine/source/lighting/shadowMap/dualParaboloidLightShadowMap.cpp new file mode 100644 index 000000000..39072b0f4 --- /dev/null +++ b/Engine/source/lighting/shadowMap/dualParaboloidLightShadowMap.cpp @@ -0,0 +1,187 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/shadowMap/dualParaboloidLightShadowMap.h" +#include "lighting/common/lightMapParams.h" +#include "lighting/shadowMap/shadowMapManager.h" +#include "math/mathUtils.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "gfx/gfxDebugEvent.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/util/gfxFrustumSaver.h" +#include "renderInstance/renderPassManager.h" +#include "materials/materialDefinition.h" +#include "math/util/matrixSet.h" + +DualParaboloidLightShadowMap::DualParaboloidLightShadowMap( LightInfo *light ) + : Parent( light ) +{ +} + +void DualParaboloidLightShadowMap::_render( RenderPassManager* renderPass, + const SceneRenderState *diffuseState ) +{ + PROFILE_SCOPE(DualParaboloidLightShadowMap_render); + + const ShadowMapParams *p = mLight->getExtended(); + const LightMapParams *lmParams = mLight->getExtended(); + const bool bUseLightmappedGeometry = lmParams ? !lmParams->representedInLightmap || lmParams->includeLightmappedGeometryInShadow : true; + + const U32 texSize = getBestTexSize( 2 ); + + if ( mShadowMapTex.isNull() || + mTexSize != texSize ) + { + mTexSize = texSize; + + mShadowMapTex.set( mTexSize * 2, mTexSize, + ShadowMapFormat, &ShadowMapProfile, + "DualParaboloidLightShadowMap" ); + } + + GFXFrustumSaver frustSaver; + GFXTransformSaver saver; + + // Set and Clear target + GFX->pushActiveRenderTarget(); + + mTarget->attachTexture(GFXTextureTarget::Color0, mShadowMapTex); + mTarget->attachTexture( GFXTextureTarget::DepthStencil, + _getDepthTarget( mShadowMapTex->getWidth(), mShadowMapTex->getHeight() ) ); + GFX->setActiveRenderTarget(mTarget); + GFX->clear(GFXClearTarget | GFXClearStencil | GFXClearZBuffer, ColorI::WHITE, 1.0f, 0); + + const bool bUseSinglePassDPM = (p->shadowType == ShadowType_DualParaboloidSinglePass); + + // Set up matrix and visible distance + mWorldToLightProj = mLight->getTransform(); + mWorldToLightProj.inverse(); + + const F32 &lightRadius = mLight->getRange().x; + const F32 paraboloidNearPlane = 0.01f; + const F32 renderPosOffset = 0.01f; + + // Alter for creation of scene state if this is a single pass map + if(bUseSinglePassDPM) + { + VectorF camDir; + MatrixF temp = mLight->getTransform(); + temp.getColumn(1, &camDir); + temp.setPosition(mLight->getPosition() - camDir * (lightRadius + renderPosOffset)); + temp.inverse(); + GFX->setWorldMatrix(temp); + GFX->setOrtho(-lightRadius, lightRadius, -lightRadius, lightRadius, paraboloidNearPlane, 2.0f * lightRadius, true); + } + else + { + VectorF camDir; + MatrixF temp = mLight->getTransform(); + temp.getColumn(1, &camDir); + temp.setPosition(mLight->getPosition() - camDir * renderPosOffset); + temp.inverse(); + GFX->setWorldMatrix(temp); + + GFX->setOrtho(-lightRadius, lightRadius, -lightRadius, lightRadius, paraboloidNearPlane, lightRadius, true); + } + + SceneManager* sceneManager = diffuseState->getSceneManager(); + + // Front map render + { + SceneRenderState frontMapRenderState + ( + sceneManager, + SPT_Shadow, + SceneCameraState::fromGFXWithViewport( diffuseState->getViewport() ), + renderPass + ); + + frontMapRenderState.getMaterialDelegate().bind( this, &LightShadowMap::getShadowMaterial ); + frontMapRenderState.renderNonLightmappedMeshes( true ); + frontMapRenderState.renderLightmappedMeshes( bUseLightmappedGeometry ); + frontMapRenderState.setDiffuseCameraTransform( diffuseState->getCameraTransform() ); + frontMapRenderState.setWorldToScreenScale( diffuseState->getWorldToScreenScale() ); + + if(bUseSinglePassDPM) + { + GFX->setWorldMatrix(mWorldToLightProj); + frontMapRenderState.getRenderPass()->getMatrixSet().setSceneView(mWorldToLightProj); + GFX->setOrtho(-lightRadius, lightRadius, -lightRadius, lightRadius, paraboloidNearPlane, lightRadius, true); + } + + GFXDEBUGEVENT_SCOPE( DualParaboloidLightShadowMap_Render_FrontFacingParaboloid, ColorI::RED ); + mShadowMapScale.set(0.5f, 1.0f); + mShadowMapOffset.set(-0.5f, 0.0f); + sceneManager->renderSceneNoLights( &frontMapRenderState, SHADOW_TYPEMASK ); + _debugRender( &frontMapRenderState ); + } + + // Back map render + if(!bUseSinglePassDPM) + { + GFXDEBUGEVENT_SCOPE( DualParaboloidLightShadowMap_Render_BackFacingParaboloid, ColorI::RED ); + + mShadowMapScale.set(0.5f, 1.0f); + mShadowMapOffset.set(0.5f, 0.0f); + + // Invert direction on camera matrix + VectorF right, forward; + MatrixF temp = mLight->getTransform(); + temp.getColumn( 1, &forward ); + temp.getColumn( 0, &right ); + forward *= -1.0f; + right *= -1.0f; + temp.setColumn( 1, forward ); + temp.setColumn( 0, right ); + temp.setPosition(mLight->getPosition() - forward * -renderPosOffset); + temp.inverse(); + GFX->setWorldMatrix(temp); + + // Create an inverted scene state for the back-map + + SceneRenderState backMapRenderState + ( + sceneManager, + SPT_Shadow, + SceneCameraState::fromGFXWithViewport( diffuseState->getViewport() ), + renderPass + ); + + backMapRenderState.getMaterialDelegate().bind( this, &LightShadowMap::getShadowMaterial ); + backMapRenderState.renderNonLightmappedMeshes( true ); + backMapRenderState.renderLightmappedMeshes( bUseLightmappedGeometry ); + backMapRenderState.setDiffuseCameraTransform( diffuseState->getCameraTransform() ); + backMapRenderState.setWorldToScreenScale( diffuseState->getWorldToScreenScale() ); + + backMapRenderState.getRenderPass()->getMatrixSet().setSceneView(temp); + + // Draw scene + sceneManager->renderSceneNoLights( &backMapRenderState ); + _debugRender( &backMapRenderState ); + } + + mTarget->resolve(); + GFX->popActiveRenderTarget(); +} diff --git a/Engine/source/lighting/shadowMap/dualParaboloidLightShadowMap.h b/Engine/source/lighting/shadowMap/dualParaboloidLightShadowMap.h new file mode 100644 index 000000000..0564c6451 --- /dev/null +++ b/Engine/source/lighting/shadowMap/dualParaboloidLightShadowMap.h @@ -0,0 +1,40 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _DUALPARABOLOIDLIGHTSHADOWMAP_H_ +#define _DUALPARABOLOIDLIGHTSHADOWMAP_H_ + +#ifndef _PARABOLOIDLIGHTSHADOWMAP_H_ +#include "lighting/shadowMap/paraboloidLightShadowMap.h" +#endif + +class DualParaboloidLightShadowMap : public ParaboloidLightShadowMap +{ + typedef ParaboloidLightShadowMap Parent; + +public: + DualParaboloidLightShadowMap( LightInfo *light ); + + virtual void _render( RenderPassManager* renderPass, const SceneRenderState *diffuseState ); +}; + +#endif // _DUALPARABOLOIDLIGHTSHADOWMAP_H_ \ No newline at end of file diff --git a/Engine/source/lighting/shadowMap/lightShadowMap.cpp b/Engine/source/lighting/shadowMap/lightShadowMap.cpp new file mode 100644 index 000000000..7e7e03242 --- /dev/null +++ b/Engine/source/lighting/shadowMap/lightShadowMap.cpp @@ -0,0 +1,752 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/shadowMap/lightShadowMap.h" + +#include "lighting/shadowMap/shadowMapManager.h" +#include "lighting/shadowMap/shadowMatHook.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxTextureManager.h" +#include "gfx/gfxOcclusionQuery.h" +#include "gfx/gfxCardProfile.h" +#include "gfx/sim/debugDraw.h" +#include "materials/materialDefinition.h" +#include "materials/baseMatInstance.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "scene/zones/SceneZoneSpace.h" +#include "lighting/lightManager.h" +#include "math/mathUtils.h" +#include "shaderGen/shaderGenVars.h" +#include "core/util/safeDelete.h" +#include "core/stream/bitStream.h" +#include "math/mathIO.h" +#include "materials/shaderData.h" + +// Used for creation in ShadowMapParams::getOrCreateShadowMap() +#include "lighting/shadowMap/singleLightShadowMap.h" +#include "lighting/shadowMap/pssmLightShadowMap.h" +#include "lighting/shadowMap/cubeLightShadowMap.h" +#include "lighting/shadowMap/dualParaboloidLightShadowMap.h" + +// Remove this when the shader constants are reworked better +#include "lighting/advanced/advancedLightManager.h" +#include "lighting/advanced/advancedLightBinManager.h" + +// TODO: Some cards (Justin's GeForce 7x series) barf on the integer format causing +// filtering artifacts. These can (sometimes) be resolved by switching the format +// to FP16 instead of Int16. +const GFXFormat LightShadowMap::ShadowMapFormat = GFXFormatR32F; // GFXFormatR8G8B8A8; + +bool LightShadowMap::smDebugRenderFrustums; +F32 LightShadowMap::smShadowTexScalar = 1.0f; + +Vector LightShadowMap::smUsedShadowMaps; +Vector LightShadowMap::smShadowMaps; + +GFX_ImplementTextureProfile( ShadowMapProfile, + GFXTextureProfile::DiffuseMap, + GFXTextureProfile::PreserveSize | + GFXTextureProfile::RenderTarget | + GFXTextureProfile::Pooled, + GFXTextureProfile::None ); + +GFX_ImplementTextureProfile( ShadowMapZProfile, + GFXTextureProfile::DiffuseMap, + GFXTextureProfile::PreserveSize | + GFXTextureProfile::NoMipmap | + GFXTextureProfile::ZTarget | + GFXTextureProfile::Pooled, + GFXTextureProfile::None ); + + +LightShadowMap::LightShadowMap( LightInfo *light ) + : mWorldToLightProj( true ), + mLight( light ), + mTexSize( 0 ), + mLastShader( NULL ), + mLastUpdate( 0 ), + mLastCull( 0 ), + mIsViewDependent( false ), + mVizQuery( NULL ), + mWasOccluded( false ), + mLastScreenSize( 0.0f ), + mLastPriority( 0.0f ) +{ + GFXTextureManager::addEventDelegate( this, &LightShadowMap::_onTextureEvent ); + + mTarget = GFX->allocRenderToTextureTarget(); + mVizQuery = GFX->createOcclusionQuery(); + + smShadowMaps.push_back( this ); +} + +LightShadowMap::~LightShadowMap() +{ + mTarget = NULL; + SAFE_DELETE( mVizQuery ); + + releaseTextures(); + + smShadowMaps.remove( this ); + smUsedShadowMaps.remove( this ); + + GFXTextureManager::removeEventDelegate( this, &LightShadowMap::_onTextureEvent ); +} + +void LightShadowMap::releaseAllTextures() +{ + PROFILE_SCOPE( LightShadowMap_ReleaseAllTextures ); + + for ( U32 i=0; i < smShadowMaps.size(); i++ ) + smShadowMaps[i]->releaseTextures(); +} + +U32 LightShadowMap::releaseUnusedTextures() +{ + PROFILE_SCOPE( LightShadowMap_ReleaseUnusedTextures ); + + const U32 currTime = Sim::getCurrentTime(); + const U32 purgeTime = 1000; + + for ( U32 i=0; i < smUsedShadowMaps.size(); ) + { + LightShadowMap *lsm = smUsedShadowMaps[i]; + + // If the shadow has not been culled in a while then + // release its textures for other shadows to use. + if ( currTime > ( lsm->mLastCull + purgeTime ) ) + { + // Internally this will remove the map from the used + // list, so don't increment the loop. + lsm->releaseTextures(); + continue; + } + + i++; + } + + return smUsedShadowMaps.size(); +} + +void LightShadowMap::_onTextureEvent( GFXTexCallbackCode code ) +{ + if ( code == GFXZombify ) + releaseTextures(); + + // We don't initialize here as we want the textures + // to be reallocated when the shadow becomes visible. +} + +void LightShadowMap::calcLightMatrices( MatrixF &outLightMatrix, const Frustum &viewFrustum ) +{ + // Create light matrix, set projection + + switch ( mLight->getType() ) + { + case LightInfo::Vector : + { + const ShadowMapParams *p = mLight->getExtended(); + + // Calculate the bonding box of the shadowed area + // we're interested in... this is the shadow box + // transformed by the frustum transform. + Box3F viewBB( -p->shadowDistance, -p->shadowDistance, -p->shadowDistance, + p->shadowDistance, p->shadowDistance, p->shadowDistance ); + viewFrustum.getTransform().mul( viewBB ); + + // Calculate a light "projection" matrix. + MatrixF lightMatrix = MathUtils::createOrientFromDir(mLight->getDirection()); + outLightMatrix = lightMatrix; + static MatrixF rotMat(EulerF( (M_PI_F / 2.0f), 0.0f, 0.0f)); + lightMatrix.mul( rotMat ); + + // This is the box in lightspace + Box3F lightViewBB(viewBB); + lightMatrix.mul(lightViewBB); + + // Now, let's position our light based on the lightViewBB + Point3F newLightPos(viewBB.getCenter()); + F32 sceneDepth = lightViewBB.maxExtents.z - lightViewBB.minExtents.z; + newLightPos += mLight->getDirection() * ((-sceneDepth / 2.0f)-1.0f); // -1 for the nearplane + outLightMatrix.setPosition(newLightPos); + + // Update light info + mLight->setRange( sceneDepth ); + mLight->setPosition( newLightPos ); + + // Set our ortho projection + F32 width = (lightViewBB.maxExtents.x - lightViewBB.minExtents.x) / 2.0f; + F32 height = (lightViewBB.maxExtents.y - lightViewBB.minExtents.y) / 2.0f; + + width = getMax(width, height); + + GFX->setOrtho(-width, width, -width, width, 1.0f, sceneDepth, true); + + + // TODO: Width * 2... really isn't that pixels being used as + // meters? Is a real physical metric of scene depth better? + //SceneManager::setVisibleDistance(width * 2.0f); + +#if 0 + DebugDrawer::get()->drawFrustum(viewFrustum, ColorF(1.0f, 0.0f, 0.0f)); + DebugDrawer::get()->drawBox(viewBB.minExtents, viewBB.maxExtents, ColorF(0.0f, 1.0f, 0.0f)); + DebugDrawer::get()->drawBox(lightViewBB.minExtents, lightViewBB.maxExtents, ColorF(0.0f, 0.0f, 1.0f)); + DebugDrawer::get()->drawBox(newLightPos - Point3F(1,1,1), newLightPos + Point3F(1,1,1), ColorF(1,1,0)); + DebugDrawer::get()->drawLine(newLightPos, newLightPos + mLight.mDirection*3.0f, ColorF(0,1,1)); + + Point3F a(newLightPos); + Point3F b(newLightPos); + Point3F offset(width, height,0.0f); + a -= offset; + b += offset; + DebugDrawer::get()->drawBox(a, b, ColorF(0.5f, 0.5f, 0.5f)); +#endif + } + break; + case LightInfo::Spot : + { + outLightMatrix = mLight->getTransform(); + F32 fov = mDegToRad( mLight->getOuterConeAngle() ); + F32 farDist = mLight->getRange().x; + F32 nearDist = farDist * 0.01f; + + F32 left, right, top, bottom; + MathUtils::makeFrustum( &left, &right, &top, &bottom, fov, 1.0f, nearDist ); + GFX->setFrustum( left, right, bottom, top, nearDist, farDist ); + } + break; + default: + AssertFatal(false, "Unsupported light type!"); + } +} + +void LightShadowMap::releaseTextures() +{ + mShadowMapTex = NULL; + mDebugTarget.setTexture( NULL ); + mLastUpdate = 0; + smUsedShadowMaps.remove( this ); +} + +void LightShadowMap::setDebugTarget( const String &name ) +{ + mDebugTarget.registerWithName( name ); + mDebugTarget.setTexture( mShadowMapTex ); +} + +GFXTextureObject* LightShadowMap::_getDepthTarget( U32 width, U32 height ) +{ + // Get a depth texture target from the pooled profile + // which is returned as a temporary. + GFXTexHandle depthTex( width, height, GFXFormatD24S8, &ShadowMapZProfile, + "LightShadowMap::_getDepthTarget()" ); + + return depthTex; +} + +bool LightShadowMap::setTextureStage( U32 currTexFlag, LightingShaderConstants* lsc ) +{ + if ( currTexFlag == Material::DynamicLight ) + { + S32 reg = lsc->mShadowMapSC->getSamplerRegister(); + if ( reg != -1 ) + GFX->setTexture( reg, mShadowMapTex); + + return true; + } + else if ( currTexFlag == Material::DynamicLightMask ) + { + S32 reg = lsc->mCookieMapSC->getSamplerRegister(); + if ( reg != -1 ) + { + ShadowMapParams *p = mLight->getExtended(); + + if ( lsc->mCookieMapSC->getType() == GFXSCT_SamplerCube ) + GFX->setCubeTexture( reg, p->getCookieCubeTex() ); + else + GFX->setTexture( reg, p->getCookieTex() ); + } + + return true; + } + + return false; +} + +void LightShadowMap::render( RenderPassManager* renderPass, + const SceneRenderState *diffuseState ) +{ + mDebugTarget.setTexture( NULL ); + _render( renderPass, diffuseState ); + mDebugTarget.setTexture( mShadowMapTex ); + + // Add it to the used list unless we're been updated. + if ( !mLastUpdate ) + { + AssertFatal( !smUsedShadowMaps.contains( this ), "LightShadowMap::render - Used shadow map inserted twice!" ); + smUsedShadowMaps.push_back( this ); + } + + mLastUpdate = Sim::getCurrentTime(); +} + +void LightShadowMap::preLightRender() +{ + PROFILE_SCOPE( LightShadowMap_prepLightRender ); + + if ( mVizQuery ) + { + mWasOccluded = mVizQuery->getStatus( true ) == GFXOcclusionQuery::Occluded; + mVizQuery->begin(); + } +} + +void LightShadowMap::postLightRender() +{ + if ( mVizQuery ) + mVizQuery->end(); +} + +BaseMatInstance* LightShadowMap::getShadowMaterial( BaseMatInstance *inMat ) const +{ + // See if we have an existing material hook. + ShadowMaterialHook *hook = static_cast( inMat->getHook( ShadowMaterialHook::Type ) ); + if ( !hook ) + { + // Create a hook and initialize it using the incoming material. + hook = new ShadowMaterialHook; + hook->init( inMat ); + inMat->addHook( hook ); + } + + return hook->getShadowMat( getShadowType() ); +} + +U32 LightShadowMap::getBestTexSize( U32 scale ) const +{ + const ShadowMapParams *params = mLight->getExtended(); + + // The view dependent shadows don't scale by screen size. + U32 texSize; + if ( isViewDependent() ) + texSize = params->texSize; + else + texSize = params->texSize * getMin( 1.0f, mLastScreenSize ); + + // Apply the shadow texture scale and make + // sure this is a power of 2. + texSize = getNextPow2( texSize * smShadowTexScalar ); + + // Get the max texture size this card supports and + // scale it down... ensuring the final texSize can + // be scaled up that many times and not go over + // the card maximum. + U32 maxTexSize = GFX->getCardProfiler()->queryProfile( "maxTextureSize", 2048 ); + if ( scale > 1 ) + maxTexSize >>= ( scale - 1 ); + + // Never let the shadow texture get smaller than 16x16 as + // it just makes the pool bigger and the fillrate savings + // are less and leass as we get smaller. + texSize = mClamp( texSize, (U32)16, maxTexSize ); + + // Return it. + return texSize; +} + +void LightShadowMap::updatePriority( const SceneRenderState *state, U32 currTimeMs ) +{ + PROFILE_SCOPE( LightShadowMap_updatePriority ); + + mLastCull = currTimeMs; + + if ( isViewDependent() ) + { + mLastScreenSize = 1.0f; + mLastPriority = F32_MAX; + return; + } + + U32 timeSinceLastUpdate = currTimeMs - mLastUpdate; + + const Point3F &camPt = state->getCameraPosition(); + F32 range = mLight->getRange().x; + F32 dist; + + if ( mLight->getType() == LightInfo::Spot ) + { + // We treat the cone as a cylinder to get the + // approximate projection distance. + + Point3F endPt = mLight->getPosition() + ( mLight->getDirection() * range ); + Point3F nearPt = MathUtils::mClosestPointOnSegment( mLight->getPosition(), endPt, camPt ); + dist = ( camPt - nearPt ).len(); + + F32 radius = range * mSin( mDegToRad( mLight->getOuterConeAngle() * 0.5f ) ); + dist -= radius; + } + else + dist = SphereF( mLight->getPosition(), range ).distanceTo( camPt ); + + // Get the approximate screen size of the light. + mLastScreenSize = state->projectRadius( dist, range ); + mLastScreenSize /= state->getViewport().extent.y; + + // Update the priority. + mLastPriority = mPow( mLastScreenSize * 50.0f, 2.0f ); + mLastPriority += timeSinceLastUpdate; + mLastPriority *= mLight->getPriority(); +} + +S32 QSORT_CALLBACK LightShadowMap::cmpPriority( LightShadowMap *const *lsm1, LightShadowMap *const *lsm2 ) +{ + F32 diff = (*lsm1)->getLastPriority() - (*lsm2)->getLastPriority(); + return diff > 0.0f ? -1 : ( diff < 0.0f ? 1 : 0 ); +} + +void LightShadowMap::_debugRender( SceneRenderState* shadowRenderState ) +{ + #ifdef TORQUE_DEBUG + + // Skip if light does not have debug rendering enabled. + if( !getLightInfo()->isDebugRenderingEnabled() ) + return; + + DebugDrawer* drawer = DebugDrawer::get(); + if( !drawer ) + return; + + if( smDebugRenderFrustums ) + shadowRenderState->getCullingState().debugRenderCullingVolumes(); + + #endif +} + + +LightingShaderConstants::LightingShaderConstants() + : mInit( false ), + mShader( NULL ), + mLightParamsSC(NULL), + mLightSpotParamsSC(NULL), + mLightPositionSC(NULL), + mLightDiffuseSC(NULL), + mLightAmbientSC(NULL), + mLightInvRadiusSqSC(NULL), + mLightSpotDirSC(NULL), + mLightSpotAngleSC(NULL), + mLightSpotFalloffSC(NULL), + mShadowMapSC(NULL), + mShadowMapSizeSC(NULL), + mCookieMapSC(NULL), + mRandomDirsConst(NULL), + mShadowSoftnessConst(NULL), + mWorldToLightProjSC(NULL), + mViewToLightProjSC(NULL), + mScaleXSC(NULL), + mScaleYSC(NULL), + mOffsetXSC(NULL), + mOffsetYSC(NULL), + mAtlasXOffsetSC(NULL), + mAtlasYOffsetSC(NULL), + mAtlasScaleSC(NULL), + mFadeStartLength(NULL), + mFarPlaneScalePSSM(NULL), + mOverDarkFactorPSSM(NULL), + mTapRotationTexSC(NULL) +{ +} + +LightingShaderConstants::~LightingShaderConstants() +{ + if (mShader.isValid()) + { + mShader->getReloadSignal().remove( this, &LightingShaderConstants::_onShaderReload ); + mShader = NULL; + } +} + +void LightingShaderConstants::init(GFXShader* shader) +{ + if (mShader.getPointer() != shader) + { + if (mShader.isValid()) + mShader->getReloadSignal().remove( this, &LightingShaderConstants::_onShaderReload ); + + mShader = shader; + mShader->getReloadSignal().notify( this, &LightingShaderConstants::_onShaderReload ); + } + + mLightParamsSC = shader->getShaderConstHandle("$lightParams"); + mLightSpotParamsSC = shader->getShaderConstHandle("$lightSpotParams"); + + // NOTE: These are the shader constants used for doing lighting + // during the forward pass. Do not confuse these for the prepass + // lighting constants which are used from AdvancedLightBinManager. + mLightPositionSC = shader->getShaderConstHandle( ShaderGenVars::lightPosition ); + mLightDiffuseSC = shader->getShaderConstHandle( ShaderGenVars::lightDiffuse ); + mLightAmbientSC = shader->getShaderConstHandle( ShaderGenVars::lightAmbient ); + mLightInvRadiusSqSC = shader->getShaderConstHandle( ShaderGenVars::lightInvRadiusSq ); + mLightSpotDirSC = shader->getShaderConstHandle( ShaderGenVars::lightSpotDir ); + mLightSpotAngleSC = shader->getShaderConstHandle( ShaderGenVars::lightSpotAngle ); + mLightSpotFalloffSC = shader->getShaderConstHandle( ShaderGenVars::lightSpotFalloff ); + + mShadowMapSC = shader->getShaderConstHandle("$shadowMap"); + mShadowMapSizeSC = shader->getShaderConstHandle("$shadowMapSize"); + + mCookieMapSC = shader->getShaderConstHandle("$cookieMap"); + + mShadowSoftnessConst = shader->getShaderConstHandle("$shadowSoftness"); + + mWorldToLightProjSC = shader->getShaderConstHandle("$worldToLightProj"); + mViewToLightProjSC = shader->getShaderConstHandle("$viewToLightProj"); + + mScaleXSC = shader->getShaderConstHandle("$scaleX"); + mScaleYSC = shader->getShaderConstHandle("$scaleY"); + mOffsetXSC = shader->getShaderConstHandle("$offsetX"); + mOffsetYSC = shader->getShaderConstHandle("$offsetY"); + mAtlasXOffsetSC = shader->getShaderConstHandle("$atlasXOffset"); + mAtlasYOffsetSC = shader->getShaderConstHandle("$atlasYOffset"); + mAtlasScaleSC = shader->getShaderConstHandle("$atlasScale"); + + mFadeStartLength = shader->getShaderConstHandle("$fadeStartLength"); + mFarPlaneScalePSSM = shader->getShaderConstHandle("$farPlaneScalePSSM"); + + mOverDarkFactorPSSM = shader->getShaderConstHandle("$overDarkPSSM"); + + mTapRotationTexSC = shader->getShaderConstHandle( "$gTapRotationTex" ); + + mInit = true; +} + +void LightingShaderConstants::_onShaderReload() +{ + if (mShader.isValid()) + init( mShader ); +} + + +const LightInfoExType ShadowMapParams::Type( "ShadowMapParams" ); + +ShadowMapParams::ShadowMapParams( LightInfo *light ) + : mLight( light ), + mShadowMap( NULL ) +{ + attenuationRatio.set( 0.0f, 1.0f, 1.0f ); + shadowType = ShadowType_Spot; + overDarkFactor.set(2000.0f, 1000.0f, 500.0f, 100.0f); + numSplits = 4; + logWeight = 0.91f; + texSize = 512; + shadowDistance = 400.0f; + shadowSoftness = 0.15f; + fadeStartDist = 0.0f; + lastSplitTerrainOnly = false; + _validate(); +} + +ShadowMapParams::~ShadowMapParams() +{ + SAFE_DELETE( mShadowMap ); +} + +void ShadowMapParams::_validate() +{ + switch ( mLight->getType() ) + { + case LightInfo::Spot: + shadowType = ShadowType_Spot; + break; + + case LightInfo::Vector: + shadowType = ShadowType_PSSM; + break; + + case LightInfo::Point: + if ( shadowType < ShadowType_Paraboloid ) + shadowType = ShadowType_DualParaboloidSinglePass; + break; + + default: + break; + } + + // The texture sizes for shadows should always + // be power of 2 in size. + texSize = getNextPow2( texSize ); + + // The maximum shadow texture size setting we're + // gonna allow... this doesn't use your hardware + // settings as you may be on a lower end system + // than your target machine. + // + // We apply the hardware specific limits during + // shadow rendering. + // + U32 maxTexSize = 4096; + + if ( mLight->getType() == LightInfo::Vector ) + { + numSplits = mClamp( numSplits, 1, 4 ); + + // Adjust the shadow texture size for the PSSM + // based on the split count to keep the total + // shadow texture size within 4096. + if ( numSplits == 2 || numSplits == 4 ) + maxTexSize = 2048; + if ( numSplits == 3 ) + maxTexSize = 1024; + } + else + numSplits = 1; + + // Keep it in a valid range... less than 32 is dumb. + texSize = mClamp( texSize, 32, maxTexSize ); +} + +LightShadowMap* ShadowMapParams::getOrCreateShadowMap() +{ + if ( mShadowMap ) + return mShadowMap; + + if ( !mLight->getCastShadows() ) + return NULL; + + switch ( mLight->getType() ) + { + case LightInfo::Spot: + mShadowMap = new SingleLightShadowMap( mLight ); + break; + + case LightInfo::Vector: + mShadowMap = new PSSMLightShadowMap( mLight ); + break; + + case LightInfo::Point: + + if ( shadowType == ShadowType_CubeMap ) + mShadowMap = new CubeLightShadowMap( mLight ); + else if ( shadowType == ShadowType_Paraboloid ) + mShadowMap = new ParaboloidLightShadowMap( mLight ); + else + mShadowMap = new DualParaboloidLightShadowMap( mLight ); + break; + + default: + break; + } + + return mShadowMap; +} + +GFXTextureObject* ShadowMapParams::getCookieTex() +{ + if ( cookie.isNotEmpty() && + ( mCookieTex.isNull() || + cookie != mCookieTex->getPath() ) ) + { + mCookieTex.set( cookie, + &GFXDefaultStaticDiffuseProfile, + "ShadowMapParams::getCookieTex()" ); + } + else if ( cookie.isEmpty() ) + mCookieTex = NULL; + + return mCookieTex.getPointer(); +} + +GFXCubemap* ShadowMapParams::getCookieCubeTex() +{ + if ( cookie.isNotEmpty() && + ( mCookieCubeTex.isNull() || + cookie != mCookieCubeTex->getPath() ) ) + { + mCookieCubeTex.set( cookie ); + } + else if ( cookie.isEmpty() ) + mCookieCubeTex = NULL; + + return mCookieCubeTex.getPointer(); +} + +void ShadowMapParams::set( const LightInfoEx *ex ) +{ + // TODO: Do we even need this? +} + +void ShadowMapParams::packUpdate( BitStream *stream ) const +{ + // HACK: We need to work out proper parameter + // validation when any field changes on the light. + + ((ShadowMapParams*)this)->_validate(); + + stream->writeInt( shadowType, 8 ); + + mathWrite( *stream, attenuationRatio ); + + stream->write( texSize ); + + stream->write( cookie ); + + stream->write( numSplits ); + stream->write( logWeight ); + + mathWrite(*stream, overDarkFactor); + + stream->write( fadeStartDist ); + stream->writeFlag( lastSplitTerrainOnly ); + + stream->write( shadowDistance ); + + stream->write( shadowSoftness ); +} + +void ShadowMapParams::unpackUpdate( BitStream *stream ) +{ + ShadowType newType = (ShadowType)stream->readInt( 8 ); + if ( shadowType != newType ) + { + // If the shadow type changes delete the shadow + // map so it can be reallocated on the next render. + shadowType = newType; + SAFE_DELETE( mShadowMap ); + } + + mathRead( *stream, &attenuationRatio ); + + stream->read( &texSize ); + + stream->read( &cookie ); + + stream->read( &numSplits ); + stream->read( &logWeight ); + mathRead(*stream, &overDarkFactor); + + stream->read( &fadeStartDist ); + lastSplitTerrainOnly = stream->readFlag(); + + stream->read( &shadowDistance ); + + stream->read( &shadowSoftness ); +} diff --git a/Engine/source/lighting/shadowMap/lightShadowMap.h b/Engine/source/lighting/shadowMap/lightShadowMap.h new file mode 100644 index 000000000..1de278932 --- /dev/null +++ b/Engine/source/lighting/shadowMap/lightShadowMap.h @@ -0,0 +1,380 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _LIGHTSHADOWMAP_H_ +#define _LIGHTSHADOWMAP_H_ + +#ifndef _GFXTEXTUREHANDLE_H_ +#include "gfx/gfxTextureHandle.h" +#endif +#ifndef _GFXCUBEMAP_H_ +#include "gfx/gfxCubemap.h" +#endif +#ifndef _GFXTARGET_H_ +#include "gfx/gfxTarget.h" +#endif +#ifndef _LIGHTINFO_H_ +#include "lighting/lightInfo.h" +#endif +#ifndef _MATHUTIL_FRUSTUM_H_ +#include "math/util/frustum.h" +#endif +#ifndef _MATTEXTURETARGET_H_ +#include "materials/matTextureTarget.h" +#endif +#ifndef _SHADOW_COMMON_H_ +#include "lighting/shadowMap/shadowCommon.h" +#endif +#ifndef _GFXSHADER_H_ +#include "gfx/gfxShader.h" +#endif + +class ShadowMapManager; +class SceneManager; +class SceneRenderState; +class BaseMatInstance; +class MaterialParameters; +class SharedShadowMapObjects; +struct SceneData; +class GFXShaderConstBuffer; +class GFXShaderConstHandle; +class GFXShader; +class GFXOcclusionQuery; +class LightManager; +class RenderPassManager; + + +// Shader constant handle lookup +// This isn't broken up as much as it could be, we're mixing single light constants +// and pssm constants. +struct LightingShaderConstants +{ + bool mInit; + + GFXShaderRef mShader; + + GFXShaderConstHandle* mLightParamsSC; + GFXShaderConstHandle* mLightSpotParamsSC; + + // NOTE: These are the shader constants used for doing + // lighting during the forward pass. Do not confuse + // these for the prepass lighting constants which are + // used from AdvancedLightBinManager. + GFXShaderConstHandle *mLightPositionSC; + GFXShaderConstHandle *mLightDiffuseSC; + GFXShaderConstHandle *mLightAmbientSC; + GFXShaderConstHandle *mLightInvRadiusSqSC; + GFXShaderConstHandle *mLightSpotDirSC; + GFXShaderConstHandle *mLightSpotAngleSC; + GFXShaderConstHandle *mLightSpotFalloffSC; + + GFXShaderConstHandle* mShadowMapSC; + GFXShaderConstHandle* mShadowMapSizeSC; + + GFXShaderConstHandle* mCookieMapSC; + + GFXShaderConstHandle* mRandomDirsConst; + GFXShaderConstHandle* mShadowSoftnessConst; + + GFXShaderConstHandle* mWorldToLightProjSC; + GFXShaderConstHandle* mViewToLightProjSC; + + GFXShaderConstHandle* mScaleXSC; + GFXShaderConstHandle* mScaleYSC; + GFXShaderConstHandle* mOffsetXSC; + GFXShaderConstHandle* mOffsetYSC; + GFXShaderConstHandle* mAtlasXOffsetSC; + GFXShaderConstHandle* mAtlasYOffsetSC; + GFXShaderConstHandle* mAtlasScaleSC; + + // fadeStartLength.x = Distance in eye space to start fading shadows + // fadeStartLength.y = 1 / Length of fade + GFXShaderConstHandle* mFadeStartLength; + GFXShaderConstHandle* mFarPlaneScalePSSM; + GFXShaderConstHandle* mOverDarkFactorPSSM; + + GFXShaderConstHandle* mTapRotationTexSC; + + LightingShaderConstants(); + ~LightingShaderConstants(); + + void init(GFXShader* buffer); + + void _onShaderReload(); +}; + +typedef Map LightConstantMap; + + +/// This represents everything we need to render +/// the shadowmap for one light. +class LightShadowMap +{ +public: + + const static GFXFormat ShadowMapFormat; + + /// Used to scale the shadow texture size for performance tweaking. + static F32 smShadowTexScalar; + + /// Whether to render shadow frustums for lights that have debug + /// rendering enabled. + static bool smDebugRenderFrustums; + +public: + + LightShadowMap( LightInfo *light ); + + virtual ~LightShadowMap(); + + void render( RenderPassManager* renderPass, + const SceneRenderState *diffuseState ); + + U32 getLastUpdate() const { return mLastUpdate; } + + //U32 getLastVisible() const { return mLastVisible; } + + bool isViewDependent() const { return mIsViewDependent; } + + bool wasOccluded() const { return mWasOccluded; } + + void preLightRender(); + + void postLightRender(); + + void updatePriority( const SceneRenderState *state, U32 currTimeMs ); + + F32 getLastScreenSize() const { return mLastScreenSize; } + + F32 getLastPriority() const { return mLastPriority; } + + virtual bool hasShadowTex() const { return mShadowMapTex.isValid(); } + + virtual bool setTextureStage( U32 currTexFlag, LightingShaderConstants* lsc ); + + LightInfo* getLightInfo() { return mLight; } + + virtual void setShaderParameters(GFXShaderConstBuffer* params, LightingShaderConstants* lsc) = 0; + + U32 getTexSize() const { return mTexSize; } + + /// Returns the best texture size based on the user + /// texture size, the last light screen size, and + /// global shadow tweak parameters. + U32 getBestTexSize( U32 scale = 1 ) const; + + const MatrixF& getWorldToLightProj() const { return mWorldToLightProj; } + + static GFXTextureObject* _getDepthTarget( U32 width, U32 height ); + + virtual ShadowType getShadowType() const = 0; + + // Cleanup texture resources + virtual void releaseTextures(); + + /// + GFXTextureObject* getTexture() const { return mShadowMapTex; } + + /// + void setDebugTarget( const String &name ); + + static void releaseAllTextures(); + + /// Releases any shadow maps that have not been culled + /// in a while and returns the count of the remaing + /// shadow maps in use. + static U32 releaseUnusedTextures(); + + /// + static S32 QSORT_CALLBACK cmpPriority( LightShadowMap *const *lsm1, LightShadowMap *const *lsm2 ); + + /// Returns the correct shadow material this type of light + /// or NULL if no shadow material is possible. + BaseMatInstance* getShadowMaterial( BaseMatInstance *inMat ) const; + +protected: + + /// All the shadow maps in the system. + static Vector smShadowMaps; + + /// All the shadow maps that have been recently rendered to. + static Vector smUsedShadowMaps; + + virtual void _render( RenderPassManager* renderPass, + const SceneRenderState *diffuseState ) = 0; + + /// If there is a LightDebugInfo attached to the light that owns this map, + /// then update its information from the given render state. + /// + /// @note This method only does something in debug builds. + void _debugRender( SceneRenderState* shadowRenderState ); + + /// Helper for rendering shadow map for debugging. + NamedTexTarget mDebugTarget; + + /// If true the shadow is view dependent and cannot + /// be skipped if visible and within active range. + bool mIsViewDependent; + + /// The time this shadow was last updated. + U32 mLastUpdate; + + /// The time this shadow was last culled and prioritized. + U32 mLastCull; + + /// The shadow occlusion query used when the light is + /// rendered to determine if any pixel of it is visible. + GFXOcclusionQuery *mVizQuery; + + /// If true the light was occluded by geometry the + /// last frame it was updated. + //the last frame. + bool mWasOccluded; + + F32 mLastScreenSize; + + F32 mLastPriority; + + MatrixF mWorldToLightProj; + + GFXTextureTargetRef mTarget; + U32 mTexSize; + GFXTexHandle mShadowMapTex; + + // The light we are rendering. + LightInfo *mLight; + + // Used for blur + GFXShader* mLastShader; + GFXShaderConstHandle* mBlurBoundaries; + + // Calculate view matrices and set proper projection with GFX + void calcLightMatrices( MatrixF& outLightMatrix, const Frustum &viewFrustum ); + + /// The callback used to get texture events. + /// @see GFXTextureManager::addEventDelegate + void _onTextureEvent( GFXTexCallbackCode code ); +}; + +GFX_DeclareTextureProfile( ShadowMapProfile ); +GFX_DeclareTextureProfile( ShadowMapZProfile ); + + +class ShadowMapParams : public LightInfoEx +{ +public: + + ShadowMapParams( LightInfo *light ); + virtual ~ShadowMapParams(); + + /// The LightInfoEx hook type. + static const LightInfoExType Type; + + // LightInfoEx + virtual void set( const LightInfoEx *ex ); + virtual const LightInfoExType& getType() const { return Type; } + virtual void packUpdate( BitStream *stream ) const; + virtual void unpackUpdate( BitStream *stream ); + + LightShadowMap* getShadowMap() const { return mShadowMap; } + + LightShadowMap* getOrCreateShadowMap(); + + bool hasCookieTex() const { return cookie.isNotEmpty(); } + + GFXTextureObject* getCookieTex(); + + GFXCubemap* getCookieCubeTex(); + + // Validates the parameters after a field is changed. + void _validate(); + +protected: + + void _initShadowMap(); + + /// + LightShadowMap *mShadowMap; + + LightInfo *mLight; + + GFXTexHandle mCookieTex; + + GFXCubemapHandle mCookieCubeTex; + +public: + + // We're leaving these public for easy access + // for console protected fields. + + /// @name Shadow Map + /// @{ + + /// + U32 texSize; + + /// + FileName cookie; + + /// @} + + Point3F attenuationRatio; + + /// @name Point Lights + /// @{ + + /// + ShadowType shadowType; + + /// @} + + /// @name Exponential Shadow Map Parameters + /// @{ + Point4F overDarkFactor; + /// @} + + /// @name Parallel Split Shadow Map + /// @{ + + /// + F32 shadowDistance; + + /// + F32 shadowSoftness; + + /// The number of splits in the shadow map. + U32 numSplits; + + /// + F32 logWeight; + + /// At what distance do we start fading the shadows out completely. + F32 fadeStartDist; + + /// This toggles only terrain being visible in the last + /// split of a PSSM shadow map. + bool lastSplitTerrainOnly; + + /// @} +}; + +#endif // _LIGHTSHADOWMAP_H_ diff --git a/Engine/source/lighting/shadowMap/paraboloidLightShadowMap.cpp b/Engine/source/lighting/shadowMap/paraboloidLightShadowMap.cpp new file mode 100644 index 000000000..91a76efdc --- /dev/null +++ b/Engine/source/lighting/shadowMap/paraboloidLightShadowMap.cpp @@ -0,0 +1,141 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/shadowMap/paraboloidLightShadowMap.h" +#include "lighting/common/lightMapParams.h" +#include "lighting/shadowMap/shadowMapManager.h" +#include "math/mathUtils.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +//#include "scene/sceneReflectPass.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/util/gfxFrustumSaver.h" +#include "renderInstance/renderPassManager.h" +#include "materials/materialDefinition.h" +#include "gui/controls/guiBitmapCtrl.h" + +ParaboloidLightShadowMap::ParaboloidLightShadowMap( LightInfo *light ) + : Parent( light ), + mShadowMapScale( 1, 1 ), + mShadowMapOffset( 0, 0 ) +{ +} + +ParaboloidLightShadowMap::~ParaboloidLightShadowMap() +{ + releaseTextures(); +} + +ShadowType ParaboloidLightShadowMap::getShadowType() const +{ + const ShadowMapParams *params = mLight->getExtended(); + return params->shadowType; +} + +void ParaboloidLightShadowMap::setShaderParameters(GFXShaderConstBuffer* params, LightingShaderConstants* lsc) +{ + if ( lsc->mTapRotationTexSC->isValid() ) + GFX->setTexture( lsc->mTapRotationTexSC->getSamplerRegister(), + SHADOWMGR->getTapRotationTex() ); + + ShadowMapParams *p = mLight->getExtended(); + if ( lsc->mLightParamsSC->isValid() ) + { + Point4F lightParams( mLight->getRange().x, p->overDarkFactor.x, 0.0f, 0.0f); + params->set( lsc->mLightParamsSC, lightParams ); + } + + // Atlasing parameters (only used in the dual case, set here to use same shaders) + params->setSafe( lsc->mAtlasScaleSC, mShadowMapScale ); + params->setSafe( lsc->mAtlasXOffsetSC, mShadowMapOffset ); + + // The softness is a factor of the texel size. + params->setSafe( lsc->mShadowSoftnessConst, p->shadowSoftness * ( 1.0f / mTexSize ) ); +} + +void ParaboloidLightShadowMap::_render( RenderPassManager* renderPass, + const SceneRenderState *diffuseState ) +{ + PROFILE_SCOPE(ParaboloidLightShadowMap_render); + + const LightMapParams *lmParams = mLight->getExtended(); + const bool bUseLightmappedGeometry = lmParams ? !lmParams->representedInLightmap || lmParams->includeLightmappedGeometryInShadow : true; + + const U32 texSize = getBestTexSize(); + + if ( mShadowMapTex.isNull() || + mTexSize != texSize ) + { + mTexSize = texSize; + + mShadowMapTex.set( mTexSize, mTexSize, + ShadowMapFormat, &ShadowMapProfile, + "ParaboloidLightShadowMap" ); + } + + GFXFrustumSaver frustSaver; + GFXTransformSaver saver; + + // Render the shadowmap! + GFX->pushActiveRenderTarget(); + + // Calc matrix and set up visible distance + mWorldToLightProj = mLight->getTransform(); + mWorldToLightProj.inverse(); + GFX->setWorldMatrix(mWorldToLightProj); + + const F32 &lightRadius = mLight->getRange().x; + GFX->setOrtho(-lightRadius, lightRadius, -lightRadius, lightRadius, 1.0f, lightRadius, true); + + // Set up target + mTarget->attachTexture( GFXTextureTarget::Color0, mShadowMapTex ); + mTarget->attachTexture( GFXTextureTarget::DepthStencil, + _getDepthTarget( mShadowMapTex->getWidth(), mShadowMapTex->getHeight() ) ); + GFX->setActiveRenderTarget(mTarget); + GFX->clear(GFXClearTarget | GFXClearStencil | GFXClearZBuffer, ColorI(255,255,255,255), 1.0f, 0); + + // Create scene state, prep it + SceneManager* sceneManager = diffuseState->getSceneManager(); + + SceneRenderState shadowRenderState + ( + sceneManager, + SPT_Shadow, + SceneCameraState::fromGFXWithViewport( diffuseState->getViewport() ), + renderPass + ); + + shadowRenderState.getMaterialDelegate().bind( this, &LightShadowMap::getShadowMaterial ); + shadowRenderState.renderNonLightmappedMeshes( true ); + shadowRenderState.renderLightmappedMeshes( bUseLightmappedGeometry ); + shadowRenderState.setDiffuseCameraTransform( diffuseState->getCameraTransform() ); + shadowRenderState.setWorldToScreenScale( diffuseState->getWorldToScreenScale() ); + + sceneManager->renderSceneNoLights( &shadowRenderState, SHADOW_TYPEMASK ); + + _debugRender( &shadowRenderState ); + + mTarget->resolve(); + GFX->popActiveRenderTarget(); +} \ No newline at end of file diff --git a/Engine/source/lighting/shadowMap/paraboloidLightShadowMap.h b/Engine/source/lighting/shadowMap/paraboloidLightShadowMap.h new file mode 100644 index 000000000..917ab99bc --- /dev/null +++ b/Engine/source/lighting/shadowMap/paraboloidLightShadowMap.h @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PARABOLOIDLIGHTSHADOWMAP_H_ +#define _PARABOLOIDLIGHTSHADOWMAP_H_ + +#ifndef _LIGHTSHADOWMAP_H_ +#include "lighting/shadowMap/lightShadowMap.h" +#endif + + +class ParaboloidLightShadowMap : public LightShadowMap +{ + typedef LightShadowMap Parent; +public: + ParaboloidLightShadowMap( LightInfo *light ); + ~ParaboloidLightShadowMap(); + + // LightShadowMap + virtual ShadowType getShadowType() const; + virtual void _render( RenderPassManager* renderPass, const SceneRenderState *diffuseState ); + virtual void setShaderParameters(GFXShaderConstBuffer* params, LightingShaderConstants* lsc); + +protected: + Point2F mShadowMapScale; + Point2F mShadowMapOffset; +}; + +#endif \ No newline at end of file diff --git a/Engine/source/lighting/shadowMap/pssmLightShadowMap.cpp b/Engine/source/lighting/shadowMap/pssmLightShadowMap.cpp new file mode 100644 index 000000000..e8bf8df31 --- /dev/null +++ b/Engine/source/lighting/shadowMap/pssmLightShadowMap.cpp @@ -0,0 +1,459 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/shadowMap/pssmLightShadowMap.h" + +#include "lighting/common/lightMapParams.h" +#include "console/console.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "lighting/lightManager.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/util/gfxFrustumSaver.h" +#include "renderInstance/renderPassManager.h" +#include "gui/controls/guiBitmapCtrl.h" +#include "lighting/shadowMap/shadowMapManager.h" +#include "materials/shaderData.h" +#include "ts/tsShapeInstance.h" +#include "console/consoleTypes.h" + + +AFTER_MODULE_INIT( Sim ) +{ + Con::addVariable( "$pref::PSSM::detailAdjustScale", + TypeF32, &PSSMLightShadowMap::smDetailAdjustScale, + "@brief Scales the model LOD when rendering into the PSSM shadow.\n" + "Use this to reduce the draw calls when rendering the shadow by having " + "meshes LOD out nearer to the camera than normal.\n" + "@see $pref::TS::detailAdjust\n" + "@ingroup AdvancedLighting" ); + + Con::addVariable( "$pref::PSSM::smallestVisiblePixelSize", + TypeF32, &PSSMLightShadowMap::smSmallestVisiblePixelSize, + "@brief The smallest pixel size an object can be and still be rendered into the PSSM shadow.\n" + "Use this to force culling of small objects which contribute little to the final shadow.\n" + "@see $pref::TS::smallestVisiblePixelSize\n" + "@ingroup AdvancedLighting" ); +} + +F32 PSSMLightShadowMap::smDetailAdjustScale = 0.85f; +F32 PSSMLightShadowMap::smSmallestVisiblePixelSize = 25.0f; + + +PSSMLightShadowMap::PSSMLightShadowMap( LightInfo *light ) + : LightShadowMap( light ), + mNumSplits( 0 ) +{ + mIsViewDependent = true; +} + +void PSSMLightShadowMap::_setNumSplits( U32 numSplits, U32 texSize ) +{ + AssertFatal( numSplits > 0 && numSplits <= MAX_SPLITS, + "PSSMLightShadowMap::_setNumSplits() - Splits must be between 1 and 4!" ); + + releaseTextures(); + + mNumSplits = numSplits; + mTexSize = texSize; + F32 texWidth, texHeight; + + // If the split count is less than 4 then do a + // 1xN layout of shadow maps... + if ( mNumSplits < 4 ) + { + texHeight = texSize; + texWidth = texSize * mNumSplits; + + for ( U32 i = 0; i < 4; i++ ) + { + mViewports[i].extent.set(texSize, texSize); + mViewports[i].point.set(texSize*i, 0); + } + } + else + { + // ... with 4 splits do a 2x2. + texWidth = texHeight = texSize * 2; + + for ( U32 i = 0; i < 4; i++ ) + { + F32 xOff = (i == 1 || i == 3) ? 0.5f : 0.0f; + F32 yOff = (i > 1) ? 0.5f : 0.0f; + mViewports[i].extent.set( texSize, texSize ); + mViewports[i].point.set( xOff * texWidth, yOff * texHeight ); + } + } + + mShadowMapTex.set( texWidth, texHeight, + ShadowMapFormat, &ShadowMapProfile, + "PSSMLightShadowMap" ); +} + +void PSSMLightShadowMap::_calcSplitPos(const Frustum& currFrustum) +{ + const F32 nearDist = 0.01f; // TODO: Should this be adjustable or different? + const F32 farDist = currFrustum.getFarDist(); + + for ( U32 i = 1; i < mNumSplits; i++ ) + { + F32 step = (F32) i / (F32) mNumSplits; + F32 logSplit = nearDist * mPow(farDist / nearDist, step); + F32 linearSplit = nearDist + (farDist - nearDist) * step; + mSplitDist[i] = mLerp( linearSplit, logSplit, mClampF( mLogWeight, 0.0f, 1.0f ) ); + } + + mSplitDist[0] = nearDist; + mSplitDist[mNumSplits] = farDist; +} + +Box3F PSSMLightShadowMap::_calcClipSpaceAABB(const Frustum& f, const MatrixF& transform, F32 farDist) +{ + // Calculate frustum center + Point3F center(0,0,0); + for (U32 i = 0; i < 8; i++) + { + const Point3F& pt = f.getPoints()[i]; + center += pt; + } + center /= 8; + + // Calculate frustum bounding sphere radius + F32 radius = 0.0f; + for (U32 i = 0; i < 8; i++) + radius = getMax(radius, (f.getPoints()[i] - center).lenSquared()); + radius = mFloor( mSqrt(radius) ); + + // Now build box for sphere + Box3F result; + Point3F radiusBox(radius, radius, radius); + result.minExtents = center - radiusBox; + result.maxExtents = center + radiusBox; + + // Transform to light projection space + transform.mul(result); + + return result; +} + +// This "rounds" the projection matrix to remove subtexel movement during shadow map +// rasterization. This is here to reduce shadow shimmering. +void PSSMLightShadowMap::_roundProjection(const MatrixF& lightMat, const MatrixF& cropMatrix, Point3F &offset, U32 splitNum) +{ + // Round to the nearest shadowmap texel, this helps reduce shimmering + MatrixF currentProj = GFX->getProjectionMatrix(); + currentProj = cropMatrix * currentProj * lightMat; + + // Project origin to screen. + Point4F originShadow4F(0,0,0,1); + currentProj.mul(originShadow4F); + Point2F originShadow(originShadow4F.x / originShadow4F.w, originShadow4F.y / originShadow4F.w); + + // Convert to texture space (0..shadowMapSize) + F32 t = mNumSplits < 4 ? mShadowMapTex->getWidth() / mNumSplits : mShadowMapTex->getWidth() / 2; + Point2F texelsToTexture(t / 2.0f, mShadowMapTex->getHeight() / 2.0f); + if (mNumSplits >= 4) texelsToTexture.y *= 0.5f; + originShadow.convolve(texelsToTexture); + + // Clamp to texel boundary + Point2F originRounded; + originRounded.x = mFloor(originShadow.x + 0.5f); + originRounded.y = mFloor(originShadow.y + 0.5f); + + // Subtract origin to get an offset to recenter everything on texel boundaries + originRounded -= originShadow; + + // Convert back to texels (0..1) and offset + originRounded.convolveInverse(texelsToTexture); + offset.x += originRounded.x; + offset.y += originRounded.y; +} + +void PSSMLightShadowMap::_render( RenderPassManager* renderPass, + const SceneRenderState *diffuseState ) +{ + PROFILE_SCOPE(PSSMLightShadowMap_render); + + const ShadowMapParams *params = mLight->getExtended(); + const LightMapParams *lmParams = mLight->getExtended(); + const bool bUseLightmappedGeometry = lmParams ? !lmParams->representedInLightmap || lmParams->includeLightmappedGeometryInShadow : true; + + const U32 texSize = getBestTexSize( params->numSplits < 4 ? params->numSplits : 2 ); + + if ( mShadowMapTex.isNull() || + mNumSplits != params->numSplits || + mTexSize != texSize ) + _setNumSplits( params->numSplits, texSize ); + mLogWeight = params->logWeight; + + Frustum fullFrustum( diffuseState->getFrustum() ); + fullFrustum.cropNearFar(fullFrustum.getNearDist(), params->shadowDistance); + + GFXFrustumSaver frustSaver; + GFXTransformSaver saver; + + // Set our render target + GFX->pushActiveRenderTarget(); + mTarget->attachTexture( GFXTextureTarget::Color0, mShadowMapTex ); + mTarget->attachTexture( GFXTextureTarget::DepthStencil, + _getDepthTarget( mShadowMapTex->getWidth(), mShadowMapTex->getHeight() ) ); + GFX->setActiveRenderTarget( mTarget ); + GFX->clear( GFXClearStencil | GFXClearZBuffer | GFXClearTarget, ColorI(255,255,255), 1.0f, 0 ); + + // Calculate our standard light matrices + MatrixF lightMatrix; + calcLightMatrices( lightMatrix, diffuseState->getFrustum() ); + lightMatrix.inverse(); + MatrixF lightViewProj = GFX->getProjectionMatrix() * lightMatrix; + + // TODO: This is just retrieving the near and far calculated + // in calcLightMatrices... we should make that clear. + F32 pnear, pfar; + GFX->getFrustum( NULL, NULL, NULL, NULL, &pnear, &pfar, NULL ); + + // Set our view up + GFX->setWorldMatrix(lightMatrix); + MatrixF toLightSpace = lightMatrix; // * invCurrentView; + + _calcSplitPos(fullFrustum); + + mWorldToLightProj = GFX->getProjectionMatrix() * toLightSpace; + + // Apply the PSSM + const F32 savedSmallestVisible = TSShapeInstance::smSmallestVisiblePixelSize; + const F32 savedDetailAdjust = TSShapeInstance::smDetailAdjust; + TSShapeInstance::smDetailAdjust *= smDetailAdjustScale; + TSShapeInstance::smSmallestVisiblePixelSize = smSmallestVisiblePixelSize; + + for (U32 i = 0; i < mNumSplits; i++) + { + GFXTransformSaver saver; + + // Calculate a sub-frustum + Frustum subFrustum(fullFrustum); + subFrustum.cropNearFar(mSplitDist[i], mSplitDist[i+1]); + + // Calculate our AABB in the light's clip space. + Box3F clipAABB = _calcClipSpaceAABB(subFrustum, lightViewProj, fullFrustum.getFarDist()); + + // Calculate our crop matrix + Point3F scale(2.0f / (clipAABB.maxExtents.x - clipAABB.minExtents.x), + 2.0f / (clipAABB.maxExtents.y - clipAABB.minExtents.y), + 1.0f); + + // TODO: This seems to produce less "pops" of the + // shadow resolution as the camera spins around and + // it should produce pixels that are closer to being + // square. + // + // Still is it the right thing to do? + // + scale.y = scale.x = ( getMin( scale.x, scale.y ) ); + //scale.x = mFloor(scale.x); + //scale.y = mFloor(scale.y); + + Point3F offset( -0.5f * (clipAABB.maxExtents.x + clipAABB.minExtents.x) * scale.x, + -0.5f * (clipAABB.maxExtents.y + clipAABB.minExtents.y) * scale.y, + 0.0f ); + + MatrixF cropMatrix(true); + cropMatrix.scale(scale); + cropMatrix.setPosition(offset); + + _roundProjection(lightMatrix, cropMatrix, offset, i); + + cropMatrix.setPosition(offset); + + // Save scale/offset for shader computations + mScaleProj[i].set(scale); + mOffsetProj[i].set(offset); + + // Adjust the far plane to the max z we got (maybe add a little to deal with split overlap) + bool isOrtho; + { + F32 left, right, bottom, top, nearDist, farDist; + GFX->getFrustum(&left, &right, &bottom, &top, &nearDist, &farDist,&isOrtho); + // BTRTODO: Fix me! + farDist = clipAABB.maxExtents.z; + if (!isOrtho) + GFX->setFrustum(left, right, bottom, top, nearDist, farDist); + else + { + // Calculate a new far plane, add a fudge factor to avoid bringing + // the far plane in too close. + F32 newFar = pfar * clipAABB.maxExtents.z + 1.0f; + mFarPlaneScalePSSM[i] = (pfar - pnear) / (newFar - pnear); + GFX->setOrtho(left, right, bottom, top, pnear, newFar, true); + } + } + + // Crop matrix multiply needs to be post-projection. + MatrixF alightProj = GFX->getProjectionMatrix(); + alightProj = cropMatrix * alightProj; + + // Set our new projection + GFX->setProjectionMatrix(alightProj); + + // Render into the quad of the shadow map we are using. + GFX->setViewport(mViewports[i]); + + SceneManager* sceneManager = diffuseState->getSceneManager(); + + // The frustum is currently the full size and has not had + // cropping applied. + // + // We make that adjustment here. + + const Frustum& uncroppedFrustum = GFX->getFrustum(); + Frustum croppedFrustum; + scale *= 0.5f; + croppedFrustum.set( + isOrtho, + uncroppedFrustum.getNearLeft() / scale.x, + uncroppedFrustum.getNearRight() / scale.x, + uncroppedFrustum.getNearTop() / scale.y, + uncroppedFrustum.getNearBottom() / scale.y, + uncroppedFrustum.getNearDist(), + uncroppedFrustum.getFarDist(), + uncroppedFrustum.getTransform() + ); + + MatrixF camera = GFX->getWorldMatrix(); + camera.inverse(); + croppedFrustum.setTransform( camera ); + + // Setup the scene state and use the diffuse state + // camera position and screen metrics values so that + // lod is done the same as in the diffuse pass. + + SceneRenderState shadowRenderState + ( + sceneManager, + SPT_Shadow, + SceneCameraState( diffuseState->getViewport(), croppedFrustum, + GFX->getWorldMatrix(), GFX->getProjectionMatrix() ), + renderPass + ); + + shadowRenderState.getMaterialDelegate().bind( this, &LightShadowMap::getShadowMaterial ); + shadowRenderState.renderNonLightmappedMeshes( true ); + shadowRenderState.renderLightmappedMeshes( bUseLightmappedGeometry ); + + shadowRenderState.setDiffuseCameraTransform( diffuseState->getCameraTransform() ); + shadowRenderState.setWorldToScreenScale( diffuseState->getWorldToScreenScale() ); + + U32 objectMask = SHADOW_TYPEMASK; + if ( i == mNumSplits-1 && params->lastSplitTerrainOnly ) + objectMask = TerrainObjectType; + + sceneManager->renderSceneNoLights( &shadowRenderState, objectMask ); + + _debugRender( &shadowRenderState ); + } + + // Restore the original TS lod settings. + TSShapeInstance::smSmallestVisiblePixelSize = savedSmallestVisible; + TSShapeInstance::smDetailAdjust = savedDetailAdjust; + + // Release our render target + mTarget->resolve(); + GFX->popActiveRenderTarget(); +} + +void PSSMLightShadowMap::setShaderParameters(GFXShaderConstBuffer* params, LightingShaderConstants* lsc) +{ + PROFILE_SCOPE( PSSMLightShadowMap_setShaderParameters ); + + if ( lsc->mTapRotationTexSC->isValid() ) + GFX->setTexture( lsc->mTapRotationTexSC->getSamplerRegister(), + SHADOWMGR->getTapRotationTex() ); + + const ShadowMapParams *p = mLight->getExtended(); + + Point4F sx(Point4F::Zero), + sy(Point4F::Zero), + ox(Point4F::Zero), + oy(Point4F::Zero), + aXOff(Point4F::Zero), + aYOff(Point4F::Zero); + + for (U32 i = 0; i < mNumSplits; i++) + { + sx[i] = mScaleProj[i].x; + sy[i] = mScaleProj[i].y; + ox[i] = mOffsetProj[i].x; + oy[i] = mOffsetProj[i].y; + } + + Point2F shadowMapAtlas; + if (mNumSplits < 4) + { + shadowMapAtlas.x = 1.0f / (F32)mNumSplits; + shadowMapAtlas.y = 1.0f; + + // 1xmNumSplits + for (U32 i = 0; i < mNumSplits; i++) + aXOff[i] = (F32)i * shadowMapAtlas.x; + } + else + { + shadowMapAtlas.set(0.5f, 0.5f); + + // 2x2 + for (U32 i = 0; i < mNumSplits; i++) + { + if (i == 1 || i == 3) + aXOff[i] = 0.5f; + if (i > 1) + aYOff[i] = 0.5f; + } + } + + params->setSafe(lsc->mScaleXSC, sx); + params->setSafe(lsc->mScaleYSC, sy); + params->setSafe(lsc->mOffsetXSC, ox); + params->setSafe(lsc->mOffsetYSC, oy); + params->setSafe(lsc->mAtlasXOffsetSC, aXOff); + params->setSafe(lsc->mAtlasYOffsetSC, aYOff); + params->setSafe(lsc->mAtlasScaleSC, shadowMapAtlas); + + Point4F lightParams( mLight->getRange().x, p->overDarkFactor.x, 0.0f, 0.0f ); + params->setSafe( lsc->mLightParamsSC, lightParams ); + + params->setSafe( lsc->mFarPlaneScalePSSM, mFarPlaneScalePSSM); + + Point2F fadeStartLength(p->fadeStartDist, 0.0f); + if (fadeStartLength.x == 0.0f) + { + // By default, lets fade the last half of the last split. + fadeStartLength.x = (mSplitDist[mNumSplits-1] + mSplitDist[mNumSplits]) / 2.0f; + } + fadeStartLength.y = 1.0f / (mSplitDist[mNumSplits] - fadeStartLength.x); + params->setSafe( lsc->mFadeStartLength, fadeStartLength); + + params->setSafe( lsc->mOverDarkFactorPSSM, p->overDarkFactor); + + // The softness is a factor of the texel size. + params->setSafe( lsc->mShadowSoftnessConst, p->shadowSoftness * ( 1.0f / mTexSize ) ); +} diff --git a/Engine/source/lighting/shadowMap/pssmLightShadowMap.h b/Engine/source/lighting/shadowMap/pssmLightShadowMap.h new file mode 100644 index 000000000..8ced7b875 --- /dev/null +++ b/Engine/source/lighting/shadowMap/pssmLightShadowMap.h @@ -0,0 +1,71 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _PSSMLIGHTSHADOWMAP_H_ +#define _PSSMLIGHTSHADOWMAP_H_ + +#ifndef _LIGHTSHADOWMAP_H_ +#include "lighting/shadowMap/lightShadowMap.h" +#endif +#ifndef _MATHUTIL_FRUSTUM_H_ +#include "math/util/frustum.h" +#endif + + +class PSSMLightShadowMap : public LightShadowMap +{ + typedef LightShadowMap Parent; +public: + PSSMLightShadowMap( LightInfo *light ); + + // LightShadowMap + virtual ShadowType getShadowType() const { return ShadowType_PSSM; } + virtual void _render( RenderPassManager* renderPass, const SceneRenderState *diffuseState ); + virtual void setShaderParameters(GFXShaderConstBuffer* params, LightingShaderConstants* lsc); + + /// Used to scale TSShapeInstance::smDetailAdjust to have + /// objects lod quicker when in the PSSM shadow. + /// @see TSShapeInstance::smDetailAdjust + static F32 smDetailAdjustScale; + + /// Like TSShapeInstance::smSmallestVisiblePixelSize this is used + /// to define the smallest LOD to render. + /// @see TSShapeInstance::smSmallestVisiblePixelSize + static F32 smSmallestVisiblePixelSize; + +protected: + + void _setNumSplits( U32 numSplits, U32 texSize ); + void _calcSplitPos(const Frustum& currFrustum); + Box3F _calcClipSpaceAABB(const Frustum& f, const MatrixF& transform, F32 farDist); + void _roundProjection(const MatrixF& lightMat, const MatrixF& cropMatrix, Point3F &offset, U32 splitNum); + + static const int MAX_SPLITS = 4; + U32 mNumSplits; + F32 mSplitDist[MAX_SPLITS+1]; // +1 because we store a cap + RectI mViewports[MAX_SPLITS]; + Point3F mScaleProj[MAX_SPLITS]; + Point3F mOffsetProj[MAX_SPLITS]; + Point4F mFarPlaneScalePSSM; + F32 mLogWeight; +}; + +#endif diff --git a/Engine/source/lighting/shadowMap/shadowCommon.h b/Engine/source/lighting/shadowMap/shadowCommon.h new file mode 100644 index 000000000..6818482e7 --- /dev/null +++ b/Engine/source/lighting/shadowMap/shadowCommon.h @@ -0,0 +1,62 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SHADOW_COMMON_H_ +#define _SHADOW_COMMON_H_ + +#ifndef _DYNAMIC_CONSOLETYPES_H_ + #include "console/dynamicTypes.h" +#endif + + +/// +enum ShadowType +{ + ShadowType_None = -1, + + ShadowType_Spot, + ShadowType_PSSM, + + ShadowType_Paraboloid, + ShadowType_DualParaboloidSinglePass, + ShadowType_DualParaboloid, + ShadowType_CubeMap, + + ShadowType_Count, +}; + +DefineEnumType( ShadowType ); + + +/// The different shadow filter modes used when rendering +/// shadowed lights. +/// @see setShadowFilterMode +enum ShadowFilterMode +{ + ShadowFilterMode_None, + ShadowFilterMode_SoftShadow, + ShadowFilterMode_SoftShadowHighQuality +}; + +DefineEnumType( ShadowFilterMode ); + +#endif // _SHADOW_COMMON_H_ diff --git a/Engine/source/lighting/shadowMap/shadowMapManager.cpp b/Engine/source/lighting/shadowMap/shadowMapManager.cpp new file mode 100644 index 000000000..078737db9 --- /dev/null +++ b/Engine/source/lighting/shadowMap/shadowMapManager.cpp @@ -0,0 +1,192 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/shadowMap/shadowMapManager.h" + +#include "lighting/shadowMap/shadowMapPass.h" +#include "lighting/shadowMap/lightShadowMap.h" +#include "materials/materialManager.h" +#include "lighting/lightManager.h" +#include "core/util/safeDelete.h" +#include "scene/sceneRenderState.h" +#include "gfx/gfxTextureManager.h" +#include "core/module.h" +#include "console/consoleTypes.h" + + +GFX_ImplementTextureProfile(ShadowMapTexProfile, + GFXTextureProfile::DiffuseMap, + GFXTextureProfile::PreserveSize | GFXTextureProfile::Dynamic , + GFXTextureProfile::None); + + +MODULE_BEGIN( ShadowMapManager ) + + MODULE_INIT + { + ManagedSingleton< ShadowMapManager >::createSingleton(); + } + + MODULE_SHUTDOWN + { + ManagedSingleton< ShadowMapManager >::deleteSingleton(); + } + +MODULE_END; + + +AFTER_MODULE_INIT( Sim ) +{ + Con::addVariable( "$pref::Shadows::textureScalar", + TypeF32, &LightShadowMap::smShadowTexScalar, + "@brief Used to scale the shadow texture sizes.\n" + "This can reduce the shadow quality and texture memory overhead or increase them.\n" + "@ingroup AdvancedLighting\n" ); + Con::NotifyDelegate callabck( &LightShadowMap::releaseAllTextures ); + Con::addVariableNotify( "$pref::Shadows::textureScalar", callabck ); + + Con::addVariable( "$pref::Shadows::disable", + TypeBool, &ShadowMapPass::smDisableShadowsPref, + "Used to disable all shadow rendering.\n" + "@ingroup AdvancedLighting\n" ); + + Con::addVariable( "$Shadows::disable", + TypeBool, &ShadowMapPass::smDisableShadowsEditor, + "Used by the editor to disable all shadow rendering.\n" + "@ingroup AdvancedLighting\n" ); + + Con::NotifyDelegate shadowCallback( &ShadowMapManager::updateShadowDisable ); + Con::addVariableNotify( "$pref::Shadows::disable", shadowCallback ); + Con::addVariableNotify( "$Shadows::disable", shadowCallback ); +} + +Signal ShadowMapManager::smShadowDeactivateSignal; + + +ShadowMapManager::ShadowMapManager() +: mShadowMapPass(NULL), + mCurrentShadowMap(NULL), + mIsActive(false) +{ +} + +ShadowMapManager::~ShadowMapManager() +{ +} + +void ShadowMapManager::setLightShadowMapForLight( LightInfo *light ) +{ + ShadowMapParams *params = light->getExtended(); + if ( params ) + mCurrentShadowMap = params->getShadowMap(); + else + mCurrentShadowMap = NULL; +} + +void ShadowMapManager::activate() +{ + ShadowManager::activate(); + + if (!getSceneManager()) + { + Con::errorf("This world has no scene manager! Shadow manager not activating!"); + return; + } + + mShadowMapPass = new ShadowMapPass(LIGHTMGR, this); + + getSceneManager()->getPreRenderSignal().notify( this, &ShadowMapManager::_onPreRender, 0.01f ); + GFXTextureManager::addEventDelegate( this, &ShadowMapManager::_onTextureEvent ); + + mIsActive = true; +} + +void ShadowMapManager::deactivate() +{ + GFXTextureManager::removeEventDelegate( this, &ShadowMapManager::_onTextureEvent ); + getSceneManager()->getPreRenderSignal().remove( this, &ShadowMapManager::_onPreRender ); + + SAFE_DELETE(mShadowMapPass); + mTapRotationTex = NULL; + + // Clean up our shadow texture memory. + LightShadowMap::releaseAllTextures(); + TEXMGR->cleanupPool(); + + mIsActive = false; + + ShadowManager::deactivate(); +} + +void ShadowMapManager::_onPreRender( SceneManager *sg, const SceneRenderState *state ) +{ + if ( mShadowMapPass && state->isDiffusePass() ) + mShadowMapPass->render( sg, state, (U32)-1 ); +} + +void ShadowMapManager::_onTextureEvent( GFXTexCallbackCode code ) +{ + if ( code == GFXZombify ) + mTapRotationTex = NULL; +} + +GFXTextureObject* ShadowMapManager::getTapRotationTex() +{ + if ( mTapRotationTex.isValid() ) + return mTapRotationTex; + + mTapRotationTex.set( 64, 64, GFXFormatR8G8B8A8, &ShadowMapTexProfile, + "ShadowMapManager::getTapRotationTex" ); + + GFXLockedRect *rect = mTapRotationTex.lock(); + U8 *f = rect->bits; + F32 angle; + for( U32 i = 0; i < 64*64; i++, f += 4 ) + { + // We only pack the rotations into the red + // and green channels... the rest are empty. + angle = M_2PI_F * gRandGen.randF(); + f[0] = U8_MAX * ( ( 1.0f + mSin( angle ) ) * 0.5f ); + f[1] = U8_MAX * ( ( 1.0f + mCos( angle ) ) * 0.5f ); + f[2] = 0; + f[3] = 0; + } + + mTapRotationTex.unlock(); + + return mTapRotationTex; +} + +void ShadowMapManager::updateShadowDisable() +{ + bool disable = false; + + if ( ShadowMapPass::smDisableShadowsEditor || ShadowMapPass::smDisableShadowsPref ) + disable = true; + + if ( disable != ShadowMapPass::smDisableShadows) + { + ShadowMapPass::smDisableShadows = disable; + smShadowDeactivateSignal.trigger(); + } +} diff --git a/Engine/source/lighting/shadowMap/shadowMapManager.h b/Engine/source/lighting/shadowMap/shadowMapManager.h new file mode 100644 index 000000000..9319f3e36 --- /dev/null +++ b/Engine/source/lighting/shadowMap/shadowMapManager.h @@ -0,0 +1,108 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SHADOWMAPMANAGER_H_ +#define _SHADOWMAPMANAGER_H_ + +#ifndef _TSINGLETON_H_ +#include "core/util/tSingleton.h" +#endif +#ifndef _SHADOWMANAGER_H_ +#include "lighting/shadowManager.h" +#endif +#ifndef _GFXENUMS_H_ +#include "gfx/gfxEnums.h" +#endif +#ifndef _GFXTEXTUREHANDLE_H_ +#include "gfx/gfxTextureHandle.h" +#endif +#ifndef _MPOINT4_H_ +#include "math/mPoint4.h" +#endif + +class LightShadowMap; +class ShadowMapPass; +class LightInfo; + +class SceneManager; +class SceneRenderState; + + +class ShadowMapManager : public ShadowManager +{ + typedef ShadowManager Parent; + + friend class ShadowMapPass; + +public: + + ShadowMapManager(); + virtual ~ShadowMapManager(); + + /// Sets the current shadowmap (used in setLightInfo/setTextureStage calls) + void setLightShadowMap( LightShadowMap *lm ) { mCurrentShadowMap = lm; } + + /// Looks up the shadow map for the light then sets it. + void setLightShadowMapForLight( LightInfo *light ); + + /// Return the current shadow map + LightShadowMap* getCurrentShadowMap() const { return mCurrentShadowMap; } + + ShadowMapPass* getShadowMapPass() const { return mShadowMapPass; } + + // Shadow manager + virtual void activate(); + virtual void deactivate(); + + GFXTextureObject* getTapRotationTex(); + + /// The shadow map deactivation signal. + static Signal smShadowDeactivateSignal; + + static void updateShadowDisable(); + +protected: + + void _onTextureEvent( GFXTexCallbackCode code ); + + void _onPreRender( SceneManager *sg, const SceneRenderState* state ); + + ShadowMapPass *mShadowMapPass; + LightShadowMap *mCurrentShadowMap; + + /// + GFXTexHandle mTapRotationTex; + + bool mIsActive; + +public: + // For ManagedSingleton. + static const char* getSingletonName() { return "ShadowMapManager"; } +}; + + +/// Returns the ShadowMapManager singleton. +#define SHADOWMGR ManagedSingleton::instance() + +GFX_DeclareTextureProfile( ShadowMapTexProfile ); + +#endif // _SHADOWMAPMANAGER_H_ \ No newline at end of file diff --git a/Engine/source/lighting/shadowMap/shadowMapPass.cpp b/Engine/source/lighting/shadowMap/shadowMapPass.cpp new file mode 100644 index 000000000..64f8df2bc --- /dev/null +++ b/Engine/source/lighting/shadowMap/shadowMapPass.cpp @@ -0,0 +1,250 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/shadowMap/shadowMapPass.h" + +#include "lighting/shadowMap/lightShadowMap.h" +#include "lighting/shadowMap/shadowMapManager.h" +#include "lighting/lightManager.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "renderInstance/renderPassManager.h" +#include "renderInstance/renderObjectMgr.h" +#include "renderInstance/renderMeshMgr.h" +#include "renderInstance/renderTerrainMgr.h" +#include "renderInstance/renderImposterMgr.h" +#include "core/util/safeDelete.h" +#include "console/consoleTypes.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/gfxDebugEvent.h" +#include "platform/platformTimer.h" + + +const String ShadowMapPass::PassTypeName("ShadowMap"); + +U32 ShadowMapPass::smActiveShadowMaps = 0; +U32 ShadowMapPass::smUpdatedShadowMaps = 0; +U32 ShadowMapPass::smNearShadowMaps = 0; +U32 ShadowMapPass::smShadowMapsDrawCalls = 0; +U32 ShadowMapPass::smShadowMapPolyCount = 0; +U32 ShadowMapPass::smRenderTargetChanges = 0; +U32 ShadowMapPass::smShadowPoolTexturesCount = 0.; +F32 ShadowMapPass::smShadowPoolMemory = 0.0f; + +bool ShadowMapPass::smDisableShadows = false; +bool ShadowMapPass::smDisableShadowsEditor = false; +bool ShadowMapPass::smDisableShadowsPref = false; + +/// We have a default 8ms render budget for shadow rendering. +U32 ShadowMapPass::smRenderBudgetMs = 8; + +ShadowMapPass::ShadowMapPass(LightManager* lightManager, ShadowMapManager* shadowManager) +{ + mLightManager = lightManager; + mShadowManager = shadowManager; + mShadowRPM = new ShadowRenderPassManager(); + mShadowRPM->assignName( "ShadowRenderPassManager" ); + mShadowRPM->registerObject(); + Sim::getRootGroup()->addObject( mShadowRPM ); + + // Setup our render pass manager + + mShadowRPM->addManager( new RenderMeshMgr(RenderPassManager::RIT_Mesh, 0.3f, 0.3f) ); + mShadowRPM->addManager( new RenderMeshMgr( RenderPassManager::RIT_Interior, 0.4f, 0.4f ) ); + //mShadowRPM->addManager( new RenderObjectMgr() ); + mShadowRPM->addManager( new RenderTerrainMgr( 0.5f, 0.5f ) ); + mShadowRPM->addManager( new RenderImposterMgr( 0.6f, 0.6f ) ); + + mActiveLights = 0; + + mTimer = PlatformTimer::create(); + + Con::addVariable( "$ShadowStats::activeMaps", TypeS32, &smActiveShadowMaps, + "The shadow stats showing the active number of shadow maps.\n" + "@ingroup AdvancedLighting\n" ); + + Con::addVariable( "$ShadowStats::updatedMaps", TypeS32, &smUpdatedShadowMaps, + "The shadow stats showing the number of shadow maps updated this frame.\n" + "@ingroup AdvancedLighting\n" ); + + Con::addVariable( "$ShadowStats::nearMaps", TypeS32, &smNearShadowMaps, + "The shadow stats showing the number of shadow maps that are close enough to be updated very frame.\n" + "@ingroup AdvancedLighting\n" ); + + Con::addVariable( "$ShadowStats::drawCalls", TypeS32, &smShadowMapsDrawCalls, + "The shadow stats showing the number of draw calls in shadow map renders for this frame.\n" + "@ingroup AdvancedLighting\n" ); + + Con::addVariable( "$ShadowStats::polyCount", TypeS32, &smShadowMapPolyCount, + "The shadow stats showing the number of triangles in shadow map renders for this frame.\n" + "@ingroup AdvancedLighting\n" ); + + Con::addVariable( "$ShadowStats::rtChanges", TypeS32, &smRenderTargetChanges, + "The shadow stats showing the number of render target changes for shadow maps in this frame.\n" + "@ingroup AdvancedLighting\n" ); + + Con::addVariable( "$ShadowStats::poolTexCount", TypeS32, &smShadowPoolTexturesCount, + "The shadow stats showing the number of shadow textures in the shadow texture pool.\n" + "@ingroup AdvancedLighting\n" ); + + Con::addVariable( "$ShadowStats::poolTexMemory", TypeF32, &smShadowPoolMemory, + "The shadow stats showing the approximate texture memory usage of the shadow map texture pool.\n" + "@ingroup AdvancedLighting\n" ); +} + +ShadowMapPass::~ShadowMapPass() +{ + SAFE_DELETE( mTimer ); + + if ( mShadowRPM ) + mShadowRPM->deleteObject(); +} + +void ShadowMapPass::render( SceneManager *sceneManager, + const SceneRenderState *diffuseState, + U32 objectMask ) +{ + PROFILE_SCOPE( ShadowMapPass_Render ); + + // Prep some shadow rendering stats. + smActiveShadowMaps = 0; + smUpdatedShadowMaps = 0; + smNearShadowMaps = 0; + GFXDeviceStatistics stats; + stats.start( GFX->getDeviceStatistics() ); + + // NOTE: The lights were already registered by SceneManager. + + // Update mLights + mLights.clear(); + mLightManager->getAllUnsortedLights( &mLights ); + mActiveLights = mLights.size(); + + // Use the per-frame incremented time for + // priority updates and to track when the + // shadow was last updated. + const U32 currTime = Sim::getCurrentTime(); + + // First do a loop thru the lights setting up the shadow + // info array for this pass. + Vector shadowMaps; + shadowMaps.reserve( mActiveLights ); + for ( U32 i = 0; i < mActiveLights; i++ ) + { + ShadowMapParams *params = mLights[i]->getExtended(); + + // Before we do anything... skip lights without shadows. + if ( !mLights[i]->getCastShadows() || smDisableShadows ) + continue; + + LightShadowMap *lsm = params->getOrCreateShadowMap(); + + // First check the visiblity query... if it wasn't + // visible skip it. + if ( lsm->wasOccluded() ) + continue; + + // Any shadow that is visible is counted as being + // active regardless if we update it or not. + ++smActiveShadowMaps; + + // Do a priority update for this shadow. + lsm->updatePriority( diffuseState, currTime ); + + shadowMaps.push_back( lsm ); + } + + // Now sort the shadow info by priority. + shadowMaps.sort( LightShadowMap::cmpPriority ); + + GFXDEBUGEVENT_SCOPE( ShadowMapPass_Render, ColorI::RED ); + + // Use a timer for tracking our shadow rendering + // budget to ensure a high precision results. + mTimer->getElapsedMs(); + mTimer->reset(); + + for ( U32 i = 0; i < shadowMaps.size(); i++ ) + { + LightShadowMap *lsm = shadowMaps[i]; + + { + GFXDEBUGEVENT_SCOPE( ShadowMapPass_Render_Shadow, ColorI::RED ); + + mShadowManager->setLightShadowMap( lsm ); + lsm->render( mShadowRPM, diffuseState ); + ++smUpdatedShadowMaps; + } + + // View dependent shadows or ones that are covering the entire + // screen are updated every frame no matter the time left in + // our shadow rendering budget. + if ( lsm->isViewDependent() || lsm->getLastScreenSize() >= 1.0f ) + { + ++smNearShadowMaps; + continue; + } + + // See if we're over our frame budget for shadow + // updates... give up completely in that case. + if ( mTimer->getElapsedMs() > smRenderBudgetMs ) + break; + } + + // Cleanup old unused textures. + LightShadowMap::releaseUnusedTextures(); + + // Update the stats. + stats.end( GFX->getDeviceStatistics() ); + smShadowMapsDrawCalls = stats.mDrawCalls; + smShadowMapPolyCount = stats.mPolyCount; + smRenderTargetChanges = stats.mRenderTargetChanges; + smShadowPoolTexturesCount = ShadowMapProfile.getStats().activeCount; + smShadowPoolMemory = ( ShadowMapProfile.getStats().activeBytes / 1024.0f ) / 1024.0f; + + // The NULL here is importaint as having it around + // will cause extra work in AdvancedLightManager::setLightInfo(). + mShadowManager->setLightShadowMap( NULL ); +} + +void ShadowRenderPassManager::addInst( RenderInst *inst ) +{ + PROFILE_SCOPE(ShadowRenderPassManager_addInst); + + if ( inst->type == RIT_Mesh || inst->type == RIT_Interior ) + { + MeshRenderInst *meshRI = static_cast( inst ); + if ( !meshRI->matInst ) + return; + + const BaseMaterialDefinition *mat = meshRI->matInst->getMaterial(); + if ( !mat->castsShadows() || mat->isTranslucent() ) + { + // Do not add this instance, return here and avoid the default behavior + // of calling up to Parent::addInst() + return; + } + } + + Parent::addInst(inst); +} \ No newline at end of file diff --git a/Engine/source/lighting/shadowMap/shadowMapPass.h b/Engine/source/lighting/shadowMap/shadowMapPass.h new file mode 100644 index 000000000..8689153cf --- /dev/null +++ b/Engine/source/lighting/shadowMap/shadowMapPass.h @@ -0,0 +1,120 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SHADOWMAPPASS_H_ +#define _SHADOWMAPPASS_H_ + +#ifndef _RENDERPASSMANAGER_H_ +#include "renderInstance/renderPassManager.h" +#endif +#ifndef _RENDERMESHMGR_H_ +#include "renderInstance/renderMeshMgr.h" +#endif +#ifndef _LIGHTINFO_H_ +#include "lighting/lightInfo.h" +#endif +#ifndef _SHADOW_COMMON_H_ +#include "lighting/shadowMap/shadowCommon.h" +#endif + +class RenderMeshMgr; +class LightShadowMap; +class LightManager; +class ShadowMapManager; +class BaseMatInstance; +class RenderObjectMgr; +class RenderTerrainMgr; +class PlatformTimer; +class ShadowRenderPassManager; + +/// ShadowMapPass, this is plugged into the SceneManager to generate +/// ShadowMaps for the scene. +class ShadowMapPass +{ +public: + + ShadowMapPass() {} // Only called by ConsoleSystem + ShadowMapPass(LightManager* LightManager, ShadowMapManager* ShadowManager); + virtual ~ShadowMapPass(); + + // + // SceneRenderPass interface + // + + /// Called to render a scene. + void render( SceneManager *sceneGraph, + const SceneRenderState *diffuseState, + U32 objectMask ); + + /// Return the type of pass this is + virtual const String& getPassType() const { return PassTypeName; }; + + /// Return our sort value. (Go first in order to have shadow maps available for RIT_Objects) + virtual F32 getSortValue() const { return 0.0f; } + + virtual bool geometryOnly() const { return true; } + + static const String PassTypeName; + + + /// Used to for debugging performance by disabling + /// shadow updates and rendering. + static bool smDisableShadows; + + static bool smDisableShadowsEditor; + static bool smDisableShadowsPref; + +private: + + static U32 smActiveShadowMaps; + static U32 smUpdatedShadowMaps; + static U32 smNearShadowMaps; + static U32 smShadowMapsDrawCalls; + static U32 smShadowMapPolyCount; + static U32 smRenderTargetChanges; + static U32 smShadowPoolTexturesCount; + static F32 smShadowPoolMemory; + + /// The milliseconds alotted for shadow map updates + /// on a per frame basis. + static U32 smRenderBudgetMs; + + PlatformTimer *mTimer; + + LightInfoList mLights; + U32 mActiveLights; + SimObjectPtr mShadowRPM; + LightManager* mLightManager; + ShadowMapManager* mShadowManager; +}; + +class ShadowRenderPassManager : public RenderPassManager +{ + typedef RenderPassManager Parent; +public: + ShadowRenderPassManager() : Parent() {} + + /// Add a RenderInstance to the list + virtual void addInst( RenderInst *inst ); +}; + +#endif // _SHADOWMAPPASS_H_ diff --git a/Engine/source/lighting/shadowMap/shadowMatHook.cpp b/Engine/source/lighting/shadowMap/shadowMatHook.cpp new file mode 100644 index 000000000..45069478d --- /dev/null +++ b/Engine/source/lighting/shadowMap/shadowMatHook.cpp @@ -0,0 +1,224 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/shadowMap/shadowMatHook.h" + +#include "materials/materialManager.h" +#include "materials/customMaterialDefinition.h" +#include "materials/materialFeatureTypes.h" +#include "materials/materialFeatureData.h" +#include "shaderGen/featureType.h" +#include "shaderGen/featureMgr.h" +#include "scene/sceneRenderState.h" +#include "terrain/terrFeatureTypes.h" + + +const MatInstanceHookType ShadowMaterialHook::Type( "ShadowMap" ); + +ShadowMaterialHook::ShadowMaterialHook() +{ + dMemset( mShadowMat, 0, sizeof( mShadowMat ) ); +} + +ShadowMaterialHook::~ShadowMaterialHook() +{ + for ( U32 i = 0; i < ShadowType_Count; i++ ) + SAFE_DELETE( mShadowMat[i] ); +} + +void ShadowMaterialHook::init( BaseMatInstance *inMat ) +{ + if( !inMat->isValid() ) + return; + + // Tweak the feature data to include just what we need. + FeatureSet features; + features.addFeature( MFT_VertTransform ); + features.addFeature( MFT_DiffuseMap ); + features.addFeature( MFT_TexAnim ); + features.addFeature( MFT_AlphaTest ); + features.addFeature( MFT_Visibility ); + + // Actually we want to include features from the inMat + // if they operate on the preTransform verts so things + // like wind/deformation effects will also affect the shadow. + const FeatureSet &inFeatures = inMat->getFeatures(); + for ( U32 i = 0; i < inFeatures.getCount(); i++ ) + { + const FeatureType& ft = inFeatures.getAt(i); + + if ( ft.getGroup() == MFG_PreTransform ) + features.addFeature( ft ); + } + + // Do instancing in shadows if we can. + if ( inFeatures.hasFeature( MFT_UseInstancing ) ) + features.addFeature( MFT_UseInstancing ); + + Material *shadowMat = (Material*)inMat->getMaterial(); + if ( dynamic_cast( shadowMat ) ) + { + // This is a custom material... who knows what it really does, but + // if it wasn't already filtered out of the shadow render then just + // give it some default depth out material. + shadowMat = MATMGR->getMaterialDefinitionByName( "AL_DefaultShadowMaterial" ); + } + + // By default we want to disable some states + // that the material might enable for us. + GFXStateBlockDesc forced; + forced.setBlend( false ); + forced.setAlphaTest( false ); + + // We should force on zwrite as the prepass + // will disable it by default. + forced.setZReadWrite( true, true ); + + // TODO: Should we render backfaces for + // shadows or does the ESM take care of + // all our acne issues? + //forced.setCullMode( GFXCullCW ); + + // Vector, and spotlights use the same shadow material. + BaseMatInstance *newMat = new ShadowMatInstance( shadowMat ); + newMat->setUserObject( inMat->getUserObject() ); + newMat->getFeaturesDelegate().bind( &ShadowMaterialHook::_overrideFeatures ); + newMat->addStateBlockDesc( forced ); + if( !newMat->init( features, inMat->getVertexFormat() ) ) + { + SAFE_DELETE( newMat ); + newMat = MATMGR->createWarningMatInstance(); + } + + mShadowMat[ShadowType_Spot] = newMat; + + newMat = new ShadowMatInstance( shadowMat ); + newMat->setUserObject( inMat->getUserObject() ); + newMat->getFeaturesDelegate().bind( &ShadowMaterialHook::_overrideFeatures ); + forced.setCullMode( GFXCullCW ); + newMat->addStateBlockDesc( forced ); + forced.cullDefined = false; + newMat->addShaderMacro( "CUBE_SHADOW_MAP", "" ); + newMat->init( features, inMat->getVertexFormat() ); + mShadowMat[ShadowType_CubeMap] = newMat; + + // A dual paraboloid shadow rendered in a single draw call. + features.addFeature( MFT_ParaboloidVertTransform ); + features.addFeature( MFT_IsSinglePassParaboloid ); + features.removeFeature( MFT_VertTransform ); + newMat = new ShadowMatInstance( shadowMat ); + newMat->setUserObject( inMat->getUserObject() ); + GFXStateBlockDesc noCull( forced ); + noCull.setCullMode( GFXCullNone ); + newMat->addStateBlockDesc( noCull ); + newMat->getFeaturesDelegate().bind( &ShadowMaterialHook::_overrideFeatures ); + newMat->init( features, inMat->getVertexFormat() ); + mShadowMat[ShadowType_DualParaboloidSinglePass] = newMat; + + // Regular dual paraboloid shadow. + features.addFeature( MFT_ParaboloidVertTransform ); + features.removeFeature( MFT_IsSinglePassParaboloid ); + features.removeFeature( MFT_VertTransform ); + newMat = new ShadowMatInstance( shadowMat ); + newMat->setUserObject( inMat->getUserObject() ); + newMat->addStateBlockDesc( forced ); + newMat->getFeaturesDelegate().bind( &ShadowMaterialHook::_overrideFeatures ); + newMat->init( features, inMat->getVertexFormat() ); + mShadowMat[ShadowType_DualParaboloid] = newMat; + + /* + // A single paraboloid shadow. + newMat = new ShadowMatInstance( startMatInstance ); + GFXStateBlockDesc noCull; + noCull.setCullMode( GFXCullNone ); + newMat->addStateBlockDesc( noCull ); + newMat->getFeaturesDelegate().bind( &ShadowMaterialHook::_overrideFeatures ); + newMat->init( features, globalFeatures, inMat->getVertexFormat() ); + mShadowMat[ShadowType_DualParaboloidSinglePass] = newMat; + */ +} + +BaseMatInstance* ShadowMaterialHook::getShadowMat( ShadowType type ) const +{ + AssertFatal( type < ShadowType_Count, "ShadowMaterialHook::getShadowMat() - Bad light type!" ); + + // The cubemap and pssm shadows use the same + // spotlight material for shadows. + if ( type == ShadowType_Spot || + type == ShadowType_PSSM ) + return mShadowMat[ShadowType_Spot]; + + // Get the specialized shadow material. + return mShadowMat[type]; +} + +void ShadowMaterialHook::_overrideFeatures( ProcessedMaterial *mat, + U32 stageNum, + MaterialFeatureData &fd, + const FeatureSet &features ) +{ + if ( stageNum != 0 ) + { + fd.features.clear(); + return; + } + + // Disable the base texture if we don't + // have alpha test enabled. + if ( !fd.features[ MFT_AlphaTest ] ) + { + fd.features.removeFeature( MFT_TexAnim ); + fd.features.removeFeature( MFT_DiffuseMap ); + } + + // HACK: Need to figure out how to enable these + // suckers without this override call! + + fd.features.setFeature( MFT_ParaboloidVertTransform, + features.hasFeature( MFT_ParaboloidVertTransform ) ); + fd.features.setFeature( MFT_IsSinglePassParaboloid, + features.hasFeature( MFT_IsSinglePassParaboloid ) ); + + // The paraboloid transform outputs linear depth, so + // it needs to use the plain depth out feature. + if ( fd.features.hasFeature( MFT_ParaboloidVertTransform ) ) + fd.features.addFeature( MFT_DepthOut ); + else + fd.features.addFeature( MFT_EyeSpaceDepthOut ); +} + +ShadowMatInstance::ShadowMatInstance( Material *mat ) + : MatInstance( *mat ) +{ + mLightmappedMaterial = mMaterial->isLightmapped(); +} + +bool ShadowMatInstance::setupPass( SceneRenderState *state, const SceneData &sgData ) +{ + // Respect SceneRenderState render flags + if( (mLightmappedMaterial && !state->renderLightmappedMeshes()) || + (!mLightmappedMaterial && !state->renderNonLightmappedMeshes()) ) + return false; + + return Parent::setupPass(state, sgData); +} diff --git a/Engine/source/lighting/shadowMap/shadowMatHook.h b/Engine/source/lighting/shadowMap/shadowMatHook.h new file mode 100644 index 000000000..4763d2f71 --- /dev/null +++ b/Engine/source/lighting/shadowMap/shadowMatHook.h @@ -0,0 +1,81 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SHADOWMATHOOK_H_ +#define _SHADOWMATHOOK_H_ + +#ifndef _MATINSTANCEHOOK_H_ +#include "materials/matInstanceHook.h" +#endif +#ifndef _MATINSTANCE_H_ +#include "materials/matInstance.h" +#endif + +// TODO: Move ShadowType enum to somewhere +// with less dependancies. +#ifndef _SHADOWMAPPASS_H_ +#include "lighting/shadowMap/shadowMapPass.h" +#endif + +class ShadowMatInstance : public MatInstance +{ + typedef MatInstance Parent; + + bool mLightmappedMaterial; +public: + ShadowMatInstance( Material *mat ); + virtual ~ShadowMatInstance() {} + + virtual bool setupPass( SceneRenderState *state, const SceneData &sgData ); +}; + +class ShadowMaterialHook : public MatInstanceHook +{ +public: + + ShadowMaterialHook(); + + // MatInstanceHook + virtual ~ShadowMaterialHook(); + virtual const MatInstanceHookType& getType() const { return Type; } + + /// The material hook type. + static const MatInstanceHookType Type; + + BaseMatInstance* getShadowMat( ShadowType type ) const; + + void init( BaseMatInstance *mat ); + +protected: + + static void _overrideFeatures( ProcessedMaterial *mat, + U32 stageNum, + MaterialFeatureData &fd, + const FeatureSet &features ); + + /// + BaseMatInstance* mShadowMat[ShadowType_Count]; + + +}; + +#endif // _SHADOWMATHOOK_H_ diff --git a/Engine/source/lighting/shadowMap/singleLightShadowMap.cpp b/Engine/source/lighting/shadowMap/singleLightShadowMap.cpp new file mode 100644 index 000000000..3d13f245c --- /dev/null +++ b/Engine/source/lighting/shadowMap/singleLightShadowMap.cpp @@ -0,0 +1,128 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "lighting/shadowMap/singleLightShadowMap.h" +#include "lighting/shadowMap/shadowMapManager.h" +#include "lighting/common/lightMapParams.h" +#include "console/console.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +//#include "scene/sceneReflectPass.h" +#include "gfx/gfxDevice.h" +#include "gfx/util/gfxFrustumSaver.h" +#include "gfx/gfxTransformSaver.h" +#include "renderInstance/renderPassManager.h" + +SingleLightShadowMap::SingleLightShadowMap( LightInfo *light ) + : LightShadowMap( light ) +{ +} + +SingleLightShadowMap::~SingleLightShadowMap() +{ + releaseTextures(); +} + +void SingleLightShadowMap::_render( RenderPassManager* renderPass, + const SceneRenderState *diffuseState ) +{ + PROFILE_SCOPE(SingleLightShadowMap_render); + + const LightMapParams *lmParams = mLight->getExtended(); + const bool bUseLightmappedGeometry = lmParams ? !lmParams->representedInLightmap || lmParams->includeLightmappedGeometryInShadow : true; + + const U32 texSize = getBestTexSize(); + + if ( mShadowMapTex.isNull() || + mTexSize != texSize ) + { + mTexSize = texSize; + + mShadowMapTex.set( mTexSize, mTexSize, + ShadowMapFormat, &ShadowMapProfile, + "SingleLightShadowMap" ); + } + + GFXFrustumSaver frustSaver; + GFXTransformSaver saver; + + MatrixF lightMatrix; + calcLightMatrices( lightMatrix, diffuseState->getFrustum() ); + lightMatrix.inverse(); + GFX->setWorldMatrix(lightMatrix); + + const MatrixF& lightProj = GFX->getProjectionMatrix(); + mWorldToLightProj = lightProj * lightMatrix; + + // Render the shadowmap! + GFX->pushActiveRenderTarget(); + mTarget->attachTexture( GFXTextureTarget::Color0, mShadowMapTex ); + mTarget->attachTexture( GFXTextureTarget::DepthStencil, + _getDepthTarget( mShadowMapTex->getWidth(), mShadowMapTex->getHeight() ) ); + GFX->setActiveRenderTarget(mTarget); + GFX->clear(GFXClearStencil | GFXClearZBuffer | GFXClearTarget, ColorI(255,255,255), 1.0f, 0); + + SceneManager* sceneManager = diffuseState->getSceneManager(); + + SceneRenderState shadowRenderState + ( + sceneManager, + SPT_Shadow, + SceneCameraState::fromGFXWithViewport( diffuseState->getViewport() ), + renderPass + ); + + shadowRenderState.getMaterialDelegate().bind( this, &LightShadowMap::getShadowMaterial ); + shadowRenderState.renderNonLightmappedMeshes( true ); + shadowRenderState.renderLightmappedMeshes( bUseLightmappedGeometry ); + shadowRenderState.setDiffuseCameraTransform( diffuseState->getCameraTransform() ); + shadowRenderState.setWorldToScreenScale( diffuseState->getWorldToScreenScale() ); + + sceneManager->renderSceneNoLights( &shadowRenderState, SHADOW_TYPEMASK ); + + _debugRender( &shadowRenderState ); + + mTarget->resolve(); + GFX->popActiveRenderTarget(); +} + +void SingleLightShadowMap::setShaderParameters(GFXShaderConstBuffer* params, LightingShaderConstants* lsc) +{ + if ( lsc->mTapRotationTexSC->isValid() ) + GFX->setTexture( lsc->mTapRotationTexSC->getSamplerRegister(), + SHADOWMGR->getTapRotationTex() ); + + ShadowMapParams *p = mLight->getExtended(); + + if ( lsc->mLightParamsSC->isValid() ) + { + Point4F lightParams( mLight->getRange().x, + p->overDarkFactor.x, + 0.0f, + 0.0f ); + params->set(lsc->mLightParamsSC, lightParams); + } + + // The softness is a factor of the texel size. + params->setSafe( lsc->mShadowSoftnessConst, p->shadowSoftness * ( 1.0f / mTexSize ) ); +} diff --git a/Engine/source/lighting/shadowMap/singleLightShadowMap.h b/Engine/source/lighting/shadowMap/singleLightShadowMap.h new file mode 100644 index 000000000..bfe5ad627 --- /dev/null +++ b/Engine/source/lighting/shadowMap/singleLightShadowMap.h @@ -0,0 +1,46 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _SINGLELIGHTSHADOWMAP_H_ +#define _SINGLELIGHTSHADOWMAP_H_ + +#ifndef _LIGHTSHADOWMAP_H_ +#include "lighting/shadowMap/lightShadowMap.h" +#endif + +// +// SingleLightShadowMap, holds the shadow map and various other things for a light. +// +// This represents everything we need to render the shadowmap for one light. +class SingleLightShadowMap : public LightShadowMap +{ +public: + SingleLightShadowMap( LightInfo *light ); + ~SingleLightShadowMap(); + + // LightShadowMap + virtual ShadowType getShadowType() const { return ShadowType_Spot; } + virtual void _render( RenderPassManager* renderPass, const SceneRenderState *diffuseState ); + virtual void setShaderParameters(GFXShaderConstBuffer* params, LightingShaderConstants* lsc); +}; + + +#endif // _SINGLELIGHTSHADOWMAP_H_ \ No newline at end of file diff --git a/Engine/source/main/main.cpp b/Engine/source/main/main.cpp new file mode 100644 index 000000000..86907b5e5 --- /dev/null +++ b/Engine/source/main/main.cpp @@ -0,0 +1,298 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_SHARED + +#ifdef WIN32 + +#include +#include + +extern "C" +{ + int (*torque_winmain)( HINSTANCE hInstance, HINSTANCE h, LPSTR lpszCmdLine, int nShow) = NULL; +}; + +int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCommandShow) +{ + char filename[4096]; + char gameLib[4096]; + + GetModuleFileNameA(NULL, filename, 4096); + filename[strlen(filename)-4] = 0; + sprintf(gameLib, "%s.dll", filename); + + HMODULE hGame = LoadLibraryA(gameLib); + + if (hGame) + torque_winmain = (int (*)(HINSTANCE hInstance, HINSTANCE h, LPSTR lpszCmdLine, int nShow))GetProcAddress(hGame, "torque_winmain"); + + char error[4096]; + if (!hGame) + { + sprintf(error, "Unable to load game library: %s. Please make sure it exists and the latest DirectX is installed.", gameLib); + MessageBoxA(NULL, error, "Error", MB_OK|MB_ICONWARNING); + return -1; + } + + if (!torque_winmain) + { + sprintf(error, "Missing torque_winmain export in game library: %s. Please make sure that it exists and the latest DirectX is installed.", gameLib); + MessageBoxA(NULL, error, "Error", MB_OK|MB_ICONWARNING); + return -1; + } + + int ret = torque_winmain(hInstance, hPrevInstance, lpszCmdLine, nCommandShow ); + + FreeLibrary(hGame); + + return ret; + +} +#endif // WIN32 + + +#ifdef __MACOSX__ + +#include +#include +#include +#include + +extern "C" { + + int (*torque_macmain)(int argc, const char **argv) = 0; + +} + +void GetBasePath(const char** cpath, const char** cname) +{ + static char path[2049]; + static char name[2049]; + + ProcessSerialNumber PSN; + ProcessInfoRec pinfo; + FSSpec pspec; + FSRef fsr; + OSStatus err; + + path[0] = 0; + name[0] = 0; + + *cpath = path; + *cname = name; + + // set up process serial number + PSN.highLongOfPSN = 0; + PSN.lowLongOfPSN = kCurrentProcess; + + // set up info block + pinfo.processInfoLength = sizeof(pinfo); + pinfo.processName = NULL; + pinfo.processAppSpec = &pspec; + + // grab the vrefnum and directory + err = GetProcessInformation(&PSN, &pinfo); + if (! err ) { + + FSSpec fss2; + + strcpy(name, &pspec.name[1]); + + err = FSMakeFSSpec(pspec.vRefNum, pspec.parID, 0, &fss2); + + if ( ! err ) { + err = FSpMakeFSRef(&fss2, &fsr); + if ( ! err ) { + err = (OSErr)FSRefMakePath(&fsr, (UInt8*)path, 2048); + } + } + } +} + +int main(int argc, const char **argv) +{ + void *gameBundle = 0; + char gameBundleFilename[2049]; + + const char* basePath; + const char* appName; + + // Get the path to our app binary and the app name + + GetBasePath(&basePath, &appName); + + if (!basePath[0] || !appName[0]) + return; + + char appNameNoDebug[2049]; + + strcpy(appNameNoDebug, appName); + + int i = strlen(appName); + while (i > 0) + { + if (!strcmp(&appName[i], "_DEBUG")) + { + appNameNoDebug[i] = 0; + break; + } + + i--; + } + + sprintf(gameBundleFilename, "%s.app/Contents/Frameworks/%s Bundle.bundle/Contents/MacOS/%s Bundle", appName, appNameNoDebug, appNameNoDebug); + + // first see if the current directory is set properly + gameBundle = dlopen(gameBundleFilename, RTLD_LAZY | RTLD_LOCAL); + + if (!gameBundle) + { + // Couldn't load the game bundle... so, using the path to the bundle binary fix up the cwd + + if (basePath[0]) { + chdir( basePath ); + chdir( "../../../" ); + } + + // and try again + gameBundle = dlopen( gameBundleFilename, RTLD_LAZY | RTLD_LOCAL); + } + + if (!gameBundle) + return -1; + + torque_macmain = (int (*)(int argc, const char **argv)) dlsym(gameBundle, "torque_macmain"); + + if (!torque_macmain) + return -1; + + return torque_macmain(argc, argv); +} + +#endif // __MACOSX + +#ifdef __linux__ + +#include +#include +#include +#include + +extern "C" +{ + int (*torque_unixmain)(int argc, const char **argv) = NULL; + void(*setExePathName)(const char *exePathName) = NULL; +} + +int main(int argc, const char **argv) +{ + // assume bin name is in argv[0] + int len = strlen(argv[0]); + char *libName = new char[len+4]; // len + .so + NUL + + strcpy(libName, argv[0]); + strcat(libName, ".so"); + + // try to load the game lib + void *gameLib = dlopen(libName, RTLD_LAZY | RTLD_LOCAL); + delete [] libName; + + if(gameLib == NULL) + { + printf("%s\n", dlerror()); + return -1; + } + + // set the filename of the exe image + setExePathName = (void(*)(const char *)) dlsym(gameLib, "setExePathName"); + if(setExePathName == NULL) + { + printf("%s\n", dlerror()); + return -1; + } + setExePathName(argv[0]); + + // try to load the lib entry point + torque_unixmain = (int(*)(int argc, const char **argv)) dlsym(gameLib, "torque_unixmain"); + + if(torque_unixmain == NULL) + { + printf("%s\n", dlerror()); + return -1; + } + + // Go! + return torque_unixmain(argc, argv); +} +#endif // __linux__ + + +#else //static exe build + +#include "platform/platform.h" +#include "app/mainLoop.h" +#include "T3D/gameFunctions.h" + +// Entry point for your game. +// +// This is build by default using the "StandardMainLoop" toolkit. Feel free +// to bring code over directly as you need to modify or extend things. You +// will need to merge against future changes to the SML code if you do this. +S32 TorqueMain(S32 argc, const char **argv) +{ + // Some handy debugging code: + // if (argc == 1) { + // static const char* argvFake[] = { "dtest.exe", "-jload", "test.jrn" }; + // argc = 3; + // argv = argvFake; + // } + + // Memory::enableLogging("testMem.log"); + // Memory::setBreakAlloc(104717); + + // Initialize the subsystems. + StandardMainLoop::init(); + + // Handle any command line args. + if(!StandardMainLoop::handleCommandLine(argc, argv)) + { + Platform::AlertOK("Error", "Failed to initialize game, shutting down."); + + return 1; + } + + // Main loop + while(StandardMainLoop::doMainLoop()); + + // Clean everything up. + StandardMainLoop::shutdown(); + + // Do we need to restart? + if( StandardMainLoop::requiresRestart() ) + Platform::restartInstance(); + + // Return. + return 0; +} + +#endif //TORQUE_SHARED diff --git a/Engine/source/materials/baseMatInstance.cpp b/Engine/source/materials/baseMatInstance.cpp new file mode 100644 index 000000000..6ddcc1994 --- /dev/null +++ b/Engine/source/materials/baseMatInstance.cpp @@ -0,0 +1,80 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "materials/baseMatInstance.h" + +#include "core/util/safeDelete.h" + + +BaseMatInstance::~BaseMatInstance() +{ + deleteAllHooks(); +} + +void BaseMatInstance::addHook( MatInstanceHook *hook ) +{ + AssertFatal( hook, "BaseMatInstance::addHook() - Got null hook!" ); + + const MatInstanceHookType &type = hook->getType(); + + while ( mHooks.size() <= type ) + mHooks.push_back( NULL ); + + delete mHooks[type]; + mHooks[type] = hook; +} + +MatInstanceHook* BaseMatInstance::getHook( const MatInstanceHookType &type ) const +{ + if ( type >= mHooks.size() ) + return NULL; + + return mHooks[ type ]; +} + +void BaseMatInstance::deleteHook( const MatInstanceHookType &type ) +{ + if ( type >= mHooks.size() ) + return; + + delete mHooks[ type ]; + mHooks[ type ] = NULL; +} + +U32 BaseMatInstance::deleteAllHooks() +{ + U32 deleteCount = 0; + + Vector::iterator iter = mHooks.begin(); + for ( ; iter != mHooks.end(); iter++ ) + { + if ( *iter ) + { + delete (*iter); + (*iter) = NULL; + ++deleteCount; + } + } + + return deleteCount; +} \ No newline at end of file diff --git a/Engine/source/materials/baseMatInstance.h b/Engine/source/materials/baseMatInstance.h new file mode 100644 index 000000000..d15c9525c --- /dev/null +++ b/Engine/source/materials/baseMatInstance.h @@ -0,0 +1,254 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _BASEMATINSTANCE_H_ +#define _BASEMATINSTANCE_H_ + +#ifndef _TSIGNAL_H_ +#include "core/util/tSignal.h" +#endif +#ifndef _BASEMATERIALDEFINITION_H_ +#include "materials/baseMaterialDefinition.h" +#endif +#ifndef _MATERIALPARAMETERS_H_ +#include "materials/materialParameters.h" +#endif +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif +#ifndef _GFXENUMS_H_ +#include "gfx/gfxEnums.h" +#endif +#ifndef _GFXSHADER_H_ +#include "gfx/gfxShader.h" +#endif +#ifndef _MATERIALFEATUREDATA_H_ +#include "materials/materialFeatureData.h" +#endif +#ifndef _MATINSTANCEHOOK_H_ +#include "materials/matInstanceHook.h" +#endif +#ifndef _MATSTATEHINT_H_ +#include "materials/matStateHint.h" +#endif + +struct RenderPassData; +class GFXVertexBufferHandleBase; +class GFXPrimitiveBufferHandle; +struct SceneData; +class SceneRenderState; +struct GFXStateBlockDesc; +class GFXVertexFormat; +class MatrixSet; +class ProcessedMaterial; + + +/// +class BaseMatInstance +{ +protected: + + /// The array of active material hooks indexed + /// by a MatInstanceHookType. + Vector mHooks; + + /// + MatFeaturesDelegate mFeaturesDelegate; + + /// Should be true if init has been called and it succeeded. + /// It is up to the derived class to set this variable appropriately. + bool mIsValid; + + /// This is set by initialization and used by the prepass. + bool mHasNormalMaps; + +public: + + virtual ~BaseMatInstance(); + + /// @param features The features you want to allow for this material. + /// + /// @param vertexFormat The vertex format on which this material will be rendered. + /// + /// @see GFXVertexFormat + /// @see FeatureSet + virtual bool init( const FeatureSet &features, + const GFXVertexFormat *vertexFormat ) = 0; + + /// Reinitializes the material using the previous + /// initialization parameters. + /// @see init + virtual bool reInit() = 0; + + /// Returns true if init has been successfully called. + /// It is up to the derived class to set this value properly. + bool isValid() { return mIsValid; } + + /// Adds this stateblock to the base state block + /// used during initialization. + /// @see init + virtual void addStateBlockDesc(const GFXStateBlockDesc& desc) = 0; + + /// Updates the state blocks for this material. + virtual void updateStateBlocks() = 0; + + /// Adds a shader macro which will be passed to the shader + /// during initialization. + /// @see init + virtual void addShaderMacro( const String &name, const String &value ) = 0; + + /// Get a MaterialParameters block for this BaseMatInstance, + /// caller is responsible for freeing it. + virtual MaterialParameters* allocMaterialParameters() = 0; + + /// Set the current parameters for this BaseMatInstance + virtual void setMaterialParameters(MaterialParameters* param) = 0; + + /// Get the current parameters for this BaseMatInstance (BaseMatInstances are created with a default active + /// MaterialParameters which is managed by BaseMatInstance. + virtual MaterialParameters* getMaterialParameters() = 0; + + /// Returns a MaterialParameterHandle for name. + virtual MaterialParameterHandle* getMaterialParameterHandle(const String& name) = 0; + + /// Sets up the next rendering pass for this material. It is + /// typically called like so... + /// + ///@code + /// while( mat->setupPass( state, sgData ) ) + /// { + /// mat->setTransforms(...); + /// mat->setSceneInfo(...); + /// ... + /// GFX->drawPrimitive(); + /// } + ///@endcode + /// + virtual bool setupPass( SceneRenderState *state, const SceneData &sgData ) = 0; + + /// This initializes the material transforms and should be + /// called after setupPass() within the pass loop. + /// @see setupPass + virtual void setTransforms( const MatrixSet &matrixSet, SceneRenderState *state ) = 0; + + /// This initializes various material scene state settings and + /// should be called after setupPass() within the pass loop. + /// @see setupPass + virtual void setSceneInfo( SceneRenderState *state, const SceneData &sgData ) = 0; + + /// This is normally called from within setupPass() automatically, so its + /// unnecessary to do so manually unless a texture stage has changed. If + /// so it should be called after setupPass() within the pass loop. + /// @see setupPass + virtual void setTextureStages(SceneRenderState *, const SceneData &sgData ) = 0; + + /// Sets the vertex and primitive buffers as well as the instancing + /// stream buffer for the current material if the material is instanced. + virtual void setBuffers( GFXVertexBufferHandleBase *vertBuffer, GFXPrimitiveBufferHandle *primBuffer ) = 0; + + /// Returns true if this material is instanced. + virtual bool isInstanced() const = 0; + + /// Used to increment the instance buffer for this material. + virtual bool stepInstance() = 0; + + /// Returns true if the material is forward lit and requires + /// a list of lights which affect it when rendering. + virtual bool isForwardLit() const = 0; + + /// Sets a SimObject which will passed into ShaderFeature::createConstHandles. + /// Normal features do not make use of this, it is for special class specific + /// or user designed features. + virtual void setUserObject( SimObject *userObject ) = 0; + virtual SimObject* getUserObject() const = 0; + + /// Returns the material this instance is based on. + virtual BaseMaterialDefinition* getMaterial() = 0; + + // BTRTODO: This stuff below should probably not be in BaseMatInstance + virtual bool hasGlow() = 0; + + virtual U32 getCurPass() = 0; + + virtual U32 getCurStageNum() = 0; + + virtual RenderPassData *getPass(U32 pass) = 0; + + /// Returns the state hint which can be used for + /// sorting and fast comparisions of the equality + /// of a material instance. + virtual const MatStateHint& getStateHint() const = 0; + + /// Returns the active features in use by this material. + /// @see getRequestedFeatures + virtual const FeatureSet& getFeatures() const = 0; + + /// Returns the features that were requested at material + /// creation time which may differ from the active features. + /// @see getFeatures + virtual const FeatureSet& getRequestedFeatures() const = 0; + + virtual const GFXVertexFormat* getVertexFormat() const = 0; + + virtual void dumpShaderInfo() const = 0; + + /// Fast test for use of normal maps in this material. + bool hasNormalMap() const { return mHasNormalMaps; } + + /// + MatFeaturesDelegate& getFeaturesDelegate() { return mFeaturesDelegate; } + + /// Returns true if this MatInstance is built from a CustomMaterial. + virtual bool isCustomMaterial() const = 0; + + /// @name Material Hook functions + /// @{ + + /// + void addHook( MatInstanceHook *hook ); + + /// Helper function for getting a hook. + /// @see getHook + template + inline HOOK* getHook() { return (HOOK*)getHook( HOOK::Type ); } + + /// + MatInstanceHook* getHook( const MatInstanceHookType &type ) const; + + /// + void deleteHook( const MatInstanceHookType &type ); + + /// + U32 deleteAllHooks(); + + /// @} + + virtual const GFXStateBlockDesc &getUserStateBlock() const = 0; + +}; + +#endif /// _BASEMATINSTANCE_H_ + + + + + + diff --git a/Engine/source/materials/baseMaterialDefinition.h b/Engine/source/materials/baseMaterialDefinition.h new file mode 100644 index 000000000..c1c033015 --- /dev/null +++ b/Engine/source/materials/baseMaterialDefinition.h @@ -0,0 +1,42 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _BASEMATERIALDEFINITION_H_ +#define _BASEMATERIALDEFINITION_H_ + +#ifndef _SIMOBJECT_H_ +#include "console/simObject.h" +#endif + +class BaseMatInstance; + +class BaseMaterialDefinition : public SimObject +{ + typedef SimObject Parent; +public: + virtual BaseMatInstance* createMatInstance() = 0; + virtual bool isTranslucent() const = 0; + virtual bool isDoubleSided() const = 0; + virtual bool isLightmapped() const = 0; + virtual bool castsShadows() const = 0; +}; + +#endif // _BASEMATERIALDEFINITION_H_ diff --git a/Engine/source/materials/customMaterialDefinition.cpp b/Engine/source/materials/customMaterialDefinition.cpp new file mode 100644 index 000000000..c61a10a34 --- /dev/null +++ b/Engine/source/materials/customMaterialDefinition.cpp @@ -0,0 +1,179 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "materials/customMaterialDefinition.h" + +#include "materials/materialManager.h" +#include "console/consoleTypes.h" +#include "materials/shaderData.h" +#include "gfx/sim/cubemapData.h" +#include "gfx/gfxCubemap.h" +#include "gfx/sim/gfxStateBlockData.h" + + +//**************************************************************************** +// Custom Material +//**************************************************************************** +IMPLEMENT_CONOBJECT(CustomMaterial); + +ConsoleDocClass( CustomMaterial, + "@brief Material object which provides more control over surface properties.\n\n" + + "CustomMaterials allow the user to specify their own shaders via the ShaderData datablock. " + "Because CustomMaterials are derived from Materials, they can hold a lot of the same properties. " + "It is up to the user to code how these properties are used.\n\n" + + "@tsexample\n" + "singleton CustomMaterial( WaterBasicMat )\n" + "{\n" + " sampler[\"reflectMap\"] = \"$reflectbuff\";\n" + " sampler[\"refractBuff\"] = \"$backbuff\";\n\n" + " cubemap = NewLevelSkyCubemap;\n" + " shader = WaterBasicShader;\n" + " stateBlock = WaterBasicStateBlock;\n" + " version = 2.0;\n" + "};\n" + "@endtsexample\n\n" + + "@see Material, GFXStateBlockData, ShaderData\n\n" + + "@ingroup Materials\n" +); + +//---------------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------------- +CustomMaterial::CustomMaterial() +{ + mFallback = NULL; + mMaxTex = 0; + mVersion = 1.1f; + mTranslucent = false; + dMemset( mFlags, 0, sizeof( mFlags ) ); + mShaderData = NULL; + mRefract = false; + mStateBlockData = NULL; + mForwardLit = false; +} + +//-------------------------------------------------------------------------- +// Init fields +//-------------------------------------------------------------------------- +void CustomMaterial::initPersistFields() +{ + addField("version", TypeF32, Offset(mVersion, CustomMaterial), + "@brief Specifies pixel shader version for hardware.\n\n" + "Valid pixel shader versions include 2.0, 3.0, etc. " + "@note All features aren't compatible with all pixel shader versions."); + addField("fallback", TYPEID< Material >(), Offset(mFallback, CustomMaterial), + "@brief Alternate material for targeting lower end hardware.\n\n" + "If the CustomMaterial requires a higher pixel shader version than the one " + "it's using, it's fallback Material will be processed instead. " + "If the fallback material wasn't defined, Torque 3D will assert and attempt to use a very " + "basic material in it's place.\n\n"); + addField("shader", TypeRealString, Offset(mShaderDataName, CustomMaterial), + "@brief Name of the ShaderData to use for this effect.\n\n"); + addField("stateBlock", TYPEID< GFXStateBlockData >(), Offset(mStateBlockData, CustomMaterial), + "@brief Name of a GFXStateBlockData for this effect.\n\n"); + addField("target", TypeRealString, Offset(mOutputTarget, CustomMaterial), + "@brief String identifier of this material's target texture."); + addField("forwardLit", TypeBool, Offset(mForwardLit, CustomMaterial), + "@brief Determines if the material should recieve lights in Basic Lighting. " + "Has no effect in Advanced Lighting.\n\n"); + + Parent::initPersistFields(); +} + +//-------------------------------------------------------------------------- +// On add - verify data settings +//-------------------------------------------------------------------------- +bool CustomMaterial::onAdd() +{ + if (Parent::onAdd() == false) + return false; + + mShaderData = dynamic_cast(Sim::findObject( mShaderDataName ) ); + if(mShaderDataName.isNotEmpty() && mShaderData == NULL) + { + logError("Failed to find ShaderData %s", mShaderDataName.c_str()); + return false; + } + + const char* samplerDecl = "sampler"; + S32 i = 0; + for (SimFieldDictionaryIterator itr(getFieldDictionary()); *itr; ++itr) + { + SimFieldDictionary::Entry* entry = *itr; + if (dStrStartsWith(entry->slotName, samplerDecl)) + { + if (i >= MAX_TEX_PER_PASS) + { + logError("Too many sampler declarations, you may only have %i", MAX_TEX_PER_PASS); + return false; + } + + if (dStrlen(entry->slotName) == dStrlen(samplerDecl)) + { + logError("sampler declarations must have a sampler name, e.g. sampler[\"diffuseMap\"]"); + return false; + } + + mSamplerNames[i] = entry->slotName + dStrlen(samplerDecl); + mSamplerNames[i].insert(0, '$'); + mTexFilename[i] = entry->value; + ++i; + } + } + + return true; +} + +//-------------------------------------------------------------------------- +// On remove +//-------------------------------------------------------------------------- +void CustomMaterial::onRemove() +{ + Parent::onRemove(); +} + +//-------------------------------------------------------------------------- +// Map this material to the texture specified in the "mapTo" data variable +//-------------------------------------------------------------------------- +void CustomMaterial::_mapMaterial() +{ + if( String(getName()).isEmpty() ) + { + Con::warnf( "Unnamed Material! Could not map to: %s", mMapTo.c_str() ); + return; + } + + if( mMapTo.isEmpty() ) + return; + + MATMGR->mapMaterial(mMapTo, getName()); +} + +const GFXStateBlockData* CustomMaterial::getStateBlockData() const +{ + return mStateBlockData; +} diff --git a/Engine/source/materials/customMaterialDefinition.h b/Engine/source/materials/customMaterialDefinition.h new file mode 100644 index 000000000..879ffd6b8 --- /dev/null +++ b/Engine/source/materials/customMaterialDefinition.h @@ -0,0 +1,78 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _CUSTOMMATERIALDEFINITION_H_ +#define _CUSTOMMATERIALDEFINITION_H_ + +#ifndef _MATERIALDEFINITION_H_ +#include "materials/materialDefinition.h" +#endif + +class ShaderData; +class GFXStateBlockData; + +//************************************************************************** +// Custom Material +//************************************************************************** +class CustomMaterial : public Material +{ + typedef Material Parent; +public: + enum CustomConsts + { + MAX_PASSES = 8, + NUM_FALLBACK_VERSIONS = 2, + }; + + FileName mTexFilename[MAX_TEX_PER_PASS]; + String mSamplerNames[MAX_TEX_PER_PASS]; + String mOutputTarget; + Material* mFallback; + bool mForwardLit; + + F32 mVersion; // 0 = legacy, 1 = DX 8.1, 2 = DX 9.0 + bool mRefract; + ShaderData* mShaderData; + + CustomMaterial(); + const GFXStateBlockData* getStateBlockData() const; + + // + // SimObject interface + // + virtual bool onAdd(); + virtual void onRemove(); + + // + // ConsoleObject interface + // + static void initPersistFields(); + DECLARE_CONOBJECT(CustomMaterial); +protected: + U32 mMaxTex; + String mShaderDataName; + U32 mFlags[MAX_TEX_PER_PASS]; + GFXStateBlockData* mStateBlockData; + + virtual void _mapMaterial(); +}; + +#endif diff --git a/Engine/source/materials/matInstance.cpp b/Engine/source/materials/matInstance.cpp new file mode 100644 index 000000000..b7be80f3e --- /dev/null +++ b/Engine/source/materials/matInstance.cpp @@ -0,0 +1,561 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "materials/matInstance.h" + +#include "materials/materialManager.h" +#include "materials/customMaterialDefinition.h" +#include "materials/processedMaterial.h" +#include "materials/processedFFMaterial.h" +#include "materials/processedShaderMaterial.h" +#include "materials/processedCustomMaterial.h" +#include "materials/materialFeatureTypes.h" +#include "shaderGen/featureMgr.h" +#include "gfx/gfxDevice.h" +#include "gfx/sim/cubemapData.h" +#include "gfx/gfxCubemap.h" +#include "core/util/safeDelete.h" + +class MatInstParameters; + +class MatInstanceParameterHandle : public MaterialParameterHandle +{ +public: + virtual ~MatInstanceParameterHandle() {} + MatInstanceParameterHandle(const String& name); + + void loadHandle(ProcessedMaterial* pmat); + + // MaterialParameterHandle interface + const String& getName() const { return mName; } + virtual bool isValid() const; + virtual S32 getSamplerRegister( U32 pass ) const; +private: + friend class MatInstParameters; + String mName; + MaterialParameterHandle* mProcessedHandle; +}; + +MatInstanceParameterHandle::MatInstanceParameterHandle(const String& name) +{ + mName = name; + mProcessedHandle = NULL; +} + +bool MatInstanceParameterHandle::isValid() const +{ + return mProcessedHandle && mProcessedHandle->isValid(); +} + +S32 MatInstanceParameterHandle::getSamplerRegister( U32 pass ) const +{ + if ( !mProcessedHandle ) + return -1; + return mProcessedHandle->getSamplerRegister( pass ); +} + +void MatInstanceParameterHandle::loadHandle(ProcessedMaterial* pmat) +{ + mProcessedHandle = pmat->getMaterialParameterHandle(mName); +} + +MatInstParameters::MatInstParameters() +{ + mOwnParameters = false; + mParameters = NULL; +} + +MatInstParameters::MatInstParameters(MaterialParameters* matParams) +{ + mOwnParameters = false; + mParameters = matParams; +} + +void MatInstParameters::loadParameters(ProcessedMaterial* pmat) +{ + mOwnParameters = true; + mParameters = pmat->allocMaterialParameters(); +} + +MatInstParameters::~MatInstParameters() +{ + if (mOwnParameters) + SAFE_DELETE(mParameters); +} + +const Vector& MatInstParameters::getShaderConstDesc() const +{ + return mParameters->getShaderConstDesc(); +} + +U32 MatInstParameters::getAlignmentValue(const GFXShaderConstType constType) +{ + return mParameters->getAlignmentValue(constType); +} + +#define MATINSTPARAMSET(handle, f) \ + if (!mParameters) \ + return; \ + AssertFatal(dynamic_cast(handle), "Invalid handle type!"); \ + MatInstanceParameterHandle* mph = static_cast(handle); \ + mParameters->set(mph->mProcessedHandle, f); \ + +void MatInstParameters::set(MaterialParameterHandle* handle, const F32 f) +{ + MATINSTPARAMSET(handle, f); +} + +void MatInstParameters::set(MaterialParameterHandle* handle, const Point2F& fv) +{ + MATINSTPARAMSET(handle, fv); +} + +void MatInstParameters::set(MaterialParameterHandle* handle, const Point3F& fv) +{ + MATINSTPARAMSET(handle, fv); +} + +void MatInstParameters::set(MaterialParameterHandle* handle, const Point4F& fv) +{ + MATINSTPARAMSET(handle, fv); +} + +void MatInstParameters::set(MaterialParameterHandle* handle, const ColorF& fv) +{ + MATINSTPARAMSET(handle, fv); +} + +void MatInstParameters::set(MaterialParameterHandle* handle, const S32 f) +{ + MATINSTPARAMSET(handle, f); +} + +void MatInstParameters::set(MaterialParameterHandle* handle, const Point2I& fv) +{ + MATINSTPARAMSET(handle, fv); +} + +void MatInstParameters::set(MaterialParameterHandle* handle, const Point3I& fv) +{ + MATINSTPARAMSET(handle, fv); +} + +void MatInstParameters::set(MaterialParameterHandle* handle, const Point4I& fv) +{ + MATINSTPARAMSET(handle, fv); +} + +void MatInstParameters::set(MaterialParameterHandle* handle, const AlignedArray& fv) +{ + MATINSTPARAMSET(handle, fv); +} + +void MatInstParameters::set(MaterialParameterHandle* handle, const AlignedArray& fv) +{ + MATINSTPARAMSET(handle, fv); +} + +void MatInstParameters::set(MaterialParameterHandle* handle, const AlignedArray& fv) +{ + MATINSTPARAMSET(handle, fv); +} + +void MatInstParameters::set(MaterialParameterHandle* handle, const AlignedArray& fv) +{ + MATINSTPARAMSET(handle, fv); +} + +void MatInstParameters::set(MaterialParameterHandle* handle, const AlignedArray& fv) +{ + MATINSTPARAMSET(handle, fv); +} + +void MatInstParameters::set(MaterialParameterHandle* handle, const AlignedArray& fv) +{ + MATINSTPARAMSET(handle, fv); +} + +void MatInstParameters::set(MaterialParameterHandle* handle, const AlignedArray& fv) +{ + MATINSTPARAMSET(handle, fv); +} + +void MatInstParameters::set(MaterialParameterHandle* handle, const AlignedArray& fv) +{ + MATINSTPARAMSET(handle, fv); +} + +void MatInstParameters::set(MaterialParameterHandle* handle, const MatrixF& mat, const GFXShaderConstType matrixType) +{ + AssertFatal(dynamic_cast(handle), "Invalid handle type!"); + MatInstanceParameterHandle* mph = static_cast(handle); + mParameters->set(mph->mProcessedHandle, mat, matrixType); +} + +void MatInstParameters::set(MaterialParameterHandle* handle, const MatrixF* mat, const U32 arraySize, const GFXShaderConstType matrixType) +{ + AssertFatal(dynamic_cast(handle), "Invalid handle type!"); + MatInstanceParameterHandle* mph = static_cast(handle); + mParameters->set(mph->mProcessedHandle, mat, arraySize, matrixType); +} +#undef MATINSTPARAMSET + +//**************************************************************************** +// Material Instance +//**************************************************************************** +MatInstance::MatInstance( Material &mat ) +{ + VECTOR_SET_ASSOCIATION( mCurrentHandles ); + VECTOR_SET_ASSOCIATION( mCurrentParameters ); + + mMaterial = &mat; + + mCreatedFromCustomMaterial = (dynamic_cast(&mat) != NULL); + + construct(); +} + +//---------------------------------------------------------------------------- +// Construct +//---------------------------------------------------------------------------- +void MatInstance::construct() +{ + mUserObject = NULL; + mCurPass = -1; + mProcessedMaterial = false; + mVertexFormat = NULL; + mMaxStages = 1; + mActiveParameters = NULL; + mDefaultParameters = NULL; + mHasNormalMaps = false; + mIsForwardLit = false; + mIsValid = false; + + MATMGR->_track(this); +} + +//---------------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------------- +MatInstance::~MatInstance() +{ + SAFE_DELETE(mProcessedMaterial); + SAFE_DELETE(mDefaultParameters); + for (U32 i = 0; i < mCurrentHandles.size(); i++) + SAFE_DELETE(mCurrentHandles[i]); + + MATMGR->_untrack(this); +} + +//---------------------------------------------------------------------------- +// Init +//---------------------------------------------------------------------------- +bool MatInstance::init( const FeatureSet &features, + const GFXVertexFormat *vertexFormat ) +{ + AssertFatal( vertexFormat, "MatInstance::init - Got null vertex format!" ); + + mFeatureList = features; + mVertexFormat = vertexFormat; + + SAFE_DELETE(mProcessedMaterial); + mIsValid = processMaterial(); + + return mIsValid; +} + + +//---------------------------------------------------------------------------- +// reInitialize +//---------------------------------------------------------------------------- +bool MatInstance::reInit() +{ + SAFE_DELETE(mProcessedMaterial); + deleteAllHooks(); + mIsValid = processMaterial(); + + if ( mIsValid ) + { + for (U32 i = 0; i < mCurrentHandles.size(); i++) + mCurrentHandles[i]->loadHandle(mProcessedMaterial); + + for (U32 i = 0; i < mCurrentParameters.size(); i++) + mCurrentParameters[i]->loadParameters(mProcessedMaterial); + } + + return mIsValid; +} + +//---------------------------------------------------------------------------- +// Process stages +//---------------------------------------------------------------------------- +bool MatInstance::processMaterial() +{ + AssertFatal( mMaterial, "MatInstance::processMaterial - Got null material!" ); + //AssertFatal( mVertexFormat, "MatInstance::processMaterial - Got null vertex format!" ); + if ( !mMaterial || !mVertexFormat ) + return false; + + SAFE_DELETE(mDefaultParameters); + + CustomMaterial *custMat = NULL; + + if( dynamic_cast(mMaterial) ) + { + F32 pixVersion = GFX->getPixelShaderVersion(); + custMat = static_cast(mMaterial); + if ((custMat->mVersion > pixVersion) || (custMat->mVersion == 0.0)) + { + if(custMat->mFallback) + { + mMaterial = custMat->mFallback; + return processMaterial(); + } + else + { + AssertWarn(custMat->mVersion == 0.0f, avar("Can't load CustomMaterial %s for %s, using generic FF fallback", + String(mMaterial->getName()).isEmpty() ? "Unknown" : mMaterial->getName(), custMat->mMapTo.c_str())); + mProcessedMaterial = new ProcessedFFMaterial(*mMaterial); + } + } + else + mProcessedMaterial = new ProcessedCustomMaterial(*mMaterial); + } + else if(GFX->getPixelShaderVersion() > 0.001) + mProcessedMaterial = getShaderMaterial(); + else + mProcessedMaterial = new ProcessedFFMaterial(*mMaterial); + + if (mProcessedMaterial) + { + mProcessedMaterial->addStateBlockDesc( mUserDefinedState ); + mProcessedMaterial->setShaderMacros( mUserMacros ); + mProcessedMaterial->setUserObject( mUserObject ); + + FeatureSet features( mFeatureList ); + features.exclude( MATMGR->getExclusionFeatures() ); + + if( !mProcessedMaterial->init(features, mVertexFormat, mFeaturesDelegate) ) + { + Con::errorf( "Failed to initialize material '%s'", getMaterial()->getName() ); + SAFE_DELETE( mProcessedMaterial ); + return false; + } + + mDefaultParameters = new MatInstParameters(mProcessedMaterial->getDefaultMaterialParameters()); + mActiveParameters = mDefaultParameters; + + const FeatureSet &finalFeatures = mProcessedMaterial->getFeatures(); + mHasNormalMaps = finalFeatures.hasFeature( MFT_NormalMap ); + + mIsForwardLit = ( custMat && custMat->mForwardLit ) || + ( !finalFeatures.hasFeature( MFT_IsEmissive ) && + finalFeatures.hasFeature( MFT_ForwardShading ) ); + + return true; + } + + return false; +} + +const MatStateHint& MatInstance::getStateHint() const +{ + if ( mProcessedMaterial ) + return mProcessedMaterial->getStateHint(); + else + return MatStateHint::Default; +} + +ProcessedMaterial* MatInstance::getShaderMaterial() +{ + return new ProcessedShaderMaterial(*mMaterial); +} + +void MatInstance::addStateBlockDesc(const GFXStateBlockDesc& desc) +{ + mUserDefinedState = desc; +} + +void MatInstance::updateStateBlocks() +{ + if ( mProcessedMaterial ) + mProcessedMaterial->updateStateBlocks(); +} + +void MatInstance::addShaderMacro( const String &name, const String &value ) +{ + // Check to see if we already have this macro. + Vector::iterator iter = mUserMacros.begin(); + for ( ; iter != mUserMacros.end(); iter++ ) + { + if ( iter->name == name ) + { + iter->value = value; + return; + } + } + + // Add a new macro. + mUserMacros.increment(); + mUserMacros.last().name = name; + mUserMacros.last().value = value; +} + +//---------------------------------------------------------------------------- +// Setup pass - needs scenegraph data because the lightmap will change across +// several materials. +//---------------------------------------------------------------------------- +bool MatInstance::setupPass(SceneRenderState * state, const SceneData &sgData ) +{ + PROFILE_SCOPE( MatInstance_SetupPass ); + + if( !mProcessedMaterial ) + return false; + + ++mCurPass; + + if ( !mProcessedMaterial->setupPass( state, sgData, mCurPass ) ) + { + mCurPass = -1; + return false; + } + + return true; +} + +void MatInstance::setTransforms(const MatrixSet &matrixSet, SceneRenderState *state) +{ + PROFILE_SCOPE(MatInstance_setTransforms); + mProcessedMaterial->setTransforms(matrixSet, state, getCurPass()); +} + +void MatInstance::setSceneInfo(SceneRenderState * state, const SceneData& sgData) +{ + PROFILE_SCOPE(MatInstance_setSceneInfo); + mProcessedMaterial->setSceneInfo(state, sgData, getCurPass()); +} + +void MatInstance::setBuffers(GFXVertexBufferHandleBase* vertBuffer, GFXPrimitiveBufferHandle* primBuffer) +{ + mProcessedMaterial->setBuffers(vertBuffer, primBuffer); +} + +void MatInstance::setTextureStages(SceneRenderState * state, const SceneData &sgData ) +{ + PROFILE_SCOPE(MatInstance_setTextureStages); + mProcessedMaterial->setTextureStages(state, sgData, getCurPass()); +} + +bool MatInstance::isInstanced() const +{ + return mProcessedMaterial->getFeatures().hasFeature( MFT_UseInstancing ); +} + +bool MatInstance::stepInstance() +{ + AssertFatal( isInstanced(), "MatInstance::stepInstance - This material isn't instanced!" ); + AssertFatal( mCurPass >= 0, "MatInstance::stepInstance - Must be within material setup pass!" ); + + return mProcessedMaterial->stepInstance(); +} + +U32 MatInstance::getCurStageNum() +{ + return mProcessedMaterial->getStageFromPass(getCurPass()); +} + +RenderPassData* MatInstance::getPass(U32 pass) +{ + return mProcessedMaterial->getPass(pass); +} + +bool MatInstance::hasGlow() +{ + if( mProcessedMaterial ) + return mProcessedMaterial->hasGlow(); + else + return false; +} + +const FeatureSet& MatInstance::getFeatures() const +{ + return mProcessedMaterial->getFeatures(); +} + +MaterialParameterHandle* MatInstance::getMaterialParameterHandle(const String& name) +{ + AssertFatal(mProcessedMaterial, "Not init'ed!"); + for (U32 i = 0; i < mCurrentHandles.size(); i++) + { + if (mCurrentHandles[i]->getName().equal(name)) + { + return mCurrentHandles[i]; + } + } + MatInstanceParameterHandle* mph = new MatInstanceParameterHandle(name); + mph->loadHandle(mProcessedMaterial); + mCurrentHandles.push_back(mph); + return mph; +} + +MaterialParameters* MatInstance::allocMaterialParameters() +{ + AssertFatal(mProcessedMaterial, "Not init'ed!"); + MatInstParameters* mip = new MatInstParameters(); + mip->loadParameters(mProcessedMaterial); + mCurrentParameters.push_back(mip); + return mip; +} + +void MatInstance::setMaterialParameters(MaterialParameters* param) +{ + AssertFatal(mProcessedMaterial, "Not init'ed!"); + mProcessedMaterial->setMaterialParameters(param, mCurPass); + AssertFatal(dynamic_cast(param), "Incorrect param type!"); + mActiveParameters = static_cast(param); +} + +MaterialParameters* MatInstance::getMaterialParameters() +{ + AssertFatal(mProcessedMaterial, "Not init'ed!"); + return mActiveParameters; +} + +void MatInstance::dumpShaderInfo() const +{ + if ( mMaterial == NULL ) + { + Con::errorf( "Trying to get Material information on an invalid MatInstance" ); + return; + } + + Con::printf( "Material Info for object %s - %s", mMaterial->getName(), mMaterial->mMapTo.c_str() ); + + if ( mProcessedMaterial == NULL ) + { + Con::printf( " [no processed material!]" ); + return; + } + + mProcessedMaterial->dumpMaterialInfo(); +} diff --git a/Engine/source/materials/matInstance.h b/Engine/source/materials/matInstance.h new file mode 100644 index 000000000..3170f45f1 --- /dev/null +++ b/Engine/source/materials/matInstance.h @@ -0,0 +1,180 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _MATINSTANCE_H_ +#define _MATINSTANCE_H_ + +#ifndef _MATERIALDEFINITION_H_ +#include "materials/materialDefinition.h" +#endif +#ifndef _BASEMATINSTANCE_H_ +#include "materials/baseMatInstance.h" +#endif +#ifndef _SCENEDATA_H_ +#include "materials/sceneData.h" +#endif +#ifndef _GFXSTATEBLOCK_H_ +#include "gfx/gfxStateBlock.h" +#endif +#ifndef _FEATURESET_H_ +#include "shaderGen/featureSet.h" +#endif + +class GFXShader; +class GFXCubemap; +class ShaderFeature; +class MatInstanceParameterHandle; +class MatInstParameters; +class ProcessedMaterial; + + +/// +class MatInstance : public BaseMatInstance +{ +public: + virtual ~MatInstance(); + + // BaseMatInstance + virtual bool init( const FeatureSet &features, + const GFXVertexFormat *vertexFormat ); + virtual bool reInit(); + virtual void addStateBlockDesc(const GFXStateBlockDesc& desc); + virtual void updateStateBlocks(); + virtual void addShaderMacro( const String &name, const String &value ); + virtual MaterialParameters* allocMaterialParameters(); + virtual void setMaterialParameters(MaterialParameters* param); + virtual MaterialParameters* getMaterialParameters(); + virtual MaterialParameterHandle* getMaterialParameterHandle(const String& name); + virtual bool setupPass(SceneRenderState *, const SceneData &sgData ); + virtual void setTransforms(const MatrixSet &matrixSet, SceneRenderState *state); + virtual void setSceneInfo(SceneRenderState *, const SceneData& sgData); + virtual void setTextureStages(SceneRenderState * state, const SceneData &sgData ); + virtual void setBuffers(GFXVertexBufferHandleBase* vertBuffer, GFXPrimitiveBufferHandle* primBuffer); + virtual bool isInstanced() const; + virtual bool stepInstance(); + virtual bool isForwardLit() const { return mIsForwardLit; } + virtual void setUserObject( SimObject *userObject ) { mUserObject = userObject; } + virtual SimObject* getUserObject() const { return mUserObject; } + virtual Material *getMaterial() { return mMaterial; } + virtual bool hasGlow(); + virtual U32 getCurPass() { return getMax( mCurPass, 0 ); } + virtual U32 getCurStageNum(); + virtual RenderPassData *getPass(U32 pass); + virtual const MatStateHint& getStateHint() const; + virtual const GFXVertexFormat* getVertexFormat() const { return mVertexFormat; } + virtual const FeatureSet& getFeatures() const; + virtual const FeatureSet& getRequestedFeatures() const { return mFeatureList; } + virtual void dumpShaderInfo() const; + + + ProcessedMaterial *getProcessedMaterial() const { return mProcessedMaterial; } + + virtual const GFXStateBlockDesc &getUserStateBlock() const { return mUserDefinedState; } + + virtual bool isCustomMaterial() const { return mCreatedFromCustomMaterial; } +protected: + + friend class Material; + + /// Create a material instance by reference to a Material. + MatInstance( Material &mat ); + + virtual bool processMaterial(); + virtual ProcessedMaterial* getShaderMaterial(); + + Material* mMaterial; + ProcessedMaterial* mProcessedMaterial; + + /// The features requested at material creation time. + FeatureSet mFeatureList; + + /// The vertex format on which this material will render. + const GFXVertexFormat *mVertexFormat; + + /// If the processed material requires forward lighting or not. + bool mIsForwardLit; + + S32 mCurPass; + U32 mMaxStages; + + GFXStateBlockDesc mUserDefinedState; + + Vector mUserMacros; + + SimObject *mUserObject; + + Vector mCurrentHandles; + Vector mCurrentParameters; + MatInstParameters* mActiveParameters; + MatInstParameters* mDefaultParameters; + + bool mCreatedFromCustomMaterial; +private: + void construct(); +}; + +// +// MatInstParameters +// +class MatInstParameters : public MaterialParameters +{ +public: + MatInstParameters(); + MatInstParameters(MaterialParameters* matParams); + virtual ~MatInstParameters(); + + void loadParameters(ProcessedMaterial* pmat); + + /// Returns our list of shader constants, the material can get this and just set the constants it knows about + virtual const Vector& getShaderConstDesc() const; + + /// @name Set shader constant values + /// @{ + /// Actually set shader constant values + /// @param name Name of the constant, this should be a name contained in the array returned in getShaderConstDesc, + /// if an invalid name is used, it is ignored. + virtual void set(MaterialParameterHandle* handle, const F32 f); + virtual void set(MaterialParameterHandle* handle, const Point2F& fv); + virtual void set(MaterialParameterHandle* handle, const Point3F& fv); + virtual void set(MaterialParameterHandle* handle, const Point4F& fv); + virtual void set(MaterialParameterHandle* handle, const ColorF& fv); + virtual void set(MaterialParameterHandle* handle, const S32 f); + virtual void set(MaterialParameterHandle* handle, const Point2I& fv); + virtual void set(MaterialParameterHandle* handle, const Point3I& fv); + virtual void set(MaterialParameterHandle* handle, const Point4I& fv); + virtual void set(MaterialParameterHandle* handle, const AlignedArray& fv); + virtual void set(MaterialParameterHandle* handle, const AlignedArray& fv); + virtual void set(MaterialParameterHandle* handle, const AlignedArray& fv); + virtual void set(MaterialParameterHandle* handle, const AlignedArray& fv); + virtual void set(MaterialParameterHandle* handle, const AlignedArray& fv); + virtual void set(MaterialParameterHandle* handle, const AlignedArray& fv); + virtual void set(MaterialParameterHandle* handle, const AlignedArray& fv); + virtual void set(MaterialParameterHandle* handle, const AlignedArray& fv); + virtual void set(MaterialParameterHandle* handle, const MatrixF& mat, const GFXShaderConstType matrixType = GFXSCT_Float4x4); + virtual void set(MaterialParameterHandle* handle, const MatrixF* mat, const U32 arraySize, const GFXShaderConstType matrixType = GFXSCT_Float4x4); + virtual U32 getAlignmentValue(const GFXShaderConstType constType); +private: + MaterialParameters* mParameters; + bool mOwnParameters; +}; + + +#endif // _MATINSTANCE_H_ diff --git a/Engine/source/materials/matInstanceHook.cpp b/Engine/source/materials/matInstanceHook.cpp new file mode 100644 index 000000000..5cc2c3de2 --- /dev/null +++ b/Engine/source/materials/matInstanceHook.cpp @@ -0,0 +1,35 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "materials/matInstanceHook.h" + + +MatInstanceHookType::MatInstanceHookType( const char *type ) +{ + TypeMap::Iterator iter = getTypeMap().find( type ); + if ( iter == getTypeMap().end() ) + iter = getTypeMap().insertUnique( type, getTypeMap().size() ); + + mTypeIndex = iter->value; +} + diff --git a/Engine/source/materials/matInstanceHook.h b/Engine/source/materials/matInstanceHook.h new file mode 100644 index 000000000..7fbe88aa9 --- /dev/null +++ b/Engine/source/materials/matInstanceHook.h @@ -0,0 +1,87 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MATINSTANCEHOOK_H_ +#define _MATINSTANCEHOOK_H_ + +#ifndef _TDICTIONARY_H_ +#include "core/util/tDictionary.h" +#endif + + +/// The hook type wrapper object +class MatInstanceHookType +{ +protected: + + typedef HashTable TypeMap; + + /// Returns the map of all the hook types. We create + /// it as a method static so that its available to other + /// statics regardless of initialization order. + static inline TypeMap& getTypeMap() + { + static TypeMap smTypeMap; + return smTypeMap; + } + + /// The hook type index for this type. + U32 mTypeIndex; + +public: + + MatInstanceHookType( const char *type ); + + inline MatInstanceHookType( const MatInstanceHookType &type ) + : mTypeIndex( type.mTypeIndex ) + { + } + + inline operator U32 () const { return mTypeIndex; } +}; + + +/// This class is used to define hook objects attached to +/// material instances and provide a registration system +/// for different hook types. +/// +/// @see BaseMatInstance +/// @see MaterialManager +/// +class MatInstanceHook +{ +public: + + /// + virtual ~MatInstanceHook() {} + + /// + virtual const MatInstanceHookType& getType() const = 0; +}; + +#endif // _MATINSTANCEHOOK_H_ + + + + + + diff --git a/Engine/source/materials/matStateHint.cpp b/Engine/source/materials/matStateHint.cpp new file mode 100644 index 000000000..71e0e4535 --- /dev/null +++ b/Engine/source/materials/matStateHint.cpp @@ -0,0 +1,61 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "materials/matStateHint.h" + +#include "materials/processedMaterial.h" + + +const MatStateHint MatStateHint::Default( "Default" ); + +void MatStateHint::init( const ProcessedMaterial *mat ) +{ + PROFILE_SCOPE( MatStateHint_init ); + + mState.clear(); + + // Write the material identifier so that we batch by material + // specific data like diffuse color and parallax scale. + // + // NOTE: This doesn't actually cause more draw calls, but it + // can cause some extra unnessasary material setup when materials + // are different by their properties are the same. + // + const Material *material = mat->getMaterial(); + mState += String::ToString( "Material: '%s', %d\n", material->getName(), material->getId() ); + + // Go thru each pass and write its state into + // the string in the most compact but uniquely + // identifiable way. + U32 passes = mat->getNumPasses(); + for ( U32 i=0; i < passes; i++ ) + mState += mat->getPass( i )->describeSelf(); + + // Finally intern the state string for + // fast pointer comparisions. + mState = mState.intern(); +} + + + + diff --git a/Engine/source/materials/matStateHint.h b/Engine/source/materials/matStateHint.h new file mode 100644 index 000000000..4bd65165d --- /dev/null +++ b/Engine/source/materials/matStateHint.h @@ -0,0 +1,77 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MATSTATEHINT_H_ +#define _MATSTATEHINT_H_ + +#ifndef _TORQUE_STRING_H_ +#include "core/util/str.h" +#endif + +class ProcessedMaterial; + + +/// A simple object for generating and comparing string based +/// hints used for sorting and identifying materials uniquely +/// by its shaders and states. +class MatStateHint +{ +public: + + /// Constructor. + MatStateHint() {} + + /// Constructor for building special hints. + MatStateHint( const String &state ) + : mState( state.intern() ) + { + } + + /// Initialize the state hint from a ProcessMaterial. This + /// assumes that the ProcessedMaterial has properly initialized + /// its passes to describe the material uniquely. + void init( const ProcessedMaterial *mat ); + + /// Clears the hint. + void clear() { mState.clear(); } + + /// Returns a 32bit hash key used for sorting by material state. + operator U32() const { return mState.getHashCaseSensitive(); } + + /// Fast comparision of state for equality. + bool operator ==( const MatStateHint& hint ) const { return mState == hint.mState; } + + /// Fast comparision of state for inequality. + bool operator !=( const MatStateHint& hint ) const { return mState != hint.mState; } + + /// A default state hint. + static const MatStateHint Default; + +protected: + + /// An interned string of the combined material shader and state info + /// for evert pass of the processed material. + String mState; + +}; + +#endif // _MATSTATEHINT_H_ diff --git a/Engine/source/materials/matTextureTarget.cpp b/Engine/source/materials/matTextureTarget.cpp new file mode 100644 index 000000000..ba84142f6 --- /dev/null +++ b/Engine/source/materials/matTextureTarget.cpp @@ -0,0 +1,141 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "materials/matTextureTarget.h" + +#include "console/console.h" +#include "platform/profiler.h" +#include "shaderGen/conditionerFeature.h" +#include "gfx/gfxTextureObject.h" +#include "gfx/gfxStructs.h" + + +NamedTexTarget::TargetMap NamedTexTarget::smTargets; + + +bool NamedTexTarget::registerWithName( const String &name ) +{ + if ( mIsRegistered ) + { + // If we're already registered with + // this name then do nothing. + if ( mName == name ) + return true; + + // Else unregister ourselves first. + unregister(); + } + + // Make sure the target name isn't empty or already taken. + if ( name.isEmpty() || smTargets.contains( name ) ) + return false; + + mName = name; + mIsRegistered = true; + smTargets.insert( mName, this ); + return true; +} + +void NamedTexTarget::unregister() +{ + if ( !mIsRegistered ) + return; + + TargetMap::Iterator iter = smTargets.find( mName ); + + AssertFatal( iter != smTargets.end() && + iter->value == this, + "NamedTexTarget::unregister - Bad registration!" ); + + mIsRegistered = false; + mName = String::EmptyString; + smTargets.erase( iter ); +} + +NamedTexTarget* NamedTexTarget::find( const String &name ) +{ + PROFILE_SCOPE( NamedTexTarget_find ); + + TargetMap::Iterator iter = smTargets.find( name ); + if ( iter != smTargets.end() ) + return iter->value; + else + return NULL; +} + +NamedTexTarget::NamedTexTarget() + : mViewport( RectI::One ), + mIsRegistered( false ), + mConditioner( NULL ) +{ +} + +NamedTexTarget::~NamedTexTarget() +{ + unregister(); +} + +void NamedTexTarget::setTexture( U32 index, GFXTextureObject *tex ) +{ + AssertFatal( index < 4, "NamedTexTarget::setTexture - Got invalid index!" ); + mTex[index] = tex; +} + +void NamedTexTarget::release() +{ + mTex[0] = NULL; + mTex[1] = NULL; + mTex[2] = NULL; + mTex[3] = NULL; +} + +void NamedTexTarget::getShaderMacros( Vector *outMacros ) +{ + ConditionerFeature *cond = getConditioner(); + if ( !cond ) + return; + + // TODO: No check for duplicates is + // going on here which might be a problem? + + String targetName = String::ToLower( mName ); + + // Add both the condition and uncondition macros. + const String &condMethod = cond->getShaderMethodName( ConditionerFeature::ConditionMethod ); + if ( condMethod.isNotEmpty() ) + { + GFXShaderMacro macro; + macro.name = targetName + "Condition"; + macro.value = condMethod; + outMacros->push_back( macro ); + } + + const String &uncondMethod = cond->getShaderMethodName( ConditionerFeature::UnconditionMethod ); + if ( uncondMethod.isNotEmpty() ) + { + GFXShaderMacro macro; + macro.name = targetName + "Uncondition"; + macro.value = uncondMethod; + outMacros->push_back( macro ); + } +} \ No newline at end of file diff --git a/Engine/source/materials/matTextureTarget.h b/Engine/source/materials/matTextureTarget.h new file mode 100644 index 000000000..da09bceb1 --- /dev/null +++ b/Engine/source/materials/matTextureTarget.h @@ -0,0 +1,163 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MATTEXTURETARGET_H_ +#define _MATTEXTURETARGET_H_ + +#ifndef _TDICTIONARY_H_ +#include "core/util/tDictionary.h" +#endif +#ifndef _REFBASE_H_ +#include "core/util/refBase.h" +#endif +#ifndef _GFXTEXTUREHANDLE_H_ +#include "gfx/gfxTextureHandle.h" +#endif +#ifndef _MRECT_H_ +#include "math/mRect.h" +#endif +#ifndef _GFXSTATEBLOCK_H_ +#include "gfx/gfxStateBlock.h" +#endif +#ifndef _UTIL_DELEGATE_H_ +#include "core/util/delegate.h" +#endif + +struct GFXShaderMacro; +class ConditionerFeature; + + +/// +class NamedTexTarget : public WeakRefBase +{ +public: + + /// + static NamedTexTarget* find( const String &name ); + + /// + NamedTexTarget(); + + /// + virtual ~NamedTexTarget(); + + /// + bool registerWithName( const String &name ); + + /// + void unregister(); + + /// + bool isRegistered() const { return mIsRegistered; } + + /// Returns the target name we were registered with. + const String& getName() const { return mName; } + + // Register the passed texture with our name, unregistering "anyone" + // priorly registered with that name. + // Pass NULL to only unregister. + void setTexture( GFXTextureObject *tex ) { setTexture( 0, tex ); } + + /// + void setTexture( U32 index, GFXTextureObject *tex ); + + /// + GFXTextureObject* getTexture( U32 index = 0 ) const; + + /// The delegate used to override the getTexture method. + /// @see getTexture + typedef Delegate TexDelegate; + + /// + /// @see getTexture + TexDelegate& getTextureDelegate() { return mTexDelegate; } + const TexDelegate& getTextureDelegate() const { return mTexDelegate; } + + /// Release all the textures. + void release(); + + // NOTE: + // + // The following members are here to support the existing conditioner + // and target system used for the deferred gbuffer and lighting. + // + // We will refactor that system as part of material2 removing the concept + // of conditioners from C++ (moving them to HLSL/GLSL) and make the shader + // features which use the texture responsible for setting the correct sampler + // states. + // + // It could be that at this time this class could completely + // be removed and instead these textures can be registered + // with the TEXMGR and looked up there exclusively. + // + void setViewport( const RectI &viewport ) { mViewport = viewport; } + const RectI& getViewport() const { return mViewport; } + void setSamplerState( const GFXSamplerStateDesc &desc ) { mSamplerDesc = desc; } + void setupSamplerState( GFXSamplerStateDesc *desc ) const { *desc = mSamplerDesc; } + void setConditioner( ConditionerFeature *cond ) { mConditioner = cond; } + ConditionerFeature* getConditioner() const { return mConditioner; } + void getShaderMacros( Vector *outMacros ); + +protected: + + typedef Map TargetMap; + + /// + static TargetMap smTargets; + + /// + bool mIsRegistered; + + /// The target name we were registered with. + String mName; + + /// The held textures. + GFXTexHandle mTex[4]; + + /// + TexDelegate mTexDelegate; + + /// + RectI mViewport; + + /// + GFXSamplerStateDesc mSamplerDesc; + + /// + ConditionerFeature *mConditioner; +}; + + +inline GFXTextureObject* NamedTexTarget::getTexture( U32 index ) const +{ + AssertFatal( index < 4, "NamedTexTarget::getTexture - Got invalid index!" ); + if ( mTexDelegate.empty() ) + return mTex[index]; + + return mTexDelegate( index ); +} + + +/// A weak reference to a texture target. +typedef WeakRefPtr NamedTexTargetRef; + +#endif // _MATTEXTURETARGET_H_ diff --git a/Engine/source/materials/materialDefinition.cpp b/Engine/source/materials/materialDefinition.cpp new file mode 100644 index 000000000..718ca7d08 --- /dev/null +++ b/Engine/source/materials/materialDefinition.cpp @@ -0,0 +1,668 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "materials/materialDefinition.h" + +#include "console/consoleTypes.h" +#include "math/mathTypes.h" +#include "materials/materialManager.h" +#include "sceneData.h" +#include "gfx/sim/cubemapData.h" +#include "gfx/gfxCubemap.h" +#include "math/mathIO.h" +#include "materials/matInstance.h" +#include "sfx/sfxTrack.h" +#include "sfx/sfxTypes.h" +#include "core/util/safeDelete.h" + + +IMPLEMENT_CONOBJECT( Material ); + +ConsoleDocClass( Material, + "@brief A material in Torque 3D is a data structure that describes a surface.\n\n" + + "It contains many different types of information for rendering properties. " + "Torque 3D generates shaders from Material definitions. The shaders are compiled " + "at runtime and output into the example/shaders directory. Any errors or warnings " + "generated from compiling the procedurally generated shaders are output to the console " + "as well as the output window in the Visual C IDE.\n\n" + + "@tsexample\n" + "singleton Material(DECAL_scorch)\n" + "{\n" + " baseTex[0] = \"./scorch_decal.png\";\n" + " vertColor[ 0 ] = true;\n\n" + " translucent = true;\n" + " translucentBlendOp = None;\n" + " translucentZWrite = true;\n" + " alphaTest = true;\n" + " alphaRef = 84;\n" + "};\n" + "@endtsexample\n\n" + + "@see Rendering\n" + "@see ShaderData\n" + + "@ingroup GFX\n"); + +ImplementBitfieldType( MaterialAnimType, + "The type of animation effect to apply to this material.\n" + "@ingroup GFX\n\n") + { Material::Scroll, "Scroll", "Scroll the material along the X/Y axis.\n" }, + { Material::Rotate, "Rotate" , "Rotate the material around a point.\n"}, + { Material::Wave, "Wave" , "Warps the material with an animation using Sin, Triangle or Square mathematics.\n"}, + { Material::Scale, "Scale", "Scales the material larger and smaller with a pulsing effect.\n" }, + { Material::Sequence, "Sequence", "Enables the material to have multiple frames of animation in its imagemap.\n" } +EndImplementBitfieldType; + +ImplementEnumType( MaterialBlendOp, + "The type of graphical blending operation to apply to this material\n" + "@ingroup GFX\n\n") + { Material::None, "None", "Disable blending for this material." }, + { Material::Mul, "Mul", "Multiplicative blending." }, + { Material::Add, "Add", "Adds the color of the material to the frame buffer with full alpha for each pixel." }, + { Material::AddAlpha, "AddAlpha", "The color is modulated by the alpha channel before being added to the frame buffer." }, + { Material::Sub, "Sub", "Subtractive Blending. Reverses the color model, causing dark colors to have a stronger visual effect." }, + { Material::LerpAlpha, "LerpAlpha", "Linearly interpolates between Material color and frame buffer color based on alpha." } +EndImplementEnumType; + +ImplementEnumType( MaterialWaveType, + "When using the Wave material animation, one of these Wave Types will be used to determine the type of wave to display.\n" + "@ingroup GFX\n") + { Material::Sin, "Sin", "Warps the material along a curved Sin Wave." }, + { Material::Triangle, "Triangle", "Warps the material along a sharp Triangle Wave." }, + { Material::Square, "Square", "Warps the material along a wave which transitions between two oppposite states. As a Square Wave, the transition is quick and sudden." }, +EndImplementEnumType; + + +bool Material::sAllowTextureTargetAssignment = false; + +GFXCubemap * Material::GetNormalizeCube() +{ + if(smNormalizeCube) + return smNormalizeCube; + smNormalizeCube = GFX->createCubemap(); + smNormalizeCube->initNormalize(64); + return smNormalizeCube; +} + +GFXCubemapHandle Material::smNormalizeCube; + +Material::Material() +{ + for( U32 i=0; i(), Offset(mAnimFlags, Material), MAX_STAGES, + "The types of animation to play on this material." ); + + addField("scrollDir", TypePoint2F, Offset(mScrollDir, Material), MAX_STAGES, + "The scroll direction in UV space when scroll animation is enabled." ); + + addField("scrollSpeed", TypeF32, Offset(mScrollSpeed, Material), MAX_STAGES, + "The speed to scroll the texture in UVs per second when scroll animation is enabled." ); + + addField("rotSpeed", TypeF32, Offset(mRotSpeed, Material), MAX_STAGES, + "The speed to rotate the texture in degrees per second when rotation animation is enabled." ); + + addField("rotPivotOffset", TypePoint2F, Offset(mRotPivotOffset, Material), MAX_STAGES, + "The piviot position in UV coordinates to center the rotation animation." ); + + addField("waveType", TYPEID< WaveType >(), Offset(mWaveType, Material), MAX_STAGES, + "The type of wave animation to perform when wave animation is enabled." ); + + addField("waveFreq", TypeF32, Offset(mWaveFreq, Material), MAX_STAGES, + "The wave frequency when wave animation is enabled." ); + + addField("waveAmp", TypeF32, Offset(mWaveAmp, Material), MAX_STAGES, + "The wave amplitude when wave animation is enabled." ); + + addField("sequenceFramePerSec", TypeF32, Offset(mSeqFramePerSec, Material), MAX_STAGES, + "The number of frames per second for frame based sequence animations if greater than zero." ); + + addField("sequenceSegmentSize", TypeF32, Offset(mSeqSegSize, Material), MAX_STAGES, + "The size of each frame in UV units for sequence animations." ); + + // Texture atlasing + addField("cellIndex", TypePoint2I, Offset(mCellIndex, Material), MAX_STAGES, + "@internal" ); + addField("cellLayout", TypePoint2I, Offset(mCellLayout, Material), MAX_STAGES, + "@internal"); + addField("cellSize", TypeS32, Offset(mCellSize, Material), MAX_STAGES, + "@internal"); + addField("bumpAtlas", TypeBool, Offset(mNormalMapAtlas, Material), MAX_STAGES, + "@internal"); + + // For backwards compatibility. + // + // They point at the new 'map' fields, but reads always return + // an empty string and writes only apply if the value is not empty. + // + addProtectedField("baseTex", TypeImageFilename, Offset(mDiffuseMapFilename, Material), + defaultProtectedSetNotEmptyFn, emptyStringProtectedGetFn, MAX_STAGES, + "For backwards compatibility.\n@see diffuseMap\n" ); + addProtectedField("detailTex", TypeImageFilename, Offset(mDetailMapFilename, Material), + defaultProtectedSetNotEmptyFn, emptyStringProtectedGetFn, MAX_STAGES, + "For backwards compatibility.\n@see detailMap\n"); + addProtectedField("overlayTex", TypeImageFilename, Offset(mOverlayMapFilename, Material), + defaultProtectedSetNotEmptyFn, emptyStringProtectedGetFn, MAX_STAGES, + "For backwards compatibility.\n@see overlayMap\n"); + addProtectedField("bumpTex", TypeImageFilename, Offset(mNormalMapFilename, Material), + defaultProtectedSetNotEmptyFn, emptyStringProtectedGetFn, MAX_STAGES, + "For backwards compatibility.\n@see normalMap\n"); + addProtectedField("envTex", TypeImageFilename, Offset(mEnvMapFilename, Material), + defaultProtectedSetNotEmptyFn, emptyStringProtectedGetFn, MAX_STAGES, + "For backwards compatibility.\n@see envMap\n"); + addProtectedField("colorMultiply", TypeColorF, Offset(mDiffuse, Material), + defaultProtectedSetNotEmptyFn, emptyStringProtectedGetFn, MAX_STAGES, + "For backwards compatibility.\n@see diffuseColor\n"); + + endArray( "Stages" ); + + addField( "castShadows", TypeBool, Offset(mCastShadows, Material), + "If set to false the lighting system will not cast shadows from this material." ); + + addField("planarReflection", TypeBool, Offset(mPlanarReflection, Material), "@internal" ); + + addField("translucent", TypeBool, Offset(mTranslucent, Material), + "If true this material is translucent blended." ); + + addField("translucentBlendOp", TYPEID< BlendOp >(), Offset(mTranslucentBlendOp, Material), + "The type of blend operation to use when the material is translucent." ); + + addField("translucentZWrite", TypeBool, Offset(mTranslucentZWrite, Material), + "If enabled and the material is translucent it will write into the depth buffer." ); + + addField("alphaTest", TypeBool, Offset(mAlphaTest, Material), + "Enables alpha test when rendering the material.\n@see alphaRef\n" ); + + addField("alphaRef", TypeS32, Offset(mAlphaRef, Material), + "The alpha reference value for alpha testing. Must be between 0 to 255.\n@see alphaTest\n" ); + + addField("cubemap", TypeRealString, Offset(mCubemapName, Material), + "The name of a CubemapData for environment mapping." ); + + addField("dynamicCubemap", TypeBool, Offset(mDynamicCubemap, Material), + "Enables the material to use the dynamic cubemap from the ShapeBase object its applied to." ); + + addGroup( "Behavioral" ); + + addField( "showFootprints", TypeBool, Offset( mShowFootprints, Material ), + "Whether to show player footprint decals on this material.\n\n" + "@see PlayerData::decalData" ); + + addField( "showDust", TypeBool, Offset( mShowDust, Material ), + "Whether to emit dust particles from a shape moving over the material. This is, for example, used by " + "vehicles or players to decide whether to show dust trails." ); + + addField( "effectColor", TypeColorF, Offset( mEffectColor, Material ), NUM_EFFECT_COLOR_STAGES, + "If #showDust is true, this is the set of colors to use for the ParticleData of the dust " + "emitter.\n\n" + "@see ParticleData::colors" ); + + addField( "footstepSoundId", TypeS32, Offset( mFootstepSoundId, Material ), + "What sound to play from the PlayerData sound list when the player walks over the material. -1 (default) to not play any sound.\n" + "\n" + "The IDs are:\n\n" + "- 0: PlayerData::FootSoftSound\n" + "- 1: PlayerData::FootHardSound\n" + "- 2: PlayerData::FootMetalSound\n" + "- 3: PlayerData::FootSnowSound\n" + "- 4: PlayerData::FootShallowSound\n" + "- 5: PlayerData::FootWadingSound\n" + "- 6: PlayerData::FootUnderwaterSound\n" + "- 7: PlayerData::FootBubblesSound\n" + "- 8: PlayerData::movingBubblesSound\n" + "- 9: PlayerData::waterBreathSound\n" + "- 10: PlayerData::impactSoftSound\n" + "- 11: PlayerData::impactHardSound\n" + "- 12: PlayerData::impactMetalSound\n" + "- 13: PlayerData::impactSnowSound\n" + "- 14: PlayerData::impactWaterEasy\n" + "- 15: PlayerData::impactWaterMedium\n" + "- 16: PlayerData::impactWaterHard\n" + "- 17: PlayerData::exitingWater\n" ); + + addField( "customFootstepSound", TypeSFXTrackName, Offset( mFootstepSoundCustom, Material ), + "The sound to play when the player walks over the material. If this is set, it overrides #footstepSoundId. This field is " + "useful for directly assigning custom footstep sounds to materials without having to rely on the PlayerData sound assignment.\n\n" + "@warn Be aware that materials are client-side objects. This means that the SFXTracks assigned to materials must be client-side, too." ); + addField( "impactSoundId", TypeS32, Offset( mImpactSoundId, Material ), + "What sound to play from the PlayerData sound list when the player impacts on the surface with a velocity equal or greater " + "than PlayerData::groundImpactMinSpeed.\n\n" + "For a list of IDs, see #footstepSoundId" ); + addField( "customImpactSound", TypeSFXTrackName, Offset( mImpactSoundCustom, Material ), + "The sound to play when the player impacts on the surface with a velocity equal or greater than PlayerData::groundImpactMinSpeed. " + "If this is set, it overrides #impactSoundId. This field is useful for directly assigning custom impact sounds to materials " + "without having to rely on the PlayerData sound assignment.\n\n" + "@warn Be aware that materials are client-side objects. This means that the SFXTracks assigned to materials must be client-side, too." ); + + //Deactivate these for the moment as they are not used. + + #if 0 + addField( "friction", TypeF32, Offset( mFriction, Material ) ); + addField( "directSoundOcclusion", TypeF32, Offset( mDirectSoundOcclusion, Material ) ); + addField( "reverbSoundOcclusion", TypeF32, Offset( mReverbSoundOcclusion, Material ) ); + #endif + + endGroup( "Behavioral" ); + + Parent::initPersistFields(); +} + +bool Material::writeField( StringTableEntry fieldname, const char *value ) +{ + // Never allow the old field names to be written. + if ( fieldname == StringTable->insert("baseTex") || + fieldname == StringTable->insert("detailTex") || + fieldname == StringTable->insert("overlayTex") || + fieldname == StringTable->insert("bumpTex") || + fieldname == StringTable->insert("envTex") || + fieldname == StringTable->insert("colorMultiply") ) + return false; + + return Parent::writeField( fieldname, value ); +} + +bool Material::onAdd() +{ + if (Parent::onAdd() == false) + return false; + + mCubemapData = dynamic_cast(Sim::findObject( mCubemapName ) ); + + if( mTranslucentBlendOp >= NumBlendTypes || mTranslucentBlendOp < 0 ) + { + Con::errorf( "Invalid blend op in material: %s", getName() ); + mTranslucentBlendOp = LerpAlpha; + } + + SimSet *matSet = MATMGR->getMaterialSet(); + if( matSet ) + matSet->addObject( (SimObject*)this ); + + // save the current script path for texture lookup later + const String scriptFile = Con::getVariable("$Con::File"); // current script file - local materials.cs + + String::SizeType slash = scriptFile.find( '/', scriptFile.length(), String::Right ); + if ( slash != String::NPos ) + mPath = scriptFile.substr( 0, slash + 1 ); + + _mapMaterial(); + + return true; +} + +void Material::onRemove() +{ + smNormalizeCube = NULL; + Parent::onRemove(); +} + +void Material::inspectPostApply() +{ + Parent::inspectPostApply(); + + // Reload the material instances which + // use this material. + if ( isProperlyAdded() ) + reload(); +} + + +bool Material::isLightmapped() const +{ + bool ret = false; + for( U32 i=0; igetLastUpdateTime(); + F32 dt = MATMGR->getDeltaTime(); + if (mLastUpdateTime != lastTime) + { + for (U32 i = 0; i < MAX_STAGES; i++) + { + mScrollOffset[i] += mScrollDir[i] * mScrollSpeed[i] * dt; + mRotPos[i] += mRotSpeed[i] * dt; + mWavePos[i] += mWaveFreq[i] * dt; + } + mLastUpdateTime = lastTime; + } +} + +void Material::_mapMaterial() +{ + if( String(getName()).isEmpty() ) + { + Con::warnf( "[Material::mapMaterial] - Cannot map unnamed Material" ); + return; + } + + // If mapTo not defined in script, try to use the base texture name instead + if( mMapTo.isEmpty() ) + { + if ( mDiffuseMapFilename[0].isEmpty() ) + return; + + else + { + // extract filename from base texture + if ( mDiffuseMapFilename[0].isNotEmpty() ) + { + U32 slashPos = mDiffuseMapFilename[0].find('/',0,String::Right); + if (slashPos == String::NPos) + // no '/' character, must be no path, just the filename + mMapTo = mDiffuseMapFilename[0]; + else + // use everything after the last slash + mMapTo = mDiffuseMapFilename[0].substr(slashPos+1, mDiffuseMapFilename[0].length() - slashPos - 1); + } + } + } + + // add mapping + MATMGR->mapMaterial(mMapTo,getName()); +} + +BaseMatInstance* Material::createMatInstance() +{ + return new MatInstance(*this); +} + +void Material::flush() +{ + MATMGR->flushInstance( this ); +} + +void Material::reload() +{ + MATMGR->reInitInstance( this ); +} + +void Material::StageData::getFeatureSet( FeatureSet *outFeatures ) const +{ + TextureTable::ConstIterator iter = mTextures.begin(); + for ( ; iter != mTextures.end(); iter++ ) + { + if ( iter->value.isValid() ) + outFeatures->addFeature( *iter->key ); + } +} + +ConsoleMethod( Material, flush, void, 2, 2, + "Flushes all material instances that use this material." ) +{ + object->flush(); +} + +ConsoleMethod( Material, reload, void, 2, 2, + "Reloads all material instances that use this material." ) +{ + object->reload(); +} + +ConsoleMethod( Material, dumpInstances, void, 2, 2, + "Dumps a formatted list of the currently allocated material instances for this material to the console." ) +{ + MATMGR->dumpMaterialInstances( object ); +} + +ConsoleMethod( Material, getAnimFlags, const char*, 3, 3, "" ) +{ + char * animFlags = Con::getReturnBuffer(512); + + if(object->mAnimFlags[ dAtoi(argv[2]) ] & Material::Scroll) + { + if(dStrcmp( animFlags, "" ) == 0) + dStrcpy( animFlags, "$Scroll" ); + } + if(object->mAnimFlags[ dAtoi(argv[2]) ] & Material::Rotate) + { + if(dStrcmp( animFlags, "" ) == 0) + dStrcpy( animFlags, "$Rotate" ); + else + dStrcat( animFlags, " | $Rotate"); + } + if(object->mAnimFlags[ dAtoi(argv[2]) ] & Material::Wave) + { + if(dStrcmp( animFlags, "" ) == 0) + dStrcpy( animFlags, "$Wave" ); + else + dStrcat( animFlags, " | $Wave"); + } + if(object->mAnimFlags[ dAtoi(argv[2]) ] & Material::Scale) + { + if(dStrcmp( animFlags, "" ) == 0) + dStrcpy( animFlags, "$Scale" ); + else + dStrcat( animFlags, " | $Scale"); + } + if(object->mAnimFlags[ dAtoi(argv[2]) ] & Material::Sequence) + { + if(dStrcmp( animFlags, "" ) == 0) + dStrcpy( animFlags, "$Sequence" ); + else + dStrcat( animFlags, " | $Sequence"); + } + + return animFlags; +} + +ConsoleMethod(Material, getFilename, const char*, 2, 2, "Get filename of material") +{ + SimObject *material = static_cast(object); + return material->getFilename(); +} + +ConsoleMethod( Material, isAutoGenerated, bool, 2, 2, + "Returns true if this Material was automatically generated by MaterialList::mapMaterials()" ) +{ + return object->isAutoGenerated(); +} + +ConsoleMethod( Material, setAutoGenerated, void, 3, 3, + "setAutoGenerated(bool isAutoGenerated): Set whether or not the Material is autogenerated." ) +{ + object->setAutoGenerated(dAtob(argv[2])); +} \ No newline at end of file diff --git a/Engine/source/materials/materialDefinition.h b/Engine/source/materials/materialDefinition.h new file mode 100644 index 000000000..cf867bdba --- /dev/null +++ b/Engine/source/materials/materialDefinition.h @@ -0,0 +1,401 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _MATERIALDEFINITION_H_ +#define _MATERIALDEFINITION_H_ + +#ifndef _BASEMATERIALDEFINITION_H_ + #include "materials/baseMaterialDefinition.h" +#endif +#ifndef _TDICTIONARY_H_ + #include "core/util/tDictionary.h" +#endif +#ifndef _GFXTEXTUREHANDLE_H_ + #include "gfx/gfxTextureHandle.h" +#endif +#ifndef _GFXSTRUCTS_H_ + #include "gfx/gfxStructs.h" +#endif +#ifndef _GFXCUBEMAP_H_ + #include "gfx/gfxCubemap.h" +#endif +#ifndef _DYNAMIC_CONSOLETYPES_H_ + #include "console/dynamicTypes.h" +#endif + + +class CubemapData; +class SFXTrack; +struct SceneData; +class FeatureSet; +class FeatureType; +class MaterialSoundProfile; +class MaterialPhysicsProfile; + + +/// The basic material definition. +class Material : public BaseMaterialDefinition +{ + typedef BaseMaterialDefinition Parent; +public: + static GFXCubemap *GetNormalizeCube(); + + //----------------------------------------------------------------------- + // Enums + //----------------------------------------------------------------------- + enum Constants + { + MAX_TEX_PER_PASS = 8, ///< Number of textures per pass + MAX_STAGES = 4, + NUM_EFFECT_COLOR_STAGES = 2, ///< Number of effect color definitions for transitioning effects. + }; + + enum TexType + { + NoTexture = 0, + Standard = 1, + Detail, + Bump, + DetailBump, + Env, + Cube, + SGCube, // scene graph cube - probably dynamic + Lightmap, + ToneMapTex, + Mask, + BackBuff, + ReflectBuff, + Misc, + DynamicLight, + DynamicLightMask, + NormalizeCube, + TexTarget, + }; + + enum BlendOp + { + None = 0, + Mul, + Add, + AddAlpha, // add modulated with alpha channel + Sub, + LerpAlpha, // linear interpolation modulated with alpha channel + ToneMap, + NumBlendTypes + }; + + enum AnimType + { + Scroll = 1, + Rotate = 2, + Wave = 4, + Scale = 8, + Sequence = 16, + }; + + enum WaveType + { + Sin = 0, + Triangle, + Square, + }; + + class StageData + { + protected: + + /// + typedef HashTable TextureTable; + + /// The sparse table of textures by feature index. + /// @see getTex + /// @see setTex + TextureTable mTextures; + + /// The cubemap for this stage. + GFXCubemap *mCubemap; + + public: + + StageData() + : mCubemap( NULL ) + { + } + + /// Returns the texture object or NULL if there is no + /// texture entry for that feature type in the table. + inline GFXTextureObject* getTex( const FeatureType &type ) const + { + TextureTable::ConstIterator iter = mTextures.find( &type ); + if ( iter == mTextures.end() ) + return NULL; + + return iter->value.getPointer(); + } + + /// Assigns a texture object by feature type. + inline void setTex( const FeatureType &type, GFXTextureObject *tex ) + { + if ( !tex ) + { + TextureTable::Iterator iter = mTextures.find( &type ); + if ( iter != mTextures.end() ) + mTextures.erase( iter ); + + return; + } + + TextureTable::Iterator iter = mTextures.findOrInsert( &type ); + iter->value = tex; + } + + /// Returns true if we have a valid texture assigned to + /// any feature in the texture table. + inline bool hasValidTex() const + { + TextureTable::ConstIterator iter = mTextures.begin(); + for ( ; iter != mTextures.end(); iter++ ) + { + if ( iter->value.isValid() ) + return true; + } + + return false; + } + + /// Returns the active texture features. + void getFeatureSet( FeatureSet *outFeatures ) const; + + /// Returns the stage cubemap. + GFXCubemap* getCubemap() const { return mCubemap; } + + /// Set the stage cubemap. + void setCubemap( GFXCubemap *cubemap ) { mCubemap = cubemap; } + + }; + +public: + + //----------------------------------------------------------------------- + // Data + //----------------------------------------------------------------------- + FileName mDiffuseMapFilename[MAX_STAGES]; + FileName mOverlayMapFilename[MAX_STAGES]; + FileName mLightMapFilename[MAX_STAGES]; + FileName mToneMapFilename[MAX_STAGES]; + FileName mDetailMapFilename[MAX_STAGES]; + FileName mNormalMapFilename[MAX_STAGES]; + + FileName mSpecularMapFilename[MAX_STAGES]; + + /// A second normal map which repeats at the detail map + /// scale and blended with the base normal map. + FileName mDetailNormalMapFilename[MAX_STAGES]; + + /// The strength scalar for the detail normal map. + F32 mDetailNormalMapStrength[MAX_STAGES]; + + FileName mEnvMapFilename[MAX_STAGES]; + + /// This color is the diffuse color of the material + /// or if it has a texture it is multiplied against + /// the diffuse texture color. + ColorF mDiffuse[MAX_STAGES]; + + ColorF mSpecular[MAX_STAGES]; + + F32 mSpecularPower[MAX_STAGES]; + bool mPixelSpecular[MAX_STAGES]; + + bool mVertLit[MAX_STAGES]; + + /// If true for a stage, vertex colors are multiplied + /// against diffuse colors. + bool mVertColor[ MAX_STAGES ]; + + F32 mParallaxScale[MAX_STAGES]; + + F32 mMinnaertConstant[MAX_STAGES]; + bool mSubSurface[MAX_STAGES]; + ColorF mSubSurfaceColor[MAX_STAGES]; + F32 mSubSurfaceRolloff[MAX_STAGES]; + + /// The repetition scale of the detail texture + /// over the base texture. + Point2F mDetailScale[MAX_STAGES]; + + U32 mAnimFlags[MAX_STAGES]; + Point2F mScrollDir[MAX_STAGES]; + F32 mScrollSpeed[MAX_STAGES]; + Point2F mScrollOffset[MAX_STAGES]; + + F32 mRotSpeed[MAX_STAGES]; + Point2F mRotPivotOffset[MAX_STAGES]; + F32 mRotPos[MAX_STAGES]; + + F32 mWavePos[MAX_STAGES]; + F32 mWaveFreq[MAX_STAGES]; + F32 mWaveAmp[MAX_STAGES]; + U32 mWaveType[MAX_STAGES]; + + F32 mSeqFramePerSec[MAX_STAGES]; + F32 mSeqSegSize[MAX_STAGES]; + + bool mGlow[MAX_STAGES]; // entire stage glows + bool mEmissive[MAX_STAGES]; + + Point2I mCellIndex[MAX_STAGES]; + Point2I mCellLayout[MAX_STAGES]; + U32 mCellSize[MAX_STAGES]; + bool mNormalMapAtlas[MAX_STAGES]; + + /// Special array of UVs for imposter rendering. + /// @see TSLastDetail + Vector mImposterUVs; + + /// Specual imposter rendering paramters. + /// @see TSLastDetail + Point4F mImposterLimits; + + /// If the stage should use anisotropic filtering. + bool mUseAnisotropic[MAX_STAGES]; + + bool mDoubleSided; + + String mCubemapName; + CubemapData* mCubemapData; + bool mDynamicCubemap; + + bool mTranslucent; + BlendOp mTranslucentBlendOp; + bool mTranslucentZWrite; + + /// A generic setting which tells the system to skip + /// generation of shadows from this material. + bool mCastShadows; + + bool mAlphaTest; + U32 mAlphaRef; + + bool mPlanarReflection; + + bool mAutoGenerated; + + static bool sAllowTextureTargetAssignment; + + ///@{ + /// Behavioral properties. + + bool mShowFootprints; ///< If true, show footprints when walking on surface with this material. Defaults to false. + bool mShowDust; ///< If true, show dust emitters (footpuffs, hover trails, etc) when on surface with this material. Defaults to false. + + /// Color to use for particle effects and such when located on this material. + ColorF mEffectColor[ NUM_EFFECT_COLOR_STAGES ]; + + /// Footstep sound to play when walking on surface with this material. + /// Numeric ID of footstep sound defined on player datablock (0 == soft, + /// 1 == hard, 2 == metal, 3 == snow). + /// Defaults to -1 which deactivates default sound. + /// @see mFootstepSoundCustom + S32 mFootstepSoundId; + S32 mImpactSoundId; + + /// Sound effect to play when walking on surface with this material. + /// If defined, overrides mFootstepSoundId. + /// @see mFootstepSoundId + SFXTrack* mFootstepSoundCustom; + SFXTrack* mImpactSoundCustom; + + F32 mFriction; ///< Friction coefficient when moving along surface. + + F32 mDirectSoundOcclusion; ///< Amount of volume occlusion on direct sounds. + F32 mReverbSoundOcclusion; ///< Amount of volume occlusion on reverb sounds. + + ///@} + + String mMapTo; // map Material to this texture name + + /// + /// Material interface + /// + Material(); + + /// Allocates and returns a BaseMatInstance for this material. Caller is responsible + /// for freeing the instance + virtual BaseMatInstance* createMatInstance(); + virtual bool isTranslucent() const { return mTranslucent && mTranslucentBlendOp != Material::None; } + virtual bool isDoubleSided() const { return mDoubleSided; } + virtual bool isAutoGenerated() const { return mAutoGenerated; } + virtual void setAutoGenerated(bool isAutoGenerated) { mAutoGenerated = isAutoGenerated; } + virtual bool isLightmapped() const; + virtual bool castsShadows() const { return mCastShadows; } + const String &getPath() const { return mPath; } + + void flush(); + + /// Re-initializes all the material instances + /// that use this material. + void reload(); + + /// Called to update time based parameters for a material. Ensures + /// that it only happens once per tick. + void updateTimeBasedParams(); + + // SimObject + virtual bool onAdd(); + virtual void onRemove(); + virtual void inspectPostApply(); + virtual bool writeField( StringTableEntry fieldname, const char *value ); + + // + // ConsoleObject interface + // + static void initPersistFields(); + + DECLARE_CONOBJECT(Material); +protected: + + // Per material animation parameters + U32 mLastUpdateTime; + + String mPath; + + static EnumTable mAnimFlagTable; + static EnumTable mBlendOpTable; + static EnumTable mWaveTypeTable; + + /// Map this material to the texture specified + /// in the "mapTo" data variable. + virtual void _mapMaterial(); + +private: + static GFXCubemapHandle smNormalizeCube; +}; + +typedef Material::AnimType MaterialAnimType; +typedef Material::BlendOp MaterialBlendOp; +typedef Material::WaveType MaterialWaveType; + +DefineBitfieldType( MaterialAnimType ); +DefineEnumType( MaterialBlendOp ); +DefineEnumType( MaterialWaveType ); + +#endif // _MATERIALDEFINITION_H_ diff --git a/Engine/source/materials/materialFeatureData.cpp b/Engine/source/materials/materialFeatureData.cpp new file mode 100644 index 000000000..66b159573 --- /dev/null +++ b/Engine/source/materials/materialFeatureData.cpp @@ -0,0 +1,46 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "materials/materialFeatureData.h" + + +MaterialFeatureData::MaterialFeatureData() +{ +} + +MaterialFeatureData::MaterialFeatureData( const MaterialFeatureData &data ) + : features( data.features ), + materialFeatures( data.materialFeatures ) +{ +} + +MaterialFeatureData::MaterialFeatureData( const FeatureSet &handle ) + : features( handle ) +{ +} + +void MaterialFeatureData::clear() +{ + features.clear(); + materialFeatures.clear(); +} diff --git a/Engine/source/materials/materialFeatureData.h b/Engine/source/materials/materialFeatureData.h new file mode 100644 index 000000000..8cd22fdb5 --- /dev/null +++ b/Engine/source/materials/materialFeatureData.h @@ -0,0 +1,75 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MATERIALFEATUREDATA_H_ +#define _MATERIALFEATUREDATA_H_ + +#ifndef _UTIL_DELEGATE_H_ +#include "core/util/delegate.h" +#endif +#ifndef _FEATURESET_H_ +#include "shaderGen/featureSet.h" +#endif + + +class ProcessedMaterial; + + +//----------------------------------------------------------------------------- +// MaterialFeatureData, this is basically a series of flags which a material can +// ask for. Shader processed materials will use the shadergen system to accomplish this, +// FF processed materials will do the best they can. +//----------------------------------------------------------------------------- +struct MaterialFeatureData +{ +public: + + // General feature data for a pass or for other purposes. + FeatureSet features; + + // This is to give hints to shader creation code. It contains + // all the features that are in a material stage instead of just + // the current pass. + FeatureSet materialFeatures; + +public: + + MaterialFeatureData(); + + MaterialFeatureData( const MaterialFeatureData &data ); + + MaterialFeatureData( const FeatureSet &handle ); + + void clear(); + + const FeatureSet& codify() const { return features; } + +}; + + +/// +typedef Delegate< void( ProcessedMaterial *mat, + U32 stageNum, + MaterialFeatureData &fd, + const FeatureSet &features) > MatFeaturesDelegate; + +#endif // _MATERIALFEATUREDATA_H_ diff --git a/Engine/source/materials/materialFeatureTypes.cpp b/Engine/source/materials/materialFeatureTypes.cpp new file mode 100644 index 000000000..afea9efc5 --- /dev/null +++ b/Engine/source/materials/materialFeatureTypes.cpp @@ -0,0 +1,86 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "materials/materialFeatureTypes.h" + + +ImplementFeatureType( MFT_UseInstancing, U32(-1), -1, false ); + +ImplementFeatureType( MFT_VertTransform, MFG_Transform, 0, true ); + +ImplementFeatureType( MFT_TexAnim, MFG_PreTexture, 1.0f, true ); +ImplementFeatureType( MFT_Parallax, MFG_PreTexture, 2.0f, true ); +ImplementFeatureType( MFT_DiffuseVertColor, MFG_PreTexture, 3.0f, true ); + +ImplementFeatureType( MFT_DiffuseMap, MFG_Texture, 2.0f, true ); +ImplementFeatureType( MFT_OverlayMap, MFG_Texture, 3.0f, true ); +ImplementFeatureType( MFT_DetailMap, MFG_Texture, 4.0f, true ); +ImplementFeatureType( MFT_DiffuseColor, MFG_Texture, 5.0f, true ); +ImplementFeatureType( MFT_AlphaTest, MFG_Texture, 7.0f, true ); +ImplementFeatureType( MFT_SpecularMap, MFG_Texture, 8.0f, true ); +ImplementFeatureType( MFT_NormalMap, MFG_Texture, 9.0f, true ); +ImplementFeatureType( MFT_DetailNormalMap, MFG_Texture, 10.0f, true ); + +ImplementFeatureType( MFT_RTLighting, MFG_Lighting, 2.0f, true ); +ImplementFeatureType( MFT_SubSurface, MFG_Lighting, 3.0f, true ); +ImplementFeatureType( MFT_LightMap, MFG_Lighting, 4.0f, true ); +ImplementFeatureType( MFT_ToneMap, MFG_Lighting, 5.0f, true ); +ImplementFeatureType( MFT_VertLitTone, MFG_Lighting, 6.0f, false ); +ImplementFeatureType( MFT_VertLit, MFG_Lighting, 7.0f, true ); +ImplementFeatureType( MFT_EnvMap, MFG_Lighting, 8.0f, true ); +ImplementFeatureType( MFT_CubeMap, MFG_Lighting, 9.0f, true ); +ImplementFeatureType( MFT_PixSpecular, MFG_Lighting, 10.0f, true ); +ImplementFeatureType( MFT_MinnaertShading, MFG_Lighting, 12.0f, true ); + +ImplementFeatureType( MFT_GlowMask, MFG_PostLighting, 1.0f, true ); +ImplementFeatureType( MFT_Visibility, MFG_PostLighting, 2.0f, true ); +ImplementFeatureType( MFT_Fog, MFG_PostProcess, 3.0f, true ); + +ImplementFeatureType( MFT_HDROut, MFG_PostProcess, 999.0f, true ); + +ImplementFeatureType( MFT_IsDXTnm, U32(-1), -1, true ); +ImplementFeatureType( MFT_IsTranslucent, U32(-1), -1, true ); +ImplementFeatureType( MFT_IsTranslucentZWrite, U32(-1), -1, true ); +ImplementFeatureType( MFT_IsEmissive, U32(-1), -1, true ); +ImplementFeatureType( MFT_GlossMap, U32(-1), -1, true ); +ImplementFeatureType( MFT_DiffuseMapAtlas, U32(-1), -1, true ); +ImplementFeatureType( MFT_NormalMapAtlas, U32(-1), -1, true ); +ImplementFeatureType( MFT_InterlacedPrePass, U32(-1), -1, true ); + +ImplementFeatureType( MFT_ParaboloidVertTransform, MFG_Transform, -1, false ); +ImplementFeatureType( MFT_IsSinglePassParaboloid, U32(-1), -1, false ); +ImplementFeatureType( MFT_EyeSpaceDepthOut, MFG_PostLighting, 2.0f, false ); +ImplementFeatureType( MFT_DepthOut, MFG_PostLighting, 3.0f, false ); +ImplementFeatureType( MFT_PrePassConditioner, MFG_PostProcess, 1.0f, false ); +ImplementFeatureType( MFT_NormalsOut, MFG_PreLighting, 1.0f, false ); + +ImplementFeatureType( MFT_LightbufferMRT, MFG_PreLighting, 1.0f, false ); +ImplementFeatureType( MFT_RenderTarget1_Zero, MFG_PreTexture, 1.0f, false ); + +ImplementFeatureType( MFT_Foliage, MFG_PreTransform, 1.0f, false ); + +ImplementFeatureType( MFT_ParticleNormal, MFG_PreTransform, 2.0f, false ); + +ImplementFeatureType( MFT_ForwardShading, U32(-1), -1, true ); + +ImplementFeatureType( MFT_ImposterVert, MFG_PreTransform, 1.0, false ); \ No newline at end of file diff --git a/Engine/source/materials/materialFeatureTypes.h b/Engine/source/materials/materialFeatureTypes.h new file mode 100644 index 000000000..8ae552317 --- /dev/null +++ b/Engine/source/materials/materialFeatureTypes.h @@ -0,0 +1,174 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MATERIALFEATURETYPES_H_ +#define _MATERIALFEATURETYPES_H_ + +#ifndef _FEATURETYPE_H_ +#include "shaderGen/featureType.h" +#endif + + +/// +enum MaterialFeatureGroup +{ + /// One or more pre-transform features are + /// allowed at any one time and are executed + /// in order to each other. + MFG_PreTransform, + + /// Only one transform feature is allowed at + /// any one time. + MFG_Transform, + + /// + MFG_PostTransform, + + /// The features that need to occur before texturing + /// takes place. Usually these are features that will + /// manipulate or generate texture coords. + MFG_PreTexture, + + /// The different diffuse color features including + /// textures and colors. + MFG_Texture, + + /// + MFG_PreLighting, + + /// + MFG_Lighting, + + /// + MFG_PostLighting, + + /// Final features like fogging. + MFG_PostProcess, + + /// Miscellaneous features that require no specialized + /// ShaderFeature object and are just queried as flags. + MFG_Misc = -1, +}; + +/// If defined then this shader should use hardware mesh instancing. +DeclareFeatureType( MFT_UseInstancing ); + +/// The standard vertex transform. +DeclareFeatureType( MFT_VertTransform ); + +/// A special transform with paraboloid warp used +/// in shadow and reflection rendering. +DeclareFeatureType( MFT_ParaboloidVertTransform ); + +/// This feature is queried from the MFT_ParaboloidVertTransform +/// feature to detect if it needs to generate a single pass. +DeclareFeatureType( MFT_IsSinglePassParaboloid ); + +/// This feature does normal map decompression for DXT1/5. +DeclareFeatureType( MFT_IsDXTnm ); + +DeclareFeatureType( MFT_TexAnim ); +DeclareFeatureType( MFT_Parallax ); + +DeclareFeatureType( MFT_DiffuseMap ); +DeclareFeatureType( MFT_OverlayMap ); +DeclareFeatureType( MFT_DetailMap ); +DeclareFeatureType( MFT_DiffuseColor ); +DeclareFeatureType( MFT_DetailNormalMap ); + +/// This feature enables vertex coloring for the diffuse channel. +DeclareFeatureType( MFT_DiffuseVertColor ); + +/// This feature is used to do alpha test clipping in +/// the shader which can be faster on SM3 and is needed +/// when the render state alpha test is not available. +DeclareFeatureType( MFT_AlphaTest ); + +DeclareFeatureType( MFT_NormalMap ); +DeclareFeatureType( MFT_RTLighting ); + +DeclareFeatureType( MFT_IsEmissive ); +DeclareFeatureType( MFT_SubSurface ); +DeclareFeatureType( MFT_LightMap ); +DeclareFeatureType( MFT_ToneMap ); +DeclareFeatureType( MFT_VertLit ); +DeclareFeatureType( MFT_VertLitTone ); + +DeclareFeatureType( MFT_EnvMap ); +DeclareFeatureType( MFT_CubeMap ); +DeclareFeatureType( MFT_PixSpecular ); +DeclareFeatureType( MFT_SpecularMap ); +DeclareFeatureType( MFT_GlossMap ); + +/// This feature is only used to detect alpha transparency +/// and does not have any code associtated with it. +DeclareFeatureType( MFT_IsTranslucent ); + +/// +DeclareFeatureType( MFT_IsTranslucentZWrite ); + +/// This feature causes MFT_NormalMap to set the world +/// space normal vector to the output color rgb. +DeclareFeatureType( MFT_NormalsOut ); + +DeclareFeatureType( MFT_MinnaertShading ); +DeclareFeatureType( MFT_GlowMask ); +DeclareFeatureType( MFT_Visibility ); +DeclareFeatureType( MFT_EyeSpaceDepthOut ); +DeclareFeatureType( MFT_DepthOut ); +DeclareFeatureType( MFT_Fog ); + +/// This should be the last feature of any material that +/// renders to a HDR render target. It converts the high +/// dynamic range color into the correct HDR encoded format. +DeclareFeatureType( MFT_HDROut ); + +/// +DeclareFeatureType( MFT_PrePassConditioner ); +DeclareFeatureType( MFT_InterlacedPrePass ); + +/// This feature causes MFT_ToneMap and MFT_LightMap to output their light color +/// to the second render-target +DeclareFeatureType( MFT_LightbufferMRT ); + +/// This feature outputs black to RenderTarget1 +DeclareFeatureType( MFT_RenderTarget1_Zero ); + +DeclareFeatureType( MFT_Foliage ); + +// Texture atlasing features +DeclareFeatureType( MFT_DiffuseMapAtlas ); +DeclareFeatureType( MFT_NormalMapAtlas ); + +// Particle features +DeclareFeatureType( MFT_ParticleNormal ); + +/// This feature is used to indicate that the material should use forward shading +/// instead of deferred shading (if applicable) +DeclareFeatureType( MFT_ForwardShading ); + +/// A special vertex feature which unpacks the imposter vertex +/// so that the rest of the material features can work on it. +DeclareFeatureType( MFT_ImposterVert ); + + +#endif // _MATERIALFEATURETYPES_H_ diff --git a/Engine/source/materials/materialList.cpp b/Engine/source/materials/materialList.cpp new file mode 100644 index 000000000..fbeab64f3 --- /dev/null +++ b/Engine/source/materials/materialList.cpp @@ -0,0 +1,412 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "materials/materialList.h" + +#include "materials/matInstance.h" +#include "materials/materialManager.h" +#include "materials/materialFeatureTypes.h" +#include "materials/processedMaterial.h" +#include "core/volume.h" +#include "console/simSet.h" + + +MaterialList::MaterialList() +{ + VECTOR_SET_ASSOCIATION(mMatInstList); + VECTOR_SET_ASSOCIATION(mMaterialNames); +} + +MaterialList::MaterialList(const MaterialList* pCopy) +{ + VECTOR_SET_ASSOCIATION(mMatInstList); + VECTOR_SET_ASSOCIATION(mMaterialNames); + + mMaterialNames.setSize(pCopy->mMaterialNames.size()); + S32 i; + for (i = 0; i < mMaterialNames.size(); i++) + { + mMaterialNames[i] = pCopy->mMaterialNames[i]; + } + + clearMatInstList(); + mMatInstList.setSize(pCopy->size()); + for( i = 0; i < mMatInstList.size(); i++ ) + { + if( i < pCopy->mMatInstList.size() && pCopy->mMatInstList[i] ) + { + mMatInstList[i] = pCopy->mMatInstList[i]->getMaterial()->createMatInstance(); + } + else + { + mMatInstList[i] = NULL; + } + } +} + + + +MaterialList::MaterialList(U32 materialCount, const char **materialNames) +{ + VECTOR_SET_ASSOCIATION(mMaterialNames); + + set(materialCount, materialNames); +} + + +//-------------------------------------- +void MaterialList::set(U32 materialCount, const char **materialNames) +{ + free(); + mMaterialNames.setSize(materialCount); + clearMatInstList(); + mMatInstList.setSize(materialCount); + for(U32 i = 0; i < materialCount; i++) + { + mMaterialNames[i] = materialNames[i]; + mMatInstList[i] = NULL; + } +} + + +//-------------------------------------- +MaterialList::~MaterialList() +{ + free(); +} + +//-------------------------------------- +void MaterialList::setMaterialName(U32 index, const String& name) +{ + if (index < mMaterialNames.size()) + mMaterialNames[index] = name; +} + +//-------------------------------------- +GFXTextureObject *MaterialList::getDiffuseTexture(U32 index) +{ + AssertFatal(index < (U32) mMatInstList.size(), "MaterialList::getDiffuseTex: index lookup out of range."); + + MatInstance *matInst = dynamic_cast(mMatInstList[index]); + if (matInst && matInst->getProcessedMaterial()) + return matInst->getProcessedMaterial()->getStageTexture(0, MFT_DiffuseMap); + else + return NULL; +} + +//-------------------------------------- +void MaterialList::free() +{ + clearMatInstList(); + mMatInstList.clear(); + mMaterialNames.clear(); +} + +/* +//-------------------------------------- +U32 MaterialList::push_back(GFXTexHandle textureHandle, const String &filename) +{ + mMaterialNames.push_back(filename); + mMatInstList.push_back(NULL); + + // return the index + return mMaterialNames.size()-1; +} +*/ + +//-------------------------------------- +U32 MaterialList::push_back(const String &filename, Material* material) +{ + mMaterialNames.push_back(filename); + mMatInstList.push_back(material ? material->createMatInstance() : NULL); + + // return the index + return mMaterialNames.size()-1; +} + +//-------------------------------------- +bool MaterialList::read(Stream &stream) +{ + free(); + + // check the stream version + U8 version; + if ( stream.read(&version) && version != BINARY_FILE_VERSION) + return readText(stream,version); + + // how many materials? + U32 count; + if ( !stream.read(&count) ) + return false; + + // pre-size the vectors for efficiency + mMaterialNames.reserve(count); + + // read in the materials + for (U32 i=0; inot be initialized. + +void MaterialList::mapMaterial( U32 i ) +{ + AssertFatal( i < size(), "MaterialList::mapMaterialList - index out of bounds" ); + + if( mMatInstList[i] != NULL ) + return; + + // lookup a material property entry + const String &matName = getMaterialName(i); + + // JMQ: this code assumes that all materials have names. + if( matName.isEmpty() ) + { + mMatInstList[i] = NULL; + return; + } + + String materialName = MATMGR->getMapEntry(matName); + + // IF we didn't find it, then look for a PolyStatic generated Material + // [a little cheesy, but we need to allow for user override of generated Materials] + if ( materialName.isEmpty() ) + materialName = MATMGR->getMapEntry( String::ToString( "polyMat_%s", matName.c_str() ) ); + + if ( materialName.isNotEmpty() ) + { + Material * mat = MATMGR->getMaterialDefinitionByName( materialName ); + mMatInstList[i] = mat ? mat->createMatInstance() : MATMGR->createWarningMatInstance(); + } + else + { + if ( Con::getBoolVariable( "$Materials::createMissing", true ) ) + { + // No Material found, create new "default" material with just a diffuseMap + + // First see if there is a valid diffuse texture + GFXTexHandle texHandle; + if (mLookupPath.isEmpty()) + { + texHandle.set( mMaterialNames[i], &GFXDefaultStaticDiffuseProfile, avar("%s() - handle (line %d)", __FUNCTION__, __LINE__) ); + } + else + { + // Should we strip off the extension of the path here before trying + // to load the texture? + String fullPath = String::ToString( "%s/%s", mLookupPath.c_str(), mMaterialNames[i].c_str() ); + texHandle.set( fullPath, &GFXDefaultStaticDiffuseProfile, avar("%s() - handle (line %d)", __FUNCTION__, __LINE__) ); + } + + if ( texHandle.isValid() ) + { + String newMatName = Sim::getUniqueName( "DefaultMaterial" ); + Material *newMat = MATMGR->allocateAndRegister( newMatName, mMaterialNames[i] ); + + // Flag this as an autogenerated Material + newMat->mAutoGenerated = true; + + // Overwrite diffuseMap in new material + newMat->mDiffuseMapFilename[0] = texHandle->mTextureLookupName; + + // Set up some defaults for transparent textures + if (texHandle->mHasTransparency) + { + newMat->mTranslucent = true; + newMat->mTranslucentBlendOp = Material::LerpAlpha; + newMat->mTranslucentZWrite = true; + newMat->mAlphaRef = 20; + } + + // create a MatInstance for the new material + mMatInstList[i] = newMat->createMatInstance(); + + #ifndef TORQUE_SHIPPING + Con::warnf( "[MaterialList::mapMaterials] Creating missing material for texture: %s", texHandle->mTextureLookupName.c_str() ); + #endif + } + else + { + Con::errorf( "[MaterialList::mapMaterials] Unable to find material for texture: %s", mMaterialNames[i].c_str() ); + mMatInstList[i] = MATMGR->createWarningMatInstance(); + } + } + else + { + mMatInstList[i] = MATMGR->createWarningMatInstance(); + } + } +} + +void MaterialList::initMatInstances( const FeatureSet &features, + const GFXVertexFormat *vertexFormat ) +{ + for( U32 i=0; i < mMatInstList.size(); i++ ) + { + BaseMatInstance *matInst = mMatInstList[i]; + if ( !matInst ) + continue; + + if ( !matInst->init( features, vertexFormat ) ) + { + Con::errorf( "MaterialList::initMatInstances - failed to initialize material instance for '%s'", + matInst->getMaterial()->getName() ); + + // Fall back to warning material. + + SAFE_DELETE( matInst ); + matInst = MATMGR->createMatInstance( "WarningMaterial" ); + matInst->init( MATMGR->getDefaultFeatures(), vertexFormat ); + mMatInstList[ i ] = matInst; + } + } + +} + +void MaterialList::setMaterialInst( BaseMatInstance *matInst, U32 texIndex ) +{ + AssertFatal( texIndex < mMatInstList.size(), "MaterialList::setMaterialInst - index out of bounds" ); + mMatInstList[texIndex] = matInst; +} diff --git a/Engine/source/materials/materialList.h b/Engine/source/materials/materialList.h new file mode 100644 index 000000000..deb45991e --- /dev/null +++ b/Engine/source/materials/materialList.h @@ -0,0 +1,106 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MATERIALLIST_H_ +#define _MATERIALLIST_H_ + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _GFXTEXTUREHANDLE_H_ +#include "gfx/gfxTextureHandle.h" +#endif + + +class Material; +class BaseMatInstance; +class Stream; +class GFXVertexFormat; +class FeatureSet; + + +class MaterialList +{ +public: + MaterialList(); + MaterialList(U32 materialCount, const char **materialNames); + virtual ~MaterialList(); + + /// Note: this is not to be confused with MaterialList(const MaterialList&). Copying + /// a material list in the middle of it's lifetime is not a good thing, so we force + /// it to copy at construction time by restricting the copy syntax to + /// ML* pML = new ML(©); + explicit MaterialList(const MaterialList*); + + const Vector &getMaterialNameList() const { return mMaterialNames; } + const String &getMaterialName(U32 index) const { return mMaterialNames[index]; } + GFXTextureObject *getDiffuseTexture(U32 index); + + void setTextureLookupPath(const String& path) { mLookupPath = path; } + void setMaterialName(U32 index, const String& name); + + void set(U32 materialCount, const char **materialNames); + U32 push_back(const String &filename, Material* = 0); + + virtual void free(); + void clearMatInstList(); + + bool empty() const { return mMaterialNames.empty(); } + U32 size() const { return (U32)mMaterialNames.size(); } + + bool read(Stream &stream); + bool write(Stream &stream); + + bool readText(Stream &stream, U8 firstByte); + bool readText(Stream &stream); + bool writeText(Stream &stream); + + void mapMaterials(); + + /// Initialize material instances in material list. + void initMatInstances( const FeatureSet &features, + const GFXVertexFormat *vertexFormat ); + + /// Return the material instance or NULL if the + /// index is out of bounds. + inline BaseMatInstance* getMaterialInst( U32 index ) const + { + return index < mMatInstList.size() ? mMatInstList[index] : NULL; + } + + void setMaterialInst( BaseMatInstance *matInst, U32 index ); + + // Needs to be accessible if were going to freely edit instances + Vector mMatInstList; + +protected: + + String mLookupPath; + Vector mMaterialNames; //!< Material 'mapTo' targets + + virtual void mapMaterial( U32 index ); + +private: + enum Constants { BINARY_FILE_VERSION = 1 }; +}; + +#endif // _MATERIALLIST_H_ diff --git a/Engine/source/materials/materialManager.cpp b/Engine/source/materials/materialManager.cpp new file mode 100644 index 000000000..7456b9f38 --- /dev/null +++ b/Engine/source/materials/materialManager.cpp @@ -0,0 +1,491 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "materials/materialManager.h" + +#include "materials/matInstance.h" +#include "materials/materialFeatureTypes.h" +#include "lighting/lightManager.h" +#include "core/util/safeDelete.h" +#include "shaderGen/shaderGen.h" +#include "core/module.h" +#include "console/consoleTypes.h" + + +MODULE_BEGIN( MaterialManager ) + + MODULE_INIT_BEFORE( GFX ) + MODULE_SHUTDOWN_BEFORE( GFX ) + + MODULE_INIT + { + MaterialManager::createSingleton(); + } + + MODULE_SHUTDOWN + { + MaterialManager::deleteSingleton(); + } + +MODULE_END; + + +MaterialManager::MaterialManager() +{ + VECTOR_SET_ASSOCIATION( mMatInstanceList ); + + mDt = 0.0f; + mAccumTime = 0.0f; + mLastTime = 0; + mWarningInst = NULL; + + GFXDevice::getDeviceEventSignal().notify( this, &MaterialManager::_handleGFXEvent ); + + // Make sure we get activation signals + // and that we're the last to get them. + LightManager::smActivateSignal.notify( this, &MaterialManager::_onLMActivate, 9999 ); + + mMaterialSet = NULL; + + mUsingPrePass = false; + + mFlushAndReInit = false; + + mDefaultAnisotropy = 1; + Con::addVariable( "$pref::Video::defaultAnisotropy", TypeS32, &mDefaultAnisotropy, + "@brief Global variable defining the default anisotropy value.\n\n" + "Controls the default anisotropic texture filtering level for all materials, including the terrain. " + "This value can be changed at runtime to see its affect without reloading.\n\n " + "@ingroup Materials"); + Con::NotifyDelegate callabck( this, &MaterialManager::_updateDefaultAnisotropy ); + Con::addVariableNotify( "$pref::Video::defaultAnisotropy", callabck ); + + Con::NotifyDelegate callabck2( this, &MaterialManager::_onDisableMaterialFeature ); + Con::setVariable( "$pref::Video::disableNormalMapping", false ); + Con::addVariableNotify( "$pref::Video::disableNormalMapping", callabck2 ); + Con::setVariable( "$pref::Video::disablePixSpecular", false ); + Con::addVariableNotify( "$pref::Video::disablePixSpecular", callabck2 ); + Con::setVariable( "$pref::Video::disableCubemapping", false ); + Con::addVariableNotify( "$pref::Video::disableCubemapping", callabck2 ); + Con::setVariable( "$pref::Video::disableParallaxMapping", false ); + Con::addVariableNotify( "$pref::Video::disableParallaxMapping", callabck2 ); +} + +MaterialManager::~MaterialManager() +{ + GFXDevice::getDeviceEventSignal().remove( this, &MaterialManager::_handleGFXEvent ); + LightManager::smActivateSignal.remove( this, &MaterialManager::_onLMActivate ); + + SAFE_DELETE( mWarningInst ); + +#ifndef TORQUE_SHIPPING + DebugMaterialMap::Iterator itr = mMeshDebugMaterialInsts.begin(); + + for ( ; itr != mMeshDebugMaterialInsts.end(); itr++ ) + delete itr->value; +#endif +} + +void MaterialManager::_onLMActivate( const char *lm, bool activate ) +{ + if ( !activate ) + return; + + // Since the light manager usually swaps shadergen features + // and changes system wide shader defines we need to completely + // flush and rebuild all the material instances. + + mFlushAndReInit = true; +} + +void MaterialManager::_updateDefaultAnisotropy() +{ + // Update all the materials. + Vector::iterator iter = mMatInstanceList.begin(); + for ( ; iter != mMatInstanceList.end(); iter++ ) + (*iter)->updateStateBlocks(); +} + +Material * MaterialManager::allocateAndRegister(const String &objectName, const String &mapToName) +{ + Material *newMat = new Material(); + + if ( mapToName.isNotEmpty() ) + newMat->mMapTo = mapToName; + + bool registered = newMat->registerObject(objectName ); + AssertFatal( registered, "Unable to register material" ); + + if (registered) + Sim::getRootGroup()->addObject( newMat ); + else + { + delete newMat; + newMat = NULL; + } + + return newMat; +} + +Material * MaterialManager::getMaterialDefinitionByName(const String &matName) +{ + // Get the material + Material * foundMat; + + if(!Sim::findObject(matName, foundMat)) + { + Con::errorf("MaterialManager: Unable to find material '%s'", matName.c_str()); + return NULL; + } + + return foundMat; +} + +BaseMatInstance* MaterialManager::createMatInstance(const String &matName) +{ + BaseMaterialDefinition* mat = NULL; + if (Sim::findObject(matName, mat)) + return mat->createMatInstance(); + + return NULL; +} + +BaseMatInstance* MaterialManager::createMatInstance( const String &matName, + const GFXVertexFormat *vertexFormat ) +{ + return createMatInstance( matName, getDefaultFeatures(), vertexFormat ); +} + +BaseMatInstance* MaterialManager::createMatInstance( const String &matName, + const FeatureSet& features, + const GFXVertexFormat *vertexFormat ) +{ + BaseMatInstance* mat = createMatInstance(matName); + if (mat) + { + mat->init( features, vertexFormat ); + return mat; + } + + return NULL; +} + +BaseMatInstance * MaterialManager::createWarningMatInstance() +{ + Material *warnMat = static_cast(Sim::findObject("WarningMaterial")); + + BaseMatInstance *warnMatInstance = NULL; + + if( warnMat != NULL ) + { + warnMatInstance = warnMat->createMatInstance(); + + GFXStateBlockDesc desc; + desc.setCullMode(GFXCullNone); + warnMatInstance->addStateBlockDesc(desc); + + warnMatInstance->init( getDefaultFeatures(), + getGFXVertexFormat() ); + } + + return warnMatInstance; +} + +// Gets the global warning material instance, callers should not free this copy +BaseMatInstance * MaterialManager::getWarningMatInstance() +{ + if (!mWarningInst) + mWarningInst = createWarningMatInstance(); + + return mWarningInst; +} + +#ifndef TORQUE_SHIPPING +BaseMatInstance * MaterialManager::createMeshDebugMatInstance(const ColorF &meshColor) +{ + String meshDebugStr = String::ToString( "Torque_MeshDebug_%d", meshColor.getRGBAPack() ); + + Material *debugMat; + if (!Sim::findObject(meshDebugStr,debugMat)) + { + debugMat = allocateAndRegister( meshDebugStr ); + + debugMat->mDiffuse[0] = meshColor; + debugMat->mEmissive[0] = true; + } + + BaseMatInstance *debugMatInstance = NULL; + + if( debugMat != NULL ) + { + debugMatInstance = debugMat->createMatInstance(); + + GFXStateBlockDesc desc; + desc.setCullMode(GFXCullNone); + desc.fillMode = GFXFillWireframe; + debugMatInstance->addStateBlockDesc(desc); + + // Disable fog and other stuff. + FeatureSet debugFeatures; + debugFeatures.addFeature( MFT_DiffuseColor ); + debugMatInstance->init( debugFeatures, getGFXVertexFormat() ); + } + + return debugMatInstance; +} + +// Gets the global material instance for a given color, callers should not free this copy +BaseMatInstance *MaterialManager::getMeshDebugMatInstance(const ColorF &meshColor) +{ + DebugMaterialMap::Iterator itr = mMeshDebugMaterialInsts.find( meshColor.getRGBAPack() ); + + BaseMatInstance *inst = NULL; + + if ( itr == mMeshDebugMaterialInsts.end() ) + inst = createMeshDebugMatInstance( meshColor ); + else + inst = itr->value; + + mMeshDebugMaterialInsts.insert( meshColor.getRGBAPack(), inst ); + + return inst; +} +#endif + +void MaterialManager::mapMaterial(const String & textureName, const String & materialName) +{ + if (getMapEntry(textureName).isNotEmpty()) + { + if (!textureName.equal("unmapped_mat", String::NoCase)) + Con::warnf(ConsoleLogEntry::General, "Warning, overwriting material for: %s", textureName.c_str()); + } + + mMaterialMap[String::ToLower(textureName)] = materialName; +} + +String MaterialManager::getMapEntry(const String & textureName) const +{ + MaterialMap::ConstIterator iter = mMaterialMap.find(String::ToLower(textureName)); + if ( iter == mMaterialMap.end() ) + return String(); + return iter->value; +} + +void MaterialManager::flushAndReInitInstances() +{ + // Clear the flag if its set. + mFlushAndReInit = false; + + // Check to see if any shader preferences have changed. + recalcFeaturesFromPrefs(); + + // First we flush all the shader gen shaders which will + // invalidate all GFXShader* to them. + SHADERGEN->flushProceduralShaders(); + mFlushSignal.trigger(); + + // First do a pass deleting all hooks as they can contain + // materials themselves. This means we have to restart the + // loop every time we delete any hooks... lame. + Vector::iterator iter = mMatInstanceList.begin(); + while ( iter != mMatInstanceList.end() ) + { + if ( (*iter)->deleteAllHooks() != 0 ) + { + // Restart the loop. + iter = mMatInstanceList.begin(); + continue; + } + + iter++; + } + + // Now do a pass re-initializing materials. + iter = mMatInstanceList.begin(); + for ( ; iter != mMatInstanceList.end(); iter++ ) + (*iter)->reInit(); +} + +// Used in the materialEditor. This flushes the material preview object so it can be reloaded easily. +void MaterialManager::flushInstance( BaseMaterialDefinition *target ) +{ + Vector::iterator iter = mMatInstanceList.begin(); + while ( iter != mMatInstanceList.end() ) + { + if ( (*iter)->getMaterial() == target ) + { + (*iter)->deleteAllHooks(); + return; + } + iter++; + } +} + +void MaterialManager::reInitInstance( BaseMaterialDefinition *target ) +{ + Vector::iterator iter = mMatInstanceList.begin(); + for ( ; iter != mMatInstanceList.end(); iter++ ) + { + if ( (*iter)->getMaterial() == target ) + (*iter)->reInit(); + } +} + +void MaterialManager::updateTime() +{ + U32 curTime = Sim::getCurrentTime(); + if(curTime > mLastTime) + { + mDt = (curTime - mLastTime) / 1000.0f; + mLastTime = curTime; + mAccumTime += mDt; + } + else + mDt = 0.0f; +} + +SimSet * MaterialManager::getMaterialSet() +{ + if(!mMaterialSet) + mMaterialSet = static_cast( Sim::findObject( "MaterialSet" ) ); + + AssertFatal( mMaterialSet, "MaterialSet not found" ); + return mMaterialSet; +} + +void MaterialManager::dumpMaterialInstances( BaseMaterialDefinition *target ) const +{ + if ( !mMatInstanceList.size() ) + return; + + if ( target ) + Con::printf( "--------------------- %s MatInstances ---------------------", target->getName() ); + else + Con::printf( "--------------------- MatInstances %d ---------------------", mMatInstanceList.size() ); + + for( U32 i=0; igetMaterial() != target ) + continue; + + inst->dumpShaderInfo(); + + Con::printf( "" ); + } + + Con::printf( "---------------------- Dump complete ----------------------"); +} + +void MaterialManager::_track( MatInstance *matInstance ) +{ + mMatInstanceList.push_back( matInstance ); +} + +void MaterialManager::_untrack( MatInstance *matInstance ) +{ + mMatInstanceList.remove( matInstance ); +} + +void MaterialManager::recalcFeaturesFromPrefs() +{ + mDefaultFeatures.clear(); + FeatureType::addDefaultTypes( &mDefaultFeatures ); + + mExclusionFeatures.setFeature( MFT_NormalMap, + Con::getBoolVariable( "$pref::Video::disableNormalMapping", false ) ); + + mExclusionFeatures.setFeature( MFT_PixSpecular, + Con::getBoolVariable( "$pref::Video::disablePixSpecular", false ) ); + + mExclusionFeatures.setFeature( MFT_CubeMap, + Con::getBoolVariable( "$pref::Video::disableCubemapping", false ) ); + + mExclusionFeatures.setFeature( MFT_Parallax, + Con::getBoolVariable( "$pref::Video::disableParallaxMapping", false ) ); +} + +bool MaterialManager::_handleGFXEvent( GFXDevice::GFXDeviceEventType event_ ) +{ + switch ( event_ ) + { + case GFXDevice::deInit: + recalcFeaturesFromPrefs(); + break; + + case GFXDevice::deDestroy : + SAFE_DELETE( mWarningInst ); + break; + + case GFXDevice::deStartOfFrame: + if ( mFlushAndReInit ) + flushAndReInitInstances(); + break; + + default: + break; + } + + return true; +} + +ConsoleFunction( reInitMaterials, void, 1, 1, + "@brief Flushes all procedural shaders and re-initializes all active material instances.\n\n" + "@ingroup Materials") +{ + MATMGR->flushAndReInitInstances(); +} + +ConsoleFunction( addMaterialMapping, void, 3, 3, "(string texName, string matName)\n" + "@brief Maps the given texture to the given material.\n\n" + "Generates a console warning before overwriting.\n\n" + "Material maps are used by terrain and interiors for triggering " + "effects when an object moves onto a terrain " + "block or interior surface using the associated texture.\n\n" + "@ingroup Materials") +{ + MATMGR->mapMaterial(argv[1],argv[2]); +} + +ConsoleFunction( getMaterialMapping, const char*, 2, 2, "(string texName)\n" + "@brief Returns the name of the material mapped to this texture.\n\n" + "If no materials are found, an empty string is returned.\n\n" + "@param texName Name of the texture\n\n" + "@ingroup Materials") +{ + return MATMGR->getMapEntry(argv[1]).c_str(); +} + +ConsoleFunction( dumpMaterialInstances, void, 1, 1, + "@brief Dumps a formatted list of currently allocated material instances to the console.\n\n" + "@ingroup Materials") +{ + MATMGR->dumpMaterialInstances(); +} + +ConsoleFunction( getMapEntry, const char *, 2, 2, + "@hide") +{ + return MATMGR->getMapEntry( String(argv[1]) ); +} \ No newline at end of file diff --git a/Engine/source/materials/materialManager.h b/Engine/source/materials/materialManager.h new file mode 100644 index 000000000..f5f429673 --- /dev/null +++ b/Engine/source/materials/materialManager.h @@ -0,0 +1,184 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _MATERIAL_MGR_H_ +#define _MATERIAL_MGR_H_ + +#ifndef _MATERIALDEFINITION_H_ +#include "materials/materialDefinition.h" +#endif +#ifndef _FEATURESET_H_ +#include "shaderGen/featureSet.h" +#endif +#ifndef _GFXDEVICE_H_ +#include "gfx/gfxDevice.h" +#endif +#ifndef _TSINGLETON_H_ +#include "core/util/tSingleton.h" +#endif + +class SimSet; +class MatInstance; + +class MaterialManager : public ManagedSingleton +{ +public: + MaterialManager(); + ~MaterialManager(); + + // ManagedSingleton + static const char* getSingletonName() { return "MaterialManager"; } + + Material * allocateAndRegister(const String &objectName, const String &mapToName = String()); + Material * getMaterialDefinitionByName(const String &matName); + SimSet * getMaterialSet(); + + // map textures to materials + void mapMaterial(const String & textureName, const String & materialName); + String getMapEntry(const String & textureName) const; + + // Return instance of named material caller is responsible for memory + BaseMatInstance * createMatInstance( const String &matName ); + + // Create a BaseMatInstance with the default feature flags. + BaseMatInstance * createMatInstance( const String &matName, const GFXVertexFormat *vertexFormat ); + BaseMatInstance * createMatInstance( const String &matName, const FeatureSet &features, const GFXVertexFormat *vertexFormat ); + + /// The default feature set for materials. + const FeatureSet& getDefaultFeatures() const { return mDefaultFeatures; } + + /// The feature exclusion list for disabling features. + const FeatureSet& getExclusionFeatures() const { return mExclusionFeatures; } + + void recalcFeaturesFromPrefs(); + + /// Get the default texture anisotropy. + U32 getDefaultAnisotropy() const { return mDefaultAnisotropy; } + + /// Allocate and return an instance of special materials. Caller is responsible for the memory. + BaseMatInstance * createWarningMatInstance(); + + /// Gets the global warning material instance, callers should not free this copy + BaseMatInstance * getWarningMatInstance(); + + /// Set the prepass enabled state. + void setPrePassEnabled( bool enabled ) { mUsingPrePass = enabled; } + + /// Get the prepass enabled state. + bool getPrePassEnabled() const { return mUsingPrePass; } + +#ifndef TORQUE_SHIPPING + + // Allocate and return an instance of mesh debugging materials. Caller is responsible for the memory. + BaseMatInstance * createMeshDebugMatInstance(const ColorF &meshColor); + + // Gets the global material instance for a given color, callers should not free this copy + BaseMatInstance * getMeshDebugMatInstance(const ColorF &meshColor); + +#endif + + void dumpMaterialInstances( BaseMaterialDefinition *target = NULL ) const; + + void updateTime(); + F32 getTotalTime() const { return mAccumTime; } + F32 getDeltaTime() const { return mDt; } + U32 getLastUpdateTime() const { return mLastTime; } + + /// Signal used to notify systems that + /// procedural shaders have been flushed. + typedef Signal FlushSignal; + + /// Returns the signal used to notify systems that the + /// procedural shaders have been flushed. + FlushSignal& getFlushSignal() { return mFlushSignal; } + + /// Flushes all the procedural shaders and re-initializes all + /// the active materials instances immediately. + void flushAndReInitInstances(); + + // Flush the instance + void flushInstance( BaseMaterialDefinition *target ); + + /// Re-initializes the material instances for a specific target material. + void reInitInstance( BaseMaterialDefinition *target ); + +protected: + + // MatInstance tracks it's instances here + friend class MatInstance; + void _track(MatInstance*); + void _untrack(MatInstance*); + + /// @see LightManager::smActivateSignal + void _onLMActivate( const char *lm, bool activate ); + + bool _handleGFXEvent(GFXDevice::GFXDeviceEventType event); + + SimSet* mMaterialSet; + Vector mMatInstanceList; + + /// The default material features. + FeatureSet mDefaultFeatures; + + /// The feature exclusion set. + FeatureSet mExclusionFeatures; + + /// Signal used to notify systems that + /// procedural shaders have been flushed. + FlushSignal mFlushSignal; + + /// If set we flush and reinitialize all materials at the + /// start of the next rendered frame. + bool mFlushAndReInit; + + // material map + typedef Map MaterialMap; + MaterialMap mMaterialMap; + + bool mUsingPrePass; + + // time tracking + F32 mDt; + F32 mAccumTime; + U32 mLastTime; + + BaseMatInstance* mWarningInst; + + /// The default max anisotropy used in texture filtering. + S32 mDefaultAnisotropy; + + /// Called when $pref::Video::defaultAnisotropy is changed. + void _updateDefaultAnisotropy(); + + /// Called when one of the feature disabling $pref::s are changed. + void _onDisableMaterialFeature() { mFlushAndReInit = true; } + +#ifndef TORQUE_SHIPPING + typedef Map DebugMaterialMap; + DebugMaterialMap mMeshDebugMaterialInsts; +#endif + +}; + +/// Helper for accessing MaterialManager singleton. +#define MATMGR MaterialManager::instance() + +#endif // _MATERIAL_MGR_H_ diff --git a/Engine/source/materials/materialParameters.h b/Engine/source/materials/materialParameters.h new file mode 100644 index 000000000..8448ab225 --- /dev/null +++ b/Engine/source/materials/materialParameters.h @@ -0,0 +1,99 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _MATERIALPARAMETERS_H_ +#define _MATERIALPARAMETERS_H_ + +#ifndef _GFXSHADER_H_ +#include "gfx/gfxShader.h" +#endif + +/// +/// Similar class to GFXShaderConsts, but this is to describe generic material parameters. +/// +class MaterialParameterHandle +{ +public: + virtual ~MaterialParameterHandle() {} + virtual const String& getName() const = 0; + // This is similar to GFXShaderConstHandle, but a Material will always return a handle when + // asked for one. Even if it doesn't have that material parameter. This is because after a + // reInitMaterials call, the constants can change underneath the MatInstance. + // Note: GFXShaderConstHandle actually works this way too, now. + virtual bool isValid() const = 0; + /// Returns -1 if this handle does not point to a Sampler. + virtual S32 getSamplerRegister( U32 pass ) const = 0; +}; + +class MaterialParameters +{ +public: + MaterialParameters() + { + VECTOR_SET_ASSOCIATION( mShaderConstDesc ); + } + virtual ~MaterialParameters() {} + + /// Returns our list of shader constants, the material can get this and just set the constants it knows about + virtual const Vector& getShaderConstDesc() const { return mShaderConstDesc; } + + /// An inline helper which ensures the handle is valid + /// before the virtual set method is called. + template< typename VALUE > + inline void setSafe( MaterialParameterHandle *handle, const VALUE& v ) + { + if ( handle->isValid() ) + set( handle, v ); + } + + /// @name Set shader constant values + /// @{ + /// Actually set shader constant values + /// @param name Name of the constant, this should be a name contained in the array returned in getShaderConstDesc, + /// if an invalid name is used, it is ignored. + virtual void set(MaterialParameterHandle* handle, const F32 f) {} + virtual void set(MaterialParameterHandle* handle, const Point2F& fv) {} + virtual void set(MaterialParameterHandle* handle, const Point3F& fv) {} + virtual void set(MaterialParameterHandle* handle, const Point4F& fv) {} + virtual void set(MaterialParameterHandle* handle, const ColorF& fv) {} + virtual void set(MaterialParameterHandle* handle, const S32 f) {} + virtual void set(MaterialParameterHandle* handle, const Point2I& fv) {} + virtual void set(MaterialParameterHandle* handle, const Point3I& fv) {} + virtual void set(MaterialParameterHandle* handle, const Point4I& fv) {} + virtual void set(MaterialParameterHandle* handle, const AlignedArray& fv) {} + virtual void set(MaterialParameterHandle* handle, const AlignedArray& fv) {} + virtual void set(MaterialParameterHandle* handle, const AlignedArray& fv) {} + virtual void set(MaterialParameterHandle* handle, const AlignedArray& fv) {} + virtual void set(MaterialParameterHandle* handle, const AlignedArray& fv) {} + virtual void set(MaterialParameterHandle* handle, const AlignedArray& fv) {} + virtual void set(MaterialParameterHandle* handle, const AlignedArray& fv) {} + virtual void set(MaterialParameterHandle* handle, const AlignedArray& fv) {} + virtual void set(MaterialParameterHandle* handle, const MatrixF& mat, const GFXShaderConstType matrixType = GFXSCT_Float4x4) {} + virtual void set(MaterialParameterHandle* handle, const MatrixF* mat, const U32 arraySize, const GFXShaderConstType matrixType = GFXSCT_Float4x4) {} + + /// Returns the alignment value for the constType in bytes. + virtual U32 getAlignmentValue(const GFXShaderConstType constType) { return 0; } + +protected: + Vector mShaderConstDesc; +}; + +#endif diff --git a/Engine/source/materials/miscShdrDat.h b/Engine/source/materials/miscShdrDat.h new file mode 100644 index 000000000..6441f1b07 --- /dev/null +++ b/Engine/source/materials/miscShdrDat.h @@ -0,0 +1,57 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _MISCSHDRDAT_H_ +#define _MISCSHDRDAT_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif + +//************************************************************************** +// This file is an attempt to keep certain classes from having to know about +// the ShaderGen class +//************************************************************************** + + +//----------------------------------------------------------------------- +// Enums +//----------------------------------------------------------------------- +enum RegisterType +{ + RT_POSITION = 0, + RT_NORMAL, + RT_BINORMAL, + RT_TANGENT, + RT_COLOR, + RT_TEXCOORD, + RT_VPOS, +}; + +enum Components +{ + C_VERT_STRUCT = 0, + C_CONNECTOR, + C_VERT_MAIN, + C_PIX_MAIN, +}; + +#endif // _MISCSHDRDAT_H_ diff --git a/Engine/source/materials/processedCustomMaterial.cpp b/Engine/source/materials/processedCustomMaterial.cpp new file mode 100644 index 000000000..3d57e0592 --- /dev/null +++ b/Engine/source/materials/processedCustomMaterial.cpp @@ -0,0 +1,498 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "materials/processedCustomMaterial.h" + +#include "gfx/sim/cubemapData.h" +#include "materials/sceneData.h" +#include "shaderGen/shaderGenVars.h" +#include "scene/sceneRenderState.h" +#include "materials/customMaterialDefinition.h" +#include "materials/shaderData.h" +#include "materials/materialManager.h" +#include "materials/matTextureTarget.h" +#include "materials/materialFeatureTypes.h" +#include "materials/materialParameters.h" +#include "gfx/sim/gfxStateBlockData.h" +#include "core/util/safeDelete.h" +#include "gfx/genericConstBuffer.h" +#include "console/simFieldDictionary.h" +#include "console/propertyParsing.h" +#include "gfx/util/screenspace.h" + + +ProcessedCustomMaterial::ProcessedCustomMaterial(Material &mat) +{ + mMaterial = &mat; + AssertFatal(dynamic_cast(mMaterial), "Incompatible Material type!"); + mCustomMaterial = static_cast(mMaterial); + mHasSetStageData = false; + mHasGlow = false; + mMaxStages = 0; + mMaxTex = 0; +} + +ProcessedCustomMaterial::~ProcessedCustomMaterial() +{ +} + +void ProcessedCustomMaterial::_setStageData() +{ + // Only do this once + if ( mHasSetStageData ) + return; + mHasSetStageData = true; + + ShaderRenderPassData* rpd = _getRPD(0); + mConditionerMacros.clear(); + + // Loop through all the possible textures, set the right flags, and load them if needed + for(U32 i=0; imTexType[i] = Material::NoTexture; // Set none as the default in case none of the cases below catch it. + String filename = mCustomMaterial->mTexFilename[i]; + + if(filename.isEmpty()) + continue; + + if(filename.equal(String("$dynamiclight"), String::NoCase)) + { + rpd->mTexType[i] = Material::DynamicLight; + mMaxTex = i+1; + continue; + } + + if(filename.equal(String("$dynamiclightmask"), String::NoCase)) + { + rpd->mTexType[i] = Material::DynamicLightMask; + mMaxTex = i+1; + continue; + } + + if(filename.equal(String("$lightmap"), String::NoCase)) + { + rpd->mTexType[i] = Material::Lightmap; + mMaxTex = i+1; + continue; + } + + if(filename.equal(String("$cubemap"), String::NoCase)) + { + if( mCustomMaterial->mCubemapData ) + { + rpd->mTexType[i] = Material::Cube; + mMaxTex = i+1; + } + else + { + mCustomMaterial->logError( "Could not find CubemapData - %s", mCustomMaterial->mCubemapName.c_str()); + } + continue; + } + + if(filename.equal(String("$dynamicCubemap"), String::NoCase)) + { + rpd->mTexType[i] = Material::SGCube; + mMaxTex = i+1; + continue; + } + + if(filename.equal(String("$backbuff"), String::NoCase)) + { + rpd->mTexType[i] = Material::BackBuff; + mMaxTex = i+1; + continue; + } + + if(filename.equal(String("$reflectbuff"), String::NoCase)) + { + rpd->mTexType[i] = Material::ReflectBuff; + mMaxTex = i+1; + continue; + } + + if(filename.equal(String("$miscbuff"), String::NoCase)) + { + rpd->mTexType[i] = Material::Misc; + mMaxTex = i+1; + continue; + } + + // Check for a RenderTexTargetBin assignment + if (filename.substr( 0, 1 ).equal("#")) + { + String texTargetBufferName = filename.substr(1, filename.length() - 1); + NamedTexTarget *texTarget = NamedTexTarget::find( texTargetBufferName ); + rpd->mTexSlot[i].texTarget = texTarget; + + // Get the conditioner macros. + if ( texTarget ) + texTarget->getShaderMacros( &mConditionerMacros ); + + rpd->mTexType[i] = Material::TexTarget; + mMaxTex = i+1; + continue; + } + + rpd->mTexSlot[i].texObject = _createTexture( filename, &GFXDefaultStaticDiffuseProfile ); + if ( !rpd->mTexSlot[i].texObject ) + { + mMaterial->logError("Failed to load texture %s", _getTexturePath(filename).c_str()); + continue; + } + rpd->mTexType[i] = Material::Standard; + mMaxTex = i+1; + } + + // We only get one cubemap + if( mCustomMaterial->mCubemapData ) + { + mCustomMaterial->mCubemapData->createMap(); + rpd->mCubeMap = mMaterial->mCubemapData->mCubemap; // BTRTODO ? + if ( !rpd->mCubeMap ) + mMaterial->logError("Failed to load cubemap"); + } + + // If this has a output target defined, it may be writing + // to a tex target bin with a conditioner, so search for + // one and add its macros. + if ( mCustomMaterial->mOutputTarget.isNotEmpty() ) + { + NamedTexTarget *texTarget = NamedTexTarget::find( mCustomMaterial->mOutputTarget ); + if ( texTarget ) + texTarget->getShaderMacros( &mConditionerMacros ); + } + + // Copy the glow state over. + mHasGlow = mCustomMaterial->mGlow[0]; +} + +bool ProcessedCustomMaterial::init( const FeatureSet &features, + const GFXVertexFormat *vertexFormat, + const MatFeaturesDelegate &featuresDelegate ) +{ + // If we don't have a shader data... we have nothing to do. + if ( !mCustomMaterial->mShaderData ) + return true; + + // Custom materials only do one pass at the moment... so + // add one for the stage data to fill in. + ShaderRenderPassData *rpd = new ShaderRenderPassData(); + mPasses.push_back( rpd ); + + _setStageData(); + _initPassStateBlocks(); + mStateHint.clear(); + + // Note: We don't use the vertex format in a custom + // material at all right now. + // + // Maybe we can add some required semantics and + // validate that the format fits the shader? + + // Build a composite list of shader macros from + // the conditioner and the user defined lists. + Vector macros; + macros.merge( mConditionerMacros ); + macros.merge( mUserMacros ); + + // Ask the shader data to give us a shader instance. + rpd->shader = mCustomMaterial->mShaderData->getShader( macros ); + if ( !rpd->shader ) + { + delete rpd; + mPasses.clear(); + return false; + } + + rpd->shaderHandles.init( rpd->shader, mCustomMaterial ); + _initMaterialParameters(); + mDefaultParameters = allocMaterialParameters(); + setMaterialParameters( mDefaultParameters, 0 ); + mStateHint.init( this ); + + return true; +} + +void ProcessedCustomMaterial::_initPassStateBlock( RenderPassData *rpd, GFXStateBlockDesc &result ) +{ + Parent::_initPassStateBlock( rpd, result ); + + if (mCustomMaterial->getStateBlockData()) + result.addDesc(mCustomMaterial->getStateBlockData()->getState()); +} + +void ProcessedCustomMaterial::_initPassStateBlocks() +{ + AssertFatal(mHasSetStageData, "State data must be set before initializing state block!"); + ShaderRenderPassData* rpd = _getRPD(0); + _initRenderStateStateBlocks( rpd ); +} + +bool ProcessedCustomMaterial::_hasCubemap(U32 pass) +{ + // If the material doesn't have a cubemap, we don't + if( mMaterial->mCubemapData ) return true; + else return false; +} + +bool ProcessedCustomMaterial::setupPass( SceneRenderState *state, const SceneData& sgData, U32 pass ) +{ + PROFILE_SCOPE( ProcessedCustomMaterial_SetupPass ); + + // Make sure we have a pass. + if ( pass >= mPasses.size() ) + return false; + + ShaderRenderPassData* rpd = _getRPD( pass ); + U32 currState = _getRenderStateIndex( state, sgData ); + GFX->setStateBlock(rpd->mRenderStates[currState]); + + // activate shader + if ( rpd->shader ) + GFX->setShader( rpd->shader ); + else + GFX->disableShaders(); + + // Set our textures + setTextureStages( state, sgData, pass ); + + GFXShaderConstBuffer* shaderConsts = _getShaderConstBuffer(pass); + GFX->setShaderConstBuffer(shaderConsts); + + // Set our shader constants. + _setTextureTransforms(pass); + _setShaderConstants(state, sgData, pass); + + LightManager* lm = state ? LIGHTMGR : NULL; + if (lm) + lm->setLightInfo(this, NULL, sgData, state, pass, shaderConsts); + + shaderConsts->setSafe(rpd->shaderHandles.mAccumTimeSC, MATMGR->getTotalTime()); + + return true; +} + +void ProcessedCustomMaterial::setTextureStages( SceneRenderState *state, const SceneData &sgData, U32 pass ) +{ + LightManager* lm = state ? LIGHTMGR : NULL; + ShaderRenderPassData* rpd = _getRPD(pass); + ShaderConstHandles* handles = _getShaderConstHandles(pass); + GFXShaderConstBuffer* shaderConsts = _getShaderConstBuffer(pass); + + const NamedTexTarget *texTarget; + GFXTextureObject *texObject; + + for( U32 i=0; imTexType[i]; + if ( !lm || !lm->setTextureStage(sgData, currTexFlag, i, shaderConsts, handles ) ) + { + GFXShaderConstHandle* handle = handles->mTexHandlesSC[i]; + if ( !handle->isValid() ) + continue; + + S32 samplerRegister = handle->getSamplerRegister(); + + switch( currTexFlag ) + { + case 0: + default: + break; + + case Material::Mask: + case Material::Standard: + case Material::Bump: + case Material::Detail: + { + GFX->setTexture( samplerRegister, rpd->mTexSlot[i].texObject ); + break; + } + + case Material::Lightmap: + { + GFX->setTexture( samplerRegister, sgData.lightmap ); + break; + } + case Material::Cube: + { + GFX->setCubeTexture( samplerRegister, rpd->mCubeMap ); + break; + } + case Material::SGCube: + { + GFX->setCubeTexture( samplerRegister, sgData.cubemap ); + break; + } + case Material::BackBuff: + { + GFX->setTexture( samplerRegister, sgData.backBuffTex ); + break; + } + case Material::ReflectBuff: + { + GFX->setTexture( samplerRegister, sgData.reflectTex ); + break; + } + case Material::Misc: + { + GFX->setTexture( samplerRegister, sgData.miscTex ); + break; + } + case Material::TexTarget: + { + texTarget = rpd->mTexSlot[i].texTarget; + if ( !texTarget ) + { + GFX->setTexture( samplerRegister, NULL ); + break; + } + + texObject = texTarget->getTexture(); + + // If no texture is available then map the default 2x2 + // black texture to it. This at least will ensure that + // we get consistant behavior across GPUs and platforms. + if ( !texObject ) + texObject = GFXTexHandle::ZERO; + + if ( handles->mRTParamsSC[samplerRegister]->isValid() && texObject ) + { + const Point3I &targetSz = texObject->getSize(); + const RectI &targetVp = texTarget->getViewport(); + Point4F rtParams; + + ScreenSpace::RenderTargetParameters(targetSz, targetVp, rtParams); + shaderConsts->set(handles->mRTParamsSC[samplerRegister], rtParams); + } + + GFX->setTexture( samplerRegister, texObject ); + break; + } + } + } + } +} + +template +void ProcessedCustomMaterial::setMaterialParameter(MaterialParameters* param, + MaterialParameterHandle* handle, + const String& value) +{ + T typedValue; + if (PropertyInfo::default_scan(value, typedValue)) + { + param->set(handle, typedValue); + } else { + Con::errorf("Error setting %s, parse error: %s", handle->getName().c_str(), value.c_str()); + } +} + +void ProcessedCustomMaterial::setMatrixParameter(MaterialParameters* param, + MaterialParameterHandle* handle, + const String& value, GFXShaderConstType matrixType) +{ + MatrixF result(true); + F32* m = result; + switch (matrixType) + { + case GFXSCT_Float2x2 : + dSscanf(value.c_str(),"%g %g %g %g", + m[result.idx(0,0)], m[result.idx(0,1)], + m[result.idx(1,0)], m[result.idx(1,1)]); + break; + case GFXSCT_Float3x3 : + dSscanf(value.c_str(),"%g %g %g %g %g %g %g %g %g", + m[result.idx(0,0)], m[result.idx(0,1)], m[result.idx(0,2)], + m[result.idx(1,0)], m[result.idx(1,1)], m[result.idx(1,2)], + m[result.idx(2,0)], m[result.idx(2,1)], m[result.idx(2,2)]); + break; + default: + AssertFatal(false, "Invalid type!"); + break; + } +} + +// BTRTODO: Support arrays!? +MaterialParameters* ProcessedCustomMaterial::allocMaterialParameters() +{ + MaterialParameters* ret = Parent::allocMaterialParameters(); + // See if any of the dynamic fields match up with shader constants we have. + SimFieldDictionary* fields = mMaterial->getFieldDictionary(); + if (!fields || fields->getNumFields() == 0) + return ret; + + const Vector& consts = ret->getShaderConstDesc(); + for (U32 i = 0; i < consts.size(); i++) + { + // strip the dollar sign from the front. + String stripped(consts[i].name); + stripped.erase(0, 1); + + SimFieldDictionary::Entry* field = fields->findDynamicField(stripped); + if (field) + { + MaterialParameterHandle* handle = getMaterialParameterHandle(consts[i].name); + switch (consts[i].constType) + { + case GFXSCT_Float : + setMaterialParameter(ret, handle, field->value); + break; + case GFXSCT_Float2: + setMaterialParameter(ret, handle, field->value); + break; + case GFXSCT_Float3: + setMaterialParameter(ret, handle, field->value); + break; + case GFXSCT_Float4: + setMaterialParameter(ret, handle, field->value); + break; + case GFXSCT_Float2x2: + case GFXSCT_Float3x3: + setMatrixParameter(ret, handle, field->value, consts[i].constType); + break; + case GFXSCT_Float4x4: + setMaterialParameter(ret, handle, field->value); + break; + case GFXSCT_Int: + setMaterialParameter(ret, handle, field->value); + break; + case GFXSCT_Int2: + setMaterialParameter(ret, handle, field->value); + break; + case GFXSCT_Int3: + setMaterialParameter(ret, handle, field->value); + break; + case GFXSCT_Int4: + setMaterialParameter(ret, handle, field->value); + break; + // Do we want to ignore these? + case GFXSCT_Sampler: + case GFXSCT_SamplerCube: + default: + break; + } + } + } + return ret; +} \ No newline at end of file diff --git a/Engine/source/materials/processedCustomMaterial.h b/Engine/source/materials/processedCustomMaterial.h new file mode 100644 index 000000000..bcdbf239b --- /dev/null +++ b/Engine/source/materials/processedCustomMaterial.h @@ -0,0 +1,72 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MATERIALS_PROCESSEDCUSTOMMATERIAL_H_ +#define _MATERIALS_PROCESSEDCUSTOMMATERIAL_H_ + +#ifndef _MATERIALS_PROCESSEDSHADERMATERIAL_H_ +#include "materials/processedShaderMaterial.h" +#endif +#ifndef _CUSTOMMATERIALDEFINITION_H_ +#include "materials/customMaterialDefinition.h" +#endif + + +/// +class ProcessedCustomMaterial : public ProcessedShaderMaterial +{ + typedef ProcessedShaderMaterial Parent; +public: + ProcessedCustomMaterial(Material &mat); + ~ProcessedCustomMaterial(); + + virtual bool setupPass(SceneRenderState *, const SceneData& sgData, U32 pass); + virtual bool init( const FeatureSet &features, const GFXVertexFormat *vertexFormat, const MatFeaturesDelegate &featuresDelegate ); + virtual void setTextureStages(SceneRenderState *, const SceneData &sgData, U32 pass ); + virtual MaterialParameters* allocMaterialParameters(); + +protected: + + virtual void _setStageData(); + virtual bool _hasCubemap(U32 pass); + void _initPassStateBlock( RenderPassData *rpd, GFXStateBlockDesc &result ); + virtual void _initPassStateBlocks(); + +private: + + CustomMaterial* mCustomMaterial; + + /// The conditioner macros passed to the + /// shader on construction. + Vector mConditionerMacros; + + /// How many texture slots are we using. + U32 mMaxTex; + + template + void setMaterialParameter(MaterialParameters* param, MaterialParameterHandle* handle, + const String& value); + void setMatrixParameter(MaterialParameters* param, + MaterialParameterHandle* handle, const String& value, GFXShaderConstType matrixType); +}; + +#endif // _MATERIALS_PROCESSEDCUSTOMMATERIAL_H_ diff --git a/Engine/source/materials/processedFFMaterial.cpp b/Engine/source/materials/processedFFMaterial.cpp new file mode 100644 index 000000000..500783aee --- /dev/null +++ b/Engine/source/materials/processedFFMaterial.cpp @@ -0,0 +1,378 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "materials/processedFFMaterial.h" + +#include "gfx/sim/cubemapData.h" +#include "materials/sceneData.h" +#include "materials/customMaterialDefinition.h" +#include "materials/materialFeatureTypes.h" +#include "gfx/sim/gfxStateBlockData.h" +#include "gfx/gfxDevice.h" +#include "gfx/genericConstBuffer.h" +#include "materials/materialParameters.h" +#include "lighting/lightInfo.h" +#include "scene/sceneRenderState.h" +#include "core/util/safeDelete.h" +#include "math/util/matrixSet.h" + +class FFMaterialParameterHandle : public MaterialParameterHandle +{ +public: + virtual ~FFMaterialParameterHandle() {} + virtual const String& getName() const { return mName; } + virtual bool isValid() const { return false; } + virtual S32 getSamplerRegister( U32 pass ) const { return -1; } +private: + String mName; +}; + +ProcessedFFMaterial::ProcessedFFMaterial() +{ + VECTOR_SET_ASSOCIATION( mParamDesc ); + + _construct(); +} + +ProcessedFFMaterial::ProcessedFFMaterial(Material &mat, const bool isLightingMaterial) +{ + VECTOR_SET_ASSOCIATION( mParamDesc ); + + _construct(); + mMaterial = &mat; + mIsLightingMaterial = isLightingMaterial; +} + +void ProcessedFFMaterial::_construct() +{ + mHasSetStageData = false; + mHasGlow = false; + mIsLightingMaterial = false; + mDefaultHandle = new FFMaterialParameterHandle(); + mDefaultParameters = new MaterialParameters(); + mCurrentParams = mDefaultParameters; +} + +ProcessedFFMaterial::~ProcessedFFMaterial() +{ + SAFE_DELETE(mDefaultParameters); + SAFE_DELETE( mDefaultHandle ); +} + +void ProcessedFFMaterial::_createPasses( U32 stageNum, const FeatureSet &features ) +{ + FixedFuncFeatureData featData; + _determineFeatures(stageNum, featData, features); + // Just create a simple pass! + _addPass(0, featData); + + mFeatures.clear(); + if ( featData.features[FixedFuncFeatureData::DiffuseMap] ) + mFeatures.addFeature( MFT_DiffuseMap ); + if ( featData.features[FixedFuncFeatureData::LightMap] ) + mFeatures.addFeature( MFT_LightMap ); + if ( featData.features[FixedFuncFeatureData::ToneMap] ) + mFeatures.addFeature( MFT_ToneMap ); + +} + +void ProcessedFFMaterial::_determineFeatures( U32 stageNum, + FixedFuncFeatureData& featData, + const FeatureSet &features ) +{ + if ( mStages[stageNum].getTex( MFT_DiffuseMap ) ) + featData.features[FixedFuncFeatureData::DiffuseMap] = true; + + if ( features.hasFeature( MFT_LightMap ) ) + featData.features[FixedFuncFeatureData::LightMap] = true; + if ( features.hasFeature( MFT_ToneMap )) + featData.features[FixedFuncFeatureData::ToneMap] = true; +} + +U32 ProcessedFFMaterial::getNumStages() +{ + // Loops through all stages to determine how many stages we actually use + U32 numStages = 0; + + U32 i; + for( i=0; imCubemapData || mMaterial->mDynamicCubemap ) + { + numStages++; + continue; + } + } + + // If we have a texture for the a feature the + // stage is active. + if ( mStages[i].hasValidTex() ) + stageActive = true; + + // If this stage has specular lighting, it's active + if ( mMaterial->mPixelSpecular[i] ) + stageActive = true; + + // If we have a Material that is vertex lit + // then it may not have a texture + if( mMaterial->mVertLit[i] ) + { + stageActive = true; + } + + // Increment the number of active stages + numStages += stageActive; + } + + + return numStages; +} + +bool ProcessedFFMaterial::setupPass( SceneRenderState *state, const SceneData &sgData, U32 pass ) +{ + PROFILE_SCOPE( ProcessedFFMaterial_SetupPass ); + + // Make sure we have a pass + if(pass >= mPasses.size()) + return false; + + _setRenderState( state, sgData, pass ); + + // Bind our textures + setTextureStages( state, sgData, pass ); + return true; +} + +void ProcessedFFMaterial::setTextureStages(SceneRenderState * state, const SceneData& sgData, U32 pass) +{ + // We may need to do some trickery in here for fixed function, this is just copy/paste from MatInstance +#ifdef TORQUE_DEBUG + AssertFatal( passmNumTex; i++ ) + { + U32 currTexFlag = rpd->mTexType[i]; + if (!LIGHTMGR || !LIGHTMGR->setTextureStage(sgData, currTexFlag, i, NULL, NULL)) + { + switch( currTexFlag ) + { + case Material::NoTexture: + if (rpd->mTexSlot[i].texObject) + GFX->setTexture( i, rpd->mTexSlot[i].texObject ); + break; + + case Material::NormalizeCube: + GFX->setCubeTexture(i, Material::GetNormalizeCube()); + break; + + case Material::Lightmap: + GFX->setTexture( i, sgData.lightmap ); + break; + + case Material::Cube: + // TODO: Is this right? + GFX->setTexture( i, rpd->mTexSlot[0].texObject ); + break; + + case Material::SGCube: + // No cubemap support just yet + //GFX->setCubeTexture( i, sgData.cubemap ); + GFX->setTexture( i, rpd->mTexSlot[0].texObject ); + break; + + case Material::BackBuff: + GFX->setTexture( i, sgData.backBuffTex ); + break; + } + } + } +} + +MaterialParameters* ProcessedFFMaterial::allocMaterialParameters() +{ + return new MaterialParameters(); +} + +MaterialParameters* ProcessedFFMaterial::getDefaultMaterialParameters() +{ + return mDefaultParameters; +} + +MaterialParameterHandle* ProcessedFFMaterial::getMaterialParameterHandle(const String& name) +{ + return mDefaultHandle; +} + +void ProcessedFFMaterial::setTransforms(const MatrixSet &matrixSet, SceneRenderState *state, const U32 pass) +{ + GFX->setWorldMatrix(matrixSet.getObjectToWorld()); + GFX->setViewMatrix(matrixSet.getWorldToCamera()); + GFX->setProjectionMatrix(matrixSet.getCameraToScreen()); +} + +void ProcessedFFMaterial::setSceneInfo(SceneRenderState * state, const SceneData& sgData, U32 pass) +{ + _setPrimaryLightInfo(*sgData.objTrans, sgData.lights[0], pass); + _setSecondaryLightInfo(*sgData.objTrans, sgData.lights[1]); +} + +void ProcessedFFMaterial::_setPrimaryLightInfo(const MatrixF &_objTrans, LightInfo* light, U32 pass) +{ + // Just in case + GFX->setGlobalAmbientColor(ColorF(0.0f, 0.0f, 0.0f, 1.0f)); + if ( light->getType() == LightInfo::Ambient ) + { + // Ambient light + GFX->setGlobalAmbientColor( light->getAmbient() ); + return; + } + + GFX->setLight(0, NULL); + GFX->setLight(1, NULL); + // This is a quick hack that lets us use FF lights + GFXLightMaterial lightMat; + lightMat.ambient = ColorF(1.0f, 1.0f, 1.0f, 1.0f); + lightMat.diffuse = ColorF(1.0f, 1.0f, 1.0f, 1.0f); + lightMat.emissive = ColorF(0.0f, 0.0f, 0.0f, 0.0f); + lightMat.specular = ColorF(0.0f, 0.0f, 0.0f, 0.0f); + lightMat.shininess = 128.0f; + GFX->setLightMaterial(lightMat); + + // set object transform + MatrixF objTrans = _objTrans; + objTrans.inverse(); + + // fill in primary light + //------------------------- + GFXLightInfo xlatedLight; + light->setGFXLight(&xlatedLight); + Point3F lightPos = light->getPosition(); + Point3F lightDir = light->getDirection(); + objTrans.mulP(lightPos); + objTrans.mulV(lightDir); + + xlatedLight.mPos = lightPos; + xlatedLight.mDirection = lightDir; + + GFX->setLight(0, &xlatedLight); +} + +void ProcessedFFMaterial::_setSecondaryLightInfo(const MatrixF &_objTrans, LightInfo* light) +{ + // set object transform + MatrixF objTrans = _objTrans; + objTrans.inverse(); + + // fill in secondary light + //------------------------- + GFXLightInfo xlatedLight; + light->setGFXLight(&xlatedLight); + + Point3F lightPos = light->getPosition(); + Point3F lightDir = light->getDirection(); + objTrans.mulP(lightPos); + objTrans.mulV(lightDir); + + xlatedLight.mPos = lightPos; + xlatedLight.mDirection = lightDir; + + GFX->setLight(1, &xlatedLight); +} + +bool ProcessedFFMaterial::init( const FeatureSet &features, + const GFXVertexFormat *vertexFormat, + const MatFeaturesDelegate &featuresDelegate ) +{ + TORQUE_UNUSED( vertexFormat ); + TORQUE_UNUSED( featuresDelegate ); + + _setStageData(); + + // Just create a simple pass + _createPasses(0, features); + _initRenderPassDataStateBlocks(); + mStateHint.init( this ); + + return true; +} + +void ProcessedFFMaterial::_addPass(U32 stageNum, FixedFuncFeatureData& featData) +{ + U32 numTex = 0; + + // Just creates a simple pass, but it can still glow! + RenderPassData rpd; + + // Base texture, texunit 0 + if(featData.features[FixedFuncFeatureData::DiffuseMap]) + { + rpd.mTexSlot[0].texObject = mStages[stageNum].getTex( MFT_DiffuseMap ); + rpd.mTexType[0] = Material::NoTexture; + numTex++; + } + + // lightmap, texunit 1 + if(featData.features[FixedFuncFeatureData::LightMap]) + { + rpd.mTexType[1] = Material::Lightmap; + numTex++; + } + + rpd.mNumTex = numTex; + rpd.mStageNum = stageNum; + rpd.mGlow = false; + + mPasses.push_back( new RenderPassData(rpd) ); +} + +void ProcessedFFMaterial::_setPassBlendOp() +{ + +} + +void ProcessedFFMaterial::_initPassStateBlock( RenderPassData *rpd, GFXStateBlockDesc &result ) +{ + Parent::_initPassStateBlock( rpd, result ); + + if ( mIsLightingMaterial ) + { + result.ffLighting = true; + result.blendDefined = true; + result.blendEnable = true; + result.blendSrc = GFXBlendOne; + result.blendSrc = GFXBlendOne; + } + + // This is here for generic FF shader fallbacks. + CustomMaterial* custmat = dynamic_cast(mMaterial); + if (custmat && custmat->getStateBlockData() ) + result.addDesc(custmat->getStateBlockData()->getState()); +} diff --git a/Engine/source/materials/processedFFMaterial.h b/Engine/source/materials/processedFFMaterial.h new file mode 100644 index 000000000..654653d6a --- /dev/null +++ b/Engine/source/materials/processedFFMaterial.h @@ -0,0 +1,125 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _MATERIALS_PROCESSEDFFMATERIAL_H_ +#define _MATERIALS_PROCESSEDFFMATERIAL_H_ + +#ifndef _MATERIALS_PROCESSEDMATERIAL_H_ +#include "materials/processedMaterial.h" +#endif + +class LightInfo; +struct GFXShaderConstDesc; + + +/// Fixed function rendering. Does not load or use shaders. Does not rely on GFXMaterialFeatureData. +/// Tries very hard to not rely on anything possibly related to shaders. +/// +/// @note Does not always succeed. +class ProcessedFFMaterial : public ProcessedMaterial +{ + typedef ProcessedMaterial Parent; +public: + ProcessedFFMaterial(); + ProcessedFFMaterial(Material &mat, const bool isLightingMaterial = false); + ~ProcessedFFMaterial(); + /// @name Render state setting + /// + /// @{ + + /// Sets necessary textures and texture ops for rendering + virtual void setTextureStages(SceneRenderState *, const SceneData &sgData, U32 pass ); + + virtual MaterialParameters* allocMaterialParameters(); + virtual MaterialParameterHandle* getMaterialParameterHandle(const String& name); + virtual MaterialParameters* getDefaultMaterialParameters(); + + virtual void setTransforms(const MatrixSet &matrixSet, SceneRenderState *state, const U32 pass); + + virtual void setSceneInfo(SceneRenderState *, const SceneData& sgData, U32 pass); + + /// @} + + virtual bool init( const FeatureSet &features, + const GFXVertexFormat *vertexFormat, + const MatFeaturesDelegate &featuresDelegate ); + + /// Sets up the given pass + /// + /// @returns false if the pass could not be set up + virtual bool setupPass(SceneRenderState *, const SceneData& sgData, U32 pass); + + /// Returns the number of stages we're using (not to be confused with the number of passes) + virtual U32 getNumStages(); + +protected: + + MaterialParameterHandle* mDefaultHandle; + MaterialParameters* mDefaultParameters; + + struct FixedFuncFeatureData + { + enum + { + DiffuseMap, + LightMap, + ToneMap, + NumFeatures + }; + bool features[NumFeatures]; + }; + + bool mIsLightingMaterial; + + Vector mParamDesc; + + /// @name Internal functions + /// + /// @{ + + /// Adds a pass for the given stage + virtual void _addPass(U32 stageNum, FixedFuncFeatureData& featData); + + /// Chooses a blend op for the pass during pass creation + virtual void _setPassBlendOp(); + + /// Creates all necessary passes for the given stage + void _createPasses( U32 stageNum, const FeatureSet &features ); + + /// Determine what features we need + void _determineFeatures( U32 stageNum, + FixedFuncFeatureData &featData, + const FeatureSet &features); + + /// Sets light info for the first light + virtual void _setPrimaryLightInfo(const MatrixF &objTrans, LightInfo* light, U32 pass); + + /// Sets light info for the second light + virtual void _setSecondaryLightInfo(const MatrixF &objTrans, LightInfo* light); + + /// Does the base render state block setting, normally per pass + virtual void _initPassStateBlock( RenderPassData *rpd, GFXStateBlockDesc &result ); + /// @} + + void _construct(); +}; + +#endif diff --git a/Engine/source/materials/processedMaterial.cpp b/Engine/source/materials/processedMaterial.cpp new file mode 100644 index 000000000..3e5d0df40 --- /dev/null +++ b/Engine/source/materials/processedMaterial.cpp @@ -0,0 +1,480 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "materials/processedMaterial.h" + +#include "materials/sceneData.h" +#include "materials/materialParameters.h" +#include "materials/matTextureTarget.h" +#include "materials/materialFeatureTypes.h" +#include "materials/materialManager.h" +#include "scene/sceneRenderState.h" +#include "gfx/gfxPrimitiveBuffer.h" +#include "gfx/sim/cubemapData.h" + + +RenderPassData::RenderPassData() +{ + reset(); +} + +void RenderPassData::reset() +{ + for( U32 i = 0; i < Material::MAX_TEX_PER_PASS; ++ i ) + destructInPlace( &mTexSlot[ i ] ); + + dMemset( &mTexSlot, 0, sizeof(mTexSlot) ); + dMemset( &mTexType, 0, sizeof(mTexType) ); + + mCubeMap = NULL; + mNumTex = mNumTexReg = mStageNum = 0; + mGlow = false; + mBlendOp = Material::None; + + mFeatureData.clear(); + + for (U32 i = 0; i < STATE_MAX; i++) + mRenderStates[i] = NULL; +} + +String RenderPassData::describeSelf() const +{ + String desc; + + // Now write all the textures. + String texName; + for ( U32 i=0; i < Material::MAX_TEX_PER_PASS; i++ ) + { + if ( mTexType[i] == Material::TexTarget ) + texName = ( mTexSlot[i].texTarget ) ? mTexSlot[i].texTarget->getName() : "null_texTarget"; + else if ( mTexType[i] == Material::Cube && mCubeMap ) + texName = mCubeMap->getPath(); + else if ( mTexSlot[i].texObject ) + texName = mTexSlot[i].texObject->getPath(); + else + continue; + + desc += String::ToString( "TexSlot %d: %d, %s\n", i, mTexType[i], texName.c_str() ); + } + + // Write out the first render state which is the + // basis for all the other states and shoud be + // enough to define the pass uniquely. + desc += mRenderStates[0]->getDesc().describeSelf(); + + return desc; +} + +ProcessedMaterial::ProcessedMaterial() +: mMaterial( NULL ), + mCurrentParams( NULL ), + mHasSetStageData( false ), + mHasGlow( false ), + mMaxStages( 0 ), + mVertexFormat( NULL ), + mUserObject( NULL ) +{ + VECTOR_SET_ASSOCIATION( mPasses ); +} + +ProcessedMaterial::~ProcessedMaterial() +{ + for_each( mPasses.begin(), mPasses.end(), delete_pointer() ); +} + +void ProcessedMaterial::_setBlendState(Material::BlendOp blendOp, GFXStateBlockDesc& desc ) +{ + switch( blendOp ) + { + case Material::Add: + { + desc.blendSrc = GFXBlendOne; + desc.blendDest = GFXBlendOne; + break; + } + case Material::AddAlpha: + { + desc.blendSrc = GFXBlendSrcAlpha; + desc.blendDest = GFXBlendOne; + break; + } + case Material::Mul: + { + desc.blendSrc = GFXBlendDestColor; + desc.blendDest = GFXBlendZero; + break; + } + case Material::LerpAlpha: + { + desc.blendSrc = GFXBlendSrcAlpha; + desc.blendDest = GFXBlendInvSrcAlpha; + break; + } + + default: + { + // default to LerpAlpha + desc.blendSrc = GFXBlendSrcAlpha; + desc.blendDest = GFXBlendInvSrcAlpha; + break; + } + } +} + +void ProcessedMaterial::setBuffers(GFXVertexBufferHandleBase* vertBuffer, GFXPrimitiveBufferHandle* primBuffer) +{ + GFX->setVertexBuffer( *vertBuffer ); + GFX->setPrimitiveBuffer( *primBuffer ); +} + +bool ProcessedMaterial::stepInstance() +{ + AssertFatal( false, "ProcessedMaterial::stepInstance() - This type of material doesn't support instancing!" ); + return false; +} + +String ProcessedMaterial::_getTexturePath(const String& filename) +{ + // if '/', then path is specified, use it. + if( filename.find('/') != String::NPos ) + { + return filename; + } + + // otherwise, construct path + return mMaterial->getPath() + filename; +} + +GFXTexHandle ProcessedMaterial::_createTexture( const char* filename, GFXTextureProfile *profile) +{ + return GFXTexHandle( _getTexturePath(filename), profile, avar("%s() - NA (line %d)", __FUNCTION__, __LINE__) ); +} + +void ProcessedMaterial::addStateBlockDesc(const GFXStateBlockDesc& sb) +{ + mUserDefined = sb; +} + +void ProcessedMaterial::_initStateBlockTemplates(GFXStateBlockDesc& stateTranslucent, GFXStateBlockDesc& stateGlow, GFXStateBlockDesc& stateReflect) +{ + // Translucency + stateTranslucent.blendDefined = true; + stateTranslucent.blendEnable = mMaterial->mTranslucentBlendOp != Material::None; + _setBlendState(mMaterial->mTranslucentBlendOp, stateTranslucent); + stateTranslucent.zDefined = true; + stateTranslucent.zWriteEnable = mMaterial->mTranslucentZWrite; + stateTranslucent.alphaDefined = true; + stateTranslucent.alphaTestEnable = mMaterial->mAlphaTest; + stateTranslucent.alphaTestRef = mMaterial->mAlphaRef; + stateTranslucent.alphaTestFunc = GFXCmpGreaterEqual; + stateTranslucent.samplersDefined = true; + stateTranslucent.samplers[0].textureColorOp = GFXTOPModulate; + stateTranslucent.samplers[0].alphaOp = GFXTOPModulate; + stateTranslucent.samplers[0].alphaArg1 = GFXTATexture; + stateTranslucent.samplers[0].alphaArg2 = GFXTADiffuse; + + // Glow + stateGlow.zDefined = true; + stateGlow.zWriteEnable = false; + + // Reflect + stateReflect.cullDefined = true; + stateReflect.cullMode = mMaterial->mDoubleSided ? GFXCullNone : GFXCullCW; +} + +void ProcessedMaterial::_initRenderPassDataStateBlocks() +{ + for (U32 pass = 0; pass < mPasses.size(); pass++) + _initRenderStateStateBlocks( mPasses[pass] ); +} + +void ProcessedMaterial::_initPassStateBlock( RenderPassData *rpd, GFXStateBlockDesc &result ) +{ + if ( rpd->mBlendOp != Material::None ) + { + result.blendDefined = true; + result.blendEnable = true; + _setBlendState( rpd->mBlendOp, result ); + } + + if (mMaterial->isDoubleSided()) + { + result.cullDefined = true; + result.cullMode = GFXCullNone; + } + + if(mMaterial->mAlphaTest) + { + result.alphaDefined = true; + result.alphaTestEnable = mMaterial->mAlphaTest; + result.alphaTestRef = mMaterial->mAlphaRef; + result.alphaTestFunc = GFXCmpGreaterEqual; + } + + result.samplersDefined = true; + NamedTexTarget *texTarget; + + U32 maxAnisotropy = 1; + if ( mMaterial->mUseAnisotropic[ rpd->mStageNum ] ) + maxAnisotropy = MATMGR->getDefaultAnisotropy(); + + for( U32 i=0; i < rpd->mNumTex; i++ ) + { + U32 currTexFlag = rpd->mTexType[i]; + + switch( currTexFlag ) + { + default: + { + result.samplers[i].textureColorOp = GFXTOPModulate; + result.samplers[i].addressModeU = GFXAddressWrap; + result.samplers[i].addressModeV = GFXAddressWrap; + + if ( maxAnisotropy > 1 ) + { + result.samplers[i].minFilter = GFXTextureFilterAnisotropic; + result.samplers[i].magFilter = GFXTextureFilterAnisotropic; + result.samplers[i].maxAnisotropy = maxAnisotropy; + } + else + { + result.samplers[i].minFilter = GFXTextureFilterLinear; + result.samplers[i].magFilter = GFXTextureFilterLinear; + } + break; + } + + case Material::Cube: + case Material::SGCube: + case Material::NormalizeCube: + { + result.samplers[i].addressModeU = GFXAddressClamp; + result.samplers[i].addressModeV = GFXAddressClamp; + result.samplers[i].addressModeW = GFXAddressClamp; + break; + } + + case Material::TexTarget: + { + texTarget = mPasses[0]->mTexSlot[i].texTarget; + if ( texTarget ) + texTarget->setupSamplerState( &result.samplers[i] ); + break; + } + } + } + + // The prepass will take care of writing to the + // zbuffer, so we don't have to by default. + // The prepass can't write to the backbuffer's zbuffer in OpenGL. + if ( MATMGR->getPrePassEnabled() && + !GFX->getAdapterType() == OpenGL && + !mFeatures.hasFeature(MFT_ForwardShading)) + result.setZReadWrite( result.zEnable, false ); + + result.addDesc(mUserDefined); +} + +/// Creates the default state blocks for a list of render states +void ProcessedMaterial::_initRenderStateStateBlocks( RenderPassData *rpd ) +{ + GFXStateBlockDesc stateTranslucent; + GFXStateBlockDesc stateGlow; + GFXStateBlockDesc stateReflect; + GFXStateBlockDesc statePass; + + _initStateBlockTemplates( stateTranslucent, stateGlow, stateReflect ); + _initPassStateBlock( rpd, statePass ); + + // Ok, we've got our templates set up, let's combine them together based on state and + // create our state blocks. + for (U32 i = 0; i < RenderPassData::STATE_MAX; i++) + { + GFXStateBlockDesc stateFinal; + + if (i & RenderPassData::STATE_REFLECT) + stateFinal.addDesc(stateReflect); + if (i & RenderPassData::STATE_TRANSLUCENT) + stateFinal.addDesc(stateTranslucent); + if (i & RenderPassData::STATE_GLOW) + stateFinal.addDesc(stateGlow); + + stateFinal.addDesc(statePass); + + if (i & RenderPassData::STATE_WIREFRAME) + stateFinal.fillMode = GFXFillWireframe; + + GFXStateBlockRef sb = GFX->createStateBlock(stateFinal); + rpd->mRenderStates[i] = sb; + } +} + +U32 ProcessedMaterial::_getRenderStateIndex( const SceneRenderState *sceneState, + const SceneData &sgData ) +{ + // Based on what the state of the world is, get our render state block + U32 currState = 0; + + // NOTE: We should only use per-material or per-pass hints to + // change the render state. This is importaint because we + // only change the state blocks between material passes. + // + // For example sgData.visibility would be bad to use + // in here without changing how RenderMeshMgr works. + + if ( sgData.binType == SceneData::GlowBin ) + currState |= RenderPassData::STATE_GLOW; + + if ( sceneState && sceneState->isReflectPass() ) + currState |= RenderPassData::STATE_REFLECT; + + if ( sgData.binType != SceneData::PrePassBin && + mMaterial->isTranslucent() ) + currState |= RenderPassData::STATE_TRANSLUCENT; + + if ( sgData.wireframe ) + currState |= RenderPassData::STATE_WIREFRAME; + + return currState; +} + +void ProcessedMaterial::_setRenderState( const SceneRenderState *state, + const SceneData& sgData, + U32 pass ) +{ + // Make sure we have the pass + if ( pass >= mPasses.size() ) + return; + + U32 currState = _getRenderStateIndex( state, sgData ); + + GFX->setStateBlock(mPasses[pass]->mRenderStates[currState]); +} + + +void ProcessedMaterial::_setStageData() +{ + // Only do this once + if ( mHasSetStageData ) + return; + mHasSetStageData = true; + + U32 i; + + // Load up all the textures for every possible stage + for( i=0; imDiffuseMapFilename[i].isNotEmpty() ) + { + mStages[i].setTex( MFT_DiffuseMap, _createTexture( mMaterial->mDiffuseMapFilename[i], &GFXDefaultStaticDiffuseProfile ) ); + if (!mStages[i].getTex( MFT_DiffuseMap )) + { + mMaterial->logError("Failed to load diffuse map %s for stage %i", _getTexturePath(mMaterial->mDiffuseMapFilename[i]).c_str(), i); + + // Load a debug texture to make it clear to the user + // that the texture for this stage was missing. + mStages[i].setTex( MFT_DiffuseMap, _createTexture( "core/art/missingTexture", &GFXDefaultStaticDiffuseProfile ) ); + } + } + + // OverlayMap + if( mMaterial->mOverlayMapFilename[i].isNotEmpty() ) + { + mStages[i].setTex( MFT_OverlayMap, _createTexture( mMaterial->mOverlayMapFilename[i], &GFXDefaultStaticDiffuseProfile ) ); + if(!mStages[i].getTex( MFT_OverlayMap )) + mMaterial->logError("Failed to load overlay map %s for stage %i", _getTexturePath(mMaterial->mOverlayMapFilename[i]).c_str(), i); + } + + // LightMap + if( mMaterial->mLightMapFilename[i].isNotEmpty() ) + { + mStages[i].setTex( MFT_LightMap, _createTexture( mMaterial->mLightMapFilename[i], &GFXDefaultStaticDiffuseProfile ) ); + if(!mStages[i].getTex( MFT_LightMap )) + mMaterial->logError("Failed to load light map %s for stage %i", _getTexturePath(mMaterial->mLightMapFilename[i]).c_str(), i); + } + + // ToneMap + if( mMaterial->mToneMapFilename[i].isNotEmpty() ) + { + mStages[i].setTex( MFT_ToneMap, _createTexture( mMaterial->mToneMapFilename[i], &GFXDefaultStaticDiffuseProfile ) ); + if(!mStages[i].getTex( MFT_ToneMap )) + mMaterial->logError("Failed to load tone map %s for stage %i", _getTexturePath(mMaterial->mToneMapFilename[i]).c_str(), i); + } + + // DetailMap + if( mMaterial->mDetailMapFilename[i].isNotEmpty() ) + { + mStages[i].setTex( MFT_DetailMap, _createTexture( mMaterial->mDetailMapFilename[i], &GFXDefaultStaticDiffuseProfile ) ); + if(!mStages[i].getTex( MFT_DetailMap )) + mMaterial->logError("Failed to load detail map %s for stage %i", _getTexturePath(mMaterial->mDetailMapFilename[i]).c_str(), i); + } + + // NormalMap + if( mMaterial->mNormalMapFilename[i].isNotEmpty() ) + { + mStages[i].setTex( MFT_NormalMap, _createTexture( mMaterial->mNormalMapFilename[i], &GFXDefaultStaticNormalMapProfile ) ); + if(!mStages[i].getTex( MFT_NormalMap )) + mMaterial->logError("Failed to load normal map %s for stage %i", _getTexturePath(mMaterial->mNormalMapFilename[i]).c_str(), i); + } + + // Detail Normal Map + if( mMaterial->mDetailNormalMapFilename[i].isNotEmpty() ) + { + mStages[i].setTex( MFT_DetailNormalMap, _createTexture( mMaterial->mDetailNormalMapFilename[i], &GFXDefaultStaticNormalMapProfile ) ); + if(!mStages[i].getTex( MFT_DetailNormalMap )) + mMaterial->logError("Failed to load normal map %s for stage %i", _getTexturePath(mMaterial->mDetailNormalMapFilename[i]).c_str(), i); + } + + // SpecularMap + if( mMaterial->mSpecularMapFilename[i].isNotEmpty() ) + { + mStages[i].setTex( MFT_SpecularMap, _createTexture( mMaterial->mSpecularMapFilename[i], &GFXDefaultStaticDiffuseProfile ) ); + if(!mStages[i].getTex( MFT_SpecularMap )) + mMaterial->logError("Failed to load specular map %s for stage %i", _getTexturePath(mMaterial->mSpecularMapFilename[i]).c_str(), i); + } + + // EnironmentMap + if( mMaterial->mEnvMapFilename[i].isNotEmpty() ) + { + mStages[i].setTex( MFT_EnvMap, _createTexture( mMaterial->mEnvMapFilename[i], &GFXDefaultStaticDiffuseProfile ) ); + if(!mStages[i].getTex( MFT_EnvMap )) + mMaterial->logError("Failed to load environment map %s for stage %i", _getTexturePath(mMaterial->mEnvMapFilename[i]).c_str(), i); + } + } + + mMaterial->mCubemapData = dynamic_cast(Sim::findObject( mMaterial->mCubemapName )); + if( !mMaterial->mCubemapData ) + mMaterial->mCubemapData = NULL; + + + // If we have a cubemap put it on stage 0 (cubemaps only supported on stage 0) + if( mMaterial->mCubemapData ) + { + mMaterial->mCubemapData->createMap(); + mStages[0].setCubemap( mMaterial->mCubemapData->mCubemap ); + if ( !mStages[0].getCubemap() ) + mMaterial->logError("Failed to load cubemap"); + } +} + diff --git a/Engine/source/materials/processedMaterial.h b/Engine/source/materials/processedMaterial.h new file mode 100644 index 000000000..841825aab --- /dev/null +++ b/Engine/source/materials/processedMaterial.h @@ -0,0 +1,306 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MATERIALS_PROCESSEDMATERIAL_H_ +#define _MATERIALS_PROCESSEDMATERIAL_H_ + +#ifndef _MATERIALDEFINITION_H_ +#include "materials/materialDefinition.h" +#endif +#ifndef _MATERIALFEATUREDATA_H_ +#include "materials/materialFeatureData.h" +#endif +#ifndef _GFXSTATEBLOCK_H_ +#include "gfx/gfxStateBlock.h" +#endif +#ifndef _MATTEXTURETARGET_H_ +#include "materials/matTextureTarget.h" +#endif +#ifndef _MATSTATEHINT_H_ +#include "materials/matStateHint.h" +#endif + +class ShaderFeature; +class MaterialParameters; +class MaterialParameterHandle; +class SceneRenderState; +class GFXVertexBufferHandleBase; +class GFXPrimitiveBufferHandle; +class MatrixSet; + + +/// This contains the common data needed to render a pass. +struct RenderPassData +{ +public: + + struct TexSlotT + { + /// This is the default type of texture which + /// is valid with most texture types. + /// @see mTexType + GFXTexHandle texObject; + + /// Only valid when the texture type is set + /// to Material::TexTarget. + /// @see mTexType + NamedTexTargetRef texTarget; + + } mTexSlot[Material::MAX_TEX_PER_PASS]; + + U32 mTexType[Material::MAX_TEX_PER_PASS]; + + /// The cubemap to use when the texture type is + /// set to Material::Cube. + /// @see mTexType + GFXCubemap *mCubeMap; + + U32 mNumTex; + + U32 mNumTexReg; + + MaterialFeatureData mFeatureData; + + bool mGlow; + + Material::BlendOp mBlendOp; + + U32 mStageNum; + + /// State permutations, used to index into + /// the render states array. + /// @see mRenderStates + enum + { + STATE_REFLECT = 1, + STATE_TRANSLUCENT = 2, + STATE_GLOW = 4, + STATE_WIREFRAME = 8, + STATE_MAX = 16 + }; + + /// + GFXStateBlockRef mRenderStates[STATE_MAX]; + + RenderPassData(); + + virtual ~RenderPassData() { reset(); } + + virtual void reset(); + + /// Creates and returns a unique description string. + virtual String describeSelf() const; +}; + +/// This is an abstract base class which provides the external +/// interface all subclasses must implement. This interface +/// primarily consists of setting state. Pass creation +/// is implementation specific, and internal, thus it is +/// not in this base class. +class ProcessedMaterial +{ +public: + ProcessedMaterial(); + virtual ~ProcessedMaterial(); + + /// @name State setting functions + /// + /// @{ + + /// + virtual void addStateBlockDesc(const GFXStateBlockDesc& sb); + + /// + virtual void updateStateBlocks() { _initRenderPassDataStateBlocks(); } + + /// Set the user defined shader macros. + virtual void setShaderMacros( const Vector ¯os ) { mUserMacros = macros; } + + /// Sets the textures needed for rendering the current pass + virtual void setTextureStages(SceneRenderState *, const SceneData &sgData, U32 pass ) = 0; + + /// Sets the transformation matrix, i.e. Model * View * Projection + virtual void setTransforms(const MatrixSet &matrixSet, SceneRenderState *state, const U32 pass) = 0; + + /// Sets the scene info like lights for the given pass. + virtual void setSceneInfo(SceneRenderState *, const SceneData& sgData, U32 pass) = 0; + + /// Sets the given vertex and primitive buffers so we can render geometry + virtual void setBuffers(GFXVertexBufferHandleBase* vertBuffer, GFXPrimitiveBufferHandle* primBuffer); + + /// @see BaseMatInstance::setUserObject + virtual void setUserObject( SimObject *userObject ) { mUserObject = userObject; } + + /// + virtual bool stepInstance(); + + /// @} + + /// Initializes us (eg. loads textures, creates passes, generates shaders) + virtual bool init( const FeatureSet& features, + const GFXVertexFormat *vertexFormat, + const MatFeaturesDelegate &featuresDelegate ) = 0; + + /// Returns the state hint which can be used for + /// sorting and fast comparisions of the equality + /// of a material instance. + virtual const MatStateHint& getStateHint() const { return mStateHint; } + + /// Sets up the given pass. Returns true if the pass was set up, false if there was an error or if + /// the specified pass is out of bounds. + virtual bool setupPass(SceneRenderState *, const SceneData& sgData, U32 pass) = 0; + + // Material parameter methods + virtual MaterialParameters* allocMaterialParameters() = 0; + virtual MaterialParameters* getDefaultMaterialParameters() = 0; + virtual void setMaterialParameters(MaterialParameters* param, S32 pass) { mCurrentParams = param; }; + virtual MaterialParameters* getMaterialParameters() { return mCurrentParams; } + virtual MaterialParameterHandle* getMaterialParameterHandle(const String& name) = 0; + + /// Returns the pass data for the given pass. + RenderPassData* getPass(U32 pass) + { + if(pass >= mPasses.size()) + return NULL; + return mPasses[pass]; + } + + /// Returns the pass data for the given pass. + const RenderPassData* getPass( U32 pass ) const { return mPasses[pass]; } + + /// Returns the number of stages we're rendering (not to be confused with the number of passes). + virtual U32 getNumStages() = 0; + + /// Returns the number of passes we are rendering (not to be confused with the number of stages). + U32 getNumPasses() const { return mPasses.size(); } + + /// Returns true if any pass glows + bool hasGlow() const { return mHasGlow; } + + /// Gets the stage number for a pass + U32 getStageFromPass(U32 pass) const + { + if(pass >= mPasses.size()) + return 0; + return mPasses[pass]->mStageNum; + } + + /// Returns the active features in use by this material. + /// @see BaseMatInstance::getFeatures + const FeatureSet& getFeatures() const { return mFeatures; } + + /// Dump shader info, or FF texture info? + virtual void dumpMaterialInfo() { } + + /// Returns the source material. + Material* getMaterial() const { return mMaterial; } + + /// Returns the texture used by a stage + GFXTexHandle getStageTexture(U32 stage, const FeatureType &type) + { + return (stage < Material::MAX_STAGES) ? mStages[stage].getTex(type) : NULL; + } + +protected: + + /// Our passes. + Vector mPasses; + + /// The active features in use by this material. + FeatureSet mFeatures; + + /// The material which we are processing. + Material* mMaterial; + + MaterialParameters* mCurrentParams; + + /// Material::StageData is used here because the shader + /// generator throws a fit if it's passed anything else. + Material::StageData mStages[Material::MAX_STAGES]; + + /// If we've already loaded the stage data + bool mHasSetStageData; + + /// If we glow + bool mHasGlow; + + /// Number of stages (not to be confused with number of passes) + U32 mMaxStages; + + /// The vertex format on which this material will render. + const GFXVertexFormat *mVertexFormat; + + /// Set by addStateBlockDesc, should be considered + /// when initPassStateBlock is called. + GFXStateBlockDesc mUserDefined; + + /// The user defined macros to pass to the + /// shader initialization. + Vector mUserMacros; + + /// The user defined object to pass to ShaderFeature::createConstHandles. + SimObject *mUserObject; + + /// The state hint used for material sorting + /// and quick equality comparision. + MatStateHint mStateHint; + + /// Loads all the textures for all of the stages in the Material + virtual void _setStageData(); + + /// Sets the blend state for rendering + void _setBlendState(Material::BlendOp blendOp, GFXStateBlockDesc& desc ); + + /// Returns the path the material will attempt to load for a given texture filename. + String _getTexturePath(const String& filename); + + /// Loads the texture located at _getTexturePath(filename) and gives it the specified profile + GFXTexHandle _createTexture( const char *filename, GFXTextureProfile *profile ); + + /// @name State blocks + /// + /// @{ + + /// Creates the default state block templates, used by initStateBlocks. + virtual void _initStateBlockTemplates(GFXStateBlockDesc& stateTranslucent, GFXStateBlockDesc& stateGlow, GFXStateBlockDesc& stateReflect); + + /// Does the base render state block setting, normally per pass. + virtual void _initPassStateBlock( RenderPassData *rpd, GFXStateBlockDesc& result); + + /// Creates the default state blocks for a list of render states. + virtual void _initRenderStateStateBlocks( RenderPassData *rpd ); + + /// Creates the default state blocks for each RenderPassData item. + virtual void _initRenderPassDataStateBlocks(); + + /// This returns the index into the renderState array based on the sgData passed in. + virtual U32 _getRenderStateIndex( const SceneRenderState *state, + const SceneData &sgData ); + + /// Activates the correct mPasses[currPass].renderState based on scene graph info + virtual void _setRenderState( const SceneRenderState *state, + const SceneData &sgData, + U32 pass ); + /// @ +}; + +#endif // _MATERIALS_PROCESSEDMATERIAL_H_ diff --git a/Engine/source/materials/processedShaderMaterial.cpp b/Engine/source/materials/processedShaderMaterial.cpp new file mode 100644 index 000000000..db6e70803 --- /dev/null +++ b/Engine/source/materials/processedShaderMaterial.cpp @@ -0,0 +1,1291 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "materials/processedShaderMaterial.h" + +#include "core/util/safeDelete.h" +#include "gfx/sim/cubemapData.h" +#include "gfx/gfxShader.h" +#include "gfx/genericConstBuffer.h" +#include "gfx/gfxPrimitiveBuffer.h" +#include "scene/sceneRenderState.h" +#include "shaderGen/shaderFeature.h" +#include "shaderGen/shaderGenVars.h" +#include "shaderGen/featureMgr.h" +#include "shaderGen/shaderGen.h" +#include "materials/sceneData.h" +#include "materials/materialFeatureTypes.h" +#include "materials/materialManager.h" +#include "materials/shaderMaterialParameters.h" +#include "materials/matTextureTarget.h" +#include "gfx/util/screenspace.h" +#include "math/util/matrixSet.h" + +// We need to include customMaterialDefinition for ShaderConstHandles::init +#include "materials/customMaterialDefinition.h" + +/// +/// ShaderConstHandles +/// +void ShaderConstHandles::init( GFXShader *shader, CustomMaterial* mat /*=NULL*/ ) +{ + mDiffuseColorSC = shader->getShaderConstHandle("$diffuseMaterialColor"); + mTexMatSC = shader->getShaderConstHandle(ShaderGenVars::texMat); + mToneMapTexSC = shader->getShaderConstHandle(ShaderGenVars::toneMap); + mSpecularColorSC = shader->getShaderConstHandle(ShaderGenVars::specularColor); + mSpecularPowerSC = shader->getShaderConstHandle(ShaderGenVars::specularPower); + mParallaxInfoSC = shader->getShaderConstHandle("$parallaxInfo"); + mFogDataSC = shader->getShaderConstHandle(ShaderGenVars::fogData); + mFogColorSC = shader->getShaderConstHandle(ShaderGenVars::fogColor); + mDetailScaleSC = shader->getShaderConstHandle(ShaderGenVars::detailScale); + mVisiblitySC = shader->getShaderConstHandle(ShaderGenVars::visibility); + mColorMultiplySC = shader->getShaderConstHandle(ShaderGenVars::colorMultiply); + mAlphaTestValueSC = shader->getShaderConstHandle(ShaderGenVars::alphaTestValue); + mModelViewProjSC = shader->getShaderConstHandle(ShaderGenVars::modelview); + mWorldViewOnlySC = shader->getShaderConstHandle(ShaderGenVars::worldViewOnly); + mWorldToCameraSC = shader->getShaderConstHandle(ShaderGenVars::worldToCamera); + mWorldToObjSC = shader->getShaderConstHandle(ShaderGenVars::worldToObj); + mViewToObjSC = shader->getShaderConstHandle(ShaderGenVars::viewToObj); + mCubeTransSC = shader->getShaderConstHandle(ShaderGenVars::cubeTrans); + mObjTransSC = shader->getShaderConstHandle(ShaderGenVars::objTrans); + mCubeEyePosSC = shader->getShaderConstHandle(ShaderGenVars::cubeEyePos); + mEyePosSC = shader->getShaderConstHandle(ShaderGenVars::eyePos); + mEyePosWorldSC = shader->getShaderConstHandle(ShaderGenVars::eyePosWorld); + m_vEyeSC = shader->getShaderConstHandle(ShaderGenVars::vEye); + mEyeMatSC = shader->getShaderConstHandle(ShaderGenVars::eyeMat); + mOneOverFarplane = shader->getShaderConstHandle(ShaderGenVars::oneOverFarplane); + mAccumTimeSC = shader->getShaderConstHandle(ShaderGenVars::accumTime); + mMinnaertConstantSC = shader->getShaderConstHandle(ShaderGenVars::minnaertConstant); + mSubSurfaceParamsSC = shader->getShaderConstHandle(ShaderGenVars::subSurfaceParams); + mDiffuseAtlasParamsSC = shader->getShaderConstHandle(ShaderGenVars::diffuseAtlasParams); + mDiffuseAtlasTileSC = shader->getShaderConstHandle(ShaderGenVars::diffuseAtlasTileParams); + mBumpAtlasParamsSC = shader->getShaderConstHandle(ShaderGenVars::bumpAtlasParams); + mBumpAtlasTileSC = shader->getShaderConstHandle(ShaderGenVars::bumpAtlasTileParams); + mRTSizeSC = shader->getShaderConstHandle( "$targetSize" ); + mOneOverRTSizeSC = shader->getShaderConstHandle( "$oneOverTargetSize" ); + mDetailBumpStrength = shader->getShaderConstHandle( "$detailBumpStrength" ); + mViewProjSC = shader->getShaderConstHandle( "$viewProj" ); + + // MFT_ImposterVert + mImposterUVs = shader->getShaderConstHandle( "$imposterUVs" ); + mImposterLimits = shader->getShaderConstHandle( "$imposterLimits" ); + + for (S32 i = 0; i < TEXTURE_STAGE_COUNT; ++i) + mRTParamsSC[i] = shader->getShaderConstHandle( String::ToString( "$rtParams%d", i ) ); + + // Clear any existing texture handles. + dMemset( mTexHandlesSC, 0, sizeof( mTexHandlesSC ) ); + if(mat) + { + for (S32 i = 0; i < Material::MAX_TEX_PER_PASS; ++i) + mTexHandlesSC[i] = shader->getShaderConstHandle(mat->mSamplerNames[i]); + } +} + +/// +/// ShaderRenderPassData +/// +void ShaderRenderPassData::reset() +{ + Parent::reset(); + + shader = NULL; + + for ( U32 i=0; i < featureShaderHandles.size(); i++ ) + delete featureShaderHandles[i]; + + featureShaderHandles.clear(); +} + +String ShaderRenderPassData::describeSelf() const +{ + // First write the shader identification. + String desc = String::ToString( "%s\n", shader->describeSelf().c_str() ); + + // Let the parent get the rest. + desc += Parent::describeSelf(); + + return desc; +} + +/// +/// ProcessedShaderMaterial +/// +ProcessedShaderMaterial::ProcessedShaderMaterial() + : mDefaultParameters( NULL ), + mInstancingState( NULL ) +{ + VECTOR_SET_ASSOCIATION( mShaderConstDesc ); + VECTOR_SET_ASSOCIATION( mParameterHandles ); +} + +ProcessedShaderMaterial::ProcessedShaderMaterial(Material &mat) + : mDefaultParameters( NULL ), + mInstancingState( NULL ) +{ + VECTOR_SET_ASSOCIATION( mShaderConstDesc ); + VECTOR_SET_ASSOCIATION( mParameterHandles ); + mMaterial = &mat; +} + +ProcessedShaderMaterial::~ProcessedShaderMaterial() +{ + SAFE_DELETE(mInstancingState); + SAFE_DELETE(mDefaultParameters); + for (U32 i = 0; i < mParameterHandles.size(); i++) + SAFE_DELETE(mParameterHandles[i]); +} + +// +// Material init +// +bool ProcessedShaderMaterial::init( const FeatureSet &features, + const GFXVertexFormat *vertexFormat, + const MatFeaturesDelegate &featuresDelegate ) +{ + // Load our textures + _setStageData(); + + // Determine how many stages we use + mMaxStages = getNumStages(); + mVertexFormat = vertexFormat; + mFeatures.clear(); + mStateHint.clear(); + SAFE_DELETE(mInstancingState); + + for( U32 i=0; isetFormat( &_getRPD( 0 )->shader->mInstancingFormat, mVertexFormat ); + } + + // Check for a RenderTexTargetBin assignment + // *IMPORTANT NOTE* + // This is a temporary solution for getting diffuse mapping working with tex targets for standard materials + // It should be removed once this is done properly, at that time the sAllowTextureTargetAssignment should also be removed + // from Material (it is necessary for catching shadow maps/post effect this shouldn't be applied to) + if (Material::sAllowTextureTargetAssignment) + if (mMaterial && mMaterial->mDiffuseMapFilename[0].isNotEmpty() && mMaterial->mDiffuseMapFilename[0].substr( 0, 1 ).equal("#")) + { + String texTargetBufferName = mMaterial->mDiffuseMapFilename[0].substr(1, mMaterial->mDiffuseMapFilename[0].length() - 1); + NamedTexTarget *texTarget = NamedTexTarget::find( texTargetBufferName ); + + RenderPassData* rpd = getPass(0); + + if (rpd) + { + rpd->mTexSlot[0].texTarget = texTarget; + rpd->mTexType[0] = Material::TexTarget; + } + } + + return true; +} + +U32 ProcessedShaderMaterial::getNumStages() +{ + // Loops through all stages to determine how many + // stages we actually use. + // + // The first stage is always active else we shouldn't be + // creating the material to begin with. + U32 numStages = 1; + + U32 i; + for( i=1; imCubemapData || mMaterial->mDynamicCubemap ) + { + numStages++; + continue; + } + } + + // If we have a texture for the a feature the + // stage is active. + if ( mStages[i].hasValidTex() ) + stageActive = true; + + // If this stage has specular lighting, it's active + if ( mMaterial->mPixelSpecular[i] ) + stageActive = true; + + // If this stage has diffuse color, it's active + if ( mMaterial->mDiffuse[i].alpha > 0 && + mMaterial->mDiffuse[i] != ColorF::WHITE ) + stageActive = true; + + // If we have a Material that is vertex lit + // then it may not have a texture + if( mMaterial->mVertLit[i] ) + stageActive = true; + + // Increment the number of active stages + numStages += stageActive; + } + + return numStages; +} + +void ProcessedShaderMaterial::_determineFeatures( U32 stageNum, + MaterialFeatureData &fd, + const FeatureSet &features ) +{ + PROFILE_SCOPE( ProcessedShaderMaterial_DetermineFeatures ); + + const float shaderVersion = GFX->getPixelShaderVersion(); + AssertFatal(shaderVersion > 0.0 , "Cannot create a shader material if we don't support shaders"); + + bool lastStage = stageNum == (mMaxStages-1); + + // First we add all the features which the + // material has defined. + + if ( mMaterial->isTranslucent() ) + { + // Note: This is for decal blending into the prepass + // for AL... it probably needs to be made clearer. + if ( mMaterial->mTranslucentBlendOp == Material::LerpAlpha && + mMaterial->mTranslucentZWrite ) + fd.features.addFeature( MFT_IsTranslucentZWrite ); + else + { + fd.features.addFeature( MFT_IsTranslucent ); + fd.features.addFeature( MFT_ForwardShading ); + } + } + + // TODO: This sort of sucks... BL should somehow force this + // feature on from the outside and not this way. + if ( dStrcmp( LIGHTMGR->getId(), "BLM" ) == 0 ) + fd.features.addFeature( MFT_ForwardShading ); + + // Disabling the InterlacedPrePass feature for now. It is not ready for prime-time + // and it should not be triggered off of the DoubleSided parameter. [2/5/2010 Pat] + /*if ( mMaterial->isDoubleSided() ) + { + fd.features.addFeature( MFT_InterlacedPrePass ); + }*/ + + // Allow instancing if it was requested and the card supports + // SM 3.0 or above. + // + // We also disable instancing for non-single pass materials + // and glowing materials because its untested/unimplemented. + // + if ( features.hasFeature( MFT_UseInstancing ) && + mMaxStages == 1 && + !mMaterial->mGlow[0] && + shaderVersion >= 3.0f ) + fd.features.addFeature( MFT_UseInstancing ); + + if ( mMaterial->mAlphaTest ) + fd.features.addFeature( MFT_AlphaTest ); + + if ( mMaterial->mEmissive[stageNum] ) + fd.features.addFeature( MFT_IsEmissive ); + else + fd.features.addFeature( MFT_RTLighting ); + + if ( mMaterial->mAnimFlags[stageNum] ) + fd.features.addFeature( MFT_TexAnim ); + + if ( mMaterial->mVertLit[stageNum] ) + fd.features.addFeature( MFT_VertLit ); + + // cubemaps only available on stage 0 for now - bramage + if ( stageNum < 1 && + ( ( mMaterial->mCubemapData && mMaterial->mCubemapData->mCubemap ) || + mMaterial->mDynamicCubemap ) ) + fd.features.addFeature( MFT_CubeMap ); + + fd.features.addFeature( MFT_Visibility ); + + if ( lastStage && + ( !gClientSceneGraph->usePostEffectFog() || + fd.features.hasFeature( MFT_IsTranslucent ) || + fd.features.hasFeature( MFT_ForwardShading )) ) + fd.features.addFeature( MFT_Fog ); + + if ( mMaterial->mMinnaertConstant[stageNum] > 0.0f ) + fd.features.addFeature( MFT_MinnaertShading ); + + if ( mMaterial->mSubSurface[stageNum] ) + fd.features.addFeature( MFT_SubSurface ); + + if ( !mMaterial->mCellLayout[stageNum].isZero() ) + { + fd.features.addFeature( MFT_DiffuseMapAtlas ); + + if ( mMaterial->mNormalMapAtlas ) + fd.features.addFeature( MFT_NormalMapAtlas ); + } + + // Grab other features like normal maps, base texture, etc. + FeatureSet mergeFeatures; + mStages[stageNum].getFeatureSet( &mergeFeatures ); + fd.features.merge( mergeFeatures ); + + if ( fd.features[ MFT_NormalMap ] ) + { + if ( mStages[stageNum].getTex( MFT_NormalMap )->mFormat == GFXFormatDXT5 && + !mStages[stageNum].getTex( MFT_NormalMap )->mHasTransparency ) + fd.features.addFeature( MFT_IsDXTnm ); + } + + // Now for some more advanced features that we + // cannot do on SM 2.0 and below. + if ( shaderVersion > 2.0f ) + { + // Only allow parallax if we have a normal map and + // we're not using DXTnm compression. + if ( mMaterial->mParallaxScale[stageNum] > 0.0f && + fd.features[ MFT_NormalMap ] && + !fd.features[ MFT_IsDXTnm ] ) + fd.features.addFeature( MFT_Parallax ); + + // If not parallax then allow per-pixel specular if + // we have real time lighting enabled. + else if ( fd.features[MFT_RTLighting] && + mMaterial->mPixelSpecular[stageNum] ) + fd.features.addFeature( MFT_PixSpecular ); + } + + // Without realtime lighting and on lower end + // shader models disable the specular map. + if ( !fd.features[ MFT_RTLighting ] || shaderVersion == 2.0 ) + fd.features.removeFeature( MFT_SpecularMap ); + + // If we have a specular map then make sure we + // have per-pixel specular enabled. + if( fd.features[ MFT_SpecularMap ] ) + { + fd.features.addFeature( MFT_PixSpecular ); + + // Check for an alpha channel on the specular map. If it has one (and it + // has values less than 255) than the artist has put the gloss map into + // the alpha channel. + if( mStages[stageNum].getTex( MFT_SpecularMap )->mHasTransparency ) + fd.features.addFeature( MFT_GlossMap ); + } + + // Without a base texture use the diffuse color + // feature to ensure some sort of output. + if (!fd.features[MFT_DiffuseMap]) + { + fd.features.addFeature( MFT_DiffuseColor ); + + // No texture coords... no overlay. + fd.features.removeFeature( MFT_OverlayMap ); + } + + // If we have a diffuse map and the alpha on the diffuse isn't + // zero and the color isn't pure white then multiply the color. + else if ( mMaterial->mDiffuse[stageNum].alpha > 0.0f && + mMaterial->mDiffuse[stageNum] != ColorF::WHITE ) + fd.features.addFeature( MFT_DiffuseColor ); + + // If lightmaps or tonemaps are enabled or we + // don't have a second UV set then we cannot + // use the overlay texture. + if ( fd.features[MFT_LightMap] || + fd.features[MFT_ToneMap] || + mVertexFormat->getTexCoordCount() < 2 ) + fd.features.removeFeature( MFT_OverlayMap ); + + // If tonemaps are enabled don't use lightmap + if ( fd.features[MFT_ToneMap] || mVertexFormat->getTexCoordCount() < 2 ) + fd.features.removeFeature( MFT_LightMap ); + + // Don't allow tonemaps if we don't have a second UV set + if ( mVertexFormat->getTexCoordCount() < 2 ) + fd.features.removeFeature( MFT_ToneMap ); + + // Always add the HDR output feature. + // + // It will be filtered out if it was disabled + // for this material creation below. + // + // Also the shader code will evaluate to a nop + // if HDR is not enabled in the scene. + // + fd.features.addFeature( MFT_HDROut ); + + // If vertex color is enabled on the material's stage and + // color is present in vertex format, add diffuse vertex + // color feature. + + if ( mMaterial->mVertColor[ stageNum ] && + mVertexFormat->hasColor() ) + fd.features.addFeature( MFT_DiffuseVertColor ); + + // Allow features to add themselves. + for ( U32 i = 0; i < FEATUREMGR->getFeatureCount(); i++ ) + { + const FeatureInfo &info = FEATUREMGR->getAt( i ); + info.feature->determineFeature( mMaterial, + mVertexFormat, + stageNum, + *info.type, + features, + &fd ); + } + + // Now disable any features that were + // not part of the input feature handle. + fd.features.filter( features ); +} + +bool ProcessedShaderMaterial::_createPasses( MaterialFeatureData &stageFeatures, U32 stageNum, const FeatureSet &features ) +{ + // Creates passes for the given stage + ShaderRenderPassData passData; + U32 texIndex = 0; + + for( U32 i=0; i < FEATUREMGR->getFeatureCount(); i++ ) + { + const FeatureInfo &info = FEATUREMGR->getAt( i ); + if ( !stageFeatures.features.hasFeature( *info.type ) ) + continue; + + U32 numTexReg = info.feature->getResources( stageFeatures ).numTexReg; + + // adds pass if blend op changes for feature + _setPassBlendOp( info.feature, passData, texIndex, stageFeatures, stageNum, features ); + + // Add pass if num tex reg is going to be too high + if( passData.mNumTexReg + numTexReg > GFX->getNumSamplers() ) + { + if( !_addPass( passData, texIndex, stageFeatures, stageNum, features ) ) + return false; + _setPassBlendOp( info.feature, passData, texIndex, stageFeatures, stageNum, features ); + } + + passData.mNumTexReg += numTexReg; + passData.mFeatureData.features.addFeature( *info.type ); + info.feature->setTexData( mStages[stageNum], stageFeatures, passData, texIndex ); + + // Add pass if tex units are maxed out + if( texIndex > GFX->getNumSamplers() ) + { + if( !_addPass( passData, texIndex, stageFeatures, stageNum, features ) ) + return false; + _setPassBlendOp( info.feature, passData, texIndex, stageFeatures, stageNum, features ); + } + } + + const FeatureSet &passFeatures = passData.mFeatureData.codify(); + if ( passFeatures.isNotEmpty() ) + { + mFeatures.merge( passFeatures ); + if( !_addPass( passData, texIndex, stageFeatures, stageNum, features ) ) + { + mFeatures.clear(); + return false; + } + } + + return true; +} + +void ProcessedShaderMaterial::_initMaterialParameters() +{ + // Cleanup anything left first. + SAFE_DELETE( mDefaultParameters ); + for ( U32 i = 0; i < mParameterHandles.size(); i++ ) + SAFE_DELETE( mParameterHandles[i] ); + + // Gather the shaders as they all need to be + // passed to the ShaderMaterialParameterHandles. + Vector shaders; + shaders.setSize( mPasses.size() ); + for ( U32 i = 0; i < mPasses.size(); i++ ) + shaders[i] = _getRPD(i)->shader; + + // Run through each shader and prepare its constants. + for ( U32 i = 0; i < mPasses.size(); i++ ) + { + const Vector& desc = shaders[i]->getShaderConstDesc(); + + Vector::const_iterator p = desc.begin(); + for ( ; p != desc.end(); p++ ) + { + // Add this to our list of shader constants + GFXShaderConstDesc d(*p); + mShaderConstDesc.push_back(d); + + ShaderMaterialParameterHandle* smph = new ShaderMaterialParameterHandle(d.name, shaders); + mParameterHandles.push_back(smph); + } + } +} + +bool ProcessedShaderMaterial::_addPass( ShaderRenderPassData &rpd, + U32 &texIndex, + MaterialFeatureData &fd, + U32 stageNum, + const FeatureSet &features ) +{ + // Set number of textures, stage, glow, etc. + rpd.mNumTex = texIndex; + rpd.mStageNum = stageNum; + rpd.mGlow |= mMaterial->mGlow[stageNum]; + + // Copy over features + rpd.mFeatureData.materialFeatures = fd.features; + + // Generate shader + GFXShader::setLogging( true, true ); + rpd.shader = SHADERGEN->getShader( rpd.mFeatureData, mVertexFormat, &mUserMacros ); + if( !rpd.shader ) + return false; + rpd.shaderHandles.init( rpd.shader ); + + // If a pass glows, we glow + if( rpd.mGlow ) + mHasGlow = true; + + ShaderRenderPassData *newPass = new ShaderRenderPassData( rpd ); + mPasses.push_back( newPass ); + + // Give each active feature a chance to create specialized shader consts. + for( U32 i=0; i < FEATUREMGR->getFeatureCount(); i++ ) + { + const FeatureInfo &info = FEATUREMGR->getAt( i ); + if ( !fd.features.hasFeature( *info.type ) ) + continue; + + ShaderFeatureConstHandles *fh = info.feature->createConstHandles( rpd.shader, mUserObject ); + if ( fh ) + newPass->featureShaderHandles.push_back( fh ); + } + + rpd.reset(); + texIndex = 0; + + return true; +} + +void ProcessedShaderMaterial::_setPassBlendOp( ShaderFeature *sf, + ShaderRenderPassData &passData, + U32 &texIndex, + MaterialFeatureData &stageFeatures, + U32 stageNum, + const FeatureSet &features ) +{ + if( sf->getBlendOp() == Material::None ) + { + return; + } + + // set up the current blend operation for multi-pass materials + if( mPasses.size() > 0) + { + // If passData.numTexReg is 0, this is a brand new pass, so set the + // blend operation to the first feature. + if( passData.mNumTexReg == 0 ) + { + passData.mBlendOp = sf->getBlendOp(); + } + else + { + // numTegReg is more than zero, if this feature + // doesn't have the same blend operation, then + // we need to create yet another pass + if( sf->getBlendOp() != passData.mBlendOp && mPasses[mPasses.size()-1]->mStageNum == stageNum) + { + _addPass( passData, texIndex, stageFeatures, stageNum, features ); + passData.mBlendOp = sf->getBlendOp(); + } + } + } +} + +// +// Runtime / rendering +// +bool ProcessedShaderMaterial::setupPass( SceneRenderState *state, const SceneData &sgData, U32 pass ) +{ + PROFILE_SCOPE( ProcessedShaderMaterial_SetupPass ); + + // Make sure we have the pass + if(pass >= mPasses.size()) + { + // If we were rendering instanced data tell + // the device to reset that vb stream. + if ( mInstancingState ) + GFX->setVertexBuffer( NULL, 1 ); + + return false; + } + + _setRenderState( state, sgData, pass ); + + // Set shaders + ShaderRenderPassData* rpd = _getRPD(pass); + if( rpd->shader ) + { + GFX->setShader( rpd->shader ); + GFX->setShaderConstBuffer(_getShaderConstBuffer(pass)); + _setShaderConstants(state, sgData, pass); + + // If we're instancing then do the initial step to get + // set the vb pointer to the const buffer. + if ( mInstancingState ) + stepInstance(); + } + else + { + GFX->disableShaders(); + GFX->setShaderConstBuffer(NULL); + } + + // Set our textures + setTextureStages( state, sgData, pass ); + _setTextureTransforms(pass); + + return true; +} + +void ProcessedShaderMaterial::setTextureStages( SceneRenderState *state, const SceneData &sgData, U32 pass ) +{ + PROFILE_SCOPE( ProcessedShaderMaterial_SetTextureStages ); + + ShaderConstHandles *handles = _getShaderConstHandles(pass); + + // Set all of the textures we need to render the give pass. +#ifdef TORQUE_DEBUG + AssertFatal( passmNumTex; i++ ) + { + U32 currTexFlag = rpd->mTexType[i]; + if (!LIGHTMGR || !LIGHTMGR->setTextureStage(sgData, currTexFlag, i, shaderConsts, handles)) + { + switch( currTexFlag ) + { + // If the flag is unset then assume its just + // a regular texture to set... nothing special. + case 0: + default: + GFX->setTexture(i, rpd->mTexSlot[i].texObject); + break; + + case Material::NormalizeCube: + GFX->setCubeTexture(i, Material::GetNormalizeCube()); + break; + + case Material::Lightmap: + GFX->setTexture( i, sgData.lightmap ); + break; + + case Material::ToneMapTex: + shaderConsts->setSafe(handles->mToneMapTexSC, (S32)i); + GFX->setTexture(i, rpd->mTexSlot[i].texObject); + break; + + case Material::Cube: + GFX->setCubeTexture( i, rpd->mCubeMap ); + break; + + case Material::SGCube: + GFX->setCubeTexture( i, sgData.cubemap ); + break; + + case Material::BackBuff: + GFX->setTexture( i, sgData.backBuffTex ); + break; + + case Material::TexTarget: + { + texTarget = rpd->mTexSlot[i].texTarget; + if ( !texTarget ) + { + GFX->setTexture( i, NULL ); + break; + } + + texObject = texTarget->getTexture(); + + // If no texture is available then map the default 2x2 + // black texture to it. This at least will ensure that + // we get consistant behavior across GPUs and platforms. + if ( !texObject ) + texObject = GFXTexHandle::ZERO; + + if ( handles->mRTParamsSC[i]->isValid() && texObject ) + { + const Point3I &targetSz = texObject->getSize(); + const RectI &targetVp = texTarget->getViewport(); + Point4F rtParams; + + ScreenSpace::RenderTargetParameters(targetSz, targetVp, rtParams); + + shaderConsts->set(handles->mRTParamsSC[i], rtParams); + } + + GFX->setTexture( i, texObject ); + break; + } + } + } + } +} + +void ProcessedShaderMaterial::_setTextureTransforms(const U32 pass) +{ + PROFILE_SCOPE( ProcessedShaderMaterial_SetTextureTransforms ); + + ShaderConstHandles* handles = _getShaderConstHandles(pass); + if (handles->mTexMatSC->isValid()) + { + MatrixF texMat( true ); + + mMaterial->updateTimeBasedParams(); + F32 waveOffset = _getWaveOffset( pass ); // offset is between 0.0 and 1.0 + + // handle scroll anim type + if( mMaterial->mAnimFlags[pass] & Material::Scroll ) + { + if( mMaterial->mAnimFlags[pass] & Material::Wave ) + { + Point3F scrollOffset; + scrollOffset.x = mMaterial->mScrollDir[pass].x * waveOffset; + scrollOffset.y = mMaterial->mScrollDir[pass].y * waveOffset; + scrollOffset.z = 1.0; + + texMat.setColumn( 3, scrollOffset ); + } + else + { + Point3F offset( mMaterial->mScrollOffset[pass].x, + mMaterial->mScrollOffset[pass].y, + 1.0 ); + + texMat.setColumn( 3, offset ); + } + + } + + // handle rotation + if( mMaterial->mAnimFlags[pass] & Material::Rotate ) + { + if( mMaterial->mAnimFlags[pass] & Material::Wave ) + { + F32 rotPos = waveOffset * M_2PI; + texMat.set( EulerF( 0.0, 0.0, rotPos ) ); + texMat.setColumn( 3, Point3F( 0.5, 0.5, 0.0 ) ); + + MatrixF test( true ); + test.setColumn( 3, Point3F( mMaterial->mRotPivotOffset[pass].x, + mMaterial->mRotPivotOffset[pass].y, + 0.0 ) ); + texMat.mul( test ); + } + else + { + texMat.set( EulerF( 0.0, 0.0, mMaterial->mRotPos[pass] ) ); + + texMat.setColumn( 3, Point3F( 0.5, 0.5, 0.0 ) ); + + MatrixF test( true ); + test.setColumn( 3, Point3F( mMaterial->mRotPivotOffset[pass].x, + mMaterial->mRotPivotOffset[pass].y, + 0.0 ) ); + texMat.mul( test ); + } + } + + // Handle scale + wave offset + if( mMaterial->mAnimFlags[pass] & Material::Scale && + mMaterial->mAnimFlags[pass] & Material::Wave ) + { + F32 wOffset = fabs( waveOffset ); + + texMat.setColumn( 3, Point3F( 0.5, 0.5, 0.0 ) ); + + MatrixF temp( true ); + temp.setRow( 0, Point3F( wOffset, 0.0, 0.0 ) ); + temp.setRow( 1, Point3F( 0.0, wOffset, 0.0 ) ); + temp.setRow( 2, Point3F( 0.0, 0.0, wOffset ) ); + temp.setColumn( 3, Point3F( -wOffset * 0.5, -wOffset * 0.5, 0.0 ) ); + texMat.mul( temp ); + } + + // handle sequence + if( mMaterial->mAnimFlags[pass] & Material::Sequence ) + { + U32 frameNum = (U32)(MATMGR->getTotalTime() * mMaterial->mSeqFramePerSec[pass]); + F32 offset = frameNum * mMaterial->mSeqSegSize[pass]; + + if ( mMaterial->mAnimFlags[pass] & Material::Scale ) + texMat.scale( Point3F( mMaterial->mSeqSegSize[pass], 1.0f, 1.0f ) ); + + Point3F texOffset = texMat.getPosition(); + texOffset.x += offset; + texMat.setPosition( texOffset ); + } + + GFXShaderConstBuffer* shaderConsts = _getShaderConstBuffer(pass); + shaderConsts->setSafe(handles->mTexMatSC, texMat); + } +} + +//-------------------------------------------------------------------------- +// Get wave offset for texture animations using a wave transform +//-------------------------------------------------------------------------- +F32 ProcessedShaderMaterial::_getWaveOffset( U32 stage ) +{ + switch( mMaterial->mWaveType[stage] ) + { + case Material::Sin: + { + return mMaterial->mWaveAmp[stage] * mSin( M_2PI * mMaterial->mWavePos[stage] ); + break; + } + + case Material::Triangle: + { + F32 frac = mMaterial->mWavePos[stage] - mFloor( mMaterial->mWavePos[stage] ); + if( frac > 0.0 && frac <= 0.25 ) + { + return mMaterial->mWaveAmp[stage] * frac * 4.0; + } + + if( frac > 0.25 && frac <= 0.5 ) + { + return mMaterial->mWaveAmp[stage] * ( 1.0 - ((frac-0.25)*4.0) ); + } + + if( frac > 0.5 && frac <= 0.75 ) + { + return mMaterial->mWaveAmp[stage] * (frac-0.5) * -4.0; + } + + if( frac > 0.75 && frac <= 1.0 ) + { + return -mMaterial->mWaveAmp[stage] * ( 1.0 - ((frac-0.75)*4.0) ); + } + + break; + } + + case Material::Square: + { + F32 frac = mMaterial->mWavePos[stage] - mFloor( mMaterial->mWavePos[stage] ); + if( frac > 0.0 && frac <= 0.5 ) + { + return 0.0; + } + else + { + return mMaterial->mWaveAmp[stage]; + } + break; + } + + } + + return 0.0; +} + +void ProcessedShaderMaterial::_setShaderConstants(SceneRenderState * state, const SceneData &sgData, U32 pass) +{ + PROFILE_SCOPE( ProcessedShaderMaterial_SetShaderConstants ); + + GFXShaderConstBuffer* shaderConsts = _getShaderConstBuffer(pass); + ShaderConstHandles* handles = _getShaderConstHandles(pass); + U32 stageNum = getStageFromPass(pass); + + // First we do all the constants which are not + // controlled via the material... we have to + // set these all the time as they could change. + + if ( handles->mFogDataSC->isValid() ) + { + Point3F fogData; + fogData.x = sgData.fogDensity; + fogData.y = sgData.fogDensityOffset; + fogData.z = sgData.fogHeightFalloff; + shaderConsts->set( handles->mFogDataSC, fogData ); + } + + shaderConsts->setSafe(handles->mFogColorSC, sgData.fogColor); + + if( handles->mOneOverFarplane->isValid() ) + { + const F32 &invfp = 1.0f / state->getFarPlane(); + Point4F oneOverFP(invfp, invfp, invfp, invfp); + shaderConsts->set( handles->mOneOverFarplane, oneOverFP ); + } + + shaderConsts->setSafe( handles->mAccumTimeSC, MATMGR->getTotalTime() ); + + // If the shader constants have not been lost then + // they contain the content from a previous render pass. + // + // In this case we can skip updating the material constants + // which do not change frame to frame. + // + // NOTE: This assumes we're not animating material parameters + // in a way that doesn't cause a shader reload... this isn't + // being done now, but it could change in the future. + // + if ( !shaderConsts->wasLost() ) + return; + + shaderConsts->setSafe(handles->mSpecularColorSC, mMaterial->mSpecular[stageNum]); + shaderConsts->setSafe(handles->mSpecularPowerSC, mMaterial->mSpecularPower[stageNum]); + + shaderConsts->setSafe(handles->mParallaxInfoSC, mMaterial->mParallaxScale[stageNum]); + shaderConsts->setSafe(handles->mMinnaertConstantSC, mMaterial->mMinnaertConstant[stageNum]); + + if ( handles->mSubSurfaceParamsSC->isValid() ) + { + Point4F subSurfParams; + dMemcpy( &subSurfParams, &mMaterial->mSubSurfaceColor[stageNum], sizeof(ColorF) ); + subSurfParams.w = mMaterial->mSubSurfaceRolloff[stageNum]; + shaderConsts->set(handles->mSubSurfaceParamsSC, subSurfParams); + } + + if ( handles->mRTSizeSC->isValid() ) + { + const Point2I &resolution = GFX->getActiveRenderTarget()->getSize(); + Point2F pixelShaderConstantData; + + pixelShaderConstantData.x = resolution.x; + pixelShaderConstantData.y = resolution.y; + + shaderConsts->set( handles->mRTSizeSC, pixelShaderConstantData ); + } + + if ( handles->mOneOverRTSizeSC->isValid() ) + { + const Point2I &resolution = GFX->getActiveRenderTarget()->getSize(); + Point2F oneOverTargetSize( 1.0f / (F32)resolution.x, 1.0f / (F32)resolution.y ); + + shaderConsts->set( handles->mOneOverRTSizeSC, oneOverTargetSize ); + } + + // set detail scale + shaderConsts->setSafe(handles->mDetailScaleSC, mMaterial->mDetailScale[stageNum]); + shaderConsts->setSafe(handles->mDetailBumpStrength, mMaterial->mDetailNormalMapStrength[stageNum]); + + // MFT_ImposterVert + if ( handles->mImposterUVs->isValid() ) + { + U32 uvCount = getMin( mMaterial->mImposterUVs.size(), 64 ); // See imposter.hlsl + AlignedArray imposterUVs( uvCount, sizeof( Point4F ), (U8*)mMaterial->mImposterUVs.address(), false ); + shaderConsts->set( handles->mImposterUVs, imposterUVs ); + } + shaderConsts->setSafe( handles->mImposterLimits, mMaterial->mImposterLimits ); + + // Diffuse + shaderConsts->setSafe(handles->mDiffuseColorSC, mMaterial->mDiffuse[stageNum]); + + shaderConsts->setSafe( handles->mAlphaTestValueSC, mClampF( (F32)mMaterial->mAlphaRef / 255.0f, 0.0f, 1.0f ) ); + + if(handles->mDiffuseAtlasParamsSC) + { + Point4F atlasParams(1.0f / mMaterial->mCellLayout[stageNum].x, // 1 / num_horizontal + 1.0f / mMaterial->mCellLayout[stageNum].y, // 1 / num_vertical + mMaterial->mCellSize[stageNum], // tile size in pixels + getBinLog2(mMaterial->mCellSize[stageNum]) ); // pow of 2 of tile size in pixels 2^9 = 512, 2^10=1024 etc + shaderConsts->setSafe(handles->mDiffuseAtlasParamsSC, atlasParams); + } + + if(handles->mBumpAtlasParamsSC) + { + Point4F atlasParams(1.0f / mMaterial->mCellLayout[stageNum].x, // 1 / num_horizontal + 1.0f / mMaterial->mCellLayout[stageNum].y, // 1 / num_vertical + mMaterial->mCellSize[stageNum], // tile size in pixels + getBinLog2(mMaterial->mCellSize[stageNum]) ); // pow of 2 of tile size in pixels 2^9 = 512, 2^10=1024 etc + shaderConsts->setSafe(handles->mBumpAtlasParamsSC, atlasParams); + } + + if(handles->mDiffuseAtlasTileSC) + { + // Sanity check the wrap flags + //AssertWarn(mMaterial->mTextureAddressModeU == mMaterial->mTextureAddressModeV, "Addresing mode mismatch, texture atlasing will be confused"); + Point4F atlasTileParams( mMaterial->mCellIndex[stageNum].x, // Tile co-ordinate, ie: [0, 3] + mMaterial->mCellIndex[stageNum].y, + 0.0f, 0.0f ); // TODO: Wrap mode flags? + shaderConsts->setSafe(handles->mDiffuseAtlasTileSC, atlasTileParams); + } + + if(handles->mBumpAtlasTileSC) + { + // Sanity check the wrap flags + //AssertWarn(mMaterial->mTextureAddressModeU == mMaterial->mTextureAddressModeV, "Addresing mode mismatch, texture atlasing will be confused"); + Point4F atlasTileParams( mMaterial->mCellIndex[stageNum].x, // Tile co-ordinate, ie: [0, 3] + mMaterial->mCellIndex[stageNum].y, + 0.0f, 0.0f ); // TODO: Wrap mode flags? + shaderConsts->setSafe(handles->mBumpAtlasTileSC, atlasTileParams); + } +} + +bool ProcessedShaderMaterial::_hasCubemap(U32 pass) +{ + // Only support cubemap on the first stage + if( mPasses[pass]->mStageNum > 0 ) + return false; + + if( mPasses[pass]->mCubeMap ) + return true; + + return false; +} + +void ProcessedShaderMaterial::setTransforms(const MatrixSet &matrixSet, SceneRenderState *state, const U32 pass) +{ + PROFILE_SCOPE( ProcessedShaderMaterial_setTransforms ); + + GFXShaderConstBuffer* shaderConsts = _getShaderConstBuffer(pass); + ShaderConstHandles* handles = _getShaderConstHandles(pass); + + // The MatrixSet will lazily generate a matrix under the + // various 'get' methods, so inline the test for a valid + // shader constant handle to avoid that work when we can. + if ( handles->mModelViewProjSC->isValid() ) + shaderConsts->set( handles->mModelViewProjSC, matrixSet.getWorldViewProjection() ); + if ( handles->mObjTransSC->isValid() ) + shaderConsts->set( handles->mObjTransSC, matrixSet.getObjectToWorld() ); + if ( handles->mWorldToObjSC->isValid() ) + shaderConsts->set( handles->mWorldToObjSC, matrixSet.getWorldToObject() ); + if ( handles->mWorldToCameraSC->isValid() ) + shaderConsts->set( handles->mWorldToCameraSC, matrixSet.getWorldToCamera() ); + if ( handles->mWorldViewOnlySC->isValid() ) + shaderConsts->set( handles->mWorldViewOnlySC, matrixSet.getObjectToCamera() ); + if ( handles->mViewToObjSC->isValid() ) + shaderConsts->set( handles->mViewToObjSC, matrixSet.getCameraToObject() ); + if ( handles->mViewProjSC->isValid() ) + shaderConsts->set( handles->mViewProjSC, matrixSet.getWorldToScreen() ); + + if ( handles->mCubeTransSC->isValid() && + ( _hasCubemap(pass) || mMaterial->mDynamicCubemap ) ) + { + // TODO: Could we not remove this constant? Use mObjTransSC and cast to float3x3 instead? + shaderConsts->set(handles->mCubeTransSC, matrixSet.getObjectToWorld(), GFXSCT_Float3x3); + } + + if ( handles->m_vEyeSC->isValid() ) + shaderConsts->set( handles->m_vEyeSC, state->getVectorEye() ); +} + +void ProcessedShaderMaterial::setSceneInfo(SceneRenderState * state, const SceneData& sgData, U32 pass) +{ + PROFILE_SCOPE( ProcessedShaderMaterial_setSceneInfo ); + + GFXShaderConstBuffer* shaderConsts = _getShaderConstBuffer(pass); + ShaderConstHandles* handles = _getShaderConstHandles(pass); + + // Set cubemap stuff here (it's convenient!) + const Point3F &eyePosWorld = state->getCameraPosition(); + if ( handles->mCubeEyePosSC->isValid() ) + { + if(_hasCubemap(pass) || mMaterial->mDynamicCubemap) + { + Point3F cubeEyePos = eyePosWorld - sgData.objTrans->getPosition(); + shaderConsts->set(handles->mCubeEyePosSC, cubeEyePos); + } + } + + shaderConsts->setSafe(handles->mVisiblitySC, sgData.visibility); + + shaderConsts->setSafe(handles->mEyePosWorldSC, eyePosWorld); + + if ( handles->mEyePosSC->isValid() ) + { + MatrixF tempMat( *sgData.objTrans ); + tempMat.inverse(); + Point3F eyepos; + tempMat.mulP( eyePosWorld, &eyepos ); + shaderConsts->set(handles->mEyePosSC, eyepos); + } + + shaderConsts->setSafe(handles->mEyeMatSC, state->getCameraTransform()); + + ShaderRenderPassData *rpd = _getRPD( pass ); + for ( U32 i=0; i < rpd->featureShaderHandles.size(); i++ ) + rpd->featureShaderHandles[i]->setConsts( state, sgData, shaderConsts ); + + LIGHTMGR->setLightInfo( this, mMaterial, sgData, state, pass, shaderConsts ); +} + +void ProcessedShaderMaterial::setBuffers( GFXVertexBufferHandleBase *vertBuffer, GFXPrimitiveBufferHandle *primBuffer ) +{ + PROFILE_SCOPE(ProcessedShaderMaterial_setBuffers); + + // If we're not instanced then just call the parent. + if ( !mInstancingState ) + { + Parent::setBuffers( vertBuffer, primBuffer ); + return; + } + + PROFILE_SCOPE(ProcessedShaderMaterial_setBuffers_instancing); + + const S32 instCount = mInstancingState->getCount(); + AssertFatal( instCount > 0, + "ProcessedShaderMaterial::setBuffers - No instances rendered!" ); + + // Nothing special here. + GFX->setPrimitiveBuffer( *primBuffer ); + + // Set the first stream the the normal VB and set the + // correct frequency for the number of instances to render. + GFX->setVertexBuffer( *vertBuffer, 0, instCount ); + + // Get a volatile VB and fill it with the vertex data. + const GFXVertexFormat *instFormat = mInstancingState->getFormat(); + GFXVertexBufferDataHandle instVB; + instVB.set( GFX, instFormat->getSizeInBytes(), instFormat, instCount, GFXBufferTypeVolatile ); + U8 *dest = instVB.lock(); + dMemcpy( dest, mInstancingState->getBuffer(), instFormat->getSizeInBytes() * instCount ); + instVB.unlock(); + + // Set the instance vb for streaming. + GFX->setVertexBuffer( instVB, 1, 1 ); + + // Finally set the vertex format which defines + // both of the streams. + GFX->setVertexFormat( mInstancingState->getDeclFormat() ); + + // Done... reset the count. + mInstancingState->resetStep(); +} + +bool ProcessedShaderMaterial::stepInstance() +{ + PROFILE_SCOPE(ProcessedShaderMaterial_stepInstance); + AssertFatal( mInstancingState, "ProcessedShaderMaterial::stepInstance - This material isn't instanced!" ); + return mInstancingState->step( &_getShaderConstBuffer( 0 )->mInstPtr ); +} + +MaterialParameters* ProcessedShaderMaterial::allocMaterialParameters() +{ + ShaderMaterialParameters* smp = new ShaderMaterialParameters(); + Vector buffers( __FILE__, __LINE__ ); + buffers.setSize(mPasses.size()); + for (U32 i = 0; i < mPasses.size(); i++) + buffers[i] = _getRPD(i)->shader->allocConstBuffer(); + // smp now owns these buffers. + smp->setBuffers(mShaderConstDesc, buffers); + return smp; +} + +MaterialParameterHandle* ProcessedShaderMaterial::getMaterialParameterHandle(const String& name) +{ + // Search our list + for (U32 i = 0; i < mParameterHandles.size(); i++) + { + if (mParameterHandles[i]->getName().equal(name)) + return mParameterHandles[i]; + } + + // If we didn't find it, we have to add it to support shader reloading. + + Vector shaders; + shaders.setSize(mPasses.size()); + for (U32 i = 0; i < mPasses.size(); i++) + shaders[i] = _getRPD(i)->shader; + + ShaderMaterialParameterHandle* smph = new ShaderMaterialParameterHandle( name, shaders ); + mParameterHandles.push_back(smph); + + return smph; +} + +/// This is here to deal with the differences between ProcessedCustomMaterials and ProcessedShaderMaterials. +GFXShaderConstBuffer* ProcessedShaderMaterial::_getShaderConstBuffer( const U32 pass ) +{ + if (pass < mPasses.size()) + { + return static_cast(mCurrentParams)->getBuffer(pass); + } + return NULL; +} + +ShaderConstHandles* ProcessedShaderMaterial::_getShaderConstHandles(const U32 pass) +{ + if (pass < mPasses.size()) + { + return &_getRPD(pass)->shaderHandles; + } + return NULL; +} + +void ProcessedShaderMaterial::dumpMaterialInfo() +{ + for ( U32 i = 0; i < getNumPasses(); i++ ) + { + const ShaderRenderPassData *passData = _getRPD( i ); + + if ( passData == NULL ) + continue; + + const GFXShader *shader = passData->shader; + + if ( shader == NULL ) + Con::printf( " [%i] [NULL shader]", i ); + else + Con::printf( " [%i] %s", i, shader->describeSelf().c_str() ); + } +} diff --git a/Engine/source/materials/processedShaderMaterial.h b/Engine/source/materials/processedShaderMaterial.h new file mode 100644 index 000000000..3858204bb --- /dev/null +++ b/Engine/source/materials/processedShaderMaterial.h @@ -0,0 +1,262 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MATERIALS_PROCESSEDSHADERMATERIAL_H_ +#define _MATERIALS_PROCESSEDSHADERMATERIAL_H_ + +#ifndef _MATERIALS_PROCESSEDMATERIAL_H_ +#include "processedMaterial.h" +#endif +#ifndef _GFXSHADER_H_ +#include "gfx/gfxShader.h" +#endif + +class GenericConstBufferLayout; +class ShaderData; +class LightInfo; +class ShaderMaterialParameterHandle; +class ShaderFeatureConstHandles; +class CustomMaterial; + + +class ShaderConstHandles +{ +public: + GFXShaderConstHandle* mDiffuseColorSC; + GFXShaderConstHandle* mToneMapTexSC; + GFXShaderConstHandle* mTexMatSC; + GFXShaderConstHandle* mSpecularColorSC; + GFXShaderConstHandle* mSpecularPowerSC; + GFXShaderConstHandle* mParallaxInfoSC; + GFXShaderConstHandle* mFogDataSC; + GFXShaderConstHandle* mFogColorSC; + GFXShaderConstHandle* mDetailScaleSC; + GFXShaderConstHandle* mVisiblitySC; + GFXShaderConstHandle* mColorMultiplySC; + GFXShaderConstHandle* mAlphaTestValueSC; + GFXShaderConstHandle* mModelViewProjSC; + GFXShaderConstHandle* mWorldViewOnlySC; + GFXShaderConstHandle* mWorldToCameraSC; + GFXShaderConstHandle* mWorldToObjSC; + GFXShaderConstHandle* mViewToObjSC; + GFXShaderConstHandle* mCubeTransSC; + GFXShaderConstHandle* mObjTransSC; + GFXShaderConstHandle* mCubeEyePosSC; + GFXShaderConstHandle* mEyePosSC; + GFXShaderConstHandle* mEyePosWorldSC; + GFXShaderConstHandle* m_vEyeSC; + GFXShaderConstHandle* mEyeMatSC; + GFXShaderConstHandle* mOneOverFarplane; + GFXShaderConstHandle* mAccumTimeSC; + GFXShaderConstHandle* mMinnaertConstantSC; + GFXShaderConstHandle* mSubSurfaceParamsSC; + GFXShaderConstHandle* mDiffuseAtlasParamsSC; + GFXShaderConstHandle* mBumpAtlasParamsSC; + GFXShaderConstHandle* mDiffuseAtlasTileSC; + GFXShaderConstHandle* mBumpAtlasTileSC; + GFXShaderConstHandle *mRTSizeSC; + GFXShaderConstHandle *mOneOverRTSizeSC; + GFXShaderConstHandle* mDetailBumpStrength; + GFXShaderConstHandle* mViewProjSC; + + GFXShaderConstHandle *mImposterUVs; + GFXShaderConstHandle *mImposterLimits; + + GFXShaderConstHandle* mTexHandlesSC[Material::MAX_TEX_PER_PASS]; + GFXShaderConstHandle* mRTParamsSC[TEXTURE_STAGE_COUNT]; + + void init( GFXShader* shader, CustomMaterial* mat = NULL ); +}; + +class ShaderRenderPassData : public RenderPassData +{ + typedef RenderPassData Parent; + +public: + + virtual ~ShaderRenderPassData() { reset(); } + + GFXShaderRef shader; + ShaderConstHandles shaderHandles; + Vector featureShaderHandles; + + virtual void reset(); + virtual String describeSelf() const; +}; + +class ProcessedShaderMaterial : public ProcessedMaterial +{ + typedef ProcessedMaterial Parent; +public: + + ProcessedShaderMaterial(); + ProcessedShaderMaterial(Material &mat); + ~ProcessedShaderMaterial(); + + // ProcessedMaterial + virtual bool init( const FeatureSet &features, + const GFXVertexFormat *vertexFormat, + const MatFeaturesDelegate &featuresDelegate ); + virtual bool setupPass(SceneRenderState *, const SceneData& sgData, U32 pass); + virtual void setTextureStages(SceneRenderState *, const SceneData &sgData, U32 pass ); + virtual void setTransforms(const MatrixSet &matrixSet, SceneRenderState *state, const U32 pass); + virtual void setSceneInfo(SceneRenderState *, const SceneData& sgData, U32 pass); + virtual void setBuffers(GFXVertexBufferHandleBase* vertBuffer, GFXPrimitiveBufferHandle* primBuffer); + virtual bool stepInstance(); + virtual void dumpMaterialInfo(); + virtual MaterialParameters* allocMaterialParameters(); + virtual MaterialParameters* getDefaultMaterialParameters() { return mDefaultParameters; } + virtual MaterialParameterHandle* getMaterialParameterHandle(const String& name); + virtual U32 getNumStages(); + +protected: + + Vector mShaderConstDesc; + MaterialParameters* mDefaultParameters; + Vector mParameterHandles; + + /// Hold the instancing state data for the material. + class InstancingState + { + const static U32 COUNT = 200; + + public: + + InstancingState() + : mInstFormat( NULL ), + mBuffer( NULL ), + mCount( -1 ) + { + } + + ~InstancingState() + { + delete [] mBuffer; + } + + void setFormat( const GFXVertexFormat *instFormat, const GFXVertexFormat *vertexFormat ) + { + mInstFormat = instFormat; + mDeclFormat.copy( *vertexFormat ); + mDeclFormat.append( *mInstFormat, 1 ); + mDeclFormat.getDecl(); + + delete [] mBuffer; + mBuffer = new U8[ mInstFormat->getSizeInBytes() * COUNT ]; + mCount = -1; + } + + bool step( U8 **outPtr ) + { + // Are we starting a new draw call? + if ( mCount < 0 ) + { + *outPtr = mBuffer; + mCount = 0; + } + else + { + // Increment to the next instance. + *outPtr += mInstFormat->getSizeInBytes(); + mCount++; + } + + return mCount < COUNT; + } + + void resetStep() { mCount = -1; } + + U8* getBuffer() const { return mBuffer; } + + S32 getCount() const { return mCount; } + + const GFXVertexFormat* getFormat() const { return mInstFormat; } + + const GFXVertexFormat* getDeclFormat() const { return &mDeclFormat; } + + protected: + + GFXVertexFormat mDeclFormat; + const GFXVertexFormat *mInstFormat; + U8 *mBuffer; + S32 mCount; + + }; + + /// The instancing state if this material + /// supports instancing. + InstancingState *mInstancingState; + + /// @name Internal functions + /// + /// @{ + + /// Adds a pass for the given stage. + virtual bool _addPass( ShaderRenderPassData &rpd, + U32 &texIndex, + MaterialFeatureData &fd, + U32 stageNum, + const FeatureSet &features); + + /// Chooses a blend op for the given pass + virtual void _setPassBlendOp( ShaderFeature *sf, + ShaderRenderPassData &passData, + U32 &texIndex, + MaterialFeatureData &stageFeatures, + U32 stageNum, + const FeatureSet &features); + + /// Creates passes for the given stage + virtual bool _createPasses( MaterialFeatureData &fd, U32 stageNum, const FeatureSet &features ); + + /// Fills in the MaterialFeatureData for the given stage + virtual void _determineFeatures( U32 stageNum, + MaterialFeatureData &fd, + const FeatureSet &features ); + + /// Do we have a cubemap on pass? + virtual bool _hasCubemap(U32 pass); + + /// Used by setTextureTransforms + F32 _getWaveOffset( U32 stage ); + + /// Sets texture transformation matrices for texture animations such as scale and wave + virtual void _setTextureTransforms(const U32 pass); + + /// Sets all of the necessary shader constants for the given pass + virtual void _setShaderConstants(SceneRenderState *, const SceneData &sgData, U32 pass); + + /// @} + + void _setPrimaryLightConst(const LightInfo* light, const MatrixF& objTrans, const U32 stageNum); + + /// This is here to deal with the differences between ProcessedCustomMaterials and ProcessedShaderMaterials. + virtual GFXShaderConstBuffer* _getShaderConstBuffer(const U32 pass); + virtual ShaderConstHandles* _getShaderConstHandles(const U32 pass); + + /// + virtual void _initMaterialParameters(); + + ShaderRenderPassData* _getRPD(const U32 pass) { return static_cast(mPasses[pass]); } +}; + +#endif // _MATERIALS_PROCESSEDSHADERMATERIAL_H_ diff --git a/Engine/source/materials/sceneData.h b/Engine/source/materials/sceneData.h new file mode 100644 index 000000000..bd719209e --- /dev/null +++ b/Engine/source/materials/sceneData.h @@ -0,0 +1,128 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _SCENEDATA_H_ +#define _SCENEDATA_H_ + +#ifndef _SCENERENDERSTATE_H_ +#include "scene/sceneRenderState.h" +#endif +#ifndef _LIGHTMANAGER_H_ +#include "lighting/lightManager.h" +#endif +#ifndef _GFXDEVICE_H_ +#include "gfx/gfxDevice.h" +#endif + +class GFXTexHandle; +class GFXCubemap; + + +struct SceneData +{ + /// The special bin types. + enum BinType + { + /// A normal render bin that isn't one of + /// the special bins we care about. + RegularBin = 0, + + /// The glow render bin. + /// @see RenderGlowMgr + GlowBin, + + /// The prepass render bin. + /// @RenderPrePassMgr + PrePassBin, + }; + + /// This defines when we're rendering a special bin + /// type that the material or lighting system needs + /// to know about. + BinType binType; + + // textures + GFXTextureObject *lightmap; + GFXTextureObject *backBuffTex; + GFXTextureObject *reflectTex; + GFXTextureObject *miscTex; + + /// The current lights to use in rendering + /// in order of the light importance. + LightInfo* lights[8]; + + /// + ColorF ambientLightColor; + + // fog + F32 fogDensity; + F32 fogDensityOffset; + F32 fogHeightFalloff; + ColorF fogColor; + + // misc + const MatrixF *objTrans; + GFXCubemap *cubemap; + F32 visibility; + + /// Enables wireframe rendering for the object. + bool wireframe; + + /// A generic hint value passed from the game + /// code down to the material for use by shader + /// features. + void *materialHint; + + /// Constructor. + SceneData() + { + dMemset( this, 0, sizeof( SceneData ) ); + objTrans = &MatrixF::Identity; + visibility = 1.0f; + } + + /// Initializes the data with the scene state setting + /// common scene wide parameters. + inline void init( const SceneRenderState *state, BinType type = RegularBin ) + { + dMemset( this, 0, sizeof( SceneData ) ); + setFogParams( state->getSceneManager()->getFogData() ); + wireframe = GFXDevice::getWireframe(); + binType = type; + objTrans = &MatrixF::Identity; + visibility = 1.0f; + ambientLightColor = state->getAmbientLightColor(); + } + + inline void setFogParams( const FogData &data ) + { + fogDensity = data.density; + fogDensityOffset = data.densityOffset; + if ( !mIsZero( data.atmosphereHeight ) ) + fogHeightFalloff = 1.0f / data.atmosphereHeight; + else + fogHeightFalloff = 0.0f; + + fogColor = data.color; + } +}; + +#endif // _SCENEDATA_H_ diff --git a/Engine/source/materials/shaderData.cpp b/Engine/source/materials/shaderData.cpp new file mode 100644 index 000000000..d0c526383 --- /dev/null +++ b/Engine/source/materials/shaderData.cpp @@ -0,0 +1,282 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "materials/shaderData.h" + +#include "console/consoleTypes.h" +#include "gfx/gfxDevice.h" +#include "core/strings/stringUnit.h" +#include "lighting/lightManager.h" +#include "console/engineAPI.h" + +using namespace Torque; + + +Vector ShaderData::smAllShaderData; + + +IMPLEMENT_CONOBJECT( ShaderData ); + +ConsoleDocClass( ShaderData, + "@brief Special type of data block that stores information about a handwritten shader.\n\n" + + "To use hand written shaders, a ShaderData datablock must be used. This datablock " + "refers only to the vertex and pixel shader filenames and a hardware target level. " + "Shaders are API specific, so DirectX and OpenGL shaders must be explicitly identified.\n\n " + + "@tsexample\n" + "// Used for the procedural clould system\n" + "singleton ShaderData( CloudLayerShader )\n" + "{\n" + " DXVertexShaderFile = \"shaders/common/cloudLayerV.hlsl\";\n" + " DXPixelShaderFile = \"shaders/common/cloudLayerP.hlsl\";\n" + " OGLVertexShaderFile = \"shaders/common/gl/cloudLayerV.glsl\";\n" + " OGLPixelShaderFile = \"shaders/common/gl/cloudLayerP.glsl\";\n" + " pixVersion = 2.0;\n" + "};\n" + "@endtsexample\n\n" + + "@ingroup Shaders\n"); + +ShaderData::ShaderData() +{ + VECTOR_SET_ASSOCIATION( mShaderMacros ); + + mUseDevicePixVersion = false; + mPixVersion = 1.0; +} + +void ShaderData::initPersistFields() +{ + addField("DXVertexShaderFile", TypeStringFilename, Offset(mDXVertexShaderName, ShaderData), + "@brief %Path to the DirectX vertex shader file to use for this ShaderData.\n\n" + "It must contain only one program and no pixel shader, just the vertex shader." + "It can be either an HLSL or assembly level shader. HLSL's must have a " + "filename extension of .hlsl, otherwise its assumed to be an assembly file."); + + addField("DXPixelShaderFile", TypeStringFilename, Offset(mDXPixelShaderName, ShaderData), + "@brief %Path to the DirectX pixel shader file to use for this ShaderData.\n\n" + "It must contain only one program and no vertex shader, just the pixel " + "shader. It can be either an HLSL or assembly level shader. HLSL's " + "must have a filename extension of .hlsl, otherwise its assumed to be an assembly file."); + + addField("OGLVertexShaderFile", TypeStringFilename, Offset(mOGLVertexShaderName, ShaderData), + "@brief %Path to an OpenGL vertex shader file to use for this ShaderData.\n\n" + "It must contain only one program and no pixel shader, just the vertex shader."); + + addField("OGLPixelShaderFile", TypeStringFilename, Offset(mOGLPixelShaderName, ShaderData), + "@brief %Path to an OpenGL pixel shader file to use for this ShaderData.\n\n" + "It must contain only one program and no vertex shader, just the pixel " + "shader."); + + addField("useDevicePixVersion", TypeBool, Offset(mUseDevicePixVersion, ShaderData), + "@brief If true, the maximum pixel shader version offered by the graphics card will be used.\n\n" + "Otherwise, the script-defined pixel shader version will be used.\n\n"); + + addField("pixVersion", TypeF32, Offset(mPixVersion, ShaderData), + "@brief Indicates target level the shader should be compiled.\n\n" + "Valid numbers at the time of this writing are 1.1, 1.4, 2.0, and 3.0. " + "The shader will not run properly if the hardware does not support the " + "level of shader compiled."); + + addField("defines", TypeRealString, Offset(mDefines, ShaderData), + "@brief String of case-sensitive defines passed to the shader compiler.\n\n" + "The string should be delimited by a semicolon, tab, or newline character." + + "@tsexample\n" + "singleton ShaderData( FlashShader )\n" + "{\n" + "DXVertexShaderFile = \"shaders/common/postFx/flashV.hlsl\";\n" + "DXPixelShaderFile = \"shaders/common/postFx/flashP.hlsl\";\n\n" + " //Define setting the color of WHITE_COLOR.\n" + "defines = \"WHITE_COLOR=float4(1.0,1.0,1.0,0.0)\";\n\n" + "pixVersion = 2.0\n" + "}\n" + "@endtsexample\n\n" + ); + + Parent::initPersistFields(); + + // Make sure we get activation signals. + LightManager::smActivateSignal.notify( &ShaderData::_onLMActivate ); +} + +bool ShaderData::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + mShaderMacros.clear(); + + // Keep track of it. + smAllShaderData.push_back( this ); + + // NOTE: We initialize the shader on request. + + return true; +} + +void ShaderData::onRemove() +{ + // Remove it from the all shaders list. + smAllShaderData.remove( this ); + + Parent::onRemove(); +} + +const Vector& ShaderData::_getMacros() +{ + // If they have already been processed then + // return the cached result. + if ( mShaderMacros.size() != 0 || mDefines.isEmpty() ) + return mShaderMacros; + + mShaderMacros.clear(); + GFXShaderMacro macro; + const U32 defineCount = StringUnit::getUnitCount( mDefines, ";\n\t" ); + for ( U32 i=0; i < defineCount; i++ ) + { + String define = StringUnit::getUnit( mDefines, i, ";\n\t" ); + + macro.name = StringUnit::getUnit( define, 0, "=" ); + macro.value = StringUnit::getUnit( define, 1, "=" ); + mShaderMacros.push_back( macro ); + } + + return mShaderMacros; +} + +GFXShader* ShaderData::getShader( const Vector ¯os ) +{ + PROFILE_SCOPE( ShaderData_GetShader ); + + // Combine the dynamic macros with our script defined macros. + Vector finalMacros; + finalMacros.merge( _getMacros() ); + finalMacros.merge( macros ); + + // Convert the final macro list to a string. + String cacheKey; + GFXShaderMacro::stringize( macros, &cacheKey ); + + // Lookup the shader for this instance. + ShaderCache::Iterator iter = mShaders.find( cacheKey ); + if ( iter != mShaders.end() ) + return iter->value; + + // Create the shader instance... if it fails then + // bail out and return nothing to the caller. + GFXShader *shader = _createShader( finalMacros ); + if ( !shader ) + return NULL; + + // Store the shader in the cache and return it. + mShaders.insertUnique( cacheKey, shader ); + return shader; +} + +GFXShader* ShaderData::_createShader( const Vector ¯os ) +{ + F32 pixver = mPixVersion; + if ( mUseDevicePixVersion ) + pixver = getMax( pixver, GFX->getPixelShaderVersion() ); + + // Enable shader error logging. + GFXShader::setLogging( true, true ); + + GFXShader *shader = GFX->createShader(); + bool success = false; + + // Initialize the right shader type. + switch( GFX->getAdapterType() ) + { + case Direct3D9_360: + case Direct3D9: + { + success = shader->init( mDXVertexShaderName, + mDXPixelShaderName, + pixver, + macros ); + break; + } + + case OpenGL: + { + success = shader->init( mOGLVertexShaderName, + mOGLPixelShaderName, + pixver, + macros ); + break; + } + + default: + // Other device types are assumed to not support shaders. + success = false; + break; + } + + // If we failed to load the shader then + // cleanup and return NULL. + if ( !success ) + SAFE_DELETE( shader ); + + return shader; +} + +void ShaderData::reloadShaders() +{ + ShaderCache::Iterator iter = mShaders.begin(); + for ( ; iter != mShaders.end(); iter++ ) + iter->value->reload(); +} + +void ShaderData::reloadAllShaders() +{ + Vector::iterator iter = smAllShaderData.begin(); + for ( ; iter != smAllShaderData.end(); iter++ ) + (*iter)->reloadShaders(); +} + +void ShaderData::_onLMActivate( const char *lm, bool activate ) +{ + // Only on activations do we do anything. + if ( !activate ) + return; + + // Since the light manager usually swaps shadergen features + // and changes system wide shader defines we need to completely + // flush and rebuild all shaders. + + reloadAllShaders(); +} + +DefineEngineMethod( ShaderData, reload, void, (),, + "@brief Rebuilds all the vertex and pixel shader instances created from this ShaderData.\n\n" + + "@tsexample\n" + "// Rebuild the shader instances from ShaderData CloudLayerShader\n" + "CloudLayerShader.reload();\n" + "@endtsexample\n\n") +{ + object->reloadShaders(); +} diff --git a/Engine/source/materials/shaderData.h b/Engine/source/materials/shaderData.h new file mode 100644 index 000000000..cc896e23f --- /dev/null +++ b/Engine/source/materials/shaderData.h @@ -0,0 +1,122 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _SHADERTDATA_H_ +#define _SHADERTDATA_H_ + +#ifndef _SIMOBJECT_H_ +#include "console/simObject.h" +#endif +#ifndef _GFXSHADER_H_ +#include "gfx/gfxShader.h" +#endif +#ifndef _TDICTIONARY_H_ +#include "core/util/tDictionary.h" +#endif + +class GFXShader; +class ShaderData; +struct GFXShaderMacro; + + +/// +class ShaderData : public SimObject +{ + typedef SimObject Parent; + +protected: + + /// + static Vector smAllShaderData; + + typedef HashTable ShaderCache; + + ShaderCache mShaders; + + bool mUseDevicePixVersion; + + F32 mPixVersion; + + FileName mDXVertexShaderName; + + FileName mDXPixelShaderName; + + FileName mOGLVertexShaderName; + + FileName mOGLPixelShaderName; + + /// A semicolon, tab, or newline delimited string of case + /// sensitive defines that are passed to the shader compiler. + /// + /// For example: + /// + /// SAMPLE_TAPS=10;USE_TEXKILL;USE_TORQUE_FOG=1 + /// + String mDefines; + + /// The shader macros built from mDefines. + /// @see _getMacros() + Vector mShaderMacros; + + /// Returns the shader macros taking care to rebuild + /// them if the content has changed. + const Vector& _getMacros(); + + /// Helper for converting an array of macros + /// into a formatted string. + void _stringizeMacros( const Vector ¯os, + String *outString ); + + /// Creates a new shader returning NULL on error. + GFXShader* _createShader( const Vector ¯os ); + + /// @see LightManager::smActivateSignal + static void _onLMActivate( const char *lm, bool activate ); + +public: + + + ShaderData(); + + /// Returns an initialized shader instance or NULL + /// if the shader failed to be created. + GFXShader* getShader( const Vector ¯os = Vector() ); + + /// Forces a reinitialization of all the instanced shaders. + void reloadShaders(); + + /// Forces a reinitialization of the instanced shaders for + /// all loaded ShaderData objects in the system. + static void reloadAllShaders(); + + /// Returns the required pixel shader version for this shader. + F32 getPixVersion() const { return mPixVersion; } + + // SimObject + virtual bool onAdd(); + virtual void onRemove(); + + // ConsoleObject + static void initPersistFields(); + DECLARE_CONOBJECT(ShaderData); +}; + +#endif // _SHADERTDATA_H_ \ No newline at end of file diff --git a/Engine/source/materials/shaderMaterialParameters.cpp b/Engine/source/materials/shaderMaterialParameters.cpp new file mode 100644 index 000000000..d929014f8 --- /dev/null +++ b/Engine/source/materials/shaderMaterialParameters.cpp @@ -0,0 +1,229 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "materials/shaderMaterialParameters.h" + +#include "console/console.h" + +// +// ShaderMaterialParameters +// +ShaderMaterialParameterHandle::ShaderMaterialParameterHandle(const String& name) +{ + VECTOR_SET_ASSOCIATION( mHandles ); + + mName = name; +} + +ShaderMaterialParameterHandle::ShaderMaterialParameterHandle(const String& name, Vector& shaders) +{ + VECTOR_SET_ASSOCIATION( mHandles ); + + mName = name; + mHandles.setSize(shaders.size()); + + for (U32 i = 0; i < shaders.size(); i++) + mHandles[i] = shaders[i]->getShaderConstHandle(name); +} + +ShaderMaterialParameterHandle::~ShaderMaterialParameterHandle() +{ +} + +S32 ShaderMaterialParameterHandle::getSamplerRegister( U32 pass ) const +{ + AssertFatal( mHandles.size() > pass, "ShaderMaterialParameterHandle::getSamplerRegister - out of bounds" ); + return mHandles[pass]->getSamplerRegister(); +} + +// +// ShaderMaterialParameters +// +ShaderMaterialParameters::ShaderMaterialParameters() +: MaterialParameters() +{ + VECTOR_SET_ASSOCIATION( mBuffers ); +} + +ShaderMaterialParameters::~ShaderMaterialParameters() +{ + releaseBuffers(); +} + +void ShaderMaterialParameters::setBuffers(Vector& constDesc, Vector& buffers) +{ + mShaderConstDesc = constDesc; + mBuffers = buffers; +} + +void ShaderMaterialParameters::releaseBuffers() +{ + for (U32 i = 0; i < mBuffers.size(); i++) + { + mBuffers[i] = NULL; + } + mBuffers.setSize(0); +} + +U32 ShaderMaterialParameters::getAlignmentValue(const GFXShaderConstType constType) +{ + if (mBuffers.size() > 0) + return mBuffers[0]->getShader()->getAlignmentValue(constType); + else + return 0; +} + +#define SHADERMATPARAM_SET(handle, f) \ + AssertFatal(handle, "Handle is NULL!" ); \ + AssertFatal(handle->isValid(), "Handle is not valid!" ); \ + AssertFatal(dynamic_cast(handle), "Invalid handle type!"); \ + ShaderMaterialParameterHandle* h = static_cast(handle); \ + AssertFatal(h->mHandles.size() == mBuffers.size(), "Handle length differs from buffer length!"); \ + for (U32 i = 0; i < h->mHandles.size(); i++) \ +{ \ + GFXShaderConstHandle* shaderHandle = h->mHandles[i]; \ + if (shaderHandle->isValid()) \ + mBuffers[i]->set(shaderHandle, f); \ +} + +void ShaderMaterialParameters::set(MaterialParameterHandle* handle, const F32 f) +{ + SHADERMATPARAM_SET(handle, f); +} + +void ShaderMaterialParameters::set(MaterialParameterHandle* handle, const Point2F& fv) +{ + SHADERMATPARAM_SET(handle, fv); +} + +void ShaderMaterialParameters::set(MaterialParameterHandle* handle, const Point3F& fv) +{ + SHADERMATPARAM_SET(handle, fv); +} + +void ShaderMaterialParameters::set(MaterialParameterHandle* handle, const Point4F& fv) +{ + SHADERMATPARAM_SET(handle, fv); +} + +void ShaderMaterialParameters::set(MaterialParameterHandle* handle, const PlaneF& fv) +{ + SHADERMATPARAM_SET(handle, fv); +} + +void ShaderMaterialParameters::set(MaterialParameterHandle* handle, const ColorF& fv) +{ + SHADERMATPARAM_SET(handle, fv); +} + +void ShaderMaterialParameters::set(MaterialParameterHandle* handle, const S32 f) +{ + SHADERMATPARAM_SET(handle, f); +} + +void ShaderMaterialParameters::set(MaterialParameterHandle* handle, const Point2I& fv) +{ + SHADERMATPARAM_SET(handle, fv); +} + +void ShaderMaterialParameters::set(MaterialParameterHandle* handle, const Point3I& fv) +{ + SHADERMATPARAM_SET(handle, fv); +} + +void ShaderMaterialParameters::set(MaterialParameterHandle* handle, const Point4I& fv) +{ + SHADERMATPARAM_SET(handle, fv); +} + +void ShaderMaterialParameters::set(MaterialParameterHandle* handle, const AlignedArray& fv) +{ + SHADERMATPARAM_SET(handle, fv); +} + +void ShaderMaterialParameters::set(MaterialParameterHandle* handle, const AlignedArray& fv) +{ + SHADERMATPARAM_SET(handle, fv); +} + +void ShaderMaterialParameters::set(MaterialParameterHandle* handle, const AlignedArray& fv) +{ + SHADERMATPARAM_SET(handle, fv); +} + +void ShaderMaterialParameters::set(MaterialParameterHandle* handle, const AlignedArray& fv) +{ + SHADERMATPARAM_SET(handle, fv); +} + +void ShaderMaterialParameters::set(MaterialParameterHandle* handle, const AlignedArray& fv) +{ + SHADERMATPARAM_SET(handle, fv); +} + +void ShaderMaterialParameters::set(MaterialParameterHandle* handle, const AlignedArray& fv) +{ + SHADERMATPARAM_SET(handle, fv); +} + +void ShaderMaterialParameters::set(MaterialParameterHandle* handle, const AlignedArray& fv) +{ + SHADERMATPARAM_SET(handle, fv); +} + +void ShaderMaterialParameters::set(MaterialParameterHandle* handle, const AlignedArray& fv) +{ + SHADERMATPARAM_SET(handle, fv); +} + +void ShaderMaterialParameters::set(MaterialParameterHandle* handle, const MatrixF& mat, const GFXShaderConstType matrixType) +{ + if ((!handle) || !handle->isValid()) + return; + AssertFatal(dynamic_cast(handle), "Invalid handle type!"); + ShaderMaterialParameterHandle* h = static_cast(handle); + AssertFatal(h->mHandles.size() == mBuffers.size(), "Handle length differs from buffer length!"); + for (U32 i = 0; i < h->mHandles.size(); i++) + { + GFXShaderConstHandle* shaderHandle = h->mHandles[i]; + if (shaderHandle && shaderHandle->isValid()) + mBuffers[i]->set(shaderHandle, mat, matrixType); + } +} + +void ShaderMaterialParameters::set(MaterialParameterHandle* handle, const MatrixF* mat, const U32 arraySize, const GFXShaderConstType matrixType) +{ + if ((!handle) || !handle->isValid()) + return; + AssertFatal(dynamic_cast(handle), "Invalid handle type!"); + ShaderMaterialParameterHandle* h = static_cast(handle); + AssertFatal(h->mHandles.size() == mBuffers.size(), "Handle length differs from buffer length!"); + for (U32 i = 0; i < h->mHandles.size(); i++) + { + GFXShaderConstHandle* shaderHandle = h->mHandles[i]; + if (shaderHandle && shaderHandle->isValid()) + mBuffers[i]->set(shaderHandle, mat, arraySize, matrixType); + } +} + +#undef SHADERMATPARAM_SET diff --git a/Engine/source/materials/shaderMaterialParameters.h b/Engine/source/materials/shaderMaterialParameters.h new file mode 100644 index 000000000..021d7729d --- /dev/null +++ b/Engine/source/materials/shaderMaterialParameters.h @@ -0,0 +1,100 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SHADERMATERIALPARAMETERS_H_ +#define _SHADERMATERIALPARAMETERS_H_ + +#ifndef _MATERIALPARAMETERS_H_ +#include "materials/materialParameters.h" +#endif +#ifndef _GFXSHADER_H_ +#include "gfx/gfxShader.h" +#endif + + +class ShaderMaterialParameterHandle : public MaterialParameterHandle +{ + friend class ShaderMaterialParameters; +public: + virtual ~ShaderMaterialParameterHandle(); + + ShaderMaterialParameterHandle(const String& name); + ShaderMaterialParameterHandle(const String& name, Vector& shaders); + + virtual const String& getName() const { return mName; } + virtual S32 getSamplerRegister( U32 pass ) const; + + // NOTE: We always return true here instead of querying the + // children... often there is only one element, so lets just + // hit the loop once on the set. + virtual bool isValid() const { return true; }; + +protected: + Vector< GFXShaderConstHandle* > mHandles; + String mName; +}; + +// This is the union of all of the shaders contained in a material +class ShaderMaterialParameters : public MaterialParameters +{ +public: + ShaderMaterialParameters(); + virtual ~ShaderMaterialParameters(); + + void setBuffers(Vector& constDesc, Vector& buffers); + GFXShaderConstBuffer* getBuffer(U32 i) { return mBuffers[i]; } + + /// + /// MaterialParameter interface + /// + + /// Returns the material parameter handle for name. + virtual void set(MaterialParameterHandle* handle, const F32 f); + virtual void set(MaterialParameterHandle* handle, const Point2F& fv); + virtual void set(MaterialParameterHandle* handle, const Point3F& fv); + virtual void set(MaterialParameterHandle* handle, const Point4F& fv); + virtual void set(MaterialParameterHandle* handle, const PlaneF& fv); + virtual void set(MaterialParameterHandle* handle, const ColorF& fv); + virtual void set(MaterialParameterHandle* handle, const S32 f); + virtual void set(MaterialParameterHandle* handle, const Point2I& fv); + virtual void set(MaterialParameterHandle* handle, const Point3I& fv); + virtual void set(MaterialParameterHandle* handle, const Point4I& fv); + virtual void set(MaterialParameterHandle* handle, const AlignedArray& fv); + virtual void set(MaterialParameterHandle* handle, const AlignedArray& fv); + virtual void set(MaterialParameterHandle* handle, const AlignedArray& fv); + virtual void set(MaterialParameterHandle* handle, const AlignedArray& fv); + virtual void set(MaterialParameterHandle* handle, const AlignedArray& fv); + virtual void set(MaterialParameterHandle* handle, const AlignedArray& fv); + virtual void set(MaterialParameterHandle* handle, const AlignedArray& fv); + virtual void set(MaterialParameterHandle* handle, const AlignedArray& fv); + virtual void set(MaterialParameterHandle* handle, const MatrixF& mat, const GFXShaderConstType matrixType = GFXSCT_Float4x4); + virtual void set(MaterialParameterHandle* handle, const MatrixF* mat, const U32 arraySize, const GFXShaderConstType matrixType = GFXSCT_Float4x4); + + virtual U32 getAlignmentValue(const GFXShaderConstType constType); + +private: + Vector mBuffers; + + void releaseBuffers(); +}; + +#endif diff --git a/Engine/source/math/mAngAxis.cpp b/Engine/source/math/mAngAxis.cpp new file mode 100644 index 000000000..d7196cdd4 --- /dev/null +++ b/Engine/source/math/mAngAxis.cpp @@ -0,0 +1,95 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "math/mAngAxis.h" +#include "math/mQuat.h" +#include "math/mMatrix.h" + +AngAxisF & AngAxisF::set( const QuatF & q ) +{ + angle = 2.0f * mAcos( q.w ); + F32 sinHalfAngle = mSqrt(q.x * q.x + q.y * q.y + q.z * q.z); + if (sinHalfAngle != 0.0f) + axis.set( q.x / sinHalfAngle, q.y / sinHalfAngle, q.z / sinHalfAngle ); + else + axis.set(1.0f,0.0f,0.0f); + return *this; +} + +AngAxisF & AngAxisF::set( const MatrixF & mat ) +{ + QuatF q( mat ); + set( q ); + return *this; +} + +MatrixF * AngAxisF::setMatrix( MatrixF * mat ) const +{ + QuatF q( *this ); + return q.setMatrix( mat ); +} + +void AngAxisF::RotateX(F32 angle, MatrixF * mat) +{ + // for now...do it the easy way + AngAxisF rotX(Point3F(1.0f,0.0f,0.0f),angle); + rotX.setMatrix(mat); +} + +void AngAxisF::RotateY(F32 angle, MatrixF * mat) +{ + // for now...do it the easy way + AngAxisF rotY(Point3F(0.0f,1.0f,0.0f),angle); + rotY.setMatrix(mat); +} + +void AngAxisF::RotateZ(F32 angle, MatrixF * mat) +{ + // for now...do it the easy way + AngAxisF rotZ(Point3F(0.0f,0.0f,1.0f),angle); + rotZ.setMatrix(mat); +} + +void AngAxisF::RotateX(F32 angle, const Point3F & from, Point3F * to) +{ + // for now...do it the easy way + MatrixF mat; + AngAxisF::RotateX(angle,&mat); + mat.mulV(from,to); +} + +void AngAxisF::RotateY(F32 angle, const Point3F & from, Point3F * to) +{ + // for now...do it the easy way + MatrixF mat; + AngAxisF::RotateY(angle,&mat); + mat.mulV(from,to); +} + +void AngAxisF::RotateZ(F32 angle, const Point3F & from, Point3F * to) +{ + // for now...do it the easy way + MatrixF mat; + AngAxisF::RotateZ(angle,&mat); + mat.mulV(from,to); +} + diff --git a/Engine/source/math/mAngAxis.h b/Engine/source/math/mAngAxis.h new file mode 100644 index 000000000..a61b8e4f6 --- /dev/null +++ b/Engine/source/math/mAngAxis.h @@ -0,0 +1,104 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MANGAXIS_H_ +#define _MANGAXIS_H_ + +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif + +class MatrixF; +class QuatF; + +//---------------------------------------------------------------------------- +// rotation about an arbitrary axis through the origin: + +class AngAxisF +{ + public: + Point3F axis; + F32 angle; + + AngAxisF(); + AngAxisF( const Point3F & _axis, F32 _angle ); + explicit AngAxisF( const MatrixF &m ); + explicit AngAxisF( const QuatF &q ); + + AngAxisF& set( const Point3F & _axis, F32 _angle ); + AngAxisF& set( const MatrixF & m ); + AngAxisF& set( const QuatF & q ); + + bool operator ==( const AngAxisF & c ) const; + bool operator !=( const AngAxisF & c ) const; + + MatrixF * setMatrix( MatrixF * mat ) const; + + static void RotateX(F32 angle, MatrixF * mat); + static void RotateY(F32 angle, MatrixF * mat); + static void RotateZ(F32 angle, MatrixF * mat); + + static void RotateX(F32 angle, const Point3F & from, Point3F * to); + static void RotateY(F32 angle, const Point3F & from, Point3F * to); + static void RotateZ(F32 angle, const Point3F & from, Point3F * to); +}; + +//---------------------------------------------------------------------------- +// AngAxisF implementation: + +inline AngAxisF::AngAxisF() +{ +} + +inline AngAxisF::AngAxisF( const Point3F & _axis, F32 _angle ) +{ + set(_axis,_angle); +} + +inline AngAxisF::AngAxisF( const MatrixF & mat ) +{ + set(mat); +} + +inline AngAxisF::AngAxisF( const QuatF & quat ) +{ + set(quat); +} + +inline AngAxisF& AngAxisF::set( const Point3F & _axis, F32 _angle ) +{ + axis = _axis; + angle = _angle; + return *this; +} + +inline bool AngAxisF::operator ==( const AngAxisF & c ) const +{ + return mFabs(angle-c.angle) < 0.0001f && (axis == c.axis); +} + +inline bool AngAxisF::operator !=( const AngAxisF & c ) const +{ + return !(*this == c); +} + +#endif // _MANGAXIS_H_ diff --git a/Engine/source/math/mBox.cpp b/Engine/source/math/mBox.cpp new file mode 100644 index 000000000..2cdd730e3 --- /dev/null +++ b/Engine/source/math/mBox.cpp @@ -0,0 +1,265 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "math/mMatrix.h" +#include "math/mSphere.h" + + +const Box3F Box3F::Invalid( F32_MAX, F32_MAX, F32_MAX, -F32_MAX, -F32_MAX, -F32_MAX ); +const Box3F Box3F::Max( -F32_MAX, -F32_MAX, -F32_MAX, F32_MAX, F32_MAX, F32_MAX ); +const Box3F Box3F::Zero( 0, 0, 0, 0, 0, 0 ); + + +//----------------------------------------------------------------------------- + +bool Box3F::isOverlapped( const SphereF& sphere ) const +{ + // Algorithm: Real-Time Rendering, Ed. 1, p323, 10.10.1 Sphere/Box Intersection + + F32 d = 0; + for( U32 i = 0; i < 3; ++ i ) + if( sphere.center[ i ] < minExtents[ i ] ) + d = d + mSquared( sphere.center[ i ] - minExtents[ i ] ); + else if( sphere.center[ i ] > maxExtents[ i ] ) + d = d + mSquared( sphere.center[ i ] - maxExtents[ i ] ); + + return !( d > mSquared( sphere.radius ) ); +} + +//----------------------------------------------------------------------------- + +bool Box3F::collideLine(const Point3F& start, const Point3F& end, F32* t, Point3F* n) const +{ + // Collide against bounding box. Need at least this for the editor. + F32 st,et; + F32 fst = 0; + F32 fet = 1; + const F32* bmin = &minExtents.x; + const F32* bmax = &maxExtents.x; + const F32* si = &start.x; + const F32* ei = &end.x; + + static const Point3F na[3] = { Point3F(1.0f, 0.0f, 0.0f), Point3F(0.0f, 1.0f, 0.0f), Point3F(0.0f, 0.0f, 1.0f) }; + Point3F finalNormal(0.0f, 0.0f, 0.0f); + + for (int i = 0; i < 3; i++) { + bool n_neg = false; + if (si[i] < ei[i]) { + if (si[i] > bmax[i] || ei[i] < bmin[i]) + return false; + F32 di = ei[i] - si[i]; + st = (si[i] < bmin[i]) ? (bmin[i] - si[i]) / di : 0.0f; + et = (ei[i] > bmax[i]) ? (bmax[i] - si[i]) / di : 1.0f; + n_neg = true; + } + else { + if (ei[i] > bmax[i] || si[i] < bmin[i]) + return false; + F32 di = ei[i] - si[i]; + st = (si[i] > bmax[i]) ? (bmax[i] - si[i]) / di : 0.0f; + et = (ei[i] < bmin[i]) ? (bmin[i] - si[i]) / di : 1.0f; + } + if (st > fst) { + fst = st; + finalNormal = na[i]; + if ( n_neg ) + finalNormal.neg(); + } + if (et < fet) + fet = et; + + if (fet < fst) + return false; + } + + *t = fst; + *n = finalNormal; + return true; +} + +//----------------------------------------------------------------------------- + +bool Box3F::collideLine(const Point3F& start, const Point3F& end) const +{ + F32 t; + Point3F normal; + return collideLine(start, end, &t, &normal); +} + +//----------------------------------------------------------------------------- + +bool Box3F::collideOrientedBox(const Point3F & bRadii, const MatrixF & toA) const +{ + Point3F p; + toA.getColumn(3,&p); + Point3F aCenter = minExtents + maxExtents; + aCenter *= 0.5f; + p -= aCenter; // this essentially puts origin of toA target space on the center of the current box + Point3F aRadii = maxExtents - minExtents; + aRadii *= 0.5f; + + F32 absXX,absXY,absXZ; + F32 absYX,absYY,absYZ; + F32 absZX,absZY,absZZ; + + const F32 * f = toA; + + absXX = mFabs(f[0]); + absYX = mFabs(f[1]); + absZX = mFabs(f[2]); + + if (aRadii.x + bRadii.x * absXX + bRadii.y * absYX + bRadii.z * absZX - mFabs(p.x)<0.0f) + return false; + + absXY = mFabs(f[4]); + absYY = mFabs(f[5]); + absZY = mFabs(f[6]); + if (aRadii.y + bRadii.x * absXY + bRadii.y * absYY + bRadii.z * absZY - mFabs(p.y)<0.0f) + return false; + + absXZ = mFabs(f[8]); + absYZ = mFabs(f[9]); + absZZ = mFabs(f[10]); + if (aRadii.z + bRadii.x * absXZ + bRadii.y * absYZ + bRadii.z * absZZ - mFabs(p.z)<0.0f) + return false; + + if (aRadii.x*absXX + aRadii.y*absXY + aRadii.z*absXZ + bRadii.x - mFabs(p.x*f[0] + p.y*f[4] + p.z*f[8])<0.0f) + return false; + + if (aRadii.x*absYX + aRadii.y*absYY + aRadii.z*absYZ + bRadii.y - mFabs(p.x*f[1] + p.y*f[5] + p.z*f[9])<0.0f) + return false; + + if (aRadii.x*absZX + aRadii.y*absZY + aRadii.z*absZZ + bRadii.z - mFabs(p.x*f[2] + p.y*f[6] + p.z*f[10])<0.0f) + return false; + + if (mFabs(p.z*f[4] - p.y*f[8]) > + aRadii.y * absXZ + aRadii.z * absXY + + bRadii.y * absZX + bRadii.z * absYX) + return false; + + if (mFabs(p.z*f[5] - p.y*f[9]) > + aRadii.y * absYZ + aRadii.z * absYY + + bRadii.x * absZX + bRadii.z * absXX) + return false; + + if (mFabs(p.z*f[6] - p.y*f[10]) > + aRadii.y * absZZ + aRadii.z * absZY + + bRadii.x * absYX + bRadii.y * absXX) + return false; + + if (mFabs(p.x*f[8] - p.z*f[0]) > + aRadii.x * absXZ + aRadii.z * absXX + + bRadii.y * absZY + bRadii.z * absYY) + return false; + + if (mFabs(p.x*f[9] - p.z*f[1]) > + aRadii.x * absYZ + aRadii.z * absYX + + bRadii.x * absZY + bRadii.z * absXY) + return false; + + if (mFabs(p.x*f[10] - p.z*f[2]) > + aRadii.x * absZZ + aRadii.z * absZX + + bRadii.x * absYY + bRadii.y * absXY) + return false; + + if (mFabs(p.y*f[0] - p.x*f[4]) > + aRadii.x * absXY + aRadii.y * absXX + + bRadii.y * absZZ + bRadii.z * absYZ) + return false; + + if (mFabs(p.y*f[1] - p.x*f[5]) > + aRadii.x * absYY + aRadii.y * absYX + + bRadii.x * absZZ + bRadii.z * absXZ) + return false; + + if (mFabs(p.y*f[2] - p.x*f[6]) > + aRadii.x * absZY + aRadii.y * absZX + + bRadii.x * absYZ + bRadii.y * absXZ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- + +Box3F Box3F::aroundPoints( const Point3F* points, U32 numPoints ) +{ + AssertFatal( points != NULL, "Box3F::aroundPoints - Receive a NULL pointer" ); + AssertFatal( numPoints >= 1, "Box3F::aroundPoints - Must have at least one point" ); + + Box3F box; + + Point3F minPoint = points[ 0 ]; + Point3F maxPoint = points[ 0 ]; + + for( U32 i = 1; i < numPoints; ++ i ) + for( U32 n = 0; n < 3; ++ n ) + { + minPoint[ n ] = getMin( minPoint[ n ], points[ i ][ n ] ); + maxPoint[ n ] = getMax( maxPoint[ n ], points[ i ][ n ] ); + } + + return Box3F( minPoint, maxPoint ); +} + +//----------------------------------------------------------------------------- + +Point3F Box3F::computeVertex( U32 corner ) const +{ + AssertFatal( corner < NUM_POINTS, "Box3F::computeVertex - Index out of range" ); + + switch( corner ) + { + case NearBottomLeft: return Point3F( minExtents.x, minExtents.y, minExtents.z ); + case NearTopLeft: return Point3F( minExtents.x, minExtents.y, maxExtents.z ); + case NearTopRight: return Point3F( maxExtents.x, minExtents.y, maxExtents.z ); + case NearBottomRight: return Point3F( maxExtents.x, minExtents.y, minExtents.z ); + + case FarBottomLeft: return Point3F( minExtents.x, maxExtents.y, minExtents.z ); + case FarTopLeft: return Point3F( minExtents.x, maxExtents.y, maxExtents.z ); + case FarTopRight: return Point3F( maxExtents.x, maxExtents.y, maxExtents.z ); + case FarBottomRight: return Point3F( maxExtents.x, maxExtents.y, minExtents.z ); + } + + // Not reached. + return Point3F( 0.f, 0.f, 0.f ); +} + +//----------------------------------------------------------------------------- + +F32 Box3F::getGreatestDiagonalLength() const +{ + F32 maxDiagonalsLen = ( computeVertex( FarTopRight ) - computeVertex( NearBottomLeft ) ).len(); + + maxDiagonalsLen = getMax( maxDiagonalsLen, ( computeVertex( FarTopLeft ) - computeVertex( NearBottomRight ) ).len() ); + maxDiagonalsLen = getMax( maxDiagonalsLen, ( computeVertex( FarBottomLeft ) - computeVertex( NearTopRight ) ).len() ); + maxDiagonalsLen = getMax( maxDiagonalsLen, ( computeVertex( FarBottomRight ) - computeVertex( NearTopLeft ) ).len() ); + + return maxDiagonalsLen; +} + +//----------------------------------------------------------------------------- + +SphereF Box3F::getBoundingSphere() const +{ + return SphereF( getCenter(), getGreatestDiagonalLength() / 2.f ); +} diff --git a/Engine/source/math/mBox.h b/Engine/source/math/mBox.h new file mode 100644 index 000000000..e0c028b60 --- /dev/null +++ b/Engine/source/math/mBox.h @@ -0,0 +1,702 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MBOX_H_ +#define _MBOX_H_ + +#ifndef _MBOXBASE_H_ +#include "math/mBoxBase.h" +#endif + +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif + +#ifndef _MPOINT2_H_ +#include "math/mPoint2.h" +#endif + + +class MatrixF; +class SphereF; + + + +/// Axis-aligned bounding box (AABB). +/// +/// A helper class for working with boxes. It runs at F32 precision. +/// +/// @see Box3D +class Box3F : public BoxBase +{ + public: + + Point3F minExtents; ///< Minimum extents of box + Point3F maxExtents; ///< Maximum extents of box + + Box3F() { } + + /// Create a box from two points. + /// + /// Normally, this function will compensate for mismatched + /// min/max values. If you know your values are valid, you + /// can set in_overrideCheck to true and skip this. + /// + /// @param in_rMin Minimum extents of box. + /// @param in_rMax Maximum extents of box. + /// @param in_overrideCheck Pass true to skip check of extents. + Box3F( const Point3F& in_rMin, const Point3F& in_rMax, const bool in_overrideCheck = false ); + + /// Create a box from six extent values. + /// + /// No checking is performed as to the validity of these + /// extents, unlike the other constructor. + Box3F( const F32 &xMin, const F32 &yMin, const F32 &zMin, + const F32 &xMax, const F32 &yMax, const F32 &zMax ); + + Box3F(F32 cubeSize); + + void set( const Point3F& in_rMin, const Point3F& in_rMax ); + + void set( const F32 &xMin, const F32 &yMin, const F32 &zMin, + const F32 &xMax, const F32 &yMax, const F32 &zMax ); + + /// Create box around origin given lengths + void set( const Point3F& in_Length ); + + /// Recenter the box + void setCenter( const Point3F& center ); + + /// Check to see if a point is contained in this box. + bool isContained( const Point3F& in_rContained ) const; + + /// Check if the Point2F is within the box xy extents. + bool isContained( const Point2F &pt ) const; + + /// Check to see if another box overlaps this box. + bool isOverlapped( const Box3F& in_rOverlap ) const; + + /// Check if the given sphere overlaps with the box. + bool isOverlapped( const SphereF& sphere ) const; + + /// Check to see if another box is contained in this box. + bool isContained( const Box3F& in_rContained ) const; + + /// Returns the length of the x extent. + F32 len_x() const { return maxExtents.x - minExtents.x; } + + /// Returns the length of the y extent. + F32 len_y() const { return maxExtents.y - minExtents.y; } + + /// Returns the length of the z extent. + F32 len_z() const { return maxExtents.z - minExtents.z; } + + /// Returns the minimum box extent. + F32 len_min() const { return getMin( len_x(), getMin( len_y(), len_z() ) ); } + + /// Returns the maximum box extent. + F32 len_max() const { return getMax( len_x(), getMax( len_y(), len_z() ) ); } + + /// Returns the diagonal box length. + F32 len() const { return ( maxExtents - minExtents ).len(); } + + /// Returns the length of extent by axis index. + /// + /// @param axis The axis index of 0, 1, or 2. + /// + F32 len( S32 axis ) const { return maxExtents[axis] - minExtents[axis]; } + + /// Returns true if any of the extent axes + /// are less than or equal to zero. + bool isEmpty() const { return len_x() <= 0.0f || len_y() <= 0.0f || len_z() <= 0.0f; } + + /// Perform an intersection operation with another box + /// and store the results in this box. + void intersect( const Box3F &in_rIntersect ); + void intersect( const Point3F &in_rIntersect ); + + /// Return the overlap between this box and @a otherBox. + Box3F getOverlap( const Box3F& otherBox ) const; + + /// Return the volume of the box. + F32 getVolume() const; + + /// Get the center of this box. + /// + /// This is the average of min and max. + void getCenter( Point3F *center ) const; + Point3F getCenter() const; + + /// Returns the max minus the min extents. + Point3F getExtents() const { return maxExtents - minExtents; } + + /// Collide a line against the box. + /// + /// @param start Start of line. + /// @param end End of line. + /// @param t Value from 0.0-1.0, indicating position + /// along line of collision. + /// @param n Normal of collision. + bool collideLine( const Point3F &start, const Point3F &end, F32 *t, Point3F *n ) const; + + /// Collide a line against the box. + /// + /// Returns true on collision. + bool collideLine( const Point3F &start, const Point3F &end ) const; + + /// Collide an oriented box against the box. + /// + /// Returns true if "oriented" box collides with us. + /// Assumes incoming box is centered at origin of source space. + /// + /// @param radii The dimension of incoming box (half x,y,z length). + /// @param toUs A transform that takes incoming box into our space. + bool collideOrientedBox( const Point3F &radii, const MatrixF &toUs ) const; + + /// Check that the min extents of the box is + /// less than or equal to the max extents. + bool isValidBox() const { return (minExtents.x <= maxExtents.x) && + (minExtents.y <= maxExtents.y) && + (minExtents.z <= maxExtents.z); } + + /// Return the closest point of the box, relative to the passed point. + Point3F getClosestPoint( const Point3F &refPt ) const; + + /// Return distance of closest point on box to refPt. + F32 getDistanceToPoint( const Point3F &refPt ) const; + + /// Return the squared distance to closest point on box to refPt. + F32 getSqDistanceToPoint( const Point3F &refPt ) const; + + /// Return one of the corner vertices of the box. + Point3F computeVertex( U32 corner ) const; + + /// Get the length of the longest diagonal in the box. + F32 getGreatestDiagonalLength() const; + + /// Return the bounding sphere that contains this AABB. + SphereF getBoundingSphere() const; + + /// Extend the box to include point. + /// @see Invalid + void extend( const Point3F &p ); + + /// Scale the box by a Point3F or F32 + void scale( const Point3F &amt ); + void scale( F32 amt ); + + /// Equality operator. + bool operator ==( const Box3F &b ) const; + + /// Inequality operator. + bool operator !=( const Box3F &b ) const; + + /// Create an AABB that fits around the given point cloud, i.e. + /// find the minimum and maximum extents of the given point set. + static Box3F aroundPoints( const Point3F* points, U32 numPoints ); + + public: + + /// An inverted bounds where the minimum point is positive + /// and the maximum point is negative. Should be used with + /// extend() to construct a minimum volume box. + /// @see extend + static const Box3F Invalid; + + /// A box that covers the entire floating point range. + static const Box3F Max; + + /// A null box of zero size. + static const Box3F Zero; +}; + +inline Box3F::Box3F(const Point3F& in_rMin, const Point3F& in_rMax, const bool in_overrideCheck) + : minExtents(in_rMin), + maxExtents(in_rMax) +{ + if (in_overrideCheck == false) { + minExtents.setMin(in_rMax); + maxExtents.setMax(in_rMin); + } +} + +inline Box3F::Box3F( const F32 &xMin, const F32 &yMin, const F32 &zMin, + const F32 &xMax, const F32 &yMax, const F32 &zMax ) + : minExtents(xMin,yMin,zMin), + maxExtents(xMax,yMax,zMax) +{ +} + +inline Box3F::Box3F(F32 cubeSize) + : minExtents(-0.5f * cubeSize, -0.5f * cubeSize, -0.5f * cubeSize), + maxExtents(0.5f * cubeSize, 0.5f * cubeSize, 0.5f * cubeSize) +{ +} + +inline void Box3F::set(const Point3F& in_rMin, const Point3F& in_rMax) +{ + minExtents.set(in_rMin); + maxExtents.set(in_rMax); +} + +inline void Box3F::set( const F32 &xMin, const F32 &yMin, const F32 &zMin, + const F32 &xMax, const F32 &yMax, const F32 &zMax ) +{ + minExtents.set( xMin, yMin, zMin ); + maxExtents.set( xMax, yMax, zMax ); +} + +inline void Box3F::set(const Point3F& in_Length) +{ + minExtents.set(-in_Length.x * 0.5f, -in_Length.y * 0.5f, -in_Length.z * 0.5f); + maxExtents.set( in_Length.x * 0.5f, in_Length.y * 0.5f, in_Length.z * 0.5f); +} + +inline void Box3F::setCenter(const Point3F& center) +{ + F32 halflenx = len_x() * 0.5f; + F32 halfleny = len_y() * 0.5f; + F32 halflenz = len_z() * 0.5f; + + minExtents.set(center.x-halflenx, center.y-halfleny, center.z-halflenz); + maxExtents.set(center.x+halflenx, center.y+halfleny, center.z+halflenz); +} + +inline bool Box3F::isContained(const Point3F& in_rContained) const +{ + return (in_rContained.x >= minExtents.x && in_rContained.x < maxExtents.x) && + (in_rContained.y >= minExtents.y && in_rContained.y < maxExtents.y) && + (in_rContained.z >= minExtents.z && in_rContained.z < maxExtents.z); +} + +inline bool Box3F::isContained( const Point2F &pt ) const +{ + return ( pt.x >= minExtents.x && pt.x < maxExtents.x ) && + ( pt.y >= minExtents.y && pt.y < maxExtents.y ); +} + +inline bool Box3F::isOverlapped(const Box3F& in_rOverlap) const +{ + if (in_rOverlap.minExtents.x > maxExtents.x || + in_rOverlap.minExtents.y > maxExtents.y || + in_rOverlap.minExtents.z > maxExtents.z) + return false; + if (in_rOverlap.maxExtents.x < minExtents.x || + in_rOverlap.maxExtents.y < minExtents.y || + in_rOverlap.maxExtents.z < minExtents.z) + return false; + return true; +} + +inline bool Box3F::isContained(const Box3F& in_rContained) const +{ + return (minExtents.x <= in_rContained.minExtents.x) && + (minExtents.y <= in_rContained.minExtents.y) && + (minExtents.z <= in_rContained.minExtents.z) && + (maxExtents.x >= in_rContained.maxExtents.x) && + (maxExtents.y >= in_rContained.maxExtents.y) && + (maxExtents.z >= in_rContained.maxExtents.z); +} + +inline void Box3F::intersect(const Box3F& in_rIntersect) +{ + minExtents.setMin(in_rIntersect.minExtents); + maxExtents.setMax(in_rIntersect.maxExtents); +} + +inline void Box3F::intersect(const Point3F& in_rIntersect) +{ + minExtents.setMin(in_rIntersect); + maxExtents.setMax(in_rIntersect); +} + +inline Box3F Box3F::getOverlap( const Box3F& otherBox ) const +{ + Box3F overlap; + + for( U32 i = 0; i < 3; ++ i ) + if( minExtents[ i ] > otherBox.maxExtents[ i ] || otherBox.minExtents[ i ] > maxExtents[ i ] ) + overlap.minExtents[ i ] = 0.f; + else + overlap.minExtents[ i ] = getMax( minExtents[ i ], otherBox.minExtents[ i ] ); + + return overlap; +} + +inline F32 Box3F::getVolume() const +{ + return ( maxExtents.x - minExtents.x ) * ( maxExtents.y - minExtents.y ) * ( maxExtents.z - minExtents.z ); +} + +inline void Box3F::getCenter(Point3F* center) const +{ + center->x = (minExtents.x + maxExtents.x) * 0.5f; + center->y = (minExtents.y + maxExtents.y) * 0.5f; + center->z = (minExtents.z + maxExtents.z) * 0.5f; +} + +inline Point3F Box3F::getCenter() const +{ + Point3F center; + center.x = (minExtents.x + maxExtents.x) * 0.5f; + center.y = (minExtents.y + maxExtents.y) * 0.5f; + center.z = (minExtents.z + maxExtents.z) * 0.5f; + return center; +} + +inline Point3F Box3F::getClosestPoint(const Point3F& refPt) const +{ + Point3F closest; + if (refPt.x <= minExtents.x) closest.x = minExtents.x; + else if (refPt.x > maxExtents.x) closest.x = maxExtents.x; + else closest.x = refPt.x; + + if (refPt.y <= minExtents.y) closest.y = minExtents.y; + else if (refPt.y > maxExtents.y) closest.y = maxExtents.y; + else closest.y = refPt.y; + + if (refPt.z <= minExtents.z) closest.z = minExtents.z; + else if (refPt.z > maxExtents.z) closest.z = maxExtents.z; + else closest.z = refPt.z; + + return closest; +} + +inline F32 Box3F::getDistanceToPoint(const Point3F& refPt) const +{ + return mSqrt( getSqDistanceToPoint( refPt ) ); +} + +inline F32 Box3F::getSqDistanceToPoint( const Point3F &refPt ) const +{ + F32 sqDist = 0.0f; + + for ( U32 i=0; i < 3; i++ ) + { + const F32 v = refPt[i]; + if ( v < minExtents[i] ) + sqDist += mSquared( minExtents[i] - v ); + else if ( v > maxExtents[i] ) + sqDist += mSquared( v - maxExtents[i] ); + } + + return sqDist; +} + +inline void Box3F::extend(const Point3F & p) +{ +#define EXTEND_AXIS(AXIS) \ +if (p.AXIS < minExtents.AXIS) \ + minExtents.AXIS = p.AXIS; \ +else if (p.AXIS > maxExtents.AXIS) \ + maxExtents.AXIS = p.AXIS; + + EXTEND_AXIS(x) + EXTEND_AXIS(y) + EXTEND_AXIS(z) + +#undef EXTEND_AXIS +} + +inline void Box3F::scale( const Point3F &amt ) +{ + minExtents *= amt; + maxExtents *= amt; +} + +inline void Box3F::scale( F32 amt ) +{ + minExtents *= amt; + maxExtents *= amt; +} + +inline bool Box3F::operator ==( const Box3F &b ) const +{ + return minExtents.equal( b.minExtents ) && maxExtents.equal( b.maxExtents ); +} + +inline bool Box3F::operator !=( const Box3F &b ) const +{ + return !minExtents.equal( b.minExtents ) || !maxExtents.equal( b.maxExtents ); +} + +//------------------------------------------------------------------------------ +/// Clone of Box3F, using 3D types. +/// +/// 3D types use F64. +/// +/// @see Box3F +class Box3D +{ + public: + Point3D minExtents; + Point3D maxExtents; + + public: + Box3D() { } + Box3D(const Point3D& in_rMin, const Point3D& in_rMax, const bool in_overrideCheck = false); + + bool isContained(const Point3D& in_rContained) const; + bool isOverlapped(const Box3D& in_rOverlap) const; + + F64 len_x() const; + F64 len_y() const; + F64 len_z() const; + + void intersect(const Box3D& in_rIntersect); + void getCenter(Point3D* center) const; + + void extend(const Point3D & p); +}; + +inline Box3D::Box3D(const Point3D& in_rMin, const Point3D& in_rMax, const bool in_overrideCheck) + : minExtents(in_rMin), + maxExtents(in_rMax) +{ + if (in_overrideCheck == false) { + minExtents.setMin(in_rMax); + maxExtents.setMax(in_rMin); + } +} + +inline bool Box3D::isContained(const Point3D& in_rContained) const +{ + return (in_rContained.x >= minExtents.x && in_rContained.x < maxExtents.x) && + (in_rContained.y >= minExtents.y && in_rContained.y < maxExtents.y) && + (in_rContained.z >= minExtents.z && in_rContained.z < maxExtents.z); +} + +inline bool Box3D::isOverlapped(const Box3D& in_rOverlap) const +{ + if (in_rOverlap.minExtents.x > maxExtents.x || + in_rOverlap.minExtents.y > maxExtents.y || + in_rOverlap.minExtents.z > maxExtents.z) + return false; + if (in_rOverlap.maxExtents.x < minExtents.x || + in_rOverlap.maxExtents.y < minExtents.y || + in_rOverlap.maxExtents.z < minExtents.z) + return false; + return true; +} + +inline F64 Box3D::len_x() const +{ + return maxExtents.x - minExtents.x; +} + +inline F64 Box3D::len_y() const +{ + return maxExtents.y - minExtents.y; +} + +inline F64 Box3D::len_z() const +{ + return maxExtents.z - minExtents.z; +} + +inline void Box3D::intersect(const Box3D& in_rIntersect) +{ + minExtents.setMin(in_rIntersect.minExtents); + maxExtents.setMax(in_rIntersect.maxExtents); +} + +inline void Box3D::getCenter(Point3D* center) const +{ + center->x = (minExtents.x + maxExtents.x) * 0.5; + center->y = (minExtents.y + maxExtents.y) * 0.5; + center->z = (minExtents.z + maxExtents.z) * 0.5; +} + +inline void Box3D::extend(const Point3D & p) +{ +#define EXTEND_AXIS(AXIS) \ +if (p.AXIS < minExtents.AXIS) \ + minExtents.AXIS = p.AXIS; \ +else if (p.AXIS > maxExtents.AXIS) \ + maxExtents.AXIS = p.AXIS; + + EXTEND_AXIS(x) + EXTEND_AXIS(y) + EXTEND_AXIS(z) + +#undef EXTEND_AXIS +} + + +/// Bounding Box +/// +/// A helper class for working with boxes. It runs at F32 precision. +/// +/// @see Box3D +class Box3I +{ +public: + Point3I minExtents; ///< Minimum extents of box + Point3I maxExtents; ///< Maximum extents of box + +public: + Box3I() { } + + /// Create a box from two points. + /// + /// Normally, this function will compensate for mismatched + /// min/max values. If you know your values are valid, you + /// can set in_overrideCheck to true and skip this. + /// + /// @param in_rMin Minimum extents of box. + /// @param in_rMax Maximum extents of box. + /// @param in_overrideCheck Pass true to skip check of extents. + Box3I(const Point3I& in_rMin, const Point3I& in_rMax, const bool in_overrideCheck = false); + + /// Create a box from six extent values. + /// + /// No checking is performed as to the validity of these + /// extents, unlike the other constructor. + Box3I(S32 xmin, S32 ymin, S32 zmin, S32 max, S32 ymax, S32 zmax); + + /// Check to see if a point is contained in this box. + bool isContained(const Point3I& in_rContained) const; + + /// Check to see if another box overlaps this box. + bool isOverlapped(const Box3I& in_rOverlap) const; + + /// Check to see if another box is contained in this box. + bool isContained(const Box3I& in_rContained) const; + + S32 len_x() const; + S32 len_y() const; + S32 len_z() const; + + /// Perform an intersection operation with another box + /// and store the results in this box. + void intersect(const Box3I& in_rIntersect); + + /// Get the center of this box. + /// + /// This is the average of min and max. + void getCenter(Point3I* center) const; + + /// Check that the box is valid. + /// + /// Currently, this just means that min < max. + bool isValidBox() const + { + return (minExtents.x <= maxExtents.x) && + (minExtents.y <= maxExtents.y) && + (minExtents.z <= maxExtents.z); + } + + void extend(const Point3I & p); +}; + +inline Box3I::Box3I(const Point3I& in_rMin, const Point3I& in_rMax, const bool in_overrideCheck) +: minExtents(in_rMin), +maxExtents(in_rMax) +{ + if (in_overrideCheck == false) + { + minExtents.setMin(in_rMax); + maxExtents.setMax(in_rMin); + } +} + +inline Box3I::Box3I(S32 xMin, S32 yMin, S32 zMin, S32 xMax, S32 yMax, S32 zMax) +: minExtents(xMin,yMin,zMin), +maxExtents(xMax,yMax,zMax) +{ +} + +inline bool Box3I::isContained(const Point3I& in_rContained) const +{ + return (in_rContained.x >= minExtents.x && in_rContained.x < maxExtents.x) && + (in_rContained.y >= minExtents.y && in_rContained.y < maxExtents.y) && + (in_rContained.z >= minExtents.z && in_rContained.z < maxExtents.z); +} + +inline bool Box3I::isOverlapped(const Box3I& in_rOverlap) const +{ + if (in_rOverlap.minExtents.x > maxExtents.x || + in_rOverlap.minExtents.y > maxExtents.y || + in_rOverlap.minExtents.z > maxExtents.z) + return false; + if (in_rOverlap.maxExtents.x < minExtents.x || + in_rOverlap.maxExtents.y < minExtents.y || + in_rOverlap.maxExtents.z < minExtents.z) + return false; + return true; +} + +inline bool Box3I::isContained(const Box3I& in_rContained) const +{ + return (minExtents.x <= in_rContained.minExtents.x) && + (minExtents.y <= in_rContained.minExtents.y) && + (minExtents.z <= in_rContained.minExtents.z) && + (maxExtents.x >= in_rContained.maxExtents.x) && + (maxExtents.y >= in_rContained.maxExtents.y) && + (maxExtents.z >= in_rContained.maxExtents.z); +} + +inline S32 Box3I::len_x() const +{ + return maxExtents.x - minExtents.x; +} + +inline S32 Box3I::len_y() const +{ + return maxExtents.y - minExtents.y; +} + +inline S32 Box3I::len_z() const +{ + return maxExtents.z - minExtents.z; +} + +inline void Box3I::intersect(const Box3I& in_rIntersect) +{ + minExtents.setMin(in_rIntersect.minExtents); + maxExtents.setMax(in_rIntersect.maxExtents); +} + +inline void Box3I::getCenter(Point3I* center) const +{ + center->x = (minExtents.x + maxExtents.x) >> 1; + center->y = (minExtents.y + maxExtents.y) >> 1; + center->z = (minExtents.z + maxExtents.z) >> 1; +} + +inline void Box3I::extend(const Point3I & p) +{ +#define EXTEND_AXIS(AXIS) \ +if (p.AXIS < minExtents.AXIS) \ + minExtents.AXIS = p.AXIS; \ +else if (p.AXIS > maxExtents.AXIS) \ + maxExtents.AXIS = p.AXIS; + + EXTEND_AXIS(x) + EXTEND_AXIS(y) + EXTEND_AXIS(z) + +#undef EXTEND_AXIS +} + + +#endif // _DBOX_H_ diff --git a/Engine/source/math/mBoxBase.h b/Engine/source/math/mBoxBase.h new file mode 100644 index 000000000..ef7e9a9e2 --- /dev/null +++ b/Engine/source/math/mBoxBase.h @@ -0,0 +1,258 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MBOXBASE_H_ +#define _MBOXBASE_H_ + +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif + + +/// Base class for box geometries. +class BoxBase +{ + public: + + /// Indices of the corner points. + /// + /// @note The order defined here is expected by several places + /// in the code! + enum Points + { + NearBottomRight, + NearTopRight, + NearTopLeft, + NearBottomLeft, + + FarBottomRight, + FarTopRight, + FarTopLeft, + FarBottomLeft, + + NUM_POINTS + }; + + /// Return the point index for the opposite corner of @a p. + static Points getOppositePoint( Points p ) + { + switch( p ) + { + case NearBottomRight: return FarTopLeft; + case NearTopRight: return FarBottomLeft; + case NearTopLeft: return FarBottomRight; + case NearBottomLeft: return FarTopRight; + + case FarBottomRight: return NearTopLeft; + case FarTopRight: return NearBottomLeft; + case FarTopLeft: return NearBottomLeft; + default: + case FarBottomLeft: return NearTopRight; + } + } + + /// Return the point index for the corner point that corresponds + /// to the octant that @a p points to. + static Points getPointIndexFromOctant( const Point3F& p ) + { + if( p.x > 0.f ) // Right + { + if( p.y > 0.f ) // Far + { + if( p.z > 0.f ) // Top + return FarTopRight; + else // Bottom + return FarBottomRight; + } + else // Near + { + if( p.z > 0.f ) // Top + return NearTopRight; + else // Bottom + return NearBottomRight; + } + } + else // Left + { + if( p.y > 0.f ) // Far + { + if( p.z > 0.f ) // Top + return FarTopLeft; + else // Bottom + return FarBottomLeft; + } + else // Near + { + if( p.z > 0.f ) // Top + return NearTopLeft; + else // Bottom + return NearBottomLeft; + } + } + } + + /// Indices for the side planes of the box. Each pair of planes + /// has successive indices. Also, the planes are ordered by X (left&right), + /// Y (near&far), and Z (top&bottom). + enum Planes + { + LeftPlane, + RightPlane, + NearPlane, + FarPlane, + TopPlane, + BottomPlane, + + NUM_PLANES + }; + + enum PlaneMasks + { + PlaneMaskLeft = ( 1 << LeftPlane ), + PlaneMaskRight = ( 1 << RightPlane ), + PlaneMaskTop = ( 1 << TopPlane ), + PlaneMaskBottom = ( 1 << BottomPlane ), + PlaneMaskNear = ( 1 << NearPlane ), + PlaneMaskFar = ( 1 << FarPlane ), + + PlaneMaskAll = 0xFFFFFFFF, + }; + + /// + static Points getPlanePointIndex( Planes plane, U32 i ) + { + switch( plane ) + { + case LeftPlane: + switch( i ) + { + case 0: return NearBottomLeft; + case 1: return NearTopLeft; + case 2: return FarTopLeft; + case 3: return FarBottomLeft; + default: AssertFatal( false, "BoxBase::getPlanePointIndex - Invalid index" ); + } + break; + case RightPlane: + switch( i ) + { + case 0: return NearBottomRight; + case 1: return FarBottomRight; + case 2: return FarTopRight; + case 3: return NearTopRight; + default: AssertFatal( false, "BoxBase::getPlanePointIndex - Invalid index" ); + } + break; + case NearPlane: + switch( i ) + { + case 0: return NearBottomLeft; + case 1: return NearBottomRight; + case 2: return NearTopRight; + case 3: return NearTopLeft; + default: AssertFatal( false, "BoxBase::getPlanePointIndex - Invalid index" ); + } + break; + case FarPlane: + switch( i ) + { + case 0: return FarBottomLeft; + case 1: return FarTopLeft; + case 2: return FarTopRight; + case 3: return FarBottomRight; + default: AssertFatal( false, "BoxBase::getPlanePointIndex - Invalid index" ); + } + break; + case TopPlane: + switch( i ) + { + case 0: return NearTopLeft; + case 1: return NearTopRight; + case 2: return FarTopRight; + case 3: return FarTopLeft; + default: AssertFatal( false, "BoxBase::getPlanePointIndex - Invalid index" ); + } + break; + default: + AssertFatal( false, "BoxBase::getPlanePointIndex - Invalid plane" ); + case BottomPlane: + switch( i ) + { + case 0: return NearBottomLeft; + case 1: return FarBottomLeft; + case 2: return FarBottomRight; + case 3: return NearBottomRight; + default: AssertFatal( false, "BoxBase::getPlanePointIndex - Invalid index" ); + } + break; + } + } + + /// Indices for the edges of the box. + enum Edges + { + NearLeftEdge, + NearBottomEdge, + NearRightEdge, + NearTopEdge, + + FarLeftEdge, + FarTopEdge, + FarRightEdge, + FarBottomEdge, + + LeftTopEdge, + LeftBottomEdge, + + RightTopEdge, + RightBottomEdge, + + NUM_EDGES, + InvalidEdge + }; + + /// Get the start and end point of the given edge. + static void getEdgePointIndices( Edges edge, Points& outP1, Points& outP2 ) + { + switch( edge ) + { + case NearLeftEdge: outP1 = NearTopLeft; outP2 = NearBottomLeft; return; + case NearBottomEdge: outP1 = NearBottomLeft; outP2 = NearBottomRight; return; + case NearRightEdge: outP1 = NearBottomRight; outP2 = NearTopRight; return; + case NearTopEdge: outP1 = NearTopRight; outP2 = NearTopLeft; return; + + case FarLeftEdge: outP1 = FarBottomLeft; outP2 = FarTopLeft; return; + case FarTopEdge: outP1 = FarTopLeft; outP2 = FarTopRight; return; + case FarRightEdge: outP1 = FarTopRight; outP2 = FarBottomRight; return; + case FarBottomEdge: outP1 = FarBottomRight; outP2 = FarBottomLeft; return; + + case LeftTopEdge: outP1 = NearTopLeft; outP2 = FarTopLeft; return; + case LeftBottomEdge: outP1 = FarBottomLeft; outP2 = NearBottomLeft; return; + + default: + case RightTopEdge: outP1 = FarTopRight; outP2 = NearTopRight; return; + case RightBottomEdge: outP1 = NearBottomRight; outP2 = FarBottomRight; return; + } + } +}; + +#endif // !_MBOXBASE_H_ diff --git a/Engine/source/math/mConsoleFunctions.cpp b/Engine/source/math/mConsoleFunctions.cpp new file mode 100644 index 000000000..0bb171fad --- /dev/null +++ b/Engine/source/math/mConsoleFunctions.cpp @@ -0,0 +1,325 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/strings/stringFunctions.h" + +#include "console/console.h" +#include "math/mMathFn.h" +#include "math/mRandom.h" + +#include "console/engineAPI.h" + + +DefineConsoleFunction( mSolveQuadratic, const char*, ( F32 a, F32 b, F32 c ),, + "Solve a quadratic equation (2nd degree polynomial) of form a*x^2 + b*x + c = 0.\n" + "@param a First Coefficient." + "@param b Second Coefficient." + "@param c Third Coefficient." + "@returns A triple, containing: (sol x0 x1). (sol) is the number of solutions" + "(being 0, 1, or 2), and (x0) and (x1) are the solutions, if any." + "@ingroup Math" ) +{ + F32 x[2]; + U32 sol = mSolveQuadratic( a, b, c, x ); + + char * retBuffer = Con::getReturnBuffer(256); + dSprintf(retBuffer, 256, "%d %g %g", sol, x[0], x[1]); + return retBuffer; +} + +DefineConsoleFunction( mSolveCubic, const char*, ( F32 a, F32 b, F32 c, F32 d ),, + "Solve a cubic equation (3rd degree polynomial) of form a*x^3 + b*x^2 + c*x + d = 0.\n" + "@param a First Coefficient." + "@param b Second Coefficient." + "@param c Third Coefficient." + "@param d Fourth Coefficient." + "@returns A 4-tuple, containing: (sol x0 x1 x2). (sol) is the number of solutions" + "(being 0, 1, 2 or 3), and (x0), (x1) and (x2) are the solutions, if any." + "@ingroup Math" ) +{ + F32 x[3]; + U32 sol = mSolveCubic( a, b, c, d, x ); + + char * retBuffer = Con::getReturnBuffer(256); + dSprintf(retBuffer, 256, "%d %g %g %g", sol, x[0], x[1], x[2]); + return retBuffer; +} + +DefineConsoleFunction( mSolveQuartic, const char*, ( F32 a, F32 b, F32 c, F32 d, F32 e ),, + "Solve a quartic equation (4th degree polynomial) of form a*x^4 + b*x^3 + c*x^2 + d*x + e = 0.\n" + "@param a First Coefficient." + "@param b Second Coefficient." + "@param c Third Coefficient." + "@param d Fourth Coefficient." + "@param e Fifth Coefficient." + "@returns A 5-tuple, containing: (sol x0 x1 x2 c3). (sol) is the number of solutions" + "(being 0, 1, 2, 3 or 4), and (x0), (x1), (x2) and (x3) are the solutions, if any." + "@ingroup Math" ) +{ + F32 x[4]; + char * retBuffer = Con::getReturnBuffer(256); + U32 sol = mSolveQuartic(a, b, c, d, e, x); + dSprintf(retBuffer, 256, "%d %g %g %g %g", sol, x[0], x[1], x[2], x[3]); + return retBuffer; +} + +DefineConsoleFunction( mFloor, S32, ( F32 v ),, + "Round v down to the nearest integer.\n" + "@param v Number to convert to integer." + "@returns Number converted to integer." + "@ingroup Math" ) +{ + return (S32)mFloor( v ); +} + +DefineConsoleFunction( mRound, S32, ( F32 v ),, + "Round v to the nearest integer.\n" + "@param v Number to convert to integer." + "@returns Number converted to integer." + "@ingroup Math" ) +{ + return (S32)mFloor( v + 0.5f ); +} + +DefineConsoleFunction( mCeil, S32, ( F32 v ),, + "Round v up to the nearest integer.\n" + "@param v Number to convert to integer." + "@returns Number converted to integer." + "@ingroup Math" ) +{ + return (S32)mCeil( v ); +} + +DefineConsoleFunction( mFloatLength, const char*, ( F32 v, U32 precision ),, + "Formats the specified number to the given number of decimal places.\n" + "@param v Number to format." + "@param precision Number of decimal places to format to (1-9)." + "@returns Number formatted to the specified number of decimal places." + "@ingroup Math" ) +{ + char fmtString[8] = "%.0f"; + if (precision > 9) + precision = 9; + fmtString[2] = '0' + precision; + + char * outBuffer = Con::getReturnBuffer(256); + dSprintf(outBuffer, 255, fmtString, v); + return outBuffer; +} + +//------------------------------------------------------------------------------ + +DefineConsoleFunction( mAbs, F32, ( F32 v ),, + "Calculate absolute value of specified value.\n" + "@param v Input Value." + "@returns Absolute value of specified value." + "@ingroup Math" ) +{ + return mFabs( v ); +} + +DefineConsoleFunction( mFMod, F32, ( F32 v, F32 d ),, + "Calculate the remainder of v/d.\n" + "@param v Input Value." + "@param d Divisor Value." + "@returns The remainder of v/d." + "@ingroup Math" ) +{ + return mFmod( v, d ); +} + +DefineConsoleFunction( mSqrt, F32, ( F32 v ),, + "Calculate the square-root of v.\n" + "@param v Input Value." + "@returns The square-root of the input value." + "@ingroup Math" ) +{ + return mSqrt (v ); +} + +DefineConsoleFunction( mPow, F32, ( F32 v, F32 p ),, + "Calculate b raised to the p-th power.\n" + "@param v Input Value." + "@param p Power to raise value by." + "@returns v raised to the p-th power." + "@ingroup Math" ) +{ + return mPow( v, p ); +} + +DefineConsoleFunction( mLog, F32, ( F32 v ),, + "Calculate the natural logarithm of v.\n" + "@param v Input Value." + "@returns The natural logarithm of the input value." + "@ingroup Math" ) +{ + return mLog( v ); +} + +DefineConsoleFunction( mSin, F32, ( F32 v ),, + "Calculate the sine of v.\n" + "@param v Input Value (in radians)." + "@returns The sine of the input value." + "@ingroup Math" ) +{ + return mSin( v ); +} + +DefineConsoleFunction( mCos, F32, ( F32 v ),, + "Calculate the cosine of v.\n" + "@param v Input Value (in radians)." + "@returns The cosine of the input value." + "@ingroup Math" ) +{ + return mCos( v ); +} + +DefineConsoleFunction( mTan, F32, ( F32 v ),, + "Calculate the tangent of v.\n" + "@param v Input Value (in radians)." + "@returns The tangent of the input value." + "@ingroup Math" ) +{ + return mTan( v ); +} + +DefineConsoleFunction( mAsin, F32, ( F32 v ),, + "Calculate the arc-sine of v.\n" + "@param v Input Value (in radians)." + "@returns The arc-sine of the input value." + "@ingroup Math" ) +{ + return mAsin( v ); +} + +DefineConsoleFunction( mAcos, F32, ( F32 v ),, + "Calculate the arc-cosine of v.\n" + "@param v Input Value (in radians)." + "@returns The arc-cosine of the input value." + "@ingroup Math" ) +{ + return mAcos( v ); +} + +DefineConsoleFunction( mAtan, F32, ( F32 rise, F32 run ),, + "Calculate the arc-tangent (slope) of a line defined by rise and run.\n" + "@param rise of line." + "@param run of line." + "@returns The arc-tangent (slope) of a line defined by rise and run." + "@ingroup Math" ) +{ + return mAtan2( rise, run ); +} + +DefineConsoleFunction( mRadToDeg, F32, ( F32 radians ),, + "Convert specified radians into degrees.\n" + "@param radians Input Value (in radians)." + "@returns The specified radians value converted to degrees." + "@ingroup Math" ) +{ + return mRadToDeg( radians ); +} + +DefineConsoleFunction( mDegToRad, F32, ( F32 degrees ),, + "Convert specified degrees into radians.\n" + "@param degrees Input Value (in degrees)." + "@returns The specified degrees value converted to radians." + "@ingroup Math" ) +{ + return mDegToRad( degrees ); +} + +DefineConsoleFunction( mClamp, F32, ( F32 v, F32 min, F32 max ),, + "Clamp the specified value between two bounds.\n" + "@param v Input value." + "@param min Minimum Bound." + "@param max Maximum Bound." + "@returns The specified value clamped to the specified bounds." + "@ingroup Math" ) +{ + return mClampF( v, min, max ); +} + +DefineConsoleFunction( mSaturate, F32, ( F32 v ),, + "Clamp the specified value between 0 and 1 (inclusive).\n" + "@param v Input value." + "@returns The specified value clamped between 0 and 1 (inclusive)." + "@ingroup Math" ) +{ + return mClampF( v, 0.0f, 1.0f ); +} + +DefineConsoleFunction( getMax, F32, ( F32 v1, F32 v2 ),, + "Calculate the greater of two specified numbers.\n" + "@param v1 Input value." + "@param v2 Input value." + "@returns The greater value of the two specified values." + "@ingroup Math" ) +{ + return getMax( v1, v2 ); +} + +DefineConsoleFunction( getMin, F32, ( F32 v1, F32 v2 ),, + "Calculate the lesser of two specified numbers.\n" + "@param v1 Input value." + "@param v2 Input value." + "@returns The lesser value of the two specified values." + "@ingroup Math" ) +{ + return getMin( v1, v2 ); +} + +DefineConsoleFunction( mLerp, F32, ( F32 v1, F32 v2, F32 time ),, + "Calculate linearly interpolated value between two specified numbers using specified normalized time.\n" + "@param v1 Interpolate From Input value." + "@param v2 Interpolate To Input value." + "@param time Normalized time used to interpolate values (0-1)." + "@returns The interpolated value between the two specified values at normalized time t." + "@ingroup Math" ) +{ + return mLerp( v1, v2, time ); +} + +DefineConsoleFunction( mPi, F32, (),, + "Return the value of PI (half-circle in radians).\n" + "@returns The value of PI." + "@ingroup Math" ) +{ + return M_PI_F; +} + +DefineConsoleFunction( m2Pi, F32, (),, + "Return the value of 2*PI (full-circle in radians).\n" + "@returns The value of 2*PI." + "@ingroup Math" ) +{ + return M_2PI_F; +} + +DefineConsoleFunction( mIsPow2, bool, ( S32 v ),, + "Returns whether the value is an exact power of two.\n" + "@param v Input value." + "@returns Whether the specified value is an exact power of two." + "@ingroup Math" ) +{ + return isPow2( v ); +} diff --git a/Engine/source/math/mConstants.h b/Engine/source/math/mConstants.h new file mode 100644 index 000000000..42b071d1e --- /dev/null +++ b/Engine/source/math/mConstants.h @@ -0,0 +1,58 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MCONSTANTS_H_ +#define _MCONSTANTS_H_ + +#undef M_PI +#undef M_SQRT2 + +#define M_PI 3.14159265358979323846 +#define M_SQRT2 1.41421356237309504880 + +#define M_2PI (3.1415926535897932384626433 * 2.0) +#define M_SQRTHALF 0.7071067811865475244008443 + +#define M_HALFPI 1.57079632679489661923 + +#define M_PI_F 3.14159265358979323846f +#define M_SQRT2_F 1.41421356237309504880f + +#define M_2PI_F (3.1415926535897932384626433f * 2.0f) +#define M_SQRTHALF_F 0.7071067811865475244008443f + +#define M_HALFPI_F 1.57079632679489661923f + +#define M_CONST_E_F 2.7182818284590452353602874f + +#define POINT_EPSILON (1e-4) ///< Epsilon for point types. + + +/// Result of an overlap test. +enum OverlapTestResult +{ + GeometryInside = 1, ///< Completely inside test volume/space. + GeometryIntersecting = 0, ///< Partly inside and partly outside test volume/space. + GeometryOutside = -1 ///< No overlap with test volume/space. +}; + +#endif diff --git a/Engine/source/math/mEase.cpp b/Engine/source/math/mEase.cpp new file mode 100644 index 000000000..0eca22fef --- /dev/null +++ b/Engine/source/math/mEase.cpp @@ -0,0 +1,171 @@ +// NOTE: methods on the EaseF convenience class + +#include "math/mMath.h" +#include "math/mEase.h" +#include "core/strings/stringFunctions.h" + +EaseF::EaseF() +{ + dir = 0; + type = 0; + param[0] = param[1] = -1.0f; +} + +EaseF::EaseF(const EaseF &ease) +{ + this->dir = ease.dir; + this->type = ease.type; + this->param[0] = ease.param[0]; + this->param[1] = ease.param[1]; +} + +EaseF::EaseF(const S32 dir, const S32 type) +{ + this->dir = dir; + this->type = type; + this->param[0] = this->param[1] = -1.0f; +} + +EaseF::EaseF(const S32 dir, const S32 type, F32 param[2]) +{ + this->dir = dir; + this->type = type; + this->param[0] = param[0]; + this->param[1] = param[1]; +} + +void EaseF::set(const S32 dir, const S32 type) +{ + this->dir = dir; + this->type = type; + this->param[0] = this->param[1] = -1.0f; +} + +void EaseF::set(const S32 dir, const S32 type, F32 param[2]) +{ + this->dir = dir; + this->type = type; + this->param[0] = param[0]; + this->param[1] = param[1]; +} + +void EaseF::set(const S32 dir, const S32 type, F32 param0, F32 param1) +{ + this->dir = dir; + this->type = type; + this->param[0] = param0; + this->param[1] = param1; +} + +void EaseF::set(const char *s) +{ + dSscanf(s,"%d %d %f %f",&dir,&type,¶m[0],¶m[1]); +} + +F32 EaseF::getValue(F32 t, F32 b, F32 c, F32 d) const +{ + F32 value = 0; + + if (type == Ease::Linear) + { + value = mLinearTween(t,b, c, d); + } + else if (type == Ease::Quadratic) + { + if (dir == Ease::In) + value = mEaseInQuad(t,b, c, d); + else if (dir == Ease::Out) + value = mEaseOutQuad(t,b, c, d); + else if (dir == Ease::InOut) + value = mEaseInOutQuad(t,b, c, d); + } + else if (type == Ease::Cubic) + { + if (dir == Ease::In) + value = mEaseInCubic(t,b, c, d); + else if (dir == Ease::Out) + value = mEaseOutCubic(t,b, c, d); + else if (dir == Ease::InOut) + value = mEaseInOutCubic(t,b, c, d); + } + else if (type == Ease::Quartic) + { + if (dir == Ease::In) + value = mEaseInQuart(t,b, c, d); + else if (dir == Ease::Out) + value = mEaseOutQuart(t,b, c, d); + else if (dir == Ease::InOut) + value = mEaseInOutQuart(t,b, c, d); + } + else if (type == Ease::Quintic) + { + if (dir == Ease::In) + value = mEaseInQuint(t,b, c, d); + else if (dir == Ease::Out) + value = mEaseOutQuint(t,b, c, d); + else if (dir == Ease::InOut) + value = mEaseInOutQuint(t,b, c, d); + } + else if (type == Ease::Sinusoidal) + { + if (dir == Ease::In) + value = mEaseInSine(t,b, c, d); + else if (dir == Ease::Out) + value = mEaseOutSine(t,b, c, d); + else if (dir == Ease::InOut) + value = mEaseInOutSine(t,b, c, d); + } + else if (type == Ease::Exponential) + { + if (dir == Ease::In) + value = mEaseInExpo(t,b, c, d); + else if (dir == Ease::Out) + value = mEaseOutExpo(t,b, c, d); + else if (dir == Ease::InOut) + value = mEaseInOutExpo(t,b, c, d); + } + else if (type == Ease::Circular) + { + if (dir == Ease::In) + value = mEaseInCirc(t,b, c, d); + else if (dir == Ease::Out) + value = mEaseOutCirc(t,b, c, d); + else if (dir == Ease::InOut) + value = mEaseInOutCirc(t,b, c, d); + } + else if (type == Ease::Elastic) + { + if (dir == Ease::In) + value = mEaseInElastic(t,b, c, d, param[0], param[1]); + else if (dir == Ease::Out) + value = mEaseOutElastic(t,b, c, d, param[0], param[1]); + else if (dir == Ease::InOut) + value = mEaseInOutElastic(t,b, c, d, param[0], param[1]); + } + else if (type == Ease::Back) + { + if (dir == Ease::In) + value = mEaseInBack(t,b, c, d, param[0]); + else if (dir == Ease::Out) + value = mEaseOutBack(t,b, c, d, param[0]); + else if (dir == Ease::InOut) + value = mEaseInOutBack(t,b, c, d, param[0]); + } + else if (type == Ease::Bounce) + { + if (dir == Ease::In) + value = mEaseInBounce(t,b, c, d); + else if (dir == Ease::Out) + value = mEaseOutBounce(t,b, c, d); + else if (dir == Ease::InOut) + value = mEaseInOutBounce(t,b, c, d); + } + else + { + // what ? + } + + return value; +} + +// < pg diff --git a/Engine/source/math/mEase.h b/Engine/source/math/mEase.h new file mode 100644 index 000000000..140a468d9 --- /dev/null +++ b/Engine/source/math/mEase.h @@ -0,0 +1,592 @@ +/* + ROBERT PENNER'S MOST EXCELLENT EASING METHODS - ported to Torque C++ by Paul Dana + + Easing Equations v1.5 + May 1, 2003 + (c) 2003 Robert Penner, all rights reserved. + This work is subject to the terms in http://www.robertpenner.com/easing_terms_of_use.html. + + These tweening functions provide different flavors of + math-based motion under a consistent API. + + Types of easing: + + Linear + Quadratic + Cubic + Quartic + Quintic + Sinusoidal + Exponential + Circular + Elastic + Back + Bounce + + Changes: + 1.5 - added bounce easing + 1.4 - added elastic and back easing + 1.3 - tweaked the exponential easing functions to make endpoints exact + 1.2 - inline optimizations (changing t and multiplying in one step)--thanks to Tatsuo Kato for the idea + + Discussed in Chapter 7 of + Robert Penner's Programming Macromedia Flash MX + (including graphs of the easing equations) + + http://www.robertpenner.com/profmx + http://www.amazon.com/exec/obidos/ASIN/0072223561/robertpennerc-20 +*/ + +#ifndef _MEASE_H_ +#define _MEASE_H_ + +// the ease methods below all are static and take atomic types as params +// so they are the most generally useful. for convenience, define here +// a type that can contain all the params needed for below to make +// data structures that use these methods cleaner... +//------------------------------------------------------------------------------ +class Ease +{ + //-------------------------------------- Public data + public: + enum enumDirection + { + InOut=0, + In, + Out + }; + + enum enumType + { + Linear=0, + Quadratic, + Cubic, + Quartic, + Quintic, + Sinusoidal, + Exponential, + Circular, + Elastic, + Back, + Bounce, + }; +}; + +class EaseF : public Ease +{ + //-------------------------------------- Public data + public: + S32 dir; // inout, in, out + S32 type; // linear, etc... + F32 param[2]; // optional params + + //-------------------------------------- Public interface + public: + EaseF(); + EaseF(const EaseF &ease); + EaseF(const S32 dir, const S32 type); + EaseF(const S32 dir, const S32 type, F32 param[2]); + + //-------------------------------------- Non-math mutators and misc functions + void set(const S32 dir, const S32 type); + void set(const S32 dir, const S32 type, F32 param[2]); + void set(const S32 dir, const S32 type, F32 param0, F32 param1); + void set(const char *s); + + F32 getValue(F32 t, F32 b, F32 c, F32 d) const; + F32 getUnitValue(F32 t, bool noExtrapolation) const + { + F32 v = getValue(t,0.0f,1.0f,1.0f); + if (noExtrapolation) + v = mClampF(v,0.0f,1.0f); + return v; + } + F32 getUnitValue(F32 t) const + { + return getValue(t,0.0f,1.0f,1.0f); + } +}; + + +// simple linear tweening - no easing +// t: current time, b: beginning value, c: change in value, d: duration +inline F32 mLinearTween(F32 t, F32 b, F32 c, F32 d) { + return c*t/d + b; +} + + + ///////////// QUADRATIC EASING: t^2 /////////////////// + +// quadratic easing in - accelerating from zero velocity +// t: current time, b: beginning value, c: change in value, d: duration +// t and d can be in frames or seconds/milliseconds +inline F32 mEaseInQuad(F32 t, F32 b, F32 c, F32 d) { + return c*(t/=d)*t + b; +}; + +// quadratic easing out - decelerating to zero velocity +inline F32 mEaseOutQuad(F32 t, F32 b, F32 c, F32 d) { + return -c *(t/=d)*(t-2) + b; +}; + +// quadratic easing in/out - acceleration until halfway, then deceleration +inline F32 mEaseInOutQuad(F32 t, F32 b, F32 c, F32 d) { + if ((t/=d/2) < 1) return c/2*t*t + b; + return -c/2 * ((--t)*(t-2) - 1) + b; +}; + + ///////////// CUBIC EASING: t^3 /////////////////////// + +// cubic easing in - accelerating from zero velocity +// t: current time, b: beginning value, c: change in value, d: duration +// t and d can be frames or seconds/milliseconds +inline F32 mEaseInCubic(F32 t, F32 b, F32 c, F32 d) { + return c*(t/=d)*t*t + b; +}; + +// cubic easing out - decelerating to zero velocity +inline F32 mEaseOutCubic(F32 t, F32 b, F32 c, F32 d) { + return c*((t=t/d-1)*t*t + 1) + b; +}; + +// cubic easing in/out - acceleration until halfway, then deceleration +inline F32 mEaseInOutCubic(F32 t, F32 b, F32 c, F32 d) { + if ((t/=d/2) < 1) return c/2*t*t*t + b; + return c/2*((t-=2)*t*t + 2) + b; +}; + + + ///////////// QUARTIC EASING: t^4 ///////////////////// + +// quartic easing in - accelerating from zero velocity +// t: current time, b: beginning value, c: change in value, d: duration +// t and d can be frames or seconds/milliseconds +inline F32 mEaseInQuart(F32 t, F32 b, F32 c, F32 d) { + return c*(t/=d)*t*t*t + b; +}; + +// quartic easing out - decelerating to zero velocity +inline F32 mEaseOutQuart(F32 t, F32 b, F32 c, F32 d) { + return -c * ((t=t/d-1)*t*t*t - 1) + b; +}; + +// quartic easing in/out - acceleration until halfway, then deceleration +inline F32 mEaseInOutQuart(F32 t, F32 b, F32 c, F32 d) { + if ((t/=d/2) < 1) return c/2*t*t*t*t + b; + return -c/2 * ((t-=2)*t*t*t - 2) + b; +}; + + + ///////////// QUINTIC EASING: t^5 //////////////////// + +// quintic easing in - accelerating from zero velocity +// t: current time, b: beginning value, c: change in value, d: duration +// t and d can be frames or seconds/milliseconds +inline F32 mEaseInQuint(F32 t, F32 b, F32 c, F32 d) { + return c*(t/=d)*t*t*t*t + b; +}; + +// quintic easing out - decelerating to zero velocity +inline F32 mEaseOutQuint(F32 t, F32 b, F32 c, F32 d) { + return c*((t=t/d-1)*t*t*t*t + 1) + b; +}; + +// quintic easing in/out - acceleration until halfway, then deceleration +inline F32 mEaseInOutQuint(F32 t, F32 b, F32 c, F32 d) { + if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b; + return c/2*((t-=2)*t*t*t*t + 2) + b; +}; + + + + ///////////// SINUSOIDAL EASING: sin(t) /////////////// + +// sinusoidal easing in - accelerating from zero velocity +// t: current time, b: beginning value, c: change in position, d: duration +inline F32 mEaseInSine(F32 t, F32 b, F32 c, F32 d) { + return -c * mCos(t/d * (M_PI_F/2)) + c + b; +}; + +// sinusoidal easing out - decelerating to zero velocity +inline F32 mEaseOutSine(F32 t, F32 b, F32 c, F32 d) { + return c * mSin(t/d * (M_PI_F/2)) + b; +}; + +// sinusoidal easing in/out - accelerating until halfway, then decelerating +inline F32 mEaseInOutSine(F32 t, F32 b, F32 c, F32 d) { + return -c/2 * (mCos(M_PI_F*t/d) - 1) + b; +}; + + + ///////////// EXPONENTIAL EASING: 2^t ///////////////// + +// exponential easing in - accelerating from zero velocity +// t: current time, b: beginning value, c: change in position, d: duration +inline F32 mEaseInExpo(F32 t, F32 b, F32 c, F32 d) { + return (t==0) ? b : c * mPow(2, 10 * (t/d - 1)) + b; +}; + +// exponential easing out - decelerating to zero velocity +inline F32 mEaseOutExpo(F32 t, F32 b, F32 c, F32 d) { + return (t==d) ? b+c : c * (-mPow(2, -10 * t/d) + 1) + b; +}; + +// exponential easing in/out - accelerating until halfway, then decelerating +inline F32 mEaseInOutExpo(F32 t, F32 b, F32 c, F32 d) { + if (t==0) return b; + if (t==d) return b+c; + if ((t/=d/2) < 1) return c/2 * mPow(2, 10 * (t - 1)) + b; + return c/2 * (-mPow(2, -10 * --t) + 2) + b; +}; + + + /////////// CIRCULAR EASING: sqrt(1-t^2) ////////////// + +// circular easing in - accelerating from zero velocity +// t: current time, b: beginning value, c: change in position, d: duration +inline F32 mEaseInCirc (F32 t, F32 b, F32 c, F32 d) { + return -c * (mSqrt(1 - (t/=d)*t) - 1) + b; +}; + +// circular easing out - decelerating to zero velocity +inline F32 mEaseOutCirc (F32 t, F32 b, F32 c, F32 d) { + return c * mSqrt(1 - (t=t/d-1)*t) + b; +}; + +// circular easing in/out - acceleration until halfway, then deceleration +inline F32 mEaseInOutCirc(F32 t, F32 b, F32 c, F32 d) { + if ((t/=d/2) < 1) return -c/2 * (mSqrt(1 - t*t) - 1) + b; + return c/2 * (mSqrt(1 - (t-=2)*t) + 1) + b; +}; + + + /////////// ELASTIC EASING: exponentially decaying sine wave ////////////// + +// t: current time, b: beginning value, c: change in value, d: duration, a: amplitude (optional), p: period (optional) +// t and d can be in frames or seconds/milliseconds + +inline F32 mEaseInElastic(F32 t, F32 b, F32 c, F32 d, F32 a, F32 p) { + if (t==0) return b; if ((t/=d)==1) return b+c; if (p<=0) p=d*.3f; + F32 s; + if (a < mFabs(c)) { a=c; s=p/4; } + else s = p/(2*M_PI_F) * mAsin (c/a); + return -(a*mPow(2,10*(t-=1)) * mSin( (t*d-s)*(2*M_PI_F)/p )) + b; +}; + +inline F32 mEaseOutElastic(F32 t, F32 b, F32 c, F32 d, F32 a, F32 p) { + if (t==0) return b; if ((t/=d)==1) return b+c; if (p<=0) p=d*.3f; + F32 s; + if (a < mFabs(c)) { a=c; s=p/4; } + else s = p/(2*M_PI_F) * mAsin (c/a); + return a*mPow(2,-10*t) * mAsin( (t*d-s)*(2*M_PI_F)/p ) + c + b; +}; + +inline F32 mEaseInOutElastic(F32 t, F32 b, F32 c, F32 d, F32 a, F32 p) { + if (t==0) return b; if ((t/=d/2)==2) return b+c; if (p<=0) p=d*(.3f*1.5f); + F32 s; + if (a < mFabs(c)) { a=c; s=p/4; } + else s = p/(2*M_PI_F) * mAsin (c/a); + if (t < 1) return -.5f*(a*mPow(2,10*(t-=1)) * mSin( (t*d-s)*(2*M_PI_F)/p )) + b; + return a*mPow(2,-10*(t-=1)) * mSin( (t*d-s)*(2*M_PI_F)/p )*.5f + c + b; +}; + + + /////////// BACK EASING: overshooting cubic easing: (s+1)*t^3 - s*t^2 ////////////// + +// back easing in - backtracking slightly, then reversing direction and moving to target +// t: current time, b: beginning value, c: change in value, d: duration, s: overshoot amount (optional) +// t and d can be in frames or seconds/milliseconds +// s controls the amount of overshoot: higher s means greater overshoot +// s has a default value of 1.70158, which produces an overshoot of 10 percent +// s==0 produces cubic easing with no overshoot +inline F32 mEaseInBack(F32 t, F32 b, F32 c, F32 d, F32 s) { + if (s < 0) s = 1.70158f; + return c*(t/=d)*t*((s+1)*t - s) + b; +}; + +// back easing out - moving towards target, overshooting it slightly, then reversing and coming back to target +inline F32 mEaseOutBack(F32 t, F32 b, F32 c, F32 d, F32 s) { + if (s < 0) s = 1.70158f; + return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; +}; + +// back easing in/out - backtracking slightly, then reversing direction and moving to target, +// then overshooting target, reversing, and finally coming back to target +inline F32 mEaseInOutBack(F32 t, F32 b, F32 c, F32 d, F32 s) { + if (s < 0) s = 1.70158f; + if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525f))+1)*t - s)) + b; + return c/2*((t-=2)*t*(((s*=(1.525f))+1)*t + s) + 2) + b; +}; + + + /////////// BOUNCE EASING: exponentially decaying parabolic bounce ////////////// + +// bounce easing out +inline F32 mEaseOutBounce(F32 t, F32 b, F32 c, F32 d) { + if ((t/=d) < (1/2.75f)) { + return c*(7.5625f*t*t) + b; + } else if (t < (2/2.75)) { + return c*(7.5625f*(t-=(1.5f/2.75f))*t + .75f) + b; + } else if (t < (2.5/2.75)) { + return c*(7.5625f*(t-=(2.25f/2.75f))*t + .9375f) + b; + } else { + return c*(7.5625f*(t-=(2.625f/2.75f))*t + .984375f) + b; + } +}; + +// bounce easing in +// t: current time, b: beginning value, c: change in position, d: duration +inline F32 mEaseInBounce(F32 t, F32 b, F32 c, F32 d) { + return c - mEaseOutBounce (d-t, 0, c, d) + b; +}; + +// bounce easing in/out +inline F32 mEaseInOutBounce(F32 t, F32 b, F32 c, F32 d) { + if (t < d/2) return mEaseInBounce (t*2, 0, c, d) * .5f + b; + return mEaseOutBounce (t*2-d, 0, c, d) * .5f + c*.5f + b; +}; + + +#if 0 +// ORIGINAL ACTION SCRIPT CODE: + +// simple linear tweening - no easing +// t: current time, b: beginning value, c: change in value, d: duration +Math.linearTween = function (t, b, c, d) { + return c*t/d + b; +}; + + + ///////////// QUADRATIC EASING: t^2 /////////////////// + +// quadratic easing in - accelerating from zero velocity +// t: current time, b: beginning value, c: change in value, d: duration +// t and d can be in frames or seconds/milliseconds +Math.easeInQuad = function (t, b, c, d) { + return c*(t/=d)*t + b; +}; + +// quadratic easing out - decelerating to zero velocity +Math.easeOutQuad = function (t, b, c, d) { + return -c *(t/=d)*(t-2) + b; +}; + +// quadratic easing in/out - acceleration until halfway, then deceleration +Math.easeInOutQuad = function (t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t + b; + return -c/2 * ((--t)*(t-2) - 1) + b; +}; + + + ///////////// CUBIC EASING: t^3 /////////////////////// + +// cubic easing in - accelerating from zero velocity +// t: current time, b: beginning value, c: change in value, d: duration +// t and d can be frames or seconds/milliseconds +Math.easeInCubic = function (t, b, c, d) { + return c*(t/=d)*t*t + b; +}; + +// cubic easing out - decelerating to zero velocity +Math.easeOutCubic = function (t, b, c, d) { + return c*((t=t/d-1)*t*t + 1) + b; +}; + +// cubic easing in/out - acceleration until halfway, then deceleration +Math.easeInOutCubic = function (t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t*t + b; + return c/2*((t-=2)*t*t + 2) + b; +}; + + + ///////////// QUARTIC EASING: t^4 ///////////////////// + +// quartic easing in - accelerating from zero velocity +// t: current time, b: beginning value, c: change in value, d: duration +// t and d can be frames or seconds/milliseconds +Math.easeInQuart = function (t, b, c, d) { + return c*(t/=d)*t*t*t + b; +}; + +// quartic easing out - decelerating to zero velocity +Math.easeOutQuart = function (t, b, c, d) { + return -c * ((t=t/d-1)*t*t*t - 1) + b; +}; + +// quartic easing in/out - acceleration until halfway, then deceleration +Math.easeInOutQuart = function (t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t*t*t + b; + return -c/2 * ((t-=2)*t*t*t - 2) + b; +}; + + + ///////////// QUINTIC EASING: t^5 //////////////////// + +// quintic easing in - accelerating from zero velocity +// t: current time, b: beginning value, c: change in value, d: duration +// t and d can be frames or seconds/milliseconds +Math.easeInQuint = function (t, b, c, d) { + return c*(t/=d)*t*t*t*t + b; +}; + +// quintic easing out - decelerating to zero velocity +Math.easeOutQuint = function (t, b, c, d) { + return c*((t=t/d-1)*t*t*t*t + 1) + b; +}; + +// quintic easing in/out - acceleration until halfway, then deceleration +Math.easeInOutQuint = function (t, b, c, d) { + if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b; + return c/2*((t-=2)*t*t*t*t + 2) + b; +}; + + + + ///////////// SINUSOIDAL EASING: sin(t) /////////////// + +// sinusoidal easing in - accelerating from zero velocity +// t: current time, b: beginning value, c: change in position, d: duration +Math.easeInSine = function (t, b, c, d) { + return -c * Math.cos(t/d * (Math.PI/2)) + c + b; +}; + +// sinusoidal easing out - decelerating to zero velocity +Math.easeOutSine = function (t, b, c, d) { + return c * Math.sin(t/d * (Math.PI/2)) + b; +}; + +// sinusoidal easing in/out - accelerating until halfway, then decelerating +Math.easeInOutSine = function (t, b, c, d) { + return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b; +}; + + + ///////////// EXPONENTIAL EASING: 2^t ///////////////// + +// exponential easing in - accelerating from zero velocity +// t: current time, b: beginning value, c: change in position, d: duration +Math.easeInExpo = function (t, b, c, d) { + return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b; +}; + +// exponential easing out - decelerating to zero velocity +Math.easeOutExpo = function (t, b, c, d) { + return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; +}; + +// exponential easing in/out - accelerating until halfway, then decelerating +Math.easeInOutExpo = function (t, b, c, d) { + if (t==0) return b; + if (t==d) return b+c; + if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b; + return c/2 * (-Math.pow(2, -10 * --t) + 2) + b; +}; + + + /////////// CIRCULAR EASING: sqrt(1-t^2) ////////////// + +// circular easing in - accelerating from zero velocity +// t: current time, b: beginning value, c: change in position, d: duration +Math.easeInCirc = function (t, b, c, d) { + return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b; +}; + +// circular easing out - decelerating to zero velocity +Math.easeOutCirc = function (t, b, c, d) { + return c * Math.sqrt(1 - (t=t/d-1)*t) + b; +}; + +// circular easing in/out - acceleration until halfway, then deceleration +Math.easeInOutCirc = function (t, b, c, d) { + if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b; + return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b; +}; + + + /////////// ELASTIC EASING: exponentially decaying sine wave ////////////// + +// t: current time, b: beginning value, c: change in value, d: duration, a: amplitude (optional), p: period (optional) +// t and d can be in frames or seconds/milliseconds + +Math.easeInElastic = function (t, b, c, d, a, p) { + if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; + if (a < Math.abs(c)) { a=c; var s=p/4; } + else var s = p/(2*Math.PI) * Math.asin (c/a); + return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; +}; + +Math.easeOutElastic = function (t, b, c, d, a, p) { + if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; + if (a < Math.abs(c)) { a=c; var s=p/4; } + else var s = p/(2*Math.PI) * Math.asin (c/a); + return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b; +}; + +Math.easeInOutElastic = function (t, b, c, d, a, p) { + if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5); + if (a < Math.abs(c)) { a=c; var s=p/4; } + else var s = p/(2*Math.PI) * Math.asin (c/a); + if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; + return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b; +}; + + + /////////// BACK EASING: overshooting cubic easing: (s+1)*t^3 - s*t^2 ////////////// + +// back easing in - backtracking slightly, then reversing direction and moving to target +// t: current time, b: beginning value, c: change in value, d: duration, s: overshoot amount (optional) +// t and d can be in frames or seconds/milliseconds +// s controls the amount of overshoot: higher s means greater overshoot +// s has a default value of 1.70158, which produces an overshoot of 10 percent +// s==0 produces cubic easing with no overshoot +Math.easeInBack = function (t, b, c, d, s) { + if (s == undefined) s = 1.70158; + return c*(t/=d)*t*((s+1)*t - s) + b; +}; + +// back easing out - moving towards target, overshooting it slightly, then reversing and coming back to target +Math.easeOutBack = function (t, b, c, d, s) { + if (s == undefined) s = 1.70158; + return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; +}; + +// back easing in/out - backtracking slightly, then reversing direction and moving to target, +// then overshooting target, reversing, and finally coming back to target +Math.easeInOutBack = function (t, b, c, d, s) { + if (s == undefined) s = 1.70158; + if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b; + return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b; +}; + + + /////////// BOUNCE EASING: exponentially decaying parabolic bounce ////////////// + +// bounce easing in +// t: current time, b: beginning value, c: change in position, d: duration +Math.easeInBounce = function (t, b, c, d) { + return c - Math.easeOutBounce (d-t, 0, c, d) + b; +}; + +// bounce easing out +Math.easeOutBounce = function (t, b, c, d) { + if ((t/=d) < (1/2.75)) { + return c*(7.5625*t*t) + b; + } else if (t < (2/2.75)) { + return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b; + } else if (t < (2.5/2.75)) { + return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b; + } else { + return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b; + } +}; + +// bounce easing in/out +Math.easeInOutBounce = function (t, b, c, d) { + if (t < d/2) return Math.easeInBounce (t*2, 0, c, d) * .5 + b; + return Math.easeOutBounce (t*2-d, 0, c, d) * .5 + c*.5 + b; +}; +#endif + + + +#endif // _MEASE_H_ diff --git a/Engine/source/math/mIntersector.h b/Engine/source/math/mIntersector.h new file mode 100644 index 000000000..e667e1715 --- /dev/null +++ b/Engine/source/math/mIntersector.h @@ -0,0 +1,385 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MINTERSECTOR_H_ +#define _MINTERSECTOR_H_ + +#ifndef _MCONSTANTS_H_ +#include "math/mConstants.h" +#endif + +#ifndef _MBOX_H_ +#include "math/mBox.h" +#endif + +#ifndef _MORIENTEDBOX_H_ +#include "math/mOrientedBox.h" +#endif + +#ifndef _MSPHERE_H_ +#include "math/mSphere.h" +#endif + +#ifndef _MPLANE_H_ +#include "math/mPlane.h" +#endif + +#ifndef _MPOLYHEDRON_H_ +#include "math/mPolyhedron.h" +#endif + +#ifndef _MPLANETRANSFORMER_H_ +#include "math/mPlaneTransformer.h" +#endif + +#ifndef _PROFILER_H_ +#include "platform/profiler.h" +#endif + + +/// @file +/// Precise and fast geometric intersection testing. + + +/// Base class for intersector implementations. +template< typename Tester, typename Testee > +struct IntersectorBase +{ + typedef Tester TesterType; + typedef Testee TesteeType; + + protected: + + TesterType mTester; + + public: + + IntersectorBase() {} + IntersectorBase( const TesterType& tester ) + : mTester( tester ) {} +}; + + +/// Convex polyhedron / AABB intersection. +/// +/// This class implements the algorithm described in "Detecting Intersection of a Rectangular +/// Solid and a Convex Polyhedron", Graphics Gems IV, Chapter 1.7, by Ned Greene. +/// +/// The polyhedron is preprocessed when an object of this class is constructed. +/// +/// This class also assumes that the polyhedron is represented in object space and thus also +/// stores local copies of the polyhedron's planes transformed into world space. +/// +/// The approach of the algorithm is simple. It uses a maximum of three successive stages +/// to determine intersection. Each stage can early out if it can draw a conclusive result +/// already. +/// +/// 1. Simple tests of the polyhedron's bounding box against the input box. +/// 2. Standard test on plane set. +/// 3. Plane tests reduced to 2D and done for each of the orthographic side projections. +/// +/// @note The intersector depends on planes facing inwards. +template< typename Polyhedron > +struct PolyhedronBoxIntersector : public IntersectorBase< Polyhedron, Box3F > +{ + typedef IntersectorBase< Polyhedron, Box3F > Parent; + typedef Polyhedron PolyhedronType; + + protected: + + /// Bounds of the polyhedron. + Box3F mBounds; + + /// World-space planes. + Vector< PlaneF > mPlanes; + + /// Number of silhouette edges for each of the orthographic projections. + Point3I mNumEdgeLines; + + /// Edge line equations. X projection first, then Y, then Z. + /// X of each equation is mapped to a, Y to b, and Z to c of the standard + /// implicit form of line equations. + Vector< Point3F > mEdgeLines; + + /// Run the preprocessing step on the current polyhedron. + void _preprocess( const MatrixF& objToWorld, const Point3F& scale ); + + /// Project the point orthogonally down the given axis such that + /// the orientation of the resulting coordinate system remains + /// right-handed. + Point2F _project( U32 axis, const Point3F& p ) + { + switch( axis ) + { + case 0: return Point2F( - p.y, p.z ); + case 1: return Point2F( p.x, p.z ); + default: // silence compiler + case 2: return Point2F( p.x, - p.y ); + } + } + + public: + + PolyhedronBoxIntersector() {} + PolyhedronBoxIntersector( const PolyhedronType& polyhedron, + const MatrixF& objToWorld, + const Point3F& scale, + const Box3F& wsBounds ) + : Parent( polyhedron ), mBounds( wsBounds ) + { + _preprocess( objToWorld, scale ); + } + + OverlapTestResult test( const Box3F& box ) const; +}; + +//----------------------------------------------------------------------------- + +template< typename Polyhedron > +void PolyhedronBoxIntersector< Polyhedron >::_preprocess( const MatrixF& objToWorld, const Point3F& scale ) +{ + PROFILE_SCOPE( PolyhedronBoxIntersector_preprocess ); + + // Transform the planes. + + const U32 numPlanes = this->mTester.getNumPlanes(); + const typename Polyhedron::PlaneType* planes = this->mTester.getPlanes(); + + PlaneTransformer transformer; + transformer.set( objToWorld, scale ); + + mPlanes.setSize( numPlanes ); + for( U32 i = 0; i < numPlanes; ++ i ) + transformer.transform( planes[ i ], mPlanes[ i ] ); + + // Extract the silhouettes for each of the three + // orthographic projections. + + const U32 numEdges = this->mTester.getNumEdges(); + const typename Polyhedron::EdgeType* edges = this->mTester.getEdges(); + const typename Polyhedron::PointType* points = this->mTester.getPoints(); + + for( U32 i = 0; i < 3; ++ i ) + { + U32 numEdgesThisProj = 0; + + // Gather edge-lines for this projection. + + for( U32 n = 0; n < numEdges; ++ n ) + { + const typename Polyhedron::EdgeType& edge = edges[ n ]; + + // Compute dot product with face normals. With our projection + // pointing straight down the current axis, this is reduced to + // '1*normal[i]'. + + F32 dotFace[ 2 ]; + + dotFace[ 0 ] = mPlanes[ edge.face[ 0 ] ][ i ]; + dotFace[ 1 ] = mPlanes[ edge.face[ 1 ] ][ i ]; + + // Skip edge if not a silhouette edge in this view. + + if( mSign( dotFace[ 0 ] ) == mSign( dotFace[ 1 ] ) ) + continue; + + // Find out which face is the front facing one. Since we expect normals + // to be pointing inwards, this means a reversal of the normal back facing + // test and we're looking for a normal facing the *same* way as our projection. + + const U32 frontFace = dotFace[ 0 ] > 0.f ? 0 : 1; + if( dotFace[ frontFace ] <= 0.f ) + continue; // This face or other face is perpendicular to us. + + // Now we want to find the line equation for the edge. For that, we first need + // the normal. The direction of the normal is important so that we identify + // the half-spaces correctly. We want it to be pointing to the inside of the + // polyhedron. + + Point3F v1 = points[ edge.vertex[ 0 ] ]; + Point3F v2 = points[ edge.vertex[ 1 ] ]; + + v1.convolve( scale ); + v2.convolve( scale ); + + objToWorld.mulP( v1 ); + objToWorld.mulP( v2 ); + + Point2F q = _project( i, v1 ); // First point on line. + Point2F p = _project( i, v2 ); // Second point on line. + + if( frontFace != 0 ) + swap( p, q ); + + Point2F normal( - ( p.y - q.y ), p.x - q.x ); + normal.normalize(); + + // Now compute c. + + const F32 c = mDot( - q, normal ); + + // Add the edge. + + mEdgeLines.push_back( + Point3F( normal.x, normal.y, c ) + ); + + numEdgesThisProj ++; + } + + mNumEdgeLines[ i ] = numEdgesThisProj; + } +} + +//----------------------------------------------------------------------------- + +template< typename Polyhedron > +OverlapTestResult PolyhedronBoxIntersector< Polyhedron >::test( const Box3F& box ) const +{ + PROFILE_SCOPE( PolyhedronBoxIntersector_test ); + + // -- Bounding box tests. -- + + // If the box does not intersect with the AABB of the polyhedron, + // it must be outside. + + if( !mBounds.isOverlapped( box ) ) + return GeometryOutside; + + // If the polyhedron's bounding box is fully contained in the given box, + // the box is intersecting. + + if( box.isContained( mBounds ) ) + return GeometryIntersecting; + + // -- Face-plane tests. -- + + bool insideAll = true; + + // Test each of the planes to see if the bounding box lies + // fully in the negative space of any one of them. + + const U32 numPlanes = mPlanes.size(); + for( U32 i = 0; i < numPlanes; ++ i ) + { + const PlaneF& plane = mPlanes[ i ]; + + PlaneF::Side boxSide = plane.whichSide( box ); + if( boxSide == PlaneF::Back ) + return GeometryOutside; + + insideAll &= ( boxSide == PlaneF::Front ); + } + + // If the box is on the positive space of all of the polyhedron's + // planes, it's inside. + + if( insideAll ) + return GeometryInside; + + // -- Edge-line tests. -- + + U32 edgeLineIndex = 0; + for( U32 i = 0; i < 3; ++ i ) + { + // Determine the mapping of 3D to 2D for this projection. + + U32 xIndex = 0; + U32 yIndex = 0; + + switch( i ) + { + case 0: xIndex = 1; yIndex = 2; break; + case 1: xIndex = 0; yIndex = 2; break; + case 2: xIndex = 0; yIndex = 1; break; + } + + // Go through the edge-lines for this projection and + // test the p-vertex for each edge line. + + const U32 numEdgesForThisProj = mNumEdgeLines[ i ]; + for( U32 n = 0; n < numEdgesForThisProj; ++ n, edgeLineIndex ++ ) + { + const Point3F& edgeLine = mEdgeLines[ edgeLineIndex ]; + + // Determine the p-vertex for the current AABB/edge combo. + // Need to account for the axis flipping we have applied to maintain + // a right-handed coordinate system. + + Point2F pVertex; + + switch( i ) + { + case 0: + pVertex.x = - ( edgeLine.x < 0.f ? box.maxExtents[ xIndex ] : box.minExtents[ xIndex ] ); + pVertex.y = edgeLine.y > 0.f ? box.maxExtents[ yIndex ] : box.minExtents[ yIndex ]; + break; + + case 1: + pVertex.x = edgeLine.x > 0.f ? box.maxExtents[ xIndex ] : box.minExtents[ xIndex ]; + pVertex.y = edgeLine.y > 0.f ? box.maxExtents[ yIndex ] : box.minExtents[ yIndex ]; + break; + + case 2: + pVertex.x = edgeLine.x > 0.f ? box.maxExtents[ xIndex ] : box.minExtents[ xIndex ]; + pVertex.y = - ( edgeLine.y < 0.f ? box.maxExtents[ yIndex ] : box.minExtents[ yIndex ] ); + break; + } + + // See if the p-vertex lies inside in the negative half-space of the + // edge line. If so, the AABB is not intersecting the polyhedron in + // this projection so we can conclude our search here. + + const F32 d = edgeLine.x * pVertex.x + edgeLine.y * pVertex.y + edgeLine.z; + if( d < 0.f ) + return GeometryOutside; + } + } + + // Done. Determined to be intersecting. + + return GeometryIntersecting; +} + + +/// Geometric intersecting testing. +/// +/// This class is meant to be used for testing multiple geometries against +/// a specific geometric object. Unlike the various intersection test routines +/// in other classes, this class might precompute and store data that is going +/// to be used repeatedly in the tests. As such, Intersector can be faster +/// in certain cases. +/// +/// Also, intersectors are required to implement *exact* intersection tests, i.e. +/// it is not acceptable for an Intersector to produce false positives on any +/// of the OverlapTestResult values. +/// +/// This class itself has no functionality. It depends on specializations. +template< typename Tester, typename Testee > +struct Intersector : public IntersectorBase< Tester, Testee > {}; + +// Specializations. + +template<> +struct Intersector< AnyPolyhedron, Box3F > : public PolyhedronBoxIntersector< AnyPolyhedron > {}; + +#endif // !_MINTERSECTOR_H_ diff --git a/Engine/source/math/mMath.h b/Engine/source/math/mMath.h new file mode 100644 index 000000000..a1e070db7 --- /dev/null +++ b/Engine/source/math/mMath.h @@ -0,0 +1,52 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MMATH_H_ +#define _MMATH_H_ + + +#ifndef _MRECT_H_ +#include "math/mRect.h" +#endif +#ifndef _MBOX_H_ +#include "math/mBox.h" +#endif +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif +#ifndef _MQUAT_H_ +#include "math/mQuat.h" +#endif +#ifndef _MANGAXIS_H_ +#include "math/mAngAxis.h" +#endif +#ifndef _MSPHERE_H_ +#include "math/mSphere.h" +#endif +#ifndef _MRANDOM_H_ +#include "math/mRandom.h" +#endif +#ifndef _MEASE_H_ +#include "math/mEase.h" +#endif + +#endif //_MMATH_H_ diff --git a/Engine/source/math/mMathAMD.cpp b/Engine/source/math/mMathAMD.cpp new file mode 100644 index 000000000..2df3729ef --- /dev/null +++ b/Engine/source/math/mMathAMD.cpp @@ -0,0 +1,216 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "math/mMathFn.h" +#include "math/mPlane.h" +#include "math/mMatrix.h" + + +// extern void (*m_matF_x_point3F)(const F32 *m, const F32 *p, F32 *presult); +// extern void (*m_matF_x_vectorF)(const F32 *m, const F32 *v, F32 *vresult); + +/* not currently implemented. +void Athlon_MatrixF_x_Point3F(const F32 *m, const F32 *p, F32 *presult) +{ + m; + p; + presult; +} +*/ + +//============================================================ +// Here's the C code for MatF_x_MatF: +// note that the code below does it in a different order (optimal asm, after all!) +// +// r[0] = a[0]*b[0] + a[1]*b[4] + a[2]*b[8] + a[3]*b[12]; +// r[1] = a[0]*b[1] + a[1]*b[5] + a[2]*b[9] + a[3]*b[13]; +// r[2] = a[0]*b[2] + a[1]*b[6] + a[2]*b[10] + a[3]*b[14]; +// r[3] = a[0]*b[3] + a[1]*b[7] + a[2]*b[11] + a[3]*b[15]; +// +// r[4] = a[4]*b[0] + a[5]*b[4] + a[6]*b[8] + a[7]*b[12]; +// r[5] = a[4]*b[1] + a[5]*b[5] + a[6]*b[9] + a[7]*b[13]; +// r[6] = a[4]*b[2] + a[5]*b[6] + a[6]*b[10] + a[7]*b[14]; +// r[7] = a[4]*b[3] + a[5]*b[7] + a[6]*b[11] + a[7]*b[15]; +// +// r[8] = a[8]*b[0] + a[9]*b[4] + a[10]*b[8] + a[11]*b[12]; +// r[9] = a[8]*b[1] + a[9]*b[5] + a[10]*b[9] + a[11]*b[13]; +// r[10]= a[8]*b[2] + a[9]*b[6] + a[10]*b[10]+ a[11]*b[14]; +// r[11]= a[8]*b[3] + a[9]*b[7] + a[10]*b[11]+ a[11]*b[15]; +// +// r[12]= a[12]*b[0]+ a[13]*b[4]+ a[14]*b[8] + a[15]*b[12]; +// r[13]= a[12]*b[1]+ a[13]*b[5]+ a[14]*b[9] + a[15]*b[13]; +// r[14]= a[12]*b[2]+ a[13]*b[6]+ a[14]*b[10]+ a[15]*b[14]; +// r[15]= a[12]*b[3]+ a[13]*b[7]+ a[14]*b[11]+ a[15]*b[15]; +//============================================================ + +#if defined(TORQUE_SUPPORTS_VC_INLINE_X86_ASM) +#define ADD_3DNOW_FUNCS +// inlined version here. +void Athlon_MatrixF_x_MatrixF(const F32 *matA, const F32 *matB, F32 *result) +{ + __asm + { + femms + + mov ecx, matA + mov edx, matB + mov eax, result + + prefetch [ecx+32] ;// These may help - + prefetch [edx+32] ;// and probably don't hurt + + movq mm0,[ecx] ;// a21 | a11 + movq mm1,[ecx+8] ;// a41 | a31 + movq mm4,[edx] ;// b21 | b11 + punpckhdq mm2,mm0 ;// a21 | + movq mm5,[edx+16] ;// b22 | b12 + punpckhdq mm3,mm1 ;// a41 | + movq mm6,[edx+32] ;// b23 | b13 + punpckldq mm0,mm0 ;// a11 | a11 + punpckldq mm1,mm1 ;// a31 | a31 + pfmul mm4,mm0 ;// a11*b21 | a11*b11 + punpckhdq mm2,mm2 ;// a21 | a21 + pfmul mm0,[edx+8] ;// a11*b41 | a11*b31 + movq mm7,[edx+48] ;// b24 | b14 + pfmul mm5,mm2 ;// a21*b22 | a21*b12 + punpckhdq mm3,mm3 ;// a41 | a41 + pfmul mm2,[edx+24] ;// a21*b42 | a21*b32 + pfmul mm6,mm1 ;// a31*b23 | a31*b13 + pfadd mm5,mm4 ;// a21*b22 + a11*b21 | a21*b12 + a11*b11 + pfmul mm1,[edx+40] ;// a31*b43 | a31*b33 + pfadd mm2,mm0 ;// a21*b42 + a11*b41 | a21*b32 + a11*b31 + pfmul mm7,mm3 ;// a41*b24 | a41*b14 + pfadd mm6,mm5 ;// a21*b22 + a11*b21 + a31*b23 | a21*b12 + a11*b11 + a31*b13 + pfmul mm3,[edx+56] ;// a41*b44 | a41*b34 + pfadd mm2,mm1 ;// a21*b42 + a11*b41 + a31*b43 | a21*b32 + a11*b31 + a31*b33 + pfadd mm7,mm6 ;// a41*b24 + a21*b22 + a11*b21 + a31*b23 | a41*b14 + a21*b12 + a11*b11 + a31*b13 + movq mm0,[ecx+16] ;// a22 | a12 + pfadd mm3,mm2 ;// a41*b44 + a21*b42 + a11*b41 + a31*b43 | a41*b34 + a21*b32 + a11*b31 + a31*b33 + movq mm1,[ecx+24] ;// a42 | a32 + movq [eax],mm7 ;// r21 | r11 + movq mm4,[edx] ;// b21 | b11 + movq [eax+8],mm3 ;// r41 | r31 + + punpckhdq mm2,mm0 ;// a22 | XXX + movq mm5,[edx+16] ;// b22 | b12 + punpckhdq mm3,mm1 ;// a42 | XXX + movq mm6,[edx+32] ;// b23 | b13 + punpckldq mm0,mm0 ;// a12 | a12 + punpckldq mm1,mm1 ;// a32 | a32 + pfmul mm4,mm0 ;// a12*b21 | a12*b11 + punpckhdq mm2,mm2 ;// a22 | a22 + pfmul mm0,[edx+8] ;// a12*b41 | a12*b31 + movq mm7,[edx+48] ;// b24 | b14 + pfmul mm5,mm2 ;// a22*b22 | a22*b12 + punpckhdq mm3,mm3 ;// a42 | a42 + pfmul mm2,[edx+24] ;// a22*b42 | a22*b32 + pfmul mm6,mm1 ;// a32*b23 | a32*b13 + pfadd mm5,mm4 ;// a12*b21 + a22*b22 | a12*b11 + a22*b12 + pfmul mm1,[edx+40] ;// a32*b43 | a32*b33 + pfadd mm2,mm0 ;// a12*b41 + a22*b42 | a12*b11 + a22*b32 + pfmul mm7,mm3 ;// a42*b24 | a42*b14 + pfadd mm6,mm5 ;// a32*b23 + a12*b21 + a22*b22 | a32*b13 + a12*b11 + a22*b12 + pfmul mm3,[edx+56] ;// a42*b44 | a42*b34 + pfadd mm2,mm1 ;// a32*b43 + a12*b41 + a22*b42 | a32*b33 + a12*b11 + a22*b32 + pfadd mm7,mm6 ;// a42*b24 + a32*b23 + a12*b21 + a22*b22 | a42*b14 + a32*b13 + a12*b11 + a22*b12 + movq mm0,[ecx+32] ;// a23 | a13 + pfadd mm3,mm2 ;// a42*b44 + a32*b43 + a12*b41 + a22*b42 | a42*b34 + a32*b33 + a12*b11 + a22*b32 + movq mm1,[ecx+40] ;// a43 | a33 + movq [eax+16],mm7 ;// r22 | r12 + movq mm4,[edx] ;// b21 | b11 + movq [eax+24],mm3 ;// r42 | r32 + + punpckhdq mm2,mm0 ;// a23 | XXX + movq mm5,[edx+16] ;// b22 | b12 + punpckhdq mm3,mm1 ;// a43 | XXX + movq mm6,[edx+32] ;// b23 | b13 + punpckldq mm0,mm0 ;// a13 | a13 + punpckldq mm1,mm1 ;// a33 | a33 + pfmul mm4,mm0 ;// a13*b21 | a13*b11 + punpckhdq mm2,mm2 ;// a23 | a23 + pfmul mm0,[edx+8] ;// a13*b41 | a13*b31 + movq mm7,[edx+48] ;// b24 | b14 + pfmul mm5,mm2 ;// a23*b22 | a23*b12 + punpckhdq mm3,mm3 ;// a43 | a43 + pfmul mm2,[edx+24] ;// a23*b42 | a23*b32 + pfmul mm6,mm1 ;// a33*b23 | a33*b13 + pfadd mm5,mm4 ;// a23*b22 + a13*b21 | a23*b12 + a13*b11 + pfmul mm1,[edx+40] ;// a33*b43 | a33*b33 + pfadd mm2,mm0 ;// a13*b41 + a23*b42 | a13*b31 + a23*b32 + pfmul mm7,mm3 ;// a43*b24 | a43*b14 + pfadd mm6,mm5 ;// a33*b23 + a23*b22 + a13*b21 | a33*b13 + a23*b12 + a13*b11 + pfmul mm3,[edx+56] ;// a43*b44 | a43*b34 + pfadd mm2,mm1 ;// a33*b43*a13*b41 + a23*b42 | a33*b33 + a13*b31 + a23*b32 + pfadd mm7,mm6 ;// a43*b24 + a33*b23 + a23*b22 + a13*b21 | a43*b14 + a33*b13 + a23*b12 + a13*b11 + movq mm0,[ecx+48] ;// a24 | a14 + pfadd mm3,mm2 ;// a43*b44 + a33*b43*a13*b41 + a23*b42 | a43*b34 + a33*b33 + a13*b31 + a23*b32 + movq mm1,[ecx+56] ;// a44 | a34 + movq [eax+32],mm7 ;// r23 | r13 + movq mm4,[edx] ;// b21 | b11 + movq [eax+40],mm3 ;// r43 | r33 + + punpckhdq mm2,mm0 ;// a24 | XXX + movq mm5,[edx+16] ;// b22 | b12 + punpckhdq mm3,mm1 ;// a44 | XXX + movq mm6,[edx+32] ;// b23 | b13 + punpckldq mm0,mm0 ;// a14 | a14 + punpckldq mm1,mm1 ;// a34 | a34 + pfmul mm4,mm0 ;// a14*b21 | a14*b11 + punpckhdq mm2,mm2 ;// a24 | a24 + pfmul mm0,[edx+8] ;// a14*b41 | a14*b31 + movq mm7,[edx+48] ;// b24 | b14 + pfmul mm5,mm2 ;// a24*b22 | a24*b12 + punpckhdq mm3,mm3 ;// a44 | a44 + pfmul mm2,[edx+24] ;// a24*b 42 | a24*b32 + pfmul mm6,mm1 ;// a34*b23 | a34*b13 + pfadd mm5,mm4 ;// a14*b21 + a24*b22 | a14*b11 + a24*b12 + pfmul mm1,[edx+40] ;// a34*b43 | a34*b33 + pfadd mm2,mm0 ;// a14*b41 + a24*b 42 | a14*b31 + a24*b32 + pfmul mm7,mm3 ;// a44*b24 | a44*b14 + pfadd mm6,mm5 ;// a34*b23 + a14*b21 + a24*b22 | a34*b13 + a14*b11 + a24*b12 + pfmul mm3,[edx+56] ;// a44*b44 | a44*b34 + pfadd mm2,mm1 ;// a34*b43 + a14*b41 + a24*b 42 | a34*b33 + a14*b31 + a24*b32 + pfadd mm7,mm6 ;// a44*b24 + a14*b23 + a24*b 42 | a44*b14 + a14*b31 + a24*b32 + pfadd mm3,mm2 ;// a44*b44 + a34*b43 + a14*b41 + a24*b42 | a44*b34 + a34*b33 + a14*b31 + a24*b32 + movq [eax+48],mm7 ;// r24 | r14 + movq [eax+56],mm3 ;// r44 | r34 + femms + } +} +#elif defined(TORQUE_SUPPORTS_NASM) +#define ADD_3DNOW_FUNCS +extern "C" +{ + void Athlon_MatrixF_x_MatrixF(const F32 *matA, const F32 *matB, F32 *result); +} + +#endif + +void mInstall_AMD_Math() +{ +#if defined(ADD_3DNOW_FUNCS) + m_matF_x_matF = Athlon_MatrixF_x_MatrixF; +#endif + // m_matF_x_point3F = Athlon_MatrixF_x_Point3F; + // m_matF_x_vectorF = Athlon_MatrixF_x_VectorF; +} + diff --git a/Engine/source/math/mMathAMD_ASM.asm b/Engine/source/math/mMathAMD_ASM.asm new file mode 100644 index 000000000..74d3fa1ec --- /dev/null +++ b/Engine/source/math/mMathAMD_ASM.asm @@ -0,0 +1,177 @@ +;----------------------------------------------------------------------------- +; Copyright (c) 2012 GarageGames, LLC +; +; Permission is hereby granted, free of charge, to any person obtaining a copy +; of this software and associated documentation files (the "Software"), to +; deal in the Software without restriction, including without limitation the +; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +; sell copies of the Software, and to permit persons to whom the Software is +; furnished to do so, subject to the following conditions: +; +; The above copyright notice and this permission notice shall be included in +; all copies or substantial portions of the Software. +; +; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +; IN THE SOFTWARE. +;----------------------------------------------------------------------------- + + +segment .data + +matA dd 0 +result dd 0 +matB dd 0 + +segment .text + +%macro export_fn 1 + %ifidn __OUTPUT_FORMAT__, elf + ; No underscore needed for ELF object files + global %1 + %1: + %else + global _%1 + _%1: + %endif +%endmacro + + +%define arg(x) [esp+(x*4)] + + + +;void Athlon_MatrixF_x_MatrixF(const F32 *matA, const F32 *matB, F32 *result) + +export_fn Athlon_MatrixF_x_MatrixF + + mov ecx, arg(1) + mov edx, arg(2) + mov eax, arg(3) + + femms + prefetch [ecx+32] ; These may help - + prefetch [edx+32] ; and probably don't hurt + + movq mm0,[ecx] ; a21 | a11 + movq mm1,[ecx+8] ; a41 | a31 + movq mm4,[edx] ; b21 | b11 + punpckhdq mm2,mm0 ; a21 | + movq mm5,[edx+16] ; b22 | b12 + punpckhdq mm3,mm1 ; a41 | + movq mm6,[edx+32] ; b23 | b13 + punpckldq mm0,mm0 ; a11 | a11 + punpckldq mm1,mm1 ; a31 | a31 + pfmul mm4,mm0 ; a11*b21 | a11*b11 + punpckhdq mm2,mm2 ; a21 | a21 + pfmul mm0,[edx+8] ; a11*b41 | a11*b31 + movq mm7,[edx+48] ; b24 | b14 + pfmul mm5,mm2 ; a21*b22 | a21*b12 + punpckhdq mm3,mm3 ; a41 | a41 + pfmul mm2,[edx+24] ; a21*b42 | a21*b32 + pfmul mm6,mm1 ; a31*b23 | a31*b13 + pfadd mm5,mm4 ; a21*b22 + a11*b21 | a21*b12 + a11*b11 + pfmul mm1,[edx+40] ; a31*b43 | a31*b33 + pfadd mm2,mm0 ; a21*b42 + a11*b41 | a21*b32 + a11*b31 + pfmul mm7,mm3 ; a41*b24 | a41*b14 + pfadd mm6,mm5 ; a21*b22 + a11*b21 + a31*b23 | a21*b12 + a11*b11 + a31*b13 + pfmul mm3,[edx+56] ; a41*b44 | a41*b34 + pfadd mm2,mm1 ; a21*b42 + a11*b41 + a31*b43 | a21*b32 + a11*b31 + a31*b33 + pfadd mm7,mm6 ; a41*b24 + a21*b22 + a11*b21 + a31*b23 | a41*b14 + a21*b12 + a11*b11 + a31*b13 + movq mm0,[ecx+16] ; a22 | a12 + pfadd mm3,mm2 ; a41*b44 + a21*b42 + a11*b41 + a31*b43 | a41*b34 + a21*b32 + a11*b31 + a31*b33 + movq mm1,[ecx+24] ; a42 | a32 + movq [eax],mm7 ; r21 | r11 + movq mm4,[edx] ; b21 | b11 + movq [eax+8],mm3 ; r41 | r31 + + punpckhdq mm2,mm0 ; a22 | XXX + movq mm5,[edx+16] ; b22 | b12 + punpckhdq mm3,mm1 ; a42 | XXX + movq mm6,[edx+32] ; b23 | b13 + punpckldq mm0,mm0 ; a12 | a12 + punpckldq mm1,mm1 ; a32 | a32 + pfmul mm4,mm0 ; a12*b21 | a12*b11 + punpckhdq mm2,mm2 ; a22 | a22 + pfmul mm0,[edx+8] ; a12*b41 | a12*b31 + movq mm7,[edx+48] ; b24 | b14 + pfmul mm5,mm2 ; a22*b22 | a22*b12 + punpckhdq mm3,mm3 ; a42 | a42 + pfmul mm2,[edx+24] ; a22*b42 | a22*b32 + pfmul mm6,mm1 ; a32*b23 | a32*b13 + pfadd mm5,mm4 ; a12*b21 + a22*b22 | a12*b11 + a22*b12 + pfmul mm1,[edx+40] ; a32*b43 | a32*b33 + pfadd mm2,mm0 ; a12*b41 + a22*b42 | a12*b11 + a22*b32 + pfmul mm7,mm3 ; a42*b24 | a42*b14 + pfadd mm6,mm5 ; a32*b23 + a12*b21 + a22*b22 | a32*b13 + a12*b11 + a22*b12 + pfmul mm3,[edx+56] ; a42*b44 | a42*b34 + pfadd mm2,mm1 ; a32*b43 + a12*b41 + a22*b42 | a32*b33 + a12*b11 + a22*b32 + pfadd mm7,mm6 ; a42*b24 + a32*b23 + a12*b21 + a22*b22 | a42*b14 + a32*b13 + a12*b11 + a22*b12 + movq mm0,[ecx+32] ; a23 | a13 + pfadd mm3,mm2 ; a42*b44 + a32*b43 + a12*b41 + a22*b42 | a42*b34 + a32*b33 + a12*b11 + a22*b32 + movq mm1,[ecx+40] ; a43 | a33 + movq [eax+16],mm7 ; r22 | r12 + movq mm4,[edx] ; b21 | b11 + movq [eax+24],mm3 ; r42 | r32 + + punpckhdq mm2,mm0 ; a23 | XXX + movq mm5,[edx+16] ; b22 | b12 + punpckhdq mm3,mm1 ; a43 | XXX + movq mm6,[edx+32] ; b23 | b13 + punpckldq mm0,mm0 ; a13 | a13 + punpckldq mm1,mm1 ; a33 | a33 + pfmul mm4,mm0 ; a13*b21 | a13*b11 + punpckhdq mm2,mm2 ; a23 | a23 + pfmul mm0,[edx+8] ; a13*b41 | a13*b31 + movq mm7,[edx+48] ; b24 | b14 + pfmul mm5,mm2 ; a23*b22 | a23*b12 + punpckhdq mm3,mm3 ; a43 | a43 + pfmul mm2,[edx+24] ; a23*b42 | a23*b32 + pfmul mm6,mm1 ; a33*b23 | a33*b13 + pfadd mm5,mm4 ; a23*b22 + a13*b21 | a23*b12 + a13*b11 + pfmul mm1,[edx+40] ; a33*b43 | a33*b33 + pfadd mm2,mm0 ; a13*b41 + a23*b42 | a13*b31 + a23*b32 + pfmul mm7,mm3 ; a43*b24 | a43*b14 + pfadd mm6,mm5 ; a33*b23 + a23*b22 + a13*b21 | a33*b13 + a23*b12 + a13*b11 + pfmul mm3,[edx+56] ; a43*b44 | a43*b34 + pfadd mm2,mm1 ; a33*b43*a13*b41 + a23*b42 | a33*b33 + a13*b31 + a23*b32 + pfadd mm7,mm6 ; a43*b24 + a33*b23 + a23*b22 + a13*b21 | a43*b14 + a33*b13 + a23*b12 + a13*b11 + movq mm0,[ecx+48] ; a24 | a14 + pfadd mm3,mm2 ; a43*b44 + a33*b43*a13*b41 + a23*b42 | a43*b34 + a33*b33 + a13*b31 + a23*b32 + movq mm1,[ecx+56] ; a44 | a34 + movq [eax+32],mm7 ; r23 | r13 + movq mm4,[edx] ; b21 | b11 + movq [eax+40],mm3 ; r43 | r33 + + punpckhdq mm2,mm0 ; a24 | XXX + movq mm5,[edx+16] ; b22 | b12 + punpckhdq mm3,mm1 ; a44 | XXX + movq mm6,[edx+32] ; b23 | b13 + punpckldq mm0,mm0 ; a14 | a14 + punpckldq mm1,mm1 ; a34 | a34 + pfmul mm4,mm0 ; a14*b21 | a14*b11 + punpckhdq mm2,mm2 ; a24 | a24 + pfmul mm0,[edx+8] ; a14*b41 | a14*b31 + movq mm7,[edx+48] ; b24 | b14 + pfmul mm5,mm2 ; a24*b22 | a24*b12 + punpckhdq mm3,mm3 ; a44 | a44 + pfmul mm2,[edx+24] ; a24*b 42 | a24*b32 + pfmul mm6,mm1 ; a34*b23 | a34*b13 + pfadd mm5,mm4 ; a14*b21 + a24*b22 | a14*b11 + a24*b12 + pfmul mm1,[edx+40] ; a34*b43 | a34*b33 + pfadd mm2,mm0 ; a14*b41 + a24*b 42 | a14*b31 + a24*b32 + pfmul mm7,mm3 ; a44*b24 | a44*b14 + pfadd mm6,mm5 ; a34*b23 + a14*b21 + a24*b22 | a34*b13 + a14*b11 + a24*b12 + pfmul mm3,[edx+56] ; a44*b44 | a44*b34 + pfadd mm2,mm1 ; a34*b43 + a14*b41 + a24*b 42 | a34*b33 + a14*b31 + a24*b32 + pfadd mm7,mm6 ; a44*b24 + a14*b23 + a24*b 42 | a44*b14 + a14*b31 + a24*b32 + pfadd mm3,mm2 ; a44*b44 + a34*b43 + a14*b41 + a24*b42 | a44*b34 + a34*b33 + a14*b31 + a24*b32 + movq [eax+48],mm7 ; r24 | r14 + movq [eax+56],mm3 ; r44 | r34 + femms + + ret diff --git a/Engine/source/math/mMathAltivec.cpp b/Engine/source/math/mMathAltivec.cpp new file mode 100644 index 000000000..6bdc9d752 --- /dev/null +++ b/Engine/source/math/mMathAltivec.cpp @@ -0,0 +1,136 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// This file is Mac specific. +#if defined( __APPLE__ ) + +#include +#include "math/mMathFn.h" +#include "console/console.h" +#include "platform/profiler.h" + +#if defined( __VEC__ ) + +// tests show BLAS to be about 4x slower than aligned altivec, 3x slower than unaligned altivec code below. + +/// Altivec 4x4 Matrix multiplication. +/// Most of our time is spent moving data in & out of the vector registers. +/// Alignment of the matrix data to 16-byte boundaries is very important, +/// because we get a much better speed gain if we can assume the data is aligned. +void vec_MatrixF_x_MatrixF(const F32 *matA, const F32 *matB, F32 *result) +{ + vector float A[4][1]; + vector float B[4][1]; + vector float C[4][1]; + + /// If the incoming pointers are not 16-byte aligned, we have to load & store the slow way. + if((int)matA & 0xF || (int)matB & 0xF || (int)result & 0xF) + { + F32 *loader; + loader = (F32*) &A; + loader[0] = matA[0]; + loader[1] = matA[1]; + loader[2] = matA[2]; + loader[3] = matA[3]; + loader[4] = matA[4]; + loader[5] = matA[5]; + loader[6] = matA[6]; + loader[7] = matA[7]; + loader[8] = matA[8]; + loader[9] = matA[9]; + loader[10] = matA[10]; + loader[11] = matA[11]; + loader[12] = matA[12]; + loader[13] = matA[13]; + loader[14] = matA[14]; + loader[15] = matA[15]; + loader = (F32*) &B; + loader[0] = matB[0]; + loader[1] = matB[1]; + loader[2] = matB[2]; + loader[3] = matB[3]; + loader[4] = matB[4]; + loader[5] = matB[5]; + loader[6] = matB[6]; + loader[7] = matB[7]; + loader[8] = matB[8]; + loader[9] = matB[9]; + loader[10] = matB[10]; + loader[11] = matB[11]; + loader[12] = matB[12]; + loader[13] = matB[13]; + loader[14] = matB[14]; + loader[15] = matB[15]; + + vMultMatMat_4x4( A, B, C); + + loader = (F32*) &C; + + result[0] = loader[0]; + result[1] = loader[1]; + result[2] = loader[2]; + result[3] = loader[3]; + result[4] = loader[4]; + result[5] = loader[5]; + result[6] = loader[6]; + result[7] = loader[7]; + result[8] = loader[8]; + result[9] = loader[9]; + result[10] = loader[10]; + result[11] = loader[11]; + result[12] = loader[12]; + result[13] = loader[13]; + result[14] = loader[14]; + result[15] = loader[15]; + } + else + { + A[0][0] = vec_ld(0, matA); + A[1][0] = vec_ld(16, matA); + A[2][0] = vec_ld(32, matA); + A[3][0] = vec_ld(48, matA); + B[0][0] = vec_ld(0, matB); + B[1][0] = vec_ld(16, matB); + B[2][0] = vec_ld(32, matB); + B[3][0] = vec_ld(48, matB); + + vMultMatMat_4x4( A, B, C); + + vec_st(C[0][0], 0, result); + vec_st(C[1][0], 16, result); + vec_st(C[2][0], 32, result); + vec_st(C[3][0], 48, result); + } +} + +void mInstallLibrary_Vec() +{ + m_matF_x_matF = vec_MatrixF_x_MatrixF; +} +#else // defined(__VEC__) +void mInstallLibrary_Vec() +{ + Con::warnf("Cannot use altivec math, this build does not support altivec."); +} +#endif// defined(__VEC__) + +#endif// defined(__APPLE__) \ No newline at end of file diff --git a/Engine/source/math/mMathFn.h b/Engine/source/math/mMathFn.h new file mode 100644 index 000000000..cdeb4b4f5 --- /dev/null +++ b/Engine/source/math/mMathFn.h @@ -0,0 +1,471 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MMATHFN_H_ +#define _MMATHFN_H_ + +#include +#include +#include + +#ifndef _MCONSTANTS_H_ +#include "math/mConstants.h" +#endif +#ifndef _PLATFORMASSERT_H_ +#include "platform/platformAssert.h" +#endif + + +extern void MathConsoleInit(); + +//-------------------------------------- +// Installable Library Prototypes +extern S32 (*m_mulDivS32)(S32 a, S32 b, S32 c); +extern U32 (*m_mulDivU32)(S32 a, S32 b, U32 c); + +extern F32 (*m_catmullrom)(F32 t, F32 p0, F32 p1, F32 p2, F32 p3); + +extern void (*m_sincos)( F32 angle, F32 *s, F32 *c ); +extern void (*m_sincosD)( F64 angle, F64 *s, F64 *c ); + +extern void (*m_point2F_normalize)(F32 *p); +extern void (*m_point2F_normalize_f)(F32 *p, F32 len); +extern void (*m_point2D_normalize)(F64 *p); +extern void (*m_point2D_normalize_f)(F64 *p, F64 len); +extern void (*m_point3F_normalize)(F32 *p); +extern void (*m_point3F_normalize_f)(F32 *p, F32 len); +extern void (*m_point3F_interpolate)(const F32 *from, const F32 *to, F32 factor, F32 *result); + +extern void (*m_point3D_normalize)(F64 *p); +extern void (*m_point3D_normalize_f)(F64 *p, F64 len); +extern void (*m_point3D_interpolate)(const F64 *from, const F64 *to, F64 factor, F64 *result); + +extern void (*m_point3F_bulk_dot)(const F32* refVector, + const F32* dotPoints, + const U32 numPoints, + const U32 pointStride, + F32* output); +extern void (*m_point3F_bulk_dot_indexed)(const F32* refVector, + const F32* dotPoints, + const U32 numPoints, + const U32 pointStride, + const U32* pointIndices, + F32* output); + +extern void (*m_quatF_set_matF)( F32 x, F32 y, F32 z, F32 w, F32* m ); + +extern void (*m_matF_set_euler)(const F32 *e, F32 *result); +extern void (*m_matF_set_euler_point)(const F32 *e, const F32 *p, F32 *result); +extern void (*m_matF_identity)(F32 *m); +extern void (*m_matF_inverse)(F32 *m); +extern void (*m_matF_invert_to)(const F32 *m, F32 *d); +extern void (*m_matF_affineInverse)(F32 *m); +extern void (*m_matF_transpose)(F32 *m); +extern void (*m_matF_scale)(F32 *m,const F32* p); +extern void (*m_matF_normalize)(F32 *m); +extern F32 (*m_matF_determinant)(const F32 *m); +extern void (*m_matF_x_matF)(const F32 *a, const F32 *b, F32 *mresult); +extern void (*m_matF_x_matF_aligned)(const F32 *a, const F32 *b, F32 *mresult); +// extern void (*m_matF_x_point3F)(const F32 *m, const F32 *p, F32 *presult); +// extern void (*m_matF_x_vectorF)(const F32 *m, const F32 *v, F32 *vresult); +extern void (*m_matF_x_point4F)(const F32 *m, const F32 *p, F32 *presult); +extern void (*m_matF_x_scale_x_planeF)(const F32 *m, const F32* s, const F32 *p, F32 *presult); +extern void (*m_matF_x_box3F)(const F32 *m, F32 *min, F32 *max); + +// Note that x must point to at least 4 values for quartics, and 3 for cubics +extern U32 (*mSolveQuadratic)(F32 a, F32 b, F32 c, F32* x); +extern U32 (*mSolveCubic)(F32 a, F32 b, F32 c, F32 d, F32* x); +extern U32 (*mSolveQuartic)(F32 a, F32 b, F32 c, F32 d, F32 e, F32* x); + +extern S32 mRandI(S32 i1, S32 i2); // random # from i1 to i2 inclusive +extern F32 mRandF(F32 f1, F32 f2); // random # from f1 to f2 inclusive +extern F32 mRandF(); // random # from 0.0 to 1.0 inclusive + + +inline void m_matF_x_point3F(const F32 *m, const F32 *p, F32 *presult) +{ + AssertFatal(p != presult, "Error, aliasing matrix mul pointers not allowed here!"); + +#ifdef TORQUE_COMPILER_GCC + const F32 p0 = p[0], p1 = p[1], p2 = p[2]; + const F32 m0 = m[0], m1 = m[1], m2 = m[2]; + const F32 m3 = m[3], m4 = m[4], m5 = m[5]; + const F32 m6 = m[6], m7 = m[7], m8 = m[8]; + const F32 m9 = m[9], m10 = m[10], m11 = m[11]; + + presult[0] = m0*p0 + m1*p1 + m2*p2 + m3; + presult[1] = m4*p0 + m5*p1 + m6*p2 + m7; + presult[2] = m8*p0 + m9*p1 + m10*p2 + m11; +#else + presult[0] = m[0]*p[0] + m[1]*p[1] + m[2]*p[2] + m[3]; + presult[1] = m[4]*p[0] + m[5]*p[1] + m[6]*p[2] + m[7]; + presult[2] = m[8]*p[0] + m[9]*p[1] + m[10]*p[2] + m[11]; +#endif +} + + +//-------------------------------------- +inline void m_matF_x_vectorF(const F32 *m, const F32 *v, F32 *vresult) +{ + AssertFatal(v != vresult, "Error, aliasing matrix mul pointers not allowed here!"); + +#ifdef TORQUE_COMPILER_GCC + const F32 v0 = v[0], v1 = v[1], v2 = v[2]; + const F32 m0 = m[0], m1 = m[1], m2 = m[2]; + const F32 m4 = m[4], m5 = m[5], m6 = m[6]; + const F32 m8 = m[8], m9 = m[9], m10 = m[10]; + + vresult[0] = m0*v0 + m1*v1 + m2*v2; + vresult[1] = m4*v0 + m5*v1 + m6*v2; + vresult[2] = m8*v0 + m9*v1 + m10*v2; +#else + vresult[0] = m[0]*v[0] + m[1]*v[1] + m[2]*v[2]; + vresult[1] = m[4]*v[0] + m[5]*v[1] + m[6]*v[2]; + vresult[2] = m[8]*v[0] + m[9]*v[1] + m[10]*v[2]; +#endif +} + + +//-------------------------------------- +// Inlines + +inline bool mIsEqual( F32 a, F32 b, const F32 epsilon = __EQUAL_CONST_F ) +{ + F32 diff = a - b; + return diff > -epsilon && diff < epsilon; +} + +inline bool mIsZero(const F32 val, const F32 epsilon = __EQUAL_CONST_F ) +{ + return (val > -epsilon) && (val < epsilon); +} + +inline F32 mClampToZero(F32& input) +{ + if (input < __EQUAL_CONST_F && input > -__EQUAL_CONST_F) + input = 0.0f; + + return input; +} + + +inline F32 mMax(const F32 x, const F32 y) +{ + if (x > y) + return x; + return y; +} + +inline F32 mFloor(const F32 val) +{ + return (F32) floor(val); +} + +inline F32 mCeil(const F32 val) +{ + return (F32) ceil(val); +} + +inline F32 mFabs(const F32 val) +{ + return (F32) fabs(val); +} + +inline F64 mFabs(const F64 val) +{ + return fabs(val); +} + +inline F32 mFmod(const F32 val, const F32 mod) +{ + return fmod(val, mod); +} + +inline S32 mAbs(const S32 val) +{ + return abs(val); +} + +inline F32 mRoundToNearest( const F32 val ) +{ + return mFloor( val + .5f ); +} + +inline S32 mClamp(S32 val, S32 low, S32 high) +{ + return getMax(getMin(val, high), low); +} + +inline U32 mClampU(U32 val, U32 low, U32 high) +{ + return getMax(getMin(val, high), low); +} + +inline F32 mClampF(F32 val, F32 low, F32 high) +{ + return (F32) getMax(getMin(val, high), low); +} + +/// Template function for doing a linear interpolation between any two +/// types which implement operators for scalar multiply and addition. +template +inline T mLerp( const T &v1, const T &v2, F32 factor ) +{ + return ( v1 * ( 1.0f - factor ) ) + ( v2 * factor ); +} + +inline S32 mMulDiv(S32 a, S32 b, S32 c) +{ + return m_mulDivS32(a, b, c); +} + +inline U32 mMulDiv(S32 a, S32 b, U32 c) +{ + return m_mulDivU32(a, b, c); +} + +inline F32 mSin(const F32 angle) +{ + return (F32) sin(angle); +} + +inline F32 mCos(const F32 angle) +{ + return (F32) cos(angle); +} + +inline F32 mTan(const F32 angle) +{ + return (F32) tan(angle); +} + +inline F32 mAsin(const F32 val) +{ + return (F32) asin(val); +} + +inline F32 mAcos(const F32 val) +{ + return (F32) acos(val); +} + +inline F32 mAtan( const F32 x ) +{ + return (F32) atan( x ); +} + +inline F32 mAtan2(const F32 y, const F32 x) +{ + return (F32)atan2(y, x); +} + +inline void mSinCos(const F32 angle, F32 &s, F32 &c) +{ + m_sincos( angle, &s, &c ); +} + +inline F32 mTanh(const F32 angle) +{ + return (F32) tanh(angle); +} + +inline F32 mSqrt(const F32 val) +{ + return (F32) sqrt(val); +} + +inline F64 mSqrt(const F64 val) +{ + return (F64) sqrt(val); +} + +inline F32 mPow(const F32 x, const F32 y) +{ + return (F32) pow(x, y); +} + +inline F32 mLog(const F32 val) +{ + return (F32) log(val); +} + +inline F32 mExp(const F32 val) +{ + return (F32) exp(val); +} + +inline F64 mSin(const F64 angle) +{ + return (F64) sin(angle); +} + +inline F64 mCos(const F64 angle) +{ + return (F64) cos(angle); +} + +inline F64 mTan(const F64 angle) +{ + return (F64) tan(angle); +} + +inline F64 mAsin(const F64 val) +{ + return (F64) asin(val); +} + +inline F64 mAcos(const F64 val) +{ + return (F64) acos(val); +} + +inline F64 mAtan( const F64 x ) +{ + return (F64) atan( x ); +} + +inline F64 mAtan2(const F64 x, const F64 y) +{ + return (F64) atan2(x, y); +} + +inline void mSinCos(const F64 angle, F64 &s, F64 &c) +{ + m_sincosD( angle, &s, &c ); +} + +inline F64 mTanh(const F64 angle) +{ + return (F64) tanh(angle); +} + +inline F64 mPow(const F64 x, const F64 y) +{ + return (F64) pow(x, y); +} + +inline F64 mLog(const F64 val) +{ + return (F64) log(val); +} + + +inline F32 mCatmullrom(F32 t, F32 p0, F32 p1, F32 p2, F32 p3) +{ + return m_catmullrom(t, p0, p1, p2, p3); +} + + +inline F64 mFabsD(const F64 val) +{ + return (F64) fabs(val); +} + +inline F64 mFmodD(const F64 val, const F64 mod) +{ + return (F64) fmod(val, mod); +} + +inline F64 mSqrtD(const F64 val) +{ + return (F64) sqrt(val); +} + +inline F64 mFloorD(const F64 val) +{ + return (F64) floor(val); +} + +inline F64 mCeilD(const F64 val) +{ + return (F64) ceil(val); +} + +/// +template< typename A, typename B > +inline A mAlignToMultiple( A val, B mul ) +{ + A rem = val % mul; + return ( rem ? val + mul - rem : val ); +} + +//-------------------------------------- +inline F32 mDegToRad(F32 d) +{ + return((d * M_PI_F) / 180.0f); +} + +inline F32 mRadToDeg(F32 r) +{ + return((r * 180.0f) / M_PI_F); +} + +inline F64 mDegToRad(F64 d) +{ + return (d * M_PI) / 180.0; +} + +inline F64 mRadToDeg(F64 r) +{ + return (r * 180.0) / M_PI; +} + +//------------------------------------------------------------------------------ + +inline bool mIsNaN_F( const F32 x ) +{ + // If x is a floating point variable, then (x != x) will be TRUE if x has the value NaN. + // This is only going to work if the compiler is IEEE 748 compliant. + // + // Tested and working on VC2k5 + return ( x != x ); +} + +inline bool mIsInf_F( const F32 x ) +{ + return ( x == std::numeric_limits< float >::infinity() ); +} + +inline F32 mSign( const F32 n ) +{ + if ( n > 0.0f ) + return 1.0f; + if ( n < 0.0f ) + return -1.0f; + + return 0.0f; +} + +/// Returns the input value squared. +inline F32 mSquared( F32 n ) +{ + return n * n; +} + +/// @copydoc mSquaredF +inline F64 mSquared( F64 n ) +{ + return n * n; +} + + +#endif //_MMATHFN_H_ diff --git a/Engine/source/math/mMathSSE.cpp b/Engine/source/math/mMathSSE.cpp new file mode 100644 index 000000000..28194c51f --- /dev/null +++ b/Engine/source/math/mMathSSE.cpp @@ -0,0 +1,385 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "math/mMathFn.h" +#include "math/mPlane.h" +#include "math/mMatrix.h" + + +#if defined(TORQUE_SUPPORTS_VC_INLINE_X86_ASM) +#define ADD_SSE_FN +// inlined version here. +void SSE_MatrixF_x_MatrixF(const F32 *matA, const F32 *matB, F32 *result) +{ + __asm + { + mov edx, matA + mov ecx, matB + mov eax, result + + movss xmm0, [edx] + movups xmm1, [ecx] + shufps xmm0, xmm0, 0 + movss xmm2, [edx+4] + mulps xmm0, xmm1 + shufps xmm2, xmm2, 0 + movups xmm3, [ecx+10h] + movss xmm7, [edx+8] + mulps xmm2, xmm3 + shufps xmm7, xmm7, 0 + addps xmm0, xmm2 + movups xmm4, [ecx+20h] + movss xmm2, [edx+0Ch] + mulps xmm7, xmm4 + shufps xmm2, xmm2, 0 + addps xmm0, xmm7 + movups xmm5, [ecx+30h] + movss xmm6, [edx+10h] + mulps xmm2, xmm5 + movss xmm7, [edx+14h] + shufps xmm6, xmm6, 0 + addps xmm0, xmm2 + shufps xmm7, xmm7, 0 + movlps [eax], xmm0 + movhps [eax+8], xmm0 + mulps xmm7, xmm3 + movss xmm0, [edx+18h] + mulps xmm6, xmm1 + shufps xmm0, xmm0, 0 + addps xmm6, xmm7 + mulps xmm0, xmm4 + movss xmm2, [edx+24h] + addps xmm6, xmm0 + movss xmm0, [edx+1Ch] + movss xmm7, [edx+20h] + shufps xmm0, xmm0, 0 + shufps xmm7, xmm7, 0 + mulps xmm0, xmm5 + mulps xmm7, xmm1 + addps xmm6, xmm0 + shufps xmm2, xmm2, 0 + movlps [eax+10h], xmm6 + movhps [eax+18h], xmm6 + mulps xmm2, xmm3 + movss xmm6, [edx+28h] + addps xmm7, xmm2 + shufps xmm6, xmm6, 0 + movss xmm2, [edx+2Ch] + mulps xmm6, xmm4 + shufps xmm2, xmm2, 0 + addps xmm7, xmm6 + mulps xmm2, xmm5 + movss xmm0, [edx+34h] + addps xmm7, xmm2 + shufps xmm0, xmm0, 0 + movlps [eax+20h], xmm7 + movss xmm2, [edx+30h] + movhps [eax+28h], xmm7 + mulps xmm0, xmm3 + shufps xmm2, xmm2, 0 + movss xmm6, [edx+38h] + mulps xmm2, xmm1 + shufps xmm6, xmm6, 0 + addps xmm2, xmm0 + mulps xmm6, xmm4 + movss xmm7, [edx+3Ch] + shufps xmm7, xmm7, 0 + addps xmm2, xmm6 + mulps xmm7, xmm5 + addps xmm2, xmm7 + movups [eax+30h], xmm2 + } +} +void SSE_MatrixF_x_MatrixF_Aligned(const F32 *matA, const F32 *matB, F32 *result) +{ + __asm + { + mov edx, matA + mov ecx, matB + mov eax, result + + movss xmm0, [edx] + movaps xmm1, [ecx] + shufps xmm0, xmm0, 0 + movss xmm2, [edx+4] + mulps xmm0, xmm1 + shufps xmm2, xmm2, 0 + movaps xmm3, [ecx+10h] + movss xmm7, [edx+8] + mulps xmm2, xmm3 + shufps xmm7, xmm7, 0 + addps xmm0, xmm2 + movaps xmm4, [ecx+20h] + movss xmm2, [edx+0Ch] + mulps xmm7, xmm4 + shufps xmm2, xmm2, 0 + addps xmm0, xmm7 + movaps xmm5, [ecx+30h] + movss xmm6, [edx+10h] + mulps xmm2, xmm5 + movss xmm7, [edx+14h] + shufps xmm6, xmm6, 0 + addps xmm0, xmm2 + shufps xmm7, xmm7, 0 + movlps [eax], xmm0 + movhps [eax+8], xmm0 + mulps xmm7, xmm3 + movss xmm0, [edx+18h] + mulps xmm6, xmm1 + shufps xmm0, xmm0, 0 + addps xmm6, xmm7 + mulps xmm0, xmm4 + movss xmm2, [edx+24h] + addps xmm6, xmm0 + movss xmm0, [edx+1Ch] + movss xmm7, [edx+20h] + shufps xmm0, xmm0, 0 + shufps xmm7, xmm7, 0 + mulps xmm0, xmm5 + mulps xmm7, xmm1 + addps xmm6, xmm0 + shufps xmm2, xmm2, 0 + movlps [eax+10h], xmm6 + movhps [eax+18h], xmm6 + mulps xmm2, xmm3 + movss xmm6, [edx+28h] + addps xmm7, xmm2 + shufps xmm6, xmm6, 0 + movss xmm2, [edx+2Ch] + mulps xmm6, xmm4 + shufps xmm2, xmm2, 0 + addps xmm7, xmm6 + mulps xmm2, xmm5 + movss xmm0, [edx+34h] + addps xmm7, xmm2 + shufps xmm0, xmm0, 0 + movlps [eax+20h], xmm7 + movss xmm2, [edx+30h] + movhps [eax+28h], xmm7 + mulps xmm0, xmm3 + shufps xmm2, xmm2, 0 + movss xmm6, [edx+38h] + mulps xmm2, xmm1 + shufps xmm6, xmm6, 0 + addps xmm2, xmm0 + mulps xmm6, xmm4 + movss xmm7, [edx+3Ch] + shufps xmm7, xmm7, 0 + addps xmm2, xmm6 + mulps xmm7, xmm5 + addps xmm2, xmm7 + movaps [eax+30h], xmm2 + } +} +// if we set our flag, we always try to build the inlined asm. +// EXCEPT if we're in an old version of Codewarrior that can't handle SSE code. +// TODO: the NASM implementation of SSE_MatrixF_x_MatrixF_Aligned is missing, +// so we temporary disable this until fixed (needed for linux dedicated build) +//#elif defined(TORQUE_SUPPORTS_NASM) +#elif 0 +#define ADD_SSE_FN +extern "C" +{ + void SSE_MatrixF_x_MatrixF(const F32 *matA, const F32 *matB, F32 *result); + void SSE_MatrixF_x_MatrixF_Aligned(const F32 *matA, const F32 *matB, F32 *result); +} + +#elif defined( TORQUE_COMPILER_GCC ) && defined( TORQUE_CPU_X86 ) +#define ADD_SSE_FN + +void SSE_MatrixF_x_MatrixF(const F32 *matA, const F32 *matB, F32 *result) +{ + asm + ( + "movss (%%edx),%%xmm0\n" + "movups (%%ecx),%%xmm1\n" + "shufps $0,%%xmm0,%%xmm0\n" + "movss 4(%%edx),%%xmm2\n" + "mulps %%xmm1,%%xmm0\n" + "shufps $0,%%xmm2,%%xmm2\n" + "movups 0x10(%%ecx),%%xmm3\n" + "movss 8(%%edx),%%xmm7\n" + "mulps %%xmm3,%%xmm2\n" + "shufps $0,%%xmm7,%%xmm7\n" + "addps %%xmm2,%%xmm0\n" + "movups 0x20(%%ecx),%%xmm4\n" + "movss 0x0c(%%edx),%%xmm2\n" + "mulps %%xmm4,%%xmm7\n" + "shufps $0,%%xmm2,%%xmm2\n" + "addps %%xmm7,%%xmm0\n" + "movups 0x30(%%ecx),%%xmm5\n" + "movss 0x10(%%edx),%%xmm6\n" + "mulps %%xmm5,%%xmm2\n" + "movss 0x14(%%edx),%%xmm7\n" + "shufps $0,%%xmm6,%%xmm6\n" + "addps %%xmm2,%%xmm0\n" + "shufps $0,%%xmm7,%%xmm7\n" + "movlps %%xmm0,(%%eax)\n" + "movhps %%xmm0,8(%%eax)\n" + "mulps %%xmm3,%%xmm7\n" + "movss 0x18(%%edx),%%xmm0\n" + "mulps %%xmm1,%%xmm6\n" + "shufps $0,%%xmm0,%%xmm0\n" + "addps %%xmm7,%%xmm6\n" + "mulps %%xmm4,%%xmm0\n" + "movss 0x24(%%edx),%%xmm2\n" + "addps %%xmm0,%%xmm6\n" + "movss 0x1c(%%edx),%%xmm0\n" + "movss 0x20(%%edx),%%xmm7\n" + "shufps $0,%%xmm0,%%xmm0\n" + "shufps $0,%%xmm7,%%xmm7\n" + "mulps %%xmm5,%%xmm0\n" + "mulps %%xmm1,%%xmm7\n" + "addps %%xmm0,%%xmm6\n" + "shufps $0,%%xmm2,%%xmm2\n" + "movlps %%xmm6,0x10(%%eax)\n" + "movhps %%xmm6,0x18(%%eax)\n" + "mulps %%xmm3,%%xmm2\n" + "movss 0x28(%%edx),%%xmm6\n" + "addps %%xmm2,%%xmm7\n" + "shufps $0,%%xmm6,%%xmm6\n" + "movss 0x2c(%%edx),%%xmm2\n" + "mulps %%xmm4,%%xmm6\n" + "shufps $0,%%xmm2,%%xmm2\n" + "addps %%xmm6,%%xmm7\n" + "mulps %%xmm5,%%xmm2\n" + "movss 0x34(%%edx),%%xmm0\n" + "addps %%xmm2,%%xmm7\n" + "shufps $0,%%xmm0,%%xmm0\n" + "movlps %%xmm7,0x20(%%eax)\n" + "movss 0x30(%%edx),%%xmm2\n" + "movhps %%xmm7,0x28(%%eax)\n" + "mulps %%xmm3,%%xmm0\n" + "shufps $0,%%xmm2,%%xmm2\n" + "movss 0x38(%%edx),%%xmm6\n" + "mulps %%xmm1,%%xmm2\n" + "shufps $0,%%xmm6,%%xmm6\n" + "addps %%xmm0,%%xmm2\n" + "mulps %%xmm4,%%xmm6\n" + "movss 0x3c(%%edx),%%xmm7\n" + "shufps $0,%%xmm7,%%xmm7\n" + "addps %%xmm6,%%xmm2\n" + "mulps %%xmm5,%%xmm7\n" + "addps %%xmm7,%%xmm2\n" + "movups %%xmm2,0x30(%%eax)\n" + + : + : "d" ( matA ), + "c" ( matB ), + "a" ( result ) + ); +} + +void SSE_MatrixF_x_MatrixF_Aligned(const F32 *matA, const F32 *matB, F32 *result) +{ + asm + ( + "movss (%%edx),%%xmm0\n" + "movaps (%%ecx),%%xmm1\n" + "shufps $0,%%xmm0,%%xmm0\n" + "movss 4(%%edx),%%xmm2\n" + "mulps %%xmm1,%%xmm0\n" + "shufps $0,%%xmm2,%%xmm2\n" + "movaps 0x10(%%ecx),%%xmm3\n" + "movss 8(%%edx),%%xmm7\n" + "mulps %%xmm3,%%xmm2\n" + "shufps $0,%%xmm7,%%xmm7\n" + "addps %%xmm2,%%xmm0\n" + "movaps 0x20(%%ecx),%%xmm4\n" + "movss 0x0c(%%edx),%%xmm2\n" + "mulps %%xmm4,%%xmm7\n" + "shufps $0,%%xmm2,%%xmm2\n" + "addps %%xmm7,%%xmm0\n" + "movaps 0x30(%%ecx),%%xmm5\n" + "movss 0x10(%%edx),%%xmm6\n" + "mulps %%xmm5,%%xmm2\n" + "movss 0x14(%%edx),%%xmm7\n" + "shufps $0,%%xmm6,%%xmm6\n" + "addps %%xmm2,%%xmm0\n" + "shufps $0,%%xmm7,%%xmm7\n" + "movlps %%xmm0,(%%eax)\n" + "movhps %%xmm0,8(%%eax)\n" + "mulps %%xmm3,%%xmm7\n" + "movss 0x18(%%edx),%%xmm0\n" + "mulps %%xmm1,%%xmm6\n" + "shufps $0,%%xmm0,%%xmm0\n" + "addps %%xmm7,%%xmm6\n" + "mulps %%xmm4,%%xmm0\n" + "movss 0x24(%%edx),%%xmm2\n" + "addps %%xmm0,%%xmm6\n" + "movss 0x1c(%%edx),%%xmm0\n" + "movss 0x20(%%edx),%%xmm7\n" + "shufps $0,%%xmm0,%%xmm0\n" + "shufps $0,%%xmm7,%%xmm7\n" + "mulps %%xmm5,%%xmm0\n" + "mulps %%xmm1,%%xmm7\n" + "addps %%xmm0,%%xmm6\n" + "shufps $0,%%xmm2,%%xmm2\n" + "movlps %%xmm6,0x10(%%eax)\n" + "movhps %%xmm6,0x18(%%eax)\n" + "mulps %%xmm3,%%xmm2\n" + "movss 0x28(%%edx),%%xmm6\n" + "addps %%xmm2,%%xmm7\n" + "shufps $0,%%xmm6,%%xmm6\n" + "movss 0x2c(%%edx),%%xmm2\n" + "mulps %%xmm4,%%xmm6\n" + "shufps $0,%%xmm2,%%xmm2\n" + "addps %%xmm6,%%xmm7\n" + "mulps %%xmm5,%%xmm2\n" + "movss 0x34(%%edx),%%xmm0\n" + "addps %%xmm2,%%xmm7\n" + "shufps $0,%%xmm0,%%xmm0\n" + "movlps %%xmm7,0x20(%%eax)\n" + "movss 0x30(%%edx),%%xmm2\n" + "movhps %%xmm7,0x28(%%eax)\n" + "mulps %%xmm3,%%xmm0\n" + "shufps $0,%%xmm2,%%xmm2\n" + "movss 0x38(%%edx),%%xmm6\n" + "mulps %%xmm1,%%xmm2\n" + "shufps $0,%%xmm6,%%xmm6\n" + "addps %%xmm0,%%xmm2\n" + "mulps %%xmm4,%%xmm6\n" + "movss 0x3c(%%edx),%%xmm7\n" + "shufps $0,%%xmm7,%%xmm7\n" + "addps %%xmm6,%%xmm2\n" + "mulps %%xmm5,%%xmm7\n" + "addps %%xmm7,%%xmm2\n" + "movaps %%xmm2,0x30(%%eax)\n" + + : + : "d" ( matA ), + "c" ( matB ), + "a" ( result ) + ); +} + +#endif + +void mInstall_Library_SSE() +{ +#if defined(ADD_SSE_FN) + m_matF_x_matF = SSE_MatrixF_x_MatrixF; + m_matF_x_matF_aligned = SSE_MatrixF_x_MatrixF_Aligned; + // m_matF_x_point3F = Athlon_MatrixF_x_Point3F; + // m_matF_x_vectorF = Athlon_MatrixF_x_VectorF; +#endif +} diff --git a/Engine/source/math/mMathSSE_ASM.asm b/Engine/source/math/mMathSSE_ASM.asm new file mode 100644 index 000000000..bfede0404 --- /dev/null +++ b/Engine/source/math/mMathSSE_ASM.asm @@ -0,0 +1,128 @@ +;----------------------------------------------------------------------------- +; Copyright (c) 2012 GarageGames, LLC +; +; Permission is hereby granted, free of charge, to any person obtaining a copy +; of this software and associated documentation files (the "Software"), to +; deal in the Software without restriction, including without limitation the +; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +; sell copies of the Software, and to permit persons to whom the Software is +; furnished to do so, subject to the following conditions: +; +; The above copyright notice and this permission notice shall be included in +; all copies or substantial portions of the Software. +; +; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +; IN THE SOFTWARE. +;----------------------------------------------------------------------------- + + +segment .data + +matA dd 0 +result dd 0 +matB dd 0 + +segment .text + +%macro export_fn 1 + %ifidn __OUTPUT_FORMAT__, elf + ; No underscore needed for ELF object files + global %1 + %1: + %else + global _%1 + _%1: + %endif +%endmacro + + +%define arg(x) [esp+(x*4)] + + + +;void SSE_MatrixF_x_MatrixF(const F32 *matA, const F32 *matB, F32 *result) + +export_fn SSE_MatrixF_x_MatrixF + + mov edx, arg(1) + mov ecx, arg(2) + mov eax, arg(3) + + movss xmm0, [edx] + movups xmm1, [ecx] + shufps xmm0, xmm0, 0 + movss xmm2, [edx+4] + mulps xmm0, xmm1 + shufps xmm2, xmm2, 0 + movups xmm3, [ecx+10h] + movss xmm7, [edx+8] + mulps xmm2, xmm3 + shufps xmm7, xmm7, 0 + addps xmm0, xmm2 + movups xmm4, [ecx+20h] + movss xmm2, [edx+0Ch] + mulps xmm7, xmm4 + shufps xmm2, xmm2, 0 + addps xmm0, xmm7 + movups xmm5, [ecx+30h] + movss xmm6, [edx+10h] + mulps xmm2, xmm5 + movss xmm7, [edx+14h] + shufps xmm6, xmm6, 0 + addps xmm0, xmm2 + shufps xmm7, xmm7, 0 + movlps [eax], xmm0 + movhps [eax+8], xmm0 + mulps xmm7, xmm3 + movss xmm0, [edx+18h] + mulps xmm6, xmm1 + shufps xmm0, xmm0, 0 + addps xmm6, xmm7 + mulps xmm0, xmm4 + movss xmm2, [edx+24h] + addps xmm6, xmm0 + movss xmm0, [edx+1Ch] + movss xmm7, [edx+20h] + shufps xmm0, xmm0, 0 + shufps xmm7, xmm7, 0 + mulps xmm0, xmm5 + mulps xmm7, xmm1 + addps xmm6, xmm0 + shufps xmm2, xmm2, 0 + movlps [eax+10h], xmm6 + movhps [eax+18h], xmm6 + mulps xmm2, xmm3 + movss xmm6, [edx+28h] + addps xmm7, xmm2 + shufps xmm6, xmm6, 0 + movss xmm2, [edx+2Ch] + mulps xmm6, xmm4 + shufps xmm2, xmm2, 0 + addps xmm7, xmm6 + mulps xmm2, xmm5 + movss xmm0, [edx+34h] + addps xmm7, xmm2 + shufps xmm0, xmm0, 0 + movlps [eax+20h], xmm7 + movss xmm2, [edx+30h] + movhps [eax+28h], xmm7 + mulps xmm0, xmm3 + shufps xmm2, xmm2, 0 + movss xmm6, [edx+38h] + mulps xmm2, xmm1 + shufps xmm6, xmm6, 0 + addps xmm2, xmm0 + mulps xmm6, xmm4 + movss xmm7, [edx+3Ch] + shufps xmm7, xmm7, 0 + addps xmm2, xmm6 + mulps xmm7, xmm5 + addps xmm2, xmm7 + movups [eax+30h], xmm2 + + ret diff --git a/Engine/source/math/mMath_ASM.asm b/Engine/source/math/mMath_ASM.asm new file mode 100644 index 000000000..17ac12c44 --- /dev/null +++ b/Engine/source/math/mMath_ASM.asm @@ -0,0 +1,239 @@ +;----------------------------------------------------------------------------- +; Copyright (c) 2012 GarageGames, LLC +; +; Permission is hereby granted, free of charge, to any person obtaining a copy +; of this software and associated documentation files (the "Software"), to +; deal in the Software without restriction, including without limitation the +; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +; sell copies of the Software, and to permit persons to whom the Software is +; furnished to do so, subject to the following conditions: +; +; The above copyright notice and this permission notice shall be included in +; all copies or substantial portions of the Software. +; +; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +; IN THE SOFTWARE. +;----------------------------------------------------------------------------- + +; +; NASM version of optimized funcs in mMath_C +; + +; The following funcs are included: +; m_ceil_ASM, m_ceilD_ASM, m_floor_ASM, m_floorD_ASM +; m_fmod_ASM, m_fmodD_ASM, m_mulDivS32_ASM, m_mulDivU32_ASM +; m_sincos_ASM, m_sincosD_ASM + +; The other funcs from mMath_C were determined to compile into fast +; code using MSVC --Paul Bowman + + +segment .data + + +temp_int64 dq 0.0 +const_0pt5_D dq 0.4999999999995 +temp_int32 dd 0 +const_0pt5 dd 0.49999995 +const_neg1 dd -1.0 + + +segment .text + +%macro export_fn 1 + %ifidn __OUTPUT_FORMAT__, elf + ; No underscore needed for ELF object files + global %1 + %1: + %else + global _%1 + _%1: + %endif +%endmacro + +%define rnd_adjD qword [const_0pt5_D] +%define rnd_adj dword [const_0pt5] + + +%define val dword [esp+4] +%define val64 qword [esp+4] +; +; static F32 m_ceil_ASM(F32 val) +; +export_fn m_ceil_ASM + fld val + fadd rnd_adj + fistp qword [temp_int64] + fild qword [temp_int64] + ret + +; +; static F64 m_ceilD_ASM(F64 val64) +; +export_fn m_ceilD_ASM + fld val64 + fadd rnd_adjD + fistp qword [temp_int64] + fild qword [temp_int64] + ret + +; +; static F32 m_floor_ASM(F32 val) +; +export_fn m_floor_ASM + fld val + fsub rnd_adj + fistp qword [temp_int64] + fild qword [temp_int64] + ret + + +; +; static F32 m_floorD_ASM( F64 val64 ) +; +export_fn m_floorD_ASM + fld val64 + fsub rnd_adjD + fistp qword [temp_int64] + fild qword [temp_int64] + ret + + + +%define arg_a dword [esp+4] +%define arg_b dword [esp+8] +%define arg_c dword [esp+12] + +; +; static S32 m_mulDivS32_ASM( S32 a, S32 b, S32 c ) +; +; // Note: this returns different (but correct) values than the C +; // version. C code must be overflowing...returns -727 +; // if a b and c are 1 million, for instance. This version returns +; // 1 million. +; return (S32) ((S64)a*(S64)b) / (S64)c; +; +export_fn m_mulDivS32_ASM + mov eax, arg_a + imul arg_b + idiv arg_c + ret + +; +; static U32 m_mulDivU32_ASM( U32 a, U32 b, U32 c ) +; +; // Note: again, C version overflows +; +export_fn m_mulDivU32_ASM + mov eax, arg_a + mul arg_b + div arg_c + ret + + + +; val is already defined above to be esp+4 +%define modulo dword [esp+8] + + +; +; static F32 m_fmod_ASM(F32 val, F32 modulo) +; +export_fn m_fmod_ASM + mov eax, val + fld modulo + fabs + fld val + fabs + fdiv st0, st1 + fld st0 + fsub rnd_adj + fistp qword [temp_int64] + fild qword [temp_int64] + fsubp st1, st0 + fmulp st1, st0 + +; // sign bit can be read as integer high bit, +; // as long as # isn't 0x80000000 + cmp eax, 0x80000000 + jbe notneg + + fmul dword [const_neg1] + +notneg: + ret + + +%define val64hi dword [esp+8] +%define val64 qword [esp+4] +%define modulo64 qword [esp+12] + +; +; static F32 m_fmodD_ASM(F64 val, F64 modulo) +; +export_fn m_fmodD_ASM + mov eax, val64hi + fld modulo64 + fabs + fld val64 + fabs + fdiv st0, st1 + fld st0 + fsub rnd_adjD + fistp qword [temp_int64] + fild qword [temp_int64] + fsubp st1, st0 + fmulp st1, st0 + +; // sign bit can be read as integer high bit, +; // as long as # isn't 0x80000000 + cmp eax, 0x80000000 + jbe notnegD + + fmul dword [const_neg1] + +notnegD: + ret + + + +%define angle dword [esp+4] +%define res_sin dword [esp+8] +%define res_cos dword [esp+12] + +; +;static void m_sincos_ASM( F32 angle, F32 *s, F32 *c ) +; +export_fn m_sincos_ASM + mov eax, res_cos + fld angle + fsincos + fstp dword [eax] + mov eax, res_sin + fstp dword [eax] + ret + + + +%define angle64 qword [esp+4] +%define res_sin64 dword [esp+12] +%define res_cos64 dword [esp+16] +; +;static void m_sincosD_ASM( F64 angle, F64 *s, F64 *c ) +; +export_fn m_sincosD_ASM + mov eax, res_cos64 + fld angle64 + fsincos + fstp qword [eax] + mov eax, res_sin64 + fstp qword [eax] + ret + + + diff --git a/Engine/source/math/mMath_C.cpp b/Engine/source/math/mMath_C.cpp new file mode 100644 index 000000000..7fac832f9 --- /dev/null +++ b/Engine/source/math/mMath_C.cpp @@ -0,0 +1,953 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "math/mMath.h" +#include "math/util/frustum.h" +#include // Caution!!! Possible platform specific include + + +//------------------------------------------------------------------------------ +// C version of Math Library + +// asm externals +extern "C" { + + S32 m_mulDivS32_ASM( S32 a, S32 b, S32 c ); + U32 m_mulDivU32_ASM( U32 a, U32 b, U32 c ); + F32 m_fmod_ASM(F32 val, F32 modulo); + F32 m_fmodD_ASM(F64 val, F64 modulo); + void m_sincos_ASM( F32 angle, F32 *s, F32 *c ); + void m_sincosD_ASM( F64 angle, F64 *s, F64 *c ); + +} +//-------------------------------------- + +static S32 m_mulDivS32_C(S32 a, S32 b, S32 c) +{ + // S64/U64 support in most 32-bit compilers generate + // horrible code, the C version are here just for porting + // assembly implementation is recommended + return (S32) ((S64)a*(S64)b) / (S64)c; +} + +static U32 m_mulDivU32_C(S32 a, S32 b, U32 c) +{ + return (U32) ((S64)a*(S64)b) / (U64)c; +} + + +//-------------------------------------- +static F32 m_catmullrom_C(F32 t, F32 p0, F32 p1, F32 p2, F32 p3) +{ + return 0.5f * ((3.0f*p1 - 3.0f*p2 + p3 - p0)*t*t*t + + (2.0f*p0 - 5.0f*p1 + 4.0f*p2 - p3)*t*t + + (p2-p0)*t + + 2.0f*p1); +} + +//-------------------------------------- +static void m_sincos_C( F32 angle, F32 *s, F32 *c ) +{ + *s = mSin( angle ); + *c = mCos( angle ); +} + +static void m_sincosD_C( F64 angle, F64 *s, F64 *c ) +{ + *s = mSin( angle ); + *c = mCos( angle ); +} + +//-------------------------------------- +static void m_point2F_normalize_C(F32 *p) +{ + F32 factor = 1.0f / mSqrt(p[0]*p[0] + p[1]*p[1] ); + p[0] *= factor; + p[1] *= factor; +} + +//-------------------------------------- +static void m_point2F_normalize_f_C(F32 *p, F32 val) +{ + F32 factor = val / mSqrt(p[0]*p[0] + p[1]*p[1] ); + p[0] *= factor; + p[1] *= factor; +} + +//-------------------------------------- +static void m_point2D_normalize_C(F64 *p) +{ + F64 factor = 1.0f / mSqrtD(p[0]*p[0] + p[1]*p[1] ); + p[0] *= factor; + p[1] *= factor; +} + +//-------------------------------------- +static void m_point2D_normalize_f_C(F64 *p, F64 val) +{ + F64 factor = val / mSqrtD(p[0]*p[0] + p[1]*p[1] ); + p[0] *= factor; + p[1] *= factor; +} + +//-------------------------------------- +static void m_point3D_normalize_f_C(F64 *p, F64 val) +{ + F64 factor = val / mSqrtD(p[0]*p[0] + p[1]*p[1] + p[2]*p[2]); + p[0] *= factor; + p[1] *= factor; + p[2] *= factor; +} + +//-------------------------------------- +static void m_point3F_normalize_C(F32 *p) +{ + F32 squared = p[0]*p[0] + p[1]*p[1] + p[2]*p[2]; + // This can happen in Container::castRay -> ForceFieldBare::castRay + //AssertFatal(squared != 0.0, "Error, zero length vector normalized!"); + if (squared != 0.0f) { + F32 factor = 1.0f / mSqrt(squared); + p[0] *= factor; + p[1] *= factor; + p[2] *= factor; + } else { + p[0] = 0.0f; + p[1] = 0.0f; + p[2] = 1.0f; + } +} + +//-------------------------------------- +static void m_point3F_normalize_f_C(F32 *p, F32 val) +{ + F32 factor = val / mSqrt(p[0]*p[0] + p[1]*p[1] + p[2]*p[2] ); + p[0] *= factor; + p[1] *= factor; + p[2] *= factor; +} + +//-------------------------------------- +static void m_point3F_interpolate_C(const F32 *from, const F32 *to, F32 factor, F32 *result ) +{ +#ifdef TORQUE_COMPILER_GCC +// remove possibility of aliases + const F32 inverse = 1.0f - factor; + const F32 from0 = from[0], from1 = from[1], from2 = from[2]; + const F32 to0 = to[0], to1 = to[1], to2 = to[2]; + + result[0] = from0 * inverse + to0 * factor; + result[1] = from1 * inverse + to1 * factor; + result[2] = from2 * inverse + to2 * factor; +#else + F32 inverse = 1.0f - factor; + result[0] = from[0] * inverse + to[0] * factor; + result[1] = from[1] * inverse + to[1] * factor; + result[2] = from[2] * inverse + to[2] * factor; +#endif +} + +//-------------------------------------- +static void m_point3D_normalize_C(F64 *p) +{ + F64 factor = 1.0f / mSqrtD(p[0]*p[0] + p[1]*p[1] + p[2]*p[2] ); + p[0] *= factor; + p[1] *= factor; + p[2] *= factor; +} + + +//-------------------------------------- +static void m_point3D_interpolate_C(const F64 *from, const F64 *to, F64 factor, F64 *result ) +{ +#ifdef TORQUE_COMPILER_GCC +// remove possibility of aliases + const F64 inverse = 1.0f - factor; + const F64 from0 = from[0], from1 = from[1], from2 = from[2]; + const F64 to0 = to[0], to1 = to[1], to2 = to[2]; + + result[0] = from0 * inverse + to0 * factor; + result[1] = from1 * inverse + to1 * factor; + result[2] = from2 * inverse + to2 * factor; +#else + F64 inverse = 1.0f - factor; + result[0] = from[0] * inverse + to[0] * factor; + result[1] = from[1] * inverse + to[1] * factor; + result[2] = from[2] * inverse + to[2] * factor; +#endif +} + + +static void m_quatF_set_matF_C( F32 x, F32 y, F32 z, F32 w, F32* m ) +{ +#define qidx(r,c) (r*4 + c) + F32 xs = x * 2.0f; + F32 ys = y * 2.0f; + F32 zs = z * 2.0f; + F32 wx = w * xs; + F32 wy = w * ys; + F32 wz = w * zs; + F32 xx = x * xs; + F32 xy = x * ys; + F32 xz = x * zs; + F32 yy = y * ys; + F32 yz = y * zs; + F32 zz = z * zs; + m[qidx(0,0)] = 1.0f - (yy + zz); + m[qidx(1,0)] = xy - wz; + m[qidx(2,0)] = xz + wy; + m[qidx(3,0)] = 0.0f; + m[qidx(0,1)] = xy + wz; + m[qidx(1,1)] = 1.0f - (xx + zz); + m[qidx(2,1)] = yz - wx; + m[qidx(3,1)] = 0.0f; + m[qidx(0,2)] = xz - wy; + m[qidx(1,2)] = yz + wx; + m[qidx(2,2)] = 1.0f - (xx + yy); + m[qidx(3,2)] = 0.0f; + + m[qidx(0,3)] = 0.0f; + m[qidx(1,3)] = 0.0f; + m[qidx(2,3)] = 0.0f; + m[qidx(3,3)] = 1.0f; +#undef qidx +} + + +//-------------------------------------- +static void m_matF_set_euler_C(const F32 *e, F32 *result) +{ + enum { + AXIS_X = (1<<0), + AXIS_Y = (1<<1), + AXIS_Z = (1<<2) + }; + + U32 axis = 0; + if (e[0] != 0.0f) axis |= AXIS_X; + if (e[1] != 0.0f) axis |= AXIS_Y; + if (e[2] != 0.0f) axis |= AXIS_Z; + + switch (axis) + { + case 0: + m_matF_identity(result); + break; + + case AXIS_X: + { + F32 cx,sx; + mSinCos( e[0], sx, cx ); + + result[0] = 1.0f; + result[1] = 0.0f; + result[2] = 0.0f; + result[3] = 0.0f; + + result[4] = 0.0f; + result[5] = cx; + result[6] = sx; + result[7] = 0.0f; + + result[8] = 0.0f; + result[9] = -sx; + result[10]= cx; + result[11]= 0.0f; + + result[12]= 0.0f; + result[13]= 0.0f; + result[14]= 0.0f; + result[15]= 1.0f; + break; + } + + case AXIS_Y: + { + F32 cy,sy; + mSinCos( e[1], sy, cy ); + + result[0] = cy; + result[1] = 0.0f; + result[2] = -sy; + result[3] = 0.0f; + + result[4] = 0.0f; + result[5] = 1.0f; + result[6] = 0.0f; + result[7] = 0.0f; + + result[8] = sy; + result[9] = 0.0f; + result[10]= cy; + result[11]= 0.0f; + + result[12]= 0.0f; + result[13]= 0.0f; + result[14]= 0.0f; + result[15]= 1.0f; + break; + } + + case AXIS_Z: + { + // the matrix looks like this: + // r1 - (r4 * sin(x)) r2 + (r3 * sin(x)) -cos(x) * sin(y) + // -cos(x) * sin(z) cos(x) * cos(z) sin(x) + // r3 + (r2 * sin(x)) r4 - (r1 * sin(x)) cos(x) * cos(y) + // + // where: + // r1 = cos(y) * cos(z) + // r2 = cos(y) * sin(z) + // r3 = sin(y) * cos(z) + // r4 = sin(y) * sin(z) + F32 cz,sz; + mSinCos( e[2], sz, cz ); + + result[0] = cz; + result[1] = sz; + result[2] = 0.0f; + result[3] = 0.0f; + + result[4] = -sz; + result[5] = cz; + result[6] = 0.0f; + result[7] = 0.0f; + + result[8] = 0.0f; + result[9] = 0.0f; + result[10]= 1.0f; + result[11]= 0.0f; + + result[12]= 0.0f; + result[13]= 0.0f; + result[14]= 0.0f; + result[15]= 1.0f; + break; + } + + default: + // the matrix looks like this: + // r1 - (r4 * sin(x)) r2 + (r3 * sin(x)) -cos(x) * sin(y) + // -cos(x) * sin(z) cos(x) * cos(z) sin(x) + // r3 + (r2 * sin(x)) r4 - (r1 * sin(x)) cos(x) * cos(y) + // + // where: + // r1 = cos(y) * cos(z) + // r2 = cos(y) * sin(z) + // r3 = sin(y) * cos(z) + // r4 = sin(y) * sin(z) + F32 cx,sx; + mSinCos( e[0], sx, cx ); + F32 cy,sy; + mSinCos( e[1], sy, cy ); + F32 cz,sz; + mSinCos( e[2], sz, cz ); + F32 r1 = cy * cz; + F32 r2 = cy * sz; + F32 r3 = sy * cz; + F32 r4 = sy * sz; + + result[0] = r1 - (r4 * sx); + result[1] = r2 + (r3 * sx); + result[2] = -cx * sy; + result[3] = 0.0f; + + result[4] = -cx * sz; + result[5] = cx * cz; + result[6] = sx; + result[7] = 0.0f; + + result[8] = r3 + (r2 * sx); + result[9] = r4 - (r1 * sx); + result[10]= cx * cy; + result[11]= 0.0f; + + result[12]= 0.0f; + result[13]= 0.0f; + result[14]= 0.0f; + result[15]= 1.0f; + break; + } +} + + +//-------------------------------------- +static void m_matF_set_euler_point_C(const F32 *e, const F32 *p, F32 *result) +{ + m_matF_set_euler(e, result); + result[3] = p[0]; + result[7] = p[1]; + result[11]= p[2]; +} + +//-------------------------------------- +static void m_matF_identity_C(F32 *m) +{ + *m++ = 1.0f; + *m++ = 0.0f; + *m++ = 0.0f; + *m++ = 0.0f; + + *m++ = 0.0f; + *m++ = 1.0f; + *m++ = 0.0f; + *m++ = 0.0f; + + *m++ = 0.0f; + *m++ = 0.0f; + *m++ = 1.0f; + *m++ = 0.0f; + + *m++ = 0.0f; + *m++ = 0.0f; + *m++ = 0.0f; + *m = 1.0f; +} + +#if 0 +// Compile this out till we hook it up. It's a more efficient matrix +// inverse than what we have (it uses intermediate results of determinant +// to same about 1/4 of the operations. +static void affineInvertTo(const F32 * m, F32 * out) +{ +#define idx(r,c) (r*4 + c) + F32 d1 = m[idx(2,2)] * m[idx(1,1)] - m[idx(2,1)] * m[idx(1,2)]; + F32 d2 = m[idx(2,0)] * m[idx(1,2)] - m[idx(2,2)] * m[idx(1,0)]; + F32 d3 = m[idx(2,1)] * m[idx(1,0)] - m[idx(2,0)] * m[idx(1,1)]; + + F32 invDet = 1.0f / (m[idx(0,0)] * d1 + m[idx(0,1)] * d2 + m[idx(0,2)] * d3); + + F32 m00 = m[idx(0,0)] * invDet; + F32 m01 = m[idx(0,1)] * invDet; + F32 m02 = m[idx(0,2)] * invDet; + + F32 * result = out; + *out++ = d1 * invDet; + *out++ = m02 * m[idx(2,1)] - m01 * m[idx(2,2)]; + *out++ = m01 * m[idx(1,2)] - m02 * m[idx(1,1)]; + *out++ = 0.0f; + + *out++ = d2 * invDet; + *out++ = m00 * m[idx(2,2)] - m02 * m[idx(2,0)]; + *out++ = m02 * m[idx(1,0)] - m00 * m[idx(1,2)]; + *out++ = 0.0f; + + *out++ = d3 * invDet; + *out++ = m01 * m[idx(2,0)] - m00 * m[idx(2,1)]; + *out++ = m00 * m[idx(1,1)] - m01 * m[idx(1,0)]; + *out++ = 0.0f; + + *out++ = -result[idx(0,0)] * m[idx(0,3)] - result[idx(0,1)] * m[idx(1,3)] - result[idx(0,2)] * m[idx(2,3)]; + *out++ = -result[idx(1,0)] * m[idx(0,3)] - result[idx(1,1)] * m[idx(1,3)] - result[idx(1,2)] * m[idx(2,3)]; + *out++ = -result[idx(2,0)] * m[idx(0,3)] - result[idx(2,1)] * m[idx(1,3)] - result[idx(2,2)] * m[idx(2,3)]; + *out++ = 1.0f; +#undef idx +} +#endif + +//-------------------------------------- +static void m_matF_inverse_C(F32 *m) +{ + // using Cramers Rule find the Inverse + // Minv = (1/det(M)) * adjoint(M) + F32 det = m_matF_determinant( m ); + AssertFatal( det != 0.0f, "MatrixF::inverse: non-singular matrix, no inverse."); + + F32 invDet = 1.0f/det; + F32 temp[16]; + + temp[0] = (m[5] * m[10]- m[6] * m[9]) * invDet; + temp[1] = (m[9] * m[2] - m[10]* m[1]) * invDet; + temp[2] = (m[1] * m[6] - m[2] * m[5]) * invDet; + + temp[4] = (m[6] * m[8] - m[4] * m[10])* invDet; + temp[5] = (m[10]* m[0] - m[8] * m[2]) * invDet; + temp[6] = (m[2] * m[4] - m[0] * m[6]) * invDet; + + temp[8] = (m[4] * m[9] - m[5] * m[8]) * invDet; + temp[9] = (m[8] * m[1] - m[9] * m[0]) * invDet; + temp[10]= (m[0] * m[5] - m[1] * m[4]) * invDet; + + m[0] = temp[0]; + m[1] = temp[1]; + m[2] = temp[2]; + + m[4] = temp[4]; + m[5] = temp[5]; + m[6] = temp[6]; + + m[8] = temp[8]; + m[9] = temp[9]; + m[10] = temp[10]; + + // invert the translation + temp[0] = -m[3]; + temp[1] = -m[7]; + temp[2] = -m[11]; + m_matF_x_vectorF(m, temp, &temp[4]); + m[3] = temp[4]; + m[7] = temp[5]; + m[11]= temp[6]; +} + +static void m_matF_invert_to_C(const F32 *m, F32 *d) +{ + // using Cramers Rule find the Inverse + // Minv = (1/det(M)) * adjoint(M) + F32 det = m_matF_determinant( m ); + AssertFatal( det != 0.0f, "MatrixF::inverse: non-singular matrix, no inverse."); + + F32 invDet = 1.0f/det; + + d[0] = (m[5] * m[10]- m[6] * m[9]) * invDet; + d[1] = (m[9] * m[2] - m[10]* m[1]) * invDet; + d[2] = (m[1] * m[6] - m[2] * m[5]) * invDet; + + d[4] = (m[6] * m[8] - m[4] * m[10])* invDet; + d[5] = (m[10]* m[0] - m[8] * m[2]) * invDet; + d[6] = (m[2] * m[4] - m[0] * m[6]) * invDet; + + d[8] = (m[4] * m[9] - m[5] * m[8]) * invDet; + d[9] = (m[8] * m[1] - m[9] * m[0]) * invDet; + d[10]= (m[0] * m[5] - m[1] * m[4]) * invDet; + + // invert the translation + F32 temp[6]; + temp[0] = -m[3]; + temp[1] = -m[7]; + temp[2] = -m[11]; + m_matF_x_vectorF(d, temp, &temp[3]); + d[3] = temp[3]; + d[7] = temp[4]; + d[11]= temp[5]; + d[ 12 ] = m[ 12 ]; + d[ 13 ] = m[ 13 ]; + d[ 14 ] = m[ 14 ]; + d[ 15 ] = m[ 15 ]; +} + +//-------------------------------------- +static void m_matF_affineInverse_C(F32 *m) +{ + // Matrix class checks to make sure this is an affine transform before calling + // this function, so we can proceed assuming it is... + F32 temp[16]; + dMemcpy(temp, m, 16 * sizeof(F32)); + + // Transpose rotation + m[1] = temp[4]; + m[4] = temp[1]; + m[2] = temp[8]; + m[8] = temp[2]; + m[6] = temp[9]; + m[9] = temp[6]; + + m[3] = -(temp[0]*temp[3] + temp[4]*temp[7] + temp[8]*temp[11]); + m[7] = -(temp[1]*temp[3] + temp[5]*temp[7] + temp[9]*temp[11]); + m[11] = -(temp[2]*temp[3] + temp[6]*temp[7] + temp[10]*temp[11]); +} + +inline void swap(F32 &a, F32 &b) +{ + F32 temp = a; + a = b; + b = temp; +} + +//-------------------------------------- +static void m_matF_transpose_C(F32 *m) +{ + swap(m[1], m[4]); + swap(m[2], m[8]); + swap(m[3], m[12]); + swap(m[6], m[9]); + swap(m[7], m[13]); + swap(m[11],m[14]); +} + +//-------------------------------------- +static void m_matF_scale_C(F32 *m,const F32 *p) +{ + // Note, doesn't allow scaling w... + + m[0] *= p[0]; m[1] *= p[1]; m[2] *= p[2]; + m[4] *= p[0]; m[5] *= p[1]; m[6] *= p[2]; + m[8] *= p[0]; m[9] *= p[1]; m[10] *= p[2]; + m[12] *= p[0]; m[13] *= p[1]; m[14] *= p[2]; +} + +//-------------------------------------- +static void m_matF_normalize_C(F32 *m) +{ + F32 col0[3], col1[3], col2[3]; + // extract columns 0 and 1 + col0[0] = m[0]; + col0[1] = m[4]; + col0[2] = m[8]; + + col1[0] = m[1]; + col1[1] = m[5]; + col1[2] = m[9]; + + // assure their relationships to one another + mCross(*(Point3F*)col0, *(Point3F*)col1, (Point3F*)col2); + mCross(*(Point3F*)col2, *(Point3F*)col0, (Point3F*)col1); + + // assure their length is 1.0f + m_point3F_normalize( col0 ); + m_point3F_normalize( col1 ); + m_point3F_normalize( col2 ); + + // store the normalized columns + m[0] = col0[0]; + m[4] = col0[1]; + m[8] = col0[2]; + + m[1] = col1[0]; + m[5] = col1[1]; + m[9] = col1[2]; + + m[2] = col2[0]; + m[6] = col2[1]; + m[10]= col2[2]; +} + + +//-------------------------------------- +static F32 m_matF_determinant_C(const F32 *m) +{ + return m[0] * (m[5] * m[10] - m[6] * m[9]) + + m[4] * (m[2] * m[9] - m[1] * m[10]) + + m[8] * (m[1] * m[6] - m[2] * m[5]) ; +} + + +//-------------------------------------- +// Removed static in order to write benchmarking code (that compares against +// specialized SSE/AMD versions) elsewhere. +void default_matF_x_matF_C(const F32 *a, const F32 *b, F32 *mresult) +{ + mresult[0] = a[0]*b[0] + a[1]*b[4] + a[2]*b[8] + a[3]*b[12]; + mresult[1] = a[0]*b[1] + a[1]*b[5] + a[2]*b[9] + a[3]*b[13]; + mresult[2] = a[0]*b[2] + a[1]*b[6] + a[2]*b[10] + a[3]*b[14]; + mresult[3] = a[0]*b[3] + a[1]*b[7] + a[2]*b[11] + a[3]*b[15]; + + mresult[4] = a[4]*b[0] + a[5]*b[4] + a[6]*b[8] + a[7]*b[12]; + mresult[5] = a[4]*b[1] + a[5]*b[5] + a[6]*b[9] + a[7]*b[13]; + mresult[6] = a[4]*b[2] + a[5]*b[6] + a[6]*b[10] + a[7]*b[14]; + mresult[7] = a[4]*b[3] + a[5]*b[7] + a[6]*b[11] + a[7]*b[15]; + + mresult[8] = a[8]*b[0] + a[9]*b[4] + a[10]*b[8] + a[11]*b[12]; + mresult[9] = a[8]*b[1] + a[9]*b[5] + a[10]*b[9] + a[11]*b[13]; + mresult[10]= a[8]*b[2] + a[9]*b[6] + a[10]*b[10]+ a[11]*b[14]; + mresult[11]= a[8]*b[3] + a[9]*b[7] + a[10]*b[11]+ a[11]*b[15]; + + mresult[12]= a[12]*b[0]+ a[13]*b[4]+ a[14]*b[8] + a[15]*b[12]; + mresult[13]= a[12]*b[1]+ a[13]*b[5]+ a[14]*b[9] + a[15]*b[13]; + mresult[14]= a[12]*b[2]+ a[13]*b[6]+ a[14]*b[10]+ a[15]*b[14]; + mresult[15]= a[12]*b[3]+ a[13]*b[7]+ a[14]*b[11]+ a[15]*b[15]; +} + + +// //-------------------------------------- +// static void m_matF_x_point3F_C(const F32 *m, const F32 *p, F32 *presult) +// { +// AssertFatal(p != presult, "Error, aliasing matrix mul pointers not allowed here!"); +// presult[0] = m[0]*p[0] + m[1]*p[1] + m[2]*p[2] + m[3]; +// presult[1] = m[4]*p[0] + m[5]*p[1] + m[6]*p[2] + m[7]; +// presult[2] = m[8]*p[0] + m[9]*p[1] + m[10]*p[2] + m[11]; +// } + + +// //-------------------------------------- +// static void m_matF_x_vectorF_C(const F32 *m, const F32 *v, F32 *vresult) +// { +// AssertFatal(v != vresult, "Error, aliasing matrix mul pointers not allowed here!"); +// vresult[0] = m[0]*v[0] + m[1]*v[1] + m[2]*v[2]; +// vresult[1] = m[4]*v[0] + m[5]*v[1] + m[6]*v[2]; +// vresult[2] = m[8]*v[0] + m[9]*v[1] + m[10]*v[2]; +// } + + +//-------------------------------------- +static void m_matF_x_point4F_C(const F32 *m, const F32 *p, F32 *presult) +{ + AssertFatal(p != presult, "Error, aliasing matrix mul pointers not allowed here!"); + presult[0] = m[0]*p[0] + m[1]*p[1] + m[2]*p[2] + m[3]*p[3]; + presult[1] = m[4]*p[0] + m[5]*p[1] + m[6]*p[2] + m[7]*p[3]; + presult[2] = m[8]*p[0] + m[9]*p[1] + m[10]*p[2] + m[11]*p[3]; + presult[3] = m[12]*p[0]+ m[13]*p[1]+ m[14]*p[2] + m[15]*p[3]; +} + +//-------------------------------------- +static void m_matF_x_scale_x_planeF_C(const F32* m, const F32* s, const F32* p, F32* presult) +{ + // We take in a matrix, a scale factor, and a plane equation. We want to output + // the resultant normal + // We have T = m*s + // To multiply the normal, we want Inv(Tr(m*s)) + // Inv(Tr(ms)) = Inv(Tr(s) * Tr(m)) + // = Inv(Tr(m)) * Inv(Tr(s)) + // + // Inv(Tr(s)) = Inv(s) = [ 1/x 0 0 0] + // [ 0 1/y 0 0] + // [ 0 0 1/z 0] + // [ 0 0 0 1] + // + // Since m is an affine matrix, + // Tr(m) = [ [ ] 0 ] + // [ [ R ] 0 ] + // [ [ ] 0 ] + // [ [ x y z ] 1 ] + // + // Inv(Tr(m)) = [ [ -1 ] 0 ] + // [ [ R ] 0 ] + // [ [ ] 0 ] + // [ [ A B C ] 1 ] + // Where: + // + // P = (x, y, z) + // A = -(Row(0, r) * P); + // B = -(Row(1, r) * P); + // C = -(Row(2, r) * P); + + MatrixF invScale(true); + F32* pScaleElems = invScale; + pScaleElems[MatrixF::idx(0, 0)] = 1.0f / s[0]; + pScaleElems[MatrixF::idx(1, 1)] = 1.0f / s[1]; + pScaleElems[MatrixF::idx(2, 2)] = 1.0f / s[2]; + + const Point3F shear( m[MatrixF::idx(3, 0)], m[MatrixF::idx(3, 1)], m[MatrixF::idx(3, 2)] ); + + const Point3F row0(m[MatrixF::idx(0, 0)], m[MatrixF::idx(0, 1)], m[MatrixF::idx(0, 2)]); + const Point3F row1(m[MatrixF::idx(1, 0)], m[MatrixF::idx(1, 1)], m[MatrixF::idx(1, 2)]); + const Point3F row2(m[MatrixF::idx(2, 0)], m[MatrixF::idx(2, 1)], m[MatrixF::idx(2, 2)]); + + const F32 A = -mDot(row0, shear); + const F32 B = -mDot(row1, shear); + const F32 C = -mDot(row2, shear); + + MatrixF invTrMatrix(true); + F32* destMat = invTrMatrix; + destMat[MatrixF::idx(0, 0)] = m[MatrixF::idx(0, 0)]; + destMat[MatrixF::idx(1, 0)] = m[MatrixF::idx(1, 0)]; + destMat[MatrixF::idx(2, 0)] = m[MatrixF::idx(2, 0)]; + destMat[MatrixF::idx(0, 1)] = m[MatrixF::idx(0, 1)]; + destMat[MatrixF::idx(1, 1)] = m[MatrixF::idx(1, 1)]; + destMat[MatrixF::idx(2, 1)] = m[MatrixF::idx(2, 1)]; + destMat[MatrixF::idx(0, 2)] = m[MatrixF::idx(0, 2)]; + destMat[MatrixF::idx(1, 2)] = m[MatrixF::idx(1, 2)]; + destMat[MatrixF::idx(2, 2)] = m[MatrixF::idx(2, 2)]; + destMat[MatrixF::idx(0, 3)] = A; + destMat[MatrixF::idx(1, 3)] = B; + destMat[MatrixF::idx(2, 3)] = C; + invTrMatrix.mul(invScale); + + Point3F norm(p[0], p[1], p[2]); + Point3F point = norm * -p[3]; + invTrMatrix.mulP(norm); + norm.normalize(); + + MatrixF temp; + dMemcpy(temp, m, sizeof(F32) * 16); + point.x *= s[0]; + point.y *= s[1]; + point.z *= s[2]; + temp.mulP(point); + + PlaneF resultPlane(point, norm); + presult[0] = resultPlane.x; + presult[1] = resultPlane.y; + presult[2] = resultPlane.z; + presult[3] = resultPlane.d; +} + +static void m_matF_x_box3F_C(const F32 *m, F32* min, F32* max) +{ + // Algorithm for axis aligned bounding box adapted from + // Graphic Gems I, pp 548-550 + // + F32 originalMin[3]; + F32 originalMax[3]; + originalMin[0] = min[0]; + originalMin[1] = min[1]; + originalMin[2] = min[2]; + originalMax[0] = max[0]; + originalMax[1] = max[1]; + originalMax[2] = max[2]; + + min[0] = max[0] = m[3]; + min[1] = max[1] = m[7]; + min[2] = max[2] = m[11]; + + const F32 * row = &m[0]; + for (U32 i = 0; i < 3; i++) + { + #define Do_One_Row(j) { \ + F32 a = (row[j] * originalMin[j]); \ + F32 b = (row[j] * originalMax[j]); \ + if (a < b) { *min += a; *max += b; } \ + else { *min += b; *max += a; } } + + // Simpler addressing (avoiding things like [ecx+edi*4]) might be worthwhile (LH): + Do_One_Row(0); + Do_One_Row(1); + Do_One_Row(2); + row += 4; + min++; + max++; + } +} + + +void m_point3F_bulk_dot_C(const F32* refVector, + const F32* dotPoints, + const U32 numPoints, + const U32 pointStride, + F32* output) +{ + for (U32 i = 0; i < numPoints; i++) + { + F32* pPoint = (F32*)(((U8*)dotPoints) + (pointStride * i)); + output[i] = ((refVector[0] * pPoint[0]) + + (refVector[1] * pPoint[1]) + + (refVector[2] * pPoint[2])); + } +} + +void m_point3F_bulk_dot_indexed_C(const F32* refVector, + const F32* dotPoints, + const U32 numPoints, + const U32 pointStride, + const U32* pointIndices, + F32* output) +{ + for (U32 i = 0; i < numPoints; i++) + { + F32* pPoint = (F32*)(((U8*)dotPoints) + (pointStride * pointIndices[i])); + output[i] = ((refVector[0] * pPoint[0]) + + (refVector[1] * pPoint[1]) + + (refVector[2] * pPoint[2])); + } +} + +//------------------------------------------------------------------------------ +// Math function pointer declarations + +S32 (*m_mulDivS32)(S32 a, S32 b, S32 c) = m_mulDivS32_C; +U32 (*m_mulDivU32)(S32 a, S32 b, U32 c) = m_mulDivU32_C; + +F32 (*m_catmullrom)(F32 t, F32 p0, F32 p1, F32 p2, F32 p3) = m_catmullrom_C; + +void (*m_sincos)( F32 angle, F32 *s, F32 *c ) = m_sincos_C; +void (*m_sincosD)( F64 angle, F64 *s, F64 *c ) = m_sincosD_C; + +void (*m_point2F_normalize)(F32 *p) = m_point2F_normalize_C; +void (*m_point2F_normalize_f)(F32 *p, F32 val) = m_point2F_normalize_f_C; +void (*m_point2D_normalize)(F64 *p) = m_point2D_normalize_C; +void (*m_point2D_normalize_f)(F64 *p, F64 val) = m_point2D_normalize_f_C; +void (*m_point3D_normalize_f)(F64 *p, F64 val) = m_point3D_normalize_f_C; +void (*m_point3F_normalize)(F32 *p) = m_point3F_normalize_C; +void (*m_point3F_normalize_f)(F32 *p, F32 len) = m_point3F_normalize_f_C; +void (*m_point3F_interpolate)(const F32 *from, const F32 *to, F32 factor, F32 *result) = m_point3F_interpolate_C; + +void (*m_point3D_normalize)(F64 *p) = m_point3D_normalize_C; +void (*m_point3D_interpolate)(const F64 *from, const F64 *to, F64 factor, F64 *result) = m_point3D_interpolate_C; + +void (*m_point3F_bulk_dot)(const F32* refVector, + const F32* dotPoints, + const U32 numPoints, + const U32 pointStride, + F32* output) = m_point3F_bulk_dot_C; +void (*m_point3F_bulk_dot_indexed)(const F32* refVector, + const F32* dotPoints, + const U32 numPoints, + const U32 pointStride, + const U32* pointIndices, + F32* output) = m_point3F_bulk_dot_indexed_C; + +void (*m_quatF_set_matF)( F32 x, F32 y, F32 z, F32 w, F32* m ) = m_quatF_set_matF_C; + +void (*m_matF_set_euler)(const F32 *e, F32 *result) = m_matF_set_euler_C; +void (*m_matF_set_euler_point)(const F32 *e, const F32 *p, F32 *result) = m_matF_set_euler_point_C; +void (*m_matF_identity)(F32 *m) = m_matF_identity_C; +void (*m_matF_inverse)(F32 *m) = m_matF_inverse_C; +void (*m_matF_affineInverse)(F32 *m) = m_matF_affineInverse_C; +void (*m_matF_invert_to)(const F32 *m, F32 *d) = m_matF_invert_to_C; +void (*m_matF_transpose)(F32 *m) = m_matF_transpose_C; +void (*m_matF_scale)(F32 *m,const F32* p) = m_matF_scale_C; +void (*m_matF_normalize)(F32 *m) = m_matF_normalize_C; +F32 (*m_matF_determinant)(const F32 *m) = m_matF_determinant_C; +void (*m_matF_x_matF)(const F32 *a, const F32 *b, F32 *mresult) = default_matF_x_matF_C; +void (*m_matF_x_matF_aligned)(const F32 *a, const F32 *b, F32 *mresult) = default_matF_x_matF_C; +// void (*m_matF_x_point3F)(const F32 *m, const F32 *p, F32 *presult) = m_matF_x_point3F_C; +// void (*m_matF_x_vectorF)(const F32 *m, const F32 *v, F32 *vresult) = m_matF_x_vectorF_C; +void (*m_matF_x_point4F)(const F32 *m, const F32 *p, F32 *presult) = m_matF_x_point4F_C; +void (*m_matF_x_scale_x_planeF)(const F32 *m, const F32* s, const F32 *p, F32 *presult) = m_matF_x_scale_x_planeF_C; +void (*m_matF_x_box3F)(const F32 *m, F32 *min, F32 *max) = m_matF_x_box3F_C; + +//------------------------------------------------------------------------------ +void mInstallLibrary_C() +{ + m_mulDivS32 = m_mulDivS32_C; + m_mulDivU32 = m_mulDivU32_C; + + m_catmullrom = m_catmullrom_C; + + m_sincos = m_sincos_C; + m_sincosD = m_sincosD_C; + + m_point2F_normalize = m_point2F_normalize_C; + m_point2F_normalize_f = m_point2F_normalize_f_C; + m_point2D_normalize = m_point2D_normalize_C; + m_point2D_normalize_f = m_point2D_normalize_f_C; + m_point3F_normalize = m_point3F_normalize_C; + m_point3F_normalize_f = m_point3F_normalize_f_C; + m_point3F_interpolate = m_point3F_interpolate_C; + + m_point3D_normalize = m_point3D_normalize_C; + m_point3D_interpolate = m_point3D_interpolate_C; + + m_point3F_bulk_dot = m_point3F_bulk_dot_C; + m_point3F_bulk_dot_indexed = m_point3F_bulk_dot_indexed_C; + + m_quatF_set_matF = m_quatF_set_matF_C; + + m_matF_set_euler = m_matF_set_euler_C; + m_matF_set_euler_point = m_matF_set_euler_point_C; + m_matF_identity = m_matF_identity_C; + m_matF_inverse = m_matF_inverse_C; + m_matF_affineInverse = m_matF_affineInverse_C; + m_matF_invert_to = m_matF_invert_to_C; + m_matF_transpose = m_matF_transpose_C; + m_matF_scale = m_matF_scale_C; + m_matF_normalize = m_matF_normalize_C; + m_matF_determinant = m_matF_determinant_C; + m_matF_x_matF = default_matF_x_matF_C; + m_matF_x_matF_aligned = default_matF_x_matF_C; +// m_matF_x_point3F = m_matF_x_point3F_C; +// m_matF_x_vectorF = m_matF_x_vectorF_C; + m_matF_x_point4F = m_matF_x_point4F_C; + m_matF_x_scale_x_planeF = m_matF_x_scale_x_planeF_C; + m_matF_x_box3F = m_matF_x_box3F_C; +} + diff --git a/Engine/source/math/mMatrix.cpp b/Engine/source/math/mMatrix.cpp new file mode 100644 index 000000000..12af818ea --- /dev/null +++ b/Engine/source/math/mMatrix.cpp @@ -0,0 +1,195 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/strings/stringFunctions.h" +#include "core/frameAllocator.h" + +#include "math/mMatrix.h" +#include "console/console.h" + + +const MatrixF MatrixF::Identity( true ); + +// idx(i,j) is index to element in column i, row j + +void MatrixF::transposeTo(F32 *matrix) const +{ + matrix[idx(0,0)] = m[idx(0,0)]; + matrix[idx(0,1)] = m[idx(1,0)]; + matrix[idx(0,2)] = m[idx(2,0)]; + matrix[idx(0,3)] = m[idx(3,0)]; + matrix[idx(1,0)] = m[idx(0,1)]; + matrix[idx(1,1)] = m[idx(1,1)]; + matrix[idx(1,2)] = m[idx(2,1)]; + matrix[idx(1,3)] = m[idx(3,1)]; + matrix[idx(2,0)] = m[idx(0,2)]; + matrix[idx(2,1)] = m[idx(1,2)]; + matrix[idx(2,2)] = m[idx(2,2)]; + matrix[idx(2,3)] = m[idx(3,2)]; + matrix[idx(3,0)] = m[idx(0,3)]; + matrix[idx(3,1)] = m[idx(1,3)]; + matrix[idx(3,2)] = m[idx(2,3)]; + matrix[idx(3,3)] = m[idx(3,3)]; +} + +bool MatrixF::isAffine() const +{ + // An affine transform is defined by the following structure + // + // [ X X X P ] + // [ X X X P ] + // [ X X X P ] + // [ 0 0 0 1 ] + // + // Where X is an orthonormal 3x3 submatrix and P is an arbitrary translation + // We'll check in the following order: + // 1: [3][3] must be 1 + // 2: Shear portion must be zero + // 3: Dot products of rows and columns must be zero + // 4: Length of rows and columns must be 1 + // + if (m[idx(3,3)] != 1.0f) + return false; + + if (m[idx(0,3)] != 0.0f || + m[idx(1,3)] != 0.0f || + m[idx(2,3)] != 0.0f) + return false; + + Point3F one, two, three; + getColumn(0, &one); + getColumn(1, &two); + getColumn(2, &three); + if (mDot(one, two) > 0.0001f || + mDot(one, three) > 0.0001f || + mDot(two, three) > 0.0001f) + return false; + + if (mFabs(1.0f - one.lenSquared()) > 0.0001f || + mFabs(1.0f - two.lenSquared()) > 0.0001f || + mFabs(1.0f - three.lenSquared()) > 0.0001f) + return false; + + getRow(0, &one); + getRow(1, &two); + getRow(2, &three); + if (mDot(one, two) > 0.0001f || + mDot(one, three) > 0.0001f || + mDot(two, three) > 0.0001f) + return false; + + if (mFabs(1.0f - one.lenSquared()) > 0.0001f || + mFabs(1.0f - two.lenSquared()) > 0.0001f || + mFabs(1.0f - three.lenSquared()) > 0.0001f) + return false; + + // We're ok. + return true; +} + +// Perform inverse on full 4x4 matrix. Used in special cases only, so not at all optimized. +bool MatrixF::fullInverse() +{ + Point4F a,b,c,d; + getRow(0,&a); + getRow(1,&b); + getRow(2,&c); + getRow(3,&d); + + // det = a0*b1*c2*d3 - a0*b1*c3*d2 - a0*c1*b2*d3 + a0*c1*b3*d2 + a0*d1*b2*c3 - a0*d1*b3*c2 - + // b0*a1*c2*d3 + b0*a1*c3*d2 + b0*c1*a2*d3 - b0*c1*a3*d2 - b0*d1*a2*c3 + b0*d1*a3*c2 + + // c0*a1*b2*d3 - c0*a1*b3*d2 - c0*b1*a2*d3 + c0*b1*a3*d2 + c0*d1*a2*b3 - c0*d1*a3*b2 - + // d0*a1*b2*c3 + d0*a1*b3*c2 + d0*b1*a2*c3 - d0*b1*a3*c2 - d0*c1*a2*b3 + d0*c1*a3*b2 + F32 det = a.x*b.y*c.z*d.w - a.x*b.y*c.w*d.z - a.x*c.y*b.z*d.w + a.x*c.y*b.w*d.z + a.x*d.y*b.z*c.w - a.x*d.y*b.w*c.z + - b.x*a.y*c.z*d.w + b.x*a.y*c.w*d.z + b.x*c.y*a.z*d.w - b.x*c.y*a.w*d.z - b.x*d.y*a.z*c.w + b.x*d.y*a.w*c.z + + c.x*a.y*b.z*d.w - c.x*a.y*b.w*d.z - c.x*b.y*a.z*d.w + c.x*b.y*a.w*d.z + c.x*d.y*a.z*b.w - c.x*d.y*a.w*b.z + - d.x*a.y*b.z*c.w + d.x*a.y*b.w*c.z + d.x*b.y*a.z*c.w - d.x*b.y*a.w*c.z - d.x*c.y*a.z*b.w + d.x*c.y*a.w*b.z; + + if (mFabs(det)<0.00001f) + return false; + + Point4F aa,bb,cc,dd; + aa.x = b.y*c.z*d.w - b.y*c.w*d.z - c.y*b.z*d.w + c.y*b.w*d.z + d.y*b.z*c.w - d.y*b.w*c.z; + aa.y = -a.y*c.z*d.w + a.y*c.w*d.z + c.y*a.z*d.w - c.y*a.w*d.z - d.y*a.z*c.w + d.y*a.w*c.z; + aa.z = a.y*b.z*d.w - a.y*b.w*d.z - b.y*a.z*d.w + b.y*a.w*d.z + d.y*a.z*b.w - d.y*a.w*b.z; + aa.w = -a.y*b.z*c.w + a.y*b.w*c.z + b.y*a.z*c.w - b.y*a.w*c.z - c.y*a.z*b.w + c.y*a.w*b.z; + + bb.x = -b.x*c.z*d.w + b.x*c.w*d.z + c.x*b.z*d.w - c.x*b.w*d.z - d.x*b.z*c.w + d.x*b.w*c.z; + bb.y = a.x*c.z*d.w - a.x*c.w*d.z - c.x*a.z*d.w + c.x*a.w*d.z + d.x*a.z*c.w - d.x*a.w*c.z; + bb.z = -a.x*b.z*d.w + a.x*b.w*d.z + b.x*a.z*d.w - b.x*a.w*d.z - d.x*a.z*b.w + d.x*a.w*b.z; + bb.w = a.x*b.z*c.w - a.x*b.w*c.z - b.x*a.z*c.w + b.x*a.w*c.z + c.x*a.z*b.w - c.x*a.w*b.z; + + cc.x = b.x*c.y*d.w - b.x*c.w*d.y - c.x*b.y*d.w + c.x*b.w*d.y + d.x*b.y*c.w - d.x*b.w*c.y; + cc.y = -a.x*c.y*d.w + a.x*c.w*d.y + c.x*a.y*d.w - c.x*a.w*d.y - d.x*a.y*c.w + d.x*a.w*c.y; + cc.z = a.x*b.y*d.w - a.x*b.w*d.y - b.x*a.y*d.w + b.x*a.w*d.y + d.x*a.y*b.w - d.x*a.w*b.y; + cc.w = -a.x*b.y*c.w + a.x*b.w*c.y + b.x*a.y*c.w - b.x*a.w*c.y - c.x*a.y*b.w + c.x*a.w*b.y; + + dd.x = -b.x*c.y*d.z + b.x*c.z*d.y + c.x*b.y*d.z - c.x*b.z*d.y - d.x*b.y*c.z + d.x*b.z*c.y; + dd.y = a.x*c.y*d.z - a.x*c.z*d.y - c.x*a.y*d.z + c.x*a.z*d.y + d.x*a.y*c.z - d.x*a.z*c.y; + dd.z = -a.x*b.y*d.z + a.x*b.z*d.y + b.x*a.y*d.z - b.x*a.z*d.y - d.x*a.y*b.z + d.x*a.z*b.y; + dd.w = a.x*b.y*c.z - a.x*b.z*c.y - b.x*a.y*c.z + b.x*a.z*c.y + c.x*a.y*b.z - c.x*a.z*b.y; + + setRow(0,aa); + setRow(1,bb); + setRow(2,cc); + setRow(3,dd); + + mul(1.0f/det); + + return true; +} + +EulerF MatrixF::toEuler() const +{ + const F32 * mat = m; + + EulerF r; + r.x = mAsin(mat[MatrixF::idx(2,1)]); + + if(mCos(r.x) != 0.f) + { + r.y = mAtan2(-mat[MatrixF::idx(2,0)], mat[MatrixF::idx(2,2)]); + r.z = mAtan2(-mat[MatrixF::idx(0,1)], mat[MatrixF::idx(1,1)]); + } + else + { + r.y = 0.f; + r.z = mAtan2(mat[MatrixF::idx(1,0)], mat[MatrixF::idx(0,0)]); + } + + return r; +} + +void MatrixF::dumpMatrix(const char *caption /* =NULL */) const +{ + U32 size = dStrlen(caption); + FrameTemp spacer(size+1); + char *spacerRef = spacer; + + dMemset(spacerRef, ' ', size); + spacerRef[size] = 0; + + Con::printf("%s = | %-8.4f %-8.4f %-8.4f %-8.4f |", caption, m[idx(0,0)], m[idx(0, 1)], m[idx(0, 2)], m[idx(0, 3)]); + Con::printf("%s | %-8.4f %-8.4f %-8.4f %-8.4f |", spacerRef, m[idx(1,0)], m[idx(1, 1)], m[idx(1, 2)], m[idx(1, 3)]); + Con::printf("%s | %-8.4f %-8.4f %-8.4f %-8.4f |", spacerRef, m[idx(2,0)], m[idx(2, 1)], m[idx(2, 2)], m[idx(2, 3)]); + Con::printf("%s | %-8.4f %-8.4f %-8.4f %-8.4f |", spacerRef, m[idx(3,0)], m[idx(3, 1)], m[idx(3, 2)], m[idx(3, 3)]); +} \ No newline at end of file diff --git a/Engine/source/math/mMatrix.h b/Engine/source/math/mMatrix.h new file mode 100644 index 000000000..f3899d0da --- /dev/null +++ b/Engine/source/math/mMatrix.h @@ -0,0 +1,591 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MMATRIX_H_ +#define _MMATRIX_H_ + +#ifndef _MPLANE_H_ +#include "math/mPlane.h" +#endif + +#ifndef _MBOX_H_ +#include "math/mBox.h" +#endif + +#ifndef _MPOINT4_H_ +#include "math/mPoint4.h" +#endif + +/// 4x4 Matrix Class +/// +/// This runs at F32 precision. + +class MatrixF +{ +private: + F32 m[16]; ///< Note: Torque uses row-major matrices + +public: + /// Create an uninitialized matrix. + /// + /// @param identity If true, initialize to the identity matrix. + explicit MatrixF(bool identity=false); + + /// Create a matrix to rotate about origin by e. + /// @see set + explicit MatrixF( const EulerF &e); + + /// Create a matrix to rotate about p by e. + /// @see set + MatrixF( const EulerF &e, const Point3F& p); + + /// Get the index in m to element in column i, row j + /// + /// This is necessary as we have m as a one dimensional array. + /// + /// @param i Column desired. + /// @param j Row desired. + static U32 idx(U32 i, U32 j) { return (i + j*4); } + + /// Initialize matrix to rotate about origin by e. + MatrixF& set( const EulerF &e); + + /// Initialize matrix to rotate about p by e. + MatrixF& set( const EulerF &e, const Point3F& p); + + /// Initialize matrix with a cross product of p. + MatrixF& setCrossProduct( const Point3F &p); + + /// Initialize matrix with a tensor product of p. + MatrixF& setTensorProduct( const Point3F &p, const Point3F& q); + + operator F32*() { return (m); } ///< Allow people to get at m. + operator const F32*() const { return (F32*)(m); } ///< Allow people to get at m. + + bool isAffine() const; ///< Check to see if this is an affine matrix. + bool isIdentity() const; ///< Checks for identity matrix. + + /// Make this an identity matrix. + MatrixF& identity(); + + /// Invert m. + MatrixF& inverse(); + /// Copy the inversion of this into out matrix. + void invertTo( MatrixF *out ); + + /// Take inverse of matrix assuming it is affine (rotation, + /// scale, sheer, translation only). + MatrixF& affineInverse(); + + /// Swap rows and columns. + MatrixF& transpose(); + + /// M * Matrix(p) -> M + MatrixF& scale( const Point3F &s ); + MatrixF& scale( F32 s ) { return scale( Point3F( s, s, s ) ); } + + /// Return scale assuming scale was applied via mat.scale(s). + Point3F getScale() const; + + EulerF toEuler() const; + + /// Compute the inverse of the matrix. + /// + /// Computes inverse of full 4x4 matrix. Returns false and performs no inverse if + /// the determinant is 0. + /// + /// Note: In most cases you want to use the normal inverse function. This method should + /// be used if the matrix has something other than (0,0,0,1) in the bottom row. + bool fullInverse(); + + /// Swaps rows and columns into matrix. + void transposeTo(F32 *matrix) const; + + /// Normalize the matrix. + void normalize(); + + /// Copy the requested column into a Point4F. + void getColumn(S32 col, Point4F *cptr) const; + Point4F getColumn4F(S32 col) const { Point4F ret; getColumn(col,&ret); return ret; } + + /// Copy the requested column into a Point3F. + /// + /// This drops the bottom-most row. + void getColumn(S32 col, Point3F *cptr) const; + Point3F getColumn3F(S32 col) const { Point3F ret; getColumn(col,&ret); return ret; } + + /// Set the specified column from a Point4F. + void setColumn(S32 col, const Point4F& cptr); + + /// Set the specified column from a Point3F. + /// + /// The bottom-most row is not set. + void setColumn(S32 col, const Point3F& cptr); + + /// Copy the specified row into a Point4F. + void getRow(S32 row, Point4F *cptr) const; + Point4F getRow4F(S32 row) const { Point4F ret; getRow(row,&ret); return ret; } + + /// Copy the specified row into a Point3F. + /// + /// Right-most item is dropped. + void getRow(S32 row, Point3F *cptr) const; + Point3F getRow3F(S32 row) const { Point3F ret; getRow(row,&ret); return ret; } + + /// Set the specified row from a Point4F. + void setRow(S32 row, const Point4F& cptr); + + /// Set the specified row from a Point3F. + /// + /// The right-most item is not set. + void setRow(S32 row, const Point3F& cptr); + + /// Get the position of the matrix. + /// + /// This is the 4th column of the matrix. + Point3F getPosition() const; + + /// Set the position of the matrix. + /// + /// This is the 4th column of the matrix. + void setPosition( const Point3F &pos ) { setColumn( 3, pos ); } + + /// Add the passed delta to the matrix position. + void displace( const Point3F &delta ); + + /// Get the x axis of the matrix. + /// + /// This is the 1st column of the matrix and is + /// normally considered the right vector. + VectorF getRightVector() const; + + /// Get the y axis of the matrix. + /// + /// This is the 2nd column of the matrix and is + /// normally considered the forward vector. + VectorF getForwardVector() const; + + /// Get the z axis of the matrix. + /// + /// This is the 3rd column of the matrix and is + /// normally considered the up vector. + VectorF getUpVector() const; + + MatrixF& mul(const MatrixF &a); ///< M * a -> M + MatrixF& mulL(const MatrixF &a); ///< a * M -> M + MatrixF& mul(const MatrixF &a, const MatrixF &b); ///< a * b -> M + + // Scalar multiplies + MatrixF& mul(const F32 a); ///< M * a -> M + MatrixF& mul(const MatrixF &a, const F32 b); ///< a * b -> M + + + void mul( Point4F& p ) const; ///< M * p -> p (full [4x4] * [1x4]) + void mulP( Point3F& p ) const; ///< M * p -> p (assume w = 1.0f) + void mulP( const Point3F &p, Point3F *d) const; ///< M * p -> d (assume w = 1.0f) + void mulV( VectorF& p ) const; ///< M * v -> v (assume w = 0.0f) + void mulV( const VectorF &p, Point3F *d) const; ///< M * v -> d (assume w = 0.0f) + + void mul(Box3F& b) const; ///< Axial box -> Axial Box + + MatrixF& add( const MatrixF& m ); + + /// Convenience function to allow people to treat this like an array. + F32& operator ()(S32 row, S32 col) { return m[idx(col,row)]; } + F32 operator ()(S32 row, S32 col) const { return m[idx(col,row)]; } + + void dumpMatrix(const char *caption=NULL) const; + + // Math operator overloads + //------------------------------------ + friend MatrixF operator * ( const MatrixF &m1, const MatrixF &m2 ); + MatrixF& operator *= ( const MatrixF &m ); + + // Static identity matrix + const static MatrixF Identity; +}; + + +//-------------------------------------- +// Inline Functions + +inline MatrixF::MatrixF(bool _identity) +{ + if (_identity) + identity(); +} + +inline MatrixF::MatrixF( const EulerF &e ) +{ + set(e); +} + +inline MatrixF::MatrixF( const EulerF &e, const Point3F& p ) +{ + set(e,p); +} + +inline MatrixF& MatrixF::set( const EulerF &e) +{ + m_matF_set_euler( e, *this ); + return (*this); +} + + +inline MatrixF& MatrixF::set( const EulerF &e, const Point3F& p) +{ + m_matF_set_euler_point( e, p, *this ); + return (*this); +} + +inline MatrixF& MatrixF::setCrossProduct( const Point3F &p) +{ + m[1] = -(m[4] = p.z); + m[8] = -(m[2] = p.y); + m[6] = -(m[9] = p.x); + m[0] = m[3] = m[5] = m[7] = m[10] = m[11] = + m[12] = m[13] = m[14] = 0.0f; + m[15] = 1; + return (*this); +} + +inline MatrixF& MatrixF::setTensorProduct( const Point3F &p, const Point3F &q) +{ + m[0] = p.x * q.x; + m[1] = p.x * q.y; + m[2] = p.x * q.z; + m[4] = p.y * q.x; + m[5] = p.y * q.y; + m[6] = p.y * q.z; + m[8] = p.z * q.x; + m[9] = p.z * q.y; + m[10] = p.z * q.z; + m[3] = m[7] = m[11] = m[12] = m[13] = m[14] = 0.0f; + m[15] = 1.0f; + return (*this); +} + +inline bool MatrixF::isIdentity() const +{ + return + m[0] == 1.0f && + m[1] == 0.0f && + m[2] == 0.0f && + m[3] == 0.0f && + m[4] == 0.0f && + m[5] == 1.0f && + m[6] == 0.0f && + m[7] == 0.0f && + m[8] == 0.0f && + m[9] == 0.0f && + m[10] == 1.0f && + m[11] == 0.0f && + m[12] == 0.0f && + m[13] == 0.0f && + m[14] == 0.0f && + m[15] == 1.0f; +} + +inline MatrixF& MatrixF::identity() +{ + m[0] = 1.0f; + m[1] = 0.0f; + m[2] = 0.0f; + m[3] = 0.0f; + m[4] = 0.0f; + m[5] = 1.0f; + m[6] = 0.0f; + m[7] = 0.0f; + m[8] = 0.0f; + m[9] = 0.0f; + m[10] = 1.0f; + m[11] = 0.0f; + m[12] = 0.0f; + m[13] = 0.0f; + m[14] = 0.0f; + m[15] = 1.0f; + return (*this); +} + + +inline MatrixF& MatrixF::inverse() +{ + m_matF_inverse(m); + return (*this); +} + +inline void MatrixF::invertTo( MatrixF *out ) +{ + m_matF_invert_to(m,*out); +} + +inline MatrixF& MatrixF::affineInverse() +{ +// AssertFatal(isAffine() == true, "Error, this matrix is not an affine transform"); + m_matF_affineInverse(m); + return (*this); +} + +inline MatrixF& MatrixF::transpose() +{ + m_matF_transpose(m); + return (*this); +} + +inline MatrixF& MatrixF::scale(const Point3F& p) +{ + m_matF_scale(m,p); + return *this; +} + +inline Point3F MatrixF::getScale() const +{ + Point3F scale; + scale.x = mSqrt(m[0]*m[0] + m[4] * m[4] + m[8] * m[8]); + scale.y = mSqrt(m[1]*m[1] + m[5] * m[5] + m[9] * m[9]); + scale.z = mSqrt(m[2]*m[2] + m[6] * m[6] + m[10] * m[10]); + return scale; +} + +inline void MatrixF::normalize() +{ + m_matF_normalize(m); +} + +inline MatrixF& MatrixF::mul( const MatrixF &a ) +{ // M * a -> M + AssertFatal(&a != this, "MatrixF::mul - a.mul(a) is invalid!"); + + MatrixF tempThis(*this); + m_matF_x_matF(tempThis, a, *this); + return (*this); +} + +inline MatrixF& MatrixF::mulL( const MatrixF &a ) +{ // a * M -> M + AssertFatal(&a != this, "MatrixF::mulL - a.mul(a) is invalid!"); + + MatrixF tempThis(*this); + m_matF_x_matF(a, tempThis, *this); + return (*this); +} + +inline MatrixF& MatrixF::mul( const MatrixF &a, const MatrixF &b ) +{ // a * b -> M + AssertFatal((&a != this) && (&b != this), "MatrixF::mul - a.mul(a, b) a.mul(b, a) a.mul(a, a) is invalid!"); + + m_matF_x_matF(a, b, *this); + return (*this); +} + + +inline MatrixF& MatrixF::mul(const F32 a) +{ + for (U32 i = 0; i < 16; i++) + m[i] *= a; + + return *this; +} + + +inline MatrixF& MatrixF::mul(const MatrixF &a, const F32 b) +{ + *this = a; + mul(b); + + return *this; +} + +inline void MatrixF::mul( Point4F& p ) const +{ + Point4F temp; + m_matF_x_point4F(*this, &p.x, &temp.x); + p = temp; +} + +inline void MatrixF::mulP( Point3F& p) const +{ + // M * p -> d + Point3F d; + m_matF_x_point3F(*this, &p.x, &d.x); + p = d; +} + +inline void MatrixF::mulP( const Point3F &p, Point3F *d) const +{ + // M * p -> d + m_matF_x_point3F(*this, &p.x, &d->x); +} + +inline void MatrixF::mulV( VectorF& v) const +{ + // M * v -> v + VectorF temp; + m_matF_x_vectorF(*this, &v.x, &temp.x); + v = temp; +} + +inline void MatrixF::mulV( const VectorF &v, Point3F *d) const +{ + // M * v -> d + m_matF_x_vectorF(*this, &v.x, &d->x); +} + +inline void MatrixF::mul(Box3F& b) const +{ + m_matF_x_box3F(*this, &b.minExtents.x, &b.maxExtents.x); +} + +inline MatrixF& MatrixF::add( const MatrixF& a ) +{ + for( U32 i = 0; i < 16; ++ i ) + m[ i ] += a.m[ i ]; + + return *this; +} + +inline void MatrixF::getColumn(S32 col, Point4F *cptr) const +{ + cptr->x = m[col]; + cptr->y = m[col+4]; + cptr->z = m[col+8]; + cptr->w = m[col+12]; +} + +inline void MatrixF::getColumn(S32 col, Point3F *cptr) const +{ + cptr->x = m[col]; + cptr->y = m[col+4]; + cptr->z = m[col+8]; +} + +inline void MatrixF::setColumn(S32 col, const Point4F &cptr) +{ + m[col] = cptr.x; + m[col+4] = cptr.y; + m[col+8] = cptr.z; + m[col+12]= cptr.w; +} + +inline void MatrixF::setColumn(S32 col, const Point3F &cptr) +{ + m[col] = cptr.x; + m[col+4] = cptr.y; + m[col+8] = cptr.z; +} + + +inline void MatrixF::getRow(S32 col, Point4F *cptr) const +{ + col *= 4; + cptr->x = m[col++]; + cptr->y = m[col++]; + cptr->z = m[col++]; + cptr->w = m[col]; +} + +inline void MatrixF::getRow(S32 col, Point3F *cptr) const +{ + col *= 4; + cptr->x = m[col++]; + cptr->y = m[col++]; + cptr->z = m[col]; +} + +inline void MatrixF::setRow(S32 col, const Point4F &cptr) +{ + col *= 4; + m[col++] = cptr.x; + m[col++] = cptr.y; + m[col++] = cptr.z; + m[col] = cptr.w; +} + +inline void MatrixF::setRow(S32 col, const Point3F &cptr) +{ + col *= 4; + m[col++] = cptr.x; + m[col++] = cptr.y; + m[col] = cptr.z; +} + +inline Point3F MatrixF::getPosition() const +{ + return Point3F( m[3], m[3+4], m[3+8] ); +} + +inline void MatrixF::displace( const Point3F &delta ) +{ + m[3] += delta.x; + m[3+4] += delta.y; + m[3+8] += delta.z; +} + +inline VectorF MatrixF::getForwardVector() const +{ + VectorF vec; + getColumn( 1, &vec ); + return vec; +} + +inline VectorF MatrixF::getRightVector() const +{ + VectorF vec; + getColumn( 0, &vec ); + return vec; +} + +inline VectorF MatrixF::getUpVector() const +{ + VectorF vec; + getColumn( 2, &vec ); + return vec; +} + +//------------------------------------ +// Math operator overloads +//------------------------------------ +inline MatrixF operator * ( const MatrixF &m1, const MatrixF &m2 ) +{ + // temp = m1 * m2 + MatrixF temp; + m_matF_x_matF(m1, m2, temp); + return temp; +} + +inline MatrixF& MatrixF::operator *= ( const MatrixF &m ) +{ + MatrixF tempThis(*this); + m_matF_x_matF(tempThis, m, *this); + return (*this); +} + +//------------------------------------ +// Non-member methods +//------------------------------------ + +inline void mTransformPlane(const MatrixF& mat, const Point3F& scale, const PlaneF& plane, PlaneF * result) +{ + m_matF_x_scale_x_planeF(mat, &scale.x, &plane.x, &result->x); +} + +#endif //_MMATRIX_H_ diff --git a/Engine/source/math/mOrientedBox.cpp b/Engine/source/math/mOrientedBox.cpp new file mode 100644 index 000000000..47037a7cd --- /dev/null +++ b/Engine/source/math/mOrientedBox.cpp @@ -0,0 +1,95 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "math/mOrientedBox.h" + +#include "math/mMatrix.h" + + + +//----------------------------------------------------------------------------- + +bool OrientedBox3F::isContained( const Point3F& point ) const +{ + Point3F distToCenter = point - getCenter(); + for( U32 i = 0; i < 3; ++ i ) + { + F32 coeff = mDot( distToCenter, getAxis( i ) ); + if( mFabs( coeff ) > getHalfExtents()[ i ] ) + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- + +void OrientedBox3F::set( const MatrixF& transform, const Point3F& extents ) +{ + mCenter = transform.getPosition(); + + mAxes[ RightVector ] = transform.getRightVector(); + mAxes[ ForwardVector ] = transform.getForwardVector(); + mAxes[ UpVector ] = transform.getUpVector(); + + mHalfExtents = extents; + + _initPoints(); +} + +//----------------------------------------------------------------------------- + +void OrientedBox3F::set( const MatrixF& transform, const Box3F& aabb ) +{ + mCenter = aabb.getCenter(); + transform.mulP( mCenter ); + + mAxes[ RightVector ] = transform.getRightVector(); + mAxes[ ForwardVector ] = transform.getForwardVector(); + mAxes[ UpVector ] = transform.getUpVector(); + + mHalfExtents[ 0 ] = aabb.len_x() / 2.f; + mHalfExtents[ 1 ] = aabb.len_y() / 2.f; + mHalfExtents[ 2 ] = aabb.len_z() / 2.f; + + _initPoints(); +} + +//----------------------------------------------------------------------------- + +void OrientedBox3F::_initPoints() +{ + const Point3F right = mAxes[ RightVector ] * mHalfExtents.x; + const Point3F forward = mAxes[ ForwardVector ] * mHalfExtents.y; + const Point3F up = mAxes[ UpVector ] * mHalfExtents.z; + + mPoints[ NearBottomLeft ] = mCenter - forward - right - up; + mPoints[ NearBottomRight ] = mCenter - forward + right - up; + mPoints[ NearTopLeft ] = mCenter - forward - right + up; + mPoints[ NearTopRight ] = mCenter - forward + right + up; + + mPoints[ FarBottomLeft ] = mCenter + forward - right - up; + mPoints[ FarBottomRight ] = mCenter + forward + right - up; + mPoints[ FarTopLeft ] = mCenter + forward - right + up; + mPoints[ FarTopRight ] = mCenter + forward + right + up; +} diff --git a/Engine/source/math/mOrientedBox.h b/Engine/source/math/mOrientedBox.h new file mode 100644 index 000000000..422dd8f27 --- /dev/null +++ b/Engine/source/math/mOrientedBox.h @@ -0,0 +1,112 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MORIENTEDBOX_H_ +#define _MORIENTEDBOX_H_ + +#ifndef _MBOXBASE_H_ +#include "math/mBoxBase.h" +#endif + +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif + + +class MatrixF; +class Box3F; + + +/// An oriented bounding box (OBB) described by a center point, three normalizes axis +/// vectors, and half-extents along each of the axes. +class OrientedBox3F : public BoxBase +{ + public: + + enum Axis + { + RightVector, + ForwardVector, + UpVector + }; + + protected: + + /// Center point. + Point3F mCenter; + + /// Normalized axis vectors. + Point3F mAxes[ 3 ]; + + /// Box half-extents along each axis. + Point3F mHalfExtents; + + /// Corner points. + Point3F mPoints[ NUM_POINTS ]; + + void _initPoints(); + + public: + + OrientedBox3F() {} + OrientedBox3F( const MatrixF& transform, const Point3F& extents ) { set( transform, extents ); } + OrientedBox3F( const MatrixF& transform, const Box3F& aabb ) { set( transform, aabb ); } + + /// Return the center point of the bounding box. + const Point3F& getCenter() const { return mCenter; } + + /// Return the normalized axis vector for the given world-space axis. + const Point3F& getAxis( U32 i ) const + { + AssertFatal( i < 3, "OrientedBox3F::getAxis - Index out of range" ); + return mAxes[ i ]; + } + + /// Return the half-extents along each axis. + /// + /// Since the OBBs are symmetrical across each axis, we store half-extents + /// instead of full extents as usually half-extents are needed in the computations. + const Point3F& getHalfExtents() const { return mHalfExtents; } + + /// Return true if the given point is contained in the OBB. + bool isContained( const Point3F& point ) const; + + /// Return the corner points of the box. + const Point3F* getPoints() const { return mPoints; } + + /// Return the array of corner points for the box. + operator const Point3F*() const { return getPoints(); } + + /// Compute the OBB values from the given transform and extents. + /// + /// @param transform World->object space transform. + /// @param extents Box extent on each axis. + void set( const MatrixF& transform, const Point3F& extents ); + + /// Compute the OBB from an AABB in the given transform space. + /// + /// @param transform Transform space for the AABB. + /// @param aabb An axis-aligned bounding box in the given transform space. + void set( const MatrixF& transform, const Box3F& aabb ); +}; + +#endif // !_MORIENTEDBOX_H_ diff --git a/Engine/source/math/mPlane.cpp b/Engine/source/math/mPlane.cpp new file mode 100644 index 000000000..e8ac30d24 --- /dev/null +++ b/Engine/source/math/mPlane.cpp @@ -0,0 +1,222 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "math/mPlane.h" + +#include "math/mathUtils.h" +#include "math/mBox.h" +#include "math/mOrientedBox.h" +#include "math/mSphere.h" + + +//----------------------------------------------------------------------------- + +bool PlaneF::intersect( const PlaneF& plane, Point3F& outLinePt, VectorF& outLineDir ) const +{ + // Compute direction of intersection line. + outLineDir = mCross( *this, plane ); + + // If d is zero, the planes are parallel (and separated) + // or coincident, so they're not considered intersecting + F32 denom = mDot( outLineDir, outLineDir ); + if ( denom < 0.00001f ) + return false; + + // Compute point on intersection line + outLinePt = - mCross( d * plane - plane.d * *this, + outLineDir ) / denom; + + return true; +} + +//----------------------------------------------------------------------------- + +bool PlaneF::isParallelTo( const PlaneF& plane, F32 epsilon ) const +{ + F32 val = 1.0f - mFabs( mDot( *this, plane ) ); + return ( val > - epsilon ) && ( val < epsilon ); +} + +//----------------------------------------------------------------------------- + +bool PlaneF::isPerpendicularTo( const PlaneF& plane, F32 epsilon ) const +{ + F32 val = mDot( *this, plane ); + return ( val > - epsilon) && ( val < epsilon ); +} + +//----------------------------------------------------------------------------- + +bool PlaneF::clipSegment( const Point3F& start, const Point3F& end, Point3F& outNewEnd ) const +{ + // Intersect ray with plane. + + F32 dist = intersect( start, end ); + if( dist == PARALLEL_PLANE || dist < 0.f || dist > 1.f ) + return false; + + // Compute distance to point on segment. + + Point3F dir = end - start; + dir *= dist; + + // Compute new end point. + + outNewEnd = start + dir; + + return true; +} + +//----------------------------------------------------------------------------- + +U32 PlaneF::clipPolygon( const Point3F* inVertices, U32 inNumVertices, Point3F* outVertices ) const +{ + // Find the first vertex that lies on the front of the plane. + + S32 start = -1; + for( U32 i = 0; i < inNumVertices; i ++ ) + { + Side side = whichSide( inVertices[ i ] ); + if( side == PlaneF::Front ) + { + start = i; + break; + } + } + + // If nothing was in front of the plane, we're done. + + if( start == -1 ) + return 0; + + Point3F finalPoints[ 128 ]; + U32 numFinalPoints = 0; + + U32 baseStart = start; + U32 end = ( start + 1 ) % inNumVertices; + + dMemcpy( outVertices, inVertices, inNumVertices * sizeof( Point3F ) ); + + while( end != baseStart ) + { + const Point3F& rStartPoint = outVertices[ start ]; + const Point3F& rEndPoint = outVertices[ end ]; + + PlaneF::Side fSide = whichSide( rStartPoint ); + PlaneF::Side eSide = whichSide( rEndPoint ); + + S32 code = fSide * 3 + eSide; + switch( code ) + { + case 4: // f f + case 3: // f o + case 1: // o f + case 0: // o o + // No Clipping required + finalPoints[ numFinalPoints ++ ] = outVertices[ start ]; + start = end; + end = ( end + 1 ) % inNumVertices; + break; + + + case 2: // f b + { + // In this case, we emit the front point, Insert the intersection, + // and advancing to point to first point that is in front or on... + // + finalPoints[ numFinalPoints ++ ] = outVertices[ start ]; + + Point3F vector = rEndPoint - rStartPoint; + F32 t = - ( distToPlane( rStartPoint ) / mDot( *this, vector ) ); + + Point3F intersection = rStartPoint + ( vector * t ); + finalPoints[ numFinalPoints ++ ] = intersection; + + U32 endSeek = ( end + 1 ) % inNumVertices; + while( whichSide( outVertices[ endSeek ] ) == PlaneF::Back ) + endSeek = ( endSeek + 1 ) % inNumVertices; + + end = endSeek; + start = ( end + ( inNumVertices - 1 ) ) % inNumVertices; + + const Point3F& rNewStartPoint = outVertices[ start ]; + const Point3F& rNewEndPoint = outVertices[ end ]; + + vector = rNewEndPoint - rNewStartPoint; + t = - ( distToPlane( rNewStartPoint ) / mDot( *this, vector ) ); + + intersection = rNewStartPoint + ( vector * t ); + outVertices[ start ] = intersection; + } + break; + + case -1: // o b + { + // In this case, we emit the front point, and advance to point to first + // point that is in front or on... + // + finalPoints[ numFinalPoints ++ ] = outVertices[ start ]; + + U32 endSeek = ( end + 1 ) % inNumVertices; + while( whichSide( outVertices[ endSeek ] ) == PlaneF::Back) + endSeek = ( endSeek + 1 ) % inNumVertices; + + end = endSeek; + start = (end + ( inNumVertices - 1 ) ) % inNumVertices; + + const Point3F& rNewStartPoint = outVertices[ start ]; + const Point3F& rNewEndPoint = outVertices[ end ]; + + Point3F vector = rNewEndPoint - rNewStartPoint; + F32 t = - ( distToPlane( rNewStartPoint ) / mDot( *this, vector ) ); + + Point3F intersection = rNewStartPoint + ( vector * t ); + outVertices[ start ] = intersection; + } + break; + + case -2: // b f + case -3: // b o + case -4: // b b + // In the algorithm used here, this should never happen... + AssertISV(false, "SGUtil::clipToPlane: error in polygon clipper"); + break; + + default: + AssertFatal(false, "SGUtil::clipToPlane: bad outcode"); + break; + } + + } + + // Emit the last point. + finalPoints[ numFinalPoints ++ ] = outVertices[ start ]; + AssertFatal( numFinalPoints >= 3, avar("Error, this shouldn't happen! Invalid winding in clipToPlane: %d", numFinalPoints ) ); + + // Copy the new rWinding, and we're set! + // + dMemcpy( outVertices, finalPoints, numFinalPoints * sizeof( Point3F ) ); + AssertISV( numFinalPoints <= 128, "MaxWindingPoints exceeded in scenegraph. Fatal error."); + + return numFinalPoints; +} diff --git a/Engine/source/math/mPlane.h b/Engine/source/math/mPlane.h new file mode 100644 index 000000000..cb83788e1 --- /dev/null +++ b/Engine/source/math/mPlane.h @@ -0,0 +1,797 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MPLANE_H_ +#define _MPLANE_H_ + +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif + +#ifndef _MBOX_H_ +#include "math/mBox.h" +#endif + +#ifndef _MORIENTEDBOX_H_ +#include "math/mOrientedBox.h" +#endif + +#ifndef _MSPHERE_H_ +#include "math/mSphere.h" +#endif + + +/// A 3D plane defined by a normal and a distance along the normal. +/// +/// @note The distance is stored negative. +class PlaneF : public Point3F +{ + public: + + /// *NEGATIVE* distance along the xyz normal. + F32 d; + + /// Return the plane's normal. + const Point3F& getNormal() const { return *this; } + + /// Return the plane's position. + Point3F getPosition() const { return Point3F( x, y, z ) * -d; } + + bool isHorizontal() const; + bool isVertical() const; + + bool isParallelTo( const PlaneF& plane, F32 epsilon = POINT_EPSILON ) const; + + bool isPerpendicularTo( const PlaneF& plane, F32 epsilon = POINT_EPSILON ) const; + + /// @name Initialization + /// @{ + + PlaneF() {} + PlaneF( const Point3F& p, const Point3F& n ); + /// NOTE: d is the NEGATIVE distance along the xyz normal. + PlaneF( F32 _x, F32 _y, F32 _z, F32 _d); + PlaneF( const Point3F& j, const Point3F& k, const Point3F& l ); + + void set( F32 _x, F32 _y, F32 _z ); + /// NOTE: d is the NEGATIVE distance along the xyz normal. + void set( F32 _x, F32 _y, F32 _z, F32 _d ); + void set( const Point3F& p, const Point3F& n); + void set( const Point3F& k, const Point3F& j, const Point3F& l ); + void setPoint(const Point3F &p); // assumes the x,y,z fields are already set + // creates an un-normalized plane + + void setXY(F32 zz); + void setYZ(F32 xx); + void setXZ(F32 yy); + void setXY(const Point3F& P, F32 dir); + void setYZ(const Point3F& P, F32 dir); + void setXZ(const Point3F& P, F32 dir); + + /// @} + + void shiftX(F32 xx); + void shiftY(F32 yy); + void shiftZ(F32 zz); + void invert(); + void neg(); + + /// Return the distance of the given point from the plane. + F32 distToPlane( const Point3F& cp ) const; + + /// Project the given point onto the plane. + Point3F project(const Point3F &pt) const; + + /// @name Intersection + /// @{ + + enum Side + { + Front = 1, + On = 0, + Back = -1 + }; + + /// Compute on which side of the plane the given point lies. + Side whichSide( const Point3F& cp ) const; + + /// Compute which side the given sphere lies on. + Side whichSide( const SphereF& sphere ) const; + + /// Compute which side the given AABB lies on. + Side whichSide( const Box3F& aabb ) const; + + /// Compute which side the given OBB lies on. + Side whichSide( const OrientedBox3F& obb ) const; + + /// Compute which side the given box lies on. + /// + /// @param center Center point. + /// @param axisx X-Axis vector. Length is box half-extent along X. + /// @param axisy Y-Axis vector. Length is box half-extent along Y. + /// @param axisz Z-Axis vector. Length is box half-extent along Z. + /// + /// @return Side that the given box is on. + Side whichSideBox( const Point3F& center, + const Point3F& axisx, + const Point3F& axisy, + const Point3F& axisz ) const; + + /// @} + + /// @name Intersection + /// @{ + + /// Compute the distance at which the ray traveling from @a start in the direction + /// of @end intersects the plane + /// @param start Starting point of the ray. + /// @param end Point in the direction of which the ray travels from @a start. + /// @return The distance (as a fraction/multiple of the length of the vector between + /// @a start and @a end) at which the ray intersects the plane or PARALLEL_PLANE if the + /// ray is parallel to the plane. + F32 intersect( const Point3F &start, const Point3F &end ) const; + + /// Compute the intersection of the ray that emanates at @a start in the direction + /// @dir. + /// @param start Point where the ray emanates. + /// @param dir Direction in which the ray travels. Must be normalized. + /// @param outHit Resulting intersection point. Only set if there indeed is a hit. + /// @return True if the ray intersects the plane or false if not. + bool intersect( const Point3F &start, const Point3F &dir, Point3F *outHit ) const; + + /// Compute the intersection between two planes. + /// + /// @param plane Plane to intersect with. + /// @param outLineOrigin Used to store the origin of the resulting intersection line. + /// @param outLineDirection Used to store the direction of the resulting intersection line. + /// + /// @return True if there is an intersection or false if the two planes are coplanar. + bool intersect( const PlaneF& plane, Point3F& outLineOrigin, Point3F& outLineDirection ) const; + + /// @} + + /// @name Clipping + /// @{ + + /// Clip a convex polygon by the plane. + /// + /// The resulting polygon will be the input polygon minus the part on the negative side + /// of the plane. The input polygon must be convex and @a inVertices must be in CCW or CW order. + /// + /// @param inVertices Array holding the vertices of the input polygon. + /// @param inNumVertices Number of vertices in @a inVertices. + /// @param outVertices Array to hold the vertices of the clipped polygon. Must have space for one additional + /// vertex in case the polygon is split by the plane such that an additional vertex appears. Must not + /// be the same as @a inVertices. + /// @return Number of vertices in the clipped polygon, i.e. number of vertices in @a outVertices. + /// + /// @note Be aware that if the polygon fully lies on the negative side of the plane, + /// the resulting @a outNumVertices will be zero, i.e. no polygon will result from the clip. + U32 clipPolygon( const Point3F* inVertices, U32 inNumVertices, Point3F* outVertices ) const; + + /// Clip a line segment by the plane. + /// + /// @param start Start point of the line segment. + /// @param end End point of the line segment. + /// @param outNewEnd New end point if there is an intersection with the plane. + /// + /// @return True + bool clipSegment( const Point3F& start, const Point3F& end, Point3F& outNewEnd ) const; + + /// @} +}; + +#define PARALLEL_PLANE 1e20f + +#define PlaneSwitchCode(s, e) (s * 3 + e) + + +//--------------------------------------------------------------------------- + +inline PlaneF::PlaneF( F32 _x, F32 _y, F32 _z, F32 _d ) +{ + set( _x, _y, _z, _d ); +} + +//----------------------------------------------------------------------------- + +inline PlaneF::PlaneF( const Point3F& p, const Point3F& n ) +{ + set(p,n); +} + +//----------------------------------------------------------------------------- + +inline PlaneF::PlaneF( const Point3F& j, const Point3F& k, const Point3F& l ) +{ + set(j,k,l); +} + +//----------------------------------------------------------------------------- + +inline void PlaneF::setXY( F32 zz ) +{ + x = y = 0.0f; z = 1.0f; d = -zz; +} + +//----------------------------------------------------------------------------- + +inline void PlaneF::setYZ( F32 xx ) +{ + x = 1.0f; z = y = 0.0f; d = -xx; +} + +//----------------------------------------------------------------------------- + +inline void PlaneF::setXZ( F32 yy ) +{ + x = z = 0.0f; y = 1.0f; d = -yy; +} + +//----------------------------------------------------------------------------- + +inline void PlaneF::setXY(const Point3F& point, F32 dir) // Normal = (0, 0, -1|1) +{ + x = y = 0.0f; + d = -((z = dir) * point.z); +} + +//----------------------------------------------------------------------------- + +inline void PlaneF::setYZ(const Point3F& point, F32 dir) // Normal = (-1|1, 0, 0) +{ + z = y = 0.0f; + d = -((x = dir) * point.x); +} + +//----------------------------------------------------------------------------- + +inline void PlaneF::setXZ(const Point3F& point, F32 dir) // Normal = (0, -1|1, 0) +{ + x = z = 0.0f; + d = -((y = dir) * point.y); +} + +//----------------------------------------------------------------------------- + +inline void PlaneF::shiftX( F32 xx ) +{ + d -= xx * x; +} + +//----------------------------------------------------------------------------- + +inline void PlaneF::shiftY( F32 yy ) +{ + d -= yy * y; +} + +//----------------------------------------------------------------------------- + +inline void PlaneF::shiftZ( F32 zz ) +{ + d -= zz * z; +} + +//----------------------------------------------------------------------------- + +inline bool PlaneF::isHorizontal() const +{ + return (x == 0.0f && y == 0.0f) ? true : false; +} + +//----------------------------------------------------------------------------- + +inline bool PlaneF::isVertical() const +{ + return ((x != 0.0f || y != 0.0f) && z == 0.0f) ? true : false; +} + +//----------------------------------------------------------------------------- + +inline Point3F PlaneF::project(const Point3F &pt) const +{ + F32 dist = distToPlane(pt); + return Point3F(pt.x - x * dist, pt.y - y * dist, pt.z - z * dist); +} + +//----------------------------------------------------------------------------- + +inline F32 PlaneF::distToPlane( const Point3F& cp ) const +{ + // return mDot(*this,cp) + d; + return (x * cp.x + y * cp.y + z * cp.z) + d; +} + +//----------------------------------------------------------------------------- + +inline PlaneF::Side PlaneF::whichSide(const Point3F& cp) const +{ + F32 dist = distToPlane(cp); + if (dist >= 0.005f) // if (mFabs(dist) < 0.005f) + return Front; // return On; + else if (dist <= -0.005f) // else if (dist > 0.0f) + return Back; // return Front; + else // else + return On; // return Back; +} + +//----------------------------------------------------------------------------- + +inline PlaneF::Side PlaneF::whichSide( const SphereF& sphere ) const +{ + const F32 dist = distToPlane( sphere.center ); + if( dist > sphere.radius ) + return Front; + else if( dist < - sphere.radius ) + return Back; + else + return On; +} + +//----------------------------------------------------------------------------- + +inline PlaneF::Side PlaneF::whichSide( const Box3F& aabb ) const +{ + // See Graphics Gems IV, 1.7 and "A Faster Overlap Test for a Plane and a Bounding Box" + // (http://replay.waybackmachine.org/19981203032829/http://www.cs.unc.edu/~hoff/research/vfculler/boxplane.html) + // for details. + + Point3F pVertex; + Point3F nVertex; + + pVertex.x = ( x > 0.0f ) ? aabb.maxExtents.x : aabb.minExtents.x; + pVertex.y = ( y > 0.0f ) ? aabb.maxExtents.y : aabb.minExtents.y; + pVertex.z = ( z > 0.0f ) ? aabb.maxExtents.z : aabb.minExtents.z; + + if( whichSide( pVertex ) == Back ) + return Back; + + nVertex.x = ( x > 0.0f ) ? aabb.minExtents.x : aabb.maxExtents.x; + nVertex.y = ( y > 0.0f ) ? aabb.minExtents.y : aabb.maxExtents.y; + nVertex.z = ( z > 0.0f ) ? aabb.minExtents.z : aabb.maxExtents.z; + + if( whichSide( nVertex ) == Front ) + return Front; + + return On; +} + +//----------------------------------------------------------------------------- + +inline PlaneF::Side PlaneF::whichSide( const OrientedBox3F& obb ) const +{ + // Project the box onto the line defined by the plane center and normal. + // See "3D Game Engine Design" chapter 4.3.2. + + const F32 r = obb.getHalfExtents().x * mFabs( mDot( obb.getAxis( 0 ), *this ) ) + + obb.getHalfExtents().y * mFabs( mDot( obb.getAxis( 1 ), *this ) ) + + obb.getHalfExtents().z * mFabs( mDot( obb.getAxis( 2 ), *this ) ); + + const F32 dist = distToPlane( obb.getCenter() ); + if( dist > r ) + return Front; + else if( dist < - r ) + return Back; + else + return On; +} + +//----------------------------------------------------------------------------- + +inline PlaneF::Side PlaneF::whichSideBox(const Point3F& center, + const Point3F& axisx, + const Point3F& axisy, + const Point3F& axisz) const +{ + F32 baseDist = distToPlane(center); + + F32 compDist = mFabs(mDot(axisx, *this)) + + mFabs(mDot(axisy, *this)) + + mFabs(mDot(axisz, *this)); + + if (baseDist >= compDist) + return Front; + else if (baseDist <= -compDist) + return Back; + else + return On; +} + +inline void PlaneF::set( F32 _x, F32 _y, F32 _z ) +{ + Point3F::set(_x,_y,_z); +} + +inline void PlaneF::set( F32 _x, F32 _y, F32 _z, F32 _d ) +{ + Point3F::set(_x,_y,_z); + d = _d; +} + +//--------------------------------------------------------------------------- +/// Calculate the coefficients of the plane passing through +/// a point with the given normal. +inline void PlaneF::setPoint(const Point3F &p) +{ + d = -(p.x * x + p.y * y + p.z * z); +} + +inline void PlaneF::set( const Point3F& p, const Point3F& n ) +{ + x = n.x; y = n.y; z = n.z; + normalize(); + + // Calculate the last plane coefficient. + + d = -(p.x * x + p.y * y + p.z * z); +} + +//--------------------------------------------------------------------------- +// Calculate the coefficients of the plane passing through +// three points. Basically it calculates the normal to the three +// points then calculates a plane through the middle point with that +// normal. +inline void PlaneF::set( const Point3F& k, const Point3F& j, const Point3F& l ) +{ + // Point3F kj, lj, pv; + // kj = k - j; + // lj = l - j; + // mCross( kj, lj, &pv ); + // set(j, pv); + + // Inline for speed... + const F32 ax = k.x-j.x; + const F32 ay = k.y-j.y; + const F32 az = k.z-j.z; + const F32 bx = l.x-j.x; + const F32 by = l.y-j.y; + const F32 bz = l.z-j.z; + x = ay*bz - az*by; + y = az*bx - ax*bz; + z = ax*by - ay*bx; + + m_point3F_normalize( (F32 *)(&x) ); + d = -(j.x * x + j.y * y + j.z * z); +} + +inline void PlaneF::invert() +{ + x = -x; + y = -y; + z = -z; + d = -d; +} + +inline void PlaneF::neg() +{ + invert(); +} + +inline F32 PlaneF::intersect(const Point3F &p1, const Point3F &p2) const +{ + const F32 den = mDot(p2 - p1, *this); + if( mIsZero( den ) ) + return PARALLEL_PLANE; + return -distToPlane(p1) / den; +} + +inline bool PlaneF::intersect( const Point3F &start, const Point3F &dir, Point3F *outHit ) const +{ + const F32 den = mDot( dir, *this ); + if ( mIsZero( den ) ) + return false; + + F32 dist = -distToPlane( start ) / den; + *outHit = start + dir * dist; + return true; +} + +class PlaneD: public Point3D +{ +public: + /// NOTE: d is the NEGATIVE distance along the xyz normal. + F64 d; + + PlaneD(); + PlaneD( const PlaneF& copy); + PlaneD( const Point3D& p, const Point3D& n ); + /// NOTE: d is the NEGATIVE distance along the xyz normal. + PlaneD( F64 _x, F64 _y, F64 _z, F64 _d); + PlaneD( const Point3D& j, const Point3D& k, const Point3D& l ); + + // Methods + //using Point3D::set; + void set(const F64 _x, const F64 _y, const F64 _z); + + void set( const Point3D& p, const Point3D& n); + void set( const Point3D& k, const Point3D& j, const Point3D& l ); + void setPoint(const Point3D &p); // assumes the x,y,z fields are already set + // creates an un-normalized plane + + void setXY(F64 zz); + void setYZ(F64 xx); + void setXZ(F64 yy); + void setXY(const Point3D& P, F64 dir); + void setYZ(const Point3D& P, F64 dir); + void setXZ(const Point3D& P, F64 dir); + void shiftX(F64 xx); + void shiftY(F64 yy); + void shiftZ(F64 zz); + void invert(); + void neg(); + Point3D project(const Point3D &pt) const; // projects the point onto the plane. + + F64 distToPlane( const Point3D& cp ) const; + + enum Side { + Front = 1, + On = 0, + Back = -1 + }; + + Side whichSide(const Point3D& cp) const; + F64 intersect(const Point3D &start, const Point3D &end) const; + //DLLAPI bool split( const Poly3F& poly, Poly3F* front, Poly3F* back ); + + bool isHorizontal() const; + bool isVertical() const; + + Side whichSideBox(const Point3D& center, + const Point3D& axisx, + const Point3D& axisy, + const Point3D& axisz) const; +}; +//#define PARALLEL_PLANE 1e20f + +//#define PlaneSwitchCode(s, e) (s * 3 + e) + + +//--------------------------------------------------------------------------- + +inline PlaneD::PlaneD() +{ +} + +inline PlaneD:: + PlaneD( F64 _x, F64 _y, F64 _z, F64 _d ) +{ + x = _x; y = _y; z = _z; d = _d; +} + +inline PlaneD::PlaneD( const PlaneF& copy) +{ + x = copy.x; y = copy.y; z = copy.z; d = copy.d; +} + +inline PlaneD::PlaneD( const Point3D& p, const Point3D& n ) +{ + set(p,n); +} + +inline PlaneD::PlaneD( const Point3D& j, const Point3D& k, const Point3D& l ) +{ + set(j,k,l); +} + +inline void PlaneD::setXY( F64 zz ) +{ + x = y = 0; z = 1; d = -zz; +} + +inline void PlaneD::setYZ( F64 xx ) +{ + x = 1; z = y = 0; d = -xx; +} + +inline void PlaneD::setXZ( F64 yy ) +{ + x = z = 0; y = 1; d = -yy; +} + +inline void PlaneD::setXY(const Point3D& point, F64 dir) // Normal = (0, 0, -1|1) +{ + x = y = 0; + d = -((z = dir) * point.z); +} + +inline void PlaneD::setYZ(const Point3D& point, F64 dir) // Normal = (-1|1, 0, 0) +{ + z = y = 0; + d = -((x = dir) * point.x); +} + +inline void PlaneD::setXZ(const Point3D& point, F64 dir) // Normal = (0, -1|1, 0) +{ + x = z = 0; + d = -((y = dir) * point.y); +} + +inline void PlaneD::shiftX( F64 xx ) +{ + d -= xx * x; +} + +inline void PlaneD::shiftY( F64 yy ) +{ + d -= yy * y; +} + +inline void PlaneD::shiftZ( F64 zz ) +{ + d -= zz * z; +} + +inline bool PlaneD::isHorizontal() const +{ + return (x == 0 && y == 0) ? true : false; +} + +inline bool PlaneD::isVertical() const +{ + return ((x != 0 || y != 0) && z == 0) ? true : false; +} + +inline Point3D PlaneD::project(const Point3D &pt) const +{ + F64 dist = distToPlane(pt); + return Point3D(pt.x - x * dist, pt.y - y * dist, pt.z - z * dist); +} + +inline F64 PlaneD::distToPlane( const Point3D& cp ) const +{ + // return mDot(*this,cp) + d; + return (x * cp.x + y * cp.y + z * cp.z) + d; +} + +inline PlaneD::Side PlaneD::whichSide(const Point3D& cp) const +{ + F64 dist = distToPlane(cp); + if (dist >= 0.005f) // if (mFabs(dist) < 0.005f) + return Front; // return On; + else if (dist <= -0.005f) // else if (dist > 0.0f) + return Back; // return Front; + else // else + return On; // return Back; +} + +inline void PlaneD::set(const F64 _x, const F64 _y, const F64 _z) +{ + Point3D::set(_x,_y,_z); +} + +//--------------------------------------------------------------------------- +// Calculate the coefficients of the plane passing through +// a point with the given normal. + +////inline void PlaneD::set( const Point3D& p, const Point3D& n ) +inline void PlaneD::setPoint(const Point3D &p) +{ + d = -(p.x * x + p.y * y + p.z * z); +} + +inline void PlaneD::set( const Point3D& p, const Point3D& n ) +{ + x = n.x; y = n.y; z = n.z; + normalize(); + + // Calculate the last plane coefficient. + + d = -(p.x * x + p.y * y + p.z * z); +} + +//--------------------------------------------------------------------------- +// Calculate the coefficients of the plane passing through +// three points. Basically it calculates the normal to the three +// points then calculates a plane through the middle point with that +// normal. + +inline void PlaneD::set( const Point3D& k, const Point3D& j, const Point3D& l ) +{ +// Point3D kj,lj,pv; +// kj = k; +// kj -= j; +// lj = l; +// lj -= j; +// mCross( kj, lj, &pv ); +// set(j,pv); + +// Above ends up making function calls up the %*#... +// This is called often enough to be a little more direct +// about it (sqrt should become intrinsic in the future)... + F64 ax = k.x-j.x; + F64 ay = k.y-j.y; + F64 az = k.z-j.z; + F64 bx = l.x-j.x; + F64 by = l.y-j.y; + F64 bz = l.z-j.z; + x = ay*bz - az*by; + y = az*bx - ax*bz; + z = ax*by - ay*bx; + F64 squared = x*x + y*y + z*z; + AssertFatal(squared != 0.0, "Error, no plane possible!"); + + // In non-debug mode + if (squared != 0) + { + F64 invSqrt = 1.0 / (F64)mSqrt(x*x + y*y + z*z); + x *= invSqrt; + y *= invSqrt; + z *= invSqrt; + d = -(j.x * x + j.y * y + j.z * z); + } + else + { + x = 0; + y = 0; + z = 1; + d = -(j.x * x + j.y * y + j.z * z); + } +} + +inline void PlaneD::invert() +{ + x = -x; + y = -y; + z = -z; + d = -d; +} + +inline void PlaneD::neg() +{ + invert(); +} + +inline F64 PlaneD::intersect(const Point3D &p1, const Point3D &p2) const +{ + F64 den = mDot(p2 - p1, *this); + if(den == 0) + return PARALLEL_PLANE; + return -distToPlane(p1) / den; +} + +inline PlaneD::Side PlaneD::whichSideBox(const Point3D& center, + const Point3D& axisx, + const Point3D& axisy, + const Point3D& axisz) const +{ + F64 baseDist = distToPlane(center); + + F64 compDist = mFabs(mDot(axisx, *this)) + + mFabs(mDot(axisy, *this)) + + mFabs(mDot(axisz, *this)); + + // Intersects + if (baseDist >= compDist) + return Front; + else if (baseDist <= -compDist) + return Back; + else + return On; + +// if (compDist > mFabs(baseDist)) +// return On; +// else +// return baseDist < 0.0 ? Back : Front; +} + +#endif // _MPLANE_H_ diff --git a/Engine/source/math/mPlaneSet.h b/Engine/source/math/mPlaneSet.h new file mode 100644 index 000000000..abeebd4dc --- /dev/null +++ b/Engine/source/math/mPlaneSet.h @@ -0,0 +1,480 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MPLANESET_H_ +#define _MPLANESET_H_ + +#ifndef _MPLANE_H_ +#include "math/mPlane.h" +#endif + +#ifndef _MBOX_H_ +#include "math/mBox.h" +#endif + +#ifndef _MSPHERE_H_ +#include "math/mSphere.h" +#endif + +#ifndef _MORIENTEDBOX_H_ +#include "math/mOrientedBox.h" +#endif + +#ifndef _TEMPALLOC_H_ +#include "util/tempAlloc.h" +#endif + +#ifndef _TALGORITHM_H_ +#include "core/tAlgorithm.h" +#endif + + +/// Set of planes which can be tested against bounding volumes. +/// +/// This class is meant as a helper to collect functionality for working with sets +/// of planes. As a helper, it does not define means to manage the data it uses. +/// +/// @warn This class does not manage the memory needed for the set. +template< typename T > +class PlaneSet +{ + protected: + + /// Number of planes in #mPlanes. + U32 mNumPlanes; + + /// Set of planes. The memory for this array is managed outside + /// this class. + const T* mPlanes; + + template< typename P > OverlapTestResult _testOverlap( const P& bounds ) const; + template< typename P > bool _isContained( const P& bounds ) const; + + public: + + /// @name Constructors + /// @{ + + /// Create an uninitialized set. + /// @warn None of the members will be initialized. + PlaneSet() {} + + /// Use the given set of planes to initialize the set. + /// + /// @param planes The planes. Memory must be valid for the entire + /// lifetime of the plane set. + /// @param numPlanes Number of planes. + PlaneSet( const T* planes, U32 numPlanes ) + : mNumPlanes( numPlanes ), mPlanes( planes ) {} + + /// @} + + /// @name Accessors + /// @{ + + /// Return the number of planes that this set consists of. + U32 getNumPlanes() const { return mNumPlanes; } + + /// Return the array of planes for this set. + const T* getPlanes() const { return mPlanes; } + + /// @} + + /// @name Intersection + /// All of these intersection methods are approximate in that they can produce + /// false positives on GeometryIntersecting. For precise results, use Intersector. + /// @{ + + /// Test intersection of the given AABB with the volume defined by the plane set. + /// + /// @param aabb Axis-aligned bounding box. + /// @return The type of overlap that the given AABB has with the plane set. + /// + /// @note The method will not test for those edge cases where none of the planes + /// rejects the given AABB yet the AABB is actually outside the volume defined by the planes. + /// This speeds up the test at the cost of losing precision which is acceptable for + /// cases where doing the additional tests for all intersecting objects will generally + /// waste more time than accepting a few additional non-intersecting objects as + /// intersecting. + OverlapTestResult testPotentialIntersection( const Box3F& aabb ) const + { + return _testOverlap( aabb ); + } + + /// Test intersection of the given sphere with the volume defined by the plane set. + /// + /// @param sphere Sphere. + /// @return The type of overlap that the given sphere has with the plane set. + /// + /// @note The method will not test for those edge cases where none of the planes + /// rejects the given sphere yet the sphere is actually outside the volume defined by the planes. + /// This speeds up the test at the cost of losing precision which is acceptable for + /// cases where doing the additional tests for all intersecting objects will generally + /// waste more time than accepting a few additional non-intersecting objects as + /// intersecting. + OverlapTestResult testPotentialIntersection( const SphereF& sphere ) const + { + return _testOverlap( sphere ); + } + + /// Test intersection of the given OBB with the volume defined by the plane set. + /// + /// @param obb Oriented bounding box. + /// @return The type of overlap that the given OBB has with the plane set. + /// + /// @note The method will not test for those edge cases where none of the planes + /// rejects the given OBB yet the OBB is actually outside the volume defined by the planes. + /// This speeds up the test at the cost of losing precision which is acceptable for + /// cases where doing the additional tests for all intersecting objects will generally + /// waste more time than accepting a few additional non-intersecting objects as + /// intersecting. + OverlapTestResult testPotentialIntersection( const OrientedBox3F& obb ) const + { + return _testOverlap( obb ); + } + + /// Returns a bitmask of which planes are hit by the given box. + U32 testPlanes( const Box3F& bounds, U32 planeMask = 0xFFFFFFFF, F32 expand = 0.0f ) const; + + /// @} + + /// @name Containment + /// Testing for containment of geometric shapes within the volume enclosed by the planes. + /// @{ + + /// Return true if the given point lies on the positive side of all the planes + /// in the set. + bool isContained( const Point3F& point, F32 epsilon = __EQUAL_CONST_F ) const; + + /// Return true if all of the given points lie on the positive side of all the planes + /// in the set. + bool isContained( const Point3F* points, U32 numPoints ) const; + + /// Return true if the given AABB lies on the positive side of all the planes + /// in the set. + bool isContained( const Box3F& aabb ) const { return _isContained( aabb ); } + + /// Return true if the given sphere lies on the positive side of all the planes + /// in the set. + bool isContained( const SphereF& sphere ) const { return _isContained( sphere ); } + + /// Return true if the given OBB lies on the positive side of all the planes + /// in the set. + bool isContained( const OrientedBox3F& obb ) const { return _isContained( obb ); } + + /// @} + + /// @name Clipping + /// @{ + + /// Clip the given line segment against the plane set. If the segment + /// intersects with any of the planes, the points will be clipped at the + /// intersection. + /// + /// @return True if any part of the segment is inside the volume defined by the plane set. + bool clipSegment( Point3F& pnt0, Point3F& pnt1 ) const; + + /// Clip a convex polygon by all planes in the set. + /// + /// @param inVertices Array holding the vertices of the input polygon. + /// @param inNumVertices Number of vertices in @a inVertices. + /// @param outVertices Array to hold the vertices of the clipped polygon. Must have spaces for + /// @a inNumVertices + numberOfPlanesInSet. Must not be the same as @a inVertices. + /// @param maxOutVertices Maximum number of vertices that can be stored in @a outVertices. If insufficient to + /// store the clipped polygon, the return value will be 0. + /// + /// @return Number of vertices in the clipped polygon, i.e. number of vertices stored in @a outVertices. + /// + /// @note Be aware that if the polygon fully lies on the negative side of all planes, + /// the resulting @a outNumVertices will be zero, i.e. no polygon will result from the clip. + U32 clipPolygon( const Point3F* inVertices, U32 inNumVertices, Point3F* outVertices, U32 maxOutVertices ) const; + + /// @} +}; + +typedef PlaneSet< PlaneF > PlaneSetF; +typedef PlaneSet< PlaneD > PlaneSetD; + + +//----------------------------------------------------------------------------- + +template< typename T > +template< typename P > +inline OverlapTestResult PlaneSet< T >::_testOverlap( const P& bounds ) const +{ + bool allInside = true; + + // First, find out whether there is any plane for which the bounds completely + // lie on the negative side. If so, the bounds are clearly outside of the volume. + + const U32 numPlanes = getNumPlanes(); + for( U32 nplane = 0; nplane < numPlanes; ++ nplane ) + { + const PlaneF& plane = getPlanes()[ nplane ]; + + const PlaneF::Side side = plane.whichSide( bounds ); + if( side == PlaneF::Back ) + return GeometryOutside; + + if( side != PlaneF::Front ) + allInside = false; + } + + // Test for containment. + + if( allInside ) + return GeometryInside; + + // Otherwise classify as intersecting. + + return GeometryIntersecting; +} + +//----------------------------------------------------------------------------- + +template< typename T > +inline bool PlaneSet< T >::isContained( const Point3F& point, F32 epsilon ) const +{ + epsilon = - epsilon; + + for( U32 i = 0; i < mNumPlanes; ++ i ) + { + const F32 dist = mPlanes[ i ].distToPlane( point ); + if( dist < epsilon ) + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- + +template< typename T > +inline bool PlaneSet< T >::isContained( const Point3F* points, U32 numPoints ) const +{ + for( U32 i = 0; i < numPoints; ++ i ) + if( !isContained( points[ i ] ) ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- + +template< typename T > +template< typename P > +inline bool PlaneSet< T >::_isContained( const P& bounds ) const +{ + for( U32 i = 0; i < mNumPlanes; ++ i ) + if( mPlanes[ i ].whichSide( bounds ) != PlaneF::Front ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- + +template< typename T > +U32 PlaneSet< T >::testPlanes( const Box3F& bounds, U32 planeMask, F32 expand ) const +{ + AssertFatal( mNumPlanes <= 32, "PlaneSet::testPlanes - Too many planes in set!" ); + + // This is based on the paper "A Faster Overlap Test for a Plane and a Bounding Box" + // by Kenny Hoff. See http://www.cs.unc.edu/~hoff/research/vfculler/boxplane.html + + U32 retMask = 0; + + const U32 numPlanes = getMin( mNumPlanes, U32( 32 ) ); + for ( S32 i = 0; i < numPlanes; i++ ) + { + U32 mask = ( 1 << i ); + + if ( !( planeMask & mask ) ) + continue; + + const PlaneF& plane = mPlanes[ i ]; + + Point3F minPoint, maxPoint; + + if( plane.x > 0 ) + { + maxPoint.x = bounds.maxExtents.x; + minPoint.x = bounds.minExtents.x; + } + else + { + maxPoint.x = bounds.minExtents.x; + minPoint.x = bounds.maxExtents.x; + } + + if( plane.y > 0 ) + { + maxPoint.y = bounds.maxExtents.y; + minPoint.y = bounds.minExtents.y; + } + else + { + maxPoint.y = bounds.minExtents.y; + minPoint.y = bounds.maxExtents.y; + } + + if( plane.z > 0 ) + { + maxPoint.z = bounds.maxExtents.z; + minPoint.z = bounds.minExtents.z; + } + else + { + maxPoint.z = bounds.minExtents.z; + minPoint.z = bounds.maxExtents.z; + } + + F32 maxDot = mDot( maxPoint, plane ); + + if( maxDot <= - ( plane.d + expand ) ) + return -1; + + F32 minDot = mDot( minPoint, plane ); + + if( ( minDot + plane.d ) < 0.0f ) + retMask |= mask; + } + + return retMask; +} + +//----------------------------------------------------------------------------- + +template< typename T > +bool PlaneSet< T >::clipSegment( Point3F &pnt0, Point3F &pnt1 ) const +{ + F32 tmin = F32_MAX; + F32 tmax = -F32_MAX; + U32 hitCount = 0; + Point3F tpnt; + + const U32 numPlanes = mNumPlanes; + for( U32 i = 0; i < numPlanes; ++ i ) + { + const PlaneF &plane = mPlanes[ i ]; + + F32 t = plane.intersect( pnt0, pnt1 ); + + if( t >= 0.0f && t <= 1.0f ) + { + tpnt.interpolate( pnt0, pnt1, t ); + + if ( isContained( tpnt, 1.0e-004f ) ) + { + tmin = getMin( tmin, t ); + tmax = getMax( tmax, t ); + hitCount ++; + } + } + } + + // If we had no intersections then either both points are inside or both are outside. + + if( hitCount == 0 ) + return isContained( pnt0 ); + + // If we had one intersection then we have one point inside. + // tmin and tmax are the same here. + if( hitCount == 1 ) + { + if( isContained( pnt0 ) ) + pnt1.interpolate( pnt0, pnt1, tmax ); + else + pnt0.interpolate( pnt0, pnt1, tmin ); + } + else + { + Point3F prevPnt0( pnt0 ); + Point3F prevPnt1( pnt1 ); + + if( tmin < F32_MAX ) + pnt0.interpolate( prevPnt0, prevPnt1, tmin ); + if( tmax > -F32_MAX ) + pnt1.interpolate( prevPnt0, prevPnt1, tmax ); + } + + return true; +} + +//----------------------------------------------------------------------------- + +template< typename T > +U32 PlaneSet< T >::clipPolygon( const Point3F* inVertices, U32 inNumVertices, Point3F* outVertices, U32 maxOutVertices ) const +{ + TempAlloc< Point3F > tempBuffer( inNumVertices + mNumPlanes ); + + // We use two buffers as interchanging roles as source and target. + // For the first iteration, inVertices is the source. + + Point3F* tempPolygon = tempBuffer; + Point3F* clippedPolygon = const_cast< Point3F* >( inVertices ); + + U32 numClippedPolygonVertices = inNumVertices; + U32 numTempPolygonVertices = 0; + + for( U32 nplane = 0; nplane < mNumPlanes; ++ nplane ) + { + // Make the output of the last iteration the + // input of this iteration. + + swap( tempPolygon, clippedPolygon ); + numTempPolygonVertices = numClippedPolygonVertices; + + if( maxOutVertices < numTempPolygonVertices + 1 ) + return 0; + + // Clip our current remainder of the original polygon + // against the current plane. + + const PlaneF& plane = mPlanes[ nplane ]; + numClippedPolygonVertices = plane.clipPolygon( tempPolygon, numTempPolygonVertices, clippedPolygon ); + + // If the polygon was completely on the backside of the plane, + // then polygon is outside the frustum. In this case, return false + // to indicate we haven't clipped anything. + + if( !numClippedPolygonVertices ) + return false; + + // On first iteration, replace the inVertices with the + // outVertices buffer. + + if( tempPolygon == inVertices ) + tempPolygon = outVertices; + } + + // If outVertices isn't the target buffer of the last + // iteration, copy the vertices over from the temporary + // buffer. + + if( clippedPolygon != outVertices ) + dMemcpy( outVertices, clippedPolygon, numClippedPolygonVertices * sizeof( Point3F ) ); + + return numClippedPolygonVertices; +} + +#endif // !_MPLANESET_H_ diff --git a/Engine/source/math/mPlaneTransformer.cpp b/Engine/source/math/mPlaneTransformer.cpp new file mode 100644 index 000000000..32ddc209b --- /dev/null +++ b/Engine/source/math/mPlaneTransformer.cpp @@ -0,0 +1,71 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "math/mPlaneTransformer.h" +#include "math/mMathFn.h" + +void PlaneTransformer::set(const MatrixF& xform, const Point3F& scale) +{ + mTransform = xform; + mScale = scale; + + MatrixF scaleMat(true); + F32* m = scaleMat; + m[MatrixF::idx(0, 0)] = scale.x; + m[MatrixF::idx(1, 1)] = scale.y; + m[MatrixF::idx(2, 2)] = scale.z; + + mTransposeInverse = xform; + mTransposeInverse.mul(scaleMat); + mTransposeInverse.transpose(); + mTransposeInverse.inverse(); +} + +void PlaneTransformer::transform(const PlaneF& plane, PlaneF& result) +{ + Point3F point = plane; + point *= -plane.d; + point.convolve(mScale); + mTransform.mulP(point); + + Point3F normal = plane; + mTransposeInverse.mulV(normal); + + result.set(point, normal); +// mTransformPlane(mTransform, mScale, plane, &result); +} + +void PlaneTransformer::setIdentity() +{ + static struct MakeIdentity + { + PlaneTransformer transformer; + MakeIdentity() + { + MatrixF defMat(true); + Point3F defScale(1.0f, 1.0f, 1.0f); + transformer.set(defMat, defScale); + } + } sMakeIdentity; + + *this = sMakeIdentity.transformer; +} diff --git a/Engine/source/math/mPlaneTransformer.h b/Engine/source/math/mPlaneTransformer.h new file mode 100644 index 000000000..8f1adb65a --- /dev/null +++ b/Engine/source/math/mPlaneTransformer.h @@ -0,0 +1,49 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MPLANETRANSFORMER_H_ +#define _MPLANETRANSFORMER_H_ + +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _MPLANE_H_ +#include "math/mPlane.h" +#endif + +// ========================================================= +class PlaneTransformer +{ + MatrixF mTransform; + MatrixF mTransposeInverse; + Point3F mScale; + + public: + void set(const MatrixF& xform, const Point3F& scale); + void transform(const PlaneF& plane, PlaneF& result); + void setIdentity(); +}; + +#endif diff --git a/Engine/source/math/mPoint.cpp b/Engine/source/math/mPoint.cpp new file mode 100644 index 000000000..6cebb69d7 --- /dev/null +++ b/Engine/source/math/mPoint.cpp @@ -0,0 +1,80 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "math/mPoint2.h" +#include "math/mPoint3.h" +#include "math/mPoint4.h" + + +const Point2I Point2I::One(1, 1); +const Point2I Point2I::Zero(0, 0); +const Point2I Point2I::Min(S32_MIN, S32_MIN); +const Point2I Point2I::Max(S32_MAX, S32_MAX); + +const Point2F Point2F::One(1.0f, 1.0f); +const Point2F Point2F::Zero(0.0f, 0.0f); +const Point2F Point2F::Min(F32_MIN, F32_MIN); +const Point2F Point2F::Max(F32_MAX, F32_MAX); + +const Point2D Point2D::One(1.0, 1.0); +const Point2D Point2D::Zero(0.0, 0.0); + +const Point3I Point3I::One(1, 1, 1); +const Point3I Point3I::Zero(0, 0, 0); + +const Point3F Point3F::One(1.0f, 1.0f, 1.0f); +const Point3F Point3F::Zero(0.0f, 0.0f, 0.0f); +const Point3F Point3F::Min(F32_MIN, F32_MIN, F32_MIN); +const Point3F Point3F::Max(F32_MAX, F32_MAX, F32_MAX); +const Point3F Point3F::UnitX(1.0f, 0.0f, 0.0f); +const Point3F Point3F::UnitY(0.0f, 1.0f, 0.0f); +const Point3F Point3F::UnitZ(0.0f, 0.0f, 1.0f); + +const Point3D Point3D::One(1.0, 1.0, 1.0); +const Point3D Point3D::Zero(0.0, 0.0, 0.0); + +const Point4I Point4I::One(1, 1, 1, 1); +const Point4I Point4I::Zero(0, 0, 0, 0); + +const Point4F Point4F::One(1.0f, 1.0f, 1.0f, 1.0f); +const Point4F Point4F::Zero(0.0f, 0.0f, 0.0f, 0.0f); + + +Point3F mPerp( const Point3F &inVec ) +{ + AssertFatal( inVec.len() > 0.0f, "mPerp() - zero length vector has no perp!" ); + AssertFatal( inVec.isUnitLength(), "mPerp() - passed vector must be normalized!" ); + + U32 idx = inVec.getLeastComponentIndex(); + + Point3F vec( 0.0f, 0.0f, 0.0f ); + vec[idx] = 1.0f; + + Point3F outVec = mCross( inVec, vec ); + outVec.normalize(); + + //AssertFatal( mIsZero( mDot( inVec, outVec ) ), "mPerp, failed to generate perpendicular" ); + + return outVec; +} + diff --git a/Engine/source/math/mPoint2.h b/Engine/source/math/mPoint2.h new file mode 100644 index 000000000..a62b93395 --- /dev/null +++ b/Engine/source/math/mPoint2.h @@ -0,0 +1,899 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MPOINT2_H_ +#define _MPOINT2_H_ + +#ifndef _MMATHFN_H_ +#include "math/mMathFn.h" +#endif + +//------------------------------------------------------------------------------ +/// 2D integer point +/// +/// Uses S32 internally. +class Point2I +{ + //-------------------------------------- Public data + public: + S32 x; ///< X position + S32 y; ///< Y position + + //-------------------------------------- Public interface + public: + Point2I(); ///< Create an uninitialized point. + Point2I(const Point2I&); ///< Copy constructor + Point2I(S32 in_x, S32 in_y); ///< Create point from two co-ordinates. + + //-------------------------------------- Non-math mutators and misc functions + void set(S32 in_x, S32 in_y); ///< Set (x,y) position + void setMin(const Point2I&); ///< Store lesser co-ordinates from parameter in this point. + void setMax(const Point2I&); ///< Store greater co-ordinates from parameter in this point. + + //-------------------------------------- Math mutators + void neg(); ///< Invert sign of point's co-ordinates. + void convolve(const Point2I&); ///< Convolve this point by parameter. + + //-------------------------------------- Queries + bool isZero() const; ///< Is this point at the origin? (No epsilon used) + F32 len() const; ///< Get the length of the point + S32 lenSquared() const; ///< Get the length-squared of the point + + //-------------------------------------- Overloaded operators + public: + operator S32*() { return &x; } + operator const S32*() const { return &x; } + + // Comparison operators + bool operator==(const Point2I&) const; + bool operator!=(const Point2I&) const; + + // Arithmetic w/ other points + Point2I operator+(const Point2I&) const; + Point2I operator-(const Point2I&) const; + Point2I& operator+=(const Point2I&); + Point2I& operator-=(const Point2I&); + + // Arithmetic w/ scalars + Point2I operator*(S32) const; + Point2I& operator*=(S32); + Point2I operator/(S32) const; + Point2I& operator/=(S32); + + Point2I operator*(const Point2I&) const; + Point2I& operator*=(const Point2I&); + Point2I operator/(const Point2I&) const; + Point2I& operator/=(const Point2I&); + + // Unary operators + Point2I operator-() const; + + //-------------------------------------- Public static constants + public: + const static Point2I One; + const static Point2I Zero; + const static Point2I Min; + const static Point2I Max; +}; + +//------------------------------------------------------------------------------ +/// 2D floating-point point. +class Point2F +{ + //-------------------------------------- Public data + public: + F32 x; + F32 y; + + public: + Point2F(); ///< Create uninitialized point. + Point2F(const Point2F&); ///< Copy constructor + Point2F(F32 _x, F32 _y); ///< Create point from co-ordinates. + + //-------------------------------------- Non-math mutators and misc functions + public: + void set(F32 _x, F32 _y); ///< Set point's co-ordinates. + + void setMin(const Point2F&); ///< Store lesser co-ordinates. + void setMax(const Point2F&); ///< Store greater co-ordinates. + + /// Interpolate from a to b, based on c. + /// + /// @param a Starting point. + /// @param b Ending point. + /// @param c Interpolation factor (0.0 .. 1.0). + void interpolate(const Point2F& a, const Point2F& b, const F32 c); + + operator F32*() { return &x; } + operator const F32*() const { return &x; } + + //-------------------------------------- Queries + public: + bool isZero() const; ///< Check for zero coordinates. (No epsilon.) + F32 len() const; ///< Get length. + F32 lenSquared() const; ///< Get squared length (one sqrt less than len()). + bool equal( const Point2F &compare ) const; ///< Is compare within POINT_EPSILON of all of the component of this point + F32 magnitudeSafe() const; + + //-------------------------------------- Mathematical mutators + public: + void neg(); ///< Invert signs of co-ordinates. + void normalize(); ///< Normalize vector. + void normalize(F32 val); ///< Normalize, scaling by val. + void normalizeSafe(); + void convolve(const Point2F&); ///< Convolve by parameter. + void convolveInverse(const Point2F&); ///< Inversely convolute by parameter. (ie, divide) + void rotate( F32 radians ); ///< Rotate vector around origin. + + /// Return a perpendicular vector to this one. The result is equivalent to rotating the + /// vector 90 degrees clockwise around the origin. + Point2F getPerpendicular() const { return Point2F( y, - x ); } + + //-------------------------------------- Overloaded operators + public: + // Comparison operators + bool operator==(const Point2F&) const; + bool operator!=(const Point2F&) const; + + // Arithmetic w/ other points + Point2F operator+(const Point2F&) const; + Point2F operator-(const Point2F&) const; + Point2F& operator+=(const Point2F&); + Point2F& operator-=(const Point2F&); + + // Arithmetic w/ scalars + Point2F operator*(F32) const; + Point2F operator/(F32) const; + Point2F& operator*=(F32); + Point2F& operator/=(F32); + + Point2F operator*(const Point2F&) const; + Point2F& operator*=(const Point2F&); + Point2F operator/(const Point2F&) const; + Point2F& operator/=(const Point2F&); + + // Unary operators + Point2F operator-() const; + + //-------------------------------------- Public static constants + public: + const static Point2F One; + const static Point2F Zero; + const static Point2F Min; + const static Point2F Max; +}; + + +//------------------------------------------------------------------------------ +/// 2D high-precision point. +/// +/// Uses F64 internally. +class Point2D +{ + //-------------------------------------- Public data + public: + F64 x; ///< X co-ordinate. + F64 y; ///< Y co-ordinate. + + public: + Point2D(); ///< Create uninitialized point. + Point2D(const Point2D&); ///< Copy constructor + Point2D(F64 _x, F64 _y); ///< Create point from coordinates. + + //-------------------------------------- Non-math mutators and misc functions + public: + void set(F64 _x, F64 _y); ///< Set point's coordinates. + + void setMin(const Point2D&); ///< Store lesser co-ordinates. + void setMax(const Point2D&); ///< Store greater co-ordinates. + + /// Interpolate from a to b, based on c. + /// + /// @param a Starting point. + /// @param b Ending point. + /// @param c Interpolation factor (0.0 .. 1.0). + void interpolate(const Point2D &a, const Point2D &b, const F64 c); + + operator F64*() { return &x; } + operator const F64*() const { return &x; } + + //-------------------------------------- Queries + public: + bool isZero() const; + F64 len() const; + F64 lenSquared() const; + + //-------------------------------------- Mathematical mutators + public: + void neg(); + void normalize(); + void normalize(F64 val); + void convolve(const Point2D&); + void convolveInverse(const Point2D&); + + //-------------------------------------- Overloaded operators + public: + // Comparison operators + bool operator==(const Point2D&) const; + bool operator!=(const Point2D&) const; + + // Arithmetic w/ other points + Point2D operator+(const Point2D&) const; + Point2D operator-(const Point2D&) const; + Point2D& operator+=(const Point2D&); + Point2D& operator-=(const Point2D&); + + // Arithmetic w/ scalars + Point2D operator*(F64) const; + Point2D operator/(F64) const; + Point2D& operator*=(F64); + Point2D& operator/=(F64); + + // Unary operators + Point2D operator-() const; + + //-------------------------------------- Public static constants + public: + const static Point2D One; + const static Point2D Zero; +}; + +//------------------------------------------------------------------------------ +//-------------------------------------- Point2I +// +inline Point2I::Point2I() +{ + // +} + + +inline Point2I::Point2I(const Point2I& _copy) + : x(_copy.x), y(_copy.y) +{ + // +} + + +inline Point2I::Point2I(S32 _x, S32 _y) + : x(_x), y(_y) +{ + // +} + + +inline void Point2I::set(S32 _x, S32 _y) +{ + x = _x; + y = _y; +} + + +inline void Point2I::setMin(const Point2I& _test) +{ + x = (_test.x < x) ? _test.x : x; + y = (_test.y < y) ? _test.y : y; +} + + +inline void Point2I::setMax(const Point2I& _test) +{ + x = (_test.x > x) ? _test.x : x; + y = (_test.y > y) ? _test.y : y; +} + + +inline void Point2I::neg() +{ + x = -x; + y = -y; +} + +inline void Point2I::convolve(const Point2I& c) +{ + x *= c.x; + y *= c.y; +} + +inline bool Point2I::isZero() const +{ + return ((x == 0) && (y == 0)); +} + + +inline F32 Point2I::len() const +{ + return mSqrt(F32(x*x + y*y)); +} + +inline S32 Point2I::lenSquared() const +{ + return x*x + y*y; +} + +inline bool Point2I::operator==(const Point2I& _test) const +{ + return ((x == _test.x) && (y == _test.y)); +} + + +inline bool Point2I::operator!=(const Point2I& _test) const +{ + return (operator==(_test) == false); +} + + +inline Point2I Point2I::operator+(const Point2I& _add) const +{ + return Point2I(x + _add.x, y + _add.y); +} + + +inline Point2I Point2I::operator-(const Point2I& _rSub) const +{ + return Point2I(x - _rSub.x, y - _rSub.y); +} + + +inline Point2I& Point2I::operator+=(const Point2I& _add) +{ + x += _add.x; + y += _add.y; + + return *this; +} + + +inline Point2I& Point2I::operator-=(const Point2I& _rSub) +{ + x -= _rSub.x; + y -= _rSub.y; + + return *this; +} + + +inline Point2I Point2I::operator-() const +{ + return Point2I(-x, -y); +} + + +inline Point2I Point2I::operator*(S32 mul) const +{ + return Point2I(x * mul, y * mul); +} + +inline Point2I Point2I::operator/(S32 div) const +{ + AssertFatal(div != 0, "Error, div by zero attempted"); + return Point2I(x/div, y/div); +} + + +inline Point2I& Point2I::operator*=(S32 mul) +{ + x *= mul; + y *= mul; + + return *this; +} + + +inline Point2I& Point2I::operator/=(S32 div) +{ + AssertFatal(div != 0, "Error, div by zero attempted"); + + x /= div; + y /= div; + + return *this; +} + +inline Point2I Point2I::operator*(const Point2I &_vec) const +{ + return Point2I(x * _vec.x, y * _vec.y); +} + +inline Point2I& Point2I::operator*=(const Point2I &_vec) +{ + x *= _vec.x; + y *= _vec.y; + return *this; +} + +inline Point2I Point2I::operator/(const Point2I &_vec) const +{ + return Point2I(x / _vec.x, y / _vec.y); +} + +inline Point2I& Point2I::operator/=(const Point2I &_vec) +{ + x /= _vec.x; + y /= _vec.y; + return *this; +} + +//------------------------------------------------------------------------------ +//-------------------------------------- Point2F +// +inline Point2F::Point2F() +{ + // +} + + +inline Point2F::Point2F(const Point2F& _copy) + : x(_copy.x), y(_copy.y) +{ + // +} + + +inline Point2F::Point2F(F32 _x, F32 _y) + : x(_x), y(_y) +{ +} + + +inline void Point2F::set(F32 _x, F32 _y) +{ + x = _x; + y = _y; +} + + +inline void Point2F::setMin(const Point2F& _test) +{ + x = (_test.x < x) ? _test.x : x; + y = (_test.y < y) ? _test.y : y; +} + + +inline void Point2F::setMax(const Point2F& _test) +{ + x = (_test.x > x) ? _test.x : x; + y = (_test.y > y) ? _test.y : y; +} + + +inline void Point2F::interpolate(const Point2F& _rFrom, const Point2F& _to, const F32 _factor) +{ + AssertFatal(_factor >= 0.0f && _factor <= 1.0f, "Out of bound interpolation factor"); + x = (_rFrom.x * (1.0f - _factor)) + (_to.x * _factor); + y = (_rFrom.y * (1.0f - _factor)) + (_to.y * _factor); +} + + +inline bool Point2F::isZero() const +{ + return (x == 0.0f) && (y == 0.0f); +} + + +inline F32 Point2F::lenSquared() const +{ + return (x * x) + (y * y); +} + + +inline bool Point2F::equal( const Point2F &compare ) const +{ + return( ( mFabs( x - compare.x ) < POINT_EPSILON ) && + ( mFabs( y - compare.y ) < POINT_EPSILON ) ); +} + + +inline void Point2F::neg() +{ + x = -x; + y = -y; +} + +inline void Point2F::convolve(const Point2F& c) +{ + x *= c.x; + y *= c.y; +} + + +inline void Point2F::convolveInverse(const Point2F& c) +{ + x /= c.x; + y /= c.y; +} + +inline void Point2F::rotate( F32 radians ) +{ + F32 sinTheta, cosTheta; + mSinCos( radians, sinTheta, cosTheta ); + + set( cosTheta * x - sinTheta * y, + sinTheta * x + cosTheta * y ); +} + +inline bool Point2F::operator==(const Point2F& _test) const +{ + return (x == _test.x) && (y == _test.y); +} + + +inline bool Point2F::operator!=(const Point2F& _test) const +{ + return operator==(_test) == false; +} + + +inline Point2F Point2F::operator+(const Point2F& _add) const +{ + return Point2F(x + _add.x, y + _add.y); +} + + +inline Point2F Point2F::operator-(const Point2F& _rSub) const +{ + return Point2F(x - _rSub.x, y - _rSub.y); +} + + +inline Point2F& Point2F::operator+=(const Point2F& _add) +{ + x += _add.x; + y += _add.y; + + return *this; +} + + +inline Point2F& Point2F::operator-=(const Point2F& _rSub) +{ + x -= _rSub.x; + y -= _rSub.y; + + return *this; +} + + +inline Point2F Point2F::operator*(F32 _mul) const +{ + return Point2F(x * _mul, y * _mul); +} + + +inline Point2F Point2F::operator/(F32 _div) const +{ + AssertFatal(_div != 0.0f, "Error, div by zero attempted"); + + F32 inv = 1.0f / _div; + + return Point2F(x * inv, y * inv); +} + + +inline Point2F& Point2F::operator*=(F32 _mul) +{ + x *= _mul; + y *= _mul; + + return *this; +} + + +inline Point2F& Point2F::operator/=(F32 _div) +{ + AssertFatal(_div != 0.0f, "Error, div by zero attempted"); + + F32 inv = 1.0f / _div; + + x *= inv; + y *= inv; + + return *this; +} + +inline Point2F Point2F::operator*(const Point2F &_vec) const +{ + return Point2F(x * _vec.x, y * _vec.y); +} + +inline Point2F& Point2F::operator*=(const Point2F &_vec) +{ + x *= _vec.x; + y *= _vec.y; + return *this; +} + +inline Point2F Point2F::operator/(const Point2F &_vec) const +{ + return Point2F(x / _vec.x, y / _vec.y); +} + +inline Point2F& Point2F::operator/=(const Point2F &_vec) +{ + x /= _vec.x; + y /= _vec.y; + return *this; +} + +inline Point2F Point2F::operator-() const +{ + return Point2F(-x, -y); +} + +inline F32 Point2F::len() const +{ + return mSqrt(x*x + y*y); +} + +inline void Point2F::normalize() +{ + m_point2F_normalize(*this); +} + +inline void Point2F::normalize(F32 val) +{ + m_point2F_normalize_f(*this, val); +} + +inline F32 Point2F::magnitudeSafe() const +{ + if( isZero() ) + return 0.0f; + else + return len(); +} + +inline void Point2F::normalizeSafe() +{ + F32 vmag = magnitudeSafe(); + + if( vmag > POINT_EPSILON ) + *this *= F32(1.0 / vmag); +} + +//------------------------------------------------------------------------------ +//-------------------------------------- Point2D +// +inline Point2D::Point2D() +{ + // +} + + +inline Point2D::Point2D(const Point2D& _copy) + : x(_copy.x), y(_copy.y) +{ + // +} + + +inline Point2D::Point2D(F64 _x, F64 _y) + : x(_x), y(_y) +{ +} + + +inline void Point2D::set(F64 _x, F64 _y) +{ + x = _x; + y = _y; +} + + +inline void Point2D::setMin(const Point2D& _test) +{ + x = (_test.x < x) ? _test.x : x; + y = (_test.y < y) ? _test.y : y; +} + + +inline void Point2D::setMax(const Point2D& _test) +{ + x = (_test.x > x) ? _test.x : x; + y = (_test.y > y) ? _test.y : y; +} + + +inline void Point2D::interpolate(const Point2D& _rFrom, const Point2D& _to, const F64 _factor) +{ + AssertFatal(_factor >= 0.0f && _factor <= 1.0f, "Out of bound interpolation factor"); + x = (_rFrom.x * (1.0f - _factor)) + (_to.x * _factor); + y = (_rFrom.y * (1.0f - _factor)) + (_to.y * _factor); +} + + +inline bool Point2D::isZero() const +{ + return (x == 0.0f) && (y == 0.0f); +} + + +inline F64 Point2D::lenSquared() const +{ + return (x * x) + (y * y); +} + + +inline void Point2D::neg() +{ + x = -x; + y = -y; +} + +inline void Point2D::convolve(const Point2D& c) +{ + x *= c.x; + y *= c.y; +} + +inline void Point2D::convolveInverse(const Point2D& c) +{ + x /= c.x; + y /= c.y; +} + +inline bool Point2D::operator==(const Point2D& _test) const +{ + return (x == _test.x) && (y == _test.y); +} + + +inline bool Point2D::operator!=(const Point2D& _test) const +{ + return operator==(_test) == false; +} + + +inline Point2D Point2D::operator+(const Point2D& _add) const +{ + return Point2D(x + _add.x, y + _add.y); +} + + +inline Point2D Point2D::operator-(const Point2D& _rSub) const +{ + return Point2D(x - _rSub.x, y - _rSub.y); +} + + +inline Point2D& Point2D::operator+=(const Point2D& _add) +{ + x += _add.x; + y += _add.y; + + return *this; +} + + +inline Point2D& Point2D::operator-=(const Point2D& _rSub) +{ + x -= _rSub.x; + y -= _rSub.y; + + return *this; +} + + +inline Point2D Point2D::operator*(F64 _mul) const +{ + return Point2D(x * _mul, y * _mul); +} + + +inline Point2D Point2D::operator/(F64 _div) const +{ + AssertFatal(_div != 0.0f, "Error, div by zero attempted"); + + F64 inv = 1.0f / _div; + + return Point2D(x * inv, y * inv); +} + + +inline Point2D& Point2D::operator*=(F64 _mul) +{ + x *= _mul; + y *= _mul; + + return *this; +} + + +inline Point2D& Point2D::operator/=(F64 _div) +{ + AssertFatal(_div != 0.0f, "Error, div by zero attempted"); + + F64 inv = 1.0f / _div; + + x *= inv; + y *= inv; + + return *this; +} + + +inline Point2D Point2D::operator-() const +{ + return Point2D(-x, -y); +} + +inline F64 Point2D::len() const +{ + return mSqrtD(x*x + y*y); +} + +inline void Point2D::normalize() +{ + m_point2D_normalize(*this); +} + +inline void Point2D::normalize(F64 val) +{ + m_point2D_normalize_f(*this, val); +} + + +//------------------------------------------------------------------- +// Non-Member Operators +//------------------------------------------------------------------- + +inline Point2I operator*(S32 mul, const Point2I& multiplicand) +{ + return multiplicand * mul; +} + +inline Point2F operator*(F32 mul, const Point2F& multiplicand) +{ + return multiplicand * mul; +} + +inline Point2D operator*(F64 mul, const Point2D& multiplicand) +{ + return multiplicand * mul; +} + +inline F32 mDot(const Point2F &p1, const Point2F &p2) +{ + return (p1.x*p2.x + p1.y*p2.y); +} + +inline F32 mDotPerp(const Point2F &p1, const Point2F &p2) +{ + return p1.x*p2.y - p2.x*p1.y; +} + +inline bool mIsNaN( const Point2F &p ) +{ + return mIsNaN_F( p.x ) || mIsNaN_F( p.y ); +} + + +namespace DictHash +{ + /// Generates a 32bit hash from a Point2I. + /// @see DictHash + inline U32 hash( const Point2I &key ) + { + return (key.x * 2230148873u) ^ key.y; + } +} + +#endif // _MPOINT2_H_ diff --git a/Engine/source/math/mPoint3.h b/Engine/source/math/mPoint3.h new file mode 100644 index 000000000..bf4af78b6 --- /dev/null +++ b/Engine/source/math/mPoint3.h @@ -0,0 +1,1023 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MPOINT3_H_ +#define _MPOINT3_H_ + +#ifndef _MMATHFN_H_ +#include "math/mMathFn.h" +#endif +#ifndef _MPOINT2_H_ +#include "math/mPoint2.h" +#endif + +//------------------------------------------------------------------------------ +/// 3D integer point +/// +/// Uses S32 internally. +class Point3I +{ + //-------------------------------------- Public data + public: + S32 x; ///< X co-ordinate + S32 y; ///< Y co-ordinate + S32 z; ///< Z co-ordinate + + //-------------------------------------- Public interface + public: + Point3I(); ///< Create an uninitialized point. + Point3I(const Point3I&); ///< Copy constructor. + explicit Point3I(S32 xyz); ///< Initializes all elements to the same value. + Point3I(S32 in_x, S32 in_y, S32 in_z); ///< Create a point from co-ordinates. + + //-------------------------------------- Non-math mutators and misc functions + void set(S32 xyz); ///< Initializes all elements to the same value. + void set(S32 in_x, S32 in_y, S32 in_z); ///< Set co-ordinates. + void setMin(const Point3I&); ///< Store lesser co-ordinates in this point. + void setMax(const Point3I&); ///< Store greater co-ordinates in this point. + + //-------------------------------------- Math mutators + void neg(); ///< Invert co-ordinate's signs. + void convolve(const Point3I&); ///< Convolve by parameter. + + //-------------------------------------- Queries + bool isZero() const; ///< Check for point at origin. (No epsilon.) + F32 len() const; ///< Get length. + + //-------------------------------------- Overloaded operators + public: + operator S32*() { return &x; } + operator const S32*() const { return &x; } + + // Comparison operators + bool operator==(const Point3I&) const; + bool operator!=(const Point3I&) const; + + // Arithmetic w/ other points + Point3I operator+(const Point3I&) const; + Point3I operator-(const Point3I&) const; + Point3I& operator+=(const Point3I&); + Point3I& operator-=(const Point3I&); + + // Arithmetic w/ scalars + Point3I operator*(S32) const; + Point3I& operator*=(S32); + Point3I operator/(S32) const; + Point3I& operator/=(S32); + + // Unary operators + Point3I operator-() const; + + //-------------------------------------- Public static constants +public: + const static Point3I One; + const static Point3I Zero; +}; + +class Point3D; + +//------------------------------------------------------------------------------ +class Point3F +{ + //-------------------------------------- Public data + public: + F32 x; + F32 y; + F32 z; + + public: + Point3F(); + Point3F(const Point3F&); + Point3F(F32 _x, F32 _y, F32 _z); + explicit Point3F(F32 xyz); + + //-------------------------------------- Non-math mutators and misc functions + public: + void set(F32 xyz); + void set(F32 _x, F32 _y, F32 _z); + void set(const Point3F&); + + void setMin(const Point3F&); + void setMax(const Point3F&); + + void interpolate(const Point3F&, const Point3F&, F32); + void zero(); + + /// Returns the smallest absolute value. + F32 least() const; + + /// Returns the greatest absolute value. + F32 most() const; + + operator F32*() { return &x; } + operator const F32*() const { return &x; } + + /// Returns the x and y coords as a Point2F. + Point2F asPoint2F() const { return Point2F( x, y ); } + + //-------------------------------------- Queries + public: + bool isZero() const; + bool isUnitLength() const; + F32 len() const; + F32 lenSquared() const; + F32 magnitudeSafe() const; + bool equal( const Point3F &compare, F32 epsilon = POINT_EPSILON ) const; + U32 getLeastComponentIndex() const; + U32 getGreatestComponentIndex() const; + + //-------------------------------------- Mathematical mutators + public: + void neg(); + void normalize(); + void normalizeSafe(); + void normalize(F32 val); + void convolve(const Point3F&); + void convolveInverse(const Point3F&); + + //-------------------------------------- Overloaded operators + public: + // Comparison operators + bool operator==(const Point3F&) const; + bool operator!=(const Point3F&) const; + + // Arithmetic w/ other points + Point3F operator+(const Point3F&) const; + Point3F operator-(const Point3F&) const; + Point3F& operator+=(const Point3F&); + Point3F& operator-=(const Point3F&); + + // Arithmetic w/ scalars + Point3F operator*(F32) const; + Point3F operator/(F32) const; + Point3F& operator*=(F32); + Point3F& operator/=(F32); + + Point3F operator*(const Point3F&) const; + Point3F& operator*=(const Point3F&); + Point3F operator/(const Point3F&) const; + Point3F& operator/=(const Point3F&); + + // Unary operators + Point3F operator-() const; + + Point3F& operator=(const Point3D&); + + //-------------------------------------- Public static constants +public: + const static Point3F One; + const static Point3F Zero; + const static Point3F Max; + const static Point3F Min; + const static Point3F UnitX; + const static Point3F UnitY; + const static Point3F UnitZ; +}; + +typedef Point3F VectorF; +typedef Point3F EulerF; + + +//------------------------------------------------------------------------------ +class Point3D +{ + //-------------------------------------- Public data + public: + F64 x; + F64 y; + F64 z; + + public: + Point3D(); + Point3D(const Point3D&); + Point3D(const Point3F&); + explicit Point3D(F64 xyz); + Point3D(F64 _x, F64 _y, F64 _z); + + //-------------------------------------- Non-math mutators and misc functions + public: + void set(F64 xyz); + void set(F64 _x, F64 _y, F64 _z); + + void setMin(const Point3D&); + void setMax(const Point3D&); + + void interpolate(const Point3D&, const Point3D&, F64); + + operator F64*() { return (&x); } + operator const F64*() const { return &x; } + + //-------------------------------------- Queries + public: + bool isZero() const; + F64 len() const; + F64 lenSquared() const; + + //-------------------------------------- Mathematical mutators + public: + void neg(); + void normalize(); + void normalize(F64 val); + void convolve(const Point3D&); + void convolveInverse(const Point3D&); + + //-------------------------------------- Overloaded operators + public: + Point3F toPoint3F() const; + // Comparison operators + bool operator==(const Point3D&) const; + bool operator!=(const Point3D&) const; + + // Arithmetic w/ other points + Point3D operator+(const Point3D&) const; + Point3D operator-(const Point3D&) const; + Point3D& operator+=(const Point3D&); + Point3D& operator-=(const Point3D&); + + // Arithmetic w/ scalars + Point3D operator*(F64) const; + Point3D operator/(F64) const; + Point3D& operator*=(F64); + Point3D& operator/=(F64); + + // Unary operators + Point3D operator-() const; + + //-------------------------------------- Public static constants +public: + const static Point3D One; + const static Point3D Zero; +}; + +//------------------------------------------------------------------------------ +//-------------------------------------- Point3I +// +inline Point3I::Point3I() +{ + // +} + +inline Point3I::Point3I(const Point3I& _copy) + : x(_copy.x), y(_copy.y), z(_copy.z) +{ + // +} + +inline Point3I::Point3I(S32 xyz) + : x(xyz), y(xyz), z(xyz) +{ + // +} + +inline Point3I::Point3I(S32 _x, S32 _y, S32 _z) + : x(_x), y(_y), z(_z) +{ + // +} + +inline void Point3I::set(S32 xyz) +{ + x = y = z = xyz; +} + +inline void Point3I::set(S32 _x, S32 _y, S32 _z) +{ + x = _x; + y = _y; + z = _z; +} + +inline void Point3I::setMin(const Point3I& _test) +{ + x = (_test.x < x) ? _test.x : x; + y = (_test.y < y) ? _test.y : y; + z = (_test.z < z) ? _test.z : z; +} + +inline void Point3I::setMax(const Point3I& _test) +{ + x = (_test.x > x) ? _test.x : x; + y = (_test.y > y) ? _test.y : y; + z = (_test.z > z) ? _test.z : z; +} + +inline void Point3I::neg() +{ + x = -x; + y = -y; + z = -z; +} + +inline F32 Point3I::len() const +{ + return mSqrt(F32(x*x + y*y + z*z)); +} + +inline void Point3I::convolve(const Point3I& c) +{ + x *= c.x; + y *= c.y; + z *= c.z; +} + +inline bool Point3I::isZero() const +{ + return ((x == 0) && (y == 0) && (z == 0)); +} + +inline bool Point3I::operator==(const Point3I& _test) const +{ + return ((x == _test.x) && (y == _test.y) && (z == _test.z)); +} + +inline bool Point3I::operator!=(const Point3I& _test) const +{ + return (operator==(_test) == false); +} + +inline Point3I Point3I::operator+(const Point3I& _add) const +{ + return Point3I(x + _add.x, y + _add.y, z + _add.z); +} + +inline Point3I Point3I::operator-(const Point3I& _rSub) const +{ + return Point3I(x - _rSub.x, y - _rSub.y, z - _rSub.z); +} + +inline Point3I& Point3I::operator+=(const Point3I& _add) +{ + x += _add.x; + y += _add.y; + z += _add.z; + + return *this; +} + +inline Point3I& Point3I::operator-=(const Point3I& _rSub) +{ + x -= _rSub.x; + y -= _rSub.y; + z -= _rSub.z; + + return *this; +} + +inline Point3I Point3I::operator-() const +{ + return Point3I(-x, -y, -z); +} + +inline Point3I Point3I::operator*(S32 mul) const +{ + return Point3I(x * mul, y * mul, z * mul); +} + +inline Point3I Point3I::operator/(S32 div) const +{ + AssertFatal(div != 0, "Error, div by zero attempted"); + return Point3I(x/div, y/div, z/div); +} + +inline Point3I& Point3I::operator*=(S32 mul) +{ + x *= mul; + y *= mul; + z *= mul; + + return *this; +} + +inline Point3I& Point3I::operator/=(S32 div) +{ + AssertFatal(div != 0, "Error, div by zero attempted"); + + x /= div; + y /= div; + z /= div; + + return *this; +} + +//------------------------------------------------------------------------------ +//-------------------------------------- Point3F +// +inline Point3F::Point3F() +#if defined(TORQUE_OS_LINUX) + : x(0.f), y(0.f), z(0.f) +#endif +{ +// Uninitialized points are definitely a problem. +// Enable the following code to see how often they crop up. +#ifdef DEBUG_MATH + *(U32 *)&x = 0x7FFFFFFA; + *(U32 *)&y = 0x7FFFFFFB; + *(U32 *)&z = 0x7FFFFFFC; +#endif +} + + +inline Point3F::Point3F(const Point3F& _copy) + : x(_copy.x), y(_copy.y), z(_copy.z) +{ + // +} + +inline Point3F::Point3F(F32 _x, F32 _y, F32 _z) + : x(_x), y(_y), z(_z) +{ + // +} + +inline Point3F::Point3F(F32 xyz) + : x(xyz), y(xyz), z(xyz) +{ + // +} + +inline void Point3F::set(F32 xyz) +{ + x = y = z = xyz; +} + +inline void Point3F::set(F32 _x, F32 _y, F32 _z) +{ + x = _x; + y = _y; + z = _z; +} + +inline void Point3F::set(const Point3F& copy) +{ + x = copy.x; + y = copy.y; + z = copy.z; +} + +inline void Point3F::setMin(const Point3F& _test) +{ + x = (_test.x < x) ? _test.x : x; + y = (_test.y < y) ? _test.y : y; + z = (_test.z < z) ? _test.z : z; +} + +inline void Point3F::setMax(const Point3F& _test) +{ + x = (_test.x > x) ? _test.x : x; + y = (_test.y > y) ? _test.y : y; + z = (_test.z > z) ? _test.z : z; +} + +inline void Point3F::interpolate(const Point3F& _from, const Point3F& _to, F32 _factor) +{ + AssertFatal(_factor >= 0.0f && _factor <= 1.0f, "Out of bound interpolation factor"); + m_point3F_interpolate( _from, _to, _factor, *this); +} + +inline void Point3F::zero() +{ + x = y = z = 0.0f; +} + +inline bool Point3F::isZero() const +{ + return ((x*x) <= POINT_EPSILON) && ((y*y) <= POINT_EPSILON) && ((z*z) <= POINT_EPSILON ); +} + +inline bool Point3F::isUnitLength() const +{ + return ( mFabs( 1.0f - ( x*x + y*y + z*z ) ) < POINT_EPSILON ); +} + +inline bool Point3F::equal( const Point3F &compare, F32 epsilon ) const +{ + return( ( mFabs( x - compare.x ) < epsilon ) && + ( mFabs( y - compare.y ) < epsilon ) && + ( mFabs( z - compare.z ) < epsilon ) ); +} + +inline U32 Point3F::getLeastComponentIndex() const +{ + U32 idx; + + if ( mFabs( x ) < mFabs( y ) ) + { + if ( mFabs( x ) < mFabs( z ) ) + idx = 0; + else + idx = 2; + } + else + { + if ( mFabs( y ) < mFabs( z ) ) + idx = 1; + else + idx = 2; + } + + return idx; +} + +inline U32 Point3F::getGreatestComponentIndex() const +{ + U32 idx; + + if ( mFabs( x ) > mFabs( y ) ) + { + if ( mFabs( x ) > mFabs( z ) ) + idx = 0; + else + idx = 2; + } + else + { + if ( mFabs( y ) > mFabs( z ) ) + idx = 1; + else + idx = 2; + } + + return idx; +} + +inline F32 Point3F::least() const +{ + return getMin( mFabs( x ), getMin( mFabs( y ), mFabs( z ) ) ); +} + +inline F32 Point3F::most() const +{ + return getMax( mFabs( x ), getMax( mFabs( y ), mFabs( z ) ) ); +} + +inline void Point3F::neg() +{ + x = -x; + y = -y; + z = -z; +} + +inline void Point3F::convolve(const Point3F& c) +{ + x *= c.x; + y *= c.y; + z *= c.z; +} + +inline void Point3F::convolveInverse(const Point3F& c) +{ + x /= c.x; + y /= c.y; + z /= c.z; +} + +inline F32 Point3F::lenSquared() const +{ + return (x * x) + (y * y) + (z * z); +} + +inline F32 Point3F::len() const +{ + return mSqrt(x*x + y*y + z*z); +} + +inline void Point3F::normalize() +{ + m_point3F_normalize(*this); +} + +inline F32 Point3F::magnitudeSafe() const +{ + if( isZero() ) + { + return 0.0f; + } + else + { + return len(); + } +} + +inline void Point3F::normalizeSafe() +{ + F32 vmag = magnitudeSafe(); + + if( vmag > POINT_EPSILON ) + { + *this *= F32(1.0 / vmag); + } +} + + +inline void Point3F::normalize(F32 val) +{ + m_point3F_normalize_f(*this, val); +} + +inline bool Point3F::operator==(const Point3F& _test) const +{ + return (x == _test.x) && (y == _test.y) && (z == _test.z); +} + +inline bool Point3F::operator!=(const Point3F& _test) const +{ + return operator==(_test) == false; +} + +inline Point3F Point3F::operator+(const Point3F& _add) const +{ + return Point3F(x + _add.x, y + _add.y, z + _add.z); +} + +inline Point3F Point3F::operator-(const Point3F& _rSub) const +{ + return Point3F(x - _rSub.x, y - _rSub.y, z - _rSub.z); +} + +inline Point3F& Point3F::operator+=(const Point3F& _add) +{ + x += _add.x; + y += _add.y; + z += _add.z; + + return *this; +} + +inline Point3F& Point3F::operator-=(const Point3F& _rSub) +{ + x -= _rSub.x; + y -= _rSub.y; + z -= _rSub.z; + + return *this; +} + +inline Point3F Point3F::operator*(F32 _mul) const +{ + return Point3F(x * _mul, y * _mul, z * _mul); +} + +inline Point3F Point3F::operator/(F32 _div) const +{ + AssertFatal(_div != 0.0f, "Error, div by zero attempted"); + + F32 inv = 1.0f / _div; + + return Point3F(x * inv, y * inv, z * inv); +} + +inline Point3F& Point3F::operator*=(F32 _mul) +{ + x *= _mul; + y *= _mul; + z *= _mul; + + return *this; +} + +inline Point3F& Point3F::operator/=(F32 _div) +{ + AssertFatal(_div != 0.0f, "Error, div by zero attempted"); + + F32 inv = 1.0f / _div; + x *= inv; + y *= inv; + z *= inv; + + return *this; +} + +inline Point3F Point3F::operator*(const Point3F &_vec) const +{ + return Point3F(x * _vec.x, y * _vec.y, z * _vec.z); +} + +inline Point3F& Point3F::operator*=(const Point3F &_vec) +{ + x *= _vec.x; + y *= _vec.y; + z *= _vec.z; + return *this; +} + +inline Point3F Point3F::operator/(const Point3F &_vec) const +{ + return Point3F(x / _vec.x, y / _vec.y, z / _vec.z); +} + +inline Point3F& Point3F::operator/=(const Point3F &_vec) +{ + x /= _vec.x; + y /= _vec.y; + z /= _vec.z; + return *this; +} + +inline Point3F Point3F::operator-() const +{ + return Point3F(-x, -y, -z); +} + + +inline Point3F& Point3F::operator=(const Point3D &_vec) +{ + x = (F32)_vec.x; + y = (F32)_vec.y; + z = (F32)_vec.z; + return *this; +} + +//------------------------------------------------------------------------------ +//-------------------------------------- Point3D +// +inline Point3D::Point3D() +{ + // +} + +inline Point3D::Point3D(const Point3D& _copy) + : x(_copy.x), y(_copy.y), z(_copy.z) +{ + // +} + +inline Point3D::Point3D(const Point3F& _copy) + : x(_copy.x), y(_copy.y), z(_copy.z) +{ + // +} + +inline Point3D::Point3D(F64 xyz) + : x(xyz), y(xyz), z(xyz) +{ + // +} + +inline Point3D::Point3D(F64 _x, F64 _y, F64 _z) + : x(_x), y(_y), z(_z) +{ + // +} + +inline void Point3D::set( F64 xyz ) +{ + x = y = z = xyz; +} + +inline void Point3D::set(F64 _x, F64 _y, F64 _z) +{ + x = _x; + y = _y; + z = _z; +} + +inline void Point3D::setMin(const Point3D& _test) +{ + x = (_test.x < x) ? _test.x : x; + y = (_test.y < y) ? _test.y : y; + z = (_test.z < z) ? _test.z : z; +} + +inline void Point3D::setMax(const Point3D& _test) +{ + x = (_test.x > x) ? _test.x : x; + y = (_test.y > y) ? _test.y : y; + z = (_test.z > z) ? _test.z : z; +} + +inline void Point3D::interpolate(const Point3D& _from, const Point3D& _to, F64 _factor) +{ + AssertFatal(_factor >= 0.0f && _factor <= 1.0f, "Out of bound interpolation factor"); + m_point3D_interpolate( _from, _to, _factor, *this); +} + +inline bool Point3D::isZero() const +{ + return (x == 0.0f) && (y == 0.0f) && (z == 0.0f); +} + +inline void Point3D::neg() +{ + x = -x; + y = -y; + z = -z; +} + +inline void Point3D::convolve(const Point3D& c) +{ + x *= c.x; + y *= c.y; + z *= c.z; +} + +inline void Point3D::convolveInverse(const Point3D& c) +{ + x /= c.x; + y /= c.y; + z /= c.z; +} + +inline F64 Point3D::lenSquared() const +{ + return (x * x) + (y * y) + (z * z); +} + +inline F64 Point3D::len() const +{ + return mSqrtD(x*x + y*y + z*z); +} + +inline void Point3D::normalize() +{ + m_point3D_normalize(*this); +} + +inline void Point3D::normalize(F64 val) +{ + m_point3D_normalize_f(*this, val); +} + +inline bool Point3D::operator==(const Point3D& _test) const +{ + return (x == _test.x) && (y == _test.y) && (z == _test.z); +} + +inline bool Point3D::operator!=(const Point3D& _test) const +{ + return operator==(_test) == false; +} + +inline Point3D Point3D::operator+(const Point3D& _add) const +{ + return Point3D(x + _add.x, y + _add.y, z + _add.z); +} + + +inline Point3D Point3D::operator-(const Point3D& _rSub) const +{ + return Point3D(x - _rSub.x, y - _rSub.y, z - _rSub.z); +} + +inline Point3D& Point3D::operator+=(const Point3D& _add) +{ + x += _add.x; + y += _add.y; + z += _add.z; + + return *this; +} + +inline Point3D& Point3D::operator-=(const Point3D& _rSub) +{ + x -= _rSub.x; + y -= _rSub.y; + z -= _rSub.z; + + return *this; +} + +inline Point3D Point3D::operator*(F64 _mul) const +{ + return Point3D(x * _mul, y * _mul, z * _mul); +} + +inline Point3D Point3D::operator/(F64 _div) const +{ + AssertFatal(_div != 0.0f, "Error, div by zero attempted"); + + F64 inv = 1.0f / _div; + + return Point3D(x * inv, y * inv, z * inv); +} + +inline Point3D& Point3D::operator*=(F64 _mul) +{ + x *= _mul; + y *= _mul; + z *= _mul; + + return *this; +} + +inline Point3D& Point3D::operator/=(F64 _div) +{ + AssertFatal(_div != 0.0f, "Error, div by zero attempted"); + + F64 inv = 1.0f / _div; + x *= inv; + y *= inv; + z *= inv; + + return *this; +} + +inline Point3D Point3D::operator-() const +{ + return Point3D(-x, -y, -z); +} + +inline Point3F Point3D::toPoint3F() const +{ + return Point3F((F32)x,(F32)y,(F32)z); +} + +//------------------------------------------------------------------- +// Non-Member Operators +//------------------------------------------------------------------- + +inline Point3I operator*(S32 mul, const Point3I& multiplicand) +{ + return multiplicand * mul; +} + +inline Point3F operator*(F32 mul, const Point3F& multiplicand) +{ + return multiplicand * mul; +} + +inline Point3D operator*(F64 mul, const Point3D& multiplicand) +{ + return multiplicand * mul; +} + +inline F32 mDot(const Point3F &p1, const Point3F &p2) +{ + return (p1.x*p2.x + p1.y*p2.y + p1.z*p2.z); +} + +inline F64 mDot(const Point3D &p1, const Point3D &p2) +{ + return (p1.x*p2.x + p1.y*p2.y + p1.z*p2.z); +} + +inline void mCross(const Point3F &a, const Point3F &b, Point3F *res) +{ + res->x = (a.y * b.z) - (a.z * b.y); + res->y = (a.z * b.x) - (a.x * b.z); + res->z = (a.x * b.y) - (a.y * b.x); +} + +inline void mCross(const Point3D &a, const Point3D &b, Point3D *res) +{ + res->x = (a.y * b.z) - (a.z * b.y); + res->y = (a.z * b.x) - (a.x * b.z); + res->z = (a.x * b.y) - (a.y * b.x); +} + +inline Point3F mCross(const Point3F &a, const Point3F &b) +{ + Point3F r; + mCross( a, b, &r ); + return r; +} + +inline Point3D mCross(const Point3D &a, const Point3D &b) +{ + Point3D r; + mCross( a, b, &r ); + return r; +} + +/// Returns the vector normalized. +inline Point3F mNormalize( const Point3F &vec ) +{ + Point3F out( vec ); + out.normalize(); + return out; +} + +/// Returns true if the point is NaN. +inline bool mIsNaN( const Point3F &p ) +{ + return mIsNaN_F( p.x ) || mIsNaN_F( p.y ) || mIsNaN_F( p.z ); +} + +/// Returns a copy of the vector reflected by a normal +inline Point3F mReflect( const Point3F &v, const Point3F &n ) +{ + return v - 2 * n * mDot( v, n ); +} + +/// Returns a perpendicular vector to the unit length input vector. +extern Point3F mPerp( const Point3F &normal ); + +#endif // _MPOINT3_H_ diff --git a/Engine/source/math/mPoint4.h b/Engine/source/math/mPoint4.h new file mode 100644 index 000000000..edd7260d6 --- /dev/null +++ b/Engine/source/math/mPoint4.h @@ -0,0 +1,228 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MPOINT4_H_ +#define _MPOINT4_H_ + +#ifndef _MMATHFN_H_ +#include "math/mMathFn.h" +#endif + +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif + + +//------------------------------------------------------------------------------ +/// 4D integer point +/// +/// Uses S32 internally. Currently storage only. +class Point4I +{ + public: + Point4I() {} + Point4I(S32 _x, S32 _y, S32 _z, S32 _w); + + S32 x; + S32 y; + S32 z; + S32 w; + + //-------------------------------------- Public static constants + public: + const static Point4I One; + const static Point4I Zero; +}; + +//------------------------------------------------------------------------------ +/// 4D floating-point point. +/// +/// Uses F32 internally. +/// +/// Useful for representing quaternions and other 4d beasties. +class Point4F +{ + //-------------------------------------- Public data + public: + F32 x; ///< X co-ordinate. + F32 y; ///< Y co-ordinate. + F32 z; ///< Z co-ordinate. + F32 w; ///< W co-ordinate. + + public: + Point4F(); ///< Create an uninitialized point. + Point4F(const Point4F&); ///< Copy constructor. + + /// Create point from coordinates. + Point4F(F32 _x, F32 _y, F32 _z, F32 _w); + + /// Set point's coordinates. + void set(F32 _x, F32 _y, F32 _z, F32 _w); + + /// Interpolate from _pt1 to _pt2, based on _factor. + /// + /// @param _pt1 Starting point. + /// @param _pt2 Ending point. + /// @param _factor Interpolation factor (0.0 .. 1.0). + void interpolate(const Point4F& _pt1, const Point4F& _pt2, F32 _factor); + + operator F32*() { return (&x); } + operator const F32*() const { return &x; } + + F32 len() const; + + Point4F operator/(F32) const; + + Point4F operator*(F32) const; + Point4F operator+(const Point4F&) const; + Point4F& operator+=(const Point4F&); + Point4F operator-(const Point4F&) const; + Point4F operator*(const Point4F&) const; + Point4F& operator*=(const Point4F&); + Point4F& operator=(const Point3F&); + Point4F& operator=(const Point4F&); + + Point3F asPoint3F() const { return Point3F(x,y,z); } + + //-------------------------------------- Public static constants + public: + const static Point4F One; + const static Point4F Zero; +}; + +typedef Point4F Vector4F; ///< Points can be vectors! + +//------------------------------------------------------------------------------ +//-------------------------------------- Point4F +// +inline Point4F::Point4F() +{ +} + +inline Point4F::Point4F(const Point4F& _copy) + : x(_copy.x), y(_copy.y), z(_copy.z), w(_copy.w) +{ +} + +inline Point4F::Point4F(F32 _x, F32 _y, F32 _z, F32 _w) + : x(_x), y(_y), z(_z), w(_w) +{ +} + +inline void Point4F::set(F32 _x, F32 _y, F32 _z, F32 _w) +{ + x = _x; + y = _y; + z = _z; + w = _w; +} + +inline F32 Point4F::len() const +{ + return mSqrt(x*x + y*y + z*z + w*w); +} + +inline void Point4F::interpolate(const Point4F& _from, const Point4F& _to, F32 _factor) +{ + x = (_from.x * (1.0f - _factor)) + (_to.x * _factor); + y = (_from.y * (1.0f - _factor)) + (_to.y * _factor); + z = (_from.z * (1.0f - _factor)) + (_to.z * _factor); + w = (_from.w * (1.0f - _factor)) + (_to.w * _factor); +} + +inline Point4F& Point4F::operator=(const Point3F &_vec) +{ + x = _vec.x; + y = _vec.y; + z = _vec.z; + w = 1.0f; + return *this; +} + +inline Point4F& Point4F::operator=(const Point4F &_vec) +{ + x = _vec.x; + y = _vec.y; + z = _vec.z; + w = _vec.w; + + return *this; +} + +inline Point4F Point4F::operator+(const Point4F& _add) const +{ + return Point4F( x + _add.x, y + _add.y, z + _add.z, w + _add.w ); +} + +inline Point4F& Point4F::operator+=(const Point4F& _add) +{ + x += _add.x; + y += _add.y; + z += _add.z; + w += _add.w; + + return *this; +} + +inline Point4F Point4F::operator-(const Point4F& _rSub) const +{ + return Point4F( x - _rSub.x, y - _rSub.y, z - _rSub.z, w - _rSub.w ); +} + +inline Point4F Point4F::operator*(const Point4F &_vec) const +{ + return Point4F(x * _vec.x, y * _vec.y, z * _vec.z, w * _vec.w); +} + +inline Point4F Point4F::operator*(F32 _mul) const +{ + return Point4F(x * _mul, y * _mul, z * _mul, w * _mul); +} + +inline Point4F Point4F::operator /(F32 t) const +{ + F32 f = 1.0f / t; + return Point4F( x * f, y * f, z * f, w * f ); +} + +//------------------------------------------------------------------------------ +//-------------------------------------- Point4F + +inline Point4I::Point4I(S32 _x, S32 _y, S32 _z, S32 _w) : x(_x), y(_y), z(_z), w(_w) +{ +} + +//------------------------------------------------------------------- +// Non-Member Operators +//------------------------------------------------------------------- + +inline Point4F operator*(F32 mul, const Point4F& multiplicand) +{ + return multiplicand * mul; +} + +inline bool mIsNaN( const Point4F &p ) +{ + return mIsNaN_F( p.x ) || mIsNaN_F( p.y ) || mIsNaN_F( p.z ) || mIsNaN_F( p.w ); +} + +#endif // _MPOINT4_H_ diff --git a/Engine/source/math/mPolyhedron.cpp b/Engine/source/math/mPolyhedron.cpp new file mode 100644 index 000000000..c9b1e60a3 --- /dev/null +++ b/Engine/source/math/mPolyhedron.cpp @@ -0,0 +1,247 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "math/mPolyhedron.h" + +#include "platform/typetraits.h" + + +//----------------------------------------------------------------------------- + +void PolyhedronVectorData::buildFromPlanes( const PlaneSetF& planes ) +{ + const U32 numSourcePlanes = planes.getNumPlanes(); + + // Go through the planes and create edges by + // intersecting the various planes. + + for( U32 i = 0; i < numSourcePlanes; ++ i ) + { + const PlaneF& currentPlane = planes.getPlanes()[ i ]; + + bool haveEdges = false; + for( U32 n = 0; n < numSourcePlanes; ++ n ) + { + if( n == i ) + continue; + + const PlaneF& intersectPlane = planes.getPlanes()[ n ]; + + Point3F start; + Point3F dir; + + // Intersect the two planes. + + if( !currentPlane.intersect( intersectPlane, start, dir ) ) + continue; + + // Absolutely make sure our direction vector is normalized. + + dir.normalize(); + + // Find the two vertices on the line that are still + // inside the polyhedron by clipping against the other + // planes in the set. + + F32 minDist = TypeTraits< F32 >::MAX; + F32 maxDist = TypeTraits< F32 >::MIN; + + Point3F v1; + Point3F v2; + + Point3F end = start + dir; + + for( U32 j = 0; j < numSourcePlanes; j ++ ) + { + // Skip if current or intersect plane. + if( j == n || j == i ) + continue; + + const PlaneF& clipPlane = planes.getPlanes()[ j ]; + + // Compute the distance at which the plane intersects + // the line. Skip if parallel. + + F32 dist = clipPlane.intersect( start, end ); + if( mIsEqual( dist, PARALLEL_PLANE ) ) + continue; + + // See if the resulting vertex is even inside the planes. + // Skip if not. + + Point3F vertex = start + dir * dist; + bool isContained = true; + for( U32 nplane = 0; nplane < numSourcePlanes; ++ nplane ) + { + // Skip all planes that we used to construct this vertex. + if( nplane == j || nplane == n || nplane == i ) + continue; + + if( planes.getPlanes()[ nplane ].whichSide( vertex ) == PlaneF::Back ) + { + isContained = false; + break; + } + } + if( !isContained ) + continue; + + // Keep track of min and max distance. + + if( mIsEqual( dist, minDist ) || mIsEqual( dist, maxDist ) ) + continue; + else if( dist < minDist ) + { + if( minDist != TypeTraits< F32 >::MAX && maxDist == TypeTraits< F32 >::MIN ) + { + maxDist = minDist; + v2 = v1; + } + + minDist = dist; + v1 = vertex; + } + else if( dist > maxDist ) + { + maxDist = dist; + v2 = vertex; + } + } + + // Skip plane pair if there's no properly formed edge. + + if( minDist == TypeTraits< F32 >::MAX || maxDist == TypeTraits< F32 >::MIN || mIsEqual( minDist, maxDist ) ) + continue; + + // See if vertex 1 already exists. + + S32 v1index = -1; + bool v1Existed = false; + for( U32 nvert = 0; nvert < pointList.size(); ++ nvert ) + if( pointList[ nvert ].equal( v1, 0.001f ) ) + { + v1index = nvert; + v1Existed = true; + break; + } + + // See if vertex 2 already exists. + + S32 v2index = -1; + bool v2Existed = false; + for( U32 nvert = 0; nvert < pointList.size(); ++ nvert ) + if( pointList[ nvert ].equal( v2, 0.001f ) ) + { + v2index = nvert; + v2Existed = true; + break; + } + + // Add vertex 1, if necessary. + + if( !v1Existed ) + { + v1index = pointList.size(); + pointList.push_back( v1 ); + } + + // Add vertex 2, if necessary. + + if( !v2Existed ) + { + v2index = pointList.size(); + pointList.push_back( v2 ); + } + + // If both v1 and v2 already existed in the point + // set, this must be an edge that we are sharing so try + // to find it. + + const U32 thisPlaneIndex = planeList.size(); + bool foundExistingEdge = false; + + if( v1Existed && v2Existed ) + { + for( U32 nedge = 0; nedge < edgeList.size(); ++ nedge ) + { + Edge& edge = edgeList[ nedge ]; + + if( ( edge.vertex[ 0 ] == v1index && edge.vertex[ 1 ] == v2index ) || + ( edge.vertex[ 0 ] == v2index && edge.vertex[ 1 ] == v1index ) ) + { + edge.face[ 1 ] = thisPlaneIndex; + foundExistingEdge = true; + break; + } + } + } + + // Otherwise, add a new edge. + + if( !foundExistingEdge ) + { + bool invert = false; + + // We need to make sure to maintain CW ordering on face[0], + // so test to see if we need to go v1->v2 or v2->v1. + + Point3F normal = mCross( currentPlane, v2 - v1 ); + Point3F testPoint = v1 + normal; + + for( U32 nplane = 0; nplane < numSourcePlanes; ++ nplane ) + { + if( nplane == i ) + continue; + + if( planes.getPlanes()[ nplane ].whichSide( testPoint ) == PlaneF::Back ) + { + invert = true; + break; + } + } + + if( !invert ) + { + edgeList.push_back( + Edge( thisPlaneIndex, 0, v1index, v2index ) + ); + } + else + { + edgeList.push_back( + Edge( thisPlaneIndex, 0, v2index, v1index ) + ); + } + } + + // This plane has edges. + + haveEdges = true; + } + + // If this plane produced edges, add it. + + if( haveEdges ) + planeList.push_back( currentPlane ); + } +} diff --git a/Engine/source/math/mPolyhedron.h b/Engine/source/math/mPolyhedron.h new file mode 100644 index 000000000..00723466f --- /dev/null +++ b/Engine/source/math/mPolyhedron.h @@ -0,0 +1,492 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MPOLYHEDRON_H_ +#define _MPOLYHEDRON_H_ + +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif + +#ifndef _MPLANE_H_ +#include "math/mPlane.h" +#endif + +#ifndef _MPLANESET_H_ +#include "math/mPlaneSet.h" +#endif + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +#ifndef _TUNMANAGEDVECTOR_H_ +#include "core/util/tUnmanagedVector.h" +#endif + +#ifndef _TFIXEDSIZEVECTOR_H_ +#include "core/util/tFixedSizeVector.h" +#endif + +#ifndef _MCONSTANTS_H_ +#include "math/mConstants.h" +#endif + + +/// @file +/// Templated polyhedron code to allow all code to use a central definition of polyhedrons and +/// their functionality (intersection, clipping, etc.) yet still maintain full control over how +/// to create and store their data. + + + +struct PolyhedronUnmanagedVectorData; +template< typename Base > struct PolyhedronImpl; + + +/// The polyhedron type to which all other polyhedron types should be convertible. +typedef PolyhedronImpl< PolyhedronUnmanagedVectorData > AnyPolyhedron; + + + +/// Base class for helping to abstract over how a polyhedron +/// stores and updates its data. +/// +/// The PolyhedronData class hierarchy is designed to give users of PolyhedronImpl +/// maximum freedom in modifying those behaviors. This leads to some duplicating +/// in the various PolyhedronData classes but it ultimately provides greater control. +/// +/// All accesses to the data go through accessors on the base classes. This gives +/// the base class the freedom to implement lazy updates, for example. +/// +/// A given base implementation is also free to store additional data or derive extended +/// classes from the base classes expected for points (Point3F), edges (Edge), and planes +/// (PlaneF). If a class does that, it loses the ability to trivially convert to +/// AnyPolyhedron, though. +struct PolyhedronData +{ + /// Winged edge. + /// + /// @note Must be oriented clockwise for face[0]! This is important! + struct Edge + { + /// Index into plane vector for the two planes that go through this + /// edge. + U32 face[ 2 ]; + + /// Index into point vector for the beginning and end point of the edge. + /// @note The vector "vertex[ 1 ] - vertex[ 0 ]" must be oriented such that + /// it defines a *clockwise* orientation for face[ 0 ]. This is important! + U32 vertex[ 2 ]; + + Edge() {} + Edge( U32 face1, U32 face2, U32 vertex1, U32 vertex2 ) + { + face[ 0 ] = face1; + face[ 1 ] = face2; + vertex[ 0 ] = vertex1; + vertex[ 1 ] = vertex2; + } + }; + + typedef Edge EdgeType; + typedef PlaneF PlaneType; + typedef Point3F PointType; + + template< typename Polyhedron > + static void buildBoxData( Polyhedron& poly, const MatrixF& mat, const Box3F& box, bool invertNormals = false ); +}; + +/// Polyhedron data stored in Vectors. +struct PolyhedronVectorData : public PolyhedronData +{ + typedef Vector< PlaneF > PlaneListType; + typedef Vector< Point3F > PointListType; + typedef Vector< Edge > EdgeListType; + + /// List of planes. Note that by default, the normals facing *inwards*. + PlaneListType planeList; + + /// List of vertices. + PointListType pointList; + + /// List of edges. + EdgeListType edgeList; + + PolyhedronVectorData() + { + VECTOR_SET_ASSOCIATION( pointList ); + VECTOR_SET_ASSOCIATION( planeList ); + VECTOR_SET_ASSOCIATION( edgeList ); + } + + /// @name Accessors + /// @{ + + /// Return the number of planes that make up this polyhedron. + U32 getNumPlanes() const { return planeList.size(); } + + /// Return the planes that make up the polyhedron. + /// @note The normals of these planes are facing *inwards*. + PlaneF* getPlanes() const { return planeList.address(); } + + /// Return the number of points that this polyhedron has. + U32 getNumPoints() const { return pointList.size(); } + + /// + Point3F* getPoints() const { return pointList.address(); } + + /// Return the number of edges that this polyhedron has. + U32 getNumEdges() const { return edgeList.size(); } + + /// + Edge* getEdges() const { return edgeList.address(); } + + /// @} + + /// Conversion to the common polyhedron type. + operator AnyPolyhedron() const; + + void buildBox( const MatrixF& mat, const Box3F& box, bool invertNormals = false ) + { + pointList.setSize( 8 ); + planeList.setSize( 6 ); + edgeList.setSize( 12 ); + + buildBoxData( *this, mat, box, invertNormals ); + } + + /// Build a polyhedron from the given set of planes. + void buildFromPlanes( const PlaneSetF& planes ); +}; + +/// Polyhedron data stored as raw points with memory +/// being managed externally. +struct PolyhedronUnmanagedVectorData : public PolyhedronData +{ + typedef UnmanagedVector< PlaneF > PlaneListType; + typedef UnmanagedVector< Point3F > PointListType; + typedef UnmanagedVector< Edge > EdgeListType; + + protected: + + /// List of planes. Note that by default, the normals facing *inwards*. + PlaneListType planeList; + + /// List of vertices. + PointListType pointList; + + /// List of edges. + EdgeListType edgeList; + + public: + + /// @name Accessors + /// @{ + + /// Return the number of planes that make up this polyhedron. + U32 getNumPlanes() const { return planeList.size(); } + + /// Return the planes that make up the polyhedron. + /// @note The normals of these planes are facing *inwards*. + const PlaneF* getPlanes() const { return planeList.address(); } + PlaneF* getPlanes() { return planeList.address(); } + + /// Return the number of points that this polyhedron has. + U32 getNumPoints() const { return pointList.size(); } + + /// + const Point3F* getPoints() const { return pointList.address(); } + Point3F* getPoints() { return pointList.address(); } + + /// Return the number of edges that this polyhedron has. + U32 getNumEdges() const { return edgeList.size(); } + + /// + const Edge* getEdges() const { return edgeList.address(); } + Edge* getEdges() { return edgeList.address(); } + + /// @} +}; + +/// Polyhedron data stored in fixed size arrays. +template< int NUM_PLANES, int NUM_POINTS, int NUM_EDGES > +struct PolyhedronFixedVectorData : public PolyhedronData +{ + typedef FixedSizeVector< PlaneF, NUM_PLANES > PlaneListType; + typedef FixedSizeVector< Point3F, NUM_POINTS > PointListType; + typedef FixedSizeVector< Edge, NUM_EDGES > EdgeListType; + + protected: + + /// List of planes. Note that by default, the normals facing *inwards*. + PlaneListType planeList; + + /// List of vertices. + PointListType pointList; + + /// List of edges. + EdgeListType edgeList; + + public: + + + /// @name Accessors + /// @{ + + /// Return the number of planes that make up this polyhedron. + U32 getNumPlanes() const { return planeList.size(); } + + /// Return the planes that make up the polyhedron. + /// @note The normals of these planes are facing *inwards*. + PlaneF* getPlanes() const { return planeList.address(); } + + /// Return the number of points that this polyhedron has. + U32 getNumPoints() const { return pointList.size(); } + + /// + Point3F* getPoints() const { return pointList.address(); } + + /// Return the number of edges that this polyhedron has. + U32 getNumEdges() const { return edgeList.size(); } + + /// + Edge* getEdges() const { return edgeList.address(); } + + /// @} + + /// Conversion to the common polyhedron type. + operator AnyPolyhedron() const; +}; + + +/// A polyhedron. +/// +/// Polyhedrons are stored as both sets of planes as well sets of edges and vertices (basically +/// a winged-edge format). +/// +/// Polyhedrons must be convex. +/// +/// @note The default orientation for the plane normals of a polyhedron is *inwards*. +template< typename Base = PolyhedronVectorData > +struct PolyhedronImpl : public Base +{ + typedef typename Base::Edge Edge; + + typedef typename Base::PlaneListType PlaneListType; + typedef typename Base::PointListType PointListType; + typedef typename Base::EdgeListType EdgeListType; + + /// Construct an empty polyhedron. + PolyhedronImpl() {} + + /// Construct a polyhedron described by the given planes and edges. + PolyhedronImpl( PlaneListType planes, PointListType points, EdgeListType edges ) + { + this->planeList = planes; + this->pointList = points; + this->edgeList = edges; + } + + /// Return the AABB around the polyhedron. + Box3F getBounds() const + { + return Box3F::aroundPoints( this->getPoints(), this->getNumPoints() ); + } + + /// Return the median point of all points defined on the polyhedron. + Point3F getCenterPoint() const; + + /// @name Transform + /// @{ + + /// Transform the polyhedron using the given transform matrix and scale. + void transform( const MatrixF& matrix, const Point3F& scale = Point3F::One ); + + /// @} + + /// @name Containment + /// @{ + + /// @see PlaneSet::isContained(const Point3F&,F32) + bool isContained( const Point3F& point, F32 epsilon = 0.f ) const + { + return PlaneSetF( this->getPlanes(), this->getNumPlanes() ).isContained( point, epsilon ); + } + + /// @see PlaneSet::isContained(const Point3F*,U32) + bool isContained( const Point3F* points, U32 numPoints ) const + { + return PlaneSetF( this->getPlanes(), this->getNumPlanes() ).isContained( points, numPoints ); + } + + /// @see PlaneSet::isContained(const Box3F&) + bool isContained( const Box3F& aabb ) const + { + return PlaneSetF( this->getPlanes(), this->getNumPlanes() ).isContained( aabb ); + } + + /// @see PlaneSet::isContained(const SphereF&) + bool isContained( const SphereF& sphere ) const + { + return PlaneSetF( this->getPlanes(), this->getNumPlanes() ).isContained( sphere ); + } + + /// @see PlaneSet::isContained(const OrientedBox3F&) + bool isContained( const OrientedBox3F& obb ) const + { + return PlaneSetF( this->getPlanes(), this->getNumPlanes() ).isContained( obb ); + } + + /// @} + + /// @name Intersection + /// All of these intersection methods are approximate in that they can produce + /// false positives on GeometryIntersecting. For precise testing, use Intersector. + /// @{ + + /// @see PlaneSet::testPotentialIntersection(const Box3F&) + OverlapTestResult testPotentialIntersection( const Box3F& aabb ) const + { + return PlaneSetF( this->getPlanes(), this->getNumPlanes() ).testPotentialIntersection( aabb ); + } + + /// @see PlaneSet::testPotentialIntersection(const SphereF&) + OverlapTestResult testPotentialIntersection( const SphereF& sphere ) const + { + return PlaneSetF( this->getPlanes(), this->getNumPlanes() ).testPotentialIntersection( sphere ); + } + + /// @see PlaneSet::testPotentialIntersection(const OrientedBox3F&) + OverlapTestResult testPotentialIntersection( const OrientedBox3F& obb ) const + { + return PlaneSetF( this->getPlanes(), this->getNumPlanes() ).testPotentialIntersection( obb ); + } + + /// @see PlaneSet::testPlanes + U32 testPlanes( const Box3F& bounds, U32 planeMask = 0xFFFFFFFF, F32 expand = 0.0f ) const + { + return PlaneSetF( this->getPlanes(), this->getNumPlanes() ).testPlanes( bounds, planeMask, expand ); + } + + /// @} + + /// @name Clipping + /// Functionality to clip other geometries against the polyhedron. + /// @{ + + /// @see PlaneSet::clipSegment + bool clipSegment( Point3F& pnt0, Point3F& pnt1 ) const + { + return PlaneSetF( this->getPlanes(), this->getNumPlanes() ).clipSegment( pnt0, pnt1 ); + } + + /// @see PlaneSet::clipPolygon + U32 clipPolygon( const Point3F* inVertices, U32 inNumVertices, Point3F* outVertices, U32 maxOutVertices ) const + { + return PlaneSetF( this->getPlanes(), this->getNumPlanes() ).clipPolygon( inVertices, inNumVertices, outVertices, maxOutVertices ); + } + + /// @} + + /// @name Construction + /// Operations for constructing solids and polygons through boolean operations involving + /// the polyhedron. + /// @{ + + /// Build the intersection of this polyhedron with the given plane. The result is a + /// polygon. + /// + /// @param plane Plane to intersect the polyhedron with. + /// @param outPoints (out) Array where the resulting polygon points are stored. A safe size is to + /// just allocate as many points as there are edges in the polyhedron. If you know the maximum + /// number of vertices that can result from the intersection (for example, 4 for a box), then + /// it is ok to only allocate that much. + /// @param maxOutPoints Number of points that can be stored in @a outPoints. If insufficient, the + /// return value will be 0. + /// + /// @return The number of points written to @a outPoints. If there is no intersection between the + /// given plane and the polyhedron, this will be zero. + /// + /// @note The resulting points will be ordered to form a proper polygon but there is no guarantee + /// on which direction the ordering is in compared to the plane. + U32 constructIntersection( const PlaneF& plane, Point3F* outPoints, U32 maxOutPoints ) const; + + /// @} + + /// @name Extraction + /// @{ + + /// Extract the polygon for the given plane. + /// + /// The resulting indices will be CW ordered if the plane normals on the polyhedron are facing + /// inwards and CCW ordered otherwise. + /// + /// @param plane Index of the plane on the polyhedron. + /// @param outIndices Array where the resulting vertex indices will be stored. Must have + /// enough room. If you don't know the exact size that you need, just allocate one index + /// for any point in the mesh. + /// @param maxOutIndices The number of indices that can be stored in @a outIndices. If insufficient, + /// the return value will be 0. + /// + /// @return Number of indices written to @a outIndices. + /// + /// @note This method relies on correct CW ordering of edges with respect to face[0]. + template< typename IndexType > + U32 extractFace( U32 plane, IndexType* outIndices, U32 maxOutIndices ) const; + + /// @} + + protected: + + template< typename P > + OverlapTestResult _testOverlap( const P& bounds ) const; +}; + +/// Default polyhedron type. +typedef PolyhedronImpl<> Polyhedron; + + +//----------------------------------------------------------------------------- + +inline PolyhedronVectorData::operator AnyPolyhedron() const +{ + return AnyPolyhedron( + AnyPolyhedron::PlaneListType( getPlanes(), getNumPlanes() ), + AnyPolyhedron::PointListType( getPoints(), getNumPoints() ), + AnyPolyhedron::EdgeListType( getEdges(), getNumEdges() ) + ); +} + +//----------------------------------------------------------------------------- + +template< int NUM_PLANES, int NUM_POINTS, int NUM_EDGES > +inline PolyhedronFixedVectorData< NUM_PLANES, NUM_POINTS, NUM_EDGES >::operator AnyPolyhedron() const +{ + return AnyPolyhedron( + AnyPolyhedron::PlaneListType( getPlanes(), getNumPlanes() ), + AnyPolyhedron::PointListType( getPoints(), getNumPoints() ), + AnyPolyhedron::EdgeListType( getEdges(), getNumEdges() ) + ); +} + +#endif // !_MPOLYHEDRON_H_ diff --git a/Engine/source/math/mPolyhedron.impl.h b/Engine/source/math/mPolyhedron.impl.h new file mode 100644 index 000000000..b36debcaa --- /dev/null +++ b/Engine/source/math/mPolyhedron.impl.h @@ -0,0 +1,505 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "math/mPlaneTransformer.h" + + +//----------------------------------------------------------------------------- + +template< typename Base > +Point3F PolyhedronImpl< Base >::getCenterPoint() const +{ + const U32 numPoints = this->getNumPoints(); + if( numPoints == 0 ) + return Point3F( 0.f, 0.f, 0.f ); + + const typename Base::PointType* pointList = this->getPoints(); + Point3F center( pointList[ 0 ] ); + + for( U32 i = 1; i < numPoints; ++ i ) + center += pointList[ i ]; + + center /= ( F32 ) numPoints; + return center; +} + +//----------------------------------------------------------------------------- + +template< typename Base > +void PolyhedronImpl< Base >::transform( const MatrixF& matrix, const Point3F& scale ) +{ + // Transform points. + + const U32 numPoints = this->getNumPoints(); + typename Base::PointType* points = this->getPoints(); + + for( U32 i = 0; i < numPoints; ++ i ) + { + matrix.mulP( points[ i ] ); + points[ i ].convolve( scale ); + } + + // Transform planes. + + const U32 numPlanes = this->getNumPlanes(); + typename Base::PlaneType* planes = this->getPlanes(); + + PlaneTransformer transformer; + transformer.set( matrix, scale ); + + for( U32 i = 0; i < numPlanes; ++ i ) + { + const typename Base::PlaneType& plane = planes[ i ]; + + PlaneF result; + transformer.transform( plane, result ); + planes[ i ] = result; + } +} + +//----------------------------------------------------------------------------- + +template< typename Base > +U32 PolyhedronImpl< Base >::constructIntersection( const PlaneF& plane, Point3F* outPoints, U32 maxOutPoints ) const +{ + // The assumption here is that the polyhedron is entirely composed + // of convex polygons implicitly described by the edges, points, and planes. + // So, any polygon can only have one of three relations to the given plane + // + // 1) None of its edges intersect with the plane, i.e. the polygon can be ignored. + // 2) One of its edges lies on the plane. + // 2) Two of its edges intersect with the plane. + // + // Conceptually, we need to find the first face with an intersecting edge, then find + // another edge on the same face that is also intersecting, and then continue with the + // face on the opposite side of the edge. + + const U32 numEdges = this->getNumEdges(); + const typename Base::EdgeType* edges = this->getEdges(); + const typename Base::PointType* points = this->getPoints(); + U32 numOutPoints = 0; + + #define ADD_POINT( p ) \ + if( numOutPoints >= maxOutPoints ) \ + return 0; \ + outPoints[ numOutPoints ++ ] = p; + + Point3F intersection; + + S32 firstEdge = -1; + S32 firstFace = -1; + + U32 v1 = 0; + U32 v2 = 0; + + PlaneF::Side v1Side = PlaneF::Front; + PlaneF::Side v2Side = PlaneF::Front; + + // Find an edge to start with. This is the first edge + // in the polyhedron that intersects the plane. + + for( U32 i = 0; i < numEdges; ++ i ) + { + const typename Base::EdgeType& edge = edges[ i ]; + + v1 = edge.vertex[ 0 ]; + v2 = edge.vertex[ 1 ]; + + const Point3F& p1 = points[ v1 ]; + const Point3F& p2 = points[ v2 ]; + + v1Side = plane.whichSide( p1 ); + v2Side = plane.whichSide( p2 ); + + if( v1Side == PlaneF::On || v2Side == PlaneF::On || + plane.clipSegment( p1, p2, intersection ) ) + { + firstEdge = i; + firstFace = edge.face[ 0 ]; + break; + } + } + + if( firstEdge == -1 ) + return 0; + + // Slice around the faces of the polyhedron until + // we get back to the starting face. + + U32 currentEdge = firstEdge; + U32 currentFace = firstFace; + + do + { + // Handle the current edge. + + if( v1Side == PlaneF::On && v2Side == PlaneF::On ) + { + // Both points of the edge lie on the plane. Add v2 + // and then look for an edge that is also connected to v1 + // *and* is connected to our current face. The other face + // of that edge is the one we need to continue with. + + ADD_POINT( points[ v2 ] ); + + for( U32 n = 0; n < numEdges; ++ n ) + { + const typename Base::EdgeType& e = edges[ n ]; + + // Skip current edge. + if( n == currentEdge ) + continue; + + // Skip edges not belonging to current face. + if( e.face[ 0 ] != currentFace && e.face[ 1 ] != currentFace ) + continue; + + // Skip edges not connected to the current point. + if( e.vertex[ 0 ] != edges[ currentEdge ].vertex[ 0 ] && + e.vertex[ 1 ] != edges[ currentEdge ].vertex[ 0 ] ) + continue; + + // It's our edge. Continue on with the face that + // isn't our current one. + + if( e.face[ 0 ] == currentFace ) + currentFace = e.face[ 1 ]; + else + currentFace = e.face[ 0 ]; + currentEdge = n; + break; + } + } + else if( v1Side == PlaneF::On || v2Side == PlaneF::On ) + { + // One of the points of the current edge is on the plane. + // Add that point. + + if( v1Side == PlaneF::On ) + { + ADD_POINT( points[ v1 ] ); + } + else + { + ADD_POINT( points[ v2 ] ); + } + + // Find edge to continue with. + + for( U32 n = 0; n < numEdges; ++ n ) + { + const typename Base::EdgeType& e = edges[ n ]; + + // Skip current edge. + if( n == currentEdge ) + continue; + + // Skip edges not belonging to current face. + if( e.face[ 0 ] != currentFace && e.face[ 1 ] != currentFace ) + continue; + + // Skip edges connected to point that is on the plane. + if( v1Side == PlaneF::On ) + { + if( e.vertex[ 0 ] == v1 || e.vertex[ 1 ] == v1 ) + continue; + } + else + { + if( e.vertex[ 0 ] == v2 || e.vertex[ 1 ] == v2 ) + continue; + } + + // Skip edges not intersecting the plane. + + U32 v1new = e.vertex[ 0 ]; + U32 v2new = e.vertex[ 1 ]; + + const Point3F& p1 = points[ v1new ]; + const Point3F& p2 = points[ v2new ]; + + PlaneF::Side v1SideNew = plane.whichSide( p1 ); + PlaneF::Side v2SideNew = plane.whichSide( p2 ); + + if( v1SideNew != PlaneF::On && v2SideNew != PlaneF::On && !plane.clipSegment( p1, p2, intersection ) ) + continue; + + // It's our edge. Continue with the face on the + // opposite side. + + if( e.face[ 0 ] == currentFace ) + currentFace = e.face[ 1 ]; + else + currentFace = e.face[ 0 ]; + currentEdge = n; + + v1 = v1new; + v2 = v2new; + + v1Side = v1SideNew; + v2Side = v2SideNew; + + break; + } + + // Already have computed all the data. + continue; + } + + // It's a clean intersecting somewhere along the edge. Add it. + + else + { + ADD_POINT( intersection ); + } + + // Find edge to continue with. + + for( U32 n = 0; n < numEdges; ++ n ) + { + const typename Base::EdgeType& e = edges[ n ]; + + // Skip current edge. + if( n == currentEdge ) + continue; + + // Skip edges not belonging to current face. + if( e.face[ 0 ] != currentFace && e.face[ 1 ] != currentFace ) + continue; + + // Skip edges not intersecting the plane. + + v1 = e.vertex[ 0 ]; + v2 = e.vertex[ 1 ]; + + const Point3F& p1 = points[ v1 ]; + const Point3F& p2 = points[ v2 ]; + + PlaneF::Side v1SideNew = plane.whichSide( p1 ); + PlaneF::Side v2SideNew = plane.whichSide( p2 ); + + if( v1SideNew != PlaneF::On && v2SideNew != PlaneF::On && !plane.clipSegment( p1, p2, intersection ) ) + continue; + + // It's our edge. Make it the current one. + + if( e.face[ 0 ] == currentFace ) + currentFace = e.face[ 1 ]; + else + currentFace = e.face[ 0 ]; + currentEdge = n; + + break; + } + } + //TODO: I guess this is insufficient; edges with vertices on the plane may lead us to take different + // paths depending on edge order + while( currentFace != firstFace && + currentEdge != firstEdge ); + + return numOutPoints; + + #undef ADD_POINT +} + +//----------------------------------------------------------------------------- + +template< typename Base > +template< typename IndexType > +U32 PolyhedronImpl< Base >::extractFace( U32 plane, IndexType* outIndices, U32 maxOutIndices ) const +{ + AssertFatal( plane < this->getNumPlanes(), "PolyhedronImpl::extractFace - Plane index out of range!" ); + + // This method relies on the fact that vertices on the edges must be CW ordered + // for face[0]. If that is not the case, it is still possible to infer the correct + // ordering by finding one edge and a point not on the edge but still on + // the polygon. By constructing a plane through that edge (simple cross product) and + // then seeing which side the point is on, we know which direction is the right one + // for the polygon. The implicit CW ordering spares us from having to do that, though. + + const U32 numEdges = this->getNumEdges(); + const Edge* edges = this->getEdges(); + + // Find first edge that belongs to the plane. + + const Edge* firstEdge = 0; + + for( U32 i = 0; i < numEdges; ++ i ) + { + const Edge& edge = edges[ i ]; + if( edge.face[ 0 ] == plane || edge.face[ 1 ] == plane ) + { + firstEdge = &edge; + break; + } + } + + // If we have found no edge, the polyhedron is degenerate, + // so abort. + + if( !firstEdge ) + return 0; + + // Choose vertex that begins a CCW traversal for this plane. + // + // Note that we expect the planes to be facing inwards by default so we + // go the opposite direction to yield a polygon facing the other way. + + U32 idx = 0; + U32 currentVertex; + const Edge* currentEdge = firstEdge; + + if( firstEdge->face[ 0 ] == plane ) + currentVertex = firstEdge->vertex[ 0 ]; + else + currentVertex = firstEdge->vertex[ 1 ]; + + // Now spider along the edges until we have gathered all indices + // for the plane in the right order. + // + // For larger edge sets, it would be more efficient to first extract + // all edges for the plane and then loop only over this subset to + // spider along the indices. However, we tend to have small sets + // so it should be sufficiently fast to just loop over the original + // set. + + do + { + // Add the vertex for the current edge. + + if( idx >= maxOutIndices ) + return 0; + + outIndices[ idx ++ ] = currentVertex; + + // Look for next edge. + + for( U32 i = 0; i < numEdges; ++ i ) + { + const Edge& edge = edges[ i ]; + + // Skip if we hit the edge that we are looking to continue from. + + if( &edge == currentEdge ) + continue; + + // Skip edge if it doesn't belong to the current plane. + + if( edge.face[ 0 ] != plane && edge.face[ 1 ] != plane ) + continue; + + // If edge connects to vertex we are looking for, make it + // the current edge and push its other vertex. + + if( edge.vertex[ 0 ] == currentVertex ) + currentVertex = edge.vertex[ 1 ]; + else if( edge.vertex[ 1 ] == currentVertex ) + currentVertex = edge.vertex[ 0 ]; + else + continue; // Skip edge. + + currentEdge = &edge; + break; + } + } + while( currentEdge != firstEdge ); + + // Done. + + return idx; +} + +//----------------------------------------------------------------------------- + +template< typename Polyhedron > +void PolyhedronData::buildBoxData( Polyhedron& poly, const MatrixF& mat, const Box3F& box, bool invertNormals ) +{ + AssertFatal( poly.getNumPoints() == 8, "PolyhedronData::buildBox - Incorrect point count!" ); + AssertFatal( poly.getNumEdges() == 12, "PolyhedronData::buildBox - Incorrect edge count!" ); + AssertFatal( poly.getNumPlanes() == 6, "PolyhedronData::buildBox - Incorrect plane count!" ); + + // Box is assumed to be axis aligned in the source space. + // Transform into geometry space. + + Point3F xvec = mat.getRightVector() * box.len_x(); + Point3F yvec = mat.getForwardVector() * box.len_y(); + Point3F zvec = mat.getUpVector() * box.len_z(); + + Point3F min; + mat.mulP( box.minExtents, &min ); + + // Corner points. + + typename Polyhedron::PointListType& pointList = poly.pointList; + + pointList[ 0 ] = min; // near left bottom + pointList[ 1 ] = min + yvec; // far left bottom + pointList[ 2 ] = min + xvec + yvec; // far right bottom + pointList[ 3 ] = min + xvec; // near right bottom + pointList[ 4 ] = pointList[ 0 ] + zvec; // near left top + pointList[ 5 ] = pointList[ 1 ] + zvec; // far left top + pointList[ 6 ] = pointList[ 2 ] + zvec; // far right top + pointList[ 7 ] = pointList[ 3 ] + zvec; // near right top + + // Side planes. + + typename Polyhedron::PlaneListType& planeList = poly.planeList; + + const F32 pos = invertNormals ? -1.f : 1.f; + const F32 neg = - pos; + + planeList[ 0 ].set( pointList[ 0 ], xvec * pos ); // left + planeList[ 1 ].set( pointList[ 2 ], yvec * neg ); // far + planeList[ 2 ].set( pointList[ 2 ], xvec * neg ); // right + planeList[ 3 ].set( pointList[ 0 ], yvec * pos ); // front + planeList[ 4 ].set( pointList[ 0 ], zvec * pos ); // bottom + planeList[ 5 ].set( pointList[ 4 ], zvec * neg ); // top + + // The edges are constructed so that the vertices + // are oriented clockwise for face[0]. + + typename Polyhedron::EdgeType* edge = &poly.edgeList[ 0 ]; + + for( U32 i = 0; i < 4; ++ i ) + { + S32 n = ( i == 3 ) ? 0: i + 1; + S32 p = ( i == 0 ) ? 3: i - 1; + + edge->vertex[ 0 ] = !invertNormals ? n : i; + edge->vertex[ 1 ] = !invertNormals ? i : n; + edge->face[ 0 ] = i; + edge->face[ 1 ] = 4; + edge ++; + + edge->vertex[ 0 ] = !invertNormals ? 4 + n : 4 + i; + edge->vertex[ 1 ] = !invertNormals ? 4 + i : 4 + n; + edge->face[ 0 ] = 5; + edge->face[ 1 ] = i; + edge ++; + + edge->vertex[ 0 ] = !invertNormals ? 4 + i : i; + edge->vertex[ 1 ] = !invertNormals ? i : 4 + i; + edge->face[ 0 ] = p; + edge->face[ 1 ] = i; + edge ++; + } +} diff --git a/Engine/source/math/mQuadPatch.cpp b/Engine/source/math/mQuadPatch.cpp new file mode 100644 index 000000000..24e67dd33 --- /dev/null +++ b/Engine/source/math/mQuadPatch.cpp @@ -0,0 +1,76 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "math/mQuadPatch.h" + + +//****************************************************************************** +// Quadratic spline patch +//****************************************************************************** +QuadPatch::QuadPatch() +{ + setNumReqControlPoints(3); +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +void QuadPatch::calcABC( const Point3F *points ) +{ + a = points[2] - points[1]; + b = points[1] - points[0]; + c = points[0]; +} + + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +void QuadPatch::submitControlPoints( SplCtrlPts &points ) +{ + Parent::submitControlPoints( points ); + calcABC( points.getPoint(0) ); +}; + + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +void QuadPatch::setControlPoint( Point3F &point, int index ) +{ + ( (SplCtrlPts*) getControlPoints() )->setPoint( point, index ); + calcABC( getControlPoint(0) ); +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +void QuadPatch::calc( F32 t, Point3F &result ) +{ + F32 t2 = t*t; + result = a*t2 + b*t + c; +} + + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +void QuadPatch::calc( Point3F *points, F32 t, Point3F &result ) +{ + calcABC( points ); + calc( t, result ); +} diff --git a/Engine/source/math/mQuadPatch.h b/Engine/source/math/mQuadPatch.h new file mode 100644 index 000000000..22dcb1ffc --- /dev/null +++ b/Engine/source/math/mQuadPatch.h @@ -0,0 +1,62 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MQUADPATCH_H_ +#define _MQUADPATCH_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _MSPLINEPATCH_H_ +#include "math/mSplinePatch.h" +#endif + +//------------------------------------------------------------------------------ +/// Quadratic spline patch. This is a special type of spline that only had 3 control points. +/// @see SplinePatch +class QuadPatch : public SplinePatch +{ + typedef SplinePatch Parent; + +private: + Point3F a, b, c; + + void calcABC( const Point3F *points ); + +public: + + QuadPatch(); + + virtual void calc( F32 t, Point3F &result ); + virtual void calc( Point3F *points, F32 t, Point3F &result ); + virtual void setControlPoint( Point3F &point, int index ); + virtual void submitControlPoints( SplCtrlPts &points ); + + +}; + + + +#endif diff --git a/Engine/source/math/mQuat.cpp b/Engine/source/math/mQuat.cpp new file mode 100644 index 000000000..78c9fb100 --- /dev/null +++ b/Engine/source/math/mQuat.cpp @@ -0,0 +1,329 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "math/mQuat.h" + +#include "math/mAngAxis.h" +#include "math/mMatrix.h" +#include "platform/profiler.h" + + +const QuatF QuatF::Identity(0.0f,0.0f,0.0f,1.0f); + +QuatF& QuatF::set( const EulerF & e ) +{ + F32 cx, sx; + F32 cy, sy; + F32 cz, sz; + mSinCos( -e.x * 0.5f, sx, cx ); + mSinCos( -e.y * 0.5f, sy, cy ); + mSinCos( -e.z * 0.5f, sz, cz ); + + // Qyaw(z) = [ (0, 0, sin z/2), cos z/2 ] + // Qpitch(x) = [ (sin x/2, 0, 0), cos x/2 ] + // Qroll(y) = [ (0, sin y/2, 0), cos y/2 ] + // this = Qresult = Qyaw*Qpitch*Qroll ZXY + // + // The code that folows is a simplification of: + // roll*=pitch; + // roll*=yaw; + // *this = roll; + F32 cycz, sysz, sycz, cysz; + cycz = cy*cz; + sysz = sy*sz; + sycz = sy*cz; + cysz = cy*sz; + w = cycz*cx + sysz*sx; + x = cycz*sx + sysz*cx; + y = sycz*cx - cysz*sx; + z = cysz*cx - sycz*sx; + + return *this; +} + +QuatF& QuatF::operator *=( const QuatF & b ) +{ + QuatF prod; + prod.w = w * b.w - x * b.x - y * b.y - z * b.z; + prod.x = w * b.x + x * b.w + y * b.z - z * b.y; + prod.y = w * b.y + y * b.w + z * b.x - x * b.z; + prod.z = w * b.z + z * b.w + x * b.y - y * b.x; + *this = prod; + return (*this); +} + +QuatF& QuatF::operator /=( const QuatF & c ) +{ + QuatF temp = c; + return ( (*this) *= temp.inverse() ); +} + +QuatF& QuatF::square() +{ + F32 t = w*2.0f; + w = (w*w) - (x*x + y*y + z*z); + x *= t; + y *= t; + z *= t; + return *this; +} + +QuatF& QuatF::inverse() +{ + F32 magnitude = w*w + x*x + y*y + z*z; + F32 invMagnitude; + if( magnitude == 1.0f ) // special case unit quaternion + { + x = -x; + y = -y; + z = -z; + } + else // else scale + { + if( magnitude == 0.0f ) + invMagnitude = 1.0f; + else + invMagnitude = 1.0f / magnitude; + w *= invMagnitude; + x *= -invMagnitude; + y *= -invMagnitude; + z *= -invMagnitude; + } + return *this; +} + +QuatF & QuatF::set( const AngAxisF & a ) +{ + return set( a.axis, a.angle ); +} + +QuatF & QuatF::set( const Point3F &axis, F32 angle ) +{ + PROFILE_SCOPE( QuatF_set_AngAxisF ); + + F32 sinHalfAngle, cosHalfAngle; + mSinCos( angle * 0.5f, sinHalfAngle, cosHalfAngle ); + x = axis.x * sinHalfAngle; + y = axis.y * sinHalfAngle; + z = axis.z * sinHalfAngle; + w = cosHalfAngle; + return *this; +} + +QuatF & QuatF::normalize() +{ + PROFILE_SCOPE( QuatF_normalize ); + + F32 l = mSqrt( x*x + y*y + z*z + w*w ); + if( l == 0.0f ) + identity(); + else + { + x /= l; + y /= l; + z /= l; + w /= l; + } + return *this; +} + +#define idx(r,c) (r*4 + c) + +QuatF & QuatF::set( const MatrixF & mat ) +{ + PROFILE_SCOPE( QuatF_set_MatrixF ); + + F32 const *m = mat; + + F32 trace = m[idx(0, 0)] + m[idx(1, 1)] + m[idx(2, 2)]; + if (trace > 0.0f) + { + F32 s = mSqrt(trace + F32(1)); + w = s * 0.5f; + s = 0.5f / s; + x = (m[idx(1,2)] - m[idx(2,1)]) * s; + y = (m[idx(2,0)] - m[idx(0,2)]) * s; + z = (m[idx(0,1)] - m[idx(1,0)]) * s; + } + else + { + F32* q = &x; + U32 i = 0; + if (m[idx(1, 1)] > m[idx(0, 0)]) i = 1; + if (m[idx(2, 2)] > m[idx(i, i)]) i = 2; + U32 j = (i + 1) % 3; + U32 k = (j + 1) % 3; + + F32 s = mSqrt((m[idx(i, i)] - (m[idx(j, j)] + m[idx(k, k)])) + 1.0f); + q[i] = s * 0.5f; + s = 0.5f / s; + q[j] = (m[idx(i,j)] + m[idx(j,i)]) * s; + q[k] = (m[idx(i,k)] + m[idx(k, i)]) * s; + w = (m[idx(j,k)] - m[idx(k, j)]) * s; + } + + // Added to resolve issue #2230 + normalize(); + + return *this; +} + +MatrixF * QuatF::setMatrix( MatrixF * mat ) const +{ + if( x*x + y*y + z*z < 10E-20f) // isIdentity() -- substituted code a little more stringent but a lot faster + mat->identity(); + else + m_quatF_set_matF( x, y, z, w, *mat ); + return mat; +} + +QuatF & QuatF::slerp( const QuatF & q, F32 t ) +{ + return interpolate( *this, q, t ); +} + +QuatF & QuatF::extrapolate( const QuatF & q1, const QuatF & q2, F32 t ) +{ + // assert t >= 0 && t <= 1 + // q1 is value at time = 0 + // q2 is value at time = t + // Computes quaternion at time = 1 + F64 flip,cos = q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w; + if (cos < 0.0) + { + cos = -cos; + flip = -1.0; + } + else + flip = 1.0; + + F64 s1,s2; + if ((1.0 - cos) > 0.00001) + { + F64 om = mAcos(cos) / t; + F64 sd = 1.0 / mSin(t * om); + s1 = flip * mSin(om) * sd; + s2 = mSin((1.0 - t) * om) * sd; + } + else + { + // If quats are very close, do linear interpolation + s1 = flip / t; + s2 = (1.0 - t) / t; + } + + x = F32(s1 * q2.x - s2 * q1.x); + y = F32(s1 * q2.y - s2 * q1.y); + z = F32(s1 * q2.z - s2 * q1.z); + w = F32(s1 * q2.w - s2 * q1.w); + + return *this; +} + +QuatF & QuatF::interpolate( const QuatF & q1, const QuatF & q2, F32 t ) +{ + //----------------------------------- + // Calculate the cosine of the angle: + + double cosOmega = q1.dot( q2 ); + + //----------------------------------- + // adjust signs if necessary: + + F32 sign2; + if ( cosOmega < 0.0 ) + { + cosOmega = -cosOmega; + sign2 = -1.0f; + } + else + sign2 = 1.0f; + + //----------------------------------- + // calculate interpolating coeffs: + + double scale1, scale2; + if ( (1.0 - cosOmega) > 0.00001 ) + { + // standard case + double omega = mAcos(cosOmega); + double sinOmega = mSin(omega); + scale1 = mSin((1.0 - t) * omega) / sinOmega; + scale2 = sign2 * mSin(t * omega) / sinOmega; + } + else + { + // if quats are very close, just do linear interpolation + scale1 = 1.0 - t; + scale2 = sign2 * t; + } + + + //----------------------------------- + // actually do the interpolation: + + x = F32(scale1 * q1.x + scale2 * q2.x); + y = F32(scale1 * q1.y + scale2 * q2.y); + z = F32(scale1 * q1.z + scale2 * q2.z); + w = F32(scale1 * q1.w + scale2 * q2.w); + return *this; +} + +Point3F & QuatF::mulP(const Point3F& p, Point3F* r) +{ + QuatF qq; + QuatF qi = *this; + QuatF qv( p.x, p.y, p.z, 0.0f); + + qi.inverse(); + qq.mul(qi, qv); + qv.mul(qq, *this); + r->set(qv.x, qv.y, qv.z); + return *r; +} + +QuatF & QuatF::mul( const QuatF &a, const QuatF &b) +{ + AssertFatal( &a != this && &b != this, "QuatF::mul: dest should not be same as source" ); + w = a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z; + x = a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y; + y = a.w * b.y + a.y * b.w + a.z * b.x - a.x * b.z; + z = a.w * b.z + a.z * b.w + a.x * b.y - a.y * b.x; + return *this; +} + +QuatF & QuatF::shortestArc( const VectorF &a, const VectorF &b ) +{ + // From Game Programming Gems pg. 217 + VectorF c = mCross( a, b ); + F32 d = mDot( a, b ); + F32 s = mSqrt( ( 1 + d ) * 2 ); + + x = c.x / s; + y = c.y / s; + z = c.z / s; + w = s / 2.f; + + return *this; +} + diff --git a/Engine/source/math/mQuat.h b/Engine/source/math/mQuat.h new file mode 100644 index 000000000..34cae7e84 --- /dev/null +++ b/Engine/source/math/mQuat.h @@ -0,0 +1,234 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MQUAT_H_ +#define _MQUAT_H_ + +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif + +class MatrixF; +class AngAxisF; + +//---------------------------------------------------------------------------- +// unit quaternion class: + +class QuatF +{ + //-------------------------------------- Public static constants +public: + const static QuatF Identity; + + public: + F32 x,y,z,w; + + QuatF() {} // no init constructor + QuatF( F32 _x, F32 _y, F32 _z, F32 w ); + QuatF( const Point3F &axis, F32 angle ); + QuatF( const MatrixF & m ); + QuatF( const AngAxisF & a ); + QuatF( const EulerF & e ); + + QuatF& set( F32 _x, F32 _y, F32 _z, F32 _w ); + QuatF& set( const Point3F &axis, F32 angle ); + QuatF& set( const MatrixF & m ); + QuatF& set( const AngAxisF & a ); + QuatF& set( const EulerF & e ); + + int operator ==( const QuatF & c ) const; + int operator !=( const QuatF & c ) const; + QuatF& operator *=( const QuatF & c ); + QuatF& operator /=( const QuatF & c ); + QuatF& operator +=( const QuatF & c ); + QuatF& operator -=( const QuatF & c ); + QuatF& operator *=( F32 a ); + QuatF& operator /=( F32 a ); + + QuatF operator-( const QuatF &c ) const; + QuatF operator*( F32 a ) const; + + QuatF& square(); + QuatF& neg(); + F32 dot( const QuatF &q ) const; + + MatrixF* setMatrix( MatrixF * mat ) const; + QuatF& normalize(); + QuatF& inverse(); + QuatF& identity(); + int isIdentity() const; + QuatF& slerp( const QuatF & q, F32 t ); + QuatF& extrapolate( const QuatF & q1, const QuatF & q2, F32 t ); + QuatF& interpolate( const QuatF & q1, const QuatF & q2, F32 t ); + F32 angleBetween( const QuatF & q ); + + Point3F& mulP(const Point3F& a, Point3F* b); // r = p * this + QuatF& mul(const QuatF& a, const QuatF& b); // This = a * b + + // Vectors passed in must be normalized + QuatF& shortestArc( const VectorF &normalizedA, const VectorF &normalizedB ); +}; + +// a couple simple utility methods +inline F32 QuatIsEqual(F32 a,F32 b,F32 epsilon = 0.0001f) { return mFabs(a-b) < epsilon; } +inline F32 QuatIsZero(F32 a,F32 epsilon = 0.0001f) { return mFabs(a) < epsilon; } + +//---------------------------------------------------------------------------- +// quaternion implementation: + +inline QuatF::QuatF( F32 _x, F32 _y, F32 _z, F32 _w ) +{ + set( _x, _y, _z, _w ); +} + +inline QuatF::QuatF( const Point3F &axis, F32 angle ) +{ + set( axis, angle ); +} + +inline QuatF::QuatF( const AngAxisF & a ) +{ + set( a ); +} + +inline QuatF::QuatF( const EulerF & e ) +{ + set( e ); +} + +inline QuatF::QuatF( const MatrixF & m ) +{ + set( m ); +} + +inline QuatF& QuatF::set( F32 _x, F32 _y, F32 _z, F32 _w ) +{ + x = _x; + y = _y; + z = _z; + w = _w; + return *this; +} + +inline int QuatF::operator ==( const QuatF & c ) const +{ + QuatF a = *this; + QuatF b = c; + a.normalize(); + b.normalize(); + b.inverse(); + a *= b; + return a.isIdentity(); +} + +inline int QuatF::isIdentity() const +{ + return QuatIsZero( x ) && QuatIsZero( y ) && QuatIsZero( z ); +} + +inline QuatF& QuatF::identity() +{ + x = 0.0f; + y = 0.0f; + z = 0.0f; + w = 1.0f; + return *this; +} + +inline int QuatF::operator !=( const QuatF & c ) const +{ + return !operator==( c ); +} + +inline QuatF& QuatF::operator +=( const QuatF & c ) +{ + x += c.x; + y += c.y; + z += c.z; + w += c.w; + return *this; +} + +inline QuatF& QuatF::operator -=( const QuatF & c ) +{ + x -= c.x; + y -= c.y; + z -= c.z; + w -= c.w; + return *this; +} + +inline QuatF& QuatF::operator *=( F32 a ) +{ + x *= a; + y *= a; + z *= a; + w *= a; + return *this; +} + +inline QuatF& QuatF::operator /=( F32 a ) +{ + x /= a; + y /= a; + z /= a; + w /= a; + return *this; +} + +inline QuatF QuatF::operator -( const QuatF &c ) const +{ + return QuatF( x - c.x, + y - c.y, + z - c.z, + w - c.w ); +} + +inline QuatF QuatF::operator *( F32 a ) const +{ + return QuatF( x * a, + y * a, + z * a, + w * a ); +} + +inline QuatF& QuatF::neg() +{ + x = -x; + y = -y; + z = -z; + w = -w; + return *this; +} + +inline F32 QuatF::dot( const QuatF &q ) const +{ + return (w*q.w + x*q.x + y*q.y + z*q.z); +} + +inline F32 QuatF::angleBetween( const QuatF & q ) +{ + // angle between to quaternions + return mAcos(x * q.x + y * q.y + z * q.z + w * q.w); +} + +#endif // _MQUAT_H_ diff --git a/Engine/source/math/mRandom.cpp b/Engine/source/math/mRandom.cpp new file mode 100644 index 000000000..7e808d7ac --- /dev/null +++ b/Engine/source/math/mRandom.cpp @@ -0,0 +1,171 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "math/mRandom.h" +#include "core/util/journal/journal.h" + +MRandomLCG gRandGen; +U32 gRandGenSeed = 1376312589; + +void MRandomLCG::setGlobalRandSeed(U32 seed) +{ + if (Journal::IsPlaying()) + Journal::Read(&gRandGenSeed); + else + { + gRandGenSeed = seed; + if (Journal::IsRecording()) + Journal::Write(gRandGenSeed); + } + + //now actually set the seed + gRandGen.setSeed(gRandGenSeed); +} + +static U32 msSeed = 1376312589; + +inline U32 generateSeed() +{ + // A very, VERY crude LCG but good enough to generate + // a nice range of seed values + msSeed = (msSeed * 0x015a4e35L) + 1; + msSeed = (msSeed>>16)&0x7fff; + return (msSeed); +} + +//-------------------------------------- +void MRandomGenerator::setSeed() +{ + setSeed(generateSeed()); +} + + +//-------------------------------------- +const S32 MRandomLCG::msQuotient = S32_MAX / 16807L; +const S32 MRandomLCG::msRemainder = S32_MAX % 16807L; + + +//-------------------------------------- +MRandomLCG::MRandomLCG() +{ + setSeed(generateSeed()); +} + +MRandomLCG::MRandomLCG(S32 s) +{ + setSeed(s); +} + + +//-------------------------------------- +void MRandomLCG::setSeed(S32 s) +{ + mSeed = s; +} + + +//-------------------------------------- +U32 MRandomLCG::randI() +{ + if ( mSeed <= msQuotient ) + mSeed = (mSeed * 16807L) % S32_MAX; + else + { + S32 high_part = mSeed / msQuotient; + S32 low_part = mSeed % msQuotient; + + S32 test = (16807L * low_part) - (msRemainder * high_part); + + if ( test > 0 ) + mSeed = test; + else + mSeed = test + S32_MAX; + + } + return mSeed; +} + + + +//-------------------------------------- +MRandomR250::MRandomR250() +{ + setSeed(generateSeed()); +} + +MRandomR250::MRandomR250(S32 s) +{ + setSeed(s); +} + + +//-------------------------------------- +void MRandomR250::setSeed(S32 s) +{ + mSeed = s; + MRandomLCG lcg( s ); + mIndex = 0; + + S32 j; + for (j = 0; j < 250; j++) // fill r250 buffer with bit values + mBuffer[j] = lcg.randI(); + + for (j = 0; j < 250; j++) // set some MSBs to 1 + if ( lcg.randI() > 0x40000000L ) + mBuffer[j] |= 0x80000000L; + + + U32 msb = 0x80000000; // turn on diagonal bit + U32 mask = 0xffffffff; // turn off the leftmost bits + + for (j = 0; j < 32; j++) + { + S32 k = 7 * j + 3; // select a word to operate on + mBuffer[k] &= mask; // turn off bits left of the diagonal + mBuffer[k] |= msb; // turn on the diagonal bit + mask >>= 1; + msb >>= 1; + } +} + + +//-------------------------------------- +U32 MRandomR250::randI() +{ + S32 j; + + // wrap pointer around + if ( mIndex >= 147 ) j = mIndex - 147; + else j = mIndex + 103; + + U32 new_rand = mBuffer[ mIndex ] ^ mBuffer[ j ]; + mBuffer[ mIndex ] = new_rand; + + // increment pointer for next time + if ( mIndex >= 249 ) mIndex = 0; + else mIndex++; + + return new_rand >> 1; +} + + diff --git a/Engine/source/math/mRandom.h b/Engine/source/math/mRandom.h new file mode 100644 index 000000000..dbe5ad970 --- /dev/null +++ b/Engine/source/math/mRandom.h @@ -0,0 +1,140 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MRANDOM_H_ +#define _MRANDOM_H_ + +#ifndef _PLATFORMASSERT_H_ +#include "platform/platformAssert.h" +#endif + + +//-------------------------------------- +/// Base class for random number generators +class MRandomGenerator +{ +protected: + MRandomGenerator() {} + S32 mSeed; + +public: + virtual ~MRandomGenerator() {} + + void setSeed(); + S32 getSeed() { return mSeed; } + virtual void setSeed(S32 s) = 0; + + virtual U32 randI( void ) = 0; ///< 0..2^31 random number generator + virtual F32 randF( void ); ///< 0.0 .. 1.0 F32 random number generator + S32 randI(S32 i, S32 n); ///< i..n integer random number generator + F32 randF(F32 i, F32 n); ///< i..n F32 random number generator +}; + + +//-------------------------------------- +inline F32 MRandomGenerator::randF() +{ + // default: multiply by 1/(2^31) + return F32(randI()) * (1.0f/2147483647.0f); +} + +inline S32 MRandomGenerator::randI(S32 i, S32 n) +{ + AssertFatal(i<=n, "MRandomGenerator::randi: inverted range."); + return (S32)(i + (randI() % (n - i + 1)) ); +} + +inline F32 MRandomGenerator::randF(F32 i, F32 n) +{ + AssertFatal(i<=n, "MRandomGenerator::randf: inverted range."); + return (i + (n - i) * randF()); +} + + +//-------------------------------------- +/// Linear Congruential Method, the "minimal standard generator" +/// +/// Fast, farly good random numbers (better than using rand) +/// +/// @author Park & Miller, 1988, Comm of the ACM, 31(10), pp. 1192-1201 +class MRandomLCG : public MRandomGenerator +{ +protected: + static const S32 msQuotient; + static const S32 msRemainder; + +public: + MRandomLCG(); + MRandomLCG(S32 s); + virtual ~MRandomLCG() {} + + static void setGlobalRandSeed(U32 seed); + + void setSeed(S32 s); +// using MRandomGenerator::randI; + S32 randI(S32 i, S32 n); ///< i..n integer generator + + U32 randI( void ); + +}; + +// Solution to "using" problem. +inline S32 MRandomLCG::randI(S32 i, S32 n) +{ + return( MRandomGenerator::randI(i,n) ); +} + + +//-------------------------------------- +/// Fast, very good random numbers +/// +/// Period = 2^249 +/// +/// Kirkpatrick, S., and E. Stoll, 1981; A Very Fast Shift-Register +/// Sequence Random Number Generator, Journal of Computational Physics, +/// V. 40. +/// +/// Maier, W.L., 1991; A Fast Pseudo Random Number Generator, +/// Dr. Dobb's Journal, May, pp. 152 - 157 +class MRandomR250: public MRandomGenerator +{ +private: + U32 mBuffer[250]; + S32 mIndex; + +public: + MRandomR250(); + MRandomR250(S32 s); + virtual ~MRandomR250() {} + + void setSeed(S32 s); +// using MRandomGenerator::randI; + U32 randI(); +}; + + +typedef MRandomLCG MRandom; + +extern MRandomLCG gRandGen; + + +#endif //_MRANDOM_H_ diff --git a/Engine/source/math/mRandomDeck.h b/Engine/source/math/mRandomDeck.h new file mode 100644 index 000000000..2d51f8ca6 --- /dev/null +++ b/Engine/source/math/mRandomDeck.h @@ -0,0 +1,138 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MRANDOMDECK_H_ +#define _MRANDOMDECK_H_ + +#ifndef _MRANDOM_H_ +#include "math/mRandom.h" +#endif + +template +class MRandomDeck +{ +protected: + + MRandomLCG *mRandGen; + + Vector mDeck; + + Vector mPile; + +public: + + MRandomDeck( MRandomLCG *randGen = &gRandGen ); + + void addToPile( const T &item ); + + void addToPile( const Vector &items ); + + void shuffle(); + + S32 draw( T *item, bool reshuffle = true ); + + void removeAll( Vector *outItems ); + +}; + +template +inline MRandomDeck::MRandomDeck( MRandomLCG *randGen ) + : mRandGen( randGen ) +{ +} + +template +inline void MRandomDeck::shuffle() +{ + // Move everything to the pile. + mPile.merge( mDeck ); + + if ( mPile.empty() ) + return; + T& last = mPile.last(); + mDeck.clear(); + + // Randomly draw from the pile + // and place them in the deck. + while ( !mPile.empty() ) + { + U32 i = mRandGen->randI( 0, mPile.size() - 1 ); + mDeck.push_back( mPile[i] ); + mPile.erase_fast( i ); + } + + // Make sure that the first drawn item + // is not the same as the last drawn item. + if ( mDeck.last() == last ) + { + mDeck.pop_back(); + mDeck.push_front( last ); + } +} + +template +inline S32 MRandomDeck::draw( T *item, bool reshuffle ) +{ + if ( mDeck.size() == 0 ) + { + if ( mPile.size() == 0 ) + return -1; + + if ( reshuffle ) + shuffle(); + else + return -1; + } + + *item = mDeck.last(); + mPile.push_back( *item ); + mDeck.pop_back(); + + return mDeck.size(); +} + +template +inline void MRandomDeck::addToPile( const T &item ) +{ + mPile.push_back( item ); +} + +template +inline void MRandomDeck::addToPile( const Vector &items ) +{ + mPile.merge( items ); +} + +template +inline void MRandomDeck::removeAll( Vector *outItems ) +{ + if ( outItems ) + { + outItems->merge( mPile ); + outItems->merge( mDeck ); + } + + mPile.clear(); + mDeck.clear(); +} + +#endif //_MRANDOMDECK_H_ diff --git a/Engine/source/math/mRandomSet.h b/Engine/source/math/mRandomSet.h new file mode 100644 index 000000000..488c430c9 --- /dev/null +++ b/Engine/source/math/mRandomSet.h @@ -0,0 +1,93 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MRANDOMSET_H_ +#define _MRANDOMSET_H_ + +#ifndef _MRANDOM_H_ +#include "math/mRandom.h" +#endif + +template +class MRandomSet +{ +protected: + + MRandomLCG *mRandGen; + + Vector mItems; + Vector mProbability; + F32 mSum; + +public: + + MRandomSet( MRandomLCG *randGen = &gRandGen ); + + void add( const T &item, F32 probability ); + + /// Return a random item from the set using the specified per + /// item probability distribution. + T get(); +}; + +template +inline MRandomSet::MRandomSet( MRandomLCG *randGen ) + : mRandGen( randGen ), + mSum( 0.0f ) +{ +} + +template +inline void MRandomSet::add( const T &item, F32 probability ) +{ + AssertFatal( probability > 0.0f, "MRandomDeck - item probability must be positive." ); + + mItems.push_back( item ); + mProbability.push_back( probability ); + mSum += probability; +} + +template +inline T MRandomSet::get() +{ + AssertFatal( mSum > 0.0f, "MRandomDeck - no items to get." ); + + F32 rand = mRandGen->randF(0.0f, mSum); + + F32 prev = -1.0f; + F32 curr = 0.0f; + + for ( S32 i = 0; i < mItems.size(); i++ ) + { + curr += mProbability[i]; + + if ( rand > prev && rand <= curr ) + return mItems[i]; + + prev = curr; + } + + AssertFatal( false, "MRandomSet::get() has failed." ); + return NULL; +} + +#endif //_MRANDOMSET_H_ diff --git a/Engine/source/math/mRect.cpp b/Engine/source/math/mRect.cpp new file mode 100644 index 000000000..7ba2a718c --- /dev/null +++ b/Engine/source/math/mRect.cpp @@ -0,0 +1,28 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "math/mRect.h" + + +const RectI RectI::Zero( 0, 0, 0, 0 ); +const RectI RectI::One( 0, 0, 1, 1 ); diff --git a/Engine/source/math/mRect.h b/Engine/source/math/mRect.h new file mode 100644 index 000000000..8b4fb6249 --- /dev/null +++ b/Engine/source/math/mRect.h @@ -0,0 +1,523 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MRECT_H_ +#define _MRECT_H_ + +#ifndef _MPOINT2_H_ +#include "math/mPoint2.h" +#endif + + +class RectI +{ + public: + Point2I point; + Point2I extent; + + public: + RectI() { } + RectI(const Point2I& in_rMin, + const Point2I& in_rExtent); + RectI(const S32 in_left, const S32 in_top, + const S32 in_width, const S32 in_height); + + void set(const Point2I& in_rMin, const Point2I& in_rExtent); + void set(const S32 in_left, const S32 in_top, + const S32 in_width, const S32 in_height); + + bool intersect(const RectI& clipRect); + bool pointInRect(const Point2I& pt) const; + bool overlaps(RectI R) const; + bool contains(const RectI& R) const; + void inset(S32 x, S32 y); + + void unionRects(const RectI&); + + S32 len_x() const; + S32 len_y() const; + + /// Returns the area of the rectangle. + S32 area() const { return extent.x * extent.y; } + + bool operator==(const RectI&) const; + bool operator!=(const RectI&) const; + + bool isValidRect() const { return (extent.x > 0 && extent.y > 0); } + +public: + + /// A rect of zero extent. + static const RectI Zero; + + /// A rect of 1,1 extent. + static const RectI One; + +}; + +class RectF +{ + public: + Point2F point; + Point2F extent; + + public: + RectF() { } + RectF(const Point2F& in_rMin, + const Point2F& in_rExtent); + RectF(const F32 in_left, const F32 in_top, + const F32 in_width, const F32 in_height); + + void set(const Point2F& in_rMin, const Point2F& in_rExtent); + void set(const F32 in_left, const F32 in_top, + const F32 in_width, const F32 in_height); + + void inset(F32 x, F32 y); + + /// Return distance of the reference point to the rectangle. + F32 getDistanceToPoint( const Point2F &refPt ) const; + + /// Return the squared distance of the reference point to the rectangle. + F32 getSqDistanceToPoint( const Point2F &refPt ) const; + + bool intersect(const RectF& clipRect); + bool pointInRect(const Point2F& pt) const; + bool overlaps(const RectF&) const; + bool contains(const Point2F &p) const + { + Point2F minkowskiP = p - point; + + // If we're beyond origin... + if(minkowskiP.x < 0 || minkowskiP.y < 0) + return false; + + // Or past extent... + if(minkowskiP.x > extent.x || minkowskiP.y > extent.y) + return false; + + // Otherwise we're ok. + return true; + } + + bool contains(const RectF& R) const; + + void unionRects( const RectF &rect ); + + F32 len_x() const; + F32 len_y() const; + + bool isValidRect() const { return (extent.x > 0.0f && extent.y > 0.0f); } + inline bool intersectTriangle(const Point2F &a, const Point2F &b, const Point2F &c); + +}; + +class RectD +{ + public: + Point2D point; + Point2D extent; + + public: + RectD() { } + RectD(const Point2D& in_rMin, + const Point2D& in_rExtent); + RectD(const F64 in_left, const F64 in_top, + const F64 in_width, const F64 in_height); + void inset(F64 x, F64 y); + + bool intersect(const RectD& clipRect); + F64 len_x() const; + F64 len_y() const; + + bool isValidRect() const { return (extent.x > 0 && extent.y > 0); } +}; + +//------------------------------------------------------------------------------ +//-------------------------------------- INLINES (RectI) +// +inline +RectI::RectI(const Point2I& in_rMin, + const Point2I& in_rExtent) + : point(in_rMin), + extent(in_rExtent) +{ + // +} + +inline +RectI::RectI(const S32 in_left, const S32 in_top, + const S32 in_width, const S32 in_height) + : point(in_left, in_top), + extent(in_width, in_height) +{ + // +} + +inline void RectI::set(const Point2I& in_rMin, const Point2I& in_rExtent) +{ + point = in_rMin; + extent = in_rExtent; +} + +inline void RectI::set(const S32 in_left, const S32 in_top, + const S32 in_width, const S32 in_height) +{ + point.set(in_left, in_top); + extent.set(in_width, in_height); +} + +inline bool RectI::intersect(const RectI& clipRect) +{ + Point2I bottomL; + bottomL.x = getMin(point.x + extent.x - 1, clipRect.point.x + clipRect.extent.x - 1); + bottomL.y = getMin(point.y + extent.y - 1, clipRect.point.y + clipRect.extent.y - 1); + + point.x = getMax(point.x, clipRect.point.x); + point.y = getMax(point.y, clipRect.point.y); + + extent.x = bottomL.x - point.x + 1; + extent.y = bottomL.y - point.y + 1; + + return isValidRect(); +} + +inline bool RectI::pointInRect(const Point2I &pt) const +{ + return (pt.x >= point.x && pt.x < point.x + extent.x && pt.y >= point.y && pt.y < point.y + extent.y); +} + +inline bool RectI::contains(const RectI& R) const +{ + if (point.x <= R.point.x && point.y <= R.point.y) + if (R.point.x + R.extent.x <= point.x + extent.x) + if (R.point.y + R.extent.y <= point.y + extent.y) + return true; + return false; +} + +inline bool RectI::overlaps(RectI R) const +{ + return R.intersect (* this); +} + +inline void RectI::inset(S32 x, S32 y) +{ + point.x += x; + point.y += y; + extent.x -= 2 * x; + extent.y -= 2 * y; +} + +inline void RectF::inset(F32 x, F32 y) +{ + point.x += x; + point.y += y; + extent.x -= 2.0f * x; + extent.y -= 2.0f * y; +} + +inline void RectD::inset(F64 x, F64 y) +{ + point.x += x; + point.y += y; + extent.x -= 2.0 * x; + extent.y -= 2.0 * y; +} + + +inline void RectI::unionRects(const RectI& u) +{ + S32 minx = point.x < u.point.x ? point.x : u.point.x; + S32 miny = point.y < u.point.y ? point.y : u.point.y; + S32 maxx = (point.x + extent.x) > (u.point.x + u.extent.x) ? (point.x + extent.x) : (u.point.x + u.extent.x); + S32 maxy = (point.y + extent.y) > (u.point.y + u.extent.y) ? (point.y + extent.y) : (u.point.y + u.extent.y); + + point.x = minx; + point.y = miny; + extent.x = maxx - minx; + extent.y = maxy - miny; +} + +inline S32 +RectI::len_x() const +{ + return extent.x; +} + +inline S32 +RectI::len_y() const +{ + return extent.y; +} + +inline bool +RectI::operator==(const RectI& in_rCompare) const +{ + return (point == in_rCompare.point) && (extent == in_rCompare.extent); +} + +inline bool +RectI::operator!=(const RectI& in_rCompare) const +{ + return (operator==(in_rCompare) == false); +} + +//------------------------------------------------------------------------------ +//-------------------------------------- INLINES (RectF) +// +inline +RectF::RectF(const Point2F& in_rMin, + const Point2F& in_rExtent) + : point(in_rMin), + extent(in_rExtent) +{ + // +} + +inline +RectF::RectF(const F32 in_left, const F32 in_top, + const F32 in_width, const F32 in_height) + : point(in_left, in_top), + extent(in_width, in_height) +{ + // +} + +inline F32 +RectF::len_x() const +{ + return extent.x; +} + +inline F32 +RectF::len_y() const +{ + return extent.y; +} + +inline void RectF::set(const Point2F& in_rMin, const Point2F& in_rExtent) +{ + point = in_rMin; + extent = in_rExtent; +} + +inline void RectF::set(const F32 in_left, const F32 in_top, + const F32 in_width, const F32 in_height) +{ + point.set(in_left, in_top); + extent.set(in_width, in_height); +} + +inline F32 RectF::getDistanceToPoint( const Point2F &refPt ) const +{ + return mSqrt( getSqDistanceToPoint( refPt ) ); +} + +inline F32 RectF::getSqDistanceToPoint( const Point2F &refPt ) const +{ + const Point2F maxPoint( point + extent ); + + F32 sqDist = 0.0f; + + for ( U32 i=0; i < 2; i++ ) + { + const F32 v = refPt[i]; + if ( v < point[i] ) + sqDist += mSquared( point[i] - v ); + else if ( v > maxPoint[i] ) + sqDist += mSquared( v - maxPoint[i] ); + } + + return sqDist; +} + +inline bool RectF::intersect(const RectF& clipRect) +{ + Point2F bottomL; + bottomL.x = getMin(point.x + extent.x, clipRect.point.x + clipRect.extent.x); + bottomL.y = getMin(point.y + extent.y, clipRect.point.y + clipRect.extent.y); + + point.x = getMax(point.x, clipRect.point.x); + point.y = getMax(point.y, clipRect.point.y); + + extent.x = bottomL.x - point.x; + extent.y = bottomL.y - point.y; + + return isValidRect(); +} + +inline bool RectF::pointInRect(const Point2F &pt) const +{ + return (pt.x >= point.x && pt.x < point.x + extent.x && pt.y >= point.y && pt.y < point.y + extent.y); +} + +inline bool RectF::overlaps(const RectF& clipRect) const +{ + RectF test = *this; + return test.intersect(clipRect); +} + +inline bool lineToLineIntersect(const Point2F & a1, const Point2F & a2, const Point2F & b1, const Point2F & b2) +{ + const F32 ua_t = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x); + const F32 ub_t = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x); + const F32 u_b = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y); + + if(u_b != 0) + { + const F32 ua = ua_t / u_b; + const F32 ub = ub_t / u_b; + + return ( 0.0f <= ua && ua <= 1.0f && 0.0f <= ub && ub <= 1.0f ); + } + else + { + return ( ua_t == 0 || ub_t == 0 ); + } +} + +inline bool RectF::intersectTriangle(const Point2F &a, const Point2F &b, const Point2F &c) +{ + const Point2F topLeft = point; + const Point2F topRight = Point2F( point.x + extent.x, point.y ); + const Point2F bottomLeft = Point2F( point.x, point.y + extent.y ); + const Point2F bottomRight = point + extent; + + // 3 point plus 12 edge tests. + + // Check each triangle point to see if it's in us. + if(contains(a) || contains(b) || contains(b)) + return true; + + // Check a-b against the rect. + if(lineToLineIntersect(topLeft, topRight, a, b)) + return true; + + if(lineToLineIntersect(topRight, bottomRight, a, b)) + return true; + + if(lineToLineIntersect(bottomRight, bottomLeft, a, b)) + return true; + + if(lineToLineIntersect(bottomLeft, topLeft, a, b)) + return true; + + // Check b-c + if(lineToLineIntersect(topLeft, topRight, b, c)) + return true; + + if(lineToLineIntersect(topRight, bottomRight, b, c)) + return true; + + if(lineToLineIntersect(bottomRight, bottomLeft, b, c)) + return true; + + if(lineToLineIntersect(bottomLeft, topLeft, b, c)) + return true; + + // Check c-a + if(lineToLineIntersect(topLeft, topRight, c, a)) + return true; + + if(lineToLineIntersect(topRight, bottomRight, c, a)) + return true; + + if(lineToLineIntersect(bottomRight, bottomLeft, c, a)) + return true; + + if(lineToLineIntersect(bottomLeft, topLeft, c, a)) + return true; + + return false; +} + +inline bool RectF::contains(const RectF& R) const +{ + if (point.x <= R.point.x && point.y <= R.point.y) + if (R.point.x + R.extent.x <= point.x + extent.x) + if (R.point.y + R.extent.y <= point.y + extent.y) + return true; + return false; +} + +inline void RectF::unionRects( const RectF &r ) +{ + F32 minx = point.x < r.point.x ? point.x : r.point.x; + F32 miny = point.y < r.point.y ? point.y : r.point.y; + F32 maxx = (point.x + extent.x) > (r.point.x + r.extent.x) ? (point.x + extent.x) : (r.point.x + r.extent.x); + F32 maxy = (point.y + extent.y) > (r.point.y + r.extent.y) ? (point.y + extent.y) : (r.point.y + r.extent.y); + + point.x = minx; + point.y = miny; + extent.x = maxx - minx; + extent.y = maxy - miny; +} + +//------------------------------------------------------------------------------ +//-------------------------------------- INLINES (RectD) +// +inline +RectD::RectD(const Point2D& in_rMin, + const Point2D& in_rExtent) + : point(in_rMin), + extent(in_rExtent) +{ + // +} + +inline +RectD::RectD(const F64 in_left, const F64 in_top, + const F64 in_width, const F64 in_height) + : point(in_left, in_top), + extent(in_width, in_height) +{ + // +} + +inline F64 +RectD::len_x() const +{ + return extent.x; +} + +inline F64 +RectD::len_y() const +{ + return extent.y; +} + +inline bool RectD::intersect(const RectD& clipRect) +{ + Point2D bottomL; + bottomL.x = getMin(point.x + extent.x, clipRect.point.x + clipRect.extent.x); + bottomL.y = getMin(point.y + extent.y, clipRect.point.y + clipRect.extent.y); + + point.x = getMax(point.x, clipRect.point.x); + point.y = getMax(point.y, clipRect.point.y); + + extent.x = bottomL.x - point.x; + extent.y = bottomL.y - point.y; + + return isValidRect(); +} + +#endif //_RECT_H_ diff --git a/Engine/source/math/mSilhouetteExtractor.h b/Engine/source/math/mSilhouetteExtractor.h new file mode 100644 index 000000000..a166000e0 --- /dev/null +++ b/Engine/source/math/mSilhouetteExtractor.h @@ -0,0 +1,384 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MSILHOUETTEEXTRACTOR_H_ +#define _MSILHOUETTEEXTRACTOR_H_ + +#ifndef _FRAMEALLOCATOR_H_ +#include "core/frameAllocator.h" +#endif + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + + +/// @file +/// Routines for extracting silhouette polygons from polyhedrons. + + + +template< typename Polyhedron > +struct SilhouetteExtractorBase +{ + typedef Polyhedron PolyhedronType; + + protected: + + /// The polyhedron from which we are extracting silhouettes. + const PolyhedronType* mPolyhedron; + + SilhouetteExtractorBase( const PolyhedronType& polyhedron ) + : mPolyhedron( &polyhedron ) {} +}; + + + +/// Silhouette extraction routines for perspective projections. +template< typename Polyhedron > +struct SilhouetteExtractorBasePerspective : public SilhouetteExtractorBase< Polyhedron > +{ + private: + + enum Orientation + { + FrontFacing, + BackFacing + }; + + /// @name Per-Extraction Data + /// @{ + + /// The facing direction of each of the polygons. + mutable Orientation* mPolygonOrientations; + + /// Frame allocator water mark to release temporary memory after silhouette extraction. + mutable U32 mWaterMark; + + /// @} + + public: + + SilhouetteExtractorBasePerspective( const Polyhedron& polyhedron ) + : SilhouetteExtractorBase< Polyhedron >( polyhedron ), + mWaterMark( 0 ), + mPolygonOrientations( NULL ) {} + + /// Initialize extraction. + /// + /// @param objectView View->object matrix. + bool begin( const MatrixF& camView ) const + { + mWaterMark = FrameAllocator::getWaterMark(); + + // Determine orientation of each of the polygons. + + const U32 numPolygons = mPolyhedron->getNumPlanes(); + mPolygonOrientations = ( Orientation* ) FrameAllocator::alloc( sizeof( Orientation ) * numPolygons ); + + Point3F camPos = camView.getPosition(); + + for( U32 i = 0; i < numPolygons; ++ i ) + { + if (mPolyhedron->getPlanes()[i].whichSide( camPos ) == PlaneF::Front) + mPolygonOrientations[i] = FrontFacing; + else + mPolygonOrientations[i] = BackFacing; + } + + return true; + } + + /// End extraction. + void end() const + { + FrameAllocator::setWaterMark( mWaterMark ); + + mWaterMark = 0; + mPolygonOrientations = NULL; + } + + /// Return true if the given edge is a silhouette edge with respect to the + /// current perspective transform. + /// + /// @param edgeIndex Index of edge to test. + /// @return True if the given edge is a silhouette when looked at from the given view position. + /// + /// @note This method depends on inward-facing normals! + bool isSilhouetteEdge( U32 edgeIndex ) const + { + AssertFatal( edgeIndex < this->mPolyhedron->getNumEdges(), "SilhouetteExtractorBasePerspective::isSilhouetteEdge - Index out of range!" ); + + const typename Polyhedron::EdgeType& edge = this->mPolyhedron->getEdges()[ edgeIndex ]; + + const U32 face0 = edge.face[ 0 ]; + const U32 face1 = edge.face[ 1 ]; + + return ( mPolygonOrientations[ face0 ] != mPolygonOrientations[ face1 ] ); + } +}; + + +/// Silhouette extraction routines for orthographic projections. +template< typename Polyhedron > +struct SilhouetteExtractorBaseOrtho : public SilhouetteExtractorBase< Polyhedron > +{ + private: + + /// @name Per-Extraction Data + /// @{ + + /// Precomputed dot products between view direction and plane normals + /// in the polyhedron. + mutable F32* mFaceDotProducts; + + /// Frame allocator water mark. + mutable U32 mWaterMark; + + /// @} + + public: + + SilhouetteExtractorBaseOrtho( const Polyhedron& polyhedron ) + : SilhouetteExtractorBase< Polyhedron >( polyhedron ), + mFaceDotProducts( NULL ), + mWaterMark( 0 ) + { + } + + /// Initialize the extractor. + void begin( const Point3F& viewDirOS ) const + { + const typename Polyhedron::PlaneType* planes = this->mPolyhedron->getPlanes(); + const U32 numPlanes = this->mPolyhedron->getNumPlanes(); + + mWaterMark = FrameAllocator::getWaterMark(); + mFaceDotProducts = ( F32* ) FrameAllocator::alloc( sizeof( F32 ) * numPlanes ); + + for( U32 i = 0; i < numPlanes; ++ i ) + mFaceDotProducts[ i ] = mDot( planes[ i ], viewDirOS ); + } + + /// Finish extraction. + void end() const + { + FrameAllocator::setWaterMark( mWaterMark ); + + mFaceDotProducts = NULL; + mWaterMark = 0; + } + + /// Return true if the given edge is a silhouette edge with respect to the + /// view direction. + /// + /// @param edgeIndex Index of edge to test. + /// @return True if the given edge is a silhouette in the projection along the view direction. + /// + /// @note This method depends on inward-facing normals! + bool isSilhouetteEdge( U32 edgeIndex ) const + { + AssertFatal( edgeIndex < this->mPolyhedron->getNumEdges(), "SilhouetteExtractorBaseOrtho::isSilhouetteEdge - Index out of range!" ); + + const typename Polyhedron::EdgeType& edge = this->mPolyhedron->getEdges()[ edgeIndex ]; + + const U32 face0 = edge.face[ 0 ]; + const U32 face1 = edge.face[ 1 ]; + + // Not a silhouette if both planes are facing the same way. + + if( mSign( mFaceDotProducts[ face0 ] ) == mSign( mFaceDotProducts[ face1 ] ) ) + return false; + + // Find out which face is the front facing one. Since we expect normals + // to be pointing inwards, this means a reversal of the normal back facing + // test and we're looking for a normal facing the *same* way as our projection. + + const U32 frontFace = mFaceDotProducts[ face0 ] > 0.f ? face0 : face1; + if( mFaceDotProducts[ frontFace ] <= 0.f ) + return false; // This face or other face is perpendicular to us. + + return true; + } +}; + + +/// Common implementation parts for silhouette extraction. +template< typename Base > +struct SilhouetteExtractorImpl : public Base +{ + typedef typename Base::PolyhedronType PolyhedronType; + + SilhouetteExtractorImpl( const PolyhedronType& polyhedron ) + : Base( polyhedron ) {} + + U32 extractSilhouette( U32* outIndices, U32 maxOutIndices ) const + { + // First, find the silhouette edges. We do this with a brute-force + // approach here. This can be optimized (see "Silhouette Algorithms" by Bruce Gooch, Mark + // Hartner, and Nathan Beddes). + + U32 numSilhouetteEdges = 0; + const U32 numTotalEdges = this->mPolyhedron->getNumEdges(); + const typename PolyhedronType::EdgeType* edges = this->mPolyhedron->getEdges(); + FrameTemp< const typename PolyhedronType::EdgeType* > silhouetteEdges( numTotalEdges ); + + for( U32 i = 0; i < numTotalEdges; ++ i ) + if( this->isSilhouetteEdge( i ) ) + silhouetteEdges[ numSilhouetteEdges ++ ] = &edges[ i ]; + + // Allow this to happen rather than asserting as projection-based silhouettes + // may fail. + if( numSilhouetteEdges < 3 ) + return 0; + + // Now walk the edge list and find the edges that are connected + // with each other. From this information, emit the silhouette + // polygon. + + U32 idx = 0; + + if( idx >= maxOutIndices ) + return 0; + outIndices[ idx ++ ] = silhouetteEdges[ 0 ]->vertex[ 1 ]; + + U32 currentIndex = silhouetteEdges[ 0 ]->vertex[ 1 ]; + U32 currentEdge = 0; + + for( U32 i = 1; i < numSilhouetteEdges; ++ i ) + { + // Find edge that continues on from the current vertex. + for( U32 n = 0; n < numSilhouetteEdges; ++ n ) + { + // Skip current edge. + if( n == currentEdge ) + continue; + + if( silhouetteEdges[ n ]->vertex[ 0 ] == currentIndex ) + currentIndex = silhouetteEdges[ n ]->vertex[ 1 ]; + else if( silhouetteEdges[ n ]->vertex[ 1 ] == currentIndex ) + currentIndex = silhouetteEdges[ n ]->vertex[ 0 ]; + else + continue; + + if( idx >= maxOutIndices ) + return 0; + + currentEdge = n; + outIndices[ idx ++ ] = currentIndex; + break; + } + } + + return idx; + } +}; + + +/// Silhouette edge extraction for orthographic projections. +template< typename Polyhedron > +struct SilhouetteExtractorOrtho +{ + protected: + + typedef SilhouetteExtractorImpl< SilhouetteExtractorBaseOrtho< Polyhedron > > ExtractorType; + + /// The actual extractor implementation. + ExtractorType mExtractor; + + public: + + SilhouetteExtractorOrtho( const Polyhedron& polyhedron ) + : mExtractor( polyhedron ) {} + + /// Generate a silhouette polygon for the polyhedron based on the given view direction. + /// + /// @param viewDirOS Object-space normalized view vector. + /// @param outIndices Array where the resulting vertex indices will be stored. Must have + /// enough room. If you don't know the exact size that you need, just allocate one index + /// for any point in the mesh. + /// @param maxOutIndices The number of indices that can be stored in @a outIndices. If insufficient, + /// the return value will be 0. + /// + /// @return Number of indices written to @a outIndices or 0 if the silhouette extraction failed. + /// + /// @note Be aware that silhouette polygons are in most cases non-planar! + /// @note The direction of the ordering of the resulting indices is undefined meaning that + /// different silhouettes extracted from the same polyhedron may have different CCW/CW ordering. + /// The only guarantee is that the resulting indices are consecutive. + U32 extractSilhouette( const Point3F& viewDirOS, U32* outIndices, U32 maxOutIndices ) const + { + U32 result = 0; + + mExtractor.begin( viewDirOS ); + result = mExtractor.extractSilhouette( outIndices, maxOutIndices ); + mExtractor.end(); + + return result; + } +}; + + +/// Silhouette edge extraction for perspective projections. +template< typename Polyhedron > +struct SilhouetteExtractorPerspective +{ + protected: + + typedef SilhouetteExtractorImpl< SilhouetteExtractorBasePerspective< Polyhedron > > ExtractorType; + + /// The actual extractor implementation. + ExtractorType mExtractor; + + public: + + SilhouetteExtractorPerspective( const Polyhedron& polyhedron ) + : mExtractor( polyhedron ) {} + + /// Generate a silhouette polygon for this polyhedron based on the transforms. + /// + /// @param camView View->object matrix. + /// @param outIndices Array where the resulting vertex indices will be stored. Must have + /// enough room. If you don't know the exact size that you need, just allocate one index + /// for any point in the mesh. + /// @param maxOutIndices The number of indices that can be stored in @a outIndices. If insufficient, + /// the return value will be 0. + /// + /// @return Number of indices written to @a outIndices. + /// + /// @note Be aware that silhouette polygons are in most cases non-planar! + /// @note The direction of the ordering of the resulting indices is undefined meaning that + /// different silhouettes extracted from the same polyhedron may have different CCW/CW ordering. + /// The only guarantee is that the resulting indices are consecutive. + U32 extractSilhouette( const MatrixF& camView, U32* outIndices, U32 maxOutIndices ) const + { + U32 result = 0; + + if( mExtractor.begin( camView ) ) + result = mExtractor.extractSilhouette( outIndices, maxOutIndices ); + + mExtractor.end(); + + return result; + } +}; + +#endif // !_MSILHOUETTEEXTRACTOR_H_ diff --git a/Engine/source/math/mSolver.cpp b/Engine/source/math/mSolver.cpp new file mode 100644 index 000000000..2bef9029e --- /dev/null +++ b/Engine/source/math/mSolver.cpp @@ -0,0 +1,253 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "math/mMathFn.h" + +//-------------------------------------------------------------------------- +#define EQN_EPSILON (1e-8) + +static inline void swap(F32 & a, F32 & b) +{ + F32 t = b; + b = a; + a = t; +} + +static inline F32 mCbrt(F32 val) +{ + if(val < 0.f) + return(-mPow(-val, F32(1.f/3.f))); + else + return(mPow(val, F32(1.f/3.f))); +} + +static inline U32 mSolveLinear(F32 a, F32 b, F32 * x) +{ + if(mIsZero(a)) + return(0); + + x[0] = -b/a; + return(1); +} + +static U32 mSolveQuadratic_c(F32 a, F32 b, F32 c, F32 * x) +{ + // really linear? + if(mIsZero(a)) + return(mSolveLinear(b, c, x)); + + // get the descriminant: (b^2 - 4ac) + F32 desc = (b * b) - (4.f * a * c); + + // solutions: + // desc < 0: two imaginary solutions + // desc > 0: two real solutions (b +- sqrt(desc)) / 2a + // desc = 0: one real solution (b / 2a) + if(mIsZero(desc)) + { + x[0] = b / (2.f * a); + return(1); + } + else if(desc > 0.f) + { + F32 sqrdesc = mSqrt(desc); + F32 den = (2.f * a); + x[0] = (-b + sqrdesc) / den; + x[1] = (-b - sqrdesc) / den; + + if(x[1] < x[0]) + swap(x[0], x[1]); + + return(2); + } + else + return(0); +} + +//-------------------------------------------------------------------------- +// from Graphics Gems I: pp 738-742 +U32 mSolveCubic_c(F32 a, F32 b, F32 c, F32 d, F32 * x) +{ + if(mIsZero(a)) + return(mSolveQuadratic(b, c, d, x)); + + // normal form: x^3 + Ax^2 + BX + C = 0 + F32 A = b / a; + F32 B = c / a; + F32 C = d / a; + + // substitute x = y - A/3 to eliminate quadric term and depress + // the cubic equation to (x^3 + px + q = 0) + F32 A2 = A * A; + F32 A3 = A2 * A; + + F32 p = (1.f/3.f) * (((-1.f/3.f) * A2) + B); + F32 q = (1.f/2.f) * (((2.f/27.f) * A3) - ((1.f/3.f) * A * B) + C); + + // use Cardano's fomula to solve the depressed cubic + F32 p3 = p * p * p; + F32 q2 = q * q; + + F32 D = q2 + p3; + + U32 num = 0; + + if(mIsZero(D)) // 1 or 2 solutions + { + if(mIsZero(q)) // 1 triple solution + { + x[0] = 0.f; + num = 1; + } + else // 1 single and 1 double + { + F32 u = mCbrt(-q); + x[0] = 2.f * u; + x[1] = -u; + num = 2; + } + } + else if(D < 0.f) // 3 solutions: casus irreducibilis + { + F32 phi = (1.f/3.f) * mAcos(-q / mSqrt(-p3)); + F32 t = 2.f * mSqrt(-p); + + x[0] = t * mCos(phi); + x[1] = -t * mCos(phi + (M_PI / 3.f)); + x[2] = -t * mCos(phi - (M_PI / 3.f)); + num = 3; + } + else // 1 solution + { + F32 sqrtD = mSqrt(D); + F32 u = mCbrt(sqrtD - q); + F32 v = -mCbrt(sqrtD + q); + + x[0] = u + v; + num = 1; + } + + // resubstitute + F32 sub = (1.f/3.f) * A; + for(U32 i = 0; i < num; i++) + x[i] -= sub; + + // sort the roots + for(S32 j = 0; j < (num - 1); j++) + for(S32 k = j + 1; k < num; k++) + if(x[k] < x[j]) + swap(x[k], x[j]); + + return(num); +} + +//-------------------------------------------------------------------------- +// from Graphics Gems I: pp 738-742 +U32 mSolveQuartic_c(F32 a, F32 b, F32 c, F32 d, F32 e, F32 * x) +{ + if(mIsZero(a)) + return(mSolveCubic(b, c, d, e, x)); + + // normal form: x^4 + ax^3 + bx^2 + cx + d = 0 + F32 A = b / a; + F32 B = c / a; + F32 C = d / a; + F32 D = e / a; + + // substitue x = y - A/4 to eliminate cubic term: + // x^4 + px^2 + qx + r = 0 + F32 A2 = A * A; + F32 A3 = A2 * A; + F32 A4 = A2 * A2; + + F32 p = ((-3.f/8.f) * A2) + B; + F32 q = ((1.f/8.f) * A3) - ((1.f/2.f) * A * B) + C; + F32 r = ((-3.f/256.f) * A4) + ((1.f/16.f) * A2 * B) - ((1.f/4.f) * A * C) + D; + + U32 num = 0; + if(mIsZero(r)) // no absolute term: y(y^3 + py + q) = 0 + { + num = mSolveCubic(1.f, 0.f, p, q, x); + x[num++] = 0.f; + } + else + { + // solve the resolvent cubic + F32 q2 = q * q; + + a = 1.f; + b = (-1.f/2.f) * p; + c = -r; + d = ((1.f/2.f) * r * p) - ((1.f/8.f) * q2); + + mSolveCubic(a, b, c, d, x); + + F32 z = x[0]; + + // build 2 quadratic equations from the one solution + F32 u = (z * z) - r; + F32 v = (2.f * z) - p; + + if(mIsZero(u)) + u = 0.f; + else if(u > 0.f) + u = mSqrt(u); + else + return(0); + + if(mIsZero(v)) + v = 0.f; + else if(v > 0.f) + v = mSqrt(v); + else + return(0); + + // solve the two quadratics + a = 1.f; + b = v; + c = z - u; + num = mSolveQuadratic(a, b, c, x); + + a = 1.f; + b = -v; + c = z + u; + num += mSolveQuadratic(a, b, c, x + num); + } + + // resubstitute + F32 sub = (1.f/4.f) * A; + for(U32 i = 0; i < num; i++) + x[i] -= sub; + + // sort the roots + for(S32 j = 0; j < (num - 1); j++) + for(S32 k = j + 1; k < num; k++) + if(x[k] < x[j]) + swap(x[k], x[j]); + + return(num); +} + +U32 (*mSolveQuadratic)( F32 a, F32 b, F32 c, F32* x ) = mSolveQuadratic_c; +U32 (*mSolveCubic)( F32 a, F32 b, F32 c, F32 d, F32* x ) = mSolveCubic_c; +U32 (*mSolveQuartic)( F32 a, F32 b, F32 c, F32 d, F32 e, F32* x ) = mSolveQuartic_c; diff --git a/Engine/source/math/mSphere.cpp b/Engine/source/math/mSphere.cpp new file mode 100644 index 000000000..c1edbd91d --- /dev/null +++ b/Engine/source/math/mSphere.cpp @@ -0,0 +1,93 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "math/mSphere.h" + +#include "math/mMatrix.h" + + +bool SphereF::intersectsRay( const Point3F &start, const Point3F &end ) const +{ + MatrixF worldToObj( true ); + worldToObj.setPosition( center ); + worldToObj.inverse(); + + VectorF dir = end - start; + dir.normalize(); + + Point3F tmpStart = start; + worldToObj.mulP( tmpStart ); + + //Compute A, B and C coefficients + F32 a = mDot(dir, dir); + F32 b = 2 * mDot(dir, tmpStart); + F32 c = mDot(tmpStart, tmpStart) - (radius * radius); + + //Find discriminant + F32 disc = b * b - 4 * a * c; + + // if discriminant is negative there are no real roots, so return + // false as ray misses sphere + if ( disc < 0 ) + return false; + + // compute q as described above + F32 distSqrt = mSqrt( disc ); + F32 q; + if ( b < 0 ) + q = (-b - distSqrt)/2.0; + else + q = (-b + distSqrt)/2.0; + + // compute t0 and t1 + F32 t0 = q / a; + F32 t1 = c / q; + + // make sure t0 is smaller than t1 + if ( t0 > t1 ) + { + // if t0 is bigger than t1 swap them around + F32 temp = t0; + t0 = t1; + t1 = temp; + } + + // This function doesn't use it + // but t would be the interpolant + // value for getting the exact + // intersection point, by interpolating + // start to end by t. + F32 t = 0; + TORQUE_UNUSED(t); + + // if t1 is less than zero, the object is in the ray's negative direction + // and consequently the ray misses the sphere + if ( t1 < 0 ) + return false; + + // if t0 is less than zero, the intersection point is at t1 + if ( t0 < 0 ) // t = t1; + return true; + else // else the intersection point is at t0 + return true; // t = t0; +} diff --git a/Engine/source/math/mSphere.h b/Engine/source/math/mSphere.h new file mode 100644 index 000000000..7990f8f21 --- /dev/null +++ b/Engine/source/math/mSphere.h @@ -0,0 +1,92 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MSPHERE_H_ +#define _MSPHERE_H_ + +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif + + +class SphereF +{ +public: + Point3F center; + F32 radius; + +public: + SphereF() { } + SphereF( const Point3F& in_rPosition, const F32 in_rRadius ) + : center(in_rPosition), + radius(in_rRadius) + { + if ( radius < 0.0f ) + radius = 0.0f; + } + + bool isContained( const Point3F& in_rContain ) const; + bool isContained( const SphereF& in_rContain ) const; + bool isIntersecting( const SphereF& in_rIntersect ) const; + bool intersectsRay( const Point3F &start, const Point3F &end ) const; + + F32 distanceTo( const Point3F &pt ) const; + F32 squareDistanceTo( const Point3F &pt ) const; +}; + +//-------------------------------------- INLINES +// +inline bool SphereF::isContained( const Point3F& in_rContain ) const +{ + F32 distSq = (center - in_rContain).lenSquared(); + + return (distSq <= (radius * radius)); +} + +inline bool SphereF::isContained( const SphereF& in_rContain ) const +{ + if (radius < in_rContain.radius) + return false; + + // Since our radius is guaranteed to be >= other's, we + // can dodge the sqrt() here. + // + F32 dist = (in_rContain.center - center).lenSquared(); + + return (dist <= ((radius - in_rContain.radius) * + (radius - in_rContain.radius))); +} + +inline bool SphereF::isIntersecting( const SphereF& in_rIntersect ) const +{ + F32 distSq = (in_rIntersect.center - center).lenSquared(); + + return (distSq <= ((in_rIntersect.radius + radius) * + (in_rIntersect.radius + radius))); +} + +inline F32 SphereF::distanceTo( const Point3F &toPt ) const +{ + return (center - toPt).len() - radius; +} + +#endif //_SPHERE_H_ diff --git a/Engine/source/math/mSplinePatch.cpp b/Engine/source/math/mSplinePatch.cpp new file mode 100644 index 000000000..467feb9df --- /dev/null +++ b/Engine/source/math/mSplinePatch.cpp @@ -0,0 +1,111 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "math/mSplinePatch.h" + + +//****************************************************************************** +// Spline control points +//****************************************************************************** +SplCtrlPts::SplCtrlPts() +{ +} + +//------------------------------------------------------------------------------ +// Destructor +//------------------------------------------------------------------------------ +SplCtrlPts::~SplCtrlPts() +{ +} + +//------------------------------------------------------------------------------ +// Get point +//------------------------------------------------------------------------------ +const Point3F * SplCtrlPts::getPoint( U32 pointNum ) +{ + return &mPoints[pointNum]; +} + +//------------------------------------------------------------------------------ +// Set point +//------------------------------------------------------------------------------ +void SplCtrlPts::setPoint( Point3F &point, U32 pointNum ) +{ + mPoints[pointNum] = point; +} + +//------------------------------------------------------------------------------ +// Add point +//------------------------------------------------------------------------------ +void SplCtrlPts::addPoint( Point3F &point ) +{ + mPoints.push_back( point ); +} + +//------------------------------------------------------------------------------ +// Submit control points +//------------------------------------------------------------------------------ +void SplCtrlPts::submitPoints( Point3F *pts, U32 num ) +{ + mPoints.clear(); + + for( int i=0; i mPoints; + +public: + + SplCtrlPts(); + virtual ~SplCtrlPts(); + + /// Gets the number of points in the spline + U32 getNumPoints(){ return mPoints.size(); } + /// Gets the point at the given index + /// @param pointNum index of the point in question + const Point3F * getPoint( U32 pointNum ); + /// Sets a point at the given index to the point given + /// @param point New value for the given point + /// @param pointNum index of the given point + void setPoint( Point3F &point, U32 pointNum ); + /// Adds a point to the end of the spline + /// @param point New point to be added + void addPoint( Point3F &point ); + /// Clears existing points and enters new points + /// @param pts List of points to be added + /// @param num Number of points to be added + void submitPoints( Point3F *pts, U32 num ); +}; + +//------------------------------------------------------------------------------ +// Base class for spline patches +//------------------------------------------------------------------------------ + +/// Base class for spline patches. The only child of this class is QuadPatch. +/// +/// Spline utility class for drawing nice pretty splines. In order to draw a spline, +/// you need to create a SplCtrlPts data structure, which contains all control +/// points on the spline. See SplCtrlPts for more information on how to submit +/// points to the spline utility. Next, submit the SplCtrlPts structure to the +/// spline utility. +/// @code +/// SplinePatch patch; +/// patch.submitControlPoints(ctrlPts); +/// @endcode +/// Next, use the SplineUtil namespace to draw your spline. +/// @code +/// SplineUtil::drawSplineBeam(camPos, numSegments, width, patch[, uvOffset, numTexRep]); +/// @endcode +/// +/// You can also create a SplineBeamInfo structure (SplineUtil::SplineBeamInfo) +/// and just pass the SplineBeamInfo structure to the SplineUtil::drawSplineBeam function. +/// @see SplCtrlPts +/// @see SplineUtil +class SplinePatch +{ +private: + U32 mNumReqControlPoints; + SplCtrlPts mControlPoints; + +protected: + void setNumReqControlPoints( U32 numPts ){ mNumReqControlPoints = numPts; } + +public: + + SplinePatch(); + + U32 getNumReqControlPoints(){ return mNumReqControlPoints; } + const SplCtrlPts * getControlPoints(){ return &mControlPoints; } + const Point3F * getControlPoint( U32 index ){ return mControlPoints.getPoint( index ); } + + // virtuals + virtual void setControlPoint( Point3F &point, int index ); + /// If you have a preconstructed "SplCtrlPts" class, submit it with this function. + /// @see SplCtrlPts + virtual void submitControlPoints( SplCtrlPts &points ){ mControlPoints = points; } + /// Recalc function. Do not call this ever - only SplineUtil needs this. + /// @see SplineUtil + virtual void calc( F32 t, Point3F &result) = 0; + /// Recalc function. Do not call this ever - only SplineUtil needs this. + /// @see SplineUtil + virtual void calc( Point3F *points, F32 t, Point3F &result ) = 0; + +}; + + +#endif diff --git a/Engine/source/math/mTransform.h b/Engine/source/math/mTransform.h new file mode 100644 index 000000000..7a2716a1c --- /dev/null +++ b/Engine/source/math/mTransform.h @@ -0,0 +1,97 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MTRANSFORM_H_ +#define _MTRANSFORM_H_ + +#ifndef _MPOINT3_H_ + #include "math/mPoint3.h" +#endif +#ifndef _MANGAXIS_H_ + #include "math/mAngAxis.h" +#endif +#ifndef _MMATRIX_H_ + #include "math/mMatrix.h" +#endif + +/// A transform expressed as a combination of a position vector and an angular +/// orientation. +class TransformF +{ + public: + + Point3F mPosition; + AngAxisF mOrientation; + bool mHasRotation; + + static const TransformF Identity; + + TransformF() + : mPosition( Point3F::Zero ), + mOrientation( Point3F( 0, 0, 1 ), 0 ), + mHasRotation(true) + { + } + + TransformF( const Point3F& position, const AngAxisF& orientation ) + { + set( position, orientation ); + mHasRotation = true; + } + + TransformF( const MatrixF& mat ) + { + set( mat ); + mHasRotation = true; + } + + bool hasRotation() const { return mHasRotation; } + + void set( const Point3F& position, const AngAxisF& orientation ) + { + mPosition = position; + mOrientation = orientation; + } + + void set( const MatrixF& mat ) + { + mPosition = mat.getPosition(); + mOrientation.set( mat ); + } + + /// Return the position vector of the transform. + const Point3F& getPosition() const { return mPosition; } + + /// REturn the orientation of the transform. + const AngAxisF& getOrientation() const { return mOrientation; } + + MatrixF getMatrix() const + { + MatrixF mat; + mOrientation.setMatrix( &mat ); + mat.setPosition( mPosition ); + + return mat; + } +}; + +#endif // !_MTRANSFORM_H_ diff --git a/Engine/source/math/mathIO.h b/Engine/source/math/mathIO.h new file mode 100644 index 000000000..ee2f0dcad --- /dev/null +++ b/Engine/source/math/mathIO.h @@ -0,0 +1,267 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MATHIO_H_ +#define _MATHIO_H_ + +//Includes +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#ifndef _STREAM_H_ +#include "core/stream/stream.h" +#endif +#ifndef _MMATH_H_ +#include "math/mMath.h" +#endif + + +//------------------------------------------------------------------------------ +//-------------------------------------- READING +// +inline bool mathRead(Stream& stream, Point2I* p) +{ + bool success = stream.read(&p->x); + success &= stream.read(&p->y); + return success; +} + +inline bool mathRead(Stream& stream, Point3I* p) +{ + bool success = stream.read(&p->x); + success &= stream.read(&p->y); + success &= stream.read(&p->z); + return success; +} + +inline bool mathRead(Stream& stream, Point2F* p) +{ + bool success = stream.read(&p->x); + success &= stream.read(&p->y); + return success; +} + +inline bool mathRead(Stream& stream, Point3F* p) +{ + bool success = stream.read(&p->x); + success &= stream.read(&p->y); + success &= stream.read(&p->z); + return success; +} + +inline bool mathRead(Stream& stream, Point4F* p) +{ + bool success = stream.read(&p->x); + success &= stream.read(&p->y); + success &= stream.read(&p->z); + success &= stream.read(&p->w); + return success; +} + +inline bool mathRead(Stream& stream, Point3D* p) +{ + bool success = stream.read(&p->x); + success &= stream.read(&p->y); + success &= stream.read(&p->z); + return success; +} + +inline bool mathRead(Stream& stream, PlaneF* p) +{ + bool success = stream.read(&p->x); + success &= stream.read(&p->y); + success &= stream.read(&p->z); + success &= stream.read(&p->d); + return success; +} + +inline bool mathRead(Stream& stream, Box3F* b) +{ + bool success = mathRead(stream, &b->minExtents); + success &= mathRead(stream, &b->maxExtents); + return success; +} + +inline bool mathRead(Stream& stream, SphereF* s) +{ + bool success = mathRead(stream, &s->center); + success &= stream.read(&s->radius); + return success; +} + +inline bool mathRead(Stream& stream, RectI* r) +{ + bool success = mathRead(stream, &r->point); + success &= mathRead(stream, &r->extent); + return success; +} + +inline bool mathRead(Stream& stream, RectF* r) +{ + bool success = mathRead(stream, &r->point); + success &= mathRead(stream, &r->extent); + return success; +} + +inline bool mathRead(Stream& stream, MatrixF* m) +{ + bool success = true; + F32* pm = *m; + for (U32 i = 0; i < 16; i++) + success &= stream.read(&pm[i]); + return success; +} + +inline bool mathRead(Stream& stream, QuatF* q) +{ + bool success = stream.read(&q->x); + success &= stream.read(&q->y); + success &= stream.read(&q->z); + success &= stream.read(&q->w); + return success; +} + +inline bool mathRead(Stream& stream, EaseF* e) +{ + bool success = stream.read( &e->dir ); + success &= stream.read( &e->type ); + success &= stream.read( &e->param[ 0 ] ); + success &= stream.read( &e->param[ 1 ] ); + return success; +} + +//------------------------------------------------------------------------------ +//-------------------------------------- WRITING +// +inline bool mathWrite(Stream& stream, const Point2I& p) +{ + bool success = stream.write(p.x); + success &= stream.write(p.y); + return success; +} + +inline bool mathWrite(Stream& stream, const Point3I& p) +{ + bool success = stream.write(p.x); + success &= stream.write(p.y); + success &= stream.write(p.z); + return success; +} + +inline bool mathWrite(Stream& stream, const Point2F& p) +{ + bool success = stream.write(p.x); + success &= stream.write(p.y); + return success; +} + +inline bool mathWrite(Stream& stream, const Point3F& p) +{ + bool success = stream.write(p.x); + success &= stream.write(p.y); + success &= stream.write(p.z); + return success; +} + +inline bool mathWrite(Stream& stream, const Point4F& p) +{ + bool success = stream.write(p.x); + success &= stream.write(p.y); + success &= stream.write(p.z); + success &= stream.write(p.w); + return success; +} + +inline bool mathWrite(Stream& stream, const Point3D& p) +{ + bool success = stream.write(p.x); + success &= stream.write(p.y); + success &= stream.write(p.z); + return success; +} + +inline bool mathWrite(Stream& stream, const PlaneF& p) +{ + bool success = stream.write(p.x); + success &= stream.write(p.y); + success &= stream.write(p.z); + success &= stream.write(p.d); + return success; +} + +inline bool mathWrite(Stream& stream, const Box3F& b) +{ + bool success = mathWrite(stream, b.minExtents); + success &= mathWrite(stream, b.maxExtents); + return success; +} + +inline bool mathWrite(Stream& stream, const SphereF& s) +{ + bool success = mathWrite(stream, s.center); + success &= stream.write(s.radius); + return success; +} + +inline bool mathWrite(Stream& stream, const RectI& r) +{ + bool success = mathWrite(stream, r.point); + success &= mathWrite(stream, r.extent); + return success; +} + +inline bool mathWrite(Stream& stream, const RectF& r) +{ + bool success = mathWrite(stream, r.point); + success &= mathWrite(stream, r.extent); + return success; +} + +inline bool mathWrite(Stream& stream, const MatrixF& m) +{ + bool success = true; + const F32* pm = m; + for (U32 i = 0; i < 16; i++) + success &= stream.write(pm[i]); + return success; +} + +inline bool mathWrite(Stream& stream, const QuatF& q) +{ + bool success = stream.write(q.x); + success &= stream.write(q.y); + success &= stream.write(q.z); + success &= stream.write(q.w); + return success; +} + +inline bool mathWrite(Stream& stream, const EaseF& e) +{ + bool success = stream.write(e.dir); + success &= stream.write(e.type); + success &= stream.write(e.param[0]); + success &= stream.write(e.param[1]); + return success; +} + +#endif //_MATHIO_H_ + diff --git a/Engine/source/math/mathTypes.cpp b/Engine/source/math/mathTypes.cpp new file mode 100644 index 000000000..d92f293e1 --- /dev/null +++ b/Engine/source/math/mathTypes.cpp @@ -0,0 +1,989 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/strings/stringFunctions.h" +#include "console/consoleTypes.h" +#include "console/console.h" +#include "console/engineAPI.h" +#include "math/mPoint2.h" +#include "math/mPoint3.h" +#include "math/mMatrix.h" +#include "math/mQuat.h" +#include "math/mRect.h" +#include "math/mBox.h" +#include "math/mAngAxis.h" +#include "math/mTransform.h" +#include "math/mathTypes.h" +#include "math/mRandom.h" +#include "math/mEase.h" +#include "math/mathUtils.h" + + +IMPLEMENT_SCOPE( MathTypes, Math,, "" ); + +IMPLEMENT_STRUCT( Point2I, + Point2I, MathTypes, + "" ) + + FIELD( x, x, 1, "X coordinate." ) + FIELD( y, y, 1, "Y coordinate." ) + +END_IMPLEMENT_STRUCT; +IMPLEMENT_STRUCT( Point2F, + Point2F, MathTypes, + "" ) + + FIELD( x, x, 1, "X coordinate." ) + FIELD( y, y, 1, "Y coordinate." ) + +END_IMPLEMENT_STRUCT; +IMPLEMENT_STRUCT( Point3F, + Point3F, MathTypes, + "" ) + + FIELD( x, x, 1, "X coordinate." ) + FIELD( y, y, 1, "Y coordinate." ) + FIELD( z, z, 1, "Z coordinate." ) + +END_IMPLEMENT_STRUCT; +IMPLEMENT_STRUCT( Point4F, + Point4F, MathTypes, + "" ) + + FIELD( x, x, 1, "X coordinate." ) + FIELD( y, y, 1, "Y coordinate." ) + FIELD( z, z, 1, "Z coordinate." ) + FIELD( w, w, 1, "W coordinate." ) + +END_IMPLEMENT_STRUCT; +IMPLEMENT_STRUCT( RectI, + RectI, MathTypes, + "" ) +END_IMPLEMENT_STRUCT; +IMPLEMENT_STRUCT( RectF, + RectF, MathTypes, + "" ) +END_IMPLEMENT_STRUCT; +IMPLEMENT_STRUCT( MatrixF, + MatrixF, MathTypes, + "" ) +END_IMPLEMENT_STRUCT; +IMPLEMENT_STRUCT( AngAxisF, + AngAxisF, MathTypes, + "" ) +END_IMPLEMENT_STRUCT; +IMPLEMENT_STRUCT( TransformF, + TransformF, MathTypes, + "" ) +END_IMPLEMENT_STRUCT; +IMPLEMENT_STRUCT( Box3F, + Box3F, MathTypes, + "" ) +END_IMPLEMENT_STRUCT; +IMPLEMENT_STRUCT( EaseF, + EaseF, MathTypes, + "" ) +END_IMPLEMENT_STRUCT; + + +//----------------------------------------------------------------------------- +// TypePoint2I +//----------------------------------------------------------------------------- +ConsoleType( Point2I, TypePoint2I, Point2I ) +ImplementConsoleTypeCasters( TypePoint2I, Point2I ) + +ConsoleGetType( TypePoint2I ) +{ + Point2I *pt = (Point2I *) dptr; + char* returnBuffer = Con::getReturnBuffer(256); + dSprintf(returnBuffer, 256, "%d %d", pt->x, pt->y); + return returnBuffer; +} + +ConsoleSetType( TypePoint2I ) +{ + if(argc == 1) + dSscanf(argv[0], "%d %d", &((Point2I *) dptr)->x, &((Point2I *) dptr)->y); + else if(argc == 2) + *((Point2I *) dptr) = Point2I(dAtoi(argv[0]), dAtoi(argv[1])); + else + Con::printf("Point2I must be set as { x, y } or \"x y\""); +} + +//----------------------------------------------------------------------------- +// TypePoint2F +//----------------------------------------------------------------------------- +ConsoleType( Point2F, TypePoint2F, Point2F ) +ImplementConsoleTypeCasters( TypePoint2F, Point2F ) + +ConsoleGetType( TypePoint2F ) +{ + Point2F *pt = (Point2F *) dptr; + char* returnBuffer = Con::getReturnBuffer(256); + dSprintf(returnBuffer, 256, "%g %g", pt->x, pt->y); + return returnBuffer; +} + +ConsoleSetType( TypePoint2F ) +{ + if(argc == 1) + dSscanf(argv[0], "%g %g", &((Point2F *) dptr)->x, &((Point2F *) dptr)->y); + else if(argc == 2) + *((Point2F *) dptr) = Point2F(dAtof(argv[0]), dAtof(argv[1])); + else + Con::printf("Point2F must be set as { x, y } or \"x y\""); +} + +//----------------------------------------------------------------------------- +// TypePoint3F +//----------------------------------------------------------------------------- +ConsoleType( Point3F, TypePoint3F, Point3F ) +ImplementConsoleTypeCasters(TypePoint3F, Point3F) + +ConsoleGetType( TypePoint3F ) +{ + Point3F *pt = (Point3F *) dptr; + char* returnBuffer = Con::getReturnBuffer(256); + dSprintf(returnBuffer, 256, "%g %g %g", pt->x, pt->y, pt->z); + return returnBuffer; +} + +ConsoleSetType( TypePoint3F ) +{ + if(argc == 1) + dSscanf(argv[0], "%g %g %g", &((Point3F *) dptr)->x, &((Point3F *) dptr)->y, &((Point3F *) dptr)->z); + else if(argc == 3) + *((Point3F *) dptr) = Point3F(dAtof(argv[0]), dAtof(argv[1]), dAtof(argv[2])); + else + Con::printf("Point3F must be set as { x, y, z } or \"x y z\""); +} + +//----------------------------------------------------------------------------- +// TypePoint4F +//----------------------------------------------------------------------------- +ConsoleType( Point4F, TypePoint4F, Point4F ) +ImplementConsoleTypeCasters( TypePoint4F, Point4F ) + +ConsoleGetType( TypePoint4F ) +{ + Point4F *pt = (Point4F *) dptr; + char* returnBuffer = Con::getReturnBuffer(256); + dSprintf(returnBuffer, 256, "%g %g %g %g", pt->x, pt->y, pt->z, pt->w); + return returnBuffer; +} + +ConsoleSetType( TypePoint4F ) +{ + if(argc == 1) + dSscanf(argv[0], "%g %g %g %g", &((Point4F *) dptr)->x, &((Point4F *) dptr)->y, &((Point4F *) dptr)->z, &((Point4F *) dptr)->w); + else if(argc == 4) + *((Point4F *) dptr) = Point4F(dAtof(argv[0]), dAtof(argv[1]), dAtof(argv[2]), dAtof(argv[3])); + else + Con::printf("Point4F must be set as { x, y, z, w } or \"x y z w\""); +} + +//----------------------------------------------------------------------------- +// TypeRectI +//----------------------------------------------------------------------------- +ConsoleType( RectI, TypeRectI, RectI ) +ImplementConsoleTypeCasters( TypeRectI, RectI ) + +ConsoleGetType( TypeRectI ) +{ + RectI *rect = (RectI *) dptr; + char* returnBuffer = Con::getReturnBuffer(256); + dSprintf(returnBuffer, 256, "%d %d %d %d", rect->point.x, rect->point.y, + rect->extent.x, rect->extent.y); + return returnBuffer; +} + +ConsoleSetType( TypeRectI ) +{ + if(argc == 1) + dSscanf(argv[0], "%d %d %d %d", &((RectI *) dptr)->point.x, &((RectI *) dptr)->point.y, + &((RectI *) dptr)->extent.x, &((RectI *) dptr)->extent.y); + else if(argc == 4) + *((RectI *) dptr) = RectI(dAtoi(argv[0]), dAtoi(argv[1]), dAtoi(argv[2]), dAtoi(argv[3])); + else + Con::printf("RectI must be set as { x, y, w, h } or \"x y w h\""); +} + +//----------------------------------------------------------------------------- +// TypeRectF +//----------------------------------------------------------------------------- +ConsoleType( RectF, TypeRectF, RectF ) +ImplementConsoleTypeCasters( TypeRectF, RectF ) + +ConsoleGetType( TypeRectF ) +{ + RectF *rect = (RectF *) dptr; + char* returnBuffer = Con::getReturnBuffer(256); + dSprintf(returnBuffer, 256, "%g %g %g %g", rect->point.x, rect->point.y, + rect->extent.x, rect->extent.y); + return returnBuffer; +} + +ConsoleSetType( TypeRectF ) +{ + if(argc == 1) + dSscanf(argv[0], "%g %g %g %g", &((RectF *) dptr)->point.x, &((RectF *) dptr)->point.y, + &((RectF *) dptr)->extent.x, &((RectF *) dptr)->extent.y); + else if(argc == 4) + *((RectF *) dptr) = RectF(dAtof(argv[0]), dAtof(argv[1]), dAtof(argv[2]), dAtof(argv[3])); + else + Con::printf("RectF must be set as { x, y, w, h } or \"x y w h\""); +} + +//----------------------------------------------------------------------------- +// TypeMatrix +//----------------------------------------------------------------------------- +ConsoleType( MatrixF, TypeMatrixF, MatrixF ) +ImplementConsoleTypeCasters( TypeMatrixF, MatrixF ) + +// Oh merry confusion. Torque stores matrices in row-major order yet to TorqueScript +// matrices were passed in column-major order, so we need to stick to this here. + +ConsoleGetType( TypeMatrixF ) +{ + MatrixF* mat = ( MatrixF* ) dptr; + + Point3F col0, col1, col2; + mat->getColumn(0, &col0); + mat->getColumn(1, &col1); + mat->getColumn(2, &col2); + char* returnBuffer = Con::getReturnBuffer(256); + dSprintf(returnBuffer,256,"%g %g %g %g %g %g %g %g %g", + col0.x, col0.y, col0.z, col1.x, col1.y, col1.z, col2.x, col2.y, col2.z); + return returnBuffer; +} + +ConsoleSetType( TypeMatrixF ) +{ + if( argc != 1 ) + { + Con::errorf( "MatrixF must be set as \"c0x c0y c0z c1x c1y c1z c2x c2y c2z\"" ); + return; + } + + Point3F col0, col1, col2; + dSscanf( argv[ 0 ], "%g %g %g %g %g %g %g %g %g", + &col0.x, &col0.y, &col0.z, &col1.x, &col1.y, &col1.z, &col2.x, &col2.y, &col2.z ); + + MatrixF* mat = ( MatrixF* ) dptr; + + mat->setColumn( 0, col0 ); + mat->setColumn( 1, col1 ); + mat->setColumn( 2, col2 ); +} + +//----------------------------------------------------------------------------- +// TypeMatrixPosition +//----------------------------------------------------------------------------- +ConsoleType( MatrixPosition, TypeMatrixPosition, MatrixF ) + +ConsoleGetType( TypeMatrixPosition ) +{ + F32 *col = (F32 *) dptr + 3; + char* returnBuffer = Con::getReturnBuffer(256); + if(col[12] == 1.f) + dSprintf(returnBuffer, 256, "%g %g %g", col[0], col[4], col[8]); + else + dSprintf(returnBuffer, 256, "%g %g %g %g", col[0], col[4], col[8], col[12]); + return returnBuffer; +} + +ConsoleSetType( TypeMatrixPosition ) +{ + F32 *col = ((F32 *) dptr) + 3; + if (argc == 1) + { + col[0] = col[4] = col[8] = 0.f; + col[12] = 1.f; + dSscanf(argv[0], "%g %g %g %g", &col[0], &col[4], &col[8], &col[12]); + } + else if (argc <= 4) + { + for (S32 i = 0; i < argc; i++) + col[i << 2] = dAtof(argv[i]); + } + else + Con::printf("Matrix position must be set as { x, y, z, w } or \"x y z w\""); +} + +//----------------------------------------------------------------------------- +// TypeMatrixRotation +//----------------------------------------------------------------------------- +ConsoleType( MatrixRotation, TypeMatrixRotation, MatrixF ) + +ConsoleGetType( TypeMatrixRotation ) +{ + AngAxisF aa(*(MatrixF *) dptr); + aa.axis.normalize(); + char* returnBuffer = Con::getReturnBuffer(256); + dSprintf(returnBuffer,256,"%g %g %g %g",aa.axis.x,aa.axis.y,aa.axis.z,mRadToDeg(aa.angle)); + return returnBuffer; +} + +ConsoleSetType( TypeMatrixRotation ) +{ + // DMM: Note that this will ONLY SET the ULeft 3x3 submatrix. + // + AngAxisF aa(Point3F(0,0,0),0); + if (argc == 1) + { + dSscanf(argv[0], "%g %g %g %g", &aa.axis.x, &aa.axis.y, &aa.axis.z, &aa.angle); + aa.angle = mDegToRad(aa.angle); + } + else if (argc == 4) + { + for (S32 i = 0; i < argc; i++) + ((F32*)&aa)[i] = dAtof(argv[i]); + aa.angle = mDegToRad(aa.angle); + } + else + Con::printf("Matrix rotation must be set as { x, y, z, angle } or \"x y z angle\""); + + // + MatrixF temp; + aa.setMatrix(&temp); + + F32* pDst = *(MatrixF *)dptr; + const F32* pSrc = temp; + for (U32 i = 0; i < 3; i++) + for (U32 j = 0; j < 3; j++) + pDst[i*4 + j] = pSrc[i*4 + j]; +} + +//----------------------------------------------------------------------------- +// TypeAngAxisF +//----------------------------------------------------------------------------- +ConsoleType( AngAxisF, TypeAngAxisF, AngAxisF ) +ImplementConsoleTypeCasters( TypeAngAxisF, AngAxisF ) + +ConsoleGetType( TypeAngAxisF ) +{ + AngAxisF* aa = ( AngAxisF* ) dptr; + char* returnBuffer = Con::getReturnBuffer(256); + dSprintf(returnBuffer,256,"%g %g %g %g",aa->axis.x,aa->axis.y,aa->axis.z,mRadToDeg(aa->angle)); + return returnBuffer; +} + +ConsoleSetType( TypeAngAxisF ) +{ + // DMM: Note that this will ONLY SET the ULeft 3x3 submatrix. + // + AngAxisF* aa = ( AngAxisF* ) dptr; + if (argc == 1) + { + dSscanf(argv[0], "%g %g %g %g", &aa->axis.x, &aa->axis.y, &aa->axis.z, &aa->angle); + aa->angle = mDegToRad(aa->angle); + } + else if (argc == 4) + { + for (S32 i = 0; i < argc; i++) + ((F32*)&aa)[i] = dAtof(argv[i]); + aa->angle = mDegToRad(aa->angle); + } + else + Con::printf("AngAxisF must be set as { x, y, z, angle } or \"x y z angle\""); +} + + +//----------------------------------------------------------------------------- +// TypeTransformF +//----------------------------------------------------------------------------- + +const TransformF TransformF::Identity( Point3F::Zero, AngAxisF( Point3F( 0, 0, 1 ), 0) ); + +ConsoleType( TransformF, TypeTransformF, TransformF ) +ImplementConsoleTypeCasters( TypeTransformF, TransformF ) + +ConsoleGetType( TypeTransformF ) +{ + TransformF* aa = ( TransformF* ) dptr; + char* returnBuffer = Con::getReturnBuffer( 256 ); + dSprintf( returnBuffer, 256, "%g %g %g %g %g %g %g", + aa->mPosition.x, aa->mPosition.y, aa->mPosition.z, + aa->mOrientation.axis.x, aa->mOrientation.axis.y, aa->mOrientation.axis.z, aa->mOrientation.angle ); + return returnBuffer; +} + +ConsoleSetType( TypeTransformF ) +{ + TransformF* aa = ( TransformF* ) dptr; + if( argc == 1 ) + { + U32 count = dSscanf( argv[ 0 ], "%g %g %g %g %g %g %g", + &aa->mPosition.x, &aa->mPosition.y, &aa->mPosition.z, + &aa->mOrientation.axis.x, &aa->mOrientation.axis.y, &aa->mOrientation.axis.z, &aa->mOrientation.angle ); + + aa->mHasRotation = ( count == 7 ); + } + else if( argc == 7 ) + { + aa->mPosition.x = dAtof( argv[ 0 ] ); + aa->mPosition.y = dAtof( argv[ 1 ] ); + aa->mPosition.z = dAtof( argv[ 2 ] ); + aa->mOrientation.axis.x = dAtof( argv[ 3 ] ); + aa->mOrientation.axis.y = dAtof( argv[ 4 ] ); + aa->mOrientation.axis.z = dAtof( argv[ 5 ] ); + aa->mOrientation.angle = dAtof( argv[ 6 ] ); + } + else + Con::errorf( "TransformF must be set as { px, py, pz, x, y, z, angle } or \"px py pz x y z angle\""); +} + + + +//----------------------------------------------------------------------------- +// TypeBox3F +//----------------------------------------------------------------------------- +ConsoleType( Box3F, TypeBox3F, Box3F ) +ImplementConsoleTypeCasters( TypeBox3F, Box3F ) + +ConsoleGetType( TypeBox3F ) +{ + const Box3F* pBox = (const Box3F*)dptr; + + char* returnBuffer = Con::getReturnBuffer(256); + dSprintf(returnBuffer, 256, "%g %g %g %g %g %g", + pBox->minExtents.x, pBox->minExtents.y, pBox->minExtents.z, + pBox->maxExtents.x, pBox->maxExtents.y, pBox->maxExtents.z); + + return returnBuffer; +} + +ConsoleSetType( TypeBox3F ) +{ + Box3F* pDst = (Box3F*)dptr; + + if (argc == 1) + { + U32 args = dSscanf(argv[0], "%g %g %g %g %g %g", + &pDst->minExtents.x, &pDst->minExtents.y, &pDst->minExtents.z, + &pDst->maxExtents.x, &pDst->maxExtents.y, &pDst->maxExtents.z); + AssertWarn(args == 6, "Warning, box probably not read properly"); + } + else + { + Con::printf("Box3F must be set as \"xMin yMin zMin xMax yMax zMax\""); + } +} + + +//----------------------------------------------------------------------------- +// TypeEaseF +//----------------------------------------------------------------------------- +ConsoleType( EaseF, TypeEaseF, EaseF ) +ImplementConsoleTypeCasters( TypeEaseF, EaseF ) + +ConsoleGetType( TypeEaseF ) +{ + const EaseF* pEase = (const EaseF*)dptr; + + char* returnBuffer = Con::getReturnBuffer(256); + dSprintf(returnBuffer, 256, "%d %d %g %g", + pEase->dir, pEase->type, pEase->param[0], pEase->param[1]); + + return returnBuffer; +} + +ConsoleSetType( TypeEaseF ) +{ + EaseF* pDst = (EaseF*)dptr; + + // defaults... + pDst->param[0] = -1.0f; + pDst->param[1] = -1.0f; + if (argc == 1) { + U32 args = dSscanf(argv[0], "%d %d %f %f", // the two params are optional and assumed -1 if not present... + &pDst->dir, &pDst->type, &pDst->param[0],&pDst->param[1]); + if( args < 2 ) + Con::warnf( "Warning, EaseF probably not read properly" ); + } else { + Con::printf("EaseF must be set as \"dir type [param0 param1]\""); + } +} + + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( VectorAdd, VectorF, ( VectorF a, VectorF b ),, + "Add two vectors.\n" + "@param a The first vector.\n" + "@param b The second vector.\n" + "@return The vector @a a + @a b.\n\n" + "@tsexample\n" + "//-----------------------------------------------------------------------------\n" + "//\n" + "// VectorAdd( %a, %b );\n" + "//\n" + "// The sum of vector a, (ax, ay, az), and vector b, (bx, by, bz) is:\n" + "//\n" + "// a + b = ( ax + bx, ay + by, az + bz )\n" + "//\n" + "//-----------------------------------------------------------------------------\n" + "%a = \"1 0 0\";\n" + "%b = \"0 1 0\";\n\n" + "// %r = \"( 1 + 0, 0 + 1, 0 + 0 )\";\n" + "// %r = \"1 1 0\";\n" + "%r = VectorAdd( %a, %b );\n" + "@endtsexample\n\n" + "@ingroup Vectors") +{ + return a + b; +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( VectorSub, VectorF, ( VectorF a, VectorF b ),, + "Subtract two vectors.\n" + "@param a The first vector.\n" + "@param b The second vector.\n" + "@return The vector @a a - @a b.\n\n" + "@tsexample\n" + "//-----------------------------------------------------------------------------\n" + "//\n" + "// VectorSub( %a, %b );\n" + "//\n" + "// The difference of vector a, (ax, ay, az), and vector b, (bx, by, bz) is:\n" + "//\n" + "// a - b = ( ax - bx, ay - by, az - bz )\n" + "//\n" + "//-----------------------------------------------------------------------------\n\n" + + "%a = \"1 0 0\";\n" + "%b = \"0 1 0\";\n\n" + + "// %r = \"( 1 - 0, 0 - 1, 0 - 0 )\";\n" + "// %r = \"1 -1 0\";\n" + "%r = VectorSub( %a, %b );\n" + "@endtsexample\n\n" + "@ingroup Vectors" ) +{ + return a - b; +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( VectorScale, VectorF, ( VectorF a, F32 scalar ),, + "Scales a vector by a scalar.\n" + "@param a The vector to scale.\n" + "@param scalar The scale factor.\n" + "@return The vector @a a * @a scalar.\n\n" + "@tsexample\n" + "//-----------------------------------------------------------------------------\n" + "//\n" + "// VectorScale( %a, %v );\n" + "//\n" + "// Scaling vector a, (ax, ay, az), but the scalar, v, is:\n" + "//\n" + "// a * v = ( ax * v, ay * v, az * v )\n" + "//\n" + "//-----------------------------------------------------------------------------\n\n" + + "%a = \"1 1 0\";\n" + "%v = \"2\";\n\n" + + "// %r = \"( 1 * 2, 1 * 2, 0 * 2 )\";\n" + "// %r = \"2 2 0\";\n" + "%r = VectorScale( %a, %v );\n" + "@endtsexample\n\n" + "@ingroup Vectors" ) +{ + return a * scalar; +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( VectorNormalize, VectorF, ( VectorF v ),, + "Brings a vector into its unit form, i.e. such that it has the magnitute 1.\n" + "@param v The vector to normalize.\n" + "@return The vector @a v scaled to length 1.\n\n" + "@tsexample\n" + "//-----------------------------------------------------------------------------\n" + "//\n" + "// VectorNormalize( %a );\n" + "//\n" + "// The normalized vector a, (ax, ay, az), is:\n" + "//\n" + "// a^ = a / ||a||\n" + "// = ( ax / ||a||, ay / ||a||, az / ||a|| )\n" + "//\n" + "//-----------------------------------------------------------------------------\n\n" + + "%a = \"1 1 0\";\n" + "%l = 1.414;\n\n" + + "// %r = \"( 1 / 1.141, 1 / 1.141, 0 / 1.141 )\";\n" + "// %r = \"0.707 0.707 0\";\n" + "%r = VectorNormalize( %a );\n" + "@endtsexample\n\n" + "@ingroup Vectors" ) +{ + VectorF n( v ); + n.normalizeSafe(); + return n; +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( VectorDot, F32, ( VectorF a, VectorF b ),, + "Compute the dot product of two vectors.\n" + "@param a The first vector.\n" + "@param b The second vector.\n" + "@return The dot product @a a * @a b.\n\n" + "@tsexample\n" + "//-----------------------------------------------------------------------------\n" + "//\n" + "// VectorDot( %a, %b );\n" + "//\n" + "// The dot product between vector a, (ax, ay, az), and vector b, (bx, by, bz), is:\n" + "//\n" + "// a . b = ( ax * bx + ay * by + az * bz )\n" + "//\n" + "//-----------------------------------------------------------------------------\n\n" + + "%a = \"1 1 0\";\n" + "%b = \"2 0 1\";\n\n" + + "// %r = \"( 1 * 2 + 1 * 0 + 0 * 1 )\";\n" + "// %r = 2;\n" + "%r = VectorDot( %a, %b );\n" + "@endtsexample\n\n" + "@ingroup Vectors" ) +{ + return mDot( a, b ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( VectorCross, VectorF, ( VectorF a, VectorF b ),, + "Calculcate the cross product of two vectors.\n" + "@param a The first vector.\n" + "@param b The second vector.\n" + "@return The cross product @a x @a b.\n\n" + "@tsexample\n" + "//-----------------------------------------------------------------------------\n" + "//\n" + "// VectorCross( %a, %b );\n" + "//\n" + "// The cross product of vector a, (ax, ay, az), and vector b, (bx, by, bz), is\n" + "//\n" + "// a x b = ( ( ay * bz ) - ( az * by ), ( az * bx ) - ( ax * bz ), ( ax * by ) - ( ay * bx ) )\n" + "//\n" + "//-----------------------------------------------------------------------------\n\n" + + "%a = \"1 1 0\";\n" + "%b = \"2 0 1\";\n\n" + + "// %r = \"( ( 1 * 1 ) - ( 0 * 0 ), ( 0 * 2 ) - ( 1 * 1 ), ( 1 * 0 ) - ( 1 * 2 ) )\";\n" + "// %r = \"1 -1 -2\";\n" + "%r = VectorCross( %a, %b );\n" + "@endtsexample\n\n" + "@ingroup Vectors" ) +{ + VectorF v; + mCross( a, b, &v ); + return v; +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( VectorDist, F32, ( VectorF a, VectorF b ),, + "Compute the distance between two vectors.\n" + "@param a The first vector.\n" + "@param b The second vector.\n" + "@return The length( @a b - @a a ).\n\n" + "@tsexample\n" + "//-----------------------------------------------------------------------------\n" + "//\n" + "// VectorDist( %a, %b );\n" + "//\n" + "// The distance between vector a, (ax, ay, az), and vector b, (bx, by, bz), is\n" + "//\n" + "// a -> b = ||( b - a )||\n" + "// = ||( bx - ax, by - ay, bz - az )||\n" + "// = mSqrt( ( bx - ax ) * ( bx - ax ) + ( by - ay ) * ( by - ay ) + ( bz - az ) * ( bz - az ) )\n" + "//\n" + "//-----------------------------------------------------------------------------\n\n" + + "%a = \"1 1 0\";\n" + "%b = \"2 0 1\";\n\n" + + "// %r = mSqrt( ( 2 - 1 ) * ( 2 - 1) + ( 0 - 1 ) * ( 0 - 1 ) + ( 1 - 0 ) * ( 1 - 0 ) );\n" + "// %r = mSqrt( 3 );\n" + "%r = VectorDist( %a, %b );\n" + "@endtsexample\n\n" + "@ingroup Vectors" ) +{ + VectorF v = b - a; + return v.len(); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( VectorLen, F32, ( VectorF v ),, + "Calculate the magnitude of the given vector.\n" + "@param v A vector.\n" + "@return The length of vector @a v.\n\n" + "@tsexample\n" + "//-----------------------------------------------------------------------------\n" + "//\n" + "// VectorLen( %a );\n" + "//\n" + "// The length or magnitude of vector a, (ax, ay, az), is:\n" + "//\n" + "// ||a|| = Sqrt( ax * ax + ay * ay + az * az )\n" + "//\n" + "//-----------------------------------------------------------------------------\n\n" + + "%a = \"1 1 0\";\n\n" + + "// %r = mSqrt( 1 * 1 + 1 * 1 + 0 * 0 );\n" + "// %r = mSqrt( 2 );\n" + "// %r = 1.414;\n" + "%r = VectorLen( %a );\n" + "@endtsexample\n\n" + "@ingroup Vectors" ) +{ + return v.len(); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( VectorOrthoBasis, MatrixF, ( AngAxisF aa ),, + "Create an orthogonal basis from the given vector.\n" + "@param aaf The vector to create the orthogonal basis from.\n" + "@return A matrix representing the orthogonal basis.\n" + "@ingroup Vectors" ) +{ + MatrixF mat; + aa.setMatrix(&mat); + return mat; +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( VectorLerp, VectorF, ( VectorF a, VectorF b, F32 t ),, + "Linearly interpolate between two vectors by @a t.\n" + "@param a Vector to start interpolation from.\n" + "@param b Vector to interpolate to.\n" + "@param t Interpolation factor (0-1). At zero, @a a is returned and at one, @a b is returned. In between, an interpolated vector " + "between @a a and @a b is returned.\n" + "@return An interpolated vector between @a a and @a b.\n\n" + "@tsexample\n" + "//-----------------------------------------------------------------------------\n" + "//\n" + "// VectorLerp( %a, %b );\n" + "//\n" + "// The point between vector a, (ax, ay, az), and vector b, (bx, by, bz), which is\n" + "// weighted by the interpolation factor, t, is\n" + "//\n" + "// r = a + t * ( b - a )\n" + "// = ( ax + t * ( bx - ax ), ay + t * ( by - ay ), az + t * ( bz - az ) )\n" + "//\n" + "//-----------------------------------------------------------------------------\n\n" + + "%a = \"1 1 0\";\n" + "%b = \"2 0 1\";\n" + "%v = \"0.25\";\n\n" + + "// %r = \"( 1 + 0.25 * ( 2 - 1 ), 1 + 0.25 * ( 0 - 1 ), 0 + 0.25 * ( 1 - 0 ) )\";\n" + "// %r = \"1.25 0.75 0.25\";\n" + "%r = VectorLerp( %a, %b );\n" + "@endtsexample\n\n" + "@ingroup Vectors" ) +{ + VectorF c; + c.interpolate( a, b, t ); + + return c; +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( MatrixCreate, TransformF, ( VectorF position, AngAxisF orientation ),, + "Create a transform from the given translation and orientation.\n" + "@param position The translation vector for the transform.\n" + "@param orientation The axis and rotation that orients the transform.\n" + "@return A transform based on the given position and orientation.\n" + "@ingroup Matrices" ) +{ + TransformF transform( position, orientation ); + return transform; +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( MatrixCreateFromEuler, TransformF, ( Point3F angles ),, + "@Create a matrix from the given rotations.\n\n" + "@param Vector3F X, Y, and Z rotation in *radians*.\n" + "@return A transform based on the given orientation.\n" + "@ingroup Matrices" ) +{ + QuatF rotQ( angles ); + AngAxisF aa; + aa.set(rotQ); + + return TransformF( Point3F::Zero, aa ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( MatrixMultiply, TransformF, ( TransformF left, TransformF right ),, + "@brief Multiply the two matrices.\n\n" + "@param left First transform.\n" + "@param right Right transform.\n" + "@return Concatenation of the two transforms.\n" + "@ingroup Matrices" ) +{ + MatrixF m1 = left.getMatrix(); + MatrixF m2 = right.getMatrix(); + + m1.mul( m2 ); + + return TransformF( m1 ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( MatrixMulVector, VectorF, ( TransformF transform, VectorF vector ),, + "@brief Multiply the vector by the transform assuming that w=0.\n\n" + "This function will multiply the given vector by the given transform such that translation will " + "not affect the vector.\n\n" + "@param transform A transform.\n" + "@param vector A vector.\n" + "@return The transformed vector.\n" + "@ingroup Matrices") +{ + MatrixF m = transform.getMatrix(); + m.mulV( vector ); + return vector; +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( MatrixMulPoint, Point3F, ( TransformF transform, Point3F point ),, + "@brief Multiply the given point by the given transform assuming that w=1.\n\n" + "This function will multiply the given vector such that translation with take effect.\n" + "@param transform A transform.\n" + "@param point A vector.\n" + "@return The transformed vector.\n" + "@ingroup Matrices") +{ + MatrixF m = transform.getMatrix(); + m.mulP( point ); + return point; +} + +ConsoleFunctionGroupEnd(MatrixMath); + +//------------------------------------------------------------------------------ + +DefineConsoleFunction( getBoxCenter, Point3F, ( Box3F box ),, + "Get the center point of an axis-aligned box.\n\n" + "@param b A Box3F, in string format using \"minExtentX minExtentY minExtentZ maxExtentX maxExtentY maxExtentZ\"\n" + "@return Center of the box.\n" + "@ingroup Math") +{ + return box.getCenter(); +} + +//------------------------------------------------------------------------------ + +DefineEngineFunction( setRandomSeed, void, ( S32 seed ), ( -1 ), + "Set the current seed for the random number generator.\n" + "Based on this seed, a repeatable sequence of numbers will be produced by getRandom().\n" + "@param seed The seed with which to initialize the randon number generator with. The same seed will always leed to" + "the same sequence of pseudo-random numbers.\n" + "If -1, the current timestamp will be used as the seed which is a good basis for randomization.\n" + "@ingroup Random" ) +{ + if( seed == -1 ) + seed = Platform::getRealMilliseconds(); + + MRandomLCG::setGlobalRandSeed( seed ); +} + +//------------------------------------------------------------------------------ + +DefineEngineFunction( getRandomSeed, S32, (),, + "Get the current seed used by the random number generator.\n" + "@return The current random number generator seed value.\n" + "@ingroup Random" ) +{ + return gRandGen.getSeed(); +} + +//------------------------------------------------------------------------------ + +S32 mRandI(S32 i1, S32 i2) +{ + return gRandGen.randI(i1, i2); +} + +F32 mRandF(F32 f1, F32 f2) +{ + return gRandGen.randF(f1, f2); +} + +F32 mRandF() +{ + return gRandGen.randF(); +} + +ConsoleFunction( getRandom, F32, 1, 3, + "( int a, int b ) " + "@brief Returns a random number based on parameters passed in..\n\n" + "If no parameters are passed in, getRandom() will return a float between 0.0 and 1.0. If one " + "parameter is passed an integer between 0 and the passed in value will be returned. Two parameters will " + "return an integer between the specified numbers.\n\n" + "@param a If this is the only parameter, a number between 0 and a is returned. Elsewise represents the lower bound.\n" + "@param b Upper bound on the random number. The random number will be <= @a b.\n" + "@return A pseudo-random integer between @a a and @a b, between 0 and a, or a " + "float between 0.0 and 1.1 depending on usage.\n\n" + "@note All parameters are optional." + "@see setRandomSeed\n" + "@ingroup Random" ) +{ + if (argc == 2) + return F32(gRandGen.randI(0,getMax( dAtoi(argv[1]), 0 ))); + else + { + if (argc == 3) + { + S32 min = dAtoi(argv[1]); + S32 max = dAtoi(argv[2]); + if (min > max) + { + S32 t = min; + min = max; + max = t; + } + return F32(gRandGen.randI(min,max)); + } + } + return gRandGen.randF(); +} + +//------------------------------------------------------------------------------ diff --git a/Engine/source/math/mathTypes.h b/Engine/source/math/mathTypes.h new file mode 100644 index 000000000..c6ff83d7b --- /dev/null +++ b/Engine/source/math/mathTypes.h @@ -0,0 +1,79 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MATHTYPES_H_ +#define _MATHTYPES_H_ + +#ifndef _DYNAMIC_CONSOLETYPES_H_ + #include "console/dynamicTypes.h" +#endif + + +void RegisterMathFunctions(void); + + +class Point2I; +class Point2F; +class Point3F; +class Point4F; +class RectI; +class RectF; +class MatrixF; +class Box3F; +class EaseF; +class AngAxisF; +class TransformF; + + +DECLARE_SCOPE( MathTypes ); + + +DECLARE_STRUCT( Point2I ); +DECLARE_STRUCT( Point2F ); +DECLARE_STRUCT( Point3F ); +DECLARE_STRUCT( Point4F ); +DECLARE_STRUCT( RectI ); +DECLARE_STRUCT( RectF ); +DECLARE_STRUCT( MatrixF ); +DECLARE_STRUCT( AngAxisF ); +DECLARE_STRUCT( TransformF ); +DECLARE_STRUCT( Box3F ); +DECLARE_STRUCT( EaseF ); + + +// Legacy console types. +DefineConsoleType( TypePoint2I, Point2I ) +DefineConsoleType( TypePoint2F, Point2F ) +DefineConsoleType( TypePoint3F, Point3F ) +DefineConsoleType( TypePoint4F, Point4F ) +DefineConsoleType( TypeRectI, RectI ) +DefineConsoleType( TypeRectF, RectF ) +DefineConsoleType( TypeMatrixF, MatrixF ) +DefineConsoleType( TypeMatrixPosition, MatrixF) +DefineConsoleType( TypeMatrixRotation, MatrixF ) +DefineConsoleType( TypeAngAxisF, AngAxisF ) +DefineConsoleType( TypeTransformF, TransformF ) +DefineConsoleType( TypeBox3F, Box3F ) +DefineConsoleType( TypeEaseF, EaseF ) + + +#endif diff --git a/Engine/source/math/mathUtils.cpp b/Engine/source/math/mathUtils.cpp new file mode 100644 index 000000000..2540191fb --- /dev/null +++ b/Engine/source/math/mathUtils.cpp @@ -0,0 +1,1824 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "math/mathUtils.h" + +#include "math/mMath.h" +#include "math/mRandom.h" +#include "math/util/frustum.h" +#include "platform/profiler.h" +#include "core/tAlgorithm.h" + +namespace MathUtils +{ + +MRandomLCG sgRandom(0xdeadbeef); ///< Our random number generator. + +//----------------------------------------------------------------------------- + +bool capsuleCapsuleOverlap(const Point3F & a1, const Point3F & b1, F32 rad1, const Point3F & a2, const Point3F & b2, F32 rad2) +{ + F32 s,t; + Point3F c1,c2; + F32 dist = segmentSegmentNearest(a1,b1,a2,b2,s,t,c1,c2); + return dist <= (rad1+rad2)*(rad1+rad2); +} + +//----------------------------------------------------------------------------- + +F32 segmentSegmentNearest(const Point3F & p1, const Point3F & q1, const Point3F & p2, const Point3F & q2, F32 & s, F32 & t, Point3F & c1, Point3F & c2) +{ + Point3F d1 = q1-p1; + Point3F d2 = q2-p2; + Point3F r = p1-p2; + F32 a = mDot(d1,d1); + F32 e = mDot(d2,d2); + F32 f = mDot(d2,r); + + const F32 EPSILON = 0.001f; + + if (a <= EPSILON && e <= EPSILON) + { + s = t = 0.0f; + c1 = p1; + c2 = p2; + return mDot(c1-c2,c1-c2); + } + + if (a <= EPSILON) + { + s = 0.0f; + t = mClampF(f/e,0.0f,1.0f); + } + else + { + F32 c = mDot(d1,r); + if (e <= EPSILON) + { + t = 0.0f; + s = mClampF(-c/a,0.0f,1.0f); + } + else + { + F32 b = mDot(d1,d2); + F32 denom = a*e-b*b; + if (denom != 0.0f) + s = mClampF((b*f-c*e)/denom,0.0f,1.0f); + else + s = 0.0f; + F32 tnom = b*s+f; + if (tnom < 0.0f) + { + t = 0.0f; + s = mClampF(-c/a,0.0f,1.0f); + } + else if (tnom>e) + { + t = 1.0f; + s = mClampF((b-c)/a,0.0f,1.0f); + } + else + t = tnom/e; + } + } + + c1 = p1 + d1*s; + c2 = p2 + d2*t; + return mDot(c1-c2,c1-c2); +} + +//----------------------------------------------------------------------------- + +bool capsuleSphereNearestOverlap(const Point3F & A0, const Point3F A1, F32 radA, const Point3F & B, F32 radB, F32 & t) +{ + Point3F V = A1-A0; + Point3F A0B = A0-B; + F32 d1 = mDot(A0B,V); + F32 d2 = mDot(A0B,A0B); + F32 d3 = mDot(V,V); + F32 R2 = (radA+radB)*(radA+radB); + if (d20 && t1<1.0f) + { + t=t1; + return true; + } + F32 t2 = (-d1+b24ac)/d3; + if (t2>0 && t2<1.0f) + { + t=t2; + return true; + } + if (t1<0 && t2>0) + { + t=0; + return true; + } + return false; +} + +//----------------------------------------------------------------------------- + +void vectorRotateZAxis( Point3F &vector, F32 radians ) +{ + F32 sin, cos; + mSinCos(radians, sin, cos); + F32 x = cos * vector.x - sin * vector.y; + F32 y = sin * vector.x + cos * vector.y; + vector.x = x; + vector.y = y; +} + +void vectorRotateZAxis( F32 radians, Point3F *vectors, U32 count ) +{ + F32 sin, cos; + mSinCos(radians, sin, cos); + + F32 x, y; + const Point3F *end = vectors + count; + for ( ; vectors != end; vectors++ ) + { + x = cos * vectors->x - sin * vectors->y; + y = sin * vectors->x + cos * vectors->y; + vectors->x = x; + vectors->y = y; + } +} + +//----------------------------------------------------------------------------- + +void getZBiasProjectionMatrix( F32 bias, const Frustum &frustum, MatrixF *outMat, bool rotate ) +{ + Frustum temp(frustum); + temp.setNearDist(frustum.getNearDist() + bias); + temp.getProjectionMatrix(outMat, rotate); +} + +//----------------------------------------------------------------------------- + +MatrixF createOrientFromDir( const Point3F &direction ) +{ + Point3F j = direction; + Point3F k(0.0f, 0.0f, 1.0f); + Point3F i; + + mCross( j, k, &i ); + + if( i.magnitudeSafe() == 0.0f ) + { + i.set( 0.0f, -1.0f, 0.0f ); + } + + i.normalizeSafe(); + mCross( i, j, &k ); + + MatrixF mat( true ); + mat.setColumn( 0, i ); + mat.setColumn( 1, j ); + mat.setColumn( 2, k ); + + return mat; +} + +//----------------------------------------------------------------------------- + +void getMatrixFromUpVector( const VectorF &up, MatrixF *outMat ) +{ + AssertFatal( up.isUnitLength(), "MathUtils::getMatrixFromUpVector() - Up vector was not normalized!" ); + AssertFatal( outMat, "MathUtils::getMatrixFromUpVector() - Got null output matrix!" ); + AssertFatal( outMat->isAffine(), "MathUtils::getMatrixFromUpVector() - Got uninitialized matrix!" ); + + VectorF forward = mPerp( up ); + VectorF right = mCross( forward, up ); + right.normalize(); + forward = mCross( up, right ); + forward.normalize(); + + outMat->setColumn( 0, right ); + outMat->setColumn( 1, forward ); + outMat->setColumn( 2, up ); +} + +//----------------------------------------------------------------------------- + +void getMatrixFromForwardVector( const VectorF &forward, MatrixF *outMat ) +{ + AssertFatal( forward.isUnitLength(), "MathUtils::getMatrixFromForwardVector() - Forward vector was not normalized!" ); + AssertFatal( outMat, "MathUtils::getMatrixFromForwardVector() - Got null output matrix!" ); + AssertFatal( outMat->isAffine(), "MathUtils::getMatrixFromForwardVector() - Got uninitialized matrix!" ); + + VectorF up = mPerp( forward ); + VectorF right = mCross( forward, up ); + right.normalize(); + up = mCross( right, forward ); + up.normalize(); + + outMat->setColumn( 0, right ); + outMat->setColumn( 1, forward ); + outMat->setColumn( 2, up ); +} + +//----------------------------------------------------------------------------- + +Point3F randomDir( const Point3F &axis, F32 thetaAngleMin, F32 thetaAngleMax, + F32 phiAngleMin, F32 phiAngleMax ) +{ + MatrixF orient = createOrientFromDir( axis ); + Point3F axisx; + orient.getColumn( 0, &axisx ); + + F32 theta = (thetaAngleMax - thetaAngleMin) * sgRandom.randF() + thetaAngleMin; + F32 phi = (phiAngleMax - phiAngleMin) * sgRandom.randF() + phiAngleMin; + + // Both phi and theta are in degs. Create axis angles out of them, and create the + // appropriate rotation matrix... + AngAxisF thetaRot(axisx, theta * (M_PI_F / 180.0f)); + AngAxisF phiRot(axis, phi * (M_PI_F / 180.0f)); + + Point3F ejectionAxis = axis; + + MatrixF temp(true); + thetaRot.setMatrix(&temp); + temp.mulP(ejectionAxis); + phiRot.setMatrix(&temp); + temp.mulP(ejectionAxis); + + return ejectionAxis; +} + +//----------------------------------------------------------------------------- + +Point3F randomPointInSphere( F32 radius ) +{ + AssertFatal( radius > 0.0f, "MathUtils::randomPointInRadius - radius must be positive" ); + + #define MAX_TRIES 20 + + Point3F out; + F32 radiusSq = radius * radius; + + for ( S32 i = 0; i < MAX_TRIES; i++ ) + { + out.x = sgRandom.randF(-radius,radius); + out.y = sgRandom.randF(-radius,radius); + out.z = sgRandom.randF(-radius,radius); + + if ( out.lenSquared() < radiusSq ) + return out; + } + + AssertFatal( false, "MathUtils::randomPointInRadius - something is wrong, should not fail this many times." ); + return Point3F::Zero; +} + +//----------------------------------------------------------------------------- + +Point2F randomPointInCircle( F32 radius ) +{ + AssertFatal( radius > 0.0f, "MathUtils::randomPointInRadius - radius must be positive" ); + + #define MAX_TRIES 20 + + Point2F out; + F32 radiusSq = radius * radius; + + for ( S32 i = 0; i < MAX_TRIES; i++ ) + { + out.x = sgRandom.randF(-radius,radius); + out.y = sgRandom.randF(-radius,radius); + + if ( out.lenSquared() < radiusSq ) + return out; + } + + AssertFatal( false, "MathUtils::randomPointInRadius - something is wrong, should not fail this many times." ); + return Point2F::Zero; +} + +//----------------------------------------------------------------------------- + +void getAnglesFromVector( const VectorF &vec, F32 &yawAng, F32 &pitchAng ) +{ + yawAng = mAtan2( vec.x, vec.y ); + if( yawAng < 0.0f ) + yawAng += M_2PI_F; + + if( mFabs(vec.x) > mFabs(vec.y) ) + pitchAng = mAtan2( mFabs(vec.z), mFabs(vec.x) ); + else + pitchAng = mAtan2( mFabs(vec.z), mFabs(vec.y) ); + if( vec.z < 0.0f ) + pitchAng = -pitchAng; +} + +//----------------------------------------------------------------------------- + +void getVectorFromAngles( VectorF &vec, F32 yawAng, F32 pitchAng ) +{ + VectorF pnt( 0.0f, 1.0f, 0.0f ); + + EulerF rot( -pitchAng, 0.0f, 0.0f ); + MatrixF mat( rot ); + + rot.set( 0.0f, 0.0f, yawAng ); + MatrixF mat2( rot ); + + mat.mulV( pnt ); + mat2.mulV( pnt ); + + vec = pnt; +} + +//----------------------------------------------------------------------------- + +void transformBoundingBox(const Box3F &sbox, const MatrixF &mat, const Point3F scale, Box3F &dbox) +{ + Point3F center; + + // set transformed center... + sbox.getCenter(¢er); + center.convolve(scale); + mat.mulP(center); + dbox.minExtents = center; + dbox.maxExtents = center; + + Point3F val; + for(U32 ix=0; ix<2; ix++) + { + if(ix & 0x1) + val.x = sbox.minExtents.x; + else + val.x = sbox.maxExtents.x; + + for(U32 iy=0; iy<2; iy++) + { + if(iy & 0x1) + val.y = sbox.minExtents.y; + else + val.y = sbox.maxExtents.y; + + for(U32 iz=0; iz<2; iz++) + { + if(iz & 0x1) + val.z = sbox.minExtents.z; + else + val.z = sbox.maxExtents.z; + + Point3F v1, v2; + v1 = val; + v1.convolve(scale); + mat.mulP(v1, &v2); + dbox.minExtents.setMin(v2); + dbox.maxExtents.setMax(v2); + } + } + } +} + +//----------------------------------------------------------------------------- + +bool mProjectWorldToScreen( const Point3F &in, + Point3F *out, + const RectI &view, + const MatrixF &world, + const MatrixF &projection ) +{ + MatrixF worldProjection = projection; + worldProjection.mul(world); + + return mProjectWorldToScreen( in, out, view, worldProjection ); +} + +//----------------------------------------------------------------------------- + +bool mProjectWorldToScreen( const Point3F &in, + Point3F *out, + const RectI &view, + const MatrixF &worldProjection ) +{ + Point4F temp(in.x,in.y,in.z,1.0f); + worldProjection.mul(temp); + + // Perform the perspective division. For orthographic + // projections, temp.w will be 1. + + temp.x /= temp.w; + temp.y /= temp.w; + temp.z /= temp.w; + + // Take the normalized device coordinates (NDC) and transform them + // into device coordinates. + + out->x = (temp.x + 1.0f) / 2.0f * view.extent.x + view.point.x; + out->y = (1.0f - temp.y) / 2.0f * view.extent.y + view.point.y; + out->z = temp.z; + + if ( out->z < 0.0f || out->z > 1.0f || + out->x < (F32)view.point.x || out->x > (F32)view.point.x + (F32)view.extent.x || + out->y < (F32)view.point.y || out->y > (F32)view.point.y + (F32)view.extent.y ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- + +void mProjectScreenToWorld( const Point3F &in, + Point3F *out, + const RectI &view, + const MatrixF &world, + const MatrixF &projection, + F32 zfar, + F32 znear ) +{ + MatrixF invWorldProjection = projection; + invWorldProjection.mul(world); + invWorldProjection.inverse(); + + Point3F vec; + vec.x = (in.x - view.point.x) * 2.0f / view.extent.x - 1.0f; + vec.y = -(in.y - view.point.y) * 2.0f / view.extent.y + 1.0f; + vec.z = (znear + in.z * (zfar - znear))/zfar; + + invWorldProjection.mulV(vec); + vec *= 1.0f + in.z * zfar; + + invWorldProjection.getColumn(3, out); + (*out) += vec; +} + +//----------------------------------------------------------------------------- + +bool pointInPolygon( const Point2F *verts, U32 vertCount, const Point2F &testPt ) +{ + U32 i, j, c = 0; + for ( i = 0, j = vertCount-1; i < vertCount; j = i++ ) + { + if ( ( ( verts[i].y > testPt.y ) != ( verts[j].y > testPt.y ) ) && + ( testPt.x < ( verts[j].x - verts[i].x ) * + ( testPt.y - verts[i].y ) / + ( verts[j].y - verts[i].y ) + verts[i].x ) ) + c = !c; + } + + return c != 0; +} + +//----------------------------------------------------------------------------- + +F32 mTriangleDistance( const Point3F &A, const Point3F &B, const Point3F &C, const Point3F &P, IntersectInfo* info ) +{ + Point3F diff = A - P; + Point3F edge0 = B - A; + Point3F edge1 = C - A; + F32 a00 = edge0.lenSquared(); + F32 a01 = mDot( edge0, edge1 ); + F32 a11 = edge1.lenSquared(); + F32 b0 = mDot( diff, edge0 ); + F32 b1 = mDot( diff, edge1 ); + F32 c = diff.lenSquared(); + F32 det = mFabs(a00*a11-a01*a01); + F32 s = a01*b1-a11*b0; + F32 t = a01*b0-a00*b1; + F32 sqrDistance; + + if (s + t <= det) + { + if (s < 0.0f) + { + if (t < 0.0f) // region 4 + { + if (b0 < 0.0f) + { + t = 0.0f; + if (-b0 >= a00) + { + s = 1.0f; + sqrDistance = a00 + (2.0f)*b0 + c; + } + else + { + s = -b0/a00; + sqrDistance = b0*s + c; + } + } + else + { + s = 0.0f; + if (b1 >= 0.0f) + { + t = 0.0f; + sqrDistance = c; + } + else if (-b1 >= a11) + { + t = 1.0f; + sqrDistance = a11 + 2.0f*b1 + c; + } + else + { + t = -b1/a11; + sqrDistance = b1*t + c; + } + } + } + else // region 3 + { + s = 0.0f; + if (b1 >= 0.0f) + { + t = 0.0f; + sqrDistance = c; + } + else if (-b1 >= a11) + { + t = 1.0f; + sqrDistance = a11 + 2.0f*b1 + c; + } + else + { + t = -b1/a11; + sqrDistance = b1*t + c; + } + } + } + else if (t < 0.0f) // region 5 + { + t = 0.0f; + if (b0 >= 0.0f) + { + s = 0.0f; + sqrDistance = c; + } + else if (-b0 >= a00) + { + s = 1.0f; + sqrDistance = a00 + 2.0f*b0 + c; + } + else + { + s = -b0/a00; + sqrDistance = b0*s + c; + } + } + else // region 0 + { + // minimum at interior point + F32 invDet = 1.0f / det; + s *= invDet; + t *= invDet; + sqrDistance = s * (a00*s + a01*t + 2.0f*b0) + + t * (a01*s + a11*t + 2.0f*b1) + c; + } + } + else + { + F32 tmp0, tmp1, numer, denom; + + if (s < 0.0f) // region 2 + { + tmp0 = a01 + b0; + tmp1 = a11 + b1; + if (tmp1 > tmp0) + { + numer = tmp1 - tmp0; + denom = a00 - 2.0f*a01 + a11; + if (numer >= denom) + { + s = 1.0f; + t = 0.0f; + sqrDistance = a00 + 2.0f*b0 + c; + } + else + { + s = numer/denom; + t = 1.0f - s; + sqrDistance = s * (a00*s + a01*t + 2.0f*b0) + + t * (a01*s + a11*t + 2.0f*b1) + c; + } + } + else + { + s = 0.0f; + if (tmp1 <= 0.0f) + { + t = 1.0f; + sqrDistance = a11 + 2.0f*b1 + c; + } + else if (b1 >= 0.0f) + { + t = 0.0f; + sqrDistance = c; + } + else + { + t = -b1/a11; + sqrDistance = b1*t + c; + } + } + } + else if (t < 0.0f) // region 6 + { + tmp0 = a01 + b1; + tmp1 = a00 + b0; + if (tmp1 > tmp0) + { + numer = tmp1 - tmp0; + denom = a00 - 2.0f*a01 + a11; + if (numer >= denom) + { + t = 1.0f; + s = 0.0f; + sqrDistance = a11 + 2.0f*b1 + c; + } + else + { + t = numer/denom; + s = 1.0f - t; + sqrDistance = s * (a00*s + a01*t + 2.0f*b0) + + t * (a01*s + a11*t + 2.0f*b1) + c; + } + } + else + { + t = 0.0f; + if (tmp1 <= 0.0f) + { + s = 1.0f; + sqrDistance = a00 + 2.0f*b0 + c; + } + else if (b0 >= 0.0f) + { + s = 0.0f; + sqrDistance = c; + } + else + { + s = -b0/a00; + sqrDistance = b0*s + c; + } + } + } + else // region 1 + { + numer = a11 + b1 - a01 - b0; + if (numer <= 0.0f) + { + s = 0.0f; + t = 1.0f; + sqrDistance = a11 + 2.0f*b1 + c; + } + else + { + denom = a00 - 2.0f*a01 + a11; + if (numer >= denom) + { + s = 1.0f; + t = 0.0f; + sqrDistance = a00 + 2.0f*b0 + c; + } + else + { + s = numer/denom; + t = 1.0f - s; + sqrDistance = s * (a00*s + a01*t + 2.0f*b0) + + t * (a01*s + a11*t + 2.0f*b1) + c; + } + } + } + } + + // account for numerical round-off error + if (sqrDistance < 0.0f) + sqrDistance = 0.0f; + + // This also calculates the barycentric coordinates and the closest point! + //m_kClosestPoint0 = P; + //m_kClosestPoint1 = A + s*edge0 + t*edge1; + //m_afTriangleBary[1] = s; + //m_afTriangleBary[2] = t; + //m_afTriangleBary[0] = (Real)1.0 - fS - fT; + if(info) + { + info->segment.p0 = P; + info->segment.p1 = A + s*edge0 + t*edge1; + info->bary.x = s; + info->bary.y = t; + info->bary.z = 1.0f - s - t; + } + + return sqrDistance; +} + +//----------------------------------------------------------------------------- + +Point3F mTriangleNormal( const Point3F &a, const Point3F &b, const Point3F &c ) +{ + // Vector from b to a. + const F32 ax = a.x-b.x; + const F32 ay = a.y-b.y; + const F32 az = a.z-b.z; + // Vector from b to c. + const F32 cx = c.x-b.x; + const F32 cy = c.y-b.y; + const F32 cz = c.z-b.z; + + Point3F n; + + // This is an in-line cross product. + n.x = ay*cz - az*cy; + n.y = az*cx - ax*cz; + n.z = ax*cy - ay*cx; + m_point3F_normalize( (F32*)(&n) ); + + return n; +} + +//----------------------------------------------------------------------------- + +Point3F mClosestPointOnSegment( const Point3F &a, const Point3F &b, const Point3F &p ) +{ + Point3F c = p - a; // Vector from a to Point + Point3F v = (b - a); + F32 d = v.len(); // Length of the line segment + v.normalize(); // Unit Vector from a to b + F32 t = mDot( v, c ); // Intersection point Distance from a + + // Check to see if the point is on the line + // if not then return the endpoint + if(t < 0) return a; + if(t > d) return b; + + // get the distance to move from point a + v *= t; + + // move from point a to the nearest point on the segment + return a + v; +} + +//----------------------------------------------------------------------------- + +void mShortestSegmentBetweenLines( const Line &line0, const Line &line1, LineSegment *outSegment ) +{ + // compute intermediate parameters + Point3F w0 = line0.origin - line1.origin; + F32 a = mDot( line0.direction, line0.direction ); + F32 b = mDot( line0.direction, line1.direction ); + F32 c = mDot( line1.direction, line1.direction ); + F32 d = mDot( line0.direction, w0 ); + F32 e = mDot( line1.direction, w0 ); + + F32 denom = a*c - b*b; + + if ( denom > -0.001f && denom < 0.001f ) + { + outSegment->p0 = line0.origin; + outSegment->p1 = line1.origin + (e/c)*line1.direction; + } + else + { + outSegment->p0 = line0.origin + ((b*e - c*d)/denom)*line0.direction; + outSegment->p1 = line1.origin + ((a*e - b*d)/denom)*line1.direction; + } +} + +//----------------------------------------------------------------------------- + +U32 greatestCommonDivisor( U32 u, U32 v ) +{ + // http://en.wikipedia.org/wiki/Binary_GCD_algorithm + + int shift; + + /* GCD(0,x) := x */ + if (u == 0 || v == 0) + return u | v; + + /* Left shift := lg K, where K is the greatest power of 2 + dividing both u and v. */ + for (shift = 0; ((u | v) & 1) == 0; ++shift) { + u >>= 1; + v >>= 1; + } + + while ((u & 1) == 0) + u >>= 1; + + /* From here on, u is always odd. */ + do { + while ((v & 1) == 0) /* Loop X */ + v >>= 1; + + /* Now u and v are both odd, so diff(u, v) is even. + Let u = min(u, v), v = diff(u, v)/2. */ + if (u < v) { + v -= u; + } else { + unsigned int diff = u - v; + u = v; + v = diff; + } + v >>= 1; + } while (v != 0); + + return u << shift; +} + +//----------------------------------------------------------------------------- + +bool mLineTriangleCollide( const Point3F &p1, const Point3F &p2, + const Point3F &t1, const Point3F &t2, const Point3F &t3, + Point3F *outUVW, F32 *outT ) +{ + VectorF ab = t2 - t1; + VectorF ac = t3 - t1; + VectorF qp = p1 - p2; + + // Compute triangle normal. Can be precalculated or cached if + // intersecting multiple segments against the same triangle + VectorF n = mCross( ab, ac ); + + // Compute denominator d. If d <= 0, segment is parallel to or points + // away from triangle, so exit early + F32 d = mDot( qp, n ); + if ( d <= 0.0f ) + return false; + + // Compute intersection t value of pq with plane of triangle. A ray + // intersects if 0 <= t. Segment intersects iff 0 <= t <= 1. Delay + // dividing by d until intersection has been found to pierce triangle + VectorF ap = p1 - t1; + F32 t = mDot( ap, n ); + 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 + VectorF e = mCross( qp, ap ); + F32 v = mDot( ac, e ); + if ( v < 0.0f || v > d ) + return false; + F32 w = -mDot( ab, e ); + if ( w < 0.0f || v + w > d ) + return false; + + // Segment/ray intersects triangle. Perform delayed division and + // compute the last barycentric coordinate component + const F32 ood = 1.0f / d; + + if ( outT ) + *outT = t * ood; + + if ( outUVW ) + { + v *= ood; + w *= ood; + outUVW->set( 1.0f - v - w, v, w ); + } + + return true; +} + +//----------------------------------------------------------------------------- + +bool mRayQuadCollide( const Quad &quad, + const Ray &ray, + Point2F *outUV, + F32 *outT ) +{ + static const F32 eps = F32(10e-6); + + // Rejects rays that are parallel to Q, and rays that intersect the plane of + // Q either on the left of the line V00V01 or on the right of the line V00V10. + + // p01-----eXX-----p11 + // ^ . ^ | + // | . | + // e03 e02 eXX + // | . | + // | . | + // p00-----e01---->p10 + + VectorF e01 = quad.p10 - quad.p00; + VectorF e03 = quad.p01 - quad.p00; + + // If the ray is perfectly perpendicular to e03, which + // represents the entire planes tangent, then the + // result of this cross product (P) will equal e01 + // If it is parallel it will result in a vector opposite e01. + + // If the ray is heading DOWN the cross product will point to the RIGHT + // If the ray is heading UP the cross product will point to the LEFT + // We do not reject based on this though... + // + // In either case cross product will be more parallel to e01 the more + // perpendicular the ray is to e03, and it will be more perpendicular to + // e01 the more parallel it is to e03. + VectorF P = mCross(ray.direction, e03); + + // det can be seen as 'the amount of vector e01 in the direction P' + F32 det = mDot(e01, P); + + // Take a Abs of the dot because we do not care if the ray is heading up or down, + // but if it is perfectly parallel to the quad we want to reject it. + if ( mFabs(det) < eps ) + return false; + + F32 inv_det = 1.0f / det; + + VectorF T = ray.origin - quad.p00; + + // alpha can be seen as 'the amount of vector T in the direction P' + // T is a vector up from the quads corner point 00 to the ray's origin. + // P is the cross product of the ray and e01, which should be "roughly" + // parallel with e03 but might be of either positive or negative magnitude. + F32 alpha = mDot(T, P) * inv_det; + if ( alpha < 0.0f ) + return false; + + // if (alpha > real(1.0)) return false; // Uncomment if VR is used. + + // The cross product of T and e01 should be roughly parallel to e03 + // and of either positive or negative magnitude. + VectorF Q = mCross(T, e01); + F32 beta = mDot(ray.direction, Q) * inv_det; + if ( beta < 0.0f ) + return false; + + // if (beta > real(1.0)) return false; // Uncomment if VR is used. + + if ( alpha + beta > 1.0f ) + //if ( false ) + { + // Rejects rays that intersect the plane of Q either on the + // left of the line V11V10 or on the right of the line V11V01. + + VectorF e23 = quad.p01 - quad.p11; + VectorF e21 = quad.p10 - quad.p11; + VectorF P_prime = mCross(ray.direction, e21); + F32 det_prime = mDot(e23, P_prime); + if ( mFabs(det_prime) < eps) + return false; + F32 inv_det_prime = 1.0f / det_prime; + VectorF T_prime = ray.origin - quad.p11; + F32 alpha_prime = mDot(T_prime, P_prime) * inv_det_prime; + if (alpha_prime < 0.0f) + return false; + VectorF Q_prime = mCross(T_prime, e23); + F32 beta_prime = mDot(ray.direction, Q_prime) * inv_det_prime; + if (beta_prime < 0.0f) + return false; + } + + // Compute the ray parameter of the intersection point, and + // reject the ray if it does not hit Q. + + F32 t = mDot(e03, Q) * inv_det; + if ( t < 0.0f ) + return false; + + + // Compute the barycentric coordinates of the fourth vertex. + // These do not depend on the ray, and can be precomputed + // and stored with the quadrilateral. + + F32 alpha_11, beta_11; + VectorF e02 = quad.p11 - quad.p00; + VectorF n = mCross(e01, e03); + + if ( mFabs(n.x) >= mFabs(n.y) && + mFabs(n.x) >= mFabs(n.z) ) + { + alpha_11 = ( e02.y * e03.z - e02.z * e03.y ) / n.x; + beta_11 = ( e01.y * e02.z - e01.z * e02.y ) / n.x; + } + else if ( mFabs(n.y) >= mFabs(n.x) && + mFabs(n.y) >= mFabs(n.z) ) + { + alpha_11 = ((e02.z * e03.x) - (e02.x * e03.z)) / n.y; + beta_11 = ((e01.z * e02.x) - (e01.x * e02.z)) / n.y; + } + else + { + alpha_11 = ((e02.x * e03.y) - (e02.y * e03.x)) / n.z; + beta_11 = ((e01.x * e02.y) - (e01.y * e02.x)) / n.z; + } + + // Compute the bilinear coordinates of the intersection point. + + F32 u,v; + + if ( mFabs(alpha_11 - 1.0f) < eps) + { + // Q is a trapezium. + u = alpha; + if ( mFabs(beta_11 - 1.0f) < eps) + v = beta; // Q is a parallelogram. + else + v = beta / ((u * (beta_11 - 1.0f)) + 1.0f); // Q is a trapezium. + } + else if ( mFabs(beta_11 - 1.0f) < eps) + { + // Q is a trapezium. + v = beta; + u = alpha / ((v * (alpha_11 - 1.0f)) + 1.0f); + } + else + { + F32 A = 1.0f - beta_11; + F32 B = (alpha * (beta_11 - 1.0f)) + - (beta * (alpha_11 - 1.0f)) - 1.0f; + F32 C = alpha; + F32 D = (B * B) - (4.0f * A * C); + F32 Q = -0.5f * (B + (B < 0.0f ? -1.0f : 1.0f) ) * mSqrt(D); + u = Q / A; + if ((u < 0.0f) || (u > 1.0f)) u = C / Q; + v = beta / ((u * (beta_11 - 1.0f)) + 1.0f); + } + + if ( outUV ) + outUV->set( u, v ); + if ( outT ) + *outT = t; + + return true; +} + +//----------------------------------------------------------------------------- + +// Used by sortQuadWindingOrder. +struct QuadSortPoint +{ + U32 id; + F32 theta; +}; + +// Used by sortQuadWindingOrder. +int QSORT_CALLBACK cmpAngleAscending( const void *a, const void *b ) +{ + const QuadSortPoint *p0 = (const QuadSortPoint*)a; + const QuadSortPoint *p1 = (const QuadSortPoint*)b; + + F32 diff = p1->theta - p0->theta; + + if ( diff > 0.0f ) + return -1; + else if ( diff < 0.0f ) + return 1; + else + return 0; +} + +// Used by sortQuadWindingOrder. +int QSORT_CALLBACK cmpAngleDescending( const void *a, const void *b ) +{ + const QuadSortPoint *p0 = (const QuadSortPoint*)a; + const QuadSortPoint *p1 = (const QuadSortPoint*)b; + + F32 diff = p1->theta - p0->theta; + + if ( diff > 0.0f ) + return 1; + else if ( diff < 0.0f ) + return -1; + else + return 0; +} + +void sortQuadWindingOrder( const MatrixF &quadMat, bool clockwise, const Point3F *verts, U32 *vertMap, U32 count ) +{ + PROFILE_SCOPE( MathUtils_sortQuadWindingOrder ); + + if ( count == 0 ) + return; + + Point3F *quadPoints = new Point3F[count]; + + for ( S32 i = 0; i < count; i++ ) + { + quadMat.mulP( verts[i], &quadPoints[i] ); + quadPoints[i].normalizeSafe(); + } + + sortQuadWindingOrder( clockwise, quadPoints, vertMap, count ); + + delete [] quadPoints; +} + +void sortQuadWindingOrder( bool clockwise, const Point3F *verts, U32 *vertMap, U32 count ) +{ + QuadSortPoint *sortPoints = new QuadSortPoint[count]; + + for ( S32 i = 0; i < count; i++ ) + { + QuadSortPoint &sortPnt = sortPoints[i]; + const Point3F &vec = verts[i]; + + sortPnt.id = i; + + F32 theta = mAtan2( vec.y, vec.x ); + + if ( vec.y < 0.0f ) + theta = M_2PI_F + theta; + + sortPnt.theta = theta; + } + + dQsort( sortPoints, count, sizeof( QuadSortPoint ), clockwise ? cmpAngleDescending : cmpAngleAscending ); + + for ( S32 i = 0; i < count; i++ ) + vertMap[i] = sortPoints[i].id; + + delete [] sortPoints; +} + +//----------------------------------------------------------------------------- + +void buildMatrix( const VectorF *rvec, const VectorF *fvec, const VectorF *uvec, const VectorF *pos, MatrixF *outMat ) +{ + /// Work in Progress + + /* + AssertFatal( !rvec || rvec->isUnitLength(), "MathUtils::buildMatrix() - Right vector was not normalized!" ); + AssertFatal( !fvec || fvec->isUnitLength(), "MathUtils::buildMatrix() - Forward vector was not normalized!" ); + AssertFatal( !uvec || uvec->isUnitLength(), "MathUtils::buildMatrix() - Up vector was not normalized!" ); + + // Note this relationship: + // + // Column0 Column1 Column2 + // Axis X Axis Y Axis Z + // Rvec Fvec Uvec + // + + enum + { + RVEC = 1, + FVEC = 1 << 1, + UVEC = 1 << 2, + ALL = RVEC | FVEC | UVEC + }; + + U8 mask = 0; + U8 count = 0; + U8 axis0, axis1; + + if ( rvec ) + { + mask |= RVEC; + axis0 == 0; + count++; + } + if ( fvec ) + { + mask |= FVEC; + if ( count == 0 ) + axis0 = 1; + else + axis1 = 1; + count++; + } + if ( uvec ) + { + mask |= UVEC; + count++; + } + + U8 bR = 1; + U8 bF = 1 << 1; + U8 bU = 1 << 2; + U8 bRF = bR | bF; + U8 bRU = bR | bU; + U8 bFU = bF | bU; + U8 bRFU = bR | bF | bU; + + + + // Cross product map. + U8 cpdMap[3][2] = + { + { 1, 2 }, + { 2, 0 }, + { 0, 1 }, + } + + if ( count == 1 ) + { + if ( mask == bR ) + { + + } + else if ( mask == bF ) + { + + } + else if ( mask == bU ) + { + + } + } + else if ( count == 2 ) + { + if ( mask == bRF ) + { + + } + else if ( mask == bRU ) + { + + } + else if ( mask == bFU ) + { + + } + } + else // bRFU + { + + } + + if ( rvec ) + { + outMat->setColumn( 0, *rvec ); + + if ( fvec ) + { + outMat->setColumn( 1, *fvec ); + + if ( uvec ) + outMat->setColumn( 2, *uvec ); + else + { + // Set uvec from rvec/fvec + tmp = mCross( rvec, fvec ); + tmp.normalizeSafe(); + outMat->setColumn( 2, tmp ); + } + } + else if ( uvec ) + { + // Set fvec from uvec/rvec + tmp = mCross( uvec, rvec ); + tmp.normalizeSafe(); + outMat->setColumn( 1, tmp ); + } + else + { + // Set fvec and uvec from rvec + Point3F tempFvec = mPerp( rvec ); + Point3F tempUvec = mCross( ) + + } + } + AssertFatal( rvec->isUnitLength(), "MathUtils::buildMatrix() - Right vector was not normalized!" ); + AssertFatal( fvec->isUnitLength(), "MathUtils::buildMatrix() - Forward vector was not normalized!" ); + AssertFatal( uvec->isUnitLength(), "MathUtils::buildMatrix() - UpVector vector was not normalized!" ); + AssertFatal( outMat, "MathUtils::buildMatrix() - Got null output matrix!" ); + AssertFatal( outMat->isAffine(), "MathUtils::buildMatrix() - Got uninitialized matrix!" ); + */ +} + +//----------------------------------------------------------------------------- + +bool reduceFrustum( const Frustum& frustum, const RectI& viewport, const RectF& area, Frustum& outFrustum ) +{ + // Just to be safe, clamp the area to the viewport. + + Point2F clampedMin; + Point2F clampedMax; + + clampedMin.x = mClampF( area.extent.x, ( F32 ) viewport.point.x, ( F32 ) viewport.point.x + viewport.extent.x ); + clampedMin.y = mClampF( area.extent.y, ( F32 ) viewport.point.y, ( F32 ) viewport.point.y + viewport.extent.y ); + + clampedMax.x = mClampF( area.extent.x, ( F32 ) viewport.point.x, ( F32 ) viewport.point.x + viewport.extent.x ); + clampedMax.y = mClampF( area.extent.y, ( F32 ) viewport.point.y, ( F32 ) viewport.point.y + viewport.extent.y ); + + // If we have ended up without a visible region on the screen, + // terminate now. + + if( mFloor( clampedMin.x ) == mFloor( clampedMax.x ) || + mFloor( clampedMin.y ) == mFloor( clampedMax.y ) ) + return false; + + // Get the extents of the frustum. + + const F32 frustumXExtent = mFabs( frustum.getNearRight() - frustum.getNearLeft() ); + const F32 frustumYExtent = mFabs( frustum.getNearTop() - frustum.getNearBottom() ); + + // Now, normalize the screen-space pixel coordinates to lie within the screen-centered + // -1 to 1 coordinate space that is used for the frustum planes. + + Point2F normalizedMin; + Point2F normalizedMax; + + normalizedMin.x = ( ( clampedMin.x / viewport.extent.x ) * frustumXExtent ) - ( frustumXExtent / 2.f ); + normalizedMin.y = ( ( clampedMin.y / viewport.extent.y ) * frustumYExtent ) - ( frustumYExtent / 2.f ); + normalizedMax.x = ( ( clampedMax.x / viewport.extent.x ) * frustumXExtent ) - ( frustumXExtent / 2.f ); + normalizedMax.y = ( ( clampedMax.y / viewport.extent.y ) * frustumYExtent ) - ( frustumYExtent / 2.f ); + + // Make sure the generated frustum metrics are somewhat sane. + + if( normalizedMax.x - normalizedMin.x < 0.001f || + normalizedMax.y - normalizedMin.y < 0.001f ) + return false; + + // Finally, create the new frustum using the original's frustum + // information except its left/right/top/bottom planes. + // + // Note that screen-space coordinates go upside down on Y whereas + // camera-space frustum coordinates go downside up on Y which is + // why we are inverting Y here. + + outFrustum.set( + frustum.isOrtho(), + normalizedMin.x, + normalizedMax.x, + - normalizedMin.y, + - normalizedMax.y, + frustum.getNearDist(), + frustum.getFarDist(), + frustum.getTransform() + ); + + return true; +} + +//----------------------------------------------------------------------------- + +void makeFrustum( F32 *outLeft, + F32 *outRight, + F32 *outTop, + F32 *outBottom, + F32 fovYInRadians, + F32 aspectRatio, + F32 nearPlane ) +{ + F32 top = nearPlane * mTan( fovYInRadians / 2.0 ); + if ( outTop ) *outTop = top; + if ( outBottom ) *outBottom = -top; + + F32 left = top * aspectRatio; + if ( outLeft ) *outLeft = -left; + if ( outRight ) *outRight = left; +} + +//----------------------------------------------------------------------------- + +void makeProjection( MatrixF *outMatrix, + F32 fovYInRadians, + F32 aspectRatio, + F32 nearPlane, + F32 farPlane, + bool gfxRotate ) +{ + F32 left, right, top, bottom; + makeFrustum( &left, &right, &top, &bottom, fovYInRadians, aspectRatio, nearPlane ); + makeProjection( outMatrix, left, right, top, bottom, nearPlane, farPlane, gfxRotate ); +} + +//----------------------------------------------------------------------------- + +/// This is the special rotation matrix applied to +/// projection matricies for GFX. +/// +/// It is a wart of the OGL to DX change over. +/// +static const MatrixF sGFXProjRotMatrix( EulerF( (M_PI_F / 2.0f), 0.0f, 0.0f ) ); + +void makeProjection( MatrixF *outMatrix, + F32 left, + F32 right, + F32 top, + F32 bottom, + F32 nearPlane, + F32 farPlane, + bool gfxRotate ) +{ + + Point4F row; + row.x = 2.0*nearPlane / (right-left); + row.y = 0.0; + row.z = 0.0; + row.w = 0.0; + outMatrix->setRow( 0, row ); + + row.x = 0.0; + row.y = 2.0 * nearPlane / (top-bottom); + row.z = 0.0; + row.w = 0.0; + outMatrix->setRow( 1, row ); + + row.x = (left+right) / (right-left); + row.y = (top+bottom) / (top-bottom); + row.z = farPlane / (nearPlane-farPlane); + row.w = -1.0; + outMatrix->setRow( 2, row ); + + row.x = 0.0; + row.y = 0.0; + row.z = nearPlane * farPlane / (nearPlane-farPlane); + row.w = 0.0; + outMatrix->setRow( 3, row ); + + outMatrix->transpose(); + + if ( gfxRotate ) + outMatrix->mul( sGFXProjRotMatrix ); +} + +//----------------------------------------------------------------------------- + +void makeOrthoProjection( MatrixF *outMatrix, + F32 left, + F32 right, + F32 top, + F32 bottom, + F32 nearPlane, + F32 farPlane, + bool gfxRotate ) +{ + Point4F row; + row.x = 2.0f / (right - left); + row.y = 0.0f; + row.z = 0.0f; + row.w = 0.0f; + outMatrix->setRow( 0, row ); + + row.x = 0.0f; + row.y = 2.0f / (top - bottom); + row.z = 0.0f; + row.w = 0.0f; + outMatrix->setRow( 1, row ); + + row.x = 0.0f; + row.y = 0.0f; + row.w = 0.0f; + + // This may need be modified to work with OpenGL (d3d has 0..1 + // projection for z, vs -1..1 in OpenGL) + row.z = 1.0f / (nearPlane - farPlane); + + outMatrix->setRow( 2, row ); + + row.x = (left + right) / (left - right); + row.y = (top + bottom) / (bottom - top); + row.z = nearPlane / (nearPlane - farPlane); + row.w = 1.0f; + outMatrix->setRow( 3, row ); + + outMatrix->transpose(); + + if ( gfxRotate ) + outMatrix->mul( sGFXProjRotMatrix ); +} + +//----------------------------------------------------------------------------- + +bool edgeFaceIntersect( const Point3F &edgeA, const Point3F &edgeB, + const Point3F &faceA, const Point3F &faceB, const Point3F &faceC, const Point3F &faceD, Point3F *intersection ) +{ + VectorF edgeAB = edgeB - edgeA; + VectorF edgeAFaceA = faceA - edgeA; + VectorF edgeAFaceB = faceB - edgeA; + VectorF edgeAFaceC = faceC - edgeA; + + VectorF m = mCross( edgeAFaceC, edgeAB ); + F32 v = mDot( edgeAFaceA, m ); + if ( v >= 0.0f ) + { + F32 u = -mDot( edgeAFaceB, m ); + if ( u < 0.0f ) + return false; + + VectorF tmp = mCross( edgeAFaceB, edgeAB ); + F32 w = mDot( edgeAFaceA, tmp ); + if ( w < 0.0f ) + return false; + + F32 denom = 1.0f / (u + v + w ); + u *= denom; + v *= denom; + w *= denom; + + (*intersection) = u * faceA + v * faceB + w * faceC; + } + else + { + VectorF edgeAFaceD = faceD - edgeA; + F32 u = mDot( edgeAFaceD, m ); + if ( u < 0.0f ) + return false; + + VectorF tmp = mCross( edgeAFaceA, edgeAB ); + F32 w = mDot( edgeAFaceD, tmp ); + if ( w < 0.0f ) + return false; + + v = -v; + + F32 denom = 1.0f / ( u + v + w ); + u *= denom; + v *= denom; + w *= denom; + + (*intersection) = u * faceA + v * faceD + w * faceC; + } + + return true; +} + +//----------------------------------------------------------------------------- + +bool isPlanarPolygon( const Point3F* vertices, U32 numVertices ) +{ + AssertFatal( vertices != NULL, "MathUtils::isPlanarPolygon - Received NULL pointer" ); + AssertFatal( numVertices >= 3, "MathUtils::isPlanarPolygon - Must have at least three vertices" ); + + // Triangles are always planar. Letting smaller numVertices + // slip through provides robustness for errors in release builds. + + if( numVertices <= 3 ) + return true; + + // Compute the normal of the first triangle in the polygon. + + Point3F triangle1Normal = mTriangleNormal( vertices[ 0 ], vertices[ 1 ], vertices[ 2 ] ); + + // Now go through all the remaining vertices and build triangles + // with the first two vertices. Then the normals of all these triangles + // must be the same (minus some variance due to floating-point inaccuracies) + // as the normal of the first triangle. + + for( U32 i = 3; i < numVertices; ++ i ) + { + Point3F triangle2Normal = mTriangleNormal( vertices[ 0 ], vertices[ 1 ], vertices[ i ] ); + if( !triangle1Normal.equal( triangle2Normal ) ) + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- + +bool isConvexPolygon( const Point3F* vertices, U32 numVertices ) +{ + AssertFatal( vertices != NULL, "MathUtils::isConvexPolygon - Received NULL pointer" ); + AssertFatal( numVertices >= 3, "MathUtils::isConvexPolygon - Must have at least three vertices" ); + + // Triangles are always convex. Letting smaller numVertices + // slip through provides robustness for errors in release builds. + + if( numVertices <= 3 ) + return true; + + U32 numPositive = 0; + U32 numNegative = 0; + + for( U32 i = 0; i < numVertices; ++ i ) + { + const Point3F& a = vertices[ i ]; + const Point3F& b = vertices[ ( i + 1 ) % numVertices ]; + const Point3F& c = vertices[ ( i + 2 ) % numVertices ]; + + const F32 crossProductLength = mCross( b - a, c - b ).len(); + + if( crossProductLength < 0.f ) + numNegative ++; + else if( crossProductLength > 0.f ) + numPositive ++; + + if( numNegative && numPositive ) + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- + +bool clipFrustumByPolygon( const Point3F* points, U32 numPoints, const RectI& viewport, const MatrixF& world, + const MatrixF& projection, const Frustum& inFrustum, const Frustum& rootFrustum, Frustum& outFrustum ) +{ + enum + { + MAX_RESULT_VERTICES = 64, + MAX_INPUT_VERTICES = MAX_RESULT_VERTICES - Frustum::PlaneCount // Clipping against each plane may add a vertex. + }; + + AssertFatal( numPoints <= MAX_INPUT_VERTICES, "MathUtils::clipFrustumByPolygon - Too many vertices!" ); + if( numPoints > MAX_INPUT_VERTICES ) + return false; + + // First, we need to clip the polygon against inFrustum. + // + // Use two buffers here in interchanging roles as sources and targets + // in clipping against the frustum planes. + + Point3F polygonBuffer1[ MAX_RESULT_VERTICES ]; + Point3F polygonBuffer2[ MAX_RESULT_VERTICES ]; + + Point3F* tempPolygon = polygonBuffer1; + Point3F* clippedPolygon = polygonBuffer2; + + dMemcpy( clippedPolygon, points, numPoints * sizeof( points[ 0 ] ) ); + + U32 numClippedPolygonVertices = numPoints; + U32 numTempPolygonVertices = 0; + + for( U32 nplane = 0; nplane < Frustum::PlaneCount; ++ nplane ) + { + // Make the output of the last iteration the + // input of this iteration. + + swap( tempPolygon, clippedPolygon ); + numTempPolygonVertices = numClippedPolygonVertices; + + // Clip our current remainder of the original polygon + // against the current plane. + + const PlaneF& plane = inFrustum.getPlanes()[ nplane ]; + numClippedPolygonVertices = plane.clipPolygon( tempPolygon, numTempPolygonVertices, clippedPolygon ); + + // If the polygon was completely on the backside of the plane, + // then polygon is outside the frustum. In this case, return false + // to indicate we haven't clipped anything. + + if( !numClippedPolygonVertices ) + return false; + } + + // Project the clipped polygon into screen space. + + MatrixF worldProjection = projection; + worldProjection.mul( world ); // Premultiply world*projection so we don't have to do this over and over for each point. + + Point3F projectedPolygon[ 10 ]; + for( U32 i = 0; i < numClippedPolygonVertices; ++ i ) + mProjectWorldToScreen( + clippedPolygon[ i ], + &projectedPolygon[ i ], + viewport, + worldProjection + ); + + // Put an axis-aligned rectangle around our polygon. + + Point2F minPoint( projectedPolygon[ 0 ].x, projectedPolygon[ 0 ].y ); + Point2F maxPoint( projectedPolygon[ 0 ].x, projectedPolygon[ 0 ].y ); + + for( U32 i = 1; i < numClippedPolygonVertices; ++ i ) + { + minPoint.setMin( Point2F( projectedPolygon[ i ].x, projectedPolygon[ i ].y ) ); + maxPoint.setMax( Point2F( projectedPolygon[ i ].x, projectedPolygon[ i ].y ) ); + } + + RectF area( minPoint, maxPoint - minPoint ); + + // Finally, reduce the input frustum to the given area. Note that we + // use rootFrustum here instead of inFrustum as the latter does not necessarily + // represent the full viewport we are using here which thus would skew the mapping. + + return reduceFrustum( rootFrustum, viewport, area, outFrustum ); +} + +//----------------------------------------------------------------------------- + +U32 extrudePolygonEdges( const Point3F* vertices, U32 numVertices, const Point3F& direction, PlaneF* outPlanes ) +{ + U32 numPlanes = 0; + U32 lastVertex = numVertices - 1; + bool invert = false; + + for( U32 i = 0; i < numVertices; lastVertex = i, ++ i ) + { + const Point3F& v1 = vertices[ i ]; + const Point3F& v2 = vertices[ lastVertex ]; + + // Skip the edge if it's length is really short. + + const Point3F edgeVector = v2 - v1; + if( edgeVector.len() < 0.05 ) + continue; + + // Compute the plane normal. The direction and the edge vector + // basically define the orientation of the plane so their cross + // product is the plane normal. + + Point3F normal; + if( !invert ) + normal = mCross( edgeVector, direction ); + else + normal = mCross( direction, edgeVector ); + + // Create a plane for the edge. + + outPlanes[ numPlanes ] = PlaneF( v1, normal ); + numPlanes ++; + + // If this is the first plane that we have created, find out whether + // the vertex ordering is giving us the plane orientations that we want + // (facing inside). If not, invert vertex order from now on. + + if( i == 0 ) + { + const PlaneF& plane = outPlanes[ numPlanes - 1 ]; + for( U32 n = i + 1; n < numVertices; ++ n ) + { + const PlaneF::Side side = plane.whichSide( vertices[ n ] ); + if( side == PlaneF::On ) + continue; + + if( side != PlaneF::Front ) + invert = true; + break; + } + } + } + + return numPlanes; +} + +//----------------------------------------------------------------------------- + +U32 extrudePolygonEdgesFromPoint( const Point3F* vertices, U32 numVertices, const Point3F& fromPoint, PlaneF* outPlanes ) +{ + U32 numPlanes = 0; + U32 lastVertex = numVertices - 1; + bool invert = false; + + for( U32 i = 0; i < numVertices; lastVertex = i, ++ i ) + { + const Point3F& v1 = vertices[ i ]; + const Point3F& v2 = vertices[ lastVertex ]; + + // Skip the edge if it's length is really short. + + const Point3F edgeVector = v2 - v1; + if( edgeVector.len() < 0.05 ) + continue; + + // Create a plane for the edge. + + if( !invert ) + outPlanes[ numPlanes ] = PlaneF( v1, fromPoint, v2 ); + else + outPlanes[ numPlanes ] = PlaneF( v2, fromPoint, v1 ); + + numPlanes ++; + + // If this is the first plane that we have created, find out whether + // the vertex ordering is giving us the plane orientations that we want + // (facing inside). If not, invert vertex order from now on. + + if( i == 0 ) + { + const PlaneF& plane = outPlanes[ numPlanes - 1 ]; + for( U32 n = i + 1; n < numVertices; ++ n ) + { + const PlaneF::Side side = plane.whichSide( vertices[ n ] ); + if( side == PlaneF::On ) + continue; + + if( side != PlaneF::Front ) + invert = true; + break; + } + } + } + + return numPlanes; +} + +} // namespace MathUtils diff --git a/Engine/source/math/mathUtils.h b/Engine/source/math/mathUtils.h new file mode 100644 index 000000000..2d708aaa2 --- /dev/null +++ b/Engine/source/math/mathUtils.h @@ -0,0 +1,411 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MATHUTILS_H_ +#define _MATHUTILS_H_ + +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif + +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif + +#ifndef _MRECT_H_ +#include "math/mRect.h" +#endif + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + + +class Box3F; +class RectI; +class Frustum; + + +/// Miscellaneous math utility functions. +namespace MathUtils +{ + /// A simple helper struct to define a line. + struct Line + { + Point3F origin; + VectorF direction; + }; + + /// A ray is also a line. + typedef Line Ray; + + /// A simple helper struct to define a line segment. + struct LineSegment + { + Point3F p0; + Point3F p1; + }; + + /// A simple helper struct to define a clockwise + /// winding quad. + struct Quad + { + Point3F p00; + Point3F p01; + Point3F p10; + Point3F p11; + }; + + /// Used by mTriangleDistance() to pass along collision info + struct IntersectInfo + { + LineSegment segment; // Starts at given point, ends at collision + Point3F bary; // Barycentric coords for collision + }; + + /// Rotate the passed vector around the world-z axis by the passed radians. + void vectorRotateZAxis( Point3F &vector, F32 radians ); + void vectorRotateZAxis( F32 radians, Point3F *vectors, U32 count ); + + /// Generates a projection matrix with the near plane + /// moved forward by the bias amount. This function is a helper primarily + /// for working around z-fighting issues. + /// + /// @param bias The amount to move the near plane forward. + /// @param frustum The frustum to generate the new projection matrix from. + /// @param outMat The resulting z-biased projection matrix. Note: It must be initialized before the call. + /// @param rotate Optional parameter specifying whether to rotate the projection matrix similarly to GFXDevice. + /// + void getZBiasProjectionMatrix( F32 bias, const Frustum &frustum, MatrixF *outMat, bool rotate = true ); + + /// Creates orientation matrix from a direction vector. Assumes ( 0 0 1 ) is up. + MatrixF createOrientFromDir( const Point3F &direction ); + + /// Creates an orthonormal basis matrix with the unit length + /// input vector in column 2 (up vector). + /// + /// @param up The non-zero unit length up vector. + /// @param outMat The output matrix which must be initialized prior to the call. + /// + void getMatrixFromUpVector( const VectorF &up, MatrixF *outMat ); + + /// Creates an orthonormal basis matrix with the unit length + /// input vector in column 1 (forward vector). + /// + /// @param forward The non-zero unit length forward vector. + /// @param outMat The output matrix which must be initialized prior to the call. + /// + void getMatrixFromForwardVector( const VectorF &forward, MatrixF *outMat ); + + /// Creates random direction given angle parameters similar to the particle system. + /// + /// The angles are relative to the specified axis. Both phi and theta are in degrees. + Point3F randomDir( const Point3F &axis, F32 thetaAngleMin, F32 thetaAngleMax, F32 phiAngleMin = 0.0, F32 phiAngleMax = 360.0 ); + + /// Returns a random 3D point within a sphere of the specified radius + /// centered at the origin. + Point3F randomPointInSphere( F32 radius ); + + /// Returns a random 2D point within a circle of the specified radius + /// centered at the origin. + Point2F randomPointInCircle( F32 radius ); + + /// Returns yaw and pitch angles from a given vector. + /// + /// Angles are in RADIANS. + /// + /// Assumes north is (0.0, 1.0, 0.0), the degrees move upwards clockwise. + /// + /// The range of yaw is 0 - 2PI. The range of pitch is -PI/2 - PI/2. + /// + /// ASSUMES Z AXIS IS UP + void getAnglesFromVector( const VectorF &vec, F32 &yawAng, F32 &pitchAng ); + + /// Returns vector from given yaw and pitch angles. + /// + /// Angles are in RADIANS. + /// + /// Assumes north is (0.0, 1.0, 0.0), the degrees move upwards clockwise. + /// + /// The range of yaw is 0 - 2PI. The range of pitch is -PI/2 - PI/2. + /// + /// ASSUMES Z AXIS IS UP + void getVectorFromAngles( VectorF &vec, F32 yawAng, F32 pitchAng ); + + /// Simple reflection equation - pass in a vector and a normal to reflect off of + inline Point3F reflect( Point3F &inVec, Point3F &norm ) + { + return inVec - norm * ( mDot( inVec, norm ) * 2.0f ); + } + + /// Collide two capsules (sphere swept lines) against each other, reporting only if they intersect or not. + /// Based on routine from "Real Time Collision Detection" by Christer Ericson pp 114. + bool capsuleCapsuleOverlap(const Point3F & a1, const Point3F & b1, F32 radius1, const Point3F & a2, const Point3F & b2, F32 radius2); + + /// Return capsule-sphere overlap. Returns time of first overlap, where time + /// is viewed as a sphere of radius radA moving from point A0 to A1. + bool capsuleSphereNearestOverlap(const Point3F & A0, const Point3F A1, F32 radA, const Point3F & B, F32 radB, F32 & t); + + /// Intersect two line segments (p1,q1) and (p2,q2), returning points on lines (c1 & c2) and line parameters (s,t). + /// Based on routine from "Real Time Collision Detection" by Christer Ericson pp 149. + F32 segmentSegmentNearest(const Point3F & p1, const Point3F & q1, const Point3F & p2, const Point3F & q2, F32 & s, F32 & t, Point3F & c1, Point3F & c2); + + /// Transform bounding box making sure to keep original box entirely contained. + void transformBoundingBox(const Box3F &sbox, const MatrixF &mat, const Point3F scale, Box3F &dbox); + + bool mProjectWorldToScreen( const Point3F &in, + Point3F *out, + const RectI &view, + const MatrixF &world, + const MatrixF &projection ); + bool mProjectWorldToScreen( const Point3F &in, + Point3F *out, + const RectI &view, + const MatrixF &worldProjection ); + + void mProjectScreenToWorld( const Point3F &in, + Point3F *out, + const RectI &view, + const MatrixF &world, + const MatrixF &projection, + F32 far, + F32 near); + + /// Clip @a inFrustum by the given polygon. + /// + /// @note The input polygon is limited to 58 vertices. + /// + /// @param points Polygon vertices. + /// @param numPoints Number of vertices in @a points. + /// @param viewport Screen viewport. Note that this corresponds to the root frustum and not necessarily to @a inFrustum. + /// @param world World->view transform. + /// @param projection Projection matrix. + /// @param inFrustum Frustum to clip. + /// @param rootFrustum Frustum corresponding to @a viewport. + /// @param outFrustum Resulting clipped frustum. + /// + /// @return True if the frustum was successfully clipped and @a outFrustum is valid, false otherwise + /// (if, for example, the input polygon is completely outside @a inFrustum). + bool clipFrustumByPolygon( const Point3F* points, + U32 numPoints, + const RectI& viewport, + const MatrixF& world, + const MatrixF& projection, + const Frustum& inFrustum, + const Frustum& rootFrustum, + Frustum& outFrustum ); + + /// Returns true if the test point is within the polygon. + /// @param verts The array of points which forms the polygon. + /// @param vertCount The number of points in the polygon. + /// @param testPt The point to test. + bool pointInPolygon( const Point2F *verts, U32 vertCount, const Point2F &testPt ); + + /// Remove all edges from the given polygon that have a total length shorter + /// than @a epsilon. + /// + U32 removeShortPolygonEdges( const Point3F* verts, U32 vertCount, F32 epsilon ); + + /// Calculates the shortest line segment between two lines. + /// + /// @param outSegment The result where .p0 is the point on line0 and .p1 is the point on line1. + /// + void mShortestSegmentBetweenLines( const Line &line0, const Line &line1, LineSegment *outSegment ); + + /// Returns the greatest common divisor of two positive integers. + U32 greatestCommonDivisor( U32 u, U32 v ); + + /// Returns the barycentric coordinates and time of intersection between + /// a line segment and a triangle. + /// + /// @param p1 The first point of the line segment. + /// @param p2 The second point of the line segment. + /// @param t1 The first point of the triangle. + /// @param t2 The second point of the triangle. + /// @param t2 The third point of the triangle. + /// @param outUVW The optional output barycentric coords. + /// @param outT The optional output time of intersection. + /// + /// @return Returns true if a collision occurs. + /// + bool mLineTriangleCollide( const Point3F &p1, const Point3F &p2, + const Point3F &t1, const Point3F &t2, const Point3F &t3, + Point3F *outUVW = NULL, + F32 *outT = NULL ); + + /// Returns the uv coords and time of intersection between + /// a ray and a quad. + /// + /// @param quad The quad. + /// @param ray The ray. + /// @param outUV The optional output UV coords of the intersection. + /// @param outT The optional output time of intersection. + /// + /// @return Returns true if a collision occurs. + /// + bool mRayQuadCollide( const Quad &quad, + const Ray &ray, + Point2F *outUV = NULL, + F32 *outT = NULL ); + + /// Returns the distance between a point and triangle 'abc'. + F32 mTriangleDistance( const Point3F &a, const Point3F &b, const Point3F &c, const Point3F &p, IntersectInfo* info=NULL ); + + /// Returns the normal of the passed triangle 'abc'. + /// + /// If we assume counter-clockwise triangle culling, normal will point + /// out from the 'solid' side of the triangle. + /// + Point3F mTriangleNormal( const Point3F &a, const Point3F &b, const Point3F &c ); + + /// Returns the closest point on the segment defined by + /// points a, b to the point p. + Point3F mClosestPointOnSegment( const Point3F &a, + const Point3F &b, + const Point3F &p ); + + /// Sort the passed verts ( Point3F ) in a clockwise or counter-clockwise winding order. + /// Verts must be co-planar and non-collinear. + /// + /// @param quadMat Transform matrix from vert space to quad space. + /// @param clockwise Sort clockwise or counter-clockwise + /// @param verts Array of Point3F verts. + /// @param vertMap Output - Array of vert element ids sorted by winding order. + /// @param count Element count of the verts and vertMap arrays which must be allocated prior to this call. + /// + void sortQuadWindingOrder( const MatrixF &quadMat, bool clockwise, const Point3F *verts, U32 *vertMap, U32 count ); + + /// Same as above except we assume that the passed verts ( Point3F ) are already + /// transformed into 'quad space'. If this was done correctly and the points + /// are coplanar this means their z components will all be zero. + void sortQuadWindingOrder( bool clockwise, const Point3F *verts, U32 *vertMap, U32 count ); + + /// + /// WORK IN PROGRESS + /// + /// Creates an orthonormal basis matrix from one, two, or three unit length + /// input vectors. If more than one input vector is provided they must be + /// mutually perpendicular. + /// + /// @param rvec Optional unit length right vector. + /// @param fvec Optional unit length forward vector. + /// @param uvec Optional unit length up vector. + /// @param pos Optional position to initialize the matrix. + /// @param outMat The output matrix which must be initialized prior to the call. + /// + void buildMatrix( const VectorF *rvec, const VectorF *fvec, const VectorF *uvec, const VectorF *pos, MatrixF *outMat ); + + /// + bool reduceFrustum( const Frustum& frustum, const RectI& viewport, const RectF& area, Frustum& outFrustum ); + + /// Build the frustum near plane dimensions from the parameters. + void makeFrustum( F32 *outLeft, + F32 *outRight, + F32 *outTop, + F32 *outBottom, + F32 fovYInRadians, + F32 aspectRatio, + F32 nearPlane ); + + /// Build a GFX projection matrix from the frustum parameters + /// including the optional rotation required by GFX. + void makeProjection( MatrixF *outMatrix, + F32 fovYInRadians, + F32 aspectRatio, + F32 nearPlane, + F32 farPlane, + bool gfxRotate ); + + /// Build a projection matrix from the frustum near plane dimensions + /// including the optional rotation required by GFX. + void makeProjection( MatrixF *outMatrix, + F32 left, + F32 right, + F32 top, + F32 bottom, + F32 nearPlane, + F32 farPlane, + bool gfxRotate ); + + /// Build an orthographic projection matrix from the frustum near + /// plane dimensions including the optional rotation required by GFX. + void makeOrthoProjection( MatrixF *outMatrix, + F32 left, + F32 right, + F32 top, + F32 bottom, + F32 nearPlane, + F32 farPlane, + bool gfxRotate ); + + /// Find the intersection of the line going from @a edgeA to @a edgeB with the triangle + /// given by @a faceA, @a faceB, and @a faceC. + /// @param edgeA Starting point of edge. + /// @param edgeB End point of edge. + /// @param faceA First vertex of triangle. + /// @param faceB Second vertex of triangle. + /// @param faceC Third vertex of triangle. + /// @param intersection If there is an intersection, the point of intersection on the triangle's + /// face is stored here. + /// @param True if there is an intersection, false otherwise. + bool edgeFaceIntersect( const Point3F &edgeA, const Point3F &edgeB, + const Point3F &faceA, const Point3F &faceB, const Point3F &faceC, const Point3F &faceD, Point3F *intersection ); + + /// Find out whether the given polygon is planar. + /// @param vertices Array of vertices of the polygon. + /// @param numVertices Number of vertices in @a vertices. + /// @return True if the polygon is planar, false otherwise. + bool isPlanarPolygon( const Point3F* vertices, U32 numVertices ); + + /// Find out whether the given polygon is convex. + /// @param vertices Array of vertices of the polygon. + /// @param numVertices Number of vertices in @a vertices. + /// @return True if the polygon is convex, false otherwise. + bool isConvexPolygon( const Point3F* vertices, U32 numVertices ); + + /// Extrude the given polygon along the given direction. + U32 extrudePolygonEdges( const Point3F* vertices, U32 numVertices, const Point3F& direction, PlaneF* outPlanes ); + + /// Extrude the edges of the given polygon away from @a fromPoint by constructing a set of planes + /// that each go through @a fromPoint and a pair of vertices. + /// + /// The resulting planes are in the same order as the vertices and have their normals facing *inwards*, + /// i.e. the resulting volume will enclose the polygon's interior space. + /// + /// @param vertices Vertices of the polygon in CCW or CW order (both are acceptable). + /// @param numVertices Number of vertices in @a vertices. + /// @param fromPoint + /// @param outPlanes Array in which the resulting planes are stored. Must have room for at least as many + /// planes are there are edges in the polygon. + /// + /// @return + /// + /// @note The input polygon does not necessarily need to be planar but it must be convex. + U32 extrudePolygonEdgesFromPoint( const Point3F* vertices, U32 numVertices, + const Point3F& fromPoint, + PlaneF* outPlanes ); + + //void findFarthestPoint( const Point3F* points, U32 numPoints, const Point3F& fromPoint, ) + +} // namespace MathUtils + +#endif // _MATHUTILS_H_ diff --git a/Engine/source/math/test/testMathPlane.cpp b/Engine/source/math/test/testMathPlane.cpp new file mode 100644 index 000000000..6600c235e --- /dev/null +++ b/Engine/source/math/test/testMathPlane.cpp @@ -0,0 +1,79 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "unit/test.h" +#include "math/mPlane.h" +#include "math/mRandom.h" + + +#ifndef TORQUE_SHIPPING + +using namespace UnitTesting; + +#define TEST( x ) test( ( x ), "FAIL: " #x ) +#define XTEST( t, x ) t->test( ( x ), "FAIL: " #x ) + +CreateUnitTest( TestMathPlane, "Math/Plane" ) +{ + static F32 randF() + { + return gRandGen.randF( -1.f, 1.f ); + } + + void test_whichSide() + { + for( U32 i = 0; i < 100; ++ i ) + { + Point3F position( randF(), randF(), randF() ); + Point3F normal( randF(), randF(), randF() ); + + PlaneF p1( position, normal ); + + TEST( p1.whichSide( position + normal ) == PlaneF::Front ); + TEST( p1.whichSide( position + ( - normal ) ) == PlaneF::Back ); + TEST( p1.whichSide( position ) == PlaneF::On ); + } + } + + void test_distToPlane() + { + for( U32 i = 0; i < 100; ++ i ) + { + Point3F position( randF(), randF(), randF() ); + Point3F normal( randF(), randF(), randF() ); + + PlaneF p1( position, normal ); + + TEST( mIsEqual( p1.distToPlane( position + normal ), normal.len() ) ); + TEST( mIsEqual( p1.distToPlane( position + ( - normal ) ), - normal.len() ) ); + TEST( mIsZero( p1.distToPlane( position ) ) ); + } + } + + void run() + { + test_whichSide(); + test_distToPlane(); + } +}; + +#endif // !TORQUE_SHIPPING diff --git a/Engine/source/math/test/testPolyhedron.cpp b/Engine/source/math/test/testPolyhedron.cpp new file mode 100644 index 000000000..2bfe9b13f --- /dev/null +++ b/Engine/source/math/test/testPolyhedron.cpp @@ -0,0 +1,104 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "unit/test.h" +#include "math/mPolyhedron.h" + + +#ifndef TORQUE_SHIPPING + +using namespace UnitTesting; + +#define TEST( x ) test( ( x ), "FAIL: " #x ) +#define XTEST( t, x ) t->test( ( x ), "FAIL: " #x ) + + +CreateUnitTest( TestMathPolyhedronBuildFromPlanes, "Math/Polyhedron/BuildFromPlanes" ) +{ + void test_unitCube() + { + Vector< PlaneF > planes; + + // Build planes for a unit cube centered at the origin. + // Note that the normals must be facing inwards. + + planes.push_back( PlaneF( Point3F( -0.5f, 0.f, 0.f ), Point3F( 1.f, 0.f, 0.f ) ) ); + planes.push_back( PlaneF( Point3F( 0.5f, 0.f, 0.f ), Point3F( -1.f, 0.f, 0.f ) ) ); + planes.push_back( PlaneF( Point3F( 0.f, -0.5f, 0.f ), Point3F( 0.f, 1.f, 0.f ) ) ); + planes.push_back( PlaneF( Point3F( 0.f, 0.5f, 0.f ), Point3F( 0.f, -1.f, 0.f ) ) ); + planes.push_back( PlaneF( Point3F( 0.f, 0.f, -0.5f ), Point3F( 0.f, 0.f, 1.f ) ) ); + planes.push_back( PlaneF( Point3F( 0.f, 0.f, 0.5f ), Point3F( 0.f, 0.f, -1.f ) ) ); + + // Turn it into a polyhedron. + + Polyhedron polyhedron; + polyhedron.buildFromPlanes( + PlaneSetF( planes.address(), planes.size() ) + ); + + // Check if we got a cube back. + + TEST( polyhedron.getNumPoints() == 8 ); + TEST( polyhedron.getNumPlanes() == 6 ); + TEST( polyhedron.getNumEdges() == 12 ); + } + + void test_extraPlane() + { + Vector< PlaneF > planes; + + // Build planes for a unit cube centered at the origin. + // Note that the normals must be facing inwards. + + planes.push_back( PlaneF( Point3F( -0.5f, 0.f, 0.f ), Point3F( 1.f, 0.f, 0.f ) ) ); + planes.push_back( PlaneF( Point3F( 0.5f, 0.f, 0.f ), Point3F( -1.f, 0.f, 0.f ) ) ); + planes.push_back( PlaneF( Point3F( 0.f, -0.5f, 0.f ), Point3F( 0.f, 1.f, 0.f ) ) ); + planes.push_back( PlaneF( Point3F( 0.f, 0.5f, 0.f ), Point3F( 0.f, -1.f, 0.f ) ) ); + planes.push_back( PlaneF( Point3F( 0.f, 0.f, -0.5f ), Point3F( 0.f, 0.f, 1.f ) ) ); + planes.push_back( PlaneF( Point3F( 0.f, 0.f, 0.5f ), Point3F( 0.f, 0.f, -1.f ) ) ); + + // Add extra plane that doesn't contribute a new edge. + + planes.push_back( PlaneF( Point3F( 0.5f, 0.5f, 0.5f ), Point3F( -1.f, -1.f, -1.f ) ) ); + + // Turn it into a polyhedron. + + Polyhedron polyhedron; + polyhedron.buildFromPlanes( + PlaneSetF( planes.address(), planes.size() ) + ); + + // Check if we got a cube back. + + TEST( polyhedron.getNumPoints() == 8 ); + TEST( polyhedron.getNumPlanes() == 6 ); + TEST( polyhedron.getNumEdges() == 12 ); + } + + void run() + { + test_unitCube(); + //test_extraPlane(); + } +}; + +#endif // !TORQUE_SHIPPING diff --git a/Engine/source/math/util/frustum.cpp b/Engine/source/math/util/frustum.cpp new file mode 100644 index 000000000..e5c6fd43b --- /dev/null +++ b/Engine/source/math/util/frustum.cpp @@ -0,0 +1,475 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "math/util/frustum.h" + +#include "math/mMathFn.h" +#include "math/mathUtils.h" +#include "math/mSphere.h" +#include "platform/profiler.h" + + +//TODO: For OBB/frustum intersections and ortho frustums, we can resort to a much quicker AABB/OBB test + + +// Must be CW ordered for face[0] of each edge! Keep in mind that normals +// are pointing *inwards* and thus what appears CCW outside is CW inside. +FrustumData::EdgeListType FrustumData::smEdges +( + PolyhedronData::Edge( PlaneNear, PlaneTop, NearTopRight, NearTopLeft ), + PolyhedronData::Edge( PlaneNear, PlaneBottom, NearBottomLeft, NearBottomRight ), + PolyhedronData::Edge( PlaneNear, PlaneLeft, NearTopLeft, NearBottomLeft ), + PolyhedronData::Edge( PlaneNear, PlaneRight, NearTopRight, NearBottomRight ), + PolyhedronData::Edge( PlaneFar, PlaneTop, FarTopLeft, FarTopRight ), + PolyhedronData::Edge( PlaneFar, PlaneBottom, FarBottomRight, FarBottomLeft ), + PolyhedronData::Edge( PlaneFar, PlaneLeft, FarBottomLeft, FarTopLeft ), + PolyhedronData::Edge( PlaneFar, PlaneRight, FarTopRight, FarBottomRight ), + PolyhedronData::Edge( PlaneTop, PlaneLeft, FarTopLeft, NearTopLeft ), + PolyhedronData::Edge( PlaneTop, PlaneRight, NearTopRight, FarTopRight ), + PolyhedronData::Edge( PlaneBottom, PlaneLeft, NearBottomLeft, FarBottomLeft ), + PolyhedronData::Edge( PlaneBottom, PlaneRight, FarBottomRight, NearBottomRight ) +); + + +//----------------------------------------------------------------------------- + +Frustum::Frustum( bool isOrtho, + F32 nearLeft, + F32 nearRight, + F32 nearTop, + F32 nearBottom, + F32 nearDist, + F32 farDist, + const MatrixF &transform ) +{ + mTransform = transform; + mPosition = transform.getPosition(); + + mNearLeft = nearLeft; + mNearRight = nearRight; + mNearTop = nearTop; + mNearBottom = nearBottom; + mNearDist = nearDist; + mFarDist = farDist; + mIsOrtho = isOrtho; + + mNumTiles = 1; + mCurrTile.set(0,0); + mTileOverlap.set(0.0f, 0.0f); +} + +//----------------------------------------------------------------------------- + +void Frustum::set( bool isOrtho, + F32 fovYInRadians, + F32 aspectRatio, + F32 nearDist, + F32 farDist, + const MatrixF &transform ) +{ + F32 left, right, top, bottom; + MathUtils::makeFrustum( &left, &right, &top, &bottom, fovYInRadians, aspectRatio, nearDist ); + + tile( &left, &right, &top, &bottom, mNumTiles, mCurrTile, mTileOverlap ); + set( isOrtho, left, right, top, bottom, nearDist, farDist, transform ); +} + +//----------------------------------------------------------------------------- + +void Frustum::set( bool isOrtho, + F32 nearLeft, + F32 nearRight, + F32 nearTop, + F32 nearBottom, + F32 nearDist, + F32 farDist, + const MatrixF &transform ) +{ + mTransform = transform; + mPosition = mTransform.getPosition(); + + mNearLeft = nearLeft; + mNearRight = nearRight; + mNearTop = nearTop; + mNearBottom = nearBottom; + mNearDist = nearDist; + mFarDist = farDist; + mIsOrtho = isOrtho; + + mDirty = true; +} + +//----------------------------------------------------------------------------- + +#if 0 +void Frustum::set( const MatrixF &projMat, bool normalize ) +{ + // From "Fast Extraction of Viewing Frustum Planes from the World-View-Projection Matrix" + // by Gil Gribb and Klaus Hartmann. + // + // http://www2.ravensoft.com/users/ggribb/plane%20extraction.pdf + + // Right clipping plane. + mPlanes[ PlaneRight ].set( projMat[3] - projMat[0], + projMat[7] - projMat[4], + projMat[11] - projMat[8], + projMat[15] - projMat[12] ); + + // Left clipping plane. + mPlanes[ PlaneLeft ].set( projMat[3] + projMat[0], + projMat[7] + projMat[4], + projMat[11] + projMat[8], + projMat[15] + projMat[12] ); + + // Bottom clipping plane. + mPlanes[ PlaneBottom ].set( projMat[3] + projMat[1], + projMat[7] + projMat[5], + projMat[11] + projMat[9], + projMat[15] + projMat[13] ); + + // Top clipping plane. + mPlanes[ PlaneTop ].set( projMat[3] - projMat[1], + projMat[7] - projMat[5], + projMat[11] - projMat[9], + projMat[15] - projMat[13] ); + + // Near clipping plane + mPlanes[ PlaneNear ].set( projMat[3] + projMat[2], + projMat[7] + projMat[6], + projMat[11] + projMat[10], + projMat[15] + projMat[14] ); + + // Far clipping plane. + mPlanes[ PlaneFar ].set( projMat[3] - projMat[2], + projMat[7] - projMat[6], + projMat[11] - projMat[10], + projMat[15] - projMat[14] ); + + if( normalize ) + { + for( S32 i = 0; i < PlaneCount; ++ i ) + mPlanes[ i ].normalize(); + } + + /* // Create the corner points via plane intersections. + mPlanes[ PlaneNear ].intersect( mPlanes[ PlaneTop ], mPlanes[ PlaneLeft ], &mPoints[ NearTopLeft ] ); + mPlanes[ PlaneNear ].intersect( mPlanes[ PlaneTop ], mPlanes[ PlaneRight ], &mPoints[ NearTopRight ] ); + mPlanes[ PlaneNear ].intersect( mPlanes[ PlaneBottom ], mPlanes[ PlaneLeft ], &mPoints[ NearBottomLeft ] ); + mPlanes[ PlaneNear ].intersect( mPlanes[ PlaneBottom ], mPlanes[ PlaneRight ], &mPoints[ NearBottomRight ] ); + mPlanes[ PlaneFar ].intersect( mPlanes[ PlaneTop ], mPlanes[ PlaneLeft ], &mPoints[ FarTopLeft ] ); + mPlanes[ PlaneFar ].intersect( mPlanes[ PlaneTop ], mPlanes[ PlaneRight ], &mPoints[ FarTopRight ] ); + mPlanes[ PlaneFar ].intersect( mPlanes[ PlaneBottom ], mPlanes[ PlaneLeft ], &mPoints[ FarBottomLeft ] ); + mPlanes[ PlaneFar ].intersect( mPlanes[ PlaneBottom ], mPlanes[ PlaneRight ], &mPoints[ FarBottomRight ] ); + */ + + // Update the axis aligned bounding box. + _updateBounds(); +} +#endif + +//----------------------------------------------------------------------------- + +void Frustum::setNearDist( F32 nearDist ) +{ + setNearFarDist( nearDist, mFarDist ); +} + +//----------------------------------------------------------------------------- + +void Frustum::setFarDist( F32 farDist ) +{ + setNearFarDist( mNearDist, farDist ); +} + +//----------------------------------------------------------------------------- + +void Frustum::setNearFarDist( F32 nearDist, F32 farDist ) +{ + if( mNearDist == nearDist && mFarDist == farDist ) + return; + + // Recalculate the frustum. + MatrixF xfm( mTransform ); + set( mIsOrtho, getFov(), getAspectRatio(), nearDist, farDist, xfm ); +} + +//----------------------------------------------------------------------------- + +void Frustum::cropNearFar(F32 newNearDist, F32 newFarDist) +{ + const F32 newOverOld = newNearDist / mNearDist; + + set( mIsOrtho, mNearLeft * newOverOld, mNearRight * newOverOld, mNearTop * newOverOld, mNearBottom * newOverOld, + newNearDist, newFarDist, mTransform); +} + +//----------------------------------------------------------------------------- + +void FrustumData::_update() const +{ + if( !mDirty ) + return; + + PROFILE_SCOPE( Frustum_update ); + + const Point3F& cameraPos = mPosition; + + // Build the frustum points in camera space first. + + if( mIsOrtho ) + { + mPoints[ NearTopLeft ].set( mNearLeft, mNearDist, mNearTop ); + mPoints[ NearTopRight ].set( mNearRight, mNearDist, mNearTop ); + mPoints[ NearBottomLeft ].set( mNearLeft, mNearDist, mNearBottom ); + mPoints[ NearBottomRight ].set( mNearRight, mNearDist, mNearBottom ); + mPoints[ FarTopLeft ].set( mNearLeft, mFarDist, mNearTop ); + mPoints[ FarTopRight ].set( mNearRight, mFarDist, mNearTop ); + mPoints[ FarBottomLeft ].set( mNearLeft, mFarDist, mNearBottom ); + mPoints[ FarBottomRight ].set( mNearRight, mFarDist, mNearBottom ); + } + else + { + const F32 farOverNear = mFarDist / mNearDist; + + mPoints[ NearTopLeft ].set( mNearLeft, mNearDist, mNearTop ); + mPoints[ NearTopRight ].set( mNearRight, mNearDist, mNearTop ); + mPoints[ NearBottomLeft ].set( mNearLeft, mNearDist, mNearBottom ); + mPoints[ NearBottomRight ].set( mNearRight, mNearDist, mNearBottom ); + mPoints[ FarTopLeft ].set( mNearLeft * farOverNear, mFarDist, mNearTop * farOverNear ); + mPoints[ FarTopRight ].set( mNearRight * farOverNear, mFarDist, mNearTop * farOverNear ); + mPoints[ FarBottomLeft ].set( mNearLeft * farOverNear, mFarDist, mNearBottom * farOverNear ); + mPoints[ FarBottomRight ].set( mNearRight * farOverNear, mFarDist, mNearBottom * farOverNear ); + } + + // Transform the points into the desired culling space. + + for( U32 i = 0; i < mPoints.size(); ++ i ) + mTransform.mulP( mPoints[ i ] ); + + // Update the axis aligned bounding box from + // the newly transformed points. + + mBounds = Box3F::aroundPoints( mPoints.address(), mPoints.size() ); + + // Finally build the planes. + + if( mIsOrtho ) + { + mPlanes[ PlaneLeft ].set( mPoints[ NearBottomLeft ], + mPoints[ FarTopLeft ], + mPoints[ FarBottomLeft ] ); + + mPlanes[ PlaneRight ].set( mPoints[ NearTopRight ], + mPoints[ FarBottomRight ], + mPoints[ FarTopRight ] ); + + mPlanes[ PlaneTop ].set( mPoints[ FarTopRight ], + mPoints[ NearTopLeft ], + mPoints[ NearTopRight ] ); + + mPlanes[ PlaneBottom ].set( mPoints[ NearBottomRight ], + mPoints[ FarBottomLeft ], + mPoints[ FarBottomRight ] ); + + mPlanes[ PlaneNear ].set( mPoints[ NearTopLeft ], + mPoints[ NearBottomLeft ], + mPoints[ NearTopRight ] ); + + mPlanes[ PlaneFar ].set( mPoints[ FarTopLeft ], + mPoints[ FarTopRight ], + mPoints[ FarBottomLeft ] ); + } + else + { + mPlanes[ PlaneLeft ].set( cameraPos, + mPoints[ NearTopLeft ], + mPoints[ NearBottomLeft ] ); + + mPlanes[ PlaneRight ].set( cameraPos, + mPoints[ NearBottomRight ], + mPoints[ NearTopRight ] ); + + mPlanes[ PlaneTop ].set( cameraPos, + mPoints[ NearTopRight ], + mPoints[ NearTopLeft ] ); + + mPlanes[ PlaneBottom ].set( cameraPos, + mPoints[ NearBottomLeft ], + mPoints[ NearBottomRight ] ); + + mPlanes[ PlaneNear ].set( mPoints[ NearTopLeft ], + mPoints[ NearBottomLeft ], + mPoints[ NearTopRight ] ); + + mPlanes[ PlaneFar ].set( mPoints[ FarTopLeft ], + mPoints[ FarTopRight ], + mPoints[ FarBottomLeft ] ); + } + + // If the frustum plane orientation doesn't match mIsInverted + // now, invert all the plane normals. + // + // Note that if we have a transform matrix with a negative scale, + // then the initial planes we have computed will always be inverted. + + const bool inverted = mPlanes[ PlaneNear ].whichSide( cameraPos ) == PlaneF::Front; + if( inverted != mIsInverted ) + { + for( U32 i = 0; i < mPlanes.size(); ++ i ) + mPlanes[ i ].invert(); + } + + AssertFatal( mPlanes[ PlaneNear ].whichSide( cameraPos ) != PlaneF::Front, + "Frustum::_update - Viewpoint lies on front side of near plane!" ); + + // And now the center points which are mostly just used in debug rendering. + + mPlaneCenters[ PlaneLeftCenter ] = ( mPoints[ NearTopLeft ] + + mPoints[ NearBottomLeft ] + + mPoints[ FarTopLeft ] + + mPoints[ FarBottomLeft ] ) / 4.0f; + + mPlaneCenters[ PlaneRightCenter ] = ( mPoints[ NearTopRight ] + + mPoints[ NearBottomRight ] + + mPoints[ FarTopRight ] + + mPoints[ FarBottomRight ] ) / 4.0f; + + mPlaneCenters[ PlaneTopCenter ] = ( mPoints[ NearTopLeft ] + + mPoints[ NearTopRight ] + + mPoints[ FarTopLeft ] + + mPoints[ FarTopRight ] ) / 4.0f; + + mPlaneCenters[ PlaneBottomCenter ] = ( mPoints[ NearBottomLeft ] + + mPoints[ NearBottomRight ] + + mPoints[ FarBottomLeft ] + + mPoints[ FarBottomRight ] ) / 4.0f; + + mPlaneCenters[ PlaneNearCenter ] = ( mPoints[ NearTopLeft ] + + mPoints[ NearTopRight ] + + mPoints[ NearBottomLeft ] + + mPoints[ NearBottomRight ] ) / 4.0f; + + mPlaneCenters[ PlaneFarCenter ] = ( mPoints[ FarTopLeft ] + + mPoints[ FarTopRight ] + + mPoints[ FarBottomLeft ] + + mPoints[ FarBottomRight ] ) / 4.0f; + + // Done. + + mDirty = false; +} + +//----------------------------------------------------------------------------- + +void Frustum::invert() +{ + mIsInverted = !mIsInverted; + _update(); +} + +//----------------------------------------------------------------------------- + +void Frustum::setTransform( const MatrixF &mat ) +{ + mTransform = mat; + mPosition = mTransform.getPosition(); + mDirty = true; +} + +//----------------------------------------------------------------------------- + +void Frustum::scaleFromCenter( F32 scale ) +{ + // Extract the fov and aspect ratio. + F32 fovInRadians = mAtan2( (mNearTop - mNearBottom)*mNumTiles/2.0f, mNearDist ) * 2.0f; + F32 aspectRatio = (mNearRight - mNearLeft)/(mNearTop - mNearBottom); + + // Now move the near and far planes out. + F32 halfDist = ( mFarDist - mNearDist ) / 2.0f; + mNearDist -= halfDist * ( scale - 1.0f ); + mFarDist += halfDist * ( scale - 1.0f ); + + // Setup the new scaled frustum. + set( mIsOrtho, fovInRadians, aspectRatio, mNearDist, mFarDist, mTransform ); +} + +//----------------------------------------------------------------------------- + +void Frustum::mul( const MatrixF& mat ) +{ + mTransform.mul( mat ); + mDirty = true; +} + +//----------------------------------------------------------------------------- + +void Frustum::mulL( const MatrixF& mat ) +{ + MatrixF last( mTransform ); + mTransform.mul( mat, last ); + + mDirty = true; +} + +//----------------------------------------------------------------------------- + +void Frustum::getProjectionMatrix( MatrixF *proj, bool gfxRotate ) const +{ + if (mIsOrtho) + MathUtils::makeOrthoProjection(proj, mNearLeft, mNearRight, mNearTop, mNearBottom, mNearDist, mFarDist, gfxRotate); + else + MathUtils::makeProjection(proj, mNearLeft, mNearRight, mNearTop, mNearBottom, mNearDist, mFarDist, gfxRotate); +} + +//----------------------------------------------------------------------------- + +void Frustum::tileFrustum(U32 numTiles, const Point2I& curTile, Point2F overlap) +{ + //These will be stored to re-tile the frustum if needed + mNumTiles = numTiles; + mCurrTile = curTile; + mTileOverlap = overlap; + + tile(&mNearLeft, &mNearRight, &mNearTop, &mNearBottom, mNumTiles, mCurrTile, mTileOverlap); +} + +//----------------------------------------------------------------------------- + +void Frustum::tile( F32 *left, F32 *right, F32 *top, F32 *bottom, U32 numTiles, const Point2I& curTile, Point2F overlap ) +{ + if (numTiles == 1) + return; + + Point2F tileSize( ( *right - *left ) / (F32)numTiles, + ( *top - *bottom ) / (F32)numTiles ); + + F32 leftOffset = tileSize.x*overlap.x; + F32 rightOffset = tileSize.x*overlap.x*2; + F32 bottomOffset = tileSize.y*overlap.y; + F32 topOffset = tileSize.y*overlap.y*2; + + *left += tileSize.x * curTile.x - leftOffset; + *right = *left + tileSize.x + rightOffset; + *bottom += tileSize.y * curTile.y - bottomOffset; + *top = *bottom + tileSize.y + topOffset; +} diff --git a/Engine/source/math/util/frustum.h b/Engine/source/math/util/frustum.h new file mode 100644 index 000000000..4a756225c --- /dev/null +++ b/Engine/source/math/util/frustum.h @@ -0,0 +1,443 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MATHUTIL_FRUSTUM_H_ +#define _MATHUTIL_FRUSTUM_H_ + +#ifndef _MPOLYHEDRON_H_ +#include "math/mPolyhedron.h" +#endif + +#ifndef _MBOX_H_ +#include "math/mBox.h" +#endif + +#ifndef _MPLANE_H_ +#include "math/mPlane.h" +#endif + +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif + +#ifndef _MQUAT_H_ +#include "math/mQuat.h" +#endif + +#ifndef _MSPHERE_H_ +#include "math/mSphere.h" +#endif + + +//TODO: Specialize intersection tests for frustums using octant tests + + +class OrientedBox3F; + + +/// Polyhedron data for use by frustums. Uses fixed-size vectors +/// and a static vector for the edge list as that never changes +/// between frustums. +struct FrustumData : public PolyhedronData +{ + enum + { + EdgeCount = 12 + }; + + /// Indices for the planes in a frustum. + /// + /// Note the planes are ordered left, right, near, + /// far, top, bottom for getting early rejections + /// from the typical horizontal scene. + enum + { + PlaneLeft, + PlaneRight, + PlaneNear, + PlaneFar, + PlaneTop, + PlaneBottom, + + /// The total number of frustum planes. + PlaneCount + }; + + /// Indices for the corner points of the frustum. + enum CornerPoints + { + NearTopLeft, + NearTopRight, + NearBottomLeft, + NearBottomRight, + FarTopLeft, + FarTopRight, + FarBottomLeft, + FarBottomRight, + + /// Total number of corner points. + CornerPointCount + }; + + /// Indices for the center points of the frustum planes. + enum PlaneCenters + { + PlaneLeftCenter, + PlaneRightCenter, + PlaneTopCenter, + PlaneBottomCenter, + PlaneNearCenter, + PlaneFarCenter, + }; + + /// Used to mask out planes for testing. + enum + { + PlaneMaskLeft = ( 1 << PlaneLeft ), + PlaneMaskRight = ( 1 << PlaneRight ), + PlaneMaskTop = ( 1 << PlaneTop ), + PlaneMaskBottom = ( 1 << PlaneBottom ), + PlaneMaskNear = ( 1 << PlaneNear ), + PlaneMaskFar = ( 1 << PlaneFar ), + + PlaneMaskAll = 0xFFFFFFFF, + }; + + typedef FixedSizeVector< PlaneF, PlaneCount > PlaneListType; + typedef FixedSizeVector< Point3F, CornerPointCount > PointListType; + typedef FixedSizeVector< Edge, EdgeCount > EdgeListType; + + protected: + + /// @name Lazily Updated Data + /// @{ + + /// When true, points, planes and bounds must be re-calculated before use. + mutable bool mDirty; + + mutable PlaneListType mPlanes; + mutable PointListType mPoints; + + /// The center points of the individual faces of the frustum. + mutable Point3F mPlaneCenters[ PlaneCount ]; + + /// The clipping-space axis-aligned bounding box which contains + /// the extents of the frustum. + mutable Box3F mBounds; + + /// @} + + /// Static edge list. Shared by all frustum polyhedrons + /// since they are always constructed the same way. + static EdgeListType smEdges; + + /// Determines whether this Frustum + /// is orthographic or perspective. + bool mIsOrtho; + + /// Whether the frustum is inverted, i.e. whether the planes are + /// facing outwards rather than inwards. + bool mIsInverted; + + /// Used to transform the frustum points from camera + /// space into the desired clipping space. + MatrixF mTransform; + + /// Camera position extracted from tarnsform. + Point3F mPosition; + + /// The size of the near plane used to generate + /// the frustum points and planes. + F32 mNearLeft; + F32 mNearRight; + F32 mNearTop; + F32 mNearBottom; + F32 mNearDist; + F32 mFarDist; + + /// Update the point and plane data from the current frustum settings. + void _update() const; + + FrustumData() + : mDirty( false ), + mIsInverted( false ) {} + + public: + + /// @name Accessors + /// @{ + + /// Return the number of planes that a frustum has. + static U32 getNumPlanes() { return PlaneCount; } + + /// Return the planes that make up the polyhedron. + /// @note The normals of these planes are facing *inwards*. + const PlaneF* getPlanes() const { _update(); return mPlanes.address(); } + + /// Return the number of corner points that a frustum has. + static U32 getNumPoints() { return CornerPointCount; } + + /// + const Point3F* getPoints() const { _update(); return mPoints.address(); } + + /// Return the number of edges that a frustum has. + static U32 getNumEdges() { return EdgeCount; } + + /// Return the edge definitions for a frustum. + static const Edge* getEdges() { return smEdges.address(); } + + /// @} + + operator AnyPolyhedron() const + { + return AnyPolyhedron( + AnyPolyhedron::PlaneListType( const_cast< PlaneF* >( getPlanes() ), getNumPlanes() ), + AnyPolyhedron::PointListType( const_cast< Point3F* >( getPoints() ), getNumPoints() ), + AnyPolyhedron::EdgeListType( const_cast< Edge* >( getEdges() ), getNumEdges() ) + ); + } +}; + + +/// This class implements a view frustum for use in culling scene objects and +/// rendering the scene. +/// +/// @warn Frustums are always non-inverted by default which means that even if +/// the frustum transform applies a negative scale, the frustum will still be +/// non-inverted. +class Frustum : public PolyhedronImpl< FrustumData > +{ + public: + + typedef PolyhedronImpl< FrustumData > Parent; + + protected: + + /// @name Tiling + /// @{ + + /// Number of subdivisions. + U32 mNumTiles; + + /// Current rendering tile. + Point2I mCurrTile; + + /// Tile overlap percentage. + Point2F mTileOverlap; + + /// @} + + public: + + /// @name Constructors + /// @{ + + /// Construct a non-inverted frustum. + /// + /// @note If the given transform has a negative scale, the plane + /// normals will automatically be inverted so that the frustum + /// will still be non-inverted. Use invert() to actually cause + /// the frustum to be inverted. + Frustum( bool orthographic = false, + F32 nearLeft = -1.0f, + F32 nearRight = 1.0f, + F32 nearTop = 1.0f, + F32 nearBottom = -1.0f, + F32 nearDist = 0.1f, + F32 farDist = 1.0f, + const MatrixF &transform = MatrixF( true ) ); + + /// @} + + + /// @name Operators + /// @{ + + bool operator==( const Frustum& frustum ) const + { + return ( ( mNearLeft == frustum.mNearLeft ) && + ( mNearTop == frustum.mNearTop ) && + ( mNearBottom == frustum.mNearBottom ) && + ( mNearDist == frustum.mNearDist ) && + ( mFarDist == frustum.mFarDist ) ); + } + bool operator!=( const Frustum& frustum ) const { return !( *this == frustum ); } + + /// @} + + + /// @name Initialization + /// + /// Functions used to initialize the frustum. + /// + /// @{ + + /// Sets the frustum from the field of view, screen aspect + /// ratio, and the near and far distances. You can pass an + /// matrix to transform the frustum. + void set( bool isOrtho, + F32 fovYInRadians, + F32 aspectRatio, + F32 nearDist, + F32 farDist, + const MatrixF &mat = MatrixF( true ) ); + + /// Sets the frustum from the near plane dimensions and + /// near and far distances. + void set( bool isOrtho, + F32 nearLeft, + F32 nearRight, + F32 nearTop, + F32 nearBottom, + F32 nearDist, + F32 farDist, + const MatrixF &transform = MatrixF( true ) ); + + /// Sets the frustum by extracting the planes from a projection, + /// view-projection, or world-view-projection matrix. + //void set( const MatrixF& projMatrix, bool normalize ); + + /// Changes the near distance of the frustum. + void setNearDist( F32 nearDist ); + + /// Changes the far distance of the frustum. + void setFarDist( F32 farDist ); + + /// Changes the near and far distance of the frustum. + void setNearFarDist( F32 nearDist, F32 farDist ); + + /// + void cropNearFar(F32 newNearDist, F32 newFarDist); + + /// Returns the far clip distance used to create + /// the frustum planes. + F32 getFarDist() const { return mFarDist; } + + /// Returns the far clip distance used to create + /// the frustum planes. + F32 getNearDist() const { return mNearDist; } + + /// Return the camera-space minimum X coordinate on the near plane. + F32 getNearLeft() const { return mNearLeft; } + + /// Return the camera-space maximum X coordinate on the near plane. + F32 getNearRight() const { return mNearRight; } + + /// Return the camera-space maximum Z coordinate on the near plane. + F32 getNearTop() const { return mNearTop; } + + /// Return the camera-space minimum Z coordinate on the near plane. + F32 getNearBottom() const { return mNearBottom; } + + /// Return the camera-space width of the frustum. + F32 getWidth() const { return mFabs( mNearRight - mNearLeft ); } + + /// Return the camera-space height of the frustum. + F32 getHeight() const { return mFabs( mNearTop - mNearBottom ); } + + /// + F32 getFov() const + { + F32 nonTiledHeight = getHeight()*mNumTiles; + return mAtan2( nonTiledHeight/2.0f, mNearDist ) * 2.0f; + } + + /// + F32 getAspectRatio() const { return (mNearRight - mNearLeft)/(mNearTop - mNearBottom); } + + /// @} + + + /// @name Transformation + /// + /// These functions for transforming the frustum from + /// one space to another. + /// + /// @{ + + /// Sets a new transform for the frustum. + void setTransform( const MatrixF &transform ); + + /// Returns the current transform matrix for the frustum. + const MatrixF& getTransform() const { return mTransform; } + + /// Scales up the frustum from its center point. + void scaleFromCenter( F32 scale ); + + /// Transforms the frustum by F = F * mat. + void mul( const MatrixF &mat ); + + /// Transforms the frustum by F = mat * F. + void mulL( const MatrixF &mat ); + + /// Flip the plane normals which has the result + /// of reversing the culling results. + void invert(); + + /// Returns true if the frustum planes point outwards. + bool isInverted() const { return mIsInverted; } + + /// Returns the origin point of the frustum. + const Point3F& getPosition() const { return mPosition; } + + /// Returns the axis aligned bounding box of the frustum + /// points typically used for early rejection. + const Box3F& getBounds() const { _update(); return mBounds; } + + /// Generates a projection matrix from the frustum. + void getProjectionMatrix( MatrixF *proj, bool gfxRotate=true ) const; + + /// @} + + /// @name Culling + /// @{ + + /// Return true if the contents of the given AABB can be culled. + bool isCulled( const Box3F& aabb ) const { return ( testPotentialIntersection( aabb ) == GeometryOutside ); } + + /// Return true if the contents of the given OBB can be culled. + bool isCulled( const OrientedBox3F& obb ) const { return ( testPotentialIntersection( obb ) == GeometryOutside ); } + + /// Return true if the contents of the given sphere can be culled. + bool isCulled( const SphereF& sphere ) const { return ( testPotentialIntersection( sphere ) == GeometryOutside ); } + + /// @} + + /// @name Projection Type + /// @{ + + bool isOrtho() const { return mIsOrtho; } + + /// @} + + /// @name Tile settings + /// @{ + + U32 getNumTiles() const { return mNumTiles; } + const Point2I& getCurTile() const { return mCurrTile; } + void tileFrustum(U32 numTiles, const Point2I& curTile, Point2F overlap); + static void tile( F32 *left, F32 *right, F32 *top, F32 *bottom, U32 numTiles, const Point2I& curTile, Point2F overlap ); + + /// @} +}; + +#endif // _MATHUTIL_FRUSTUM_H_ \ No newline at end of file diff --git a/Engine/source/math/util/matrixSet.cpp b/Engine/source/math/util/matrixSet.cpp new file mode 100644 index 000000000..bc2e2ef0d --- /dev/null +++ b/Engine/source/math/util/matrixSet.cpp @@ -0,0 +1,50 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "math/util/matrixSet.h" + + +MatrixSet::MatrixSet() +{ + // [9/4/2009 Pat] Until we get better control over heap allocations in Torque + // this class will provide a place where aligned/specalized matrix math can take place. + // We should be able to plug in any kind of platform-specific optimization + // behind the delgates. + AssertFatal( ((int)this & 0xF) == 0, "MatrixSet has been allocated off a 16-byte boundary!" ); + + // Must be initialized by name, not a for(), it's macro magic + MATRIX_SET_BIND_VALUE(ObjectToWorld); + MATRIX_SET_BIND_VALUE(WorldToCamera); + MATRIX_SET_BIND_VALUE(CameraToScreen); + MATRIX_SET_BIND_VALUE(ObjectToCamera); + MATRIX_SET_BIND_VALUE(WorldToObject); + MATRIX_SET_BIND_VALUE(CameraToWorld); + MATRIX_SET_BIND_VALUE(ObjectToScreen); + MATRIX_SET_BIND_VALUE(CameraToObject); + MATRIX_SET_BIND_VALUE(WorldToScreen); + MATRIX_SET_BIND_VALUE(SceneView); + MATRIX_SET_BIND_VALUE(SceneProjection); + + mViewSource = NULL; + mProjectionSource = NULL; +} \ No newline at end of file diff --git a/Engine/source/math/util/matrixSet.h b/Engine/source/math/util/matrixSet.h new file mode 100644 index 000000000..0a2ba8e53 --- /dev/null +++ b/Engine/source/math/util/matrixSet.h @@ -0,0 +1,165 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _MATRIXSET_H_ +#define _MATRIXSET_H_ + +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif +#ifndef _UTIL_DELEGATE_H_ +#include "core/util/delegate.h" +#endif +#ifndef _MATRIXSETDELEGATES_H_ +#include "math/util/matrixSetDelegateMethods.h" +#endif + + +dALIGN_BEGIN + +class MatrixSet +{ + typedef Delegate MatrixEvalDelegate; + enum _Transforms + { + ObjectToWorld = 0, // World + WorldToCamera, // View + CameraToScreen, // Projection + ObjectToScreen, // World * View * Proj + ObjectToCamera, // World * View + WorldToObject, // World^-1 + CameraToWorld, // View^-1 + CameraToObject, // (World * View)^-1 + WorldToScreen, // View * Projection + SceneView, // The View matrix for the SceneState + SceneProjection, // The Projection matrix for the SceneState + NumTransforms, + }; + + MatrixF mTransform[NumTransforms]; + MatrixEvalDelegate mEvalDelegate[NumTransforms]; + + const MatrixF *mViewSource; + const MatrixF *mProjectionSource; + + MATRIX_SET_GET_VALUE(ObjectToWorld); + MATRIX_SET_GET_VALUE(WorldToCamera); + MATRIX_SET_GET_VALUE(CameraToScreen); + MATRIX_SET_GET_VALUE(ObjectToCamera); + MATRIX_SET_GET_VALUE(WorldToObject); + MATRIX_SET_GET_VALUE(CameraToWorld); + MATRIX_SET_GET_VALUE(ObjectToScreen); + MATRIX_SET_GET_VALUE(CameraToObject); + MATRIX_SET_GET_VALUE(WorldToScreen); + MATRIX_SET_GET_VALUE(SceneView); + MATRIX_SET_GET_VALUE(SceneProjection); + + MATRIX_SET_IS_INVERSE_OF(WorldToObject, ObjectToWorld); + MATRIX_SET_IS_INVERSE_OF(CameraToWorld, WorldToCamera); + + MATRIX_SET_MULT_ASSIGN(WorldToCamera, ObjectToWorld, ObjectToCamera); + MATRIX_SET_IS_INVERSE_OF(CameraToObject, ObjectToCamera); + + MATRIX_SET_MULT_ASSIGN(CameraToScreen, WorldToCamera, WorldToScreen); + + MATRIX_SET_MULT_ASSIGN(WorldToScreen, ObjectToWorld, ObjectToScreen); + +public: + MatrixSet(); + + // Direct accessors + inline const MatrixF &getObjectToWorld() const { return mTransform[ObjectToWorld]; } + inline const MatrixF &getWorldToCamera() const { return mTransform[WorldToCamera]; } + inline const MatrixF &getCameraToScreen() const { return mTransform[CameraToScreen]; } + + // Delegate driven, lazy-evaluation accessors + inline const MatrixF &getWorldToScreen() const { return mEvalDelegate[WorldToScreen](); } + inline const MatrixF &getWorldViewProjection() const { return mEvalDelegate[ObjectToScreen](); } + inline const MatrixF &getWorldToObject() const { return mEvalDelegate[WorldToObject](); } + inline const MatrixF &getCameraToWorld() const { return mEvalDelegate[CameraToWorld](); } + inline const MatrixF &getObjectToCamera() const { return mEvalDelegate[ObjectToCamera](); } + inline const MatrixF &getCameraToObject() const { return mEvalDelegate[CameraToObject](); } + + // Assignment for the world/view/projection matrices + inline void setWorld(const MatrixF &world) + { + mTransform[ObjectToWorld] = world; + mEvalDelegate[WorldToObject].bind(this, &MatrixSet::MATRIX_SET_IS_INVERSE_OF_FN(WorldToObject, ObjectToWorld)); + mEvalDelegate[ObjectToScreen].bind(this, &MatrixSet::MATRIX_SET_MULT_ASSIGN_FN(WorldToScreen, ObjectToWorld, ObjectToScreen)); + mEvalDelegate[ObjectToCamera].bind(this, &MatrixSet::MATRIX_SET_MULT_ASSIGN_FN(WorldToCamera, ObjectToWorld, ObjectToCamera)); + mEvalDelegate[CameraToObject].bind(this, &MatrixSet::MATRIX_SET_IS_INVERSE_OF_FN(CameraToObject, ObjectToCamera)); + } + + inline void setView(const MatrixF &view) + { + if(&view == mViewSource) + return; + + mViewSource = &view; + mTransform[WorldToCamera] = view; + mEvalDelegate[CameraToWorld].bind(this, &MatrixSet::MATRIX_SET_IS_INVERSE_OF_FN(CameraToWorld, WorldToCamera)); + mEvalDelegate[ObjectToScreen].bind(this, &MatrixSet::MATRIX_SET_MULT_ASSIGN_FN(WorldToScreen, ObjectToWorld, ObjectToScreen)); + mEvalDelegate[WorldToScreen].bind(this, &MatrixSet::MATRIX_SET_MULT_ASSIGN_FN(CameraToScreen, WorldToCamera, WorldToScreen)); + mEvalDelegate[ObjectToCamera].bind(this, &MatrixSet::MATRIX_SET_MULT_ASSIGN_FN(WorldToCamera, ObjectToWorld, ObjectToCamera)); + mEvalDelegate[CameraToObject].bind(this, &MatrixSet::MATRIX_SET_IS_INVERSE_OF_FN(CameraToObject, ObjectToCamera)); + } + + inline void setProjection(const MatrixF &projection) + { + if(&projection == mProjectionSource) + return; + + mProjectionSource = &projection; + mTransform[CameraToScreen] = projection; + mEvalDelegate[ObjectToScreen].bind(this, &MatrixSet::MATRIX_SET_MULT_ASSIGN_FN(WorldToScreen, ObjectToWorld, ObjectToScreen)); + mEvalDelegate[WorldToScreen].bind(this, &MatrixSet::MATRIX_SET_MULT_ASSIGN_FN(CameraToScreen, WorldToCamera, WorldToScreen)); + } + + void setSceneView(const MatrixF &view) + { + mViewSource = NULL; + setView(view); + mViewSource = &mTransform[WorldToCamera]; + mTransform[SceneView] = view; + } + + void setSceneProjection(const MatrixF &projection) + { + mProjectionSource = NULL; + setProjection(projection); + mProjectionSource = &mTransform[CameraToScreen]; + mTransform[SceneProjection] = projection; + } + + void restoreSceneViewProjection() + { + mViewSource = NULL; + mProjectionSource = NULL; + setView(mTransform[SceneView]); + setProjection(mTransform[SceneProjection]); + mViewSource = &mTransform[WorldToCamera]; + mProjectionSource = &mTransform[CameraToScreen]; + } +} + +dALIGN_END; + +#endif // _MATRIXSET_H_ diff --git a/Engine/source/math/util/matrixSetDelegateMethods.h b/Engine/source/math/util/matrixSetDelegateMethods.h new file mode 100644 index 000000000..f5d237472 --- /dev/null +++ b/Engine/source/math/util/matrixSetDelegateMethods.h @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _MATRIXSETDELEGATES_H_ +#define _MATRIXSETDELEGATES_H_ + + // Access to the direct value +#define MATRIX_SET_GET_VALUE_FN(xfm) _transform_##xfm +#define MATRIX_SET_GET_VALUE(xfm) inline const MatrixF &MATRIX_SET_GET_VALUE_FN(xfm)() { return mTransform[xfm]; } +#define MATRIX_SET_BIND_VALUE(xfm) mEvalDelegate[xfm].bind(this, &MatrixSet::MATRIX_SET_GET_VALUE_FN(xfm)) + +#define MATRIX_SET_IS_INVERSE_OF_FN(inv_xfm, src_xfm) _##inv_xfm##_is_inverse_of_##src_xfm +#define MATRIX_SET_IS_INVERSE_OF(inv_xfm, src_xfm) inline const MatrixF &MATRIX_SET_IS_INVERSE_OF_FN(inv_xfm, src_xfm)() \ + { \ + m_matF_invert_to(mEvalDelegate[src_xfm](), mTransform[inv_xfm]); \ + MATRIX_SET_BIND_VALUE(inv_xfm); \ + return mTransform[inv_xfm]; \ + } + + +#define MATRIX_SET_MULT_ASSIGN_FN(matA, matB, matC) _##matC##_is_##matA##_x_##matB +#define MATRIX_SET_MULT_ASSIGN(matA, matB, matC) inline const MatrixF &MATRIX_SET_MULT_ASSIGN_FN(matA, matB, matC)() \ + { \ + m_matF_x_matF_aligned(mEvalDelegate[matA](), mEvalDelegate[matB](), mTransform[matC]); \ + MATRIX_SET_BIND_VALUE(matC); \ + return mTransform[matC]; \ + } + + +#endif // _MATRIXSETDELEGATES_H_ diff --git a/Engine/source/math/util/quadTransforms.cpp b/Engine/source/math/util/quadTransforms.cpp new file mode 100644 index 000000000..f947bf78d --- /dev/null +++ b/Engine/source/math/util/quadTransforms.cpp @@ -0,0 +1,165 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "math/util/quadTransforms.h" + + +BiQuadToSqr::BiQuadToSqr( const Point2F &p00, + const Point2F &p10, + const Point2F &p11, + const Point2F &p01 ) + : m_kP00( p00 ) +{ + m_kB = p10 - p00 ; // width + m_kC = p01 - p00; // height + m_kD = p11 + p00 - p10 - p01; // diagonal dist + + if(mFabs(m_kD.x) < POINT_EPSILON) + m_kD.x = 0.f; + if(mFabs(m_kD.y) < POINT_EPSILON) + m_kD.y = 0.f; + + m_fBC = mDotPerp( m_kB, m_kC ); + m_fBD = mDotPerp( m_kB, m_kD ); + m_fCD = mDotPerp( m_kC, m_kD ); +} + +Point2F BiQuadToSqr::transform( const Point2F &p ) const +{ + Point2F kA = m_kP00 - p; + + F32 fAB = mDotPerp( kA, m_kB ); + F32 fAC = mDotPerp( kA, m_kC); + + // 0 = ac*bc+(bc^2+ac*bd-ab*cd)*s+bc*bd*s^2 = k0 + k1*s + k2*s^2 + F32 fK0 = fAC*m_fBC; + F32 fK1 = m_fBC*m_fBC + fAC*m_fBD - fAB*m_fCD; + F32 fK2 = m_fBC*m_fBD; + + if (mFabs(fK2) > POINT_EPSILON) + { + // s-equation is quadratic + F32 fInv = 0.5f/fK2; + F32 fDiscr = fK1*fK1 - 4.0f*fK0*fK2; + F32 fRoot = mSqrt( mFabs(fDiscr) ); + + Point2F kResult0( 0, 0 ); + kResult0.x = (-fK1 - fRoot)*fInv; + kResult0.y = fAB/(m_fBC + m_fBD*kResult0.x); + F32 fDeviation0 = deviation(kResult0); + if ( fDeviation0 == 0.0f ) + return kResult0; + + Point2F kResult1( 0, 0 ); + kResult1.x = (-fK1 + fRoot)*fInv; + kResult1.y = fAB/(m_fBC + m_fBD*kResult1.x); + F32 fDeviation1 = deviation(kResult1); + if ( fDeviation1 == 0.0f ) + return kResult1; + + if (fDeviation0 <= fDeviation1) + { + if ( fDeviation0 < POINT_EPSILON ) + return kResult0; + } + else + { + if ( fDeviation1 < POINT_EPSILON ) + return kResult1; + } + } + else + { + // s-equation is linear + Point2F kResult( 0, 0 ); + + kResult.x = -fK0/fK1; + kResult.y = fAB/(m_fBC + m_fBD*kResult.x); + F32 fDeviation = deviation(kResult); + if ( fDeviation < POINT_EPSILON ) + return kResult; + } + + // point is outside the quadrilateral, return invalid + return Point2F(F32_MAX,F32_MAX); +} + +F32 BiQuadToSqr::deviation( const Point2F &sp ) +{ + // deviation is the squared distance of the point from the unit square + F32 fDeviation = 0.0f; + F32 fDelta; + + if (sp.x < 0.0f) + { + fDeviation += sp.x*sp.x; + } + else if (sp.x > 1.0f) + { + fDelta = sp.x - 1.0f; + fDeviation += fDelta*fDelta; + } + + if (sp.y < 0.0f) + { + fDeviation += sp.y*sp.y; + } + else if (sp.y > 1.0f) + { + fDelta = sp.y - 1.0f; + fDeviation += fDelta*fDelta; + } + + return fDeviation; +} + + +BiSqrToQuad3D::BiSqrToQuad3D( const Point3F& pnt00, + const Point3F& pnt10, + const Point3F& pnt11, + const Point3F& pnt01) +{ + p00 = pnt00; + p10 = pnt10; + p11 = pnt11; + p01 = pnt01; +} + +Point3F BiSqrToQuad3D::transform( const Point2F &p ) const +{ + //Let p00, p10, p01, and p11 be your 3-tuples that are the quad's + //vertices. You can parameterize the quad as follows. + + //q(s,t) = (1-s)*((1-t)*p00 + t*p01) + s*((1-t)*p10 + t*p11) + + //for 0 <= s <= 1 and 0 <= t <= 1. Notice that q(0,0) = p00, + //q(1,0) = p10, q(0,1) = p01, and q(1,1) = p11, so the parameter + //"square" whose points are (s,t) will be mapped to the quad. + + const F32 &s = p.x; + const F32 &t = p.y; + + Point3F result = (1.0f-s)*((1.0f-t)*p00 + t*p01) + s*((1.0f-t)*p10 + t*p11); + return result; +} + diff --git a/Engine/source/math/util/quadTransforms.h b/Engine/source/math/util/quadTransforms.h new file mode 100644 index 000000000..dfd0ad197 --- /dev/null +++ b/Engine/source/math/util/quadTransforms.h @@ -0,0 +1,82 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _QUADTOQUADTRANSFORMS_H_ +#define _QUADTOQUADTRANSFORMS_H_ + +#ifndef _MPOINT2_H_ +#include "math/mPoint2.h" +#endif +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif + +// NOTE: The code in these classes originate from the Wild Magic Source Code +// library by David Eberly and is used with permission. + + +/// This class does bilinear mapping of quadrilateral to a square. +class BiQuadToSqr +{ +public: + + /// Constructs the transform class from the quadrilateral + /// points in counter clockwise order. + BiQuadToSqr( const Point2F &p00, + const Point2F &p10, + const Point2F &p11, + const Point2F &p01 ); + + /// Transforms the point. + Point2F transform( const Point2F &p ) const; + +protected: + + static F32 deviation( const Point2F &sp ); + + Point2F m_kP00, m_kB, m_kC, m_kD; + + F32 m_fBC, m_fBD, m_fCD; + +}; + + +class BiSqrToQuad3D +{ +public: + + BiSqrToQuad3D( const Point3F &pnt00, + const Point3F &pnt10, + const Point3F &pnt11, + const Point3F &pnt01 ); + + Point3F transform( const Point2F &pnt ) const; + +protected: + + Point3F p00, p01, p10, p11; +}; + +#endif // _QUADTOQUADTRANSFORMS_H_ diff --git a/Engine/source/math/util/sphereMesh.cpp b/Engine/source/math/util/sphereMesh.cpp new file mode 100644 index 000000000..59dc37433 --- /dev/null +++ b/Engine/source/math/util/sphereMesh.cpp @@ -0,0 +1,250 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "math/util/sphereMesh.h" + +SphereMesh::SphereMesh(U32 baseType) +{ + VECTOR_SET_ASSOCIATION(mDetails); + + switch(baseType) + { + case Tetrahedron: + mDetails.push_back(createTetrahedron()); + break; + + case Octahedron: + mDetails.push_back(createOctahedron()); + break; + + case Icosahedron: + mDetails.push_back(createIcosahedron()); + break; + } + calcNormals(mDetails[0]); +} + +//------------------------------------------------------------------------------ + +SphereMesh::TriangleMesh * SphereMesh::createTetrahedron() +{ + const F32 sqrt3 = 0.5773502692f; + + static Point3F spherePnts[] = { + Point3F( sqrt3, sqrt3, sqrt3 ), + Point3F(-sqrt3,-sqrt3, sqrt3 ), + Point3F(-sqrt3, sqrt3,-sqrt3 ), + Point3F( sqrt3,-sqrt3,-sqrt3 ) + }; + + static Triangle tetrahedron[] = { + Triangle(spherePnts[0], spherePnts[1], spherePnts[2]), + Triangle(spherePnts[0], spherePnts[3], spherePnts[1]), + Triangle(spherePnts[2], spherePnts[1], spherePnts[3]), + Triangle(spherePnts[3], spherePnts[0], spherePnts[2]), + }; + + static TriangleMesh tetrahedronMesh = { + Tetrahedron, + &tetrahedron[0] + }; + + return(&tetrahedronMesh); +} + +//------------------------------------------------------------------------------ + +SphereMesh::TriangleMesh * SphereMesh::createOctahedron() +{ + // + static Point3F spherePnts[] = { + Point3F( 1, 0, 0), + Point3F(-1, 0, 0), + Point3F( 0, 1, 0), + Point3F( 0,-1, 0), + Point3F( 0, 0, 1), + Point3F( 0, 0,-1) + }; + + // + static Triangle octahedron[] = { + Triangle(spherePnts[0], spherePnts[4], spherePnts[2]), + Triangle(spherePnts[2], spherePnts[4], spherePnts[1]), + Triangle(spherePnts[1], spherePnts[4], spherePnts[3]), + Triangle(spherePnts[3], spherePnts[4], spherePnts[0]), + Triangle(spherePnts[0], spherePnts[2], spherePnts[5]), + Triangle(spherePnts[2], spherePnts[1], spherePnts[5]), + Triangle(spherePnts[1], spherePnts[3], spherePnts[5]), + Triangle(spherePnts[3], spherePnts[0], spherePnts[5]) + }; + + // + static TriangleMesh octahedronMesh = { + Octahedron, + &octahedron[0] + }; + + return(&octahedronMesh); +} + +SphereMesh::TriangleMesh * SphereMesh::createIcosahedron() +{ + const F32 tau = 0.8506508084f; + const F32 one = 0.5257311121f; + + static Point3F spherePnts[] = { + Point3F( tau, one, 0), + Point3F(-tau, one, 0), + Point3F(-tau,-one, 0), + Point3F( tau,-one, 0), + Point3F( one, 0, tau), + Point3F( one, 0,-tau), + Point3F(-one, 0,-tau), + Point3F(-one, 0, tau), + Point3F( 0, tau, one), + Point3F( 0,-tau, one), + Point3F( 0,-tau,-one), + Point3F( 0, tau,-one), + }; + + static Triangle icosahedron[] = { + Triangle(spherePnts[4], spherePnts[8], spherePnts[7]), + Triangle(spherePnts[4], spherePnts[7], spherePnts[9]), + Triangle(spherePnts[5], spherePnts[6], spherePnts[11]), + Triangle(spherePnts[5], spherePnts[10], spherePnts[6]), + Triangle(spherePnts[0], spherePnts[4], spherePnts[3]), + Triangle(spherePnts[0], spherePnts[3], spherePnts[5]), + Triangle(spherePnts[2], spherePnts[7], spherePnts[1]), + Triangle(spherePnts[2], spherePnts[1], spherePnts[6]), + Triangle(spherePnts[8], spherePnts[0], spherePnts[11]), + Triangle(spherePnts[8], spherePnts[11], spherePnts[1]), + Triangle(spherePnts[9], spherePnts[10], spherePnts[3]), + Triangle(spherePnts[9], spherePnts[2], spherePnts[10]), + Triangle(spherePnts[8], spherePnts[4], spherePnts[0]), + Triangle(spherePnts[11], spherePnts[0], spherePnts[5]), + Triangle(spherePnts[4], spherePnts[9], spherePnts[3]), + Triangle(spherePnts[5], spherePnts[3], spherePnts[10]), + Triangle(spherePnts[7], spherePnts[8], spherePnts[1]), + Triangle(spherePnts[6], spherePnts[1], spherePnts[11]), + Triangle(spherePnts[7], spherePnts[2], spherePnts[9]), + Triangle(spherePnts[6], spherePnts[10], spherePnts[2]), + }; + + static TriangleMesh icosahedronMesh = { + Icosahedron, + &icosahedron[0] + }; + + return(&icosahedronMesh); +} + +//------------------------------------------------------------------------------ + +void SphereMesh::calcNormals(TriangleMesh * mesh) +{ + for(U32 i = 0; i < mesh->numPoly; i++) + { + Triangle & tri = mesh->poly[i]; + mCross(tri.pnt[1] - tri.pnt[0], tri.pnt[2] - tri.pnt[0], &tri.normal); + } +} + +//------------------------------------------------------------------------------ + +SphereMesh::~SphereMesh() +{ + // level 0 is static data + for(U32 i = 1; i < mDetails.size(); i++) + { + delete [] mDetails[i]->poly; + delete mDetails[i]; + } +} + +//------------------------------------------------------------------------------ + +const SphereMesh::TriangleMesh * SphereMesh::getMesh(U32 level) +{ + AssertFatal(mDetails.size(), "SphereMesh::getMesh: no details!"); + + if(level > MaxLevel) + level = MaxLevel; + + // + while(mDetails.size() <= level) + mDetails.push_back(subdivideMesh(mDetails.last())); + + return(mDetails[level]); +} + +SphereMesh::TriangleMesh * SphereMesh::subdivideMesh(TriangleMesh * prevMesh) +{ + AssertFatal(prevMesh, "SphereMesh::subdivideMesh: invalid previous mesh level!"); + + // + TriangleMesh * mesh = new TriangleMesh; + + mesh->numPoly = prevMesh->numPoly * 4; + mesh->poly = new Triangle [mesh->numPoly]; + + // + for(U32 i = 0; i < prevMesh->numPoly; i++) + { + Triangle * pt = &prevMesh->poly[i]; + Triangle * nt = &mesh->poly[i*4]; + + Point3F a = (pt->pnt[0] + pt->pnt[2]) / 2; + Point3F b = (pt->pnt[0] + pt->pnt[1]) / 2; + Point3F c = (pt->pnt[1] + pt->pnt[2]) / 2; + + // force the point onto the unit sphere surface + a.normalize(); + b.normalize(); + c.normalize(); + + // + nt->pnt[0] = pt->pnt[0]; + nt->pnt[1] = b; + nt->pnt[2] = a; + nt++; + + // + nt->pnt[0] = b; + nt->pnt[1] = pt->pnt[1]; + nt->pnt[2] = c; + nt++; + + // + nt->pnt[0] = a; + nt->pnt[1] = b; + nt->pnt[2] = c; + nt++; + + // + nt->pnt[0] = a; + nt->pnt[1] = c; + nt->pnt[2] = pt->pnt[2]; + } + + calcNormals(mesh); + return(mesh); +} diff --git a/Engine/source/math/util/sphereMesh.h b/Engine/source/math/util/sphereMesh.h new file mode 100644 index 000000000..2f8d5df19 --- /dev/null +++ b/Engine/source/math/util/sphereMesh.h @@ -0,0 +1,88 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SPHEREMESH_H_ +#define _SPHEREMESH_H_ + +#ifndef _MMATH_H_ +#include "math/mMath.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +//------------------------------------------------------------------------------ +// Class: SphereMesh +//------------------------------------------------------------------------------ +// * ctor takes type of base polyhedron that is subdivided to create sphere +// * getMesh(...) will subdivide the current mesh to the desired level where +// (each level has 4 times the polys of the previous level) + +class SphereMesh +{ + public: + + // regular polyhedra with triangle face polygons (num of faces) + enum { + Tetrahedron = 4, + Octahedron = 8, + Icosahedron = 20, + + MaxLevel = 5 + }; + + struct Triangle { + Triangle() {} + Triangle(const Point3F &a, const Point3F &b, const Point3F &c) + { + pnt[0] = a; + pnt[1] = b; + pnt[2] = c; + } + + Point3F pnt[3]; + Point3F normal; + }; + + struct TriangleMesh { + U32 numPoly; + Triangle * poly; + }; + + SphereMesh(U32 baseType = Octahedron); + ~SphereMesh(); + + const TriangleMesh * getMesh(U32 level = 0); + + private: + + TriangleMesh * createTetrahedron(); + TriangleMesh * createOctahedron(); + TriangleMesh * createIcosahedron(); + + Vector mDetails; + + void calcNormals(TriangleMesh *); + TriangleMesh * subdivideMesh(TriangleMesh*); +}; + +#endif diff --git a/Engine/source/math/util/tResponseCurve.cpp b/Engine/source/math/util/tResponseCurve.cpp new file mode 100644 index 000000000..450549d61 --- /dev/null +++ b/Engine/source/math/util/tResponseCurve.cpp @@ -0,0 +1,79 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "tResponseCurve.h" + +IMPLEMENT_CONOBJECT( SimResponseCurve ); + +ConsoleDocClass( SimResponseCurve, + "@brief A ResponseCurve wrapped as a SimObject.\n\n" + "Currently no applied use, not network ready, not intended " + "for game development, for editors or internal use only.\n\n " + "@internal"); + +SimResponseCurve::SimResponseCurve() +{ + +} + +bool SimResponseCurve::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + return true; +} + +void SimResponseCurve::onRemove() +{ + Parent::onRemove(); +} + +void SimResponseCurve::addPoint(F32 value, F32 time) +{ + mCurve.addPoint( value, time ); +} + +F32 SimResponseCurve::getValue(F32 time) +{ + return mCurve.getVal( time ); +} + +void SimResponseCurve::clear() +{ + mCurve.clear(); +} + +ConsoleMethod( SimResponseCurve, addPoint, void, 4, 4, "addPoint( F32 value, F32 time )" ) +{ + object->addPoint( dAtof(argv[2]), dAtof(argv[3]) ); +} + +ConsoleMethod( SimResponseCurve, getValue, F32, 3, 3, "getValue( F32 time )" ) +{ + return object->getValue( dAtof(argv[2]) ); +} + +ConsoleMethod( SimResponseCurve, clear, void, 2, 2, "clear()" ) +{ + object->clear(); +} \ No newline at end of file diff --git a/Engine/source/math/util/tResponseCurve.h b/Engine/source/math/util/tResponseCurve.h new file mode 100644 index 000000000..ad5329bda --- /dev/null +++ b/Engine/source/math/util/tResponseCurve.h @@ -0,0 +1,247 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// Notice: +// Some of this code originates from an article in AI Game Programming Wisdom +// by Dave Mark. + +#ifndef _TRESPONSECURVE_H_ +#define _TRESPONSECURVE_H_ + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +#ifndef _MMATHFN_H_ +#include "math/mMathFn.h" +#endif + +#ifndef _SIMOBJECT_H_ +#include "console/simObject.h" +#endif + + +// ------------------------------- +// Represents a sigmoid function +// Note: not used by ResponseCurve +// ------------------------------- + +class Sigmoid +{ +public: + Sigmoid( F32 s, F32 m) { _s = s; _m = m; } + ~Sigmoid() {}; + + inline F32 get( F32 x ) + { + F32 pow = -2.0f * ( ( x - _m ) / _s ); + F32 y = 1.0f / mPow( 1 + M_CONST_E_F, pow ); + return y; + } + + F32 _s; + F32 _m; +}; + +// ----------------------------------------------------------------------------- +// Represents a response curve, can query values +// ----------------------------------------------------------------------------- + +template< class T > +class ResponseCurve +{ +public: + + struct Sample + { + Sample() {} + Sample( F32 f, const T &val ) : mF(f), mVal(val) {} + + F32 mF; + T mVal; + }; + + typedef Vector< Sample > SampleList; + SampleList mSamples; + + const SampleList& getSamples() { return mSamples; } + + ResponseCurve() {} + ResponseCurve( U32 numSamples ) { mSamples.reserve(numSamples); } + + void clear() { mSamples.clear(); } + void addPoint( F32 f, const T &val ); + //void addPoints( U32 count, F32 f[], const T &val[] ); + T getVal( F32 f ) const; + S32 setPoint( S32 idx, F32 f, const T &val ); + void removePoint( S32 idx ); + S32 getSampleCount() const { return mSamples.size(); } +}; + +//----------------------------------------------------------------------------- +// Adds a new value to the Response Curve, at the position f +//----------------------------------------------------------------------------- +template< class T > +inline void ResponseCurve::addPoint( F32 f, const T &val ) +{ + typename SampleList::iterator iter = mSamples.begin(); + for ( ; iter != mSamples.end(); iter++ ) + { + if ( iter->mF == f ) + { + Con::warnf( "Warn: ResponseCurve::AddPoint, Duplicate values are not allowed." ); + return; + } + + if ( iter->mF > f ) + break; + } + + mSamples.insert( iter, Sample( f, val ) ); +} + +//----------------------------------------------------------------------------- +// Finds the right value at position f, interpolating between the previous +// and the following values +//----------------------------------------------------------------------------- +template< class T > +inline T ResponseCurve::getVal( F32 f ) const +{ + T retVal; + + if ( mSamples.empty() ) + { + retVal = T(); + } + else + { + U32 nSamples = mSamples.size(); + if ( nSamples == 1 || f <= mSamples[0].mF ) + { + retVal = mSamples[0].mVal; + } + else if ( f >= mSamples[nSamples-1].mF ) + { + retVal = mSamples[nSamples-1].mVal; + } + else + { + U32 i = 1; + while ( i < (nSamples-1) && mSamples[i].mF < f ) + ++i; + + // Interpolate between m_Samples[i-1] and m_Samples[i] + F32 fSampleMin = mSamples[i-1].mF; + F32 fSampleMax = mSamples[i].mF; + AssertWarn(fSampleMin != fSampleMax, "fSampleMin should not equal fSampleMax" ); + + F32 t = (f - fSampleMin) / (fSampleMax - fSampleMin); + retVal = mSamples[i-1].mVal + ( mSamples[i].mVal - mSamples[i-1].mVal) * t; + } + } + + return retVal; +} + +template< class T > +inline S32 ResponseCurve< T >::setPoint( S32 idx, F32 f, const T &val ) +{ + mSamples.erase( idx ); + + typename SampleList::iterator iter = mSamples.begin(); + for ( ; iter != mSamples.end(); iter++ ) + { + if ( iter->mF == f ) + { + Con::warnf( "Warn: ResponseCurve::AddPoint, Duplicate values are not allowed." ); + return -1; + } + + if ( iter->mF > f ) + break; + } + + mSamples.insert( iter, Sample( f, val ) ); + + return (S32)( iter - mSamples.begin() ); +} + + +class FloatCurve : public ResponseCurve +{ +public: + FloatCurve() {} +}; + + +// ----------------------------------------------- +// A ResponseCurve wrapped as a SimObject +// ----------------------------------------------- + +class SimResponseCurve : public SimObject +{ + typedef SimObject Parent; + +public: + + SimResponseCurve(); + //~SimResponseCurve(); + + DECLARE_CONOBJECT( SimResponseCurve ); + + virtual bool onAdd(); + virtual void onRemove(); + + void addPoint( F32 value, F32 time ); + F32 getValue( F32 time ); + void clear(); + + ResponseCurve mCurve; +}; + + +// A networked-datablock version of ResponseCurve +/* +class ResponseCurveData : public SimDataBlock +{ + typedef SimDataBlock Parent; + +public: + + ResponseCurveData(); + //~ResponseCurveData(); + + DECLARE_CONOBJECT( ResponseCurveData ); + + virtual bool onAdd(); + virtual void onRemove(); + + + void addPoint( F32 value, F32 time ); + F32 getValue( F32 time ); + void clear(); + + ResponseCurve mCurve; +}; +*/ + +#endif diff --git a/Engine/source/platform/async/asyncBufferedStream.h b/Engine/source/platform/async/asyncBufferedStream.h new file mode 100644 index 000000000..c1b69b3af --- /dev/null +++ b/Engine/source/platform/async/asyncBufferedStream.h @@ -0,0 +1,417 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _ASYNCBUFFEREDSTREAM_H_ +#define _ASYNCBUFFEREDSTREAM_H_ + +#ifndef _TSTREAM_H_ + #include "core/stream/tStream.h" +#endif +#ifndef _THREADPOOL_H_ + #include "platform/threads/threadPool.h" +#endif +#ifndef _THREADSAFEDEQUE_H_ + #include "platform/threads/threadSafeDeque.h" +#endif + + +// Disable nonsense warning about unreferenced +// local function on VC. +#ifdef TORQUE_COMPILER_VISUALC + #pragma warning( disable: 4505 ) +#endif + + +template< typename T, class Stream > +class AsyncBufferedReadItem; + + + +//============================================================================= +// AsyncBufferedInputStream. +//============================================================================= + + +/// +template< typename T, class Stream = IInputStream< T >* > +class AsyncBufferedInputStream : public IInputStreamFilter< T, Stream >, + public ThreadSafeRefCount< AsyncBufferedInputStream< T, Stream > > +{ + public: + + typedef IInputStreamFilter< T, Stream > Parent; + + /// Type of elements read, buffered, and returned by this stream. + typedef typename Parent::ElementType ElementType; + + /// Type of the source stream being read by this stream. + typedef typename Parent::SourceStreamType SourceStreamType; + + /// Type of elements being read from the source stream. + /// + /// @note This does not need to correspond to the type of elements buffered + /// in this stream. + typedef typename Parent::SourceElementType SourceElementType; + + enum + { + /// The number of elements to buffer in advance by default. + DEFAULT_STREAM_LOOKAHEAD = 3 + }; + + friend class AsyncBufferedReadItem< T, Stream >; // _onArrival + + protected: + + /// Stream elements are kept on deques that can be concurrently + /// accessed by multiple threads. + typedef ThreadSafeDeque< ElementType > ElementList; + + /// If true, the stream will restart over from the beginning once + /// it has been read in entirety. + bool mIsLooping; + + /// If true, no further requests should be issued on this stream. + /// @note This in itself doesn't say anything about pending requests. + bool mIsStopped; + + /// Number of source elements remaining in the source stream. + U32 mNumRemainingSourceElements; + + /// Number of elements currently on buffer list. + U32 mNumBufferedElements; + + /// Maximum number of elements allowed on buffer list. + U32 mMaxBufferedElements; + + /// List of buffered elements. + ElementList mBufferedElements; + + /// The thread pool to which read items are queued. + ThreadPool* mThreadPool; + + /// The thread context used for prioritizing read items in the pool. + ThreadContext* mThreadContext; + + /// Request the next element from the underlying stream. + virtual void _requestNext() = 0; + + /// Called when an element read has been completed on the underlying stream. + virtual void _onArrival( const ElementType& element ); + + public: + + /// Construct a new buffered stream reading from "source". + /// + /// @param stream The source stream from which to read the actual data elements. + /// @param numSourceElementsToRead Total number of elements to read from "stream". + /// @param numReadAhead Number of packets to read and buffer in advance. + /// @param isLooping If true, the packet stream will loop infinitely over the source stream. + /// @param pool The ThreadPool to use for asynchronous packet reads. + /// @param context The ThreadContext to place asynchronous packet reads in. + AsyncBufferedInputStream( const Stream& stream, + U32 numSourceElementsToRead = 0, + U32 numReadAhead = DEFAULT_STREAM_LOOKAHEAD, + bool isLooping = false, + ThreadPool* pool = &ThreadPool::GLOBAL(), + ThreadContext* context = ThreadContext::ROOT_CONTEXT() ); + + virtual ~AsyncBufferedInputStream(); + + /// @return true if the stream is looping infinitely. + bool isLooping() const { return mIsLooping; } + + /// @return the number of elements that will be read and buffered in advance. + U32 getReadAhead() const { return mMaxBufferedElements; } + + /// Initiate the request chain of the element stream. + void start() { _requestNext(); } + + /// Call for the request chain of the element stream to stop at the next + /// synchronization point. + void stop() { mIsStopped = true; } + + // IInputStream. + virtual U32 read( ElementType* buffer, U32 num ); +}; + +//----------------------------------------------------------------------------- + +template< typename T, typename Stream > +AsyncBufferedInputStream< T, Stream >::AsyncBufferedInputStream + ( const Stream& stream, + U32 numSourceElementsToRead, + U32 numReadAhead, + bool isLooping, + ThreadPool* threadPool, + ThreadContext* threadContext ) + : Parent( stream ), + mIsStopped( false ), + mIsLooping( isLooping ), + mNumRemainingSourceElements( numSourceElementsToRead ), + mNumBufferedElements( 0 ), + mMaxBufferedElements( numReadAhead ), + mThreadPool( threadPool ), + mThreadContext( threadContext ) +{ + if( mIsLooping ) + { + // Stream is looping so we don't count down source elements. + + mNumRemainingSourceElements = 0; + } + else if( !mNumRemainingSourceElements ) + { + // If not given number of elements to read, see if the source + // stream is sizeable. If so, take its size as the number of elements. + + if( dynamic_cast< ISizeable<>* >( &Deref( stream ) ) ) + mNumRemainingSourceElements = ( ( ISizeable<>* ) &Deref( stream ) )->getSize(); + else + { + // Can't tell how many source elements there are. + + mNumRemainingSourceElements = U32_MAX; + } + } +} + +//----------------------------------------------------------------------------- + +template< typename T, typename Stream > +AsyncBufferedInputStream< T, Stream >::~AsyncBufferedInputStream() +{ + ElementType element; + while( mBufferedElements.tryPopFront( element ) ) + destructSingle( element ); +} + +//----------------------------------------------------------------------------- + +template< typename T, typename Stream > +void AsyncBufferedInputStream< T, Stream >::_onArrival( const ElementType& element ) +{ + mBufferedElements.pushBack( element ); + + // Adjust buffer count. + + while( 1 ) + { + S32 numBuffered = mNumBufferedElements; + if( dCompareAndSwap( mNumBufferedElements, numBuffered, numBuffered + 1 ) ) + { + // If we haven't run against the lookahead limit and haven't reached + // the end of the stream, immediately trigger a new request. + + if( !mIsStopped && ( numBuffered + 1 ) < mMaxBufferedElements ) + _requestNext(); + + break; + } + } +} + +//----------------------------------------------------------------------------- + +template< typename T, typename Stream > +U32 AsyncBufferedInputStream< T, Stream >::read( ElementType* buffer, U32 num ) +{ + if( !num ) + return 0; + + U32 numRead = 0; + for( U32 i = 0; i < num; ++ i ) + { + // Try to pop a element off the buffered element list. + + ElementType element; + if( mBufferedElements.tryPopFront( element ) ) + { + buffer[ i ] = element; + numRead ++; + } + else + break; + } + + // Get the request chain going again, if it has stopped. + + while( 1 ) + { + U32 numBuffered = mNumBufferedElements; + U32 newNumBuffered = numBuffered - numRead; + + if( dCompareAndSwap( mNumBufferedElements, numBuffered, newNumBuffered ) ) + { + if( numBuffered == mMaxBufferedElements ) + _requestNext(); + + break; + } + } + + return numRead; +} + + +//============================================================================= +// AsyncSingleBufferedInputStream. +//============================================================================= + + +/// Asynchronous work item for reading an element from the source stream. +template< typename T, typename Stream = IInputStream< T >* > +class AsyncBufferedReadItem : public ThreadWorkItem +{ + public: + + typedef ThreadWorkItem Parent; + typedef ThreadSafeRef< AsyncBufferedInputStream< T, Stream > > AsyncStreamRef; + + protected: + + /// The issueing async state. + AsyncStreamRef mAsyncStream; + + /// + Stream mSourceStream; + + /// The element read from the stream. + T mElement; + + // WorkItem + virtual void execute() + { + if( Deref( mSourceStream ).read( &mElement, 1 ) ) + { + // Buffer the element. + + if( this->cancellationPoint() ) return; + mAsyncStream->_onArrival( mElement ); + } + } + virtual void onCancelled() + { + Parent::onCancelled(); + destructSingle( mElement ); + mAsyncStream = NULL; + } + + public: + + /// + AsyncBufferedReadItem( + const AsyncStreamRef& asyncStream, + ThreadPool::Context* context = NULL + ) + : Parent( context ), + mAsyncStream( asyncStream ), + mSourceStream( asyncStream->getSourceStream() ) + { + } + +}; + + +/// A stream filter that performs background read-aheads on its source stream +/// and buffers the results. +/// +/// As each element is read in an independent threaded operation, reading an +/// element should invole a certain amount of work for using this class to +/// make sense. +/// +/// @note For looping streams, the stream must implement the IResettable interface. +/// +template< typename T, typename Stream = IInputStream< T >*, class ReadItem = AsyncBufferedReadItem< T, Stream > > +class AsyncSingleBufferedInputStream : public AsyncBufferedInputStream< T, Stream > +{ + public: + + typedef AsyncBufferedInputStream< T, Stream > Parent; + typedef typename Parent::ElementType ElementType; + typedef typename Parent::SourceElementType SourceElementType; + typedef typename Parent::SourceStreamType SourceStreamType; + + protected: + + // AsyncBufferedInputStream. + virtual void _requestNext(); + + /// Create a new work item that reads the next element. + virtual void _newReadItem( ThreadSafeRef< ThreadWorkItem >& outRef ) + { + outRef = new ReadItem( this, this->mThreadContext ); + } + + public: + + /// Construct a new buffered stream reading from "source". + /// + /// @param stream The source stream from which to read the actual data elements. + /// @param numSourceElementsToRead Total number of elements to read from "stream". + /// @param numReadAhead Number of packets to read and buffer in advance. + /// @param isLooping If true, the packet stream will loop infinitely over the source stream. + /// @param pool The ThreadPool to use for asynchronous packet reads. + /// @param context The ThreadContext to place asynchronous packet reads in. + AsyncSingleBufferedInputStream( const Stream& stream, + U32 numSourceElementsToRead = 0, + U32 numReadAhead = Parent::DEFAULT_STREAM_LOOKAHEAD, + bool isLooping = false, + ThreadPool* pool = &ThreadPool::GLOBAL(), + ThreadContext* context = ThreadContext::ROOT_CONTEXT() ) + : Parent( stream, + numSourceElementsToRead, + numReadAhead, + isLooping, + pool, + context ) {} +}; + +//----------------------------------------------------------------------------- + +template< typename T, typename Stream, class ReadItem > +void AsyncSingleBufferedInputStream< T, Stream, ReadItem >::_requestNext() +{ + Stream& stream = this->getSourceStream(); + bool isEOS = !this->mNumRemainingSourceElements; + if( isEOS && this->mIsLooping ) + { + SourceStreamType* s = &Deref( stream ); + dynamic_cast< IResettable* >( s )->reset(); + isEOS = false; + } + else if( isEOS ) + return; + + //TODO: could scale priority depending on feed status + + // Queue a stream packet work item. + + if( !this->mIsLooping && this->mNumRemainingSourceElements != U32_MAX ) + -- this->mNumRemainingSourceElements; + + ThreadSafeRef< ThreadWorkItem > workItem; + _newReadItem( workItem ); + this->mThreadPool->queueWorkItem( workItem ); +} + +#endif // !_ASYNCBUFFEREDSTREAM_H_ diff --git a/Engine/source/platform/async/asyncPacketQueue.h b/Engine/source/platform/async/asyncPacketQueue.h new file mode 100644 index 000000000..8d30f0c96 --- /dev/null +++ b/Engine/source/platform/async/asyncPacketQueue.h @@ -0,0 +1,314 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _ASYNCPACKETQUEUE_H_ +#define _ASYNCPACKETQUEUE_H_ + +#ifndef _TFIXEDSIZEQUEUE_H_ +#include "core/util/tFixedSizeDeque.h" +#endif + +#ifndef _TSTREAM_H_ +#include "core/stream/tStream.h" +#endif + +#ifndef _TYPETRAITS_H_ +#include "platform/typetraits.h" +#endif + + +//#define DEBUG_SPEW + + +/// @file +/// Time-based packet streaming. +/// +/// The classes contained in this file can be used for any kind +/// of continuous playback that depends on discrete samplings of +/// a source stream (i.e. any kind of digital media streaming). + + + +//-------------------------------------------------------------------------- +// Async packet queue. +//-------------------------------------------------------------------------- + +/// Time-based packet stream queue. +/// +/// AsyncPacketQueue writes data packets to a consumer stream in sync to +/// a tick time source. Outdated packets may optionally be dropped automatically +/// by the queue. A fixed maximum number of packets can reside in the queue +/// concurrently at any one time. +/// +/// Be aware that using single item queues for synchronizing to a timer +/// will usually result in bad timing behavior when packet uploading takes +/// any non-trivial amount of time. +/// +/// @note While the queue associates a variable tick count with each +/// individual packet, the queue fill status is measured in number of +/// packets rather than in total tick time. +/// +/// @param Packet Value type of packets passed through this queue. +/// @param TimeSource Value type for time tick source to which the queue +/// is synchronized. +/// @param Consumer Value type of stream to which the packets are written. +/// +template< typename Packet, typename TimeSource = IPositionable< U32 >*, typename Consumer = IOutputStream< Packet >*, typename Tick = U32 > +class AsyncPacketQueue +{ + public: + + typedef void Parent; + + /// The type of data packets being streamed through this queue. + typedef typename TypeTraits< Packet >::BaseType PacketType; + + /// The type of consumer that receives the packets from this queue. + typedef typename TypeTraits< Consumer >::BaseType ConsumerType; + + /// + typedef typename TypeTraits< TimeSource >::BaseType TimeSourceType; + + /// Type for counting ticks. + typedef Tick TickType; + + protected: + + /// Information about the time slice covered by an + /// individual packet currently on the queue. + struct QueuedPacket + { + /// First tick contained in this packet. + TickType mStartTick; + + /// First tick *not* contained in this packet anymore. + TickType mEndTick; + + QueuedPacket( TickType start, TickType end ) + : mStartTick( start ), mEndTick( end ) {} + + /// Return the total number of ticks in this packet. + TickType getNumTicks() const + { + return ( mEndTick - mStartTick ); + } + }; + + typedef FixedSizeDeque< QueuedPacket > PacketQueue; + + /// If true, packets that have missed their proper queuing timeframe + /// will be dropped. If false, they will be queued nonetheless. + bool mDropPackets; + + /// Total number of ticks spanned by the total queue playback time. + /// If this is zero, the total queue time is considered to be infinite. + TickType mTotalTicks; + + /// Total number of ticks submitted to the queue so far. + TickType mTotalQueuedTicks; + + /// Queue that holds records for each packet currently in the queue. New packets + /// are added to back. + PacketQueue mPacketQueue; + + /// The time source to which we are sync'ing. + TimeSource mTimeSource; + + /// The output stream that this queue feeds into. + Consumer mConsumer; + + /// Total number of packets queued so far. + U32 mTotalQueuedPackets; + + public: + + /// Construct an AsyncPacketQueue of the given length. + /// + /// @param maxQueuedPackets The length of the queue in packets. Only a maximum of + /// 'maxQueuedPackets' packets can be concurrently in the queue at any one time. + /// @param timeSource The tick time source to which the queue synchronizes. + /// @param consumer The output stream that receives the packets in sync to timeSource. + /// @param totalTicks The total number of ticks that will be played back through the + /// queue; if 0, the length is considered indefinite. + /// @param dropPackets Whether the queue should drop outdated packets; if dropped, a + /// packet will not reach the consumer. + AsyncPacketQueue( U32 maxQueuedPackets, + TimeSource timeSource, + Consumer consumer, + TickType totalTicks = 0, + bool dropPackets = false ) + : mTotalTicks( totalTicks ), + mTotalQueuedTicks( 0 ), + mPacketQueue( maxQueuedPackets ), + mTimeSource( timeSource ), + mConsumer( consumer ), + mDropPackets( dropPackets ) + { + #ifdef TORQUE_DEBUG + mTotalQueuedPackets = 0; + #endif + } + + /// Return true if there are currently + bool isEmpty() const { return mPacketQueue.isEmpty(); } + + /// Return true if all packets have been streamed. + bool isAtEnd() const; + + /// Return true if the queue needs one or more new packets to be submitted. + bool needPacket(); + + /// Submit a data packet to the queue. + /// + /// @param packet The data packet. + /// @param packetTicks The duration of the packet in ticks. + /// @param isLast If true, the packet is the last one in the stream. + /// @param packetPos The absolute position of the packet in the stream; if this is not supplied + /// the packet is assumed to immediately follow the preceding packet. + /// + /// @return true if the packet has been queued or false if it has been dropped. + bool submitPacket( Packet packet, + TickType packetTicks, + bool isLast = false, + TickType packetPos = TypeTraits< TickType >::MAX ); + + /// Return the current playback position according to the time source. + TickType getCurrentTick() const { return Deref( mTimeSource ).getPosition(); } + + /// Return the total number of ticks that have been queued so far. + TickType getTotalQueuedTicks() const { return mTotalQueuedTicks; } + + /// Return the total number of packets that have been queued so far. + U32 getTotalQueuedPackets() const { return mTotalQueuedPackets; } +}; + +template< typename Packet, typename TimeSource, typename Consumer, typename Tick > +inline bool AsyncPacketQueue< Packet, TimeSource, Consumer, Tick >::isAtEnd() const +{ + // Never at end if infinite. + + if( !mTotalTicks ) + return false; + + // Otherwise, we're at end if we're past the total tick count. + + return ( getCurrentTick() >= mTotalTicks + && ( mDropPackets || mTotalQueuedTicks >= mTotalTicks ) ); +} + +template< typename Packet, typename TimeSource, typename Consumer, typename Tick > +bool AsyncPacketQueue< Packet, TimeSource, Consumer, Tick >::needPacket() +{ + // Never need more packets once we have reached the + // end. + + if( isAtEnd() ) + return false; + + // Always needs packets while the queue is not + // filled up completely. + + if( mPacketQueue.capacity() != 0 ) + return true; + + // Unqueue packets that have expired their playtime. + + TickType currentTick = getCurrentTick(); + while( mPacketQueue.size() && currentTick >= mPacketQueue.front().mEndTick ) + { + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[AsyncPacketQueue] expired packet #%i: %i-%i (tick: %i; queue: %i)", + mTotalQueuedPackets - mPacketQueue.size(), + U32( mPacketQueue.front().mStartTick ), + U32( mPacketQueue.front().mEndTick ), + U32( currentTick ), + mPacketQueue.size() ); + #endif + + mPacketQueue.popFront(); + } + + // Need more packets if the queue isn't full anymore. + + return ( mPacketQueue.capacity() != 0 ); +} + +template< typename Packet, typename TimeSource, typename Consumer, typename Tick > +bool AsyncPacketQueue< Packet, TimeSource, Consumer, Tick >::submitPacket( Packet packet, TickType packetTicks, bool isLast, TickType packetPos ) +{ + AssertFatal( mPacketQueue.capacity() != 0, + "AsyncPacketQueue::submitPacket() - Queue is full!" ); + + TickType packetStartPos; + TickType packetEndPos; + + if( packetPos != TypeTraits< TickType >::MAX ) + { + packetStartPos = packetPos; + packetEndPos = packetPos + packetTicks; + } + else + { + packetStartPos = mTotalQueuedTicks; + packetEndPos = mTotalQueuedTicks + packetTicks; + } + + // Check whether the packet is outdated, if enabled. + + bool dropPacket = false; + if( mDropPackets ) + { + TickType currentTick = getCurrentTick(); + if( currentTick >= packetEndPos ) + dropPacket = true; + } + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[AsyncPacketQueue] new packet #%i: %i-%i (ticks: %i, current: %i, queue: %i)%s", + mTotalQueuedPackets, + U32( mTotalQueuedTicks ), + U32( packetEndPos ), + U32( packetTicks ), + U32( getCurrentTick() ), + mPacketQueue.size(), + dropPacket ? " !! DROPPED !!" : "" ); + #endif + + // Queue the packet. + + if( !dropPacket ) + { + mPacketQueue.pushBack( QueuedPacket( packetStartPos, packetEndPos ) ); + Deref( mConsumer ).write( &packet, 1 ); + } + + mTotalQueuedTicks = packetEndPos; + if( isLast && !mTotalTicks ) + mTotalTicks = mTotalQueuedTicks; + + mTotalQueuedPackets ++; + + return !dropPacket; +} + +#undef DEBUG_SPEW +#endif // _ASYNCPACKETQUEUE_H_ diff --git a/Engine/source/platform/async/asyncPacketStream.h b/Engine/source/platform/async/asyncPacketStream.h new file mode 100644 index 000000000..459583e98 --- /dev/null +++ b/Engine/source/platform/async/asyncPacketStream.h @@ -0,0 +1,327 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _ASYNCPACKETSTREAM_H_ +#define _ASYNCPACKETSTREAM_H_ + +#ifndef _ASYNCBUFFEREDSTREAM_H_ + #include "platform/async/asyncBufferedStream.h" +#endif +#ifndef _RAWDATA_H_ + #include "core/util/rawData.h" +#endif +#ifndef _THREADPOOLASYNCIO_H_ + #include "platform/threads/threadPoolAsyncIO.h" +#endif + + +//#define DEBUG_SPEW + + +/// @file +/// Input stream filter definitions for turning linear streams into +/// streams that yield data in discrete packets using background +/// reads. + + +//-------------------------------------------------------------------------- +// Async stream packets. +//-------------------------------------------------------------------------- + +/// Stream packet read by an asynchronous packet stream. +template< typename T > +class AsyncPacket : public RawDataT< T > +{ + public: + + typedef RawDataT< T > Parent; + + AsyncPacket() + : mIndex( 0 ), mIsLast( false ), mSizeActual( 0 ) {} + AsyncPacket( T* data, U32 size, bool ownMemory = false ) + : Parent( data, size, ownMemory ), + mIndex( 0 ), mIsLast( false ), mSizeActual( 0 ) {} + + /// Running number in stream. + U32 mIndex; + + /// Number of items that have actually been read into the packet. + /// This may be less than "size" for end-of-stream packets in non-looping + /// streams. + /// + /// @note Extraneous space at the end of the packet will be cleared using + /// constructArray() calls. + U32 mSizeActual; + + /// If true this is the last packet in the stream. + bool mIsLast; +}; + +//-------------------------------------------------------------------------- +// Async packet streams. +//-------------------------------------------------------------------------- + +/// A packet stream turns a continuous stream of elements into a +/// stream of discrete packets of elements. +/// +/// All packets are of the exact same size even if, for end-of-stream +/// packets, they actually contain less data than their actual size. +/// Extraneous space is cleared. +/// +/// @note For looping streams, the stream must implement the +/// IResettable interface. +template< typename Stream, class Packet = AsyncPacket< typename TypeTraits< Stream >::BaseType::ElementType > > +class AsyncPacketBufferedInputStream : public AsyncBufferedInputStream< Packet*, Stream > +{ + public: + + typedef AsyncBufferedInputStream< Packet*, Stream > Parent; + typedef Packet PacketType; + typedef typename TypeTraits< Stream >::BaseType StreamType; + + protected: + + class PacketReadItem; + friend class PacketReadItem; // _onArrival + + /// Asynchronous work item for reading a packet from the source stream. + class PacketReadItem : public AsyncReadItem< typename Parent::SourceElementType, StreamType > + { + public: + + typedef AsyncReadItem< typename AsyncPacketBufferedInputStream< Stream, Packet >::SourceElementType, StreamType > Parent; + + PacketReadItem( const ThreadSafeRef< AsyncPacketBufferedInputStream< Stream, Packet > >& asyncStream, + PacketType* packet, + U32 numElements, + ThreadPool::Context* context = NULL ) + : Parent( asyncStream->getSourceStream(), numElements, 0, *packet, false, 0, context ), + mAsyncStream( asyncStream ), + mPacket( packet ) {} + + protected: + + typedef ThreadSafeRef< AsyncPacketBufferedInputStream< Stream, Packet > > AsyncPacketStreamPtr; + + /// The issueing async state. + AsyncPacketStreamPtr mAsyncStream; + + /// The packet that receives the data. + PacketType* mPacket; + + // WorkItem + virtual void execute() + { + Parent::execute(); + mPacket->mSizeActual += this->mNumElementsRead; + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[AsyncPacketStream] read %i elements into packet #%i with size %i", + this->mNumElementsRead, mPacket->mIndex, mPacket->size ); + #endif + + // Handle extraneous space at end of packet. + + if( this->cancellationPoint() ) return; + U32 numExtraElements = mPacket->size - this->mNumElementsRead; + if( numExtraElements ) + { + if( mAsyncStream->mIsLooping + && dynamic_cast< IResettable* >( &Deref( this->getStream() ) ) ) + { + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[AsyncPacketStream] resetting stream and reading %i more elements", numExtraElements ); + #endif + + // Wrap around and start re-reading from beginning of stream. + + dynamic_cast< IResettable* >( &Deref( this->getStream() ) )->reset(); + + this->mOffsetInBuffer += this->mNumElementsRead; + this->mOffsetInStream = 0; + this->mNumElements = numExtraElements; + + this->_prep(); + Parent::execute(); + + mPacket->mSizeActual += this->mNumElementsRead; + } + else + constructArray( &mPacket->data[ this->mNumElementsRead ], numExtraElements ); + } + + // Buffer the packet. + + if( this->cancellationPoint() ) return; + mAsyncStream->_onArrival( mPacket ); + } + virtual void onCancelled() + { + Parent::onCancelled(); + destructSingle< PacketType* >( mPacket ); + mAsyncStream = NULL; + } + }; + + typedef ThreadSafeRef< PacketReadItem > PacketReadItemRef; + + /// Number of elements to read per packet. + U32 mPacketSize; + + /// Running number of next stream packet. + U32 mNextPacketIndex; + + /// Total number of elements in the source stream. + U32 mNumTotalSourceElements; + + /// Create a new stream packet of the given size. + virtual PacketType* _newPacket( U32 packetSize ) { return constructSingle< PacketType* >( packetSize ); } + + /// Request the next packet from the underlying stream. + virtual void _requestNext(); + + /// Create a new work item that reads "numElements" into "packet". + virtual void _newReadItem( PacketReadItemRef& outRef, + PacketType* packet, + U32 numElements ) + { + outRef = new PacketReadItem( this, packet, numElements, this->mThreadContext ); + } + + public: + + /// Construct a new packet stream reading from "stream". + /// + /// @note If looping is used and "stream" is not read from the beginning, "stream" should + /// implement IPositionable or ISizeable so the async stream can tell how many elements + /// there actually are in the stream after resetting. + /// + /// @param stream The source stream from which to read the actual data elements. + /// @param packetSize Size of stream packets returned by the stream in number of elements. + /// @param numSourceElementsToRead Number of elements to read from "stream". + /// @param numReadAhead Number of packets to read and buffer in advance. + /// @param isLooping If true, the packet stream will loop infinitely over the source stream. + /// @param pool The ThreadPool to use for asynchronous packet reads. + /// @param context The ThreadContext to place asynchronous packet reads in. + AsyncPacketBufferedInputStream( const Stream& stream, + U32 packetSize, + U32 numSourceElementsToRead = 0, + U32 numReadAhead = Parent::DEFAULT_STREAM_LOOKAHEAD, + bool isLooping = false, + ThreadPool* pool = &ThreadPool::GLOBAL(), + ThreadContext* context = ThreadContext::ROOT_CONTEXT() ); + + /// @return the size of stream packets returned by this stream in number of elements. + U32 getPacketSize() const { return mPacketSize; } +}; + +template< typename Stream, class Packet > +AsyncPacketBufferedInputStream< Stream, Packet >::AsyncPacketBufferedInputStream + ( const Stream& stream, + U32 packetSize, + U32 numSourceElementsToRead, + U32 numReadAhead, + bool isLooping, + ThreadPool* threadPool, + ThreadContext* threadContext ) + : Parent( stream, numSourceElementsToRead, numReadAhead, isLooping, threadPool, threadContext ), + mPacketSize( packetSize ), + mNumTotalSourceElements( numSourceElementsToRead ), + mNextPacketIndex( 0 ) +{ + AssertFatal( mPacketSize > 0, + "AsyncPacketStream::AsyncPacketStream() - packet size cannot be zero" ); + + // Determine total number of elements in stream, if possible. + + IPositionable< U32 >* positionable = dynamic_cast< IPositionable< U32 >* >( &Deref( stream ) ); + if( positionable ) + mNumTotalSourceElements += positionable->getPosition(); + else + { + ISizeable< U32 >* sizeable = dynamic_cast< ISizeable< U32 >* >( &Deref( stream ) ); + if( sizeable ) + mNumTotalSourceElements = sizeable->getSize(); + } + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[AsyncPacketStream] %i remaining, %i total (%i packets)", + this->mNumRemainingSourceElements, mNumTotalSourceElements, + ( this->mNumRemainingSourceElements / mPacketSize ) + ( this->mNumRemainingSourceElements % mPacketSize ? 1 : 0 ) ); + #endif +} + +template< typename Stream, class Packet > +void AsyncPacketBufferedInputStream< Stream, Packet >::_requestNext() +{ + Stream& stream = this->getSourceStream(); + bool isEOS = !this->mNumRemainingSourceElements; + if( isEOS && this->mIsLooping ) + { + StreamType* s = &Deref( stream ); + IResettable* resettable = dynamic_cast< IResettable* >( s ); + if( resettable ) + { + resettable->reset(); + isEOS = false; + this->mNumRemainingSourceElements = mNumTotalSourceElements; + } + } + else if( isEOS ) + return; + + //TODO: scale priority depending on feed status + + // Allocate a packet. + + U32 numElements = mPacketSize; + PacketType* packet = _newPacket( numElements ); + packet->mIndex = mNextPacketIndex; + mNextPacketIndex ++; + + // Queue a stream packet work item. + + if( numElements >= this->mNumRemainingSourceElements ) + { + if( !this->mIsLooping ) + { + this->mNumRemainingSourceElements = 0; + packet->mIsLast = true; + } + else + this->mNumRemainingSourceElements = ( this->mNumTotalSourceElements - numElements + this->mNumRemainingSourceElements ); + } + else + this->mNumRemainingSourceElements -= numElements; + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[AsyncPacketStream] packet %i, %i remaining, %i total", + packet->mIndex, this->mNumRemainingSourceElements, mNumTotalSourceElements ); + #endif + + ThreadSafeRef< PacketReadItem > workItem; + _newReadItem( workItem, packet, numElements ); + this->mThreadPool->queueWorkItem( workItem ); +} + +#undef DEBUG_SPEW +#endif // !_ASYNCPACKETSTREAM_H_ diff --git a/Engine/source/platform/async/asyncUpdate.cpp b/Engine/source/platform/async/asyncUpdate.cpp new file mode 100644 index 000000000..ec3cd3474 --- /dev/null +++ b/Engine/source/platform/async/asyncUpdate.cpp @@ -0,0 +1,83 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/async/asyncUpdate.h" +#include "core/stream/tStream.h" + + +//----------------------------------------------------------------------------- +// AsyncUpdateList implementation. +//----------------------------------------------------------------------------- + +void AsyncUpdateList::process( S32 timeOut ) +{ + U32 endTime = 0; + if( timeOut != -1 ) + endTime = Platform::getRealMilliseconds() + timeOut; + + // Flush the process list. + + IPolled* ptr; + IPolled* firstProcessedPtr = 0; + + while( mUpdateList.tryPopFront( ptr ) ) + { + if( ptr == firstProcessedPtr ) + { + // We've wrapped around. Stop. + + mUpdateList.pushFront( ptr ); + break; + } + + if( ptr->update() ) + { + mUpdateList.pushBack( ptr ); + + if( !firstProcessedPtr ) + firstProcessedPtr = ptr; + } + + // Stop if we have exceeded our processing time budget. + + if( timeOut != -1 + && Platform::getRealMilliseconds() >= endTime ) + break; + } +} + +//-------------------------------------------------------------------------- +// AsyncUpdateThread implementation. +//-------------------------------------------------------------------------- + +void AsyncUpdateThread::run( void* ) +{ + _setName( getName() ); + + while( !checkForStop() ) + { + _waitForEventAndReset(); + + if( !checkForStop() ) + mUpdateList->process(); + } +} diff --git a/Engine/source/platform/async/asyncUpdate.h b/Engine/source/platform/async/asyncUpdate.h new file mode 100644 index 000000000..1bb416774 --- /dev/null +++ b/Engine/source/platform/async/asyncUpdate.h @@ -0,0 +1,162 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _ASYNCUPDATE_H_ +#define _ASYNCUPDATE_H_ + +#ifndef _PLATFORM_THREADS_THREAD_H_ +# include "platform/threads/thread.h" +#endif +#ifndef _THREADSAFEREFCOUNT_H_ +# include "platform/threads/threadSafeRefCount.h" +#endif +#ifndef _THREADSAFEDEQUE_H_ +# include "platform/threads/threadSafeDeque.h" +#endif + + +class IPolled; + +//-------------------------------------------------------------------------- +// Async update list. +//-------------------------------------------------------------------------- + +/// This structure keeps track of the objects that need +/// updating. +class AsyncUpdateList : public ThreadSafeRefCount< AsyncUpdateList > +{ + protected: + + typedef ThreadSafeDeque< IPolled* > UpdateList; + + /// List of structures currently in the update loop. + UpdateList mUpdateList; + + public: + + virtual ~AsyncUpdateList() {} + + /// Update the structures currently on the processing list. + /// + /// @param timeOut Soft limit in milliseconds on the time + /// spent on flushing the list. Default of -1 means no + /// limit and function will only return, if update list + /// has been fully flushed. + virtual void process( S32 timeOut = -1 ); + + /// Add the structure to the update list. It will stay + /// on this list, until its update() method returns false. + /// + /// @note This can be called on different threads. + virtual void add( IPolled* ptr ) + { + mUpdateList.pushBack( ptr ); + } +}; + +//-------------------------------------------------------------------------- +// Async update thread. +//-------------------------------------------------------------------------- + +/// Abstract baseclass for async update threads. +class AsyncUpdateThread : public Thread, public ThreadSafeRefCount< AsyncUpdateThread > +{ + public: + + typedef Thread Parent; + + protected: + + /// Name of this thread. + String mName; + + /// Platform-dependent event data. + void* mUpdateEvent; + + /// The update list processed on this thread. + ThreadSafeRef< AsyncUpdateList > mUpdateList; + + /// Wait for an update event being triggered and + /// immediately reset the event. + /// + /// @note Note that this must be an atomic operation to avoid + /// a race condition. Immediately resetting the event shields + /// us from event releases happening during us updating getting + /// ignored. + virtual void _waitForEventAndReset(); + + public: + + /// Create the update thread. + /// The thread won't immediately start (we have virtual functions + /// so construction needs to finish first) and will not auto-delete + /// itself. + AsyncUpdateThread( String name, AsyncUpdateList* updateList ); + + virtual ~AsyncUpdateThread(); + + virtual void run( void* ); + + /// Trigger the update event to notify the thread about + /// pending updates. + virtual void triggerUpdate(); + + /// + const String& getName() const { return mName; } + + /// + void* getUpdateEvent() const { return mUpdateEvent; } +}; + +/// Extension to update thread that also does automatic +/// periodic updates. +class AsyncPeriodicUpdateThread : public AsyncUpdateThread +{ + typedef AsyncUpdateThread Parent; + + protected: + + /// Platform-dependent timer event. + void* mUpdateTimer; + + /// Time between periodic updates in milliseconds. + U32 mIntervalMS; + + virtual void _waitForEventAndReset(); + + public: + + enum + { + /// Default interval between periodic updates in milliseconds. + DEFAULT_UPDATE_INTERVAL = 4000 + }; + + /// + AsyncPeriodicUpdateThread( String name, + AsyncUpdateList* updateList, + U32 intervalMS = DEFAULT_UPDATE_INTERVAL ); + + virtual ~AsyncPeriodicUpdateThread(); +}; + +#endif // _TORQUE_CORE_ASYNC_ASYNCUPDATE_H_ diff --git a/Engine/source/platform/event.h b/Engine/source/platform/event.h new file mode 100644 index 000000000..dfe7bc206 --- /dev/null +++ b/Engine/source/platform/event.h @@ -0,0 +1,392 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +/// @file +/// Library-wide input events +/// +/// All external events are converted into system events, which are defined +/// in this file. + +/// +#ifndef _EVENT_H_ +#define _EVENT_H_ + +#include "platform/types.h" +#include "core/util/journal/journaledSignal.h" + +/// @defgroup input_constants Input system constants +/// @{ + +/// Input event constants: +enum InputObjectInstances +{ + KEY_NULL = 0x000, ///< Invalid KeyCode + KEY_BACKSPACE = 0x001, + KEY_TAB = 0x002, + KEY_RETURN = 0x003, + KEY_CONTROL = 0x004, + KEY_ALT = 0x005, + KEY_SHIFT = 0x006, + KEY_PAUSE = 0x007, + KEY_CAPSLOCK = 0x008, + KEY_ESCAPE = 0x009, + KEY_SPACE = 0x00a, + KEY_PAGE_DOWN = 0x00b, + KEY_PAGE_UP = 0x00c, + KEY_END = 0x00d, + KEY_HOME = 0x00e, + KEY_LEFT = 0x00f, + KEY_UP = 0x010, + KEY_RIGHT = 0x011, + KEY_DOWN = 0x012, + KEY_PRINT = 0x013, + KEY_INSERT = 0x014, + KEY_DELETE = 0x015, + KEY_HELP = 0x016, + + KEY_0 = 0x017, + KEY_1 = 0x018, + KEY_2 = 0x019, + KEY_3 = 0x01a, + KEY_4 = 0x01b, + KEY_5 = 0x01c, + KEY_6 = 0x01d, + KEY_7 = 0x01e, + KEY_8 = 0x01f, + KEY_9 = 0x020, + + KEY_A = 0x021, + KEY_B = 0x022, + KEY_C = 0x023, + KEY_D = 0x024, + KEY_E = 0x025, + KEY_F = 0x026, + KEY_G = 0x027, + KEY_H = 0x028, + KEY_I = 0x029, + KEY_J = 0x02a, + KEY_K = 0x02b, + KEY_L = 0x02c, + KEY_M = 0x02d, + KEY_N = 0x02e, + KEY_O = 0x02f, + KEY_P = 0x030, + KEY_Q = 0x031, + KEY_R = 0x032, + KEY_S = 0x033, + KEY_T = 0x034, + KEY_U = 0x035, + KEY_V = 0x036, + KEY_W = 0x037, + KEY_X = 0x038, + KEY_Y = 0x039, + KEY_Z = 0x03a, + + KEY_TILDE = 0x03b, + KEY_MINUS = 0x03c, + KEY_EQUALS = 0x03d, + KEY_LBRACKET = 0x03e, + KEY_RBRACKET = 0x03f, + KEY_BACKSLASH = 0x040, + KEY_SEMICOLON = 0x041, + KEY_APOSTROPHE = 0x042, + KEY_COMMA = 0x043, + KEY_PERIOD = 0x044, + KEY_SLASH = 0x045, + KEY_NUMPAD0 = 0x046, + KEY_NUMPAD1 = 0x047, + KEY_NUMPAD2 = 0x048, + KEY_NUMPAD3 = 0x049, + KEY_NUMPAD4 = 0x04a, + KEY_NUMPAD5 = 0x04b, + KEY_NUMPAD6 = 0x04c, + KEY_NUMPAD7 = 0x04d, + KEY_NUMPAD8 = 0x04e, + KEY_NUMPAD9 = 0x04f, + KEY_MULTIPLY = 0x050, + KEY_ADD = 0x051, + KEY_SEPARATOR = 0x052, + KEY_SUBTRACT = 0x053, + KEY_DECIMAL = 0x054, + KEY_DIVIDE = 0x055, + KEY_NUMPADENTER = 0x056, + + KEY_F1 = 0x057, + KEY_F2 = 0x058, + KEY_F3 = 0x059, + KEY_F4 = 0x05a, + KEY_F5 = 0x05b, + KEY_F6 = 0x05c, + KEY_F7 = 0x05d, + KEY_F8 = 0x05e, + KEY_F9 = 0x05f, + KEY_F10 = 0x060, + KEY_F11 = 0x061, + KEY_F12 = 0x062, + KEY_F13 = 0x063, + KEY_F14 = 0x064, + KEY_F15 = 0x065, + KEY_F16 = 0x066, + KEY_F17 = 0x067, + KEY_F18 = 0x068, + KEY_F19 = 0x069, + KEY_F20 = 0x06a, + KEY_F21 = 0x06b, + KEY_F22 = 0x06c, + KEY_F23 = 0x06d, + KEY_F24 = 0x06e, + + KEY_NUMLOCK = 0x06f, + KEY_SCROLLLOCK = 0x070, + KEY_LCONTROL = 0x071, + KEY_RCONTROL = 0x072, + KEY_LALT = 0x073, + KEY_RALT = 0x074, + KEY_LSHIFT = 0x075, + KEY_RSHIFT = 0x076, + KEY_WIN_LWINDOW = 0x077, + KEY_WIN_RWINDOW = 0x078, + KEY_WIN_APPS = 0x079, + KEY_OEM_102 = 0x080, + + KEY_MAC_OPT = 0x090, + KEY_MAC_LOPT = 0x091, + KEY_MAC_ROPT = 0x092, + + KEY_BUTTON0 = 0x0100, + KEY_BUTTON1 = 0x0101, + KEY_BUTTON2 = 0x0102, + KEY_BUTTON3 = 0x0103, + KEY_BUTTON4 = 0x0104, + KEY_BUTTON5 = 0x0105, + KEY_BUTTON6 = 0x0106, + KEY_BUTTON7 = 0x0107, + KEY_BUTTON8 = 0x0108, + KEY_BUTTON9 = 0x0109, + KEY_BUTTON10 = 0x010A, + KEY_BUTTON11 = 0x010B, + KEY_BUTTON12 = 0x010C, + KEY_BUTTON13 = 0x010D, + KEY_BUTTON14 = 0x010E, + KEY_BUTTON15 = 0x010F, + KEY_BUTTON16 = 0x0110, + KEY_BUTTON17 = 0x0111, + KEY_BUTTON18 = 0x0112, + KEY_BUTTON19 = 0x0113, + KEY_BUTTON20 = 0x0114, + KEY_BUTTON21 = 0x0115, + KEY_BUTTON22 = 0x0116, + KEY_BUTTON23 = 0x0117, + KEY_BUTTON24 = 0x0118, + KEY_BUTTON25 = 0x0119, + KEY_BUTTON26 = 0x011A, + KEY_BUTTON27 = 0x011B, + KEY_BUTTON28 = 0x011C, + KEY_BUTTON29 = 0x011D, + KEY_BUTTON30 = 0x011E, + KEY_BUTTON31 = 0x011F, + KEY_ANYKEY = 0xfffe, + + /// Joystick event codes. + SI_XPOV = 0x204, + SI_YPOV = 0x205, + SI_UPOV = 0x206, + SI_DPOV = 0x207, + SI_LPOV = 0x208, + SI_RPOV = 0x209, + SI_XAXIS = 0x20B, + SI_YAXIS = 0x20C, + SI_ZAXIS = 0x20D, + SI_RXAXIS = 0x20E, + SI_RYAXIS = 0x20F, + SI_RZAXIS = 0x210, + SI_SLIDER = 0x211, + SI_XPOV2 = 0x212, + SI_YPOV2 = 0x213, + SI_UPOV2 = 0x214, + SI_DPOV2 = 0x215, + SI_LPOV2 = 0x216, + SI_RPOV2 = 0x217, + + XI_CONNECT = 0x300, + XI_THUMBLX = 0x301, + XI_THUMBLY = 0x302, + XI_THUMBRX = 0x303, + XI_THUMBRY = 0x304, + XI_LEFT_TRIGGER = 0x305, + XI_RIGHT_TRIGGER = 0x306, + + /*XI_DPAD_UP = 0x307, + XI_DPAD_DOWN = 0x308, + XI_DPAD_LEFT = 0x309, + XI_DPAD_RIGHT = 0x310,*/ + + XI_START = 0x311, + XI_BACK = 0x312, + XI_LEFT_THUMB = 0x313, + XI_RIGHT_THUMB = 0x314, + XI_LEFT_SHOULDER = 0x315, + XI_RIGHT_SHOULDER = 0x316, + + XI_A = 0x317, + XI_B = 0x318, + XI_X = 0x319, + XI_Y = 0x320, +}; + +/// Input device types +enum InputDeviceTypes +{ + UnknownDeviceType, + MouseDeviceType, + KeyboardDeviceType, + JoystickDeviceType, + GamepadDeviceType, + XInputDeviceType, + + NUM_INPUT_DEVICE_TYPES +}; + +/// Device Event Action Types +enum InputActionType +{ + /// Button was depressed. + SI_MAKE = 0x01, + + /// Button was released. + SI_BREAK = 0x02, + + /// An axis moved. + SI_MOVE = 0x03, + + /// A key repeat occurred. Happens in between a SI_MAKE and SI_BREAK. + SI_REPEAT = 0x04, +}; + +///Device Event Types +enum InputEventType +{ + SI_UNKNOWN = 0x01, + SI_BUTTON = 0x02, + SI_POV = 0x03, + SI_AXIS = 0x04, + SI_KEY = 0x0A, +}; + +/// Wildcard match used by the input system. +#define SI_ANY 0xff + +// Modifier Keys +enum InputModifiers +{ + /// shift and ctrl are the same between platforms. + SI_LSHIFT = BIT(0), + SI_RSHIFT = BIT(1), + SI_SHIFT = (SI_LSHIFT|SI_RSHIFT), + SI_LCTRL = BIT(2), + SI_RCTRL = BIT(3), + SI_CTRL = (SI_LCTRL|SI_RCTRL), + + /// win altkey, mapped to mac cmdkey. + SI_LALT = BIT(4), + SI_RALT = BIT(5), + SI_ALT = (SI_LALT|SI_RALT), + + /// mac optionkey + SI_MAC_LOPT = BIT(6), + SI_MAC_ROPT = BIT(7), + SI_MAC_OPT = (SI_MAC_LOPT|SI_MAC_ROPT), + + /// modifier keys used for common operations +#if defined(TORQUE_OS_MAC) + SI_COPYPASTE = SI_ALT, + SI_MULTISELECT = SI_ALT, + SI_RANGESELECT = SI_SHIFT, + SI_PRIMARY_ALT = SI_MAC_OPT, ///< Primary key used for toggling into alternates of commands. + SI_PRIMARY_CTRL = SI_ALT, ///< Primary key used for triggering commands. +#else + SI_COPYPASTE = SI_CTRL, + SI_MULTISELECT = SI_CTRL, + SI_RANGESELECT = SI_SHIFT, + SI_PRIMARY_ALT = SI_ALT, + SI_PRIMARY_CTRL = SI_CTRL, +#endif + /// modfier key used in conjunction w/ arrow keys to move cursor to next word +#if defined(TORQUE_OS_MAC) + SI_WORDJUMP = SI_MAC_OPT, +#else + SI_WORDJUMP = SI_CTRL, +#endif + /// modifier key used in conjunction w/ arrow keys to move cursor to beginning / end of line + SI_LINEJUMP = SI_ALT, + + /// modifier key used in conjunction w/ home & end to jump to the top or bottom of a document +#if defined(TORQUE_OS_MAC) + SI_DOCJUMP = SI_ANY, +#else + SI_DOCJUMP = SI_CTRL, +#endif +}; + +/// @} + + +/// Generic input event. +struct InputEventInfo +{ + InputEventInfo() + { + deviceInst = 0; + fValue = 0.f; + deviceType = (InputDeviceTypes)0; + objType = (InputEventType)0; + ascii = 0; + objInst = (InputObjectInstances)0; + action = (InputActionType)0; + modifier = (InputModifiers)0; + } + + /// Device instance: joystick0, joystick1, etc + U32 deviceInst; + + /// Value ranges from -1.0 to 1.0 + F32 fValue; + + /// What was the action? (MAKE/BREAK/MOVE) + InputActionType action; + InputDeviceTypes deviceType; + InputEventType objType; + InputObjectInstances objInst; + + /// ASCII character code if this is a keyboard event. + U16 ascii; + + /// Modifiers to action: SI_LSHIFT, SI_LCTRL, etc. + InputModifiers modifier; + + inline void postToSignal(InputEvent &ie) + { + ie.trigger(deviceInst, fValue, deviceType, objType, ascii, objInst, action, modifier); + } +}; + + +#endif diff --git a/Engine/source/platform/menus/menuBar.cpp b/Engine/source/platform/menus/menuBar.cpp new file mode 100644 index 000000000..d12f209a9 --- /dev/null +++ b/Engine/source/platform/menus/menuBar.cpp @@ -0,0 +1,127 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "platform/menus/menuBar.h" +#include "platform/menus/popupMenu.h" +#include "gui/core/guiCanvas.h" + +//----------------------------------------------------------------------------- +// Constructor/Destructor +//----------------------------------------------------------------------------- + +MenuBar::MenuBar() +{ + createPlatformPopupMenuData(); + + mCanvas = NULL; +} + +MenuBar::~MenuBar() +{ + removeFromCanvas(); + + deletePlatformPopupMenuData(); +} + +IMPLEMENT_CONOBJECT(MenuBar); + +ConsoleDocClass( MenuBar, + "@brief Used for rendering platform menu bars\n\n" + "Internal use only\n\n" + "@internal" +); + +//----------------------------------------------------------------------------- +// Public Methods +//----------------------------------------------------------------------------- + +void MenuBar::addObject(SimObject *obj) +{ + Parent::addObject(obj); + updateMenuBar(dynamic_cast(obj)); +} + +void MenuBar::removeObject(SimObject *obj) +{ + Parent::removeObject(obj); + updateMenuBar(dynamic_cast(obj)); +} + +void MenuBar::insertObject(SimObject *obj, S32 pos) +{ + Parent::addObject(obj); + + if(pos >= size()) + pos = size() - 1; + + if(pos < size()) + { + if(pos < 0) pos = 0; + Parent::reOrder(obj, at(pos)); + } + updateMenuBar(dynamic_cast(obj)); +} + +void MenuBar::pushObject(SimObject *obj) +{ + Parent::pushObject(obj); + updateMenuBar(dynamic_cast(obj)); +} + +void MenuBar::popObject() +{ + Parent::popObject(); + updateMenuBar(); +} + +bool MenuBar::reOrder(SimObject *obj, SimObject *target /*= 0*/) +{ + bool ret = Parent::reOrder(obj, target); + if(ret) + updateMenuBar(dynamic_cast(obj)); + return ret; +} + +//----------------------------------------------------------------------------- +// Console Methods +//----------------------------------------------------------------------------- + +ConsoleMethod(MenuBar, attachToCanvas, void, 4, 4, "(GuiCanvas, pos)") +{ + object->attachToCanvas(dynamic_cast(Sim::findObject(argv[2])), dAtoi(argv[3])); +} + +ConsoleMethod(MenuBar, removeFromCanvas, void, 2, 2, "()") +{ + object->removeFromCanvas(); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod(MenuBar, insert, void, 4, 4,"(object, pos) insert object at position") +{ + SimObject* pObject = Sim::findObject(argv[2]); + + if(pObject) + object->insertObject(pObject, dAtoi(argv[3])); +} diff --git a/Engine/source/platform/menus/menuBar.h b/Engine/source/platform/menus/menuBar.h new file mode 100644 index 000000000..0e0e64602 --- /dev/null +++ b/Engine/source/platform/menus/menuBar.h @@ -0,0 +1,71 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/simBase.h" + +#ifndef _MENUBAR_H_ +#define _MENUBAR_H_ + +// Forward Refs +class PlatformMenuBarData; +class PopupMenu; +class GuiCanvas; + +class MenuBar : public SimSet +{ + typedef SimSet Parent; + +protected: + PlatformMenuBarData *mData; + GuiCanvas *mCanvas; + + /// Update the native menu bar to ensure consistency with the set + void updateMenuBar(PopupMenu *menu = NULL); + + void createPlatformPopupMenuData(); + void deletePlatformPopupMenuData(); + +public: + MenuBar(); + virtual ~MenuBar(); + DECLARE_CONOBJECT(MenuBar); + + /// Attach this menu bar to the native menu bar + void attachToCanvas(GuiCanvas *owner, S32 pos); + /// Remove this menu bar from the native menu bar + void removeFromCanvas(); + + /// Returns true if this menu is attached to the menu bar + bool isAttachedToCanvas() { return mCanvas != NULL; } + + virtual void insertObject(SimObject *obj, S32 pos); + + // Overridden SimSet methods to ensure menu bar consistency when attached + virtual void addObject(SimObject *obj); + virtual void removeObject(SimObject *obj); + virtual void pushObject(SimObject *obj); + virtual void popObject(); + + virtual bool reOrder(SimObject *obj, SimObject *target = 0); +}; + +#endif // _MENUBAR_H_ diff --git a/Engine/source/platform/menus/popupMenu.cpp b/Engine/source/platform/menus/popupMenu.cpp new file mode 100644 index 000000000..9d567ca65 --- /dev/null +++ b/Engine/source/platform/menus/popupMenu.cpp @@ -0,0 +1,270 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/menus/popupMenu.h" +#include "console/consoleTypes.h" +#include "gui/core/guiCanvas.h" +#include "core/util/safeDelete.h" + +static U32 sMaxPopupGUID = 0; +PopupMenuEvent PopupMenu::smPopupMenuEvent; +bool PopupMenu::smSelectionEventHandled = false; + +/// Event class used to remove popup menus from the event notification in a safe way +class PopUpNotifyRemoveEvent : public SimEvent +{ +public: + void process(SimObject *object) + { + PopupMenu::smPopupMenuEvent.remove((PopupMenu *)object, &PopupMenu::handleSelectEvent); + } +}; + +//----------------------------------------------------------------------------- +// Constructor/Destructor +//----------------------------------------------------------------------------- + +PopupMenu::PopupMenu() : mCanvas(NULL) +{ + createPlatformPopupMenuData(); + + mSubmenus = new SimSet; + mSubmenus->registerObject(); + + mBarTitle = StringTable->insert(""); + mIsPopup = false; + + mPopupGUID = sMaxPopupGUID++; +} + +PopupMenu::~PopupMenu() +{ + // This searches the menu bar so is safe to call for menus + // that aren't on it, since nothing will happen. + removeFromMenuBar(); + + SimSet::iterator i; + while((i = mSubmenus->begin()) != mSubmenus->end()) + { + (*i)->deleteObject(); + } + + mSubmenus->deleteObject(); + deletePlatformPopupMenuData(); + + PopupMenu::smPopupMenuEvent.remove(this, &PopupMenu::handleSelectEvent); +} + +IMPLEMENT_CONOBJECT(PopupMenu); + +ConsoleDocClass( PopupMenu, + "@brief PopupMenu represents a system menu.\n\n" + "You can add menu items to the menu, but there is no torque object associated " + "with these menu items, they exist only in a platform specific manner.\n\n" + "@note Internal use only\n\n" + "@internal" +); + + +//----------------------------------------------------------------------------- + +void PopupMenu::initPersistFields() +{ + addField("isPopup", TypeBool, Offset(mIsPopup, PopupMenu), "true if this is a pop-up/context menu. defaults to false."); + addField("barTitle", TypeCaseString, Offset(mBarTitle, PopupMenu), "the title of this menu when attached to a menu bar"); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +bool PopupMenu::onAdd() +{ + if(! Parent::onAdd()) + return false; + + createPlatformMenu(); + + Con::executef(this, "onAdd"); + return true; +} + +void PopupMenu::onRemove() +{ + Con::executef(this, "onRemove"); + + Parent::onRemove(); +} + +//----------------------------------------------------------------------------- + +void PopupMenu::onMenuSelect() +{ + Con::executef(this, "onMenuSelect"); +} + +//----------------------------------------------------------------------------- + +void PopupMenu::handleSelectEvent(U32 popID, U32 command) +{ + if (popID == mPopupGUID && canHandleID(command)) + if (handleSelect(command)) + smSelectionEventHandled = true; +} + +//----------------------------------------------------------------------------- + +void PopupMenu::onAttachToMenuBar(GuiCanvas *canvas, S32 pos, const char *title) +{ + mCanvas = canvas; + + // Attached menus must be notified of menu events + smPopupMenuEvent.notify(this, &PopupMenu::handleSelectEvent); + + // Pass on to sub menus + for(SimSet::iterator i = mSubmenus->begin();i != mSubmenus->end();++i) + { + PopupMenu *mnu = dynamic_cast(*i); + if(mnu == NULL) + continue; + + mnu->onAttachToMenuBar(canvas, pos, title); + } + + // Call script + if(isProperlyAdded()) + Con::executef(this, "onAttachToMenuBar", Con::getIntArg(canvas ? canvas->getId() : 0), Con::getIntArg(pos), title); +} + +void PopupMenu::onRemoveFromMenuBar(GuiCanvas *canvas) +{ + mCanvas = NULL; + + // We are no longer interested in select events, remove ourselves from the notification list in a safe way + Sim::postCurrentEvent(this, new PopUpNotifyRemoveEvent()); + + // Pass on to sub menus + for(SimSet::iterator i = mSubmenus->begin();i != mSubmenus->end();++i) + { + PopupMenu *mnu = dynamic_cast(*i); + if(mnu == NULL) + continue; + + mnu->onRemoveFromMenuBar(canvas); + } + + // Call script + if(isProperlyAdded()) + Con::executef(this, "onRemoveFromMenuBar", Con::getIntArg(canvas ? canvas->getId() : 0)); +} + +//----------------------------------------------------------------------------- + +bool PopupMenu::onMessageReceived(StringTableEntry queue, const char* event, const char* data) +{ + return Con::executef(this, "onMessageReceived", queue, event, data); +} + + +bool PopupMenu::onMessageObjectReceived(StringTableEntry queue, Message *msg ) +{ + return Con::executef(this, "onMessageReceived", queue, Con::getIntArg(msg->getId())); +} + +//----------------------------------------------------------------------------- +// Console Methods +//----------------------------------------------------------------------------- + +ConsoleMethod(PopupMenu, insertItem, S32, 3, 5, "(pos[, title][, accelerator])") +{ + return object->insertItem(dAtoi(argv[2]), argc < 4 ? NULL : argv[3], argc < 5 ? "" : argv[4]); +} + +ConsoleMethod(PopupMenu, removeItem, void, 3, 3, "(pos)") +{ + object->removeItem(dAtoi(argv[2])); +} + +ConsoleMethod(PopupMenu, insertSubMenu, S32, 5, 5, "(pos, title, subMenu)") +{ + PopupMenu *mnu = dynamic_cast(Sim::findObject(argv[4])); + if(mnu == NULL) + { + Con::errorf("PopupMenu::insertSubMenu - Invalid PopupMenu object specified for submenu"); + return -1; + } + return object->insertSubMenu(dAtoi(argv[2]), argv[3], mnu); +} + +ConsoleMethod(PopupMenu, setItem, bool, 4, 5, "(pos, title[, accelerator])") +{ + return object->setItem(dAtoi(argv[2]), argv[3], argc < 5 ? "" : argv[4]); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod(PopupMenu, enableItem, void, 4, 4, "(pos, enabled)") +{ + object->enableItem(dAtoi(argv[2]), dAtob(argv[3])); +} + +ConsoleMethod(PopupMenu, checkItem, void, 4, 4, "(pos, checked)") +{ + object->checkItem(dAtoi(argv[2]), dAtob(argv[3])); +} + +ConsoleMethod(PopupMenu, checkRadioItem, void, 5, 5, "(firstPos, lastPos, checkPos)") +{ + object->checkRadioItem(dAtoi(argv[2]), dAtoi(argv[3]), dAtoi(argv[4])); +} + +ConsoleMethod(PopupMenu, isItemChecked, bool, 3, 3, "(pos)") +{ + return object->isItemChecked(dAtoi(argv[2])); +} + +ConsoleMethod(PopupMenu, getItemCount, S32, 2, 2, "()") +{ + return object->getItemCount(); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod(PopupMenu, attachToMenuBar, void, 5, 5, "(GuiCanvas, pos, title)") +{ + object->attachToMenuBar(dynamic_cast(Sim::findObject(argv[2])),dAtoi(argv[3]), argv[4]); +} + +ConsoleMethod(PopupMenu, removeFromMenuBar, void, 2, 2, "()") +{ + object->removeFromMenuBar(); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod(PopupMenu, showPopup, void, 3, 5, "(Canvas,[x, y])") +{ + GuiCanvas *pCanvas = dynamic_cast(Sim::findObject(argv[2])); + S32 x = argc >= 4 ? dAtoi(argv[3]) : -1; + S32 y = argc >= 5 ? dAtoi(argv[4]) : -1; + object->showPopup(pCanvas, x, y); +} diff --git a/Engine/source/platform/menus/popupMenu.h b/Engine/source/platform/menus/popupMenu.h new file mode 100644 index 000000000..c4080aeb0 --- /dev/null +++ b/Engine/source/platform/menus/popupMenu.h @@ -0,0 +1,189 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#include "console/simBase.h" +#include "core/util/tVector.h" +#include "util/messaging/dispatcher.h" +#include "gui/core/guiCanvas.h" + +#ifndef _POPUPMENU_H_ +#define _POPUPMENU_H_ + +// Forward ref used by the platform code +struct PlatformPopupMenuData; +class MenuBar; + +// PopupMenu represents a menu. +// You can add menu items to the menu, but there is no torque object associated +// with these menu items, they exist only in a platform specific manner. +class PopupMenu : public SimObject, public virtual Dispatcher::IMessageListener +{ + typedef SimObject Parent; + + friend class MenuBar; + +private: + /// Used by MenuBar to attach the menu to the menu bar. Do not use anywhere else. + void attachToMenuBar(GuiCanvas *owner, S32 pos); + +protected: + PlatformPopupMenuData *mData; + + SimSet *mSubmenus; + SimObjectPtr mCanvas; + + StringTableEntry mBarTitle; + + U32 mPopupGUID; + + bool mIsPopup; + +public: + PopupMenu(); + virtual ~PopupMenu(); + void createPlatformPopupMenuData(); + void deletePlatformPopupMenuData(); + + DECLARE_CONOBJECT(PopupMenu); + + static void initPersistFields(); + + virtual bool onAdd(); + virtual void onRemove(); + + static PopupMenuEvent smPopupMenuEvent; + static bool smSelectionEventHandled; /// Set to true if any menu or submenu handles a selection event + + /// Creates the platform specific menu object, a peer to this object. + /// The platform menu *must* exist before calling any method that manipulates + /// menu items or displays the menu. + /// implementd on a per-platform basis. + void createPlatformMenu(); + + void setBarTitle(const char * val) { mBarTitle = StringTable->insert(val, true); } + StringTableEntry getBarTitle() const { return mBarTitle; } + + /// pass NULL for @p title to insert a separator + /// returns the menu item's ID, or -1 on failure. + /// implementd on a per-platform basis. + /// TODO: factor out common code + S32 insertItem(S32 pos, const char *title, const char* accelerator); + + /// Sets the name title and accelerator for + /// an existing item. + bool setItem(S32 pos, const char *title, const char* accelerator); + + /// pass NULL for @p title to insert a separator + /// returns the menu item's ID, or -1 on failure. + /// adds the submenu to the mSubmenus vector. + /// implemented on a per-platform basis. + /// TODO: factor out common code + S32 insertSubMenu(S32 pos, const char *title, PopupMenu *submenu); + + /// remove the menu item at @p itemPos + /// if the item has a submenu, it is removed from the mSubmenus list. + /// implemented on a per-platform basis. + /// TODO: factor out common code + void removeItem(S32 itemPos); + + /// implemented on a per-platform basis. + void enableItem(S32 pos, bool enable); + /// implemented on a per-platform basis. + void checkItem(S32 pos, bool checked); + + /// All items at positions firstPos through lastPos are unchecked, and the + /// item at checkPos is checked. + /// implemented on a per-platform basis. + void checkRadioItem(S32 firstPos, S32 lastPos, S32 checkPos); + bool isItemChecked(S32 pos); + + /// Returns the number of items in the menu. + U32 getItemCount(); + + /// Returns the popup GUID + U32 getPopupGUID() { return mPopupGUID; } + + //----------------------------------------------------------------------------- + // New code should not use these methods directly, use the menu bar instead. + // + // They remain for compatibility with old code and will be changing/going away + // once the existing code is moved over to the menu bar. + //----------------------------------------------------------------------------- + + /// Places this menu in the menu bar of the application's main window. + /// @param owner The GuiCanvas that owns the PlatformWindow that this call is associated with + /// @param pos The relative position at which to place the menu. + /// @param title The name of the menu + void attachToMenuBar(GuiCanvas *owner, S32 pos, const char *title); + + /// Removes this menu from the menu bar. + void removeFromMenuBar(); + + //----------------------------------------------------------------------------- + + /// Called when the menu has been attached to the menu bar + void onAttachToMenuBar(GuiCanvas *canvas, S32 pos, const char *title); + + /// Called when the menu has been removed from the menu bar + void onRemoveFromMenuBar(GuiCanvas *canvas); + + /// Returns the position index of this menu on the bar. + S32 getPosOnMenuBar(); + + /// Returns true if this menu is attached to the menu bar + bool isAttachedToMenuBar() { return mCanvas != NULL; } + + /// Displays this menu as a popup menu and blocks until the user has selected + /// an item. + /// @param canvas the owner to show this popup associated with + /// @param x window local x coordinate at which to display the popup menu + /// @param y window local y coordinate at which to display the popup menu + /// implemented on a per-platform basis. + void showPopup(GuiCanvas *owner, S32 x = -1, S32 y = -1); + + /// Returns true iff this menu contains an item that matches @p iD. + /// implemented on a per-platform basis. + /// TODO: factor out common code + bool canHandleID(U32 iD); + + /// A menu item in this menu has been selected by id. + /// Submenus are given a chance to respond to the command first. + /// If no submenu can handle the command id, this menu handles it. + /// The script callback this::onSelectItem( position, text) is called. + /// If @p text is null, then the text arg passed to script is the text of + /// the selected menu item. + /// implemented on a per-platform basis. + /// TODO: factor out common code + bool handleSelect(U32 command, const char *text = NULL); + + void onMenuSelect(); + + /// Helper function to allow menu selections from signal events. + /// Wraps canHandleID() and handleSelect() in one function + /// without changing their internal functionality, so + /// it should work regardless of platform. + void handleSelectEvent(U32 popID, U32 command); + + virtual bool onMessageReceived(StringTableEntry queue, const char* event, const char* data ); + virtual bool onMessageObjectReceived(StringTableEntry queue, Message *msg ); +}; + +#endif // _POPUPMENU_H_ diff --git a/Engine/source/platform/nativeDialogs/fileDialog.h b/Engine/source/platform/nativeDialogs/fileDialog.h new file mode 100644 index 000000000..b5b4ed618 --- /dev/null +++ b/Engine/source/platform/nativeDialogs/fileDialog.h @@ -0,0 +1,208 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _FILEDIALOG_H_ +#define _FILEDIALOG_H_ +#include "console/simBase.h" + +// [03/14/07] The file dialogs need refactoring, and will be refactored in Jugg. +// Things that might need to change: +// - The interface is not in fact platform agnotsic, it is win32 oriented. +// - Filter format is highly windows specific, and is a little fragile, both for +// win32 and for other platforms. +// - Platform specific path strings are exposed to the console, because the +// protected validators save them as such. +// - Several of the FDS_XXX values are not options we want to give the user, such +// as NOT warning on file overwrite. The values FDS_OVERWRITEPROMPT, +// FDS_MUSTEXIST, and FDS_CHANGEPATH are not good things to give the user. +// - The Execute method is virtual for good reason. It should be implemented for +// each subclass. If common behavior is needed for Execute(), it can be +// factored out in hidden platform specific code. + + +/// @defgroup SystemDialogs Using System Dialogs + +/// @ingroup SystemDialogs +/// FileDialogOpaqueData is both defined and implemented on a platform specific +/// basis. +class FileDialogOpaqueData; + +/// @ingroup SystemDialogs +/// @internal +/// Platform Agnostic Structure for holding information about a file dialog. +struct FileDialogData +{ + +public: + FileDialogData(); + ~FileDialogData(); + + enum DialogStyle + { + FDS_OPEN = BIT(0),///< This is an open dialog. + FDS_SAVE = BIT(1),///< This is a save dialog. + FDS_OVERWRITEPROMPT = BIT(2),///< Can only be used in conjunction with style SaveDialog: prompt for a confirmation if a file will be overwritten. + FDS_MUSTEXIST = BIT(3),///< The user may only select files that actually exist. + FDS_MULTIPLEFILES = BIT(4),///< Can only be used in conjunction with style OpenDialog: allows selecting multiple files. + FDS_CHANGEPATH = BIT(5),///< Change the current working path to the directory where the file(s) chosen by the user are. + FDS_BROWSEFOLDER = BIT(6) ///< Select folders instead of files + }; + U8 mStyle; ///< Specifies the Style of the File Dialog @see DialogStyle + + StringTableEntry mFilters; ///< List of Filters pipe separated e.g. "BMP Files (*.bmp)|*.bmp|JPG Files (*.jpg)|*.jpg" + //StringTableEntry mFiles; // this is never used ///< Should only be referenced when using dialogStyle OpenDialog AND MultipleFiles: List of Files returned pipe separated + StringTableEntry mFile; ///< Should be referenced when dialogStyle MultipleFiles is NOT used: the file path of the user selected file. + StringTableEntry mDefaultPath; ///< Default path of dialog + StringTableEntry mDefaultFile; ///< Default selected file of dialog + StringTableEntry mTitle; ///< Title to display in file dialog + + FileDialogOpaqueData *mOpaqueData; ///< Stores platform specific info about the dialog + +}; + +/// @ingroup SystemDialogs +/// FileDialog is a platform agnostic dialog interface for querying the user for +/// file locations. It is designed to be used through the exposed +/// scripting interface. +/// +/// FileDialog is the base class for Native File Dialog controls in Torque. It provides these +/// basic areas of functionality: +/// +/// - Inherits from SimObject and is exposed to the scripting interface +/// - Provides blocking interface to allow instant return to script execution +/// - Simple object configuration makes practical use easy and effective +/// +/// @attention +/// FileDialog is *NOT* intended to be used directly in script and is only exposed to script +/// to expose generic file dialog attributes. +/// @see OpenFileDialog for a practical example on opening a file +/// @see SaveFileDialog for a practical example of saving a file +/// +/// +/// @{ +class FileDialog : public SimObject +{ + typedef SimObject Parent; + +protected: + FileDialogData mData; ///< Stores platform agnostic information about the dialogs properties + bool mChangePath; ///< Exposed ChangePath Property + bool mBoolTranslator; ///< Internally used to translate boolean values into their respective bits of dialog style +public: + + FileDialog(); + virtual ~FileDialog(); + DECLARE_CONOBJECT(FileDialog); + + static void initPersistFields(); + + virtual bool Execute(); + + FileDialogData &getData() { return mData; }; +protected: + /// @name FileDialog Properties + /// @{ + /// @@property DefaultPath (String) : Path to use as the default when the dialog is shown. + /// @code %fd.DefaultPath = "/source/myGameProject/data/images"; @endcode + /// + /// @li @b ChangePath (bool) : Will change the working path of the tools to the selected path when not canceled + /// @code %fd.ChangePath = true; // Change Working Path on Success @endcode + /// @internal + static bool setDefaultPath( void *object, const char *index, const char *data ); + static bool setDefaultFile( void *object, const char *index, const char *data ); + static bool setFilters( void *object, const char *index, const char *data ); + static bool setChangePath( void *object, const char *index, const char *data ); + static const char* getChangePath(void* obj, const char* data); + /// + /// @} + + static bool setFile( void *object, const char *index, const char *data ); +}; +/// @} + +class OpenFileDialog : public FileDialog +{ + typedef FileDialog Parent; + + /// Field Values + /// @{ + /// @internal + bool mMustExist; ///< Corresponds to FDS_MUSTEXIST flag on the PlatformFileDlgData structure + bool mMultipleFiles; ///< Corresponds to the FDS_MULTIPLEFILES flag on the PlatformFileDlgData structure + /// @} + +public: + + OpenFileDialog(); + virtual ~OpenFileDialog(); + + DECLARE_CONOBJECT(OpenFileDialog); /// @internal + + static void initPersistFields(); + +protected: + /// + /// @} + + /// Must Exist Property + static bool setMustExist( void *object, const char *index, const char *data ); + static const char*getMustExist(void* obj, const char* data); + + /// Multiple Files Property + static bool setMultipleFiles( void *object, const char *index, const char *data ); + static const char* getMultipleFiles(void* obj, const char* data); +}; + +class OpenFolderDialog : public OpenFileDialog +{ + typedef OpenFileDialog Parent; + +public: + StringTableEntry mMustExistInDir; + + OpenFolderDialog(); + DECLARE_CONOBJECT(OpenFolderDialog); + + static void initPersistFields(); +}; + +class SaveFileDialog : public FileDialog +{ + typedef FileDialog Parent; + +public: + + SaveFileDialog(); + virtual ~SaveFileDialog(); + DECLARE_CONOBJECT(SaveFileDialog); + + bool mOverwritePrompt; + + static void initPersistFields(); + +protected: + // Overwrite Prompt Property + static bool setOverwritePrompt( void *object, const char *index, const char *data ); + static const char* getOverwritePrompt(void* obj, const char* data); + +}; + +#endif // _FILEDIALOG_H_ diff --git a/Engine/source/platform/nativeDialogs/msgBox.cpp b/Engine/source/platform/nativeDialogs/msgBox.cpp new file mode 100644 index 000000000..8e44f9860 --- /dev/null +++ b/Engine/source/platform/nativeDialogs/msgBox.cpp @@ -0,0 +1,101 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/strings/stringFunctions.h" +#include "core/module.h" +#include "console/console.h" +#include "console/engineAPI.h" +#include "platform/nativeDialogs/msgBox.h" + + +DefineEnumType( MBButtons ); +DefineEnumType( MBIcons ); +DefineEnumType( MBReturnVal ); + + +static const MBReturnVal gsOK = MROk; +static const MBReturnVal gsCancel = MRCancel; +static const MBReturnVal gsRetry = MRRetry; +static const MBReturnVal gsDontSave = MRDontSave; + +AFTER_MODULE_INIT( Sim ) +{ + #if !defined( _XBOX ) && !defined( TORQUE_DEDICATED ) + Con::addConstant( "$MROk", TypeS32, &gsOK, "Determines the ok button press state in a message box.\n" + "@ingroup Platform" ); + Con::addConstant( "$MRCancel", TypeS32, &gsCancel, "Determines the cancel button press state in a message box.\n" + "@ingroup Platform" ); + Con::addConstant( "$MRRetry", TypeS32, &gsRetry, "Determines the retry button press state in a message box.\n" + "@ingroup Platform"); + Con::addConstant( "$MRDontSave", TypeS32, &gsDontSave, "Determines the don't save button press state in a message box.\n" + "@ingroup Platform" ); + #endif +} + + +//----------------------------------------------------------------------------- + +ImplementEnumType( MBButtons, + "Which buttons to display on a message box.\n\n" + "@ingroup Platform" ) + { MBOk, "Ok" }, + { MBOkCancel, "OkCancel" }, + { MBRetryCancel, "RetryCancel" }, + { MBSaveDontSave, "SaveDontSave" }, // maps to yes/no on win, to save/discard on mac. + { MBSaveDontSaveCancel, "SaveDontSaveCancel" }, // maps to yes/no/cancel on win, to save/cancel/don'tsave on mac. +EndImplementEnumType; + +ImplementEnumType( MBIcons, + "What icon to show on a message box.\n\n" + "@ingroup Platform" ) + { MIInformation, "Information" },// win: blue i, mac: app icon or talking head + { MIWarning, "Warning" }, // win & mac: yellow triangle with exclamation pt + { MIStop, "Stop" }, // win: red x, mac: app icon or stop icon, depending on version + { MIQuestion, "Question" }, // win: blue ?, mac: app icon +EndImplementEnumType; + +ImplementEnumType( MBReturnVal, + "Return value for messageBox() indicating which button was pressed by the user.\n\n" + "@ingroup Platform" ) + { MROk, "OK" }, + { MRCancel, "Cancelled" }, + { MRRetry, "Retry" }, + { MRDontSave, "DontSave" } +EndImplementEnumType; + + +//----------------------------------------------------------------------------- + +DefineEngineFunction( messageBox, S32, ( const char* title, const char* message, MBButtons buttons, MBIcons icons ), ( MBOkCancel, MIInformation ), + "Display a modal message box using the platform's native message box implementation.\n\n" + "@param title The title to display on the message box window.\n" + "@param message The text message to display in the box.\n" + "@param buttons Which buttons to put on the message box.\n" + "@param icons Which icon to show next to the message.\n" + "@return One of $MROK, $MRCancel, $MRRetry, and $MRDontSave identifying the button that the user pressed.\n" + "@tsexample\n" + "messageBox( \"Error\", \"\" );\n" //TODO + "@endtsexample\n\n" + "@ingroup Platform" ) +{ + return Platform::messageBox( title, message, buttons, icons ); +} diff --git a/Engine/source/platform/nativeDialogs/msgBox.h b/Engine/source/platform/nativeDialogs/msgBox.h new file mode 100644 index 000000000..9c210a417 --- /dev/null +++ b/Engine/source/platform/nativeDialogs/msgBox.h @@ -0,0 +1,62 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MSGBOX_H_ +#define _MSGBOX_H_ + + +// [tom, 10/17/2006] Note: If you change either of these enums, make sure you +// update the relevant code in the all the platform layers. + +// [pauls, 3/20/2007] Reduced the available types of dialog boxes in order to +// maintain a consistent but platform - appropriate look and feel in Torque. + +enum MBButtons +{ + MBOk, + MBOkCancel, + MBRetryCancel, + MBSaveDontSave, + MBSaveDontSaveCancel, +}; + +enum MBIcons +{ + MIWarning, + MIInformation, + MIQuestion, + MIStop, +}; + +enum MBReturnVal +{ + MROk = 1, // Start from 1 to allow use of 0 for errors + MRCancel, + MRRetry, + MRDontSave, +}; + + + +extern void initMessageBoxVars(); + +#endif // _MSGBOX_H_ diff --git a/Engine/source/platform/platform.cpp b/Engine/source/platform/platform.cpp new file mode 100644 index 000000000..fe9b14167 --- /dev/null +++ b/Engine/source/platform/platform.cpp @@ -0,0 +1,155 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include + +#include "platform/platform.h" +#include "console/console.h" +#include "console/consoleTypes.h" +#include "platform/threads/mutex.h" +#include "app/mainLoop.h" +#include "platform/event.h" +#include "platform/typetraits.h" + + +const F32 TypeTraits< F32 >::MIN = - F32_MAX; +const F32 TypeTraits< F32 >::MAX = F32_MAX; +const F32 TypeTraits< F32 >::ZERO = 0; +const F32 Float_Inf = std::numeric_limits< F32 >::infinity(); + +// The tools prefer to allow the CPU time to process +#ifndef TORQUE_TOOLS +S32 sgBackgroundProcessSleepTime = 25; +#else +S32 sgBackgroundProcessSleepTime = 200; +#endif +S32 sgTimeManagerProcessInterval = 1; + +Vector gKeyboardExclusionList; +bool gInitKeyboardExclusionList = false; +static bool gWebDeployment = false; + +void Platform::initConsole() +{ + Con::addVariable("$platform::backgroundSleepTime", TypeS32, &sgBackgroundProcessSleepTime, "Controls processor time usage when the game window is out of focus.\n" + "@ingroup Platform\n"); + Con::addVariable("$platform::timeManagerProcessInterval", TypeS32, &sgTimeManagerProcessInterval, "Controls processor time usage when the game window is in focus.\n" + "@ingroup Platform\n"); +} + +S32 Platform::getBackgroundSleepTime() +{ + return sgBackgroundProcessSleepTime; +} + +ConsoleToolFunction(restartInstance, void, 1, 1, "restartInstance()") +{ + StandardMainLoop::setRestart(true); + Platform::postQuitMessage( 0 ); +} + +void Platform::clearKeyboardInputExclusion() +{ + gKeyboardExclusionList.clear(); + gInitKeyboardExclusionList = true; +} + +void Platform::addKeyboardInputExclusion(const KeyboardInputExclusion &kie) +{ + gKeyboardExclusionList.push_back(kie); +} + +const bool Platform::checkKeyboardInputExclusion(const InputEventInfo *info) +{ + // Do one-time initialization of platform defaults. + if(!gInitKeyboardExclusionList) + { + gInitKeyboardExclusionList = true; + + // CodeReview Looks like we don't even need to do #ifdefs here since + // things like cmd-tab don't appear on windows, and alt-tab is an unlikely + // desired bind on other platforms - might be best to simply have a + // global exclusion list and keep it standard on all platforms. + // This might not be so, but it's the current assumption. [bjg 5/4/07] + + // Alt-tab + { + KeyboardInputExclusion kie; + kie.key = KEY_TAB; + kie.andModifierMask = SI_ALT; + addKeyboardInputExclusion(kie); + } + + // ... others go here... + } + + // Walk the list and look for matches. + for(S32 i=0; iobjType != SI_KEY) + return false; + + if(info->objInst != key) + return false; + + if((info->modifier & andModifierMask) != andModifierMask) + return false; + + if(info->modifier & !(info->modifier & orModifierMask)) + return false; + + return true; +} + +S32 Platform::compareModifiedTimes( const char *firstPath, const char *secondPath ) +{ + FileTime firstModTime; + if ( !getFileTimes( firstPath, NULL, &firstModTime ) ) + return -1; + + FileTime secondModTime; + if ( !getFileTimes( secondPath, NULL, &secondModTime ) ) + return -1; + + return compareFileTimes( firstModTime, secondModTime ); +} + +bool Platform::getWebDeployment() +{ + return gWebDeployment; +} + +void Platform::setWebDeployment(bool v) +{ + gWebDeployment = v; +} + + diff --git a/Engine/source/platform/platform.h b/Engine/source/platform/platform.h new file mode 100644 index 000000000..67b3a97a9 --- /dev/null +++ b/Engine/source/platform/platform.h @@ -0,0 +1,590 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PLATFORM_H_ +#define _PLATFORM_H_ + +#include + +#ifndef _TORQUECONFIG_H_ +#include "torqueConfig.h" +#endif +#ifndef _TORQUE_TYPES_H_ +#include "platform/types.h" +#endif +#ifndef _PLATFORMASSERT_H_ +#include "platform/platformAssert.h" +#endif +#ifndef _MSGBOX_H_ +#include "platform/nativeDialogs/msgBox.h" +#endif +#ifndef _VERSION_H_ +#include "app/version.h" +#endif +#ifndef _TORQUE_STRING_H_ +#include "core/util/str.h" +#endif +#ifndef _TORQUE_SAFEDELETE_H_ +#include "core/util/safeDelete.h" +#endif + +#include +#include + +/// Global processor identifiers. +/// +/// @note These enums must be globally scoped so that they work with the inline assembly +enum ProcessorType +{ + // x86 + CPU_X86Compatible, + CPU_Intel_Unknown, + CPU_Intel_486, + CPU_Intel_Pentium, + CPU_Intel_PentiumMMX, + CPU_Intel_PentiumPro, + CPU_Intel_PentiumII, + CPU_Intel_PentiumCeleron, + CPU_Intel_PentiumIII, + CPU_Intel_Pentium4, + CPU_Intel_PentiumM, + CPU_Intel_Core, + CPU_Intel_Core2, + CPU_Intel_Corei7Xeon, // Core i7 or Xeon + CPU_AMD_K6, + CPU_AMD_K6_2, + CPU_AMD_K6_3, + CPU_AMD_Athlon, + CPU_AMD_Unknown, + CPU_Cyrix_6x86, + CPU_Cyrix_MediaGX, + CPU_Cyrix_6x86MX, + CPU_Cyrix_GXm, ///< Media GX w/ MMX + CPU_Cyrix_Unknown, + + // PowerPC + CPU_PowerPC_Unknown, + CPU_PowerPC_601, + CPU_PowerPC_603, + CPU_PowerPC_603e, + CPU_PowerPC_603ev, + CPU_PowerPC_604, + CPU_PowerPC_604e, + CPU_PowerPC_604ev, + CPU_PowerPC_G3, + CPU_PowerPC_G4, + CPU_PowerPC_G4_7450, + CPU_PowerPC_G4_7455, + CPU_PowerPC_G4_7447, + CPU_PowerPC_G5, + + // Xenon + CPU_Xenon, + +}; + +/// Properties for CPU. +enum ProcessorProperties +{ + CPU_PROP_C = (1<<0), ///< We should use C fallback math functions. + CPU_PROP_FPU = (1<<1), ///< Has an FPU. (It better!) + CPU_PROP_MMX = (1<<2), ///< Supports MMX instruction set extension. + CPU_PROP_3DNOW = (1<<3), ///< Supports AMD 3dNow! instruction set extension. + CPU_PROP_SSE = (1<<4), ///< Supports SSE instruction set extension. + CPU_PROP_RDTSC = (1<<5), ///< Supports Read Time Stamp Counter op. + CPU_PROP_SSE2 = (1<<6), ///< Supports SSE2 instruction set extension. + CPU_PROP_SSE3 = (1<<7), ///< Supports SSE3 instruction set extension. + CPU_PROP_SSE3xt = (1<<8), ///< Supports extended SSE3 instruction set + CPU_PROP_SSE4_1 = (1<<9), ///< Supports SSE4_1 instruction set extension. + CPU_PROP_SSE4_2 = (1<<10), ///< Supports SSE4_2 instruction set extension. + CPU_PROP_MP = (1<<11), ///< This is a multi-processor system. + CPU_PROP_LE = (1<<12), ///< This processor is LITTLE ENDIAN. + CPU_PROP_64bit = (1<<13), ///< This processor is 64-bit capable + CPU_PROP_ALTIVEC = (1<<14), ///< Supports AltiVec instruction set extension (PPC only). +}; + +/// Processor info manager. +struct Processor +{ + /// Gather processor state information. + static void init(); +}; + +#if defined(TORQUE_SUPPORTS_GCC_INLINE_X86_ASM) +#define TORQUE_DEBUGBREAK() { asm ( "int 3"); } +#elif defined (TORQUE_SUPPORTS_VC_INLINE_X86_ASM) // put this test second so that the __asm syntax doesn't break the Visual Studio Intellisense parser +#define TORQUE_DEBUGBREAK() { __asm { int 3 }; } +#else +/// Macro to do in-line debug breaks, used for asserts. Does inline assembly when possible. +#define TORQUE_DEBUGBREAK() Platform::debugBreak(); +#endif + +/// Physical type of a drive. +enum DriveType +{ + DRIVETYPE_FIXED = 0, ///< Non-removable fixed drive. + DRIVETYPE_REMOVABLE = 1, ///< Removable drive. + DRIVETYPE_REMOTE = 2, ///< Networked/remote drive. + DRIVETYPE_CDROM = 3, ///< CD-Rom. + DRIVETYPE_RAMDISK = 4, ///< A ramdisk! + DRIVETYPE_UNKNOWN = 5 ///< Don't know. +}; + +// Some forward declares for later. +class Point2I; +template class Vector; +template class Signal; +struct InputEventInfo; + +namespace Platform +{ + // Time + struct LocalTime + { + U8 sec; ///< Seconds after minute (0-59) + U8 min; ///< Minutes after hour (0-59) + U8 hour; ///< Hours after midnight (0-23) + U8 month; ///< Month (0-11; 0=january) + U8 monthday; ///< Day of the month (1-31) + U8 weekday; ///< Day of the week (0-6, 6=sunday) + U16 year; ///< Current year minus 1900 + U16 yearday; ///< Day of year (0-365) + bool isdst; ///< True if daylight savings time is active + }; + + void getLocalTime(LocalTime &); + + /// Converts the local time to a formatted string appropriate + /// for the current platform. + String localTimeToString( const LocalTime < ); + + U32 getTime(); + U32 getVirtualMilliseconds(); + + /// Returns the milliseconds since the system was started. You should + /// not depend on this for high precision timing. + /// @see PlatformTimer + U32 getRealMilliseconds(); + + void advanceTime(U32 delta); + S32 getBackgroundSleepTime(); + + // Platform control + void init(); + void initConsole(); + void shutdown(); + void process(); + + // Math control state + U32 getMathControlState(); + void setMathControlState(U32 state); + void setMathControlStateKnown(); + + // Process control + void sleep(U32 ms); + bool excludeOtherInstances(const char *string); + bool checkOtherInstances(const char *string); + void restartInstance(); + void postQuitMessage(const U32 in_quitVal); + void forceShutdown(S32 returnValue); + + // Debug + void outputDebugString(const char *string, ...); + void debugBreak(); + + // Random + float getRandom(); + + // Window state + void setWindowLocked(bool locked); + void minimizeWindow(); + //const Point2I &getWindowSize(); + void setWindowSize( U32 newWidth, U32 newHeight, bool fullScreen ); + void closeWindow(); + + // File stuff + bool doCDCheck(); + StringTableEntry createPlatformFriendlyFilename(const char *filename); + struct FileInfo + { + const char* pFullPath; + const char* pFileName; + U32 fileSize; + }; + bool cdFileExists(const char *filePath, const char *volumeName, S32 serialNum); + void fileToLocalTime(const FileTime &ft, LocalTime *lt); + /// compare file times returns < 0 if a is earlier than b, >0 if b is earlier than a + S32 compareFileTimes(const FileTime &a, const FileTime &b); + bool stringToFileTime(const char * string, FileTime * time); + bool fileTimeToString(FileTime * time, char * string, U32 strLen); + + /// Compares the last modified time between two file paths. Returns < 0 if + /// the first file is earlier than the second, > 0 if the second file is earlier + /// than the first, and 0 if the files are equal. + /// + /// If either of the files doesn't exist it returns -1. + S32 compareModifiedTimes( const char *firstPath, const char *secondPath ); + + // Directory functions. Dump path returns false iff the directory cannot be + // opened. + + StringTableEntry getCurrentDirectory(); + bool setCurrentDirectory(StringTableEntry newDir); + + StringTableEntry getTemporaryDirectory(); + StringTableEntry getTemporaryFileName(); + + /// Returns the filename of the torque executable. + /// On Win32, this is the .exe file. + /// On Mac, this is the .app/ directory bundle. + StringTableEntry getExecutableName(); + /// Returns full pathname of the torque executable without filename + StringTableEntry getExecutablePath(); + + /// Returns the full path to the directory that contains main.cs. + /// Tools scripts are validated as such if they are in this directory or a + /// subdirectory of this directory. + StringTableEntry getMainDotCsDir(); + + /// Set main.cs directory. Used in runEntryScript() + void setMainDotCsDir(const char *dir); + + StringTableEntry getPrefsPath(const char *file = NULL); + + char *makeFullPathName(const char *path, char *buffer, U32 size, const char *cwd = NULL); + StringTableEntry stripBasePath(const char *path); + bool isFullPath(const char *path); + StringTableEntry makeRelativePathName(const char *path, const char *to); + + String stripExtension( String fileName, Vector< String >& validExtensions ); + + bool dumpPath(const char *in_pBasePath, Vector& out_rFileVector, S32 recurseDepth = -1); + bool dumpDirectories( const char *path, Vector &directoryVector, S32 depth = 0, bool noBasePath = false ); + bool hasSubDirectory( const char *pPath ); + bool getFileTimes(const char *filePath, FileTime *createTime, FileTime *modifyTime); + bool isFile(const char *pFilePath); + S32 getFileSize(const char *pFilePath); + bool isDirectory(const char *pDirPath); + bool isSubDirectory(const char *pParent, const char *pDir); + + void addExcludedDirectory(const char *pDir); + void clearExcludedDirectories(); + bool isExcludedDirectory(const char *pDir); + + /// Given a directory path, create all necessary directories for that path to exist. + bool createPath(const char *path); // create a directory path + + // Alerts + void AlertOK(const char *windowTitle, const char *message); + bool AlertOKCancel(const char *windowTitle, const char *message); + bool AlertRetry(const char *windowTitle, const char *message); + + // Volumes + struct VolumeInformation + { + StringTableEntry RootPath; + StringTableEntry Name; + StringTableEntry FileSystem; + U32 SerialNumber; + U32 Type; + bool ReadOnly; + }; + extern struct VolumeInformation *PVolumeInformation; + + // Volume functions. + void getVolumeNamesList( Vector& out_rNameVector, bool bOnlyFixedDrives = false ); + void getVolumeInformationList( Vector& out_rVolumeInfoVector, bool bOnlyFixedDrives = false ); + + struct SystemInfo_struct + { + struct Processor + { + ProcessorType type; + const char* name; + U32 mhz; + bool isMultiCore; + bool isHyperThreaded; + U32 numLogicalProcessors; + U32 numPhysicalProcessors; + U32 numAvailableCores; + U32 properties; // CPU type specific enum + } processor; + }; + extern Signal SystemInfoReady; + extern SystemInfo_struct SystemInfo; + + // Web page launch function: + bool openWebBrowser( const char* webAddress ); + + // display Splash Window + bool displaySplashWindow( ); + + void openFolder( const char* path ); + + // Open file at the OS level, according to registered file-types. + void openFile( const char* path ); + + const char* getLoginPassword(); + bool setLoginPassword( const char* password ); + + const char* getClipboard(); + bool setClipboard(const char *text); + + // User Specific Functions + StringTableEntry getUserHomeDirectory(); + StringTableEntry getUserDataDirectory(); + bool getUserIsAdministrator(); + + // Displays a fancy platform specific message box + S32 messageBox(const UTF8 *title, const UTF8 *message, MBButtons buttons = MBOkCancel, MBIcons icon = MIInformation); + + /// Description of a keyboard input we want to ignore. + struct KeyboardInputExclusion + { + KeyboardInputExclusion() + { + key = 0; + orModifierMask = 0; + andModifierMask = 0; + } + + /// The key code to ignore, e.g. KEY_TAB. If this and the other + /// conditions match, ignore the key. + S32 key; + + /// if(modifiers | orModifierMask) and the other conditions match, + /// ignore the key. + U32 orModifierMask; + + /// if((modifiers & andModifierMask) == andModifierMask) and the + /// other conditions match, ignore the key stroke. + U32 andModifierMask; + + /// Based on the values above, determine if a given input event + /// matchs this exclusion rule. + const bool checkAgainstInput(const InputEventInfo *info) const; + }; + + /// Reset the keyboard input exclusion list. + void clearKeyboardInputExclusion(); + + /// Add a new keyboard exclusion. + void addKeyboardInputExclusion(const KeyboardInputExclusion &kie); + + /// Check if a given input event should be excluded. + const bool checkKeyboardInputExclusion(const InputEventInfo *info); + + + /// Set/Get whether this is a web deployment + bool getWebDeployment(); + void setWebDeployment(bool v); + +}; + +//------------------------------------------------------------------------------ +// Unicode string conversions +// UNICODE is a windows platform API switching flag. Don't define it on other platforms. +#ifdef UNICODE +#define dT(s) L##s +#else +#define dT(s) s +#endif + +//------------------------------------------------------------------------------ +// Misc StdLib functions +#define QSORT_CALLBACK FN_CDECL +inline void dQsort(void *base, U32 nelem, U32 width, int (QSORT_CALLBACK *fcmp)(const void *, const void *)) +{ + qsort(base, nelem, width, fcmp); +} + +//-------------------------------------- Some all-around useful inlines and globals +// + +///@defgroup ObjTrickery Object Management Trickery +/// +/// These functions are to construct and destruct objects in memory +/// without causing a free or malloc call to occur. This is so that +/// we don't have to worry about allocating, say, space for a hundred +/// NetAddresses with a single malloc call, calling delete on a single +/// NetAdress, and having it try to free memory out from under us. +/// +/// @{ + +/// Constructs an object that already has memory allocated for it. +template +inline T* constructInPlace(T* p) +{ + return new ( p ) T; +} +template< class T > +inline T* constructArrayInPlace( T* p, U32 num ) +{ + return new ( p ) T[ num ]; +} + +/// Copy constructs an object that already has memory allocated for it. +template +inline T* constructInPlace(T* p, const T* copy) +{ + return new ( p ) T( *copy ); +} + +template inline T* constructInPlace(T* ptr, T2 t2) +{ + return new ( ptr ) T( t2 ); +} + +template inline T* constructInPlace(T* ptr, T2 t2, T3 t3) +{ + return new ( ptr ) T( t2, t3 ); +} + +template inline T* constructInPlace(T* ptr, T2 t2, T3 t3, T4 t4) +{ + return new ( ptr ) T( t2, t3, t4 ); +} + +template inline T* constructInPlace(T* ptr, T2 t2, T3 t3, T4 t4, T5 t5) +{ + return new ( ptr ) T( t2, t3, t4, t5 ); +} + +/// Destructs an object without freeing the memory associated with it. +template +inline void destructInPlace(T* p) +{ + p->~T(); +} + + +//------------------------------------------------------------------------------ +/// Memory functions + +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +# define TORQUE_TMM_ARGS_DECL , const char* fileName, const U32 lineNum +# define TORQUE_TMM_ARGS , fileName, lineNum +# define TORQUE_TMM_LOC , __FILE__, __LINE__ + extern void* FN_CDECL operator new(dsize_t size, const char*, const U32); + extern void* FN_CDECL operator new[](dsize_t size, const char*, const U32); + extern void FN_CDECL operator delete(void* ptr); + extern void FN_CDECL operator delete[](void* ptr); +# define _new new(__FILE__, __LINE__) +# define new _new +#else +# define TORQUE_TMM_ARGS_DECL +# define TORQUE_TMM_ARGS +# define TORQUE_TMM_LOC +#endif + +#define dMalloc(x) dMalloc_r(x, __FILE__, __LINE__) +#define dRealloc(x, y) dRealloc_r(x, y, __FILE__, __LINE__) + +extern void setBreakAlloc(dsize_t); +extern void setMinimumAllocUnit(U32); +extern void* dMalloc_r(dsize_t in_size, const char*, const dsize_t); +extern void dFree(void* in_pFree); +extern void* dRealloc_r(void* in_pResize, dsize_t in_size, const char*, const dsize_t); +extern void* dRealMalloc(dsize_t); +extern void dRealFree(void*); + +extern void *dMalloc_aligned(dsize_t in_size, int alignment); +extern void dFree_aligned(void *); + + +inline void dFree( const void* p ) +{ + dFree( ( void* ) p ); +} + +// Helper function to copy one array into another of different type +template void dCopyArray(T *dst, const S *src, dsize_t size) +{ + for (dsize_t i = 0; i < size; i++) + dst[i] = (T)src[i]; +} + +// Special case of the above function when the arrays are the same type (use memcpy) +template void dCopyArray(T *dst, const T *src, dsize_t size) +{ + dMemcpy(dst, src, size * sizeof(T)); +} + +extern void* dMemcpy(void *dst, const void *src, dsize_t size); +extern void* dMemmove(void *dst, const void *src, dsize_t size); +extern void* dMemset(void *dst, int c, dsize_t size); +extern int dMemcmp(const void *ptr1, const void *ptr2, dsize_t size); + +/// The dALIGN macro ensures the passed declaration is +/// data aligned at 16 byte boundaries. +#if defined( TORQUE_COMPILER_VISUALC ) + #define dALIGN( decl ) __declspec( align( 16 ) ) decl + #define dALIGN_BEGIN __declspec( align( 16 ) ) + #define dALIGN_END +#elif defined( TORQUE_COMPILER_GCC ) + #define dALIGN( decl ) decl __attribute__( ( aligned( 16 ) ) ) + #define dALIGN_BEGIN + #define dALIGN_END __attribute__( ( aligned( 16 ) ) ) +#else + #define dALIGN( decl ) decl + #define dALIGN_BEGIN() + #define dALIGN_END() +#endif + +//------------------------------------------------------------------------------ +// FileIO functions +extern bool dFileDelete(const char *name); +extern bool dFileRename(const char *oldName, const char *newName); +extern bool dFileTouch(const char *name); +extern bool dPathCopy(const char *fromName, const char *toName, bool nooverwrite = true); + +typedef void* FILE_HANDLE; +enum DFILE_STATUS +{ + DFILE_OK = 1 +}; + +extern FILE_HANDLE dOpenFileRead(const char *name, DFILE_STATUS &error); +extern FILE_HANDLE dOpenFileReadWrite(const char *name, bool append, DFILE_STATUS &error); +extern int dFileRead(FILE_HANDLE handle, U32 bytes, char *dst, DFILE_STATUS &error); +extern int dFileWrite(FILE_HANDLE handle, U32 bytes, const char *dst, DFILE_STATUS &error); +extern void dFileClose(FILE_HANDLE handle); + +extern StringTableEntry osGetTemporaryDirectory(); + +//------------------------------------------------------------------------------ +struct Math +{ + /// Initialize the math library with the appropriate libraries + /// to support hardware acceleration features. + /// + /// @param properties Leave zero to detect available hardware. Otherwise, + /// pass CPU instruction set flags that you want to load + /// support for. + static void init(U32 properties = 0); +}; + +/// @} + +#endif + + diff --git a/Engine/source/platform/platformAssert.cpp b/Engine/source/platform/platformAssert.cpp new file mode 100644 index 000000000..aa5926e88 --- /dev/null +++ b/Engine/source/platform/platformAssert.cpp @@ -0,0 +1,172 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include + +#include "core/strings/stringFunctions.h" +#include "console/console.h" + + +//-------------------------------------- STATIC Declaration +PlatformAssert *PlatformAssert::platformAssert = NULL; + +//-------------------------------------- +PlatformAssert::PlatformAssert() +{ + processing = false; +} + +//-------------------------------------- +PlatformAssert::~PlatformAssert() +{ +} + +//-------------------------------------- +void PlatformAssert::create( PlatformAssert* newAssertClass ) +{ + if (!platformAssert) + platformAssert = newAssertClass ? newAssertClass : new PlatformAssert; +} + + +//-------------------------------------- +void PlatformAssert::destroy() +{ + if (platformAssert) + delete platformAssert; + platformAssert = NULL; +} + + +//-------------------------------------- +bool PlatformAssert::displayMessageBox(const char *title, const char *message, bool retry) +{ + if (retry) + return Platform::AlertRetry(title, message); + + Platform::AlertOK(title, message); + return false; +} + +static const char *typeName[] = { "Unknown", "Fatal-ISV", "Fatal", "Warning" }; +//------------------------------------------------------------------------------ +static bool askToEnterDebugger(const char* message ) +{ + static bool haveAsked = false; + static bool useDebugger = true; + if(!haveAsked ) + { + static char tempBuff[1024]; + dSprintf( tempBuff, 1024, "Torque has encountered an assertion with message\n\n" + "%s\n\n" + "Would you like to use the debugger? If you cancel, you won't be asked" + " again until you restart Torque.", message); + + useDebugger = Platform::AlertOKCancel("Use debugger?", tempBuff ); + haveAsked = true; + } + return useDebugger; +} + +//-------------------------------------- + +bool PlatformAssert::process(Type assertType, + const char *filename, + U32 lineNumber, + const char *message) +{ + // If we're somehow recursing, just die. + if(processing) + Platform::debugBreak(); + + processing = true; + bool ret = true; + + // always dump to the Assert to the Console + if (Con::isActive()) + { + if (assertType == Warning) + Con::warnf(ConsoleLogEntry::Assert, "%s(%ld) : %s - %s", filename, lineNumber, typeName[assertType], message); + else + Con::errorf(ConsoleLogEntry::Assert, "%s(%ld) : %s - %s", filename, lineNumber, typeName[assertType], message); + } + + // if not a WARNING pop-up a dialog box + if (assertType != Warning) + { + // used for processing navGraphs (an assert won't botch the whole build) + if(Con::getBoolVariable("$FP::DisableAsserts", false) == true) + Platform::forceShutdown(1); + + char buffer[2048]; + dSprintf(buffer, 2048, "%s(%ld) : %s", filename, lineNumber, typeName[assertType] ); + +#ifdef TORQUE_DEBUG + // In debug versions, allow a retry even for ISVs... + bool retry = displayMessageBox(buffer, message, true); +#else + bool retry = displayMessageBox(buffer, message, ((assertType == Fatal) ? true : false) ); +#endif + if(!retry) + Platform::forceShutdown(1); + + ret = askToEnterDebugger(message); + } + + processing = false; + + return ret; +} + +bool PlatformAssert::processingAssert() +{ + return platformAssert ? platformAssert->processing : false; +} + +//-------------------------------------- +bool PlatformAssert::processAssert(Type assertType, + const char *filename, + U32 lineNumber, + const char *message) +{ + if (platformAssert) + return platformAssert->process(assertType, filename, lineNumber, message); + else // when platAssert NULL (during _start/_exit) try direct output... + dPrintf("\n%s: (%s @ %ld) %s\n", typeName[assertType], filename, lineNumber, message); + + // this could also be platform-specific: OutputDebugString on PC, DebugStr on Mac. + // Will raw printfs do the job? In the worst case, it's a break-pointable line of code. + // would have preferred Con but due to race conditions, it might not be around... + // Con::errorf(ConsoleLogEntry::Assert, "%s: (%s @ %ld) %s", typeName[assertType], filename, lineNumber, message); + + return true; +} + +//-------------------------------------- +const char* avar(const char *message, ...) +{ + static char buffer[4096]; + va_list args; + va_start(args, message); + dVsprintf(buffer, sizeof(buffer), message, args); + return( buffer ); +} diff --git a/Engine/source/platform/platformAssert.h b/Engine/source/platform/platformAssert.h new file mode 100644 index 000000000..4865d387c --- /dev/null +++ b/Engine/source/platform/platformAssert.h @@ -0,0 +1,141 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PLATFORMASSERT_H_ +#define _PLATFORMASSERT_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif + +class PlatformAssert +{ +public: + enum Type + { + Warning = 3, + Fatal = 2, + Fatal_ISV = 1 + }; + +private: + static PlatformAssert *platformAssert; + bool processing; + + virtual bool displayMessageBox(const char *title, const char *message, bool retry); + virtual bool process(Type assertType, + const char* filename, + U32 lineNumber, + const char* message); + + PlatformAssert(); + virtual ~PlatformAssert(); + +public: + static void create( PlatformAssert* newAssertClass = NULL ); + static void destroy(); + static bool processAssert(Type assertType, + const char* filename, + U32 lineNumber, + const char* message); + static char *message(const char *message, ...); + static bool processingAssert(); +}; + + +#ifdef TORQUE_ENABLE_ASSERTS + /*! + Assert that the statement x is true, and continue processing. + + If the statment x is true, continue processing. + + If the statement x is false, log the file and line where the assert occured, + the message y and continue processing. + + These asserts are only present in DEBUG builds. + */ + #define AssertWarn(x, y) \ + { if ((x)==0) \ + ::PlatformAssert::processAssert(::PlatformAssert::Warning, __FILE__, __LINE__, y); } + + /*! + Assert that the statement x is true, otherwise halt. + + If the statement x is true, continue processing. + + If the statement x is false, log the file and line where the assert occured, + the message y and displaying a dialog containing the message y. The user then + has the option to halt or continue causing the debugger to break. + + These asserts are only present in DEBUG builds. + + This assert is very useful for verifying data as well as function entry and + exit conditions. + */ + #define AssertFatal(x, y) \ + { if (((bool)(x))==(bool)0) \ + { if ( ::PlatformAssert::processAssert(::PlatformAssert::Fatal, __FILE__, __LINE__, y) ) { ::Platform::debugBreak(); } } } + +#else + #define AssertFatal(x, y) { (void)sizeof(x); (void)sizeof(y); } + #define AssertWarn(x, y) { (void)sizeof(x); (void)sizeof(y); } +#endif + +/*! + Assert (In Shipping Version) that the statement x is true, otherwise halt. + + If the statement x is true, continue processing. + + If the statement x is false, log the file and line where the assert occurred, + the message y and exit the program displaying a dialog containing the message y. + These asserts are present in both OPTIMIZED and DEBUG builds. + + This assert should only be used for rare conditions where the application cannot continue + execution without seg-faulting and you want to display a nice exit message. + */ +#define AssertISV(x, y) \ + { if ((x)==0) \ +{ if ( ::PlatformAssert::processAssert(::PlatformAssert::Fatal_ISV, __FILE__, __LINE__, y) ) { ::Platform::debugBreak(); } } } + + +/*! + Sprintf style string formating into a fixed temporary buffer. + @param in_msg sprintf style format string + @returns pointer to fixed buffer containing formatted string + + \b Example: + \code + U8 a = 5; + S16 b = -10; + char *output = avar("hello %s! a=%u, b=%d", "world"); + ouput = "hello world! a=5, b=-10" + \endcode + + @warning avar uses a static fixed buffer. Treat the buffer as volatile data + and use it immediately. Other functions my use avar too and clobber the buffer. + */ +const char* avar(const char *in_msg, ...); + + + +#endif // _PLATFORM_ASSERT_H_ + diff --git a/Engine/source/platform/platformCPU.cpp b/Engine/source/platform/platformCPU.cpp new file mode 100644 index 000000000..1fc7c0336 --- /dev/null +++ b/Engine/source/platform/platformCPU.cpp @@ -0,0 +1,269 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "platform/platformCPUCount.h" +#include "core/strings/stringFunctions.h" +#include "core/stringTable.h" +#include "core/util/tSignal.h" + +Signal Platform::SystemInfoReady; + +enum CPUFlags +{ + BIT_FPU = BIT(0), + BIT_RDTSC = BIT(4), + BIT_MMX = BIT(23), + BIT_SSE = BIT(25), + BIT_SSE2 = BIT(26), + BIT_3DNOW = BIT(31), + + // These use a different value for comparison than the above flags + BIT_SSE3 = BIT(0), + BIT_SSE3xt = BIT(9), + BIT_SSE4_1 = BIT(19), + BIT_SSE4_2 = BIT(20), +}; + +// fill the specified structure with information obtained from asm code +void SetProcessorInfo(Platform::SystemInfo_struct::Processor& pInfo, + char* vendor, U32 processor, U32 properties, U32 properties2) +{ + Platform::SystemInfo.processor.properties |= (properties & BIT_FPU) ? CPU_PROP_FPU : 0; + Platform::SystemInfo.processor.properties |= (properties & BIT_RDTSC) ? CPU_PROP_RDTSC : 0; + Platform::SystemInfo.processor.properties |= (properties & BIT_MMX) ? CPU_PROP_MMX : 0; + + if (dStricmp(vendor, "GenuineIntel") == 0) + { + pInfo.properties |= (properties & BIT_SSE) ? CPU_PROP_SSE : 0; + pInfo.properties |= (properties & BIT_SSE2) ? CPU_PROP_SSE2 : 0; + pInfo.properties |= (properties2 & BIT_SSE3) ? CPU_PROP_SSE3 : 0; + pInfo.properties |= (properties2 & BIT_SSE3xt) ? CPU_PROP_SSE3xt : 0; + pInfo.properties |= (properties2 & BIT_SSE4_1) ? CPU_PROP_SSE4_1 : 0; + pInfo.properties |= (properties2 & BIT_SSE4_2) ? CPU_PROP_SSE4_2 : 0; + + pInfo.type = CPU_Intel_Unknown; + // switch on processor family code + switch ((processor >> 8) & 0x0f) + { + case 4: + pInfo.type = CPU_Intel_486; + pInfo.name = StringTable->insert("Intel 486 class"); + break; + + // Pentium Family + case 5: + // switch on processor model code + switch ((processor >> 4) & 0xf) + { + case 1: + case 2: + case 3: + pInfo.type = CPU_Intel_Pentium; + pInfo.name = StringTable->insert("Intel Pentium"); + break; + case 4: + pInfo.type = CPU_Intel_PentiumMMX; + pInfo.name = StringTable->insert("Intel Pentium MMX"); + break; + default: + pInfo.type = CPU_Intel_Pentium; + pInfo.name = StringTable->insert( "Intel (unknown)" ); + break; + } + break; + + // Pentium Pro/II/II family + case 6: + { + U32 extendedModel = ( processor & 0xf0000 ) >> 16; + // switch on processor model code + switch ((processor >> 4) & 0xf) + { + case 1: + pInfo.type = CPU_Intel_PentiumPro; + pInfo.name = StringTable->insert("Intel Pentium Pro"); + break; + case 3: + case 5: + pInfo.type = CPU_Intel_PentiumII; + pInfo.name = StringTable->insert("Intel Pentium II"); + break; + case 6: + pInfo.type = CPU_Intel_PentiumCeleron; + pInfo.name = StringTable->insert("Intel Pentium Celeron"); + break; + case 7: + case 8: + case 11: + pInfo.type = CPU_Intel_PentiumIII; + pInfo.name = StringTable->insert("Intel Pentium III"); + break; + case 0xA: + if( extendedModel == 1) + { + pInfo.type = CPU_Intel_Corei7Xeon; + pInfo.name = StringTable->insert( "Intel Core i7 / Xeon" ); + } + else + { + pInfo.type = CPU_Intel_PentiumIII; + pInfo.name = StringTable->insert( "Intel Pentium III Xeon" ); + } + break; + case 0xD: + if( extendedModel == 1 ) + { + pInfo.type = CPU_Intel_Corei7Xeon; + pInfo.name = StringTable->insert( "Intel Core i7 / Xeon" ); + } + else + { + pInfo.type = CPU_Intel_PentiumM; + pInfo.name = StringTable->insert( "Intel Pentium/Celeron M" ); + } + break; + case 0xE: + pInfo.type = CPU_Intel_Core; + pInfo.name = StringTable->insert( "Intel Core" ); + break; + case 0xF: + pInfo.type = CPU_Intel_Core2; + pInfo.name = StringTable->insert( "Intel Core 2" ); + break; + default: + pInfo.type = CPU_Intel_PentiumPro; + pInfo.name = StringTable->insert( "Intel (unknown)" ); + break; + } + break; + } + + // Pentium4 Family + case 0xf: + pInfo.type = CPU_Intel_Pentium4; + pInfo.name = StringTable->insert( "Intel Pentium 4" ); + break; + + default: + pInfo.type = CPU_Intel_Unknown; + pInfo.name = StringTable->insert( "Intel (unknown)" ); + break; + } + } + //-------------------------------------- + else + if (dStricmp(vendor, "AuthenticAMD") == 0) + { + // AthlonXP processors support SSE + pInfo.properties |= (properties & BIT_SSE) ? CPU_PROP_SSE : 0; + pInfo.properties |= ( properties & BIT_SSE2 ) ? CPU_PROP_SSE2 : 0; + pInfo.properties |= (properties & BIT_3DNOW) ? CPU_PROP_3DNOW : 0; + // switch on processor family code + switch ((processor >> 8) & 0xf) + { + // K6 Family + case 5: + // switch on processor model code + switch ((processor >> 4) & 0xf) + { + case 0: + case 1: + case 2: + case 3: + pInfo.type = CPU_AMD_K6_3; + pInfo.name = StringTable->insert("AMD K5"); + break; + case 4: + case 5: + case 6: + case 7: + pInfo.type = CPU_AMD_K6; + pInfo.name = StringTable->insert("AMD K6"); + break; + case 8: + pInfo.type = CPU_AMD_K6_2; + pInfo.name = StringTable->insert("AMD K6-2"); + break; + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + pInfo.type = CPU_AMD_K6_3; + pInfo.name = StringTable->insert("AMD K6-3"); + break; + } + break; + + // Athlon Family + case 6: + pInfo.type = CPU_AMD_Athlon; + pInfo.name = StringTable->insert("AMD Athlon"); + break; + + default: + pInfo.type = CPU_AMD_Unknown; + pInfo.name = StringTable->insert("AMD (unknown)"); + break; + } + } + //-------------------------------------- + else + if (dStricmp(vendor, "CyrixInstead") == 0) + { + switch (processor) + { + case 0x520: + pInfo.type = CPU_Cyrix_6x86; + pInfo.name = StringTable->insert("Cyrix 6x86"); + break; + case 0x440: + pInfo.type = CPU_Cyrix_MediaGX; + pInfo.name = StringTable->insert("Cyrix Media GX"); + break; + case 0x600: + pInfo.type = CPU_Cyrix_6x86MX; + pInfo.name = StringTable->insert("Cyrix 6x86mx/MII"); + break; + case 0x540: + pInfo.type = CPU_Cyrix_GXm; + pInfo.name = StringTable->insert("Cyrix GXm"); + break; + default: + pInfo.type = CPU_Cyrix_Unknown; + pInfo.name = StringTable->insert("Cyrix (unknown)"); + break; + } + } + + // Get multithreading caps. + + CPUInfo::EConfig config = CPUInfo::CPUCount( pInfo.numLogicalProcessors, pInfo.numAvailableCores, pInfo.numPhysicalProcessors ); + pInfo.isHyperThreaded = CPUInfo::isHyperThreaded( config ); + pInfo.isMultiCore = CPUInfo::isMultiCore( config ); + + // Trigger the signal + Platform::SystemInfoReady.trigger(); +} diff --git a/Engine/source/platform/platformCPUCount.cpp b/Engine/source/platform/platformCPUCount.cpp new file mode 100644 index 000000000..1a4b5b450 --- /dev/null +++ b/Engine/source/platform/platformCPUCount.cpp @@ -0,0 +1,667 @@ +// Original code is: +// Copyright (c) 2005 Intel Corporation +// All Rights Reserved +// +// CPUCount.cpp : Detects three forms of hardware multi-threading support across IA-32 platform +// The three forms of HW multithreading are: Multi-processor, Multi-core, and +// HyperThreading Technology. +// This application enumerates all the logical processors enabled by OS and BIOS, +// determine the HW topology of these enabled logical processors in the system +// using information provided by CPUID instruction. +// A multi-processing system can support any combination of the three forms of HW +// multi-threading support. The relevant topology can be identified using a +// three level decomposition of the "initial APIC ID" into +// Package_id, core_id, and SMT_id. Such decomposition provides a three-level map of +// the topology of hardware resources and +// allow multi-threaded software to manage shared hardware resources in +// the platform to reduce resource contention + +// Multicore detection algorithm for processor and cache topology requires +// all leaf functions of CPUID instructions be available. System administrator +// must ensure BIOS settings is not configured to restrict CPUID functionalities. +//------------------------------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "platform/platformCPUCount.h" + +// Consoles don't need this +#if defined(TORQUE_OS_XENON) || defined(TORQUE_OS_PS3) +namespace CPUInfo +{ + +EConfig CPUCount(U32& TotAvailLogical, U32& TotAvailCore, U32& PhysicalNum) +{ + TotAvailLogical = 6; + TotAvailCore = 6; + PhysicalNum = 3; + + return CONFIG_MultiCoreAndHTEnabled; +} + +}; // namespace +#else + +#ifdef TORQUE_OS_LINUX +// The Linux source code listing can be compiled using Linux kernel verison 2.6 +// or higher (e.g. RH 4AS-2.8 using GCC 3.4.4). +// Due to syntax variances of Linux affinity APIs with earlier kernel versions +// and dependence on glibc library versions, compilation on Linux environment +// with older kernels and compilers may require kernel patches or compiler upgrades. + +#include +#include +#include +#include +#define DWORD unsigned long +#elif defined( TORQUE_OS_WIN32 ) +#include +#elif defined( TORQUE_OS_MAC ) +# include +# include +#else +#error Not implemented on platform. +#endif +#include +#include + +namespace CPUInfo { + +#define HWD_MT_BIT 0x10000000 // EDX[28] Bit 28 is set if HT or multi-core is supported +#define NUM_LOGICAL_BITS 0x00FF0000 // EBX[23:16] Bit 16-23 in ebx contains the number of logical + // processors per physical processor when execute cpuid with + // eax set to 1 +#define NUM_CORE_BITS 0xFC000000 // EAX[31:26] Bit 26-31 in eax contains the number of cores minus one + // per physical processor when execute cpuid with + // eax set to 4. + + +#define INITIAL_APIC_ID_BITS 0xFF000000 // EBX[31:24] Bits 24-31 (8 bits) return the 8-bit unique + // initial APIC ID for the processor this code is running on. + + + #ifndef TORQUE_OS_MAC + static unsigned int CpuIDSupported(void); + static unsigned int find_maskwidth(unsigned int); + static unsigned int HWD_MTSupported(void); + static unsigned int MaxLogicalProcPerPhysicalProc(void); + static unsigned int MaxCorePerPhysicalProc(void); + static unsigned char GetAPIC_ID(void); + static unsigned char GetNzbSubID(unsigned char, unsigned char, unsigned char); + #endif + + static char g_s3Levels[2048]; + +#ifndef TORQUE_OS_MAC + + // + // CpuIDSupported will return 0 if CPUID instruction is unavailable. Otherwise, it will return + // the maximum supported standard function. + // + static unsigned int CpuIDSupported(void) + { + unsigned int MaxInputValue; + // If CPUID instruction is supported +#ifdef TORQUE_COMPILER_GCC + try + { + MaxInputValue = 0; + // call cpuid with eax = 0 + asm + ( + "pushl %%ebx\n\t" + "xorl %%eax,%%eax\n\t" + "cpuid\n\t" + "popl %%ebx\n\t" + : "=a" (MaxInputValue) + : + : "%ecx", "%edx" + ); + } + catch (...) + { + return(0); // cpuid instruction is unavailable + } +#elif defined( TORQUE_COMPILER_VISUALC ) + try + { + MaxInputValue = 0; + // call cpuid with eax = 0 + __asm + { + xor eax, eax + cpuid + mov MaxInputValue, eax + } + } + catch (...) + { + return(0); // cpuid instruction is unavailable + } +#else +# error Not implemented. +#endif + + return MaxInputValue; + + } + + + + // + // Function returns the maximum cores per physical package. Note that the number of + // AVAILABLE cores per physical to be used by an application might be less than this + // maximum value. + // + + static unsigned int MaxCorePerPhysicalProc(void) + { + + unsigned int Regeax = 0; + + if (!HWD_MTSupported()) return (unsigned int) 1; // Single core +#ifdef TORQUE_COMPILER_GCC + { + asm + ( + "pushl %ebx\n\t" + "xorl %eax, %eax\n\t" + "cpuid\n\t" + "cmpl $4, %eax\n\t" // check if cpuid supports leaf 4 + "jl .single_core\n\t" // Single core + "movl $4, %eax\n\t" + "movl $0, %ecx\n\t" // start with index = 0; Leaf 4 reports + "popl %ebx\n\t" + ); // at least one valid cache level + asm + ( + "cpuid" + : "=a" (Regeax) + : + : "%ecx", "%edx" + ); + asm + ( + "jmp .multi_core\n" + ".single_core:\n\t" + "xor %eax, %eax\n" + ".multi_core:" + ); + } +#elif defined( TORQUE_COMPILER_VISUALC ) + __asm + { + xor eax, eax + cpuid + cmp eax, 4 // check if cpuid supports leaf 4 + jl single_core // Single core + mov eax, 4 + mov ecx, 0 // start with index = 0; Leaf 4 reports + cpuid // at least one valid cache level + mov Regeax, eax + jmp multi_core + +single_core: + xor eax, eax + +multi_core: + + } +#else +# error Not implemented. +#endif + return (unsigned int)((Regeax & NUM_CORE_BITS) >> 26)+1; + + } + + + + // + // The function returns 0 when the hardware multi-threaded bit is not set. + // + static unsigned int HWD_MTSupported(void) + { + + + unsigned int Regedx = 0; + + + if ((CpuIDSupported() >= 1)) + { +#ifdef TORQUE_COMPILER_GCC + asm + ( + "pushl %%ebx\n\t" + "movl $1,%%eax\n\t" + "cpuid\n\t" + "popl %%ebx\n\t" + : "=d" (Regedx) + : + : "%eax","%ecx" + ); +#elif defined( TORQUE_COMPILER_VISUALC ) + __asm + { + mov eax, 1 + cpuid + mov Regedx, edx + } +#else +# error Not implemented. +#endif + } + + return (Regedx & HWD_MT_BIT); + + + } + + + + // + // Function returns the maximum logical processors per physical package. Note that the number of + // AVAILABLE logical processors per physical to be used by an application might be less than this + // maximum value. + // + static unsigned int MaxLogicalProcPerPhysicalProc(void) + { + + unsigned int Regebx = 0; + + if (!HWD_MTSupported()) return (unsigned int) 1; +#ifdef TORQUE_COMPILER_GCC + asm + ( + "movl $1,%%eax\n\t" + "cpuid" + : "=b" (Regebx) + : + : "%eax","%ecx","%edx" + ); +#elif defined( TORQUE_COMPILER_VISUALC ) + __asm + { + mov eax, 1 + cpuid + mov Regebx, ebx + } +#else +# error Not implemented. +#endif + return (unsigned int) ((Regebx & NUM_LOGICAL_BITS) >> 16); + + } + + + static unsigned char GetAPIC_ID(void) + { + + unsigned int Regebx = 0; +#ifdef TORQUE_COMPILER_GCC + asm + ( + "movl $1, %%eax\n\t" + "cpuid" + : "=b" (Regebx) + : + : "%eax","%ecx","%edx" + ); + +#elif defined( TORQUE_COMPILER_VISUALC ) + __asm + { + mov eax, 1 + cpuid + mov Regebx, ebx + } +#else +# error Not implemented. +#endif + + return (unsigned char) ((Regebx & INITIAL_APIC_ID_BITS) >> 24); + + } + + // + // Determine the width of the bit field that can represent the value count_item. + // + unsigned int find_maskwidth(unsigned int CountItem) + { + unsigned int MaskWidth, + count = CountItem; +#ifdef TORQUE_COMPILER_GCC + asm + ( +#ifdef __x86_64__ // define constant to compile + "push %%rcx\n\t" // under 64-bit Linux + "push %%rax\n\t" +#else + "pushl %%ecx\n\t" + "pushl %%eax\n\t" +#endif + // "movl $count, %%eax\n\t" //done by Assembler below + "xorl %%ecx, %%ecx" + // "movl %%ecx, MaskWidth\n\t" //done by Assembler below + : "=c" (MaskWidth) + : "a" (count) + // : "%ecx", "%eax" We don't list these as clobbered because we don't want the assembler + //to put them back when we are done + ); + asm + ( + "decl %%eax\n\t" + "bsrw %%ax,%%cx\n\t" + "jz next\n\t" + "incw %%cx\n\t" + // "movl %%ecx, MaskWidth\n" //done by Assembler below + : "=c" (MaskWidth) + : + ); + asm + ( + "next:\n\t" +#ifdef __x86_64__ + "pop %rax\n\t" + "pop %rcx" +#else + "popl %eax\n\t" + "popl %ecx" +#endif + ); + +#elif defined( TORQUE_COMPILER_VISUALC ) + __asm + { + mov eax, count + mov ecx, 0 + mov MaskWidth, ecx + dec eax + bsr cx, ax + jz next + inc cx + mov MaskWidth, ecx +next: + + } +#else +# error Not implemented. +#endif + return MaskWidth; + } + + + // + // Extract the subset of bit field from the 8-bit value FullID. It returns the 8-bit sub ID value + // + static unsigned char GetNzbSubID(unsigned char FullID, + unsigned char MaxSubIDValue, + unsigned char ShiftCount) + { + unsigned int MaskWidth; + unsigned char MaskBits; + + MaskWidth = find_maskwidth((unsigned int) MaxSubIDValue); + MaskBits = (0xff << ShiftCount) ^ + ((unsigned char) (0xff << (ShiftCount + MaskWidth))); + + return (FullID & MaskBits); + } + +#endif + + + // + // + // + EConfig CPUCount(U32& TotAvailLogical, U32& TotAvailCore, U32& PhysicalNum) + { + EConfig StatusFlag = CONFIG_UserConfigIssue; + + g_s3Levels[0] = 0; + TotAvailCore = 1; + PhysicalNum = 1; + + unsigned int numLPEnabled = 0; + int MaxLPPerCore = 1; + +#ifdef TORQUE_OS_MAC + + //FIXME: This isn't a proper port but more or less just some sneaky cheating + // to get around having to mess with yet another crap UNIX-style API. Seems + // like there isn't a way to do this that's working across all OSX incarnations + // and machine configurations anyway. + + int numCPUs; + int numPackages; + + // Get the number of CPUs. + + size_t len = sizeof( numCPUs ); + if( sysctlbyname( "hw.ncpu", &numCPUs, &len, 0, 0 ) == -1 ) + return CONFIG_UserConfigIssue; + + // Get the number of packages. + len = sizeof( numPackages ); + if( sysctlbyname( "hw.packages", &numPackages, &len, 0, 0 ) == -1 ) + return CONFIG_UserConfigIssue; + + TotAvailCore = numCPUs; + TotAvailLogical = numCPUs; + PhysicalNum = numPackages; +#else + + U32 dwAffinityMask; + int j = 0; + unsigned char apicID, PackageIDMask; + unsigned char tblPkgID[256], tblCoreID[256], tblSMTID[256]; + char tmp[256]; + +#ifdef TORQUE_OS_LINUX + //we need to make sure that this process is allowed to run on + //all of the logical processors that the OS itself can run on. + //A process could acquire/inherit affinity settings that restricts the + // current process to run on a subset of all logical processor visible to OS. + + // Linux doesn't easily allow us to look at the Affinity Bitmask directly, + // but it does provide an API to test affinity maskbits of the current process + // against each logical processor visible under OS. + int sysNumProcs = sysconf(_SC_NPROCESSORS_CONF); //This will tell us how many + //CPUs are currently enabled. + + //this will tell us which processors this process can run on. + cpu_set_t allowedCPUs; + sched_getaffinity(0, sizeof(allowedCPUs), &allowedCPUs); + + for (int i = 0; i < sysNumProcs; i++ ) + { + if ( CPU_ISSET(i, &allowedCPUs) == 0 ) + return CONFIG_UserConfigIssue; + } +#elif defined( TORQUE_OS_WIN32 ) + DWORD dwProcessAffinity, dwSystemAffinity; + GetProcessAffinityMask(GetCurrentProcess(), + &dwProcessAffinity, + &dwSystemAffinity); + if (dwProcessAffinity != dwSystemAffinity) // not all CPUs are enabled + return CONFIG_UserConfigIssue; +#else +# error Not implemented. +#endif + + // Assume that cores within a package have the SAME number of + // logical processors. Also, values returned by + // MaxLogicalProcPerPhysicalProc and MaxCorePerPhysicalProc do not have + // to be power of 2. + + MaxLPPerCore = MaxLogicalProcPerPhysicalProc() / MaxCorePerPhysicalProc(); + dwAffinityMask = 1; + +#ifdef TORQUE_OS_LINUX + cpu_set_t currentCPU; + while ( j < sysNumProcs ) + { + CPU_ZERO(¤tCPU); + CPU_SET(j, ¤tCPU); + if ( sched_setaffinity (0, sizeof(currentCPU), ¤tCPU) == 0 ) + { + sleep(0); // Ensure system to switch to the right CPU +#elif defined( TORQUE_OS_WIN32 ) + while (dwAffinityMask && dwAffinityMask <= dwSystemAffinity) + { + if (SetThreadAffinityMask(GetCurrentThread(), dwAffinityMask)) + { + Sleep(0); // Ensure system to switch to the right CPU +#else +# error Not implemented. +#endif + apicID = GetAPIC_ID(); + + + // Store SMT ID and core ID of each logical processor + // Shift vlaue for SMT ID is 0 + // Shift value for core ID is the mask width for maximum logical + // processors per core + + tblSMTID[j] = GetNzbSubID(apicID, MaxLPPerCore, 0); + tblCoreID[j] = GetNzbSubID(apicID, + MaxCorePerPhysicalProc(), + (unsigned char) find_maskwidth(MaxLPPerCore)); + + // Extract package ID, assume single cluster. + // Shift value is the mask width for max Logical per package + + PackageIDMask = (unsigned char) (0xff << + find_maskwidth(MaxLogicalProcPerPhysicalProc())); + + tblPkgID[j] = apicID & PackageIDMask; + sprintf(tmp," AffinityMask = %d; Initial APIC = %d; Physical ID = %d, Core ID = %d, SMT ID = %d\n", + dwAffinityMask, apicID, tblPkgID[j], tblCoreID[j], tblSMTID[j]); + strcat(g_s3Levels, tmp); + + numLPEnabled ++; // Number of available logical processors in the system. + + } // if + + j++; + dwAffinityMask = 1 << j; + } // while + + // restore the affinity setting to its original state +#ifdef TORQUE_OS_LINUX + sched_setaffinity (0, sizeof(allowedCPUs), &allowedCPUs); + sleep(0); +#elif defined( TORQUE_OS_WIN32 ) + SetThreadAffinityMask(GetCurrentThread(), dwProcessAffinity); + Sleep(0); +#else +# error Not implemented. +#endif + TotAvailLogical = numLPEnabled; + + // + // Count available cores (TotAvailCore) in the system + // + unsigned char CoreIDBucket[256]; + DWORD ProcessorMask, pCoreMask[256]; + unsigned int i, ProcessorNum; + + CoreIDBucket[0] = tblPkgID[0] | tblCoreID[0]; + ProcessorMask = 1; + pCoreMask[0] = ProcessorMask; + + for (ProcessorNum = 1; ProcessorNum < numLPEnabled; ProcessorNum++) + { + ProcessorMask <<= 1; + for (i = 0; i < TotAvailCore; i++) + { + // Comparing bit-fields of logical processors residing in different packages + // Assuming the bit-masks are the same on all processors in the system. + if ((tblPkgID[ProcessorNum] | tblCoreID[ProcessorNum]) == CoreIDBucket[i]) + { + pCoreMask[i] |= ProcessorMask; + break; + } + + } // for i + + if (i == TotAvailCore) // did not match any bucket. Start a new one. + { + CoreIDBucket[i] = tblPkgID[ProcessorNum] | tblCoreID[ProcessorNum]; + pCoreMask[i] = ProcessorMask; + + TotAvailCore++; // Number of available cores in the system + + } + + } // for ProcessorNum + + + // + // Count physical processor (PhysicalNum) in the system + // + unsigned char PackageIDBucket[256]; + DWORD pPackageMask[256]; + + PackageIDBucket[0] = tblPkgID[0]; + ProcessorMask = 1; + pPackageMask[0] = ProcessorMask; + + for (ProcessorNum = 1; ProcessorNum < numLPEnabled; ProcessorNum++) + { + ProcessorMask <<= 1; + for (i = 0; i < PhysicalNum; i++) + { + // Comparing bit-fields of logical processors residing in different packages + // Assuming the bit-masks are the same on all processors in the system. + if (tblPkgID[ProcessorNum]== PackageIDBucket[i]) + { + pPackageMask[i] |= ProcessorMask; + break; + } + + } // for i + + if (i == PhysicalNum) // did not match any bucket. Start a new one. + { + PackageIDBucket[i] = tblPkgID[ProcessorNum]; + pPackageMask[i] = ProcessorMask; + + PhysicalNum++; // Total number of physical processors in the system + + } + + } // for ProcessorNum +#endif + + // + // Check to see if the system is multi-core + // Check if the system is hyper-threading + // + if (TotAvailCore > PhysicalNum) + { + // Multi-core + if (MaxLPPerCore == 1) + StatusFlag = CONFIG_MultiCoreAndHTNotCapable; + else if (numLPEnabled > TotAvailCore) + StatusFlag = CONFIG_MultiCoreAndHTEnabled; + else StatusFlag = CONFIG_MultiCoreAndHTDisabled; + + } + else + { + // Single-core + if (MaxLPPerCore == 1) + StatusFlag = CONFIG_SingleCoreAndHTNotCapable; + else if (numLPEnabled > TotAvailCore) + StatusFlag = CONFIG_SingleCoreHTEnabled; + else StatusFlag = CONFIG_SingleCoreHTDisabled; + + + } + + + + return StatusFlag; + } + +} // namespace CPUInfo +#endif \ No newline at end of file diff --git a/Engine/source/platform/platformCPUCount.h b/Engine/source/platform/platformCPUCount.h new file mode 100644 index 000000000..2ee07c2eb --- /dev/null +++ b/Engine/source/platform/platformCPUCount.h @@ -0,0 +1,75 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TORQUE_PLATFORM_PLATFORMCPUCOUNT_H_ +#define _TORQUE_PLATFORM_PLATFORMCPUCOUNT_H_ + +#include "platform/platform.h" + +namespace CPUInfo +{ + enum EConfig + { + CONFIG_UserConfigIssue, + CONFIG_SingleCoreHTEnabled, + CONFIG_SingleCoreHTDisabled, + CONFIG_SingleCoreAndHTNotCapable, + CONFIG_MultiCoreAndHTNotCapable, + CONFIG_MultiCoreAndHTEnabled, + CONFIG_MultiCoreAndHTDisabled, + }; + + inline bool isMultiCore( EConfig config ) + { + switch( config ) + { + case CONFIG_MultiCoreAndHTNotCapable: + case CONFIG_MultiCoreAndHTEnabled: + case CONFIG_MultiCoreAndHTDisabled: + return true; + + default: + return false; + } + } + + inline bool isHyperThreaded( EConfig config ) + { + switch( config ) + { + case CONFIG_SingleCoreHTEnabled: + case CONFIG_MultiCoreAndHTEnabled: + return true; + + default: + return false; + } + } + + EConfig CPUCount( U32& totalAvailableLogical, + U32& totalAvailableCores, + U32& numPhysical ); + +} // namespace CPUInfo + +#endif // _TORQUE_PLATFORM_PLATFORMCOUNT_H_ + diff --git a/Engine/source/platform/platformCPUInfo.asm b/Engine/source/platform/platformCPUInfo.asm new file mode 100644 index 000000000..bce39d220 --- /dev/null +++ b/Engine/source/platform/platformCPUInfo.asm @@ -0,0 +1,128 @@ +;----------------------------------------------------------------------------- +; Copyright (c) 2012 GarageGames, LLC +; +; Permission is hereby granted, free of charge, to any person obtaining a copy +; of this software and associated documentation files (the "Software"), to +; deal in the Software without restriction, including without limitation the +; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +; sell copies of the Software, and to permit persons to whom the Software is +; furnished to do so, subject to the following conditions: +; +; The above copyright notice and this permission notice shall be included in +; all copies or substantial portions of the Software. +; +; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +; IN THE SOFTWARE. +;----------------------------------------------------------------------------- + + +segment .text + +; syntax: export_fn +%macro export_fn 1 + %ifidn __OUTPUT_FORMAT__, elf + ; No underscore needed for ELF object files + global %1 + %1: + %else + global _%1 + _%1: + %endif +%endmacro + +; push registers +%macro pushreg 0 +; pushad + push ebx + push ebp + push esi + push edi +%endmacro + +; pop registers +%macro popreg 0 + pop edi + pop esi + pop ebp + pop ebx +; popad +%endmacro + +; void detectX86CPUInfo(char *vendor, U32 *processor, U32 *properties); +export_fn detectX86CPUInfo + push ebp + mov ebp, esp + + pushreg + + push edx + push ecx + pushfd + pushfd ; save EFLAGS to stack + pop eax ; move EFLAGS into EAX + mov ebx, eax + xor eax, 0x200000 ; flip bit 21 + push eax + popfd ; restore EFLAGS + pushfd + pop eax + cmp eax, ebx + jz EXIT ; doesn't support CPUID instruction + + ; + ; get vendor information using CPUID eax == 0 + xor eax, eax + cpuid + + ; store the vendor tag (12 bytes in ebx, edx, ecx) in the first parameter, + ; which should be a char[13] + push eax ; save eax + mov eax, [ebp+8] ; store the char* address in eax + mov [eax], ebx ; move ebx into the first 4 bytes + add eax, 4 ; advance the char* 4 bytes + mov [eax], edx ; move edx into the next 4 bytes + add eax, 4 ; advance the char* 4 bytes + mov [eax], ecx ; move ecx into the last 4 bytes + pop eax ; restore eax + + ; get generic extended CPUID info + mov eax, 1 + cpuid ; eax=1, so cpuid queries feature information + + and eax, 0x0fff3fff + push ecx + mov ecx, [ebp+12] + mov [ecx], eax ; just store the model bits in processor param + mov ecx, [ebp+16] + mov [ecx], edx ; set properties param + pop ecx + + ; want to check for 3DNow(tm). + ; need to see if extended cpuid functions present. + mov eax, 0x80000000 + cpuid + cmp eax, 0x80000000 + jbe MAYBE_3DLATER + mov eax, 0x80000001 + cpuid + ; 3DNow if bit 31 set -> put bit in our properties + and edx, 0x80000000 + push eax + mov eax, [ebp+16] + or [eax], edx + pop eax +MAYBE_3DLATER: +EXIT: + popfd + pop ecx + pop edx + + popreg + + pop ebp + ret diff --git a/Engine/source/platform/platformDlibrary.h b/Engine/source/platform/platformDlibrary.h new file mode 100644 index 000000000..c10fab333 --- /dev/null +++ b/Engine/source/platform/platformDlibrary.h @@ -0,0 +1,72 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef OS_DLIBRARY_H +#define OS_DLIBRARY_H + +#include "core/util/refBase.h" + +// DLLs use the standard calling convention +#define DLL_CALL __stdcall +#define DLL_EXPORT_CALL __declspec(dllexport) +#define DLL_IMPORT_CALL __declspec(dllimport) + +// Export functions from the DLL +#if defined(DLL_CODE) + #define DLL_DECL DLL_EXPORT_CALL +#else + #define DLL_DECL DLL_IMPORT_CALL +#endif + + +//----------------------------------------------------------------------------- + +///@defgroup KernelDLL Loadable Libraries +/// Basic DLL handling and symbol resolving. When a library is first loaded +/// it's "open" function will be called, and it's "close" function is called +/// right before the library is unloaded. +///@ingroup OsModule +///@{ + +/// Dynamic Library +/// Interface for library objects loaded using the loadLibrary() function. +class DLibrary: public StrongRefBase +{ +public: + virtual ~DLibrary() {} + virtual void *bind( const char *name ) = 0; +}; +typedef StrongRefPtr DLibraryRef; + +/// Load a library +/// Returns 0 if the library fails to load. Symbols are +/// resolved through the DLibrary interface. +DLibraryRef OsLoadLibrary( const char *file ); + +///@} + +//----------------------------------------------------------------------------- + + + +#endif + diff --git a/Engine/source/platform/platformFileIO.cpp b/Engine/source/platform/platformFileIO.cpp new file mode 100644 index 000000000..0209f239e --- /dev/null +++ b/Engine/source/platform/platformFileIO.cpp @@ -0,0 +1,531 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/strings/stringFunctions.h" +#include "util/tempAlloc.h" +#include "console/console.h" +#include "core/stringTable.h" + +//----------------------------------------------------------------------------- + +StringTableEntry Platform::getTemporaryDirectory() +{ + StringTableEntry path = osGetTemporaryDirectory(); + + if(! Platform::isDirectory(path)) + path = Platform::getCurrentDirectory(); + + return path; +} + +ConsoleFunction(getTemporaryDirectory, const char *, 1, 1, "()" + "@brief Returns the OS temporary directory, \"C:/Users/Mich/AppData/Local/Temp\" for example\n\n" + "@note This can be useful to adhering to OS standards and practices, " + "but not really used in Torque 3D right now.\n" + "@note Be very careful when getting into OS level File I/O." + "@return String containing path to OS temp directory\n" + "@note This is legacy function brought over from TGB, and does not appear " + "to have much use. Possibly deprecate?\n" + "@ingroup FileSystem\n" + "@internal") +{ + return Platform::getTemporaryDirectory(); +} + +StringTableEntry Platform::getTemporaryFileName() +{ + char buf[512]; + StringTableEntry path = Platform::getTemporaryDirectory(); + + dSprintf(buf, sizeof(buf), "%s/tgb.%08x.%02x.tmp", path, Platform::getRealMilliseconds(), U32(Platform::getRandom() * 255)); + + // [tom, 9/7/2006] This shouldn't be needed, but just in case + if(Platform::isFile(buf)) + return Platform::getTemporaryFileName(); + + return StringTable->insert(buf); +} + +ConsoleFunction(getTemporaryFileName, const char *, 1, 1, "()" + "@brief Creates a name and extension for a potential temporary file\n\n" + "This does not create the actual file. It simply creates a random name " + "for a file that does not exist.\n\n" + "@note This is legacy function brought over from TGB, and does not appear " + "to have much use. Possibly deprecate?\n" + "@ingroup FileSystem\n" + "@internal") +{ + return Platform::getTemporaryFileName(); +} + +//----------------------------------------------------------------------------- + +static StringTableEntry sgMainCSDir = NULL; + +StringTableEntry Platform::getMainDotCsDir() +{ + if(sgMainCSDir == NULL) + sgMainCSDir = Platform::getExecutablePath(); + + return sgMainCSDir; +} + +void Platform::setMainDotCsDir(const char *dir) +{ + sgMainCSDir = StringTable->insert(dir); +} + +//----------------------------------------------------------------------------- + +typedef Vector CharVector; +static CharVector gPlatformDirectoryExcludeList( __FILE__, __LINE__ ); + +void Platform::addExcludedDirectory(const char *pDir) +{ + gPlatformDirectoryExcludeList.push_back(dStrdup(pDir)); +} + +void Platform::clearExcludedDirectories() +{ + while(gPlatformDirectoryExcludeList.size()) + { + dFree(gPlatformDirectoryExcludeList.last()); + gPlatformDirectoryExcludeList.pop_back(); + } +} + +bool Platform::isExcludedDirectory(const char *pDir) +{ + for(CharVector::iterator i=gPlatformDirectoryExcludeList.begin(); i!=gPlatformDirectoryExcludeList.end(); i++) + if(!dStrcmp(pDir, *i)) + return true; + + return false; +} + +//----------------------------------------------------------------------------- + +inline void catPath(char *dst, const char *src, U32 len) +{ + if(*dst != '/') + { + ++dst; --len; + *dst = '/'; + } + + ++dst; --len; + + dStrncpy(dst, src, len); + dst[len - 1] = 0; +} + +// converts the posix root path "/" to "c:/" for win32 +// FIXME: this is not ideal. the c: drive is not guaranteed to exist. +#if defined(TORQUE_OS_WIN32) +static inline void _resolveLeadingSlash(char* buf, U32 size) +{ + if(buf[0] != '/') + return; + + AssertFatal(dStrlen(buf) + 2 < size, "Expanded path would be too long"); + dMemmove(buf + 2, buf, dStrlen(buf)); + buf[0] = 'c'; + buf[1] = ':'; +} +#endif + +static void makeCleanPathInPlace( char* path ) +{ + U32 pathDepth = 0; + char* fromPtr = path; + char* toPtr = path; + + bool isAbsolute = false; + if( *fromPtr == '/' ) + { + fromPtr ++; + toPtr ++; + isAbsolute = true; + } + else if( fromPtr[ 0 ] != '\0' && fromPtr[ 1 ] == ':' ) + { + toPtr += 3; + fromPtr += 3; + isAbsolute = true; + } + + while( *fromPtr ) + { + if( fromPtr[ 0 ] == '.' && fromPtr[ 1 ] == '.' && fromPtr[ 2 ] == '/' ) + { + // Back up from '../' + + if( pathDepth > 0 ) + { + pathDepth --; + toPtr -= 2; + while( toPtr >= path && *toPtr != '/' ) + toPtr --; + toPtr ++; + } + else if( !isAbsolute ) + { + dMemcpy( toPtr, fromPtr, 3 ); + toPtr += 3; + } + + fromPtr += 3; + } + else if( fromPtr[ 0 ] == '.' && fromPtr[ 1 ] == '/' ) + { + // Ignore. + fromPtr += 2; + } + else + { + if( fromPtr[ 0 ] == '/' ) + pathDepth ++; + + *toPtr ++ = *fromPtr ++; + } + } + + *toPtr = '\0'; +} + +char * Platform::makeFullPathName(const char *path, char *buffer, U32 size, const char *cwd /* = NULL */) +{ + char bspath[1024]; + dStrncpy(bspath, path, sizeof(bspath)); + bspath[sizeof(bspath)-1] = 0; + + for(S32 i = 0;i < dStrlen(bspath);++i) + { + if(bspath[i] == '\\') + bspath[i] = '/'; + } + + if(Platform::isFullPath(bspath)) + { + // Already a full path + #if defined(TORQUE_OS_WIN32) + _resolveLeadingSlash(bspath, sizeof(bspath)); + #endif + dStrncpy(buffer, bspath, size); + buffer[size-1] = 0; + return buffer; + } + + // [rene, 05/05/2008] Based on overall file handling in Torque, it does not seem to make + // that much sense to me to base things off the current working directory here. + + if(cwd == NULL) + cwd = Con::isCurrentScriptToolScript() ? Platform::getMainDotCsDir() : Platform::getCurrentDirectory(); + + dStrncpy(buffer, cwd, size); + buffer[size-1] = 0; + + const char* defaultDir = Con::getVariable("defaultGame"); + + char *ptr = bspath; + char *slash = NULL; + char *endptr = buffer + dStrlen(buffer) - 1; + + do + { + slash = dStrchr(ptr, '/'); + if(slash) + { + *slash = 0; + + // Directory + + if(dStrcmp(ptr, "..") == 0) + { + // Parent + endptr = dStrrchr(buffer, '/'); + if (endptr) + *endptr-- = 0; + } + else if(dStrcmp(ptr, ".") == 0) + { + // Current dir + } + else if(dStrcmp(ptr, "~") == 0) + { + catPath(endptr, defaultDir, size - (endptr - buffer)); + endptr += dStrlen(endptr) - 1; + } + else if(endptr) + { + catPath(endptr, ptr, size - (endptr - buffer)); + endptr += dStrlen(endptr) - 1; + } + + ptr = slash + 1; + } + else if(endptr) + { + // File + + catPath(endptr, ptr, size - (endptr - buffer)); + endptr += dStrlen(endptr) - 1; + } + + } while(slash); + + return buffer; +} + +bool Platform::isFullPath(const char *path) +{ + // Quick way out + if(path[0] == '/' || path[1] == ':') + return true; + + return false; +} + +//----------------------------------------------------------------------------- + +/// Return "fileName" stripped of its extension. Only extensions contained +/// in "validExtensions" will be stripped from the filename. +/// +/// @note Extensions in "validExtension" should include the dot. +String Platform::stripExtension( String fileName, Vector< String >& validExtensions ) +{ + // See if we have a valid extension to strip off + String ext; + S32 dotPos = fileName.find( '.', 0, String::Right ); + if( dotPos != String::NPos ) + ext = fileName.substr( dotPos ); + + U32 numValidExt = validExtensions.size(); + if( ext.isNotEmpty() && numValidExt ) + { + bool validExt = false; + for( U32 i = 0; i < numValidExt; i++ ) + { + if( ext.equal( validExtensions[ i ], String::NoCase ) ) + { + validExt = true; + break; + } + } + + if( !validExt ) + ext = String::EmptyString; + } + + if( ext.isEmpty() ) + return fileName; + else + return fileName.substr( 0, fileName.length() - ext.length() ); +} + +//----------------------------------------------------------------------------- +// TODO: wow really shouldn't be adding everything to the string table, use the string class! +StringTableEntry Platform::makeRelativePathName(const char *path, const char *to) +{ + // Make sure 'to' is a proper absolute path terminated with a forward slash. + + char buffer[ 2048 ]; + if( !to ) + { + dSprintf( buffer, sizeof( buffer ), "%s/", Platform::getMainDotCsDir() ); + to = buffer; + } + else if( !Platform::isFullPath( to ) ) + { + dSprintf( buffer, sizeof( buffer ), "%s/%s/", Platform::getMainDotCsDir(), to ); + makeCleanPathInPlace( buffer ); + to = buffer; + } + else if( to[ dStrlen( to ) - 1 ] != '/' ) + { + U32 length = getMin( (U32)dStrlen( to ), sizeof( buffer ) - 2 ); + dMemcpy( buffer, to, length ); + buffer[ length ] = '/'; + buffer[ length + 1 ] = '\0'; + to = buffer; + } + + // If 'path' isn't absolute, make it now. Let's us use a single + // absolute/absolute merge path from here on. + + char buffer2[ 1024 ]; + if( !Platform::isFullPath( path ) ) + { + dSprintf( buffer2, sizeof( buffer2 ), "%s/%s", Platform::getMainDotCsDir(), path ); + makeCleanPathInPlace( buffer2 ); + path = buffer2; + } + + // First, find the common prefix and see where 'path' branches off from 'to'. + + const char *pathPtr, *toPtr, *branch = path; + for(pathPtr = path, toPtr = to;*pathPtr && *toPtr && dTolower(*pathPtr) == dTolower(*toPtr);++pathPtr, ++toPtr) + { + if(*pathPtr == '/') + branch = pathPtr; + } + + // If there's no common part, the two paths are on different drives and + // there's nothing we can do. + + if( pathPtr == path ) + return StringTable->insert( path ); + + // If 'path' and 'to' are identical (minus trailing slash or so), we can just return './'. + + else if((*pathPtr == 0 || (*pathPtr == '/' && *(pathPtr + 1) == 0)) && + (*toPtr == 0 || (*toPtr == '/' && *(toPtr + 1) == 0))) + { + char* bufPtr = buffer; + *bufPtr ++ = '.'; + + if(*pathPtr == '/' || *(pathPtr - 1) == '/') + *bufPtr++ = '/'; + + *bufPtr = 0; + return StringTable->insert(buffer); + } + + // If 'to' is a proper prefix of 'path', the remainder of 'path' is our relative path. + + else if( *toPtr == '\0' && toPtr[ -1 ] == '/' ) + return StringTable->insert( pathPtr ); + + // Otherwise have to step up the remaining directories in 'to' and then + // append the remainder of 'path'. + + else + { + if((*pathPtr == 0 && *toPtr == '/') || (*toPtr == '/' && *pathPtr == 0)) + branch = pathPtr; + + // Allocate a new temp so we aren't prone to buffer overruns. + + TempAlloc< char > temp( dStrlen( toPtr ) + dStrlen( branch ) + 1 ); + char* bufPtr = temp; + + // Figure out parent dirs + + for(toPtr = to + (branch - path);*toPtr;++toPtr) + { + if(*toPtr == '/' && *(toPtr + 1) != 0) + { + *bufPtr++ = '.'; + *bufPtr++ = '.'; + *bufPtr++ = '/'; + } + } + *bufPtr = 0; + + // Copy the rest + if(*branch) + dStrcpy(bufPtr, branch + 1); + else + *--bufPtr = 0; + + return StringTable->insert( temp ); + } +} + +//----------------------------------------------------------------------------- + +static StringTableEntry tryStripBasePath(const char *path, const char *base) +{ + U32 len = dStrlen(base); + if(dStrnicmp(path, base, len) == 0) + { + if(*(path + len) == '/') ++len; + return StringTable->insert(path + len, true); + } + return NULL; +} + +StringTableEntry Platform::stripBasePath(const char *path) +{ + if(path == NULL) + return NULL; + + StringTableEntry str = tryStripBasePath(path, Platform::getMainDotCsDir()); + + if(str != NULL ) + return str; + + str = tryStripBasePath(path, Platform::getCurrentDirectory()); + if(str != NULL ) + return str; + + str = tryStripBasePath(path, Platform::getPrefsPath()); + if(str != NULL ) + return str; + + return path; +} + +//----------------------------------------------------------------------------- + +StringTableEntry Platform::getPrefsPath(const char *file /* = NULL */) +{ +#ifndef TORQUE2D_TOOLS_FIXME + return StringTable->insert(file ? file : ""); +#else + char buf[1024]; + const char *company = Con::getVariable("$Game::CompanyName"); + if(company == NULL || *company == 0) + company = "GarageGames"; + + const char *appName = Con::getVariable("$Game::GameName"); + if(appName == NULL || *appName == 0) + appName = TORQUE_APP_NAME; + + if(file) + { + if(dStrstr(file, "..")) + { + Con::errorf("getPrefsPath - filename cannot be relative"); + return NULL; + } + + dSprintf(buf, sizeof(buf), "%s/%s/%s/%s", Platform::getUserDataDirectory(), company, appName, file); + } + else + dSprintf(buf, sizeof(buf), "%s/%s/%s", Platform::getUserDataDirectory(), company, appName); + + return StringTable->insert(buf, true); +#endif +} + +//----------------------------------------------------------------------------- + +ConsoleFunction(getUserDataDirectory, const char*, 1, 1, "getUserDataDirectory()") +{ + return Platform::getUserDataDirectory(); +} + +ConsoleFunction(getUserHomeDirectory, const char*, 1, 1, "getUserHomeDirectory()") +{ + return Platform::getUserHomeDirectory(); +} diff --git a/Engine/source/platform/platformFont.cpp b/Engine/source/platform/platformFont.cpp new file mode 100644 index 000000000..ec686ec4e --- /dev/null +++ b/Engine/source/platform/platformFont.cpp @@ -0,0 +1,54 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "platform/platformFont.h" + + +const char *getCharSetName(const U32 charSet) +{ + switch(charSet) + { + case TGE_ANSI_CHARSET: return "ansi"; + case TGE_SYMBOL_CHARSET: return "symbol"; + case TGE_SHIFTJIS_CHARSET: return "shiftjis"; + case TGE_HANGEUL_CHARSET: return "hangeul"; + case TGE_HANGUL_CHARSET: return "hangul"; + case TGE_GB2312_CHARSET: return "gb2312"; + case TGE_CHINESEBIG5_CHARSET: return "chinesebig5"; + case TGE_OEM_CHARSET: return "oem"; + case TGE_JOHAB_CHARSET: return "johab"; + case TGE_HEBREW_CHARSET: return "hebrew"; + case TGE_ARABIC_CHARSET: return "arabic"; + case TGE_GREEK_CHARSET: return "greek"; + case TGE_TURKISH_CHARSET: return "turkish"; + case TGE_VIETNAMESE_CHARSET: return "vietnamese"; + case TGE_THAI_CHARSET: return "thai"; + case TGE_EASTEUROPE_CHARSET: return "easteurope"; + case TGE_RUSSIAN_CHARSET: return "russian"; + case TGE_MAC_CHARSET: return "mac"; + case TGE_BALTIC_CHARSET: return "baltic"; + } + + AssertISV(false, "getCharSetName - unknown charset! Update table in platformString.cc!"); + return ""; +} \ No newline at end of file diff --git a/Engine/source/platform/platformFont.h b/Engine/source/platform/platformFont.h new file mode 100644 index 000000000..a0deffc82 --- /dev/null +++ b/Engine/source/platform/platformFont.h @@ -0,0 +1,97 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif + +#ifndef _PLATFORMFONT_H_ +#define _PLATFORMFONT_H_ + +// Charsets for fonts + +// [tom, 7/27/2005] These are intended to map to their Win32 equivalents. This +// enumeration may require changes to accommodate other platforms. +enum FontCharset +{ + TGE_ANSI_CHARSET = 0, + TGE_SYMBOL_CHARSET, + TGE_SHIFTJIS_CHARSET, + TGE_HANGEUL_CHARSET, + TGE_HANGUL_CHARSET, + TGE_GB2312_CHARSET, + TGE_CHINESEBIG5_CHARSET, + TGE_OEM_CHARSET, + TGE_JOHAB_CHARSET, + TGE_HEBREW_CHARSET, + TGE_ARABIC_CHARSET, + TGE_GREEK_CHARSET, + TGE_TURKISH_CHARSET, + TGE_VIETNAMESE_CHARSET, + TGE_THAI_CHARSET, + TGE_EASTEUROPE_CHARSET, + TGE_RUSSIAN_CHARSET, + TGE_MAC_CHARSET, + TGE_BALTIC_CHARSET +}; + +extern const char *getCharSetName(const U32 charSet); + +class PlatformFont +{ +public: + struct CharInfo + { + S16 bitmapIndex; ///< @note -1 indicates character is NOT to be + /// rendered, i.e., \n, \r, etc. + U32 xOffset; ///< x offset into bitmap sheet + U32 yOffset; ///< y offset into bitmap sheet + U32 width; ///< width of character (pixels) + U32 height; ///< height of character (pixels) + S32 xOrigin; + S32 yOrigin; + S32 xIncrement; + U8 *bitmapData; ///< temp storage for bitmap data + }; + + virtual ~PlatformFont() {} + + /// Is the specified character valid for rendering? + virtual bool isValidChar(const UTF16 ch) const = 0; + virtual bool isValidChar(const UTF8 *str) const = 0; + + virtual U32 getFontHeight() const = 0; + virtual U32 getFontBaseLine() const = 0; + + virtual PlatformFont::CharInfo &getCharInfo(const UTF16 ch) const = 0; + virtual PlatformFont::CharInfo &getCharInfo(const UTF8 *str) const = 0; + + /// This is just for createPlatformFont to call. + /// + /// @todo Rethink this so we don't have a private public. + virtual bool create(const char *name, U32 size, U32 charset = TGE_ANSI_CHARSET) = 0; + static void enumeratePlatformFonts( Vector& fonts, UTF16* fontFamily = NULL ); +}; + +extern PlatformFont *createPlatformFont(const char *name, U32 size, U32 charset = TGE_ANSI_CHARSET); + +#endif // _PLATFORMFONT_H_ diff --git a/Engine/source/platform/platformInput.h b/Engine/source/platform/platformInput.h new file mode 100644 index 000000000..79f05e888 --- /dev/null +++ b/Engine/source/platform/platformInput.h @@ -0,0 +1,134 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PLATFORMINPUT_H_ +#define _PLATFORMINPUT_H_ + +#ifndef _SIMBASE_H_ +#include "console/simBase.h" +#endif + +#include "platform/event.h" + +//------------------------------------------------------------------------------ +U8 TranslateOSKeyCode( U8 vcode ); +U8 TranslateKeyCodeToOS(U8 keycode); + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +class InputDevice : public SimObject +{ +protected: + char mName[30]; + +public: + struct ObjInfo + { + InputEventType mType; + InputObjectInstances mInst; + S32 mMin, mMax; + }; + + inline const char* getDeviceName() + { + return mName; + } + + virtual bool process() = 0; +}; + +//------------------------------------------------------------------------------ + +class InputManager : public SimGroup +{ +protected: + bool mEnabled; + +public: + inline bool isEnabled() + { + return mEnabled; + } + + virtual bool enable() = 0; + virtual void disable() = 0; + virtual void process() = 0; +}; + +enum KEY_STATE +{ + STATE_LOWER, + STATE_UPPER, + STATE_GOOFY +}; + +//------------------------------------------------------------------------------ +class Input +{ +protected: + static InputManager* smManager; + + static bool smActive; + + /// Current modifier keys. + static U8 smModifierKeys; + + static bool smLastKeyboardActivated; + static bool smLastMouseActivated; + static bool smLastJoystickActivated; + +public: + static void init(); + static void destroy(); + + static bool enable(); + static void disable(); + + static void activate(); + static void deactivate(); + + static U16 getAscii( U16 keyCode, KEY_STATE keyState ); + static U16 getKeyCode( U16 asciiCode ); + + static bool isEnabled(); + static bool isActive(); + + static void process(); + + static InputManager* getManager(); + + static U8 getModifierKeys() {return smModifierKeys;} + static void setModifierKeys(U8 mod) {smModifierKeys = mod;} +#ifdef LOG_INPUT + static void log( const char* format, ... ); +#endif + +#ifdef TORQUE_OS_XENON + static S32 getLockedController(); +#endif + + /// Global input routing JournaledSignal; post input events here for + /// processing. + static InputEvent smInputEvent; +}; + +#endif // _H_PLATFORMINPUT_ diff --git a/Engine/source/platform/platformIntrinsics.gcc.h b/Engine/source/platform/platformIntrinsics.gcc.h new file mode 100644 index 000000000..3571d1df4 --- /dev/null +++ b/Engine/source/platform/platformIntrinsics.gcc.h @@ -0,0 +1,101 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TORQUE_PLATFORM_PLATFORMINTRINSICS_GCC_H_ +#define _TORQUE_PLATFORM_PLATFORMINTRINSICS_GCC_H_ + +/// @file +/// Compiler intrinsics for GCC. + +#ifdef TORQUE_OS_MAC +#include +#elif defined(TORQUE_OS_PS3) +#include +#endif + +// Fetch-And-Add +// +// NOTE: These do not return the pre-add value because +// not all platforms (damn you OSX) can do that. +// +inline void dFetchAndAdd( volatile U32& ref, U32 val ) +{ + #if defined(TORQUE_OS_PS3) + cellAtomicAdd32( (std::uint32_t *)&ref, val ); + #elif !defined(TORQUE_OS_MAC) + __sync_fetch_and_add( ( volatile long* ) &ref, val ); + #else + OSAtomicAdd32( val, (int32_t* ) &ref); + #endif +} + +inline void dFetchAndAdd( volatile S32& ref, S32 val ) +{ + #if defined(TORQUE_OS_PS3) + cellAtomicAdd32( (std::uint32_t *)&ref, val ); + #elif !defined(TORQUE_OS_MAC) + __sync_fetch_and_add( ( volatile long* ) &ref, val ); + #else + OSAtomicAdd32( val, (int32_t* ) &ref); + #endif +} + +// Compare-And-Swap + +inline bool dCompareAndSwap( volatile U32& ref, U32 oldVal, U32 newVal ) +{ + // bool + //OSAtomicCompareAndSwap32(int32_t oldValue, int32_t newValue, volatile int32_t *theValue); + #if defined(TORQUE_OS_PS3) + return ( cellAtomicCompareAndSwap32( (std::uint32_t *)&ref, newVal, oldVal ) == oldVal ); + #elif !defined(TORQUE_OS_MAC) + return ( __sync_val_compare_and_swap( ( volatile long* ) &ref, oldVal, newVal ) == oldVal ); + #else + return OSAtomicCompareAndSwap32(oldVal, newVal, (int32_t *) &ref); + #endif +} + +inline bool dCompareAndSwap( volatile U64& ref, U64 oldVal, U64 newVal ) +{ + #if defined(TORQUE_OS_PS3) + return ( cellAtomicCompareAndSwap32( (std::uint32_t *)&ref, newVal, oldVal ) == oldVal ); + #elif !defined(TORQUE_OS_MAC) + return ( __sync_val_compare_and_swap( ( volatile long long* ) &ref, oldVal, newVal ) == oldVal ); + #else + return OSAtomicCompareAndSwap64(oldVal, newVal, (int64_t *) &ref); + #endif + +} + +/// Performs an atomic read operation. +inline U32 dAtomicRead( volatile U32 &ref ) +{ + #if defined(TORQUE_OS_PS3) + return cellAtomicAdd32( (std::uint32_t *)&ref, 0 ); + #elif !defined(TORQUE_OS_MAC) + return __sync_fetch_and_add( ( volatile long* ) &ref, 0 ); + #else + return OSAtomicAdd32( 0, (int32_t* ) &ref); + #endif +} + +#endif // _TORQUE_PLATFORM_PLATFORMINTRINSICS_GCC_H_ diff --git a/Engine/source/platform/platformIntrinsics.h b/Engine/source/platform/platformIntrinsics.h new file mode 100644 index 000000000..fd2e11916 --- /dev/null +++ b/Engine/source/platform/platformIntrinsics.h @@ -0,0 +1,57 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PLATFORMINTRINSICS_H_ +#define _PLATFORMINTRINSICS_H_ + +#ifndef _TORQUE_TYPES_H_ +# include "platform/types.h" +#endif + +#if defined( TORQUE_COMPILER_VISUALC ) +# include "platform/platformIntrinsics.visualc.h" +#elif defined ( TORQUE_COMPILER_GCC ) +# include "platform/platformIntrinsics.gcc.h" +#else +# error No intrinsics implemented for compiler. +#endif + +//TODO: 64bit safe + +template< typename T > +inline bool dCompareAndSwap( T* volatile& refPtr, T* oldPtr, T* newPtr ) +{ + return dCompareAndSwap( *reinterpret_cast< volatile U32* >( &refPtr ), ( U32 ) oldPtr, ( U32 ) newPtr ); +} + +// Test-And-Set + +inline bool dTestAndSet( volatile U32& ref ) +{ + return dCompareAndSwap( ref, 0, 1 ); +} +inline bool dTestAndSet( volatile U64& ref ) +{ + return dCompareAndSwap( ref, 0, 1 ); +} + +#endif // _PLATFORMINTRINSICS_H_ diff --git a/Engine/source/platform/platformIntrinsics.visualc.h b/Engine/source/platform/platformIntrinsics.visualc.h new file mode 100644 index 000000000..843274499 --- /dev/null +++ b/Engine/source/platform/platformIntrinsics.visualc.h @@ -0,0 +1,76 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TORQUE_PLATFORM_PLATFORMINTRINSICS_VISUALC_H_ +#define _TORQUE_PLATFORM_PLATFORMINTRINSICS_VISUALC_H_ + +/// @file +/// Compiler intrinsics for Visual C++. + +#if defined(TORQUE_OS_XENON) +# include +# define _InterlockedExchangeAdd InterlockedExchangeAdd +# define _InterlockedExchangeAdd64 InterlockedExchangeAdd64 +#else +# include +#endif + +// Fetch-And-Add +// +// NOTE: These do not return the pre-add value because +// not all platforms (damn you OSX) can do that. +// +inline void dFetchAndAdd( volatile U32& ref, U32 val ) +{ + _InterlockedExchangeAdd( ( volatile long* ) &ref, val ); +} +inline void dFetchAndAdd( volatile S32& ref, S32 val ) +{ + _InterlockedExchangeAdd( ( volatile long* ) &ref, val ); +} + +#if defined(TORQUE_OS_XENON) +// Not available on x86 +inline void dFetchAndAdd( volatile U64& ref, U64 val ) +{ + _InterlockedExchangeAdd64( ( volatile __int64* ) &ref, val ); +} +#endif + +// Compare-And-Swap + +inline bool dCompareAndSwap( volatile U32& ref, U32 oldVal, U32 newVal ) +{ + return ( _InterlockedCompareExchange( ( volatile long* ) &ref, newVal, oldVal ) == oldVal ); +} +inline bool dCompareAndSwap( volatile U64& ref, U64 oldVal, U64 newVal ) +{ + return ( _InterlockedCompareExchange64( ( volatile __int64* ) &ref, newVal, oldVal ) == oldVal ); +} + +/// Performs an atomic read operation. +inline U32 dAtomicRead( volatile U32 &ref ) +{ + return _InterlockedExchangeAdd( ( volatile long* )&ref, 0 ); +} + +#endif // _TORQUE_PLATFORM_PLATFORMINTRINSICS_VISUALC_H_ diff --git a/Engine/source/platform/platformMemory.cpp b/Engine/source/platform/platformMemory.cpp new file mode 100644 index 000000000..925a15422 --- /dev/null +++ b/Engine/source/platform/platformMemory.cpp @@ -0,0 +1,1795 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platformMemory.h" +#include "console/dynamicTypes.h" +#include "console/engineAPI.h" +#include "core/stream/fileStream.h" +#include "core/strings/stringFunctions.h" +#include "console/console.h" +#include "platform/profiler.h" +#include "platform/threads/mutex.h" +#include "core/module.h" + +// If profile paths are enabled, disable profiling of the +// memory manager as that would cause a cyclic dependency +// through the string table's allocation stuff used by the +// profiler (talk about a long sentence...) + +#ifdef TORQUE_ENABLE_PROFILE_PATH +# undef PROFILE_START +# undef PROFILE_END +# undef PROFILE_SCOPE +# define PROFILE_START( x ) +# define PROFILE_END() +# define PROFILE_SCOPE( x ) +#endif + +#ifdef TORQUE_MULTITHREAD +void * gMemMutex = NULL; +#endif + +//-------------------------------------- Make sure we don't have the define set +#ifdef new +#undef new +#endif + +enum MemConstants { + Allocated = BIT(0), + Array = BIT(1), + DebugFlag = BIT(2), + Reallocated = BIT(3), /// This flag is set if the memory has been allocated, then 'realloc' is called + GlobalFlag = BIT(4), + StaticFlag = BIT(5), + AllocatedGuard = 0xCEDEFEDE, + FreeGuard = 0x5555FFFF, + MaxAllocationAmount = 0xFFFFFFFF, + TreeNodeAllocCount = 2048, +}; + +inline U32 flagToBit( Memory::EFlag flag ) +{ + using namespace Memory; + + U32 bit = 0; + switch( flag ) + { + case FLAG_Debug: bit = DebugFlag; break; + case FLAG_Global: bit = GlobalFlag; break; + case FLAG_Static: bit = StaticFlag; break; + } + return bit; +} + +enum RedBlackTokens { + Red = 0, + Black = 1 +}; + +static U32 MinPageSize = 8 * 1024 * 1024; + +#if !defined(TORQUE_SHIPPING) && defined(TORQUE_DEBUG_GUARD) +#define LOG_PAGE_ALLOCS +#endif + +U32 gNewNewTotal = 0; +U32 gImageAlloc = 0; + +//--------------------------------------------------------------------------- + +namespace Memory +{ + +ConsoleFunctionGroupBegin( Memory, "Memory manager utility functions."); + +struct FreeHeader; + +/// Red/Black Tree Node - used to store queues of free blocks +struct TreeNode +{ + U32 size; + TreeNode *parent; + TreeNode *left; + TreeNode *right; + U32 color; + FreeHeader *queueHead; + FreeHeader *queueTail; + U32 unused; +}; + +struct Header +{ + // doubly linked list of allocated and free blocks - + // contiguous in memory. +#ifdef TORQUE_DEBUG_GUARD + U32 preguard[4]; +#endif + Header *next; + Header *prev; + dsize_t size; + U32 flags; +#ifdef TORQUE_DEBUG_GUARD + #ifdef TORQUE_ENABLE_PROFILE_PATH + U32 unused[5]; + #else + U32 unused[4]; + #endif + U32 postguard[4]; +#endif +}; + +struct AllocatedHeader +{ +#ifdef TORQUE_DEBUG_GUARD + U32 preguard[4]; +#endif + Header *next; + Header *prev; + dsize_t size; + U32 flags; + +#ifdef TORQUE_DEBUG_GUARD + // an allocated header will only have this stuff if TORQUE_DEBUG_GUARD + U32 line; + U32 allocNum; + const char *fileName; + #ifdef TORQUE_ENABLE_PROFILE_PATH + const char * profilePath; + #endif + U32 realSize; + U32 postguard[4]; +#endif + + void* getUserPtr() + { + return ( this + 1 ); + } +}; + +struct FreeHeader +{ +#ifdef TORQUE_DEBUG_GUARD + U32 preguard[4]; +#endif + Header *next; + Header *prev; + dsize_t size; + U32 flags; + +// since a free header has at least one cache line (16 bytes) +// we can tag some more stuff on: + + FreeHeader *nextQueue; // of the same size + FreeHeader *prevQueue; // doubly linked + TreeNode *treeNode; // which tree node we're coming off of. + U32 guard; +#ifdef TORQUE_DEBUG_GUARD + #ifdef TORQUE_ENABLE_PROFILE_PATH + U32 unused; + #endif + U32 postguard[4]; +#endif +}; + +struct PageRecord +{ + dsize_t allocSize; + PageRecord *prevPage; + Header *headerList; // if headerList is NULL, this is a treeNode page + void *basePtr; + U32 unused[4]; // even out the record to 32 bytes... + // so if we're on a 32-byte cache-line comp, the tree nodes + // will cache better +}; + +PageRecord *gPageList = NULL; +TreeNode nil; +TreeNode *NIL = &nil; +TreeNode *gFreeTreeRoot = &nil; +TreeNode *gTreeFreeList = NULL; + +U32 gInsertCount = 0; +U32 gRemoveCount = 0; +U32 gBreakAlloc = 0xFFFFFFFF; +U32 gCurrAlloc = 0; +char gLogFilename[256] = "memlog.txt"; +bool gEnableLogging = false; +bool gNeverLogLeaks = 0; +bool gAlwaysLogLeaks = 0; +U32 gBytesAllocated = 0; +U32 gBlocksAllocated = 0; +U32 gPageBytesAllocated = 0; + +struct HeapIterator +{ + PageRecord* mCurrentPage; + Header* mCurrentHeader; + bool mAllocatedOnly; + + HeapIterator( bool allocatedOnly = true ) + : mCurrentPage( gPageList ), + mAllocatedOnly( allocatedOnly ), + mCurrentHeader( NULL ) + { + if( mCurrentPage ) + { + mCurrentHeader = mCurrentPage->headerList; + while( !mCurrentHeader && mCurrentPage ) + { + mCurrentPage = mCurrentPage->prevPage; + mCurrentHeader = mCurrentPage->headerList; + } + + if( mCurrentHeader && mAllocatedOnly && !( mCurrentHeader->flags & Allocated ) ) + ++ ( *this ); // Advance to first allocated record. + } + } + + bool isValid() const + { + return ( mCurrentHeader != NULL ); + } + HeapIterator& operator ++() + { + do + { + if( !mCurrentHeader ) + { + if( mCurrentPage ) + mCurrentPage = mCurrentPage->prevPage; + + if( !mCurrentPage ) + break; + mCurrentHeader = mCurrentPage->headerList; + } + else + mCurrentHeader = mCurrentHeader->next; + } + while( !mCurrentHeader || ( mAllocatedOnly && !( mCurrentHeader->flags & Allocated ) ) ); + + return *this; + } + operator Header*() const + { + return mCurrentHeader; + } + Header* operator *() const + { + return mCurrentHeader; + } + Header* operator ->() const + { + return mCurrentHeader; + } +}; + +#ifdef TORQUE_DEBUG_GUARD + +static bool checkGuard(Header *header, bool alloc) +{ + U32 guardVal = alloc ? AllocatedGuard : FreeGuard; + for(U32 i = 0; i < 4; i++) + if(header->preguard[i] != guardVal || header->postguard[i] != guardVal) + { + Platform::debugBreak(); + return false; + } + + return true; +} + +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +static void setGuard(Header *header, bool alloc) +{ + U32 guardVal = alloc ? AllocatedGuard : FreeGuard; + for(U32 i = 0; i < 4; i++) + header->preguard[i] = header->postguard[i] = guardVal; +} +#endif // !defined(TORQUE_DISABLE_MEMORY_MANAGER) + +#endif // TORQUE_DEBUG_GUARD + +static void memoryError() +{ + // free all the pages + PageRecord *walk = gPageList; + while(walk) { + PageRecord *prev = walk->prevPage; + dRealFree(walk); + walk = prev; + } + AssertFatal(false, "Error allocating memory! Shutting down."); + Platform::AlertOK("Torque Memory Error", "Error allocating memory. Shutting down.\n"); + Platform::forceShutdown(-1); +} + +PageRecord *allocPage(dsize_t pageSize) +{ + pageSize += sizeof(PageRecord); + void* base = dRealMalloc(pageSize); + if (base == NULL) + memoryError(); + + PageRecord *rec = (PageRecord *) base; + rec->basePtr = (void *) (rec + 1); + rec->allocSize = pageSize; + rec->prevPage = gPageList; + gPageList = rec; + rec->headerList = NULL; + return rec; +} + +TreeNode *allocTreeNode() +{ + if(!gTreeFreeList) + { + PageRecord *newPage = allocPage(TreeNodeAllocCount * sizeof(TreeNode)); + TreeNode *walk = (TreeNode *) newPage->basePtr; + U32 i; + gTreeFreeList = walk; + for(i = 0; i < TreeNodeAllocCount - 1; i++, walk++) + walk->parent = walk + 1; + walk->parent = NULL; + } + TreeNode *ret = gTreeFreeList; + gTreeFreeList = ret->parent; + return ret; +} + +void freeTreeNode(TreeNode *tn) +{ + tn->parent = gTreeFreeList; + gTreeFreeList = tn; +} + + +static U32 validateTreeRecurse(TreeNode *tree) +{ + if(tree == NIL) + return 1; + // check my left tree + int lcount, rcount, nc = 0; + + if(tree->color == Red) + { + if(tree->left->color == Red || tree->right->color == Red) + Platform::debugBreak(); + } + else + nc = 1; + + FreeHeader *walk = tree->queueHead; + if(!walk) + Platform::debugBreak(); + + FreeHeader *prev = NULL; + while(walk) + { + if(walk->prevQueue != prev) + Platform::debugBreak(); + if(walk->treeNode != tree) + Platform::debugBreak(); + if(walk->size != tree->size) + Platform::debugBreak(); + if(!walk->nextQueue && walk != tree->queueTail) + Platform::debugBreak(); + prev = walk; + walk = walk->nextQueue; + } + + lcount = validateTreeRecurse(tree->left); + rcount = validateTreeRecurse(tree->right); + if(lcount != rcount) + Platform::debugBreak(); + return lcount + nc; +} + +static void validateParentageRecurse(TreeNode *tree) +{ + if(tree->left != NIL) + { + if(tree->left->parent != tree) + Platform::debugBreak(); + + if(tree->left->size > tree->size) + Platform::debugBreak(); + validateParentageRecurse(tree->left); + } + if(tree->right != NIL) + { + if(tree->right->parent != tree) + Platform::debugBreak(); + + if(tree->right->size < tree->size) + Platform::debugBreak(); + validateParentageRecurse(tree->right); + } +} + +static void validateTree() +{ + if(gFreeTreeRoot == NIL) + return; + validateParentageRecurse(gFreeTreeRoot); + validateTreeRecurse(gFreeTreeRoot); +} + +void validate() +{ +#ifdef TORQUE_MULTITHREAD + if(!gMemMutex) + gMemMutex = Mutex::createMutex(); + + Mutex::lockMutex(gMemMutex); +#endif + + + // first validate the free tree: + validateTree(); + // now validate all blocks: + for(PageRecord *list = gPageList; list; list = list->prevPage) + { + Header *prev = NULL; + for(Header *walk = list->headerList; walk; walk = walk->next) + { +#ifdef TORQUE_DEBUG_GUARD + checkGuard(walk, walk->flags & Allocated); +#endif + if(walk->prev != prev) + Platform::debugBreak(); + prev = walk; + if(walk->next && ((const char *)(walk->next) != (const char *)(walk) + sizeof(Header) + walk->size)) + Platform::debugBreak(); + } + } + +#ifdef TORQUE_MULTITHREAD + Mutex::unlockMutex(gMemMutex); +#endif +} + +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +static void rotateLeft(TreeNode *hdr) +{ + TreeNode *temp = hdr->right; + hdr->right = temp->left; + if(temp->left != NIL) + temp->left->parent = hdr; + temp->parent = hdr->parent; + if(temp->parent == NIL) + gFreeTreeRoot = temp; + else if(hdr == hdr->parent->left) + hdr->parent->left = temp; + else + hdr->parent->right = temp; + temp->left = hdr; + hdr->parent = temp; +} +#endif + +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +static void rotateRight(TreeNode *hdr) +{ + TreeNode *temp = hdr->left; + hdr->left = temp->right; + if(temp->right != NIL) + temp->right->parent = hdr; + temp->parent = hdr->parent; + if(temp->parent == NIL) + gFreeTreeRoot = temp; + else if(hdr == hdr->parent->left) + hdr->parent->left = temp; + else + hdr->parent->right = temp; + temp->right = hdr; + hdr->parent = temp; +} +#endif + +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +static void treeInsert(FreeHeader *fhdr) +{ +#ifdef TORQUE_DEBUG_GUARD + checkGuard((Header *) fhdr, true); // check to see that it's got allocated guards + setGuard((Header *) fhdr, false); +#endif + //gInsertCount++; + + TreeNode *newParent = NIL; + TreeNode *walk = gFreeTreeRoot; + while(walk != NIL) + { + newParent = walk; + if(fhdr->size < walk->size) + walk = walk->left; + else if(fhdr->size > walk->size) + walk = walk->right; + else // tag it on the end of the queue... + { + // insert it on this header... + walk->queueTail->nextQueue = fhdr; + fhdr->prevQueue = walk->queueTail; + walk->queueTail = fhdr; + fhdr->nextQueue = NULL; + fhdr->treeNode = walk; + return; + } + } + TreeNode *hdr = allocTreeNode(); + hdr->size = fhdr->size; + hdr->queueHead = hdr->queueTail = fhdr; + fhdr->nextQueue = fhdr->prevQueue = NULL; + fhdr->treeNode = hdr; + + hdr->left = NIL; + hdr->right = NIL; + + hdr->parent = newParent; + + if(newParent == NIL) + gFreeTreeRoot = hdr; + else if(hdr->size < newParent->size) + newParent->left = hdr; + else + newParent->right = hdr; + + // do red/black rotations + hdr->color = Red; + while(hdr != gFreeTreeRoot && (hdr->parent->color == Red)) + { + TreeNode *parent = hdr->parent; + TreeNode *pparent = hdr->parent->parent; + + if(parent == pparent->left) + { + TreeNode *temp = pparent->right; + if(temp->color == Red) + { + parent->color = Black; + temp->color = Black; + pparent->color = Red; + hdr = pparent; + } + else + { + if(hdr == parent->right) + { + hdr = parent; + rotateLeft(hdr); + parent = hdr->parent; + pparent = hdr->parent->parent; + } + parent->color = Black; + pparent->color = Red; + rotateRight(pparent); + } + } + else + { + TreeNode *temp = pparent->left; + if(temp->color == Red) + { + parent->color = Black; + temp->color = Black; + pparent->color = Red; + hdr = pparent; + } + else + { + if(hdr == parent->left) + { + hdr = parent; + rotateRight(hdr); + parent = hdr->parent; + pparent = hdr->parent->parent; + } + parent->color = Black; + pparent->color = Red; + rotateLeft(pparent); + } + } + } + gFreeTreeRoot->color = Black; + //validateTree(); +} +#endif + +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +static void treeRemove(FreeHeader *hdr) +{ +#ifdef TORQUE_DEBUG_GUARD + checkGuard((Header *) hdr, false); + setGuard((Header *) hdr, true); +#endif + //validateTree(); + //gRemoveCount++; + + FreeHeader *prev = hdr->prevQueue; + FreeHeader *next = hdr->nextQueue; + + if(prev) + prev->nextQueue = next; + else + hdr->treeNode->queueHead = next; + + if(next) + next->prevQueue = prev; + else + hdr->treeNode->queueTail = prev; + + if(prev || next) + return; + + TreeNode *z = hdr->treeNode; + + nil.color = Black; + + TreeNode *y, *x; + if(z->left == NIL || z->right == NIL) + y = z; + else + { + y = z->right; + while(y->left != NIL) + y = y->left; + } + if(y->left != NIL) + x = y->left; + else + x = y->right; + + x->parent = y->parent; + if(y->parent == NIL) + gFreeTreeRoot = x; + else if(y == y->parent->left) + y->parent->left = x; + else + y->parent->right = x; + + U32 yColor = y->color; + if(y != z) + { + // copy y's important fields into z (since we're going to free y) + if(z->parent->left == z) + z->parent->left = y; + else if(z->parent->right == z) + z->parent->right = y; + y->left = z->left; + y->right = z->right; + if(y->left != NIL) + y->left->parent = y; + if(y->right != NIL) + y->right->parent = y; + y->parent = z->parent; + if(z->parent == NIL) + gFreeTreeRoot = y; + y->color = z->color; + if(x->parent == z) + x->parent = y; + //validateTree(); + } + freeTreeNode(z); + + if(yColor == Black) + { + while(x != gFreeTreeRoot && x->color == Black) + { + TreeNode *w; + if(x == x->parent->left) + { + w = x->parent->right; + if(w->color == Red) + { + w->color = Black; + x->parent->color = Red; + rotateLeft(x->parent); + w = x->parent->right; + } + if(w->left->color == Black && w->right->color == Black) + { + w->color = Red; + x = x->parent; + } + else + { + if(w->right->color == Black) + { + w->left->color = Black; + rotateRight(w); + w = x->parent->right; + } + w->color = x->parent->color; + x->parent->color = Black; + w->right->color = Black; + rotateLeft(x->parent); + x = gFreeTreeRoot; + } + } + else + { + w = x->parent->left; + if(w->color == Red) + { + w->color = Black; + x->parent->color = Red; + rotateRight(x->parent); + w = x->parent->left; + } + if(w->left->color == Black && w->right->color == Black) + { + w->color = Red; + x = x->parent; + } + else + { + if(w->left->color == Black) + { + w->right->color = Black; + rotateLeft(w); + w = x->parent->left; + } + w->color = x->parent->color; + x->parent->color = Black; + w->left->color = Black; + rotateRight(x->parent); + x = gFreeTreeRoot; + } + } + } + x->color = Black; + } + //validateTree(); +} +#endif + +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +static FreeHeader *treeFindSmallestGreaterThan(dsize_t size) +{ + TreeNode *bestMatch = NIL; + TreeNode *walk = gFreeTreeRoot; + while(walk != NIL) + { + if(size == walk->size) + return walk->queueHead; + else if(size > walk->size) + walk = walk->right; + else // size < walk->size + { + bestMatch = walk; + walk = walk->left; + } + } + //validateTree(); + if(bestMatch != NIL) + return bestMatch->queueHead; + + return NULL; +} +#endif + +/// Trigger a breakpoint if ptr is not a valid heap pointer or if its memory guards +/// have been destroyed (only if TORQUE_DEBUG_GUARD is enabled). +/// +/// @note This function does not allow interior pointers! + +void checkPtr( void* ptr ) +{ + for( HeapIterator iter; iter.isValid(); ++ iter ) + { + AllocatedHeader* header = ( AllocatedHeader* ) *iter; + if( header->getUserPtr() == ptr ) + { + char buffer[ 1024 ]; + +#ifdef TORQUE_DEBUG_GUARD + if( !checkGuard( *iter, true ) ) + { + dSprintf( buffer, sizeof( buffer ), "0x%x is a valid heap pointer but has its guards corrupted", ptr ); + Platform::outputDebugString( buffer ); + return; + } +#endif + + //dSprintf( buffer, sizeof( buffer ), "0x%x is a valid heap pointer", ptr ); + //Platform::outputDebugString( buffer ); + return; + } + } + + char buffer[ 1024 ]; + dSprintf( buffer, sizeof( buffer ), "0x%x is not a valid heap pointer", ptr ); + Platform::outputDebugString( buffer ); + + Platform::debugBreak(); +} + +/// Dump info on all memory blocks that are still allocated. +/// @note Only works if TORQUE_DISABLE_MEMORY_MANAGER is not defined; otherwise this is a NOP. + +void ensureAllFreed() +{ +#ifndef TORQUE_DISABLE_MEMORY_MANAGER + + U32 numLeaks = 0; + U32 bytesLeaked = 0; + + for( HeapIterator iter; iter.isValid(); ++ iter ) + { + AllocatedHeader* header = ( AllocatedHeader* ) *iter; + if( !( header->flags & GlobalFlag ) ) + { + // Note: can't spill profile paths here since they by + // now are all invalid (they're on the now freed string table) + +#ifdef TORQUE_DEBUG_GUARD + Platform::outputDebugString( "MEMORY LEAKED: 0x%x %i %s %s:%i = %i (%i)", + header->getUserPtr(), + header->allocNum, + ( header->flags & StaticFlag ? "(static)" : "" ), + header->fileName, header->line, header->realSize, header->size ); + numLeaks ++; + bytesLeaked += header->size; +#endif + } + } + + if( numLeaks ) + Platform::outputDebugString( "NUM LEAKS: %i (%i bytes)", numLeaks, bytesLeaked ); +#endif +} + +struct MemDumpLog +{ + U32 size; + U32 count; + U32 depthTotal; + U32 maxDepth; + U32 minDepth; +}; + +void logDumpTraverse(MemDumpLog *sizes, TreeNode *header, U32 depth) +{ + if(header == NIL) + return; + MemDumpLog *mySize = sizes; + while(mySize->size < header->size) + mySize++; + + U32 cnt = 0; + for(FreeHeader *walk = header->queueHead; walk; walk = walk->nextQueue) + cnt++; + mySize->count += cnt; + mySize->depthTotal += depth * cnt; + mySize->maxDepth = depth > mySize->maxDepth ? depth : mySize->maxDepth; + mySize->minDepth = depth < mySize->minDepth ? depth : mySize->minDepth; + logDumpTraverse(sizes, header->left, depth + 1); + logDumpTraverse(sizes, header->right, depth + 1); +} + +#ifdef TORQUE_DEBUG +DefineConsoleFunction( validateMemory, void, ( ),, + "@brief Used to validate memory space for the game.\n\n" + "@ingroup Debugging" ) +{ + validate(); +} +#endif + +DefineConsoleFunction( freeMemoryDump, void, ( ),, + "@brief Dumps some useful statistics regarding free memory.\n\n" + "Dumps an analysis of \'free chunks\' of memory. " + "Does not print how much memory is free.\n\n" + "@ingroup Debugging" ) +{ + U32 startSize = 16; + MemDumpLog memSizes[20]; + U32 i; + for(i = 0; i < 20; i++) + { + memSizes[i].size = startSize << i; + memSizes[i].count = 0; + memSizes[i].depthTotal = 0; + memSizes[i].maxDepth = 0; + memSizes[i].minDepth = 1000; + } + memSizes[19].size = MaxAllocationAmount; + logDumpTraverse(memSizes, gFreeTreeRoot, 1); + MemDumpLog fullMem; + fullMem.count = 0; + fullMem.depthTotal = 0; + fullMem.maxDepth = 0; + fullMem.minDepth = 1000; + + for(i = 0; i < 20; i++) + { + if(memSizes[i].count) + Con::printf("Size: %d - Free blocks: %d Max Depth: %d Min Depth: %d Average Depth: %g", + memSizes[i].size, memSizes[i].count, memSizes[i].maxDepth, memSizes[i].minDepth, + F32(memSizes[i].depthTotal) / F32(memSizes[i].count)); + + fullMem.count += memSizes[i].count; + fullMem.depthTotal += memSizes[i].depthTotal; + fullMem.maxDepth = memSizes[i].maxDepth > fullMem.maxDepth ? memSizes[i].maxDepth : fullMem.maxDepth; + fullMem.minDepth = memSizes[i].minDepth < fullMem.minDepth ? memSizes[i].minDepth : fullMem.minDepth; + } + Con::printf("Total free blocks: %d Max Depth: %d Min Depth: %d Average Depth: %g", + fullMem.count, fullMem.maxDepth, fullMem.minDepth, F32(fullMem.depthTotal) / F32(fullMem.count)); +} + +#ifdef TORQUE_DEBUG_GUARD + +void flagCurrentAllocs( EFlag flag ) +{ +#ifdef TORQUE_ENABLE_PROFILE_PATH + if (gProfiler && !gProfiler->isEnabled()) + { + gProfiler->enable(true); + // warm it up + //gProfiler->dumpToConsole(); + } +#endif + + U32 bit = flagToBit( flag ); + for( HeapIterator iter; iter.isValid(); ++ iter ) + iter->flags |= bit; +} + +DefineEngineFunction(flagCurrentAllocs, void, (),, + "@brief Flags all current memory allocations.\n\n" + "Flags all current memory allocations for exclusion in subsequent calls to dumpUnflaggedAllocs(). " + "Helpful in detecting memory leaks and analyzing memory usage.\n\n" + "@ingroup Debugging" ) +{ + flagCurrentAllocs(); +} + +void dumpUnflaggedAllocs(const char *file, EFlag flag) +{ + countUnflaggedAllocs(file, NULL, flag); +} + +S32 countUnflaggedAllocs(const char * filename, S32 *outUnflaggedRealloc, EFlag flag) +{ + S32 unflaggedAllocCount = 0; + S32 unflaggedReAllocCount = 0; + + FileStream fws; + bool useFile = filename && *filename; + if (useFile) + useFile = fws.open(filename, Torque::FS::File::Write); + char buffer[1024]; + + U32 bit = flagToBit( flag ); + + PageRecord* walk; + for (walk = gPageList; walk; walk = walk->prevPage) + { + for(Header *probe = walk->headerList; probe; probe = probe->next) + { + if (probe->flags & Allocated) + { + AllocatedHeader* pah = (AllocatedHeader*)probe; + if (!(pah->flags & bit)) + { + // If you want to extract further information from an unflagged + // memory allocation, do the following: + // U8 *foo = (U8 *)pah; + // foo += sizeof(Header); + // FooObject *obj = (FooObject *)foo; + dSprintf(buffer, 1023, "%s%s\t%d\t%d\t%d\r\n", + pah->flags & Reallocated ? "[R] " : "", + pah->fileName != NULL ? pah->fileName : "Undetermined", + pah->line, pah->realSize, pah->allocNum); + + if( pah->flags & Reallocated ) + unflaggedReAllocCount++; + else + unflaggedAllocCount++; + + if (useFile) + { + fws.write(dStrlen(buffer), buffer); + fws.write(2, "\r\n"); + } + else + { + if( pah->flags & Reallocated ) + Con::warnf(buffer); + else + Con::errorf(buffer); + } + +#ifdef TORQUE_ENABLE_PROFILE_PATH + static char line[4096]; + dSprintf(line, sizeof(line), " %s\r\nreal size=%d", + pah->profilePath ? pah->profilePath : "unknown", + pah->realSize); + + if (useFile) + { + fws.write(dStrlen(line), line); + fws.write(2, "\r\n"); + } + else + { + if( pah->flags & Reallocated ) + Con::warnf(line); + else + Con::errorf(line); + } +#endif + + } + } + } + } + + if (useFile) + fws.close(); + + if( outUnflaggedRealloc != NULL ) + *outUnflaggedRealloc = unflaggedReAllocCount; + + return unflaggedAllocCount; +} + +DefineEngineFunction(dumpUnflaggedAllocs, void, ( const char* fileName ), ( "" ), + "@brief Dumps all unflagged memory allocations.\n\n" + "Dumps all memory allocations that were made after a call to flagCurrentAllocs(). " + "Helpful when used with flagCurrentAllocs() for detecting memory leaks and analyzing general memory usage.\n\n" + "@param fileName Optional file path and location to dump all memory allocations not flagged by flagCurrentAllocs(). " + "If left blank, data will be dumped to the console.\n\n" + "@tsexample\n" + "dumpMemSnapshot(); // dumps info to console\n" + "dumpMemSnapshot( \"C:/Torque/profilerlog1.txt\" ); // dumps info to file\n" + "@endtsexample\n\n" + "@note Available in debug builds only. " + "In torqueConfig.h, TORQUE_DISABLE_MEMORY_MANAGER must be undefined to use this function.\n\n" + "@ingroup Debugging" ) +{ + dumpUnflaggedAllocs(fileName); +} + +static void initLog() +{ + static const char* sInitString = " --- INIT MEMORY LOG (ACTION): (FILE) (LINE) (SIZE) (ALLOCNUMBER) ---\r\n"; + + FileStream fws; + fws.open(gLogFilename, Torque::FS::File::Write); + fws.write(dStrlen(sInitString), sInitString); + fws.close(); +} + +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +static void logAlloc(const AllocatedHeader* hdr, S32 memSize) +{ + FileStream fws; + fws.open(gLogFilename, Torque::FS::File::ReadWrite); + fws.setPosition(fws.getStreamSize()); + + char buffer[1024]; + dSprintf(buffer, 1023, "alloc: %s %d %d %d\r\n", + hdr->fileName != NULL ? hdr->fileName : "Undetermined", + hdr->line, memSize, hdr->allocNum); + fws.write(dStrlen(buffer), buffer); + fws.close(); +} +#endif + +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +static void logRealloc(const AllocatedHeader* hdr, S32 memSize) +{ + FileStream fws; + fws.open(gLogFilename, Torque::FS::File::ReadWrite); + fws.setPosition(fws.getStreamSize()); + + char buffer[1024]; + dSprintf(buffer, 1023, "realloc: %s %d %d %d\r\n", + hdr->fileName != NULL ? hdr->fileName : "Undetermined", + hdr->line, memSize, hdr->allocNum); + fws.write(dStrlen(buffer), buffer); + fws.close(); +} +#endif + +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +static void logFree(const AllocatedHeader* hdr) +{ + FileStream fws; + fws.open(gLogFilename, Torque::FS::File::ReadWrite); + fws.setPosition(fws.getStreamSize()); + + char buffer[1024]; + dSprintf(buffer, 1023, "free: %s %d %d\r\n", + hdr->fileName != NULL ? hdr->fileName : "Undetermined", + hdr->line, hdr->allocNum); + fws.write(dStrlen(buffer), buffer); + fws.close(); +} +#endif // !defined(TORQUE_DISABLE_MEMORY_MANAGER) + +#endif + +void enableLogging(const char* fileName) +{ + dStrcpy(gLogFilename, fileName); + if (!gEnableLogging) + { + gEnableLogging = true; +#ifdef TORQUE_DEBUG_GUARD + initLog(); +#endif + } +} + +void disableLogging() +{ + gLogFilename[0] = '\0'; + gEnableLogging = false; +} + +// CodeReview - this is never called so commented out to save warning. +// Do we want to re-enable it? Might be nice to get leak tracking on +// exit...or maybe that is just a problematical feature we shouldn't +// worry about. +//static void shutdown() +//{ +//#ifdef TORQUE_MULTITHREAD +// Mutex::destroyMutex(gMemMutex); +// gMemMutex = NULL; +//#endif +// +//#ifdef TORQUE_DEBUG_GUARD +// +// // write out leaks and such +// const U32 maxNumLeaks = 1024; +// U32 numLeaks = 0; +// +// AllocatedHeader* pLeaks[maxNumLeaks]; +// for (PageRecord * walk = gPageList; walk; walk = walk->prevPage) +// for(Header *probe = walk->headerList; probe; probe = probe->next) +// if ((probe->flags & Allocated) && ((AllocatedHeader *)probe)->fileName != NULL) +// pLeaks[numLeaks++] = (AllocatedHeader *) probe; +// +// if (numLeaks && !gNeverLogLeaks) +// { +// if (gAlwaysLogLeaks || Platform::AlertOKCancel("Memory Status", "Memory leaks detected. Write to memoryLeaks.log?") == true) +// { +// char buffer[1024]; +// FileStream logFile; +// logFile.open("memoryLeaks.log", Torque::FS::File::Write); +// +// for (U32 i = 0; i < numLeaks; i++) +// { +// dSprintf(buffer, 1023, "Leak in %s: %d (%d)\r\n", pLeaks[i]->fileName, pLeaks[i]->line, pLeaks[i]->allocNum); +// logFile.write(dStrlen(buffer), buffer); +// } +// logFile.close(); +// } +// } +//#endif +// +// // then free all the memory pages +// for (PageRecord * walk = gPageList; walk; ) +// { +// PageRecord *prev = walk->prevPage; +// dRealFree(walk); +// walk = prev; +// } +//} + +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +static Header *allocMemPage(dsize_t pageSize) +{ + pageSize += sizeof(Header); + if(pageSize < MinPageSize) + pageSize = MinPageSize; + PageRecord *base = allocPage(pageSize); + + Header* rec = (Header*)base->basePtr; + base->headerList = rec; + + rec->size = pageSize - sizeof(Header); + rec->next = NULL; + rec->prev = NULL; + rec->flags = 0; +#ifdef TORQUE_DEBUG_GUARD + setGuard(rec, true); +#endif + +#ifdef LOG_PAGE_ALLOCS + gPageBytesAllocated += pageSize; + // total bytes allocated so far will be 0 when TORQUE_DEBUG_GUARD is disabled, so convert that into more meaningful string + const U32 StrSize = 256; + char strBytesAllocated[StrSize]; + if (gBytesAllocated > 0) + dSprintf(strBytesAllocated, sizeof(strBytesAllocated), "%i", gBytesAllocated); + else + dStrncpy(strBytesAllocated,"unknown - enable TORQUE_DEBUG_GUARD", StrSize); + +#ifndef TORQUE_MULTITHREAD // May deadlock. + // NOTE: This code may be called within Con::_printf, and if that is the case + // this will infinitly recurse. This is the reason for the code in Con::_printf + // that sets Con::active to false. -patw + if (Con::isActive()) + Con::errorf("PlatformMemory: allocating new page, total bytes allocated so far: %s (total bytes in all pages=%i)",strBytesAllocated,gPageBytesAllocated); +#endif +#endif + return rec; +} +#endif + +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +static void checkUnusedAlloc(FreeHeader *header, U32 size) +{ + //validate(); + if(header->size >= size + sizeof(Header) + 16) + { + U8 *basePtr = (U8 *) header; + basePtr += sizeof(Header); + FreeHeader *newHeader = (FreeHeader *) (basePtr + size); + newHeader->next = header->next; + newHeader->prev = (Header *) header; + header->next = (Header *) newHeader; + if(newHeader->next) + newHeader->next->prev = (Header *) newHeader; + newHeader->flags = 0; + newHeader->size = header->size - size - sizeof(Header); + header->size = size; +#ifdef TORQUE_DEBUG_GUARD + setGuard((Header *) newHeader, true); +#endif + treeInsert(newHeader); + } +} +#endif + +#if defined(TORQUE_MULTITHREAD) && !defined(TORQUE_DISABLE_MEMORY_MANAGER) +static bool gReentrantGuard = false; +#endif + +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +static void* alloc(dsize_t size, bool array, const char* fileName, const U32 line) +{ + AssertFatal(size < MaxAllocationAmount, "Memory::alloc - tried to allocate > MaxAllocationAmount!"); + +#ifdef TORQUE_MULTITHREAD + if(!gMemMutex && !gReentrantGuard) + { + gReentrantGuard = true; + gMemMutex = Mutex::createMutex(); + gReentrantGuard = false; + } + + if(!gReentrantGuard) + Mutex::lockMutex(gMemMutex); + +#endif + + AssertFatal(size < MaxAllocationAmount, "Size error."); + //validate(); + if (size == 0) + { +#ifdef TORQUE_MULTITHREAD + if(!gReentrantGuard) + Mutex::unlockMutex(gMemMutex); +#endif + return NULL; + } + +#ifndef TORQUE_ENABLE_PROFILE_PATH + // Note: will cause crash if profile path is on + PROFILE_START(MemoryAlloc); +#endif + +#ifdef TORQUE_DEBUG_GUARD + // if we're guarding, round up to the nearest DWORD + size = ((size + 3) & ~0x3); +#else + // round up size to nearest 16 byte boundary (cache lines and all...) + size = ((size + 15) & ~0xF); +#endif + + FreeHeader *header = treeFindSmallestGreaterThan(size); + if(header) + treeRemove(header); + else + header = (FreeHeader *) allocMemPage(size); + + // ok, see if there's enough room in the block to make another block + // for this to happen it has to have enough room for a header + // and 16 more bytes. + + U8 *basePtr = (U8 *) header; + basePtr += sizeof(Header); + + checkUnusedAlloc(header, size); + + AllocatedHeader *retHeader = (AllocatedHeader *) header; + retHeader->flags = array ? (Allocated | Array) : Allocated; + +#ifdef TORQUE_DEBUG_GUARD + retHeader->line = line; + retHeader->fileName = fileName; + retHeader->allocNum = gCurrAlloc; + retHeader->realSize = size; +#ifdef TORQUE_ENABLE_PROFILE_PATH + retHeader->profilePath = gProfiler ? gProfiler->getProfilePath() : "pre"; +#endif + gBytesAllocated += size; + gBlocksAllocated ++; + //static U32 skip = 0; + //if ((++skip % 1000) == 0) + // Con::printf("new=%i, newnew=%i, imagenew=%i",gBytesAllocated,gNewNewTotal,gImageAlloc); + if (gEnableLogging) + logAlloc(retHeader, size); +#endif + if(gCurrAlloc == gBreakAlloc && gBreakAlloc != 0xFFFFFFFF) + Platform::debugBreak(); + gCurrAlloc++; +#ifndef TORQUE_ENABLE_PROFILE_PATH + PROFILE_END(); +#endif + //validate(); + +#ifdef TORQUE_DEBUG + // fill the block with the fill value. although this is done in free(), that won't fill + // newly allocated MM memory (which hasn't been freed yet). We use a different fill value + // to diffentiate filled freed memory from filled new memory; this may aid debugging. + #ifndef TORQUE_ENABLE_PROFILE_PATH + PROFILE_START(stompMem1); + #endif + dMemset(basePtr, 0xCF, size); + #ifndef TORQUE_ENABLE_PROFILE_PATH + PROFILE_END(); + #endif +#endif + + if(gCurrAlloc == gBreakAlloc && gBreakAlloc != 0xFFFFFFFF) + Platform::debugBreak(); + + gCurrAlloc++; + +#ifdef TORQUE_MULTITHREAD + if(!gReentrantGuard) + Mutex::unlockMutex(gMemMutex); +#endif + + return basePtr; +} +#endif + +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +static void free(void* mem, bool array) +{ + // validate(); + + if (!mem) + return; + +#ifdef TORQUE_MULTITHREAD + if(!gMemMutex) + gMemMutex = Mutex::createMutex(); + + if( mem != gMemMutex ) + Mutex::lockMutex(gMemMutex); + else + gMemMutex = NULL; +#endif + + PROFILE_START(MemoryFree); + AllocatedHeader *hdr = ((AllocatedHeader *)mem) - 1; + + AssertFatal(hdr->flags & Allocated, avar("Not an allocated block!")); + AssertFatal(((bool)((hdr->flags & Array)==Array))==array, avar("Array alloc mismatch. ")); + + gBlocksAllocated --; +#ifdef TORQUE_DEBUG_GUARD + gBytesAllocated -= hdr->realSize; + if (gEnableLogging) + logFree(hdr); +#endif + + hdr->flags = 0; + + // fill the block with the fill value + +#ifdef TORQUE_DEBUG + #ifndef TORQUE_ENABLE_PROFILE_PATH + PROFILE_START(stompMem2); + #endif + dMemset(mem, 0xCE, hdr->size); + #ifndef TORQUE_ENABLE_PROFILE_PATH + PROFILE_END(); + #endif +#endif + + // see if we can merge hdr with the block after it. + + Header* next = hdr->next; + if (next && next->flags == 0) + { + treeRemove((FreeHeader *) next); + hdr->size += next->size + sizeof(Header); + hdr->next = next->next; + if(next->next) + next->next->prev = (Header *) hdr; + } + + // see if we can merge hdr with the block before it. + Header* prev = hdr->prev; + + if (prev && prev->flags == 0) + { + treeRemove((FreeHeader *) prev); + prev->size += hdr->size + sizeof(Header); + prev->next = hdr->next; + if (hdr->next) + hdr->next->prev = prev; + + hdr = (AllocatedHeader *) prev; + } + + // throw this puppy into the tree! + treeInsert((FreeHeader *) hdr); + PROFILE_END(); + +// validate(); + +#ifdef TORQUE_MULTITHREAD + Mutex::unlockMutex(gMemMutex); +#endif +} +#endif + +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) +static void* realloc(void* mem, dsize_t size, const char* fileName, const U32 line) +{ + //validate(); + if (!size) { + free(mem, false); + return NULL; + } + if(!mem) + return alloc(size, false, fileName, line); + +#ifdef TORQUE_MULTITHREAD + if(!gMemMutex) + gMemMutex = Mutex::createMutex(); + + Mutex::lockMutex(gMemMutex); +#endif + + AllocatedHeader* hdr = ((AllocatedHeader *)mem) - 1; +#ifdef TORQUE_DEBUG_GUARD + checkGuard( ( Header* ) hdr, true ); +#endif + + AssertFatal((hdr->flags & Allocated) == Allocated, "Bad block flags."); + + size = (size + 0xF) & ~0xF; + + U32 oldSize = hdr->size; + if(oldSize == size) + { +#ifdef TORQUE_MULTITHREAD + Mutex::unlockMutex(gMemMutex); +#endif + return mem; + } + PROFILE_START(MemoryRealloc); + + FreeHeader *next = (FreeHeader *) hdr->next; + +#ifdef TORQUE_DEBUG_GUARD + // adjust header size and allocated bytes size + hdr->realSize += size - oldSize; + gBytesAllocated += size - oldSize; + if (gEnableLogging) + logRealloc(hdr, size); + + // Add reallocated flag, note header changes will not persist if the realloc + // decides tofree, and then perform a fresh allocation for the memory. The flag will + // be manually set again after this takes place, down at the bottom of this fxn. + hdr->flags |= Reallocated; + + // Note on Above ^ + // A more useful/robust implementation can be accomplished by storing an additional + // AllocatedHeader* in DEBUG_GUARD builds inside the AllocatedHeader structure + // itself to create a sort of reallocation history. This will be, essentially, + // a allocation header stack for each allocation. Each time the memory is reallocated + // it should use dRealMalloc (IMPORTANT!!) to allocate a AllocatedHeader* and chain + // it to the reallocation history chain, and the dump output changed to display + // reallocation history. It is also important to clean up this chain during 'free' + // using dRealFree (Since memory for the chain was allocated via dRealMalloc). + // + // See patw for details. +#endif + if (next && !(next->flags & Allocated) && next->size + hdr->size + sizeof(Header) >= size) + { + // we can merge with the next dude. + treeRemove(next); + hdr->size += sizeof(Header) + next->size; + hdr->next = next->next; + if(next->next) + next->next->prev = (Header *) hdr; + + checkUnusedAlloc((FreeHeader *) hdr, size); + //validate(); + PROFILE_END(); +#ifdef TORQUE_MULTITHREAD + Mutex::unlockMutex(gMemMutex); +#endif + return mem; + } + else if(size < oldSize) + { + checkUnusedAlloc((FreeHeader *) hdr, size); + PROFILE_END(); +#ifdef TORQUE_MULTITHREAD + Mutex::unlockMutex(gMemMutex); +#endif + return mem; + } +#ifdef TORQUE_DEBUG_GUARD + // undo above adjustment because we're going though alloc instead + hdr->realSize -= size - oldSize; + gBytesAllocated -= size - oldSize; +#endif + void* ret = alloc(size, false, fileName, line); + dMemcpy(ret, mem, oldSize); + free(mem, false); + PROFILE_END(); + + // Re-enable the 'Reallocated' flag so that this allocation can be ignored by + // a non-strict run of the flag/dumpunflagged. + hdr = ((AllocatedHeader *)ret) - 1; + hdr->flags |= Reallocated; + +#ifdef TORQUE_MULTITHREAD + Mutex::unlockMutex(gMemMutex); +#endif + return ret; +} +#endif + +dsize_t getMemoryUsed() +{ + U32 size = 0; + + PageRecord* walk; + for (walk = gPageList; walk; walk = walk->prevPage) { + for(Header *probe = walk->headerList; probe; probe = probe->next) + if (probe->flags & Allocated) { + size += probe->size; + } + } + + return size; +} + +#ifdef TORQUE_DEBUG_GUARD +DefineEngineFunction( dumpAlloc, void, ( int allocNum ),, + "@brief Dumps information about the given allocated memory block.\n\n" + "@param allocNum Memory block to dump information about." + "@note Available in debug builds only. " + "In torqueConfig.h, TORQUE_DISABLE_MEMORY_MANAGER must be undefined to use this function.\n\n" + "@ingroup Debugging") +{ + PageRecord* walk; + for( walk = gPageList; walk; walk = walk->prevPage ) + for( Header* probe = walk->headerList; probe; probe = probe->next ) + if( probe->flags & Allocated ) + { + AllocatedHeader* pah = ( AllocatedHeader* ) probe; + if( pah->allocNum == allocNum ) + { + Con::printf( "file: %s\n" + "line: %i\n" + "size: %i\n" + "allocNum: %i\n" + "reallocated: %s", + pah->fileName != NULL ? pah->fileName : "Undetermined", + pah->line, + pah->realSize, + pah->allocNum, + pah->flags & Reallocated ? "yes" : "no" + ); + + // Dump the profile path, if we have one. + + #ifdef TORQUE_ENABLE_PROFILE_PATH + if( pah->profilePath && pah->profilePath[ 0 ] ) + Con::printf( "profilepath: %s", pah->profilePath ); + #endif + } + } +} + +DefineEngineFunction( dumpMemSnapshot, void, ( const char* fileName ),, + "@brief Dumps a snapshot of current memory to a file.\n\n" + "The total memory used will also be output to the console.\n" + "This function will attempt to create the file if it does not already exist.\n" + "@param fileName Name and path of file to save profiling stats to. Must use forward slashes (/)\n" + "@tsexample\n" + "dumpMemSnapshot( \"C:/Torque/ProfilerLogs/profilerlog1.txt\" );\n" + "@endtsexample\n\n" + "@note Available in debug builds only. " + "In torqueConfig.h, TORQUE_DISABLE_MEMORY_MANAGER must be undefined to use this function.\n\n" + "@ingroup Debugging") +{ + FileStream fws; + fws.open(fileName, Torque::FS::File::Write); + char buffer[ 2048 ]; + + PageRecord* walk; + for (walk = gPageList; walk; walk = walk->prevPage) { + for(Header *probe = walk->headerList; probe; probe = probe->next) + if (probe->flags & Allocated) { + AllocatedHeader* pah = (AllocatedHeader*)probe; + + dSprintf( buffer, sizeof( buffer ), "%s%s\t%d\t%d\t%d\r\n", + pah->flags & Reallocated ? "[R] " : "", + pah->fileName != NULL ? pah->fileName : "Undetermined", + pah->line, pah->realSize, pah->allocNum); + fws.write(dStrlen(buffer), buffer); + + // Dump the profile path, if we have one. + + #ifdef TORQUE_ENABLE_PROFILE_PATH + if( pah->profilePath ) + { + dSprintf( buffer, sizeof( buffer ), "%s\r\n\r\n", pah->profilePath ); + fws.write( dStrlen( buffer ), buffer ); + } + #endif + } + } + + Con::errorf("total memory used: %d",getMemoryUsed()); + fws.close(); +} +#endif + +dsize_t getMemoryAllocated() +{ + return 0; +} + +void getMemoryInfo( void* ptr, Info& info ) +{ + #ifndef TORQUE_DISABLE_MEMORY_MANAGER + + AllocatedHeader* header = ( ( AllocatedHeader* ) ptr ) - 1; + + info.mAllocSize = header->size; + #ifdef TORQUE_DEBUG_GUARD + info.mAllocNumber = header->allocNum; + info.mLineNumber = header->line; + info.mFileName = header->fileName; + #endif + info.mIsArray = header->flags & Array; + info.mIsGlobal = header->flags & GlobalFlag; + info.mIsStatic = header->flags & StaticFlag; + + #endif +} + +void setBreakAlloc(U32 breakAlloc) +{ + gBreakAlloc = breakAlloc; +} + +ConsoleFunctionGroupEnd( Memory ); + +} // namespace Memory + +void setMinimumAllocUnit(U32 allocUnit) +{ + AssertFatal(isPow2(allocUnit) && allocUnit > (2 << 20), + "Error, allocunit must be a power of two, and greater than 2 megs"); + + MinPageSize = allocUnit; +} + +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- + +#if !defined(TORQUE_DISABLE_MEMORY_MANAGER) + +// Manage our own memory, add overloaded memory operators and functions + +void* FN_CDECL operator new(dsize_t size, const char* fileName, const U32 line) +{ + return Memory::alloc(size, false, fileName, line); +} + +void* FN_CDECL operator new[](dsize_t size, const char* fileName, const U32 line) +{ + return Memory::alloc(size, true, fileName, line); +} + +void* FN_CDECL operator new(dsize_t size) +{ + return Memory::alloc(size, false, NULL, 0); +} + +void* FN_CDECL operator new[](dsize_t size) +{ + return Memory::alloc(size, true, NULL, 0); +} + +void FN_CDECL operator delete(void* mem) +{ + Memory::free(mem, false); +} + +void FN_CDECL operator delete[](void* mem) +{ + Memory::free(mem, true); +} + +void* dMalloc_r(dsize_t in_size, const char* fileName, const dsize_t line) +{ + return Memory::alloc(in_size, false, fileName, line); +} + +void dFree(void* in_pFree) +{ + Memory::free(in_pFree, false); +} + +void* dRealloc_r(void* in_pResize, dsize_t in_size, const char* fileName, const dsize_t line) +{ + return Memory::realloc(in_pResize, in_size, fileName, line); +} + +AFTER_MODULE_INIT( Sim ) +{ + Con::addVariable( "$Memory::numBlocksAllocated", TypeS32, &Memory::gBlocksAllocated, + "Total number of memory blocks currently allocated.\n\n" + "@ingroup Debugging" ); + Con::addVariable( "$Memory::numBytesAllocated", TypeS32, &Memory::gBytesAllocated, + "Total number of bytes currently allocated.\n\n" + "@ingroup Debugging" ); +} + +#else + +// Don't manage our own memory +void* dMalloc_r(dsize_t in_size, const char* fileName, const dsize_t line) +{ + return malloc(in_size); +} + +void dFree(void* in_pFree) +{ + free(in_pFree); +} + +void* dRealloc_r(void* in_pResize, dsize_t in_size, const char* fileName, const dsize_t line) +{ + return realloc(in_pResize,in_size); +} + +#endif diff --git a/Engine/source/platform/platformMemory.h b/Engine/source/platform/platformMemory.h new file mode 100644 index 000000000..96db06584 --- /dev/null +++ b/Engine/source/platform/platformMemory.h @@ -0,0 +1,59 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TORQUE_PLATFORM_PLATFORMMEMORY_H_ +#define _TORQUE_PLATFORM_PLATFORMMEMORY_H_ + +#include "platform/platform.h" + +namespace Memory +{ + enum EFlag + { + FLAG_Debug, + FLAG_Global, + FLAG_Static + }; + + struct Info + { + U32 mAllocNumber; + U32 mAllocSize; + const char* mFileName; + U32 mLineNumber; + bool mIsArray; + bool mIsGlobal; + bool mIsStatic; + }; + + void checkPtr( void* ptr ); + void flagCurrentAllocs( EFlag flag = FLAG_Debug ); + void ensureAllFreed(); + void dumpUnflaggedAllocs(const char *file, EFlag flag = FLAG_Debug ); + S32 countUnflaggedAllocs(const char *file, S32 *outUnflaggedRealloc = NULL, EFlag flag = FLAG_Debug ); + dsize_t getMemoryUsed(); + dsize_t getMemoryAllocated(); + void getMemoryInfo( void* ptr, Info& info ); + void validate(); +} + +#endif // _TORQUE_PLATFORM_PLATFORMMEMORY_H_ diff --git a/Engine/source/platform/platformNet.cpp b/Engine/source/platform/platformNet.cpp new file mode 100644 index 000000000..30dcee4c4 --- /dev/null +++ b/Engine/source/platform/platformNet.cpp @@ -0,0 +1,1051 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platformNet.h" +#include "platform/event.h" +#include "core/strings/stringFunctions.h" + +#if defined (TORQUE_OS_WIN32) +#define TORQUE_USE_WINSOCK +#include +#include +#define EINPROGRESS WSAEINPROGRESS +#define ioctl ioctlsocket + +typedef int socklen_t; + +#elif defined ( TORQUE_OS_MAC ) + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef sockaddr_in SOCKADDR_IN; +typedef sockaddr * PSOCKADDR; +typedef sockaddr SOCKADDR; +typedef in_addr IN_ADDR; + +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 + +#define closesocket close + +#elif defined TORQUE_OS_LINUX + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef sockaddr_in SOCKADDR_IN; +typedef sockaddr * PSOCKADDR; +typedef sockaddr SOCKADDR; +typedef in_addr IN_ADDR; + +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 + +#define closesocket close + +#elif defined( TORQUE_OS_XENON ) + +#include +#include + +#define TORQUE_USE_WINSOCK +#define EINPROGRESS WSAEINPROGRESS +#define ioctl ioctlsocket +typedef int socklen_t; + +DWORD _getLastErrorAndClear() +{ + DWORD err = WSAGetLastError(); + WSASetLastError( 0 ); + + return err; +} + +#else + +#endif + +#if defined(TORQUE_USE_WINSOCK) +static const char* strerror_wsa( int code ) +{ + switch( code ) + { +#define E( name ) case name: return #name; + E( WSANOTINITIALISED ); + E( WSAENETDOWN ); + E( WSAEADDRINUSE ); + E( WSAEINPROGRESS ); + E( WSAEALREADY ); + E( WSAEADDRNOTAVAIL ); + E( WSAEAFNOSUPPORT ); + E( WSAEFAULT ); + E( WSAEINVAL ); + E( WSAEISCONN ); + E( WSAENETUNREACH ); + E( WSAEHOSTUNREACH ); + E( WSAENOBUFS ); + E( WSAENOTSOCK ); + E( WSAETIMEDOUT ); + E( WSAEWOULDBLOCK ); + E( WSAEACCES ); +#undef E + default: + return "Unknown"; + } +} +#endif + +#include "core/util/tVector.h" +#include "platform/platformNetAsync.h" +#include "console/console.h" +#include "core/util/journal/process.h" +#include "core/util/journal/journal.h" + +static Net::Error getLastError(); +static S32 defaultPort = 28000; +static S32 netPort = 0; +static int udpSocket = InvalidSocket; + +ConnectionNotifyEvent Net::smConnectionNotify; +ConnectionAcceptedEvent Net::smConnectionAccept; +ConnectionReceiveEvent Net::smConnectionReceive; +PacketReceiveEvent Net::smPacketReceive; + +// local enum for socket states for polled sockets +enum SocketState +{ + InvalidState, + Connected, + ConnectionPending, + Listening, + NameLookupRequired +}; + +// the Socket structure helps us keep track of the +// above states +struct Socket +{ + Socket() + { + fd = InvalidSocket; + state = InvalidState; + remoteAddr[0] = 0; + remotePort = -1; + } + + NetSocket fd; + S32 state; + char remoteAddr[256]; + S32 remotePort; +}; + +// list of polled sockets +static Vector gPolledSockets( __FILE__, __LINE__ ); + +static Socket* addPolledSocket(NetSocket& fd, S32 state, + char* remoteAddr = NULL, S32 port = -1) +{ + Socket* sock = new Socket(); + sock->fd = fd; + sock->state = state; + if (remoteAddr) + dStrcpy(sock->remoteAddr, remoteAddr); + if (port != -1) + sock->remotePort = port; + gPolledSockets.push_back(sock); + return sock; +} + +enum { + MaxConnections = 1024, +}; + + +bool netSocketWaitForWritable(NetSocket fd, S32 timeoutMs) +{ + fd_set writefds; + timeval timeout; + + FD_ZERO(&writefds); + FD_SET( fd, &writefds ); + + timeout.tv_sec = timeoutMs / 1000; + timeout.tv_usec = ( timeoutMs % 1000 ) * 1000; + + if( select(fd + 1, NULL, &writefds, NULL, &timeout) > 0 ) + return true; + + return false; +} + +static S32 initCount = 0; + +bool Net::init() +{ +#if defined(TORQUE_USE_WINSOCK) + if(!initCount) + { +#ifdef TORQUE_OS_XENON + // Configure startup parameters + XNetStartupParams xnsp; + memset( &xnsp, 0, sizeof( xnsp ) ); + xnsp.cfgSizeOfStruct = sizeof( XNetStartupParams ); + +#ifndef TORQUE_DISABLE_PC_CONNECTIVITY + xnsp.cfgFlags = XNET_STARTUP_BYPASS_SECURITY; + Con::warnf("XNET_STARTUP_BYPASS_SECURITY enabled! This build can talk to PCs!"); +#endif + + AssertISV( !XNetStartup( &xnsp ), "Net::init - failed to init XNet" ); +#endif + + WSADATA stWSAData; + AssertISV( !WSAStartup( 0x0101, &stWSAData ), "Net::init - failed to init WinSock!" ); + + //logprintf("Winsock initialization %s", success ? "succeeded." : "failed!"); + } +#endif + initCount++; + + Process::notify(&Net::process, PROCESS_NET_ORDER); + + return(true); +} + +void Net::shutdown() +{ + Process::remove(&Net::process); + + while (gPolledSockets.size() > 0) + closeConnectTo(gPolledSockets[0]->fd); + + closePort(); + initCount--; + +#if defined(TORQUE_USE_WINSOCK) + if(!initCount) + { + WSACleanup(); + +#ifdef TORQUE_OS_XENON + XNetCleanup(); +#endif + } +#endif +} + +Net::Error getLastError() +{ +#if defined(TORQUE_USE_WINSOCK) + S32 err = WSAGetLastError(); + switch(err) + { + case 0: + return Net::NoError; + case WSAEWOULDBLOCK: + return Net::WouldBlock; + default: + return Net::UnknownError; + } +#else + if (errno == EAGAIN) + return Net::WouldBlock; + if (errno == 0) + return Net::NoError; + return Net::UnknownError; +#endif +} + +static void netToIPSocketAddress(const NetAddress *address, struct sockaddr_in *sockAddr) +{ + dMemset(sockAddr, 0, sizeof(struct sockaddr_in)); + sockAddr->sin_family = AF_INET; + sockAddr->sin_port = htons(address->port); + char tAddr[20]; + dSprintf(tAddr, 20, "%d.%d.%d.%d", address->netNum[0], address->netNum[1], address->netNum[2], address->netNum[3]); + //fprintf(stdout,"netToIPSocketAddress(): %s\n",tAddr);fflush(NULL); + sockAddr->sin_addr.s_addr = inet_addr(tAddr); +} + +static void IPSocketToNetAddress(const struct sockaddr_in *sockAddr, NetAddress *address) +{ + address->type = NetAddress::IPAddress; + address->port = htons(sockAddr->sin_port); +#ifndef TORQUE_OS_XENON + char *tAddr; + tAddr = inet_ntoa(sockAddr->sin_addr); + //fprintf(stdout,"IPSocketToNetAddress(): %s\n",tAddr);fflush(NULL); + U8 nets[4]; + nets[0] = atoi(strtok(tAddr, ".")); + nets[1] = atoi(strtok(NULL, ".")); + nets[2] = atoi(strtok(NULL, ".")); + nets[3] = atoi(strtok(NULL, ".")); + //fprintf(stdout,"0 = %d, 1 = %d, 2 = %d, 3 = %d\n", nets[0], nets[1], nets[2], nets[3]); + address->netNum[0] = nets[0]; + address->netNum[1] = nets[1]; + address->netNum[2] = nets[2]; + address->netNum[3] = nets[3]; +#else + address->netNum[0] = sockAddr->sin_addr.s_net; + address->netNum[1] = sockAddr->sin_addr.s_host; + address->netNum[2] = sockAddr->sin_addr.s_lh; + address->netNum[3] = sockAddr->sin_addr.s_impno; +#endif +} + +NetSocket Net::openListenPort(U16 port) +{ + if(Journal::IsPlaying()) + { + U32 ret; + Journal::Read(&ret); + return NetSocket(ret); + } + + NetSocket sock = openSocket(); + if (sock == InvalidSocket) + { + Con::errorf("Unable to open listen socket: %s", strerror(errno)); + return InvalidSocket; + } + + if (bind(sock, port) != NoError) + { + Con::errorf("Unable to bind port %d: %s", port, strerror(errno)); + ::closesocket(sock); + return InvalidSocket; + } + if (listen(sock, 4) != NoError) + { + Con::errorf("Unable to listen on port %d: %s", port, strerror(errno)); + ::closesocket(sock); + return InvalidSocket; + } + + setBlocking(sock, false); + addPolledSocket(sock, Listening); + + if(Journal::IsRecording()) + Journal::Write(U32(sock)); + + return sock; +} + +NetSocket Net::openConnectTo(const char *addressString) +{ + if(!dStrnicmp(addressString, "ipx:", 4)) + // ipx support deprecated + return InvalidSocket; + if(!dStrnicmp(addressString, "ip:", 3)) + addressString += 3; // eat off the ip: + char remoteAddr[256]; + dStrcpy(remoteAddr, addressString); + + char *portString = dStrchr(remoteAddr, ':'); + + U16 port; + if(portString) + { + *portString++ = 0; + port = htons(dAtoi(portString)); + } + else + port = htons(defaultPort); + + if(!dStricmp(remoteAddr, "broadcast")) + return InvalidSocket; + + if(Journal::IsPlaying()) + { + U32 ret; + Journal::Read(&ret); + return NetSocket(ret); + } + NetSocket sock = openSocket(); + setBlocking(sock, false); + + sockaddr_in ipAddr; + dMemset(&ipAddr, 0, sizeof(ipAddr)); + ipAddr.sin_addr.s_addr = inet_addr(remoteAddr); + + if(ipAddr.sin_addr.s_addr != INADDR_NONE) + { + ipAddr.sin_port = port; + ipAddr.sin_family = AF_INET; + if(::connect(sock, (struct sockaddr *)&ipAddr, sizeof(ipAddr)) == -1) + { + S32 err = getLastError(); + if(err != Net::WouldBlock) + { + Con::errorf("Error connecting %s: %s", + addressString, strerror(err)); + ::closesocket(sock); + sock = InvalidSocket; + } + } + if(sock != InvalidSocket) + { + // add this socket to our list of polled sockets + addPolledSocket(sock, ConnectionPending); + } + } + else + { + // need to do an asynchronous name lookup. first, add the socket + // to the polled list + addPolledSocket(sock, NameLookupRequired, remoteAddr, port); + // queue the lookup + gNetAsync.queueLookup(remoteAddr, sock); + } + if(Journal::IsRecording()) + Journal::Write(U32(sock)); + return sock; +} + +void Net::closeConnectTo(NetSocket sock) +{ + if(Journal::IsPlaying()) + return; + + // if this socket is in the list of polled sockets, remove it + for (int i = 0; i < gPolledSockets.size(); ++i) + { + if (gPolledSockets[i]->fd == sock) + { + delete gPolledSockets[i]; + gPolledSockets.erase(i); + break; + } + } + + closeSocket(sock); +} + +Net::Error Net::sendtoSocket(NetSocket socket, const U8 *buffer, int bufferSize) +{ + if(Journal::IsPlaying()) + { + U32 e; + Journal::Read(&e); + + return (Net::Error) e; + } + + Net::Error e = send(socket, buffer, bufferSize); + + if(Journal::IsRecording()) + Journal::Write(U32(e)); + + return e; +} + +bool Net::openPort(S32 port, bool doBind) +{ + if(udpSocket != InvalidSocket) + ::closesocket(udpSocket); + + // we turn off VDP in non-release builds because VDP does not support broadcast packets + // which are required for LAN queries (PC->Xbox connectivity). The wire protocol still + // uses the VDP packet structure, though. + int protocol = 0; + bool useVDP = false; +#ifdef TORQUE_DISABLE_PC_CONNECTIVITY + // Xbox uses a VDP (voice/data protocol) socket for networking + protocol = IPPROTO_VDP; + useVDP = true; +#endif + + udpSocket = socket(AF_INET, SOCK_DGRAM, protocol); + + if(udpSocket != InvalidSocket) + { + Net::Error error = NoError; + if (doBind) + { + error = bind(udpSocket, port); + } + + if(error == NoError) + error = setBufferSize(udpSocket, 32768); + + if(error == NoError && !useVDP) + error = setBroadcast(udpSocket, true); + + if(error == NoError) + error = setBlocking(udpSocket, false); + + if(error == NoError) + Con::printf("UDP initialized on port %d", port); + else + { + ::closesocket(udpSocket); + udpSocket = InvalidSocket; + Con::printf("Unable to initialize UDP - error %d", error); + } + } + netPort = port; + return udpSocket != InvalidSocket; +} + +NetSocket Net::getPort() + +{ + + return udpSocket; + +} + + +void Net::closePort() +{ + if(udpSocket != InvalidSocket) + ::closesocket(udpSocket); +} + +Net::Error Net::sendto(const NetAddress *address, const U8 *buffer, S32 bufferSize) +{ + if(Journal::IsPlaying()) + return NoError; + + if(address->type == NetAddress::IPAddress) + { + sockaddr_in ipAddr; + netToIPSocketAddress(address, &ipAddr); + if(::sendto(udpSocket, (const char*)buffer, bufferSize, 0, + (sockaddr *) &ipAddr, sizeof(sockaddr_in)) == SOCKET_ERROR) + return getLastError(); + else + return NoError; + } + else + { + SOCKADDR_IN ipAddr; + netToIPSocketAddress(address, &ipAddr); + if(::sendto(udpSocket, (const char*)buffer, bufferSize, 0, + (PSOCKADDR) &ipAddr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR) + return getLastError(); + else + return NoError; + } +} + +void Net::process() +{ + sockaddr sa; + sa.sa_family = AF_UNSPEC; + NetAddress srcAddress; + RawData tmpBuffer; + tmpBuffer.alloc(MaxPacketDataSize); + + for(;;) + { + socklen_t addrLen = sizeof(sa); + S32 bytesRead = -1; + + if(udpSocket != InvalidSocket) + bytesRead = recvfrom(udpSocket, (char *) tmpBuffer.data, MaxPacketDataSize, 0, &sa, &addrLen); + + if(bytesRead == -1) + break; + + if(sa.sa_family == AF_INET) + IPSocketToNetAddress((sockaddr_in *) &sa, &srcAddress); + else + continue; + + if(bytesRead <= 0) + continue; + + if(srcAddress.type == NetAddress::IPAddress && + srcAddress.netNum[0] == 127 && + srcAddress.netNum[1] == 0 && + srcAddress.netNum[2] == 0 && + srcAddress.netNum[3] == 1 && + srcAddress.port == netPort) + continue; + + tmpBuffer.size = bytesRead; + + Net::smPacketReceive.trigger(srcAddress, tmpBuffer); + } + + // process the polled sockets. This blob of code performs functions + // similar to WinsockProc in winNet.cc + + if (gPolledSockets.size() == 0) + return; + + S32 optval; + socklen_t optlen = sizeof(S32); + S32 bytesRead; + Net::Error err; + bool removeSock = false; + Socket *currentSock = NULL; + sockaddr_in ipAddr; + NetSocket incoming = InvalidSocket; + char out_h_addr[1024]; + int out_h_length = 0; + RawData readBuff; + + for (S32 i = 0; i < gPolledSockets.size(); + /* no increment, this is done at end of loop body */) + { + removeSock = false; + currentSock = gPolledSockets[i]; + switch (currentSock->state) + { + case ::InvalidState: + Con::errorf("Error, InvalidState socket in polled sockets list"); + break; + case ::ConnectionPending: + // see if it is now connected +#ifdef TORQUE_OS_XENON + // WSASetLastError has no return value, however part of the SO_ERROR behavior + // is to clear the last error, so this needs to be done here. + if( ( optval = _getLastErrorAndClear() ) == -1 ) +#else + if (getsockopt(currentSock->fd, SOL_SOCKET, SO_ERROR, + (char*)&optval, &optlen) == -1) +#endif + { + Con::errorf("Error getting socket options: %s", strerror(errno)); + + Net::smConnectionNotify.trigger(currentSock->fd, Net::ConnectFailed); + removeSock = true; + } + else + { + if (optval == EINPROGRESS) + // still connecting... + break; + + if (optval == 0) + { + // poll for writable status to be sure we're connected. + bool ready = netSocketWaitForWritable(currentSock->fd,0); + if(!ready) + break; + + currentSock->state = ::Connected; + Net::smConnectionNotify.trigger(currentSock->fd, Net::Connected); + } + else + { + // some kind of error + Con::errorf("Error connecting: %s", strerror(errno)); + Net::smConnectionNotify.trigger(currentSock->fd, Net::ConnectFailed); + removeSock = true; + } + } + break; + case ::Connected: + + // try to get some data + bytesRead = 0; + readBuff.alloc(MaxPacketDataSize); + err = Net::recv(currentSock->fd, (U8*)readBuff.data, MaxPacketDataSize, &bytesRead); + if(err == Net::NoError) + { + if (bytesRead > 0) + { + // got some data, post it + readBuff.size = bytesRead; + Net::smConnectionReceive.trigger(currentSock->fd, readBuff); + } + else + { + // ack! this shouldn't happen + if (bytesRead < 0) + Con::errorf("Unexpected error on socket: %s", strerror(errno)); + + // zero bytes read means EOF + Net::smConnectionNotify.trigger(currentSock->fd, Net::Disconnected); + + removeSock = true; + } + } + else if (err != Net::NoError && err != Net::WouldBlock) + { + Con::errorf("Error reading from socket: %s", strerror(errno)); + Net::smConnectionNotify.trigger(currentSock->fd, Net::Disconnected); + removeSock = true; + } + break; + case ::NameLookupRequired: + // is the lookup complete? + if (!gNetAsync.checkLookup( + currentSock->fd, out_h_addr, &out_h_length, + sizeof(out_h_addr))) + break; + + U32 newState; + if (out_h_length == -1) + { + Con::errorf("DNS lookup failed: %s", currentSock->remoteAddr); + newState = Net::DNSFailed; + removeSock = true; + } + else + { + // try to connect + dMemcpy(&(ipAddr.sin_addr.s_addr), out_h_addr, out_h_length); + ipAddr.sin_port = currentSock->remotePort; + ipAddr.sin_family = AF_INET; + if(::connect(currentSock->fd, (struct sockaddr *)&ipAddr, + sizeof(ipAddr)) == -1) + { + int errorCode; +#if defined(TORQUE_USE_WINSOCK) + errorCode = WSAGetLastError(); + if( errorCode == WSAEINPROGRESS || errorCode == WSAEWOULDBLOCK ) +#else + errorCode = errno; + if (errno == EINPROGRESS) +#endif + { + newState = Net::DNSResolved; + currentSock->state = ::ConnectionPending; + } + else + { + const char* errorString; +#if defined(TORQUE_USE_WINSOCK) + errorString = strerror_wsa( errorCode ); +#else + errorString = strerror( errorCode ); +#endif + Con::errorf("Error connecting to %s: %s (%i)", + currentSock->remoteAddr, errorString, errorCode); + newState = Net::ConnectFailed; + removeSock = true; + } + } + else + { + newState = Net::Connected; + currentSock->state = Net::Connected; + } + } + + Net::smConnectionNotify.trigger(currentSock->fd, newState); + break; + case ::Listening: + NetAddress incomingAddy; + + incoming = Net::accept(currentSock->fd, &incomingAddy); + if(incoming != InvalidSocket) + { + setBlocking(incoming, false); + addPolledSocket(incoming, Connected); + Net::smConnectionAccept.trigger(currentSock->fd, incoming, incomingAddy); + } + break; + } + + // only increment index if we're not removing the connection, since + // the removal will shift the indices down by one + if (removeSock) + closeConnectTo(currentSock->fd); + else + i++; + } +} + +NetSocket Net::openSocket() +{ + int retSocket; + retSocket = socket(AF_INET, SOCK_STREAM, 0); + + if(retSocket == InvalidSocket) + return InvalidSocket; + else + return retSocket; +} + +Net::Error Net::closeSocket(NetSocket socket) +{ + if(socket != InvalidSocket) + { + if(!closesocket(socket)) + return NoError; + else + return getLastError(); + } + else + return NotASocket; +} + +Net::Error Net::connect(NetSocket socket, const NetAddress *address) +{ + if(address->type != NetAddress::IPAddress) + return WrongProtocolType; + sockaddr_in socketAddress; + netToIPSocketAddress(address, &socketAddress); + if(!::connect(socket, (sockaddr *) &socketAddress, sizeof(socketAddress))) + return NoError; + return getLastError(); +} + +Net::Error Net::listen(NetSocket socket, S32 backlog) +{ + if(!::listen(socket, backlog)) + return NoError; + return getLastError(); +} + +NetSocket Net::accept(NetSocket acceptSocket, NetAddress *remoteAddress) +{ + sockaddr_in socketAddress; + socklen_t addrLen = sizeof(socketAddress); + + int retVal = ::accept(acceptSocket, (sockaddr *) &socketAddress, &addrLen); + if(retVal != InvalidSocket) + { + IPSocketToNetAddress(&socketAddress, remoteAddress); + return retVal; + } + return InvalidSocket; +} + +Net::Error Net::bind(NetSocket socket, U16 port) +{ + S32 error; + + sockaddr_in socketAddress; + dMemset((char *)&socketAddress, 0, sizeof(socketAddress)); + socketAddress.sin_family = AF_INET; + // It's entirely possible that there are two NIC cards. + // We let the user specify which one the server runs on. + + // thanks to [TPG]P1aGu3 for the name + const char* serverIP = Con::getVariable( "pref::Net::BindAddress" ); + // serverIP is guaranteed to be non-0. + AssertFatal( serverIP, "serverIP is NULL!" ); + + if( serverIP[0] != '\0' ) { + // we're not empty + socketAddress.sin_addr.s_addr = inet_addr( serverIP ); + + if( socketAddress.sin_addr.s_addr != INADDR_NONE ) { + Con::printf( "Binding server port to %s", serverIP ); + } else { + Con::warnf( ConsoleLogEntry::General, + "inet_addr() failed for %s while binding!", + serverIP ); + socketAddress.sin_addr.s_addr = INADDR_ANY; + } + + } else { + Con::printf( "Binding server port to default IP" ); + socketAddress.sin_addr.s_addr = INADDR_ANY; + } + + socketAddress.sin_port = htons(port); + error = ::bind(socket, (sockaddr *) &socketAddress, sizeof(socketAddress)); + + if(!error) + return NoError; + return getLastError(); +} + +Net::Error Net::setBufferSize(NetSocket socket, S32 bufferSize) +{ + S32 error; + error = setsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char *) &bufferSize, sizeof(bufferSize)); + if(!error) + error = setsockopt(socket, SOL_SOCKET, SO_SNDBUF, (char *) &bufferSize, sizeof(bufferSize)); + if(!error) + return NoError; + return getLastError(); +} + +Net::Error Net::setBroadcast(NetSocket socket, bool broadcast) +{ + S32 bc = broadcast; + S32 error = setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char*)&bc, sizeof(bc)); + if(!error) + return NoError; + return getLastError(); +} + +Net::Error Net::setBlocking(NetSocket socket, bool blockingIO) +{ + unsigned long notblock = !blockingIO; + S32 error = ioctl(socket, FIONBIO, ¬block); + if(!error) + return NoError; + return getLastError(); +} + +Net::Error Net::send(NetSocket socket, const U8 *buffer, S32 bufferSize) +{ + errno = 0; + S32 bytesWritten = ::send(socket, (const char*)buffer, bufferSize, 0); + if(bytesWritten == -1) +#if defined(TORQUE_USE_WINSOCK) + Con::errorf("Could not write to socket. Error: %s",strerror_wsa( WSAGetLastError() )); +#else + Con::errorf("Could not write to socket. Error: %s",strerror(errno)); +#endif + + return getLastError(); +} + +Net::Error Net::recv(NetSocket socket, U8 *buffer, S32 bufferSize, S32 *bytesRead) +{ + *bytesRead = ::recv(socket, (char*)buffer, bufferSize, 0); + if(*bytesRead == -1) + return getLastError(); + return NoError; +} + +bool Net::compareAddresses(const NetAddress *a1, const NetAddress *a2) +{ + if((a1->type != a2->type) || + (*((U32 *)a1->netNum) != *((U32 *)a2->netNum)) || + (a1->port != a2->port)) + return false; + + if(a1->type == NetAddress::IPAddress) + return true; + for(S32 i = 0; i < 6; i++) + if(a1->nodeNum[i] != a2->nodeNum[i]) + return false; + return true; +} + +bool Net::stringToAddress(const char *addressString, NetAddress *address) +{ + if(!dStrnicmp(addressString, "ipx:", 4)) + // ipx support deprecated + return false; + + if(!dStrnicmp(addressString, "ip:", 3)) + addressString += 3; // eat off the ip: + + sockaddr_in ipAddr; + char remoteAddr[256]; + if(strlen(addressString) > 255) + return false; + + dStrcpy(remoteAddr, addressString); + + char *portString = dStrchr(remoteAddr, ':'); + if(portString) + *portString++ = '\0'; + + if(!dStricmp(remoteAddr, "broadcast")) + ipAddr.sin_addr.s_addr = htonl(INADDR_BROADCAST); + else + { + ipAddr.sin_addr.s_addr = inet_addr(remoteAddr); + + if (ipAddr.sin_addr.s_addr == INADDR_NONE) // error + { + // On the Xbox, 'gethostbyname' does not exist so... +#ifndef TORQUE_OS_XENON + struct hostent *hp; + if((hp = gethostbyname(remoteAddr)) == 0) + return false; + else + memcpy(&ipAddr.sin_addr.s_addr, hp->h_addr, sizeof(in_addr)); +#else + // On the Xbox do XNetDnsLookup + XNDNS *pxndns = NULL; + HANDLE hEvent = CreateEvent(NULL, false, false, NULL); + XNetDnsLookup(remoteAddr, hEvent, &pxndns); + + // Wait for event (passing NULL as a handle to XNetDnsLookup will NOT + // cause it to behave synchronously, so do not remove the handle/wait + while(pxndns->iStatus == WSAEINPROGRESS) + WaitForSingleObject(hEvent, INFINITE); + + bool foundAddr = pxndns->iStatus == 0 && pxndns->cina > 0; + if(foundAddr) + { + // Lets just grab the first address returned, for now + memcpy(&ipAddr.sin_addr, pxndns->aina, sizeof(IN_ADDR)); + } + + XNetDnsRelease(pxndns); + CloseHandle(hEvent); + + // If we didn't successfully resolve the DNS lookup, bail after the + // handles are released + if(!foundAddr) + return false; +#endif + } + } + if(portString) + ipAddr.sin_port = htons(dAtoi(portString)); + else + ipAddr.sin_port = htons(defaultPort); + ipAddr.sin_family = AF_INET; + IPSocketToNetAddress(&ipAddr, address); + return true; +} + +void Net::addressToString(const NetAddress *address, char addressString[256]) +{ + if(address->type == NetAddress::IPAddress) + { + sockaddr_in ipAddr; + netToIPSocketAddress(address, &ipAddr); + + if(ipAddr.sin_addr.s_addr == htonl(INADDR_BROADCAST)) + dSprintf(addressString, 256, "IP:Broadcast:%d", ntohs(ipAddr.sin_port)); + else + { +#ifndef TORQUE_OS_XENON + dSprintf(addressString, 256, "IP:%s:%d", inet_ntoa(ipAddr.sin_addr), + ntohs(ipAddr.sin_port)); +#else + dSprintf(addressString, 256, "IP:%d.%d.%d.%d:%d", ipAddr.sin_addr.s_net, + ipAddr.sin_addr.s_host, ipAddr.sin_addr.s_lh, + ipAddr.sin_addr.s_impno, ntohs( ipAddr.sin_port ) ); + +#endif + } + } + else + { + *addressString = 0; + return; + } +} + diff --git a/Engine/source/platform/platformNet.h b/Engine/source/platform/platformNet.h new file mode 100644 index 000000000..b37558ebd --- /dev/null +++ b/Engine/source/platform/platformNet.h @@ -0,0 +1,150 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PLATFORM_PLATFORMNET_H_ +#define _PLATFORM_PLATFORMNET_H_ + +#include "platform/platform.h" +#include "core/util/rawData.h" +#include "core/util/journal/journaledSignal.h" + +#ifndef MAXPACKETSIZE +#define MAXPACKETSIZE 1500 +#endif + +typedef int NetConnectionId; + +/// Generic network address +/// +/// This is used to represent IP addresses. +struct NetAddress +{ + int type; ///< Type of address (IPAddress currently) + + /// Acceptable NetAddress types. + enum + { + IPAddress, + }; + + U8 netNum[4]; ///< For IP: sin_addr
      + U8 nodeNum[6]; ///< For IP: Not used.
      + U16 port; ///< For IP: sin_port
      +}; + +typedef S32 NetSocket; +const NetSocket InvalidSocket = -1; + +/// void event(NetSocket sock, U32 state) +typedef JournaledSignal ConnectionNotifyEvent; + +/// void event(NetSocket listeningPort, NetSocket newConnection, NetAddress originatingAddress) +typedef JournaledSignal ConnectionAcceptedEvent; + +/// void event(NetSocket connection, RawData incomingData) +typedef JournaledSignal ConnectionReceiveEvent; + +/// void event(NetAddress originator, RawData incomingData) +typedef JournaledSignal PacketReceiveEvent; + +/// Platform-specific network operations. +struct Net +{ + enum Error + { + NoError, + WrongProtocolType, + InvalidPacketProtocol, + WouldBlock, + NotASocket, + UnknownError + }; + + enum ConnectionState { + DNSResolved, + DNSFailed, + Connected, + ConnectFailed, + Disconnected + }; + + enum Protocol + { + UDPProtocol, + TCPProtocol + }; + + static const int MaxPacketDataSize = MAXPACKETSIZE; + + static ConnectionNotifyEvent smConnectionNotify; + static ConnectionAcceptedEvent smConnectionAccept; + static ConnectionReceiveEvent smConnectionReceive; + static PacketReceiveEvent smPacketReceive; + + static bool init(); + static void shutdown(); + + // Unreliable net functions (UDP) + // sendto is for sending data + // all incoming data comes in on packetReceiveEventType + // App can only open one unreliable port... who needs more? ;) + + static bool openPort(S32 connectPort, bool doBind = true); + static NetSocket getPort(); + + static void closePort(); + static Error sendto(const NetAddress *address, const U8 *buffer, S32 bufferSize); + + // Reliable net functions (TCP) + // all incoming messages come in on the Connected* events + static NetSocket openListenPort(U16 port); + static NetSocket openConnectTo(const char *stringAddress); // does the DNS resolve etc. + static void closeConnectTo(NetSocket socket); + static Error sendtoSocket(NetSocket socket, const U8 *buffer, S32 bufferSize); + + static bool compareAddresses(const NetAddress *a1, const NetAddress *a2); + static bool stringToAddress(const char *addressString, NetAddress *address); + static void addressToString(const NetAddress *address, char addressString[256]); + + // lower level socked based network functions + static NetSocket openSocket(); + static Error closeSocket(NetSocket socket); + + static Error send(NetSocket socket, const U8 *buffer, S32 bufferSize); + static Error recv(NetSocket socket, U8 *buffer, S32 bufferSize, S32 *bytesRead); + + static Error connect(NetSocket socket, const NetAddress *address); + static Error listen(NetSocket socket, S32 maxConcurrentListens); + static NetSocket accept(NetSocket acceptSocket, NetAddress *remoteAddress); + + static Error bind(NetSocket socket, U16 port); + static Error setBufferSize(NetSocket socket, S32 bufferSize); + static Error setBroadcast(NetSocket socket, bool broadcastEnable); + static Error setBlocking(NetSocket socket, bool blockingIO); + + +private: + static void process(); + +}; + +#endif \ No newline at end of file diff --git a/Engine/source/platform/platformNetAsync.cpp b/Engine/source/platform/platformNetAsync.cpp new file mode 100644 index 000000000..8f246d39f --- /dev/null +++ b/Engine/source/platform/platformNetAsync.cpp @@ -0,0 +1,187 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platformNetAsync.h" +#include "core/strings/stringFunctions.h" +#include "platform/threads/threadPool.h" +#include "console/console.h" + +#if defined(TORQUE_OS_WIN32) +# include +#elif defined(TORQUE_OS_XENON) +# include +#else +# include +# include +#endif + +#include + +NetAsync gNetAsync; + +//-------------------------------------------------------------------------- +// NetAsync::NameLookupRequest. +//-------------------------------------------------------------------------- + +// internal structure for storing information about a name lookup request +struct NetAsync::NameLookupRequest +{ + NetSocket sock; + char remoteAddr[4096]; + char out_h_addr[4096]; + int out_h_length; + bool complete; + + NameLookupRequest() + { + sock = InvalidSocket; + remoteAddr[0] = 0; + out_h_addr[0] = 0; + out_h_length = -1; + complete = false; + } +}; + +//-------------------------------------------------------------------------- +// NetAsync::NameLookupWorkItem. +//-------------------------------------------------------------------------- + +/// Work item issued to the thread pool for each lookup request. + +struct NetAsync::NameLookupWorkItem : public ThreadPool::WorkItem +{ + typedef ThreadPool::WorkItem Parent; + + NameLookupWorkItem( NameLookupRequest& request, ThreadPool::Context* context = 0 ) + : Parent( context ), + mRequest( request ) + { + } + +protected: + virtual void execute() + { +#ifndef TORQUE_OS_XENON + // do it + struct hostent* hostent = gethostbyname(mRequest.remoteAddr); + if (hostent == NULL) + { + // oh well! leave the lookup data unmodified (h_length) should + // still be -1 from initialization + mRequest.complete = true; + } + else + { + // copy the stuff we need from the hostent + dMemset(mRequest.out_h_addr, 0, + sizeof(mRequest.out_h_addr)); + dMemcpy(mRequest.out_h_addr, hostent->h_addr, hostent->h_length); + + mRequest.out_h_length = hostent->h_length; + mRequest.complete = true; + } +#else + XNDNS *pxndns = NULL; + HANDLE hEvent = CreateEvent(NULL, false, false, NULL); + XNetDnsLookup(mRequest.remoteAddr, hEvent, &pxndns); + + while(pxndns->iStatus == WSAEINPROGRESS) + WaitForSingleObject(hEvent, INFINITE); + + if(pxndns->iStatus == 0 && pxndns->cina > 0) + { + dMemset(mRequest.out_h_addr, 0, sizeof(mRequest.out_h_addr)); + + // This is a suspect section. I need to revisit. [2/22/2010 Pat] + dMemcpy(mRequest.out_h_addr, pxndns->aina, sizeof(IN_ADDR)); + mRequest.out_h_length = sizeof(IN_ADDR); + } + + mRequest.complete = true; + + XNetDnsRelease(pxndns); + CloseHandle(hEvent); +#endif + } + +private: + NameLookupRequest& mRequest; +}; + +//-------------------------------------------------------------------------- +// NetAsync. +//-------------------------------------------------------------------------- + +NetAsync::NetAsync() +{ + VECTOR_SET_ASSOCIATION( mLookupRequests ); +} + +void NetAsync::queueLookup(const char* remoteAddr, NetSocket socket) +{ + // do we have it already? + + unsigned int i = 0; + for (i = 0; i < mLookupRequests.size(); ++i) + { + if (mLookupRequests[i].sock == socket) + // found it. ignore more than one lookup at a time for a socket. + return; + } + + // not found, so add it + + mLookupRequests.increment(); + NameLookupRequest& lookupRequest = mLookupRequests.last(); + lookupRequest.sock = socket; + dStrncpy(lookupRequest.remoteAddr, remoteAddr, sizeof(lookupRequest.remoteAddr)); + + ThreadSafeRef< NameLookupWorkItem > workItem( new NameLookupWorkItem( lookupRequest ) ); + ThreadPool::GLOBAL().queueWorkItem( workItem ); +} + +bool NetAsync::checkLookup(NetSocket socket, char* out_h_addr, + int* out_h_length, int out_h_addr_size) +{ + bool found = false; + + // search for the socket + RequestIterator iter; + for (iter = mLookupRequests.begin(); + iter != mLookupRequests.end(); + ++iter) + // if we found it and it is complete... + if (socket == iter->sock && iter->complete) + { + // copy the lookup data to the callers parameters + dMemcpy(out_h_addr, iter->out_h_addr, out_h_addr_size); + *out_h_length = iter->out_h_length; + found = true; + break; + } + + // we found the socket, so we are done with it. erase. + if (found) + mLookupRequests.erase(iter); + + return found; +} diff --git a/Engine/source/platform/platformNetAsync.h b/Engine/source/platform/platformNetAsync.h new file mode 100644 index 000000000..ddeff56f3 --- /dev/null +++ b/Engine/source/platform/platformNetAsync.h @@ -0,0 +1,63 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef PLATFORM_NET_ASYNC_H +#define PLATFORM_NET_ASYNC_H + +#include "platform/platform.h" +#include "platform/platformNet.h" +#include "core/util/tVector.h" + +// class for doing asynchronous network operations on unix (linux and +// hopefully osx) platforms. right now it only implements dns lookups +class NetAsync +{ + private: + struct NameLookupRequest; + struct NameLookupWorkItem; + + typedef Vector< NameLookupRequest > RequestVector; + typedef RequestVector::iterator RequestIterator; + + RequestVector mLookupRequests; + + public: + NetAsync(); + + // queue a DNS lookup. only one dns lookup can be queued per socket at + // a time. subsequent queue request for the socket are ignored. use + // checkLookup() to check the status of a request. + void queueLookup(const char* remoteAddr, NetSocket socket); + + // check on the status of a dns lookup for a socket. if the lookup is + // not yet complete, the function will return false. if it is + // complete, the function will return true, and out_h_addr and + // out_h_length will be set appropriately. if out_h_length is -1, then + // name could not be resolved. otherwise, it provides the number of + // address bytes copied into out_h_addr. + bool checkLookup(NetSocket socket, char* out_h_addr, int* out_h_length, int out_h_addr_size); +}; + +// the global net async object +extern NetAsync gNetAsync; + +#endif diff --git a/Engine/source/platform/platformRedBook.cpp b/Engine/source/platform/platformRedBook.cpp new file mode 100644 index 000000000..e884c7ceb --- /dev/null +++ b/Engine/source/platform/platformRedBook.cpp @@ -0,0 +1,291 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/strings/stringFunctions.h" +#include "console/console.h" +#include "platform/platformRedBook.h" + +//------------------------------------------------------------------------------ +// Class: RedBookDevice +//------------------------------------------------------------------------------ +RedBookDevice::RedBookDevice() +{ + mAcquired = false; + mDeviceName = 0; +} + +RedBookDevice::~RedBookDevice() +{ + delete [] mDeviceName; +} + +//------------------------------------------------------------------------------ +// Class: RedBook +//------------------------------------------------------------------------------ +Vector RedBook::smDeviceList(__FILE__, __LINE__); +RedBookDevice * RedBook::smCurrentDevice; +char RedBook::smLastError[1024]; + +//------------------------------------------------------------------------------ +void RedBook::init() +{ +} + +void RedBook::destroy() +{ + close(); + + for( Vector::iterator i = smDeviceList.begin( ); i != smDeviceList.end( ); i++ ) { + delete *i; + } + + smDeviceList.clear( ); +} + +//------------------------------------------------------------------------------ +void RedBook::installDevice(RedBookDevice * device) +{ + smDeviceList.push_back(device); +} + +RedBookDevice * RedBook::getCurrentDevice() +{ + return(smCurrentDevice); +} + +U32 RedBook::getDeviceCount() +{ + return(smDeviceList.size()); +} + +const char * RedBook::getDeviceName(U32 idx) +{ + if(idx >= getDeviceCount()) + { + setLastError("Invalid device index"); + return(""); + } + return(smDeviceList[idx]->mDeviceName); +} + +void RedBook::setLastError(const char * error) +{ + if(!error || dStrlen(error) >= sizeof(smLastError)) + setLastError("Invalid error string passed"); + else + dStrcpy(smLastError, error); +} + +const char * RedBook::getLastError() +{ + return(smLastError); +} + +void RedBook::handleCallback(U32 type) +{ + switch(type) + { + case PlayFinished: + Con::executef("RedBookCallback", "PlayFinished"); + break; + } +} + +//------------------------------------------------------------------------------ +bool RedBook::open(const char * deviceName) +{ + if(!deviceName) + { + setLastError("Invalid device name"); + return(false); + } + + for(U32 i = 0; i < smDeviceList.size(); i++) + if(!dStricmp(deviceName, smDeviceList[i]->mDeviceName)) + return(open(smDeviceList[i])); + + setLastError("Failed to find device"); + return(false); +} + +bool RedBook::open(RedBookDevice * device) +{ + if(!device) + { + setLastError("Invalid device passed"); + return(false); + } + + close(); + smCurrentDevice = device; + return(smCurrentDevice->open()); +} + +bool RedBook::close() +{ + if(smCurrentDevice) + { + bool ret = smCurrentDevice->close(); + smCurrentDevice = 0; + return(ret); + } + + setLastError("No device is currently open"); + return(false); +} + +bool RedBook::play(U32 track) +{ + if(!smCurrentDevice) + { + setLastError("No device is currently open"); + return(false); + } + return(smCurrentDevice->play(track)); +} + +bool RedBook::stop() +{ + if(!smCurrentDevice) + { + setLastError("No device is currently open"); + return(false); + } + return(smCurrentDevice->stop()); +} + +bool RedBook::getTrackCount(U32 * trackCount) +{ + if(!smCurrentDevice) + { + setLastError("No device is currently open"); + return(false); + } + return(smCurrentDevice->getTrackCount(trackCount)); +} + +bool RedBook::getVolume(F32 * volume) +{ + if(!smCurrentDevice) + { + setLastError("No device is currently open"); + return(false); + } + return(smCurrentDevice->getVolume(volume)); +} + +bool RedBook::setVolume(F32 volume) +{ + if(!smCurrentDevice) + { + setLastError("No device is currently open"); + return(false); + } + return(smCurrentDevice->setVolume(volume)); +} + +//------------------------------------------------------------------------------ +// console methods +//------------------------------------------------------------------------------ + +ConsoleFunctionGroupBegin( Redbook, "Control functions for Redbook audio (ie, CD audio)."); + +ConsoleFunction(redbookOpen, bool, 1, 2, "(string device=NULL)" + "@brief Deprecated\n\n" + "@internal") +{ + if(argc == 1) + return(RedBook::open(RedBook::getDeviceName(0))); + else + return(RedBook::open(argv[1])); +} + +ConsoleFunction(redbookClose, bool, 1, 1, "Close the current Redbook device." + "@brief Deprecated\n\n" + "@internal") +{ + return(RedBook::close()); +} + +ConsoleFunction( redbookPlay, bool, 2, 2, "(int track) Play the selected track." + "@brief Deprecated\n\n" + "@internal") +{ + return(RedBook::play(dAtoi(argv[1]))); +} + +ConsoleFunction( redbookStop, bool, 1, 1, "Stop playing." + "@brief Deprecated\n\n" + "@internal") +{ + return(RedBook::stop()); +} +ConsoleFunction(redbookGetTrackCount, S32, 1, 1, "Return the number of tracks." + "@brief Deprecated\n\n" + "@internal") +{ + U32 trackCount; + if(!RedBook::getTrackCount(&trackCount)) + return(0); + return(trackCount); +} + +ConsoleFunction(redbookGetVolume, F32, 1, 1, "Get the volume." + "@brief Deprecated\n\n" + "@internal") +{ + F32 vol; + if(!RedBook::getVolume(&vol)) + return(0.f); + else + return(vol); +} + +ConsoleFunction(redbookSetVolume, bool, 2, 2, "(float volume) Set playback volume." + "@brief Deprecated\n\n" + "@internal") +{ + return(RedBook::setVolume(dAtof(argv[1]))); +} + +ConsoleFunction( redbookGetDeviceCount, S32, 1, 1, "get the number of redbook devices." + "@brief Deprecated\n\n" + "@internal") +{ + return(RedBook::getDeviceCount()); +} + +ConsoleFunction( redbookGetDeviceName, const char *, 2, 2, "(int index) Get name of specified Redbook device." + "@brief Deprecated\n\n" + "@internal") +{ + return(RedBook::getDeviceName(dAtoi(argv[1]))); +} + +ConsoleFunction( redbookGetLastError, const char*, 1, 1, "Get a string explaining the last redbook error." + "@brief Deprecated\n\n" + "@internal") +{ + return(RedBook::getLastError()); +} + +ConsoleFunctionGroupEnd( Redbook ); diff --git a/Engine/source/platform/platformRedBook.h b/Engine/source/platform/platformRedBook.h new file mode 100644 index 000000000..49e393c89 --- /dev/null +++ b/Engine/source/platform/platformRedBook.h @@ -0,0 +1,87 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PLATFORMREDBOOK_H_ +#define _PLATFORMREDBOOK_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +class RedBookDevice +{ + public: + RedBookDevice(); + virtual ~RedBookDevice(); + + bool mAcquired; + char * mDeviceName; + + virtual bool open() = 0; + virtual bool close() = 0; + virtual bool play(U32) = 0; + virtual bool stop() = 0; + virtual bool getTrackCount(U32 *) = 0; + virtual bool getVolume(F32 *) = 0; + virtual bool setVolume(F32) = 0; +}; + +class RedBook +{ + private: + static Vector smDeviceList; + static RedBookDevice * smCurrentDevice; + static char smLastError[]; + + public: + enum { + PlayFinished = 0, + }; + static void handleCallback(U32); + + static void init(); + static void destroy(); + + static void installDevice(RedBookDevice *); + static U32 getDeviceCount(); + static const char * getDeviceName(U32); + static RedBookDevice * getCurrentDevice(); + + static void setLastError(const char *); + static const char * getLastError(); + + static bool open(const char *); + static bool open(RedBookDevice *); + static bool close(); + static bool play(U32); + static bool stop(); + static bool getTrackCount(U32 *); + static bool getVolume(F32 *); + static bool setVolume(F32); +}; + +//------------------------------------------------------------------------------ + +#endif diff --git a/Engine/source/platform/platformTLS.h b/Engine/source/platform/platformTLS.h new file mode 100644 index 000000000..69308ca45 --- /dev/null +++ b/Engine/source/platform/platformTLS.h @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PLATFORMTLS_H_ +#define _PLATFORMTLS_H_ + +#ifndef _TORQUE_TYPES_H_ +#include "platform/types.h" +#endif + +struct PlatformThreadStorage; + +/// Platform independent per-thread storage class. +class ThreadStorage +{ + enum + { + PlatformThreadStorageStorageSize = 32, + }; + + PlatformThreadStorage *mThreadStorage; + U8 mStorage[PlatformThreadStorageStorageSize]; +public: + /// ThreadStorage constructor. + ThreadStorage(); + /// ThreadStorage destructor. + ~ThreadStorage(); + + /// returns the per-thread stored void pointer for this ThreadStorage. The default value is NULL. + void *get(); + /// sets the per-thread stored void pointer for this ThreadStorage object. + void set(void *data); +}; + + +#endif diff --git a/Engine/source/platform/platformTimer.cpp b/Engine/source/platform/platformTimer.cpp new file mode 100644 index 000000000..58f735840 --- /dev/null +++ b/Engine/source/platform/platformTimer.cpp @@ -0,0 +1,179 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platformTimer.h" +#include "core/util/journal/process.h" + +void TimeManager::_updateTime() +{ + // Calculate & filter time delta since last event. + + // How long since last update? + S32 delta = mTimer->getElapsedMs(); + + // Now - we want to try to sleep until the time threshold will hit. + S32 msTillThresh = (mBackground ? mBackgroundThreshold : mForegroundThreshold) - delta; + + if(msTillThresh > 0) + { + // There's some time to go, so let's sleep. + Platform::sleep( msTillThresh ); + } + + // Ok - let's grab the new elapsed and send that out. + S32 finalDelta = mTimer->getElapsedMs(); + mTimer->reset(); + + timeEvent.trigger(finalDelta); +} + +TimeManager::TimeManager() +{ + mBackground = false; + mTimer = PlatformTimer::create(); + Process::notify(this, &TimeManager::_updateTime, PROCESS_TIME_ORDER); + + mForegroundThreshold = 5; + mBackgroundThreshold = 10; +} + +TimeManager::~TimeManager() +{ + Process::remove(this, &TimeManager::_updateTime); + delete mTimer; +} + +void TimeManager::setForegroundThreshold(const S32 msInterval) +{ + AssertFatal(msInterval > 0, "TimeManager::setForegroundThreshold - should have at least 1 ms between time events to avoid math problems!"); + mForegroundThreshold = msInterval; +} + +const S32 TimeManager::getForegroundThreshold() const +{ + return mForegroundThreshold; +} + +void TimeManager::setBackgroundThreshold(const S32 msInterval) +{ + AssertFatal(msInterval > 0, "TimeManager::setBackgroundThreshold - should have at least 1 ms between time events to avoid math problems!"); + mBackgroundThreshold = msInterval; +} + +const S32 TimeManager::getBackgroundThreshold() const +{ + return mBackgroundThreshold; +} + +//---------------------------------------------------------------------------------- + +#pragma message("Mac/Lunix will need to implement this or get unresolved externals.") +#pragma message(" It was previously defined here with a Win32 ifdef which goes against") +#pragma message(" how torque implements its platform agnostic systems - JDD") +//PlatformTimer *PlatformTimer::create() +//{ +// return new DefaultPlatformTimer(); +//} + +PlatformTimer::PlatformTimer() +{ +} + +PlatformTimer::~PlatformTimer() +{ +} + + +// Exposes PlatformTimer to script for when high precision is needed. + +#include "core/util/tDictionary.h" +#include "console/console.h" + +class ScriptTimerMan +{ +public: + + ScriptTimerMan(); + ~ScriptTimerMan(); + + S32 startTimer(); + S32 stopTimer( S32 id ); + +protected: + + static S32 smNextId; + + typedef Map TimerMap; + + TimerMap mTimers; +}; + +S32 ScriptTimerMan::smNextId = 1; + +ScriptTimerMan::ScriptTimerMan() +{ +} + +ScriptTimerMan::~ScriptTimerMan() +{ + TimerMap::Iterator itr = mTimers.begin(); + + for ( ; itr != mTimers.end(); itr++ ) + delete itr->value; + + mTimers.clear(); +} + +S32 ScriptTimerMan::startTimer() +{ + PlatformTimer *timer = PlatformTimer::create(); + mTimers.insert( smNextId, timer ); + smNextId++; + return ( smNextId - 1 ); +} + +S32 ScriptTimerMan::stopTimer( S32 id ) +{ + TimerMap::Iterator itr = mTimers.find( id ); + if ( itr == mTimers.end() ) + return -1; + + PlatformTimer *timer = itr->value; + S32 elapsed = timer->getElapsedMs(); + + mTimers.erase( itr ); + delete timer; + + return elapsed; +} + +ScriptTimerMan gScriptTimerMan; + +ConsoleFunction( startPrecisionTimer, S32, 1, 1, "startPrecisionTimer() - Create and start a high resolution platform timer. Returns the timer id." ) +{ + return gScriptTimerMan.startTimer(); +} + +ConsoleFunction( stopPrecisionTimer, S32, 2, 2, "stopPrecisionTimer( S32 id ) - Stop and destroy timer with the passed id. Returns the elapsed milliseconds." ) +{ + return gScriptTimerMan.stopTimer( dAtoi( argv[1] ) ); +} \ No newline at end of file diff --git a/Engine/source/platform/platformTimer.h b/Engine/source/platform/platformTimer.h new file mode 100644 index 000000000..5a896e51c --- /dev/null +++ b/Engine/source/platform/platformTimer.h @@ -0,0 +1,111 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PLATFORM_PLATFORMTIMER_H_ +#define _PLATFORM_PLATFORMTIMER_H_ + +#include "platform/platform.h" +#include "core/util/journal/journaledSignal.h" + +/// Platform-specific timer class. +/// +/// This exists primarily as support for the TimeManager, but may be useful +/// elsewhere. +class PlatformTimer +{ +protected: + PlatformTimer(); +public: + virtual ~PlatformTimer(); + + /// Get the number of MS that have elapsed since creation or the last + /// reset call. + virtual const S32 getElapsedMs()=0; + + /// Reset elapsed ms back to zero. + virtual void reset()=0; + + /// Create a new PlatformTimer. + static PlatformTimer *create(); +}; + +/// Utility class to fire journalled time-delta events at regular intervals. +/// +/// Most games and simulations need the ability to update their state based on +/// a time-delta. However, tracking time accurately and sending out well-conditioned +/// events (for instance, allowing no events with delta=0) tends to be platform +/// specific. This class provides an abstraction around this platform mojo. +/// +/// In addition, a well behaved application may want to alter how frequently +/// it processes time advancement depending on its execution state. For instance, +/// a game running in the background can significantly reduce CPU usage +/// by only updating every 100ms, instead of trying to maintain a 1ms update +/// update rate. +class TimeManager +{ + PlatformTimer *mTimer; + S32 mForegroundThreshold, mBackgroundThreshold; + bool mBackground; + + void _updateTime(); + +public: + + TimeManagerEvent timeEvent; + + TimeManager(); + ~TimeManager(); + + void setForegroundThreshold(const S32 msInterval); + const S32 getForegroundThreshold() const; + + void setBackgroundThreshold(const S32 msInterval); + const S32 getBackgroundThreshold() const; + + void setBackground(const bool isBackground) { mBackground = isBackground; }; + const bool getBackground() const { return mBackground; }; + +}; + +class DefaultPlatformTimer : public PlatformTimer +{ + S32 mLastTime, mNextTime; + +public: + DefaultPlatformTimer() + { + mLastTime = mNextTime = Platform::getRealMilliseconds(); + } + + const S32 getElapsedMs() + { + mNextTime = Platform::getRealMilliseconds(); + return (mNextTime - mLastTime); + } + + void reset() + { + mLastTime = mNextTime; + } +}; + +#endif \ No newline at end of file diff --git a/Engine/source/platform/platformVFS.h b/Engine/source/platform/platformVFS.h new file mode 100644 index 000000000..6c2877aee --- /dev/null +++ b/Engine/source/platform/platformVFS.h @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PLATFORMVFS_H_ +#define _PLATFORMVFS_H_ + +namespace Zip +{ + class ZipArchive; +} + +extern Zip::ZipArchive *openEmbeddedVFSArchive(); +extern void closeEmbeddedVFSArchive(); + +#endif // _PLATFORMVFS_H_ diff --git a/Engine/source/platform/platformVideoInfo.cpp b/Engine/source/platform/platformVideoInfo.cpp new file mode 100644 index 000000000..577d20057 --- /dev/null +++ b/Engine/source/platform/platformVideoInfo.cpp @@ -0,0 +1,99 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platformVideoInfo.h" +#include "core/strings/stringFunctions.h" + +//------------------------------------------------------------------------------ + +PlatformVideoInfo::PlatformVideoInfo() +{ + VECTOR_SET_ASSOCIATION( mAdapters ); +} + +//------------------------------------------------------------------------------ + +PlatformVideoInfo::~PlatformVideoInfo() +{ + +} + +//------------------------------------------------------------------------------ + + +bool PlatformVideoInfo::profileAdapters() +{ + // Initialize the child class + if( !_initialize() ) + return false; + + mAdapters.clear(); + + // Query the number of adapters + String tempString; + + mAdapters.increment( 1 ); + //if( !_queryProperty( PVI_NumAdapters, 0, &tempString ) ) + // return false; + + //mAdapters.increment( dAtoi( tempString ) ); + + U32 adapterNum = 0; + for( Vector::iterator itr = mAdapters.begin(); itr != mAdapters.end(); itr++ ) + { + PVIAdapter &adapter = *itr; + + + U32 querySuccessFlags = U32_MAX; + AssertFatal( PVI_QueryCount < sizeof( querySuccessFlags ) * 8, "Not enough bits in query success mask." ); + querySuccessFlags -= ( ( 1 << PVI_QueryCount ) - 1 ); + + // Fill in adapter information +#define _QUERY_MASK_HELPER( querytype, outstringaddr ) \ + querySuccessFlags |= ( _queryProperty( querytype, adapterNum, outstringaddr ) ? 1 << querytype : 0 ) + + _QUERY_MASK_HELPER( PVI_NumDevices, &tempString ); + adapter.numDevices = dAtoi( tempString ); + + _QUERY_MASK_HELPER( PVI_VRAM, &tempString ); + adapter.vram = dAtoi( tempString ); + + _QUERY_MASK_HELPER( PVI_Description, &adapter.description ); + _QUERY_MASK_HELPER( PVI_Name, &adapter.name ); + _QUERY_MASK_HELPER( PVI_ChipSet, &adapter.chipSet ); + _QUERY_MASK_HELPER( PVI_DriverVersion, &adapter.driverVersion ); + +#undef _QUERY_MASK_HELPER + + // Test flags here for success + } + + return true; +} + +//------------------------------------------------------------------------------ + +const PlatformVideoInfo::PVIAdapter &PlatformVideoInfo::getAdapterInformation( const U32 adapterIndex ) const +{ + AssertFatal( adapterIndex < mAdapters.size(), "Not that many adapters" ); + return mAdapters[adapterIndex]; +} \ No newline at end of file diff --git a/Engine/source/platform/platformVideoInfo.h b/Engine/source/platform/platformVideoInfo.h new file mode 100644 index 000000000..8ac67ad02 --- /dev/null +++ b/Engine/source/platform/platformVideoInfo.h @@ -0,0 +1,99 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PLATFORM_VIDEOINFO_H_ +#define _PLATFORM_VIDEOINFO_H_ + +#include "platform/platform.h" +#include "core/util/str.h" +#include "core/util/tVector.h" + +// The purpose of this class is to abstract the gathering of video adapter information. +// This information is not specific to the API being used to do the rendering, or +// the capabilities of that renderer. That information is queried in a different +// class. + +class PlatformVideoInfo +{ + // # of devices + // description + // manufacturer + // chip set + // driver version + // VRAM +public: + enum PVIQueryType + { + PVI_QueryStart = 0, ///< Start of the enum for looping + + // The NumAdapters query is the only non-adapter specific query, the following + // queries are all specific to an adapter. + PVI_NumDevices = 0, ///< Number of sub adapters + PVI_Description, ///< String description of the adapter + PVI_Name, ///< Card name string + PVI_ChipSet, ///< Chipset string + PVI_DriverVersion, ///< Driver version string + PVI_VRAM, ///< Dedicated video memory in megabytes + + // Please add query types above this value + PVI_QueryCount, ///< Counter so that this enum can be looped over + PVI_NumAdapters, ///< Number of adapters on the system + }; + + struct PVIAdapter + { + U32 numDevices; + String description; + String name; + String chipSet; + String driverVersion; + U32 vram; + }; + +private: + Vector mAdapters; ///< Vector of adapters + + /// Signal handling method for GFX signals + // bool processGFXSignal( GFXDevice::GFXDeviceEventType gfxEvent ); + +protected: + /// This method will be called before any queries are made. All initialization, + /// for example Win32 COM startup, should be done in this method. If the return + /// value is false, no querys will be made. + virtual bool _initialize() = 0; + + /// This is the query method which subclasses must implement. The querys made + /// are all found in the PVIQueryType enum. If NULL is specified for outValue, + /// the sub class should simply return true if the query type is supported, and + /// false otherwise. + virtual bool _queryProperty( const PVIQueryType queryType, const U32 adapterId, String *outValue ) = 0; + +public: + PlatformVideoInfo(); + virtual ~PlatformVideoInfo(); + + bool profileAdapters(); + + const PVIAdapter &getAdapterInformation( const U32 adapterIndex ) const; +}; + +#endif \ No newline at end of file diff --git a/Engine/source/platform/platformVolume.cpp b/Engine/source/platform/platformVolume.cpp new file mode 100644 index 000000000..6f3c0ec78 --- /dev/null +++ b/Engine/source/platform/platformVolume.cpp @@ -0,0 +1,91 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#if defined(TORQUE_OS_WIN32) || defined(TORQUE_OS_XBOX) || defined(TORQUE_OS_XENON) +#include +#else +#include +#endif + +#include "platform/platformVolume.h" +#include "core/util/zip/zipVolume.h" + +using namespace Torque; +using namespace Torque::FS; + +namespace Platform +{ +namespace FS +{ + +bool MountDefaults() +{ + String path = getAssetDir(); + + bool mounted = Mount( "game", createNativeFS( path )); + + if ( !mounted ) + return false; + +#ifndef TORQUE_DISABLE_VIRTUAL_MOUNT_SYSTEM + // Note that the VirtualMountSystem must be enabled in volume.cpp for zip support to work. + return MountZips("game"); +#else + return true; +#endif +} + +bool MountZips(const String &root) +{ + Path basePath; + basePath.setRoot(root); + Vector outList; + + S32 num = FindByPattern(basePath, "*.zip", true, outList); + if(num == 0) + return true; // not an error + + S32 mounted = 0; + for(S32 i = 0;i < outList.size();++i) + { + String &zipfile = outList[i]; + mounted += (S32)Mount(root, new ZipFileSystem(zipfile, true)); + } + + return mounted == outList.size(); +} + +//----------------------------------------------------------------------------- + +bool Touch( const Path &path ) +{ +#if defined(TORQUE_OS_WIN32) || defined(TORQUE_OS_XBOX) || defined(TORQUE_OS_XENON) + return( utime( path.getFullPath(), 0 ) != -1 ); +#else + return( utimes( path.getFullPath(), NULL) == 0 ); // utimes returns 0 on success. +#endif +} + +} // namespace FS +} // namespace Platform diff --git a/Engine/source/platform/platformVolume.h b/Engine/source/platform/platformVolume.h new file mode 100644 index 000000000..8fe6c8b56 --- /dev/null +++ b/Engine/source/platform/platformVolume.h @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PLATFORMVOLUME_H_ +#define _PLATFORMVOLUME_H_ + +#include "core/volume.h" + + +namespace Platform +{ +namespace FS +{ + using namespace Torque; + using namespace Torque::FS; + + FileSystemRef createNativeFS( const String &volume ); + + String getAssetDir(); + + /// Mount default OS file systems. + /// On POSIX environment this means mounting a root FileSystem "/", mounting + /// the $HOME environment variable as the "home:/" file system and setting the + /// current working directory to the current OS working directory. + bool InstallFileSystems(); + + bool MountDefaults(); + bool MountZips(const String &root); + + bool Touch( const Path &path ); + +} // Namespace FS +} // Namespace Platform + +#endif + diff --git a/Engine/source/platform/profiler.cpp b/Engine/source/platform/profiler.cpp new file mode 100644 index 000000000..f97100f09 --- /dev/null +++ b/Engine/source/platform/profiler.cpp @@ -0,0 +1,760 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#if defined(TORQUE_OS_WIN32) +#include // for SetThreadAffinityMask +#endif + +#if defined(TORQUE_OS_MAC) +#include // For high resolution timer +#endif + +#include "core/stream/fileStream.h" +#include "core/frameAllocator.h" +#include "core/strings/stringFunctions.h" +#include "core/stringTable.h" + +#include "platform/profiler.h" +#include "platform/threads/thread.h" + +#include "console/engineAPI.h" + +#ifdef TORQUE_ENABLE_PROFILER +ProfilerRootData *ProfilerRootData::sRootList = NULL; +Profiler *gProfiler = NULL; + +// Uncomment the following line to enable a debugging aid for mismatched profiler blocks. +//#define TORQUE_PROFILER_DEBUG + +// Machinery to record the stack of node names, as a debugging aid to find +// mismatched PROFILE_START and PROFILE_END blocks. We profile from the +// beginning to catch profile block errors that occur when torque is starting up. +#ifdef TORQUE_PROFILER_DEBUG +Vector gProfilerNodeStack; +#define TORQUE_PROFILE_AT_ENGINE_START true +#define PROFILER_DEBUG_PUSH_NODE( nodename ) \ + gProfilerNodeStack.push_back( nodename ); +#define PROFILER_DEBUG_POP_NODE() \ + gProfilerNodeStack.pop_back(); +#else +#define TORQUE_PROFILE_AT_ENGINE_START false +#define PROFILER_DEBUG_PUSH_NODE( nodename ) ; +#define PROFILER_DEBUG_POP_NODE() ; +#endif + +#if defined(TORQUE_SUPPORTS_VC_INLINE_X86_ASM) +// platform specific get hires times... +void startHighResolutionTimer(U32 time[2]) +{ + //time[0] = Platform::getRealMilliseconds(); + + __asm + { + push eax + push edx + push ecx + rdtsc + mov ecx, time + mov DWORD PTR [ecx], eax + mov DWORD PTR [ecx + 4], edx + pop ecx + pop edx + pop eax + } +} + +U32 endHighResolutionTimer(U32 time[2]) +{ + U32 ticks; + //ticks = Platform::getRealMilliseconds() - time[0]; + //return ticks; + + __asm + { + push eax + push edx + push ecx + //db 0fh, 31h + rdtsc + mov ecx, time + sub edx, DWORD PTR [ecx+4] + sbb eax, DWORD PTR [ecx] + mov DWORD PTR ticks, eax + pop ecx + pop edx + pop eax + } + return ticks; +} + +#elif defined(TORQUE_SUPPORTS_GCC_INLINE_X86_ASM) + +// platform specific get hires times... +void startHighResolutionTimer(U32 time[2]) +{ + __asm__ __volatile__( + "rdtsc\n" + : "=a" (time[0]), "=d" (time[1]) + ); +} + +U32 endHighResolutionTimer(U32 time[2]) +{ + U32 ticks; + __asm__ __volatile__( + "rdtsc\n" + "sub 0x4(%%ecx), %%edx\n" + "sbb (%%ecx), %%eax\n" + : "=a" (ticks) : "c" (time) + ); + return ticks; +} + +#elif defined(TORQUE_OS_MAC) + + +void startHighResolutionTimer(U32 time[2]) { + UnsignedWide t; + Microseconds(&t); + time[0] = t.lo; + time[1] = t.hi; +} + +U32 endHighResolutionTimer(U32 time[2]) { + UnsignedWide t; + Microseconds(&t); + return t.lo - time[0]; + // given that we're returning a 32 bit integer, and this is unsigned subtraction... + // it will just wrap around, we don't need the upper word of the time. + // NOTE: the code assumes that more than 3 hrs will not go by between calls to startHighResolutionTimer() and endHighResolutionTimer(). + // I mean... that damn well better not happen anyway. +} + +#else + +void startHighResolutionTimer(U32 time[2]) +{ +} + +U32 endHighResolutionTimer(U32 time[2]) +{ + return 1; +} + +#endif + +Profiler::Profiler() +{ + mMaxStackDepth = MaxStackDepth; + mCurrentHash = 0; + + mCurrentProfilerData = (ProfilerData *) malloc(sizeof(ProfilerData)); + mCurrentProfilerData->mRoot = NULL; + mCurrentProfilerData->mNextForRoot = NULL; + mCurrentProfilerData->mNextProfilerData = NULL; + mCurrentProfilerData->mNextHash = NULL; + mCurrentProfilerData->mParent = NULL; + mCurrentProfilerData->mNextSibling = NULL; + mCurrentProfilerData->mFirstChild = NULL; + mCurrentProfilerData->mLastSeenProfiler = NULL; + mCurrentProfilerData->mHash = 0; + mCurrentProfilerData->mSubDepth = 0; + mCurrentProfilerData->mInvokeCount = 0; + mCurrentProfilerData->mTotalTime = 0; + mCurrentProfilerData->mSubTime = 0; +#ifdef TORQUE_ENABLE_PROFILE_PATH + mCurrentProfilerData->mPath = ""; +#endif + mRootProfilerData = mCurrentProfilerData; + + for(U32 i = 0; i < ProfilerData::HashTableSize; i++) + mCurrentProfilerData->mChildHash[i] = 0; + + mProfileList = NULL; + + mEnabled = TORQUE_PROFILE_AT_ENGINE_START; + mNextEnable = TORQUE_PROFILE_AT_ENGINE_START; + mStackDepth = 0; + gProfiler = this; + mDumpToConsole = false; + mDumpToFile = false; + mDumpFileName[0] = '\0'; +} + +Profiler::~Profiler() +{ + reset(); + free(mRootProfilerData); + gProfiler = NULL; +} + +void Profiler::reset() +{ + mEnabled = false; // in case we're in a profiler call. + while(mProfileList) + { + free(mProfileList); + mProfileList = NULL; + } + for(ProfilerRootData *walk = ProfilerRootData::sRootList; walk; walk = walk->mNextRoot) + { + walk->mFirstProfilerData = 0; + walk->mTotalTime = 0; + walk->mSubTime = 0; + walk->mTotalInvokeCount = 0; + } + mCurrentProfilerData = mRootProfilerData; + mCurrentProfilerData->mNextForRoot = 0; + mCurrentProfilerData->mFirstChild = 0; + for(U32 i = 0; i < ProfilerData::HashTableSize; i++) + mCurrentProfilerData->mChildHash[i] = 0; + mCurrentProfilerData->mInvokeCount = 0; + mCurrentProfilerData->mTotalTime = 0; + mCurrentProfilerData->mSubTime = 0; + mCurrentProfilerData->mSubDepth = 0; + mCurrentProfilerData->mLastSeenProfiler = 0; +} + +static Profiler aProfiler; // allocate the global profiler + +ProfilerRootData::ProfilerRootData(const char *name) +{ + for(ProfilerRootData *walk = sRootList; walk; walk = walk->mNextRoot) + if(!dStrcmp(walk->mName, name)) + AssertFatal( false, avar( "Duplicate profile name: %s", name ) ); + + mName = name; + mNameHash = _StringTable::hashString(name); + mNextRoot = sRootList; + sRootList = this; + mTotalTime = 0; + mTotalInvokeCount = 0; + mFirstProfilerData = NULL; + mEnabled = true; +} + +void Profiler::validate() +{ + for(ProfilerRootData *walk = ProfilerRootData::sRootList; walk; walk = walk->mNextRoot) + { + for(ProfilerData *dp = walk->mFirstProfilerData; dp; dp = dp->mNextForRoot) + { + if(dp->mRoot != walk) + Platform::debugBreak(); + // check if it's in the parent's list... + ProfilerData *wk; + for(wk = dp->mParent->mFirstChild; wk; wk = wk->mNextSibling) + if(wk == dp) + break; + if(!wk) + Platform::debugBreak(); + for(wk = dp->mParent->mChildHash[walk->mNameHash & (ProfilerData::HashTableSize - 1)] ; + wk; wk = wk->mNextHash) + if(wk == dp) + break; + if(!wk) + Platform::debugBreak(); + } + } +} + +#ifdef TORQUE_ENABLE_PROFILE_PATH +const char * Profiler::getProfilePath() +{ +#ifdef TORQUE_MULTITHREAD + // Ignore non-main-thread profiler activity. + if( !ThreadManager::isMainThread() ) + return "[non-main thread]"; +#endif + + return (mEnabled && mCurrentProfilerData) ? mCurrentProfilerData->mPath : "na"; +} +#endif + +#ifdef TORQUE_ENABLE_PROFILE_PATH +const char * Profiler::constructProfilePath(ProfilerData * pd) +{ + if (pd->mParent) + { + const bool saveEnable = gProfiler->mEnabled; + gProfiler->mEnabled = false; + + const char * connector = " -> "; + U32 len = dStrlen(pd->mParent->mPath); + if (!len) + connector = ""; + len += dStrlen(connector); + len += dStrlen(pd->mRoot->mName); + + U32 mark = FrameAllocator::getWaterMark(); + char * buf = (char*)FrameAllocator::alloc(len+1); + dStrcpy(buf,pd->mParent->mPath); + dStrcat(buf,connector); + dStrcat(buf,pd->mRoot->mName); + const char * ret = StringTable->insert(buf); + FrameAllocator::setWaterMark(mark); + + gProfiler->mEnabled = saveEnable; + + return ret; + } + return "root"; +} +#endif +void Profiler::hashPush(ProfilerRootData *root) +{ +#ifdef TORQUE_MULTITHREAD + // Ignore non-main-thread profiler activity. + if( !ThreadManager::isMainThread() ) + return; +#endif + + mStackDepth++; + PROFILER_DEBUG_PUSH_NODE(root->mName); + AssertFatal(mStackDepth <= mMaxStackDepth, + "Stack overflow in profiler. You may have mismatched PROFILE_START and PROFILE_ENDs"); + if(!mEnabled) + return; + + ProfilerData *nextProfiler = NULL; + if(!root->mEnabled || mCurrentProfilerData->mRoot == root) + { + mCurrentProfilerData->mSubDepth++; + return; + } + + if(mCurrentProfilerData->mLastSeenProfiler && + mCurrentProfilerData->mLastSeenProfiler->mRoot == root) + nextProfiler = mCurrentProfilerData->mLastSeenProfiler; + + if(!nextProfiler) + { + // first see if it's in the hash table... + U32 index = root->mNameHash & (ProfilerData::HashTableSize - 1); + nextProfiler = mCurrentProfilerData->mChildHash[index]; + while(nextProfiler) + { + if(nextProfiler->mRoot == root) + break; + nextProfiler = nextProfiler->mNextHash; + } + if(!nextProfiler) + { + nextProfiler = (ProfilerData *) malloc(sizeof(ProfilerData)); + for(U32 i = 0; i < ProfilerData::HashTableSize; i++) + nextProfiler->mChildHash[i] = 0; + + nextProfiler->mRoot = root; + nextProfiler->mNextForRoot = root->mFirstProfilerData; + root->mFirstProfilerData = nextProfiler; + + nextProfiler->mNextProfilerData = mProfileList; + mProfileList = nextProfiler; + + nextProfiler->mNextHash = mCurrentProfilerData->mChildHash[index]; + mCurrentProfilerData->mChildHash[index] = nextProfiler; + + nextProfiler->mParent = mCurrentProfilerData; + nextProfiler->mNextSibling = mCurrentProfilerData->mFirstChild; + mCurrentProfilerData->mFirstChild = nextProfiler; + nextProfiler->mFirstChild = NULL; + nextProfiler->mLastSeenProfiler = NULL; + nextProfiler->mHash = root->mNameHash; + nextProfiler->mInvokeCount = 0; + nextProfiler->mTotalTime = 0; + nextProfiler->mSubTime = 0; + nextProfiler->mSubDepth = 0; +#ifdef TORQUE_ENABLE_PROFILE_PATH + nextProfiler->mPath = constructProfilePath(nextProfiler); +#endif + } + } + root->mTotalInvokeCount++; + nextProfiler->mInvokeCount++; + startHighResolutionTimer(nextProfiler->mStartTime); + mCurrentProfilerData->mLastSeenProfiler = nextProfiler; + mCurrentProfilerData = nextProfiler; +} + +void Profiler::enable(bool enabled) +{ + mNextEnable = enabled; +} + +void Profiler::dumpToConsole() +{ + mDumpToConsole = true; + mDumpToFile = false; + mDumpFileName[0] = '\0'; +} + +void Profiler::dumpToFile(const char* fileName) +{ + AssertFatal(dStrlen(fileName) < DumpFileNameLength, "Error, dump filename too long"); + mDumpToFile = true; + mDumpToConsole = false; + dStrcpy(mDumpFileName, fileName); +} + +void Profiler::hashPop(ProfilerRootData *expected) +{ +#ifdef TORQUE_MULTITHREAD + // Ignore non-main-thread profiler activity. + if( !ThreadManager::isMainThread() ) + return; +#endif + + mStackDepth--; + PROFILER_DEBUG_POP_NODE(); + AssertFatal(mStackDepth >= 0, "Stack underflow in profiler. You may have mismatched PROFILE_START and PROFILE_ENDs"); + if(mEnabled) + { + if(mCurrentProfilerData->mSubDepth) + { + mCurrentProfilerData->mSubDepth--; + return; + } + + if(expected) + { + AssertISV(expected == mCurrentProfilerData->mRoot, "Profiler::hashPop - didn't get expected ProfilerRoot!"); + } + + F64 fElapsed = endHighResolutionTimer(mCurrentProfilerData->mStartTime); + + mCurrentProfilerData->mTotalTime += fElapsed; + mCurrentProfilerData->mParent->mSubTime += fElapsed; // mark it in the parent as well... + mCurrentProfilerData->mRoot->mTotalTime += fElapsed; + if(mCurrentProfilerData->mParent->mRoot) + mCurrentProfilerData->mParent->mRoot->mSubTime += fElapsed; // mark it in the parent as well... + + mCurrentProfilerData = mCurrentProfilerData->mParent; + } + if(mStackDepth == 0) + { + // apply the next enable... + if(mDumpToConsole || mDumpToFile) + { + dump(); + startHighResolutionTimer(mCurrentProfilerData->mStartTime); + } + if(!mEnabled && mNextEnable) + startHighResolutionTimer(mCurrentProfilerData->mStartTime); + +#if defined(TORQUE_OS_WIN32) + // The high performance counters under win32 are unreliable when running on multiple + // processors. When the profiler is enabled, we restrict Torque to a single processor. + if(mNextEnable != mEnabled) + { + + if(mNextEnable) + { + Con::warnf("Warning: forcing the Torque profiler thread to run only on cpu 1."); + SetThreadAffinityMask(GetCurrentThread(), 1); + } + else + { + Con::warnf("Warning: the Torque profiler thread may now run on any cpu."); + DWORD procMask; + DWORD sysMask; + GetProcessAffinityMask( GetCurrentProcess(), &procMask, &sysMask); + SetThreadAffinityMask( GetCurrentThread(), procMask); + } + } +#endif + + mEnabled = mNextEnable; + } +} + +static S32 QSORT_CALLBACK rootDataCompare(const void *s1, const void *s2) +{ + const ProfilerRootData *r1 = *((ProfilerRootData **) s1); + const ProfilerRootData *r2 = *((ProfilerRootData **) s2); + if((r2->mTotalTime - r2->mSubTime) > (r1->mTotalTime - r1->mSubTime)) + return 1; + return -1; +} + +static void profilerDataDumpRecurse(ProfilerData *data, char *buffer, U32 bufferLen, F64 totalTime) +{ + // dump out this one: + Con::printf("%7.3f %7.3f %8d %s%s", + 100 * data->mTotalTime / totalTime, + 100 * (data->mTotalTime - data->mSubTime) / totalTime, + data->mInvokeCount, + buffer, + data->mRoot ? data->mRoot->mName : "ROOT" ); + data->mTotalTime = 0; + data->mSubTime = 0; + data->mInvokeCount = 0; + + buffer[bufferLen] = ' '; + buffer[bufferLen+1] = ' '; + buffer[bufferLen+2] = 0; + // sort data's children... + ProfilerData *list = NULL; + while(data->mFirstChild) + { + ProfilerData *ins = data->mFirstChild; + data->mFirstChild = ins->mNextSibling; + ProfilerData **walk = &list; + while(*walk && (*walk)->mTotalTime > ins->mTotalTime) + walk = &(*walk)->mNextSibling; + ins->mNextSibling = *walk; + *walk = ins; + } + data->mFirstChild = list; + while(list) + { + if(list->mInvokeCount) + profilerDataDumpRecurse(list, buffer, bufferLen + 2, totalTime); + list = list->mNextSibling; + } + buffer[bufferLen] = 0; +} + +static void profilerDataDumpRecurseFile(ProfilerData *data, char *buffer, U32 bufferLen, F64 totalTime, FileStream& fws) +{ + char pbuffer[256]; + dSprintf(pbuffer, 255, "%7.3f %7.3f %8d %s%s\n", + 100 * data->mTotalTime / totalTime, + 100 * (data->mTotalTime - data->mSubTime) / totalTime, + data->mInvokeCount, + buffer, + data->mRoot ? data->mRoot->mName : "ROOT" ); + fws.write(dStrlen(pbuffer), pbuffer); + data->mTotalTime = 0; + data->mSubTime = 0; + data->mInvokeCount = 0; + + buffer[bufferLen] = ' '; + buffer[bufferLen+1] = ' '; + buffer[bufferLen+2] = 0; + // sort data's children... + ProfilerData *list = NULL; + while(data->mFirstChild) + { + ProfilerData *ins = data->mFirstChild; + data->mFirstChild = ins->mNextSibling; + ProfilerData **walk = &list; + while(*walk && (*walk)->mTotalTime > ins->mTotalTime) + walk = &(*walk)->mNextSibling; + ins->mNextSibling = *walk; + *walk = ins; + } + data->mFirstChild = list; + while(list) + { + if(list->mInvokeCount) + profilerDataDumpRecurseFile(list, buffer, bufferLen + 2, totalTime, fws); + list = list->mNextSibling; + } + buffer[bufferLen] = 0; +} + +void Profiler::dump() +{ + bool enableSave = mEnabled; + mEnabled = false; + mStackDepth++; + // may have some profiled calls... gotta turn em off. + + Vector rootVector; + F64 totalTime = 0; + for(ProfilerRootData *walk = ProfilerRootData::sRootList; walk; walk = walk->mNextRoot) + { + totalTime += walk->mTotalTime - walk->mSubTime; + rootVector.push_back(walk); + } + dQsort((void *) &rootVector[0], rootVector.size(), sizeof(ProfilerRootData *), rootDataCompare); + + + if (mDumpToConsole == true) + { + Con::printf("Profiler Data Dump:"); + Con::printf("Ordered by non-sub total time -"); + Con::printf("%%NSTime %% Time Invoke # Name"); + for(U32 i = 0; i < rootVector.size(); i++) + { + Con::printf("%7.3f %7.3f %8d %s", + 100 * (rootVector[i]->mTotalTime - rootVector[i]->mSubTime) / totalTime, + 100 * rootVector[i]->mTotalTime / totalTime, + rootVector[i]->mTotalInvokeCount, + rootVector[i]->mName); + rootVector[i]->mTotalInvokeCount = 0; + rootVector[i]->mTotalTime = 0; + rootVector[i]->mSubTime = 0; + } + Con::printf(""); + Con::printf("Ordered by stack trace total time -"); + Con::printf("%% Time %% NSTime Invoke # Name"); + + mCurrentProfilerData->mTotalTime = endHighResolutionTimer(mCurrentProfilerData->mStartTime); + + char depthBuffer[MaxStackDepth * 2 + 1]; + depthBuffer[0] = 0; + profilerDataDumpRecurse(mCurrentProfilerData, depthBuffer, 0, totalTime); + mEnabled = enableSave; + mStackDepth--; + } + else if (mDumpToFile == true && mDumpFileName[0] != '\0') + { + FileStream fws; + bool success = fws.open(mDumpFileName, Torque::FS::File::Write); + AssertFatal(success, "Cannot write profile dump to specified file!"); + char buffer[1024]; + + dStrcpy(buffer, "Profiler Data Dump:\n"); + fws.write(dStrlen(buffer), buffer); + dStrcpy(buffer, "Ordered by non-sub total time -\n"); + fws.write(dStrlen(buffer), buffer); + dStrcpy(buffer, "%%NSTime %% Time Invoke # Name\n"); + fws.write(dStrlen(buffer), buffer); + + for(U32 i = 0; i < rootVector.size(); i++) + { + dSprintf(buffer, 1023, "%7.3f %7.3f %8d %s\n", + 100 * (rootVector[i]->mTotalTime - rootVector[i]->mSubTime) / totalTime, + 100 * rootVector[i]->mTotalTime / totalTime, + rootVector[i]->mTotalInvokeCount, + rootVector[i]->mName); + fws.write(dStrlen(buffer), buffer); + + rootVector[i]->mTotalInvokeCount = 0; + rootVector[i]->mTotalTime = 0; + rootVector[i]->mSubTime = 0; + } + dStrcpy(buffer, "\nOrdered by non-sub total time -\n"); + fws.write(dStrlen(buffer), buffer); + dStrcpy(buffer, "%%NSTime %% Time Invoke # Name\n"); + fws.write(dStrlen(buffer), buffer); + + mCurrentProfilerData->mTotalTime = endHighResolutionTimer(mCurrentProfilerData->mStartTime); + + char depthBuffer[MaxStackDepth * 2 + 1]; + depthBuffer[0] = 0; + profilerDataDumpRecurseFile(mCurrentProfilerData, depthBuffer, 0, totalTime, fws); + mEnabled = enableSave; + mStackDepth--; + + fws.close(); + } + + mDumpToConsole = false; + mDumpToFile = false; + mDumpFileName[0] = '\0'; +} + +void Profiler::enableMarker(const char *marker, bool enable) +{ + reset(); + U32 markerLen = dStrlen(marker); + if(markerLen == 0) + return; + bool sn = marker[markerLen - 1] == '*'; + for(ProfilerRootData *data = ProfilerRootData::sRootList; data; data = data->mNextRoot) + { + if(sn) + { + if(!dStrncmp(marker, data->mName, markerLen - 1)) + data->mEnabled = enable; + } + else + { + if(!dStrcmp(marker, data->mName)) + data->mEnabled = enable; + } + } +} + +//============================================================================= +// Console Functions. +//============================================================================= +// MARK: ---- Console Functions ---- + +//----------------------------------------------------------------------------- + +DefineEngineFunction( profilerMarkerEnable, void, ( const char* markerName, bool enable ), ( true ), + "@brief Enable or disable a specific profile.\n\n" + "@param enable Optional paramater to enable or disable the profile.\n" + "@param markerName Name of a specific marker to enable or disable.\n" + "@note Calling this function will first call profilerReset(), clearing all data from profiler. " + "All profile markers are enabled by default.\n\n" + "@ingroup Debugging") +{ + if( gProfiler ) + gProfiler->enableMarker( markerName, enable ); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( profilerEnable, void, ( bool enable ),, + "@brief Enables or disables the profiler.\n\n" + "Data is only gathered while the profiler is enabled.\n\n" + "@note Profiler is not available in shipping builds.\n" + "T3D has predefined profiling areas surrounded by markers, " + "but you may need to define additional markers (in C++) around areas you wish to profile," + " by using the PROFILE_START( markerName ); and PROFILE_END(); macros.\n\n" + "@ingroup Debugging\n" ) +{ + if(gProfiler) + gProfiler->enable(enable); +} + +DefineEngineFunction(profilerDump, void, (),, + "@brief Dumps current profiling stats to the console window.\n\n" + "@note Markers disabled with profilerMarkerEnable() will be skipped over. " + "If the profiler is currently running, it will be disabled.\n" + "@ingroup Debugging") +{ + if(gProfiler) + gProfiler->dumpToConsole(); +} + +DefineEngineFunction( profilerDumpToFile, void, ( const char* fileName ),, + "@brief Dumps current profiling stats to a file.\n\n" + "@note If the profiler is currently running, it will be disabled.\n" + "@param fileName Name and path of file to save profiling stats to. Must use forward slashes (/). " + "Will attempt to create the file if it does not already exist.\n" + "@tsexample\n" + "profilerDumpToFile( \"C:/Torque/log1.txt\" );\n" + "@endtsexample\n\n" + "@ingroup Debugging" ) +{ + if(gProfiler) + gProfiler->dumpToFile(fileName); +} + +DefineEngineFunction( profilerReset, void, (),, + "@brief Resets the profiler, clearing it of all its data.\n\n" + "If the profiler is currently running, it will first be disabled. " + "All markers will retain their current enabled/disabled status.\n\n" + "@ingroup Debugging" ) +{ + if(gProfiler) + gProfiler->reset(); +} + +#endif diff --git a/Engine/source/platform/profiler.h b/Engine/source/platform/profiler.h new file mode 100644 index 000000000..3b11d93fd --- /dev/null +++ b/Engine/source/platform/profiler.h @@ -0,0 +1,194 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PROFILER_H_ +#define _PROFILER_H_ + +#ifndef _TORQUECONFIG_H_ +#include "torqueConfig.h" +#endif + +#ifdef TORQUE_ENABLE_PROFILER + +struct ProfilerData; +struct ProfilerRootData; +/// The Profiler is used to see how long a specific chunk of code takes to execute. +/// All values outputted by the profiler are percentages of the time that it takes +/// to run entire main loop. +/// +/// First, you must #define TORQUE_ENABLE_PROFILER in profiler.h in order to +/// active it. Examples of script use: +/// @code +/// //enables or disables profiling. Data is only gathered when the profiler is enabled. +/// profilerEnable(bool enable); +/// profilerReset(); //resets the data gathered by the profiler +/// profilerDump(); //dumps all profiler data to the console +/// profilerDumpToFile(string filename); //dumps all profiler data to a given file +/// profilerMarkerEnable((string markerName, bool enable); //enables or disables a given profile tag +/// @endcode +/// +/// The C++ code side of the profiler uses pairs of PROFILE_START() and PROFILE_END(). +/// +/// When using these macros, make sure there is a PROFILE_END() for every PROFILE_START +/// and a PROFILE_START() for every PROFILE_END(). It is fine to nest these macros, however, +/// you must make sure that no matter what execution path the code takes, the PROFILE macros +/// will be balanced. +/// +/// The profiler can be used to locate areas of code that are slow or should be considered for +/// optimization. Since it tracks the relative time of execution of that code to the execution +/// of the main loop, it is possible to benchmark any given code to see if changes made will +/// actually improve performance. +/// +/// Here are some examples: +/// @code +/// PROFILE_START(TerrainRender); +/// //some code here +/// PROFILE_START(TerrainRenderGridSquare); +/// //some code here +/// PROFILE_END(); +/// //possibly some code here +/// PROFILE_END(); +/// @endcode +class Profiler +{ + enum { + MaxStackDepth = 256, + DumpFileNameLength = 256 + }; + U32 mCurrentHash; + + ProfilerData *mCurrentProfilerData; + ProfilerData *mProfileList; + ProfilerData *mRootProfilerData; + + bool mEnabled; + S32 mStackDepth; + bool mNextEnable; + U32 mMaxStackDepth; + bool mDumpToConsole; + bool mDumpToFile; + char mDumpFileName[DumpFileNameLength]; + void dump(); + void validate(); +public: + Profiler(); + ~Profiler(); + + /// Reset the data in the profiler + void reset(); + /// Dumps the profile to console + void dumpToConsole(); + /// Dumps the profile data to a file + /// @param fileName filename to dump data to + void dumpToFile(const char *fileName); + /// Enable profiling + void enable(bool enabled); + bool isEnabled() { return mNextEnable; } + /// Helper function for macro definition PROFILE_START + void hashPush(ProfilerRootData *data); + /// Helper function for macro definition PROFILE_END + void hashPop(ProfilerRootData *expected=NULL); + /// Enable a profiler marker + void enableMarker(const char *marker, bool enabled); +#ifdef TORQUE_ENABLE_PROFILE_PATH + /// Get current profile path + const char * getProfilePath(); + /// Construct profile path of given profiler data + const char * constructProfilePath(ProfilerData * pd); +#endif +}; + +extern Profiler *gProfiler; + +struct ProfilerRootData +{ + const char *mName; + U32 mNameHash; + ProfilerData *mFirstProfilerData; + ProfilerRootData *mNextRoot; + F64 mTotalTime; + F64 mSubTime; + U32 mTotalInvokeCount; + bool mEnabled; + + static ProfilerRootData *sRootList; + + ProfilerRootData(const char *name); +}; + +struct ProfilerData +{ + ProfilerRootData *mRoot; ///< link to root node. + ProfilerData *mNextForRoot; ///< links together all ProfilerData's for a particular root + ProfilerData *mNextProfilerData; ///< links all the profilerDatas + ProfilerData *mNextHash; + ProfilerData *mParent; + ProfilerData *mNextSibling; + ProfilerData *mFirstChild; + enum { + HashTableSize = 32, + }; + ProfilerData *mChildHash[HashTableSize]; + ProfilerData *mLastSeenProfiler; + + U32 mHash; + U32 mSubDepth; + U32 mInvokeCount; + U32 mStartTime[2]; + F64 mTotalTime; + F64 mSubTime; +#ifdef TORQUE_ENABLE_PROFILE_PATH + const char * mPath; +#endif +}; + + +#define PROFILE_START(name) \ +static ProfilerRootData pdata##name##obj (#name); \ +if(gProfiler) gProfiler->hashPush(& pdata##name##obj ) + +#define PROFILE_END() if(gProfiler) gProfiler->hashPop() + +#define PROFILE_END_NAMED(name) if(gProfiler) gProfiler->hashPop(& pdata##name##obj) + +class ScopedProfiler { +public: + ScopedProfiler(ProfilerRootData *data) { + if (gProfiler) gProfiler->hashPush(data); + } + ~ScopedProfiler() { + if (gProfiler) gProfiler->hashPop(); + } +}; + +#define PROFILE_SCOPE(name) \ + static ProfilerRootData pdata##name##obj (#name); \ + ScopedProfiler scopedProfiler##name##obj(&pdata##name##obj); + +#else +#define PROFILE_START(x) +#define PROFILE_END() +#define PROFILE_SCOPE(x) +#define PROFILE_END_NAMED(x) +#endif + +#endif diff --git a/Engine/source/platform/test/testAlerts.cpp b/Engine/source/platform/test/testAlerts.cpp new file mode 100644 index 000000000..c79d9ff44 --- /dev/null +++ b/Engine/source/platform/test/testAlerts.cpp @@ -0,0 +1,50 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "unit/test.h" + +using namespace UnitTesting; + +CreateInteractiveTest(CheckPlatformAlerts, "Platform/Alerts") +{ + void run() + { + // Run through all the alert types. + Platform::AlertOK("Test #1 - AlertOK", "This is a test of Platform::AlertOK. I am a blocking dialog with an OK button. Please hit OK to continue."); + test(true, "AlertOK should return when the user clicks on it..."); // <-- gratuitous test point. + + bool res; + + res = Platform::AlertOKCancel("Test #2 - AlertOKCancel", "This is a test of Platform::alertOKCancel. I am a blocking dialog with an OK and a Cancel button. Please hit Cancel to continue."); + test(res==false,"AlertOKCancel - Didn't get cancel. User error, or just bad code?"); + + res = Platform::AlertOKCancel("Test #3 - AlertOKCancel", "This is a test of Platform::alertOKCancel. I am a blocking dialog with an OK and a Cancel button. Please hit OK to continue."); + test(res==true,"AlertOKCancel - Didn't get ok. User error, or just bad code?"); + + res = Platform::AlertRetry("Test #4 - AlertRetry", "This is a test of Platform::AlertRetry. I am a blocking dialog with an Retry and a Cancel button. Please hit Retry to continue."); + test(res==true,"AlertRetry - Didn't get retry. User error, or just bad code?"); + + res = Platform::AlertRetry("Test #5 - AlertRetry", "This is a test of Platform::AlertRetry. I am a blocking dialog with an Retry and a Cancel button. Please hit Cancel to continue."); + test(res==false,"AlertRetry - Didn't get cancel. User error, or just bad code?"); + } +}; \ No newline at end of file diff --git a/Engine/source/platform/test/testAsyncPacketQueue.cpp b/Engine/source/platform/test/testAsyncPacketQueue.cpp new file mode 100644 index 000000000..7c2d72333 --- /dev/null +++ b/Engine/source/platform/test/testAsyncPacketQueue.cpp @@ -0,0 +1,151 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "unit/test.h" +#include "platform/async/asyncPacketQueue.h" +#include "console/console.h" +#include "core/util/tVector.h" + +#ifndef TORQUE_SHIPPING + +using namespace UnitTesting; + +#define TEST( x ) test( ( x ), "FAIL: " #x ) + +CreateUnitTest( TestAsyncPacketQueue, "Platform/AsyncPacketQueue" ) +{ + struct Packet + { + typedef void Parent; + + StringChar mChar; + U32 mDuration; + S32 mWriteIndex; + U32 mWriteTime; + + Packet() {} + Packet( StringChar ch, U32 duration ) + : mChar( ch ), mDuration( duration ), mWriteIndex( -2 ), mWriteTime( 0 ) {} + }; + + struct TimeSource + { + typedef void Parent; + + U32 mStartTime; + + TimeSource() + : mStartTime( Platform::getRealMilliseconds() ) {} + + U32 getPosition() + { + return ( Platform::getRealMilliseconds() - mStartTime ); + } + }; + + struct Consumer : public IOutputStream< Packet* > + { + typedef IOutputStream< Packet* > Parent; + + U32 mIndex; + + Consumer() + : mIndex( 0 ) {} + + virtual void write( Packet* const* packets, U32 num ) + { + for( U32 i = 0; i < num; ++ i ) + { + Packet* p = packets[ i ]; + + Con::printf( "%c", p->mChar ); + + p->mWriteTime = Platform::getRealMilliseconds(); + p->mWriteIndex = mIndex; + mIndex ++; + } + } + }; + + void test1( bool dropPackets, U32 queueLength ) + { + F32 factor = Con::getFloatVariable( "$testAsyncPacketQueue::timeFactor", 100.0f ); + String str = Con::getVariable( "$testAsyncPacketQueue::string" ); + if( str.isEmpty() ) + str = "This is a test string"; + + Vector< Packet > packets; + for( U32 i = 0; i < str.size(); ++ i ) + packets.push_back( Packet( str[ i ], U32( Platform::getRandom() * factor ) ) ); + + U32 totalTime = 0; + for( U32 i = 0; i < packets.size(); ++ i ) + totalTime += packets[ i ].mDuration; + + TimeSource timeSource; + Consumer consumer; + AsyncPacketQueue< Packet*, TimeSource* > queue( queueLength, &timeSource, &consumer, totalTime, dropPackets ); + + U32 index = 0; + while( !queue.isAtEnd() ) + { + if( queue.needPacket() + && index < packets.size() ) + { + + Packet* packet = &packets[ index ]; + index ++; + + queue.submitPacket( packet, packet->mDuration ); + } + } + + U32 time = timeSource.mStartTime; + S32 lastIndex = -1; + for( U32 i = 0; i < packets.size(); ++ i ) + { + TEST( ( packets[ i ].mWriteIndex == -2 && dropPackets ) // not written = dropped + || packets[ i ].mWriteIndex == lastIndex + 1 ); + + if( packets[ i ].mWriteIndex != -2 ) + lastIndex ++; + + if( queueLength == 1 ) + TEST( packets[ i ].mWriteTime >= time || dropPackets ); // start time okay + + time += packets[ i ].mDuration; + if( dropPackets ) + TEST( packets[ i ].mWriteTime < time ); // end time okay (if not dropping) + } + } + + void run() + { + test1( false, 1 ); + test1( true, 1 ); + + test1( false, 4 ); + test1( true, 4 ); + } +}; + +#endif // !TORQUE_SHIPPING diff --git a/Engine/source/platform/test/testBasicTypes.cpp b/Engine/source/platform/test/testBasicTypes.cpp new file mode 100644 index 000000000..848356e2f --- /dev/null +++ b/Engine/source/platform/test/testBasicTypes.cpp @@ -0,0 +1,124 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/util/endian.h" +#include "unit/test.h" + +using namespace UnitTesting; + +CreateUnitTest(CheckTypeSizes, "Platform/Types/Sizes") +{ + void run() + { + // Run through all the types and ensure they're the right size. + +#define CheckType(typeName, expectedSize) \ + test( sizeof(typeName) == expectedSize, "Wrong size for a " #typeName ", expected " #expectedSize); + + // One byte types. + CheckType(bool, 1); + CheckType(U8, 1); + CheckType(S8, 1); + CheckType(UTF8, 1); + + // Two byte types. + CheckType(U16, 2); + CheckType(S16, 2); + CheckType(UTF16, 2); + + // Four byte types. + CheckType(U32, 4); + CheckType(S32, 4); + CheckType(F32, 4); + CheckType(UTF32, 4); + + // Eight byte types. + CheckType(U64, 8); + CheckType(S64, 8); + CheckType(F64, 8); + + // 16 byte (128bit) types will go here, when we get some. +#undef CheckType + } +}; + +CreateUnitTest(CheckEndianConversion, "Platform/Types/EndianRoundTrip") +{ + void run() + { + // Convenient and non-palindrome byte patterns to test with. + const U16 U16Test = 0xA1B2; + const S16 S16Test = 0x52A1; + + const U32 U32Test = 0xA1B2C3D4; + const S32 S32Test = 0xD4C3B2A1; + const F32 F32Test = 1234.5678f; + + //const U64 U64Test = 0xA1B2C3D4E3F2E10A; + //const S64 S64Test = 0x1A2B3C4D3E2F1EA0; + const F64 F64Test = 12345678.9101112131415; + + // Run through all the conversions - bump stuff from host to little or big + // endian and back again. +#define CheckEndianRoundTrip(type, b_or_l) \ + test( type##Test == convert##b_or_l##EndianToHost(convertHostTo##b_or_l##Endian(type##Test)), "Failed to convert the " #type " test value to " #b_or_l " endian and back to host endian order."); + +#define CheckTypeBothWays(type) \ + CheckEndianRoundTrip(type, B); \ + CheckEndianRoundTrip(type, L); + +#define CheckIntsForBitSize(bits) \ + CheckTypeBothWays( U##bits ); \ + CheckTypeBothWays( S##bits ); + + // Don't check 8-bit types - they aren't affected by endian issues. + + // Check the >1 byte int types, though. + CheckIntsForBitSize(16); + CheckIntsForBitSize(32); + // CheckIntsForBitSize(64); // don't have convertHostToLEndian(const U64/S64) so this doesn't work + + // And check the float types. + CheckTypeBothWays(F32); + CheckTypeBothWays(F64); + + // We'd check 128bit types here, if we had any. + +#undef CheckIntsForBitSize +#undef CheckTypeBothWays +#undef CheckEndianRoundTrip + } +}; + +CreateUnitTest(CheckEndianSwap, "Platform/Types/EndianSwap") +{ + void run() + { + U32 swap32 = 0xABCDEF12; + U16 swap16 = 0xABCD; + + test(endianSwap(swap32) == 0x12EFCDAB, "32 bit endianSwap should reverse byte order, but didn't."); + test(endianSwap(swap16) == 0xCDAB, "16 bit endianSwap should reverse byte order, but didn't."); + } +}; + diff --git a/Engine/source/platform/test/testFile.cpp b/Engine/source/platform/test/testFile.cpp new file mode 100644 index 000000000..667f9fbee --- /dev/null +++ b/Engine/source/platform/test/testFile.cpp @@ -0,0 +1,150 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/fileio.h" +#include "unit/test.h" +#include "core/util/tVector.h" +#include "console/console.h" + +using namespace UnitTesting; + +CreateUnitTest(CheckFileListingAndExclusion, "File/ListDirectoryAndExclusions") +{ + void run() + { + // Just dump everything under the current directory. We should + // find at least one file. + + // Exclude .svn and CVS + Platform::clearExcludedDirectories(); + Platform::addExcludedDirectory(".svn"); + Platform::addExcludedDirectory("CVS"); + + test(Platform::isExcludedDirectory("foo") == false, "Doesn't match list, shouldn't be excluded."); + test(Platform::isExcludedDirectory(".svn") == true, "On list, should be excluded."); + test(Platform::isExcludedDirectory("CVS") == true, "On list, should be excluded."); + test(Platform::isExcludedDirectory(".svnCVS") == false, "Looks like a duck, but it shouldn't be excluded cuz it's distinct from all entries on the exclusion list."); + + // Ok, now our exclusion list is setup, so let's dump some paths. + Vector < Platform::FileInfo > pathInfo; + Platform::dumpPath (Platform::getCurrentDirectory(), pathInfo, 2); + + Con::printf("Dump of files in '%s', up to 2 levels deep...", Platform::getCurrentDirectory()); + for(S32 i=0; i 0, "Should find at least SOMETHING in the current directory!"); + + // This'll nuke info if we run it in a live situation... so don't run unit + // tests in a live situation. ;) + Platform::clearExcludedDirectories(); + } +}; + +CreateUnitTest(CheckFileTouchAndTime, "File/TouchAndTime") +{ + void run() + { + FileTime create[2], modify[2]; + + // Create a file and sleep for a second. + File f; + f.open("testTouch.file", File::WriteAppend); + f.close(); + + Platform::sleep(2000); + + // Touch a file and note its last-modified. + dFileTouch("testTouch.file"); + test(Platform::isFile("testTouch.file"), "We just touched this file - it should exist."); + test(Platform::getFileTimes("testTouch.file", &create[0], &modify[0]), "Failed to get filetimes for a file we just created."); + + // Sleep for a few seconds... + Platform::sleep(5000); + + // Touch it again, and compare the last-modifieds. + test(Platform::isFile("testTouch.file"), "We just touched this file - it should exist."); + dFileTouch("testTouch.file"); + test(Platform::isFile("testTouch.file"), "We just touched this file - it should exist."); + test(Platform::getFileTimes("testTouch.file", &create[1], &modify[1]), "Failed to get filetimes for a file we just created."); + + // Now compare the times... + test(Platform::compareFileTimes(modify[0], modify[1]) < 0, "Timestamps are wrong - modify[0] should be before modify[1]!"); + + // This seems to fail even on a valid case... + // test(Platform::compareFileTimes(create[0], create[1]) == 0, "Create timestamps should match - we didn't delete the file during this test."); + + // Clean up.. + dFileDelete("testTouch.file"); + test(!Platform::isFile("testTouch.file"), "Somehow failed to delete our test file."); + } +}; + +// Mac has no implementations for these functions, so we 'def it out for now. +#if 0 +CreateUnitTest(CheckVolumes, "File/Volumes") +{ + void run() + { + Con::printf("Dumping volumes by name:"); + + Vector names; + Platform::getVolumeNamesList(names); + + test(names.size() > 0, "We should have at least one volume..."); + + for(S32 i=0; i info; + Platform::getVolumeInformationList(info); + + test(names.size() == info.size(), "Got inconsistent number of volumes back from info vs. name list functions!"); + + for(S32 i=0; i 0, "Didn't get any data back!"); + } +}; + +CreateUnitTest( TestTCPRequestJournal, "Platform/Net/JournalTCPRequest") +{ + NetSocket mSocket; + S32 mDataRecved; + + void handleNotify(NetSocket sock, U32 state) + { + // Only consider our own socket. + if(mSocket != sock) + return; + + // Ok - what's the state? We do some dumb responses to given states + // in order to fulfill the request. + if(state == Net::Connected) + { + U8 reqBuffer[] = { + "GET / HTTP/1.0\nUser-Agent: Torque/1.0\n\n" + }; + + Net::Error e = Net::sendtoSocket(mSocket, reqBuffer, sizeof(reqBuffer)); + + test(e == Net::NoError, "Got an error sending our HTTP request!"); + } + else if(state == Net::Disconnected) + { + Process::requestShutdown(); + mSocket = NULL; + } + } + + void handleReceive(NetSocket sock, RawData incomingData) + { + // Only consider our own socket. + if(mSocket != sock) + return; + + char buff[4096]; + dMemcpy(buff, incomingData.data, incomingData.size); + buff[incomingData.size] = 0; + + UnitPrint("Got a message...\n"); + UnitPrint(buff); + UnitPrint("------\n"); + + mDataRecved += incomingData.size; + } + + void makeRequest() + { + mSocket = InvalidSocket; + mDataRecved = 0; + + // Initialize networking - done by initLibraries currently + //test(Net::init(), "Failed to initialize networking!"); + + // Hook into the signals. + Net::smConnectionNotify. notify(this, &TestTCPRequestJournal::handleNotify); + Net::smConnectionReceive.notify(this, &TestTCPRequestJournal::handleReceive); + + // Open a TCP connection to garagegames.com + mSocket = Net::openConnectTo("ip:72.246.107.193:80"); + + // Let the callbacks enable things to process. + while(Process::processEvents()) + ; + + // Unhook from the signals. + Net::smConnectionNotify. remove(this, &TestTCPRequestJournal::handleNotify); + Net::smConnectionReceive.remove(this, &TestTCPRequestJournal::handleReceive); + + test(mDataRecved > 0, "Didn't get any data back!"); + } + + void run() + { + Journal::Record("journalTCP.jrn"); + + makeRequest(); + + S32 bytesRead = mDataRecved; + + Journal::Stop(); + + Journal::Play("journalTCP.jrn"); + + makeRequest(); + + Journal::Stop(); + + test(bytesRead == mDataRecved, "Didn't get same data back from journal playback."); + + } +}; \ No newline at end of file diff --git a/Engine/source/platform/test/testThreadPool.cpp b/Engine/source/platform/test/testThreadPool.cpp new file mode 100644 index 000000000..4055ce3fb --- /dev/null +++ b/Engine/source/platform/test/testThreadPool.cpp @@ -0,0 +1,85 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "unit/test.h" +#include "platform/threads/threadPool.h" +#include "console/console.h" +#include "core/util/tVector.h" + +#ifndef TORQUE_SHIPPING + +using namespace UnitTesting; + +#define TEST( x ) test( ( x ), "FAIL: " #x ) + +// Simple test that creates and verifies an array of numbers using +// thread pool work items. + +CreateUnitTest( TestThreadPool, "Platform/ThreadPool/Simple" ) +{ + enum { DEFAULT_NUM_ITEMS = 4000 }; + + static Vector< U32 > results; + + struct TestItem : public ThreadPool::WorkItem + { + typedef ThreadPool::WorkItem Parent; + + U32 mIndex; + + TestItem( U32 index ) + : mIndex( index ) {} + + protected: + virtual void execute() + { + results[ mIndex ] = mIndex; + } + }; + + void run() + { + U32 numItems = Con::getIntVariable( "$testThreadPool::numValues", DEFAULT_NUM_ITEMS ); + ThreadPool* pool = &ThreadPool::GLOBAL(); + results.setSize( numItems ); + + for( U32 i = 0; i < numItems; ++ i ) + results[ i ] = U32( -1 ); + + for( U32 i = 0; i < numItems; ++ i ) + { + ThreadSafeRef< TestItem > item( new TestItem( i ) ); + pool->queueWorkItem( item ); + } + + pool->flushWorkItems(); + + for( U32 i = 0; i < numItems; ++ i ) + test( results[ i ] == i, "result mismatch" ); + + results.clear(); + } +}; + +Vector< U32 > TestThreadPool::results( __FILE__, __LINE__ ); + +#endif // !TORQUE_SHIPPING diff --git a/Engine/source/platform/test/testThreadSafeDeque.cpp b/Engine/source/platform/test/testThreadSafeDeque.cpp new file mode 100644 index 000000000..8b3e019a4 --- /dev/null +++ b/Engine/source/platform/test/testThreadSafeDeque.cpp @@ -0,0 +1,403 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "unit/test.h" +#include "platform/threads/threadSafeDeque.h" +#include "platform/threads/thread.h" +#include "core/util/tVector.h" +#include "console/console.h" + + +#ifndef TORQUE_SHIPPING + +using namespace UnitTesting; + +#define TEST( x ) test( ( x ), "FAIL: " #x ) +#define XTEST( t, x ) t->test( ( x ), "FAIL: " #x ) + + +// Test deque without concurrency. + +CreateUnitTest( TestThreadSafeDequeSerial, "Platform/ThreadSafeDeque/Serial" ) +{ + void test1() + { + ThreadSafeDeque< char > deque; + String str = "teststring"; + + for( U32 i = 0; i < str.length(); ++ i ) + deque.pushBack( str[ i ] ); + + TEST( !deque.isEmpty() ); + + for( U32 i = 0; i < str.length(); ++ i ) + { + char ch; + TEST( deque.tryPopFront( ch ) && ch == str[ i ] ); + } + } + + void test2() + { + ThreadSafeDeque< char > deque; + String str = "teststring"; + + const char* p1 = str.c_str() + 4; + const char* p2 = p1 + 1; + while( *p2 ) + { + deque.pushFront( *p1 ); + deque.pushBack( *p2 ); + + -- p1; + ++ p2; + } + +#ifdef TORQUE_DEBUG + deque.dumpDebug(); +#endif + + for( U32 i = 0; i < str.length(); ++ i ) + { + char ch; + TEST( deque.tryPopFront( ch ) && ch == str[ i ] ); + } + } + + void test3() + { + ThreadSafeDeque< char > deque; + String str = "teststring"; + + const char* p1 = str.c_str() + 4; + const char* p2 = p1 + 1; + while( *p2 ) + { + deque.pushFront( *p1 ); + deque.pushBack( *p2 ); + + -- p1; + ++ p2; + } + +#ifdef TORQUE_DEBUG + deque.dumpDebug(); +#endif + + for( S32 i = ( str.length() - 1 ); i >= 0; -- i ) + { + char ch; + TEST( deque.tryPopBack( ch ) && ch == str[ i ] ); + } + } + + void test4() + { + ThreadSafeDeque< char > deque; + char ch; + + TEST( deque.isEmpty() ); + + deque.pushFront( 'a' ); + TEST( !deque.isEmpty() ); + TEST( deque.tryPopFront( ch ) ); + TEST( ch == 'a' ); + + deque.pushBack( 'a' ); + TEST( !deque.isEmpty() ); + TEST( deque.tryPopFront( ch ) ); + TEST( ch == 'a' ); + + deque.pushBack( 'a' ); + TEST( !deque.isEmpty() ); + TEST( deque.tryPopBack( ch ) ); + TEST( ch == 'a' ); + + deque.pushFront( 'a' ); + TEST( !deque.isEmpty() ); + TEST( deque.tryPopBack( ch ) ); + TEST( ch == 'a' ); + } + + void run() + { + test1(); + test2(); + test3(); + test4(); + } +}; + +// Test deque in a concurrent setting. + +CreateUnitTest( TestThreadSafeDequeConcurrentSimple, "Platform/ThreadSafeDeque/ConcurrentSimple" ) +{ +public: + typedef TestThreadSafeDequeConcurrentSimple TestType; + + enum + { + DEFAULT_NUM_VALUES = 100000, + }; + + struct Value : public ThreadSafeRefCount< Value > + { + U32 mIndex; + U32 mTick; + + Value() {} + Value( U32 index, U32 tick ) + : mIndex( index ), mTick( tick ) {} + }; + + typedef ThreadSafeRef< Value > ValueRef; + + struct Deque : public ThreadSafeDeque< ValueRef > + { + typedef ThreadSafeDeque Parent; + + U32 mPushIndex; + U32 mPopIndex; + + Deque() + : mPushIndex( 0 ), mPopIndex( 0 ) {} + + void pushBack( const ValueRef& value ) + { + AssertFatal( value->mIndex == mPushIndex, "index out of line" ); + mPushIndex ++; + Parent::pushBack( value ); + } + bool tryPopFront( ValueRef& outValue ) + { + if( Parent::tryPopFront( outValue ) ) + { + AssertFatal( outValue->mIndex == mPopIndex, "index out of line" ); + mPopIndex ++; + return true; + } + else + return false; + } + }; + + Deque mDeque; + Vector< U32 > mValues; + + struct ProducerThread : public Thread + { + ProducerThread( TestType* test ) + : Thread( 0, test ) {} + + virtual void run( void* arg ) + { + _setName( "ProducerThread" ); + Platform::outputDebugString( "Starting ProducerThread" ); + + TestType* test = ( TestType* ) arg; + + for( U32 i = 0; i < test->mValues.size(); ++ i ) + { + U32 tick = Platform::getRealMilliseconds(); + test->mValues[ i ] = tick; + + ValueRef val = new Value( i, tick ); + test->mDeque.pushBack( val ); + } + Platform::outputDebugString( "Stopping ProducerThread" ); + } + }; + struct ConsumerThread : public Thread + { + ConsumerThread( TestType* test ) + : Thread( 0, test ) {} + + virtual void run( void* arg ) + { + _setName( "ConsumerThread" ); + Platform::outputDebugString( "Starting CosumerThread" ); + TestType* t = ( TestType* ) arg; + + for( U32 i = 0; i < t->mValues.size(); ++ i ) + { + ValueRef value; + while( !t->mDeque.tryPopFront( value ) ); + + XTEST( t, value->mIndex == i ); + XTEST( t, t->mValues[ i ] == value->mTick ); + } + Platform::outputDebugString( "Stopping ConsumerThread" ); + } + }; + + void run() + { + U32 numValues = Con::getIntVariable( "$testThreadSafeDeque::numValues", DEFAULT_NUM_VALUES ); + mValues.setSize( numValues ); + + ProducerThread pThread( this ); + ConsumerThread cThread( this ); + + pThread.start(); + cThread.start(); + + pThread.join(); + cThread.join(); + + mValues.clear(); + } +}; + +CreateUnitTest( TestThreadSafeDequeConcurrent, "Platform/ThreadSafeDeque/Concurrent" ) +{ +public: + typedef TestThreadSafeDequeConcurrent TestType; + + enum + { + DEFAULT_NUM_VALUES = 100000, + DEFAULT_NUM_CONSUMERS = 10, + DEFAULT_NUM_PRODUCERS = 10 + }; + + struct Value : public ThreadSafeRefCount< Value > + { + U32 mIndex; + U32 mTick; + + Value() {} + Value( U32 index, U32 tick ) + : mIndex( index ), mTick( tick ) {} + }; + + typedef ThreadSafeRef< Value > ValueRef; + + U32 mProducerIndex; + U32 mConsumerIndex; + ThreadSafeDeque< ValueRef > mDeque; + Vector< U32 > mValues; + + struct ProducerThread : public Thread + { + ProducerThread( TestType* test ) + : Thread( 0, test ) {} + + virtual void run( void* arg ) + { + _setName( "ProducerThread" ); + Platform::outputDebugString( "Starting ProducerThread" ); + TestType* test = ( TestType* ) arg; + + while( 1 ) + { + U32 index = test->mProducerIndex; + if( index == test->mValues.size() ) + break; + + if( dCompareAndSwap( test->mProducerIndex, index, index + 1 ) ) + { + U32 tick = Platform::getRealMilliseconds(); + test->mValues[ index ] = tick; + + ValueRef val = new Value( index, tick ); + test->mDeque.pushBack( val ); + } + } + Platform::outputDebugString( "Stopping ProducerThread" ); + } + }; + struct ConsumerThread : public Thread + { + ConsumerThread( TestType* test ) + : Thread( 0, test ) {} + + virtual void run( void* arg ) + { + _setName( "ConsumerThread" ); + Platform::outputDebugString( "Starting ConsumerThread" ); + TestType* t = ( TestType* ) arg; + + while( t->mConsumerIndex < t->mValues.size() ) + { + ValueRef value; + if( t->mDeque.tryPopFront( value ) ) + { + dFetchAndAdd( t->mConsumerIndex, 1 ); + XTEST( t, t->mValues[ value->mIndex ] == value->mTick ); + t->mValues[ value->mIndex ] = 0; + } + } + + Platform::outputDebugString( "Stopping ConsumerThread" ); + } + }; + + void run() + { + U32 numValues = Con::getIntVariable( "$testThreadSafeDeque::numValues", DEFAULT_NUM_VALUES ); + U32 numConsumers = Con::getIntVariable( "$testThreadSafeDeque::numConsumers", DEFAULT_NUM_CONSUMERS ); + U32 numProducers = Con::getIntVariable( "$testThreadSafeDeque::numProducers", DEFAULT_NUM_PRODUCERS ); + + mProducerIndex = 0; + mConsumerIndex = 0; + mValues.setSize( numValues ); + + U32 tick = Platform::getRealMilliseconds(); + for( U32 i = 0; i < numValues; ++ i ) + mValues[ i ] = tick; + + Vector< ProducerThread* > producers; + Vector< ConsumerThread* > consumers; + + producers.setSize( numProducers ); + consumers.setSize( numConsumers ); + + for( U32 i = 0; i < numProducers; ++ i ) + { + producers[ i ] = new ProducerThread( this ); + producers[ i ]->start(); + } + for( U32 i = 0; i < numConsumers; ++ i ) + { + consumers[ i ] = new ConsumerThread( this ); + consumers[ i ]->start(); + } + + for( U32 i = 0; i < numProducers; ++ i ) + { + producers[ i ]->join(); + delete producers[ i ]; + } + for( U32 i = 0; i < numConsumers; ++ i ) + { + consumers[ i ]->join(); + delete consumers[ i ]; + } + + for( U32 i = 0; i < mValues.size(); ++ i ) + TEST( mValues[ i ] == 0 ); + + mValues.clear(); + } +}; + +#endif // !TORQUE_SHIPPING diff --git a/Engine/source/platform/test/testThreadSafePriorityQueue.cpp b/Engine/source/platform/test/testThreadSafePriorityQueue.cpp new file mode 100644 index 000000000..5933eae2e --- /dev/null +++ b/Engine/source/platform/test/testThreadSafePriorityQueue.cpp @@ -0,0 +1,245 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "unit/test.h" +#include "platform/threads/threadSafePriorityQueue.h" +#include "platform/threads/thread.h" +#include "core/util/tVector.h" +#include "console/console.h" + + +#ifndef TORQUE_SHIPPING + +using namespace UnitTesting; + +#define TEST( x ) test( ( x ), "FAIL: " #x ) +#define XTEST( t, x ) t->test( ( x ), "FAIL: " #x ) + + +// Test queue without concurrency. + +CreateUnitTest( TestThreadSafePriorityQueueSerial, "Platform/ThreadSafePriorityQueue/Serial" ) +{ + struct Value + { + F32 mPriority; + U32 mIndex; + + Value() {} + Value( F32 priority, U32 index ) + : mPriority( priority ), mIndex( index ) {} + }; + + template< bool SORT_MIN_TO_MAX > + void test1() + { + Vector< Value > values; + + values.push_back( Value( 0.2f, 2 ) ); + values.push_back( Value( 0.7f, 7 ) ); + values.push_back( Value( 0.4f, 4 ) ); + values.push_back( Value( 0.6f, 6 ) ); + values.push_back( Value( 0.1f, 1 ) ); + values.push_back( Value( 0.5f, 5 ) ); + values.push_back( Value( 0.3f, 3 ) ); + values.push_back( Value( 0.8f, 8 ) ); + values.push_back( Value( 0.6f, 6 ) ); + values.push_back( Value( 0.9f, 9 ) ); + values.push_back( Value( 0.0f, 0 ) ); + + const S32 min = 0; + const S32 max = 9; + + ThreadSafePriorityQueue< U32, F32, SORT_MIN_TO_MAX > queue; + + for( U32 i = 0; i < values.size(); ++ i ) + queue.insert( values[ i ].mPriority, values[ i ].mIndex ); + + TEST( !queue.isEmpty() ); + + S32 index; + if( SORT_MIN_TO_MAX ) + index = min - 1; + else + index = max + 1; + + for( U32 i = 0; i < values.size(); ++ i ) + { + U32 value; + TEST( queue.takeNext( value ) ); + + if( value != index ) + { + if( SORT_MIN_TO_MAX ) + index ++; + else + index --; + } + + TEST( value == index ); + } + } + + void run() + { + test1< true >(); + test1< false >(); + } +}; + +// Test queue with concurrency. + +CreateUnitTest( TestThreadSafePriorityQueueConcurrent, "Platform/ThreadSafePriorityQueue/Concurrent" ) +{ +public: + typedef TestThreadSafePriorityQueueConcurrent TestType; + + enum + { + DEFAULT_NUM_VALUES = 100000, + DEFAULT_NUM_CONSUMERS = 10, + DEFAULT_NUM_PRODUCERS = 10 + }; + + struct Value : public ThreadSafeRefCount< Value > + { + U32 mIndex; + F32 mPriority; + bool mCheck; + + Value() : mCheck( false ) {} + Value( U32 index, F32 priority ) + : mIndex( index ), mPriority( priority ), mCheck( false ) {} + }; + + typedef ThreadSafeRef< Value > ValueRef; + + U32 mProducerIndex; + U32 mConsumerIndex; + ThreadSafePriorityQueue< ValueRef > mQueue; + Vector< ValueRef > mValues; + + struct ProducerThread : public Thread + { + ProducerThread( TestType* test ) + : Thread( 0, test ) {} + + virtual void run( void* arg ) + { + _setName( "ProducerThread" ); + Platform::outputDebugString( "Starting ProducerThread" ); + TestType* test = ( TestType* ) arg; + + while( 1 ) + { + U32 index = test->mProducerIndex; + if( index == test->mValues.size() ) + break; + + if( dCompareAndSwap( test->mProducerIndex, index, index + 1 ) ) + { + F32 priority = Platform::getRandom(); + ValueRef val = new Value( index, priority ); + test->mValues[ index ] = val; + test->mQueue.insert( priority, val ); + } + } + Platform::outputDebugString( "Stopping ProducerThread" ); + } + }; + struct ConsumerThread : public Thread + { + ConsumerThread( TestType* test ) + : Thread( 0, test ) {} + + virtual void run( void* arg ) + { + _setName( "ConsumerThread" ); + Platform::outputDebugString( "Starting ConsumerThread" ); + TestType* t = ( TestType* ) arg; + + while( t->mConsumerIndex < t->mValues.size() ) + { + ValueRef value; + if( t->mQueue.takeNext( value ) ) + { + dFetchAndAdd( t->mConsumerIndex, 1 ); + XTEST( t, t->mValues[ value->mIndex ] == value ); + value->mCheck = true; + } + else + Platform::sleep( 5 ); + } + Platform::outputDebugString( "Stopping ConsumerThread" ); + } + }; + + void run() + { + U32 numValues = Con::getIntVariable( "$testThreadSafePriorityQueue::numValues", DEFAULT_NUM_VALUES ); + U32 numConsumers = Con::getIntVariable( "$testThreadSafePriorityQueue::numConsumers", DEFAULT_NUM_CONSUMERS ); + U32 numProducers = Con::getIntVariable( "$testThreadSafePriorityQueue::numProducers", DEFAULT_NUM_PRODUCERS ); + + mProducerIndex = 0; + mConsumerIndex = 0; + mValues.setSize( numValues ); + + Vector< ProducerThread* > producers; + Vector< ConsumerThread* > consumers; + + producers.setSize( numProducers ); + consumers.setSize( numConsumers ); + + for( U32 i = 0; i < numProducers; ++ i ) + { + producers[ i ] = new ProducerThread( this ); + producers[ i ]->start(); + } + for( U32 i = 0; i < numConsumers; ++ i ) + { + consumers[ i ] = new ConsumerThread( this ); + consumers[ i ]->start(); + } + + for( U32 i = 0; i < numProducers; ++ i ) + { + producers[ i ]->join(); + delete producers[ i ]; + } + for( U32 i = 0; i < numConsumers; ++ i ) + { + consumers[ i ]->join(); + delete consumers[ i ]; + } + + for( U32 i = 0; i < mValues.size(); ++ i ) + { + TEST( mValues[ i ] != NULL ); + if( mValues[ i ] != NULL ) + TEST( mValues[ i ]->mCheck ); + } + + mValues.clear(); + } +}; + +#endif // !TORQUE_SHIPPING diff --git a/Engine/source/platform/test/testThreadSafeRefCount.cpp b/Engine/source/platform/test/testThreadSafeRefCount.cpp new file mode 100644 index 000000000..8ad371797 --- /dev/null +++ b/Engine/source/platform/test/testThreadSafeRefCount.cpp @@ -0,0 +1,227 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "unit/test.h" +#include "platform/threads/threadSafeRefCount.h" +#include "platform/threads/thread.h" +#include "core/util/tVector.h" +#include "console/console.h" + +#ifndef TORQUE_SHIPPING + +using namespace UnitTesting; + +#define TEST( x ) test( ( x ), "FAIL: " #x ) + +CreateUnitTest( TestThreadSafeRefCountSerial, "Platform/ThreadSafeRefCount/Serial" ) +{ + struct TestObject : public ThreadSafeRefCount< TestObject > + { + static bool smDeleted; + + TestObject() + { + smDeleted = false; + } + ~TestObject() + { + smDeleted = true; + } + }; + + typedef ThreadSafeRef< TestObject > TestObjectRef; + + void run() + { + TestObjectRef ref1 = new TestObject; + TEST( !ref1->isShared() ); + TEST( ref1 != NULL ); + + TestObjectRef ref2 = ref1; + TEST( ref1->isShared() ); + TEST( ref2->isShared() ); + TEST( ref1 == ref2 ); + + ref1 = NULL; + TEST( !ref2->isShared() ); + + ref2 = NULL; + TEST( TestObject::smDeleted ); + } +}; + +bool TestThreadSafeRefCountSerial::TestObject::smDeleted; + +CreateUnitTest( TestThreadSafeRefCountConcurrent, "Platform/ThreadSafeRefCount/Concurrent" ) +{ +public: + typedef TestThreadSafeRefCountConcurrent TestType; + enum + { + NUM_ADD_REFS_PER_THREAD = 1000, + NUM_EXTRA_REFS_PER_THREAD = 1000, + NUM_THREADS = 10 + }; + + class TestObject : public ThreadSafeRefCount< TestObject > + { + public: + }; + + ThreadSafeRef< TestObject > mRef; + + class TestThread : public Thread + { + public: + TestType* mTest; + Vector< ThreadSafeRef< TestObject > > mExtraRefs; + + TestThread( TestType* test ) + : mTest( test ) {} + + void run( void* arg ) + { + if( !arg ) + { + for( U32 i = 0; i < NUM_ADD_REFS_PER_THREAD; ++ i ) + mTest->mRef->addRef(); + + mExtraRefs.setSize( NUM_EXTRA_REFS_PER_THREAD ); + for( U32 i = 0; i < NUM_EXTRA_REFS_PER_THREAD; ++ i ) + mExtraRefs[ i ] = mTest->mRef; + } + else + { + mExtraRefs.clear(); + + for( U32 i = 0; i < NUM_ADD_REFS_PER_THREAD; ++ i ) + mTest->mRef->release(); + } + } + }; + + void run() + { + mRef = new TestObject; + TEST( mRef->getRefCount() == 2 ); // increments of 2 + + Vector< TestThread* > threads; + threads.setSize( NUM_THREADS ); + + // Create threads. + for( U32 i = 0; i < NUM_THREADS; ++ i ) + threads[ i ] = new TestThread( this ); + + // Run phase 1: create references. + for( U32 i = 0; i < NUM_THREADS; ++ i ) + threads[ i ]->start( NULL ); + + // Wait for completion. + for( U32 i = 0; i < NUM_THREADS; ++ i ) + threads[ i ]->join(); + + Con::printf( "REF: %i", mRef->getRefCount() ); + TEST( mRef->getRefCount() == 2 + ( ( NUM_ADD_REFS_PER_THREAD + NUM_EXTRA_REFS_PER_THREAD ) * NUM_THREADS * 2 ) ); + + // Run phase 2: release references. + for( U32 i = 0; i < NUM_THREADS; ++ i ) + threads[ i ]->start( ( void* ) 1 ); + + // Wait for completion. + for( U32 i = 0; i < NUM_THREADS; ++ i ) + { + threads[ i ]->join(); + delete threads[ i ]; + } + + TEST( mRef->getRefCount() == 2 ); // increments of two + + mRef = NULL; + } +}; + +CreateUnitTest( TestThreadSafeRefCountTagging, "Platform/ThreadSafeRefCount/Tagging" ) +{ + struct TestObject : public ThreadSafeRefCount< TestObject > {}; + + typedef ThreadSafeRef< TestObject > TestObjectRef; + + void run() + { + TestObjectRef ref; + + TEST( !ref.isTagged() ); + TEST( !ref ); + TEST( !ref.ptr() ); + + TEST( ref.trySetFromTo( ref, NULL ) ); + TEST( !ref.isTagged() ); + + TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_Set ) ); + TEST( ref.isTagged() ); + TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_Set ) ); + TEST( ref.isTagged() ); + + TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_Unset ) ); + TEST( !ref.isTagged() ); + TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_Unset ) ); + TEST( !ref.isTagged() ); + + TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_SetOrFail ) ); + TEST( ref.isTagged() ); + TEST( !ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_SetOrFail ) ); + TEST( ref.isTagged() ); + TEST( !ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_FailIfSet ) ); + + TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_UnsetOrFail ) ); + TEST( !ref.isTagged() ); + TEST( !ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_UnsetOrFail ) ); + TEST( !ref.isTagged() ); + TEST( !ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_FailIfUnset ) ); + + TestObjectRef objectA = new TestObject; + TestObjectRef objectB = new TestObject; + + TEST( !objectA->isShared() ); + TEST( !objectB->isShared() ); + + ref = objectA; + TEST( !ref.isTagged() ); + TEST( ref == objectA ); + TEST( ref == objectA.ptr() ); + TEST( objectA->isShared() ); + + TEST( ref.trySetFromTo( objectA, objectB, TestObjectRef::TAG_Set ) ); + TEST( ref.isTagged() ); + TEST( ref == objectB ); + TEST( ref == objectB.ptr() ); + TEST( objectB->isShared() ); + TEST( !objectA->isShared() ); + + TEST( ref.trySetFromTo( ref, objectA ) ); + TEST( ref.isTagged() ); + TEST( ref == objectA ); + TEST( ref == objectA.ptr() ); + } +}; + +#endif // !TORQUE_SHIPPING diff --git a/Engine/source/platform/test/testThreading.cpp b/Engine/source/platform/test/testThreading.cpp new file mode 100644 index 000000000..ede62a507 --- /dev/null +++ b/Engine/source/platform/test/testThreading.cpp @@ -0,0 +1,417 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "platform/threads/thread.h" +#include "platform/threads/semaphore.h" +#include "platform/threads/mutex.h" +#include "unit/test.h" +#include "core/util/tVector.h" +#include "console/console.h" + +using namespace UnitTesting; + +class ThreadTestHarness +{ + U32 mStartTime, mEndTime, mCleanupTime; + void (*mThreadBody)(void*); + S32 mThreadCount; + Thread **mThreads; + +public: + ThreadTestHarness() + { + mStartTime = mEndTime = mCleanupTime = 0; + mThreadBody = NULL; + mThreadCount = 1; + mThreads = NULL; + } + + void startThreads(void (*threadBody)(void*), void *arg, U32 threadCount) + { + mThreadCount = threadCount; + mThreadBody = threadBody; + + // Start up threadCount threads... + mThreads = new Thread*[threadCount]; + + mStartTime = Platform::getRealMilliseconds(); + + //Con::printf(" Running with %d threads...", threadCount); + for(S32 i=0; istart(); + } + } + + void waitForThreadExit(U32 checkFrequencyMs) + { + // And wait for them to complete. + bool someAlive = true; + S32 liveCount = mThreadCount; + + while(someAlive) + { + //Con::printf(" - Sleeping for %dms with %d live threads.", checkFrequencyMs, liveCount); + Platform::sleep(checkFrequencyMs); + + someAlive = false; + liveCount = 0; + + for(S32 i=0; iisAlive()) + continue; + + someAlive = true; + liveCount++; + } + + } + + mEndTime = Platform::getRealMilliseconds(); + + // Clean up memory at this point. + for(S32 i=0; iacquire(false), "Should succeed at acquiring a new semaphore with count 1."); + test(sem2->acquire(false), "This one should succeed too, see previous test."); + + // Test that we can do non-blocking acquires that fail. + test(sem1->acquire(false)==false, "Should failed, as we've already got the sem."); + sem1->release(); + test(sem2->acquire(false)==false, "Should also fail."); + sem2->release(); + + // Test that we can do blocking acquires that succeed. + test(sem1->acquire(true)==true, "Should succeed as we just released."); + test(sem2->acquire(true)==true, "Should succeed as we just released."); + + // Can't test blocking acquires that never happen... :) + + // Clean up. + delete sem1; + delete sem2; + } +}; + +CreateUnitTest( SemaphoreWaitTest, "Platform/Threads/SemaphoreWaitTest") +{ + static void threadBody(void *self) + { + SemaphoreWaitTest *me = (SemaphoreWaitTest*)self; + + // Wait for the semaphore to get released. + me->mSemaphore->acquire(); + + // Increment the counter. + Mutex::lockMutex(me->mMutex); + me->mDoneCount++; + Mutex::unlockMutex(me->mMutex); + + // Signal back to the main thread we're done. + me->mPostbackSemaphore->release(); + } + + Semaphore *mSemaphore; + Semaphore *mPostbackSemaphore; + void *mMutex; + U32 mDoneCount; + + const static int csmThreadCount = 10; + + void run() + { + ThreadTestHarness tth; + + mDoneCount = 0; + mSemaphore = new Semaphore(0); + mPostbackSemaphore = new Semaphore(0); + mMutex = Mutex::createMutex(); + + tth.startThreads(&threadBody, this, csmThreadCount); + + Platform::sleep(500); + + Mutex::lockMutex(mMutex); + test(mDoneCount == 0, "no threads should have touched the counter yet."); + Mutex::unlockMutex(mMutex); + + // Let 500 come out. + for(S32 i=0; irelease(); + + // And wait for 500 postbacks. + for(S32 i=0; iacquire(); + + Mutex::lockMutex(mMutex); + test(mDoneCount == csmThreadCount / 2, "Didn't get expected number of done threads! (a)"); + Mutex::unlockMutex(mMutex); + + // Ok, now do the rest. + // Let 500 come out. + for(S32 i=0; irelease(); + + // And wait for 500 postbacks. + for(S32 i=0; iacquire(); + + Mutex::lockMutex(mMutex); + test(mDoneCount == csmThreadCount, "Didn't get expected number of done threads! (b)"); + Mutex::unlockMutex(mMutex); + + // Wait for the threads to exit - shouldn't have to wait ever though. + tth.waitForThreadExit(10); + + // Make sure no one touched our data after shutdown time. + Mutex::lockMutex(mMutex); + test(mDoneCount == csmThreadCount, "Didn't get expected number of done threads! (c)"); + Mutex::unlockMutex(mMutex); + } +}; + +CreateUnitTest( MutexWaitTest, "Platform/Threads/MutexWaitTest") +{ + static void threadBody(void *self) + { + MutexWaitTest *me = (MutexWaitTest*)self; + + // Increment the counter. We'll block until the mutex + // is open. + Mutex::lockMutex(me->mMutex); + me->mDoneCount++; + Mutex::unlockMutex(me->mMutex); + } + + void *mMutex; + U32 mDoneCount; + + const static int csmThreadCount = 10; + + void run() + { + mMutex = Mutex::createMutex(); + mDoneCount = 0; + + // We lock the mutex before we create any threads, so that all the threads + // block on the mutex. Then we unlock it and let them all work their way + // through the increment. + Mutex::lockMutex(mMutex); + + ThreadTestHarness tth; + tth.startThreads(&threadBody, this, csmThreadCount); + + Platform::sleep(5000); + + // Check count is still zero. + test(mDoneCount == 0, "Uh oh - a thread somehow didn't get blocked by the locked mutex!"); + + // Open the flood gates... + Mutex::unlockMutex(mMutex); + + // Wait for the threads to all finish executing. + tth.waitForThreadExit(10); + + Mutex::lockMutex(mMutex); + test(mDoneCount == csmThreadCount, "Hmm - all threads reported done, but we didn't get the expected count."); + Mutex::unlockMutex(mMutex); + + // Kill the mutex. + Mutex::destroyMutex(mMutex); + } +}; \ No newline at end of file diff --git a/Engine/source/platform/test/testTimeManager.cpp b/Engine/source/platform/test/testTimeManager.cpp new file mode 100644 index 000000000..7b157035c --- /dev/null +++ b/Engine/source/platform/test/testTimeManager.cpp @@ -0,0 +1,105 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "platform/platformTimer.h" +#include "core/util/journal/journaledSignal.h" +#include "core/util/journal/process.h" +#include "math/mMath.h" +#include "console/console.h" + +#include "unit/test.h" +using namespace UnitTesting; + +CreateUnitTest(Check_advanceTime, "Platform/Time/advanceTime") +{ + void run() + { + U32 time = Platform::getVirtualMilliseconds(); + Platform::advanceTime(10); + U32 newTime = Platform::getVirtualMilliseconds(); + + test(newTime - time == 10, "Platform::advanceTime is borked, we advanced 10ms but didn't get a 10ms delta!"); + } +}; + +CreateUnitTest(Check_platformSleep, "Platform/Time/Sleep") +{ + const static S32 sleepTimeMs = 500; + void run() + { + U32 start = Platform::getRealMilliseconds(); + Platform::sleep(sleepTimeMs); + U32 end = Platform::getRealMilliseconds(); + + test(end - start >= sleepTimeMs, "We didn't sleep at least as long as we requested!"); + } +}; + +CreateUnitTest(Check_timeManager, "Platform/Time/Manager") +{ + void handleTimeEvent(S32 timeDelta) + { + mElapsedTime += timeDelta; + mNumberCalls++; + + if(mElapsedTime >= 1000) + Process::requestShutdown(); + } + + S32 mElapsedTime; + S32 mNumberCalls; + + void run() + { + mElapsedTime = mNumberCalls = 0; + + // Initialize the time manager... + TimeManager time; + time.timeEvent.notify(this, &Check_timeManager::handleTimeEvent); + + // Event loop till at least one second has passed. + const U32 start = Platform::getRealMilliseconds(); + + while(Process::processEvents()) + { + // If we go too long, kill it off... + if(Platform::getRealMilliseconds() - start > 30*1000) + { + test(false, "Terminated process loop due to watchdog, not due to time manager event, after 30 seconds."); + Process::requestShutdown(); + } + } + + const U32 end = Platform::getRealMilliseconds(); + + // Now, confirm we have approximately similar elapsed times. + S32 elapsedRealTime = end - start; + test(mAbs(elapsedRealTime - mElapsedTime) < 50, "Failed to elapse time to within the desired tolerance."); + + test(mNumberCalls > 0, "Somehow got no event callbacks from TimeManager?"); + + Con::printf(" Got %d time events, and elapsed %dms from TimeManager, " + "%dms according to Platform::getRealMilliseconds()", + mNumberCalls, mElapsedTime, elapsedRealTime); + } +}; \ No newline at end of file diff --git a/Engine/source/platform/threads/mutex.h b/Engine/source/platform/threads/mutex.h new file mode 100644 index 000000000..3fecb423a --- /dev/null +++ b/Engine/source/platform/threads/mutex.h @@ -0,0 +1,129 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/types.h" +#include "platform/platformAssert.h" + +#ifndef _PLATFORM_THREADS_MUTEX_H_ +#define _PLATFORM_THREADS_MUTEX_H_ + +// Forward ref used by platform code +struct PlatformMutexData; + +class Mutex +{ +protected: + PlatformMutexData *mData; + +public: + Mutex(); + virtual ~Mutex(); + + virtual bool lock(bool block = true); + virtual void unlock(); + + // Old API so that we don't have to change a load of code + static void *createMutex() + { + Mutex *mutex = new Mutex; + return (void *)mutex; + } + + static void destroyMutex(void *mutex) + { + Mutex *realMutex = reinterpret_cast(mutex); + delete realMutex; + } + + static bool lockMutex(void *mutex, bool block = true) + { + Mutex *realMutex = reinterpret_cast(mutex); + return realMutex->lock(block); + } + + static void unlockMutex(void *mutex) + { + Mutex *realMutex = reinterpret_cast(mutex); + realMutex->unlock(); + } +}; + +/// Helper for simplifying mutex locking code. +/// +/// This class will automatically unlock a mutex that you've +/// locked through it, saving you from managing a lot of complex +/// exit cases. For instance: +/// +/// @code +/// MutexHandle handle; +/// handle.lock(myMutex); +/// +/// if(error1) +/// return; // Auto-unlocked by handle if we leave here - normally would +/// // leave the mutex locked, causing much pain later. +/// +/// handle.unlock(); +/// @endcode +class MutexHandle +{ +private: + void *mMutexPtr; + +public: + MutexHandle() + : mMutexPtr(NULL) + { + } + + ~MutexHandle() + { + if(mMutexPtr) + unlock(); + } + + bool lock(void *mutex, bool blocking=false) + { + AssertFatal(!mMutexPtr, "MutexHandle::lock - shouldn't be locking things twice!"); + + bool ret = Mutex::lockMutex(mutex, blocking); + + if(ret) + { + // We succeeded, do book-keeping. + mMutexPtr = mutex; + } + + return ret; + } + + void unlock() + { + if(mMutexPtr) + { + Mutex::unlockMutex(mMutexPtr); + mMutexPtr = NULL; + } + } + +}; + +#endif // _PLATFORM_THREADS_MUTEX_H_ diff --git a/Engine/source/platform/threads/semaphore.h b/Engine/source/platform/threads/semaphore.h new file mode 100644 index 000000000..2da23f44e --- /dev/null +++ b/Engine/source/platform/threads/semaphore.h @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PLATFORM_THREAD_SEMAPHORE_H_ +#define _PLATFORM_THREAD_SEMAPHORE_H_ + +#ifndef _TORQUE_TYPES_H_ +#include "platform/types.h" +#endif + +// Forward ref used by platform code +class PlatformSemaphore; + +class Semaphore +{ +protected: + PlatformSemaphore *mData; + +public: + /// Create a semaphore. initialCount defaults to 1. + Semaphore(S32 initialCount = 1); + /// Delete a semaphore, ignoring it's count. + ~Semaphore(); + + /// Acquire the semaphore, decrementing its count. + /// if the initial count is less than 1, block until it goes above 1, then acquire. + /// Returns true if the semaphore was acquired, false if the semaphore could + /// not be acquired and block was false. + bool acquire(bool block = true, S32 timeoutMS = -1); + + /// Release the semaphore, incrementing its count. + /// Never blocks. + void release(); +}; + +#endif diff --git a/Engine/source/platform/threads/thread.h b/Engine/source/platform/threads/thread.h new file mode 100644 index 000000000..0ae5a12b7 --- /dev/null +++ b/Engine/source/platform/threads/thread.h @@ -0,0 +1,245 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PLATFORM_THREADS_THREAD_H_ +#define _PLATFORM_THREADS_THREAD_H_ + +#ifndef _TORQUE_TYPES_H_ + #include "platform/types.h" +#endif +#ifndef _TVECTOR_H_ + #include "core/util/tVector.h" +#endif +#ifndef _PLATFORM_THREADS_MUTEX_H_ + #include "platform/threads/mutex.h" +#endif +#ifndef _TSINGLETON_H_ + #include "core/util/tSingleton.h" +#endif + + +// Forward ref used by platform code +class PlatformThreadData; + + +// Typedefs +typedef void (*ThreadRunFunction)(void *data); + +class Thread +{ +public: + typedef void Parent; + +protected: + PlatformThreadData* mData; + + /// Used to signal threads need to stop. + /// Threads set this flag to false in start() + U32 shouldStop; + + /// Set the name of this thread for identification in debuggers. + /// Maybe a NOP on platforms that do not support this. Always a NOP + /// in non-debug builds. + void _setName( const char* name ); + +public: + /// If set, the thread will delete itself once it has finished running. + bool autoDelete; + + /// Create a thread. + /// @param func The starting function for the thread. + /// @param arg Data to be passed to func, when the thread starts. + /// @param start_thread Supported for compatibility. Must be false. Starting threads from + /// within the constructor is not allowed anymore as the run() method is virtual. + Thread(ThreadRunFunction func = 0, void *arg = 0, bool start_thread = false, bool autodelete = false); + + /// Destroy a thread. + /// The thread MUST be allowed to exit before it is destroyed. + virtual ~Thread(); + + /// Start a thread. + /// Sets shouldStop to false and calls run() in a new thread of execution. + void start( void* arg = 0 ); + + /// Ask a thread to stop running. + void stop() + { + shouldStop = true; + } + + /// Block until the thread stops running. + /// @note Don't use this in combination with auto-deletion as otherwise the thread will kill + /// itself while still executing the join() method on the waiting thread. + bool join(); + + /// Threads may call checkForStop() periodically to check if they've been + /// asked to stop. As soon as checkForStop() returns true, the thread should + /// clean up and return. + bool checkForStop() + { + return shouldStop; + } + + /// Run the Thread's entry point function. + /// Override this method in a subclass of Thread to create threaded code in + /// an object oriented way, and without passing a function ptr to Thread(). + /// Also, you can call this method directly to execute the thread's + /// code in a non-threaded way. + virtual void run(void *arg = 0); + + /// Returns true if the thread is running. + bool isAlive(); + + /// Returns the platform specific thread id for this thread. + U32 getId(); +}; + + +/// +class ThreadManager +{ + Vector threadPool; + Mutex poolLock; + + struct MainThreadId + { + U32 mId; + MainThreadId() + { + mId = ThreadManager::getCurrentThreadId(); + } + U32 get() + { + // Okay, this is a bit soso. The main thread ID may get queried during + // global ctor phase before MainThreadId's ctor ran. Since global + // ctors will/should all run on the main thread, we can sort of safely + // assume here that we can just query the current thread's ID. + + if( !mId ) + mId = ThreadManager::getCurrentThreadId(); + return mId; + } + }; + + static MainThreadId smMainThreadId; + +public: + ThreadManager() + { + VECTOR_SET_ASSOCIATION( threadPool ); + } + + /// Return true if the caller is running on the main thread. + static bool isMainThread(); + + /// Returns true if threadId is the same as the calling thread's id. + static bool isCurrentThread(U32 threadId); + + /// Returns true if the 2 thread ids represent the same thread. Some thread + /// APIs return an opaque object as a thread id, so the == operator cannot + /// reliably compare thread ids. + // this comparator is needed by pthreads and ThreadManager. + static bool compare(U32 threadId_1, U32 threadId_2); + + /// Returns the platform specific thread id of the calling thread. Some + /// platforms do not guarantee that this ID stays the same over the life of + /// the thread, so use ThreadManager::compare() to compare thread ids. + static U32 getCurrentThreadId(); + + /// Returns the platform specific thread id ot the main thread. + static U32 getMainThreadId() { return smMainThreadId.get(); } + + /// Each thread should add itself to the thread pool the first time it runs. + static void addThread(Thread* thread) + { + ThreadManager &manager = *ManagedSingleton< ThreadManager >::instance(); + manager.poolLock.lock(); + Thread *alreadyAdded = getThreadById(thread->getId()); + if(!alreadyAdded) + manager.threadPool.push_back(thread); + manager.poolLock.unlock(); + } + + static void removeThread(Thread* thread) + { + ThreadManager &manager = *ManagedSingleton< ThreadManager >::instance(); + manager.poolLock.lock(); + + U32 threadID = thread->getId(); + for(U32 i = 0;i < manager.threadPool.size();++i) + { + if( compare( manager.threadPool[i]->getId(), threadID ) ) + { + manager.threadPool.erase(i); + break; + } + } + + manager.poolLock.unlock(); + } + + /// Searches the pool of known threads for a thread whose id is equivalent to + /// the given threadid. Compares thread ids with ThreadManager::compare(). + static Thread* getThreadById(U32 threadid) + { + AssertFatal(threadid != 0, "ThreadManager::getThreadById() Searching for a bad thread id."); + Thread* ret = NULL; + + ThreadManager &manager = *ManagedSingleton< ThreadManager >::instance(); + manager.poolLock.lock(); + Vector &pool = manager.threadPool; + for( S32 i = pool.size() - 1; i >= 0; i--) + { + Thread* p = pool[i]; + if(compare(p->getId(), threadid)) + { + ret = p; + break; + } + } + manager.poolLock.unlock(); + return ret; + } + + static Thread* getCurrentThread() + { + return getThreadById(ThreadManager::getCurrentThreadId()); + } + + static const char* getSingletonName() + { + return "ThreadManager"; + } +}; + +inline bool ThreadManager::isMainThread() +{ + return compare( ThreadManager::getCurrentThreadId(), smMainThreadId.get() ); +} + +inline bool ThreadManager::isCurrentThread(U32 threadId) +{ + U32 current = getCurrentThreadId(); + return compare(current, threadId); +} + +#endif // _PLATFORM_THREADS_THREAD_H_ diff --git a/Engine/source/platform/threads/threadPool.cpp b/Engine/source/platform/threads/threadPool.cpp new file mode 100644 index 000000000..b0a34434d --- /dev/null +++ b/Engine/source/platform/threads/threadPool.cpp @@ -0,0 +1,479 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/threads/threadPool.h" +#include "platform/threads/thread.h" +#include "platform/platformCPUCount.h" +#include "core/strings/stringFunctions.h" +#include "core/util/tSingleton.h" + + +//#define DEBUG_SPEW + + +//============================================================================= +// ThreadPool::Context. +//============================================================================= + +ThreadPool::Context ThreadPool::Context::smRootContext( "ROOT", NULL, 1.0 ); + +//-------------------------------------------------------------------------- + +ThreadPool::Context::Context( const char* name, ThreadPool::Context* parent, F32 priorityBias ) + : mName( name ), + mParent( parent ), + mSibling( 0 ), + mChildren( 0 ), + mPriorityBias( priorityBias ), + mAccumulatedPriorityBias( 0.0 ) +{ + if( parent ) + { + mSibling = mParent->mChildren; + mParent->mChildren = this; + } +} + +//-------------------------------------------------------------------------- + +ThreadPool::Context::~Context() +{ + if( mParent ) + for( Context* context = mParent->mChildren, *prev = 0; context != 0; prev = context, context = context->mSibling ) + if( context == this ) + { + if( !prev ) + mParent->mChildren = this->mSibling; + else + prev->mSibling = this->mSibling; + } +} + +//-------------------------------------------------------------------------- + +ThreadPool::Context* ThreadPool::Context::getChild( const char* name ) +{ + for( Context* child = getChildren(); child != 0; child = child->getSibling() ) + if( dStricmp( child->getName(), name ) == 0 ) + return child; + return 0; +} + +//-------------------------------------------------------------------------- + +F32 ThreadPool::Context::getAccumulatedPriorityBias() +{ + if( !mAccumulatedPriorityBias ) + updateAccumulatedPriorityBiases(); + return mAccumulatedPriorityBias; +} + +//-------------------------------------------------------------------------- + +void ThreadPool::Context::setPriorityBias( F32 value ) +{ + mPriorityBias = value; + mAccumulatedPriorityBias = 0.0; +} + +//-------------------------------------------------------------------------- + +void ThreadPool::Context::updateAccumulatedPriorityBiases() +{ + // Update our own priority bias. + + mAccumulatedPriorityBias = mPriorityBias; + for( Context* context = getParent(); context != 0; context = context->getParent() ) + mAccumulatedPriorityBias *= context->getPriorityBias(); + + // Update our children. + + for( Context* child = getChildren(); child != 0; child = child->getSibling() ) + child->updateAccumulatedPriorityBiases(); +} + +//============================================================================= +// ThreadPool::WorkItem. +//============================================================================= + +//-------------------------------------------------------------------------- + +void ThreadPool::WorkItem::process() +{ + execute(); +} + +//-------------------------------------------------------------------------- + +bool ThreadPool::WorkItem::isCancellationRequested() +{ + return false; +} + +//-------------------------------------------------------------------------- + +bool ThreadPool::WorkItem::cancellationPoint() +{ + if( isCancellationRequested() ) + { + onCancelled(); + return true; + } + else + return false; +} + +//-------------------------------------------------------------------------- + +F32 ThreadPool::WorkItem::getPriority() +{ + return 1.0; +} + +//============================================================================= +// ThreadPool::WorkItemWrapper. +//============================================================================= + +/// Value wrapper for work items while placed on priority queue. +/// Conforms to interface dictated by ThreadSafePriorityQueueWithUpdate. +/// +/// @see ThreadSafePriorityQueueWithUpdate +/// @see ThreadPool::WorkItem +/// +struct ThreadPool::WorkItemWrapper : public ThreadSafeRef< WorkItem > +{ + typedef ThreadSafeRef< WorkItem > Parent; + + WorkItemWrapper() {} + WorkItemWrapper( WorkItem* item ) + : Parent( item ) {} + + bool isAlive(); + F32 getPriority(); +}; + +inline bool ThreadPool::WorkItemWrapper::isAlive() +{ + WorkItem* item = ptr(); + if( !item ) + return false; + else if( item->isCancellationRequested() ) + { + ( *this ) = 0; + return false; + } + else + return true; +} + +inline F32 ThreadPool::WorkItemWrapper::getPriority() +{ + WorkItem* item = ptr(); + AssertFatal( item != 0, "ThreadPool::WorkItemWrapper::getPriority - called on dead item" ); + + // Compute a scaled priority value based on the item's context. + return ( item->getContext()->getAccumulatedPriorityBias() * item->getPriority() ); +} + +//============================================================================= +// ThreadPool::WorkerThread. +//============================================================================= + +/// +/// +struct ThreadPool::WorkerThread : public Thread +{ + WorkerThread( ThreadPool* pool, U32 index ); + + WorkerThread* getNext(); + virtual void run( void* arg = 0 ); + +private: + U32 mIndex; + ThreadPool* mPool; + WorkerThread* mNext; +}; + +ThreadPool::WorkerThread::WorkerThread( ThreadPool* pool, U32 index ) + : mPool( pool ), + mIndex( index ) +{ + // Link us to the pool's thread list. + + mNext = pool->mThreads; + pool->mThreads = this; +} + +inline ThreadPool::WorkerThread* ThreadPool::WorkerThread::getNext() +{ + return mNext; +} + +void ThreadPool::WorkerThread::run( void* arg ) +{ + #ifdef TORQUE_DEBUG + { + // Set the thread's name for debugging. + char buffer[ 2048 ]; + dSprintf( buffer, sizeof( buffer ), "ThreadPool(%s) WorkerThread %i", mPool->mName.c_str(), mIndex ); + _setName( buffer ); + } + #endif + +#if defined(TORQUE_OS_XENON) + // On Xbox 360 you must explicitly assign software threads to hardware threads. + + // This will distribute job threads across the secondary CPUs leaving both + // primary CPU cores available to the "main" thread. This will help prevent + // more L2 thrashing of the main thread/core. + static U32 sCoreAssignment = 2; + XSetThreadProcessor( GetCurrentThread(), sCoreAssignment ); + sCoreAssignment = sCoreAssignment < 6 ? sCoreAssignment + 1 : 2; +#endif + + while( 1 ) + { + if( checkForStop() ) + { +#ifdef DEBUG_SPEW + Platform::outputDebugString( "[ThreadPool::WorkerThread] thread '%i' exits", getId() ); +#endif + dFetchAndAdd( mPool->mNumThreads, ( U32 ) -1 ); + return; + } + + // Mark us as potentially blocking. + dFetchAndAdd( mPool->mNumThreadsReady, ( U32 ) -1 ); + + bool waitForSignal = false; + { + // Try to take an item from the queue. Do + // this in a separate block, so we'll be + // releasing the item after we have finished. + + WorkItemWrapper workItem; + if( mPool->mWorkItemQueue.takeNext( workItem ) ) + { + // Mark us as non-blocking as this loop definitely + // won't wait on the semaphore. + dFetchAndAdd( mPool->mNumThreadsReady, 1 ); + +#ifdef DEBUG_SPEW + Platform::outputDebugString( "[ThreadPool::WorkerThread] thread '%i' takes item '0x%x'", getId(), *workItem ); +#endif + workItem->process(); + } + else + waitForSignal = true; + } + + if( waitForSignal ) + { + dFetchAndAdd( mPool->mNumThreadsAwake, ( U32 ) -1 ); + +#ifdef DEBUG_SPEW + Platform::outputDebugString( "[ThreadPool::WorkerThread] thread '%i' going to sleep", getId() ); +#endif + mPool->mSemaphore.acquire(); +#ifdef DEBUG_SPEW + Platform::outputDebugString( "[ThreadPool::WorkerThread] thread '%i' waking up", getId() ); +#endif + + dFetchAndAdd( mPool->mNumThreadsAwake, 1 ); + dFetchAndAdd( mPool->mNumThreadsReady, 1 ); + } + } +} + +//============================================================================= +// ThreadPool. +//============================================================================= + +bool ThreadPool::smForceAllMainThread; +U32 ThreadPool::smMainThreadTimeMS; +ThreadPool::QueueType ThreadPool::smMainThreadQueue; + +//-------------------------------------------------------------------------- + +ThreadPool::ThreadPool( const char* name, U32 numThreads ) + : mName( name ), + mNumThreads( numThreads ), + mNumThreadsAwake( 0 ), + mThreads( 0 ), + mSemaphore( 0 ) +{ + // Number of worker threads to create. + + if( !mNumThreads ) + { + // Use platformCPUInfo directly as in the case of the global pool, + // Platform::SystemInfo will not yet have been initialized. + + U32 numLogical; + U32 numPhysical; + U32 numCores; + + CPUInfo::CPUCount( numLogical, numCores, numPhysical ); + + const U32 baseCount = getMax( numLogical, numCores ); + if( baseCount ) + mNumThreads = baseCount; + else + mNumThreads = 2; + } + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[ThreadPool] spawning %i threads", mNumThreads ); + #endif + + // Create the threads. + + mNumThreadsAwake = mNumThreads; + mNumThreadsReady = mNumThreads; + for( U32 i = 0; i < mNumThreads; i ++ ) + { + WorkerThread* thread = new WorkerThread( this, i ); + thread->start(); + } +} + +//-------------------------------------------------------------------------- + +ThreadPool::~ThreadPool() +{ + shutdown(); +} + +//-------------------------------------------------------------------------- + +void ThreadPool::shutdown() +{ + const U32 numThreads = mNumThreads; + + // Tell our worker threads to stop. + + for( WorkerThread* thread = mThreads; thread != 0; thread = thread->getNext() ) + thread->stop(); + + // Release the semaphore as many times as there are threads. + // Doing this separately guarantees we're not waking a thread + // that hasn't been set its stop flag yet. + + for( U32 n = 0; n < numThreads; ++ n ) + mSemaphore.release(); + + // Delete each worker thread. Wait until death as we're prone to + // running into issues with decomposing work item lists otherwise. + + for( WorkerThread* thread = mThreads; thread != 0; ) + { + WorkerThread* next = thread->getNext(); + thread->join(); + delete thread; + thread = next; + } + + mThreads = NULL; + mNumThreads = 0; +} + +//-------------------------------------------------------------------------- + +void ThreadPool::queueWorkItem( WorkItem* item ) +{ + bool executeRightAway = ( getForceAllMainThread() ); +#ifdef DEBUG_SPEW + Platform::outputDebugString( "[ThreadPool] %s work item '0x%x'", + ( executeRightAway ? "executing" : "queuing" ), + item ); +#endif + + if( executeRightAway ) + item->process(); + else + { + // Put the item in the queue. + + mWorkItemQueue.insert( item->getPriority(), item ); + + // Wake up some thread, if we need to. + // Use the ready count here as the wake count does + // not correctly protect the critical section in the + // thread's run function. This may lead us to release + // the semaphore more often than necessary, but it avoids + // a race condition. + + if( !dCompareAndSwap( mNumThreadsReady, mNumThreads, mNumThreads ) ) + mSemaphore.release(); + } +} + +//-------------------------------------------------------------------------- + +void ThreadPool::flushWorkItems( S32 timeOut ) +{ + AssertFatal( mNumThreads, "ThreadPool::flushWorkItems() - no worker threads in pool" ); + + U32 endTime = 0; + if( timeOut != -1 ) + endTime = Platform::getRealMilliseconds() + timeOut; + + // Spinlock until the queue is empty. + + while( !mWorkItemQueue.isEmpty() ) + { + Platform::sleep( 25 ); + + // Stop if we have exceeded our processing time budget. + + if( timeOut != -1 + && Platform::getRealMilliseconds() >= endTime ) + break; + } +} + +//-------------------------------------------------------------------------- + +void ThreadPool::queueWorkItemOnMainThread( WorkItem* item ) +{ + smMainThreadQueue.insert( item->getPriority(), item ); +} + +//-------------------------------------------------------------------------- + +void ThreadPool::processMainThreadWorkItems() +{ + AssertFatal( ThreadManager::isMainThread(), + "ThreadPool::processMainThreadWorkItems - this function must only be called on the main thread" ); + + U32 timeLimit = ( Platform::getRealMilliseconds() + getMainThreadThresholdTimeMS() ); + + do + { + WorkItemWrapper item; + if( !smMainThreadQueue.takeNext( item ) ) + break; + else + item->process(); + } + while( Platform::getRealMilliseconds() < timeLimit ); +} diff --git a/Engine/source/platform/threads/threadPool.h b/Engine/source/platform/threads/threadPool.h new file mode 100644 index 000000000..2f18a5bee --- /dev/null +++ b/Engine/source/platform/threads/threadPool.h @@ -0,0 +1,398 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _THREADPOOL_H_ +#define _THREADPOOL_H_ + +#ifndef _THREADSAFEREFCOUNT_H_ + #include "platform/threads/threadSafeRefCount.h" +#endif +#ifndef _THREADSAFEPRIORITYQUEUE_H_ + #include "platform/threads/threadSafePriorityQueue.h" +#endif +#ifndef _PLATFORM_THREAD_SEMAPHORE_H_ + #include "platform/threads/semaphore.h" +#endif +#ifndef _TSINGLETON_H_ + #include "core/util/tSingleton.h" +#endif + + +/// @file +/// Interface for an asynchronous work manager. + + +/// Asynchronous work manager. +/// +/// Thread pooling allows to submit work items for background execution. +/// Each work item will be placed on a queue and, based on a total priority +/// ordering, executed when it has the highest priority and a worker thread +/// becomes available. +/// +/// @note The global pool maintains the invariant that only the main thread +/// may submit items in order to be able to flush the item queue reliably +/// from the main thread itself. If other threads were issuing items to +/// the queue, the queue may never empty out and the main thread will +/// deadlock. +/// +/// Flushing is the simplest method to guarantee that no asynchronous +/// operation is pending in a specific case (deletion of the target object +/// being the most common case). However, when possible, avoid this +/// situation and design your work items to operate independently, +/// e.g. by having only a single point of access to data that may have +/// disappeared in the meantime and putting a check around that single +/// access so that the item will silently die when its target object has +/// disappeared. +/// +/// The cleanest safe solution to this is to create a separate concurrently +/// reference-counted structure that holds all interfacing state and +/// functionality shared between a work item and its issueing code. This way +/// the host object can safely disappear with the interfacing structure +/// automatically being released once the last concurrent work item has been +/// processed or discarded. +/// +class ThreadPool +{ + public: + + /// A ThreadPool context defines a logical context in which WorkItems are + /// being executed. Their primary use is for biasing priorities of + /// WorkItems. + /// + /// Contexts are arranged in a tree hierarchy. Each parent node's priority + /// bias scales all the priority biases underneath it. + /// + /// Note that instances of this class are meant to be instantiated + /// globally only. + /// + class Context + { + protected: + + /// Superordinate context; scales this context's priority bias. + Context* mParent; + + /// First child. + Context* mChildren; + + /// Next sibling in child chain. + Context* mSibling; + + /// Name of this context. Should be unique in parent namespace. + const char* mName; + + /// Priority scale factor of this context. + F32 mPriorityBias; + + /// Accumulated scale factor. + F32 mAccumulatedPriorityBias; + + /// The root context; does not modify priorities. All contexts should be direct or indirect children of this one. + static Context smRootContext; + + /// Recursively update cached accumulated priority biases. + void updateAccumulatedPriorityBiases(); + + public: + + Context( const char* name, Context* parent, F32 priorityBias ); + ~Context(); + + /// Return the name of the worker threading context. + const char* getName() const + { + return mName; + } + + /// Return the context's own work item priority bias. + F32 getPriorityBias() const + { + return mPriorityBias; + } + + /// Return the superordinate node to the current context. + Context* getParent() const + { + return mParent; + } + + /// Return the next sibling to the current context. + Context* getSibling() const + { + return mSibling; + } + + /// Return the first child context. + Context* getChildren() const + { + return mChildren; + } + + /// Return the root context. + static Context* ROOT_CONTEXT() + { + return &smRootContext; + } + + /// + F32 getAccumulatedPriorityBias(); + + /// + Context* getChild( const char* name ); + + /// + void setPriorityBias( F32 value ); + }; + + /// An action to execute on a worker thread from the pool. + /// + /// Work items are concurrently reference-counted and will be + /// automatically released once the last reference disappears. + /// + class WorkItem : public ThreadSafeRefCount< WorkItem > + { + public: + + typedef ThreadSafeRefCount< WorkItem > Parent; + + protected: + + /// The work context of this item. + Context* mContext; + + /// Mark a point in a work item's execution where the item can + /// be safely cancelled. + /// + /// This method should be called by subclasses' execute() methods + /// whenever an item can be safely cancelled. When it returns true, + /// the work item should exit from its execute() method. + bool cancellationPoint(); + + /// Called when the item has been cancelled. + virtual void onCancelled() {} + + /// Execute the actions associated with this work item. + /// This is the primary function to implement by subclasses. + virtual void execute() = 0; + + public: + + /// Construct a new work item. + /// + /// @param context The work context in which the item should be placed. + /// If NULL, the root context will be used. + WorkItem( Context* context = 0 ) + : mContext( context ? context : Context::ROOT_CONTEXT() ) + { + } + + virtual ~WorkItem() {} + + /// Return the work context associated with the work item. + inline Context* getContext() const + { + return mContext; + } + + /// Process the work item. + void process(); + + /// Return true if the work item should be cancelled. + /// + /// This method can be overridden by subclasses. It's value will be + /// checked each time cancellationPoint() is called. When it returns + /// true, the item's process() method will exit automatically. + /// + /// @return true, if item should be cancelled; default is false. + /// @see ThreadPool::WorkItem::cancellationPoint + virtual bool isCancellationRequested(); + + /// Return the item's base priority value. + /// @return item priority; defaults to 1.0. + virtual F32 getPriority(); + }; + + typedef ThreadSafeRef< WorkItem > WorkItemPtr; + struct GlobalThreadPool; + + protected: + + struct WorkItemWrapper; + struct WorkerThread; + + friend struct WorkerThread; // mSemaphore, mNumThreadsAwake, mThreads + + typedef ThreadSafePriorityQueueWithUpdate< WorkItemWrapper, F32 > QueueType; + + /// Name of this pool. Mainly for debugging. Used to name worker threads. + String mName; + + /// Number of worker threads spawned by the pool. + U32 mNumThreads; + + /// Number of worker threads in non-sleeping state. + U32 mNumThreadsAwake; + + /// Number of worker threads guaranteed to be non-blocking. + U32 mNumThreadsReady; + + /// Semaphore used to wake up threads, if necessary. + Semaphore mSemaphore; + + /// Threaded priority queue for concurrent access by worker threads. + QueueType mWorkItemQueue; + + /// List of worker threads. + WorkerThread* mThreads; + + /// Force all work items to execute on main thread; + /// turns this into a single-threaded system. + /// Primarily useful to find whether malfunctions are caused + /// by parallel execution or not. + static bool smForceAllMainThread; + + /// + static U32 smMainThreadTimeMS; + + /// Work queue for main thread; can be used to ping back work items to + /// main thread that need processing that can only happen on main thread. + static QueueType smMainThreadQueue; + + public: + + /// Create a new thread pool with the given number of worker threads. + /// + /// If numThreads is zero (the default), the number of threads created + /// will be based on the number of CPU cores available. + /// + /// @param numThreads Number of threads to create or zero for default. + ThreadPool( const char* name, U32 numThreads = 0 ); + + ~ThreadPool(); + + /// Manually shutdown threads outside of static destructors. + void shutdown(); + + /// + void queueWorkItem( WorkItem* item ); + + /// + /// For the global pool, it is very important to only ever call + /// this function on the main thread and to let work items only ever + /// come from the main thread. Otherwise this function has the potential + /// of dead-locking as new work items may constantly be fed to the queue + /// without it ever getting empty. + /// + /// @param timeOut Soft limit on the number of milliseconds to wait for + /// the queue to flush out. -1 = infinite. + void flushWorkItems( S32 timeOut = -1 ); + + /// Add a work item to the main thread's work queue. + /// + /// The main thread's work queue will be processed each frame using + /// a set timeout to limit the work being done. Nonetheless, work + /// items will not be suspended in-midst of processing, so make sure + /// that whatever work you issue to the main thread is light work + /// or you may see short hangs in gameplay. + /// + /// To reiterate this: any code executed through this interface directly + /// adds to frame processing time on the main thread. + /// + /// This method *may* (and is meant to) be called from threads + /// other than the main thread. + static void queueWorkItemOnMainThread( WorkItem* item ); + + /// Process work items waiting on the main thread's work queue. + /// + /// There is a soft limit imposed on the time this method is allowed + /// to run so as to balance frame-to-frame load. However, work + /// items, once their processing is initiated, will not be suspended + /// and will run for as long as they take to complete, so make sure + /// individual items perform as little work as necessary. + /// + /// @see ThreadPool::getMainThreadThesholdTimeMS + static void processMainThreadWorkItems(); + + /// Return the interval in which item priorities are updated on the queue. + /// @return update interval in milliseconds. + U32 getQueueUpdateInterval() const + { + return mWorkItemQueue.getUpdateInterval(); + } + + /// Return the priority increment applied to work items on each passing of the update interval. + F32 getQueueTimeBasedPriorityBoost() const + { + return mWorkItemQueue.getTimeBasedPriorityBoost(); + } + + /// Set the update interval of the work item queue to the given value. + /// @param milliSeconds Time between updates in milliseconds. + void setQueueUpdateInterval( U32 milliSeconds ) + { + mWorkItemQueue.setUpdateInterval( milliSeconds ); + } + + /// Set the priority increment applied to work items on each update interval. + /// @param value Priority increment. Set to zero to deactivate. + void setQueueTimeBasedPriorityBoost( F32 value ) + { + mWorkItemQueue.setTimeBasedPriorityBoost( value ); + } + + /// + static U32& getMainThreadThresholdTimeMS() + { + return smMainThreadTimeMS; + } + + /// + static bool& getForceAllMainThread() + { + return smForceAllMainThread; + } + + /// Return the global thread pool singleton. + static ThreadPool& GLOBAL(); +}; + +typedef ThreadPool::Context ThreadContext; +typedef ThreadPool::WorkItem ThreadWorkItem; + + +struct ThreadPool::GlobalThreadPool : public ThreadPool, public ManagedSingleton< GlobalThreadPool > +{ + typedef ThreadPool Parent; + + GlobalThreadPool() + : Parent( "GLOBAL" ) {} + + // For ManagedSingleton. + static const char* getSingletonName() { return "GlobalThreadPool"; } +}; + +inline ThreadPool& ThreadPool::GLOBAL() +{ + return *( GlobalThreadPool::instance() ); +} + +#endif // !_THREADPOOL_H_ diff --git a/Engine/source/platform/threads/threadPoolAsyncIO.h b/Engine/source/platform/threads/threadPoolAsyncIO.h new file mode 100644 index 000000000..0d7c38104 --- /dev/null +++ b/Engine/source/platform/threads/threadPoolAsyncIO.h @@ -0,0 +1,357 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _THREADPOOLASYNCIO_H_ +#define _THREADPOOLASYNCIO_H_ + +#ifndef _THREADPOOL_H_ +# include "platform/threads/threadPool.h" +#endif +#ifndef _RAWDATA_H_ +# include "core/util/rawData.h" +#endif +#ifndef _TSTREAM_H_ +# include "core/stream/tStream.h" +#endif + + +//RDTODO: I/O error handling + +/// @file +/// Thread pool work items for asynchronous stream I/O. +/// Through the use of stream filters, this can be basically used for any +/// type of asynchronous stream processing. + +//-------------------------------------------------------------------------- +// AsyncIOItem. +//-------------------------------------------------------------------------- + +/// Abstract superclass of async I/O work items. +/// +/// Supports both offset-based stream I/O as well as I/O on streams with +/// implicit positions. Note that if you use the latter type, make sure +/// that no other thread is messing with the stream at the same time or +/// chaos will ensue. +/// +/// @param T Type of elements being streamed. +template< typename T, class Stream > +class AsyncIOItem : public ThreadPool::WorkItem +{ + public: + + typedef WorkItem Parent; + typedef T ValueType; + typedef RawDataT< ValueType > BufferType; + typedef U32 OffsetType; + typedef Stream StreamType; + + protected: + + /// Buffer keeping/receiving the data elements. + BufferType mBuffer; + + /// The stream to read from/write to. + StreamType* mStream; + + /// Number of elements to read from/write to the stream. + U32 mNumElements; + + /// Offset in "mBuffer" from where to read/where to start writing to. + U32 mOffsetInBuffer; + + /// Offset in stream from where to read/where to write to. + /// @note This is only meaningful if the stream is an offset I/O + /// stream. For a stream that is can do both types of I/O, + /// explicit offsets are preferred and this value is used. + OffsetType mOffsetInStream; + + /// + ValueType* getBufferPtr() + { + return &getBuffer().data[ getOffsetInBuffer() ]; + } + + public: + + /// + /// If the stream uses implicit positioning, then the supplied "offsetInStream" + /// is meaningless and ignored. + AsyncIOItem( StreamType* stream, U32 numElements, OffsetType offsetInStream, + ThreadContext* context = 0 ) + : Parent( context ), + mStream( stream ), + mNumElements( numElements ), + mOffsetInStream( offsetInStream ), + mOffsetInBuffer( 0 ) {} + + /// Construct a read item on "stream" that stores data into the given "buffer". + /// + AsyncIOItem( StreamType* stream, BufferType& buffer, U32 offsetInBuffer, + U32 numElements, OffsetType offsetInStream, bool takeOwnershipOfBuffer = true, + ThreadContext* context = 0 ) + : Parent( context ), + mStream( stream ), + mBuffer( buffer ), + mNumElements( numElements ), + mOffsetInStream( offsetInStream ), + mOffsetInBuffer( offsetInBuffer ) + { + if( takeOwnershipOfBuffer ) + mBuffer.ownMemory = true; + } + + /// Return the stream being written to/read from. + StreamType* getStream() + { + return mStream; + } + + /// Return the data buffer being written to/read from. + /// @note This may not yet have been allocated. + BufferType& getBuffer() + { + return mBuffer; + + } + + /// Return the number of elements involved in the transfer. + U32 getNumElements() + { + return mNumElements; + } + + /// Return the position in the data buffer at which to start the transfer. + U32 getOffsetInBuffer() + { + return mOffsetInBuffer; + } + + /// Return the position in the stream at which to start the transfer. + /// @note Only meaningful for streams that support offset I/O. + OffsetType getOffsetInStream() + { + return mOffsetInStream; + } +}; + +//-------------------------------------------------------------------------- +// AsyncReadItem. +//-------------------------------------------------------------------------- + +//RDTODO: error handling +/// Work item to asynchronously read from a stream. +/// +/// The given stream type may implement any of the input stream +/// interfaces. Preference is given to IAsyncInputStream, then to +/// IOffsetInputStream, and only if none of these are implemented +/// IInputStream is used. +/// +/// For IAsyncInputStreams, the async read operation is issued immediately +/// on the constructing thread and then picked up on the worker thread. +/// This ensures optimal use of concurrency. + +template< typename T, class Stream = IOffsetInputStream< T > > +class AsyncReadItem : public AsyncIOItem< T, Stream > +{ + public: + + typedef AsyncIOItem< T, Stream > Parent; + typedef typename Parent::StreamType StreamType; + typedef typename Parent::OffsetType OffsetType; + typedef typename Parent::BufferType BufferType; + typedef typename Parent::ValueType ValueType; + + /// Construct a read item that reads "numElements" at "offsetInStream" + /// from "stream". + /// + /// Since with this constructor no data buffer is supplied, it will be + /// dynamically allocated by the read() method. Note that this only makes + /// sense if this class is subclassed and processing is done on the buffer + /// after it has been read. + /// + /// @param stream The stream to read from. + /// @param numElement The number of elements to read from the stream. + /// @param offsetInStream The offset at which to read from the stream; + /// ignored if the stream uses implicit positioning + /// @param context The tread pool context to place the item into. + AsyncReadItem( StreamType* stream, U32 numElements, OffsetType offsetInStream, + ThreadContext* context = 0 ) + : Parent( stream, numElements, offsetInStream, context ) + { + _prep(); + } + + AsyncReadItem( StreamType* stream, U32 numElements, OffsetType offsetInStream, + BufferType& buffer, bool takeOwnershipOfBuffer = false, + U32 offsetInBuffer = 0, ThreadContext* context = 0 ) + : Parent( stream, buffer, offsetInBuffer, numElements, offsetInStream, takeOwnershipOfBuffer, context ) + { + _prep(); + } + + /// @return The number of elements actually read from the stream. + U32 getNumElementsRead() + { + return mNumElementsRead; + } + + protected: + + /// Handle of asynchronous stream read, if we are using an async interface. + void* mAsyncHandle; + + /// After the read operation has completed, this holds the number of + /// elements actually read from the stream. + U32 mNumElementsRead; + + virtual void execute(); + + void _allocBuffer() + { + if( !this->getBuffer().data ) + this->getBuffer().alloc( this->getNumElements() ); + } + + void _prep() + { + IAsyncInputStream< T >* s = dynamic_cast< IAsyncInputStream< T >* >( this->getStream() ); + if( s ) + { + _allocBuffer(); + mAsyncHandle = s->issueReadAt( this->getOffsetInStream(), this->getBufferPtr(), this->getNumElements() ); + } + } + + // Helper functions to differentiate between stream types. + + void _read( IInputStream< T >* stream ) + { + mNumElementsRead = stream->read( this->getBufferPtr(), this->getNumElements() ); + } + void _read( IOffsetInputStream< T >* stream ) + { + mNumElementsRead = stream->readAt( this->getOffsetInStream(), this->getBufferPtr(), this->getNumElements() ); + } + void _read( IAsyncInputStream< T >* stream ) + { + stream->tryCompleteReadAt( mAsyncHandle, mNumElementsRead, true ); + } +}; + +template< typename T, class Stream > +void AsyncReadItem< T, Stream >::execute() +{ + _allocBuffer(); + + // Read the data. Do a dynamic cast for any of the + // interfaces we prefer. + + if( this->cancellationPoint() ) return; + StreamType* stream = this->getStream(); + if( dynamic_cast< IAsyncInputStream< T >* >( stream ) ) + _read( ( IAsyncInputStream< T >* ) stream ); + else if( dynamic_cast< IOffsetInputStream< T >* >( stream ) ) + _read( ( IOffsetInputStream< T >* ) stream ); + else + _read( stream ); +} + +//-------------------------------------------------------------------------- +// AsyncWriteItem. +//-------------------------------------------------------------------------- + +/// Work item for writing to an output stream. +/// +/// The stream being written to may implement any of the given output stream +/// interfaces. Preference is given to IAsyncOutputStream, then to +/// IOffsetOutputStream, and only if none of these is implemented IOutputStream +/// is used. +/// +/// A useful feature is to yield ownership of the data buffer to the +/// write item. This way, this can be pretty much used in a fire-and-forget +/// manner where after submission, no further synchronization happens +/// between the client and the work item. +/// +/// @note Be aware that if writing to an output stream that has an implicit +/// position property, multiple concurrent writes will interfere with each other. +template< typename T, class Stream = IOffsetOutputStream< T > > +class AsyncWriteItem : public AsyncIOItem< T, Stream > +{ + public: + + typedef AsyncIOItem< T, Stream > Parent; + typedef typename Parent::StreamType StreamType; + typedef typename Parent::OffsetType OffsetType; + typedef typename Parent::BufferType BufferType; + typedef typename Parent::ValueType ValueType; + + AsyncWriteItem( StreamType* stream, U32 numElements, OffsetType offsetInStream, + BufferType& buffer, bool takeOwnershipOfBuffer = true, + U32 offsetInBuffer = 0, ThreadContext* context = 0 ) + : Parent( stream, buffer, offsetInBuffer, numElements, offsetInStream, takeOwnershipOfBuffer, context ) + { + _prep( stream ); + } + + protected: + + /// Handle of asynchronous write operation, if the stream implements IAsyncOutputStream. + void* mAsyncHandle; + + virtual void execute(); + + void _prep( StreamType* stream ) + { + IAsyncOutputStream< T >* s = dynamic_cast< IAsyncOutputStream< T >* >( stream ); + if( s ) + mAsyncHandle = s->issueWriteAt( this->getOffset(), this->getBufferPtr(), this->getNumElements() ); + } + + void _write( IOutputStream< T >* stream ) + { + stream->write( this->getBufferPtr(), this->getNumElements() ); + } + void _write( IOffsetOutputStream< T >* stream ) + { + stream->writeAt( this->getOffsetInStream(), this->getBufferPtr(), this->getNumElements() ); + } + void _write( IAsyncOutputStream< T >* stream ) + { + stream->tryCompleteWriteAt( mAsyncHandle, true ); + } +}; + +template< typename T, class Stream > +void AsyncWriteItem< T, Stream >::execute() +{ + if( this->cancellationPoint() ) return; + + StreamType* stream = this->getStream(); + if( dynamic_cast< IAsyncOutputStream< T >* >( stream ) ) + _write( ( IAsyncOutputStream< T >* ) stream ); + if( dynamic_cast< IOffsetOutputStream< T >* >( stream ) ) + _write( ( IOffsetOutputStream< T >* ) stream ); + else + _write( stream ); +} + +#endif // _THREADPOOLASYNCIO_H_ diff --git a/Engine/source/platform/threads/threadSafeDeque.h b/Engine/source/platform/threads/threadSafeDeque.h new file mode 100644 index 000000000..795511a20 --- /dev/null +++ b/Engine/source/platform/threads/threadSafeDeque.h @@ -0,0 +1,474 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _THREADSAFEDEQUE_H_ +#define _THREADSAFEDEQUE_H_ + +#ifndef _THREADSAFEFREELIST_H_ +# include "platform/threads/threadSafeFreeList.h" +#endif + +#include "platform/tmm_off.h" + + +/// Fast, lock-free double-ended queue for concurrent access. +/// +/// @param T Type of list elements; must have default contructor. +template< typename T > +class ThreadSafeDeque +{ + // Lock-free deques using just single-word atomic writes are + // very tricky as each pointer update must immediately result + // in a fully valid list state. The idea here is to maintain the + // deque's head and tail pointers unreliably but otherwise keep a + // regular double-linked list (since we never insert nodes in the + // middle, single-word writes are all we need). + // + // Deletions are a bit less straightforward and require the threads + // to work cooperatively. Since failure of a pointer update depends + // on the deletion state, the deletion flag has to be encoded into + // the link fields. However, as there are two link fields this creates + // two independent deletion flags for each single node, one on the + // next link and one on the prev link. + // + // This will not lead to a problem, though, as it only becomes relevant + // when there is only a single value in the list which, even if the + // respective node gets both deleted and appended/prepended a new node, + // will result in a valid list state. + + + public: + + typedef T ValueType; + + protected: + + class Node; + class DeleteNode; + typedef ThreadSafeRef< Node > NodeRef; + + /// List node. + class Node : public ThreadSafeFreeListNode< Node, DeleteNode > + { + public: + + friend class DeleteNode; // mFreeList; + typedef typename ThreadSafeDeque< T >::ValueType ValueType; + + /// Thread claim flag. This is to prevent two threads who concurrently + /// do a tryPopFront() and tryPopBack() respectively on a deque with just + /// a single node to both claim and return the same value (which would happen + /// without the flag as otherwise both threads would use two different + /// deletion bits for claiming the node). + U32 mIsClaimed; + + /// Link to the freelist that the node has been + /// allocated from. + ThreadSafeFreeList< Node >& mFreeList; + + /// Value contained in the node. + ValueType mValue; + + /// Reference to next node and deletion bit. + NodeRef mNext; + + /// Reference to previous node and deletion bit. + NodeRef mPrev; + + /// Construct an unlinked node allocated from "freeList". + Node( ThreadSafeFreeList< Node >& freeList, const ValueType& value ) + : mIsClaimed( 0 ), mFreeList( freeList ), mValue( value ) {} + }; + + class DeleteNode + { + public: + template< typename N > + static void destroy( N* ptr ) + { + AssertFatal( ptr->mIsClaimed, + "ThreadSafeDeque::DeleteNode::destroy() - deleting unclaimed node" ); + destructInPlace( ptr ); + ptr->mFreeList.free( ptr ); + } + }; + + #ifdef TORQUE_DEBUG + S32 mNumValues; + #endif + + /// Reference to the head node. + NodeRef mHead; + + /// + NodeRef mTail; + + /// Free list for list nodes. + ThreadSafeFreeList< Node > mFreeList; + + /// @return the leftmost node in the list. + /// @note Updates the list state and may purge deleted nodes. + NodeRef getHead(); + + /// @return the rightmost node in the list. + /// @note Updates the list state and may purge deleted nodes. + NodeRef getTail(); + + public: + + /// Construct an empty deque. + ThreadSafeDeque() + { + #ifdef TORQUE_DEBUG + mNumValues = 0; + #endif + } + + ~ThreadSafeDeque() + { + ValueType value; + while( tryPopFront( value ) ); + AssertFatal( isEmpty(), "ThreadSafeDeque::~ThreadSafeDeque() - not empty" ); + } + + /// @return true if the queue is empty. + bool isEmpty() + { + return ( !getHead() && !getTail() ); + } + + /// Prepend the given value to the list. + void pushFront( const ValueType& value ); + + /// Append the given value to the list. + void pushBack( const ValueType& value ); + + /// Try to take the leftmost value from the deque. + /// Fails if the deque is empty at the time the method tries to + /// take a node from the list. + bool tryPopFront( ValueType& outValue ); + + /// Try to take the rightmost value from the deque. + /// Fails if the deque is empty at the time the method tries to + /// take a node from the list. + bool tryPopBack( ValueType& outValue ); + + void dumpDebug() + { + #ifdef TORQUE_DEBUG + Platform::outputDebugString( "[ThreadSafeDeque] numValues=%i", mNumValues ); + mFreeList.dumpDebug(); + #endif + } +}; + +// The getHead() and getTail() code here is pretty much brute-force in order +// to keep the complexities of synchronizing it bounded. We just let each +// thread work as if it is the only thread but require each one to start from +// scratch on each iteration. + +template< typename T > +typename ThreadSafeDeque< T >::NodeRef ThreadSafeDeque< T >::getHead() +{ + // Find leftmost node. + + NodeRef result; + while( 1 ) + { + // Iterate through to leftmost node. + + { + NodeRef head = mHead; + while( head != NULL ) + { + NodeRef prev = head->mPrev; + if( prev != NULL ) + mHead.trySetFromTo( head, prev, NodeRef::TAG_Unset ); + else + break; + + head = mHead; + } + } + + // Clear out dead nodes at front of list. + + { + NodeRef head = mHead; + if( head && head->mPrev.isTagged() ) + { + NodeRef next = head->mNext; + + mHead.trySetFromTo( head, next, NodeRef::TAG_Unset ); + mTail.trySetFromTo( head, next, NodeRef::TAG_Unset ); + + if( next != NULL ) + next->mPrev.trySetFromTo( head, NULL ); + + head->mNext.trySetFromTo( next, NULL, NodeRef::TAG_Set ); + + continue; // Restart. + } + } + + // Try head. + + NodeRef head = mHead; + if( head != NULL && !head->mPrev.isTagged() ) + { + result = head; + break; + } + + // Try tail. + + if( !head ) + { + head = mTail; + if( !head ) + break; + } + + // Update head. + + NodeRef prev = head->mPrev; + if( head->mPrev != NULL ) + { + if( !mHead.trySetFromTo( head, prev, NodeRef::TAG_Unset ) ) + mHead.trySetFromTo( NULL, prev ); + } + else + mHead.trySetFromTo( NULL, head ); + } + + AssertFatal( !result.isTagged(), "ThreadSafeDeque::getHead() - head got tagged" ); + return result; +} + +template< typename T > +typename ThreadSafeDeque< T >::NodeRef ThreadSafeDeque< T >::getTail() +{ + // Find rightmost node. + + NodeRef result; + while( 1 ) + { + // Iterate through to rightmost node. + + { + NodeRef tail = mTail; + while( tail != NULL ) + { + NodeRef next = tail->mNext; + if( next != NULL ) + mTail.trySetFromTo( tail, next, NodeRef::TAG_Unset ); + else + break; + + tail = mTail; + } + } + + // Clear out dead nodes at tail of list. + + { + NodeRef tail = mTail; + if( tail != NULL && tail->mNext.isTagged() ) + { + NodeRef prev = tail->mPrev; + + mHead.trySetFromTo( tail, prev, NodeRef::TAG_Unset ); + mTail.trySetFromTo( tail, prev, NodeRef::TAG_Unset ); + + if( prev != NULL ) + prev->mNext.trySetFromTo( tail, NULL ); + + tail->mPrev.trySetFromTo( prev, NULL, NodeRef::TAG_Set ); + + continue; // Restart. + } + } + + // Try tail. + + NodeRef tail = mTail; + if( tail != NULL && !tail->mNext.isTagged() ) + { + result = tail; + break; + } + + // Try head. + + if( !tail ) + { + tail = mHead; + if( !tail ) + break; + } + + // Update tail. + + NodeRef next = tail->mNext; + if( next != NULL ) + { + if( !mTail.trySetFromTo( tail, next, NodeRef::TAG_Unset ) ) + mTail.trySetFromTo( NULL, next ); + } + else + mTail.trySetFromTo( NULL, tail ); + } + + AssertFatal( !result.isTagged(), "ThreadSafeDeque::getTail() - tail got tagged" ); + return result; +} + +template< typename T > +void ThreadSafeDeque< T >::pushFront( const ValueType& value ) +{ + NodeRef nextNode; + NodeRef newNode; + + NodeRef::unsafeWrite( newNode, new ( mFreeList ) Node( mFreeList, value ) ); + + while( 1 ) + { + nextNode = getHead(); + if( !nextNode ) + { + newNode->mNext = NULL; + if( mHead.trySetFromTo( NULL, newNode ) ) + break; + } + else + { + newNode->mNext = nextNode; + if( nextNode->mPrev.trySetFromTo( NULL, newNode, NodeRef::TAG_FailIfSet ) ) + break; + } + } + +#ifdef TORQUE_DEBUG + dFetchAndAdd( mNumValues, 1 ); +#endif +} + +template< typename T > +void ThreadSafeDeque< T >::pushBack( const ValueType& value ) +{ + NodeRef prevNode; + NodeRef newNode; + + NodeRef::unsafeWrite( newNode, new ( mFreeList ) Node( mFreeList, value ) ); + + while( 1 ) + { + prevNode = getTail(); + if( !prevNode ) + { + newNode->mPrev = NULL; + if( mHead.trySetFromTo( NULL, newNode ) ) // use head so we synchronize with pushFront + break; + } + else + { + newNode->mPrev = prevNode; + if( prevNode->mNext.trySetFromTo( NULL, newNode, NodeRef::TAG_FailIfSet ) ) + break; + } + } + +#ifdef TORQUE_DEBUG + dFetchAndAdd( mNumValues, 1 ); +#endif +} + +template< typename T > +bool ThreadSafeDeque< T >::tryPopFront( ValueType& outValue ) +{ + NodeRef oldHead; + + while( 1 ) + { + oldHead = getHead(); + if( !oldHead ) + return false; + + // Try to claim the node. + + if( oldHead->mPrev.trySetFromTo( NULL, NULL, NodeRef::TAG_SetOrFail ) ) + { + if( dCompareAndSwap( oldHead->mIsClaimed, 0, 1 ) ) + break; + else + continue; + } + } + + outValue = oldHead->mValue; + oldHead = NULL; + + // Cleanup. + getHead(); + +#ifdef TORQUE_DEBUG + dFetchAndAdd( mNumValues, -1 ); +#endif + return true; +} + +template< typename T > +bool ThreadSafeDeque< T >::tryPopBack( ValueType& outValue ) +{ + NodeRef oldTail; + + while( 1 ) + { + oldTail = getTail(); + if( !oldTail ) + return false; + + // Try to claim the node. + + if( oldTail->mNext.trySetFromTo( NULL, NULL, NodeRef::TAG_SetOrFail ) ) + { + if( dCompareAndSwap( oldTail->mIsClaimed, 0, 1 ) ) + break; + } + } + + outValue = oldTail->mValue; + oldTail = NULL; + + // Cleanup. + getTail(); + +#ifdef TORQUE_DEBUG + dFetchAndAdd( mNumValues, -1 ); +#endif + return true; +} + + +#include "platform/tmm_on.h" + +#endif // _THREADSAFEDEQUE_H_ diff --git a/Engine/source/platform/threads/threadSafeFreeList.h b/Engine/source/platform/threads/threadSafeFreeList.h new file mode 100644 index 000000000..bb34e6bbe --- /dev/null +++ b/Engine/source/platform/threads/threadSafeFreeList.h @@ -0,0 +1,192 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _THREADSAFEFREELIST_H_ +#define _THREADSAFEFREELIST_H_ + +#ifndef _THREADSAFEREFCOUNT_H_ +# include "platform/threads/threadSafeRefCount.h" +#endif +#ifndef _PLATFORMINTRINSICS_H_ +# include "platform/platformIntrinsics.h" +#endif + +#include "platform/tmm_off.h" + + +/// @file +/// Lock-free freelists for concurrent access. + + +/// Freelist for re-using allocations in a concurrent setting. +/// +/// @note Make sure that there are no more allocations in use +/// when the free list is destructed. +/// @note Allocated instances come with a reference already counted +/// on the instance. +/// +/// @param T Type of elements to allocate; must be derived from +/// ThreadSafeRefCount and have at least define one additional +/// pointer-sized field. +template< class T > +class ThreadSafeFreeList +{ + protected: + + T* mFreeList; + + #ifdef TORQUE_DEBUG + S32 mNumNodesTotal; + S32 mNumNodesFree; + #endif + + T*& getNext( T* ptr ) + { + return *( ( T** ) &( ( U8* ) ptr )[ sizeof( T ) - sizeof( T* ) ] ); + } + + public: + + /// Create the freelist. + /// + /// @param numPreAlloc Number of instances to pre-allocate. + ThreadSafeFreeList( U32 numPreAlloc = 0 ) + : mFreeList( 0 ) + { + #ifdef TORQUE_DEBUG + mNumNodesTotal = 0; + mNumNodesFree = 0; + #endif + + for( U32 i = 0; i < numPreAlloc; ++ i ) + free( alloc() ); + } + + ~ThreadSafeFreeList() + { + #ifdef TORQUE_DEBUG + AssertWarn( mNumNodesTotal == mNumNodesFree, + "ThreadSafeFreeList::~ThreadSafeFreeList() - still got live instances" ); + #endif + + // Destroy remaining nodes. Not synchronized. We assume all + // concurrent processing to have finished. + + while( mFreeList ) + { + T* next = getNext( mFreeList ); + dFree( mFreeList ); + mFreeList = next; + } + } + + /// Return memory for a new instance. + void* alloc() + { + T* ptr; + while( 1 ) + { + ptr = ThreadSafeRef< T >::safeRead( mFreeList ); + if( !ptr ) + { + ptr = ( T* ) dMalloc( sizeof( T ) ); + dMemset( ptr, 0, sizeof( T ) ); + + #ifdef TORQUE_DEBUG + dFetchAndAdd( mNumNodesTotal, 1 ); + #endif + + ptr->addRef(); + break; + } + else if( dCompareAndSwap( mFreeList, ptr, getNext( ptr ) ) ) + { + #ifdef TORQUE_DEBUG + dFetchAndAdd( mNumNodesFree, -1 ); + #endif + + ptr->clearLowestBit(); + break; + } + else + ptr->release(); + } + + return ptr; + } + + /// Return the memory allocated to the given instance to the freelist. + void free( void* ptr ) + { + AssertFatal( ptr, "ThreadSafeFreeList::free() - got a NULL pointer" ); + T* node = ( T* ) ptr; + + while( 1 ) + { + T* list = mFreeList; + getNext( node ) = list; + if( dCompareAndSwap( mFreeList, list, node ) ) + break; + } + + #ifdef TORQUE_DEBUG + dFetchAndAdd( mNumNodesFree, 1 ); + #endif + } + + void dumpDebug() + { + #ifdef TORQUE_DEBUG + Platform::outputDebugString( "[ThreadSafeFreeList] total=%i, free=%i", + mNumNodesTotal, mNumNodesFree ); + #endif + } +}; + +/// Baseclass for objects allocated from ThreadSafeFreeLists. +template< class T, class DeletePolicy = DeleteSingle > +class ThreadSafeFreeListNode : public ThreadSafeRefCount< T, DeletePolicy > +{ + public: + + typedef ThreadSafeRefCount< T, DeletePolicy > Parent; + + ThreadSafeFreeListNode() + : Parent( false ) {} + + static void* operator new( size_t size, ThreadSafeFreeList< T >& freeList ) + { + AssertFatal( size <= sizeof( T ), + "ThreadSafeFreeListNode::new() - size exceeds limit of freelist" ); + TORQUE_UNUSED( size ); + return freeList.alloc(); + } + static void operator delete( void* ptr, ThreadSafeFreeList< T >& freeList ) + { + freeList.free( ptr ); + } +}; + + +#include "platform/tmm_on.h" + +#endif // _THREADSAFEFREELIST_H_ diff --git a/Engine/source/platform/threads/threadSafePriorityQueue.h b/Engine/source/platform/threads/threadSafePriorityQueue.h new file mode 100644 index 000000000..6e8a8f55e --- /dev/null +++ b/Engine/source/platform/threads/threadSafePriorityQueue.h @@ -0,0 +1,740 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _THREADSAFEPRIORITYQUEUE_H_ +#define _THREADSAFEPRIORITYQUEUE_H_ + +#ifndef _PLATFORMINTRINSICS_H_ + #include "platform/platformIntrinsics.h" +#endif +#ifndef _THREADSAFEREFCOUNT_H_ + #include "platform/threads/threadSafeRefCount.h" +#endif +#ifndef _TYPETRAITS_H_ + #include "platform/typetraits.h" +#endif + + +// Disable TMM's new operator grabbing. +#include "platform/tmm_off.h" + + +//#define DEBUG_SPEW + + +/// @file +/// Template code for an efficient thread-safe priority queue +/// implementation. There are two alternative implementations to +/// choose from: ThreadSafePriorityQueue and ThreadSafePriorityQueueWithUpdate +/// where the latter adds concurrent status updates of queue items to +/// the former implementation. + + +//-------------------------------------------------------------------------- +// ThreadSafePriorityQueue. +//-------------------------------------------------------------------------- + +/// Fast, lock-free priority queue implementation for concurrent access. +/// +/// Equal priorities are allowed and are placed before existing items of +/// identical priority in the queue. +/// +/// Based on (but with significant deviations from) "Fast and Lock-Free Concurrent +/// Priority Queues for Multi-Thread Systems" by Hakan Sundell and Philippas Tsigas. +/// Parts of the skiplist code is based on work by William Pugh. +/// +/// @param T The item value type. Must have a default constructor. +/// @param K The priority key type. Must be comparable, have a default constructor, +/// and be a valid template parameter to TypeTraits. +/// @param SORT_MIN_TO_MAX If true, the queue sorts from minimum to maximum priority or +/// the reverse if false. +/// @param MAX_LEVEL The number of levels a node can have at most. +/// @param PROBABILISTIC_BIAS The probabilistic level distribution factor for +/// the skiplist. Multiplied by 100 and turned into int to conform to restrictions +/// on non-type template parameters. +/// +/// @see TypeTraits + +template< typename T, typename K = F32, bool SORT_MIN_TO_MAX = false, U32 MAX_LEVEL = 4, U32 PROBABILISTIC_BIAS = 50 > +struct ThreadSafePriorityQueue +{ + typedef T ValueType; + typedef K KeyType; + + enum { MAX_LEVEL_CONST = MAX_LEVEL }; + + ThreadSafePriorityQueue(); + + bool isEmpty(); + void insert( KeyType priority, const T& value ); + bool takeNext( T& outValue, KeyType upToPriority = ( SORT_MIN_TO_MAX ? TypeTraits< KeyType >::MAX : TypeTraits< KeyType >::MIN ) ); + +protected: + struct Node; + typedef ThreadSafeRef< Node > NodePtr; + friend class ThreadSafeRefCount< Node >; + friend struct DeleteSingle; + + /// A queue node. + /// + /// Nodes are reference-counted to coordinate memory management + /// between the different threads. Reclamation happens on the + /// thread that releases the last reference. + /// + /// Reference-counting and deletion requests are kept separate. + /// A given node is marked for deletion and will then have its references + /// progressively disappear and eventually be reclaimed once the + /// reference count drops to zero. + /// + /// Note that 'Next' references are released by the destructor which + /// is only called when the reference count to the node itself drops to + /// zero. This is to avoid threads getting trapped in a node with no + /// link out. + + struct Node : public ThreadSafeRefCount< Node > + { + typedef ThreadSafeRefCount< Node > Parent; + + Node( KeyType priority, const ValueType& value ); + ~Node(); + + KeyType getPriority() { return mPriority; } + ValueType& getValue() { return mValue; } + U32 getLevel(); + NodePtr& getNext( U32 level ); + + bool isMarkedForDeletion(); + bool tryMarkForDeletion(); + + void clearValue() { mValue = ValueType(); } + + static U32 randomLevel(); + + void* operator new( size_t size, S32 level = -1 ); + void operator delete( void* ptr ); + + private: + KeyType mPriority; ///< Priority key. + U32 mLevel; ///< Level count and deletion bit (highest). + ValueType mValue; + Node* mNext[ 1 ]; ///< Variable-sized array of next pointers. + + struct FreeList + { + bool mDestroyed; + Node* mNodes; + + ~FreeList(); + }; + + static FreeList smFreeLists[ MAX_LEVEL ]; + }; + + NodePtr mHead; ///< Artificial head node. + NodePtr mTail; ///< Artificial tail node. + + void readNext( NodePtr& refPrev, NodePtr& refNext, U32 level ); + void scan( NodePtr& refPrev, NodePtr& refNext, U32 level, KeyType priority ); + void scanFromHead( NodePtr& refPrev, NodePtr& refNext, U32 level, KeyType priority ); + void insert( KeyType priority, const T& value, NodePtr& outResult ); + void helpDelete(); +}; + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +typename ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::FreeList ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::smFreeLists[ MAX_LEVEL ]; + +/// Construct an empty queue. +/// +/// Internally, this creates a head node with maximal priority and a tail node with minimal priority, +/// both at maximum level. + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::ThreadSafePriorityQueue() +{ + NodePtr::unsafeWrite( mHead, new ( MAX_LEVEL - 1 ) + Node( SORT_MIN_TO_MAX ? TypeTraits< KeyType >::MIN : TypeTraits< KeyType >::MAX, ValueType() ) ); + NodePtr::unsafeWrite( mTail, new ( MAX_LEVEL - 1 ) + Node( SORT_MIN_TO_MAX ? TypeTraits< KeyType >::MAX : TypeTraits< KeyType >::MIN, ValueType() ) ); + + for( U32 level = 0; level < MAX_LEVEL; level ++ ) + mHead->getNext( level ) = mTail; +} + +/// Return true if the queue does not currently contain an item. + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +bool ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::isEmpty() +{ + return ( mHead->getNext( 0 ) == mTail ); +} + +/// Insert the given value into the queue at the place determined by the given priority. + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +inline void ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::insert( KeyType priority, const ValueType& value ) +{ + NodePtr result; + insert( priority, value, result ); +} + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +void ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::insert( KeyType priority, const ValueType& value, NodePtr& outResult ) +{ + // Create a new node at a random level. + + outResult = NULL; + NodePtr::unsafeWrite( outResult, new Node( priority, value ) ); + U32 resultNodeLevel = outResult->getLevel(); + + // Link up all the levels. Do this bottom-up instead of + // top-down (as would be the right way for a skiplist) so + // that our list state always remains valid. If going top-down, + // we'll insert nodes with NULL pointers at their lower levels. + + U32 currentLevel = 0; + do + { + while( 1 ) + { + NodePtr nextNode; + NodePtr prevNode; + + scanFromHead( prevNode, nextNode, currentLevel, priority ); + + outResult->getNext( currentLevel ) = nextNode; + if( prevNode->getNext( currentLevel ).trySetFromTo( nextNode, outResult, NodePtr::TAG_FailIfSet ) ) + break; + else + outResult->getNext( currentLevel ) = 0; + } + + currentLevel ++; + } + while( currentLevel <= resultNodeLevel + && !outResult->isMarkedForDeletion() ); // No point linking up remaining levels if another thread already took this node. +} + +/// Take the item with the highest priority from the queue. +/// +/// @param outValue Reference to where the resulting value should be stored. +/// @param upToPriority Priority limit (inclusive) up to which items are taken from the queue. +/// @return true if there was a matching item in the queue. + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +bool ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::takeNext( T& outValue, KeyType upToPriority ) +{ + // Iterate through to the first unmarked node. + + NodePtr prevNode = mHead; + while( 1 ) + { + NodePtr node; + readNext( prevNode, node, 0 ); + + if( node == mTail ) + return false; // End reached. + + bool priorityThresholdReached = SORT_MIN_TO_MAX + ? ( upToPriority >= node->getPriority() ) + : ( upToPriority <= node->getPriority() ); + + if( !priorityThresholdReached ) + return false; + else + { + // Try to mark the node for deletion. Only if that succeeds, taking the + // node was a success and we can return. If it fails, spin and try again. + + if( node->tryMarkForDeletion() ) + { + helpDelete(); + + // Node is now off the list and will disappear as soon as + // all references held by threads (including this one) + // go out of scope. + + outValue = node->getValue(); + node->clearValue(); + + return true; + } + } + } +} + +/// Update the given references to the next non-deleted node at the given level. +/// refPrev will be updated to reference the immediate predecessor of the next +/// node returned. Note that this can be a node in deleted state. +/// +/// @param refPrev Reference to a node of which the successor node should be +/// returned. Updated to immediate predecessor of refNext on return. +/// @param refNext Reference to update to refer to next non-deleted node on +/// the given level. +/// @param level Skiplist level to operate on. + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +inline void ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::readNext( NodePtr& refPrev, NodePtr& refNext, U32 level ) +{ + while( 1 ) + { + refNext = refPrev->getNext( level ); + AssertFatal( refNext != NULL, "ThreadSafePriorityQueue::readNext() - next is NULL" ); + if( !refNext->isMarkedForDeletion() || refNext == mTail ) + break; + + refPrev = refNext; + } +} + +/// Scan for the position at which to insert a node of the given priority. +/// Upon return, the position between refPrev and refNext is the one to insert at. +/// +/// @param refPrev position at which to start scanning; updated to match insert position. +/// @param refNext + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +void ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::scan( NodePtr& refPrev, NodePtr& refNext, U32 level, KeyType priority ) +{ + while( 1 ) + { + readNext( refPrev, refNext, level ); + if( refNext == mTail + || ( SORT_MIN_TO_MAX + ? ( refNext->getPriority() > priority ) + : ( refNext->getPriority() < priority ) ) ) + break; + + refPrev = refNext; + } +} + +/// + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +void ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::scanFromHead( NodePtr& refPrev, NodePtr& refNext, U32 level, KeyType priority ) +{ + // Purge dead nodes at left end of queue so + // we don't get stuck hitting the same node + // in deletable state over and over again. + helpDelete(); + + S32 currentLevel = MAX_LEVEL - 1; + refPrev = mHead; + do + { + scan( refPrev, refNext, currentLevel, priority ); + currentLevel --; + } + while( currentLevel >= S32( level ) ); +} + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +void ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::helpDelete() +{ + // Clean out all the references from head. + // Spin over a given reference on each level until head + // clearly refers to a node in non-deletable state. This + // makes this code work cooperatively with other threads + // doing takeNexts on prior or later nodes while also + // guaranteeing that all next pointers to us will eventually + // disappear. + // + // Note that this is *the only place* where we will be cleaning + // out our lists. + + S32 level = MAX_LEVEL - 1; + do + { + while( 1 ) + { + NodePtr ptr = mHead->getNext( level ); + if( !ptr->isMarkedForDeletion() ) + break; + else + { + NodePtr& next = ptr->getNext( level ); + next.setTag(); + mHead->getNext( level ).trySetFromTo( ptr, next, NodePtr::TAG_Unset ); + AssertFatal( next->getRefCount() >= 2, "ThreadSafePriorityQueue::helpDelete() - invalid refcount" ); + } + } + + level --; + } + while( level >= 0 ); +} + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +inline ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::Node( KeyType priority, const ValueType& value ) + : Parent( false ), + mPriority( priority ), + mValue( value ) +{ + dMemset( mNext, 0, sizeof( Node* ) * ( getLevel() + 1 ) ); + + // Level is already set by the allocation routines. +} + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::~Node() +{ + for( U32 level = 0; level < ( getLevel() + 1 ); level ++ ) + getNext( level ) = NULL; +} + +/// Return the skip list level the node is at. + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +inline U32 ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::getLevel() +{ + // Mask out the deletion request bit. + + return ( mLevel & 0x7FFFFFFF ); +} + +/// Return the successor node at the given level. +/// @param level The level of the desired successor node; must be within the node's level bounds. + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +inline typename ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::NodePtr& ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::getNext( U32 level ) +{ + return *reinterpret_cast< NodePtr* >( &mNext[ level ] ); +} + +/// Return true if the node is marked to be deleted. + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +inline bool ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::isMarkedForDeletion() +{ + return ( mLevel & 0x80000000 ); +} + +/// Attempt to mark the node for deletion. If the mark bit has not yet been set +/// and setting it on the current thread succeeds, returns true. +/// +/// @return true, if the marking succeeded. + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +inline bool ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::tryMarkForDeletion() +{ + U32 oldVal = mLevel & 0x7FFFFFFF; + U32 newVal = oldVal | 0x80000000; + + return ( dCompareAndSwap( mLevel, oldVal, newVal ) ); +} + +/// Choose a random level. +/// +/// The chosen level depends on the given PROBABILISTIC_BIAS and MAX_LEVEL, +/// but is not affected by the actual number of nodes in a queue. + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +U32 ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::randomLevel() +{ + U32 level = 0; + while( Platform::getRandom() < ( ( ( F32 ) PROBABILISTIC_BIAS ) / 100 ) && level < ( MAX_LEVEL - 1 ) ) + level ++; + return level; +} + +/// Allocate a new node. +/// The node comes with a reference count of one and its level already set. +/// +/// @param level The level to allocate the node at. If this is -1, a random level is chosen. +/// @return a new node. + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +void* ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::operator new( size_t size, S32 level ) +{ + if( level == -1 ) + level = randomLevel(); + + Node* node = 0; + while( 1 ) + { + // Try to take a node from the freelist. If there's none, + // allocate a new one. + + if( !smFreeLists[ level ].mDestroyed ) + node = Node::safeRead( smFreeLists[ level ].mNodes ); + + if( !node ) + { + node = ( Node* ) dMalloc( sizeof( Node ) + sizeof( Node* ) * level ); + dMemset( node, 0, sizeof( Node ) ); + node->mLevel = level; + node->addRef(); + break; + } + else if( dCompareAndSwap( smFreeLists[ level ].mNodes, node, node->mNext[ 0 ] ) ) + { + node->clearLowestBit(); + break; + } + else + node->release(); // Other thread was quicker than us; release. + } + + AssertFatal( node->getRefCount() != 0, "ThreadSafePriorityQueue::new Node() - invalid refcount" ); + AssertFatal( ( node->getRefCount() % 2 ) == 0, "ThreadSafePriorityQueue::new Node() - invalid refcount" ); + return node; +} + +/// Reclaim a node. +/// +/// @param node The node to reclaim. Must refer to a Node instance. + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +void ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::operator delete( void* ptr ) +{ + //TODO: limit number of nodes kept + + Node* node = ( Node* ) ptr; + U32 level = node->getLevel(); + node->mLevel = level; // Reset the node's deletion bit. + + while( !smFreeLists[ level ].mDestroyed ) + { + // Put the node on the freelist. + + Node* freeList = smFreeLists[ level ].mNodes; + node->mNext[ 0 ] = freeList; + + if( dCompareAndSwap( smFreeLists[ level ].mNodes, freeList, node ) ) + { + node = NULL; + break; + } + } + + if( node ) + dFree( node ); +} + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::Node::FreeList::~FreeList() +{ + mDestroyed = true; + while( mNodes ) + { + //FIXME: could leak some bytes under unfortunate circumstances (this in + // combination with mDestroyed is a dependent write) + + Node* next = mNodes; + if( dCompareAndSwap( mNodes, next, next->mNext[ 0 ] ) ) + dFree( next ); + } +} + +//-------------------------------------------------------------------------- +// ThreadSafePriorityQueueWithUpdate. +//-------------------------------------------------------------------------- + +/// Fast, lock-free priority queue implementation for concurrent access that +/// performs dynamic re-prioritization of items. +/// +/// Within the bounds of a set update interval UPDATE_INTERVAL, the takeNext +/// method is guaranteed to always return the item that has the highest priority +/// at the time the method is called rather than at the time items were inserted +/// into the queue. +/// +/// Values placed on the queue must implement the following interface: +/// +/// @code +/// template< typename K > +/// struct IThreadSafePriorityQueueItem +/// { +/// // Default constructor. +/// IThreadSafePriorityQueueItem(); +/// +/// // Return the current priority. +/// // This must run normally even if the item is already dead. +/// K getPriority(); +/// +/// // Return true if the item is still meant to be waiting in the queue. +/// bool isAlive(); +/// }; +/// @endcode + +template< typename T, typename K, bool SORT_MIN_TO_MAX = false, U32 MAX_LEVEL = 4, U32 PROBABILISTIC_BIAS = 50 > +struct ThreadSafePriorityQueueWithUpdate : public ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS > +{ + + typedef T ValueType; + typedef K KeyType; + + enum { DEFAULT_UPDATE_INTERVAL = 256 }; + + ThreadSafePriorityQueueWithUpdate( U32 updateInterval = DEFAULT_UPDATE_INTERVAL ); + + void insert( KeyType priority, const T& value ); + bool takeNext( T& outValue, KeyType upToPriority = ( SORT_MIN_TO_MAX ? TypeTraits< KeyType >::MAX : TypeTraits< KeyType >::MIN ) ); + + U32 getUpdateInterval() const; + void setUpdateInterval( U32 value ); + + KeyType getTimeBasedPriorityBoost() const; + void setTimeBasedPriorityBoost( KeyType value ); + + void updatePriorities(); + +protected: + typedef ThreadSafePriorityQueue< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS > Parent; + typedef U32 TickType; + typedef typename Parent::NodePtr NodePtr; + + U32 mUpdateInterval; + KeyType mPriorityBoost; ///< If this is non-zero, priorities will be boosted by this amount each update. This can be used to prevent constant high-priority inserts to starve low-priority items already in the queue. + + /// Work queue for node updates. + ThreadSafePriorityQueue< NodePtr, TickType, true, MAX_LEVEL, PROBABILISTIC_BIAS > mUpdateQueue; + + TickType getTick() { return Platform::getRealMilliseconds(); } +}; + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +ThreadSafePriorityQueueWithUpdate< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::ThreadSafePriorityQueueWithUpdate( U32 updateInterval ) + : mUpdateInterval( updateInterval ), + mPriorityBoost( TypeTraits< KeyType >::ZERO ) +{ +} + +/// Return the current update interval in milliseconds. + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +U32 ThreadSafePriorityQueueWithUpdate< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::getUpdateInterval() const +{ + return mUpdateInterval; +} + +/// Set update interval of queue to given value. +/// +/// Call this method on the main thread only. +/// +/// @param value Time between priority updates in milliseconds. + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +void ThreadSafePriorityQueueWithUpdate< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::setUpdateInterval( U32 value ) +{ + mUpdateInterval = value; +} + +/// Return the delta to apply to priorities on each update. +/// Set to zero to deactivate time-based priority adjustments. + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +K ThreadSafePriorityQueueWithUpdate< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::getTimeBasedPriorityBoost() const +{ + return mPriorityBoost; +} + +/// Set the delta for time-based priority adjustments to the given value. +/// +/// Call this method on the main thread only. +/// +/// @param value The new priority adjustment value. + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +void ThreadSafePriorityQueueWithUpdate< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::setTimeBasedPriorityBoost( KeyType value ) +{ + mPriorityBoost = value; +} + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +void ThreadSafePriorityQueueWithUpdate< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::insert( KeyType priority, const ValueType& value ) +{ + NodePtr node; + Parent::insert( priority, value, node ); + mUpdateQueue.insert( getTick() + getUpdateInterval(), node ); +} + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +bool ThreadSafePriorityQueueWithUpdate< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::takeNext( T& outValue, KeyType upToPriority ) +{ + updatePriorities(); + + bool result = false; + do + { + result = Parent::takeNext( outValue, upToPriority ); + } + while( result && !outValue.isAlive() ); + + return result; +} + +/// + +template< typename T, typename K, bool SORT_MIN_TO_MAX, U32 MAX_LEVEL, U32 PROBABILISTIC_BIAS > +void ThreadSafePriorityQueueWithUpdate< T, K, SORT_MIN_TO_MAX, MAX_LEVEL, PROBABILISTIC_BIAS >::updatePriorities() +{ + TickType currentTime = getTick(); + U32 numNodesUpdated = 0; + U32 numNodesDead = 0; + U32 numNodesChanged = 0; + + NodePtr node; + while( mUpdateQueue.takeNext( node, currentTime ) ) + { + numNodesUpdated ++; + + // Since we're updating nodes on the update queue only periodically, + // their associated values or main queue nodes may have died in the + // meantime. If so, we just discard them here. + + if( node->getValue().isAlive() + && !node->isMarkedForDeletion() ) + { + KeyType newPriority = node->getValue().getPriority() + getTimeBasedPriorityBoost(); + if( newPriority != node->getPriority() ) + { + // Node is outdated. Reinsert with new priority and mark the + // old node for deletion. + + insert( newPriority, node->getValue() ); + node->tryMarkForDeletion(); + numNodesChanged ++; + } + else + { + // Node is still current. Just move to end. + + mUpdateQueue.insert( currentTime + getUpdateInterval(), node ); + } + } + else + numNodesDead ++; + } + + #ifdef DEBUG_SPEW + if( numNodesUpdated ) + Platform::outputDebugString( "[ThreadSafePriorityQueueWithUpdate] updated %i nodes (%i changed, %i dead)", + numNodesUpdated, numNodesChanged, numNodesDead ); + #endif +} + +// Re-enable TMM if necessary. +#include "platform/tmm_on.h" + +#undef DEBUG_SPEW + +#endif // !_THREADSAFEPRIORITYQUEUE_H_ diff --git a/Engine/source/platform/threads/threadSafeRefCount.h b/Engine/source/platform/threads/threadSafeRefCount.h new file mode 100644 index 000000000..96e11aa20 --- /dev/null +++ b/Engine/source/platform/threads/threadSafeRefCount.h @@ -0,0 +1,380 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _THREADSAFEREFCOUNT_H_ +#define _THREADSAFEREFCOUNT_H_ + +#ifndef _PLATFORMINTRINSICS_H_ +# include "platform/platformIntrinsics.h" +#endif +#ifndef _TYPETRAITS_H_ +# include "platform/typetraits.h" +#endif + + +/// @file +/// Templated code for concurrent reference-counting. +/// +/// Part of this code is based on work by J.D. Valois, Michael M. Maged, +/// and Scott L. Michael. + + +//-------------------------------------------------------------------------- +// ThreadSafeRefCount. +//-------------------------------------------------------------------------- + +/// Baseclass for concurrently reference-counted objects. +/// +/// @note NOTE that freshly instantiated objects start out with a reference +/// count of ZERO! Depending on how this class is used, this may not +/// be desirable, so override this behavior in constructors if necessary. +/// +/// @param T the class being reference counted; this is passed to this class, +/// so it can call the correct destructor without having to force users +/// to have virtual methods + +template< class T, class DeletePolicy = DeleteSingle > +class ThreadSafeRefCount +{ + public: + + typedef void Parent; + + ThreadSafeRefCount() + : mRefCount( 0 ) {} + ThreadSafeRefCount( bool noSet ) {} + + bool isShared() const; + U32 getRefCount() const; + void addRef(); + void release(); + void clearLowestBit(); + static T* safeRead( T* const volatile& refPtr ); + + protected: + + U32 mRefCount; ///< Reference count and claim bit. Note that this increments in steps of two. + + static U32 decrementAndTestAndSet( U32& refCount ); +}; + +/// @return true if the object is referenced by more than a single +/// reference. + +template< class T, class DeletePolicy > +inline bool ThreadSafeRefCount< T, DeletePolicy >::isShared() const +{ + return ( mRefCount > 3 ); +} + +/// Get the current reference count. This method is mostly meant for +/// debugging and should not normally be used. + +template< class T, class DeletePolicy > +inline U32 ThreadSafeRefCount< T, DeletePolicy >::getRefCount() const +{ + return mRefCount; +} + +/// Increase the reference count on the object. + +template< class T, class DeletePolicy > +inline void ThreadSafeRefCount< T, DeletePolicy >::addRef() +{ + dFetchAndAdd( mRefCount, 2 ); +} + +/// Decrease the object's reference count and delete the object, if the count +/// drops to zero and claiming the object by the current thread succeeds. + +template< class T, class DeletePolicy > +inline void ThreadSafeRefCount< T, DeletePolicy >::release() +{ + AssertFatal( mRefCount != 0, "ThreadSafeRefCount::release() - refcount of zero" ); + if( decrementAndTestAndSet( mRefCount ) != 0 ) + DeletePolicy::destroy( ( T* ) this ); +} + +/// Dereference a reference-counted pointer in a multi-thread safe way. + +template< class T, class DeletePolicy > +T* ThreadSafeRefCount< T, DeletePolicy >::safeRead( T* const volatile& refPtr ) +{ + while( 1 ) + { + // Support tagged pointers here. + + T* ptr = TypeTraits< T* >::getUntaggedPtr( refPtr ); + if( !ptr ) + return 0; + + ptr->addRef(); + if( ptr == TypeTraits< T* >::getUntaggedPtr( refPtr ) ) + return ptr; + else + ptr->release(); + } +} + +/// Decrement the given reference count. Return 1 if the count dropped to zero +/// and the claim bit has been successfully set; return 0 otherwise. + +template< class T, class DeletePolicy > +U32 ThreadSafeRefCount< T, DeletePolicy >::decrementAndTestAndSet( U32& refCount ) +{ + U32 oldVal; + U32 newVal; + + do + { + oldVal = refCount; + newVal = oldVal - 2; + + AssertFatal( oldVal >= 2, + "ThreadSafeRefCount::decrementAndTestAndSet() - invalid refcount" ); + + if( newVal == 0 ) + newVal = 1; + } + while( !dCompareAndSwap( refCount, oldVal, newVal ) ); + + return ( ( oldVal - newVal ) & 1 ); +} + +/// + +template< class T, class DeletePolicy > +inline void ThreadSafeRefCount< T, DeletePolicy >::clearLowestBit() +{ + AssertFatal( mRefCount % 2 != 0, "ThreadSafeRefCount::clearLowestBit() - invalid refcount" ); + + U32 oldVal; + U32 newVal; + + do + { + oldVal = mRefCount; + newVal = oldVal - 1; + } + while( !dCompareAndSwap( mRefCount, oldVal, newVal ) ); +} + +//-------------------------------------------------------------------------- +// ThreadSafeRef. +//-------------------------------------------------------------------------- + +/// Reference to a concurrently reference-counted object. +/// +/// This class takes care of the reference-counting as well as protecting +/// the reference itself from concurrent operations. +/// +/// Tagging allows the pointer contained in the reference to be flagged. +/// Tag state is preserved through updates to the reference. +/// +/// @note If you directly assign a freshly created object with a reference +/// count of zero to a ThreadSafeRef, make absolutely sure the ThreadSafeRef +/// is accessed only by a single thread. Otherwise there's a risk of the +/// object being released and freed in midst of trying to set the reference. +template< class T > +class ThreadSafeRef +{ + public: + + enum ETag + { + TAG_PreserveOld, ///< Preserve existing tagging state when changing pointer. + TAG_PreserveNew, ///< Preserve tagging state of new pointer when changing pointer. + TAG_Set, ///< Set tag when changing pointer; okay if already set. + TAG_Unset, ///< Unset tag when changing pointer; okay if already unset. + TAG_SetOrFail, ///< Set tag when changing pointer; fail if already set. + TAG_UnsetOrFail, ///< Unset tag when changing pointer; fail if already unset. + TAG_FailIfSet, ///< Fail changing pointer when currently tagged. + TAG_FailIfUnset ///< Fail changing pointer when currently untagged. + }; + + typedef ThreadSafeRef< T > ThisType; + + ThreadSafeRef() : mPtr( 0 ) {} + ThreadSafeRef( T* ptr ) : mPtr( ThreadSafeRefCount< T >::safeRead( ptr ) ) {} + ThreadSafeRef( const ThisType& ref ) : mPtr( ThreadSafeRefCount< T >::safeRead( ref.mPtr ) ) {} + ~ThreadSafeRef() + { + T* ptr = NULL; + while( !trySetFromTo( mPtr, ptr ) ); + } + + T* ptr() const { return getUntaggedPtr( mPtr ) ; } + void setTag() { while( !trySetFromTo( mPtr, mPtr, TAG_Set ) ); } + bool isTagged() const { return isTaggedPtr( mPtr ); } + bool trySetFromTo( T* oldVal, T* const volatile& newVal, ETag tag = TAG_PreserveOld ); + bool trySetFromTo( T* oldVal, const ThisType& newVal, ETag tag = TAG_PreserveOld ); + bool trySetFromTo( const ThisType& oldVal, const ThisType& newVal, ETag tag = TAG_PreserveOld ); + static void unsafeWrite( ThisType& ref, T* ptr ); + static T* safeRead( T* const volatile& refPtr ) { return ThreadSafeRefCount< T >::safeRead( refPtr ); } + + bool operator ==( T* ptr ) const; + bool operator ==( const ThisType& ref ) const; + bool operator !=( T* ptr ) const { return !( *this == ptr ); } + bool operator !=( const ThisType& ref ) const { return !( *this == ref ); } + ThisType& operator =( T* ptr ); + ThisType& operator =( const ThisType& ref ); + + bool operator !() const { return ( ptr() == 0 ); } + T& operator *() const { return *ptr(); } + T* operator ->() const { return ptr(); } + operator T*() const { return ptr(); } + + protected: + + T* volatile mPtr; + + static bool isTaggedPtr( T* ptr ) { return TypeTraits< T* >::isTaggedPtr( ptr ); } + static T* getTaggedPtr( T* ptr ) { return TypeTraits< T* >::getTaggedPtr( ptr ); } + static T* getUntaggedPtr( T* ptr ) { return TypeTraits< T* >::getUntaggedPtr( ptr ); } +}; + +/// Update the reference from pointing to oldVal to point to newVal. +/// Do so in a thread-safe way. +/// +/// This operation will only succeed, if, when doing the pointer-swapping, +/// the reference still points to oldVal. If, however, the reference +/// has been changed in the meantime by another thread, the operation will +/// fail. +/// +/// @param oldVal The pointer assumed to currently be contained in this ThreadSafeRef. +/// @param newVal The pointer to store in this ThreadSafeRef. +/// @param tag Operation to perform on the reference's tag field. +/// +/// @return true, if the reference now points to newVal. + +template< class T > +bool ThreadSafeRef< T >::trySetFromTo( T* oldVal, T* const volatile& newVal, ETag tag ) +{ + bool setTag = false; + bool getTag = false; + bool isTagged = isTaggedPtr( oldVal ); + + switch( tag ) + { + case TAG_PreserveOld: setTag = isTaggedPtr( oldVal ); break; + case TAG_PreserveNew: setTag = isTaggedPtr( newVal ); break; + case TAG_Set: setTag = true; break; + case TAG_Unset: setTag = false; break; + case TAG_SetOrFail: setTag = true; getTag = true; break; + case TAG_UnsetOrFail: setTag = false; getTag = true; break; + case TAG_FailIfSet: if( isTagged ) return false; break; + case TAG_FailIfUnset: if( !isTagged ) return false; break; + } + + T* newValPtr = ( setTag + ? getTaggedPtr( ThreadSafeRefCount< T >::safeRead( newVal ) ) + : getUntaggedPtr( ThreadSafeRefCount< T >::safeRead( newVal ) ) ); + + if( dCompareAndSwap( mPtr, + ( getTag + ? ( setTag + ? getUntaggedPtr( oldVal ) + : getTaggedPtr( oldVal ) ) + : oldVal ), + newValPtr ) ) + { + if( getUntaggedPtr( oldVal ) ) + getUntaggedPtr( oldVal )->release(); + return true; + } + else + { + if( getUntaggedPtr( newValPtr ) ) + getUntaggedPtr( newValPtr )->release(); + return false; + } +} + +template< class T > +inline bool ThreadSafeRef< T >::trySetFromTo( T* oldVal, const ThisType& newVal, ETag tag ) +{ + return trySetFromTo( oldVal, newVal.mPtr, tag ); +} + +template< class T > +inline bool ThreadSafeRef< T >::trySetFromTo( const ThisType& oldVal, const ThisType& newVal, ETag tag ) +{ + return trySetFromTo( oldVal.mPtr, newVal.mPtr, tag ); +} + +/// Update ref to point to ptr but do not release an existing +/// reference held by ref nor do the operation in a thread-safe way. +/// +/// This method is only for when you absolutely know that your +/// thread is the only thread operating on a reference and you +/// are keeping track of reference counts yourself. +/// +/// @param ref The reference to update. +/// @param ptr The new pointer to store in ref. + +template< class T > +inline void ThreadSafeRef< T >::unsafeWrite( ThisType& ref, T* ptr ) +{ + ref.mPtr = ptr; +} + +template< class T > +inline bool ThreadSafeRef< T >::operator ==( T* p ) const +{ + return ( ptr() == p ); +} + +template< class T > +inline bool ThreadSafeRef< T >::operator ==( const ThisType& ref ) const +{ + return ( ptr() == ref.ptr() ); +} + +template< class T > +inline ThreadSafeRef< T >& ThreadSafeRef< T >::operator =( T* ptr ) +{ + while( !trySetFromTo( mPtr, ptr, TAG_PreserveNew ) ); + return *this; +} + +template< class T > +inline ThreadSafeRef< T >& ThreadSafeRef< T >::operator =( const ThisType& ref ) +{ + while( !trySetFromTo( mPtr, ref, TAG_PreserveNew ) ); + return *this; +} + + +template< typename T > +struct TypeTraits< ThreadSafeRef< T > > : public TypeTraits< T* > {}; +template< typename T > +inline T& Deref( ThreadSafeRef< T >& ref ) +{ + return *ref; +} +template< typename T > +inline T& Deref( const ThreadSafeRef< T >& ref ) +{ + return *ref; +} + +#endif // _THREADSAFEREFCOUNT_H_ diff --git a/Engine/source/platform/tmm_off.h b/Engine/source/platform/tmm_off.h new file mode 100644 index 000000000..928de6955 --- /dev/null +++ b/Engine/source/platform/tmm_off.h @@ -0,0 +1,23 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#undef new diff --git a/Engine/source/platform/tmm_on.h b/Engine/source/platform/tmm_on.h new file mode 100644 index 000000000..a7e8cc28b --- /dev/null +++ b/Engine/source/platform/tmm_on.h @@ -0,0 +1,25 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef TORQUE_DISABLE_MEMORY_MANAGER +# define new _new +#endif diff --git a/Engine/source/platform/types.codewarrior.h b/Engine/source/platform/types.codewarrior.h new file mode 100644 index 000000000..64912784d --- /dev/null +++ b/Engine/source/platform/types.codewarrior.h @@ -0,0 +1,98 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef INCLUDED_TYPES_CODEWARRIOR_H +#define INCLUDED_TYPES_CODEWARRIOR_H + +#pragma once + +// If using the IDE detect if DEBUG build was requested +#if __ide_target("Torque-W32-Debug") + #define TORQUE_DEBUG +#elif __ide_target("Torque-MacCarb-Debug") + #define TORQUE_DEBUG +#elif __ide_target("Torque-MacX-Debug") + #define TORQUE_DEBUG +#endif + + +//-------------------------------------- +// Types +typedef signed long long S64; ///< Compiler independent Signed 64-bit integer +typedef unsigned long long U64; ///< Compiler independent Unsigned 64-bit integer + + + +//-------------------------------------- +// Compiler Version +#define TORQUE_COMPILER_CODEWARRIOR __MWERKS__ + +#define TORQUE_COMPILER_STRING "CODEWARRIOR" + + +//-------------------------------------- +// Identify the Operating System +#if defined(_WIN32) +# define TORQUE_OS_STRING "Win32" +# define TORQUE_OS_WIN32 +# include "platform/types.win32.h" + +#elif defined(macintosh) || defined(__APPLE__) +# define TORQUE_OS_STRING "Mac" +# define TORQUE_OS_MAC +# if defined(__MACH__) +# define TORQUE_OS_MAC +# endif +# include "platform/types.ppc.h" +#else +# error "CW: Unsupported Operating System" +#endif + + +//-------------------------------------- +// Identify the CPU +#if defined(_M_IX86) +# define TORQUE_CPU_STRING "x86" +# define TORQUE_CPU_X86 +# define TORQUE_LITTLE_ENDIAN +# define TORQUE_SUPPORTS_NASM +# define TORQUE_SUPPORTS_VC_INLINE_X86_ASM + + // Compiling with the CW IDE we cannot use NASM :( +# if __ide_target("Torque-W32-Debug") +# undef TORQUE_SUPPORTS_NASM +# elif __ide_target("Torque-W32-Release") +# undef TORQUE_SUPPORTS_NASM +# endif + +#elif defined(__POWERPC__) +# define TORQUE_CPU_STRING "PowerPC" +# define TORQUE_CPU_PPC +# define TORQUE_BIG_ENDIAN + +#else +# error "CW: Unsupported Target CPU" +#endif + + +#endif // INCLUDED_TYPES_CODEWARRIOR_H + diff --git a/Engine/source/platform/types.gcc.h b/Engine/source/platform/types.gcc.h new file mode 100644 index 000000000..406b07ef1 --- /dev/null +++ b/Engine/source/platform/types.gcc.h @@ -0,0 +1,163 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TYPESGCC_H +#define _TYPESGCC_H + + +// For additional information on GCC predefined macros +// http://gcc.gnu.org/onlinedocs/gcc-3.0.2/cpp.html + + +//-------------------------------------- +// Types +typedef signed long long S64; +typedef unsigned long long U64; + + +//-------------------------------------- +// Compiler Version +#define TORQUE_COMPILER_GCC (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) + + +//-------------------------------------- +// Identify the compiler string + +#if defined(__MINGW32__) +# define TORQUE_COMPILER_STRING "GCC (MinGW)" +# define TORQUE_COMPILER_MINGW +#elif defined(__CYGWIN__) +# define TORQUE_COMPILER_STRING "GCC (Cygwin)" +# define TORQUE_COMPILER_MINGW +#else +# define TORQUE_COMPILER_STRING "GCC " +#endif + + +//-------------------------------------- +// Identify the Operating System +#if defined(__WIN32__) || defined(_WIN32) +# define TORQUE_OS_STRING "Win32" +# define TORQUE_OS_WIN32 +# define TORQUE_SUPPORTS_NASM +# define TORQUE_SUPPORTS_GCC_INLINE_X86_ASM +# include "platform/types.win32.h" + +#elif defined(SN_TARGET_PS3) +# define TORQUE_OS_STRING "PS3" +# define TORQUE_OS_PS3 +# include "platform/types.posix.h" + +#elif defined(linux) +# define TORQUE_OS_STRING "Linux" +# define TORQUE_OS_LINUX +# define TORQUE_SUPPORTS_NASM +# define TORQUE_SUPPORTS_GCC_INLINE_X86_ASM +# include "platform/types.posix.h" + +#elif defined(__OpenBSD__) +# define TORQUE_OS_STRING "OpenBSD" +# define TORQUE_OS_OPENBSD +# define TORQUE_SUPPORTS_NASM +# define TORQUE_SUPPORTS_GCC_INLINE_X86_ASM +# include "platform/types.posix.h" + +#elif defined(__FreeBSD__) +# define TORQUE_OS_STRING "FreeBSD" +# define TORQUE_OS_FREEBSD +# define TORQUE_SUPPORTS_NASM +# define TORQUE_SUPPORTS_GCC_INLINE_X86_ASM +# include "platform/types.posix.h" + +#elif defined(__APPLE__) +# define TORQUE_OS_STRING "MacOS X" +# define TORQUE_OS_MAC +# include "platform/types.mac.h" +# if defined(i386) +// Disabling ASM on XCode for shared library build code relocation issues +// This could be reconfigured for static builds, though minimal impact +//# define TORQUE_SUPPORTS_NASM +# endif +#else +# error "GCC: Unsupported Operating System" +#endif + + +//-------------------------------------- +// Identify the CPU +#if defined(i386) +# define TORQUE_CPU_STRING "Intel x86" +# define TORQUE_CPU_X86 +# define TORQUE_LITTLE_ENDIAN + +#elif defined(__ppc__) +# define TORQUE_CPU_STRING "PowerPC" +# define TORQUE_CPU_PPC +# define TORQUE_BIG_ENDIAN + +#elif defined(SN_TARGET_PS3) +# define TORQUE_CPU_STRING "PowerPC" +# define TORQUE_CPU_PPC +# define TORQUE_BIG_ENDIAN + +#else +# error "GCC: Unsupported Target CPU" +#endif + +#ifndef Offset +/// Offset macro: +/// Calculates the location in memory of a given member x of class cls from the +/// start of the class. Need several definitions to account for various +/// flavors of GCC. + +// now, for each compiler type, define the Offset macros that should be used. +// The Engine code usually uses the Offset macro, but OffsetNonConst is needed +// when a variable is used in the indexing of the member field (see +// TSShapeConstructor::initPersistFields for an example) + +// compiler is non-GCC, or gcc < 3 +#if (__GNUC__ < 3) +#define Offset(x, cls) _Offset_Normal(x, cls) +#define OffsetNonConst(x, cls) _Offset_Normal(x, cls) + +// compiler is GCC 3 with minor version less than 4 +#elif defined(TORQUE_COMPILER_GCC) && (__GNUC__ == 3) && (__GNUC_MINOR__ < 4) +#define Offset(x, cls) _Offset_Variant_1(x, cls) +#define OffsetNonConst(x, cls) _Offset_Variant_1(x, cls) + +// compiler is GCC 3 with minor version greater than 4 +#elif defined(TORQUE_COMPILER_GCC) && (__GNUC__ == 3) && (__GNUC_MINOR__ >= 4) +#include +#define Offset(x, cls) _Offset_Variant_2(x, cls) +#define OffsetNonConst(x, cls) _Offset_Variant_1(x, cls) + +// compiler is GCC 4 +#elif defined(TORQUE_COMPILER_GCC) && (__GNUC__ == 4) +#include +#define Offset(x, cls) _Offset_Normal(x, cls) +#define OffsetNonConst(x, cls) _Offset_Variant_1(x, cls) + +#endif +#endif + +#endif // INCLUDED_TYPES_GCC_H + diff --git a/Engine/source/platform/types.h b/Engine/source/platform/types.h new file mode 100644 index 000000000..c53bd87fc --- /dev/null +++ b/Engine/source/platform/types.h @@ -0,0 +1,299 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TORQUE_TYPES_H_ +#define _TORQUE_TYPES_H_ + +//------------------------------------------------------------------------------ +//-------------------------------------- Basic Types... + +typedef signed char S8; ///< Compiler independent Signed Char +typedef unsigned char U8; ///< Compiler independent Unsigned Char + +typedef signed short S16; ///< Compiler independent Signed 16-bit short +typedef unsigned short U16; ///< Compiler independent Unsigned 16-bit short + +typedef signed int S32; ///< Compiler independent Signed 32-bit integer +typedef unsigned int U32; ///< Compiler independent Unsigned 32-bit integer + +typedef float F32; ///< Compiler independent 32-bit float +typedef double F64; ///< Compiler independent 64-bit float + +struct EmptyType {}; ///< "Null" type used by templates + +#define TORQUE_UNUSED(var) (void)var + +//------------------------------------------------------------------------------ +//------------------------------------- String Types + +typedef char UTF8; ///< Compiler independent 8 bit Unicode encoded character +typedef unsigned short UTF16; ///< Compiler independent 16 bit Unicode encoded character +typedef unsigned int UTF32; ///< Compiler independent 32 bit Unicode encoded character + +typedef const char* StringTableEntry; + +//------------------------------------------------------------------------------ +//-------------------------------------- Type constants... +#define __EQUAL_CONST_F F32(0.000001) ///< Constant float epsilon used for F32 comparisons + +extern const F32 Float_Inf; +static const F32 Float_One = F32(1.0); ///< Constant float 1.0 +static const F32 Float_Half = F32(0.5); ///< Constant float 0.5 +static const F32 Float_Zero = F32(0.0); ///< Constant float 0.0 +static const F32 Float_Pi = F32(3.14159265358979323846); ///< Constant float PI +static const F32 Float_2Pi = F32(2.0 * 3.14159265358979323846); ///< Constant float 2*PI +static const F32 Float_InversePi = F32(1.0 / 3.14159265358979323846); ///< Constant float 1 / PI +static const F32 Float_HalfPi = F32(0.5 * 3.14159265358979323846); ///< Constant float 1/2 * PI +static const F32 Float_2InversePi = F32(2.0 / 3.14159265358979323846);///< Constant float 2 / PI +static const F32 Float_Inverse2Pi = F32(0.5 / 3.14159265358979323846);///< Constant float 0.5 / PI + +static const F32 Float_Sqrt2 = F32(1.41421356237309504880f); ///< Constant float sqrt(2) +static const F32 Float_SqrtHalf = F32(0.7071067811865475244008443f); ///< Constant float sqrt(0.5) + +static const S8 S8_MIN = S8(-128); ///< Constant Min Limit S8 +static const S8 S8_MAX = S8(127); ///< Constant Max Limit S8 +static const U8 U8_MAX = U8(255); ///< Constant Max Limit U8 + +static const S16 S16_MIN = S16(-32768); ///< Constant Min Limit S16 +static const S16 S16_MAX = S16(32767); ///< Constant Max Limit S16 +static const U16 U16_MAX = U16(65535); ///< Constant Max Limit U16 + +static const S32 S32_MIN = S32(-2147483647 - 1); ///< Constant Min Limit S32 +static const S32 S32_MAX = S32(2147483647); ///< Constant Max Limit S32 +static const U32 U32_MAX = U32(0xffffffff); ///< Constant Max Limit U32 + +static const F32 F32_MIN = F32(1.175494351e-38F); ///< Constant Min Limit F32 +static const F32 F32_MAX = F32(3.402823466e+38F); ///< Constant Max Limit F32 + +// define all the variants of Offset that we might use +#define _Offset_Normal(x, cls) ((dsize_t)((const char *)&(((cls *)1)->x)-(const char *)1)) +#define _Offset_Variant_1(x, cls) ((int)(&((cls *)1)->x) - 1) +#define _Offset_Variant_2(x, cls) offsetof(cls, x) // also requires #include + +//-------------------------------------- +// Identify the compiler being used + +// PC-lint +#if defined(_lint) +# include "platform/types.lint.h" +// Metrowerks CodeWarrior +#elif defined(__MWERKS__) +# include "platform/types.codewarrior.h" +// Microsoft Visual C++/Visual.NET +#elif defined(_MSC_VER) +# include "platform/types.visualc.h" +// GNU GCC +#elif defined(__GNUC__) +# include "platform/types.gcc.h" +#else +# error "Unknown Compiler" +#endif + +/// Integral type matching the host's memory address width. +#ifdef TORQUE_64BITS + typedef U64 MEM_ADDRESS; +#else + typedef U32 MEM_ADDRESS; +#endif + +//-------------------------------------- Some all-around useful inlines and globals +// + +/// Returns power of 2 number which is as small as possible but +/// still greater than or equal to input number. Note: returns 0 +/// for an input of 0 even though that is not a power of 2. +/// @param num Any U32 +inline U32 getNextPow2(U32 num) +{ + // Taken from: http://graphics.stanford.edu/~seander/bithacks.html + + num--; + num |= num >> 1; + num |= num >> 2; + num |= num >> 4; + num |= num >> 8; + num |= num >> 16; + num++; + + return num; +} + +/// Return integer log2 of input number (rounding down). So, e.g., +/// getBinLog2(7) == 2 whereas getBinLog2(8) == 3. If known +/// @param num Any U32 +/// @param knownPow2 Is num a known power of 2? +inline U32 getBinLog2(U32 num, bool knownPow2 = false) +{ + // Taken from: http://graphics.stanford.edu/~seander/bithacks.html + + static const U32 MultiplyDeBruijnBitPosition[32] = + { + 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, + 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 + }; + + if (!knownPow2) + { + num |= num >> 1; // first round down to power of 2 + num |= num >> 2; + num |= num >> 4; + num |= num >> 8; + num |= num >> 16; + num = (num >> 1) + 1; + } + + return MultiplyDeBruijnBitPosition[(num * 0x077CB531UL) >> 27]; +} + +/// Determines if the given U32 is some 2^n +/// @param num Any U32 +/// @returns true if in_num is a power of two, otherwise false +inline bool isPow2(const U32 num) +{ + return (num & (num - 1)) == 0; +} + +/// Determines the binary logarithm of the next greater power of two of the input number. +inline U32 getNextBinLog2(U32 number) +{ + return getBinLog2(number) + (isPow2(number) ? 0 : 1); +} + +//----------------Many versions of min and max------------- +//---not using template functions because MS VC++ chokes--- + +/// Returns the lesser of the two parameters: a & b. +inline U32 getMin(U32 a, U32 b) +{ + return a>b ? b : a; +} + +/// Returns the lesser of the two parameters: a & b. +inline U16 getMin(U16 a, U16 b) +{ + return a>b ? b : a; +} + +/// Returns the lesser of the two parameters: a & b. +inline U8 getMin(U8 a, U8 b) +{ + return a>b ? b : a; +} + +/// Returns the lesser of the two parameters: a & b. +inline S32 getMin(S32 a, S32 b) +{ + return a>b ? b : a; +} + +/// Returns the lesser of the two parameters: a & b. +inline S16 getMin(S16 a, S16 b) +{ + return a>b ? b : a; +} + +/// Returns the lesser of the two parameters: a & b. +inline S8 getMin(S8 a, S8 b) +{ + return a>b ? b : a; +} + +/// Returns the lesser of the two parameters: a & b. +inline float getMin(float a, float b) +{ + return a>b ? b : a; +} + +/// Returns the lesser of the two parameters: a & b. +inline double getMin(double a, double b) +{ + return a>b ? b : a; +} + +/// Returns the greater of the two parameters: a & b. +inline U32 getMax(U32 a, U32 b) +{ + return a>b ? a : b; +} + +/// Returns the greater of the two parameters: a & b. +inline U16 getMax(U16 a, U16 b) +{ + return a>b ? a : b; +} + +/// Returns the greater of the two parameters: a & b. +inline U8 getMax(U8 a, U8 b) +{ + return a>b ? a : b; +} + +/// Returns the greater of the two parameters: a & b. +inline S32 getMax(S32 a, S32 b) +{ + return a>b ? a : b; +} + +/// Returns the greater of the two parameters: a & b. +inline S16 getMax(S16 a, S16 b) +{ + return a>b ? a : b; +} + +/// Returns the greater of the two parameters: a & b. +inline S8 getMax(S8 a, S8 b) +{ + return a>b ? a : b; +} + +/// Returns the greater of the two parameters: a & b. +inline float getMax(float a, float b) +{ + return a>b ? a : b; +} + +/// Returns the greater of the two parameters: a & b. +inline double getMax(double a, double b) +{ + return a>b ? a : b; +} + +//-------------------------------------- Use this instead of Win32 FOURCC() +// macro... +// +#define makeFourCCTag(ch0, ch1, ch2, ch3) \ + (((U32(ch0) & 0xFF) << 0) | \ + ((U32(ch1) & 0xFF) << 8) | \ + ((U32(ch2) & 0xFF) << 16) | \ + ((U32(ch3) & 0xFF) << 24) ) + +#define makeFourCCString(ch0, ch1, ch2, ch3) { ch0, ch1, ch2, ch3 } + +#define BIT(x) (1 << (x)) ///< Returns value with bit x set (2^x) + +#if defined(TORQUE_OS_WIN32) +#define STDCALL __stdcall +#else +#define STDCALL +#endif + +#endif //_TORQUE_TYPES_H_ diff --git a/Engine/source/platform/types.lint.h b/Engine/source/platform/types.lint.h new file mode 100644 index 000000000..fff33d28d --- /dev/null +++ b/Engine/source/platform/types.lint.h @@ -0,0 +1,58 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef TORQUE_TYPES_LINT_H_ +#define TORQUE_TYPES_LINT_H_ + +typedef signed long long S64; +typedef unsigned long long U64; + +typedef unsigned int dsize_t; + +struct FileTime +{ + U32 v1; + U32 v2; +}; + +#define TORQUE_OS_STRING "Lint" +#define TORQUE_CPU_STRING "x86" +#define TORQUE_LITTLE_ENDIAN +#define TORQUE_SUPPORTS_NASM +#define TORQUE_SUPPORTS_VC_INLINE_X86_ASM +#define TORQUE_OS_WIN32 +#define TORQUE_COMPILER_VISUALC 1500 + +#ifndef FN_CDECL +#define FN_CDECL +#endif + +#ifndef Offset +#define Offset(x, cls) _Offset_Normal(x, cls) +#define OffsetNonConst(x, cls) _Offset_Normal(x, cls) +#endif + +#ifndef NULL +#define NULL 0 +#endif + +#endif diff --git a/Engine/source/platform/types.mac.h b/Engine/source/platform/types.mac.h new file mode 100644 index 000000000..e700afac0 --- /dev/null +++ b/Engine/source/platform/types.mac.h @@ -0,0 +1,51 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TYPESPPC_H_ +#define _TYPESPPC_H_ + +///< Calling convention +#define FN_CDECL +#define STDCALL + +// size_t is needed to overload new +// size_t tends to be OS and compiler specific and may need to +// be if/def'ed in the future +typedef unsigned long dsize_t; + + +/** Platform dependent file date-time structure. The defination of this structure + * will likely be different for each OS platform. + * On the PPC is a 64-bit structure for storing the date/time for a file + */ + +// 64-bit structure for storing the date/time for a file +// The date and time, specified in seconds since the unix epoch. +// NOTE: currently, this is only 32-bits in value, so the upper 32 are all zeroes. +typedef U64 FileTime; + +#ifndef NULL +# define NULL (0) +#endif + + +#endif //_TYPESPPC_H_ diff --git a/Engine/source/platform/types.posix.h b/Engine/source/platform/types.posix.h new file mode 100644 index 000000000..faa708af7 --- /dev/null +++ b/Engine/source/platform/types.posix.h @@ -0,0 +1,46 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TYPESPOSIX_H_ +#define _TYPESPOSIX_H_ + + +#define FN_CDECL ///< Calling convention + +// size_t is needed to overload new +// size_t tends to be OS and compiler specific and may need to +// be if/def'ed in the future +typedef unsigned int dsize_t; + + +/** Platform dependent file date-time structure. The defination of this structure + * will likely be different for each OS platform. + */ +typedef S32 FileTime; + + +#ifndef NULL +# define NULL (0) +#endif + + +#endif //_TYPESPOSIX_H_ diff --git a/Engine/source/platform/types.ppc.h b/Engine/source/platform/types.ppc.h new file mode 100644 index 000000000..c28f70a60 --- /dev/null +++ b/Engine/source/platform/types.ppc.h @@ -0,0 +1,50 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TYPESPPC_H_ +#define _TYPESPPC_H_ + +///< Calling convention +#define FN_CDECL + +// size_t is needed to overload new +// size_t tends to be OS and compiler specific and may need to +// be if/def'ed in the future +typedef unsigned long dsize_t; + + +/** Platform dependent file date-time structure. The defination of this structure + * will likely be different for each OS platform. + * On the PPC is a 64-bit structure for storing the date/time for a file + */ + +// 64-bit structure for storing the date/time for a file +// The date and time, specified in seconds since the unix epoch. +// NOTE: currently, this is only 32-bits in value, so the upper 32 are all zeroes. +typedef U64 FileTime; + +#ifndef NULL +# define NULL (0) +#endif + + +#endif //_TYPESPPC_H_ diff --git a/Engine/source/platform/types.visualc.h b/Engine/source/platform/types.visualc.h new file mode 100644 index 000000000..baedd85dd --- /dev/null +++ b/Engine/source/platform/types.visualc.h @@ -0,0 +1,99 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef INCLUDED_TYPES_VISUALC_H +#define INCLUDED_TYPES_VISUALC_H + + +// For more information on VisualC++ predefined macros +// http://support.microsoft.com/default.aspx?scid=kb;EN-US;q65472 + +//-------------------------------------- +// Types +typedef signed _int64 S64; +typedef unsigned _int64 U64; + + +//-------------------------------------- +// Compiler Version +#define TORQUE_COMPILER_VISUALC _MSC_VER + +//-------------------------------------- +// Identify the compiler string +#if _MSC_VER < 1200 + // No support for old compilers +# error "VC: Minimum VisualC++ 6.0 or newer required" +#else _MSC_VER >= 1200 +# define TORQUE_COMPILER_STRING "VisualC++" +#endif + + +//-------------------------------------- +// Identify the Operating System +#if _XBOX_VER >= 200 +# define TORQUE_OS_STRING "Xenon" +# ifndef TORQUE_OS_XENON +# define TORQUE_OS_XENON +# endif +# include "platform/types.xenon.h" +#elif defined( _XBOX_VER ) +# define TORQUE_OS_STRING "Xbox" +# define TORQUE_OS_XBOX +# include "platform/types.win32.h" +#elif defined(_WIN32) +# define TORQUE_OS_STRING "Win32" +# define TORQUE_OS_WIN32 +# include "platform/types.win32.h" +#else +# error "VC: Unsupported Operating System" +#endif + + +//-------------------------------------- +// Identify the CPU +#if defined(_M_IX86) +# define TORQUE_CPU_STRING "x86" +# define TORQUE_CPU_X86 +# define TORQUE_LITTLE_ENDIAN +# define TORQUE_SUPPORTS_NASM +# define TORQUE_SUPPORTS_VC_INLINE_X86_ASM +#elif defined(TORQUE_OS_XENON) +# define TORQUE_CPU_STRING "ppc" +# define TORQUE_CPU_PPC +# define TORQUE_BIG_ENDIAN +#else +# error "VC: Unsupported Target CPU" +#endif + +#ifndef FN_CDECL +# define FN_CDECL __cdecl ///< Calling convention +#endif + +#define for if(false) {} else for ///< Hack to work around Microsoft VC's non-C++ compliance on variable scoping + +// disable warning caused by memory layer +// see msdn.microsoft.com "Compiler Warning (level 1) C4291" for more details +#pragma warning(disable: 4291) + + +#endif // INCLUDED_TYPES_VISUALC_H + diff --git a/Engine/source/platform/types.win32.h b/Engine/source/platform/types.win32.h new file mode 100644 index 000000000..d95d3cec5 --- /dev/null +++ b/Engine/source/platform/types.win32.h @@ -0,0 +1,49 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TYPESWIN32_H_ +#define _TYPESWIN32_H_ + + +#define FN_CDECL __cdecl ///< Calling convention + +// size_t is needed to overload new +// size_t tends to be OS and compiler specific and may need to +// be if/def'ed in the future +typedef unsigned int dsize_t; + + +/// Platform dependent file date-time structure. The definition of this structure +/// will likely be different for each OS platform. +struct FileTime +{ + U32 v1; + U32 v2; +}; + + +#ifndef NULL +# define NULL 0 +#endif + + +#endif //_TYPESWIN32_H_ diff --git a/Engine/source/platform/types.xenon.h b/Engine/source/platform/types.xenon.h new file mode 100644 index 000000000..5dc47547c --- /dev/null +++ b/Engine/source/platform/types.xenon.h @@ -0,0 +1,49 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TYPESXENON_H_ +#define _TYPESXENON_H_ + +///< Calling convention +#ifdef FN_CDECL +# undef FN_CDECL +#endif +#define FN_CDECL __cdecl + +// size_t is needed to overload new +// size_t tends to be OS and compiler specific and may need to +// be if/def'ed in the future +typedef size_t dsize_t; + +struct FileTime +{ + U32 v1; + U32 v2; +}; + + +#ifndef NULL +# define NULL (0) +#endif + + +#endif //_TYPESXENON_H_ \ No newline at end of file diff --git a/Engine/source/platform/typesLinux.h b/Engine/source/platform/typesLinux.h new file mode 100644 index 000000000..f3b851028 --- /dev/null +++ b/Engine/source/platform/typesLinux.h @@ -0,0 +1,80 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TYPESLINUX_H_ +#define _TYPESLINUX_H_ + +/* eek. */ +#ifndef NULL +#define NULL 0 +#endif + +#define PLATFORM_LITTLE_ENDIAN + +#define FN_CDECL + +typedef signed char S8; +typedef unsigned char U8; + +typedef signed short S16; +typedef unsigned short U16; + +typedef signed int S32; +typedef unsigned int U32; + +typedef signed long long S64; +typedef unsigned long long U64; + +typedef float F32; +typedef double F64; + +typedef unsigned int dsize_t; + +typedef const char* StringTableEntry; + +typedef S32 FileTime; + +#define __EQUAL_CONST_F F32(0.000001) + +static const F32 Float_One = F32(1.0); +static const F32 Float_Half = F32(0.5); +static const F32 Float_Zero = F32(0.0); +static const F32 Float_Pi = F32(3.14159265358979323846); +static const F32 Float_2Pi = F32(2.0 * 3.14159265358979323846); + +static const S8 S8_MIN = S8(-128); +static const S8 S8_MAX = S8(127); +static const U8 U8_MAX = U8(255); + +static const S16 S16_MIN = S16(-32768); +static const S16 S16_MAX = S16(32767); +static const U16 U16_MAX = U16(65535); + +static const S32 S32_MIN = S32(-2147483647 - 1); +static const S32 S32_MAX = S32(2147483647); +static const U32 U32_MAX = U32(0xffffffff); + +static const F32 F32_MAX = F32(3.402823466e+38F); +static const F32 F32_MIN = F32(1.175494351e-38F); + + +#endif diff --git a/Engine/source/platform/typesPPC.h b/Engine/source/platform/typesPPC.h new file mode 100644 index 000000000..2ec1d1cfd --- /dev/null +++ b/Engine/source/platform/typesPPC.h @@ -0,0 +1,99 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TYPES_PPC_H_ +#define _TYPES_PPC_H_ + +// We have to check this. Since every file will eventually wind up including +// this header, but not every header includes a windows or system header... +// +#ifndef NULL +#define NULL 0 +#endif + +// Let's just have this in a nice central location. Again, since every file +// will wind up including this file, we can affect compilation most effectively +// from this location. +// +#define PLATFORM_BIG_ENDIAN + +#define FN_CDECL + + +//------------------------------------------------------------------------------ +//-------------------------------------- Basic Types... + +typedef signed char S8; +typedef unsigned char U8; + +typedef signed short S16; +typedef unsigned short U16; + +typedef signed int S32; +typedef unsigned int U32; + +typedef signed long long S64; +typedef unsigned long long U64; + +typedef float F32; +typedef double F64; + +// size_t is needed to overload new +// size_t tends to be OS and compiler specific and may need to +// be if/def'ed in the future +typedef unsigned long dsize_t; + +typedef const char* StringTableEntry; + +// 64-bit structure for storing the date/time for a file +// The date and time, specified in seconds since the unix epoch. +// NOTE: currently, this is only 32-bits in value, so the upper 32 are all zeroes. +typedef U64 FileTime; + + +//------------------------------------------------------------------------------ +//-------------------------------------- Type constants... +#define __EQUAL_CONST_F F32(0.000001) + +static const F32 Float_One = F32(1.0); +static const F32 Float_Half = F32(0.5); +static const F32 Float_Zero = F32(0.0); +static const F32 Float_Pi = F32(3.14159265358979323846); +static const F32 Float_2Pi = F32(2.0 * 3.14159265358979323846); + +static const S8 S8_MIN = S8(-128); +static const S8 S8_MAX = S8(127); +static const U8 U8_MAX = U8(255); + +static const S16 S16_MIN = S16(-32768); +static const S16 S16_MAX = S16(32767); +static const U16 U16_MAX = U16(65535); + +static const S32 S32_MIN = S32(-2147483647 - 1); +static const S32 S32_MAX = S32(2147483647); +static const U32 U32_MAX = U32(0xffffffff); + +static const F32 F32_MAX = F32(3.402823466e+38F); +static const F32 F32_MIN = F32(1.175494351e-38F); + + +#endif //_TYPES_PPC_H_ diff --git a/Engine/source/platform/typesWin32.h b/Engine/source/platform/typesWin32.h new file mode 100644 index 000000000..247baa355 --- /dev/null +++ b/Engine/source/platform/typesWin32.h @@ -0,0 +1,123 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TYPESWIN32_H_ +#define _TYPESWIN32_H_ + +// We have to check this. Since every file will eventually wind up including +// this header, but not every header includes a windows or system header... +// +#ifndef NULL +#define NULL 0 +#endif + +// Let's just have this in a nice central location. Again, since every file +// will wind up including this file, we can affect compilation most effectively +// from this location. +// +#define PLATFORM_LITTLE_ENDIAN ///< Signals this platfrom is Little Endian + + +#define FN_CDECL __cdecl ///< Calling convention + +//------------------------------------------------------------------------------ +//-------------------------------------- Basic Types... + +typedef signed char S8; ///< Compiler independent Signed Char +typedef unsigned char U8; ///< Compiler independent Unsigned Char + +typedef signed short S16; ///< Compiler independent Signed 16-bit short +typedef unsigned short U16; ///< Compiler independent Unsigned 16-bit short + +typedef signed int S32; ///< Compiler independent Signed 32-bit integer +typedef unsigned int U32; ///< Compiler independent Unsigned 32-bit integer + +#ifdef __BORLANDC__ +typedef signed __int64 S64; ///< Compiler independent Signed 64-bit integer +typedef unsigned __int64 U64; ///< Compiler independent Unsigned 64-bit integer + +#elif defined(__MWERKS__) // This has to go before MSC_VER since CodeWarrior defines MSC_VER too +typedef signed long long S64; ///< Compiler independent Signed 64-bit integer +typedef unsigned long long U64; ///< Compiler independent Unsigned 64-bit integer + +#elif defined(_MSC_VER) +typedef signed _int64 S64; ///< Compiler independent Signed 64-bit integer +typedef unsigned _int64 U64; ///< Compiler independent Unsigned 64-bit integer +#pragma warning(disable: 4291) // disable warning caused by memory layer... +#pragma warning(disable: 4996) // turn off "deprecation" warnings + +#else +typedef signed long long S64; ///< Compiler independent Signed 64-bit integer +typedef unsigned long long U64; ///< Compiler independent Unsigned 64-bit integer +#endif + +typedef float F32; ///< Compiler independent 32-bit float +typedef double F64; ///< Compiler independent 64-bit float + +// size_t is needed to overload new +// size_t tends to be OS and compiler specific and may need to +// be if/def'ed in the future +typedef unsigned int dsize_t; + +typedef const char* StringTableEntry; + +/* Platform dependent file date-time structure. The defination of this structure + * will likely be different for each OS platform. + */ +struct FileTime +{ + U32 v1; + U32 v2; +}; + +//------------------------------------------------------------------------------ +//-------------------------------------- Type constants... +#define __EQUAL_CONST_F F32(0.000001) ///< Constant float epsilon used for F32 comparisons + +static const F32 Float_One = F32(1.0); ///< Constant float 1.0 +static const F32 Float_Half = F32(0.5); ///< Constant float 0.5 +static const F32 Float_Zero = F32(0.0); ///< Constant float 0.0 +static const F32 Float_Pi = F32(3.14159265358979323846); ///< Constant float PI +static const F32 Float_2Pi = F32(2.0 * 3.14159265358979323846); ///< Constant float 2*PI + +static const S8 S8_MIN = S8(-128); ///< Constant Min Limit S8 +static const S8 S8_MAX = S8(127); ///< Constant Max Limit S8 +static const U8 U8_MAX = U8(255); ///< Constant Max Limit U8 + +static const S16 S16_MIN = S16(-32768); ///< Constant Min Limit S16 +static const S16 S16_MAX = S16(32767); ///< Constant Max Limit S16 +static const U16 U16_MAX = U16(65535); ///< Constant Max Limit U16 + +static const S32 S32_MIN = S32(-2147483647 - 1); ///< Constant Min Limit S32 +static const S32 S32_MAX = S32(2147483647); ///< Constant Max Limit S32 +static const U32 U32_MAX = U32(0xffffffff); ///< Constant Max Limit U32 + +static const F32 F32_MIN = F32(1.175494351e-38F); ///< Constant Min Limit F32 +static const F32 F32_MAX = F32(3.402823466e+38F); ///< Constant Max Limit F32 + + +#ifdef _MSC_VER +#define for if(false) {} else for ///< Hack to work around Microsoft VC's non-C++ compliance on variable scoping +#endif + + +#endif //_NTYPES_H_ diff --git a/Engine/source/platform/typesX86UNIX.h b/Engine/source/platform/typesX86UNIX.h new file mode 100644 index 000000000..56ba18679 --- /dev/null +++ b/Engine/source/platform/typesX86UNIX.h @@ -0,0 +1,80 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TYPESX86UNIX_H_ +#define _TYPESX86UNIX_H_ + +/* eek. */ +#ifndef NULL +#define NULL 0 +#endif + +#define PLATFORM_LITTLE_ENDIAN + +#define FN_CDECL + +typedef signed char S8; +typedef unsigned char U8; + +typedef signed short S16; +typedef unsigned short U16; + +typedef signed int S32; +typedef unsigned int U32; + +typedef signed long long S64; +typedef unsigned long long U64; + +typedef float F32; +typedef double F64; + +typedef unsigned int dsize_t; + +typedef const char* StringTableEntry; + +typedef S32 FileTime; + +#define __EQUAL_CONST_F F32(0.000001) + +static const F32 Float_One = F32(1.0); +static const F32 Float_Half = F32(0.5); +static const F32 Float_Zero = F32(0.0); +static const F32 Float_Pi = F32(3.14159265358979323846); +static const F32 Float_2Pi = F32(2.0 * 3.14159265358979323846); + +static const S8 S8_MIN = S8(-128); +static const S8 S8_MAX = S8(127); +static const U8 U8_MAX = U8(255); + +static const S16 S16_MIN = S16(-32768); +static const S16 S16_MAX = S16(32767); +static const U16 U16_MAX = U16(65535); + +static const S32 S32_MIN = S32(-2147483647 - 1); +static const S32 S32_MAX = S32(2147483647); +static const U32 U32_MAX = U32(0xffffffff); + +static const F32 F32_MAX = F32(3.402823466e+38F); +static const F32 F32_MIN = F32(1.175494351e-38F); + + +#endif diff --git a/Engine/source/platform/typetraits.h b/Engine/source/platform/typetraits.h new file mode 100644 index 000000000..a686ab782 --- /dev/null +++ b/Engine/source/platform/typetraits.h @@ -0,0 +1,425 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TYPETRAITS_H_ +#define _TYPETRAITS_H_ + +#ifndef _PLATFORM_H_ +# include "platform/platform.h" +#endif + + +/// @file +/// Template definitions for introspecting type properties. + + +//-------------------------------------------------------------------------- +// Type Predicating. +//-------------------------------------------------------------------------- + +struct TrueType {}; +struct FalseType {}; + +template< typename T > +inline bool IsTrueType() +{ + return false; +} +template<> +inline bool IsTrueType< TrueType >() +{ + return true; +} + +template< typename T > +inline bool IsFalseType() +{ + return false; +} +template<> +inline bool IsFalseType< FalseType >() +{ + return true; +} + + +template< typename T, typename IfTrue, typename IfFalse > +struct IfTrueType : public IfFalse {}; +template< typename IfTrue, typename IfFalse > +struct IfTrueType< TrueType, IfTrue, IfFalse > : public IfTrue {}; + +template< typename T, typename IfTrue, typename IfFalse > +struct IfFalseType : public IfTrue {}; +template< typename IfTrue, typename IfFalse > +struct IfFalseType< FalseType, IfTrue, IfFalse > : public IfFalse {}; + +//-------------------------------------------------------------------------- +// Construct. +//-------------------------------------------------------------------------- + +struct _ConstructDefault +{ + template< typename T > + static T single() + { + return T(); + } + template< typename T, typename A > + static T single( A a ) + { + return T( a ); + } + template< typename T, typename A, typename B > + static T single( A a, B b ) + { + return T( a, b ); + } + template< typename T > + static void array( T* ptr, U32 num ) + { + constructArrayInPlace< T >( ptr, num ); + } + template< typename T, typename A > + static void array( T* ptr, U32 num, A a ) + { + for( U32 i = 0; i < num; ++ i ) + ptr[ i ] = single< T >( a ); + } +}; +struct _ConstructPrim +{ + template< typename T > + static T single() + { + return 0; + } + template< typename T, typename A > + static T single( T a ) + { + return a; + } + template< typename T > + static void array( T* ptr, U32 num ) + { + dMemset( ptr, 0, num * sizeof( T ) ); + } + template< typename T, typename A > + static void array( T* ptr, U32 num, T a ) + { + for( U32 i = 0; i < num; ++ i ) + ptr[ i ] = a; + } +}; +struct _ConstructPtr +{ + template< typename T > + static T* single() + { + return new T; + } + template< typename T, typename A > + static T* single( A a ) + { + return new T( a ); + } + template< typename T, typename A, typename B > + static T* single( A a, B b ) + { + return new T( a, b ); + } + template< typename T > + static void array( T** ptr, U32 num ) + { + for( U32 i = 0; i < num; ++ i ) + ptr[ i ] = single< T >(); + } + template< typename T, typename A > + static void array( T** ptr, U32 num, A a ) + { + for( U32 i = 0; i < num; ++ i ) + ptr[ i ] = single< T >( a ); + } +}; + +//-------------------------------------------------------------------------- +// Destruct. +//-------------------------------------------------------------------------- + +struct _DestructDefault +{ + template< typename T > + static void single( T& val ) + { + val.~T(); + } + template< typename T > + static void array( T* ptr, U32 num ) + { + for( U32 i = 0; i < num; ++ i ) + single< T >( ptr[ i ] ); + } +}; +struct _DestructPrim +{ + template< typename T > + static void single( T& val ) {} + template< typename T > + static void array( T* ptr, U32 num ) {} +}; +struct _DestructPtr +{ + template< typename T > + static void single( T*& val ) + { + delete val; + val = NULL; + } + template< typename T > + static void array( T* ptr, U32 num ) + { + for( U32 i = 0; i < num; ++ i ) + single< T >( ptr[ i ] ); + } +}; + +//-------------------------------------------------------------------------- +// TypeTraits. +//-------------------------------------------------------------------------- + +template< typename T > +struct _TypeTraits +{ + typedef T BaseType; + typedef const T ConstType; + typedef _ConstructDefault Construct; + typedef _DestructDefault Destruct; +}; +template< typename T > +struct _TypeTraits< T* > +{ + typedef T BaseType; + typedef const T ConstType; + typedef _ConstructPtr Construct; + typedef _DestructPtr Destruct; + + template< typename A > + static bool isTaggedPtr( A* ptr ) { return ( U32( ptr ) & 0x1 ); } //TODO: 64bits + template< typename A > + static A* getTaggedPtr( A* ptr ) { return ( A* ) ( U32( ptr ) | 0x1 ); } //TODO: 64bits + template< typename A > + static A* getUntaggedPtr( A* ptr ) { return ( A* ) ( U32( ptr ) & 0xFFFFFFFE ); } //TODO: 64bit +}; + +template< typename T > +struct TypeTraits : public TypeTraits< typename T::Parent > +{ + typedef T BaseType; + typedef const T ConstType; +}; +template< typename T > +struct TypeTraits< T* > : public TypeTraits< typename T::Parent* > +{ + typedef T BaseType; + typedef const T ConstType; +}; +template< typename T > +struct TypeTraits< T* const > : public TypeTraits< typename T::Parent* > +{ + typedef T BaseType; + typedef const T ConstType; +}; +template<> +struct TypeTraits< void > : public _TypeTraits< void > {}; +template<> +struct TypeTraits< void* > : public _TypeTraits< void* > {}; +template<> +struct TypeTraits< void* const > : public _TypeTraits< void* > {}; + +// Type traits for primitive types. + +template<> +struct TypeTraits< bool > : public _TypeTraits< bool > +{ + typedef _ConstructPrim Construct; + typedef _DestructPrim Destruct; +}; +template<> +struct TypeTraits< S8 > : public _TypeTraits< S8 > +{ + static const S8 MIN = S8_MIN; + static const S8 MAX = S8_MAX; + static const S8 ZERO = 0; + typedef _ConstructPrim Construct; + typedef _DestructPrim Destruct; +}; +template<> +struct TypeTraits< U8 > : public _TypeTraits< U8 > +{ + static const U8 MIN = 0; + static const U8 MAX = U8_MAX; + static const U8 ZERO = 0; + typedef _ConstructPrim Construct; + typedef _DestructPrim Destruct; +}; +template<> +struct TypeTraits< S16 > : public _TypeTraits< S16 > +{ + static const S16 MIN = S16_MIN; + static const S16 MAX = S16_MAX; + static const S16 ZERO = 0; + typedef _ConstructPrim Construct; + typedef _DestructPrim Destruct; +}; +template<> +struct TypeTraits< U16 > : public _TypeTraits< U16 > +{ + static const U16 MIN = 0; + static const U16 MAX = U16_MAX; + static const U16 ZERO = 0; + typedef _ConstructPrim Construct; + typedef _DestructPrim Destruct; +}; +template<> +struct TypeTraits< S32 > : public _TypeTraits< S32 > +{ + static const S32 MIN = S32_MIN; + static const S32 MAX = S32_MAX; + static const S32 ZERO = 0; + typedef _ConstructPrim Construct; + typedef _DestructPrim Destruct; +}; +template<> +struct TypeTraits< U32 > : public _TypeTraits< U32 > +{ + static const U32 MIN = 0; + static const U32 MAX = U32_MAX; + static const U32 ZERO = 0; + typedef _ConstructPrim Construct; + typedef _DestructPrim Destruct; +}; +template<> +struct TypeTraits< F32 > : public _TypeTraits< F32 > +{ + static const F32 MIN; + static const F32 MAX; + static const F32 ZERO; + typedef _ConstructPrim Construct; + typedef _DestructPrim Destruct; +}; + +//-------------------------------------------------------------------------- +// Utilities. +//-------------------------------------------------------------------------- + +template< typename T > +inline T constructSingle() +{ + typedef typename TypeTraits< T >::BaseType Type; + typedef typename TypeTraits< T >::Construct Construct; + return Construct::template single< Type >(); +} +template< typename T, typename A > +inline T constructSingle( A a ) +{ + typedef typename TypeTraits< T >::BaseType BaseType; + typedef typename TypeTraits< T >::Construct Construct; + return Construct::template single< BaseType >( a ); +} +template< typename T, typename A, typename B > +inline T constructSingle( A a, B b ) +{ + typedef typename TypeTraits< T >::BaseType BaseType; + typedef typename TypeTraits< T >::Construct Construct; + return Construct::template single< BaseType >( a, b ); +} +template< typename T > +inline void constructArray( T* ptr, U32 num ) +{ + typedef typename TypeTraits< T >::BaseType BaseType; + typedef typename TypeTraits< T >::Construct Construct; + Construct::template array< BaseType >( ptr, num ); +} +template< typename T, typename A > +inline void constructArray( T* ptr, U32 num, A a ) +{ + typedef typename TypeTraits< T >::BaseType BaseType; + typedef typename TypeTraits< T >::Construct Construct; + Construct::template array< BaseType >( ptr, num, a ); +} +template< typename T > +inline void destructSingle( T& val ) +{ + typedef typename TypeTraits< T >::BaseType BaseType; + typedef typename TypeTraits< T >::Destruct Destruct; + Destruct::template single< BaseType >( val ); +} +template< typename T > +inline void destructArray( T* ptr, U32 num ) +{ + typedef typename TypeTraits< T >::BaseType BaseType; + typedef typename TypeTraits< T >::Destruct Destruct; + Destruct::template array< BaseType >( ptr, num ); +} + +template< typename T> +inline T& Deref( T& val ) +{ + return val; +} +template< typename T > +inline T& Deref( T* ptr ) +{ + return *ptr; +} + +/// Delete a single object policy. +struct DeleteSingle +{ + template + static void destroy(T *ptr) { delete ptr; } +}; + +/// Delete an array of objects policy. +struct DeleteArray +{ + template + static void destroy(T *ptr) { delete [] ptr; } +}; + +/// +template< typename T > +struct ValueHolder +{ + T value; + + ValueHolder( const T& value ) + : value( value ) {} + + operator T() const { return value; } +}; +template<> +struct ValueHolder< void > +{ + ValueHolder() {} +}; + +#endif // _TYPETRAITS_H_ diff --git a/Engine/source/platformMac/cursors/resizeNESW.png b/Engine/source/platformMac/cursors/resizeNESW.png new file mode 100644 index 000000000..cd9e83a00 Binary files /dev/null and b/Engine/source/platformMac/cursors/resizeNESW.png differ diff --git a/Engine/source/platformMac/cursors/resizeNWSE.png b/Engine/source/platformMac/cursors/resizeNWSE.png new file mode 100644 index 000000000..b4bc196bd Binary files /dev/null and b/Engine/source/platformMac/cursors/resizeNWSE.png differ diff --git a/Engine/source/platformMac/cursors/resizeall.png b/Engine/source/platformMac/cursors/resizeall.png new file mode 100644 index 000000000..f1950da50 Binary files /dev/null and b/Engine/source/platformMac/cursors/resizeall.png differ diff --git a/Engine/source/platformMac/macApplication.h b/Engine/source/platformMac/macApplication.h new file mode 100644 index 000000000..f191b0e96 --- /dev/null +++ b/Engine/source/platformMac/macApplication.h @@ -0,0 +1,38 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// +// macApplication.h +// T3D +// +// Created by admin account on 2/1/08. +// Copyright 2008 __MyCompanyName__. All rights reserved. +// + +#import + + +@interface macApplication : NSApplication +{ +} + +@end diff --git a/Engine/source/platformMac/macApplication.mm b/Engine/source/platformMac/macApplication.mm new file mode 100644 index 000000000..bc58df02b --- /dev/null +++ b/Engine/source/platformMac/macApplication.mm @@ -0,0 +1,79 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "macApplication.h" +#include "windowManager/mac/macWindow.h" +#include "windowManager/mac/macView.h" +#include "console/console.h" + +@implementation macApplication + +- (void)sendEvent:(NSEvent*)theEvent +{ + if([theEvent type] == NSKeyUp) + { + if([theEvent modifierFlags] & NSCommandKeyMask) + { + // These will normally be blocked, but we wants them! + [[self delegate] keyUp:theEvent]; + return; + } + } + + MacWindow* window = [(GGMacView*)[self delegate] torqueWindow]; + if(window && window->isFullscreen()) + { + switch([theEvent type]) + { + case NSLeftMouseDown: + [[self delegate] mouseDown:theEvent]; + return; + case NSRightMouseDown: + [[self delegate] rightMouseDown:theEvent]; + return; + case NSLeftMouseUp: + [[self delegate] mouseUp:theEvent]; + return; + case NSRightMouseUp: + [[self delegate] rightMouseUp:theEvent]; + return; + case NSMouseMoved: + [[self delegate] mouseMoved:theEvent]; + return; + case NSLeftMouseDragged: + [[self delegate] mouseDragged:theEvent]; + return; + case NSRightMouseDragged: + [[self delegate] rightMouseDragged:theEvent]; + return; + case NSScrollWheel: + [[self delegate] scrollWheel:theEvent]; + return; + default: + break; + } + } + + [super sendEvent:theEvent]; +} + +@end diff --git a/Engine/source/platformMac/macCarbAsync.cpp b/Engine/source/platformMac/macCarbAsync.cpp new file mode 100644 index 000000000..4208f51c8 --- /dev/null +++ b/Engine/source/platformMac/macCarbAsync.cpp @@ -0,0 +1,64 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include +#include "platform/async/asyncUpdate.h" + + +AsyncUpdateThread::AsyncUpdateThread( String name, AsyncUpdateList* updateList ) + : Parent( 0, 0, false, false ), + mUpdateList( updateList ), + mName( name ) +{ + MPCreateEvent( ( MPEventID* ) &mUpdateEvent ); +} + +AsyncUpdateThread::~AsyncUpdateThread() +{ + MPDeleteEvent( *( ( MPEventID* ) &mUpdateEvent ) ) ; +} + +void AsyncUpdateThread::_waitForEventAndReset() +{ + MPWaitForEvent( *( ( MPEventID* ) &mUpdateEvent ), NULL, kDurationForever ); +} + +void AsyncUpdateThread::triggerUpdate() +{ + MPSetEvent( *( ( MPEventID* ) &mUpdateEvent ), 1 ); +} + +AsyncPeriodicUpdateThread::AsyncPeriodicUpdateThread + ( String name, AsyncUpdateList* updateList, U32 intervalMS ) + : Parent( name, updateList ), + mIntervalMS( intervalMS ) +{ +} + +AsyncPeriodicUpdateThread::~AsyncPeriodicUpdateThread() +{ +} + +void AsyncPeriodicUpdateThread::_waitForEventAndReset() +{ + MPWaitForEvent( *( ( MPEventID* ) &mUpdateEvent ), NULL, kDurationMillisecond * mIntervalMS ); +} diff --git a/Engine/source/platformMac/macCarbCPUInfo.cpp b/Engine/source/platformMac/macCarbCPUInfo.cpp new file mode 100644 index 000000000..f7b71f3ba --- /dev/null +++ b/Engine/source/platformMac/macCarbCPUInfo.cpp @@ -0,0 +1,285 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +#include "platformMac/platformMacCarb.h" +#include "platform/platformAssert.h" +#include "console/console.h" +#include "core/stringTable.h" + + +// Original code by Sean O'Brien (http://www.garagegames.com/community/forums/viewthread/81815). + + +// Reads sysctl() string value into buffer at DEST with maximum length MAXLEN +// Return: 0 on success, non-zero is error in accordance with stdlib and +int _getSysCTLstring(const char key[], char * dest, size_t maxlen) { + size_t len = 0; + int err; + // Call with NULL for 'dest' to have the required size stored in 'len'. If the 'key' + // doesn't exist, 'err' will be -1 and if all goes well, it will be 0. + err = sysctlbyname(key, NULL, &len, NULL, 0); + if (err == 0) { + AssertWarn((len <= maxlen), ("Insufficient buffer length for SYSCTL() read. Truncating.\n")); + if (len > maxlen) + len = maxlen; + // Call with actual pointers to 'dest' and clamped 'len' fields to perform the read. + err = sysctlbyname(key, dest, &len, NULL, 0); + } + return err; +} + +// TEMPLATED Reads sysctl() integer value into variable DEST of type T +// The two predominant types used are unsigned longs and unsiged long longs +// and the size of the argument is on a case-by-case value. As a "guide" the +// resources at Apple claim that any "byte count" or "frequency" values will +// be returned as ULL's and most everything else will be UL's. +// Return: 0 on success, non-zero is error in accordance with stdlib and +template +int _getSysCTLvalue(const char key[], T * dest) { + size_t len = 0; + int err; + // Call with NULL for 'dest' to get the size. If the 'key' doesn't exist, the + // 'err' returned will be -1, so 0 indicates success. + err = sysctlbyname(key, NULL, &len, NULL, 0); + if (err == 0) { + AssertFatal((len == sizeof(T)), "Mis-matched destination type for SYSCTL() read.\n"); + // We're just double-checking that we're being called with the correct type of + // pointer for 'dest' so we don't clobber anything nearby when writing back. + err = sysctlbyname(key, dest, &len, NULL, 0); + } + return err; +} + +Platform::SystemInfo_struct Platform::SystemInfo; + +#define BASE_MHZ_SPEED 0 + +void Processor::init() +{ + U32 procflags; + int err, cpufam, cputype, cpusub; + char buf[20]; + unsigned long lraw; + unsigned long long llraw; + + Con::printf( "System & Processor Information:" ); + + SInt32 MacVersion; + if( Gestalt( gestaltSystemVersion, &MacVersion ) == noErr ) + { + U32 revision = MacVersion & 0xf; + U32 minorVersion = ( MacVersion & 0xf0 ) >> 4; + U32 majorVersion = ( MacVersion & 0xff00 ) >> 8; + + Con::printf( " OSX Version: %x.%x.%x", majorVersion, minorVersion, revision ); + } + + err = _getSysCTLstring("kern.ostype", buf, sizeof(buf)); + if (err) + Con::printf( " Unable to determine OS type\n" ); + else + Con::printf( " Mac OS Kernel name: %s", buf); + + err = _getSysCTLstring("kern.osrelease", buf, sizeof(buf)); + if (err) + Con::printf( " Unable to determine OS release number\n" ); + else + Con::printf( " Mac OS Kernel version: %s", buf ); + + err = _getSysCTLvalue("hw.memsize", &llraw); + if (err) + Con::printf( " Unable to determine amount of physical RAM\n" ); + else + Con::printf( " Physical memory installed: %d MB", (llraw >> 20)); + + err = _getSysCTLvalue("hw.usermem", &lraw); + if (err) + Con::printf( " Unable to determine available user address space\n"); + else + Con::printf( " Addressable user memory: %d MB", (lraw >> 20)); + + //////////////////////////////// + // Values for the Family Type, CPU Type and CPU Subtype are defined in the + // SDK files for the Mach Kernel ==> mach/machine.h + //////////////////////////////// + + // CPU Family, Type, and Subtype + cpufam = 0; + cputype = 0; + cpusub = 0; + err = _getSysCTLvalue("hw.cpufamily", &lraw); + if (err) + Con::printf( " Unable to determine 'family' of CPU\n"); + else { + cpufam = (int) lraw; + err = _getSysCTLvalue("hw.cputype", &lraw); + if (err) + Con::printf( " Unable to determine CPU type\n"); + else { + cputype = (int) lraw; + err = _getSysCTLvalue("hw.cpusubtype", &lraw); + if (err) + Con::printf( " Unable to determine CPU subtype\n"); + else + cpusub = (int) lraw; + // If we've made it this far, + Con::printf( " Installed processor ID: Family 0x%08x Type %d Subtype %d",cpufam, cputype,cpusub); + } + } + + // The Gestalt version was known to have issues with some Processor Upgrade cards + // but it is uncertain whether this version has similar issues. + err = _getSysCTLvalue("hw.cpufrequency", &llraw); + if (err) { + llraw = BASE_MHZ_SPEED; + Con::printf( " Unable to determine CPU Frequency. Defaulting to %d MHz\n", llraw); + } else { + llraw /= 1000000; + Con::printf( " Installed processor clock frequency: %d MHz", llraw); + } + Platform::SystemInfo.processor.mhz = (unsigned int)llraw; + + // Here's one that the original version of this routine couldn't do -- number + // of processors (cores) + U32 ncpu = 1; + err = _getSysCTLvalue("hw.ncpu", &lraw); + if (err) + Con::printf( " Unable to determine number of processor cores\n"); + else + { + ncpu = lraw; + Con::printf( " Installed/available processor cores: %d", lraw); + } + + // Now use CPUFAM to determine and then store the processor type + // and 'friendly name' in GG-accessible structure. Note that since + // we have access to the Family code, the Type and Subtypes are useless. + // + // NOTE: Even this level of detail is almost assuredly not needed anymore + // and the Optional Capability flags (further down) should be more than enough. + switch(cpufam) + { + case CPUFAMILY_POWERPC_G3: + Platform::SystemInfo.processor.type = CPU_PowerPC_G3; + Platform::SystemInfo.processor.name = StringTable->insert("PowerPC G3"); + break; + case CPUFAMILY_POWERPC_G4: + Platform::SystemInfo.processor.type = CPU_PowerPC_G3; + Platform::SystemInfo.processor.name = StringTable->insert("PowerPC G4"); + break; + case CPUFAMILY_POWERPC_G5: + Platform::SystemInfo.processor.type = CPU_PowerPC_G3; + Platform::SystemInfo.processor.name = StringTable->insert("PowerPC G5"); + break; + case CPUFAMILY_INTEL_6_14: + Platform::SystemInfo.processor.type = CPU_Intel_Core; + if( ncpu == 2 ) + Platform::SystemInfo.processor.name = StringTable->insert("Intel Core Duo"); + else + Platform::SystemInfo.processor.name = StringTable->insert("Intel Core"); + break; + #ifdef CPUFAMILY_INTEL_6_23 + case CPUFAMILY_INTEL_6_23: + #endif + case CPUFAMILY_INTEL_6_15: + Platform::SystemInfo.processor.type = CPU_Intel_Core2; + if( ncpu == 4 ) + Platform::SystemInfo.processor.name = StringTable->insert("Intel Core 2 Quad"); + else + Platform::SystemInfo.processor.name = StringTable->insert("Intel Core 2 Duo"); + break; + + #ifdef CPUFAMILY_INTEL_6_26 + case CPUFAMILY_INTEL_6_26: + Platform::SystemInfo.processor.type = CPU_Intel_Core2; + Platform::SystemInfo.processor.name = StringTable->insert( "Intel 'Nehalem' Core Processor" ); + break; + #endif + + default: + // explain why we can't get the processor type. + Con::warnf( " Unknown Processor (family, type, subtype): 0x%x\t%d %d", cpufam, cputype, cpusub); + // for now, identify it as an x86 processor, because Apple is moving to Intel chips... + Platform::SystemInfo.processor.type = CPU_X86Compatible; + Platform::SystemInfo.processor.name = StringTable->insert("Unknown Processor, assuming x86 Compatible"); + break; + } + // Now we can directly query the system about a litany of "Optional" processor capabilities + // and determine the status by using BOTH the 'err' value and the 'lraw' value. If we request + // a non-existant feature from SYSCTL(), the 'err' result will be -1; 0 denotes it exists + // >>>> BUT <<<<< + // it may not be supported, only defined. Thus we need to check 'lraw' to determine if it's + // actually supported/implemented by the processor: 0 = no, 1 = yes, others are undefined. + procflags = 0; + // Seriously this one should be an Assert() + err = _getSysCTLvalue("hw.optional.floatingpoint", &lraw); + if ((err==0)&&(lraw==1)) procflags |= CPU_PROP_FPU; + // List of chip-specific features + err = _getSysCTLvalue("hw.optional.mmx", &lraw); + if ((err==0)&&(lraw==1)) procflags |= CPU_PROP_MMX; + err = _getSysCTLvalue("hw.optional.sse", &lraw); + if ((err==0)&&(lraw==1)) procflags |= CPU_PROP_SSE; + err = _getSysCTLvalue("hw.optional.sse2", &lraw); + if ((err==0)&&(lraw==1)) procflags |= CPU_PROP_SSE2; + err = _getSysCTLvalue("hw.optional.sse3", &lraw); + if ((err==0)&&(lraw==1)) procflags |= CPU_PROP_SSE3; + err = _getSysCTLvalue("hw.optional.supplementalsse3", &lraw); + if ((err==0)&&(lraw==1)) procflags |= CPU_PROP_SSE3xt; + err = _getSysCTLvalue("hw.optional.sse4_1", &lraw); + if ((err==0)&&(lraw==1)) procflags |= CPU_PROP_SSE4_1; + err = _getSysCTLvalue("hw.optional.sse4_2", &lraw); + if ((err==0)&&(lraw==1)) procflags |= CPU_PROP_SSE4_2; + err = _getSysCTLvalue("hw.optional.altivec", &lraw); + if ((err==0)&&(lraw==1)) procflags |= CPU_PROP_ALTIVEC; + // Finally some architecture-wide settings + err = _getSysCTLvalue("hw.ncpu", &lraw); + if ((err==0)&&(lraw>1)) procflags |= CPU_PROP_MP; + err = _getSysCTLvalue("hw.cpu64bit_capable", &lraw); + if ((err==0)&&(lraw==1)) procflags |= CPU_PROP_64bit; + err = _getSysCTLvalue("hw.byteorder", &lraw); + if ((err==0)&&(lraw==1234)) procflags |= CPU_PROP_LE; + + Platform::SystemInfo.processor.properties = procflags; + + Con::printf( "%s, %2.2f GHz", Platform::SystemInfo.processor.name, F32( Platform::SystemInfo.processor.mhz ) / 1000.0 ); + if (Platform::SystemInfo.processor.properties & CPU_PROP_MMX) + Con::printf( " MMX detected"); + if (Platform::SystemInfo.processor.properties & CPU_PROP_SSE) + Con::printf( " SSE detected"); + if (Platform::SystemInfo.processor.properties & CPU_PROP_SSE2) + Con::printf( " SSE2 detected"); + if (Platform::SystemInfo.processor.properties & CPU_PROP_SSE3) + Con::printf( " SSE3 detected"); + if (Platform::SystemInfo.processor.properties & CPU_PROP_ALTIVEC) + Con::printf( " AltiVec detected"); + + Con::printf( "" ); + + // Trigger the signal + Platform::SystemInfoReady.trigger(); +} diff --git a/Engine/source/platformMac/macCarbFileio.mm b/Engine/source/platformMac/macCarbFileio.mm new file mode 100644 index 000000000..9b95d67fc --- /dev/null +++ b/Engine/source/platformMac/macCarbFileio.mm @@ -0,0 +1,940 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +// Get our GL header included before Apple's +#include "platformMac/platformMacCarb.h" +// Don't include Apple's +#define __gl_h_ + +#include "platform/tmm_off.h" +#include +#include "platform/tmm_on.h" + +#include "core/fileio.h" +#include "core/util/tVector.h" +#include "core/stringTable.h" +#include "core/strings/stringFunctions.h" +#include "console/console.h" +#include "platform/profiler.h" +#include "cinterface/cinterface.h"; + +//TODO: file io still needs some work... + +#define MAX_MAC_PATH_LONG 2048 + +//----------------------------------------------------------------------------- +#if defined(TORQUE_OS_MAC) +#include +#else +#include +#endif + +//----------------------------------------------------------------------------- +bool dFileDelete(const char * name) +{ + if(!name ) + return(false); + + if (dStrlen(name) > MAX_MAC_PATH_LONG) + Con::warnf("dFileDelete: Filename length is pretty long..."); + + return(remove(name) == 0); // remove returns 0 on success +} + + +//----------------------------------------------------------------------------- +bool dFileTouch(const char *path) +{ + if (!path || !*path) + return false; + + // set file at path's modification and access times to now. + return( utimes( path, NULL) == 0); // utimes returns 0 on success. +} + +//----------------------------------------------------------------------------- +// Constructors & Destructor +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// After construction, the currentStatus will be Closed and the capabilities +// will be 0. +//----------------------------------------------------------------------------- +File::File() +: currentStatus(Closed), capability(0) +{ + handle = NULL; +} + +//----------------------------------------------------------------------------- +// insert a copy constructor here... (currently disabled) +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Destructor +//----------------------------------------------------------------------------- +File::~File() +{ + close(); + handle = NULL; +} + + +//----------------------------------------------------------------------------- +// Open a file in the mode specified by openMode (Read, Write, or ReadWrite). +// Truncate the file if the mode is either Write or ReadWrite and truncate is +// true. +// +// Sets capability appropriate to the openMode. +// Returns the currentStatus of the file. +//----------------------------------------------------------------------------- +File::Status File::open(const char *filename, const AccessMode openMode) +{ + if (dStrlen(filename) > MAX_MAC_PATH_LONG) + Con::warnf("File::open: Filename length is pretty long..."); + + // Close the file if it was already open... + if (currentStatus != Closed) + close(); + + // create the appropriate type of file... + switch (openMode) + { + case Read: + handle = (void *)fopen(filename, "rb"); // read only + break; + case Write: + handle = (void *)fopen(filename, "wb"); // write only + break; + case ReadWrite: + handle = (void *)fopen(filename, "ab+"); // write(append) and read + break; + case WriteAppend: + handle = (void *)fopen(filename, "ab"); // write(append) only + break; + default: + AssertFatal(false, "File::open: bad access mode"); + } + + // handle not created successfully + if (handle == NULL) + return setStatus(); + + // successfully created file, so set the file capabilities... + switch (openMode) + { + case Read: + capability = FileRead; + break; + case Write: + case WriteAppend: + capability = FileWrite; + break; + case ReadWrite: + capability = FileRead | FileWrite; + break; + default: + AssertFatal(false, "File::open: bad access mode"); + } + + // must set the file status before setting the position. + currentStatus = Ok; + + if (openMode == ReadWrite) + setPosition(0); + + // success! + return currentStatus; +} + +//----------------------------------------------------------------------------- +// Get the current position of the file pointer. +//----------------------------------------------------------------------------- +U32 File::getPosition() const +{ + AssertFatal(currentStatus != Closed , "File::getPosition: file closed"); + AssertFatal(handle != NULL, "File::getPosition: invalid file handle"); + + return ftell((FILE*)handle); +} + +//----------------------------------------------------------------------------- +// Set the position of the file pointer. +// Absolute and relative positioning is supported via the absolutePos +// parameter. +// +// If positioning absolutely, position MUST be positive - an IOError results if +// position is negative. +// Position can be negative if positioning relatively, however positioning +// before the start of the file is an IOError. +// +// Returns the currentStatus of the file. +//----------------------------------------------------------------------------- +File::Status File::setPosition(S32 position, bool absolutePos) +{ + AssertFatal(Closed != currentStatus, "File::setPosition: file closed"); + AssertFatal(handle != NULL, "File::setPosition: invalid file handle"); + + if (currentStatus != Ok && currentStatus != EOS ) + return currentStatus; + + U32 finalPos; + if(absolutePos) + { + // absolute position + AssertFatal(0 <= position, "File::setPosition: negative absolute position"); + // position beyond EOS is OK + fseek((FILE*)handle, position, SEEK_SET); + finalPos = ftell((FILE*)handle); + } + else + { + // relative position + AssertFatal((getPosition() + position) >= 0, "File::setPosition: negative relative position"); + // position beyond EOS is OK + fseek((FILE*)handle, position, SEEK_CUR); + finalPos = ftell((FILE*)handle); + } + + // ftell returns -1 on error. set error status + if (0xffffffff == finalPos) + return setStatus(); + + // success, at end of file + else if (finalPos >= getSize()) + return currentStatus = EOS; + + // success! + else + return currentStatus = Ok; +} + +//----------------------------------------------------------------------------- +// Get the size of the file in bytes. +// It is an error to query the file size for a Closed file, or for one with an +// error status. +//----------------------------------------------------------------------------- +U32 File::getSize() const +{ + AssertWarn(Closed != currentStatus, "File::getSize: file closed"); + AssertFatal(handle != NULL, "File::getSize: invalid file handle"); + + if (Ok == currentStatus || EOS == currentStatus) + { + struct stat statData; + + if(fstat(fileno((FILE*)handle), &statData) != 0) + return 0; + + // return the size in bytes + return statData.st_size; + } + + return 0; +} + +//----------------------------------------------------------------------------- +// Flush the file. +// It is an error to flush a read-only file. +// Returns the currentStatus of the file. +//----------------------------------------------------------------------------- +File::Status File::flush() +{ + AssertFatal(Closed != currentStatus, "File::flush: file closed"); + AssertFatal(handle != NULL, "File::flush: invalid file handle"); + AssertFatal(true == hasCapability(FileWrite), "File::flush: cannot flush a read-only file"); + + if (fflush((FILE*)handle) != 0) + return setStatus(); + else + return currentStatus = Ok; +} + +//----------------------------------------------------------------------------- +// Close the File. +// +// Returns the currentStatus +//----------------------------------------------------------------------------- +File::Status File::close() +{ + // check if it's already closed... + if (Closed == currentStatus) + return currentStatus; + + // it's not, so close it... + if (handle != NULL) + { + if (fclose((FILE*)handle) != 0) + return setStatus(); + } + handle = NULL; + return currentStatus = Closed; +} + +//----------------------------------------------------------------------------- +// Self-explanatory. +//----------------------------------------------------------------------------- +File::Status File::getStatus() const +{ + return currentStatus; +} + +//----------------------------------------------------------------------------- +// Sets and returns the currentStatus when an error has been encountered. +//----------------------------------------------------------------------------- +File::Status File::setStatus() +{ + switch (errno) + { + case EACCES: // permission denied + currentStatus = IOError; + break; + case EBADF: // Bad File Pointer + case EINVAL: // Invalid argument + case ENOENT: // file not found + case ENAMETOOLONG: + default: + currentStatus = UnknownError; + } + + return currentStatus; +} + +//----------------------------------------------------------------------------- +// Sets and returns the currentStatus to status. +//----------------------------------------------------------------------------- +File::Status File::setStatus(File::Status status) +{ + return currentStatus = status; +} + +//----------------------------------------------------------------------------- +// Read from a file. +// The number of bytes to read is passed in size, the data is returned in src. +// The number of bytes read is available in bytesRead if a non-Null pointer is +// provided. +//----------------------------------------------------------------------------- +File::Status File::read(U32 size, char *dst, U32 *bytesRead) +{ + AssertFatal(Closed != currentStatus, "File::read: file closed"); + AssertFatal(handle != NULL, "File::read: invalid file handle"); + AssertFatal(NULL != dst, "File::read: NULL destination pointer"); + AssertFatal(true == hasCapability(FileRead), "File::read: file lacks capability"); + AssertWarn(0 != size, "File::read: size of zero"); + + if (Ok != currentStatus || 0 == size) + return currentStatus; + + // read from stream + U32 nBytes = fread(dst, 1, size, (FILE*)handle); + + // did we hit the end of the stream? + if( nBytes != size) + currentStatus = EOS; + + // if bytesRead is a valid pointer, send number of bytes read there. + if(bytesRead) + *bytesRead = nBytes; + + // successfully read size bytes + return currentStatus; +} + +//----------------------------------------------------------------------------- +// Write to a file. +// The number of bytes to write is passed in size, the data is passed in src. +// The number of bytes written is available in bytesWritten if a non-Null +// pointer is provided. +//----------------------------------------------------------------------------- +File::Status File::write(U32 size, const char *src, U32 *bytesWritten) +{ + AssertFatal(Closed != currentStatus, "File::write: file closed"); + AssertFatal(handle != NULL, "File::write: invalid file handle"); + AssertFatal(NULL != src, "File::write: NULL source pointer"); + AssertFatal(true == hasCapability(FileWrite), "File::write: file lacks capability"); + AssertWarn(0 != size, "File::write: size of zero"); + + if ((Ok != currentStatus && EOS != currentStatus) || 0 == size) + return currentStatus; + + // write bytes to the stream + U32 nBytes = fwrite(src, 1, size,(FILE*)handle); + + // if we couldn't write everything, we've got a problem. set error status. + if(nBytes != size) + setStatus(); + + // if bytesWritten is a valid pointer, put number of bytes read there. + if(bytesWritten) + *bytesWritten = nBytes; + + // return current File status, whether good or ill. + return currentStatus; +} + + +//----------------------------------------------------------------------------- +// Self-explanatory. +//----------------------------------------------------------------------------- +bool File::hasCapability(Capability cap) const +{ + return (0 != (U32(cap) & capability)); +} + +//----------------------------------------------------------------------------- +S32 Platform::compareFileTimes(const FileTime &a, const FileTime &b) +{ + if(a > b) + return 1; + if(a < b) + return -1; + return 0; +} + + +//----------------------------------------------------------------------------- +// either time param COULD be null. +//----------------------------------------------------------------------------- +bool Platform::getFileTimes(const char *path, FileTime *createTime, FileTime *modifyTime) +{ + // MacOSX is NOT guaranteed to be running off a HFS volume, + // and UNIX does not keep a record of a file's creation time anywhere. + // So instead of creation time we return changed time, + // just like the Linux platform impl does. + + if (!path || !*path) + return false; + + struct stat statData; + + if (stat(path, &statData) == -1) + return false; + + if(createTime) + *createTime = statData.st_ctime; + + if(modifyTime) + *modifyTime = statData.st_mtime; + + return true; +} + + +//----------------------------------------------------------------------------- +bool Platform::createPath(const char *file) +{ + // if the path exists, we're done. + struct stat statData; + if( stat(file, &statData) == 0 ) + { + return true; // exists, rejoice. + } + + Con::warnf( "creating path %s", file ); + + // get the parent path. + // we're not using basename because it's not thread safe. + U32 len = dStrlen(file); + char parent[len]; + bool isDirPath = false; + + dStrncpy(parent,file,len); + parent[len] = '\0'; + if(parent[len - 1] == '/') + { + parent[len - 1] = '\0'; // cut off the trailing slash, if there is one + isDirPath = true; // we got a trailing slash, so file is a directory. + } + + // recusively create the parent path. + // only recurse if newpath has a slash that isn't a leading slash. + char *slash = dStrrchr(parent,'/'); + if( slash && slash != parent) + { + // snip the path just after the last slash. + slash[1] = '\0'; + // recusively create the parent path. fail if parent path creation failed. + if(!Platform::createPath(parent)) + return false; + } + + // create *file if it is a directory path. + if(isDirPath) + { + // try to create the directory + if( mkdir(file, 0777) != 0) // app may reside in global apps dir, and so must be writable to all. + return false; + } + + return true; +} + + +//----------------------------------------------------------------------------- +bool Platform::cdFileExists(const char *filePath, const char *volumeName, S32 serialNum) +{ + return true; +} + +#pragma mark ---- Directories ---- +//----------------------------------------------------------------------------- +StringTableEntry Platform::getCurrentDirectory() +{ + // get the current directory, the one that would be opened if we did a fopen(".") + char* cwd = getcwd(NULL, 0); + StringTableEntry ret = StringTable->insert(cwd); + free(cwd); + return ret; +} + +//----------------------------------------------------------------------------- +bool Platform::setCurrentDirectory(StringTableEntry newDir) +{ + return (chdir(newDir) == 0); +} + +//----------------------------------------------------------------------------- +void Platform::openFolder(const char* path ) +{ + // TODO: users can still run applications by calling openfolder on an app bundle. + // this may be a bad thing. + if(!Platform::isDirectory(path)) + { + Con::errorf(avar("Error: not a directory: %s",path)); + return; + } + + const char* arg = avar("open '%s'", path); + U32 ret = system(arg); + if(ret != 0) + Con::printf(strerror(errno)); +} + +void Platform::openFile(const char* path ) +{ + if( !Platform::isFile( path ) ) + { + Con::errorf( avar( "Error: not a file: %s", path ) ); + return; + } + + const char* arg = avar( "open '%s'", path ); + U32 ret = system( arg ); + if( ret != 0 ) + Con::printf( strerror( errno ) ); +} + +// helper func for getWorkingDirectory +bool isMainDotCsPresent(NSString* dir) +{ + return [[NSFileManager defaultManager] fileExistsAtPath:[dir stringByAppendingPathComponent:@"main.cs"]] == YES; +} + +//----------------------------------------------------------------------------- +/// Finds and sets the current working directory. +/// Torque tries to automatically detect whether you have placed the game files +/// inside or outside the application's bundle. It checks for the presence of +/// the file 'main.cs'. If it finds it, Torque will assume that the other game +/// files are there too. If Torque does not see 'main.cs' inside its bundle, it +/// will assume the files are outside the bundle. +/// Since you probably don't want to copy the game files into the app every time +/// you build, you will want to leave them outside the bundle for development. +/// +/// Placing all content inside the application bundle gives a much better user +/// experience when you distribute your app. +StringTableEntry Platform::getExecutablePath() +{ + static const char* cwd = NULL; + + // this isn't actually being used due to some static constructors at bundle load time + // calling this method (before there is a chance to set it) + // for instance, FMOD sound provider (this should be fixed in FMOD as it is with windows) + if (!cwd && torque_getexecutablepath()) + { + // we're in a plugin using the cinterface + cwd = torque_getexecutablepath(); + chdir(cwd); + } + else if(!cwd) + { + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + //first check the cwd for main.cs + static char buf[4096]; + NSString* currentDir = [[NSString alloc ] initWithCString:getcwd(buf,(4096 * sizeof(char))) ]; + + if (isMainDotCsPresent(currentDir)) + { + cwd = buf; + [pool release]; + return cwd; + } + + NSString* string = [[NSBundle mainBundle] pathForResource:@"main" ofType:@"cs"]; + if(!string) + string = [[NSBundle mainBundle] bundlePath]; + + string = [string stringByDeletingLastPathComponent]; + AssertISV(isMainDotCsPresent(string), "Platform::getExecutablePath - Failed to find main.cs!"); + cwd = dStrdup([string UTF8String]); + chdir(cwd); + [pool release]; + } + + return cwd; +} + +//----------------------------------------------------------------------------- +StringTableEntry Platform::getExecutableName() +{ + static const char* name = NULL; + if(!name) + name = [[[[NSBundle mainBundle] bundlePath] lastPathComponent] UTF8String]; + + return name; +} + +//----------------------------------------------------------------------------- +bool Platform::isFile(const char *path) +{ + if (!path || !*path) + return false; + + // make sure we can stat the file + struct stat statData; + if( stat(path, &statData) < 0 ) + return false; + + // now see if it's a regular file + if( (statData.st_mode & S_IFMT) == S_IFREG) + return true; + + return false; +} + + +//----------------------------------------------------------------------------- +bool Platform::isDirectory(const char *path) +{ + if (!path || !*path) + return false; + + // make sure we can stat the file + struct stat statData; + if( stat(path, &statData) < 0 ) + return false; + + // now see if it's a directory + if( (statData.st_mode & S_IFMT) == S_IFDIR) + return true; + + return false; +} + + +S32 Platform::getFileSize(const char* pFilePath) +{ + if (!pFilePath || !*pFilePath) + return 0; + + struct stat statData; + if( stat(pFilePath, &statData) < 0 ) + return 0; + + // and return it's size in bytes + return (S32)statData.st_size; +} + + +//----------------------------------------------------------------------------- +bool Platform::isSubDirectory(const char *pathParent, const char *pathSub) +{ + char fullpath[MAX_MAC_PATH_LONG]; + dStrcpyl(fullpath, MAX_MAC_PATH_LONG, pathParent, "/", pathSub, NULL); + return isDirectory((const char *)fullpath); +} + +//----------------------------------------------------------------------------- +// utility for platform::hasSubDirectory() and platform::dumpDirectories() +// ensures that the entry is a directory, and isnt on the ignore lists. +inline bool isGoodDirectory(dirent* entry) +{ + return (entry->d_type == DT_DIR // is a dir + && dStrcmp(entry->d_name,".") != 0 // not here + && dStrcmp(entry->d_name,"..") != 0 // not parent + && !Platform::isExcludedDirectory(entry->d_name)); // not excluded +} + +//----------------------------------------------------------------------------- +bool Platform::hasSubDirectory(const char *path) +{ + DIR *dir; + dirent *entry; + + dir = opendir(path); + if(!dir) + return false; // we got a bad path, so no, it has no subdirectory. + + while( (entry = readdir(dir)) ) + { + if(isGoodDirectory(entry) ) + { + closedir(dir); + return true; // we have a subdirectory, that isnt on the exclude list. + } + } + + closedir(dir); + return false; // either this dir had no subdirectories, or they were all on the exclude list. +} + +//----------------------------------------------------------------------------- +bool recurseDumpDirectories(const char *basePath, const char *path, Vector &directoryVector, S32 depth, bool noBasePath) +{ + DIR *dir; + dirent *entry; + U32 len = dStrlen(basePath) + dStrlen(path) + 2; + char pathbuf[len]; + + // construct the file path + dSprintf(pathbuf, len, "%s/%s", basePath, path); + pathbuf[len] = '\0'; + + // be sure it opens. + dir = opendir(pathbuf); + if(!dir) + return false; + + // look inside the current directory + while( (entry = readdir(dir)) ) + { + // we just want directories. + if(!isGoodDirectory(entry)) + continue; + + // TODO: better unicode file name handling + // // Apple's file system stores unicode file names in decomposed form. + // // ATSUI will not reliably draw out just the accent character by itself, + // // so our text renderer has no chance of rendering decomposed form unicode. + // // We have to convert the entry name to precomposed normalized form. + // CFStringRef cfdname = CFStringCreateWithCString(NULL,entry->d_name,kCFStringEncodingUTF8); + // CFMutableStringRef cfentryName = CFStringCreateMutableCopy(NULL,0,cfdname); + // CFStringNormalize(cfentryName,kCFStringNormalizationFormC); + // + // U32 entryNameLen = CFStringGetLength(cfentryName) * 4 + 1; + // char entryName[entryNameLen]; + // CFStringGetCString(cfentryName, entryName, entryNameLen, kCFStringEncodingUTF8); + // entryName[entryNameLen-1] = NULL; // sometimes, CFStringGetCString() doesn't null terminate. + // CFRelease(cfentryName); + // CFRelease(cfdname); + + // construct the new path string, we'll need this below. + U32 newpathlen = dStrlen(path) + dStrlen(entry->d_name) + 2; + char newpath[newpathlen]; + if(dStrlen(path) > 0) // prevent extra slashes in the path + dSprintf(newpath, newpathlen,"%s/%s",path,entry->d_name); + else + dStrncpy(newpath,entry->d_name, newpathlen); + newpath[newpathlen] = '\0'; + + // we have a directory, add it to the list. + if( noBasePath ) + directoryVector.push_back(StringTable->insert(newpath)); + else { + U32 fullpathlen = dStrlen(basePath) + dStrlen(newpath) + 2; + char fullpath[fullpathlen]; + dSprintf(fullpath,fullpathlen,"%s/%s",basePath,newpath); + fullpath[fullpathlen] = '\0'; + + directoryVector.push_back(StringTable->insert(fullpath)); + } + + // and recurse into it, unless we've run out of depth + if( depth != 0) // passing a val of -1 as the recurse depth means go forever + recurseDumpDirectories(basePath, newpath, directoryVector, depth-1, noBasePath); + } + closedir(dir); + return true; +} + +//----------------------------------------------------------------------------- +bool Platform::dumpDirectories(const char *path, Vector &directoryVector, S32 depth, bool noBasePath) +{ + PROFILE_START(dumpDirectories); + + int len = dStrlen(path); + char newpath[len]; + + dStrncpy(newpath,path,len); + newpath[len] = '\0'; + if(newpath[len - 1] == '/') + newpath[len - 1] = '\0'; // cut off the trailing slash, if there is one + + bool ret = recurseDumpDirectories(newpath, "", directoryVector, depth, noBasePath); + PROFILE_END(); + + return ret; +} + +//----------------------------------------------------------------------------- +static bool recurseDumpPath(const char* curPath, Vector& fileVector, U32 depth) +{ + DIR *dir; + dirent *entry; + + // be sure it opens. + dir = opendir(curPath); + if(!dir) + return false; + + // look inside the current directory + while( (entry = readdir(dir)) ) + { + // construct the full file path. we need this to get the file size and to recurse + U32 len = dStrlen(curPath) + entry->d_namlen + 2; + char pathbuf[len]; + dSprintf( pathbuf, len, "%s/%s", curPath, entry->d_name); + pathbuf[len] = '\0'; + + // ok, deal with directories and files seperately. + if( entry->d_type == DT_DIR ) + { + if( depth == 0) + continue; + + // filter out dirs we dont want. + if( !isGoodDirectory(entry) ) + continue; + + // recurse into the dir + recurseDumpPath( pathbuf, fileVector, depth-1); + } + else + { + //add the file entry to the list + // unlike recurseDumpDirectories(), we need to return more complex info here. + U32 fileSize = Platform::getFileSize(pathbuf); + fileVector.increment(); + Platform::FileInfo& rInfo = fileVector.last(); + rInfo.pFullPath = StringTable->insert(curPath); + rInfo.pFileName = StringTable->insert(entry->d_name); + rInfo.fileSize = fileSize; + } + } + closedir(dir); + return true; + +} + + +//----------------------------------------------------------------------------- +bool Platform::dumpPath(const char *path, Vector& fileVector, S32 depth) +{ + PROFILE_START(dumpPath); + int len = dStrlen(path); + char newpath[len+1]; + + dStrncpy(newpath,path,len); + newpath[len] = '\0'; // null terminate + if(newpath[len - 1] == '/') + newpath[len - 1] = '\0'; // cut off the trailing slash, if there is one + + bool ret = recurseDumpPath( newpath, fileVector, depth); + PROFILE_END(); + + return ret; +} + +// TODO: implement stringToFileTime() +bool Platform::stringToFileTime(const char * string, FileTime * time) { return false;} +// TODO: implement fileTimeToString() +bool Platform::fileTimeToString(FileTime * time, char * string, U32 strLen) { return false;} + +//----------------------------------------------------------------------------- +#if defined(TORQUE_DEBUG) +ConsoleFunction(testHasSubdir,void,2,2,"tests platform::hasSubDirectory") { + Con::printf("testing %s",argv[1]); + Platform::addExcludedDirectory(".svn"); + if(Platform::hasSubDirectory(argv[1])) + Con::printf(" has subdir"); + else + Con::printf(" does not have subdir"); +} + +ConsoleFunction(testDumpDirectories,void,4,4,"testDumpDirectories('path', int depth, bool noBasePath)") { + Vector paths; + const S32 depth = dAtoi(argv[2]); + const bool noBasePath = dAtob(argv[3]); + + Platform::addExcludedDirectory(".svn"); + + Platform::dumpDirectories(argv[1], paths, depth, noBasePath); + + Con::printf("Dumping directories starting from %s with depth %i", argv[1],depth); + + for(Vector::iterator itr = paths.begin(); itr != paths.end(); itr++) { + Con::printf(*itr); + } + +} + +ConsoleFunction(testDumpPaths, void, 3, 3, "testDumpPaths('path', int depth)") +{ + Vector files; + S32 depth = dAtoi(argv[2]); + + Platform::addExcludedDirectory(".svn"); + + Platform::dumpPath(argv[1], files, depth); + + for(Vector::iterator itr = files.begin(); itr != files.end(); itr++) { + Con::printf("%s/%s",itr->pFullPath, itr->pFileName); + } +} + +//----------------------------------------------------------------------------- +ConsoleFunction(testFileTouch, bool , 2,2, "testFileTouch('path')") +{ + return dFileTouch(argv[1]); +} + +ConsoleFunction(testGetFileTimes, bool, 2,2, "testGetFileTimes('path')") +{ + FileTime create, modify; + bool ok = Platform::getFileTimes(argv[1], &create, &modify); + Con::printf("%s Platform::getFileTimes %i, %i", ok ? "+OK" : "-FAIL", create, modify); + return ok; +} + +#endif diff --git a/Engine/source/platformMac/macCarbFont.cpp b/Engine/source/platformMac/macCarbFont.cpp new file mode 100644 index 000000000..0b9c7349b --- /dev/null +++ b/Engine/source/platformMac/macCarbFont.cpp @@ -0,0 +1,449 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformMac/macCarbFont.h" +#include "platformMac/platformMacCarb.h" +#include "math/mRect.h" +#include "console/console.h" +#include "core/strings/unicode.h" +#include "core/stringTable.h" +#include "core/strings/stringFunctions.h" + + +//------------------------------------------------------------------------------ +// New Unicode capable font class. +PlatformFont *createPlatformFont(const char *name, U32 size, U32 charset /* = TGE_ANSI_CHARSET */) +{ + PlatformFont *retFont = new MacCarbFont; + + if(retFont->create(name, size, charset)) + return retFont; + + delete retFont; + return NULL; +} + +//------------------------------------------------------------------------------ +MacCarbFont::MacCarbFont() +{ + mStyle = NULL; + mLayout = NULL; + mColorSpace = NULL; +} + +MacCarbFont::~MacCarbFont() +{ + // apple docs say we should dispose the layout first. + ATSUDisposeTextLayout(mLayout); + ATSUDisposeStyle(mStyle); + CGColorSpaceRelease(mColorSpace); +} + +//------------------------------------------------------------------------------ +bool MacCarbFont::create( const char* name, U32 size, U32 charset) +{ + String nameStr = name; + nameStr = nameStr.trim(); + + // create and cache the style and layout. + // based on apple sample code at http://developer.apple.com/qa/qa2001/qa1027.html + + // note: charset is ignored on mac. -- we don't need it to get the right chars. + // But do we need it to translate encodings? hmm... + + CFStringRef cfsName; + ATSUFontID atsuFontID; + ATSFontRef atsFontRef; + Fixed atsuSize; + ATSURGBAlphaColor black; + ATSFontMetrics fontMetrics; + U32 scaledSize; + + bool isBold = false; + bool isItalic = false; + + bool haveModifier; + do + { + haveModifier = false; + if( nameStr.compare( "Bold", 4, String::NoCase | String::Right ) == 0 ) + { + isBold = true; + nameStr = nameStr.substr( 0, nameStr.length() - 4 ).trim(); + haveModifier = true; + } + if( nameStr.compare( "Italic", 6, String::NoCase | String::Right ) == 0 ) + { + isItalic = true; + nameStr = nameStr.substr( 0, nameStr.length() - 6 ).trim(); + haveModifier = true; + } + } + while( haveModifier ); + + // Look up the font. We need it in 2 differnt formats, for differnt Apple APIs. + cfsName = CFStringCreateWithCString( kCFAllocatorDefault, nameStr.c_str(), kCFStringEncodingUTF8); + if(!cfsName) + Con::errorf("Error: could not make a cfstring out of \"%s\" ",nameStr.c_str()); + + atsFontRef = ATSFontFindFromName( cfsName, kATSOptionFlagsDefault); + atsuFontID = FMGetFontFromATSFontRef( atsFontRef); + + // make sure we found it. ATSFontFindFromName() appears to return 0 if it cant find anything. Apple docs contain no info on error returns. + if( !atsFontRef || !atsuFontID ) + { + Con::errorf("MacCarbFont::create - could not load font -%s-",name); + return false; + } + + // adjust the size. win dpi = 96, mac dpi = 72. 72/96 = .75 + // Interestingly enough, 0.75 is not what makes things the right size. + scaledSize = size - 2 - (int)((float)size * 0.1); + mSize = scaledSize; + + // Set up the size and color. We send these to ATSUSetAttributes(). + atsuSize = IntToFixed(scaledSize); + black.red = black.green = black.blue = black.alpha = 1.0; + + // Three parrallel arrays for setting up font, size, and color attributes. + ATSUAttributeTag theTags[] = { kATSUFontTag, kATSUSizeTag, kATSURGBAlphaColorTag}; + ByteCount theSizes[] = { sizeof(ATSUFontID), sizeof(Fixed), sizeof(ATSURGBAlphaColor) }; + ATSUAttributeValuePtr theValues[] = { &atsuFontID, &atsuSize, &black }; + + // create and configure the style object. + ATSUCreateStyle(&mStyle); + ATSUSetAttributes( mStyle, 3, theTags, theSizes, theValues ); + + if( isBold ) + { + ATSUAttributeTag tag = kATSUQDBoldfaceTag; + ByteCount size = sizeof( Boolean ); + Boolean value = true; + ATSUAttributeValuePtr valuePtr = &value; + ATSUSetAttributes( mStyle, 1, &tag, &size, &valuePtr ); + } + + if( isItalic ) + { + ATSUAttributeTag tag = kATSUQDItalicTag; + ByteCount size = sizeof( Boolean ); + Boolean value = true; + ATSUAttributeValuePtr valuePtr = &value; + ATSUSetAttributes( mStyle, 1, &tag, &size, &valuePtr ); + } + + // create the layout object, + ATSUCreateTextLayout(&mLayout); + // we'll bind the layout to a bitmap context when we actually draw. + // ATSUSetTextPointerLocation() - will set the text buffer + // ATSUSetLayoutControls() - will set the cg context. + + // get font metrics, save our baseline and height + ATSFontGetHorizontalMetrics(atsFontRef, kATSOptionFlagsDefault, &fontMetrics); + mBaseline = scaledSize * fontMetrics.ascent; + mHeight = scaledSize * ( fontMetrics.ascent - fontMetrics.descent + fontMetrics.leading ) + 1; + + // cache our grey color space, so we dont have to re create it every time. + mColorSpace = CGColorSpaceCreateDeviceGray(); + + // and finally cache the font's name. We use this to cheat some antialiasing options below. + mName = StringTable->insert(name); + + return true; +} + +//------------------------------------------------------------------------------ +bool MacCarbFont::isValidChar(const UTF8 *str) const +{ + // since only low order characters are invalid, and since those characters + // are single codeunits in UTF8, we can safely cast here. + return isValidChar((UTF16)*str); +} + +bool MacCarbFont::isValidChar( const UTF16 ch) const +{ + // We cut out the ASCII control chars here. Only printable characters are valid. + // 0x20 == 32 == space + if( ch < 0x20 ) + return false; + + return true; +} + +PlatformFont::CharInfo& MacCarbFont::getCharInfo(const UTF8 *str) const +{ + return getCharInfo(oneUTF32toUTF16(oneUTF8toUTF32(str,NULL))); +} + +PlatformFont::CharInfo& MacCarbFont::getCharInfo(const UTF16 ch) const +{ + // We use some static data here to avoid re allocating the same variable in a loop. + // this func is primarily called by GFont::loadCharInfo(), + Rect imageRect; + CGContextRef imageCtx; + U32 bitmapDataSize; + ATSUTextMeasurement tbefore, tafter, tascent, tdescent; + OSStatus err; + + // 16 bit character buffer for the ATUSI calls. + // -- hey... could we cache this at the class level, set style and loc *once*, + // then just write to this buffer and clear the layout cache, to speed up drawing? + static UniChar chUniChar[1]; + chUniChar[0] = ch; + + // Declare and clear out the CharInfo that will be returned. + static PlatformFont::CharInfo c; + dMemset(&c, 0, sizeof(c)); + + // prep values for GFont::addBitmap() + c.bitmapIndex = 0; + c.xOffset = 0; + c.yOffset = 0; + + // put the text in the layout. + // we've hardcoded a string length of 1 here, but this could work for longer strings... (hint hint) + // note: ATSUSetTextPointerLocation() also clears the previous cached layout information. + ATSUSetTextPointerLocation( mLayout, chUniChar, 0, 1, 1); + ATSUSetRunStyle( mLayout, mStyle, 0,1); + + // get the typographic bounds. this tells us how characters are placed relative to other characters. + ATSUGetUnjustifiedBounds( mLayout, 0, 1, &tbefore, &tafter, &tascent, &tdescent); + c.xIncrement = FixedToInt(tafter); + + // find out how big of a bitmap we'll need. + // as a bonus, we also get the origin where we should draw, encoded in the Rect. + ATSUMeasureTextImage( mLayout, 0, 1, 0, 0, &imageRect); + U32 xFudge = 2; + U32 yFudge = 1; + c.width = imageRect.right - imageRect.left + xFudge; // add 2 because small fonts don't always have enough room + c.height = imageRect.bottom - imageRect.top + yFudge; + c.xOrigin = imageRect.left; // dist x0 -> center line + c.yOrigin = -imageRect.top; // dist y0 -> base line + + // kick out early if the character is undrawable + if( c.width == xFudge || c.height == yFudge) + return c; + + // allocate a greyscale bitmap and clear it. + bitmapDataSize = c.width * c.height; + c.bitmapData = new U8[bitmapDataSize]; + dMemset(c.bitmapData,0x00,bitmapDataSize); + + // get a graphics context on the bitmap + imageCtx = CGBitmapContextCreate( c.bitmapData, c.width, c.height, 8, c.width, mColorSpace, kCGImageAlphaNone); + if(!imageCtx) { + Con::errorf("Error: failed to create a graphics context on the CharInfo bitmap! Drawing a blank block."); + c.xIncrement = c.width; + dMemset(c.bitmapData,0x0F,bitmapDataSize); + return c; + } + + // Turn off antialiasing for monospaced console fonts. yes, this is cheating. + if(mSize < 12 && ( dStrstr(mName,"Monaco")!=NULL || dStrstr(mName,"Courier")!=NULL )) + CGContextSetShouldAntialias(imageCtx, false); + + // Set up drawing options for the context. + // Since we're not going straight to the screen, we need to adjust accordingly + CGContextSetShouldSmoothFonts(imageCtx, false); + CGContextSetRenderingIntent(imageCtx, kCGRenderingIntentAbsoluteColorimetric); + CGContextSetInterpolationQuality( imageCtx, kCGInterpolationNone); + CGContextSetGrayFillColor( imageCtx, 1.0, 1.0); + CGContextSetTextDrawingMode( imageCtx, kCGTextFill); + + // tell ATSUI to substitute fonts as needed for missing glyphs + ATSUSetTransientFontMatching(mLayout, true); + + // set up three parrallel arrays for setting up attributes. + // this is how most options in ATSUI are set, by passing arrays of options. + ATSUAttributeTag theTags[] = { kATSUCGContextTag }; + ByteCount theSizes[] = { sizeof(CGContextRef) }; + ATSUAttributeValuePtr theValues[] = { &imageCtx }; + + // bind the layout to the context. + ATSUSetLayoutControls( mLayout, 1, theTags, theSizes, theValues ); + + // Draw the character! + int yoff = c.height < 3 ? 1 : 0; // kludge for 1 pixel high characters, such as '-' and '_' + int xoff = 1; + err = ATSUDrawText( mLayout, 0, 1, IntToFixed(-imageRect.left + xoff), IntToFixed(imageRect.bottom + yoff ) ); + CGContextRelease(imageCtx); + + if(err != noErr) { + Con::errorf("Error: could not draw the character! Drawing a blank box."); + dMemset(c.bitmapData,0x0F,bitmapDataSize); + } + + +#if TORQUE_DEBUG +// Con::printf("Font Metrics: Rect = %2i %2i %2i %2i Char= %C, 0x%x Size= %i, Baseline= %i, Height= %i",imageRect.top, imageRect.bottom, imageRect.left, imageRect.right,ch,ch, mSize,mBaseline, mHeight); +// Con::printf("Font Bounds: left= %2i right= %2i Char= %C, 0x%x Size= %i",FixedToInt(tbefore), FixedToInt(tafter), ch,ch, mSize); +#endif + + return c; +} + +void PlatformFont::enumeratePlatformFonts( Vector< StringTableEntry >& fonts, UTF16* fontFamily ) +{ + if( fontFamily ) + { + // Determine the font ID from the family name. + + ATSUFontID fontID; + if( ATSUFindFontFromName( + fontFamily, + dStrlen( fontFamily ) * 2, + kFontFamilyName, + kFontMicrosoftPlatform, + kFontNoScriptCode, + kFontNoLanguageCode, &fontID ) != kATSUInvalidFontErr ) + { + // Get the number of fonts in the family. + + ItemCount numFonts; + ATSUCountFontNames( fontID, &numFonts ); + + // Read out font names. + + U32 bufferSize = 512; + char* buffer = ( char* ) dMalloc( bufferSize ); + + for( U32 i = 0; i < numFonts; ++ i ) + { + for( U32 n = 0; n < 2; ++ n ) + { + ByteCount actualNameLength; + FontNameCode fontNameCode; + FontPlatformCode fontPlatformCode; + FontScriptCode fontScriptCode; + FontLanguageCode fontLanguageCode; + + if( ATSUGetIndFontName( + fontID, + i, + bufferSize - 2, + buffer, + &actualNameLength, + &fontNameCode, + &fontPlatformCode, + &fontScriptCode, + &fontLanguageCode ) == noErr ) + { + *( ( UTF16* ) &buffer[ actualNameLength ] ) = '\0'; + char* utf8 = convertUTF16toUTF8( ( UTF16* ) buffer ); + fonts.push_back( StringTable->insert( utf8 ) ); + delete [] utf8; + break; + } + + // Allocate larger buffer. + + bufferSize = actualNameLength + 2; + buffer = ( char* ) dRealloc( buffer, bufferSize ); + } + } + + dFree( buffer ); + } + } + else + { + // Get the number of installed fonts. + + ItemCount numFonts; + ATSUFontCount( &numFonts ); + + // Get all the font IDs. + + ATSUFontID* fontIDs = new ATSUFontID[ numFonts ]; + if( ATSUGetFontIDs( fontIDs, numFonts, &numFonts ) == noErr ) + { + U32 bufferSize = 512; + char* buffer = ( char* ) dMalloc( bufferSize ); + + // Read all family names. + + for( U32 i = 0; i < numFonts; ++ i ) + { + for( U32 n = 0; n < 2; ++ n ) + { + ByteCount actualNameLength; + ItemCount fontIndex; + + OSStatus result = ATSUFindFontName( + fontIDs[ i ], + kFontFamilyName, + kFontMicrosoftPlatform, + kFontNoScriptCode, + kFontNoLanguageCode, + bufferSize - 2, + buffer, + &actualNameLength, + &fontIndex ); + + if( result == kATSUNoFontNameErr ) + break; + else if( result == noErr ) + { + *( ( UTF16* ) &buffer[ actualNameLength ] ) = '\0'; + char* utf8 = convertUTF16toUTF8( ( UTF16* ) buffer ); + StringTableEntry name = StringTable->insert( utf8 ); + delete [] utf8; + + // Avoid duplicates. + + bool duplicate = false; + for( U32 i = 0, num = fonts.size(); i < num; ++ i ) + if( fonts[ i ] == name ) + { + duplicate = true; + break; + } + + if( !duplicate ) + fonts.push_back( name ); + + break; + } + + // Allocate larger buffer. + + bufferSize = actualNameLength + 2; + buffer = ( char* ) dRealloc( buffer, bufferSize ); + } + } + + dFree( buffer ); + } + + delete [] fontIDs; + } +} + +//----------------------------------------------------------------------------- +// The following code snippet demonstrates how to get the elusive GlyphIDs, +// which are needed when you want to do various complex and arcane things +// with ATSUI and CoreGraphics. +// +// ATSUGlyphInfoArray glyphinfoArr; +// ATSUGetGlyphInfo( mLayout, kATSUFromTextBeginning, kATSUToTextEnd,sizeof(ATSUGlyphInfoArray), &glyphinfoArr); +// ATSUGlyphInfo glyphinfo = glyphinfoArr.glyphs[0]; +// Con::printf(" Glyphinfo: screenX= %i, idealX=%f, deltaY=%f", glyphinfo.screenX, glyphinfo.idealX, glyphinfo.deltaY); diff --git a/Engine/source/platformMac/macCarbFont.h b/Engine/source/platformMac/macCarbFont.h new file mode 100644 index 000000000..fb181898d --- /dev/null +++ b/Engine/source/platformMac/macCarbFont.h @@ -0,0 +1,74 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include +#include "platform/platformFont.h" + + +class MacCarbFont : public PlatformFont +{ +private: + // Caches style, layout and colorspace data to speed up character drawing. + // TODO: style colors + ATSUStyle mStyle; + ATSUTextLayout mLayout; + CGColorSpaceRef mColorSpace; + + // Cache the baseline and height for the getter methods below. + U32 mHeight; // distance between lines + U32 mBaseline; // distance from drawing point to typographic baseline, + // think of the drawing point as the upper left corner of a text box. + // note: 'baseline' is synonymous with 'ascent' in Torque. + + // Cache the size and name requested in create() + U32 mSize; + StringTableEntry mName; + +public: + MacCarbFont(); + virtual ~MacCarbFont(); + + /// Look up the requested font, cache style, layout, colorspace, and some metrics. + virtual bool create( const char* name, U32 size, U32 charset = TGE_ANSI_CHARSET); + + /// Determine if the character requested is a drawable character, or if it should be ignored. + virtual bool isValidChar( const UTF16 ch) const; + virtual bool isValidChar( const UTF8 *str) const; + + /// Get some vertical data on the font at large. Useful for drawing multiline text, and sizing text boxes. + virtual U32 getFontHeight() const; + virtual U32 getFontBaseLine() const; + + // Draw the character to a temporary bitmap, and fill the CharInfo with various text metrics. + virtual PlatformFont::CharInfo &getCharInfo(const UTF16 ch) const; + virtual PlatformFont::CharInfo &getCharInfo(const UTF8 *str) const; +}; + +inline U32 MacCarbFont::getFontHeight() const +{ + return mHeight; +} + +inline U32 MacCarbFont::getFontBaseLine() const +{ + return mBaseline; +} \ No newline at end of file diff --git a/Engine/source/platformMac/macCarbInput.cpp b/Engine/source/platformMac/macCarbInput.cpp new file mode 100644 index 000000000..381699cd9 --- /dev/null +++ b/Engine/source/platformMac/macCarbInput.cpp @@ -0,0 +1,500 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include +#include + +#include "platform/platformInput.h" +#include "console/console.h" +#include "core/strings/unicode.h" +#include "core/util/tVector.h" + +// Static class variables: +InputManager* Input::smManager; +bool Input::smActive; +U8 Input::smModifierKeys; +InputEvent Input::smInputEvent; + +//----------------------------------------------------------------------------- +// Keycode mapping. + +struct KeyCode +{ + U32 mKeyCode; + UniChar mCharLower; + UniChar mCharUpper; + + KeyCode( U32 keyCode ) + : mKeyCode( keyCode ) {} +}; + +static KeyCode sOSToKeyCode[] = +{ + KEY_A, // 0x00 + KEY_S, // 0x01 + KEY_D, // 0x02 + KEY_F, // 0x03 + KEY_H, // 0x04 + KEY_G, // 0x05 + KEY_Z, // 0x06 + KEY_X, // 0x07 + KEY_C, // 0x08 + KEY_V, // 0x09 + 0, // 0x0A + KEY_B, // 0x0B + KEY_Q, // 0x0C + KEY_W, // 0x0D + KEY_E, // 0x0E + KEY_R, // 0x0F + KEY_Y, // 0x10 + KEY_T, // 0x11 + KEY_1, // 0x12 + KEY_2, // 0x13 + KEY_3, // 0x14 + KEY_4, // 0x15 + KEY_6, // 0x16 + KEY_5, // 0x17 + KEY_EQUALS, // 0x18 + KEY_9, // 0x19 + KEY_7, // 0x1A + KEY_MINUS, // 0x1B + KEY_8, // 0x1C + KEY_0, // 0x1D + KEY_RBRACKET, // 0x1E + KEY_O, // 0x1F + KEY_U, // 0x20 + KEY_LBRACKET, // 0x21 + KEY_I, // 0x22 + KEY_P, // 0x23 + KEY_RETURN, // 0x24 + KEY_L, // 0x25 + KEY_J, // 0x26 + KEY_APOSTROPHE, // 0x27 + KEY_K, // 0x28 + KEY_SEMICOLON, // 0x29 + KEY_BACKSLASH, // 0x2A + KEY_COMMA, // 0x2B + KEY_SLASH, // 0x2C + KEY_N, // 0x2D + KEY_M, // 0x2E + KEY_PERIOD, // 0x2F + KEY_TAB, // 0x30 + KEY_SPACE, // 0x31 + KEY_TILDE, // 0x32 + KEY_BACKSPACE, // 0x33 + 0, // 0x34 + KEY_ESCAPE, // 0x35 + KEY_RALT, // 0x36 + KEY_LALT, // 0x37 + KEY_LSHIFT, // 0x38 + KEY_CAPSLOCK, // 0x39 + KEY_MAC_LOPT, // 0x3A + KEY_LCONTROL, // 0x3B + KEY_RSHIFT, // 0x3C + KEY_MAC_ROPT, // 0x3D + KEY_RCONTROL, // 0x3E + 0, // 0x3F + 0, // 0x40 + KEY_DECIMAL, // 0x41 + 0, // 0x42 + KEY_MULTIPLY, // 0x43 + 0, // 0x44 + KEY_ADD, // 0x45 + 0, // 0x46 + KEY_NUMLOCK, // 0x47 + 0, // 0x48 + 0, // 0x49 + 0, // 0x4A + KEY_DIVIDE, // 0x4B + KEY_NUMPADENTER, // 0x4C + 0, // 0x4D + KEY_SUBTRACT, // 0x4E + 0, // 0x4F + 0, // 0x50 + KEY_SEPARATOR, // 0x51 + KEY_NUMPAD0, // 0x52 + KEY_NUMPAD1, // 0x53 + KEY_NUMPAD2, // 0x54 + KEY_NUMPAD3, // 0x55 + KEY_NUMPAD4, // 0x56 + KEY_NUMPAD5, // 0x57 + KEY_NUMPAD6, // 0x58 + KEY_NUMPAD7, // 0x59 + 0, // 0x5A + KEY_NUMPAD8, // 0x5B + KEY_NUMPAD9, // 0x5C + 0, // 0x5D + 0, // 0x5E + 0, // 0x5F + KEY_F5, // 0x60 + KEY_F6, // 0x61 + KEY_F7, // 0x62 + KEY_F3, // 0x63 + KEY_F8, // 0x64 + KEY_F9, // 0x65 + 0, // 0x66 + KEY_F11, // 0x67 + 0, // 0x68 + KEY_F13, // 0x69 + KEY_F16, // 0x6A + KEY_F14, // 0x6B + 0, // 0x6C + KEY_F10, // 0x6D + 0, // 0x6E + KEY_F12, // 0x6F + 0, // 0x70 + KEY_F15, // 0x71 + KEY_INSERT, // 0x72 + KEY_HOME, // 0x73 + KEY_PAGE_UP, // 0x74 + KEY_DELETE, // 0x75 + KEY_F4, // 0x76 + KEY_END, // 0x77 + KEY_F2, // 0x78 + KEY_PAGE_DOWN, // 0x79 + KEY_F1, // 0x7A + KEY_LEFT, // 0x7B + KEY_RIGHT, // 0x7C + KEY_DOWN, // 0x7D + KEY_UP, // 0x7E +}; + +static Vector< U8 > sKeyCodeToOS( __FILE__, __LINE__ ); + +#define NSShiftKeyMask ( 1 << 17 ) + +static KeyboardLayoutRef sKeyLayout; +static SInt32 sKeyLayoutKind = -1; +static SInt32 sKeyLayoutID = -1; +static SInt32 sLastKeyLayoutID = -1; + +static void GetKeyboardLayout() +{ + KLGetCurrentKeyboardLayout( &sKeyLayout ); + KLGetKeyboardLayoutProperty( sKeyLayout, kKLKind, ( const void** ) &sKeyLayoutKind ); + KLGetKeyboardLayoutProperty( sKeyLayout, kKLIdentifier, ( const void** ) &sKeyLayoutID ); +} + +static bool KeyboardLayoutHasChanged() +{ + GetKeyboardLayout(); + return ( sKeyLayoutID != sLastKeyLayoutID ); +} + +static UniChar OSKeyCodeToUnicode( UInt16 osKeyCode, bool shift = false ) +{ + // Translate the key code. + + UniChar uniChar = 0; + if( sKeyLayoutKind == kKLKCHRKind ) + { + // KCHR mapping. + + void* KCHRData; + KLGetKeyboardLayoutProperty( sKeyLayout, kKLKCHRData, ( const void** ) & KCHRData ); + + UInt16 key = ( osKeyCode & 0x7f ); + if( shift ) + key |= NSShiftKeyMask; + + UInt32 keyTranslateState = 0; + UInt32 charCode = KeyTranslate( KCHRData, key, &keyTranslateState ); + charCode &= 0xff; + + if( keyTranslateState == 0 && charCode ) + uniChar = charCode; + } + else + { + // UCHR mapping. + + UCKeyboardLayout* uchrData; + KLGetKeyboardLayoutProperty( sKeyLayout, kKLuchrData, ( const void** ) &uchrData ); + + UInt32 deadKeyState; + UniCharCount actualStringLength; + UniChar unicodeString[ 4 ]; + UCKeyTranslate( uchrData, + osKeyCode, + kUCKeyActionDown, + ( shift ? 0x02 : 0 ), // Oh yeah... Apple docs are fun... + LMGetKbdType(), + 0, + &deadKeyState, + sizeof( unicodeString ) / sizeof( unicodeString[ 0 ] ), + &actualStringLength, + unicodeString ); + + if( actualStringLength ) + uniChar = unicodeString[ 0 ]; // Well, Unicode is something else, but... + } + + return uniChar; +} + +static void InitKeyCodeMapping() +{ + const U32 numOSKeyCodes = sizeof( sOSToKeyCode ) / sizeof( sOSToKeyCode[ 0 ] ); + GetKeyboardLayout(); + sLastKeyLayoutID = sKeyLayoutID; + + U32 maxKeyCode = 0; + for( U32 i = 0; i < numOSKeyCodes; ++ i ) + { + sOSToKeyCode[ i ].mCharLower = OSKeyCodeToUnicode( i, false ); + sOSToKeyCode[ i ].mCharUpper = OSKeyCodeToUnicode( i, true ); + + if( sOSToKeyCode[ i ].mKeyCode > maxKeyCode ) + maxKeyCode = sOSToKeyCode[ i ].mKeyCode; + } + + if( !sKeyCodeToOS.size() ) + { + sKeyCodeToOS.setSize( maxKeyCode + 1 ); + dMemset( sKeyCodeToOS.address(), 0, sKeyCodeToOS.size() ); + for( U32 i = 0; i < numOSKeyCodes; ++ i ) + sKeyCodeToOS[ sOSToKeyCode[ i ].mKeyCode ] = i; + } +} + +U8 TranslateOSKeyCode(U8 macKeycode) +{ + AssertWarn(macKeycode < sizeof(sOSToKeyCode) / sizeof(sOSToKeyCode[0]), avar("TranslateOSKeyCode - could not translate code %i", macKeycode)); + if(macKeycode >= sizeof(sOSToKeyCode) / sizeof(sOSToKeyCode[0])) + return KEY_NULL; + + return sOSToKeyCode[ macKeycode ].mKeyCode; +} + +U8 TranslateKeyCodeToOS( U8 keycode ) +{ + return sKeyCodeToOS[ keycode ]; +} + +#pragma mark ---- Clipboard functions ---- +//----------------------------------------------------------------------------- +const char* Platform::getClipboard() +{ + // mac clipboards can contain multiple items, + // and each item can be in several differnt flavors, + // such as unicode or plaintext or pdf, etc. + // scan through the clipboard, and return the 1st piece of actual text. + ScrapRef clip; + char *retBuf = ""; + OSStatus err = noErr; + char *dataBuf = ""; + + // get a local ref to the system clipboard + GetScrapByName( kScrapClipboardScrap, kScrapGetNamedScrap, &clip ); + + + // First try to get unicode data, then try to get plain text data. + Size dataSize = 0; + bool plaintext = false; + err = GetScrapFlavorSize(clip, kScrapFlavorTypeUnicode, &dataSize); + if( err != noErr || dataSize <= 0) + { + Con::errorf("some error getting unicode clip"); + plaintext = true; + err = GetScrapFlavorSize(clip, kScrapFlavorTypeText, &dataSize); + } + + // kick out if we don't have any data. + if( err != noErr || dataSize <= 0) + { + Con::errorf("no data, kicking out. size = %i",dataSize); + return ""; + } + + if( err == noErr && dataSize > 0 ) + { + // ok, we've got something! allocate a buffer and copy it in. + char buf[dataSize+1]; + dMemset(buf, 0, dataSize+1); + dataBuf = buf; + // plain text needs no conversion. + // unicode data needs to be converted to normalized utf-8 format. + if(plaintext) + { + GetScrapFlavorData(clip, kScrapFlavorTypeText, &dataSize, &buf); + retBuf = Con::getReturnBuffer(dataSize + 1); + dMemcpy(retBuf,buf,dataSize); + } + else + { + GetScrapFlavorData(clip, kScrapFlavorTypeUnicode, &dataSize, &buf); + + // normalize + CFStringRef cfBuf = CFStringCreateWithBytes(NULL, (const UInt8*)buf, dataSize, kCFStringEncodingUnicode, false); + CFMutableStringRef normBuf = CFStringCreateMutableCopy(NULL, 0, cfBuf); + CFStringNormalize(normBuf, kCFStringNormalizationFormC); + + // convert to utf-8 + U32 normBufLen = CFStringGetLength(normBuf); + U32 retBufLen = CFStringGetMaximumSizeForEncoding(normBufLen,kCFStringEncodingUTF8) + 1; // +1 for the null terminator + retBuf = Con::getReturnBuffer(retBufLen); + CFStringGetCString( normBuf, retBuf, retBufLen, kCFStringEncodingUTF8); + dataSize = retBufLen; + } + + // manually null terminate, just in case. + retBuf[dataSize] = 0; + } + + // return the data, or the empty string if we did not find any data. + return retBuf; +} + +//----------------------------------------------------------------------------- +bool Platform::setClipboard(const char *text) +{ + ScrapRef clip; + U32 textSize; + OSStatus err = noErr; + + // make sure we have something to copy + textSize = dStrlen(text); + if(textSize == 0) + return false; + + // get a local ref to the system clipboard + GetScrapByName( kScrapClipboardScrap, kScrapClearNamedScrap, &clip ); + + // put the data on the clipboard as text + err = PutScrapFlavor( clip, kScrapFlavorTypeText, kScrapFlavorMaskNone, textSize, text); + + // put the data on the clipboard as unicode + const UTF16 *utf16Data = convertUTF8toUTF16(text); + err |= PutScrapFlavor( clip, kScrapFlavorTypeUnicode, kScrapFlavorMaskNone, + dStrlen(utf16Data) * sizeof(UTF16), utf16Data); + delete [] utf16Data; + + // and see if we were successful. + if( err == noErr ) + return true; + else + return false; +} + +void Input::init() +{ + smManager = NULL; + smActive = false; + + InitKeyCodeMapping(); +} + +U16 Input::getKeyCode( U16 asciiCode ) +{ + if( KeyboardLayoutHasChanged() ) + InitKeyCodeMapping(); + + for( U32 i = 0; i < ( sizeof( sOSToKeyCode ) / sizeof( sOSToKeyCode[ 0 ] ) ); ++ i ) + if( sOSToKeyCode[ i ].mCharLower == asciiCode + || sOSToKeyCode[ i ].mCharUpper == asciiCode ) + return sOSToKeyCode[ i ].mKeyCode; + + return 0; +} + +U16 Input::getAscii( U16 keyCode, KEY_STATE keyState ) +{ + GetKeyboardLayout(); + return OSKeyCodeToUnicode( TranslateKeyCodeToOS( keyCode ), ( keyState == STATE_UPPER ? true : false ) ); +} + +void Input::destroy() +{ +} + +bool Input::enable() +{ + return true; +} + +void Input::disable() +{ +} + +void Input::activate() +{ +} + +void Input::deactivate() +{ +} + +bool Input::isEnabled() +{ + return true; +} + +bool Input::isActive() +{ + return true; +} + +void Input::process() +{ +} + +InputManager* Input::getManager() +{ + return smManager; +} + +ConsoleFunction( enableMouse, bool, 1, 1, "enableMouse()" ) +{ + return true; +} + +ConsoleFunction( disableMouse, void, 1, 1, "disableMouse()" ) +{ +} + +ConsoleFunction( echoInputState, void, 1, 1, "echoInputState()" ) +{ +} + +ConsoleFunction( toggleInputState, void, 1, 1, "toggleInputState()" ) +{ +} + +ConsoleFunction( isJoystickDetected, bool, 1, 1, "Always false on the MAC." ) +{ + return false; +} + +ConsoleFunction( getJoystickAxes, const char*, 2, 2, "(handle instance)" ) +{ + return ""; +} + +ConsoleFunction( deactivateKeyboard, void, 1, 1, "deactivateKeyboard();") +{ + // these are only useful on the windows side. They deal with some vagaries of win32 DirectInput. +} + +ConsoleFunction( activateKeyboard, void, 1, 1, "activateKeyboard();") +{ + // these are only useful on the windows side. They deal with some vagaries of win32 DirectInput. +} diff --git a/Engine/source/platformMac/macCarbMath.cpp b/Engine/source/platformMac/macCarbMath.cpp new file mode 100644 index 000000000..e5c8aab89 --- /dev/null +++ b/Engine/source/platformMac/macCarbMath.cpp @@ -0,0 +1,123 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformMac/platformMacCarb.h" +#include "platform/platform.h" +#include "console/console.h" +#include "math/mMath.h" +#include "core/strings/stringFunctions.h" + +extern void mInstallLibrary_C(); +extern void mInstallLibrary_Vec(); +extern void mInstall_Library_SSE(); + +static MRandomLCG sgPlatRandom; + +U32 Platform::getMathControlState() +{ + return 0; +} + +void Platform::setMathControlStateKnown() +{ + +} + +void Platform::setMathControlState(U32 state) +{ + +} + +//-------------------------------------- +ConsoleFunction( MathInit, void, 1, 10, "(DETECT|C|VEC|SSE)") +{ + U32 properties = CPU_PROP_C; // C entensions are always used + + if (argc == 1) + { + Math::init(0); + return; + } + for (argc--, argv++; argc; argc--, argv++) + { + if (dStricmp(*argv, "DETECT") == 0) { + Math::init(0); + return; + } + if (dStricmp(*argv, "C") == 0) { + properties |= CPU_PROP_C; + continue; + } + if (dStricmp(*argv, "VEC") == 0) { + properties |= CPU_PROP_ALTIVEC; + continue; + } + if( dStricmp( *argv, "SSE" ) == 0 ) + { + properties |= CPU_PROP_SSE; + continue; + } + Con::printf("Error: MathInit(): ignoring unknown math extension '%s'", *argv); + } + Math::init(properties); +} + + + +//------------------------------------------------------------------------------ +void Math::init(U32 properties) +{ + if (!properties) + // detect what's available + properties = Platform::SystemInfo.processor.properties; + else + // Make sure we're not asking for anything that's not supported + properties &= Platform::SystemInfo.processor.properties; + + Con::printf("Math Init:"); + Con::printf(" Installing Standard C extensions"); + mInstallLibrary_C(); + + #if defined(__VEC__) + if (properties & CPU_PROP_ALTIVEC) + { + Con::printf(" Installing Altivec extensions"); + mInstallLibrary_Vec(); + } + #endif + #ifdef TORQUE_CPU_X86 + if( properties & CPU_PROP_SSE ) + { + Con::printf( " Installing SSE extensions" ); + mInstall_Library_SSE(); + } + #endif + + Con::printf(" "); +} + +//------------------------------------------------------------------------------ +F32 Platform::getRandom() +{ + return sgPlatRandom.randF(); +} + diff --git a/Engine/source/platformMac/macCarbMemory.cpp b/Engine/source/platformMac/macCarbMemory.cpp new file mode 100644 index 000000000..2d25d73d0 --- /dev/null +++ b/Engine/source/platformMac/macCarbMemory.cpp @@ -0,0 +1,73 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include +#include +#include + +//-------------------------------------- +void* dRealMalloc(dsize_t in_size) +{ + return malloc(in_size); +} + + +//-------------------------------------- +void dRealFree(void* in_pFree) +{ + free(in_pFree); +} + +void *dMalloc_aligned(dsize_t in_size, int alignment) +{ + return _mm_malloc(in_size, alignment); +} + +void dFree_aligned(void* p) +{ + return _mm_free(p); +} + +void* dMemcpy(void *dst, const void *src, dsize_t size) +{ + return memcpy(dst,src,size); +} + + +//-------------------------------------- +void* dMemmove(void *dst, const void *src, dsize_t size) +{ + return memmove(dst,src,size); +} + +//-------------------------------------- +void* dMemset(void *dst, int c, dsize_t size) +{ + return memset(dst,c,size); +} + +//-------------------------------------- +int dMemcmp(const void *ptr1, const void *ptr2, dsize_t len) +{ + return(memcmp(ptr1, ptr2, len)); +} diff --git a/Engine/source/platformMac/macCarbMutex.cpp b/Engine/source/platformMac/macCarbMutex.cpp new file mode 100644 index 000000000..0aa30acde --- /dev/null +++ b/Engine/source/platformMac/macCarbMutex.cpp @@ -0,0 +1,97 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include +#include +#include +#include "platform/platform.h" +#include "platform/threads/mutex.h" +#include "platform/threads/thread.h" +// TODO: examine & dump errno if pthread_* funcs fail. ( only in debug build ) + +class PlatformMutexData +{ +public: + pthread_mutex_t mMutex; + bool locked; + U32 lockedByThread; +}; + +Mutex::Mutex(void) +{ + int ok; + mData = new PlatformMutexData; + pthread_mutexattr_t attr; + ok = pthread_mutexattr_init(&attr); + ok = pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE); + ok = pthread_mutex_init(&(mData->mMutex),&attr); + AssertFatal(ok == 0, "Mutex() failed: pthread_mutex_init() failed."); + + mData->locked = false; + mData->lockedByThread = 0; +} + +Mutex::~Mutex() +{ + int ok; + ok = pthread_mutex_destroy( &(mData->mMutex) ); + AssertFatal(ok == 0, "~Mutex() failed: pthread_mutex_destroy() failed."); + + delete mData; +} + +bool Mutex::lock( bool block) +{ + int ok; + + if(block) + { + ok = pthread_mutex_lock( &(mData->mMutex) ); + AssertFatal( ok != EINVAL, "Mutex::lockMutex() failed: invalid mutex."); + AssertFatal( ok != EDEADLK, "Mutex::lockMutex() failed: system detected a deadlock!"); + AssertFatal( ok == 0, "Mutex::lockMutex() failed: pthread_mutex_lock() failed -- unknown reason."); + } + else { + ok = pthread_mutex_trylock( &(mData->mMutex) ); + // returns EBUSY if mutex was locked by another thread, + // returns EINVAL if mutex was not a valid mutex pointer, + // returns 0 if lock succeeded. + AssertFatal( ok != EINVAL, "Mutex::lockMutex(non blocking) failed: invalid mutex."); + if( ok != 0 ) + return false; + + AssertFatal( ok == 0, "Mutex::lockMutex(non blocking) failed: pthread_mutex_trylock() failed -- unknown reason."); + } + + mData->locked = true; + mData->lockedByThread = ThreadManager::getCurrentThreadId(); + return true; +} + +void Mutex::unlock() +{ + int ok; + ok = pthread_mutex_unlock( &(mData->mMutex) ); + AssertFatal( ok == 0, "Mutex::unlockMutex() failed: pthread_mutex_unlock() failed."); + mData->locked = false; + mData->lockedByThread = 0; +} diff --git a/Engine/source/platformMac/macCarbProcessControl.cpp b/Engine/source/platformMac/macCarbProcessControl.cpp new file mode 100644 index 000000000..6f316d082 --- /dev/null +++ b/Engine/source/platformMac/macCarbProcessControl.cpp @@ -0,0 +1,65 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformMac/platformMacCarb.h" +#include "platform/event.h" +#include "core/util/journal/process.h" +#include "console/console.h" + +void Platform::postQuitMessage(const U32 in_quitVal) +{ + Process::requestShutdown(); +} + +void Platform::debugBreak() +{ + DebugStr("\pDEBUG_BREAK!"); +} + +void Platform::forceShutdown(S32 returnValue) +{ + exit(returnValue); +} + +void Platform::restartInstance() +{ + // execl() leaves open file descriptors open, that's the main reason it's not + // used here. We want to start fresh. + // get the path to the torque executable + CFBundleRef mainBundle = CFBundleGetMainBundle(); + CFURLRef execURL = CFBundleCopyExecutableURL(mainBundle); + CFStringRef execString = CFURLCopyFileSystemPath(execURL, kCFURLPOSIXPathStyle); + + // append ampersand so that we can launch without blocking. + // encase in quotes so that spaces in the path are accepted. + CFMutableStringRef mut = CFStringCreateMutableCopy(NULL, 0, execString); + CFStringInsert(mut, 0, CFSTR("\"")); + CFStringAppend(mut, CFSTR("\" & ")); + + U32 len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(mut), kCFStringEncodingUTF8); + char *execCString = new char[len+1]; + CFStringGetCString(mut, execCString, len, kCFStringEncodingUTF8); + execCString[len] = '\0'; + + Con::printf("---- %s -----",execCString); + system(execCString); +} diff --git a/Engine/source/platformMac/macCarbSemaphore.cpp b/Engine/source/platformMac/macCarbSemaphore.cpp new file mode 100644 index 000000000..024cda342 --- /dev/null +++ b/Engine/source/platformMac/macCarbSemaphore.cpp @@ -0,0 +1,68 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include +#include "platform/platform.h" +#include "platform/threads/semaphore.h" + +class PlatformSemaphore +{ +public: + MPSemaphoreID mSemaphore; + + PlatformSemaphore(S32 initialCount) + { + OSStatus err = MPCreateSemaphore(S32_MAX - 1, initialCount, &mSemaphore); + AssertFatal(err == noErr, "Failed to allocate semaphore!"); + } + + ~PlatformSemaphore() + { + OSStatus err = MPDeleteSemaphore(mSemaphore); + AssertFatal(err == noErr, "Failed to destroy semaphore!"); + } +}; + +Semaphore::Semaphore(S32 initialCount) +{ + mData = new PlatformSemaphore(initialCount); +} + +Semaphore::~Semaphore() +{ + AssertFatal(mData && mData->mSemaphore, "Semaphore::destroySemaphore: invalid semaphore"); + delete mData; +} + +bool Semaphore::acquire( bool block, S32 timeoutMS ) +{ + AssertFatal(mData && mData->mSemaphore, "Semaphore::acquireSemaphore: invalid semaphore"); + OSStatus err = MPWaitOnSemaphore(mData->mSemaphore, block ? ( timeoutMS == -1 ? kDurationForever : timeoutMS ) : kDurationImmediate); + return(err == noErr); +} + +void Semaphore::release() +{ + AssertFatal(mData && mData->mSemaphore, "Semaphore::releaseSemaphore: invalid semaphore"); + OSStatus err = MPSignalSemaphore(mData->mSemaphore); + AssertFatal(err == noErr, "Failed to release semaphore!"); +} diff --git a/Engine/source/platformMac/macCarbStrings.cpp b/Engine/source/platformMac/macCarbStrings.cpp new file mode 100644 index 000000000..1f3ecb0c9 --- /dev/null +++ b/Engine/source/platformMac/macCarbStrings.cpp @@ -0,0 +1,69 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include +#include +#include +#include +#include +#include "core/strings/stringFunctions.h" + +char *dStrnew(const char *src) +{ + char *buffer = new char[dStrlen(src) + 1]; + dStrcpy(buffer, src); + return buffer; +} + +char* dStrstr(char *str1, char *str2) +{ + return strstr(str1,str2); +} + +int dSprintf(char *buffer, dsize_t /*bufferSize*/, const char *format, ...) +{ + va_list args; + va_start(args, format); + S32 len = vsprintf(buffer, format, args); + + return (len); +} + + +int dVsprintf(char *buffer, dsize_t /*bufferSize*/, const char *format, void *arglist) +{ + S32 len = vsprintf(buffer, format, (char*)arglist); + + return (len); +} + +int dFflushStdout() +{ + return fflush(stdout); +} + +int dFflushStderr() +{ + return fflush(stderr); +} + diff --git a/Engine/source/platformMac/macCarbThread.cpp b/Engine/source/platformMac/macCarbThread.cpp new file mode 100644 index 000000000..bc83e111e --- /dev/null +++ b/Engine/source/platformMac/macCarbThread.cpp @@ -0,0 +1,159 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include +#include "platform/threads/thread.h" +#include "platform/threads/semaphore.h" +#include "platform/threads/mutex.h" +#include + +class PlatformThreadData +{ +public: + ThreadRunFunction mRunFunc; + void* mRunArg; + Thread* mThread; + Semaphore mGateway; // default count is 1 + U32 mThreadID; + bool mDead; +}; + +ThreadManager::MainThreadId ThreadManager::smMainThreadId; + +//----------------------------------------------------------------------------- +// Function: ThreadRunHandler +// Summary: Calls Thread::run() with the thread's specified run argument. +// Neccesary because Thread::run() is provided as a non-threaded +// way to execute the thread's run function. So we have to keep +// track of the thread's lock here. +static void *ThreadRunHandler(void * arg) +{ + PlatformThreadData *mData = reinterpret_cast(arg); + Thread *thread = mData->mThread; + + // mThreadID is filled in twice, once here and once in pthread_create(). + // We fill in mThreadID here as well as in pthread_create() because addThread() + // can execute before pthread_create() returns and sets mThreadID. + // The value from pthread_create() and pthread_self() are guaranteed to be equivalent (but not identical) + mData->mThreadID = ThreadManager::getCurrentThreadId(); + + ThreadManager::addThread(thread); + thread->run(mData->mRunArg); + ThreadManager::removeThread(thread); + + bool autoDelete = thread->autoDelete; + + mData->mThreadID = 0; + mData->mDead = true; + mData->mGateway.release(); + + if( autoDelete ) + delete thread; + + // return value for pthread lib's benefit + return NULL; + // the end of this function is where the created pthread will die. +} + +//----------------------------------------------------------------------------- +Thread::Thread(ThreadRunFunction func, void* arg, bool start_thread, bool autodelete) +{ + AssertFatal( !start_thread, "Thread::Thread() - auto-starting threads from ctor has been disallowed since the run() method is virtual" ); + + mData = new PlatformThreadData; + mData->mRunFunc = func; + mData->mRunArg = arg; + mData->mThread = this; + mData->mThreadID = 0; + mData->mDead = false; + autoDelete = autodelete; +} + +Thread::~Thread() +{ + stop(); + if( isAlive() ) + join(); + + delete mData; +} + +void Thread::start( void* arg ) +{ + // cause start to block out other pthreads from using this Thread, + // at least until ThreadRunHandler exits. + mData->mGateway.acquire(); + + // reset the shouldStop flag, so we'll know when someone asks us to stop. + shouldStop = false; + + mData->mDead = false; + + if( !mData->mRunArg ) + mData->mRunArg = arg; + + pthread_create((pthread_t*)(&mData->mThreadID), NULL, ThreadRunHandler, mData); +} + +bool Thread::join() +{ + // not using pthread_join here because pthread_join cannot deal + // with multiple simultaneous calls. + + mData->mGateway.acquire(); + AssertFatal( !isAlive(), "Thread::join() - thread not dead after join()" ); + mData->mGateway.release(); + + return true; +} + +void Thread::run(void* arg) +{ + if(mData->mRunFunc) + mData->mRunFunc(arg); +} + +bool Thread::isAlive() +{ + return ( !mData->mDead ); +} + +U32 Thread::getId() +{ + return mData->mThreadID; +} + +void Thread::_setName( const char* ) +{ + // Not supported. Wading through endless lists of Thread-1, Thread-2, Thread-3, ... trying to find + // that one thread you are looking for is just so much fun. +} + +U32 ThreadManager::getCurrentThreadId() +{ + return (U32)pthread_self(); +} + +bool ThreadManager::compare(U32 threadId_1, U32 threadId_2) +{ + return pthread_equal((_opaque_pthread_t*)threadId_1, (_opaque_pthread_t*)threadId_2); +} diff --git a/Engine/source/platformMac/macCarbTime.cpp b/Engine/source/platformMac/macCarbTime.cpp new file mode 100644 index 000000000..823a7842a --- /dev/null +++ b/Engine/source/platformMac/macCarbTime.cpp @@ -0,0 +1,143 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include +#include "platform/platformTimer.h" +#include +#include + +//-------------------------------------- + +static U32 sgCurrentTime = 0; + +//-------------------------------------- +void Platform::getLocalTime(LocalTime <) +{ + struct tm systime; + time_t long_time; + + /// Get time as long integer. + time( &long_time ); + /// Convert to local time, thread safe. + localtime_r( &long_time, &systime ); + + /// Fill the return struct + lt.sec = systime.tm_sec; + lt.min = systime.tm_min; + lt.hour = systime.tm_hour; + lt.month = systime.tm_mon; + lt.monthday = systime.tm_mday; + lt.weekday = systime.tm_wday; + lt.year = systime.tm_year; + lt.yearday = systime.tm_yday; + lt.isdst = systime.tm_isdst; +} + +String Platform::localTimeToString( const LocalTime < ) +{ + tm systime; + + systime.tm_sec = lt.sec; + systime.tm_min = lt.min; + systime.tm_hour = lt.hour; + systime.tm_mon = lt.month; + systime.tm_mday = lt.monthday; + systime.tm_wday = lt.weekday; + systime.tm_year = lt.year; + systime.tm_yday = lt.yearday; + systime.tm_isdst = lt.isdst; + + return asctime( &systime ); +} + +/// Gets the time in seconds since the Epoch +U32 Platform::getTime() +{ + time_t epoch_time; + time( &epoch_time ); + return epoch_time; +} + +/// Gets the time in milliseconds since some epoch. In this case, system start time. +/// Storing milliseconds in a U32 overflows every 49.71 days +U32 Platform::getRealMilliseconds() +{ + // Duration is a S32 value. + // if negative, it is in microseconds. + // if positive, it is in milliseconds. + Duration durTime = AbsoluteToDuration(UpTime()); + U32 ret; + if( durTime < 0 ) + ret = durTime / -1000; + else + ret = durTime; + + return ret; +} + +U32 Platform::getVirtualMilliseconds() +{ + return sgCurrentTime; +} + +void Platform::advanceTime(U32 delta) +{ + sgCurrentTime += delta; +} + +/// Asks the operating system to put the process to sleep for at least ms milliseconds +void Platform::sleep(U32 ms) +{ + // note: this will overflow if you want to sleep for more than 49 days. just so ye know. + usleep( ms * 1000 ); +} + +//---------------------------------------------------------------------------------- +PlatformTimer* PlatformTimer::create() +{ + return new DefaultPlatformTimer; +} + +void Platform::fileToLocalTime(const FileTime & ft, LocalTime * lt) +{ + if(!lt) + return; + + time_t long_time = ft; + + struct tm systime; + + /// Convert to local time, thread safe. + localtime_r( &long_time, &systime ); + + /// Fill the return struct + lt->sec = systime.tm_sec; + lt->min = systime.tm_min; + lt->hour = systime.tm_hour; + lt->month = systime.tm_mon; + lt->monthday = systime.tm_mday; + lt->weekday = systime.tm_wday; + lt->year = systime.tm_year; + lt->yearday = systime.tm_yday; + lt->isdst = systime.tm_isdst; +} + diff --git a/Engine/source/platformMac/macCarbUtil.cpp b/Engine/source/platformMac/macCarbUtil.cpp new file mode 100644 index 000000000..c44204ee7 --- /dev/null +++ b/Engine/source/platformMac/macCarbUtil.cpp @@ -0,0 +1,65 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include +#include + +#include "platform/platform.h" +#include "core/strings/stringFunctions.h" + +void Platform::outputDebugString( const char *string, ... ) +{ +#ifdef TORQUE_DEBUG + char buffer[ 2048 ]; + + va_list args; + va_start( args, string ); + + dVsprintf( buffer, sizeof( buffer ), string, args ); + va_end( args ); + + U32 length = strlen( buffer ); + if( length == ( sizeof( buffer ) - 1 ) ) + length --; + + buffer[ length ] = '\n'; + buffer[ length + 1 ] = '\0'; + + fputs( buffer, stderr ); + fflush(stderr); +#endif +} + +#pragma mark ---- Platform utility funcs ---- +//-------------------------------------- +// Web browser function: +//-------------------------------------- +bool Platform::openWebBrowser( const char* webAddress ) +{ + OSStatus err; + CFURLRef url = CFURLCreateWithBytes(NULL,(UInt8*)webAddress,dStrlen(webAddress),kCFStringEncodingASCII,NULL); + err = LSOpenCFURLRef(url,NULL); + CFRelease(url); + + return(err==noErr); +} + diff --git a/Engine/source/platformMac/macCarbVolume.cpp b/Engine/source/platformMac/macCarbVolume.cpp new file mode 100644 index 000000000..f40e28991 --- /dev/null +++ b/Engine/source/platformMac/macCarbVolume.cpp @@ -0,0 +1,189 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include + +#include "platform/platform.h" +#include "platformMac/macCarbVolume.h" +#include "platform/platformVolume.h" +#include "console/console.h" + + +//#define DEBUG_SPEW + + +struct MacFileSystemChangeNotifier::Event +{ + FSEventStreamRef mStream; + Torque::Path mDir; + bool mHasChanged; +}; + + +static void fsNotifyCallback( + ConstFSEventStreamRef stream, + void* callbackInfo, + size_t numEvents, + void* eventPaths, + const FSEventStreamEventFlags eventFlags[], + const FSEventStreamEventId eventIds[] ) +{ + MacFileSystemChangeNotifier::Event* event = + reinterpret_cast< MacFileSystemChangeNotifier::Event* >( callbackInfo ); + + // Defer handling this to internalProcessOnce() so we stay in + // line with how the volume system expects notifications to + // be reported. + + event->mHasChanged = true; +} + +//----------------------------------------------------------------------------- +// Change notifications. +//----------------------------------------------------------------------------- + + +MacFileSystemChangeNotifier::MacFileSystemChangeNotifier( MacFileSystem* fs ) + : Parent( fs ) +{ + VECTOR_SET_ASSOCIATION( mEvents ); +} + +MacFileSystemChangeNotifier::~MacFileSystemChangeNotifier() +{ + for( U32 i = 0, num = mEvents.size(); i < num; ++ i ) + { + FSEventStreamStop( mEvents[ i ]->mStream ); + FSEventStreamInvalidate( mEvents[ i ]->mStream ); + FSEventStreamRelease( mEvents[ i ]->mStream ); + + SAFE_DELETE( mEvents[ i ] ); + } +} + +void MacFileSystemChangeNotifier::internalProcessOnce() +{ + for( U32 i = 0; i < mEvents.size(); ++ i ) + if( mEvents[ i ]->mHasChanged ) + { + // Signal the change. + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[MacFileSystemChangeNotifier] Directory %i changed: '%s'", + i + 1, mEvents[ i ]->mDir.getFullPath().c_str() ); + #endif + + internalNotifyDirChanged( mEvents[ i ]->mDir ); + mEvents[i ]->mHasChanged = false; + } +} + +bool MacFileSystemChangeNotifier::internalAddNotification( const Torque::Path& dir ) +{ + // Map the path. + + Torque::Path fullFSPath = mFS->mapTo( dir ); + String osPath = PathToOS( fullFSPath ); + + // Create event stream. + + Event* event = new Event; + + CFStringRef path = CFStringCreateWithCharacters( NULL, osPath.utf16(), osPath.numChars() ); + CFArrayRef paths = CFArrayCreate( NULL, ( const void** ) &path, 1, NULL ); + + FSEventStreamRef stream; + CFAbsoluteTime latency = 3.f; + + FSEventStreamContext context; + dMemset( &context, 0, sizeof( context ) ); + context.info = event; + + stream = FSEventStreamCreate( + NULL, + &fsNotifyCallback, + &context, + paths, + kFSEventStreamEventIdSinceNow, + latency, + kFSEventStreamCreateFlagNone + ); + + event->mStream = stream; + event->mDir = dir; + event->mHasChanged = false; + + mEvents.push_back( event ); + + // Put it in the run loop and start the stream. + + FSEventStreamScheduleWithRunLoop( stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode ); + FSEventStreamStart( stream ); + + CFRelease( path ); + CFRelease( paths ); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[MacFileSystemChangeNotifier] Added change notification %i to '%s' (full path: %s)", + mEvents.size(), dir.getFullPath().c_str(), osPath.c_str() ); + #endif + + return true; +} + +bool MacFileSystemChangeNotifier::internalRemoveNotification( const Torque::Path& dir ) +{ + for( U32 i = 0, num = mEvents.size(); i < num; ++ i ) + if( mEvents[ i ]->mDir == dir ) + { + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[MacFileSystemChangeNotifier] Removing change notification %i from '%s'", + i + 1, dir.getFullPath().c_str() ); + #endif + + FSEventStreamStop( mEvents[ i ]->mStream ); + FSEventStreamInvalidate( mEvents[ i ]->mStream ); + FSEventStreamRelease( mEvents[ i ]->mStream ); + + SAFE_DELETE( mEvents[ i ] ); + + mEvents.erase( i ); + + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Platform API. +//----------------------------------------------------------------------------- + +Torque::FS::FileSystemRef Platform::FS::createNativeFS( const String &volume ) +{ + return new MacFileSystem( volume ); +} + +bool Torque::FS::VerifyWriteAccess(const Torque::Path &path) +{ + return true; +} diff --git a/Engine/source/platformMac/macCarbVolume.h b/Engine/source/platformMac/macCarbVolume.h new file mode 100644 index 000000000..d5223cf16 --- /dev/null +++ b/Engine/source/platformMac/macCarbVolume.h @@ -0,0 +1,79 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MACCARBVOLUME_H_ +#define _MACCARBVOLUME_H_ + +#ifndef _POSIXVOLUME_H_ + #include "platformPOSIX/posixVolume.h" +#endif +#ifndef _TVECTOR_H_ + #include "core/util/tVector.h" +#endif + + +class MacFileSystem; + + +/// File system change notifications on Mac. +class MacFileSystemChangeNotifier : public Torque::FS::FileSystemChangeNotifier +{ + public: + + typedef Torque::FS::FileSystemChangeNotifier Parent; + + struct Event; + + protected: + + /// Array of FSEventStream events set up. Uses pointers to dynamically + /// allocated memory rather than allocating inline with the array to be + /// able to pass around pointers without colliding with array reallocation. + Vector< Event* > mEvents; + + // FileSystemChangeNotifier. + virtual void internalProcessOnce(); + virtual bool internalAddNotification( const Torque::Path& dir ); + virtual bool internalRemoveNotification( const Torque::Path& dir ); + + public: + + MacFileSystemChangeNotifier( MacFileSystem* fs ); + + virtual ~MacFileSystemChangeNotifier(); +}; + +/// Mac filesystem. +class MacFileSystem : public Torque::Posix::PosixFileSystem +{ + public: + + typedef Torque::Posix::PosixFileSystem Parent; + + MacFileSystem( String volume ) + : Parent( volume ) + { + mChangeNotifier = new MacFileSystemChangeNotifier( this ); + } +}; + +#endif // !_MACCARBVOLUME_H_ diff --git a/Engine/source/platformMac/macCocoaDialogs.mm b/Engine/source/platformMac/macCocoaDialogs.mm new file mode 100644 index 000000000..ec7d5003c --- /dev/null +++ b/Engine/source/platformMac/macCocoaDialogs.mm @@ -0,0 +1,623 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// Get our GL header included before Apple's +#include "platformMac/platformMacCarb.h" +// Don't include Apple's +#define __gl_h_ + +#include "platform/tmm_off.h" +#include +#include "platform/tmm_on.h" + +#include "console/simBase.h" +#include "platform/nativeDialogs/fileDialog.h" +#include "platform/threads/mutex.h" +#include "core/util/safeDelete.h" +#include "math/mMath.h" +#include "core/strings/unicode.h" +#include "console/consoleTypes.h" +#include "platform/threads/thread.h" +#include "platform/threads/semaphore.h" + +class FileDialogOpaqueData +{ +public: + Semaphore *sem; + FileDialogOpaqueData() { sem = new Semaphore(0); } + ~FileDialogOpaqueData() { delete sem; } +}; + +class FileDialogFileExtList +{ +public: + Vector list; + UTF8* data; + + FileDialogFileExtList(const char* exts) { data = dStrdup(exts); } + ~FileDialogFileExtList() { SAFE_DELETE(data); } +}; + +class FileDialogFileTypeList +{ +public: + UTF8* filterData; + Vector names; + Vector exts; + bool any; + + FileDialogFileTypeList(const char* filter) { filterData = dStrdup(filter); any = false;} + ~FileDialogFileTypeList() + { + SAFE_DELETE(filterData); + for(U32 i = 0; i < exts.size(); i++) + delete exts[i]; + } +}; + +#undef new + +//----------------------------------------------------------------------------- +// PlatformFileDlgData Implementation +//----------------------------------------------------------------------------- +FileDialogData::FileDialogData() +{ + // Default Path + // + // Try to provide consistent experience by recalling the last file path + // - else + // Default to Working Directory if last path is not set or is invalid + mDefaultPath = StringTable->insert( Con::getVariable("Tools::FileDialogs::LastFilePath") ); + if( mDefaultPath == StringTable->lookup("") || !Platform::isDirectory( mDefaultPath ) ) + mDefaultPath = Platform::getCurrentDirectory(); + + mDefaultFile = StringTable->insert(""); + mFilters = StringTable->insert(""); + mFile = StringTable->insert(""); + mTitle = StringTable->insert(""); + + mStyle = 0; + + mOpaqueData = new FileDialogOpaqueData(); + +} +FileDialogData::~FileDialogData() +{ + delete mOpaqueData; +} + +//----------------------------------------------------------------------------- +// FileDialog Implementation +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(FileDialog); + +FileDialog::FileDialog() : mData() +{ + // Default to File Must Exist Open Dialog style + mData.mStyle = FileDialogData::FDS_OPEN | FileDialogData::FDS_MUSTEXIST; + mChangePath = false; +} + +FileDialog::~FileDialog() +{ +} + +void FileDialog::initPersistFields() +{ + // why is this stuff buried in another class? + addProtectedField("DefaultPath", TypeString, Offset(mData.mDefaultPath, FileDialog), &setDefaultPath, &defaultProtectedGetFn, "Default Path when Dialog is shown"); + addProtectedField("DefaultFile", TypeString, Offset(mData.mDefaultFile, FileDialog), &setDefaultFile, &defaultProtectedGetFn, "Default File when Dialog is shown"); + addProtectedField("FileName", TypeString, Offset(mData.mFile, FileDialog), &setFile, &defaultProtectedGetFn, "Default File when Dialog is shown"); + addProtectedField("Filters", TypeString, Offset(mData.mFilters, FileDialog), &setFilters, &defaultProtectedGetFn, "Default File when Dialog is shown"); + addField("Title", TypeString, Offset(mData.mTitle, FileDialog), "Default File when Dialog is shown"); + addProtectedField("ChangePath", TypeBool, Offset(mChangePath, FileDialog), &setChangePath, &getChangePath, "True/False whether to set the working directory to the directory returned by the dialog" ); + Parent::initPersistFields(); +} + + +static FileDialogFileExtList* _MacCarbGetFileExtensionsFromString(const char* filter) +{ + FileDialogFileExtList* list = new FileDialogFileExtList(filter); + + char* token = list->data; + char* place = list->data; + + for( ; *place; place++) + { + if(*place != ';') + continue; + + *place = '\0'; + + list->list.push_back(token); + + ++place; + token = place; + } + // last token + list->list.push_back(token); + + return list; + +} + +static FileDialogFileTypeList* _MacCarbGetFileTypesFromString(const char* filter) +{ + FileDialogFileTypeList &list = *(new FileDialogFileTypeList(filter)); + + char* token = list.filterData; + char* place = list.filterData; + + // scan the filter list until we hit a null. + // when we see the separator '|', replace it with a null, and save the token + // format is description|extension|description|extension + bool isDesc = true; + for( ; *place; place++) + { + if(*place != '|') + continue; + + *place = '\0'; + + if(isDesc) + list.names.push_back(token); + else + { + // detect *.* + if(dStrstr((const char*)token, "*.*")) + list.any = true; + + list.exts.push_back(_MacCarbGetFileExtensionsFromString(token)); + } + + + isDesc = !isDesc; + ++place; + token = place; + } + list.exts.push_back(_MacCarbGetFileExtensionsFromString(token)); + + return &list; +} + +static NSArray* _MacCocoaCreateAndRunSavePanel(FileDialogData &mData) +{ + NSSavePanel* panel = [NSSavePanel savePanel]; + + // User freedom niceties + [panel setCanCreateDirectories:YES]; + [panel setCanSelectHiddenExtension:YES]; + [panel setTreatsFilePackagesAsDirectories:YES]; + + NSString *initialFile = [[NSString stringWithUTF8String:mData.mDefaultFile] lastPathComponent]; + + // we only use mDefaultDir if mDefault path is not set. + NSString *dir; + if(dStrlen(mData.mDefaultPath) < 1) + dir = [[NSString stringWithUTF8String:mData.mDefaultFile] stringByDeletingLastPathComponent]; + else + dir = [NSString stringWithUTF8String: mData.mDefaultPath]; + [panel setDirectory:dir]; + + // todo: move file type handling to an accessory view. + // parse file types + FileDialogFileTypeList *fTypes = _MacCarbGetFileTypesFromString(mData.mFilters); + + // fill an array with the possible file types + NSMutableArray* types = [NSMutableArray arrayWithCapacity:10]; + for(U32 i = 0; i < fTypes->exts.size(); i++) + { + for(U32 j = 0; j < fTypes->exts[i]->list.size(); j++) + { + char* ext = fTypes->exts[i]->list[j]; + if(ext) + { + if(dStrlen(ext) == 0) + continue; + if(dStrncmp(ext, "*.*", 3) == 0) + continue; + if(dStrncmp(ext, "*.", 2) == 0) + ext+=2; + + [types addObject:[NSString stringWithUTF8String:ext]]; + } + } + } + if([types count] > 0) + [panel setAllowedFileTypes:types]; + + // if any file type was *.*, user may select any file type. + [panel setAllowsOtherFileTypes:fTypes->any]; + + + //--------------------------------------------------------------------------- + // Display the panel, enter a modal loop. This blocks. + //--------------------------------------------------------------------------- + U32 button = [panel runModalForDirectory:dir file:initialFile]; + + // return the file name + NSMutableArray *array = [NSMutableArray arrayWithCapacity:10]; + if(button != NSFileHandlingPanelCancelButton) + [array addObject:[panel filename]]; + + delete fTypes; + + return array; + + // TODO: paxorr: show as sheet + // crashes when we try to display the window as a sheet. Not sure why. + // the sheet is instantly dismissed, and crashes as it's dismissing itself. + // here's the code snippet to get an nswindow from our carbon WindowRef + //NSWindow *nsAppWindow = [[NSWindow alloc] initWithWindowRef:platState.appWindow]; +} + +NSArray* _MacCocoaCreateAndRunOpenPanel(FileDialogData &mData) +{ + NSOpenPanel* panel = [NSOpenPanel openPanel]; + + // User freedom niceties + [panel setCanCreateDirectories:YES]; + [panel setCanSelectHiddenExtension:YES]; + [panel setTreatsFilePackagesAsDirectories:YES]; + + [panel setAllowsMultipleSelection:(mData.mStyle & FileDialogData::FDS_MULTIPLEFILES)]; + + // + bool chooseDir = (mData.mStyle & FileDialogData::FDS_BROWSEFOLDER); + [panel setCanChooseFiles: !chooseDir ]; + [panel setCanChooseDirectories: chooseDir ]; + if(chooseDir) + { + [panel setPrompt:@"Choose"]; + [panel setTitle:@"Choose Folder"]; + } + + NSString *initialFile = [[NSString stringWithUTF8String:mData.mDefaultFile] lastPathComponent]; + + // we only use mDefaultDir if mDefault path is not set. + NSString *dir; + if(dStrlen(mData.mDefaultPath) < 1) + dir = [[NSString stringWithUTF8String:mData.mDefaultFile] stringByDeletingLastPathComponent]; + else + dir = [NSString stringWithUTF8String: mData.mDefaultPath]; + [panel setDirectory:dir]; + + // todo: move file type handling to an accessory view. + // parse file types + FileDialogFileTypeList *fTypes = _MacCarbGetFileTypesFromString(mData.mFilters); + + // fill an array with the possible file types + NSMutableArray* types = [NSMutableArray arrayWithCapacity:10]; + for(U32 i = 0; i < fTypes->exts.size(); i++) + { + for(U32 j = 0; j < fTypes->exts[i]->list.size(); j++) + { + char* ext = fTypes->exts[i]->list[j]; + if(ext) + { + if(dStrncmp(ext, "*.", 2) == 0) + ext+=2; + + [types addObject:[NSString stringWithUTF8String:ext]]; + } + } + } + if([types count] > 0) + [panel setAllowedFileTypes:types]; + + // if any file type was *.*, user may select any file type. + [panel setAllowsOtherFileTypes:fTypes->any]; + + + //--------------------------------------------------------------------------- + // Display the panel, enter a modal loop. This blocks. + //--------------------------------------------------------------------------- + U32 button = [panel runModalForDirectory:dir file:initialFile ]; + + // return the file name + NSMutableArray *array = [NSMutableArray arrayWithCapacity:10]; + if(button != NSFileHandlingPanelCancelButton) + [array addObject:[panel filename]]; + + delete fTypes; + + return array; +} + +void MacCarbShowDialog(void* dialog) +{ + FileDialog* d = static_cast(dialog); + d->Execute(); +} +// +// Execute Method +// +bool FileDialog::Execute() +{ +// if(! ThreadManager::isCurrentThread(platState.firstThreadId)) +// { +// MacCarbSendTorqueEventToMain(kEventTorqueModalDialog,this); +// mData.mOpaqueData->sem->acquire(); +// return; +// } + + NSArray* nsFileArray; + if(mData.mStyle & FileDialogData::FDS_OPEN) + nsFileArray = _MacCocoaCreateAndRunOpenPanel(mData); + else if(mData.mStyle & FileDialogData::FDS_SAVE) + nsFileArray = _MacCocoaCreateAndRunSavePanel(mData); + else + { + Con::errorf("Bad File Dialog Setup."); + mData.mOpaqueData->sem->release(); + return false; + } + + if([nsFileArray count] == 0) + return false; + + if(! (mData.mStyle & FileDialogData::FDS_MULTIPLEFILES) && [nsFileArray count] >= 1) + { + const UTF8* f = [(NSString*)[nsFileArray objectAtIndex:0] UTF8String]; + mData.mFile = StringTable->insert(f); + } + else + { + for(U32 i = 0; i < [nsFileArray count]; i++) + { + const UTF8* f = [(NSString*)[nsFileArray objectAtIndex:i] UTF8String]; + setDataField(StringTable->insert("files"), Con::getIntArg(i), StringTable->insert(f)); + } + setDataField(StringTable->insert("fileCount"), NULL, Con::getIntArg([nsFileArray count])); + } + mData.mOpaqueData->sem->release(); + + + return true; + +} + +ConsoleMethod( FileDialog, Execute, bool, 2, 2, "%fileDialog.Execute();" ) +{ + return object->Execute(); +} + +//----------------------------------------------------------------------------- +// Dialog Filters +//----------------------------------------------------------------------------- +bool FileDialog::setFilters(void* obj, const char* index, const char* data) +{ + // Will do validate on write at some point. + if( !data ) + return true; + + return true; + +}; + + +//----------------------------------------------------------------------------- +// Default Path Property - String Validated on Write +//----------------------------------------------------------------------------- +bool FileDialog::setDefaultPath(void* obj, const char* index, const char* data) +{ + + if( !data ) + return true; + + return true; + +}; + +//----------------------------------------------------------------------------- +// Default File Property - String Validated on Write +//----------------------------------------------------------------------------- +bool FileDialog::setDefaultFile(void* obj, const char* index, const char* data) +{ + if( !data ) + return true; + + return true; +}; + +//----------------------------------------------------------------------------- +// ChangePath Property - Change working path on successful file selection +//----------------------------------------------------------------------------- +bool FileDialog::setChangePath(void* obj, const char* index, const char* data) +{ + bool bChangePath = dAtob( data ); + + FileDialog *pDlg = static_cast( obj ); + + if( bChangePath ) + pDlg->mData.mStyle |= FileDialogData::FDS_CHANGEPATH; + else + pDlg->mData.mStyle &= ~FileDialogData::FDS_CHANGEPATH; + + return true; +}; + +const char* FileDialog::getChangePath(void* obj, const char* data) +{ + FileDialog *pDlg = static_cast( obj ); + if( pDlg->mData.mStyle & FileDialogData::FDS_CHANGEPATH ) + return StringTable->insert("true"); + else + return StringTable->insert("false"); +} + +bool FileDialog::setFile(void* obj, const char* index, const char* data) +{ + return false; +}; + +//----------------------------------------------------------------------------- +// OpenFileDialog Implementation +//----------------------------------------------------------------------------- +OpenFileDialog::OpenFileDialog() +{ + // Default File Must Exist + mData.mStyle = FileDialogData::FDS_OPEN | FileDialogData::FDS_MUSTEXIST; +} + +OpenFileDialog::~OpenFileDialog() +{ + mMustExist = true; + mMultipleFiles = false; +} + +IMPLEMENT_CONOBJECT(OpenFileDialog); + +//----------------------------------------------------------------------------- +// Console Properties +//----------------------------------------------------------------------------- +void OpenFileDialog::initPersistFields() +{ + addProtectedField("MustExist", TypeBool, Offset(mMustExist, OpenFileDialog), &setMustExist, &getMustExist, "True/False whether the file returned must exist or not" ); + addProtectedField("MultipleFiles", TypeBool, Offset(mMultipleFiles, OpenFileDialog), &setMultipleFiles, &getMultipleFiles, "True/False whether multiple files may be selected and returned or not" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- +// File Must Exist - Boolean +//----------------------------------------------------------------------------- +bool OpenFileDialog::setMustExist(void* obj, const char* index, const char* data) +{ + bool bMustExist = dAtob( data ); + + OpenFileDialog *pDlg = static_cast( obj ); + + if( bMustExist ) + pDlg->mData.mStyle |= FileDialogData::FDS_MUSTEXIST; + else + pDlg->mData.mStyle &= ~FileDialogData::FDS_MUSTEXIST; + + return true; +}; + +const char* OpenFileDialog::getMustExist(void* obj, const char* data) +{ + OpenFileDialog *pDlg = static_cast( obj ); + if( pDlg->mData.mStyle & FileDialogData::FDS_MUSTEXIST ) + return StringTable->insert("true"); + else + return StringTable->insert("false"); +} + +//----------------------------------------------------------------------------- +// Can Select Multiple Files - Boolean +//----------------------------------------------------------------------------- +bool OpenFileDialog::setMultipleFiles(void* obj, const char* index, const char* data) +{ + bool bMustExist = dAtob( data ); + + OpenFileDialog *pDlg = static_cast( obj ); + + if( bMustExist ) + pDlg->mData.mStyle |= FileDialogData::FDS_MULTIPLEFILES; + else + pDlg->mData.mStyle &= ~FileDialogData::FDS_MULTIPLEFILES; + + return true; +}; + +const char* OpenFileDialog::getMultipleFiles(void* obj, const char* data) +{ + OpenFileDialog *pDlg = static_cast( obj ); + if( pDlg->mData.mStyle & FileDialogData::FDS_MULTIPLEFILES ) + return StringTable->insert("true"); + else + return StringTable->insert("false"); +} + +//----------------------------------------------------------------------------- +// SaveFileDialog Implementation +//----------------------------------------------------------------------------- +SaveFileDialog::SaveFileDialog() +{ + // Default File Must Exist + mData.mStyle = FileDialogData::FDS_SAVE | FileDialogData::FDS_OVERWRITEPROMPT; + mOverwritePrompt = true; +} + +SaveFileDialog::~SaveFileDialog() +{ +} + +IMPLEMENT_CONOBJECT(SaveFileDialog); + +//----------------------------------------------------------------------------- +// Console Properties +//----------------------------------------------------------------------------- +void SaveFileDialog::initPersistFields() +{ + addProtectedField("OverwritePrompt", TypeBool, Offset(mOverwritePrompt, SaveFileDialog), &setOverwritePrompt, &getOverwritePrompt, "True/False whether the dialog should prompt before accepting an existing file name" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- +// Prompt on Overwrite - Boolean +//----------------------------------------------------------------------------- +bool SaveFileDialog::setOverwritePrompt(void* obj, const char* index, const char* data) +{ + bool bOverwrite = dAtob( data ); + + SaveFileDialog *pDlg = static_cast( obj ); + + if( bOverwrite ) + pDlg->mData.mStyle |= FileDialogData::FDS_OVERWRITEPROMPT; + else + pDlg->mData.mStyle &= ~FileDialogData::FDS_OVERWRITEPROMPT; + + return true; +}; + +const char* SaveFileDialog::getOverwritePrompt(void* obj, const char* data) +{ + SaveFileDialog *pDlg = static_cast( obj ); + if( pDlg->mData.mStyle & FileDialogData::FDS_OVERWRITEPROMPT ) + return StringTable->insert("true"); + else + return StringTable->insert("false"); +} + +//----------------------------------------------------------------------------- +// OpenFolderDialog Implementation +//----------------------------------------------------------------------------- + +OpenFolderDialog::OpenFolderDialog() +{ + mData.mStyle = FileDialogData::FDS_OPEN | FileDialogData::FDS_OVERWRITEPROMPT | FileDialogData::FDS_BROWSEFOLDER; + + mMustExistInDir = ""; +} + +IMPLEMENT_CONOBJECT(OpenFolderDialog); + +void OpenFolderDialog::initPersistFields() +{ + addField("fileMustExist", TypeFilename, Offset(mMustExistInDir, OpenFolderDialog), "File that must in selected folder for it to be valid"); + + Parent::initPersistFields(); +} diff --git a/Engine/source/platformMac/macCocoaPlatform.mm b/Engine/source/platformMac/macCocoaPlatform.mm new file mode 100644 index 000000000..24b8808e9 --- /dev/null +++ b/Engine/source/platformMac/macCocoaPlatform.mm @@ -0,0 +1,239 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#import +#import +#include +#include "platform/platform.h" +#include "console/console.h" +#include "core/stringTable.h" +#include "platform/platformInput.h" +#include "platform/threads/thread.h" + +#pragma mark ---- Various Directories ---- +//----------------------------------------------------------------------------- +const char* Platform::getUserDataDirectory() +{ + // application support directory is most in line with the current usages of this function. + // this may change with later usage + // perhaps the user data directory should be pref-controlled? + NSString *nsDataDir = [@"~/Library/Application Support/" stringByStandardizingPath]; + return StringTable->insert([nsDataDir UTF8String]); +} + +//----------------------------------------------------------------------------- +const char* Platform::getUserHomeDirectory() +{ + return StringTable->insert([[@"~/" stringByStandardizingPath] UTF8String]); +} + +//----------------------------------------------------------------------------- +StringTableEntry osGetTemporaryDirectory() +{ + NSString *tdir = NSTemporaryDirectory(); + const char *path = [tdir UTF8String]; + return StringTable->insert(path); +} + +#pragma mark ---- Administrator ---- +//----------------------------------------------------------------------------- +bool Platform::getUserIsAdministrator() +{ + // if we can write to /Library, we're probably an admin + // HACK: this is not really very good, because people can chmod Library. + return (access("/Library", W_OK) == 0); +} + +#pragma mark ---- Cosmetic ---- +//----------------------------------------------------------------------------- +bool Platform::displaySplashWindow() +{ + return false; +} + +#pragma mark ---- File IO ---- +//----------------------------------------------------------------------------- +bool dPathCopy(const char* source, const char* dest, bool nooverwrite) +{ + NSFileManager *manager = [NSFileManager defaultManager]; + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSString *nsource = [[NSString stringWithUTF8String:source] stringByStandardizingPath]; + NSString *ndest = [[NSString stringWithUTF8String:dest] stringByStandardizingPath]; + NSString *ndestFolder = [ndest stringByDeletingLastPathComponent]; + + if(! [manager fileExistsAtPath:nsource]) + { + Con::errorf("dPathCopy: no file exists at %s",source); + return false; + } + + if( [manager fileExistsAtPath:ndest] ) + { + if(nooverwrite) + { + Con::errorf("dPathCopy: file already exists at %s",dest); + return false; + } + Con::warnf("Deleting files at path: %s", dest); + bool deleted = [manager removeFileAtPath:ndest handler:nil]; + if(!deleted) + { + Con::errorf("Copy failed! Could not delete files at path: %s", dest); + return false; + } + } + + if([manager fileExistsAtPath:ndestFolder] == NO) + { + ndestFolder = [ndestFolder stringByAppendingString:@"/"]; // createpath requires a trailing slash + Platform::createPath([ndestFolder UTF8String]); + } + + bool ret = [manager copyPath:nsource toPath:ndest handler:nil]; + + [pool release]; + return ret; + +} + +//----------------------------------------------------------------------------- +bool dFileRename(const char *source, const char *dest) +{ + if(source == NULL || dest == NULL) + return false; + + NSFileManager *manager = [NSFileManager defaultManager]; + + NSString *nsource = [manager stringWithFileSystemRepresentation:source length:dStrlen(source)]; + NSString *ndest = [manager stringWithFileSystemRepresentation:dest length:dStrlen(dest)]; + + if(! [manager fileExistsAtPath:nsource]) + { + Con::errorf("dFileRename: no file exists at %s",source); + return false; + } + + if( [manager fileExistsAtPath:ndest] ) + { + Con::warnf("dFileRename: Deleting files at path: %s", dest); + } + + bool ret = [manager movePath:nsource toPath:ndest handler:nil]; + + return ret; +} + +#pragma mark - +#pragma mark ---- ShellExecute ---- +class ExecuteThread : public Thread +{ + const char* zargs; + const char* directory; + const char* executable; +public: + ExecuteThread(const char *_executable, const char *_args /* = NULL */, const char *_directory /* = NULL */) : Thread(0, NULL, false, true) + { + zargs = dStrdup(_args); + directory = dStrdup(_directory); + executable = dStrdup(_executable); + start(); + } + + virtual void run(void* arg); +}; + +static char* _unDoubleQuote(char* arg) +{ + U32 len = dStrlen(arg); + if(!len) + return arg; + + if(arg[0] == '"' && arg[len-1] == '"') + { + arg[len - 1] = '\0'; + return arg + 1; + } + return arg; +} + +void ExecuteThread::run(void* arg) +{ +// 2k should be enough. if it's not, bail. +// char buf[2048]; +// U32 len = dSprintf(buf, sizeof(buf), "%s %s -workingDir %s", executable, args, directory); +// if( len >= sizeof(buf)) +// { +// Con::errorf("shellExecute(): the command was too long, and won't be run."); +// return; +// } +// // calls sh with the string and blocks until the command returns. +// system(buf); + + // FIXME: there is absolutely no error checking in here. + printf("creating nstask\n"); + NSTask *aTask = [[NSTask alloc] init]; + NSMutableArray *array = [NSMutableArray array]; + + // scan the args list, breaking it up, space delimited, backslash escaped. + U32 len = dStrlen(zargs); + char args[len+1]; + dStrncpy(args, zargs, len+1); + char *lastarg = args; + bool escaping = false; + for(int i = 0; i< len; i++) + { + char c = args[i]; + // a backslash escapes the next character + if(escaping) + continue; + if(c == '\\') + escaping = true; + + if(c == ' ') + { + args[i] = '\0'; + if(*lastarg) + [array addObject:[NSString stringWithUTF8String: _unDoubleQuote(lastarg)]]; + lastarg = args + i + 1; + } + } + if(*lastarg) + [array addObject:[NSString stringWithUTF8String: _unDoubleQuote(lastarg)]]; + + [aTask setArguments: array]; + + [aTask setCurrentDirectoryPath:[NSString stringWithUTF8String: this->directory]]; + [aTask setLaunchPath:[NSString stringWithUTF8String:executable]]; + [aTask launch]; + [aTask waitUntilExit]; + U32 ret = [aTask terminationStatus]; + Con::executef("onExecuteDone", Con::getIntArg(ret)); + printf("done nstask\n"); +} + +ConsoleFunction(shellExecute, bool, 2, 4, "(executable, [args], [directory])") +{ + ExecuteThread *et = new ExecuteThread(argv[1], argc > 2 ? argv[2] : NULL, argc > 3 ? argv[3] : NULL); + TORQUE_UNUSED(et); + return true; // Bug: BPNC error: need feedback on whether the command was sucessful +} diff --git a/Engine/source/platformMac/macDLibrary.cpp b/Engine/source/platformMac/macDLibrary.cpp new file mode 100644 index 000000000..1f2839b38 --- /dev/null +++ b/Engine/source/platformMac/macDLibrary.cpp @@ -0,0 +1,78 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/types.h" +#include "platform/platformDlibrary.h" +#include + +class MacDLibrary : public DLibrary +{ + void* _handle; +public: + MacDLibrary(); + ~MacDLibrary(); + bool open(const char* file); + void close(); + void* bind(const char* name); +}; + +MacDLibrary::MacDLibrary() +{ + _handle = NULL; +} + +MacDLibrary::~MacDLibrary() +{ + close(); +} + +bool MacDLibrary::open(const char* file) +{ + Platform::getExecutablePath(); + _handle = dlopen(file, RTLD_LAZY | RTLD_LOCAL); + return _handle != NULL; +} + +void* MacDLibrary::bind(const char* name) +{ + return _handle ? dlsym(_handle, name) : NULL; +} + +void MacDLibrary::close() +{ + if(_handle) + dlclose(_handle); + + _handle = NULL; +} + +DLibraryRef OsLoadLibrary(const char* file) +{ + MacDLibrary* library = new MacDLibrary(); + if(!library->open(file)) + { + delete library; + return NULL; + } + + return library; +} diff --git a/Engine/source/platformMac/macGLUtils.h b/Engine/source/platformMac/macGLUtils.h new file mode 100644 index 000000000..50a065462 --- /dev/null +++ b/Engine/source/platformMac/macGLUtils.h @@ -0,0 +1,78 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MACGLUTILS_H_ +#define _MACGLUTILS_H_ + +static Vector _beginPixelFormatAttributesForDisplay(CGDirectDisplayID display) +{ + Vector attributes; + attributes.reserve(16); // Most attribute lists won't exceed this + + attributes.push_back(NSOpenGLPFAScreenMask); + attributes.push_back((NSOpenGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(display)); + attributes.push_back(NSOpenGLPFANoRecovery); + attributes.push_back(NSOpenGLPFADoubleBuffer); + attributes.push_back(NSOpenGLPFAAccelerated); + attributes.push_back(NSOpenGLPFAAuxBuffers); + attributes.push_back((NSOpenGLPixelFormatAttribute)1); + return attributes; +} + +static void _addColorAlphaDepthStencilAttributes(Vector& attributes, U32 color, U32 alpha, U32 depth, U32 stencil) +{ + attributes.push_back(NSOpenGLPFAColorSize); attributes.push_back((NSOpenGLPixelFormatAttribute)color); + attributes.push_back(NSOpenGLPFAAlphaSize); attributes.push_back((NSOpenGLPixelFormatAttribute)alpha); + attributes.push_back(NSOpenGLPFADepthSize); attributes.push_back((NSOpenGLPixelFormatAttribute)depth); + attributes.push_back(NSOpenGLPFAStencilSize); attributes.push_back((NSOpenGLPixelFormatAttribute)stencil); +} + +static void _addFullscreenAttributes(Vector& attributes) +{ + attributes.push_back(NSOpenGLPFAFullScreen); +} + +static void _endAttributeList(Vector& attributes) +{ + attributes.push_back((NSOpenGLPixelFormatAttribute)0); +} + +static Vector _createStandardPixelFormatAttributesForDisplay(CGDirectDisplayID display) +{ + Vector attributes = _beginPixelFormatAttributesForDisplay(display); + _addColorAlphaDepthStencilAttributes(attributes, 24, 8, 24, 8); + _endAttributeList(attributes); + + return attributes; +} + +/// returns an opengl pixel format suitable for creating shared opengl contexts. +static NSOpenGLPixelFormat* _createStandardPixelFormat() +{ + Vector attributes = _createStandardPixelFormatAttributesForDisplay(kCGDirectMainDisplay); + + NSOpenGLPixelFormat* fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes.address()]; + + return fmt; +} + +#endif \ No newline at end of file diff --git a/Engine/source/platformMac/macMain.mm b/Engine/source/platformMac/macMain.mm new file mode 100644 index 000000000..e8fe66fde --- /dev/null +++ b/Engine/source/platformMac/macMain.mm @@ -0,0 +1,194 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include +#include "app/mainLoop.h" +#include "platform/platformInput.h" +#include +#include +#include "console/console.h" +#include "platform/threads/thread.h" + +// TODO: let the mainLoop's sleep time happen via rescheduling the timer every run-through. +extern S32 sgTimeManagerProcessInterval; + +@interface MainLoopTimerHandler : NSObject +{ + U32 argc; + const char** argv; + NSTimeInterval interval; +} + +(id)startTimerWithintervalMs:(U32)intervalMs argc:(U32)_argc argv:(const char**)_argv; + -(void)firstFire:(NSTimer*)theTimer; + -(void)fireTimer:(NSTimer*)theTimer; +@end +@implementation MainLoopTimerHandler + -(void)firstFire:(NSTimer*)theTimer + { + StandardMainLoop::init(); + StandardMainLoop::handleCommandLine(argc, argv); + [NSTimer scheduledTimerWithTimeInterval:interval target:self selector:@selector(fireTimer:) userInfo:nil repeats:YES]; + } + -(void)fireTimer:(NSTimer*)theTimer + { + if(!StandardMainLoop::doMainLoop()) + { + StandardMainLoop::shutdown(); + [theTimer invalidate]; + [NSApp setDelegate:nil]; + [NSApp terminate:self]; + } +// if(!mainLoop || !mainLoop->mainLoop()) +// { +// // stop the timer from firing again +// if(mainLoop) +// mainLoop->shutdown(); +// +// [theTimer invalidate]; +// [NSApp setDelegate:nil]; +// [NSApp terminate:self]; +// } + } + +(id)startTimerWithintervalMs:(U32)intervalMs argc:(U32)_argc argv:(const char**)_argv + { + MainLoopTimerHandler* handler = [[[MainLoopTimerHandler alloc] init] autorelease]; + handler->argc = _argc; + handler->argv = _argv; + handler->interval = intervalMs / 1000.0; // interval in milliseconds + [NSTimer scheduledTimerWithTimeInterval:handler->interval target:handler selector:@selector(firstFire:) userInfo:nil repeats:NO]; + return handler; + } +@end + +#pragma mark - + +#ifndef TORQUE_SHARED +//----------------------------------------------------------------------------- +// main() - the real one - this is the actual program entry point. +//----------------------------------------------------------------------------- +S32 main(S32 argc, const char **argv) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + // get command line and text file args, filter them + + // now, we prepare to hand off execution to torque & macosx. + U32 appReturn = 0; + printf("installing torque main loop timer\n"); + [MainLoopTimerHandler startTimerWithintervalMs:1 argc:argc argv:argv]; + printf("starting NSApplicationMain\n"); + appReturn = NSApplicationMain(argc, argv); + printf("NSApplicationMain exited\n"); + + // shut down the engine + + [pool release]; + + return appReturn; +} + +#endif + +static NSApplication *app = NULL; +static NSAutoreleasePool* pool = NULL; + +void torque_mac_engineinit(S32 argc, const char **argv) +{ + + if (!Platform::getWebDeployment()) + { + pool = [[NSAutoreleasePool alloc] init]; + app = [NSApplication sharedApplication]; + } + +} + +void torque_mac_enginetick() +{ + + if (!Platform::getWebDeployment()) + { + + NSEvent *e = [app nextEventMatchingMask: NSAnyEventMask + untilDate: [NSDate distantPast] + inMode: NSDefaultRunLoopMode + dequeue: YES]; + if (e) + [app sendEvent: e]; + + } +} + +void torque_mac_engineshutdown() +{ + if (!Platform::getWebDeployment()) + { + [pool release]; + } +} + + +extern "C" { + +//----------------------------------------------------------------------------- +// torque_macmain() - entry point for application using bundle +//----------------------------------------------------------------------------- +S32 torque_macmain(S32 argc, const char **argv) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + // get command line and text file args, filter them + + // now, we prepare to hand off execution to torque & macosx. + U32 appReturn = 0; + printf("installing torque main loop timer\n"); + [MainLoopTimerHandler startTimerWithintervalMs:1 argc:argc argv:argv]; + printf("starting NSApplicationMain\n"); + appReturn = NSApplicationMain(argc, argv); + printf("NSApplicationMain exited\n"); + + // shut down the engine + + [pool release]; + + return appReturn; +} + +} // extern "C" + + + +#pragma mark ---- Init funcs ---- +//------------------------------------------------------------------------------ +void Platform::init() +{ + // Set the platform variable for the scripts + Con::setVariable( "$platform", "macos" ); + + Input::init(); +} + +//------------------------------------------------------------------------------ +void Platform::shutdown() +{ + Input::destroy(); +} diff --git a/Engine/source/platformMac/macMsgBox.mm b/Engine/source/platformMac/macMsgBox.mm new file mode 100644 index 000000000..4040cc120 --- /dev/null +++ b/Engine/source/platformMac/macMsgBox.mm @@ -0,0 +1,180 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#import +#include "platform/nativeDialogs/msgBox.h" +#include "console/console.h" + +void Platform::AlertOK(const char *windowTitle, const char *message) +{ + Platform::messageBox(windowTitle, message, MBOk, MIInformation); +} + +//-------------------------------------- +bool Platform::AlertOKCancel(const char *windowTitle, const char *message) +{ + return ( Platform::messageBox(windowTitle, message, MBOkCancel, MIInformation) == MROk ); +} + +//-------------------------------------- +bool Platform::AlertRetry(const char *windowTitle, const char *message) +{ + return ( Platform::messageBox(windowTitle, message, MBRetryCancel, MIInformation) == MRRetry ); +} + +namespace MsgBoxMac +{ + struct _NSStringMap + { + S32 num; + NSString* ok; + NSString* cancel; + NSString* third; + }; + + static _NSStringMap sgButtonTextMap[] = + { + { MBOk, @"OK", nil, nil }, + { MBOkCancel, @"OK", @"Cancel", nil }, + { MBRetryCancel, @"Retry", @"Cancel", nil }, + { MBSaveDontSave, @"Yes", @"No", nil }, + { MBSaveDontSaveCancel, @"Yes", @"No", @"Cancel" }, + { -1, nil, nil, nil } + }; + + struct _NSAlertResultMap + { + S32 num; + S32 ok; + S32 cancel; + S32 third; + }; + + static _NSAlertResultMap sgAlertResultMap[] = + { + { MBOk, MROk, 0, 0 }, + { MBOkCancel, MROk, MRCancel, 0 }, + { MBRetryCancel, MRRetry, MRCancel, 0 }, + { MBSaveDontSave, MROk, MRDontSave, 0 }, + { MBSaveDontSaveCancel, MROk, MRDontSave, MRCancel }, + { -1, nil, nil, nil } + }; +} // end MsgBoxMac namespace + +//----------------------------------------------------------------------------- +S32 Platform::messageBox(const UTF8 *title, const UTF8 *message, MBButtons buttons, MBIcons icon) +{ +// TODO: put this on the main thread + + // determine the button text + NSString *okBtn = nil; + NSString *cancelBtn = nil; + NSString *thirdBtn = nil; + U32 i; + for(i = 0; MsgBoxMac::sgButtonTextMap[i].num != -1; i++) + { + if(MsgBoxMac::sgButtonTextMap[i].num != buttons) + continue; + + okBtn = MsgBoxMac::sgButtonTextMap[i].ok; + cancelBtn = MsgBoxMac::sgButtonTextMap[i].cancel; + thirdBtn = MsgBoxMac::sgButtonTextMap[i].third; + break; + } + if(MsgBoxMac::sgButtonTextMap[i].num == -1) + Con::errorf("Unknown message box button set requested. Mac Platform::messageBox() probably needs to be updated."); + + // convert title and message to NSStrings + NSString *nsTitle = [NSString stringWithUTF8String:title]; + NSString *nsMessage = [NSString stringWithUTF8String:message]; + // TODO: ensure that the cursor is the expected shape + // show the alert + S32 result = -2; + + NSAlert *alert = [NSAlert alertWithMessageText:nsTitle + defaultButton:okBtn + alternateButton:thirdBtn + otherButton:cancelBtn + informativeTextWithFormat:nsMessage]; + + switch(icon) + { + // TODO: + // Currently, NSAlert only provides two alert icon options. + // NSWarningAlertStyle and NSInformationalAlertStyle are identical and + // display the application icon, while NSCriticalAlertStyle displays + // a shrunken app icon badge on a yellow-triangle-with-a-bang icon. + // If custom icons were created, they could be used here with the + // message [alert setIcon:foo] + case MIWarning: // MIWarning = 0 + + case MIQuestion: // MIquestion = 3 + [alert setAlertStyle:NSWarningAlertStyle]; + break; + + case MIInformation: // MIInformation = 1 + [alert setAlertStyle:NSInformationalAlertStyle]; + break; + + case MIStop: // MIStop = 3 + [alert setAlertStyle:NSCriticalAlertStyle]; + break; + + default: + Con::errorf("Unknown message box icon requested. Mac Platform::messageBox() probably needs to be updated."); + } + + id appDelegate = [NSApp delegate]; + [NSApp setDelegate: nil]; + + U32 cursorDepth = 0; + + while(!CGCursorIsVisible()) + { + CGDisplayShowCursor(kCGDirectMainDisplay); + cursorDepth++; + } + + CGAssociateMouseAndMouseCursorPosition(true); + result = [alert runModal]; + + [NSApp setDelegate: appDelegate]; + + S32 ret = 0; + for(U32 i = 0; MsgBoxMac::sgAlertResultMap[i].num != -1; i++) + { + if(MsgBoxMac::sgAlertResultMap[i].num != buttons) + continue; + + switch(result) + { + case NSAlertDefaultReturn: + ret = MsgBoxMac::sgAlertResultMap[i].ok; break; + case NSAlertOtherReturn: + ret = MsgBoxMac::sgAlertResultMap[i].cancel; break; + case NSAlertAlternateReturn: + ret = MsgBoxMac::sgAlertResultMap[i].third; break; + } + } + + return ret; +} \ No newline at end of file diff --git a/Engine/source/platformMac/menus/mainMenu.nib/classes.nib b/Engine/source/platformMac/menus/mainMenu.nib/classes.nib new file mode 100644 index 000000000..bf6aa25e8 --- /dev/null +++ b/Engine/source/platformMac/menus/mainMenu.nib/classes.nib @@ -0,0 +1,30 @@ + + + + + IBClasses + + + ACTIONS + + toggleAutomaticLinkDetection + id + toggleAutomaticQuoteSubstitution + id + toggleGrammarChecking + id + toggleSmartInsertDelete + id + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + IBVersion + 1 + + diff --git a/Engine/source/platformMac/menus/mainMenu.nib/info.nib b/Engine/source/platformMac/menus/mainMenu.nib/info.nib new file mode 100644 index 000000000..439f91214 --- /dev/null +++ b/Engine/source/platformMac/menus/mainMenu.nib/info.nib @@ -0,0 +1,20 @@ + + + + + IBFramework Version + 629 + IBLastKnownRelativeProjectPath + ../../../../../GameExamples/T3D/buildFiles/Xcode/T3D.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 29 + + IBSystem Version + 9F33 + targetFramework + IBCocoaFramework + + diff --git a/Engine/source/platformMac/menus/mainMenu.nib/keyedobjects.nib b/Engine/source/platformMac/menus/mainMenu.nib/keyedobjects.nib new file mode 100644 index 000000000..01a177f25 Binary files /dev/null and b/Engine/source/platformMac/menus/mainMenu.nib/keyedobjects.nib differ diff --git a/Engine/source/platformMac/menus/menuBarMac.cpp b/Engine/source/platformMac/menus/menuBarMac.cpp new file mode 100644 index 000000000..f5aebad69 --- /dev/null +++ b/Engine/source/platformMac/menus/menuBarMac.cpp @@ -0,0 +1,303 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformMac/platformMacCarb.h" +#include "platform/menus/menuBar.h" +#include "platform/menus/popupMenu.h" +#include "gui/core/guiCanvas.h" +#include "windowManager/platformWindowMgr.h" +#include "windowManager/platformWindow.h" + + +class PlatformMenuBarData +{ +public: + PlatformMenuBarData() : + mMenuEventHandlerRef(NULL), + mCommandEventHandlerRef(NULL), + mMenuOpenCount( 0 ), + mLastCloseTime( 0 ) + {} + + EventHandlerRef mMenuEventHandlerRef; + EventHandlerRef mCommandEventHandlerRef; + + /// More evil hacking for OSX. There seems to be no way to disable menu shortcuts and + /// they are automatically routed within that Cocoa thing outside of our control. Also, + /// there's no way of telling what triggered a command event and thus no way of knowing + /// whether it was a keyboard shortcut. Sigh. + /// + /// So what we do here is monitor the sequence of events leading to a command event. We + /// capture the time the last open menu was closed and then, when we receive a command + /// event (which are dished out after the menus are closed) and keyboard accelerators are + /// disabled, we check whether we are a certain very short time away in the event stream + /// from the menu close event. If so, we figure the event came from clicking in a menu. + /// + /// Utterly evil and dirty but seems to do the trick. + U32 mMenuOpenCount; + EventTime mLastCloseTime; +}; + +//----------------------------------------------------------------------------- + +#pragma mark - +#pragma mark ---- menu event handler ---- +static OSStatus _OnMenuEvent(EventHandlerCallRef nextHandler, EventRef theEvent, void *userData) +{ + PlatformMenuBarData* mbData = ( PlatformMenuBarData* ) userData; + MenuRef menu; + + GetEventParameter(theEvent, kEventParamDirectObject, typeMenuRef, NULL, sizeof(MenuRef), NULL, &menu); + + // Count open/close for the sake of hotkey disabling. + + UInt32 kind = GetEventKind( theEvent ); + if( kind == kEventMenuOpening ) + mbData->mMenuOpenCount ++; + else + { + AssertWarn( mbData->mMenuOpenCount > 0, "Unbalanced menu open/close events in _OnMenuEvent" ); + if( mbData->mMenuOpenCount ) + mbData->mMenuOpenCount --; + + // Initial menu closed. Capture time. + + if( !mbData->mMenuOpenCount ) + mbData->mLastCloseTime = GetEventTime( theEvent ); + } + + OSStatus err = eventNotHandledErr; + PopupMenu *torqueMenu; + if( CountMenuItems( menu ) > 0 ) + { + // I don't know of a way to get the Torque PopupMenu object from a Carbon MenuRef + // other than going through its first menu item + err = GetMenuItemProperty(menu, 1, 'GG2d', 'ownr', sizeof(PopupMenu*), NULL, &torqueMenu); + if( err == noErr && torqueMenu != NULL ) + { + torqueMenu->onMenuSelect(); + } + } + + return err; +} + +//----------------------------------------------------------------------------- + +#pragma mark - +#pragma mark ---- menu command event handler ---- +static bool MacCarbHandleMenuCommand( void* hiCommand, PlatformMenuBarData* mbData ) +{ + HICommand *cmd = (HICommand*)hiCommand; + + if(cmd->commandID != kHICommandTorque) + return false; + + MenuRef menu = cmd->menu.menuRef; + MenuItemIndex item = cmd->menu.menuItemIndex; + + // Run the command handler. + + PopupMenu* torqueMenu; + OSStatus err = GetMenuItemProperty(menu, item, 'GG2d', 'ownr', sizeof(PopupMenu*), NULL, &torqueMenu); + AssertFatal(err == noErr, "Could not resolve the PopupMenu stored on a native menu item"); + + UInt32 command; + err = GetMenuItemRefCon(menu, item, &command); + AssertFatal(err == noErr, "Could not find the tag of a native menu item"); + + if(!torqueMenu->canHandleID(command)) + Con::errorf("menu claims it cannot handle that id. how odd."); + + // un-highlight currently selected menu + HiliteMenu( 0 ); + + return torqueMenu->handleSelect(command,NULL); +} + +//----------------------------------------------------------------------------- + +#pragma mark - +#pragma mark ---- Command Events ---- + +static OSStatus _OnCommandEvent(EventHandlerCallRef nextHandler, EventRef theEvent, void* userData) +{ + PlatformMenuBarData* mbData = ( PlatformMenuBarData* ) userData; + + HICommand commandStruct; + + OSStatus result = eventNotHandledErr; + + GetEventParameter(theEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &commandStruct); + + // pass menu command events to a more specific handler. + if(commandStruct.attributes & kHICommandFromMenu) + { + bool handleEvent = true; + + // Do menu-close check hack. + + PlatformWindow* window = PlatformWindowManager::get()->getFocusedWindow(); + if( !window || !window->getAcceleratorsEnabled() ) + { + F32 deltaTime = mFabs( GetEventTime( theEvent ) - mbData->mLastCloseTime ); + if( deltaTime > 0.1f ) + handleEvent = false; + } + + if( handleEvent && MacCarbHandleMenuCommand(&commandStruct, mbData) ) + result = noErr; + } + + return result; +} + + + +//----------------------------------------------------------------------------- +// MenuBar Methods +//----------------------------------------------------------------------------- + +void MenuBar::createPlatformPopupMenuData() +{ + + mData = new PlatformMenuBarData; + +} + +void MenuBar::deletePlatformPopupMenuData() +{ + + SAFE_DELETE(mData); +} + +//----------------------------------------------------------------------------- + +void MenuBar::attachToCanvas(GuiCanvas *owner, S32 pos) +{ + if(owner == NULL || isAttachedToCanvas()) + return; + + mCanvas = owner; + + + // Add the items + for(S32 i = 0;i < size();++i) + { + PopupMenu *mnu = dynamic_cast(at(i)); + if(mnu == NULL) + { + Con::warnf("MenuBar::attachToMenuBar - Non-PopupMenu object in set"); + continue; + } + + if(mnu->isAttachedToMenuBar()) + mnu->removeFromMenuBar(); + + mnu->attachToMenuBar(owner, pos + i, mnu->getBarTitle()); + } + + // register as listener for menu opening events + static EventTypeSpec menuEventTypes[ 2 ]; + + menuEventTypes[ 0 ].eventClass = kEventClassMenu; + menuEventTypes[ 0 ].eventKind = kEventMenuOpening; + menuEventTypes[ 1 ].eventClass = kEventClassMenu; + menuEventTypes[ 1 ].eventKind = kEventMenuClosed; + + EventHandlerUPP menuEventHandlerUPP; + menuEventHandlerUPP = NewEventHandlerUPP(_OnMenuEvent); + InstallEventHandler(GetApplicationEventTarget(), menuEventHandlerUPP, 2, menuEventTypes, mData, &mData->mMenuEventHandlerRef); + + // register as listener for process command events + static EventTypeSpec comEventTypes[1]; + comEventTypes[0].eventClass = kEventClassCommand; + comEventTypes[0].eventKind = kEventCommandProcess; + + EventHandlerUPP commandEventHandlerUPP; + commandEventHandlerUPP = NewEventHandlerUPP(_OnCommandEvent); + InstallEventHandler(GetApplicationEventTarget(), commandEventHandlerUPP, 1, comEventTypes, mData, &mData->mCommandEventHandlerRef); +} + +//----------------------------------------------------------------------------- + +void MenuBar::removeFromCanvas() +{ + if(mCanvas == NULL || ! isAttachedToCanvas()) + return; + + if(mData->mCommandEventHandlerRef != NULL) + RemoveEventHandler( mData->mCommandEventHandlerRef ); + mData->mCommandEventHandlerRef = NULL; + + if(mData->mMenuEventHandlerRef != NULL) + RemoveEventHandler( mData->mMenuEventHandlerRef ); + mData->mMenuEventHandlerRef = NULL; + + // Add the items + for(S32 i = 0;i < size();++i) + { + PopupMenu *mnu = dynamic_cast(at(i)); + if(mnu == NULL) + { + Con::warnf("MenuBar::removeFromMenuBar - Non-PopupMenu object in set"); + continue; + } + + mnu->removeFromMenuBar(); + } + + mCanvas = NULL; +} + +//----------------------------------------------------------------------------- + +void MenuBar::updateMenuBar(PopupMenu* menu) +{ + if(! isAttachedToCanvas()) + return; + + menu->removeFromMenuBar(); + SimSet::iterator itr = find(begin(), end(), menu); + if(itr == end()) + return; + + // Get the item currently at the position we want to add to + S32 pos = itr - begin(); + S16 posID = 0; + + PopupMenu *nextMenu = NULL; + for(S32 i = pos + 1; i < size(); i++) + { + PopupMenu *testMenu = dynamic_cast(at(i)); + if (testMenu && testMenu->isAttachedToMenuBar()) + { + nextMenu = testMenu; + break; + } + } + + if(nextMenu) + posID = GetMenuID(nextMenu->mData->mMenu); + + menu->attachToMenuBar(mCanvas, posID, menu->mBarTitle); +} \ No newline at end of file diff --git a/Engine/source/platformMac/menus/popupMenu.cpp b/Engine/source/platformMac/menus/popupMenu.cpp new file mode 100644 index 000000000..3fd392176 --- /dev/null +++ b/Engine/source/platformMac/menus/popupMenu.cpp @@ -0,0 +1,439 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformMac/platformMacCarb.h" +#include "platform/menus/popupmenu.h" +#include "core/util/safeDelete.h" +#include "gui/core/guiCanvas.h" + +void PopupMenu::createPlatformPopupMenuData() +{ + mData = new PlatformPopupMenuData; +} + +void PopupMenu::deletePlatformPopupMenuData() +{ + SAFE_DELETE(mData); +} + +void PopupMenu::createPlatformMenu() +{ + OSStatus err = CreateNewMenu( mData->tag, kMenuAttrAutoDisable,&(mData->mMenu)); + CFRetain(mData->mMenu); + AssertFatal(err == noErr, "Could not create Carbon MenuRef"); +} + +static int _getModifierMask(const char* accel) +{ + int ret = 0; + if(dStrstr(accel, "ctrl")) + ret |= kMenuControlModifier; + if(dStrstr(accel, "shift")) + ret |= kMenuShiftModifier; + if(dStrstr(accel, "alt")) + ret |= kMenuOptionModifier; + if(!(dStrstr(accel, "cmd") || dStrstr(accel, "command"))) + ret |= kMenuNoCommandModifier; + + return ret; +} + +static void _assignCommandKeys(const char* accel, MenuRef menu, MenuItemIndex item) +{ + if(!(accel && *accel)) + return; + + // get the modifier keys + String _accel = String::ToLower( accel ); + int mods = _getModifierMask(_accel); + + // accel is space or dash delimted. + // the modifier key is either the last token in accel, or the first char in accel. + const char* key = dStrrchr(_accel, ' '); + if(!key) + key = dStrrchr(_accel, '-'); + if(!key) + key = _accel; + else + key++; + + if(dStrlen(key) <= 1) + { + char k = dToupper( key[0] ); + SetMenuItemCommandKey( menu, item, false, k ); + } + else + { + SInt16 glyph = kMenuNullGlyph; + + //*** A lot of these mappings came from a listing at http://developer.apple.com/releasenotes/Carbon/HIToolboxOlderNotes.html + if(!dStricmp(key, "DELETE")) + glyph = kMenuDeleteRightGlyph; + + else if(!dStricmp(key, "HOME")) + glyph = kMenuNorthwestArrowGlyph; + + else if(!dStricmp(key, "END")) + glyph = kMenuSoutheastArrowGlyph; + + else if(!dStricmp(key, "BACKSPACE")) + glyph = kMenuDeleteLeftGlyph; + + else if(!dStricmp(key, "TAB")) + glyph = kMenuTabRightGlyph; + + else if(!dStricmp(key, "RETURN")) + glyph = kMenuReturnGlyph; + + else if(!dStricmp(key, "ENTER")) + glyph = kMenuEnterGlyph; + + else if(!dStricmp(key, "PG UP")) + glyph = kMenuPageUpGlyph; + + else if(!dStricmp(key, "PG DOWN")) + glyph = kMenuPageDownGlyph; + + else if(!dStricmp(key, "ESC")) + glyph = kMenuEscapeGlyph; + + else if(!dStricmp(key, "LEFT")) + glyph = kMenuLeftArrowGlyph; + + else if(!dStricmp(key, "RIGHT")) + glyph = kMenuRightArrowGlyph; + + else if(!dStricmp(key, "UP")) + glyph = kMenuUpArrowGlyph; + + else if(!dStricmp(key, "DOWN")) + glyph = kMenuDownArrowGlyph; + + else if(!dStricmp(key, "SPACE")) + glyph = kMenuSpaceGlyph; + + else if(!dStricmp(key, "F1")) + glyph = kMenuF1Glyph; + + else if(!dStricmp(key, "F2")) + glyph = kMenuF2Glyph; + + else if(!dStricmp(key, "F3")) + glyph = kMenuF3Glyph; + + else if(!dStricmp(key, "F4")) + glyph = kMenuF4Glyph; + + else if(!dStricmp(key, "F5")) + glyph = kMenuF5Glyph; + + else if(!dStricmp(key, "F6")) + glyph = kMenuF6Glyph; + + else if(!dStricmp(key, "F7")) + glyph = kMenuF7Glyph; + + else if(!dStricmp(key, "F8")) + glyph = kMenuF8Glyph; + + else if(!dStricmp(key, "F9")) + glyph = kMenuF9Glyph; + + else if(!dStricmp(key, "F10")) + glyph = kMenuF10Glyph; + + else if(!dStricmp(key, "F11")) + glyph = kMenuF11Glyph; + + else if(!dStricmp(key, "F12")) + glyph = kMenuF12Glyph; + + else if(!dStricmp(key, "F13")) + glyph = kMenuF13Glyph; + + else if(!dStricmp(key, "F14")) + glyph = kMenuF14Glyph; + + else if(!dStricmp(key, "F15")) + glyph = kMenuF15Glyph; + + SetMenuItemKeyGlyph(menu, item, glyph); + } + + SetMenuItemModifiers(menu, item, mods); +} + + +S32 PopupMenu::insertItem(S32 pos, const char *title, const char* accel) +{ + MenuItemIndex item; + CFStringRef cftitle; + MenuItemAttributes attr = 0; + + bool needRelease = false; + if(title && *title) + { + cftitle = CFStringCreateWithCString(NULL,title,kCFStringEncodingUTF8); + needRelease = true; + } + else + { + cftitle = CFSTR("-"); + attr = kMenuItemAttrSeparator; + } + + InsertMenuItemTextWithCFString(mData->mMenu, cftitle, pos, attr, kHICommandTorque + 1); + if( needRelease ) + CFRelease( cftitle ); + + // ensure that we have the correct index for the new menu item + MenuRef outref; + GetIndMenuItemWithCommandID(mData->mMenu, kHICommandTorque+1, 1, &outref, &item); + SetMenuItemCommandID(mData->mMenu, item, kHICommandTorque); + + // save a ref to the PopupMenu that owns this item. + PopupMenu* thisMenu = this; + SetMenuItemProperty(mData->mMenu, item, 'GG2d', 'ownr', sizeof(PopupMenu*), &thisMenu); + + // construct the accelerator keys + _assignCommandKeys(accel, mData->mMenu, item); + + S32 tag = PlatformPopupMenuData::getTag(); + SetMenuItemRefCon(mData->mMenu, item, tag); + + return tag; +} + +S32 PopupMenu::insertSubMenu(S32 pos, const char *title, PopupMenu *submenu) +{ + for(S32 i = 0;i < mSubmenus->size();i++) + { + if(submenu == (*mSubmenus)[i]) + { + Con::errorf("PopupMenu::insertSubMenu - Attempting to add submenu twice"); + return -1; + } + } + + CFStringRef cftitle = CFStringCreateWithCString(NULL,title,kCFStringEncodingUTF8); + InsertMenuItemTextWithCFString(mData->mMenu, cftitle, pos, 0, kHICommandTorque + 1); + CFRelease( cftitle ); + + // ensure that we have the correct index for the new menu item + MenuRef outref; + MenuItemIndex item; + GetIndMenuItemWithCommandID(mData->mMenu, kHICommandTorque+1, 1, &outref, &item); + SetMenuItemCommandID(mData->mMenu, item, 0); + + S32 tag = PlatformPopupMenuData::getTag(); + SetMenuItemRefCon( mData->mMenu, item, tag); + + // store a pointer to the PopupMenu this item represents. See PopupMenu::removeItem() + SetMenuItemProperty(mData->mMenu, item, 'GG2d', 'subm', sizeof(PopupMenu*), submenu); + + SetMenuItemHierarchicalMenu( mData->mMenu, item, submenu->mData->mMenu); + mSubmenus->addObject(submenu); + + return tag; +} + +void PopupMenu::removeItem(S32 itemPos) +{ + PopupMenu* submenu; + itemPos++; // adjust torque -> mac menu index + + OSStatus err = GetMenuItemProperty(mData->mMenu, itemPos, 'GG2d', 'subm', sizeof(PopupMenu*),NULL,&submenu); + if(err == noErr) + mSubmenus->removeObject(submenu); + + // deleting the item decrements the ref count on the mac submenu. + DeleteMenuItem(mData->mMenu, itemPos); +} + +////////////////////////////////////////////////////////////////////////// + +void PopupMenu::enableItem(S32 pos, bool enable) +{ + pos++; // adjust torque -> mac menu index. + if(enable) + EnableMenuItem(mData->mMenu, pos); + else + DisableMenuItem(mData->mMenu, pos); +} + +void PopupMenu::checkItem(S32 pos, bool checked) +{ + pos++; + CheckMenuItem(mData->mMenu, pos, checked); +} + +void PopupMenu::checkRadioItem(S32 firstPos, S32 lastPos, S32 checkPos) +{ + // uncheck items + for(int i = firstPos; i <= lastPos; i++) + checkItem( i, false); + + // check the selected item + checkItem( checkPos, true); +} + +bool PopupMenu::isItemChecked(S32 pos) +{ + CharParameter mark; + GetItemMark(mData->mMenu, pos, &mark); + return (mark == checkMark); +} + +////////////////////////////////////////////////////////////////////////// + +// this method really isn't necessary for the mac implementation +bool PopupMenu::canHandleID(U32 iD) +{ + for(S32 i = 0;i < mSubmenus->size();i++) + { + PopupMenu *subM = dynamic_cast((*mSubmenus)[i]); + if(subM == NULL) + continue; + + if(subM->canHandleID(iD)) + return true; + } + + UInt32 refcon; + U32 nItems = CountMenuItems(mData->mMenu); + for(int i = 1; i <= nItems; i++) + { + GetMenuItemRefCon(mData->mMenu, i, &refcon); + if(refcon == iD) + return true; + } + + return false; +} + +bool PopupMenu::handleSelect(U32 command, const char *text /* = NULL */) +{ + // [tom, 8/20/2006] Pass off to a sub menu if it's for them + for(S32 i = 0;i < mSubmenus->size();i++) + { + PopupMenu *subM = dynamic_cast((*mSubmenus)[i]); + if(subM == NULL) + continue; + + if(subM->canHandleID(command)) + { + return subM->handleSelect(command, text); + } + } + + // ensure that this menu actually has an item with the specificed command / refcon. + // this is not strictly necessary, we're just doing it here to keep the behavior + // in line with the windows implementation. + UInt32 refcon; + U32 nItems = CountMenuItems(mData->mMenu); + S32 pos = -1; + for(int i = 1; i <= nItems; i++) + { + GetMenuItemRefCon(mData->mMenu, i, &refcon); + if(refcon == command) + pos = i; + } + if(pos == -1) + { + Con::errorf("PopupMenu::handleSelect - Could not find menu item position for ID %d ... this shouldn't happen!", command); + return false; + } + + char textbuf[1024]; + if(!text) + { + CFStringRef cfstr; + CopyMenuItemTextAsCFString(mData->mMenu, pos, &cfstr); + CFStringGetCString(cfstr,textbuf,sizeof(textbuf) - 1,kCFStringEncodingUTF8); + CFRelease( cfstr ); + text = textbuf; + } + + // [tom, 8/20/2006] Wasn't handled by a submenu, pass off to script + return dAtob(Con::executef(this, "onSelectItem", Con::getIntArg(pos - 1), text ? text : "")); +} + +////////////////////////////////////////////////////////////////////////// + +void PopupMenu::showPopup(GuiCanvas* canvas, S32 x /* = -1 */, S32 y /* = -1 */) +{ + if(x < 0 || y < 0) + { + Point2I p = canvas->getCursorPos(); + x = p.x; + y = p.y; + } + + PopUpMenuSelect(mData->mMenu, y, x, 0); +} + +////////////////////////////////////////////////////////////////////////// + +void PopupMenu::attachToMenuBar(GuiCanvas* canvas, S32 pos, const char *title) +{ + CFStringRef cftitle = CFStringCreateWithCString(NULL,title,kCFStringEncodingUTF8); + SetMenuTitleWithCFString(mData->mMenu, cftitle); + CFRelease( cftitle ); + InsertMenu(mData->mMenu, pos); + + onAttachToMenuBar(canvas, pos, title); +} + +void PopupMenu::removeFromMenuBar() +{ + DeleteMenu(mData->tag); + + onRemoveFromMenuBar(mCanvas); +} + +U32 PopupMenu::getItemCount() +{ + return CountMenuItems( mData->mMenu ); +} + +bool PopupMenu::setItem(S32 pos, const char *title, const char *accelerator) +{ + //TODO: update accelerator? + + pos += 1; // Torque to mac index + + CFStringRef cftitle = CFStringCreateWithCString( NULL, title, kCFStringEncodingUTF8 ); + SetMenuItemTextWithCFString( mData->mMenu, pos, cftitle ); + CFRelease( cftitle ); + + return true; +} + +S32 PopupMenu::getPosOnMenuBar() +{ + return -1; +} + +void PopupMenu::attachToMenuBar(GuiCanvas *owner, S32 pos) +{ + +} diff --git a/Engine/source/platformMac/platformMacCarb.h b/Engine/source/platformMac/platformMacCarb.h new file mode 100644 index 000000000..3a26eb249 --- /dev/null +++ b/Engine/source/platformMac/platformMacCarb.h @@ -0,0 +1,175 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PLATFORMMACCARB_H_ +#define _PLATFORMMACCARB_H_ + +/// NOTE: Placing system headers before Torque's platform.h will work around the Torque-Redefines-New problems. +#include +#include +#include "platform/platform.h" +#include "math/mMath.h" + +#include "gfx/gl/ggl/ggl.h" +#define __gl_h_ +#include + +class MacCarbPlatState +{ +public: + GDHandle hDisplay; + CGDirectDisplayID cgDisplay; + + bool captureDisplay; + bool fadeWindows; + + WindowPtr appWindow; + char appWindowTitle[256]; + WindowGroupRef torqueWindowGroup; + + bool quit; + + AGLContext ctx; + bool ctxNeedsUpdate; + + S32 desktopBitsPixel; + S32 desktopWidth; + S32 desktopHeight; + U32 currentTime; + bool isFullScreen; + + U32 osVersion; + + TSMDocumentID tsmDoc; + bool tsmActive; + + U32 firstThreadId; + + void* alertSemaphore; + S32 alertHit; + DialogRef alertDlg; + EventQueueRef mainEventQueue; + + MRandomLCG platRandom; + + bool mouseLocked; + bool backgrounded; + + U32 sleepTicks; + + Point2I windowSize; + + U32 appReturn; + + U32 argc; + char** argv; + + U32 lastTimeTick; + + MacCarbPlatState(); +}; + +/// Global singleton that encapsulates a lot of mac platform state & globals. +extern MacCarbPlatState platState; + +/// @name Misc Mac Plat Functions +/// Functions that are used by multiple files in the mac plat, but too trivial +/// to require their own header file. +/// @{ +/// Fills gGLState with info about this gl renderer's capabilities. +void getGLCapabilities(void); + +/// Creates a new mac window, of a particular size, centered on the screen. +/// If a fullScreen window is requested, then the window is created without +/// decoration, in front of all other normal windows AND BEHIND asian text input methods. +/// This path to a fullScreen window allows asian text input methods to work +/// in full screen mode, because it avoids capturing the display. +WindowPtr MacCarbCreateOpenGLWindow( GDHandle hDevice, U32 width, U32 height, bool fullScreen ); + +/// Asnychronously fade a window into existence, and set menu bar visibility. +/// The fading can be turned off via the preference $pref::mac::fadeWindows. +/// It also sends itself to the main thread if it is called on any other thread. +void MacCarbFadeInWindow( WindowPtr window ); + +/// Asnychronously fade a window out of existence. The window will be destroyed +/// when the fade is complete. +/// The fading can be turned off via the preference $pref::mac::fadeWindows. +/// It also sends itself to the main thread if it is called on any other thread. +void MacCarbFadeAndReleaseWindow( WindowPtr window ); + +/// Translates a Mac keycode to a Torque keycode +U8 TranslateOSKeyCode(U8 vcode); +/// @} + +/// @name Misc Mac Plat constants +/// @{ + +/// earlier versions of OSX don't have these convinience macros, so manually stick them here. +#ifndef IntToFixed +#define IntToFixed(a) ((Fixed)(a) <<16) +#define FixedToInt(a) ((short)(((Fixed)(a) + fixed1/2) >> 16)) +#endif + +/// window level constants +const U32 kTAlertWindowLevel = CGShieldingWindowLevel() - 1; +const U32 kTUtilityWindowLevel = CGShieldingWindowLevel() - 2; +const U32 kTFullscreenWindowLevel = CGShieldingWindowLevel() - 3; + +/// mouse wheel sensitivity factor +const S32 kTMouseWheelMagnificationFactor = 25; + +/// Torque Menu Command ID +const U32 kHICommandTorque = 'TORQ'; + +/// @} + +//----------------------------------------------------------------------------- +// Platform Menu Data +//----------------------------------------------------------------------------- +class PlatformPopupMenuData + { + public: + // We assign each new menu item an arbitrary integer tag. + static S32 getTag() + { + static S32 lastTag = 'TORQ'; + return ++lastTag; + } + + MenuRef mMenu; + S32 tag; + PlatformPopupMenuData() + { + mMenu = NULL; + tag = getTag(); + } + + ~PlatformPopupMenuData() + { + if(mMenu) + CFRelease(mMenu); + mMenu = NULL; + } +}; + +#endif //_PLATFORMMACCARB_H_ + diff --git a/Engine/source/platformMac/torqueDemo.icns b/Engine/source/platformMac/torqueDemo.icns new file mode 100644 index 000000000..6695aaf34 Binary files /dev/null and b/Engine/source/platformMac/torqueDemo.icns differ diff --git a/Engine/source/platformPOSIX/posixVolume.cpp b/Engine/source/platformPOSIX/posixVolume.cpp new file mode 100644 index 000000000..604611543 --- /dev/null +++ b/Engine/source/platformPOSIX/posixVolume.cpp @@ -0,0 +1,598 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#include +#include +#include + +#include "core/crc.h" +#include "core/frameAllocator.h" + +#include "core/util/str.h" +#include "core/strings/stringFunctions.h" + +#include "platform/platformVolume.h" +#include "platformPOSIX/posixVolume.h" + +#ifndef PATH_MAX +#include +#endif + +#ifndef NGROUPS_UMAX + #define NGROUPS_UMAX 32 +#endif + + +//#define DEBUG_SPEW + + +namespace Torque +{ +namespace Posix +{ + +//----------------------------------------------------------------------------- + +static String buildFileName(const String& prefix,const Path& path) +{ + // Need to join the path (minus the root) with our + // internal path name. + String file = prefix; + file = Path::Join(file,'/',path.getPath()); + file = Path::Join(file,'/',path.getFileName()); + file = Path::Join(file,'.',path.getExtension()); + return file; +} + +/* +static bool isFile(const String& file) +{ + struct stat info; + if (stat(file.c_str(),&info) == 0) + return S_ISREG(info.st_mode); + return false; +} + +static bool isDirectory(const String& file) +{ + struct stat info; + if (stat(file.c_str(),&info) == 0) + return S_ISDIR(info.st_mode); + return false; +} +*/ + +//----------------------------------------------------------------------------- + +static uid_t _Uid; // Current user id +static int _GroupCount; // Number of groups in the table +static gid_t _Groups[NGROUPS_UMAX+1]; // Table of all the user groups + +static void copyStatAttributes(const struct stat& info, FileNode::Attributes* attr) +{ + // We need to user and group id's in order to determin file + // read-only access permission. This information is only retrieved + // once per execution. + if (!_Uid) + { + _Uid = getuid(); + _GroupCount = getgroups(NGROUPS_UMAX,_Groups); + _Groups[_GroupCount++] = getegid(); + } + + // Fill in the return struct. The read-only flag is determined + // by comparing file user and group ownership. + attr->flags = 0; + if (S_ISDIR(info.st_mode)) + attr->flags |= FileNode::Directory; + + if (S_ISREG(info.st_mode)) + attr->flags |= FileNode::File; + + if (info.st_uid == _Uid) + { + if (!(info.st_mode & S_IWUSR)) + attr->flags |= FileNode::ReadOnly; + } + else + { + S32 i = 0; + for (; i < _GroupCount; i++) + { + if (_Groups[i] == info.st_gid) + break; + } + if (i != _GroupCount) + { + if (!(info.st_mode & S_IWGRP)) + attr->flags |= FileNode::ReadOnly; + } + else + { + if (!(info.st_mode & S_IWOTH)) + attr->flags |= FileNode::ReadOnly; + } + } + + attr->size = info.st_size; + attr->mtime = UnixTimeToTime(info.st_mtime); + attr->atime = UnixTimeToTime(info.st_atime); +} + + +//----------------------------------------------------------------------------- + +PosixFileSystem::PosixFileSystem(String volume) +{ + _volume = volume; +} + +PosixFileSystem::~PosixFileSystem() +{ +} + +FileNodeRef PosixFileSystem::resolve(const Path& path) +{ + String file = buildFileName(_volume,path); + struct stat info; + if (stat(file.c_str(),&info) == 0) + { + // Construct the appropriate object + if (S_ISREG(info.st_mode)) + return new PosixFile(path,file); + + if (S_ISDIR(info.st_mode)) + return new PosixDirectory(path,file); + } + + return 0; +} + +FileNodeRef PosixFileSystem::create(const Path& path, FileNode::Mode mode) +{ + // The file will be created on disk when it's opened. + if (mode & FileNode::File) + return new PosixFile(path,buildFileName(_volume,path)); + + // Default permissions are read/write/search/executate by everyone, + // though this will be modified by the current umask + if (mode & FileNode::Directory) + { + String file = buildFileName(_volume,path); + + if (mkdir(file.c_str(),S_IRWXU | S_IRWXG | S_IRWXO)) + return new PosixDirectory(path,file); + } + + return 0; +} + +bool PosixFileSystem::remove(const Path& path) +{ + // Should probably check for outstanding files or directory objects. + String file = buildFileName(_volume,path); + + struct stat info; + int error = stat(file.c_str(),&info); + if (error < 0) + return false; + + if (S_ISDIR(info.st_mode)) + return !rmdir(file); + + return !unlink(file); +} + +bool PosixFileSystem::rename(const Path& from,const Path& to) +{ + String fa = buildFileName(_volume,from); + String fb = buildFileName(_volume,to); + + if (!rename(fa.c_str(),fb.c_str())) + return true; + + return false; +} + +Path PosixFileSystem::mapTo(const Path& path) +{ + return buildFileName(_volume,path); +} + + +Path PosixFileSystem::mapFrom(const Path& path) +{ + const String::SizeType volumePathLen = _volume.length(); + + String pathStr = path.getFullPath(); + + if ( _volume.compare( pathStr, volumePathLen, String::NoCase )) + return Path(); + + return pathStr.substr( volumePathLen, pathStr.length() - volumePathLen ); +} + +//----------------------------------------------------------------------------- + +PosixFile::PosixFile(const Path& path,String name) +{ + _path = path; + _name = name; + _status = Closed; + _handle = 0; +} + +PosixFile::~PosixFile() +{ + if (_handle) + close(); +} + +Path PosixFile::getName() const +{ + return _path; +} + +FileNode::Status PosixFile::getStatus() const +{ + return _status; +} + +bool PosixFile::getAttributes(Attributes* attr) +{ + struct stat info; + int error = _handle? fstat(fileno(_handle),&info): stat(_name.c_str(),&info); + + if (error < 0) + { + _updateStatus(); + return false; + } + + copyStatAttributes(info,attr); + attr->name = _path; + + return true; +} + +U32 PosixFile::calculateChecksum() +{ + if (!open( Read )) + return 0; + + U64 fileSize = getSize(); + U32 bufSize = 1024 * 1024 * 4; + FrameTemp< U8 > buf( bufSize ); + U32 crc = CRC::INITIAL_CRC_VALUE; + + while( fileSize > 0 ) + { + U32 bytesRead = getMin( fileSize, bufSize ); + if( read( buf, bytesRead ) != bytesRead ) + { + close(); + return 0; + } + + fileSize -= bytesRead; + crc = CRC::calculateCRC( buf, bytesRead, crc ); + } + + close(); + + return crc; +} + +bool PosixFile::open(AccessMode mode) +{ + close(); + + if (_name.isEmpty()) + { + return _status; + } + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[PosixFile] opening '%s'", _name.c_str() ); + #endif + + const char* fmode = "r"; + switch (mode) + { + case Read: fmode = "r"; break; + case Write: fmode = "w"; break; + case ReadWrite: + { + fmode = "r+"; + // Ensure the file exists. + FILE* temp = fopen( _name.c_str(), "a+" ); + fclose( temp ); + break; + } + case WriteAppend: fmode = "a"; break; + default: break; + } + + if (!(_handle = fopen(_name.c_str(), fmode))) + { + _updateStatus(); + return false; + } + + _status = Open; + return true; +} + +bool PosixFile::close() +{ + if (_handle) + { + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[PosixFile] closing '%s'", _name.c_str() ); + #endif + + fflush(_handle); + fclose(_handle); + _handle = 0; + } + + _status = Closed; + return true; +} + +U32 PosixFile::getPosition() +{ + if (_status == Open || _status == EndOfFile) + return ftell(_handle); + + return 0; +} + +U32 PosixFile::setPosition(U32 delta, SeekMode mode) +{ + if (_status != Open && _status != EndOfFile) + return 0; + + S32 fmode = 0; + switch (mode) + { + case Begin: fmode = SEEK_SET; break; + case Current: fmode = SEEK_CUR; break; + case End: fmode = SEEK_END; break; + default: break; + } + + if (fseek(_handle, delta, fmode)) + { + _status = UnknownError; + return 0; + } + + _status = Open; + + return ftell(_handle); +} + +U32 PosixFile::read(void* dst, U32 size) +{ + if (_status != Open && _status != EndOfFile) + return 0; + + U32 bytesRead = fread(dst, 1, size, _handle); + + if (bytesRead != size) + { + if (feof(_handle)) + _status = EndOfFile; + else + _updateStatus(); + } + + return bytesRead; +} + +U32 PosixFile::write(const void* src, U32 size) +{ + if ((_status != Open && _status != EndOfFile) || !size) + return 0; + + U32 bytesWritten = fwrite(src, 1, size, _handle); + + if (bytesWritten != size) + _updateStatus(); + + return bytesWritten; +} + +void PosixFile::_updateStatus() +{ + switch (errno) + { + case EACCES: _status = AccessDenied; break; + case ENOSPC: _status = FileSystemFull; break; + case ENOTDIR: _status = NoSuchFile; break; + case ENOENT: _status = NoSuchFile; break; + case EISDIR: _status = AccessDenied; break; + case EROFS: _status = AccessDenied; break; + default: _status = UnknownError; break; + } +} + +//----------------------------------------------------------------------------- + +PosixDirectory::PosixDirectory(const Path& path,String name) +{ + _path = path; + _name = name; + _status = Closed; + _handle = 0; +} + +PosixDirectory::~PosixDirectory() +{ + if (_handle) + close(); +} + +Path PosixDirectory::getName() const +{ + return _path; +} + +bool PosixDirectory::open() +{ + if ((_handle = opendir(_name)) == 0) + { + _updateStatus(); + return false; + } + + _status = Open; + return true; +} + +bool PosixDirectory::close() +{ + if (_handle) + { + closedir(_handle); + _handle = NULL; + return true; + } + + return false; +} + +bool PosixDirectory::read(Attributes* entry) +{ + if (_status != Open) + return false; + + struct dirent* de = readdir(_handle); + + if (!de) + { + _status = EndOfFile; + return false; + } + + // Skip "." and ".." entries + if (de->d_name[0] == '.' && (de->d_name[1] == '\0' || + (de->d_name[1] == '.' && de->d_name[2] == '\0'))) + return read(entry); + + // The dirent structure doesn't actually return much beside + // the name, so we must call stat for more info. + struct stat info; + String file = _name + "/" + de->d_name; + + int error = stat(file.c_str(),&info); + + if (error < 0) + { + _updateStatus(); + return false; + } + copyStatAttributes(info,entry); + entry->name = de->d_name; + return true; +} + +U32 PosixDirectory::calculateChecksum() +{ + // Return checksum of current entry + return 0; +} + +bool PosixDirectory::getAttributes(Attributes* attr) +{ + struct stat info; + if (stat(_name.c_str(),&info)) + { + _updateStatus(); + return false; + } + + copyStatAttributes(info,attr); + attr->name = _path; + return true; +} + +FileNode::Status PosixDirectory::getStatus() const +{ + return _status; +} + +void PosixDirectory::_updateStatus() +{ + switch (errno) + { + case EACCES: _status = AccessDenied; break; + case ENOTDIR: _status = NoSuchFile; break; + case ENOENT: _status = NoSuchFile; break; + default: _status = UnknownError; break; + } +} + +} // Namespace POSIX + +} // Namespace Torque + + +//----------------------------------------------------------------------------- + +#ifndef TORQUE_OS_MAC // Mac has its own native FS build on top of the POSIX one. + +Torque::FS::FileSystemRef Platform::FS::createNativeFS( const String &volume ) +{ + return new Posix::PosixFileSystem( volume ); +} + +#endif + +String Platform::FS::getAssetDir() +{ + return Platform::getExecutablePath(); +} + +/// Function invoked by the kernel layer to install OS specific +/// file systems. +bool Platform::FS::InstallFileSystems() +{ + Platform::FS::Mount( "/", Platform::FS::createNativeFS( String() ) ); + + // Setup the current working dir. + char buffer[PATH_MAX]; + if (::getcwd(buffer,sizeof(buffer))) + { + // add trailing '/' if it isn't there + if (buffer[dStrlen(buffer) - 1] != '/') + dStrcat(buffer, "/"); + + Platform::FS::SetCwd(buffer); + } + + // Mount the home directory + if (char* home = getenv("HOME")) + Platform::FS::Mount( "home", Platform::FS::createNativeFS(home) ); + + return true; +} diff --git a/Engine/source/platformPOSIX/posixVolume.h b/Engine/source/platformPOSIX/posixVolume.h new file mode 100644 index 000000000..3f6b48cf0 --- /dev/null +++ b/Engine/source/platformPOSIX/posixVolume.h @@ -0,0 +1,129 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _POSIXVOLUME_H_ +#define _POSIXVOLUME_H_ + +#ifndef _VOLUME_H_ +#include "core/volume.h" +#endif + +#include +#include +#include + +namespace Torque +{ +using namespace FS; + +namespace Posix +{ + +//----------------------------------------------------------------------------- + +class PosixFileSystem: public FileSystem +{ + String _volume; + +public: + PosixFileSystem(String volume); + ~PosixFileSystem(); + + String getTypeStr() const { return "POSIX"; } + + FileNodeRef resolve(const Path& path); + FileNodeRef create(const Path& path,FileNode::Mode); + bool remove(const Path& path); + bool rename(const Path& from,const Path& to); + Path mapTo(const Path& path); + Path mapFrom(const Path& path); +}; + + +//----------------------------------------------------------------------------- +/// Posix stdio file access. +/// This class makes use the fopen, fread and fwrite for buffered io. +class PosixFile: public File +{ + friend class PosixFileSystem; + Path _path; + String _name; + FILE* _handle; + Status _status; + + PosixFile(const Path& path,String name); + bool _updateInfo(); + void _updateStatus(); + +public: + ~PosixFile(); + + Path getName() const; + Status getStatus() const; + bool getAttributes(Attributes*); + + U32 getPosition(); + U32 setPosition(U32,SeekMode); + + bool open(AccessMode); + bool close(); + + U32 read(void* dst, U32 size); + U32 write(const void* src, U32 size); + +private: + U32 calculateChecksum(); +}; + + +//----------------------------------------------------------------------------- + +class PosixDirectory: public Directory +{ + friend class PosixFileSystem; + Path _path; + String _name; + DIR* _handle; + Status _status; + + PosixDirectory(const Path& path,String name); + void _updateStatus(); + +public: + ~PosixDirectory(); + + Path getName() const; + Status getStatus() const; + bool getAttributes(Attributes*); + + bool open(); + bool close(); + bool read(Attributes*); + +private: + U32 calculateChecksum(); +}; + + +} // Namespace +} // Namespace +#endif diff --git a/Engine/source/platformWin32/VFSRes.h b/Engine/source/platformWin32/VFSRes.h new file mode 100644 index 000000000..40e15e74d --- /dev/null +++ b/Engine/source/platformWin32/VFSRes.h @@ -0,0 +1,38 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by zipCat.rc +// +#define IDR_ZIPFILE 500 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 501 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Engine/source/platformWin32/cardProfile.cpp b/Engine/source/platformWin32/cardProfile.cpp new file mode 100644 index 000000000..50f70bbbf --- /dev/null +++ b/Engine/source/platformWin32/cardProfile.cpp @@ -0,0 +1,81 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/strings/stringFunctions.h" +#include "console/console.h" +#include "platformWin32/platformWin32.h" + + +void initDisplayDeviceInfo() +{ + Con::printf( "Reading Display Device information..." ); + + U8 i = 0; + + DISPLAY_DEVICEA ddData; + ddData.cb = sizeof( DISPLAY_DEVICEA ); + + // Search for the primary display adapter, because that is what the rendering + // context will get created on. + while( EnumDisplayDevicesA( NULL, i, &ddData, 0 ) != 0 ) + { + // If we find the primary display adapter, break out + if( ddData.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE ) + break; + + i++; + } + + Con::printf( " Primary Display Device Found:" ); + + // Ok, now we have the primary display device. Parse the device information. + char ven[9]; + char dev[9]; + + ven[8] = dev[8] = '\0'; + + // It may seem a bit silly here to cast, but there are two implimentations in Platform.h + // This usage is the "const" version... + char *pos = dStrstr( ddData.DeviceID, (const char *)"VEN_"); + + dStrncpy( ven, ( pos ) ? pos : "VEN_0000", 8 ); + + Con::printf( " Vendor Id: %s", ven ); + + pos = dStrstr( ddData.DeviceID, (const char *)"DEV_" ); + + dStrncpy( dev, ( pos ) ? pos : "DEV_0000", 8 ); + + Con::printf( " Device Id: %s", dev ); + + // We now have the information, set them to console variables so we can parse + // the file etc in script using getField and so on. + Con::setVariable( "$PCI_VEN", ven ); + Con::setVariable( "$PCI_DEV", dev ); +} + +ConsoleFunction( initDisplayDeviceInfo, void, 1, 1, "()" + "@brief Initializes variables that track device and vendor information/IDs\n\n" + "@ingroup Rendering") +{ + initDisplayDeviceInfo(); +} \ No newline at end of file diff --git a/Engine/source/platformWin32/menus/menuBarWin32.cpp b/Engine/source/platformWin32/menus/menuBarWin32.cpp new file mode 100644 index 000000000..aa69b8d3a --- /dev/null +++ b/Engine/source/platformWin32/menus/menuBarWin32.cpp @@ -0,0 +1,171 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformWin32/platformWin32.h" +#include "platform/menus/menuBar.h" +#include "platform/menus/popupMenu.h" +#include "gui/core/guiCanvas.h" +#include "windowManager/platformWindowMgr.h" +#include "windowManager/win32/win32Window.h" +#include "core/util/safeDelete.h" + +//----------------------------------------------------------------------------- +// Platform Data +//----------------------------------------------------------------------------- + +// class PlatformMenuBarData +// { +// +// }; + +//----------------------------------------------------------------------------- +// MenuBar Methods +//----------------------------------------------------------------------------- + +void MenuBar::createPlatformPopupMenuData() +{ +// mData = new PlatformMenuBarData; + + // [tom, 6/4/2007] Nothing currently needed for win32 + mData = NULL; +} + +void MenuBar::deletePlatformPopupMenuData() +{ +// SAFE_DELETE(mData); +} + +//----------------------------------------------------------------------------- + +void MenuBar::updateMenuBar(PopupMenu *menu /* = NULL */) +{ + if(! isAttachedToCanvas()) + return; + + if(menu == NULL) + { + // [tom, 6/4/2007] Kludgetastic + GuiCanvas *oldCanvas = mCanvas; + S32 pos = -1; + PopupMenu *mnu = dynamic_cast(at(0)); + if(mnu) + pos = mnu->getPosOnMenuBar(); + + removeFromCanvas(); + attachToCanvas(oldCanvas, pos); + + return; + } + + menu->removeFromMenuBar(); + SimSet::iterator itr = find(begin(), end(), menu); + if(itr == end()) + return; + + menu->attachToMenuBar(mCanvas, itr - begin()); + + Win32Window *pWindow = dynamic_cast(mCanvas->getPlatformWindow()); + if(pWindow == NULL) + return; + + HWND hWindow = pWindow->getHWND(); + DrawMenuBar(hWindow); +} + +//----------------------------------------------------------------------------- + +void MenuBar::attachToCanvas(GuiCanvas *owner, S32 pos) +{ + if(owner == NULL || isAttachedToCanvas()) + return; + + // This is set for popup menus in the onAttachToMenuBar() callback + mCanvas = owner; + + Win32Window *pWindow = dynamic_cast(owner->getPlatformWindow()); + if(pWindow == NULL) + return; + + // Setup the native menu bar + HMENU hWindowMenu = pWindow->getMenuHandle(); + if(hWindowMenu == NULL && !Journal::IsPlaying()) + { + hWindowMenu = CreateMenu(); + if(hWindowMenu) + { + pWindow->setMenuHandle( hWindowMenu); + } + } + + // Add the items + for(S32 i = 0;i < size();++i) + { + PopupMenu *mnu = dynamic_cast(at(i)); + if(mnu == NULL) + { + Con::warnf("MenuBar::attachToMenuBar - Non-PopupMenu object in set"); + continue; + } + + if(mnu->isAttachedToMenuBar()) + mnu->removeFromMenuBar(); + + mnu->attachToMenuBar(owner, pos + i); + } + + HWND hWindow = pWindow->getHWND(); + DrawMenuBar(hWindow); + +} + +void MenuBar::removeFromCanvas() +{ + if(mCanvas == NULL || ! isAttachedToCanvas()) + return; + + Win32Window *pWindow = dynamic_cast(mCanvas->getPlatformWindow()); + if(pWindow == NULL) + return; + + // Setup the native menu bar + HMENU hWindowMenu = pWindow->getMenuHandle(); + if(hWindowMenu == NULL) + return; + + // Add the items + for(S32 i = 0;i < size();++i) + { + PopupMenu *mnu = dynamic_cast(at(i)); + if(mnu == NULL) + { + Con::warnf("MenuBar::removeFromMenuBar - Non-PopupMenu object in set"); + continue; + } + + mnu->removeFromMenuBar(); + } + + HWND hWindow = pWindow->getHWND(); + DrawMenuBar(hWindow); + + mCanvas = NULL; +} diff --git a/Engine/source/platformWin32/menus/popupMenuWin32.cpp b/Engine/source/platformWin32/menus/popupMenuWin32.cpp new file mode 100644 index 000000000..f04ae1b58 --- /dev/null +++ b/Engine/source/platformWin32/menus/popupMenuWin32.cpp @@ -0,0 +1,742 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/menus/popupMenu.h" +#include "platformWin32/platformWin32.h" +#include "console/consoleTypes.h" +#include "gui/core/guiCanvas.h" +#include "windowManager/platformWindowMgr.h" +#include "windowManager/win32/win32Window.h" +#include "core/util/safeDelete.h" + +#include "sim/actionMap.h" +#include "platform/platformInput.h" + +////////////////////////////////////////////////////////////////////////// +// Platform Menu Data +////////////////////////////////////////////////////////////////////////// + +struct PlatformPopupMenuData +{ + static U32 mLastPopupMenuID; + static const U32 PopupMenuIDRange; + + HMENU mMenu; + U32 mMenuID; + U32 mLastID; + + Win32Window::AcceleratorList mAccelerators; + Win32Window::AcceleratorList mDisabledAccelerators; + + PlatformPopupMenuData() + { + mMenu = NULL; + mMenuID = mLastPopupMenuID++; + mLastID = 0; + } + + ~PlatformPopupMenuData() + { + if(mMenu) + DestroyMenu(mMenu); + } + + void insertAccelerator(EventDescriptor &desc, U32 id); + void removeAccelerator(U32 id); + void setAccelleratorEnabled(U32 id, bool enabled); +}; + +U32 PlatformPopupMenuData::mLastPopupMenuID = 0; +const U32 PlatformPopupMenuData::PopupMenuIDRange = 100; + +////////////////////////////////////////////////////////////////////////// + +void PlatformPopupMenuData::insertAccelerator(EventDescriptor &desc, U32 id) +{ + if(desc.eventType != SI_KEY) + return; + + Win32Window::AcceleratorList::iterator i; + for(i = mAccelerators.begin();i != mAccelerators.end();++i) + { + if(i->mID == id) + { + // Update existing entry + i->mDescriptor.eventType = desc.eventType; + i->mDescriptor.eventCode = desc.eventCode; + i->mDescriptor.flags = desc.flags; + return; + } + + if(i->mDescriptor.eventType == desc.eventType && i->mDescriptor.eventCode == desc.eventCode && i->mDescriptor.flags == desc.flags) + { + // Already have a matching accelerator, don't add another one + return; + } + } + + Win32Window::Accelerator accel; + accel.mDescriptor = desc; + accel.mID = id; + mAccelerators.push_back(accel); +} + +void PlatformPopupMenuData::removeAccelerator(U32 id) +{ + Win32Window::AcceleratorList::iterator i; + for(i = mAccelerators.begin();i != mAccelerators.end();++i) + { + if(i->mID == id) + { + mAccelerators.erase(i); + return; + } + } +} + +void PlatformPopupMenuData::setAccelleratorEnabled( U32 id, bool enabled ) +{ + Win32Window::AcceleratorList *src = NULL; + Win32Window::AcceleratorList *dst = NULL; + + if ( enabled ) + { + src = &mDisabledAccelerators; + dst = &mAccelerators; + } + else + { + src = &mAccelerators; + dst = &mDisabledAccelerators; + } + + Win32Window::AcceleratorList::iterator i; + for ( i = src->begin(); i != src->end(); ++i ) + { + if ( i->mID == id ) + { + Win32Window::Accelerator tmp = *i; + src->erase( i ); + dst->push_back( tmp ); + return; + } + } +} + +////////////////////////////////////////////////////////////////////////// + +void PopupMenu::createPlatformPopupMenuData() +{ + mData = new PlatformPopupMenuData; +} + +void PopupMenu::deletePlatformPopupMenuData() +{ + SAFE_DELETE(mData); +} +void PopupMenu::createPlatformMenu() +{ + mData->mMenu = mIsPopup ? CreatePopupMenu() : CreateMenu(); + AssertFatal(mData->mMenu, "Unable to create menu"); + + MENUINFO mi; + mi.cbSize = sizeof(mi); + mi.fMask = MIM_MENUDATA; + mi.dwMenuData = (ULONG_PTR)this; + SetMenuInfo(mData->mMenu, &mi); +} + +////////////////////////////////////////////////////////////////////////// +// Public Methods +////////////////////////////////////////////////////////////////////////// + +S32 PopupMenu::insertItem(S32 pos, const char *title, const char* accelerator) +{ + Win32Window *pWindow = mCanvas ? dynamic_cast(mCanvas->getPlatformWindow()) : NULL; + bool isAttached = isAttachedToMenuBar(); + if(isAttached && pWindow == NULL) + return -1; + + MENUITEMINFOA mi; + mi.cbSize = sizeof(mi); + mi.fMask = MIIM_ID|MIIM_TYPE; + mi.wID = (mData->mMenuID * PlatformPopupMenuData::PopupMenuIDRange) + mData->mLastID + 1; + mData->mLastID++; + if(title && *title) + mi.fType = MFT_STRING; + else + mi.fType = MFT_SEPARATOR; + + char buf[1024]; + if(accelerator && *accelerator) + { + dSprintf(buf, sizeof(buf), "%s\t%s", title, accelerator); + + if(isAttached) + pWindow->removeAccelerators(mData->mAccelerators); + + // Build entry for accelerator table + EventDescriptor accelDesc; + if(ActionMap::createEventDescriptor(accelerator, &accelDesc)) + mData->insertAccelerator(accelDesc, mi.wID); + else + Con::errorf("PopupMenu::insertItem - Could not create event descriptor for accelerator \"%s\"", accelerator); + + if(isAttached) + pWindow->addAccelerators(mData->mAccelerators); + } + else + dSprintf(buf, sizeof(buf), "%s", title); + + mi.dwTypeData = (LPSTR)buf; + + if(InsertMenuItemA(mData->mMenu, pos, TRUE, &mi)) + { + if(isAttached) + { + HWND hWindow = pWindow->getHWND(); + DrawMenuBar(hWindow); + } + return mi.wID; + } + + return -1; +} + +S32 PopupMenu::insertSubMenu(S32 pos, const char *title, PopupMenu *submenu) +{ + Win32Window *pWindow = mCanvas ? dynamic_cast(mCanvas->getPlatformWindow()) : NULL; + bool isAttached = isAttachedToMenuBar(); + if(isAttached && pWindow == NULL) + return -1; + + for(S32 i = 0;i < mSubmenus->size();i++) + { + if(submenu == (*mSubmenus)[i]) + { + Con::errorf("PopupMenu::insertSubMenu - Attempting to add submenu twice"); + return -1; + } + } + + MENUITEMINFOA mi; + mi.cbSize = sizeof(mi); + mi.fMask = MIIM_ID|MIIM_TYPE|MIIM_SUBMENU|MIIM_DATA; + mi.wID = (mData->mMenuID * PlatformPopupMenuData::PopupMenuIDRange) + mData->mLastID + 1; + if(title && *title) + mi.fType = MFT_STRING; + else + mi.fType = MFT_SEPARATOR; + mi.dwTypeData = (LPSTR)title; + mi.hSubMenu = submenu->mData->mMenu; + mi.dwItemData = (ULONG_PTR)submenu; + if(InsertMenuItemA(mData->mMenu, pos, TRUE, &mi)) + { + mSubmenus->addObject(submenu); + + if(isAttached) + { + pWindow->addAccelerators(submenu->mData->mAccelerators); + + HWND hWindow = pWindow->getHWND(); + DrawMenuBar(hWindow); + } + return mi.wID; + } + + return -1; +} + +bool PopupMenu::setItem(S32 pos, const char *title, const char* accelerator) +{ + Win32Window *pWindow = mCanvas ? dynamic_cast(mCanvas->getPlatformWindow()) : NULL; + bool isAttached = isAttachedToMenuBar(); + if(isAttached && pWindow == NULL) + return false; + + // Are we out of range? + if ( pos >= getItemCount() ) + return false; + + MENUITEMINFOA mi; + mi.cbSize = sizeof(mi); + mi.fMask = MIIM_TYPE; + + if(title && *title) + mi.fType = MFT_STRING; + else + mi.fType = MFT_SEPARATOR; + + char buf[1024]; + if(accelerator && *accelerator) + { + dSprintf(buf, sizeof(buf), "%s\t%s", title, accelerator); + + if(isAttached) + pWindow->removeAccelerators(mData->mAccelerators); + + // Build entry for accelerator table + EventDescriptor accelDesc; + if(ActionMap::createEventDescriptor(accelerator, &accelDesc)) + mData->insertAccelerator(accelDesc, pos); + else + Con::errorf("PopupMenu::setItem - Could not create event descriptor for accelerator \"%s\"", accelerator); + + if(isAttached) + pWindow->addAccelerators(mData->mAccelerators); + } + else + dSprintf(buf, sizeof(buf), "%s", title); + + mi.dwTypeData = (LPSTR)buf; + + if(SetMenuItemInfoA(mData->mMenu, pos, TRUE, &mi)) + { + if(isAttached) + { + HWND hWindow = pWindow->getHWND(); + DrawMenuBar(hWindow); + } + + return true; + } + + return false; +} + +void PopupMenu::removeItem(S32 itemPos) +{ + Win32Window *pWindow = mCanvas ? dynamic_cast(mCanvas->getPlatformWindow()) : NULL; + bool isAttached = isAttachedToMenuBar(); + if(isAttached && pWindow == NULL) + return; + + MENUITEMINFOA mi; + mi.cbSize = sizeof(mi); + mi.fMask = MIIM_DATA|MIIM_ID; + if(GetMenuItemInfoA(mData->mMenu, itemPos, TRUE, &mi)) + { + bool submenu = false; + + // Update list of submenus if this is a submenu + if(mi.fMask & MIIM_DATA) + { + PopupMenu *mnu = (PopupMenu *)mi.dwItemData; + if( mnu != NULL ) + { + if(isAttached) + pWindow->removeAccelerators(mnu->mData->mAccelerators); + mSubmenus->removeObject(mnu); + + submenu = true; + } + } + + if(! submenu) + { + // Update accelerators if this has an accelerator and wasn't a sub menu + for(S32 i = 0;i < mData->mAccelerators.size();++i) + { + if(mData->mAccelerators[i].mID == mi.wID) + { + if(isAttached) + pWindow->removeAccelerators(mData->mAccelerators); + + mData->mAccelerators.erase(i); + + if(isAttached) + pWindow->addAccelerators(mData->mAccelerators); + + break; + } + } + } + } + else + return; + + RemoveMenu(mData->mMenu, itemPos, MF_BYPOSITION); + + if(isAttached) + { + HWND hWindow = pWindow->getHWND(); + DrawMenuBar(hWindow); + } +} + +////////////////////////////////////////////////////////////////////////// + +void PopupMenu::enableItem( S32 pos, bool enable ) +{ + U32 flags = enable ? MF_ENABLED : MF_GRAYED; + EnableMenuItem( mData->mMenu, pos, MF_BYPOSITION|flags ); + + // Update accelerators. + + // NOTE: This really DOES need to happen. A disabled menu item + // should not still have an accelerator mapped to it. + // + // Unfortunately, the editors currently only set menu items + // enabled/disabled when the menu itself is selected which means our + // accelerators would be out of synch. + + /* + Win32Window *pWindow = mCanvas ? dynamic_cast( mCanvas->getPlatformWindow() ) : NULL; + bool isAttached = isAttachedToMenuBar(); + if ( isAttached && pWindow == NULL ) + return; + + MENUITEMINFOA mi; + mi.cbSize = sizeof(mi); + mi.fMask = MIIM_DATA|MIIM_ID; + if ( !GetMenuItemInfoA( mData->mMenu, pos, TRUE, &mi) ) + return; + + if ( isAttached ) + pWindow->removeAccelerators( mData->mAccelerators ); + + mData->setAccelleratorEnabled( mi.wID, enable ); + + if ( isAttached ) + pWindow->addAccelerators( mData->mAccelerators ); + */ +} + +void PopupMenu::checkItem(S32 pos, bool checked) +{ +// U32 flags = checked ? MF_CHECKED : MF_UNCHECKED; +// CheckMenuItem(mData->mMenu, pos, MF_BYPOSITION|flags); + + MENUITEMINFOA mi; + mi.cbSize = sizeof(mi); + mi.fMask = MIIM_STATE; + mi.fState = checked ? MFS_CHECKED : MFS_UNCHECKED; + SetMenuItemInfoA(mData->mMenu, pos, TRUE, &mi); +} + +void PopupMenu::checkRadioItem(S32 firstPos, S32 lastPos, S32 checkPos) +{ + CheckMenuRadioItem(mData->mMenu, firstPos, lastPos, checkPos, MF_BYPOSITION); +} + +bool PopupMenu::isItemChecked(S32 pos) +{ + MENUITEMINFOA mi; + mi.cbSize = sizeof(mi); + mi.fMask = MIIM_STATE; + if(GetMenuItemInfoA(mData->mMenu, pos, TRUE, &mi) && (mi.fState & MFS_CHECKED)) + return true; + return false; +} + +U32 PopupMenu::getItemCount() +{ + return GetMenuItemCount( mData->mMenu ); +} + +////////////////////////////////////////////////////////////////////////// + +bool PopupMenu::canHandleID(U32 id) +{ + for(S32 i = 0;i < mSubmenus->size();i++) + { + PopupMenu *subM = dynamic_cast((*mSubmenus)[i]); + if(subM == NULL) + continue; + + if(subM->canHandleID(id)) + return true; + } + + if(id >= mData->mMenuID * PlatformPopupMenuData::PopupMenuIDRange && + id < (mData->mMenuID+1) * PlatformPopupMenuData::PopupMenuIDRange) + { + return true; + } + + return false; +} + +bool PopupMenu::handleSelect(U32 command, const char *text /* = NULL */) +{ + // [tom, 8/20/2006] Pass off to a sub menu if it's for them + for(S32 i = 0;i < mSubmenus->size();i++) + { + PopupMenu *subM = dynamic_cast((*mSubmenus)[i]); + if(subM == NULL) + continue; + + if(subM->canHandleID(command)) + { + return subM->handleSelect(command, text); + } + } + + // [tom, 8/21/2006] Cheesey hack to find the position based on ID + char buf[512]; + MENUITEMINFOA mi; + mi.cbSize = sizeof(mi); + mi.dwTypeData = NULL; + + S32 numItems = GetMenuItemCount(mData->mMenu); + S32 pos = -1; + for(S32 i = 0;i < numItems;i++) + { + mi.fMask = MIIM_ID|MIIM_STRING|MIIM_STATE; + if(GetMenuItemInfoA(mData->mMenu, i, TRUE, &mi)) + { + if(mi.wID == command) + { + if(text == NULL) + { + mi.dwTypeData = buf; + mi.cch++; + GetMenuItemInfoA(mData->mMenu, i, TRUE, &mi); + + // [tom, 5/11/2007] Don't do anything if the menu item is disabled + if(mi.fState & MFS_DISABLED) + return false; + + text = StringTable->insert(mi.dwTypeData); + } + pos = i; + break; + } + } + } + + if(pos == -1) + { + Con::errorf("PopupMenu::handleSelect - Could not find menu item position for ID %d ... this shouldn't happen!", command); + return false; + } + + // [tom, 8/20/2006] Wasn't handled by a submenu, pass off to script + return dAtob(Con::executef(this, "onSelectItem", Con::getIntArg(pos), text ? text : "")); +} + +////////////////////////////////////////////////////////////////////////// + +void PopupMenu::showPopup(GuiCanvas *owner, S32 x /* = -1 */, S32 y /* = -1 */) +{ + if( owner == NULL ) + { + Con::warnf("PopupMenu::showPopup - Invalid canvas supplied!"); + return; + } + + // [tom, 6/4/2007] showPopup() blocks until the menu is closed by the user, + // so the canvas pointer is not needed beyond the scope of this function + // when working with context menus. Setting mCanvas here will cause undesired + // behavior in relation to the menu bar. + + Win32Window *pWindow = dynamic_cast(owner->getPlatformWindow()); + if(pWindow == NULL) + return; + HWND hWindow = pWindow->getHWND(); + POINT p; + if(x == -1 && y == -1) + GetCursorPos(&p); + else + { + p.x = x; + p.y = y; + ClientToScreen(hWindow, &p); + } + + winState.renderThreadBlocked = true; + U32 opt = (int)TrackPopupMenu(mData->mMenu, TPM_NONOTIFY|TPM_RETURNCMD, p.x, p.y, 0, hWindow, NULL); + if(opt > 0) + handleSelect(opt, NULL); + winState.renderThreadBlocked = false; +} + +////////////////////////////////////////////////////////////////////////// + +void PopupMenu::attachToMenuBar(GuiCanvas *owner, S32 pos, const char *title) +{ + if(owner == NULL || isAttachedToMenuBar()) + return; + + // This is set for sub-menus in the onAttachToMenuBar() callback + mCanvas = owner; + + Win32Window *pWindow = dynamic_cast(owner->getPlatformWindow()); + if(pWindow == NULL) + return; + + HMENU hWindowMenu = pWindow->getMenuHandle(); + if(hWindowMenu == NULL) + { + hWindowMenu = CreateMenu(); + if(hWindowMenu) + { + pWindow->setMenuHandle( hWindowMenu); + } + } + + MENUITEMINFOA mii; + + mii.cbSize = sizeof(MENUITEMINFOA); + + mii.fMask = MIIM_STRING|MIIM_DATA; + mii.dwTypeData = (LPSTR)title; + mii.fMask |= MIIM_ID; + mii.wID = mData->mMenuID; + mii.fMask |= MIIM_SUBMENU; + mii.hSubMenu = mData->mMenu; + mii.dwItemData = (ULONG_PTR)this; + + InsertMenuItemA(hWindowMenu, pos, TRUE, &mii); + + HWND hWindow = pWindow->getHWND(); + DrawMenuBar(hWindow); + + pWindow->addAccelerators(mData->mAccelerators); + + // Add accelerators for sub menus + for(SimSet::iterator i = mSubmenus->begin();i != mSubmenus->end();++i) + { + PopupMenu *submenu = dynamic_cast(*i); + if(submenu == NULL) + continue; + + pWindow->addAccelerators(submenu->mData->mAccelerators); + } + + onAttachToMenuBar(owner, pos, title); +} + +// New version of above for use by MenuBar class. Do not use yet. +void PopupMenu::attachToMenuBar(GuiCanvas *owner, S32 pos) +{ + Win32Window *pWindow = dynamic_cast(owner->getPlatformWindow()); + if(pWindow == NULL) + return; + + //When playing a journal, the system menu is not actually shown + if (Journal::IsPlaying()) + { + onAttachToMenuBar(owner, pos, mBarTitle); + return; + } + + HMENU hWindowMenu = pWindow->getMenuHandle(); + + MENUITEMINFOA mii; + + mii.cbSize = sizeof(MENUITEMINFOA); + + mii.fMask = MIIM_STRING|MIIM_DATA; + mii.dwTypeData = (LPSTR)mBarTitle; + mii.fMask |= MIIM_ID; + mii.wID = mData->mMenuID; + mii.fMask |= MIIM_SUBMENU; + mii.hSubMenu = mData->mMenu; + mii.dwItemData = (ULONG_PTR)this; + + InsertMenuItemA(hWindowMenu, pos, TRUE, &mii); + + pWindow->addAccelerators(mData->mAccelerators); + + // Add accelerators for sub menus (have to do this here as it's platform specific) + for(SimSet::iterator i = mSubmenus->begin();i != mSubmenus->end();++i) + { + PopupMenu *submenu = dynamic_cast(*i); + if(submenu == NULL) + continue; + + pWindow->addAccelerators(submenu->mData->mAccelerators); + } + + onAttachToMenuBar(owner, pos, mBarTitle); +} + +void PopupMenu::removeFromMenuBar() +{ + S32 pos = getPosOnMenuBar(); + if(pos == -1) + return; + + Win32Window *pWindow = mCanvas ? dynamic_cast(mCanvas->getPlatformWindow()) : NULL; + if(pWindow == NULL) + return; + + HMENU hMenuHandle = pWindow->getMenuHandle(); + if(!hMenuHandle) + return; + + RemoveMenu(hMenuHandle, pos, MF_BYPOSITION); + + HWND hWindow = pWindow->getHWND(); + + DrawMenuBar(hWindow); + + pWindow->removeAccelerators(mData->mAccelerators); + + // Remove accelerators for sub menus + for(SimSet::iterator i = mSubmenus->begin();i != mSubmenus->end();++i) + { + PopupMenu *submenu = dynamic_cast(*i); + if(submenu == NULL) + continue; + + pWindow->removeAccelerators(submenu->mData->mAccelerators); + } + + onRemoveFromMenuBar(mCanvas); +} + +S32 PopupMenu::getPosOnMenuBar() +{ + if(mCanvas == NULL) + return -1; + + Win32Window *pWindow = mCanvas ? dynamic_cast(mCanvas->getPlatformWindow()) : NULL; + if(pWindow == NULL) + return -1; + + HMENU hMenuHandle = pWindow->getMenuHandle(); + S32 numItems = GetMenuItemCount(hMenuHandle); + S32 pos = -1; + for(S32 i = 0;i < numItems;i++) + { + MENUITEMINFOA mi; + mi.cbSize = sizeof(mi); + mi.fMask = MIIM_DATA; + if(GetMenuItemInfoA(hMenuHandle, i, TRUE, &mi)) + { + if(mi.fMask & MIIM_DATA) + { + PopupMenu *mnu = (PopupMenu *)mi.dwItemData; + if(mnu == this) + { + pos = i; + break; + } + } + } + } + + return pos; +} + diff --git a/Engine/source/platformWin32/minidump/winMiniDump.cpp b/Engine/source/platformWin32/minidump/winMiniDump.cpp new file mode 100644 index 000000000..fd1ad9ab2 --- /dev/null +++ b/Engine/source/platformWin32/minidump/winMiniDump.cpp @@ -0,0 +1,337 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE ) + +#include "platformWin32/platformWin32.h" +#include "platformWin32/minidump/winStackWalker.h" +#include "core/fileio.h" +#include "core/strings/stringFunctions.h" +#include "console/console.h" +#include "app/net/serverQuery.h" + +#pragma pack(push,8) +#include +#include +#pragma pack(pop) + +#pragma comment(lib, "dbghelp.lib") + +extern Win32PlatState winState; + +//Forward declarations for the dialog functions +BOOL CALLBACK MiniDumpDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam); +LRESULT DisplayMiniDumpDialog(HINSTANCE hinst, HWND hwndOwner); +LPWORD lpwAlign(LPWORD lpIn); +char gUserInput[4096]; + +//Console variables +extern StringTableEntry gMiniDumpDir; +extern StringTableEntry gMiniDumpUser; +extern StringTableEntry gMiniDumpExec; +extern StringTableEntry gMiniDumpParams; +extern StringTableEntry gMiniDumpExecDir; + + +char* dStrrstr(char* dst, const char* src, const char* findStr, char* replaceStr) +{ + //see if str contains findStr, if not then return + const char* findpos = strstr(src, findStr); + if(!findpos) + { + strcpy(dst, src); + } + else + { + //copy the new string to the buffer + dst[0]='\0'; + strncat(dst, src, findpos-src); + strcat(dst, replaceStr); + const char* cur = findpos + strlen(findStr); + strcat(dst, cur); + } + + return dst; +} + + +//----------------------------------------------------------------------------------------------------------------------------------------- +// CreateMiniDump() +//----------------------------------------------------------------------------------------------------------------------------------------- +INT CreateMiniDump( LPEXCEPTION_POINTERS ExceptionInfo) +{ + //Get any information we can from the user and store it in gUserInput + try + { + while(ShowCursor(TRUE) < 0); + DisplayMiniDumpDialog(winState.appInstance, winState.appWindow); + } + catch(...) + { + //dSprintf(gUserInput, 4096, "The user could not enter a description of what was occurring.\n\n\n"); + } + + //Build a Game, Date and Time stamped MiniDump folder + time_t theTime; + time(&theTime); + tm* pLocalTime = localtime(&theTime); + char crashFolder[2048]; + dSprintf(crashFolder, 2048, "%s_%02d.%02d_%02d.%02d.%02d", + Platform::getExecutableName(), + pLocalTime->tm_mon+1, pLocalTime->tm_mday, + pLocalTime->tm_hour, pLocalTime->tm_min, pLocalTime->tm_sec); + + //Builed the fully qualified MiniDump path + char crashPath[2048]; + char fileName[2048]; + if(gMiniDumpDir==NULL) + { + dSprintf(crashPath, 2048, "%s/MiniDump/%s", Platform::getCurrentDirectory(), crashFolder); + } + else + { + dSprintf(crashPath, 2048, "%s/%s", gMiniDumpDir, crashFolder); + } + + dSprintf(fileName, 2048, "%s/Minidump.dmp",crashPath); + if (!Platform::createPath (fileName))return false; //create the directory + + //Save the minidump + File fileObject; + if(fileObject.open(fileName, File::Write) == File::Ok) + { + MINIDUMP_EXCEPTION_INFORMATION DumpExceptionInfo; + DumpExceptionInfo.ThreadId = GetCurrentThreadId(); + DumpExceptionInfo.ExceptionPointers = ExceptionInfo; + DumpExceptionInfo.ClientPointers = true; + MiniDumpWriteDump( GetCurrentProcess(), GetCurrentProcessId(), (HANDLE)fileObject.getHandle(), MiniDumpNormal, &DumpExceptionInfo, NULL, NULL ); + fileObject.close(); + } + + //copy over the log file + char fromFile[2048]; + dSprintf(fromFile, 2048, "%s/%s", Platform::getCurrentDirectory(), "console.log" ); + dSprintf(fileName, 2048, "%s/console.log", crashPath); + Con::setLogMode(3); //ensure that the log file is closed (so it can be copied) + dPathCopy(fromFile, fileName, true); + + //copy over the exe file + char exeName[1024]; + dSprintf(exeName, 1024, Platform::getExecutableName()); + exeName[dStrlen(exeName)-4]=0; + dSprintf(fromFile, 2048, "%s/%s.dll", Platform::getCurrentDirectory(), exeName ); + dSprintf(fileName, 2048, "%s/%s.dll", crashPath, exeName ); + dPathCopy(fromFile, fileName, true); + + //copy over the pdb file + char pdbName[1024]; + dStrcpy(pdbName, exeName); + dStrncat(pdbName, ".pdb", 4); + dSprintf(fromFile, 2048, "%s/%s", Platform::getCurrentDirectory(), pdbName ); + dSprintf(fileName, 2048, "%s/%s", crashPath, pdbName ); + dPathCopy(fromFile, fileName, true); + + //save the call stack + char traceBuffer[65536]; + traceBuffer[0] = 0; + dGetStackTrace( traceBuffer, *static_cast(ExceptionInfo->ContextRecord) ); + + //save the user input and the call stack to a file + char crashlogFile[2048]; + dSprintf(crashlogFile, 2048, "%s/crash.log", crashPath); + if(fileObject.open(crashlogFile, File::Write) == File::Ok) + { + fileObject.write(strlen(gUserInput), gUserInput); + fileObject.write(strlen(traceBuffer), traceBuffer); + fileObject.close(); + } + + //call the external program indicated in script + if(gMiniDumpExec!= NULL) + { + //replace special variables in gMiniDumpParams + if(gMiniDumpParams) + { + char updateParams[4096]; + char finalParams[4096]; + dStrrstr(finalParams, gMiniDumpParams, "%crashpath%", crashPath); + dStrrstr(updateParams, finalParams, "%crashfolder%", crashFolder); + dStrrstr(finalParams, updateParams, "%crashlog%", crashlogFile); + ShellExecuteA(NULL, "", gMiniDumpExec, finalParams, gMiniDumpExecDir ? gMiniDumpExecDir : "", SW_SHOWNORMAL); + } + else + { + ShellExecuteA(NULL, "", gMiniDumpExec, "", gMiniDumpExecDir ? gMiniDumpExecDir : "", SW_SHOWNORMAL); + } + } + + return EXCEPTION_EXECUTE_HANDLER; +} + + +//----------------------------------------------------------------------------------------------------------------------------------------- +// MiniDumpDialogProc - Used By DisplayMiniDumpDialog +//----------------------------------------------------------------------------------------------------------------------------------------- +const int ID_TEXT=200; +const int ID_USERTEXT=300; +const int ID_DONE=400; +BOOL CALLBACK MiniDumpDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + char text[128]= ""; + + switch (message) + { + case WM_INITDIALOG : + SetDlgItemTextA ( hwndDlg, ID_USERTEXT, text ); + return TRUE ; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case ID_DONE: + if( !GetDlgItemTextA(hwndDlg, ID_USERTEXT, gUserInput, 4096) ) gUserInput[0]='\0'; + strcat(gUserInput, "\n\n\n"); + EndDialog(hwndDlg, wParam); + return TRUE; + default: + return TRUE; + } + } + return FALSE; +} + + +//----------------------------------------------------------------------------------------------------------------------------------------- +// Helper function to DWORD align the Dialog Box components (Used in DisplayMiniDumpDialog() +//----------------------------------------------------------------------------------------------------------------------------------------- +LPWORD lpwAlign(LPWORD lpIn) +{ + ULONG ul; + + ul = (ULONG)lpIn; + ul ++; + ul >>=1; + ul <<=1; + return (LPWORD)ul; +} + + +//----------------------------------------------------------------------------------------------------------------------------------------- +// Create the Dialog Box to get input from the user +//----------------------------------------------------------------------------------------------------------------------------------------- +LRESULT DisplayMiniDumpDialog(HINSTANCE hinst, HWND hwndOwner) +{ + HGLOBAL hgbl = GlobalAlloc(GMEM_ZEROINIT, 1024); + if (!hgbl) return -1; + + //----------------------------------------------------------------- + // Define the dialog box + //----------------------------------------------------------------- + LPDLGTEMPLATE lpdt = (LPDLGTEMPLATE)GlobalLock(hgbl); + lpdt->style = WS_POPUP | WS_BORDER | DS_MODALFRAME | WS_CAPTION; + lpdt->cdit = 3; // Number of controls + lpdt->x = 100; + lpdt->y = 100; + lpdt->cx = 300; + lpdt->cy = 90; + + LPWORD lpw = (LPWORD)(lpdt + 1); + *lpw++ = 0; // No menu + *lpw++ = 0; // Predefined dialog box class (by default) + + LPWSTR lpwsz = (LPWSTR)lpw; + int nchar = 1 + MultiByteToWideChar(CP_ACP, 0, "MiniDump Crash Report", -1, lpwsz, 50); + lpw += nchar; + + //----------------------------------------------------------------- + // Define a static text message + //----------------------------------------------------------------- + lpw = lpwAlign(lpw); // Align DLGITEMTEMPLATE on DWORD boundary + LPDLGITEMTEMPLATE lpdit = (LPDLGITEMTEMPLATE)lpw; + lpdit->x = 10; + lpdit->y = 10; + lpdit->cx = 290; + lpdit->cy = 10; + lpdit->id = ID_TEXT; // Text identifier + lpdit->style = WS_CHILD | WS_VISIBLE | SS_LEFT; + + lpw = (LPWORD)(lpdit + 1); + *lpw++ = 0xFFFF; + *lpw++ = 0x0082; // Static class + + LPSTR msg = "The program has crashed. Please describe what was happening:"; + for (lpwsz = (LPWSTR)lpw; *lpwsz++ = (WCHAR)*msg++;); + lpw = (LPWORD)lpwsz; + + *lpw++ = 0; // No creation data + + //----------------------------------------------------------------- + // Define a DONE button + //----------------------------------------------------------------- + lpw = lpwAlign(lpw); // Align DLGITEMTEMPLATE on DWORD boundary + lpdit = (LPDLGITEMTEMPLATE)lpw; + lpdit->x = 265; + lpdit->y = 75; + lpdit->cx = 25; + lpdit->cy = 12; + lpdit->id = ID_DONE; // OK button identifier + lpdit->style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;// | BS_DEFPUSHBUTTON; + + lpw = (LPWORD)(lpdit + 1); + *lpw++ = 0xFFFF; + *lpw++ = 0x0080; // Button class + + lpwsz = (LPWSTR)lpw; + nchar = 1 + MultiByteToWideChar(CP_ACP, 0, "Done", -1, lpwsz, 50); + lpw += nchar; + *lpw++ = 0; // No creation data + + //----------------------------------------------------------------- + // Define a text entry message + //----------------------------------------------------------------- + lpw = lpwAlign(lpw); // Align DLGITEMTEMPLATE on DWORD boundary + lpdit = (LPDLGITEMTEMPLATE)lpw; + lpdit->x = 10; + lpdit->y = 22; + lpdit->cx = 280; + lpdit->cy = 50; + lpdit->id = ID_USERTEXT; // Text identifier + lpdit->style = ES_LEFT | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE; + + lpw = (LPWORD)(lpdit + 1); + *lpw++ = 0xFFFF; + *lpw++ = 0x0081; // Text edit class + + *lpw++ = 0; // No creation data + + + + GlobalUnlock(hgbl); + LRESULT ret = DialogBoxIndirect( hinst, + (LPDLGTEMPLATE)hgbl, + hwndOwner, + (DLGPROC)MiniDumpDialogProc); + GlobalFree(hgbl); + return ret; +} + +#endif \ No newline at end of file diff --git a/Engine/source/platformWin32/minidump/winStackWalker.cpp b/Engine/source/platformWin32/minidump/winStackWalker.cpp new file mode 100644 index 000000000..a533aa97f --- /dev/null +++ b/Engine/source/platformWin32/minidump/winStackWalker.cpp @@ -0,0 +1,825 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE ) + +//----------------------------------------------------------------------------------------------------------------------------------------- +// Sourced from http://www.codeproject.com/threads/StackWalker.asp +//----------------------------------------------------------------------------------------------------------------------------------------- +#include "winStackWalker.h" + +#undef UNICODE + +#include +#include +#include +#include + +#pragma comment(lib, "version.lib") // for "VerQueryValue" +#pragma comment(lib, "dbghelp.lib") +#pragma comment(lib, "psapi.lib") + + +// secure-CRT_functions are only available starting with VC8 +#if _MSC_VER < 1400 +#define strcpy_s strcpy +#define strcat_s(dst, len, src) strcat(dst, src) +#define _snprintf_s _snprintf +#define _tcscat_s _tcscat +#endif + +// Entry for each Callstack-Entry +const int STACKWALK_MAX_NAMELEN = 1024; // max name length for symbols +struct CallstackEntry +{ + DWORD64 offset; // if 0, we have no valid entry + char name[STACKWALK_MAX_NAMELEN]; + char undName[STACKWALK_MAX_NAMELEN]; + char undFullName[STACKWALK_MAX_NAMELEN]; + DWORD64 offsetFromSmybol; + DWORD64 offsetFromLine; + DWORD lineNumber; + char lineFileName[STACKWALK_MAX_NAMELEN]; + DWORD symType; + const char * symTypeString; + char moduleName[STACKWALK_MAX_NAMELEN]; + DWORD64 baseOfImage; + char loadedImageName[STACKWALK_MAX_NAMELEN]; +}; + + +//----------------------------------------------------------------------------------------------------------------------------------------- +// Implementation of platform functions +//----------------------------------------------------------------------------------------------------------------------------------------- +void dGetStackTrace(char * traceBuffer, CONTEXT const & ContextRecord) +{ + StackWalker sw; + sw.setOutputBuffer(traceBuffer); + sw.ShowCallstack(GetCurrentThread(), ContextRecord); + sw.setOutputBuffer(NULL); +} + + +//----------------------------------------------------------------------------------------------------------------------------------------- +//Constructor +//----------------------------------------------------------------------------------------------------------------------------------------- +StackWalker::StackWalker(DWORD options, LPCSTR szSymPath) +: m_dwProcessId(GetCurrentProcessId()), +m_hProcess(GetCurrentProcess()), +m_options(options), +m_modulesLoaded(false), +m_pOutputBuffer(NULL) +{ + if (szSymPath != NULL) + { + m_szSymPath = _strdup(szSymPath); + m_options |= SymBuildPath; + } + else + m_szSymPath = NULL; +} + + +//----------------------------------------------------------------------------------------------------------------------------------------- +//Destructor +//----------------------------------------------------------------------------------------------------------------------------------------- +StackWalker::~StackWalker() +{ + SymCleanup(m_hProcess); + if (m_szSymPath != NULL) free(m_szSymPath); + m_szSymPath = NULL; +} + + +//----------------------------------------------------------------------------------------------------------------------------------------- +//setOutputBuffer - Points the StackWalker at a buffer where it will write out any output. If this is set to NULL then the output +// will only be sent to DebugString +//----------------------------------------------------------------------------------------------------------------------------------------- +void StackWalker::setOutputBuffer(char * buffer) +{ + m_pOutputBuffer = buffer; +} + + +//----------------------------------------------------------------------------------------------------------------------------------------- +//OnOutput +//----------------------------------------------------------------------------------------------------------------------------------------- +void StackWalker::OnOutput(LPCSTR buffer) +{ + OutputDebugStringA(buffer); + if(m_pOutputBuffer) strcat(m_pOutputBuffer, buffer); +} + + +//----------------------------------------------------------------------------------------------------------------------------------------- +//Init +//----------------------------------------------------------------------------------------------------------------------------------------- +bool StackWalker::Init(LPCSTR szSymPath) +{ + // SymInitialize + if (szSymPath != NULL) m_szSymPath = _strdup(szSymPath); + if (SymInitialize(m_hProcess, m_szSymPath, FALSE) == FALSE) + { + this->OnDbgHelpErr("SymInitialize", GetLastError(), 0); + } + + DWORD symOptions = SymGetOptions(); + symOptions |= SYMOPT_LOAD_LINES; + symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS; + symOptions = SymSetOptions(symOptions); + + char buf[STACKWALK_MAX_NAMELEN] = {0}; + if(SymGetSearchPath(m_hProcess, buf, STACKWALK_MAX_NAMELEN) == FALSE) + { + this->OnDbgHelpErr("SymGetSearchPath", GetLastError(), 0); + } + + char szUserName[1024] = {0}; + DWORD dwSize = 1024; + GetUserNameA(szUserName, &dwSize); + this->OnSymInit(buf, symOptions, szUserName); + + return TRUE; +} + + +//----------------------------------------------------------------------------------------------------------------------------------------- +//LoadModules +//----------------------------------------------------------------------------------------------------------------------------------------- +bool StackWalker::LoadModules() +{ + if (m_modulesLoaded) return true; + + // Build the sym-path: + char *szSymPath = NULL; + if ( (m_options & SymBuildPath) != 0) + { + const size_t nSymPathLen = 4096; + szSymPath = (char*) malloc(nSymPathLen); + if (szSymPath == NULL) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return false; + } + szSymPath[0] = 0; + // Now first add the (optional) provided sympath: + if (m_szSymPath != NULL) + { + strcat_s(szSymPath, nSymPathLen, m_szSymPath); + strcat_s(szSymPath, nSymPathLen, ";"); + } + + strcat_s(szSymPath, nSymPathLen, ".;"); + + const size_t nTempLen = 1024; + CHAR szTemp[nTempLen]; + // Now add the current directory: + if (GetCurrentDirectoryA(nTempLen, szTemp) > 0) + { + szTemp[nTempLen-1] = 0; + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + + // Now add the path for the main-module: + if (GetModuleFileNameA(NULL, szTemp, nTempLen) > 0) + { + szTemp[nTempLen-1] = 0; + for (char *p = (szTemp+strlen(szTemp)-1); p >= szTemp; --p) + { + // locate the rightmost path separator + if ( (*p == '\\') || (*p == '/') || (*p == ':') ) + { + *p = 0; + break; + } + } // for (search for path separator...) + if (strlen(szTemp) > 0) + { + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + } + if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", szTemp, nTempLen) > 0) + { + szTemp[nTempLen-1] = 0; + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", szTemp, nTempLen) > 0) + { + szTemp[nTempLen-1] = 0; + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + if (GetEnvironmentVariableA("SYSTEMROOT", szTemp, nTempLen) > 0) + { + szTemp[nTempLen-1] = 0; + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + // also add the "system32"-directory: + strcat_s(szTemp, nTempLen, "\\system32"); + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + + if ( (m_options & SymBuildPath) != 0 ) + { + if (GetEnvironmentVariableA("SYSTEMDRIVE", szTemp, nTempLen) > 0) + { + szTemp[nTempLen-1] = 0; + strcat_s(szSymPath, nSymPathLen, "SRV*"); + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, "\\websymbols"); + strcat_s(szSymPath, nSymPathLen, "*http://msdl.microsoft.com/download/symbols;"); + } + else + strcat_s(szSymPath, nSymPathLen, "SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols;"); + } + } + + // First Init the whole stuff... + bool bRet = Init(szSymPath); + if (szSymPath != NULL) + { + free(szSymPath); + szSymPath = NULL; + } + if (bRet == false) + { + this->OnDbgHelpErr("Error while initializing dbghelp.dll", 0, 0); + SetLastError(ERROR_DLL_INIT_FAILED); + return false; + } + + if(GetModuleListTH32(m_hProcess, m_dwProcessId)) + { + m_modulesLoaded = true; + return true; + } + + // then try psapi + if(GetModuleListPSAPI(m_hProcess)) + { + m_modulesLoaded = true; + return true; + } + + return false; +} + + +//----------------------------------------------------------------------------------------------------------------------------------------- +//LoadModule +//----------------------------------------------------------------------------------------------------------------------------------------- +DWORD StackWalker::LoadModule(HANDLE hProcess, LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size) +{ + CHAR *szImg = _strdup(img); + CHAR *szMod = _strdup(mod); + DWORD result = ERROR_SUCCESS; + if ( (szImg == NULL) || (szMod == NULL) ) + result = ERROR_NOT_ENOUGH_MEMORY; + else + { + if (SymLoadModule64(hProcess, 0, szImg, szMod, baseAddr, size) == 0) + result = GetLastError(); + } + ULONGLONG fileVersion = 0; + if(szImg != NULL) + { + // try to retrieve the file-version: + if ( (m_options & StackWalker::RetrieveFileVersion) != 0) + { + VS_FIXEDFILEINFO *fInfo = NULL; + DWORD dwHandle; + DWORD dwSize = GetFileVersionInfoSizeA(szImg, &dwHandle); + if (dwSize > 0) + { + LPVOID vData = malloc(dwSize); + if (vData != NULL) + { + if (GetFileVersionInfoA(szImg, dwHandle, dwSize, vData) != 0) + { + UINT len; + char szSubBlock[] = _T("\\"); + if (VerQueryValueA(vData, szSubBlock, (LPVOID*) &fInfo, &len) == 0) + fInfo = NULL; + else + { + fileVersion = ((ULONGLONG)fInfo->dwFileVersionLS) + ((ULONGLONG)fInfo->dwFileVersionMS << 32); + } + } + free(vData); + } + } + } + + // Retrive some additional-infos about the module + IMAGEHLP_MODULE64 Module; + const char *szSymType = "-unknown-"; + if (this->GetModuleInfo(hProcess, baseAddr, &Module) != FALSE) + { + switch(Module.SymType) + { + case SymNone: + szSymType = "-nosymbols-"; + break; + case SymCoff: + szSymType = "COFF"; + break; + case SymCv: + szSymType = "CV"; + break; + case SymPdb: + szSymType = "PDB"; + break; + case SymExport: + szSymType = "-exported-"; + break; + case SymDeferred: + szSymType = "-deferred-"; + break; + case SymSym: + szSymType = "SYM"; + break; + case 8: //SymVirtual: + szSymType = "Virtual"; + break; + case 9: // SymDia: + szSymType = "DIA"; + break; + } + } + this->OnLoadModule(img, mod, baseAddr, size, result, szSymType, Module.LoadedImageName, fileVersion); + } + if (szImg != NULL) free(szImg); + if (szMod != NULL) free(szMod); + return result; +} + + +// The following is used to pass the "userData"-Pointer to the user-provided readMemoryFunction +// This has to be done due to a problem with the "hProcess"-parameter in x64... +// Because this class is in no case multi-threading-enabled (because of the limitations of dbghelp.dll) it is "safe" to use a static-variable +static StackWalker::PReadProcessMemoryRoutine s_readMemoryFunction = NULL; +static LPVOID s_readMemoryFunction_UserData = NULL; + + +//----------------------------------------------------------------------------------------------------------------------------------------- +//ShowCallstack +//----------------------------------------------------------------------------------------------------------------------------------------- +bool StackWalker::ShowCallstack(HANDLE hThread, CONTEXT const & context, PReadProcessMemoryRoutine readMemoryFunction, LPVOID pUserData) +{ + CONTEXT c = context; + IMAGEHLP_SYMBOL64 *pSym = NULL; + IMAGEHLP_MODULE64 Module; + IMAGEHLP_LINE64 Line; + int frameNum; + + if (!m_modulesLoaded) LoadModules(); + + + s_readMemoryFunction = readMemoryFunction; + s_readMemoryFunction_UserData = pUserData; + + + + // init STACKFRAME for first call + STACKFRAME64 s; // in/out stackframe + memset(&s, 0, sizeof(s)); + DWORD imageType; +#ifdef _M_IX86 + // normally, call ImageNtHeader() and use machine info from PE header + imageType = IMAGE_FILE_MACHINE_I386; + s.AddrPC.Offset = c.Eip; + s.AddrPC.Mode = AddrModeFlat; + s.AddrFrame.Offset = c.Ebp; + s.AddrFrame.Mode = AddrModeFlat; + s.AddrStack.Offset = c.Esp; + s.AddrStack.Mode = AddrModeFlat; +#elif _M_X64 + imageType = IMAGE_FILE_MACHINE_AMD64; + s.AddrPC.Offset = c.Rip; + s.AddrPC.Mode = AddrModeFlat; + s.AddrFrame.Offset = c.Rsp; + s.AddrFrame.Mode = AddrModeFlat; + s.AddrStack.Offset = c.Rsp; + s.AddrStack.Mode = AddrModeFlat; +#elif _M_IA64 + imageType = IMAGE_FILE_MACHINE_IA64; + s.AddrPC.Offset = c.StIIP; + s.AddrPC.Mode = AddrModeFlat; + s.AddrFrame.Offset = c.IntSp; + s.AddrFrame.Mode = AddrModeFlat; + s.AddrBStore.Offset = c.RsBSP; + s.AddrBStore.Mode = AddrModeFlat; + s.AddrStack.Offset = c.IntSp; + s.AddrStack.Mode = AddrModeFlat; +#else +#error "Platform not supported!" +#endif + + pSym = (IMAGEHLP_SYMBOL64 *) malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN); + if (!pSym) goto cleanup; // not enough memory... + memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN); + pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); + pSym->MaxNameLength = STACKWALK_MAX_NAMELEN; + + memset(&Line, 0, sizeof(Line)); + Line.SizeOfStruct = sizeof(Line); + + memset(&Module, 0, sizeof(Module)); + Module.SizeOfStruct = sizeof(Module); + + for (frameNum = 0; ; ++frameNum ) + { + // get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64()) + // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can + // assume that either you are done, or that the stack is so hosed that the next + // deeper frame could not be found. + // CONTEXT need not to be suplied if imageTyp is IMAGE_FILE_MACHINE_I386! + if ( ! StackWalk64(imageType, this->m_hProcess, hThread, &s, &c, myReadProcMem, SymFunctionTableAccess64, SymGetModuleBase64, NULL) ) + { + this->OnDbgHelpErr("StackWalk64", GetLastError(), s.AddrPC.Offset); + break; + } + + CallstackEntry csEntry; + csEntry.offset = s.AddrPC.Offset; + csEntry.name[0] = 0; + csEntry.undName[0] = 0; + csEntry.undFullName[0] = 0; + csEntry.offsetFromSmybol = 0; + csEntry.offsetFromLine = 0; + csEntry.lineFileName[0] = 0; + csEntry.lineNumber = 0; + csEntry.loadedImageName[0] = 0; + csEntry.moduleName[0] = 0; + if (s.AddrPC.Offset == s.AddrReturn.Offset) + { + OnDbgHelpErr("StackWalk64-Endless-Callstack!", 0, s.AddrPC.Offset); + break; + } + if (s.AddrPC.Offset != 0) + { + // we seem to have a valid PC, show procedure info + if (SymGetSymFromAddr64(m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromSmybol), pSym) != FALSE) + { + // TODO: Mache dies sicher...! + strcpy_s(csEntry.name, pSym->Name); + UnDecorateSymbolName( pSym->Name, csEntry.undName, STACKWALK_MAX_NAMELEN, UNDNAME_NAME_ONLY ); + UnDecorateSymbolName( pSym->Name, csEntry.undFullName, STACKWALK_MAX_NAMELEN, UNDNAME_COMPLETE ); + } + else + { + this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), s.AddrPC.Offset); + } + + // show line number info, NT5.0-method + if (SymGetLineFromAddr64(this->m_hProcess, s.AddrPC.Offset, (PDWORD)&(csEntry.offsetFromLine), &Line) != FALSE) + { + csEntry.lineNumber = Line.LineNumber; + // TODO: Mache dies sicher...! + strcpy_s(csEntry.lineFileName, Line.FileName); + } + else + { + this->OnDbgHelpErr("SymGetLineFromAddr64", GetLastError(), s.AddrPC.Offset); + } + + // show module info + if( GetModuleInfo(this->m_hProcess, s.AddrPC.Offset, &Module) ) + { + switch ( Module.SymType ) + { + case SymNone: + csEntry.symTypeString = "-nosymbols-"; + break; + case SymCoff: + csEntry.symTypeString = "COFF"; + break; + case SymCv: + csEntry.symTypeString = "CV"; + break; + case SymPdb: + csEntry.symTypeString = "PDB"; + break; + case SymExport: + csEntry.symTypeString = "-exported-"; + break; + case SymDeferred: + csEntry.symTypeString = "-deferred-"; + break; + case SymSym: + csEntry.symTypeString = "SYM"; + break; + case SymDia: + csEntry.symTypeString = "DIA"; + break; + case SymVirtual: + csEntry.symTypeString = "Virtual"; + break; + default: + //_snprintf( ty, sizeof ty, "symtype=%ld", (long) Module.SymType ); + csEntry.symTypeString = NULL; + break; + } + + // TODO: Mache dies sicher...! + strcpy_s(csEntry.moduleName, Module.ModuleName); + csEntry.baseOfImage = Module.BaseOfImage; + strcpy_s(csEntry.loadedImageName, Module.LoadedImageName); + } + else + { + OnDbgHelpErr("SymGetModuleInfo64", GetLastError(), s.AddrPC.Offset); + } + } + + CallstackEntryType et = nextEntry; + if (frameNum == 0) et = firstEntry; + OnCallstackEntry(et, csEntry); + + if (s.AddrReturn.Offset == 0) + { + OnCallstackEntry(lastEntry, csEntry); + SetLastError(ERROR_SUCCESS); + break; + } + } + +cleanup: + if (pSym) free( pSym ); + + return true; +} + + + +BOOL __stdcall StackWalker::myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead) +{ + if (s_readMemoryFunction == NULL) + { + SIZE_T st; + BOOL bRet = ReadProcessMemory(hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, &st); + *lpNumberOfBytesRead = (DWORD) st; + //printf("ReadMemory: hProcess: %p, baseAddr: %p, buffer: %p, size: %d, read: %d, result: %d\n", hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, (DWORD) st, (DWORD) bRet); + return bRet; + } + else + { + return s_readMemoryFunction(hProcess, qwBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead, s_readMemoryFunction_UserData); + } +} + + +//----------------------------------------------------------------------------------------------------------------------------------------- +//OnLoadModule +//----------------------------------------------------------------------------------------------------------------------------------------- +void StackWalker::OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion) +{ + CHAR buffer[STACKWALK_MAX_NAMELEN]; + if (fileVersion == 0) + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s'\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName); + else + { + DWORD v4 = (DWORD) fileVersion & 0xFFFF; + DWORD v3 = (DWORD) (fileVersion>>16) & 0xFFFF; + DWORD v2 = (DWORD) (fileVersion>>32) & 0xFFFF; + DWORD v1 = (DWORD) (fileVersion>>48) & 0xFFFF; + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s', fileVersion: %d.%d.%d.%d\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName, v1, v2, v3, v4); + } + if(OutputModules & m_options) OnOutput(buffer); +} + + +//----------------------------------------------------------------------------------------------------------------------------------------- +//GetModuleInfo +//----------------------------------------------------------------------------------------------------------------------------------------- +bool StackWalker::GetModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULE64 *pModuleInfo) +{ + + pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64); + void *pData = malloc(4096); // reserve enough memory, so the bug in v6.3.5.1 does not lead to memory-overwrites... + if (pData == NULL) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return false; + } + memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64)); + if (SymGetModuleInfo64(hProcess, baseAddr, (IMAGEHLP_MODULE64*) pData) != FALSE) + { + // only copy as much memory as is reserved... + memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64)); + pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64); + free(pData); + return true; + } + free(pData); + SetLastError(ERROR_DLL_INIT_FAILED); + return false; +} + + +//----------------------------------------------------------------------------------------------------------------------------------------- +//GetModuleListTH32 +//----------------------------------------------------------------------------------------------------------------------------------------- +bool StackWalker::GetModuleListTH32(HANDLE hProcess, DWORD pid) +{ + // CreateToolhelp32Snapshot() + typedef HANDLE (__stdcall *tCT32S)(DWORD dwFlags, DWORD th32ProcessID); + // Module32First() + typedef BOOL (__stdcall *tM32F)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); + // Module32Next() + typedef BOOL (__stdcall *tM32N)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); + + // try both dlls... + const char *dllname[] = { "kernel32.dll", "tlhelp32.dll" }; + HINSTANCE hToolhelp = NULL; + tCT32S pCT32S = NULL; + tM32F pM32F = NULL; + tM32N pM32N = NULL; + + HANDLE hSnap; + MODULEENTRY32 me; + me.dwSize = sizeof(me); + BOOL keepGoing; + size_t i; + + for (i = 0; i<(sizeof(dllname) / sizeof(dllname[0])); i++ ) + { + hToolhelp = LoadLibraryA( dllname[i] ); + if (hToolhelp == NULL) + continue; + pCT32S = (tCT32S) GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot"); + pM32F = (tM32F) GetProcAddress(hToolhelp, "Module32First"); + pM32N = (tM32N) GetProcAddress(hToolhelp, "Module32Next"); + if ( (pCT32S != NULL) && (pM32F != NULL) && (pM32N != NULL) ) + break; // found the functions! + FreeLibrary(hToolhelp); + hToolhelp = NULL; + } + + if (hToolhelp == NULL) + return false; + + hSnap = pCT32S( TH32CS_SNAPMODULE, pid ); + if (hSnap == (HANDLE) -1) + return false; + + keepGoing = !!pM32F( hSnap, &me ); + int cnt = 0; + while (keepGoing) + { + this->LoadModule(hProcess, me.szExePath, me.szModule, (DWORD64) me.modBaseAddr, me.modBaseSize); + cnt++; + keepGoing = !!pM32N( hSnap, &me ); + } + CloseHandle(hSnap); + FreeLibrary(hToolhelp); + if (cnt <= 0) return false; + + return true; +} + + +//----------------------------------------------------------------------------------------------------------------------------------------- +//GetModuleListPSAPI +//----------------------------------------------------------------------------------------------------------------------------------------- +bool StackWalker::GetModuleListPSAPI(HANDLE hProcess) +{ + DWORD i; + //ModuleEntry e; + DWORD cbNeeded; + MODULEINFO mi; + HMODULE *hMods = 0; + char *tt = NULL; + char *tt2 = NULL; + const SIZE_T TTBUFLEN = 8096; + int cnt = 0; + + hMods = (HMODULE*) malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof HMODULE)); + tt = (char*) malloc(sizeof(char) * TTBUFLEN); + tt2 = (char*) malloc(sizeof(char) * TTBUFLEN); + if ( (hMods == NULL) || (tt == NULL) || (tt2 == NULL) ) + goto cleanup; + + if ( !EnumProcessModules(hProcess, hMods, TTBUFLEN, &cbNeeded) ) + { + //_ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle ); + goto cleanup; + } + + if ( cbNeeded > TTBUFLEN ) + { + //_ftprintf(fLogFile, _T("%lu: More than %lu module handles. Huh?\n"), g_dwShowCount, lenof( hMods ) ); + goto cleanup; + } + + for ( i = 0; i < cbNeeded / sizeof hMods[0]; i++ ) + { + // base address, size + GetModuleInformation(hProcess, hMods[i], &mi, sizeof mi ); + // image file name + tt[0] = 0; + GetModuleFileNameExA(hProcess, hMods[i], tt, TTBUFLEN ); + // module name + tt2[0] = 0; + GetModuleBaseNameA(hProcess, hMods[i], tt2, TTBUFLEN ); + + DWORD dwRes = this->LoadModule(hProcess, tt, tt2, (DWORD64) mi.lpBaseOfDll, mi.SizeOfImage); + if (dwRes != ERROR_SUCCESS) + this->OnDbgHelpErr("LoadModule", dwRes, 0); + cnt++; + } + +cleanup: + if (tt2 != NULL) free(tt2); + if (tt != NULL) free(tt); + if (hMods != NULL) free(hMods); + + return cnt != 0; +} + + +//----------------------------------------------------------------------------------------------------------------------------------------- +//OnCallstackEntry +//----------------------------------------------------------------------------------------------------------------------------------------- +void StackWalker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry) +{ + CHAR buffer[STACKWALK_MAX_NAMELEN]; + if ( (eType != lastEntry) && (entry.offset != 0) ) + { + if (entry.name[0] == 0) strcpy_s(entry.name, "(function-name not available)"); + if (entry.undName[0] != 0) strcpy_s(entry.name, entry.undName); + if (entry.undFullName[0] != 0) strcpy_s(entry.name, entry.undFullName); + if (entry.lineFileName[0] == 0) + { + strcpy_s(entry.lineFileName, "(filename not available)"); + if (entry.moduleName[0] == 0) strcpy_s(entry.moduleName, "(module-name not available)"); + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%p (%s): %s: %s\n", (LPVOID) entry.offset, entry.moduleName, entry.lineFileName, entry.name); + } + else + { + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s (%d): %s\n", entry.lineFileName, entry.lineNumber, entry.name); + } + OnOutput(buffer); + } +} + + +//----------------------------------------------------------------------------------------------------------------------------------------- +//OnDbgHelpErr +//----------------------------------------------------------------------------------------------------------------------------------------- +void StackWalker::OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) +{ + CHAR buffer[STACKWALK_MAX_NAMELEN]; + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "ERROR: %s, GetLastError: %d (Address: %p)\n", szFuncName, gle, (LPVOID) addr); + OnOutput(buffer); +} + + +//----------------------------------------------------------------------------------------------------------------------------------------- +//OnSymInit +//----------------------------------------------------------------------------------------------------------------------------------------- +void StackWalker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName) +{ + //Symbol Search path + CHAR buffer[STACKWALK_MAX_NAMELEN]; + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n", szSearchPath, symOptions, szUserName); + if(OutputSymPath & m_options) OnOutput(buffer); + + //OS-version + OSVERSIONINFOEX ver; + ZeroMemory(&ver, sizeof(OSVERSIONINFOEX)); + ver.dwOSVersionInfoSize = sizeof(ver); + if (GetVersionEx( (OSVERSIONINFO*) &ver) != FALSE) + { + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "OS-Version: %d.%d.%d (%s) 0x%x-0x%x\n", + ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber, + ver.szCSDVersion, ver.wSuiteMask, ver.wProductType); + if(OutputOS & m_options) OnOutput(buffer); + } +} + +#endif + diff --git a/Engine/source/platformWin32/minidump/winStackWalker.h b/Engine/source/platformWin32/minidump/winStackWalker.h new file mode 100644 index 000000000..d39219de4 --- /dev/null +++ b/Engine/source/platformWin32/minidump/winStackWalker.h @@ -0,0 +1,102 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef __WIN_PLATFORM_STACKWALKER__ +#define __WIN_PLATFORM_STACKWALKER__ + +#if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE ) + +#include +#include + +class StackWalker +{ +public: + + typedef enum StackWalkOptions + { + // RetrieveNone = 0, // No additional info will be retrieved (only the address is available) + // RetrieveSymbol = 1, // Try to get the symbol-name + // RetrieveLine = 2, // Try to get the line for this symbol + // RetrieveModuleInfo = 4, // Try to retrieve the module-infos + RetrieveFileVersion = 8, // Also retrieve the version for the DLL/EXE + RetrieveVerbose = 0xF, // Contains all of the above retrieve options + + SymBuildPath = 0x10, // Generate a "good" symbol-search-path + SymUseSymSrv = 0x20, // Also use a public Symbol Server + SymAll = 0x30, // Contains all of the above Symbol options + + OutputSymPath = 0x80, //print out the symbol path + OutputOS = 0x100, //print out the OS path + OutputModules = 0x200, //print out the Modules + + OptionsDefault = 0x3F, // Less verbose output (default) + OptionsAll = 0x2FF // Contains all options + }; + + StackWalker(DWORD optionFlags = OptionsDefault, LPCSTR szSymPath = NULL); + virtual ~StackWalker(); + + typedef BOOL (__stdcall *PReadProcessMemoryRoutine)(HANDLE hProcess, + DWORD64 qwBaseAddress, + PVOID lpBuffer, + DWORD nSize, + LPDWORD lpNumberOfBytesRead, + LPVOID pUserData // optional data, which was passed in "ShowCallstack" + ); + + // pUserData is optional to identify some data in the 'readMemoryFunction'-callback + bool ShowCallstack(HANDLE hThread, CONTEXT const & Context, PReadProcessMemoryRoutine readMemoryFunction = NULL, LPVOID pUserData = NULL); + + void setOutputBuffer(char * buffer); + +private: + typedef enum CallstackEntryType {firstEntry, nextEntry, lastEntry}; + + HANDLE m_hProcess; + DWORD m_dwProcessId; + bool m_modulesLoaded; + LPSTR m_szSymPath; + int m_options; + char * m_pOutputBuffer; + + static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead); + + bool Init(LPCSTR szSymPath); + + virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName); + virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion); + virtual void OnCallstackEntry(CallstackEntryType eType, struct CallstackEntry &entry); + virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr); + virtual void OnOutput(LPCSTR szText); + + bool LoadModules(); + DWORD LoadModule(HANDLE hProcess, LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size); + bool GetModuleListTH32(HANDLE hProcess, DWORD pid); + bool GetModuleListPSAPI(HANDLE hProcess); + bool GetModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULE64 *pModuleInfo); +}; + +void dGetStackTrace(char * traceBuffer, CONTEXT const & ContextRecord); + +#endif +#endif \ No newline at end of file diff --git a/Engine/source/platformWin32/nativeDialogs/fileDialog.cpp b/Engine/source/platformWin32/nativeDialogs/fileDialog.cpp new file mode 100644 index 000000000..5dce1a87f --- /dev/null +++ b/Engine/source/platformWin32/nativeDialogs/fileDialog.cpp @@ -0,0 +1,948 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/simBase.h" +#include "platform/nativeDialogs/fileDialog.h" +#include "platform/threads/mutex.h" +#include "platformWin32/platformWin32.h" +#include "core/util/safeDelete.h" +#include "math/mMath.h" +#include "core/strings/unicode.h" +#include "console/consoleTypes.h" +#include "platform/profiler.h" +#include +#include +#include "console/engineAPI.h" + +#ifdef TORQUE_TOOLS +//----------------------------------------------------------------------------- +// PlatformFileDlgData Implementation +//----------------------------------------------------------------------------- +FileDialogData::FileDialogData() +{ + // Default Path + // + // Try to provide consistent experience by recalling the last file path + // - else + // Default to Working Directory if last path is not set or is invalid + mDefaultPath = StringTable->insert( Con::getVariable("Tools::FileDialogs::LastFilePath") ); + if( mDefaultPath == StringTable->lookup("") || !Platform::isDirectory( mDefaultPath ) ) + mDefaultPath = Platform::getCurrentDirectory(); + + mDefaultFile = StringTable->insert(""); + mFilters = StringTable->insert(""); + mFile = StringTable->insert(""); + mTitle = StringTable->insert(""); + + mStyle = 0; + +} +FileDialogData::~FileDialogData() +{ + +} + +static LRESULT PASCAL OKBtnFolderHackProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + WNDPROC oldProc = (WNDPROC)GetProp(hWnd, dT("OldWndProc")); + + switch(uMsg) + { + case WM_COMMAND: + if(LOWORD(wParam) == IDOK) + { + LPOPENFILENAME ofn = (LPOPENFILENAME)GetProp(hWnd, dT("OFN")); + if(ofn == NULL) + break; + + SendMessage(hWnd, CDM_GETFILEPATH, ofn->nMaxFile, (LPARAM)ofn->lpstrFile); + + char *filePath; +#ifdef UNICODE + char fileBuf[MAX_PATH]; + convertUTF16toUTF8(ofn->lpstrFile, fileBuf, sizeof(fileBuf)); + filePath = fileBuf; +#else + filePath = ofn->lpstrFile; +#endif + + if(Platform::isDirectory(filePath)) + { + // Got a directory + EndDialog(hWnd, IDOK); + } + } + break; + } + + if(oldProc) + return CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam); + else + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + +static UINT_PTR CALLBACK FolderHookProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam){ + HWND hParent = GetParent(hdlg); + + switch(uMsg) + { + case WM_INITDIALOG: + { + LPOPENFILENAME lpofn = (LPOPENFILENAME)lParam; + + SendMessage(hParent, CDM_SETCONTROLTEXT, stc3, (LPARAM)dT("Folder name:")); + SendMessage(hParent, CDM_HIDECONTROL, cmb1, 0); + SendMessage(hParent, CDM_HIDECONTROL, stc2, 0); + + LONG oldProc = SetWindowLong(hParent, GWL_WNDPROC, (LONG)OKBtnFolderHackProc); + SetProp(hParent, dT("OldWndProc"), (HANDLE)oldProc); + SetProp(hParent, dT("OFN"), (HANDLE)lpofn); + } + break; + + case WM_NOTIFY: + { + LPNMHDR nmhdr = (LPNMHDR)lParam; + switch(nmhdr->code) + { + case CDN_FOLDERCHANGE: + { + LPOFNOTIFY lpofn = (LPOFNOTIFY)lParam; + + OpenFolderDialog *ofd = (OpenFolderDialog *)lpofn->lpOFN->lCustData; + +#ifdef UNICODE + UTF16 buf[MAX_PATH]; +#else + char buf[MAX_PATH]; +#endif + + SendMessage(hParent, CDM_GETFOLDERPATH, sizeof(buf), (LPARAM)buf); + + char filePath[MAX_PATH]; +#ifdef UNICODE + convertUTF16toUTF8(buf, filePath, sizeof(filePath)); +#else + dStrcpy( filePath, buf ); +#endif + + // [tom, 12/8/2006] Hack to remove files from the list because + // CDN_INCLUDEITEM doesn't work for regular files and folders. + HWND shellView = GetDlgItem(hParent, lst2); + HWND listView = FindWindowEx(shellView, 0, WC_LISTVIEW, NULL); + if(listView) + { + S32 count = ListView_GetItemCount(listView); + for(S32 i = count - 1;i >= 0;--i) + { + ListView_GetItemText(listView, i, 0, buf, sizeof(buf)); + +#ifdef UNICODE + char buf2[MAX_PATH]; + convertUTF16toUTF8(buf, buf2, sizeof(buf2)); +#else + char *buf2 = buf; +#endif + char full[MAX_PATH]; + dSprintf(full, sizeof(full), "%s\\%s", filePath, buf2); + + if(!Platform::isDirectory(full)) + { + ListView_DeleteItem(listView, i); + } + } + } + + if(ofd->mMustExistInDir == NULL || *ofd->mMustExistInDir == 0) + break; + + HWND hOK = GetDlgItem(hParent, IDOK); + if(hOK == NULL) + break; + + char checkPath[MAX_PATH]; + dSprintf(checkPath, sizeof(checkPath), "%s\\%s", filePath, ofd->mMustExistInDir); + + EnableWindow(hOK, Platform::isFile(checkPath)); + } + break; + } + } + break; + } + return 0; +} + +//----------------------------------------------------------------------------- +// FileDialog Implementation +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(FileDialog); + +ConsoleDocClass( FileDialog, + "@brief Base class responsible for displaying an OS file browser.\n\n" + + "FileDialog is a platform agnostic dialog interface for querying the user for " + "file locations. It is designed to be used through the exposed scripting interface.\n\n" + + "FileDialog is the base class for Native File Dialog controls in Torque. It provides these basic areas of functionality:\n\n" + " - Inherits from SimObject and is exposed to the scripting interface\n" + " - Provides blocking interface to allow instant return to script execution\n" + " - Simple object configuration makes practical use easy and effective\n\n" + + "FileDialog is *NOT* intended to be used directly in script and is only exposed to script to expose generic file dialog attributes.\n\n" + + "This base class is usable in TorqueScript, but is does not specify what functionality is intended (open or save?). " + "Its children, OpenFileDialog and SaveFileDialog, do make use of DialogStyle flags and do make use of specific funcationality. " + "These are the preferred classes to use\n\n" + + "However, the FileDialog base class does contain the key properties and important method for file browing. The most " + "important function is Execute(). This is used by both SaveFileDialog and OpenFileDialog to initiate the browser.\n\n" + + "@tsexample\n" + "// NOTE: This is not he preferred class to use, but this still works\n\n" + "// Create the file dialog\n" + "%baseFileDialog = new FileDialog()\n" + "{\n" + " // Allow browsing of all file types\n" + " filters = \"*.*\";\n\n" + " // No default file\n" + " defaultFile = "";\n\n" + " // Set default path relative to project\n" + " defaultPath = \"./\";\n\n" + " // Set the title\n" + " title = \"Durpa\";\n\n" + " // Allow changing of path you are browsing\n" + " changePath = true;\n" + "};\n\n" + " // Launch the file dialog\n" + " %baseFileDialog.Execute();\n" + " \n" + " // Don't forget to cleanup\n" + " %baseFileDialog.delete();\n\n\n" + "@endtsexample\n\n" + + "@note FileDialog and its related classes are only availble in a Tools build of Torque.\n\n" + + "@see OpenFileDialog for a practical example on opening a file\n" + "@see SaveFileDialog for a practical example of saving a file\n" + + "@ingroup FileSystem\n" +); + +FileDialog::FileDialog() : mData() +{ + // Default to File Must Exist Open Dialog style + mData.mStyle = FileDialogData::FDS_OPEN | FileDialogData::FDS_MUSTEXIST; + mChangePath = false; +} + +FileDialog::~FileDialog() +{ +} + +void FileDialog::initPersistFields() +{ + addProtectedField( "defaultPath", TypeString, Offset(mData.mDefaultPath, FileDialog), &setDefaultPath, &defaultProtectedGetFn, + "The default directory path when the dialog is shown." ); + + addProtectedField( "defaultFile", TypeString, Offset(mData.mDefaultFile, FileDialog), &setDefaultFile, &defaultProtectedGetFn, + "The default file path when the dialog is shown." ); + + addProtectedField( "fileName", TypeString, Offset(mData.mFile, FileDialog), &setFile, &defaultProtectedGetFn, + "The default file name when the dialog is shown." ); + + addProtectedField( "filters", TypeString, Offset(mData.mFilters, FileDialog), &setFilters, &defaultProtectedGetFn, + "The filter string for limiting the types of files visible in the dialog. It makes use of the pipe symbol '|' " + "as a delimiter. For example:\n\n" + "'All Files|*.*'\n\n" + "'Image Files|*.png;*.jpg|Png Files|*.png|Jepg Files|*.jpg'" ); + + addField( "title", TypeString, Offset(mData.mTitle, FileDialog), + "The title for the dialog." ); + + addProtectedField( "changePath", TypeBool, Offset(mChangePath, FileDialog), &setChangePath, &getChangePath, + "True/False whether to set the working directory to the directory returned by the dialog." ); + + Parent::initPersistFields(); +} + +static const U32 convertUTF16toUTF8DoubleNULL( const UTF16 *unistring, UTF8 *outbuffer, U32 len) +{ + AssertFatal(len >= 1, "Buffer for unicode conversion must be large enough to hold at least the null terminator."); + PROFILE_START(convertUTF16toUTF8DoubleNULL); + U32 walked, nCodeunits, codeunitLen; + UTF32 middleman; + + nCodeunits=0; + while( ! (*unistring == '\0' && *(unistring + 1) == '\0') && nCodeunits + 3 < len ) + { + walked = 1; + middleman = oneUTF16toUTF32(unistring,&walked); + codeunitLen = oneUTF32toUTF8(middleman, &outbuffer[nCodeunits]); + unistring += walked; + nCodeunits += codeunitLen; + } + + nCodeunits = getMin(nCodeunits,len - 1); + outbuffer[nCodeunits] = '\0'; + outbuffer[nCodeunits+1] = '\0'; + + PROFILE_END(); + return nCodeunits; +} + +// +// Execute Method +// +bool FileDialog::Execute() +{ + static char pszResult[MAX_PATH]; +#ifdef UNICODE + UTF16 pszFile[MAX_PATH]; + UTF16 pszInitialDir[MAX_PATH]; + UTF16 pszTitle[MAX_PATH]; + UTF16 pszFilter[1024]; + UTF16 pszFileTitle[MAX_PATH]; + UTF16 pszDefaultExtension[MAX_PATH]; + // Convert parameters to UTF16*'s + convertUTF8toUTF16((UTF8 *)mData.mDefaultFile, pszFile, sizeof(pszFile)); + convertUTF8toUTF16((UTF8 *)mData.mDefaultPath, pszInitialDir, sizeof(pszInitialDir)); + convertUTF8toUTF16((UTF8 *)mData.mTitle, pszTitle, sizeof(pszTitle)); + convertUTF8toUTF16((UTF8 *)mData.mFilters, pszFilter, sizeof(pszFilter) ); +#else + // Not Unicode, All char*'s! + char pszFile[MAX_PATH]; + char pszFilter[1024]; + char pszFileTitle[MAX_PATH]; + dStrcpy( pszFile, mData.mDefaultFile ); + dStrcpy( pszFilter, mData.mFilters ); + const char* pszInitialDir = mData.mDefaultPath; + const char* pszTitle = mData.mTitle; + +#endif + + pszFileTitle[0] = 0; + + // Convert Filters + U32 filterLen = dStrlen( pszFilter ); + S32 dotIndex = -1; + for( U32 i = 0; i < filterLen; i++ ) + { + if( pszFilter[i] == '|' ) + pszFilter[i] = '\0'; + + if( pszFilter[ i ] == '.' && dotIndex == -1 ) + dotIndex = i; + } + // Add second NULL terminator at the end + pszFilter[ filterLen + 1 ] = '\0'; + + // Get default extension. + dMemset( pszDefaultExtension, 0, sizeof( pszDefaultExtension ) ); + if( dotIndex != -1 ) + { + for( U32 i = 0; i < MAX_PATH; ++ i ) + { + UTF16 ch = pszFilter[ dotIndex + 1 + i ]; + if( !ch || ch == ';' || ch == '|' || dIsspace( ch ) ) + break; + + pszDefaultExtension[ i ] = ch; + } + } + + OPENFILENAME ofn; + dMemset(&ofn, 0, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = getWin32WindowHandle(); + ofn.lpstrFile = pszFile; + + + if( !dStrncmp( mData.mDefaultFile, "", 1 ) ) + ofn.lpstrFile[0] = '\0'; + + + ofn.nMaxFile = sizeof(pszFile); + ofn.lpstrFilter = pszFilter; + ofn.nFilterIndex = 1; + ofn.lpstrInitialDir = pszInitialDir; + ofn.lCustData = (LPARAM)this; + ofn.lpstrFileTitle = pszFileTitle; + ofn.nMaxFileTitle = sizeof(pszFileTitle); + ofn.lpstrDefExt = pszDefaultExtension[ 0 ] ? pszDefaultExtension : NULL; + + if( mData.mTitle != StringTable->lookup("") ) + ofn.lpstrTitle = pszTitle; + + // Build Proper Flags. + ofn.Flags = OFN_EXPLORER | OFN_ENABLESIZING | OFN_HIDEREADONLY; + + if(mData.mStyle & FileDialogData::FDS_BROWSEFOLDER) + { + ofn.lpfnHook = FolderHookProc; + ofn.Flags |= OFN_ENABLEHOOK; + } + + if( !(mData.mStyle & FileDialogData::FDS_CHANGEPATH) ) + ofn.Flags |= OFN_NOCHANGEDIR; + + if( mData.mStyle & FileDialogData::FDS_MUSTEXIST ) + ofn.Flags |= OFN_FILEMUSTEXIST; + + if( mData.mStyle & FileDialogData::FDS_OPEN && mData.mStyle & FileDialogData::FDS_MULTIPLEFILES ) + ofn.Flags |= OFN_ALLOWMULTISELECT; + + if( mData.mStyle & FileDialogData::FDS_OVERWRITEPROMPT ) + ofn.Flags |= OFN_OVERWRITEPROMPT; + + + // Flag we're showing file browser so we can do some render hacking + winState.renderThreadBlocked = true; + + // Get the current working directory, so we can back up to it once Windows has + // done its craziness and messed with it. + StringTableEntry cwd = Platform::getCurrentDirectory(); + + // Execute Dialog (Blocking Call) + bool dialogSuccess = false; + if( mData.mStyle & FileDialogData::FDS_OPEN ) + dialogSuccess = GetOpenFileName(&ofn); + else if( mData.mStyle & FileDialogData::FDS_SAVE ) + dialogSuccess = GetSaveFileName(&ofn); + + // Dialog is gone. + winState.renderThreadBlocked = false; + + // Restore the working directory. + Platform::setCurrentDirectory( cwd ); + + // Did we select a file? + if( !dialogSuccess ) + return false; + + // Handle Result Properly for Unicode as well as ANSI +#ifdef UNICODE + if(pszFileTitle[0] || ! ( mData.mStyle & FileDialogData::FDS_OPEN && mData.mStyle & FileDialogData::FDS_MULTIPLEFILES )) + convertUTF16toUTF8( (UTF16*)pszFile, (UTF8*)pszResult, sizeof(pszResult)); + else + convertUTF16toUTF8DoubleNULL( (UTF16*)pszFile, (UTF8*)pszResult, sizeof(pszResult)); +#else + if(pszFileTitle[0] || ! ( mData.mStyle & FileDialogData::FDS_OPEN && mData.mStyle & FileDialogData::FDS_MULTIPLEFILES )) + dStrcpy(pszResult,pszFile); + else + { + // [tom, 1/4/2007] pszResult is a double-NULL terminated, NULL separated list in this case so we can't just dSstrcpy() + char *sptr = pszFile, *dptr = pszResult; + while(! (*sptr == 0 && *(sptr+1) == 0)) + *dptr++ = *sptr++; + *dptr++ = 0; + } +#endif + + forwardslash(pszResult); + + // [tom, 1/5/2007] Windows is ridiculously dumb. If you select a single file in a multiple + // select file dialog then it will return the file the same way as it would in a single + // select dialog. The only difference is pszFileTitle is empty if multiple files + // are selected. + + // Store the result on our object + if( mData.mStyle & FileDialogData::FDS_BROWSEFOLDER || ( pszFileTitle[0] && ! ( mData.mStyle & FileDialogData::FDS_OPEN && mData.mStyle & FileDialogData::FDS_MULTIPLEFILES ))) + { + // Single file selection, do it the easy way + mData.mFile = StringTable->insert( pszResult ); + } + else if(pszFileTitle[0] && ( mData.mStyle & FileDialogData::FDS_OPEN && mData.mStyle & FileDialogData::FDS_MULTIPLEFILES )) + { + // Single file selection in a multiple file selection dialog + setDataField(StringTable->insert("files"), "0", pszResult); + setDataField(StringTable->insert("fileCount"), NULL, "1"); + } + else + { + // Multiple file selection, break out into an array + S32 numFiles = 0; + const char *dir = pszResult; + const char *file = dir + dStrlen(dir) + 1; + char buffer[1024]; + + while(*file) + { + Platform::makeFullPathName(file, buffer, sizeof(buffer), dir); + setDataField(StringTable->insert("files"), Con::getIntArg(numFiles++), buffer); + + file = file + dStrlen(file) + 1; + } + + setDataField(StringTable->insert("fileCount"), NULL, Con::getIntArg(numFiles)); + } + + // Return success. + return true; + +} +DefineEngineMethod( FileDialog, Execute, bool, (),, + "@brief Launches the OS file browser\n\n" + + "After an Execute() call, the chosen file name and path is available in one of two areas. " + "If only a single file selection is permitted, the results will be stored in the @a fileName " + "attribute.\n\n" + + "If multiple file selection is permitted, the results will be stored in the " + "@a files array. The total number of files in the array will be stored in the " + "@a fileCount attribute.\n\n" + + "@tsexample\n" + "// NOTE: This is not he preferred class to use, but this still works\n\n" + "// Create the file dialog\n" + "%baseFileDialog = new FileDialog()\n" + "{\n" + " // Allow browsing of all file types\n" + " filters = \"*.*\";\n\n" + " // No default file\n" + " defaultFile = "";\n\n" + " // Set default path relative to project\n" + " defaultPath = \"./\";\n\n" + " // Set the title\n" + " title = \"Durpa\";\n\n" + " // Allow changing of path you are browsing\n" + " changePath = true;\n" + "};\n\n" + " // Launch the file dialog\n" + " %baseFileDialog.Execute();\n" + " \n" + " // Don't forget to cleanup\n" + " %baseFileDialog.delete();\n\n\n" + + " // A better alternative is to use the \n" + " // derived classes which are specific to file open and save\n\n" + " // Create a dialog dedicated to opening files\n" + " %openFileDlg = new OpenFileDialog()\n" + " {\n" + " // Look for jpg image files\n" + " // First part is the descriptor|second part is the extension\n" + " Filters = \"Jepg Files|*.jpg\";\n" + " // Allow browsing through other folders\n" + " ChangePath = true;\n\n" + " // Only allow opening of one file at a time\n" + " MultipleFiles = false;\n" + " };\n\n" + " // Launch the open file dialog\n" + " %result = %openFileDlg.Execute();\n\n" + " // Obtain the chosen file name and path\n" + " if ( %result )\n" + " {\n" + " %seletedFile = %openFileDlg.file;\n" + " }\n" + " else\n" + " {\n" + " %selectedFile = \"\";\n" + " }\n" + " // Cleanup\n" + " %openFileDlg.delete();\n\n\n" + + " // Create a dialog dedicated to saving a file\n" + " %saveFileDlg = new SaveFileDialog()\n" + " {\n" + " // Only allow for saving of COLLADA files\n" + " Filters = \"COLLADA Files (*.dae)|*.dae|\";\n\n" + " // Default save path to where the WorldEditor last saved\n" + " DefaultPath = $pref::WorldEditor::LastPath;\n\n" + " // No default file specified\n" + " DefaultFile = \"\";\n\n" + " // Do not allow the user to change to a new directory\n" + " ChangePath = false;\n\n" + " // Prompt the user if they are going to overwrite an existing file\n" + " OverwritePrompt = true;\n" + " };\n\n" + " // Launch the save file dialog\n" + " %result = %saveFileDlg.Execute();\n\n" + " // Obtain the file name\n" + " %selectedFile = \"\";\n" + " if ( %result )\n" + " %selectedFile = %saveFileDlg.file;\n\n" + " // Cleanup\n" + " %saveFileDlg.delete();\n" + "@endtsexample\n\n" + + "@return True if the file was selected was successfully found (opened) or declared (saved).") +{ + return object->Execute(); +} + +//----------------------------------------------------------------------------- +// Dialog Filters +//----------------------------------------------------------------------------- +bool FileDialog::setFilters( void *object, const char *index, const char *data ) +{ + // Will do validate on write at some point. + if( !data ) + return true; + + return true; + +}; + + +//----------------------------------------------------------------------------- +// Default Path Property - String Validated on Write +//----------------------------------------------------------------------------- +bool FileDialog::setDefaultPath( void *object, const char *index, const char *data ) +{ + + if( !data || !dStrncmp( data, "", 1 ) ) + return true; + + // Copy and Backslash the path (Windows dialogs are VERY picky about this format) + static char szPathValidate[512]; + dStrcpy( szPathValidate, data ); + + Platform::makeFullPathName( data,szPathValidate, sizeof(szPathValidate)); + backslash( szPathValidate ); + + // Remove any trailing \'s + S8 validateLen = dStrlen( szPathValidate ); + if( szPathValidate[ validateLen - 1 ] == '\\' ) + szPathValidate[ validateLen - 1 ] = '\0'; + + // Now check + if( Platform::isDirectory( szPathValidate ) ) + { + // Finally, assign in proper format. + FileDialog *pDlg = static_cast( object ); + pDlg->mData.mDefaultPath = StringTable->insert( szPathValidate ); + } +#ifdef TORQUE_DEBUG + else + Con::errorf(ConsoleLogEntry::GUI, "FileDialog - Invalid Default Path Specified!"); +#endif + + return false; + +}; + +//----------------------------------------------------------------------------- +// Default File Property - String Validated on Write +//----------------------------------------------------------------------------- +bool FileDialog::setDefaultFile( void *object, const char *index, const char *data ) +{ + if( !data || !dStrncmp( data, "", 1 ) ) + return true; + + // Copy and Backslash the path (Windows dialogs are VERY picky about this format) + static char szPathValidate[512]; + Platform::makeFullPathName( data,szPathValidate, sizeof(szPathValidate) ); + backslash( szPathValidate ); + + // Remove any trailing \'s + S8 validateLen = dStrlen( szPathValidate ); + if( szPathValidate[ validateLen - 1 ] == '\\' ) + szPathValidate[ validateLen - 1 ] = '\0'; + + // Finally, assign in proper format. + FileDialog *pDlg = static_cast( object ); + pDlg->mData.mDefaultFile = StringTable->insert( szPathValidate ); + + return false; +}; + +//----------------------------------------------------------------------------- +// ChangePath Property - Change working path on successful file selection +//----------------------------------------------------------------------------- +bool FileDialog::setChangePath( void *object, const char *index, const char *data ) +{ + bool bMustExist = dAtob( data ); + + FileDialog *pDlg = static_cast( object ); + + if( bMustExist ) + pDlg->mData.mStyle |= FileDialogData::FDS_CHANGEPATH; + else + pDlg->mData.mStyle &= ~FileDialogData::FDS_CHANGEPATH; + + return true; +}; + +const char* FileDialog::getChangePath(void* obj, const char* data) +{ + FileDialog *pDlg = static_cast( obj ); + if( pDlg->mData.mStyle & FileDialogData::FDS_CHANGEPATH ) + return StringTable->insert("true"); + else + return StringTable->insert("false"); +} + +bool FileDialog::setFile( void *object, const char *index, const char *data ) +{ + return false; +}; + +//----------------------------------------------------------------------------- +// OpenFileDialog Implementation +//----------------------------------------------------------------------------- + +ConsoleDocClass( OpenFileDialog, + "@brief Derived from FileDialog, this class is responsible for opening a file browser with the intention of opening a file.\n\n" + + "The core usage of this dialog is to locate a file in the OS and return the path and name. This does not handle " + "the actual file parsing or data manipulation. That functionality is left up to the FileObject class.\n\n" + + "@tsexample\n" + " // Create a dialog dedicated to opening files\n" + " %openFileDlg = new OpenFileDialog()\n" + " {\n" + " // Look for jpg image files\n" + " // First part is the descriptor|second part is the extension\n" + " Filters = \"Jepg Files|*.jpg\";\n" + " // Allow browsing through other folders\n" + " ChangePath = true;\n\n" + " // Only allow opening of one file at a time\n" + " MultipleFiles = false;\n" + " };\n\n" + " // Launch the open file dialog\n" + " %result = %openFileDlg.Execute();\n\n" + " // Obtain the chosen file name and path\n" + " if ( %result )\n" + " {\n" + " %seletedFile = %openFileDlg.file;\n" + " }\n" + " else\n" + " {\n" + " %selectedFile = \"\";\n" + " }\n\n" + " // Cleanup\n" + " %openFileDlg.delete();\n\n\n" + "@endtsexample\n\n" + + "@note FileDialog and its related classes are only availble in a Tools build of Torque.\n\n" + + "@see FileDialog\n" + "@see SaveFileDialog\n" + "@see FileObject\n" + + "@ingroup FileSystem\n" +); +OpenFileDialog::OpenFileDialog() +{ + // Default File Must Exist + mData.mStyle = FileDialogData::FDS_OPEN | FileDialogData::FDS_MUSTEXIST; +} + +OpenFileDialog::~OpenFileDialog() +{ + mMustExist = true; + mMultipleFiles = false; +} + +IMPLEMENT_CONOBJECT(OpenFileDialog); + +//----------------------------------------------------------------------------- +// Console Properties +//----------------------------------------------------------------------------- +void OpenFileDialog::initPersistFields() +{ + addProtectedField("MustExist", TypeBool, Offset(mMustExist, OpenFileDialog), &setMustExist, &getMustExist, "True/False whether the file returned must exist or not" ); + addProtectedField("MultipleFiles", TypeBool, Offset(mMultipleFiles, OpenFileDialog), &setMultipleFiles, &getMultipleFiles, "True/False whether multiple files may be selected and returned or not" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- +// File Must Exist - Boolean +//----------------------------------------------------------------------------- +bool OpenFileDialog::setMustExist( void *object, const char *index, const char *data ) +{ + bool bMustExist = dAtob( data ); + + OpenFileDialog *pDlg = static_cast( object ); + + if( bMustExist ) + pDlg->mData.mStyle |= FileDialogData::FDS_MUSTEXIST; + else + pDlg->mData.mStyle &= ~FileDialogData::FDS_MUSTEXIST; + + return true; +}; + +const char* OpenFileDialog::getMustExist(void* obj, const char* data) +{ + OpenFileDialog *pDlg = static_cast( obj ); + if( pDlg->mData.mStyle & FileDialogData::FDS_MUSTEXIST ) + return StringTable->insert("true"); + else + return StringTable->insert("false"); +} + +//----------------------------------------------------------------------------- +// Can Select Multiple Files - Boolean +//----------------------------------------------------------------------------- +bool OpenFileDialog::setMultipleFiles( void *object, const char *index, const char *data ) +{ + bool bMustExist = dAtob( data ); + + OpenFileDialog *pDlg = static_cast( object ); + + if( bMustExist ) + pDlg->mData.mStyle |= FileDialogData::FDS_MULTIPLEFILES; + else + pDlg->mData.mStyle &= ~FileDialogData::FDS_MULTIPLEFILES; + + return true; +}; + +const char* OpenFileDialog::getMultipleFiles(void* obj, const char* data) +{ + OpenFileDialog *pDlg = static_cast( obj ); + if( pDlg->mData.mStyle & FileDialogData::FDS_MULTIPLEFILES ) + return StringTable->insert("true"); + else + return StringTable->insert("false"); +} + +//----------------------------------------------------------------------------- +// SaveFileDialog Implementation +//----------------------------------------------------------------------------- +ConsoleDocClass( SaveFileDialog, + "@brief Derived from FileDialog, this class is responsible for opening a file browser with the intention of saving a file.\n\n" + + "The core usage of this dialog is to locate a file in the OS and return the path and name. This does not handle " + "the actual file writing or data manipulation. That functionality is left up to the FileObject class.\n\n" + + "@tsexample\n" + " // Create a dialog dedicated to opening file\n" + " %saveFileDlg = new SaveFileDialog()\n" + " {\n" + " // Only allow for saving of COLLADA files\n" + " Filters = \"COLLADA Files (*.dae)|*.dae|\";\n\n" + " // Default save path to where the WorldEditor last saved\n" + " DefaultPath = $pref::WorldEditor::LastPath;\n\n" + " // No default file specified\n" + " DefaultFile = \"\";\n\n" + " // Do not allow the user to change to a new directory\n" + " ChangePath = false;\n\n" + " // Prompt the user if they are going to overwrite an existing file\n" + " OverwritePrompt = true;\n" + " };\n\n" + " // Launch the save file dialog\n" + " %saveFileDlg.Execute();\n\n" + " if ( %result )\n" + " {\n" + " %seletedFile = %openFileDlg.file;\n" + " }\n" + " else\n" + " {\n" + " %selectedFile = \"\";\n" + " }\n\n" + " // Cleanup\n" + " %saveFileDlg.delete();\n" + "@endtsexample\n\n" + + "@note FileDialog and its related classes are only availble in a Tools build of Torque.\n\n" + + "@see FileDialog\n" + "@see OpenFileDialog\n" + "@see FileObject\n" + + "@ingroup FileSystem\n" +); +SaveFileDialog::SaveFileDialog() +{ + // Default File Must Exist + mData.mStyle = FileDialogData::FDS_SAVE | FileDialogData::FDS_OVERWRITEPROMPT; + mOverwritePrompt = true; +} + +SaveFileDialog::~SaveFileDialog() +{ +} + +IMPLEMENT_CONOBJECT(SaveFileDialog); + +//----------------------------------------------------------------------------- +// Console Properties +//----------------------------------------------------------------------------- +void SaveFileDialog::initPersistFields() +{ + addProtectedField("OverwritePrompt", TypeBool, Offset(mOverwritePrompt, SaveFileDialog), &setOverwritePrompt, &getOverwritePrompt, "True/False whether the dialog should prompt before accepting an existing file name" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- +// Prompt on Overwrite - Boolean +//----------------------------------------------------------------------------- +bool SaveFileDialog::setOverwritePrompt( void *object, const char *index, const char *data ) +{ + bool bMustExist = dAtob( data ); + + SaveFileDialog *pDlg = static_cast( object ); + + if( bMustExist ) + pDlg->mData.mStyle |= FileDialogData::FDS_OVERWRITEPROMPT; + else + pDlg->mData.mStyle &= ~FileDialogData::FDS_OVERWRITEPROMPT; + + return true; +}; + +const char* SaveFileDialog::getOverwritePrompt(void* obj, const char* data) +{ + SaveFileDialog *pDlg = static_cast( obj ); + if( pDlg->mData.mStyle & FileDialogData::FDS_OVERWRITEPROMPT ) + return StringTable->insert("true"); + else + return StringTable->insert("false"); +} + +//----------------------------------------------------------------------------- +// OpenFolderDialog Implementation +//----------------------------------------------------------------------------- + +OpenFolderDialog::OpenFolderDialog() +{ + mData.mStyle = FileDialogData::FDS_OPEN | FileDialogData::FDS_OVERWRITEPROMPT | FileDialogData::FDS_BROWSEFOLDER; + + mMustExistInDir = ""; +} + +IMPLEMENT_CONOBJECT(OpenFolderDialog); + +ConsoleDocClass( OpenFolderDialog, + "@brief OS level dialog used for browsing folder structures.\n\n" + + "This is essentially an OpenFileDialog, but only used for returning directory paths, not files.\n\n" + + "@note FileDialog and its related classes are only availble in a Tools build of Torque.\n\n" + + "@see OpenFileDialog for more details on functionality.\n\n" + + "@ingroup FileSystem\n" +); + +void OpenFolderDialog::initPersistFields() +{ + addField("fileMustExist", TypeFilename, Offset(mMustExistInDir, OpenFolderDialog), "File that must be in selected folder for it to be valid"); + + Parent::initPersistFields(); +} + +#endif \ No newline at end of file diff --git a/Engine/source/platformWin32/nativeDialogs/win32MsgBox.cpp b/Engine/source/platformWin32/nativeDialogs/win32MsgBox.cpp new file mode 100644 index 000000000..0d4820acb --- /dev/null +++ b/Engine/source/platformWin32/nativeDialogs/win32MsgBox.cpp @@ -0,0 +1,129 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "platformWin32/platformWin32.h" +#include "platform/platformInput.h" +#include "platform/nativeDialogs/msgBox.h" + +#include "console/console.h" +#include "core/strings/unicode.h" + +#include "windowManager/platformWindowMgr.h" +#include "windowManager/win32/win32Window.h" + +struct _FlagMap +{ + S32 num; + U32 flag; +}; + +static _FlagMap sgButtonMap[] = +{ + { MBOk, MB_OK }, + { MBOkCancel, MB_OKCANCEL }, + { MBRetryCancel, MB_RETRYCANCEL }, + { MBSaveDontSave, MB_YESNO }, + { MBSaveDontSaveCancel, MB_YESNOCANCEL }, + { 0xffffffff, 0xffffffff } +}; + +static _FlagMap sgIconMap[] = +{ + { MIWarning, MB_ICONWARNING }, + { MIInformation, MB_ICONINFORMATION }, + { MIQuestion, MB_ICONQUESTION }, + { MIStop, MB_ICONSTOP }, + { 0xffffffff, 0xffffffff } +}; + +static _FlagMap sgMsgBoxRetMap[] = +{ + { IDCANCEL, MRCancel }, + { IDNO, MRDontSave }, + { IDOK, MROk}, + { IDRETRY, MRRetry }, + { IDYES, MROk }, + { 0xffffffff, 0xffffffff } +}; + +//----------------------------------------------------------------------------- + +static U32 getMaskFromID(_FlagMap *map, S32 id) +{ + for(S32 i = 0;map[i].num != 0xffffffff && map[i].flag != 0xffffffff;++i) + { + if(map[i].num == id) + return map[i].flag; + } + + return 0; +} + +//----------------------------------------------------------------------------- + +S32 Platform::messageBox(const UTF8 *title, const UTF8 *message, MBButtons buttons, MBIcons icon) +{ + PlatformWindow *pWindow = WindowManager->getFirstWindow(); + + // Get us rendering while we're blocking. + winState.renderThreadBlocked = true; + + // We don't keep a locked mouse or else we're going + // to end up possibly locking our mouse out of the + // message box area + bool cursorLocked = pWindow && pWindow->isMouseLocked(); + if( cursorLocked ) + pWindow->setMouseLocked( false ); + + // Need a visible cursor to click stuff accurately + bool cursorVisible = !pWindow || pWindow->isCursorVisible(); + if( !cursorVisible ) + pWindow->setCursorVisible(true); + +#ifdef UNICODE + const UTF16 *msg = convertUTF8toUTF16(message); + const UTF16 *t = convertUTF8toUTF16(title); +#else + const UTF8 *msg = message; + const UTF8 *t = title; +#endif + + HWND parent = pWindow ? static_cast(pWindow)->getHWND() : NULL; + S32 ret = ::MessageBox( parent, msg, t, getMaskFromID(sgButtonMap, buttons) | getMaskFromID(sgIconMap, icon)); + +#ifdef UNICODE + delete [] msg; + delete [] t; +#endif + + // Dialog is gone. + winState.renderThreadBlocked = false; + + if( cursorVisible == false ) + pWindow->setCursorVisible( false ); + + if( cursorLocked == true ) + pWindow->setMouseLocked( true ); + + return getMaskFromID(sgMsgBoxRetMap, ret); +} diff --git a/Engine/source/platformWin32/platformWin32.h b/Engine/source/platformWin32/platformWin32.h new file mode 100644 index 000000000..ba29ee334 --- /dev/null +++ b/Engine/source/platformWin32/platformWin32.h @@ -0,0 +1,138 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PLATFORMWIN32_H_ +#define _PLATFORMWIN32_H_ + +// Sanity check for UNICODE +#ifdef TORQUE_UNICODE +# ifndef UNICODE +# error "ERROR: You must have UNICODE defined in your preprocessor settings (ie, /DUNICODE) if you have TORQUE_UNICODE enabled in torqueConfig.h!" +# endif +#endif + +#include +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#ifndef _MRECT_H_ +#include "math/mRect.h" +#endif + +#if defined(TORQUE_COMPILER_CODEWARRIOR) +# include +# include +# include +#else +# include +# include +#endif + +#ifdef _MSC_VER +#pragma warning(disable: 4996) // turn off "deprecation" warnings +#endif + +#define NOMINMAX + +// Hack to get a correct HWND instead of using global state. +extern HWND getWin32WindowHandle(); + +struct Win32PlatState +{ + FILE *log_fp; + HINSTANCE hinstOpenGL; + HINSTANCE hinstGLU; + HINSTANCE hinstOpenAL; + HWND appWindow; + HDC appDC; + HINSTANCE appInstance; + HGLRC hGLRC; + DWORD processId; + bool renderThreadBlocked; + S32 nMessagesPerFrame; ///< The max number of messages to dispatch per frame + HMENU appMenu; ///< The menu bar for the window +#ifdef UNICODE + //HIMC imeHandle; +#endif + + S32 desktopBitsPixel; + S32 desktopWidth; + S32 desktopHeight; + S32 desktopClientWidth; + S32 desktopClientHeight; + U32 currentTime; + + // minimum time per frame + U32 sleepTicks; + // are we in the background? + bool backgrounded; + + Win32PlatState(); +}; + +extern Win32PlatState winState; + +extern void setModifierKeys( S32 modKeys ); + +//-------------------------------------- Helper Functions + +template< typename T > +inline void forwardslashT( T *str ) +{ + while(*str) + { + if(*str == '\\') + *str = '/'; + str++; + } +} + +inline void forwardslash( char* str ) +{ + forwardslashT< char >( str ); +} +inline void forwardslash( WCHAR* str ) +{ + forwardslashT< WCHAR >( str ); +} + +template< typename T > +inline void backslashT( T *str ) +{ + while(*str) + { + if(*str == '/') + *str = '\\'; + str++; + } +} + +inline void backslash( char* str ) +{ + backslashT< char >( str ); +} +inline void backslash( WCHAR* str ) +{ + backslashT< WCHAR >( str ); +} + +#endif //_PLATFORMWIN32_H_ diff --git a/Engine/source/platformWin32/threads/mutex.cpp b/Engine/source/platformWin32/threads/mutex.cpp new file mode 100644 index 000000000..4c8e75cec --- /dev/null +++ b/Engine/source/platformWin32/threads/mutex.cpp @@ -0,0 +1,74 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/threads/mutex.h" +#include "platformWin32/platformWin32.h" +#include "core/util/safeDelete.h" + +//----------------------------------------------------------------------------- +// Mutex Data +//----------------------------------------------------------------------------- + +struct PlatformMutexData +{ + CRITICAL_SECTION mCriticalSection; +}; + +//----------------------------------------------------------------------------- +// Constructor/Destructor +//----------------------------------------------------------------------------- + +Mutex::Mutex() +{ + mData = new PlatformMutexData; + InitializeCriticalSection( &mData->mCriticalSection ); +} + +Mutex::~Mutex() +{ + AssertFatal( TryEnterCriticalSection( &mData->mCriticalSection ), "Mutex::~Mutex - Critical section is locked!" ); + DeleteCriticalSection( &mData->mCriticalSection ); + SAFE_DELETE( mData ); +} + +//----------------------------------------------------------------------------- +// Public Methods +//----------------------------------------------------------------------------- + +bool Mutex::lock( bool block ) +{ + AssertFatal( mData, "Mutex::lock - No data!" ); + + if( !block ) + return TryEnterCriticalSection( &mData->mCriticalSection ); + else + { + EnterCriticalSection( &mData->mCriticalSection ); + return true; + } +} + +void Mutex::unlock() +{ + AssertFatal( mData, "Mutex::unlock - No data!" ); + LeaveCriticalSection( &mData->mCriticalSection ); +} diff --git a/Engine/source/platformWin32/threads/thread.cpp b/Engine/source/platformWin32/threads/thread.cpp new file mode 100644 index 000000000..6e66ddc6d --- /dev/null +++ b/Engine/source/platformWin32/threads/thread.cpp @@ -0,0 +1,208 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef TORQUE_OS_XENON +#include "platformWin32/platformWin32.h" +#endif +#include "platform/threads/thread.h" +#include "platform/threads/semaphore.h" +#include "platform/platformIntrinsics.h" +#include "core/util/safeDelete.h" + +#include // [tom, 4/20/2006] for _beginthread() + +ThreadManager::MainThreadId ThreadManager::smMainThreadId; + +//----------------------------------------------------------------------------- +// Thread data +//----------------------------------------------------------------------------- + +class PlatformThreadData +{ +public: + ThreadRunFunction mRunFunc; + void* mRunArg; + Thread* mThread; + HANDLE mThreadHnd; + Semaphore mGateway; + U32 mThreadID; + U32 mDead; + + PlatformThreadData() + { + mRunFunc = NULL; + mRunArg = 0; + mThread = 0; + mThreadHnd = 0; + mDead = false; + }; +}; + +//----------------------------------------------------------------------------- +// Static Functions/Methods +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Function: ThreadRunHandler +// Summary: Calls Thread::run() with the thread's specified run argument. +// Neccesary because Thread::run() is provided as a non-threaded +// way to execute the thread's run function. So we have to keep +// track of the thread's lock here. +static unsigned int __stdcall ThreadRunHandler(void * arg) +{ + PlatformThreadData* mData = reinterpret_cast(arg); + mData->mThreadID = GetCurrentThreadId(); + + ThreadManager::addThread(mData->mThread); + mData->mThread->run(mData->mRunArg); + ThreadManager::removeThread(mData->mThread); + + bool autoDelete = mData->mThread->autoDelete; + + mData->mThreadHnd = NULL; // mark as dead + dCompareAndSwap( mData->mDead, false, true ); + mData->mGateway.release(); // don't access data after this. + + if( autoDelete ) + delete mData->mThread; // Safe as we own the data. + + _endthreadex( 0 ); + return 0; +} + +//----------------------------------------------------------------------------- +// Constructor/Destructor +//----------------------------------------------------------------------------- + +Thread::Thread(ThreadRunFunction func /* = 0 */, void *arg /* = 0 */, bool start_thread /* = true */, bool autodelete /*= false*/) + : autoDelete( autodelete ) +{ + AssertFatal( !start_thread, "Thread::Thread() - auto-starting threads from ctor has been disallowed since the run() method is virtual" ); + + mData = new PlatformThreadData; + mData->mRunFunc = func; + mData->mRunArg = arg; + mData->mThread = this; +} + +Thread::~Thread() +{ + stop(); + if( isAlive() ) + join(); + + SAFE_DELETE(mData); +} + +//----------------------------------------------------------------------------- +// Public Methods +//----------------------------------------------------------------------------- + +void Thread::start( void* arg ) +{ + AssertFatal( !mData->mThreadHnd, + "Thread::start() - thread already started" ); + + // cause start to block out other pthreads from using this Thread, + // at least until ThreadRunHandler exits. + mData->mGateway.acquire(); + + // reset the shouldStop flag, so we'll know when someone asks us to stop. + shouldStop = false; + + mData->mDead = false; + + if( !mData->mRunArg ) + mData->mRunArg = arg; + + mData->mThreadHnd = (HANDLE)_beginthreadex(0, 0, ThreadRunHandler, mData, 0, 0); +} + +bool Thread::join() +{ + mData->mGateway.acquire(); + AssertFatal( !isAlive(), "Thread::join() - thread still alive after join" ); + mData->mGateway.release(); // release for further joins + return true; +} + +void Thread::run(void *arg /* = 0 */) +{ + if(mData->mRunFunc) + mData->mRunFunc(arg); +} + +bool Thread::isAlive() +{ + return ( !mData->mDead ); +} + +U32 Thread::getId() +{ + return mData->mThreadID; +} + +void Thread::_setName( const char* name ) +{ +#if defined( TORQUE_DEBUG ) && defined( TORQUE_COMPILER_VISUALC ) && defined( TORQUE_OS_WIN32 ) + + // See http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx + + #define MS_VC_EXCEPTION 0x406D1388 + + #pragma pack(push,8) + typedef struct tagTHREADNAME_INFO + { + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. + } THREADNAME_INFO; + #pragma pack(pop) + + Sleep(10); + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = name; + info.dwThreadID = getId(); + info.dwFlags = 0; + + __try + { + RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info ); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + } +#endif +} + +U32 ThreadManager::getCurrentThreadId() +{ + return GetCurrentThreadId(); +} + +bool ThreadManager::compare(U32 threadId_1, U32 threadId_2) +{ + return (threadId_1 == threadId_2); +} diff --git a/Engine/source/platformWin32/videoInfo/wmiVideoInfo.cpp b/Engine/source/platformWin32/videoInfo/wmiVideoInfo.cpp new file mode 100644 index 000000000..24cfb816c --- /dev/null +++ b/Engine/source/platformWin32/videoInfo/wmiVideoInfo.cpp @@ -0,0 +1,579 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#define _WIN32_DCOM + +//#include +#include +//#include +#pragma comment(lib, "comsuppw.lib") +#pragma comment(lib, "wbemuuid.lib") + +#include "platformWin32/videoInfo/wmiVideoInfo.h" +#include "core/util/safeRelease.h" +#include "console/console.h" + +// http://www.spectranaut.net/sourcecode/WMI.cpp + +// Add constructor to GUID. +struct MYGUID : public GUID +{ + MYGUID( DWORD a, SHORT b, SHORT c, BYTE d, BYTE e, BYTE f, BYTE g, BYTE h, BYTE i, BYTE j, BYTE k ) + { + Data1 = a; + Data2 = b; + Data3 = c; + Data4[ 0 ] = d; + Data4[ 1 ] = e; + Data4[ 2 ] = f; + Data4[ 3 ] = g; + Data4[ 4 ] = h; + Data4[ 5 ] = i; + Data4[ 6 ] = j; + Data4[ 7 ] = k; + } +}; + +//------------------------------------------------------------------------------ +// DXGI decls for retrieving device info on Vista. We manually declare that +// stuff here, so we don't depend on headers and compile on any setup. At +// run-time, it depends on whether we can successfully load the DXGI DLL; if +// not, nothing of this here will be used. + +struct IDXGIObject; +struct IDXGIFactory; +struct IDXGIAdapter; +struct IDXGIOutput; + +struct DXGI_SWAP_CHAIN_DESC; +struct DXGI_ADAPTER_DESC; + +struct IDXGIObject : public IUnknown +{ + virtual HRESULT STDMETHODCALLTYPE SetPrivateData( REFGUID, UINT, const void* ) = 0; + virtual HRESULT STDMETHODCALLTYPE SetPrivateDataInterface( REFGUID, const IUnknown* ) = 0; + virtual HRESULT STDMETHODCALLTYPE GetPrivateData( REFGUID, UINT*, void* ) = 0; + virtual HRESULT STDMETHODCALLTYPE GetParent( REFIID, void** ) = 0; +}; + +struct IDXGIFactory : public IDXGIObject +{ + virtual HRESULT STDMETHODCALLTYPE EnumAdapters( UINT, IDXGIAdapter** ) = 0; + virtual HRESULT STDMETHODCALLTYPE MakeWindowAssociation( HWND, UINT ) = 0; + virtual HRESULT STDMETHODCALLTYPE GetWindowAssociation( HWND ) = 0; + virtual HRESULT STDMETHODCALLTYPE CreateSwapChain( IUnknown*, DXGI_SWAP_CHAIN_DESC* ) = 0; + virtual HRESULT STDMETHODCALLTYPE CreateSoftwareAdapter( HMODULE, IDXGIAdapter** ) = 0; +}; + +struct IDXGIAdapter : public IDXGIObject +{ + virtual HRESULT STDMETHODCALLTYPE EnumOutputs( UINT, IDXGIOutput** ) = 0; + virtual HRESULT STDMETHODCALLTYPE GetDesc( DXGI_ADAPTER_DESC* ) = 0; + virtual HRESULT STDMETHODCALLTYPE CheckInterfaceSupport( REFGUID, LARGE_INTEGER* ) = 0; +}; + +struct DXGI_ADAPTER_DESC +{ + WCHAR Description[ 128 ]; + UINT VendorId; + UINT DeviceId; + UINT SubSysId; + UINT Revision; + SIZE_T DedicatedVideoMemory; + SIZE_T DedicatedSystemMemory; + SIZE_T SharedSystemMemory; + LUID AdapterLuid; +}; + +static MYGUID IID_IDXGIFactory( 0x7b7166ec, 0x21c7, 0x44ae, 0xb2, 0x1a, 0xc9, 0xae, 0x32, 0x1a, 0xe3, 0x69 ); + +//------------------------------------------------------------------------------ +// DXDIAG declarations. + +struct DXDIAG_INIT_PARAMS +{ + DWORD dwSize; + DWORD dwDxDiagHeaderVersion; + BOOL bAllowWHQLChecks; + LPVOID pReserved; +}; + +struct IDxDiagContainer : public IUnknown +{ + virtual HRESULT STDMETHODCALLTYPE GetNumberOfChildContaiiners( DWORD* pdwCount ) = 0; + virtual HRESULT STDMETHODCALLTYPE EnumChildContainerNames( DWORD dwIndex, LPWSTR pwszContainer, DWORD cchContainer ) = 0; + virtual HRESULT STDMETHODCALLTYPE GetChildContainer( LPCWSTR pwszContainer, IDxDiagContainer** ppInstance ) = 0; + virtual HRESULT STDMETHODCALLTYPE GetNumberOfProps( DWORD* pdwCount ) = 0; + virtual HRESULT STDMETHODCALLTYPE EnumPropNames( DWORD dwIndex, LPWSTR pwszPropName, DWORD cchPropName ) = 0; + virtual HRESULT STDMETHODCALLTYPE GetProp( LPCWSTR pwszPropName, VARIANT* pvarProp ) = 0; +}; + +struct IDxDiagProvider : public IUnknown +{ + virtual HRESULT STDMETHODCALLTYPE Initialize( DXDIAG_INIT_PARAMS* pParams ) = 0; + virtual HRESULT STDMETHODCALLTYPE GetRootContainer( IDxDiagContainer** ppInstance ) = 0; +}; + +static MYGUID CLSID_DxDiagProvider( 0xA65B8071, 0x3BFE, 0x4213, 0x9A, 0x5B, 0x49, 0x1D, 0xA4, 0x46, 0x1C, 0xA7 ); +static MYGUID IID_IDxDiagProvider( 0x9C6B4CB0, 0x23F8, 0x49CC, 0xA3, 0xED, 0x45, 0xA5, 0x50, 0x00, 0xA6, 0xD2 ); +static MYGUID IID_IDxDiagContainer( 0x7D0F462F, 0x4064, 0x4862, 0xBC, 0x7F, 0x93, 0x3E, 0x50, 0x58, 0xC1, 0x0F ); + +//------------------------------------------------------------------------------ + +WCHAR *WMIVideoInfo::smPVIQueryTypeToWMIString [] = +{ + L"MaxNumberControlled", //PVI_NumDevices + L"Description", //PVI_Description + L"Name", //PVI_Name + L"VideoProcessor", //PVI_ChipSet + L"DriverVersion", //PVI_DriverVersion + L"AdapterRAM", //PVI_VRAM +}; + +//------------------------------------------------------------------------------ + +WMIVideoInfo::WMIVideoInfo() + : PlatformVideoInfo(), + mLocator( NULL ), + mServices( NULL ), + mComInitialized( false ), + mDXGIModule( NULL ), + mDXGIFactory( NULL ), + mDxDiagProvider( NULL ) +{ + +} + +//------------------------------------------------------------------------------ + +WMIVideoInfo::~WMIVideoInfo() +{ + SAFE_RELEASE( mLocator ); + SAFE_RELEASE( mServices ); + + if( mDxDiagProvider ) + SAFE_RELEASE( mDxDiagProvider ); + + if( mDXGIFactory ) + SAFE_RELEASE( mDXGIFactory ); + if( mDXGIModule ) + FreeLibrary( ( HMODULE ) mDXGIModule ); + + if( mComInitialized ) + CoUninitialize(); +} + +//------------------------------------------------------------------------------ + +bool WMIVideoInfo::_initialize() +{ + // Init COM + HRESULT hr = CoInitialize( NULL ); + mComInitialized = SUCCEEDED( hr ); + + if( !mComInitialized ) + return false; + + bool success = false; + + success |= _initializeDXGI(); + success |= _initializeDxDiag(); + success |= _initializeWMI(); + + return success; +} + +bool WMIVideoInfo::_initializeWMI() +{ + //// Set security levels + //hr = CoInitializeSecurity( + // NULL, + // -1, // COM authentication + // NULL, // Authentication services + // NULL, // Reserved + // RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication + // RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation + // NULL, // Authentication info + // EOAC_NONE, // Additional capabilities + // NULL // Reserved + // ); + + //if( FAILED( hr ) ) + //{ + // Con::errorf( "WMIVideoInfo: Failed to initialize com security." ); + // return false; + //} + + // Obtain the locator to WMI + HRESULT hr = CoCreateInstance( + CLSID_WbemLocator, + 0, + CLSCTX_INPROC_SERVER, + IID_IWbemLocator, + (void**)&mLocator + ); + + if( FAILED( hr ) ) + { + Con::errorf( "WMIVideoInfo: Failed to create instance of IID_IWbemLocator." ); + return false; + } + + // Connect to the root\cimv2 namespace with + // the current user and obtain pointer pSvc + // to make IWbemServices calls. + hr = mLocator->ConnectServer( + BSTR(L"ROOT\\CIMV2"), // Object path of WMI namespace + NULL, // User name. NULL = current user + NULL, // User password. NULL = current + 0, // Locale. NULL indicates current + NULL, // Security flags. + 0, // Authority (e.g. Kerberos) + 0, // Context object + &mServices // pointer to IWbemServices proxy + ); + + if( FAILED( hr ) ) + { + Con::errorf( "WMIVideoInfo: Connect server failed." ); + return false; + } + + + // Set security levels on the proxy + hr = CoSetProxyBlanket( + mServices, // Indicates the proxy to set + RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx + RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx + NULL, // Server principal name + RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx + RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx + NULL, // client identity + EOAC_NONE // proxy capabilities + ); + + if( FAILED( hr ) ) + { + Con::errorf( "WMIVideoInfo: CoSetProxyBlanket failed" ); + return false; + } + + return true; +} + +bool WMIVideoInfo::_initializeDXGI() +{ + // Try going for DXGI. Will only succeed on Vista. +#if 0 + mDXGIModule = ( HMODULE ) LoadLibrary( L"dxgi.dll" ); + if( mDXGIModule != 0 ) + { + typedef HRESULT (* CreateDXGIFactoryFuncType )( REFIID, void** ); + CreateDXGIFactoryFuncType factoryFunction = + ( CreateDXGIFactoryFuncType ) GetProcAddress( ( HMODULE ) mDXGIModule, "CreateDXGIFactory" ); + + if( factoryFunction && factoryFunction( IID_IDXGIFactory, ( void** ) &mDXGIFactory ) == S_OK ) + return true; + else + { + FreeLibrary( ( HMODULE ) mDXGIModule ); + mDXGIModule = 0; + } + } +#endif + return false; +} + +bool WMIVideoInfo::_initializeDxDiag() +{ + if( CoCreateInstance( CLSID_DxDiagProvider, NULL, CLSCTX_INPROC_SERVER, IID_IDxDiagProvider, ( void** ) &mDxDiagProvider ) == S_OK ) + { + DXDIAG_INIT_PARAMS params; + dMemset( ¶ms, 0, sizeof( DXDIAG_INIT_PARAMS ) ); + + params.dwSize = sizeof( DXDIAG_INIT_PARAMS ); + params.dwDxDiagHeaderVersion = 111; + params.bAllowWHQLChecks = false; + + HRESULT result = mDxDiagProvider->Initialize( ¶ms ); + if( result != S_OK ) + { + Con::errorf( "WMIVideoInfo: DxDiag initialization failed (%i)", result ); + SAFE_RELEASE( mDxDiagProvider ); + return false; + } + else + { + Con::printf( "WMIVideoInfo: DxDiag initialized" ); + return true; + } + } + + return false; +} + +//------------------------------------------------------------------------------ +// http://msdn2.microsoft.com/en-us/library/aa394512.aspx +// +// The Win32_VideoController WMI class represents the capabilities and management capacity of the +// video controller on a computer system running Windows. +// +// Starting with Windows Vista, hardware that is not compatible with Windows Display Driver Model (WDDM) +// returns inaccurate property values for instances of this class. +// +// Windows Server 2003, Windows XP, Windows 2000, and Windows NT 4.0: This class is supported. +//------------------------------------------------------------------------------ + +bool WMIVideoInfo::_queryProperty( const PVIQueryType queryType, const U32 adapterId, String *outValue ) +{ + if( _queryPropertyDXGI( queryType, adapterId, outValue ) ) + return true; + else if( _queryPropertyDxDiag( queryType, adapterId, outValue ) ) + return true; + else + return _queryPropertyWMI( queryType, adapterId, outValue ); +} + +bool WMIVideoInfo::_queryPropertyDxDiag( const PVIQueryType queryType, const U32 adapterId, String *outValue ) +{ + if( mDxDiagProvider != 0 ) + { + IDxDiagContainer* rootContainer = 0; + IDxDiagContainer* displayDevicesContainer = 0; + IDxDiagContainer* deviceContainer = 0; + + WCHAR adapterIdString[ 2 ]; + adapterIdString[ 0 ] = L'0' + adapterId; + adapterIdString[ 1 ] = L'\0'; + + String value; + if( mDxDiagProvider->GetRootContainer( &rootContainer ) == S_OK + && rootContainer->GetChildContainer( L"DxDiag_DisplayDevices", &displayDevicesContainer ) == S_OK + && displayDevicesContainer->GetChildContainer( adapterIdString, &deviceContainer ) == S_OK ) + { + const WCHAR* propertyName = 0; + + switch( queryType ) + { + case PVI_Description: + propertyName = L"szDescription"; + break; + + case PVI_Name: + propertyName = L"szDeviceName"; + break; + + case PVI_ChipSet: + propertyName = L"szChipType"; + break; + + case PVI_DriverVersion: + propertyName = L"szDriverVersion"; + break; + + // Don't get VRAM via DxDiag as that won't tell us about the actual amount of dedicated + // video memory but rather some dedicated+shared RAM value. + } + + if( propertyName ) + { + VARIANT val; + if( deviceContainer->GetProp( propertyName, &val ) == S_OK ) + switch( val.vt ) + { + case VT_BSTR: + value = String( val.bstrVal ); + break; + + default: + AssertWarn( false, avar( "WMIVideoInfo: property type '%i' not implemented", val.vt ) ); + } + } + } + + if( rootContainer ) + SAFE_RELEASE( rootContainer ); + if( displayDevicesContainer ) + SAFE_RELEASE( displayDevicesContainer ); + if( deviceContainer ) + SAFE_RELEASE( deviceContainer ); + + if( value.isNotEmpty() ) + { + // Try to get the DxDiag data into some canonical form. Otherwise, we + // won't be giving the card profiler much opportunity for matching up + // its data with profile scripts. + + switch( queryType ) + { + case PVI_ChipSet: + if( value.compare( "ATI", 3, String::NoCase ) == 0 ) + value = "ATI Technologies Inc."; + else if( value.compare( "NVIDIA", 6, String::NoCase ) == 0 ) + value = "NVIDIA"; + else if( value.compare( "INTEL", 5, String::NoCase ) == 0 ) + value = "INTEL"; + else if( value.compare( "MATROX", 6, String::NoCase ) == 0 ) + value = "MATROX"; + break; + + case PVI_Description: + if( value.compare( "ATI ", 4, String::NoCase ) == 0 ) + { + value = value.substr( 4, value.length() - 4 ); + if( value.compare( " Series", 7, String::NoCase | String::Right ) == 0 ) + value = value.substr( 0, value.length() - 7 ); + } + else if( value.compare( "NVIDIA ", 7, String::NoCase ) == 0 ) + value = value.substr( 7, value.length() - 7 ); + else if( value.compare( "INTEL ", 6, String::NoCase ) == 0 ) + value = value.substr( 6, value.length() - 6 ); + else if( value.compare( "MATROX ", 7, String::NoCase ) == 0 ) + value = value.substr( 7, value.length() - 7 ); + break; + } + + *outValue = value; + return true; + } + } + return false; +} + +bool WMIVideoInfo::_queryPropertyDXGI( const PVIQueryType queryType, const U32 adapterId, String *outValue ) +{ +#if 0 + if( mDXGIFactory ) + { + IDXGIAdapter* adapter; + if( mDXGIFactory->EnumAdapters( adapterId, &adapter ) != S_OK ) + return false; + + DXGI_ADAPTER_DESC desc; + if( adapter->GetDesc( &desc ) != S_OK ) + { + adapter->Release(); + return false; + } + + String value; + switch( queryType ) + { + case PVI_Description: + value = String( desc.Description ); + break; + + case PVI_Name: + value = String( avar( "%i", desc.DeviceId ) ); + break; + + case PVI_VRAM: + value = String( avar( "%i", desc.DedicatedVideoMemory / 1048576 ) ); + break; + + //RDTODO + } + + adapter->Release(); + *outValue = value; + return true; + } +#endif + return false; +} + +bool WMIVideoInfo::_queryPropertyWMI( const PVIQueryType queryType, const U32 adapterId, String *outValue ) +{ + if( mServices == NULL ) + return false; + + BSTR bstrWQL = SysAllocString(L"WQL"); + BSTR bstrPath = SysAllocString(L"select * from Win32_VideoController"); + IEnumWbemClassObject* enumerator; + + // Use the IWbemServices pointer to make requests of WMI + HRESULT hr = mServices->ExecQuery(bstrWQL, bstrPath, WBEM_FLAG_FORWARD_ONLY, NULL, &enumerator); + + if( FAILED( hr ) ) + return false; + + IWbemClassObject *adapter = NULL; + ULONG uReturned; + + // Get the appropriate adapter. + for ( S32 i = 0; i <= adapterId; i++ ) + { + hr = enumerator->Next(WBEM_INFINITE, 1, &adapter, &uReturned ); + + if ( FAILED( hr ) || uReturned == 0 ) + { + enumerator->Release(); + return false; + } + } + + // Now get the property + VARIANT v; + hr = adapter->Get( smPVIQueryTypeToWMIString[queryType], 0, &v, NULL, NULL ); + + bool result = SUCCEEDED( hr ); + + if ( result ) + { + switch( v.vt ) + { + case VT_I4: + { + LONG longVal = v.lVal; + + if( queryType == PVI_VRAM ) + longVal = longVal >> 20; // Convert to megabytes + + *outValue = String::ToString( (S32)longVal ); + break; + } + + case VT_UI4: + { + *outValue = String::ToString( (U32)v.ulVal ); + break; + } + + case VT_BSTR: + { + *outValue = String( v.bstrVal ); + break; + } + case VT_LPSTR: + case VT_LPWSTR: + break; + } + + + } + + // Cleanup + adapter->Release(); + enumerator->Release(); + + return result; +} \ No newline at end of file diff --git a/Engine/source/platformWin32/videoInfo/wmiVideoInfo.h b/Engine/source/platformWin32/videoInfo/wmiVideoInfo.h new file mode 100644 index 000000000..6d5457039 --- /dev/null +++ b/Engine/source/platformWin32/videoInfo/wmiVideoInfo.h @@ -0,0 +1,63 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _WMI_CARDINFO_H_ +#define _WMI_CARDINFO_H_ + +#include "platform/platformVideoInfo.h" + +struct IWbemLocator; +struct IWbemServices; + +struct IDXGIFactory; +struct IDxDiagProvider; + +class WMIVideoInfo : public PlatformVideoInfo +{ +private: + IWbemLocator *mLocator; + IWbemServices *mServices; + bool mComInitialized; + + void* mDXGIModule; + IDXGIFactory* mDXGIFactory; + IDxDiagProvider* mDxDiagProvider; + + bool _initializeDXGI(); + bool _initializeDxDiag(); + bool _initializeWMI(); + + bool _queryPropertyDXGI( const PVIQueryType queryType, const U32 adapterId, String *outValue ); + bool _queryPropertyDxDiag( const PVIQueryType queryType, const U32 adapterId, String *outValue ); + bool _queryPropertyWMI( const PVIQueryType queryType, const U32 adapterId, String *outValue ); + +protected: + static WCHAR *smPVIQueryTypeToWMIString []; + bool _queryProperty( const PVIQueryType queryType, const U32 adapterId, String *outValue ); + bool _initialize(); + +public: + WMIVideoInfo(); + ~WMIVideoInfo(); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/platformWin32/winAsmBlit.cpp b/Engine/source/platformWin32/winAsmBlit.cpp new file mode 100644 index 000000000..8dd6dc413 --- /dev/null +++ b/Engine/source/platformWin32/winAsmBlit.cpp @@ -0,0 +1,207 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "math/mMath.h" +#include "gfx/bitmap/gBitmap.h" +#include "gfx/bitmap/bitmapUtils.h" + +#if !defined(__MWERKS__) && defined(_MSC_VER) +#define asm _asm +#endif + +//-------------------------------------------------------------------------- +void bitmapExtrude5551_asm(const void *srcMip, void *mip, U32 height, U32 width) +{ + const U16 *src = (const U16 *) srcMip; + U16 *dst = (U16 *) mip; + U32 stride = width << 1; + + for(U32 y = 0; y < height; y++) + { + for(U32 x = 0; x < width; x++) + { + U32 a = src[0]; + U32 b = src[1]; + U32 c = src[stride]; + U32 d = src[stride+1]; + dst[x] = ((((a >> 11) + (b >> 11) + (c >> 11) + (d >> 11)) >> 2) << 11) | + ((( ((a >> 6) & 0x1f) + ((b >> 6) & 0x1f) + ((c >> 6) & 0x1f) + ((d >> 6) & 0x1F) ) >> 2) << 6) | + ((( ((a >> 1) & 0x1F) + ((b >> 1) & 0x1F) + ((c >> 1) & 0x1f) + ((d >> 1) & 0x1f)) >> 2) << 1); + src += 2; + } + src += stride; + dst += width; + } +} + + +#if defined(TORQUE_SUPPORTS_VC_INLINE_X86_ASM) + +//-------------------------------------------------------------------------- +void bitmapExtrudeRGB_mmx(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth) +{ + if (srcHeight == 1 || srcWidth == 1) { + bitmapExtrudeRGB_c(srcMip, mip, srcHeight, srcWidth); + return; + } + + U32 width = srcWidth >> 1; + U32 height = srcHeight >> 1; + + if (width <= 1) + { + bitmapExtrudeRGB_c(srcMip, mip, srcHeight, srcWidth); + return; + } + + U64 ZERO = 0x0000000000000000; + const U8 *src = (const U8 *) srcMip; + U8 *dst = (U8 *) mip; + U32 srcStride = (width << 1) * 3; + U32 dstStride = width * 3; + + for(U32 y = 0; y < height; y++) + { + asm + { + mov eax, src + mov ebx, eax + add ebx, srcStride + mov ecx, dst + mov edx, width + + //-------------------------------------- + row_loop: + + punpcklbw mm0, [eax] + psrlw mm0, 8 + + punpcklbw mm1, [eax+3] + psrlw mm1, 8 + paddw mm0, mm1 + + punpcklbw mm1, [ebx] + psrlw mm1, 8 + paddw mm0, mm1 + + punpcklbw mm1, [ebx+3] + psrlw mm1, 8 + paddw mm0, mm1 + + psrlw mm0, 2 + //pxor mm1, mm1 + packuswb mm0, ZERO // mm1 + + movd [ecx], mm0 + add eax, 6 + add ebx, 6 + add ecx, 3 + dec edx + jnz row_loop + } + src += srcStride + srcStride; // advance to next line + dst += dstStride; + } + asm + { + emms + } +} + + +//-------------------------------------------------------------------------- +void bitmapConvertRGB_to_5551_mmx(U8 *src, U32 pixels) +{ + U64 MULFACT = 0x0008200000082000; // RGB quad word multiplier + U64 REDBLUE = 0x00f800f800f800f8; // Red-Blue mask + U64 GREEN = 0x0000f8000000f800; // Green mask + U64 ALPHA = 0x0000000000010001; // 100% Alpha mask + U64 ZERO = 0x0000000000000000; + + U32 evenPixels = pixels >> 1; // the MMX loop can only do an even number + U32 oddPixels = pixels & 1; // of pixels since it processes 2 at a time + + U16 *dst = (U16*)src; + + if (evenPixels) + { + asm + { + mov eax, src // YES, src = dst at start + mov ebx, dst // convert image in place + mov edx, evenPixels + + pixel_loop2: + movd mm0, [eax] // get first 24-bit pixel + movd mm1, [eax+3] // get second 24-bit pixel + punpckldq mm0, mm1 // put second in high dword + movq mm1, mm0 // save the original data + pand mm0, REDBLUE // mask out all but the 5MSBits of red and blue + pmaddwd mm0, MULFACT // multiply each word by + // 2**13, 2**3, 2**13, 2**3 and add results + pand mm1, GREEN // mask out all but the 5MSBits of green + por mm0, mm1 // combine the red, green, and blue bits + psrld mm0, 6 // shift into position + packssdw mm0, ZERO // pack into single dword + pslld mm0, 1 // shift into final position + por mm0, ALPHA // add the alpha bit + movd [ebx], mm0 + + add eax, 6 + add ebx, 4 + dec edx + jnz pixel_loop2 + + mov src, eax + mov dst, ebx + emms + } + } + + if (oddPixels) + { + U32 r = src[0] >> 3; + U32 g = src[1] >> 3; + U32 b = src[2] >> 3; + + *dst = (b << 1) | (g << 6) | (r << 11) | 1; + } +} + +#endif + + + +//-------------------------------------------------------------------------- +void PlatformBlitInit() +{ + bitmapExtrude5551 = bitmapExtrude5551_asm; + bitmapExtrudeRGB = bitmapExtrudeRGB_c; + + if (Platform::SystemInfo.processor.properties & CPU_PROP_MMX) + { +#if defined(TORQUE_SUPPORTS_VC_INLINE_X86_ASM) + bitmapExtrudeRGB = bitmapExtrudeRGB_mmx; + bitmapConvertRGB_to_5551 = bitmapConvertRGB_to_5551_mmx; +#endif + } +} diff --git a/Engine/source/platformWin32/winAsync.cpp b/Engine/source/platformWin32/winAsync.cpp new file mode 100644 index 000000000..0dc0d787c --- /dev/null +++ b/Engine/source/platformWin32/winAsync.cpp @@ -0,0 +1,84 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// For VS2005. +#define _WIN32_WINNT 0x501 +#ifndef TORQUE_OS_XENON +#include "platformWin32/platformWin32.h" +#endif +#include "platform/async/asyncUpdate.h" + + +AsyncUpdateThread::AsyncUpdateThread( String name, AsyncUpdateList* updateList ) + : Parent( 0, 0, false, false ), + mUpdateList( updateList ), + mName( name ) +{ + // Create an auto-reset event in non-signaled state. + mUpdateEvent = CreateEvent( NULL, false, false, NULL ); +} + +AsyncUpdateThread::~AsyncUpdateThread() +{ + CloseHandle( ( HANDLE ) mUpdateEvent ); +} + +void AsyncUpdateThread::_waitForEventAndReset() +{ + WaitForSingleObject( ( HANDLE ) mUpdateEvent, INFINITE ); +} + +void AsyncUpdateThread::triggerUpdate() +{ + SetEvent( ( HANDLE ) mUpdateEvent ); +} + +AsyncPeriodicUpdateThread::AsyncPeriodicUpdateThread( String name, + AsyncUpdateList* updateList, + U32 intervalMS ) + : Parent( name, updateList ) +{ + mUpdateTimer = CreateWaitableTimer( NULL, FALSE, NULL ); + + // This is a bit contrived. The 'dueTime' is in 100 nanosecond intervals + // and relative if it is negative. The period is in milliseconds. + + LARGE_INTEGER deltaTime; + deltaTime.QuadPart = - LONGLONG( intervalMS * 10 /* micro */ * 1000 /* milli */ ); + + SetWaitableTimer( ( HANDLE ) mUpdateTimer, &deltaTime, intervalMS, NULL, NULL, FALSE ); +} + +AsyncPeriodicUpdateThread::~AsyncPeriodicUpdateThread() +{ + CloseHandle( ( HANDLE ) mUpdateTimer ); +} + +void AsyncPeriodicUpdateThread::_waitForEventAndReset() +{ + HANDLE handles[ 2 ]; + + handles[ 0 ] = ( HANDLE ) mUpdateEvent; + handles[ 1 ] = ( HANDLE ) mUpdateTimer; + + WaitForMultipleObjects( 2, handles, FALSE, INFINITE ); +} diff --git a/Engine/source/platformWin32/winCPUInfo.cpp b/Engine/source/platformWin32/winCPUInfo.cpp new file mode 100644 index 000000000..e116a266e --- /dev/null +++ b/Engine/source/platformWin32/winCPUInfo.cpp @@ -0,0 +1,197 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "platformWin32/platformWin32.h" +#include "console/console.h" +#include "core/stringTable.h" +#include + +Platform::SystemInfo_struct Platform::SystemInfo; +extern void PlatformBlitInit(); +extern void SetProcessorInfo(Platform::SystemInfo_struct::Processor& pInfo, + char* vendor, U32 processor, U32 properties, U32 properties2); // platform/platformCPU.cc + + +#if defined(TORQUE_SUPPORTS_NASM) +// asm cpu detection routine from platform code +extern "C" +{ + void detectX86CPUInfo(char *vendor, U32 *processor, U32 *properties); +} +#endif + + +void Processor::init() +{ + // Reference: + // www.cyrix.com + // www.amd.com + // www.intel.com + // http://developer.intel.com/design/PentiumII/manuals/24512701.pdf + + Con::printf("Processor Init:"); + + Platform::SystemInfo.processor.type = CPU_X86Compatible; + Platform::SystemInfo.processor.name = StringTable->insert("Unknown x86 Compatible"); + Platform::SystemInfo.processor.mhz = 0; + Platform::SystemInfo.processor.properties = CPU_PROP_C | CPU_PROP_LE; + + char vendor[13] = {0,}; + U32 properties = 0; + U32 processor = 0; + U32 properties2 = 0; + +#if defined(TORQUE_SUPPORTS_VC_INLINE_X86_ASM) + __asm + { + //-------------------------------------- + // is CPUID supported + push ebx + push edx + push ecx + pushfd + pushfd // save EFLAGS to stack + pop eax // move EFLAGS into EAX + mov ebx, eax + xor eax, 0x200000 // flip bit 21 + push eax + popfd // restore EFLAGS + pushfd + pop eax + cmp eax, ebx + jz EXIT // doesn't support CPUID instruction + + //-------------------------------------- + // Get Vendor Informaion using CPUID eax==0 + xor eax, eax + cpuid + + mov DWORD PTR vendor, ebx + mov DWORD PTR vendor+4, edx + mov DWORD PTR vendor+8, ecx + + // get Generic Extended CPUID info + mov eax, 1 + cpuid // eax=1, so cpuid queries feature information + + and eax, 0x0fff3fff + mov processor, eax // just store the model bits + mov properties, edx + mov properties2, ecx + + // Want to check for 3DNow(tm). Need to see if extended cpuid functions present. + mov eax, 0x80000000 + cpuid + cmp eax, 0x80000000 + jbe MAYBE_3DLATER + mov eax, 0x80000001 + cpuid + and edx, 0x80000000 // 3DNow if bit 31 set -> put bit in our properties + or properties, edx + MAYBE_3DLATER: + + + EXIT: + popfd + pop ecx + pop edx + pop ebx + } +#elif defined(TORQUE_SUPPORTS_NASM) + + detectX86CPUInfo(vendor, &processor, &properties); + +#endif + + SetProcessorInfo(Platform::SystemInfo.processor, vendor, processor, properties, properties2); + +// now calculate speed of processor... + U32 nearmhz = 0; // nearest rounded mhz + U32 mhz = 0; // calculated value. + + LONG result; + DWORD data = 0; + DWORD dataSize = 4; + HKEY hKey; + + result = ::RegOpenKeyExA (HKEY_LOCAL_MACHINE,"Hardware\\Description\\System\\CentralProcessor\\0", 0, KEY_QUERY_VALUE, &hKey); + + if (result == ERROR_SUCCESS) + { + result = ::RegQueryValueExA (hKey, "~MHz",NULL, NULL,(LPBYTE)&data, &dataSize); + + if (result == ERROR_SUCCESS) + nearmhz = mhz = data; + + ::RegCloseKey(hKey); + } + + Platform::SystemInfo.processor.mhz = mhz; + + if (mhz==0) + { + Con::printf(" %s, (Unknown) Mhz", Platform::SystemInfo.processor.name); + // stick SOMETHING in so it isn't ZERO. + Platform::SystemInfo.processor.mhz = 200; // seems a decent value. + } + else + { + if (nearmhz >= 1000) + Con::printf(" %s, ~%.2f Ghz", Platform::SystemInfo.processor.name, ((float)nearmhz)/1000.0f); + else + Con::printf(" %s, ~%d Mhz", Platform::SystemInfo.processor.name, nearmhz); + if (nearmhz != mhz) + { + if (mhz >= 1000) + Con::printf(" (timed at roughly %.2f Ghz)", ((float)mhz)/1000.0f); + else + Con::printf(" (timed at roughly %d Mhz)", mhz); + } + } + + if( Platform::SystemInfo.processor.numAvailableCores > 0 + || Platform::SystemInfo.processor.numPhysicalProcessors > 0 + || Platform::SystemInfo.processor.isHyperThreaded ) + Platform::SystemInfo.processor.properties |= CPU_PROP_MP; + + if (Platform::SystemInfo.processor.properties & CPU_PROP_FPU) + Con::printf( " FPU detected" ); + if (Platform::SystemInfo.processor.properties & CPU_PROP_MMX) + Con::printf( " MMX detected" ); + if (Platform::SystemInfo.processor.properties & CPU_PROP_3DNOW) + Con::printf( " 3DNow detected" ); + if (Platform::SystemInfo.processor.properties & CPU_PROP_SSE) + Con::printf( " SSE detected" ); + if( Platform::SystemInfo.processor.properties & CPU_PROP_SSE2 ) + Con::printf( " SSE2 detected" ); + if( Platform::SystemInfo.processor.isHyperThreaded ) + Con::printf( " HT detected" ); + if( Platform::SystemInfo.processor.properties & CPU_PROP_MP ) + Con::printf( " MP detected [%i cores, %i logical, %i physical]", + Platform::SystemInfo.processor.numAvailableCores, + Platform::SystemInfo.processor.numLogicalProcessors, + Platform::SystemInfo.processor.numPhysicalProcessors ); + Con::printf(" "); + + PlatformBlitInit(); +} diff --git a/Engine/source/platformWin32/winConsole.cpp b/Engine/source/platformWin32/winConsole.cpp new file mode 100644 index 000000000..339c41b2b --- /dev/null +++ b/Engine/source/platformWin32/winConsole.cpp @@ -0,0 +1,341 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/util/rawData.h" +#include "core/strings/stringFunctions.h" +#include "core/strings/unicode.h" + +#include "platformWin32/platformWin32.h" +#include "platformWin32/winConsole.h" +#include "console/consoleTypes.h" +#include "core/util/journal/process.h" + + +WinConsole *WindowsConsole = NULL; + +namespace Con +{ + extern bool alwaysUseDebugOutput; +} + +ConsoleFunction(enableWinConsole, void, 2, 2, "enableWinConsole(bool);") +{ + argc; + WindowsConsole->enable(dAtob(argv[1])); +} + +void WinConsole::create() +{ + if( !WindowsConsole ) + WindowsConsole = new WinConsole(); +} + +void WinConsole::destroy() +{ + if( WindowsConsole ) + delete WindowsConsole; + WindowsConsole = NULL; +} + +void WinConsole::enable(bool enabled) +{ + winConsoleEnabled = enabled; + if(winConsoleEnabled) + { + AllocConsole(); + const char *title = Con::getVariable("Con::WindowTitle"); + if (title && *title) + { +#ifdef UNICODE + UTF16 buf[512]; + convertUTF8toUTF16((UTF8 *)title, buf, sizeof(buf)); + SetConsoleTitle(buf); +#else + SetConsoleTitle(title); +#endif + } + stdOut = GetStdHandle(STD_OUTPUT_HANDLE); + stdIn = GetStdHandle(STD_INPUT_HANDLE); + stdErr = GetStdHandle(STD_ERROR_HANDLE); + + printf("%s", Con::getVariable("Con::Prompt")); + } +} + +bool WinConsole::isEnabled() +{ + if ( WindowsConsole ) + return WindowsConsole->winConsoleEnabled; + + return false; +} + +static void winConsoleConsumer(U32 level, const char *line) +{ + if (WindowsConsole) + { + WindowsConsole->processConsoleLine(line); +#ifndef TORQUE_SHIPPING + // see console.cpp for a description of Con::alwaysUseDebugOutput + if( level == ConsoleLogEntry::Error || Con::alwaysUseDebugOutput) + { + // [rene, 04/05/2008] This is incorrect. Should do conversion from UTF8 here. + // Skipping for the sake of speed. Not meant to be seen by user anyway. + OutputDebugStringA( line ); + OutputDebugStringA( "\n" ); + } +#endif + } +} + +WinConsole::WinConsole() +{ + for (S32 iIndex = 0; iIndex < MAX_CMDS; iIndex ++) + rgCmds[iIndex][0] = '\0'; + + iCmdIndex = 0; + winConsoleEnabled = false; + Con::addConsumer(winConsoleConsumer); + inpos = 0; + lineOutput = false; + + Process::notify(this, &WinConsole::process, PROCESS_LAST_ORDER); +} + +WinConsole::~WinConsole() +{ + Process::remove(this, &WinConsole::process); + Con::removeConsumer(winConsoleConsumer); +} + +void WinConsole::printf(const char *s, ...) +{ + // Get the line into a buffer. + static const int BufSize = 4096; + static char buffer[4096]; + DWORD bytes; + va_list args; + va_start(args, s); + _vsnprintf(buffer, BufSize, s, args); + // Replace tabs with carats, like the "real" console does. + char *pos = buffer; + while (*pos) { + if (*pos == '\t') { + *pos = '^'; + } + pos++; + } + // Axe the color characters. + Con::stripColorChars(buffer); + // Print it. + WriteFile(stdOut, buffer, strlen(buffer), &bytes, NULL); + FlushFileBuffers( stdOut ); +} + +void WinConsole::processConsoleLine(const char *consoleLine) +{ + if(winConsoleEnabled) + { + inbuf[inpos] = 0; + if(lineOutput) + printf("%s\n", consoleLine); + else + printf("%c%s\n%s%s", '\r', consoleLine, Con::getVariable("Con::Prompt"), inbuf); + } +} + +void WinConsole::process() +{ + if(winConsoleEnabled) + { + DWORD numEvents; + GetNumberOfConsoleInputEvents(stdIn, &numEvents); + if(numEvents) + { + INPUT_RECORD rec[20]; + char outbuf[512]; + S32 outpos = 0; + + ReadConsoleInput(stdIn, rec, 20, &numEvents); + DWORD i; + for(i = 0; i < numEvents; i++) + { + if(rec[i].EventType == KEY_EVENT) + { + KEY_EVENT_RECORD *ke = &(rec[i].Event.KeyEvent); + if(ke->bKeyDown) + { + switch (ke->uChar.AsciiChar) + { + // If no ASCII char, check if it's a handled virtual key + case 0: + switch (ke->wVirtualKeyCode) + { + // UP ARROW + case 0x26 : + // Go to the previous command in the cyclic array + if ((-- iCmdIndex) < 0) + iCmdIndex = MAX_CMDS - 1; + + // If this command isn't empty ... + if (rgCmds[iCmdIndex][0] != '\0') + { + // Obliterate current displayed text + for (S32 i = outpos = 0; i < inpos; i ++) + { + outbuf[outpos ++] = '\b'; + outbuf[outpos ++] = ' '; + outbuf[outpos ++] = '\b'; + } + + // Copy command into command and display buffers + for (inpos = 0; inpos < (S32)strlen(rgCmds[iCmdIndex]); inpos ++, outpos ++) + { + outbuf[outpos] = rgCmds[iCmdIndex][inpos]; + inbuf [inpos ] = rgCmds[iCmdIndex][inpos]; + } + } + // If previous is empty, stay on current command + else if ((++ iCmdIndex) >= MAX_CMDS) + { + iCmdIndex = 0; + } + + break; + + // DOWN ARROW + case 0x28 : { + // Go to the next command in the command array, if + // it isn't empty + if (rgCmds[iCmdIndex][0] != '\0' && (++ iCmdIndex) >= MAX_CMDS) + iCmdIndex = 0; + + // Obliterate current displayed text + for (S32 i = outpos = 0; i < inpos; i ++) + { + outbuf[outpos ++] = '\b'; + outbuf[outpos ++] = ' '; + outbuf[outpos ++] = '\b'; + } + + // Copy command into command and display buffers + for (inpos = 0; inpos < (S32)strlen(rgCmds[iCmdIndex]); inpos ++, outpos ++) + { + outbuf[outpos] = rgCmds[iCmdIndex][inpos]; + inbuf [inpos ] = rgCmds[iCmdIndex][inpos]; + } + } + break; + + // LEFT ARROW + case 0x25 : + break; + + // RIGHT ARROW + case 0x27 : + break; + + default : + break; + } + break; + case '\b': + if(inpos > 0) + { + outbuf[outpos++] = '\b'; + outbuf[outpos++] = ' '; + outbuf[outpos++] = '\b'; + inpos--; + } + break; + case '\t': + // In the output buffer, we're going to have to erase the current line (in case + // we're cycling through various completions) and write out the whole input + // buffer, so (inpos * 3) + complen <= 512. Should be OK. The input buffer is + // also 512 chars long so that constraint will also be fine for the input buffer. + { + // Erase the current line. + U32 i; + for (i = 0; i < inpos; i++) { + outbuf[outpos++] = '\b'; + outbuf[outpos++] = ' '; + outbuf[outpos++] = '\b'; + } + // Modify the input buffer with the completion. + U32 maxlen = 512 - (inpos * 3); + if (ke->dwControlKeyState & SHIFT_PRESSED) { + inpos = Con::tabComplete(inbuf, inpos, maxlen, false); + } + else { + inpos = Con::tabComplete(inbuf, inpos, maxlen, true); + } + // Copy the input buffer to the output. + for (i = 0; i < inpos; i++) { + outbuf[outpos++] = inbuf[i]; + } + } + break; + case '\n': + case '\r': + outbuf[outpos++] = '\r'; + outbuf[outpos++] = '\n'; + + inbuf[inpos] = 0; + outbuf[outpos] = 0; + printf("%s", outbuf); + + // Pass the line along to the console for execution. + { + RawData rd; + rd.size = inpos + 1; + rd.data = ( S8* ) inbuf; + + Con::smConsoleInput.trigger(rd); + } + + // If we've gone off the end of our array, wrap + // back to the beginning + if (iCmdIndex >= MAX_CMDS) + iCmdIndex %= MAX_CMDS; + + // Put the new command into the array + strcpy(rgCmds[iCmdIndex ++], inbuf); + + printf("%s", Con::getVariable("Con::Prompt")); + inpos = outpos = 0; + break; + default: + inbuf[inpos++] = ke->uChar.AsciiChar; + outbuf[outpos++] = ke->uChar.AsciiChar; + break; + } + } + } + } + if(outpos) + { + outbuf[outpos] = 0; + printf("%s", outbuf); + } + } + } +} diff --git a/Engine/source/platformWin32/winConsole.h b/Engine/source/platformWin32/winConsole.h new file mode 100644 index 000000000..4c86bbb34 --- /dev/null +++ b/Engine/source/platformWin32/winConsole.h @@ -0,0 +1,65 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _WINCONSOLE_H_ +#define _WINCONSOLE_H_ + +#define MAX_CMDS 10 +#ifndef _CONSOLE_H_ +#include "console/console.h" +#endif +#ifndef _EVENT_H_ +#include "platform/event.h" +#endif + +class WinConsole +{ + bool winConsoleEnabled; + + HANDLE stdOut; + HANDLE stdIn; + HANDLE stdErr; + char inbuf[512]; + S32 inpos; + bool lineOutput; + char curTabComplete[512]; + S32 tabCompleteStart; + char rgCmds[MAX_CMDS][512]; + S32 iCmdIndex; + + void printf(const char *s, ...); + +public: + WinConsole(); + ~WinConsole(); + + void process(); + void enable(bool); + void processConsoleLine(const char *consoleLine); + static void create(); + static void destroy(); + static bool isEnabled(); +}; + +extern WinConsole *WindowsConsole; + +#endif diff --git a/Engine/source/platformWin32/winDInputDevice.cpp b/Engine/source/platformWin32/winDInputDevice.cpp new file mode 100644 index 000000000..992a88194 --- /dev/null +++ b/Engine/source/platformWin32/winDInputDevice.cpp @@ -0,0 +1,1589 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef INITGUID +#define INITGUID +#endif + +#include "platform/platform.h" +#include "platformWin32/winDInputDevice.h" + +#include "math/mMath.h" +#include "console/console.h" +#include "core/strings/unicode.h" +#include "windowManager/platformWindowMgr.h" + +// Static class data: +LPDIRECTINPUT8 DInputDevice::smDInputInterface; +U8 DInputDevice::smDeviceCount[ NUM_INPUT_DEVICE_TYPES ]; +bool DInputDevice::smInitialized = false; + +#ifdef LOG_INPUT +const char* getKeyName( U16 key ); +#endif + +//------------------------------------------------------------------------------ +DInputDevice::DInputDevice( const DIDEVICEINSTANCE* dii ) +{ + mDeviceInstance = *dii; + mDevice = NULL; + mAcquired = false; + mNeedSync = false; + mObjInstance = NULL; + mObjFormat = NULL; + mObjInfo = NULL; + mObjBuffer1 = NULL; + mObjBuffer2 = NULL; + mPrevObjBuffer = NULL; + + mForceFeedbackEffect = NULL; + mNumForceFeedbackAxes = 0; + mForceFeedbackAxes[0] = 0; + mForceFeedbackAxes[1] = 0; + + const char* deviceTypeName = "unknown"; + U8 deviceType = UnknownDeviceType; + + switch ( GET_DIDEVICE_TYPE( mDeviceInstance.dwDevType ) ) + { + // [rene, 12/09/2008] why do we turn a gamepad into a joystick here? + + case DI8DEVTYPE_GAMEPAD: + case DI8DEVTYPE_JOYSTICK: + deviceTypeName = "joystick"; + deviceType = JoystickDeviceType; + break; + + case DI8DEVTYPE_KEYBOARD: + deviceTypeName = "keyboard"; + deviceType = KeyboardDeviceType; + break; + + case DI8DEVTYPE_MOUSE: + deviceTypeName = "mouse"; + deviceType = MouseDeviceType; + break; + } + + mDeviceType = deviceType; + mDeviceID = smDeviceCount[ deviceType ] ++; + + dSprintf( mName, 29, "%s%d", deviceTypeName, mDeviceID ); +} + +//------------------------------------------------------------------------------ +DInputDevice::~DInputDevice() +{ + destroy(); +} + +//------------------------------------------------------------------------------ +void DInputDevice::init() +{ + // Reset all of the static variables: + smDInputInterface = NULL; + dMemset( smDeviceCount, 0, sizeof( smDeviceCount ) ); +} + +//------------------------------------------------------------------------------ +bool DInputDevice::create() +{ + HRESULT result; + + if ( smDInputInterface ) + { + result = smDInputInterface->CreateDevice( mDeviceInstance.guidInstance, &mDevice, NULL ); + if ( result == DI_OK ) + { + mDeviceCaps.dwSize = sizeof( DIDEVCAPS ); + if ( FAILED( mDevice->GetCapabilities( &mDeviceCaps ) ) ) + { + Con::errorf( " Failed to get the capabilities of the %s input device.", mName ); +#ifdef LOG_INPUT + Input::log( "Failed to get the capabilities of &s!\n", mName ); +#endif + return false; + } + +#ifdef LOG_INPUT + Input::log( "%s detected, created as %s (%s).\n", mDeviceInstance.tszProductName, mName, ( isPolled() ? "polled" : "asynchronous" ) ); +#endif + + if ( enumerateObjects() ) + { + // Set the device's data format: + DIDATAFORMAT dataFormat; + dMemset( &dataFormat, 0, sizeof( DIDATAFORMAT ) ); + dataFormat.dwSize = sizeof( DIDATAFORMAT ); + dataFormat.dwObjSize = sizeof( DIOBJECTDATAFORMAT ); + dataFormat.dwFlags = ( mDeviceType == MouseDeviceType ) ? DIDF_RELAXIS : DIDF_ABSAXIS; + dataFormat.dwDataSize = mObjBufferSize; + dataFormat.dwNumObjs = mObjCount; + dataFormat.rgodf = mObjFormat; + + result = mDevice->SetDataFormat( &dataFormat ); + if ( FAILED( result ) ) + { + Con::errorf( " Failed to set the data format for the %s input device.", mName ); +#ifdef LOG_INPUT + Input::log( "Failed to set the data format for %s!\n", mName ); +#endif + return false; + } + + // Set up the data buffer for buffered input: + DIPROPDWORD prop; + prop.diph.dwSize = sizeof( DIPROPDWORD ); + prop.diph.dwHeaderSize = sizeof( DIPROPHEADER ); + prop.diph.dwObj = 0; + prop.diph.dwHow = DIPH_DEVICE; + if ( isPolled() ) + prop.dwData = mObjBufferSize; + else + prop.dwData = QUEUED_BUFFER_SIZE; + + result = mDevice->SetProperty( DIPROP_BUFFERSIZE, &prop.diph ); + if ( FAILED( result ) ) + { + Con::errorf( " Failed to set the buffer size property for the %s input device.", mName ); +#ifdef LOG_INPUT + Input::log( "Failed to set the buffer size property for %s!\n", mName ); +#endif + return false; + } + + // If this device is a mouse, set it to relative axis mode: + if ( mDeviceType == MouseDeviceType ) + { + prop.diph.dwObj = 0; + prop.diph.dwHow = DIPH_DEVICE; + prop.dwData = DIPROPAXISMODE_REL; + + result = mDevice->SetProperty( DIPROP_AXISMODE, &prop.diph ); + if ( FAILED( result ) ) + { + Con::errorf( " Failed to set relative axis mode for the %s input device.", mName ); +#ifdef LOG_INPUT + Input::log( "Failed to set relative axis mode for %s!\n", mName ); +#endif + return false; + } + } + } + } + else + { +#ifdef LOG_INPUT + switch ( result ) + { + case DIERR_DEVICENOTREG: + Input::log( "CreateDevice failed -- The device or device instance is not registered with DirectInput.\n" ); + break; + + case DIERR_INVALIDPARAM: + Input::log( "CreateDevice failed -- Invalid parameter.\n" ); + break; + + case DIERR_NOINTERFACE: + Input::log( "CreateDevice failed -- The specified interface is not supported by the object.\n" ); + break; + + case DIERR_OUTOFMEMORY: + Input::log( "CreateDevice failed -- Out of memory.\n" ); + break; + + default: + Input::log( "CreateDevice failed -- Unknown error.\n" ); + break; + }; +#endif // LOG_INPUT + Con::printf( " CreateDevice failed for the %s input device!", mName ); + return false; + } + } + + Con::printf( " %s input device created.", mName ); + return true; +} + +//------------------------------------------------------------------------------ +void DInputDevice::destroy() +{ + if ( mDevice ) + { + unacquire(); + + // Tear down our forcefeedback. + if (mForceFeedbackEffect) + { + mForceFeedbackEffect->Release(); + mForceFeedbackEffect = NULL; + mNumForceFeedbackAxes = 0; +#ifdef LOG_INPUT + Input::log("DInputDevice::destroy - releasing constant force feeback effect\n"); +#endif + } + + mDevice->Release(); + mDevice = NULL; + + delete [] mObjInstance; + delete [] mObjFormat; + delete [] mObjInfo; + delete [] mObjBuffer1; + delete [] mObjBuffer2; + + mObjInstance = NULL; + mObjFormat = NULL; + mObjInfo = NULL; + mObjBuffer1 = NULL; + mObjBuffer2 = NULL; + mPrevObjBuffer = NULL; + mName[0] = 0; + } +} + +//------------------------------------------------------------------------------ +bool DInputDevice::acquire() +{ + if ( mDevice ) + { + if ( mAcquired ) + return( true ); + + bool result = false; + // Set the cooperative level: + // (do this here so that we have a valid app window) + DWORD coopLevel = DISCL_BACKGROUND; + if ( mDeviceType == JoystickDeviceType +// #ifndef DEBUG +// || mDeviceType == MouseDeviceType +// #endif + ) + // Exclusive access is required in order to perform force feedback + coopLevel = DISCL_EXCLUSIVE | DISCL_FOREGROUND; + else + coopLevel |= DISCL_NONEXCLUSIVE; + + result = mDevice->SetCooperativeLevel( getWin32WindowHandle(), coopLevel ); + if ( FAILED( result ) ) + { + Con::errorf( "Failed to set the cooperative level for the %s input device.", mName ); +#ifdef LOG_INPUT + Input::log( "Failed to set the cooperative level for %s!\n", mName ); +#endif + return false; + } + + // Enumerate joystick axes to enable force feedback + if ( NULL == mForceFeedbackEffect && JoystickDeviceType == mDeviceType) + { + // Since we will be playing force feedback effects, we should disable the auto-centering spring. + DIPROPDWORD dipdw; + dipdw.diph.dwSize = sizeof(DIPROPDWORD); + dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER); + dipdw.diph.dwObj = 0; + dipdw.diph.dwHow = DIPH_DEVICE; + dipdw.dwData = FALSE; + + if( FAILED( result = mDevice->SetProperty( DIPROP_AUTOCENTER, &dipdw.diph ) ) ) + return false; + } + + S32 code = mDevice->Acquire(); + result = SUCCEEDED( code ); + if ( result ) + { + Con::printf( "%s input device acquired.", mName ); +#ifdef LOG_INPUT + Input::log( "%s acquired.\n", mName ); +#endif + mAcquired = true; + + // If we were previously playing a force feedback effect, before + // losing acquisition, we do not automatically restart it. This is + // where you could call mForceFeedbackEffect->Start( INFINITE, 0 ); + // if you want that behavior. + + // Update all of the key states: + if ( !isPolled() ) + mNeedSync = true; + } + else + { + const char* reason; + switch ( code ) + { + case DIERR_INVALIDPARAM: reason = "Invalid parameter" ; break; + case DIERR_NOTINITIALIZED: reason = "Not initialized"; break; + case DIERR_OTHERAPPHASPRIO: reason = "Other app has priority"; break; + case S_FALSE: reason = "Already acquired"; break; + default: reason = "Unknown error"; break; + } + Con::warnf( "%s input device NOT acquired: %s", mName, reason ); +#ifdef LOG_INPUT + Input::log( "Failed to acquire %s: %s\n", mName, reason ); +#endif + } + + return( result ); + } + + return( false ); +} + +//------------------------------------------------------------------------------ +bool DInputDevice::unacquire() +{ + if ( mDevice ) + { + if ( !mAcquired ) + return( true ); + + bool result = false; + result = SUCCEEDED( mDevice->Unacquire() ); + if ( result ) + { + Con::printf( "%s input device unacquired.", mName ); +#ifdef LOG_INPUT + Input::log( "%s unacquired.\n", mName ); +#endif + mAcquired = false; + } + else + { + Con::warnf( ConsoleLogEntry::General, "%s input device NOT unacquired.", mName ); +#ifdef LOG_INPUT + Input::log( "Failed to unacquire %s!\n", mName ); +#endif + } + + return( result ); + } + + return( false ); +} + +//------------------------------------------------------------------------------ +BOOL CALLBACK DInputDevice::EnumObjectsProc( const DIDEVICEOBJECTINSTANCE* doi, LPVOID pvRef ) +{ + // Don't enumerate unknown types: + if ( doi->guidType == GUID_Unknown ) + return (DIENUM_CONTINUE); + + // Reduce a couple pointers: + DInputDevice* diDevice = (DInputDevice*) pvRef; + DIDEVICEOBJECTINSTANCE* objInstance = &diDevice->mObjInstance[diDevice->mObjEnumCount]; + DIOBJECTDATAFORMAT* objFormat = &diDevice->mObjFormat[diDevice->mObjEnumCount]; + + // Fill in the object instance structure: + *objInstance = *doi; + + // DWORD objects must be DWORD aligned: + if ( !(objInstance->dwType & DIDFT_BUTTON ) ) + diDevice->mObjBufferOfs = ( diDevice->mObjBufferOfs + 3 ) & ~3; + + objInstance->dwOfs = diDevice->mObjBufferOfs; + + // Fill in the object data format structure: + objFormat->pguid = &objInstance->guidType; + objFormat->dwType = objInstance->dwType; + objFormat->dwFlags= 0; + objFormat->dwOfs = diDevice->mObjBufferOfs; + + // Advance the enumeration counters: + if ( objFormat->dwType & DIDFT_BUTTON ) + diDevice->mObjBufferOfs += SIZEOF_BUTTON; + else + diDevice->mObjBufferOfs += SIZEOF_AXIS; + diDevice->mObjEnumCount++; + + return (DIENUM_CONTINUE); +} + +//------------------------------------------------------------------------------ +bool DInputDevice::enumerateObjects() +{ + if ( !mDevice ) + return false; + + // Calculate the needed buffer sizes and allocate them: + mObjCount = ( mDeviceCaps.dwAxes + mDeviceCaps.dwButtons + mDeviceCaps.dwPOVs ); + mObjBufferSize = mObjCount * sizeof( DWORD ); + + mObjInstance = new DIDEVICEOBJECTINSTANCE[mObjCount]; + mObjFormat = new DIOBJECTDATAFORMAT[mObjCount]; + mObjInfo = new ObjInfo[mObjCount]; + + if ( isPolled() ) + { + mObjBuffer1 = new U8[mObjBufferSize]; + dMemset( mObjBuffer1, 0, mObjBufferSize ); + mObjBuffer2 = new U8[mObjBufferSize]; + dMemset( mObjBuffer2, 0, mObjBufferSize ); + } + mObjEnumCount = 0; + mObjBufferOfs = 0; + + // We are about to enumerate, clear the axes we claim to know about + mNumForceFeedbackAxes = 0; + + // Enumerate all of the 'objects' detected on the device: + if ( FAILED( mDevice->EnumObjects( EnumObjectsProc, this, DIDFT_ALL ) ) ) + return false; + + // We only supports one or two axis joysticks + if( mNumForceFeedbackAxes > 2 ) + mNumForceFeedbackAxes = 2; + + // if we enumerated fewer objects than are supposedly available, reset the + // object count + if (mObjEnumCount < mObjCount) + mObjCount = mObjEnumCount; + + mObjBufferSize = ( mObjBufferSize + 3 ) & ~3; // Fill in the actual size to nearest DWORD + + U32 buttonCount = 0; + U32 povCount = 0; + U32 keyCount = 0; + U32 unknownCount = 0; + + // Fill in the device object's info structure: + for ( U32 i = 0; i < mObjCount; i++ ) + { + if ( mObjInstance[i].guidType == GUID_Button ) + { + mObjInfo[i].mType = SI_BUTTON; + mObjInfo[i].mInst = (InputObjectInstances)(KEY_BUTTON0 + buttonCount++); + } + else if ( mObjInstance[i].guidType == GUID_POV ) + { + // This is actually intentional - the POV handling code lower down + // takes the instance number and converts everything to button events. + mObjInfo[i].mType = SI_POV; + mObjInfo[i].mInst = (InputObjectInstances)povCount++; + } + else if ( mObjInstance[i].guidType == GUID_XAxis ) + { + mObjInfo[i].mType = SI_AXIS; + mObjInfo[i].mInst = SI_XAXIS; + + if (mObjInstance[i].dwFFMaxForce > 0) + mForceFeedbackAxes[mNumForceFeedbackAxes++] = mObjInstance[i].dwOfs; + } + else if ( mObjInstance[i].guidType == GUID_YAxis ) + { + mObjInfo[i].mType = SI_AXIS; + mObjInfo[i].mInst = SI_YAXIS; + + if (mObjInstance[i].dwFFMaxForce > 0) + mForceFeedbackAxes[mNumForceFeedbackAxes++] = mObjInstance[i].dwOfs; + } + else if ( mObjInstance[i].guidType == GUID_ZAxis ) + { + mObjInfo[i].mType = SI_AXIS; + mObjInfo[i].mInst = SI_ZAXIS; + } + else if ( mObjInstance[i].guidType == GUID_RxAxis ) + { + mObjInfo[i].mType = SI_AXIS; + mObjInfo[i].mInst = SI_RXAXIS; + } + else if ( mObjInstance[i].guidType == GUID_RyAxis ) + { + mObjInfo[i].mType = SI_AXIS; + mObjInfo[i].mInst = SI_RYAXIS; + } + else if ( mObjInstance[i].guidType == GUID_RzAxis ) + { + mObjInfo[i].mType = SI_AXIS; + mObjInfo[i].mInst = SI_RZAXIS; + } + else if ( mObjInstance[i].guidType == GUID_Slider ) + { + mObjInfo[i].mType = SI_AXIS; + mObjInfo[i].mInst = SI_SLIDER; + } + else if ( mObjInstance[i].guidType == GUID_Key ) + { + mObjInfo[i].mType = SI_KEY; + mObjInfo[i].mInst = DIK_to_Key( DIDFT_GETINSTANCE( mObjFormat[i].dwType ) ); + keyCount++; + } + else + { + mObjInfo[i].mType = SI_UNKNOWN; + mObjInfo[i].mInst = (InputObjectInstances)unknownCount++; + } + + // Set the device object's min and max values: + if ( mObjInstance[i].guidType == GUID_Button + || mObjInstance[i].guidType == GUID_Key + || mObjInstance[i].guidType == GUID_POV ) + { + mObjInfo[i].mMin = DIPROPRANGE_NOMIN; + mObjInfo[i].mMax = DIPROPRANGE_NOMAX; + } + else + { + // This is an axis or a slider, so find out its range: + DIPROPRANGE pr; + pr.diph.dwSize = sizeof( pr ); + pr.diph.dwHeaderSize = sizeof( pr.diph ); + pr.diph.dwHow = DIPH_BYID; + pr.diph.dwObj = mObjFormat[i].dwType; + + if ( SUCCEEDED( mDevice->GetProperty( DIPROP_RANGE, &pr.diph ) ) ) + { + mObjInfo[i].mMin = pr.lMin; + mObjInfo[i].mMax = pr.lMax; + } + else + { + mObjInfo[i].mMin = DIPROPRANGE_NOMIN; + mObjInfo[i].mMax = DIPROPRANGE_NOMAX; + } + } + } + +#ifdef LOG_INPUT + Input::log( " %d total objects detected.\n", mObjCount ); + if ( buttonCount ) + Input::log( " %d buttons.\n", buttonCount ); + if ( povCount ) + Input::log( " %d POVs.\n", povCount ); + + if ( keyCount ) + Input::log( " %d keys.\n", keyCount ); + if ( unknownCount ) + Input::log( " %d unknown objects.\n", unknownCount ); + Input::log( "\n" ); +#endif + + return true; +} + +//------------------------------------------------------------------------------ +const char* DInputDevice::getName() +{ +#ifdef UNICODE + static UTF8 buf[512]; + convertUTF16toUTF8(mDeviceInstance.tszInstanceName, buf, sizeof(buf)); + return (const char *)buf; +#else + return mDeviceInstance.tszInstanceName; +#endif +} + +//------------------------------------------------------------------------------ +const char* DInputDevice::getProductName() +{ +#ifdef UNICODE + static UTF8 buf[512]; + convertUTF16toUTF8(mDeviceInstance.tszProductName, buf, sizeof(buf)); + return (const char *)buf; +#else + return mDeviceInstance.tszProductName; +#endif +} + +//------------------------------------------------------------------------------ +bool DInputDevice::process() +{ + if ( mAcquired ) + { + if ( isPolled() ) + return processImmediate(); + else + return processAsync(); + } + + return false; +} + +//------------------------------------------------------------------------------ +bool DInputDevice::processAsync() +{ + DIDEVICEOBJECTDATA eventBuffer[QUEUED_BUFFER_SIZE]; + DWORD numEvents = QUEUED_BUFFER_SIZE; + HRESULT result; + + if ( !mDevice ) + return false; + + do + { + result = mDevice->GetDeviceData( sizeof( DIDEVICEOBJECTDATA ), eventBuffer, &numEvents, 0 ); + + if ( !SUCCEEDED( result ) ) + { + switch ( result ) + { + case DIERR_INPUTLOST: + // Data stream was interrupted, so try to reacquire the device: + mAcquired = false; + acquire(); + break; + + case DIERR_INVALIDPARAM: + Con::errorf( "DInputDevice::processAsync -- Invalid parameter passed to GetDeviceData of the %s input device!", mName ); +#ifdef LOG_INPUT + Input::log( "Invalid parameter passed to GetDeviceData for %s!\n", mName ); +#endif + break; + + case DIERR_NOTACQUIRED: + // We can't get the device, so quit: + mAcquired = false; + // Don't error out - this is actually a natural occurrence... + //Con::errorf( "DInputDevice::processAsync -- GetDeviceData called when %s input device is not acquired!", mName ); +#ifdef LOG_INPUT + Input::log( "GetDeviceData called when %s is not acquired!\n", mName ); +#endif + break; + } + + return false; + } + + // We have buffered input, so act on it: + for ( DWORD i = 0; i < numEvents; i++ ) + buildEvent( findObjInstance( eventBuffer[i].dwOfs ), eventBuffer[i].dwData, eventBuffer[i].dwData ); + + // Check for buffer overflow: + if ( result == DI_BUFFEROVERFLOW ) + { + // This is a problem, but we can keep going... + Con::errorf( "DInputDevice::processAsync -- %s input device's event buffer overflowed!", mName ); +#ifdef LOG_INPUT + Input::log( "%s event buffer overflowed!\n", mName ); +#endif + mNeedSync = true; // Let it know to resync next time through... + } + } + while ( numEvents ); + + return true; +} + +//------------------------------------------------------------------------------ +bool DInputDevice::processImmediate() +{ + if ( !mDevice ) + return false; + + mDevice->Poll(); + + U8* buffer = ( mPrevObjBuffer == mObjBuffer1 ) ? mObjBuffer2 : mObjBuffer1; + HRESULT result = mDevice->GetDeviceState( mObjBufferSize, buffer ); + if ( !SUCCEEDED( result ) ) + { + switch ( result ) + { + case DIERR_INPUTLOST: + // Data stream was interrupted, so try to reacquire the device: + mAcquired = false; + acquire(); + break; + + case DIERR_INVALIDPARAM: + Con::errorf( "DInputDevice::processPolled -- invalid parameter passed to GetDeviceState on %s input device!", mName ); +#ifdef LOG_INPUT + Input::log( "Invalid parameter passed to GetDeviceState on %s.\n", mName ); +#endif + break; + + case DIERR_NOTACQUIRED: + Con::errorf( "DInputDevice::processPolled -- GetDeviceState called when %s input device is not acquired!", mName ); +#ifdef LOG_INPUT + Input::log( "GetDeviceState called when %s is not acquired!\n", mName ); +#endif + break; + + case E_PENDING: + Con::warnf( "DInputDevice::processPolled -- Data not yet available for the %s input device!", mName ); +#ifdef LOG_INPUT + Input::log( "Data pending for %s.", mName ); +#endif + break; + } + + return false; + } + + // Loop through all of the objects and produce events where + // the states have changed: + + // (oldData = 0 prevents a crashing bug in Torque. There is a case where + // Torque accessed oldData without it ever being set.) + S32 newData, oldData = 0; + for ( DWORD i = 0; i < mObjCount; i++ ) + { + if ( mObjFormat[i].dwType & DIDFT_BUTTON ) + { + if ( mPrevObjBuffer ) + { + newData = *( (U8*) ( buffer + mObjFormat[i].dwOfs ) ); + oldData = *( (U8*) ( mPrevObjBuffer + mObjFormat[i].dwOfs ) ); + if ( newData == oldData ) + continue; + } + else + continue; + } + else if ( mObjFormat[i].dwType & DIDFT_POV ) + { + if ( mPrevObjBuffer ) + { + newData = *( (S32*) ( buffer + mObjFormat[i].dwOfs ) ); + oldData = *( (S32*) ( mPrevObjBuffer + mObjFormat[i].dwOfs ) ); + if ( LOWORD( newData ) == LOWORD( oldData ) ) + continue; + } + else + continue; + } + else + { + // report normal axes every time through the loop: + newData = *( (S32*) ( buffer + mObjFormat[i].dwOfs ) ); + } + + // Build an event: + buildEvent( i, newData, oldData ); + } + mPrevObjBuffer = buffer; + + return true; +} + +//------------------------------------------------------------------------------ +DWORD DInputDevice::findObjInstance( DWORD offset ) +{ + DIDEVICEOBJECTINSTANCE *inst = mObjInstance; + for ( U32 i = 0; i < mObjCount; i++, inst++ ) + { + if ( inst->dwOfs == offset ) + return i; + } + + AssertFatal( false, "DInputDevice::findObjInstance -- failed to locate object instance." ); + return 0; +} + +//------------------------------------------------------------------------------ +enum Win32POVDirBits +{ + POV_up = 1 << 0, + POV_right = 1 << 1, + POV_down = 1 << 2, + POV_left = 1 << 3, +}; + +enum Win32POVDirsInQuadrant +{ + POVq_center = 0, + POVq_up = POV_up, + POVq_upright = POV_up | POV_right, + POVq_right = POV_right, + POVq_downright = POV_down | POV_right, + POVq_down = POV_down, + POVq_downleft = POV_down | POV_left, + POVq_left = POV_left, + POVq_upleft = POV_up | POV_left, +}; + +static const U32 Win32POVQuadrantMap[] = +{ + POVq_up, POVq_upright, + POVq_right, POVq_downright, + POVq_down, POVq_downleft, + POVq_left, POVq_upleft +}; + +static U32 _Win32GetPOVDirs(U32 data) +{ + U32 quadrant = (data / 4500) % 8; + U32 dirs = (data == 0xffff) ? POVq_center : Win32POVQuadrantMap[quadrant]; + return dirs; +} + +#if defined(LOG_INPUT) +static void _Win32LogPOVInput(InputEventInfo &newEvent) +{ + + U32 inst = 0xffff; + const char* sstate = ( newEvent.action == SI_MAKE ) ? "pressed" : "released"; + const char* dir = ""; + switch( newEvent.objInst ) + { + case SI_UPOV: + case SI_UPOV2: + dir = "Up"; inst = (newEvent.objInst == SI_UPOV) ? 1 : 2; break; + case SI_RPOV: + case SI_RPOV2: + dir = "Right"; inst = (newEvent.objInst == SI_RPOV) ? 1 : 2; break; + case SI_DPOV: + case SI_DPOV2: + dir = "Down"; inst = (newEvent.objInst == SI_DPOV) ? 1 : 2; break; + case SI_LPOV: + case SI_LPOV2: + dir = "Left"; inst = (newEvent.objInst == SI_LPOV) ? 1 : 2; break; + } + Con::printf( "EVENT (DInput): %s POV %d %s.\n", dir, inst, sstate); +} +#else +#define _Win32LogPOVInput( a ) +#endif + +//------------------------------------------------------------------------------ +bool DInputDevice::buildEvent( DWORD offset, S32 newData, S32 oldData ) +{ + ObjInfo &objInfo = mObjInfo[offset]; + + if ( objInfo.mType == SI_UNKNOWN ) + return false; + + InputEventInfo newEvent; + newEvent.deviceType = (InputDeviceTypes)mDeviceType; + newEvent.deviceInst = mDeviceID; + newEvent.objType = objInfo.mType; + newEvent.objInst = objInfo.mInst; + newEvent.modifier = (InputModifiers)0; + + switch ( newEvent.objType ) + { + case SI_AXIS: + newEvent.action = SI_MOVE; + if ( newEvent.deviceType == MouseDeviceType ) + { + newEvent.fValue = float( newData ); + +#ifdef LOG_INPUT +#ifdef LOG_MOUSEMOVE + if ( newEvent.objInst == SI_XAXIS ) + Input::log( "EVENT (DInput): %s move (%.1f, 0.0).\n", mName, newEvent.fValue ); + else if ( newEvent.objInst == SI_YAXIS ) + Input::log( "EVENT (DInput): %s move (0.0, %.1f).\n", mName, newEvent.fValue ); + else +#endif + if ( newEvent.objInst == SI_ZAXIS ) + Input::log( "EVENT (DInput): %s wheel move %.1f.\n", mName, newEvent.fValue ); +#endif + } + else // Joystick or other device: + { + // Scale to the range -1.0 to 1.0: + if ( objInfo.mMin != DIPROPRANGE_NOMIN && objInfo.mMax != DIPROPRANGE_NOMAX ) + { + float range = float( objInfo.mMax - objInfo.mMin ); + newEvent.fValue = float( ( 2 * newData ) - objInfo.mMax - objInfo.mMin ) / range; + } + else + newEvent.fValue = (F32)newData; + +#ifdef LOG_INPUT + // Keep this commented unless you REALLY want these messages for something-- + // they come once per each iteration of the main game loop. + //switch ( newEvent.objType ) + //{ + //case SI_XAXIS: + //if ( newEvent.fValue < -0.01f || newEvent.fValue > 0.01f ) + //Input::log( "EVENT (DInput): %s X-axis move %.2f.\n", mName, newEvent.fValue ); + //break; + //case SI_YAXIS: + //if ( newEvent.fValue < -0.01f || newEvent.fValue > 0.01f ) + //Input::log( "EVENT (DInput): %s Y-axis move %.2f.\n", mName, newEvent.fValue ); + //break; + //case SI_ZAXIS: + //Input::log( "EVENT (DInput): %s Z-axis move %.1f.\n", mName, newEvent.fValue ); + //break; + //case SI_RXAXIS: + //Input::log( "EVENT (DInput): %s R-axis move %.1f.\n", mName, newEvent.fValue ); + //break; + //case SI_RYAXIS: + //Input::log( "EVENT (DInput): %s U-axis move %.1f.\n", mName, newEvent.fValue ); + //break; + //case SI_RZAXIS: + //Input::log( "EVENT (DInput): %s V-axis move %.1f.\n", mName, newEvent.fValue ); + //break; + //case SI_SLIDER: + //Input::log( "EVENT (DInput): %s slider move %.1f.\n", mName, newEvent.fValue ); + //break; + //}; +#endif + } + + newEvent.postToSignal(Input::smInputEvent); + break; + + case SI_BUTTON: + newEvent.action = ( newData & 0x80 ) ? SI_MAKE : SI_BREAK; + newEvent.fValue = ( newEvent.action == SI_MAKE ) ? 1.0f : 0.0f; + +#ifdef LOG_INPUT + if ( newEvent.action == SI_MAKE ) + Input::log( "EVENT (DInput): %s button%d pressed.\n", mName, newEvent.objInst - KEY_BUTTON0 ); + else + Input::log( "EVENT (DInput): %s button%d released.\n", mName, newEvent.objInst - KEY_BUTTON0 ); +#endif + + newEvent.postToSignal(Input::smInputEvent); + break; + + case SI_POV: + // Handle artificial POV up/down/left/right buttons + + // If we're not a polling device, oldData and newData are the same, so "fake" transitions + if(!isPolled()) { + oldData = mPrevPOVPos; + mPrevPOVPos = newData; + } + + newData = LOWORD( newData ); + oldData = LOWORD( oldData ); + + newData = _Win32GetPOVDirs(newData); + oldData = _Win32GetPOVDirs(oldData); + + U32 setkeys = newData & (~oldData); + U32 clearkeys = oldData & (~newData); + U32 objInst = newEvent.objInst; + + if ( setkeys || clearkeys ) + { + if ( clearkeys ) + { + newEvent.action = SI_BREAK; + newEvent.fValue = 0.0f; + // post events for all buttons that need to be cleared. + if( clearkeys & POV_up) + { + newEvent.objInst = ( objInst == 0 ) ? SI_UPOV : SI_UPOV2; + _Win32LogPOVInput(newEvent); + newEvent.postToSignal(Input::smInputEvent); + + } + if( clearkeys & POV_right) + { + newEvent.objInst = ( objInst == 0 ) ? SI_RPOV : SI_RPOV2; + _Win32LogPOVInput(newEvent); + newEvent.postToSignal(Input::smInputEvent); + } + if( clearkeys & POV_down) + { + newEvent.objInst = ( objInst == 0 ) ? SI_DPOV : SI_DPOV; + _Win32LogPOVInput(newEvent); + newEvent.postToSignal(Input::smInputEvent); + } + if( clearkeys & POV_left) + { + newEvent.objInst = ( objInst == 0 ) ? SI_LPOV : SI_LPOV2; + _Win32LogPOVInput(newEvent); + newEvent.postToSignal(Input::smInputEvent); + } + } // clear keys + + if ( setkeys ) + { + newEvent.action = SI_MAKE; + newEvent.fValue = 1.0f; + // post events for all buttons that need to be set. + if( setkeys & POV_up) + { + newEvent.objInst = ( objInst == 0 ) ? SI_UPOV : SI_UPOV2; + _Win32LogPOVInput(newEvent); + newEvent.postToSignal(Input::smInputEvent); + } + if( setkeys & POV_right) + { + newEvent.objInst = ( objInst == 0 ) ? SI_RPOV : SI_RPOV2; + _Win32LogPOVInput(newEvent); + newEvent.postToSignal(Input::smInputEvent); + } + if( setkeys & POV_down) + { + newEvent.objInst = ( objInst == 0 ) ? SI_DPOV : SI_DPOV; + _Win32LogPOVInput(newEvent); + newEvent.postToSignal(Input::smInputEvent); + } + if( setkeys & POV_left) + { + newEvent.objInst = ( objInst == 0 ) ? SI_LPOV : SI_LPOV2; + _Win32LogPOVInput(newEvent); + newEvent.postToSignal(Input::smInputEvent); + } + } // set keys + } + break; + } + + return true; +} + +void DInputDevice::rumble(float x, float y) +{ + LONG rglDirection[2] = { 0, 0 }; + DICONSTANTFORCE cf = { 0 }; + HRESULT result; + + // Now set the new parameters and start the effect immediately. + if (!mForceFeedbackEffect) + { +#ifdef LOG_INPUT + Input::log("DInputDevice::rumbleJoystick - creating constant force feeback effect\n"); +#endif + DIEFFECT eff; + ZeroMemory( &eff, sizeof(eff) ); + eff.dwSize = sizeof(DIEFFECT); + eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; + eff.dwDuration = INFINITE; + eff.dwSamplePeriod = 0; + eff.dwGain = DI_FFNOMINALMAX; + eff.dwTriggerButton = DIEB_NOTRIGGER; + eff.dwTriggerRepeatInterval = 0; + eff.cAxes = mNumForceFeedbackAxes; + eff.rgdwAxes = mForceFeedbackAxes; + eff.rglDirection = rglDirection; + eff.lpEnvelope = 0; + eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); + eff.lpvTypeSpecificParams = &cf; + eff.dwStartDelay = 0; + + // Create the prepared effect + if ( FAILED( result = mDevice->CreateEffect( GUID_ConstantForce, &eff, &mForceFeedbackEffect, NULL ) ) ) + { +#ifdef LOG_INPUT + Input::log( "DInputDevice::rumbleJoystick - %s does not support force feedback.\n", mName ); +#endif + Con::errorf( "DInputDevice::rumbleJoystick - %s does not support force feedback.\n", mName ); + return; + } + else + { +#ifdef LOG_INPUT + Input::log( "DInputDevice::rumbleJoystick - %s supports force feedback.\n", mName ); +#endif + Con::printf( "DInputDevice::rumbleJoystick - %s supports force feedback.\n", mName ); + } + } + + // Clamp the input floats to [0 - 1] + x = max(0, min(1, x)); + y = max(0, min(1, y)); + + if ( 1 == mNumForceFeedbackAxes ) + { + cf.lMagnitude = (DWORD)( x * DI_FFNOMINALMAX ); + } + else + { + rglDirection[0] = (DWORD)( x * DI_FFNOMINALMAX ); + rglDirection[1] = (DWORD)( y * DI_FFNOMINALMAX ); + cf.lMagnitude = (DWORD)sqrt( (double)(x * x * DI_FFNOMINALMAX * DI_FFNOMINALMAX + y * y * DI_FFNOMINALMAX * DI_FFNOMINALMAX) ); + } + + DIEFFECT eff; + ZeroMemory( &eff, sizeof(eff) ); + eff.dwSize = sizeof(DIEFFECT); + eff.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS; + eff.dwDuration = INFINITE; + eff.dwSamplePeriod = 0; + eff.dwGain = DI_FFNOMINALMAX; + eff.dwTriggerButton = DIEB_NOTRIGGER; + eff.dwTriggerRepeatInterval = 0; + eff.cAxes = mNumForceFeedbackAxes; + eff.rglDirection = rglDirection; + eff.lpEnvelope = 0; + eff.cbTypeSpecificParams = sizeof(DICONSTANTFORCE); + eff.lpvTypeSpecificParams = &cf; + eff.dwStartDelay = 0; + + if ( FAILED( result = mForceFeedbackEffect->SetParameters( &eff, DIEP_DIRECTION | DIEP_TYPESPECIFICPARAMS | DIEP_START ) ) ) + { + const char* errorString = NULL; + switch ( result ) + { + case DIERR_INPUTLOST: + errorString = "DIERR_INPUTLOST"; + break; + + case DIERR_INVALIDPARAM: + errorString = "DIERR_INVALIDPARAM"; + break; + + case DIERR_NOTACQUIRED: + errorString = "DIERR_NOTACQUIRED"; + break; + + default: + errorString = "Unknown Error"; + } + +#ifdef LOG_INPUT + Input::log( "DInputDevice::rumbleJoystick - %s - Failed to start rumble effect\n", errorString ); +#endif + Con::errorf( "DInputDevice::rumbleJoystick - %s - Failed to start rumble effect\n", errorString ); + } +} + +//------------------------------------------------------------------------------ +// +// This function translates the DirectInput scan code to the associated +// internal key code (as defined in event.h). +// +//------------------------------------------------------------------------------ +InputObjectInstances DIK_to_Key( U8 dikCode ) +{ + switch ( dikCode ) + { + case DIK_ESCAPE: return KEY_ESCAPE; + + case DIK_1: return KEY_1; + case DIK_2: return KEY_2; + case DIK_3: return KEY_3; + case DIK_4: return KEY_4; + case DIK_5: return KEY_5; + case DIK_6: return KEY_6; + case DIK_7: return KEY_7; + case DIK_8: return KEY_8; + case DIK_9: return KEY_9; + case DIK_0: return KEY_0; + + case DIK_MINUS: return KEY_MINUS; + case DIK_EQUALS: return KEY_EQUALS; + case DIK_BACK: return KEY_BACKSPACE; + case DIK_TAB: return KEY_TAB; + + case DIK_Q: return KEY_Q; + case DIK_W: return KEY_W; + case DIK_E: return KEY_E; + case DIK_R: return KEY_R; + case DIK_T: return KEY_T; + case DIK_Y: return KEY_Y; + case DIK_U: return KEY_U; + case DIK_I: return KEY_I; + case DIK_O: return KEY_O; + case DIK_P: return KEY_P; + + case DIK_LBRACKET: return KEY_LBRACKET; + case DIK_RBRACKET: return KEY_RBRACKET; + case DIK_RETURN: return KEY_RETURN; + case DIK_LCONTROL: return KEY_LCONTROL; + + case DIK_A: return KEY_A; + case DIK_S: return KEY_S; + case DIK_D: return KEY_D; + case DIK_F: return KEY_F; + case DIK_G: return KEY_G; + case DIK_H: return KEY_H; + case DIK_J: return KEY_J; + case DIK_K: return KEY_K; + case DIK_L: return KEY_L; + + case DIK_SEMICOLON: return KEY_SEMICOLON; + case DIK_APOSTROPHE: return KEY_APOSTROPHE; + case DIK_GRAVE: return KEY_TILDE; + case DIK_LSHIFT: return KEY_LSHIFT; + case DIK_BACKSLASH: return KEY_BACKSLASH; + + case DIK_Z: return KEY_Z; + case DIK_X: return KEY_X; + case DIK_C: return KEY_C; + case DIK_V: return KEY_V; + case DIK_B: return KEY_B; + case DIK_N: return KEY_N; + case DIK_M: return KEY_M; + + case DIK_COMMA: return KEY_COMMA; + case DIK_PERIOD: return KEY_PERIOD; + case DIK_SLASH: return KEY_SLASH; + case DIK_RSHIFT: return KEY_RSHIFT; + case DIK_MULTIPLY: return KEY_MULTIPLY; + case DIK_LALT: return KEY_LALT; + case DIK_SPACE: return KEY_SPACE; + case DIK_CAPSLOCK: return KEY_CAPSLOCK; + + case DIK_F1: return KEY_F1; + case DIK_F2: return KEY_F2; + case DIK_F3: return KEY_F3; + case DIK_F4: return KEY_F4; + case DIK_F5: return KEY_F5; + case DIK_F6: return KEY_F6; + case DIK_F7: return KEY_F7; + case DIK_F8: return KEY_F8; + case DIK_F9: return KEY_F9; + case DIK_F10: return KEY_F10; + + case DIK_NUMLOCK: return KEY_NUMLOCK; + case DIK_SCROLL: return KEY_SCROLLLOCK; + + case DIK_NUMPAD7: return KEY_NUMPAD7; + case DIK_NUMPAD8: return KEY_NUMPAD8; + case DIK_NUMPAD9: return KEY_NUMPAD9; + case DIK_SUBTRACT: return KEY_SUBTRACT; + + case DIK_NUMPAD4: return KEY_NUMPAD4; + case DIK_NUMPAD5: return KEY_NUMPAD5; + case DIK_NUMPAD6: return KEY_NUMPAD6; + case DIK_ADD: return KEY_ADD; + + case DIK_NUMPAD1: return KEY_NUMPAD1; + case DIK_NUMPAD2: return KEY_NUMPAD2; + case DIK_NUMPAD3: return KEY_NUMPAD3; + case DIK_NUMPAD0: return KEY_NUMPAD0; + case DIK_DECIMAL: return KEY_DECIMAL; + + case DIK_F11: return KEY_F11; + case DIK_F12: return KEY_F12; + case DIK_F13: return KEY_F13; + case DIK_F14: return KEY_F14; + case DIK_F15: return KEY_F15; + + case DIK_KANA: return KEY_NULL; + case DIK_CONVERT: return KEY_NULL; + case DIK_NOCONVERT: return KEY_NULL; + case DIK_YEN: return KEY_NULL; + case DIK_NUMPADEQUALS: return KEY_NULL; + case DIK_CIRCUMFLEX: return KEY_NULL; + case DIK_AT: return KEY_NULL; + case DIK_COLON: return KEY_NULL; + case DIK_UNDERLINE: return KEY_NULL; + case DIK_KANJI: return KEY_NULL; + case DIK_STOP: return KEY_NULL; + case DIK_AX: return KEY_NULL; + case DIK_UNLABELED: return KEY_NULL; + + case DIK_NUMPADENTER: return KEY_NUMPADENTER; + case DIK_RCONTROL: return KEY_RCONTROL; + case DIK_NUMPADCOMMA: return KEY_SEPARATOR; + case DIK_DIVIDE: return KEY_DIVIDE; + case DIK_SYSRQ: return KEY_PRINT; + case DIK_RALT: return KEY_RALT; + case DIK_PAUSE: return KEY_PAUSE; + + case DIK_HOME: return KEY_HOME; + case DIK_UP: return KEY_UP; + case DIK_PGUP: return KEY_PAGE_UP; + case DIK_LEFT: return KEY_LEFT; + case DIK_RIGHT: return KEY_RIGHT; + case DIK_END: return KEY_END; + case DIK_DOWN: return KEY_DOWN; + case DIK_PGDN: return KEY_PAGE_DOWN; + case DIK_INSERT: return KEY_INSERT; + case DIK_DELETE: return KEY_DELETE; + + case DIK_LWIN: return KEY_WIN_LWINDOW; + case DIK_RWIN: return KEY_WIN_RWINDOW; + case DIK_APPS: return KEY_WIN_APPS; + case DIK_OEM_102: return KEY_OEM_102; + } + + return KEY_NULL; +} + +//------------------------------------------------------------------------------ +// +// This function translates an internal key code to the associated +// DirectInput scan code +// +//------------------------------------------------------------------------------ +U8 Key_to_DIK( U16 keyCode ) +{ + switch ( keyCode ) + { + case KEY_BACKSPACE: return DIK_BACK; + case KEY_TAB: return DIK_TAB; + case KEY_RETURN: return DIK_RETURN; + //KEY_CONTROL: + //KEY_ALT: + //KEY_SHIFT: + case KEY_PAUSE: return DIK_PAUSE; + case KEY_CAPSLOCK: return DIK_CAPSLOCK; + case KEY_ESCAPE: return DIK_ESCAPE; + + case KEY_SPACE: return DIK_SPACE; + case KEY_PAGE_DOWN: return DIK_PGDN; + case KEY_PAGE_UP: return DIK_PGUP; + case KEY_END: return DIK_END; + case KEY_HOME: return DIK_HOME; + case KEY_LEFT: return DIK_LEFT; + case KEY_UP: return DIK_UP; + case KEY_RIGHT: return DIK_RIGHT; + case KEY_DOWN: return DIK_DOWN; + case KEY_PRINT: return DIK_SYSRQ; + case KEY_INSERT: return DIK_INSERT; + case KEY_DELETE: return DIK_DELETE; + case KEY_HELP: return 0; + + case KEY_0: return DIK_0; + case KEY_1: return DIK_1; + case KEY_2: return DIK_2; + case KEY_3: return DIK_3; + case KEY_4: return DIK_4; + case KEY_5: return DIK_5; + case KEY_6: return DIK_6; + case KEY_7: return DIK_7; + case KEY_8: return DIK_8; + case KEY_9: return DIK_9; + + case KEY_A: return DIK_A; + case KEY_B: return DIK_B; + case KEY_C: return DIK_C; + case KEY_D: return DIK_D; + case KEY_E: return DIK_E; + case KEY_F: return DIK_F; + case KEY_G: return DIK_G; + case KEY_H: return DIK_H; + case KEY_I: return DIK_I; + case KEY_J: return DIK_J; + case KEY_K: return DIK_K; + case KEY_L: return DIK_L; + case KEY_M: return DIK_M; + case KEY_N: return DIK_N; + case KEY_O: return DIK_O; + case KEY_P: return DIK_P; + case KEY_Q: return DIK_Q; + case KEY_R: return DIK_R; + case KEY_S: return DIK_S; + case KEY_T: return DIK_T; + case KEY_U: return DIK_U; + case KEY_V: return DIK_V; + case KEY_W: return DIK_W; + case KEY_X: return DIK_X; + case KEY_Y: return DIK_Y; + case KEY_Z: return DIK_Z; + + case KEY_TILDE: return DIK_GRAVE; + case KEY_MINUS: return DIK_MINUS; + case KEY_EQUALS: return DIK_EQUALS; + case KEY_LBRACKET: return DIK_LBRACKET; + case KEY_RBRACKET: return DIK_RBRACKET; + case KEY_BACKSLASH: return DIK_BACKSLASH; + case KEY_SEMICOLON: return DIK_SEMICOLON; + case KEY_APOSTROPHE: return DIK_APOSTROPHE; + case KEY_COMMA: return DIK_COMMA; + case KEY_PERIOD: return DIK_PERIOD; + case KEY_SLASH: return DIK_SLASH; + + case KEY_NUMPAD0: return DIK_NUMPAD0; + case KEY_NUMPAD1: return DIK_NUMPAD1; + case KEY_NUMPAD2: return DIK_NUMPAD2; + case KEY_NUMPAD3: return DIK_NUMPAD3; + case KEY_NUMPAD4: return DIK_NUMPAD4; + case KEY_NUMPAD5: return DIK_NUMPAD5; + case KEY_NUMPAD6: return DIK_NUMPAD6; + case KEY_NUMPAD7: return DIK_NUMPAD7; + case KEY_NUMPAD8: return DIK_NUMPAD8; + case KEY_NUMPAD9: return DIK_NUMPAD9; + case KEY_MULTIPLY: return DIK_MULTIPLY; + case KEY_ADD: return DIK_ADD; + case KEY_SEPARATOR: return DIK_NUMPADCOMMA; + case KEY_SUBTRACT: return DIK_SUBTRACT; + case KEY_DECIMAL: return DIK_DECIMAL; + case KEY_DIVIDE: return DIK_DIVIDE; + case KEY_NUMPADENTER: return DIK_NUMPADENTER; + + case KEY_F1: return DIK_F1; + case KEY_F2: return DIK_F2; + case KEY_F3: return DIK_F3; + case KEY_F4: return DIK_F4; + case KEY_F5: return DIK_F5; + case KEY_F6: return DIK_F6; + case KEY_F7: return DIK_F7; + case KEY_F8: return DIK_F8; + case KEY_F9: return DIK_F9; + case KEY_F10: return DIK_F10; + case KEY_F11: return DIK_F11; + case KEY_F12: return DIK_F12; + case KEY_F13: return DIK_F13; + case KEY_F14: return DIK_F14; + case KEY_F15: return DIK_F15; + case KEY_F16: + case KEY_F17: + case KEY_F18: + case KEY_F19: + case KEY_F20: + case KEY_F21: + case KEY_F22: + case KEY_F23: + case KEY_F24: return 0; + + case KEY_NUMLOCK: return DIK_NUMLOCK; + case KEY_SCROLLLOCK: return DIK_SCROLL; + case KEY_LCONTROL: return DIK_LCONTROL; + case KEY_RCONTROL: return DIK_RCONTROL; + case KEY_LALT: return DIK_LALT; + case KEY_RALT: return DIK_RALT; + case KEY_LSHIFT: return DIK_LSHIFT; + case KEY_RSHIFT: return DIK_RSHIFT; + + case KEY_WIN_LWINDOW: return DIK_LWIN; + case KEY_WIN_RWINDOW: return DIK_RWIN; + case KEY_WIN_APPS: return DIK_APPS; + case KEY_OEM_102: return DIK_OEM_102; + + }; + + return 0; +} + +#ifdef LOG_INPUT +//------------------------------------------------------------------------------ +const char* getKeyName( U16 key ) +{ + switch ( key ) + { + case KEY_BACKSPACE: return "Backspace"; + case KEY_TAB: return "Tab"; + case KEY_RETURN: return "Return"; + case KEY_PAUSE: return "Pause"; + case KEY_CAPSLOCK: return "CapsLock"; + case KEY_ESCAPE: return "Esc"; + + case KEY_SPACE: return "SpaceBar"; + case KEY_PAGE_DOWN: return "PageDown"; + case KEY_PAGE_UP: return "PageUp"; + case KEY_END: return "End"; + case KEY_HOME: return "Home"; + case KEY_LEFT: return "Left"; + case KEY_UP: return "Up"; + case KEY_RIGHT: return "Right"; + case KEY_DOWN: return "Down"; + case KEY_PRINT: return "PrintScreen"; + case KEY_INSERT: return "Insert"; + case KEY_DELETE: return "Delete"; + case KEY_HELP: return "Help"; + + case KEY_NUMPAD0: return "Numpad 0"; + case KEY_NUMPAD1: return "Numpad 1"; + case KEY_NUMPAD2: return "Numpad 2"; + case KEY_NUMPAD3: return "Numpad 3"; + case KEY_NUMPAD4: return "Numpad 4"; + case KEY_NUMPAD5: return "Numpad 5"; + case KEY_NUMPAD6: return "Numpad 6"; + case KEY_NUMPAD7: return "Numpad 7"; + case KEY_NUMPAD8: return "Numpad 8"; + case KEY_NUMPAD9: return "Numpad 9"; + case KEY_MULTIPLY: return "Multiply"; + case KEY_ADD: return "Add"; + case KEY_SEPARATOR: return "Separator"; + case KEY_SUBTRACT: return "Subtract"; + case KEY_DECIMAL: return "Decimal"; + case KEY_DIVIDE: return "Divide"; + case KEY_NUMPADENTER: return "Numpad Enter"; + + case KEY_F1: return "F1"; + case KEY_F2: return "F2"; + case KEY_F3: return "F3"; + case KEY_F4: return "F4"; + case KEY_F5: return "F5"; + case KEY_F6: return "F6"; + case KEY_F7: return "F7"; + case KEY_F8: return "F8"; + case KEY_F9: return "F9"; + case KEY_F10: return "F10"; + case KEY_F11: return "F11"; + case KEY_F12: return "F12"; + case KEY_F13: return "F13"; + case KEY_F14: return "F14"; + case KEY_F15: return "F15"; + case KEY_F16: return "F16"; + case KEY_F17: return "F17"; + case KEY_F18: return "F18"; + case KEY_F19: return "F19"; + case KEY_F20: return "F20"; + case KEY_F21: return "F21"; + case KEY_F22: return "F22"; + case KEY_F23: return "F23"; + case KEY_F24: return "F24"; + + case KEY_NUMLOCK: return "NumLock"; + case KEY_SCROLLLOCK: return "ScrollLock"; + case KEY_LCONTROL: return "LCtrl"; + case KEY_RCONTROL: return "RCtrl"; + case KEY_LALT: return "LAlt"; + case KEY_RALT: return "RAlt"; + case KEY_LSHIFT: return "LShift"; + case KEY_RSHIFT: return "RShift"; + + case KEY_WIN_LWINDOW: return "LWin"; + case KEY_WIN_RWINDOW: return "RWin"; + case KEY_WIN_APPS: return "Apps"; + } + + static char returnString[5]; + dSprintf( returnString, sizeof( returnString ), "%c", Input::getAscii( key, STATE_UPPER ) ); + return returnString; +} +#endif // LOG_INPUT + +//------------------------------------------------------------------------------ +const char* DInputDevice::getJoystickAxesString() +{ + if ( mDeviceType != JoystickDeviceType ) + return( "" ); + + U32 axisCount = mDeviceCaps.dwAxes; + char buf[64]; + dSprintf( buf, sizeof( buf ), "%d", axisCount ); + for ( U32 i = 0; i < mObjCount; i++ ) + { + switch ( mObjInfo[i].mInst ) + { + case SI_XAXIS: + dStrcat( buf, "\tX" ); + break; + case SI_YAXIS: + dStrcat( buf, "\tY" ); + break; + case SI_ZAXIS: + dStrcat( buf, "\tZ" ); + break; + case SI_RXAXIS: + dStrcat( buf, "\tR" ); + break; + case SI_RYAXIS: + dStrcat( buf, "\tU" ); + break; + case SI_RZAXIS: + dStrcat( buf, "\tV" ); + break; + case SI_SLIDER: + dStrcat( buf, "\tS" ); + break; + } + } + + char* returnString = Con::getReturnBuffer( dStrlen( buf ) + 1 ); + dStrcpy( returnString, buf ); + return( returnString ); +} + +//------------------------------------------------------------------------------ +bool DInputDevice::joystickDetected() +{ + return( smDeviceCount[ JoystickDeviceType ] > 0 ); +} + + + diff --git a/Engine/source/platformWin32/winDInputDevice.h b/Engine/source/platformWin32/winDInputDevice.h new file mode 100644 index 000000000..46a9d1a60 --- /dev/null +++ b/Engine/source/platformWin32/winDInputDevice.h @@ -0,0 +1,162 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _WINDINPUTDEVICE_H_ +#define _WINDINPUTDEVICE_H_ + +#ifndef _PLATFORMWIN32_H_ +#include "platformWin32/platformWin32.h" +#endif +#ifndef _PLATFORMINPUT_H_ +#include "platform/platformInput.h" +#endif +#ifndef _EVENT_H_ +#include "platform/event.h" +#endif + +#define DIRECTINPUT_VERSION 0x0800 +#include + + +class DInputDevice : public InputDevice +{ + public: + static LPDIRECTINPUT8 smDInputInterface; + + protected: + enum Constants + { + QUEUED_BUFFER_SIZE = 128, + + SIZEOF_BUTTON = 1, // size of an object's data in bytes + SIZEOF_KEY = 1, + SIZEOF_AXIS = 4, + SIZEOF_POV = 4, + }; + + static U8 smDeviceCount[ NUM_INPUT_DEVICE_TYPES ]; + static bool smInitialized; + + /// Are we an XInput device? + bool mIsXInput; + + //-------------------------------------- + LPDIRECTINPUTDEVICE8 mDevice; + DIDEVICEINSTANCE mDeviceInstance; + DIDEVCAPS mDeviceCaps; + U8 mDeviceType; + U8 mDeviceID; + + bool mAcquired; + bool mNeedSync; + + LPDIRECTINPUTEFFECT mForceFeedbackEffect; ///< Holds our DirectInput FF Effect + DWORD mNumForceFeedbackAxes; ///< # axes (we only support 0, 1, or 2 + DWORD mForceFeedbackAxes[2]; ///< Force Feedback axes offsets into DIOBJECTFORMAT + + //-------------------------------------- + DIDEVICEOBJECTINSTANCE* mObjInstance; + DIOBJECTDATAFORMAT* mObjFormat; + ObjInfo* mObjInfo; + U8* mObjBuffer1; // polled device input buffers + U8* mObjBuffer2; + U8* mPrevObjBuffer; // points to buffer 1 or 2 + + // Hack for POV + S32 mPrevPOVPos; + + U32 mObjBufferSize; // size of objBuffer* + U32 mObjCount; // number of objects on this device + U32 mObjEnumCount; // used during enumeration ONLY + U32 mObjBufferOfs; // used during enumeration ONLY + + static BOOL CALLBACK EnumObjectsProc( const DIDEVICEOBJECTINSTANCE *doi, LPVOID pvRef ); + + bool enumerateObjects(); + bool processAsync(); + bool processImmediate(); + + DWORD findObjInstance( DWORD offset ); + bool buildEvent( DWORD offset, S32 newData, S32 oldData ); + + public: + DInputDevice( const DIDEVICEINSTANCE* deviceInst ); + ~DInputDevice(); + + static void init(); + + bool create(); + void destroy(); + + bool acquire(); + bool unacquire(); + + bool isAcquired(); + bool isPolled(); + + U8 getDeviceType(); + U8 getDeviceID(); + + const char* getName(); + const char* getProductName(); + + // Constant Effect Force Feedback + void rumble( float x, float y ); + + // Console interface functions: + const char* getJoystickAxesString(); + static bool joystickDetected(); + // + + bool process(); +}; + +//------------------------------------------------------------------------------ +inline bool DInputDevice::isAcquired() +{ + return mAcquired; +} + +//------------------------------------------------------------------------------ +inline bool DInputDevice::isPolled() +{ + //return true; + return ( mDeviceCaps.dwFlags & DIDC_POLLEDDEVICE ) != 0; +} + +//------------------------------------------------------------------------------ +inline U8 DInputDevice::getDeviceType() +{ + return mDeviceType; +} + +//------------------------------------------------------------------------------ +inline U8 DInputDevice::getDeviceID() +{ + return mDeviceID; +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +InputObjectInstances DIK_to_Key( U8 dikCode ); +U8 Key_to_DIK( U16 keyCode ); +#endif // _H_WINDINPUTDEVICE_ diff --git a/Engine/source/platformWin32/winDirectInput.cpp b/Engine/source/platformWin32/winDirectInput.cpp new file mode 100644 index 000000000..72254c17c --- /dev/null +++ b/Engine/source/platformWin32/winDirectInput.cpp @@ -0,0 +1,954 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformWin32/platformWin32.h" +#include "platformWin32/winDirectInput.h" +#include "platformWin32/winDInputDevice.h" +#include "platform/event.h" +#include "console/console.h" +#include "console/consoleTypes.h" +#include "sim/actionMap.h" + +#include + +//------------------------------------------------------------------------------ +// Static class variables: +bool DInputManager::smJoystickEnabled = true; +bool DInputManager::smXInputEnabled = true; + +// Type definitions: +typedef HRESULT (WINAPI* FN_DirectInputCreate)(HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN punkOuter); + +//------------------------------------------------------------------------------ +DInputManager::DInputManager() +{ + mEnabled = false; + mDInputLib = NULL; + mDInputInterface = NULL; + mJoystickActive = mXInputActive = true; + mXInputLib = NULL; + + for(S32 i=0; i<4; i++) + mLastDisconnectTime[i] = -1; +} + +//------------------------------------------------------------------------------ +void DInputManager::init() +{ + Con::addVariable( "pref::Input::JoystickEnabled", TypeBool, &smJoystickEnabled, + "@brief If true, the joystick is currently enabled.\n\n" + "@ingroup Game"); +} + +//------------------------------------------------------------------------------ +bool DInputManager::enable() +{ + FN_DirectInputCreate fnDInputCreate; + + disable(); + + // Dynamically load the XInput 9 DLL and cache function pointers to the + // two APIs we use +#ifdef LOG_INPUT + Input::log( "Enabling XInput...\n" ); +#endif + mXInputLib = LoadLibrary( dT("xinput9_1_0.dll") ); + if ( mXInputLib ) + { + mfnXInputGetState = (FN_XInputGetState) GetProcAddress( mXInputLib, "XInputGetState" ); + mfnXInputSetState = (FN_XInputSetState) GetProcAddress( mXInputLib, "XInputSetState" ); + if ( mfnXInputGetState && mfnXInputSetState ) + { +#ifdef LOG_INPUT + Input::log( "XInput detected.\n" ); +#endif + mXInputStateReset = true; + mXInputDeadZoneOn = true; + smXInputEnabled = true; + } + } + else + { +#ifdef LOG_INPUT + Input::log( "XInput was not found.\n" ); + mXInputStateReset = false; + mXInputDeadZoneOn = false; +#endif + } + +#ifdef LOG_INPUT + Input::log( "Enabling DirectInput...\n" ); +#endif + mDInputLib = LoadLibrary( dT("DInput8.dll") ); + if ( mDInputLib ) + { + fnDInputCreate = (FN_DirectInputCreate) GetProcAddress( mDInputLib, "DirectInput8Create" ); + if ( fnDInputCreate ) + { + bool result = SUCCEEDED( fnDInputCreate( winState.appInstance, DIRECTINPUT_VERSION, IID_IDirectInput8, reinterpret_cast(&mDInputInterface), NULL )); + if ( result ) + { +#ifdef LOG_INPUT + Input::log( "DirectX 8 or greater detected.\n" ); +#endif + } + + if ( result ) + { + enumerateDevices(); + mEnabled = true; + return true; + } + } + } + + disable(); + +#ifdef LOG_INPUT + Input::log( "Failed to enable DirectInput.\n" ); +#endif + + return false; +} + +//------------------------------------------------------------------------------ + +void DInputManager::disable() +{ + unacquire( SI_ANY, SI_ANY ); + + DInputDevice* dptr; + iterator ptr = begin(); + while ( ptr != end() ) + { + dptr = dynamic_cast( *ptr ); + if ( dptr ) + { + removeObject( dptr ); + //if ( dptr->getManager() ) + //dptr->getManager()->unregisterObject( dptr ); + delete dptr; + ptr = begin(); + } + else + ptr++; + } + + if ( mDInputInterface ) + { + mDInputInterface->Release(); + mDInputInterface = NULL; + } + + if ( mDInputLib ) + { + FreeLibrary( mDInputLib ); + mDInputLib = NULL; + } + + if ( mfnXInputGetState ) + { + mXInputStateReset = true; + mfnXInputGetState = NULL; + mfnXInputSetState = NULL; + } + + if ( mXInputLib ) + { + FreeLibrary( mXInputLib ); + mXInputLib = NULL; + } + + mEnabled = false; +} + +//------------------------------------------------------------------------------ +void DInputManager::onDeleteNotify( SimObject* object ) +{ + Parent::onDeleteNotify( object ); +} + +//------------------------------------------------------------------------------ +bool DInputManager::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + acquire( SI_ANY, SI_ANY ); + return true; +} + +//------------------------------------------------------------------------------ +void DInputManager::onRemove() +{ + unacquire( SI_ANY, SI_ANY ); + Parent::onRemove(); +} + +//------------------------------------------------------------------------------ +bool DInputManager::acquire( U8 deviceType, U8 deviceID ) +{ + bool anyActive = false; + DInputDevice* dptr; + for ( iterator ptr = begin(); ptr != end(); ptr++ ) + { + dptr = dynamic_cast( *ptr ); + if ( dptr + && ( ( deviceType == SI_ANY ) || ( dptr->getDeviceType() == deviceType ) ) + && ( ( deviceID == SI_ANY ) || ( dptr->getDeviceID() == deviceID ) ) ) + { + if ( dptr->acquire() ) + anyActive = true; + } + } + + return anyActive; +} + +//------------------------------------------------------------------------------ +void DInputManager::unacquire( U8 deviceType, U8 deviceID ) +{ + DInputDevice* dptr; + for ( iterator ptr = begin(); ptr != end(); ptr++ ) + { + dptr = dynamic_cast( *ptr ); + if ( dptr + && ( ( deviceType == SI_ANY ) || ( dptr->getDeviceType() == deviceType ) ) + && ( ( deviceID == SI_ANY ) || ( dptr->getDeviceID() == deviceID ) ) ) + dptr->unacquire(); + } +} + +//------------------------------------------------------------------------------ +void DInputManager::process() +{ + // Because the XInput APIs manage everything for all four controllers trivially, + // we don't need the abstraction of a DInputDevice for an unknown number of devices + // with varying buttons & whistles... nice and easy! + if ( isXInputActive() ) + processXInput(); + + DInputDevice* dptr; + for ( iterator ptr = begin(); ptr != end(); ptr++ ) + { + dptr = dynamic_cast( *ptr ); + if ( dptr ) + dptr->process(); + } +} + +//------------------------------------------------------------------------------ +void DInputManager::enumerateDevices() +{ + if ( mDInputInterface ) + { +#ifdef LOG_INPUT + Input::log( "Enumerating input devices...\n" ); +#endif + + DInputDevice::init(); + DInputDevice::smDInputInterface = mDInputInterface; + mDInputInterface->EnumDevices( DI8DEVTYPE_KEYBOARD, EnumDevicesProc, this, DIEDFL_ATTACHEDONLY ); + mDInputInterface->EnumDevices( DI8DEVTYPE_MOUSE, EnumDevicesProc, this, DIEDFL_ATTACHEDONLY ); + mDInputInterface->EnumDevices( DI8DEVCLASS_GAMECTRL, EnumDevicesProc, this, DIEDFL_ATTACHEDONLY ); + } +} + +//------------------------------------------------------------------------------ +BOOL CALLBACK DInputManager::EnumDevicesProc( const DIDEVICEINSTANCE* pddi, LPVOID pvRef ) +{ + DInputManager* manager = (DInputManager*) pvRef; + DInputDevice* newDevice = new DInputDevice( pddi ); + manager->addObject( newDevice ); + if ( !newDevice->create() ) + { + manager->removeObject( newDevice ); + delete newDevice; + } + + return (DIENUM_CONTINUE); +} + +//------------------------------------------------------------------------------ +bool DInputManager::enableJoystick() +{ + DInputManager* mgr = dynamic_cast( Input::getManager() ); + if ( !mgr || !mgr->isEnabled() ) + return( false ); + + if ( smJoystickEnabled && mgr->isJoystickActive() ) + return( true ); + + smJoystickEnabled = true; + if ( Input::isActive() ) + smJoystickEnabled = mgr->activateJoystick(); + + if ( smJoystickEnabled ) + { + Con::printf( "DirectInput joystick enabled." ); +#ifdef LOG_INPUT + Input::log( "Joystick enabled.\n" ); +#endif + } + else + { + Con::warnf( "DirectInput joystick failed to enable!" ); +#ifdef LOG_INPUT + Input::log( "Joystick failed to enable!\n" ); +#endif + } + + return( smJoystickEnabled ); +} + +//------------------------------------------------------------------------------ +void DInputManager::disableJoystick() +{ + DInputManager* mgr = dynamic_cast( Input::getManager() ); + if ( !mgr || !mgr->isEnabled() || !smJoystickEnabled ) + return; + + mgr->deactivateJoystick(); + smJoystickEnabled = false; + Con::printf( "DirectInput joystick disabled." ); +#ifdef LOG_INPUT + Input::log( "Joystick disabled.\n" ); +#endif +} + +//------------------------------------------------------------------------------ +bool DInputManager::isJoystickEnabled() +{ + return( smJoystickEnabled ); +} + +//------------------------------------------------------------------------------ +bool DInputManager::activateJoystick() +{ + if ( !mEnabled || !Input::isActive() || !smJoystickEnabled ) + return( false ); + + mJoystickActive = acquire( JoystickDeviceType, SI_ANY ); +#ifdef LOG_INPUT + Input::log( mJoystickActive ? "Joystick activated.\n" : "Joystick failed to activate!\n" ); +#endif + return( mJoystickActive ); +} + +//------------------------------------------------------------------------------ +void DInputManager::deactivateJoystick() +{ + if ( mEnabled && mJoystickActive ) + { + unacquire( JoystickDeviceType, SI_ANY ); + mJoystickActive = false; +#ifdef LOG_INPUT + Input::log( "Joystick deactivated.\n" ); +#endif + } +} + +//------------------------------------------------------------------------------ +const char* DInputManager::getJoystickAxesString( U32 deviceID ) +{ + DInputDevice* dptr; + for ( iterator ptr = begin(); ptr != end(); ptr++ ) + { + dptr = dynamic_cast( *ptr ); + if ( dptr && ( dptr->getDeviceType() == JoystickDeviceType ) && ( dptr->getDeviceID() == deviceID ) ) + return( dptr->getJoystickAxesString() ); + } + + return( "" ); +} + +//------------------------------------------------------------------------------ +bool DInputManager::enableXInput() +{ + // Largely, this series of functions is identical to the Joystick versions, + // except that XInput cannot be "activated" or "deactivated". You either have + // the DLL or you don't. Beyond that, you have up to four controllers + // connected at any given time + + DInputManager* mgr = dynamic_cast( Input::getManager() ); + if ( !mgr || !mgr->isEnabled() ) + return( false ); + + if ( mgr->isXInputActive() ) + return( true ); + + if ( Input::isActive() ) + mgr->activateXInput(); + + if ( smXInputEnabled ) + { + Con::printf( "XInput enabled." ); +#ifdef LOG_INPUT + Input::log( "XInput enabled.\n" ); +#endif + } + else + { + Con::warnf( "XInput failed to enable!" ); +#ifdef LOG_INPUT + Input::log( "XInput failed to enable!\n" ); +#endif + } + + return( smXInputEnabled ); +} + +//------------------------------------------------------------------------------ +void DInputManager::disableXInput() +{ + DInputManager* mgr = dynamic_cast( Input::getManager() ); + if ( !mgr || !mgr->isEnabled()) + return; + + mgr->deactivateXInput(); + Con::printf( "XInput disabled." ); +#ifdef LOG_INPUT + Input::log( "XInput disabled.\n" ); +#endif +} + +//------------------------------------------------------------------------------ +bool DInputManager::isXInputEnabled() +{ + return( smXInputEnabled ); +} + +//------------------------------------------------------------------------------ +bool DInputManager::isXInputConnected(int controllerID) +{ + return( mXInputStateNew[controllerID].bConnected ); +} + +int DInputManager::getXInputState(int controllerID, int property, bool current) +{ + int retVal; + + switch(property) + { +#define CHECK_PROP_ANALOG(prop, stateTest) \ + case prop: (current) ? retVal = mXInputStateNew[controllerID].state.Gamepad.##stateTest : retVal = mXInputStateOld[controllerID].state.Gamepad.##stateTest; return retVal; + + CHECK_PROP_ANALOG(XI_THUMBLX, sThumbLX) + CHECK_PROP_ANALOG(XI_THUMBLY, sThumbLY) + CHECK_PROP_ANALOG(XI_THUMBRX, sThumbRX) + CHECK_PROP_ANALOG(XI_THUMBRY, sThumbRY) + CHECK_PROP_ANALOG(XI_LEFT_TRIGGER, bLeftTrigger) + CHECK_PROP_ANALOG(XI_RIGHT_TRIGGER, bRightTrigger) + +#undef CHECK_PROP_ANALOG + +#define CHECK_PROP_DIGITAL(prop, stateTest) \ + case prop: (current) ? retVal = (( mXInputStateNew[controllerID].state.Gamepad.wButtons & stateTest ) != 0 ) : retVal = (( mXInputStateOld[controllerID].state.Gamepad.wButtons & stateTest ) != 0 ); return retVal; + + CHECK_PROP_DIGITAL(SI_UPOV, XINPUT_GAMEPAD_DPAD_UP) + CHECK_PROP_DIGITAL(SI_DPOV, XINPUT_GAMEPAD_DPAD_DOWN) + CHECK_PROP_DIGITAL(SI_LPOV, XINPUT_GAMEPAD_DPAD_LEFT) + CHECK_PROP_DIGITAL(SI_RPOV, XINPUT_GAMEPAD_DPAD_RIGHT) + CHECK_PROP_DIGITAL(XI_START, XINPUT_GAMEPAD_START) + CHECK_PROP_DIGITAL(XI_BACK, XINPUT_GAMEPAD_BACK) + CHECK_PROP_DIGITAL(XI_LEFT_THUMB, XINPUT_GAMEPAD_LEFT_THUMB) + CHECK_PROP_DIGITAL(XI_RIGHT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB) + CHECK_PROP_DIGITAL(XI_LEFT_SHOULDER, XINPUT_GAMEPAD_LEFT_SHOULDER) + CHECK_PROP_DIGITAL(XI_RIGHT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER) + CHECK_PROP_DIGITAL(XI_A, XINPUT_GAMEPAD_A) + CHECK_PROP_DIGITAL(XI_B, XINPUT_GAMEPAD_B) + CHECK_PROP_DIGITAL(XI_X, XINPUT_GAMEPAD_X) + CHECK_PROP_DIGITAL(XI_Y, XINPUT_GAMEPAD_Y) + +#undef CHECK_PROP_DIGITAL + } + + return -1; +} + +//------------------------------------------------------------------------------ +bool DInputManager::activateXInput() +{ + if ( !mEnabled || !Input::isActive()) + return( false ); + + mXInputActive = true; //acquire( GamepadDeviceType, SI_ANY ); +#ifdef LOG_INPUT + Input::log( mXInputActive ? "XInput activated.\n" : "XInput failed to activate!\n" ); +#endif + return( mXInputActive ); +} + +//------------------------------------------------------------------------------ +void DInputManager::deactivateXInput() +{ + if ( mEnabled && mXInputActive ) + { + unacquire( GamepadDeviceType, SI_ANY ); + mXInputActive = false; +#ifdef LOG_INPUT + Input::log( "XInput deactivated.\n" ); +#endif + } +} + +//------------------------------------------------------------------------------ +bool DInputManager::rumble( const char *pDeviceName, float x, float y ) +{ + // Determine the device + U32 deviceType; + U32 deviceInst; + + // Find the requested device + if ( !ActionMap::getDeviceTypeAndInstance(pDeviceName, deviceType, deviceInst) ) + { + Con::printf("DInputManager::rumble: unknown device: %s", pDeviceName); + return false; + } + + // clamp (x, y) to the range of [0 ... 1] each + x = mClampF(x, 0.f, 1.f); + y = mClampF(y, 0.f, 1.f); + + // Easy path for xinput devices. + if(deviceType == GamepadDeviceType) + { + XINPUT_VIBRATION vibration; + vibration.wLeftMotorSpeed = static_cast(x * 65535); + vibration.wRightMotorSpeed = static_cast(y * 65535); + return ( mfnXInputSetState(deviceInst, &vibration) == ERROR_SUCCESS ); + } + + switch ( deviceType ) + { + case JoystickDeviceType: + + // Find the device and shake it! + DInputDevice* dptr; + for ( iterator ptr = begin(); ptr != end(); ptr++ ) + { + dptr = dynamic_cast( *ptr ); + if ( dptr ) + { + if (deviceType == dptr->getDeviceType() && deviceInst == dptr->getDeviceID()) + { + dptr->rumble(x, y); + return true; + } + } + } + + // We should never get here... something's really messed up + Con::errorf( "DInputManager::rumbleJoystick - Couldn't find device to rumble! This should never happen!\n" ); + return false; + + default: + Con::printf("DInputManager::rumble - only supports joystick and xinput/gamepad devices"); + return false; + } +} + +void DInputManager::buildXInputEvent( U32 deviceInst, InputEventType objType, InputObjectInstances objInst, InputActionType action, float fValue ) +{ + InputEventInfo newEvent; + + newEvent.deviceType = GamepadDeviceType; + newEvent.deviceInst = deviceInst; + newEvent.objType = objType; + newEvent.objInst = objInst; + newEvent.action = action; + newEvent.fValue = fValue; + + newEvent.postToSignal(Input::smInputEvent); +} + +// The next three functions: fireXInputConnectEvent, fireXInputMoveEvent, and fireXInputButtonEvent +// determine whether a "delta" has occurred between the last captured controller state and the +// currently captured controller state and only if so, do we fire an event. The shortcutter +// "mXInputStateReset" is the exception and is true whenever DirectInput gets reset (because +// the user ALT-TABBED away, for example). That means that after every context switch, +// you will get a full set of updates on the "true" state of the controller. +inline void DInputManager::fireXInputConnectEvent( int controllerID, bool condition, bool connected ) +{ + if ( mXInputStateReset || condition ) + { +#ifdef LOG_INPUT + Input::log( "EVENT (XInput): xinput%d CONNECT %s\n", controllerID, connected ? "make" : "break" ); +#endif + buildXInputEvent( controllerID, SI_BUTTON, XI_CONNECT, connected ? SI_MAKE : SI_BREAK, 0); + } +} + +inline void DInputManager::fireXInputMoveEvent( int controllerID, bool condition, InputObjectInstances objInst, float fValue ) +{ + if ( mXInputStateReset || condition ) + { +#ifdef LOG_INPUT + char *objName; + switch (objInst) + { + case XI_THUMBLX: objName = "THUMBLX"; break; + case XI_THUMBLY: objName = "THUMBLY"; break; + case XI_THUMBRX: objName = "THUMBRX"; break; + case XI_THUMBRY: objName = "THUMBRY"; break; + case XI_LEFT_TRIGGER: objName = "LEFT_TRIGGER"; break; + case XI_RIGHT_TRIGGER: objName = "RIGHT_TRIGGER"; break; + default: objName = "UNKNOWN"; break; + } + + Input::log( "EVENT (XInput): xinput%d %s MOVE %.1f.\n", controllerID, objName, fValue ); +#endif + buildXInputEvent( controllerID, SI_AXIS, objInst, SI_MOVE, fValue ); + } +} + +inline void DInputManager::fireXInputButtonEvent( int controllerID, bool forceFire, int button, InputObjectInstances objInst ) +{ + if ( mXInputStateReset || forceFire || ((mXInputStateNew[controllerID].state.Gamepad.wButtons & button) != (mXInputStateOld[controllerID].state.Gamepad.wButtons & button)) ) + { +#ifdef LOG_INPUT + char *objName; + switch (objInst) + { + /* + case XI_DPAD_UP: objName = "DPAD_UP"; break; + case XI_DPAD_DOWN: objName = "DPAD_DOWN"; break; + case XI_DPAD_LEFT: objName = "DPAD_LEFT"; break; + case XI_DPAD_RIGHT: objName = "DPAD_RIGHT"; break; + */ + case XI_START: objName = "START"; break; + case XI_BACK: objName = "BACK"; break; + case XI_LEFT_THUMB: objName = "LEFT_THUMB"; break; + case XI_RIGHT_THUMB: objName = "RIGHT_THUMB"; break; + case XI_LEFT_SHOULDER: objName = "LEFT_SHOULDER"; break; + case XI_RIGHT_SHOULDER: objName = "RIGHT_SHOULDER"; break; + case XI_A: objName = "A"; break; + case XI_B: objName = "B"; break; + case XI_X: objName = "X"; break; + case XI_Y: objName = "Y"; break; + default: objName = "UNKNOWN"; break; + } + + Input::log( "EVENT (XInput): xinput%d %s %s\n", controllerID, objName, ((mXInputStateNew[controllerID].state.Gamepad.wButtons & button) != 0) ? "make" : "break" ); +#endif + InputActionType action = ((mXInputStateNew[controllerID].state.Gamepad.wButtons & button) != 0) ? SI_MAKE : SI_BREAK; + buildXInputEvent( controllerID, SI_BUTTON, objInst, action, ( action == SI_MAKE ? 1 : 0 ) ); + } +} + +void DInputManager::processXInput( void ) +{ + const U32 curTime = Platform::getRealMilliseconds(); + + // We only want to check one disconnected device per frame. + bool foundDisconnected = false; + + if ( mfnXInputGetState ) + { + for ( int i=0; i<4; i++ ) + { + // Calling XInputGetState on a disconnected controller takes a fair + // amount of time (probably because it tries to locate it), so we + // add a delay - only check every 250ms or so. + if(mLastDisconnectTime[i] != -1) + { + // If it's not -1, then it was disconnected list time we checked. + // So skip until it's time. + if((curTime-mLastDisconnectTime[i]) < csmDisconnectedSkipDelay) + { + continue; + } + + // If we already checked a disconnected controller, don't try any + // further potentially disconnected devices. + if(foundDisconnected) + { + // If we're skipping this, defer it out by the skip delay + // so we don't get clumped checks. + mLastDisconnectTime[i] += csmDisconnectedSkipDelay; + continue; + } + } + + mXInputStateOld[i] = mXInputStateNew[i]; + mXInputStateNew[i].bConnected = ( mfnXInputGetState( i, &mXInputStateNew[i].state ) == ERROR_SUCCESS ); + + // Update the last connected time. + if(mXInputStateNew[i].bConnected) + mLastDisconnectTime[i] = -1; + else + { + foundDisconnected = true; + mLastDisconnectTime[i] = curTime; + } + + // trim the controller's thumbsticks to zero if they are within the deadzone + if( mXInputDeadZoneOn ) + { + // Zero value if thumbsticks are within the dead zone + if( (mXInputStateNew[i].state.Gamepad.sThumbLX < XINPUT_DEADZONE && mXInputStateNew[i].state.Gamepad.sThumbLX > -XINPUT_DEADZONE) && + (mXInputStateNew[i].state.Gamepad.sThumbLY < XINPUT_DEADZONE && mXInputStateNew[i].state.Gamepad.sThumbLY > -XINPUT_DEADZONE) ) + { + mXInputStateNew[i].state.Gamepad.sThumbLX = 0; + mXInputStateNew[i].state.Gamepad.sThumbLY = 0; + } + + if( (mXInputStateNew[i].state.Gamepad.sThumbRX < XINPUT_DEADZONE && mXInputStateNew[i].state.Gamepad.sThumbRX > -XINPUT_DEADZONE) && + (mXInputStateNew[i].state.Gamepad.sThumbRY < XINPUT_DEADZONE && mXInputStateNew[i].state.Gamepad.sThumbRY > -XINPUT_DEADZONE) ) + { + mXInputStateNew[i].state.Gamepad.sThumbRX = 0; + mXInputStateNew[i].state.Gamepad.sThumbRY = 0; + } + } + + // this controller was connected or disconnected + bool bJustConnected = ( ( mXInputStateOld[i].bConnected != mXInputStateNew[i].bConnected ) && ( mXInputStateNew[i].bConnected ) ); + fireXInputConnectEvent( i, (mXInputStateOld[i].bConnected != mXInputStateNew[i].bConnected), mXInputStateNew[i].bConnected ); + + // If this controller is disconnected, stop reporting events for it + if ( !mXInputStateNew[i].bConnected ) + continue; + + // == LEFT THUMBSTICK == + fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.sThumbLX != mXInputStateOld[i].state.Gamepad.sThumbLX), XI_THUMBLX, (mXInputStateNew[i].state.Gamepad.sThumbLX / 32768.0f) ); + fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.sThumbLY != mXInputStateOld[i].state.Gamepad.sThumbLY), XI_THUMBLY, (mXInputStateNew[i].state.Gamepad.sThumbLY / 32768.0f) ); + + // == RIGHT THUMBSTICK == + fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.sThumbRX != mXInputStateOld[i].state.Gamepad.sThumbRX), XI_THUMBRX, (mXInputStateNew[i].state.Gamepad.sThumbRX / 32768.0f) ); + fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.sThumbRY != mXInputStateOld[i].state.Gamepad.sThumbRY), XI_THUMBRY, (mXInputStateNew[i].state.Gamepad.sThumbRY / 32768.0f) ); + + // == LEFT & RIGHT REAR TRIGGERS == + fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.bLeftTrigger != mXInputStateOld[i].state.Gamepad.bLeftTrigger), XI_LEFT_TRIGGER, (mXInputStateNew[i].state.Gamepad.bLeftTrigger / 255.0f) ); + fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.bRightTrigger != mXInputStateOld[i].state.Gamepad.bRightTrigger), XI_RIGHT_TRIGGER, (mXInputStateNew[i].state.Gamepad.bRightTrigger / 255.0f) ); + + // == BUTTONS: DPAD == + fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_DPAD_UP, SI_UPOV ); + fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_DPAD_DOWN, SI_DPOV ); + fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_DPAD_LEFT, SI_LPOV ); + fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_DPAD_RIGHT, SI_RPOV ); + + // == BUTTONS: START & BACK == + fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_START, XI_START ); + fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_BACK, XI_BACK ); + + // == BUTTONS: LEFT AND RIGHT THUMBSTICK == + fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_LEFT_THUMB, XI_LEFT_THUMB ); + fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_RIGHT_THUMB, XI_RIGHT_THUMB ); + + // == BUTTONS: LEFT AND RIGHT SHOULDERS (formerly WHITE and BLACK on Xbox 1) == + fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_LEFT_SHOULDER, XI_LEFT_SHOULDER ); + fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_RIGHT_SHOULDER, XI_RIGHT_SHOULDER ); + + // == BUTTONS: A, B, X, and Y == + fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_A, XI_A ); + fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_B, XI_B ); + fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_X, XI_X ); + fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_Y, XI_Y ); + } + + if ( mXInputStateReset ) + mXInputStateReset = false; + } +} +ConsoleFunction( enableJoystick, bool, 1, 1, "()" + "@brief Enables use of the joystick.\n\n" + "@note DirectInput must be enabled and active to use this function.\n\n" + "@ingroup Input") +{ + argc; argv; + return( DInputManager::enableJoystick() ); +} + +//------------------------------------------------------------------------------ +ConsoleFunction( disableJoystick, void, 1, 1,"()" + "@brief Disables use of the joystick.\n\n" + "@note DirectInput must be enabled and active to use this function.\n\n" + "@ingroup Input") +{ + argc; argv; + DInputManager::disableJoystick(); +} + +//------------------------------------------------------------------------------ +ConsoleFunction( isJoystickEnabled, bool, 1, 1, "()" + "@brief Queries input manager to see if a joystick is enabled\n\n" + "@return 1 if a joystick exists and is enabled, 0 if it's not.\n" + "@ingroup Input") +{ + argc; argv; + return DInputManager::isJoystickEnabled(); +} + +//------------------------------------------------------------------------------ +ConsoleFunction( enableXInput, bool, 1, 1, "()" + "@brief Enables XInput for Xbox 360 controllers.\n\n" + "@note XInput is enabled by default. Disable to use an Xbox 360 " + "Controller as a joystick device.\n\n" + "@ingroup Input") +{ + // Although I said above that you couldn't change the "activation" of XInput, + // you can enable and disable it. It gets enabled by default if you have the + // DLL. You would want to disable it if you have 360 controllers and want to + // read them as joysticks... why you'd want to do that is beyond me + + argc; argv; + return( DInputManager::enableXInput() ); +} + +//------------------------------------------------------------------------------ +ConsoleFunction( disableXInput, void, 1, 1, "()" + "@brief Disables XInput for Xbox 360 controllers.\n\n" + "@ingroup Input") +{ + argc; argv; + DInputManager::disableXInput(); +} + +//------------------------------------------------------------------------------ +ConsoleFunction( resetXInput, void, 1, 1, "()" + "@brief Rebuilds the XInput section of the InputManager\n\n" + "Requests a full refresh of events for all controllers. Useful when called at the beginning " + "of game code after actionMaps are set up to hook up all appropriate events.\n\n" + "@ingroup Input") +{ + // This function requests a full "refresh" of events for all controllers the + // next time we go through the input processing loop. This is useful to call + // at the beginning of your game code after your actionMap is set up to hook + // all of the appropriate events + + argc; argv; + DInputManager* mgr = dynamic_cast( Input::getManager() ); + if ( mgr && mgr->isEnabled() ) + mgr->resetXInput(); +} + +//------------------------------------------------------------------------------ +ConsoleFunction( isXInputConnected, bool, 2, 2, "( int controllerID )" + "@brief Checks to see if an Xbox 360 controller is connected\n\n" + "@param controllerID Zero-based index of the controller to check.\n" + "@return 1 if the controller is connected, 0 if it isn't, and 205 if XInput " + "hasn't been initialized." + "@ingroup Input") +{ + argc; argv; + DInputManager* mgr = dynamic_cast( Input::getManager() ); + if ( mgr && mgr->isEnabled() ) return mgr->isXInputConnected( atoi( argv[1] ) ); + return false; +} + +//------------------------------------------------------------------------------ +ConsoleFunction( getXInputState, int, 3, 4, "( int controllerID, string property, bool current )" + "@brief Queries the current state of a connected Xbox 360 controller.\n\n" + "XInput Properties:\n\n" + " - XI_THUMBLX, XI_THUMBLY - X and Y axes of the left thumbstick. \n" + " - XI_THUMBRX, XI_THUMBRY - X and Y axes of the right thumbstick. \n" + " - XI_LEFT_TRIGGER, XI_RIGHT_TRIGGER - Left and Right triggers. \n" + " - SI_UPOV, SI_DPOV, SI_LPOV, SI_RPOV - Up, Down, Left, and Right on the directional pad.\n" + " - XI_START, XI_BACK - The Start and Back buttons.\n" + " - XI_LEFT_THUMB, XI_RIGHT_THUMB - Clicking in the left and right thumbstick.\n" + " - XI_LEFT_SHOULDER, XI_RIGHT_SHOULDER - Left and Right bumpers.\n" + " - XI_A, XI_B , XI_X, XI_Y - The A, B, X, and Y buttons.\n\n" + "@param controllerID Zero-based index of the controller to return information about.\n" + "@param property Name of input action being queried, such as \"XI_THUMBLX\".\n" + "@param current True checks current device in action.\n" + "@return Button queried - 1 if the button is pressed, 0 if it's not.\n" + "@return Thumbstick queried - Int representing displacement from rest position." + "@return %Trigger queried - Int from 0 to 255 representing how far the trigger is displaced." + "@ingroup Input") +{ + argc; argv; + DInputManager* mgr = dynamic_cast( Input::getManager() ); + + if ( !mgr || !mgr->isEnabled() ) + return -1; + + // Use a little bit of macro magic to simplify this otherwise monolothic + // block of code. +#define GET_XI_STATE(constName) \ + if (!dStricmp(argv[2], #constName)) \ + return mgr->getXInputState( dAtoi( argv[1] ), constName, ( dAtoi ( argv[3] ) == 1) ); + + GET_XI_STATE(XI_THUMBLX); + GET_XI_STATE(XI_THUMBLY); + GET_XI_STATE(XI_THUMBRX); + GET_XI_STATE(XI_THUMBRY); + GET_XI_STATE(XI_LEFT_TRIGGER); + GET_XI_STATE(XI_RIGHT_TRIGGER); + GET_XI_STATE(SI_UPOV); + GET_XI_STATE(SI_DPOV); + GET_XI_STATE(SI_LPOV); + GET_XI_STATE(SI_RPOV); + GET_XI_STATE(XI_START); + GET_XI_STATE(XI_BACK); + GET_XI_STATE(XI_LEFT_THUMB); + GET_XI_STATE(XI_RIGHT_THUMB); + GET_XI_STATE(XI_LEFT_SHOULDER); + GET_XI_STATE(XI_RIGHT_SHOULDER); + GET_XI_STATE(XI_A); + GET_XI_STATE(XI_B); + GET_XI_STATE(XI_X); + GET_XI_STATE(XI_Y); +#undef GET_XI_STATE + + return -1; +} + +//------------------------------------------------------------------------------ +ConsoleFunction( echoInputState, void, 1, 1, "()" + "@brief Prints information to the console stating if DirectInput and a Joystick are enabled and active.\n\n" + "@ingroup Input") +{ + argc; argv; + DInputManager* mgr = dynamic_cast( Input::getManager() ); + if ( mgr && mgr->isEnabled() ) + { + Con::printf( "DirectInput is enabled %s.", Input::isActive() ? "and active" : "but inactive" ); + Con::printf( "- Joystick is %sabled and %sactive.", + mgr->isJoystickEnabled() ? "en" : "dis", + mgr->isJoystickActive() ? "" : "in" ); + } + else + Con::printf( "DirectInput is not enabled." ); +} + +ConsoleFunction( rumble, void, 4, 4, "(string device, float xRumble, float yRumble)" + "@brief Activates the vibration motors in the specified controller.\n\n" + "The controller will constantly at it's xRumble and yRumble intensities until " + "changed or told to stop." + "Valid inputs for xRumble/yRumble are [0 - 1].\n" + "@param device Name of the device to rumble.\n" + "@param xRumble Intensity to apply to the left motor.\n" + "@param yRumble Intensity to apply to the right motor.\n" + "@note in an Xbox 360 controller, the left motor is low-frequency, " + "while the right motor is high-frequency." + "@ingroup Input") +{ + DInputManager* mgr = dynamic_cast( Input::getManager() ); + if ( mgr && mgr->isEnabled() ) + { + mgr->rumble(argv[1], dAtof(argv[2]), dAtof(argv[3])); + } + else + { + Con::printf( "DirectInput/XInput is not enabled." ); + } +} diff --git a/Engine/source/platformWin32/winDirectInput.h b/Engine/source/platformWin32/winDirectInput.h new file mode 100644 index 000000000..378cbec03 --- /dev/null +++ b/Engine/source/platformWin32/winDirectInput.h @@ -0,0 +1,132 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _WINDIRECTINPUT_H_ +#define _WINDIRECTINPUT_H_ + +#ifndef _PLATFORMWIN32_H_ +#include "platformWin32/platformWin32.h" +#endif +#ifndef _PLATFORMINPUT_H_ +#include "platform/platformInput.h" +#endif +#ifndef _WINDINPUTDEVICE_H_ +#include "platformWin32/winDInputDevice.h" +#endif + +#define DIRECTINPUT_VERSION 0x0800 +#include +#include + +// XInput related definitions +typedef DWORD (WINAPI* FN_XInputGetState)(DWORD dwUserIndex, XINPUT_STATE* pState); +typedef DWORD (WINAPI* FN_XInputSetState)(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration); +#define XINPUT_MAX_CONTROLLERS 4 // XInput handles up to 4 controllers +#define XINPUT_DEADZONE ( 0.24f * FLOAT(0x7FFF) ) // Default to 24% of the +/- 32767 range. This is a reasonable default value but can be altered if needed. +struct XINPUT_CONTROLLER_STATE +{ + XINPUT_STATE state; + bool bConnected; +}; + +//------------------------------------------------------------------------------ +class DInputManager : public InputManager +{ + private: + typedef SimGroup Parent; + + // XInput state + HMODULE mXInputLib; + FN_XInputGetState mfnXInputGetState; + FN_XInputSetState mfnXInputSetState; + XINPUT_CONTROLLER_STATE mXInputStateOld[XINPUT_MAX_CONTROLLERS]; + XINPUT_CONTROLLER_STATE mXInputStateNew[XINPUT_MAX_CONTROLLERS]; + U32 mLastDisconnectTime[XINPUT_MAX_CONTROLLERS]; + bool mXInputStateReset; + bool mXInputDeadZoneOn; + + /// Number of milliseconds to skip checking an xinput device if it was + /// disconnected on last check. + const static U32 csmDisconnectedSkipDelay = 250; + + HMODULE mDInputLib; + LPDIRECTINPUT8 mDInputInterface; + + static bool smJoystickEnabled; + static bool smXInputEnabled; + + bool mJoystickActive; + bool mXInputActive; + + void enumerateDevices(); + + static BOOL CALLBACK EnumDevicesProc( const DIDEVICEINSTANCE *pddi, LPVOID pvRef ); + + bool acquire( U8 deviceType, U8 deviceID ); + void unacquire( U8 deviceType, U8 deviceID ); + + // XInput worker functions + void buildXInputEvent( U32 deviceInst, InputEventType objType, InputObjectInstances objInst, InputActionType action, float fValue ); + void fireXInputConnectEvent( int controllerID, bool condition, bool connected ); + void fireXInputMoveEvent( int controllerID, bool condition, InputObjectInstances objInst, float fValue ); + void fireXInputButtonEvent( int controllerID, bool forceFire, int button, InputObjectInstances objInst ); + void processXInput(); + + public: + DInputManager(); + + bool enable(); + void disable(); + + void onDeleteNotify( SimObject* object ); + bool onAdd(); + void onRemove(); + + void process(); + + // DirectInput functions: + static void init(); + + static bool enableJoystick(); + static void disableJoystick(); + static bool isJoystickEnabled(); + bool activateJoystick(); + void deactivateJoystick(); + bool isJoystickActive() { return( mJoystickActive ); } + + static bool enableXInput(); + static void disableXInput(); + static bool isXInputEnabled(); + bool activateXInput(); + void deactivateXInput(); + bool isXInputActive() { return( mXInputActive ); } + void resetXInput() { mXInputStateReset = true; } + bool isXInputConnected(int controllerID); + int getXInputState(int controllerID, int property, bool current); + + // Console interface: + const char* getJoystickAxesString( U32 deviceID ); + + bool rumble( const char *pDeviceName, float x, float y ); +}; + +#endif // _H_WINDIRECTINPUT_ diff --git a/Engine/source/platformWin32/winDlibrary.cpp b/Engine/source/platformWin32/winDlibrary.cpp new file mode 100644 index 000000000..eae7b07c9 --- /dev/null +++ b/Engine/source/platformWin32/winDlibrary.cpp @@ -0,0 +1,92 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#define NOMINMAX +#define WIN32_LEAN_AND_MEAN +#include + +#include "platform/types.h" +#include "platform/platformDlibrary.h" + +class Win32DLibrary: public DLibrary +{ + HMODULE _handle; +public: + Win32DLibrary(); + ~Win32DLibrary(); + bool open(const char* file); + void close(); + void *bind(const char *name); +}; + +Win32DLibrary::Win32DLibrary() +{ + _handle = 0; +} + +Win32DLibrary::~Win32DLibrary() +{ + close(); +} + +bool Win32DLibrary::open(const char* file) +{ + // dlopen should also include the RTLD_LOCAL flag, but it seems to be + // missing from cygwin + _handle = LoadLibraryA(file); + if (!_handle) + return false; + bool (*open)() = (bool(*)())bind("dllopen"); + if (open && !(*open)()) { + FreeLibrary(_handle); + _handle = 0; + return false; + } + return true; +} + +void Win32DLibrary::close() +{ + if (_handle) { + void (*close)() = (void(*)())bind("dllclose"); + if (close) + (*close)(); + FreeLibrary(_handle); + _handle = 0; + } +} + +void* Win32DLibrary::bind(const char *name) +{ + return _handle? (void*)GetProcAddress(_handle,name): 0; +} + +DLibraryRef OsLoadLibrary(const char* file) +{ + Win32DLibrary* library = new Win32DLibrary(); + if (!library->open(file)) { + delete library; + return 0; + } + return library; +} + diff --git a/Engine/source/platformWin32/winExec.cpp b/Engine/source/platformWin32/winExec.cpp new file mode 100644 index 000000000..93357b1cb --- /dev/null +++ b/Engine/source/platformWin32/winExec.cpp @@ -0,0 +1,188 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformWin32/platformWin32.h" +#include "console/console.h" +#include "console/simBase.h" +#include "core/strings/unicode.h" +#include "platform/threads/thread.h" +#include "platform/threads/mutex.h" +#include "core/util/safeDelete.h" +#include "util/tempAlloc.h" + +//----------------------------------------------------------------------------- +// Thread for executing in +//----------------------------------------------------------------------------- + +class ExecuteThread : public Thread +{ + // [tom, 12/14/2006] mProcess is only used in the constructor before the thread + // is started and in the thread itself so we should be OK without a mutex. + HANDLE mProcess; + +public: + ExecuteThread(const char *executable, const char *args = NULL, const char *directory = NULL); + + virtual void run(void *arg = 0); +}; + +//----------------------------------------------------------------------------- +// Event for cleanup +//----------------------------------------------------------------------------- + +class ExecuteCleanupEvent : public SimEvent +{ + ExecuteThread *mThread; + bool mOK; + +public: + ExecuteCleanupEvent(ExecuteThread *thread, bool ok) + { + mThread = thread; + mOK = ok; + } + + virtual void process(SimObject *object) + { + if( Con::isFunction( "onExecuteDone" ) ) + Con::executef( "onExecuteDone", Con::getIntArg( mOK ) ); + SAFE_DELETE(mThread); + } +}; + +//----------------------------------------------------------------------------- + +ExecuteThread::ExecuteThread(const char *executable, const char *args /* = NULL */, const char *directory /* = NULL */) : Thread(0, NULL, false) +{ + SHELLEXECUTEINFO shl; + dMemset(&shl, 0, sizeof(shl)); + + shl.cbSize = sizeof(shl); + shl.fMask = SEE_MASK_NOCLOSEPROCESS; + + char exeBuf[1024]; + Platform::makeFullPathName(executable, exeBuf, sizeof(exeBuf)); + + TempAlloc< TCHAR > dirBuf( ( directory ? dStrlen( directory ) : 0 ) + 1 ); + dirBuf[ dirBuf.size - 1 ] = 0; + +#ifdef UNICODE + WCHAR exe[ 1024 ]; + convertUTF8toUTF16( exeBuf, exe, sizeof( exe ) / sizeof( exe[ 0 ] ) ); + + TempAlloc< WCHAR > argsBuf( ( args ? dStrlen( args ) : 0 ) + 1 ); + argsBuf[ argsBuf.size - 1 ] = 0; + + if( args ) + convertUTF8toUTF16( args, argsBuf, argsBuf.size ); + if( directory ) + convertUTF8toUTF16( directory, dirBuf, dirBuf.size ); +#else + char* exe = exeBuf; + char* argsBuf = args; + if( directory ) + dStrpcy( dirBuf, directory ); +#endif + + backslash( exe ); + backslash( dirBuf ); + + shl.lpVerb = TEXT( "open" ); + shl.lpFile = exe; + shl.lpParameters = argsBuf; + shl.lpDirectory = dirBuf; + + shl.nShow = SW_SHOWNORMAL; + + if(ShellExecuteEx(&shl) && shl.hProcess) + { + mProcess = shl.hProcess; + start(); + } +} + +void ExecuteThread::run(void *arg /* = 0 */) +{ + if(mProcess == NULL) + return; + + DWORD wait = WAIT_OBJECT_0 - 1; // i.e., not WAIT_OBJECT_0 + while(! checkForStop() && (wait = WaitForSingleObject(mProcess, 200)) != WAIT_OBJECT_0) ; + + Sim::postEvent(Sim::getRootGroup(), new ExecuteCleanupEvent(this, wait == WAIT_OBJECT_0), -1); +} + +//----------------------------------------------------------------------------- +// Console Functions +//----------------------------------------------------------------------------- + +ConsoleFunction(shellExecute, bool, 2, 4, "(string executable, string args, string directory)" + "@brief Launches an outside executable or batch file\n\n" + "@param executable Name of the executable or batch file\n" + "@param args Optional list of arguments, in string format, to pass to the executable\n" + "@param directory Optional string containing path to output or shell\n" + "@ingroup Platform") +{ + ExecuteThread *et = new ExecuteThread(argv[1], argc > 2 ? argv[2] : NULL, argc > 3 ? argv[3] : NULL); + if(! et->isAlive()) + { + delete et; + return false; + } + + return true; +} + +void Platform::openFolder(const char* path ) +{ + char filePath[1024]; + Platform::makeFullPathName(path, filePath, sizeof(filePath)); + +#ifdef UNICODE + WCHAR p[ 1024 ]; + convertUTF8toUTF16( filePath, p, sizeof( p ) / sizeof( p[ 0 ] ) ); +#else + char* p = filePath; +#endif + + backslash( p ); + + ::ShellExecute( NULL,TEXT("explore"),p, NULL, NULL, SW_SHOWNORMAL); +} + +void Platform::openFile(const char* path ) +{ + char filePath[1024]; + Platform::makeFullPathName(path, filePath, sizeof(filePath)); + +#ifdef UNICODE + WCHAR p[ 1024 ]; + convertUTF8toUTF16( filePath, p, sizeof( p ) / sizeof( p[ 0 ] ) ); +#else + char* p = filePath; +#endif + + backslash( p ); + + ::ShellExecute( NULL,TEXT("open"),p, NULL, NULL, SW_SHOWNORMAL); +} + diff --git a/Engine/source/platformWin32/winFileio.cpp b/Engine/source/platformWin32/winFileio.cpp new file mode 100644 index 000000000..255c8b83a --- /dev/null +++ b/Engine/source/platformWin32/winFileio.cpp @@ -0,0 +1,1477 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/strings/stringFunctions.h" +#include "platformWin32/platformWin32.h" +#include "core/fileio.h" +#include "core/util/tVector.h" +#include "core/stringTable.h" +#include "console/console.h" +#include "core/strings/unicode.h" +#include "util/tempAlloc.h" +#include "core/util/safeDelete.h" + +// Microsoft VC++ has this POSIX header in the wrong directory +#if defined(TORQUE_COMPILER_VISUALC) +#include +#elif defined (TORQUE_COMPILER_GCC) +#include +#include +#else +#include +#endif + +StringTableEntry Platform::createPlatformFriendlyFilename( const char *filename ) +{ + return StringTable->insert( filename ); +} + +//----------------------------------------------------------------------------- +bool dFileDelete(const char * name) +{ + AssertFatal( name != NULL, "dFileDelete - NULL file name" ); + + TempAlloc< TCHAR > buf( dStrlen( name ) + 1 ); + +#ifdef UNICODE + convertUTF8toUTF16( name, buf, buf.size ); +#else + dStrcpy( buf, name ); +#endif + + backslash( buf ); + if( Platform::isFile( name ) ) + return DeleteFile( buf ); + else + return RemoveDirectory( buf ); +} + +bool dFileRename(const char *oldName, const char *newName) +{ + AssertFatal( oldName != NULL && newName != NULL, "dFileRename - NULL file name" ); + + TempAlloc< TCHAR > oldf( dStrlen( oldName ) + 1 ); + TempAlloc< TCHAR > newf( dStrlen( newName ) + 1 ); + +#ifdef UNICODE + convertUTF8toUTF16( oldName, oldf, oldf.size ); + convertUTF8toUTF16( newName, newf, newf.size ); +#else + dStrcpy(oldf, oldName); + dStrcpy(newf, newName); +#endif + backslash(oldf); + backslash(newf); + + return MoveFile( oldf, newf ); +} + +bool dFileTouch(const char * name) +{ + AssertFatal( name != NULL, "dFileTouch - NULL file name" ); + + TempAlloc< TCHAR > buf( dStrlen( name ) + 1 ); + +#ifdef UNICODE + convertUTF8toUTF16( name, buf, buf.size ); +#else + dStrcpy( buf, name ); +#endif + + backslash( buf ); + FILETIME ftime; + GetSystemTimeAsFileTime( &ftime ); + HANDLE handle = CreateFile( buf, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, 0, NULL ); + if( handle == INVALID_HANDLE_VALUE ) + return false; + bool result = SetFileTime( handle, NULL, NULL, &ftime ); + CloseHandle( handle ); + + return result; +}; + +bool dPathCopy(const char *fromName, const char *toName, bool nooverwrite) +{ + AssertFatal( fromName != NULL && toName != NULL, "dPathCopy - NULL file name" ); + + TempAlloc< TCHAR > from( dStrlen( fromName ) + 1 ); + TempAlloc< TCHAR > to( dStrlen( toName ) + 1 ); + +#ifdef UNICODE + convertUTF8toUTF16( fromName, from, from.size ); + convertUTF8toUTF16( toName, to, to.size ); +#else + dStrcpy( from, fromName ); + dStrcpy( to, toName ); +#endif + + backslash( from ); + backslash( to ); + + // Copy File + if (Platform::isFile(fromName)) + return CopyFile( from, to, nooverwrite ); + // Copy Path + else if (Platform::isDirectory(fromName)) + { + // If the destination path exists and we don't want to overwrite, return. + if ((Platform::isDirectory(toName) || Platform::isFile(toName)) && nooverwrite) + return false; + + Vector directoryInfo; + Platform::dumpDirectories(fromName, directoryInfo, -1); + + Vector fileInfo; + Platform::dumpPath(fromName, fileInfo); + + Platform::clearExcludedDirectories(); + + TempAlloc< char > tempBuf( to.size * 3 + MAX_PATH * 3 ); + + // Create all the directories. + for (S32 i = 0; i < directoryInfo.size(); i++) + { + const char* fromDir = directoryInfo[i]; + + char* toDir = tempBuf; + Platform::makeFullPathName(fromDir + dStrlen(fromName) + (dStricmp(fromDir, fromName) ? 1 : 0), tempBuf, tempBuf.size, toName); + if(*(toDir + dStrlen(toDir) - 1) != '/') + dStrcat(toDir, "/"); + forwardslash(toDir); + + if (!Platform::createPath(toDir)) + { + //TODO: New directory should be deleted here. + return false; + } + } + + TempAlloc< char > tempBuf1( from.size * 3 + MAX_PATH * 3 ); +#ifdef UNICODE + TempAlloc< WCHAR > wtempBuf( tempBuf.size / 3 ); + TempAlloc< WCHAR > wtempBuf1( tempBuf1.size / 3 ); +#endif + + for (S32 i = 0; i < fileInfo.size(); i++) + { + char* fromFile = tempBuf1; + dSprintf( tempBuf1, tempBuf1.size, "%s/%s", fileInfo[i].pFullPath, fileInfo[i].pFileName); + + char* toFile = tempBuf; + Platform::makeFullPathName(fileInfo[i].pFullPath + dStrlen(fromName) + (dStricmp(fileInfo[i].pFullPath, fromName) ? 1 : 0), tempBuf, tempBuf.size, toName); + dStrcat(toFile, "/"); + dStrcat(toFile, fileInfo[i].pFileName); + + backslash(fromFile); + backslash(toFile); + +#ifdef UNICODE + convertUTF8toUTF16( tempBuf, wtempBuf, wtempBuf.size ); + convertUTF8toUTF16( tempBuf1, wtempBuf1, wtempBuf1.size ); + WCHAR* f = wtempBuf1; + WCHAR* t = wtempBuf; +#else + char *f = (char*)fromFile; + char *t = (char*)toFile; +#endif + + if (!::CopyFile(f, t, nooverwrite)) + { + // New directory should be deleted here. + return false; + } + + } + + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Constructors & Destructor +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// After construction, the currentStatus will be Closed and the capabilities +// will be 0. +//----------------------------------------------------------------------------- +File::File() +: currentStatus(Closed), capability(0) +{ + AssertFatal(sizeof(HANDLE) == sizeof(void *), "File::File: cannot cast void* to HANDLE"); + + handle = (void *)INVALID_HANDLE_VALUE; +} + +//----------------------------------------------------------------------------- +// insert a copy constructor here... (currently disabled) +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Destructor +//----------------------------------------------------------------------------- +File::~File() +{ + close(); + handle = (void *)INVALID_HANDLE_VALUE; +} + + +//----------------------------------------------------------------------------- +// Open a file in the mode specified by openMode (Read, Write, or ReadWrite). +// Truncate the file if the mode is either Write or ReadWrite and truncate is +// true. +// +// Sets capability appropriate to the openMode. +// Returns the currentStatus of the file. +//----------------------------------------------------------------------------- +File::Status File::open(const char *filename, const AccessMode openMode) +{ + AssertFatal(NULL != filename, "File::open: NULL fname"); + AssertWarn(INVALID_HANDLE_VALUE == (HANDLE)handle, "File::open: handle already valid"); + + TempAlloc< TCHAR > fname( dStrlen( filename ) + 1 ); + +#ifdef UNICODE + convertUTF8toUTF16( filename, fname, fname.size ); +#else + dStrcpy(fname, filename); +#endif + backslash( fname ); + + // Close the file if it was already open... + if (Closed != currentStatus) + close(); + + // create the appropriate type of file... + switch (openMode) + { + case Read: + handle = (void *)CreateFile(fname, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + break; + case Write: + handle = (void *)CreateFile(fname, + GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + break; + case ReadWrite: + handle = (void *)CreateFile(fname, + GENERIC_WRITE | GENERIC_READ, + 0, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + break; + case WriteAppend: + handle = (void *)CreateFile(fname, + GENERIC_WRITE, + 0, + NULL, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + break; + + default: + AssertFatal(false, "File::open: bad access mode"); // impossible + } + + if (INVALID_HANDLE_VALUE == (HANDLE)handle) // handle not created successfully + { + return setStatus(); + } + else + { + // successfully created file, so set the file capabilities... + switch (openMode) + { + case Read: + capability = U32(FileRead); + break; + case Write: + case WriteAppend: + capability = U32(FileWrite); + break; + case ReadWrite: + capability = U32(FileRead) | + U32(FileWrite); + break; + default: + AssertFatal(false, "File::open: bad access mode"); + } + return currentStatus = Ok; // success! + } +} + +//----------------------------------------------------------------------------- +// Get the current position of the file pointer. +//----------------------------------------------------------------------------- +U32 File::getPosition() const +{ + AssertFatal(Closed != currentStatus, "File::getPosition: file closed"); + AssertFatal(INVALID_HANDLE_VALUE != (HANDLE)handle, "File::getPosition: invalid file handle"); + + return SetFilePointer((HANDLE)handle, + 0, // how far to move + NULL, // pointer to high word + FILE_CURRENT); // from what point +} + +//----------------------------------------------------------------------------- +// Set the position of the file pointer. +// Absolute and relative positioning is supported via the absolutePos +// parameter. +// +// If positioning absolutely, position MUST be positive - an IOError results if +// position is negative. +// Position can be negative if positioning relatively, however positioning +// before the start of the file is an IOError. +// +// Returns the currentStatus of the file. +//----------------------------------------------------------------------------- +File::Status File::setPosition(S32 position, bool absolutePos) +{ + AssertFatal(Closed != currentStatus, "File::setPosition: file closed"); + AssertFatal(INVALID_HANDLE_VALUE != (HANDLE)handle, "File::setPosition: invalid file handle"); + + if (Ok != currentStatus && EOS != currentStatus) + return currentStatus; + + U32 finalPos; + if (absolutePos) + { + AssertFatal(0 <= position, "File::setPosition: negative absolute position"); + + // position beyond EOS is OK + finalPos = SetFilePointer((HANDLE)handle, + position, + NULL, + FILE_BEGIN); + } + else + { + AssertFatal((getPosition() >= (U32)abs(position) && 0 > position) || 0 <= position, "File::setPosition: negative relative position"); + + // position beyond EOS is OK + finalPos = SetFilePointer((HANDLE)handle, + position, + NULL, + FILE_CURRENT); + } + + if (0xffffffff == finalPos) + return setStatus(); // unsuccessful + else if (finalPos >= getSize()) + return currentStatus = EOS; // success, at end of file + else + return currentStatus = Ok; // success! +} + +//----------------------------------------------------------------------------- +// Get the size of the file in bytes. +// It is an error to query the file size for a Closed file, or for one with an +// error status. +//----------------------------------------------------------------------------- +U32 File::getSize() const +{ + AssertWarn(Closed != currentStatus, "File::getSize: file closed"); + AssertFatal(INVALID_HANDLE_VALUE != (HANDLE)handle, "File::getSize: invalid file handle"); + + if (Ok == currentStatus || EOS == currentStatus) + { + DWORD high; + return GetFileSize((HANDLE)handle, &high); // success! + } + else + return 0; // unsuccessful +} + +//----------------------------------------------------------------------------- +// Flush the file. +// It is an error to flush a read-only file. +// Returns the currentStatus of the file. +//----------------------------------------------------------------------------- +File::Status File::flush() +{ + AssertFatal(Closed != currentStatus, "File::flush: file closed"); + AssertFatal(INVALID_HANDLE_VALUE != (HANDLE)handle, "File::flush: invalid file handle"); + AssertFatal(true == hasCapability(FileWrite), "File::flush: cannot flush a read-only file"); + + if (0 != FlushFileBuffers((HANDLE)handle)) + return setStatus(); // unsuccessful + else + return currentStatus = Ok; // success! +} + +//----------------------------------------------------------------------------- +// Close the File. +// +// Returns the currentStatus +//----------------------------------------------------------------------------- +File::Status File::close() +{ + // check if it's already closed... + if (Closed == currentStatus) + return currentStatus; + + // it's not, so close it... + if (INVALID_HANDLE_VALUE != (HANDLE)handle) + { + if (0 == CloseHandle((HANDLE)handle)) + return setStatus(); // unsuccessful + } + handle = (void *)INVALID_HANDLE_VALUE; + return currentStatus = Closed; +} + +//----------------------------------------------------------------------------- +// Self-explanatory. +//----------------------------------------------------------------------------- +File::Status File::getStatus() const +{ + return currentStatus; +} + +//----------------------------------------------------------------------------- +// Sets and returns the currentStatus when an error has been encountered. +//----------------------------------------------------------------------------- +File::Status File::setStatus() +{ + switch (GetLastError()) + { + case ERROR_INVALID_HANDLE: + case ERROR_INVALID_ACCESS: + case ERROR_TOO_MANY_OPEN_FILES: + case ERROR_FILE_NOT_FOUND: + case ERROR_SHARING_VIOLATION: + case ERROR_HANDLE_DISK_FULL: + return currentStatus = IOError; + + default: + return currentStatus = UnknownError; + } +} + +//----------------------------------------------------------------------------- +// Sets and returns the currentStatus to status. +//----------------------------------------------------------------------------- +File::Status File::setStatus(File::Status status) +{ + return currentStatus = status; +} + +//----------------------------------------------------------------------------- +// Read from a file. +// The number of bytes to read is passed in size, the data is returned in src. +// The number of bytes read is available in bytesRead if a non-Null pointer is +// provided. +//----------------------------------------------------------------------------- +File::Status File::read(U32 size, char *dst, U32 *bytesRead) +{ + AssertFatal(Closed != currentStatus, "File::read: file closed"); + AssertFatal(INVALID_HANDLE_VALUE != (HANDLE)handle, "File::read: invalid file handle"); + AssertFatal(NULL != dst, "File::read: NULL destination pointer"); + AssertFatal(true == hasCapability(FileRead), "File::read: file lacks capability"); + AssertWarn(0 != size, "File::read: size of zero"); + + if (Ok != currentStatus || 0 == size) + return currentStatus; + else + { + DWORD lastBytes; + DWORD *bytes = (NULL == bytesRead) ? &lastBytes : (DWORD *)bytesRead; + if (0 != ReadFile((HANDLE)handle, dst, size, bytes, NULL)) + { + if(*((U32 *)bytes) != size) + return currentStatus = EOS; // end of stream + } + else + return setStatus(); // unsuccessful + } + return currentStatus = Ok; // successfully read size bytes +} + +//----------------------------------------------------------------------------- +// Write to a file. +// The number of bytes to write is passed in size, the data is passed in src. +// The number of bytes written is available in bytesWritten if a non-Null +// pointer is provided. +//----------------------------------------------------------------------------- +File::Status File::write(U32 size, const char *src, U32 *bytesWritten) +{ + AssertFatal(Closed != currentStatus, "File::write: file closed"); + AssertFatal(INVALID_HANDLE_VALUE != (HANDLE)handle, "File::write: invalid file handle"); + AssertFatal(NULL != src, "File::write: NULL source pointer"); + AssertFatal(true == hasCapability(FileWrite), "File::write: file lacks capability"); + AssertWarn(0 != size, "File::write: size of zero"); + + if ((Ok != currentStatus && EOS != currentStatus) || 0 == size) + return currentStatus; + else + { + DWORD lastBytes; + DWORD *bytes = (NULL == bytesWritten) ? &lastBytes : (DWORD *)bytesWritten; + if (0 != WriteFile((HANDLE)handle, src, size, bytes, NULL)) + return currentStatus = Ok; // success! + else + return setStatus(); // unsuccessful + } +} + +//----------------------------------------------------------------------------- +// Self-explanatory. +//----------------------------------------------------------------------------- +bool File::hasCapability(Capability cap) const +{ + return (0 != (U32(cap) & capability)); +} + +S32 Platform::compareFileTimes(const FileTime &a, const FileTime &b) +{ + if(a.v2 > b.v2) + return 1; + if(a.v2 < b.v2) + return -1; + if(a.v1 > b.v1) + return 1; + if(a.v1 < b.v1) + return -1; + return 0; +} + +static bool recurseDumpPath(const char *path, const char *pattern, Vector &fileVector, S32 recurseDepth ) +{ + WIN32_FIND_DATA findData; + + TempAlloc< char > fullPath( dStrlen( path ) * 3 + MAX_PATH * 3 + 1 ); + Platform::makeFullPathName( path, fullPath, fullPath.size ); + + U32 lenFullPath = dStrlen( fullPath ); + TempAlloc< char > buf( lenFullPath + MAX_PATH * 3 + 2 ); + dSprintf( buf, buf.size, "%s/%s", fullPath.ptr, pattern ); + +#ifdef UNICODE + TempAlloc< WCHAR > searchBuf( buf.size ); + convertUTF8toUTF16( buf, searchBuf, searchBuf.size ); + WCHAR* search = searchBuf; +#else + char *search = buf; +#endif + + backslash( search ); + + HANDLE handle = FindFirstFile(search, &findData); + if (handle == INVALID_HANDLE_VALUE) + return false; + + do + { +#ifdef UNICODE + convertUTF16toUTF8( findData.cFileName, buf, buf.size ); + char* fnbuf = buf; +#else + char *fnbuf = findData.cFileName; +#endif + + if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + // make sure it is a directory + if (findData.dwFileAttributes & (FILE_ATTRIBUTE_OFFLINE|FILE_ATTRIBUTE_SYSTEM) ) + continue; + + // skip . and .. directories + if (dStrcmp( findData.cFileName, TEXT( "." ) ) == 0 || dStrcmp( findData.cFileName, TEXT( ".." ) ) == 0) + continue; + + // Skip excluded directores + if(Platform::isExcludedDirectory(fnbuf)) + continue; + + dSprintf( fullPath, fullPath.size, "%s/%s", path, fnbuf); + char* child = fullPath; + if( recurseDepth > 0 ) + recurseDumpPath(child, pattern, fileVector, recurseDepth - 1); + else if (recurseDepth == -1) + recurseDumpPath(child, pattern, fileVector, -1); + } + else + { + // make sure it is the kind of file we're looking for + if (findData.dwFileAttributes & + (FILE_ATTRIBUTE_DIRECTORY| + FILE_ATTRIBUTE_OFFLINE| + FILE_ATTRIBUTE_SYSTEM| + FILE_ATTRIBUTE_TEMPORARY) ) + continue; + + // add it to the list + fileVector.increment(); + Platform::FileInfo& rInfo = fileVector.last(); + + forwardslash( fnbuf ); + + rInfo.pFullPath = StringTable->insert(path); + rInfo.pFileName = StringTable->insert(fnbuf); + rInfo.fileSize = findData.nFileSizeLow; + } + + }while(FindNextFile(handle, &findData)); + + FindClose(handle); + return true; +} + + +//-------------------------------------- + +bool Platform::getFileTimes(const char *filePath, FileTime *createTime, FileTime *modifyTime) +{ + WIN32_FIND_DATA findData; + + TempAlloc< TCHAR > fp( dStrlen( filePath ) + 1 ); + +#ifdef UNICODE + convertUTF8toUTF16( filePath, fp, fp.size ); +#else + dStrcpy( fp, filePath ); +#endif + + backslash( fp ); + + HANDLE h = FindFirstFile(fp, &findData); + if(h == INVALID_HANDLE_VALUE) + return false; + + if(createTime) + { + createTime->v1 = findData.ftCreationTime.dwLowDateTime; + createTime->v2 = findData.ftCreationTime.dwHighDateTime; + } + if(modifyTime) + { + modifyTime->v1 = findData.ftLastWriteTime.dwLowDateTime; + modifyTime->v2 = findData.ftLastWriteTime.dwHighDateTime; + } + FindClose(h); + return true; +} + +//-------------------------------------- +bool Platform::createPath(const char *file) +{ + TempAlloc< TCHAR > pathbuf( dStrlen( file ) + 1 ); + +#ifdef UNICODE + TempAlloc< WCHAR > fileBuf( pathbuf.size ); + convertUTF8toUTF16( file, fileBuf, fileBuf.size ); + const WCHAR* fileName = fileBuf; + const WCHAR* dir; +#else + const char* fileName = file; + const char* dir; +#endif + + pathbuf[ 0 ] = 0; + U32 pathLen = 0; + + while((dir = dStrchr(fileName, '/')) != NULL) + { + TCHAR* pathptr = pathbuf; + dMemcpy( pathptr + pathLen, fileName, ( dir - fileName ) * sizeof( TCHAR ) ); + pathbuf[pathLen + dir-fileName] = 0; + + // ignore return value because we are fine with already existing directory + CreateDirectory(pathbuf, NULL); + + pathLen += dir - fileName; + pathbuf[pathLen++] = '\\'; + fileName = dir + 1; + } + return true; +} + +// [rene, 04/05/2008] Not used currently so did not bother updating. +#if 0 +// [tom, 7/12/2005] Rather then converting this to unicode, just using the ANSI +// versions of the Win32 API as its quicker for testing. +bool Platform::cdFileExists(const char *filePath, const char *volumeName, S32 serialNum) +{ + if (!filePath || !filePath[0]) + return true; + + //first find the CD device... + char fileBuf[1024]; + char drivesBuf[256]; + S32 length = GetLogicalDriveStringsA(256, drivesBuf); + char *drivePtr = drivesBuf; + while (S32(drivePtr - drivesBuf) < length) + { + char driveVolume[256], driveFileSystem[256]; + U32 driveSerial, driveFNLength, driveFlags; + if ((dStricmp(drivePtr, "A:\\") != 0 && dStricmp(drivePtr, "B:\\") != 0) && + GetVolumeInformationA((const char*)drivePtr, &driveVolume[0], (unsigned long)255, + (unsigned long*)&driveSerial, (unsigned long*)&driveFNLength, + (unsigned long*)&driveFlags, &driveFileSystem[0], (unsigned long)255)) + { +#if defined (TORQUE_DEBUG) || !defined (TORQUE_SHIPPING) + Con::printf("Found Drive: %s, vol: %s, serial: %d", drivePtr, driveVolume, driveSerial); +#endif + //see if the volume and serial number match + if (!dStricmp(volumeName, driveVolume) && (!serialNum || (serialNum == driveSerial))) + { + //see if the file exists on this volume + if(dStrlen(drivePtr) == 3 && drivePtr[2] == '\\' && filePath[0] == '\\') + dSprintf(fileBuf, sizeof(fileBuf), "%s%s", drivePtr, filePath + 1); + else + dSprintf(fileBuf, sizeof(fileBuf), "%s%s", drivePtr, filePath); +#if defined (TORQUE_DEBUG) || !defined (TORQUE_SHIPPING) + Con::printf("Looking for file: %s on %s", fileBuf, driveVolume); +#endif + WIN32_FIND_DATAA findData; + HANDLE h = FindFirstFileA(fileBuf, &findData); + if(h != INVALID_HANDLE_VALUE) + { + FindClose(h); + return true; + } + FindClose(h); + } + } + + //check the next drive + drivePtr += dStrlen(drivePtr) + 1; + } + + return false; +} +#endif + +//-------------------------------------- +bool Platform::dumpPath(const char *path, Vector &fileVector, S32 recurseDepth) +{ + return recurseDumpPath(path, "*", fileVector, recurseDepth ); +} + + +//-------------------------------------- + +//StringTableEntry Platform::getWorkingDirectory() +//{ +// return getCurrentDirectory(); +//} + +StringTableEntry Platform::getCurrentDirectory() +{ + TempAlloc< TCHAR > buf( 2048 ); + + GetCurrentDirectory( buf.size, buf ); + forwardslash( buf ); + +#ifdef UNICODE + char* utf8 = convertUTF16toUTF8( buf ); + StringTableEntry result = StringTable->insert( utf8 ); + SAFE_DELETE_ARRAY( utf8 ); + return result; +#else + return StringTable->insert( buf ); +#endif +} + +bool Platform::setCurrentDirectory(StringTableEntry newDir) +{ + + if (Platform::getWebDeployment()) + return true; + + TempAlloc< TCHAR > buf( dStrlen( newDir ) + 2 ); + +#ifdef UNICODE + convertUTF8toUTF16( newDir, buf, buf.size - 1 ); +#else + dStrcpy( buf, newDir ); +#endif + + backslash( buf ); + return SetCurrentDirectory( buf ); +} + +#ifdef UNICODE +static void getExecutableInfo( StringTableEntry* path, StringTableEntry* exe ) +{ + static StringTableEntry pathEntry = NULL; + static StringTableEntry exeEntry = NULL; + + if( !pathEntry ) + { + if (!Platform::getWebDeployment()) + { + WCHAR cen_buf[ 2048 ]; + GetModuleFileNameW( NULL, cen_buf, sizeof( cen_buf ) / sizeof( cen_buf[ 0 ] ) ); + forwardslash( cen_buf ); + + WCHAR* delimiter = dStrrchr( cen_buf, '/' ); + if( delimiter ) + *delimiter = '\0'; + + char* pathBuf = convertUTF16toUTF8( cen_buf ); + char* exeBuf = convertUTF16toUTF8( delimiter + 1 ); + + pathEntry = StringTable->insert( pathBuf ); + exeEntry = StringTable->insert( exeBuf ); + + SAFE_DELETE_ARRAY( pathBuf ); + SAFE_DELETE_ARRAY( exeBuf ); + } + else + { + char cdir[4096]; + GetCurrentDirectoryA(4096, cdir); + pathEntry = StringTable->insert(cdir); + exeEntry = StringTable->insert("WebGameCtrl.exe"); + } + } + + if( path ) + *path = pathEntry; + if( exe ) + *exe = exeEntry; +} +#endif + +StringTableEntry Platform::getExecutableName() +{ +#ifdef UNICODE + StringTableEntry exe; + getExecutableInfo( NULL, &exe ); + return exe; +#else + static StringTableEntry cen = NULL; + if (!cen) + { + char cen_buf[2048]; + GetModuleFileNameA( NULL, cen_buf, 2047); + forwardslash(cen_buf); + + char *delimiter = dStrrchr( cen_buf, '/' ); + + if( delimiter != NULL ) + { + *delimiter = 0x00; + delimiter++; + cen = StringTable->insert(delimiter); + } + else + cen = StringTable->insert(cen_buf); + } + return cen; +#endif +} + +StringTableEntry Platform::getExecutablePath() +{ +#ifdef UNICODE + StringTableEntry path; + getExecutableInfo( &path, NULL ); + return path; +#else + static StringTableEntry cen = NULL; + if (!cen) + { + char cen_buf[2048]; + GetModuleFileNameA( NULL, cen_buf, 2047); + forwardslash(cen_buf); + + char *delimiter = dStrrchr( cen_buf, '/' ); + + if( delimiter != NULL ) + *delimiter = 0x00; + + cen = StringTable->insert(cen_buf); + } + return cen; +#endif +} + +//-------------------------------------- +bool Platform::isFile(const char *pFilePath) +{ + if (!pFilePath || !*pFilePath) + return false; + + TempAlloc< TCHAR > buf( dStrlen( pFilePath ) + 1 ); + +#ifdef UNICODE + convertUTF8toUTF16( pFilePath, buf, buf.size ); +#else + dStrcpy( buf, pFilePath ); +#endif + backslash( buf ); + + // Get file info + WIN32_FIND_DATA findData; + HANDLE handle = FindFirstFile(buf, &findData); + FindClose(handle); + + if(handle == INVALID_HANDLE_VALUE) + return false; + + // if the file is a Directory, Offline, System or Temporary then FALSE + if (findData.dwFileAttributes & + (FILE_ATTRIBUTE_DIRECTORY| + FILE_ATTRIBUTE_OFFLINE| + FILE_ATTRIBUTE_SYSTEM| + FILE_ATTRIBUTE_TEMPORARY) ) + return false; + + // must be a real file then + return true; +} + +//-------------------------------------- +S32 Platform::getFileSize(const char *pFilePath) +{ + if (!pFilePath || !*pFilePath) + return -1; + + TempAlloc< TCHAR > buf( dStrlen( pFilePath ) + 1 ); + +#ifdef UNICODE + convertUTF8toUTF16( pFilePath, buf, buf.size ); +#else + dStrcpy( buf, pFilePath ); +#endif + backslash( buf ); + + // Get file info + WIN32_FIND_DATA findData; + HANDLE handle = FindFirstFile(buf, &findData); + + if(handle == INVALID_HANDLE_VALUE) + return -1; + + FindClose(handle); + + // if the file is a Directory, Offline, System or Temporary then FALSE + if (findData.dwFileAttributes & + (FILE_ATTRIBUTE_DIRECTORY| + FILE_ATTRIBUTE_OFFLINE| + FILE_ATTRIBUTE_SYSTEM| + FILE_ATTRIBUTE_TEMPORARY) ) + return -1; + + // must be a real file then + return findData.nFileSizeLow; +} + + +//-------------------------------------- +bool Platform::isDirectory(const char *pDirPath) +{ + if (!pDirPath || !*pDirPath) + return false; + + TempAlloc< TCHAR > buf( dStrlen( pDirPath ) + 1 ); + +#ifdef UNICODE + convertUTF8toUTF16( pDirPath, buf, buf.size ); +#else + dStrcpy( buf, pDirPath ); +#endif + backslash( buf ); + + // Get file info + WIN32_FIND_DATA findData; + HANDLE handle = FindFirstFile(buf, &findData); + + // [neo, 5/15/2007] + // This check was AFTER FindClose for some reason - this is most probably the + // original intent. + if(handle == INVALID_HANDLE_VALUE) + return false; + + FindClose(handle); + + // if the file is a Directory, Offline, System or Temporary then FALSE + if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + // make sure it's a valid game directory + if (findData.dwFileAttributes & (FILE_ATTRIBUTE_OFFLINE|FILE_ATTRIBUTE_SYSTEM) ) + return false; + + // must be a directory + return true; + } + + return false; +} + + + +//-------------------------------------- +bool Platform::isSubDirectory(const char *pParent, const char *pDir) +{ + if (!pParent || !*pDir) + return false; + + const char* fileName = avar("%s/*", pParent); + + TempAlloc< TCHAR > file( dStrlen( fileName ) + 1 ); + TempAlloc< TCHAR > dir( dStrlen( pDir ) + 1 ); + +#ifdef UNICODE + convertUTF8toUTF16( fileName, file, file.size ); + convertUTF8toUTF16( pDir, dir, dir.size ); +#else + dStrcpy( file, fileName ); + dStrcpy( dir, pDir ); +#endif + + backslash( file ); + backslash( dir ); + + // this is somewhat of a brute force method but we need to be 100% sure + // that the user cannot enter things like ../dir or /dir etc,... + WIN32_FIND_DATA findData; + HANDLE handle = FindFirstFile(file, &findData); + if (handle == INVALID_HANDLE_VALUE) + return false; + do + { + // if it is a directory... + if (findData.dwFileAttributes & + (FILE_ATTRIBUTE_DIRECTORY| + FILE_ATTRIBUTE_OFFLINE| + FILE_ATTRIBUTE_SYSTEM| + FILE_ATTRIBUTE_TEMPORARY) ) + { + //FIXME: this has to be dStrcasecmp but there's no implementation for Unicode + + // and the names match + if (dStrcmp(dir, findData.cFileName ) == 0) + { + // then we have a real sub directory + FindClose(handle); + return true; + } + } + }while(FindNextFile(handle, &findData)); + + FindClose(handle); + return false; +} + +//------------------------------------------------------------------------------ + +bool Platform::fileTimeToString(FileTime * time, char * string, U32 strLen) +{ + if(!time || !string) + return(false); + + dSprintf(string, strLen, "%d:%d", time->v2, time->v1); + return(true); +} + +bool Platform::stringToFileTime(const char * string, FileTime * time) +{ + if(!time || !string) + return(false); + + char buf[80]; + dSprintf(buf, sizeof(buf), (char *)string); + + char * sep = (char *)dStrstr((const char *)buf, (const char *)":"); + if(!sep) + return(false); + + *sep = 0; + sep++; + + time->v2 = dAtoi(buf); + time->v1 = dAtoi(sep); + + return(true); +} + +// Volume Functions + +void Platform::getVolumeNamesList( Vector& out_rNameVector, bool bOnlyFixedDrives ) +{ + DWORD dwDrives = GetLogicalDrives(); + DWORD dwMask = 1; + char driveLetter[12]; + + out_rNameVector.clear(); + + for(int i = 0; i < 32; i++ ) + { + dMemset(driveLetter,0,12); + if( dwDrives & dwMask ) + { + dSprintf(driveLetter, 12, "%c:", (i + 'A')); + + if( bOnlyFixedDrives && GetDriveTypeA(driveLetter) == DRIVE_FIXED ) + out_rNameVector.push_back( StringTable->insert( driveLetter ) ); + else if ( !bOnlyFixedDrives ) + out_rNameVector.push_back( StringTable->insert( driveLetter ) ); + } + dwMask <<= 1; + } +} + +void Platform::getVolumeInformationList( Vector& out_rVolumeInfoVector, bool bOnlyFixedDrives ) +{ + Vector drives; + + getVolumeNamesList( drives, bOnlyFixedDrives ); + + if( ! drives.empty() ) + { + Vector::iterator i; + for( i = drives.begin(); i != drives.end(); i++ ) + { + VolumeInformation info; + TCHAR lpszVolumeName[ 256 ]; + TCHAR lpszFileSystem[ 256 ]; + DWORD dwSerial = 0; + DWORD dwMaxComponentLength = 0; + DWORD dwFileSystemFlags = 0; + + dMemset( lpszVolumeName, 0, sizeof( lpszVolumeName ) ); + dMemset( lpszFileSystem, 0, sizeof( lpszFileSystem ) ); + dMemset( &info, 0, sizeof( VolumeInformation ) ); + + // More volume information + UINT uDriveType = GetDriveTypeA( (*i) ); + if( uDriveType == DRIVE_UNKNOWN ) + info.Type = DRIVETYPE_UNKNOWN; + else if( uDriveType == DRIVE_REMOVABLE ) + info.Type = DRIVETYPE_REMOVABLE; + else if( uDriveType == DRIVE_FIXED ) + info.Type = DRIVETYPE_FIXED; + else if( uDriveType == DRIVE_CDROM ) + info.Type = DRIVETYPE_CDROM; + else if( uDriveType == DRIVE_RAMDISK ) + info.Type = DRIVETYPE_RAMDISK; + else if( uDriveType == DRIVE_REMOTE ) + info.Type = DRIVETYPE_REMOTE; + + info.RootPath = StringTable->insert( (*i) ); + + // We don't retrieve drive volume info for removable drives, because it's loud :( + if( info.Type != DRIVETYPE_REMOVABLE ) + { +#ifdef UNICODE + WCHAR ibuf[ 3 ]; + ibuf[ 0 ] = ( *i )[ 0 ]; + ibuf[ 1 ] = ':'; + ibuf[ 2 ] = '\0'; +#else + char* ibuf = *i; +#endif + // Standard volume information + GetVolumeInformation( ibuf, lpszVolumeName, sizeof( lpszVolumeName ) / sizeof( lpszVolumeName[ 0 ] ), + &dwSerial, &dwMaxComponentLength, &dwFileSystemFlags, lpszFileSystem, + sizeof( lpszFileSystem ) / sizeof( lpszFileSystem[ 0 ] ) ); + +#ifdef UNICODE + char buf[ sizeof( lpszFileSystem ) / sizeof( lpszFileSystem[ 0 ] ) * 3 + 1 ]; + convertUTF16toUTF8( lpszFileSystem, buf, sizeof( buf ) / sizeof( buf[ 0 ] ) ); + info.FileSystem = StringTable->insert( buf ); + + convertUTF16toUTF8( lpszVolumeName, buf, sizeof( buf ) / sizeof( buf[ 0 ] ) ); + info.Name = StringTable->insert( buf ); +#else + info.FileSystem = StringTable->insert( lpszFileSystem ); + info.Name = StringTable->insert( lpszVolumeName ); +#endif + info.SerialNumber = dwSerial; + // Won't compile on something prior to XP. + info.ReadOnly = dwFileSystemFlags & FILE_READ_ONLY_VOLUME; + } + out_rVolumeInfoVector.push_back( info ); + + // I opted not to get free disk space because of the overhead of the calculations required for it + + } + } +} + + +bool Platform::hasSubDirectory(const char *pPath) +{ + if( !pPath ) + return false; + + char searchBuf[1024]; + + // Compose our search string - Format : ([path]/[subpath]/*) + char trail = pPath[ dStrlen(pPath) - 1 ]; + if( trail == '/' ) + dStrcpy( searchBuf, pPath ); + else + dSprintf(searchBuf, 1024, "%s/*", pPath ); + +#ifdef UNICODE + WCHAR buf[ 1024 ]; + convertUTF8toUTF16( searchBuf, buf, sizeof( buf ) / sizeof( buf[ 0 ] ) ); + WCHAR* search = buf; +#else + char* search = searchBuf; +#endif + + backslash( search ); + + // See if we get any hits + WIN32_FIND_DATA findData; + HANDLE handle = FindFirstFile(search, &findData); + if (handle == INVALID_HANDLE_VALUE) + return false; + + bool result = false; + do + { + if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + // skip . and .. directories + if (dStrcmp(findData.cFileName, TEXT( "." ) ) == 0 || dStrcmp(findData.cFileName, TEXT( ".." ) ) == 0) + continue; + +#ifdef UNICODE + char fileName[ 1024 ]; + convertUTF16toUTF8( findData.cFileName, fileName, sizeof( fileName ) / sizeof( fileName[ 0 ] ) ); +#else + char* fileName = findData.cFileName; +#endif + + if( Platform::isExcludedDirectory( fileName ) ) + continue; + + result = true; + break; + } + } + while(FindNextFile(handle, &findData)); + + FindClose(handle); + + Platform::clearExcludedDirectories(); + + return result; +} + + +static bool recurseDumpDirectories(const char *basePath, const char *subPath, Vector &directoryVector, S32 currentDepth, S32 recurseDepth, bool noBasePath) +{ + TempAlloc< char > search( 1024 ); + + //----------------------------------------------------------------------------- + // Compose our search string - Format : ([path]/[subpath]/*) + //----------------------------------------------------------------------------- + + char trail = basePath[ dStrlen(basePath) - 1 ]; + char subTrail = subPath ? subPath[ dStrlen(subPath) - 1 ] : '\0'; + + if( trail == '/' ) + { + // we have a sub path and it's not an empty string + if( subPath && ( dStrncmp( subPath, "", 1 ) != 0 ) ) + { + if( subTrail == '/' ) + dSprintf(search, search.size, "%s%s*", basePath,subPath ); + else + dSprintf(search, search.size, "%s%s/*", basePath,subPath ); + } + else + dSprintf( search, search.size, "%s*", basePath ); + } + else + { + if( subPath && ( dStrncmp( subPath, "", 1 ) != 0 ) ) + if( subTrail == '/' ) + dSprintf(search, search.size, "%s%s*", basePath,subPath ); + else + dSprintf(search, search.size, "%s%s/*", basePath,subPath ); + else + dSprintf(search, search.size, "%s/*", basePath ); + } + +#ifdef UNICODE + TempAlloc< WCHAR > searchStr( dStrlen( search ) + 1 ); + convertUTF8toUTF16( search, searchStr, searchStr.size ); +#else + char* searchStr = search; +#endif + + backslash( searchStr ); + + //----------------------------------------------------------------------------- + // See if we get any hits + //----------------------------------------------------------------------------- + + WIN32_FIND_DATA findData; + HANDLE handle = FindFirstFile(searchStr, &findData); + if (handle == INVALID_HANDLE_VALUE) + return false; + + //----------------------------------------------------------------------------- + // add path to our return list ( provided it is valid ) + //----------------------------------------------------------------------------- + if( !Platform::isExcludedDirectory( subPath ) ) + { + + if( noBasePath ) + { + // We have a path and it's not an empty string or an excluded directory + if( ( subPath && ( dStrncmp( subPath, "", 1 ) != 0 ) ) ) + directoryVector.push_back( StringTable->insert( subPath ) ); + } + else + { + if( ( subPath && ( dStrncmp( subPath, "", 1 ) != 0 ) ) ) + { + char szPath [ 1024 ]; + dMemset( szPath, 0, 1024 ); + if( trail != '/' ) + dSprintf( szPath, 1024, "%s%s", basePath, subPath ); + else + dSprintf( szPath, 1024, "%s%s", basePath, &subPath[1] ); + directoryVector.push_back( StringTable->insert( szPath ) ); + } + else + directoryVector.push_back( StringTable->insert( basePath ) ); + } + } + + //----------------------------------------------------------------------------- + // Iterate through and grab valid directories + //----------------------------------------------------------------------------- + +#ifdef UNICODE + TempAlloc< char > fileName( 1024 ); +#endif + + do + { + if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + // skip . and .. directories + if (dStrcmp(findData.cFileName, TEXT( "." )) == 0 || dStrcmp(findData.cFileName, TEXT( ".." )) == 0) + continue; + +#ifdef UNICODE + convertUTF16toUTF8( findData.cFileName, fileName, fileName.size ); +#else + char* fileName = findData.cFileName; +#endif + + // skip excluded directories + if( Platform::isExcludedDirectory( fileName ) ) + continue; + + if( ( subPath && ( dStrncmp( subPath, "", 1 ) != 0 ) )) + { + if( subTrail == '/' ) + dSprintf(search, search.size, "%s%s", subPath, fileName); + else + dSprintf(search, search.size, "%s/%s", subPath, fileName); + char* child = search; + + if( currentDepth < recurseDepth || recurseDepth == -1 ) + recurseDumpDirectories(basePath, child, directoryVector, currentDepth+1, recurseDepth, noBasePath ); + + } + else + { + char* child; + if( trail == '/' ) + child = fileName; + else + { + dSprintf(search, search.size, "/%s", fileName); + child = search; + } + + if( currentDepth < recurseDepth || recurseDepth == -1 ) + recurseDumpDirectories(basePath, child, directoryVector, currentDepth+1, recurseDepth, noBasePath ); + } + } + } + while(FindNextFile(handle, &findData)); + + FindClose(handle); + return true; +} + +bool Platform::dumpDirectories( const char *path, Vector &directoryVector, S32 depth, bool noBasePath ) +{ + bool retVal = recurseDumpDirectories( path, "", directoryVector, -1, depth, noBasePath ); + + clearExcludedDirectories(); + + return retVal; +} + +//----------------------------------------------------------------------------- + +StringTableEntry osGetTemporaryDirectory() +{ + TCHAR buf[ 1024 ]; + const U32 bufSize = sizeof( buf ) / sizeof( buf[ 0 ] ); + DWORD len = GetTempPath( sizeof( buf ) / sizeof( buf[ 0 ] ), buf ); + + TempAlloc< TCHAR > temp; + TCHAR* buffer = buf; + if( len > bufSize - 1 ) + { + temp = TempAlloc< TCHAR >( len + 1 ); + buffer = temp; + GetTempPath( len + 1, buffer ); + } + + // Remove the trailing slash + buffer[len-1] = 0; + +#ifdef UNICODE + TempAlloc< char > dirBuffer( len * 3 + 1 ); + char* dir = dirBuffer; + convertUTF16toUTF8( buffer, dir, dirBuffer.size ); +#else + char* dir = buf; +#endif + + forwardslash(dir); + return StringTable->insert(dir); +} diff --git a/Engine/source/platformWin32/winFont.cpp b/Engine/source/platformWin32/winFont.cpp new file mode 100644 index 000000000..1a625d2bc --- /dev/null +++ b/Engine/source/platformWin32/winFont.cpp @@ -0,0 +1,311 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformWin32/platformWin32.h" +#include "platformWin32/winFont.h" + +#include "gfx/gFont.h" +#include "gfx/bitmap/gBitmap.h" +#include "math/mRect.h" +#include "console/console.h" +#include "core/strings/unicode.h" +#include "core/strings/stringFunctions.h" +#include "core/stringTable.h" + +static HDC fontHDC = NULL; +static HBITMAP fontBMP = NULL; + +static U32 charsetMap[]= +{ + ANSI_CHARSET, + SYMBOL_CHARSET, + SHIFTJIS_CHARSET, + HANGEUL_CHARSET, + HANGUL_CHARSET, + GB2312_CHARSET, + CHINESEBIG5_CHARSET, + OEM_CHARSET, + JOHAB_CHARSET, + HEBREW_CHARSET, + ARABIC_CHARSET, + GREEK_CHARSET, + TURKISH_CHARSET, + VIETNAMESE_CHARSET, + THAI_CHARSET, + EASTEUROPE_CHARSET, + RUSSIAN_CHARSET, + MAC_CHARSET, + BALTIC_CHARSET, +}; +#define NUMCHARSETMAP (sizeof(charsetMap) / sizeof(U32)) + +void createFontInit(void); +void createFontShutdown(void); +void CopyCharToBitmap(GBitmap *pDstBMP, HDC hSrcHDC, const RectI &r); + +void createFontInit() +{ + //shared library sets the appInstance here + winState.appInstance = GetModuleHandle(NULL); + fontHDC = CreateCompatibleDC(NULL); + fontBMP = CreateCompatibleBitmap(fontHDC, 256, 256); +} + +void createFontShutdown() +{ + DeleteObject(fontBMP); + DeleteObject(fontHDC); +} + +void CopyCharToBitmap(GBitmap *pDstBMP, HDC hSrcHDC, const RectI &r) +{ + for (S32 i = r.point.y; i < r.point.y + r.extent.y; i++) + { + for (S32 j = r.point.x; j < r.point.x + r.extent.x; j++) + { + COLORREF color = GetPixel(hSrcHDC, j, i); + if (color) + *pDstBMP->getAddress(j, i) = 255; + else + *pDstBMP->getAddress(j, i) = 0; + } + } +} + +//----------------------------------------------------------------------------- +// WinFont class +//----------------------------------------------------------------------------- + +BOOL CALLBACK EnumFamCallBack(LPLOGFONT logFont, LPNEWTEXTMETRIC textMetric, DWORD fontType, LPARAM lParam) +{ + if( !( fontType & TRUETYPE_FONTTYPE ) ) + return true; + + Vector* fonts = (Vector< StringTableEntry>*)lParam; + + const U32 len = dStrlen( logFont->lfFaceName ) * 3 + 1; + FrameTemp buffer( len ); + convertUTF16toUTF8( logFont->lfFaceName, buffer, len ); + + fonts->push_back( StringTable->insert( buffer ) ); + + return true; +} + +void PlatformFont::enumeratePlatformFonts( Vector& fonts, UTF16* fontFamily ) +{ + EnumFontFamilies( fontHDC, fontFamily, (FONTENUMPROC)EnumFamCallBack, (LPARAM)&fonts ); +} + +PlatformFont *createPlatformFont(const char *name, U32 size, U32 charset /* = TGE_ANSI_CHARSET */) +{ + PlatformFont *retFont = new WinFont; + + if(retFont->create(name, size, charset)) + return retFont; + + delete retFont; + return NULL; +} + +WinFont::WinFont() : mFont(NULL) +{ +} + +WinFont::~WinFont() +{ + if(mFont) + { + DeleteObject(mFont); + } +} + +bool WinFont::create(const char *name, U32 size, U32 charset /* = TGE_ANSI_CHARSET */) +{ + if(name == NULL || size < 1) + return false; + + if(charset > NUMCHARSETMAP) + charset = TGE_ANSI_CHARSET; + + U32 weight = 0; + U32 doItalic = 0; + + String nameStr = name; + nameStr = nameStr.trim(); + + bool haveModifier; + do + { + haveModifier = false; + if( nameStr.compare( "Bold", 4, String::NoCase | String::Right ) == 0 ) + { + weight = 700; + nameStr = nameStr.substr( 0, nameStr.length() - 4 ).trim(); + haveModifier = true; + } + if( nameStr.compare( "Italic", 6, String::NoCase | String::Right ) == 0 ) + { + doItalic = 1; + nameStr = nameStr.substr( 0, nameStr.length() - 6 ).trim(); + haveModifier = true; + } + } + while( haveModifier ); + +#ifdef UNICODE + const UTF16* n = nameStr.utf16(); + mFont = CreateFont(size,0,0,0,weight,doItalic,0,0,DEFAULT_CHARSET,OUT_TT_PRECIS,0,PROOF_QUALITY,0,n); +#else + mFont = CreateFont(size,0,0,0,weight,doItalic,0,0,charsetMap[charset],OUT_TT_PRECIS,0,PROOF_QUALITY,0,name); +#endif + if(mFont == NULL) + return false; + + SelectObject(fontHDC, fontBMP); + SelectObject(fontHDC, mFont); + GetTextMetrics(fontHDC, &mTextMetric); + + return true; +} + +bool WinFont::isValidChar(const UTF16 ch) const +{ + return ch != 0 /* && (ch >= mTextMetric.tmFirstChar && ch <= mTextMetric.tmLastChar)*/; +} + +bool WinFont::isValidChar(const UTF8 *str) const +{ + return isValidChar(oneUTF8toUTF32(str)); +} + + +PlatformFont::CharInfo &WinFont::getCharInfo(const UTF16 ch) const +{ + static PlatformFont::CharInfo c; + + dMemset(&c, 0, sizeof(c)); + c.bitmapIndex = -1; + + static U8 scratchPad[65536]; + + COLORREF backgroundColorRef = RGB( 0, 0, 0); + COLORREF foregroundColorRef = RGB(255, 255, 255); + SelectObject(fontHDC, fontBMP); + SelectObject(fontHDC, mFont); + SetBkColor(fontHDC, backgroundColorRef); + SetTextColor(fontHDC, foregroundColorRef); + + MAT2 matrix; + GLYPHMETRICS metrics; + RectI clip; + + FIXED zero; + zero.fract = 0; + zero.value = 0; + FIXED one; + one.fract = 0; + one.value = 1; + + matrix.eM11 = one; + matrix.eM12 = zero; + matrix.eM21 = zero; + matrix.eM22 = one; + + + if(GetGlyphOutline( + fontHDC, // handle of device context + ch, // character to query + GGO_GRAY8_BITMAP, // format of data to return + &metrics, // address of structure for metrics + sizeof(scratchPad), // size of buffer for data + scratchPad, // address of buffer for data + &matrix // address of transformation matrix structure + ) != GDI_ERROR) + { + U32 rowStride = (metrics.gmBlackBoxX + 3) & ~3; // DWORD aligned + U32 size = rowStride * metrics.gmBlackBoxY; + + // [neo, 5/7/2007 - #3055] + // If we get large font sizes rowStride * metrics.gmBlackBoxY will + // be larger than scratch pad size and so overwrite mem, boom! + // Added range check < scratchPad for now but we need to review what + // to do here - do we want to call GetGlyphOutline() first with null + // values and get the real size to alloc buffer? + //if( size > sizeof( scratchPad ) ) + // DebugBreak(); + + for(U32 j = 0; j < size && j < sizeof(scratchPad); j++) + { + U32 pad = U32(scratchPad[j]) << 2; + if(pad > 255) + pad = 255; + scratchPad[j] = pad; + } + S32 inc = metrics.gmCellIncX; + if(inc < 0) + inc = -inc; + + c.xOffset = 0; + c.yOffset = 0; + c.width = metrics.gmBlackBoxX; + c.height = metrics.gmBlackBoxY; + c.xOrigin = metrics.gmptGlyphOrigin.x; + c.yOrigin = metrics.gmptGlyphOrigin.y; + c.xIncrement = metrics.gmCellIncX; + + c.bitmapData = new U8[c.width * c.height]; + AssertFatal( c.bitmapData != NULL, "Could not allocate memory for font bitmap data!"); + for(U32 y = 0; S32(y) < c.height; y++) + { + U32 x; + for(x = 0; x < c.width; x++) + { + // [neo, 5/7/2007 - #3055] + // See comments above about scratchPad overrun + S32 spi = y * rowStride + x; + + if( spi >= sizeof(scratchPad) ) + return c; + + c.bitmapData[y * c.width + x] = scratchPad[spi]; + } + } + } + else + { + SIZE size; + GetTextExtentPoint32W(fontHDC, &ch, 1, &size); + if(size.cx) + { + c.xIncrement = size.cx; + c.bitmapIndex = 0; + } + } + + return c; +} + +PlatformFont::CharInfo &WinFont::getCharInfo(const UTF8 *str) const +{ + return getCharInfo(oneUTF8toUTF32(str)); +} diff --git a/Engine/source/platformWin32/winFont.h b/Engine/source/platformWin32/winFont.h new file mode 100644 index 000000000..eac8b1bac --- /dev/null +++ b/Engine/source/platformWin32/winFont.h @@ -0,0 +1,60 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PLATFORMFONT_H_ +#include "platform/platformFont.h" +#endif + +#ifndef _WINFONT_H_ +#define _WINFONT_H_ + +class WinFont : public PlatformFont +{ +private: + HFONT mFont; + TEXTMETRIC mTextMetric; + +public: + WinFont(); + virtual ~WinFont(); + + // PlatformFont virtual methods + virtual bool isValidChar(const UTF16 ch) const; + virtual bool isValidChar(const UTF8 *str) const; + + inline virtual U32 getFontHeight() const + { + return mTextMetric.tmHeight; + } + + inline virtual U32 getFontBaseLine() const + { + return mTextMetric.tmAscent; + } + + virtual PlatformFont::CharInfo &getCharInfo(const UTF16 ch) const; + virtual PlatformFont::CharInfo &getCharInfo(const UTF8 *str) const; + + virtual bool create(const char *name, dsize_t size, U32 charset = TGE_ANSI_CHARSET); +}; + +#endif // _WINFONT_H_ diff --git a/Engine/source/platformWin32/winInput.cpp b/Engine/source/platformWin32/winInput.cpp new file mode 100644 index 000000000..f6ee3cc02 --- /dev/null +++ b/Engine/source/platformWin32/winInput.cpp @@ -0,0 +1,868 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformWin32/platformWin32.h" + +#include "platform/platformInput.h" +#include "platformWin32/winDirectInput.h" +#include "platform/event.h" +#include "console/console.h" +#include "core/util/journal/process.h" +#include "windowManager/platformWindowMgr.h" + +#ifdef LOG_INPUT +#include +#include +#endif + +// Static class variables: +InputManager* Input::smManager; +bool Input::smActive; +U8 Input::smModifierKeys; +bool Input::smLastKeyboardActivated; +bool Input::smLastMouseActivated; +bool Input::smLastJoystickActivated; +InputEvent Input::smInputEvent; + +#ifdef LOG_INPUT +static HANDLE gInputLog; +#endif + +static void fillAsciiTable(); + +//------------------------------------------------------------------------------ +// +// This function gets the standard ASCII code corresponding to our key code +// and the existing modifier key state. +// +//------------------------------------------------------------------------------ +struct AsciiData +{ + struct KeyData + { + U16 ascii; + bool isDeadChar; + }; + + KeyData upper; + KeyData lower; + KeyData goofy; +}; + + +#define NUM_KEYS ( KEY_OEM_102 + 1 ) +#define KEY_FIRST KEY_ESCAPE +static AsciiData AsciiTable[NUM_KEYS]; + +//------------------------------------------------------------------------------ +void Input::init() +{ + Con::printf( "Input Init:" ); + + destroy(); + +#ifdef LOG_INPUT + struct tm* newTime; + time_t aclock; + time( &aclock ); + newTime = localtime( &aclock ); + asctime( newTime ); + + gInputLog = CreateFile( L"input.log", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); + log( "Input log opened at %s\n", asctime( newTime ) ); +#endif + + smActive = false; + smLastKeyboardActivated = true; + smLastMouseActivated = true; + smLastJoystickActivated = true; + + OSVERSIONINFO OSVersionInfo; + dMemset( &OSVersionInfo, 0, sizeof( OSVERSIONINFO ) ); + OSVersionInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO ); + if ( GetVersionEx( &OSVersionInfo ) ) + { +#ifdef LOG_INPUT + log( "Operating System:\n" ); + switch ( OSVersionInfo.dwPlatformId ) + { + case VER_PLATFORM_WIN32s: + log( " Win32s on Windows 3.1 version %d.%d\n", OSVersionInfo.dwMajorVersion, OSVersionInfo.dwMinorVersion ); + break; + + case VER_PLATFORM_WIN32_WINDOWS: + log( " Windows 95 version %d.%d\n", OSVersionInfo.dwMajorVersion, OSVersionInfo.dwMinorVersion ); + log( " Build number %d\n", LOWORD( OSVersionInfo.dwBuildNumber ) ); + break; + + case VER_PLATFORM_WIN32_NT: + log( " WinNT version %d.%d\n", OSVersionInfo.dwMajorVersion, OSVersionInfo.dwMinorVersion ); + log( " Build number %d\n", OSVersionInfo.dwBuildNumber ); + break; + } + + if ( OSVersionInfo.szCSDVersion != NULL ) + log( " %s\n", OSVersionInfo.szCSDVersion ); + + log( "\n" ); +#endif + + if ( !( OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT && OSVersionInfo.dwMajorVersion < 5 ) ) + { + smManager = new DInputManager; + if ( !smManager->enable() ) + { + Con::printf( " DirectInput not enabled." ); + delete smManager; + smManager = NULL; + } + else + { + DInputManager::init(); + Con::printf( " DirectInput enabled." ); + } + } + else + Con::printf( " WinNT detected -- DirectInput not enabled." ); + } + + // Init the current modifier keys + setModifierKeys(0); + fillAsciiTable(); + Con::printf( "" ); + + // Set ourselves to participate in per-frame processing. + Process::notify(Input::process, PROCESS_INPUT_ORDER); + +} + +//------------------------------------------------------------------------------ +ConsoleFunction( isJoystickDetected, bool, 1, 1, "isJoystickDetected()" ) +{ + argc; argv; + return( DInputDevice::joystickDetected() ); +} + +//------------------------------------------------------------------------------ +ConsoleFunction( getJoystickAxes, const char*, 2, 2, "getJoystickAxes( instance )" ) +{ + argc; + DInputManager* mgr = dynamic_cast( Input::getManager() ); + if ( mgr ) + return( mgr->getJoystickAxesString( dAtoi( argv[1] ) ) ); + + return( "" ); +} + +//------------------------------------------------------------------------------ +static void fillAsciiTable() +{ +#ifdef LOG_INPUT + char buf[256]; + Input::log( "--- Filling the ASCII table! ---\n" ); +#endif + + //HKL layout = GetKeyboardLayout( 0 ); + U8 state[256]; + U16 ascii[2]; + U32 dikCode, vKeyCode, keyCode; + S32 result; + + dMemset( &AsciiTable, 0, sizeof( AsciiTable ) ); + dMemset( &state, 0, sizeof( state ) ); + + for ( keyCode = KEY_FIRST; keyCode < NUM_KEYS; keyCode++ ) + { + ascii[0] = ascii[1] = 0; + dikCode = Key_to_DIK( keyCode ); + + // This is a special case for numpad keys. + // + // The KEY_NUMPAD# torque types represent the event generated when a + // numpad key is pressed WITH NUMLOCK ON. Therefore it does have an ascii + // value, but to get it from windows we must specify in the keyboard state + // that numlock is on. + if ( KEY_NUMPAD0 <= keyCode && keyCode <= KEY_NUMPAD9 ) + { + state[VK_NUMLOCK] = 0x80; + + // Also, numpad keys return completely different keycodes when + // numlock is not pressed (home,insert,etc) and MapVirtualKey + // appears to always return those values. + // + // So I'm using TranslateKeyCodeToOS instead. Really it looks + // like we could be using this method for all of them and + // cut out the Key_to_DIK middleman. + // + vKeyCode = TranslateKeyCodeToOS( keyCode ); + + result = ToAscii( vKeyCode, dikCode, state, ascii, 0 ); + + AsciiTable[keyCode].lower.ascii = ascii[0]; + AsciiTable[keyCode].upper.ascii = 0; + AsciiTable[keyCode].goofy.ascii = 0; + + state[VK_NUMLOCK] = 0; + + continue; + } + + if ( dikCode ) + { + //vKeyCode = MapVirtualKeyEx( dikCode, 1, layout ); + vKeyCode = MapVirtualKey( dikCode, 1 ); +#ifdef LOG_INPUT + dSprintf( buf, sizeof( buf ), "KC: %#04X DK: %#04X VK: %#04X\n", + keyCode, dikCode, vKeyCode ); + Input::log( buf ); +#endif + + // Lower case: + ascii[0] = ascii[1] = 0; + //result = ToAsciiEx( vKeyCode, dikCode, state, ascii, 0, layout ); + result = ToAscii( vKeyCode, dikCode, state, ascii, 0 ); +#ifdef LOG_INPUT + dSprintf( buf, sizeof( buf ), " LOWER- R: %d A[0]: %#06X A[1]: %#06X\n", + result, ascii[0], ascii[1] ); + Input::log( buf ); +#endif + if ( result == 2 ) + AsciiTable[keyCode].lower.ascii = ascii[1] ? ascii[1] : ( ascii[0] >> 8 ); + else if ( result == 1 ) + AsciiTable[keyCode].lower.ascii = ascii[0]; + else if ( result < 0 ) + { + AsciiTable[keyCode].lower.ascii = ascii[0]; + AsciiTable[keyCode].lower.isDeadChar = true; + // Need to clear the dead character from the keyboard layout: + //ToAsciiEx( vKeyCode, dikCode, state, ascii, 0, layout ); + ToAscii( vKeyCode, dikCode, state, ascii, 0 ); + } + + // Upper case: + ascii[0] = ascii[1] = 0; + state[VK_SHIFT] = 0x80; + //result = ToAsciiEx( vKeyCode, dikCode, state, ascii, 0, layout ); + result = ToAscii( vKeyCode, dikCode, state, ascii, 0 ); +#ifdef LOG_INPUT + dSprintf( buf, sizeof( buf ), " UPPER- R: %d A[0]: %#06X A[1]: %#06X\n", + result, ascii[0], ascii[1] ); + Input::log( buf ); +#endif + if ( result == 2 ) + AsciiTable[keyCode].upper.ascii = ascii[1] ? ascii[1] : ( ascii[0] >> 8 ); + else if ( result == 1 ) + AsciiTable[keyCode].upper.ascii = ascii[0]; + else if ( result < 0 ) + { + AsciiTable[keyCode].upper.ascii = ascii[0]; + AsciiTable[keyCode].upper.isDeadChar = true; + // Need to clear the dead character from the keyboard layout: + //ToAsciiEx( vKeyCode, dikCode, state, ascii, 0, layout ); + ToAscii( vKeyCode, dikCode, state, ascii, 0 ); + } + state[VK_SHIFT] = 0; + + // Foreign mod case: + ascii[0] = ascii[1] = 0; + state[VK_CONTROL] = 0x80; + state[VK_MENU] = 0x80; + //result = ToAsciiEx( vKeyCode, dikCode, state, ascii, 0, layout ); + result = ToAscii( vKeyCode, dikCode, state, ascii, 0 ); +#ifdef LOG_INPUT + dSprintf( buf, sizeof( buf ), " GOOFY- R: %d A[0]: %#06X A[1]: %#06X\n", + result, ascii[0], ascii[1] ); + Input::log( buf ); +#endif + if ( result == 2 ) + AsciiTable[keyCode].goofy.ascii = ascii[1] ? ascii[1] : ( ascii[0] >> 8 ); + else if ( result == 1 ) + AsciiTable[keyCode].goofy.ascii = ascii[0]; + else if ( result < 0 ) + { + AsciiTable[keyCode].goofy.ascii = ascii[0]; + AsciiTable[keyCode].goofy.isDeadChar = true; + // Need to clear the dead character from the keyboard layout: + //ToAsciiEx( vKeyCode, dikCode, state, ascii, 0, layout ); + ToAscii( vKeyCode, dikCode, state, ascii, 0 ); + } + state[VK_CONTROL] = 0; + state[VK_MENU] = 0; + } + } + +#ifdef LOG_INPUT + Input::log( "--- Finished filling the ASCII table! ---\n\n" ); +#endif +} + +//------------------------------------------------------------------------------ +U16 Input::getKeyCode( U16 asciiCode ) +{ + U16 keyCode = 0; + U16 i; + + // This is done three times so the lowerkey will always + // be found first. Some foreign keyboards have duplicate + // chars on some keys. + for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ ) + { + if ( AsciiTable[i].lower.ascii == asciiCode ) + { + keyCode = i; + break; + }; + } + + for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ ) + { + if ( AsciiTable[i].upper.ascii == asciiCode ) + { + keyCode = i; + break; + }; + } + + for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ ) + { + if ( AsciiTable[i].goofy.ascii == asciiCode ) + { + keyCode = i; + break; + }; + } + + return( keyCode ); +} + +//------------------------------------------------------------------------------ +U16 Input::getAscii( U16 keyCode, KEY_STATE keyState ) +{ + if ( keyCode >= NUM_KEYS ) + return 0; + + switch ( keyState ) + { + case STATE_LOWER: + return AsciiTable[keyCode].lower.ascii; + case STATE_UPPER: + return AsciiTable[keyCode].upper.ascii; + case STATE_GOOFY: + return AsciiTable[keyCode].goofy.ascii; + default: + return(0); + + } +} + +//------------------------------------------------------------------------------ +void Input::destroy() +{ + Process::remove(Input::process); + +#ifdef LOG_INPUT + if ( gInputLog ) + { + log( "*** CLOSING LOG ***\n" ); + CloseHandle( gInputLog ); + gInputLog = NULL; + } +#endif + + if ( smManager && smManager->isEnabled() ) + { + smManager->disable(); + delete smManager; + smManager = NULL; + } +} + +//------------------------------------------------------------------------------ +bool Input::enable() +{ + if ( smManager && !smManager->isEnabled() ) + return( smManager->enable() ); + + return( false ); +} + +//------------------------------------------------------------------------------ +void Input::disable() +{ + if ( smManager && smManager->isEnabled() ) + smManager->disable(); +} + +//------------------------------------------------------------------------------ + +void Input::activate() +{ +#ifdef UNICODE + //winState.imeHandle = ImmGetContext( getWin32WindowHandle() ); + //ImmReleaseContext( getWin32WindowHandle(), winState.imeHandle ); +#endif + + if ( !Con::getBoolVariable( "$enableDirectInput" ) ) + return; + + if ( smManager && smManager->isEnabled() && !smActive ) + { + Con::printf( "Activating DirectInput..." ); +#ifdef LOG_INPUT + Input::log( "Activating DirectInput...\n" ); +#endif + smActive = true; + DInputManager* dInputManager = dynamic_cast( smManager ); + if ( dInputManager ) + { + if ( dInputManager->isJoystickEnabled() && smLastJoystickActivated ) + dInputManager->activateJoystick(); + } + } +} + +//------------------------------------------------------------------------------ +void Input::deactivate() +{ + if ( smManager && smManager->isEnabled() && smActive ) + { +#ifdef LOG_INPUT + Input::log( "Deactivating DirectInput...\n" ); +#endif + DInputManager* dInputManager = dynamic_cast( smManager ); + + if ( dInputManager ) + { + smLastJoystickActivated = dInputManager->isJoystickActive(); + dInputManager->deactivateJoystick(); + } + + smActive = false; + Con::printf( "DirectInput deactivated." ); + } +} + +//------------------------------------------------------------------------------ +bool Input::isEnabled() +{ + if ( smManager ) + return smManager->isEnabled(); + return false; +} + +//------------------------------------------------------------------------------ +bool Input::isActive() +{ + return smActive; +} + +//------------------------------------------------------------------------------ +void Input::process() +{ + if ( smManager && smManager->isEnabled() && smActive ) + smManager->process(); +} + +//------------------------------------------------------------------------------ +InputManager* Input::getManager() +{ + return( smManager ); +} + +#ifdef LOG_INPUT +//------------------------------------------------------------------------------ +void Input::log( const char* format, ... ) +{ + if ( !gInputLog ) + return; + + va_list argptr; + va_start( argptr, format ); + + char buffer[512]; + dVsprintf( buffer, 511, format, argptr ); + DWORD bytes; + WriteFile( gInputLog, buffer, dStrlen( buffer ), &bytes, NULL ); + + va_end( argptr ); +} + +ConsoleFunction( inputLog, void, 2, 2, "inputLog( string )" ) +{ + argc; + Input::log( "%s\n", argv[1] ); +} +#endif // LOG_INPUT + + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +static U8 VcodeRemap[256] = +{ +0, // 0x00 +0, // 0x01 VK_LBUTTON +0, // 0x02 VK_RBUTTON +0, // 0x03 VK_CANCEL +0, // 0x04 VK_MBUTTON +0, // 0x05 +0, // 0x06 +0, // 0x07 +KEY_BACKSPACE, // 0x08 VK_BACK +KEY_TAB, // 0x09 VK_TAB +0, // 0x0A +0, // 0x0B +0, // 0x0C VK_CLEAR +KEY_RETURN, // 0x0D VK_RETURN +0, // 0x0E +0, // 0x0F +KEY_SHIFT, // 0x10 VK_SHIFT +KEY_CONTROL, // 0x11 VK_CONTROL +KEY_ALT, // 0x12 VK_MENU +KEY_PAUSE, // 0x13 VK_PAUSE +KEY_CAPSLOCK, // 0x14 VK_CAPITAL +0, // 0x15 VK_KANA, VK_HANGEUL, VK_HANGUL +0, // 0x16 +0, // 0x17 VK_JUNJA +0, // 0x18 VK_FINAL +0, // 0x19 VK_HANJA, VK_KANJI +0, // 0x1A +KEY_ESCAPE, // 0x1B VK_ESCAPE + +0, // 0x1C VK_CONVERT +0, // 0x1D VK_NONCONVERT +0, // 0x1E VK_ACCEPT +0, // 0x1F VK_MODECHANGE + +KEY_SPACE, // 0x20 VK_SPACE +KEY_PAGE_UP, // 0x21 VK_PRIOR +KEY_PAGE_DOWN, // 0x22 VK_NEXT +KEY_END, // 0x23 VK_END +KEY_HOME, // 0x24 VK_HOME +KEY_LEFT, // 0x25 VK_LEFT +KEY_UP, // 0x26 VK_UP +KEY_RIGHT, // 0x27 VK_RIGHT +KEY_DOWN, // 0x28 VK_DOWN +0, // 0x29 VK_SELECT +KEY_PRINT, // 0x2A VK_PRINT +0, // 0x2B VK_EXECUTE +0, // 0x2C VK_SNAPSHOT +KEY_INSERT, // 0x2D VK_INSERT +KEY_DELETE, // 0x2E VK_DELETE +KEY_HELP, // 0x2F VK_HELP + +KEY_0, // 0x30 VK_0 VK_0 thru VK_9 are the same as ASCII '0' thru '9' (// 0x30 - // 0x39) +KEY_1, // 0x31 VK_1 +KEY_2, // 0x32 VK_2 +KEY_3, // 0x33 VK_3 +KEY_4, // 0x34 VK_4 +KEY_5, // 0x35 VK_5 +KEY_6, // 0x36 VK_6 +KEY_7, // 0x37 VK_7 +KEY_8, // 0x38 VK_8 +KEY_9, // 0x39 VK_9 +0, // 0x3A +0, // 0x3B +0, // 0x3C +0, // 0x3D +0, // 0x3E +0, // 0x3F +0, // 0x40 + +KEY_A, // 0x41 VK_A VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (// 0x41 - // 0x5A) +KEY_B, // 0x42 VK_B +KEY_C, // 0x43 VK_C +KEY_D, // 0x44 VK_D +KEY_E, // 0x45 VK_E +KEY_F, // 0x46 VK_F +KEY_G, // 0x47 VK_G +KEY_H, // 0x48 VK_H +KEY_I, // 0x49 VK_I +KEY_J, // 0x4A VK_J +KEY_K, // 0x4B VK_K +KEY_L, // 0x4C VK_L +KEY_M, // 0x4D VK_M +KEY_N, // 0x4E VK_N +KEY_O, // 0x4F VK_O +KEY_P, // 0x50 VK_P +KEY_Q, // 0x51 VK_Q +KEY_R, // 0x52 VK_R +KEY_S, // 0x53 VK_S +KEY_T, // 0x54 VK_T +KEY_U, // 0x55 VK_U +KEY_V, // 0x56 VK_V +KEY_W, // 0x57 VK_W +KEY_X, // 0x58 VK_X +KEY_Y, // 0x59 VK_Y +KEY_Z, // 0x5A VK_Z + + +KEY_WIN_LWINDOW, // 0x5B VK_LWIN +KEY_WIN_RWINDOW, // 0x5C VK_RWIN +KEY_WIN_APPS, // 0x5D VK_APPS +0, // 0x5E +0, // 0x5F + +KEY_NUMPAD0, // 0x60 VK_NUMPAD0 +KEY_NUMPAD1, // 0x61 VK_NUMPAD1 +KEY_NUMPAD2, // 0x62 VK_NUMPAD2 +KEY_NUMPAD3, // 0x63 VK_NUMPAD3 +KEY_NUMPAD4, // 0x64 VK_NUMPAD4 +KEY_NUMPAD5, // 0x65 VK_NUMPAD5 +KEY_NUMPAD6, // 0x66 VK_NUMPAD6 +KEY_NUMPAD7, // 0x67 VK_NUMPAD7 +KEY_NUMPAD8, // 0x68 VK_NUMPAD8 +KEY_NUMPAD9, // 0x69 VK_NUMPAD9 +KEY_MULTIPLY, // 0x6A VK_MULTIPLY +KEY_ADD, // 0x6B VK_ADD +KEY_SEPARATOR, // 0x6C VK_SEPARATOR +KEY_SUBTRACT, // 0x6D VK_SUBTRACT +KEY_DECIMAL, // 0x6E VK_DECIMAL +KEY_DIVIDE, // 0x6F VK_DIVIDE +KEY_F1, // 0x70 VK_F1 +KEY_F2, // 0x71 VK_F2 +KEY_F3, // 0x72 VK_F3 +KEY_F4, // 0x73 VK_F4 +KEY_F5, // 0x74 VK_F5 +KEY_F6, // 0x75 VK_F6 +KEY_F7, // 0x76 VK_F7 +KEY_F8, // 0x77 VK_F8 +KEY_F9, // 0x78 VK_F9 +KEY_F10, // 0x79 VK_F10 +KEY_F11, // 0x7A VK_F11 +KEY_F12, // 0x7B VK_F12 +KEY_F13, // 0x7C VK_F13 +KEY_F14, // 0x7D VK_F14 +KEY_F15, // 0x7E VK_F15 +KEY_F16, // 0x7F VK_F16 +KEY_F17, // 0x80 VK_F17 +KEY_F18, // 0x81 VK_F18 +KEY_F19, // 0x82 VK_F19 +KEY_F20, // 0x83 VK_F20 +KEY_F21, // 0x84 VK_F21 +KEY_F22, // 0x85 VK_F22 +KEY_F23, // 0x86 VK_F23 +KEY_F24, // 0x87 VK_F24 +0, // 0x88 +0, // 0x89 +0, // 0x8A +0, // 0x8B +0, // 0x8C +0, // 0x8D +0, // 0x8E +0, // 0x8F + +KEY_NUMLOCK, // 0x90 VK_NUMLOCK +KEY_SCROLLLOCK, // 0x91 VK_OEM_SCROLL +0, // 0x92 +0, // 0x93 +0, // 0x94 +0, // 0x95 +0, // 0x96 +0, // 0x97 +0, // 0x98 +0, // 0x99 +0, // 0x9A +0, // 0x9B +0, // 0x9C +0, // 0x9D +0, // 0x9E +0, // 0x9F + +KEY_LSHIFT, // 0xA0 VK_LSHIFT +KEY_RSHIFT, // 0xA1 VK_RSHIFT +KEY_LCONTROL, // 0xA2 VK_LCONTROL +KEY_RCONTROL, // 0xA3 VK_RCONTROL +KEY_LALT, // 0xA4 VK_LMENU +KEY_RALT, // 0xA5 VK_RMENU +0, // 0xA6 +0, // 0xA7 +0, // 0xA8 +0, // 0xA9 +0, // 0xAA +0, // 0xAB +0, // 0xAC +0, // 0xAD +0, // 0xAE +0, // 0xAF +0, // 0xB0 +0, // 0xB1 +0, // 0xB2 +0, // 0xB3 +0, // 0xB4 +0, // 0xB5 +0, // 0xB6 +0, // 0xB7 +0, // 0xB8 +0, // 0xB9 +KEY_SEMICOLON, // 0xBA VK_OEM_1 +KEY_EQUALS, // 0xBB VK_OEM_PLUS +KEY_COMMA, // 0xBC VK_OEM_COMMA +KEY_MINUS, // 0xBD VK_OEM_MINUS +KEY_PERIOD, // 0xBE VK_OEM_PERIOD +KEY_SLASH, // 0xBF VK_OEM_2 +KEY_TILDE, // 0xC0 VK_OEM_3 +0, // 0xC1 +0, // 0xC2 +0, // 0xC3 +0, // 0xC4 +0, // 0xC5 +0, // 0xC6 +0, // 0xC7 +0, // 0xC8 +0, // 0xC9 +0, // 0xCA +0, // 0xCB +0, // 0xCC +0, // 0xCD +0, // 0xCE +0, // 0xCF +0, // 0xD0 +0, // 0xD1 +0, // 0xD2 +0, // 0xD3 +0, // 0xD4 +0, // 0xD5 +0, // 0xD6 +0, // 0xD7 +0, // 0xD8 +0, // 0xD9 +0, // 0xDA +KEY_LBRACKET, // 0xDB VK_OEM_4 +KEY_BACKSLASH, // 0xDC VK_OEM_5 +KEY_RBRACKET, // 0xDD VK_OEM_6 +KEY_APOSTROPHE, // 0xDE VK_OEM_7 +0, // 0xDF VK_OEM_8 +0, // 0xE0 +0, // 0xE1 VK_OEM_AX AX key on Japanese AX keyboard +KEY_OEM_102, // 0xE2 VK_OEM_102 +0, // 0xE3 +0, // 0xE4 + +0, // 0xE5 VK_PROCESSKEY + +0, // 0xE6 +0, // 0xE7 +0, // 0xE8 +0, // 0xE9 +0, // 0xEA +0, // 0xEB +0, // 0xEC +0, // 0xED +0, // 0xEE +0, // 0xEF + +0, // 0xF0 +0, // 0xF1 +0, // 0xF2 +0, // 0xF3 +0, // 0xF4 +0, // 0xF5 + +0, // 0xF6 VK_ATTN +0, // 0xF7 VK_CRSEL +0, // 0xF8 VK_EXSEL +0, // 0xF9 VK_EREOF +0, // 0xFA VK_PLAY +0, // 0xFB VK_ZOOM +0, // 0xFC VK_NONAME +0, // 0xFD VK_PA1 +0, // 0xFE VK_OEM_CLEAR +0 // 0xFF +}; + + +//------------------------------------------------------------------------------ +// +// This function translates a virtual key code to our corresponding internal +// key code using the preceding table. +// +//------------------------------------------------------------------------------ +U8 TranslateOSKeyCode(U8 vcode) +{ + return VcodeRemap[vcode]; +} + +U8 TranslateKeyCodeToOS(U8 keycode) +{ + for(S32 i = 0;i < sizeof(VcodeRemap) / sizeof(U8);++i) + { + if(VcodeRemap[i] == keycode) + return i; + } + return 0; +} + +//----------------------------------------------------------------------------- +// Clipboard functions +const char* Platform::getClipboard() +{ + HGLOBAL hGlobal; + LPVOID pGlobal; + + //make sure we can access the clipboard + if (!IsClipboardFormatAvailable(CF_TEXT)) + return ""; + if (!OpenClipboard(NULL)) + return ""; + + hGlobal = GetClipboardData(CF_TEXT); + pGlobal = GlobalLock(hGlobal); + S32 cbLength = strlen((char *)pGlobal); + char *returnBuf = Con::getReturnBuffer(cbLength + 1); + strcpy(returnBuf, (char *)pGlobal); + returnBuf[cbLength] = '\0'; + GlobalUnlock(hGlobal); + CloseClipboard(); + + //note - this function never returns NULL + return returnBuf; +} + +//----------------------------------------------------------------------------- +bool Platform::setClipboard(const char *text) +{ + if (!text) + return false; + + //make sure we can access the clipboard + if (!OpenClipboard(NULL)) + return false; + + S32 cbLength = strlen(text); + + HGLOBAL hGlobal; + LPVOID pGlobal; + + hGlobal = GlobalAlloc(GHND, cbLength + 1); + pGlobal = GlobalLock (hGlobal); + + strcpy((char *)pGlobal, text); + + GlobalUnlock(hGlobal); + + EmptyClipboard(); + SetClipboardData(CF_TEXT, hGlobal); + CloseClipboard(); + + return true; +} + diff --git a/Engine/source/platformWin32/winMath.cpp b/Engine/source/platformWin32/winMath.cpp new file mode 100644 index 000000000..cdcba5eae --- /dev/null +++ b/Engine/source/platformWin32/winMath.cpp @@ -0,0 +1,129 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/strings/stringFunctions.h" +#include "console/console.h" + +#include "math/mMath.h" + + +extern void mInstallLibrary_C(); +extern void mInstallLibrary_ASM(); +extern void mInstall_AMD_Math(); +extern void mInstall_Library_SSE(); + +//-------------------------------------- +ConsoleFunction( mathInit, void, 1, 10, "( ... )" + "@brief Install the math library with specified extensions.\n\n" + "Possible parameters are:\n\n" + " - 'DETECT' Autodetect math lib settings.\n\n" + " - 'C' Enable the C math routines. C routines are always enabled.\n\n" + " - 'FPU' Enable floating point unit routines.\n\n" + " - 'MMX' Enable MMX math routines.\n\n" + " - '3DNOW' Enable 3dNow! math routines.\n\n" + " - 'SSE' Enable SSE math routines.\n\n" + "@ingroup Math") + + +{ + U32 properties = CPU_PROP_C; // C entensions are always used + + if (argc == 1) + { + Math::init(0); + return; + } + for (argc--, argv++; argc; argc--, argv++) + { + if (dStricmp(*argv, "DETECT") == 0) { + Math::init(0); + return; + } + if (dStricmp(*argv, "C") == 0) { + properties |= CPU_PROP_C; + continue; + } + if (dStricmp(*argv, "FPU") == 0) { + properties |= CPU_PROP_FPU; + continue; + } + if (dStricmp(*argv, "MMX") == 0) { + properties |= CPU_PROP_MMX; + continue; + } + if (dStricmp(*argv, "3DNOW") == 0) { + properties |= CPU_PROP_3DNOW; + continue; + } + if (dStricmp(*argv, "SSE") == 0) { + properties |= CPU_PROP_SSE; + continue; + } + Con::printf("Error: MathInit(): ignoring unknown math extension '%s'", *argv); + } + Math::init(properties); +} + + + +//------------------------------------------------------------------------------ +void Math::init(U32 properties) +{ + if (!properties) + // detect what's available + properties = Platform::SystemInfo.processor.properties; + else + // Make sure we're not asking for anything that's not supported + properties &= Platform::SystemInfo.processor.properties; + + Con::printf("Math Init:"); + Con::printf(" Installing Standard C extensions"); + mInstallLibrary_C(); + + Con::printf(" Installing Assembly extensions"); + mInstallLibrary_ASM(); + + if (properties & CPU_PROP_FPU) + { + Con::printf(" Installing FPU extensions"); + } + + if (properties & CPU_PROP_MMX) + { + Con::printf(" Installing MMX extensions"); + if (properties & CPU_PROP_3DNOW) + { + Con::printf(" Installing 3DNow extensions"); + mInstall_AMD_Math(); + } + } + + if (properties & CPU_PROP_SSE) + { + Con::printf(" Installing SSE extensions"); + mInstall_Library_SSE(); + } + + Con::printf(" "); +} + + diff --git a/Engine/source/platformWin32/winMath_ASM.cpp b/Engine/source/platformWin32/winMath_ASM.cpp new file mode 100644 index 000000000..0bc373494 --- /dev/null +++ b/Engine/source/platformWin32/winMath_ASM.cpp @@ -0,0 +1,109 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "math/mMath.h" + +#if defined(TORQUE_SUPPORTS_VC_INLINE_X86_ASM) +static S32 m_mulDivS32_ASM(S32 a, S32 b, S32 c) +{ // a * b / c + S32 r; + _asm + { + mov eax, a + imul b + idiv c + mov r, eax + } + return r; +} + + +static U32 m_mulDivU32_ASM(S32 a, S32 b, U32 c) +{ // a * b / c + S32 r; + _asm + { + mov eax, a + mov edx, 0 + mul b + div c + mov r, eax + } + return r; +} + +static void m_sincos_ASM( F32 angle, F32 *s, F32 *c ) +{ + _asm + { + fld angle + fsincos + mov eax, c + fstp dword ptr [eax] + mov eax, s + fstp dword ptr [eax] + } +} + +U32 Platform::getMathControlState() +{ + U16 cw; + _asm + { + fstcw cw + } + return cw; +} + +void Platform::setMathControlState(U32 state) +{ + U16 cw = state; + _asm + { + fldcw cw + } +} + +void Platform::setMathControlStateKnown() +{ + U16 cw = 0x27F; + _asm + { + fldcw cw + } +} + + +#endif + +//------------------------------------------------------------------------------ +void mInstallLibrary_ASM() +{ +#if defined(TORQUE_SUPPORTS_VC_INLINE_X86_ASM) + m_mulDivS32 = m_mulDivS32_ASM; + m_mulDivU32 = m_mulDivU32_ASM; + + m_sincos = m_sincos_ASM; +#endif +} + + diff --git a/Engine/source/platformWin32/winMemory.cpp b/Engine/source/platformWin32/winMemory.cpp new file mode 100644 index 000000000..c4cb821bc --- /dev/null +++ b/Engine/source/platformWin32/winMemory.cpp @@ -0,0 +1,74 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformWin32/platformWin32.h" +#include + +void* dMemcpy(void *dst, const void *src, unsigned size) +{ + return memcpy(dst,src,size); +} + + +//-------------------------------------- +void* dMemmove(void *dst, const void *src, unsigned size) +{ + return memmove(dst,src,size); +} + +//-------------------------------------- +void* dMemset(void *dst, S32 c, unsigned size) +{ + return memset(dst,c,size); +} + +//-------------------------------------- +S32 dMemcmp(const void *ptr1, const void *ptr2, unsigned len) +{ + return memcmp(ptr1, ptr2, len); +} + +#if defined(TORQUE_COMPILER_MINGW) +#include +#endif + +//-------------------------------------- + +void* dRealMalloc(dsize_t s) +{ + return malloc(s); +} + +void dRealFree(void* p) +{ + free(p); +} + +void *dMalloc_aligned(dsize_t in_size, int alignment) +{ + return _mm_malloc(in_size, alignment); +} + +void dFree_aligned(void* p) +{ + return _mm_free(p); +} \ No newline at end of file diff --git a/Engine/source/platformWin32/winProcessControl.cpp b/Engine/source/platformWin32/winProcessControl.cpp new file mode 100644 index 000000000..f20905428 --- /dev/null +++ b/Engine/source/platformWin32/winProcessControl.cpp @@ -0,0 +1,70 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformWin32/platformWin32.h" +#include "core/strings/stringFunctions.h" + +void Platform::postQuitMessage(const U32 in_quitVal) +{ + if (!Platform::getWebDeployment()) + PostQuitMessage(in_quitVal); +} + +void Platform::debugBreak() +{ + DebugBreak(); +} + +void Platform::forceShutdown(S32 returnValue) +{ + // Don't do an ExitProcess here or you'll wreak havoc in a multithreaded + // environment. + + exit( returnValue ); +} + +void Platform::outputDebugString( const char *string, ... ) +{ + // Expand string. + + char buffer[ 2048 ]; + + va_list args; + va_start( args, string ); + + dVsprintf( buffer, sizeof( buffer ), string, args ); + va_end( args ); + + // Append a newline to buffer. This is better than calling OutputDebugStringA + // twice as in a multi-threaded environment, some other thread may output some + // stuff in between the two calls. + + U32 length = strlen( buffer ); + if( length == ( sizeof( buffer ) - 1 ) ) + length --; + + buffer[ length ] = '\n'; + buffer[ length + 1 ] = '\0'; + + OutputDebugStringA( buffer ); +} + diff --git a/Engine/source/platformWin32/winRedbook.cpp b/Engine/source/platformWin32/winRedbook.cpp new file mode 100644 index 000000000..38c5b3933 --- /dev/null +++ b/Engine/source/platformWin32/winRedbook.cpp @@ -0,0 +1,497 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformWin32/platformWin32.h" +#include "platform/platformRedBook.h" +#include "core/strings/unicode.h" +#include "core/strings/stringFunctions.h" + +class Win32RedBookDevice : public RedBookDevice +{ + private: + typedef RedBookDevice Parent; + + U32 mDeviceId; + + void setLastError(const char *); + void setLastError(U32); + + MIXERCONTROLDETAILS mMixerVolumeDetails; + MIXERCONTROLDETAILS_UNSIGNED mMixerVolumeValue; + + union { + HMIXEROBJ mVolumeDeviceId; + UINT mAuxVolumeDeviceId; + }; + + U32 mOriginalVolume; + bool mVolumeInitialized; + + bool mUsingMixer; + + void openVolume(); + void closeVolume(); + + public: + Win32RedBookDevice(); + ~Win32RedBookDevice(); + + U32 getDeviceId(); + + bool open(); + bool close(); + bool play(U32); + bool stop(); + bool getTrackCount(U32 *); + bool getVolume(F32 *); + bool setVolume(F32); +}; + +//------------------------------------------------------------------------------ +// Win32 specific +//------------------------------------------------------------------------------ +void installRedBookDevices() +{ + U32 bufSize = ::GetLogicalDriveStrings(0,0); + + char * buf = new char[bufSize]; + + ::GetLogicalDriveStringsA(bufSize, buf); + + char * str = buf; + while(*str) + { + if(::GetDriveTypeA(str) == DRIVE_CDROM) + { + Win32RedBookDevice * device = new Win32RedBookDevice; + device->mDeviceName = new char[dStrlen(str) + 1]; + dStrcpy(device->mDeviceName, str); + + RedBook::installDevice(device); + } + str += dStrlen(str) + 1; + } + + delete [] buf; +} + +void handleRedBookCallback(U32 code, U32 deviceId) +{ + if(code != MCI_NOTIFY_SUCCESSFUL) + return; + + Win32RedBookDevice * device = dynamic_cast(RedBook::getCurrentDevice()); + if(!device) + return; + + if(device->getDeviceId() != deviceId) + return; + + // only installed callback on play (no callback if play is aborted) + RedBook::handleCallback(RedBook::PlayFinished); +} + +//------------------------------------------------------------------------------ +// Class: Win32RedBookDevice +//------------------------------------------------------------------------------ +Win32RedBookDevice::Win32RedBookDevice() +{ + mVolumeInitialized = false; +} + +Win32RedBookDevice::~Win32RedBookDevice() +{ + close(); +} + +U32 Win32RedBookDevice::getDeviceId() +{ + return(mDeviceId); +} + +bool Win32RedBookDevice::open() +{ + if(mAcquired) + { + setLastError("Device is already open."); + return(false); + } + + U32 error; + + // open the device + MCI_OPEN_PARMS openParms; +#ifdef UNICODE + openParms.lpstrDeviceType = (LPCWSTR)MCI_DEVTYPE_CD_AUDIO; + + UTF16 buf[512]; + convertUTF8toUTF16((UTF8 *)mDeviceName, buf, sizeof(buf)); + openParms.lpstrElementName = buf; +#else + openParms.lpstrDeviceType = (LPCSTR)MCI_DEVTYPE_CD_AUDIO; + openParms.lpstrElementName = mDeviceName; +#endif + + error = mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID, (DWORD_PTR)(LPMCI_OPEN_PARMS)&openParms); + if(error) + { + // attempt to open as a shared device + error = mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID|MCI_OPEN_SHAREABLE, (DWORD_PTR)(LPMCI_OPEN_PARMS)&openParms); + if(error) + { + setLastError(error); + return(false); + } + } + + // set time mode to milliseconds + MCI_SET_PARMS setParms; + setParms.dwTimeFormat = MCI_FORMAT_MILLISECONDS; + + error = mciSendCommand(openParms.wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)(LPMCI_SET_PARMS)&setParms); + if(error) + { + setLastError(error); + return(false); + } + + // + mDeviceId = openParms.wDeviceID; + mAcquired = true; + + openVolume(); + setLastError(""); + return(true); +} + +bool Win32RedBookDevice::close() +{ + if(!mAcquired) + { + setLastError("Device has not been acquired"); + return(false); + } + + stop(); + + U32 error; + + MCI_GENERIC_PARMS closeParms; + error = mciSendCommand(mDeviceId, MCI_CLOSE, 0, (DWORD_PTR)(LPMCI_GENERIC_PARMS)&closeParms); + if(error) + { + setLastError(error); + return(false); + } + + mAcquired = false; + closeVolume(); + setLastError(""); + return(true); +} + +bool Win32RedBookDevice::play(U32 track) +{ + if(!mAcquired) + { + setLastError("Device has not been acquired"); + return(false); + } + + U32 numTracks; + if(!getTrackCount(&numTracks)) + return(false); + + if(track >= numTracks) + { + setLastError("Track index is out of range"); + return(false); + } + + MCI_STATUS_PARMS statusParms; + + // get track start time + statusParms.dwItem = MCI_STATUS_POSITION; + statusParms.dwTrack = track + 1; + + U32 error; + error = mciSendCommand(mDeviceId, MCI_STATUS, MCI_STATUS_ITEM|MCI_TRACK|MCI_WAIT, + (DWORD_PTR)(LPMCI_STATUS_PARMS)&statusParms); + + if(error) + { + setLastError(error); + return(false); + } + + MCI_PLAY_PARMS playParms; + playParms.dwFrom = statusParms.dwReturn; + + // get track end time + statusParms.dwItem = MCI_STATUS_LENGTH; + error = mciSendCommand(mDeviceId, MCI_STATUS, MCI_STATUS_ITEM|MCI_TRACK|MCI_WAIT, + (DWORD_PTR)(LPMCI_STATUS_PARMS)&statusParms); + + if(error) + { + setLastError(error); + return(false); + } + + playParms.dwTo = playParms.dwFrom + statusParms.dwReturn; + + // play the track + playParms.dwCallback = MAKELONG(getWin32WindowHandle(), 0); + error = mciSendCommand(mDeviceId, MCI_PLAY, MCI_FROM|MCI_TO|MCI_NOTIFY, + (DWORD_PTR)(LPMCI_PLAY_PARMS)&playParms); + + if(error) + { + setLastError(error); + return(false); + } + + setLastError(""); + return(true); +} + +bool Win32RedBookDevice::stop() +{ + if(!mAcquired) + { + setLastError("Device has not been acquired"); + return(false); + } + + MCI_GENERIC_PARMS genParms; + + U32 error = mciSendCommand(mDeviceId, MCI_STOP, 0, (DWORD_PTR)(LPMCI_GENERIC_PARMS)&genParms); + if(error) + { + setLastError(error); + return(false); + } + + setLastError(""); + return(true); +} + +bool Win32RedBookDevice::getTrackCount(U32 * numTracks) +{ + if(!mAcquired) + { + setLastError("Device has not been acquired"); + return(false); + } + + MCI_STATUS_PARMS statusParms; + + statusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS; + U32 error = mciSendCommand(mDeviceId, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR)(LPMCI_STATUS_PARMS)&statusParms); + if(error) + { + setLastError(error); + return(false); + } + + *numTracks = statusParms.dwReturn; + return(true); +} + +bool Win32RedBookDevice::getVolume(F32 * volume) +{ + if(!mAcquired) + { + setLastError("Device has not been acquired"); + return(false); + } + + if(!mVolumeInitialized) + { + setLastError("Volume failed to initialize"); + return(false); + } + + U32 vol = 0; + if(mUsingMixer) + { + mixerGetControlDetails(mVolumeDeviceId, &mMixerVolumeDetails, MIXER_GETCONTROLDETAILSF_VALUE); + vol = mMixerVolumeValue.dwValue; + } + else + auxGetVolume(mAuxVolumeDeviceId, (unsigned long *)&vol); + + vol &= 0xffff; + *volume = F32(vol) / 65535.f; + + setLastError(""); + return(true); +} + +bool Win32RedBookDevice::setVolume(F32 volume) +{ + if(!mAcquired) + { + setLastError("Device has not been acquired"); + return(false); + } + + if(!mVolumeInitialized) + { + setLastError("Volume failed to initialize"); + return(false); + } + + // move into a U32 - left/right U16 volumes + U32 vol = U32(volume * 65536.f); + if(vol > 0xffff) + vol = 0xffff; + + if(mUsingMixer) + { + mMixerVolumeValue.dwValue = vol; + mixerSetControlDetails(mVolumeDeviceId, &mMixerVolumeDetails, MIXER_SETCONTROLDETAILSF_VALUE); + } + else + { + vol |= vol << 16; + auxSetVolume(mAuxVolumeDeviceId, vol); + } + + setLastError(""); + return(true); +} + +//------------------------------------------------------------------------------ + +void Win32RedBookDevice::openVolume() +{ + setLastError(""); + + // first attempt to get the volume control through the mixer API + S32 i; + for(i = mixerGetNumDevs() - 1; i >= 0; i--) + { + // open the mixer + if(mixerOpen((HMIXER*)&mVolumeDeviceId, i, 0, 0, 0) == MMSYSERR_NOERROR) + { + MIXERLINE lineInfo; + memset(&lineInfo, 0, sizeof(lineInfo)); + lineInfo.cbStruct = sizeof(lineInfo); + lineInfo.dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC; + + // get the cdaudio line + if(mixerGetLineInfo(mVolumeDeviceId, &lineInfo, MIXER_GETLINEINFOF_COMPONENTTYPE) == MMSYSERR_NOERROR) + { + MIXERLINECONTROLS lineControls; + MIXERCONTROL volumeControl; + + memset(&lineControls, 0, sizeof(lineControls)); + lineControls.cbStruct = sizeof(lineControls); + lineControls.dwLineID = lineInfo.dwLineID; + lineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; + lineControls.cControls = 1; + lineControls.cbmxctrl = sizeof(volumeControl); + lineControls.pamxctrl = &volumeControl; + + memset(&volumeControl, 0, sizeof(volumeControl)); + volumeControl.cbStruct = sizeof(volumeControl); + + // get the volume control + if(mixerGetLineControls(mVolumeDeviceId, &lineControls, MIXER_GETLINECONTROLSF_ONEBYTYPE) == MMSYSERR_NOERROR) + { + memset(&mMixerVolumeDetails, 0, sizeof(mMixerVolumeDetails)); + mMixerVolumeDetails.cbStruct = sizeof(mMixerVolumeDetails); + mMixerVolumeDetails.dwControlID = volumeControl.dwControlID; + mMixerVolumeDetails.cChannels = 1; + mMixerVolumeDetails.cbDetails = sizeof(mMixerVolumeValue); + mMixerVolumeDetails.paDetails = &mMixerVolumeValue; + + memset(&mMixerVolumeValue, 0, sizeof(mMixerVolumeValue)); + + // query the current value + if(mixerGetControlDetails(mVolumeDeviceId, &mMixerVolumeDetails, MIXER_GETCONTROLDETAILSF_VALUE) == MMSYSERR_NOERROR) + { + mUsingMixer = true; + mVolumeInitialized = true; + mOriginalVolume = mMixerVolumeValue.dwValue; + return; + } + } + } + } + + mixerClose((HMIXER)mVolumeDeviceId); + } + + // try aux + for(i = auxGetNumDevs() - 1; i >= 0; i--) + { + AUXCAPS caps; + auxGetDevCaps(i, &caps, sizeof(AUXCAPS)); + if((caps.wTechnology == AUXCAPS_CDAUDIO) && (caps.dwSupport & AUXCAPS_VOLUME)) + { + mAuxVolumeDeviceId = i; + mVolumeInitialized = true; + mUsingMixer = false; + auxGetVolume(i, (unsigned long *)&mOriginalVolume); + return; + } + } + + setLastError("Volume failed to initialize"); +} + +void Win32RedBookDevice::closeVolume() +{ + setLastError(""); + if(!mVolumeInitialized) + return; + + if(mUsingMixer) + { + mMixerVolumeValue.dwValue = mOriginalVolume; + mixerSetControlDetails(mVolumeDeviceId, &mMixerVolumeDetails, MIXER_SETCONTROLDETAILSF_VALUE); + mixerClose((HMIXER)mVolumeDeviceId); + } + else + auxSetVolume(mAuxVolumeDeviceId, mOriginalVolume); + + mVolumeInitialized = false; +} + +//------------------------------------------------------------------------------ + +void Win32RedBookDevice::setLastError(const char * error) +{ + RedBook::setLastError(error); +} + +void Win32RedBookDevice::setLastError(U32 errorId) +{ + char buffer[256]; + if(!mciGetErrorStringA(errorId, buffer, sizeof(buffer) - 1)) + setLastError("Failed to get MCI error string!"); + else + setLastError(buffer); +} + diff --git a/Engine/source/platformWin32/winSemaphore.cpp b/Engine/source/platformWin32/winSemaphore.cpp new file mode 100644 index 000000000..9b58551cf --- /dev/null +++ b/Engine/source/platformWin32/winSemaphore.cpp @@ -0,0 +1,75 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformWin32/platformWin32.h" +#include "platform/threads/semaphore.h" + +class PlatformSemaphore +{ +public: + HANDLE *semaphore; + + PlatformSemaphore(S32 initialCount) + { + semaphore = new HANDLE; + *semaphore = CreateSemaphore(0, initialCount, S32_MAX, 0); + } + + ~PlatformSemaphore() + { + CloseHandle(*(HANDLE*)(semaphore)); + delete semaphore; + semaphore = NULL; + } +}; + +Semaphore::Semaphore(S32 initialCount) +{ + mData = new PlatformSemaphore(initialCount); +} + +Semaphore::~Semaphore() +{ + AssertFatal(mData && mData->semaphore, "Semaphore::destroySemaphore: invalid semaphore"); + delete mData; +} + +bool Semaphore::acquire(bool block, S32 timeoutMS) +{ + AssertFatal(mData && mData->semaphore, "Semaphore::acquireSemaphore: invalid semaphore"); + if(block) + { + WaitForSingleObject(*(HANDLE*)(mData->semaphore), timeoutMS != -1 ? timeoutMS : INFINITE ); + return(true); + } + else + { + DWORD result = WaitForSingleObject(*(HANDLE*)(mData->semaphore), 0); + return(result == WAIT_OBJECT_0); + } +} + +void Semaphore::release() +{ + AssertFatal(mData && mData->semaphore, "Semaphore::releaseSemaphore: invalid semaphore"); + ReleaseSemaphore(*(HANDLE*)(mData->semaphore), 1, 0); +} diff --git a/Engine/source/platformWin32/winTLS.cpp b/Engine/source/platformWin32/winTLS.cpp new file mode 100644 index 000000000..61bb2bc79 --- /dev/null +++ b/Engine/source/platformWin32/winTLS.cpp @@ -0,0 +1,90 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platformTLS.h" +#include "platformWin32/platformWin32.h" +#include "core/util/safeDelete.h" + +#define TORQUE_ALLOC_STORAGE(member, cls, data) \ + AssertFatal(sizeof(cls) <= sizeof(data), avar("Error, storage for %s must be %d bytes.", #cls, sizeof(cls))); \ + member = (cls *) data; \ + constructInPlace(member) + +//----------------------------------------------------------------------------- + +struct PlatformThreadStorage +{ + DWORD mTlsIndex; +}; + +//----------------------------------------------------------------------------- + +ThreadStorage::ThreadStorage() +{ + TORQUE_ALLOC_STORAGE(mThreadStorage, PlatformThreadStorage, mStorage); + mThreadStorage->mTlsIndex = TlsAlloc(); +} + +ThreadStorage::~ThreadStorage() +{ + TlsFree(mThreadStorage->mTlsIndex); + destructInPlace(mThreadStorage); +} + +void *ThreadStorage::get() +{ + return TlsGetValue(mThreadStorage->mTlsIndex); +} + +void ThreadStorage::set(void *value) +{ + TlsSetValue(mThreadStorage->mTlsIndex, value); +} + +/* POSIX IMPLEMENTATION LOOKS LIKE THIS: + +class PlatformThreadStorage +{ +pthread_key_t mThreadKey; +}; + +ThreadStorage::ThreadStorage() +{ +TORQUE_ALLOC_STORAGE(mThreadStorage, PlatformThreadStorage, mStorage); +pthread_key_create(&mThreadStorage->mThreadKey, NULL); +} + +ThreadStorage::~ThreadStorage() +{ +pthread_key_delete(mThreadStorage->mThreadKey); +} + +void *ThreadStorage::get() +{ +return pthread_getspecific(mThreadStorage->mThreadKey); +} + +void ThreadStorage::set(void *value) +{ +pthread_setspecific(mThreadStorage->mThreadKey, value); +} +*/ \ No newline at end of file diff --git a/Engine/source/platformWin32/winTime.cpp b/Engine/source/platformWin32/winTime.cpp new file mode 100644 index 000000000..e4174eb83 --- /dev/null +++ b/Engine/source/platformWin32/winTime.cpp @@ -0,0 +1,168 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "platformWin32/platformWin32.h" + +#include "time.h" + +void Platform::sleep(U32 ms) +{ + Sleep(ms); +} + +//-------------------------------------- +void Platform::getLocalTime(LocalTime <) +{ + struct tm *systime; + time_t long_time; + + time( &long_time ); // Get time as long integer. + systime = localtime( &long_time ); // Convert to local time. + + lt.sec = systime->tm_sec; + lt.min = systime->tm_min; + lt.hour = systime->tm_hour; + lt.month = systime->tm_mon; + lt.monthday = systime->tm_mday; + lt.weekday = systime->tm_wday; + lt.year = systime->tm_year; + lt.yearday = systime->tm_yday; + lt.isdst = systime->tm_isdst; +} + +String Platform::localTimeToString( const LocalTime < ) +{ + // Converting a LocalTime to SYSTEMTIME + // requires a few annoying adjustments. + SYSTEMTIME st; + st.wMilliseconds = 0; + st.wSecond = lt.sec; + st.wMinute = lt.min; + st.wHour = lt.hour; + st.wDay = lt.monthday; + st.wDayOfWeek = lt.weekday; + st.wMonth = lt.month + 1; + st.wYear = lt.year + 1900; + + TCHAR buffer[1024] = {0}; + + int result = 0; + + String outStr; + + // Note: The 'Ex' version of GetDateFormat and GetTimeFormat are preferred + // and have better support for supplemental locales but are not supported + // for version of windows prior to Vista. + // + // Would be nice if Torque was more aware of the OS version and + // take full advantage of it. + + result = GetDateFormat( LOCALE_USER_DEFAULT, + DATE_SHORTDATE, + &st, + NULL, + (LPTSTR)buffer, + 1024 ); + + // Also would be nice to have a standard system for torque to + // retrieve and display windows level errors using GetLastError and + // FormatMessage... + AssertWarn( result != 0, "Platform::getLocalTime" ); + + outStr += buffer; + outStr += "\t"; + + result = GetTimeFormat( LOCALE_USER_DEFAULT, + 0, + &st, + NULL, + (LPTSTR)buffer, + 1024 ); + + AssertWarn( result != 0, "Platform::localTimeToString, error occured!" ); + + outStr += buffer; + + return outStr; +} + +U32 Platform::getTime() +{ + time_t long_time; + time( &long_time ); + return long_time; +} + +void Platform::fileToLocalTime(const FileTime & ft, LocalTime * lt) +{ + if(!lt) + return; + + dMemset(lt, 0, sizeof(LocalTime)); + + FILETIME winFileTime; + winFileTime.dwLowDateTime = ft.v1; + winFileTime.dwHighDateTime = ft.v2; + + SYSTEMTIME winSystemTime; + + // convert the filetime to local time + FILETIME convertedFileTime; + if(::FileTimeToLocalFileTime(&winFileTime, &convertedFileTime)) + { + // get the time into system time struct + if(::FileTimeToSystemTime((const FILETIME *)&convertedFileTime, &winSystemTime)) + { + SYSTEMTIME * time = &winSystemTime; + + // fill it in... + lt->sec = time->wSecond; + lt->min = time->wMinute; + lt->hour = time->wHour; + lt->month = time->wMonth; + lt->monthday = time->wDay; + lt->weekday = time->wDayOfWeek; + lt->year = (time->wYear < 1900) ? 1900 : (time->wYear - 1900); + + // not calculated + lt->yearday = 0; + lt->isdst = false; + } + } +} + +U32 Platform::getRealMilliseconds() +{ + return GetTickCount(); +} + +U32 Platform::getVirtualMilliseconds() +{ + return winState.currentTime; +} + +void Platform::advanceTime(U32 delta) +{ + winState.currentTime += delta; +} + diff --git a/Engine/source/platformWin32/winTimer.cpp b/Engine/source/platformWin32/winTimer.cpp new file mode 100644 index 000000000..ce300d82c --- /dev/null +++ b/Engine/source/platformWin32/winTimer.cpp @@ -0,0 +1,88 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// Grab the win32 headers so we can access QPC +#define WIN32_LEAN_AND_MEAN +#include + +#include "platform/platformTimer.h" +#include "math/mMath.h" + +class Win32Timer : public PlatformTimer +{ +private: + U32 mTickCountCurrent; + U32 mTickCountNext; + S64 mPerfCountCurrent; + S64 mPerfCountNext; + S64 mFrequency; + F64 mPerfCountRemainderCurrent; + F64 mPerfCountRemainderNext; + bool mUsingPerfCounter; +public: + + Win32Timer() + { + mPerfCountRemainderCurrent = 0.0f; + + // Attempt to use QPC for high res timing, otherwise fallback to GTC. + mUsingPerfCounter = QueryPerformanceFrequency((LARGE_INTEGER *) &mFrequency); + if(mUsingPerfCounter) + mUsingPerfCounter = QueryPerformanceCounter((LARGE_INTEGER *) &mPerfCountCurrent); + if(!mUsingPerfCounter) + mTickCountCurrent = GetTickCount(); + } + + const S32 getElapsedMs() + { + if(mUsingPerfCounter) + { + // Use QPC, update remainders so we don't leak time, and return the elapsed time. + QueryPerformanceCounter( (LARGE_INTEGER *) &mPerfCountNext); + F64 elapsedF64 = (1000.0 * F64(mPerfCountNext - mPerfCountCurrent) / F64(mFrequency)); + elapsedF64 += mPerfCountRemainderCurrent; + U32 elapsed = (U32)mFloor(elapsedF64); + mPerfCountRemainderNext = elapsedF64 - F64(elapsed); + + return elapsed; + } + else + { + // Do something naive with GTC. + mTickCountNext = GetTickCount(); + return mTickCountNext - mTickCountCurrent; + } + } + + void reset() + { + // Do some simple copying to reset the timer to 0. + mTickCountCurrent = mTickCountNext; + mPerfCountCurrent = mPerfCountNext; + mPerfCountRemainderCurrent = mPerfCountRemainderNext; + } +}; + +PlatformTimer *PlatformTimer::create() +{ + return new Win32Timer(); +} diff --git a/Engine/source/platformWin32/winUser.cpp b/Engine/source/platformWin32/winUser.cpp new file mode 100644 index 000000000..666b0858b --- /dev/null +++ b/Engine/source/platformWin32/winUser.cpp @@ -0,0 +1,225 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/console.h" +#include "core/stringTable.h" +#include "core/strings/unicode.h" + +typedef long SHANDLE_PTR; +#include +#include +#include + +#define CSIDL_PROFILE 0x0028 + +const char *Platform::getUserDataDirectory() +{ + TCHAR szBuffer[ MAX_PATH + 1 ]; + + if(! SHGetSpecialFolderPath( NULL, szBuffer, CSIDL_APPDATA, true ) ) + return ""; + + TCHAR *ptr = szBuffer; + while(*ptr) + { + if(*ptr == '\\') + *ptr = '/'; + ++ptr; + } + +#ifdef UNICODE + char path[ MAX_PATH * 3 + 1 ]; + convertUTF16toUTF8( szBuffer, path, sizeof( path ) ); +#else + char* path = szBuffer; +#endif + + + return StringTable->insert( path ); +} + +const char *Platform::getUserHomeDirectory() +{ + TCHAR szBuffer[ MAX_PATH + 1 ]; + if(! SHGetSpecialFolderPath( NULL, szBuffer, CSIDL_PERSONAL, false ) ) + if(! SHGetSpecialFolderPath( NULL, szBuffer, CSIDL_COMMON_DOCUMENTS, false ) ) + return ""; + + TCHAR *ptr = szBuffer; + while(*ptr) + { + if(*ptr == '\\') + *ptr = '/'; + ++ptr; + } + +#ifdef UNICODE + char path[ MAX_PATH * 3 + 1 ]; + convertUTF16toUTF8( szBuffer, path, sizeof( path ) ); +#else + char* path = szBuffer; +#endif + + return StringTable->insert( path ); +} + + +bool Platform::getUserIsAdministrator() +{ + BOOL fReturn = FALSE; + DWORD dwStatus; + DWORD dwAccessMask; + DWORD dwAccessDesired; + DWORD dwACLSize; + DWORD dwStructureSize = sizeof(PRIVILEGE_SET); + PACL pACL = NULL; + PSID psidAdmin = NULL; + + HANDLE hToken = NULL; + HANDLE hImpersonationToken = NULL; + + PRIVILEGE_SET ps; + GENERIC_MAPPING GenericMapping; + + PSECURITY_DESCRIPTOR psdAdmin = NULL; + SID_IDENTIFIER_AUTHORITY SystemSidAuthority = SECURITY_NT_AUTHORITY; + + + /* + Determine if the current thread is running as a user that is a member of + the local admins group. To do this, create a security descriptor that + has a DACL which has an ACE that allows only local aministrators access. + Then, call AccessCheck with the current thread's token and the security + descriptor. It will say whether the user could access an object if it + had that security descriptor. Note: you do not need to actually create + the object. Just checking access against the security descriptor alone + will be sufficient. + */ + const DWORD ACCESS_READ = 1; + const DWORD ACCESS_WRITE = 2; + + + __try + { + + /* + AccessCheck() requires an impersonation token. We first get a primary + token and then create a duplicate impersonation token. The + impersonation token is not actually assigned to the thread, but is + used in the call to AccessCheck. Thus, this function itself never + impersonates, but does use the identity of the thread. If the thread + was impersonating already, this function uses that impersonation context. + */ + if (!OpenThreadToken(GetCurrentThread(), TOKEN_DUPLICATE|TOKEN_QUERY, TRUE, &hToken)) + { + if (GetLastError() != ERROR_NO_TOKEN) + __leave; + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE|TOKEN_QUERY, &hToken)) + __leave; + } + + if (!DuplicateToken (hToken, SecurityImpersonation, &hImpersonationToken)) + __leave; + + + /* + Create the binary representation of the well-known SID that + represents the local administrators group. Then create the security + descriptor and DACL with an ACE that allows only local admins access. + After that, perform the access check. This will determine whether + the current user is a local admin. + */ + if (!AllocateAndInitializeSid(&SystemSidAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidAdmin)) + __leave; + + psdAdmin = LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); + if (psdAdmin == NULL) + __leave; + + if (!InitializeSecurityDescriptor(psdAdmin, SECURITY_DESCRIPTOR_REVISION)) + __leave; + + // Compute size needed for the ACL. + dwACLSize = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psidAdmin) - sizeof(DWORD); + + pACL = (PACL)LocalAlloc(LPTR, dwACLSize); + if (pACL == NULL) + __leave; + + if (!InitializeAcl(pACL, dwACLSize, ACL_REVISION2)) + __leave; + + dwAccessMask= ACCESS_READ | ACCESS_WRITE; + + if (!AddAccessAllowedAce(pACL, ACL_REVISION2, dwAccessMask, psidAdmin)) + __leave; + + if (!SetSecurityDescriptorDacl(psdAdmin, TRUE, pACL, FALSE)) + __leave; + + /* + AccessCheck validates a security descriptor somewhat; set the group + and owner so that enough of the security descriptor is filled out to + make AccessCheck happy. + */ + SetSecurityDescriptorGroup(psdAdmin, psidAdmin, FALSE); + SetSecurityDescriptorOwner(psdAdmin, psidAdmin, FALSE); + + if (!IsValidSecurityDescriptor(psdAdmin)) + __leave; + + dwAccessDesired = ACCESS_READ; + + /* + Initialize GenericMapping structure even though you + do not use generic rights. + */ + GenericMapping.GenericRead = ACCESS_READ; + GenericMapping.GenericWrite = ACCESS_WRITE; + GenericMapping.GenericExecute = 0; + GenericMapping.GenericAll = ACCESS_READ | ACCESS_WRITE; + + if (!AccessCheck(psdAdmin, hImpersonationToken, dwAccessDesired, + &GenericMapping, &ps, &dwStructureSize, &dwStatus, + &fReturn)) + { + fReturn = FALSE; + __leave; + } + } + __finally + { + + // Clean up. + if (pACL) LocalFree(pACL); + if (psdAdmin) LocalFree(psdAdmin); + if (psidAdmin) FreeSid(psidAdmin); + if (hImpersonationToken) CloseHandle (hImpersonationToken); + if (hToken) CloseHandle (hToken); + } + + return fReturn; + +} \ No newline at end of file diff --git a/Engine/source/platformWin32/winVFS.cpp b/Engine/source/platformWin32/winVFS.cpp new file mode 100644 index 000000000..fabc33c66 --- /dev/null +++ b/Engine/source/platformWin32/winVFS.cpp @@ -0,0 +1,111 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformWin32/platformWin32.h" +#include "platform/platformVFS.h" +#include "console/console.h" + +#include "core/stream/memstream.h" +#include "core/util/zip/zipArchive.h" + +#include "core/util/safeDelete.h" + +#include "VFSRes.h" + +//----------------------------------------------------------------------------- + +struct Win32VFSState +{ + S32 mRefCount; + + HGLOBAL mResData; + + MemStream *mZipStream; + Zip::ZipArchive *mZip; + + Win32VFSState() : mResData(NULL), mZip(NULL), mRefCount(0), mZipStream(NULL) + { + } +}; + +static Win32VFSState gVFSState; + +//----------------------------------------------------------------------------- + +Zip::ZipArchive *openEmbeddedVFSArchive() +{ + if(gVFSState.mZip) + { + ++gVFSState.mRefCount; + return gVFSState.mZip; + } + + HRSRC hRsrc = FindResource(NULL, MAKEINTRESOURCE(IDR_ZIPFILE), dT("RT_RCDATA")); + + if(hRsrc == NULL) + return NULL; + + if((gVFSState.mResData = LoadResource(NULL, hRsrc)) == NULL) + return NULL; + + void * mem = LockResource(gVFSState.mResData); + if(mem != NULL) + { + U32 size = SizeofResource(NULL, hRsrc); + gVFSState.mZipStream = new MemStream(size, mem, true, false); + gVFSState.mZip = new Zip::ZipArchive; + + if(gVFSState.mZip->openArchive(gVFSState.mZipStream)) + { + ++gVFSState.mRefCount; + return gVFSState.mZip; + } + + SAFE_DELETE(gVFSState.mZip); + SAFE_DELETE(gVFSState.mZipStream); + } + + FreeResource(gVFSState.mResData); + gVFSState.mResData = NULL; + + return NULL; +} + +void closeEmbeddedVFSArchive() +{ + if(gVFSState.mRefCount == 0) + return; + + --gVFSState.mRefCount; + + if(gVFSState.mRefCount < 1) + { + SAFE_DELETE(gVFSState.mZip); + SAFE_DELETE(gVFSState.mZipStream); + + if(gVFSState.mResData) + { + FreeResource(gVFSState.mResData); + gVFSState.mResData = NULL; + } + } +} diff --git a/Engine/source/platformWin32/winVolume.cpp b/Engine/source/platformWin32/winVolume.cpp new file mode 100644 index 000000000..1df04e3ca --- /dev/null +++ b/Engine/source/platformWin32/winVolume.cpp @@ -0,0 +1,786 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include + +#include "core/crc.h" +#include "core/frameAllocator.h" +#include "core/util/str.h" +#include "core/strings/stringFunctions.h" +#include "core/strings/unicode.h" + +#include "platform/platformVolume.h" + +#include "platformWin32/winVolume.h" + +#include "console/console.h" + + +#ifndef NGROUPS_UMAX + #define NGROUPS_UMAX 32 +#endif + +namespace Torque +{ +namespace Win32 +{ + + // If the file is a Directory, Offline, System or Temporary then FALSE +#define S_ISREG(Flags) \ + !((Flags) & \ + (FILE_ATTRIBUTE_DIRECTORY | \ + FILE_ATTRIBUTE_OFFLINE | \ + FILE_ATTRIBUTE_SYSTEM | \ + FILE_ATTRIBUTE_TEMPORARY)) + +#define S_ISDIR(Flags) \ + ((Flags) & FILE_ATTRIBUTE_DIRECTORY) + +//----------------------------------------------------------------------------- + + class Win32FileSystemChangeNotifier : public FileSystemChangeNotifier + { + public: + Win32FileSystemChangeNotifier( FileSystem *fs ) + : FileSystemChangeNotifier( fs ) + { + VECTOR_SET_ASSOCIATION( mHandleList ); + VECTOR_SET_ASSOCIATION( mDirs ); + } + + // for use in the thread itself + U32 getNumHandles() const { return mHandleList.size(); } + HANDLE *getHANDLES() { return mHandleList.address(); } + + private: + virtual void internalProcessOnce(); + + virtual bool internalAddNotification( const Path &dir ); + virtual bool internalRemoveNotification( const Path &dir ); + + Vector mDirs; + Vector mHandleList; + }; + +//----------------------------------------------------------------------------- + +static String _BuildFileName(const String& prefix,const Path& path) +{ + // Need to join the path (minus the root) with our + // internal path name. + String file = prefix; + file = Path::Join(file, '/', path.getPath()); + file = Path::Join(file, '/', path.getFileName()); + file = Path::Join(file, '.', path.getExtension()); + return file; +} + +/* +static bool _IsFile(const String& file) +{ + // Get file info + WIN32_FIND_DATA info; + HANDLE handle = ::FindFirstFile(PathToOS(file).utf16(), &info); + ::FindClose(handle); + if (handle == INVALID_HANDLE_VALUE) + return false; + + return S_ISREG(info.dwFileAttributes); +} +*/ + +static bool _IsDirectory(const String& file) +{ + // Get file info + WIN32_FIND_DATAW info; + HANDLE handle = ::FindFirstFileW(PathToOS(file).utf16(), &info); + ::FindClose(handle); + if (handle == INVALID_HANDLE_VALUE) + return false; + + return S_ISDIR(info.dwFileAttributes); +} + + +//----------------------------------------------------------------------------- + +static void _CopyStatAttributes(const WIN32_FIND_DATAW& info, FileNode::Attributes* attr) +{ + // Fill in the return struct. + attr->flags = 0; + if (S_ISDIR(info.dwFileAttributes)) + attr->flags |= FileNode::Directory; + if (S_ISREG(info.dwFileAttributes)) + attr->flags |= FileNode::File; + + if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) + attr->flags |= FileNode::ReadOnly; + + attr->size = info.nFileSizeLow; + attr->mtime = Win32FileTimeToTime( + info.ftLastWriteTime.dwLowDateTime, + info.ftLastWriteTime.dwHighDateTime); + + attr->atime = Win32FileTimeToTime( + info.ftLastAccessTime.dwLowDateTime, + info.ftLastAccessTime.dwHighDateTime); +} + + +//----------------------------------------------------------------------------- + +bool Win32FileSystemChangeNotifier::internalAddNotification( const Path &dir ) +{ + for ( U32 i = 0; i < mDirs.size(); ++i ) + { + if ( mDirs[i] == dir ) + return false; + } + + Path fullFSPath = mFS->mapTo( dir ); + String osPath = PathToOS( fullFSPath ); + +// Con::printf( "[Win32FileSystemChangeNotifier::internalAddNotification] : [%s]", osPath.c_str() ); + + HANDLE changeHandle = ::FindFirstChangeNotificationW( + osPath.utf16(), // directory to watch + FALSE, // do not watch subtree + FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES); // watch file write changes + + if (changeHandle == INVALID_HANDLE_VALUE || changeHandle == NULL) + { + Con::errorf("[Win32FileSystemChangeNotifier::internalAddNotification] : failed on [%s] [%d]", osPath.c_str(), GetLastError()); + + return false; + } + + mDirs.push_back( dir ); + mHandleList.push_back( changeHandle ); + + return true; +} + +bool Win32FileSystemChangeNotifier::internalRemoveNotification( const Path &dir ) +{ + for ( U32 i = 0; i < mDirs.size(); ++i ) + { + if ( mDirs[i] != dir ) + continue; + + ::FindCloseChangeNotification( mHandleList[i] ); + mDirs.erase( i ); + mHandleList.erase( i ); + + return true; + } + + return false; +} + +void Win32FileSystemChangeNotifier::internalProcessOnce() +{ + // WaitForMultipleObjects has a limit of MAXIMUM_WAIT_OBJECTS (64 at + // the moment), so we have to loop till we've handled the entire set. + + for ( U32 i=0; i < mHandleList.size(); i += MAXIMUM_WAIT_OBJECTS ) + { + U32 numHandles = getMin( (U32)MAXIMUM_WAIT_OBJECTS, (U32)mHandleList.size() - i ); + + DWORD dwWaitStatus = WaitForMultipleObjects( numHandles, mHandleList.address()+i, FALSE, 0); + if ( dwWaitStatus == WAIT_FAILED || dwWaitStatus == WAIT_TIMEOUT ) + continue; + + if ( dwWaitStatus >= WAIT_OBJECT_0 && dwWaitStatus <= (WAIT_OBJECT_0 + numHandles - 1)) + { + U32 index = i + dwWaitStatus; + + // reset our notification + // NOTE: we do this before letting the volume system check mod times so we don't miss any. + // It is going to loop over the files and check their mod time vs. the saved time. + // This may result in extra calls to internalNotifyDirChanged(), but it will simpley check mod times again. + ::FindNextChangeNotification( mHandleList[index] ); + + internalNotifyDirChanged( mDirs[index] ); + } + } +} + +//----------------------------------------------------------------------------- + +Win32FileSystem::Win32FileSystem(String volume) +{ + mVolume = volume; + mChangeNotifier = new Win32FileSystemChangeNotifier( this ); +} + +Win32FileSystem::~Win32FileSystem() +{ +} + +FileNodeRef Win32FileSystem::resolve(const Path& path) +{ + String file = _BuildFileName(mVolume,path); + + WIN32_FIND_DATAW info; + HANDLE handle = ::FindFirstFileW(PathToOS(file).utf16(), &info); + ::FindClose(handle); + if (handle != INVALID_HANDLE_VALUE) + { + if (S_ISREG(info.dwFileAttributes)) + return new Win32File(path,file); + if (S_ISDIR(info.dwFileAttributes)) + return new Win32Directory(path,file); + } + return 0; +} + +FileNodeRef Win32FileSystem::create(const Path& path, FileNode::Mode mode) +{ + // The file will be created on disk when it's opened. + if (mode & FileNode::File) + return new Win32File(path,_BuildFileName(mVolume,path)); + + // Create with default permissions. + if (mode & FileNode::Directory) + { + String file = PathToOS(_BuildFileName(mVolume,path)); + + if (::CreateDirectoryW(file.utf16(), 0)) + return new Win32Directory(path, file); + } + return 0; +} + +bool Win32FileSystem::remove(const Path& path) +{ + // Should probably check for outstanding files or directory objects. + String file = PathToOS(_BuildFileName(mVolume,path)); + + WIN32_FIND_DATAW info; + HANDLE handle = ::FindFirstFileW(file.utf16(), &info); + ::FindClose(handle); + if (handle == INVALID_HANDLE_VALUE) + return false; + + if (S_ISDIR(info.dwFileAttributes)) + return ::RemoveDirectoryW(file.utf16()); + + return ::DeleteFileW(file.utf16()); +} + +bool Win32FileSystem::rename(const Path& from,const Path& to) +{ + String fa = PathToOS(_BuildFileName(mVolume,from)); + String fb = PathToOS(_BuildFileName(mVolume,to)); + + return MoveFile(fa.utf16(),fb.utf16()); +} + +Path Win32FileSystem::mapTo(const Path& path) +{ + return _BuildFileName(mVolume,path); +} + +Path Win32FileSystem::mapFrom(const Path& path) +{ + const String::SizeType volumePathLen = mVolume.length(); + + String pathStr = path.getFullPath(); + + if ( mVolume.compare( pathStr, volumePathLen, String::NoCase )) + return Path(); + + return pathStr.substr( volumePathLen, pathStr.length() - volumePathLen ); +} + +//----------------------------------------------------------------------------- + +Win32File::Win32File(const Path& path,String name) +{ + mPath = path; + mName = name; + mStatus = Closed; + mHandle = 0; +} + +Win32File::~Win32File() +{ + if (mHandle) + close(); +} + +Path Win32File::getName() const +{ + return mPath; +} + +FileNode::Status Win32File::getStatus() const +{ + return mStatus; +} + +bool Win32File::getAttributes(Attributes* attr) +{ + WIN32_FIND_DATAW info; + HANDLE handle = ::FindFirstFileW(PathToOS(mName).utf16(), &info); + ::FindClose(handle); + if (handle == INVALID_HANDLE_VALUE) + return false; + + _CopyStatAttributes(info,attr); + attr->name = mPath; + return true; +} + +U64 Win32File::getSize() +{ + U64 size; + + if (mStatus == Open) + { + // Special case if file is open (handles unflushed buffers) + if ( !GetFileSizeEx(mHandle, (PLARGE_INTEGER)&size) ) + size = 0; + return size; + } + else + { + // Fallback to generic function + size = File::getSize(); + } + + return size; +} + +U32 Win32File::calculateChecksum() +{ + if (!open( Read )) + return 0; + + U64 fileSize = getSize(); + U32 bufSize = 1024 * 1024 * 4; // 4MB + FrameTemp buf( bufSize ); + U32 crc = CRC::INITIAL_CRC_VALUE; + + while ( fileSize > 0 ) + { + U32 bytesRead = getMin( fileSize, bufSize ); + if ( read( buf, bytesRead ) != bytesRead ) + { + close(); + return 0; + } + + fileSize -= bytesRead; + crc = CRC::calculateCRC(buf, bytesRead, crc); + } + + close(); + + return crc; +} + +bool Win32File::open(AccessMode mode) +{ + close(); + + if (mName.isEmpty()) + return mStatus; + + struct Mode + { + DWORD mode,share,open; + } Modes[] = + { + { GENERIC_READ,FILE_SHARE_READ,OPEN_EXISTING }, // Read + { GENERIC_WRITE,0,CREATE_ALWAYS }, // Write + { GENERIC_WRITE | GENERIC_READ,0,OPEN_ALWAYS }, // ReadWrite + { GENERIC_WRITE,0,OPEN_ALWAYS } // WriteAppend + }; + + Mode& m = (mode == Read)? Modes[0]: (mode == Write)? Modes[1]: + (mode == ReadWrite)? Modes[2]: Modes[3]; + + mHandle = (void*)::CreateFileW(PathToOS(mName).utf16(), + m.mode, m.share, + NULL, m.open, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + + if ( mHandle == INVALID_HANDLE_VALUE || mHandle == NULL ) + { + _updateStatus(); + return false; + } + + mStatus = Open; + return true; +} + +bool Win32File::close() +{ + if (mHandle) + { + ::CloseHandle((HANDLE)mHandle); + mHandle = 0; + } + + mStatus = Closed; + return true; +} + +U32 Win32File::getPosition() +{ + if (mStatus == Open || mStatus == EndOfFile) + return ::SetFilePointer((HANDLE)mHandle,0,0,FILE_CURRENT); + return 0; +} + +U32 Win32File::setPosition(U32 delta, SeekMode mode) +{ + if (mStatus != Open && mStatus != EndOfFile) + return 0; + + DWORD fmode; + switch (mode) + { + case Begin: fmode = FILE_BEGIN; break; + case Current: fmode = FILE_CURRENT; break; + case End: fmode = FILE_END; break; + default: fmode = 0; break; + } + + DWORD pos = ::SetFilePointer((HANDLE)mHandle,delta,0,fmode); + if (pos == INVALID_SET_FILE_POINTER) + { + mStatus = UnknownError; + return 0; + } + + mStatus = Open; + + return pos; +} + +U32 Win32File::read(void* dst, U32 size) +{ + if (mStatus != Open && mStatus != EndOfFile) + return 0; + + DWORD bytesRead; + if (!::ReadFile((HANDLE)mHandle,dst,size,&bytesRead,0)) + _updateStatus(); + else if (bytesRead != size) + mStatus = EndOfFile; + + return bytesRead; +} + +U32 Win32File::write(const void* src, U32 size) +{ + if ((mStatus != Open && mStatus != EndOfFile) || !size) + return 0; + + DWORD bytesWritten; + if (!::WriteFile((HANDLE)mHandle,src,size,&bytesWritten,0)) + _updateStatus(); + return bytesWritten; +} + +void Win32File::_updateStatus() +{ + switch (::GetLastError()) + { + case ERROR_INVALID_ACCESS: mStatus = AccessDenied; break; + case ERROR_TOO_MANY_OPEN_FILES: mStatus = UnknownError; break; + case ERROR_PATH_NOT_FOUND: mStatus = NoSuchFile; break; + case ERROR_FILE_NOT_FOUND: mStatus = NoSuchFile; break; + case ERROR_SHARING_VIOLATION: mStatus = SharingViolation; break; + case ERROR_HANDLE_DISK_FULL: mStatus = FileSystemFull; break; + case ERROR_ACCESS_DENIED: mStatus = AccessDenied; break; + default: mStatus = UnknownError; break; + } +} + +//----------------------------------------------------------------------------- + +Win32Directory::Win32Directory(const Path& path,String name) +{ + mPath = path; + mName = name; + mStatus = Closed; + mHandle = 0; +} + +Win32Directory::~Win32Directory() +{ + if (mHandle) + close(); +} + +Path Win32Directory::getName() const +{ + return mPath; +} + +bool Win32Directory::open() +{ + if (!_IsDirectory(mName)) + { + mStatus = NoSuchFile; + return false; + } + mStatus = Open; + return true; +} + +bool Win32Directory::close() +{ + if (mHandle) + { + ::FindClose((HANDLE)mHandle); + mHandle = 0; + return true; + } + return false; +} + +bool Win32Directory::read(Attributes* entry) +{ + if (mStatus != Open) + return false; + + WIN32_FIND_DATA info; + if (!mHandle) + { + mHandle = ::FindFirstFileW((PathToOS(mName) + "\\*").utf16(), &info); + + if (mHandle == NULL) + { + _updateStatus(); + return false; + } + } + else + if (!::FindNextFileW((HANDLE)mHandle, &info)) + { + _updateStatus(); + return false; + } + + // Skip "." and ".." entries + if (info.cFileName[0] == '.' && (info.cFileName[1] == '\0' || + (info.cFileName[1] == '.' && info.cFileName[2] == '\0'))) + return read(entry); + + _CopyStatAttributes(info,entry); + entry->name = info.cFileName; + return true; +} + + +U32 Win32Directory::calculateChecksum() +{ + // Return checksum of current entry + return 0; +} + +bool Win32Directory::getAttributes(Attributes* attr) +{ + WIN32_FIND_DATA info; + HANDLE handle = ::FindFirstFileW(PathToOS(mName).utf16(), &info); + ::FindClose(handle); + if (handle == INVALID_HANDLE_VALUE) + { + _updateStatus(); + return false; + } + + _CopyStatAttributes(info,attr); + attr->name = mPath; + return true; +} + +FileNode::Status Win32Directory::getStatus() const +{ + return mStatus; +} + +void Win32Directory::_updateStatus() +{ + switch (::GetLastError()) + { + case ERROR_NO_MORE_FILES: mStatus = EndOfFile; break; + case ERROR_INVALID_ACCESS: mStatus = AccessDenied; break; + case ERROR_PATH_NOT_FOUND: mStatus = NoSuchFile; break; + case ERROR_SHARING_VIOLATION: mStatus = SharingViolation; break; + case ERROR_ACCESS_DENIED: mStatus = AccessDenied; break; + default: mStatus = UnknownError; break; + } +} + +} // Namespace Win32 + +bool FS::VerifyWriteAccess(const Path &path) +{ + // due to UAC's habit of creating "virtual stores" when permission isn't actually available + // actually create, write, read, verify, and delete a file to the folder being tested + + String temp = path.getFullPath(); + temp += "\\torque_writetest.tmp"; + + // first, (try and) delete the file if it exists + ::DeleteFileW(temp.utf16()); + + // now, create the file + + HANDLE hFile = ::CreateFileW(PathToOS(temp).utf16(), + GENERIC_WRITE, 0, + NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + + if ( hFile == INVALID_HANDLE_VALUE || hFile == NULL ) + return false; + + U32 t = Platform::getTime(); + + DWORD bytesWritten; + if (!::WriteFile(hFile,&t,sizeof(t),&bytesWritten,0)) + { + ::CloseHandle(hFile); + ::DeleteFileW(temp.utf16()); + return false; + } + + // close the file + ::CloseHandle(hFile); + + // open for read + + hFile = ::CreateFileW(PathToOS(temp).utf16(), + GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + + if ( hFile == INVALID_HANDLE_VALUE || hFile == NULL ) + return false; + + U32 t2 = 0; + + DWORD bytesRead; + if (!::ReadFile(hFile,&t2,sizeof(t2),&bytesRead,0)) + { + ::CloseHandle(hFile); + ::DeleteFileW(temp.utf16()); + return false; + } + + ::CloseHandle(hFile); + ::DeleteFileW(temp.utf16()); + + return t == t2; +} + + +} // Namespace Torque + +//----------------------------------------------------------------------------- + +Torque::FS::FileSystemRef Platform::FS::createNativeFS( const String &volume ) +{ + return new Win32::Win32FileSystem( volume ); +} + +String Platform::FS::getAssetDir() +{ + char cen_buf[2048]; +#ifdef TORQUE_UNICODE + if (!Platform::getWebDeployment()) + { + TCHAR buf[ 2048 ]; + ::GetModuleFileNameW( NULL, buf, sizeof( buf ) ); + convertUTF16toUTF8( buf, cen_buf, sizeof( cen_buf ) ); + } + else + { + TCHAR buf[ 2048 ]; + GetCurrentDirectoryW( sizeof( buf ) / sizeof( buf[ 0 ] ), buf ); + convertUTF16toUTF8( buf, cen_buf, sizeof( cen_buf ) ); + return Path::CleanSeparators(cen_buf); + } +#else + ::GetModuleFileNameA( NULL, cen_buf, 2047); +#endif + + char *delimiter = dStrrchr( cen_buf, '\\' ); + + if( delimiter != NULL ) + *delimiter = '\0'; + + return Path::CleanSeparators(cen_buf); +} + +/// Function invoked by the kernel layer to install OS specific +/// file systems. +bool Platform::FS::InstallFileSystems() +{ + WCHAR buffer[1024]; + + // [8/24/2009 tomb] This stops Windows from complaining about drives that have no disks in + SetErrorMode(SEM_FAILCRITICALERRORS); + + // Load all the Win32 logical drives. + DWORD mask = ::GetLogicalDrives(); + char drive[] = "A"; + char volume[] = "A:/"; + while (mask) + { + if (mask & 1) + { + volume[0] = drive[0]; + Platform::FS::Mount(drive, Platform::FS::createNativeFS(volume)); + } + mask >>= 1; + drive[0]++; + } + + // Set the current working dir. Windows normally returns + // upper case driver letters, but the cygwin bash shell + // seems to make it return lower case drives. Force upper + // to be consistent with the mounts. + ::GetCurrentDirectory(sizeof(buffer), buffer); + + if (buffer[1] == ':') + buffer[0] = dToupper(buffer[0]); + + String wd = buffer; + + wd += '/'; + + Platform::FS::SetCwd(wd); + + return true; +} + + diff --git a/Engine/source/platformWin32/winVolume.h b/Engine/source/platformWin32/winVolume.h new file mode 100644 index 000000000..b41b6938c --- /dev/null +++ b/Engine/source/platformWin32/winVolume.h @@ -0,0 +1,130 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _WINVOLUME_H_ +#define _WINVOLUME_H_ + +#ifndef _VOLUME_H_ +#include "core/volume.h" +#endif + +namespace Torque +{ +using namespace FS; + +namespace Win32 +{ + +//----------------------------------------------------------------------------- + +class Win32FileSystem: public FileSystem +{ +public: + Win32FileSystem(String volume); + ~Win32FileSystem(); + + String getTypeStr() const { return "Win32"; } + + FileNodeRef resolve(const Path& path); + FileNodeRef create(const Path& path,FileNode::Mode); + bool remove(const Path& path); + bool rename(const Path& from,const Path& to); + Path mapTo(const Path& path); + Path mapFrom(const Path& path); + +private: + String mVolume; +}; + + +//----------------------------------------------------------------------------- +/// Win32 stdio file access. +/// This class makes use the fopen, fread and fwrite for buffered io. +class Win32File: public File +{ +public: + ~Win32File(); + + Path getName() const; + Status getStatus() const; + bool getAttributes(Attributes*); + U64 getSize(); + + U32 getPosition(); + U32 setPosition(U32,SeekMode); + + bool open(AccessMode); + bool close(); + + U32 read(void* dst, U32 size); + U32 write(const void* src, U32 size); + +private: + friend class Win32FileSystem; + + U32 calculateChecksum(); + + Path mPath; + String mName; + void *mHandle; + Status mStatus; + + Win32File(const Path &path, String name); + + bool _updateInfo(); + void _updateStatus(); +}; + + +//----------------------------------------------------------------------------- + +class Win32Directory: public Directory +{ +public: + ~Win32Directory(); + + Path getName() const; + Status getStatus() const; + bool getAttributes(Attributes*); + + bool open(); + bool close(); + bool read(Attributes*); + +private: + friend class Win32FileSystem; + + U32 calculateChecksum(); + + Path mPath; + String mName; + void *mHandle; + Status mStatus; + + Win32Directory(const Path &path,String name); + + void _updateStatus(); +}; + +} // Namespace +} // Namespace +#endif diff --git a/Engine/source/platformWin32/winWindow.cpp b/Engine/source/platformWin32/winWindow.cpp new file mode 100644 index 000000000..f2199e8db --- /dev/null +++ b/Engine/source/platformWin32/winWindow.cpp @@ -0,0 +1,631 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "platform/event.h" +#include "platformWin32/platformWin32.h" +#include "platformWin32/winConsole.h" +#include "platformWin32/winDirectInput.h" +#include "windowManager/win32/win32Window.h" +#include "console/console.h" +#include "math/mRandom.h" +#include "core/stream/fileStream.h" +#include "T3D/resource.h" +#include +#include "gfx/gfxInit.h" +#include "gfx/gfxDevice.h" +#include "core/strings/unicode.h" +#include "gui/core/guiCanvas.h" + + +extern void createFontInit(); +extern void createFontShutdown(); +extern void installRedBookDevices(); +extern void handleRedBookCallback(U32, U32); + +static MRandomLCG sgPlatRandom; +static bool sgQueueEvents; + +// is keyboard input a standard (non-changing) VK keycode +#define dIsStandardVK(c) (((0x08 <= (c)) && ((c) <= 0x12)) || \ + ((c) == 0x1b) || \ + ((0x20 <= (c)) && ((c) <= 0x2e)) || \ + ((0x30 <= (c)) && ((c) <= 0x39)) || \ + ((0x41 <= (c)) && ((c) <= 0x5a)) || \ + ((0x70 <= (c)) && ((c) <= 0x7B))) + +extern InputObjectInstances DIK_to_Key( U8 dikCode ); + +// static helper variables +static HANDLE gMutexHandle = NULL; +static bool sgDoubleByteEnabled = false; + +// track window states +Win32PlatState winState; + + +//----------------------------------------------------------------------------------------------------------------------------------------------------------- +// +// Microsoft Layer for Unicode +// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/mslu/winprog/compiling_your_application_with_the_microsoft_layer_for_unicode.asp +// +//----------------------------------------------------------------------------------------------------------------------------------------------------------- +#ifdef UNICODE + +HMODULE LoadUnicowsProc(void) +{ + return(LoadLibraryA("unicows.dll")); +} + +#ifdef _cplusplus +extern "C" { +#endif +extern FARPROC _PfnLoadUnicows = (FARPROC) &LoadUnicowsProc; +#ifdef _cplusplus +} +#endif + +#endif + +//-------------------------------------- +Win32PlatState::Win32PlatState() +{ + log_fp = NULL; + hinstOpenGL = NULL; + hinstGLU = NULL; + hinstOpenAL = NULL; + appDC = NULL; + appInstance = NULL; + currentTime = 0; + processId = 0; +} + +//-------------------------------------- +bool Platform::excludeOtherInstances(const char *mutexName) +{ +#ifdef UNICODE + UTF16 b[512]; + convertUTF8toUTF16((UTF8 *)mutexName, b, sizeof(b)); + gMutexHandle = CreateMutex(NULL, true, b); +#else + gMutexHandle = CreateMutex(NULL, true, mutexName); +#endif + if(!gMutexHandle) + return false; + + if(GetLastError() == ERROR_ALREADY_EXISTS) + { + CloseHandle(gMutexHandle); + gMutexHandle = NULL; + return false; + } + + return true; +} + +void Platform::restartInstance() +{ + STARTUPINFO si; + PROCESS_INFORMATION pi; + + ZeroMemory( &si, sizeof(si) ); + si.cb = sizeof(si); + ZeroMemory( &pi, sizeof(pi) ); + + TCHAR cen_buf[2048]; + GetModuleFileName( NULL, cen_buf, 2047); + + // Start the child process. + if( CreateProcess( cen_buf, + NULL, // Command line + NULL, // Process handle not inheritable + NULL, // Thread handle not inheritable + FALSE, // Set handle inheritance to FALSE + 0, // No creation flags + NULL, // Use parent's environment block + NULL, // Use parent's starting directory + &si, // Pointer to STARTUPINFO structure + &pi ) // Pointer to PROCESS_INFORMATION structure + != false ) + { + WaitForInputIdle( pi.hProcess, 5000 ); + CloseHandle( pi.hProcess ); + CloseHandle( pi.hThread ); + } +} + +///just check if the app's global mutex exists, and if so, +///return true - otherwise, false. Should be called before ExcludeOther +/// at very start of app execution. +bool Platform::checkOtherInstances(const char *mutexName) +{ +#ifdef TORQUE_MULTITHREAD + + HANDLE pMutex = NULL; + +#ifdef UNICODE + UTF16 b[512]; + convertUTF8toUTF16((UTF8 *)mutexName, b, sizeof(b)); + pMutex = CreateMutex(NULL, true, b); +#else + pMutex = CreateMutex(NULL, true, mutexName); +#endif + + if(!pMutex) + return false; + + if(GetLastError() == ERROR_ALREADY_EXISTS) + { + //another mutex of the same name exists + //close ours + CloseHandle(pMutex); + pMutex = NULL; + return true; + } + + CloseHandle(pMutex); + pMutex = NULL; +#endif + + //we don;t care, always false + return false; +} + +//-------------------------------------- +void Platform::AlertOK(const char *windowTitle, const char *message) +{ + ShowCursor(true); +#ifdef UNICODE + UTF16 m[1024], t[512]; + convertUTF8toUTF16((UTF8 *)windowTitle, t, sizeof(t)); + convertUTF8toUTF16((UTF8 *)message, m, sizeof(m)); + MessageBox(NULL, m, t, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_OK); +#else + MessageBox(NULL, message, windowTitle, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_OK); +#endif +} + +//-------------------------------------- +bool Platform::AlertOKCancel(const char *windowTitle, const char *message) +{ + ShowCursor(true); +#ifdef UNICODE + UTF16 m[1024], t[512]; + convertUTF8toUTF16((UTF8 *)windowTitle, t, sizeof(t)); + convertUTF8toUTF16((UTF8 *)message, m, sizeof(m)); + return MessageBox(NULL, m, t, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_OKCANCEL) == IDOK; +#else + return MessageBox(NULL, message, windowTitle, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_OKCANCEL) == IDOK; +#endif +} + +//-------------------------------------- +bool Platform::AlertRetry(const char *windowTitle, const char *message) +{ + ShowCursor(true); +#ifdef UNICODE + UTF16 m[1024], t[512]; + convertUTF8toUTF16((UTF8 *)windowTitle, t, sizeof(t)); + convertUTF8toUTF16((UTF8 *)message, m, sizeof(m)); + return (MessageBox(NULL, m, t, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_RETRYCANCEL) == IDRETRY); +#else + return (MessageBox(NULL, message, windowTitle, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_RETRYCANCEL) == IDRETRY); +#endif +} + +//-------------------------------------- +HIMC gIMEContext; + +static void InitInput() +{ +#ifndef TORQUE_LIB +#ifdef UNICODE + //gIMEContext = ImmGetContext(getWin32WindowHandle()); + //ImmReleaseContext( getWin32WindowHandle(), gIMEContext ); +#endif +#endif +} + +//-------------------------------------- +void Platform::init() +{ + Con::printf("Initializing platform..."); + + // Set the platform variable for the scripts + Con::setVariable( "$platform", "windows" ); + + WinConsole::create(); + + if ( !WinConsole::isEnabled() ) + Input::init(); + + InitInput(); // in case DirectInput falls through + + installRedBookDevices(); + + sgDoubleByteEnabled = GetSystemMetrics( SM_DBCSENABLED ); + sgQueueEvents = true; + Con::printf("Done"); +} + +//-------------------------------------- +void Platform::shutdown() +{ + sgQueueEvents = false; + + if(gMutexHandle) + CloseHandle(gMutexHandle); + + Input::destroy(); + + GFXDevice::destroy(); + + WinConsole::destroy(); +} + +extern bool LinkConsoleFunctions; + +#ifndef TORQUE_SHARED + +extern S32 TorqueMain(S32 argc, const char **argv); + +//-------------------------------------- +static S32 run(S32 argc, const char **argv) +{ + // Console hack to ensure consolefunctions get linked in + LinkConsoleFunctions=true; + + createFontInit(); + + S32 ret = TorqueMain(argc, argv); + + createFontShutdown(); + + return ret; +} + +//-------------------------------------- +S32 main(S32 argc, const char **argv) +{ + winState.appInstance = GetModuleHandle(NULL); + return run(argc, argv); +} + +//-------------------------------------- + +#include "unit/test.h" +#include "app/mainLoop.h" + +S32 PASCAL WinMain( HINSTANCE hInstance, HINSTANCE, LPSTR lpszCmdLine, S32) +{ +#if 0 + // Run a unit test. + StandardMainLoop::initCore(); + UnitTesting::TestRun tr; + tr.test("Platform", true); +#else + + Vector argv( __FILE__, __LINE__ ); + + char moduleName[256]; +#ifdef TORQUE_UNICODE + { + TCHAR buf[ 256 ]; + GetModuleFileNameW( NULL, buf, sizeof( buf ) ); + convertUTF16toUTF8( buf, moduleName, sizeof( moduleName ) ); + } +#else + GetModuleFileNameA(NULL, moduleName, sizeof(moduleName)); +#endif + argv.push_back(moduleName); + + for (const char* word,*ptr = lpszCmdLine; *ptr; ) + { + // Eat white space + for (; dIsspace(*ptr) && *ptr; ptr++) + ; + + // Pick out the next word + for (word = ptr; !dIsspace(*ptr) && *ptr; ptr++) + ; + + // Add the word to the argument list. + if (*word) + { + int len = ptr - word; + char *arg = (char *) dMalloc(len + 1); + dStrncpy(arg, word, len); + arg[len] = 0; + argv.push_back(arg); + } + } + + winState.appInstance = hInstance; + + S32 retVal = run(argv.size(), (const char **) argv.address()); + + for(U32 j = 1; j < argv.size(); j++) + dFree(argv[j]); + + return retVal; +#endif +} + +#else //TORQUE_SHARED + +extern "C" +{ + bool torque_engineinit(int argc, const char **argv); + int torque_enginetick(); + bool torque_engineshutdown(); +}; + +int TorqueMain(int argc, const char **argv) +{ + if (!torque_engineinit(argc, argv)) + return 1; + + while(torque_enginetick()) + { + + } + + torque_engineshutdown(); + + return 0; + +} + + + +extern "C" { + +S32 torque_winmain( HINSTANCE hInstance, HINSTANCE, LPSTR lpszCmdLine, S32) +{ + Vector argv( __FILE__, __LINE__ ); + + char moduleName[256]; +#ifdef TORQUE_UNICODE + { + TCHAR buf[ 256 ]; + GetModuleFileNameW( NULL, buf, sizeof( buf ) ); + convertUTF16toUTF8( buf, moduleName, sizeof( moduleName ) ); +} +#else + GetModuleFileNameA(NULL, moduleName, sizeof(moduleName)); +#endif + argv.push_back(moduleName); + + for (const char* word,*ptr = lpszCmdLine; *ptr; ) + { + // Eat white space + for (; dIsspace(*ptr) && *ptr; ptr++) + ; + + // Test for quotes + bool withinQuotes = dIsquote(*ptr); + + if (!withinQuotes) + { + // Pick out the next word + for (word = ptr; !dIsspace(*ptr) && *ptr; ptr++) + ; + } + else + { + // Advance past the first quote. We don't want to include it. + ptr++; + + // Pick out the next quote + for (word = ptr; !dIsquote(*ptr) && *ptr; ptr++) + ; + } + + // Add the word to the argument list. + if (*word) + { + int len = ptr - word; + char *arg = (char *) dMalloc(len + 1); + dStrncpy(arg, word, len); + arg[len] = 0; + argv.push_back(arg); + } + + // If we had a quote, skip past it for the next arg + if (withinQuotes && *ptr) + { + ptr++; + } + } + + winState.appInstance = hInstance; + + S32 retVal = TorqueMain(argv.size(), (const char **) argv.address()); + + for(U32 j = 1; j < argv.size(); j++) + dFree(argv[j]); + + return retVal; +} + +} // extern "C" + +#endif + + + +//-------------------------------------- + +F32 Platform::getRandom() +{ + return sgPlatRandom.randF(); +} + +////-------------------------------------- +/// Spawn the default Operating System web browser with a URL +/// @param webAddress URL to pass to browser +/// @return true if browser successfully spawned +bool Platform::openWebBrowser( const char* webAddress ) +{ + static bool sHaveKey = false; + static wchar_t sWebKey[512]; + char utf8WebKey[512]; + + { + HKEY regKey; + DWORD size = sizeof( sWebKey ); + + if ( RegOpenKeyEx( HKEY_CLASSES_ROOT, dT("\\http\\shell\\open\\command"), 0, KEY_QUERY_VALUE, ®Key ) != ERROR_SUCCESS ) + { + Con::errorf( ConsoleLogEntry::General, "Platform::openWebBrowser - Failed to open the HKCR\\http registry key!!!"); + return( false ); + } + + if ( RegQueryValueEx( regKey, dT(""), NULL, NULL, (unsigned char *)sWebKey, &size ) != ERROR_SUCCESS ) + { + Con::errorf( ConsoleLogEntry::General, "Platform::openWebBrowser - Failed to query the open command registry key!!!" ); + return( false ); + } + + RegCloseKey( regKey ); + sHaveKey = true; + + convertUTF16toUTF8(sWebKey,utf8WebKey,512); + +#ifdef UNICODE + char *p = dStrstr((const char *)utf8WebKey, "%1"); +#else + char *p = strstr( (const char *) sWebKey , "%1"); +#endif + if (p) *p = 0; + + } + + STARTUPINFO si; + dMemset( &si, 0, sizeof( si ) ); + si.cb = sizeof( si ); + + char buf[1024]; +#ifdef UNICODE + dSprintf( buf, sizeof( buf ), "%s %s", utf8WebKey, webAddress ); + UTF16 b[1024]; + convertUTF8toUTF16((UTF8 *)buf, b, sizeof(b)); +#else + dSprintf( buf, sizeof( buf ), "%s %s", sWebKey, webAddress ); +#endif + + //Con::errorf( ConsoleLogEntry::General, "** Web browser command = %s **", buf ); + + PROCESS_INFORMATION pi; + dMemset( &pi, 0, sizeof( pi ) ); + CreateProcess( NULL, +#ifdef UNICODE + b, +#else + buf, +#endif + NULL, + NULL, + false, + CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP, + NULL, + NULL, + &si, + &pi ); + + return( true ); +} + +//-------------------------------------- +// Login password routines: +//-------------------------------------- +#ifdef UNICODE +static const UTF16* TorqueRegKey = dT("SOFTWARE\\GarageGames\\Torque"); +#else +static const char* TorqueRegKey = "SOFTWARE\\GarageGames\\Torque"; +#endif + +const char* Platform::getLoginPassword() +{ + HKEY regKey; + char* returnString = NULL; + if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, TorqueRegKey, 0, KEY_QUERY_VALUE, ®Key ) == ERROR_SUCCESS ) + { + U8 buf[32]; + DWORD size = sizeof( buf ); + if ( RegQueryValueEx( regKey, dT("LoginPassword"), NULL, NULL, buf, &size ) == ERROR_SUCCESS ) + { + returnString = Con::getReturnBuffer( size + 1 ); + dStrcpy( returnString, (const char*) buf ); + } + + RegCloseKey( regKey ); + } + + if ( returnString ) + return( returnString ); + else + return( "" ); +} + +//-------------------------------------- +bool Platform::setLoginPassword( const char* password ) +{ + HKEY regKey; + if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, TorqueRegKey, 0, KEY_WRITE, ®Key ) == ERROR_SUCCESS ) + { + if ( RegSetValueEx( regKey, dT("LoginPassword"), 0, REG_SZ, (const U8*) password, dStrlen( password ) + 1 ) != ERROR_SUCCESS ) + Con::errorf( ConsoleLogEntry::General, "setLoginPassword - Failed to set the subkey value!" ); + + RegCloseKey( regKey ); + return( true ); + } + else + Con::errorf( ConsoleLogEntry::General, "setLoginPassword - Failed to open the Torque registry key!" ); + + return( false ); +} + +//-------------------------------------- +// Silly Korean registry key checker: +// +// NOTE: "Silly" refers to the nature of this hack, and is not intended +// as commentary on Koreans as a nationality. Thank you for your +// attention. +//-------------------------------------- +ConsoleFunction( isKoreanBuild, bool, 1, 1, "isKoreanBuild()" ) +{ + argc; argv; + HKEY regKey; + bool result = false; + if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, TorqueRegKey, 0, KEY_QUERY_VALUE, ®Key ) == ERROR_SUCCESS ) + { + DWORD val; + DWORD size = sizeof( val ); + if ( RegQueryValueEx( regKey, dT("Korean"), NULL, NULL, (U8*) &val, &size ) == ERROR_SUCCESS ) + result = ( val > 0 ); + + RegCloseKey( regKey ); + } + + return( result ); +} \ No newline at end of file diff --git a/Engine/source/platformWin32/win_common_prefix.h b/Engine/source/platformWin32/win_common_prefix.h new file mode 100644 index 000000000..edc0dd7e1 --- /dev/null +++ b/Engine/source/platformWin32/win_common_prefix.h @@ -0,0 +1,54 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +//------------------------------ +//win_common_prefix.h +//------------------------------ + +//------------------------------ +// basic build defines + +// define our platform +//#define TARG_WIN32 1 +//#define WIN32 1 + +// normally, this should be on for a PC build +//#define USEASSEMBLYTERRBLEND 1 + + +//------------------------------ +// setup compiler-specific flags + +// METROWERKS CODEWARRIOR +//#if __MWERKS__ +//#pragma once +// we turn this on to use inlined CW6-safe ASM in CWProject builds. +// ... and thus not require NASM for CWProject building ... +//#define TARG_INLINED_ASM 1 +//#endif + + +// these are general build flags Torque uses. +//#define PNG_NO_READ_tIME 1 +//#define PNG_NO_WRITE_TIME 1 + +//#define NO_MILES_OPENAL 1 diff --git a/Engine/source/platformWin32/win_debug_prefix.h b/Engine/source/platformWin32/win_debug_prefix.h new file mode 100644 index 000000000..7ed61000b --- /dev/null +++ b/Engine/source/platformWin32/win_debug_prefix.h @@ -0,0 +1,41 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +//win_debug_prefix.h + +#include "win_common_prefix.h" + +// our defines +//#define BUILD_SUFFIX "_DEBUG" +//#define DEBUG 1 +//#define ENABLE_ASSERTS 1 + +// the PC doesn't need this. just the mac... +//#define ITFDUMP_NOASM 1 + + +//#define USEASSEMBLYTERRBLEND 1 + +//#define PNG_NO_READ_tIME 1 +//#define PNG_NO_WRITE_TIME 1 + +//#define NO_MILES_OPENAL 1 diff --git a/Engine/source/platformWin32/win_release_prefix.h b/Engine/source/platformWin32/win_release_prefix.h new file mode 100644 index 000000000..6aa55e1e3 --- /dev/null +++ b/Engine/source/platformWin32/win_release_prefix.h @@ -0,0 +1,45 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +//win_debug_prefix.h + +#include "win_common_prefix.h" + +// our defines +//#define TGE_RELEASE 1 +//#define TGE_NO_ASSERTS 1 +//#define BUILD_SUFFIX "" + +// these should be off in a release build +//#define DEBUG 1 +//#define ENABLE_ASSERTS 1 + +// the PC doesn't need this. just the mac... +//#define ITFDUMP_NOASM 1 + + +//#define USEASSEMBLYTERRBLEND 1 + +//#define PNG_NO_READ_tIME 1 +//#define PNG_NO_WRITE_TIME 1 + +//#define NO_MILES_OPENAL 1 diff --git a/Engine/source/platformX86UNIX/gl_types.h b/Engine/source/platformX86UNIX/gl_types.h new file mode 100644 index 000000000..c74570814 --- /dev/null +++ b/Engine/source/platformX86UNIX/gl_types.h @@ -0,0 +1,983 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _X86UNIX_GL_TYPES_H_ +#define _X86UNIX_GL_TYPES_H_ + +// added by JMQ: +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF + +#define GL_FOG_COORDINATE_SOURCE_EXT 0x8450 +#define GL_FOG_COORDINATE_EXT 0x8451 +#define GL_FRAGMENT_DEPTH_EXT 0x8452 +#define GL_CURRENT_FOG_COORDINATE_EXT 0x8453 +#define GL_FOG_COORDINATE_ARRAY_TYPE_EXT 0x8454 +#define GL_FOG_COORDINATE_ARRAY_STRIDE_EXT 0x8455 +#define GL_FOG_COORDINATE_ARRAY_POINTER_EXT 0x8456 +#define GL_FOG_COORDINATE_ARRAY_EXT 0x8457 + +#define GL_COMPRESSED_ALPHA_ARB 0x84E9 +#define GL_COMPRESSED_LUMINANCE_ARB 0x84EA +#define GL_COMPRESSED_LUMINANCE_ALPHA_ARB 0x84EB +#define GL_COMPRESSED_INTENSITY_ARB 0x84EC +#define GL_COMPRESSED_RGB_ARB 0x84ED +#define GL_COMPRESSED_RGBA_ARB 0x84EE +#define GL_TEXTURE_COMPRESSION_HINT_ARB 0x84EF +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB 0x86A0 +#define GL_TEXTURE_COMPRESSED_ARB 0x86A1 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A3 + +#ifndef GL_EXT_packed_pixels +#define GL_UNSIGNED_BYTE_3_3_2_EXT 0x8032 +#define GL_UNSIGNED_SHORT_4_4_4_4_EXT 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1_EXT 0x8034 +#define GL_UNSIGNED_INT_8_8_8_8_EXT 0x8035 +#define GL_UNSIGNED_INT_10_10_10_2_EXT 0x8036 +#endif + +#ifndef GL_EXT_paletted_texture +#define GL_COLOR_INDEX1_EXT 0x80E2 +#define GL_COLOR_INDEX2_EXT 0x80E3 +#define GL_COLOR_INDEX4_EXT 0x80E4 +#define GL_COLOR_INDEX8_EXT 0x80E5 +#define GL_COLOR_INDEX12_EXT 0x80E6 +#define GL_COLOR_INDEX16_EXT 0x80E7 +#define GL_TEXTURE_INDEX_SIZE_EXT 0x80ED +#endif + +#define GL_CLAMP_TO_EDGE_EXT 0x812F + +#define GL_V12MTVFMT_EXT 0x8702 +#define GL_V12MTNVFMT_EXT 0x8703 +#define GL_V12FTVFMT_EXT 0x8704 +#define GL_V12FMTVFMT_EXT 0x8705 + +#ifndef GL_EXT_texture_env_combine +#define GL_COMBINE_EXT 0x8570 +#define GL_COMBINE_RGB_EXT 0x8571 +#define GL_COMBINE_ALPHA_EXT 0x8572 +#define GL_RGB_SCALE_EXT 0x8573 +#define GL_ADD_SIGNED_EXT 0x8574 +#define GL_INTERPOLATE_EXT 0x8575 +#define GL_CONSTANT_EXT 0x8576 +#define GL_PRIMARY_COLOR_EXT 0x8577 +#define GL_PREVIOUS_EXT 0x8578 +#define GL_SOURCE0_RGB_EXT 0x8580 +#define GL_SOURCE1_RGB_EXT 0x8581 +#define GL_SOURCE2_RGB_EXT 0x8582 +#define GL_SOURCE3_RGB_EXT 0x8583 +#define GL_SOURCE4_RGB_EXT 0x8584 +#define GL_SOURCE5_RGB_EXT 0x8585 +#define GL_SOURCE6_RGB_EXT 0x8586 +#define GL_SOURCE7_RGB_EXT 0x8587 +#define GL_SOURCE0_ALPHA_EXT 0x8588 +#define GL_SOURCE1_ALPHA_EXT 0x8589 +#define GL_SOURCE2_ALPHA_EXT 0x858A +#define GL_SOURCE3_ALPHA_EXT 0x858B +#define GL_SOURCE4_ALPHA_EXT 0x858C +#define GL_SOURCE5_ALPHA_EXT 0x858D +#define GL_SOURCE6_ALPHA_EXT 0x858E +#define GL_SOURCE7_ALPHA_EXT 0x858F +#define GL_OPERAND0_RGB_EXT 0x8590 +#define GL_OPERAND1_RGB_EXT 0x8591 +#define GL_OPERAND2_RGB_EXT 0x8592 +#define GL_OPERAND3_RGB_EXT 0x8593 +#define GL_OPERAND4_RGB_EXT 0x8594 +#define GL_OPERAND5_RGB_EXT 0x8595 +#define GL_OPERAND6_RGB_EXT 0x8596 +#define GL_OPERAND7_RGB_EXT 0x8597 +#define GL_OPERAND0_ALPHA_EXT 0x8598 +#define GL_OPERAND1_ALPHA_EXT 0x8599 +#define GL_OPERAND2_ALPHA_EXT 0x859A +#define GL_OPERAND3_ALPHA_EXT 0x859B +#define GL_OPERAND4_ALPHA_EXT 0x859C +#define GL_OPERAND5_ALPHA_EXT 0x859D +#define GL_OPERAND6_ALPHA_EXT 0x859E +#define GL_OPERAND7_ALPHA_EXT 0x859F +#endif + +/* + * Mesa 3-D graphics library + * Version: 3.4 + * + * Copyright (C) 1999-2000 Brian Paul All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + + +#define GL_VERSION_1_1 1 +#define GL_VERSION_1_2 1 + +/* + * + * Datatypes + * + */ +#ifdef CENTERLINE_CLPP +#define signed +#endif +typedef unsigned int GLenum; +typedef unsigned char GLboolean; +typedef unsigned int GLbitfield; +typedef void GLvoid; +typedef signed char GLbyte; /* 1-byte signed */ +typedef short GLshort; /* 2-byte signed */ +typedef int GLint; /* 4-byte signed */ +typedef unsigned char GLubyte; /* 1-byte unsigned */ +typedef unsigned short GLushort; /* 2-byte unsigned */ +typedef unsigned int GLuint; /* 4-byte unsigned */ +typedef int GLsizei; /* 4-byte signed */ +typedef float GLfloat; /* single precision float */ +typedef float GLclampf; /* single precision float in [0,1] */ +typedef double GLdouble; /* double precision float */ +typedef double GLclampd; /* double precision float in [0,1] */ + + + +/* + * + * Constants + * + */ + +/* Boolean values */ +#define GL_FALSE 0x0 +#define GL_TRUE 0x1 + +/* Data types */ +#define GL_BYTE 0x1400 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_SHORT 0x1402 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_INT 0x1404 +#define GL_UNSIGNED_INT 0x1405 +#define GL_FLOAT 0x1406 +#define GL_DOUBLE 0x140A +#define GL_2_BYTES 0x1407 +#define GL_3_BYTES 0x1408 +#define GL_4_BYTES 0x1409 + +/* Primitives */ +#define GL_POINTS 0x0000 +#define GL_LINES 0x0001 +#define GL_LINE_LOOP 0x0002 +#define GL_LINE_STRIP 0x0003 +#define GL_TRIANGLES 0x0004 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_TRIANGLE_FAN 0x0006 +#define GL_QUADS 0x0007 +#define GL_QUAD_STRIP 0x0008 +#define GL_POLYGON 0x0009 + +/* Vertex Arrays */ +#define GL_VERTEX_ARRAY 0x8074 +#define GL_NORMAL_ARRAY 0x8075 +#define GL_COLOR_ARRAY 0x8076 +#define GL_INDEX_ARRAY 0x8077 +#define GL_TEXTURE_COORD_ARRAY 0x8078 +#define GL_EDGE_FLAG_ARRAY 0x8079 +#define GL_VERTEX_ARRAY_SIZE 0x807A +#define GL_VERTEX_ARRAY_TYPE 0x807B +#define GL_VERTEX_ARRAY_STRIDE 0x807C +#define GL_NORMAL_ARRAY_TYPE 0x807E +#define GL_NORMAL_ARRAY_STRIDE 0x807F +#define GL_COLOR_ARRAY_SIZE 0x8081 +#define GL_COLOR_ARRAY_TYPE 0x8082 +#define GL_COLOR_ARRAY_STRIDE 0x8083 +#define GL_INDEX_ARRAY_TYPE 0x8085 +#define GL_INDEX_ARRAY_STRIDE 0x8086 +#define GL_TEXTURE_COORD_ARRAY_SIZE 0x8088 +#define GL_TEXTURE_COORD_ARRAY_TYPE 0x8089 +#define GL_TEXTURE_COORD_ARRAY_STRIDE 0x808A +#define GL_EDGE_FLAG_ARRAY_STRIDE 0x808C +#define GL_VERTEX_ARRAY_POINTER 0x808E +#define GL_NORMAL_ARRAY_POINTER 0x808F +#define GL_COLOR_ARRAY_POINTER 0x8090 +#define GL_INDEX_ARRAY_POINTER 0x8091 +#define GL_TEXTURE_COORD_ARRAY_POINTER 0x8092 +#define GL_EDGE_FLAG_ARRAY_POINTER 0x8093 +#define GL_V2F 0x2A20 +#define GL_V3F 0x2A21 +#define GL_C4UB_V2F 0x2A22 +#define GL_C4UB_V3F 0x2A23 +#define GL_C3F_V3F 0x2A24 +#define GL_N3F_V3F 0x2A25 +#define GL_C4F_N3F_V3F 0x2A26 +#define GL_T2F_V3F 0x2A27 +#define GL_T4F_V4F 0x2A28 +#define GL_T2F_C4UB_V3F 0x2A29 +#define GL_T2F_C3F_V3F 0x2A2A +#define GL_T2F_N3F_V3F 0x2A2B +#define GL_T2F_C4F_N3F_V3F 0x2A2C +#define GL_T4F_C4F_N3F_V4F 0x2A2D + +/* Matrix Mode */ +#define GL_MATRIX_MODE 0x0BA0 +#define GL_MODELVIEW 0x1700 +#define GL_PROJECTION 0x1701 +#define GL_TEXTURE 0x1702 + +/* Points */ +#define GL_POINT_SMOOTH 0x0B10 +#define GL_POINT_SIZE 0x0B11 +#define GL_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_POINT_SIZE_RANGE 0x0B12 + +/* Lines */ +#define GL_LINE_SMOOTH 0x0B20 +#define GL_LINE_STIPPLE 0x0B24 +#define GL_LINE_STIPPLE_PATTERN 0x0B25 +#define GL_LINE_STIPPLE_REPEAT 0x0B26 +#define GL_LINE_WIDTH 0x0B21 +#define GL_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_LINE_WIDTH_RANGE 0x0B22 + +/* Polygons */ +#define GL_POINT 0x1B00 +#define GL_LINE 0x1B01 +#define GL_FILL 0x1B02 +#define GL_CW 0x0900 +#define GL_CCW 0x0901 +#define GL_FRONT 0x0404 +#define GL_BACK 0x0405 +#define GL_POLYGON_MODE 0x0B40 +#define GL_POLYGON_SMOOTH 0x0B41 +#define GL_POLYGON_STIPPLE 0x0B42 +#define GL_EDGE_FLAG 0x0B43 +#define GL_CULL_FACE 0x0B44 +#define GL_CULL_FACE_MODE 0x0B45 +#define GL_FRONT_FACE 0x0B46 +#define GL_POLYGON_OFFSET_FACTOR 0x8038 +#define GL_POLYGON_OFFSET_UNITS 0x2A00 +#define GL_POLYGON_OFFSET_POINT 0x2A01 +#define GL_POLYGON_OFFSET_LINE 0x2A02 +#define GL_POLYGON_OFFSET_FILL 0x8037 + +/* Display Lists */ +#define GL_COMPILE 0x1300 +#define GL_COMPILE_AND_EXECUTE 0x1301 +#define GL_LIST_BASE 0x0B32 +#define GL_LIST_INDEX 0x0B33 +#define GL_LIST_MODE 0x0B30 + +/* Depth buffer */ +#define GL_NEVER 0x0200 +#define GL_LESS 0x0201 +#define GL_EQUAL 0x0202 +#define GL_LEQUAL 0x0203 +#define GL_GREATER 0x0204 +#define GL_NOTEQUAL 0x0205 +#define GL_GEQUAL 0x0206 +#define GL_ALWAYS 0x0207 +#define GL_DEPTH_TEST 0x0B71 +#define GL_DEPTH_BITS 0x0D56 +#define GL_DEPTH_CLEAR_VALUE 0x0B73 +#define GL_DEPTH_FUNC 0x0B74 +#define GL_DEPTH_RANGE 0x0B70 +#define GL_DEPTH_WRITEMASK 0x0B72 +#define GL_DEPTH_COMPONENT 0x1902 + +/* Lighting */ +#define GL_LIGHTING 0x0B50 +#define GL_LIGHT0 0x4000 +#define GL_LIGHT1 0x4001 +#define GL_LIGHT2 0x4002 +#define GL_LIGHT3 0x4003 +#define GL_LIGHT4 0x4004 +#define GL_LIGHT5 0x4005 +#define GL_LIGHT6 0x4006 +#define GL_LIGHT7 0x4007 +#define GL_SPOT_EXPONENT 0x1205 +#define GL_SPOT_CUTOFF 0x1206 +#define GL_CONSTANT_ATTENUATION 0x1207 +#define GL_LINEAR_ATTENUATION 0x1208 +#define GL_QUADRATIC_ATTENUATION 0x1209 +#define GL_AMBIENT 0x1200 +#define GL_DIFFUSE 0x1201 +#define GL_SPECULAR 0x1202 +#define GL_SHININESS 0x1601 +#define GL_EMISSION 0x1600 +#define GL_POSITION 0x1203 +#define GL_SPOT_DIRECTION 0x1204 +#define GL_AMBIENT_AND_DIFFUSE 0x1602 +#define GL_COLOR_INDEXES 0x1603 +#define GL_LIGHT_MODEL_TWO_SIDE 0x0B52 +#define GL_LIGHT_MODEL_LOCAL_VIEWER 0x0B51 +#define GL_LIGHT_MODEL_AMBIENT 0x0B53 +#define GL_FRONT_AND_BACK 0x0408 +#define GL_SHADE_MODEL 0x0B54 +#define GL_FLAT 0x1D00 +#define GL_SMOOTH 0x1D01 +#define GL_COLOR_MATERIAL 0x0B57 +#define GL_COLOR_MATERIAL_FACE 0x0B55 +#define GL_COLOR_MATERIAL_PARAMETER 0x0B56 +#define GL_NORMALIZE 0x0BA1 + +/* User clipping planes */ +#define GL_CLIP_PLANE0 0x3000 +#define GL_CLIP_PLANE1 0x3001 +#define GL_CLIP_PLANE2 0x3002 +#define GL_CLIP_PLANE3 0x3003 +#define GL_CLIP_PLANE4 0x3004 +#define GL_CLIP_PLANE5 0x3005 + +/* Accumulation buffer */ +#define GL_ACCUM_RED_BITS 0x0D58 +#define GL_ACCUM_GREEN_BITS 0x0D59 +#define GL_ACCUM_BLUE_BITS 0x0D5A +#define GL_ACCUM_ALPHA_BITS 0x0D5B +#define GL_ACCUM_CLEAR_VALUE 0x0B80 +#define GL_ACCUM 0x0100 +#define GL_ADD 0x0104 +#define GL_LOAD 0x0101 +#define GL_MULT 0x0103 +#define GL_RETURN 0x0102 + +/* Alpha testing */ +#define GL_ALPHA_TEST 0x0BC0 +#define GL_ALPHA_TEST_REF 0x0BC2 +#define GL_ALPHA_TEST_FUNC 0x0BC1 + +/* Blending */ +#define GL_BLEND 0x0BE2 +#define GL_BLEND_SRC 0x0BE1 +#define GL_BLEND_DST 0x0BE0 +#define GL_ZERO 0x0 +#define GL_ONE 0x1 +#define GL_SRC_COLOR 0x0300 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_DST_COLOR 0x0306 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_SRC_ALPHA 0x0302 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_DST_ALPHA 0x0304 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 +#define GL_SRC_ALPHA_SATURATE 0x0308 +#define GL_CONSTANT_COLOR 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 + +/* Render Mode */ +#define GL_FEEDBACK 0x1C01 +#define GL_RENDER 0x1C00 +#define GL_SELECT 0x1C02 + +/* Feedback */ +#define GL_2D 0x0600 +#define GL_3D 0x0601 +#define GL_3D_COLOR 0x0602 +#define GL_3D_COLOR_TEXTURE 0x0603 +#define GL_4D_COLOR_TEXTURE 0x0604 +#define GL_POINT_TOKEN 0x0701 +#define GL_LINE_TOKEN 0x0702 +#define GL_LINE_RESET_TOKEN 0x0707 +#define GL_POLYGON_TOKEN 0x0703 +#define GL_BITMAP_TOKEN 0x0704 +#define GL_DRAW_PIXEL_TOKEN 0x0705 +#define GL_COPY_PIXEL_TOKEN 0x0706 +#define GL_PASS_THROUGH_TOKEN 0x0700 +#define GL_FEEDBACK_BUFFER_POINTER 0x0DF0 +#define GL_FEEDBACK_BUFFER_SIZE 0x0DF1 +#define GL_FEEDBACK_BUFFER_TYPE 0x0DF2 + +/* Selection */ +#define GL_SELECTION_BUFFER_POINTER 0x0DF3 +#define GL_SELECTION_BUFFER_SIZE 0x0DF4 + +/* Fog */ +#define GL_FOG 0x0B60 +#define GL_FOG_MODE 0x0B65 +#define GL_FOG_DENSITY 0x0B62 +#define GL_FOG_COLOR 0x0B66 +#define GL_FOG_INDEX 0x0B61 +#define GL_FOG_START 0x0B63 +#define GL_FOG_END 0x0B64 +#define GL_LINEAR 0x2601 +#define GL_EXP 0x0800 +#define GL_EXP2 0x0801 + +/* Logic Ops */ +#define GL_LOGIC_OP 0x0BF1 +#define GL_INDEX_LOGIC_OP 0x0BF1 +#define GL_COLOR_LOGIC_OP 0x0BF2 +#define GL_LOGIC_OP_MODE 0x0BF0 +#define GL_CLEAR 0x1500 +#define GL_SET 0x150F +#define GL_COPY 0x1503 +#define GL_COPY_INVERTED 0x150C +#define GL_NOOP 0x1505 +#define GL_INVERT 0x150A +#define GL_AND 0x1501 +#define GL_NAND 0x150E +#define GL_OR 0x1507 +#define GL_NOR 0x1508 +#define GL_XOR 0x1506 +#define GL_EQUIV 0x1509 +#define GL_AND_REVERSE 0x1502 +#define GL_AND_INVERTED 0x1504 +#define GL_OR_REVERSE 0x150B +#define GL_OR_INVERTED 0x150D + +/* Stencil */ +#define GL_STENCIL_TEST 0x0B90 +#define GL_STENCIL_WRITEMASK 0x0B98 +#define GL_STENCIL_BITS 0x0D57 +#define GL_STENCIL_FUNC 0x0B92 +#define GL_STENCIL_VALUE_MASK 0x0B93 +#define GL_STENCIL_REF 0x0B97 +#define GL_STENCIL_FAIL 0x0B94 +#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 +#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 +#define GL_STENCIL_CLEAR_VALUE 0x0B91 +#define GL_STENCIL_INDEX 0x1901 +#define GL_KEEP 0x1E00 +#define GL_REPLACE 0x1E01 +#define GL_INCR 0x1E02 +#define GL_DECR 0x1E03 + +/* Buffers, Pixel Drawing/Reading */ +#define GL_NONE 0x0 +#define GL_LEFT 0x0406 +#define GL_RIGHT 0x0407 +/*GL_FRONT 0x0404 */ +/*GL_BACK 0x0405 */ +/*GL_FRONT_AND_BACK 0x0408 */ +#define GL_FRONT_LEFT 0x0400 +#define GL_FRONT_RIGHT 0x0401 +#define GL_BACK_LEFT 0x0402 +#define GL_BACK_RIGHT 0x0403 +#define GL_AUX0 0x0409 +#define GL_AUX1 0x040A +#define GL_AUX2 0x040B +#define GL_AUX3 0x040C +#define GL_COLOR_INDEX 0x1900 +#define GL_RED 0x1903 +#define GL_GREEN 0x1904 +#define GL_BLUE 0x1905 +#define GL_ALPHA 0x1906 +#define GL_LUMINANCE 0x1909 +#define GL_LUMINANCE_ALPHA 0x190A +#define GL_ALPHA_BITS 0x0D55 +#define GL_RED_BITS 0x0D52 +#define GL_GREEN_BITS 0x0D53 +#define GL_BLUE_BITS 0x0D54 +#define GL_INDEX_BITS 0x0D51 +#define GL_SUBPIXEL_BITS 0x0D50 +#define GL_AUX_BUFFERS 0x0C00 +#define GL_READ_BUFFER 0x0C02 +#define GL_DRAW_BUFFER 0x0C01 +#define GL_DOUBLEBUFFER 0x0C32 +#define GL_STEREO 0x0C33 +#define GL_BITMAP 0x1A00 +#define GL_COLOR 0x1800 +#define GL_DEPTH 0x1801 +#define GL_STENCIL 0x1802 +#define GL_DITHER 0x0BD0 +#define GL_RGB 0x1907 +#define GL_RGBA 0x1908 + +/* Implementation limits */ +#define GL_MAX_LIST_NESTING 0x0B31 +#define GL_MAX_ATTRIB_STACK_DEPTH 0x0D35 +#define GL_MAX_MODELVIEW_STACK_DEPTH 0x0D36 +#define GL_MAX_NAME_STACK_DEPTH 0x0D37 +#define GL_MAX_PROJECTION_STACK_DEPTH 0x0D38 +#define GL_MAX_TEXTURE_STACK_DEPTH 0x0D39 +#define GL_MAX_EVAL_ORDER 0x0D30 +#define GL_MAX_LIGHTS 0x0D31 +#define GL_MAX_CLIP_PLANES 0x0D32 +#define GL_MAX_TEXTURE_SIZE 0x0D33 +#define GL_MAX_PIXEL_MAP_TABLE 0x0D34 +#define GL_MAX_VIEWPORT_DIMS 0x0D3A +#define GL_MAX_CLIENT_ATTRIB_STACK_DEPTH 0x0D3B + +/* Gets */ +#define GL_ATTRIB_STACK_DEPTH 0x0BB0 +#define GL_CLIENT_ATTRIB_STACK_DEPTH 0x0BB1 +#define GL_COLOR_CLEAR_VALUE 0x0C22 +#define GL_COLOR_WRITEMASK 0x0C23 +#define GL_CURRENT_INDEX 0x0B01 +#define GL_CURRENT_COLOR 0x0B00 +#define GL_CURRENT_NORMAL 0x0B02 +#define GL_CURRENT_RASTER_COLOR 0x0B04 +#define GL_CURRENT_RASTER_DISTANCE 0x0B09 +#define GL_CURRENT_RASTER_INDEX 0x0B05 +#define GL_CURRENT_RASTER_POSITION 0x0B07 +#define GL_CURRENT_RASTER_TEXTURE_COORDS 0x0B06 +#define GL_CURRENT_RASTER_POSITION_VALID 0x0B08 +#define GL_CURRENT_TEXTURE_COORDS 0x0B03 +#define GL_INDEX_CLEAR_VALUE 0x0C20 +#define GL_INDEX_MODE 0x0C30 +#define GL_INDEX_WRITEMASK 0x0C21 +#define GL_MODELVIEW_MATRIX 0x0BA6 +#define GL_MODELVIEW_STACK_DEPTH 0x0BA3 +#define GL_NAME_STACK_DEPTH 0x0D70 +#define GL_PROJECTION_MATRIX 0x0BA7 +#define GL_PROJECTION_STACK_DEPTH 0x0BA4 +#define GL_RENDER_MODE 0x0C40 +#define GL_RGBA_MODE 0x0C31 +#define GL_TEXTURE_MATRIX 0x0BA8 +#define GL_TEXTURE_STACK_DEPTH 0x0BA5 +#define GL_VIEWPORT 0x0BA2 + +/* Evaluators */ +#define GL_AUTO_NORMAL 0x0D80 +#define GL_MAP1_COLOR_4 0x0D90 +#define GL_MAP1_GRID_DOMAIN 0x0DD0 +#define GL_MAP1_GRID_SEGMENTS 0x0DD1 +#define GL_MAP1_INDEX 0x0D91 +#define GL_MAP1_NORMAL 0x0D92 +#define GL_MAP1_TEXTURE_COORD_1 0x0D93 +#define GL_MAP1_TEXTURE_COORD_2 0x0D94 +#define GL_MAP1_TEXTURE_COORD_3 0x0D95 +#define GL_MAP1_TEXTURE_COORD_4 0x0D96 +#define GL_MAP1_VERTEX_3 0x0D97 +#define GL_MAP1_VERTEX_4 0x0D98 +#define GL_MAP2_COLOR_4 0x0DB0 +#define GL_MAP2_GRID_DOMAIN 0x0DD2 +#define GL_MAP2_GRID_SEGMENTS 0x0DD3 +#define GL_MAP2_INDEX 0x0DB1 +#define GL_MAP2_NORMAL 0x0DB2 +#define GL_MAP2_TEXTURE_COORD_1 0x0DB3 +#define GL_MAP2_TEXTURE_COORD_2 0x0DB4 +#define GL_MAP2_TEXTURE_COORD_3 0x0DB5 +#define GL_MAP2_TEXTURE_COORD_4 0x0DB6 +#define GL_MAP2_VERTEX_3 0x0DB7 +#define GL_MAP2_VERTEX_4 0x0DB8 +#define GL_COEFF 0x0A00 +#define GL_DOMAIN 0x0A02 +#define GL_ORDER 0x0A01 + +/* Hints */ +#define GL_FOG_HINT 0x0C54 +#define GL_LINE_SMOOTH_HINT 0x0C52 +#define GL_PERSPECTIVE_CORRECTION_HINT 0x0C50 +#define GL_POINT_SMOOTH_HINT 0x0C51 +#define GL_POLYGON_SMOOTH_HINT 0x0C53 +#define GL_DONT_CARE 0x1100 +#define GL_FASTEST 0x1101 +#define GL_NICEST 0x1102 + +/* Scissor box */ +#define GL_SCISSOR_TEST 0x0C11 +#define GL_SCISSOR_BOX 0x0C10 + +/* Pixel Mode / Transfer */ +#define GL_MAP_COLOR 0x0D10 +#define GL_MAP_STENCIL 0x0D11 +#define GL_INDEX_SHIFT 0x0D12 +#define GL_INDEX_OFFSET 0x0D13 +#define GL_RED_SCALE 0x0D14 +#define GL_RED_BIAS 0x0D15 +#define GL_GREEN_SCALE 0x0D18 +#define GL_GREEN_BIAS 0x0D19 +#define GL_BLUE_SCALE 0x0D1A +#define GL_BLUE_BIAS 0x0D1B +#define GL_ALPHA_SCALE 0x0D1C +#define GL_ALPHA_BIAS 0x0D1D +#define GL_DEPTH_SCALE 0x0D1E +#define GL_DEPTH_BIAS 0x0D1F +#define GL_PIXEL_MAP_S_TO_S_SIZE 0x0CB1 +#define GL_PIXEL_MAP_I_TO_I_SIZE 0x0CB0 +#define GL_PIXEL_MAP_I_TO_R_SIZE 0x0CB2 +#define GL_PIXEL_MAP_I_TO_G_SIZE 0x0CB3 +#define GL_PIXEL_MAP_I_TO_B_SIZE 0x0CB4 +#define GL_PIXEL_MAP_I_TO_A_SIZE 0x0CB5 +#define GL_PIXEL_MAP_R_TO_R_SIZE 0x0CB6 +#define GL_PIXEL_MAP_G_TO_G_SIZE 0x0CB7 +#define GL_PIXEL_MAP_B_TO_B_SIZE 0x0CB8 +#define GL_PIXEL_MAP_A_TO_A_SIZE 0x0CB9 +#define GL_PIXEL_MAP_S_TO_S 0x0C71 +#define GL_PIXEL_MAP_I_TO_I 0x0C70 +#define GL_PIXEL_MAP_I_TO_R 0x0C72 +#define GL_PIXEL_MAP_I_TO_G 0x0C73 +#define GL_PIXEL_MAP_I_TO_B 0x0C74 +#define GL_PIXEL_MAP_I_TO_A 0x0C75 +#define GL_PIXEL_MAP_R_TO_R 0x0C76 +#define GL_PIXEL_MAP_G_TO_G 0x0C77 +#define GL_PIXEL_MAP_B_TO_B 0x0C78 +#define GL_PIXEL_MAP_A_TO_A 0x0C79 +#define GL_PACK_ALIGNMENT 0x0D05 +#define GL_PACK_LSB_FIRST 0x0D01 +#define GL_PACK_ROW_LENGTH 0x0D02 +#define GL_PACK_SKIP_PIXELS 0x0D04 +#define GL_PACK_SKIP_ROWS 0x0D03 +#define GL_PACK_SWAP_BYTES 0x0D00 +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_UNPACK_LSB_FIRST 0x0CF1 +#define GL_UNPACK_ROW_LENGTH 0x0CF2 +#define GL_UNPACK_SKIP_PIXELS 0x0CF4 +#define GL_UNPACK_SKIP_ROWS 0x0CF3 +#define GL_UNPACK_SWAP_BYTES 0x0CF0 +#define GL_ZOOM_X 0x0D16 +#define GL_ZOOM_Y 0x0D17 + +/* Texture mapping */ +#define GL_TEXTURE_ENV 0x2300 +#define GL_TEXTURE_ENV_MODE 0x2200 +#define GL_TEXTURE_1D 0x0DE0 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_TEXTURE_ENV_COLOR 0x2201 +#define GL_TEXTURE_GEN_S 0x0C60 +#define GL_TEXTURE_GEN_T 0x0C61 +#define GL_TEXTURE_GEN_MODE 0x2500 +#define GL_TEXTURE_BORDER_COLOR 0x1004 +#define GL_TEXTURE_WIDTH 0x1000 +#define GL_TEXTURE_HEIGHT 0x1001 +#define GL_TEXTURE_BORDER 0x1005 +#define GL_TEXTURE_COMPONENTS 0x1003 +#define GL_TEXTURE_RED_SIZE 0x805C +#define GL_TEXTURE_GREEN_SIZE 0x805D +#define GL_TEXTURE_BLUE_SIZE 0x805E +#define GL_TEXTURE_ALPHA_SIZE 0x805F +#define GL_TEXTURE_LUMINANCE_SIZE 0x8060 +#define GL_TEXTURE_INTENSITY_SIZE 0x8061 +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 +#define GL_OBJECT_LINEAR 0x2401 +#define GL_OBJECT_PLANE 0x2501 +#define GL_EYE_LINEAR 0x2400 +#define GL_EYE_PLANE 0x2502 +#define GL_SPHERE_MAP 0x2402 +#define GL_DECAL 0x2101 +#define GL_MODULATE 0x2100 +#define GL_NEAREST 0x2600 +#define GL_REPEAT 0x2901 +#define GL_CLAMP 0x2900 +#define GL_S 0x2000 +#define GL_T 0x2001 +#define GL_R 0x2002 +#define GL_Q 0x2003 +#define GL_TEXTURE_GEN_R 0x0C62 +#define GL_TEXTURE_GEN_Q 0x0C63 + +/* GL 1.1 texturing */ +#define GL_PROXY_TEXTURE_1D 0x8063 +#define GL_PROXY_TEXTURE_2D 0x8064 +#define GL_TEXTURE_PRIORITY 0x8066 +#define GL_TEXTURE_RESIDENT 0x8067 +#define GL_TEXTURE_BINDING_1D 0x8068 +#define GL_TEXTURE_BINDING_2D 0x8069 +#define GL_TEXTURE_INTERNAL_FORMAT 0x1003 + +/* GL 1.2 texturing */ +#define GL_PACK_SKIP_IMAGES 0x806B +#define GL_PACK_IMAGE_HEIGHT 0x806C +#define GL_UNPACK_SKIP_IMAGES 0x806D +#define GL_UNPACK_IMAGE_HEIGHT 0x806E +#define GL_TEXTURE_3D 0x806F +#define GL_PROXY_TEXTURE_3D 0x8070 +#define GL_TEXTURE_DEPTH 0x8071 +#define GL_TEXTURE_WRAP_R 0x8072 +#define GL_MAX_3D_TEXTURE_SIZE 0x8073 +#define GL_TEXTURE_BINDING_3D 0x806A + +/* Internal texture formats (GL 1.1) */ +#define GL_ALPHA4 0x803B +#define GL_ALPHA8 0x803C +#define GL_ALPHA12 0x803D +#define GL_ALPHA16 0x803E +#define GL_LUMINANCE4 0x803F +#define GL_LUMINANCE8 0x8040 +#define GL_LUMINANCE12 0x8041 +#define GL_LUMINANCE16 0x8042 +#define GL_LUMINANCE4_ALPHA4 0x8043 +#define GL_LUMINANCE6_ALPHA2 0x8044 +#define GL_LUMINANCE8_ALPHA8 0x8045 +#define GL_LUMINANCE12_ALPHA4 0x8046 +#define GL_LUMINANCE12_ALPHA12 0x8047 +#define GL_LUMINANCE16_ALPHA16 0x8048 +#define GL_INTENSITY 0x8049 +#define GL_INTENSITY4 0x804A +#define GL_INTENSITY8 0x804B +#define GL_INTENSITY12 0x804C +#define GL_INTENSITY16 0x804D +#define GL_R3_G3_B2 0x2A10 +#define GL_RGB4 0x804F +#define GL_RGB5 0x8050 +#define GL_RGB8 0x8051 +#define GL_RGB10 0x8052 +#define GL_RGB12 0x8053 +#define GL_RGB16 0x8054 +#define GL_RGBA2 0x8055 +#define GL_RGBA4 0x8056 +#define GL_RGB5_A1 0x8057 +#define GL_RGBA8 0x8058 +#define GL_RGB10_A2 0x8059 +#define GL_RGBA12 0x805A +#define GL_RGBA16 0x805B + +/* Utility */ +#define GL_VENDOR 0x1F00 +#define GL_RENDERER 0x1F01 +#define GL_VERSION 0x1F02 +#define GL_EXTENSIONS 0x1F03 + +/* Errors */ +#define GL_NO_ERROR 0x0 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_OPERATION 0x0502 +#define GL_STACK_OVERFLOW 0x0503 +#define GL_STACK_UNDERFLOW 0x0504 +#define GL_OUT_OF_MEMORY 0x0505 + + +/* OpenGL 1.2 */ +#define GL_RESCALE_NORMAL 0x803A +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_MAX_ELEMENTS_VERTICES 0x80E8 +#define GL_MAX_ELEMENTS_INDICES 0x80E9 +#define GL_BGR 0x80E0 +#define GL_BGRA 0x80E1 +#define GL_UNSIGNED_BYTE_3_3_2 0x8032 +#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#define GL_UNSIGNED_INT_10_10_10_2 0x8036 +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#define GL_LIGHT_MODEL_COLOR_CONTROL 0x81F8 +#define GL_SINGLE_COLOR 0x81F9 +#define GL_SEPARATE_SPECULAR_COLOR 0x81FA +#define GL_TEXTURE_MIN_LOD 0x813A +#define GL_TEXTURE_MAX_LOD 0x813B +#define GL_TEXTURE_BASE_LEVEL 0x813C +#define GL_TEXTURE_MAX_LEVEL 0x813D +#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 +#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 +#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_ALIASED_POINT_SIZE_RANGE 0x846D +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E + + + +/* + * OpenGL 1.2 imaging subset (NOT IMPLEMENTED BY MESA) + */ +/* GL_EXT_color_table */ +#define GL_COLOR_TABLE 0x80D0 +#define GL_POST_CONVOLUTION_COLOR_TABLE 0x80D1 +#define GL_POST_COLOR_MATRIX_COLOR_TABLE 0x80D2 +#define GL_PROXY_COLOR_TABLE 0x80D3 +#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE 0x80D4 +#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE 0x80D5 +#define GL_COLOR_TABLE_SCALE 0x80D6 +#define GL_COLOR_TABLE_BIAS 0x80D7 +#define GL_COLOR_TABLE_FORMAT 0x80D8 +#define GL_COLOR_TABLE_WIDTH 0x80D9 +#define GL_COLOR_TABLE_RED_SIZE 0x80DA +#define GL_COLOR_TABLE_GREEN_SIZE 0x80DB +#define GL_COLOR_TABLE_BLUE_SIZE 0x80DC +#define GL_COLOR_TABLE_ALPHA_SIZE 0x80DD +#define GL_COLOR_TABLE_LUMINANCE_SIZE 0x80DE +#define GL_COLOR_TABLE_INTENSITY_SIZE 0x80DF +/* GL_EXT_convolution and GL_HP_convolution_border_modes */ +#define GL_CONVOLUTION_1D 0x8010 +#define GL_CONVOLUTION_2D 0x8011 +#define GL_SEPARABLE_2D 0x8012 +#define GL_CONVOLUTION_BORDER_MODE 0x8013 +#define GL_CONVOLUTION_FILTER_SCALE 0x8014 +#define GL_CONVOLUTION_FILTER_BIAS 0x8015 +#define GL_REDUCE 0x8016 +#define GL_CONVOLUTION_FORMAT 0x8017 +#define GL_CONVOLUTION_WIDTH 0x8018 +#define GL_CONVOLUTION_HEIGHT 0x8019 +#define GL_MAX_CONVOLUTION_WIDTH 0x801A +#define GL_MAX_CONVOLUTION_HEIGHT 0x801B +#define GL_POST_CONVOLUTION_RED_SCALE 0x801C +#define GL_POST_CONVOLUTION_GREEN_SCALE 0x801D +#define GL_POST_CONVOLUTION_BLUE_SCALE 0x801E +#define GL_POST_CONVOLUTION_ALPHA_SCALE 0x801F +#define GL_POST_CONVOLUTION_RED_BIAS 0x8020 +#define GL_POST_CONVOLUTION_GREEN_BIAS 0x8021 +#define GL_POST_CONVOLUTION_BLUE_BIAS 0x8022 +#define GL_POST_CONVOLUTION_ALPHA_BIAS 0x8023 +#define GL_CONSTANT_BORDER 0x8151 +#define GL_REPLICATE_BORDER 0x8153 +#define GL_CONVOLUTION_BORDER_COLOR 0x8154 +/* GL_SGI_color_matrix */ +#define GL_COLOR_MATRIX 0x80B1 +#define GL_COLOR_MATRIX_STACK_DEPTH 0x80B2 +#define GL_MAX_COLOR_MATRIX_STACK_DEPTH 0x80B3 +#define GL_POST_COLOR_MATRIX_RED_SCALE 0x80B4 +#define GL_POST_COLOR_MATRIX_GREEN_SCALE 0x80B5 +#define GL_POST_COLOR_MATRIX_BLUE_SCALE 0x80B6 +#define GL_POST_COLOR_MATRIX_ALPHA_SCALE 0x80B7 +#define GL_POST_COLOR_MATRIX_RED_BIAS 0x80B8 +#define GL_POST_COLOR_MATRIX_GREEN_BIAS 0x80B9 +#define GL_POST_COLOR_MATRIX_BLUE_BIAS 0x80BA +#define GL_POST_COLOR_MATRIX_ALPHA_BIAS 0x80BB +/* GL_EXT_histogram */ +#define GL_HISTOGRAM 0x8024 +#define GL_PROXY_HISTOGRAM 0x8025 +#define GL_HISTOGRAM_WIDTH 0x8026 +#define GL_HISTOGRAM_FORMAT 0x8027 +#define GL_HISTOGRAM_RED_SIZE 0x8028 +#define GL_HISTOGRAM_GREEN_SIZE 0x8029 +#define GL_HISTOGRAM_BLUE_SIZE 0x802A +#define GL_HISTOGRAM_ALPHA_SIZE 0x802B +#define GL_HISTOGRAM_LUMINANCE_SIZE 0x802C +#define GL_HISTOGRAM_SINK 0x802D +#define GL_MINMAX 0x802E +#define GL_MINMAX_FORMAT 0x802F +#define GL_MINMAX_SINK 0x8030 +#define GL_TABLE_TOO_LARGE 0x8031 +/* GL_EXT_blend_color, GL_EXT_blend_minmax */ +#define GL_BLEND_EQUATION 0x8009 +#define GL_MIN 0x8007 +#define GL_MAX 0x8008 +#define GL_FUNC_ADD 0x8006 +#define GL_FUNC_SUBTRACT 0x800A +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#define GL_BLEND_COLOR 0x8005 + + +/* glPush/PopAttrib bits */ +#define GL_CURRENT_BIT 0x00000001 +#define GL_POINT_BIT 0x00000002 +#define GL_LINE_BIT 0x00000004 +#define GL_POLYGON_BIT 0x00000008 +#define GL_POLYGON_STIPPLE_BIT 0x00000010 +#define GL_PIXEL_MODE_BIT 0x00000020 +#define GL_LIGHTING_BIT 0x00000040 +#define GL_FOG_BIT 0x00000080 +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_ACCUM_BUFFER_BIT 0x00000200 +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#define GL_VIEWPORT_BIT 0x00000800 +#define GL_TRANSFORM_BIT 0x00001000 +#define GL_ENABLE_BIT 0x00002000 +#define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_HINT_BIT 0x00008000 +#define GL_EVAL_BIT 0x00010000 +#define GL_LIST_BIT 0x00020000 +#define GL_TEXTURE_BIT 0x00040000 +#define GL_SCISSOR_BIT 0x00080000 +#define GL_ALL_ATTRIB_BITS 0x000FFFFF + + +#define GL_CLIENT_PIXEL_STORE_BIT 0x00000001 +#define GL_CLIENT_VERTEX_ARRAY_BIT 0x00000002 +#define GL_ALL_CLIENT_ATTRIB_BITS 0xFFFFFFFF + +/* + * GL_ARB_multitexture (ARB extension 1 and OpenGL 1.2.1) + */ +#define GL_TEXTURE0_ARB 0x84C0 +#define GL_TEXTURE1_ARB 0x84C1 +#define GL_TEXTURE2_ARB 0x84C2 +#define GL_TEXTURE3_ARB 0x84C3 +#define GL_TEXTURE4_ARB 0x84C4 +#define GL_TEXTURE5_ARB 0x84C5 +#define GL_TEXTURE6_ARB 0x84C6 +#define GL_TEXTURE7_ARB 0x84C7 +#define GL_TEXTURE8_ARB 0x84C8 +#define GL_TEXTURE9_ARB 0x84C9 +#define GL_TEXTURE10_ARB 0x84CA +#define GL_TEXTURE11_ARB 0x84CB +#define GL_TEXTURE12_ARB 0x84CC +#define GL_TEXTURE13_ARB 0x84CD +#define GL_TEXTURE14_ARB 0x84CE +#define GL_TEXTURE15_ARB 0x84CF +#define GL_TEXTURE16_ARB 0x84D0 +#define GL_TEXTURE17_ARB 0x84D1 +#define GL_TEXTURE18_ARB 0x84D2 +#define GL_TEXTURE19_ARB 0x84D3 +#define GL_TEXTURE20_ARB 0x84D4 +#define GL_TEXTURE21_ARB 0x84D5 +#define GL_TEXTURE22_ARB 0x84D6 +#define GL_TEXTURE23_ARB 0x84D7 +#define GL_TEXTURE24_ARB 0x84D8 +#define GL_TEXTURE25_ARB 0x84D9 +#define GL_TEXTURE26_ARB 0x84DA +#define GL_TEXTURE27_ARB 0x84DB +#define GL_TEXTURE28_ARB 0x84DC +#define GL_TEXTURE29_ARB 0x84DD +#define GL_TEXTURE30_ARB 0x84DE +#define GL_TEXTURE31_ARB 0x84DF +#define GL_ACTIVE_TEXTURE_ARB 0x84E0 +#define GL_CLIENT_ACTIVE_TEXTURE_ARB 0x84E1 +#define GL_MAX_TEXTURE_UNITS_ARB 0x84E2 + +#define GL_COMBINE 0x8570 +#define GL_COMBINE_RGB 0x8571 +#define GL_COMBINE_ALPHA 0x8572 +#define GL_SOURCE0_RGB 0x8580 +#define GL_SOURCE1_RGB 0x8581 +#define GL_SOURCE2_RGB 0x8582 +#define GL_SOURCE0_ALPHA 0x8588 +#define GL_SOURCE1_ALPHA 0x8589 +#define GL_SOURCE2_ALPHA 0x858A +#define GL_OPERAND0_RGB 0x8590 +#define GL_OPERAND1_RGB 0x8591 +#define GL_OPERAND2_RGB 0x8592 +#define GL_OPERAND0_ALPHA 0x8598 +#define GL_OPERAND1_ALPHA 0x8599 +#define GL_OPERAND2_ALPHA 0x859A +#define GL_RGB_SCALE 0x8573 +#define GL_ADD_SIGNED 0x8574 +#define GL_INTERPOLATE 0x8575 +#define GL_SUBTRACT 0x84E7 +#define GL_CONSTANT 0x8576 +#define GL_PRIMARY_COLOR 0x8577 +#define GL_PREVIOUS 0x8578 +#define GL_DOT3_RGB 0x86AE +#define GL_DOT3_RGBA 0x86AF + + + + + +#endif // #ifndef _X86UNIX_GL_TYPES_H_ diff --git a/Engine/source/platformX86UNIX/platformAL.h b/Engine/source/platformX86UNIX/platformAL.h new file mode 100644 index 000000000..8bb7bed85 --- /dev/null +++ b/Engine/source/platformX86UNIX/platformAL.h @@ -0,0 +1,162 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PLATFORMAL_H_ +#define _PLATFORMAL_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#define AL_NO_PROTOTYPES +#include +#include +#include + +// extra enums for win32/miles implementation +enum { + // error values + AL_CONTEXT_ALREADY_INSTANTIATED = 0xbaadf00d, + AL_ENVIRONMENT_ALREADY_INSTANTIATED, + AL_UNSUPPORTED, + AL_INVALID_BUFFER, + AL_ERROR, + + // context extention + ALC_PROVIDER, + ALC_PROVIDER_COUNT, + ALC_PROVIDER_NAME, + ALC_SPEAKER, + ALC_SPEAKER_COUNT, + ALC_SPEAKER_NAME, + ALC_BUFFER_DYNAMIC_MEMORY_SIZE, + ALC_BUFFER_DYNAMIC_MEMORY_USAGE, + ALC_BUFFER_DYNAMIC_COUNT, + ALC_BUFFER_MEMORY_USAGE, + ALC_BUFFER_COUNT, + ALC_BUFFER_LATENCY, + + // misc 3d params + AL_MIN_DISTANCE, + AL_MAX_DISTANCE, + AL_CONE_OUTER_GAIN, + + // relative with pos(0,0,0) won't work for ambient sounds with miles + AL_SOURCE_AMBIENT, + AL_PAN, + + // other extensions + AL_BUFFER_KEEP_RESIDENT, + AL_FORMAT_WAVE_EXT, + + // Environment extensions: + AL_ENV_EFFECT_VOLUME_EXT, + AL_ENV_FLAGS_EXT, + AL_ENV_DAMPING_EXT, + AL_ENV_ENVIRONMENT_SIZE_EXT, + AL_ENV_ROOM_VOLUME_EXT, +}; + +enum { + // sample level environment: + AL_ENV_SAMPLE_REVERB_MIX_EXT = 0, + AL_ENV_SAMPLE_DIRECT_EXT, + AL_ENV_SAMPLE_DIRECT_HF_EXT, + AL_ENV_SAMPLE_ROOM_EXT, + AL_ENV_SAMPLE_ROOM_HF_EXT, + AL_ENV_SAMPLE_OBSTRUCTION_EXT, + AL_ENV_SAMPLE_OBSTRUCTION_LF_RATIO_EXT, + AL_ENV_SAMPLE_OCCLUSION_EXT, + AL_ENV_SAMPLE_OCCLUSION_LF_RATIO_EXT, + AL_ENV_SAMPLE_OCCLUSION_ROOM_RATIO_EXT, + AL_ENV_SAMPLE_ROOM_ROLLOFF_EXT, + AL_ENV_SAMPLE_AIR_ABSORPTION_EXT, + AL_ENV_SAMPLE_OUTSIDE_VOLUME_HF_EXT, + AL_ENV_SAMPLE_FLAGS_EXT, + + AL_ENV_SAMPLE_COUNT, +}; + +// room types: same as miles/eax +enum { + AL_ENVIRONMENT_GENERIC = 0, + AL_ENVIRONMENT_PADDEDCELL, + AL_ENVIRONMENT_ROOM, + AL_ENVIRONMENT_BATHROOM, + AL_ENVIRONMENT_LIVINGROOM, + AL_ENVIRONMENT_STONEROOM, + AL_ENVIRONMENT_AUDITORIUM, + AL_ENVIRONMENT_CONCERTHALL, + AL_ENVIRONMENT_CAVE, + AL_ENVIRONMENT_ARENA, + AL_ENVIRONMENT_HANGAR, + AL_ENVIRONMENT_CARPETEDHALLWAY, + AL_ENVIRONMENT_HALLWAY, + AL_ENVIRONMENT_STONECORRIDOR, + AL_ENVIRONMENT_ALLEY, + AL_ENVIRONMENT_FOREST, + AL_ENVIRONMENT_CITY, + AL_ENVIRONMENT_MOUNTAINS, + AL_ENVIRONMENT_QUARRY, + AL_ENVIRONMENT_PLAIN, + AL_ENVIRONMENT_PARKINGLOT, + AL_ENVIRONMENT_SEWERPIPE, + AL_ENVIRONMENT_UNDERWATER, + AL_ENVIRONMENT_DRUGGED, + AL_ENVIRONMENT_DIZZY, + AL_ENVIRONMENT_PSYCHOTIC, + + AL_ENVIRONMENT_COUNT +}; + +// declare OpenAL functions +#define AL_EXTENSION(ext_name) extern bool gDoesSupport_##ext_name; +#define AL_FUNCTION(fn_return,fn_name,fn_args) extern fn_return (FN_CDECL *fn_name)fn_args; +#define AL_EXT_FUNCTION(ext_name,fn_return,fn_name,fn_args) extern fn_return (FN_CDECL *fn_name)fn_args; +#ifndef _OPENALFN_H_ +#include +#endif + +namespace Audio +{ + +bool libraryInit(const char *library); +void libraryInitExtensions(); +void libraryShutdown(); + +inline bool doesSupportIASIG() +{ + return gDoesSupport_AL_EXT_IASIG; +} + +inline bool doesSupportDynamix() +{ + return gDoesSupport_AL_EXT_DYNAMIX; +} + +// helpers +F32 DBToLinear(F32 value); +F32 linearToDB(F32 value); + +} // end namespace Audio + + +#endif // _H_PLATFORMAL_ diff --git a/Engine/source/platformX86UNIX/platformX86UNIX.h b/Engine/source/platformX86UNIX/platformX86UNIX.h new file mode 100644 index 000000000..96a176a39 --- /dev/null +++ b/Engine/source/platformX86UNIX/platformX86UNIX.h @@ -0,0 +1,69 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PLATFORMX86UNIX_H_ +#define _PLATFORMX86UNIX_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#ifndef _EVENT_H_ +#include "platform/event.h" +#endif + +#include +#include + +// these will be used to construct the user preference directory where +// created files will be stored (~/PREF_DIR_ROOT/PREF_DIR_GAME_NAME) +#define PREF_DIR_ROOT ".garagegames" +#define PREF_DIR_GAME_NAME "torqueDemo" + +// event codes for custom SDL events +const S32 TORQUE_SETVIDEOMODE = 1; + +extern bool GL_EXT_Init( void ); + +extern void PlatformBlitInit( void ); + +// Process Control functions +void Cleanup(bool minimal=false); +void ImmediateShutdown(S32 exitCode, S32 signalNum = 0); +void ProcessControlInit(); +bool AcquireProcessMutex(const char *mutexName); + +// Utility functions +// Convert a string to lowercase in place +char *strtolwr(char* str); + +void DisplayErrorAlert(const char* errMsg, bool showSDLError = true); + +// Just like strstr, except case insensitive +// (Found this function at http://www.codeguru.com/string/stristr.html) +extern char *stristr(char *szStringToBeSearched, const char *szSubstringToSearchFor); + +extern "C" +{ + // x86UNIX doesn't have a way to automatically get the executable file name + void setExePathName(const char* exePathName); +} +#endif diff --git a/Engine/source/platformX86UNIX/threads/mutex.cpp b/Engine/source/platformX86UNIX/threads/mutex.cpp new file mode 100644 index 000000000..c3d5acfe9 --- /dev/null +++ b/Engine/source/platformX86UNIX/threads/mutex.cpp @@ -0,0 +1,76 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/console.h" +#include "platformX86UNIX/platformX86UNIX.h" +#include "platform/threads/mutex.h" +#include "core/util/safeDelete.h" + +#include +#include +#include +#include +#include + +struct PlatformMutexData +{ + pthread_mutex_t mutex; +}; + +Mutex::Mutex() +{ + mData = new PlatformMutexData; + pthread_mutexattr_t attr; + + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE); + + pthread_mutex_init(&mData->mutex, &attr); +} + +Mutex::~Mutex() +{ + AssertFatal(mData, "Mutex::destroyMutex: invalid mutex"); + pthread_mutex_destroy(&mData->mutex); + SAFE_DELETE(mData); +} + +bool Mutex::lock(bool block) +{ + if(mData == NULL) + return false; + if(block) + { + return pthread_mutex_lock(&mData->mutex) == 0; + } + else + { + return pthread_mutex_trylock(&mData->mutex) == 0; + } +} + +void Mutex::unlock() +{ + if(mData == NULL) + return; + pthread_mutex_unlock(&mData->mutex); +} diff --git a/Engine/source/platformX86UNIX/threads/semaphore.cpp b/Engine/source/platformX86UNIX/threads/semaphore.cpp new file mode 100644 index 000000000..2bdaade06 --- /dev/null +++ b/Engine/source/platformX86UNIX/threads/semaphore.cpp @@ -0,0 +1,78 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformX86UNIX/platformX86UNIX.h" +#include "platform/threads/semaphore.h" +// Instead of that mess that was here before, lets use the SDL lib to deal +// with the semaphores. + +#include +#include + +struct PlatformSemaphore +{ + SDL_sem *semaphore; + + PlatformSemaphore(S32 initialCount) + { + semaphore = SDL_CreateSemaphore(initialCount); + AssertFatal(semaphore, "PlatformSemaphore constructor - Failed to create SDL Semaphore."); + } + + ~PlatformSemaphore() + { + SDL_DestroySemaphore(semaphore); + } +}; + +Semaphore::Semaphore(S32 initialCount) +{ + mData = new PlatformSemaphore(initialCount); +} + +Semaphore::~Semaphore() +{ + AssertFatal(mData, "Semaphore destructor - Invalid semaphore."); + delete mData; +} + +bool Semaphore::acquire(bool block) +{ + AssertFatal(mData, "Semaphore::acquire - Invalid semaphore."); + if (block) + { + if (SDL_SemWait(mData->semaphore) < 0) + AssertFatal(false, "Semaphore::acquie - Wait failed."); + return (true); + } + else + { + int res = SDL_SemTryWait(mData->semaphore); + return (res == 0); + } +} + +void Semaphore::release() +{ + AssertFatal(mData, "Semaphore::releaseSemaphore - Invalid semaphore."); + SDL_SemPost(mData->semaphore); +} diff --git a/Engine/source/platformX86UNIX/threads/thread.cpp b/Engine/source/platformX86UNIX/threads/thread.cpp new file mode 100644 index 000000000..a8c1cf1b1 --- /dev/null +++ b/Engine/source/platformX86UNIX/threads/thread.cpp @@ -0,0 +1,159 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include +#include "platform/threads/thread.h" +#include "platform/threads/semaphore.h" +#include "platform/threads/mutex.h" +#include + +class PlatformThreadData +{ +public: + ThreadRunFunction mRunFunc; + void* mRunArg; + Thread* mThread; + Semaphore mGateway; // default count is 1 + pthread_t mThreadID; + bool mDead; +}; + +ThreadManager::MainThreadId ThreadManager::smMainThreadId; + +//----------------------------------------------------------------------------- +// Function: ThreadRunHandler +// Summary: Calls Thread::run() with the thread's specified run argument. +// Neccesary because Thread::run() is provided as a non-threaded +// way to execute the thread's run function. So we have to keep +// track of the thread's lock here. +static void *ThreadRunHandler(void * arg) +{ + PlatformThreadData *mData = reinterpret_cast(arg); + Thread *thread = mData->mThread; + + // mThreadID is filled in twice, once here and once in pthread_create(). + // We fill in mThreadID here as well as in pthread_create() because addThread() + // can execute before pthread_create() returns and sets mThreadID. + // The value from pthread_create() and pthread_self() are guaranteed to be equivalent (but not identical) + mData->mThreadID = pthread_self(); + + ThreadManager::addThread(thread); + thread->run(mData->mRunArg); + ThreadManager::removeThread(thread); + + bool autoDelete = thread->autoDelete; + + mData->mThreadID = 0; + mData->mDead = true; + mData->mGateway.release(); + + if( autoDelete ) + delete thread; + + // return value for pthread lib's benefit + return NULL; + // the end of this function is where the created pthread will die. +} + +//----------------------------------------------------------------------------- +Thread::Thread(ThreadRunFunction func, void* arg, bool start_thread, bool autodelete) +{ + AssertFatal( !start_thread, "Thread::Thread() - auto-starting threads from ctor has been disallowed since the run() method is virtual" ); + + mData = new PlatformThreadData; + mData->mRunFunc = func; + mData->mRunArg = arg; + mData->mThread = this; + mData->mThreadID = 0; + mData->mDead = false; + autoDelete = autodelete; +} + +Thread::~Thread() +{ + stop(); + if( isAlive() ) + join(); + + delete mData; +} + +void Thread::start( void* arg ) +{ + // cause start to block out other pthreads from using this Thread, + // at least until ThreadRunHandler exits. + mData->mGateway.acquire(); + + // reset the shouldStop flag, so we'll know when someone asks us to stop. + shouldStop = false; + + mData->mDead = false; + + if( !mData->mRunArg ) + mData->mRunArg = arg; + + pthread_create(&mData->mThreadID, NULL, ThreadRunHandler, mData); +} + +bool Thread::join() +{ + // not using pthread_join here because pthread_join cannot deal + // with multiple simultaneous calls. + + mData->mGateway.acquire(); + AssertFatal( !isAlive(), "Thread::join() - thread not dead after join()" ); + mData->mGateway.release(); + + return true; +} + +void Thread::run(void* arg) +{ + if(mData->mRunFunc) + mData->mRunFunc(arg); +} + +bool Thread::isAlive() +{ + return ( !mData->mDead ); +} + +U32 Thread::getId() +{ + return (U32)mData->mThreadID; +} + +void Thread::_setName( const char* ) +{ + // Not supported. Wading through endless lists of Thread-1, Thread-2, Thread-3, ... trying to find + // that one thread you are looking for is just so much fun. +} + +U32 ThreadManager::getCurrentThreadId() +{ + return (U32)pthread_self(); +} + +bool ThreadManager::compare(U32 threadId_1, U32 threadId_2) +{ + return pthread_equal((pthread_t)threadId_1, (pthread_t)threadId_2); +} diff --git a/Engine/source/platformX86UNIX/x86UNIXAsmBlit.cpp b/Engine/source/platformX86UNIX/x86UNIXAsmBlit.cpp new file mode 100644 index 000000000..be3dad3b6 --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXAsmBlit.cpp @@ -0,0 +1,63 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "math/mMath.h" +#include "gfx/bitmap/bitmapUtils.h" + +//-------------------------------------------------------------------------- +void bitmapExtrude5551_asm(const void *srcMip, void *mip, U32 height, U32 width) +{ + const U16 *src = (const U16 *) srcMip; + U16 *dst = (U16 *) mip; + U32 stride = width << 1; + + for(U32 y = 0; y < height; y++) + { + for(U32 x = 0; x < width; x++) + { + U32 a = src[0]; + U32 b = src[1]; + U32 c = src[stride]; + U32 d = src[stride+1]; + dst[x] = ((((a >> 11) + (b >> 11) + (c >> 11) + (d >> 11)) >> 2) << 11) | + ((( ((a >> 6) & 0x1f) + ((b >> 6) & 0x1f) + ((c >> 6) & 0x1f) + ((d >> 6) & 0x1F) ) >> 2) << 6) | + ((( ((a >> 1) & 0x1F) + ((b >> 1) & 0x1F) + ((c >> 1) & 0x1f) + ((d >> 1) & 0x1f)) >> 2) << 1); + src += 2; + } + src += stride; + dst += width; + } +} + +//-------------------------------------------------------------------------- +void PlatformBlitInit() +{ + bitmapExtrude5551 = bitmapExtrude5551_asm; + bitmapExtrudeRGB = bitmapExtrudeRGB_c; + + if (Platform::SystemInfo.processor.properties & CPU_PROP_MMX) + { + // JMQ: haven't bothered porting mmx bitmap funcs because they don't + // seem to offer a big performance boost right now. + } +} + diff --git a/Engine/source/platformX86UNIX/x86UNIXCPUInfo.cpp b/Engine/source/platformX86UNIX/x86UNIXCPUInfo.cpp new file mode 100644 index 000000000..0fa7ca337 --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXCPUInfo.cpp @@ -0,0 +1,156 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "platformX86UNIX/platformX86UNIX.h" +#include "console/console.h" +#include "core/stringTable.h" +#include "core/strings/stringFunctions.h" +#include + +Platform::SystemInfo_struct Platform::SystemInfo; + +extern void PlatformBlitInit(); +extern void SetProcessorInfo(Platform::SystemInfo_struct::Processor& pInfo, + char* vendor, U32 processor, U32 properties, U32 properties2); // platform/platformCPU.cc + +// asm cpu detection routine from platform code +extern "C" +{ +void detectX86CPUInfo(char *vendor, U32 *processor, U32 *properties); +} + +/* used in the asm */ +static U32 time[2]; +static char vendor[13] = {0,}; +static U32 properties = 0; +static U32 processor = 0; +U32 clockticks = 0; +U32 timeHi = 0; +U32 timeLo = 0; + +void Processor::init() +{ + // Reference: + // www.cyrix.com + // www.amd.com + // www.intel.com + // http://developer.intel.com/design/PentiumII/manuals/24512701.pdf + Platform::SystemInfo.processor.type = CPU_X86Compatible; + Platform::SystemInfo.processor.name = StringTable->insert("Unknown x86 Compatible"); + Platform::SystemInfo.processor.mhz = 0; + Platform::SystemInfo.processor.properties = CPU_PROP_C; + + clockticks = properties = processor = time[0] = 0; + dStrcpy(vendor, ""); + + detectX86CPUInfo(vendor, &processor, &properties); + SetProcessorInfo(Platform::SystemInfo.processor, + vendor, processor, properties, 0); + + //-------------------------------------- + // if RDTSC support calculate the aproximate Mhz of the CPU + if (Platform::SystemInfo.processor.properties & CPU_PROP_RDTSC && + Platform::SystemInfo.processor.properties & CPU_PROP_FPU) + { + const U32 MS_INTERVAL = 750; + +#if defined(TORQUE_COMPILER_GCC) && ((__GNUC__ >= 3) && (__GNUC_MINOR__ >=4)) || ((__GNUC__ >= 4) && (__GNUC_MINOR__ >=0)) + asm("rdtsc" : "=a" (timeLo), "=d" (timeHi)); +#else + __asm__( + "pushl %eax\n" + "pushl %edx\n" + "rdtsc\n" + "movl %eax, (time)\n" + "movl %edx, (time+4)\n" + "popl %edx\n" + "popl %eax\n" + ); +#endif + U32 ms = Platform::getRealMilliseconds(); + while ( Platform::getRealMilliseconds() < ms+MS_INTERVAL ) + { /* empty */ } + ms = Platform::getRealMilliseconds()-ms; +#if defined(TORQUE_COMPILER_GCC) && ((__GNUC__ >= 3) && (__GNUC_MINOR__ >= 4)) || ((__GNUC__ >= 4) && (__GNUC_MINOR__ >=0)) + asm( + "pushl %eax\n" + "pushl %edx\n" + "rdtsc\n" + "sub (timeHi), %edx\n" + "sbb (timeLo), %eax\n" + "mov %eax, (clockticks)\n" + "popl %edx\n" + "popl %eax\n" + ); +#else + asm( + "pushl %eax\n" + "pushl %edx\n" + "rdtsc\n" + "sub (time+4), %edx\n" + "sbb (time), %eax\n" + "mov %eax, (clockticks)\n" + "popl %edx\n" + "popl %eax\n" + ); +#endif + U32 mhz = static_cast(F32(clockticks) / F32(ms) / 1000.0f); + + // catch-22 the timing method used above to calc Mhz is generally + // wrong by a few percent so we want to round to the nearest clock + // multiple but we also want to be careful to not touch overclocked + // results + + // measure how close the Raw Mhz number is to the center of each clock + // bucket + U32 bucket25 = mhz % 25; + U32 bucket33 = mhz % 33; + U32 bucket50 = mhz % 50; + + if (bucket50 < 8 || bucket50 > 42) + Platform::SystemInfo.processor.mhz = + U32((mhz+(50.0f/2.0f))/50.0f) * 50; + else if (bucket25 < 5 || bucket25 > 20) + Platform::SystemInfo.processor.mhz = + U32((mhz+(25.0f/2.0f))/25.0f) * 25; + else if (bucket33 < 5 || bucket33 > 28) + Platform::SystemInfo.processor.mhz = + U32((mhz+(33.0f/2.0f))/33.0f) * 33; + else + Platform::SystemInfo.processor.mhz = U32(mhz); + } + + Con::printf("Processor Init:"); + Con::printf(" %s, %d Mhz", Platform::SystemInfo.processor.name, Platform::SystemInfo.processor.mhz); + if (Platform::SystemInfo.processor.properties & CPU_PROP_FPU) + Con::printf(" FPU detected"); + if (Platform::SystemInfo.processor.properties & CPU_PROP_MMX) + Con::printf(" MMX detected"); + if (Platform::SystemInfo.processor.properties & CPU_PROP_3DNOW) + Con::printf(" 3DNow detected"); + if (Platform::SystemInfo.processor.properties & CPU_PROP_SSE) + Con::printf(" SSE detected"); + Con::printf(" "); + + PlatformBlitInit(); +} diff --git a/Engine/source/platformX86UNIX/x86UNIXConsole.cpp b/Engine/source/platformX86UNIX/x86UNIXConsole.cpp new file mode 100644 index 000000000..256b5ca5c --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXConsole.cpp @@ -0,0 +1,404 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformX86UNIX/platformX86UNIX.h" +#include "platformX86UNIX/x86UNIXStdConsole.h" +#include "platformX86UNIX/x86UNIXUtils.h" +#include "platform/event.h" +#include "platform/platform.h" +#include "core/util/rawData.h" +#include "core/strings/stringFunctions.h" +#include "core/util/journal/process.h" + +#include +#include +#include +#include + +StdConsole *stdConsole = NULL; + +ConsoleFunction(enableWinConsole, void, 2, 2, "enableWinConsole(bool);") +{ + argc; + if (stdConsole) + stdConsole->enable(dAtob(argv[1])); +} + +void StdConsole::create() +{ + if (stdConsole == NULL) + stdConsole = new StdConsole(); +} + +void StdConsole::destroy() +{ + if (stdConsole && stdConsole->isEnabled()) + stdConsole->enable(false); + + delete stdConsole; + stdConsole = NULL; +} + +static void signalHandler(int sigtype) +{ + if (sigtype == SIGCONT && stdConsole != NULL) + stdConsole->resetTerminal(); +} + +void StdConsole::resetTerminal() +{ + if (stdConsoleEnabled) + { + /* setup the proper terminal modes */ + struct termios termModes; + tcgetattr(stdIn, &termModes); + termModes.c_lflag &= ~ICANON; // enable non-canonical mode + termModes.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHOKE); + termModes.c_cc[VMIN] = 0; + termModes.c_cc[VTIME] = 5; + tcsetattr(stdIn, TCSAFLUSH, &termModes); + } +} + +void StdConsole::enable(bool enabled) +{ + if (enabled && !stdConsoleEnabled) + { + stdConsoleEnabled = true; + + // install signal handler for sigcont + signal(SIGCONT, &signalHandler); + + // save the terminal state + if (originalTermState == NULL) + originalTermState = new termios; + + tcgetattr(stdIn, originalTermState); + + // put the terminal into our preferred mode + resetTerminal(); + + printf("%s", Con::getVariable("Con::Prompt")); + + } + else if (!enabled && stdConsoleEnabled) + { + stdConsoleEnabled = false; + + // uninstall signal handler + signal(SIGCONT, SIG_DFL); + + // reset the original terminal state + if (originalTermState != NULL) + tcsetattr(stdIn, TCSANOW, originalTermState); + } +} + +bool StdConsole::isEnabled() +{ + if ( stdConsole ) + return stdConsole->stdConsoleEnabled; + + return false; +} + +static void stdConsoleConsumer(/*ConsoleLogEntry::Level*/ U32, const char *line) +{ + stdConsole->processConsoleLine(line); +} + +StdConsole::StdConsole() +{ + for (S32 iIndex = 0; iIndex < MAX_CMDS; iIndex ++) + rgCmds[iIndex][0] = '\0'; + + stdOut = dup(1); + stdIn = dup(0); + stdErr = dup(2); + + iCmdIndex = 0; + stdConsoleEnabled = false; + Con::addConsumer(stdConsoleConsumer); + inpos = 0; + lineOutput = false; + inBackground = false; + originalTermState = NULL; + + Process::notify(this, &StdConsole::process, PROCESS_LAST_ORDER); +} + +StdConsole::~StdConsole() +{ + Con::removeConsumer(stdConsoleConsumer); + + if (stdConsoleEnabled) + enable(false); + + if (originalTermState != NULL) + { + delete originalTermState; + originalTermState = NULL; + } +} + +void StdConsole::printf(const char *s, ...) +{ + // Get the line into a buffer. + static const int BufSize = 4096; + static char buffer[BufSize]; + va_list args; + va_start(args, s); + vsnprintf(buffer, BufSize, s, args); + // Replace tabs with carats, like the "real" console does. + char *pos = buffer; + while (*pos) { + if (*pos == '\t') { + *pos = '^'; + } + pos++; + } + // Axe the color characters. + Con::stripColorChars(buffer); + // Print it. + write(stdOut, buffer, strlen(buffer)); + fflush(stdout); +} + +void StdConsole::processConsoleLine(const char *consoleLine) +{ + if(stdConsoleEnabled) + { + inbuf[inpos] = 0; + if(lineOutput) + printf("%s\n", consoleLine); + else + printf("%c%s\n%s%s", '\r', consoleLine, Con::getVariable("Con::Prompt"), inbuf); + } +} + +void StdConsole::process() +{ + if(stdConsoleEnabled) + { + //U16 key; + char typedData[64]; // damn, if you can type this fast... :-D + + if (UUtils->inBackground()) + // we don't have the terminal + inBackground = true; + else + { + // if we were in the background, reset the terminal + if (inBackground) + resetTerminal(); + inBackground = false; + } + + // see if stdIn has any input waiting + // mojo for select call + fd_set rfds; + struct timeval tv; + int retval; + FD_ZERO(&rfds); + FD_SET(stdIn, &rfds); + // don't wait at all in select + tv.tv_sec = 0; + tv.tv_usec = 0; + + int numEvents = select(stdIn+1, &rfds, NULL, NULL, &tv); + if (numEvents <= 0) + // no data available + return; + + numEvents = read(stdIn, typedData, 64); + if (numEvents == -1) + return; + + typedData[numEvents] = '\0'; + if (numEvents > 0) + { + char outbuf[512]; + S32 outpos = 0; + + for (int i = 0; i < numEvents; i++) + { + switch(typedData[i]) + { + case 8: + case 127: + /* backspace */ + if (inpos > 0) + { + outbuf[outpos++] = '\b'; + outbuf[outpos++] = ' '; + outbuf[outpos++] = '\b'; + inpos--; + } + break; + + // XXX Don't know if we can detect shift-TAB. So, only handling + // TAB for now. + + case '\t': + // In the output buffer, we're going to have to erase the current line (in case + // we're cycling through various completions) and write out the whole input + // buffer, so (inpos * 3) + complen <= 512. Should be OK. The input buffer is + // also 512 chars long so that constraint will also be fine for the input buffer. + { + // Erase the current line. + U32 i; + for (i = 0; i < inpos; i++) { + outbuf[outpos++] = '\b'; + outbuf[outpos++] = ' '; + outbuf[outpos++] = '\b'; + } + // Modify the input buffer with the completion. + U32 maxlen = 512 - (inpos * 3); + inpos = Con::tabComplete(inbuf, inpos, maxlen, true); + // Copy the input buffer to the output. + for (i = 0; i < inpos; i++) { + outbuf[outpos++] = inbuf[i]; + } + } + break; + + case '\n': + case '\r': + /* new line */ + outbuf[outpos++] = '\n'; + + inbuf[inpos] = 0; + outbuf[outpos] = 0; + printf("%s", outbuf); + + S32 eventSize; + eventSize = 1; + + { + RawData rd; + rd.size = inpos + 1; + rd.data = (S8*) inbuf; + + Con::smConsoleInput.trigger(rd); + } + + // If we've gone off the end of our array, wrap + // back to the beginning + if (iCmdIndex >= MAX_CMDS) + iCmdIndex %= MAX_CMDS; + + // Put the new command into the array + strcpy(rgCmds[iCmdIndex ++], inbuf); + + printf("%s", Con::getVariable("Con::Prompt")); + inpos = outpos = 0; + break; + case 27: + // JMQTODO: are these magic numbers keyboard map specific? + if (typedData[i+1] == 91 || typedData[i+1] == 79) + { + i += 2; + // an arrow key was pressed. + switch(typedData[i]) + { + case 'A': + /* up arrow */ + // Go to the previous command in the cyclic array + if ((-- iCmdIndex) < 0) + iCmdIndex = MAX_CMDS - 1; + + // If this command isn't empty ... + if (rgCmds[iCmdIndex][0] != '\0') + { + // Obliterate current displayed text + for (S32 i = outpos = 0; i < inpos; i ++) + { + outbuf[outpos++] = '\b'; + outbuf[outpos++] = ' '; + outbuf[outpos++] = '\b'; + } + + // Copy command into command and display buffers + for (inpos = 0; inpos < (S32)strlen(rgCmds[iCmdIndex]); inpos++, outpos++) + { + outbuf[outpos] = rgCmds[iCmdIndex][inpos]; + inbuf [inpos ] = rgCmds[iCmdIndex][inpos]; + } + } + // If previous is empty, stay on current command + else if ((++ iCmdIndex) >= MAX_CMDS) + { + iCmdIndex = 0; + } + break; + case 'B': + /* down arrow */ + // Go to the next command in the command array, if + // it isn't empty + if (rgCmds[iCmdIndex][0] != '\0' && (++ iCmdIndex) >= MAX_CMDS) + iCmdIndex = 0; + + // Obliterate current displayed text + for (S32 i = outpos = 0; i < inpos; i ++) + { + outbuf[outpos++] = '\b'; + outbuf[outpos++] = ' '; + outbuf[outpos++] = '\b'; + } + + // Copy command into command and display buffers + for (inpos = 0; inpos < (S32)strlen(rgCmds[iCmdIndex]); inpos++, outpos++) + { + outbuf[outpos] = rgCmds[iCmdIndex][inpos]; + inbuf [inpos ] = rgCmds[iCmdIndex][inpos]; + } + break; + case 'C': + /* right arrow */ + break; + case 'D': + /* left arrow */ + break; + } + // read again to get rid of a bad char. + //read(stdIn, &key, sizeof(char)); + break; + } else { + inbuf[inpos++] = typedData[i]; + outbuf[outpos++] = typedData[i]; + break; + } + break; + default: + inbuf[inpos++] = typedData[i]; + outbuf[outpos++] = typedData[i]; + break; + } + } + if (outpos) + { + outbuf[outpos] = 0; + printf("%s", outbuf); + } + } + } +} diff --git a/Engine/source/platformX86UNIX/x86UNIXFileio.cpp b/Engine/source/platformX86UNIX/x86UNIXFileio.cpp new file mode 100644 index 000000000..aed6a1afe --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXFileio.cpp @@ -0,0 +1,1270 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + + /* JMQ: + + Here's the scoop on unix file IO. The windows platform makes some + assumptions about fileio: 1) the file system is case-insensitive, and + 2) the platform can write to the directory in which + the game is running. Both of these are usually false on linux. So, to + compensate, we "route" created files and directories to the user's home + directory (see GetPrefPath()). When a file is to be accessed, the code + looks in the home directory first. If the file is not found there and the + open mode is read only, the code will look in the game installation + directory. Files are never created or modified in the game directory. + + For case-sensitivity, the MungePath code will test whether a given path + specified by the engine exists. If not, it will use the MungeCase function + which will try to determine if an actual filesystem path matches the + specified path case insensitive. If one is found, the actual path + transparently (we hope) replaces the one requested by the engine. + + The preference directory is global to all torque executables with the same + name. You should make sure you keep it clean if you build from multiple + torque development trees. + */ + + // evil hack to get around insane X windows #define-happy header files + #ifdef Status + #undef Status + #endif + + #include "platformX86UNIX/platformX86UNIX.h" + #include "core/fileio.h" + #include "core/util/tVector.h" + #include "core/stringTable.h" + #include "console/console.h" + #include "core/strings/stringFunctions.h" + #include "util/tempAlloc.h" + #include "cinterface/cinterface.h" + + #if defined(__FreeBSD__) + #include + #endif + #include + + /* these are for reading directors, getting stats, etc. */ + #include + #include + #include + #include + #include + #include + #include + + extern int x86UNIXOpen(const char *path, int oflag); + extern int x86UNIXClose(int fd); + extern ssize_t x86UNIXRead(int fd, void *buf, size_t nbytes); + extern ssize_t x86UNIXWrite(int fd, const void *buf, size_t nbytes); + + const int MaxPath = PATH_MAX; + + namespace + { + const char sTempDir[] = "/tmp/"; + + static char sBinPathName[MaxPath] = ""; + static char *sBinName = sBinPathName; + bool sUseRedirect = true; + } + + StringTableEntry osGetTemporaryDirectory() + { + return StringTable->insert(sTempDir); + } + + // Various handy utility functions: + //------------------------------------------------------------------------------ + // find all \ in a path and convert them in place to / + static void ForwardSlash(char *str) + { + while(*str) + { + if(*str == '\\') + *str = '/'; + str++; + } + } + + //------------------------------------------------------------------------------ + // copy a file from src to dest + static bool CopyFile(const char* src, const char* dest) + { + S32 srcFd = x86UNIXOpen(src, O_RDONLY); + S32 destFd = x86UNIXOpen(dest, O_WRONLY | O_CREAT | O_TRUNC); + bool error = false; + + if (srcFd != -1 && destFd != -1) + { + const int BufSize = 8192; + char buf[BufSize]; + S32 bytesRead = 0; + while ((bytesRead = x86UNIXRead(srcFd, buf, BufSize)) > 0) + { + // write data + if (x86UNIXWrite(destFd, buf, bytesRead) == -1) + { + error = true; + break; + } + } + + if (bytesRead == -1) + error = true; + } + + if (srcFd != -1) + x86UNIXClose(srcFd); + if (destFd != -1) + x86UNIXClose(destFd); + + if (error) + { + Con::errorf("Error copying file: %s, %s", src, dest); + remove(dest); + } + return error; + } + +bool dPathCopy(const char *fromName, const char *toName, bool nooverwrite) +{ + return CopyFile(fromName,toName); +} + + //----------------------------------------------------------------------------- + static char sgPrefDir[MaxPath]; + static bool sgPrefDirInitialized = false; + + // get the "pref dir", which is where game output files are stored. the pref + // dir is ~/PREF_DIR_ROOT/PREF_DIR_GAME_NAME + static const char* GetPrefDir() + { + if (sgPrefDirInitialized) + return sgPrefDir; + + if (sUseRedirect) + { + const char *home = getenv("HOME"); + AssertFatal(home, "HOME environment variable must be set"); + + dSprintf(sgPrefDir, MaxPath, "%s/%s/%s", + home, PREF_DIR_ROOT, PREF_DIR_GAME_NAME); + } + else + { + getcwd(sgPrefDir, MaxPath); + } + + sgPrefDirInitialized = true; + return sgPrefDir; + } + + //------------------------------------------------------------------------------ + // munge the case of the specified pathName. This means try to find the actual + // filename in with case-insensitive matching on the specified pathName, and + // store the actual found name. + static void MungeCase(char* pathName, S32 pathNameSize) + { + char tempBuf[MaxPath]; + dStrncpy(tempBuf, pathName, pathNameSize); + + AssertFatal(pathName[0] == '/', "PATH must be absolute"); + + struct stat filestat; + const int MaxPathEl = 200; + char *currChar = pathName; + char testPath[MaxPath]; + char pathEl[MaxPathEl]; + bool done = false; + + dStrncpy(tempBuf, "/", MaxPath); + currChar++; + + while (!done) + { + char* termChar = dStrchr(currChar, '/'); + if (termChar == NULL) + termChar = dStrchr(currChar, '\0'); + AssertFatal(termChar, "Can't find / or NULL terminator"); + + S32 pathElLen = (termChar - currChar); + dStrncpy(pathEl, currChar, pathElLen); + pathEl[pathElLen] = '\0'; + dStrncpy(testPath, tempBuf, MaxPath); + dStrcat(testPath, pathEl); + if (stat(testPath, &filestat) != -1) + { + dStrncpy(tempBuf, testPath, MaxPath); + } + else + { + DIR *dir = opendir(tempBuf); + struct dirent* ent; + bool foundMatch = false; + while (dir != NULL && (ent = readdir(dir)) != NULL) + { + if (dStricmp(pathEl, ent->d_name) == 0) + { + foundMatch = true; + dStrcat(tempBuf, ent->d_name); + break; + } + } + + if (!foundMatch) + dStrncpy(tempBuf, testPath, MaxPath); + if (dir) + closedir(dir); + } + if (*termChar == '/') + { + dStrcat(tempBuf, "/"); + termChar++; + currChar = termChar; + } + else + done = true; + } + + dStrncpy(pathName, tempBuf, pathNameSize); + } + + //----------------------------------------------------------------------------- + // Returns true if the pathname exists, false otherwise. If isFile is true, + // the pathname is assumed to be a file path, and only the directory part + // will be examined (everything before last /) + bool DirExists(char* pathname, bool isFile) + { + static char testpath[20000]; + dStrncpy(testpath, pathname, sizeof(testpath)); + if (isFile) + { + // find the last / and make it into null + char* lastSlash = dStrrchr(testpath, '/'); + if (lastSlash != NULL) + *lastSlash = 0; + } + return Platform::isDirectory(testpath); + } + + //----------------------------------------------------------------------------- + // Munge the specified path. + static void MungePath(char* dest, S32 destSize, + const char* src, const char* absolutePrefix) + { + char tempBuf[MaxPath]; + dStrncpy(dest, src, MaxPath); + + // translate all \ to / + ForwardSlash(dest); + + // if it is relative, make it absolute with the absolutePrefix + if (dest[0] != '/') + { + AssertFatal(absolutePrefix, "Absolute Prefix must not be NULL"); + + dSprintf(tempBuf, MaxPath, "%s/%s", + absolutePrefix, dest); + + // copy the result back into dest + dStrncpy(dest, tempBuf, destSize); + } + + // if the path exists, we're done + struct stat filestat; + if (stat(dest, &filestat) != -1) + return; + + // otherwise munge the case of the path + MungeCase(dest, destSize); + } + + //----------------------------------------------------------------------------- + enum + { + TOUCH, + DELETE + }; + + //----------------------------------------------------------------------------- + // perform a modification on the specified file. allowed modifications are + // specified in the enum above. + bool ModifyFile(const char * name, S32 modType) + { + if(!name || (dStrlen(name) >= MaxPath) || dStrstr(name, "../") != NULL) + return(false); + + // if its absolute skip it + if (name[0]=='/' || name[0]=='\\') + return(false); + + // only modify files in home directory + char prefPathName[MaxPath]; + MungePath(prefPathName, MaxPath, name, GetPrefDir()); + + if (modType == TOUCH) + return(utime(prefPathName, 0) != -1); + else if (modType == DELETE) + return (remove(prefPathName) != -1); + else + AssertFatal(false, "Unknown File Mod type"); + return false; + } + + //----------------------------------------------------------------------------- + static bool RecurseDumpPath(const char *path, const char* relativePath, const char *pattern, Vector &fileVector) + { + char search[1024]; + + dSprintf(search, sizeof(search), "%s", path, pattern); + + DIR *directory = opendir(search); + + if (directory == NULL) + return false; + + struct dirent *fEntry; + fEntry = readdir(directory); // read the first "file" in the directory + + if (fEntry == NULL) + { + closedir(directory); + return false; + } + + do + { + char filename[BUFSIZ+1]; + struct stat fStat; + + dSprintf(filename, sizeof(filename), "%s/%s", search, fEntry->d_name); // "construct" the file name + stat(filename, &fStat); // get the file stats + + if ( (fStat.st_mode & S_IFMT) == S_IFDIR ) + { + // Directory + // skip . and .. directories + if (dStrcmp(fEntry->d_name, ".") == 0 || dStrcmp(fEntry->d_name, "..") == 0) + continue; + + // skip excluded directories + if( Platform::isExcludedDirectory(fEntry->d_name)) + continue; + + + char child[MaxPath]; + dSprintf(child, sizeof(child), "%s/%s", path, fEntry->d_name); + char* childRelative = NULL; + char childRelativeBuf[MaxPath]; + if (relativePath) + { + dSprintf(childRelativeBuf, sizeof(childRelativeBuf), "%s/%s", + relativePath, fEntry->d_name); + childRelative = childRelativeBuf; + } + RecurseDumpPath(child, childRelative, pattern, fileVector); + } + else + { + // File + + // add it to the list + fileVector.increment(); + Platform::FileInfo& rInfo = fileVector.last(); + + if (relativePath) + rInfo.pFullPath = StringTable->insert(relativePath); + else + rInfo.pFullPath = StringTable->insert(path); + rInfo.pFileName = StringTable->insert(fEntry->d_name); + rInfo.fileSize = fStat.st_size; + //dPrintf("Adding file: %s/%s\n", rInfo.pFullPath, rInfo.pFileName); + } + + } while( (fEntry = readdir(directory)) != NULL ); + + closedir(directory); + return true; + } + + //----------------------------------------------------------------------------- + bool dFileDelete(const char * name) + { + return ModifyFile(name, DELETE); + } + + //----------------------------------------------------------------------------- + bool dFileTouch(const char * name) + { + return ModifyFile(name, TOUCH); + } + + bool dFileRename(const char *oldName, const char *newName) + { + AssertFatal( oldName != NULL && newName != NULL, "dFileRename - NULL file name" ); + + // only modify files in home directory + TempAlloc oldPrefPathName(MaxPath); + TempAlloc newPrefPathName(MaxPath); + MungePath(oldPrefPathName, MaxPath, oldName, GetPrefDir()); + MungePath(newPrefPathName, MaxPath, newName, GetPrefDir()); + + return rename(oldPrefPathName, newPrefPathName) == 0; + } + + //----------------------------------------------------------------------------- + // Constructors & Destructor + //----------------------------------------------------------------------------- + + //----------------------------------------------------------------------------- + // After construction, the currentStatus will be Closed and the capabilities + // will be 0. + //----------------------------------------------------------------------------- + File::File() + : currentStatus(Closed), capability(0) + { + // AssertFatal(sizeof(int) == sizeof(void *), "File::File: cannot cast void* to int"); + + handle = (void *)NULL; + } + + //----------------------------------------------------------------------------- + // insert a copy constructor here... (currently disabled) + //----------------------------------------------------------------------------- + + //----------------------------------------------------------------------------- + // Destructor + //----------------------------------------------------------------------------- + File::~File() + { + close(); + handle = (void *)NULL; + } + + //----------------------------------------------------------------------------- + // Open a file in the mode specified by openMode (Read, Write, or ReadWrite). + // Truncate the file if the mode is either Write or ReadWrite and truncate is + // true. + // + // Sets capability appropriate to the openMode. + // Returns the currentStatus of the file. + //----------------------------------------------------------------------------- + File::Status File::open(const char *filename, const AccessMode openMode) + { + AssertFatal(NULL != filename, "File::open: NULL filename"); + AssertWarn(NULL == handle, "File::open: handle already valid"); + + // Close the file if it was already open... + if (Closed != currentStatus) + close(); + + char prefPathName[MaxPath]; + char gamePathName[MaxPath]; + char cwd[MaxPath]; + getcwd(cwd, MaxPath); + MungePath(prefPathName, MaxPath, filename, GetPrefDir()); + MungePath(gamePathName, MaxPath, filename, cwd); + + int oflag; + struct stat filestat; + handle = (void *)dRealMalloc(sizeof(int)); + + switch (openMode) + { + case Read: + oflag = O_RDONLY; + break; + case Write: + oflag = O_WRONLY | O_CREAT | O_TRUNC; + break; + case ReadWrite: + oflag = O_RDWR | O_CREAT; + // if the file does not exist copy it before reading/writing + if (stat(prefPathName, &filestat) == -1) + bool ret = CopyFile(gamePathName, prefPathName); + break; + case WriteAppend: + oflag = O_WRONLY | O_CREAT | O_APPEND; + // if the file does not exist copy it before appending + if (stat(prefPathName, &filestat) == -1) + bool ret = CopyFile(gamePathName, prefPathName); + break; + default: + AssertFatal(false, "File::open: bad access mode"); // impossible + } + + // if we are writing, make sure output path exists + if (openMode == Write || openMode == ReadWrite || openMode == WriteAppend) + Platform::createPath(prefPathName); + + int fd = -1; + fd = x86UNIXOpen(prefPathName, oflag); + if (fd == -1 && openMode == Read) + // for read only files we can use the gamePathName + fd = x86UNIXOpen(gamePathName, oflag); + + dMemcpy(handle, &fd, sizeof(int)); + + #ifdef DEBUG + // fprintf(stdout,"fd = %d, handle = %d\n", fd, *((int *)handle)); + #endif + + if (*((int *)handle) == -1) + { + // handle not created successfully + Con::errorf("Can't open file: %s", filename); + return setStatus(); + } + else + { + // successfully created file, so set the file capabilities... + switch (openMode) + { + case Read: + capability = U32(FileRead); + break; + case Write: + case WriteAppend: + capability = U32(FileWrite); + break; + case ReadWrite: + capability = U32(FileRead) | + U32(FileWrite); + break; + default: + AssertFatal(false, "File::open: bad access mode"); + } + return currentStatus = Ok; // success! + } + } + + //----------------------------------------------------------------------------- + // Get the current position of the file pointer. + //----------------------------------------------------------------------------- + U32 File::getPosition() const + { + AssertFatal(Closed != currentStatus, "File::getPosition: file closed"); + AssertFatal(NULL != handle, "File::getPosition: invalid file handle"); + + #ifdef DEBUG + // fprintf(stdout, "handle = %d\n",*((int *)handle));fflush(stdout); + #endif + return (U32) lseek(*((int *)handle), 0, SEEK_CUR); + } + + //----------------------------------------------------------------------------- + // Set the position of the file pointer. + // Absolute and relative positioning is supported via the absolutePos + // parameter. + // + // If positioning absolutely, position MUST be positive - an IOError results if + // position is negative. + // Position can be negative if positioning relatively, however positioning + // before the start of the file is an IOError. + // + // Returns the currentStatus of the file. + //----------------------------------------------------------------------------- + File::Status File::setPosition(S32 position, bool absolutePos) + { + AssertFatal(Closed != currentStatus, "File::setPosition: file closed"); + AssertFatal(NULL != handle, "File::setPosition: invalid file handle"); + + if (Ok != currentStatus && EOS != currentStatus) + return currentStatus; + + U32 finalPos = 0; + switch (absolutePos) + { + case true: // absolute position + AssertFatal(0 <= position, "File::setPosition: negative absolute position"); + + // position beyond EOS is OK + finalPos = lseek(*((int *)handle), position, SEEK_SET); + break; + case false: // relative position + AssertFatal((getPosition() >= (U32)abs(position) && 0 > position) || 0 <= position, "File::setPosition: negative relative position"); + + // position beyond EOS is OK + finalPos = lseek(*((int *)handle), position, SEEK_CUR); + break; + } + + if (0xffffffff == finalPos) + return setStatus(); // unsuccessful + else if (finalPos >= getSize()) + return currentStatus = EOS; // success, at end of file + else + return currentStatus = Ok; // success! + } + + //----------------------------------------------------------------------------- + // Get the size of the file in bytes. + // It is an error to query the file size for a Closed file, or for one with an + // error status. + //----------------------------------------------------------------------------- + U32 File::getSize() const + { + AssertWarn(Closed != currentStatus, "File::getSize: file closed"); + AssertFatal(NULL != handle, "File::getSize: invalid file handle"); + + if (Ok == currentStatus || EOS == currentStatus) + { + long currentOffset = getPosition(); // keep track of our current position + long fileSize; + lseek(*((int *)handle), 0, SEEK_END); // seek to the end of the file + fileSize = getPosition(); // get the file size + lseek(*((int *)handle), currentOffset, SEEK_SET); // seek back to our old offset + return fileSize; // success! + } + else + return 0; // unsuccessful + } + + //----------------------------------------------------------------------------- + // Flush the file. + // It is an error to flush a read-only file. + // Returns the currentStatus of the file. + //----------------------------------------------------------------------------- + File::Status File::flush() + { + AssertFatal(Closed != currentStatus, "File::flush: file closed"); + AssertFatal(NULL != handle, "File::flush: invalid file handle"); + AssertFatal(true == hasCapability(FileWrite), "File::flush: cannot flush a read-only file"); + + if (fsync(*((int *)handle)) == 0) + return currentStatus = Ok; // success! + else + return setStatus(); // unsuccessful + } + + //----------------------------------------------------------------------------- + // Close the File. + // + // Returns the currentStatus + //----------------------------------------------------------------------------- + File::Status File::close() + { + // if the handle is non-NULL, close it if necessary and free it + if (NULL != handle) + { + // make a local copy of the handle value and + // free the handle + int handleVal = *((int *)handle); + dRealFree(handle); + handle = (void *)NULL; + + // close the handle if it is valid + if (handleVal != -1 && x86UNIXClose(handleVal) != 0) + return setStatus(); // unsuccessful + } + // Set the status to closed + return currentStatus = Closed; + } + + //----------------------------------------------------------------------------- + // Self-explanatory. + //----------------------------------------------------------------------------- + File::Status File::getStatus() const + { + return currentStatus; + } + + //----------------------------------------------------------------------------- + // Sets and returns the currentStatus when an error has been encountered. + //----------------------------------------------------------------------------- + File::Status File::setStatus() + { + Con::printf("File IO error: %s", strerror(errno)); + return currentStatus = IOError; + } + + //----------------------------------------------------------------------------- + // Sets and returns the currentStatus to status. + //----------------------------------------------------------------------------- + File::Status File::setStatus(File::Status status) + { + return currentStatus = status; + } + + //----------------------------------------------------------------------------- + // Read from a file. + // The number of bytes to read is passed in size, the data is returned in src. + // The number of bytes read is available in bytesRead if a non-Null pointer is + // provided. + //----------------------------------------------------------------------------- + File::Status File::read(U32 size, char *dst, U32 *bytesRead) + { + #ifdef DEBUG + // fprintf(stdout,"reading %d bytes\n",size);fflush(stdout); + #endif + AssertFatal(Closed != currentStatus, "File::read: file closed"); + AssertFatal(NULL != handle, "File::read: invalid file handle"); + AssertFatal(NULL != dst, "File::read: NULL destination pointer"); + AssertFatal(true == hasCapability(FileRead), "File::read: file lacks capability"); + AssertWarn(0 != size, "File::read: size of zero"); + + /* show stats for this file */ + #ifdef DEBUG + //struct stat st; + //fstat(*((int *)handle), &st); + //fprintf(stdout,"file size = %d\n", st.st_size); + #endif + /****************************/ + + if (Ok != currentStatus || 0 == size) + return currentStatus; + else + { + long lastBytes; + long *bytes = (NULL == bytesRead) ? &lastBytes : (long *)bytesRead; + if ( (*((U32 *)bytes) = x86UNIXRead(*((int *)handle), dst, size)) == -1) + { + #ifdef DEBUG + // fprintf(stdout,"unsuccessful: %d\n", *((U32 *)bytes));fflush(stdout); + #endif + return setStatus(); // unsuccessful + } else { + // dst[*((U32 *)bytes)] = '\0'; + if (*((U32 *)bytes) != size || *((U32 *)bytes) == 0) { + #ifdef DEBUG + // fprintf(stdout,"end of stream: %d\n", *((U32 *)bytes));fflush(stdout); + #endif + return currentStatus = EOS; // end of stream + } + } + } + // dst[*bytesRead] = '\0'; + #ifdef DEBUG + //fprintf(stdout, "We read:\n"); + //fprintf(stdout, "====================================================\n"); + //fprintf(stdout, "%s\n",dst); + //fprintf(stdout, "====================================================\n"); + //fprintf(stdout,"read ok: %d\n", *bytesRead);fflush(stdout); + #endif + return currentStatus = Ok; // successfully read size bytes + } + + //----------------------------------------------------------------------------- + // Write to a file. + // The number of bytes to write is passed in size, the data is passed in src. + // The number of bytes written is available in bytesWritten if a non-Null + // pointer is provided. + //----------------------------------------------------------------------------- + File::Status File::write(U32 size, const char *src, U32 *bytesWritten) + { + // JMQ: despite the U32 parameters, the maximum filesize supported by this + // function is probably the max value of S32, due to the unix syscall + // api. + AssertFatal(Closed != currentStatus, "File::write: file closed"); + AssertFatal(NULL != handle, "File::write: invalid file handle"); + AssertFatal(NULL != src, "File::write: NULL source pointer"); + AssertFatal(true == hasCapability(FileWrite), "File::write: file lacks capability"); + AssertWarn(0 != size, "File::write: size of zero"); + + if ((Ok != currentStatus && EOS != currentStatus) || 0 == size) + return currentStatus; + else + { + S32 numWritten = x86UNIXWrite(*((int *)handle), src, size); + if (numWritten < 0) + return setStatus(); + + if (bytesWritten) + *bytesWritten = static_cast(numWritten); + return currentStatus = Ok; + } + } + + //----------------------------------------------------------------------------- + // Self-explanatory. JMQ: No explanation needed. Move along. These aren't + // the droids you're looking for. + //----------------------------------------------------------------------------- + bool File::hasCapability(Capability cap) const + { + return (0 != (U32(cap) & capability)); + } + + //----------------------------------------------------------------------------- + S32 Platform::compareFileTimes(const FileTime &a, const FileTime &b) + { + if(a > b) + return 1; + if(a < b) + return -1; + return 0; + } + + //----------------------------------------------------------------------------- + static bool GetFileTimes(const char *filePath, FileTime *createTime, FileTime *modifyTime) + { + struct stat fStat; + + if (stat(filePath, &fStat) == -1) + return false; + + if(createTime) + { + // no where does SysV/BSD UNIX keep a record of a file's + // creation time. instead of creation time I'll just use + // changed time for now. + *createTime = fStat.st_ctime; + } + if(modifyTime) + { + *modifyTime = fStat.st_mtime; + } + + return true; + } + + //----------------------------------------------------------------------------- + bool Platform::getFileTimes(const char *filePath, FileTime *createTime, FileTime *modifyTime) + { + char pathName[MaxPath]; + + // if it starts with cwd, we need to strip that off so that we can look for + // the file in the pref dir + char cwd[MaxPath]; + getcwd(cwd, MaxPath); + if (dStrstr(filePath, cwd) == filePath) + filePath = filePath + dStrlen(cwd) + 1; + + // if its relative, first look in the pref dir + if (filePath[0] != '/' && filePath[0] != '\\') + { + MungePath(pathName, MaxPath, filePath, GetPrefDir()); + if (GetFileTimes(pathName, createTime, modifyTime)) + return true; + } + + // here if the path is absolute or not in the pref dir + MungePath(pathName, MaxPath, filePath, cwd); + return GetFileTimes(pathName, createTime, modifyTime); + } + + //----------------------------------------------------------------------------- + bool Platform::createPath(const char *file) + { + char pathbuf[MaxPath]; + const char *dir; + pathbuf[0] = 0; + U32 pathLen = 0; + + // all paths should be created in home directory + char prefPathName[MaxPath]; + MungePath(prefPathName, MaxPath, file, GetPrefDir()); + file = prefPathName; + + // does the directory exist already? + if (DirExists(prefPathName, true)) // true means that the path is a filepath + return true; + + while((dir = dStrchr(file, '/')) != NULL) + { + dStrncpy(pathbuf + pathLen, file, dir - file); + pathbuf[pathLen + dir-file] = 0; + bool ret = mkdir(pathbuf, 0700); + pathLen += dir - file; + pathbuf[pathLen++] = '/'; + file = dir + 1; + } + return true; + } + + // JMQ: Platform:cdFileExists in unimplemented + //------------------------------------------------------------------------------ + // bool Platform::cdFileExists(const char *filePath, const char *volumeName, + // S32 serialNum) + // { + // } + + //----------------------------------------------------------------------------- + bool Platform::dumpPath(const char *path, Vector &fileVector, int depth) + { + const char* pattern = "*"; + + // if it is not absolute, dump the pref dir first + if (path[0] != '/' && path[0] != '\\') + { + char prefPathName[MaxPath]; + MungePath(prefPathName, MaxPath, path, GetPrefDir()); + RecurseDumpPath(prefPathName, path, pattern, fileVector); + } + + // munge the requested path and dump it + char mungedPath[MaxPath]; + char cwd[MaxPath]; + getcwd(cwd, MaxPath); + MungePath(mungedPath, MaxPath, path, cwd); + return RecurseDumpPath(mungedPath, path, pattern, fileVector); + } + + //----------------------------------------------------------------------------- + StringTableEntry Platform::getCurrentDirectory() + { + char cwd_buf[2048]; + getcwd(cwd_buf, 2047); + return StringTable->insert(cwd_buf); + } + + //----------------------------------------------------------------------------- + bool Platform::setCurrentDirectory(StringTableEntry newDir) + { + if (Platform::getWebDeployment()) + return true; + + TempAlloc< UTF8 > buf( dStrlen( newDir ) + 2 ); + + dStrcpy( buf, newDir ); + + ForwardSlash( buf ); + return chdir( buf ) == 0; + } + + //----------------------------------------------------------------------------- + const char *Platform::getUserDataDirectory() + { + return StringTable->insert( GetPrefDir() ); + } + + //----------------------------------------------------------------------------- + StringTableEntry Platform::getUserHomeDirectory() + { + char *home = getenv( "HOME" ); + return StringTable->insert( home ); + } + + StringTableEntry Platform::getExecutablePath() +{ + if( !sBinPathName[0] ) + { + const char *cpath; + if( (cpath = torque_getexecutablepath()) ) + { + dStrncpy(sBinPathName, cpath, sizeof(sBinPathName)); + chdir(sBinPathName); + } + else + { + getcwd(sBinPathName, sizeof(sBinPathName)-1); + } + } + + return StringTable->insert(sBinPathName); +} + + //----------------------------------------------------------------------------- + bool Platform::isFile(const char *pFilePath) + { + if (!pFilePath || !*pFilePath) + return false; + // Get file info + struct stat fStat; + if (stat(pFilePath, &fStat) < 0) + return false; + + // if the file is a "regular file" then true + if ( (fStat.st_mode & S_IFMT) == S_IFREG) + return true; + // must be some other file (directory, device, etc.) + return false; + } + + //----------------------------------------------------------------------------- + S32 Platform::getFileSize(const char *pFilePath) + { + if (!pFilePath || !*pFilePath) + return -1; + // Get the file info + struct stat fStat; + if (stat(pFilePath, &fStat) < 0) + return -1; + // if the file is a "regular file" then return the size + if ( (fStat.st_mode & S_IFMT) == S_IFREG) + return fStat.st_size; + // Must be something else or we can't read the file. + return -1; + } + + //----------------------------------------------------------------------------- + bool Platform::isDirectory(const char *pDirPath) + { + if (!pDirPath || !*pDirPath) + return false; + + // Get file info + struct stat fStat; + if (stat(pDirPath, &fStat) < 0) + return false; + + // if the file is a Directory then true + if ( (fStat.st_mode & S_IFMT) == S_IFDIR) + return true; + + return false; + } + + //----------------------------------------------------------------------------- + bool Platform::isSubDirectory(const char *pParent, const char *pDir) + { + if (!pParent || !*pDir) + return false; + + // this is somewhat of a brute force method but we need to be 100% sure + // that the user cannot enter things like ../dir or /dir etc,... + DIR *directory; + + directory = opendir(pParent); + if (directory == NULL) + return false; + + struct dirent *fEntry; + fEntry = readdir(directory); + if ( fEntry == NULL ) + { + closedir(directory); + return false; + } + + do + { + char dirBuf[MaxPath]; + struct stat fStat; + + dSprintf(dirBuf, sizeof(dirBuf), "%s/%s", pParent, fEntry->d_name); + if (stat(dirBuf, &fStat) < 0) + continue; + // if it is a directory... + if ( (fStat.st_mode & S_IFMT) == S_IFDIR) + { + // and the names match + if (dStrcmp(pDir, fEntry->d_name ) == 0) + { + // then we have a real sub directory + closedir(directory); + return true; + } + } + } while( (fEntry = readdir(directory)) != NULL ); + + closedir(directory); + return false; + } + + //----------------------------------------------------------------------------- + + + // This is untested -- BJG + + bool Platform::fileTimeToString(FileTime * time, char * string, U32 strLen) + { + if(!time || !string) + return(false); + + dSprintf(string, strLen, "%ld", *time); + return(true); + } + + bool Platform::stringToFileTime(const char * string, FileTime * time) + { + if(!time || !string) + return(false); + + *time = dAtoi(string); + + return(true); + } + + bool Platform::hasSubDirectory(const char *pPath) + { + if (!pPath) + return false; + + struct dirent *d; + DIR *dip; + dip = opendir(pPath); + if (dip == NULL) + return false; + + while (d = readdir(dip)) + { + bool isDir = false; + if (d->d_type == DT_UNKNOWN) + { + char child [1024]; + if ((pPath[dStrlen(pPath) - 1] == '/')) + dSprintf(child, 1024, "%s%s", pPath, d->d_name); + else + dSprintf(child, 1024, "%s/%s", pPath, d->d_name); + isDir = Platform::isDirectory (child); + } + else if (d->d_type & DT_DIR) + isDir = true; + if( isDir ) + { + // Skip the . and .. directories + if (dStrcmp(d->d_name, ".") == 0 ||dStrcmp(d->d_name, "..") == 0) + continue; + if (Platform::isExcludedDirectory(d->d_name)) + continue; + Platform::clearExcludedDirectories(); + closedir(dip); + return true; + } + } + closedir(dip); + Platform::clearExcludedDirectories(); + return false; + } + + static bool recurseDumpDirectories(const char *basePath, const char *subPath, Vector &directoryVector, S32 currentDepth, S32 recurseDepth, bool noBasePath) + { + char Path[1024]; + DIR *dip; + struct dirent *d; + + if (subPath && (dStrncmp(subPath, "", 1) != 0)) + { + if ((basePath[dStrlen(basePath) - 1]) == '/') + dSprintf(Path, 1024, "%s%s", basePath, subPath); + else + dSprintf(Path, 1024, "%s/%s", basePath, subPath); + } + else + dSprintf(Path, 1024, "%s", basePath); + dip = opendir(Path); + if (dip == NULL) + return false; + ////////////////////////////////////////////////////////////////////////// + // add path to our return list ( provided it is valid ) + ////////////////////////////////////////////////////////////////////////// + if (!Platform::isExcludedDirectory(subPath)) + { + if (noBasePath) + { + // We have a path and it's not an empty string or an excluded directory + if ( (subPath && (dStrncmp (subPath, "", 1) != 0)) ) + directoryVector.push_back(StringTable->insert(subPath)); + } + else + { + if ( (subPath && (dStrncmp(subPath, "", 1) != 0)) ) + { + char szPath[1024]; + dMemset(szPath, 0, 1024); + if ( (basePath[dStrlen(basePath) - 1]) != '/') + dSprintf(szPath, 1024, "%s%s", basePath, subPath); + else + dSprintf(szPath, 1024, "%s%s", basePath, &subPath[1]); + directoryVector.push_back(StringTable->insert(szPath)); + } + else + directoryVector.push_back(StringTable->insert(basePath)); + } + } + ////////////////////////////////////////////////////////////////////////// + // Iterate through and grab valid directories + ////////////////////////////////////////////////////////////////////////// + + while (d = readdir(dip)) + { + bool isDir; + isDir = false; + if (d->d_type == DT_UNKNOWN) + { + char child [1024]; + if ((Path[dStrlen(Path) - 1] == '/')) + dSprintf(child, 1024, "%s%s", Path, d->d_name); + else + dSprintf(child, 1024, "%s/%s", Path, d->d_name); + isDir = Platform::isDirectory (child); + } + else if (d->d_type & DT_DIR) + isDir = true; + + if ( isDir ) + { + if (dStrcmp(d->d_name, ".") == 0 || + dStrcmp(d->d_name, "..") == 0) + continue; + if (Platform::isExcludedDirectory(d->d_name)) + continue; + if ( (subPath && (dStrncmp(subPath, "", 1) != 0)) ) + { + char child[1024]; + if ((subPath[dStrlen(subPath) - 1] == '/')) + dSprintf(child, 1024, "%s%s", subPath, d->d_name); + else + dSprintf(child, 1024, "%s/%s", subPath, d->d_name); + if (currentDepth < recurseDepth || recurseDepth == -1 ) + recurseDumpDirectories(basePath, child, directoryVector, + currentDepth + 1, recurseDepth, + noBasePath); + } + else + { + char child[1024]; + if ( (basePath[dStrlen(basePath) - 1]) == '/') + dStrcpy (child, d->d_name); + else + dSprintf(child, 1024, "/%s", d->d_name); + if (currentDepth < recurseDepth || recurseDepth == -1) + recurseDumpDirectories(basePath, child, directoryVector, + currentDepth + 1, recurseDepth, + noBasePath); + } + } + } + closedir(dip); + return true; + } + + bool Platform::dumpDirectories(const char *path, Vector &directoryVector, S32 depth, bool noBasePath) + { + bool retVal = recurseDumpDirectories(path, "", directoryVector, 0, depth, noBasePath); + clearExcludedDirectories(); + return retVal; + } + +StringTableEntry Platform::getExecutableName() +{ + return StringTable->insert(sBinName); +} + +extern "C" +{ + void setExePathName(const char* exePathName) + { + if (exePathName == NULL) + sBinPathName[0] = '\0'; + else + dStrncpy(sBinPathName, exePathName, sizeof(sBinPathName)); + + // set the base exe name field + char *binName = dStrrchr(sBinPathName, '/'); + if( !binName ) + binName = sBinPathName; + else + *binName++ = '\0'; + sBinName = binName; + } +} diff --git a/Engine/source/platformX86UNIX/x86UNIXFont.client.cpp b/Engine/source/platformX86UNIX/x86UNIXFont.client.cpp new file mode 100644 index 000000000..94781d54a --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXFont.client.cpp @@ -0,0 +1,348 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/gFont.h" +#include "gfx/bitmap/gBitmap.h" +#include "math/mRect.h" +#include "console/console.h" +#include "core/strings/unicode.h" +#include "core/stringTable.h" +#include "platformX86UNIX/platformX86UNIX.h" +#include "platformX86UNIX/x86UNIXFont.h" + +// Needed by createFont +#include +#include +#include +#include +#include +#include // For XRenderColor + +// Needed for getenv in createFont +#include +XftFont *loadFont(const char *name, S32 size, Display *display) +{ + XftFont *fontInfo = NULL; + char* fontname = const_cast(name); + if (dStrlen(fontname)==0) + fontname = "arial"; + else if (stristr(const_cast(name), "arial") != NULL) + fontname = "arial"; + else if (stristr(const_cast(name), "lucida console") != NULL) + fontname = "lucida console"; + + char* weight = "medium"; + char* slant = "roman"; // no slant + + if (stristr(const_cast(name), "bold") != NULL) + weight = "bold"; + if (stristr(const_cast(name), "italic") != NULL) + slant = "italic"; + + int mSize = size - 2 - (int)((float)size * 0.1); + char xfontName[512]; + // We specify a lower DPI to get 'correct' looking fonts, if we go with the + // native DPI the fonts are to big and don't fit the widgets. + dSprintf(xfontName, 512, "%s-%d:%s:slant=%s:dpi=76", fontname, mSize, weight, slant); + + // Lets see if Xft can get a font for us. + char xftname[1024]; + fontInfo = XftFontOpenName(display, DefaultScreen(display), xfontName); + // Cant find a suitabke font, default to a known font (6x10) + if ( !fontInfo ) + { + dSprintf(xfontName, 512, "6x10-%d:%s:slant=%s:dpi=76", mSize, weight, slant); + fontInfo = XftFontOpenName(display, DefaultScreen(display), xfontName); + } + XftNameUnparse(fontInfo->pattern, xftname, 1024); + +#ifdef DEBUG + Con::printf("Font '%s %d' mapped to '%s'\n", name, size, xftname); +#endif + + return fontInfo; +} + + +//GOldFont* createFont(const char *name, dsize_t size, U32 charset) +//{ +// Display *display = XOpenDisplay(getenv("DISPLAY")); +// int screen; +// +// if (!display) +// AssertFatal(false, "createFont: cannot connect to X server"); +// screen = DefaultScreen(display); +// +// XftFont *font = loadFont (name, size, display); +// if (!font) // This should almost never trigger anymore. +// AssertFatal(false, "createFont: cannot load font"); +// +// // Create the pixmap to draw on. +// Pixmap pixmap = XCreatePixmap(display, +// DefaultRootWindow(display), +// font->max_advance_width, +// font->height, +// DefaultDepth(display, screen)); +// // And the Xft wrapper around it. +// XftDraw *draw = XftDrawCreate(display, +// pixmap, +// DefaultVisual(display, screen), +// DefaultColormap(display, screen)); +// // Allocate some colors, we don't use XftColorAllocValue here as that +// // Don't appear to function correctly (or I'm using it wrong) As we only do +// // this twice per new un cached font it isn't that big of a penalty. (Each +// // call to XftColorAllocName involves a round trip to the X Server) +// XftColor black, white; +// XftColorAllocName(display, +// DefaultVisual(display, screen), +// DefaultColormap(display, screen), +// "black", +// &black); +// // White +// XftColorAllocName(display, +// DefaultVisual(display, screen), +// DefaultColormap(display, screen), +// "white", +// &white); +// +// // The font. +// GOldFont *retFont = new GOldFont; +// static U8 scratchPad[65536]; +// int x, y; +// // insert bitmaps into the font for each character +// for(U16 i = 32; i < 256; i++) +// { +// XGlyphInfo extent; +// FT_UInt glyph; +// if (!XftCharExists(display, font, i)) +// { +// retFont->insertBitmap(i, scratchPad, 0, 0, 0, 0, 0, font->max_advance_width); +// continue; +// } +// // Get the glyph and its extents. +// glyph = XftCharIndex(display, font, i); +// XftGlyphExtents (display, font, &glyph, 1, &extent); +// // Clear the bounding box and draw the glyph +// XftDrawRect (draw, &black, 0, 0, font->max_advance_width, font->height); +// XftDrawGlyphs (draw, &white, font, 0, font->ascent, &glyph, 1); +// // Grab the rendered image ... +// XImage *ximage = XGetImage(display, pixmap, 0, 0, +// extent.xOff, font->height, +// AllPlanes, XYPixmap); +// if (ximage == NULL) +// AssertFatal(false, "cannot get x image"); +// // And store each pixel in the scratchPad for insertion into the bitmap. +// // We grab the full height of the pixmap. +// for(y = 0; y < font->height; y++) +// { +// // and the width of the glyph and its padding. +// for(x = 0; x < extent.xOff; x++) +// scratchPad[y * extent.xOff + x] = static_cast(XGetPixel(ximage, x, y)); +// } +// // Done with the image. +// XDestroyImage(ximage); +// // Add it to the bitmap. +// retFont->insertBitmap(i, // index +// scratchPad, // src +// extent.xOff, // stride +// extent.xOff, // width +// font->height, // height +// 0, // xOrigin +// font->ascent, // yOrigin +// extent.xOff); // xIncrement +// +// } +// retFont->pack(font->height, font->ascent); +// XftFontClose(display, font); +// +// XftColorFree(display, DefaultVisual(display, screen), +// DefaultColormap(display, screen), &black); +// XftColorFree(display, DefaultVisual(display, screen), +// DefaultColormap(display, screen), &white); +// XftDrawDestroy(draw); +// XFreePixmap(display, pixmap); +// XCloseDisplay(display); +// return retFont; +//} + + +// XA: New class for the unix unicode font +PlatformFont *createPlatformFont(const char *name, U32 size, U32 charset /* = TGE_ANSI_CHARSET */) +{ + PlatformFont *retFont = new x86UNIXFont; + + if(retFont->create(name, size, charset)) + return retFont; + + delete retFont; + return NULL; +} + +x86UNIXFont::x86UNIXFont() +{} + +x86UNIXFont::~x86UNIXFont() +{} + + +bool x86UNIXFont::create(const char *name, U32 size, U32 charset) +{ + Display *display = XOpenDisplay(getenv("DISPLAY")); + if (display == NULL) + AssertFatal(false, "createFont: cannot connect to X server"); + + XftFont *font = loadFont(name, size, display); + + if (!font) + { + Con::errorf("Error: Could not load font -%s-", name); + return false; + } + char xfontname[1024]; + XftNameUnparse(font->pattern, xfontname, 1024); +#ifdef DEBUG + Con::printf("CreateFont: request for %s %d, using %s", name, size, xfontname); +#endif + // store some info about the font + baseline = font->ascent; + height = font->height; + mFontName = StringTable->insert(xfontname); + XftFontClose (display, font); + // DISPLAY + XCloseDisplay(display); + + return true; +} + +bool x86UNIXFont::isValidChar(const UTF16 str) const +{ + // 0x20 == 32 + // 0x100 == 256 + if( str < 0x20 || str > 0x100 ) + return false; + + return true; +} + +bool x86UNIXFont::isValidChar(const UTF8 *str) const +{ + + return isValidChar(oneUTF32toUTF16(oneUTF8toUTF32(str,NULL))); +} + +PlatformFont::CharInfo &x86UNIXFont::getCharInfo(const UTF16 ch) const +{ + Display *display = XOpenDisplay(getenv("DISPLAY")); + if (!display ) + AssertFatal(false, "createFont: cannot connect to X server"); + + static PlatformFont::CharInfo c; + dMemset(&c, 0, sizeof(c)); + c.bitmapIndex = 0; + c.xOffset = 0; + c.yOffset = 0; + + XftFont *fontInfo = XftFontOpenName(display, DefaultScreen(display), mFontName); + if (!fontInfo) + AssertFatal(false, "createFont: cannot load font"); + + int screen = DefaultScreen(display); + // Create the pixmap to draw on. + Drawable pixmap = XCreatePixmap(display, + DefaultRootWindow(display), + fontInfo->max_advance_width, + fontInfo->height, + DefaultDepth(display, screen)); + // And the Xft wrapper around it. + XftDraw *draw = XftDrawCreate(display, + pixmap, + DefaultVisual(display, screen), + DefaultColormap(display, screen)); + // Allocate some colors, we don't use XftColorAllocValue here as that + // Don't appear to function correctly (or I'm using it wrong) As we only do + // this twice per new un cached font it isn't that big of a penalty. (Each + // call to XftColorAllocName involves a round trip to the X Server) + XftColor black, white; + XftColorAllocName(display, + DefaultVisual(display, screen), + DefaultColormap(display, screen), + "black", + &black); + // White + XftColorAllocName(display, + DefaultVisual(display, screen), + DefaultColormap(display, screen), + "white", + &white); + + XGlyphInfo charinfo; + XftTextExtents16(display, fontInfo, &ch, 1, &charinfo); + c.height = fontInfo->height; + c.xOrigin = 0; + c.yOrigin = fontInfo->ascent; + c.xIncrement = charinfo.xOff; + c.width = charinfo.xOff; + // kick out early if the character is undrawable + if( c.width == 0 || c.height == 0) + return c; + + // allocate a greyscale bitmap and clear it. + int bitmapDataSize = c.width * c.height; + c.bitmapData = new U8[bitmapDataSize]; + dMemset(c.bitmapData, 0, bitmapDataSize); + + XftDrawRect (draw, &black, 0, 0, fontInfo->max_advance_width, fontInfo->height); + XftDrawString16 (draw, &white, fontInfo, 0, fontInfo->ascent, &ch, 1); + // grab the pixmap image + + XImage *ximage = XGetImage(display, pixmap, 0, 0, + charinfo.xOff, fontInfo->height, + AllPlanes, XYPixmap); + if (!ximage) + AssertFatal(false, "cannot get x image"); + int x, y; + + // grab each pixel and store it in the scratchPad + for(y = 0; y < fontInfo->height; y++) + { + for(x = 0; x < charinfo.xOff; x++) + c.bitmapData[y * charinfo.xOff + x] = static_cast(XGetPixel(ximage, x, y)); + } + XDestroyImage(ximage); + + XftColorFree(display, DefaultVisual(display, screen), + DefaultColormap(display, screen), &black); + XftColorFree(display, DefaultVisual(display, screen), + DefaultColormap(display, screen), &white); + XftDrawDestroy(draw); + XFreePixmap(display, pixmap); + XCloseDisplay(display); + + return c; +} + + +PlatformFont::CharInfo &x86UNIXFont::getCharInfo(const UTF8 *str) const +{ + return getCharInfo(oneUTF32toUTF16(oneUTF8toUTF32(str,NULL))); +} + diff --git a/Engine/source/platformX86UNIX/x86UNIXFont.h b/Engine/source/platformX86UNIX/x86UNIXFont.h new file mode 100644 index 000000000..018b477d8 --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXFont.h @@ -0,0 +1,66 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PLATFORMFONT_H_ +#include "platform/platformFont.h" +#include "platform/platform.h" +#endif + +#ifndef _X86UNIXFONT_H_ +#define _X86UNIXFONT_H_ +// Needed by createFont +#include +#include +#include +#include + +class x86UNIXFont : public PlatformFont +{ + private: + int baseline; + int height; + StringTableEntry mFontName; + public: + x86UNIXFont(); + virtual ~x86UNIXFont(); + + // PlatformFont virtual methods + virtual bool isValidChar(const UTF16 ch) const; + virtual bool isValidChar(const UTF8 *str) const; + + inline U32 getFontHeight() const + { + return height; + } + + inline U32 getFontBaseLine() const + { + return baseline; + } + + virtual PlatformFont::CharInfo &getCharInfo(const UTF16 ch) const; + virtual PlatformFont::CharInfo &getCharInfo(const UTF8 *str) const; + + virtual bool create(const char *name, dsize_t size, U32 charset = TGE_ANSI_CHARSET); +}; + +#endif diff --git a/Engine/source/platformX86UNIX/x86UNIXGL.client.cpp b/Engine/source/platformX86UNIX/x86UNIXGL.client.cpp new file mode 100644 index 000000000..ff70c247e --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXGL.client.cpp @@ -0,0 +1,391 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformX86UNIX/platformGL.h" +#include "platformX86UNIX/platformX86UNIX.h" +#include "console/console.h" + +#include +#include + +// declare stub functions +#define GL_FUNCTION(fn_return, fn_name, fn_args, fn_value) fn_return stub_##fn_name fn_args{ fn_value } +#include "platform/GLCoreFunc.h" +#include "platform/GLExtFunc.h" +#undef GL_FUNCTION + +// point gl function pointers at stub functions +#define GL_FUNCTION(fn_return,fn_name,fn_args, fn_value) fn_return (*fn_name)fn_args = stub_##fn_name; +#include "platform/GLCoreFunc.h" +#include "platform/GLExtFunc.h" +#undef GL_FUNCTION + +static void* dlHandle = NULL; + +//------------------------------------------------------------------ +//bind functions for each function prototype +//------------------------------------------------------------------ + +//GL_EXT/ARB +enum { + ARB_multitexture = BIT(0), + ARB_texture_compression = BIT(1), + EXT_compiled_vertex_array = BIT(2), + EXT_fog_coord = BIT(3), + EXT_paletted_texture = BIT(4), + NV_vertex_array_range = BIT(5), + EXT_blend_color = BIT(6), + EXT_blend_minmax = BIT(7) +}; + +//WGL_ARB +enum { + WGL_ARB_extensions_string = BIT(0), + WGL_EXT_swap_control = BIT(1), + WGL_3DFX_gamma_control = BIT(2) +}; + + +static bool isFnOk( const char *name) +{ + bool ok = false; + + // JMQ: these are specific to torque's d3d->gl wrapper. They are not used under linux. + if (dStrcmp(name, "glAvailableVertexBufferEXT")==0) + ok = true; + else if (dStrcmp(name, "glAllocateVertexBufferEXT")==0) + ok = true; + else if (dStrcmp(name, "glLockVertexBufferEXT")==0) + ok = true; + else if (dStrcmp(name, "glUnlockVertexBufferEXT")==0) + ok = true; + else if (dStrcmp(name, "glSetVertexBufferEXT")==0) + ok = true; + else if (dStrcmp(name, "glOffsetVertexBufferEXT")==0) + ok = true; + else if (dStrcmp(name, "glFillVertexBufferEXT")==0) + ok = true; + else if (dStrcmp(name, "glFreeVertexBufferEXT")==0) + ok = true; + + return ok; +} + +//------------------------------------------------------------------ +//bind functions for each function prototype +//------------------------------------------------------------------ +static bool bindGLFunction( void *&fnAddress, const char *name ) +{ + void* addr = (void*)SDL_GL_GetProcAddress(name); + bool ok = (bool)addr; + if( !ok ) + { + if (!isFnOk(name)) + Con::errorf(ConsoleLogEntry::General, " Missing OpenGL function '%s'", name); + else + ok = true; + } + else + fnAddress = addr; + return ok; +} + +static bool bindEXTFunction( void *&fnAddress, const char *name ) +{ + void* addr = (void*)SDL_GL_GetProcAddress(name); + if( !addr ) + Con::errorf(ConsoleLogEntry::General, " Missing OpenGL extension '%s'", name); + else + fnAddress = addr; + return (addr != NULL); +} + +//------------------------------------------------------------------ +//binds for each function group +//------------------------------------------------------------------ +static bool bindGLFunctions() +{ + bool result = true; + #define GL_FUNCTION(fn_return, fn_name, fn_args, fn_value) \ + result &= bindGLFunction( *(void**)&fn_name, #fn_name); + #include "platform/GLCoreFunc.h" + #undef GL_FUNCTION + return result; +} + +static bool bindEXTFunctions(U32 extMask) +{ + bool result = true; + + #define GL_GROUP_BEGIN( flag ) \ + if( extMask & flag ) { + #define GL_GROUP_END() } + + #define GL_FUNCTION(fn_return, fn_name, fn_args, fn_value) \ + result &= bindEXTFunction( *(void**)&fn_name, #fn_name); + #include "platform/GLExtFunc.h" + #undef GL_FUNCTION + + #undef GL_GROUP_BEGIN + #undef GL_GROUP_END + + return result; +} + +static void unbindGLFunctions() +{ + // point gl function pointers at stub functions +#define GL_FUNCTION(fn_return, fn_name, fn_args, fn_value) fn_name = stub_##fn_name; +#include "platform/GLCoreFunc.h" +#include "platform/GLExtFunc.h" +#undef GL_FUNCTION +} + +namespace GLLoader +{ + + bool OpenGLInit() + { + return OpenGLDLLInit(); + } + + void OpenGLShutdown() + { + OpenGLDLLShutdown(); + } + + bool OpenGLDLLInit() + { + OpenGLDLLShutdown(); + + // load libGL.so + if (SDL_GL_LoadLibrary("libGL.so") == -1 && + SDL_GL_LoadLibrary("libGL.so.1") == -1) + { + Con::errorf("Error loading GL library: %s", SDL_GetError()); + return false; + } + + // bind functions + if (!bindGLFunctions()) + { + Con::errorf("Error binding GL functions"); + OpenGLDLLShutdown(); + return false; + } + + return true; + } + + void OpenGLDLLShutdown() + { + // there is no way to tell SDL to unload the library + if (dlHandle != NULL) + { + dlclose(dlHandle); + dlHandle = NULL; + } + + unbindGLFunctions(); + } + +} + +GLState gGLState; + +bool gOpenGLDisablePT = false; +bool gOpenGLDisableCVA = false; +bool gOpenGLDisableTEC = false; +bool gOpenGLDisableARBMT = false; +bool gOpenGLDisableFC = false; +bool gOpenGLDisableTCompress = false; +bool gOpenGLNoEnvColor = false; +float gOpenGLGammaCorrection = 0.5; +bool gOpenGLNoDrawArraysAlpha = false; + +// JMQTODO: really need a platform-shared version of this nastiness +bool GL_EXT_Init( ) +{ + // Load extensions... + // + const char* pExtString = reinterpret_cast(glGetString(GL_EXTENSIONS)); + gGLState.primMode = 0; + U32 extBitMask = 0; + + // GL_EXT_paletted_texture + if (pExtString && dStrstr(pExtString, (const char*)"GL_EXT_paletted_texture") != NULL) + { + extBitMask |= EXT_paletted_texture; + gGLState.suppPalettedTexture = true; + } + else + gGLState.suppPalettedTexture = false; + + // EXT_compiled_vertex_array + if (pExtString && dStrstr(pExtString, (const char*)"GL_EXT_compiled_vertex_array") != NULL) + { + extBitMask |= EXT_compiled_vertex_array; + gGLState.suppLockedArrays = true; + } + else + { + gGLState.suppLockedArrays = false; + } + + // ARB_multitexture + if (pExtString && dStrstr(pExtString, (const char*)"GL_ARB_multitexture") != NULL) + { + extBitMask |= ARB_multitexture; + gGLState.suppARBMultitexture = true; + } else { + gGLState.suppARBMultitexture = false; + } + + // EXT_blend_color + if(pExtString && dStrstr(pExtString, (const char*)"GL_EXT_blend_color") != NULL) + { + extBitMask |= EXT_blend_color; + gGLState.suppEXTblendcolor = true; + } else { + gGLState.suppEXTblendcolor = false; + } + + // EXT_blend_minmax + if(pExtString && dStrstr(pExtString, (const char*)"GL_EXT_blend_minmax") != NULL) + { + extBitMask |= EXT_blend_color; + gGLState.suppEXTblendminmax = true; + } else { + gGLState.suppEXTblendminmax = false; + } + + // EXT_fog_coord + if (pExtString && dStrstr(pExtString, (const char*)"GL_EXT_fog_coord") != NULL) + { + extBitMask |= EXT_fog_coord; + gGLState.suppFogCoord = true; + } else { + gGLState.suppFogCoord = false; + } + + // EXT_texture_compression_s3tc + if (pExtString && dStrstr(pExtString, (const char*)"GL_EXT_texture_compression_s3tc") != NULL) + gGLState.suppS3TC = true; + else + gGLState.suppS3TC = false; + + // ARB_texture_compression + if (pExtString && dStrstr(pExtString, (const char*)"GL_ARB_texture_compression") != NULL) + { + extBitMask |= ARB_texture_compression; + gGLState.suppTextureCompression = true; + } else { + gGLState.suppTextureCompression = false; + } + + // NV_vertex_array_range (not on *nix) + gGLState.suppVertexArrayRange = false; + + // 3DFX_texture_compression_FXT1 + if (pExtString && dStrstr(pExtString, (const char*)"3DFX_texture_compression_FXT1") != NULL) + gGLState.suppFXT1 = true; + else + gGLState.suppFXT1 = false; + + if (!bindEXTFunctions(extBitMask)) + Con::warnf("You are missing some OpenGL Extensions. You may experience rendering problems."); + + // Binary states, i.e., no supporting functions + // EXT_packed_pixels + // EXT_texture_env_combine + // + // dhc note: a number of these can have multiple matching 'versions', private, ext, and arb. + gGLState.suppPackedPixels = pExtString? (dStrstr(pExtString, (const char*)"GL_EXT_packed_pixels") != NULL) : false; + gGLState.suppTextureEnvCombine = pExtString? (dStrstr(pExtString, (const char*)"GL_EXT_texture_env_combine") != NULL) : false; + gGLState.suppEdgeClamp = pExtString? (dStrstr(pExtString, (const char*)"GL_EXT_texture_edge_clamp") != NULL) : false; + gGLState.suppEdgeClamp |= pExtString? (dStrstr(pExtString, (const char*)"GL_SGIS_texture_edge_clamp") != NULL) : false; + gGLState.suppTexEnvAdd = pExtString? (dStrstr(pExtString, (const char*)"GL_ARB_texture_env_add") != NULL) : false; + gGLState.suppTexEnvAdd |= pExtString? (dStrstr(pExtString, (const char*)"GL_EXT_texture_env_add") != NULL) : false; + + // Anisotropic filtering + gGLState.suppTexAnisotropic = pExtString? (dStrstr(pExtString, (const char*)"GL_EXT_texture_filter_anisotropic") != NULL) : false; + if (gGLState.suppTexAnisotropic) + glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gGLState.maxAnisotropy); + if (gGLState.suppARBMultitexture) + glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &gGLState.maxTextureUnits); + else + gGLState.maxTextureUnits = 1; + + // JMQ: vsync/swap interval skipped + gGLState.suppSwapInterval = false; + + Con::printf("OpenGL Init: Enabled Extensions"); + if (gGLState.suppARBMultitexture) Con::printf(" ARB_multitexture (Max Texture Units: %d)", gGLState.maxTextureUnits); + if (gGLState.suppEXTblendcolor) Con::printf(" EXT_blend_color"); + if (gGLState.suppEXTblendminmax) Con::printf(" EXT_blend_minmax"); + if (gGLState.suppPalettedTexture) Con::printf(" EXT_paletted_texture"); + if (gGLState.suppLockedArrays) Con::printf(" EXT_compiled_vertex_array"); + if (gGLState.suppVertexArrayRange) Con::printf(" NV_vertex_array_range"); + if (gGLState.suppTextureEnvCombine) Con::printf(" EXT_texture_env_combine"); + if (gGLState.suppPackedPixels) Con::printf(" EXT_packed_pixels"); + if (gGLState.suppFogCoord) Con::printf(" EXT_fog_coord"); + if (gGLState.suppTextureCompression) Con::printf(" ARB_texture_compression"); + if (gGLState.suppS3TC) Con::printf(" EXT_texture_compression_s3tc"); + if (gGLState.suppFXT1) Con::printf(" 3DFX_texture_compression_FXT1"); + if (gGLState.suppTexEnvAdd) Con::printf(" (ARB|EXT)_texture_env_add"); + if (gGLState.suppTexAnisotropic) Con::printf(" EXT_texture_filter_anisotropic (Max anisotropy: %f)", gGLState.maxAnisotropy); + if (gGLState.suppSwapInterval) Con::printf(" WGL_EXT_swap_control"); + + Con::warnf("OpenGL Init: Disabled Extensions"); + if (!gGLState.suppARBMultitexture) Con::warnf(" ARB_multitexture"); + if (!gGLState.suppEXTblendcolor) Con::warnf(" EXT_blend_color"); + if (!gGLState.suppEXTblendminmax) Con::warnf(" EXT_blend_minmax"); + if (!gGLState.suppPalettedTexture) Con::warnf(" EXT_paletted_texture"); + if (!gGLState.suppLockedArrays) Con::warnf(" EXT_compiled_vertex_array"); + if (!gGLState.suppVertexArrayRange) Con::warnf(" NV_vertex_array_range"); + if (!gGLState.suppTextureEnvCombine) Con::warnf(" EXT_texture_env_combine"); + if (!gGLState.suppPackedPixels) Con::warnf(" EXT_packed_pixels"); + if (!gGLState.suppFogCoord) Con::warnf(" EXT_fog_coord"); + if (!gGLState.suppTextureCompression) Con::warnf(" ARB_texture_compression"); + if (!gGLState.suppS3TC) Con::warnf(" EXT_texture_compression_s3tc"); + if (!gGLState.suppFXT1) Con::warnf(" 3DFX_texture_compression_FXT1"); + if (!gGLState.suppTexEnvAdd) Con::warnf(" (ARB|EXT)_texture_env_add"); + if (!gGLState.suppTexAnisotropic) Con::warnf(" EXT_texture_filter_anisotropic"); + if (!gGLState.suppSwapInterval) Con::warnf(" WGL_EXT_swap_control"); + Con::printf(" "); + + // Set some console variables: + Con::setBoolVariable( "$FogCoordSupported", gGLState.suppFogCoord ); + Con::setBoolVariable( "$TextureCompressionSupported", gGLState.suppTextureCompression ); + Con::setBoolVariable( "$AnisotropySupported", gGLState.suppTexAnisotropic ); + Con::setBoolVariable( "$PalettedTextureSupported", gGLState.suppPalettedTexture ); + Con::setBoolVariable( "$SwapIntervalSupported", gGLState.suppSwapInterval ); + + if (!gGLState.suppPalettedTexture && Con::getBoolVariable("$pref::OpenGL::forcePalettedTexture",false)) + { + Con::setBoolVariable("$pref::OpenGL::forcePalettedTexture", false); + Con::setBoolVariable("$pref::OpenGL::force16BitTexture", true); + } + + return true; +} + diff --git a/Engine/source/platformX86UNIX/x86UNIXIO.cpp b/Engine/source/platformX86UNIX/x86UNIXIO.cpp new file mode 100644 index 000000000..975b9a9f5 --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXIO.cpp @@ -0,0 +1,57 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformX86UNIX/platformX86UNIX.h" +#include "core/volume.h" + +#include +#include +#include +#include +#include +#include +#include + +int x86UNIXOpen(const char *path, int oflag) +{ + return open(path, oflag, 0666); +} + +int x86UNIXClose(int fd) +{ + return close(fd); +} + +ssize_t x86UNIXRead(int fd, void *buf, size_t nbytes) +{ + return read(fd, buf, nbytes); +} + +ssize_t x86UNIXWrite(int fd, const void *buf, size_t nbytes) +{ + return write(fd, buf, nbytes); +} + +bool Torque::FS::VerifyWriteAccess(const Torque::Path &path) +{ + return true; +} diff --git a/Engine/source/platformX86UNIX/x86UNIXInput.client.cpp b/Engine/source/platformX86UNIX/x86UNIXInput.client.cpp new file mode 100644 index 000000000..0db1d7a0f --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXInput.client.cpp @@ -0,0 +1,604 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformX86UNIX/platformX86UNIX.h" +#include "platform/platformInput.h" +#include "platform/platformVideo.h" +#include "platform/event.h" +#include "platform/gameInterface.h" +#include "console/console.h" +#include "platformX86UNIX/x86UNIXState.h" +#include "platformX86UNIX/x86UNIXInputManager.h" + +#include +#include +#include + +#include + +#ifdef LOG_INPUT +#include +#include +#include +#include + +extern int x86UNIXOpen(const char *path, int oflag); +extern int x86UNIXClose(int fd); +extern ssize_t x86UNIXWrite(int fd, const void *buf, size_t nbytes); +#endif + +class XClipboard +{ + private: + Atom mClipboardProperty; + Atom mClipboard; + Atom mPrimary; + bool mInitialized; + U8 *mXData; + char *mTData; + S32 mTDataSize; + + void init(); + void freeXData(); + void freeTData(); + void checkTDataSize(S32 requestedSize); + public: + XClipboard(); + ~XClipboard(); + + bool setClipboard(const char *text); + const char* getClipboard(); + void handleSelectionRequest(XSelectionRequestEvent& request); +}; + +// Static class variables: +InputManager* Input::smManager; + +// smActive is not maintained under unix. Use Input::isActive() +// instead +bool Input::smActive = false; + +// unix platform state +extern x86UNIXPlatformState * x86UNIXState; + +extern AsciiData AsciiTable[NUM_KEYS]; + +static XClipboard xclipboard; + +#ifdef LOG_INPUT +S32 gInputLog = -1; +#endif + +//------------------------------------------------------------------------------ +void Input::init() +{ + Con::printf( "Input Init:" ); + + destroy(); + +#ifdef LOG_INPUT + struct tm* newTime; + time_t aclock; + time( &aclock ); + newTime = localtime( &aclock ); + asctime( newTime ); + + gInputLog = x86UNIXOpen("input.log", O_WRONLY | O_CREAT); + log("Input log opened at %s\n", asctime( newTime ) ); + log("Operating System:\n" ); + log(" %s", UUtils->getOSName()); + log("\n"); +#endif + + smActive = false; + smManager = NULL; + + UInputManager *uInputManager = new UInputManager; + if ( !uInputManager->enable() ) + { + Con::errorf( " Failed to enable Input Manager." ); + delete uInputManager; + return; + } + + uInputManager->init(); + + smManager = uInputManager; + + Con::printf(" Input initialized"); + Con::printf(" "); +} + +//------------------------------------------------------------------------------ +ConsoleFunction( isJoystickDetected, bool, 1, 1, "isJoystickDetected()" ) +{ + argc; argv; + UInputManager* manager = dynamic_cast(Input::getManager()); + if (manager) + return manager->joystickDetected(); + else + return false; +} + +//------------------------------------------------------------------------------ +ConsoleFunction( getJoystickAxes, const char*, 2, 2, "getJoystickAxes( instance )" ) +{ + argc; argv; + UInputManager* manager = dynamic_cast(Input::getManager()); + if (manager) + return manager->getJoystickAxesString(dAtoi(argv[1])); + else + return ""; +} + +//------------------------------------------------------------------------------ +U16 Input::getKeyCode( U16 asciiCode ) +{ + U16 keyCode = 0; + U16 i; + + // This is done three times so the lowerkey will always + // be found first. Some foreign keyboards have duplicate + // chars on some keys. + for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ ) + { + if ( AsciiTable[i].lower.ascii == asciiCode ) + { + keyCode = i; + break; + }; + } + + for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ ) + { + if ( AsciiTable[i].upper.ascii == asciiCode ) + { + keyCode = i; + break; + }; + } + + for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ ) + { + if ( AsciiTable[i].goofy.ascii == asciiCode ) + { + keyCode = i; + break; + }; + } + + return( keyCode ); +} + +//----------------------------------------------------------------------------- +// +// This function gets the standard ASCII code corresponding to our key code +// and the existing modifier key state. +// +//----------------------------------------------------------------------------- +U16 Input::getAscii( U16 keyCode, KEY_STATE keyState ) +{ + if ( keyCode >= NUM_KEYS ) + return 0; + + switch ( keyState ) + { + case STATE_LOWER: + return AsciiTable[keyCode].lower.ascii; + case STATE_UPPER: + return AsciiTable[keyCode].upper.ascii; + case STATE_GOOFY: + return AsciiTable[keyCode].goofy.ascii; + default: + return(0); + + } +} + +//------------------------------------------------------------------------------ +void Input::destroy() +{ +#ifdef LOG_INPUT + if ( gInputLog != -1 ) + { + log( "*** CLOSING LOG ***\n" ); + x86UNIXClose(gInputLog); + gInputLog = -1; + } +#endif + + if ( smManager && smManager->isEnabled() ) + { + smManager->disable(); + delete smManager; + smManager = NULL; + } +} + +//------------------------------------------------------------------------------ +bool Input::enable() +{ + if ( smManager && !smManager->isEnabled() ) + return( smManager->enable() ); + + return( false ); +} + +//------------------------------------------------------------------------------ +void Input::disable() +{ + if ( smManager && smManager->isEnabled() ) + smManager->disable(); +} + +//------------------------------------------------------------------------------ +void Input::activate() +{ + if ( smManager && smManager->isEnabled() && !isActive()) + { +#ifdef LOG_INPUT + Input::log( "Activating Input...\n" ); +#endif + UInputManager* uInputManager = dynamic_cast( smManager ); + if ( uInputManager ) + uInputManager->activate(); + } +} + +//------------------------------------------------------------------------------ +void Input::deactivate() +{ + if ( smManager && smManager->isEnabled() && isActive() ) + { +#ifdef LOG_INPUT + Input::log( "Deactivating Input...\n" ); +#endif + UInputManager* uInputManager = dynamic_cast( smManager ); + if ( uInputManager ) + uInputManager->deactivate(); + } +} + +//------------------------------------------------------------------------------ +void Input::reactivate() +{ + Input::deactivate(); + Input::activate(); +} + +//------------------------------------------------------------------------------ +bool Input::isEnabled() +{ + if ( smManager ) + return smManager->isEnabled(); + return false; +} + +//------------------------------------------------------------------------------ +bool Input::isActive() +{ + UInputManager* uInputManager = dynamic_cast( smManager ); + if ( uInputManager ) + return uInputManager->isActive(); + return false; +} + +//------------------------------------------------------------------------------ +void Input::process() +{ + if ( smManager ) + smManager->process(); +} + +//------------------------------------------------------------------------------ +InputManager* Input::getManager() +{ + return smManager; +} + +#ifdef LOG_INPUT +//------------------------------------------------------------------------------ +void Input::log( const char* format, ... ) +{ + if ( gInputLog == -1) + return; + + va_list argptr; + va_start( argptr, format ); + + const int BufSize = 4096; + char buffer[BufSize]; + dVsprintf( buffer, BufSize, format, argptr ); + x86UNIXWrite(gInputLog, buffer, dStrlen( buffer )); + va_end( argptr ); +} + +ConsoleFunction( inputLog, void, 2, 2, "inputLog( string )" ) +{ + argc; + Input::log( "%s\n", argv[1] ); +} +#endif // LOG_INPUT + +//------------------------------------------------------------------------------ +void NotifySelectionEvent(XEvent& event) +{ + // somebody sent us a select event + if (event.type == SelectionRequest) + xclipboard.handleSelectionRequest(event.xselectionrequest); +} + +//------------------------------------------------------------------------------ +const char* Platform::getClipboard() +{ + return xclipboard.getClipboard(); +} + +//------------------------------------------------------------------------------ +bool Platform::setClipboard(const char *text) +{ + return xclipboard.setClipboard(text); +} + +//----------------------------------------------------------------------------- +// XClipboard members +XClipboard::XClipboard() +{ + mInitialized = false; +} + +//------------------------------------------------------------------------------ +XClipboard::~XClipboard() +{ + freeXData(); + freeTData(); +} + +//------------------------------------------------------------------------------ +void XClipboard::init() +{ + DisplayPtrManager xdisplay; + Display* display = xdisplay.getDisplayPointer(); + + mClipboardProperty = XInternAtom(display, + "TORQUE_CLIPBOARD_ATOM", False); + mClipboard = XInternAtom(display, "CLIPBOARD", + False); + mPrimary = XA_PRIMARY; //XInternAtom(display, "PRIMARY", False); + mXData = NULL; + mTData = NULL; + mTDataSize = 0; + + mInitialized = true; +} + +//------------------------------------------------------------------------------ +inline void XClipboard::freeXData() +{ + if (mXData != NULL) + { + XFree(mXData); + mXData = NULL; + } +} + +//------------------------------------------------------------------------------ +inline void XClipboard::freeTData() +{ + if (mTData != NULL) + { + dRealFree(mTData); + mTData = NULL; + mTDataSize = 0; + } +} + +// +// JMQ: As you might expect, X clipboard usage is bizarre. I +// found this document to be useful. +// +// http://www.freedesktop.org/standards/clipboards.txt +// +// JMQ: later note: programming the X clipboard is not just +// bizarre, it SUCKS. No wonder so many apps have +// clipboard problems. +// +//------------------------------------------------------------------------------ +const char* XClipboard::getClipboard() +{ + DisplayPtrManager xdisplay; + Display* display = xdisplay.getDisplayPointer(); + + if (!mInitialized) + init(); + + // find the owner of the clipboard + Atom targetSelection = mClipboard; + Window clipOwner = XGetSelectionOwner(display, + targetSelection); + if (clipOwner == None) + { + // It seems like KDE/QT reads the clipboard but doesn't set it. + // This is a bug, that supposedly will be fixed in QT3. + // I tried working around this by using + // PRIMARY instead of CLIPBOARD, but this has some nonintuitive + // side effects. So, no pasting from KDE apps for now. + //targetSelection = mPrimary; + //clipOwner = XGetSelectionOwner(display, targetSelection); + } + + if (clipOwner == None) + // oh well + return ""; + + // request that the owner convert the selection to a string + XConvertSelection(display, targetSelection, + XA_STRING, mClipboardProperty, x86UNIXState->getWindow(), CurrentTime); + + // flush the output buffer to make sure the selection request event gets + // sent now + XFlush(display); + + XEvent xevent; + + // if our window is the current owner, (e.g. copy from one part of + // torque and paste to another), then we just sent an event to our + // window that won't get processed until we get back to the event + // loop in x86Unixwindow. So look for selection request events in + // the event queue immediately and handle them. + while (XCheckTypedWindowEvent(display, + x86UNIXState->getWindow(), SelectionRequest, &xevent)) + handleSelectionRequest(xevent.xselectionrequest); + + // poll for the SelectionNotify event for 5 seconds. in most cases + // we should get the event very quickly + U32 startTime = Platform::getRealMilliseconds(); + bool timeOut = false; + while (!XCheckTypedWindowEvent(display, + x86UNIXState->getWindow(), SelectionNotify, &xevent) && + !timeOut) + { + // we'll be spinning here, but who cares + if ((Platform::getRealMilliseconds() - startTime) > 5000) + timeOut = true; + } + + if (timeOut) + { + Con::warnf(ConsoleLogEntry::General, + "XClipboard: waited too long for owner to convert selection"); + return ""; + } + + if (xevent.xselection.property == None) + return ""; + + // free the X data from a previous get + freeXData(); + + // grab the string data from the property + Atom actual_type; + int actual_format; + unsigned long bytes_after; + unsigned long nitems; + // query the property length the 250000 is "the length in 32-bit + // multiples of the data to be retrieved". so we support up to a + // million bytes of returned data. + int numToRetrieve = 250000; + int status = XGetWindowProperty(display, + x86UNIXState->getWindow(), + mClipboardProperty, 0, numToRetrieve, True, XA_STRING, + &actual_type, &actual_format, &nitems, &bytes_after, &mXData); + + // we should have returned OK, with string type, 8bit data, + // and > 0 items. + if ((status != Success) || (actual_type != XA_STRING) || + (actual_format != 8) || (nitems == 0)) + return ""; + + // if there is data left in the clipboard, warn about it + if (bytes_after > 0) + Con::warnf(ConsoleLogEntry::General, + "XClipboard: some data was not retrieved"); + + return reinterpret_cast(mXData); +} + +//------------------------------------------------------------------------------ +void XClipboard::checkTDataSize(S32 requestedSize) +{ + if (mTDataSize < requestedSize) + { + freeTData(); + mTData = static_cast(dRealMalloc(sizeof(char) * requestedSize)); + AssertFatal(mTData, "unable to allocate clipboard buffer data!"); + mTDataSize = requestedSize; + } +} + +//------------------------------------------------------------------------------ +bool XClipboard::setClipboard(const char *text) +{ + DisplayPtrManager xdisplay; + Display* display = xdisplay.getDisplayPointer(); + + if (!mInitialized) + init(); + + // get the length of the text + S32 len = dStrlen(text) + 1; + + // reallocate the storage buffer if necessary + checkTDataSize(len); + + // copy the data into the storage buffer + dStrcpy(mTData, text); + + // tell X that we own the clipboard. (we'll get events + // if an app tries to paste) + XSetSelectionOwner(display, mClipboard, + x86UNIXState->getWindow(), CurrentTime); + + return true; +} + +//------------------------------------------------------------------------------ +void XClipboard::handleSelectionRequest(XSelectionRequestEvent& request) +{ + DisplayPtrManager xdisplay; + Display* display = xdisplay.getDisplayPointer(); + + // init our response + XSelectionEvent notify; + + notify.type = SelectionNotify; + notify.display = display; + notify.requestor = request.requestor; + notify.selection = request.selection; + notify.target = XA_STRING; + notify.property = None; + notify.time = CurrentTime; + + // make sure the owner is our window, and that the + // requestor wants the clipboard + if (request.owner == x86UNIXState->getWindow() && + request.selection == mClipboard) + { + notify.property = request.property; + // check to see if they did not set the property + if (notify.property == None) + notify.property = mClipboardProperty; + + // get the length of the data in the clipboard + S32 length = dStrlen(mTData); + // set the property on the requestor window + XChangeProperty(display, request.requestor, + notify.property, XA_STRING, + 8, PropModeReplace, reinterpret_cast(mTData), + length); + } + XSendEvent(display, notify.requestor, False, 0, + reinterpret_cast(¬ify)); + + // flush the output buffer to send the event now + XFlush(display); +} diff --git a/Engine/source/platformX86UNIX/x86UNIXInputManager.client.cpp b/Engine/source/platformX86UNIX/x86UNIXInputManager.client.cpp new file mode 100644 index 000000000..749ef6b04 --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXInputManager.client.cpp @@ -0,0 +1,1835 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformX86UNIX/platformX86UNIX.h" +#include "console/consoleTypes.h" +#include "platform/event.h" +#include "platform/gameInterface.h" +#include "platformX86UNIX/x86UNIXState.h" +#include "platformX86UNIX/x86UNIXInputManager.h" +#include "math/mMathFn.h" + +#include +#include +#include +#include + +#include + +// ascii table +AsciiData AsciiTable[NUM_KEYS]; + +// keymap table +static const U32 SDLtoTKeyMapSize = SDLK_LAST; +static U8 SDLtoTKeyMap[SDLtoTKeyMapSize]; +static bool keyMapsInitialized = false; + +// helper functions +static void MapKey(Uint16 SDLkey, U8 tkey, KeySym xkeysym); +static void InitKeyMaps(); +static inline U8 TranslateSDLKeytoTKey(SDLKey keysym); + +// unix platform state +extern x86UNIXPlatformState * x86UNIXState; + +// constants + +static const U32 MouseMask = SDL_MOUSEEVENTMASK; +static const U32 KeyboardMask = SDL_KEYUPMASK | SDL_KEYDOWNMASK; +static const U32 JoystickMask = SDL_JOYEVENTMASK; + +static const U32 AllInputEvents = MouseMask | KeyboardMask | JoystickMask; + +// defined in SDL +extern "C" Uint16 X11_KeyToUnicode( SDLKey keysym, SDLMod modifiers ); + +//============================================================================== +// Static helper functions +//============================================================================== +static void MapKey(Uint16 SDLkey, U8 tkey, KeySym xkeysym) +{ + DisplayPtrManager xdisplay; + Display* display = xdisplay.getDisplayPointer(); + + SDLtoTKeyMap[SDLkey] = tkey; + + Uint16 key = 0; + SDLKey skey = (SDLKey)SDLkey; + SDLMod mod = KMOD_NONE; + // lower case + key = X11_KeyToUnicode( skey, mod ); + AsciiTable[tkey].lower.ascii = key; + // upper case + mod = KMOD_LSHIFT; + key = X11_KeyToUnicode( skey, mod ); + AsciiTable[tkey].upper.ascii = key; + // goofy (i18n) case + mod = KMOD_MODE; + key = X11_KeyToUnicode( skey, mod ); + AsciiTable[tkey].goofy.ascii = key; + +#if 0 + if (xkeysym == 0) + return; + + XKeyPressedEvent fooKey; + const int keybufSize = 256; + char keybuf[keybufSize]; + + // find the x keycode for the keysym + KeyCode xkeycode = XKeysymToKeycode( + display, xkeysym); + +// Display *dpy = XOpenDisplay(NULL); +// KeyCode xkeycode = XKeysymToKeycode( +// dpy, xkeysym); + + if (!xkeycode) + return; + + // create an event with the keycode + dMemset(&fooKey, 0, sizeof(fooKey)); + fooKey.type = KeyPress; + fooKey.display = display; + fooKey.window = DefaultRootWindow(display); + fooKey.time = CurrentTime; + fooKey.keycode = xkeycode; + + // translate the event with no modifiers (yields lowercase) + KeySym dummyKeySym; + int numChars = XLookupString( + &fooKey, keybuf, keybufSize, &dummyKeySym, NULL); + if (numChars) + { + //Con::printf("assigning lowercase string %c", *keybuf); + // ignore everything but first char + AsciiTable[tkey].lower.ascii = *keybuf; + AsciiTable[tkey].goofy.ascii = *keybuf; + } + + // translate the event with shift modifier (yields uppercase) + fooKey.state |= ShiftMask; + numChars = XLookupString(&fooKey, keybuf, keybufSize, &dummyKeySym, NULL); + if (numChars) + { + //Con::printf("assigning uppercase string %c", *keybuf); + // ignore everything but first char + AsciiTable[tkey].upper.ascii = *keybuf; + } +#endif +} + +//------------------------------------------------------------------------------ +void InitKeyMaps() +{ + dMemset( &AsciiTable, 0, sizeof( AsciiTable ) ); + dMemset(SDLtoTKeyMap, KEY_NULL, SDLtoTKeyMapSize); + + // set up the X to Torque key map + // stuff + MapKey(SDLK_BACKSPACE, KEY_BACKSPACE, XK_BackSpace); + MapKey(SDLK_TAB, KEY_TAB, XK_Tab); + MapKey(SDLK_RETURN, KEY_RETURN, XK_Return); + MapKey(SDLK_PAUSE, KEY_PAUSE, XK_Pause); + MapKey(SDLK_CAPSLOCK, KEY_CAPSLOCK, XK_Caps_Lock); + MapKey(SDLK_ESCAPE, KEY_ESCAPE, XK_Escape); + + // more stuff + MapKey(SDLK_SPACE, KEY_SPACE, XK_space); + MapKey(SDLK_PAGEDOWN, KEY_PAGE_DOWN, XK_Page_Down); + MapKey(SDLK_PAGEUP, KEY_PAGE_UP, XK_Page_Up); + MapKey(SDLK_END, KEY_END, XK_End); + MapKey(SDLK_HOME, KEY_HOME, XK_Home); + MapKey(SDLK_LEFT, KEY_LEFT, XK_Left); + MapKey(SDLK_UP, KEY_UP, XK_Up); + MapKey(SDLK_RIGHT, KEY_RIGHT, XK_Right); + MapKey(SDLK_DOWN, KEY_DOWN, XK_Down); + MapKey(SDLK_PRINT, KEY_PRINT, XK_Print); + MapKey(SDLK_INSERT, KEY_INSERT, XK_Insert); + MapKey(SDLK_DELETE, KEY_DELETE, XK_Delete); + + S32 keysym; + S32 tkeycode; + KeySym xkey; + // main numeric keys + for (keysym = SDLK_0, tkeycode = KEY_0, xkey = XK_0; + keysym <= SDLK_9; + ++keysym, ++tkeycode, ++xkey) + MapKey(static_cast(keysym), tkeycode, xkey); + + // lowercase letters + for (keysym = SDLK_a, tkeycode = KEY_A, xkey = XK_a; + keysym <= SDLK_z; + ++keysym, ++tkeycode, ++xkey) + MapKey(static_cast(keysym), tkeycode, xkey); + + // various punctuation + MapKey('|', KEY_TILDE, XK_grave); + MapKey(SDLK_BACKQUOTE, KEY_TILDE, XK_grave); + MapKey(SDLK_MINUS, KEY_MINUS, XK_minus); + MapKey(SDLK_EQUALS, KEY_EQUALS, XK_equal); + MapKey(SDLK_LEFTBRACKET, KEY_LBRACKET, XK_bracketleft); + MapKey('{', KEY_LBRACKET, XK_bracketleft); + MapKey(SDLK_RIGHTBRACKET, KEY_RBRACKET, XK_bracketright); + MapKey('}', KEY_RBRACKET, XK_bracketright); + MapKey(SDLK_BACKSLASH, KEY_BACKSLASH, XK_backslash); + MapKey(SDLK_SEMICOLON, KEY_SEMICOLON, XK_semicolon); + MapKey(SDLK_QUOTE, KEY_APOSTROPHE, XK_apostrophe); + MapKey(SDLK_COMMA, KEY_COMMA, XK_comma); + MapKey(SDLK_PERIOD, KEY_PERIOD, XK_period); + MapKey(SDLK_SLASH, KEY_SLASH, XK_slash); + + // numpad numbers + for (keysym = SDLK_KP0, tkeycode = KEY_NUMPAD0, xkey = XK_KP_0; + keysym <= SDLK_KP9; + ++keysym, ++tkeycode, ++xkey) + MapKey(static_cast(keysym), tkeycode, xkey); + + // other numpad stuff + MapKey(SDLK_KP_MULTIPLY, KEY_MULTIPLY, XK_KP_Multiply); + MapKey(SDLK_KP_PLUS, KEY_ADD, XK_KP_Add); + MapKey(SDLK_KP_EQUALS, KEY_SEPARATOR, XK_KP_Separator); + MapKey(SDLK_KP_MINUS, KEY_SUBTRACT, XK_KP_Subtract); + MapKey(SDLK_KP_PERIOD, KEY_DECIMAL, XK_KP_Decimal); + MapKey(SDLK_KP_DIVIDE, KEY_DIVIDE, XK_KP_Divide); + MapKey(SDLK_KP_ENTER, KEY_NUMPADENTER, XK_KP_Enter); + + // F keys + for (keysym = SDLK_F1, tkeycode = KEY_F1, xkey = XK_F1; + keysym <= SDLK_F15; + ++keysym, ++tkeycode, ++xkey) + MapKey(static_cast(keysym), tkeycode, xkey); + + // various modifiers + MapKey(SDLK_NUMLOCK, KEY_NUMLOCK, XK_Num_Lock); + MapKey(SDLK_SCROLLOCK, KEY_SCROLLLOCK, XK_Scroll_Lock); + MapKey(SDLK_LCTRL, KEY_LCONTROL, XK_Control_L); + MapKey(SDLK_RCTRL, KEY_RCONTROL, XK_Control_R); + MapKey(SDLK_LALT, KEY_LALT, XK_Alt_L); + MapKey(SDLK_RALT, KEY_RALT, XK_Alt_R); + MapKey(313, KEY_RALT, XK_Alt_R); + MapKey(SDLK_LSHIFT, KEY_LSHIFT, XK_Shift_L); + MapKey(SDLK_RSHIFT, KEY_RSHIFT, XK_Shift_R); + MapKey(SDLK_LSUPER, KEY_WIN_LWINDOW, 0); + MapKey(SDLK_RSUPER, KEY_WIN_RWINDOW, 0); + MapKey(SDLK_MENU, KEY_WIN_APPS, 0); + MapKey(SDLK_MODE, KEY_OEM_102, 0); + + keyMapsInitialized = true; +}; + +//------------------------------------------------------------------------------ +U8 TranslateSDLKeytoTKey(SDLKey keysym) +{ + if (!keyMapsInitialized) + { + Con::printf("WARNING: SDLkeysymMap is not initialized"); + return 0; + } + if (keysym < 0 || + static_cast(keysym) >= SDLtoTKeyMapSize) + { + Con::printf("WARNING: invalid keysym: %d", keysym); + return 0; + } + return SDLtoTKeyMap[keysym]; +} + +//------------------------------------------------------------------------------ +// this shouldn't be used, use TranslateSDLKeytoTKey instead +U8 TranslateOSKeyCode(U8 vcode) +{ + Con::printf("WARNING: TranslateOSKeyCode is not supported in unix"); + return 0; +} + +//============================================================================== +// UInputManager +//============================================================================== +UInputManager::UInputManager() +{ + mActive = false; + mEnabled = false; + mLocking = true; // locking enabled by default + mKeyboardEnabled = mMouseEnabled = mJoystickEnabled = false; + mKeyboardActive = mMouseActive = mJoystickActive = false; +} + +//------------------------------------------------------------------------------ +void UInputManager::init() +{ + Con::addVariable( "pref::Input::KeyboardEnabled", + TypeBool, &mKeyboardEnabled ); + Con::addVariable( "pref::Input::MouseEnabled", + TypeBool, &mMouseEnabled ); + Con::addVariable( "pref::Input::JoystickEnabled", + TypeBool, &mJoystickEnabled ); +} + +//------------------------------------------------------------------------------ +bool UInputManager::enable() +{ + disable(); +#ifdef LOG_INPUT + Input::log( "Enabling Input...\n" ); +#endif + + mModifierKeys = 0; + dMemset( mMouseButtonState, 0, sizeof( mMouseButtonState ) ); + dMemset( mKeyboardState, 0, 256 ); + + InitKeyMaps(); + + mJoystickEnabled = false; + initJoystick(); + + mEnabled = true; + mMouseEnabled = true; + mKeyboardEnabled = true; + + SDL_EnableKeyRepeat( + SDL_DEFAULT_REPEAT_DELAY, + SDL_DEFAULT_REPEAT_INTERVAL); + + return true; +} + +//------------------------------------------------------------------------------ +void UInputManager::disable() +{ + deactivate(); + mEnabled = false; + return; +} + +//------------------------------------------------------------------------------ +void UInputManager::initJoystick() +{ + mJoystickList.clear(); + + // initialize SDL joystick system + if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0) + { + Con::warnf(" Unable to initialize joystick: %s", SDL_GetError()); + return; + } + + int numJoysticks = SDL_NumJoysticks(); + if (numJoysticks == 0) + Con::printf(" No joysticks found."); + + // disable joystick events (use polling instead) + SDL_JoystickEventState(SDL_IGNORE); + + // install joysticks + for(int i = 0; i < numJoysticks; i++ ) + { + JoystickInputDevice* newDevice = new JoystickInputDevice(i); + addObject(newDevice); + mJoystickList.push_back(newDevice); + Con::printf(" %s: %s", + newDevice->getDeviceName(), newDevice->getName()); +#ifdef LOG_INPUT + Input::log(" %s: %s\n", + newDevice->getDeviceName(), newDevice->getName()); +#endif + } + + mJoystickEnabled = true; +} + +//------------------------------------------------------------------------------ +void UInputManager::activate() +{ + if (mEnabled && !isActive()) + { + mActive = true; + SDL_ShowCursor(SDL_DISABLE); + resetInputState(); + // hack; if the mouse or keyboard has been disabled, re-enable them. + // prevents scripts like default.cs from breaking our input, although + // there is probably a better solution + mMouseEnabled = mKeyboardEnabled = true; + activateMouse(); + activateKeyboard(); + activateJoystick(); + if (x86UNIXState->windowLocked()) + lockInput(); + } +} + +//------------------------------------------------------------------------------ +void UInputManager::deactivate() +{ + if (mEnabled && isActive()) + { + unlockInput(); + deactivateKeyboard(); + deactivateMouse(); + deactivateJoystick(); + resetInputState(); + SDL_ShowCursor(SDL_ENABLE); + mActive = false; + } +} + +//------------------------------------------------------------------------------ +void UInputManager::resetKeyboardState() +{ + // unpress any pressed keys; in the future we may want + // to actually sync with the keyboard state + for (int i = 0; i < 256; ++i) + { + if (mKeyboardState[i]) + { + InputEvent event; + + event.deviceInst = 0; + event.deviceType = KeyboardDeviceType; + event.objType = SI_KEY; + event.objInst = i; + event.action = SI_BREAK; + event.fValue = 0.0; + Game->postEvent(event); + } + } + dMemset(mKeyboardState, 0, 256); + + // clear modifier keys + mModifierKeys = 0; +} + +//------------------------------------------------------------------------------ +void UInputManager::resetMouseState() +{ + // unpress any buttons; in the future we may want + // to actually sync with the mouse state + for (int i = 0; i < 3; ++i) + { + if (mMouseButtonState[i]) + { + // add KEY_BUTTON0 to the index to get the real + // button ID + S32 buttonID = i + KEY_BUTTON0; + InputEvent event; + + event.deviceInst = 0; + event.deviceType = MouseDeviceType; + event.objType = SI_BUTTON; + event.objInst = buttonID; + event.action = SI_BREAK; + event.fValue = 0.0; + Game->postEvent(event); + } + } + + dMemset(mMouseButtonState, 0, 3); +} + +//------------------------------------------------------------------------------ +void UInputManager::resetInputState() +{ + resetKeyboardState(); + resetMouseState(); + + // reset joysticks + for (Vector::iterator iter = mJoystickList.begin(); + iter != mJoystickList.end(); + ++iter) + { + (*iter)->reset(); + } + + // JMQTODO: make event arrays be members + // dispose of any lingering SDL input events + static const int MaxEvents = 255; + static SDL_Event events[MaxEvents]; + SDL_PumpEvents(); + SDL_PeepEvents(events, MaxEvents, SDL_GETEVENT, + AllInputEvents); +} + +//------------------------------------------------------------------------------ +void UInputManager::setLocking(bool enabled) +{ + mLocking = enabled; + if (mLocking) + lockInput(); + else + unlockInput(); +} + +//------------------------------------------------------------------------------ +void UInputManager::lockInput() +{ + if (x86UNIXState->windowActive() && x86UNIXState->windowLocked() && + mLocking && + SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_OFF) + SDL_WM_GrabInput(SDL_GRAB_ON); +} + +//------------------------------------------------------------------------------ +void UInputManager::unlockInput() +{ + if (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON) + SDL_WM_GrabInput(SDL_GRAB_OFF); +} + +//------------------------------------------------------------------------------ +void UInputManager::onDeleteNotify( SimObject* object ) +{ + Parent::onDeleteNotify( object ); +} + +//------------------------------------------------------------------------------ +bool UInputManager::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + return true; +} + +//------------------------------------------------------------------------------ +void UInputManager::onRemove() +{ + deactivate(); + Parent::onRemove(); +} + +//------------------------------------------------------------------------------ +void UInputManager::mouseMotionEvent(const SDL_Event& event) +{ +// Con::printf("motion event: %d %d %d %d", +// event.motion.xrel, event.motion.yrel, +// event.motion.x, event.motion.y); + if (x86UNIXState->windowLocked()) + { + InputEvent ievent; + ievent.deviceInst = 0; + ievent.deviceType = MouseDeviceType; + ievent.objInst = 0; + ievent.modifier = mModifierKeys; + ievent.ascii = 0; + ievent.action = SI_MOVE; + + // post events if things have changed + if (event.motion.xrel != 0) + { + ievent.objType = SI_XAXIS; + ievent.fValue = event.motion.xrel; + Game->postEvent(ievent); + } + if (event.motion.yrel != 0) + { + ievent.objType = SI_YAXIS; + ievent.fValue = event.motion.yrel; + Game->postEvent(ievent); + } +#ifdef LOG_INPUT +#ifdef LOG_MOUSEMOVE + Input::log( "EVENT (Input): Mouse relative move (%.1f, %.1f).\n", + event.motion.xrel != 0 ? F32(event.motion.xrel) : 0.0, + event.motion.yrel != 0 ? F32(event.motion.yrel) : 0.0); +#endif +#endif + } + else + { + MouseMoveEvent mmevent; + mmevent.xPos = mLastMouseX = event.motion.x; + mmevent.yPos = mLastMouseY = event.motion.y; + mmevent.modifier = mModifierKeys; + Game->postEvent(mmevent); +#ifdef LOG_INPUT +#ifdef LOG_MOUSEMOVE + Input::log( "EVENT (Input): Mouse absolute move (%.1f, %.1f).\n", + F32(event.motion.x), + F32(event.motion.y)); +#endif +#endif + } +} + +//------------------------------------------------------------------------------ +void UInputManager::joyButtonEvent(const SDL_Event& event) +{ + joyButtonEvent(event.jbutton.which, event.jbutton.button, + event.type == SDL_JOYBUTTONDOWN); +} + +//------------------------------------------------------------------------------ +void UInputManager::joyButtonEvent(U8 deviceID, U8 buttonNum, bool pressed) + +{ + S32 action = pressed ? SI_MAKE : SI_BREAK; + S32 objInst = buttonNum + KEY_BUTTON0; + + InputEvent ievent; + + ievent.deviceInst = deviceID; + ievent.deviceType = JoystickDeviceType; + ievent.modifier = mModifierKeys; + ievent.ascii = 0; + ievent.objType = SI_BUTTON; + ievent.objInst = objInst; + ievent.action = action; + ievent.fValue = (action == SI_MAKE) ? 1.0 : 0.0; + + Game->postEvent(ievent); +#ifdef LOG_INPUT + Input::log( "EVENT (Input): joystick%d button%d %s. MODS:%c%c%c \n", + deviceID, + buttonNum, + pressed ? "pressed" : "released", + ( mModifierKeys & SI_SHIFT ? 'S' : '.' ), + ( mModifierKeys & SI_CTRL ? 'C' : '.' ), + ( mModifierKeys & SI_ALT ? 'A' : '.' )); +#endif +} + +//------------------------------------------------------------------------------ +void UInputManager::joyHatEvent(U8 deviceID, U8 hatNum, + U8 prevHatState, U8 currHatState) +{ + if (prevHatState == currHatState) + return; + + InputEvent ievent; + + ievent.deviceInst = deviceID; + ievent.deviceType = JoystickDeviceType; + ievent.modifier = mModifierKeys; + ievent.ascii = 0; + ievent.objType = SI_POV; + + // first break any positions that are no longer valid + ievent.action = SI_BREAK; + ievent.fValue = 0.0; + + if (prevHatState & SDL_HAT_UP && !(currHatState & SDL_HAT_UP)) + { +#ifdef LOG_INPUT + Input::log( "EVENT (Input): Up POV released.\n"); +#endif + ievent.objInst = SI_UPOV; + Game->postEvent(ievent); + } + else if (prevHatState & SDL_HAT_DOWN && !(currHatState & SDL_HAT_DOWN)) + { +#ifdef LOG_INPUT + Input::log( "EVENT (Input): Down POV released.\n"); +#endif + ievent.objInst = SI_DPOV; + Game->postEvent(ievent); + } + if (prevHatState & SDL_HAT_LEFT && !(currHatState & SDL_HAT_LEFT)) + { +#ifdef LOG_INPUT + Input::log( "EVENT (Input): Left POV released.\n"); +#endif + ievent.objInst = SI_LPOV; + Game->postEvent(ievent); + } + else if (prevHatState & SDL_HAT_RIGHT && !(currHatState & SDL_HAT_RIGHT)) + { +#ifdef LOG_INPUT + Input::log( "EVENT (Input): Right POV released.\n"); +#endif + ievent.objInst = SI_RPOV; + Game->postEvent(ievent); + } + + // now do the make events + ievent.action = SI_MAKE; + ievent.fValue = 1.0; + + if (!(prevHatState & SDL_HAT_UP) && currHatState & SDL_HAT_UP) + { +#ifdef LOG_INPUT + Input::log( "EVENT (Input): Up POV pressed.\n"); +#endif + ievent.objInst = SI_UPOV; + Game->postEvent(ievent); + } + else if (!(prevHatState & SDL_HAT_DOWN) && currHatState & SDL_HAT_DOWN) + { +#ifdef LOG_INPUT + Input::log( "EVENT (Input): Down POV pressed.\n"); +#endif + ievent.objInst = SI_DPOV; + Game->postEvent(ievent); + } + if (!(prevHatState & SDL_HAT_LEFT) && currHatState & SDL_HAT_LEFT) + { +#ifdef LOG_INPUT + Input::log( "EVENT (Input): Left POV pressed.\n"); +#endif + ievent.objInst = SI_LPOV; + Game->postEvent(ievent); + } + else if (!(prevHatState & SDL_HAT_RIGHT) && currHatState & SDL_HAT_RIGHT) + { +#ifdef LOG_INPUT + Input::log( "EVENT (Input): Right POV pressed.\n"); +#endif + ievent.objInst = SI_RPOV; + Game->postEvent(ievent); + } +} + +//------------------------------------------------------------------------------ +void UInputManager::joyAxisEvent(const SDL_Event& event) +{ + joyAxisEvent(event.jaxis.which, event.jaxis.axis, event.jaxis.value); +} + +//------------------------------------------------------------------------------ +void UInputManager::joyAxisEvent(U8 deviceID, U8 axisNum, S16 axisValue) +{ + JoystickInputDevice* stick; + + stick = mJoystickList[deviceID]; + AssertFatal(stick, "JoystickInputDevice* is NULL"); + JoystickAxisInfo axisInfo = stick->getAxisInfo(axisNum); + + if (axisInfo.type == -1) + return; + + // scale the value to [-1,1] + F32 scaledValue = 0; + if (axisValue < 0) + scaledValue = -F32(axisValue) / axisInfo.minValue; + else if (axisValue > 0) + scaledValue = F32(axisValue) / axisInfo.maxValue; + +// F32 range = F32(axisInfo.maxValue - axisInfo.minValue); +// F32 scaledValue = F32((2 * axisValue) - axisInfo.maxValue - +// axisInfo.minValue) / range; + + if (scaledValue > 1.f) + scaledValue = 1.f; + else if (scaledValue < -1.f) + scaledValue = -1.f; + + // create and post the event + InputEvent ievent; + + ievent.deviceInst = deviceID; + ievent.deviceType = JoystickDeviceType; + ievent.modifier = mModifierKeys; + ievent.ascii = 0; + ievent.objType = axisInfo.type; + ievent.objInst = 0; + ievent.action = SI_MOVE; + ievent.fValue = scaledValue; + + Game->postEvent(ievent); + +#ifdef LOG_INPUT + Input::log( "EVENT (Input): joystick axis %d moved: %.1f.\n", + axisNum, ievent.fValue); +#endif + +} + +//------------------------------------------------------------------------------ +void UInputManager::mouseButtonEvent(const SDL_Event& event) +{ + S32 action = (event.type == SDL_MOUSEBUTTONDOWN) ? SI_MAKE : SI_BREAK; + S32 objInst = -1; + // JMQTODO: support wheel delta like windows version? + // JMQTODO: make this value configurable? + S32 wheelDelta = 10; + bool wheel = false; + + switch (event.button.button) + { + case SDL_BUTTON_LEFT: + objInst = KEY_BUTTON0; + break; + case SDL_BUTTON_RIGHT: + objInst = KEY_BUTTON1; + break; + case SDL_BUTTON_MIDDLE: + objInst = KEY_BUTTON2; + break; + case Button4: + wheel = true; + break; + case Button5: + wheel = true; + wheelDelta = -wheelDelta; + break; + } + + if (objInst == -1 && !wheel) + // unsupported button + return; + + InputEvent ievent; + + ievent.deviceInst = 0; + ievent.deviceType = MouseDeviceType; + ievent.modifier = mModifierKeys; + ievent.ascii = 0; + + if (wheel) + { + // SDL generates a button press/release for each wheel move, + // so ignore breaks to translate those into a single event + if (action == SI_BREAK) + return; + ievent.objType = SI_ZAXIS; + ievent.objInst = 0; + ievent.action = SI_MOVE; + ievent.fValue = wheelDelta; +#ifdef LOG_INPUT + Input::log( "EVENT (Input): mouse wheel moved %s: %.1f. MODS:%c%c%c\n", + wheelDelta > 0 ? "up" : "down", + ievent.fValue, + ( mModifierKeys & SI_SHIFT ? 'S' : '.' ), + ( mModifierKeys & SI_CTRL ? 'C' : '.' ), + ( mModifierKeys & SI_ALT ? 'A' : '.' )); +#endif + } + else // regular button + { + S32 buttonID = (objInst - KEY_BUTTON0); + if (buttonID < 3) + mMouseButtonState[buttonID] = ( action == SI_MAKE ) ? true : false; + + ievent.objType = SI_BUTTON; + ievent.objInst = objInst; + ievent.action = action; + ievent.fValue = (action == SI_MAKE) ? 1.0 : 0.0; +#ifdef LOG_INPUT + Input::log( "EVENT (Input): mouse button%d %s. MODS:%c%c%c\n", + buttonID, + action == SI_MAKE ? "pressed" : "released", + ( mModifierKeys & SI_SHIFT ? 'S' : '.' ), + ( mModifierKeys & SI_CTRL ? 'C' : '.' ), + ( mModifierKeys & SI_ALT ? 'A' : '.' )); +#endif + } + + Game->postEvent(ievent); +} + +//------------------------------------------------------------------------------ +const char* getKeyName( U16 key ) +{ + switch ( key ) + { + case KEY_BACKSPACE: return "Backspace"; + case KEY_TAB: return "Tab"; + case KEY_RETURN: return "Return"; + case KEY_PAUSE: return "Pause"; + case KEY_CAPSLOCK: return "CapsLock"; + case KEY_ESCAPE: return "Esc"; + + case KEY_SPACE: return "SpaceBar"; + case KEY_PAGE_DOWN: return "PageDown"; + case KEY_PAGE_UP: return "PageUp"; + case KEY_END: return "End"; + case KEY_HOME: return "Home"; + case KEY_LEFT: return "Left"; + case KEY_UP: return "Up"; + case KEY_RIGHT: return "Right"; + case KEY_DOWN: return "Down"; + case KEY_PRINT: return "PrintScreen"; + case KEY_INSERT: return "Insert"; + case KEY_DELETE: return "Delete"; + case KEY_HELP: return "Help"; + + case KEY_NUMPAD0: return "Numpad 0"; + case KEY_NUMPAD1: return "Numpad 1"; + case KEY_NUMPAD2: return "Numpad 2"; + case KEY_NUMPAD3: return "Numpad 3"; + case KEY_NUMPAD4: return "Numpad 4"; + case KEY_NUMPAD5: return "Numpad 5"; + case KEY_NUMPAD6: return "Numpad 6"; + case KEY_NUMPAD7: return "Numpad 7"; + case KEY_NUMPAD8: return "Numpad 8"; + case KEY_NUMPAD9: return "Numpad 9"; + case KEY_MULTIPLY: return "Multiply"; + case KEY_ADD: return "Add"; + case KEY_SEPARATOR: return "Separator"; + case KEY_SUBTRACT: return "Subtract"; + case KEY_DECIMAL: return "Decimal"; + case KEY_DIVIDE: return "Divide"; + case KEY_NUMPADENTER: return "Numpad Enter"; + + case KEY_F1: return "F1"; + case KEY_F2: return "F2"; + case KEY_F3: return "F3"; + case KEY_F4: return "F4"; + case KEY_F5: return "F5"; + case KEY_F6: return "F6"; + case KEY_F7: return "F7"; + case KEY_F8: return "F8"; + case KEY_F9: return "F9"; + case KEY_F10: return "F10"; + case KEY_F11: return "F11"; + case KEY_F12: return "F12"; + case KEY_F13: return "F13"; + case KEY_F14: return "F14"; + case KEY_F15: return "F15"; + case KEY_F16: return "F16"; + case KEY_F17: return "F17"; + case KEY_F18: return "F18"; + case KEY_F19: return "F19"; + case KEY_F20: return "F20"; + case KEY_F21: return "F21"; + case KEY_F22: return "F22"; + case KEY_F23: return "F23"; + case KEY_F24: return "F24"; + + case KEY_NUMLOCK: return "NumLock"; + case KEY_SCROLLLOCK: return "ScrollLock"; + case KEY_LCONTROL: return "LCtrl"; + case KEY_RCONTROL: return "RCtrl"; + case KEY_LALT: return "LAlt"; + case KEY_RALT: return "RAlt"; + case KEY_LSHIFT: return "LShift"; + case KEY_RSHIFT: return "RShift"; + + case KEY_WIN_LWINDOW: return "LWin"; + case KEY_WIN_RWINDOW: return "RWin"; + case KEY_WIN_APPS: return "Apps"; + } + + static char returnString[5]; + dSprintf( returnString, sizeof( returnString ), "%c", Input::getAscii( key, STATE_UPPER ) ); + return returnString; +} + + +//------------------------------------------------------------------------------ +void UInputManager::keyEvent(const SDL_Event& event) +{ + S32 action = (event.type == SDL_KEYDOWN) ? SI_MAKE : SI_BREAK; + InputEvent ievent; + + ievent.deviceInst = 0; + ievent.deviceType = KeyboardDeviceType; + ievent.objType = SI_KEY; + ievent.objInst = TranslateSDLKeytoTKey(event.key.keysym.sym); + // if the action is a make but this key is already pressed, + // count it as a repeat + if (action == SI_MAKE && mKeyboardState[ievent.objInst]) + action = SI_REPEAT; + ievent.action = action; + ievent.fValue = (action == SI_MAKE || action == SI_REPEAT) ? 1.0 : 0.0; + + processKeyEvent(ievent); + Game->postEvent(ievent); + +#if 0 + if (ievent.action == SI_MAKE) + dPrintf("key event: : %s key pressed. MODS:%c%c%c\n", + getKeyName(ievent.objInst), + ( mModifierKeys & SI_SHIFT ? 'S' : '.' ), + ( mModifierKeys & SI_CTRL ? 'C' : '.' ), + ( mModifierKeys & SI_ALT ? 'A' : '.' )); + else if (ievent.action == SI_REPEAT) + dPrintf("key event: : %s key repeated. MODS:%c%c%c\n", + getKeyName(ievent.objInst), + ( mModifierKeys & SI_SHIFT ? 'S' : '.' ), + ( mModifierKeys & SI_CTRL ? 'C' : '.' ), + ( mModifierKeys & SI_ALT ? 'A' : '.' )); + else if (ievent.action == SI_BREAK) + dPrintf("key event: : %s key released. MODS:%c%c%c\n", + getKeyName(ievent.objInst), + ( mModifierKeys & SI_SHIFT ? 'S' : '.' ), + ( mModifierKeys & SI_CTRL ? 'C' : '.' ), + ( mModifierKeys & SI_ALT ? 'A' : '.' )); + else + dPrintf("unknown key event!\n"); +#endif + +#ifdef LOG_INPUT + Input::log( "EVENT (Input): %s key %s. MODS:%c%c%c\n", + getKeyName(ievent.objInst), + action == SI_MAKE ? "pressed" : "released", + ( mModifierKeys & SI_SHIFT ? 'S' : '.' ), + ( mModifierKeys & SI_CTRL ? 'C' : '.' ), + ( mModifierKeys & SI_ALT ? 'A' : '.' )); +#endif +} + +//------------------------------------------------------------------------------ +// This function was ripped from DInputDevice almost entirely intact. +bool UInputManager::processKeyEvent( InputEvent &event ) +{ + if ( event.deviceType != KeyboardDeviceType || event.objType != SI_KEY ) + return false; + + bool modKey = false; + U8 keyCode = event.objInst; + + if ( event.action == SI_MAKE || event.action == SI_REPEAT) + { + // Maintain the key structure: + mKeyboardState[keyCode] = true; + + switch ( event.objInst ) + { + case KEY_LSHIFT: + mModifierKeys |= SI_LSHIFT; + modKey = true; + break; + + case KEY_RSHIFT: + mModifierKeys |= SI_RSHIFT; + modKey = true; + break; + + case KEY_LCONTROL: + mModifierKeys |= SI_LCTRL; + modKey = true; + break; + + case KEY_RCONTROL: + mModifierKeys |= SI_RCTRL; + modKey = true; + break; + + case KEY_LALT: + mModifierKeys |= SI_LALT; + modKey = true; + break; + + case KEY_RALT: + mModifierKeys |= SI_RALT; + modKey = true; + break; + } + } + else + { + // Maintain the keys structure: + mKeyboardState[keyCode] = false; + + switch ( event.objInst ) + { + case KEY_LSHIFT: + mModifierKeys &= ~SI_LSHIFT; + modKey = true; + break; + + case KEY_RSHIFT: + mModifierKeys &= ~SI_RSHIFT; + modKey = true; + break; + + case KEY_LCONTROL: + mModifierKeys &= ~SI_LCTRL; + modKey = true; + break; + + case KEY_RCONTROL: + mModifierKeys &= ~SI_RCTRL; + modKey = true; + break; + + case KEY_LALT: + mModifierKeys &= ~SI_LALT; + modKey = true; + break; + + case KEY_RALT: + mModifierKeys &= ~SI_RALT; + modKey = true; + break; + } + } + + if ( modKey ) + event.modifier = 0; + else + event.modifier = mModifierKeys; + + // TODO: alter this getAscii call + KEY_STATE state = STATE_LOWER; + if (event.modifier & (SI_CTRL|SI_ALT) ) + { + state = STATE_GOOFY; + } + if ( event.modifier & SI_SHIFT ) + { + state = STATE_UPPER; + } + + event.ascii = Input::getAscii( event.objInst, state ); + + return modKey; +} + +//------------------------------------------------------------------------------ +void UInputManager::setWindowLocked(bool locked) +{ + if (locked) + lockInput(); + else + { + unlockInput(); + // SDL keeps track of abs mouse position in fullscreen mode, which means + // that if you switch to unlocked mode while fullscreen, the mouse will + // suddenly warp to someplace unexpected on screen. To fix this, we + // warp the mouse to the last known Torque abs mouse position. + if (mLastMouseX != -1 && mLastMouseY != -1) + SDL_WarpMouse(mLastMouseX, mLastMouseY); + } +} + +//------------------------------------------------------------------------------ +void UInputManager::process() +{ + if (!mEnabled || !isActive()) + return; + + // JMQTODO: make these be class members + static const int MaxEvents = 255; + static SDL_Event events[MaxEvents]; + + U32 mask = 0; + + // process keyboard and mouse events + if (mMouseActive) + mask |= MouseMask; + if (mKeyboardActive) + mask |= KeyboardMask; + + if (mask != 0) + { + SDL_PumpEvents(); + S32 numEvents = SDL_PeepEvents(events, MaxEvents, SDL_GETEVENT, mask); + + for (int i = 0; i < numEvents; ++i) + { + switch (events[i].type) + { + case SDL_MOUSEMOTION: + mouseMotionEvent(events[i]); + break; + case SDL_MOUSEBUTTONUP: + case SDL_MOUSEBUTTONDOWN: + mouseButtonEvent(events[i]); + break; + case SDL_KEYDOWN: + case SDL_KEYUP: + keyEvent(events[i]); + break; + } + } + } + + // poll joysticks + if (!mJoystickActive) + return; + + SDL_JoystickUpdate(); + + for (Vector::iterator iter = mJoystickList.begin(); + iter != mJoystickList.end(); + ++iter) + { + (*iter)->process(); + } +} + +//------------------------------------------------------------------------------ +bool UInputManager::enableKeyboard() +{ + if ( !isEnabled() ) + return( false ); + + if ( isKeyboardEnabled() && isKeyboardActive() ) + return( true ); + + mKeyboardEnabled = true; + if ( isActive() ) + mKeyboardEnabled = activateKeyboard(); + + if ( mKeyboardEnabled ) + { + Con::printf( "Keyboard enabled." ); +#ifdef LOG_INPUT + Input::log( "Keyboard enabled.\n" ); +#endif + } + else + { + Con::warnf( "Keyboard failed to enable!" ); +#ifdef LOG_INPUT + Input::log( "Keyboard failed to enable!\n" ); +#endif + } + + return( mKeyboardEnabled ); +} + +//------------------------------------------------------------------------------ +void UInputManager::disableKeyboard() +{ + if ( !isEnabled() || !isKeyboardEnabled()) + return; + + deactivateKeyboard(); + mKeyboardEnabled = false; + Con::printf( "Keyboard disabled." ); +#ifdef LOG_INPUT + Input::log( "Keyboard disabled.\n" ); +#endif +} + +//------------------------------------------------------------------------------ +bool UInputManager::activateKeyboard() +{ + if ( !isEnabled() || !isActive() || !isKeyboardEnabled() ) + return( false ); + + mKeyboardActive = true; +#ifdef LOG_INPUT + Input::log( mKeyboardActive ? "Keyboard activated.\n" : "Keyboard failed to activate!\n" ); +#endif + return( mKeyboardActive ); +} + +//------------------------------------------------------------------------------ +void UInputManager::deactivateKeyboard() +{ + if ( isEnabled() && isKeyboardActive() ) + { + mKeyboardActive = false; +#ifdef LOG_INPUT + Input::log( "Keyboard deactivated.\n" ); +#endif + } +} + +//------------------------------------------------------------------------------ +bool UInputManager::enableMouse() +{ + if ( !isEnabled() ) + return( false ); + + if ( isMouseEnabled() && isMouseActive() ) + return( true ); + + mMouseEnabled = true; + if ( isActive() ) + mMouseEnabled = activateMouse(); + + if ( mMouseEnabled ) + { + Con::printf( "Mouse enabled." ); +#ifdef LOG_INPUT + Input::log( "Mouse enabled.\n" ); +#endif + } + else + { + Con::warnf( "Mouse failed to enable!" ); +#ifdef LOG_INPUT + Input::log( "Mouse failed to enable!\n" ); +#endif + } + + return( mMouseEnabled ); +} + +//------------------------------------------------------------------------------ +void UInputManager::disableMouse() +{ + if ( !isEnabled() || !isMouseEnabled()) + return; + + deactivateMouse(); + mMouseEnabled = false; + Con::printf( "Mouse disabled." ); +#ifdef LOG_INPUT + Input::log( "Mouse disabled.\n" ); +#endif +} + +//------------------------------------------------------------------------------ +bool UInputManager::activateMouse() +{ + if ( !isEnabled() || !isActive() || !isMouseEnabled() ) + return( false ); + + mMouseActive = true; +#ifdef LOG_INPUT + Input::log( mMouseActive ? + "Mouse activated.\n" : "Mouse failed to activate!\n" ); +#endif + return( mMouseActive ); +} + +//------------------------------------------------------------------------------ +void UInputManager::deactivateMouse() +{ + if ( isEnabled() && isMouseActive() ) + { + mMouseActive = false; +#ifdef LOG_INPUT + Input::log( "Mouse deactivated.\n" ); +#endif + } +} + +//------------------------------------------------------------------------------ +bool UInputManager::enableJoystick() +{ + if ( !isEnabled() ) + return( false ); + + if ( isJoystickEnabled() && isJoystickActive() ) + return( true ); + + mJoystickEnabled = true; + if ( isActive() ) + mJoystickEnabled = activateJoystick(); + + if ( mJoystickEnabled ) + { + Con::printf( "Joystick enabled." ); +#ifdef LOG_INPUT + Input::log( "Joystick enabled.\n" ); +#endif + } + else + { + Con::warnf( "Joystick failed to enable!" ); +#ifdef LOG_INPUT + Input::log( "Joystick failed to enable!\n" ); +#endif + } + + return( mJoystickEnabled ); +} + +//------------------------------------------------------------------------------ +void UInputManager::disableJoystick() +{ + if ( !isEnabled() || !isJoystickEnabled()) + return; + + deactivateJoystick(); + mJoystickEnabled = false; + Con::printf( "Joystick disabled." ); +#ifdef LOG_INPUT + Input::log( "Joystick disabled.\n" ); +#endif +} + +//------------------------------------------------------------------------------ +bool UInputManager::activateJoystick() +{ + if ( !isEnabled() || !isActive() || !isJoystickEnabled() ) + return( false ); + + mJoystickActive = false; + JoystickInputDevice* dptr; + for ( iterator ptr = begin(); ptr != end(); ptr++ ) + { + dptr = dynamic_cast( *ptr ); + if ( dptr && dptr->getDeviceType() == JoystickDeviceType) + if ( dptr->activate() ) + mJoystickActive = true; + } +#ifdef LOG_INPUT + Input::log( mJoystickActive ? + "Joystick activated.\n" : "Joystick failed to activate!\n" ); +#endif + return( mJoystickActive ); +} + +//------------------------------------------------------------------------------ +void UInputManager::deactivateJoystick() +{ + if ( isEnabled() && isJoystickActive() ) + { + mJoystickActive = false; + JoystickInputDevice* dptr; + for ( iterator ptr = begin(); ptr != end(); ptr++ ) + { + dptr = dynamic_cast( *ptr ); + if ( dptr && dptr->getDeviceType() == JoystickDeviceType) + dptr->deactivate(); + } +#ifdef LOG_INPUT + Input::log( "Joystick deactivated.\n" ); +#endif + } +} + +//------------------------------------------------------------------------------ +const char* UInputManager::getJoystickAxesString( U32 deviceID ) +{ + for (Vector::iterator iter = mJoystickList.begin(); + iter != mJoystickList.end(); + ++iter) + { + if ((*iter)->getDeviceID() == deviceID) + return (*iter)->getJoystickAxesString(); + } + return( "" ); +} + +//============================================================================== +// JoystickInputDevice +//============================================================================== +JoystickInputDevice::JoystickInputDevice(U8 deviceID) +{ + mActive = false; + mStick = NULL; + mAxisList.clear(); + mDeviceID = deviceID; + dSprintf(mName, 29, "joystick%d", mDeviceID); + + mButtonState.clear(); + mHatState.clear(); + mNumAxes = mNumButtons = mNumHats = mNumBalls = 0; + + loadJoystickInfo(); + + // initialize state variables + for (int i = 0; i < mNumButtons; ++i) + mButtonState.push_back(false); // all buttons unpressed initially + + for (int i = 0; i < mNumHats; ++i) + mHatState.push_back(SDL_HAT_CENTERED); // hats centered initially +} + +//------------------------------------------------------------------------------ +JoystickInputDevice::~JoystickInputDevice() +{ + if (isActive()) + deactivate(); +} + +//------------------------------------------------------------------------------ +bool JoystickInputDevice::activate() +{ + if (isActive()) + return true; + + // open the stick + mStick = SDL_JoystickOpen(mDeviceID); + if (mStick == NULL) + { + Con::printf("Unable to activate %s: %s", getDeviceName(), SDL_GetError()); + return false; + } + + // reload axis mapping info + loadAxisInfo(); + + mActive = true; + return true; +} + +//------------------------------------------------------------------------------ +bool JoystickInputDevice::deactivate() +{ + if (!isActive()) + return true; + + if (mStick != NULL) + { + SDL_JoystickClose(mStick); + mStick = NULL; + } + + mActive = false; + return true; +} + +//------------------------------------------------------------------------------ +const char* JoystickInputDevice::getName() +{ + return SDL_JoystickName(mDeviceID); +} + +//------------------------------------------------------------------------------ +void JoystickInputDevice::reset() +{ + UInputManager* manager = dynamic_cast(Input::getManager()); + if (!manager) + return; + + // clear joystick state variables + + // buttons + for (int i = 0; i < mButtonState.size(); ++i) + if (mButtonState[i]) + { + manager->joyButtonEvent(mDeviceID, i, false); + mButtonState[i] = false; + } + + // hats + for (int i = 0; i < mHatState.size(); ++i) + if (mHatState[i] != SDL_HAT_CENTERED) + { + manager->joyHatEvent(mDeviceID, i, mHatState[i], SDL_HAT_CENTERED); + mHatState[i] = SDL_HAT_CENTERED; + } + + // axis and ball state is not maintained +} + +//------------------------------------------------------------------------------ +bool JoystickInputDevice::process() +{ + if (!isActive()) + return false; + + UInputManager* manager = dynamic_cast(Input::getManager()); + if (!manager) + return false; + + // axes + for (int i = 0; i < mNumAxes; ++i) + { + // skip the axis if we don't have a mapping for it + if (mAxisList[i].type == -1) + continue; + manager->joyAxisEvent(mDeviceID, i, SDL_JoystickGetAxis(mStick, i)); + } + + // buttons + for (int i = 0; i < mNumButtons; ++i) + { + if (bool(SDL_JoystickGetButton(mStick, i)) == + mButtonState[i]) + continue; + mButtonState[i] = !mButtonState[i]; + manager->joyButtonEvent(mDeviceID, i, mButtonState[i]); + } + + // hats + for (int i = 0; i < mNumHats; ++i) + { + U8 currHatState = SDL_JoystickGetHat(mStick, i); + if (mHatState[i] == currHatState) + continue; + + manager->joyHatEvent(mDeviceID, i, mHatState[i], currHatState); + mHatState[i] = currHatState; + } + + // ballz + // JMQTODO: how to map ball events (xaxis,yaxis?) + return true; +} + +//------------------------------------------------------------------------------ +static S32 GetAxisType(S32 axisNum, const char* namedType) +{ + S32 axisType = -1; + + if (namedType != NULL) + { + if (dStricmp(namedType, "xaxis")==0) + axisType = SI_XAXIS; + else if (dStricmp(namedType, "yaxis")==0) + axisType = SI_YAXIS; + else if (dStricmp(namedType, "zaxis")==0) + axisType = SI_ZAXIS; + else if (dStricmp(namedType, "rxaxis")==0) + axisType = SI_RXAXIS; + else if (dStricmp(namedType, "ryaxis")==0) + axisType = SI_RYAXIS; + else if (dStricmp(namedType, "rzaxis")==0) + axisType = SI_RZAXIS; + else if (dStricmp(namedType, "slider")==0) + axisType = SI_SLIDER; + } + + if (axisType == -1) + { + // use a hardcoded default mapping if possible + switch (axisNum) + { + case 0: + axisType = SI_XAXIS; + break; + case 1: + axisType = SI_YAXIS; + break; + case 2: + axisType = SI_RZAXIS; + break; + case 3: + axisType = SI_SLIDER; + break; + } + } + + return axisType; +} + +//------------------------------------------------------------------------------ +void JoystickInputDevice::loadJoystickInfo() +{ + bool opened = false; + if (mStick == NULL) + { + mStick = SDL_JoystickOpen(mDeviceID); + if (mStick == NULL) + { + Con::printf("Unable to open %s: %s", getDeviceName(), SDL_GetError()); + return; + } + opened = true; + } + + // get the number of thingies on this joystick + mNumAxes = SDL_JoystickNumAxes(mStick); + mNumButtons = SDL_JoystickNumButtons(mStick); + mNumHats = SDL_JoystickNumHats(mStick); + mNumBalls = SDL_JoystickNumBalls(mStick); + + // load axis mapping info + loadAxisInfo(); + + if (opened) + SDL_JoystickClose(mStick); +} + +//------------------------------------------------------------------------------ +// for each axis on a joystick, torque needs to know the type of the axis +// (SI_XAXIS, etc), the minimum value, and the maximum value. However none of +// this information is generally available with the unix/linux api. All you +// get is a device and axis number and a value. Therefore, +// we allow the user to specify these values in preferences. hopefully +// someday we can implement a gui joystick calibrator that takes care of this +// cruft for the user. +void JoystickInputDevice::loadAxisInfo() +{ + mAxisList.clear(); + + AssertFatal(mStick, "mStick is NULL"); + + static int AxisDefaults[] = { SI_XAXIS, SI_YAXIS, SI_ZAXIS, + SI_RXAXIS, SI_RYAXIS, SI_RZAXIS, + SI_SLIDER }; + + int numAxis = SDL_JoystickNumAxes(mStick); + for (int i = 0; i < numAxis; ++i) + { + JoystickAxisInfo axisInfo; + + // defaults + axisInfo.type = -1; + axisInfo.minValue = -32768; + axisInfo.maxValue = 32767; + + // look in console to see if there is mapping information for this axis + const int TempBufSize = 1024; + char tempBuf[TempBufSize]; + dSprintf(tempBuf, TempBufSize, "$Pref::Input::Joystick%d::Axis%d", + mDeviceID, i); + + const char* axisStr = Con::getVariable(tempBuf); + if (axisStr == NULL || dStrlen(axisStr) == 0) + { + if (i < sizeof(AxisDefaults)) + axisInfo.type = AxisDefaults[i]; + } + else + { + // format is "TorqueAxisName MinValue MaxValue"; + dStrncpy(tempBuf, axisStr, TempBufSize); + char* temp = dStrtok( tempBuf, " \0" ); + if (temp) + { + axisInfo.type = GetAxisType(i, temp); + temp = dStrtok( NULL, " \0" ); + if (temp) + { + axisInfo.minValue = dAtoi(temp); + temp = dStrtok( NULL, "\0" ); + if (temp) + { + axisInfo.maxValue = dAtoi(temp); + } + } + } + } + + mAxisList.push_back(axisInfo); + } +} + +//------------------------------------------------------------------------------ +const char* JoystickInputDevice::getJoystickAxesString() +{ + char buf[64]; + dSprintf( buf, sizeof( buf ), "%d", mAxisList.size()); + + for (Vector::iterator iter = mAxisList.begin(); + iter != mAxisList.end(); + ++iter) + { + switch ((*iter).type) + { + case SI_XAXIS: + dStrcat( buf, "\tX" ); + break; + case SI_YAXIS: + dStrcat( buf, "\tY" ); + break; + case SI_ZAXIS: + dStrcat( buf, "\tZ" ); + break; + case SI_RXAXIS: + dStrcat( buf, "\tR" ); + break; + case SI_RYAXIS: + dStrcat( buf, "\tU" ); + break; + case SI_RZAXIS: + dStrcat( buf, "\tV" ); + break; + case SI_SLIDER: + dStrcat( buf, "\tS" ); + break; + } + } + + char* returnString = Con::getReturnBuffer( dStrlen( buf ) + 1 ); + dStrcpy( returnString, buf ); + return( returnString ); +} + + +//============================================================================== +// Console Functions +//============================================================================== +ConsoleFunction( activateKeyboard, bool, 1, 1, "activateKeyboard()" ) +{ + UInputManager* mgr = dynamic_cast( Input::getManager() ); + if ( mgr ) + return( mgr->activateKeyboard() ); + + return( false ); +} + +// JMQ: disabled deactivateKeyboard since the script calls it but there is +// no fallback keyboard input in unix, resulting in a permanently disabled +// keyboard +//------------------------------------------------------------------------------ +ConsoleFunction( deactivateKeyboard, void, 1, 1, "deactivateKeyboard()" ) +{ +#if 0 + UInputManager* mgr = dynamic_cast( Input::getManager() ); + if ( mgr ) + mgr->deactivateKeyboard(); +#endif +} + +//------------------------------------------------------------------------------ +ConsoleFunction( enableMouse, bool, 1, 1, "enableMouse()" ) +{ + UInputManager* mgr = dynamic_cast( Input::getManager() ); + if ( mgr ) + return( mgr->enableMouse() ); + + return ( false ); +} + +//------------------------------------------------------------------------------ +ConsoleFunction( disableMouse, void, 1, 1, "disableMouse()" ) +{ + UInputManager* mgr = dynamic_cast( Input::getManager() ); + if ( mgr ) + mgr->disableMouse(); +} + +//------------------------------------------------------------------------------ +ConsoleFunction( enableJoystick, bool, 1, 1, "enableJoystick()" ) +{ + UInputManager* mgr = dynamic_cast( Input::getManager() ); + if ( mgr ) + return( mgr->enableJoystick() ); + + return ( false ); +} + +//------------------------------------------------------------------------------ +ConsoleFunction( disableJoystick, void, 1, 1, "disableJoystick()" ) +{ + UInputManager* mgr = dynamic_cast( Input::getManager() ); + if ( mgr ) + mgr->disableJoystick(); +} + +//------------------------------------------------------------------------------ +ConsoleFunction( enableLocking, void, 1, 1, "enableLocking()" ) +{ + UInputManager* mgr = dynamic_cast( Input::getManager() ); + if ( mgr ) + mgr->setLocking(true); +} + +//------------------------------------------------------------------------------ +ConsoleFunction( disableLocking, void, 1, 1, "disableLocking()" ) +{ + UInputManager* mgr = dynamic_cast( Input::getManager() ); + if ( mgr ) + mgr->setLocking(false); +} + +//------------------------------------------------------------------------------ +ConsoleFunction( toggleLocking, void, 1, 1, "toggleLocking()" ) +{ + UInputManager* mgr = dynamic_cast( Input::getManager() ); + if ( mgr ) + mgr->setLocking(!mgr->getLocking()); +} + +//------------------------------------------------------------------------------ +ConsoleFunction( echoInputState, void, 1, 1, "echoInputState()" ) +{ + UInputManager* mgr = dynamic_cast( Input::getManager() ); + if ( mgr && mgr->isEnabled() ) + { + Con::printf( "Input is enabled %s.", + mgr->isActive() ? "and active" : "but inactive" ); + Con::printf( "- Keyboard is %sabled and %sactive.", + mgr->isKeyboardEnabled() ? "en" : "dis", + mgr->isKeyboardActive() ? "" : "in" ); + Con::printf( "- Mouse is %sabled and %sactive.", + mgr->isMouseEnabled() ? "en" : "dis", + mgr->isMouseActive() ? "" : "in" ); + Con::printf( "- Joystick is %sabled and %sactive.", + mgr->isJoystickEnabled() ? "en" : "dis", + mgr->isJoystickActive() ? "" : "in" ); + } + else + Con::printf( "Input is not enabled." ); +} diff --git a/Engine/source/platformX86UNIX/x86UNIXInputManager.h b/Engine/source/platformX86UNIX/x86UNIXInputManager.h new file mode 100644 index 000000000..3420be377 --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXInputManager.h @@ -0,0 +1,190 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _X86UNIXINPUTMANAGER_H_ +#define _X86UNIXINPUTMANAGER_H_ + +#include "core/tVector.h" +#include "platform/platformInput.h" +#include "platformX86UNIX/platformX86UNIX.h" + +#include + +#define NUM_KEYS ( KEY_OEM_102 + 1 ) +#define KEY_FIRST KEY_ESCAPE + +struct AsciiData +{ + struct KeyData + { + U16 ascii; + bool isDeadChar; + }; + + KeyData upper; + KeyData lower; + KeyData goofy; +}; + +typedef struct _SDL_Joystick; + +struct JoystickAxisInfo +{ + S32 type; + S32 minValue; + S32 maxValue; +}; + +//------------------------------------------------------------------------------ +class JoystickInputDevice : public InputDevice +{ + public: + JoystickInputDevice(U8 deviceID); + ~JoystickInputDevice(); + + bool activate(); + bool deactivate(); + bool isActive() { return( mActive ); } + + U8 getDeviceType() { return( JoystickDeviceType ); } + U8 getDeviceID() { return( mDeviceID ); } + const char* getName(); + const char* getJoystickAxesString(); + + void loadJoystickInfo(); + void loadAxisInfo(); + JoystickAxisInfo& getAxisInfo(int axisNum) { return mAxisList[axisNum]; } + + bool process(); + void reset(); + + private: + bool mActive; + U8 mDeviceID; + SDL_Joystick* mStick; + Vector mAxisList; + Vector mButtonState; + Vector mHatState; + + S32 mNumAxes; + S32 mNumButtons; + S32 mNumHats; + S32 mNumBalls; +}; + +//------------------------------------------------------------------------------ +class UInputManager : public InputManager +{ + friend bool JoystickInputDevice::process(); // for joystick event funcs + friend void JoystickInputDevice::reset(); + + public: + UInputManager(); + + void init(); + bool enable(); + void disable(); + void activate(); + void deactivate(); + void setWindowLocked(bool locked); + bool isActive() { return( mActive ); } + + void onDeleteNotify( SimObject* object ); + bool onAdd(); + void onRemove(); + + void process(); + + bool enableKeyboard(); + void disableKeyboard(); + bool isKeyboardEnabled() { return( mKeyboardEnabled ); } + bool activateKeyboard(); + void deactivateKeyboard(); + bool isKeyboardActive() { return( mKeyboardActive ); } + + bool enableMouse(); + void disableMouse(); + bool isMouseEnabled() { return( mMouseEnabled ); } + bool activateMouse(); + void deactivateMouse(); + bool isMouseActive() { return( mMouseActive ); } + + bool enableJoystick(); + void disableJoystick(); + bool isJoystickEnabled() { return( mJoystickEnabled ); } + bool activateJoystick(); + void deactivateJoystick(); + bool isJoystickActive() { return( mJoystickActive ); } + + void setLocking(bool enabled); + bool getLocking() { return mLocking; } + + const char* getJoystickAxesString( U32 deviceID ); + bool joystickDetected() { return mJoystickList.size() > 0; } + private: + typedef SimGroup Parent; + // the following vector is just for quick access during event processing. + // it does not manage the cleanup of the JoystickInputDevice objects + Vector mJoystickList; + + bool mKeyboardEnabled; + bool mMouseEnabled; + bool mJoystickEnabled; + + bool mKeyboardActive; + bool mMouseActive; + bool mJoystickActive; + + bool mActive; + + // Device state variables + S32 mModifierKeys; + bool mKeyboardState[256]; + bool mMouseButtonState[3]; + + // last mousex and y are maintained when window is unlocked + S32 mLastMouseX; + S32 mLastMouseY; + + void initJoystick(); + + void resetKeyboardState(); + void resetMouseState(); + void resetInputState(); + + void lockInput(); + void unlockInput(); + bool mLocking; + + void joyHatEvent(U8 deviceID, U8 hatNum, + U8 prevHatState, U8 currHatState); + void joyButtonEvent(U8 deviceID, U8 buttonNum, bool pressed); + void joyButtonEvent(const SDL_Event& event); + void joyAxisEvent(const SDL_Event& event); + void joyAxisEvent(U8 deviceID, U8 axisNum, S16 axisValue); + void mouseButtonEvent(const SDL_Event& event); + void mouseMotionEvent(const SDL_Event& event); + void keyEvent(const SDL_Event& event); + bool processKeyEvent(InputEvent &event); +}; + +#endif // _H_X86UNIXINPUTMANAGER_ diff --git a/Engine/source/platformX86UNIX/x86UNIXMain.cpp b/Engine/source/platformX86UNIX/x86UNIXMain.cpp new file mode 100644 index 000000000..75228f21f --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXMain.cpp @@ -0,0 +1,148 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "platform/platformInput.h" +#include "console/console.h" + +#include "platformX86UNIX/platformX86UNIX.h" +#include "platformX86UNIX/x86UNIXStdConsole.h" + +//------------------------------------------------------------------------------ +void Platform::init() +{ + Con::printf("Initializing platform..."); + + // Set the platform variable for the scripts + Con::setVariable( "$platform", "x86UNIX" ); +#if defined(__linux__) + Con::setVariable( "$platformUnixType", "Linux" ); +#elif defined(__OpenBSD__) + Con::setVariable( "$platformUnixType", "OpenBSD" ); +#else + Con::setVariable( "$platformUnixType", "Unknown" ); +#endif + + StdConsole::create(); + + Input::init(); + + //installRedBookDevices(); + +#if 0 +#ifndef DEDICATED + // if we're not dedicated do more initialization + if (!x86UNIXState->isDedicated()) + { + // init SDL + if (!InitSDL()) + { + DisplayErrorAlert("Unable to initialize SDL."); + ImmediateShutdown(1); + } + + Con::printf( "Video Init:" ); + + // load gl library + if (!GLLoader::OpenGLInit()) + { + DisplayErrorAlert("Unable to initialize OpenGL."); + ImmediateShutdown(1); + } + + // initialize video + Video::init(); + if ( Video::installDevice( OpenGLDevice::create() ) ) + Con::printf( " OpenGL display device detected." ); + else + Con::printf( " OpenGL display device not detected." ); + + Con::printf(" "); + } +#endif + // if we are dedicated, do sleep timing and display results + if (x86UNIXState->isDedicated()) + { + const S32 MaxSleepIter = 10; + U32 totalSleepTime = 0; + U32 start; + for (S32 i = 0; i < MaxSleepIter; ++i) + { + start = Platform::getRealMilliseconds(); + Sleep(0, 1000000); + totalSleepTime += Platform::getRealMilliseconds() - start; + } + U32 average = static_cast(totalSleepTime / MaxSleepIter); + + Con::printf("Sleep latency: %ums", average); + // dPrintf as well, since console output won't be visible yet + dPrintf("Sleep latency: %ums\n", average); + if (!x86UNIXState->getDSleep() && average < 10) + { + const char* msg = "Sleep latency ok, enabling dsleep for lower cpu " \ + "utilization"; + Con::printf("%s", msg); + dPrintf("%s\n", msg); + x86UNIXState->setDSleep(true); + } + } +#endif +} + +//------------------------------------------------------------------------------ +void Platform::shutdown() +{ + Cleanup(); +} + +//------------------------------------------------------------------------------ + + +extern "C" +{ + bool torque_engineinit(int argc, const char **argv); + int torque_enginetick(); + bool torque_engineshutdown(); + + int torque_unixmain(int argc, const char **argv) + { + if (!torque_engineinit(argc, argv)) + return 1; + + while(torque_enginetick()) + { + + } + + torque_engineshutdown(); + + return 0; + + } +} + +extern S32 TorqueMain(S32 argc, const char **argv); + +int main(int argc, const char **argv) +{ + return TorqueMain(argc, argv); +} diff --git a/Engine/source/platformX86UNIX/x86UNIXMath.cpp b/Engine/source/platformX86UNIX/x86UNIXMath.cpp new file mode 100644 index 000000000..300515083 --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXMath.cpp @@ -0,0 +1,146 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/console.h" +#include "math/mMath.h" +#include "core/strings/stringFunctions.h" + + +extern void mInstallLibrary_C(); +extern void mInstallLibrary_ASM(); + + +extern void mInstall_AMD_Math(); +extern void mInstall_Library_SSE(); + + +//-------------------------------------- +ConsoleFunction( MathInit, void, 1, 10, "(detect|C|FPU|MMX|3DNOW|SSE|...)") +{ + U32 properties = CPU_PROP_C; // C entensions are always used + + if (argc == 1) + { + Math::init(0); + return; + } + for (argc--, argv++; argc; argc--, argv++) + { + if (dStricmp(*argv, "DETECT") == 0) { + Math::init(0); + return; + } + if (dStricmp(*argv, "C") == 0) { + properties |= CPU_PROP_C; + continue; + } + if (dStricmp(*argv, "FPU") == 0) { + properties |= CPU_PROP_FPU; + continue; + } + if (dStricmp(*argv, "MMX") == 0) { + properties |= CPU_PROP_MMX; + continue; + } + if (dStricmp(*argv, "3DNOW") == 0) { + properties |= CPU_PROP_3DNOW; + continue; + } + if (dStricmp(*argv, "SSE") == 0) { + properties |= CPU_PROP_SSE; + continue; + } + Con::printf("Error: MathInit(): ignoring unknown math extension '%s'", *argv); + } + Math::init(properties); +} + + + +//------------------------------------------------------------------------------ +void Math::init(U32 properties) +{ + if (!properties) + // detect what's available + properties = Platform::SystemInfo.processor.properties; + else + // Make sure we're not asking for anything that's not supported + properties &= Platform::SystemInfo.processor.properties; + + Con::printf("Math Init:"); + Con::printf(" Installing Standard C extensions"); + mInstallLibrary_C(); + + Con::printf(" Installing Assembly extensions"); + mInstallLibrary_ASM(); + + if (properties & CPU_PROP_FPU) + { + Con::printf(" Installing FPU extensions"); + } + + if (properties & CPU_PROP_MMX) + { + Con::printf(" Installing MMX extensions"); + if (properties & CPU_PROP_3DNOW) + { + Con::printf(" Installing 3DNow extensions"); + mInstall_AMD_Math(); + } + } + +#if !defined(__MWERKS__) || (__MWERKS__ >= 0x2400) + if (properties & CPU_PROP_SSE) + { + Con::printf(" Installing SSE extensions"); + mInstall_Library_SSE(); + } +#endif //mwerks>2.4 + + Con::printf(" "); +} + + +//------------------------------------------------------------------------------ + +static MRandomLCG sgPlatRandom; + +F32 Platform::getRandom() +{ + return sgPlatRandom.randF(); +} + +U32 Platform::getMathControlState() +{ + return 0; +} + +void Platform::setMathControlStateKnown() +{ + +} + +void Platform::setMathControlState(U32 state) +{ + +} diff --git a/Engine/source/platformX86UNIX/x86UNIXMath_ASM.cpp b/Engine/source/platformX86UNIX/x86UNIXMath_ASM.cpp new file mode 100644 index 000000000..4c691e983 --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXMath_ASM.cpp @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "math/mMath.h" + +static S32 m_mulDivS32_ASM(S32 a, S32 b, S32 c) +{ // a * b / c + S32 r; + + __asm__ __volatile__( + "imul %2\n" + "idiv %3\n" + : "=a" (r) : "a" (a) , "b" (b) , "c" (c) + ); + return r; +} + + +static U32 m_mulDivU32_ASM(S32 a, S32 b, U32 c) +{ // a * b / c + S32 r; + __asm__ __volatile__( + "mov $0, %%edx\n" + "mul %2\n" + "div %3\n" + : "=a" (r) : "a" (a) , "b" (b) , "c" (c) + ); + return r; +} + +//------------------------------------------------------------------------------ +void mInstallLibrary_ASM() +{ + m_mulDivS32 = m_mulDivS32_ASM; + m_mulDivU32 = m_mulDivU32_ASM; +} diff --git a/Engine/source/platformX86UNIX/x86UNIXMemory.cpp b/Engine/source/platformX86UNIX/x86UNIXMemory.cpp new file mode 100644 index 000000000..7567bdb6f --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXMemory.cpp @@ -0,0 +1,68 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformX86UNIX/platformX86UNIX.h" +#include +#include + +void* dMemcpy(void *dst, const void *src, unsigned size) +{ + return memcpy(dst,src,size); +} + +//-------------------------------------- +void* dMemmove(void *dst, const void *src, unsigned size) +{ + return memmove(dst,src,size); +} + +//-------------------------------------- +void* dMemset(void *dst, S32 c, unsigned size) +{ + return memset(dst,c,size); +} + +//-------------------------------------- +S32 dMemcmp(const void *ptr1, const void *ptr2, unsigned len) +{ + return memcmp(ptr1, ptr2, len); +} + +void* dRealMalloc(dsize_t s) +{ + return malloc(s); +} + +void dRealFree(void* p) +{ + free(p); +} + +void *dMalloc_aligned(dsize_t in_size, int alignment) +{ + return _mm_malloc(in_size, alignment); +} + +void dFree_aligned(void* p) +{ + return _mm_free(p); +} diff --git a/Engine/source/platformX86UNIX/x86UNIXMessageBox.client.cpp b/Engine/source/platformX86UNIX/x86UNIXMessageBox.client.cpp new file mode 100644 index 000000000..e54c6eb9b --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXMessageBox.client.cpp @@ -0,0 +1,495 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include +#include +#include + +#include +#include +#include + +#include "platformX86UNIX/x86UNIXMessageBox.h" + +#define MessageBox_MaxWinWidth 800 +#define MessageBox_MaxWinHeight 600 +#define MessageBox_MinWinWidth 450 + +#define MessageBox_ButtonBoxWidth 60 +#define MessageBox_ButtonBoxHeight 22 +#define MessageBox_ButtonSpacer 20 +#define MessageBox_ButtonVMargin 10 +#define MessageBox_ButtonHMargin 10 + +#define MessageBox_LineSpacer 2 +#define MessageBox_LineVMargin 10 +#define MessageBox_LineHMargin 10 + +XMessageBoxButton::XMessageBoxButton() +{ + strcpy(mLabel, ""); + mClickVal = -1; + mLabelWidth = mX = mY = mWidth = mHeight = mMouseX = mMouseX = -1; + mMouseDown = false; +} + +XMessageBoxButton::XMessageBoxButton(const char* label, int clickVal) +{ + strncpy(mLabel, label, LabelSize); + mClickVal = clickVal; + mLabelWidth = mX = mY = mWidth = mHeight = mMouseX = mMouseX = -1; + mMouseDown = false; +} + +XMessageBox::XMessageBox(Display* display) +{ + mMessage = ""; + mFS = NULL; + mDisplay = display; +} + +XMessageBox::~XMessageBox() +{ + clearMessageLines(); + if (mDisplay != NULL) + { + mDisplay = NULL; + } +} + +int XMessageBox::alertOK(const char *windowTitle, const char *message) +{ + mMessage = message; + mTitle = windowTitle; + mButtons.clear(); + mButtons.push_back(XMessageBoxButton("OK", OK)); + return show(); +} + +int XMessageBox::alertOKCancel(const char *windowTitle, const char *message) +{ + mMessage = message; + mTitle = windowTitle; + mButtons.clear(); + mButtons.push_back(XMessageBoxButton("OK", OK)); + mButtons.push_back(XMessageBoxButton("Cancel", Cancel)); + return show(); +} + +int XMessageBox::alertRetryCancel(const char *windowTitle, const char *message) +{ + mMessage = message; + mTitle = windowTitle; + mButtons.clear(); + mButtons.push_back(XMessageBoxButton("Retry", Retry)); + mButtons.push_back(XMessageBoxButton("Cancel", Cancel)); + return show(); +} + +void XMessageBox::repaint() +{ + int white = WhitePixel(mDisplay, DefaultScreen(mDisplay)); + int black = BlackPixel(mDisplay, DefaultScreen(mDisplay)); + + int x = 0; + int y = 0; + + // line V margin + y = y + MessageBox_LineVMargin * 2; + + // line H margin + x = MessageBox_LineHMargin; + + XSetForeground(mDisplay, mGC, black); + for (unsigned int i = 0; i < mMessageLines.size(); ++i) + { + XDrawString(mDisplay, mWin, mGC, x, y, mMessageLines[i], + strlen(mMessageLines[i])); + if (i < (mMessageLines.size() - 1)) + y = y + MessageBox_LineSpacer + mFontHeight; + } + XFlush(mDisplay); + + // line V margin + y = y + MessageBox_LineVMargin; + + int maxButWidth = MessageBox_ButtonBoxWidth; + int maxButHeight = MessageBox_ButtonBoxHeight; + + // compute size of text labels on buttons + int fgColor, bgColor; + + int fontDirection, fontAscent, fontDescent; + Vector::iterator iter; + for (iter = mButtons.begin(); iter != mButtons.end(); ++iter) + { + XCharStruct strInfo; + XTextExtents(mFS, iter->getLabel(), strlen(iter->getLabel()), + &fontDirection, &fontAscent, &fontDescent, + &strInfo); +// if (maxButWidth < strInfo.width) +// maxButWidth = strInfo.width; +// if (maxButHeight < (strInfo.ascent + strInfo.descent)) +// maxButHeight = (strInfo.ascent + strInfo.descent); + iter->setLabelWidth(strInfo.width); + } + int buttonBoxWidth = maxButWidth; + int buttonBoxHeight = maxButHeight; + + // draw buttons + // button V margin + y = y + MessageBox_ButtonVMargin; + + // center the buttons + x = MessageBox_ButtonHMargin + (mMBWidth - getButtonLineWidth()) / 2; + + for (iter = mButtons.begin(); iter != mButtons.end(); ++iter) + { + if (iter->drawReverse()) + { + fgColor = white; + bgColor = black; + } + else + { + fgColor = black; + bgColor = white; + } + + XSetForeground(mDisplay, mGC, bgColor); + XFillRectangle(mDisplay, mWin, mGC, x, y, + buttonBoxWidth, buttonBoxHeight); + XSetForeground(mDisplay, mGC, fgColor); + XDrawRectangle(mDisplay, mWin, mGC, x, y, + buttonBoxWidth, buttonBoxHeight); + XDrawString(mDisplay, mWin, mGC, + x + ((buttonBoxWidth - iter->getLabelWidth()) / 2), + y + mFontAscent + ((buttonBoxHeight - mFontAscent) / 2), + iter->getLabel(), + strlen(iter->getLabel())); + iter->setButtonRect(x, y, buttonBoxWidth, buttonBoxHeight); + x = x + buttonBoxWidth + MessageBox_ButtonSpacer; + } +} + +template +static inline Type max(Type v1, Type v2) +{ + if (v1 <= v2) + return v2; + else + return v1; +} + +template +static inline Type min(Type v1, Type v2) +{ + if (v1 > v2) + return v2; + else + return v1; +} + +void XMessageBox::clearMessageLines() +{ + Vector::iterator iter; + for (iter = mMessageLines.begin(); iter != mMessageLines.end(); ++iter) + delete [] *iter; + mMessageLines.clear(); +} + +void XMessageBox::splitMessage() +{ + clearMessageLines(); + if (mMessage == NULL || strlen(mMessage)==0) + // JMQTODO: what to do with empty strings? + return; + + // need to break message up in to lines, and store lines in + // mMessageLines + + int numChars = strlen(mMessage); + const int ScratchBufSize = 2048; + char scratchBuf[ScratchBufSize]; + memset(scratchBuf, 0, ScratchBufSize); + + int fontDirection, fontAscent, fontDescent; + XCharStruct strInfo; + + char *curChar = const_cast(mMessage); + char *endChar; + char *curWrapped = scratchBuf; + int curWidth = 0; + int maxWidth = mMaxWindowWidth - (MessageBox_LineHMargin); + + while ( // while pointers are in range... + (curChar - mMessage) < numChars && + (curWrapped - scratchBuf) < ScratchBufSize) + { + // look for next space in remaining string + endChar = index(curChar, ' '); + if (endChar == NULL) + endChar = index(curChar, '\0'); + + if (endChar != NULL) + // increment one char past the space to include it + endChar++; + else + // otherwise, set the endchar to one char ahead + endChar = curChar + 1; + + // compute length of substr + int len = endChar - curChar; + XTextExtents(mFS, curChar, len, + &fontDirection, &fontAscent, &fontDescent, + &strInfo); + // if its too big, time to add a new line... + if ((curWidth + strInfo.width) > maxWidth) + { + // create a new block for the line and add it + *curWrapped = '\0'; + int len = strlen(scratchBuf); + char* line = new char[len+1]; + strncpy(line, scratchBuf, len+1); // +1 gets the null char + mMessageLines.push_back(line); + + // reset curWrapped to the beginning of the scratch buffer + curWrapped = scratchBuf; + curWidth = 0; + } + // copy the current string into curWrapped if we have enough room + int bytesRemaining = + ScratchBufSize - (curWrapped - scratchBuf); + if (bytesRemaining >= len) + strncpy(curWrapped, curChar, len); + + curWrapped += len; + curWidth += strInfo.width; + curChar = endChar; + } + + // make a final line out of any leftover stuff in the scratch buffer + if (curWrapped != scratchBuf) + { + *curWrapped = '\0'; + int len = strlen(scratchBuf); + char* line = new char[len+1]; + strncpy(line, scratchBuf, len+1); // +1 gets the null char + mMessageLines.push_back(line); + } +} + +int XMessageBox::loadFont() +{ + // load the font + mFS = XLoadQueryFont(mDisplay, + "-*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*"); + + if (mFS == NULL) + mFS = XLoadQueryFont(mDisplay, "fixed"); + + if (mFS == NULL) + return -1; + + // dummy call to XTextExtents to get the font specs + XCharStruct strInfo; + + XTextExtents(mFS, "foo", 1, + &mFontDirection, &mFontAscent, &mFontDescent, + &strInfo); + + mFontHeight = mFontAscent + mFontDescent; + return 0; +} + +int XMessageBox::getButtonLineWidth() +{ + return mButtons.size() * MessageBox_ButtonBoxWidth + + (mButtons.size() - 1) * MessageBox_ButtonSpacer + + MessageBox_ButtonHMargin * 2; +} + +void XMessageBox::setDimensions() +{ + mMBWidth = MessageBox_MaxWinWidth; + mMBHeight = MessageBox_MaxWinHeight; + + // determine width of button line + int buttonWidth = getButtonLineWidth(); + + // if there is only one line, the desired width is the greater of the + // line width and the buttonWidth, otherwise the lineWidth is the + // max possible width which we already set. + if (mMessageLines.size() == 1) + { + XCharStruct strInfo; + int fontDirection, fontAscent, fontDescent; + + XTextExtents(mFS, mMessageLines[0], strlen(mMessageLines[0]), + &fontDirection, &fontAscent, &fontDescent, + &strInfo); + + mMBWidth = max(MessageBox_LineHMargin * 2 + strInfo.width, + buttonWidth); + mMBWidth = max(mMBWidth, MessageBox_MinWinWidth); + } + + // determine the height of the button line + int buttonHeight = MessageBox_ButtonBoxHeight + + MessageBox_ButtonVMargin * 2; + + int lineHeight = mFontHeight * mMessageLines.size() + + (mMessageLines.size() - 1) * MessageBox_LineSpacer + + MessageBox_LineVMargin * 2; + + mMBHeight = buttonHeight + lineHeight; +} + +int XMessageBox::show() +{ + if (mDisplay == NULL) + return -1; + + int retVal = 0; + retVal = loadFont(); + if (retVal < 0) + return retVal; + + // set the maximum window dimensions + mScreenWidth = DisplayWidth(mDisplay, DefaultScreen(mDisplay)); + mScreenHeight = DisplayHeight(mDisplay, DefaultScreen(mDisplay)); + mMaxWindowWidth = min(mScreenWidth, MessageBox_MaxWinWidth); + mMaxWindowHeight = min(mScreenHeight, MessageBox_MaxWinHeight); + + // split the message into a vector of lines + splitMessage(); + + // set the dialog dimensions + setDimensions(); + + mWin = XCreateSimpleWindow( + mDisplay, + DefaultRootWindow(mDisplay), + (mScreenWidth - mMBWidth) / 2, (mScreenHeight - mMBHeight) / 2, + mMBWidth, mMBHeight, + 1, + BlackPixel(mDisplay, DefaultScreen(mDisplay)), + WhitePixel(mDisplay, DefaultScreen(mDisplay))); + + mGC = XCreateGC(mDisplay, mWin, 0, 0); + + XSetFont(mDisplay, mGC, mFS->fid); + + // set input mask + XSelectInput(mDisplay, mWin, + ExposureMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask); + + // set wm protocols in case they hit X + Atom wm_delete_window = + XInternAtom(mDisplay, "WM_DELETE_WINDOW", False); + Atom wm_protocols = + XInternAtom(mDisplay, "WM_PROTOCOLS", False); + XSetWMProtocols (mDisplay, mWin, &wm_delete_window, 1); + // set pop up dialog hint + XSetTransientForHint(mDisplay, mWin, mWin); + + // set title + XTextProperty wtitle; + wtitle.value = (unsigned char *)mTitle; + wtitle.encoding = XA_STRING; + wtitle.format = 8; + wtitle.nitems = strlen(mTitle); + XSetWMName(mDisplay, mWin, &wtitle); + + // show window + XMapWindow(mDisplay, mWin); + // move it in case some bozo window manager repositioned it + XMoveWindow(mDisplay, mWin, + (mScreenWidth - mMBWidth) / 2, (mScreenHeight - mMBHeight) / 2); + // raise it to top + XRaiseWindow(mDisplay, mWin); + + XMessageBoxButton* clickedButton = NULL; + XEvent event; + Vector::iterator iter; + bool done = false; + while (!done) + { + XNextEvent(mDisplay, &event); + switch (event.type) + { + case Expose: + repaint(); + break; + case MotionNotify: + for (iter = mButtons.begin(); iter != mButtons.end(); ++iter) + iter->setMouseCoordinates(event.xmotion.x, event.xmotion.y); + break; + case ButtonPress: + for (iter = mButtons.begin(); iter != mButtons.end(); ++iter) + { + if (iter->pointInRect(event.xbutton.x, event.xbutton.y)) + { + iter->setMouseDown(true); + iter->setMouseCoordinates(event.xbutton.x, event.xbutton.y); + break; + } + } + break; + case ButtonRelease: + for (iter = mButtons.begin(); iter != mButtons.end(); ++iter) + { + if (iter->pointInRect(event.xbutton.x, event.xbutton.y) && + iter->isMouseDown()) + { + // we got a winner! + clickedButton = iter; + done = true; + break; + } + } + if (clickedButton == NULL) + { + // user released outside a button. clear the button states + for (iter = mButtons.begin(); iter != mButtons.end(); ++iter) + iter->setMouseDown(false); + } + break; + case ClientMessage: + if (event.xclient.message_type == wm_protocols && + event.xclient.data.l[0] == static_cast(wm_delete_window)) + done = true; + break; + } + repaint(); + } + + XUnmapWindow(mDisplay, mWin); + XDestroyWindow(mDisplay, mWin); + XFreeGC(mDisplay, mGC); + XFreeFont(mDisplay, mFS); + + if (clickedButton != NULL) + return clickedButton->getClickVal(); + else + return -1; +} diff --git a/Engine/source/platformX86UNIX/x86UNIXMessageBox.h b/Engine/source/platformX86UNIX/x86UNIXMessageBox.h new file mode 100644 index 000000000..7e7477700 --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXMessageBox.h @@ -0,0 +1,120 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _X86UNIXMESSAGEBOX_H_ +#define _X86UNIXMESSAGEBOX_H_ + +#include +#include "core/util/tVector.h" + +class XMessageBoxButton +{ + public: + XMessageBoxButton(); + XMessageBoxButton(const char* label, int clickVal); + + const char *getLabel() { return static_cast(mLabel); } + int getClickVal() { return mClickVal; } + + int getLabelWidth() { return mLabelWidth; } + void setLabelWidth(int width) { mLabelWidth = width; } + + void setButtonRect(int x, int y, int width, int height) + { + mX = x; + mY = y; + mWidth = width; + mHeight = height; + } + void setMouseCoordinates(int x, int y) + { + mMouseX = x; + mMouseY = y; + } + + bool drawReverse() + { + return mMouseDown && pointInRect(mMouseX, mMouseY); + } + + bool pointInRect(int x, int y) + { + if (x >= mX && x <= (mX+mWidth) && + y >= mY && y <= (mY+mHeight)) + return true; + return false; + } + + void setMouseDown(bool mouseDown) { mMouseDown = mouseDown; } + bool isMouseDown() { return mMouseDown; } + + private: + static const int LabelSize = 100; + char mLabel[LabelSize]; + int mClickVal; + int mLabelWidth; + int mX, mY, mWidth, mHeight; + int mMouseX, mMouseY; + bool mMouseDown; +}; + +class XMessageBox +{ + public: + static const int OK = 1; + static const int Cancel = 2; + static const int Retry = 3; + + XMessageBox(Display* display); + ~XMessageBox(); + + int alertOK(const char *windowTitle, const char *message); + int alertOKCancel(const char *windowTitle, const char *message); + int alertRetryCancel(const char *windowTitle, const char *message); + private: + int show(); + void repaint(); + void splitMessage(); + void clearMessageLines(); + int loadFont(); + void setDimensions(); + int getButtonLineWidth(); + + const char* mMessage; + const char* mTitle; + Vector mButtons; + Vector mMessageLines; + + Display* mDisplay; + GC mGC; + Window mWin; + XFontStruct* mFS; + int mFontHeight; + int mFontAscent; + int mFontDescent; + int mFontDirection; + + int mScreenWidth, mScreenHeight, mMaxWindowWidth, mMaxWindowHeight; + int mMBWidth, mMBHeight; +}; + +#endif // #define _X86UNIXMESSAGEBOX_H_ diff --git a/Engine/source/platformX86UNIX/x86UNIXNet.cpp b/Engine/source/platformX86UNIX/x86UNIXNet.cpp new file mode 100644 index 000000000..e186b8f8a --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXNet.cpp @@ -0,0 +1,924 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#if 0 + +#include "platformX86UNIX/platformX86UNIX.h" +#include "platform/platform.h" +#include "platform/event.h" +#include "platform/platformNetAsync.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +/* for PROTO_IPX */ +#if defined(__linux__) +#include +#include /* ioctl() */ +#include +#elif defined(__OpenBSD__) || defined(__FreeBSD__) +#include /* ioctl() */ +#include +#endif + +#include +#include + +#include "console/console.h" +#include "platform/gameInterface.h" +#include "core/fileStream.h" +#include "core/tVector.h" + +static Net::Error getLastError(); +static S32 defaultPort = 28000; +static S32 netPort = 0; +static int ipxSocket = InvalidSocket; +static int udpSocket = InvalidSocket; + +// local enum for socket states for polled sockets +enum SocketState +{ + InvalidState, + Connected, + ConnectionPending, + Listening, + NameLookupRequired +}; + +// the Socket structure helps us keep track of the +// above states +struct Socket +{ + Socket() + { + fd = InvalidSocket; + state = InvalidState; + remoteAddr[0] = 0; + remotePort = -1; + } + + NetSocket fd; + S32 state; + char remoteAddr[256]; + S32 remotePort; +}; + +// list of polled sockets +static Vector gPolledSockets; + +static Socket* addPolledSocket(NetSocket& fd, S32 state, + char* remoteAddr = NULL, S32 port = -1) +{ + Socket* sock = new Socket(); + sock->fd = fd; + sock->state = state; + if (remoteAddr) + dStrcpy(sock->remoteAddr, remoteAddr); + if (port != -1) + sock->remotePort = port; + gPolledSockets.push_back(sock); + return sock; +} + +enum { + MaxConnections = 1024, +}; + +S32 Poll(NetSocket fd, S32 eventMask, S32 timeoutMs) +{ + pollfd pfd; + S32 retVal; + pfd.fd = fd; + pfd.events = eventMask; + + retVal = poll(&pfd, 1, timeoutMs); + return retVal; + if (retVal <= 0) + return retVal; + else + return pfd.revents; +} + +bool Net::init() +{ + NetAsync::startAsync(); + return(true); +} + +void Net::shutdown() +{ + while (gPolledSockets.size() > 0) + closeConnectTo(gPolledSockets[0]->fd); + + closePort(); + NetAsync::stopAsync(); +} + +static void netToIPSocketAddress(const NetAddress *address, struct sockaddr_in *sockAddr) +{ + dMemset(sockAddr, 0, sizeof(struct sockaddr_in)); + sockAddr->sin_family = AF_INET; + sockAddr->sin_port = htons(address->port); + char tAddr[20]; + dSprintf(tAddr, 20, "%d.%d.%d.%d\n", address->netNum[0], address->netNum[1], address->netNum[2], address->netNum[3]); +//fprintf(stdout,"netToIPSocketAddress(): %s\n",tAddr);fflush(NULL); + sockAddr->sin_addr.s_addr = inet_addr(tAddr); +// sockAddr->sin_addr.s_addr = address->netNum[0]; // hopefully this will work. +} + +static void IPSocketToNetAddress(const struct sockaddr_in *sockAddr, NetAddress *address) +{ + address->type = NetAddress::IPAddress; + address->port = htons(sockAddr->sin_port); + char *tAddr; + tAddr = inet_ntoa(sockAddr->sin_addr); +//fprintf(stdout,"IPSocketToNetAddress(): %s\n",tAddr);fflush(NULL); + U8 nets[4]; + nets[0] = atoi(strtok(tAddr, ".")); + nets[1] = atoi(strtok(NULL, ".")); + nets[2] = atoi(strtok(NULL, ".")); + nets[3] = atoi(strtok(NULL, ".")); +//fprintf(stdout,"0 = %d, 1 = %d, 2 = %d, 3 = %d\n", nets[0], nets[1], nets[2], nets[3]); + address->netNum[0] = nets[0]; + address->netNum[1] = nets[1]; + address->netNum[2] = nets[2]; + address->netNum[3] = nets[3]; +} + +static void netToIPXSocketAddress(const NetAddress *address, sockaddr_ipx *sockAddr) +{ +#if !defined(__FreeBSD__) + dMemset(sockAddr, 0, sizeof(sockaddr_ipx)); + sockAddr->sipx_family = AF_INET; + sockAddr->sipx_port = htons(address->port); + sockAddr->sipx_network = address->netNum[0]; + sockAddr->sipx_node[0] = address->nodeNum[0]; + sockAddr->sipx_node[1] = address->nodeNum[1]; + sockAddr->sipx_node[2] = address->nodeNum[2]; + sockAddr->sipx_node[3] = address->nodeNum[3]; + sockAddr->sipx_node[4] = address->nodeNum[4]; + sockAddr->sipx_node[5] = address->nodeNum[5]; +#endif +} + +static void IPXSocketToNetAddress(const sockaddr_ipx *sockAddr, NetAddress *address) +{ +#if !defined(__FreeBSD__) + address->type = NetAddress::IPXAddress; + address->port = htons(sockAddr->sipx_port); + address->netNum[0] = sockAddr->sipx_network; + address->nodeNum[0] = sockAddr->sipx_node[0]; + address->nodeNum[1] = sockAddr->sipx_node[1]; + address->nodeNum[2] = sockAddr->sipx_node[2]; + address->nodeNum[3] = sockAddr->sipx_node[3]; + address->nodeNum[4] = sockAddr->sipx_node[4]; + address->nodeNum[5] = sockAddr->sipx_node[5]; +#endif +} + +NetSocket Net::openListenPort(U16 port) +{ + if(Game->isJournalReading()) + { + U32 ret; + Game->journalRead(&ret); + return NetSocket(ret); + } + NetSocket sock = openSocket(); + if (sock == InvalidSocket) + { + Con::errorf("Unable to open listen socket: %s", strerror(errno)); + return InvalidSocket; + } + + if (bind(sock, port) != NoError) + { + Con::errorf("Unable to bind port %d: %s", port, strerror(errno)); + ::close(sock); + return InvalidSocket; + } + if (listen(sock, 4) != NoError) + { + Con::errorf("Unable to listen on port %d: %s", port, strerror(errno)); + ::close(sock); + return InvalidSocket; + } + + setBlocking(sock, false); + addPolledSocket(sock, Listening); + if (Game->isJournalWriting()) + Game->journalWrite(U32(sock)); + return sock; +} + +NetSocket Net::openConnectTo(const char *addressString) +{ + if(!dStrnicmp(addressString, "ipx:", 4)) + return InvalidSocket; + if(!dStrnicmp(addressString, "ip:", 3)) + addressString += 3; // eat off the ip: + char remoteAddr[256]; + dStrcpy(remoteAddr, addressString); + + char *portString = dStrchr(remoteAddr, ':'); + + U16 port; + if(portString) + { + *portString++ = 0; + port = htons(dAtoi(portString)); + } + else + port = htons(defaultPort); + + if(!dStricmp(remoteAddr, "broadcast")) + return InvalidSocket; + + if(Game->isJournalReading()) + { + U32 ret; + Game->journalRead(&ret); + return NetSocket(ret); + } + NetSocket sock = openSocket(); + setBlocking(sock, false); + + sockaddr_in ipAddr; + dMemset(&ipAddr, 0, sizeof(ipAddr)); + + if (inet_aton(remoteAddr, &ipAddr.sin_addr) != 0) + { + ipAddr.sin_port = port; + ipAddr.sin_family = AF_INET; + if(::connect(sock, (struct sockaddr *)&ipAddr, sizeof(ipAddr)) == -1 && + errno != EINPROGRESS) + { + Con::errorf("Error connecting %s: %s", + addressString, strerror(errno)); + ::close(sock); + sock = InvalidSocket; + } + if(sock != InvalidSocket) { + // add this socket to our list of polled sockets + addPolledSocket(sock, ConnectionPending); + } + } + else + { + // need to do an asynchronous name lookup. first, add the socket + // to the polled list + addPolledSocket(sock, NameLookupRequired, remoteAddr, port); + // queue the lookup + gNetAsync.queueLookup(remoteAddr, sock); + } + if(Game->isJournalWriting()) + Game->journalWrite(U32(sock)); + return sock; +} + +void Net::closeConnectTo(NetSocket sock) +{ + if(Game->isJournalReading()) + return; + + // if this socket is in the list of polled sockets, remove it + for (int i = 0; i < gPolledSockets.size(); ++i) + if (gPolledSockets[i]->fd == sock) + { + delete gPolledSockets[i]; + gPolledSockets.erase(i); + break; + } + + closeSocket(sock); +} + +Net::Error Net::sendtoSocket(NetSocket socket, const U8 *buffer, int bufferSize) +{ + if(Game->isJournalReading()) + { + U32 e; + Game->journalRead(&e); + + return (Net::Error) e; + } + Net::Error e = send(socket, buffer, bufferSize); + if(Game->isJournalWriting()) + Game->journalWrite(U32(e)); + return e; +} + +bool Net::openPort(S32 port) +{ + if(udpSocket != InvalidSocket) + close(udpSocket); + if(ipxSocket != InvalidSocket) + close(ipxSocket); + + udpSocket = socket(AF_INET, SOCK_DGRAM, 0); + ipxSocket = socket(AF_IPX, SOCK_DGRAM, 0); + + if(udpSocket != InvalidSocket) + { + Net::Error error; + error = bind(udpSocket, port); + if(error == NoError) + error = setBufferSize(udpSocket, 32768); + if(error == NoError) + error = setBroadcast(udpSocket, true); + if(error == NoError) + error = setBlocking(udpSocket, false); + if(error == NoError) + Con::printf("UDP initialized on port %d", port); + else + { + close(udpSocket); + udpSocket = InvalidSocket; + Con::printf("Unable to initialize UDP - error %d", error); + } + } + if(ipxSocket != InvalidSocket) + { + Net::Error error = NoError; + sockaddr_ipx ipxAddress; + memset((char *)&ipxAddress, 0, sizeof(ipxAddress)); + ipxAddress.sipx_family = AF_IPX; + ipxAddress.sipx_port = htons(port); + S32 err = ::bind(ipxSocket, (struct sockaddr *)&ipxAddress, sizeof(ipxAddress)); + if(err) + error = getLastError(); + if(error == NoError) + error = setBufferSize(ipxSocket, 32768); + if(error == NoError) + error = setBroadcast(ipxSocket, true); + if(error == NoError) + error = setBlocking(ipxSocket, false); + if(error == NoError) + Con::printf("IPX initialized on port %d", port); + else + { + close(ipxSocket); + ipxSocket = InvalidSocket; + Con::printf("Unable to initialize IPX - error %d", error); + } + } + netPort = port; + return ipxSocket != InvalidSocket || udpSocket != InvalidSocket; +} + +void Net::closePort() +{ + if(ipxSocket != InvalidSocket) + close(ipxSocket); + if(udpSocket != InvalidSocket) + close(udpSocket); +} + +Net::Error Net::sendto(const NetAddress *address, const U8 *buffer, S32 bufferSize) +{ + if(Game->isJournalReading()) + return NoError; + + if(address->type == NetAddress::IPXAddress) + { + sockaddr_ipx ipxAddr; + netToIPXSocketAddress(address, &ipxAddr); + if(::sendto(ipxSocket, (const char*)buffer, bufferSize, 0, + (sockaddr *) &ipxAddr, sizeof(sockaddr_ipx)) == -1) + return getLastError(); + else + return NoError; + } + else + { + sockaddr_in ipAddr; + netToIPSocketAddress(address, &ipAddr); + if(::sendto(udpSocket, (const char*)buffer, bufferSize, 0, + (sockaddr *) &ipAddr, sizeof(sockaddr_in)) == -1) + return getLastError(); + else + return NoError; + } +} + +void Net::process() +{ + sockaddr sa; + + PacketReceiveEvent receiveEvent; + for(;;) + { + U32 addrLen = sizeof(sa); + S32 bytesRead = -1; + if(udpSocket != InvalidSocket) + bytesRead = recvfrom(udpSocket, (char *) receiveEvent.data, MaxPacketDataSize, 0, &sa, &addrLen); + if(bytesRead == -1 && ipxSocket != InvalidSocket) + { + addrLen = sizeof(sa); + bytesRead = recvfrom(ipxSocket, (char *) receiveEvent.data, MaxPacketDataSize, 0, &sa, &addrLen); + } + + if(bytesRead == -1) + break; + + if(sa.sa_family == AF_INET) + IPSocketToNetAddress((sockaddr_in *) &sa, &receiveEvent.sourceAddress); + else if(sa.sa_family == AF_IPX) + IPXSocketToNetAddress((sockaddr_ipx *) &sa, &receiveEvent.sourceAddress); + else + continue; + + NetAddress &na = receiveEvent.sourceAddress; + if(na.type == NetAddress::IPAddress && + na.netNum[0] == 127 && + na.netNum[1] == 0 && + na.netNum[2] == 0 && + na.netNum[3] == 1 && + na.port == netPort) + continue; + if(bytesRead <= 0) + continue; + receiveEvent.size = PacketReceiveEventHeaderSize + bytesRead; + Game->postEvent(receiveEvent); + } + + // process the polled sockets. This blob of code performs functions + // similar to WinsockProc in winNet.cc + + if (gPolledSockets.size() == 0) + return; + + static ConnectedNotifyEvent notifyEvent; + static ConnectedAcceptEvent acceptEvent; + static ConnectedReceiveEvent cReceiveEvent; + + S32 optval; + socklen_t optlen = sizeof(S32); + S32 bytesRead; + Net::Error err; + bool removeSock = false; + Socket *currentSock = NULL; + sockaddr_in ipAddr; + NetSocket incoming = InvalidSocket; + char out_h_addr[1024]; + int out_h_length = 0; + + for (S32 i = 0; i < gPolledSockets.size(); + /* no increment, this is done at end of loop body */) + { + removeSock = false; + currentSock = gPolledSockets[i]; + switch (currentSock->state) + { + case InvalidState: + Con::errorf("Error, InvalidState socket in polled sockets list"); + break; + case ConnectionPending: + notifyEvent.tag = currentSock->fd; + // see if it is now connected + if (getsockopt(currentSock->fd, SOL_SOCKET, SO_ERROR, + &optval, &optlen) == -1) + { + Con::errorf("Error getting socket options: %s", strerror(errno)); + notifyEvent.state = ConnectedNotifyEvent::ConnectFailed; + Game->postEvent(notifyEvent); + removeSock = true; + } + else + { + if (optval == EINPROGRESS) + // still connecting... + break; + + if (optval == 0) + { + // connected + notifyEvent.state = ConnectedNotifyEvent::Connected; + Game->postEvent(notifyEvent); + currentSock->state = Connected; + } + else + { + // some kind of error + Con::errorf("Error connecting: %s", strerror(errno)); + notifyEvent.state = ConnectedNotifyEvent::ConnectFailed; + Game->postEvent(notifyEvent); + removeSock = true; + } + } + break; + case Connected: + bytesRead = 0; + // try to get some data + err = Net::recv(currentSock->fd, cReceiveEvent.data, + MaxPacketDataSize, &bytesRead); + if(err == Net::NoError) + { + if (bytesRead > 0) + { + // got some data, post it + cReceiveEvent.tag = currentSock->fd; + cReceiveEvent.size = ConnectedReceiveEventHeaderSize + + bytesRead; + Game->postEvent(cReceiveEvent); + } + else + { + // zero bytes read means EOF + if (bytesRead < 0) + // ack! this shouldn't happen + Con::errorf("Unexpected error on socket: %s", + strerror(errno)); + + notifyEvent.tag = currentSock->fd; + notifyEvent.state = ConnectedNotifyEvent::Disconnected; + Game->postEvent(notifyEvent); + removeSock = true; + } + } + else if (err != Net::NoError && err != Net::WouldBlock) + { + Con::errorf("Error reading from socket: %s", strerror(errno)); + notifyEvent.tag = currentSock->fd; + notifyEvent.state = ConnectedNotifyEvent::Disconnected; + Game->postEvent(notifyEvent); + removeSock = true; + } + break; + case NameLookupRequired: + // is the lookup complete? + if (!gNetAsync.checkLookup( + currentSock->fd, out_h_addr, &out_h_length, + sizeof(out_h_addr))) + break; + + notifyEvent.tag = currentSock->fd; + if (out_h_length == -1) + { + Con::errorf("DNS lookup failed: %s", currentSock->remoteAddr); + notifyEvent.state = ConnectedNotifyEvent::DNSFailed; + removeSock = true; + } + else + { + // try to connect + dMemcpy(&(ipAddr.sin_addr.s_addr), out_h_addr, out_h_length); + ipAddr.sin_port = currentSock->remotePort; + ipAddr.sin_family = AF_INET; + if(::connect(currentSock->fd, (struct sockaddr *)&ipAddr, + sizeof(ipAddr)) == -1) + { + if (errno == EINPROGRESS) + { + notifyEvent.state = ConnectedNotifyEvent::DNSResolved; + currentSock->state = ConnectionPending; + } + else + { + Con::errorf("Error connecting to %s: %s", + currentSock->remoteAddr, strerror(errno)); + notifyEvent.state = ConnectedNotifyEvent::ConnectFailed; + removeSock = true; + } + } + else + { + notifyEvent.state = ConnectedNotifyEvent::Connected; + currentSock->state = Connected; + } + } + Game->postEvent(notifyEvent); + break; + case Listening: + incoming = + Net::accept(currentSock->fd, &acceptEvent.address); + if(incoming != InvalidSocket) + { + acceptEvent.portTag = currentSock->fd; + acceptEvent.connectionTag = incoming; + setBlocking(incoming, false); + addPolledSocket(incoming, Connected); + Game->postEvent(acceptEvent); + } + break; + } + + // only increment index if we're not removing the connection, since + // the removal will shift the indices down by one + if (removeSock) + closeConnectTo(currentSock->fd); + else + i++; + } +} + +NetSocket Net::openSocket() +{ + int retSocket; + retSocket = socket(AF_INET, SOCK_STREAM, 0); + + if(retSocket == InvalidSocket) + return InvalidSocket; + else + return retSocket; +} + +Net::Error Net::closeSocket(NetSocket socket) +{ + if(socket != InvalidSocket) + { + if(!close(socket)) + return NoError; + else + return getLastError(); + } + else + return NotASocket; +} + +Net::Error Net::connect(NetSocket socket, const NetAddress *address) +{ + if(address->type != NetAddress::IPAddress) + return WrongProtocolType; + sockaddr_in socketAddress; + netToIPSocketAddress(address, &socketAddress); + if(!::connect(socket, (sockaddr *) &socketAddress, sizeof(socketAddress))) + return NoError; + return getLastError(); +} + +Net::Error Net::listen(NetSocket socket, S32 backlog) +{ + if(!::listen(socket, backlog)) + return NoError; + return getLastError(); +} + +NetSocket Net::accept(NetSocket acceptSocket, NetAddress *remoteAddress) +{ + sockaddr_in socketAddress; + U32 addrLen = sizeof(socketAddress); + + int retVal = ::accept(acceptSocket, (sockaddr *) &socketAddress, &addrLen); + if(retVal != InvalidSocket) + { + IPSocketToNetAddress(&socketAddress, remoteAddress); + return retVal; + } + return InvalidSocket; +} + +Net::Error Net::bind(NetSocket socket, U16 port) +{ + S32 error; + + sockaddr_in socketAddress; + dMemset((char *)&socketAddress, 0, sizeof(socketAddress)); + socketAddress.sin_family = AF_INET; + // It's entirely possible that there are two NIC cards. + // We let the user specify which one the server runs on. + + // thanks to [TPG]P1aGu3 for the name + const char* serverIP = Con::getVariable( "Pref::Net::BindAddress" ); + // serverIP is guaranteed to be non-0. + AssertFatal( serverIP, "serverIP is NULL!" ); + + if( serverIP[0] != '\0' ) { + // we're not empty + socketAddress.sin_addr.s_addr = inet_addr( serverIP ); + + if( socketAddress.sin_addr.s_addr != INADDR_NONE ) { + Con::printf( "Binding server port to %s", serverIP ); + } else { + Con::warnf( ConsoleLogEntry::General, + "inet_addr() failed for %s while binding!", + serverIP ); + socketAddress.sin_addr.s_addr = INADDR_ANY; + } + + } else { + Con::printf( "Binding server port to default IP" ); + socketAddress.sin_addr.s_addr = INADDR_ANY; + } + + socketAddress.sin_port = htons(port); + error = ::bind(socket, (sockaddr *) &socketAddress, sizeof(socketAddress)); + + if(!error) + return NoError; + return getLastError(); +} + +Net::Error Net::setBufferSize(NetSocket socket, S32 bufferSize) +{ + S32 error; + error = setsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char *) &bufferSize, sizeof(bufferSize)); + if(!error) + error = setsockopt(socket, SOL_SOCKET, SO_SNDBUF, (char *) &bufferSize, sizeof(bufferSize)); + if(!error) + return NoError; + return getLastError(); +} + +Net::Error Net::setBroadcast(NetSocket socket, bool broadcast) +{ + S32 bc = broadcast; + S32 error = setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char*)&bc, sizeof(bc)); + if(!error) + return NoError; + return getLastError(); +} + +Net::Error Net::setBlocking(NetSocket socket, bool blockingIO) +{ + int notblock = !blockingIO; + S32 error = ioctl(socket, FIONBIO, ¬block); + if(!error) + return NoError; + return getLastError(); +} + +Net::Error Net::send(NetSocket socket, const U8 *buffer, S32 bufferSize) +{ + // Poll for write status. this blocks. should really + // do this in a separate thread or set it up so that the data can + // get queued and sent later + // JMQTODO + Poll(socket, POLLOUT, 10000); + + S32 error = ::send(socket, (const char*)buffer, bufferSize, 0); + if(error != -1) + return NoError; + + return getLastError(); +} + +Net::Error Net::recv(NetSocket socket, U8 *buffer, S32 bufferSize, S32 *bytesRead) +{ + *bytesRead = ::recv(socket, (char*)buffer, bufferSize, 0); + if(*bytesRead == -1) + return getLastError(); + return NoError; +} + +bool Net::compareAddresses(const NetAddress *a1, const NetAddress *a2) +{ + if((a1->type != a2->type) || + (*((U32 *)a1->netNum) != *((U32 *)a2->netNum)) || + (a1->port != a2->port)) + return false; + + if(a1->type == NetAddress::IPAddress) + return true; + for(S32 i = 0; i < 6; i++) + if(a1->nodeNum[i] != a2->nodeNum[i]) + return false; + return true; +} + +bool Net::stringToAddress(const char *addressString, NetAddress *address) +{ + if(dStrnicmp(addressString, "ipx:", 4)) + { + // assume IP if it doesn't have ipx: at the front. + + if(!dStrnicmp(addressString, "ip:", 3)) + addressString += 3; // eat off the ip: + + sockaddr_in ipAddr; + char remoteAddr[256]; + if(strlen(addressString) > 255) + return false; + + dStrcpy(remoteAddr, addressString); + + char *portString = dStrchr(remoteAddr, ':'); + if(portString) + *portString++ = '\0'; + + struct hostent *hp; + if(!dStricmp(remoteAddr, "broadcast")) + ipAddr.sin_addr.s_addr = htonl(INADDR_BROADCAST); + else + { + if (inet_aton(remoteAddr,&ipAddr.sin_addr) == 0) // error + { + if((hp = gethostbyname(remoteAddr)) == 0) + return false; + else + memcpy(&ipAddr.sin_addr.s_addr, hp->h_addr, sizeof(in_addr)); + } + } + if(portString) + ipAddr.sin_port = htons(dAtoi(portString)); + else + ipAddr.sin_port = htons(defaultPort); + ipAddr.sin_family = AF_INET; + IPSocketToNetAddress(&ipAddr, address); + return true; + } + else + { + S32 i; + S32 port; + + address->type = NetAddress::IPXAddress; + for(i = 0; i < 6; i++) + address->nodeNum[i] = 0xFF; + + // it's an IPX string + addressString += 4; + if(!dStricmp(addressString, "broadcast")) + { + address->port = defaultPort; + return true; + } + else if(sscanf(addressString, "broadcast:%d", &port) == 1) + { + address->port = port; + return true; + } + else + { + S32 nodeNum[6]; + S32 netNum[4]; + S32 count = dSscanf(addressString, "%2x%2x%2x%2x:%2x%2x%2x%2x%2x%2x:%d", + &netNum[0], &netNum[1], &netNum[2], &netNum[3], + &nodeNum[0], &nodeNum[1], &nodeNum[2], &nodeNum[3], &nodeNum[4], &nodeNum[5], + &port); + + if(count == 10) + { + port = defaultPort; + count++; + } + if(count != 11) + return false; + + for(i = 0; i < 6; i++) + address->nodeNum[i] = nodeNum[i]; + for(i = 0; i < 4; i++) + address->netNum[i] = netNum[i]; + address->port = port; + return true; + } + } +} + +void Net::addressToString(const NetAddress *address, char addressString[256]) +{ + if(address->type == NetAddress::IPAddress) + { + sockaddr_in ipAddr; + netToIPSocketAddress(address, &ipAddr); + + if(ipAddr.sin_addr.s_addr == htonl(INADDR_BROADCAST)) + dSprintf(addressString, 256, "IP:Broadcast:%d", ntohs(ipAddr.sin_port)); + else + dSprintf(addressString, 256, "IP:%s:%d", inet_ntoa(ipAddr.sin_addr), + ntohs(ipAddr.sin_port)); +// dSprintf(addressString, 256, "IP:%d:%d", ipAddr.sin_addr.s_addr, +// ntohs(ipAddr.sin_port)); + } + else + { + return; + dSprintf(addressString, 256, "IPX:%.2X%.2X%.2X%.2X:%.2X%.2X%.2X%.2X%.2X%.2X:%d", + address->netNum[0], address->netNum[1], address->netNum[2], address->netNum[3], + address->nodeNum[0], address->nodeNum[1], address->nodeNum[2], address->nodeNum[3], address->nodeNum[4], address->nodeNum[5], + address->port); + } +} + +Net::Error getLastError() +{ + if (errno == EAGAIN) + return Net::WouldBlock; + return Net::UnknownError; +} + +#endif diff --git a/Engine/source/platformX86UNIX/x86UNIXOGLVideo.client.cpp b/Engine/source/platformX86UNIX/x86UNIXOGLVideo.client.cpp new file mode 100644 index 000000000..edc5d3a17 --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXOGLVideo.client.cpp @@ -0,0 +1,505 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/console.h" +#include "platform/event.h" +#include "platform/gameInterface.h" + +#include "platformX86UNIX/platformX86UNIX.h" +#include "platformX86UNIX/platformGL.h" +#include "platformX86UNIX/x86UNIXOGLVideo.h" +#include "platformX86UNIX/x86UNIXState.h" + +#include +#include +#include + +//------------------------------------------------------------------------------ +bool InitOpenGL() +{ + DisplayDevice::init(); + + // Get the video settings from the prefs: + const char* resString = Con::getVariable( "$pref::Video::resolution" ); + char* tempBuf = new char[dStrlen( resString ) + 1]; + dStrcpy( tempBuf, resString ); + char* temp = dStrtok( tempBuf, " x\0" ); + U32 width = ( temp ? dAtoi( temp ) : 800 ); + temp = dStrtok( NULL, " x\0" ); + U32 height = ( temp ? dAtoi( temp ) : 600 ); + temp = dStrtok( NULL, "\0" ); + U32 bpp = ( temp ? dAtoi( temp ) : 16 ); + delete [] tempBuf; + + bool fullScreen = Con::getBoolVariable( "$pref::Video::fullScreen" ); + + // the only supported video device in unix is OpenGL + if ( !Video::setDevice( "OpenGL", width, height, bpp, fullScreen ) ) + { + Con::errorf("Unable to create default OpenGL mode: %d %d %d %d", + width, height, bpp, fullScreen); + + // if we can't create the default, attempt to create a "safe" window + if ( !Video::setDevice( "OpenGL", 640, 480, 16, true ) ) + { + DisplayErrorAlert("Could not find a compatible OpenGL display " \ + "resolution. Please check your driver configuration."); + return false; + } + } + + return true; +} + +//------------------------------------------------------------------------------ +bool OpenGLDevice::smCanSwitchBitDepth = false; + +//------------------------------------------------------------------------------ +OpenGLDevice::OpenGLDevice() +{ + initDevice(); +} + +//------------------------------------------------------------------------------ +OpenGLDevice::~OpenGLDevice() +{ +} + +//------------------------------------------------------------------------------ +void OpenGLDevice::addResolution(S32 width, S32 height, bool check) +{ + Point2I desktopSize = x86UNIXState->getDesktopSize(); + U32 desktopBpp = x86UNIXState->getDesktopBpp(); + + // don't allow any resolution under this size + if (width < 640 || height < 480) + return; + + if (check) + { + // don't allow resolutions that exceed the current desktop size + if (width > desktopSize.x || height > desktopSize.y) + return; + } + + if (smCanSwitchBitDepth) + { + // add both 16 and 32 bit resolutions + mResolutionList.push_back(Resolution(width, height, 16)); + mResolutionList.push_back(Resolution(width, height, 32)); + } + else + { + // add just the desktop resolution + mResolutionList.push_back(Resolution(width, height, desktopBpp)); + } +} + +//------------------------------------------------------------------------------ +void OpenGLDevice::initDevice() +{ + mDeviceName = "OpenGL"; + mFullScreenOnly = false; +} + +//------------------------------------------------------------------------------ +void OpenGLDevice::loadResolutions() +{ + mResolutionList.clear(); + + // X cannot switch bit depths on the fly. In case this feature is + // implemented someday, calling this function will let you take + // advantage of it + if (Con::getBoolVariable("$pref::Unix::CanSwitchBitDepth")) + smCanSwitchBitDepth = true; + + // add some default resolutions + addResolution(640, 480); + addResolution(800, 600); + addResolution(1024, 768); + addResolution(1152, 864); + addResolution(1280, 1024); + addResolution(1600, 1200); + + // specifying full screen should give us the resolutions that the + // X server allows + SDL_Rect** modes = SDL_ListModes(NULL, SDL_FULLSCREEN); + if (modes && + (modes != (SDL_Rect **)-1)) + { + for (int i = 0; modes[i] != NULL; ++i) + { + // do we already have this mode? + bool found = false; + for (Vector::iterator iter = mResolutionList.begin(); + iter != mResolutionList.end(); + ++iter) + { + if (iter->w == modes[i]->w && iter->h == modes[i]->h) + { + found = true; + break; + } + } + if (!found) + // don't check these resolutions because they should be OK + // (and checking might drop resolutions that are higher than the + // current desktop bpp) + addResolution(modes[i]->w, modes[i]->h, false); + } + } +} + +//------------------------------------------------------------------------------ +bool OpenGLDevice::activate( U32 width, U32 height, U32 bpp, bool fullScreen ) +{ + if (!setScreenMode(width, height, bpp, fullScreen)) + { + Con::printf("Unable to set screen mode."); + return false; + } + + // Output some driver info to the console + const char* vendorString = (const char*) glGetString( GL_VENDOR ); + const char* rendererString = (const char*) glGetString( GL_RENDERER ); + const char* versionString = (const char*) glGetString( GL_VERSION ); + Con::printf( "OpenGL driver information:" ); + if ( vendorString ) + Con::printf( " Vendor: %s", vendorString ); + if ( rendererString ) + Con::printf( " Renderer: %s", rendererString ); + if ( versionString ) + Con::printf( " Version: %s", versionString ); + + GL_EXT_Init(); + + Con::setVariable( "$pref::Video::displayDevice", mDeviceName ); + + // Do this here because we now know about the extensions: + if ( gGLState.suppSwapInterval ) + setVerticalSync( + !Con::getBoolVariable( "$pref::Video::disableVerticalSync" ) ); + Con::setBoolVariable("$pref::OpenGL::allowTexGen", true); + + return true; +} + + +//------------------------------------------------------------------------------ +void OpenGLDevice::shutdown() +{ + // Shutdown is deferred to Platform::shutdown() +} + +//------------------------------------------------------------------------------ +static void PrintGLAttributes() +{ + int doubleBuf; + int bufferSize, depthSize, stencilSize; + int red, green, blue, alpha; + int aRed, aGreen, aBlue, aAlpha; + + SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &doubleBuf); + SDL_GL_GetAttribute(SDL_GL_BUFFER_SIZE, &bufferSize); + SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depthSize); + SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &stencilSize); + SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &red); + SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &green); + SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &blue); + SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &alpha); + SDL_GL_GetAttribute(SDL_GL_ACCUM_RED_SIZE, &aRed); + SDL_GL_GetAttribute(SDL_GL_ACCUM_GREEN_SIZE, &aGreen); + SDL_GL_GetAttribute(SDL_GL_ACCUM_BLUE_SIZE, &aBlue); + SDL_GL_GetAttribute(SDL_GL_ACCUM_ALPHA_SIZE, &aAlpha); + + Con::printf("OpenGL Attributes:"); + Con::printf(" DoubleBuffer: %d", doubleBuf); + Con::printf(" BufferSize: %d, DepthSize: %d, StencilSize: %d", + bufferSize, depthSize, stencilSize); + Con::printf(" Red: %d, Green: %d, Blue: %d, Alpha: %d", + red, green, blue, alpha); + Con::printf(" Accum Red: %d, Green: %d, Blue: %d, Alpha: %d", + aRed, aGreen, aBlue, aAlpha); +} + +//------------------------------------------------------------------------------ +bool OpenGLDevice::setScreenMode( U32 width, U32 height, U32 bpp, + bool fullScreen, bool forceIt, bool repaint ) +{ + // load resolutions, this is done lazily so that we can check the setting + // of smCanSwitchBitDepth, which may be overridden by console + if (mResolutionList.size()==0) + loadResolutions(); + + if (mResolutionList.size()==0) + { + Con::printf("No resolutions available!"); + return false; + } + + if (bpp == 0) + { + // bpp comes in as "0" when it is set to "Default" + bpp = x86UNIXState->getDesktopBpp(); + } + + if (height == 0 || width == 0) + { + // paranoia check. set it to the default to prevent crashing + width = 800; + height = 600; + } + + U32 desktopDepth = x86UNIXState->getDesktopBpp(); + // if we can't switch bit depths and the requested bpp is not equal to + // the desktop bpp, set bpp to the desktop bpp + if (!smCanSwitchBitDepth && + bpp != desktopDepth) + { + bpp = desktopDepth; + } + + bool IsInList = false; + + Resolution NewResolution( width, height, bpp ); + + // See if the desired resolution is in the list + if ( mResolutionList.size() ) + { + for ( int i = 0; i < mResolutionList.size(); i++ ) + { + if ( width == mResolutionList[i].w + && height == mResolutionList[i].h + && bpp == mResolutionList[i].bpp ) + { + IsInList = true; + break; + } + } + + if ( !IsInList ) + { + Con::printf( "Selected resolution not available: %d %d %d", + width, height, bpp); + return false; + } + } + else + { + AssertFatal( false, "No resolution list found!!" ); + } + + // Here if we found a matching resolution in the list + + bool needResurrect = false; + if (x86UNIXState->windowCreated()) + { + Con::printf( "Killing the texture manager..." ); + Game->textureKill(); + needResurrect = true; + } + + // Set the desired GL Attributes + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); +// JMQ: NVIDIA 2802+ doesn't like this setting for stencil size +// SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); + SDL_GL_SetAttribute(SDL_GL_ACCUM_RED_SIZE, 0); + SDL_GL_SetAttribute(SDL_GL_ACCUM_GREEN_SIZE, 0); + SDL_GL_SetAttribute(SDL_GL_ACCUM_BLUE_SIZE, 0); + SDL_GL_SetAttribute(SDL_GL_ACCUM_ALPHA_SIZE, 0); +// SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); +// SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); +// SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6); +// SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); + + U32 flags = SDL_OPENGL; + if (fullScreen) + flags |= SDL_FULLSCREEN; + + Con::printf( "Setting screen mode to %dx%dx%d (%s)...", width, height, + bpp, ( fullScreen ? "fs" : "w" ) ); + + // set the new video mode + if (SDL_SetVideoMode(width, height, bpp, flags) == NULL) + { + Con::printf("Unable to set SDL Video Mode: %s", SDL_GetError()); + return false; + } + + PrintGLAttributes(); + + // clear screen here to prevent buffer garbage from being displayed when + // video mode is switched + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + if ( needResurrect ) + { + // Reload the textures: + Con::printf( "Resurrecting the texture manager..." ); + Game->textureResurrect(); + } + + if ( gGLState.suppSwapInterval ) + setVerticalSync( !Con::getBoolVariable( "$pref::Video::disableVerticalSync" ) ); + + // reset the window in platform state + SDL_SysWMinfo sysinfo; + SDL_VERSION(&sysinfo.version); + if (SDL_GetWMInfo(&sysinfo) == 0) + { + Con::printf("Unable to set SDL Video Mode: %s", SDL_GetError()); + return false; + } + x86UNIXState->setWindow(sysinfo.info.x11.window); + + // set various other parameters + x86UNIXState->setWindowCreated(true); + smCurrentRes = NewResolution; + Platform::setWindowSize ( width, height ); + smIsFullScreen = fullScreen; + Con::setBoolVariable( "$pref::Video::fullScreen", smIsFullScreen ); + char tempBuf[15]; + dSprintf( tempBuf, sizeof( tempBuf ), "%d %d %d", + smCurrentRes.w, smCurrentRes.h, smCurrentRes.bpp ); + Con::setVariable( "$pref::Video::resolution", tempBuf ); + + // post a TORQUE_SETVIDEOMODE user event + SDL_Event event; + event.type = SDL_USEREVENT; + event.user.code = TORQUE_SETVIDEOMODE; + event.user.data1 = NULL; + event.user.data2 = NULL; + SDL_PushEvent(&event); + + // reset the caption + SDL_WM_SetCaption(x86UNIXState->getWindowName(), NULL); + + // repaint + if ( repaint ) + Con::evaluate( "resetCanvas();" ); + + return true; +} + +//------------------------------------------------------------------------------ +void OpenGLDevice::swapBuffers() +{ + SDL_GL_SwapBuffers(); +} + +//------------------------------------------------------------------------------ +const char* OpenGLDevice::getDriverInfo() +{ + const char* vendorString = (const char*) glGetString( GL_VENDOR ); + const char* rendererString = (const char*) glGetString( GL_RENDERER ); + const char* versionString = (const char*) glGetString( GL_VERSION ); + const char* extensionsString = (const char*) glGetString( GL_EXTENSIONS ); + + U32 bufferLen = ( vendorString ? dStrlen( vendorString ) : 0 ) + + ( rendererString ? dStrlen( rendererString ) : 0 ) + + ( versionString ? dStrlen( versionString ) : 0 ) + + ( extensionsString ? dStrlen( extensionsString ) : 0 ) + + 4; + + char* returnString = Con::getReturnBuffer( bufferLen ); + dSprintf( returnString, bufferLen, "%s\t%s\t%s\t%s", + ( vendorString ? vendorString : "" ), + ( rendererString ? rendererString : "" ), + ( versionString ? versionString : "" ), + ( extensionsString ? extensionsString : "" ) ); + + return( returnString ); +} + +//------------------------------------------------------------------------------ +bool OpenGLDevice::getGammaCorrection(F32 &g) +{ + U16 redtable[256]; + U16 greentable[256]; + U16 bluetable[256]; + + if (SDL_GetGammaRamp(redtable, greentable, bluetable) == -1) + { + Con::warnf("getGammaCorrection error: %s", SDL_GetError()); + return false; + } + + F32 csum = 0.0; + U32 ccount = 0; + + for (U16 i = 0; i < 256; ++i) + { + if (i != 0 && redtable[i] != 0 && redtable[i] != 65535) + { + F64 b = (F64) i/256.0; + F64 a = (F64) redtable[i]/65535.0; + F32 c = (F32) (mLog(a)/mLog(b)); + + csum += c; + ++ccount; + } + } + g = csum/ccount; + + return true; +} + +//------------------------------------------------------------------------------ +bool OpenGLDevice::setGammaCorrection(F32 g) +{ + U16 redtable[256]; + U16 greentable[256]; + U16 bluetable[256]; + + for (U16 i = 0; i < 256; ++i) + redtable[i] = static_cast(mPow((F32) i/256.0f, g) * 65535.0f); + dMemcpy(greentable,redtable,256*sizeof(U16)); + dMemcpy(bluetable,redtable,256*sizeof(U16)); + + S32 ok = SDL_SetGammaRamp(redtable, greentable, bluetable); + if (ok == -1) + Con::warnf("Error setting gamma correction: %s", SDL_GetError()); + + return ok != -1; +} + +//------------------------------------------------------------------------------ +bool OpenGLDevice::setVerticalSync( bool on ) +{ + Con::printf("WARNING: OpenGLDevice::setVerticalSync is unimplemented %s %d\n", __FILE__, __LINE__); + return false; +#if 0 + if ( !gGLState.suppSwapInterval ) + return( false ); + + return( qwglSwapIntervalEXT( on ? 1 : 0 ) ); +#endif +} + +//------------------------------------------------------------------------------ +DisplayDevice* OpenGLDevice::create() +{ + return new OpenGLDevice(); +} diff --git a/Engine/source/platformX86UNIX/x86UNIXOGLVideo.h b/Engine/source/platformX86UNIX/x86UNIXOGLVideo.h new file mode 100644 index 000000000..63eb5c050 --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXOGLVideo.h @@ -0,0 +1,58 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _X86UNIXOGLVIDEO_H_ +#define _X86UNIXOGLVIDEO_H_ + +#ifndef _PLATFORMVIDEO_H_ +#include "platform/platformVideo.h" +#endif + +class OpenGLDevice : public DisplayDevice +{ + static bool smCanSwitchBitDepth; + + bool mRestoreGamma; + U16 mOriginalRamp[256*3]; + + void addResolution(S32 width, S32 height, bool check=true); + + public: + OpenGLDevice(); + virtual ~OpenGLDevice(); + + void initDevice(); + bool activate( U32 width, U32 height, U32 bpp, bool fullScreen ); + void shutdown(); + void destroy(); + bool setScreenMode( U32 width, U32 height, U32 bpp, bool fullScreen, bool forceIt = false, bool repaint = true ); + void swapBuffers(); + const char* getDriverInfo(); + bool getGammaCorrection(F32 &g); + bool setGammaCorrection(F32 g); + bool setVerticalSync( bool on ); + void loadResolutions(); + + static DisplayDevice* create(); +}; + +#endif // _H_X86UNIXOGLVIDEO diff --git a/Engine/source/platformX86UNIX/x86UNIXOpenAL.client.cpp b/Engine/source/platformX86UNIX/x86UNIXOpenAL.client.cpp new file mode 100644 index 000000000..e87920f8a --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXOpenAL.client.cpp @@ -0,0 +1,284 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformX86UNIX/platformX86UNIX.h" +#include "console/console.h" + +#include + +#include +#include +#define INITGUID +#include + + +// Define the OpenAL and Extension Stub functions +#define AL_FUNCTION(fn_return, fn_name, fn_args, fn_value) fn_return stub_##fn_name fn_args{ fn_value } +#include +#include +#include +#undef AL_FUNCTION + + +// Declare the OpenAL and Extension Function pointers +// And initialize them to the stub functions +#define AL_FUNCTION(fn_return,fn_name,fn_args, fn_value) fn_return (*fn_name)fn_args = stub_##fn_name; +#include +#include +#include +#undef AL_FUNCTION + +// Declarations for the "emulated" functions (al functions that don't +// exist in the loki openal implementation) +ALboolean emu_alGetBoolean(ALenum param); +ALint emu_alGetInteger(ALenum param); +ALfloat emu_alGetFloat(ALenum param); +ALdouble emu_alGetDouble(ALenum param); +void emu_alListeneri( ALenum param, ALint value ); +void emu_alGetListener3f(ALenum pname,ALfloat *v1,ALfloat *v2,ALfloat *v3); +ALCdevice* emu_alcGetContextsDevice(ALCcontext *context); + +static void *dlHandle = NULL; +static char* dlError = "no error"; + +/*! Get an "emulated" function address and bind it to the function pointer +*/ +static bool bindEmulatedFunction(void *&fnAddress, const char *name) +{ + fnAddress = NULL; + + if (dStrcmp(name, "alGetBoolean")==0) + fnAddress = (void*)&emu_alGetBoolean; + else if (dStrcmp(name, "alGetInteger")==0) + fnAddress = (void*)&emu_alGetInteger; + else if (dStrcmp(name, "alGetFloat")==0) + fnAddress = (void*)&emu_alGetFloat; + else if (dStrcmp(name, "alGetDouble")==0) + fnAddress = (void*)&emu_alGetDouble; + else if (dStrcmp(name, "alListeneri")==0) + fnAddress = (void*)&emu_alListeneri; + else if (dStrcmp(name, "alGetListener3f")==0) + fnAddress = (void*)&emu_alGetListener3f; + else if (dStrcmp(name, "alcGetContextsDevice")==0) + fnAddress = (void*)&emu_alcGetContextsDevice; + + return fnAddress != NULL; +} + +/*! Get a function address from the OpenAL DLL and bind it to the +* function pointer +*/ +static bool bindFunction( void *&fnAddress, const char *name ) +{ + fnAddress = dlsym(dlHandle, name); + if( !fnAddress ) + if (bindEmulatedFunction(fnAddress, name)) + Con::warnf(ConsoleLogEntry::General, " Missing OpenAL function '%s', using emulated function", name); + else + Con::errorf(ConsoleLogEntry::General, " Missing OpenAL function '%s'", name); + return (fnAddress != NULL); +} + +/*! Get a function address for an OpenAL extension function and bind it +* to it's function pointer +*/ +static bool bindExtensionFunction( void *&fnAddress, const char *name ) +{ + fnAddress = alGetProcAddress( (ALubyte*)name ); + if( !fnAddress ) + Con::errorf(ConsoleLogEntry::General, " Missing OpenAL Extension function '%s'", name); + return (fnAddress != NULL); +} + +/*! Bind the functions in the OpenAL DLL to the al interface functions +*/ +static bool bindOpenALFunctions() +{ + bool result = true; + #define AL_FUNCTION(fn_return, fn_name, fn_args, fn_value) result &= bindFunction( *(void**)&fn_name, #fn_name); + #include + #include + #undef AL_FUNCTION + return result; +} + +/*! Bind the stub functions to the al interface functions +*/ +static void unbindOpenALFunctions() +{ + #define AL_FUNCTION(fn_return, fn_name, fn_args, fn_value) fn_name = stub_##fn_name; + #include + #include + #include + #undef AL_FUNCTION +} + +/*! Bind the EAX Extension functions to the EAX interface functions +*/ +static bool bindEAXFunctions() +{ + bool result = true; + #define AL_FUNCTION(fn_return, fn_name, fn_args, fn_value) result &= bindExtensionFunction( *(void**)&fn_name, #fn_name); + #include + #undef AL_FUNCTION + return result; +} + +// Definitions for the emulated functions +ALboolean emu_alGetBoolean(ALenum param) +{ + ALboolean alboolean; + alGetBooleanv(param, &alboolean); + return alboolean; +} + +ALint emu_alGetInteger(ALenum param) +{ + ALint alint; + alGetIntegerv(param, &alint); + return alint; +} + +ALfloat emu_alGetFloat(ALenum param) +{ + ALfloat alfloat; + alGetFloatv(param, &alfloat); + return alfloat; +} + +ALdouble emu_alGetDouble(ALenum param) +{ + ALdouble aldouble; + alGetDoublev(param, &aldouble); + return aldouble; +} + +void emu_alGetListener3f(ALenum pname,ALfloat *v0,ALfloat *v1,ALfloat *v2) +{ + ALfloat ptArray[10]; + ptArray[0] = *v0; + ptArray[1] = *v1; + ptArray[2] = *v2; + alGetListenerfv(pname, ptArray); + *v0 = ptArray[0]; + *v1 = ptArray[1]; + *v2 = ptArray[2]; +} + +void emu_alListeneri( ALenum param, ALint value ) +{ + alListenerf(param, static_cast(value)); +} + +ALCdevice* emu_alcGetContextsDevice(ALCcontext *context) +{ + // this function isn't emulated + AssertFatal(false, "alcGetContextsDevice is not available"); + return NULL; +} + +namespace Audio +{ + +/*! Shutdown and Unload the OpenAL DLL +*/ +void OpenALDLLShutdown() +{ + if (dlHandle != NULL) + { + dlclose(dlHandle); + // FreeBSD didn't like that const dlerror() was returning. + if ((dlError = (char *)dlerror()) != NULL) + Con::errorf(ConsoleLogEntry::General, " Error unloading OpenAL Library: %s", dlError); + } + dlHandle = NULL; + + unbindOpenALFunctions(); +} + +/*! Dynamically Loads the OpenAL DLL if present and binds all the functions. +* If there is no DLL or an unexpected error occurs binding functions the +* stub functions are automatically bound. +*/ +bool OpenALDLLInit() +{ + OpenALDLLShutdown(); + + const char* libName = "libopenal.so"; + + // these are relative to the current working directory + const char* searchPath[] = { + "lib", + "tplib", // superceeded by "lib", here for backass compatibility + "", // i.e.: current working directory + NULL // this must be last + }; + + char openalPath[4096]; + for (int i = 0; searchPath[i] != NULL; ++i) + { + dSprintf(openalPath, sizeof(openalPath), "%s/%s/%s", + Platform::getWorkingDirectory(), + searchPath[i], + libName); + + Con::printf(" Searching for OpenAl at location : %s", openalPath); + dlHandle = dlopen(openalPath, RTLD_NOW); + if (dlHandle != NULL) + { + // found it + Con::printf(" Loading OpenAL: %s", openalPath); + break; + } + } + + if (dlHandle == NULL) + { + // couldn't find it in our searchPath, try the system path + dlHandle = dlopen(libName, RTLD_NOW); + if (dlHandle != NULL) + Con::printf(" Loading OpenAL from system (dlopen) path"); + } + + if (dlHandle != NULL) + { + // if the DLL loaded bind the OpenAL function pointers + if(bindOpenALFunctions()) + { + // if EAX is available bind it's function pointers + if (alIsExtensionPresent((ALubyte*)"EAX" )) + bindEAXFunctions(); + return(true); + } + + // an error occured, shutdown + OpenALDLLShutdown(); + } + else + { + Con::errorf(ConsoleLogEntry::General, " Error loading OpenAL Library: %s", dlerror()); + } + return(false); +} + +} // end namespace Audio + diff --git a/Engine/source/platformX86UNIX/x86UNIXProcessControl.cpp b/Engine/source/platformX86UNIX/x86UNIXProcessControl.cpp new file mode 100644 index 000000000..bbc103209 --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXProcessControl.cpp @@ -0,0 +1,225 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformX86UNIX/platformX86UNIX.h" +#include "platformX86UNIX/x86UNIXState.h" +#include "platformX86UNIX/x86UNIXStdConsole.h" +#include "platform/platformInput.h" +#include "console/console.h" + +#include +#include +#include + +#ifndef DEDICATED +#include +#endif + +//----------------------------------------------------------------------------- +// This is a mainly a debugging function for intercepting a nonzero exit code +// and generating a core dump for a stack trace. +// Need an S64 here because postQuitMessage uses a U32, and +// forceshutdown uses an S32. So S64 is needed to +// accomodate them both +static void CheckExitCode(S64 exitCode) +{ + if (exitCode != 0) + { + Con::errorf(ConsoleLogEntry::General, + "Nonzero exit code: %d, triggering SIGSEGV for core dump", + exitCode); + kill(getpid(), SIGSEGV); + } +} + +//----------------------------------------------------------------------------- +static void SignalHandler(int sigtype) +{ + if (sigtype == SIGSEGV || sigtype == SIGTRAP) + { + signal(SIGSEGV, SIG_DFL); + signal(SIGTRAP, SIG_DFL); + // restore the signal handling to default so that we don't get into + // a crash loop with ImmediateShutdown + ImmediateShutdown(-sigtype, sigtype); + } + else + { + signal(sigtype, SIG_DFL); + dPrintf("Unknown signal caught by SignalHandler: %d\n", sigtype); + // exit to be safe + ImmediateShutdown(1); + } +} + +//----------------------------------------------------------------------------- +void Cleanup(bool minimal) +{ + if (!minimal) + { + Input::destroy(); + } + + StdConsole::destroy(); + SDL_Quit(); +} + +//----------------------------------------------------------------------------- +void ImmediateShutdown(S32 exitCode, S32 signalNum) +{ + bool segfault = signalNum > 0; + + Cleanup(segfault); + + if (!segfault) + { + dPrintf("Exiting\n"); + // exit (doesn't call destructors) + _exit(exitCode); + } + else + { +// there is a problem in kernel 2.4.17 which causes a hang when a segfault +// occurs. also subsequent runs of "ps" will hang and the machine has to be +// hard reset to clear up the problem +// JMQ: this bug appears to be fixed in 2.4.18 +//#define KERNEL_2_4_WORKAROUND +#ifdef KERNEL_2_4_WORKAROUND + dPrintf("Segmentation Fault (Exiting without core dump due to #define KERNEL_2_4_WORKAROUND)\n"); + dFflushStdout(); + _exit(exitCode); +#else + // kill with signal + kill(getpid(), signalNum); +#endif + } + +} + +//----------------------------------------------------------------------------- +void ProcessControlInit() +{ + // JMQ: ignore IO signals background read/write terminal (so that we don't + // get suspended in daemon mode) + signal(SIGTTIN, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + + // we're not interested in the exit status of child processes, so this + // prevents zombies from accumulating. +#if defined(__FreeBSD__) + signal(SIGCHLD, SIG_IGN); +#else + signal(SIGCLD, SIG_IGN); +#endif + + // install signal handler for SIGSEGV, so that we can attempt + // clean shutdown + signal(SIGSEGV, &SignalHandler); + signal(SIGTRAP, &SignalHandler); +} + +//----------------------------------------------------------------------------- +void Platform::postQuitMessage(const U32 in_quitVal) +{ + // if we have a window send a quit event, otherwise just force shutdown +#if 0 + if (x86UNIXState->windowCreated()) + { + CheckExitCode(in_quitVal); + SendQuitEvent(); + } + else +#endif + { + forceShutdown(in_quitVal); + } +} + +//----------------------------------------------------------------------------- +void Platform::debugBreak() +{ + // in windows, "Calling DebugBreak causes the program to display + // a dialog box as if it had crashed." So we segfault. + Con::errorf(ConsoleLogEntry::General, + "Platform::debugBreak: triggering SIGSEGV for core dump"); + //kill(getpid(), SIGSEGV); + kill(getpid(), SIGTRAP); +} + +//----------------------------------------------------------------------------- +void Platform::forceShutdown(S32 returnValue) +{ + CheckExitCode(returnValue); + +#if 0 + // if a dedicated server is running, turn it off + if (x86UNIXState->isDedicated() && Game->isRunning()) + Game->setRunning(false); + else +#endif + ImmediateShutdown(returnValue); +} + +//----------------------------------------------------------------------------- +void Platform::outputDebugString(const char *string, ...) +{ + char buffer[2048]; + + va_list args; + va_start( args, string ); + + dVsprintf( buffer, sizeof(buffer), string, args ); + va_end( args ); + + U32 length = dStrlen(buffer); + if( length == (sizeof(buffer) - 1 ) ) + length--; + + buffer[length++] = '\n'; + buffer[length] = '\0'; + + fwrite(buffer, sizeof(char), length, stderr); +} + +//----------------------------------------------------------------------------- +// testing function +ConsoleFunction(debug_debugbreak, void, 1, 1, "debug_debugbreak()") +{ + Platform::debugBreak(); +} + +//----------------------------------------------------------------------------- +void Platform::restartInstance() +{ +/* + if (Game->isRunning() ) + { + //Con::errorf( "Error restarting Instance. Game is Still running!"); + return; + } + + char cmd[2048]; + sprintf(cmd, "%s &", x86UNIXState->getExePathName()); + system(cmd); +*/ + exit(0); +} diff --git a/Engine/source/platformX86UNIX/x86UNIXRedbook.cpp b/Engine/source/platformX86UNIX/x86UNIXRedbook.cpp new file mode 100644 index 000000000..cfed8008d --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXRedbook.cpp @@ -0,0 +1,455 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/console.h" +#include "platformX86UNIX/platformX86UNIX.h" +#include "platform/platformRedBook.h" +#include "core/strings/stringFunctions.h" + +#if defined(__linux__) +#include +#include +#include +#include +#endif + +#include + +class UnixRedBookDevice : public RedBookDevice +{ +#if !defined(__FreeBSD__) + private: + S32 mDeviceId; + SDL_CD *mCD; + cdrom_volctrl mOriginalVolume; + bool mVolumeInitialized; +#endif + bool mPlaying; + + void openVolume(); + void closeVolume(); + void setLastError(const char *); + + public: + UnixRedBookDevice(); + ~UnixRedBookDevice(); + + bool open(); + bool close(); + bool play(U32); + bool stop(); + bool getTrackCount(U32 *); + bool getVolume(F32 *); + bool setVolume(F32); + + bool isPlaying() { return mPlaying; } + bool updateStatus(); + void setDeviceInfo(S32 deviceId, const char *deviceName); +}; + +//------------------------------------------------------------------------------- +// Class: UnixRedBookDevice +//------------------------------------------------------------------------------- +UnixRedBookDevice::UnixRedBookDevice() +{ +#if !defined(__FreeBSD__) + mVolumeInitialized = false; + mDeviceId = -1; + mDeviceName = NULL; + mCD = NULL; + mPlaying = false; +#endif // !defined(__FreeBSD__) +} + +//------------------------------------------------------------------------------ +UnixRedBookDevice::~UnixRedBookDevice() +{ +#if !defined(__FreeBSD__) + close(); +#endif // !defined(__FreeBSD__) +} + +//------------------------------------------------------------------------------ +bool UnixRedBookDevice::updateStatus() +{ +#if !defined(__FreeBSD__) + AssertFatal(mCD, "mCD is NULL"); + + CDstatus status = SDL_CDStatus(mCD); + if (status == CD_ERROR) + { + setLastError("Error accessing device"); + return(false); + } + else if (status == CD_TRAYEMPTY) + { + setLastError("CD tray empty"); + return false; + } + + mPlaying = (status == CD_PLAYING); + return true; +#endif // !defined(__FreeBSD__) +} + +//------------------------------------------------------------------------------ +void UnixRedBookDevice::setDeviceInfo(S32 deviceId, const char *deviceName) +{ +#if !defined(__FreeBSD__) + mDeviceId = deviceId; + mDeviceName = new char[dStrlen(deviceName) + 1]; + dStrcpy(mDeviceName, deviceName); +#endif // !defined(__FreeBSD__) +} + +//------------------------------------------------------------------------------ +bool UnixRedBookDevice::open() +{ +#if !defined(__FreeBSD__) + if(mAcquired) + { + setLastError("Device is already open."); + return(false); + } + + // open the device + mCD = SDL_CDOpen(mDeviceId); + if (mCD == NULL) + { + setLastError(SDL_GetError()); + return false; + } + + mAcquired = true; + + openVolume(); + setLastError(""); + return(true); +#endif // !defined(__FreeBSD__) +} + +//------------------------------------------------------------------------------ +bool UnixRedBookDevice::close() +{ +#if !defined(__FreeBSD__) + if(!mAcquired) + { + setLastError("Device has not been acquired"); + return(false); + } + + stop(); + closeVolume(); + + if (mCD != NULL) + { + SDL_CDClose(mCD); + mCD = NULL; + } + + mAcquired = false; + setLastError(""); + return(true); +#endif // !defined(__FreeBSD__) +} + +//------------------------------------------------------------------------------ +bool UnixRedBookDevice::play(U32 track) +{ +#if !defined(__FreeBSD__) + if(!mAcquired) + { + setLastError("Device has not been acquired"); + return(false); + } + + U32 numTracks; + if(!getTrackCount(&numTracks)) + return(false); + + if(track >= numTracks) + { + setLastError("Track index is out of range"); + return(false); + } + + if (!updateStatus()) + return false; + + AssertFatal(mCD, "mCD is NULL"); + if (SDL_CDPlayTracks(mCD, track, 0, 1, 0) == -1) + { + setLastError(SDL_GetError()); + return false; + } + + mPlaying = true; + + setLastError(""); + return(true); +#endif // !defined(__FreeBSD__) +} + +//------------------------------------------------------------------------------ +bool UnixRedBookDevice::stop() +{ +#if !defined(__FreeBSD__) + if(!mAcquired) + { + setLastError("Device has not been acquired"); + return(false); + } + + AssertFatal(mCD, "mCD is NULL"); + + if (SDL_CDStop(mCD) == -1) + { + setLastError(SDL_GetError()); + return(false); + } + + mPlaying = false; + + setLastError(""); + return(true); +#endif // !defined(__FreeBSD__) +} + +//------------------------------------------------------------------------------ +bool UnixRedBookDevice::getTrackCount(U32 * numTracks) +{ +#if !defined(__FreeBSD__) + if(!mAcquired) + { + setLastError("Device has not been acquired"); + return(false); + } + + if (!updateStatus()) + return false; + + AssertFatal(mCD, "mCD is NULL"); + *numTracks = mCD->numtracks; + + return(true); +#endif // !defined(__FreeBSD__) +} + +template +static inline Type max(Type v1, Type v2) +{ +#if !defined(__FreeBSD__) + if (v1 <= v2) + return v2; + else + return v1; +#endif // !defined(__FreeBSD__) +} +//------------------------------------------------------------------------------ +bool UnixRedBookDevice::getVolume(F32 * volume) +{ +#if !defined(__FreeBSD__) + if(!mAcquired) + { + setLastError("Device has not been acquired"); + return(false); + } + + if(!mVolumeInitialized) + { + setLastError("Volume failed to initialize"); + return(false); + } + +#if defined(__linux__) + AssertFatal(mCD, "mCD is NULL"); + + setLastError(""); + cdrom_volctrl sysvol; + if (ioctl(mCD->id, CDROMVOLREAD, &sysvol) == -1) + { + setLastError(strerror(errno)); + return(false); + } + U8 maxVol = max(sysvol.channel0, sysvol.channel1); + // JMQTODO: support different left/right channel volumes? + *volume = static_cast(maxVol) / 255.f; + return true; +#else + return(false); +#endif +#endif // !defined(__FreeBSD__) +} + +//------------------------------------------------------------------------------ +bool UnixRedBookDevice::setVolume(F32 volume) +{ +#if !defined(__FreeBSD__) + if(!mAcquired) + { + setLastError("Device has not been acquired"); + return(false); + } + + if(!mVolumeInitialized) + { + setLastError("Volume failed to initialize"); + return(false); + } + +#if defined(__linux__) + AssertFatal(mCD, "mCD is NULL"); + + setLastError(""); + cdrom_volctrl sysvol; + volume = volume * 255.f; + if (volume > 255) + volume = 255; + if (volume < 0) + volume = 0; + sysvol.channel0 = sysvol.channel1 = static_cast<__u8>(volume); + if (ioctl(mCD->id, CDROMVOLCTRL, &sysvol) == -1) + { + setLastError(strerror(errno)); + return(false); + } + return true; +#else + return(false); +#endif +#endif // !defined(__FreeBSD__) +} + +//------------------------------------------------------------------------------ +void UnixRedBookDevice::openVolume() +{ +#if !defined(__FreeBSD__) +// Its unforunate that we have to do it this way, but SDL does not currently +// support setting CD audio volume +#if defined(__linux__) + AssertFatal(mCD, "mCD is NULL"); + + setLastError(""); + + if (ioctl(mCD->id, CDROMVOLREAD, &mOriginalVolume) == -1) + { + setLastError(strerror(errno)); + return; + } + + mVolumeInitialized = true; +#else + setLastError("Volume failed to initialize"); +#endif +#endif // !defined(__FreeBSD__) +} + +void UnixRedBookDevice::closeVolume() +{ +#if !defined(__FreeBSD__) + if(!mVolumeInitialized) + return; + +#if defined(__linux__) + AssertFatal(mCD, "mCD is NULL"); + + setLastError(""); + + if (ioctl(mCD->id, CDROMVOLCTRL, &mOriginalVolume) == -1) + { + setLastError(strerror(errno)); + return; + } +#endif + + mVolumeInitialized = false; +#endif // !defined(__FreeBSD__) +} + +//------------------------------------------------------------------------------ +void UnixRedBookDevice::setLastError(const char * error) +{ +#if !defined(__FreeBSD__) + RedBook::setLastError(error); +#endif // !defined(__FreeBSD__) +} + +//------------------------------------------------------------------------------ +void InstallRedBookDevices() +{ +#if !defined(__FreeBSD__) + Con::printf("CD Audio Init:"); + if (SDL_InitSubSystem(SDL_INIT_CDROM) == -1) + { + Con::printf(" Unable to initialize CD Audio: %s", SDL_GetError()); + return; + } + + S32 numDrives = SDL_CDNumDrives(); + if (numDrives == 0) + { + Con::printf(" No drives found."); + return; + } + + for (int i = 0; i < numDrives; ++i) + { + const char * deviceName = SDL_CDName(i); + Con::printf(" Installing CD Audio device: %s", deviceName); + + UnixRedBookDevice * device = new UnixRedBookDevice; + device->setDeviceInfo(i, deviceName); + RedBook::installDevice(device); + } + + Con::printf(" "); +#endif // !defined(__FreeBSD__) +} + +//------------------------------------------------------------------------------ +void PollRedbookDevices() +{ +#if !defined(__FreeBSD__) + UnixRedBookDevice *device = dynamic_cast(RedBook::getCurrentDevice()); + + if (device == NULL || !device->isPlaying()) + return; + + static const U32 PollDelay = 1000; + + static U32 lastPollTime = 0; + U32 curTime = Platform::getVirtualMilliseconds(); + + if (lastPollTime != 0 && + (curTime - lastPollTime) < PollDelay) + return; + + lastPollTime = curTime; + + if (device->isPlaying()) + { + device->updateStatus(); + if (!device->isPlaying()) + RedBook::handleCallback(RedBook::PlayFinished); + } +#endif // !defined(__FreeBSD__) +} diff --git a/Engine/source/platformX86UNIX/x86UNIXState.h b/Engine/source/platformX86UNIX/x86UNIXState.h new file mode 100644 index 000000000..eb5920ce1 --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXState.h @@ -0,0 +1,274 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "math/mPoint2.h" +#include "platformX86UNIX/platformX86UNIX.h" +//#include "platformX86UNIX/platformGL.h" +#include "core/strings/stringFunctions.h" + +#ifndef DEDICATED +#include // for Display, Window and other X mojo +#else +#define Display int +#define Window int +#define Screen int +#endif + +#include // for basename + +typedef void (*LockFunc_t)(void); + +class DisplayPtrManager; + +class x86UNIXPlatformState +{ + friend class DisplayPtrManager; + + private: + Point2I mDesktopSize; + Point2I mWindowSize; + S32 mDesktopBpp; + Display *mDisplay; + Window mCurrentWindow; + Screen *mScreenPointer; + int mScreenNumber; + char mWindowName[40]; + char mExePathName[4096]; + char mExeName[40]; + bool mWindowCreated; + bool mWindowActive; + bool mWindowLocked; + bool mXWindowsRunning; + bool mDedicated; + bool mCDAudioEnabled; + bool mDSleep; + bool mUseRedirect; + + // Access to the display* needs to be controlled because the SDL event + // loop runs in a separate thread. If you need the display pointer, + // use the DisplayPtrManager class. See the clipboard functions in + // x86unixinput.cc for an example. + Display *getDisplayPointer() { return mDisplay; } + + public: + U32 currentTime; + + void setDisplayPointer( Display *displayPointer ) + { mDisplay = displayPointer; } + + void setScreenNumber( int newNumber ) { mScreenNumber = newNumber; } + int getScreenNumber() { return mScreenNumber; } + + void setScreenPointer( Screen *newScreenPointer ) + { mScreenPointer = newScreenPointer; } + Screen * getScreenPointer() { return mScreenPointer; } + + // for compatibility, convert 24 bpp to 32 + void setDesktopBpp( S32 bpp ) + { + if (bpp == 24) + mDesktopBpp = 32; + else + mDesktopBpp = bpp; + } + S32 getDesktopBpp() { return mDesktopBpp; } + + void setDesktopSize( S32 horizontal, S32 vertical ) + { mDesktopSize.set( horizontal, vertical ); } + Point2I getDesktopSize() { return mDesktopSize; } + + void setWindow( Window newWindow ) { mCurrentWindow = newWindow; } + Window getWindow() { return mCurrentWindow; } + + void setWindowSize (S32 horizontal, S32 vertical ) + { mWindowSize.set ( horizontal, vertical ); } + void setWindowSize( Point2I size ) { mWindowSize = size; } + Point2I& getWindowSize() { return ( mWindowSize ); } + + void setWindowName (const char * windowName) + { + if (windowName == NULL) + dStrncpy( mWindowName, "", sizeof( mWindowName )); + else + dStrncpy( mWindowName, windowName, sizeof( mWindowName ) ); + } + const char * getWindowName() { return mWindowName; } + + void setExePathName(const char* exePathName) + { + if (exePathName == NULL) + dStrncpy(mExePathName, "", sizeof(mExePathName)); + else + dStrncpy(mExePathName, exePathName, sizeof(mExePathName)); + + // set the base exe name field + char tempBuf[2048]; + dStrncpy(tempBuf, mExePathName, 2048); + dStrncpy(mExeName, basename(tempBuf), sizeof(mExeName)); + } + const char * getExePathName() { return mExePathName; } + const char * getExeName() { return mExeName; } + + bool windowCreated() { return mWindowCreated; } + bool windowActive() { return mWindowActive; } + bool windowLocked() { return mWindowLocked; } + void setWindowCreated(bool windowCreated) + { mWindowCreated = windowCreated; } + void setWindowActive(bool windowActive) + { mWindowActive = windowActive; } + void setWindowLocked(bool windowLocked) + { mWindowLocked = windowLocked; } + + bool isXWindowsRunning() { return mXWindowsRunning; } + void setXWindowsRunning(bool running) { mXWindowsRunning = running; } + + bool isDedicated() { return mDedicated; } + void setDedicated(bool dedicated) { mDedicated = dedicated; } + + bool getCDAudioEnabled() { return mCDAudioEnabled; } + void setCDAudioEnabled(bool enabled) { mCDAudioEnabled = enabled; } + + bool getDSleep() { return mDSleep; } + void setDSleep(bool enabled) { mDSleep = enabled; } + + bool getUseRedirect() { return mUseRedirect; } + void setUseRedirect(bool enabled) { mUseRedirect = enabled; } + + x86UNIXPlatformState() + { + currentTime = 0; + mDesktopBpp = 16; + mDesktopSize.set( 0, 0 ); + mWindowSize.set( 800, 600 ); + setWindowName("Torque"); + setExePathName(NULL); + mWindowCreated = mWindowActive = mWindowLocked = false; + mXWindowsRunning = false; + mDedicated = false; + mCDAudioEnabled = false; + mDSleep = false; +#ifdef USE_FILE_REDIRECT + mUseRedirect = true; +#else + mUseRedirect = false; +#endif + } +}; + +extern x86UNIXPlatformState * x86UNIXState; + +class DisplayPtrManager +{ + // static interface + private: + static bool sgDisplayLocked; + static LockFunc_t sgLockFunc; + static LockFunc_t sgUnlockFunc; + + static bool lockDisplay() + { + if (!sgDisplayLocked && sgLockFunc) + { + sgLockFunc(); + sgDisplayLocked = true; + return true; + } + else + return false; + } + static void unlockDisplay() + { + if (sgDisplayLocked && sgUnlockFunc) + { + sgUnlockFunc(); + sgDisplayLocked = false; + } + } + + //friend Display* x86UNIXPlatformState::getDisplayPointer(); + + public: + static void setDisplayLockFunction(LockFunc_t lockFunc) + { sgLockFunc = lockFunc; } + static void setDisplayUnlockFunction(LockFunc_t unlockFunc) + { sgUnlockFunc = unlockFunc; } + + // nonstatic interface + private: + bool mAcquiredLock; // true if this instance acquired the display lock + // (multiple instances of DisplayPtrManager can coexist, but only + // the first to access the display pointer will be responsible for + // acquiring and releasing the lock) + bool mOpenedDisplay; // true if this instance created a display pointer + // because the one in platform state was null. + Display* mDisplay; + + private: + Display* openDisplay() + { +#ifndef DEDICATED + mDisplay = XOpenDisplay(NULL); + if (mDisplay != NULL) + mOpenedDisplay = true; +#endif + return mDisplay; + } + + void closeDisplay() + { + if (mOpenedDisplay) + { +#ifndef DEDICATED + XCloseDisplay(mDisplay); + mDisplay = NULL; + mOpenedDisplay = false; +#endif + } + } + public: + DisplayPtrManager() + { + mAcquiredLock = false; + mOpenedDisplay = false; + mDisplay = NULL; + } + + ~DisplayPtrManager() + { + if (mAcquiredLock) + { + DisplayPtrManager::unlockDisplay(); + mAcquiredLock = false; + } + closeDisplay(); + } + + Display* getDisplayPointer() + { + Display* display = x86UNIXState->getDisplayPointer(); + if (display == NULL) + return openDisplay(); + + mAcquiredLock = DisplayPtrManager::lockDisplay(); + return display; + } +}; diff --git a/Engine/source/platformX86UNIX/x86UNIXStdConsole.h b/Engine/source/platformX86UNIX/x86UNIXStdConsole.h new file mode 100644 index 000000000..29d50d905 --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXStdConsole.h @@ -0,0 +1,73 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _X86UNIXSTDCONSOLE_H_ +#define _X86UNIXSTDCONSOLE_H_ + +#define MAX_CMDS 10 +#ifndef _CONSOLE_H_ +#include "console/console.h" +#endif +#ifndef _EVENT_H_ +#include "platform/event.h" +#endif + +#include + +class StdConsole +{ + bool stdConsoleEnabled; + // true if we're running in the background + bool inBackground; + + int stdOut; + int stdIn; + int stdErr; + char inbuf[512]; + S32 inpos; + bool lineOutput; + char curTabComplete[512]; + S32 tabCompleteStart; + char rgCmds[MAX_CMDS][512]; + S32 iCmdIndex; + + // this holds the original terminal state + // before we messed with it + struct termios *originalTermState; + + void printf(const char *s, ...); + +public: + StdConsole(); + virtual ~StdConsole(); + void process(); + void enable(bool); + void processConsoleLine(const char *consoleLine); + static void create(); + static void destroy(); + static bool isEnabled(); + void resetTerminal(); +}; + +extern StdConsole *stdConsole; + +#endif diff --git a/Engine/source/platformX86UNIX/x86UNIXStrings.cpp b/Engine/source/platformX86UNIX/x86UNIXStrings.cpp new file mode 100644 index 000000000..1e19aa9da --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXStrings.cpp @@ -0,0 +1,70 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformX86UNIX/platformX86UNIX.h" +#include "core/strings/stringFunctions.h" +#include +#include +#include + + +char *stristr(char *szStringToBeSearched, const char *szSubstringToSearchFor) +{ + char *pPos = NULL; + char *szCopy1 = NULL; + char *szCopy2 = NULL; + + // verify parameters + if ( szStringToBeSearched == NULL || + szSubstringToSearchFor == NULL ) + { + return szStringToBeSearched; + } + + // empty substring - return input (consistent with strstr) + if (strlen(szSubstringToSearchFor) == 0 ) { + return szStringToBeSearched; + } + + szCopy1 = dStrlwr(strdup(szStringToBeSearched)); + szCopy2 = dStrlwr(strdup(szSubstringToSearchFor)); + + if ( szCopy1 == NULL || szCopy2 == NULL ) { + // another option is to raise an exception here + free((void*)szCopy1); + free((void*)szCopy2); + return NULL; + } + + pPos = strstr(szCopy1, szCopy2); + + if ( pPos != NULL ) { + // map to the original string + pPos = szStringToBeSearched + (pPos - szCopy1); + } + + free((void*)szCopy1); + free((void*)szCopy2); + + return pPos; +} // stristr(...) + diff --git a/Engine/source/platformX86UNIX/x86UNIXStub.dedicated.cpp b/Engine/source/platformX86UNIX/x86UNIXStub.dedicated.cpp new file mode 100644 index 000000000..b9814862a --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXStub.dedicated.cpp @@ -0,0 +1,141 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platformInput.h" +#include "platformX86UNIX/x86UNIXFont.h" +#include "platform/nativeDialogs/fileDialog.h" + +#if 0 +// declare stub functions +#define GL_FUNCTION(fn_return, fn_name, fn_args, fn_value) fn_return stub_##fn_name fn_args{ fn_value } +#include "platform/GLCoreFunc.h" +#include "platform/GLExtFunc.h" +#include "platform/GLUFunc.h" +#undef GL_FUNCTION + +// point gl function pointers at stub functions +#define GL_FUNCTION(fn_return,fn_name,fn_args, fn_value) fn_return (*fn_name)fn_args = stub_##fn_name; +#include "platform/GLCoreFunc.h" +#include "platform/GLExtFunc.h" +#include "platform/GLUFunc.h" +#undef GL_FUNCTION + +GLState gGLState; +bool gOpenGLDisablePT = false; +bool gOpenGLDisableCVA = false; +bool gOpenGLDisableTEC = false; +bool gOpenGLDisableARBMT = false; +bool gOpenGLDisableFC = false; +bool gOpenGLDisableTCompress = false; +bool gOpenGLNoEnvColor = false; +float gOpenGLGammaCorrection = 0.5; +bool gOpenGLNoDrawArraysAlpha = false; + +// AL stubs +//#include +//#include +//#define INITGUID +//#include + +// Define the OpenAL and Extension Stub functions +#define AL_FUNCTION(fn_return, fn_name, fn_args, fn_value) fn_return stub_##fn_name fn_args{ fn_value } +#include +#include +#include +#undef AL_FUNCTION +#include "platform/platformInput.h" + +// Declare the OpenAL and Extension Function pointers +// And initialize them to the stub functions +#define AL_FUNCTION(fn_return,fn_name,fn_args, fn_value) fn_return (*fn_name)fn_args = stub_##fn_name; +#include +#include +#include +#undef AL_FUNCTION + +namespace Audio +{ +bool OpenALDLLInit() { return false; } +void OpenALDLLShutdown() {} +} +#endif + +// Platform Stubs + +// clipboard +const char* Platform::getClipboard() { return ""; } +bool Platform::setClipboard(const char *text) { return false; } + +// fs +void Platform::openFolder(const char *path) { } +void Platform::openFile(const char *path) { } + +// window +bool Platform::displaySplashWindow() { return false; } + +// font +PlatformFont *createPlatformFont(const char *name, U32 size, U32 charset) { return NULL; } +bool x86UNIXFont::create(const char *name, U32 size, U32 charset) { return false; } + +// web +bool Platform::openWebBrowser(const char *) { return false; } + +// messagebox +void Platform::AlertOK(const char *, const char *) {} +bool Platform::AlertOKCancel(const char *, const char *) { return false; } +S32 Platform::messageBox(char const*, char const*, MBButtons, MBIcons) { return 0; } +bool Platform::AlertRetry(char const*, char const*) { return false ; } + +// file dialog +IMPLEMENT_CONOBJECT(FileDialog); +FileDialog::FileDialog() {} +FileDialog::~FileDialog() {} +bool FileDialog::Execute() { return false; } +void FileDialog::initPersistFields() { Parent::initPersistFields(); } + +class FileDialogOpaqueData {}; + +FileDialogData::FileDialogData() {} +FileDialogData::~FileDialogData() {} + +IMPLEMENT_CONOBJECT(OpenFileDialog); +OpenFileDialog::OpenFileDialog() {} +OpenFileDialog::~OpenFileDialog() {} +void OpenFileDialog::initPersistFields() { Parent::initPersistFields(); } + +// Input stubs +void Input::init() {} +void Input::destroy() {} +bool Input::enable() { return false; } +void Input::disable() {} +void Input::activate() {} +void Input::deactivate() {} +U16 Input::getAscii( U16 keyCode, KEY_STATE keyState ) { return 0; } +U16 Input::getKeyCode( U16 asciiCode ) { return 0; } +bool Input::isEnabled() { return false; } +bool Input::isActive() { return false; } +void Input::process() {} +InputManager* Input::getManager() { return NULL; } +InputEvent Input::smInputEvent; +U8 Input::smModifierKeys; + +bool OpenGLInit() { return false; } diff --git a/Engine/source/platformX86UNIX/x86UNIXTime.cpp b/Engine/source/platformX86UNIX/x86UNIXTime.cpp new file mode 100644 index 000000000..ec3e73642 --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXTime.cpp @@ -0,0 +1,161 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformX86UNIX/platformX86UNIX.h" +#include "platform/platformTimer.h" +#include "time.h" +#include +#include +#include +#include +#include + +static U32 sgCurrentTime = 0; + +U32 x86UNIXGetTickCount(); + +//-------------------------------------- +void Platform::getLocalTime(LocalTime <) +{ + struct tm *systime; + time_t long_time; + + time( &long_time ); // Get time as long integer. + systime = localtime( &long_time ); // Convert to local time. + + lt.sec = systime->tm_sec; + lt.min = systime->tm_min; + lt.hour = systime->tm_hour; + lt.month = systime->tm_mon; + lt.monthday = systime->tm_mday; + lt.weekday = systime->tm_wday; + lt.year = systime->tm_year; + lt.yearday = systime->tm_yday; + lt.isdst = systime->tm_isdst; +} + +String Platform::localTimeToString( const LocalTime < ) +{ + tm systime; + + systime.tm_sec = lt.sec; + systime.tm_min = lt.min; + systime.tm_hour = lt.hour; + systime.tm_mon = lt.month; + systime.tm_mday = lt.monthday; + systime.tm_wday = lt.weekday; + systime.tm_year = lt.year; + systime.tm_yday = lt.yearday; + systime.tm_isdst = lt.isdst; + + return asctime( &systime ); +} + +U32 Platform::getTime() +{ + time_t long_time; + time( &long_time ); + return long_time; +} + +U32 Platform::getRealMilliseconds() +{ +// struct rusage usageStats; +// getrusage(RUSAGE_SELF, &usageStats); +// return usageStats.ru_utime.tv_usec; + return x86UNIXGetTickCount(); +} + +U32 Platform::getVirtualMilliseconds() +{ + return sgCurrentTime; +} + +void Platform::advanceTime(U32 delta) +{ + sgCurrentTime += delta; +} + +void Platform::fileToLocalTime(const FileTime & ft, LocalTime * lt) +{ + if(!lt) + return; + + time_t long_time = ft; + + struct tm systime; + + /// Convert to local time, thread safe. + localtime_r( &long_time, &systime ); + + /// Fill the return struct + lt->sec = systime.tm_sec; + lt->min = systime.tm_min; + lt->hour = systime.tm_hour; + lt->month = systime.tm_mon; + lt->monthday = systime.tm_mday; + lt->weekday = systime.tm_wday; + lt->year = systime.tm_year; + lt->yearday = systime.tm_yday; + lt->isdst = systime.tm_isdst; +} + +PlatformTimer *PlatformTimer::create() +{ + return new DefaultPlatformTimer(); +} +//------------------------------------------------------------------------------ +//-------------------------------------- x86UNIX Implementation +// +// +static bool sg_initialized = false; +static U32 sg_secsOffset = 0; +//-------------------------------------- +U32 x86UNIXGetTickCount() +{ + // TODO: What happens when crossing a day boundary? + // + timeval t; + + if (sg_initialized == false) { + sg_initialized = true; + + gettimeofday(&t, NULL); + sg_secsOffset = t.tv_sec; + } + + gettimeofday(&t, NULL); + + U32 secs = t.tv_sec - sg_secsOffset; + U32 uSecs = t.tv_usec; + + // Make granularity 1 ms + return (secs * 1000) + (uSecs / 1000); +} + + +void Platform::sleep(U32 ms) +{ + // note: this will overflow if you want to sleep for more than 49 days. just so ye know. + usleep( ms * 1000 ); +} + diff --git a/Engine/source/platformX86UNIX/x86UNIXUtils.cpp b/Engine/source/platformX86UNIX/x86UNIXUtils.cpp new file mode 100644 index 000000000..a7b0eafa7 --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXUtils.cpp @@ -0,0 +1,284 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include +#include +#include +#include + +// for UnixCommandExecutor +#include +#include +#include +#include + +#include "platformX86UNIX/platformX86UNIX.h" +#include "platformX86UNIX/x86UNIXUtils.h" + +UnixUtils *UUtils = NULL; +UnixUtils utils; + +UnixUtils::UnixUtils() +{ + UUtils = this; + + mUnameInfo = (struct utsname*)dRealMalloc(sizeof(struct utsname));; + if (uname(mUnameInfo) == -1) + { + // oh well + dRealFree(mUnameInfo); + mUnameInfo = NULL; + } +} + +UnixUtils::~UnixUtils() +{ + if (mUnameInfo != NULL) + { + dRealFree(mUnameInfo); + mUnameInfo = NULL; + } +} + +const char* UnixUtils::getOSName() +{ + if (mUnameInfo == NULL) + return ""; + + return mUnameInfo->sysname; +} + +bool UnixUtils::inBackground() +{ + int terminalGroupId = tcgetpgrp(fileno(stdin)); + int myPid = getpid(); + if (terminalGroupId != myPid) + return true; + else + return false; +} + +//----------------------------------------------------------------------------- +// UnixCommandExecutor +void UnixCommandExecutor::clearFields() +{ + mRet = -1; + mStdoutSave = -1; + mStderrSave = -1; + mPipeFiledes[0] = -1; + mPipeFiledes[1] = -1; + mChildPID = -1; + mBytesRead = 0; + mStdoutClosed = false; + mStderrClosed = false; + mChildExited = false; +} + +UnixCommandExecutor::UnixCommandExecutor() +{ + clearFields(); +} + +UnixCommandExecutor::~UnixCommandExecutor() +{ + cleanup(); +} + +int UnixCommandExecutor::exec(char* args[], + char* stdoutCapture, int stdoutCaptureSize) +{ + // check for shitty parameters + if (args == NULL || stdoutCapture == NULL || + stdoutCaptureSize <= 0) + return -666; + + // we're going to be redirecting stdout, so save it so that we can + // restore it + mRet = dup(1); + if (mRet == -1) + { + cleanup(); + return mRet; + } + mStdoutSave = mRet; + + // save stderr + mRet = dup(2); + if (mRet == -1) + { + cleanup(); + return mRet; + } + mStderrSave = mRet; + + // we'll need some pipe action for communicating with subprocess + mRet = pipe(mPipeFiledes); + if (mRet == -1) + { + cleanup(); + return mRet; + } + + // close stdout + mRet = close(1); + if (mRet == -1) + { + cleanup(); + return mRet; + } + mStdoutClosed = true; + + // stderr just gets closed and the output discarded + mRet = close(2); + if (mRet == -1) + { + cleanup(); + return mRet; + } + mStderrClosed = true; + + // dup the pipe output into stdout + mRet = dup2(mPipeFiledes[1], 1); + if (mRet == -1) + { + cleanup(); + return mRet; + } + + // fork + mRet = fork(); + if (mRet == -1) + { + cleanup(); + return mRet; + } + + if (mRet == 0) + { + // child process + + //close(mPipeFiledes[0]); + mRet = execvp(args[0], args); + // if exec returns, some bad shit went down, so just + // get outta here + exit(mRet); + } + + // parent process + mChildPID = mRet; + + // need to suck in data from pipe while child is running, + // otherwise child will eventually block on write and we'll + // wait forever + memset(stdoutCapture, 0, stdoutCaptureSize); + + // set input to be non blocking so that we don't block on read + mRet = fcntl(mPipeFiledes[0], F_SETFL, O_NONBLOCK); + if (mRet == -1) + { + cleanup(); + return mRet; + } + + // check to see if child has exited + mRet = waitpid(mChildPID, NULL, WNOHANG); + while (mRet == 0) + { + // not exited, read some data + mRet = read(mPipeFiledes[0], stdoutCapture + mBytesRead, + stdoutCaptureSize - mBytesRead); + // any error that isn't EAGAIN means we should exit + if (mRet == -1 && errno != EAGAIN) + { + cleanup(); + return mRet; + } + + // if the read was ok, increment bytes read + if (mRet != -1) + mBytesRead += mRet; + + // check again for child exit + mRet = waitpid(mChildPID, NULL, WNOHANG); + } + + // check for error from waitpid + if (mRet == -1 && errno != ECHILD) + { + cleanup(); + return mRet; + } + + // if we get here, the child exited + mChildExited = true; + + // read final bit of data + mRet = read(mPipeFiledes[0], stdoutCapture + mBytesRead, + stdoutCaptureSize - mBytesRead); + if (mRet == -1 && errno != EAGAIN) + { + cleanup(); + return mRet; + } + + if (mRet != -1) + mBytesRead += mRet; + + // done...cleanup + cleanup(); + + return 0; +} + +void UnixCommandExecutor::cleanup() +{ + // if child spawned and not exited, wait + if (mChildPID > 0 && !mChildExited) + waitpid(mChildPID, NULL, 0); + // close pipe descriptors + if (mPipeFiledes[0] != -1) + close(mPipeFiledes[0]); + if (mPipeFiledes[1] != -1) + close(mPipeFiledes[1]); + // if stdout is redirected, restore it + if (mStdoutClosed && mStdoutSave != -1) + dup2(mStdoutSave, 1); + // close stdout save descriptor + if (mStdoutSave != -1) + close(mStdoutSave); + // if stderr is redirected, restore it + if (mStderrClosed && mStderrSave != -1) + dup2(mStderrSave, 2); + // close stderr save descriptor + if (mStderrSave != -1) + close(mStderrSave); + + clearFields(); +} + +/* Usage: + UnixCommandExecutor exec; + char* args[] = { "ps", "-aux", NULL }; + char data[20000]; + int ret = exec.exec(args, data, sizeof(data)); + printf("%s", data); +*/ diff --git a/Engine/source/platformX86UNIX/x86UNIXUtils.h b/Engine/source/platformX86UNIX/x86UNIXUtils.h new file mode 100644 index 000000000..4fe7d5c7f --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXUtils.h @@ -0,0 +1,90 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _X86UNIXUTILS_H_ +#define _X86UNIXUTILS_H_ + +struct utsname; + +class UnixUtils +{ +public: + UnixUtils(); + virtual ~UnixUtils(); + + /** + Returns true if we're running in the background, false otherwise. + There's no "standard" way to determine this in unix, but + modern job control unices should support the method described + here: + + http://www.faqs.org/faqs/unix-faq/faq/part3/ + + (question 3.7) + */ + bool inBackground(); + + /** + Returns the name of the OS, as reported by uname. + */ + const char* getOSName(); + +private: + struct utsname* mUnameInfo; +}; + +extern UnixUtils *UUtils; + +// utility class for running a unix command and capturing its output +class UnixCommandExecutor +{ + private: + int mRet; + int mStdoutSave; + int mStderrSave; + int mPipeFiledes[2]; + int mChildPID; + int mBytesRead; + bool mStdoutClosed; + bool mStderrClosed; + bool mChildExited; + + void clearFields(); + void cleanup(); + + public: + UnixCommandExecutor(); + ~UnixCommandExecutor(); + + // Runs the specified command. + // - args is a null terminated list of the command and its arguments, + // e.g: "ps", "-aux", NULL + // - stdoutCapture is the buffer where stdout data will be stored + // - stdoutCaptureSize is the size of the buffer + // None of these parameters may be null. stdoutCaptureSize must be > 0 + // + // returns -2 if the parameters are bad. returns -1 if some other + // error occurs, check errno for the exact error. + int exec(char* args[], char* stdoutCapture, int stdoutCaptureSize); +}; + +#endif diff --git a/Engine/source/platformX86UNIX/x86UNIXWindow.client.cpp b/Engine/source/platformX86UNIX/x86UNIXWindow.client.cpp new file mode 100644 index 000000000..736a28ad8 --- /dev/null +++ b/Engine/source/platformX86UNIX/x86UNIXWindow.client.cpp @@ -0,0 +1,859 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + + + +#include "console/console.h" +#include "core/fileStream.h" +#include "game/resource.h" +#include "game/version.h" +#include "math/mRandom.h" +#include "platformX86UNIX/platformX86UNIX.h" +#include "platformX86UNIX/x86UNIXStdConsole.h" +#include "platform/event.h" +#include "platform/gameInterface.h" +#include "platform/platform.h" +#include "platform/platformAL.h" +#include "platform/platformInput.h" +#include "platform/platformVideo.h" +#include "platform/profiler.h" +#include "platformX86UNIX/platformGL.h" +#include "platformX86UNIX/x86UNIXOGLVideo.h" +#include "platformX86UNIX/x86UNIXState.h" + +#ifndef DEDICATED +#include "platformX86UNIX/x86UNIXMessageBox.h" +#include "platformX86UNIX/x86UNIXInputManager.h" +#endif + +#include +#include +#include +#include // fork, execvp, chdir +#include // nanosleep + +#ifndef DEDICATED +#include +#include + +#include +#include +#include +#endif + +x86UNIXPlatformState *x86UNIXState; + +bool DisplayPtrManager::sgDisplayLocked = false; +LockFunc_t DisplayPtrManager::sgLockFunc = NULL; +LockFunc_t DisplayPtrManager::sgUnlockFunc = NULL; + +static U32 lastTimeTick; +static MRandomLCG sgPlatRandom; + +#ifndef DEDICATED +extern void InstallRedBookDevices(); +extern void PollRedbookDevices(); +extern bool InitOpenGL(); +// This is called when some X client sends +// a selection event (e.g. SelectionRequest) +// to the window +extern void NotifySelectionEvent(XEvent& event); +#endif + +//------------------------------------------------------------------------------ +static S32 ParseCommandLine(S32 argc, const char **argv, + Vector& newCommandLine) +{ + x86UNIXState->setExePathName(argv[0]); + bool foundDedicated = false; + + for ( int i=0; i < argc; i++ ) + { + // look for platform specific args + if (dStrcmp(argv[i], "-version") == 0) + { + dPrintf("%s (built on %s)\n", getVersionString(), getCompileTimeString()); + dPrintf("gcc: %s\n", __VERSION__); + return 1; + } + if (dStrcmp(argv[i], "-cdaudio") == 0) + { + x86UNIXState->setCDAudioEnabled(true); + continue; + } + if (dStrcmp(argv[i], "-dedicated") == 0) + { + foundDedicated = true; + // no continue because dedicated is also handled by script + } + if (dStrcmp(argv[i], "-dsleep") == 0) + { + x86UNIXState->setDSleep(true); + continue; + } + if (dStrcmp(argv[i], "-nohomedir") == 0) + { + x86UNIXState->setUseRedirect(false); + continue; + } + if (dStrcmp(argv[i], "-chdir") == 0) + { + if ( ++i >= argc ) + { + dPrintf("Follow -chdir option with the desired working directory.\n"); + return 1; + } + if (chdir(argv[i]) == -1) + { + dPrintf("Unable to chdir to %s: %s\n", argv[i], strerror(errno)); + return 1; + } + continue; + } + + // copy the arg into newCommandLine + int argLen = dStrlen(argv[i]) + 1; + char* argBuf = new char[argLen]; // this memory is deleted in main() + dStrncpy(argBuf, argv[i], argLen); + newCommandLine.push_back(argBuf); + } + x86UNIXState->setDedicated(foundDedicated); +#if defined(DEDICATED) && !defined(TORQUE_ENGINE) + if (!foundDedicated) + { + dPrintf("This is a dedicated server build. You must supply the -dedicated command line parameter.\n"); + return 1; + } +#endif + return 0; +} + +static void DetectWindowingSystem() +{ +#ifndef DEDICATED + Display* dpy = XOpenDisplay(NULL); + if (dpy != NULL) + { + x86UNIXState->setXWindowsRunning(true); + XCloseDisplay(dpy); + } +#endif +} + +//------------------------------------------------------------------------------ +static void InitWindow(const Point2I &initialSize, const char *name) +{ + x86UNIXState->setWindowSize(initialSize); + x86UNIXState->setWindowName(name); +} + +#ifndef DEDICATED +//------------------------------------------------------------------------------ +static bool InitSDL() +{ + if (SDL_Init(SDL_INIT_VIDEO) != 0) + return false; + + atexit(SDL_Quit); + + SDL_SysWMinfo sysinfo; + SDL_VERSION(&sysinfo.version); + if (SDL_GetWMInfo(&sysinfo) == 0) + return false; + + x86UNIXState->setDisplayPointer(sysinfo.info.x11.display); + DisplayPtrManager::setDisplayLockFunction(sysinfo.info.x11.lock_func); + DisplayPtrManager::setDisplayUnlockFunction(sysinfo.info.x11.unlock_func); + + DisplayPtrManager xdisplay; + Display* display = xdisplay.getDisplayPointer(); + + x86UNIXState->setScreenNumber( + DefaultScreen( display ) ); + x86UNIXState->setScreenPointer( + DefaultScreenOfDisplay( display ) ); + + x86UNIXState->setDesktopSize( + (S32) DisplayWidth( + display, + x86UNIXState->getScreenNumber()), + (S32) DisplayHeight( + display, + x86UNIXState->getScreenNumber()) + ); + x86UNIXState->setDesktopBpp( + (S32) DefaultDepth( + display, + x86UNIXState->getScreenNumber())); + + // indicate that we want sys WM messages + SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); + + return true; +} + +//------------------------------------------------------------------------------ +static void ProcessSYSWMEvent(const SDL_Event& event) +{ + XEvent& xevent = event.syswm.msg->event.xevent; + //Con::printf("xevent : %d", xevent.type); + switch (xevent.type) + { + case SelectionRequest: + // somebody wants our clipboard + NotifySelectionEvent(xevent); + break; + } +} + +//------------------------------------------------------------------------------ +static void SetAppState() +{ + U8 state = SDL_GetAppState(); + + // if we're not active but we have appactive and inputfocus, set window + // active and reactivate input + if ((!x86UNIXState->windowActive() || !Input::isActive()) && + state & SDL_APPACTIVE && + state & SDL_APPINPUTFOCUS) + { + x86UNIXState->setWindowActive(true); + Input::reactivate(); + } + // if we are active, but we don't have appactive or input focus, + // deactivate input (if window not locked) and clear windowActive + else if (x86UNIXState->windowActive() && + !(state & SDL_APPACTIVE && state & SDL_APPINPUTFOCUS)) + { + if (x86UNIXState->windowLocked()) + Input::deactivate(); + x86UNIXState->setWindowActive(false); + } +} + +//------------------------------------------------------------------------------ +static S32 NumEventsPending() +{ + static const int MaxEvents = 255; + static SDL_Event events[MaxEvents]; + + SDL_PumpEvents(); + return SDL_PeepEvents(events, MaxEvents, SDL_PEEKEVENT, SDL_ALLEVENTS); +} + +//------------------------------------------------------------------------------ +static void PrintSDLEventQueue() +{ + static const int MaxEvents = 255; + static SDL_Event events[MaxEvents]; + + SDL_PumpEvents(); + S32 numEvents = SDL_PeepEvents( + events, MaxEvents, SDL_PEEKEVENT, SDL_ALLEVENTS); + if (numEvents <= 0) + { + dPrintf("SDL Event Queue is empty\n"); + return; + } + + dPrintf("SDL Event Queue:\n"); + for (int i = 0; i < numEvents; ++i) + { + const char *eventType; + switch (events[i].type) + { + case SDL_NOEVENT: eventType = "SDL_NOEVENT"; break; + case SDL_ACTIVEEVENT: eventType = "SDL_ACTIVEEVENT"; break; + case SDL_KEYDOWN: eventType = "SDL_KEYDOWN"; break; + case SDL_KEYUP: eventType = "SDL_KEYUP"; break; + case SDL_MOUSEMOTION: eventType = "SDL_MOUSEMOTION"; break; + case SDL_MOUSEBUTTONDOWN: eventType = "SDL_MOUSEBUTTONDOWN"; break; + case SDL_MOUSEBUTTONUP: eventType = "SDL_MOUSEBUTTONUP"; break; + case SDL_JOYAXISMOTION: eventType = "SDL_JOYAXISMOTION"; break; + case SDL_JOYBALLMOTION: eventType = "SDL_JOYBALLMOTION"; break; + case SDL_JOYHATMOTION: eventType = "SDL_JOYHATMOTION"; break; + case SDL_JOYBUTTONDOWN: eventType = "SDL_JOYBUTTONDOWN"; break; + case SDL_JOYBUTTONUP: eventType = "SDL_JOYBUTTONUP"; break; + case SDL_QUIT: eventType = "SDL_QUIT"; break; + case SDL_SYSWMEVENT: eventType = "SDL_SYSWMEVENT"; break; + case SDL_VIDEORESIZE: eventType = "SDL_VIDEORESIZE"; break; + case SDL_VIDEOEXPOSE: eventType = "SDL_VIDEOEXPOSE"; break; + /* Events SDL_USEREVENT through SDL_MAXEVENTS-1 are for your use */ + case SDL_USEREVENT: eventType = "SDL_USEREVENT"; break; + default: eventType = "UNKNOWN!"; break; + } + dPrintf("Event %d: %s\n", i, eventType); + } +} + +//------------------------------------------------------------------------------ +static bool ProcessMessages() +{ + static const int MaxEvents = 255; + static const U32 Mask = + SDL_QUITMASK | SDL_VIDEORESIZEMASK | SDL_VIDEOEXPOSEMASK | + SDL_ACTIVEEVENTMASK | SDL_SYSWMEVENTMASK | + SDL_EVENTMASK(SDL_USEREVENT); + static SDL_Event events[MaxEvents]; + + SDL_PumpEvents(); + S32 numEvents = SDL_PeepEvents(events, MaxEvents, SDL_GETEVENT, Mask); + if (numEvents == 0) + return true; + for (int i = 0; i < numEvents; ++i) + { + SDL_Event& event = events[i]; + switch (event.type) + { + case SDL_QUIT: + return false; + break; + case SDL_VIDEORESIZE: + case SDL_VIDEOEXPOSE: + Game->refreshWindow(); + break; + case SDL_USEREVENT: + if (event.user.code == TORQUE_SETVIDEOMODE) + { + SetAppState(); + // SDL will send a motion event to restore the mouse position + // on the new window. Ignore that if the window is locked. + if (x86UNIXState->windowLocked()) + { + SDL_Event tempEvent; + SDL_PeepEvents(&tempEvent, 1, SDL_GETEVENT, + SDL_MOUSEMOTIONMASK); + } + } + break; + case SDL_ACTIVEEVENT: + SetAppState(); + break; + case SDL_SYSWMEVENT: + ProcessSYSWMEvent(event); + break; + } + } + return true; +} + +//------------------------------------------------------------------------------ +// send a destroy window event to the window. assumes +// window is created. +void SendQuitEvent() +{ + SDL_Event quitevent; + quitevent.type = SDL_QUIT; + SDL_PushEvent(&quitevent); +} +#endif // DEDICATED + +//------------------------------------------------------------------------------ +static inline void Sleep(int secs, int nanoSecs) +{ + timespec sleeptime; + sleeptime.tv_sec = secs; + sleeptime.tv_nsec = nanoSecs; + nanosleep(&sleeptime, NULL); +} + +#ifndef DEDICATED +struct AlertWinState +{ + bool fullScreen; + bool cursorHidden; + bool inputGrabbed; +}; + +//------------------------------------------------------------------------------ +void DisplayErrorAlert(const char* errMsg, bool showSDLError) +{ + char fullErrMsg[2048]; + dStrncpy(fullErrMsg, errMsg, sizeof(fullErrMsg)); + + if (showSDLError) + { + char* sdlerror = SDL_GetError(); + if (sdlerror != NULL && dStrlen(sdlerror) > 0) + { + dStrcat(fullErrMsg, " (Error: "); + dStrcat(fullErrMsg, sdlerror); + dStrcat(fullErrMsg, ")"); + } + } + + Platform::AlertOK("Error", fullErrMsg); +} + + +//------------------------------------------------------------------------------ +static inline void AlertDisableVideo(AlertWinState& state) +{ + + state.fullScreen = Video::isFullScreen(); + state.cursorHidden = (SDL_ShowCursor(SDL_QUERY) == SDL_DISABLE); + state.inputGrabbed = (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON); + + if (state.fullScreen) + SDL_WM_ToggleFullScreen(SDL_GetVideoSurface()); + if (state.cursorHidden) + SDL_ShowCursor(SDL_ENABLE); + if (state.inputGrabbed) + SDL_WM_GrabInput(SDL_GRAB_OFF); +} + +//------------------------------------------------------------------------------ +static inline void AlertEnableVideo(AlertWinState& state) +{ + if (state.fullScreen) + SDL_WM_ToggleFullScreen(SDL_GetVideoSurface()); + if (state.cursorHidden) + SDL_ShowCursor(SDL_DISABLE); + if (state.inputGrabbed) + SDL_WM_GrabInput(SDL_GRAB_ON); +} +#endif // DEDICATED + +//------------------------------------------------------------------------------ +void Platform::AlertOK(const char *windowTitle, const char *message) +{ +#ifndef DEDICATED + if (x86UNIXState->isXWindowsRunning()) + { + AlertWinState state; + AlertDisableVideo(state); + + DisplayPtrManager xdisplay; + XMessageBox mBox(xdisplay.getDisplayPointer()); + mBox.alertOK(windowTitle, message); + + AlertEnableVideo(state); + } + else +#endif + { + if (Con::isActive() && StdConsole::isEnabled()) + Con::printf("Alert: %s %s", windowTitle, message); + else + dPrintf("Alert: %s %s\n", windowTitle, message); + } +} + +//------------------------------------------------------------------------------ +bool Platform::AlertOKCancel(const char *windowTitle, const char *message) +{ +#ifndef DEDICATED + if (x86UNIXState->isXWindowsRunning()) + { + AlertWinState state; + AlertDisableVideo(state); + + DisplayPtrManager xdisplay; + XMessageBox mBox(xdisplay.getDisplayPointer()); + bool val = + mBox.alertOKCancel(windowTitle, message) == XMessageBox::OK; + + AlertEnableVideo(state); + return val; + } + else +#endif + { + if (Con::isActive() && StdConsole::isEnabled()) + Con::printf("Alert: %s %s", windowTitle, message); + else + dPrintf("Alert: %s %s\n", windowTitle, message); + return false; + } +} + +//------------------------------------------------------------------------------ +bool Platform::AlertRetry(const char *windowTitle, const char *message) +{ +#ifndef DEDICATED + if (x86UNIXState->isXWindowsRunning()) + { + AlertWinState state; + AlertDisableVideo(state); + + DisplayPtrManager xdisplay; + XMessageBox mBox(xdisplay.getDisplayPointer()); + bool val = + mBox.alertRetryCancel(windowTitle, message) == XMessageBox::Retry; + + AlertEnableVideo(state); + return val; + } + else +#endif + { + if (Con::isActive() && StdConsole::isEnabled()) + Con::printf("Alert: %s %s", windowTitle, message); + else + dPrintf("Alert: %s %s\n", windowTitle, message); + return false; + } +} + +//------------------------------------------------------------------------------ +bool Platform::excludeOtherInstances(const char *mutexName) +{ + return AcquireProcessMutex(mutexName); +} + + +//------------------------------------------------------------------------------ +void Platform::enableKeyboardTranslation(void) +{ +#ifndef DEDICATED + // JMQ: not sure if this is needed for i18n keyboards + //SDL_EnableUNICODE( 1 ); +// SDL_EnableKeyRepeat( +// SDL_DEFAULT_REPEAT_DELAY, +// SDL_DEFAULT_REPEAT_INTERVAL); +#endif +} + +//------------------------------------------------------------------------------ +void Platform::disableKeyboardTranslation(void) +{ +#ifndef DEDICATED + //SDL_EnableUNICODE( 0 ); + // SDL_EnableKeyRepeat(0, 0); +#endif +} + +//------------------------------------------------------------------------------ +void Platform::setWindowLocked(bool locked) +{ +#ifndef DEDICATED + x86UNIXState->setWindowLocked(locked); + + UInputManager* uInputManager = + dynamic_cast( Input::getManager() ); + + if ( uInputManager && uInputManager->isEnabled() && + Input::isActive() ) + uInputManager->setWindowLocked(locked); +#endif +} + +//------------------------------------------------------------------------------ +void Platform::minimizeWindow() +{ +#ifndef DEDICATED + if (x86UNIXState->windowCreated()) + SDL_WM_IconifyWindow(); +#endif +} + +//------------------------------------------------------------------------------ +void Platform::process() +{ + PROFILE_START(XUX_PlatformProcess); + stdConsole->process(); + + if (x86UNIXState->windowCreated()) + { +#ifndef DEDICATED + // process window events + PROFILE_START(XUX_ProcessMessages); + bool quit = !ProcessMessages(); + PROFILE_END(); + if(quit) + { + // generate a quit event + Event quitEvent; + quitEvent.type = QuitEventType; + Game->postEvent(quitEvent); + } + + // process input events + PROFILE_START(XUX_InputProcess); + Input::process(); + PROFILE_END(); + + // poll redbook state + PROFILE_START(XUX_PollRedbookDevices); + PollRedbookDevices(); + PROFILE_END(); + + // if we're not the foreground window, sleep for 1 ms + if (!x86UNIXState->windowActive()) + Sleep(0, getBackgroundSleepTime() * 1000000); +#endif + } + else + { + // no window + // if we're not in journal mode, sleep for 1 ms + // JMQ: since linux's minimum sleep latency seems to be 20ms, this can + // increase player pings by 10-20ms in the dedicated server. So + // you have to use -dsleep to enable it. the server sleeps anyway when + // there are no players connected. + // JMQ: recent kernels (such as RH 8.0 2.4.18) reduce the latency + // to 2-4 ms on average. + if (!Game->isJournalReading() && (x86UNIXState->getDSleep() || + Con::getIntVariable("Server::PlayerCount") - + Con::getIntVariable("Server::BotCount") <= 0)) + { + PROFILE_START(XUX_Sleep); + Sleep(0, getBackgroundSleepTime() * 1000000); + PROFILE_END(); + } + } + +#ifndef DEDICATED +#if 0 +// JMQ: disabled this because it may fire mistakenly in some configurations. +// sdl's default event handling scheme should be enough. + // crude check to make sure that we're not loading up events. the sdl + // event queue should never have more than (say) 25 events in it at this + // point + const int MaxEvents = 25; + if (NumEventsPending() > MaxEvents) + { + PrintSDLEventQueue(); + AssertFatal(false, "The SDL event queue has too many events!"); + } +#endif +#endif + PROFILE_END(); +} + +// extern U32 calculateCRC(void * buffer, S32 len, U32 crcVal ); + +// #if defined(DEBUG) || defined(INTERNAL_RELEASE) +// static U32 stubCRC = 0; +// #else +// static U32 stubCRC = 0xEA63F56C; +// #endif + +//------------------------------------------------------------------------------ +const Point2I &Platform::getWindowSize() +{ + return x86UNIXState->getWindowSize(); +} + + +//------------------------------------------------------------------------------ +void Platform::setWindowSize( U32 newWidth, U32 newHeight ) +{ + x86UNIXState->setWindowSize( (S32) newWidth, (S32) newHeight ); +} + + +//------------------------------------------------------------------------------ +void Platform::initWindow(const Point2I &initialSize, const char *name) +{ +#ifndef DEDICATED + // initialize window + InitWindow(initialSize, name); + if (!InitOpenGL()) + ImmediateShutdown(1); +#endif +} + + +//------------------------------------------------------------------------------ +// Web browser function: +//------------------------------------------------------------------------------ +bool Platform::openWebBrowser( const char* webAddress ) +{ + if (!webAddress || dStrlen(webAddress)==0) + return false; + + // look for a browser preference variable + // JMQTODO: be nice to implement some UI to customize this + const char* webBrowser = Con::getVariable("Pref::Unix::WebBrowser"); + if (dStrlen(webBrowser) == 0) + webBrowser = NULL; + + pid_t pid = fork(); + if (pid == -1) + { + Con::printf("WARNING: Platform::openWebBrowser failed to fork"); + return false; + } + else if (pid != 0) + { + // parent + if (Video::isFullScreen()) + Video::toggleFullScreen(); + + return true; + } + else if (pid == 0) + { + // child + // try to exec konqueror, then netscape + char* argv[3]; + argv[0] = ""; + argv[1] = const_cast(webAddress); + argv[2] = 0; + + int ok = -1; + + // if execvp returns, it means it couldn't execute the program + if (webBrowser != NULL) + ok = execvp(webBrowser, argv); + + ok = execvp("konqueror", argv); + ok = execvp("mozilla", argv); + ok = execvp("netscape", argv); + // use dPrintf instead of Con here since we're now in another process, + dPrintf("WARNING: Platform::openWebBrowser: couldn't launch a web browser\n"); + _exit(-1); + return false; + } + else + { + Con::printf("WARNING: Platform::openWebBrowser: forking problem"); + return false; + } +} + +//------------------------------------------------------------------------------ +// Login password routines: +//------------------------------------------------------------------------------ +const char* Platform::getLoginPassword() +{ + Con::printf("WARNING: Platform::getLoginPassword() is unimplemented"); + return ""; +} + +//------------------------------------------------------------------------------ +bool Platform::setLoginPassword( const char* password ) +{ + Con::printf("WARNING: Platform::setLoginPassword is unimplemented"); + return false; +} + +//------------------------------------------------------------------------------- +void TimeManager::process() +{ + U32 curTime = Platform::getRealMilliseconds(); + TimeEvent event; + event.elapsedTime = curTime - lastTimeTick; + if(event.elapsedTime > sgTimeManagerProcessInterval) + { + lastTimeTick = curTime; + Game->postEvent(event); + } +} + +//------------------------------------------------------------------------------ +ConsoleFunction( getDesktopResolution, const char*, 1, 1, + "getDesktopResolution()" ) +{ + if (!x86UNIXState->windowCreated()) + return "0 0 0"; + + char buffer[256]; + char* returnString = Con::getReturnBuffer( dStrlen( buffer ) + 1 ); + + dSprintf( buffer, sizeof( buffer ), "%d %d %d", + x86UNIXState->getDesktopSize().x, + x86UNIXState->getDesktopSize().y, + x86UNIXState->getDesktopBpp() ); + dStrcpy( returnString, buffer ); + return( returnString ); +} + +//------------------------------------------------------------------------------ +// Silly Korean registry key checker: +//------------------------------------------------------------------------------ +ConsoleFunction( isKoreanBuild, bool, 1, 1, "isKoreanBuild()" ) +{ + Con::printf("WARNING: isKoreanBuild() is unimplemented"); + return false; +} + +//------------------------------------------------------------------------------ +int main(S32 argc, const char **argv) +{ + // init platform state + x86UNIXState = new x86UNIXPlatformState; + + // parse the command line for unix-specific params + Vector newCommandLine; + S32 returnVal = ParseCommandLine(argc, argv, newCommandLine); + if (returnVal != 0) + return returnVal; + + // init lastTimeTick for TimeManager::process() + lastTimeTick = Platform::getRealMilliseconds(); + + // init process control stuff + ProcessControlInit(); + + // check to see if X is running + DetectWindowingSystem(); + + // run the game + returnVal = Game->main(newCommandLine.size(), + const_cast(newCommandLine.address())); + + // dispose of command line + for(U32 i = 0; i < newCommandLine.size(); i++) + delete [] newCommandLine[i]; + + // dispose of state + delete x86UNIXState; + + return returnVal; +} + +void Platform::setWindowTitle( const char* title ) +{ +#ifndef DEDICATED + x86UNIXState->setWindowName(title); + SDL_WM_SetCaption(x86UNIXState->getWindowName(), NULL); +#endif +} + +Resolution Video::getDesktopResolution() +{ + Resolution Result; + Result.h = x86UNIXState->getDesktopSize().x; + Result.w = x86UNIXState->getDesktopSize().y; + Result.bpp = x86UNIXState->getDesktopBpp(); + + return Result; +} + + +//----------------------------------------------------------------------------- +void Platform::restartInstance() +{ + + if (Game->isRunning() ) + { + //Con::errorf( "Error restarting Instance. Game is Still running!"); + return; + } + + char cmd[2048]; + sprintf(cmd, "%s &", x86UNIXState->getExePathName()); + system(cmd); + exit(0); +} diff --git a/Engine/source/postFx/postEffect.cpp b/Engine/source/postFx/postEffect.cpp new file mode 100644 index 000000000..dce0c9447 --- /dev/null +++ b/Engine/source/postFx/postEffect.cpp @@ -0,0 +1,1541 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "postFx/postEffect.h" + +#include "console/engineAPI.h" +#include "core/stream/fileStream.h" +#include "core/strings/stringUnit.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "math/util/frustum.h" +#include "math/mathUtils.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/gfxStringEnumTranslate.h" +#include "gfx/gfxTextureManager.h" +#include "gfx/gfxDebugEvent.h" +#include "gfx/util/screenspace.h" +#include "gfx/sim/gfxStateBlockData.h" +#include "scene/sceneRenderState.h" +#include "shaderGen/shaderGenVars.h" +#include "lighting/lightInfo.h" +#include "lighting/lightManager.h" +#include "materials/materialManager.h" +#include "materials/shaderData.h" +#include "postFx/postEffectManager.h" +#include "postFx/postEffectVis.h" + + +ConsoleDocClass( PostEffect, + "@brief A fullscreen shader effect.\n\n" + + "@section PFXTextureIdentifiers\n\n" + + "@ingroup Rendering\n" +); + +IMPLEMENT_CALLBACK( PostEffect, onAdd, void, (), (), + "Called when this object is first created and registered." +); + +IMPLEMENT_CALLBACK( PostEffect, preProcess, void, (), (), + "Called when an effect is processed but before textures are bound. This " + "allows the user to change texture related paramaters or macros at runtime.\n" + "@tsexample\n" + "function SSAOPostFx::preProcess( %this )\n" + "{\n" + " if ( $SSAOPostFx::quality !$= %this.quality )\n" + " {\n" + " %this.quality = mClamp( mRound( $SSAOPostFx::quality ), 0, 2 );\n" + " \n" + " %this.setShaderMacro( \"QUALITY\", %this.quality );\n" + " }\n" + " %this.targetScale = $SSAOPostFx::targetScale;\n" + "}\n" + "@endtsexample\n" + "@see setShaderConst\n" + "@see setShaderMacro" +); + +IMPLEMENT_CALLBACK( PostEffect, setShaderConsts, void, (), (), + "Called immediate before processing this effect. This is the user's chance " + "to set the value of shader uniforms (constants).\n" + "@see setShaderConst" +); + +IMPLEMENT_CALLBACK( PostEffect, onEnabled, bool, (), (), + "Called when this effect becomes enabled. If the user returns false from " + "this callback the effect will not be enabled.\n" + "@return True to allow this effect to be enabled." +); + +IMPLEMENT_CALLBACK( PostEffect, onDisabled, void, (), (), + "Called when this effect becomes disabled." +); + +ImplementEnumType( PFXRenderTime, + "When to process this effect during the frame.\n" + "@ingroup Rendering\n\n") + { PFXBeforeBin, "PFXBeforeBin", "Before a RenderInstManager bin.\n" }, + { PFXAfterBin, "PFXAfterBin", "After a RenderInstManager bin.\n" }, + { PFXAfterDiffuse, "PFXAfterDiffuse", "After the diffuse rendering pass.\n" }, + { PFXEndOfFrame, "PFXEndOfFrame", "When the end of the frame is reached.\n" }, + { PFXTexGenOnDemand, "PFXTexGenOnDemand", "This PostEffect is not processed by the manager. It will generate its texture when it is requested.\n" } +EndImplementEnumType; + +ImplementEnumType( PFXTargetClear, + "Describes when the target texture should be cleared\n" + "@ingroup Rendering\n\n") + { PFXTargetClear_None, "PFXTargetClear_None", "Never clear the PostEffect target.\n" }, + { PFXTargetClear_OnCreate, "PFXTargetClear_OnCreate", "Clear once on create.\n" }, + { PFXTargetClear_OnDraw, "PFXTargetClear_OnDraw", "Clear before every draw.\n" }, +EndImplementEnumType; + + +GFXImplementVertexFormat( PFXVertex ) +{ + addElement( "POSITION", GFXDeclType_Float3 ); + addElement( "TEXCOORD", GFXDeclType_Float2, 0 ); + addElement( "TEXCOORD", GFXDeclType_Float3, 1 ); +}; + +GFX_ImplementTextureProfile( PostFxTargetProfile, + GFXTextureProfile::DiffuseMap, + GFXTextureProfile::PreserveSize | + GFXTextureProfile::RenderTarget | + GFXTextureProfile::Pooled, + GFXTextureProfile::None ); + +IMPLEMENT_CONOBJECT(PostEffect); + + +GFX_ImplementTextureProfile( PostFxTextureProfile, + GFXTextureProfile::DiffuseMap, + GFXTextureProfile::Static | GFXTextureProfile::PreserveSize | GFXTextureProfile::NoMipmap, + GFXTextureProfile::None ); + + +void PostEffect::EffectConst::set( const String &newVal ) +{ + if ( mStringVal == newVal ) + return; + + mStringVal = newVal; + mDirty = true; +} + +void PostEffect::EffectConst::setToBuffer( GFXShaderConstBufferRef buff ) +{ + // Nothing to do if the value hasn't changed. + if ( !mDirty ) + return; + mDirty = false; + + // If we don't have a handle... get it now. + if ( !mHandle ) + mHandle = buff->getShader()->getShaderConstHandle( mName ); + + // If the handle isn't valid then we're done. + if ( !mHandle->isValid() ) + return; + + const GFXShaderConstType type = mHandle->getType(); + + // For now, we're only going + // to support float4 arrays. + // Expand to other types as necessary. + U32 arraySize = mHandle->getArraySize(); + + const char *strVal = mStringVal.c_str(); + + if ( type == GFXSCT_Float ) + { + F32 val; + Con::setData( TypeF32, &val, 0, 1, &strVal ); + buff->set( mHandle, val ); + } + else if ( type == GFXSCT_Float2 ) + { + Point2F val; + Con::setData( TypePoint2F, &val, 0, 1, &strVal ); + buff->set( mHandle, val ); + } + else if ( type == GFXSCT_Float3 ) + { + Point3F val; + Con::setData( TypePoint3F, &val, 0, 1, &strVal ); + buff->set( mHandle, val ); + } + else + { + Point4F val; + + if ( arraySize > 1 ) + { + // Do array setup! + //U32 unitCount = StringUnit::getUnitCount( strVal, "\t" ); + //AssertFatal( unitCount == arraySize, "" ); + + String tmpString; + Vector valArray; + + for ( U32 i = 0; i < arraySize; i++ ) + { + tmpString = StringUnit::getUnit( strVal, i, "\t" ); + valArray.increment(); + const char *tmpCStr = tmpString.c_str(); + + Con::setData( TypePoint4F, &valArray.last(), 0, 1, &tmpCStr ); + } + + AlignedArray rectData( valArray.size(), sizeof( Point4F ), (U8*)valArray.address(), false ); + buff->set( mHandle, rectData ); + } + else + { + // Do regular setup. + Con::setData( TypePoint4F, &val, 0, 1, &strVal ); + buff->set( mHandle, val ); + } + } +} + + +//------------------------------------------------------------------------- +// PostEffect +//------------------------------------------------------------------------- + +PostEffect::PostEffect() + : mRenderTime( PFXAfterDiffuse ), + mRenderPriority( 1.0 ), + mEnabled( false ), + mSkip( false ), + mUpdateShader( true ), + mStateBlockData( NULL ), + mAllowReflectPass( false ), + mTargetClear( PFXTargetClear_None ), + mTargetScale( Point2F::One ), + mTargetSize( Point2I::Zero ), + mTargetFormat( GFXFormatR8G8B8A8 ), + mTargetClearColor( ColorF::BLACK ), + mOneFrameOnly( false ), + mOnThisFrame( true ), + mShaderReloadKey( 0 ), + mIsValid( false ), + mRTSizeSC( NULL ), + mOneOverRTSizeSC( NULL ), + mViewportOffsetSC( NULL ), + mFogDataSC( NULL ), + mFogColorSC( NULL ), + mEyePosSC( NULL ), + mMatWorldToScreenSC( NULL ), + mMatScreenToWorldSC( NULL ), + mMatPrevScreenToWorldSC( NULL ), + mNearFarSC( NULL ), + mInvNearFarSC( NULL ), + mWorldToScreenScaleSC( NULL ), + mWaterColorSC( NULL ), + mWaterFogDataSC( NULL ), + mAmbientColorSC( NULL ), + mWaterFogPlaneSC( NULL ), + mWaterDepthGradMaxSC( NULL ), + mScreenSunPosSC( NULL ), + mLightDirectionSC( NULL ), + mCameraForwardSC( NULL ), + mAccumTimeSC( NULL ), + mDeltaTimeSC( NULL ), + mInvCameraMatSC( NULL ) +{ + dMemset( mActiveTextures, 0, sizeof( GFXTextureObject* ) * NumTextures ); + dMemset( mActiveNamedTarget, 0, sizeof( NamedTexTarget* ) * NumTextures ); + dMemset( mActiveTextureViewport, 0, sizeof( RectI ) * NumTextures ); + dMemset( mTexSizeSC, 0, sizeof( GFXShaderConstHandle* ) * NumTextures ); + dMemset( mRenderTargetParamsSC, 0, sizeof( GFXShaderConstHandle* ) * NumTextures ); +} + +PostEffect::~PostEffect() +{ + EffectConstTable::Iterator iter = mEffectConsts.begin(); + for ( ; iter != mEffectConsts.end(); iter++ ) + delete iter->value; +} + +void PostEffect::initPersistFields() +{ + addField( "shader", TypeRealString, Offset( mShaderName, PostEffect ), + "Name of a GFXShaderData for this effect." ); + + addField( "stateBlock", TYPEID(), Offset( mStateBlockData, PostEffect ), + "Name of a GFXStateBlockData for this effect." ); + + addField( "target", TypeRealString, Offset( mTargetName, PostEffect ), + "String identifier of this effect's target texture.\n" + "@see PFXTextureIdentifiers" ); + + addField( "targetDepthStencil", TypeRealString, Offset( mTargetDepthStencilName, PostEffect ), + "Optional string identifier for this effect's target depth/stencil texture.\n" + "@see PFXTextureIdentifiers" ); + + addField( "targetScale", TypePoint2F, Offset( mTargetScale, PostEffect ), + "If targetSize is zero this is used to set a relative size from the current target." ); + + addField( "targetSize", TypePoint2I, Offset( mTargetSize, PostEffect ), + "If non-zero this is used as the absolute target size." ); + + addField( "targetFormat", TypeGFXFormat, Offset( mTargetFormat, PostEffect ), + "Format of the target texture, not applicable if writing to the backbuffer." ); + + addField( "targetClearColor", TypeColorF, Offset( mTargetClearColor, PostEffect ), + "Color to which the target texture is cleared before rendering." ); + + addField( "targetClear", TYPEID< PFXTargetClear >(), Offset( mTargetClear, PostEffect ), + "Describes when the target texture should be cleared." ); + + addField( "texture", TypeImageFilename, Offset( mTexFilename, PostEffect ), NumTextures, + "Input textures to this effect ( samplers ).\n" + "@see PFXTextureIdentifiers" ); + + addField( "renderTime", TYPEID< PFXRenderTime >(), Offset( mRenderTime, PostEffect ), + "When to process this effect during the frame." ); + + addField( "renderBin", TypeRealString, Offset( mRenderBin, PostEffect ), + "Name of a renderBin, used if renderTime is PFXBeforeBin or PFXAfterBin." ); + + addField( "renderPriority", TypeF32, Offset( mRenderPriority, PostEffect ), + "PostEffects are processed in DESCENDING order of renderPriority if more than one has the same renderBin/Time." ); + + addField( "allowReflectPass", TypeBool, Offset( mAllowReflectPass, PostEffect ), + "Is this effect processed during reflection render passes." ); + + addProtectedField( "isEnabled", TypeBool, Offset( mEnabled, PostEffect), + &PostEffect::_setIsEnabled, &defaultProtectedGetFn, + "Is the effect on." ); + + addField( "onThisFrame", TypeBool, Offset( mOnThisFrame, PostEffect ), + "Allows you to turn on a PostEffect for only a single frame." ); + + addField( "oneFrameOnly", TypeBool, Offset( mOneFrameOnly, PostEffect ), + "Allows you to turn on a PostEffect for only a single frame." ); + + addField( "skip", TypeBool, Offset( mSkip, PostEffect ), + "Skip processing of this PostEffect and its children even if its parent " + "is enabled. Parent and sibling PostEffects in the chain are still processed." ); + + Parent::initPersistFields(); +} + +bool PostEffect::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + LightManager::smActivateSignal.notify( this, &PostEffect::_onLMActivate ); + mUpdateShader = true; + + // Grab the script path. + Torque::Path scriptPath( Con::getVariable( "$Con::File" ) ); + scriptPath.setFileName( String::EmptyString ); + scriptPath.setExtension( String::EmptyString ); + + // Find additional textures + for( int i = 0; i < NumTextures; i++ ) + { + String texFilename = mTexFilename[i]; + + // Skip empty stages or ones with variable or target names. + if ( texFilename.isEmpty() || + texFilename[0] == '$' || + texFilename[0] == '#' ) + continue; + + // If '/', then path is specified, open normally + if ( texFilename[0] != '/' ) + texFilename = scriptPath.getFullPath() + '/' + texFilename; + + // Try to load the texture. + mTextures[i].set( texFilename, &PostFxTextureProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ) ); + } + + // Is the target a named target? + if ( mTargetName.isNotEmpty() && mTargetName[0] == '#' ) + { + mNamedTarget.registerWithName( mTargetName.substr( 1 ) ); + mNamedTarget.getTextureDelegate().bind( this, &PostEffect::_getTargetTexture ); + } + if ( mTargetDepthStencilName.isNotEmpty() && mTargetDepthStencilName[0] == '#' ) + mNamedTargetDepthStencil.registerWithName( mTargetDepthStencilName.substr( 1 ) ); + + if (mNamedTarget.isRegistered() || mNamedTargetDepthStencil.isRegistered()) + GFXTextureManager::addEventDelegate( this, &PostEffect::_onTextureEvent ); + + // Call onAdd in script + onAdd_callback(); + + // Should we start enabled? + if ( mEnabled ) + { + mEnabled = false; + enable(); + } + + getSet()->addObject( this ); + + return true; +} + +void PostEffect::onRemove() +{ + Parent::onRemove(); + + PFXMGR->_removeEffect( this ); + + LightManager::smActivateSignal.remove( this, &PostEffect::_onLMActivate ); + + mShader = NULL; + _cleanTargets(); + + if ( mNamedTarget.isRegistered() || mNamedTargetDepthStencil.isRegistered() ) + GFXTextureManager::removeEventDelegate( this, &PostEffect::_onTextureEvent ); + + if ( mNamedTarget.isRegistered() ) + { + mNamedTarget.unregister(); + mNamedTarget.getTextureDelegate().clear(); + } + + if ( mNamedTargetDepthStencil.isRegistered() ) + mNamedTargetDepthStencil.unregister(); +} + +void PostEffect::_updateScreenGeometry( const Frustum &frustum, + GFXVertexBufferHandle *outVB ) +{ + outVB->set( GFX, 4, GFXBufferTypeVolatile ); + + const Point3F *frustumPoints = frustum.getPoints(); + const Point3F& cameraPos = frustum.getPosition(); + + PFXVertex *vert = outVB->lock(); + + vert->point.set( -1.0, -1.0, 0.0 ); + vert->texCoord.set( 0.0f, 1.0f ); + vert->wsEyeRay = frustumPoints[Frustum::FarBottomLeft] - cameraPos; + vert++; + + vert->point.set( -1.0, 1.0, 0.0 ); + vert->texCoord.set( 0.0f, 0.0f ); + vert->wsEyeRay = frustumPoints[Frustum::FarTopLeft] - cameraPos; + vert++; + + vert->point.set( 1.0, 1.0, 0.0 ); + vert->texCoord.set( 1.0f, 0.0f ); + vert->wsEyeRay = frustumPoints[Frustum::FarTopRight] - cameraPos; + vert++; + + vert->point.set( 1.0, -1.0, 0.0 ); + vert->texCoord.set( 1.0f, 1.0f ); + vert->wsEyeRay = frustumPoints[Frustum::FarBottomRight] - cameraPos; + vert++; + + outVB->unlock(); +} + +void PostEffect::_setupStateBlock( const SceneRenderState *state ) +{ + if ( mStateBlock.isNull() ) + { + GFXStateBlockDesc desc; + if ( mStateBlockData ) + desc = mStateBlockData->getState(); + + mStateBlock = GFX->createStateBlock( desc ); + } + + GFX->setStateBlock( mStateBlock ); +} + +void PostEffect::_setupConstants( const SceneRenderState *state ) +{ + // Alloc the const buffer. + if ( mShaderConsts.isNull() ) + { + mShaderConsts = mShader->allocConstBuffer(); + + mRTSizeSC = mShader->getShaderConstHandle( "$targetSize" ); + mOneOverRTSizeSC = mShader->getShaderConstHandle( "$oneOverTargetSize" ); + + mTexSizeSC[0] = mShader->getShaderConstHandle( "$texSize0" ); + mTexSizeSC[1] = mShader->getShaderConstHandle( "$texSize1" ); + mTexSizeSC[2] = mShader->getShaderConstHandle( "$texSize2" ); + mTexSizeSC[3] = mShader->getShaderConstHandle( "$texSize3" ); + mTexSizeSC[4] = mShader->getShaderConstHandle( "$texSize4" ); + mTexSizeSC[5] = mShader->getShaderConstHandle( "$texSize5" ); + + mRenderTargetParamsSC[0] = mShader->getShaderConstHandle( "$rtParams0" ); + mRenderTargetParamsSC[1] = mShader->getShaderConstHandle( "$rtParams1" ); + mRenderTargetParamsSC[2] = mShader->getShaderConstHandle( "$rtParams2" ); + mRenderTargetParamsSC[3] = mShader->getShaderConstHandle( "$rtParams3" ); + mRenderTargetParamsSC[4] = mShader->getShaderConstHandle( "$rtParams4" ); + mRenderTargetParamsSC[5] = mShader->getShaderConstHandle( "$rtParams5" ); + + //mViewportSC = shader->getShaderConstHandle( "$viewport" ); + + mFogDataSC = mShader->getShaderConstHandle( ShaderGenVars::fogData ); + mFogColorSC = mShader->getShaderConstHandle( ShaderGenVars::fogColor ); + + mEyePosSC = mShader->getShaderConstHandle( ShaderGenVars::eyePosWorld ); + + mNearFarSC = mShader->getShaderConstHandle( "$nearFar" ); + mInvNearFarSC = mShader->getShaderConstHandle( "$invNearFar" ); + mWorldToScreenScaleSC = mShader->getShaderConstHandle( "$worldToScreenScale" ); + + mMatWorldToScreenSC = mShader->getShaderConstHandle( "$matWorldToScreen" ); + mMatScreenToWorldSC = mShader->getShaderConstHandle( "$matScreenToWorld" ); + mMatPrevScreenToWorldSC = mShader->getShaderConstHandle( "$matPrevScreenToWorld" ); + + mWaterColorSC = mShader->getShaderConstHandle( "$waterColor" ); + mAmbientColorSC = mShader->getShaderConstHandle( "$ambientColor" ); + mWaterFogDataSC = mShader->getShaderConstHandle( "$waterFogData" ); + mWaterFogPlaneSC = mShader->getShaderConstHandle( "$waterFogPlane" ); + mWaterDepthGradMaxSC = mShader->getShaderConstHandle( "$waterDepthGradMax" ); + mScreenSunPosSC = mShader->getShaderConstHandle( "$screenSunPos" ); + mLightDirectionSC = mShader->getShaderConstHandle( "$lightDirection" ); + mCameraForwardSC = mShader->getShaderConstHandle( "$camForward" ); + + mAccumTimeSC = mShader->getShaderConstHandle( "$accumTime" ); + mDeltaTimeSC = mShader->getShaderConstHandle( "$deltaTime" ); + + mInvCameraMatSC = mShader->getShaderConstHandle( "$invCameraMat" ); + } + + // Set up shader constants for source image size + if ( mRTSizeSC->isValid() ) + { + const Point2I &resolution = GFX->getActiveRenderTarget()->getSize(); + Point2F pixelShaderConstantData; + + pixelShaderConstantData.x = resolution.x; + pixelShaderConstantData.y = resolution.y; + + mShaderConsts->set( mRTSizeSC, pixelShaderConstantData ); + } + + if ( mOneOverRTSizeSC->isValid() ) + { + const Point2I &resolution = GFX->getActiveRenderTarget()->getSize(); + Point2F oneOverTargetSize( 1.0f / (F32)resolution.x, 1.0f / (F32)resolution.y ); + + mShaderConsts->set( mOneOverRTSizeSC, oneOverTargetSize ); + } + + // Set up additional textures + Point2F texSizeConst; + for( U32 i = 0; i < NumTextures; i++ ) + { + if( !mActiveTextures[i] ) + continue; + + if ( mTexSizeSC[i]->isValid() ) + { + texSizeConst.x = (F32)mActiveTextures[i]->getWidth(); + texSizeConst.y = (F32)mActiveTextures[i]->getHeight(); + mShaderConsts->set( mTexSizeSC[i], texSizeConst ); + } + } + + for ( U32 i = 0; i < NumTextures; i++ ) + { + if ( !mRenderTargetParamsSC[i]->isValid() ) + continue; + + Point4F rtParams( Point4F::One ); + + if ( mActiveTextures[i] ) + { + const Point3I &targetSz = mActiveTextures[i]->getSize(); + RectI targetVp = mActiveTextureViewport[i]; + ScreenSpace::RenderTargetParameters(targetSz, targetVp, rtParams); + } + + mShaderConsts->set( mRenderTargetParamsSC[i], rtParams ); + } + + // Set the fog data. + if ( mFogDataSC->isValid() ) + { + const FogData &data = state->getSceneManager()->getFogData(); + + Point3F params; + params.x = data.density; + params.y = data.densityOffset; + + if ( !mIsZero( data.atmosphereHeight ) ) + params.z = 1.0f / data.atmosphereHeight; + else + params.z = 0.0f; + + mShaderConsts->set( mFogDataSC, params ); + } + + const PFXFrameState &thisFrame = PFXMGR->getFrameState(); + + if ( mMatWorldToScreenSC->isValid() ) + { + // Screen space->world space + MatrixF tempMat = thisFrame.cameraToScreen; + tempMat.mul( thisFrame.worldToCamera ); + tempMat.fullInverse(); + tempMat.transpose(); + + // Support using these matrices as float3x3 or float4x4... + mShaderConsts->set( mMatWorldToScreenSC, tempMat, mMatWorldToScreenSC->getType() ); + } + + if ( mMatScreenToWorldSC->isValid() ) + { + // World space->screen space + MatrixF tempMat = thisFrame.cameraToScreen; + tempMat.mul( thisFrame.worldToCamera ); + tempMat.transpose(); + + // Support using these matrices as float3x3 or float4x4... + mShaderConsts->set( mMatScreenToWorldSC, tempMat, mMatScreenToWorldSC->getType() ); + } + + if ( mMatPrevScreenToWorldSC->isValid() ) + { + const PFXFrameState &lastFrame = PFXMGR->getLastFrameState(); + + // Previous frame world space->screen space + MatrixF tempMat = lastFrame.cameraToScreen; + tempMat.mul( lastFrame.worldToCamera ); + tempMat.transpose(); + mShaderConsts->set( mMatPrevScreenToWorldSC, tempMat ); + } + + if ( mAmbientColorSC->isValid() ) + { + const ColorF &sunlight = state->getAmbientLightColor(); + Point3F ambientColor( sunlight.red, sunlight.green, sunlight.blue ); + + mShaderConsts->set( mAmbientColorSC, ambientColor ); + } + + mShaderConsts->setSafe( mAccumTimeSC, MATMGR->getTotalTime() ); + mShaderConsts->setSafe( mDeltaTimeSC, MATMGR->getDeltaTime() ); + + // Now set all the constants that are dependent on the scene state. + if ( state ) + { + mShaderConsts->setSafe( mEyePosSC, state->getDiffuseCameraPosition() ); + mShaderConsts->setSafe( mNearFarSC, Point2F( state->getNearPlane(), state->getFarPlane() ) ); + mShaderConsts->setSafe( mInvNearFarSC, Point2F( 1.0f / state->getNearPlane(), 1.0f / state->getFarPlane() ) ); + mShaderConsts->setSafe( mWorldToScreenScaleSC, state->getWorldToScreenScale() ); + mShaderConsts->setSafe( mFogColorSC, state->getSceneManager()->getFogData().color ); + + if ( mWaterColorSC->isValid() ) + { + ColorF color( state->getSceneManager()->getWaterFogData().color ); + mShaderConsts->set( mWaterColorSC, color ); + } + + if ( mWaterFogDataSC->isValid() ) + { + const WaterFogData &data = state->getSceneManager()->getWaterFogData(); + Point4F params( data.density, data.densityOffset, data.wetDepth, data.wetDarkening ); + mShaderConsts->set( mWaterFogDataSC, params ); + } + + if ( mWaterFogPlaneSC->isValid() ) + { + const PlaneF &plane = state->getSceneManager()->getWaterFogData().plane; + mShaderConsts->set( mWaterFogPlaneSC, plane ); + } + + if ( mWaterDepthGradMaxSC->isValid() ) + { + mShaderConsts->set( mWaterDepthGradMaxSC, state->getSceneManager()->getWaterFogData().depthGradMax ); + } + + if ( mScreenSunPosSC->isValid() ) + { + // Grab our projection matrix + // from the frustum. + Frustum frust = state->getFrustum(); + MatrixF proj( true ); + frust.getProjectionMatrix( &proj ); + + // Grab the ScatterSky world matrix. + MatrixF camMat = state->getCameraTransform(); + camMat.inverse(); + MatrixF tmp( true ); + tmp = camMat; + tmp.setPosition( Point3F( 0, 0, 0 ) ); + + Point3F sunPos( 0, 0, 0 ); + + // Get the light manager and sun light object. + LightInfo *sunLight = LIGHTMGR->getSpecialLight( LightManager::slSunLightType ); + + // Grab the light direction and scale + // by the ScatterSky radius to get the world + // space sun position. + const VectorF &lightDir = sunLight->getDirection(); + + Point3F lightPos( lightDir.x * (6378.0f * 1000.0f), + lightDir.y * (6378.0f * 1000.0f), + lightDir.z * (6378.0f * 1000.0f) ); + + // Get the screen space sun position. + MathUtils::mProjectWorldToScreen( lightPos, &sunPos, GFX->getViewport(), tmp, proj ); + + // And normalize it to the 0 to 1 range. + sunPos.x /= (F32)GFX->getViewport().extent.x; + sunPos.y /= (F32)GFX->getViewport().extent.y; + + mShaderConsts->set( mScreenSunPosSC, Point2F( sunPos.x, sunPos.y ) ); + } + + if ( mLightDirectionSC->isValid() ) + { + LightInfo *sunLight = LIGHTMGR->getSpecialLight( LightManager::slSunLightType ); + + const VectorF &lightDir = sunLight->getDirection(); + mShaderConsts->set( mLightDirectionSC, lightDir ); + } + + if ( mCameraForwardSC->isValid() ) + { + const MatrixF &camMat = state->getCameraTransform(); + VectorF camFwd( 0, 0, 0 ); + + camMat.getColumn( 1, &camFwd ); + + mShaderConsts->set( mCameraForwardSC, camFwd ); + } + + if ( mInvCameraMatSC->isValid() ) + { + MatrixF mat = state->getCameraTransform(); + mat.inverse(); + mShaderConsts->set( mInvCameraMatSC, mat, mInvCameraMatSC->getType() ); + } + + } // if ( state ) + + // Set EffectConsts - specified from script + + // If our shader has reloaded since last frame we must mark all + // EffectConsts dirty so they will be reset. + if ( mShader->getReloadKey() != mShaderReloadKey ) + { + mShaderReloadKey = mShader->getReloadKey(); + + EffectConstTable::Iterator iter = mEffectConsts.begin(); + for ( ; iter != mEffectConsts.end(); iter++ ) + iter->value->mDirty = true; + } + + // Doesn't look like anyone is using this anymore. + // But if we do want to pass this info to script, + // we should do so in the same way as I am doing below. + /* + Point2F texSizeScriptConst( 0, 0 ); + String buffer; + if ( mActiveTextures[0] ) + { + texSizeScriptConst.x = (F32)mActiveTextures[0]->getWidth(); + texSizeScriptConst.y = (F32)mActiveTextures[0]->getHeight(); + + dSscanf( buffer.c_str(), "%g %g", texSizeScriptConst.x, texSizeScriptConst.y ); + } + */ + + { + PROFILE_SCOPE( PostEffect_SetShaderConsts ); + + // Pass some data about the current render state to script. + // + // TODO: This is pretty messy... it should go away. This info + // should be available from some other script accessible method + // or field which isn't PostEffect specific. + // + if ( state ) + { + Con::setFloatVariable( "$Param::NearDist", state->getNearPlane() ); + Con::setFloatVariable( "$Param::FarDist", state->getFarPlane() ); + } + + setShaderConsts_callback(); + } + + EffectConstTable::Iterator iter = mEffectConsts.begin(); + for ( ; iter != mEffectConsts.end(); iter++ ) + iter->value->setToBuffer( mShaderConsts ); +} + +void PostEffect::_setupTexture( U32 stage, GFXTexHandle &inputTex, const RectI *inTexViewport ) +{ + const String &texFilename = mTexFilename[ stage ]; + + GFXTexHandle theTex; + NamedTexTarget *namedTarget = NULL; + + RectI viewport = GFX->getViewport(); + + if ( texFilename.compare( "$inTex", 0, String::NoCase ) == 0 ) + { + theTex = inputTex; + + if ( inTexViewport ) + { + viewport = *inTexViewport; + } + else if ( theTex ) + { + viewport.set( 0, 0, theTex->getWidth(), theTex->getHeight() ); + } + } + else if ( texFilename.compare( "$backBuffer", 0, String::NoCase ) == 0 ) + { + theTex = PFXMGR->getBackBufferTex(); + + // Always use the GFX viewport when reading from the backbuffer + } + else if ( texFilename.isNotEmpty() && texFilename[0] == '#' ) + { + namedTarget = NamedTexTarget::find( texFilename.c_str() + 1 ); + if ( namedTarget ) + { + theTex = namedTarget->getTexture( 0 ); + viewport = namedTarget->getViewport(); + } + } + else + { + theTex = mTextures[ stage ]; + if ( theTex ) + viewport.set( 0, 0, theTex->getWidth(), theTex->getHeight() ); + } + + mActiveTextures[ stage ] = theTex; + mActiveNamedTarget[ stage ] = namedTarget; + mActiveTextureViewport[ stage ] = viewport; + + if ( theTex.isValid() ) + GFX->setTexture( stage, theTex ); +} + +void PostEffect::_setupTransforms() +{ + // Set everything to identity. + GFX->setWorldMatrix( MatrixF::Identity ); + GFX->setProjectionMatrix( MatrixF::Identity ); +} + +void PostEffect::_setupTarget( const SceneRenderState *state, bool *outClearTarget ) +{ + if ( mNamedTarget.isRegistered() || + mTargetName.compare( "$outTex", 0, String::NoCase ) == 0 ) + { + // Size it relative to the texture of the first stage or + // if NULL then use the current target. + + Point2I targetSize; + + // If we have an absolute target size then use that. + if ( !mTargetSize.isZero() ) + targetSize = mTargetSize; + + // Else generate a relative size using the target scale. + else if ( mActiveTextures[ 0 ] ) + { + const Point3I &texSize = mActiveTextures[ 0 ]->getSize(); + + targetSize.set( texSize.x * mTargetScale.x, + texSize.y * mTargetScale.y ); + } + else + { + GFXTarget *oldTarget = GFX->getActiveRenderTarget(); + const Point2I &oldTargetSize = oldTarget->getSize(); + + targetSize.set( oldTargetSize.x * mTargetScale.x, + oldTargetSize.y * mTargetScale.y ); + } + + // Make sure its at least 1x1. + targetSize.setMax( Point2I::One ); + + if ( mNamedTarget.isRegistered() || + !mTargetTex || + mTargetTex.getWidthHeight() != targetSize ) + { + mTargetTex.set( targetSize.x, targetSize.y, mTargetFormat, + &PostFxTargetProfile, "PostEffect::_setupTarget" ); + + if ( mTargetClear == PFXTargetClear_OnCreate ) + *outClearTarget = true; + + mNamedTarget.setViewport( RectI( 0, 0, targetSize.x, targetSize.y ) ); + } + } + else + mTargetTex = NULL; + + // Do we have a named depthStencil target? + if ( mNamedTargetDepthStencil.isRegistered() ) + { + // Size it relative to the texture of the first stage or + // if NULL then use the current target. + Point2I targetSize; + + // If we have an absolute target size then use that. + if ( !mTargetSize.isZero() ) + targetSize = mTargetSize; + + // Else generate a relative size using the target scale. + else if ( mActiveTextures[ 0 ] ) + { + const Point3I &texSize = mActiveTextures[ 0 ]->getSize(); + + targetSize.set( texSize.x * mTargetScale.x, + texSize.y * mTargetScale.y ); + } + else + { + GFXTarget *oldTarget = GFX->getActiveRenderTarget(); + const Point2I &oldTargetSize = oldTarget->getSize(); + + targetSize.set( oldTargetSize.x * mTargetScale.x, + oldTargetSize.y * mTargetScale.y ); + } + + // Make sure its at least 1x1. + targetSize.setMax( Point2I::One ); + + if ( mNamedTargetDepthStencil.isRegistered() && + mTargetDepthStencil.getWidthHeight() != targetSize ) + { + mTargetDepthStencil.set( targetSize.x, targetSize.y, GFXFormatD24S8, + &GFXDefaultZTargetProfile, "PostEffect::_setupTarget" ); + + if ( mTargetClear == PFXTargetClear_OnCreate ) + *outClearTarget = true; + + mNamedTargetDepthStencil.setViewport( RectI( 0, 0, targetSize.x, targetSize.y ) ); + } + } + else + mTargetDepthStencil = NULL; + + if ( mTargetClear == PFXTargetClear_OnDraw ) + *outClearTarget = true; + + if ( !mTarget && (mTargetTex || mTargetDepthStencil) ) + mTarget = GFX->allocRenderToTextureTarget(); +} + +void PostEffect::_cleanTargets( bool recurse ) +{ + mTargetTex = NULL; + mTargetDepthStencil = NULL; + mTarget = NULL; + + if ( !recurse ) + return; + + // Clear the children too! + for ( U32 i = 0; i < size(); i++ ) + { + PostEffect *effect = (PostEffect*)(*this)[i]; + effect->_cleanTargets( true ); + } +} + +void PostEffect::process( const SceneRenderState *state, + GFXTexHandle &inOutTex, + const RectI *inTexViewport ) +{ + // If the shader is forced to be skipped... then skip. + if ( mSkip ) + return; + + // Skip out if we don't support reflection passes. + if ( state && state->isReflectPass() && !mAllowReflectPass ) + return; + + if ( mOneFrameOnly && !mOnThisFrame ) + return; + + // Check requirements if the shader needs updating. + if ( mUpdateShader ) + { + _checkRequirements(); + + // Clear the targets if we failed passing + // the requirements at this time. + if ( !mIsValid ) + _cleanTargets( true ); + } + + // If we're not valid then we cannot render. + if ( !mIsValid ) + return; + + GFXDEBUGEVENT_SCOPE_EX( PostEffect_Process, ColorI::GREEN, avar("PostEffect: %s", getName()) ); + + preProcess_callback(); + + GFXTransformSaver saver; + + // Set the textures. + for ( U32 i = 0; i < NumTextures; i++ ) + _setupTexture( i, inOutTex, inTexViewport ); + + _setupStateBlock( state ) ; + _setupTransforms(); + + bool clearTarget = false; + _setupTarget( state, &clearTarget ); + + if ( mTargetTex || mTargetDepthStencil ) + { + +#ifdef TORQUE_OS_XENON + // You may want to disable this functionality for speed reasons as it does + // add some overhead. The upside is it makes things "just work". If you + // re-work your post-effects properly, this is not needed. + // + // If this post effect doesn't alpha blend to the back-buffer, than preserve + // the active render target contents so they are still around the next time + // that render target activates + if(!mStateBlockData->getState().blendEnable) + GFX->getActiveRenderTarget()->preserve(); +#endif + + GFX->pushActiveRenderTarget(); + mTarget->attachTexture( GFXTextureTarget::Color0, mTargetTex ); + + // Set the right depth stencil target. + if ( !mTargetDepthStencil && mTargetTex.getWidthHeight() == GFX->getActiveRenderTarget()->getSize() ) + mTarget->attachTexture( GFXTextureTarget::DepthStencil, GFXTextureTarget::sDefaultDepthStencil ); + else + mTarget->attachTexture( GFXTextureTarget::DepthStencil, mTargetDepthStencil ); + + GFX->setActiveRenderTarget( mTarget ); + } + + if ( clearTarget ) + GFX->clear( GFXClearTarget, mTargetClearColor, 1.f, 0 ); + + // Setup the shader and constants. + if ( mShader ) + { + _setupConstants( state ); + + GFX->setShader( mShader ); + GFX->setShaderConstBuffer( mShaderConsts ); + } + else + GFX->disableShaders(); + + Frustum frustum; + if ( state ) + frustum = state->getFrustum(); + else + { + // If we don't have a scene state then setup + // a dummy frustum... you better not be depending + // on this being related to the camera in any way. + + frustum = Frustum(); + } + + GFXVertexBufferHandle vb; + _updateScreenGeometry( frustum, &vb ); + + // Draw it. + GFX->setVertexBuffer( vb ); + GFX->drawPrimitive( GFXTriangleFan, 0, 2 ); + + // Allow PostEffecVis to hook in. + PFXVIS->onPFXProcessed( this ); + + if ( mTargetTex || mTargetDepthStencil ) + { + mTarget->resolve(); + GFX->popActiveRenderTarget(); + } + else + { + // We wrote to the active back buffer, so release + // the current texture copy held by the manager. + // + // This ensures a new copy is made. + PFXMGR->releaseBackBufferTex(); + } + + // Return and release our target texture. + inOutTex = mTargetTex; + if ( !mNamedTarget.isRegistered() ) + mTargetTex = NULL; + + // Restore the transforms before the children + // are processed as it screws up the viewport. + saver.restore(); + + // Now process my children. + iterator i = begin(); + for ( ; i != end(); i++ ) + { + PostEffect *effect = static_cast(*i); + effect->process( state, inOutTex ); + } + + if ( mOneFrameOnly ) + mOnThisFrame = false; +} + +bool PostEffect::_setIsEnabled( void *object, const char *index, const char *data ) +{ + bool enabled = dAtob( data ); + if ( enabled ) + static_cast( object )->enable(); + else + static_cast( object )->disable(); + + // Always return false from a protected field. + return false; +} + +void PostEffect::enable() +{ + // Don't add TexGen PostEffects to the PostEffectManager! + if ( mRenderTime == PFXTexGenOnDemand ) + return; + + // Ignore it if its already enabled. + if ( mEnabled ) + return; + + mEnabled = true; + + // We cannot really enable the effect + // until its been registed. + if ( !isProperlyAdded() ) + return; + + // If the enable callback returns 'false' then + // leave the effect disabled. + if ( !onEnabled_callback() ) + { + mEnabled = false; + return; + } + + PFXMGR->_addEffect( this ); +} + +void PostEffect::disable() +{ + if ( !mEnabled ) + return; + + mEnabled = false; + _cleanTargets( true ); + + if ( isProperlyAdded() ) + { + PFXMGR->_removeEffect( this ); + onDisabled_callback(); + } +} + +void PostEffect::reload() +{ + // Reload the shader if we have one or mark it + // for updating when its processed next. + if ( mShader ) + mShader->reload(); + else + mUpdateShader = true; + + // Null stateblock so it is reloaded. + mStateBlock = NULL; + + // Call reload on any children + // this PostEffect may have. + for ( U32 i = 0; i < size(); i++ ) + { + PostEffect *effect = (PostEffect*)(*this)[i]; + effect->reload(); + } +} + +void PostEffect::setTexture( U32 index, const String &texFilePath ) +{ + // Set the new texture name. + mTexFilename[index] = texFilePath; + mTextures[index].free(); + + // Skip empty stages or ones with variable or target names. + if ( texFilePath.isEmpty() || + texFilePath[0] == '$' || + texFilePath[0] == '#' ) + return; + + // Try to load the texture. + mTextures[index].set( texFilePath, &PostFxTextureProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ) ); +} + +void PostEffect::setShaderConst( const String &name, const String &val ) +{ + PROFILE_SCOPE( PostEffect_SetShaderConst ); + + EffectConstTable::Iterator iter = mEffectConsts.find( name ); + if ( iter == mEffectConsts.end() ) + { + EffectConst *newConst = new EffectConst( name, val ); + iter = mEffectConsts.insertUnique( name, newConst ); + } + + iter->value->set( val ); +} + +F32 PostEffect::getAspectRatio() const +{ + const Point2I &rtSize = GFX->getActiveRenderTarget()->getSize(); + return (F32)rtSize.x / (F32)rtSize.y; +} + +void PostEffect::_checkRequirements() +{ + // This meets requirements if its shader loads + // properly, we can find all the input textures, + // and its formats are supported. + + mIsValid = false; + mUpdateShader = false; + mShader = NULL; + + // First make sure the target format is supported. + if ( mNamedTarget.isRegistered() ) + { + Vector formats; + formats.push_back( mTargetFormat ); + GFXFormat format = GFX->selectSupportedFormat( &PostFxTargetProfile, + formats, + true, + false, + false ); + + // If we didn't get our format out then its unsupported! + if ( format != mTargetFormat ) + return; + } + + // Gather macros specified on this PostEffect. + Vector macros( mShaderMacros ); + + // Now check the input named targets and make sure + // they exist... else we're invalid. + for ( U32 i=0; i < NumTextures; i++ ) + { + const String &texFilename = mTexFilename[i]; + + if ( texFilename.isNotEmpty() && texFilename[0] == '#' ) + { + NamedTexTarget *namedTarget = NamedTexTarget::find( texFilename.c_str() + 1 ); + if ( !namedTarget ) + return; + + // Grab the macros for shader initialization. + namedTarget->getShaderMacros( ¯os ); + } + } + + // Finally find and load the shader. + ShaderData *shaderData; + if ( Sim::findObject( mShaderName, shaderData ) ) + if ( shaderData->getPixVersion() <= GFX->getPixelShaderVersion() ) + mShader = shaderData->getShader( macros ); + + // If we didn't get a shader... we're done. + if ( !mShader ) + return; + + // If we got here then we're valid. + mIsValid = true; +} + +bool PostEffect::dumpShaderDisassembly( String &outFilename ) const +{ + String data; + + if ( !mShader || !mShader->getDisassembly( data ) ) + return false; + + outFilename = FS::MakeUniquePath( "", "ShaderDisassembly", "txt" ); + + FileStream *fstream = FileStream::createAndOpen( outFilename, Torque::FS::File::Write ); + if ( !fstream ) + return false; + + fstream->write( data ); + fstream->close(); + delete fstream; + + return true; +} + +SimSet* PostEffect::getSet() const +{ + SimSet *set; + if ( !Sim::findObject( "PFXSet", set ) ) + { + set = new SimSet(); + set->registerObject( "PFXSet" ); + Sim::getRootGroup()->addObject( set ); + } + + return set; +} + +void PostEffect::setShaderMacro( const String &name, const String &value ) +{ + // Check to see if we already have this macro. + Vector::iterator iter = mShaderMacros.begin(); + for ( ; iter != mShaderMacros.end(); iter++ ) + { + if ( iter->name == name ) + { + if ( iter->value != value ) + { + iter->value = value; + mUpdateShader = true; + } + return; + } + } + + // Add a new macro. + mShaderMacros.increment(); + mShaderMacros.last().name = name; + mShaderMacros.last().value = value; + mUpdateShader = true; +} + +bool PostEffect::removeShaderMacro( const String &name ) +{ + Vector::iterator iter = mShaderMacros.begin(); + for ( ; iter != mShaderMacros.end(); iter++ ) + { + if ( iter->name == name ) + { + mShaderMacros.erase( iter ); + mUpdateShader = true; + return true; + } + } + + return false; +} + +void PostEffect::clearShaderMacros() +{ + if ( mShaderMacros.empty() ) + return; + + mShaderMacros.clear(); + mUpdateShader = true; +} + +GFXTextureObject* PostEffect::_getTargetTexture( U32 ) +{ + // A TexGen PostEffect will generate its texture now if it + // has not already. + if ( mRenderTime == PFXTexGenOnDemand && + ( !mTargetTex || mUpdateShader ) ) + { + GFXTexHandle chainTex; + process( NULL, chainTex ); + + // TODO: We should add a conditional copy + // to a non-RT texture here to reduce the + // amount of non-swappable RTs in use. + } + + return mTargetTex.getPointer(); +} + +DefineEngineMethod( PostEffect, reload, void, (),, + "Reloads the effect shader and textures." ) +{ + return object->reload(); +} + +DefineEngineMethod( PostEffect, enable, void, (),, + "Enables the effect." ) +{ + object->enable(); +} + +DefineEngineMethod( PostEffect, disable, void, (),, + "Disables the effect." ) +{ + object->disable(); +} + +DefineEngineMethod( PostEffect, toggle, bool, (),, + "Toggles the effect between enabled / disabled.\n" + "@return True if effect is enabled." ) +{ + if ( object->isEnabled() ) + object->disable(); + else + object->enable(); + + return object->isEnabled(); +} + +DefineEngineMethod( PostEffect, isEnabled, bool, (),, + "@return True if the effect is enabled." ) +{ + return object->isEnabled(); +} + +DefineEngineMethod( PostEffect, setTexture, void, ( S32 index, const char *filePath ),, + "This is used to set the texture file and load the texture on a running effect. " + "If the texture file is not different from the current file nothing is changed. If " + "the texture cannot be found a null texture is assigned.\n" + "@param index The texture stage index.\n" + "@param filePath The file name of the texture to set.\n" ) +{ + if ( index > -1 && index < PostEffect::NumTextures ) + object->setTexture( index, filePath ); +} + +DefineEngineMethod( PostEffect, setShaderConst, void, ( const char* name, const char* value ),, + "Sets the value of a uniform defined in the shader. This will usually " + "be called within the setShaderConsts callback. Array type constants are " + "not supported.\n" + "@param name Name of the constanst, prefixed with '$'.\n" + "@param value Value to set, space seperate values with more than one element.\n" + "@tsexample\n" + "function MyPfx::setShaderConsts( %this )\n" + "{\n" + " // example float4 uniform\n" + " %this.setShaderConst( \"$colorMod\", \"1.0 0.9 1.0 1.0\" );\n" + " // example float1 uniform\n" + " %this.setShaderConst( \"$strength\", \"3.0\" );\n" + " // example integer uniform\n" + " %this.setShaderConst( \"$loops\", \"5\" );" + "}\n" + "@endtsexample" ) +{ + object->setShaderConst( name, value ); +} + +DefineEngineMethod( PostEffect, getAspectRatio, F32, (),, + "@return Width over height of the backbuffer." ) +{ + return object->getAspectRatio(); +} + +DefineEngineMethod( PostEffect, dumpShaderDisassembly, String, (),, + "Dumps this PostEffect shader's disassembly to a temporary text file.\n" + "@return Full path to the dumped file or an empty string if failed." ) +{ + String fileName; + object->dumpShaderDisassembly( fileName ); + + return fileName; +} + +DefineEngineMethod( PostEffect, setShaderMacro, void, ( const char* key, const char* value ), ( "" ), + "Adds a macro to the effect's shader or sets an existing one's value. " + "This will usually be called within the onAdd or preProcess callback.\n" + "@param key lval of the macro." + "@param value rval of the macro, or may be empty." + "@tsexample\n" + "function MyPfx::onAdd( %this )\n" + "{\n" + " %this.setShaderMacro( \"NUM_SAMPLES\", \"10\" );\n" + " %this.setShaderMacro( \"HIGH_QUALITY_MODE\" );\n" + " \n" + " // In the shader looks like... \n" + " // #define NUM_SAMPLES 10\n" + " // #define HIGH_QUALITY_MODE\n" + "}\n" + "@endtsexample" ) +{ + object->setShaderMacro( key, value ); +} + +DefineEngineMethod( PostEffect, removeShaderMacro, void, ( const char* key ),, + "Remove a shader macro. This will usually be called within the preProcess callback.\n" + "@param key Macro to remove." ) +{ + object->removeShaderMacro( key ); +} + +DefineEngineMethod( PostEffect, clearShaderMacros, void, (),, + "Remove all shader macros." ) +{ + object->clearShaderMacros(); +} + +DefineEngineFunction( dumpRandomNormalMap, void, (),, + "Creates a 64x64 normal map texture filled with noise. The texture is saved " + "to randNormTex.png in the location of the game executable.\n\n" + "@ingroup GFX") +{ + GFXTexHandle tex; + + tex.set( 64, 64, GFXFormatR8G8B8A8, &GFXDefaultPersistentProfile, "" ); + + GFXLockedRect *rect = tex.lock(); + U8 *f = rect->bits; + + for ( U32 i = 0; i < 64*64; i++, f += 4 ) + { + VectorF vec; + + vec.x = mRandF( -1.0f, 1.0f ); + vec.y = mRandF( -1.0f, 1.0f ); + vec.z = mRandF( -1.0f, 1.0f ); + + vec.normalizeSafe(); + + f[0] = U8_MAX * ( ( 1.0f + vec.x ) * 0.5f ); + f[1] = U8_MAX * ( ( 1.0f + vec.y ) * 0.5f ); + f[2] = U8_MAX * ( ( 1.0f + vec.z ) * 0.5f ); + f[3] = U8_MAX; + } + + tex.unlock(); + + String path = Torque::FS::MakeUniquePath( "", "randNormTex", "png" ); + tex->dumpToDisk( "png", path ); +} \ No newline at end of file diff --git a/Engine/source/postFx/postEffect.h b/Engine/source/postFx/postEffect.h new file mode 100644 index 000000000..71ee725e8 --- /dev/null +++ b/Engine/source/postFx/postEffect.h @@ -0,0 +1,360 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _POST_EFFECT_H_ +#define _POST_EFFECT_H_ + +#ifndef _SIMSET_H_ +#include "console/simSet.h" +#endif +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _MPOINT2_H_ +#include "math/mPoint2.h" +#endif +#ifndef _GFXSHADER_H_ +#include "gfx/gfxShader.h" +#endif +#ifndef _GFXSTATEBLOCK_H_ +#include "gfx/gfxStateBlock.h" +#endif +#ifndef _GFXTEXTUREHANDLE_H_ +#include "gfx/gfxTextureHandle.h" +#endif +#ifndef _GFXTARGET_H_ +#include "gfx/gfxTarget.h" +#endif +#ifndef _GFXVERTEXBUFFER_H_ +#include "gfx/gfxVertexBuffer.h" +#endif +#ifndef _POSTEFFECTCOMMON_H_ +#include "postFx/postEffectCommon.h" +#endif +#ifndef _TDICTIONARY_H_ +#include "core/util/tDictionary.h" +#endif +#ifndef _MATTEXTURETARGET_H_ +#include "materials/matTextureTarget.h" +#endif + +class GFXStateBlockData; +class Frustum; +class SceneRenderState; +class ConditionerFeature; + + +/// +GFX_DeclareTextureProfile( PostFxTargetProfile ); + + + + +/// +class PostEffect : public SimGroup +{ + typedef SimGroup Parent; + + friend class PostEffectVis; + +public: + + enum + { + NumTextures = 6, + }; + +protected: + + FileName mTexFilename[NumTextures]; + + GFXTexHandle mTextures[NumTextures]; + + NamedTexTarget mNamedTarget; + NamedTexTarget mNamedTargetDepthStencil; + + GFXTextureObject *mActiveTextures[NumTextures]; + + NamedTexTarget *mActiveNamedTarget[NumTextures]; + + RectI mActiveTextureViewport[NumTextures]; + + GFXStateBlockData *mStateBlockData; + + GFXStateBlockRef mStateBlock; + + String mShaderName; + + GFXShaderRef mShader; + + Vector mShaderMacros; + + GFXShaderConstBufferRef mShaderConsts; + + GFXShaderConstHandle *mRTSizeSC; + GFXShaderConstHandle *mOneOverRTSizeSC; + + GFXShaderConstHandle *mTexSizeSC[NumTextures]; + GFXShaderConstHandle *mRenderTargetParamsSC[NumTextures]; + + GFXShaderConstHandle *mViewportOffsetSC; + + GFXShaderConstHandle *mFogDataSC; + GFXShaderConstHandle *mFogColorSC; + GFXShaderConstHandle *mEyePosSC; + GFXShaderConstHandle *mMatWorldToScreenSC; + GFXShaderConstHandle *mMatScreenToWorldSC; + GFXShaderConstHandle *mMatPrevScreenToWorldSC; + GFXShaderConstHandle *mNearFarSC; + GFXShaderConstHandle *mInvNearFarSC; + GFXShaderConstHandle *mWorldToScreenScaleSC; + GFXShaderConstHandle *mWaterColorSC; + GFXShaderConstHandle *mWaterFogDataSC; + GFXShaderConstHandle *mAmbientColorSC; + GFXShaderConstHandle *mWaterFogPlaneSC; + GFXShaderConstHandle *mWaterDepthGradMaxSC; + GFXShaderConstHandle *mScreenSunPosSC; + GFXShaderConstHandle *mLightDirectionSC; + GFXShaderConstHandle *mCameraForwardSC; + GFXShaderConstHandle *mAccumTimeSC; + GFXShaderConstHandle *mDeltaTimeSC; + GFXShaderConstHandle *mInvCameraMatSC; + + bool mAllowReflectPass; + + /// If true update the shader. + bool mUpdateShader; + + GFXTextureTargetRef mTarget; + + String mTargetName; + GFXTexHandle mTargetTex; + + String mTargetDepthStencilName; + GFXTexHandle mTargetDepthStencil; + + /// If mTargetSize is zero then this scale is + /// used to make a relative texture size to the + /// active render target. + Point2F mTargetScale; + + /// If non-zero this is used as the absolute + /// texture target size. + /// @see mTargetScale + Point2I mTargetSize; + + GFXFormat mTargetFormat; + + /// The color to prefill the named target when + /// first created by the effect. + ColorF mTargetClearColor; + + PFXRenderTime mRenderTime; + PFXTargetClear mTargetClear; + + String mRenderBin; + + F32 mRenderPriority; + + /// This is true if the effect has been succesfully + /// initialized and all requirements are met for use. + bool mIsValid; + + /// True if the effect has been enabled by the manager. + bool mEnabled; + + /// Skip processing of this PostEffect and its children even if its parent is enabled. + /// Parent and sibling PostEffects in the chain are still processed. + /// This is intended for debugging purposes. + bool mSkip; + + bool mOneFrameOnly; + bool mOnThisFrame; + + U32 mShaderReloadKey; + + class EffectConst + { + public: + + EffectConst( const String &name, const String &val ) + : mName( name ), + mHandle( NULL ), + mDirty( true ) + { + set( val ); + } + + void set( const String &newVal ); + + void setToBuffer( GFXShaderConstBufferRef buff ); + + String mName; + + GFXShaderConstHandle *mHandle; + + String mStringVal; + + bool mDirty; + }; + + typedef HashTable EffectConstTable; + + EffectConstTable mEffectConsts; + + /// + virtual void _updateScreenGeometry( const Frustum &frustum, + GFXVertexBufferHandle *outVB ); + + /// + virtual void _setupStateBlock( const SceneRenderState *state ); + + /// + virtual void _setupConstants( const SceneRenderState *state ); + + /// + virtual void _setupTransforms(); + + /// + virtual void _setupTarget( const SceneRenderState *state, bool *outClearTarget ); + + /// + virtual void _setupTexture( U32 slot, GFXTexHandle &inputTex, const RectI *inTexViewport ); + + /// Protected set method for toggling the enabled state. + static bool _setIsEnabled( void *object, const char *index, const char *data ); + + /// Called from the light manager activate signal. + /// @see LightManager::addActivateCallback + void _onLMActivate( const char*, bool activate ) + { + if ( activate ) + mUpdateShader = true; + } + + /// We handle texture events to release named rendered targets. + /// @see GFXTextureManager::addEventDelegate + void _onTextureEvent( GFXTexCallbackCode code ) + { + if ( code == GFXZombify && (mNamedTarget.isRegistered() || mNamedTargetDepthStencil.isRegistered()) ) + _cleanTargets(); + } + + /// + void _updateConditioners(); + + /// + void _cleanTargets( bool recurse = false ); + + /// + void _checkRequirements(); + + /// + GFXTextureObject* _getTargetTexture( U32 index ); + +public: + + /// Constructor. + PostEffect(); + + /// Destructor. + virtual ~PostEffect(); + + DECLARE_CONOBJECT(PostEffect); + + // SimObject + virtual bool onAdd(); + virtual void onRemove(); + static void initPersistFields(); + + /// @name Callbacks + /// @{ + + DECLARE_CALLBACK( void, onAdd, () ); + DECLARE_CALLBACK( void, preProcess, () ); + DECLARE_CALLBACK( void, setShaderConsts, () ); + DECLARE_CALLBACK( bool, onEnabled, () ); + DECLARE_CALLBACK( void, onDisabled, () ); + + /// @} + + virtual void process( const SceneRenderState *state, + GFXTexHandle &inOutTex, + const RectI *inTexViewport = NULL ); + + /// + void reload(); + + /// + void enable(); + + /// + void disable(); + + /// Dump the shader disassembly to a temporary text file. + /// Returns true and sets outFilename to the file if successful. + bool dumpShaderDisassembly( String &outFilename ) const; + + /// Returns the SimSet which contains all PostEffects. + SimSet* getSet() const; + + /// + bool isEnabled() const { return mEnabled; } + + /// Is set to skip rendering. + bool isSkipped() const { return mSkip; } + + /// Set the effect to skip rendering. + void setSkip( bool skip ) { mSkip = skip; } + + PFXRenderTime getRenderTime() const { return mRenderTime; } + + const String& getRenderBin() const { return mRenderBin; } + + F32 getPriority() const { return mRenderPriority; } + + void setTexture( U32 index, const String &filePath ); + + void setShaderMacro( const String &name, const String &value = String::EmptyString ); + bool removeShaderMacro( const String &name ); + void clearShaderMacros(); + + /// + void setShaderConst( const String &name, const String &val ); + + void setOnThisFrame( bool enabled ) { mOnThisFrame = enabled; } + bool isOnThisFrame() { return mOnThisFrame; } + void setOneFrameOnly( bool enabled ) { mOneFrameOnly = enabled; } + bool isOneFrameOnly() { return mOneFrameOnly; } + + F32 getAspectRatio() const; + + + enum PostEffectRequirements + { + RequiresDepth = BIT(0), + RequiresNormals = BIT(1), + RequiresLightInfo = BIT(2), + }; +}; + +#endif // _POST_EFFECT_H_ \ No newline at end of file diff --git a/Engine/source/postFx/postEffectCommon.h b/Engine/source/postFx/postEffectCommon.h new file mode 100644 index 000000000..dcbbdda27 --- /dev/null +++ b/Engine/source/postFx/postEffectCommon.h @@ -0,0 +1,99 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _POSTEFFECTCOMMON_H_ +#define _POSTEFFECTCOMMON_H_ + +#ifndef _DYNAMIC_CONSOLETYPES_H_ + #include "console/dynamicTypes.h" +#endif + + +/// +enum PFXRenderTime +{ + /// Before a RenderInstManager bin. + PFXBeforeBin, + + /// After a RenderInstManager bin. + PFXAfterBin, + + /// After the diffuse rendering pass. + PFXAfterDiffuse, + + /// When the end of the frame is reached. + PFXEndOfFrame, + + /// This PostEffect is not processed by the manager. + /// It will generate its texture when it is requested. + PFXTexGenOnDemand +}; + +DefineEnumType( PFXRenderTime ); + + +/// PFXTargetClear specifies whether and how +/// often a given PostEffect's target will be cleared. +enum PFXTargetClear +{ + /// Never clear the PostEffect target. + PFXTargetClear_None, + + /// Clear once on create. + PFXTargetClear_OnCreate, + + /// Clear before every draw. + PFXTargetClear_OnDraw, +}; + +DefineEnumType( PFXTargetClear ); + +/// +struct PFXFrameState +{ + MatrixF worldToCamera; + MatrixF cameraToScreen; + + PFXFrameState() + : worldToCamera( true ), + cameraToScreen( true ) + { + } +}; + +/// +GFX_DeclareTextureProfile( PostFxTextureProfile ); + +/// +GFXDeclareVertexFormat( PFXVertex ) +{ + /// xyz position. + Point3F point; + + /// The screen space texture coord. + Point2F texCoord; + + /// + Point3F wsEyeRay; +}; + +#endif // _POSTEFFECTCOMMON_H_ \ No newline at end of file diff --git a/Engine/source/postFx/postEffectManager.cpp b/Engine/source/postFx/postEffectManager.cpp new file mode 100644 index 000000000..a5ea5218e --- /dev/null +++ b/Engine/source/postFx/postEffectManager.cpp @@ -0,0 +1,321 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "postFx/postEffectManager.h" + +#include "postFx/postEffect.h" +#include "postFx/postEffectVis.h" +#include "renderInstance/renderBinManager.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "console/consoleTypes.h" +#include "core/module.h" + + +MODULE_BEGIN( PostEffectManager ) + + MODULE_SHUTDOWN_AFTER( Sim ) + + MODULE_INIT + { + ManagedSingleton< PostEffectManager >::createSingleton(); + } + + MODULE_SHUTDOWN + { + ManagedSingleton< PostEffectManager >::deleteSingleton(); + } + +MODULE_END; + + +bool PostEffectManager::smRenderEffects = true; + +PostEffectManager::PostEffectManager() : + mFrameStateSwitch( false ), + mLastBackBufferTarget( NULL ) +{ + GFXDevice::getDeviceEventSignal().notify( this, &PostEffectManager::_handleDeviceEvent ); + RenderPassManager::getRenderBinSignal().notify( this, &PostEffectManager::_handleBinEvent ); + SceneManager::getPostRenderSignal().notify( this, &PostEffectManager::_onPostRenderPass ); + + Con::addVariable("pref::enablePostEffects", TypeBool, &smRenderEffects, + "@brief If true, post effects will be eanbled.\n\n" + "@ingroup Game"); +} + +PostEffectManager::~PostEffectManager() +{ + GFXDevice::getDeviceEventSignal().remove( this, &PostEffectManager::_handleDeviceEvent ); + RenderPassManager::getRenderBinSignal().remove( this, &PostEffectManager::_handleBinEvent ); + SceneManager::getPostRenderSignal().remove( this, &PostEffectManager::_onPostRenderPass ); +} + +bool PostEffectManager::_handleDeviceEvent( GFXDevice::GFXDeviceEventType evt ) +{ + switch( evt ) + { + case GFXDevice::deStartOfFrame: + PFXVIS->onStartOfFrame(); + + // Fall through + + case GFXDevice::deDestroy: + + // Free the back buffer as the device or + // its content is now invalid. + releaseBackBufferTex(); + + break; + + case GFXDevice::deEndOfFrame: + + renderEffects( NULL, PFXEndOfFrame ); + + // Toggle frame state history switch + mFrameStateSwitch = !mFrameStateSwitch; + + break; + + default: + break; + } + + return true; +} + +void PostEffectManager::_handleBinEvent( RenderBinManager *bin, + const SceneRenderState* sceneState, + bool isBinStart ) +{ + if ( sceneState->isShadowPass() || + sceneState->isOtherPass() ) + return; + + // We require a bin name to process effects... without + // it we can skip the bin entirely. + String binName( bin->getName() ); + if ( binName.isEmpty() ) + return; + + renderEffects( sceneState, isBinStart ? PFXBeforeBin : PFXAfterBin, binName ); +} + +void PostEffectManager::_onPostRenderPass( SceneManager *sceneGraph, const SceneRenderState *sceneState ) +{ + if ( !sceneState->isDiffusePass() ) + return; + + renderEffects( sceneState, PFXAfterDiffuse ); +} + +GFXTextureObject* PostEffectManager::getBackBufferTex() +{ + GFXTarget *target = GFX->getActiveRenderTarget(); + + if ( mBackBufferCopyTex.isNull() || + target != mLastBackBufferTarget ) + { + const Point2I &targetSize = target->getSize(); + GFXFormat targetFormat = target->getFormat(); + + mBackBufferCopyTex.set( targetSize.x, targetSize.y, + targetFormat, + &PostFxTargetProfile, "mBackBufferCopyTex" ); + + target->resolveTo( mBackBufferCopyTex ); + mLastBackBufferTarget = target; + } + + return mBackBufferCopyTex; +} + +void PostEffectManager::releaseBackBufferTex() +{ + mBackBufferCopyTex = NULL; + mLastBackBufferTarget = NULL; +} + +bool PostEffectManager::_addEffect( PostEffect *effect ) +{ + EffectVector *effects = NULL; + + const String &binName = effect->getRenderBin(); + + switch( effect->getRenderTime() ) + { + case PFXAfterDiffuse: + effects = &mAfterDiffuseList; + break; + + case PFXEndOfFrame: + effects = &mEndOfFrameList; + break; + + case PFXBeforeBin: + effects = &mBeforeBinMap[binName]; + break; + + case PFXAfterBin: + effects = &mAfterBinMap[binName]; + break; + + case PFXTexGenOnDemand: + break; + } + + if ( effects == NULL ) + return false; + + effects->push_back( effect ); + + // Resort the effects by priority. + effects->sort( &_effectPrioritySort ); + + return true; +} + +bool PostEffectManager::_removeEffect( PostEffect *effect ) +{ + // Check the end of frame list. + EffectVector::iterator iter = find( mEndOfFrameList.begin(), mEndOfFrameList.end(), effect ); + if ( iter != mEndOfFrameList.end() ) + { + mEndOfFrameList.erase( iter ); + return true; + } + + // Check the diffuse list. + iter = find( mAfterDiffuseList.begin(), mAfterDiffuseList.end(), effect ); + if ( iter != mAfterDiffuseList.end() ) + { + mAfterDiffuseList.erase( iter ); + return true; + } + + // Now check the bin maps. + EffectMap::Iterator mapIter = mAfterBinMap.begin(); + for( ; mapIter != mAfterBinMap.end(); mapIter++ ) + { + EffectVector &effects = mapIter->value; + iter = find( effects.begin(), effects.end(), effect ); + if ( iter != effects.end() ) + { + effects.erase( iter ); + return true; + } + } + + mapIter = mBeforeBinMap.begin(); + for( ; mapIter != mBeforeBinMap.end(); mapIter++ ) + { + EffectVector &effects = mapIter->value; + iter = find( effects.begin(), effects.end(), effect ); + if ( iter != effects.end() ) + { + effects.erase( iter ); + return true; + } + } + + return false; +} + +void PostEffectManager::renderEffects( const SceneRenderState *state, + const PFXRenderTime effectTiming, + const String &binName ) +{ + // MACHAX - The proper fix is to ensure that PostFX do not get rendered if + // their shader failed to load. +#ifdef TORQUE_OS_MAC + return; +#endif + + // Check the global render effect state as + // well as the + if ( !smRenderEffects || + ( state && !state->usePostEffects() )) + return; + + EffectVector *effects = NULL; + + switch( effectTiming ) + { + case PFXBeforeBin: + effects = &mBeforeBinMap[binName]; + break; + + case PFXAfterBin: + effects = &mAfterBinMap[binName]; + break; + + case PFXAfterDiffuse: + effects = &mAfterDiffuseList; + break; + + case PFXEndOfFrame: + effects = &mEndOfFrameList; + break; + + case PFXTexGenOnDemand: + break; + } + + AssertFatal( effects != NULL, "Bad effect time" ); + + // Skip out if we don't have any effects. + if ( effects->empty() ) + return; + + // This is used to pass the output texture + // of one effect into the next effect. + GFXTexHandle chainTex; + + // Process the effects. + for ( U32 i = 0; i < effects->size(); i++ ) + { + PostEffect *effect = (*effects)[i]; + AssertFatal( effect != NULL, "Somehow this happened" ); + effect->process( state, chainTex ); + } +} + +void PostEffectManager::setFrameMatrices( const MatrixF &worldToCamera, const MatrixF &cameraToScreen ) +{ + PFXFrameState &thisFrame = mFrameState[mFrameStateSwitch]; + thisFrame.worldToCamera = worldToCamera; + thisFrame.cameraToScreen = cameraToScreen; +} + +S32 PostEffectManager::_effectPrioritySort( PostEffect* const *e1, PostEffect* const *e2 ) +{ + F32 p1 = (*e1)->getPriority(); + F32 p2 = (*e2)->getPriority(); + + if( p1 > p2 ) + return -1; + else if( p1 < p2 ) + return 1; + + return 0; +} diff --git a/Engine/source/postFx/postEffectManager.h b/Engine/source/postFx/postEffectManager.h new file mode 100644 index 000000000..0ef72a586 --- /dev/null +++ b/Engine/source/postFx/postEffectManager.h @@ -0,0 +1,139 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _POSTEFFECTMANAGER_H_ +#define _POSTEFFECTMANAGER_H_ + +#ifndef _GFXDEVICE_H_ +#include "gfx/gfxDevice.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _TDICTIONARY_H_ +#include "core/util/tDictionary.h" +#endif +#ifndef _TSINGLETON_H_ +#include "core/util/tSingleton.h" +#endif +#ifndef _POSTEFFECTCOMMON_H_ +#include "postFx/postEffectCommon.h" +#endif + +class PostEffect; +class RenderBinManager; +class SceneRenderState; +class SceneManager; + + +class PostEffectManager +{ +protected: + + friend class PostEffect; + + typedef Vector EffectVector; + + typedef Map EffectMap; + + /// A global flag for toggling the post effect system. It + /// is tied to the $pref::enablePostEffects preference. + static bool smRenderEffects; + + EffectVector mEndOfFrameList; + EffectVector mAfterDiffuseList; + EffectMap mAfterBinMap; + EffectMap mBeforeBinMap; + + /// A copy of the last requested back buffer. + GFXTexHandle mBackBufferCopyTex; + + //GFXTexHandle mBackBufferFloatCopyTex; + + /// The target at the time the last back buffer + /// was copied. Used to detect the need to recopy. + GFXTarget *mLastBackBufferTarget; + + // State for current frame and last frame + bool mFrameStateSwitch; + + PFXFrameState mFrameState[2]; + + bool _handleDeviceEvent( GFXDevice::GFXDeviceEventType evt ); + + void _handleBinEvent( RenderBinManager *bin, + const SceneRenderState* sceneState, + bool isBinStart ); + + /// + void _onPostRenderPass( SceneManager *sceneGraph, const SceneRenderState *sceneState ); + + // Helper method + void _updateResources(); + + /// + static S32 _effectPrioritySort( PostEffect* const*e1, PostEffect* const*e2 ); + + bool _addEffect( PostEffect *effect ); + + bool _removeEffect( PostEffect *effect ); + +public: + + PostEffectManager(); + + virtual ~PostEffectManager(); + + void renderEffects( const SceneRenderState *state, + const PFXRenderTime effectTiming, + const String &binName = String::EmptyString ); + + /// Returns the current back buffer texture taking + /// a copy of if the target has changed or the buffer + /// was previously released. + GFXTextureObject* getBackBufferTex(); + + /// Releases the current back buffer so that a + /// new copy is made on the next request. + void releaseBackBufferTex(); + + /* + bool submitEffect( PostEffect *effect, const PFXRenderTime renderTime = PFXDefaultRenderTime, const GFXRenderBinTypes afterBin = GFXBin_DefaultPostProcessBin ) + { + return _addEntry( effect, false, renderTime, afterBin ); + } + */ + + // State interface + const PFXFrameState &getFrameState() const { return mFrameState[mFrameStateSwitch]; } + const PFXFrameState &getLastFrameState() const { return mFrameState[!mFrameStateSwitch]; } + + void setFrameMatrices( const MatrixF &worldToCamera, const MatrixF &cameraToScreen ); + + // For ManagedSingleton. + static const char* getSingletonName() { return "PostEffectManager"; } +}; + +/// Returns the PostEffectManager singleton. +#define PFXMGR ManagedSingleton::instance() + +#endif // _POSTEFFECTMANAGER_H_ \ No newline at end of file diff --git a/Engine/source/postFx/postEffectVis.cpp b/Engine/source/postFx/postEffectVis.cpp new file mode 100644 index 000000000..bf4e40057 --- /dev/null +++ b/Engine/source/postFx/postEffectVis.cpp @@ -0,0 +1,458 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "postFx/postEffectVis.h" +#include "gui/containers/guiWindowCtrl.h" +#include "gui/controls/guiBitmapCtrl.h" +#include "gui/core/guiCanvas.h" +#include "postFx/postEffectManager.h" +#include "core/module.h" + +ConsoleDoc( + "@class PfxVis\n" + "@brief Singleton class that exposes ConsoleStaticFunctions for debug visualizing PostEffects.\n\n" + "@tsexample\n" + "// Script interface...\n" + "PfxVis::open( PostEffect ) // Multiple PostEffects can be visualized at the same time\n" + "PfxVis::clear() // Clear all visualizer windows\n" + "PfxVis::hide() // Hide all windows (are not destroyed)\n" + "PfxVis::show()\n" + "@endtsexample\n\n" + "@ingroup GFX\n" +); + +MODULE_BEGIN( PostEffectVis ) + + MODULE_INIT + { + ManagedSingleton< PostEffectVis >::createSingleton(); + } + + MODULE_SHUTDOWN + { + ManagedSingleton< PostEffectVis >::deleteSingleton(); + } + +MODULE_END; + + +PostEffectVis::PostEffectVis() + : mContent( NULL ) +{ +} + +PostEffectVis::~PostEffectVis() +{ +} + +void PostEffectVis::open( PostEffect *pfx ) +{ + GuiControl *content = _getContentControl(); + + // If we already have this PostEffect added + // remove it first so we can recreate its controls. + VisVector::iterator itr = mWindows.begin(); + for ( ; itr != mWindows.end(); itr++ ) + { + if ( itr->pfx == pfx ) + { + for ( U32 i = 0; i < TexCount; i++ ) + { + // Deleting the GuiWindowCtrl will automatically also delete + // any child controls we have allocated. + if ( itr->window[i] ) + itr->window[i]->deleteObject(); + } + + mWindows.erase_fast( itr ); + break; + } + } + + // Allocate VisWindow struct. + mWindows.increment(); + VisWindow &window = mWindows.last(); + window.pfx = pfx; + + for ( U32 i = 0; i < TexCount; i++ ) + { + // Only allocate window/bitmaps for input textures that are actually used. + if ( i > Target ) + { + if ( pfx->mTexFilename[i-1].isEmpty() ) + { + window.window[i] = NULL; + window.bmp[i] = NULL; + continue; + } + } + + // Allocate GuiWindowCtrl + GuiWindowCtrl *winCtrl = new GuiWindowCtrl(); + winCtrl->setPosition( Point2I( 50, 50 ) + Point2I( 15, 15 ) * i ); + winCtrl->setExtent( 347, 209 ); + winCtrl->setMinExtent( Point2I( 150, 100 ) ); + winCtrl->setMobility( true, true, true, true, false, false ); + winCtrl->setCanResize( true, true ); + winCtrl->setDataField( StringTable->insert( "closeCommand" ), NULL, "PfxVis::onWindowClosed( $ThisControl );" ); + winCtrl->registerObject(); + + window.window[i] = winCtrl; + + _setDefaultCaption( window, i ); + + // Allocate background GuiBitmapCtrl + GuiBitmapCtrl *bmpCtrl = new GuiBitmapCtrl(); + bmpCtrl->setPosition( 3, 23 ); + bmpCtrl->setSizing( GuiControl::horizResizeWidth, GuiControl::vertResizeHeight ); + bmpCtrl->setExtent( 341, 181 ); + bmpCtrl->setDataField( StringTable->insert( "wrap" ), NULL, "1" ); + bmpCtrl->setBitmap( "core/art/gui/images/transp_grid" ); + bmpCtrl->registerObject(); + winCtrl->addObject( bmpCtrl ); + + // Allocate GuiBitmapCtrl + bmpCtrl = new GuiBitmapCtrl(); + bmpCtrl->setPosition( 3, 23 ); + bmpCtrl->setSizing( GuiControl::horizResizeWidth, GuiControl::vertResizeHeight ); + bmpCtrl->setExtent( 341, 181 ); + bmpCtrl->registerObject(); + winCtrl->addObject( bmpCtrl ); + + window.bmp[i] = bmpCtrl; + + content->addObject( winCtrl ); + } + + // Make sure we visible. + setVisible( true ); +} + +void PostEffectVis::setVisible( bool visible ) +{ + GuiCanvas *canvas = NULL; + if ( !Sim::findObject( "Canvas", canvas ) ) + { + Con::errorf( "PostEffectVis::setVisible, Canvas was not found." ); + return; + } + + GuiControl *content = _getContentControl(); + + if ( visible && !content->isAwake() ) + canvas->pushDialogControl( content, 100 ); + + if ( !visible && content->isAwake() ) + canvas->popDialogControl( content ); +} + +void PostEffectVis::clear() +{ + GuiControl *content = _getContentControl(); + + content->clear(); + mWindows.clear(); +} + +void PostEffectVis::onStartOfFrame() +{ + if ( mWindows.empty() ) + return; + if ( !_getContentControl()->isAwake() ) + return; + + // Restore vis windows to a default state. + // This ensures to users that open PostEffects that are not + // actively being processed are obvious. + + VisVector::iterator itr = mWindows.begin(); + for ( ; itr != mWindows.end(); itr++ ) + { + for ( U32 i = 0; i < TexCount; i++ ) + { + if ( !itr->bmp[i] || itr->pfx->getRenderTime() == PFXTexGenOnDemand ) + continue; + + itr->bmp[i]->setBitmap( NULL ); + _setDefaultCaption( *itr, i ); + } + } +} + +void PostEffectVis::onPFXProcessed( PostEffect *pfx ) +{ + // If we have no windows we can early out before even testing + // isAwake so we avoid creating the content control unnecessarily. + if ( mWindows.empty() ) + return; + + if ( !_getContentControl()->isAwake() ) + return; + + VisVector::iterator itr = mWindows.begin(); + for ( ; itr != mWindows.end(); itr++ ) + { + if ( itr->pfx == pfx ) + { + GuiBitmapCtrl *pBmpCtrl = NULL; + GuiWindowCtrl *pWinCtrl = NULL; + + if ( itr->bmp[Target] != NULL ) + { + pBmpCtrl = itr->bmp[Target]; + pWinCtrl = itr->window[Target]; + + GFXTextureObject *tex; + + if ( pfx->mTargetTex ) + tex = pfx->mTargetTex; + else + tex = PFXMGR->getBackBufferTex(); + + pBmpCtrl->setBitmapHandle( tex ); + + char caption[256]; + char name[256]; + + if ( pfx->getName() == NULL || dStrlen( pfx->getName() ) == 0 ) + dSprintf( name, 256, "(none)" ); + else + dSprintf( name, 256, "%s", pfx->getName() ); + + + if ( tex ) + dSprintf( caption, 256, "%s[%i] target - %s [ %ix%i ]", name, pfx->getId(), pfx->mTargetName.c_str(), tex->getWidth(), tex->getHeight() ); + else + dSprintf( caption, 256, "%s[%i] target", name, pfx->getId() ); + + + pWinCtrl->setDataField( StringTable->insert("text"), NULL, caption ); + } + + for ( U32 i = Input1; i < TexCount; i++ ) + { + if ( itr->bmp[i] == NULL ) + continue; + + pBmpCtrl = itr->bmp[i]; + pWinCtrl = itr->window[i]; + + GFXTextureObject *tex = pfx->mActiveTextures[i-1]; + + pBmpCtrl->setBitmapHandle( tex ); + + char caption[256]; + char name[256]; + + if ( pfx->getName() == NULL || dStrlen( pfx->getName() ) == 0 ) + dSprintf( name, 256, "(none)" ); + else + dSprintf( name, 256, "%s", pfx->getName() ); + + + if ( tex ) + dSprintf( caption, 256, "%s[%i] input%i - %s [ %ix%i ]", name, pfx->getId(), i-1, pfx->mTexFilename[i-1].c_str(), tex->getWidth(), tex->getHeight() ); + else + dSprintf( caption, 256, "%s[%i] input%i - %s", name, pfx->getId(), i-1, pfx->mTexFilename[i-1].c_str() ); + + pWinCtrl->setDataField( StringTable->insert("text"), NULL, caption ); + } + } + } +} + +void PostEffectVis::onWindowClosed( GuiWindowCtrl *ctrl ) +{ + VisVector::iterator itr = mWindows.begin(); + + for ( ; itr != mWindows.end(); itr++ ) + { + for ( U32 i = 0; i < TexCount; i++ ) + { + if ( itr->window[i] == ctrl ) + { + itr->window[i] = NULL; + itr->bmp[i] = NULL; + ctrl->setVisible( false ); + + // Avoid deleting immediately since this happens in response to a + // script callback. + Con::evaluate( "%i.schedule( 1, \"delete\" );" ); + + return; + } + } + } + + Con::errorf( "PostEffectVis::onWindowClosed, passed window (%s) [%i] was found.", StringTable->insert( ctrl->getName() ), ctrl->getId() ); +} + +GuiControl* PostEffectVis::_getContentControl() +{ + if ( mContent == NULL ) + { + GuiCanvas *canvas = NULL; + if ( !Sim::findObject( "Canvas", canvas ) ) + { + AssertFatal( false, "PostEffectVis::_getContentControl, Canvas not found." ); + return NULL; + } + + mContent = new GuiControl(); + mContent->setPosition( 0, 0 ); + mContent->setExtent( 1024, 768 ); + mContent->setDataField( StringTable->insert( "noCursor" ), NULL, "1" ); + mContent->setDataField( StringTable->insert( "profile" ), NULL, "GuiModelessDialogProfile" ); + mContent->registerObject( "PfxVisContent" ); + + canvas->pushDialogControl( mContent, 100 ); + } + + return mContent; +} + +void PostEffectVis::_setDefaultCaption( VisWindow &vis, U32 texIndex ) +{ + PostEffect *pfx = vis.pfx; + GuiWindowCtrl *winCtrl = vis.window[texIndex]; + + if ( texIndex == Target ) + { + char caption[256]; + char name[256]; + + if ( pfx->getName() == NULL || dStrlen( pfx->getName() ) == 0 ) + dSprintf( name, 256, "(none)" ); + else + dSprintf( name, 256, "%s", pfx->getName() ); + + dSprintf( caption, 256, "%s[%i] target [NOT ENABLED]", name, pfx->getId() ); + + winCtrl->setDataField( StringTable->insert("text"), NULL, caption ); + } + else + { + char caption[256]; + char name[256]; + + if ( pfx->getName() == NULL || dStrlen( pfx->getName() ) == 0 ) + dSprintf( name, 256, "(none)" ); + else + dSprintf( name, 256, "%s", pfx->getName() ); + + dSprintf( caption, 256, "%s[%i] input%i - %s [NOT ENABLED]", name, pfx->getId(), texIndex-1, pfx->mTexFilename[texIndex-1].c_str() ); + + winCtrl->setDataField( StringTable->insert("text"), NULL, caption ); + } +} + +static ConsoleDocFragment _PfxVisclear( + "@brief Close all visualization windows.\n\n" + "@tsexample\n" + "PfxVis::clear();" + "@endtsexample\n\n", + "PfxVis", + "void clear();" ); + +ConsoleStaticMethod( PfxVis, clear, void, 1, 1, "()" + "@hide") +{ + PFXVIS->clear(); +} + +static ConsoleDocFragment _PfxVisopen( + "@brief Open visualization windows for all input and target textures.\n\n" + "@param effect Name of the PostEffect to open\n" + "@param clear True to close all visualization windows before opening the effect\n\n" + "@tsexample\n" + "// Multiple PostEffects can be visualized at the same time\n" + "PfxVis::open( PostEffect )\n" + "@endtsexample\n\n", + "PfxVis", + "void open(PostEffect effect, bool clear);" ); + +ConsoleStaticMethod( PfxVis, open, void, 2, 3, "( PostEffect, [bool clear = false] )" + "@hide") +{ + if ( argc == 3 && dAtob( argv[2] ) ) + PFXVIS->clear(); + + PostEffect *pfx; + if ( !Sim::findObject( argv[1], pfx ) ) + { + Con::errorf( "PfxVis::add, argument %s was not a PostEffect", argv[1] ); + return; + } + + PFXVIS->open( pfx ); +} + +static ConsoleDocFragment _PfxVishide( + "@brief Hide all visualization windows (they are not destroyed).\n\n" + "@tsexample\n" + "PfxVis::hide();" + "@endtsexample\n\n", + "PfxVis", + "void hide();" ); + +ConsoleStaticMethod( PfxVis, hide, void, 1, 1, "()" + "@hide") +{ + PFXVIS->setVisible( false ); +} + +static ConsoleDocFragment _PfxVisshow( + "@brief Show all visualization windows.\n\n" + "@tsexample\n" + "PfxVis::show();" + "@endtsexample\n\n", + "PfxVis", + "void show();" ); + +ConsoleStaticMethod( PfxVis, show, void, 1, 1, "()" + "@hide") +{ + PFXVIS->setVisible( true ); +} + +static ConsoleDocFragment _PfxVisonWindowClosed( + "@brief Callback when a visualization window is closed.\n\n" + "@param ctrl Name of the GUI control being closed\n" + "@tsexample\n" + "PfxVis::onWindowClosed( VisWindow )\n" + "@endtsexample\n\n", + "PfxVis", + "void onWindowClosed(GuiWindowCtrl *ctrl);" ); + +ConsoleStaticMethod( PfxVis, onWindowClosed, void, 2, 2, "( GuiWindowCtrl )" + "@hide") +{ + GuiWindowCtrl *ctrl; + if ( !Sim::findObject( argv[1], ctrl ) ) + { + Con::errorf( "PfxVis::onWindowClosed, argument %s was not a GuiWindowCtrl", argv[1] ); + return; + } + + PFXVIS->onWindowClosed( ctrl ); +} \ No newline at end of file diff --git a/Engine/source/postFx/postEffectVis.h b/Engine/source/postFx/postEffectVis.h new file mode 100644 index 000000000..972a7ea78 --- /dev/null +++ b/Engine/source/postFx/postEffectVis.h @@ -0,0 +1,110 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _POSTEFFECTVIS_H_ +#define _POSTEFFECTVIS_H_ + +#ifndef _TSINGLETON_H_ +#include "core/util/tSingleton.h" +#endif + +#ifndef _POST_EFFECT_H_ +#include "postFx/postEffect.h" +#endif + +class GuiWindowCtrl; +class GuiBitmapCtrl; +class GuiControl; + +class PostEffectVis +{ + // Protected constructor. + // Use PFXVIS define to access singleton. + PostEffectVis(); + friend class ManagedSingleton; + +public: + + ~PostEffectVis(); + + /// Open visualization windows for all input and target textures. + void open( PostEffect *pfx ); + + /// Close all visualization windows. + void clear(); + + /// Hide or show all visualization windows. + void setVisible( bool visible ); + + /// Callback from PostEffectManager at the start of a frame. + void onStartOfFrame(); + + /// Callback from PostEffect to update visualization. + void onPFXProcessed( PostEffect *pfx ); + + /// Callback when a visualization window is closed. + void onWindowClosed( GuiWindowCtrl *ctrl ); + +protected: + + /// Get or create the content control, the parent of all visualization windows. + GuiControl* _getContentControl(); + +protected: + + enum TexIndex + { + Target = 0, + Input1, + Input2, + Input3, + Input4, + TexCount + }; + + /// Structure representing a single 'opened' PostEffect + /// including GuiControls for displaying any input/target textures. + struct VisWindow + { + PostEffect *pfx; + GuiWindowCtrl *window[TexCount]; + GuiBitmapCtrl *bmp[TexCount]; + }; + + void _setDefaultCaption( VisWindow &vis, U32 texIndex ); + + typedef Vector< VisWindow > VisVector; + + VisVector mWindows; + + GuiControl *mContent; + +public: + + // For ManagedSingleton. + static const char* getSingletonName() { return "PostEffectVis"; } +}; + +/// Returns the PostEffectVis singleton. +#define PFXVIS ManagedSingleton::instance() + +#endif // _POSTEFFECTVIS_H_ \ No newline at end of file diff --git a/Engine/source/renderInstance/forcedMaterialMeshMgr.cpp b/Engine/source/renderInstance/forcedMaterialMeshMgr.cpp new file mode 100644 index 000000000..4fbe0ffbd --- /dev/null +++ b/Engine/source/renderInstance/forcedMaterialMeshMgr.cpp @@ -0,0 +1,134 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#include "renderInstance/forcedMaterialMeshMgr.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/gfxPrimitiveBuffer.h" +#include "gfx/gfxDebugEvent.h" +#include "materials/sceneData.h" +#include "materials/materialManager.h" +#include "materials/materialDefinition.h" +#include "console/consoleTypes.h" +#include "math/util/matrixSet.h" + +IMPLEMENT_CONOBJECT(ForcedMaterialMeshMgr); + +ConsoleDocClass( ForcedMaterialMeshMgr, + "@brief Basically the same as RenderMeshMgr, but will override the material " + "of the instance. Exists for backwards compatibility, not currently used, soon to be deprecated\n\n" + "@internal" +); + +ForcedMaterialMeshMgr::ForcedMaterialMeshMgr() +{ + mOverrideInstance = NULL; + mOverrideMaterial = NULL; +} + +ForcedMaterialMeshMgr::ForcedMaterialMeshMgr(RenderInstType riType, F32 renderOrder, F32 processAddOrder, BaseMatInstance* overrideMaterial) +: RenderMeshMgr(riType, renderOrder, processAddOrder) +{ + mOverrideInstance = overrideMaterial; + mOverrideMaterial = NULL; +} + +void ForcedMaterialMeshMgr::setOverrideMaterial(BaseMatInstance* overrideMaterial) +{ + SAFE_DELETE(mOverrideInstance); + mOverrideInstance = overrideMaterial; +} + +ForcedMaterialMeshMgr::~ForcedMaterialMeshMgr() +{ + setOverrideMaterial(NULL); +} + +void ForcedMaterialMeshMgr::initPersistFields() +{ + addProtectedField("material", TYPEID< Material >(), Offset(mOverrideMaterial, ForcedMaterialMeshMgr), + &_setOverrideMat, &_getOverrideMat, "Material used to draw all meshes in the render bin."); + + Parent::initPersistFields(); +} + +void ForcedMaterialMeshMgr::render(SceneRenderState * state) +{ + PROFILE_SCOPE(ForcedMaterialMeshMgr_render); + + if(!mOverrideInstance && mOverrideMaterial.isValid()) + { + mOverrideInstance = mOverrideMaterial->createMatInstance(); + mOverrideInstance->init( MATMGR->getDefaultFeatures(), getGFXVertexFormat() ); + } + + // Early out if nothing to draw. + if(!mElementList.size() || !mOverrideInstance) + return; + + GFXDEBUGEVENT_SCOPE(ForcedMaterialMeshMgr_Render, ColorI::RED); + + // Automagically save & restore our viewport and transforms. + GFXTransformSaver saver; + + // init loop data + SceneData sgData; + sgData.init( state ); + + MeshRenderInst *ri = static_cast(mElementList[0].inst); + setupSGData( ri, sgData ); + + while (mOverrideInstance->setupPass(state, sgData)) + { + for( U32 j=0; j(mElementList[j].inst); + if(passRI->primBuff->getPointer()->mPrimitiveArray[passRI->primBuffIndex].numVertices < 1) + continue; + + getRenderPass()->getMatrixSet().setWorld(*passRI->objectToWorld); + getRenderPass()->getMatrixSet().setView(*passRI->worldToCamera); + getRenderPass()->getMatrixSet().setProjection(*passRI->projection); + mOverrideInstance->setTransforms(getRenderPass()->getMatrixSet(), state); + + mOverrideInstance->setBuffers(passRI->vertBuff, passRI->primBuff); + GFX->drawPrimitive( passRI->primBuffIndex ); + } + } +} + +const char* ForcedMaterialMeshMgr::_getOverrideMat( void *object, const char *data ) +{ + ForcedMaterialMeshMgr &mgr = *reinterpret_cast( object ); + if( mgr.mOverrideMaterial.isValid() ) + return mgr.mOverrideMaterial->getIdString(); + else + return "0"; +} + +bool ForcedMaterialMeshMgr::_setOverrideMat( void *object, const char *index, const char *data ) +{ + ForcedMaterialMeshMgr &mgr = *reinterpret_cast( object ); + BaseMatInstance* material; + Sim::findObject( data, material ); + mgr.setOverrideMaterial( material ); + return false; +} + diff --git a/Engine/source/renderInstance/forcedMaterialMeshMgr.h b/Engine/source/renderInstance/forcedMaterialMeshMgr.h new file mode 100644 index 000000000..78e5ff1fb --- /dev/null +++ b/Engine/source/renderInstance/forcedMaterialMeshMgr.h @@ -0,0 +1,55 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _RENDERFORCEDMATMESHMGR_H_ +#define _RENDERFORCEDMATMESHMGR_H_ + +#ifndef _RENDERMESHMGR_H_ +#include "renderInstance/renderMeshMgr.h" +#endif + +class Material; + +/// Basically the same as RenderMeshMgr, but will override the material of the instance. +class ForcedMaterialMeshMgr : public RenderMeshMgr +{ + typedef RenderMeshMgr Parent; +public: + ForcedMaterialMeshMgr(); + ForcedMaterialMeshMgr(RenderInstType riType, F32 renderOrder, F32 processAddOrder, BaseMatInstance* overrideMaterial); + virtual ~ForcedMaterialMeshMgr(); + + void setOverrideMaterial(BaseMatInstance* overrideMaterial); + + // RenderBinManager interface + virtual void render(SceneRenderState * state); + + DECLARE_CONOBJECT(ForcedMaterialMeshMgr); + static void initPersistFields(); +private: + BaseMatInstance* mOverrideInstance; + SimObjectPtr mOverrideMaterial; + + static const char* _getOverrideMat( void* object, const char* data ); + static bool _setOverrideMat( void *object, const char *index, const char *data ); +}; + +#endif diff --git a/Engine/source/renderInstance/renderBinManager.cpp b/Engine/source/renderInstance/renderBinManager.cpp new file mode 100644 index 000000000..5fb05eebf --- /dev/null +++ b/Engine/source/renderInstance/renderBinManager.cpp @@ -0,0 +1,177 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "renderInstance/renderBinManager.h" + +#include "console/consoleTypes.h" +#include "materials/matInstance.h" +#include "scene/sceneManager.h" +#include "console/engineAPI.h" + + +IMPLEMENT_CONOBJECT(RenderBinManager); + + +RenderBinManager::RenderBinManager( const RenderInstType& ritype, F32 renderOrder, F32 processAddOrder ) : + mRenderInstType( ritype ), + mRenderOrder( renderOrder ), + mProcessAddOrder( processAddOrder ), + mRenderPass( NULL ) +{ + VECTOR_SET_ASSOCIATION( mElementList ); + mElementList.reserve( 2048 ); +} + + +ConsoleDocClass( RenderBinManager, + "@brief The abstract base for all render bins.\n\n" + "The render bins are used by the engine as a high level method to order and batch rendering " + "operations.\n" + "@ingroup RenderBin\n" ); + +void RenderBinManager::initPersistFields() +{ + addField( "binType", TypeRealString, Offset(mRenderInstType.mName, RenderBinManager), + "Sets the render bin type which limits what render instances are added to this bin." ); + + addField("renderOrder", TypeF32, Offset(mRenderOrder, RenderBinManager), + "Defines the order for rendering in relation to other bins." ); + + addField("processAddOrder", TypeF32, Offset(mProcessAddOrder, RenderBinManager), + "Defines the order for adding instances in relation to other bins." ); + + Parent::initPersistFields(); +} + +void RenderBinManager::onRemove() +{ + // Tell the render pass to remove us when + // we're being unregistered. + if ( mRenderPass ) + mRenderPass->removeManager( this ); + + Parent::onRemove(); +} + +void RenderBinManager::notifyType( const RenderInstType &type ) +{ + // Avoid duplicate types. + if ( !type.isValid() || + mRenderInstType == type || + mOtherTypes.contains( type ) ) + return; + + mOtherTypes.push_back( type ); + + // Register for the signal if the pass + // has already been assigned. + if ( mRenderPass ) + mRenderPass->getAddSignal(type).notify( this, &RenderBinManager::addElement, mProcessAddOrder ); +} + +void RenderBinManager::setRenderPass( RenderPassManager *rpm ) +{ + if ( mRenderPass ) + { + if ( mRenderInstType.isValid() ) + mRenderPass->getAddSignal(mRenderInstType).remove( this, &RenderBinManager::addElement ); + + for ( U32 i=0; i < mOtherTypes.size(); i++ ) + mRenderPass->getAddSignal(mOtherTypes[i]).remove( this, &RenderBinManager::addElement ); + } + + mRenderPass = rpm; + + if ( mRenderPass ) + { + if ( mRenderInstType.isValid() ) + mRenderPass->getAddSignal(mRenderInstType).notify( this, &RenderBinManager::addElement, mProcessAddOrder ); + + for ( U32 i=0; i < mOtherTypes.size(); i++ ) + mRenderPass->getAddSignal(mOtherTypes[i]).notify( this, &RenderBinManager::addElement, mProcessAddOrder ); + } +} + +void RenderBinManager::addElement( RenderInst *inst ) +{ + internalAddElement(inst); +} + +void RenderBinManager::internalAddElement(RenderInst* inst) +{ + mElementList.increment(); + MainSortElem &elem = mElementList.last(); + elem.inst = inst; + elem.key = elem.key2 = 0; + + elem.key = inst->defaultKey; + elem.key2 = inst->defaultKey2; +} + +void RenderBinManager::clear() +{ + mElementList.clear(); +} + +void RenderBinManager::sort() +{ + dQsort( mElementList.address(), mElementList.size(), sizeof(MainSortElem), cmpKeyFunc); +} + +S32 FN_CDECL RenderBinManager::cmpKeyFunc(const void* p1, const void* p2) +{ + const MainSortElem* mse1 = (const MainSortElem*) p1; + const MainSortElem* mse2 = (const MainSortElem*) p2; + + S32 test1 = S32(mse2->key) - S32(mse1->key); + + return ( test1 == 0 ) ? S32(mse1->key2) - S32(mse2->key2) : test1; +} + +void RenderBinManager::setupSGData( MeshRenderInst *ri, SceneData &data ) +{ + PROFILE_SCOPE( RenderBinManager_setupSGData ); + + // NOTE: We do not reset or clear the scene state + // here as the caller has initialized non-RI members + // himself and we must preserve them. + // + // It also saves a bunch of CPU as this is called for + // every MeshRenderInst in every pass. + + dMemcpy( data.lights, ri->lights, sizeof( data.lights ) ); + data.objTrans = ri->objectToWorld; + data.backBuffTex = ri->backBuffTex; + data.cubemap = ri->cubemap; + data.miscTex = ri->miscTex; + data.reflectTex = ri->reflectTex; + data.lightmap = ri->lightmap; + data.visibility = ri->visibility; + data.materialHint = ri->materialHint; +} + +DefineEngineMethod( RenderBinManager, getBinType, const char*, (),, + "Returns the bin type string." ) +{ + return object->getRenderInstType().getName(); +} diff --git a/Engine/source/renderInstance/renderBinManager.h b/Engine/source/renderInstance/renderBinManager.h new file mode 100644 index 000000000..c906eeaf8 --- /dev/null +++ b/Engine/source/renderInstance/renderBinManager.h @@ -0,0 +1,170 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _RENDERBINMANAGER_H_ +#define _RENDERBINMANAGER_H_ + +#ifndef _CONSOLEOBJECT_H_ +#include "console/consoleObject.h" +#endif +#ifndef _RENDERPASSMANAGER_H_ +#include "renderInstance/renderPassManager.h" +#endif +#ifndef _BASEMATINSTANCE_H_ +#include "materials/baseMatInstance.h" +#endif +#ifndef _UTIL_DELEGATE_H_ +#include "core/util/delegate.h" +#endif + +class SceneRenderState; + + +/// This delegate is used in derived RenderBinManager classes +/// to allow material instances to be overriden. +typedef Delegate MaterialOverrideDelegate; + + +/// The RenderBinManager manages and renders lists of MainSortElem, which +/// is a light wrapper around RenderInst. +class RenderBinManager : public SimObject +{ + typedef SimObject Parent; + + friend class RenderPassManager; + +public: + + RenderBinManager( const RenderInstType& ritype = RenderInstType::Invalid, + F32 renderOrder = 1.0f, + F32 processAddOrder = 1.0f ); + virtual ~RenderBinManager() {} + + // SimObject + void onRemove(); + + virtual void addElement( RenderInst *inst ); + virtual void sort(); + virtual void render( SceneRenderState *state ) {} + virtual void clear(); + + // Manager info + F32 getProcessAddOrder() const { return mProcessAddOrder; } + void setProcessAddOrder(F32 processAddOrder) { mProcessAddOrder = processAddOrder; } + F32 getRenderOrder() const { return mRenderOrder; } + void setRenderOrder(F32 renderOrder) { mRenderOrder = renderOrder; } + + /// Returns the primary render instance type. + const RenderInstType& getRenderInstType() { return mRenderInstType; } + + /// Returns the render pass this bin is registered to. + RenderPassManager* getRenderPass() const { return mRenderPass; } + + /// QSort callback function + static S32 FN_CDECL cmpKeyFunc(const void* p1, const void* p2); + + DECLARE_CONOBJECT(RenderBinManager); + static void initPersistFields(); + + MaterialOverrideDelegate& getMatOverrideDelegate() { return mMatOverrideDelegate; } + +protected: + + struct MainSortElem + { + RenderInst *inst; + U32 key; + U32 key2; + }; + + void setRenderPass( RenderPassManager *rpm ); + + /// Called from derived bins to add additional + /// render instance types to be notified about. + void notifyType( const RenderInstType &type ); + + Vector< MainSortElem > mElementList; // List of our instances + F32 mProcessAddOrder; // Where in the list do we process RenderInstance additions? + F32 mRenderOrder; // Where in the list do we render? + + /// The primary render instance type this bin supports. + RenderInstType mRenderInstType; + + /// The list of additional render instance types + /// this bin wants to process. + Vector mOtherTypes; + + /// The render pass manager this bin is registered with. + RenderPassManager *mRenderPass; + + MaterialOverrideDelegate mMatOverrideDelegate; + + virtual void setupSGData(MeshRenderInst *ri, SceneData &data ); + virtual void internalAddElement(RenderInst* inst); + + /// A inlined helper method for testing if the next + /// MeshRenderInst requires a new batch/pass. + inline bool newPassNeeded( MeshRenderInst *ri, MeshRenderInst* nextRI ) const; + + /// Inlined utility function which gets the material from the + /// RenderInst if available, otherwise, return NULL. + inline BaseMatInstance* getMaterial( RenderInst *inst ) const; + +}; + + +inline bool RenderBinManager::newPassNeeded( MeshRenderInst *ri, MeshRenderInst* nextRI ) const +{ + if ( ri == nextRI ) + return false; + + // We can depend completely on the state hint to check + // for changes in the material as it uniquely identifies it. + if ( ri->matInst->getStateHint() != nextRI->matInst->getStateHint() ) + return true; + + if ( ri->vertBuff != nextRI->vertBuff || + ri->primBuff != nextRI->primBuff || + ri->prim != nextRI->prim || + ri->primBuffIndex != nextRI->primBuffIndex || + + // NOTE: Keep an eye on this... should we find a more + // optimal test for light set changes? + // + dMemcmp( ri->lights, nextRI->lights, sizeof( ri->lights ) ) != 0 ) + + return true; + + return false; +} + +inline BaseMatInstance* RenderBinManager::getMaterial( RenderInst *inst ) const +{ + if ( inst->type == RenderPassManager::RIT_Mesh || + inst->type == RenderPassManager::RIT_Interior || + inst->type == RenderPassManager::RIT_Decal || + inst->type == RenderPassManager::RIT_Translucent ) + return static_cast(inst)->matInst; + + return NULL; +} + +#endif // _RENDERBINMANAGER_H_ diff --git a/Engine/source/renderInstance/renderFormatChanger.cpp b/Engine/source/renderInstance/renderFormatChanger.cpp new file mode 100644 index 000000000..bba53ed53 --- /dev/null +++ b/Engine/source/renderInstance/renderFormatChanger.cpp @@ -0,0 +1,375 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "renderInstance/renderFormatChanger.h" + +#include "console/consoleTypes.h" +#include "gfx/gfxStringEnumTranslate.h" +#include "gfx/gfxTextureManager.h" +#include "gfx/gfxDebugEvent.h" +#include "postFx/postEffect.h" +#include "postFx/postEffectManager.h" + +extern ColorI gCanvasClearColor; + +IMPLEMENT_CONOBJECT(RenderFormatToken); + +ConsoleDocClass( RenderFormatToken, + "@brief Used to change the render target format when rendering in AL.\n\n" + + "RenderFormatToken is an implementation which changes the format of the " + "back buffer and/or the depth buffer.\n\n" + + "The RenderPassStateBin manager changes the rendering state associated with " + "this token. In stock Torque 3D, a single example exists in the " + "way of AL_FormatToken (found in renderManager.cs). In that script file, all the " + "render managers are intialized, and a single RenderFormatToken is used. This " + "implementation basically exists to ensure Advanced Lighting works with MSAA.\n\n" + + "The actions for this token toggle the format of the back/depth buffers " + "and it lets you specify a custom shader to \"copy\" the data so it can " + "be reformatted or altered. This is done through the variables copyEffect and " + "resolveEffect (which are post processes just like fog or glow)\n\n" + + "@tsexample\n" + "// This token, and the associated render managers, ensure " + "that driver MSAA does not get used for Advanced Lighting renders.\n" + "// The 'AL_FormatResolve' PostEffect copies the result to the backbuffer.\n" + "new RenderFormatToken(AL_FormatToken)\n" + "{\n" + " enabled = \"false\";\n\n" + " format = \"GFXFormatR8G8B8A8\";\n" + " depthFormat = \"GFXFormatD24S8\";\n" + " aaLevel = 0; // -1 = match backbuffer\n\n" + " // The contents of the back buffer before this format token is executed\n" + " // is provided in $inTex\n" + " copyEffect = \"AL_FormatCopy\";\n\n" + " // The contents of the render target created by this format token is\n" + " // provided in $inTex\n" + " resolveEffect = \"AL_FormatCopy\";\n" + "};\n" + "@endtsexample\n\n" + + "@see RenderPassToken\n\n" + "@see RenderPassStateBin\n" + "@see game/core/scripts/client/renderManager.cs\n" + + "@ingroup GFX\n" +); + +RenderFormatToken::RenderFormatToken() + : Parent(), + mFCState(FTSDisabled), + mColorFormat(GFXFormat_COUNT), + mDepthFormat(GFXFormat_COUNT), + mTargetUpdatePending(true), + mTargetChainIdx(0), + mTargetSize(Point2I::Zero), + mTargetAALevel(GFXTextureManager::AA_MATCH_BACKBUFFER), + mCopyPostEffect(NULL), + mResolvePostEffect(NULL) +{ + GFXDevice::getDeviceEventSignal().notify(this, &RenderFormatToken::_handleGFXEvent); + GFXTextureManager::addEventDelegate(this, &RenderFormatToken::_onTextureEvent); +} + +RenderFormatToken::~RenderFormatToken() +{ + GFXTextureManager::removeEventDelegate(this, &RenderFormatToken::_onTextureEvent); + GFXDevice::getDeviceEventSignal().remove(this, &RenderFormatToken::_handleGFXEvent); + + _teardownTargets(); +} + +void RenderFormatToken::process(SceneRenderState *state, RenderPassStateBin *callingBin) +{ + switch(mFCState) + { + case FTSWaiting: + { + GFXDEBUGEVENT_SCOPE_EX(RFT_Waiting, ColorI::BLUE, avar("[%s Activate] (%s)", getName(), GFXStringTextureFormat[mColorFormat])); + mFCState = FTSActive; + + mTarget.setViewport( GFX->getViewport() ); + + // Update targets + _updateTargets(); + + // If we have a copy PostEffect then get the active backbuffer copy + // now before we swap the render targets. + GFXTexHandle curBackBuffer; + if(mCopyPostEffect.isValid()) + curBackBuffer = PFXMGR->getBackBufferTex(); + + // Push target + GFX->pushActiveRenderTarget(); + GFX->setActiveRenderTarget(mTargetChain[mTargetChainIdx]); + + // Set viewport + GFX->setViewport( mTarget.getViewport() ); + + // Clear + GFX->clear(GFXClearTarget | GFXClearZBuffer | GFXClearStencil, gCanvasClearColor, 1.0f, 0); + + // Set active z target on render pass + if(mTargetDepthStencilTexture[mTargetChainIdx].isValid()) + { + if(callingBin->getRenderPass()->getDepthTargetTexture() != GFXTextureTarget::sDefaultDepthStencil) + mStoredPassZTarget = callingBin->getRenderPass()->getDepthTargetTexture(); + else + mStoredPassZTarget = NULL; + + callingBin->getRenderPass()->setDepthTargetTexture(mTargetDepthStencilTexture[mTargetChainIdx]); + } + + // Run the PostEffect which copies data into the new target. + if ( mCopyPostEffect.isValid() ) + mCopyPostEffect->process( state, curBackBuffer, &mTarget.getViewport() ); + } + break; + + case FTSActive: + { + GFXDEBUGEVENT_SCOPE_EX(RFT_Active, ColorI::BLUE, avar("[%s Deactivate]", getName())); + mFCState = FTSComplete; + + // Pop target + AssertFatal(GFX->getActiveRenderTarget() == mTargetChain[mTargetChainIdx], "Render target stack went wrong somewhere"); + mTargetChain[mTargetChainIdx]->resolve(); + GFX->popActiveRenderTarget(); + mTarget.setTexture( mTargetColorTexture[mTargetChainIdx] ); + + // This is the GFX viewport when we were first processed. + GFX->setViewport( mTarget.getViewport() ); + + // Restore active z-target + if(mTargetDepthStencilTexture[mTargetChainIdx].isValid()) + { + callingBin->getRenderPass()->setDepthTargetTexture(mStoredPassZTarget.getPointer()); + mStoredPassZTarget = NULL; + } + + // Run the PostEffect which copies data to the backbuffer + if(mResolvePostEffect.isValid()) + { + // Need to create a texhandle here, since inOutTex gets assigned during process() + GFXTexHandle inOutTex = mTargetColorTexture[mTargetChainIdx]; + mResolvePostEffect->process( state, inOutTex, &mTarget.getViewport() ); + } + } + break; + + case FTSComplete: + AssertFatal(false, "process() called on a RenderFormatToken which was already complete."); + // fall through + case FTSDisabled: + break; + } +} + +void RenderFormatToken::reset() +{ + AssertFatal(mFCState != FTSActive, "RenderFormatToken still active during reset()!"); + if(mFCState != FTSDisabled) + mFCState = FTSWaiting; +} + +void RenderFormatToken::_updateTargets() +{ + if ( GFX->getActiveRenderTarget() == NULL ) + return; + + const Point2I &rtSize = GFX->getActiveRenderTarget()->getSize(); + + if ( rtSize.x <= mTargetSize.x && + rtSize.y <= mTargetSize.y && + !mTargetUpdatePending ) + return; + + mTargetSize = rtSize; + mTargetUpdatePending = false; + mTargetChainIdx = 0; + + for( U32 i = 0; i < TargetChainLength; i++ ) + { + if( !mTargetChain[i] ) + mTargetChain[i] = GFX->allocRenderToTextureTarget(); + + // Update color target + if(mColorFormat != GFXFormat_COUNT) + { + mTargetColorTexture[i].set( rtSize.x, rtSize.y, mColorFormat, + &GFXDefaultRenderTargetProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ), + 1, mTargetAALevel ); + mTargetChain[i]->attachTexture( GFXTextureTarget::Color0, mTargetColorTexture[i] ); + } + + mTargetChain[i]->attachTexture( GFXTextureTarget::Color0, mTargetColorTexture[i] ); + + + // Update depth target + if(mDepthFormat != GFXFormat_COUNT) + { + mTargetDepthStencilTexture[i].set( rtSize.x, rtSize.y, mDepthFormat, + &GFXDefaultZTargetProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ), + 1, mTargetAALevel ); + } + + mTargetChain[i]->attachTexture( GFXTextureTarget::DepthStencil, mTargetDepthStencilTexture[i] ); + } +} + +void RenderFormatToken::_teardownTargets() +{ + mTarget.release(); + + for(int i = 0; i < TargetChainLength; i++) + { + mTargetColorTexture[i] = NULL; + mTargetDepthStencilTexture[i] = NULL; + mTargetChain[i] = NULL; + } +} + +bool RenderFormatToken::_setFmt( void *object, const char *index, const char *data ) +{ + // Flag update pending + reinterpret_cast( object )->mTargetUpdatePending = true; + + // Allow console system to assign value + return true; +} + +const char* RenderFormatToken::_getCopyPostEffect( void* object, const char* data ) +{ + RenderFormatToken* token = reinterpret_cast< RenderFormatToken* >( object ); + if( token->mCopyPostEffect.isValid() ) + return token->mCopyPostEffect->getIdString(); + return "0"; +} + +const char* RenderFormatToken::_getResolvePostEffect( void* object, const char* data ) +{ + RenderFormatToken* token = reinterpret_cast< RenderFormatToken* >( object ); + if( token->mResolvePostEffect.isValid() ) + return token->mResolvePostEffect->getIdString(); + return "0"; +} + +bool RenderFormatToken::_setCopyPostEffect( void* object, const char* index, const char* data ) +{ + RenderFormatToken* token = reinterpret_cast< RenderFormatToken* >( object ); + PostEffect* effect; + Sim::findObject( data, effect ); + token->mCopyPostEffect = effect; + return false; +} + +bool RenderFormatToken::_setResolvePostEffect( void* object, const char* index, const char* data ) +{ + RenderFormatToken* token = reinterpret_cast< RenderFormatToken* >( object ); + PostEffect* effect; + Sim::findObject( data, effect ); + token->mResolvePostEffect = effect; + return false; +} + +void RenderFormatToken::enable( bool enabled /*= true*/ ) +{ + AssertFatal(mFCState != FTSActive, "RenderFormatToken is active, cannot change state now!"); + + if(enabled) + mFCState = FTSWaiting; + else + mFCState = FTSDisabled; +} + +bool RenderFormatToken::isEnabled() const +{ + return (mFCState != FTSDisabled); +} + +void RenderFormatToken::initPersistFields() +{ + addProtectedField("format", TypeGFXFormat, Offset(mColorFormat, RenderFormatToken), &_setFmt, &defaultProtectedGetFn, + "Sets the color buffer format for this token."); + + addProtectedField("depthFormat", TypeGFXFormat, Offset(mDepthFormat, RenderFormatToken), &_setFmt, &defaultProtectedGetFn, + "Sets the depth/stencil buffer format for this token."); + + addProtectedField("copyEffect", TYPEID(), Offset(mCopyPostEffect, RenderFormatToken), + &_setCopyPostEffect, &_getCopyPostEffect, + "This PostEffect will be run when the render target is changed to the format specified " + "by this token. It is used to copy/format data into the token rendertarget"); + + addProtectedField("resolveEffect", TYPEID(), Offset(mResolvePostEffect, RenderFormatToken), + &_setResolvePostEffect, &_getResolvePostEffect, + "This PostEffect will be run when the render target is changed back to the format " + "active prior to this token. It is used to copy/format data from the token rendertarget to the backbuffer."); + + addField("aaLevel", TypeS32, Offset(mTargetAALevel, RenderFormatToken), + "Anti-ailiasing level for the this token. 0 disables, -1 uses adapter default."); + + Parent::initPersistFields(); +} + + +bool RenderFormatToken::_handleGFXEvent(GFXDevice::GFXDeviceEventType event_) +{ + if ( event_ == GFXDevice::deStartOfFrame ) + { + mTargetChainIdx++; + if ( mTargetChainIdx >= TargetChainLength ) + mTargetChainIdx = 0; + } + + return true; +} + +void RenderFormatToken::_onTextureEvent( GFXTexCallbackCode code ) +{ + if(code == GFXZombify) + { + _teardownTargets(); + mTargetUpdatePending = true; + } +} + +bool RenderFormatToken::onAdd() +{ + if(!Parent::onAdd()) + return false; + + mTarget.registerWithName( getName() ); + mTarget.setSamplerState( GFXSamplerStateDesc::getClampPoint() ); + + return true; +} + +void RenderFormatToken::onRemove() +{ + mTarget.unregister(); + mTarget.release(); + + Parent::onRemove(); +} \ No newline at end of file diff --git a/Engine/source/renderInstance/renderFormatChanger.h b/Engine/source/renderInstance/renderFormatChanger.h new file mode 100644 index 000000000..f57463051 --- /dev/null +++ b/Engine/source/renderInstance/renderFormatChanger.h @@ -0,0 +1,99 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _RENDERFORMATCHANGER_H_ +#define _RENDERFORMATCHANGER_H_ + +#ifndef _RENDERPASSSTATETOKEN_H_ +#include "renderInstance/renderPassStateToken.h" +#endif +#ifndef _MATTEXTURETARGET_H_ +#include "materials/matTextureTarget.h" +#endif + +class PostEffect; + + +class RenderFormatToken : public RenderPassStateToken +{ + typedef RenderPassStateToken Parent; + +public: + enum FormatTokenState + { + FTSDisabled, + FTSWaiting, + FTSActive, + FTSComplete, + }; + + const static U32 TargetChainLength = 1; + +protected: + + FormatTokenState mFCState; + GFXFormat mColorFormat; + GFXFormat mDepthFormat; + bool mTargetUpdatePending; + U32 mTargetChainIdx; + Point2I mTargetSize; + S32 mTargetAALevel; + SimObjectPtr mCopyPostEffect; + SimObjectPtr mResolvePostEffect; + + NamedTexTarget mTarget; + + GFXTexHandle mTargetColorTexture[TargetChainLength]; + GFXTexHandle mTargetDepthStencilTexture[TargetChainLength]; + GFXTextureTargetRef mTargetChain[TargetChainLength]; + + GFXTexHandle mStoredPassZTarget; + + void _updateTargets(); + void _teardownTargets(); + + void _onTextureEvent( GFXTexCallbackCode code ); + virtual bool _handleGFXEvent(GFXDevice::GFXDeviceEventType event); + + static bool _setFmt( void *object, const char *index, const char *data ); + static const char* _getCopyPostEffect( void* object, const char* data ); + static const char* _getResolvePostEffect( void* object, const char* data ); + static bool _setCopyPostEffect( void* object, const char* index, const char* data ); + static bool _setResolvePostEffect( void* object, const char* index, const char* data ); + +public: + + DECLARE_CONOBJECT(RenderFormatToken); + static void initPersistFields(); + virtual bool onAdd(); + virtual void onRemove(); + + RenderFormatToken(); + virtual ~RenderFormatToken(); + + virtual void process(SceneRenderState *state, RenderPassStateBin *callingBin); + virtual void reset(); + virtual void enable(bool enabled = true); + virtual bool isEnabled() const; +}; + +#endif // _RENDERFORMATCHANGER_H_ diff --git a/Engine/source/renderInstance/renderGlowMgr.cpp b/Engine/source/renderInstance/renderGlowMgr.cpp new file mode 100644 index 000000000..58fd8843c --- /dev/null +++ b/Engine/source/renderInstance/renderGlowMgr.cpp @@ -0,0 +1,223 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "renderInstance/renderGlowMgr.h" + +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "materials/sceneData.h" +#include "materials/matInstance.h" +#include "materials/materialFeatureTypes.h" +#include "materials/processedMaterial.h" +#include "postFx/postEffect.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/gfxDebugEvent.h" +#include "math/util/matrixSet.h" + +IMPLEMENT_CONOBJECT( RenderGlowMgr ); + + +ConsoleDocClass( RenderGlowMgr, + "@brief A render bin for the glow pass.\n\n" + "When the glow buffer PostEffect is enabled this bin gathers mesh render " + "instances with glow materials and renders them to the #glowbuffer offscreen " + "render target.\n\n" + "This render target is then used by the 'GlowPostFx' PostEffect to blur and " + "render the glowing portions of the screen.\n\n" + "@ingroup RenderBin\n" ); + +const MatInstanceHookType RenderGlowMgr::GlowMaterialHook::Type( "Glow" ); + + +RenderGlowMgr::GlowMaterialHook::GlowMaterialHook( BaseMatInstance *matInst ) + : mGlowMatInst( NULL ) +{ + mGlowMatInst = (MatInstance*)matInst->getMaterial()->createMatInstance(); + mGlowMatInst->getFeaturesDelegate().bind( &GlowMaterialHook::_overrideFeatures ); + mGlowMatInst->init( matInst->getRequestedFeatures(), + matInst->getVertexFormat() ); +} + +RenderGlowMgr::GlowMaterialHook::~GlowMaterialHook() +{ + SAFE_DELETE( mGlowMatInst ); +} + +void RenderGlowMgr::GlowMaterialHook::_overrideFeatures( ProcessedMaterial *mat, + U32 stageNum, + MaterialFeatureData &fd, + const FeatureSet &features ) +{ + // If this isn't a glow pass... then add the glow mask feature. + if ( mat->getMaterial() && + !mat->getMaterial()->mGlow[stageNum] ) + fd.features.addFeature( MFT_GlowMask ); + + // Don't allow fog or HDR encoding on + // the glow materials. + fd.features.removeFeature( MFT_Fog ); + fd.features.removeFeature( MFT_HDROut ); +} + +RenderGlowMgr::RenderGlowMgr() + : RenderTexTargetBinManager( RenderPassManager::RIT_Mesh, + 1.0f, + 1.0f, + GFXFormatR8G8B8A8, + Point2I( 512, 512 ) ) +{ + notifyType( RenderPassManager::RIT_Interior ); + notifyType( RenderPassManager::RIT_Decal ); + notifyType( RenderPassManager::RIT_Translucent ); + + mNamedTarget.registerWithName( "glowbuffer" ); + mTargetSizeType = WindowSize; +} + +RenderGlowMgr::~RenderGlowMgr() +{ +} + +PostEffect* RenderGlowMgr::getGlowEffect() +{ + if ( !mGlowEffect ) + mGlowEffect = dynamic_cast( Sim::findObject( "GlowPostFx" ) ); + + return mGlowEffect; +} + +bool RenderGlowMgr::isGlowEnabled() +{ + return getGlowEffect() && getGlowEffect()->isEnabled(); +} + +void RenderGlowMgr::addElement( RenderInst *inst ) +{ + // Skip out if we don't have the glow post + // effect enabled at this time. + if ( !isGlowEnabled() ) + return; + + // TODO: We need to get the scene state here in a more reliable + // manner so we can skip glow in a non-diffuse render pass. + //if ( !mParentManager->getSceneManager()->getSceneState()->isDiffusePass() ) + //return RenderBinManager::arSkipped; + + // Skip it if we don't have a glowing material. + BaseMatInstance *matInst = getMaterial( inst ); + if ( !matInst || !matInst->hasGlow() ) + return; + + internalAddElement(inst); +} + +void RenderGlowMgr::render( SceneRenderState *state ) +{ + PROFILE_SCOPE( RenderGlowMgr_Render ); + + if ( !isGlowEnabled() ) + return; + + const U32 binSize = mElementList.size(); + + // If this is a non-diffuse pass or we have no objects to + // render then tell the effect to skip rendering. + if ( !state->isDiffusePass() || binSize == 0 ) + { + getGlowEffect()->setSkip( true ); + return; + } + + GFXDEBUGEVENT_SCOPE( RenderGlowMgr_Render, ColorI::GREEN ); + + GFXTransformSaver saver; + + // Tell the superclass we're about to render, preserve contents + const bool isRenderingToTarget = _onPreRender( state, true ); + + // Clear all the buffers to black. + GFX->clear( GFXClearTarget, ColorI::BLACK, 1.0f, 0); + + // Restore transforms + MatrixSet &matrixSet = getRenderPass()->getMatrixSet(); + matrixSet.restoreSceneViewProjection(); + + // init loop data + SceneData sgData; + sgData.init( state, SceneData::GlowBin ); + + for( U32 j=0; j(mElementList[j].inst); + + setupSGData( ri, sgData ); + + BaseMatInstance *mat = ri->matInst; + GlowMaterialHook *hook = mat->getHook(); + if ( !hook ) + { + hook = new GlowMaterialHook( ri->matInst ); + ri->matInst->addHook( hook ); + } + BaseMatInstance *glowMat = hook->getMatInstance(); + + U32 matListEnd = j; + + while( glowMat && glowMat->setupPass( state, sgData ) ) + { + U32 a; + for( a=j; a(mElementList[a].inst); + + if ( newPassNeeded( ri, passRI ) ) + break; + + matrixSet.setWorld(*passRI->objectToWorld); + matrixSet.setView(*passRI->worldToCamera); + matrixSet.setProjection(*passRI->projection); + glowMat->setTransforms(matrixSet, state); + + glowMat->setSceneInfo(state, sgData); + glowMat->setBuffers(passRI->vertBuff, passRI->primBuff); + + if ( passRI->prim ) + GFX->drawPrimitive( *passRI->prim ); + else + GFX->drawPrimitive( passRI->primBuffIndex ); + } + matListEnd = a; + setupSGData( ri, sgData ); + } + + // force increment if none happened, otherwise go to end of batch + j = ( j == matListEnd ) ? j+1 : matListEnd; + } + + // Finish up. + if ( isRenderingToTarget ) + _onPostRender(); + + // Make sure the effect is gonna render. + getGlowEffect()->setSkip( false ); +} diff --git a/Engine/source/renderInstance/renderGlowMgr.h b/Engine/source/renderInstance/renderGlowMgr.h new file mode 100644 index 000000000..7b92218c1 --- /dev/null +++ b/Engine/source/renderInstance/renderGlowMgr.h @@ -0,0 +1,89 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _RENDERGLOWMGR_H_ +#define _RENDERGLOWMGR_H_ + +#ifndef _TEXTARGETBIN_MGR_H_ +#include "renderInstance/renderTexTargetBinManager.h" +#endif + + +class PostEffect; + + +/// +class RenderGlowMgr : public RenderTexTargetBinManager +{ + typedef RenderTexTargetBinManager Parent; + +public: + + RenderGlowMgr(); + virtual ~RenderGlowMgr(); + + /// Returns the glow post effect. + PostEffect* getGlowEffect(); + + /// Returns true if the glow post effect is + /// enabled and the glow buffer should be updated. + bool isGlowEnabled(); + + // RenderBinManager + virtual void addElement( RenderInst *inst ); + virtual void render( SceneRenderState *state ); + + // ConsoleObject + DECLARE_CONOBJECT( RenderGlowMgr ); + +protected: + + class GlowMaterialHook : public MatInstanceHook + { + public: + + GlowMaterialHook( BaseMatInstance *matInst ); + virtual ~GlowMaterialHook(); + + virtual BaseMatInstance *getMatInstance() { return mGlowMatInst; } + + virtual const MatInstanceHookType& getType() const { return Type; } + + /// Our material hook type. + static const MatInstanceHookType Type; + + protected: + + static void _overrideFeatures( ProcessedMaterial *mat, + U32 stageNum, + MaterialFeatureData &fd, + const FeatureSet &features ); + + BaseMatInstance *mGlowMatInst; + }; + + SimObjectPtr mGlowEffect; + +}; + + +#endif // _RENDERGLOWMGR_H_ diff --git a/Engine/source/renderInstance/renderImposterMgr.cpp b/Engine/source/renderInstance/renderImposterMgr.cpp new file mode 100644 index 000000000..c225108b5 --- /dev/null +++ b/Engine/source/renderInstance/renderImposterMgr.cpp @@ -0,0 +1,351 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "renderInstance/renderImposterMgr.h" + +#include "scene/sceneManager.h" +#include "T3D/gameBase/gameConnection.h" +#include "materials/shaderData.h" +#include "lighting/lightManager.h" +#include "lighting/lightInfo.h" +#include "scene/sceneRenderState.h" +#include "gfx/gfxDebugEvent.h" +#include "renderInstance/renderPrePassMgr.h" +#include "gfx/gfxTransformSaver.h" +#include "console/consoleTypes.h" +#include "gfx/util/screenspace.h" +#include "math/util/matrixSet.h" +#include "materials/materialManager.h" +#include "materials/materialFeatureTypes.h" + +/* +GFXImplementVertexFormat( ImposterCorner ) +{ + addElement( "ImposterCorner", GFXDeclType_Float, 4 ); +}; +*/ + +const RenderInstType RenderImposterMgr::RIT_Imposter( "Imposter" ); +const RenderInstType RenderImposterMgr::RIT_ImposterBatch( "ImposterBatch" ); + + +U32 RenderImposterMgr::smRendered = 0.0f; +U32 RenderImposterMgr::smBatches = 0.0f; +U32 RenderImposterMgr::smDrawCalls = 0.0f; +U32 RenderImposterMgr::smPolyCount = 0.0f; +U32 RenderImposterMgr::smRTChanges = 0.0f; + + +IMPLEMENT_CONOBJECT(RenderImposterMgr); + +ConsoleDocClass( RenderImposterMgr, + "@brief A render bin for batch rendering imposters.\n\n" + "This render bin gathers imposter render instances and renders them in large " + "batches.\n\n" + "You can type 'metrics( imposter )' in the console to see rendering statistics.\n\n" + "@ingroup RenderBin\n" ); + + +RenderImposterMgr::RenderImposterMgr( F32 renderOrder, F32 processAddOrder ) + : RenderBinManager( RIT_Imposter, renderOrder, processAddOrder ) +{ + notifyType( RIT_ImposterBatch ); + RenderPrePassMgr::getRenderSignal().notify( this, &RenderImposterMgr::_renderPrePass ); +} + +void RenderImposterMgr::initPersistFields() +{ + GFXDevice::getDeviceEventSignal().notify( &RenderImposterMgr::_clearStats ); + + Con::addVariable( "$ImposterStats::rendered", TypeS32, &smRendered, "@internal" ); + Con::addVariable( "$ImposterStats::batches", TypeS32, &smBatches, "@internal" ); + Con::addVariable( "$ImposterStats::drawCalls", TypeS32, &smDrawCalls, "@internal" ); + Con::addVariable( "$ImposterStats::polyCount", TypeS32, &smPolyCount, "@internal" ); + Con::addVariable( "$ImposterStats::rtChanges", TypeS32, &smRTChanges, "@internal" ); + + Parent::initPersistFields(); +} + +RenderImposterMgr::~RenderImposterMgr() +{ + RenderPrePassMgr::getRenderSignal().remove( this, &RenderImposterMgr::_renderPrePass ); + + mIB = NULL; +} + +void RenderImposterMgr::render( SceneRenderState *state ) +{ + PROFILE_SCOPE( RenderImposterMgr_Render ); + + if ( !mElementList.size() ) + return; + + GFXDEBUGEVENT_SCOPE( RenderImposterMgr_Render, ColorI::RED ); + + _innerRender( state, NULL ); +} + +bool RenderImposterMgr::_clearStats( GFXDevice::GFXDeviceEventType type ) +{ + if ( type == GFXDevice::deStartOfFrame ) + { + smRendered = 0.0f; + smBatches = 0.0f; + smDrawCalls = 0.0f; + smPolyCount = 0.0f; + smRTChanges = 0.0f; + } + + return true; +} + +void RenderImposterMgr::_renderPrePass( const SceneRenderState *state, RenderPrePassMgr *prePassBin, bool startPrePass ) +{ + PROFILE_SCOPE( RenderImposterMgr_RenderPrePass ); + + if ( !mElementList.size() || !startPrePass ) + return; + + GFXDEBUGEVENT_SCOPE( RenderImposterMgr_RenderPrePass, ColorI::RED ); + + _innerRender( state, prePassBin ); +} + +void RenderImposterMgr::_innerRender( const SceneRenderState *state, RenderPrePassMgr *prePassBin ) +{ + PROFILE_SCOPE( RenderImposterMgr_InnerRender ); + + // Capture the GFX stats for this render. + GFXDeviceStatistics stats; + stats.start( GFX->getDeviceStatistics() ); + + GFXTransformSaver saver; + + // Restore transforms + MatrixSet &matrixSet = getRenderPass()->getMatrixSet(); + matrixSet.restoreSceneViewProjection(); + matrixSet.setWorld( MatrixF::Identity ); + + // Setup the large static index buffer for rendering the imposters. + if ( !mIB.isValid() ) + { + // Setup a static index buffer for rendering. + mIB.set( GFX, smImposterBatchSize * 6, 0, GFXBufferTypeStatic ); + U16 *idxBuff; + mIB.lock(&idxBuff, NULL, NULL, NULL); + for ( U32 i=0; i < smImposterBatchSize; i++ ) + { + // + // The vertex pattern in the VB for each + // imposter is as follows... + // + // 0----1 + // |\ | + // | \ | + // | \ | + // | \| + // 3----2 + // + // We setup the index order below to ensure + // sequental, cache friendly, access. + // + U32 offset = i * 4; + idxBuff[i*6+0] = 0 + offset; + idxBuff[i*6+1] = 1 + offset; + idxBuff[i*6+2] = 2 + offset; + idxBuff[i*6+3] = 2 + offset; + idxBuff[i*6+4] = 3 + offset; + idxBuff[i*6+5] = 0 + offset; + } + mIB.unlock(); + } + + /* + if ( !mCornerVB.isValid() ) + { + // Setup a static vertex buffer for the corner index for each imposter state. + mCornerVB.set( GFX, smImposterBatchSize * 4, GFXBufferTypeStatic ); + ImposterCorner *corner = mCornerVB.lock( 0 ); + for ( U32 i=0; i < smImposterBatchSize; i++ ) + { + corner->corner = 0; corner++; + corner->corner = 1; corner++; + corner->corner = 2; corner++; + corner->corner = 3; corner++; + } + mCornerVB.unlock(); + } + */ + + // Set the buffers here once. + GFX->setPrimitiveBuffer( mIB ); + + // Batch up the imposters into the buffer. These + // are already sorted by texture, to minimize switches + // so just batch them up and render as they come. + + ImposterState* statePtr = NULL; + U32 stateCount; + ImposterBaseRenderInst *ri; + ImposterRenderInst *imposter; + ImposterBatchRenderInst *batch; + const U32 binSize = mElementList.size(); + BaseMatInstance *setupMat, *currMat; + GFXVertexBufferHandle vb; + + // TODO: We could maybe do better with lights when forward + // rendering the imposters. Just pass a light list with it + // and do some simple tests to break the batch when the light + // list changes. + + SceneData sgData; + sgData.init( state, prePassBin ? SceneData::PrePassBin : SceneData::RegularBin ); + sgData.lights[0] = LIGHTMGR->getDefaultLight(); + + // TODO: I should rework this loop to generate the VB first then + // do all the material passes... should be easier to read and faster. + // + // Also consider making this store two element lists... one for + // batches and one for individual imposters. + // + + for ( U32 i=0; i < binSize; ) + { + currMat = static_cast( mElementList[i].inst )->mat; + setupMat = prePassBin ? prePassBin->getPrePassMaterial( currMat ) : currMat; + + // TODO: Fix MatInstance to take a const SceneRenderState! + while ( setupMat->setupPass( (SceneRenderState*)state, sgData ) ) + { + setupMat->setSceneInfo( (SceneRenderState*)state, sgData ); + setupMat->setTransforms( matrixSet, (SceneRenderState*)state ); + + for ( ; i < binSize; ) + { + ri = static_cast( mElementList[i].inst ); + + // NOTE: Its safe to compare matinstances here instead of + // the state hint because imposters all share the same + // material instances.... if this changes revise. + if ( ri->mat != currMat ) + break; + + // Ok if this is a batch then we can just fire off the draw now. + if ( ri->type == RIT_ImposterBatch ) + { + batch = static_cast( ri ); + + GFX->setVertexBuffer( batch->vertBuff->getPointer() ); + GFX->drawPrimitive( GFXTriangleList, 0, batch->vertBuff->getPointer()->mNumVerts / 3 ); + + i++; + continue; + } + + // This wasn't a batch so build up all the single imposters into + // a dynamic batch and render it. + + statePtr = mBuffer; + stateCount = 0; + + // Loop for each individual imposter. + for ( ; i < binSize; i++ ) + { + if ( mElementList[i].inst->type == RIT_ImposterBatch ) + break; + + imposter = static_cast( mElementList[i].inst ); + + // Stop the loop if the material changed. + if ( imposter->mat != currMat ) + break; + + ++smRendered; + + // If we're out of vb space then draw what we got. + if ( stateCount + 1 >= smImposterBatchSize ) + { + smBatches++; + + vb.set( GFX, stateCount*4, GFXBufferTypeVolatile ); + dMemcpy( vb.lock(), mBuffer, stateCount * 4 * sizeof( ImposterState ) ); + vb.unlock(); + + //GFX->setVertexBuffer( mCornerVB, 0, stateCount * 4 ); + GFX->setVertexBuffer( vb ); + ///GFX->setVertexFormat( &mImposterVertDecl ); + + GFX->drawIndexedPrimitive( GFXTriangleList, 0, 0, stateCount * 4, 0, stateCount * 2 ); + statePtr = mBuffer; + stateCount = 0; + } + + // Setup the imposter state. + *statePtr = imposter->state; + statePtr->corner = 0; + statePtr++; + + *statePtr = imposter->state; + statePtr->corner = 1; + statePtr++; + + *statePtr = imposter->state; + statePtr->corner = 2; + statePtr++; + + *statePtr = imposter->state; + statePtr->corner = 3; + statePtr++; + + stateCount++; + + } // for ( ; i < binSize; i++ ) + + // Any remainder to dump? + if ( stateCount > 0 ) + { + smBatches++; + + vb.set( GFX, stateCount*4, GFXBufferTypeVolatile ); + dMemcpy( vb.lock(), mBuffer, stateCount * 4 * sizeof( ImposterState ) ); + vb.unlock(); + + //GFX->setVertexBuffer( mCornerVB, 0, stateCount * 4 ); + GFX->setVertexBuffer( vb ); + ///GFX->setVertexFormat( &mImposterVertDecl ); + + GFX->drawIndexedPrimitive( GFXTriangleList, 0, 0, stateCount * 4, 0, stateCount * 2 ); + } + + } // for( U32 i=0; i < binSize; ) + + } // while ( currMat->setupPass( (SceneRenderState*)state, sgData ) ) + + } // for ( U32 i=0; i < binSize; ) + + // Capture the GFX stats for this render. + stats.end( GFX->getDeviceStatistics() ); + smDrawCalls += stats.mDrawCalls; + smPolyCount += stats.mPolyCount; + smRTChanges += stats.mRenderTargetChanges; +} diff --git a/Engine/source/renderInstance/renderImposterMgr.h b/Engine/source/renderInstance/renderImposterMgr.h new file mode 100644 index 000000000..5ec3c542e --- /dev/null +++ b/Engine/source/renderInstance/renderImposterMgr.h @@ -0,0 +1,139 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _IMPOSTERRENDERMGR_H_ +#define _IMPOSTERRENDERMGR_H_ + +#ifndef _RENDERBINMANAGER_H_ +#include "renderInstance/renderBinManager.h" +#endif +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif +#ifndef _MATTEXTURETARGET_H_ +#include "materials/matTextureTarget.h" +#endif +#ifndef _TSLASTDETAIL_H_ +#include "ts/tsLastDetail.h" +#endif + +class TSLastDetail; +class GFXTextureObject; +class RenderPrePassMgr; +struct ImposterRenderInst; + + +/* +GFXDeclareVertexFormat( ImposterCorner ) +{ + /// billboard corner index + float corner; +}; +*/ + +/// This is a special render manager for processing single +/// billboard imposters typically generated by the tsLastDetail +/// class. It tries to render them in large batches with as +/// few state changes as possible. For an example of use see +/// TSLastDetail::render(). +class RenderImposterMgr : public RenderBinManager +{ +protected: + + typedef RenderBinManager Parent; + + static const U32 smImposterBatchSize = 1000; + + static U32 smRendered; + static U32 smBatches; + static U32 smDrawCalls; + static U32 smPolyCount; + static U32 smRTChanges; + + ImposterState mBuffer[smImposterBatchSize*4]; + + GFXPrimitiveBufferHandle mIB; + //GFXVertexBufferHandle mCornerVB; + + void _innerRender( const SceneRenderState *state, RenderPrePassMgr *prePassBin ); + + void _renderPrePass( const SceneRenderState *state, RenderPrePassMgr *prePassBin, bool startPrePass ); + + static bool _clearStats( GFXDevice::GFXDeviceEventType type ); + +public: + + static const RenderInstType RIT_Imposter; + static const RenderInstType RIT_ImposterBatch; + + RenderImposterMgr( F32 renderOrder = 1.0f, F32 processAddOrder = 1.0f ); + virtual ~RenderImposterMgr(); + + // ConsoleObject + DECLARE_CONOBJECT(RenderImposterMgr); + static void initPersistFields(); + + // RenderBinManager + virtual void render( SceneRenderState *state ); +}; + + +/// This is a shared base render instance type TSLastDetail imposters. +/// @see TSLastDetail +/// @see RenderImposterMgr +struct ImposterBaseRenderInst : public RenderInst +{ + /// The material for this imposter. + BaseMatInstance *mat; +}; + + +/// This is a render instance for a single imposter. +struct ImposterRenderInst : public ImposterBaseRenderInst +{ + /// The imposter state. + ImposterState state; + + /// Helper for setting this instance to a default state. + void clear() + { + dMemset( this, 0, sizeof( ImposterRenderInst ) ); + type = RenderImposterMgr::RIT_Imposter; + } +}; + + +/// This is a render instance for a cached multiple imposter batch. +struct ImposterBatchRenderInst : public ImposterBaseRenderInst +{ + /// The pre-built vertex buffer batch of imposters. + GFXVertexBufferHandleBase *vertBuff; + + /// Helper for setting this instance to a default state. + void clear() + { + dMemset( this, 0, sizeof( ImposterBatchRenderInst ) ); + type = RenderImposterMgr::RIT_ImposterBatch; + } +}; + +#endif // _TSIMPOSTERRENDERMGR_H_ diff --git a/Engine/source/renderInstance/renderMeshMgr.cpp b/Engine/source/renderInstance/renderMeshMgr.cpp new file mode 100644 index 000000000..0689a23d5 --- /dev/null +++ b/Engine/source/renderInstance/renderMeshMgr.cpp @@ -0,0 +1,261 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "renderInstance/renderMeshMgr.h" + +#include "console/consoleTypes.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/gfxPrimitiveBuffer.h" +#include "materials/sceneData.h" +#include "materials/processedMaterial.h" +#include "materials/materialManager.h" +#include "scene/sceneRenderState.h" +#include "gfx/gfxDebugEvent.h" +#include "math/util/matrixSet.h" + + +IMPLEMENT_CONOBJECT(RenderMeshMgr); + +ConsoleDocClass( RenderMeshMgr, + "@brief A render bin for mesh rendering.\n\n" + "This is the primary render bin in Torque which does most of the " + "work of rendering DTS shapes and arbitrary mesh geometry. It knows " + "how to render mesh instances using materials and supports hardware mesh " + "instancing.\n\n" + "@ingroup RenderBin\n" ); + + +RenderMeshMgr::RenderMeshMgr() +: RenderBinManager(RenderPassManager::RIT_Mesh, 1.0f, 1.0f) +{ +} + +RenderMeshMgr::RenderMeshMgr(RenderInstType riType, F32 renderOrder, F32 processAddOrder) + : RenderBinManager(riType, renderOrder, processAddOrder) +{ +} + +void RenderMeshMgr::init() +{ + GFXStateBlockDesc d; + + d.cullDefined = true; + d.cullMode = GFXCullCCW; + d.samplersDefined = true; + d.samplers[0] = GFXSamplerStateDesc::getWrapLinear(); + + mNormalSB = GFX->createStateBlock(d); + + d.cullMode = GFXCullCW; + mReflectSB = GFX->createStateBlock(d); +} + +void RenderMeshMgr::initPersistFields() +{ + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- +// add element +//----------------------------------------------------------------------------- +void RenderMeshMgr::addElement( RenderInst *inst ) +{ + // If this instance is translucent handle it in RenderTranslucentMgr + if (inst->translucentSort) + return; + + AssertFatal( inst->defaultKey != 0, "RenderMeshMgr::addElement() - Got null sort key... did you forget to set it?" ); + + internalAddElement(inst); +} + +//----------------------------------------------------------------------------- +// render +//----------------------------------------------------------------------------- +void RenderMeshMgr::render(SceneRenderState * state) +{ + PROFILE_SCOPE(RenderMeshMgr_render); + + // Early out if nothing to draw. + if(!mElementList.size()) + return; + + + GFXDEBUGEVENT_SCOPE( RenderMeshMgr_Render, ColorI::GREEN ); + + // Automagically save & restore our viewport and transforms. + GFXTransformSaver saver; + + // Restore transforms + MatrixSet &matrixSet = getRenderPass()->getMatrixSet(); + matrixSet.restoreSceneViewProjection(); + + // init loop data + GFXTextureObject *lastLM = NULL; + GFXCubemap *lastCubemap = NULL; + GFXTextureObject *lastReflectTex = NULL; + GFXTextureObject *lastMiscTex = NULL; + + SceneData sgData; + sgData.init( state ); + + U32 binSize = mElementList.size(); + + for( U32 j=0; j(mElementList[j].inst); + + setupSGData( ri, sgData ); + BaseMatInstance *mat = ri->matInst; + + // If we have an override delegate then give it a + // chance to swap the material with another. + if ( mMatOverrideDelegate ) + { + mat = mMatOverrideDelegate( mat ); + if ( !mat ) + { + j++; + continue; + } + } + + if( !mat ) + mat = MATMGR->getWarningMatInstance(); + + + U32 matListEnd = j; + lastMiscTex = sgData.miscTex; + U32 a; + + while( mat && mat->setupPass(state, sgData ) ) + { + for( a=j; a(mElementList[a].inst); + + // Check to see if we need to break this batch. + if ( newPassNeeded( ri, passRI ) || + lastMiscTex != passRI->miscTex ) + { + lastLM = NULL; + break; + } + + matrixSet.setWorld(*passRI->objectToWorld); + matrixSet.setView(*passRI->worldToCamera); + matrixSet.setProjection(*passRI->projection); + mat->setTransforms(matrixSet, state); + + setupSGData( passRI, sgData ); + mat->setSceneInfo( state, sgData ); + + // If we're instanced then don't render yet. + if ( mat->isInstanced() ) + { + // Let the material increment the instance buffer, but + // break the batch if it runs out of room for more. + if ( !mat->stepInstance() ) + { + a++; + break; + } + + continue; + } + + // TODO: This could proably be done in a cleaner way. + // + // This section of code is dangerous, it overwrites the + // lightmap values in sgData. This could be a problem when multiple + // render instances use the same multi-pass material. When + // the first pass is done, setupPass() is called again on + // the material, but the lightmap data has been changed in + // sgData to the lightmaps in the last renderInstance rendered. + + // This section sets the lightmap data for the current batch. + // For the first iteration, it sets the same lightmap data, + // however the redundancy will be caught by GFXDevice and not + // actually sent to the card. This is done for simplicity given + // the possible condition mentioned above. Better to set always + // than to get bogged down into special case detection. + //------------------------------------- + bool dirty = false; + + // set the lightmaps if different + if( passRI->lightmap && passRI->lightmap != lastLM ) + { + sgData.lightmap = passRI->lightmap; + lastLM = passRI->lightmap; + dirty = true; + } + + // set the cubemap if different. + if ( passRI->cubemap != lastCubemap ) + { + sgData.cubemap = passRI->cubemap; + lastCubemap = passRI->cubemap; + dirty = true; + } + + if ( passRI->reflectTex != lastReflectTex ) + { + sgData.reflectTex = passRI->reflectTex; + lastReflectTex = passRI->reflectTex; + dirty = true; + } + + if ( dirty ) + mat->setTextureStages( state, sgData ); + + // Setup the vertex and index buffers. + mat->setBuffers( passRI->vertBuff, passRI->primBuff ); + + // Render this sucker. + if ( passRI->prim ) + GFX->drawPrimitive( *passRI->prim ); + else + GFX->drawPrimitive( passRI->primBuffIndex ); + } + + // Draw the instanced batch. + if ( mat->isInstanced() ) + { + // Sets the buffers including the instancing stream. + mat->setBuffers( ri->vertBuff, ri->primBuff ); + + // Render the instanced stream. + if ( ri->prim ) + GFX->drawPrimitive( *ri->prim ); + else + GFX->drawPrimitive( ri->primBuffIndex ); + } + + matListEnd = a; + } + + // force increment if none happened, otherwise go to end of batch + j = ( j == matListEnd ) ? j+1 : matListEnd; + } +} + diff --git a/Engine/source/renderInstance/renderMeshMgr.h b/Engine/source/renderInstance/renderMeshMgr.h new file mode 100644 index 000000000..fa0d3cb2a --- /dev/null +++ b/Engine/source/renderInstance/renderMeshMgr.h @@ -0,0 +1,54 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _RENDERMESHMGR_H_ +#define _RENDERMESHMGR_H_ + +#ifndef _RENDERBINMANAGER_H_ +#include "renderInstance/renderBinManager.h" +#endif + +//************************************************************************** +// RenderMeshMgr +//************************************************************************** +class RenderMeshMgr : public RenderBinManager +{ + typedef RenderBinManager Parent; +public: + RenderMeshMgr(); + RenderMeshMgr(RenderInstType riType, F32 renderOrder, F32 processAddOrder); + + // RenderBinManager interface + virtual void init(); + virtual void render(SceneRenderState * state); + virtual void addElement( RenderInst *inst ); + + // ConsoleObject interface + static void initPersistFields(); + DECLARE_CONOBJECT(RenderMeshMgr); +protected: + GFXStateBlockRef mNormalSB; + GFXStateBlockRef mReflectSB; + + void construct(); +}; + +#endif // _RENDERMESHMGR_H_ diff --git a/Engine/source/renderInstance/renderObjectMgr.cpp b/Engine/source/renderInstance/renderObjectMgr.cpp new file mode 100644 index 000000000..6f79f9128 --- /dev/null +++ b/Engine/source/renderInstance/renderObjectMgr.cpp @@ -0,0 +1,75 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#include "renderObjectMgr.h" +#include "console/consoleTypes.h" +#include "scene/sceneObject.h" + +IMPLEMENT_CONOBJECT(RenderObjectMgr); + +ConsoleDocClass( RenderObjectMgr, + "@brief A render bin which uses object callbacks for rendering.\n\n" + "This render bin gathers object render instances and calls its delegate " + "method to perform rendering. It is used infrequently for specialized " + "scene objects which perform custom rendering.\n\n" + "@ingroup RenderBin\n" ); + + +RenderObjectMgr::RenderObjectMgr() +: RenderBinManager(RenderPassManager::RIT_Object, 1.0f, 1.0f) +{ + mOverrideMat = NULL; +} + +RenderObjectMgr::RenderObjectMgr(RenderInstType riType, F32 renderOrder, F32 processAddOrder) + : RenderBinManager(riType, renderOrder, processAddOrder) +{ + mOverrideMat = NULL; +} + +void RenderObjectMgr::initPersistFields() +{ + Parent::initPersistFields(); +} + +void RenderObjectMgr::setOverrideMaterial(BaseMatInstance* overrideMat) +{ + mOverrideMat = overrideMat; +} + +//----------------------------------------------------------------------------- +// render objects +//----------------------------------------------------------------------------- +void RenderObjectMgr::render( SceneRenderState *state ) +{ + PROFILE_SCOPE(RenderObjectMgr_render); + + // Early out if nothing to draw. + if(!mElementList.size()) + return; + + for( U32 i=0; i(mElementList[i].inst); + if ( ri->renderDelegate ) + ri->renderDelegate( ri, state, mOverrideMat ); + } +} \ No newline at end of file diff --git a/Engine/source/renderInstance/renderObjectMgr.h b/Engine/source/renderInstance/renderObjectMgr.h new file mode 100644 index 000000000..3686fe93e --- /dev/null +++ b/Engine/source/renderInstance/renderObjectMgr.h @@ -0,0 +1,51 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _RENDEROBJECTMGR_H_ +#define _RENDEROBJECTMGR_H_ + +#ifndef _RENDERBINMANAGER_H_ +#include "renderInstance/renderBinManager.h" +#endif + +//************************************************************************** +// RenderObjectMgr +//************************************************************************** +class RenderObjectMgr : public RenderBinManager +{ + typedef RenderBinManager Parent; +public: + RenderObjectMgr(); + RenderObjectMgr(RenderInstType riType, F32 renderOrder, F32 processAddOrder); + + virtual void setOverrideMaterial(BaseMatInstance* overrideMat); + + // RenderBinMgr + virtual void render(SceneRenderState * state); + + // ConsoleObject + static void initPersistFields(); + DECLARE_CONOBJECT(RenderObjectMgr); +protected: + BaseMatInstance* mOverrideMat; +}; + +#endif // _RENDEROBJECTMGR_H_ \ No newline at end of file diff --git a/Engine/source/renderInstance/renderOcclusionMgr.cpp b/Engine/source/renderInstance/renderOcclusionMgr.cpp new file mode 100644 index 000000000..4d9d32bfe --- /dev/null +++ b/Engine/source/renderInstance/renderOcclusionMgr.cpp @@ -0,0 +1,254 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "renderOcclusionMgr.h" + +#include "console/consoleTypes.h" +#include "scene/sceneObject.h" +#include "gfx/gfxOcclusionQuery.h" +#include "gfx/gfxDrawUtil.h" +#include "gfx/gfxTransformSaver.h" +#include "math/util/sphereMesh.h" +#include "gfx/gfxDebugEvent.h" + + +IMPLEMENT_CONOBJECT(RenderOcclusionMgr); + +ConsoleDocClass( RenderOcclusionMgr, + "@brief A render bin which renders occlusion query requests.\n\n" + "This render bin gathers occlusion query render instances and renders them. " + "It is currently used by light flares and ShapeBase reflection cubemaps.\n\n" + "You can type '$RenderOcclusionMgr::debugRender = true' in the console to " + "see debug rendering of the occlusion geometry.\n\n" + "@ingroup RenderBin\n" ); + + +bool RenderOcclusionMgr::smDebugRender = false; + +RenderOcclusionMgr::RenderOcclusionMgr() +: RenderBinManager(RenderPassManager::RIT_Occluder, 1.0f, 1.0f) +{ + mOverrideMat = NULL; + mSpherePrimCount = 0; +} + +RenderOcclusionMgr::RenderOcclusionMgr(RenderInstType riType, F32 renderOrder, F32 processAddOrder) +: RenderBinManager(riType, renderOrder, processAddOrder) +{ + mOverrideMat = NULL; +} + +static const Point3F cubePoints[8] = +{ + Point3F(-0.5, -0.5, -0.5), Point3F(-0.5, -0.5, 0.5), Point3F(-0.5, 0.5, -0.5), Point3F(-0.5, 0.5, 0.5), + Point3F( 0.5, -0.5, -0.5), Point3F( 0.5, -0.5, 0.5), Point3F( 0.5, 0.5, -0.5), Point3F( 0.5, 0.5, 0.5) +}; + +static const U32 cubeFaces[6][4] = +{ + { 0, 4, 6, 2 }, { 0, 2, 3, 1 }, { 0, 1, 5, 4 }, + { 3, 2, 6, 7 }, { 7, 6, 4, 5 }, { 3, 7, 5, 1 } +}; + +void RenderOcclusionMgr::init() +{ + GFXStateBlockDesc d; + + d.setBlend( false ); + d.cullDefined = true; + d.cullMode = GFXCullCCW; + d.setZReadWrite( true, false ); + + mDebugSB = GFX->createStateBlock(d); + + d.setColorWrites( false, false, false, false ); + + mNormalSB = GFX->createStateBlock(d); + + d.setZReadWrite( false, false ); + + mTestSB = GFX->createStateBlock(d); + + mBoxBuff.set( GFX, 36, GFXBufferTypeStatic ); + GFXVertexPC *verts = mBoxBuff.lock(); + + U32 vertexIndex = 0; + U32 idx; + for(int i = 0; i < 6; i++) + { + idx = cubeFaces[i][0]; + verts[vertexIndex].point = cubePoints[idx]; + verts[vertexIndex].color.set( 1,0,1,1 ); + vertexIndex++; + + idx = cubeFaces[i][1]; + verts[vertexIndex].point = cubePoints[idx]; + verts[vertexIndex].color.set( 1,0,1,1 ); + vertexIndex++; + + idx = cubeFaces[i][3]; + verts[vertexIndex].point = cubePoints[idx]; + verts[vertexIndex].color.set( 1,0,1,1 ); + vertexIndex++; + + idx = cubeFaces[i][1]; + verts[vertexIndex].point = cubePoints[idx]; + verts[vertexIndex].color.set( 1,0,1,1 ); + vertexIndex++; + + idx = cubeFaces[i][3]; + verts[vertexIndex].point = cubePoints[idx]; + verts[vertexIndex].color.set( 1,0,1,1 ); + vertexIndex++; + + idx = cubeFaces[i][2]; + verts[vertexIndex].point = cubePoints[idx]; + verts[vertexIndex].color.set( 1,0,1,1 ); + vertexIndex++; + } + + mBoxBuff.unlock(); + + SphereMesh sphere; + const SphereMesh::TriangleMesh *sphereMesh = sphere.getMesh(1); + mSpherePrimCount = sphereMesh->numPoly; + mSphereBuff.set( GFX, mSpherePrimCount * 3, GFXBufferTypeStatic ); + verts = mSphereBuff.lock(); + vertexIndex = 0; + + for ( S32 i = 0; i < mSpherePrimCount; i++ ) + { + verts[vertexIndex].point = sphereMesh->poly[i].pnt[0]; + verts[vertexIndex].color.set( 1,0,1,1 ); + vertexIndex++; + + verts[vertexIndex].point = sphereMesh->poly[i].pnt[1]; + verts[vertexIndex].color.set( 1,0,1,1 ); + vertexIndex++; + + verts[vertexIndex].point = sphereMesh->poly[i].pnt[2]; + verts[vertexIndex].color.set( 1,0,1,1 ); + vertexIndex++; + } + mSphereBuff.unlock(); +} + +void RenderOcclusionMgr::consoleInit() +{ + Con::addVariable( "$RenderOcclusionMgr::debugRender", TypeBool, &RenderOcclusionMgr::smDebugRender, + "@brief A debugging feature which renders the occlusion volumes to the scene.\n" + "@see RenderOcclusionMgr\n" + "@ingroup RenderBin\n" ); +} + +void RenderOcclusionMgr::initPersistFields() +{ + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- +// render objects +//----------------------------------------------------------------------------- +void RenderOcclusionMgr::render( SceneRenderState *state ) +{ + PROFILE_SCOPE(RenderOcclusionMgr_render); + + // Early out if nothing to draw. + if ( !mElementList.size() ) + return; + + GFXDEBUGEVENT_SCOPE(RenderOcclusionMgr_Render, ColorI::BLUE); + + if ( mNormalSB.isNull() ) + init(); + + GFX->disableShaders(); + GFX->setupGenericShaders( GFXDevice::GSColor ); + + + OccluderRenderInst *firstEl = static_cast(mElementList[0].inst); + + if ( firstEl->isSphere ) + GFX->setVertexBuffer( mSphereBuff ); + else + GFX->setVertexBuffer( mBoxBuff ); + + bool wasSphere = firstEl->isSphere; + + + for( U32 i=0; i(mElementList[i].inst); + + AssertFatal( ri->query != NULL, "RenderOcclusionMgr::render, OcclusionRenderInst has NULL GFXOcclusionQuery" ); + + if ( ri->isSphere != wasSphere ) + { + if ( ri->isSphere ) + GFX->setVertexBuffer( mSphereBuff ); + else + GFX->setVertexBuffer( mBoxBuff ); + + wasSphere = ri->isSphere; + } + + GFX->pushWorldMatrix(); + + MatrixF xfm( *ri->orientation ); + xfm.setPosition( ri->position ); + xfm.scale( ri->scale ); + + //GFXTransformSaver saver; + GFX->multWorld( xfm ); + + if ( smDebugRender ) + GFX->setStateBlock( mDebugSB ); + else + GFX->setStateBlock( mNormalSB ); + + ri->query->begin(); + + if ( wasSphere ) + GFX->drawPrimitive( GFXTriangleList, 0, mSpherePrimCount ); + else + GFX->drawPrimitive( GFXTriangleList, 0, 12 ); + + ri->query->end(); + + if ( ri->query2 ) + { + GFX->setStateBlock( mTestSB ); + + ri->query2->begin(); + + if ( wasSphere ) + GFX->drawPrimitive( GFXTriangleList, 0, mSpherePrimCount ); + else + GFX->drawPrimitive( GFXTriangleList, 0, 12 ); + + ri->query2->end(); + } + + GFX->popWorldMatrix(); + } +} \ No newline at end of file diff --git a/Engine/source/renderInstance/renderOcclusionMgr.h b/Engine/source/renderInstance/renderOcclusionMgr.h new file mode 100644 index 000000000..bcdefdffa --- /dev/null +++ b/Engine/source/renderInstance/renderOcclusionMgr.h @@ -0,0 +1,61 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _RENDEROCCLUSIONMGR_H_ +#define _RENDEROCCLUSIONMGR_H_ + +#ifndef _RENDERBINMANAGER_H_ +#include "renderInstance/renderBinManager.h" +#endif + +//************************************************************************** +// RenderOcclusionMgr +//************************************************************************** +class RenderOcclusionMgr : public RenderBinManager +{ + typedef RenderBinManager Parent; +public: + RenderOcclusionMgr(); + RenderOcclusionMgr(RenderInstType riType, F32 renderOrder, F32 processAddOrder); + + // RenderOcclusionMgr + virtual void init(); + virtual void render(SceneRenderState * state); + + // ConsoleObject + static void consoleInit(); + static void initPersistFields(); + DECLARE_CONOBJECT(RenderOcclusionMgr); + +protected: + BaseMatInstance* mOverrideMat; + GFXStateBlockRef mNormalSB; + GFXStateBlockRef mTestSB; + + GFXStateBlockRef mDebugSB; + static bool smDebugRender; + + GFXVertexBufferHandle mBoxBuff; + GFXVertexBufferHandle mSphereBuff; + U32 mSpherePrimCount; +}; + +#endif // _RENDEROCCLUSIONMGR_H_ \ No newline at end of file diff --git a/Engine/source/renderInstance/renderParticleMgr.cpp b/Engine/source/renderInstance/renderParticleMgr.cpp new file mode 100644 index 000000000..76c3ec2ff --- /dev/null +++ b/Engine/source/renderInstance/renderParticleMgr.cpp @@ -0,0 +1,806 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "renderInstance/renderParticleMgr.h" +#include "renderInstance/renderPrePassMgr.h" +#include "scene/sceneManager.h" +#include "scene/sceneObject.h" +#include "scene/sceneRenderState.h" +#include "gfx/gfxPrimitiveBuffer.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/gfxDebugEvent.h" +#include "materials/shaderData.h" +#include "materials/sceneData.h" +#include "materials/matInstance.h" +#include "gfx/util/screenspace.h" +#include "gfx/gfxDrawUtil.h" +#include "collision/clippedPolyList.h" + +static const Point4F cubePoints[9] = +{ + Point4F(-0.5, -0.5, -0.5, 1.0f), Point4F(-0.5, -0.5, 0.5, 1.0f), Point4F(-0.5, 0.5, -0.5, 1.0f), Point4F(-0.5, 0.5, 0.5, 1.0f), + Point4F( 0.5, -0.5, -0.5, 1.0f), Point4F( 0.5, -0.5, 0.5, 1.0f), Point4F( 0.5, 0.5, -0.5, 1.0f), Point4F( 0.5, 0.5, 0.5, 1.0f) +}; + +GFXImplementVertexFormat( CompositeQuadVert ) +{ + addElement( "COLOR", GFXDeclType_Color ); +} + +IMPLEMENT_CONOBJECT(RenderParticleMgr); + + +ConsoleDocClass( RenderParticleMgr, + "@brief A render bin which renders particle geometry.\n\n" + "This render bin gathers particle render instances, sorts, and renders them. " + "It is currently used by ParticleEmitter and LightFlareData.\n\n" + "@ingroup RenderBin\n" ); + + +const RenderInstType RenderParticleMgr::RIT_Particles("ParticleSystem"); + +// TODO: Replace these once they are supported via options +const bool RenderToParticleTarget = true; +const bool RenderToSingleTarget = true; + +RenderParticleMgr::RenderParticleMgr() +: Parent( RenderParticleMgr::RIT_Particles, + 1.0f, + 1.0f, + GFXFormatR8G8B8A8, + Point2I( Parent::DefaultTargetSize, Parent::DefaultTargetSize), + RenderToParticleTarget ? Parent::DefaultTargetChainLength : 0 ), + mParticleShader( NULL ) +{ + // Render particles at 1/4 resolution + mTargetSizeType = WindowSizeScaled; + mTargetScale.set(0.25f, 0.25f); + + // We use the target chain like a texture pool, not like a swap chain + if(!RenderToSingleTarget) + setTargetChainLength(5); + else + mOffscreenSystems.setSize(1); + + LightManager::smActivateSignal.notify( this, &RenderParticleMgr::_onLMActivate ); +} + +RenderParticleMgr::~RenderParticleMgr() +{ + LightManager::smActivateSignal.remove( this, &RenderParticleMgr::_onLMActivate ); +} + +void RenderParticleMgr::setTargetChainLength( const U32 chainLength ) +{ + Parent::setTargetChainLength(chainLength); + + if(!RenderToSingleTarget) + mOffscreenSystems.setSize(chainLength); +} + +void RenderParticleMgr::addElement( RenderInst *inst ) +{ + ParticleRenderInst *pri = reinterpret_cast(inst); + + // If this system isn't waiting for an offscreen draw, skip it + if( pri->systemState != PSS_AwaitingOffscreenDraw ) + return; + + // If offscreen rendering isn't enabled, set to high-res, and skip + if(!mOffscreenRenderEnabled) + { + pri->systemState = PSS_AwaitingHighResDraw; + return; + } + + // Assign a target index + RectF screenRect; + S32 chainIndex = -1; + if(RenderToSingleTarget) + { + pri->targetIndex = 0; + screenRect.point.set(-1.0f, -1.0f); + screenRect.extent.set(2.0f, 2.0f); + + mElementList.setSize(1); + } + else + { + + // If we can't fit this into the offscreen systems, skip it, it will render + // high resolution + // + // TODO: Improve this once we are grouping systems + if( mTargetChainIdx == OffscreenPoolSize ) + return; + + // Transform bounding box into screen space + const static PlaneF planes[] = { + PlaneF(Point3F( 1.0f, 0.0f, 0.0f), Point3F(-1.0f, 0.0f, 0.0f)), + PlaneF(Point3F(-1.0f, 0.0f, 0.0f), Point3F( 1.0f, 0.0f, 0.0f)), + + PlaneF(Point3F( 0.0f, 1.0f, 0.0f), Point3F( 0.0f, -1.0f, 0.0f)), + PlaneF(Point3F( 0.0f, -1.0f, 0.0f), Point3F( 0.0f, 1.0f, 0.0f)), + + PlaneF(Point3F( 0.0f, 0.0f, 0.0f), Point3F( 0.0f, 0.0f, 1.0f)), + PlaneF(Point3F( 0.0f, 0.0f, 1.0f), Point3F( 0.0f, 0.0f, -1.0f)), + }; + const static dsize_t numPlanes = sizeof(planes) / sizeof(PlaneF); + + // Set up a clipper + ClippedPolyList screenClipper; + screenClipper.setBaseTransform(MatrixF::Identity); + screenClipper.setTransform(&MatrixF::Identity, Point3F::One); + TORQUE_UNUSED(numPlanes); + + Point4F tempPt(0.0f, 0.0f, 0.0f, 1.0f); + pri->bbModelViewProj->mul(tempPt); + tempPt = tempPt / tempPt.w; + + for(int i = 0; i < 1; i++) + { + screenClipper.mPlaneList.push_back(planes[i]); + screenClipper.mPlaneList.last() += tempPt.asPoint3F(); + } + + Box3F screenSpaceBoundingBox; + screenSpaceBoundingBox.minExtents = Point3F::Zero; + screenSpaceBoundingBox.maxExtents = Point3F::Zero; + + for(int i = 0; i < 8; i++) + { + tempPt = cubePoints[i]; + pri->bbModelViewProj->mul(tempPt); + tempPt = tempPt / tempPt.w; + + screenSpaceBoundingBox.maxExtents.setMax(tempPt.asPoint3F()); + screenSpaceBoundingBox.minExtents.setMin(tempPt.asPoint3F()); + } + + screenClipper.addBox(screenSpaceBoundingBox); + + screenClipper.cullUnusedVerts(); + //screenClipper.triangulate(); + + // Empty vertex list? Skip! + if(screenClipper.mVertexList.empty()) + { + pri->systemState = PSS_AwaitingHighResDraw; + return; + } + + Point2F minExtents(0.0f, 0.0f), maxExtents(0.0f, 0.0f); + for(ClippedPolyList::VertexList::const_iterator itr = screenClipper.mVertexList.begin(); + itr != screenClipper.mVertexList.end(); itr++) + { + minExtents.x = getMin(minExtents.x, (*itr).point.x); + minExtents.y = getMin(minExtents.y, (*itr).point.y); + maxExtents.x = getMax(maxExtents.x, (*itr).point.x); + maxExtents.y = getMax(maxExtents.y, (*itr).point.y); + } + screenRect.set( minExtents, maxExtents - minExtents ); + + // Check the size of the system on screen. If it is small, it won't + // be eating fillrate anyway, so just draw it high-resolution. + // The value it checks against is one I found from experimentation, + // not anything really meaningful. + if( screenRect.extent.x < 0.35f || screenRect.extent.y < 0.35f ) + { + pri->systemState = PSS_AwaitingHighResDraw; + return; + } + + pri->targetIndex = mTargetChainIdx; + chainIndex = mTargetChainIdx; + mTargetChainIdx++; + + // TODO: Rewrite this... + mElementList.increment(); + } + + // Set up system entry + OffscreenSystemEntry &systemEntry = mOffscreenSystems[pri->targetIndex]; + + systemEntry.screenRect = screenRect; + systemEntry.targetChainIdx = chainIndex; + systemEntry.pInstances.push_back(pri); + + // TODO: Rewrite this block + // Assign proper values to sort element + MainSortElem& elem = mElementList.last(); + elem.inst = reinterpret_cast(&systemEntry); + elem.key = *((U32*)&inst->sortDistSq); + elem.key2 = inst->defaultKey; + + // TODO: [re]move this block + systemEntry.clipMatrix.identity(); + if(!RenderToSingleTarget) + { + // Construct crop matrix + Point3F scale( getMax(2.0f / systemEntry.screenRect.extent.x, 1.0f), + getMax(2.0f / systemEntry.screenRect.extent.y, 1.0f), + 1.0f); + + Point3F offset((systemEntry.screenRect.point.x + systemEntry.screenRect.extent.x * 0.5f) * scale.x, + (systemEntry.screenRect.point.y + systemEntry.screenRect.extent.y * 0.5f) * scale.y, + 0.0f); + + //systemEntry.clipMatrix.scale(scale); + //systemEntry.clipMatrix.setPosition(-offset); + } + + // The translucent mgr will also pick up particles, and will call this class + // to composiste the particle systems back into the main scene +} + +void RenderParticleMgr::sort() +{ + Parent::sort(); +} + +void RenderParticleMgr::clear() +{ + Parent::clear(); + + // Reset pool index + if(!RenderToSingleTarget) + mTargetChainIdx = 0; + + for(Vector::iterator itr = mOffscreenSystems.begin(); + itr != mOffscreenSystems.end(); itr++) + { + (*itr).drawnThisFrame = false; + (*itr).pInstances.clear(); + } +} + +void RenderParticleMgr::render( SceneRenderState *state ) +{ + PROFILE_SCOPE(RenderParticleMgr_render); + + // Early out if nothing to draw + if( !mElementList.size() || + (!mParticleShader && !_initShader()) ) + return; + + GFXDEBUGEVENT_SCOPE(RenderParticleMgr_Render, ColorI::RED); + + GFXTransformSaver saver; + + // Iterate render instances + for( Vector::const_iterator itr = mElementList.begin(); + itr != mElementList.end(); itr++ ) + { + OffscreenSystemEntry &systemEntry = *reinterpret_cast(itr->inst); + + // Setup target + // NOTE: If you are using this on the Xbox360 with Basic Lighting, + // you are going to have to mess with either the render order, or + // you are going to have to make this a 'preserve' draw + if(!RenderToSingleTarget) + mTargetChainIdx = systemEntry.targetChainIdx; + _onPreRender(state); + + // Clear offscreen target + GFX->clear(GFXClearTarget, ColorI::ZERO, 1.0f, 0); + + // Draw offscreen systems + for( Vector::const_iterator itr2 = systemEntry.pInstances.begin(); + itr2 != systemEntry.pInstances.end(); itr2++ ) + { + ParticleRenderInst *ri = *itr2; + + // Sanity check + if(ri->systemState == PSS_AwaitingOffscreenDraw) + { + // If this is not a diffuse path, flag the system appropriately, and skip + // the offscreen processing. + if( !state->isDiffusePass() ) + { + if(state->isReflectPass()) + ri->systemState = PSS_AwaitingHighResDraw; + else + ri->systemState = PSS_DrawComplete; + continue; + } + + // Draw system offscreen + renderInstance(ri, state); + } + } + + // Cleanup + _onPostRender(); + } +} + +void RenderParticleMgr::_initGFXResources() +{ + // Screen quad + U16 *prims = NULL; + mScreenQuadPrimBuff.set(GFX, 4, 2, GFXBufferTypeStatic); + mScreenQuadPrimBuff.lock(&prims); + (*prims++) = 0; + (*prims++) = 1; + (*prims++) = 2; + (*prims++) = 3; + mScreenQuadPrimBuff.unlock(); + + mScreenQuadVertBuff.set(GFX, 4, GFXBufferTypeStatic); + CompositeQuadVert *verts = mScreenQuadVertBuff.lock(); + (*verts++).uvCoord.set(0, 0, 0, 0); + (*verts++).uvCoord.set(0, 255, 0, 0); + (*verts++).uvCoord.set(255, 0, 0, 0); + (*verts++).uvCoord.set(255, 255, 0, 0); + mScreenQuadVertBuff.unlock(); + + // Stencil setup state block + GFXStateBlockDesc d; + + d.setCullMode(GFXCullNone); + d.setColorWrites(false, false, false, false); + d.setBlend(false); + d.setZReadWrite(false, false); + + d.stencilDefined = true; + d.stencilEnable = true; + d.stencilMask = RenderParticleMgr::ParticleSystemStencilMask; + d.stencilWriteMask = RenderParticleMgr::ParticleSystemStencilMask; + d.stencilFunc = GFXCmpAlways; + d.stencilPassOp = GFXStencilOpZero; + + mStencilClearSB = GFX->createStateBlock(d); +} + +void RenderParticleMgr::renderInstance(ParticleRenderInst *ri, SceneRenderState *state) +{ + // Draw system path, or draw composite path + if(ri->systemState == PSS_DrawComplete) + return; + + if(ri->systemState != PSS_AwaitingCompositeDraw) + { + // Set proper stateblock, and update state + if(ri->systemState == PSS_AwaitingOffscreenDraw) + { + GFX->setStateBlock( _getOffscreenStateBlock(ri) ); + ri->systemState = PSS_AwaitingCompositeDraw; + mParticleShaderConsts.mShaderConsts->setSafe( mParticleShaderConsts.mModelViewProjSC, + *ri->modelViewProj * mOffscreenSystems[ri->targetIndex].clipMatrix ); + } + else + { + if(ri->systemState == PSS_AwaitingMixedResDraw) + GFX->setStateBlock( _getMixedResStateBlock( ri ) ); + else + GFX->setStateBlock( _getHighResStateBlock( ri ) ); + ri->systemState = PSS_DrawComplete; + mParticleShaderConsts.mShaderConsts->setSafe( mParticleShaderConsts.mModelViewProjSC, *ri->modelViewProj ); + } + + // We want to turn everything into variation on a pre-multiplied alpha blend + F32 alphaFactor = 0.0f, alphaScale = 1.0f; + switch(ri->blendStyle) + { + // SrcAlpha, InvSrcAlpha + case ParticleRenderInst::BlendNormal: + alphaFactor = 1.0f; + break; + + // SrcAlpha, One + case ParticleRenderInst::BlendAdditive: + alphaFactor = 1.0f; + alphaScale = 0.0f; + break; + + // SrcColor, One + case ParticleRenderInst::BlendGreyscale: + alphaFactor = -1.0f; + alphaScale = 0.0f; + break; + } + mParticleShaderConsts.mShaderConsts->setSafe( mParticleShaderConsts.mAlphaFactorSC, alphaFactor ); + mParticleShaderConsts.mShaderConsts->setSafe( mParticleShaderConsts.mAlphaScaleSC, alphaScale ); + + mParticleShaderConsts.mShaderConsts->setSafe( mParticleShaderConsts.mFSModelViewProjSC, *ri->modelViewProj ); + mParticleShaderConsts.mShaderConsts->setSafe( mParticleShaderConsts.mOneOverFarSC, 1.0f / state->getFarPlane() ); + + if ( mParticleShaderConsts.mOneOverSoftnessSC->isValid() ) + { + F32 oneOverSoftness = 1.0f; + if ( ri->softnessDistance > 0.0f ) + oneOverSoftness = 1.0f / ( ri->softnessDistance / state->getFarPlane() ); + mParticleShaderConsts.mShaderConsts->set( mParticleShaderConsts.mOneOverSoftnessSC, oneOverSoftness ); + } + + GFX->setShader( mParticleShader ); + GFX->setShaderConstBuffer( mParticleShaderConsts.mShaderConsts ); + + GFX->setTexture( 0, ri->diffuseTex ); + + // Set up the prepass texture. + if ( mParticleShaderConsts.mPrePassTargetParamsSC->isValid() ) + { + GFXTextureObject *texObject = mPrepassTarget ? mPrepassTarget->getTexture(0) : NULL; + GFX->setTexture( 1, texObject ); + + Point4F rtParams( 0.0f, 0.0f, 1.0f, 1.0f ); + if ( texObject ) + ScreenSpace::RenderTargetParameters(texObject->getSize(), mPrepassTarget->getViewport(), rtParams); + + mParticleShaderConsts.mShaderConsts->set( mParticleShaderConsts.mPrePassTargetParamsSC, rtParams ); + } + + GFX->setPrimitiveBuffer( *ri->primBuff ); + GFX->setVertexBuffer( *ri->vertBuff ); + + GFX->drawIndexedPrimitive( GFXTriangleList, 0, 0, ri->count * 4, 0, ri->count * 2 ); + } + else if(ri->systemState == PSS_AwaitingCompositeDraw) + { + OffscreenSystemEntry &systemEntry = mOffscreenSystems[ri->targetIndex]; + + // If this system has already been composited this frame, skip it + if(systemEntry.drawnThisFrame) + return; + + // Non-target render, composite the particle system back into the scene + GFX->setVertexBuffer(mScreenQuadVertBuff); + GFX->setPrimitiveBuffer(mScreenQuadPrimBuff); + + + // Set up shader constants + mParticleCompositeShaderConsts.mShaderConsts->setSafe( mParticleCompositeShaderConsts.mScreenRect, *((Point4F *)&systemEntry.screenRect) ); + + // Set offscreen texture + Point4F rtParams; + GFXTextureObject *particleSource = mNamedTarget.getTexture(); + GFX->setTexture( 0, particleSource ); + if(particleSource) + { + ScreenSpace::RenderTargetParameters(particleSource->getSize(), mNamedTarget.getViewport(), rtParams); + mParticleCompositeShaderConsts.mShaderConsts->setSafe( mParticleCompositeShaderConsts.mOffscreenTargetParamsSC, rtParams ); + } + + // And edges + GFXTextureObject *texObject = mEdgeTarget ? mEdgeTarget->getTexture() : NULL; + GFX->setTexture( 1, texObject ); + if(texObject) + { + ScreenSpace::RenderTargetParameters(texObject->getSize(), mEdgeTarget->getViewport(), rtParams); + mParticleCompositeShaderConsts.mShaderConsts->setSafe( mParticleCompositeShaderConsts.mEdgeTargetParamsSC, rtParams ); + } + + // Set shader and constant buffer + GFX->setShader( mParticleCompositeShader ); + GFX->setShaderConstBuffer( mParticleCompositeShaderConsts.mShaderConsts ); + + // Draw to stencil buffer only to clear the stencil values + GFX->setStateBlock( mStencilClearSB ); + GFX->drawIndexedPrimitive( GFXTriangleStrip, 0, 0, 4, 0, 2 ); + + // composite particle system back into the scene + GFX->setStateBlock( _getCompositeStateBlock(ri) ); + GFX->drawIndexedPrimitive( GFXTriangleStrip, 0, 0, 4, 0, 2 ); + + // Re-draw the particle systems in high-res, but only to the stenciled + // areas which were enabled via the edge buffer + for( Vector::const_iterator itr = systemEntry.pInstances.begin(); + itr != systemEntry.pInstances.end(); itr++ ) + { + ParticleRenderInst *pri = *itr; + if(pri->systemState == PSS_AwaitingCompositeDraw) + { + pri->systemState = PSS_AwaitingMixedResDraw; + renderInstance(pri, state); + } + } + + // Mark this system as having been composited this frame + systemEntry.drawnThisFrame = true; + } +} + +bool RenderParticleMgr::_initShader() +{ + ShaderData *shaderData = NULL; + bool ret = true; + + // Need depth from pre-pass, so get the macros + Vector macros; + if ( mPrepassTarget ) + mPrepassTarget->getShaderMacros( ¯os ); + + // Create particle shader + if ( !Sim::findObject( "ParticlesShaderData", shaderData ) || !shaderData ) + Con::warnf( "RenderParticleMgr::_initShader - failed to locate shader ParticlesShaderData!" ); + if( shaderData ) + mParticleShader = shaderData->getShader( macros ); + ret &= (mParticleShader != NULL); + + if ( mParticleShader ) + { + mParticleShaderConsts.mShaderConsts = mParticleShader->allocConstBuffer(); + mParticleShaderConsts.mModelViewProjSC = mParticleShader->getShaderConstHandle( "$modelViewProj" ); + mParticleShaderConsts.mOneOverFarSC = mParticleShader->getShaderConstHandle( "$oneOverFar" ); + mParticleShaderConsts.mOneOverSoftnessSC = mParticleShader->getShaderConstHandle( "$oneOverSoftness" ); + mParticleShaderConsts.mAlphaFactorSC = mParticleShader->getShaderConstHandle( "$alphaFactor" ); + mParticleShaderConsts.mAlphaScaleSC = mParticleShader->getShaderConstHandle( "$alphaScale" ); + mParticleShaderConsts.mFSModelViewProjSC = mParticleShader->getShaderConstHandle( "$fsModelViewProj" ); + mParticleShaderConsts.mPrePassTargetParamsSC = mParticleShader->getShaderConstHandle( "$prePassTargetParams" ); + } + + shaderData = NULL; + + // Create off screen particle composite shader + if ( !Sim::findObject( "OffscreenParticleCompositeShaderData", shaderData ) || !shaderData ) + Con::warnf( "RenderParticleMgr::_initShader - failed to locate shader OffscreenParticleCompositeShaderData!" ); + if( shaderData ) + mParticleCompositeShader = shaderData->getShader( macros ); + ret &= (mParticleCompositeShader != NULL); + + if ( mParticleCompositeShader ) + { + mParticleCompositeShaderConsts.mShaderConsts = mParticleCompositeShader->allocConstBuffer(); + mParticleCompositeShaderConsts.mScreenRect = mParticleCompositeShader->getShaderConstHandle( "$screenRect" ); + mParticleCompositeShaderConsts.mEdgeTargetParamsSC = mParticleCompositeShader->getShaderConstHandle( "$edgeTargetParams" ); + mParticleCompositeShaderConsts.mOffscreenTargetParamsSC = mParticleCompositeShader->getShaderConstHandle( "$offscreenTargetParams" ); + } + + return ret; +} + +void RenderParticleMgr::_onLMActivate( const char*, bool activate ) +{ + RenderPassManager *rpm = getRenderPass(); + if ( !rpm ) + return; + + // Hunt for the pre-pass manager/target + RenderPrePassMgr *prePassBin = NULL; + for( U32 i = 0; i < rpm->getManagerCount(); i++ ) + { + RenderBinManager *bin = rpm->getManager(i); + if( bin->getRenderInstType() == RenderPrePassMgr::RIT_PrePass ) + { + prePassBin = (RenderPrePassMgr*)bin; + break; + } + } + + // If we found the prepass bin, set this bin to render very shortly afterwards + // and re-add this render-manager. If there is no pre-pass bin, or it doesn't + // have a depth-texture, we can't render offscreen. + mOffscreenRenderEnabled = prePassBin && (prePassBin->getTargetChainLength() > 0); + if(mOffscreenRenderEnabled) + { + rpm->removeManager(this); + setRenderOrder( prePassBin->getRenderOrder() + 0.011f ); + rpm->addManager(this); + } + + // Find the targets we use + mPrepassTarget = NamedTexTarget::find( "prepass" ); + mEdgeTarget = NamedTexTarget::find( "edge" ); + + // Setup the shader + if ( activate ) + _initShader(); + + if ( mScreenQuadVertBuff.isNull() ) + _initGFXResources(); +} + +GFXStateBlockRef RenderParticleMgr::_getOffscreenStateBlock(ParticleRenderInst *ri) +{ + const U8 blendStyle = ri->blendStyle; + if ( mOffscreenBlocks[blendStyle].isValid() ) + return mOffscreenBlocks[blendStyle]; + + GFXStateBlockDesc d; + + d.setCullMode(GFXCullNone); + d.setZReadWrite(false, false); // No zreads or writes, all z-testing is done in the pixel shader + + // Draw everything either subtractive, or using a variation on premultiplied + // alpha + if(blendStyle == ParticleRenderInst::BlendSubtractive) + d.setBlend(true, GFXBlendZero, GFXBlendInvSrcColor); + else + d.setBlend(true, GFXBlendOne, GFXBlendInvSrcAlpha); + + // Offscreen target, we need to add alpha. + d.separateAlphaBlendDefined = true; + d.separateAlphaBlendEnable = true; + d.separateAlphaBlendSrc = GFXBlendOne; + d.separateAlphaBlendDest = GFXBlendInvSrcAlpha; + + d.samplersDefined = true; + + // Diffuse texture sampler + d.samplers[0] = GFXSamplerStateDesc::getClampLinear(); + d.samplers[0].alphaOp = GFXTOPModulate; + d.samplers[0].alphaArg1 = GFXTATexture; + d.samplers[0].alphaArg2 = GFXTADiffuse; + + // Prepass sampler + d.samplers[1] = GFXSamplerStateDesc::getClampPoint(); + + mOffscreenBlocks[blendStyle] = GFX->createStateBlock(d); + return mOffscreenBlocks[blendStyle]; +} + +GFXStateBlockRef RenderParticleMgr::_getHighResStateBlock(ParticleRenderInst *ri) +{ + const U8 blendStyle = ri->blendStyle; + if ( mHighResBlocks[blendStyle].isValid() ) + return mHighResBlocks[blendStyle]; + + GFXStateBlockDesc d; + + d.setZReadWrite(true, false); + d.setCullMode(GFXCullNone); + + // Draw everything either subtractive, or using a variation on premultiplied + // alpha + if(blendStyle == ParticleRenderInst::BlendSubtractive) + d.setBlend(true, GFXBlendZero, GFXBlendInvSrcColor); + else + d.setBlend(true, GFXBlendOne, GFXBlendInvSrcAlpha); + + d.samplersDefined = true; + + // Diffuse texture sampler + d.samplers[0] = GFXSamplerStateDesc::getClampLinear(); + d.samplers[0].alphaOp = GFXTOPModulate; + d.samplers[0].alphaArg1 = GFXTATexture; + d.samplers[0].alphaArg2 = GFXTADiffuse; + + // Prepass sampler + d.samplers[1] = GFXSamplerStateDesc::getClampPoint(); + + mHighResBlocks[blendStyle] = GFX->createStateBlock(d); + return mHighResBlocks[blendStyle]; +} + +GFXStateBlockRef RenderParticleMgr::_getMixedResStateBlock(ParticleRenderInst *ri) +{ + const U8 blendStyle = ri->blendStyle; + if ( mHighResBlocks[blendStyle].isValid() ) + return mHighResBlocks[blendStyle]; + + GFXStateBlockDesc d; + + d.setZReadWrite(true, false); + d.setCullMode(GFXCullNone); + + /* + // Old blend styles... + switch (blendStyle) + { + case ParticleRenderInst::BlendNormal: + d.blendSrc = GFXBlendSrcAlpha; + d.blendDest = GFXBlendInvSrcAlpha; + break; + + case ParticleRenderInst::BlendSubtractive: + d.blendSrc = GFXBlendZero; + d.blendDest = GFXBlendInvSrcColor; + break; + + case ParticleRenderInst::BlendPremultAlpha: + d.blendSrc = GFXBlendOne; + d.blendDest = GFXBlendInvSrcAlpha; + break; + + // Default to additive blend mode + case ParticleRenderInst::BlendAdditive: + case ParticleRenderInst::BlendUndefined: + default: + d.blendSrc = GFXBlendSrcAlpha; + d.blendDest = GFXBlendOne; + break; + } + */ + + // Draw everything either subtractive, or using a variation on premultiplied + // alpha + if(blendStyle == ParticleRenderInst::BlendSubtractive) + d.setBlend(true, GFXBlendZero, GFXBlendInvSrcColor); + else + d.setBlend(true, GFXBlendOne, GFXBlendInvSrcAlpha); + + // Draw to anything but the stencil ref value (the edges) + d.stencilDefined = true; + d.stencilEnable = true; + d.stencilRef = RenderParticleMgr::HighResStencilRef; + d.stencilMask = RenderParticleMgr::ParticleSystemStencilMask; + d.stencilPassOp = GFXStencilOpKeep; + d.stencilFailOp = GFXStencilOpKeep; + d.stencilZFailOp = GFXStencilOpKeep; + d.stencilFunc = GFXCmpNotEqual; + + d.samplersDefined = true; + + // Diffuse texture sampler + d.samplers[0] = GFXSamplerStateDesc::getClampLinear(); + d.samplers[0].alphaOp = GFXTOPModulate; + d.samplers[0].alphaArg1 = GFXTATexture; + d.samplers[0].alphaArg2 = GFXTADiffuse; + + // Prepass sampler + d.samplers[1] = GFXSamplerStateDesc::getClampPoint(); + + mHighResBlocks[blendStyle] = GFX->createStateBlock(d); + return mHighResBlocks[blendStyle]; +} + +GFXStateBlockRef RenderParticleMgr::_getCompositeStateBlock(ParticleRenderInst *ri) +{ + const U8 blendStyle = ri->blendStyle; + if ( mBackbufferBlocks[blendStyle].isValid() ) + return mBackbufferBlocks[blendStyle]; + + GFXStateBlockDesc d; + + // This is a billboard + d.setCullMode(GFXCullNone); + d.setZReadWrite(false, false); + + // When we re-composite the particles, it is always either a pre-mult alpha + // blend, or a subtractive blend! + if(blendStyle == ParticleRenderInst::BlendSubtractive) + d.setBlend(true, GFXBlendZero, GFXBlendInvSrcColor); + else + d.setBlend(true, GFXBlendOne, GFXBlendInvSrcAlpha); + + // All areas which are not along the edges of geometry where the particle system + // is being drawn get assigned a stencil ref value as the system is composited + // back into the scene. The high-res stateblock uses this value as a mask, and + // draws only in areas which are NOT this ref value. This causes high resolution + // draws to ONLY the edge areas. + d.stencilDefined = true; + d.stencilEnable = true; + d.stencilRef = RenderParticleMgr::HighResStencilRef; + d.stencilWriteMask = RenderParticleMgr::ParticleSystemStencilMask; + d.stencilMask = RenderParticleMgr::ParticleSystemStencilMask; + d.stencilPassOp = GFXStencilOpReplace; + d.stencilFunc = GFXCmpGreater; + + // Diffuse texture sampler and + d.samplersDefined = true; + d.samplers[0] = GFXSamplerStateDesc::getClampLinear(); + d.samplers[1] = GFXSamplerStateDesc::getClampLinear(); + + mBackbufferBlocks[blendStyle] = GFX->createStateBlock(d); + return mBackbufferBlocks[blendStyle]; +} + +bool RenderParticleMgr::_handleGFXEvent( GFXDevice::GFXDeviceEventType event ) +{ + if(RenderToSingleTarget) + return Parent::_handleGFXEvent( event ); + + // Do nothing. This render manager uses its target chain as a pool of targets. + return true; +} \ No newline at end of file diff --git a/Engine/source/renderInstance/renderParticleMgr.h b/Engine/source/renderInstance/renderParticleMgr.h new file mode 100644 index 000000000..2fefe842d --- /dev/null +++ b/Engine/source/renderInstance/renderParticleMgr.h @@ -0,0 +1,141 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _RENDER_PARTICLE_MGR_H_ +#define _RENDER_PARTICLE_MGR_H_ + +#ifndef _TEXTARGETBIN_MGR_H_ +#include "renderInstance/renderTexTargetBinManager.h" +#endif + +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif + +GFXDeclareVertexFormat( CompositeQuadVert ) +{ + GFXVertexColor uvCoord; +}; + +class RenderParticleMgr : public RenderTexTargetBinManager +{ + typedef RenderTexTargetBinManager Parent; + friend class RenderTranslucentMgr; + +public: + // Generic PrePass Render Instance Type + static const RenderInstType RIT_Particles; + + RenderParticleMgr(); + RenderParticleMgr( F32 renderOrder, F32 processAddOrder ); + virtual ~RenderParticleMgr(); + + // RenderBinManager + void render(SceneRenderState * state); + void sort(); + void clear(); + void addElement( RenderInst *inst ); + + // ConsoleObject + DECLARE_CONOBJECT(RenderParticleMgr); + + const static U8 HighResStencilRef = 0x80; + const static U8 ParticleSystemStencilMask = 0x80; // We are using the top bit + const static U32 OffscreenPoolSize = 5; + + virtual void setTargetChainLength(const U32 chainLength); + +protected: + + // Override + virtual bool _handleGFXEvent(GFXDevice::GFXDeviceEventType event); + + bool _initShader(); + void _initGFXResources(); + void _onLMActivate( const char*, bool activate ); + + + // Not only a helper method, but a method for the RenderTranslucentMgr to + // request a particle system draw + void renderInstance(ParticleRenderInst *ri, SceneRenderState *state); + + bool mOffscreenRenderEnabled; + + /// The prepass render target used for the + /// soft particle shader effect. + NamedTexTargetRef mPrepassTarget; + + /// The shader used for particle rendering. + GFXShaderRef mParticleShader; + + GFXShaderRef mParticleCompositeShader; + NamedTexTargetRef mEdgeTarget; + + struct OffscreenSystemEntry + { + S32 targetChainIdx; + MatrixF clipMatrix; + RectF screenRect; + bool drawnThisFrame; + Vector pInstances; + }; + Vector mOffscreenSystems; + + struct ShaderConsts + { + GFXShaderConstBufferRef mShaderConsts; + GFXShaderConstHandle *mModelViewProjSC; + GFXShaderConstHandle *mFSModelViewProjSC; + GFXShaderConstHandle *mOneOverFarSC; + GFXShaderConstHandle *mOneOverSoftnessSC; + GFXShaderConstHandle *mPrePassTargetParamsSC; + GFXShaderConstHandle *mAlphaFactorSC; + GFXShaderConstHandle *mAlphaScaleSC; + + } mParticleShaderConsts; + + struct CompositeShaderConsts + { + GFXShaderConstBufferRef mShaderConsts; + GFXShaderConstHandle *mSystemDepth; + GFXShaderConstHandle *mScreenRect; + GFXShaderConstHandle *mEdgeTargetParamsSC; + GFXShaderConstHandle *mOffscreenTargetParamsSC; + } mParticleCompositeShaderConsts; + + GFXVertexBufferHandle mScreenQuadVertBuff; + GFXPrimitiveBufferHandle mScreenQuadPrimBuff; + + GFXStateBlockRef mStencilClearSB; + GFXStateBlockRef mHighResBlocks[ParticleRenderInst::BlendStyle_COUNT]; + GFXStateBlockRef mOffscreenBlocks[ParticleRenderInst::BlendStyle_COUNT]; + GFXStateBlockRef mBackbufferBlocks[ParticleRenderInst::BlendStyle_COUNT]; + GFXStateBlockRef mMixedResBlocks[ParticleRenderInst::BlendStyle_COUNT]; + + GFXStateBlockRef _getHighResStateBlock(ParticleRenderInst *ri); + GFXStateBlockRef _getMixedResStateBlock(ParticleRenderInst *ri); + GFXStateBlockRef _getOffscreenStateBlock(ParticleRenderInst *ri); + GFXStateBlockRef _getCompositeStateBlock(ParticleRenderInst *ri); +}; + + +#endif // _RENDER_TRANSLUCENT_MGR_H_ diff --git a/Engine/source/renderInstance/renderPassManager.cpp b/Engine/source/renderInstance/renderPassManager.cpp new file mode 100644 index 000000000..95f8e4f71 --- /dev/null +++ b/Engine/source/renderInstance/renderPassManager.cpp @@ -0,0 +1,365 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "renderInstance/renderPassManager.h" + +#include "materials/sceneData.h" +#include "materials/matInstance.h" +#include "materials/customMaterialDefinition.h" +#include "materials/materialManager.h" +#include "scene/sceneManager.h" +#include "scene/sceneObject.h" +#include "gfx/primBuilder.h" +#include "platform/profiler.h" +#include "renderInstance/renderBinManager.h" +#include "renderInstance/renderObjectMgr.h" +#include "renderInstance/renderMeshMgr.h" +#include "renderInstance/renderTranslucentMgr.h" +#include "renderInstance/renderGlowMgr.h" +#include "renderInstance/renderTerrainMgr.h" +#include "core/util/safeDelete.h" +#include "math/util/matrixSet.h" +#include "console/engineAPI.h" + + +const RenderInstType RenderInstType::Invalid( String::EmptyString ); + +const RenderInstType RenderPassManager::RIT_Interior("Interior"); +const RenderInstType RenderPassManager::RIT_Mesh("Mesh"); +const RenderInstType RenderPassManager::RIT_Shadow("Shadow"); +const RenderInstType RenderPassManager::RIT_Sky("Sky"); +const RenderInstType RenderPassManager::RIT_Terrain("Terrain"); +const RenderInstType RenderPassManager::RIT_Object("Object"); +const RenderInstType RenderPassManager::RIT_ObjectTranslucent("ObjectTranslucent"); +const RenderInstType RenderPassManager::RIT_Decal("Decal"); +const RenderInstType RenderPassManager::RIT_Water("Water"); +const RenderInstType RenderPassManager::RIT_Foliage("Foliage"); +const RenderInstType RenderPassManager::RIT_Translucent("Translucent"); +const RenderInstType RenderPassManager::RIT_Begin("Begin"); +const RenderInstType RenderPassManager::RIT_Custom("Custom"); +const RenderInstType RenderPassManager::RIT_Particle("Particle"); +const RenderInstType RenderPassManager::RIT_Occluder("Occluder"); +const RenderInstType RenderPassManager::RIT_Editor("Editor"); + + +//***************************************************************************** +// RenderInstance +//***************************************************************************** + +void RenderInst::clear() +{ + dMemset( this, 0, sizeof(RenderInst) ); +} + +void MeshRenderInst::clear() +{ + dMemset( this, 0, sizeof(MeshRenderInst) ); + visibility = 1.0f; +} + +void ParticleRenderInst::clear() +{ + dMemset( this, 0, sizeof(ParticleRenderInst) ); +} + +void ObjectRenderInst::clear() +{ + userData = NULL; + + dMemset( this, 0, sizeof( ObjectRenderInst ) ); + + // The memset here is kinda wrong... it clears the + // state initialized by the delegate constructor. + // + // This fixes it... but we probably need to have a + // real constructor for RenderInsts. + renderDelegate.clear(); +} + +void OccluderRenderInst::clear() +{ + dMemset( this, 0, sizeof(OccluderRenderInst) ); +} + + +IMPLEMENT_CONOBJECT(RenderPassManager); + + +ConsoleDocClass( RenderPassManager, + "@brief A grouping of render bin managers which forms a render pass.\n\n" + "The render pass is used to order a set of RenderBinManager objects which are used " + "when rendering a scene. This class does little work itself other than managing " + "its list of render bins.\n\n" + "In 'core/scripts/client/renderManager.cs' you will find the DiffuseRenderPassManager " + "which is used by the C++ engine to render the scene.\n\n" + "@see RenderBinManager\n" + "@ingroup RenderBin\n" ); + +RenderPassManager::RenderBinEventSignal& RenderPassManager::getRenderBinSignal() +{ + static RenderBinEventSignal theSignal; + return theSignal; +} + +void RenderPassManager::initPersistFields() +{ +} + +RenderPassManager::RenderPassManager() +{ + mSceneManager = NULL; + VECTOR_SET_ASSOCIATION( mRenderBins ); + + mMatrixSet = reinterpret_cast(dMalloc_aligned(sizeof(MatrixSet), 16)); + constructInPlace(mMatrixSet); +} + +RenderPassManager::~RenderPassManager() +{ + dFree_aligned(mMatrixSet); + + // Any bins left need to be deleted. + for ( U32 i=0; isetRenderPass( NULL ); + bin->deleteObject(); + } +} + +void RenderPassManager::_insertSort(Vector& list, RenderBinManager* mgr, bool renderOrder) +{ + U32 i; + for (i = 0; i < list.size(); i++) + { + bool renderCompare = mgr->getRenderOrder() < list[i]->getRenderOrder(); + bool processAddCompare = mgr->getProcessAddOrder() < list[i]->getProcessAddOrder(); + if ((renderOrder && renderCompare) || (!renderOrder && processAddCompare)) + { + list.insert(i); + list[i] = mgr; + return; + } + } + + list.push_back(mgr); +} + +void RenderPassManager::addManager(RenderBinManager* mgr) +{ + if ( !mgr->isProperlyAdded() ) + mgr->registerObject(); + + AssertFatal( mgr->getRenderPass() == NULL, "RenderPassManager::addManager() - Bin is still part of another pass manager!" ); + mgr->setRenderPass( this ); + + _insertSort(mRenderBins, mgr, true); +} + +void RenderPassManager::removeManager(RenderBinManager* mgr) +{ + AssertFatal( mgr->getRenderPass() == this, "RenderPassManager::removeManager() - We do not own this bin!" ); + + mgr->setRenderPass( NULL ); + mRenderBins.remove( mgr ); +} + +RenderBinManager* RenderPassManager::getManager(S32 i) const +{ + if (i >= 0 && i < mRenderBins.size()) + return mRenderBins[i]; + else + return NULL; +} + +void RenderPassManager::addInst( RenderInst *inst ) +{ + PROFILE_SCOPE( RenderPassManager_addInst ); + + AssertFatal( inst != NULL, "RenderPassManager::addInst - Got null instance!" ); + + AddInstTable::Iterator iter = mAddInstSignals.find( inst->type ); + if ( iter == mAddInstSignals.end() ) + return; + + iter->value.trigger( inst ); +} + +void RenderPassManager::sort() +{ + PROFILE_SCOPE( RenderPassManager_Sort ); + + for (Vector::iterator itr = mRenderBins.begin(); + itr != mRenderBins.end(); itr++) + { + AssertFatal(*itr, "Render manager invalid!"); + (*itr)->sort(); + } +} + +void RenderPassManager::clear() +{ + PROFILE_SCOPE( RenderPassManager_Clear ); + + mChunker.clear(); + + for (Vector::iterator itr = mRenderBins.begin(); + itr != mRenderBins.end(); itr++) + { + AssertFatal(*itr, "Invalid render manager!"); + (*itr)->clear(); + } +} + +void RenderPassManager::render(SceneRenderState * state) +{ + PROFILE_SCOPE( RenderPassManager_Render ); + + GFX->pushWorldMatrix(); + MatrixF proj = GFX->getProjectionMatrix(); + + + for (Vector::iterator itr = mRenderBins.begin(); + itr != mRenderBins.end(); itr++) + { + RenderBinManager *curBin = *itr; + AssertFatal(curBin, "Invalid render manager!"); + getRenderBinSignal().trigger(curBin, state, true); + curBin->render(state); + getRenderBinSignal().trigger(curBin, state, false); + } + + GFX->popWorldMatrix(); + GFX->setProjectionMatrix( proj ); + + // Restore a clean state for subsequent rendering. + GFX->disableShaders(); + for(S32 i = 0; i < GFX->getNumSamplers(); ++i) + GFX->setTexture(i, NULL); +} + +void RenderPassManager::renderPass(SceneRenderState * state) +{ + PROFILE_SCOPE( RenderPassManager_RenderPass ); + sort(); + render(state); + clear(); +} + +GFXTextureObject *RenderPassManager::getDepthTargetTexture() +{ + // If this is OpenGL, or something else has set the depth buffer, return the pointer + if( mDepthBuff.isValid() ) + { + // If this is OpenGL, make sure the depth target matches up + // with the active render target. Otherwise recreate. + + if( GFX->getAdapterType() == OpenGL ) + { + GFXTarget* activeRT = GFX->getActiveRenderTarget(); + AssertFatal( activeRT, "Must be an active render target to call 'getDepthTargetTexture'" ); + + Point2I activeRTSize = activeRT->getSize(); + if( mDepthBuff.getWidth() == activeRTSize.x && + mDepthBuff.getHeight() == activeRTSize.y ) + return mDepthBuff.getPointer(); + } + else + return mDepthBuff.getPointer(); + } + + if(GFX->getAdapterType() == OpenGL) + { + AssertFatal(GFX->getActiveRenderTarget(), "Must be an active render target to call 'getDepthTargetTexture'"); + + const Point2I rtSize = GFX->getActiveRenderTarget()->getSize(); + mDepthBuff.set(rtSize.x, rtSize.y, GFXFormatD24S8, + &GFXDefaultZTargetProfile, avar("%s() - mDepthBuff (line %d)", __FUNCTION__, __LINE__)); + return mDepthBuff.getPointer(); + } + + // Default return value + return GFXTextureTarget::sDefaultDepthStencil; +} + +void RenderPassManager::setDepthTargetTexture( GFXTextureObject *zTarget ) +{ + mDepthBuff = zTarget; +} + +const MatrixF* RenderPassManager::allocSharedXform( SharedTransformType stt ) +{ + AssertFatal(stt == View || stt == Projection, "Bad shared transform type"); + + // Enable this to simulate non-shared transform performance + //#define SIMULATE_NON_SHARED_TRANSFORMS +#ifdef SIMULATE_NON_SHARED_TRANSFORMS + return allocUniqueXform(stt == View ? mMatrixSet->getWorldToCamera() : mMatrixSet->getCameraToScreen()); +#else + return &(stt == View ? mMatrixSet->getWorldToCamera() : mMatrixSet->getCameraToScreen()); +#endif +} + +void RenderPassManager::assignSharedXform( SharedTransformType stt, const MatrixF &xfm ) +{ + AssertFatal(stt == View || stt == Projection, "Bad shared transform type"); + if(stt == View) + mMatrixSet->setSceneView(xfm); + else + mMatrixSet->setSceneProjection(xfm); +} + +// Script interface + +DefineEngineMethod(RenderPassManager, getManagerCount, S32, (),, + "Returns the total number of bin managers." ) +{ + return object->getManagerCount(); +} + +DefineEngineMethod( RenderPassManager, getManager, RenderBinManager*, ( S32 index ),, + "Returns the render bin manager at the index or null if the index is out of range." ) +{ + if(index < 0 || index >= object->getManagerCount()) + return NULL; + + return object->getManager(index); +} + +DefineEngineMethod( RenderPassManager, addManager, void, ( RenderBinManager *renderBin ),, + "Add as a render bin manager to the pass." ) +{ + if ( renderBin ) + object->addManager( renderBin ); +} + +DefineEngineMethod( RenderPassManager, removeManager, void, ( RenderBinManager *renderBin ),, + "Removes a render bin manager." ) +{ + if ( renderBin ) + object->removeManager( renderBin ); +} + diff --git a/Engine/source/renderInstance/renderPassManager.h b/Engine/source/renderInstance/renderPassManager.h new file mode 100644 index 000000000..d80964866 --- /dev/null +++ b/Engine/source/renderInstance/renderPassManager.h @@ -0,0 +1,446 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _RENDERPASSMANAGER_H_ +#define _RENDERPASSMANAGER_H_ + +#ifndef _GFXDEVICE_H_ +#include "gfx/gfxDevice.h" +#endif +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _SIMOBJECT_H_ +#include "console/simObject.h" +#endif +#ifndef _DATACHUNKER_H_ +#include "core/dataChunker.h" +#endif +#ifndef _SCENEMANAGER_H_ +#include "scene/sceneManager.h" +#endif + +class SceneRenderState; +class ISceneObject; +class BaseMatInstance; +struct SceneData; +class ShaderData; +class RenderBinManager; +class LightInfo; +struct RenderInst; +class MatrixSet; +class GFXPrimitiveBufferHandle; + +/// A RenderInstType hash value. +typedef U32 RenderInstTypeHash; + +/// A a tiny wrapper around String that exposes a U32 operator so +/// that we can assign the RIT to RenderInst::type field. +class RenderInstType +{ + /// For direct access to mName. + friend class RenderBinManager; + +protected: + + String mName; + +public: + + RenderInstType( const RenderInstType &type = Invalid ) + : mName( type.mName ) + { + } + + RenderInstType( const String &name ) + : mName( name ) + { + } + + ~RenderInstType() {} + + operator RenderInstTypeHash() const { return (RenderInstTypeHash)mName.getHashCaseInsensitive(); } + + const String& getName() const { return mName; } + + bool isValid() const { return (RenderInstTypeHash)*this != (RenderInstTypeHash)Invalid; } + + static const RenderInstType Invalid; +}; + + +/// +class RenderPassManager : public SimObject +{ + typedef SimObject Parent; + +public: + + // Default bin types. Not necessarily the only bin types in the system. + // RIT = "R"ender "I"nstance "T"ype + static const RenderInstType RIT_Interior; + static const RenderInstType RIT_Mesh; + static const RenderInstType RIT_Shadow; + static const RenderInstType RIT_Sky; + static const RenderInstType RIT_Terrain; + static const RenderInstType RIT_Object; // objects that do their own rendering + static const RenderInstType RIT_ObjectTranslucent;// self rendering; but sorted with static const RenderInstType RIT_Translucent + static const RenderInstType RIT_Decal; + static const RenderInstType RIT_Water; + static const RenderInstType RIT_Foliage; + static const RenderInstType RIT_Translucent; + static const RenderInstType RIT_Begin; + static const RenderInstType RIT_Custom; + static const RenderInstType RIT_Particle; + static const RenderInstType RIT_Occluder; + static const RenderInstType RIT_Editor; + +public: + + RenderPassManager(); + virtual ~RenderPassManager(); + + /// @name Allocation interface + /// @{ + + /// Allocate a render instance, use like so: MyRenderInstType* t = gRenderInstMgr->allocInst(); + /// Valid until ::clear called. + template + T* allocInst() + { + T* inst = mChunker.alloc(); + inst->clear(); + return inst; + } + + /// Allocate a matrix, valid until ::clear called. + MatrixF* allocUniqueXform(const MatrixF& data) + { + MatrixF *r = mChunker.alloc(); + *r = data; + return r; + } + + enum SharedTransformType + { + View, + Projection, + }; + + const MatrixF* allocSharedXform(SharedTransformType stt); + + void assignSharedXform(SharedTransformType stt, const MatrixF &xfm); + + MatrixSet &getMatrixSet() { return *mMatrixSet; } + + /// Allocate a GFXPrimitive object which will remain valid + /// until the pass manager is cleared. + GFXPrimitive* allocPrim() { return mChunker.alloc(); } + /// @} + + /// Add a RenderInstance to the list + virtual void addInst( RenderInst *inst ); + + /// Sorts the list of RenderInst's per bin. (Normally, one should just call renderPass) + void sort(); + + /// Renders the list of RenderInsts (Normally, one should just call renderPass) + void render( SceneRenderState *state ); + + /// Resets our allocated RenderInstances and Matrices. (Normally, one should just call renderPass) + void clear(); + + // Calls sort, render, and clear + void renderPass( SceneRenderState *state ); + + /// Returns the active depth buffer for this pass (NOTE: This value may be GFXTextureTarget::sDefaultDepthStencil) + GFXTextureObject *getDepthTargetTexture(); + + /// Assigns the value for the above method + void setDepthTargetTexture(GFXTextureObject *zTarget); + + /// @name RenderBinManager interface + /// @{ + + /// Add a render bin manager to the list of render bin manager, this SceneRenderPassManager now owns the render bin manager and will free it when needed. + /// @param mgr Render manager to add + /// @param processAddOrder Where to add the manager in the addInst list, set to NO_PROCESSADD to skip processing + /// this is in place for RenderManagers that will bypass the main ::addInst interface and doesn't want to process + /// them. + /// @param renderOrder Where to add the manager in the render list. + void addManager(RenderBinManager* mgr); + + /// Removes a manager from render and process add lists + /// @param mgr Render bin manager to remove, the caller is now responsible for freeing the mgr. + void removeManager(RenderBinManager* mgr); + + /// How many render bin managers do we have? + U32 getManagerCount() const { return mRenderBins.size(); } + + /// Get the render manager at i + RenderBinManager* getManager( S32 i ) const; + + /// @} + + /// Get scene manager which this render pass belongs to. + SceneManager* getSceneManager() + { + if ( !mSceneManager ) + mSceneManager = gClientSceneGraph; + + return mSceneManager; + } + + /// This signal is triggered when a render bin is about to be rendered. + /// + /// @param bin The render bin we're signaling. + /// @param state The current scene state. + /// @params preRender If true it is before the bin is rendered, else its + /// after being rendered. + /// + typedef Signal RenderBinEventSignal; + + /// @see RenderBinEventSignal + static RenderBinEventSignal& getRenderBinSignal(); + + + typedef Signal AddInstSignal; + + AddInstSignal& getAddSignal( RenderInstTypeHash type ) + { + return mAddInstSignals.findOrInsert( type )->value; + } + + // ConsoleObject interface + static void initPersistFields(); + DECLARE_CONOBJECT(RenderPassManager); + +protected: + + MultiTypedChunker mChunker; + + Vector< RenderBinManager* > mRenderBins; + + + typedef HashTable AddInstTable; + + AddInstTable mAddInstSignals; + + SceneManager * mSceneManager; + GFXTexHandle mDepthBuff; + MatrixSet *mMatrixSet; + + /// Do a sorted insert into a vector, renderOrder bool controls which test we run for insertion. + void _insertSort(Vector& list, RenderBinManager* mgr, bool renderOrder); +}; + +//************************************************************************** +// Render Instance +//************************************************************************** +struct RenderInst +{ + /// The type of render instance this is. + RenderInstTypeHash type; + + /// This should be true if the object needs to be sorted + /// back to front with other translucent instances. + /// @see sortDistSq + bool translucentSort; + + /// The reference squared distance from the camera used for + /// back to front sorting of the instances. + /// @see translucentSort + F32 sortDistSq; + + /// The default key used by render managers for + /// internal sorting. + U32 defaultKey; + + /// The secondary key used by render managers for + /// internal sorting. + U32 defaultKey2; + + /// Does a memset to clear the render instance. + void clear(); +}; + +struct ObjectRenderInst : public RenderInst +{ + /// This is a delegate specific index which is usually + /// used to define a mounted object. + S32 objectIndex; + + /// Extra data to be used within the render callback. + /// ObjectRenderInst does not own or cleanup this data. + void *userData; + + /// The delegate callback function to call to render + /// this object instance. + /// + /// @param ri The ObjectRenderInst that called the delegate. + /// + /// @param state The scene state we're rendering. + /// + /// @param overrideMat An alternative material to use during rendering... usually + /// used for special renders like shadows. If the object doesn't + /// support override materials it shouldn't render at all. + Delegate renderDelegate; + + // Clear this instance. + void clear(); +}; + +struct MeshRenderInst : public RenderInst +{ + //// + GFXVertexBufferHandleBase *vertBuff; + + //// + GFXPrimitiveBufferHandle *primBuff; + + /// If not NULL it is used to draw the primitive, else + /// the primBuffIndex is used. + /// @see primBuffIndex + GFXPrimitive *prim; + + /// If prim is NULL then this index is used to draw the + /// indexed primitive from the primitive buffer. + /// @see prim + U32 primBuffIndex; + + /// The material to setup when drawing this instance. + BaseMatInstance *matInst; + + /// The object to world transform (world transform in most API's). + const MatrixF *objectToWorld; + + /// The worldToCamera (view transform in most API's). + const MatrixF* worldToCamera; + + /// The projection matrix. + const MatrixF* projection; + + // misc render states + U8 transFlags; + bool reflective; + F32 visibility; + + /// A generic hint value passed from the game + /// code down to the material for use by shader + /// features. + void *materialHint; + + /// The lights we pass to the material for this + /// mesh in order light importance. + LightInfo* lights[8]; + + // textures + GFXTextureObject *lightmap; + GFXTextureObject *fogTex; + GFXTextureObject *backBuffTex; + GFXTextureObject *reflectTex; + GFXTextureObject *miscTex; + GFXCubemap *cubemap; + + void clear(); +}; + +enum ParticleSystemState +{ + PSS_AwaitingHighResDraw = 0, // Keep this as first element so that if the offscreen manager rejects a particle system it will get drawn high-res + PSS_AwaitingOffscreenDraw, + PSS_AwaitingCompositeDraw, + PSS_AwaitingMixedResDraw, + PSS_DrawComplete, +}; + +/// A special render instance for particles. +struct ParticleRenderInst : public RenderInst +{ + /// The vertex buffer. + GFXVertexBufferHandleBase *vertBuff; + + /// The primitive buffer. + GFXPrimitiveBufferHandle *primBuff; + + /// The total particle count to render. + S32 count; + + /// The combined model, camera, and projection transform. + const MatrixF *modelViewProj; + + /// Blend style for the particle system + enum BlendStyle { + BlendUndefined = 0, + BlendNormal, + BlendAdditive, + BlendSubtractive, + BlendPremultAlpha, + BlendGreyscale, + BlendStyle_COUNT, + }; + U8 blendStyle; + + /// For the offscreen particle manager + U8 targetIndex; + + /// State for the particle system + ParticleSystemState systemState; + + /// The soft particle fade distance in meters. + F32 softnessDistance; + + /// Bounding box render transform + const MatrixF *bbModelViewProj; + + /// The particle texture. + GFXTextureObject *diffuseTex; + + void clear(); +}; + +class GFXOcclusionQuery; +class SceneObject; + +/// A special render instance for occlusion tests. +struct OccluderRenderInst : public RenderInst +{ + Point3F scale; + Point3F position; + const MatrixF *orientation; + GFXOcclusionQuery *query; + + // This optional query will have all pixels rendered. + // Its purpose is to return to the user the full pixel count for comparison + // with the other query. + GFXOcclusionQuery *query2; + + /// Render a sphere or a box. + bool isSphere; + + void clear(); +}; + +#endif // _RENDERPASSMANAGER_H_ diff --git a/Engine/source/renderInstance/renderPassStateToken.cpp b/Engine/source/renderInstance/renderPassStateToken.cpp new file mode 100644 index 000000000..dfa328f69 --- /dev/null +++ b/Engine/source/renderInstance/renderPassStateToken.cpp @@ -0,0 +1,163 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "renderInstance/renderPassStateToken.h" +#include "console/consoleTypes.h" + +#include "console/engineAPI.h" + + +IMPLEMENT_CONOBJECT(RenderPassStateToken); + +ConsoleDocClass( RenderPassStateToken, + "@brief Abstract base class for RenderFormatToken, used to manipulate what goes on in the render manager\n\n" + + "You cannot actually instantiate RenderPassToken, only its child: RenderFormatToken. " + "RenderFormatToken is an implementation which changes the format of the " + "back buffer and/or the depth buffer.\n\n" + + "The RenderPassStateBin manager changes the rendering state associated with " + "a token it is declared with. In stock Torque 3D, a single example exists in the " + "way of AL_FormatToken (found in renderManager.cs). In that script file, all the " + "render managers are intialized, and a single RenderFormatToken is used. This " + "implementation basically exists to ensure Advanced Lighting works with MSAA.\n\n" + + "@see RenderFormatToken\n" + "@see RenderPassStateBin\n" + "@see game/core/scripts/client/renderManager.cs\n" + + "@ingroup RenderBin\n" +); + +void RenderPassStateToken::process(SceneRenderState *state, RenderPassStateBin *callingBin) +{ + TORQUE_UNUSED(state); + TORQUE_UNUSED(callingBin); + AssertWarn(false, "RenderPassStateToken is an abstract class, you must re-implement process()"); +} + +void RenderPassStateToken::reset() +{ + AssertWarn(false, "RenderPassStateToken is an abstract class, you must re-implement reset()"); +} + +void RenderPassStateToken::enable( bool enabled /*= true*/ ) +{ + TORQUE_UNUSED(enabled); + AssertWarn(false, "RenderPassStateToken is an abstract class, you must re-implement enable()"); +} + +bool RenderPassStateToken::isEnabled() const +{ + AssertWarn(false, "RenderPassStateToken is an abstract class, you must re-implement isEnabled()"); + return false; +} + +static bool _set_enable( void *object, const char *index, const char *data ) +{ + reinterpret_cast(object)->enable(dAtob(data)); + return false; +} + +static const char *_get_enable(void* obj, const char* data) +{ + TORQUE_UNUSED(data); + return reinterpret_cast(obj)->isEnabled() ? "true" : "false"; +} + +void RenderPassStateToken::initPersistFields() +{ + addProtectedField("enabled", TypeBool, NULL, &_set_enable, &_get_enable, "Enables or disables this token."); + Parent::initPersistFields(); +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +IMPLEMENT_CONOBJECT(RenderPassStateBin); + + +ConsoleDocClass( RenderPassStateBin, + "@brief A non-rendering render bin used to enable/disable a RenderPassStateToken.\n\n" + + "This is a utility RenderBinManager which does not render any render instances. Its only " + "used to define a point in the render bin order at which a RenderPassStateToken is triggered.\n\n" + + "@see RenderPassStateToken\n" + + "@ingroup RenderBin\n" +); + +RenderPassStateBin::RenderPassStateBin() + : Parent() +{ +} + +RenderPassStateBin::~RenderPassStateBin() +{ + +} + +void RenderPassStateBin::render(SceneRenderState *state) +{ + if(mStateToken.isValid()) + mStateToken->process(state, this); +} + +void RenderPassStateBin::clear() +{ + if(mStateToken.isValid()) + mStateToken->reset(); +} + +void RenderPassStateBin::sort() +{ + +} + +void RenderPassStateBin::initPersistFields() +{ + addProtectedField( "stateToken", TYPEID< RenderPassStateToken >(), Offset( mStateToken, RenderPassStateBin ), + _setStateToken, _getStateToken ); + + Parent::initPersistFields(); +} + +//------------------------------------------------------------------------------ + +DefineEngineMethod(RenderPassStateToken, enable, void, (),, + "@brief Enables the token." ) +{ + object->enable(true); +} + +DefineEngineMethod(RenderPassStateToken, disable, void, (),, + "@brief Disables the token.") +{ + object->enable(false); +} + +DefineEngineMethod(RenderPassStateToken, toggle, void, (),, + "@brief Toggles the token from enabled to disabled or vice versa." ) +{ + object->enable(!object->isEnabled()); +} \ No newline at end of file diff --git a/Engine/source/renderInstance/renderPassStateToken.h b/Engine/source/renderInstance/renderPassStateToken.h new file mode 100644 index 000000000..f472c72c7 --- /dev/null +++ b/Engine/source/renderInstance/renderPassStateToken.h @@ -0,0 +1,87 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _RENDERPASSSTATETOKEN_H_ +#define _RENDERPASSSTATETOKEN_H_ + +#ifndef _RENDERBINMANAGER_H_ +#include "renderInstance/renderBinManager.h" +#endif + +class RenderPassStateBin; + + +class RenderPassStateToken : public SimObject +{ + typedef SimObject Parent; + +public: + DECLARE_CONOBJECT(RenderPassStateToken); + + static void initPersistFields(); + + // These must be re-implemented, and will assert if called on the base class. + // They just can't be pure-virtual, due to SimObject. + virtual void process(SceneRenderState *state, RenderPassStateBin *callingBin); + virtual void reset(); + virtual void enable(bool enabled = true); + virtual bool isEnabled() const; +}; + +//------------------------------------------------------------------------------ + +class RenderPassStateBin : public RenderBinManager +{ + typedef RenderBinManager Parent; + +protected: + SimObjectPtr< RenderPassStateToken > mStateToken; + + static bool _setStateToken( void* object, const char* index, const char* data ) + { + RenderPassStateToken* stateToken; + Sim::findObject( data, stateToken ); + reinterpret_cast< RenderPassStateBin* >( object )->mStateToken = stateToken; + return false; + } + static const char* _getStateToken( void* object, const char* data ) + { + RenderPassStateBin* bin = reinterpret_cast< RenderPassStateBin* >( object ); + if( bin->mStateToken.isValid() ) + return bin->mStateToken->getIdString(); + else + return "0"; + } + +public: + DECLARE_CONOBJECT(RenderPassStateBin); + static void initPersistFields(); + + RenderPassStateBin(); + virtual ~RenderPassStateBin(); + + void render(SceneRenderState *state); + void clear(); + void sort(); +}; + +#endif // _RENDERPASSSTATETOKEN_H_ diff --git a/Engine/source/renderInstance/renderPrePassMgr.cpp b/Engine/source/renderInstance/renderPrePassMgr.cpp new file mode 100644 index 000000000..f82bc09f2 --- /dev/null +++ b/Engine/source/renderInstance/renderPrePassMgr.cpp @@ -0,0 +1,855 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "renderInstance/renderPrePassMgr.h" + +#include "gfx/gfxTransformSaver.h" +#include "materials/sceneData.h" +#include "materials/materialManager.h" +#include "materials/materialFeatureTypes.h" +#include "core/util/safeDelete.h" +#include "shaderGen/featureMgr.h" +#include "shaderGen/HLSL/depthHLSL.h" +#include "shaderGen/GLSL/depthGLSL.h" +#include "shaderGen/conditionerFeature.h" +#include "shaderGen/shaderGenVars.h" +#include "scene/sceneRenderState.h" +#include "gfx/gfxStringEnumTranslate.h" +#include "gfx/gfxDebugEvent.h" +#include "materials/customMaterialDefinition.h" +#include "lighting/advanced/advancedLightManager.h" +#include "lighting/advanced/advancedLightBinManager.h" +#include "terrain/terrCell.h" +#include "renderInstance/renderTerrainMgr.h" +#include "terrain/terrCellMaterial.h" +#include "math/mathUtils.h" +#include "math/util/matrixSet.h" + +const MatInstanceHookType PrePassMatInstanceHook::Type( "PrePass" ); +const String RenderPrePassMgr::BufferName("prepass"); +const RenderInstType RenderPrePassMgr::RIT_PrePass("PrePass"); + +IMPLEMENT_CONOBJECT(RenderPrePassMgr); + +ConsoleDocClass( RenderPrePassMgr, + "@brief The render bin which performs a z+normals prepass used in Advanced Lighting.\n\n" + "This render bin is used in Advanced Lighting to gather all opaque mesh render instances " + "and render them to the g-buffer for use in lighting the scene and doing effects.\n\n" + "PostEffect and other shaders can access the output of this bin by using the #prepass " + "texture target name. See the edge anti-aliasing post effect for an example.\n\n" + "@see game/core/scripts/client/postFx/edgeAA.cs\n" + "@ingroup RenderBin\n" ); + + +RenderPrePassMgr::RenderSignal& RenderPrePassMgr::getRenderSignal() +{ + static RenderSignal theSignal; + return theSignal; +} + + +RenderPrePassMgr::RenderPrePassMgr( bool gatherDepth, + GFXFormat format ) + : Parent( RIT_PrePass, + 0.01f, + 0.01f, + format, + Point2I( Parent::DefaultTargetSize, Parent::DefaultTargetSize), + gatherDepth ? Parent::DefaultTargetChainLength : 0 ), + mPrePassMatInstance( NULL ) +{ + notifyType( RenderPassManager::RIT_Decal ); + notifyType( RenderPassManager::RIT_Mesh ); + notifyType( RenderPassManager::RIT_Interior ); + notifyType( RenderPassManager::RIT_Terrain ); + notifyType( RenderPassManager::RIT_Object ); + + // We want a full-resolution buffer + mTargetSizeType = RenderTexTargetBinManager::WindowSize; + + if(getTargetChainLength() > 0) + GFXShader::addGlobalMacro( "TORQUE_LINEAR_DEPTH" ); + + mNamedTarget.registerWithName( BufferName ); + + _registerFeatures(); +} + +RenderPrePassMgr::~RenderPrePassMgr() +{ + GFXShader::removeGlobalMacro( "TORQUE_LINEAR_DEPTH" ); + + _unregisterFeatures(); + SAFE_DELETE( mPrePassMatInstance ); +} + +void RenderPrePassMgr::_registerFeatures() +{ + ConditionerFeature *cond = new LinearEyeDepthConditioner( getTargetFormat() ); + FEATUREMGR->registerFeature( MFT_PrePassConditioner, cond ); + mNamedTarget.setConditioner( cond ); +} + +void RenderPrePassMgr::_unregisterFeatures() +{ + mNamedTarget.setConditioner( NULL ); + FEATUREMGR->unregisterFeature(MFT_PrePassConditioner); +} + +bool RenderPrePassMgr::setTargetSize(const Point2I &newTargetSize) +{ + bool ret = Parent::setTargetSize( newTargetSize ); + mNamedTarget.setViewport( GFX->getViewport() ); + return ret; +} + +bool RenderPrePassMgr::_updateTargets() +{ + PROFILE_SCOPE(RenderPrePassMgr_updateTargets); + + bool ret = Parent::_updateTargets(); + + // check for an output conditioner, and update it's format + ConditionerFeature *outputConditioner = dynamic_cast(FEATUREMGR->getByType(MFT_PrePassConditioner)); + if( outputConditioner && outputConditioner->setBufferFormat(mTargetFormat) ) + { + // reload materials, the conditioner needs to alter the generated shaders + } + + // Attach the light info buffer as a second render target, if there is + // lightmapped geometry in the scene. + AdvancedLightBinManager *lightBin; + if ( Sim::findObject( "AL_LightBinMgr", lightBin ) && + lightBin->MRTLightmapsDuringPrePass() && + lightBin->isProperlyAdded() ) + { + // Update the size of the light bin target here. This will call _updateTargets + // on the light bin + ret &= lightBin->setTargetSize( mTargetSize ); + if ( ret ) + { + // Sanity check + AssertFatal(lightBin->getTargetChainLength() == mTargetChainLength, "Target chain length mismatch"); + + // Attach light info buffer to Color1 for each target in the chain + for ( U32 i = 0; i < mTargetChainLength; i++ ) + { + GFXTexHandle lightInfoTex = lightBin->getTargetTexture(0, i); + mTargetChain[i]->attachTexture(GFXTextureTarget::Color1, lightInfoTex); + } + } + } + + return ret; +} + +void RenderPrePassMgr::_createPrePassMaterial() +{ + SAFE_DELETE(mPrePassMatInstance); + + const GFXVertexFormat *vertexFormat = getGFXVertexFormat(); + + MatInstance* prepassMat = static_cast(MATMGR->createMatInstance("AL_DefaultPrePassMaterial", vertexFormat)); + AssertFatal( prepassMat, "TODO: Handle this better." ); + mPrePassMatInstance = new PrePassMatInstance(prepassMat, this); + mPrePassMatInstance->init( MATMGR->getDefaultFeatures(), vertexFormat); + delete prepassMat; +} + +void RenderPrePassMgr::setPrePassMaterial( PrePassMatInstance *mat ) +{ + SAFE_DELETE(mPrePassMatInstance); + mPrePassMatInstance = mat; +} + +void RenderPrePassMgr::addElement( RenderInst *inst ) +{ + PROFILE_SCOPE( RenderPrePassMgr_addElement ) + + // Skip out if this bin is disabled. + if ( gClientSceneGraph->getCurrentRenderState() && + gClientSceneGraph->getCurrentRenderState()->disableAdvancedLightingBins() ) + return; + + // First what type of render instance is it? + const bool isDecalMeshInst = inst->type == RenderPassManager::RIT_Decal; + + const bool isMeshInst = inst->type == RenderPassManager::RIT_Mesh || + inst->type == RenderPassManager::RIT_Interior; + + const bool isTerrainInst = inst->type == RenderPassManager::RIT_Terrain; + + // Get the material if its a mesh. + BaseMatInstance* matInst = NULL; + if ( isMeshInst || isDecalMeshInst ) + matInst = static_cast(inst)->matInst; + + // Skip decals if they don't have normal maps. + if ( isDecalMeshInst && !matInst->hasNormalMap() ) + return; + + // If its a custom material and it refracts... skip it. + if ( matInst && + matInst->isCustomMaterial() && + static_cast( matInst->getMaterial() )->mRefract ) + return; + + // Make sure we got a prepass material. + if ( matInst ) + { + matInst = getPrePassMaterial( matInst ); + if ( !matInst || !matInst->isValid() ) + return; + } + + // We're gonna add it to the bin... get the right element list. + Vector< MainSortElem > *elementList; + if ( isMeshInst || isDecalMeshInst ) + elementList = &mElementList; + else if ( isTerrainInst ) + elementList = &mTerrainElementList; + else + elementList = &mObjectElementList; + + elementList->increment(); + MainSortElem &elem = elementList->last(); + elem.inst = inst; + + // Store the original key... we might need it. + U32 originalKey = elem.key; + + // Sort front-to-back first to get the most fillrate savings. + const F32 invSortDistSq = F32_MAX - inst->sortDistSq; + elem.key = *((U32*)&invSortDistSq); + + // Next sort by pre-pass material if its a mesh... use the original sort key. + if ( isMeshInst ) + elem.key2 = matInst->getStateHint(); + else + elem.key2 = originalKey; +} + +void RenderPrePassMgr::sort() +{ + PROFILE_SCOPE( RenderPrePassMgr_sort ); + Parent::sort(); + dQsort( mTerrainElementList.address(), mTerrainElementList.size(), sizeof(MainSortElem), cmpKeyFunc); + dQsort( mObjectElementList.address(), mObjectElementList.size(), sizeof(MainSortElem), cmpKeyFunc); +} + +void RenderPrePassMgr::clear() +{ + Parent::clear(); + mTerrainElementList.clear(); + mObjectElementList.clear(); +} + +void RenderPrePassMgr::render( SceneRenderState *state ) +{ + PROFILE_SCOPE(RenderPrePassMgr_render); + + // Take a look at the SceneRenderState and see if we should skip drawing the pre-pass + if ( state->disableAdvancedLightingBins() ) + return; + + // NOTE: We don't early out here when the element list is + // zero because we need the prepass to be cleared. + + // Automagically save & restore our viewport and transforms. + GFXTransformSaver saver; + + GFXDEBUGEVENT_SCOPE( RenderPrePassMgr_Render, ColorI::RED ); + + // Tell the superclass we're about to render + const bool isRenderingToTarget = _onPreRender(state); + + // Clear all the buffers to white so that the + // default depth is to the far plane. + GFX->clear( GFXClearTarget | GFXClearZBuffer | GFXClearStencil, ColorI::WHITE, 1.0f, 0); + + // Restore transforms + MatrixSet &matrixSet = getRenderPass()->getMatrixSet(); + matrixSet.restoreSceneViewProjection(); + const MatrixF worldViewXfm = GFX->getWorldMatrix(); + + // Setup the default prepass material for object instances. + if ( !mPrePassMatInstance ) + _createPrePassMaterial(); + if ( mPrePassMatInstance ) + { + matrixSet.setWorld(MatrixF::Identity); + mPrePassMatInstance->setTransforms(matrixSet, state); + } + + // Signal start of pre-pass + getRenderSignal().trigger( state, this, true ); + + // First do a loop and render all the terrain... these are + // usually the big blockers in a scene and will save us fillrate + // on the smaller meshes and objects. + + // The terrain doesn't need any scene graph data + // in the the prepass... so just clear it. + SceneData sgData; + sgData.init( state, SceneData::PrePassBin ); + + Vector< MainSortElem >::const_iterator itr = mTerrainElementList.begin(); + for ( ; itr != mTerrainElementList.end(); itr++ ) + { + TerrainRenderInst *ri = static_cast( itr->inst ); + + TerrainCellMaterial *mat = ri->cellMat->getPrePassMat(); + + GFX->setPrimitiveBuffer( ri->primBuff ); + GFX->setVertexBuffer( ri->vertBuff ); + + mat->setTransformAndEye( *ri->objectToWorldXfm, + worldViewXfm, + GFX->getProjectionMatrix(), + state->getFarPlane() ); + + while ( mat->setupPass( state, sgData ) ) + GFX->drawPrimitive( ri->prim ); + } + + + // Next render all the meshes. + itr = mElementList.begin(); + for ( ; itr != mElementList.end(); ) + { + MeshRenderInst *ri = static_cast( itr->inst ); + + // Get the prepass material. + BaseMatInstance *mat = getPrePassMaterial( ri->matInst ); + + // Set up SG data proper like and flag it + // as a pre-pass render + setupSGData( ri, sgData ); + + Vector< MainSortElem >::const_iterator meshItr, endOfBatchItr = itr; + + while ( mat->setupPass( state, sgData ) ) + { + meshItr = itr; + for ( ; meshItr != mElementList.end(); meshItr++ ) + { + MeshRenderInst *passRI = static_cast( meshItr->inst ); + + // Check to see if we need to break this batch. + // + // NOTE: We're comparing the non-prepass materials + // here so we don't incur the cost of looking up the + // prepass hook on each inst. + // + if ( newPassNeeded( ri, passRI ) ) + break; + + // Set up SG data for this instance. + setupSGData( passRI, sgData ); + + matrixSet.setWorld(*passRI->objectToWorld); + matrixSet.setView(*passRI->worldToCamera); + matrixSet.setProjection(*passRI->projection); + + mat->setSceneInfo(state, sgData); + mat->setTransforms(matrixSet, state); + + // If we're instanced then don't render yet. + if ( mat->isInstanced() ) + { + // Let the material increment the instance buffer, but + // break the batch if it runs out of room for more. + if ( !mat->stepInstance() ) + { + meshItr++; + break; + } + + continue; + } + + // Setup the vertex and index buffers. + mat->setBuffers( passRI->vertBuff, passRI->primBuff ); + + // Render this sucker. + if ( passRI->prim ) + GFX->drawPrimitive( *passRI->prim ); + else + GFX->drawPrimitive( passRI->primBuffIndex ); + } + + // Draw the instanced batch. + if ( mat->isInstanced() ) + { + // Sets the buffers including the instancing stream. + mat->setBuffers( ri->vertBuff, ri->primBuff ); + + if ( ri->prim ) + GFX->drawPrimitive( *ri->prim ); + else + GFX->drawPrimitive( ri->primBuffIndex ); + } + + endOfBatchItr = meshItr; + + } // while( mat->setupPass(state, sgData) ) + + // Force the increment if none happened, otherwise go to end of batch. + itr = ( itr == endOfBatchItr ) ? itr + 1 : endOfBatchItr; + } + + // The final loop is for object render instances. + itr = mObjectElementList.begin(); + for ( ; itr != mObjectElementList.end(); itr++ ) + { + ObjectRenderInst *ri = static_cast( itr->inst ); + if ( ri->renderDelegate ) + ri->renderDelegate( ri, state, mPrePassMatInstance ); + } + + // Signal end of pre-pass + getRenderSignal().trigger( state, this, false ); + + if(isRenderingToTarget) + _onPostRender(); +} + +const GFXStateBlockDesc & RenderPrePassMgr::getOpaqueStenciWriteDesc( bool lightmappedGeometry /*= true*/ ) +{ + static bool sbInit = false; + static GFXStateBlockDesc sOpaqueStaticLitStencilWriteDesc; + static GFXStateBlockDesc sOpaqueDynamicLitStencilWriteDesc; + + if(!sbInit) + { + sbInit = true; + + // Build the static opaque stencil write/test state block descriptions + sOpaqueStaticLitStencilWriteDesc.stencilDefined = true; + sOpaqueStaticLitStencilWriteDesc.stencilEnable = true; + sOpaqueStaticLitStencilWriteDesc.stencilWriteMask = 0x03; + sOpaqueStaticLitStencilWriteDesc.stencilMask = 0x03; + sOpaqueStaticLitStencilWriteDesc.stencilRef = RenderPrePassMgr::OpaqueStaticLitMask; + sOpaqueStaticLitStencilWriteDesc.stencilPassOp = GFXStencilOpReplace; + sOpaqueStaticLitStencilWriteDesc.stencilFailOp = GFXStencilOpKeep; + sOpaqueStaticLitStencilWriteDesc.stencilZFailOp = GFXStencilOpKeep; + sOpaqueStaticLitStencilWriteDesc.stencilFunc = GFXCmpAlways; + + // Same only dynamic + sOpaqueDynamicLitStencilWriteDesc = sOpaqueStaticLitStencilWriteDesc; + sOpaqueDynamicLitStencilWriteDesc.stencilRef = RenderPrePassMgr::OpaqueDynamicLitMask; + } + + return (lightmappedGeometry ? sOpaqueStaticLitStencilWriteDesc : sOpaqueDynamicLitStencilWriteDesc); +} + +const GFXStateBlockDesc & RenderPrePassMgr::getOpaqueStencilTestDesc() +{ + static bool sbInit = false; + static GFXStateBlockDesc sOpaqueStencilTestDesc; + + if(!sbInit) + { + // Build opaque test + sbInit = true; + sOpaqueStencilTestDesc.stencilDefined = true; + sOpaqueStencilTestDesc.stencilEnable = true; + sOpaqueStencilTestDesc.stencilWriteMask = 0xFE; + sOpaqueStencilTestDesc.stencilMask = 0x03; + sOpaqueStencilTestDesc.stencilRef = 0; + sOpaqueStencilTestDesc.stencilPassOp = GFXStencilOpKeep; + sOpaqueStencilTestDesc.stencilFailOp = GFXStencilOpKeep; + sOpaqueStencilTestDesc.stencilZFailOp = GFXStencilOpKeep; + sOpaqueStencilTestDesc.stencilFunc = GFXCmpLess; + } + + return sOpaqueStencilTestDesc; +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + + +ProcessedPrePassMaterial::ProcessedPrePassMaterial( Material& mat, const RenderPrePassMgr *prePassMgr ) +: Parent(mat), mPrePassMgr(prePassMgr) +{ + +} + +void ProcessedPrePassMaterial::_determineFeatures( U32 stageNum, + MaterialFeatureData &fd, + const FeatureSet &features ) +{ + Parent::_determineFeatures( stageNum, fd, features ); + + // Find this for use down below... + bool bEnableMRTLightmap = false; + AdvancedLightBinManager *lightBin; + if ( Sim::findObject( "AL_LightBinMgr", lightBin ) ) + bEnableMRTLightmap = lightBin->MRTLightmapsDuringPrePass(); + + // If this material has a lightmap or tonemap (texture or baked vertex color), + // it must be static. Otherwise it is dynamic. + mIsLightmappedGeometry = ( fd.features.hasFeature( MFT_ToneMap ) || + fd.features.hasFeature( MFT_LightMap ) || + fd.features.hasFeature( MFT_VertLit ) || + ( bEnableMRTLightmap && fd.features.hasFeature( MFT_IsTranslucent ) || + fd.features.hasFeature( MFT_ForwardShading ) || + fd.features.hasFeature( MFT_IsTranslucentZWrite ) ) ); + + // Integrate proper opaque stencil write state + mUserDefined.addDesc( mPrePassMgr->getOpaqueStenciWriteDesc( mIsLightmappedGeometry ) ); + + FeatureSet newFeatures; + + // These are always on for prepass. + newFeatures.addFeature( MFT_EyeSpaceDepthOut ); + newFeatures.addFeature( MFT_PrePassConditioner ); + +#ifndef TORQUE_DEDICATED + + for ( U32 i=0; i < fd.features.getCount(); i++ ) + { + const FeatureType &type = fd.features.getAt( i ); + + // Turn on the diffuse texture only if we + // have alpha test. + if ( type == MFT_AlphaTest ) + { + newFeatures.addFeature( MFT_AlphaTest ); + newFeatures.addFeature( MFT_DiffuseMap ); + } + + else if ( type == MFT_IsTranslucentZWrite ) + { + newFeatures.addFeature( MFT_IsTranslucentZWrite ); + newFeatures.addFeature( MFT_DiffuseMap ); + } + + // Always allow these. + else if ( type == MFT_IsDXTnm || + type == MFT_TexAnim || + type == MFT_NormalMap || + type == MFT_DetailNormalMap || + type == MFT_AlphaTest || + type == MFT_Parallax || + type == MFT_InterlacedPrePass || + type == MFT_Visibility || + type == MFT_UseInstancing || + type == MFT_DiffuseVertColor ) + newFeatures.addFeature( type ); + + // Add any transform features. + else if ( type.getGroup() == MFG_PreTransform || + type.getGroup() == MFG_Transform || + type.getGroup() == MFG_PostTransform ) + newFeatures.addFeature( type ); + } + + // If there is lightmapped geometry support, add the MRT light buffer features + if(bEnableMRTLightmap) + { + // If this material has a lightmap, pass it through, and flag it to + // send it's output to RenderTarget1 + if( fd.features.hasFeature( MFT_ToneMap ) ) + { + newFeatures.addFeature( MFT_ToneMap ); + newFeatures.addFeature( MFT_LightbufferMRT ); + } + else if( fd.features.hasFeature( MFT_LightMap ) ) + { + newFeatures.addFeature( MFT_LightMap ); + newFeatures.addFeature( MFT_LightbufferMRT ); + } + else if( fd.features.hasFeature( MFT_VertLit ) ) + { + // Flag un-tone-map if necesasary + if( fd.features.hasFeature( MFT_DiffuseMap ) ) + newFeatures.addFeature( MFT_VertLitTone ); + + newFeatures.addFeature( MFT_VertLit ); + newFeatures.addFeature( MFT_LightbufferMRT ); + } + else + { + // If this object isn't lightmapped, add a zero-output feature to it + newFeatures.addFeature( MFT_RenderTarget1_Zero ); + } + } + +#endif + + // Set the new features. + fd.features = newFeatures; +} + +U32 ProcessedPrePassMaterial::getNumStages() +{ + // Return 1 stage so this material gets processed for sure + return 1; +} + +void ProcessedPrePassMaterial::addStateBlockDesc(const GFXStateBlockDesc& desc) +{ + GFXStateBlockDesc prePassStateBlock = desc; + + // Adjust color writes if this is a pure z-fill pass + const bool pixelOutEnabled = mPrePassMgr->getTargetChainLength() > 0; + if ( !pixelOutEnabled ) + { + prePassStateBlock.colorWriteDefined = true; + prePassStateBlock.colorWriteRed = pixelOutEnabled; + prePassStateBlock.colorWriteGreen = pixelOutEnabled; + prePassStateBlock.colorWriteBlue = pixelOutEnabled; + prePassStateBlock.colorWriteAlpha = pixelOutEnabled; + } + + // Never allow the alpha test state when rendering + // the prepass as we use the alpha channel for the + // depth information... MFT_AlphaTest will handle it. + prePassStateBlock.alphaDefined = true; + prePassStateBlock.alphaTestEnable = false; + + // If we're translucent then we're doing prepass blending + // which never writes to the depth channels. + const bool isTranslucent = getMaterial()->isTranslucent(); + if ( isTranslucent ) + { + prePassStateBlock.setBlend( true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha ); + prePassStateBlock.setColorWrites( true, true, false, false ); + } + + // Enable z reads, but only enable zwrites if we're not translucent. + prePassStateBlock.setZReadWrite( true, isTranslucent ? false : true ); + + // Pass to parent + Parent::addStateBlockDesc(prePassStateBlock); +} + +PrePassMatInstance::PrePassMatInstance(MatInstance* root, const RenderPrePassMgr *prePassMgr) +: Parent(*root->getMaterial()), mPrePassMgr(prePassMgr) +{ + mFeatureList = root->getRequestedFeatures(); + mVertexFormat = root->getVertexFormat(); + mUserObject = root->getUserObject(); +} + +PrePassMatInstance::~PrePassMatInstance() +{ +} + +ProcessedMaterial* PrePassMatInstance::getShaderMaterial() +{ + return new ProcessedPrePassMaterial(*mMaterial, mPrePassMgr); +} + +bool PrePassMatInstance::init( const FeatureSet &features, + const GFXVertexFormat *vertexFormat ) +{ + return Parent::init( features, vertexFormat ); +} + +PrePassMatInstanceHook::PrePassMatInstanceHook( MatInstance *baseMatInst, + const RenderPrePassMgr *prePassMgr ) + : mHookedPrePassMatInst(NULL), mPrePassManager(prePassMgr) +{ + // If the material is a custom material then + // hope that using DefaultPrePassMaterial gives + // them a good prepass. + if ( baseMatInst->isCustomMaterial() ) + { + MatInstance* dummyInst = static_cast( MATMGR->createMatInstance( "AL_DefaultPrePassMaterial", baseMatInst->getVertexFormat() ) ); + + mHookedPrePassMatInst = new PrePassMatInstance( dummyInst, prePassMgr ); + mHookedPrePassMatInst->init( dummyInst->getRequestedFeatures(), baseMatInst->getVertexFormat()); + + delete dummyInst; + return; + } + + // Create the prepass material instance. + mHookedPrePassMatInst = new PrePassMatInstance(baseMatInst, prePassMgr); + mHookedPrePassMatInst->getFeaturesDelegate() = baseMatInst->getFeaturesDelegate(); + + // Get the features, but remove the instancing feature if the + // original material didn't end up using it. + FeatureSet features = baseMatInst->getRequestedFeatures(); + if ( !baseMatInst->isInstanced() ) + features.removeFeature( MFT_UseInstancing ); + + // Initialize the material. + mHookedPrePassMatInst->init(features, baseMatInst->getVertexFormat()); +} + +PrePassMatInstanceHook::~PrePassMatInstanceHook() +{ + SAFE_DELETE(mHookedPrePassMatInst); +} + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +void LinearEyeDepthConditioner::processPix( Vector &componentList, const MaterialFeatureData &fd ) +{ + // find depth + ShaderFeature *depthFeat = FEATUREMGR->getByType( MFT_EyeSpaceDepthOut ); + AssertFatal( depthFeat != NULL, "No eye space depth feature found!" ); + + Var *depth = (Var*) LangElement::find(depthFeat->getOutputVarName()); + AssertFatal( depth, "Something went bad with ShaderGen. The depth should be already generated by the EyeSpaceDepthOut feature." ); + + MultiLine *meta = new MultiLine; + + meta->addStatement( assignOutput( depth ) ); + + output = meta; +} + +Var *LinearEyeDepthConditioner::_conditionOutput( Var *unconditionedOutput, MultiLine *meta ) +{ + Var *retVar = NULL; + + String fracMethodName = (GFX->getAdapterType() == OpenGL) ? "fract" : "frac"; + + switch(getBufferFormat()) + { + case GFXFormatR8G8B8A8: + retVar = new Var; + retVar->setType("float4"); + retVar->setName("_ppDepth"); + meta->addStatement( new GenOp( " // depth conditioner: packing to rgba\r\n" ) ); + meta->addStatement( new GenOp( + avar( " @ = %s(@ * (255.0/256) * float4(1, 255, 255 * 255, 255 * 255 * 255));\r\n", fracMethodName.c_str() ), + new DecOp(retVar), unconditionedOutput ) ); + break; + default: + retVar = unconditionedOutput; + meta->addStatement( new GenOp( " // depth conditioner: no conditioning\r\n" ) ); + break; + } + + AssertFatal( retVar != NULL, avar( "Cannot condition output to buffer format: %s", GFXStringTextureFormat[getBufferFormat()] ) ); + return retVar; +} + +Var *LinearEyeDepthConditioner::_unconditionInput( Var *conditionedInput, MultiLine *meta ) +{ + String float4Typename = (GFX->getAdapterType() == OpenGL) ? "vec4" : "float4"; + + Var *retVar = conditionedInput; + if(getBufferFormat() != GFXFormat_COUNT) + { + retVar = new Var; + retVar->setType(float4Typename.c_str()); + retVar->setName("_ppDepth"); + meta->addStatement( new GenOp( avar( " @ = %s(0, 0, 1, 1);\r\n", float4Typename.c_str() ), new DecOp(retVar) ) ); + + switch(getBufferFormat()) + { + case GFXFormatR32F: + case GFXFormatR16F: + meta->addStatement( new GenOp( " // depth conditioner: float texture\r\n" ) ); + meta->addStatement( new GenOp( " @.w = @.r;\r\n", retVar, conditionedInput ) ); + break; + + case GFXFormatR8G8B8A8: + meta->addStatement( new GenOp( " // depth conditioner: unpacking from rgba\r\n" ) ); + meta->addStatement( new GenOp( + avar( " @.w = dot(@ * (256.0/255), %s(1, 1 / 255, 1 / (255 * 255), 1 / (255 * 255 * 255)));\r\n", float4Typename.c_str() ) + , retVar, conditionedInput ) ); + break; + default: + AssertFatal(false, "LinearEyeDepthConditioner::_unconditionInput - Unrecognized buffer format"); + } + } + + return retVar; +} + +Var* LinearEyeDepthConditioner::printMethodHeader( MethodType methodType, const String &methodName, Stream &stream, MultiLine *meta ) +{ + const bool isCondition = ( methodType == ConditionerFeature::ConditionMethod ); + + Var *retVal = NULL; + + // The uncondition method inputs are changed + if( isCondition ) + retVal = Parent::printMethodHeader( methodType, methodName, stream, meta ); + else + { + Var *methodVar = new Var; + methodVar->setName(methodName); + if (GFX->getAdapterType() == OpenGL) + methodVar->setType("vec4"); + else + methodVar->setType("inline float4"); + DecOp *methodDecl = new DecOp(methodVar); + + Var *prepassSampler = new Var; + prepassSampler->setName("prepassSamplerVar"); + prepassSampler->setType("sampler2D"); + DecOp *prepassSamplerDecl = new DecOp(prepassSampler); + + Var *screenUV = new Var; + screenUV->setName("screenUVVar"); + if (GFX->getAdapterType() == OpenGL) + screenUV->setType("vec2"); + else + screenUV->setType("float2"); + DecOp *screenUVDecl = new DecOp(screenUV); + + Var *bufferSample = new Var; + bufferSample->setName("bufferSample"); + if (GFX->getAdapterType() == OpenGL) + bufferSample->setType("vec4"); + else + bufferSample->setType("float4"); + DecOp *bufferSampleDecl = new DecOp(bufferSample); + + meta->addStatement( new GenOp( "@(@, @)\r\n", methodDecl, prepassSamplerDecl, screenUVDecl ) ); + + meta->addStatement( new GenOp( "{\r\n" ) ); + + meta->addStatement( new GenOp( " // Sampler g-buffer\r\n" ) ); + + // The linear depth target has no mipmaps, so use tex2dlod when + // possible so that the shader compiler can optimize. + meta->addStatement( new GenOp( " #if TORQUE_SM >= 30\r\n" ) ); + if (GFX->getAdapterType() == OpenGL) + meta->addStatement( new GenOp( " @ = texture2DLod(@, @, 0); \r\n", bufferSampleDecl, prepassSampler, screenUV) ); + else + meta->addStatement( new GenOp( " @ = tex2Dlod(@, float4(@,0,0));\r\n", bufferSampleDecl, prepassSampler, screenUV ) ); + meta->addStatement( new GenOp( " #else\r\n" ) ); + if (GFX->getAdapterType() == OpenGL) + meta->addStatement( new GenOp( " @ = texture2D(@, @);\r\n", bufferSampleDecl, prepassSampler, screenUV) ); + else + meta->addStatement( new GenOp( " @ = tex2D(@, @);\r\n", bufferSampleDecl, prepassSampler, screenUV ) ); + meta->addStatement( new GenOp( " #endif\r\n\r\n" ) ); + + // We don't use this way of passing var's around, so this should cause a crash + // if something uses this improperly + retVal = bufferSample; + } + + return retVal; +} diff --git a/Engine/source/renderInstance/renderPrePassMgr.h b/Engine/source/renderInstance/renderPrePassMgr.h new file mode 100644 index 000000000..70a36eb84 --- /dev/null +++ b/Engine/source/renderInstance/renderPrePassMgr.h @@ -0,0 +1,204 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _PREPASS_MGR_H_ +#define _PREPASS_MGR_H_ + +#include "renderInstance/renderTexTargetBinManager.h" +#include "materials/matInstance.h" +#include "materials/processedShaderMaterial.h" +#include "shaderGen/conditionerFeature.h" +#include "core/util/autoPtr.h" + +// Forward declare +class PrePassMatInstance; + +// This render manager renders opaque objects to the z-buffer as a z-fill pass. +// It can optionally accumulate data from this opaque render pass into a render +// target for later use. +class RenderPrePassMgr : public RenderTexTargetBinManager +{ + typedef RenderTexTargetBinManager Parent; + +public: + + // registered buffer name + static const String BufferName; + + // Generic PrePass Render Instance Type + static const RenderInstType RIT_PrePass; + + RenderPrePassMgr( bool gatherDepth = true, + GFXFormat format = GFXFormatR16G16B16A16 ); + + virtual ~RenderPrePassMgr(); + + virtual void setPrePassMaterial( PrePassMatInstance *mat ); + + // RenderBinManager interface + virtual void render(SceneRenderState * state); + virtual void sort(); + virtual void clear(); + virtual void addElement( RenderInst *inst ); + + // ConsoleObject + DECLARE_CONOBJECT(RenderPrePassMgr); + + + typedef Signal RenderSignal; + + static RenderSignal& getRenderSignal(); + + static const U32 OpaqueStaticLitMask = BIT(1); ///< Stencil mask for opaque, lightmapped pixels + static const U32 OpaqueDynamicLitMask = BIT(0); ///< Stencil mask for opaque, dynamic lit pixels + + static const GFXStateBlockDesc &getOpaqueStencilTestDesc(); + static const GFXStateBlockDesc &getOpaqueStenciWriteDesc(bool lightmappedGeometry = true); + + virtual bool setTargetSize(const Point2I &newTargetSize); + + inline BaseMatInstance* getPrePassMaterial( BaseMatInstance *mat ); + +protected: + + /// The terrain render instance elements. + Vector< MainSortElem > mTerrainElementList; + + /// The object render instance elements. + Vector< MainSortElem > mObjectElementList; + + PrePassMatInstance *mPrePassMatInstance; + + virtual void _registerFeatures(); + virtual void _unregisterFeatures(); + virtual bool _updateTargets(); + virtual void _createPrePassMaterial(); + + bool _lightManagerActivate(bool active); +}; + +//------------------------------------------------------------------------------ + +class ProcessedPrePassMaterial : public ProcessedShaderMaterial +{ + typedef ProcessedShaderMaterial Parent; + +public: + ProcessedPrePassMaterial(Material& mat, const RenderPrePassMgr *prePassMgr); + + virtual U32 getNumStages(); + + virtual void addStateBlockDesc(const GFXStateBlockDesc& desc); + +protected: + virtual void _determineFeatures( U32 stageNum, MaterialFeatureData &fd, const FeatureSet &features ); + + const RenderPrePassMgr *mPrePassMgr; + bool mIsLightmappedGeometry; +}; + +//------------------------------------------------------------------------------ + +class PrePassMatInstance : public MatInstance +{ + typedef MatInstance Parent; + +public: + PrePassMatInstance(MatInstance* root, const RenderPrePassMgr *prePassMgr); + virtual ~PrePassMatInstance(); + + bool init() + { + return init( mFeatureList, mVertexFormat ); + } + + // MatInstance + virtual bool init( const FeatureSet &features, + const GFXVertexFormat *vertexFormat ); + +protected: + virtual ProcessedMaterial* getShaderMaterial(); + + const RenderPrePassMgr *mPrePassMgr; +}; + +//------------------------------------------------------------------------------ + +class PrePassMatInstanceHook : public MatInstanceHook +{ +public: + PrePassMatInstanceHook(MatInstance *baseMatInst, const RenderPrePassMgr *prePassMgr); + virtual ~PrePassMatInstanceHook(); + + virtual PrePassMatInstance *getPrePassMatInstance() { return mHookedPrePassMatInst; } + + virtual const MatInstanceHookType& getType() const { return Type; } + + /// The type for prepass material hooks. + static const MatInstanceHookType Type; + +protected: + PrePassMatInstance *mHookedPrePassMatInst; + const RenderPrePassMgr *mPrePassManager; +}; + +//------------------------------------------------------------------------------ + +// A very simple, default depth conditioner feature +class LinearEyeDepthConditioner : public ConditionerFeature +{ + typedef ConditionerFeature Parent; + +public: + LinearEyeDepthConditioner(const GFXFormat bufferFormat) + : Parent(bufferFormat) + { + + } + + virtual String getName() + { + return "Linear Eye-Space Depth Conditioner"; + } + + virtual void processPix( Vector &componentList, const MaterialFeatureData &fd ); +protected: + virtual Var *_conditionOutput( Var *unconditionedOutput, MultiLine *meta ); + virtual Var *_unconditionInput( Var *conditionedInput, MultiLine *meta ); + + virtual Var *printMethodHeader( MethodType methodType, const String &methodName, Stream &stream, MultiLine *meta ); +}; + + +inline BaseMatInstance* RenderPrePassMgr::getPrePassMaterial( BaseMatInstance *mat ) +{ + PrePassMatInstanceHook *hook = static_cast( mat->getHook( PrePassMatInstanceHook::Type ) ); + if ( !hook ) + { + hook = new PrePassMatInstanceHook( static_cast( mat ), this ); + mat->addHook( hook ); + } + + return hook->getPrePassMatInstance(); +} + +#endif // _PREPASS_MGR_H_ + diff --git a/Engine/source/renderInstance/renderTerrainMgr.cpp b/Engine/source/renderInstance/renderTerrainMgr.cpp new file mode 100644 index 000000000..f298ff51c --- /dev/null +++ b/Engine/source/renderInstance/renderTerrainMgr.cpp @@ -0,0 +1,200 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "renderInstance/renderTerrainMgr.h" + +#include "platform/profiler.h" +#include "scene/sceneManager.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/gfxDrawUtil.h" +#include "gfx/gfxDebugEvent.h" +#include "materials/shaderData.h" +#include "materials/matInstance.h" +#include "scene/sceneRenderState.h" +#include "console/consoleTypes.h" +#include "terrain/terrCell.h" +#include "terrain/terrCellMaterial.h" +#include "math/util/matrixSet.h" + +bool RenderTerrainMgr::smRenderWireframe = false; + +S32 RenderTerrainMgr::smCellsRendered = 0; +S32 RenderTerrainMgr::smOverrideCells = 0; +S32 RenderTerrainMgr::smDrawCalls = 0; + + +IMPLEMENT_CONOBJECT(RenderTerrainMgr); + + +ConsoleDocClass( RenderTerrainMgr, + "@brief A render bin for terrain mesh rendering.\n\n" + "This bin renders terrain render instances from a TerrainBlock. " + "Normally a mesh would render via the RenderMeshMgr, but terrain uses " + "a TerrainMaterial designed for multi-layered surfaces which this " + "bin can processs.\n\n" + "@ingroup RenderBin\n" ); + +RenderTerrainMgr::RenderTerrainMgr() + : RenderBinManager( RenderPassManager::RIT_Terrain, 1.0f, 1.0f ) +{ +} + +RenderTerrainMgr::RenderTerrainMgr( F32 renderOrder, F32 processAddOrder ) + : RenderBinManager( RenderPassManager::RIT_Terrain, renderOrder, processAddOrder ) +{ +} + +RenderTerrainMgr::~RenderTerrainMgr() +{ +} + +void RenderTerrainMgr::initPersistFields() +{ + Con::addVariable( "RenderTerrainMgr::renderWireframe", TypeBool, &smRenderWireframe, + "Used to enable wireframe rendering on terrain for debugging.\n" + "@ingroup RenderBin\n" ); + + // For stats. + GFXDevice::getDeviceEventSignal().notify( &RenderTerrainMgr::_clearStats ); + Con::addVariable( "$TerrainBlock::cellsRendered", TypeS32, &smCellsRendered, "@internal" ); + Con::addVariable( "$TerrainBlock::overrideCells", TypeS32, &smOverrideCells, "@internal" ); + Con::addVariable( "$TerrainBlock::drawCalls", TypeS32, &smDrawCalls, "@internal" ); + + Parent::initPersistFields(); +} + +bool RenderTerrainMgr::_clearStats( GFXDevice::GFXDeviceEventType type ) +{ + if ( type == GFXDevice::deStartOfFrame ) + { + smCellsRendered = 0; + smOverrideCells = 0; + smDrawCalls = 0; + } + + return true; +} + +void RenderTerrainMgr::internalAddElement( RenderInst *inst_ ) +{ + TerrainRenderInst *inst = static_cast( inst_ ); + mInstVector.push_back( inst ); +} + +void RenderTerrainMgr::sort() +{ + // TODO: We could probably sort this in some + // manner to improve terrain rendering perf. +} + +void RenderTerrainMgr::clear() +{ + mInstVector.clear(); +} + +void RenderTerrainMgr::render( SceneRenderState *state ) +{ + if ( mInstVector.empty() ) + return; + + PROFILE_SCOPE( RenderTerrainMgr_Render ); + + GFXTransformSaver saver; + + // Prepare the common scene graph data. + SceneData sgData; + sgData.init( state ); + sgData.wireframe |= smRenderWireframe; + + // Restore transforms + MatrixSet &matrixSet = getRenderPass()->getMatrixSet(); + matrixSet.restoreSceneViewProjection(); + + GFXDEBUGEVENT_SCOPE( RenderTerrainMgr_Render, ColorI::GREEN ); + + const MatrixF worldViewXfm = matrixSet.getWorldToCamera(); + const MatrixF &projXfm = matrixSet.getCameraToScreen(); + + // HACKTASTIC! + // + // If this is a shadow pass then render the terrain + // with the first material we get. + // + // In the near future terrains will operate using regular + // MatInstance materials like any other mesh in which case + // this will all be replaced by a normal mesh render bin. + // + if ( state->isShadowPass() ) + { + PROFILE_SCOPE( RenderTerrainMgr_Render_Shadow ); + + Vector::iterator inst = mInstVector.begin(); + BaseMatInstance *overideMat = (*inst)->mat; + while ( overideMat && overideMat->setupPass( state, sgData ) ) + { + for ( ; inst != mInstVector.end(); inst++ ) + { + smOverrideCells++; + + GFX->setPrimitiveBuffer( (*inst)->primBuff ); + GFX->setVertexBuffer( (*inst)->vertBuff ); + + matrixSet.setWorld(*(*inst)->objectToWorldXfm); + + overideMat->setSceneInfo( state, sgData ); + overideMat->setTransforms( matrixSet, state ); + + GFX->drawPrimitive( (*inst)->prim ); + } + } + + return; + } + + // Do the detail map passes. + Vector::iterator inst = mInstVector.begin(); + for ( ; inst != mInstVector.end(); inst++ ) + { + TerrainCellMaterial *mat = (*inst)->cellMat; + + GFX->setPrimitiveBuffer( (*inst)->primBuff ); + GFX->setVertexBuffer( (*inst)->vertBuff ); + + ++smCellsRendered; + + mat->setTransformAndEye( *(*inst)->objectToWorldXfm, + worldViewXfm, + projXfm, + state->getFarPlane() ); + + sgData.objTrans = (*inst)->objectToWorldXfm; + dMemcpy( sgData.lights, (*inst)->lights, sizeof( sgData.lights ) ); + + while ( mat->setupPass( state, sgData ) ) + { + ++smDrawCalls; + GFX->drawPrimitive( (*inst)->prim ); + } + } +} + diff --git a/Engine/source/renderInstance/renderTerrainMgr.h b/Engine/source/renderInstance/renderTerrainMgr.h new file mode 100644 index 000000000..c235588db --- /dev/null +++ b/Engine/source/renderInstance/renderTerrainMgr.h @@ -0,0 +1,105 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _RENDERTERRAINMGR_H_ +#define _RENDERTERRAINMGR_H_ + +#ifndef _RENDERBINMANAGER_H_ +#include "renderInstance/renderBinManager.h" +#endif +#ifndef _GFXVERTEXBUFFER_H_ +#include "gfx/gfxVertexBuffer.h" +#endif +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif + +class TerrCell; +class GFXTextureObject; +class TerrainCellMaterial; + + +/// The render instance for terrain cells. +struct TerrainRenderInst : public RenderInst +{ + GFXVertexBuffer *vertBuff; + + GFXPrimitiveBuffer *primBuff; + + GFXPrimitive prim; + + BaseMatInstance *mat; + + const MatrixF *objectToWorldXfm; + + TerrainCellMaterial *cellMat; + + /// The lights we pass to the material for + /// this cell in order light importance. + LightInfo *lights[8]; + + void clear() + { + dMemset( this, 0, sizeof( TerrainRenderInst ) ); + type = RenderPassManager::RIT_Terrain; + } +}; + + +/// +class RenderTerrainMgr : public RenderBinManager +{ + typedef RenderBinManager Parent; + +protected: + + Vector mInstVector; + + static bool smRenderWireframe; + + static S32 smCellsRendered; + static S32 smOverrideCells; + static S32 smDrawCalls; + + static bool _clearStats( GFXDevice::GFXDeviceEventType type ); + + // RenderBinManager + virtual void internalAddElement( RenderInst *inst ); + +public: + + RenderTerrainMgr(); + RenderTerrainMgr( F32 renderOrder, F32 processAddOrder ); + virtual ~RenderTerrainMgr(); + + // ConsoleObject + static void initPersistFields(); + DECLARE_CONOBJECT(RenderTerrainMgr); + + // RenderBinManager + virtual void sort(); + virtual void render( SceneRenderState *state ); + virtual void clear(); + +}; + +#endif // _RENDERTERRAINMGR_H_ diff --git a/Engine/source/renderInstance/renderTexTargetBinManager.cpp b/Engine/source/renderInstance/renderTexTargetBinManager.cpp new file mode 100644 index 000000000..6dab6256a --- /dev/null +++ b/Engine/source/renderInstance/renderTexTargetBinManager.cpp @@ -0,0 +1,309 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "renderInstance/renderTexTargetBinManager.h" + +#include "shaderGen/conditionerFeature.h" +#include "core/util/safeDelete.h" +#include "gfx/gfxTextureManager.h" +#include "gfx/gfxStringEnumTranslate.h" +#include "scene/sceneRenderState.h" +#include "scene/sceneManager.h" + + +IMPLEMENT_CONOBJECT(RenderTexTargetBinManager); + +ConsoleDocClass( RenderTexTargetBinManager, + "@brief An abstract base class for render bin managers that render to a named textue target.\n\n" + "This bin itself doesn't do any rendering work. It offers functionality to manage " + "a texture render target which derived render bin classes can render into.\n\n" + "@see RenderPrePassMgr\n" + "@ingroup RenderBin\n" ); + + +RenderTexTargetBinManager::RenderTexTargetBinManager( const RenderInstType& ritype, + F32 renderOrder, + F32 processAddOrder, + const GFXFormat targetFormat, + const Point2I &targetSize, + const U32 targetChainLength ) + + : Parent( ritype, renderOrder, processAddOrder ), + mTargetFormat(targetFormat), + mTargetSize(targetSize), + mTargetScale(1.0f, 1.0f), + mTargetSizeType(FixedSize), + mTargetChainLength(targetChainLength), + mTargetChainIdx(0), + mNumRenderTargets(1), + mTargetChain(NULL), + mTargetChainTextures(NULL) + #ifndef TORQUE_SHIPPING + ,m_NeedsOnPostRender(false) + #endif +{ + GFXDevice::getDeviceEventSignal().notify(this, &RenderTexTargetBinManager::_handleGFXEvent); + GFXTextureManager::addEventDelegate( this, &RenderTexTargetBinManager::_onTextureEvent ); + + mNamedTarget.setSamplerState( GFXSamplerStateDesc::getClampPoint() ); +} + +RenderTexTargetBinManager::~RenderTexTargetBinManager() +{ + _teardownTargets(); + + GFXTextureManager::removeEventDelegate( this, &RenderTexTargetBinManager::_onTextureEvent ); + GFXDevice::getDeviceEventSignal().remove(this, &RenderTexTargetBinManager::_handleGFXEvent); +} + +bool RenderTexTargetBinManager::onAdd() +{ + if(!Parent::onAdd()) + return false; + + _setupTargets(); + + return true; +} + +ImplementEnumType( RenderTexTargetSize, + "What size to render the target texture. Sizes are based on the Window the render is occuring in.\n" + "@ingroup RenderBin\n\n") + { RenderTexTargetBinManager::WindowSize, "windowsize", "Render to the size of the window.\n" }, + { RenderTexTargetBinManager::WindowSizeScaled, "windowsizescaled", "Render to the size of the window, scaled to the render target's size.\n" }, + { RenderTexTargetBinManager::FixedSize, "fixedsize", "Don't scale the target texture, and render to its default size.\n" }, +EndImplementEnumType; + +void RenderTexTargetBinManager::initPersistFields() +{ + // TOM_TODO: + //addField( "targetScale", mTargetScale ); + //addPropertyNOPS( "targetSizeType", mTargetSizeType)->setEnumTable(gSizeTypeEnumTable); + //addPropertyNOPS( "targetFormat")->setEnumTable(gTextureFormatEnumTable)->addGet(this, &RenderTexTargetBinManager::getTargetFormatConsole)->addSet(this, &RenderTexTargetBinManager::setTargetFormatConsole); + //addProperty( "blur" )->addSet(this, &RenderTexTargetBinManager::setBlur)->addGet(this, &RenderTexTargetBinManager::getBlur); + + Parent::initPersistFields(); +} + +bool RenderTexTargetBinManager::setTargetSize(const Point2I &newTargetSize) +{ + if( GFX->getAdapterType() != OpenGL && // Targets need to match up exactly in size on OpenGL. + mTargetSize.x >= newTargetSize.x && + mTargetSize.y >= newTargetSize.y ) + return true; + + mTargetSize = newTargetSize; + mNamedTarget.setViewport( RectI( Point2I::Zero, mTargetSize ) ); + + return _updateTargets(); +} + +bool RenderTexTargetBinManager::setTargetFormat(const GFXFormat &newTargetFormat) +{ + if(mTargetFormat == newTargetFormat) + return true; + + mTargetFormat = newTargetFormat; + ConditionerFeature *conditioner = mNamedTarget.getConditioner(); + if(conditioner) + conditioner->setBufferFormat(mTargetFormat); + + return _updateTargets(); +} + +void RenderTexTargetBinManager::setTargetChainLength(const U32 chainLength) +{ + if(mTargetChainLength != chainLength) + { + mTargetChainLength = chainLength; + _setupTargets(); + } +} + +GFXTextureObject* RenderTexTargetBinManager::getTargetTexture( U32 mrtIndex, S32 chainIndex ) const +{ + const U32 chainIdx = ( chainIndex > -1 ) ? chainIndex : mTargetChainIdx; + if(chainIdx < mTargetChainLength) + return mTargetChainTextures[chainIdx][mrtIndex]; + return NULL; +} + +bool RenderTexTargetBinManager::_updateTargets() +{ + PROFILE_SCOPE(RenderTexTargetBinManager_updateTargets); + + bool ret = true; + + mNamedTarget.release(); + + // Update the target size + for( U32 i = 0; i < mTargetChainLength; i++ ) + { + AssertFatal( mTargetChain != NULL, "RenderTexTargetBinManager - target chain not set up" ); + + if( !mTargetChain[i] ) + mTargetChain[i] = GFX->allocRenderToTextureTarget(); + + for( U32 j = 0; j < mNumRenderTargets; j++ ) + { + ret &= mTargetChainTextures[i][j].set( mTargetSize.x, mTargetSize.y, mTargetFormat, + &GFXDefaultRenderTargetProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ), + 1, GFXTextureManager::AA_MATCH_BACKBUFFER ); + + mTargetChain[i]->attachTexture( GFXTextureTarget::RenderSlot(GFXTextureTarget::Color0 + j), mTargetChainTextures[i][j] ); + } + } + + return ret; +} + +bool RenderTexTargetBinManager::_handleGFXEvent( GFXDevice::GFXDeviceEventType event_ ) +{ + if ( event_ == GFXDevice::deStartOfFrame ) + { + mTargetChainIdx++; + if ( mTargetChainIdx >= mTargetChainLength ) + mTargetChainIdx = 0; + } + + return true; +} + +void RenderTexTargetBinManager::_onTextureEvent( GFXTexCallbackCode code ) +{ + switch(code) + { + case GFXZombify: + _teardownTargets(); + break; + + case GFXResurrect: + _setupTargets(); + break; + } +} + +bool RenderTexTargetBinManager::_setupTargets() +{ + _teardownTargets(); + + mTargetChain = new GFXTextureTargetRef[mTargetChainLength]; + mTargetChainTextures = new GFXTexHandle*[mTargetChainLength]; + + for( U32 i = 0; i < mTargetChainLength; i++ ) + mTargetChainTextures[i] = new GFXTexHandle[mNumRenderTargets]; + + mTargetChainIdx = 0; + + mTargetSize = Point2I::Zero; + + return true; +} + +void RenderTexTargetBinManager::_teardownTargets() +{ + mNamedTarget.release(); + + SAFE_DELETE_ARRAY(mTargetChain); + if(mTargetChainTextures != NULL) + { + for( U32 i = 0; i < mTargetChainLength; i++ ) + SAFE_DELETE_ARRAY(mTargetChainTextures[i]); + } + SAFE_DELETE_ARRAY(mTargetChainTextures); +} + +GFXTextureTargetRef RenderTexTargetBinManager::_getTextureTarget(const U32 idx /* = 0 */) +{ + return mTargetChain[idx]; +} + +bool RenderTexTargetBinManager::_onPreRender(SceneRenderState * state, bool preserve /* = false */) +{ + PROFILE_SCOPE(RenderTexTargetBinManager_onPreRender); + +#ifndef TORQUE_SHIPPING + AssertFatal( m_NeedsOnPostRender == false, "_onPostRender not called on RenderTexTargetBinManager, or sub-class." ); + m_NeedsOnPostRender = false; +#endif + + // Update the render target size + const Point2I &rtSize = GFX->getActiveRenderTarget()->getSize(); + switch(mTargetSizeType) + { + case WindowSize: + setTargetSize(rtSize); + break; + case WindowSizeScaled: + { + Point2I scaledTargetSize(mFloor(rtSize.x * mTargetScale.x), mFloor(rtSize.y * mTargetScale.y)); + setTargetSize(scaledTargetSize); + break; + } + case FixedSize: + // No adjustment necessary + break; + } + + if( mTargetChainLength == 0 ) + return false; + + GFXTextureTargetRef binTarget = _getTextureTarget(mTargetChainIdx); + + if( binTarget.isNull() ) + return false; + + // Attach active depth target texture + binTarget->attachTexture(GFXTextureTarget::DepthStencil, getRenderPass()->getDepthTargetTexture()); + + // Preserve contents + if(preserve) + GFX->getActiveRenderTarget()->preserve(); + + GFX->pushActiveRenderTarget(); + GFX->setActiveRenderTarget(binTarget); + GFX->setViewport( mNamedTarget.getViewport() ); + + #ifndef TORQUE_SHIPPING + m_NeedsOnPostRender = true; + #endif + + return true; +} + +void RenderTexTargetBinManager::_onPostRender() +{ + PROFILE_SCOPE(RenderTexTargetBinManager_onPostRender); + + #ifndef TORQUE_SHIPPING + m_NeedsOnPostRender = false; + #endif + + GFXTextureTargetRef binTarget = _getTextureTarget(mTargetChainIdx); + binTarget->resolve(); + + GFX->popActiveRenderTarget(); + + for ( U32 i=0; i < mNumRenderTargets; i++ ) + mNamedTarget.setTexture( i, mTargetChainTextures[mTargetChainIdx][i] ); +} diff --git a/Engine/source/renderInstance/renderTexTargetBinManager.h b/Engine/source/renderInstance/renderTexTargetBinManager.h new file mode 100644 index 000000000..67270a191 --- /dev/null +++ b/Engine/source/renderInstance/renderTexTargetBinManager.h @@ -0,0 +1,135 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _TEXTARGETBIN_MGR_H_ +#define _TEXTARGETBIN_MGR_H_ + +#ifndef _RENDERBINMANAGER_H_ +#include "renderInstance/renderBinManager.h" +#endif +#ifndef _MATTEXTURETARGET_H_ +#include "materials/matTextureTarget.h" +#endif + +class ConditionerFeature; + + +class RenderTexTargetBinManager : public RenderBinManager +{ + typedef RenderBinManager Parent; + +public: + static const GFXFormat DefaultTargetFormat = GFXFormatR8G8B8A8; + static const U32 DefaultTargetChainLength = 1; + static const U32 DefaultTargetSize = 256; + + enum TargetSizeType + { + WindowSize = 0, + WindowSizeScaled, + FixedSize + }; + +public: + + RenderTexTargetBinManager( const RenderInstType &ritype = RenderPassManager::RIT_Mesh, + F32 renderOrder = 1.0f, + F32 processAddOrder = 1.0f, + const GFXFormat targetFormat = DefaultTargetFormat, + const Point2I &targetSize = Point2I(DefaultTargetSize, DefaultTargetSize), + const U32 targetChainLength = DefaultTargetChainLength); + + virtual ~RenderTexTargetBinManager(); + + virtual bool setTargetSize(const Point2I &newTargetSize); + + virtual GFXTextureObject* getTargetTexture( U32 mrtIndex, S32 chainIndex = -1 ) const; + + /// Force a target update + virtual bool updateTargets() { return _updateTargets(); } + + void setTargetFormatConsole(const S32 &fmt) { setTargetFormat(GFXFormat(fmt)); } + virtual bool setTargetFormat(const GFXFormat &newTargetFormat); + + S32 getTargetFormatConsole() { return getTargetFormat(); } + virtual const GFXFormat &getTargetFormat() const { return mTargetFormat; } + + virtual void setTargetChainLength(const U32 chainLength); + virtual U32 getTargetChainLength() const { return mTargetChainLength; } + + DECLARE_CONOBJECT(RenderTexTargetBinManager); + static void initPersistFields(); + virtual bool onAdd(); + +protected: + + NamedTexTarget mNamedTarget; + + GFXFormat mTargetFormat; + Point2I mTargetSize; + Point2F mTargetScale; + + //RectI mTargetViewport; + TargetSizeType mTargetSizeType; + + U32 mTargetChainLength; + U32 mTargetChainIdx; + U32 mNumRenderTargets; + GFXTextureTargetRef *mTargetChain; + GFXTexHandle **mTargetChainTextures; + +#ifndef TORQUE_SHIPPING + bool m_NeedsOnPostRender; +#endif + bool mPreserve; + + virtual bool _handleGFXEvent(GFXDevice::GFXDeviceEventType event); + virtual GFXTextureTargetRef _getTextureTarget(const U32 idx = 0); + + /// Pushes the active render target, and sets itself as a render target. The + /// target is then cleared using 'mTargetClearColor', viewport is set properly, + /// and true is returned, and '_onPostRender' must be called after rendering + /// is complete. If the return value is false, than '_onPostRender' + /// should not be called. + /// + /// @param preserve If set to true, the contents of the current render target + // will be the same when _onPostRender is called. Otherwise + // the contents are undefined on console platforms. + virtual bool _onPreRender(SceneRenderState * state, bool preserve = false); + + /// Resolves the active render target, pops the render target from _onPreRender, and sets debug info. + virtual void _onPostRender(); + + virtual bool _updateTargets(); + + virtual bool _setupTargets(); + + virtual void _teardownTargets(); + + /// The callback used to get texture events. + /// @see GFXTextureManager::addEventDelegate + void _onTextureEvent( GFXTexCallbackCode code ); +}; + +typedef RenderTexTargetBinManager::TargetSizeType RenderTexTargetSize; +DefineEnumType( RenderTexTargetSize ); + +#endif // _TEXTARGETBIN_MGR_H_ \ No newline at end of file diff --git a/Engine/source/renderInstance/renderTranslucentMgr.cpp b/Engine/source/renderInstance/renderTranslucentMgr.cpp new file mode 100644 index 000000000..b755e12c9 --- /dev/null +++ b/Engine/source/renderInstance/renderTranslucentMgr.cpp @@ -0,0 +1,280 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "renderInstance/renderTranslucentMgr.h" + +#include "materials/sceneData.h" +#include "scene/sceneManager.h" +#include "scene/sceneObject.h" +#include "scene/sceneRenderState.h" +#include "materials/matInstance.h" +#include "gfx/gfxPrimitiveBuffer.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/gfxDebugEvent.h" +#include "renderInstance/renderParticleMgr.h" +#include "math/util/matrixSet.h" + +#define HIGH_NUM ((U32(-1)/2) - 1) + +IMPLEMENT_CONOBJECT(RenderTranslucentMgr); + +ConsoleDocClass( RenderTranslucentMgr, + "@brief A render bin for rendering translucent meshes.\n\n" + "This bin is used to render translucent render mesh instances and render object " + "instances. It is generally ordered late in the RenderPassManager after all opaque " + "geometry bins.\n\n" + "@ingroup RenderBin\n" ); + + +RenderTranslucentMgr::RenderTranslucentMgr() + : RenderBinManager( RenderPassManager::RIT_Translucent, 1.0f, 1.0f ), mParticleRenderMgr(NULL) +{ + notifyType( RenderPassManager::RIT_ObjectTranslucent ); + notifyType( RenderPassManager::RIT_Particle ); +} + +RenderTranslucentMgr::~RenderTranslucentMgr() +{ +} + +void RenderTranslucentMgr::setupSGData(MeshRenderInst *ri, SceneData &data ) +{ + Parent::setupSGData( ri, data ); + + // We do not support these in the translucent bin. + data.backBuffTex = NULL; + data.cubemap = NULL; + data.lightmap = NULL; +} + +void RenderTranslucentMgr::addElement( RenderInst *inst ) +{ + // Right off the bat if its not translucent skip it. + if ( !inst->translucentSort ) + return; + + // What type of instance is this. + const bool isMeshInst = inst->type == RenderPassManager::RIT_Translucent; + + // Get its material if its a mesh. + BaseMatInstance* matInst = NULL; + if ( isMeshInst ) + matInst = static_cast( inst )->matInst; + + // If the material isn't translucent the skip it. + if ( matInst && !matInst->getMaterial()->isTranslucent() ) + return; + + // We made it this far, add the instance. + mElementList.increment(); + MainSortElem& elem = mElementList.last(); + elem.inst = inst; + + // Override the instances default key to be the sort distance. All + // the pointer dereferencing is in there to prevent us from losing + // information when converting to a U32. + elem.key = *((U32*)&inst->sortDistSq); + + AssertFatal( inst->defaultKey != 0, "RenderTranslucentMgr::addElement() - Got null sort key... did you forget to set it?" ); + + // Then use the instances primary key as our secondary key + elem.key2 = inst->defaultKey; +} + +GFXStateBlockRef RenderTranslucentMgr::_getStateBlock( U8 transFlag ) +{ + if ( mStateBlocks[transFlag].isValid() ) + return mStateBlocks[transFlag]; + + GFXStateBlockDesc d; + + d.cullDefined = true; + d.cullMode = GFXCullNone; + d.blendDefined = true; + d.blendEnable = true; + d.blendSrc = (GFXBlend)((transFlag >> 4) & 0x0f); + d.blendDest = (GFXBlend)(transFlag & 0x0f); + d.alphaDefined = true; + + // See http://www.garagegames.com/mg/forums/result.thread.php?qt=81397 + d.alphaTestEnable = (d.blendSrc == GFXBlendSrcAlpha && (d.blendDest == GFXBlendInvSrcAlpha || d.blendDest == GFXBlendOne)); + d.alphaTestRef = 1; + d.alphaTestFunc = GFXCmpGreaterEqual; + d.zDefined = true; + d.zWriteEnable = false; + d.samplersDefined = true; + d.samplers[0] = GFXSamplerStateDesc::getClampLinear(); + d.samplers[0].alphaOp = GFXTOPModulate; + d.samplers[0].alphaArg1 = GFXTATexture; + d.samplers[0].alphaArg2 = GFXTADiffuse; + + mStateBlocks[transFlag] = GFX->createStateBlock(d); + return mStateBlocks[transFlag]; +} + +void RenderTranslucentMgr::render( SceneRenderState *state ) +{ + PROFILE_SCOPE(RenderTranslucentMgr_render); + + // Early out if nothing to draw. + if(!mElementList.size()) + return; + + GFXDEBUGEVENT_SCOPE(RenderTranslucentMgr_Render, ColorI::BLUE); + + // Find the particle render manager (if we don't have it) + if(mParticleRenderMgr == NULL) + { + RenderPassManager *rpm = state->getRenderPass(); + for( U32 i = 0; i < rpm->getManagerCount(); i++ ) + { + RenderBinManager *bin = rpm->getManager(i); + if( bin->getRenderInstType() == RenderParticleMgr::RIT_Particles ) + { + mParticleRenderMgr = reinterpret_cast(bin); + break; + } + } + } + + GFXTransformSaver saver; + + SceneData sgData; + sgData.init( state ); + + GFXVertexBuffer * lastVB = NULL; + GFXPrimitiveBuffer * lastPB = NULL; + + // Restore transforms + MatrixSet &matrixSet = getRenderPass()->getMatrixSet(); + matrixSet.restoreSceneViewProjection(); + + U32 binSize = mElementList.size(); + for( U32 j=0; jtype == RenderPassManager::RIT_ObjectTranslucent ) + { + ObjectRenderInst* objRI = static_cast(baseRI); + objRI->renderDelegate( objRI, state, NULL ); + + lastVB = NULL; + lastPB = NULL; + j++; + continue; + } + else if ( baseRI->type == RenderPassManager::RIT_Particle ) + { + ParticleRenderInst *ri = static_cast(baseRI); + + // Tell Particle RM to draw the system. (This allows the particle render manager + // to manage drawing offscreen particle systems, and allows the systems + // to be composited back into the scene with proper translucent + // sorting order) + mParticleRenderMgr->renderInstance(ri, state); + + lastVB = NULL; // no longer valid, null it + lastPB = NULL; // no longer valid, null it + + j++; + continue; + } + else if ( baseRI->type == RenderPassManager::RIT_Translucent ) + { + MeshRenderInst* ri = static_cast(baseRI); + BaseMatInstance *mat = ri->matInst; + + setupSGData( ri, sgData ); + + while( mat->setupPass( state, sgData ) ) + { + U32 a; + for( a=j; atype != RenderPassManager::RIT_Translucent ) + break; + + MeshRenderInst *passRI = static_cast(nextRI); + + // Check to see if we need to break this batch. + if ( newPassNeeded( ri, passRI ) ) + break; + + // Z sorting and stuff is still not working in this mgr... + setupSGData( passRI, sgData ); + mat->setSceneInfo(state, sgData); + matrixSet.setWorld(*passRI->objectToWorld); + matrixSet.setView(*passRI->worldToCamera); + matrixSet.setProjection(*passRI->projection); + mat->setTransforms(matrixSet, state); + + // If we're instanced then don't render yet. + if ( mat->isInstanced() ) + { + // Let the material increment the instance buffer, but + // break the batch if it runs out of room for more. + if ( !mat->stepInstance() ) + { + a++; + break; + } + + continue; + } + + // Setup the vertex and index buffers. + mat->setBuffers( passRI->vertBuff, passRI->primBuff ); + + // Render this sucker. + if ( passRI->prim ) + GFX->drawPrimitive( *passRI->prim ); + else + GFX->drawPrimitive( passRI->primBuffIndex ); + } + + // Draw the instanced batch. + if ( mat->isInstanced() ) + { + // Sets the buffers including the instancing stream. + mat->setBuffers( ri->vertBuff, ri->primBuff ); + + // Render the instanced stream. + if ( ri->prim ) + GFX->drawPrimitive( *ri->prim ); + else + GFX->drawPrimitive( ri->primBuffIndex ); + } + + matListEnd = a; + } + + // force increment if none happened, otherwise go to end of batch + j = ( j == matListEnd ) ? j+1 : matListEnd; + } + } +} \ No newline at end of file diff --git a/Engine/source/renderInstance/renderTranslucentMgr.h b/Engine/source/renderInstance/renderTranslucentMgr.h new file mode 100644 index 000000000..e6464f7d9 --- /dev/null +++ b/Engine/source/renderInstance/renderTranslucentMgr.h @@ -0,0 +1,67 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _RENDER_TRANSLUCENT_MGR_H_ +#define _RENDER_TRANSLUCENT_MGR_H_ + +#ifndef _RENDERBINMANAGER_H_ +#include "renderInstance/renderBinManager.h" +#endif +#ifndef _MATTEXTURETARGET_H_ +#include "materials/matTextureTarget.h" +#endif + +class RenderParticleMgr; + +class RenderTranslucentMgr : public RenderBinManager +{ + typedef RenderBinManager Parent; + +public: + + RenderTranslucentMgr(); + RenderTranslucentMgr( F32 renderOrder, F32 processAddOrder ); + virtual ~RenderTranslucentMgr(); + + // RenderBinManager + void render(SceneRenderState * state); + void addElement( RenderInst *inst ); + void setupSGData( MeshRenderInst *ri, SceneData &data ); + + // ConsoleObject + DECLARE_CONOBJECT(RenderTranslucentMgr); + +protected: + // This is a stateblock per potential blend type, we create + // these as needed. + enum + { + MaxBlend = 256 + }; + GFXStateBlockRef mStateBlocks[MaxBlend]; + + GFXStateBlockRef _getStateBlock( U8 transFlag ); + RenderParticleMgr *mParticleRenderMgr;; +}; + + +#endif // _RENDER_TRANSLUCENT_MGR_H_ diff --git a/Engine/source/scene/culling/sceneCullingState.cpp b/Engine/source/scene/culling/sceneCullingState.cpp new file mode 100644 index 000000000..b8608160d --- /dev/null +++ b/Engine/source/scene/culling/sceneCullingState.cpp @@ -0,0 +1,934 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "scene/culling/sceneCullingState.h" + +#include "scene/sceneManager.h" +#include "scene/sceneObject.h" +#include "scene/zones/sceneZoneSpace.h" +#include "math/mathUtils.h" +#include "platform/profiler.h" +#include "terrain/terrData.h" +#include "util/tempAlloc.h" +#include "gfx/sim/debugDraw.h" + + +extern bool gEditingMission; + + +bool SceneCullingState::smDisableTerrainOcclusion = false; +bool SceneCullingState::smDisableZoneCulling = false; +U32 SceneCullingState::smMaxOccludersPerZone = 4; +F32 SceneCullingState::smOccluderMinWidthPercentage = 0.1f; +F32 SceneCullingState::smOccluderMinHeightPercentage = 0.1f; + + + +//----------------------------------------------------------------------------- + +SceneCullingState::SceneCullingState( SceneManager* sceneManager, const SceneCameraState& viewState ) + : mSceneManager( sceneManager ), + mCameraState( viewState ), + mDisableZoneCulling( smDisableZoneCulling ), + mDisableTerrainOcclusion( smDisableTerrainOcclusion ) +{ + AssertFatal( sceneManager->getZoneManager(), "SceneCullingState::SceneCullingState - SceneManager must have a zone manager!" ); + + VECTOR_SET_ASSOCIATION( mZoneStates ); + VECTOR_SET_ASSOCIATION( mAddedOccluderObjects ); + + // Allocate zone states. + + const U32 numZones = sceneManager->getZoneManager()->getNumZones(); + mZoneStates.setSize( numZones ); + dMemset( mZoneStates.address(), 0, sizeof( SceneZoneCullingState ) * numZones ); + + // Allocate the zone visibility flags. + + mZoneVisibilityFlags.setSize( numZones ); + mZoneVisibilityFlags.clear(); + + // Construct the root culling volume from + // the camera's view frustum. Omit the frustum's + // near and far plane so we don't test it repeatedly. + + const Frustum& frustum = mCameraState.getFrustum(); + PlaneF* planes = allocateData< PlaneF >( 4 ); + + planes[ 0 ] = frustum.getPlanes()[ Frustum::PlaneLeft ]; + planes[ 1 ] = frustum.getPlanes()[ Frustum::PlaneRight ]; + planes[ 2 ] = frustum.getPlanes()[ Frustum::PlaneTop]; + planes[ 3 ] = frustum.getPlanes()[ Frustum::PlaneBottom ]; + + mRootVolume = SceneCullingVolume( + SceneCullingVolume::Includer, + PlaneSetF( planes, 4 ) + ); +} + +//----------------------------------------------------------------------------- + +bool SceneCullingState::isWithinVisibleZone( SceneObject* object ) const +{ + for( SceneObject::ZoneRef* ref = object->_getZoneRefHead(); + ref != NULL; ref = ref->nextInObj ) + if( mZoneVisibilityFlags.test( ref->zone ) ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- + +void SceneCullingState::addOccluder( SceneObject* object ) +{ + PROFILE_SCOPE( SceneCullingState_addOccluder ); + + // If the occluder is itself occluded, don't add it. + // + // NOTE: We do allow near plane intersections here. Silhouette extraction + // should take that into account. + + if( cullObjects( &object, 1, DontCullRenderDisabled ) != 1 ) + return; + + // If the occluder has already been added, do nothing. Check this + // after the culling check since the same occluder can be added by + // two separate zones and not be visible in one yet be visible in the + // other. + + if( mAddedOccluderObjects.contains( object ) ) + return; + mAddedOccluderObjects.push_back( object ); + + // Let the object build a silhouette. If it doesn't + // return one, abort. + + Vector< Point3F > silhouette; + object->buildSilhouette( getCameraState(), silhouette ); + + if( silhouette.empty() || silhouette.size() < 3 ) + return; + + // Generate the culling volume. + + SceneCullingVolume volume; + if( !createCullingVolume( + silhouette.address(), + silhouette.size(), + SceneCullingVolume::Occluder, + volume ) ) + return; + + // Add the frustum to all zones that the object is assigned to. + + for( SceneObject::ZoneRef* ref = object->_getZoneRefHead(); ref != NULL; ref = ref->nextInObj ) + addCullingVolumeToZone( ref->zone, volume ); +} + +//----------------------------------------------------------------------------- + +bool SceneCullingState::addCullingVolumeToZone( U32 zoneId, const SceneCullingVolume& volume ) +{ + PROFILE_SCOPE( SceneCullingState_addCullingVolumeToZone ); + + AssertFatal( zoneId < mZoneStates.size(), "SceneCullingState::addCullingVolumeToZone - Zone ID out of range" ); + SceneZoneCullingState& zoneState = mZoneStates[ zoneId ]; + + // [rene, 07-Apr-10] I previously used to attempt to merge things here and detect whether + // the visibility state of the zone has changed at all. Since we allow polyhedra to be + // degenerate here and since polyhedra cannot be merged easily like frustums, I have opted + // to remove this for now. I'm also convinced that with the current traversal system it + // adds little benefit. + + // Link the volume to the zone state. + + typedef SceneZoneCullingState::CullingVolumeLink LinkType; + + LinkType* link = reinterpret_cast< LinkType* >( allocateData( sizeof( LinkType ) ) ); + + link->mVolume = volume; + link->mNext = zoneState.mCullingVolumes; + zoneState.mCullingVolumes = link; + + if( volume.isOccluder() ) + zoneState.mHaveOccluders = true; + else + zoneState.mHaveIncluders = true; + + // Mark sorting state as dirty. + + zoneState.mHaveSortedVolumes = false; + + // Set the visibility flag for the zone. + + if( volume.isIncluder() ) + mZoneVisibilityFlags.set( zoneId ); + + return true; +} + +//----------------------------------------------------------------------------- + +bool SceneCullingState::addCullingVolumeToZone( U32 zoneId, SceneCullingVolume::Type type, const AnyPolyhedron& polyhedron ) +{ + // Allocate space on our chunker. + + const U32 numPlanes = polyhedron.getNumPlanes(); + PlaneF* planes = allocateData< PlaneF >( numPlanes ); + + // Copy the planes over. + + dMemcpy( planes, polyhedron.getPlanes(), numPlanes * sizeof( planes[ 0 ] ) ); + + // Create a culling volume. + + SceneCullingVolume volume( + type, + PlaneSetF( planes, numPlanes ) + ); + + // And add it. + + return addCullingVolumeToZone( zoneId, volume ); +} + +//----------------------------------------------------------------------------- + +bool SceneCullingState::createCullingVolume( const Point3F* vertices, U32 numVertices, SceneCullingVolume::Type type, SceneCullingVolume& outVolume ) +{ + const Point3F& viewPos = getCameraState().getViewPosition(); + const Point3F& viewDir = getCameraState().getViewDirection(); + const bool isOrtho = getFrustum().isOrtho(); + + //TODO: check if we need to handle penetration of the near plane for occluders specially + + // Allocate space for the clipping planes we generate. Assume the worst case + // of every edge generating a plane and, for includers, all edges meeting at + // steep angles so we need to insert extra planes (the latter is not possible, + // of course, but it makes things less complicated here). For occluders, add + // an extra plane for the near cap. + + const U32 maxPlanes = ( type == SceneCullingVolume::Occluder ? numVertices + 1 : numVertices * 2 ); + PlaneF* planes = allocateData< PlaneF >( maxPlanes ); + + // Keep track of the world-space bounds of the polygon. We use this later + // to derive some metrics. + + Box3F wsPolyBounds; + + wsPolyBounds.minExtents = Point3F( TypeTraits< F32 >::MAX, TypeTraits< F32 >::MAX, TypeTraits< F32 >::MAX ); + wsPolyBounds.maxExtents = Point3F( TypeTraits< F32 >::MIN, TypeTraits< F32 >::MIN, TypeTraits< F32 >::MIN ); + + // For occluders, also keep track of the nearest, and two farthest silhouette points. We use + // this later to construct a near capping plane. + F32 minVertexDistanceSquared = TypeTraits< F32 >::MAX; + U32 leastDistantVert = 0; + + F32 maxVertexDistancesSquared[ 2 ] = { TypeTraits< F32 >::MIN, TypeTraits< F32 >::MIN }; + U32 mostDistantVertices[ 2 ] = { 0, 0 }; + + // Generate the extrusion volume. For orthographic projections, extrude + // parallel to the view direction whereas for parallel projections, extrude + // from the viewpoint. + + U32 numPlanes = 0; + U32 lastVertex = numVertices - 1; + bool invert = false; + + for( U32 i = 0; i < numVertices; lastVertex = i, ++ i ) + { + AssertFatal( numPlanes < maxPlanes, "SceneCullingState::createCullingVolume - Did not allocate enough planes!" ); + + const Point3F& v1 = vertices[ i ]; + const Point3F& v2 = vertices[ lastVertex ]; + + // Keep track of bounds. + + wsPolyBounds.minExtents.setMin( v1 ); + wsPolyBounds.maxExtents.setMax( v1 ); + + // Skip the edge if it's length is really short. + + const Point3F edgeVector = v2 - v1; + const F32 edgeVectorLenSquared = edgeVector.lenSquared(); + if( edgeVectorLenSquared < 0.025f ) + continue; + + //TODO: might need to do additional checks here for non-planar polygons used by occluders + //TODO: test for colinearity of edge vector with view vector (occluders only) + + // Create a plane for the edge. + + if( isOrtho ) + { + // Compute a plane through the two edge vertices and one + // of the vertices extended along the view direction. + + if( !invert ) + planes[ numPlanes ] = PlaneF( v1, v1 + viewDir, v2 ); + else + planes[ numPlanes ] = PlaneF( v2, v1 + viewDir, v1 ); + } + else + { + // Compute a plane going through the viewpoint and the two + // edge vertices. + + if( !invert ) + planes[ numPlanes ] = PlaneF( v1, viewPos, v2 ); + else + planes[ numPlanes ] = PlaneF( v2, viewPos, v1 ); + } + + numPlanes ++; + + // If this is the first plane that we have created, find out whether + // the vertex ordering is giving us the plane orientations that we want + // (facing inside). If not, invert vertex order from now on. + + if( numPlanes == 1 ) + { + Point3F center( 0, 0, 0 ); + for( U32 n = 0; n < numVertices; ++ n ) + center += vertices[n]; + center /= numVertices; + + if( planes[numPlanes - 1].whichSide( center ) == PlaneF::Back ) + { + invert = true; + planes[ numPlanes - 1 ].invert(); + } + } + + // For occluders, keep tabs of the nearest, and two farthest vertices. + + if( type == SceneCullingVolume::Occluder ) + { + const F32 distSquared = ( v1 - viewPos ).lenSquared(); + if( distSquared < minVertexDistanceSquared ) + { + minVertexDistanceSquared = distSquared; + leastDistantVert = i; + } + if( distSquared > maxVertexDistancesSquared[ 0 ] ) + { + // Move 0 to 1. + maxVertexDistancesSquared[ 1 ] = maxVertexDistancesSquared[ 0 ]; + mostDistantVertices[ 1 ] = mostDistantVertices[ 0 ]; + + // Replace 0. + maxVertexDistancesSquared[ 0 ] = distSquared; + mostDistantVertices[ 0 ] = i; + } + else if( distSquared > maxVertexDistancesSquared[ 1 ] ) + { + // Replace 1. + maxVertexDistancesSquared[ 1 ] = distSquared; + mostDistantVertices[ 1 ] = i; + } + } + } + + // If the extrusion produced no useful result, abort. + + if( numPlanes < 3 ) + return false; + + // For includers, test the angle of the edges at the current vertex. + // If too steep, add an extra plane to improve culling efficiency. + + if( false )//type == SceneCullingVolume::Includer ) + { + const U32 numOriginalPlanes = numPlanes; + U32 lastPlaneIndex = numPlanes - 1; + + for( U32 i = 0; i < numOriginalPlanes; lastPlaneIndex = i, ++ i ) + { + const PlaneF& currentPlane = planes[ i ]; + const PlaneF& lastPlane = planes[ lastPlaneIndex ]; + + // Compute the cosine of the angle between the two plane normals. + + const F32 cosAngle = mFabs( mDot( currentPlane, lastPlane ) ); + + // The planes meet at increasingly steep angles the more they point + // in opposite directions, i.e the closer the angle of their normals + // is to 180 degrees. Skip any two planes that don't get near that. + + if( cosAngle > 0.1f ) + continue; + + //TODO + + const Point3F addNormals = currentPlane + lastPlane; + const Point3F crossNormals = mCross( currentPlane, lastPlane ); + + Point3F newNormal = currentPlane + lastPlane;//addNormals - mDot( addNormals, crossNormals ) * crossNormals; + + // + + planes[ numPlanes ] = PlaneF( currentPlane.getPosition(), newNormal ); + numPlanes ++; + } + } + + // Compute the metrics of the culling volume in relation to the view frustum. + // + // For this, we are short-circuiting things slightly. The correct way (other than doing + // full screen projections) would be to transform all the polygon points into camera + // space, lay an AABB around those points, and then find the X and Z extents on the near plane. + // + // However, while not as accurate, a faster way is to just project the axial vectors + // of the bounding box onto both the camera right and up vector. This gives us a rough + // estimate of the camera-space size of the polygon we're looking at. + + const MatrixF& cameraTransform = getCameraState().getViewWorldMatrix(); + const Point3F cameraRight = cameraTransform.getRightVector(); + const Point3F cameraUp = cameraTransform.getUpVector(); + + const Point3F wsPolyBoundsExtents = wsPolyBounds.getExtents(); + + F32 widthEstimate = + getMax( mFabs( wsPolyBoundsExtents.x * cameraRight.x ), + getMax( mFabs( wsPolyBoundsExtents.y * cameraRight.y ), + mFabs( wsPolyBoundsExtents.z * cameraRight.z ) ) ); + + F32 heightEstimate = + getMax( mFabs( wsPolyBoundsExtents.x * cameraUp.x ), + getMax( mFabs( wsPolyBoundsExtents.y * cameraUp.y ), + mFabs( wsPolyBoundsExtents.z * cameraUp.z ) ) ); + + // If the current camera is a perspective one, divide the two estimates + // by the distance of the nearest bounding box vertex to the camera + // to account for perspective distortion. + + if( !isOrtho ) + { + const Point3F nearestVertex = wsPolyBounds.computeVertex( + Box3F::getPointIndexFromOctant( - viewDir ) + ); + + const F32 distance = ( nearestVertex - viewPos ).len(); + + widthEstimate /= distance; + heightEstimate /= distance; + } + + // If we are creating an occluder, check to see if the estimates fit + // our minimum requirements. + + if( type == SceneCullingVolume::Occluder ) + { + const F32 widthEstimatePercentage = widthEstimate / getFrustum().getWidth(); + const F32 heightEstimatePercentage = heightEstimate / getFrustum().getHeight(); + + if( widthEstimatePercentage < smOccluderMinWidthPercentage || + heightEstimatePercentage < smOccluderMinHeightPercentage ) + return false; // Reject. + } + + // Use the area estimate as the volume's sort point. + + const F32 sortPoint = widthEstimate * heightEstimate; + + // Finally, if it's an occluder, compute a near cap. The near cap prevents objects + // in front of the occluder from testing positive. The same could be achieved by + // manually comparing distances before testing objects but since that would amount + // to the same checks the plane/AABB tests do, it's easier to just add another plane. + // Additionally, it gives the benefit of being able to create more precise culling + // results by angling the plane. + + //NOTE: Could consider adding a near cap for includers too when generating a volume + // for the outdoor zone as that may prevent quite a bit of space from being included. + // However, given that this space will most likely just be filled with interior + // stuff anyway, it's probably not worth it. + + if( type == SceneCullingVolume::Occluder ) + { + const U32 nearCapIndex = numPlanes; + planes[ nearCapIndex ] = PlaneF( + vertices[ mostDistantVertices[ 0 ] ], + vertices[ mostDistantVertices[ 1 ] ], + vertices[ leastDistantVert ] ); + + // Invert the plane, if necessary. + if( planes[ nearCapIndex ].whichSide( viewPos ) == PlaneF::Front ) + planes[ nearCapIndex ].invert(); + + numPlanes ++; + } + + // Create the volume from the planes. + + outVolume = SceneCullingVolume( + type, + PlaneSetF( planes, numPlanes ) + ); + outVolume.setSortPoint( sortPoint ); + + // Done. + + return true; +} + +//----------------------------------------------------------------------------- + +namespace { + struct ZoneArrayIterator + { + U32 mCurrent; + U32 mNumZones; + const U32* mZones; + + ZoneArrayIterator( const U32* zones, U32 numZones ) + : mCurrent( 0 ), + mNumZones( numZones ), + mZones( zones ) {} + + bool isValid() const + { + return ( mCurrent < mNumZones ); + } + ZoneArrayIterator& operator ++() + { + mCurrent ++; + return *this; + } + U32 operator *() const + { + return mZones[ mCurrent ]; + } + }; +} + +template< typename T, typename Iter > +inline SceneZoneCullingState::CullingTestResult SceneCullingState::_testOccludersOnly( const T& bounds, Iter zoneIter ) const +{ + // Test the culling states of all zones that the object + // is assigned to. + + for( ; zoneIter.isValid(); ++ zoneIter ) + { + const SceneZoneCullingState& zoneState = getZoneState( *zoneIter ); + + // Skip zone if there are no occluders. + + if( !zoneState.hasOccluders() ) + continue; + + // If the object's world bounds overlaps any of the volumes + // for this zone, it's rendered. + + if( zoneState.testVolumes( bounds, true ) == SceneZoneCullingState::CullingTestPositiveByOcclusion ) + return SceneZoneCullingState::CullingTestPositiveByOcclusion; + } + + return SceneZoneCullingState::CullingTestNegative; +} + +template< typename T, typename Iter > +inline SceneZoneCullingState::CullingTestResult SceneCullingState::_test( const T& bounds, Iter zoneIter, + const PlaneF& nearPlane, const PlaneF& farPlane ) const +{ + // Defer test of near and far plane until we've hit a zone + // which actually has visible space. This prevents us from + // doing near/far tests on objects that were included in the + // potential render list but aren't actually in any visible + // zone. + bool haveTestedNearAndFar = false; + + // Test the culling states of all zones that the object + // is assigned to. + + for( ; zoneIter.isValid(); ++ zoneIter ) + { + const SceneZoneCullingState& zoneState = getZoneState( *zoneIter ); + + // Skip zone if there are no positive culling volumes. + + if( !zoneState.hasIncluders() ) + continue; + + // If we haven't tested the near and far plane yet, do so + // now. + + if( !haveTestedNearAndFar ) + { + // Test near plane. + + PlaneF::Side nearSide = nearPlane.whichSide( bounds ); + if( nearSide == PlaneF::Back ) + return SceneZoneCullingState::CullingTestNegative; + + // Test far plane. + + PlaneF::Side farSide = farPlane.whichSide( bounds ); + if( farSide == PlaneF::Back ) + return SceneZoneCullingState::CullingTestNegative; + + haveTestedNearAndFar = true; + } + + // If the object's world bounds overlaps any of the volumes + // for this zone, it's rendered. + + SceneZoneCullingState::CullingTestResult result = zoneState.testVolumes( bounds ); + + if( result == SceneZoneCullingState::CullingTestPositiveByInclusion ) + return result; + else if( result == SceneZoneCullingState::CullingTestPositiveByOcclusion ) + return result; + } + + return SceneZoneCullingState::CullingTestNegative; +} + +//----------------------------------------------------------------------------- + +template< bool OCCLUDERS_ONLY, typename T > +inline SceneZoneCullingState::CullingTestResult SceneCullingState::_test( const T& bounds, const U32* zones, U32 numZones ) const +{ + // If zone culling is disabled, only test against + // the root frustum. + + if( disableZoneCulling() ) + { + if( !OCCLUDERS_ONLY && !getFrustum().isCulled( bounds ) ) + return SceneZoneCullingState::CullingTestPositiveByInclusion; + + return SceneZoneCullingState::CullingTestNegative; + } + + // Otherwise test each of the zones. + + if( OCCLUDERS_ONLY ) + { + return _testOccludersOnly( + bounds, + ZoneArrayIterator( zones, numZones ) + ); + } + else + { + const PlaneF* frustumPlanes = getFrustum().getPlanes(); + + return _test( + bounds, + ZoneArrayIterator( zones, numZones ), + frustumPlanes[ Frustum::PlaneNear ], + frustumPlanes[ Frustum::PlaneFar ] + ); + } +} + +//----------------------------------------------------------------------------- + +bool SceneCullingState::isCulled( const Box3F& aabb, const U32* zones, U32 numZones ) const +{ + SceneZoneCullingState::CullingTestResult result = _test< false >( aabb, zones, numZones ); + return ( result == SceneZoneCullingState::CullingTestNegative || + result == SceneZoneCullingState::CullingTestPositiveByOcclusion ); +} + +//----------------------------------------------------------------------------- + +bool SceneCullingState::isCulled( const OrientedBox3F& obb, const U32* zones, U32 numZones ) const +{ + SceneZoneCullingState::CullingTestResult result = _test< false >( obb, zones, numZones ); + return ( result == SceneZoneCullingState::CullingTestNegative || + result == SceneZoneCullingState::CullingTestPositiveByOcclusion ); +} + +//----------------------------------------------------------------------------- + +bool SceneCullingState::isCulled( const SphereF& sphere, const U32* zones, U32 numZones ) const +{ + SceneZoneCullingState::CullingTestResult result = _test< false >( sphere, zones, numZones ); + return ( result == SceneZoneCullingState::CullingTestNegative || + result == SceneZoneCullingState::CullingTestPositiveByOcclusion ); +} + +//----------------------------------------------------------------------------- + +bool SceneCullingState::isOccluded( SceneObject* object ) const +{ + if( disableZoneCulling() ) + return false; + + CullingTestResult result = _testOccludersOnly( + object->getWorldBox(), + SceneObject::ObjectZonesIterator( object ) + ); + + return ( result == SceneZoneCullingState::CullingTestPositiveByOcclusion ); +} + +//----------------------------------------------------------------------------- + +bool SceneCullingState::isOccluded( const Box3F& aabb, const U32* zones, U32 numZones ) const +{ + return ( _test< true >( aabb, zones, numZones ) == SceneZoneCullingState::CullingTestPositiveByOcclusion ); +} + +//----------------------------------------------------------------------------- + +bool SceneCullingState::isOccluded( const OrientedBox3F& obb, const U32* zones, U32 numZones ) const +{ + return ( _test< true >( obb, zones, numZones ) == SceneZoneCullingState::CullingTestPositiveByOcclusion ); +} + +//----------------------------------------------------------------------------- + +bool SceneCullingState::isOccluded( const SphereF& sphere, const U32* zones, U32 numZones ) const +{ + return ( _test< true >( sphere, zones, numZones ) == SceneZoneCullingState::CullingTestPositiveByOcclusion ); +} + +//----------------------------------------------------------------------------- + +U32 SceneCullingState::cullObjects( SceneObject** objects, U32 numObjects, U32 cullOptions ) const +{ + PROFILE_SCOPE( SceneCullingState_cullObjects ); + + U32 numRemainingObjects = 0; + + // We test near and far planes separately in order to not do the tests + // repeatedly, so fetch the planes now. + const PlaneF& nearPlane = getFrustum().getPlanes()[ Frustum::PlaneNear ]; + const PlaneF& farPlane = getFrustum().getPlanes()[ Frustum::PlaneFar ]; + + for( U32 i = 0; i < numObjects; ++ i ) + { + SceneObject* object = objects[ i ]; + bool isCulled = true; + + // If we should respect editor overrides, test that now. + + if( !( cullOptions & CullEditorOverrides ) && + gEditingMission && + ( ( object->isCullingDisabledInEditor() && object->isRenderEnabled() ) || object->isSelected() ) ) + { + isCulled = false; + } + + // If the object is render-disabled, it gets culled. The only + // way around this is the editor override above. + + else if( !( cullOptions & DontCullRenderDisabled ) && + !object->isRenderEnabled() ) + { + isCulled = true; + } + + // Global bounds objects are never culled. Note that this means + // that if these objects are to respect zoning, they need to manually + // trigger the respective culling checks for whatever they want to + // batch. + + else if( object->isGlobalBounds() ) + isCulled = false; + + // If terrain occlusion checks are enabled, run them now. + + else if( !mDisableTerrainOcclusion && + object->getWorldBox().minExtents.x > -1e5 && + isOccludedByTerrain( object ) ) + { + // Occluded by terrain. + isCulled = true; + } + + // If the object shouldn't be subjected to more fine-grained culling + // or if zone culling is disabled, just test against the root frustum. + + else if( !( object->getTypeMask() & CULLING_INCLUDE_TYPEMASK ) || + ( object->getTypeMask() & CULLING_EXCLUDE_TYPEMASK ) || + disableZoneCulling() ) + { + isCulled = getFrustum().isCulled( object->getWorldBox() ); + } + + // Go through the zones that the object is assigned to and + // test the object against the frustums of each of the zones. + + else + { + CullingTestResult result = _test( + object->getWorldBox(), + SceneObject::ObjectZonesIterator( object ), + nearPlane, + farPlane + ); + + isCulled = ( result == SceneZoneCullingState::CullingTestNegative || + result == SceneZoneCullingState::CullingTestPositiveByOcclusion ); + } + + if( !isCulled ) + objects[ numRemainingObjects ++ ] = object; + } + + return numRemainingObjects; +} + +//----------------------------------------------------------------------------- + +bool SceneCullingState::isOccludedByTerrain( SceneObject* object ) const +{ + PROFILE_SCOPE( SceneCullingState_isOccludedByTerrain ); + + // Don't try to occlude globally bounded objects. + if( object->isGlobalBounds() ) + return false; + + const Vector< SceneObject* >& terrains = getSceneManager()->getContainer()->getTerrains(); + const U32 numTerrains = terrains.size(); + + for( U32 terrainIdx = 0; terrainIdx < numTerrains; ++ terrainIdx ) + { + TerrainBlock* terrain = dynamic_cast< TerrainBlock* >( terrains[ terrainIdx ] ); + if( !terrain ) + continue; + + Point3F localCamPos = getCameraState().getViewPosition(); + terrain->getWorldTransform().mulP( localCamPos ); + F32 height; + terrain->getHeight( Point2F( localCamPos.x, localCamPos.y ), &height ); + bool aboveTerrain = ( height <= localCamPos.z ); + + // Don't occlude if we're below the terrain. This prevents problems when + // looking out from underground bases... + if( !aboveTerrain ) + continue; + + const Box3F& oBox = object->getObjBox(); + F32 minSide = getMin(oBox.len_x(), oBox.len_y()); + if (minSide > 85.0f) + continue; + + const Box3F& rBox = object->getWorldBox(); + Point3F ul(rBox.minExtents.x, rBox.minExtents.y, rBox.maxExtents.z); + Point3F ur(rBox.minExtents.x, rBox.maxExtents.y, rBox.maxExtents.z); + Point3F ll(rBox.maxExtents.x, rBox.minExtents.y, rBox.maxExtents.z); + Point3F lr(rBox.maxExtents.x, rBox.maxExtents.y, rBox.maxExtents.z); + + terrain->getWorldTransform().mulP(ul); + terrain->getWorldTransform().mulP(ur); + terrain->getWorldTransform().mulP(ll); + terrain->getWorldTransform().mulP(lr); + + Point3F xBaseL0_s = ul - localCamPos; + Point3F xBaseL0_e = lr - localCamPos; + Point3F xBaseL1_s = ur - localCamPos; + Point3F xBaseL1_e = ll - localCamPos; + + static F32 checkPoints[3] = {0.75, 0.5, 0.25}; + RayInfo rinfo; + for( U32 i = 0; i < 3; i ++ ) + { + Point3F start = (xBaseL0_s * checkPoints[i]) + localCamPos; + Point3F end = (xBaseL0_e * checkPoints[i]) + localCamPos; + + if (terrain->castRay(start, end, &rinfo)) + continue; + + terrain->getHeight(Point2F(start.x, start.y), &height); + if ((height <= start.z) == aboveTerrain) + continue; + + start = (xBaseL1_s * checkPoints[i]) + localCamPos; + end = (xBaseL1_e * checkPoints[i]) + localCamPos; + + if (terrain->castRay(start, end, &rinfo)) + continue; + + Point3F test = (start + end) * 0.5; + if (terrain->castRay(localCamPos, test, &rinfo) == false) + continue; + + return true; + } + } + + return false; +} + +//----------------------------------------------------------------------------- + +void SceneCullingState::debugRenderCullingVolumes() const +{ + const ColorI occluderColor( 255, 0, 0, 255 ); + const ColorI includerColor( 0, 255, 0, 255 ); + + const PlaneF& nearPlane = getFrustum().getPlanes()[ Frustum::PlaneNear ]; + const PlaneF& farPlane = getFrustum().getPlanes()[ Frustum::PlaneFar ]; + + DebugDrawer* drawer = DebugDrawer::get(); + const SceneZoneSpaceManager* zoneManager = mSceneManager->getZoneManager(); + + bool haveDebugZone = false; + const U32 numZones = mZoneStates.size(); + for( S32 zoneId = numZones - 1; zoneId >= 0; -- zoneId ) + { + if( !zoneManager->isValidZoneId( zoneId ) ) + continue; + + const SceneZoneCullingState& zoneState = mZoneStates[ zoneId ]; + if( !zoneManager->getZoneOwner( zoneId )->isSelected() && ( zoneId != SceneZoneSpaceManager::RootZoneId || haveDebugZone ) ) + continue; + + haveDebugZone = true; + + for( SceneZoneCullingState::CullingVolumeIterator iter( zoneState ); + iter.isValid(); ++ iter ) + { + // Temporarily add near and far plane to culling volume so that + // no matter how it is defined, it has a chance of being properly + // capped. + + const U32 numPlanes = iter->getPlanes().getNumPlanes(); + const PlaneF* planes = iter->getPlanes().getPlanes(); + + TempAlloc< PlaneF > tempPlanes( numPlanes + 2 ); + + tempPlanes[ 0 ] = nearPlane; + tempPlanes[ 1 ] = farPlane; + + dMemcpy( &tempPlanes[ 2 ], planes, numPlanes * sizeof( PlaneF ) ); + + // Build a polyhedron from the plane set. + + Polyhedron polyhedron; + polyhedron.buildFromPlanes( + PlaneSetF( tempPlanes, numPlanes + 2 ) + ); + + // If the polyhedron has any renderable data, + // hand it over to the debug drawer. + + if( polyhedron.getNumEdges() ) + drawer->drawPolyhedron( polyhedron, iter->isOccluder() ? occluderColor : includerColor ); + } + } +} diff --git a/Engine/source/scene/culling/sceneCullingState.h b/Engine/source/scene/culling/sceneCullingState.h new file mode 100644 index 000000000..b7a231da8 --- /dev/null +++ b/Engine/source/scene/culling/sceneCullingState.h @@ -0,0 +1,310 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SCENECULLINGSTATE_H_ +#define _SCENECULLINGSTATE_H_ + +#ifndef _SCENEZONECULLINGSTATE_H_ +#include "scene/culling/sceneZoneCullingState.h" +#endif + +#ifndef _MATHUTIL_FRUSTUM_H_ +#include "math/util/frustum.h" +#endif + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +#ifndef _SCENECAMERASTATE_H_ +#include "scene/sceneCameraState.h" +#endif + +#ifndef _DATACHUNKER_H_ +#include "core/dataChunker.h" +#endif + +#ifndef _BITVECTOR_H_ +#include "core/bitVector.h" +#endif + + +class SceneObject; +class SceneManager; + + +/// An object that gathers the culling state for a scene. +class SceneCullingState +{ + public: + + /// Used to disable the somewhat expensive terrain occlusion testing + /// done in during scene culling. + static bool smDisableTerrainOcclusion; + + /// Whether to force zone culling to off by default. + static bool smDisableZoneCulling; + + /// @name Occluder Restrictions + /// Size restrictions on occlusion culling volumes. Any occlusion volume + /// that does not meet these minimum requirements is not accepted into the + /// rendering state. + /// + /// Having independent restrictions on both width and height allows filtering + /// out occluders that might have a lot of area but only by covering very thin + /// stretches of the screen. + /// @{ + + /// If more than this number of occlusion volumes are added to a ZoneState, + /// then the occlusions volumes corresponding to the smallest amount of screen + /// real estate get dropped such as to never exceed this total number of occlusion + /// volumes. + static U32 smMaxOccludersPerZone; + + /// Percentage of camera-space frustum near plane height that an occlusion culler must + /// at least fill in order to not be rejected. + /// @note The height computed for occluders is only an estimate. + static F32 smOccluderMinHeightPercentage; + + /// Percentage of camera-space frustum near plane width that an occlusion culler must + /// at least fill in order to not be rejected. + /// @note The width computed for occluders is only an estimate. + static F32 smOccluderMinWidthPercentage; + + /// @} + + protected: + + /// Scene which is being culled. + SceneManager* mSceneManager; + + /// The viewing state that defines how the scene is being viewed. + SceneCameraState mCameraState; + + /// The root culling volume corresponding to the camera frustum. + SceneCullingVolume mRootVolume; + + /// Occluders that have been added to this render state. Adding an occluder does not + /// necessarily result in an occluder volume being added. To not repeatedly try to + /// process the same occluder object, all objects that are added are recorded here. + Vector< SceneObject* > mAddedOccluderObjects; + + /// + BitVector mZoneVisibilityFlags; + + /// ZoneState entries for all zones in the scene. + Vector< SceneZoneCullingState > mZoneStates; + + /// Allocator for culling data that can be freed in one go when + /// the culling state is freed. + DataChunker mDataChunker; + + /// If true, occlusion checks will not be done against the terrains + /// in the scene. + bool mDisableTerrainOcclusion; + + /// If true, all objects will only be tested against the root + /// frustum. + bool mDisableZoneCulling; + + public: + + /// + SceneCullingState( SceneManager* sceneManager, + const SceneCameraState& cameraState ); + + /// Return the scene which is being culled in this state. + SceneManager* getSceneManager() const { return mSceneManager; } + + /// Return the root frustum which is used to set up scene visibility. + const Frustum& getFrustum() const { return getCameraState().getFrustum(); } + + /// Return the viewing state that defines how the scene is being viewed. + const SceneCameraState& getCameraState() const { return mCameraState; } + + /// Return the root culling volume that corresponds to the camera frustum. + /// @note This volume omits the near and far plane of the frustum's polyhedron + /// as these will be tested separately during culling. Testing them repeatedly + /// just wastes time. + const SceneCullingVolume& getRootVolume() const { return mRootVolume; } + + /// @name Visibility and Occlusion + /// @{ + + enum CullOptions + { + /// Cull objects that have their SceneObject::DisableCullingInEditorFlag set. + /// By default, these objects will not get culled if the editor is active. + CullEditorOverrides = BIT( 0 ), + + /// Do not cull objects that are render-disabled. + /// @see SceneObject::isRenderEnabled() + DontCullRenderDisabled = BIT( 1 ) + }; + + /// Cull the given list of objects according to the current culling state. + /// + /// @param object Array of objects. This array will be modified in place. + /// @param numObjects Number of objects in @a objects. + /// @param cullOptions Combination of CullOptions. + /// + /// @return Number of objects remaining in the list. + U32 cullObjects( SceneObject** objects, U32 numObjects, U32 cullOptions = 0 ) const; + + /// Return true if the given object is culled according to the current culling state. + bool isCulled( SceneObject* object ) const { return ( cullObjects( &object, 1 ) == 0 ); } + + /// Return true if the given AABB is culled in any of the given zones. + bool isCulled( const Box3F& aabb, const U32* zones, U32 numZones ) const; + + /// Return true if the given OBB is culled in any of the given zones. + bool isCulled( const OrientedBox3F& obb, const U32* zones, U32 numZones ) const; + + /// Return true if the given sphere is culled in any of the given zones. + bool isCulled( const SphereF& sphere, const U32* zones, U32 numZones ) const; + + /// Return true if the given object is occluded according to the current culling state. + bool isOccluded( SceneObject* object ) const; + + /// Return true if the given AABB is occluded according to the current culling state. + bool isOccluded( const Box3F& aabb, const U32* zones, U32 numZones ) const; + + /// Return true if the given OBB is occluded according to the current culling state. + bool isOccluded( const OrientedBox3F& obb, const U32* zones, U32 numZones ) const; + + /// Return true if the given sphere is occluded according to the current culling state. + bool isOccluded( const SphereF& sphere, const U32* zones, U32 numZones ) const; + + /// Add the occlusion information contained in the given object. + /// + /// @note This should only be called after all positive frustums have been added + /// to the zone state. + void addOccluder( SceneObject* object ); + + /// Test whether the given object is occluded by any of the terrains + /// in the scene. + bool isOccludedByTerrain( SceneObject* object ) const; + + /// Set whether isCulled() should do terrain occlusion checks or not. + void setDisableTerrainOcclusion( bool value ) { mDisableTerrainOcclusion = value; } + + /// @} + + /// @name Zones + /// @{ + + /// If true, culling will only be performed against the root frustum + /// and not against frustums of individual zones. + /// + /// @note This also disables occluders as these are added to the zone frustums. + bool disableZoneCulling() const { return mDisableZoneCulling; } + void disableZoneCulling( bool value ) { mDisableZoneCulling = value; } + + /// Return true if any of the zones that the object is currently are + /// visible. + bool isWithinVisibleZone( SceneObject* object ) const; + + /// Return a bit vector with one bit for each zone in the scene. If the bit is set, + /// the zone has includer culling volumes attached to it and thus is visible. + const BitVector& getZoneVisibilityFlags() const { return mZoneVisibilityFlags; } + + /// Return the culling state for a particular zone. + /// @param zoneId Numeric ID of zone. + const SceneZoneCullingState& getZoneState( U32 zoneId ) const + { + AssertFatal( zoneId < ( U32 ) mZoneStates.size(), "SceneCullingState::getZoneState - Index out of bounds" ); + return mZoneStates[ zoneId ]; + } + + /// Returns the culling state for a particular zone. + /// @param zoneId Numeric ID of zone. + SceneZoneCullingState& getZoneState( U32 zoneId ) + { + return const_cast< SceneZoneCullingState& >( static_cast< const SceneCullingState* >( this )->getZoneState( zoneId ) ); + } + + /// Add a culling volume to the visibility state of the given zone. + /// + /// @param zoneId ID of zone to which to add the given frustum's visibility information. + /// @param volume A culling volume. Note that the data in the volume must have + /// a lifetime at least as long as the culling state. + /// + /// @return True if the visibility state of the zone has changed, i.e. if the volume + /// was either added in whole or merged with an existing set of planes. If the visibility + /// state of the zone has not changed, returns false. + bool addCullingVolumeToZone( U32 zoneId, const SceneCullingVolume& volume ); + + /// Copy the data from the given polyhedron to the culling state, create + /// a new culling volume it and add it to the current culling state of the given zone. + /// + /// @param zoneId ID of zone to which to add the given frustum's visibility information. + /// @param type Which type of culling volume to add. + /// @param polyhedron Polyhedron describing the space of the culling volume. + bool addCullingVolumeToZone( U32 zoneId, SceneCullingVolume::Type type, const AnyPolyhedron& polyhedron ); + + /// Create a new culling volume by extruding the given polygon away from the viewpoint. + /// + /// @param vertices Array of polygon vertices. + /// @param numVertices Number of vertices in @a vertices. + /// @param type Type of culling volume to create. + /// @param outVolume (out) Receives the generated volume, if successful. + /// + /// @return True if a volume could be generated from the given polygon or false if not. + bool createCullingVolume( const Point3F* vertices, U32 numVertices, SceneCullingVolume::Type type, SceneCullingVolume& outVolume ); + + /// @} + + /// @name Memory Management + /// + /// Rather than allocating a lot of individual point and plane data for the culling volumes, + /// it is more efficient to batch allocate chunks of memory and then release all the memory + /// for all culling volumes in one go. This is facilitated by this interface. + /// + /// @{ + + /// Allocate memory from this culling state. The memory is freed when the + /// culling state is destroyed. + void* allocateData( U32 size ) { return mDataChunker.alloc( size ); } + + /// Allocate memory for @a num instances of T from this culling state. + template< typename T > + T* allocateData( U32 num ) { return reinterpret_cast< T* >( allocateData( sizeof( T ) * num ) ); } + + /// @} + + /// Queue debug visualizations of the culling volumes of all currently selected zones + /// (or, if no zone is selected, all volumes in the outdoor zone) to the debug drawer. + void debugRenderCullingVolumes() const; + + private: + + typedef SceneZoneCullingState::CullingTestResult CullingTestResult; + + // Helper methods to avoid code duplication. + + template< bool OCCLUDERS_ONLY, typename T > CullingTestResult _test( const T& bounds, const U32* zones, U32 numZones ) const; + template< typename T, typename Iter > CullingTestResult _test + ( const T& bounds, Iter iter, const PlaneF& nearPlane, const PlaneF& farPlane ) const; + template< typename T, typename Iter > CullingTestResult _testOccludersOnly( const T& bounds, Iter iter ) const; +}; + +#endif // !_SCENECULLINGSTATE_H_ diff --git a/Engine/source/scene/culling/sceneCullingVolume.cpp b/Engine/source/scene/culling/sceneCullingVolume.cpp new file mode 100644 index 000000000..0a7278c73 --- /dev/null +++ b/Engine/source/scene/culling/sceneCullingVolume.cpp @@ -0,0 +1,24 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "scene/culling/sceneCullingVolume.h" diff --git a/Engine/source/scene/culling/sceneCullingVolume.h b/Engine/source/scene/culling/sceneCullingVolume.h new file mode 100644 index 000000000..b79e2437b --- /dev/null +++ b/Engine/source/scene/culling/sceneCullingVolume.h @@ -0,0 +1,130 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SCENECULLINGVOLUME_H_ +#define _SCENECULLINGVOLUME_H_ + +#ifndef _MPLANESET_H_ +#include "math/mPlaneSet.h" +#endif + + +/// A volume used to include or exclude space in a scene. +/// +/// Culling volumes are represented as sets of clipping planes. +/// +/// @note Culling is performed in world space so the plane data for culling volumes +/// must be in world space too. +class SceneCullingVolume +{ + public: + + /// Type of culling. + enum Type + { + Includer, + Occluder, + }; + + protected: + + /// What type of culling volume this is. + Type mType; + + /// + F32 mSortPoint; + + /// The set of clipping planes that defines the clipping volume for this culler. + PlaneSetF mClippingPlanes; + + /// Test the given bounds against this culling volume. + /// + /// Note that we allow false positives here for includers. This will only cause an + /// occasional object to be classified as intersecting when in fact it is outside. + /// This is still better though than requiring the expensive intersection tests for + /// all intersecting objects. + /// + /// @return True if the culling volume accepts the given bounds. + template< typename B > bool _testBounds( const B& bounds ) const + { + if( isOccluder() ) + return getPlanes().isContained( bounds ); + else + return ( getPlanes().testPotentialIntersection( bounds ) != GeometryOutside ); + } + + public: + + /// Create an *uninitialized* culling volume. + SceneCullingVolume() {} + + /// + SceneCullingVolume( Type type, const PlaneSetF& planes ) + : mType( type ), mClippingPlanes( planes ), mSortPoint( 1.f ) {} + + /// Return the type of volume defined by this culling volume, i.e. whether it includes + /// or excludes space. + Type getType() const { return mType; } + + /// Return true if this is an inclusion volume. + bool isIncluder() const { return ( getType() == Includer ); } + + /// Return true if this is an occlusion volume. + bool isOccluder() const { return ( getType() == Occluder ); } + + /// Return the set of clipping planes that defines the culling volume. + const PlaneSetF& getPlanes() const { return mClippingPlanes; } + + /// @name Sorting + /// + /// Before testing, culling volumes will be sorted by decreasing probability of causing + /// test positives. Thus, the sort point of a volume should be a rough metric of the amount + /// of scene/screen space it covers. + /// + /// Note that sort points for occluders are independent of sort points for includers. + /// @{ + + /// Return the sort point value of the volume. The larger the value, the more likely the + /// volume is to cause positive test results with bounding volumes. + F32 getSortPoint() const { return mSortPoint; } + + /// + void setSortPoint( F32 value ) { mSortPoint = value; } + + /// @} + + /// @name Testing + /// @{ + + /// Return true if the volume accepts the given AABB. + bool test( const Box3F& aabb ) const { return _testBounds( aabb ); } + + /// Return true if the volume accepts the given OBB. + bool test( const OrientedBox3F& obb ) const { return _testBounds( obb ); } + + /// Return true if the volume accepts the given sphere. + bool test( const SphereF& sphere ) const { return _testBounds( sphere ); } + + /// @} +}; + +#endif // !_SCENECULLINGVOLUME_H_ diff --git a/Engine/source/scene/culling/sceneZoneCullingState.cpp b/Engine/source/scene/culling/sceneZoneCullingState.cpp new file mode 100644 index 000000000..051975ca6 --- /dev/null +++ b/Engine/source/scene/culling/sceneZoneCullingState.cpp @@ -0,0 +1,235 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "scene/culling/sceneZoneCullingState.h" + +#include "scene/culling/sceneCullingState.h" +#include "platform/profiler.h" + + +//----------------------------------------------------------------------------- + +template< typename T > +inline SceneZoneCullingState::CullingTestResult SceneZoneCullingState::_testVolumes( T bounds, bool occludersOnly ) const +{ + // If we are testing for occlusion only and we don't have any + // occlusion volumes, we can early out here. + + if( occludersOnly && !mHaveOccluders ) + return CullingTestNegative; + + // If we haven't sorted the volumes on this zone state yet, + // do so now. + + if( !mHaveSortedVolumes ) + _sortVolumes(); + + // Now go through the volumes in this zone and test them + // against the bounds. + + for( CullingVolumeLink* link = mCullingVolumes; link != NULL; link = link->mNext ) + { + const SceneCullingVolume& volume = link->mVolume; + + if( volume.isOccluder() ) + { + if( volume.test( bounds ) ) + return CullingTestPositiveByOcclusion; + } + else + { + // If we are testing for occlusion only, we can early out as soon + // as we have reached the first non-inverted volume. + if( occludersOnly ) + return CullingTestNegative; + + if( volume.test( bounds ) ) + return CullingTestPositiveByInclusion; + } + } + + return CullingTestNegative; +} + +//----------------------------------------------------------------------------- + +SceneZoneCullingState::CullingTestResult SceneZoneCullingState::testVolumes( const Box3F& aabb, bool invertedOnly ) const +{ + PROFILE_SCOPE( SceneZoneCullingState_testVolumes ); + return _testVolumes( aabb, invertedOnly ); +} + +//----------------------------------------------------------------------------- + +SceneZoneCullingState::CullingTestResult SceneZoneCullingState::testVolumes( const OrientedBox3F& obb, bool invertedOnly ) const +{ + PROFILE_SCOPE( SceneZoneCullingState_testVolumes_OBB ); + return _testVolumes( obb, invertedOnly ); +} + +//----------------------------------------------------------------------------- + +SceneZoneCullingState::CullingTestResult SceneZoneCullingState::testVolumes( const SphereF& sphere, bool invertedOnly ) const +{ + PROFILE_SCOPE( SceneZoneCullingState_testVolumes_Sphere ); + return _testVolumes( sphere, invertedOnly ); +} + +//----------------------------------------------------------------------------- + +void SceneZoneCullingState::_sortVolumes() const +{ + // First do a pass to gather all occlusion volumes. These must be put on the + // list in front of all inclusion volumes. Otherwise, an inclusion volume + // may test positive when in fact an occlusion volume would reject the object. + + CullingVolumeLink* occluderHead = NULL; + CullingVolumeLink* occluderTail = NULL; + + if( mHaveOccluders ) + { + U32 numOccluders = 0; + + for( CullingVolumeLink* current = mCullingVolumes, *prev = NULL; current != NULL; ) + { + CullingVolumeLink* next = current->mNext; + + if( !current->mVolume.isOccluder() ) + prev = current; + else + { + // Unlink from list. + + if( prev ) + prev->mNext = next; + else + mCullingVolumes = next; + + // Sort into list. + + _insertSorted( occluderHead, occluderTail, current ); + ++ numOccluders; + } + + current = next; + } + + // If we ended up with more inverted (occlusion) volumes than we want, + // chop off any but the first N volumes. Since we have sorted the volumes + // by screen coverage, this will get rid of smallest occlusion volumes. + + if( numOccluders > SceneCullingState::smMaxOccludersPerZone ) + { + CullingVolumeLink* last = occluderHead; + for( U32 i = 0; i < ( SceneCullingState::smMaxOccludersPerZone - 1 ); ++ i ) + last = last->mNext; + + // Chop off rest. The links are allocated on the chunker + // and thus will simply disappear when the state gets deleted. + + last->mNext = NULL; + occluderTail = last; + } + } + + // Now, do a second pass to sort all includer volumes by decreasing screen + // real estate so that when testing against them, we test the larger volumes first + // and the smaller ones later. + + CullingVolumeLink* includerHead = NULL; + CullingVolumeLink* includerTail = NULL; + + while( mCullingVolumes ) + { + CullingVolumeLink* current = mCullingVolumes; + + AssertFatal( !current->mVolume.isOccluder(), + "SceneCullingState::ZoneState::_sortFrustums - Occluders must have been filtered out at this point" ); + + // Unlink from list. + + mCullingVolumes = current->mNext; + + // Sort into list. + + _insertSorted( includerHead, includerTail, current ); + } + + // Merge the two lists. Put inverted volumes first and + // non-inverted volumes second. + + if( occluderHead != NULL ) + { + mCullingVolumes = occluderHead; + occluderTail->mNext = includerHead; + } + else + mCullingVolumes = includerHead; + + // Done. + + mHaveSortedVolumes = true; +} + +//----------------------------------------------------------------------------- + +void SceneZoneCullingState::_insertSorted( CullingVolumeLink*& head, CullingVolumeLink*& tail, CullingVolumeLink* link ) +{ + // If first element, just put it in the list + // and return. + + if( !head ) + { + head = link; + tail = link; + link->mNext = NULL; + + return; + } + + // Otherwise, search for where to put it. + + F32 sortPoint = link->mVolume.getSortPoint(); + + for( CullingVolumeLink* current = head, *prev = NULL; current != NULL; prev = current, current = current->mNext ) + { + F32 currentSortPoint = current->mVolume.getSortPoint(); + if( currentSortPoint > sortPoint ) + continue; + + if( !prev ) + head = link; + else + prev->mNext = link; + + link->mNext = current; + return; + } + + // Smallest frustum in list. Append to end. + + tail->mNext = link; + link->mNext = NULL; + + tail = link; +} diff --git a/Engine/source/scene/culling/sceneZoneCullingState.h b/Engine/source/scene/culling/sceneZoneCullingState.h new file mode 100644 index 000000000..3420442dd --- /dev/null +++ b/Engine/source/scene/culling/sceneZoneCullingState.h @@ -0,0 +1,171 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SCENEZONECULLINGSTATE_H_ +#define _SCENEZONECULLINGSTATE_H_ + +#ifndef _SCENECULLINGVOLUME_H_ +#include "scene/culling/sceneCullingVolume.h" +#endif + + +/// Culling state for a zone. +/// +/// Zone states keep track of the culling volumes that are generated during traversal +/// for a particular zone in a scene. +/// +/// @note This class has no meaningful constructor; the memory for all zone states is +/// cleared en bloc. +class SceneZoneCullingState +{ + public: + + friend class SceneCullingState; // mCullingVolumes + + /// Result of a culling test in a zone. + enum CullingTestResult + { + /// An includer tested positive on the bounding volume. + CullingTestPositiveByInclusion, + + /// An occluder tested positive on the bounding volume. + CullingTestPositiveByOcclusion, + + /// None of the culling volumes included or excluded the bounding volume. + CullingTestNegative + }; + + /// A culling volume linked to a zone. + /// + /// @note Memory for CullingVolumeLink instances is maintained by SceneCullingState. + struct CullingVolumeLink + { + /// Culling volume. + SceneCullingVolume mVolume; + + /// Next culling volume linked to the zone. + CullingVolumeLink* mNext; + + CullingVolumeLink( const SceneCullingVolume& volume ) + : mVolume( volume ) {} + }; + + /// Iterator over the culling volumes assigned to a zone. + struct CullingVolumeIterator + { + CullingVolumeIterator( const SceneZoneCullingState& state ) + : mCurrent( state.getCullingVolumes() ) {} + + bool isValid() const { return mCurrent != NULL; } + const SceneCullingVolume& operator *() const + { + AssertFatal( isValid(), "SceneCullingState::ZoneState::CullingVolumeIterator::operator* - Invalid iterator" ); + return mCurrent->mVolume; + } + const SceneCullingVolume* operator ->() const + { + AssertFatal( isValid(), "SceneCullingState::ZoneState::CullingVolumeIterator::operator-> - Invalid iterator" ); + return &mCurrent->mVolume; + } + CullingVolumeIterator& operator ++() + { + AssertFatal( isValid(), "SceneCullingState::ZoneState::CullingVolumeIterator::operator++ - Invalid iterator" ); + mCurrent = mCurrent->mNext; + return *this; + } + + private: + CullingVolumeLink* mCurrent; + }; + + protected: + + /// Whether tests can be short-circuited, i.e. the first culler that rejects or accepts + /// will cause the test to terminate. This is the case if there are no includers inside + /// occluders, i.e. if occluders can be trusted to fully exclude any space they cover. + bool mCanShortcuit;//RDTODO: implement this + + /// Link of culling volumes defining the visibility state of the zone. Since there may be + /// multiple portals leading into a zone or multiple occluders inside a zone, we may have multiple + /// culling volumes. + mutable CullingVolumeLink* mCullingVolumes; + + /// Whether culling volumes for this zone state have already been sorted. + mutable bool mHaveSortedVolumes; + + /// Whether there are inclusion volumes on this state. + bool mHaveIncluders; + + /// Whether there are occlusion volumes on this state. + bool mHaveOccluders; + + /// Culling volume test abstracted over bounding volume type. + template< typename T > CullingTestResult _testVolumes( T bounds, bool occludersOnly ) const; + + /// Sort the culling volumes such that the volumes with the highest probability + /// of rejecting objects come first in the list. Also, make sure that all + /// occluders come before all includers so that occlusion is handled correctly. + void _sortVolumes() const; + + /// Insert the volume in @a link at the proper position in the list represented + /// by @a head and @a tail. + static void _insertSorted( CullingVolumeLink*& head, CullingVolumeLink*& tail, CullingVolumeLink* link ); + + public: + + /// Zone states are constructed by SceneCullingState. This constructor should not + /// be used otherwise. It is public due to the use through Vector in SceneCullingState. + SceneZoneCullingState() {} + + /// Return true if the zone is visible. This is the case if any + /// includers have been added to the zone's rendering state. + bool isZoneVisible() const { return mHaveIncluders; } + + /// Return the list of culling volumes attached to the zone. + CullingVolumeLink* getCullingVolumes() const { _sortVolumes(); return mCullingVolumes; } + + /// Test whether the culling volumes added to the zone test positive on the + /// given AABB, i.e. whether they include or exclude the given AABB. + CullingTestResult testVolumes( const Box3F& aabb, bool occludersOnly = false ) const; + + /// Test whether the culling volumes added to the zone test positive on the + /// given OBB, i.e. whether they include or exclude the given OBB. + /// + /// @param obb An OBB described by 8 points. + /// @param invertedOnly If true, only inverted cullers are tested. + CullingTestResult testVolumes( const OrientedBox3F& obb, bool occludersOnly = false ) const; + + /// Test whether the culling volumes added to the zone test positive on the + /// given sphere, i.e. whether they include or exclude the given sphere. + CullingTestResult testVolumes( const SphereF& sphere, bool occludersOnly = false ) const; + + /// Return true if the zone has more than one culling volume assigned to it. + bool hasMultipleVolumes() const { return ( mCullingVolumes && mCullingVolumes->mNext ); } + + /// Return true if the zone has inclusion volumes assigned to it. + bool hasIncluders() const { return mHaveIncluders; } + + /// Return true if the zone has occlusion volumes assigned to it. + bool hasOccluders() const { return mHaveOccluders; } +}; + +#endif // !_SCENEZONECULLINGSTATE_H_ diff --git a/Engine/source/scene/fogStructs.h b/Engine/source/scene/fogStructs.h new file mode 100644 index 000000000..dd003db4f --- /dev/null +++ b/Engine/source/scene/fogStructs.h @@ -0,0 +1,67 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FOGSTRUCTS_H_ +#define _FOGSTRUCTS_H_ + +/// The aerial fog settings. +struct FogData +{ + F32 density; + F32 densityOffset; + F32 atmosphereHeight; + ColorF color; + + FogData() + { + density = 0.0f; + densityOffset = 0.0f; + atmosphereHeight = 0.0f; + color.set( 0.5f, 0.5f, 0.5f, 1.0f ); + } +}; + + +/// The water fog settings. +struct WaterFogData +{ + F32 density; + F32 densityOffset; + F32 wetDepth; + F32 wetDarkening; + ColorI color; + PlaneF plane; + F32 depthGradMax; + + WaterFogData() + { + density = 0.0f; + densityOffset = 0.0f; + wetDepth = 0.0f; + wetDarkening = 0.0f; + color.set( 0.5f, 0.5f, 0.5f, 1.0f ); + plane.set( 0.0f, 0.0f, 1.0f ); + depthGradMax = 0.0f; + } +}; + +#endif // _FOGSTRUCTS_H_ \ No newline at end of file diff --git a/Engine/source/scene/mixin/sceneAmbientSoundObject.h b/Engine/source/scene/mixin/sceneAmbientSoundObject.h new file mode 100644 index 000000000..ef3f2c82c --- /dev/null +++ b/Engine/source/scene/mixin/sceneAmbientSoundObject.h @@ -0,0 +1,73 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SCENEAMBIENTSOUNDOBJECT_H_ +#define _SCENEAMBIENTSOUNDOBJECT_H_ + + +class SFXAmbience; + + +/// Template mixin to add ability to hold a custom SFXAmbience to +/// a SceneObject. +template< typename Base > +class SceneAmbientSoundObject : public Base +{ + public: + + typedef Base Parent; + + protected: + + enum + { + SoundMask = Parent::NextFreeMask << 0, ///< Ambient sound properties have changed. + NextFreeMask = Parent::NextFreeMask << 1, + }; + + /// Ambient sound properties for this space. + SFXAmbience* mSoundAmbience; + + public: + + SceneAmbientSoundObject(); + + /// Set the ambient sound properties for the space. + void setSoundAmbience( SFXAmbience* ambience ); + + // SimObject. + static void initPersistFields(); + + // NetObject. + virtual U32 packUpdate( NetConnection* connection, U32 mask, BitStream* stream ); + virtual void unpackUpdate( NetConnection* connection, BitStream* stream ); + + // SceneObject. + virtual SFXAmbience* getSoundAmbience() const { return mSoundAmbience; } + + private: + + // Console field getters/setters. + static bool _setSoundAmbience( void* object, const char* index, const char* data ); +}; + +#endif // !_SCENEAMBIENTSOUNDOBJECT_H_ diff --git a/Engine/source/scene/mixin/sceneAmbientSoundObject.impl.h b/Engine/source/scene/mixin/sceneAmbientSoundObject.impl.h new file mode 100644 index 000000000..b975a2d41 --- /dev/null +++ b/Engine/source/scene/mixin/sceneAmbientSoundObject.impl.h @@ -0,0 +1,114 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "scene/mixin/sceneAmbientSoundObject.h" + +#include "T3D/sfx/sfx3DWorld.h" +#include "sfx/sfxTypes.h" +#include "sfx/sfxAmbience.h" +#include "core/stream/bitStream.h" +#include "console/engineAPI.h" + + + + +//----------------------------------------------------------------------------- + +template< typename Base > +SceneAmbientSoundObject< Base >::SceneAmbientSoundObject() + : mSoundAmbience( NULL ) +{ +} + +//----------------------------------------------------------------------------- + +template< typename Base > +void SceneAmbientSoundObject< Base >::initPersistFields() +{ + Parent::addGroup( "Sound" ); + Parent::addProtectedField( "soundAmbience", TypeSFXAmbienceName, Offset( mSoundAmbience, SceneAmbientSoundObject ), + &_setSoundAmbience, &defaultProtectedGetFn, + "Ambient sound environment for the space." ); + Parent::endGroup( "Sound" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +template< typename Base > +U32 SceneAmbientSoundObject< Base >::packUpdate( NetConnection* connection, U32 mask, BitStream* stream ) +{ + U32 retMask = Parent::packUpdate( connection, mask, stream ); + + if( stream->writeFlag( mask & SoundMask ) ) + sfxWrite( stream, mSoundAmbience ); + + return retMask; +} + +//----------------------------------------------------------------------------- + +template< typename Base > +void SceneAmbientSoundObject< Base >::unpackUpdate( NetConnection* connection, BitStream* stream ) +{ + Parent::unpackUpdate( connection, stream ); + + if( stream->readFlag() ) // SoundMask + { + SFXAmbience* ambience; + + String errorStr; + if( !sfxReadAndResolve( stream, &ambience, errorStr ) ) + Con::errorf( "SceneAmbientSoundObject::unpackUpdate - bad packet: %s", errorStr.c_str() ); + else + setSoundAmbience( ambience ); + } +} + +//----------------------------------------------------------------------------- + +template< typename Base > +void SceneAmbientSoundObject< Base >::setSoundAmbience( SFXAmbience* ambience ) +{ + if( mSoundAmbience == ambience ) + return; + + mSoundAmbience = ambience; + + if( this->isServerObject() ) + this->setMaskBits( SoundMask ); + else if( this->isProperlyAdded() && gSFX3DWorld ) + gSFX3DWorld->notifyChanged( this ); +} + +//----------------------------------------------------------------------------- + +template< typename Base > +bool SceneAmbientSoundObject< Base >::_setSoundAmbience( void* object, const char* index, const char* data ) +{ + SceneAmbientSoundObject* p = reinterpret_cast< SceneAmbientSoundObject* >( object ); + SFXAmbience* ambience = EngineUnmarshallData< SFXAmbience* >()( data ); + p->setSoundAmbience( ambience ); + return false; +} diff --git a/Engine/source/scene/mixin/scenePolyhedralObject.h b/Engine/source/scene/mixin/scenePolyhedralObject.h new file mode 100644 index 000000000..6a60d0b6d --- /dev/null +++ b/Engine/source/scene/mixin/scenePolyhedralObject.h @@ -0,0 +1,116 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SCENEPOLYHEDRALOBJECT_H_ +#define _SCENEPOLYHEDRALOBJECT_H_ + +#ifndef _MPOLYHEDRON_H_ +#include "math/mPolyhedron.h" +#endif + + +/// Shared interface for polyhedral objects. +struct IScenePolyhedralObject +{ + /// Convert the polyhedral object to a raw polyhedron. + virtual AnyPolyhedron ToAnyPolyhedron() const = 0; +}; + + +/// Helper template for mixing a polyhedral volume definition into +/// the superclass hierarchy of a SceneSpace-derived class. +template< typename Base, typename P = Polyhedron > +class ScenePolyhedralObject : public Base, public IScenePolyhedralObject +{ + public: + + typedef Base Parent; + typedef P PolyhedronType; + + enum + { + MAX_PLANES = 256, + MAX_POINTS = 256, + MAX_EDGES = 256 + }; + + protected: + + enum + { + PolyMask = Parent::NextFreeMask << 0, + NextFreeMask = Parent::NextFreeMask << 1 + }; + + /// Whether the polyhedron corresponds to the object box. If so, + /// several things can be fast-tracked. For example, serializing the + /// polyhedron is pointless as it can be easily reconstructed from the + /// object box on load time. Also, certain operations like containment + /// tests have significantly faster formulations for AABBs (given that the + /// input data is transformed into object space) than for general + /// polyhedrons. + bool mIsBox; + + /// The polyhedron that defines the volume of the object. + /// @note Defined in object space by default. + PolyhedronType mPolyhedron; + + /// + virtual void _renderObject( ObjectRenderInst* ri, SceneRenderState* state, BaseMatInstance* overrideMat ); + + public: + + ScenePolyhedralObject() + : mIsBox( true ) {} + + ScenePolyhedralObject( const PolyhedronType& polyhedron ) + : mIsBox( false ), + mPolyhedron( polyhedron ) {} + + /// Return the polyhedron that describes the space. + const PolyhedronType& getPolyhedron() const { return mPolyhedron; } + + // SimObject. + virtual bool onAdd(); + virtual void writeFields( Stream& stream, U32 tabStop ); + virtual bool writeField( StringTableEntry name, const char* value ); + + static void initPersistFields(); + + // NetObject. + virtual U32 packUpdate( NetConnection* connection, U32 mask, BitStream* stream ); + virtual void unpackUpdate( NetConnection* connection, BitStream* stream ); + + // SceneObject. + virtual bool containsPoint( const Point3F& point ); + + // IScenePolyhedralObject. + virtual AnyPolyhedron ToAnyPolyhedron() const { return getPolyhedron(); } + + private: + + static bool _setPlane( void* object, const char* index, const char* data ); + static bool _setPoint( void* object, const char* index, const char* data ); + static bool _setEdge( void* object, const char* index, const char* data ); +}; + +#endif // !_SCENEPOLYHEDRALOBJECT_H_ diff --git a/Engine/source/scene/mixin/scenePolyhedralObject.impl.h b/Engine/source/scene/mixin/scenePolyhedralObject.impl.h new file mode 100644 index 000000000..43a1e3e9f --- /dev/null +++ b/Engine/source/scene/mixin/scenePolyhedralObject.impl.h @@ -0,0 +1,395 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "scene/mixin/scenePolyhedralObject.h" + +#include "console/consoleTypes.h" +#include "gfx/gfxDrawUtil.h" +#include "gfx/gfxTransformSaver.h" +#include "core/stream/bitStream.h" +#include "math/mathIO.h" + +#if 0 // Enable when enabling debug rendering below. +#include "scene/sceneRenderState.h" +#include "gfx/sim/debugDraw.h" +#endif + + +//----------------------------------------------------------------------------- + +template< typename Base, typename P > +void ScenePolyhedralObject< Base, P >::initPersistFields() +{ + Parent::addGroup( "Internal" ); + + Parent::addProtectedField( "plane", TypeRealString, NULL, + &_setPlane, &defaultProtectedGetFn, + "For internal use only.", + AbstractClassRep::FIELD_HideInInspectors ); + Parent::addProtectedField( "point", TypeRealString, NULL, + &_setPoint, &defaultProtectedGetFn, + "For internal use only.", + AbstractClassRep::FIELD_HideInInspectors ); + Parent::addProtectedField( "edge", TypeRealString, NULL, + &_setEdge, &defaultProtectedGetFn, + "For internal use only.", + AbstractClassRep::FIELD_HideInInspectors ); + + Parent::endGroup( "Internal" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +template< typename Base, typename P > +bool ScenePolyhedralObject< Base, P >::onAdd() +{ + // If no polyhedron has been initialized for the zone, default + // to object box. Do this before calling the parent's onAdd() + // so that we set the object box correctly. + + if( mPolyhedron.getNumPlanes() == 0 ) + { + mPolyhedron.buildBox( MatrixF::Identity, this->getObjBox() ); + mIsBox = true; + } + else + { + mIsBox = false; + + // Compute object-space bounds from polyhedron. + this->mObjBox = mPolyhedron.getBounds(); + } + + if( !Parent::onAdd() ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- + +template< typename Base, typename P > +bool ScenePolyhedralObject< Base, P >::containsPoint( const Point3F& point ) +{ + // If our shape is the OBB, use the default implementation + // inherited from SceneObject. + + if( this->mIsBox ) + return Parent::containsPoint( point ); + + // Take the point into our local object space. + + Point3F p = point; + this->getWorldTransform().mulP( p ); + p.convolveInverse( this->getScale() ); + + // See if the polyhedron contains the point. + + return mPolyhedron.isContained( p ); +} + +//----------------------------------------------------------------------------- + +template< typename Base, typename P > +void ScenePolyhedralObject< Base, P >::_renderObject( ObjectRenderInst* ri, SceneRenderState* state, BaseMatInstance* overrideMat ) +{ + if( overrideMat ) + return; + + if( this->mIsBox ) + Parent::_renderObject( ri, state, overrideMat ); + else if( !this->mEditorRenderMaterial ) + { + GFXTransformSaver saver; + + MatrixF mat = this->getRenderTransform(); + mat.scale( this->getScale() ); + + GFX->multWorld( mat ); + + GFXStateBlockDesc desc; + desc.setZReadWrite( true, false ); + desc.setBlend( true ); + desc.setCullMode( GFXCullNone ); + + GFX->getDrawUtil()->drawPolyhedron( desc, mPolyhedron, this->_getDefaultEditorSolidColor() ); + + // Render black wireframe. + + desc.setFillModeWireframe(); + GFX->getDrawUtil()->drawPolyhedron( desc, mPolyhedron, this->_getDefaultEditorWireframeColor() ); + } + else + { + //TODO: render polyhedron with material + } + + // Debug rendering. + + #if 0 + if( state->isDiffusePass() ) + DebugDrawer::get()->drawPolyhedronDebugInfo( mPolyhedron, this->getTransform(), this->getScale() ); + #endif +} + +//----------------------------------------------------------------------------- + +template< typename Base, typename P > +U32 ScenePolyhedralObject< Base, P >::packUpdate( NetConnection* connection, U32 mask, BitStream* stream ) +{ + U32 retMask = Parent::packUpdate( connection, mask, stream ); + + if( stream->writeFlag( !mIsBox && ( mask & PolyMask ) ) ) + { + // Write planes. + + const U32 numPlanes = mPolyhedron.getNumPlanes(); + const typename PolyhedronType::PlaneType* planes = mPolyhedron.getPlanes(); + + stream->writeInt( numPlanes, 8 ); + for( U32 i = 0; i < numPlanes; ++ i ) + mathWrite( *stream, planes[ i ] ); + + // Write points. + + const U32 numPoints = mPolyhedron.getNumPoints(); + const typename PolyhedronType::PointType* points = mPolyhedron.getPoints(); + + stream->writeInt( numPoints, 8 ); + for( U32 i = 0; i < numPoints; ++ i ) + mathWrite( *stream, points[ i ] ); + + // Write edges. + + const U32 numEdges = mPolyhedron.getNumEdges(); + const typename PolyhedronType::EdgeType* edges = mPolyhedron.getEdges(); + + stream->writeInt( numEdges, 8 ); + for( U32 i = 0; i < numEdges; ++ i ) + { + const typename PolyhedronType::EdgeType& edge = edges[ i ]; + + stream->writeInt( edge.face[ 0 ], 8 ); + stream->writeInt( edge.face[ 1 ], 8 ); + stream->writeInt( edge.vertex[ 0 ], 8 ); + stream->writeInt( edge.vertex[ 1 ], 8 ); + } + } + + return retMask; +} + +//----------------------------------------------------------------------------- + +template< typename Base, typename P > +void ScenePolyhedralObject< Base, P >::unpackUpdate( NetConnection* connection, BitStream* stream ) +{ + Parent::unpackUpdate( connection, stream ); + + if( stream->readFlag() ) // PolyMask + { + // Read planes. + + const U32 numPlanes = stream->readInt( 8 ); + mPolyhedron.planeList.setSize( numPlanes ); + + for( U32 i = 0; i < numPlanes; ++ i ) + mathRead( *stream, &mPolyhedron.planeList[ i ] ); + + // Read points. + + const U32 numPoints = stream->readInt( 8 ); + mPolyhedron.pointList.setSize( numPoints ); + + for( U32 i = 0; i < numPoints; ++ i ) + mathRead( *stream, &mPolyhedron.pointList[ i ] ); + + // Read edges. + + const U32 numEdges = stream->readInt( 8 ); + mPolyhedron.edgeList.setSize( numEdges ); + + for( U32 i = 0; i < numEdges; ++ i ) + { + typename PolyhedronType::EdgeType& edge = mPolyhedron.edgeList[ i ]; + + edge.face[ 0 ] = stream->readInt( 8 ); + edge.face[ 1 ] = stream->readInt( 8 ); + edge.vertex[ 0 ] = stream->readInt( 8 ); + edge.vertex[ 1 ] = stream->readInt( 8 ); + } + } +} + +//----------------------------------------------------------------------------- + +template< typename Base, typename P > +bool ScenePolyhedralObject< Base, P >::writeField( StringTableEntry name, const char* value ) +{ + StringTableEntry sPlane = StringTable->insert( "plane" ); + StringTableEntry sPoint = StringTable->insert( "point" ); + StringTableEntry sEdge = StringTable->insert( "edge" ); + + if( name == sPlane || name == sPoint || name == sEdge ) + return false; + + return Parent::writeField( name, value ); +} + +//----------------------------------------------------------------------------- + +template< typename Base, typename P > +void ScenePolyhedralObject< Base, P >::writeFields( Stream& stream, U32 tabStop ) +{ + Parent::writeFields( stream, tabStop ); + + // If the polyhedron is the same as our object box, + // don't bother writing out the planes and points. + + if( mIsBox ) + return; + + stream.write( 2, "\r\n" ); + + // Write all planes. + + const U32 numPlanes = mPolyhedron.getNumPlanes(); + for( U32 i = 0; i < numPlanes; ++ i ) + { + const PlaneF& plane = mPolyhedron.getPlanes()[ i ]; + + stream.writeTabs( tabStop ); + + char buffer[ 1024 ]; + dSprintf( buffer, sizeof( buffer ), "plane = \"%g %g %g %g\";", + plane.x, plane.y, plane.z, plane.d + ); + + stream.writeLine( reinterpret_cast< const U8* >( buffer ) ); + } + + // Write all points. + + const U32 numPoints = mPolyhedron.getNumPoints(); + for( U32 i = 0; i < numPoints; ++ i ) + { + const Point3F& point = mPolyhedron.getPoints()[ i ]; + + stream.writeTabs( tabStop ); + + char buffer[ 1024 ]; + dSprintf( buffer, sizeof( buffer ), "point = \"%g %g %g\";", + point.x, point.y, point.z + ); + + stream.writeLine( reinterpret_cast< const U8* >( buffer ) ); + } + + // Write all edges. + + const U32 numEdges = mPolyhedron.getNumEdges(); + for( U32 i = 0; i < numEdges; ++ i ) + { + const PolyhedronData::Edge& edge = mPolyhedron.getEdges()[ i ]; + + stream.writeTabs( tabStop ); + + char buffer[ 1024 ]; + dSprintf( buffer, sizeof( buffer ), "edge = \"%i %i %i %i\";", + edge.face[ 0 ], edge.face[ 1 ], + edge.vertex[ 0 ], edge.vertex[ 1 ] + ); + + stream.writeLine( reinterpret_cast< const U8* >( buffer ) ); + } +} + +//----------------------------------------------------------------------------- + +template< typename Base, typename P > +bool ScenePolyhedralObject< Base, P >::_setPlane( void* object, const char* index, const char* data ) +{ + ScenePolyhedralObject* obj = reinterpret_cast< ScenePolyhedralObject* >( object ); + + PlaneF plane; + + dSscanf( data, "%g %g %g %g", + &plane.x, + &plane.y, + &plane.z, + &plane.d + ); + + obj->mPolyhedron.planeList.push_back( plane ); + obj->setMaskBits( PolyMask ); + obj->mIsBox = false; + + return false; +} + +//----------------------------------------------------------------------------- + +template< typename Base, typename P > +bool ScenePolyhedralObject< Base, P >::_setPoint( void* object, const char* index, const char* data ) +{ + ScenePolyhedralObject* obj = reinterpret_cast< ScenePolyhedralObject* >( object ); + + Point3F point; + + dSscanf( data, "%g %g %g %g", + &point[ 0 ], + &point[ 1 ], + &point[ 2 ] + ); + + obj->mPolyhedron.pointList.push_back( point ); + obj->setMaskBits( PolyMask ); + obj->mIsBox = false; + + return false; +} + +//----------------------------------------------------------------------------- + +template< typename Base, typename P > +bool ScenePolyhedralObject< Base, P >::_setEdge( void* object, const char* index, const char* data ) +{ + ScenePolyhedralObject* obj = reinterpret_cast< ScenePolyhedralObject* >( object ); + + PolyhedronData::Edge edge; + + dSscanf( data, "%i %i %i %i", + &edge.face[ 0 ], + &edge.face[ 1 ], + &edge.vertex[ 0 ], + &edge.vertex[ 1 ] + ); + + obj->mPolyhedron.edgeList.push_back( edge ); + obj->setMaskBits( PolyMask ); + obj->mIsBox = false; + + return false; +} diff --git a/Engine/source/scene/pathManager.cpp b/Engine/source/scene/pathManager.cpp new file mode 100644 index 000000000..2ed41d17a --- /dev/null +++ b/Engine/source/scene/pathManager.cpp @@ -0,0 +1,434 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "gfx/gfxDevice.h" +#include "scene/pathManager.h" +#include "sim/netConnection.h" +#include "core/stream/bitStream.h" +#include "scene/simPath.h" +#include "interior/interiorInstance.h" +#include "math/mathIO.h" +#include "scene/sceneRenderState.h" +#include "scene/sceneManager.h" +#include "platform/profiler.h" +#include "core/module.h" + +extern bool gEditingMission; + + +namespace { + +U32 countNumBits(U32 n) +{ + U32 count = 0; + while (n != 0) { + n >>= 1; + count++; + } + + return count ? count : 1; +} + +} // namespace {} + + +MODULE_BEGIN( PathManager ) + + MODULE_INIT + { + AssertFatal(gClientPathManager == NULL && gServerPathManager == NULL, "Error, already initialized the path manager!"); + + gClientPathManager = new PathManager(false); + gServerPathManager = new PathManager(true); + } + + MODULE_SHUTDOWN + { + AssertFatal(gClientPathManager != NULL && gServerPathManager != NULL, "Error, path manager not initialized!"); + + delete gClientPathManager; + gClientPathManager = NULL; + delete gServerPathManager; + gServerPathManager = NULL; + } + +MODULE_END; + + +//-------------------------------------------------------------------------- +//-------------------------------------- PathManagerEvent +// +class PathManagerEvent : public NetEvent +{ + public: + U32 modifiedPath; + bool clearPaths; + PathManager::PathEntry path; + + public: + typedef NetEvent Parent; + PathManagerEvent() { } + + void pack(NetConnection*, BitStream*); + void write(NetConnection*, BitStream*); + void unpack(NetConnection*, BitStream*); + void process(NetConnection*); + + DECLARE_CONOBJECT(PathManagerEvent); +}; + +void PathManagerEvent::pack(NetConnection*, BitStream* stream) +{ + // Write out the modified path... + stream->write(modifiedPath); + stream->writeFlag(clearPaths); + stream->write(path.totalTime); + stream->write(path.positions.size()); + + + // This is here for safety. You can remove it if you want to try your luck at bigger sizes. -- BJG + AssertWarn(path.positions.size() < 1500/40, "Warning! Path size is pretty big - may cause packet overrun!"); + + // Each one of these is about 8 floats and 2 ints + // so we'll say it's about 40 bytes in size, which is where the 40 in the above calc comes from. + for (U32 j = 0; j < path.positions.size(); j++) + { + mathWrite(*stream, path.positions[j]); + mathWrite(*stream, path.rotations[j]); + stream->write(path.msToNext[j]); + stream->write(path.smoothingType[j]); + } +} + +void PathManagerEvent::write(NetConnection*nc, BitStream *stream) +{ + pack(nc, stream); +} + +void PathManagerEvent::unpack(NetConnection*, BitStream* stream) +{ + // Read in the modified path... + + stream->read(&modifiedPath); + clearPaths = stream->readFlag(); + stream->read(&path.totalTime); + + U32 numPoints; + stream->read(&numPoints); + path.positions.setSize(numPoints); + path.rotations.setSize(numPoints); + path.msToNext.setSize(numPoints); + path.smoothingType.setSize(numPoints); + for (U32 j = 0; j < path.positions.size(); j++) + { + mathRead(*stream, &path.positions[j]); + mathRead(*stream, &path.rotations[j]); + stream->read(&path.msToNext[j]); + stream->read(&path.smoothingType[j]); + } +} + +void PathManagerEvent::process(NetConnection*) +{ + if (clearPaths) + { + // Clear out all the client's paths... + gClientPathManager->clearPaths(); + } + AssertFatal(modifiedPath <= gClientPathManager->mPaths.size(), "Error out of bounds path!"); + if (modifiedPath == gClientPathManager->mPaths.size()) { + PathManager::PathEntry *pe = new PathManager::PathEntry; + *pe = path; + gClientPathManager->mPaths.push_back(pe); + } + else + *(gClientPathManager->mPaths[modifiedPath]) = path; +} + +IMPLEMENT_CO_NETEVENT_V1(PathManagerEvent); + +// Will be internalized once the @internal tag is working +ConsoleDocClass( PathManagerEvent, + "@brief Class responsible for the registration, transmission, and management " + "of paths on client and server.\n\n" + + "For internal use only, not intended for use in TorqueScript or game development\n\n" + + "@internal\n" +); + +//-------------------------------------------------------------------------- +//-------------------------------------- PathManager Implementation +// +PathManager* gClientPathManager = NULL; +PathManager* gServerPathManager = NULL; + +//-------------------------------------------------------------------------- +PathManager::PathManager(const bool isServer) +{ + VECTOR_SET_ASSOCIATION(mPaths); + + mIsServer = isServer; +} + +PathManager::~PathManager() +{ + clearPaths(); +} + +void PathManager::clearPaths() +{ + for (U32 i = 0; i < mPaths.size(); i++) + delete mPaths[i]; + mPaths.setSize(0); +#ifdef TORQUE_DEBUG + // This gets rid of the memory used by the vector. + // Prevents it from showing up in memory leak logs. + mPaths.compact(); +#endif +} + +ConsoleFunction(clearServerPaths, void, 1, 1, "") +{ + gServerPathManager->clearPaths(); +} + +ConsoleFunction(clearClientPaths, void, 1, 1, "") +{ + gClientPathManager->clearPaths(); +} + +//-------------------------------------------------------------------------- +U32 PathManager::allocatePathId() +{ + mPaths.increment(); + mPaths.last() = new PathEntry; + + return (mPaths.size() - 1); +} + + +void PathManager::updatePath(const U32 id, + const Vector& positions, + const Vector& rotations, + const Vector& times, + const Vector& smoothingTypes) +{ + AssertFatal(mIsServer == true, "PathManager::updatePath: Error, must be called on the server side"); + AssertFatal(id < mPaths.size(), "PathManager::updatePath: error, id out of range"); + AssertFatal(positions.size() == times.size() && positions.size() == smoothingTypes.size(), "Error, times and positions must match!"); + + PathEntry& rEntry = *mPaths[id]; + + rEntry.positions = positions; + rEntry.rotations = rotations; + rEntry.msToNext = times; + rEntry.smoothingType = smoothingTypes; + + rEntry.totalTime = 0; + for (S32 i = 0; i < S32(rEntry.msToNext.size()); i++) + rEntry.totalTime += rEntry.msToNext[i]; + + transmitPath(id); +} + + +//-------------------------------------------------------------------------- +void PathManager::transmitPaths(NetConnection* nc) +{ + AssertFatal(mIsServer, "Error, cannot call transmitPaths on client path manager!"); + + // Send over paths + for(S32 i = 0; i < mPaths.size(); i++) + { + PathManagerEvent* event = new PathManagerEvent; + event->clearPaths = (i == 0); + event->modifiedPath = i; + event->path = *(mPaths[i]); + nc->postNetEvent(event); + } +} + +void PathManager::transmitPath(const U32 id) +{ + AssertFatal(mIsServer, "Error, cannot call transmitNewPath on client path manager!"); + + // Post to all active clients that have already received their paths... + // + SimGroup* pClientGroup = Sim::getClientGroup(); + for (SimGroup::iterator itr = pClientGroup->begin(); itr != pClientGroup->end(); itr++) { + NetConnection* nc = dynamic_cast(*itr); + if (nc && nc->missionPathsSent()) + { + // Transmit the updated path... + PathManagerEvent* event = new PathManagerEvent; + event->modifiedPath = id; + event->clearPaths = false; + event->path = *(mPaths[id]); + nc->postNetEvent(event); + } + } +} + +void PathManager::getPathPosition(const U32 id, + const F64 msPosition, + Point3F& rPosition, + QuatF &rotation) +{ + AssertFatal(isValidPath(id), "Error, this is not a valid path!"); + PROFILE_START(PathManGetPos); + + // Ok, query holds our path information... + F64 ms = msPosition; + if (ms > mPaths[id]->totalTime) + ms = mPaths[id]->totalTime; + + S32 startNode = 0; + while (ms > mPaths[id]->msToNext[startNode]) { + ms -= mPaths[id]->msToNext[startNode]; + startNode++; + } + S32 endNode = (startNode + 1) % mPaths[id]->positions.size(); + + Point3F& rStart = mPaths[id]->positions[startNode]; + Point3F& rEnd = mPaths[id]->positions[endNode]; + + F64 interp = ms / F32(mPaths[id]->msToNext[startNode]); + if(mPaths[id]->smoothingType[startNode] == Marker::SmoothingTypeLinear) + { + rPosition = (rStart * (1.0 - interp)) + (rEnd * interp); + } + else if(mPaths[id]->smoothingType[startNode] == Marker::SmoothingTypeAccelerate) + { + interp = mSin(interp * M_PI - (M_PI / 2)) * 0.5 + 0.5; + rPosition = (rStart * (1.0 - interp)) + (rEnd * interp); + } + else if(mPaths[id]->smoothingType[startNode] == Marker::SmoothingTypeSpline) + { + S32 preStart = startNode - 1; + S32 postEnd = endNode + 1; + if(postEnd >= mPaths[id]->positions.size()) + postEnd = 0; + if(preStart < 0) + preStart = mPaths[id]->positions.size() - 1; + Point3F p0 = mPaths[id]->positions[preStart]; + Point3F p1 = rStart; + Point3F p2 = rEnd; + Point3F p3 = mPaths[id]->positions[postEnd]; + rPosition.x = mCatmullrom(interp, p0.x, p1.x, p2.x, p3.x); + rPosition.y = mCatmullrom(interp, p0.y, p1.y, p2.y, p3.y); + rPosition.z = mCatmullrom(interp, p0.z, p1.z, p2.z, p3.z); + } + rotation.interpolate( mPaths[id]->rotations[startNode], mPaths[id]->rotations[endNode], interp ); + PROFILE_END(); +} + +U32 PathManager::getPathTotalTime(const U32 id) const +{ + AssertFatal(isValidPath(id), "Error, this is not a valid path!"); + + return mPaths[id]->totalTime; +} + +U32 PathManager::getPathNumWaypoints(const U32 id) const +{ + AssertFatal(isValidPath(id), "Error, this is not a valid path!"); + + return mPaths[id]->positions.size(); +} + +U32 PathManager::getWaypointTime(const U32 id, const U32 wayPoint) const +{ + AssertFatal(isValidPath(id), "Error, this is not a valid path!"); + AssertFatal(wayPoint < getPathNumWaypoints(id), "Invalid waypoint!"); + + U32 time = 0; + for (U32 i = 0; i < wayPoint; i++) + time += mPaths[id]->msToNext[i]; + + return time; +} + +U32 PathManager::getPathTimeBits(const U32 id) +{ + AssertFatal(isValidPath(id), "Error, this is not a valid path!"); + + return countNumBits(mPaths[id]->totalTime); +} + +U32 PathManager::getPathWaypointBits(const U32 id) +{ + AssertFatal(isValidPath(id), "Error, this is not a valid path!"); + + return countNumBits(mPaths[id]->positions.size()); +} + + +bool PathManager::dumpState(BitStream* stream) const +{ + stream->write(mPaths.size()); + + for (U32 i = 0; i < mPaths.size(); i++) { + const PathEntry& rEntry = *mPaths[i]; + stream->write(rEntry.totalTime); + + stream->write(rEntry.positions.size()); + for (U32 j = 0; j < rEntry.positions.size(); j++) { + mathWrite(*stream, rEntry.positions[j]); + stream->write(rEntry.msToNext[j]); + } + } + + return stream->getStatus() == Stream::Ok; +} + +bool PathManager::readState(BitStream* stream) +{ + U32 i; + for (i = 0; i < mPaths.size(); i++) + delete mPaths[i]; + + U32 numPaths; + stream->read(&numPaths); + mPaths.setSize(numPaths); + + for (i = 0; i < mPaths.size(); i++) { + mPaths[i] = new PathEntry; + PathEntry& rEntry = *mPaths[i]; + + stream->read(&rEntry.totalTime); + + U32 numPositions; + stream->read(&numPositions); + rEntry.positions.setSize(numPositions); + rEntry.msToNext.setSize(numPositions); + for (U32 j = 0; j < rEntry.positions.size(); j++) { + mathRead(*stream, &rEntry.positions[j]); + stream->read(&rEntry.msToNext[j]); + } + } + + return stream->getStatus() == Stream::Ok; +} + + + diff --git a/Engine/source/scene/pathManager.h b/Engine/source/scene/pathManager.h new file mode 100644 index 000000000..d239c275f --- /dev/null +++ b/Engine/source/scene/pathManager.h @@ -0,0 +1,129 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PATHMANAGER_H_ +#define _PATHMANAGER_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _MQUAT_H_ +#include "math/mQuat.h" +#endif +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif + +class NetConnection; +class BitStream; + +class PathManager +{ + friend class PathManagerEvent; + + private: + struct PathEntry { + U32 totalTime; + + Vector positions; + Vector rotations; + Vector smoothingType; + Vector msToNext; + + PathEntry() { + VECTOR_SET_ASSOCIATION(positions); + VECTOR_SET_ASSOCIATION(rotations); + VECTOR_SET_ASSOCIATION(smoothingType); + VECTOR_SET_ASSOCIATION(msToNext); + } + }; + + Vector mPaths; + + public: + enum PathType { + BackAndForth, + Looping + }; + + public: + PathManager(const bool isServer); + ~PathManager(); + + void clearPaths(); + + //-------------------------------------- Path querying + public: + bool isValidPath(const U32 id) const; + void getPathPosition(const U32 id, const F64 msPosition, Point3F& rPosition, QuatF &rotation); + U32 getPathTotalTime(const U32 id) const; + U32 getPathNumWaypoints(const U32 id) const; + U32 getWaypointTime(const U32 id, const U32 wayPoint) const; + + U32 getPathTimeBits(const U32 id); + U32 getPathWaypointBits(const U32 id); + + //-------------------------------------- Path Registration/Transmission/Management + public: + // Called after mission load to clear out the paths on the client, and to transmit + // the information for the current mission's paths. + void transmitPaths(NetConnection*); + void transmitPath(U32); + + U32 allocatePathId(); + void updatePath(const U32 id, const Vector&, const Vector&, const Vector &, const Vector&); + + //-------------------------------------- State dumping/reading + public: + bool dumpState(BitStream*) const; + bool readState(BitStream*); + + private: + bool mIsServer; + bool mPathsSent; +}; + +struct PathNode { + Point3F position; + QuatF rotation; + U32 smoothingType; + U32 msToNext; +}; + +extern PathManager* gClientPathManager; +extern PathManager* gServerPathManager; + +//-------------------------------------------------------------------------- +inline bool PathManager::isValidPath(const U32 id) const +{ + return (id < U32(mPaths.size())) && mPaths[id]->positions.size() > 0; +} + + + +#endif // _H_PATHMANAGER diff --git a/Engine/source/scene/reflectionManager.cpp b/Engine/source/scene/reflectionManager.cpp new file mode 100644 index 000000000..7ef64f240 --- /dev/null +++ b/Engine/source/scene/reflectionManager.cpp @@ -0,0 +1,315 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "scene/reflectionManager.h" + +#include "platform/profiler.h" +#include "platform/platformTimer.h" +#include "console/consoleTypes.h" +#include "core/tAlgorithm.h" +#include "math/mMathFn.h" +#include "T3D/gameBase/gameConnection.h" +#include "ts/tsShapeInstance.h" +#include "gui/3d/guiTSControl.h" +#include "scene/sceneManager.h" +#include "gfx/gfxDebugEvent.h" +#include "gfx/gfxStringEnumTranslate.h" +#include "gfx/screenshot.h" +#include "core/module.h" +#include "scene/reflectionMatHook.h" +#include "console/engineAPI.h" + + +MODULE_BEGIN( ReflectionManager ) + + MODULE_INIT + { + ManagedSingleton< ReflectionManager >::createSingleton(); + ReflectionManager::initConsole(); + } + + MODULE_SHUTDOWN + { + ManagedSingleton< ReflectionManager >::deleteSingleton(); + } + +MODULE_END; + + +GFX_ImplementTextureProfile( ReflectRenderTargetProfile, + GFXTextureProfile::DiffuseMap, + GFXTextureProfile::PreserveSize | GFXTextureProfile::NoMipmap | GFXTextureProfile::RenderTarget | GFXTextureProfile::Pooled, + GFXTextureProfile::None ); + +GFX_ImplementTextureProfile( RefractTextureProfile, + GFXTextureProfile::DiffuseMap, + GFXTextureProfile::PreserveSize | + GFXTextureProfile::RenderTarget | + GFXTextureProfile::Pooled, + GFXTextureProfile::None ); + +static S32 QSORT_CALLBACK compareReflectors( const void *a, const void *b ) +{ + const ReflectorBase *A = *((ReflectorBase**)a); + const ReflectorBase *B = *((ReflectorBase**)b); + + F32 dif = B->score - A->score; + return (S32)mFloor( dif ); +} + + +U32 ReflectionManager::smFrameReflectionMS = 10; +F32 ReflectionManager::smRefractTexScale = 0.5f; + +ReflectionManager::ReflectionManager() + : mUpdateRefract( true ), + mReflectFormat( GFXFormatR8G8B8A8 ) +{ + mTimer = PlatformTimer::create(); + + GFXDevice::getDeviceEventSignal().notify( this, &ReflectionManager::_handleDeviceEvent ); +} + +void ReflectionManager::initConsole() +{ + Con::addVariable( "$pref::Reflect::refractTexScale", TypeF32, &ReflectionManager::smRefractTexScale, "RefractTex has dimensions equal to the active render target scaled in both x and y by this float.\n" + "@ingroup Rendering"); + Con::addVariable( "$pref::Reflect::frameLimitMS", TypeS32, &ReflectionManager::smFrameReflectionMS, "ReflectionManager tries not to spend more than this amount of time updating reflections per frame.\n" + "@ingroup Rendering"); +} + +ReflectionManager::~ReflectionManager() +{ + SAFE_DELETE( mTimer ); + AssertFatal( mReflectors.size() == 0, "ReflectionManager, some reflectors were left nregistered!" ); + + GFXDevice::getDeviceEventSignal().remove( this, &ReflectionManager::_handleDeviceEvent ); +} + +void ReflectionManager::registerReflector( ReflectorBase *reflector ) +{ + mReflectors.push_back_unique( reflector ); +} + +void ReflectionManager::unregisterReflector( ReflectorBase *reflector ) +{ + mReflectors.remove( reflector ); +} + +void ReflectionManager::update( F32 timeSlice, + const Point2I &resolution, + const CameraQuery &query ) +{ + GFXDEBUGEVENT_SCOPE( UpdateReflections, ColorI::WHITE ); + + if ( mReflectors.empty() ) + return; + + PROFILE_SCOPE( ReflectionManager_Update ); + + // Calculate our target time from the slice. + U32 targetMs = timeSlice * smFrameReflectionMS; + + // Setup a culler for testing the + // visibility of reflectors. + Frustum culler; + culler.set( false, + query.fov, + (F32)resolution.x / (F32)resolution.y, + query.nearPlane, + query.farPlane, + query.cameraMatrix ); + + // Manipulate the frustum for tiled screenshots + const bool screenShotMode = gScreenShot && gScreenShot->isPending(); + if ( screenShotMode ) + gScreenShot->tileFrustum( culler ); + + // We use the frame time and not real time + // here as this may be called multiple times + // within a frame. + U32 startOfUpdateMs = Platform::getVirtualMilliseconds(); + + // Save this for interested parties. + mLastUpdateMs = startOfUpdateMs; + + ReflectParams refparams; + refparams.query = &query; + refparams.viewportExtent = resolution; + refparams.culler = culler; + refparams.startOfUpdateMs = startOfUpdateMs; + + // Update the reflection score. + ReflectorList::iterator reflectorIter = mReflectors.begin(); + for ( ; reflectorIter != mReflectors.end(); reflectorIter++ ) + (*reflectorIter)->calcScore( refparams ); + + // Sort them by the score. + dQsort( mReflectors.address(), mReflectors.size(), sizeof(ReflectorBase*), compareReflectors ); + + // Update as many reflections as we can + // within the target time limit. + mTimer->getElapsedMs(); + mTimer->reset(); + U32 numUpdated = 0; + reflectorIter = mReflectors.begin(); + for ( ; reflectorIter != mReflectors.end(); reflectorIter++ ) + { + // We're sorted by score... so once we reach + // a zero score we have nothing more to update. + if ( (*reflectorIter)->score <= 0.0f && !screenShotMode ) + break; + + (*reflectorIter)->updateReflection( refparams ); + (*reflectorIter)->lastUpdateMs = startOfUpdateMs; + numUpdated++; + + // If we run out of update time then stop. + if ( mTimer->getElapsedMs() > targetMs && !screenShotMode && (*reflectorIter)->score < 1000.0f ) + break; + } + + U32 totalElapsed = mTimer->getElapsedMs(); + + // Set metric/debug related script variables... + + U32 numEnabled = mReflectors.size(); + U32 numVisible = 0; + U32 numOccluded = 0; + + reflectorIter = mReflectors.begin(); + for ( ; reflectorIter != mReflectors.end(); reflectorIter++ ) + { + ReflectorBase *pReflector = (*reflectorIter); + if ( pReflector->isOccluded() ) + numOccluded++; + else + numVisible++; + } + +#ifdef TORQUE_GATHER_METRICS + const GFXTextureProfileStats &stats = ReflectRenderTargetProfile.getStats(); + + F32 mb = ( stats.activeBytes / 1024.0f ) / 1024.0f; + char temp[256]; + + dSprintf( temp, 256, "%s %d %0.2f\n", + ReflectRenderTargetProfile.getName().c_str(), + stats.activeCount, + mb ); + + Con::setVariable( "$Reflect::textureStats", temp ); + Con::setIntVariable( "$Reflect::renderTargetsAllocated", stats.allocatedTextures ); + Con::setIntVariable( "$Reflect::poolSize", stats.activeCount ); + Con::setIntVariable( "$Reflect::numObjects", numEnabled ); + Con::setIntVariable( "$Reflect::numVisible", numVisible ); + Con::setIntVariable( "$Reflect::numOccluded", numOccluded ); + Con::setIntVariable( "$Reflect::numUpdated", numUpdated ); + Con::setIntVariable( "$Reflect::elapsed", totalElapsed ); +#endif +} + +GFXTexHandle ReflectionManager::allocRenderTarget( const Point2I &size ) +{ + return GFXTexHandle( size.x, size.y, mReflectFormat, + &ReflectRenderTargetProfile, + avar("%s() - mReflectTex (line %d)", __FUNCTION__, __LINE__) ); +} + +GFXTextureObject* ReflectionManager::getRefractTex() +{ + GFXTarget *target = GFX->getActiveRenderTarget(); + GFXFormat targetFormat = target->getFormat(); + const Point2I &targetSize = target->getSize(); + +#if defined(TORQUE_OS_XENON) + // On the Xbox360, it needs to do a resolveTo from the active target, so this + // may as well be the full size of the active target + const U32 desWidth = targetSize.x; + const U32 desHeight = targetSize.y; +#else + const U32 desWidth = mFloor( (F32)targetSize.x * smRefractTexScale ); + const U32 desHeight = mFloor( ( F32)targetSize.y * smRefractTexScale ); +#endif + + if ( mRefractTex.isNull() || + mRefractTex->getWidth() != desWidth || + mRefractTex->getHeight() != desHeight || + mRefractTex->getFormat() != targetFormat ) + { + mRefractTex.set( desWidth, desHeight, targetFormat, &RefractTextureProfile, "mRefractTex" ); + mUpdateRefract = true; + } + + if ( mUpdateRefract ) + { + target->resolveTo( mRefractTex ); + mUpdateRefract = false; + } + + return mRefractTex; +} + +BaseMatInstance* ReflectionManager::getReflectionMaterial( BaseMatInstance *inMat ) const +{ + // See if we have an existing material hook. + ReflectionMaterialHook *hook = static_cast( inMat->getHook( ReflectionMaterialHook::Type ) ); + if ( !hook ) + { + // Create a hook and initialize it using the incoming material. + hook = new ReflectionMaterialHook; + hook->init( inMat ); + inMat->addHook( hook ); + } + + return hook->getReflectMat(); +} + +bool ReflectionManager::_handleDeviceEvent( GFXDevice::GFXDeviceEventType evt ) +{ + switch( evt ) + { + case GFXDevice::deStartOfFrame: + + mUpdateRefract = true; + break; + + case GFXDevice::deDestroy: + + mRefractTex = NULL; + break; + + default: + break; + } + + return true; +} + +DefineEngineFunction( setReflectFormat, void, ( GFXFormat format ),, + "Set the reflection texture format.\n" + "@ingroup GFX\n" ) +{ + REFLECTMGR->setReflectFormat( format ); +} + diff --git a/Engine/source/scene/reflectionManager.h b/Engine/source/scene/reflectionManager.h new file mode 100644 index 000000000..3d392af75 --- /dev/null +++ b/Engine/source/scene/reflectionManager.h @@ -0,0 +1,154 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _REFLECTIONMANAGER_H_ +#define _REFLECTIONMANAGER_H_ + +#ifndef _GFXDEVICE_H_ +#include "gfx/gfxDevice.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _UTIL_DELEGATE_H_ +#include "core/util/delegate.h" +#endif +#ifndef _GFXTEXTUREHANDLE_H_ +#include "gfx/gfxTextureHandle.h" +#endif +#ifndef _TSINGLETON_H_ +#include "core/util/tSingleton.h" +#endif +#ifndef _REFLECTOR_H_ +#include "scene/reflector.h" +#endif + +class PlatformTimer; +class BaseMatInstance; + +enum ReflectMode +{ + ReflectNever = 0, + ReflectDynamic, + ReflectAlways +}; + +typedef Delegate ReflectDelegate; +class SceneObject; + +struct Reflector +{ + SceneObject *object; + ReflectDelegate updateFn; + F32 priority; + U32 maxRateMs; + F32 maxDist; + U32 lastUpdateMs; + F32 score; + bool updated; + bool tried; + bool hasTexture; +}; + +typedef Vector ReflectorVec; + +GFX_DeclareTextureProfile( ReflectRenderTargetProfile ); +GFX_DeclareTextureProfile( RefractTextureProfile ); + +class ReflectionManager +{ +public: + + ReflectionManager(); + virtual ~ReflectionManager(); + + static void initConsole(); + + /// Called to change the reflection texture format. + void setReflectFormat( GFXFormat format ) { mReflectFormat = format; } + + /// Returns the current reflection format. + GFXFormat getReflectFormat() const { return mReflectFormat; } + + /// Doll out callbacks to registered objects based on + /// scoring and elapsed time. This should be called + /// once for each viewport that renders. + void update( F32 timeSlice, + const Point2I &resolution, + const CameraQuery &query ); + + void registerReflector( ReflectorBase *reflector ); + void unregisterReflector( ReflectorBase *reflector ); + + GFXTexHandle allocRenderTarget( const Point2I &size ); + + GFXTextureObject* getRefractTex(); + + BaseMatInstance* getReflectionMaterial( BaseMatInstance *inMat ) const; + + const U32& getLastUpdateMs() const { return mLastUpdateMs; } + +protected: + + bool _handleDeviceEvent( GFXDevice::GFXDeviceEventType evt ); + +protected: + + /// ReflectionManager tries not to spend more than this amount of time + /// updating reflections per frame. + static U32 smFrameReflectionMS; + + /// RefractTex has dimensions equal to the active render target scaled in + /// both x and y by this float. + static F32 smRefractTexScale; + + /// A timer used for tracking update time. + PlatformTimer *mTimer; + + /// All registered reflections which we handle updating. + ReflectorList mReflectors; + + /// Refraction texture copied from the backbuffer once per frame that + /// gets used by all WaterObjects. + GFXTexHandle mRefractTex; + + /// The texture format to use for reflection and + /// refraction texture sources. + GFXFormat mReflectFormat; + + /// Set when the refraction texture is dirty + /// and requires an update. + bool mUpdateRefract; + + /// Platform time in milliseconds of the last update. + U32 mLastUpdateMs; + +public: + // For ManagedSingleton. + static const char* getSingletonName() { return "ReflectionManager"; } +}; + + +/// Returns the ReflectionManager singleton. +#define REFLECTMGR ManagedSingleton::instance() + +#endif // _REFLECTIONMANAGER_H_ \ No newline at end of file diff --git a/Engine/source/scene/reflectionMatHook.cpp b/Engine/source/scene/reflectionMatHook.cpp new file mode 100644 index 000000000..b869cabc9 --- /dev/null +++ b/Engine/source/scene/reflectionMatHook.cpp @@ -0,0 +1,108 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "scene/reflectionMatHook.h" + +#include "materials/materialManager.h" +#include "materials/customMaterialDefinition.h" +#include "materials/materialFeatureTypes.h" +#include "materials/materialFeatureData.h" +#include "shaderGen/featureType.h" +#include "shaderGen/featureMgr.h" +#include "scene/sceneRenderState.h" + + +const MatInstanceHookType ReflectionMaterialHook::Type( "Reflection" ); + +ReflectionMaterialHook::ReflectionMaterialHook() : + mReflectMat(NULL) +{ + +} + +ReflectionMaterialHook::~ReflectionMaterialHook() +{ + SAFE_DELETE(mReflectMat); +} + +void ReflectionMaterialHook::init( BaseMatInstance *inMat ) +{ + if( !inMat->isValid() ) + return; + + Material *reflectMat = (Material*)inMat->getMaterial(); + if ( inMat->isCustomMaterial() ) + { + // This is a custom material... who knows what it really does...do something + // smart here later. + } + + // We may want to disable some states that the material might enable for us. + GFXStateBlockDesc refractState = inMat->getUserStateBlock(); + + // Always z-read, and z-write if the material isn't translucent + refractState.setZReadWrite( true, reflectMat->isTranslucent() ? false : true ); + + // Create reflection material instance. + BaseMatInstance *newMat = new ReflectionMatInstance( reflectMat ); + newMat->setUserObject( inMat->getUserObject() ); + newMat->getFeaturesDelegate().bind( &ReflectionMaterialHook::_overrideFeatures ); + newMat->addStateBlockDesc( refractState ); + if( !newMat->init( inMat->getFeatures(), inMat->getVertexFormat() ) ) + { + SAFE_DELETE( newMat ); + newMat = MATMGR->createWarningMatInstance(); + } + + mReflectMat = newMat; +} + +void ReflectionMaterialHook::_overrideFeatures( ProcessedMaterial *mat, + U32 stageNum, + MaterialFeatureData &fd, + const FeatureSet &features ) +{ + // First stage only in reflections + if( stageNum != 0 ) + { + fd.features.clear(); + return; + } + + // Forward shading on materials in reflections + fd.features.addFeature( MFT_ForwardShading ); + fd.features.addFeature( MFT_Fog ); +} + +//------------------------------------------------------------------------------ + +ReflectionMatInstance::ReflectionMatInstance( Material *mat ) + : MatInstance( *mat ) +{ + +} + +bool ReflectionMatInstance::setupPass( SceneRenderState *state, const SceneData &sgData ) +{ + return Parent::setupPass(state, sgData); +} diff --git a/Engine/source/scene/reflectionMatHook.h b/Engine/source/scene/reflectionMatHook.h new file mode 100644 index 000000000..dab5e2b8c --- /dev/null +++ b/Engine/source/scene/reflectionMatHook.h @@ -0,0 +1,74 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _REFLECTIONMATHOOK_H_ +#define _REFLECTIONMATHOOK_H_ + +#ifndef _MATINSTANCEHOOK_H_ +#include "materials/matInstanceHook.h" +#endif +#ifndef _MATINSTANCE_H_ +#include "materials/matInstance.h" +#endif + +class ReflectionMatInstance : public MatInstance +{ + typedef MatInstance Parent; + +public: + ReflectionMatInstance( Material *mat ); + virtual ~ReflectionMatInstance() {} + + virtual bool setupPass( SceneRenderState *state, const SceneData &sgData ); +}; + +class ReflectionMaterialHook : public MatInstanceHook +{ +public: + + ReflectionMaterialHook(); + + // MatInstanceHook + virtual ~ReflectionMaterialHook(); + virtual const MatInstanceHookType& getType() const { return Type; } + + /// The material hook type. + static const MatInstanceHookType Type; + + BaseMatInstance* getReflectMat() const { return mReflectMat; } + + void init( BaseMatInstance *mat ); + +protected: + + static void _overrideFeatures( ProcessedMaterial *mat, + U32 stageNum, + MaterialFeatureData &fd, + const FeatureSet &features ); + + /// + BaseMatInstance* mReflectMat; + + +}; + +#endif // _SHADOWMATHOOK_H_ diff --git a/Engine/source/scene/reflector.cpp b/Engine/source/scene/reflector.cpp new file mode 100644 index 000000000..df2e7f580 --- /dev/null +++ b/Engine/source/scene/reflector.cpp @@ -0,0 +1,743 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "scene/reflector.h" + +#include "console/consoleTypes.h" +#include "gfx/gfxCubemap.h" +#include "gfx/gfxDebugEvent.h" +#include "gfx/gfxTransformSaver.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "core/stream/bitStream.h" +#include "scene/reflectionManager.h" +#include "gui/3d/guiTSControl.h" +#include "ts/tsShapeInstance.h" +#include "gfx/gfxOcclusionQuery.h" +#include "lighting/lightManager.h" +#include "lighting/shadowMap/lightShadowMap.h" +#include "math/mathUtils.h" +#include "math/util/frustum.h" +#include "gfx/screenshot.h" + +extern ColorI gCanvasClearColor; + + +//------------------------------------------------------------------------- +// ReflectorDesc +//------------------------------------------------------------------------- + +IMPLEMENT_CO_DATABLOCK_V1( ReflectorDesc ); + +ConsoleDocClass( ReflectorDesc, + "@brief A datablock which defines performance and quality properties for " + "dynamic reflections.\n\n" + + "ReflectorDesc is not itself a reflection and does not render reflections. " + "It is a dummy class for holding and exposing to the user a set of " + "reflection related properties. Objects which support dynamic reflections " + "may then reference a ReflectorDesc.\n\n" + + "@tsexample\n" + "datablock ReflectorDesc( ExampleReflectorDesc )\n" + "{\n" + " texSize = 256;\n" + " nearDist = 0.1;\n" + " farDist = 500;\n" + " objectTypeMask = 0xFFFFFFFF;\n" + " detailAdjust = 1.0;\n" + " priority = 1.0;\n" + " maxRateMs = 0;\n" + " useOcclusionQuery = true;\n" + "};\n" + "@endtsexample\n" + + "@see ShapeBaseData::cubeReflectorDesc\n" + "@ingroup enviroMisc" +); + +ReflectorDesc::ReflectorDesc() +{ + texSize = 256; + nearDist = 0.1f; + farDist = 1000.0f; + objectTypeMask = 0xFFFFFFFF; + detailAdjust = 1.0f; + priority = 1.0f; + maxRateMs = 15; + useOcclusionQuery = true; +} + +ReflectorDesc::~ReflectorDesc() +{ +} + +void ReflectorDesc::initPersistFields() +{ + addGroup( "ReflectorDesc" ); + + addField( "texSize", TypeS32, Offset( texSize, ReflectorDesc ), + "Size in pixels of the (square) reflection texture. For a cubemap " + "this value is interpreted as size of each face." ); + + addField( "nearDist", TypeF32, Offset( nearDist, ReflectorDesc ), + "Near plane distance to use when rendering this reflection. Adjust " + "this to limit self-occlusion artifacts." ); + + addField( "farDist", TypeF32, Offset( farDist, ReflectorDesc ), + "Far plane distance to use when rendering reflections." ); + + addField( "objectTypeMask", TypeS32, Offset( objectTypeMask, ReflectorDesc ), + "Object types which render into this reflection." ); + + addField( "detailAdjust", TypeF32, Offset( detailAdjust, ReflectorDesc ), + "Scale applied to lod calculation of objects rendering into " + "this reflection ( modulates $pref::TS::detailAdjust )." ); + + addField( "priority", TypeF32, Offset( priority, ReflectorDesc ), + "Priority for updating this reflection, relative to others." ); + + addField( "maxRateMs", TypeS32, Offset( maxRateMs, ReflectorDesc ), + "If less than maxRateMs has elapsed since this relfection was last " + "updated, then do not update it again. This 'skip' can be disabled by " + "setting maxRateMs to zero." ); + + addField( "useOcclusionQuery", TypeBool, Offset( useOcclusionQuery, ReflectorDesc ), + "If available on the device use HOQs to determine if the reflective object " + "is visible before updating its reflection." ); + + endGroup( "ReflectorDesc" ); + + Parent::initPersistFields(); +} + +void ReflectorDesc::packData( BitStream *stream ) +{ + Parent::packData( stream ); + + stream->write( texSize ); + stream->write( nearDist ); + stream->write( farDist ); + stream->write( objectTypeMask ); + stream->write( detailAdjust ); + stream->write( priority ); + stream->write( maxRateMs ); + stream->writeFlag( useOcclusionQuery ); +} + +void ReflectorDesc::unpackData( BitStream *stream ) +{ + Parent::unpackData( stream ); + + stream->read( &texSize ); + stream->read( &nearDist ); + stream->read( &farDist ); + stream->read( &objectTypeMask ); + stream->read( &detailAdjust ); + stream->read( &priority ); + stream->read( &maxRateMs ); + useOcclusionQuery = stream->readFlag(); +} + +bool ReflectorDesc::preload( bool server, String &errorStr ) +{ + if ( !Parent::preload( server, errorStr ) ) + return false; + + return true; +} + +//------------------------------------------------------------------------- +// ReflectorBase +//------------------------------------------------------------------------- +ReflectorBase::ReflectorBase() +{ + mEnabled = false; + mOccluded = false; + mIsRendering = false; + mDesc = NULL; + mObject = NULL; + mOcclusionQuery = GFX->createOcclusionQuery(); + mQueryPending = false; +} + +ReflectorBase::~ReflectorBase() +{ + delete mOcclusionQuery; +} + +void ReflectorBase::unregisterReflector() +{ + if ( mEnabled ) + { + REFLECTMGR->unregisterReflector( this ); + mEnabled = false; + } +} + +F32 ReflectorBase::calcScore( const ReflectParams ¶ms ) +{ + PROFILE_SCOPE( ReflectorBase_calcScore ); + + // First check the occlusion query to see if we're hidden. + if ( mDesc->useOcclusionQuery && + mOcclusionQuery ) + { + GFXOcclusionQuery::OcclusionQueryStatus status = mOcclusionQuery->getStatus( false ); + + if ( status == GFXOcclusionQuery::Waiting ) + { + mQueryPending = true; + // Don't change mOccluded since we don't know yet, use the value + // from last frame. + } + else + { + mQueryPending = false; + + if ( status == GFXOcclusionQuery::Occluded ) + mOccluded = true; + else if ( status == GFXOcclusionQuery::NotOccluded ) + mOccluded = false; + } + } + + // If we're disabled for any reason then there + // is nothing more left to do. + if ( !mEnabled || + mOccluded || + params.culler.isCulled( mObject->getWorldBox() ) ) + { + score = 0; + return score; + } + + // This mess is calculating a score based on LOD. + + /* + F32 sizeWS = getMax( object->getWorldBox().len_z(), 0.001f ); + Point3F cameraOffset = params.culler.getPosition() - object->getPosition(); + F32 dist = getMax( cameraOffset.len(), 0.01f ); + F32 worldToScreenScaleY = ( params.culler.getNearDist() * params.viewportExtent.y ) / + ( params.culler.getNearTop() - params.culler.getNearBottom() ); + F32 sizeSS = sizeWS / dist * worldToScreenScaleY; + */ + + if ( mDesc->priority == -1.0f ) + { + score = 1000.0f; + return score; + } + + F32 lodFactor = 1.0f; //sizeSS; + + F32 maxRate = getMax( (F32)mDesc->maxRateMs, 1.0f ); + U32 delta = params.startOfUpdateMs - lastUpdateMs; + F32 timeFactor = getMax( (F32)delta / maxRate - 1.0f, 0.0f ); + + score = mDesc->priority * timeFactor * lodFactor; + + return score; +} + + +//------------------------------------------------------------------------- +// CubeReflector +//------------------------------------------------------------------------- + +CubeReflector::CubeReflector() + : mLastTexSize( 0 ) +{ +} + +void CubeReflector::registerReflector( SceneObject *object, + ReflectorDesc *desc ) +{ + if ( mEnabled ) + return; + + mEnabled = true; + mObject = object; + mDesc = desc; + REFLECTMGR->registerReflector( this ); +} + +void CubeReflector::unregisterReflector() +{ + if ( !mEnabled ) + return; + + REFLECTMGR->unregisterReflector( this ); + + mEnabled = false; +} + +void CubeReflector::updateReflection( const ReflectParams ¶ms ) +{ + GFXDEBUGEVENT_SCOPE( CubeReflector_UpdateReflection, ColorI::WHITE ); + + mIsRendering = true; + + // Setup textures and targets... + S32 texDim = mDesc->texSize; + texDim = getMax( texDim, 32 ); + + // Protect against the reflection texture being bigger + // than the current game back buffer. + texDim = getMin( texDim, params.viewportExtent.x ); + texDim = getMin( texDim, params.viewportExtent.y ); + + bool texResize = ( texDim != mLastTexSize ); + + const GFXFormat reflectFormat = REFLECTMGR->getReflectFormat(); + + if ( texResize || + cubemap.isNull() || + cubemap->getFormat() != reflectFormat ) + { + cubemap = GFX->createCubemap(); + cubemap->initDynamic( texDim, reflectFormat ); + } + + GFXTexHandle depthBuff = LightShadowMap::_getDepthTarget( texDim, texDim ); + + if ( renderTarget.isNull() ) + renderTarget = GFX->allocRenderToTextureTarget(); + + GFX->pushActiveRenderTarget(); + renderTarget->attachTexture( GFXTextureTarget::DepthStencil, depthBuff ); + + + F32 oldVisibleDist = gClientSceneGraph->getVisibleDistance(); + gClientSceneGraph->setVisibleDistance( mDesc->farDist ); + + + for ( U32 i = 0; i < 6; i++ ) + updateFace( params, i ); + + + GFX->popActiveRenderTarget(); + + gClientSceneGraph->setVisibleDistance(oldVisibleDist); + + mIsRendering = false; + mLastTexSize = texDim; +} + +void CubeReflector::updateFace( const ReflectParams ¶ms, U32 faceidx ) +{ + GFXDEBUGEVENT_SCOPE( CubeReflector_UpdateFace, ColorI::WHITE ); + + // store current matrices + GFXTransformSaver saver; + + // set projection to 90 degrees vertical and horizontal + F32 left, right, top, bottom; + MathUtils::makeFrustum( &left, &right, &top, &bottom, M_HALFPI_F, 1.0f, mDesc->nearDist ); + GFX->setFrustum( left, right, bottom, top, mDesc->nearDist, mDesc->farDist ); + + // We don't use a special clipping projection, but still need to initialize + // this for objects like SkyBox which will use it during a reflect pass. + gClientSceneGraph->setNonClipProjection( GFX->getProjectionMatrix() ); + + // Standard view that will be overridden below. + VectorF vLookatPt(0.0f, 0.0f, 0.0f), vUpVec(0.0f, 0.0f, 0.0f), vRight(0.0f, 0.0f, 0.0f); + + switch( faceidx ) + { + case 0 : // D3DCUBEMAP_FACE_POSITIVE_X: + vLookatPt = VectorF( 1.0f, 0.0f, 0.0f ); + vUpVec = VectorF( 0.0f, 1.0f, 0.0f ); + break; + case 1 : // D3DCUBEMAP_FACE_NEGATIVE_X: + vLookatPt = VectorF( -1.0f, 0.0f, 0.0f ); + vUpVec = VectorF( 0.0f, 1.0f, 0.0f ); + break; + case 2 : // D3DCUBEMAP_FACE_POSITIVE_Y: + vLookatPt = VectorF( 0.0f, 1.0f, 0.0f ); + vUpVec = VectorF( 0.0f, 0.0f,-1.0f ); + break; + case 3 : // D3DCUBEMAP_FACE_NEGATIVE_Y: + vLookatPt = VectorF( 0.0f, -1.0f, 0.0f ); + vUpVec = VectorF( 0.0f, 0.0f, 1.0f ); + break; + case 4 : // D3DCUBEMAP_FACE_POSITIVE_Z: + vLookatPt = VectorF( 0.0f, 0.0f, 1.0f ); + vUpVec = VectorF( 0.0f, 1.0f, 0.0f ); + break; + case 5: // D3DCUBEMAP_FACE_NEGATIVE_Z: + vLookatPt = VectorF( 0.0f, 0.0f, -1.0f ); + vUpVec = VectorF( 0.0f, 1.0f, 0.0f ); + break; + } + + // create camera matrix + VectorF cross = mCross( vUpVec, vLookatPt ); + cross.normalizeSafe(); + + MatrixF matView(true); + matView.setColumn( 0, cross ); + matView.setColumn( 1, vLookatPt ); + matView.setColumn( 2, vUpVec ); + matView.setPosition( mObject->getPosition() ); + matView.inverse(); + + GFX->setWorldMatrix(matView); + + renderTarget->attachTexture( GFXTextureTarget::Color0, cubemap, faceidx ); + GFX->setActiveRenderTarget( renderTarget ); + GFX->clear( GFXClearStencil | GFXClearTarget | GFXClearZBuffer, gCanvasClearColor, 1.0f, 0 ); + + SceneRenderState reflectRenderState + ( + gClientSceneGraph, + SPT_Reflect, + SceneCameraState::fromGFX() + ); + + reflectRenderState.getMaterialDelegate().bind( REFLECTMGR, &ReflectionManager::getReflectionMaterial ); + reflectRenderState.setDiffuseCameraTransform( params.query->cameraMatrix ); + reflectRenderState.disableAdvancedLightingBins(true); + + // render scene + LIGHTMGR->registerGlobalLights( &reflectRenderState.getFrustum(), false ); + gClientSceneGraph->renderSceneNoLights( &reflectRenderState, mDesc->objectTypeMask ); + LIGHTMGR->unregisterAllLights(); + + // Clean up. + renderTarget->resolve(); +} + +F32 CubeReflector::calcFaceScore( const ReflectParams ¶ms, U32 faceidx ) +{ + if ( Parent::calcScore( params ) <= 0.0f ) + return score; + + VectorF vLookatPt(0.0f, 0.0f, 0.0f); + + switch( faceidx ) + { + case 0 : // D3DCUBEMAP_FACE_POSITIVE_X: + vLookatPt = VectorF( 1.0f, 0.0f, 0.0f ); + break; + case 1 : // D3DCUBEMAP_FACE_NEGATIVE_X: + vLookatPt = VectorF( -1.0f, 0.0f, 0.0f ); + break; + case 2 : // D3DCUBEMAP_FACE_POSITIVE_Y: + vLookatPt = VectorF( 0.0f, 1.0f, 0.0f ); + break; + case 3 : // D3DCUBEMAP_FACE_NEGATIVE_Y: + vLookatPt = VectorF( 0.0f, -1.0f, 0.0f ); + break; + case 4 : // D3DCUBEMAP_FACE_POSITIVE_Z: + vLookatPt = VectorF( 0.0f, 0.0f, 1.0f ); + break; + case 5: // D3DCUBEMAP_FACE_NEGATIVE_Z: + vLookatPt = VectorF( 0.0f, 0.0f, -1.0f ); + break; + } + + VectorF cameraDir; + params.query->cameraMatrix.getColumn( 1, &cameraDir ); + + F32 dot = mDot( cameraDir, -vLookatPt ); + + dot = getMax( ( dot + 1.0f ) / 2.0f, 0.1f ); + + score *= dot; + + return score; +} + +F32 CubeReflector::CubeFaceReflector::calcScore( const ReflectParams ¶ms ) +{ + score = cube->calcFaceScore( params, faceIdx ); + mOccluded = cube->isOccluded(); + return score; +} + + +//------------------------------------------------------------------------- +// PlaneReflector +//------------------------------------------------------------------------- + +void PlaneReflector::registerReflector( SceneObject *object, + ReflectorDesc *desc ) +{ + mEnabled = true; + mObject = object; + mDesc = desc; + mLastDir = Point3F::One; + mLastPos = Point3F::Max; + + REFLECTMGR->registerReflector( this ); +} + +F32 PlaneReflector::calcScore( const ReflectParams ¶ms ) +{ + if ( Parent::calcScore( params ) <= 0.0f || score >= 1000.0f ) + return score; + + // The planar reflection is view dependent to score it + // higher if the view direction and/or position has changed. + + // Get the current camera info. + VectorF camDir = params.query->cameraMatrix.getForwardVector(); + Point3F camPos = params.query->cameraMatrix.getPosition(); + + // Scale up the score based on the view direction change. + F32 dot = mDot( camDir, mLastDir ); + dot = ( 1.0f - dot ) * 1000.0f; + score += dot * mDesc->priority; + + // Also account for the camera movement. + score += ( camPos - mLastPos ).lenSquared() * mDesc->priority; + + return score; +} + +void PlaneReflector::updateReflection( const ReflectParams ¶ms ) +{ + PROFILE_SCOPE(PlaneReflector_updateReflection); + GFXDEBUGEVENT_SCOPE( PlaneReflector_updateReflection, ColorI::WHITE ); + + mIsRendering = true; + + S32 texDim = mDesc->texSize; + texDim = getMax( texDim, 32 ); + + // Protect against the reflection texture being bigger + // than the current game back buffer. + texDim = getMin( texDim, params.viewportExtent.x ); + texDim = getMin( texDim, params.viewportExtent.y ); + + bool texResize = ( texDim != mLastTexSize ); + mLastTexSize = texDim; + + const Point2I texSize( texDim, texDim ); + + if ( texResize || + reflectTex.isNull() || + reflectTex->getFormat() != REFLECTMGR->getReflectFormat() ) + reflectTex = REFLECTMGR->allocRenderTarget( texSize ); + + GFXTexHandle depthBuff = LightShadowMap::_getDepthTarget( texSize.x, texSize.y ); + + // store current matrices + GFXTransformSaver saver; + + F32 aspectRatio = F32( params.viewportExtent.x ) / F32( params.viewportExtent.y ); + + Frustum frustum; + frustum.set(false, params.query->fov, aspectRatio, params.query->nearPlane, params.query->farPlane); + + // Manipulate the frustum for tiled screenshots + const bool screenShotMode = gScreenShot && gScreenShot->isPending(); + if ( screenShotMode ) + gScreenShot->tileFrustum( frustum ); + + GFX->setFrustum( frustum ); + + // Store the last view info for scoring. + mLastDir = params.query->cameraMatrix.getForwardVector(); + mLastPos = params.query->cameraMatrix.getPosition(); + + if ( objectSpace ) + { + // set up camera transform relative to object + MatrixF invObjTrans = mObject->getRenderTransform(); + invObjTrans.inverse(); + MatrixF relCamTrans = invObjTrans * params.query->cameraMatrix; + + MatrixF camReflectTrans = getCameraReflection( relCamTrans ); + MatrixF camTrans = mObject->getRenderTransform() * camReflectTrans; + camTrans.inverse(); + + GFX->setWorldMatrix( camTrans ); + + // use relative reflect transform for modelview since clip plane is in object space + camTrans = camReflectTrans; + camTrans.inverse(); + + // set new projection matrix + gClientSceneGraph->setNonClipProjection( (MatrixF&) GFX->getProjectionMatrix() ); + MatrixF clipProj = getFrustumClipProj( camTrans ); + GFX->setProjectionMatrix( clipProj ); + } + else + { + MatrixF camTrans = params.query->cameraMatrix; + + // set world mat from new camera view + MatrixF camReflectTrans = getCameraReflection( camTrans ); + camReflectTrans.inverse(); + GFX->setWorldMatrix( camReflectTrans ); + + // set new projection matrix + gClientSceneGraph->setNonClipProjection( (MatrixF&) GFX->getProjectionMatrix() ); + MatrixF clipProj = getFrustumClipProj( camReflectTrans ); + GFX->setProjectionMatrix( clipProj ); + } + + // Adjust the detail amount + F32 detailAdjustBackup = TSShapeInstance::smDetailAdjust; + TSShapeInstance::smDetailAdjust *= mDesc->detailAdjust; + + + if(reflectTarget.isNull()) + reflectTarget = GFX->allocRenderToTextureTarget(); + reflectTarget->attachTexture( GFXTextureTarget::Color0, reflectTex ); + reflectTarget->attachTexture( GFXTextureTarget::DepthStencil, depthBuff ); + GFX->pushActiveRenderTarget(); + GFX->setActiveRenderTarget( reflectTarget ); + + SceneRenderState reflectRenderState + ( + gClientSceneGraph, + SPT_Reflect, + SceneCameraState::fromGFX() + ); + + reflectRenderState.getMaterialDelegate().bind( REFLECTMGR, &ReflectionManager::getReflectionMaterial ); + reflectRenderState.setDiffuseCameraTransform( params.query->cameraMatrix ); + reflectRenderState.disableAdvancedLightingBins(true); + + U32 objTypeFlag = -1; + LIGHTMGR->registerGlobalLights( &reflectRenderState.getFrustum(), false ); + + // Since we can sometime be rendering a reflection for 1 or 2 frames before + // it gets updated do to the lag associated with getting the results from + // a HOQ we can sometimes see into parts of the reflection texture that + // have nothing but clear color ( eg. under the water ). + // To make this look less crappy use the ambient color of the sun. + // + // In the future we may want to fix this instead by having the scatterSky + // render a skirt or something in its lower half. + // + ColorF clearColor = reflectRenderState.getAmbientLightColor(); + GFX->clear( GFXClearZBuffer | GFXClearStencil | GFXClearTarget, clearColor, 1.0f, 0 ); + + gClientSceneGraph->renderSceneNoLights( &reflectRenderState, objTypeFlag ); + + LIGHTMGR->unregisterAllLights(); + + // Clean up. + reflectTarget->resolve(); + GFX->popActiveRenderTarget(); + + // Restore detail adjust amount. + TSShapeInstance::smDetailAdjust = detailAdjustBackup; + + mIsRendering = false; +} + +MatrixF PlaneReflector::getCameraReflection( MatrixF &camTrans ) +{ + Point3F normal = refplane; + + // Figure out new cam position + Point3F camPos = camTrans.getPosition(); + F32 dist = refplane.distToPlane( camPos ); + Point3F newCamPos = camPos - normal * dist * 2.0; + + // Figure out new look direction + Point3F i, j, k; + camTrans.getColumn( 0, &i ); + camTrans.getColumn( 1, &j ); + camTrans.getColumn( 2, &k ); + + i = MathUtils::reflect( i, normal ); + j = MathUtils::reflect( j, normal ); + k = MathUtils::reflect( k, normal ); + //mCross( i, j, &k ); + + + MatrixF newTrans(true); + newTrans.setColumn( 0, i ); + newTrans.setColumn( 1, j ); + newTrans.setColumn( 2, k ); + + newTrans.setPosition( newCamPos ); + + return newTrans; +} + +inline float sgn(float a) +{ + if (a > 0.0F) return (1.0F); + if (a < 0.0F) return (-1.0F); + return (0.0F); +} + +MatrixF PlaneReflector::getFrustumClipProj( MatrixF &modelview ) +{ + static MatrixF rotMat(EulerF( static_cast(M_PI / 2.f), 0.0, 0.0)); + static MatrixF invRotMat(EulerF( -static_cast(M_PI / 2.f), 0.0, 0.0)); + + + MatrixF revModelview = modelview; + revModelview = rotMat * revModelview; // add rotation to modelview because it needs to be removed from projection + + // rotate clip plane into modelview space + Point4F clipPlane; + Point3F pnt = refplane * -(refplane.d + 0.0 ); + Point3F norm = refplane; + + revModelview.mulP( pnt ); + revModelview.mulV( norm ); + norm.normalize(); + + clipPlane.set( norm.x, norm.y, norm.z, -mDot( pnt, norm ) ); + + + // Manipulate projection matrix + //------------------------------------------------------------------------ + MatrixF proj = GFX->getProjectionMatrix(); + proj.mul( invRotMat ); // reverse rotation imposed by Torque + proj.transpose(); // switch to row-major order + + // Calculate the clip-space corner point opposite the clipping plane + // as (sgn(clipPlane.x), sgn(clipPlane.y), 1, 1) and + // transform it into camera space by multiplying it + // by the inverse of the projection matrix + Vector4F q; + q.x = sgn(clipPlane.x) / proj(0,0); + q.y = sgn(clipPlane.y) / proj(1,1); + q.z = -1.0F; + q.w = ( 1.0F - proj(2,2) ) / proj(3,2); + + F32 a = 1.0 / (clipPlane.x * q.x + clipPlane.y * q.y + clipPlane.z * q.z + clipPlane.w * q.w); + + Vector4F c = clipPlane * a; + + // CodeReview [ags 1/23/08] Come up with a better way to deal with this. + if(GFX->getAdapterType() == OpenGL) + c.z += 1.0f; + + // Replace the third column of the projection matrix + proj.setColumn( 2, c ); + proj.transpose(); // convert back to column major order + proj.mul( rotMat ); // restore Torque rotation + + return proj; +} \ No newline at end of file diff --git a/Engine/source/scene/reflector.h b/Engine/source/scene/reflector.h new file mode 100644 index 000000000..25c6399a8 --- /dev/null +++ b/Engine/source/scene/reflector.h @@ -0,0 +1,229 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _REFLECTOR_H_ +#define _REFLECTOR_H_ + +#ifndef _GFXCUBEMAP_H_ +#include "gfx/gfxCubemap.h" +#endif +#ifndef _GFXTARGET_H_ +#include "gfx/gfxTarget.h" +#endif +#ifndef _SIMDATABLOCK_H_ +#include "console/simDatablock.h" +#endif +#ifndef _MMATH_H_ +#include "math/mMath.h" +#endif +#ifndef _MATHUTIL_FRUSTUM_H_ +#include "math/util/frustum.h" +#endif + +struct CameraQuery; +class Point2I; +class Frustum; +class SceneManager; +class SceneObject; +class GFXOcclusionQuery; + + +struct ReflectParams +{ + const CameraQuery *query; + Point2I viewportExtent; + Frustum culler; + U32 startOfUpdateMs; +}; + + +class ReflectorDesc : public SimDataBlock +{ + typedef SimDataBlock Parent; + +public: + + ReflectorDesc(); + virtual ~ReflectorDesc(); + + DECLARE_CONOBJECT( ReflectorDesc ); + + static void initPersistFields(); + + virtual void packData( BitStream *stream ); + virtual void unpackData( BitStream* stream ); + virtual bool preload( bool server, String &errorStr ); + + U32 texSize; + F32 nearDist; + F32 farDist; + U32 objectTypeMask; + F32 detailAdjust; + F32 priority; + U32 maxRateMs; + bool useOcclusionQuery; + //U32 lastLodSize; +}; + + +class ReflectorBase +{ +public: + + ReflectorBase(); + virtual ~ReflectorBase(); + + bool isEnabled() const { return mEnabled; } + + virtual void unregisterReflector(); + virtual F32 calcScore( const ReflectParams ¶ms ); + virtual void updateReflection( const ReflectParams ¶ms ) {} + + GFXOcclusionQuery* getOcclusionQuery() const { return mOcclusionQuery; } + + bool isOccluded() const { return mOccluded; } + + /// Returns true if this reflector is in the process of rendering. + bool isRendering() const { return mIsRendering; } + + /// Signifies that the query has not finished yet and a new query + /// does not need to be submitted. + bool mQueryPending; + +protected: + + bool mEnabled; + + bool mIsRendering; + + GFXOcclusionQuery *mOcclusionQuery; + + bool mOccluded; + + SceneObject *mObject; + + ReflectorDesc *mDesc; + +public: + + // These are public because some of them + // are exposed as fields. + + F32 score; + U32 lastUpdateMs; + + +}; + +typedef Vector ReflectorList; + + +class CubeReflector : public ReflectorBase +{ + typedef ReflectorBase Parent; + +public: + + CubeReflector(); + virtual ~CubeReflector() {} + + void registerReflector( SceneObject *inObject, + ReflectorDesc *inDesc ); + + virtual void unregisterReflector(); + virtual void updateReflection( const ReflectParams ¶ms ); + + GFXCubemap* getCubemap() const { return cubemap; } + + void updateFace( const ReflectParams ¶ms, U32 faceidx ); + F32 calcFaceScore( const ReflectParams ¶ms, U32 faceidx ); + +protected: + + GFXTexHandle depthBuff; + GFXTextureTargetRef renderTarget; + GFXCubemapHandle cubemap; + U32 mLastTexSize; + + class CubeFaceReflector : public ReflectorBase + { + typedef ReflectorBase Parent; + friend class CubeReflector; + + public: + U32 faceIdx; + CubeReflector *cube; + + virtual void updateReflection( const ReflectParams ¶ms ) { cube->updateFace( params, faceIdx ); } + virtual F32 calcScore( const ReflectParams ¶ms ); + }; + + CubeFaceReflector mFaces[6]; +}; + + +class PlaneReflector : public ReflectorBase +{ + typedef ReflectorBase Parent; + +public: + + PlaneReflector() + { + refplane.set( Point3F(0,0,0), Point3F(0,0,1) ); + objectSpace = false; + mLastTexSize = 0; + } + + virtual ~PlaneReflector() {} + + void registerReflector( SceneObject *inObject, + ReflectorDesc *inDesc ); + + virtual F32 calcScore( const ReflectParams ¶ms ); + virtual void updateReflection( const ReflectParams ¶ms ); + + /// Set up camera matrix for a reflection on the plane + MatrixF getCameraReflection( MatrixF &camTrans ); + + /// Oblique frustum clipping - use near plane of zbuffer as a clip plane + MatrixF getFrustumClipProj( MatrixF &modelview ); + +protected: + + U32 mLastTexSize; + + // The camera position at the last update. + Point3F mLastPos; + + // The camera direction at the last update. + VectorF mLastDir; + +public: + + GFXTextureTargetRef reflectTarget; + GFXTexHandle reflectTex; + PlaneF refplane; + bool objectSpace; +}; + +#endif // _REFLECTOR_H_ \ No newline at end of file diff --git a/Engine/source/scene/sceneCameraState.cpp b/Engine/source/scene/sceneCameraState.cpp new file mode 100644 index 000000000..82b1c9daa --- /dev/null +++ b/Engine/source/scene/sceneCameraState.cpp @@ -0,0 +1,65 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "scene/sceneCameraState.h" + +#include "gfx/gfxDevice.h" + + +//----------------------------------------------------------------------------- + +SceneCameraState::SceneCameraState( const RectI& viewport, const Frustum& frustum, const MatrixF& worldView, const MatrixF& projection ) + : mViewport( viewport ), + mFrustum( frustum ), + mWorldViewMatrix( worldView ), + mProjectionMatrix( projection ) +{ + mViewDirection = frustum.getTransform().getForwardVector(); +} + +//----------------------------------------------------------------------------- + +SceneCameraState SceneCameraState::fromGFX() +{ + return fromGFXWithViewport( GFX->getViewport() ); +} + +//----------------------------------------------------------------------------- + +SceneCameraState SceneCameraState::fromGFXWithViewport( const RectI& viewport ) +{ + const MatrixF& world = GFX->getWorldMatrix(); + + MatrixF camera = world; + camera.inverse(); + + Frustum frustum = GFX->getFrustum(); + frustum.setTransform( camera ); + + return SceneCameraState( + viewport, + frustum, + world, + GFX->getProjectionMatrix() + ); +} diff --git a/Engine/source/scene/sceneCameraState.h b/Engine/source/scene/sceneCameraState.h new file mode 100644 index 000000000..9eec5d488 --- /dev/null +++ b/Engine/source/scene/sceneCameraState.h @@ -0,0 +1,101 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SCENECAMERASTATE_H_ +#define _SCENECAMERASTATE_H_ + +#ifndef _MATHUTIL_FRUSTUM_H_ +#include "math/util/frustum.h" +#endif + +#ifndef _MRECT_H_ +#include "math/mRect.h" +#endif + +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif + + +/// An object that combines all the state that is relevant to looking into the +/// scene from a particular point of view. +class SceneCameraState +{ + protected: + + /// The screen-space viewport rectangle. + RectI mViewport; + + /// The viewing frustum. + Frustum mFrustum; + + /// The inverse of the frustum's transform stored here for caching. + MatrixF mWorldViewMatrix; + + /// The projection matrix. + MatrixF mProjectionMatrix; + + /// World-space vector representing the view direction. + Point3F mViewDirection; + + /// Internal constructor. + SceneCameraState() {} + + public: + + /// Freeze the given viewing state. + /// + /// @param viewport Screen-space viewport rectangle. + /// @param frustum Camera frustum. + /// @param worldView World->view matrix. + /// @param projection Projection matrix. + SceneCameraState( const RectI& viewport, const Frustum& frustum, const MatrixF& worldView, const MatrixF& projection ); + + /// Capture the view state from the current GFX state. + static SceneCameraState fromGFX(); + + /// + static SceneCameraState fromGFXWithViewport( const RectI& viewport ); + + /// Return the screen-space viewport rectangle. + const RectI& getViewport() const { return mViewport; } + + /// Return the camera frustum. + const Frustum& getFrustum() const { return mFrustum; } + + /// Return the view position. This is a shortcut for getFrustum().getPosition(). + const Point3F& getViewPosition() const { return mFrustum.getPosition(); } + + /// Return the world-space view vector. + const Point3F& getViewDirection() const { return mViewDirection; } + + /// Return the view->world transform. This is a shortcut for getFrustum().getTransform(). + const MatrixF& getViewWorldMatrix() const { return mFrustum.getTransform(); } + + /// Return the world->view transform. + const MatrixF& getWorldViewMatrix() const { return mWorldViewMatrix; } + + /// Return the projection transform. + const MatrixF& getProjectionMatrix() const { return mProjectionMatrix; } +}; + +#endif // !_SCENECAMERASTATE_H_ diff --git a/Engine/source/scene/sceneContainer.cpp b/Engine/source/scene/sceneContainer.cpp new file mode 100644 index 000000000..3643fa5db --- /dev/null +++ b/Engine/source/scene/sceneContainer.cpp @@ -0,0 +1,1649 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "scene/sceneContainer.h" + +#include "collision/extrudedPolyList.h" +#include "collision/earlyOutPolyList.h" +#include "scene/sceneObject.h" +#include "platform/profiler.h" +#include "console/engineAPI.h" +#include "math/util/frustum.h" + + +// [rene, 02-Mar-11] +// - *Loads* of copy&paste sin in this file (among its many other sins); all the findObjectXXX methods +// are trivial permutations of the same snippet of copy&pasted code +// - FindCallback should return a bool so it's possible to use the findObjectXXX methods to look +// for the first object matching a certain criteria + + +SceneContainer gServerContainer; +SceneContainer gClientContainer; + +const U32 SceneContainer::csmNumBins = 16; +const F32 SceneContainer::csmBinSize = 64; +const F32 SceneContainer::csmTotalBinSize = SceneContainer::csmBinSize * SceneContainer::csmNumBins; +const U32 SceneContainer::csmRefPoolBlockSize = 4096; + +// Statics used by buildPolyList methods +static AbstractPolyList* sPolyList; +static SphereF sBoundingSphere; +static Box3F sBoundingBox; + + +//============================================================================= +// SceneContainer::Link. +//============================================================================= + +//----------------------------------------------------------------------------- + +SceneContainer::Link::Link() +{ + next = prev = this; +} + +//----------------------------------------------------------------------------- + +void SceneContainer::Link::unlink() +{ + next->prev = prev; + prev->next = next; + next = prev = this; +} + +//----------------------------------------------------------------------------- + +void SceneContainer::Link::linkAfter(SceneContainer::Link* ptr) +{ + next = ptr->next; + next->prev = this; + prev = ptr; + prev->next = this; +} + +//============================================================================= +// SceneContainer. +//============================================================================= + +//----------------------------------------------------------------------------- + +SceneContainer::SceneContainer() +{ + mSearchInProgress = false; + mCurrSeqKey = 0; + + mEnd.next = mEnd.prev = &mStart; + mStart.next = mStart.prev = &mEnd; + + mBinArray = new SceneObjectRef[csmNumBins * csmNumBins]; + for (U32 i = 0; i < csmNumBins; i++) + { + U32 base = i * csmNumBins; + for (U32 j = 0; j < csmNumBins; j++) + { + mBinArray[base + j].object = NULL; + mBinArray[base + j].nextInBin = NULL; + mBinArray[base + j].prevInBin = NULL; + mBinArray[base + j].nextInObj = NULL; + } + } + mOverflowBin.object = NULL; + mOverflowBin.nextInBin = NULL; + mOverflowBin.prevInBin = NULL; + mOverflowBin.nextInObj = NULL; + + VECTOR_SET_ASSOCIATION( mRefPoolBlocks ); + VECTOR_SET_ASSOCIATION( mSearchList ); + VECTOR_SET_ASSOCIATION( mWaterAndZones ); + VECTOR_SET_ASSOCIATION( mTerrains ); + + mFreeRefPool = NULL; + addRefPoolBlock(); + + cleanupSearchVectors(); +} + +//----------------------------------------------------------------------------- + +SceneContainer::~SceneContainer() +{ + delete[] mBinArray; + + for (U32 i = 0; i < mRefPoolBlocks.size(); i++) + { + SceneObjectRef* pool = mRefPoolBlocks[i]; + for (U32 j = 0; j < csmRefPoolBlockSize; j++) + { + // Depressingly, this can give weird results if its pointing at bad memory... + if(pool[j].object != NULL) + Con::warnf("Error, a %s (%x) isn't properly out of the bins!", pool[j].object->getClassName(), pool[j].object); + + // If you're getting this it means that an object created didn't + // remove itself from its container before we destroyed the + // container. Typically you get this behavior from particle + // emitters, as they try to hang around until all their particles + // die. In general it's benign, though if you get it for things + // that aren't particle emitters it can be a bad sign! + } + + delete [] pool; + } + mFreeRefPool = NULL; + + cleanupSearchVectors(); +} + +//----------------------------------------------------------------------------- + +bool SceneContainer::addObject(SceneObject* obj) +{ + AssertFatal(obj->mContainer == NULL, "Adding already added object."); + obj->mContainer = this; + obj->linkAfter(&mStart); + + insertIntoBins(obj); + + // Also insert water and physical zone types into the special vector. + if ( obj->getTypeMask() & ( WaterObjectType | PhysicalZoneObjectType ) ) + mWaterAndZones.push_back(obj); + if( obj->getTypeMask() & TerrainObjectType ) + mTerrains.push_back( obj ); + + return true; +} + +//----------------------------------------------------------------------------- + +bool SceneContainer::removeObject(SceneObject* obj) +{ + AssertFatal(obj->mContainer == this, "Trying to remove from wrong container."); + removeFromBins(obj); + + // Remove water and physical zone types from the special vector. + if ( obj->getTypeMask() & ( WaterObjectType | PhysicalZoneObjectType ) ) + { + Vector::iterator iter = find( mWaterAndZones.begin(), mWaterAndZones.end(), obj ); + if( iter != mTerrains.end() ) + mWaterAndZones.erase_fast(iter); + } + + // Remove terrain objects from special vector. + if( obj->getTypeMask() & TerrainObjectType ) + { + Vector< SceneObject* >::iterator iter = find( mTerrains.begin(), mTerrains.end(), obj ); + if( iter != mTerrains.end() ) + mTerrains.erase_fast(iter); + } + + obj->mContainer = 0; + obj->unlink(); + return true; +} + +//----------------------------------------------------------------------------- + +void SceneContainer::addRefPoolBlock() +{ + mRefPoolBlocks.push_back(new SceneObjectRef[csmRefPoolBlockSize]); + for (U32 i = 0; i < csmRefPoolBlockSize-1; i++) + { + mRefPoolBlocks.last()[i].object = NULL; + mRefPoolBlocks.last()[i].prevInBin = NULL; + mRefPoolBlocks.last()[i].nextInBin = NULL; + mRefPoolBlocks.last()[i].nextInObj = &(mRefPoolBlocks.last()[i+1]); + } + mRefPoolBlocks.last()[csmRefPoolBlockSize-1].object = NULL; + mRefPoolBlocks.last()[csmRefPoolBlockSize-1].prevInBin = NULL; + mRefPoolBlocks.last()[csmRefPoolBlockSize-1].nextInBin = NULL; + mRefPoolBlocks.last()[csmRefPoolBlockSize-1].nextInObj = mFreeRefPool; + + mFreeRefPool = &(mRefPoolBlocks.last()[0]); +} + +//----------------------------------------------------------------------------- + +void SceneContainer::insertIntoBins(SceneObject* obj) +{ + AssertFatal(obj != NULL, "No object?"); + AssertFatal(obj->mBinRefHead == NULL, "Error, already have a bin chain!"); + + // The first thing we do is find which bins are covered in x and y... + const Box3F* pWBox = &obj->getWorldBox(); + + U32 minX, maxX, minY, maxY; + getBinRange(pWBox->minExtents.x, pWBox->maxExtents.x, minX, maxX); + getBinRange(pWBox->minExtents.y, pWBox->maxExtents.y, minY, maxY); + + // Store the current regions for later queries + obj->mBinMinX = minX; + obj->mBinMaxX = maxX; + obj->mBinMinY = minY; + obj->mBinMaxY = maxY; + + // For huge objects, dump them into the overflow bin. Otherwise, everything + // goes into the grid... + if ((maxX - minX + 1) < csmNumBins || (maxY - minY + 1) < csmNumBins && !obj->isGlobalBounds()) + { + SceneObjectRef** pCurrInsert = &obj->mBinRefHead; + + for (U32 i = minY; i <= maxY; i++) + { + U32 insertY = i % csmNumBins; + U32 base = insertY * csmNumBins; + for (U32 j = minX; j <= maxX; j++) + { + U32 insertX = j % csmNumBins; + + SceneObjectRef* ref = allocateObjectRef(); + + ref->object = obj; + ref->nextInBin = mBinArray[base + insertX].nextInBin; + ref->prevInBin = &mBinArray[base + insertX]; + ref->nextInObj = NULL; + + if (mBinArray[base + insertX].nextInBin) + mBinArray[base + insertX].nextInBin->prevInBin = ref; + mBinArray[base + insertX].nextInBin = ref; + + *pCurrInsert = ref; + pCurrInsert = &ref->nextInObj; + } + } + } + else + { + SceneObjectRef* ref = allocateObjectRef(); + + ref->object = obj; + ref->nextInBin = mOverflowBin.nextInBin; + ref->prevInBin = &mOverflowBin; + ref->nextInObj = NULL; + + if (mOverflowBin.nextInBin) + mOverflowBin.nextInBin->prevInBin = ref; + mOverflowBin.nextInBin = ref; + + obj->mBinRefHead = ref; + } +} + +//----------------------------------------------------------------------------- + +void SceneContainer::insertIntoBins(SceneObject* obj, + U32 minX, U32 maxX, + U32 minY, U32 maxY) +{ + PROFILE_START(InsertBins); + AssertFatal(obj != NULL, "No object?"); + + AssertFatal(obj->mBinRefHead == NULL, "Error, already have a bin chain!"); + // Store the current regions for later queries + obj->mBinMinX = minX; + obj->mBinMaxX = maxX; + obj->mBinMinY = minY; + obj->mBinMaxY = maxY; + + // For huge objects, dump them into the overflow bin. Otherwise, everything + // goes into the grid... + // + if ((maxX - minX + 1) < csmNumBins || (maxY - minY + 1) < csmNumBins && !obj->isGlobalBounds()) + { + SceneObjectRef** pCurrInsert = &obj->mBinRefHead; + + for (U32 i = minY; i <= maxY; i++) + { + U32 insertY = i % csmNumBins; + U32 base = insertY * csmNumBins; + for (U32 j = minX; j <= maxX; j++) + { + U32 insertX = j % csmNumBins; + + SceneObjectRef* ref = allocateObjectRef(); + + ref->object = obj; + ref->nextInBin = mBinArray[base + insertX].nextInBin; + ref->prevInBin = &mBinArray[base + insertX]; + ref->nextInObj = NULL; + + if (mBinArray[base + insertX].nextInBin) + mBinArray[base + insertX].nextInBin->prevInBin = ref; + mBinArray[base + insertX].nextInBin = ref; + + *pCurrInsert = ref; + pCurrInsert = &ref->nextInObj; + } + } + } + else + { + SceneObjectRef* ref = allocateObjectRef(); + + ref->object = obj; + ref->nextInBin = mOverflowBin.nextInBin; + ref->prevInBin = &mOverflowBin; + ref->nextInObj = NULL; + + if (mOverflowBin.nextInBin) + mOverflowBin.nextInBin->prevInBin = ref; + mOverflowBin.nextInBin = ref; + obj->mBinRefHead = ref; + } + PROFILE_END(); +} + +//----------------------------------------------------------------------------- + +void SceneContainer::removeFromBins(SceneObject* obj) +{ + PROFILE_START(RemoveFromBins); + AssertFatal(obj != NULL, "No object?"); + + SceneObjectRef* chain = obj->mBinRefHead; + obj->mBinRefHead = NULL; + + while (chain) + { + SceneObjectRef* trash = chain; + chain = chain->nextInObj; + + AssertFatal(trash->prevInBin != NULL, "Error, must have a previous entry in the bin!"); + if (trash->nextInBin) + trash->nextInBin->prevInBin = trash->prevInBin; + trash->prevInBin->nextInBin = trash->nextInBin; + + freeObjectRef(trash); + } + PROFILE_END(); +} + +//----------------------------------------------------------------------------- + +void SceneContainer::checkBins(SceneObject* obj) +{ + AssertFatal(obj != NULL, "No object?"); + + PROFILE_START(CheckBins); + if (obj->mBinRefHead == NULL) + { + insertIntoBins(obj); + PROFILE_END(); + return; + } + + // Otherwise, the object is already in the bins. Let's see if it has strayed out of + // the bins that it's currently in... + const Box3F* pWBox = &obj->getWorldBox(); + + U32 minX, maxX, minY, maxY; + getBinRange(pWBox->minExtents.x, pWBox->maxExtents.x, minX, maxX); + getBinRange(pWBox->minExtents.y, pWBox->maxExtents.y, minY, maxY); + + if (obj->mBinMinX != minX || obj->mBinMaxX != maxX || + obj->mBinMinY != minY || obj->mBinMaxY != maxY) + { + // We have to rebin the object + removeFromBins(obj); + insertIntoBins(obj, minX, maxX, minY, maxY); + } + PROFILE_END(); +} + +//----------------------------------------------------------------------------- + +void SceneContainer::findObjects(const Box3F& box, U32 mask, FindCallback callback, void *key) +{ + PROFILE_SCOPE(ContainerFindObjects_Box); + + // If we're searching for just water, just physical zones, or + // just water and physical zones then use the optimized path. + if ( mask == WaterObjectType || + mask == PhysicalZoneObjectType || + mask == (WaterObjectType|PhysicalZoneObjectType) ) + { + _findSpecialObjects( mWaterAndZones, box, mask, callback, key ); + return; + } + else if( mask == TerrainObjectType ) + { + _findSpecialObjects( mTerrains, box, mask, callback, key ); + return; + } + + AssertFatal( !mSearchInProgress, "SceneContainer::findObjects - Container queries are not re-entrant" ); + mSearchInProgress = true; + + U32 minX, maxX, minY, maxY; + getBinRange(box.minExtents.x, box.maxExtents.x, minX, maxX); + getBinRange(box.minExtents.y, box.maxExtents.y, minY, maxY); + mCurrSeqKey++; + for (U32 i = minY; i <= maxY; i++) + { + U32 insertY = i % csmNumBins; + U32 base = insertY * csmNumBins; + for (U32 j = minX; j <= maxX; j++) + { + U32 insertX = j % csmNumBins; + + SceneObjectRef* chain = mBinArray[base + insertX].nextInBin; + while (chain) + { + if (chain->object->getContainerSeqKey() != mCurrSeqKey) + { + chain->object->setContainerSeqKey(mCurrSeqKey); + + if ((chain->object->getTypeMask() & mask) != 0 && + chain->object->isCollisionEnabled()) + { + if (chain->object->getWorldBox().isOverlapped(box) || chain->object->isGlobalBounds()) + { + (*callback)(chain->object,key); + } + } + } + chain = chain->nextInBin; + } + } + } + SceneObjectRef* chain = mOverflowBin.nextInBin; + while (chain) + { + if (chain->object->getContainerSeqKey() != mCurrSeqKey) + { + chain->object->setContainerSeqKey(mCurrSeqKey); + + if ((chain->object->getTypeMask() & mask) != 0 && + chain->object->isCollisionEnabled()) + { + if (chain->object->getWorldBox().isOverlapped(box) || chain->object->isGlobalBounds()) + { + (*callback)(chain->object,key); + } + } + } + chain = chain->nextInBin; + } + + mSearchInProgress = false; +} + +//----------------------------------------------------------------------------- + +void SceneContainer::findObjects( const Frustum &frustum, U32 mask, FindCallback callback, void *key ) +{ + PROFILE_SCOPE(ContainerFindObjects_Frustum); + + Box3F searchBox = frustum.getBounds(); + + if ( mask == WaterObjectType || + mask == PhysicalZoneObjectType || + mask == (WaterObjectType|PhysicalZoneObjectType) ) + { + _findSpecialObjects( mWaterAndZones, searchBox, mask, callback, key ); + return; + } + else if( mask == TerrainObjectType ) + { + _findSpecialObjects( mTerrains, searchBox, mask, callback, key ); + return; + } + + AssertFatal( !mSearchInProgress, "SceneContainer::findObjects - Container queries are not re-entrant" ); + mSearchInProgress = true; + + U32 minX, maxX, minY, maxY; + getBinRange(searchBox.minExtents.x, searchBox.maxExtents.x, minX, maxX); + getBinRange(searchBox.minExtents.y, searchBox.maxExtents.y, minY, maxY); + mCurrSeqKey++; + + for (U32 i = minY; i <= maxY; i++) + { + U32 insertY = i % csmNumBins; + U32 base = insertY * csmNumBins; + for (U32 j = minX; j <= maxX; j++) + { + U32 insertX = j % csmNumBins; + + SceneObjectRef* chain = mBinArray[base + insertX].nextInBin; + while (chain) + { + SceneObject *object = chain->object; + + if (object->getContainerSeqKey() != mCurrSeqKey) + { + object->setContainerSeqKey(mCurrSeqKey); + + if ((object->getTypeMask() & mask) != 0 && + object->isCollisionEnabled()) + { + const Box3F &worldBox = object->getWorldBox(); + if ( object->isGlobalBounds() || worldBox.isOverlapped(searchBox) ) + { + if ( !frustum.isCulled( worldBox ) ) + (*callback)(chain->object,key); + } + } + } + chain = chain->nextInBin; + } + } + } + + SceneObjectRef* chain = mOverflowBin.nextInBin; + while (chain) + { + SceneObject *object = chain->object; + + if (object->getContainerSeqKey() != mCurrSeqKey) + { + object->setContainerSeqKey(mCurrSeqKey); + + if ((object->getTypeMask() & mask) != 0 && + object->isCollisionEnabled()) + { + const Box3F &worldBox = object->getWorldBox(); + + if ( object->isGlobalBounds() || worldBox.isOverlapped(searchBox) ) + { + if ( !frustum.isCulled( worldBox ) ) + (*callback)(object,key); + } + } + } + chain = chain->nextInBin; + } + + mSearchInProgress = false; +} + +//----------------------------------------------------------------------------- + +void SceneContainer::polyhedronFindObjects(const Polyhedron& polyhedron, U32 mask, FindCallback callback, void *key) +{ + PROFILE_SCOPE(ContainerFindObjects_polyhedron); + + U32 i; + Box3F box; + box.minExtents.set(1e9, 1e9, 1e9); + box.maxExtents.set(-1e9, -1e9, -1e9); + for (i = 0; i < polyhedron.pointList.size(); i++) + { + box.minExtents.setMin(polyhedron.pointList[i]); + box.maxExtents.setMax(polyhedron.pointList[i]); + } + + if ( mask == WaterObjectType || + mask == PhysicalZoneObjectType || + mask == (WaterObjectType|PhysicalZoneObjectType) ) + { + _findSpecialObjects( mWaterAndZones, box, mask, callback, key ); + return; + } + else if( mask == TerrainObjectType ) + { + _findSpecialObjects( mTerrains, mask, callback, key ); + return; + } + + AssertFatal( !mSearchInProgress, "SceneContainer::polyhedronFindObjects - Container queries are not re-entrant" ); + mSearchInProgress = true; + + U32 minX, maxX, minY, maxY; + getBinRange(box.minExtents.x, box.maxExtents.x, minX, maxX); + getBinRange(box.minExtents.y, box.maxExtents.y, minY, maxY); + mCurrSeqKey++; + for (i = minY; i <= maxY; i++) + { + U32 insertY = i % csmNumBins; + U32 base = insertY * csmNumBins; + for (U32 j = minX; j <= maxX; j++) + { + U32 insertX = j % csmNumBins; + + SceneObjectRef* chain = mBinArray[base + insertX].nextInBin; + while (chain) + { + if (chain->object->getContainerSeqKey() != mCurrSeqKey) + { + chain->object->setContainerSeqKey(mCurrSeqKey); + + if ((chain->object->getTypeMask() & mask) != 0 && + chain->object->isCollisionEnabled()) + { + if (chain->object->getWorldBox().isOverlapped(box) || chain->object->isGlobalBounds()) + { + (*callback)(chain->object,key); + } + } + } + chain = chain->nextInBin; + } + } + } + SceneObjectRef* chain = mOverflowBin.nextInBin; + while (chain) + { + if (chain->object->getContainerSeqKey() != mCurrSeqKey) + { + chain->object->setContainerSeqKey(mCurrSeqKey); + + if ((chain->object->getTypeMask() & mask) != 0 && + chain->object->isCollisionEnabled()) + { + if (chain->object->getWorldBox().isOverlapped(box) || chain->object->isGlobalBounds()) + { + (*callback)(chain->object,key); + } + } + } + chain = chain->nextInBin; + } + + mSearchInProgress = false; +} + +//----------------------------------------------------------------------------- + +void SceneContainer::findObjectList( const Box3F& searchBox, U32 mask, Vector *outFound ) +{ + PROFILE_SCOPE( Container_FindObjectList_Box ); + + AssertFatal( !mSearchInProgress, "SceneContainer::findObjectList - Container queries are not re-entrant" ); + mSearchInProgress = true; + + // TODO: Optimize for water and zones? + + U32 minX, maxX, minY, maxY; + getBinRange(searchBox.minExtents.x, searchBox.maxExtents.x, minX, maxX); + getBinRange(searchBox.minExtents.y, searchBox.maxExtents.y, minY, maxY); + mCurrSeqKey++; + + for (U32 i = minY; i <= maxY; i++) + { + U32 insertY = i % csmNumBins; + U32 base = insertY * csmNumBins; + for (U32 j = minX; j <= maxX; j++) + { + U32 insertX = j % csmNumBins; + + SceneObjectRef* chain = mBinArray[base + insertX].nextInBin; + while (chain) + { + SceneObject *object = chain->object; + + if (object->getContainerSeqKey() != mCurrSeqKey) + { + object->setContainerSeqKey(mCurrSeqKey); + + if ((object->getTypeMask() & mask) != 0 && + object->isCollisionEnabled()) + { + const Box3F &worldBox = object->getWorldBox(); + if ( object->isGlobalBounds() || worldBox.isOverlapped( searchBox ) ) + { + outFound->push_back( object ); + } + } + } + chain = chain->nextInBin; + } + } + } + + SceneObjectRef* chain = mOverflowBin.nextInBin; + while (chain) + { + SceneObject *object = chain->object; + + if (object->getContainerSeqKey() != mCurrSeqKey) + { + object->setContainerSeqKey(mCurrSeqKey); + + if ((object->getTypeMask() & mask) != 0 && + object->isCollisionEnabled()) + { + const Box3F &worldBox = object->getWorldBox(); + + if ( object->isGlobalBounds() || worldBox.isOverlapped( searchBox ) ) + { + outFound->push_back( object ); + } + } + } + chain = chain->nextInBin; + } + + mSearchInProgress = false; +} + +//----------------------------------------------------------------------------- + +void SceneContainer::findObjectList( const Frustum &frustum, U32 mask, Vector *outFound ) +{ + PROFILE_SCOPE( Container_FindObjectList_Frustum ); + + // Do a box find first. + findObjectList( frustum.getBounds(), mask, outFound ); + + // Now do the frustum testing. + for ( U32 i=0; i < outFound->size(); ) + { + const Box3F &worldBox = (*outFound)[i]->getWorldBox(); + if ( frustum.isCulled( worldBox ) ) + outFound->erase_fast( i ); + else + i++; + } +} + +//----------------------------------------------------------------------------- + +void SceneContainer::findObjectList( U32 mask, Vector *outFound ) +{ + for ( Link* itr = mStart.next; itr != &mEnd; itr = itr->next ) + { + SceneObject* ptr = static_cast( itr ); + if ( ( ptr->getTypeMask() & mask ) != 0 ) + outFound->push_back( ptr ); + } +} + +//----------------------------------------------------------------------------- + +void SceneContainer::findObjects( U32 mask, FindCallback callback, void *key ) +{ + for (Link* itr = mStart.next; itr != &mEnd; itr = itr->next) { + SceneObject* ptr = static_cast(itr); + if ((ptr->getTypeMask() & mask) != 0 && !ptr->mCollisionCount) + (*callback)(ptr,key); + } +} + +//----------------------------------------------------------------------------- + +void SceneContainer::_findSpecialObjects( const Vector< SceneObject* >& vector, U32 mask, FindCallback callback, void *key ) +{ + PROFILE_SCOPE( Container_findSpecialObjects ); + + Vector::const_iterator iter = vector.begin(); + for ( ; iter != vector.end(); iter++ ) + { + if ( (*iter)->getTypeMask() & mask ) + callback( *iter, key ); + } +} + +//----------------------------------------------------------------------------- + +void SceneContainer::_findSpecialObjects( const Vector< SceneObject* >& vector, const Box3F &box, U32 mask, FindCallback callback, void *key ) +{ + PROFILE_SCOPE( Container_findSpecialObjects_Box ); + + Vector::const_iterator iter = vector.begin(); + + for ( ; iter != vector.end(); iter++ ) + { + SceneObject *pObj = *iter; + + if ( pObj->getTypeMask() & mask && + ( pObj->isGlobalBounds() || pObj->getWorldBox().isOverlapped(box) ) ) + { + callback( pObj, key ); + } + } +} + +//----------------------------------------------------------------------------- + +bool SceneContainer::castRay( const Point3F& start, const Point3F& end, U32 mask, RayInfo* info, CastRayCallback callback ) +{ + AssertFatal( info->userData == NULL, "SceneContainer::castRay - RayInfo->userData cannot be used here!" ); + + PROFILE_START( SceneContainer_CastRay ); + bool result = _castRay( CollisionGeometry, start, end, mask, info, callback ); + PROFILE_END(); + return result; +} + +//----------------------------------------------------------------------------- + +bool SceneContainer::castRayRendered( const Point3F& start, const Point3F& end, U32 mask, RayInfo* info, CastRayCallback callback ) +{ + AssertFatal( info->userData == NULL, "SceneContainer::castRayRendered - RayInfo->userData cannot be used here!" ); + + PROFILE_START( SceneContainer_CastRayRendered ); + bool result = _castRay( RenderedGeometry, start, end, mask, info, callback ); + PROFILE_END(); + return result; +} + +//----------------------------------------------------------------------------- + +// DMMNOTE: There are still some optimizations to be done here. In particular: +// - After checking the overflow bin, we can potentially shorten the line +// that we rasterize against the grid if there is a collision with say, +// the terrain. +// - The optimal grid size isn't necessarily what we have set here. possibly +// a resolution of 16 meters would give better results +// - The line rasterizer is pretty lame. Unfortunately we can't use a +// simple bres. here, since we need to check every grid element that the line +// passes through, which bres does _not_ do for us. Possibly there's a +// rasterizer for anti-aliased lines that will serve better than what +// we have below. + +bool SceneContainer::_castRay( U32 type, const Point3F& start, const Point3F& end, U32 mask, RayInfo* info, CastRayCallback callback ) +{ + AssertFatal( !mSearchInProgress, "SceneContainer::_castRay - Container queries are not re-entrant" ); + mSearchInProgress = true; + + F32 currentT = 2.0; + mCurrSeqKey++; + + SceneObjectRef* chain = mOverflowBin.nextInBin; + while (chain) + { + SceneObject* ptr = chain->object; + if (ptr->getContainerSeqKey() != mCurrSeqKey) + { + ptr->setContainerSeqKey(mCurrSeqKey); + + // In the overflow bin, the world box is always going to intersect the line, + // so we can omit that test... + if ((ptr->getTypeMask() & mask) != 0 && + ptr->isCollisionEnabled() == true) + { + Point3F xformedStart, xformedEnd; + ptr->mWorldToObj.mulP(start, &xformedStart); + ptr->mWorldToObj.mulP(end, &xformedEnd); + xformedStart.convolveInverse(ptr->mObjScale); + xformedEnd.convolveInverse(ptr->mObjScale); + + RayInfo ri; + ri.generateTexCoord = info->generateTexCoord; + bool result = false; + if (type == CollisionGeometry) + result = ptr->castRay(xformedStart, xformedEnd, &ri); + else if (type == RenderedGeometry) + result = ptr->castRayRendered(xformedStart, xformedEnd, &ri); + if (result) + { + if( ri.t < currentT && ( !callback || callback( &ri ) ) ) + { + *info = ri; + info->point.interpolate(start, end, info->t); + currentT = ri.t; + info->distance = (start - info->point).len(); + } + } + } + } + chain = chain->nextInBin; + } + + // These are just for rasterizing the line against the grid. We want the x coord + // of the start to be <= the x coord of the end + Point3F normalStart, normalEnd; + if (start.x <= end.x) + { + normalStart = start; + normalEnd = end; + } + else + { + normalStart = end; + normalEnd = start; + } + + // Ok, let's scan the grids. The simplest way to do this will be to scan across in + // x, finding the y range for each affected bin... + U32 minX, maxX; + U32 minY, maxY; +//if (normalStart.x == normalEnd.x) +// Con::printf("X start = %g, end = %g", normalStart.x, normalEnd.x); + + getBinRange(normalStart.x, normalEnd.x, minX, maxX); + getBinRange(getMin(normalStart.y, normalEnd.y), + getMax(normalStart.y, normalEnd.y), minY, maxY); + +//if (normalStart.x == normalEnd.x && minX != maxX) +// Con::printf("X min = %d, max = %d", minX, maxX); +//if (normalStart.y == normalEnd.y && minY != maxY) +// Con::printf("Y min = %d, max = %d", minY, maxY); + + // We'll optimize the case that the line is contained in one bin row or column, which + // will be quite a few lines. No sense doing more work than we have to... + // + if ((mFabs(normalStart.x - normalEnd.x) < csmTotalBinSize && minX == maxX) || + (mFabs(normalStart.y - normalEnd.y) < csmTotalBinSize && minY == maxY)) + { + U32 count; + U32 incX, incY; + if (minX == maxX) + { + count = maxY - minY + 1; + incX = 0; + incY = 1; + } + else + { + count = maxX - minX + 1; + incX = 1; + incY = 0; + } + + U32 x = minX; + U32 y = minY; + for (U32 i = 0; i < count; i++) + { + U32 checkX = x % csmNumBins; + U32 checkY = y % csmNumBins; + + SceneObjectRef* chain = mBinArray[(checkY * csmNumBins) + checkX].nextInBin; + while (chain) + { + SceneObject* ptr = chain->object; + if (ptr->getContainerSeqKey() != mCurrSeqKey) + { + ptr->setContainerSeqKey(mCurrSeqKey); + + if ((ptr->getTypeMask() & mask) != 0 && + ptr->isCollisionEnabled() == true) + { + if (ptr->getWorldBox().collideLine(start, end) || chain->object->isGlobalBounds()) + { + Point3F xformedStart, xformedEnd; + ptr->mWorldToObj.mulP(start, &xformedStart); + ptr->mWorldToObj.mulP(end, &xformedEnd); + xformedStart.convolveInverse(ptr->mObjScale); + xformedEnd.convolveInverse(ptr->mObjScale); + + RayInfo ri; + ri.generateTexCoord = info->generateTexCoord; + bool result = false; + if (type == CollisionGeometry) + result = ptr->castRay(xformedStart, xformedEnd, &ri); + else if (type == RenderedGeometry) + result = ptr->castRayRendered(xformedStart, xformedEnd, &ri); + if (result) + { + if( ri.t < currentT && ( !callback || callback( &ri ) ) ) + { + *info = ri; + info->point.interpolate(start, end, info->t); + currentT = ri.t; + info->distance = (start - info->point).len(); + } + } + } + } + } + chain = chain->nextInBin; + } + + x += incX; + y += incY; + } + } + else + { + // Oh well, let's earn our keep. We know that after the above conditional, we're + // going to cross at least one boundary, so that simplifies our job... + + F32 currStartX = normalStart.x; + + AssertFatal(currStartX != normalEnd.x, "This is going to cause problems in SceneContainer::castRay"); + while (currStartX != normalEnd.x) + { + F32 currEndX = getMin(currStartX + csmTotalBinSize, normalEnd.x); + + F32 currStartT = (currStartX - normalStart.x) / (normalEnd.x - normalStart.x); + F32 currEndT = (currEndX - normalStart.x) / (normalEnd.x - normalStart.x); + + F32 y1 = normalStart.y + (normalEnd.y - normalStart.y) * currStartT; + F32 y2 = normalStart.y + (normalEnd.y - normalStart.y) * currEndT; + + U32 subMinX, subMaxX; + getBinRange(currStartX, currEndX, subMinX, subMaxX); + + F32 subStartX = currStartX; + F32 subEndX = currStartX; + + if (currStartX < 0.0f) + subEndX -= mFmod(subEndX, csmBinSize); + else + subEndX += (csmBinSize - mFmod(subEndX, csmBinSize)); + + for (U32 currXBin = subMinX; currXBin <= subMaxX; currXBin++) + { + U32 checkX = currXBin % csmNumBins; + + F32 subStartT = (subStartX - currStartX) / (currEndX - currStartX); + F32 subEndT = getMin(F32((subEndX - currStartX) / (currEndX - currStartX)), 1.f); + + F32 subY1 = y1 + (y2 - y1) * subStartT; + F32 subY2 = y1 + (y2 - y1) * subEndT; + + U32 newMinY, newMaxY; + getBinRange(getMin(subY1, subY2), getMax(subY1, subY2), newMinY, newMaxY); + + for (U32 i = newMinY; i <= newMaxY; i++) + { + U32 checkY = i % csmNumBins; + + SceneObjectRef* chain = mBinArray[(checkY * csmNumBins) + checkX].nextInBin; + while (chain) + { + SceneObject* ptr = chain->object; + if (ptr->getContainerSeqKey() != mCurrSeqKey) + { + ptr->setContainerSeqKey(mCurrSeqKey); + + if ((ptr->getTypeMask() & mask) != 0 && + ptr->isCollisionEnabled() == true) + { + if (ptr->getWorldBox().collideLine(start, end)) + { + Point3F xformedStart, xformedEnd; + ptr->mWorldToObj.mulP(start, &xformedStart); + ptr->mWorldToObj.mulP(end, &xformedEnd); + xformedStart.convolveInverse(ptr->mObjScale); + xformedEnd.convolveInverse(ptr->mObjScale); + + RayInfo ri; + ri.generateTexCoord = info->generateTexCoord; + bool result = false; + if (type == CollisionGeometry) + result = ptr->castRay(xformedStart, xformedEnd, &ri); + else if (type == RenderedGeometry) + result = ptr->castRayRendered(xformedStart, xformedEnd, &ri); + if (result) + { + if( ri.t < currentT && ( !callback || callback( &ri ) ) ) + { + *info = ri; + info->point.interpolate(start, end, info->t); + currentT = ri.t; + info->distance = (start - info->point).len(); + } + } + } + } + } + chain = chain->nextInBin; + } + } + + subStartX = subEndX; + subEndX = getMin(subEndX + csmBinSize, currEndX); + } + + currStartX = currEndX; + } + } + + mSearchInProgress = false; + + // Bump the normal into worldspace if appropriate. + if(currentT != 2) + { + PlaneF fakePlane; + fakePlane.x = info->normal.x; + fakePlane.y = info->normal.y; + fakePlane.z = info->normal.z; + fakePlane.d = 0; + + PlaneF result; + mTransformPlane(info->object->getTransform(), info->object->getScale(), fakePlane, &result); + info->normal = result; + + return true; + } + else + { + // Do nothing and exit... + return false; + } +} + +//----------------------------------------------------------------------------- + +// collide with the objects projected object box +bool SceneContainer::collideBox(const Point3F &start, const Point3F &end, U32 mask, RayInfo * info) +{ + AssertFatal( info->userData == NULL, "SceneContainer::collideBox - RayInfo->userData cannot be used here!" ); + + F32 currentT = 2; + for (Link* itr = mStart.next; itr != &mEnd; itr = itr->next) + { + SceneObject* ptr = static_cast(itr); + if (ptr->getTypeMask() & mask && !ptr->mCollisionCount) + { + Point3F xformedStart, xformedEnd; + ptr->mWorldToObj.mulP(start, &xformedStart); + ptr->mWorldToObj.mulP(end, &xformedEnd); + xformedStart.convolveInverse(ptr->mObjScale); + xformedEnd.convolveInverse(ptr->mObjScale); + + RayInfo ri; + if(ptr->collideBox(xformedStart, xformedEnd, &ri)) + { + if(ri.t < currentT) + { + *info = ri; + info->point.interpolate(start, end, info->t); + currentT = ri.t; + } + } + } + } + return currentT != 2; +} + +//----------------------------------------------------------------------------- + +static void buildCallback(SceneObject* object,void *key) +{ + SceneContainer::CallbackInfo* info = reinterpret_cast(key); + object->buildPolyList(info->context,info->polyList,info->boundingBox,info->boundingSphere); +} + +bool SceneContainer::buildPolyList(PolyListContext context, const Box3F &box, U32 mask, AbstractPolyList *polyList) +{ + CallbackInfo info; + info.context = context; + info.boundingBox = box; + info.polyList = polyList; + + // Build bounding sphere + info.boundingSphere.center = (info.boundingBox.minExtents + info.boundingBox.maxExtents) * 0.5; + VectorF bv = box.maxExtents - info.boundingSphere.center; + info.boundingSphere.radius = bv.len(); + + sPolyList = polyList; + findObjects(box,mask,buildCallback,&info); + return !polyList->isEmpty(); +} + +//----------------------------------------------------------------------------- + +void SceneContainer::cleanupSearchVectors() +{ + for (U32 i = 0; i < mSearchList.size(); i++) + delete mSearchList[i]; + mSearchList.clear(); + mCurrSearchPos = -1; +} + +//----------------------------------------------------------------------------- + +static Point3F sgSortReferencePoint; +static int QSORT_CALLBACK cmpSearchPointers(const void* inP1, const void* inP2) +{ + SimObjectPtr** p1 = (SimObjectPtr**)inP1; + SimObjectPtr** p2 = (SimObjectPtr**)inP2; + + Point3F temp; + F32 d1, d2; + + if (bool(**p1)) + { + (**p1)->getWorldBox().getCenter(&temp); + d1 = (temp - sgSortReferencePoint).len(); + } + else + { + d1 = 0; + } + if (bool(**p2)) + { + (**p2)->getWorldBox().getCenter(&temp); + d2 = (temp - sgSortReferencePoint).len(); + } + else + { + d2 = 0; + } + + if (d1 > d2) + return 1; + else if (d1 < d2) + return -1; + else + return 0; +} + +void SceneContainer::initRadiusSearch(const Point3F& searchPoint, + const F32 searchRadius, + const U32 searchMask) +{ + cleanupSearchVectors(); + + mSearchReferencePoint = searchPoint; + + Box3F queryBox(searchPoint, searchPoint); + queryBox.minExtents -= Point3F(searchRadius, searchRadius, searchRadius); + queryBox.maxExtents += Point3F(searchRadius, searchRadius, searchRadius); + + SimpleQueryList queryList; + findObjects(queryBox, searchMask, SimpleQueryList::insertionCallback, &queryList); + + F32 radiusSquared = searchRadius * searchRadius; + + const F32* pPoint = &searchPoint.x; + for (U32 i = 0; i < queryList.mList.size(); i++) + { + const F32* bMins; + const F32* bMaxs; + bMins = &queryList.mList[i]->getWorldBox().minExtents.x; + bMaxs = &queryList.mList[i]->getWorldBox().maxExtents.x; + F32 sum = 0; + for (U32 j = 0; j < 3; j++) + { + if (pPoint[j] < bMins[j]) + sum += (pPoint[j] - bMins[j])*(pPoint[j] - bMins[j]); + else if (pPoint[j] > bMaxs[j]) + sum += (pPoint[j] - bMaxs[j])*(pPoint[j] - bMaxs[j]); + } + if (sum < radiusSquared || queryList.mList[i]->isGlobalBounds()) + { + mSearchList.push_back(new SimObjectPtr); + *(mSearchList.last()) = queryList.mList[i]; + } + } + if (mSearchList.size() != 0) + { + sgSortReferencePoint = mSearchReferencePoint; + dQsort(mSearchList.address(), mSearchList.size(), + sizeof(SimObjectPtr*), cmpSearchPointers); + } +} + +//----------------------------------------------------------------------------- + +void SceneContainer::initTypeSearch(const U32 searchMask) +{ + cleanupSearchVectors(); + + SimpleQueryList queryList; + findObjects(searchMask, SimpleQueryList::insertionCallback, &queryList); + + for (U32 i = 0; i < queryList.mList.size(); i++) + { + mSearchList.push_back(new SimObjectPtr); + *(mSearchList.last()) = queryList.mList[i]; + } + if (mSearchList.size() != 0) + { + sgSortReferencePoint = mSearchReferencePoint; + dQsort(mSearchList.address(), mSearchList.size(), + sizeof(SimObjectPtr*), cmpSearchPointers); + } +} + +//----------------------------------------------------------------------------- + +SceneObject* SceneContainer::containerSearchNextObject() +{ + if (mCurrSearchPos >= mSearchList.size()) + return NULL; + + mCurrSearchPos++; + while (mCurrSearchPos < mSearchList.size() && bool(*mSearchList[mCurrSearchPos]) == false) + mCurrSearchPos++; + + if (mCurrSearchPos == mSearchList.size()) + return NULL; + + return (*mSearchList[mCurrSearchPos]); +} + +//----------------------------------------------------------------------------- + +U32 SceneContainer::containerSearchNext() +{ + SceneObject* object = containerSearchNextObject(); + if( !object ) + return 0; + return object->getId(); +} + +//----------------------------------------------------------------------------- + +F32 SceneContainer::containerSearchCurrDist() +{ + AssertFatal(mCurrSearchPos != -1, "Error, must call containerSearchNext before containerSearchCurrDist"); + + if (mCurrSearchPos == -1 || mCurrSearchPos >= mSearchList.size() || + bool(*mSearchList[mCurrSearchPos]) == false) + return 0.0; + + Point3F pos; + (*mSearchList[mCurrSearchPos])->getWorldBox().getCenter(&pos); + return (pos - mSearchReferencePoint).len(); +} + +//----------------------------------------------------------------------------- + +F32 SceneContainer::containerSearchCurrRadiusDist() +{ + AssertFatal(mCurrSearchPos != -1, "Error, must call containerSearchNext before containerSearchCurrDist"); + + if (mCurrSearchPos == -1 || mCurrSearchPos >= mSearchList.size() || + bool(*mSearchList[mCurrSearchPos]) == false) + return 0.0; + + Point3F pos; + (*mSearchList[mCurrSearchPos])->getWorldBox().getCenter(&pos); + + F32 dist = (pos - mSearchReferencePoint).len(); + + F32 min = (*mSearchList[mCurrSearchPos])->getWorldBox().len_x(); + if ((*mSearchList[mCurrSearchPos])->getWorldBox().len_y() < min) + min = (*mSearchList[mCurrSearchPos])->getWorldBox().len_y(); + if ((*mSearchList[mCurrSearchPos])->getWorldBox().len_z() < min) + min = (*mSearchList[mCurrSearchPos])->getWorldBox().len_z(); + + dist -= min; + if (dist < 0) + dist = 0; + + return dist; +} + +//----------------------------------------------------------------------------- + +void SceneContainer::getBinRange( const F32 min, const F32 max, U32& minBin, U32& maxBin ) +{ + AssertFatal(max >= min, "Error, bad range! in getBinRange"); + + if ((max - min) >= (SceneContainer::csmTotalBinSize - SceneContainer::csmBinSize)) + { + F32 minCoord = mFmod(min, SceneContainer::csmTotalBinSize); + if (minCoord < 0.0f) + { + minCoord += SceneContainer::csmTotalBinSize; + + // This is truly lame, but it can happen. There must be a better way to + // deal with this. + if (minCoord == SceneContainer::csmTotalBinSize) + minCoord = SceneContainer::csmTotalBinSize - 0.01; + } + + AssertFatal(minCoord >= 0.0 && minCoord < SceneContainer::csmTotalBinSize, "Bad minCoord"); + + minBin = U32(minCoord / SceneContainer::csmBinSize); + AssertFatal(minBin < SceneContainer::csmNumBins, avar("Error, bad clipping! (%g, %d)", minCoord, minBin)); + + maxBin = minBin + (SceneContainer::csmNumBins - 1); + return; + } + else + { + + F32 minCoord = mFmod(min, SceneContainer::csmTotalBinSize); + + if (minCoord < 0.0f) + { + minCoord += SceneContainer::csmTotalBinSize; + + // This is truly lame, but it can happen. There must be a better way to + // deal with this. + if (minCoord == SceneContainer::csmTotalBinSize) + minCoord = SceneContainer::csmTotalBinSize - 0.01; + } + AssertFatal(minCoord >= 0.0 && minCoord < SceneContainer::csmTotalBinSize, "Bad minCoord"); + + F32 maxCoord = mFmod(max, SceneContainer::csmTotalBinSize); + if (maxCoord < 0.0f) { + maxCoord += SceneContainer::csmTotalBinSize; + + // This is truly lame, but it can happen. There must be a better way to + // deal with this. + if (maxCoord == SceneContainer::csmTotalBinSize) + maxCoord = SceneContainer::csmTotalBinSize - 0.01; + } + AssertFatal(maxCoord >= 0.0 && maxCoord < SceneContainer::csmTotalBinSize, "Bad maxCoord"); + + minBin = U32(minCoord / SceneContainer::csmBinSize); + maxBin = U32(maxCoord / SceneContainer::csmBinSize); + AssertFatal(minBin < SceneContainer::csmNumBins, avar("Error, bad clipping(min)! (%g, %d)", maxCoord, minBin)); + AssertFatal(minBin < SceneContainer::csmNumBins, avar("Error, bad clipping(max)! (%g, %d)", maxCoord, maxBin)); + + // MSVC6 seems to be generating some bad floating point code around + // here when full optimizations are on. The min != max test should + // not be needed, but it clears up the VC issue. + if (min != max && minCoord > maxCoord) + maxBin += SceneContainer::csmNumBins; + + AssertFatal(maxBin >= minBin, "Error, min should always be less than max!"); + } +} + +//============================================================================= +// Console API. +//============================================================================= +// MARK: ---- Console API ---- + +ConsoleFunctionGroupBegin( Containers, "Functions for ray casting and spatial queries.\n\n"); + +//----------------------------------------------------------------------------- + +DefineEngineFunction( containerBoxEmpty, bool, + ( U32 mask, Point3F center, F32 xRadius, F32 yRadius, F32 zRadius, bool useClientContainer ), ( -1, -1, false ), + "@brief See if any objects of the given types are present in box of given extent.\n\n" + "@note Extent parameter is last since only one radius is often needed. If " + "one radius is provided, the yRadius and zRadius are assumed to be the same. Unfortunately, " + "if you need to use the client container, you'll need to set all of the radius parameters. " + "Fortunately, this function is mostly used on the server.\n" + "@param mask Indicates the type of objects we are checking against.\n" + "@param center Center of box.\n" + "@param xRadius Search radius in the x-axis. See note above.\n" + "@param yRadius Search radius in the y-axis. See note above.\n" + "@param zRadius Search radius in the z-axis. See note above.\n" + "@param useClientContainer Optionally indicates the search should be within the " + "client container.\n" + "@return true if the box is empty, false if any object is found.\n" + "@ingroup Game") +{ + Point3F extent( xRadius, yRadius, zRadius ); + extent.y = extent.y >= 0 ? extent.y : extent.x; + extent.z = extent.z >= 0 ? extent.z : extent.x; + + Box3F B(center - extent, center + extent, true); + + EarlyOutPolyList polyList; + polyList.mPlaneList.clear(); + polyList.mNormal.set(0,0,0); + polyList.mPlaneList.setSize(6); + polyList.mPlaneList[0].set(B.minExtents, VectorF(-1,0,0)); + polyList.mPlaneList[1].set(B.maxExtents, VectorF(0,1,0)); + polyList.mPlaneList[2].set(B.maxExtents, VectorF(1,0,0)); + polyList.mPlaneList[3].set(B.minExtents, VectorF(0,-1,0)); + polyList.mPlaneList[4].set(B.minExtents, VectorF(0,0,-1)); + polyList.mPlaneList[5].set(B.maxExtents, VectorF(0,0,1)); + + SceneContainer* pContainer = useClientContainer ? &gClientContainer : &gServerContainer; + + return ! pContainer->buildPolyList(PLC_Collision, B, mask, &polyList); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( initContainerRadiusSearch, void, ( Point3F pos, F32 radius, U32 mask, bool useClientContainer ), ( false ), + "@brief Start a search for items at the given position and within the given radius, filtering by mask.\n\n" + + "@param pos Center position for the search\n" + "@param radius Search radius\n" + "@param mask Bitmask of object types to include in the search\n" + "@param useClientContainer Optionally indicates the search should be within the " + "client container.\n" + + "@see containerSearchNext\n" + "@ingroup Game") +{ + SceneContainer* pContainer = useClientContainer ? &gClientContainer : &gServerContainer; + + pContainer->initRadiusSearch( pos, radius, mask ); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( initContainerTypeSearch, void, ( U32 mask, bool useClientContainer ), ( false ), + "@brief Start a search for all items of the types specified by the bitset mask.\n\n" + + "@param mask Bitmask of object types to include in the search\n" + "@param useClientContainer Optionally indicates the search should be within the " + "client container.\n" + + "@see containerSearchNext\n" + "@ingroup Game") +{ + SceneContainer* pContainer = useClientContainer ? &gClientContainer : &gServerContainer; + + pContainer->initTypeSearch( mask ); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( containerSearchNext, SceneObject*, ( bool useClientContainer ), ( false ), + "@brief Get next item from a search started with initContainerRadiusSearch() or " + "initContainerTypeSearch().\n\n" + + "@param useClientContainer Optionally indicates the search should be within the " + "client container.\n" + "@return the next object found in the search, or null if no more\n" + + "@tsexample\n" + "// print the names of all nearby ShapeBase derived objects\n" + "%position = %obj.getPosition;\n" + "%radius = 20;\n" + "%mask = $TypeMasks::ShapeBaseObjectType;\n" + "initContainerRadiusSearch( %position, %radius, %mask );\n" + "while ( (%targetObject = containerSearchNext()) != 0 )\n" + "{\n" + " echo( \"Found: \" @ %targetObject.getName() );\n" + "}\n" + "@endtsexample\n" + + "@see initContainerRadiusSearch()\n" + "@see initContainerTypeSearch()\n" + "@ingroup Game") +{ + SceneContainer* pContainer = useClientContainer ? &gClientContainer : &gServerContainer; + + return pContainer->containerSearchNextObject(); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( containerSearchCurrDist, F32, ( bool useClientContainer ), ( false ), + "@brief Get distance of the center of the current item from the center of the " + "current initContainerRadiusSearch.\n\n" + + "@param useClientContainer Optionally indicates the search should be within the " + "client container.\n" + "@return distance from the center of the current object to the center of " + "the search\n" + + "@see containerSearchNext\n" + "@ingroup Game") +{ + SceneContainer* pContainer = useClientContainer ? &gClientContainer : &gServerContainer; + + return pContainer->containerSearchCurrDist(); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( containerSearchCurrRadiusDist, F32, ( bool useClientContainer ), ( false ), + "@brief Get the distance of the closest point of the current item from the center " + "of the current initContainerRadiusSearch.\n\n" + + "@param useClientContainer Optionally indicates the search should be within the " + "client container.\n" + "@return distance from the closest point of the current object to the " + "center of the search\n" + + "@see containerSearchNext\n" + "@ingroup Game") +{ + SceneContainer* pContainer = useClientContainer ? &gClientContainer : &gServerContainer; + + return pContainer->containerSearchCurrRadiusDist(); +} + +//----------------------------------------------------------------------------- + +//TODO: make RayInfo an API type +DefineEngineFunction( containerRayCast, const char*, + ( Point3F start, Point3F end, U32 mask, SceneObject *pExempt, bool useClientContainer ), ( NULL, false ), + "@brief Cast a ray from start to end, checking for collision against items matching mask.\n\n" + + "If pExempt is specified, then it is temporarily excluded from collision checks (For " + "instance, you might want to exclude the player if said player was firing a weapon.)\n" + + "@param start An XYZ vector containing the tail position of the ray.\n" + "@param end An XYZ vector containing the head position of the ray\n" + "@param mask A bitmask corresponding to the type of objects to check for\n" + "@param pExempt An optional ID for a single object that ignored for this raycast\n" + "@param useClientContainer Optionally indicates the search should be within the " + "client container.\n" + + "@returns A string containing either null, if nothing was struck, or these fields:\n" + "
      • The ID of the object that was struck.
      • " + "
      • The x, y, z position that it was struck.
      • " + "
      • The x, y, z of the normal of the face that was struck.
      " + + "@ingroup Game") +{ + if (pExempt) + pExempt->disableCollision(); + + SceneContainer* pContainer = useClientContainer ? &gClientContainer : &gServerContainer; + + RayInfo rinfo; + S32 ret = 0; + if (pContainer->castRay(start, end, mask, &rinfo) == true) + ret = rinfo.object->getId(); + + if (pExempt) + pExempt->enableCollision(); + + // add the hit position and normal? + char *returnBuffer = Con::getReturnBuffer(256); + if(ret) + { + dSprintf(returnBuffer, 256, "%d %g %g %g %g %g %g", + ret, rinfo.point.x, rinfo.point.y, rinfo.point.z, + rinfo.normal.x, rinfo.normal.y, rinfo.normal.z); + } + else + { + returnBuffer[0] = '0'; + returnBuffer[1] = '\0'; + } + + return(returnBuffer); +} + +ConsoleFunctionGroupEnd( Containers ); diff --git a/Engine/source/scene/sceneContainer.h b/Engine/source/scene/sceneContainer.h new file mode 100644 index 000000000..b79317aff --- /dev/null +++ b/Engine/source/scene/sceneContainer.h @@ -0,0 +1,335 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SCENECONTAINER_H_ +#define _SCENECONTAINER_H_ + +#ifndef _MBOX_H_ +#include "math/mBox.h" +#endif + +#ifndef _MSPHERE_H_ +#include "math/mSphere.h" +#endif + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +#ifndef _MPOLYHEDRON_H_ +#include "math/mPolyhedron.h" +#endif + +#ifndef _SIMOBJECT_H_ +#include "console/simObject.h" +#endif + + +/// @file +/// SceneObject database. + + +class SceneObject; +class AbstractPolyList; +class OptimizedPolyList; +class Frustum; +class Point3F; + +struct RayInfo; + + +template< typename T > +class SceneObjectRefBase +{ + public: + + /// Object that is referenced in the link. + SceneObject* object; + + /// Next link in chain of container. + T* nextInBin; + + /// Previous link in chain of container. + T* prevInBin; + + /// Next link in chain that is associated with #object. + T* nextInObj; +}; + + +/// Reference to a scene object. +class SceneObjectRef : public SceneObjectRefBase< SceneObjectRef > {}; + + +/// A contextual hint passed to the polylist methods which +/// allows it to return the appropriate geometry. +enum PolyListContext +{ + /// A hint that the polyist is intended + /// for collision testing. + PLC_Collision, + + /// A hint that the polyist is for decal + /// geometry generation. + PLC_Decal, + + /// A hint that the polyist is used for + /// selection from an editor or other tool. + PLC_Selection, + + /// A hint that the polyist will be used + /// to export geometry and would like to have + /// texture coords and materials. + PLC_Export +}; + + +/// For simple queries. Simply creates a vector of the objects +class SimpleQueryList +{ + public: + + Vector< SceneObject* > mList; + + SimpleQueryList() + { + VECTOR_SET_ASSOCIATION( mList ); + } + + void insertObject( SceneObject* obj ) { mList.push_back(obj); } + static void insertionCallback( SceneObject* obj, void* key ) + { + SimpleQueryList* pList = reinterpret_cast< SimpleQueryList* >( key ); + pList->insertObject( obj ); + } +}; + + +//---------------------------------------------------------------------------- + +/// Database for SceneObjects. +/// +/// ScenceContainer implements a grid-based spatial subdivision for the contents of a scene. +class SceneContainer +{ + enum CastRayType + { + CollisionGeometry, + RenderedGeometry, + }; + + public: + + struct Link + { + Link* next; + Link* prev; + Link(); + void unlink(); + void linkAfter(Link* ptr); + }; + + struct CallbackInfo + { + PolyListContext context; + AbstractPolyList* polyList; + Box3F boundingBox; + SphereF boundingSphere; + void *key; + }; + + private: + + Link mStart; + Link mEnd; + + /// Container queries based on #mCurrSeqKey are are not re-entrant; + /// this is used to detect when it happens. + bool mSearchInProgress; + + /// Current sequence key. + U32 mCurrSeqKey; + + SceneObjectRef* mFreeRefPool; + Vector< SceneObjectRef* > mRefPoolBlocks; + + SceneObjectRef* mBinArray; + SceneObjectRef mOverflowBin; + + /// A vector that contains just the water and physical zone + /// object types which is used to optimize searches. + Vector< SceneObject* > mWaterAndZones; + + /// Vector that contains just the terrain objects in the container. + Vector< SceneObject* > mTerrains; + + static const U32 csmNumBins; + static const F32 csmBinSize; + static const F32 csmTotalBinSize; + static const U32 csmRefPoolBlockSize; + + public: + + SceneContainer(); + ~SceneContainer(); + + /// Return a vector containing all the water and physical zone objects in this container. + const Vector< SceneObject* >& getWaterAndPhysicalZones() const { return mWaterAndZones; } + + /// Return a vector containing all terrain objects in this container. + const Vector< SceneObject* >& getTerrains() const { return mTerrains; } + + /// @name Basic database operations + /// @{ + + /// + typedef void ( *FindCallback )( SceneObject* object, void* key ); + + /// Find all objects of the given type(s) and invoke the given callback for each + /// of them. + /// @param mask Object type mask (@see SimObjectTypes). + /// @param callback Pointer to function to invoke for each object. + /// @param key User data to pass to the "key" argument of @a callback. + void findObjects( U32 mask, FindCallback callback, void* key = NULL ); + + void findObjects( const Box3F& box, U32 mask, FindCallback, void *key = NULL ); + void findObjects( const Frustum& frustum, U32 mask, FindCallback, void *key = NULL ); + + void polyhedronFindObjects( const Polyhedron& polyhedron, U32 mask, FindCallback, void *key = NULL ); + + /// Find all objects of the given type(s) and add them to the given vector. + /// @param mask Object type mask (@see SimObjectTypes). + /// @param outFound Vector to add found objects to. + void findObjectList( U32 mask, Vector< SceneObject* >* outFound ); + + /// + void findObjectList( const Box3F& box, U32 mask, Vector< SceneObject* >* outFound ); + + /// + void findObjectList( const Frustum& frustum, U32 mask, Vector< SceneObject* >* outFound ); + + /// @} + + /// @name Line intersection + /// @{ + + typedef bool ( *CastRayCallback )( RayInfo* ri ); + + /// Test against collision geometry -- fast. + bool castRay( const Point3F &start, const Point3F &end, U32 mask, RayInfo* info, CastRayCallback callback = NULL ); + + /// Test against rendered geometry -- slow. + bool castRayRendered( const Point3F &start, const Point3F &end, U32 mask, RayInfo* info, CastRayCallback callback = NULL ); + + bool collideBox(const Point3F &start, const Point3F &end, U32 mask, RayInfo* info); + + /// @} + + /// @name Poly list + /// @{ + + /// + bool buildPolyList( PolyListContext context, + const Box3F &box, + U32 typeMask, + AbstractPolyList *polylist ); + + /// @} + + /// Add an object to the database. + /// @param object A SceneObject. + bool addObject( SceneObject* object ); + + /// Remove an object from the database. + /// @param object A SceneObject. + bool removeObject( SceneObject* object ); + + void addRefPoolBlock(); + SceneObjectRef* allocateObjectRef(); + void freeObjectRef(SceneObjectRef*); + void insertIntoBins( SceneObject* object ); + void removeFromBins( SceneObject* object ); + + /// Make sure that we're not just sticking the object right back + /// where it came from. The overloaded insertInto is so we don't calculate + /// the ranges twice. + void checkBins( SceneObject* object ); + void insertIntoBins(SceneObject*, U32, U32, U32, U32); + + void initRadiusSearch(const Point3F& searchPoint, + const F32 searchRadius, + const U32 searchMask); + void initTypeSearch(const U32 searchMask); + SceneObject* containerSearchNextObject(); + U32 containerSearchNext(); + F32 containerSearchCurrDist(); + F32 containerSearchCurrRadiusDist(); + + private: + + Vector*> mSearchList;///< Object searches to support console querying of the database. ONLY WORKS ON SERVER + S32 mCurrSearchPos; + Point3F mSearchReferencePoint; + + void cleanupSearchVectors(); + + /// Base cast ray code + bool _castRay( U32 type, const Point3F &start, const Point3F &end, U32 mask, RayInfo* info, CastRayCallback callback ); + + void _findSpecialObjects( const Vector< SceneObject* >& vector, U32 mask, FindCallback, void *key = NULL ); + void _findSpecialObjects( const Vector< SceneObject* >& vector, const Box3F &box, U32 mask, FindCallback callback, void *key = NULL ); + + static void getBinRange( const F32 min, const F32 max, U32& minBin, U32& maxBin ); +}; + +//----------------------------------------------------------------------------- + +extern SceneContainer gServerContainer; +extern SceneContainer gClientContainer; + +//----------------------------------------------------------------------------- + +inline void SceneContainer::freeObjectRef(SceneObjectRef* trash) +{ + trash->object = NULL; + trash->nextInBin = NULL; + trash->prevInBin = NULL; + trash->nextInObj = mFreeRefPool; + mFreeRefPool = trash; +} + +//----------------------------------------------------------------------------- + +inline SceneObjectRef* SceneContainer::allocateObjectRef() +{ + if( mFreeRefPool == NULL ) + addRefPoolBlock(); + AssertFatal( mFreeRefPool!=NULL, "Error, should always have a free reference here!" ); + + SceneObjectRef* ret = mFreeRefPool; + mFreeRefPool = mFreeRefPool->nextInObj; + + ret->nextInObj = NULL; + return ret; +} + +#endif // !_SCENECONTAINER_H_ \ No newline at end of file diff --git a/Engine/source/scene/sceneManager.cpp b/Engine/source/scene/sceneManager.cpp new file mode 100644 index 000000000..3e323efdb --- /dev/null +++ b/Engine/source/scene/sceneManager.cpp @@ -0,0 +1,692 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "scene/sceneManager.h" + +#include "scene/sceneObject.h" +#include "scene/zones/sceneTraversalState.h" +#include "scene/sceneRenderState.h" +#include "scene/zones/sceneRootZone.h" +#include "scene/zones/sceneZoneSpace.h" +#include "lighting/lightManager.h" +#include "renderInstance/renderPassManager.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxDrawUtil.h" +#include "gfx/gfxDebugEvent.h" +#include "console/engineAPI.h" +#include "sim/netConnection.h" +#include "T3D/gameBase/gameConnection.h" + +// For player object bounds workaround. +#include "T3D/player.h" + + +extern bool gEditingMission; + + +MODULE_BEGIN( Scene ) + + MODULE_INIT_AFTER( Sim ) + MODULE_SHUTDOWN_BEFORE( Sim ) + + MODULE_INIT + { + // Client scene. + gClientSceneGraph = new SceneManager( true ); + + // Server scene. + gServerSceneGraph = new SceneManager( false ); + + Con::addVariable( "$Scene::lockCull", TypeBool, &SceneManager::smLockDiffuseFrustum, + "Debug tool which locks the frustum culling to the current camera location.\n" + "@ingroup Rendering\n" ); + + Con::addVariable( "$Scene::disableTerrainOcclusion", TypeBool, &SceneCullingState::smDisableTerrainOcclusion, + "Used to disable the somewhat expensive terrain occlusion testing.\n" + "@ingroup Rendering\n" ); + + Con::addVariable( "$Scene::disableZoneCulling", TypeBool, &SceneCullingState::smDisableZoneCulling, + "If true, zone culling will be disabled and the scene contents will only be culled against the root frustum.\n\n" + "@ingroup Rendering\n" ); + + Con::addVariable( "$Scene::renderBoundingBoxes", TypeBool, &SceneManager::smRenderBoundingBoxes, + "If true, the bounding boxes of objects will be displayed.\n\n" + "@ingroup Rendering" ); + + Con::addVariable( "$Scene::maxOccludersPerZone", TypeS32, &SceneCullingState::smMaxOccludersPerZone, + "Maximum number of occluders that will be concurrently allowed into the scene culling state of any given zone.\n\n" + "@ingroup Rendering" ); + + Con::addVariable( "$Scene::occluderMinWidthPercentage", TypeF32, &SceneCullingState::smOccluderMinWidthPercentage, + "TODO\n\n" + "@ingroup Rendering" ); + + Con::addVariable( "$Scene::occluderMinHeightPercentage", TypeF32, &SceneCullingState::smOccluderMinHeightPercentage, + "TODO\n\n" + "@ingroup Rendering" ); + } + + MODULE_SHUTDOWN + { + SAFE_DELETE( gClientSceneGraph ); + SAFE_DELETE( gServerSceneGraph ); + } + +MODULE_END; + + +bool SceneManager::smRenderBoundingBoxes; +bool SceneManager::smLockDiffuseFrustum = false; +SceneCameraState SceneManager::smLockedDiffuseCamera = SceneCameraState( RectI(), Frustum(), MatrixF(), MatrixF() ); + +SceneManager* gClientSceneGraph = NULL; +SceneManager* gServerSceneGraph = NULL; + + +//----------------------------------------------------------------------------- + +SceneManager::SceneManager( bool isClient ) + : mLightManager( NULL ), + mCurrentRenderState( NULL ), + mIsClient( isClient ), + mUsePostEffectFog( true ), + mDisplayTargetResolution( 0, 0 ), + mDefaultRenderPass( NULL ), + mVisibleDistance( 500.f ), + mNearClip( 0.1f ), + mAmbientLightColor( ColorF( 0.1f, 0.1f, 0.1f, 1.0f ) ), + mZoneManager( NULL ) +{ + VECTOR_SET_ASSOCIATION( mBatchQueryList ); + + // For the client, create a zone manager. + + if( isClient ) + { + mZoneManager = new SceneZoneSpaceManager( getContainer() ); + + // Add the root zone to the scene. + + addObjectToScene( mZoneManager->getRootZone() ); + } +} + +//----------------------------------------------------------------------------- + +SceneManager::~SceneManager() +{ + SAFE_DELETE( mZoneManager ); + + if( mLightManager ) + mLightManager->deactivate(); +} + +//----------------------------------------------------------------------------- + +void SceneManager::renderScene( ScenePassType passType, U32 objectMask ) +{ + SceneCameraState cameraState = SceneCameraState::fromGFX(); + + // Handle frustum locking. + + const bool lockedFrustum = ( smLockDiffuseFrustum && passType == SPT_Diffuse ); + if( lockedFrustum ) + cameraState = smLockedDiffuseCamera; + else if( passType == SPT_Diffuse ) + { + // Store the camera state so if we lock, this will become the + // locked state. + + if( passType == SPT_Diffuse ) + smLockedDiffuseCamera = cameraState; + } + + // Create the render state. + + SceneRenderState renderState( this, passType, cameraState ); + + // If we have locked the frustum, reset the view transform + // on the render pass which the render state has just set + // to the view matrix corresponding to the locked frustum. For + // rendering, however, we need the true view matrix from the + // GFX state. + + if( lockedFrustum ) + { + RenderPassManager* rpm = renderState.getRenderPass(); + rpm->assignSharedXform( RenderPassManager::View, GFX->getWorldMatrix() ); + } + + // Render. + + renderScene( &renderState, objectMask ); +} + +//----------------------------------------------------------------------------- + +void SceneManager::renderScene( SceneRenderState* renderState, U32 objectMask, SceneZoneSpace* baseObject, U32 baseZone ) +{ + PROFILE_SCOPE( SceneGraph_renderScene ); + + // Get the lights for rendering the scene. + + PROFILE_START( SceneGraph_registerLights ); + LIGHTMGR->registerGlobalLights( &renderState->getFrustum(), false ); + PROFILE_END(); + + // If its a diffuse pass, update the current ambient light level. + // To do that find the starting zone and determine whether it has a custom + // ambient light color. If so, pass it on to the ambient light manager. + // If not, use the ambient light color of the sunlight. + // + // Note that we retain the starting zone information here and pass it + // on to renderSceneNoLights so that we don't need to look it up twice. + + if( renderState->isDiffusePass() ) + { + if( !baseObject && getZoneManager() ) + { + getZoneManager()->findZone( renderState->getCameraPosition(), baseObject, baseZone ); + AssertFatal( baseObject != NULL, "SceneManager::renderScene - findZone() did not return an object" ); + } + + ColorF zoneAmbient; + if( baseObject && baseObject->getZoneAmbientLightColor( baseZone, zoneAmbient ) ) + mAmbientLightColor.setTargetValue( zoneAmbient ); + else + { + const LightInfo* sunlight = LIGHTMGR->getSpecialLight( LightManager::slSunLightType ); + if( sunlight ) + mAmbientLightColor.setTargetValue( sunlight->getAmbient() ); + } + + renderState->setAmbientLightColor( mAmbientLightColor.getCurrentValue() ); + } + + // Trigger the pre-render signal. + + PROFILE_START( SceneGraph_preRenderSignal); + mCurrentRenderState = renderState; + getPreRenderSignal().trigger( this, renderState ); + mCurrentRenderState = NULL; + PROFILE_END(); + + // Render the scene. + + renderSceneNoLights( renderState, objectMask, baseObject, baseZone ); + + // Trigger the post-render signal. + + PROFILE_START( SceneGraphRender_postRenderSignal ); + mCurrentRenderState = renderState; + getPostRenderSignal().trigger( this, renderState ); + mCurrentRenderState = NULL; + PROFILE_END(); + + // Remove the previously registered lights. + + PROFILE_START( SceneGraph_unregisterLights); + LIGHTMGR->unregisterAllLights(); + PROFILE_END(); +} + +//----------------------------------------------------------------------------- + +void SceneManager::renderSceneNoLights( SceneRenderState* renderState, U32 objectMask, SceneZoneSpace* baseObject, U32 baseZone ) +{ + // Set the current state. + + mCurrentRenderState = renderState; + + // Render. + + _renderScene( mCurrentRenderState, objectMask, baseObject, baseZone ); + + #ifdef TORQUE_DEBUG + + // If frustum is locked and this is a diffuse pass, render the culling volumes of + // zones that are selected (or the volumes of the outdoor zone if no zone is + // selected). + + if( gEditingMission && renderState->isDiffusePass() && smLockDiffuseFrustum ) + renderState->getCullingState().debugRenderCullingVolumes(); + + #endif + + mCurrentRenderState = NULL; +} + +//----------------------------------------------------------------------------- + +void SceneManager::_renderScene( SceneRenderState* state, U32 objectMask, SceneZoneSpace* baseObject, U32 baseZone ) +{ + AssertFatal( this == gClientSceneGraph, "SceneManager::_buildSceneGraph - Only the client scenegraph can support this call!" ); + + PROFILE_SCOPE( SceneGraph_batchRenderImages ); + + // In the editor, override the type mask for diffuse passes. + + if( gEditingMission && state->isDiffusePass() ) + objectMask = EDITOR_RENDER_TYPEMASK; + + // Update the zoning state and traverse zones. + + if( getZoneManager() ) + { + // Update. + + getZoneManager()->updateZoningState(); + + // If zone culling isn't disabled, traverse the + // zones now. + + if( !state->getCullingState().disableZoneCulling() ) + { + // Find the start zone if we haven't already. + + if( !baseObject ) + { + getZoneManager()->findZone( state->getCameraPosition(), baseObject, baseZone ); + AssertFatal( baseObject != NULL, "SceneManager::_renderScene - findZone() did not return an object" ); + } + + // Traverse zones starting in base object. + + SceneTraversalState traversalState( &state->getCullingState() ); + PROFILE_START( Scene_traverseZones ); + baseObject->traverseZones( &traversalState, baseZone ); + PROFILE_END(); + + // Set the scene render box to the area we have traversed. + + state->setRenderArea( traversalState.getTraversedArea() ); + } + } + + // Set the query box for the container query. Never + // make it larger than the frustum's AABB. In the editor, + // always query the full frustum as that gives objects + // the opportunity to render editor visualizations even if + // they are otherwise not in view. + + if( !state->getFrustum().getBounds().isOverlapped( state->getRenderArea() ) ) + { + // This handles fringe cases like flying backwards into a zone where you + // end up pretty much standing on a zone border and looking directly into + // its "walls". In that case the traversal area will be behind the frustum + // (remember that the camera isn't where visibility starts, it's the near + // distance). + + return; + } + + Box3F queryBox = state->getFrustum().getBounds(); + if( !gEditingMission ) + { + queryBox.minExtents.setMax( state->getRenderArea().minExtents ); + queryBox.maxExtents.setMin( state->getRenderArea().maxExtents ); + } + + PROFILE_START( Scene_cullObjects ); + + //TODO: We should split the codepaths here based on whether the outdoor zone has visible space. + // If it has, we should use the container query-based path. + // If it hasn't, we should fill the object list directly from the zone lists which will usually + // include way fewer objects. + + // Gather all objects that intersect the scene render box. + + mBatchQueryList.clear(); + getContainer()->findObjectList( queryBox, objectMask, &mBatchQueryList ); + + // Cull the list. + + U32 numRenderObjects = state->getCullingState().cullObjects( + mBatchQueryList.address(), + mBatchQueryList.size(), + !state->isDiffusePass() ? SceneCullingState::CullEditorOverrides : 0 // Keep forced editor stuff out of non-diffuse passes. + ); + + //HACK: If the control object is a Player and it is not in the render list, force + // it into it. This really should be solved by collision bounds being separate from + // object bounds; only because the Player class is using bounds not encompassing + // the actual player object is it that we have this problem in the first place. + // Note that we are forcing the player object into ALL passes here but such + // is the power of proliferation of things done wrong. + + GameConnection* connection = GameConnection::getConnectionToServer(); + if( connection ) + { + Player* player = dynamic_cast< Player* >( connection->getControlObject() ); + if( player ) + { + mBatchQueryList.setSize( numRenderObjects ); + if( !mBatchQueryList.contains( player ) ) + { + mBatchQueryList.push_back( player ); + numRenderObjects ++; + } + } + } + + PROFILE_END(); + + // Render the remaining objects. + + PROFILE_START( Scene_renderObjects ); + state->renderObjects( mBatchQueryList.address(), numRenderObjects ); + PROFILE_END(); + + // Render bounding boxes, if enabled. + + if( smRenderBoundingBoxes && state->isDiffusePass() ) + { + GFXDEBUGEVENT_SCOPE( Scene_renderBoundingBoxes, ColorI::WHITE ); + + GameBase* cameraObject = 0; + if( connection ) + cameraObject = connection->getCameraObject(); + + GFXStateBlockDesc desc; + desc.setFillModeWireframe(); + desc.setZReadWrite( true, false ); + + for( U32 i = 0; i < numRenderObjects; ++ i ) + { + SceneObject* object = mBatchQueryList[ i ]; + + // Skip global bounds object. + if( object->isGlobalBounds() ) + continue; + + // Skip camera object as we're viewing the scene from it. + if( object == cameraObject ) + continue; + + const Box3F& worldBox = object->getWorldBox(); + GFX->getDrawUtil()->drawObjectBox( + desc, + Point3F( worldBox.len_x(), worldBox.len_y(), worldBox.len_z() ), + worldBox.getCenter(), + MatrixF::Identity, + ColorI::WHITE + ); + } + } +} + +//----------------------------------------------------------------------------- + +struct ScopingInfo +{ + Point3F scopePoint; + F32 scopeDist; + F32 scopeDistSquared; + NetConnection* connection; +}; + +static void _scopeCallback( SceneObject* object, void* data ) +{ + if( !object->isScopeable() ) + return; + + ScopingInfo* info = reinterpret_cast< ScopingInfo* >( data ); + NetConnection* connection = info->connection; + + F32 difSq = ( object->getWorldSphere().center - info->scopePoint ).lenSquared(); + if( difSq < info->scopeDistSquared ) + { + // Not even close, it's in... + connection->objectInScope( object ); + } + else + { + // Check a little more closely... + F32 realDif = mSqrt( difSq ); + if( realDif - object->getWorldSphere().radius < info->scopeDist) + connection->objectInScope( object ); + } +} + +void SceneManager::scopeScene( CameraScopeQuery* query, NetConnection* netConnection ) +{ + PROFILE_SCOPE( SceneGraph_scopeScene ); + + // Note that this method does not use the zoning information in the scene + // to scope objects. The reason is that with the way that scoping is implemented + // in the networking layer--i.e. by killing off ghosts of objects that are out + // of scope--, it doesn't make sense to let, for example, all objects in the outdoor + // zone go out of scope, just because there is no exterior portal that is visible from + // the current camera viewpoint (in any direction). + // + // So, we perform a simple box query on the area covered by the camera query + // and then scope in everything that is in range. + + // Set up scoping info. + + ScopingInfo info; + + info.scopePoint = query->pos; + info.scopeDist = query->visibleDistance; + info.scopeDistSquared = info.scopeDist * info.scopeDist; + info.connection = netConnection; + + // Scope all objects in the query area. + + Box3F area( query->visibleDistance ); + area.setCenter( query->pos ); + + getContainer()->findObjects( area, 0xFFFFFFFF, _scopeCallback, &info ); +} + +//----------------------------------------------------------------------------- + +bool SceneManager::addObjectToScene( SceneObject* object ) +{ + AssertFatal( !object->mSceneManager, "SceneManager::addObjectToScene - Object already part of a scene" ); + + // Mark the object as belonging to us. + + object->mSceneManager = this; + + // Register with managers except its the root zone. + + if( !dynamic_cast< SceneRootZone* >( object ) ) + { + // Add to container. + + getContainer()->addObject( object ); + + // Register the object with the zone manager. + + if( getZoneManager() ) + getZoneManager()->registerObject( object ); + } + + // Notify the object. + + return object->onSceneAdd(); +} + +//----------------------------------------------------------------------------- + +void SceneManager::removeObjectFromScene( SceneObject* obj ) +{ + AssertFatal( obj->getSceneManager() == this, "SceneManager::removeObjectFromScene - Object not part of SceneManager" ); + + // Notify the object. + + obj->onSceneRemove(); + + // Remove the object from the container. + + getContainer()->removeObject( obj ); + + // Remove the object from the zoning system. + + if( getZoneManager() ) + getZoneManager()->unregisterObject( obj ); + + // Clear out the reference to us. + + obj->mSceneManager = NULL; +} + +//----------------------------------------------------------------------------- + +void SceneManager::notifyObjectDirty( SceneObject* object ) +{ + // Update container state. + + if( object->mContainer ) + object->mContainer->checkBins( object ); + + // Mark zoning state as dirty. + + if( getZoneManager() ) + getZoneManager()->notifyObjectChanged( object ); +} + +//----------------------------------------------------------------------------- + +void SceneManager::setDisplayTargetResolution( const Point2I &size ) +{ + mDisplayTargetResolution = size; +} + +//----------------------------------------------------------------------------- + +const Point2I & SceneManager::getDisplayTargetResolution() const +{ + return mDisplayTargetResolution; +} + +//----------------------------------------------------------------------------- + +bool SceneManager::setLightManager( const char* lmName ) +{ + LightManager *lm = LightManager::findByName( lmName ); + if ( !lm ) + return false; + + return _setLightManager( lm ); +} + +//----------------------------------------------------------------------------- + +bool SceneManager::_setLightManager( LightManager* lm ) +{ + // Avoid unnecessary work reinitializing materials. + if ( lm == mLightManager ) + return true; + + // Make sure its valid... else fail! + if ( !lm->isCompatible() ) + return false; + + // We only deactivate it... all light managers are singletons + // and will manager their own lifetime. + if ( mLightManager ) + mLightManager->deactivate(); + + mLightManager = lm; + + if ( mLightManager ) + mLightManager->activate( this ); + + return true; +} + +//----------------------------------------------------------------------------- + +RenderPassManager* SceneManager::getDefaultRenderPass() const +{ + if( !mDefaultRenderPass ) + { + Sim::findObject( "DiffuseRenderPassManager", mDefaultRenderPass ); + AssertISV( mDefaultRenderPass, "SceneManager::_setDefaultRenderPass - No DiffuseRenderPassManager defined! Must be set up in script!" ); + } + + return mDefaultRenderPass; +} + +//============================================================================= +// Console API. +//============================================================================= +// MARK: ---- Console API ---- + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( sceneDumpZoneStates, void, ( bool updateFirst ), ( true ), + "Dump the current zoning states of all zone spaces in the scene to the console.\n\n" + "@param updateFirst If true, zoning states are brought up to date first; if false, the zoning states " + "are dumped as is.\n\n" + "@note Only valid on the client.\n" + "@ingroup Game" ) +{ + if( !gClientSceneGraph ) + { + Con::errorf( "sceneDumpZoneStates - Only valid on client!" ); + return; + } + + SceneZoneSpaceManager* manager = gClientSceneGraph->getZoneManager(); + if( !manager ) + { + Con::errorf( "sceneDumpZoneStates - Scene is not using zones!" ); + return; + } + + manager->dumpZoneStates( updateFirst ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( sceneGetZoneOwner, SceneObject*, ( U32 zoneId ), ( true ), + "Return the SceneObject that contains the given zone.\n\n" + "@param zoneId ID of zone.\n" + "@return A SceneObject or NULL if the given @a zoneId is invalid.\n\n" + "@note Only valid on the client.\n" + "@ingroup Game" ) +{ + if( !gClientSceneGraph ) + { + Con::errorf( "sceneGetZoneOwner - Only valid on client!" ); + return NULL; + } + + SceneZoneSpaceManager* manager = gClientSceneGraph->getZoneManager(); + if( !manager ) + { + Con::errorf( "sceneGetZoneOwner - Scene is not using zones!" ); + return NULL; + } + + if( !manager->isValidZoneId( zoneId ) ) + { + Con::errorf( "sceneGetZoneOwner - Invalid zone ID: %i", zoneId ); + return NULL; + } + + return manager->getZoneOwner( zoneId ); +} diff --git a/Engine/source/scene/sceneManager.h b/Engine/source/scene/sceneManager.h new file mode 100644 index 000000000..deb43fe25 --- /dev/null +++ b/Engine/source/scene/sceneManager.h @@ -0,0 +1,354 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SCENEMANAGER_H_ +#define _SCENEMANAGER_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif + +#ifndef _SCENEZONESPACEMANAGER_H_ +#include "scene/zones/sceneZoneSpaceManager.h" +#endif + +#ifndef _MRECT_H_ +#include "math/mRect.h" +#endif + +#ifndef _COLOR_H_ +#include "core/color.h" +#endif + +#ifndef _INTERPOLATEDCHANGEPROPERTY_H_ +#include "util/interpolatedChangeProperty.h" +#endif + +#ifndef _GFXTEXTUREHANDLE_H_ +#include "gfx/gfxTextureHandle.h" +#endif + +#ifndef _FOGSTRUCTS_H_ +#include "scene/fogStructs.h" +#endif + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +#ifndef _TSIGNAL_H_ +#include "core/util/tSignal.h" +#endif + + +class LightManager; +class SceneRootZone; +class SceneRenderState; +class SceneCameraState; +class SceneZoneSpace; +class NetConnection; +class RenderPassManager; + + +/// The type of scene pass. +/// @see SceneManager +/// @see SceneRenderState +enum ScenePassType +{ + /// The regular diffuse scene pass. + SPT_Diffuse, + + /// The scene pass made for reflection rendering. + SPT_Reflect, + + /// The scene pass made for shadow map rendering. + SPT_Shadow, + + /// A scene pass that isn't one of the other + /// predefined scene pass types. + SPT_Other, +}; + + +/// An object that manages the SceneObjects belonging to a scene. +class SceneManager +{ + public: + + /// A signal used to notify of render passes. + typedef Signal< void( SceneManager*, const SceneRenderState* ) > RenderSignal; + + /// If true use the last stored locked frustum for culling + /// the diffuse render pass. + /// @see smLockedDiffuseFrustum + static bool smLockDiffuseFrustum; + + /// If true, render the AABBs of objects for debugging. + static bool smRenderBoundingBoxes; + + protected: + + /// Whether this is the client-side scene. + bool mIsClient; + + /// Manager for the zones in this scene. + SceneZoneSpaceManager* mZoneManager; + + // NonClipProjection is the projection matrix without oblique frustum clipping + // applied to it (in reflections) + MatrixF mNonClipProj; + + /// + bool mUsePostEffectFog; + + /// @see setDisplayTargetResolution + Point2I mDisplayTargetResolution; + + /// The currently active render state or NULL if we're + /// not in the process of rendering. + SceneRenderState* mCurrentRenderState; + + F32 mVisibleDistance; + + F32 mNearClip; + + FogData mFogData; + + WaterFogData mWaterFogData; + + /// The stored last diffuse pass frustum for locking the cull. + static SceneCameraState smLockedDiffuseCamera; + + /// @name Lighting + /// @{ + + typedef InterpolatedChangeProperty< ColorF > AmbientLightInterpolator; + + /// Light manager that is active for the scene. + LightManager* mLightManager; + + /// Global ambient light level in the scene. + AmbientLightInterpolator mAmbientLightColor; + + /// Deactivates the previous light manager and activates the new one. + bool _setLightManager( LightManager *lm ); + + /// @} + + /// @name Rendering + /// @{ + + /// RenderPassManager for the default render pass. This is set up + /// in script and looked up by getDefaultRenderPass(). + mutable RenderPassManager* mDefaultRenderPass; + + /// + Vector< SceneObject* > mBatchQueryList; + + /// Render scene using the given state. + /// + /// @param state SceneManager render state. + /// @param objectMask Object type mask with which to filter scene objects. + /// @param baseObject Zone manager to start traversal in. If null, the zone manager + /// that contains @a state's camera position will be used. + /// @param baseZone Zone in @a zone manager in which to start traversal. Ignored if + /// @a baseObject is NULL. + void _renderScene( SceneRenderState* state, + U32 objectMask = ( U32 ) -1, + SceneZoneSpace* baseObject = NULL, + U32 baseZone = 0 ); + + /// Callback for the container query. + static void _batchObjectCallback( SceneObject* object, void* key ); + + /// @} + + public: + + SceneManager( bool isClient ); + ~SceneManager(); + + /// Return the SceneContainer for this scene. + SceneContainer* getContainer() const { return mIsClient ? &gClientContainer : &gServerContainer; } + + /// Return the manager for the zones in this scene. + /// @note Only client scenes have a zone manager as for the server, no zoning data is kept. + const SceneZoneSpaceManager* getZoneManager() const { return mZoneManager; } + SceneZoneSpaceManager* getZoneManager() { return mZoneManager; } + + /// @name SceneObject Management + /// @{ + + /// Add the given object to the scene. + bool addObjectToScene( SceneObject* object ); + + /// Remove the given object from the scene. + void removeObjectFromScene( SceneObject* object ); + + /// Let the scene manager know that the given object has changed its transform or + /// sizing state. + void notifyObjectDirty( SceneObject* object ); + + /// @} + + /// @name Rendering + /// @{ + + /// Return the default RenderPassManager for the scene. + RenderPassManager* getDefaultRenderPass() const; + + /// Set the default render pass for the scene. + void setDefaultRenderPass( RenderPassManager* rpm ) { mDefaultRenderPass = rpm; } + + /// Render the scene with the default render pass. + /// @note This uses the current GFX state (transforms, viewport, frustum) to initialize + /// the render state. + void renderScene( ScenePassType passType, U32 objectMask = DEFAULT_RENDER_TYPEMASK ); + + /// Render the scene with a custom rendering pass. + void renderScene( SceneRenderState *state, U32 objectMask = DEFAULT_RENDER_TYPEMASK, SceneZoneSpace* baseObject = NULL, U32 baseZone = 0 ); + + /// Render the scene with a custom rendering pass and no lighting set up. + void renderSceneNoLights( SceneRenderState *state, U32 objectMask = DEFAULT_RENDER_TYPEMASK, SceneZoneSpace* baseObject = NULL, U32 baseZone = 0 ); + + /// Returns the currently active scene state or NULL if no state is currently active. + SceneRenderState* getCurrentRenderState() const { return mCurrentRenderState; } + + static RenderSignal& getPreRenderSignal() + { + static RenderSignal theSignal; + return theSignal; + } + + static RenderSignal& getPostRenderSignal() + { + static RenderSignal theSignal; + return theSignal; + } + + /// @} + + /// @name Lighting + /// @{ + + /// Finds the light manager by name and activates it. + bool setLightManager( const char *lmName ); + + /// Return the current global ambient light color. + const ColorF& getAmbientLightColor() const { return mAmbientLightColor.getCurrentValue(); } + + /// Set the time it takes for a new ambient light color to take full effect. + void setAmbientLightTransitionTime( SimTime time ) { mAmbientLightColor.setTransitionTime( time ); } + + /// Set the interpolation curve to use for blending from one global ambient light + /// color to a different one. + void setAmbientLightTransitionCurve( const EaseF& ease ) { mAmbientLightColor.setTransitionCurve( ease ); } + + /// @} + + /// @name Networking + /// @{ + + /// Set the scoping states of the objects in the scene. + void scopeScene( CameraScopeQuery* query, NetConnection* netConnection ); + + /// @} + + /// @name Fog/Visibility Management + /// @{ + + void setPostEffectFog( bool enable ) { mUsePostEffectFog = enable; } + bool usePostEffectFog() const { return mUsePostEffectFog; } + + /// Accessor for the FogData structure. + const FogData& getFogData() { return mFogData; } + + /// Sets the FogData structure. + void setFogData( const FogData &data ) { mFogData = data; } + + /// Accessor for the WaterFogData structure. + const WaterFogData& getWaterFogData() { return mWaterFogData; } + + /// Sets the WaterFogData structure. + void setWaterFogData( const WaterFogData &data ) { mWaterFogData = data; } + + /// Used by LevelInfo to set the default visible distance for + /// rendering the scene. + /// + /// Note this should not be used to alter culling which is + /// controlled by the active frustum when a SceneRenderState is created. + /// + /// @see SceneRenderState + /// @see GameProcessCameraQuery + /// @see LevelInfo + void setVisibleDistance( F32 dist ) { mVisibleDistance = dist; } + + /// Returns the default visible distance for the scene. + F32 getVisibleDistance() { return mVisibleDistance; } + + /// Used by LevelInfo to set the default near clip plane + /// for rendering the scene. + /// + /// @see GameProcessCameraQuery + /// @see LevelInfo + void setNearClip( F32 nearClip ) { mNearClip = nearClip; } + + /// Returns the default near clip distance for the scene. + F32 getNearClip() { return mNearClip; } + + /// @} + + /// @name dtr Display Target Resolution + /// + /// Some rendering must be targeted at a specific display resolution. + /// This display resolution is distinct from the current RT's size + /// (such as when rendering a reflection to a texture, for instance) + /// so we store the size at which we're going to display the results of + /// the current render. + /// + /// @{ + + /// + void setDisplayTargetResolution(const Point2I &size); + const Point2I &getDisplayTargetResolution() const; + + /// @} + + // NonClipProjection is the projection matrix without oblique frustum clipping + // applied to it (in reflections) + void setNonClipProjection( const MatrixF &proj ) { mNonClipProj = proj; } + const MatrixF& getNonClipProjection() const { return mNonClipProj; } +}; + +//----------------------------------------------------------------------------- + +//TODO: these two need to go + +/// The client-side scene graph. Not used if the engine is running +/// as a dedicated server. +extern SceneManager* gClientSceneGraph; + +/// The server-side scene graph. Not used if the engine is running +/// as a pure client. +extern SceneManager* gServerSceneGraph; + +#endif //_SCENEMANAGER_H_ diff --git a/Engine/source/scene/sceneObject.cpp b/Engine/source/scene/sceneObject.cpp new file mode 100644 index 000000000..9a6137a51 --- /dev/null +++ b/Engine/source/scene/sceneObject.cpp @@ -0,0 +1,1434 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "scene/sceneObject.h" + +#include "platform/profiler.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "console/simPersistID.h" +#include "sim/netConnection.h" +#include "core/stream/bitStream.h" +#include "scene/sceneManager.h" +#include "scene/sceneTracker.h" +#include "scene/sceneRenderState.h" +#include "scene/zones/sceneZoneSpace.h" +#include "collision/extrudedPolyList.h" +#include "collision/earlyOutPolyList.h" +#include "collision/optimizedPolyList.h" +#include "math/mPolyhedron.h" +#include "gfx/bitmap/gBitmap.h" +#include "math/util/frustum.h" +#include "math/mathIO.h" +#include "math/mTransform.h" +#include "T3D/gameBase/gameProcess.h" + +IMPLEMENT_CONOBJECT(SceneObject); + +ConsoleDocClass( SceneObject, + "@brief A networkable object that exists in the 3D world.\n\n" + + "The SceneObject class provides the foundation for 3D objects in the Engine. It " + "exposes the functionality for:\n\n" + + "
      • Position, rotation and scale within the world.
      • " + "
      • Working with a scene graph (in the Zone and Portal sections), allowing efficient " + "and robust rendering of the game scene.
      • " + "
      • Various helper functions, including functions to get bounding information " + "and momentum/velocity.
      • " + "
      • Mounting one SceneObject to another.
      • " + "
      • An interface for collision detection, as well as ray casting.
      • " + "
      • Lighting. SceneObjects can register lights both at lightmap generation " + "time, and dynamic lights at runtime (for special effects, such as from flame " + "or a projectile, or from an explosion).
      \n\n" + + "You do not typically work with SceneObjects themselves. The SceneObject provides a reference " + "within the game world (the scene), but does not render to the client on its own. The " + "same is true of collision detection beyond that of the bounding box. Instead you " + "use one of the many classes that derrive from SceneObject, such as TSStatic.\n\n" + + "@section SceneObject_Hiding Difference Between setHidden() and isRenderEnabled\n\n" + + "When it comes time to decide if a SceneObject should render or not, there are two " + "methods that can stop the SceneObject from rendering at all. You need to be aware of " + "the differences between these two methods as they impact how the SceneObject is networked " + "from the server to the client.\n\n" + + "The first method of manually controlling if a SceneObject is rendered is through its " + "SceneObject::isRenderEnabled property. When set to false the SceneObject is considered invisible but " + "still present within the scene. This means it still takes part in collisions and continues " + "to be networked.\n\n" + + "The second method is using the setHidden() method. This will actually remove a SceneObject " + "from the scene and it will no longer be networked from the server to the cleint. Any client-side " + "ghost of the object will be deleted as the server no longer considers the object to be in scope.\n\n" + + "@ingroup gameObjects\n" +); + + +Signal< void( SceneObject* ) > SceneObject::smSceneObjectAdd; +Signal< void( SceneObject* ) > SceneObject::smSceneObjectRemove; + + +//----------------------------------------------------------------------------- + +SceneObject::SceneObject() +{ + mContainer = 0; + mTypeMask = DefaultObjectType; + mCollisionCount = 0; + mGlobalBounds = false; + + mObjScale.set(1,1,1); + mObjToWorld.identity(); + mWorldToObj.identity(); + + mObjBox = Box3F(Point3F(0, 0, 0), Point3F(0, 0, 0)); + mWorldBox = Box3F(Point3F(0, 0, 0), Point3F(0, 0, 0)); + mWorldSphere = SphereF(Point3F(0, 0, 0), 0); + + mRenderObjToWorld.identity(); + mRenderWorldToObj.identity(); + mRenderWorldBox = Box3F(Point3F(0, 0, 0), Point3F(0, 0, 0)); + mRenderWorldSphere = SphereF(Point3F(0, 0, 0), 0); + + mContainerSeqKey = 0; + + mBinRefHead = NULL; + + mSceneManager = NULL; + + mNumCurrZones = 0; + mZoneRefHead = NULL; + mZoneRefDirty = false; + + mBinMinX = 0xFFFFFFFF; + mBinMaxX = 0xFFFFFFFF; + mBinMinY = 0xFFFFFFFF; + mBinMaxY = 0xFFFFFFFF; + mLightPlugin = NULL; + + mMount.object = NULL; + mMount.link = NULL; + mMount.list = NULL; + mMount.node = -1; + mMount.xfm = MatrixF::Identity; + mMountPID = NULL; + + mSceneObjectLinks = NULL; + + mObjectFlags.set( RenderEnabledFlag | SelectionEnabledFlag ); + mIsScopeAlways = false; +} + +//----------------------------------------------------------------------------- + +SceneObject::~SceneObject() +{ + AssertFatal( mZoneRefHead == NULL && mBinRefHead == NULL, + "SceneObject::~SceneObject - Object still linked in reference lists!"); + AssertFatal( !mSceneObjectLinks, + "SceneObject::~SceneObject() - object is still linked to SceneTrackers" ); + + unlink(); +} + +//----------------------------------------------------------------------------- + +bool SceneObject::castRayRendered(const Point3F &start, const Point3F &end, RayInfo *info) +{ + // By default, all ray checking against the rendered mesh will be passed + // on to the collision mesh. This saves having to define both methods + // for simple objects. + return castRay( start, end, info ); +} + +//----------------------------------------------------------------------------- + +bool SceneObject::containsPoint( const Point3F& point ) +{ + // If it's not in the AABB, then it can't be in the OBB either, + // so early out. + + if( !mWorldBox.isContained( point ) ) + return false; + + // Transform point into object space and test it against + // our object space bounding box. + + Point3F objPoint( 0, 0, 0 ); + getWorldTransform().mulP( point, &objPoint ); + objPoint.convolveInverse( getScale() ); + + return ( mObjBox.isContained( objPoint ) ); +} + +//----------------------------------------------------------------------------- + +bool SceneObject::collideBox(const Point3F &start, const Point3F &end, RayInfo *info) +{ + const F32 * pStart = (const F32*)start; + const F32 * pEnd = (const F32*)end; + const F32 * pMin = (const F32*)mObjBox.minExtents; + const F32 * pMax = (const F32*)mObjBox.maxExtents; + + F32 maxStartTime = -1; + F32 minEndTime = 1; + F32 startTime; + F32 endTime; + + // used for getting normal + U32 hitIndex = 0xFFFFFFFF; + U32 side; + + // walk the axis + for(U32 i = 0; i < 3; i++) + { + // + if(pStart[i] < pEnd[i]) + { + if(pEnd[i] < pMin[i] || pStart[i] > pMax[i]) + return(false); + + F32 dist = pEnd[i] - pStart[i]; + + startTime = (pStart[i] < pMin[i]) ? (pMin[i] - pStart[i]) / dist : -1; + endTime = (pEnd[i] > pMax[i]) ? (pMax[i] - pStart[i]) / dist : 1; + side = 1; + } + else + { + if(pStart[i] < pMin[i] || pEnd[i] > pMax[i]) + return(false); + + F32 dist = pStart[i] - pEnd[i]; + startTime = (pStart[i] > pMax[i]) ? (pStart[i] - pMax[i]) / dist : -1; + endTime = (pEnd[i] < pMin[i]) ? (pStart[i] - pMin[i]) / dist : 1; + side = 0; + } + + // + if(startTime > maxStartTime) + { + maxStartTime = startTime; + hitIndex = i * 2 + side; + } + if(endTime < minEndTime) + minEndTime = endTime; + if(minEndTime < maxStartTime) + return(false); + } + + // fail if inside + if(maxStartTime < 0.f) + return(false); + + // + static Point3F boxNormals[] = { + Point3F( 1, 0, 0), + Point3F(-1, 0, 0), + Point3F( 0, 1, 0), + Point3F( 0,-1, 0), + Point3F( 0, 0, 1), + Point3F( 0, 0,-1), + }; + + // + AssertFatal(hitIndex != 0xFFFFFFFF, "SceneObject::collideBox"); + info->t = maxStartTime; + info->object = this; + mObjToWorld.mulV(boxNormals[hitIndex], &info->normal); + info->material = 0; + return(true); +} + +//----------------------------------------------------------------------------- + +void SceneObject::disableCollision() +{ + mCollisionCount++; + AssertFatal(mCollisionCount < 50, "SceneObject::disableCollision called 50 times on the same object. Is this inside a circular loop?" ); +} + +//----------------------------------------------------------------------------- + +void SceneObject::enableCollision() +{ + if (mCollisionCount) + --mCollisionCount; +} + +//----------------------------------------------------------------------------- + +bool SceneObject::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + mIsScopeAlways = mNetFlags.test( ScopeAlways ); + + mWorldToObj = mObjToWorld; + mWorldToObj.affineInverse(); + resetWorldBox(); + + setRenderTransform(mObjToWorld); + + resolveMountPID(); + + smSceneObjectAdd.trigger(this); + + return true; +} + +//----------------------------------------------------------------------------- + +void SceneObject::onRemove() +{ + smSceneObjectRemove.trigger(this); + + unmount(); + plUnlink(); + + Parent::onRemove(); +} + +//----------------------------------------------------------------------------- + +void SceneObject::addToScene() +{ + if( mSceneManager ) + return; + + if( isClientObject() ) + gClientSceneGraph->addObjectToScene( this ); + else + gServerSceneGraph->addObjectToScene( this ); +} + +//----------------------------------------------------------------------------- + +void SceneObject::removeFromScene() +{ + if( !mSceneManager ) + return; + + mSceneManager->removeObjectFromScene( this ); +} + +//----------------------------------------------------------------------------- + +void SceneObject::onDeleteNotify( SimObject *obj ) +{ + // We are comparing memory addresses so even if obj really is not a + // ProcessObject this cast shouldn't break anything. + if ( obj == mAfterObject ) + mAfterObject = NULL; + + if ( obj == mMount.object ) + unmount(); + + Parent::onDeleteNotify( obj ); +} + +//----------------------------------------------------------------------------- + +void SceneObject::inspectPostApply() +{ + if( isServerObject() ) + setMaskBits( MountedMask ); + + Parent::inspectPostApply(); +} + +//----------------------------------------------------------------------------- + +void SceneObject::setGlobalBounds() +{ + mGlobalBounds = true; + mObjBox.minExtents.set( -1e10, -1e10, -1e10 ); + mObjBox.maxExtents.set( 1e10, 1e10, 1e10 ); + + if( mSceneManager ) + mSceneManager->notifyObjectDirty( this ); +} + +//----------------------------------------------------------------------------- + +void SceneObject::setTransform( const MatrixF& mat ) +{ + // This test is a bit expensive so turn it off in release. +#ifdef TORQUE_DEBUG + //AssertFatal( mat.isAffine(), "SceneObject::setTransform() - Bad transform (non affine)!" ); +#endif + + PROFILE_SCOPE( SceneObject_setTransform ); + + // Update the transforms. + + mObjToWorld = mWorldToObj = mat; + mWorldToObj.affineInverse(); + + // Update the world-space AABB. + + resetWorldBox(); + + // If we're in a SceneManager, sync our scene state. + + if( mSceneManager != NULL ) + mSceneManager->notifyObjectDirty( this ); + + setRenderTransform( mat ); +} + +//----------------------------------------------------------------------------- + +void SceneObject::setScale( const VectorF &scale ) +{ + AssertFatal( !mIsNaN( scale ), "SceneObject::setScale() - The scale is NaN!" ); + + // Avoid unnecessary scaling operations. + if ( mObjScale.equal( scale ) ) + return; + + mObjScale = scale; + setTransform(MatrixF(mObjToWorld)); + + // Make sure that any subclasses of me get a chance to react to the + // scale being changed. + onScaleChanged(); + + setMaskBits( ScaleMask ); +} + +//----------------------------------------------------------------------------- + +void SceneObject::resetWorldBox() +{ + AssertFatal(mObjBox.isValidBox(), "SceneObject::resetWorldBox - Bad object box!"); + + mWorldBox = mObjBox; + mWorldBox.minExtents.convolve(mObjScale); + mWorldBox.maxExtents.convolve(mObjScale); + mObjToWorld.mul(mWorldBox); + + AssertFatal(mWorldBox.isValidBox(), "SceneObject::resetWorldBox - Bad world box!"); + + // Create mWorldSphere from mWorldBox + mWorldBox.getCenter(&mWorldSphere.center); + mWorldSphere.radius = (mWorldBox.maxExtents - mWorldSphere.center).len(); + + // Update tracker links. + + for( SceneObjectLink* link = mSceneObjectLinks; link != NULL; + link = link->getNextLink() ) + link->update(); +} + +//----------------------------------------------------------------------------- + +void SceneObject::resetObjectBox() +{ + AssertFatal( mWorldBox.isValidBox(), "SceneObject::resetObjectBox - Bad world box!" ); + + mObjBox = mWorldBox; + mWorldToObj.mul( mObjBox ); + + Point3F objScale( mObjScale ); + objScale.setMax( Point3F( (F32)POINT_EPSILON, (F32)POINT_EPSILON, (F32)POINT_EPSILON ) ); + mObjBox.minExtents.convolveInverse( objScale ); + mObjBox.maxExtents.convolveInverse( objScale ); + + AssertFatal( mObjBox.isValidBox(), "SceneObject::resetObjectBox - Bad object box!" ); + + // Update the mWorldSphere from mWorldBox + mWorldBox.getCenter( &mWorldSphere.center ); + mWorldSphere.radius = ( mWorldBox.maxExtents - mWorldSphere.center ).len(); + + // Update scene managers. + + for( SceneObjectLink* link = mSceneObjectLinks; link != NULL; + link = link->getNextLink() ) + link->update(); +} + +//----------------------------------------------------------------------------- + +void SceneObject::setRenderTransform(const MatrixF& mat) +{ + PROFILE_START(SceneObj_setRenderTransform); + mRenderObjToWorld = mRenderWorldToObj = mat; + mRenderWorldToObj.affineInverse(); + + AssertFatal(mObjBox.isValidBox(), "Bad object box!"); + resetRenderWorldBox(); + PROFILE_END(); +} + +//----------------------------------------------------------------------------- + +void SceneObject::resetRenderWorldBox() +{ + AssertFatal( mObjBox.isValidBox(), "Bad object box!" ); + + mRenderWorldBox = mObjBox; + mRenderWorldBox.minExtents.convolve( mObjScale ); + mRenderWorldBox.maxExtents.convolve( mObjScale ); + mRenderObjToWorld.mul( mRenderWorldBox ); + + AssertFatal( mRenderWorldBox.isValidBox(), "Bad world box!" ); + + // Create mRenderWorldSphere from mRenderWorldBox. + + mRenderWorldBox.getCenter( &mRenderWorldSphere.center ); + mRenderWorldSphere.radius = ( mRenderWorldBox.maxExtents - mRenderWorldSphere.center ).len(); +} + +//----------------------------------------------------------------------------- + +void SceneObject::setHidden( bool hidden ) +{ + if( hidden != isHidden() ) + { + // Add/remove the object from the scene. Removing it + // will also cause the NetObject to go out of scope since + // the container query will not find it anymore. However, + // ScopeAlways objects need to be treated separately as we + // do next. + + if( !hidden ) + addToScene(); + else + removeFromScene(); + + // ScopeAlways objects stay in scope no matter what, i.e. even + // if they aren't in the scene query anymore. So, to force ghosts + // to go away, we need to clear ScopeAlways while we are hidden. + + if( hidden && mIsScopeAlways ) + clearScopeAlways(); + else if( !hidden && mIsScopeAlways ) + setScopeAlways(); + + Parent::setHidden( hidden ); + } +} + +//----------------------------------------------------------------------------- + +void SceneObject::initPersistFields() +{ + addGroup( "Transform" ); + + addProtectedField( "position", TypeMatrixPosition, Offset( mObjToWorld, SceneObject ), + &_setFieldPosition, &defaultProtectedGetFn, + "Object world position." ); + addProtectedField( "rotation", TypeMatrixRotation, Offset( mObjToWorld, SceneObject ), + &_setFieldRotation, &defaultProtectedGetFn, + "Object world orientation." ); + addProtectedField( "scale", TypePoint3F, Offset( mObjScale, SceneObject ), + &_setFieldScale, &defaultProtectedGetFn, + "Object world scale." ); + + endGroup( "Transform" ); + + addGroup( "Editing" ); + + addProtectedField( "isRenderEnabled", TypeBool, Offset( mObjectFlags, SceneObject ), + &_setRenderEnabled, &_getRenderEnabled, + "Controls client-side rendering of the object.\n" + "@see isRenderable()\n" ); + + addProtectedField( "isSelectionEnabled", TypeBool, Offset( mObjectFlags, SceneObject ), + &_setSelectionEnabled, &_getSelectionEnabled, + "Determines if the object may be selected from wihin the Tools.\n" + "@see isSelectable()\n" ); + + endGroup( "Editing" ); + + addGroup( "Mounting" ); + + addProtectedField( "mountPID", TypePID, Offset( mMountPID, SceneObject ), &_setMountPID, &defaultProtectedGetFn, + "@brief PersistentID of object we are mounted to.\n\n" + "Unlike the SimObjectID that is determined at run time, the PersistentID of an object is saved with the level/mission and " + "may be used to form a link between objects." ); + addField( "mountNode", TypeS32, Offset( mMount.node, SceneObject ), "Node we are mounted to." ); + addField( "mountPos", TypeMatrixPosition, Offset( mMount.xfm, SceneObject ), "Position we are mounted at ( object space of our mount object )." ); + addField( "mountRot", TypeMatrixRotation, Offset( mMount.xfm, SceneObject ), "Rotation we are mounted at ( object space of our mount object )." ); + + endGroup( "Mounting" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +bool SceneObject::_setFieldPosition( void *object, const char *index, const char *data ) +{ + SceneObject* so = static_cast( object ); + if ( so ) + { + MatrixF txfm( so->getTransform() ); + Con::setData( TypeMatrixPosition, &txfm, 0, 1, &data ); + so->setTransform( txfm ); + } + return false; +} + +//----------------------------------------------------------------------------- + +bool SceneObject::_setFieldRotation( void *object, const char *index, const char *data ) +{ + SceneObject* so = static_cast( object ); + if ( so ) + { + MatrixF txfm( so->getTransform() ); + Con::setData( TypeMatrixRotation, &txfm, 0, 1, &data ); + so->setTransform( txfm ); + } + return false; +} + +//----------------------------------------------------------------------------- + +bool SceneObject::_setFieldScale( void *object, const char *index, const char *data ) +{ + SceneObject* so = static_cast( object ); + if ( so ) + { + Point3F scale; + Con::setData( TypePoint3F, &scale, 0, 1, &data ); + so->setScale( scale ); + } + return false; +} + +//----------------------------------------------------------------------------- + +bool SceneObject::writeField( StringTableEntry fieldName, const char* value ) +{ + if( !Parent::writeField( fieldName, value ) ) + return false; + + static StringTableEntry sIsRenderEnabled = StringTable->insert( "isRenderEnabled" ); + static StringTableEntry sIsSelectionEnabled = StringTable->insert( "isSelectionEnabled" ); + static StringTableEntry sMountNode = StringTable->insert( "mountNode" ); + static StringTableEntry sMountPos = StringTable->insert( "mountPos" ); + static StringTableEntry sMountRot = StringTable->insert( "mountRot" ); + + // Don't write flag fields if they are at their default values. + + if( fieldName == sIsRenderEnabled && dAtob( value ) ) + return false; + else if( fieldName == sIsSelectionEnabled && dAtob( value ) ) + return false; + else if ( mMountPID == NULL && ( fieldName == sMountNode || + fieldName == sMountPos || + fieldName == sMountRot ) ) + { + return false; + } + + + return true; +} + +//----------------------------------------------------------------------------- + +static void scopeCallback( SceneObject* obj, void* conPtr ) +{ + NetConnection* ptr = reinterpret_cast< NetConnection* >( conPtr ); + if( obj->isScopeable() ) + ptr->objectInScope(obj); +} + +void SceneObject::onCameraScopeQuery( NetConnection* connection, CameraScopeQuery* query ) +{ + // Object itself is in scope. + + if( this->isScopeable() ) + connection->objectInScope( this ); + + // If we're mounted to something, that object is in scope too. + + if( isMounted() ) + connection->objectInScope( mMount.object ); + + // If we're added to a scene graph, let the graph do the scene scoping. + // Otherwise just put everything in the server container in scope. + + if( getSceneManager() ) + getSceneManager()->scopeScene( query, connection ); + else + gServerContainer.findObjects( 0xFFFFFFFF, scopeCallback, connection ); +} + +//----------------------------------------------------------------------------- + +bool SceneObject::isRenderEnabled() const +{ + AbstractClassRep *classRep = getClassRep(); + return ( mObjectFlags.test( RenderEnabledFlag ) && classRep->isRenderEnabled() ); +} + +//----------------------------------------------------------------------------- + +void SceneObject::setRenderEnabled( bool value ) +{ + if( value ) + mObjectFlags.set( RenderEnabledFlag ); + else + mObjectFlags.clear( RenderEnabledFlag ); + + setMaskBits( FlagMask ); +} + +//----------------------------------------------------------------------------- + +const char* SceneObject::_getRenderEnabled( void* object, const char* data ) +{ + SceneObject* obj = reinterpret_cast< SceneObject* >( object ); + if( obj->mObjectFlags.test( RenderEnabledFlag ) ) + return "true"; + else + return "false"; +} + +//----------------------------------------------------------------------------- + +bool SceneObject::_setRenderEnabled( void *object, const char *index, const char *data ) +{ + SceneObject* obj = reinterpret_cast< SceneObject* >( object ); + obj->setRenderEnabled( dAtob( data ) ); + return false; +} + +//----------------------------------------------------------------------------- + +bool SceneObject::isSelectionEnabled() const +{ + AbstractClassRep *classRep = getClassRep(); + return ( mObjectFlags.test( SelectionEnabledFlag ) && classRep->isSelectionEnabled() ); +} + +//----------------------------------------------------------------------------- + +void SceneObject::setSelectionEnabled( bool value ) +{ + if( value ) + mObjectFlags.set( SelectionEnabledFlag ); + else + mObjectFlags.clear( SelectionEnabledFlag ); + + // Not synchronized on network so don't set dirty bit. +} + +//----------------------------------------------------------------------------- + +const char* SceneObject::_getSelectionEnabled( void* object, const char* data ) +{ + SceneObject* obj = reinterpret_cast< SceneObject* >( object ); + if( obj->mObjectFlags.test( SelectionEnabledFlag ) ) + return "true"; + else + return "false"; +} + +//----------------------------------------------------------------------------- + +bool SceneObject::_setSelectionEnabled( void *object, const char *index, const char *data ) +{ + SceneObject* obj = reinterpret_cast< SceneObject* >( object ); + obj->setSelectionEnabled( dAtob( data ) ); + return false; +} + +//-------------------------------------------------------------------------- + +U32 SceneObject::packUpdate( NetConnection* conn, U32 mask, BitStream* stream ) +{ + U32 retMask = Parent::packUpdate( conn, mask, stream ); + + if ( stream->writeFlag( mask & FlagMask ) ) + stream->writeRangedU32( (U32)mObjectFlags, 0, getObjectFlagMax() ); + + if ( mask & MountedMask ) + { + if ( mMount.object ) + { + S32 gIndex = conn->getGhostIndex( mMount.object ); + + if ( stream->writeFlag( gIndex != -1 ) ) + { + stream->writeFlag( true ); + stream->writeInt( gIndex, NetConnection::GhostIdBitSize ); + if ( stream->writeFlag( mMount.node != -1 ) ) + stream->writeInt( mMount.node, NumMountPointBits ); + mathWrite( *stream, mMount.xfm ); + } + else + // Will have to try again later + retMask |= MountedMask; + } + else + // Unmount if this isn't the initial packet + if ( stream->writeFlag( !(mask & InitialUpdateMask) ) ) + stream->writeFlag( false ); + } + else + stream->writeFlag( false ); + + return retMask; +} + +//----------------------------------------------------------------------------- + +void SceneObject::unpackUpdate( NetConnection* conn, BitStream* stream ) +{ + Parent::unpackUpdate( conn, stream ); + + // FlagMask + if ( stream->readFlag() ) + mObjectFlags = stream->readRangedU32( 0, getObjectFlagMax() ); + + // MountedMask + if ( stream->readFlag() ) + { + if ( stream->readFlag() ) + { + S32 gIndex = stream->readInt( NetConnection::GhostIdBitSize ); + SceneObject* obj = dynamic_cast( conn->resolveGhost( gIndex ) ); + S32 node = -1; + if ( stream->readFlag() ) // node != -1 + node = stream->readInt( NumMountPointBits ); + MatrixF xfm; + mathRead( *stream, &xfm ); + if ( !obj ) + { + conn->setLastError( "Invalid packet from server." ); + return; + } + obj->mountObject( this, node, xfm ); + } + else + unmount(); + } +} + +//----------------------------------------------------------------------------- + +void SceneObject::_updateZoningState() const +{ + if( mZoneRefDirty ) + { + SceneZoneSpaceManager* manager = getSceneManager()->getZoneManager(); + if( manager ) + manager->updateObject( const_cast< SceneObject* >( this ) ); + else + mZoneRefDirty = false; + } +} + +//----------------------------------------------------------------------------- + +U32 SceneObject::_getCurrZone( const U32 index ) const +{ + _updateZoningState(); + + // Not the most efficient way to do this, walking the list, + // but it's an uncommon call... + ZoneRef* walk = mZoneRefHead; + for( U32 i = 0; i < index; ++ i ) + { + walk = walk->nextInObj; + AssertFatal( walk != NULL, "SceneObject::_getCurrZone - Too few object refs!" ); + } + AssertFatal( walk != NULL, "SceneObject::_getCurrZone - Too few object refs!" ); + + return walk->zone; +} + +//----------------------------------------------------------------------------- + +Point3F SceneObject::getPosition() const +{ + Point3F pos; + mObjToWorld.getColumn(3, &pos); + return pos; +} + +//----------------------------------------------------------------------------- + +Point3F SceneObject::getRenderPosition() const +{ + Point3F pos; + mRenderObjToWorld.getColumn(3, &pos); + return pos; +} + +//----------------------------------------------------------------------------- + +void SceneObject::setPosition(const Point3F &pos) +{ + AssertFatal( !mIsNaN( pos ), "SceneObject::setPosition() - The position is NaN!" ); + + MatrixF xform = mObjToWorld; + xform.setColumn(3, pos); + setTransform(xform); +} + +//----------------------------------------------------------------------------- + +F32 SceneObject::distanceTo(const Point3F &pnt) const +{ + return mWorldBox.getDistanceToPoint( pnt ); +} + +//----------------------------------------------------------------------------- + +void SceneObject::processAfter( ProcessObject *obj ) +{ + AssertFatal( dynamic_cast( obj ), "SceneObject::processAfter - Got non-SceneObject!" ); + + mAfterObject = (SceneObject*)obj; + if ( mAfterObject->mAfterObject == this ) + mAfterObject->mAfterObject = NULL; + + getProcessList()->markDirty(); +} + +//----------------------------------------------------------------------------- + +void SceneObject::clearProcessAfter() +{ + mAfterObject = NULL; +} + +//----------------------------------------------------------------------------- + +void SceneObject::setProcessTick( bool t ) +{ + if ( t == mProcessTick ) + return; + + if ( mProcessTick ) + { + plUnlink(); + mProcessTick = false; + } + else + { + // Just to be sure... + plUnlink(); + + getProcessList()->addObject( this ); + + mProcessTick = true; + } +} + +//----------------------------------------------------------------------------- + +ProcessList* SceneObject::getProcessList() const +{ + if ( isClientObject() ) + return ClientProcessList::get(); + else + return ServerProcessList::get(); +} + +//------------------------------------------------------------------------- + +bool SceneObject::isMounted() +{ + resolveMountPID(); + + return mMount.object != NULL; +} + +//----------------------------------------------------------------------------- + +S32 SceneObject::getMountedObjectCount() +{ + S32 count = 0; + for (SceneObject* itr = mMount.list; itr; itr = itr->mMount.link) + count++; + return count; +} + +//----------------------------------------------------------------------------- + +SceneObject* SceneObject::getMountedObject(S32 idx) +{ + if (idx >= 0) { + S32 count = 0; + for (SceneObject* itr = mMount.list; itr; itr = itr->mMount.link) + if (count++ == idx) + return itr; + } + return NULL; +} + +//----------------------------------------------------------------------------- + +S32 SceneObject::getMountedObjectNode(S32 idx) +{ + if (idx >= 0) { + S32 count = 0; + for (SceneObject* itr = mMount.list; itr; itr = itr->mMount.link) + if (count++ == idx) + return itr->mMount.node; + } + return -1; +} + +//----------------------------------------------------------------------------- + +SceneObject* SceneObject::getMountNodeObject(S32 node) +{ + for (SceneObject* itr = mMount.list; itr; itr = itr->mMount.link) + if (itr->mMount.node == node) + return itr; + return NULL; +} + +//----------------------------------------------------------------------------- + +bool SceneObject::_setMountPID( void* object, const char* index, const char* data ) +{ + SceneObject* so = static_cast( object ); + if ( so ) + { + // Unmount old object (PID reference is released even if it had been resolved yet) + if ( so->mMountPID ) + { + so->mMountPID->decRefCount(); + so->mMountPID = NULL; + } + so->unmount(); + + // Get the new PID (new object will be mounted on demand) + Con::setData( TypePID, &so->mMountPID, 0, 1, &data ); + if ( so->mMountPID ) + so->mMountPID->incRefCount(); // Prevent PID from being deleted out from under us! + } + return false; +} + +void SceneObject::resolveMountPID() +{ + if ( mMountPID && !mMount.object ) + { + SceneObject *obj = dynamic_cast< SceneObject* >( mMountPID->getObject() ); + if ( obj ) + obj->mountObject( this, mMount.node, mMount.xfm ); + } +} + +//----------------------------------------------------------------------------- + +void SceneObject::mountObject( SceneObject *obj, S32 node, const MatrixF &xfm ) +{ + if ( obj->mMount.object == this ) + { + // Already mounted to this + // So update our node and xfm which may have changed. + obj->mMount.node = node; + obj->mMount.xfm = xfm; + } + else + { + if ( obj->mMount.object ) + obj->unmount(); + + obj->mMount.object = this; + obj->mMount.node = node; + obj->mMount.link = mMount.list; + obj->mMount.xfm = xfm; + mMount.list = obj; + + // Assign PIDs to both objects + if ( isServerObject() ) + { + obj->getOrCreatePersistentId(); + if ( !obj->mMountPID ) + { + obj->mMountPID = getOrCreatePersistentId(); + obj->mMountPID->incRefCount(); + } + } + + obj->onMount( this, node ); + } +} + +//----------------------------------------------------------------------------- + +void SceneObject::unmountObject( SceneObject *obj ) +{ + if ( obj->mMount.object == this ) + { + // Find and unlink the object + for ( SceneObject **ptr = &mMount.list; *ptr; ptr = &(*ptr)->mMount.link ) + { + if ( *ptr == obj ) + { + *ptr = obj->mMount.link; + break; + } + } + + obj->mMount.object = NULL; + obj->mMount.link = NULL; + + if( obj->mMountPID != NULL ) // Only on server. + { + obj->mMountPID->decRefCount(); + obj->mMountPID = NULL; + } + + obj->onUnmount( this, obj->mMount.node ); + } +} + +//----------------------------------------------------------------------------- + +void SceneObject::unmount() +{ + if (mMount.object) + mMount.object->unmountObject(this); +} + +//----------------------------------------------------------------------------- + +void SceneObject::onMount( SceneObject *obj, S32 node ) +{ + deleteNotify( obj ); + + if ( !isGhost() ) + { + setMaskBits( MountedMask ); + //onMount_callback( node ); + } +} + +//----------------------------------------------------------------------------- + +void SceneObject::onUnmount( SceneObject *obj, S32 node ) +{ + clearNotify(obj); + + if ( !isGhost() ) + { + setMaskBits( MountedMask ); + //onUnmount_callback( node ); + } +} + +//----------------------------------------------------------------------------- + +void SceneObject::getMountTransform( S32 index, const MatrixF &xfm, MatrixF *outMat ) +{ + MatrixF mountTransform( xfm ); + const Point3F &scale = getScale(); + Point3F position = mountTransform.getPosition(); + position.convolve( scale ); + mountTransform.setPosition( position ); + + outMat->mul( mObjToWorld, mountTransform ); +} + +//----------------------------------------------------------------------------- + +void SceneObject::getRenderMountTransform( F32 delta, S32 index, const MatrixF &xfm, MatrixF *outMat ) +{ + MatrixF mountTransform( xfm ); + const Point3F &scale = getScale(); + Point3F position = mountTransform.getPosition(); + position.convolve( scale ); + mountTransform.setPosition( position ); + + outMat->mul( mRenderObjToWorld, mountTransform ); +} + +//============================================================================= +// Console API. +//============================================================================= +// MARK: ---- Console API ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SceneObject, getType, S32, (),, + "Return the type mask for this object.\n" + "@return The numeric type mask for the object." ) +{ + return object->getTypeMask(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SceneObject, mountObject, bool, + ( SceneObject* objB, S32 slot, TransformF txfm ), ( MatrixF::Identity ), + "@brief Mount objB to this object at the desired slot with optional transform.\n\n" + + "@param objB Object to mount onto us\n" + "@param slot Mount slot ID\n" + "@param txfm (optional) mount offset transform\n" + "@return true if successful, false if failed (objB is not valid)" ) +{ + if ( objB ) + { + object->mountObject( objB, slot, txfm.getMatrix() ); + return true; + } + return false; +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SceneObject, unmountObject, bool, ( SceneObject* target ),, + "@brief Unmount an object from ourselves.\n\n" + + "@param target object to unmount\n" + "@return true if successful, false if failed\n" ) +{ + if ( target ) + { + object->unmountObject(target); + return true; + } + return false; +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SceneObject, unmount, void, (),, + "Unmount us from the currently mounted object if any.\n" ) +{ + object->unmount(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SceneObject, isMounted, bool, (),, + "@brief Check if we are mounted to another object.\n\n" + "@return true if mounted to another object, false if not mounted." ) +{ + return object->isMounted(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SceneObject, getObjectMount, S32, (),, + "@brief Get the object we are mounted to.\n\n" + "@return the SimObjectID of the object we're mounted to, or 0 if not mounted." ) +{ + return object->isMounted()? object->getObjectMount()->getId(): 0; +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SceneObject, getMountedObjectCount, S32, (),, + "Get the number of objects mounted to us.\n" + "@return the number of mounted objects." ) +{ + return object->getMountedObjectCount(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SceneObject, getMountedObject, S32, ( S32 slot ),, + "Get the object mounted at a particular slot.\n" + "@param slot mount slot index to query\n" + "@return ID of the object mounted in the slot, or 0 if no object." ) +{ + SceneObject* mobj = object->getMountedObject( slot ); + return mobj? mobj->getId(): 0; +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SceneObject, getMountedObjectNode, S32, ( S32 slot ),, + "@brief Get the mount node index of the object mounted at our given slot.\n\n" + "@param slot mount slot index to query\n" + "@return index of the mount node used by the object mounted in this slot." ) +{ + return object->getMountedObjectNode( slot ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SceneObject, getMountNodeObject, S32, ( S32 node ),, + "@brief Get the object mounted at our given node index.\n\n" + "@param node mount node index to query\n" + "@return ID of the first object mounted at the node, or 0 if none found." ) +{ + SceneObject* mobj = object->getMountNodeObject( node ); + return mobj? mobj->getId(): 0; +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SceneObject, getTransform, TransformF, (),, + "Get the object's transform.\n" + "@return the current transform of the object\n" ) +{ + return object->getTransform(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SceneObject, getInverseTransform, TransformF, (),, + "Get the object's inverse transform.\n" + "@return the inverse transform of the object\n" ) +{ + return object->getWorldTransform(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SceneObject, getPosition, Point3F, (),, + "Get the object's world position.\n" + "@return the current world position of the object\n" ) +{ + return object->getTransform().getPosition(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SceneObject, getEulerRotation, Point3F, (),, + "Get Euler rotation of this object.\n" + "@return the orientation of the object in the form of rotations around the " + "X, Y and Z axes in degrees.\n" ) +{ + Point3F euler = object->getTransform().toEuler(); + + // Convert to degrees. + euler.x = mRadToDeg( euler.x ); + euler.y = mRadToDeg( euler.y ); + euler.z = mRadToDeg( euler.z ); + + return euler; +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SceneObject, getForwardVector, VectorF, (),, + "Get the direction this object is facing.\n" + "@return a vector indicating the direction this object is facing.\n" + "@note This is the object's y axis." ) +{ + return object->getTransform().getForwardVector(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SceneObject, getRightVector, VectorF, (),, + "Get the right vector of the object.\n" + "@return a vector indicating the right direction of this object." + "@note This is the object's x axis." ) +{ + return object->getTransform().getRightVector(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SceneObject, getUpVector, VectorF, (),, + "Get the up vector of the object.\n" + "@return a vector indicating the up direction of this object." + "@note This is the object's z axis." ) +{ + return object->getTransform().getUpVector(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SceneObject, setTransform, void, ( TransformF txfm ),, + "Set the object's transform (orientation and position)." + "@param txfm object transform to set" ) +{ + if ( !txfm.hasRotation() ) + object->setPosition( txfm.getPosition() ); + else + object->setTransform( txfm.getMatrix() ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SceneObject, getScale, Point3F, (),, + "Get the object's scale.\n" + "@return object scale as a Point3F" ) +{ + return object->getScale(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SceneObject, setScale, void, ( Point3F scale ),, + "Set the object's scale.\n" + "@param scale object scale to set\n" ) +{ + object->setScale( scale ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SceneObject, getWorldBox, Box3F, (),, + "Get the object's world bounding box.\n" + "@return six fields, two Point3Fs, containing the min and max points of the " + "worldbox." ) +{ + return object->getWorldBox(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SceneObject, getWorldBoxCenter, Point3F, (),, + "Get the center of the object's world bounding box.\n" + "@return the center of the world bounding box for this object." ) +{ + Point3F center; + object->getWorldBox().getCenter( ¢er ); + return center; +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SceneObject, getObjectBox, Box3F, (),, + "Get the object's bounding box (relative to the object's origin).\n" + "@return six fields, two Point3Fs, containing the min and max points of the " + "objectbox." ) +{ + return object->getObjBox(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SceneObject, isGlobalBounds, bool, (),, + "Check if this object has a global bounds set.\n" + "If global bounds are set to be true, then the object is assumed to have an " + "infinitely large bounding box for collision and rendering purposes.\n" + "@return true if the object has a global bounds." ) +{ + return object->isGlobalBounds(); +} diff --git a/Engine/source/scene/sceneObject.h b/Engine/source/scene/sceneObject.h new file mode 100644 index 000000000..cf682f1bf --- /dev/null +++ b/Engine/source/scene/sceneObject.h @@ -0,0 +1,773 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SCENEOBJECT_H_ +#define _SCENEOBJECT_H_ + +#ifndef _NETOBJECT_H_ +#include "sim/netObject.h" +#endif + +#ifndef _COLLISION_H_ +#include "collision/collision.h" +#endif + +#ifndef _OBJECTTYPES_H_ +#include "T3D/objectTypes.h" +#endif + +#ifndef _COLOR_H_ +#include "core/color.h" +#endif + +#ifndef _BITSET_H_ +#include "core/bitSet.h" +#endif + +#ifndef _PROCESSLIST_H_ +#include "T3D/gameBase/processList.h" +#endif + +#ifndef _SCENECONTAINER_H_ +#include "scene/sceneContainer.h" +#endif + + +class SceneManager; +class SceneRenderState; +class SceneTraversalState; +class SceneCameraState; +class SceneObjectLink; +class SceneObjectLightingPlugin; + +class Convex; +class LightInfo; +class SFXAmbience; + +struct ObjectRenderInst; +struct Move; + + +/// A 3D object. +/// +/// @section SceneObject_intro Introduction +/// +/// SceneObject exists as a foundation for 3D objects in Torque. It provides the +/// basic functionality for: +/// - A scene graph (in the Zones and Portals sections), allowing efficient +/// and robust rendering of the game scene. +/// - Various helper functions, including functions to get bounding information +/// and momentum/velocity. +/// - Collision detection, as well as ray casting. +/// - Lighting. SceneObjects can register lights both at lightmap generation time, +/// and dynamic lights at runtime (for special effects, such as from flame or +/// a projectile, or from an explosion). +/// - Manipulating scene objects, for instance varying scale. +/// +/// @section SceneObject_example An Example +/// +/// Melv May has written a most marvelous example object deriving from SceneObject. +/// Unfortunately this page is too small to contain it. +/// +/// @see http://www.garagegames.com/index.php?sec=mg&mod=resource&page=view&qid=3217 +/// for a copy of Melv's example. +class SceneObject : public NetObject, private SceneContainer::Link, public ProcessObject +{ + public: + + typedef NetObject Parent; + + friend class SceneManager; + friend class SceneContainer; + friend class SceneZoneSpaceManager; + friend class SceneCullingState; // _getZoneRefHead + friend class SceneObjectLink; // mSceneObjectLinks + + enum + { + /// Maximum number of zones that an object can concurrently be assigned to. + MaxObjectZones = 128, + + NumMountPoints = 32, + NumMountPointBits = 5, + }; + + /// Networking dirty mask. + enum SceneObjectMasks + { + InitialUpdateMask = BIT( 0 ), + ScaleMask = BIT( 1 ), + FlagMask = BIT( 2 ), + MountedMask = BIT( 3 ), + NextFreeMask = BIT( 4 ) + }; + + /// Bit-flags stored in mObjectFlags. + /// If a derived class adds more flags they must overload + /// getObjectFlagMax to ensure those flags will be transmitted over + /// the network. + /// @see getObjectFlagMax + enum SceneObjectFlags + { + /// If set, the object can be rendered. + /// @note The per-class render disable flag can override the per-object flag. + RenderEnabledFlag = BIT( 0 ), + + /// If set, the object can be selected in the editor. + /// @note The per-class selection disable flag can override the per-object flag. + SelectionEnabledFlag = BIT( 1 ), + + /// If set, object will not be subjected to culling when in the editor. + /// This is useful to bypass zone culling and always render certain editor-only + /// visual elements (like the zones themselves). + DisableCullingInEditorFlag = BIT( 2 ), + + /// If set, object will be used as a visual occluder. In this case, + /// the object should implement buildSilhouette() and return a + /// *convex* silhouette polygon. + VisualOccluderFlag = BIT( 3 ), + + /// If set, object will be used as a sound occluder. + SoundOccluderFlag = BIT( 4 ), + + NextFreeFlag = BIT( 5 ) + }; + + protected: + + /// Combination of SceneObjectFlags. + BitSet32 mObjectFlags; + + /// SceneManager to which this SceneObject belongs. + SceneManager* mSceneManager; + + /// Links installed by SceneTrackers attached to this object. + SceneObjectLink* mSceneObjectLinks; + + /// SceneObjectLightingPlugin attached to this object. + SceneObjectLightingPlugin* mLightPlugin; + + /// Object type mask. + /// @see SimObjectTypes + U32 mTypeMask; + + /// @name Mounting + /// @{ + + /// Mounted object. + struct MountInfo + { + SceneObject* list; ///< Linked-list of objects mounted on this object + SceneObject* object; ///< Object this object is mounted on. + SceneObject* link; ///< Link to next object mounted to this object's mount + S32 node; ///< Node point we are mounted to. + MatrixF xfm; + }; + + /// + MountInfo mMount; + + /// + SimPersistID* mMountPID; + + /// @} + + /// @name Zoning + /// @{ + + /// Bidirectional link between a zone manager and its objects. + struct ZoneRef : public SceneObjectRefBase< ZoneRef > + { + /// ID of zone. + U32 zone; + }; + + /// Iterator over the zones that the object is assigned to. + /// @note This iterator expects a clean zoning state. It will not update the + /// zoning state in case it is dirty. + struct ObjectZonesIterator + { + ObjectZonesIterator( SceneObject* object ) + : mCurrent( object->_getZoneRefHead() ) {} + + bool isValid() const + { + return ( mCurrent != NULL ); + } + ObjectZonesIterator& operator ++() + { + AssertFatal( isValid(), "SceneObject::ObjectZonesIterator::operator++ - Invalid iterator!" ); + mCurrent = mCurrent->nextInObj; + return *this; + } + U32 operator *() const + { + AssertFatal( isValid(), "SceneObject::ObjectZonesIterator::operator* - Invalid iterator!" ); + return mCurrent->zone; + } + + private: + ZoneRef* mCurrent; + }; + + friend struct ObjectZonesIterator; + + /// If an object moves, its zoning state needs to be updated. This is deferred + /// to when the state is actually needed and this flag indicates a refresh + /// is necessary. + mutable bool mZoneRefDirty; + + /// Number of zones this object is assigned to. + /// @note If #mZoneRefDirty is set, this might be outdated. + mutable U32 mNumCurrZones; + + /// List of zones that this object is part of. + /// @note If #mZoneRefDirty is set, this might be outdated. + mutable ZoneRef* mZoneRefHead; + + /// Refresh the zoning state of this object, if it isn't up-to-date anymore. + void _updateZoningState() const; + + /// Return the first link in the zone list of this object. Each link represents + /// a single zone that the object is assigned to. + /// + /// @note This method will return the zoning list as is. In case the zoning state + /// of the object is dirty, the list contents may be outdated. + ZoneRef* _getZoneRefHead() const { return mZoneRefHead; } + + /// Gets the number of zones containing this object. + U32 _getNumCurrZones() const { return mNumCurrZones; } + + /// Returns the nth zone containing this object. + U32 _getCurrZone( const U32 index ) const; + + /// @} + + /// @name Transform and Collision Members + /// @{ + + /// Transform from object space to world space. + MatrixF mObjToWorld; + + /// Transform from world space to object space (inverse). + MatrixF mWorldToObj; + + /// Object scale. + Point3F mObjScale; + + /// Bounding box in object space. + Box3F mObjBox; + + /// Bounding box (AABB) in world space. + Box3F mWorldBox; + + /// Bounding sphere in world space. + SphereF mWorldSphere; + + /// Render matrix to transform object space to world space. + MatrixF mRenderObjToWorld; + + /// Render matrix to transform world space to object space. + MatrixF mRenderWorldToObj; + + /// Render bounding box in world space. + Box3F mRenderWorldBox; + + /// Render bounding sphere in world space. + SphereF mRenderWorldSphere; + + /// Whether this object is considered to have an infinite bounding box. + bool mGlobalBounds; + + /// + S32 mCollisionCount; + + /// Regenerates the world-space bounding box and bounding sphere. + void resetWorldBox(); + + /// Regenerates the render-world-space bounding box and sphere. + void resetRenderWorldBox(); + + /// Regenerates the object-space bounding box from the world-space + /// bounding box, the world space to object space transform, and + /// the object scale. + void resetObjectBox(); + + /// Called when the size of the object changes. + virtual void onScaleChanged() {} + + /// @} + + /// Object which must be ticked before this object. + SimObjectPtr< SceneObject > mAfterObject; + + /// @name SceneContainer Interface + /// + /// When objects are searched, we go through all the zones and ask them for + /// all of their objects. Because an object can exist in multiple zones, the + /// container sequence key is set to the id of the current search. Then, while + /// searching, we check to see if an object's sequence key is the same as the + /// current search key. If it is, it will NOT be added to the list of returns + /// since it has already been processed. + /// + /// @{ + + /// Container database that the object is assigned to. + SceneContainer* mContainer; + + /// SceneContainer sequence key. + U32 mContainerSeqKey; + + /// + SceneObjectRef* mBinRefHead; + + U32 mBinMinX; + U32 mBinMaxX; + U32 mBinMinY; + U32 mBinMaxY; + + /// Returns the container sequence key. + U32 getContainerSeqKey() const { return mContainerSeqKey; } + + /// Sets the container sequence key. + void setContainerSeqKey( const U32 key ) { mContainerSeqKey = key; } + + /// @} + + /// Called when this is added to a SceneManager. + virtual bool onSceneAdd() { return true; } + + /// Called when this is removed from its current SceneManager. + virtual void onSceneRemove() {} + + /// Returns the greatest object flag bit defined. + /// Only bits within this range will be transmitted over the network. + virtual U32 getObjectFlagMax() const { return NextFreeFlag - 1; } + + public: + + SceneObject(); + virtual ~SceneObject(); + + /// Triggered when a SceneObject onAdd is called. + static Signal< void( SceneObject* ) > smSceneObjectAdd; + + /// Triggered when a SceneObject onRemove is called. + static Signal< void( SceneObject* ) > smSceneObjectRemove; + + /// Return the type mask that indicates to which broad object categories + /// this object belongs. + U32 getTypeMask() const { return mTypeMask; } + + /// @name SceneManager Functionality + /// @{ + + /// Return the SceneManager that this SceneObject belongs to. + SceneManager* getSceneManager() const { return mSceneManager; } + + /// Adds object to the client or server container depending on the object + void addToScene(); + + /// Removes the object from the client/server container + void removeFromScene(); + + /// Returns a pointer to the container that contains this object + SceneContainer* getContainer() { return mContainer; } + + /// @} + + /// @name Flags + /// @{ + + /// Return true if this object is rendered. + bool isRenderEnabled() const; + + /// Set whether the object gets rendered. + void setRenderEnabled( bool value ); + + /// Return true if this object can be selected in the editor. + bool isSelectionEnabled() const; + + /// Set whether the object can be selected in the editor. + void setSelectionEnabled( bool value ); + + /// Return true if the object doesn't want to be subjected to culling + /// when in the editor. + bool isCullingDisabledInEditor() const { return mObjectFlags.test( DisableCullingInEditorFlag ); } + + /// Return true if the object should be taken into account for visual occlusion. + bool isVisualOccluder() const { return mObjectFlags.test( VisualOccluderFlag ); } + + /// @} + + /// @name Collision and transform related interface + /// + /// The Render Transform is the interpolated transform with respect to the + /// frame rate. The Render Transform will differ from the object transform + /// because the simulation is updated in fixed intervals, which controls the + /// object transform. The framerate is, most likely, higher than this rate, + /// so that is why the render transform is interpolated and will differ slightly + /// from the object transform. + /// + /// @{ + + /// Disables collisions for this object including raycasts + virtual void disableCollision(); + + /// Enables collisions for this object + virtual void enableCollision(); + + /// Returns true if collisions are enabled + bool isCollisionEnabled() const { return mCollisionCount == 0; } + + /// This gets called when an object collides with this object. + /// @param object Object colliding with this object + /// @param vec Vector along which collision occurred + virtual void onCollision( SceneObject *object, const VectorF &vec ) {} + + /// Returns true if this object allows itself to be displaced + /// @see displaceObject + virtual bool isDisplacable() const { return false; } + + /// Returns the momentum of this object + virtual Point3F getMomentum() const { return Point3F( 0, 0, 0 ); } + + /// Sets the momentum of this object + /// @param momentum Momentum + virtual void setMomentum( const Point3F& momentum ) {} + + /// Returns the mass of this object + virtual F32 getMass() const { return 1.f; } + + /// Displaces this object by a vector + /// @param displaceVector Displacement vector + virtual bool displaceObject( const Point3F& displaceVector ) { return false; } + + /// Returns the transform which can be used to convert object space + /// to world space + virtual const MatrixF& getTransform() const { return mObjToWorld; } + + /// Returns the transform which can be used to convert world space + /// into object space + const MatrixF& getWorldTransform() const { return mWorldToObj; } + + /// Returns the scale of the object + const VectorF& getScale() const { return mObjScale; } + + /// Returns the bounding box for this object in local coordinates. + const Box3F& getObjBox() const { return mObjBox; } + + /// Returns the bounding box for this object in world coordinates. + const Box3F& getWorldBox() const { return mWorldBox; } + + /// Returns the bounding sphere for this object in world coordinates. + const SphereF& getWorldSphere() const { return mWorldSphere; } + + /// Returns the center of the bounding box in world coordinates + Point3F getBoxCenter() const { return ( mWorldBox.minExtents + mWorldBox.maxExtents ) * 0.5f; } + + /// Sets the Object -> World transform + /// + /// @param mat New transform matrix + virtual void setTransform( const MatrixF &mat ); + + /// Sets the scale for the object + /// @param scale Scaling values + virtual void setScale( const VectorF &scale ); + + /// This sets the render transform for this object + /// @param mat New render transform + virtual void setRenderTransform(const MatrixF &mat); + + /// Returns the render transform + const MatrixF& getRenderTransform() const { return mRenderObjToWorld; } + + /// Returns the render transform to convert world to local coordinates + const MatrixF& getRenderWorldTransform() const { return mRenderWorldToObj; } + + /// Returns the render world box + const Box3F& getRenderWorldBox() const { return mRenderWorldBox; } + + /// Sets the state of this object as hidden or not. If an object is hidden + /// it is removed entirely from collisions, it is not ghosted and is + /// essentially "non existant" as far as simulation is concerned. + /// @param hidden True if object is to be hidden + virtual void setHidden( bool hidden ); + + /// Builds a convex hull for this object. + /// + /// Think of a convex hull as a low-res mesh which covers, as tightly as + /// possible, the object mesh, and is used as a collision mesh. + /// @param box + /// @param convex Convex mesh generated (out) + virtual void buildConvex( const Box3F& box,Convex* convex ) {} + + /// Builds a list of polygons which intersect a bounding volume. + /// + /// This will use either the sphere or the box, not both, the + /// SceneObject implementation ignores sphere. + /// + /// @see AbstractPolyList + /// @param context A contentual hint as to the type of polylist to build. + /// @param polyList Poly list build (out) + /// @param box Box bounding volume + /// @param sphere Sphere bounding volume + /// + virtual bool buildPolyList( PolyListContext context, + AbstractPolyList* polyList, + const Box3F& box, + const SphereF& sphere ) { return false; } + + /// Casts a ray and obtain collision information, returns true if RayInfo is modified. + /// + /// @param start Start point of ray + /// @param end End point of ray + /// @param info Collision information obtained (out) + virtual bool castRay( const Point3F& start, const Point3F& end, RayInfo* info ) { return false; } + + /// Casts a ray against rendered geometry, returns true if RayInfo is modified. + /// + /// @param start Start point of ray + /// @param end End point of ray + /// @param info Collision information obtained (out) + virtual bool castRayRendered( const Point3F& start, const Point3F& end, RayInfo* info ); + + /// Build a world-space silhouette polygon for the object for the given camera settings. + /// This is used for occlusion. + /// + /// @param cameraState Camera view parameters. + /// @param outPoints Vector to store the resulting polygon points in. Leave untouched + /// if method is not implemented. + virtual void buildSilhouette( const SceneCameraState& cameraState, Vector< Point3F >& outPoints ) {} + + /// Return true if the given point is contained by the object's (collision) shape. + /// + /// The default implementation will return true if the point is within the object's + /// bounding box. Subclasses should implement more precise tests. + virtual bool containsPoint( const Point3F &point ); + + virtual bool collideBox( const Point3F& start, const Point3F& end, RayInfo* info ); + + /// Returns the position of the object. + virtual Point3F getPosition() const; + + /// Returns the render-position of the object. + /// + /// @see getRenderTransform + Point3F getRenderPosition() const; + + /// Sets the position of the object + void setPosition ( const Point3F& pos ); + + /// Gets the velocity of the object. + virtual Point3F getVelocity() const { return Point3F::Zero; } + + /// Sets the velocity of the object + /// @param v Velocity + virtual void setVelocity( const Point3F &v ) {} + + /// Applies an impulse force to this object + /// @param pos Position where impulse came from in world space + /// @param vec Velocity vector (Impulse force F = m * v) + virtual void applyImpulse( const Point3F &pos, const VectorF &vec ) {} + + /// Applies a radial impulse to the object + /// using the impulse origin and force. + /// @param origin Point of origin of the radial impulse. + /// @param radius The radius of the impulse area. + /// @param magnitude The strength of the impulse. + virtual void applyRadialImpulse( const Point3F &origin, F32 radius, F32 magnitude ) {} + + /// Returns the distance from this object to a point + /// @param pnt World space point to measure to + virtual F32 distanceTo( const Point3F &pnt ) const; + + /// @} + + /// @name Mounting + /// @{ + + /// ex: Mount B to A at A's node N + /// A.mountObject( B, N ) + /// + /// @param obj Object to mount + /// @param node Mount node ID + virtual void mountObject( SceneObject *obj, S32 node, const MatrixF &xfm = MatrixF::Identity ); + + /// Remove an object mounting + /// @param obj Object to unmount + virtual void unmountObject( SceneObject *obj ); + + /// Unmount this object from it's mount + virtual void unmount(); + + /// Callback when this object is mounted. + /// @param obj Object we are mounting to. + /// @param node Node we are unmounting from. + virtual void onMount( SceneObject *obj, S32 node ); + + /// Callback when this object is unmounted. This should be overridden to + /// set maskbits or do other object type specific work. + /// @param obj Object we are unmounting from. + /// @param node Node we are unmounting from. + virtual void onUnmount( SceneObject *obj, S32 node ); + + // Returns mount point to world space transform at tick time. + virtual void getMountTransform( S32 index, const MatrixF &xfm, MatrixF *outMat ); + + // Returns mount point to world space transform at render time. + // Note this will only be correct if called after this object has interpolated. + virtual void getRenderMountTransform( F32 delta, S32 index, const MatrixF &xfm, MatrixF *outMat ); + + /// Return the object that this object is mounted to. + virtual SceneObject* getObjectMount() { return mMount.object; } + + /// Return object link of next object mounted to this object's mount + virtual SceneObject* getMountLink() { return mMount.link; } + + /// Returns object list of objects mounted to this object. + virtual SceneObject* getMountList() { return mMount.list; } + + /// Returns the mount id that this is mounted to. + virtual U32 getMountNode() { return mMount.node; } + + /// Returns true if this object is mounted to anything at all + /// Also try to resolve the PID to objectId here if it is pending. + virtual bool isMounted(); + + /// Returns the number of object mounted along with this + virtual S32 getMountedObjectCount(); + + /// Returns the object mounted at a position in the mount list + /// @param idx Position on the mount list + virtual SceneObject* getMountedObject( S32 idx ); + + /// Returns the node the object at idx is mounted to + /// @param idx Index + virtual S32 getMountedObjectNode( S32 idx ); + + /// Returns the object a object on the mount list is mounted to + /// @param node + virtual SceneObject* getMountNodeObject( S32 node ); + + void resolveMountPID(); + + /// @} + + /// @name Sound + /// @{ + + /// Return whether the object's collision shape is blocking sound. + bool isOccludingSound() const { return mObjectFlags.test( SoundOccluderFlag ); } + + /// Return the ambient sound space active inside the volume of this object or NULL if the object does + /// not have its own ambient space. + virtual SFXAmbience* getSoundAmbience() const { return NULL; } + + /// @} + + /// @name Rendering + /// @{ + + /// Called when the SceneManager is ready for the registration of render instances. + /// @param state Rendering state. + virtual void prepRenderImage( SceneRenderState* state ) {} + + /// @} + + /// @name Lighting + /// @{ + + void setLightingPlugin( SceneObjectLightingPlugin* plugin ) { mLightPlugin = plugin; } + SceneObjectLightingPlugin* getLightingPlugin() { return mLightPlugin; } + + /// @} + + /// @name Global Bounds + /// @{ + + const bool isGlobalBounds() const + { + return mGlobalBounds; + } + + /// If global bounds are set to be true, then the object is assumed to + /// have an infinitely large bounding box for collision and rendering + /// purposes. + /// + /// They can't be toggled currently. + void setGlobalBounds(); + + /// @} + + /// Return the ProcessList for this object to use. + ProcessList* getProcessList() const; + + // ProcessObject, + virtual void processAfter( ProcessObject *obj ); + virtual void clearProcessAfter(); + virtual ProcessObject* getAfterObject() const { return mAfterObject; } + virtual void setProcessTick( bool t ); + + // NetObject. + virtual U32 packUpdate( NetConnection* conn, U32 mask, BitStream* stream ); + virtual void unpackUpdate( NetConnection* conn, BitStream* stream ); + virtual void onCameraScopeQuery( NetConnection* connection, CameraScopeQuery* query ); + + // SimObject. + virtual bool onAdd(); + virtual void onRemove(); + virtual void onDeleteNotify( SimObject *object ); + virtual void inspectPostApply(); + virtual bool writeField( StringTableEntry fieldName, const char* value ); + + static void initPersistFields(); + + DECLARE_CONOBJECT( SceneObject ); + + private: + + SceneObject( const SceneObject& ); ///< @deprecated disallowed + + /// For ScopeAlways objects to be able to properly implement setHidden(), they + /// need to temporarily give up ScopeAlways status while being hidden. Otherwise + /// the client-side ghost will not disappear as the server-side object will be + /// forced to stay in scope. + bool mIsScopeAlways; + + /// @name Protected field getters/setters + /// @{ + + static const char* _getRenderEnabled( void *object, const char *data ); + static bool _setRenderEnabled( void *object, const char *index, const char *data ); + static const char* _getSelectionEnabled( void *object, const char *data ); + static bool _setSelectionEnabled( void *object, const char *index, const char *data ); + static bool _setFieldPosition( void *object, const char *index, const char *data ); + static bool _setFieldRotation( void *object, const char *index, const char *data ); + static bool _setFieldScale( void *object, const char *index, const char *data ); + static bool _setMountPID( void* object, const char* index, const char* data ); + + /// @} +}; + +#endif // _SCENEOBJECT_H_ + diff --git a/Engine/source/scene/sceneObjectLightingPlugin.h b/Engine/source/scene/sceneObjectLightingPlugin.h new file mode 100644 index 000000000..177d2768c --- /dev/null +++ b/Engine/source/scene/sceneObjectLightingPlugin.h @@ -0,0 +1,44 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SCENEOBJECTLIGHTINGPLUGIN_H_ +#define _SCENEOBJECTLIGHTINGPLUGIN_H_ + +class SceneObject; +class NetConnection; +class BitStream; + +class SceneObjectLightingPlugin +{ + public: + + virtual ~SceneObjectLightingPlugin() {} + + /// Reset light plugin to clean state. + virtual void reset() {} + + // Called by statics + virtual U32 packUpdate( SceneObject* obj, U32 checkMask, NetConnection* conn, U32 mask, BitStream* stream ) = 0; + virtual void unpackUpdate( SceneObject* obj, NetConnection* conn, BitStream* stream ) = 0; +}; + +#endif // !_SCENEOBJECTLIGHTINGPLUGIN_H_ diff --git a/Engine/source/scene/scenePolyhedralSpace.cpp b/Engine/source/scene/scenePolyhedralSpace.cpp new file mode 100644 index 000000000..5690e0eec --- /dev/null +++ b/Engine/source/scene/scenePolyhedralSpace.cpp @@ -0,0 +1,26 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "scene/scenePolyhedralSpace.h" + +#include "scene/mixin/scenePolyhedralObject.impl.h" \ No newline at end of file diff --git a/Engine/source/scene/scenePolyhedralSpace.h b/Engine/source/scene/scenePolyhedralSpace.h new file mode 100644 index 000000000..bafa998fb --- /dev/null +++ b/Engine/source/scene/scenePolyhedralSpace.h @@ -0,0 +1,47 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SCENEPOLYHEDRALSPACE_H_ +#define _SCENEPOLYHEDRALSPACE_H_ + +#ifndef _SCENESPACE_H_ +#include "scene/sceneSpace.h" +#endif + +#ifndef _SCENEPOLYHEDRALOBJECT_H_ +#include "scene/mixin/scenePolyhedralObject.h" +#endif + + +/// +class ScenePolyhedralSpace : public ScenePolyhedralObject< SceneSpace > +{ + public: + + typedef ScenePolyhedralObject< SceneSpace > Parent; + + ScenePolyhedralSpace() {} + ScenePolyhedralSpace( const PolyhedronType& polyhedron ) + : Parent( polyhedron ) {} +}; + +#endif // !_SCENEPOLYHEDRALSPACE_H_ diff --git a/Engine/source/scene/sceneRenderState.cpp b/Engine/source/scene/sceneRenderState.cpp new file mode 100644 index 000000000..8da65407d --- /dev/null +++ b/Engine/source/scene/sceneRenderState.cpp @@ -0,0 +1,110 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "scene/sceneRenderState.h" + +#include "renderInstance/renderPassManager.h" +#include "math/util/matrixSet.h" + + + +//----------------------------------------------------------------------------- + +SceneRenderState::SceneRenderState( SceneManager* sceneManager, + ScenePassType passType, + const SceneCameraState& view, + RenderPassManager* renderPass /* = NULL */, + bool usePostEffects /* = true */ ) + : mSceneManager( sceneManager ), + mCullingState( sceneManager, view ), + mRenderPass( renderPass ? renderPass : sceneManager->getDefaultRenderPass() ), + mScenePassType( passType ), + mRenderNonLightmappedMeshes( true ), + mRenderLightmappedMeshes( true ), + mUsePostEffects( usePostEffects ), + mDisableAdvancedLightingBins( false ), + mRenderArea( view.getFrustum().getBounds() ), + mAmbientLightColor( sceneManager->getAmbientLightColor() ) +{ + // Setup the default parameters for the screen metrics methods. + mDiffuseCameraTransform = view.getViewWorldMatrix(); + + // The vector eye is the camera vector with its + // length normalized to 1 / zFar. + getCameraTransform().getColumn( 1, &mVectorEye ); + mVectorEye.normalize( 1.0f / getFarPlane() ); + + // TODO: What about ortho modes? Is near plane ok + // or do i need to remove it... maybe ortho has a near + // plane of 1 and it just works out? + + const Frustum& frustum = view.getFrustum(); + const RectI& viewport = view.getViewport(); + + mWorldToScreenScale.set( ( frustum.getNearDist() * viewport.extent.x ) / ( frustum.getNearRight() - frustum.getNearLeft() ), + ( frustum.getNearDist() * viewport.extent.y ) / ( frustum.getNearTop() - frustum.getNearBottom() ) ); + + // Assign shared matrix data to the render pass. + + mRenderPass->assignSharedXform( RenderPassManager::View, view.getWorldViewMatrix() ); + mRenderPass->assignSharedXform( RenderPassManager::Projection, view.getProjectionMatrix() ); +} + +//----------------------------------------------------------------------------- + +SceneRenderState::~SceneRenderState() +{ +} + +//----------------------------------------------------------------------------- + +const MatrixF& SceneRenderState::getWorldViewMatrix() const +{ + return getRenderPass()->getMatrixSet().getWorldToCamera(); +} + +//----------------------------------------------------------------------------- + +const MatrixF& SceneRenderState::getProjectionMatrix() const +{ + return getRenderPass()->getMatrixSet().getCameraToScreen(); +} + +//----------------------------------------------------------------------------- + +void SceneRenderState::renderObjects( SceneObject** objects, U32 numObjects ) +{ + // Let the objects batch their stuff. + + PROFILE_START( SceneRenderState_prepRenderImages ); + for( U32 i = 0; i < numObjects; ++ i ) + { + SceneObject* object = objects[ i ]; + object->prepRenderImage( this ); + } + PROFILE_END(); + + // Render what the objects have batched. + + getRenderPass()->renderPass( this ); +} diff --git a/Engine/source/scene/sceneRenderState.h b/Engine/source/scene/sceneRenderState.h new file mode 100644 index 000000000..e8503548c --- /dev/null +++ b/Engine/source/scene/sceneRenderState.h @@ -0,0 +1,315 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SCENERENDERSTATE_H_ +#define _SCENERENDERSTATE_H_ + +#ifndef _COLOR_H_ +#include "core/color.h" +#endif + +#ifndef _SCENEMANAGER_H_ +#include "scene/sceneManager.h" +#endif + +#ifndef _SCENECULLINGSTATE_H_ +#include "scene/culling/sceneCullingState.h" +#endif + +#ifndef _UTIL_DELEGATE_H_ +#include "core/util/delegate.h" +#endif + + +class SceneObject; +class RenderPassManager; +class BaseMatInstance; + + + +/// The SceneRenderState describes the state of the scene being rendered. +/// +/// It keeps track of the information that objects need to render properly with regard to +/// the camera position, any fog information, viewing frustum, the global environment +/// map for reflections, viewable distance, etc. +/// +/// It also owns the current culling state. +class SceneRenderState +{ + public: + + /// The delegate used for material overrides. + /// @see getOverrideMaterial + typedef Delegate< BaseMatInstance*( BaseMatInstance* ) > MatDelegate; + + protected: + + /// SceneManager being rendered in this state. + SceneManager* mSceneManager; + + /// The type of scene render pass we're doing. + ScenePassType mScenePassType; + + /// The render pass which we are setting up with this scene state. + RenderPassManager* mRenderPass; + + /// Culling state of the scene. + SceneCullingState mCullingState; + + /// The optional material override delegate. + MatDelegate mMatDelegate; + + /// + MatrixF mDiffuseCameraTransform; + + /// The world to screen space scalar used for LOD calculations. + Point2F mWorldToScreenScale; + + /// The AABB that encloses the space in the scene that we render. + Box3F mRenderArea; + + /// The camera vector normalized to 1 / far dist. + Point3F mVectorEye; + + /// Global ambient light color. + ColorF mAmbientLightColor; + + /// Forces bin based post effects to be disabled + /// during rendering with this scene state. + bool mUsePostEffects; + + /// Disables AdvancedLighting bin draws during rendering with this scene state. + bool mDisableAdvancedLightingBins; + + /// If true (default) lightmapped meshes should be rendered. + bool mRenderLightmappedMeshes; + + /// If true (default) non-lightmapped meshes should be rendered. + bool mRenderNonLightmappedMeshes; + + public: + + /// Construct a new SceneRenderState. + /// + /// @param sceneManager SceneManager rendered in this SceneRenderState. + /// @param passType Type of rendering pass that the SceneRenderState is for. + /// @param view The view that is being rendered + /// @param renderPass The render pass which is being set up by this SceneRenderState. If NULL, + /// then Scene::getDefaultRenderPass() is used. + /// @param usePostEffect Whether PostFX are enabled in the rendering pass. + SceneRenderState( SceneManager* sceneManager, + ScenePassType passType, + const SceneCameraState& view = SceneCameraState::fromGFX(), + RenderPassManager* renderPass = NULL, + bool usePostEffects = true ); + + ~SceneRenderState(); + + /// Return the SceneManager that is being rendered in this SceneRenderState. + SceneManager* getSceneManager() const { return mSceneManager; } + + /// If true then bin based post effects are disabled + /// during rendering with this scene state. + bool usePostEffects() const { return mUsePostEffects; } + void usePostEffects( bool value ) { mUsePostEffects = value; } + + /// @name Culling + /// @{ + + /// Return the culling state for the scene. + const SceneCullingState& getCullingState() const { return mCullingState; } + SceneCullingState& getCullingState() { return mCullingState; } + + /// Returns the root frustum. + const Frustum& getFrustum() const { return getCullingState().getFrustum(); } + + /// @} + + /// @name Rendering + /// @{ + + /// Get the AABB around the scene portion that we render. + const Box3F& getRenderArea() const { return mRenderArea; } + + /// Set the AABB of the space that should be rendered. + void setRenderArea( const Box3F& area ) { mRenderArea = area; } + + /// Batch the given objects to the render pass manager and then + /// render the batched instances. + /// + /// @param objects List of objects. + /// @param numObjects Number of objects in @a objects. + void renderObjects( SceneObject** objects, U32 numObjects ); + + /// @} + + /// @name Lighting + /// @{ + + /// Return the ambient light color to use for rendering the scene. + /// + /// At the moment, we only support a single global ambient color with which + /// all objects in the scene are rendered. This is because when using + /// Advanced Lighting, we are not resolving light contribution on a per-surface + /// or per-object basis but rather do it globally by gathering light + /// contribution to the whole scene and since the ambient factor is decided + /// by the sun/vector light, it simply becomes a base light level onto which + /// shadowing/lighting is blended based on the shadow maps of the sun/vector + /// light. + /// + /// @return The ambient light color for rendering. + ColorF getAmbientLightColor() const { return mAmbientLightColor; } + + /// Set the global ambient light color to render with. + void setAmbientLightColor( const ColorF& color ) { mAmbientLightColor = color; } + + /// If true then Advanced Lighting bin draws are disabled during rendering with + /// this scene state. + bool disableAdvancedLightingBins() const { return mDisableAdvancedLightingBins; } + void disableAdvancedLightingBins(bool enabled) { mDisableAdvancedLightingBins = enabled; } + + bool renderLightmappedMeshes() const { return mRenderLightmappedMeshes; } + void renderLightmappedMeshes( bool enabled ) { mRenderLightmappedMeshes = enabled; } + + bool renderNonLightmappedMeshes() const { return mRenderNonLightmappedMeshes; } + void renderNonLightmappedMeshes( bool enabled ) { mRenderNonLightmappedMeshes = enabled; } + + /// @} + + /// @name Passes + /// @{ + + /// Return the RenderPassManager that manages rendering objects batched + /// for this SceneRenderState. + RenderPassManager* getRenderPass() const { return mRenderPass; } + + /// Returns the type of scene rendering pass that we're doing. + ScenePassType getScenePassType() const { return mScenePassType; } + + /// Returns true if this is a diffuse scene rendering pass. + bool isDiffusePass() const { return mScenePassType == SPT_Diffuse; } + + /// Returns true if this is a reflection scene rendering pass. + bool isReflectPass() const { return mScenePassType == SPT_Reflect; } + + /// Returns true if this is a shadow scene rendering pass. + bool isShadowPass() const { return mScenePassType == SPT_Shadow; } + + /// Returns true if this is not one of the other rendering passes. + bool isOtherPass() const { return mScenePassType >= SPT_Other; } + + /// @} + + /// @name Transforms, projections, and viewports. + /// @{ + + /// Return the screen-space viewport rectangle. + const RectI& getViewport() const { return getCullingState().getCameraState().getViewport(); } + + /// Return the world->view transform matrix. + const MatrixF& getWorldViewMatrix() const; + + /// Return the project transform matrix. + const MatrixF& getProjectionMatrix() const; + + /// Returns the actual camera position. + /// @see getDiffuseCameraPosition + const Point3F& getCameraPosition() const { return getCullingState().getCameraState().getViewPosition(); } + + /// Returns the camera transform (view->world) this SceneRenderState is using. + const MatrixF& getCameraTransform() const { return getCullingState().getCameraState().getViewWorldMatrix(); } + + /// Returns the minimum distance something must be from the camera to not be culled. + F32 getNearPlane() const { return getFrustum().getNearDist(); } + + /// Returns the maximum distance something can be from the camera to not be culled. + F32 getFarPlane() const { return getFrustum().getFarDist(); } + + /// Returns the camera vector normalized to 1 / far distance. + const Point3F& getVectorEye() const { return mVectorEye; } + + /// Returns the possibly overloaded world to screen scale. + /// @see projectRadius + const Point2F& getWorldToScreenScale() const { return mWorldToScreenScale; } + + /// Set a new world to screen scale to overload + /// future screen metrics operations. + void setWorldToScreenScale( const Point2F& scale ) { mWorldToScreenScale = scale; } + + /// Returns the pixel size of the radius projected to the screen at a desired distance. + /// + /// Internally this uses the stored world to screen scale and viewport extents. This + /// allows the projection to be overloaded in special cases like when rendering shadows + /// or reflections. + /// + /// @see getWorldToScreenScale + /// @see getViewportExtent + F32 projectRadius( F32 dist, F32 radius ) const + { + // We fixup any negative or zero distance + // so we don't get a divide by zero. + dist = dist > 0.0f ? dist : 0.001f; + return ( radius / dist ) * mWorldToScreenScale.y; + } + + /// Returns the camera position used during the diffuse rendering pass which may be different + /// from the actual camera position. + /// + /// This is useful when doing level of detail calculations that need to be relative to the + /// diffuse pass. + /// + /// @see getCameraPosition + Point3F getDiffuseCameraPosition() const { return mDiffuseCameraTransform.getPosition(); } + const MatrixF& getDiffuseCameraTransform() const { return mDiffuseCameraTransform; } + + /// Set a new diffuse camera transform. + /// @see getDiffuseCameraTransform + void setDiffuseCameraTransform( const MatrixF &mat ) { mDiffuseCameraTransform = mat; } + + /// @} + + /// @name Material Overrides + /// @{ + + /// When performing a special render pass like shadows this + /// returns a specialized override material. It can return + /// NULL if the override wants to disable rendering. If + /// there is no override in place then the input material is + /// returned unaltered. + BaseMatInstance* getOverrideMaterial( BaseMatInstance* matInst ) const + { + if ( !matInst || mMatDelegate.empty() ) + return matInst; + + return mMatDelegate( matInst ); + } + + /// Returns the optional material override delegate which is + /// used during some special render passes. + /// @see getOverrideMaterial + MatDelegate& getMaterialDelegate() { return mMatDelegate; } + const MatDelegate& getMaterialDelegate() const { return mMatDelegate; } + + /// @} +}; + +#endif // _SCENERENDERSTATE_H_ diff --git a/Engine/source/scene/sceneSpace.cpp b/Engine/source/scene/sceneSpace.cpp new file mode 100644 index 000000000..5e8db41bd --- /dev/null +++ b/Engine/source/scene/sceneSpace.cpp @@ -0,0 +1,206 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "scene/sceneSpace.h" + +#include "core/stream/bitStream.h" +#include "console/engineAPI.h" +#include "math/mathIO.h" +#include "math/mOrientedBox.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxDrawUtil.h" +#include "gfx/gfxTransformSaver.h" +#include "renderInstance/renderPassManager.h" +#include "materials/materialDefinition.h" +#include "materials/baseMatInstance.h" +#include "scene/sceneRenderState.h" + + +extern bool gEditingMission; + + +//----------------------------------------------------------------------------- + +SceneSpace::SceneSpace() + : mEditorRenderMaterial( NULL ) +{ + mNetFlags.set( Ghostable | ScopeAlways ); + mTypeMask |= StaticObjectType; + + // Except when their rendering is otherwise suppressed, we do not want + // spaces to get culled away when in the editor. + mObjectFlags |= DisableCullingInEditorFlag; +} + +//----------------------------------------------------------------------------- + +SceneSpace::~SceneSpace() +{ + SAFE_DELETE( mEditorRenderMaterial ); +} + +//----------------------------------------------------------------------------- + +bool SceneSpace::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + addToScene(); + return true; +} + +//----------------------------------------------------------------------------- + +void SceneSpace::onRemove() +{ + removeFromScene(); + Parent::onRemove(); +} + +//----------------------------------------------------------------------------- + +void SceneSpace::setTransform(const MatrixF & mat) +{ + Parent::setTransform( mat ); + + if( isServerObject() ) + setMaskBits( TransformMask ); +} + +//----------------------------------------------------------------------------- + +void SceneSpace::onEditorEnable() +{ + // If we haven't created a material for editor rendering yet, + // try so now. + + if( isClientObject() && !mEditorRenderMaterial ) + mEditorRenderMaterial = _createEditorRenderMaterial(); +} + +//----------------------------------------------------------------------------- + +void SceneSpace::onEditorDisable() +{ + SAFE_DELETE( mEditorRenderMaterial ); +} + +//----------------------------------------------------------------------------- + +BaseMatInstance* SceneSpace::_createEditorRenderMaterial() +{ + String materialName = String::ToString( "Editor%sMaterial", getClassName() ); + + Material* material; + if( !Sim::findObject( materialName, material ) ) + return NULL; + + return material->createMatInstance(); +} + +//----------------------------------------------------------------------------- + +void SceneSpace::prepRenderImage( SceneRenderState* state ) +{ + if( !gEditingMission ) + return; + + if( !state->isDiffusePass() ) + return; + + ObjectRenderInst* ri = state->getRenderPass()->allocInst< ObjectRenderInst >(); + ri->renderDelegate.bind( this, &SceneSpace::_renderObject ); + ri->type = RenderPassManager::RIT_Editor; + ri->defaultKey = 0; + ri->defaultKey2 = 0; + state->getRenderPass()->addInst( ri ); +} + +//----------------------------------------------------------------------------- + +void SceneSpace::_renderObject( ObjectRenderInst* ri, SceneRenderState* state, BaseMatInstance* overrideMat ) +{ + if( overrideMat ) + return; + + if( !mEditorRenderMaterial ) + { + // We have no material for rendering so just render + // a plain box. + + GFXTransformSaver saver; + + MatrixF mat = getRenderTransform(); + mat.scale( getScale() ); + + GFX->multWorld( mat ); + + GFXStateBlockDesc desc; + desc.setZReadWrite( true, false ); + desc.setBlend( true ); + desc.setCullMode( GFXCullNone ); + + GFXDrawUtil *drawer = GFX->getDrawUtil(); + drawer->drawCube( desc, mObjBox, _getDefaultEditorSolidColor() ); + + // Render black wireframe. + + desc.setFillModeWireframe(); + drawer->drawCube( desc, mObjBox, _getDefaultEditorWireframeColor() ); + } + else + { + //RDTODO + } +} + +//----------------------------------------------------------------------------- + +U32 SceneSpace::packUpdate( NetConnection* connection, U32 mask, BitStream* stream ) +{ + U32 retMask = Parent::packUpdate( connection, mask, stream ); + + if( stream->writeFlag( mask & TransformMask ) ) + { + mathWrite( *stream, mObjToWorld ); + mathWrite( *stream, mObjScale ); + } + + return retMask; +} + +//----------------------------------------------------------------------------- + +void SceneSpace::unpackUpdate( NetConnection* connection, BitStream* stream ) +{ + Parent::unpackUpdate( connection, stream ); + + if( stream->readFlag() ) // TransformMask + { + mathRead( *stream, &mObjToWorld ); + mathRead( *stream, &mObjScale ); + + setTransform( mObjToWorld ); + } +} diff --git a/Engine/source/scene/sceneSpace.h b/Engine/source/scene/sceneSpace.h new file mode 100644 index 000000000..bff78d81f --- /dev/null +++ b/Engine/source/scene/sceneSpace.h @@ -0,0 +1,83 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SCENESPACE_H_ +#define _SCENESPACE_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif + + +/// Abstract base class for SceneObjects that define subspaces in a scene. +/// +/// Use SceneObject::containsPoint to find out whether a given space contains a particular point. +class SceneSpace : public SceneObject +{ + public: + + typedef SceneObject Parent; + + protected: + + enum + { + TransformMask = Parent::NextFreeMask << 0, ///< Object transform has changed. + NextFreeMask = Parent::NextFreeMask << 1, + }; + + /// + BaseMatInstance* mEditorRenderMaterial; + + /// + virtual BaseMatInstance* _createEditorRenderMaterial(); + + /// Render a visualization of the volume. + virtual void _renderObject( ObjectRenderInst* ri, SceneRenderState* state, BaseMatInstance* overrideMat ); + + /// + virtual ColorI _getDefaultEditorSolidColor() const { return ColorI( 255, 255, 255, 45 ); } + virtual ColorI _getDefaultEditorWireframeColor() const { return ColorI::BLACK; } + + public: + + SceneSpace(); + ~SceneSpace(); + + // SimObject. + virtual bool onAdd(); + virtual void onRemove(); + + // SceneObject. + virtual void setTransform( const MatrixF &mat ); + virtual void prepRenderImage( SceneRenderState* state ); + + // NetObject. + virtual U32 packUpdate( NetConnection* connection, U32 mask, BitStream* stream ); + virtual void unpackUpdate( NetConnection* connection, BitStream* stream ); + + // SimObject. + virtual void onEditorEnable(); + virtual void onEditorDisable(); +}; + +#endif // !_SCENESPACE_H_ diff --git a/Engine/source/scene/sceneTracker.cpp b/Engine/source/scene/sceneTracker.cpp new file mode 100644 index 000000000..51bf10211 --- /dev/null +++ b/Engine/source/scene/sceneTracker.cpp @@ -0,0 +1,128 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "scene/sceneTracker.h" + + +//============================================================================= +// SceneObjectLink. +//============================================================================= + +//----------------------------------------------------------------------------- + +SceneObjectLink::SceneObjectLink( SceneTracker* tracker, SceneObject* object ) + : mTracker( tracker ), + mObject( object ), + mNextLink( NULL ), + mPrevLink( NULL ) +{ + if( object ) + { + mNextLink = object->mSceneObjectLinks; + if( mNextLink ) + mNextLink->mPrevLink = this; + object->mSceneObjectLinks = this; + } +} + +//----------------------------------------------------------------------------- + +SceneObjectLink::~SceneObjectLink() +{ + if( mObject ) + { + // Unlink from SceneObject's tracker list. + + if( mNextLink ) + mNextLink->mPrevLink = mPrevLink; + if( mPrevLink ) + mPrevLink->mNextLink = mNextLink; + else + mObject->mSceneObjectLinks = mNextLink; + } +} + +//----------------------------------------------------------------------------- + +void SceneObjectLink::update() +{ + getTracker()->updateObject( this ); +} + +//----------------------------------------------------------------------------- + +SceneObjectLink* SceneObjectLink::getLinkForTracker( SceneTracker* tracker, SceneObject* fromObject ) +{ + for( SceneObjectLink* link = fromObject->mSceneObjectLinks; link != NULL; link = link->getNextLink() ) + if( link->getTracker() == tracker ) + return link; + + return NULL; +} + +//============================================================================= +// SceneObjectLink. +//============================================================================= + +//----------------------------------------------------------------------------- + +SceneTracker::SceneTracker( bool isClientTracker, U32 typeMask ) + : mIsClientTracker( isClientTracker ), + mObjectTypeMask( typeMask ) +{ + // Hook up to SceneObject add/remove notifications. + + SceneObject::smSceneObjectAdd.notify( this, &SceneTracker::registerObject ); + SceneObject::smSceneObjectRemove.notify( this, &SceneTracker::unregisterObject ); +} + +//----------------------------------------------------------------------------- + +SceneTracker::~SceneTracker() +{ + SceneObject::smSceneObjectAdd.remove( this, &SceneTracker::registerObject ); + SceneObject::smSceneObjectRemove.remove( this, &SceneTracker::unregisterObject ); +} + +//----------------------------------------------------------------------------- + +void SceneTracker::init() +{ + // Register existing scene graph objects. + + SceneContainer* container; + if( isClientTracker() ) + container = &gClientContainer; + else + container = &gServerContainer; + + container->findObjects( getObjectTypeMask(), + ( SceneContainer::FindCallback ) &_containerFindCallback, + this ); +} + +//----------------------------------------------------------------------------- + +void SceneTracker::_containerFindCallback( SceneObject* object, SceneTracker* tracker ) +{ + tracker->registerObject( object ); +} diff --git a/Engine/source/scene/sceneTracker.h b/Engine/source/scene/sceneTracker.h new file mode 100644 index 000000000..ea57d0daf --- /dev/null +++ b/Engine/source/scene/sceneTracker.h @@ -0,0 +1,163 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SCENETRACKER_H_ +#define _SCENETRACKER_H_ + +#ifndef _SCENEOBJECT_H_ + #include "scene/sceneObject.h" +#endif + + +/// @file +/// This file contains an abstract framework for tracking SceneObjects. + + +class SceneTracker; + + +//----------------------------------------------------------------------------- +// SceneObjectLink. +//----------------------------------------------------------------------------- + + +/// A SceneObjectLink represents the link between a SceneObject and a SceneTracker. +class SceneObjectLink +{ + public: + + typedef void Parent; + friend class SceneTracker; // Administers our link fields. + + protected: + + /// SceneObject being linked to; always set and never changes. + SceneObject* mObject; + + /// The scene tracker to which this link belongs. + SceneTracker* mTracker; + + /// Next scope link on this SceneObject; NULL if last. + SceneObjectLink* mNextLink; + + /// Previous scope link on this SceneObject; NULL if first. + SceneObjectLink* mPrevLink; + + public: + + /// + SceneObjectLink( SceneTracker* tracker, SceneObject* object ); + + virtual ~SceneObjectLink(); + + /// @return The SceneScopeTracker managing this link. + SceneTracker* getTracker() const { return mTracker; } + + /// @return The object being linked to. + SceneObject* getObject() const { return mObject; } + + /// @return The next link in this link chain. + SceneObjectLink* getNextLink() const { return mNextLink; } + + /// @return The previous link in this link chain. + SceneObjectLink* getPrevLink() const { return mPrevLink; } + + /// Notify the associated tracker that the transform state of the + /// scene object represented by this link has changed. + void update(); + + /// + static SceneObjectLink* getLinkForTracker( SceneTracker* tracker, SceneObject* fromObject ); +}; + + +//----------------------------------------------------------------------------- +// SceneTracker. +//----------------------------------------------------------------------------- + + +/// A SceneTracker tracks SceneObjects. +/// +/// This is an abstract base class. +class SceneTracker +{ + public: + + typedef void Parent; + friend class SceneObjectLink; // SceneObjectLink::update() notifies us on SceneObject state changes. + + protected: + + /// If true, only client SceneObjects will be tracked; otherwise it's only server SceneObjects. + bool mIsClientTracker; + + /// Type mask that SceneObjects must match in order to be allowed to register. + U32 mObjectTypeMask; + + /// Return true if the given object qualifies for being managed by this SceneTracker. + virtual bool _isTrackableObject( SceneObject* object ) const + { + return ( object->isClientObject() == mIsClientTracker + && ( object->getTypeMask() & getObjectTypeMask() ) ); + } + + /// Callback used for the initial scan of objects in init(). + static void _containerFindCallback( SceneObject* object, SceneTracker* tracker ); + + public: + + /// + SceneTracker( bool isClientTracker, U32 typeMask = 0xFFFFFFFF ); + + virtual ~SceneTracker(); + + /// Initialize the tracker from the current scene. + virtual void init(); + + /// @return The type mask that must be matched by objects in order to be allowed to register. + U32 getObjectTypeMask() const { return mObjectTypeMask; } + + /// Set the type mask that objects must match in order to be allowed to register. + void setObjectTypeMask( U32 typeMask ) { mObjectTypeMask = typeMask; } + + /// @return True if this tracker only deals with client objects; false if only server objects. + bool isClientTracker() const { return mIsClientTracker; } + + /// Register a SceneObject for being tracked by this tracker. + /// + /// Only objects that fit the tracker's client/server state and + /// object type mask will actually get registered. For other objects, + /// this is a NOP. + /// + /// @param object Scene object. + virtual void registerObject( SceneObject* object ) = 0; + + /// Unregister the given object from the tracker. + /// @param object Scene object. + virtual void unregisterObject( SceneObject* object ) = 0; + + /// Notify the tracker that the transform state of the given scene object has changed. + /// @param object Scene object. + virtual void updateObject( SceneObjectLink* object ) = 0; +}; + +#endif // !_SCENETRACKER_H_ diff --git a/Engine/source/scene/sgUtil.cpp b/Engine/source/scene/sgUtil.cpp new file mode 100644 index 000000000..39513bcc1 --- /dev/null +++ b/Engine/source/scene/sgUtil.cpp @@ -0,0 +1,327 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "scene/sgUtil.h" +#include "math/mRect.h" +#include "math/mMatrix.h" +#include "math/mPlane.h" + +namespace { + +// Static state for sgComputeNewFrustum +// +Point3F sgCamPoint; +MatrixF sgWSToOSMatrix; +MatrixF sgProjMatrix; +PlaneF sgOSPlaneFar; +PlaneF sgOSPlaneXMin; +PlaneF sgOSPlaneXMax; +PlaneF sgOSPlaneYMin; +PlaneF sgOSPlaneYMax; + + +void clipToPlane(Point3F* points, U32& rNumPoints, const PlaneF& rPlane) +{ + S32 start = -1; + for (U32 i = 0; i < rNumPoints; i++) { + if (rPlane.whichSide(points[i]) == PlaneF::Front) { + start = i; + break; + } + } + + // Nothing was in front of the plane... + if (start == -1) { + rNumPoints = 0; + return; + } + + Point3F finalPoints[128]; + U32 numFinalPoints = 0; + + U32 baseStart = start; + U32 end = (start + 1) % rNumPoints; + + while (end != baseStart) { + const Point3F& rStartPoint = points[start]; + const Point3F& rEndPoint = points[end]; + + PlaneF::Side fSide = rPlane.whichSide(rStartPoint); + PlaneF::Side eSide = rPlane.whichSide(rEndPoint); + + S32 code = fSide * 3 + eSide; + switch (code) { + case 4: // f f + case 3: // f o + case 1: // o f + case 0: // o o + // No Clipping required + finalPoints[numFinalPoints++] = points[start]; + start = end; + end = (end + 1) % rNumPoints; + break; + + + case 2: { // f b + // In this case, we emit the front point, Insert the intersection, + // and advancing to point to first point that is in front or on... + // + finalPoints[numFinalPoints++] = points[start]; + + Point3F vector = rEndPoint - rStartPoint; + F32 t = -(rPlane.distToPlane(rStartPoint) / mDot(rPlane, vector)); + + Point3F intersection = rStartPoint + (vector * t); + finalPoints[numFinalPoints++] = intersection; + + U32 endSeek = (end + 1) % rNumPoints; + while (rPlane.whichSide(points[endSeek]) == PlaneF::Back) + endSeek = (endSeek + 1) % rNumPoints; + + end = endSeek; + start = (end + (rNumPoints - 1)) % rNumPoints; + + const Point3F& rNewStartPoint = points[start]; + const Point3F& rNewEndPoint = points[end]; + + vector = rNewEndPoint - rNewStartPoint; + t = -(rPlane.distToPlane(rNewStartPoint) / mDot(rPlane, vector)); + + intersection = rNewStartPoint + (vector * t); + points[start] = intersection; + } + break; + + case -1: {// o b + // In this case, we emit the front point, and advance to point to first + // point that is in front or on... + // + finalPoints[numFinalPoints++] = points[start]; + + U32 endSeek = (end + 1) % rNumPoints; + while (rPlane.whichSide(points[endSeek]) == PlaneF::Back) + endSeek = (endSeek + 1) % rNumPoints; + + end = endSeek; + start = (end + (rNumPoints - 1)) % rNumPoints; + + const Point3F& rNewStartPoint = points[start]; + const Point3F& rNewEndPoint = points[end]; + + Point3F vector = rNewEndPoint - rNewStartPoint; + F32 t = -(rPlane.distToPlane(rNewStartPoint) / mDot(rPlane, vector)); + + Point3F intersection = rNewStartPoint + (vector * t); + points[start] = intersection; + } + break; + + case -2: // b f + case -3: // b o + case -4: // b b + // In the algorithm used here, this should never happen... + AssertISV(false, "SGUtil::clipToPlane: error in polygon clipper"); + break; + + default: + AssertFatal(false, "SGUtil::clipToPlane: bad outcode"); + break; + } + + } + + // Emit the last point. + finalPoints[numFinalPoints++] = points[start]; + AssertFatal(numFinalPoints >= 3, avar("Error, this shouldn't happen! Invalid winding in clipToPlane: %d", numFinalPoints)); + + // Copy the new rWinding, and we're set! + // + dMemcpy(points, finalPoints, numFinalPoints * sizeof(Point3F)); + rNumPoints = numFinalPoints; + AssertISV(rNumPoints <= 128, "MaxWindingPoints exceeded in scenegraph. Fatal error."); +} + + +void fixupViewport(const F64* oldFrustum, + const RectI& oldViewport, + F64* newFrustum, + RectI& newViewport) +{ + F64 widthV = newFrustum[1] - newFrustum[0]; + F64 heightV = newFrustum[3] - newFrustum[2]; + + F64 fx0 = (newFrustum[0] - oldFrustum[0]) / (oldFrustum[1] - oldFrustum[0]); + F64 fx1 = (oldFrustum[1] - newFrustum[1]) / (oldFrustum[1] - oldFrustum[0]); + + F64 dV0 = F64(oldViewport.point.x) + fx0 * F64(oldViewport.extent.x); + F64 dV1 = F64(oldViewport.point.x + + oldViewport.extent.x) - fx1 * F64(oldViewport.extent.x); + + F64 fdV0 = mFloor(dV0); + F64 cdV1 = mCeil(dV1); + + F64 new0 = newFrustum[0] - ((dV0 - fdV0) * (widthV / F64(oldViewport.extent.x))); + F64 new1 = newFrustum[1] + ((cdV1 - dV1) * (widthV / F64(oldViewport.extent.x))); + + newFrustum[0] = new0; + newFrustum[1] = new1; + + newViewport.point.x = S32(fdV0); + newViewport.extent.x = S32(cdV1) - newViewport.point.x; + + F64 fy0 = (oldFrustum[3] - newFrustum[3]) / (oldFrustum[3] - oldFrustum[2]); + F64 fy1 = (newFrustum[2] - oldFrustum[2]) / (oldFrustum[3] - oldFrustum[2]); + + dV0 = F64(oldViewport.point.y) + fy0 * F64(oldViewport.extent.y); + dV1 = F64(oldViewport.point.y + oldViewport.extent.y) - fy1 * F64(oldViewport.extent.y); + fdV0 = mFloor(dV0); + cdV1 = mCeil(dV1); + + new0 = newFrustum[2] - ((cdV1 - dV1) * (heightV / F64(oldViewport.extent.y))); + new1 = newFrustum[3] + ((dV0 - fdV0) * (heightV / F64(oldViewport.extent.y))); + newFrustum[2] = new0; + newFrustum[3] = new1; + + newViewport.point.y = S32(fdV0); + newViewport.extent.y = S32(cdV1) - newViewport.point.y; +} + +bool projectClipAndBoundWinding(const SGWinding& rWinding, F64* pResult) +{ + AssertFatal(rWinding.numPoints >= 3, "Error, that's not a winding!"); + + static Point3F windingPoints[128]; + U32 i; + for (i = 0; i < rWinding.numPoints; i++) + windingPoints[i] = rWinding.points[i]; + U32 numPoints = rWinding.numPoints; + + clipToPlane(windingPoints, numPoints, sgOSPlaneFar); + if (numPoints != 0) + clipToPlane(windingPoints, numPoints, sgOSPlaneXMin); + if (numPoints != 0) + clipToPlane(windingPoints, numPoints, sgOSPlaneXMax); + if (numPoints != 0) + clipToPlane(windingPoints, numPoints, sgOSPlaneYMin); + if (numPoints != 0) + clipToPlane(windingPoints, numPoints, sgOSPlaneYMax); + + if (numPoints == 0) + return false; + + Point4F projPoint; + for (i = 0; i < numPoints; i++) { + projPoint.set(windingPoints[i].x, windingPoints[i].y, windingPoints[i].z, 1.0); + sgProjMatrix.mul(projPoint); + + AssertFatal(projPoint.w != 0.0, "Error, that's bad! (Point projected with non-zero w.)"); + projPoint.x /= projPoint.w; + projPoint.y /= projPoint.w; + + if (projPoint.x < pResult[0]) + pResult[0] = projPoint.x; + if (projPoint.x > pResult[1]) + pResult[1] = projPoint.x; + if (projPoint.y < pResult[2]) + pResult[2] = projPoint.y; + if (projPoint.y > pResult[3]) + pResult[3] = projPoint.y; + } + + if (pResult[0] < -1.0f) pResult[0] = -1.0f; + if (pResult[2] < -1.0f) pResult[2] = -1.0f; + if (pResult[1] > 1.0f) pResult[1] = 1.0f; + if (pResult[3] > 1.0f) pResult[3] = 1.0f; + + return true; +} + +} // namespace { } + + +//-------------------------------------------------------------------------- +bool sgComputeNewFrustum(const Frustum &oldFrustum, + const F64 nearPlane, + const F64 farPlane, + const RectI& oldViewport, + const SGWinding* windings, + const U32 numWindings, + const MatrixF& modelview, + F64 *newFrustum, + RectI& newViewport, + const bool flippedMatrix) +{ + return false; +} + + +void sgComputeOSFrustumPlanes(const F64 frustumParameters[6], + const MatrixF& worldSpaceToObjectSpace, + const Point3F& wsCamPoint, + PlaneF& outFarPlane, + PlaneF& outXMinPlane, + PlaneF& outXMaxPlane, + PlaneF& outYMinPlane, + PlaneF& outYMaxPlane) +{ + // Create the object space clipping planes... + Point3F ul(frustumParameters[0] * 1000.0, frustumParameters[4] * 1000.0, frustumParameters[3] * 1000.0); + Point3F ur(frustumParameters[1] * 1000.0, frustumParameters[4] * 1000.0, frustumParameters[3] * 1000.0); + Point3F ll(frustumParameters[0] * 1000.0, frustumParameters[4] * 1000.0, frustumParameters[2] * 1000.0); + Point3F lr(frustumParameters[1] * 1000.0, frustumParameters[4] * 1000.0, frustumParameters[2] * 1000.0); + Point3F farPlane(0, frustumParameters[5], 0); + + worldSpaceToObjectSpace.mulP(ul); + worldSpaceToObjectSpace.mulP(ur); + worldSpaceToObjectSpace.mulP(ll); + worldSpaceToObjectSpace.mulP(lr); + worldSpaceToObjectSpace.mulP(farPlane); + + outFarPlane.set(farPlane, wsCamPoint - farPlane); + outXMinPlane.set(wsCamPoint, ul, ll); + outXMaxPlane.set(wsCamPoint, lr, ur); + outYMinPlane.set(wsCamPoint, ur, ul); + outYMaxPlane.set(wsCamPoint, ll, lr); +} + +// MM/JF: Added for mirrorSubObject fix. +void sgOrientClipPlanes( + PlaneF * planes, + const Point3F & camPos, + const Point3F & leftUp, + const Point3F & leftDown, + const Point3F & rightUp, + const Point3F & rightDown) +{ + AssertFatal(planes, "orientClipPlanes: NULL planes ptr"); + planes[0].set(camPos, leftUp, leftDown); + planes[1].set(camPos, rightUp, leftUp); + planes[2].set(camPos, rightDown, rightUp); + planes[3].set(camPos, leftDown, rightDown); + planes[4].set(leftUp, rightUp, rightDown); + + // clip-planes through mirror portal are inverted + PlaneF plane(leftUp, rightUp, rightDown); + if(plane.whichSide(camPos) == PlaneF::Back) + for(U32 i = 0; i < 5; i++) + planes[i].invert(); +} diff --git a/Engine/source/scene/sgUtil.h b/Engine/source/scene/sgUtil.h new file mode 100644 index 000000000..be83c7c57 --- /dev/null +++ b/Engine/source/scene/sgUtil.h @@ -0,0 +1,75 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SGUTIL_H_ +#define _SGUTIL_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif + +class Frustum; +class RectI; +class MatrixF; +class PlaneF; + +struct SGWinding +{ + Point3F points[32]; + U32 numPoints; +}; + +bool sgComputeNewFrustum(const Frustum &oldFrustum, + const F64 nearPlane, + const F64 farPlane, + const RectI& oldViewport, + const SGWinding* windings, + const U32 numWindings, + const MatrixF& modelview, + F64 *newFrustum, + RectI& newViewport, + const bool flippedMatrix); + +/// Compute frustrum planes. +/// +/// Frustum parameters are: +/// - [0] = left +/// - [1] = right +/// - [2] = top +/// - [3] = bottom +/// - [4] = near +/// - [5] = far +void sgComputeOSFrustumPlanes(const F64 frustumParameters[6], + const MatrixF& worldSpaceToObjectSpace, + const Point3F& wsCamPoint, + PlaneF& outFarPlane, + PlaneF& outXMinPlane, + PlaneF& outXMaxPlane, + PlaneF& outYMinPlane, + PlaneF& outYMaxPlane); + +void sgOrientClipPlanes(PlaneF * planes, const Point3F & camPos, const Point3F & leftUp, const Point3F & leftDown, const Point3F & rightUp, const Point3F & rightDown); + +#endif // _H_SGUTIL_ diff --git a/Engine/source/scene/simPath.cpp b/Engine/source/scene/simPath.cpp new file mode 100644 index 000000000..1ab5677ec --- /dev/null +++ b/Engine/source/scene/simPath.cpp @@ -0,0 +1,526 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "scene/simPath.h" + +#include "gfx/gfxDevice.h" +#include "gfx/gfxVertexBuffer.h" +#include "gfx/gfxPrimitiveBuffer.h" +#include "gfx/gfxTransformSaver.h" +#include "console/consoleTypes.h" +#include "scene/pathManager.h" +#include "scene/sceneRenderState.h" +#include "math/mathIO.h" +#include "core/stream/bitStream.h" +#include "renderInstance/renderPassManager.h" +#include "console/engineAPI.h" + +extern bool gEditingMission; + +//-------------------------------------------------------------------------- +//-------------------------------------- Console functions and cmp funcs +// +DefineEngineFunction(pathOnMissionLoadDone, void, (),, + "@brief Load all Path information from the mission.\n\n" + + "This function is usually called from the loadMissionStage2() server-side function " + "after the mission file has loaded. Internally it places all Paths into the server's " + "PathManager. From this point the Paths are ready for transmission to the clients.\n\n" + + "@tsexample\n" + "// Inform the engine to load all Path information from the mission.\n" + "pathOnMissionLoadDone();\n\n" + "@endtsexample\n" + + "@see NetConnection::transmitPaths()\n" + "@see NetConnection::clearPaths()\n" + "@see Path\n" + + "@ingroup Networking") +{ + // Need to load subobjects for all loaded interiors... + SimGroup* pMissionGroup = dynamic_cast(Sim::findObject("MissionGroup")); + AssertFatal(pMissionGroup != NULL, "Error, mission done loading and no mission group?"); + + U32 currStart = 0; + U32 currEnd = 1; + Vector groups; + groups.push_back(pMissionGroup); + + while (true) { + for (U32 i = currStart; i < currEnd; i++) { + for (SimGroup::iterator itr = groups[i]->begin(); itr != groups[i]->end(); itr++) { + if (dynamic_cast(*itr) != NULL) + groups.push_back(static_cast(*itr)); + } + } + + if (groups.size() == currEnd) { + break; + } else { + currStart = currEnd; + currEnd = groups.size(); + } + } + + for (U32 i = 0; i < groups.size(); i++) { + SimPath::Path* pPath = dynamic_cast(groups[i]); + if (pPath) + pPath->updatePath(); + } +} + +S32 FN_CDECL cmpPathObject(const void* p1, const void* p2) +{ + SimObject* o1 = *((SimObject**)p1); + SimObject* o2 = *((SimObject**)p2); + + Marker* m1 = dynamic_cast(o1); + Marker* m2 = dynamic_cast(o2); + + if (m1 == NULL && m2 == NULL) + return 0; + else if (m1 != NULL && m2 == NULL) + return 1; + else if (m1 == NULL && m2 != NULL) + return -1; + else { + // Both markers... + return S32(m1->mSeqNum) - S32(m2->mSeqNum); + } +} + +ConsoleDocClass(SimPath::Path, + "@brief A spline along which various objects can move along. The spline object acts like a container for Marker objects, which make\n" + "up the joints, or knots, along the path. Paths can be assigned a speed, can be looping or non-looping. Each of a path's markers can be\n" + "one of three primary movement types: \"normal\", \"Position Only\", or \"Kink\". \n" + + "@tsexample\n" + "new path()\n" + " {\n" + " isLooping = \"1\";\n" + "\n" + " new Marker()\n" + " {\n" + " seqNum = \"0\";\n" + " type = \"Normal\";\n" + " msToNext = \"1000\";\n" + " smoothingType = \"Spline\";\n" + " position = \"-0.054708 -35.0612 234.802\";\n" + " rotation = \"1 0 0 0\";\n" + " };\n" + "\n" + " };\n" + "@endtsexample\n" + + "@see Marker\n" + "@see NetConnection::transmitPaths()\n" + "@see NetConnection::clearPaths()\n" + "@see Path\n" + + "@ingroup enviroMisc\n" +); + +namespace SimPath +{ + +//-------------------------------------------------------------------------- +//-------------------------------------- Implementation +// +IMPLEMENT_CONOBJECT(Path); + +Path::Path() +{ + mPathIndex = NoPathIndex; + mIsLooping = true; +} + +Path::~Path() +{ + // +} + +//-------------------------------------------------------------------------- +void Path::initPersistFields() +{ + addField("isLooping", TypeBool, Offset(mIsLooping, Path), "If this is true, the loop is closed, otherwise it is open.\n"); + + Parent::initPersistFields(); + // +} + + + +//-------------------------------------------------------------------------- +bool Path::onAdd() +{ + if(!Parent::onAdd()) + return false; + + return true; +} + + +void Path::onRemove() +{ + // + + Parent::onRemove(); +} + + + +//-------------------------------------------------------------------------- +/// Sort the markers objects into sequence order +void Path::sortMarkers() +{ + dQsort(objectList.address(), objectList.size(), sizeof(SimObject*), cmpPathObject); +} + +void Path::updatePath() +{ + // If we need to, allocate a path index from the manager + if (mPathIndex == NoPathIndex) + mPathIndex = gServerPathManager->allocatePathId(); + + sortMarkers(); + + Vector positions; + Vector rotations; + Vector times; + Vector smoothingTypes; + + for (iterator itr = begin(); itr != end(); itr++) + { + Marker* pMarker = dynamic_cast(*itr); + if (pMarker != NULL) + { + Point3F pos; + pMarker->getTransform().getColumn(3, &pos); + positions.push_back(pos); + + QuatF rot; + rot.set(pMarker->getTransform()); + rotations.push_back(rot); + + times.push_back(pMarker->mMSToNext); + smoothingTypes.push_back(pMarker->mSmoothingType); + } + } + + // DMMTODO: Looping paths. + gServerPathManager->updatePath(mPathIndex, positions, rotations, times, smoothingTypes); +} + +void Path::addObject(SimObject* obj) +{ + Parent::addObject(obj); + + if (mPathIndex != NoPathIndex) { + // If we're already finished, and this object is a marker, then we need to + // update our path information... + if (dynamic_cast(obj) != NULL) + updatePath(); + } +} + +void Path::removeObject(SimObject* obj) +{ + bool recalc = dynamic_cast(obj) != NULL; + + Parent::removeObject(obj); + + if (mPathIndex != NoPathIndex && recalc == true) + updatePath(); +} + +DefineEngineMethod( Path, getPathId, S32, (),, + "@brief Returns the PathID (not the object ID) of this path.\n\n" + "@return PathID (not the object ID) of this path.\n" + "@tsexample\n" + "// Acquire the PathID of this path object.\n" + "%pathID = %thisPath.getPathId();\n\n" + "@endtsexample\n\n" + ) +{ + Path *path = static_cast(object); + return path->getPathIndex(); +} + +} // Namespace + +//-------------------------------------------------------------------------- +//-------------------------------------------------------------------------- + +GFXStateBlockRef Marker::smStateBlock; +GFXVertexBufferHandle Marker::smVertexBuffer; +GFXPrimitiveBufferHandle Marker::smPrimitiveBuffer; + +static Point3F wedgePoints[4] = { + Point3F(-1, -1, 0), + Point3F( 0, 1, 0), + Point3F( 1, -1, 0), + Point3F( 0,-.75, .5), +}; + +void Marker::initGFXResources() +{ + if(smVertexBuffer != NULL) + return; + + GFXStateBlockDesc d; + d.cullDefined = true; + d.cullMode = GFXCullNone; + + smStateBlock = GFX->createStateBlock(d); + + smVertexBuffer.set(GFX, 4, GFXBufferTypeStatic); + GFXVertexPC* verts = smVertexBuffer.lock(); + verts[0].point = wedgePoints[0] * 0.25f; + verts[1].point = wedgePoints[1] * 0.25f; + verts[2].point = wedgePoints[2] * 0.25f; + verts[3].point = wedgePoints[3] * 0.25f; + verts[0].color = verts[1].color = verts[2].color = verts[3].color = GFXVertexColor(ColorI(0, 255, 0, 255)); + smVertexBuffer.unlock(); + + smPrimitiveBuffer.set(GFX, 24, 12, GFXBufferTypeStatic); + U16* prims; + smPrimitiveBuffer.lock(&prims); + prims[0] = 0; + prims[1] = 3; + prims[2] = 3; + prims[3] = 1; + prims[4] = 1; + prims[5] = 0; + + prims[6] = 3; + prims[7] = 1; + prims[8] = 1; + prims[9] = 2; + prims[10] = 2; + prims[11] = 3; + + prims[12] = 0; + prims[13] = 3; + prims[14] = 3; + prims[15] = 2; + prims[16] = 2; + prims[17] = 0; + + prims[18] = 0; + prims[19] = 2; + prims[20] = 2; + prims[21] = 1; + prims[22] = 1; + prims[23] = 0; + smPrimitiveBuffer.unlock(); +} + +IMPLEMENT_CO_NETOBJECT_V1(Marker); + +ConsoleDocClass( Marker, + "@brief A single joint, or knot, along a path. Should be stored inside a Path container object. A path markers can be\n" + "one of three primary movement types: \"normal\", \"Position Only\", or \"Kink\". \n" + + "@tsexample\n" + "new path()\n" + " {\n" + " isLooping = \"1\";\n" + "\n" + " new Marker()\n" + " {\n" + " seqNum = \"0\";\n" + " type = \"Normal\";\n" + " msToNext = \"1000\";\n" + " smoothingType = \"Spline\";\n" + " position = \"-0.054708 -35.0612 234.802\";\n" + " rotation = \"1 0 0 0\";\n" + " };\n" + "\n" + " };\n" + "@endtsexample\n" + "@see Path\n" + "@ingroup enviroMisc\n" +); + +Marker::Marker() +{ + // Not ghostable unless we're editing... + mNetFlags.clear(Ghostable); + + mTypeMask |= MarkerObjectType; + + mSeqNum = 0; + mSmoothingType = SmoothingTypeLinear; + mMSToNext = 1000; + mSmoothingType = SmoothingTypeSpline; + mKnotType = KnotTypeNormal; +} + +Marker::~Marker() +{ + // +} + +//-------------------------------------------------------------------------- + +ImplementEnumType( MarkerSmoothingType, + "The type of smoothing this marker will have for pathed objects.\n" + "@ingroup enviroMisc\n\n") + { Marker::SmoothingTypeSpline , "Spline", "Marker will cause the movements of the pathed object to be smooth.\n" }, + { Marker::SmoothingTypeLinear , "Linear", "Marker will have no smoothing effect.\n" }, + //{ Marker::SmoothingTypeAccelerate , "Accelerate" }, +EndImplementEnumType; + +ImplementEnumType( MarkerKnotType, + "The type of knot that this marker will be.\n" + "@ingroup enviroMisc\n\n") + { Marker::KnotTypeNormal , "Normal", "Knot will have a smooth camera translation/rotation effect.\n" }, + { Marker::KnotTypePositionOnly, "Position Only", "Will do the same for translations, leaving rotation un-touched.\n" }, + { Marker::KnotTypeKink, "Kink", "The rotation will take effect immediately for an abrupt rotation change.\n" }, +EndImplementEnumType; + +void Marker::initPersistFields() +{ + addGroup( "Misc" ); + addField("seqNum", TypeS32, Offset(mSeqNum, Marker), "Marker position in sequence of markers on this path.\n"); + addField("type", TYPEID< KnotType >(), Offset(mKnotType, Marker), "Type of this marker/knot. A \"normal\" knot will have a smooth camera translation/rotation effect.\n\"Position Only\"ťwill do the same for translations, leaving rotation un-touched.\nLastly, a \"Kink\" means the rotation will take effect immediately for an abrupt rotation change.\n"); + addField("msToNext", TypeS32, Offset(mMSToNext, Marker), "Milliseconds to next marker in sequence.\n"); + addField("smoothingType", TYPEID< SmoothingType >(), Offset(mSmoothingType, Marker), "Path smoothing at this marker/knot. \"Linear\"ťmeans no smoothing, while \"Spline\" means to smooth.\n"); + endGroup("Misc"); + + Parent::initPersistFields(); +} + +//-------------------------------------------------------------------------- +bool Marker::onAdd() +{ + if(!Parent::onAdd()) + return false; + + mObjBox = Box3F(Point3F(-.25, -.25, -.25), Point3F(.25, .25, .25)); + resetWorldBox(); + + if(gEditingMission) + onEditorEnable(); + + return true; +} + + +void Marker::onRemove() +{ + if(gEditingMission) + onEditorDisable(); + + Parent::onRemove(); + + smVertexBuffer = NULL; + smPrimitiveBuffer = NULL; +} + +void Marker::onGroupAdd() +{ + mSeqNum = getGroup()->size() - 1; +} + + +/// Enable scoping so we can see this thing on the client. +void Marker::onEditorEnable() +{ + mNetFlags.set(Ghostable); + setScopeAlways(); + addToScene(); +} + +/// Disable scoping so we can see this thing on the client +void Marker::onEditorDisable() +{ + removeFromScene(); + mNetFlags.clear(Ghostable); + clearScopeAlways(); +} + + +/// Tell our parent that this Path has been modified +void Marker::inspectPostApply() +{ + SimPath::Path *path = dynamic_cast(getGroup()); + if (path) + path->updatePath(); +} + + +//-------------------------------------------------------------------------- +void Marker::prepRenderImage( SceneRenderState* state ) +{ + // This should be sufficient for most objects that don't manage zones, and + // don't need to return a specialized RenderImage... + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &Marker::renderObject ); + ri->type = RenderPassManager::RIT_Editor; + state->getRenderPass()->addInst(ri); +} + + +void Marker::renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* overrideMat) +{ + initGFXResources(); + + for(U32 i = 0; i < GFX->getNumSamplers(); i++) + GFX->setTexture(i, NULL); + GFXTransformSaver saver; + MatrixF mat = getRenderTransform(); + mat.scale(mObjScale); + GFX->multWorld(mat); + + GFX->setStateBlock(smStateBlock); + GFX->setVertexBuffer(smVertexBuffer); + GFX->setPrimitiveBuffer(smPrimitiveBuffer); + GFX->setupGenericShaders(); + GFX->drawIndexedPrimitive(GFXLineList, 0, 0, 4, 0, 12); +} + + +//-------------------------------------------------------------------------- +U32 Marker::packUpdate(NetConnection* con, U32 mask, BitStream* stream) +{ + U32 retMask = Parent::packUpdate(con, mask, stream); + + // Note that we don't really care about efficiency here, since this is an + // edit-only ghost... + stream->writeAffineTransform(mObjToWorld); + + return retMask; +} + +void Marker::unpackUpdate(NetConnection* con, BitStream* stream) +{ + Parent::unpackUpdate(con, stream); + + // Transform + MatrixF otow; + stream->readAffineTransform(&otow); + + setTransform(otow); +} \ No newline at end of file diff --git a/Engine/source/scene/simPath.h b/Engine/source/scene/simPath.h new file mode 100644 index 000000000..9e172bdf4 --- /dev/null +++ b/Engine/source/scene/simPath.h @@ -0,0 +1,157 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SIMPATH_H_ +#define _SIMPATH_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef _GFXSTATEBLOCK_H_ +#include "gfx/gfxStateBlock.h" +#endif +#ifndef _GFXVERTEXBUFFER_H_ +#include "gfx/gfxVertexBuffer.h" +#endif +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif + +class BaseMatInstance; + + +namespace SimPath +{ + +//-------------------------------------------------------------------------- +/// A path! +class Path : public SimGroup +{ + typedef SimGroup Parent; + + public: + enum { + NoPathIndex = 0xFFFFFFFF + }; + + + private: + U32 mPathIndex; + bool mIsLooping; + + protected: + bool onAdd(); + void onRemove(); + + public: + Path(); + ~Path(); + + void addObject(SimObject*); + void removeObject(SimObject*); + + void sortMarkers(); + void updatePath(); + bool isLooping() { return mIsLooping; } + U32 getPathIndex() const; + + DECLARE_CONOBJECT(Path); + static void initPersistFields(); +}; + +//-------------------------------------------------------------------------- +//-------------------------------------------------------------------------- +inline U32 Path::getPathIndex() const +{ + return mPathIndex; +} + +} // Namespace + + +//-------------------------------------------------------------------------- +class Marker : public SceneObject +{ + typedef SceneObject Parent; + friend class Path; + + public: + enum SmoothingType + { + SmoothingTypeLinear, + SmoothingTypeSpline, + SmoothingTypeAccelerate, + }; + + enum KnotType + { + KnotTypeNormal, + KnotTypePositionOnly, + KnotTypeKink, + }; + + + U32 mSeqNum; + U32 mSmoothingType; + U32 mKnotType; + + U32 mMSToNext; + + // Rendering + protected: + void prepRenderImage(SceneRenderState *state); + void renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance* overrideMat); + + protected: + bool onAdd(); + void onRemove(); + void onGroupAdd(); + + void onEditorEnable(); + void onEditorDisable(); + + static void initGFXResources(); + + static GFXStateBlockRef smStateBlock; + static GFXVertexBufferHandle smVertexBuffer; + static GFXPrimitiveBufferHandle smPrimitiveBuffer; + + public: + Marker(); + ~Marker(); + + DECLARE_CONOBJECT(Marker); + static void initPersistFields(); + void inspectPostApply(); + + U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream); + void unpackUpdate(NetConnection *conn, BitStream *stream); +}; + +typedef Marker::SmoothingType MarkerSmoothingType; +typedef Marker::KnotType MarkerKnotType; + +DefineEnumType( MarkerSmoothingType ); +DefineEnumType( MarkerKnotType ); + +#endif // _H_PATH + diff --git a/Engine/source/scene/zones/scenePolyhedralZone.cpp b/Engine/source/scene/zones/scenePolyhedralZone.cpp new file mode 100644 index 000000000..2fccd09f6 --- /dev/null +++ b/Engine/source/scene/zones/scenePolyhedralZone.cpp @@ -0,0 +1,102 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "scene/zones/scenePolyhedralZone.h" + +#include "scene/mixin/scenePolyhedralObject.impl.h" + + +//----------------------------------------------------------------------------- + +ScenePolyhedralZone::ScenePolyhedralZone( const PolyhedronType& polyhedron ) + : Parent( polyhedron ) +{ +} + +//----------------------------------------------------------------------------- + +bool ScenePolyhedralZone::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + // Precompute polyhedron/AABB intersection data, if not + // a trivial box polyhedron. + + if( !mIsBox ) + { + _updateIntersector(); + + // Also need to update OBB. + _updateOrientedWorldBox(); + } + + return true; +} + +//----------------------------------------------------------------------------- + +void ScenePolyhedralZone::setTransform( const MatrixF& mat ) +{ + Parent::setTransform( mat ); + + // Recompute intersection data. + + if( !mIsBox ) + _updateIntersector(); +} + +//----------------------------------------------------------------------------- + +void ScenePolyhedralZone::_updateOrientedWorldBox() +{ + if( mIsBox ) + Parent::_updateOrientedWorldBox(); + else + mOrientedWorldBox.set( getTransform(), Point3F( mObjBox.len_x(), mObjBox.len_y(), mObjBox.len_z() ) ); +} + +//----------------------------------------------------------------------------- + +bool ScenePolyhedralZone::getOverlappingZones( const Box3F& aabb, U32* outZones, U32& outNumZones ) +{ + // If a trivial box, let parent handle this. + + if( mIsBox ) + return Parent::getOverlappingZones( aabb, outZones, outNumZones ); + + // Otherwise, use the intersector. + + OverlapTestResult overlap = mIntersector.test( aabb ); + + if( overlap == GeometryOutside ) + { + outNumZones = 0; + return true; + } + + outZones[ 0 ] = getZoneRangeStart(); + outNumZones = 1; + + return ( overlap != GeometryInside ); +} diff --git a/Engine/source/scene/zones/scenePolyhedralZone.h b/Engine/source/scene/zones/scenePolyhedralZone.h new file mode 100644 index 000000000..b6a9c7b03 --- /dev/null +++ b/Engine/source/scene/zones/scenePolyhedralZone.h @@ -0,0 +1,81 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SCENEPOLYHEDRALZONE_H_ +#define _SCENEPOLYHEDRALZONE_H_ + +#ifndef _SCENESIMPLEZONE_H_ +#include "scene/zones/sceneSimpleZone.h" +#endif + +#ifndef _SCENEPOLYHEDRALOBJECT_H_ +#include "scene/mixin/scenePolyhedralObject.h" +#endif + +#ifndef _MINTERSECTOR_H_ +#include "math/mIntersector.h" +#endif + + +/// A simple zone space that is described by a polyhedron. +/// +/// By default, if no other polyhedron is assigned to a polyhedral zone, the +/// polyhedron is initialized from the zone's object box. +class ScenePolyhedralZone : public ScenePolyhedralObject< SceneSimpleZone > +{ + public: + + typedef ScenePolyhedralObject< SceneSimpleZone > Parent; + + protected: + + typedef PolyhedronBoxIntersector< PolyhedronType > IntersectorType; + + /// Fast polyhedron/AABB intersector used for testing SceneObject AABBs + /// for overlap with the zone. + IntersectorType mIntersector; + + /// Precompute polyhedron/AABB intersection data. + void _updateIntersector() + { + mIntersector = IntersectorType( getPolyhedron(), getTransform(), getScale(), getWorldBox() ); + } + + // SceneSimpleZone. + virtual void _updateOrientedWorldBox(); + + public: + + ScenePolyhedralZone() {} + ScenePolyhedralZone( const PolyhedronType& polyhedron ); + + // SimObject. + virtual bool onAdd(); + + // SceneObject. + virtual void setTransform( const MatrixF& mat ); + + // SceneZoneSpace. + virtual bool getOverlappingZones( const Box3F& aabb, U32* outZones, U32& outNumZones ); +}; + +#endif // !_SCENEPOLYHEDRALZONE_H_ diff --git a/Engine/source/scene/zones/sceneRootZone.cpp b/Engine/source/scene/zones/sceneRootZone.cpp new file mode 100644 index 000000000..d389de5d5 --- /dev/null +++ b/Engine/source/scene/zones/sceneRootZone.cpp @@ -0,0 +1,121 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "scene/zones/sceneRootZone.h" + +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" + + + +//RD: for the SceneRootZone, it may be worthwhile to put an optimized path in place that +// does some spatially aware testing of portals rather than just blindly going through +// the list of connected zone managers + + +//----------------------------------------------------------------------------- + +SceneRootZone::SceneRootZone() +{ + setGlobalBounds(); + resetWorldBox(); + + // Clear netflags we have inherited. We don't + // network scene roots. + mNetFlags.clear( Ghostable | ScopeAlways ); + + // Clear type flags we have inherited. + mTypeMask &= ~StaticObjectType; +} + +//----------------------------------------------------------------------------- + +String SceneRootZone::describeSelf() const +{ + String str = Parent::describeSelf(); + + str += "|SceneRootZone"; + + return str; +} + +//----------------------------------------------------------------------------- + +bool SceneRootZone::onSceneAdd() +{ + if( !Parent::onSceneAdd() ) + return false; + + AssertFatal( getZoneRangeStart() == SceneZoneSpaceManager::RootZoneId, "SceneRootZone::onSceneAdd - SceneRootZone must be first scene object zone manager!" ); + + return true; +} + +//----------------------------------------------------------------------------- + +void SceneRootZone::onSceneRemove() +{ + AssertFatal( getZoneRangeStart() == SceneZoneSpaceManager::RootZoneId, "SceneRootZone::onSceneRemove - SceneRootZone must be first scene object zone manager!"); + Parent::onSceneRemove(); +} + +//----------------------------------------------------------------------------- + +bool SceneRootZone::onAdd() +{ + AssertFatal( false, "SceneRootZone::onAdd - Must not register SceneRootZone!" ); + return false; +} + +//----------------------------------------------------------------------------- + +void SceneRootZone::onRemove() +{ + AssertFatal( false, "SceneRootZone::onRemove - Must not be called!" ); +} + +//----------------------------------------------------------------------------- + +U32 SceneRootZone::getPointZone( const Point3F &p ) const +{ + return 0; +} + +//----------------------------------------------------------------------------- + +bool SceneRootZone::getOverlappingZones( const Box3F& aabb, U32* outZones, U32& outNumZones ) +{ + // Everything overlaps the outside zone. + + outZones[ 0 ] = SceneZoneSpaceManager::RootZoneId; + outNumZones = 1; + + return false; +} + +//----------------------------------------------------------------------------- + +bool SceneRootZone::containsPoint( const Point3F &point ) const +{ + return true; +} diff --git a/Engine/source/scene/zones/sceneRootZone.h b/Engine/source/scene/zones/sceneRootZone.h new file mode 100644 index 000000000..6cc7932dc --- /dev/null +++ b/Engine/source/scene/zones/sceneRootZone.h @@ -0,0 +1,69 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SCENEROOTZONE_H_ +#define _SCENEROOTZONE_H_ + +#ifndef _SCENESIMPLEZONE_H_ +#include "scene/zones/sceneSimpleZone.h" +#endif + + + +/// Root zone in a scene. +/// +/// The root zone is an infinite zone that contains all others zones and +/// objects in the scene. +/// +/// This class is not declared as a ConsoleObject and is not visible +/// from TorqueScript. SceneRootZone objects are not registered with the +/// Sim system. +class SceneRootZone : public SceneSimpleZone +{ + public: + + typedef SceneSimpleZone Parent; + + protected: + + // SceneObject. + virtual bool onSceneAdd(); + virtual void onSceneRemove(); + + public: + + SceneRootZone(); + + // SimObject. + virtual bool onAdd(); + virtual void onRemove(); + virtual String describeSelf() const; + + // SceneObject. + virtual bool containsPoint( const Point3F &point ) const; + + // SceneZoneSpace. + virtual U32 getPointZone( const Point3F &p ) const; + virtual bool getOverlappingZones( const Box3F& aabb, U32* outZones, U32& outNumZones ); +}; + +#endif //_SCENEROOTZONE_H_ diff --git a/Engine/source/scene/zones/sceneSimpleZone.cpp b/Engine/source/scene/zones/sceneSimpleZone.cpp new file mode 100644 index 000000000..9d968b5e2 --- /dev/null +++ b/Engine/source/scene/zones/sceneSimpleZone.cpp @@ -0,0 +1,361 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "scene/zones/sceneSimpleZone.h" + +#include "scene/sceneRenderState.h" +#include "scene/sceneManager.h" +#include "scene/zones/sceneTraversalState.h" +#include "scene/culling/sceneCullingVolume.h" +#include "core/stream/bitStream.h" +#include "console/engineAPI.h" +#include "platform/profiler.h" + + +extern bool gEditingMission; + + +//----------------------------------------------------------------------------- + +SceneSimpleZone::SceneSimpleZone() + : mUseAmbientLightColor( false ), + mAmbientLightColor( 0.1f, 0.1f, 0.1f, 1.f ), + mIsRotated( false ) +{ + // Box zones are unit cubes that are scaled to fit. + + mObjScale.set( 10, 10, 10 ); + mObjBox.set( + Point3F( -0.5f, -0.5f, -0.5f ), + Point3F( 0.5f, 0.5f, 0.5f ) + ); +} + +//----------------------------------------------------------------------------- + +void SceneSimpleZone::initPersistFields() +{ + addGroup( "Lighting" ); + + addProtectedField( "useAmbientLightColor", TypeBool, Offset( mUseAmbientLightColor, SceneSimpleZone ), + &_setUseAmbientLightColor, &defaultProtectedGetFn, + "Whether to use #ambientLightColor for ambient lighting in this zone or the global ambient color." ); + addProtectedField( "ambientLightColor", TypeColorF, Offset( mAmbientLightColor, SceneSimpleZone ), + &_setAmbientLightColor, &defaultProtectedGetFn, + "Color of ambient lighting in this zone.\n\n" + "Only used if #useAmbientLightColor is true." ); + + endGroup( "Lighting" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +String SceneSimpleZone::describeSelf() const +{ + String str = Parent::describeSelf(); + str += String::ToString( "|zoneid: %i", getZoneRangeStart() ); + return str; +} + +//----------------------------------------------------------------------------- + +bool SceneSimpleZone::onSceneAdd() +{ + if( !Parent::onSceneAdd() ) + return false; + + // Register our zone. + + SceneZoneSpaceManager* manager = getSceneManager()->getZoneManager(); + if( manager ) + manager->registerZones( this, 1 ); + + return true; +} + +//----------------------------------------------------------------------------- + +U32 SceneSimpleZone::packUpdate( NetConnection* connection, U32 mask, BitStream* stream ) +{ + U32 retMask = Parent::packUpdate( connection, mask, stream ); + + if( stream->writeFlag( mask & AmbientMask ) ) + { + stream->writeFlag( mUseAmbientLightColor ); + stream->writeFloat( mAmbientLightColor.red, 7 ); + stream->writeFloat( mAmbientLightColor.green, 7 ); + stream->writeFloat( mAmbientLightColor.blue, 7 ); + stream->writeFloat( mAmbientLightColor.alpha, 7 ); + } + + return retMask; +} + +//----------------------------------------------------------------------------- + +void SceneSimpleZone::unpackUpdate( NetConnection* connection, BitStream* stream ) +{ + Parent::unpackUpdate( connection, stream ); + + if( stream->readFlag() ) // AmbientMask + { + mUseAmbientLightColor = stream->readFlag(); + mAmbientLightColor.red = stream->readFloat( 7 ); + mAmbientLightColor.green = stream->readFloat( 7 ); + mAmbientLightColor.blue = stream->readFloat( 7 ); + mAmbientLightColor.alpha = stream->readFloat( 7 ); + } +} + +//----------------------------------------------------------------------------- + +void SceneSimpleZone::setUseAmbientLightColor( bool value ) +{ + if( mUseAmbientLightColor == value ) + return; + + mUseAmbientLightColor = value; + if( isServerObject() ) + setMaskBits( AmbientMask ); +} + +//----------------------------------------------------------------------------- + +void SceneSimpleZone::setAmbientLightColor( const ColorF& color ) +{ + mAmbientLightColor = color; + if( isServerObject() ) + setMaskBits( AmbientMask ); +} + +//----------------------------------------------------------------------------- + +bool SceneSimpleZone::getZoneAmbientLightColor( U32 zone, ColorF& outColor ) const +{ + AssertFatal( zone == getZoneRangeStart(), "SceneSimpleZone::getZoneAmbientLightColor - Invalid zone ID!" ); + + if( !mUseAmbientLightColor ) + return false; + + outColor = mAmbientLightColor; + return true; +} + +//----------------------------------------------------------------------------- + +U32 SceneSimpleZone::getPointZone( const Point3F& p ) +{ + if( !containsPoint( p ) ) + return SceneZoneSpaceManager::InvalidZoneId; + + return getZoneRangeStart(); +} + +//----------------------------------------------------------------------------- + +bool SceneSimpleZone::getOverlappingZones( const Box3F& aabb, U32* outZones, U32& outNumZones ) +{ + PROFILE_SCOPE( SceneBoxZone_getOverlappingZones ); + + bool isOverlapped = false; + bool isContained = false; + + // If the zone has not been rotated, we can simply use straightforward + // AABB/AABB intersection based on the world boxes of the zone and the + // object. + // + // If, however, the zone has been rotated, then we must use the zone's + // OBB and test that against the object's AABB. + + if( !mIsRotated ) + { + isOverlapped = mWorldBox.isOverlapped( aabb ); + isContained = isOverlapped && mWorldBox.isContained( aabb ); + } + else + { + // Check if the zone's OBB intersects the object's AABB. + + isOverlapped = aabb.collideOrientedBox( + getScale() / 2.f, + getTransform() + ); + + // If so, check whether the object's AABB is fully contained + // inside the zone's OBB. + + if( isOverlapped ) + { + isContained = true; + + for( U32 i = 0; i < Box3F::NUM_POINTS; ++ i ) + { + Point3F cornerPoint = aabb.computeVertex( i ); + if( !mOrientedWorldBox.isContained( cornerPoint ) ) + { + isContained = false; + break; + } + } + } + } + + if( isOverlapped ) + { + outNumZones = 1; + outZones[ 0 ] = getZoneRangeStart(); + } + else + outNumZones = 0; + + return !isContained; +} + +//----------------------------------------------------------------------------- + +void SceneSimpleZone::setTransform( const MatrixF& mat ) +{ + Parent::setTransform( mat ); + + // Find out whether the zone has been rotated. + + EulerF rotation = getTransform().toEuler(); + mIsRotated = !mIsZero( rotation.x ) || + !mIsZero( rotation.y ) || + !mIsZero( rotation.z ); + + // Update the OBB. + + _updateOrientedWorldBox(); +} + +//----------------------------------------------------------------------------- + +void SceneSimpleZone::prepRenderImage( SceneRenderState* state ) +{ + if( isRootZone() ) + return; + + Parent::prepRenderImage( state ); +} + +//----------------------------------------------------------------------------- + +void SceneSimpleZone::traverseZones( SceneTraversalState* state ) +{ + traverseZones( state, getZoneRangeStart() ); +} + +//----------------------------------------------------------------------------- + +void SceneSimpleZone::traverseZones( SceneTraversalState* state, U32 startZoneId ) +{ + PROFILE_SCOPE( SceneSimpleZone_traverseZones ); + AssertFatal( startZoneId == getZoneRangeStart(), "SceneSimpleZone::traverseZones - Invalid start zone ID!" ); + + // If we aren't the root of the traversal, do a number of checks + // to see if we can early out of the traversal here. The primary reason + // we don't do the checks if we are the root is because of the frustum + // near plane. The start zone of a traversal is selected based on the + // current viewpoint. However, that point still has some space in between + // it and the near plane so if we end up with a case where that's all the + // space that is needed to cull away our starting zone, we won't see any + // traversal at all and get a blank scene back even if the starting zone + // would actually hand the traversal over to other zones and eventually + // discover visible space. + + if( state->getTraversalDepth() > 0 ) + { + // If we have already visited this zone in this traversal chain, + // exit out. This can happen when zones are grouped together. + // Note that this can also happen with the outdoor zone since it isn't + // convex. However, in that case, this acts as a nice side optimization + // we get since if the outdoor zone is already on the stack, our culling + // culling volume can only be smaller than the one we started with and thus + // by earlying out here, we prevent adding a pointless culling volume + // to the zone. + + //TODO: would be nice to catch this via "visibility changed?" checks but + // that's non-trivial + + if( state->haveVisitedZone( getZoneRangeStart() ) ) + return; + + // First check whether we even intersect the given frustum at all. + + if( mIsRotated ) + { + // Space has been rotated, so do a frustum/OBB check. + if( !state->getCurrentCullingVolume().test( _getOrientedWorldBox() ) ) + return; + } + else + { + // Space has not been rotated, so we can do a faster frustum/ABB check. + if( !state->getCurrentCullingVolume().test( getWorldBox() ) ) + return; + } + } + + // Add the current culling volume to the culling state for this zone. + // If that doesn't result in new space becoming visible, we can terminate the traversal + // here. + + if( !state->getCullingState()->addCullingVolumeToZone( startZoneId, state->getCurrentCullingVolume() ) ) + return; + + // Add our occluders to the rendering state. + + _addOccludersToCullingState( state->getCullingState() ); + + // Merge the zone into the traversal area. + + state->addToTraversedArea( getWorldBox() ); + + // Push our zone ID on the traversal stack and traverse into our + // connected zone managers. + + state->pushZone( startZoneId ); + _traverseConnectedZoneSpaces( state ); + state->popZone(); +} + +//----------------------------------------------------------------------------- + +bool SceneSimpleZone::_setUseAmbientLightColor( void* object, const char* index, const char* data ) +{ + SceneSimpleZone* zone = reinterpret_cast< SceneSimpleZone* >( object ); + zone->setUseAmbientLightColor( EngineUnmarshallData< bool >()( data ) ); + return false; +} + +//----------------------------------------------------------------------------- + +bool SceneSimpleZone::_setAmbientLightColor( void* object, const char* index, const char* data ) +{ + SceneSimpleZone* zone = reinterpret_cast< SceneSimpleZone* >( object ); + zone->setAmbientLightColor( EngineUnmarshallData< ColorF >()( data ) ); + return false; +} diff --git a/Engine/source/scene/zones/sceneSimpleZone.h b/Engine/source/scene/zones/sceneSimpleZone.h new file mode 100644 index 000000000..4d08a9f8f --- /dev/null +++ b/Engine/source/scene/zones/sceneSimpleZone.h @@ -0,0 +1,145 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SCENESIMPLEZONE_H_ +#define _SCENESIMPLEZONE_H_ + +#ifndef _SCENEZONESPACE_H_ +#include "scene/zones/sceneZoneSpace.h" +#endif + +#ifndef _MORIENTEDBOX_H_ +#include "math/mOrientedBox.h" +#endif + + +class SceneRenderState; +class BaseMatInstance; + + +/// Abstract base class for a zone space that contains only a single zone. +/// +/// Simple zones are required to be convex. +class SceneSimpleZone : public SceneZoneSpace +{ + public: + + typedef SceneZoneSpace Parent; + + protected: + + enum + { + AmbientMask = Parent::NextFreeMask << 0, ///< Ambient light color setting has changed. + NextFreeMask = Parent::NextFreeMask << 1, + }; + + /// @name Ambient Lighting + /// @{ + + /// If this is true, then the zone defines its own ambient + /// light color in #mAmbientColor. + bool mUseAmbientLightColor; + + /// Ambient light color in this zone. + ColorF mAmbientLightColor; + + /// @} + + /// @name Transforms + /// @{ + + /// Whether the zone has been rotated. This is used to fasttrack overlap + /// tests to avoid doing an OBB/AABB intersection test when we can simply + /// use a much quicker AABB/AABB intersection test on the world boxes of + /// both objects. + bool mIsRotated; + + /// The OBB for the zone. + OrientedBox3F mOrientedWorldBox; + + /// @} + + /// Return the oriented bounding box for the zone. + const OrientedBox3F& _getOrientedWorldBox() const { return mOrientedWorldBox; } + + /// Update the OBB for the zone. + virtual void _updateOrientedWorldBox() { mOrientedWorldBox.set( getTransform(), getScale() ); } + + // SceneObject. + virtual bool onSceneAdd(); + + public: + + SceneSimpleZone(); + + /// @name Ambient Lighting + /// @{ + + /// Return true if the zone defines its own ambient light color. + bool useAmbientLightColor() const { return mUseAmbientLightColor; } + + /// Set whether a custom ambient light color is active in this zone. + void setUseAmbientLightColor( bool value ); + + /// Return the ambient light color for this zone. + ColorF getAmbientLightColor() const { return mAmbientLightColor; } + + /// Set the ambient light color for the zone. + /// @note This only takes effect if useAmbientLightColor() return true. + /// @see setUseAmbientLightColor + void setAmbientLightColor( const ColorF& color ); + + /// @} + + /// @name Inherited + /// @{ + + // SimObject. + virtual String describeSelf() const; + + static void initPersistFields(); + + // NetObject + virtual U32 packUpdate( NetConnection *conn, U32 mask, BitStream *stream ); + virtual void unpackUpdate( NetConnection *conn, BitStream *stream ); + + // SceneObject + virtual void prepRenderImage( SceneRenderState* state ); + virtual void setTransform( const MatrixF& mat ); + + // SceneZoneSpace. + virtual U32 getPointZone( const Point3F &p ); + virtual bool getOverlappingZones( const Box3F& aabb, U32* outZones, U32& outNumZones ); + virtual void traverseZones( SceneTraversalState* state ); + virtual bool getZoneAmbientLightColor( U32 zone, ColorF& outColor ) const; + virtual void traverseZones( SceneTraversalState* state, U32 startZoneId ); + + /// @} + + private: + + static bool _setUseAmbientLightColor( void* object, const char* index, const char* data ); + static bool _setAmbientLightColor( void* object, const char* index, const char* data ); +}; + +#endif // !_SCENESIMPLEZONE_H_ diff --git a/Engine/source/scene/zones/sceneTraversalState.cpp b/Engine/source/scene/zones/sceneTraversalState.cpp new file mode 100644 index 000000000..7bc787523 --- /dev/null +++ b/Engine/source/scene/zones/sceneTraversalState.cpp @@ -0,0 +1,70 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "scene/zones/sceneTraversalState.h" + +#include "scene/sceneManager.h" +#include "scene/culling/sceneCullingState.h" +#include "scene/culling/sceneCullingVolume.h" +#include "scene/zones/sceneZoneSpaceManager.h" + + +//----------------------------------------------------------------------------- + +SceneTraversalState::SceneTraversalState( SceneCullingState* cullingState ) + : mCullingState( cullingState ), + mTraversedArea( 0.f ) +{ + VECTOR_SET_ASSOCIATION( mZoneStack ); + VECTOR_SET_ASSOCIATION( mCullingVolumeStack ); + + // Push the polyhedron of the root frustum onto the traversal + // stack as the current culling volume. + + pushCullingVolume( cullingState->getRootVolume() ); +} + +//----------------------------------------------------------------------------- + +SceneZoneSpace* SceneTraversalState::getZoneFromStack( U32 depth ) +{ + SceneZoneSpaceManager* zoneManager = getCullingState()->getSceneManager()->getZoneManager(); + U32 zoneId = getZoneIdFromStack( depth ); + + return zoneManager->getZoneOwner( zoneId ); +} + +//----------------------------------------------------------------------------- + +void SceneTraversalState::pushCullingVolume( const SceneCullingVolume& volume ) +{ + mCullingVolumeStack.push_back( volume ); +} + +//----------------------------------------------------------------------------- + +void SceneTraversalState::popCullingVolume() +{ + AssertFatal( mCullingVolumeStack.size() > 1, "SceneTraversalState::popCullingVolume - Must not pop root volume" ); + mCullingVolumeStack.pop_back(); +} diff --git a/Engine/source/scene/zones/sceneTraversalState.h b/Engine/source/scene/zones/sceneTraversalState.h new file mode 100644 index 000000000..47dc57bfa --- /dev/null +++ b/Engine/source/scene/zones/sceneTraversalState.h @@ -0,0 +1,119 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SCENETRAVERSALSTATE_H_ +#define _SCENETRAVERSALSTATE_H_ + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +#ifndef _MBOX_H_ +#include "math/mBox.h" +#endif + +#ifndef _MPLANESET_H_ +#include "math/mPlaneSet.h" +#endif + + +class SceneCullingState; +class SceneCullingVolume; +class SceneZoneSpace; + + +/// Temporary state for zone traversals. Keeps track of the zones +/// that were visited in the current traversal chain as well as of +/// the total area that so far has been visited in the traversal. +class SceneTraversalState +{ + protected: + + /// Scene culling state. + SceneCullingState* mCullingState; + + /// Stack of zones visited in current traversal chain. + Vector< U32 > mZoneStack; + + /// Stack of culling volumes. Topmost is current volume. + Vector< SceneCullingVolume > mCullingVolumeStack; + + /// Area of scene visited in traversal. + Box3F mTraversedArea; + + public: + + /// Construct an empty scene traversal state. + /// @param cullingState Scene culling state. + SceneTraversalState( SceneCullingState* cullingState ); + + /// Return the scene culling state for this traversal. + SceneCullingState* getCullingState() const { return mCullingState; } + + /// Get the scene area that has been visited so far in the traversal. + const Box3F& getTraversedArea() const { return mTraversedArea; } + + /// Add an area to what has been visited so far in the traversal. + void addToTraversedArea( const Box3F& area ) { mTraversedArea.intersect( area ); } + + /// @name Zone Stack + /// @{ + + /// Return the number of zones that are currently on the traversal stack. + U32 getTraversalDepth() const { return mZoneStack.size(); } + + /// Push a zone onto the traversal stack. + void pushZone( U32 zoneId ) { mZoneStack.push_back( zoneId ); } + + /// Pop the topmost zone from the traversal stack. + void popZone() { mZoneStack.pop_back(); } + + /// Return true if the given zone has already been visited in the + /// current traversal chain, i.e. if it is currently on the traversal + /// stack. + bool haveVisitedZone( U32 zoneId ) const { return mZoneStack.contains( zoneId ); } + + /// Return the zone ID of the topmost zone on the traversal stack. + U32 getZoneIdFromStack( U32 depth = 0 ) { return mZoneStack[ mZoneStack.size() - 1 - depth ]; } + + /// Return the zone space that owns the topmost zone on the traversal stack. + SceneZoneSpace* getZoneFromStack( U32 depth = 0 ); + + /// @} + + /// @name Culling Volume Stack + /// @{ + + /// Push a culling volume onto the stack so that it becomes the current culling + /// volume. + void pushCullingVolume( const SceneCullingVolume& volume ); + + /// Pop the current culling volume from the stack. + void popCullingVolume(); + + /// + const SceneCullingVolume& getCurrentCullingVolume() const { return mCullingVolumeStack.last(); } + + /// @} +}; + +#endif // !_SCENETRAVERSALSTATE_H_ diff --git a/Engine/source/scene/zones/sceneZoneSpace.cpp b/Engine/source/scene/zones/sceneZoneSpace.cpp new file mode 100644 index 000000000..4e5101ad2 --- /dev/null +++ b/Engine/source/scene/zones/sceneZoneSpace.cpp @@ -0,0 +1,362 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "scene/zones/sceneZoneSpace.h" + +#include "scene/zones/sceneTraversalState.h" +#include "scene/zones/sceneZoneSpaceManager.h" +#include "scene/sceneRenderState.h" +#include "sim/netConnection.h" +#include "core/stream/bitStream.h" +#include "console/engineAPI.h" + + +//#define DEBUG_SPEW + + +ClassChunker< SceneZoneSpace::ZoneSpaceRef > SceneZoneSpace::smZoneSpaceRefChunker; + + +//----------------------------------------------------------------------------- + +SceneZoneSpace::SceneZoneSpace() + : mManager( NULL ), + mZoneGroup( InvalidZoneGroup ), + mZoneRangeStart( SceneZoneSpaceManager::InvalidZoneId ), + mZoneFlags( ZoneFlag_IsClosedOffSpace ), + mNumZones( 0 ), + mConnectedZoneSpaces( NULL ) +{ + VECTOR_SET_ASSOCIATION( mOccluders ); +} + +//----------------------------------------------------------------------------- + +SceneZoneSpace::~SceneZoneSpace() +{ + AssertFatal( mConnectedZoneSpaces == NULL, "SceneZoneSpace::~SceneZoneSpace - Still have connected zone spaces!" ); +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpace::onSceneRemove() +{ + _disconnectAllZoneSpaces(); + Parent::onSceneRemove(); +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpace::initPersistFields() +{ + addGroup( "Zoning" ); + + addProtectedField( "zoneGroup", TypeS32, Offset( mZoneGroup, SceneZoneSpace ), + &_setZoneGroup, &defaultProtectedGetFn, + "ID of group the zone is part of." ); + + endGroup( "Zoning" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +bool SceneZoneSpace::writeField( StringTableEntry fieldName, const char* value ) +{ + // Don't write zoneGroup field if at default. + static StringTableEntry sZoneGroup = StringTable->insert( "zoneGroup" ); + if( fieldName == sZoneGroup && getZoneGroup() == InvalidZoneGroup ) + return false; + + return Parent::writeField( fieldName, value ); +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpace::setZoneGroup( U32 group ) +{ + if( mZoneGroup == group ) + return; + + mZoneGroup = group; + setMaskBits( ZoneGroupMask ); + + // Rezone to establish new connectivity. + + if( mManager ) + mManager->notifyObjectChanged( this ); +} + +//----------------------------------------------------------------------------- + +U32 SceneZoneSpace::packUpdate( NetConnection* connection, U32 mask, BitStream* stream ) +{ + U32 retMask = Parent::packUpdate( connection, mask, stream ); + + if( stream->writeFlag( mask & ZoneGroupMask ) ) + stream->write( mZoneGroup ); + + return retMask; +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpace::unpackUpdate( NetConnection* connection, BitStream* stream ) +{ + Parent::unpackUpdate( connection, stream ); + + if( stream->readFlag() ) // ZoneGroupMask + { + U32 zoneGroup; + stream->read( &zoneGroup ); + setZoneGroup( zoneGroup ); + } +} + +//----------------------------------------------------------------------------- + +bool SceneZoneSpace::getOverlappingZones( SceneObject* obj, U32* outZones, U32& outNumZones ) +{ + return getOverlappingZones( obj->getWorldBox(), outZones, outNumZones ); +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpace::_onZoneAddObject( SceneObject* object, const U32* zoneIDs, U32 numZones ) +{ + if( object->isVisualOccluder() ) + _addOccluder( object ); + + // If this isn't the root zone and the object is zone space, + // see if we should automatically connect the two. + + if( !isRootZone() && object->getTypeMask() & ZoneObjectType ) + { + SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( object ); + + // Don't connect a zone space that has the same closed off status + // that we have except it is assigned to the same zone group. + + if( zoneSpace && + ( zoneSpace->mZoneFlags.test( ZoneFlag_IsClosedOffSpace ) != mZoneFlags.test( ZoneFlag_IsClosedOffSpace ) || + ( zoneSpace->getZoneGroup() == getZoneGroup() && + zoneSpace->getZoneGroup() != InvalidZoneGroup ) ) && + _automaticallyConnectZoneSpace( zoneSpace ) ) + { + connectZoneSpace( zoneSpace ); + } + } +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpace::_onZoneRemoveObject( SceneObject* object ) +{ + if( object->isVisualOccluder() ) + _removeOccluder( object ); + + if( !isRootZone() && object->getTypeMask() & ZoneObjectType ) + { + SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( object ); + if( zoneSpace ) + disconnectZoneSpace( zoneSpace ); + } +} + +//----------------------------------------------------------------------------- + +bool SceneZoneSpace::_automaticallyConnectZoneSpace( SceneZoneSpace* zoneSpace ) const +{ + //TODO: This is suboptimal. While it prevents the most blatantly wrong automatic connections, + // we need a true polyhedron/polyhedron intersection to accurately determine zone intersection + // when it comes to automatic connections. + + U32 numZones = 0; + U32 zones[ SceneObject::MaxObjectZones ]; + + zoneSpace->getOverlappingZones( getWorldBox(), zones, numZones ); + + return ( numZones > 0 ); +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpace::connectZoneSpace( SceneZoneSpace* zoneSpace ) +{ + // If the zone space is already in the list, do nothing. + + for( ZoneSpaceRef* ref = mConnectedZoneSpaces; ref != NULL; ref = ref->mNext ) + if( ref->mZoneSpace == zoneSpace ) + return; + + // Link the zone space to the zone space refs. + + ZoneSpaceRef* ref = smZoneSpaceRefChunker.alloc(); + + ref->mZoneSpace = zoneSpace; + ref->mNext = mConnectedZoneSpaces; + + mConnectedZoneSpaces = ref; + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SceneZoneSpace] Connecting %i-%i to %i-%i", + getZoneRangeStart(), getZoneRangeStart() + getZoneRange(), + zoneSpace->getZoneRangeStart(), zoneSpace->getZoneRangeStart() + zoneSpace->getZoneRange() + ); + #endif +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpace::disconnectZoneSpace( SceneZoneSpace* zoneSpace ) +{ + ZoneSpaceRef* prev = NULL; + for( ZoneSpaceRef* ref = mConnectedZoneSpaces; ref != NULL; prev = ref, ref = ref->mNext ) + if( ref->mZoneSpace == zoneSpace ) + { + if( prev ) + prev->mNext = ref->mNext; + else + mConnectedZoneSpaces = ref->mNext; + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SceneZoneSpace] Disconnecting %i-%i from %i-%i", + getZoneRangeStart(), getZoneRangeStart() + getZoneRange(), + zoneSpace->getZoneRangeStart(), zoneSpace->getZoneRangeStart() + zoneSpace->getZoneRange() + ); + #endif + + smZoneSpaceRefChunker.free( ref ); + break; + } +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpace::_disconnectAllZoneSpaces() +{ + #ifdef DEBUG_SPEW + if( mConnectedZoneSpaces != NULL ) + Platform::outputDebugString( "[SceneZoneSpace] Disconnecting all from %i-%i", + getZoneRangeStart(), getZoneRangeStart() + getZoneRange() + ); + #endif + + for( ZoneSpaceRef* ref = mConnectedZoneSpaces; ref != NULL; ) + { + ZoneSpaceRef* next = ref->mNext; + smZoneSpaceRefChunker.free( ref ); + ref = next; + } + mConnectedZoneSpaces = NULL; +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpace::_addOccluder( SceneObject* object ) +{ + AssertFatal( !mOccluders.contains( object ), "SceneZoneSpace::_addOccluder - Occluder already added to this zone space!" ); + mOccluders.push_back( object ); +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpace::_removeOccluder( SceneObject* object ) +{ + const U32 numOccluders = mOccluders.size(); + for( U32 i = 0; i < numOccluders; ++ i ) + if( mOccluders[ i ] == object ) + { + mOccluders.erase_fast( i ); + break; + } + + AssertFatal( !mOccluders.contains( object ), "SceneZoneSpace::_removeOccluder - Occluder still added to this zone space!" ); +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpace::_addOccludersToCullingState( SceneCullingState* state ) const +{ + const U32 numOccluders = mOccluders.size(); + for( U32 i = 0; i < numOccluders; ++ i ) + state->addOccluder( mOccluders[ i ] ); +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpace::_traverseConnectedZoneSpaces( SceneTraversalState* state ) +{ + // Hand the traversal over to all connected zone spaces. + + for( ZoneSpaceRef* ref = mConnectedZoneSpaces; ref != NULL; ref = ref->mNext ) + { + SceneZoneSpace* zoneSpace = ref->mZoneSpace; + zoneSpace->traverseZones( state ); + } +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpace::dumpZoneState( bool update ) +{ + // Nothing to dump if not registered. + + if( !mManager ) + return; + + // If we should update, trigger rezoning for the space + // we occupy. + + if( update ) + mManager->_rezoneObjects( getWorldBox() ); + + Con::printf( "====== Zones in: %s =====", describeSelf().c_str() ); + + // Dump connections. + + for( ZoneSpaceRef* ref = mConnectedZoneSpaces; ref != NULL; ref = ref->mNext ) + Con::printf( "Connected to: %s", ref->mZoneSpace->describeSelf().c_str() ); + + // Dump objects. + + for( U32 i = 0; i < getZoneRange(); ++ i ) + { + U32 zoneId = getZoneRangeStart() + i; + + Con::printf( "--- Zone %i", zoneId ); + + for( SceneZoneSpaceManager::ZoneContentIterator iter( mManager, zoneId, false ); iter.isValid(); ++ iter ) + Con::printf( iter->describeSelf() ); + } +} + +//----------------------------------------------------------------------------- + +bool SceneZoneSpace::_setZoneGroup( void* object, const char* index, const char* data ) +{ + SceneZoneSpace* zone = reinterpret_cast< SceneZoneSpace* >( object ); + zone->setZoneGroup( EngineUnmarshallData< S32 >()( data ) ); + return false; +} diff --git a/Engine/source/scene/zones/sceneZoneSpace.h b/Engine/source/scene/zones/sceneZoneSpace.h new file mode 100644 index 000000000..2b792a53d --- /dev/null +++ b/Engine/source/scene/zones/sceneZoneSpace.h @@ -0,0 +1,298 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SCENEZONESPACE_H_ +#define _SCENEZONESPACE_H_ + +#ifndef _SCENESPACE_H_ +#include "scene/sceneSpace.h" +#endif + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + + +class SceneZoneSpaceManager; +class SceneCullingState; + + +/// Abstract base class for an object that manages zones in a scene. +/// +/// This class adds the ability to SceneSpace to define and manage zones within the object's +/// space. Zones are used to determine visibility in a scene. +/// +/// Each zone space manages one or more zones in a scene. All the zones must be within the +/// AABB of the zone space but within that AABB, the zone space is free to distribute and +/// manage zones in arbitrary fashion. +/// +/// For scene traversal, zone spaces are interconnected. By default, zone spaces get a chance +/// to connect to each other when being moved into each other's zones. An exception to this +/// is the root zone since it is both immobile and without position and (limited) extents. If +/// a zone space wants to connect to the root zone, it must do so manually (same goes for +/// disconnecting). +class SceneZoneSpace : public SceneSpace +{ + public: + + typedef SceneSpace Parent; + + friend class SceneZoneSpaceManager; + friend class SceneRootZone; // mZoneFlags, connectZoneSpace, disconnectZoneSpace + + enum + { + InvalidZoneGroup = 0 + }; + + enum ZoneFlags + { + /// Whether this zoning space is "closed off" or not. When connections between + /// zoning spaces are established, by default only spaces that are closed off are + /// connected to those that are *not* and vice versa. This defines a natural progression + /// where spaces that explicitly want to propagate traversals are connected to those + /// that want to contain it. + /// + /// This flag is set by default. + ZoneFlag_IsClosedOffSpace = BIT( 0 ), + }; + + protected: + + enum + { + ZoneGroupMask = Parent::NextFreeMask << 0, + NextFreeMask = Parent::NextFreeMask << 1 + }; + + /// The manager to which this zone space is registered. + SceneZoneSpaceManager* mManager; + + /// ID of first zone defined by object. + U32 mZoneRangeStart; + + /// Number of zones managed by #obj. IDs are consecutive + /// starting with #mZoneRangeStart. + U32 mNumZones; + + /// Group which the zone is assigned to. Zone spaces that would normally not connect + /// do connect if they are assigned the same zone group. O to disable (default). + U32 mZoneGroup; + + /// + BitSet32 mZoneFlags; + + /// @name Occluders + /// + /// Zone spaces keep track of the occluders that get added to them so that during + /// traversal, they can be taken on board as early as possible. This allows traversals + /// themselves to take occlusion into account. + /// + /// @{ + + /// Occluders in this space. Every object that is assigned to this space + /// and has the OccluderObjectType flag set, is added to this list. + Vector< SceneObject* > mOccluders; + + /// Add the given object to the list of occluders in this zone space. + void _addOccluder( SceneObject* object ); + + /// Remove the given object from the list of occluders in this zone space. + void _removeOccluder( SceneObject* object ); + + /// Register the occluders in this zone with the given culling state. + void _addOccludersToCullingState( SceneCullingState* state ) const; + + /// @} + + /// @name Zone Space Connectivity + /// @{ + + //TODO: we should have both automatic and manual connections; only automatic connections + // should get reset when a zone space moves + + /// Link to another zone space. + struct ZoneSpaceRef + { + // We could be storing zone IDs here to connect individual zones rather than just + // the managers but since no one requires this at the moment, the code doesn't do it. + // However, it's trivial to add. + + SceneZoneSpace* mZoneSpace; + ZoneSpaceRef* mNext; + }; + + /// List of zone spaces that this space is connected to. This is used + /// for traversals. + ZoneSpaceRef* mConnectedZoneSpaces; + + /// Allocator for ZoneSpaceRefs. + static ClassChunker< ZoneSpaceRef > smZoneSpaceRefChunker; + + /// Disconnect all zone spaces currently connected to this space. + virtual void _disconnectAllZoneSpaces(); + + /// Hand the traversal over to connected zone spaces. + virtual void _traverseConnectedZoneSpaces( SceneTraversalState* state ); + + /// Return true if the given zone space, which has crossed into this space, should + /// be automatically connected. Note that event + virtual bool _automaticallyConnectZoneSpace( SceneZoneSpace* zoneSpace ) const; + + /// @} + + /// @name SceneManager Notifications + /// + /// These methods are called when SceneManager assigns or removes objects to/from our zones. + /// + /// @{ + + /// Called by the SceneManager when an object is added to one or more zones that + /// are managed by this object. + virtual void _onZoneAddObject( SceneObject* object, const U32* zoneIDs, U32 numZones ); + + /// Called by the SceneManager when an object that was previously added to one or more + /// zones managed by this object is now removed. + virtual void _onZoneRemoveObject( SceneObject* object ); + + /// @} + + // SceneObject. + virtual void onSceneRemove(); + + public: + + SceneZoneSpace(); + virtual ~SceneZoneSpace(); + + /// Return true if this is the outdoor zone. + bool isRootZone() const { return ( getZoneRangeStart() == 0 ); } + + /// Gets the index of the first zone this object manages in the collection of zones or 0xFFFFFFFF if the + /// object is not managing zones. + U32 getZoneRangeStart() const { return mZoneRangeStart; } + + /// Return the number of zones that are managed by this object. + U32 getZoneRange() const { return mNumZones; } + + /// Return the zone group that this zone space belongs to. 0 by default which means the + /// zone space is not allocated to a specific zone group. + U32 getZoneGroup() const { return mZoneGroup; } + + /// Set the zone group of this zone space. Zone spaces in the same group will connect even if + /// not connecting by default. Set to 0 to deactivate. + void setZoneGroup( U32 group ); + + /// Dump a listing of all objects assigned to this zone space to the console as well + /// as a list of all connected spaces. + /// + /// @param update Whether to update the zone contents before dumping. Since the zoning states of + /// SceneObjects are updated lazily, the contents of a zone can be outdated. + void dumpZoneState( bool update = true ); + + /// Get the ambient light color of the given zone in this space or return false if the + /// given zone does not have an ambient light color assigned to it. + virtual bool getZoneAmbientLightColor( U32 zone, ColorF& outColor ) const { return false; } + + /// @name Containment Tests + /// @{ + + /// + virtual bool getOverlappingZones( const Box3F& aabb, U32* zones, U32& numZones ) = 0; + + /// Find the zones in this object that @a obj is part of. + /// + /// @param obj Object in question. + /// @param outZones Indices of zones containing the object. Must have at least as many entries + /// as there as zones in this object or SceneObject::MaxObjectZones, whichever is smaller. + /// Note that implementations should never write more than SceneObject::MaxObjectZones entries. + /// @param outNumZones Number of elements in the returned array. + /// + /// @return Return true if the world box of @a obj is fully contained within the zones of this object or + /// false if it is at least partially outside of them. + virtual bool getOverlappingZones( SceneObject* obj, U32* outZones, U32& outNumZones ); + + /// Returns the ID of the zone that are managed by this object that contains @a p. + /// @param p Point to test. + /// @return ID of the zone containing @a p or InvalidZoneId if none of the zones defined by this + /// object contain the point. + virtual U32 getPointZone( const Point3F& p ) = 0; + + /// @} + + /// @name Connectivity + /// @{ + + /// Connect this zone space to the given zone space. + /// + /// @param zoneSpace A zone space. + /// + /// @note Connectivity is reset when a zone space is moved! + virtual void connectZoneSpace( SceneZoneSpace* zoneSpace ); + + /// If the object is a zone space, then this method is called to instruct the object + /// to remove any zone connectivity to @a zoneSpace. + /// + /// @param zoneSpace A zone space which had previously been passed to connectZoneSpace(). + virtual void disconnectZoneSpace( SceneZoneSpace* zoneSpace ); + + /// @} + + /// @name Traversals + /// @{ + + /// Traverse into the zones of this space. Set the render states of those zones and add the frustums + /// that lead into them. + /// + /// The traversal stack is expected to not be empty and the topmost entry on the stack should be the + /// zone of another manager from which traversal is handed over to this manager. + /// + /// @param state Scene traversal state. + /// + /// @note If the zone's of a zone space are reached via different traversal paths, this method + /// will be called multiple times on the same space. + virtual void traverseZones( SceneTraversalState* state ) = 0; + + /// Traverse the zones in this space starting with the given zone. Where appropriate, traversal + /// should be handed off to connected spaces. + /// + /// @param state State which should be updated by the traversal. + /// @param startZoneId ID of zone in this manager in which to start traversal. + virtual void traverseZones( SceneTraversalState* state, U32 startZoneId ) = 0; + + /// @} + + // SimObject. + virtual bool writeField( StringTableEntry fieldName, const char* value ); + + static void initPersistFields(); + + // NetObject. + virtual U32 packUpdate( NetConnection* connection, U32 mask, BitStream* stream ); + virtual void unpackUpdate( NetConnection* connection, BitStream* stream ); + + private: + + static bool _setZoneGroup( void* object, const char* index, const char* data ); +}; + +#endif // !_SCENEZONESPACE_H_ diff --git a/Engine/source/scene/zones/sceneZoneSpaceManager.cpp b/Engine/source/scene/zones/sceneZoneSpaceManager.cpp new file mode 100644 index 000000000..ab0fa6ebe --- /dev/null +++ b/Engine/source/scene/zones/sceneZoneSpaceManager.cpp @@ -0,0 +1,923 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "scene/zones/sceneZoneSpaceManager.h" + +#include "platform/profiler.h" +#include "platform/platformMemory.h" +#include "scene/sceneContainer.h" +#include "scene/zones/sceneRootZone.h" +#include "scene/zones/sceneZoneSpace.h" + + +// Uncomment to enable verification code for debugging. This slows the +// manager down significantly but will allow to find zoning state corruption +// much quicker. +//#define DEBUG_VERIFY + +//#define DEBUG_SPEW + + +ClassChunker< SceneObject::ZoneRef > SceneZoneSpaceManager::smZoneRefChunker; + + +//----------------------------------------------------------------------------- + +SceneZoneSpaceManager::SceneZoneSpaceManager( SceneContainer* container ) + : mContainer( container ), + mRootZone( new SceneRootZone() ), + mNumTotalAllocatedZones( 0 ), + mNumActiveZones( 0 ), + mDirtyArea( Box3F::Invalid ) +{ + VECTOR_SET_ASSOCIATION( mZoneSpaces ); + VECTOR_SET_ASSOCIATION( mZoneLists ); + VECTOR_SET_ASSOCIATION( mZoneSpacesQueryList ); + VECTOR_SET_ASSOCIATION( mDirtyObjects ); + VECTOR_SET_ASSOCIATION( mDirtyZoneSpaces ); +} + +//----------------------------------------------------------------------------- + +SceneZoneSpaceManager::~SceneZoneSpaceManager() +{ + // Delete root zone. + SAFE_DELETE( mRootZone ); + + mNumTotalAllocatedZones = 0; + mNumActiveZones = 0; +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpaceManager::registerZones( SceneZoneSpace* object, U32 numZones ) +{ + AssertFatal( _getZoneSpaceIndex( object ) == -1, "SceneZoneSpaceManager::registerZones - Object already registered" ); + _compactZonesCheck(); + + const U32 zoneRangeStart = mNumTotalAllocatedZones; + + mNumTotalAllocatedZones += numZones; + mNumActiveZones += numZones; + + object->mNumZones = numZones; + object->mZoneRangeStart = zoneRangeStart; + + // Allocate zone lists for all of the zones managed by the object. + // Add an entry to each list that points back to the zone space. + + mZoneLists.increment( numZones ); + for( U32 i = zoneRangeStart; i < mNumTotalAllocatedZones; ++ i ) + { + SceneObject::ZoneRef* zoneRef = smZoneRefChunker.alloc(); + + zoneRef->object = object; + zoneRef->nextInBin = NULL; + zoneRef->prevInBin = NULL; + zoneRef->nextInObj = NULL; + zoneRef->zone = i; + + mZoneLists[ i ] = zoneRef; + } + + // Add space to list. + + mZoneSpaces.push_back( object ); + object->mManager = this; + + // Set ZoneObjectType. + + object->mTypeMask |= ZoneObjectType; + + // Put the object on the dirty list. + + if( !object->isRootZone() ) + { + // Make sure the object gets on the zone space list even + // if it is already on the object dirty list. + object->mZoneRefDirty = false; + + notifyObjectChanged( object ); + } + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SceneZoneSpaceManager] Range %i-%i allocated to: %s", + zoneRangeStart, numZones, object->describeSelf().c_str() ); + #endif +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpaceManager::unregisterZones( SceneZoneSpace* object ) +{ + S32 zoneSpaceIndex = _getZoneSpaceIndex( object ); + + AssertFatal( zoneSpaceIndex != -1, "SceneZoneSpaceManager::unregisterZones - Object not registered as zone space" ); + AssertFatal( mNumActiveZones >= object->mNumZones, "SceneZoneSpaceManager::unregisterZones - Too many zones removed"); + + const U32 zoneRangeStart = object->getZoneRangeStart(); + const U32 numZones = object->getZoneRange(); + + // Destroy the zone lists for the zones registered + // by the object. + + for( U32 j = zoneRangeStart; j < zoneRangeStart + numZones; j ++ ) + { + // Delete all object links. + + _clearZoneList( j ); + + // Delete the first link which refers to the zone itself. + + smZoneRefChunker.free( mZoneLists[ j ] ); + mZoneLists[ j ] = NULL; + } + + // Destroy the connections the zone space has. + + object->_disconnectAllZoneSpaces(); + + // Remove the zone manager entry. + + mNumActiveZones -= numZones; + mZoneSpaces.erase( zoneSpaceIndex ); + + // Clear ZoneObjectType. + + object->mTypeMask &= ~ZoneObjectType; + + // Clear zone assignments. + + object->mZoneRangeStart = InvalidZoneId; + object->mNumZones = 0; + object->mManager = NULL; + + // Mark the zone space's area as dirty. + + mDirtyArea.intersect( object->getWorldBox() ); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SceneZoneSpaceManager] Range %i-%i released from: %s", + zoneRangeStart, numZones, object->describeSelf().c_str() ); + #endif +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpaceManager::_rezoneObjects( const Box3F& area ) +{ + static Vector< SceneObject* > sObjects( __FILE__, __LINE__ ); + + // Find all objects in the area. We cannot use the callback + // version here and directly trigger rezoning since the rezoning + // itself does a container query. + + sObjects.clear(); + mContainer->findObjectList( area, 0xFFFFFFFF, &sObjects ); + + // Rezone the objects. + + const U32 numObjects = sObjects.size(); + for( U32 i = 0; i < numObjects; ++ i ) + { + SceneObject* object = sObjects[ i ]; + if( object != getRootZone() ) + _rezoneObject( object ); + } +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpaceManager::_compactZonesCheck() +{ + if( mNumActiveZones > ( mNumTotalAllocatedZones / 2 ) ) + return; + + // Redistribute the zone IDs among the current zone spaces + // so that the range of IDs is consecutive. + + const U32 numZoneSpaces = mZoneSpaces.size(); + U32 nextZoneId = 0; + + Vector< SceneObject::ZoneRef* > newZoneLists; + newZoneLists.setSize( mNumActiveZones ); + + for( U32 i = 0; i < numZoneSpaces; ++ i ) + { + SceneZoneSpace* space = mZoneSpaces[ i ]; + + const U32 oldZoneRangeStart = space->getZoneRangeStart(); + const U32 newZoneRangeStart = nextZoneId; + const U32 numZones = space->getZoneRange(); + + // Assign the new zone range start. + + space->mZoneRangeStart = newZoneRangeStart; + nextZoneId += numZones; + + // Relocate the zone lists to match the new zone IDs and update + // the contents of the zone lists to match the new IDs. + + for( U32 n = 0; n < numZones; ++ n ) + { + const U32 newZoneId = newZoneRangeStart + n; + const U32 oldZoneId = oldZoneRangeStart + n; + + // Relocate list. + + newZoneLists[ newZoneId ] = mZoneLists[ oldZoneId ]; + + // Update entries. + + for( SceneObject::ZoneRef* ref = newZoneLists[ newZoneId ]; ref != NULL; ref = ref->nextInBin ) + ref->zone = newZoneId; + } + } + + mNumTotalAllocatedZones = nextZoneId; + mZoneLists = newZoneLists; + + AssertFatal( mNumTotalAllocatedZones == mNumActiveZones, "SceneZoneSpaceManager::_compactZonesCheck - Error during compact; mismatch between active and allocated zones" ); +} + +//----------------------------------------------------------------------------- + +S32 SceneZoneSpaceManager::_getZoneSpaceIndex( SceneZoneSpace* object ) const +{ + const U32 numZoneSpaces = getNumZoneSpaces(); + for( U32 i = 0; i < numZoneSpaces; ++ i ) + if( mZoneSpaces[ i ] == object ) + return i; + + return -1; +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpaceManager::findZone( const Point3F& p, SceneZoneSpace*& owner, U32& zone ) const +{ + AssertFatal( mNumActiveZones >= 1, "SceneZoneSpaceManager::findZone - Must have at least one active zone in scene (outdoor zone)" ); + + // If there are no zones in the level other than the outdoor + // zone, just return that. + + if( mNumActiveZones == 1 ) + { + owner = getRootZone(); + zone = RootZoneId; + + return; + } + + PROFILE_SCOPE( SceneZoneSpaceManager_findZone ); + + // Query the scene container for zones with a query + // box that tightly fits around the point. + + Box3F queryBox( p.x - 0.1f, p.y - 0.1f, p.z - 0.1f, + p.x + 0.1f, p.y + 0.1f, p.z + 0.1f ); + + _queryZoneSpaces( queryBox ); + + // Go through the zones and look for the first one that + // contains the given point. + + const U32 numZones = mZoneSpacesQueryList.size(); + for( U32 i = 0; i < numZones; ++ i ) + { + SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( mZoneSpacesQueryList[ i ] ); + if( !zoneSpace ) + continue; + + AssertFatal( zoneSpace != getRootZone(), "SceneZoneSpaceManager::findZone - SceneRootZone returned by zone manager query" ); + + // If the point is in one of the zones of this manager, + // then make this the result. + + U32 inZone = zoneSpace->getPointZone( p ); + if( inZone != InvalidZoneId ) + { + owner = zoneSpace; + zone = inZone; + + return; + } + } + + // No other zone matched so return the outdoor zone. + + owner = getRootZone(); + zone = RootZoneId; +} + +//----------------------------------------------------------------------------- + +U32 SceneZoneSpaceManager::findZones( const Box3F& area, Vector< U32 >& outZones ) const +{ + // Query all zone spaces in the area. + + _queryZoneSpaces( area ); + + // Query each zone space for overlaps with the given + // area and add the zones to outZones. + + bool outsideIncluded = false; + U32 numTotalZones = 0; + + const U32 numZoneSpaces = mZoneSpacesQueryList.size(); + for( U32 i = 0; i < numZoneSpaces; ++ i ) + { + SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( mZoneSpacesQueryList[ i ] ); + if( !zoneSpace ) + continue; + + AssertFatal( zoneSpace != getRootZone(), "SceneZoneSpaceManager::findZones - SceneRootZone returned by zone manager query" ); + + // Query manager. + + U32 zones[ SceneObject::MaxObjectZones ]; + U32 numZones = 0; + + outsideIncluded |= zoneSpace->getOverlappingZones( area, zones, numZones ); + + // Add overlapped zones. + + for( U32 n = 0; n < numZones; n ++ ) + { + outZones.push_back( zones[ n ] ); + numTotalZones ++; + } + } + + // If the area box wasn't fully enclosed by the zones of the + // manager(s) or the query only returned the outside zone, + // add the outside zone to the list. + + if( outsideIncluded || numTotalZones == 0 ) + { + outZones.push_back( RootZoneId ); + numTotalZones ++; + } + + return numTotalZones; +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpaceManager::_rezoneObject( SceneObject* object ) +{ + PROFILE_SCOPE( SceneZoneSpaceManager_rezoneObject ); + + AssertFatal( !dynamic_cast< SceneRootZone* >( object ), "SceneZoneSpaceManager::_rezoneObject - Cannot rezone the SceneRootZone!" ); + + // If the object is not yet assigned to zones, + // do so now and return. + + if( !object->mNumCurrZones ) + { + _zoneInsert( object ); + return; + } + + // If we have no zones in the scene other than the outdoor zone or if the + // object has global bounds on (and thus is always in the outdoor zone) or + // is an object that is restricted to the outdoor zone, leave the object's + // zoning state untouched. + + if( mNumActiveZones == 1 || object->isGlobalBounds() || object->getTypeMask() & OUTDOOR_OBJECT_TYPEMASK ) + { + object->mZoneRefDirty = false; + return; + } + + // First, find out whether there's even a chance of the zoning to have changed + // for the object. + + _queryZoneSpaces( object->getWorldBox() ); + + const U32 numZoneSpaces = mZoneSpacesQueryList.size(); + if( !numZoneSpaces ) + { + // There is no zone in the object's area. If it is already assigned to the + // root zone, then we don't need an update. Otherwise, we do. + + if( object->mNumCurrZones == 1 && + object->mZoneRefHead && + object->mZoneRefHead->zone == RootZoneId ) + { + object->mZoneRefDirty = false; + return; + } + } + + // Update the object's zoning information by removing and recomputing + // its zoning information. + + _zoneRemove( object ); + _zoneInsert( object, true ); // Query already in place. +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpaceManager::registerObject( SceneObject* object ) +{ + // Just put it on the dirty list. + + notifyObjectChanged( object ); +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpaceManager::unregisterObject( SceneObject* object ) +{ + // Remove from dirty list. + + mDirtyObjects.remove( object ); + + // Remove from zone lists. + + _zoneRemove( object ); + + // If it's a zone space, unregister it. + + if( object->getTypeMask() & ZoneObjectType && dynamic_cast< SceneZoneSpace* >( object ) ) + { + SceneZoneSpace* zoneSpace = static_cast< SceneZoneSpace* >( object ); + unregisterZones( zoneSpace ); + mDirtyZoneSpaces.remove( zoneSpace ); + } +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpaceManager::updateObject( SceneObject* object ) +{ + // If no zone spaces have changed and the object's zoning + // state is clean, then there's nothing to do for this object. + + if( mDirtyZoneSpaces.empty() && !object->mZoneRefDirty ) + return; + + // Otherwise update all the dirty zoning state. + + updateZoningState(); +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpaceManager::notifyObjectChanged( SceneObject* object ) +{ + AssertFatal( object != getRootZone(), "SceneZoneSpaceManager::notifyObjectChanged - Cannot dirty root zone!" ); + + // Ignore if object is already on the dirty list. + + if( object->mZoneRefDirty ) + return; + + // Put the object on the respective dirty list. + + if( object->getTypeMask() & ZoneObjectType ) + { + SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( object ); + AssertFatal( zoneSpace != NULL, "SceneZoneSpaceManager::notifyObjectChanged - ZoneObjectType is not a SceneZoneSpace!" ); + + if( zoneSpace ) + mDirtyZoneSpaces.push_back( zoneSpace ); + } + else + { + mDirtyObjects.push_back( object ); + } + + // Mark object as dirty. + + object->mZoneRefDirty = true; +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpaceManager::updateZoningState() +{ + // If there are no dirty objects, there's nothing to do. + + if( mDirtyObjects.empty() && + mDirtyZoneSpaces.empty() && + mDirtyArea == Box3F::Invalid ) + return; + + // Otherwise, first update the zone spaces. Do this in two passes: + // first take all the dirty zone spaces out of the zoning state and + // then rezone the combined area of all dirty zone spaces. + // + // Note that this path here is pretty much only relevant during loading + // or editing and thus can be less performant than the path for individual + // objects below. + + while( !mDirtyZoneSpaces.empty() ) + { + SceneZoneSpace* zoneSpace = mDirtyZoneSpaces.last(); + mDirtyZoneSpaces.decrement(); + + // Remove the zoning state of the object. + + _zoneRemove( zoneSpace ); + + // Destroy all connections that this zone space has to + // other zone spaces. + + zoneSpace->_disconnectAllZoneSpaces(); + + // Nuke its zone lists. + + const U32 numZones = zoneSpace->getZoneRange(); + for( U32 n = 0; n < numZones; ++ n ) + _clearZoneList( zoneSpace->getZoneRangeStart() + n ); + + // Merge into dirty region. + + mDirtyArea.intersect( zoneSpace->getWorldBox() ); + } + + if( mDirtyArea != Box3F::Invalid ) + { + // Rezone everything in the dirty region. + + _rezoneObjects( mDirtyArea ); + mDirtyArea = Box3F::Invalid; + + // Verify zoning state. + + #ifdef DEBUG_VERIFY + verifyState(); + #endif + + // Fire the zoning changed signal to let interested parties + // know that the zoning setup of the scene has changed. + + getZoningChangedSignal().trigger( this ); + } + + // And finally, update objects that have changed state. + + while( !mDirtyObjects.empty() ) + { + SceneObject* object = mDirtyObjects.last(); + mDirtyObjects.decrement(); + + if( object->mZoneRefDirty ) + _rezoneObject( object ); + + AssertFatal( !object->mZoneRefDirty, "SceneZoneSpaceManager::updateZoningState - Object still dirty!" ); + } + + AssertFatal( mDirtyObjects.empty(), "SceneZoneSpaceManager::updateZoningState - Still have dirty objects!" ); + AssertFatal( mDirtyZoneSpaces.empty(), "SceneZoneSpaceManager::updateZoningState - Still have dirty zones!" ); +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpaceManager::_zoneInsert( SceneObject* object, bool queryListInitialized ) +{ + PROFILE_SCOPE( SceneZoneSpaceManager_zoneInsert ); + + AssertFatal( object->mNumCurrZones == 0, "SceneZoneSpaceManager::_zoneInsert - Object already in zone list" ); + AssertFatal( object->getContainer() != NULL, "SceneZoneSpaceManager::_zoneInsert - Object must be in scene" ); + AssertFatal( !dynamic_cast< SceneRootZone* >( object ), "SceneZoneSpaceManager::_zoneInsert - Must not be called on SceneRootZone" ); + + // If all we have is a single zone in the scene, then it must + // be the outdoor zone. Simply assign the object to it. Also do this + // if the object has global bounds on since we always assign these to + // just the outdoor zone. Finally, also do it for all object types that + // we want to restrict to the outdoor zone. + + if( mNumActiveZones == 1 || object->isGlobalBounds() || object->getTypeMask() & OUTDOOR_OBJECT_TYPEMASK ) + _addToOutdoorZone( object ); + else + { + // Otherwise find all zones spaces that intersect with the object's + // world box. + + if( !queryListInitialized ) + _queryZoneSpaces( object->getWorldBox() ); + + // Go through the zone spaces and link all zones that the object + // overlaps. + + bool outsideIncluded = true; + const U32 numZoneSpaces = mZoneSpacesQueryList.size(); + for( U32 i = 0; i < numZoneSpaces; ++ i ) + { + SceneZoneSpace* zoneSpace = dynamic_cast< SceneZoneSpace* >( mZoneSpacesQueryList[ i ] ); + if( !zoneSpace ) + continue; + + AssertFatal( zoneSpace != getRootZone(), "SceneZoneSpaceManager::_zoneInsert - SceneRootZone returned by zone space query" ); + + // If we are inserting a zone space, then the query will turn up + // the object itself at some point. Skip it. + + if( zoneSpace == object ) + continue; + + // Find the zones that the object overlaps within + // the zone space. + + U32 numZones = 0; + U32 zones[ SceneObject::MaxObjectZones ]; + + bool overlapsOutside = zoneSpace->getOverlappingZones( object, zones, numZones ); + AssertFatal( numZones != 0 || overlapsOutside, + "SceneZoneSpaceManager::_zoneInsert - Object must be fully contained in one or more zones or intersect the outside zone" ); + + outsideIncluded &= overlapsOutside; // Only include outside if *none* of the zones fully contains the object. + + // Link the object to the zones. + + for( U32 n = 0; n < numZones; ++ n ) + _addToZoneList( zones[ n ], object ); + + // Let the zone manager know we have added objects to its + // zones. + + if( numZones > 0 ) + zoneSpace->_onZoneAddObject( object, zones, numZones ); + } + + // If the object crosses into the outside zone or hasn't been + // added to any zone above, add it to the outside zone. + + if( outsideIncluded ) + _addToOutdoorZone( object ); + } + + // Mark the zoning state of the object as current. + + object->mZoneRefDirty = false; +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpaceManager::_zoneRemove( SceneObject* obj ) +{ + PROFILE_SCOPE( SceneZoneSpaceManager_zoneRemove ); + + // Remove the object from the zone lists. + + for( SceneObject::ZoneRef* walk = obj->mZoneRefHead; walk != NULL; ) + { + // Let the zone owner know we are removing an object + // from its zones. + + getZoneOwner( walk->zone )->_onZoneRemoveObject( walk->object ); + + // Now remove the ZoneRef link this object has in the + // zone list of the current zone. + + SceneObject::ZoneRef* remove = walk; + walk = walk->nextInObj; + + remove->prevInBin->nextInBin = remove->nextInBin; + if( remove->nextInBin ) + remove->nextInBin->prevInBin = remove->prevInBin; + + smZoneRefChunker.free( remove ); + } + + // Clear the object's zoning state. + + obj->mZoneRefHead = NULL; + obj->mZoneRefDirty = false; + obj->mNumCurrZones = 0; +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpaceManager::_addToZoneList( U32 zoneId, SceneObject* object ) +{ + SceneObject::ZoneRef* zoneList = mZoneLists[ zoneId ]; + + AssertFatal( zoneList != NULL, "SceneZoneSpaceManager::_addToZoneList - Zone list not initialized" ); + AssertFatal( object != zoneList->object, "SCene::_addToZoneList - Cannot add zone to itself" ); + + SceneObject::ZoneRef* newRef = smZoneRefChunker.alloc(); + + // Add the object to the zone list. + + newRef->zone = zoneId; + newRef->object = object; + newRef->nextInBin = zoneList->nextInBin; + newRef->prevInBin = zoneList; + + if( zoneList->nextInBin ) + zoneList->nextInBin->prevInBin = newRef; + + zoneList->nextInBin = newRef; + + // Add the zone to the object list. + + newRef->nextInObj = object->mZoneRefHead; + object->mZoneRefHead = newRef; + object->mNumCurrZones ++; +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpaceManager::_clearZoneList( U32 zoneId ) +{ + AssertFatal( zoneId < getNumZones(), "SceneZoneSpaceManager::_clearZoneList - Zone ID out of range" ); + + SceneObject::ZoneRef* list = mZoneLists[ zoneId ]; + SceneZoneSpace* zoneSpace = getZoneOwner( zoneId ); + + // Go through the objects in the zone list and unlink and + // delete their zone entries. + + for( SceneObject::ZoneRef* walk = list->nextInBin; walk != NULL; walk = walk->nextInBin ) + { + SceneObject* object = walk->object; + AssertFatal( object != NULL, "SceneZoneSpaceManager::_clearZoneList - Object field not set on link" ); + + // The zone entry links on the objects are singly-linked lists + // linked through nextInObject so we need to find where in the + // objects zone entry list the node for the current zone is. + + SceneObject::ZoneRef** ptrNext = &object->mZoneRefHead; + while( *ptrNext && *ptrNext != walk ) + ptrNext = &( *ptrNext )->nextInObj; + + AssertFatal( *ptrNext == walk, "SceneZoneSpaceManager::_clearZoneList - Zone entry not found on object in zone list!"); + + // Unlink and delete the entry. + + *ptrNext = ( *ptrNext )->nextInObj; + smZoneRefChunker.free( walk ); + + object->mNumCurrZones --; + + // If this is the only zone the object was in, mark + // its zoning state as dirty so it will get assigned + // to the outdoor zone on the next update. + + if( !object->mZoneRefHead ) + object->mZoneRefDirty = true; + + // Let the zone know we have removed the object. + + zoneSpace->_onZoneRemoveObject( object ); + } + + list->nextInBin = NULL; +} + +//----------------------------------------------------------------------------- + +SceneObject::ZoneRef* SceneZoneSpaceManager::_findInZoneList( U32 zoneId, SceneObject* object ) const +{ + for( SceneObject::ZoneRef* ref = object->mZoneRefHead; ref != NULL; ref = ref->nextInObj ) + if( ref->zone == zoneId ) + return ref; + + return NULL; +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpaceManager::_addToOutdoorZone( SceneObject* object ) +{ + AssertFatal( !object->mZoneRefHead || !_findInZoneList( RootZoneId, object ), + "SceneZoneSpaceManager::_addToOutdoorZone - Object already added to outdoor zone" ); + + // Add the object to the outside's zone list. This method is always called + // *last* after the object has already been assigned to any other zone it + // intersects. Since we always prepend to the zoning lists, this means that + // the outdoor zone will always be *first* in the list of zones that an object + // is assigned to which generally is a good order. + + _addToZoneList( RootZoneId, object ); + + // Let the zone know we added an object to it. + + const U32 zoneId = RootZoneId; + static_cast< SceneZoneSpace* >( getRootZone() )->_onZoneAddObject( object, &zoneId, 1 ); +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpaceManager::_queryZoneSpaces( const Box3F& area ) const +{ + mZoneSpacesQueryList.clear(); + mContainer->findObjectList( area, ZoneObjectType, &mZoneSpacesQueryList ); +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpaceManager::dumpZoneStates( bool update ) +{ + if( update ) + _rezoneObjects( getRootZone()->getWorldBox() ); + + const U32 numZoneSpaces = mZoneSpaces.size(); + for( U32 i = 0; i < numZoneSpaces; ++ i ) + mZoneSpaces[ i ]->dumpZoneState( false ); +} + +//----------------------------------------------------------------------------- + +void SceneZoneSpaceManager::verifyState() +{ + AssertFatal( mZoneSpaces.size() <= mNumActiveZones, + "SceneZoneSpaceManager::verifyState - More zone spaces than active zones!" ); + AssertFatal( mNumTotalAllocatedZones >= mNumActiveZones, + "SceneZoneSpaceManager::verifyState - Fewer allocated than active zones!" ); + AssertFatal( mRootZone->getZoneRangeStart() == 0, + "SceneZoneSpaceManager::verifyState - Invalid ID on root zone!" ); + AssertFatal( mRootZone->getZoneRange() == 1, + "SceneZoneSpaceManager::verifyState - Invalid zone range on root zone!" ); + + // First validate the zone spaces themselves. + + const U32 numZoneSpaces = mZoneSpaces.size(); + for( U32 i = 0; i < numZoneSpaces; ++ i ) + { + SceneZoneSpace* space = mZoneSpaces[ i ]; + + #ifndef TORQUE_DISABLE_MEMORY_MANAGER + Memory::checkPtr( space ); + #endif + + AssertFatal( space->getTypeMask() & ZoneObjectType, "SceneZoneSpaceManager::verifyState - Zone space is not a ZoneObjectType!" ); + + const U32 zoneRangeStart = space->getZoneRangeStart(); + const U32 numZones = space->getZoneRange(); + + // Verify each of the allocated zones in this space. + + for( U32 n = 0; n < numZones; ++ n ) + { + const U32 zoneId = zoneRangeStart + n; + + // Simple validation of zone ID. + AssertFatal( isValidZoneId( zoneId ), "SceneZoneSpaceManager::verifyState - Zone space is assigned in invalid zone ID!" ); + + AssertFatal( mZoneLists[ zoneId ] != NULL, "SceneZoneSpaceManager::verifyState - Zone list missing for zone!" ); + AssertFatal( mZoneLists[ zoneId ]->object == space, "SceneZoneSpaceManager::verifyState - Zone list entry #0 is not referring back to zone!" ); + + for( SceneObject::ZoneRef* ref = mZoneLists[ zoneId ]; ref != NULL; ref = ref->nextInBin ) + { + AssertFatal( ref->zone == zoneId, "SceneZoneSpaceManager::verifyState - Incorrect ID in zone list!" ); + AssertFatal( ref->object != NULL, "SceneZoneSpaceManager::verifyState - Null object pointer in zone list!" ); + + #ifndef TORQUE_DISABLE_MEMORY_MANAGER + Memory::checkPtr( ref->object ); + #endif + } + } + + // Make sure no other zone space owns any of the same IDs. + + for( U32 n = 0; n < numZoneSpaces; ++ n ) + { + if( n == i ) + continue; + + SceneZoneSpace* otherSpace = mZoneSpaces[ n ]; + AssertFatal( otherSpace->getZoneRangeStart() >= zoneRangeStart + numZones || + otherSpace->getZoneRangeStart() + otherSpace->getZoneRange() <= zoneRangeStart, + "SceneZoneSpaceManager::verifyState - Overlap between zone ID ranges of zone spaces!" ); + } + + // Make sure that all zone connections appear to be valid. + + for( SceneZoneSpace::ZoneSpaceRef* ref = space->mConnectedZoneSpaces; ref != NULL; ref = ref->mNext ) + { + #ifndef TORQUE_DISABLE_MEMORY_MANAGER + Memory::checkPtr( ref->mZoneSpace ); + #endif + + AssertFatal( _getZoneSpaceIndex( ref->mZoneSpace ) != -1, "SceneZoneSpaceManager::verifyState - Zone connected to invalid zone!" ); + AssertFatal( ref->mZoneSpace->getTypeMask() & ZoneObjectType, "SceneZoneSpaceManager::verifyState - Zone space is not a ZoneObjectType!" ); + } + } + + //TODO: can do a lot more validation here +} diff --git a/Engine/source/scene/zones/sceneZoneSpaceManager.h b/Engine/source/scene/zones/sceneZoneSpaceManager.h new file mode 100644 index 000000000..51b696326 --- /dev/null +++ b/Engine/source/scene/zones/sceneZoneSpaceManager.h @@ -0,0 +1,331 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SCENEZONESPACEMANAGER_H_ +#define _SCENEZONESPACEMANAGER_H_ + +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +#ifndef _TSIGNAL_H_ +#include "core/util/tSignal.h" +#endif + +#ifndef _DATACHUNKER_H_ +#include "core/dataChunker.h" +#endif + + + +class SceneContainer; +class SceneRootZone; +class SceneZoneSpace; + + +/// Object that manages zone spaces in a scene. +class SceneZoneSpaceManager +{ + public: + + class ZoneContentIterator; + + friend class SceneZoneSpace; // mZoneLists + friend class ZoneContentIterator; // mZoneLists + + /// A signal used to notify that the zone setup of the scene has changed. + /// + /// @note If you use this signal to maintain state that depends on the zoning + /// setup, it's best to not immediately update the sate in response to this + /// signal. The reason is that during loading and editing, the signal may + /// be fired a lot and continuously updating dependent data may waste a lot + /// of time. + typedef Signal< void( SceneZoneSpaceManager* ) > ZoningChangedSignal; + + enum + { + /// Zone ID of the exterior zone. + RootZoneId = 0, + + /// Constant to indicate an invalid zone ID. + InvalidZoneId = 0xFFFFFFFF, + }; + + /// Iterator for the contents of a given zone. + class ZoneContentIterator + { + public: + + ZoneContentIterator( SceneZoneSpaceManager* manager, int zoneId, bool upToDate = true ) + { + AssertFatal( zoneId < manager->getNumZones(), "SceneZoneSpaceManager::ZoneContentIterator - Zone ID out of range" ); + + if( upToDate ) + { + // Since zoning is updated lazily, the zone contents may actually + // be out of date. Force an update by triggering rezoning on the + // zone object. This is brute-force but this iterator is not meant + // to be used for high-frequency code anyway. + // + // Use the area-based rezoning so that we can also properly iterate + // over the contents of SceneRootZone. + manager->_rezoneObjects( ( ( SceneObject* ) manager->getZoneOwner( zoneId ) )->getWorldBox() ); + } + + mCurrent = manager->mZoneLists[ zoneId ]->nextInBin; // Skip zone object entry. + } + + bool isValid() const + { + return ( mCurrent != NULL ); + } + bool operator !() const + { + return ( mCurrent == NULL ); + } + ZoneContentIterator& operator ++() + { + if( mCurrent ) + mCurrent = mCurrent->nextInBin; + return *this; + } + ZoneContentIterator& operator --() + { + if( mCurrent ) + mCurrent = mCurrent->prevInBin; + return *this; + } + SceneObject* operator *() const + { + AssertFatal( mCurrent != NULL, "SceneManager::ZoneContentIterator::operator* - Invalid iterator" ); + return mCurrent->object; + } + SceneObject* operator ->() const + { + AssertFatal( mCurrent != NULL, "SceneManager::ZoneContentIterator::operator-> - Invalid iterator" ); + return mCurrent->object; + } + + private: + + SceneObject::ZoneRef* mCurrent; + }; + + protected: + + /// The root and outdoor zone of the scene. + SceneRootZone* mRootZone; + + /// Scene container that holds the zone spaces we are managing. + SceneContainer* mContainer; + + /// Collection of objects that manage zones. + Vector< SceneZoneSpace* > mZoneSpaces; + + /// Total number of zones that have been allocated in the scene. + U32 mNumTotalAllocatedZones; + + /// Number of zone IDs that are in active use. + U32 mNumActiveZones; + + /// Object list for each zone in the scene. + /// First entry in the list points back to the zone manager. + Vector< SceneObject::ZoneRef* > mZoneLists; + + /// Vector used repeatedly for zone space queries on the container. + mutable Vector< SceneObject* > mZoneSpacesQueryList; + + /// Allocator for ZoneRefs. + static ClassChunker< SceneObject::ZoneRef > smZoneRefChunker; + + /// @name Dirty Lists + /// Updating the zoning state of a scene is done en block rather than + /// individually for each object as it changes transforms or size. + /// @{ + + /// Area of the scene that needs to be rezoned. + Box3F mDirtyArea; + + /// List of zone spaces that have changed state and need updating. + Vector< SceneZoneSpace* > mDirtyZoneSpaces; + + /// List of objects (non-zone spaces) that have changed state and need + /// updating. + Vector< SceneObject* > mDirtyObjects; + + /// @} + + /// Check to see if we have accumulated a lot of unallocate zone IDs and if so, + /// compact the zoning lists by reassigning IDs. + /// + /// @warn This method may alter all zone IDs in the scene! + void _compactZonesCheck(); + + /// Return the index into #mZoneSpaces for the given object or -1 if + /// @object is not a zone manager. + S32 _getZoneSpaceIndex( SceneZoneSpace* object ) const; + + /// Attach zoning state to the given object. + void _zoneInsert( SceneObject* object, bool queryListInitialized = false ); + + /// Detach zoning state from the given object. + void _zoneRemove( SceneObject* object ); + + /// Add to given object to the zone list of the given zone. + void _addToZoneList( U32 zoneId, SceneObject* object ); + + /// Clear all objects assigned to the given zone. + /// @note This does not remove the first link in the zone list which is the link + /// back to the zone manager. + void _clearZoneList( U32 zoneId ); + + /// Find the given object in the zone list of the given zone. + SceneObject::ZoneRef* _findInZoneList( U32 zoneId, SceneObject* object ) const; + + /// Assign the given object to the outdoor zone. + void _addToOutdoorZone( SceneObject* object ); + + /// Rezone all objects in the given area. + void _rezoneObjects( const Box3F& area ); + + /// Update the zoning state of the given object. + void _rezoneObject( SceneObject* object ); + + /// Fill #mZoneSpacesQueryList with all ZoneObjectType objects in the given area. + void _queryZoneSpaces( const Box3F& area ) const; + + public: + + SceneZoneSpaceManager( SceneContainer* container ); + ~SceneZoneSpaceManager(); + + /// Bring the zoning state of the scene up to date. This will cause objects + /// that have moved or have been resized to be rezoned and will updated regions + /// of the scene that had their zoning setup changed. + /// + /// @note This method depends on proper use of notifyObjectChanged(). + void updateZoningState(); + + /// @name Objects + /// @{ + + /// Add zoning state to the given object. + void registerObject( SceneObject* object ); + + /// Remove the given object from the zoning state. + void unregisterObject( SceneObject* object ); + + /// Let the manager know that state relevant to zoning of the given + /// object has changed. + void notifyObjectChanged( SceneObject* object ); + + /// Update the zoning state of the given object. + void updateObject( SceneObject* object ); + + /// @} + + /// @name Zones + /// @{ + + /// Return the root zone of the scene. + SceneRootZone* getRootZone() const { return mRootZone; } + + /// Register a zone manager. + /// + /// @param object SceneZoneSpace object that contains zones. + /// @param numZones Number of zones that @a object contains. + void registerZones( SceneZoneSpace* object, U32 numZones ); + + /// Unregister a zone manager. + /// + /// @param object Object that contains zones. + void unregisterZones( SceneZoneSpace* object ); + + /// Return true if the given ID belongs to a currently registered zone. + bool isValidZoneId( const U32 zoneId ) const + { + return ( zoneId < mNumTotalAllocatedZones && mZoneLists[ zoneId ] ); + } + + /// Get the scene object that contains the zone with the given ID. + /// + /// @param zoneId ID of the zone. Must be valid. + /// @return The zone space that has registered the given zone. + SceneZoneSpace* getZoneOwner( const U32 zoneId ) const + { + AssertFatal( isValidZoneId( zoneId ), "SceneManager::getZoneOwner - Invalid zone ID!"); + return ( SceneZoneSpace* ) mZoneLists[ zoneId ]->object; + } + + /// Return the total number of zones in the scene. + U32 getNumZones() const { return mNumTotalAllocatedZones; } + + /// Return the effective amount of used zone IDs in the scene. + U32 getNumActiveZones() const { return mNumActiveZones; } + + /// Return the total number of objects in the scene that manage zones. + U32 getNumZoneSpaces() const { return mZoneSpaces.size(); } + + /// Find the zone that contains the given point. + /// + /// Note that the result can be any zone containing the given + /// point. + void findZone( const Point3F& point, SceneZoneSpace*& outZoneSpace, U32& outZoneID ) const; + + /// Collect the IDs of all zones that overlap the given area. + /// + /// @param area AABB of scene space to query. + /// @param outZones IDs of all overlapped zones are added to this vector. + /// + /// @return Number of zones that have been added to @a outZones. Always at least + /// 1 as at least the outdoor zone always overlaps the given area (though if another zone + /// manager fully contains @a area, the outdoor zone will not be added to the list). + U32 findZones( const Box3F& area, Vector< U32 >& outZones ) const; + + static ZoningChangedSignal& getZoningChangedSignal() + { + static ZoningChangedSignal sSignal; + return sSignal; + } + + /// @name Debugging + /// @{ + + /// Verify the current zoning state. This makes sure all the connectivity + /// information and all the zone assignments appear to be correct. + void verifyState(); + + /// Dump the current state of all zone spaces in the scene to the console. + /// @param update If true, zoning state states are updated first; if false, zoning is + /// dumped as is. + void dumpZoneStates( bool update = true ); + + /// @} + + /// @} +}; + +#endif // !_SCENEZONESPACEMANAGER_H_ diff --git a/Engine/source/sfx/dsound/dsFunctions.h b/Engine/source/sfx/dsound/dsFunctions.h new file mode 100644 index 000000000..ad7a3e53b --- /dev/null +++ b/Engine/source/sfx/dsound/dsFunctions.h @@ -0,0 +1,27 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// The various functions we need to grab from DSound.dll +DS_FUNCTION( DirectSoundEnumerateA, HRESULT, (LPDSENUMCALLBACKA pDSEnumCallback, LPVOID pContext) ) +DS_FUNCTION( DirectSoundEnumerateW, HRESULT, (LPDSENUMCALLBACKW pDSEnumCallback, LPVOID pContext) ) +DS_FUNCTION( DirectSoundCreate8, HRESULT, (LPCGUID pcGuidDevice, LPDIRECTSOUND8 *ppDS8, LPUNKNOWN pUnkOuter) ) + diff --git a/Engine/source/sfx/dsound/sfxDSBuffer.cpp b/Engine/source/sfx/dsound/sfxDSBuffer.cpp new file mode 100644 index 000000000..d7d7bb9d9 --- /dev/null +++ b/Engine/source/sfx/dsound/sfxDSBuffer.cpp @@ -0,0 +1,277 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/dsound/sfxDSBuffer.h" +#include "sfx/sfxStream.h" +#include "sfx/sfxDescription.h" +#include "sfx/sfxInternal.h" +#include "platform/async/asyncUpdate.h" +#include "core/util/safeRelease.h" +#include "core/util/safeCast.h" + + +SFXDSBuffer* SFXDSBuffer::create( IDirectSound8 *dsound, + const ThreadSafeRef< SFXStream >& stream, + SFXDescription* description, + bool useHardware ) +{ + AssertFatal( dsound, "SFXDSBuffer::create() - Got null dsound!" ); + AssertFatal( stream, "SFXDSBuffer::create() - Got a null stream!" ); + AssertFatal( description, "SFXDSBuffer::create() - Got a null description" ); + + SFXDSBuffer* buffer = new SFXDSBuffer( dsound, + stream, + description, + useHardware ); + + + if( !buffer->_createBuffer( &buffer->mBuffer ) ) + SAFE_DELETE( buffer ); + + return buffer; +} + +SFXDSBuffer::SFXDSBuffer( IDirectSound8* dsound, + const ThreadSafeRef< SFXStream >& stream, + SFXDescription* description, + bool useHardware ) + : Parent( stream, description ), + mDSound( dsound ), + mIs3d( description->mIs3D ), + mUseHardware( useHardware ), + mBuffer( NULL ), + mDuplicate( false ) +{ + AssertFatal( mDSound, "SFXDSBuffer::SFXDSBuffer() - Got null dsound!" ); + + mDSound->AddRef(); +} + +SFXDSBuffer::~SFXDSBuffer() +{ + SAFE_RELEASE( mBuffer ); + SAFE_RELEASE( mDSound ); +} + +bool SFXDSBuffer::_createBuffer( IDirectSoundBuffer8 **buffer8 ) +{ + AssertFatal( mAsyncState != NULL, + "SFXDSBuffer::_createBuffer() - Can't create buffer when not connected to stream!" ); + + const SFXFormat& format = getFormat(); + + // Set up WAV format structure. + WAVEFORMATEX wfx; + dMemset( &wfx, 0, sizeof( WAVEFORMATEX ) ); + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = format.getChannels(); + wfx.nSamplesPerSec = format.getSamplesPerSecond(); + wfx.wBitsPerSample = format.getBitsPerChannel(); + wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8; + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + + // Set up DSBUFFERDESC structure. + DSBUFFERDESC dsbdesc; + dMemset( &dsbdesc, 0, sizeof( DSBUFFERDESC ) ); + dsbdesc.dwSize = sizeof( DSBUFFERDESC ); + dsbdesc.dwFlags = + ( mIs3d ? DSBCAPS_CTRL3D | DSBCAPS_MUTE3DATMAXDISTANCE : DSBCAPS_CTRLPAN ) | + ( isStreaming() ? DSBCAPS_CTRLPOSITIONNOTIFY : 0 ) | + DSBCAPS_CTRLFREQUENCY | + DSBCAPS_CTRLVOLUME | + DSBCAPS_GETCURRENTPOSITION2 | + DSBCAPS_GLOBALFOCUS | + DSBCAPS_STATIC | + ( mUseHardware ? DSBCAPS_LOCHARDWARE : DSBCAPS_LOCSOFTWARE ); + dsbdesc.dwBufferBytes = mBufferSize; + if ( mIs3d ) + dsbdesc.guid3DAlgorithm = DS3DALG_HRTF_FULL; + dsbdesc.lpwfxFormat = &wfx; + + // Create the buffer. + IDirectSoundBuffer *buffer = NULL; + HRESULT hr = mDSound->CreateSoundBuffer( &dsbdesc, &buffer, NULL ); + if ( FAILED( hr ) || !buffer ) + return false; + + // Grab the version 8 interface. + IDirectSoundBuffer8* buffer8Ptr; + hr = buffer->QueryInterface( IID_IDirectSoundBuffer8, ( LPVOID* ) &buffer8Ptr ); + + // Release the original interface. + buffer->Release(); + + // If we failed to get the 8 interface... exit. + if ( FAILED( hr ) || !buffer8Ptr ) + return false; + + // Set up notification positions, if this is a streaming buffer. + + if( isStreaming() ) + { + using namespace SFXInternal; + + const U32 maxQueuedPackets = SFXAsyncQueue::DEFAULT_STREAM_QUEUE_LENGTH; + const U32 packetSize = mAsyncState->mStream->getPacketSize(); + + LPDIRECTSOUNDNOTIFY8 lpDsNotify; + if( FAILED( hr = buffer8Ptr->QueryInterface( IID_IDirectSoundNotify8, ( LPVOID* ) &lpDsNotify ) ) ) + { + SAFE_RELEASE( buffer8Ptr ); + return false; + } + + const U32 numNotifies = maxQueuedPackets + 1; // Add one for end-of-stream notification pos. + DSBPOSITIONNOTIFY* dsbNotifyPos = + ( DSBPOSITIONNOTIFY* ) _alloca( numNotifies * sizeof( DSBPOSITIONNOTIFY ) ); + + // Events seem to be triggered way too early by DS causing the playback queues to + // reject updates, so we nudge the update markers "somewhat" to the right here. + // This value here is based on experimentation. No harm should result if we don't + // hit it other than updates happening in sub-optimal timing. + enum { OFFSET_DELTA = 5000 }; + + U32 offset = ( packetSize + OFFSET_DELTA ) % mBufferSize; + HANDLE updateEvent = ( HANDLE ) UPDATE_THREAD()->getUpdateEvent(); + for( U32 i = 0; i < maxQueuedPackets; ++ i, offset = ( offset + packetSize ) % mBufferSize ) + { + dsbNotifyPos[ i ].dwOffset = offset; + dsbNotifyPos[ i ].hEventNotify = updateEvent; + } + + // A end-of-stream notification position. + + //FIXME: this position will start to be wrong when doing stream seeks + + dsbNotifyPos[ numNotifies - 1 ].dwOffset = ( format.getDataLength( getDuration() ) + OFFSET_DELTA ) % mBufferSize; + dsbNotifyPos[ numNotifies - 1 ].hEventNotify = updateEvent; + + // Install the notifications. + + lpDsNotify->SetNotificationPositions( numNotifies, dsbNotifyPos ); + SAFE_RELEASE( lpDsNotify ); + + // Don't need to notify on stop as when playback is stopped, + // the packet buffers will just fill up and stop updating + // when saturated. + } + + *buffer8 = buffer8Ptr; + return true; +} + +bool SFXDSBuffer::_copyData( U32 offset, + const U8 *data, + U32 length ) +{ + AssertFatal( mBuffer, "SFXDSBuffer::_copyData() - no buffer" ); + + // Fill the buffer with the resource data. + VOID* lpvWrite; + DWORD dwLength; + VOID* lpvWrite2; + DWORD dwLength2; + HRESULT hr = mBuffer->Lock( + offset, // Offset at which to start lock. + length, // Size of lock. + &lpvWrite, // Gets address of first part of lock. + &dwLength, // Gets size of first part of lock. + &lpvWrite2, // Address of wraparound not needed. + &dwLength2, // Size of wraparound not needed. + 0 ); + if ( FAILED( hr ) ) + return false; + + // Copy the first part. + dMemcpy( lpvWrite, data, dwLength ); + + // Do we have a wrap? + if ( lpvWrite2 ) + dMemcpy( lpvWrite2, data + dwLength, dwLength2 ); + + // And finally, unlock. + hr = mBuffer->Unlock( + lpvWrite, // Address of lock start. + dwLength, // Size of lock. + lpvWrite2, // No wraparound portion. + dwLength2 ); // No wraparound size. + + // Return success code. + return SUCCEEDED(hr); +} + +void SFXDSBuffer::_flush() +{ + AssertFatal( isStreaming(), "SFXDSBuffer::_flush() - not a streaming buffer" ); + AssertFatal( SFXInternal::isSFXThread(), "SFXDSBuffer::_flush() - not on SFX thread" ); + + Parent::_flush(); + mBuffer->SetCurrentPosition( 0 ); +} + +bool SFXDSBuffer::_duplicateBuffer( IDirectSoundBuffer8 **buffer8 ) +{ + AssertFatal( mBuffer, "SFXDSBuffer::_duplicateBuffer() - Duplicate buffer is null!" ); + + // If this is the first duplicate then + // give the caller our original buffer. + if ( !mDuplicate ) + { + mDuplicate = true; + + *buffer8 = mBuffer; + (*buffer8)->AddRef(); + + return true; + } + + IDirectSoundBuffer *buffer1 = NULL; + HRESULT hr = mDSound->DuplicateSoundBuffer( mBuffer, &buffer1 ); + if ( FAILED( hr ) || !buffer1 ) + return false; + + // Grab the version 8 interface. + hr = buffer1->QueryInterface( IID_IDirectSoundBuffer8, (LPVOID*)buffer8 ); + + // Release the original interface. + buffer1->Release(); + + return SUCCEEDED( hr ) && (*buffer8); +} + +bool SFXDSBuffer::createVoice( IDirectSoundBuffer8 **buffer8 ) +{ + return ( mBuffer && _duplicateBuffer( buffer8 ) && *buffer8 ); +} + +void SFXDSBuffer::releaseVoice( IDirectSoundBuffer8 **buffer ) +{ + AssertFatal( *buffer, "SFXDSBuffer::releaseVoice() - Got null buffer!" ); + + if ( *buffer == mBuffer ) + { + mDuplicate = false; + (*buffer)->Stop(); + } + + SAFE_RELEASE( (*buffer) ); +} diff --git a/Engine/source/sfx/dsound/sfxDSBuffer.h b/Engine/source/sfx/dsound/sfxDSBuffer.h new file mode 100644 index 000000000..94549df86 --- /dev/null +++ b/Engine/source/sfx/dsound/sfxDSBuffer.h @@ -0,0 +1,101 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXDSBUFFER_H_ +#define _SFXDSBUFFER_H_ + +#include + +#ifndef _SFXINTERNAL_H_ +# include "sfx/sfxInternal.h" +#endif + + +/// DirectSound SFXBuffer implementation. +/// +/// Note that the actual sound buffer held by the buffer may +/// get duplicated around for individual voices. This is kind +/// of ugly as the resulting buffers aren't tied to a SFXDSBuffer +/// anymore. +class SFXDSBuffer : public SFXInternal::SFXWrapAroundBuffer +{ + typedef SFXInternal::SFXWrapAroundBuffer Parent; + + friend class SFXDSDevice; + friend class SFXDSVoice; + + protected: + + /// + bool mIs3d; + + /// + bool mUseHardware; + + IDirectSound8 *mDSound; + + /// The buffer used when duplication is allowed. + IDirectSoundBuffer8 *mBuffer; + + /// We set this to true when the original buffer has + /// been handed out and duplicates need to be made. + bool mDuplicate; + + /// + SFXDSBuffer( IDirectSound8 *dsound, + const ThreadSafeRef< SFXStream >& stream, + SFXDescription* description, + bool useHardware ); + + virtual ~SFXDSBuffer(); + + /// Set up a DirectSound buffer. + /// @note This method will not fill the buffer with data. + /// @note If this is a streaming buffer, the resulting buffer + /// will have its notification positions set up and already + /// be registered with SFXDSStreamThread. + bool _createBuffer( IDirectSoundBuffer8 **buffer8 ); + + /// + bool _duplicateBuffer( IDirectSoundBuffer8 **buffer8 ); + + // SFXWrapAroundBuffer. + virtual bool _copyData( U32 offset, const U8* data, U32 length ); + virtual void _flush(); + +public: + + /// + static SFXDSBuffer* create( IDirectSound8* dsound, + const ThreadSafeRef< SFXStream >& stream, + SFXDescription* description, + bool useHardware ); + + // + bool createVoice( IDirectSoundBuffer8 **buffer ); + + // + void releaseVoice( IDirectSoundBuffer8 **buffer ); + +}; + +#endif // _SFXDSBUFFER_H_ \ No newline at end of file diff --git a/Engine/source/sfx/dsound/sfxDSDevice.cpp b/Engine/source/sfx/dsound/sfxDSDevice.cpp new file mode 100644 index 000000000..b83ed120c --- /dev/null +++ b/Engine/source/sfx/dsound/sfxDSDevice.cpp @@ -0,0 +1,229 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/dsound/sfxDSDevice.h" +#include "sfx/dsound/sfxDSBuffer.h" +#include "sfx/dsound/sfxDSVoice.h" +#include "platformWin32/platformWin32.h" +#include "core/util/safeRelease.h" +#include "platform/async/asyncUpdate.h" +#include "console/console.h" + + +SFXDSDevice::SFXDSDevice( SFXProvider* provider, + DSoundFNTable *dsFnTbl, + GUID* guid, + String name, + bool useHardware, + S32 maxBuffers ) + : SFXDevice( name, provider, useHardware, maxBuffers ), + mDSound( NULL ), + mPrimaryBuffer( NULL ), + mListener( NULL ), + mDSoundTbl( dsFnTbl ), + mGUID( guid ) +{ +} + +bool SFXDSDevice::_init() +{ + HRESULT hr = mDSoundTbl->DirectSoundCreate8( mGUID, &mDSound, NULL ); + if ( FAILED( hr ) || !mDSound ) + { + Con::errorf( "SFXDSDevice::SFXDSDevice() - DirectSoundCreate8 failed" ); + return false; + } + + hr = mDSound->SetCooperativeLevel( getWin32WindowHandle(), DSSCL_PRIORITY ); + if ( FAILED( hr ) ) + { + Con::errorf( "SFXDSDevice::SFXDSDevice() - SetCooperativeLevel failed" ); + return false; + } + + // Get the primary buffer. + DSBUFFERDESC dsbd; + dMemset( &dsbd, 0, sizeof( DSBUFFERDESC ) ); + dsbd.dwSize = sizeof( DSBUFFERDESC ); + dsbd.dwFlags = DSBCAPS_CTRL3D | DSBCAPS_PRIMARYBUFFER; + hr = mDSound->CreateSoundBuffer( &dsbd, &mPrimaryBuffer, NULL ); + if ( FAILED( hr ) ) + { + Con::errorf( "SFXDSDevice::SFXDSDevice - Creating primary sound buffer failed" ); + return false; + } + + // Set the format and bitrate on the primary buffer. + S32 frequency = Con::getIntVariable( "$pref::SFX::frequency", 44100 ); + S32 bitrate = Con::getIntVariable( "$pref::SFX::bitrate", 32 ); + + WAVEFORMATEX wfx; + dMemset( &wfx, 0, sizeof( WAVEFORMATEX ) ); + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = 2; + wfx.nSamplesPerSec = frequency; + wfx.wBitsPerSample = bitrate; + wfx.nBlockAlign = ( wfx.nChannels * wfx.wBitsPerSample ) / 8; + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + hr = mPrimaryBuffer->SetFormat( &wfx ); + if( FAILED( hr ) ) + { + Con::errorf( "SFXDSDevice::SFXDSDevice() - Setting format of primary buffer failed" ); + return false; + } + + // Grab the 3d listener. + hr = mPrimaryBuffer->QueryInterface( IID_IDirectSound3DListener8, (LPVOID*)&mListener ); + if ( FAILED( hr ) ) + { + Con::errorf( "SFXDSDevice::SFXDevice() - Querying the listener interface failed!" ); + mListener = NULL; + } + + mCaps.dwSize = sizeof( DSCAPS ); + mDSound->GetCaps( &mCaps ); + + // If the device reports no hardware buffers then + // we have no choice but to disable hardware. + if ( mCaps.dwMaxHw3DAllBuffers == 0 ) + mUseHardware = false; + + // If mMaxBuffers is negative then use the caps + // to decide on a good maximum value... or set 8. + if ( mMaxBuffers < 0 ) + mMaxBuffers = getMax( mCaps.dwMaxHw3DAllBuffers, 8 ); + + // Start the stream thread. + + if( !Con::getBoolVariable( "$_forceAllMainThread" ) ) + { + SFXInternal::gUpdateThread = + new AsyncUpdateThread( "DirectSound Update Thread", SFXInternal::gBufferUpdateList ); + SFXInternal::gUpdateThread->start(); + } + + return true; +} + +SFXDSDevice::~SFXDSDevice() +{ + // And release our resources. + SAFE_RELEASE( mListener ); + SAFE_RELEASE( mPrimaryBuffer ); + SAFE_RELEASE( mDSound ); +} + +SFXBuffer* SFXDSDevice::createBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ) +{ + AssertFatal( stream, "SFXDSDevice::createBuffer() - Got null stream!" ); + AssertFatal( description, "SFXDSDevice::createBuffer() - Got null description!" ); + + SFXDSBuffer* buffer = SFXDSBuffer::create( mDSound, + stream, + description, + mUseHardware ); + + if( buffer ) + _addBuffer( buffer ); + + return buffer; +} + +SFXVoice* SFXDSDevice::createVoice( bool is3D, SFXBuffer *buffer ) +{ + // Don't bother going any further if we've + // exceeded the maximum voices. + if ( mVoices.size() >= mMaxBuffers ) + return NULL; + + AssertFatal( buffer, "SFXDSDevice::createVoice() - Got null buffer!" ); + + SFXDSBuffer* dsBuffer = dynamic_cast( buffer ); + AssertFatal( dsBuffer, "SFXDSDevice::createVoice() - Got bad buffer!" ); + + SFXDSVoice* voice = SFXDSVoice::create( this, dsBuffer ); + if ( !voice ) + return NULL; + + _addVoice( voice ); + return voice; +} + +void SFXDSDevice::_commitDeferred() +{ + if( mListener ) + mListener->CommitDeferredSettings(); +} + +void SFXDSDevice::update() +{ + Parent::update(); + + // Apply the deferred settings that changed between updates. + mListener->CommitDeferredSettings(); +} + +void SFXDSDevice::setListener( U32 index, const SFXListenerProperties& listener ) +{ + // Get the transform from the listener. + const MatrixF& transform = listener.getTransform(); + Point3F pos, dir, up; + transform.getColumn( 3, &pos ); + transform.getColumn( 1, &dir ); + transform.getColumn( 2, &up ); + + // And the velocity... + const VectorF& velocity = listener.getVelocity(); + + // Finally, set it all to DSound! + mListener->SetPosition( pos.x, pos.z, pos.y, DS3D_DEFERRED ); + mListener->SetOrientation( dir.x, dir.z, dir.y, up.x, up.z, up.y, DS3D_DEFERRED ); + mListener->SetVelocity( velocity.x, velocity.z, velocity.y, DS3D_DEFERRED ); +} + +void SFXDSDevice::setDistanceModel( SFXDistanceModel model ) +{ + switch( model ) + { + case SFXDistanceModelLinear: + Con::errorf( "SFXDSDevice::setDistanceModel - 'linear' distance attenuation not supported by DirectSound" ); + break; + + case SFXDistanceModelLogarithmic: + break; // Nothing to do. + + default: + AssertWarn( false, "SFXDSDevice::setDistanceModel() - model not implemented" ); + } +} + +void SFXDSDevice::setDopplerFactor( F32 factor ) +{ + if( mListener ) + mListener->SetDopplerFactor( factor, DS3D_DEFERRED ); // Committed in update. +} + +void SFXDSDevice::setRolloffFactor( F32 factor ) +{ + if( mListener ) + mListener->SetRolloffFactor( factor, DS3D_DEFERRED ); // Committed in update. +} diff --git a/Engine/source/sfx/dsound/sfxDSDevice.h b/Engine/source/sfx/dsound/sfxDSDevice.h new file mode 100644 index 000000000..540294c85 --- /dev/null +++ b/Engine/source/sfx/dsound/sfxDSDevice.h @@ -0,0 +1,142 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXDSDEVICE_H_ +#define _SFXDSDEVICE_H_ + +#ifndef _STRINGFUNCTIONS_H_ +# include "core/strings/stringFunctions.h" +#endif +#ifndef _SFXDEVICE_H_ + #include "sfx/sfxDevice.h" +#endif +#ifndef _SFXDSVOICE_H_ + #include "sfx/dsound/sfxDSVoice.h" +#endif +#ifndef OS_DLIBRARY_H + #include "platform/platformDlibrary.h" +#endif + +#include + +// Typedefs +#define DS_FUNCTION(fn_name, fn_return, fn_args) \ + typedef fn_return (WINAPI *DSFNPTR##fn_name##)##fn_args##; +#include "sfx/dsound/dsFunctions.h" +#undef DS_FUNCTION + +// Function table +struct DSoundFNTable +{ + DSoundFNTable() : isLoaded( false ) {}; + bool isLoaded; + DLibraryRef dllRef; + +#define DS_FUNCTION(fn_name, fn_return, fn_args) \ + DSFNPTR##fn_name fn_name; +#include "sfx/dsound/dsFunctions.h" +#undef DS_FUNCTION +}; + + +/// Helper for asserting on dsound HRESULTS. +inline void DSAssert( HRESULT hr, const char *info ) +{ + #ifdef TORQUE_DEBUG + + if( FAILED( hr ) ) + { + char buf[256]; + dSprintf( buf, 256, "Error code: %x\n%s", hr, info ); + AssertFatal( false, buf ); + } + + #endif +} + + +/// The DirectSound device implementation exposes a couple +/// of settings to script that you should be aware of: +/// +/// $DirectSound::dopplerFactor - This controls the scale of +/// the doppler effect. Valid factors are 0.0 to 10.0 and it +/// defaults to 0.75. +/// +/// $DirectSound::distanceFactor - This sets the unit conversion +/// for +/// +/// $DirectSound::rolloffFactor - ; +/// +/// +class SFXDSDevice : public SFXDevice +{ + typedef SFXDevice Parent; + + friend class SFXDSVoice; + friend class SFXDSProvider; // _init + + public: + + //explicit SFXDSDevice(); + + SFXDSDevice( SFXProvider* provider, + DSoundFNTable *dsFnTbl, + GUID* guid, + String name, + bool useHardware, + S32 maxBuffers ); + + virtual ~SFXDSDevice(); + + protected: + + IDirectSound8 *mDSound; + + IDirectSound3DListener8 *mListener; + + IDirectSoundBuffer *mPrimaryBuffer; + + DSoundFNTable *mDSoundTbl; + + DSCAPS mCaps; + + GUID* mGUID; + + bool _init(); + + /// Called from SFXDSVoice to commit any deferred + /// settings before playback begins. + void _commitDeferred(); + + public: + + // SFXDevice + virtual SFXBuffer* createBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ); + virtual SFXVoice* createVoice( bool is3D, SFXBuffer *buffer ); + virtual void update(); + virtual void setListener( U32 index, const SFXListenerProperties& listener ); + virtual void setDistanceModel( SFXDistanceModel mode ); + virtual void setDopplerFactor( F32 factor ); + virtual void setRolloffFactor( F32 factor ); +}; + +#endif // _SFXDSDEVICE_H_ diff --git a/Engine/source/sfx/dsound/sfxDSProvider.cpp b/Engine/source/sfx/dsound/sfxDSProvider.cpp new file mode 100644 index 000000000..a18101484 --- /dev/null +++ b/Engine/source/sfx/dsound/sfxDSProvider.cpp @@ -0,0 +1,198 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/sfxProvider.h" +#include "sfx/dsound/sfxDSDevice.h" +#include "core/util/safeRelease.h" +#include "console/console.h" +#include "core/strings/unicode.h" +#include "core/util/safeDelete.h" +#include "core/module.h" + + +class SFXDSProvider : public SFXProvider +{ +public: + + SFXDSProvider() + : SFXProvider( "DirectSound" ) {} + virtual ~SFXDSProvider(); + + void init(); + +protected: + DSoundFNTable mDSound; + + struct DSDeviceInfo : SFXDeviceInfo + { + GUID* guid; + DSCAPS caps; + }; + + static BOOL CALLBACK dsEnumProc( + LPGUID lpGUID, + LPCTSTR lpszDesc, + LPCTSTR lpszDrvName, + LPVOID lpContext ); + + void addDeviceDesc( GUID* guid, const String& name, const String& desc ); + +public: + + SFXDevice* createDevice( const String& deviceName, bool useHardware, S32 maxBuffers ); + +}; + +MODULE_BEGIN( DirectSound ) + + MODULE_INIT_BEFORE( SFX ) + MODULE_SHUTDOWN_AFTER( SFX ) + + SFXDSProvider* mProvider; + + MODULE_INIT + { + mProvider = new SFXDSProvider; + } + + MODULE_SHUTDOWN + { + delete mProvider; + } + +MODULE_END; + +//------------------------------------------------------------------------------ +// Helper + +bool dsBindFunction( DLibrary *dll, void *&fnAddress, const char *name ) +{ + fnAddress = dll->bind( name ); + + if (!fnAddress) + Con::warnf( "DSound Loader: DLL bind failed for %s", name ); + + return fnAddress != 0; +} + +//------------------------------------------------------------------------------ + +SFXDSProvider::~SFXDSProvider() +{ +} + +void SFXDSProvider::init() +{ + // Grab the functions we'll want from the dsound DLL. + mDSound.dllRef = OsLoadLibrary( "dsound.dll" ); + mDSound.isLoaded = true; +#define DS_FUNCTION(fn_name, fn_return, fn_args) \ + mDSound.isLoaded &= dsBindFunction(mDSound.dllRef, *(void**)&mDSound.fn_name, #fn_name); +#include "sfx/dsound/dsFunctions.h" +#undef DS_FUNCTION + + AssertISV( mDSound.isLoaded, "DirectSound failed to load." ); + + // All we need to do to init is enumerate the + // devices... if this fails then don't register + // the provider as it's broken in some way. + if ( FAILED( mDSound.DirectSoundEnumerate( dsEnumProc, (VOID*)this ) ) ) + { + Con::errorf( "SFXDSProvider - Device enumeration failed!" ); + return; + } + + // Did we get any devices? + if ( mDeviceInfo.empty() ) + { + Con::errorf( "SFXDSProvider - No valid devices found!" ); + return; + } + + // Wow, we made it - register the provider. + regProvider( this ); +} + + +BOOL CALLBACK SFXDSProvider::dsEnumProc( + LPGUID lpGUID, + LPCTSTR lpszDesc, + LPCTSTR lpszDrvName, + LPVOID lpContext ) +{ + SFXDSProvider* provider = (SFXDSProvider*)lpContext; + provider->addDeviceDesc( lpGUID, lpszDrvName, lpszDesc ); + return TRUE; +} + +void SFXDSProvider::addDeviceDesc( GUID* guid, const String& name, const String& desc ) +{ + // Create a dummy device to get the caps. + IDirectSound8* dsound; + HRESULT hr = mDSound.DirectSoundCreate8( guid, &dsound, NULL ); + if ( FAILED( hr ) || !dsound ) + return; + + // Init the caps structure and have the device fill it out. + DSCAPS caps; + dMemset( &caps, 0, sizeof( caps ) ); + caps.dwSize = sizeof( caps ); + hr = dsound->GetCaps( &caps ); + + // Clean up and handle errors. + SAFE_RELEASE( dsound ); + + if ( FAILED( hr ) ) + return; + + // Now, record the desc info into our own internal list. + DSDeviceInfo* info = new DSDeviceInfo; + info->name = desc; + info->driver = name; + info->hasHardware = caps.dwMaxHw3DAllBuffers > 0; + info->maxBuffers = caps.dwMaxHw3DAllBuffers; + info->guid = guid; + info->caps = caps; + + mDeviceInfo.push_back( info ); +} + +SFXDevice* SFXDSProvider::createDevice( const String& deviceName, bool useHardware, S32 maxBuffers ) +{ + DSDeviceInfo* info = dynamic_cast< DSDeviceInfo* > + ( _findDeviceInfo( deviceName ) ); + + if( !info ) + return NULL; + + SFXDSDevice* device = new SFXDSDevice( this, + &mDSound, + info->guid, + info->name, + useHardware, + maxBuffers ); + + if( !device->_init() ) + SAFE_DELETE( device ); + + return device; +} diff --git a/Engine/source/sfx/dsound/sfxDSVoice.cpp b/Engine/source/sfx/dsound/sfxDSVoice.cpp new file mode 100644 index 000000000..ffabec043 --- /dev/null +++ b/Engine/source/sfx/dsound/sfxDSVoice.cpp @@ -0,0 +1,229 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/dsound/sfxDSVoice.h" +#include "sfx/dsound/sfxDSDevice.h" +#include "core/util/safeRelease.h" + + +SFXDSVoice* SFXDSVoice::create( SFXDSDevice *device, SFXDSBuffer *buffer ) +{ + AssertFatal( buffer, "SFXDSVoice::create() - Got null buffer!" ); + + IDirectSoundBuffer8 *dsBuffer8 = NULL; + if ( !buffer->createVoice( &dsBuffer8 ) || !dsBuffer8 ) + return NULL; + + // Now try to grab a 3D interface... if we don't + // get one its probably because its not a 3d sound. + IDirectSound3DBuffer8* dsBuffer3d8 = NULL; + dsBuffer8->QueryInterface( IID_IDirectSound3DBuffer8, (LPVOID*)&dsBuffer3d8 ); + + // Create the voice and return! + SFXDSVoice* voice = new SFXDSVoice( device, + buffer, + dsBuffer8, + dsBuffer3d8 ); + + // Now set the voice to a default state. + // The buffer from which we have duplicated may have been assigned different + // properties and we don't want to inherit these. + + voice->setVolume( 1.0 ); + voice->setPitch( 1.0 ); + + return voice; +} + +SFXDSVoice::SFXDSVoice( SFXDSDevice *device, + SFXDSBuffer *buffer, + IDirectSoundBuffer8 *dsBuffer, + IDirectSound3DBuffer8 *dsBuffer3d ) + : Parent( buffer ), + mDevice( device ), + mDSBuffer( dsBuffer ), + mDSBuffer3D( dsBuffer3d ), + mIsLooping( false ) +{ + AssertFatal( mDevice, "SFXDSVoice::SFXDSVoice() - SFXDSDevice is null!" ); + AssertFatal( mBuffer, "SFXDSVoice::SFXDSVoice() - SFXDSBuffer is null!" ); + AssertFatal( mDSBuffer, "SFXDSVoice::SFXDSVoice() - Dsound buffer is null!" ); +} + +SFXDSVoice::~SFXDSVoice() +{ + SAFE_RELEASE( mDSBuffer3D ); + + SFXDSBuffer* dsBuffer = _getBuffer(); + if( dsBuffer ) + dsBuffer->releaseVoice( &mDSBuffer ); + + mBuffer = NULL; +} + +SFXStatus SFXDSVoice::_status() const +{ + DWORD status = 0; + mDSBuffer->GetStatus( &status ); + + if ( status & DSBSTATUS_PLAYING ) + return SFXStatusPlaying; + else + return SFXStatusStopped; +} + +void SFXDSVoice::_play() +{ + DSAssert( mDSBuffer->Play( 0, 0, mIsLooping ? DSBPLAY_LOOPING : 0 ), + "SFXDSVoice::_play() - Playback failed!" ); +} + +void SFXDSVoice::_stop() +{ + DSAssert( mDSBuffer->Stop(), "SFXDSVoice::pause - stop failed!" ); + mDSBuffer->SetCurrentPosition( 0 ); +} + +void SFXDSVoice::_pause() +{ + DSAssert( mDSBuffer->Stop(), "SFXDSVoice::pause - stop failed!" ); +} + +void SFXDSVoice::_seek( U32 sample ) +{ + U32 pos = mBuffer->getFormat().getBytesPerSample() * sample; + mDSBuffer->SetCurrentPosition( pos ); +} + +U32 SFXDSVoice::_tell() const +{ + DWORD position = 0; + mDSBuffer->GetCurrentPosition( &position, NULL ); + U32 samplePos = _getBuffer()->getSamplePos( position ); + return samplePos; +} + +void SFXDSVoice::setMinMaxDistance( F32 min, F32 max ) +{ + if ( !mDSBuffer3D ) + return; + + mDSBuffer3D->SetMinDistance( min, DS3D_DEFERRED ); + mDSBuffer3D->SetMaxDistance( max, DS3D_DEFERRED ); +} + +void SFXDSVoice::play( bool looping ) +{ + // If this is a 3d sound then we need + // to commit any deferred settings before + // we start playback else we can get some + // glitches. + + if ( mDSBuffer3D ) + mDevice->_commitDeferred(); + + // If this is a streaming buffer, + // force looping. + + const bool isStreaming = mBuffer->isStreaming(); + if( isStreaming ) + looping = true; + mIsLooping = looping; + + Parent::play( looping ); +} + +void SFXDSVoice::setVelocity( const VectorF& velocity ) +{ + if ( !mDSBuffer3D ) + return; + + DSAssert( mDSBuffer3D->SetVelocity( velocity.x, velocity.z, velocity.y, DS3D_DEFERRED ), + "SFXDSVoice::setVelocity - couldn't update buffer!" ); +} + +void SFXDSVoice::setTransform( const MatrixF& transform ) +{ + if ( !mDSBuffer3D ) + return; + + Point3F pos, dir; + transform.getColumn( 3, &pos ); + transform.getColumn( 1, &dir ); + DSAssert( mDSBuffer3D->SetPosition( pos.x, pos.z, pos.y, DS3D_DEFERRED ), + "SFXDSVoice::setTransform - couldn't set position of the buffer." ); + + DSAssert( mDSBuffer3D->SetConeOrientation( dir.x, dir.z, dir.y, DS3D_DEFERRED ), + "SFXDSVoice::setTransform - couldn't set cone orientation of the buffer." ); +} + +/// Helper for converting floating point linear volume +/// to a logrithmic integer volume for dsound. +LONG SFXDSVoice::_linearToLogVolume( F32 linVolume ) +{ + LONG logVolume; + + if ( linVolume <= 0.0f ) + logVolume = DSBVOLUME_MIN; + else + { + logVolume = -2000.0 * mLog( 1.0f / linVolume ); + logVolume = mClamp( logVolume, DSBVOLUME_MIN, DSBVOLUME_MAX ); + } + + return logVolume; +} + +void SFXDSVoice::setVolume( F32 volume ) +{ + LONG logVolume = _linearToLogVolume( volume ); + + HRESULT hr = mDSBuffer->SetVolume( logVolume ); + DSAssert( hr, "SFXDSVoice::setVolume - couldn't set volume!" ); +} + +void SFXDSVoice::setPitch( F32 pitch ) +{ + F32 sampleRate = _getBuffer()->getFormat().getSamplesPerSecond(); + F32 frequency = mFloor( mClampF( sampleRate * pitch, DSBFREQUENCY_MIN, DSBFREQUENCY_MAX ) ); + + DSAssert( mDSBuffer->SetFrequency( ( U32 )frequency ), + "SFXDSVoice::setPitch - couldn't set playback frequency."); +} + +void SFXDSVoice::setCone( F32 innerAngle, F32 outerAngle, F32 outerVolume ) +{ + if ( !mDSBuffer3D ) + return; + + DSAssert( mDSBuffer3D->SetConeAngles( innerAngle, + outerAngle, + DS3D_DEFERRED ), + "SFXDSVoice::setCone - couldn't set cone angles!" ); + + + LONG logVolume = _linearToLogVolume( outerVolume ); + + DSAssert( mDSBuffer3D->SetConeOutsideVolume( logVolume, + DS3D_DEFERRED ), + "SFXDSVoice::setCone - couldn't set cone outside volume!" ); +} diff --git a/Engine/source/sfx/dsound/sfxDSVoice.h b/Engine/source/sfx/dsound/sfxDSVoice.h new file mode 100644 index 000000000..e428832c1 --- /dev/null +++ b/Engine/source/sfx/dsound/sfxDSVoice.h @@ -0,0 +1,92 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXDSVOICE_H_ +#define _SFXDSVOICE_H_ + +#ifndef _SFXVOICE_H_ + #include "sfx/sfxVoice.h" +#endif +#ifndef _SFXDSBUFFER_H_ + #include "sfx/dsound/sfxDSBuffer.h" +#endif + +#include + +class SFXDSDevice; + + +class SFXDSVoice : public SFXVoice +{ + typedef SFXVoice Parent; + + protected: + + SFXDSVoice( SFXDSDevice *device, + SFXDSBuffer *buffer, + IDirectSoundBuffer8 *dsBuffer, + IDirectSound3DBuffer8 *dsBuffer3d ); + + /// The device used to commit deferred settings. + SFXDSDevice *mDevice; + + IDirectSoundBuffer8 *mDSBuffer; + + IDirectSound3DBuffer8 *mDSBuffer3D; + + bool mIsLooping; + + SFXDSBuffer* _getBuffer() const { return ( SFXDSBuffer* ) mBuffer.getPointer(); } + + /// Helper for converting floating point linear volume + /// to a logrithmic integer volume for dsound. + static LONG _linearToLogVolume( F32 linVolume ); + + // SFXVoice + virtual SFXStatus _status() const; + virtual void _play(); + virtual void _pause(); + virtual void _stop(); + virtual void _seek( U32 sample ); + virtual U32 _tell() const; + + public: + + /// + static SFXDSVoice* create( SFXDSDevice *device, + SFXDSBuffer *buffer ); + + /// + virtual ~SFXDSVoice(); + + // SFXVoice + void setMinMaxDistance( F32 min, F32 max ); + void play( bool looping ); + void setVelocity( const VectorF& velocity ); + void setTransform( const MatrixF& transform ); + void setVolume( F32 volume ); + void setPitch( F32 pitch ); + void setCone( F32 innerAngle, F32 outerAngle, F32 outerVolume ); + +}; + +#endif // _SFXDSBUFFER_H_ \ No newline at end of file diff --git a/Engine/source/sfx/fmod/fmodErrors.h b/Engine/source/sfx/fmod/fmodErrors.h new file mode 100644 index 000000000..265d0dd55 --- /dev/null +++ b/Engine/source/sfx/fmod/fmodErrors.h @@ -0,0 +1,115 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +FMOD_ERROR( FMOD_OK ) +FMOD_ERROR( FMOD_ERR_ALREADYLOCKED ) +FMOD_ERROR( FMOD_ERR_BADCOMMAND ) +FMOD_ERROR( FMOD_ERR_CDDA_DRIVERS ) +FMOD_ERROR( FMOD_ERR_CDDA_INIT ) +FMOD_ERROR( FMOD_ERR_CDDA_INVALID_DEVICE ) +FMOD_ERROR( FMOD_ERR_CDDA_NOAUDIO ) +FMOD_ERROR( FMOD_ERR_CDDA_NODEVICES ) +FMOD_ERROR( FMOD_ERR_CDDA_NODISC ) +FMOD_ERROR( FMOD_ERR_CDDA_READ ) +FMOD_ERROR( FMOD_ERR_CHANNEL_ALLOC ) +FMOD_ERROR( FMOD_ERR_CHANNEL_STOLEN ) +FMOD_ERROR( FMOD_ERR_COM ) +FMOD_ERROR( FMOD_ERR_DMA ) +FMOD_ERROR( FMOD_ERR_DSP_CONNECTION ) +FMOD_ERROR( FMOD_ERR_DSP_FORMAT ) +FMOD_ERROR( FMOD_ERR_DSP_NOTFOUND ) +FMOD_ERROR( FMOD_ERR_DSP_RUNNING ) +FMOD_ERROR( FMOD_ERR_DSP_TOOMANYCONNECTIONS ) +FMOD_ERROR( FMOD_ERR_FILE_BAD ) +FMOD_ERROR( FMOD_ERR_FILE_COULDNOTSEEK ) +FMOD_ERROR( FMOD_ERR_FILE_DISKEJECTED ) +FMOD_ERROR( FMOD_ERR_FILE_EOF ) +FMOD_ERROR( FMOD_ERR_FILE_NOTFOUND ) +FMOD_ERROR( FMOD_ERR_FILE_UNWANTED ) +FMOD_ERROR( FMOD_ERR_FORMAT ) +FMOD_ERROR( FMOD_ERR_HTTP ) +FMOD_ERROR( FMOD_ERR_HTTP_ACCESS ) +FMOD_ERROR( FMOD_ERR_HTTP_PROXY_AUTH ) +FMOD_ERROR( FMOD_ERR_HTTP_SERVER_ERROR ) +FMOD_ERROR( FMOD_ERR_HTTP_TIMEOUT ) +FMOD_ERROR( FMOD_ERR_INITIALIZATION ) +FMOD_ERROR( FMOD_ERR_INITIALIZED ) +FMOD_ERROR( FMOD_ERR_INTERNAL ) +FMOD_ERROR( FMOD_ERR_INVALID_ADDRESS ) +FMOD_ERROR( FMOD_ERR_INVALID_FLOAT ) +FMOD_ERROR( FMOD_ERR_INVALID_HANDLE ) +FMOD_ERROR( FMOD_ERR_INVALID_PARAM ) +FMOD_ERROR( FMOD_ERR_INVALID_POSITION ) +FMOD_ERROR( FMOD_ERR_INVALID_SPEAKER ) +FMOD_ERROR( FMOD_ERR_INVALID_SYNCPOINT ) +FMOD_ERROR( FMOD_ERR_INVALID_VECTOR ) +FMOD_ERROR( FMOD_ERR_MAXAUDIBLE ) +FMOD_ERROR( FMOD_ERR_MEMORY ) +FMOD_ERROR( FMOD_ERR_MEMORY_CANTPOINT ) +FMOD_ERROR( FMOD_ERR_MEMORY_SRAM ) +FMOD_ERROR( FMOD_ERR_NEEDS2D ) +FMOD_ERROR( FMOD_ERR_NEEDS3D ) +FMOD_ERROR( FMOD_ERR_NEEDSHARDWARE ) +FMOD_ERROR( FMOD_ERR_NEEDSSOFTWARE ) +FMOD_ERROR( FMOD_ERR_NET_CONNECT ) +FMOD_ERROR( FMOD_ERR_NET_SOCKET_ERROR ) +FMOD_ERROR( FMOD_ERR_NET_URL ) +FMOD_ERROR( FMOD_ERR_NET_WOULD_BLOCK ) +FMOD_ERROR( FMOD_ERR_NOTREADY ) +FMOD_ERROR( FMOD_ERR_OUTPUT_ALLOCATED ) +FMOD_ERROR( FMOD_ERR_OUTPUT_CREATEBUFFER ) +FMOD_ERROR( FMOD_ERR_OUTPUT_DRIVERCALL ) +FMOD_ERROR( FMOD_ERR_OUTPUT_ENUMERATION ) +FMOD_ERROR( FMOD_ERR_OUTPUT_FORMAT ) +FMOD_ERROR( FMOD_ERR_OUTPUT_INIT ) +FMOD_ERROR( FMOD_ERR_OUTPUT_NOHARDWARE ) +FMOD_ERROR( FMOD_ERR_OUTPUT_NOSOFTWARE ) +FMOD_ERROR( FMOD_ERR_PAN ) +FMOD_ERROR( FMOD_ERR_PLUGIN ) +FMOD_ERROR( FMOD_ERR_PLUGIN_INSTANCES ) +FMOD_ERROR( FMOD_ERR_PLUGIN_MISSING ) +FMOD_ERROR( FMOD_ERR_PLUGIN_RESOURCE ) +FMOD_ERROR( FMOD_ERR_RECORD ) +FMOD_ERROR( FMOD_ERR_REVERB_INSTANCE ) +FMOD_ERROR( FMOD_ERR_SUBSOUND_ALLOCATED ) +FMOD_ERROR( FMOD_ERR_SUBSOUND_CANTMOVE ) +FMOD_ERROR( FMOD_ERR_SUBSOUND_MODE ) +FMOD_ERROR( FMOD_ERR_SUBSOUNDS ) +FMOD_ERROR( FMOD_ERR_TAGNOTFOUND ) +FMOD_ERROR( FMOD_ERR_TOOMANYCHANNELS ) +FMOD_ERROR( FMOD_ERR_UNIMPLEMENTED ) +FMOD_ERROR( FMOD_ERR_UNINITIALIZED ) +FMOD_ERROR( FMOD_ERR_UNSUPPORTED ) +FMOD_ERROR( FMOD_ERR_UPDATE ) +FMOD_ERROR( FMOD_ERR_VERSION ) +FMOD_ERROR( FMOD_ERR_PRELOADED ) +FMOD_ERROR( FMOD_ERR_EVENT_FAILED ) +FMOD_ERROR( FMOD_ERR_EVENT_INFOONLY ) +FMOD_ERROR( FMOD_ERR_EVENT_INTERNAL ) +FMOD_ERROR( FMOD_ERR_EVENT_MAXSTREAMS ) +FMOD_ERROR( FMOD_ERR_EVENT_MISMATCH ) +FMOD_ERROR( FMOD_ERR_EVENT_NAMECONFLICT ) +FMOD_ERROR( FMOD_ERR_EVENT_NOTFOUND ) +FMOD_ERROR( FMOD_ERR_EVENT_NEEDSSIMPLE ) +FMOD_ERROR( FMOD_ERR_EVENT_GUIDCONFLICT ) +FMOD_ERROR( FMOD_ERR_EVENT_ALREADY_LOADED ) +FMOD_ERROR( FMOD_ERR_MUSIC_UNINITIALIZED ) diff --git a/Engine/source/sfx/fmod/fmodFunctions.h b/Engine/source/sfx/fmod/fmodFunctions.h new file mode 100644 index 000000000..833525666 --- /dev/null +++ b/Engine/source/sfx/fmod/fmodFunctions.h @@ -0,0 +1,157 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// Xcode has problems properly detecting the dependencies for this file. +// If you change something here, manually recompile all the FMOD modules. + + +// FMOD Ex API: + +FMOD_FUNCTION( FMOD_Channel_GetPaused, (FMOD_CHANNEL *channel, FMOD_BOOL *paused)) +FMOD_FUNCTION( FMOD_Channel_SetPaused, (FMOD_CHANNEL *channel, FMOD_BOOL paused)); +FMOD_FUNCTION( FMOD_Channel_IsPlaying, (FMOD_CHANNEL *channel, FMOD_BOOL *isplaying)) +FMOD_FUNCTION( FMOD_Channel_Set3DAttributes, (FMOD_CHANNEL *channel, const FMOD_VECTOR *pos, const FMOD_VECTOR *vel)) +FMOD_FUNCTION( FMOD_Channel_SetFrequency, (FMOD_CHANNEL *channel, float frequency)) +FMOD_FUNCTION( FMOD_Channel_SetLoopCount, (FMOD_CHANNEL *channel, int loopcount)) +FMOD_FUNCTION( FMOD_Channel_SetPosition, (FMOD_CHANNEL *channel, unsigned int position, FMOD_TIMEUNIT postype)) +FMOD_FUNCTION( FMOD_Channel_GetPosition, (FMOD_CHANNEL* channel, unsigned int* position, FMOD_TIMEUNIT postype)) +FMOD_FUNCTION( FMOD_Channel_SetVolume, (FMOD_CHANNEL *channel, float volume)) +FMOD_FUNCTION( FMOD_Channel_GetVolume, (FMOD_CHANNEL *channel, float *volume)) +FMOD_FUNCTION( FMOD_Channel_Stop, (FMOD_CHANNEL *channel)) +FMOD_FUNCTION( FMOD_Channel_SetMode, (FMOD_CHANNEL *channel, FMOD_MODE mode)) +FMOD_FUNCTION( FMOD_Channel_Set3DMinMaxDistance, (FMOD_CHANNEL *channel, float mindistance, float maxdistance)); +FMOD_FUNCTION( FMOD_Channel_Set3DConeSettings, (FMOD_CHANNEL *channel, float insideconeangle, float outsideconeangle, float outsidevolume)) +FMOD_FUNCTION( FMOD_Channel_Set3DConeOrientation, (FMOD_CHANNEL *channel, FMOD_VECTOR *orientation)) +FMOD_FUNCTION( FMOD_Channel_SetReverbProperties, ( FMOD_CHANNEL* channel, const FMOD_REVERB_CHANNELPROPERTIES* prop ) ) +FMOD_FUNCTION( FMOD_Channel_SetPriority, ( FMOD_CHANNEL* channel, int priority ) ) +FMOD_FUNCTION( FMOD_Channel_IsVirtual, ( FMOD_CHANNEL* channel, FMOD_BOOL* isvirtual ) ) +FMOD_FUNCTION( FMOD_Channel_AddDSP, ( FMOD_CHANNEL* channel, FMOD_DSP* dsp, FMOD_DSPCONNECTION** connection ) ) + +FMOD_FUNCTION( FMOD_Sound_Lock, (FMOD_SOUND *sound, unsigned int offset, unsigned int length, void **ptr1, void **ptr2, unsigned int *len1, unsigned int *len2)) +FMOD_FUNCTION( FMOD_Sound_Unlock, (FMOD_SOUND *sound, void *ptr1, void *ptr2, unsigned int len1, unsigned int len2)) +FMOD_FUNCTION( FMOD_Sound_Release, (FMOD_SOUND *sound)) +FMOD_FUNCTION( FMOD_Sound_Set3DMinMaxDistance, (FMOD_SOUND *sound, float min, float max)) +FMOD_FUNCTION( FMOD_Sound_SetMode, (FMOD_SOUND *sound, FMOD_MODE mode)) +FMOD_FUNCTION( FMOD_Sound_GetMode, (FMOD_SOUND *sound, FMOD_MODE *mode)) +FMOD_FUNCTION( FMOD_Sound_SetLoopCount, (FMOD_SOUND *sound, int loopcount)) +FMOD_FUNCTION( FMOD_Sound_GetFormat, ( FMOD_SOUND* sound, FMOD_SOUND_TYPE* type, FMOD_SOUND_FORMAT* format, int* channels, int* bits ) ) +FMOD_FUNCTION( FMOD_Sound_GetLength, ( FMOD_SOUND* sound, unsigned int* length, FMOD_TIMEUNIT lengthtype ) ) +FMOD_FUNCTION( FMOD_Sound_GetDefaults, ( FMOD_SOUND* sound, float* frequency, float* volume, float* pan, int* priority ) ) +FMOD_FUNCTION( FMOD_Sound_GetMemoryInfo, ( FMOD_SOUND* sound, unsigned int memorybits, unsigned int event_memorybits, unsigned int* memoryused, unsigned int* memoryused_array ) ); + +FMOD_FUNCTION( FMOD_Geometry_AddPolygon, ( FMOD_GEOMETRY* geometry, float directocclusion, float reverbocclusion, FMOD_BOOL doublesided, int numvertices, const FMOD_VECTOR* vertices, int* polygonindex ) ) +FMOD_FUNCTION( FMOD_Geometry_SetPosition, ( FMOD_GEOMETRY* geometry, const FMOD_VECTOR* position ) ) +FMOD_FUNCTION( FMOD_Geometry_SetRotation, ( FMOD_GEOMETRY* geometry, const FMOD_VECTOR* forward, const FMOD_VECTOR* up ) ) +FMOD_FUNCTION( FMOD_Geometry_SetScale, ( FMOD_GEOMETRY* geometry, const FMOD_VECTOR* scale ) ) +FMOD_FUNCTION( FMOD_Geometry_Release, ( FMOD_GEOMETRY* geometry ) ) + +FMOD_FUNCTION( FMOD_DSP_GetInfo, ( FMOD_DSP* dsp, char* name, unsigned int* version, int* channels, int* configwidth, int* configheight ) ) +FMOD_FUNCTION( FMOD_DSP_Release, ( FMOD_DSP* dsp ) ) +FMOD_FUNCTION( FMOD_DSP_GetParameterInfo, ( FMOD_DSP* dsp, int index, char* name, char* label, char* description, int descriptionlen, float* min, float* max ) ) +FMOD_FUNCTION( FMOD_DSP_GetNumParameters, ( FMOD_DSP* dsp, int* numparams ) ) +FMOD_FUNCTION( FMOD_DSP_GetParameter, ( FMOD_DSP* dsp, int index, float* value, char* valuestr, int valuestrlen ) ) +FMOD_FUNCTION( FMOD_DSP_SetParameter, ( FMOD_DSP* dsp, int index, float value ) ) +FMOD_FUNCTION( FMOD_DSP_GetNumInputs, ( FMOD_DSP* dsp, int* numinputs ) ) +FMOD_FUNCTION( FMOD_DSP_GetNumOutputs, ( FMOD_DSP* dsp, int* numoutputs ) ) + +FMOD_FUNCTION( FMOD_System_Close, (FMOD_SYSTEM *system)) +FMOD_FUNCTION( FMOD_System_Create, (FMOD_SYSTEM **system)) +FMOD_FUNCTION( FMOD_System_Release, (FMOD_SYSTEM *system)) +FMOD_FUNCTION( FMOD_System_CreateSound, (FMOD_SYSTEM *system, const char *name_or_data, FMOD_MODE mode, FMOD_CREATESOUNDEXINFO *exinfo, FMOD_SOUND **sound)) +FMOD_FUNCTION( FMOD_System_CreateGeometry, ( FMOD_SYSTEM* system, int maxpolygons, int maxvertices, FMOD_GEOMETRY** geometry ) ) +FMOD_FUNCTION( FMOD_System_SetDriver, (FMOD_SYSTEM *system, int driver)) +FMOD_FUNCTION( FMOD_System_GetDriverCaps, (FMOD_SYSTEM *system, int id, FMOD_CAPS *caps, int *controlpaneloutputrate, FMOD_SPEAKERMODE *controlpanelspeakermode)) +FMOD_FUNCTION( FMOD_System_GetDriverInfo, (FMOD_SYSTEM *system, int id, char *name, int namelen, FMOD_GUID *GUID)) +FMOD_FUNCTION( FMOD_System_GetNumDrivers, (FMOD_SYSTEM *system, int *numdrivers)) +FMOD_FUNCTION( FMOD_System_GetVersion, (FMOD_SYSTEM *system, unsigned int *version)) +FMOD_FUNCTION( FMOD_System_Init, (FMOD_SYSTEM *system, int maxchannels, FMOD_INITFLAGS flags, void *extradriverdata)) +FMOD_FUNCTION( FMOD_System_PlaySound, (FMOD_SYSTEM *system, FMOD_CHANNELINDEX channelid, FMOD_SOUND *sound, FMOD_BOOL paused, FMOD_CHANNEL **channel)) +FMOD_FUNCTION( FMOD_System_Set3DListenerAttributes, (FMOD_SYSTEM *system, int listener, const FMOD_VECTOR *pos, const FMOD_VECTOR *vel, const FMOD_VECTOR *forward, const FMOD_VECTOR *up)) +FMOD_FUNCTION( FMOD_System_Set3DNumListeners, ( FMOD_SYSTEM* system, int numlisteners ) ) +FMOD_FUNCTION( FMOD_System_SetGeometrySettings, ( FMOD_SYSTEM* system, float maxworldsize ) ) +FMOD_FUNCTION( FMOD_System_Get3DSettings, (FMOD_SYSTEM* system, float* dopplerFactor, float* distanceFactor, float* rolloffFactor)) +FMOD_FUNCTION( FMOD_System_Set3DSettings, (FMOD_SYSTEM *system, float dopplerscale, float distancefactor, float rolloffscale)) +FMOD_FUNCTION( FMOD_System_SetDSPBufferSize, (FMOD_SYSTEM *system, unsigned int bufferlength, int numbuffers)) +FMOD_FUNCTION( FMOD_System_SetSpeakerMode, (FMOD_SYSTEM *system, FMOD_SPEAKERMODE speakermode)) +FMOD_FUNCTION( FMOD_System_SetReverbProperties, ( FMOD_SYSTEM* system, const FMOD_REVERB_PROPERTIES* prop ) ) +FMOD_FUNCTION( FMOD_System_SetReverbAmbientProperties, ( FMOD_SYSTEM* system, FMOD_REVERB_PROPERTIES* prop ) ) +FMOD_FUNCTION( FMOD_System_Update, ( FMOD_SYSTEM *system ) ) +FMOD_FUNCTION( FMOD_System_CreateDSPByType, ( FMOD_SYSTEM* system, FMOD_DSP_TYPE type, FMOD_DSP** dsp ) ) +FMOD_FUNCTION( FMOD_System_AddDSP, ( FMOD_SYSTEM* system, FMOD_DSP* dsp, FMOD_DSPCONNECTION** connection ) ) +FMOD_FUNCTION( FMOD_System_GetMemoryInfo, ( FMOD_SYSTEM* system, unsigned int memorybits, unsigned int event_memorybits, unsigned int* memoryused, unsigned int* memoryused_array ) ) +FMOD_FUNCTION( FMOD_System_SetFileSystem, ( FMOD_SYSTEM* system, FMOD_FILE_OPENCALLBACK useropen, FMOD_FILE_CLOSECALLBACK userclose, FMOD_FILE_READCALLBACK userread, FMOD_FILE_SEEKCALLBACK userseek, int blockalign ) ) +FMOD_FUNCTION( FMOD_System_SetPluginPath, ( FMOD_SYSTEM* system, const char* path ) ) +FMOD_FUNCTION( FMOD_System_GetHardwareChannels, ( FMOD_SYSTEM* system, int* num2d, int* num3d, int* total ) ) + +FMOD_FUNCTION( FMOD_Memory_GetStats, ( int*, int* ) ) +FMOD_FUNCTION( FMOD_Memory_Initialize, ( void *poolmem, int poollen, FMOD_MEMORY_ALLOCCALLBACK useralloc, FMOD_MEMORY_REALLOCCALLBACK userrealloc, FMOD_MEMORY_FREECALLBACK userfree ) ) + +// FMOD Designer API: + +FMOD_EVENT_FUNCTION( FMOD_EventSystem_Create, ( FMOD_EVENTSYSTEM** eventsystem ), "_FMOD_EventSystem_Create@4" ) +FMOD_EVENT_FUNCTION( FMOD_EventSystem_GetSystemObject, ( FMOD_EVENTSYSTEM* eventsystem, FMOD_SYSTEM** system ), "_FMOD_EventSystem_GetSystemObject@8" ) +FMOD_EVENT_FUNCTION( FMOD_EventSystem_GetVersion, ( FMOD_EVENTSYSTEM* eventsystem, unsigned int* version ), "_FMOD_EventSystem_GetVersion@8" ) +FMOD_EVENT_FUNCTION( FMOD_EventSystem_Init, ( FMOD_EVENTSYSTEM* eventsystem, int maxchannels, FMOD_INITFLAGS flags, void* extradriverdata, FMOD_EVENT_INITFLAGS eventflags ), "_FMOD_EventSystem_Init@20" ) +FMOD_EVENT_FUNCTION( FMOD_EventSystem_Release, ( FMOD_EVENTSYSTEM* eventsystem ), "_FMOD_EventSystem_Release@4" ) +FMOD_EVENT_FUNCTION( FMOD_EventSystem_Load, ( FMOD_EVENTSYSTEM* eventsystem, const char* name_or_data, FMOD_EVENT_LOADINFO* loadinfo, FMOD_EVENTPROJECT** project ), "_FMOD_EventSystem_Load@16" ) +FMOD_EVENT_FUNCTION( FMOD_EventSystem_Update, ( FMOD_EVENTSYSTEM* eventsystem ), "_FMOD_EventSystem_Update@4" ) +FMOD_EVENT_FUNCTION( FMOD_EventSystem_GetMemoryInfo, ( FMOD_EVENTSYSTEM* eventsystem, unsigned int memorybits, unsigned int event_memorybits, unsigned int* memoryused, unsigned int* memoryused_array ), "_FMOD_EventSystem_GetMemoryInfo@20" ) +FMOD_EVENT_FUNCTION( FMOD_EventSystem_SetMediaPath, ( FMOD_EVENTSYSTEM* eventsystem, const char* path ), "_FMOD_EventSystem_SetMediaPath@8" ) + +FMOD_EVENT_FUNCTION( FMOD_EventProject_Release, ( FMOD_EVENTPROJECT* eventproject ), "_FMOD_EventProject_Release@4" ) +FMOD_EVENT_FUNCTION( FMOD_EventProject_GetInfo, ( FMOD_EVENTPROJECT* eventproject, FMOD_EVENT_PROJECTINFO* info ), "_FMOD_EventProject_GetInfo@8" ) +FMOD_EVENT_FUNCTION( FMOD_EventProject_GetNumEvents, ( FMOD_EVENTPROJECT* eventproject, int* numevents ), "_FMOD_EventProject_GetNumEvents@8" ) +FMOD_EVENT_FUNCTION( FMOD_EventProject_GetNumGroups, ( FMOD_EVENTPROJECT* eventproject, int* numgroups ), "_FMOD_EventProject_GetNumGroups@8" ) +FMOD_EVENT_FUNCTION( FMOD_EventProject_GetGroupByIndex, ( FMOD_EVENTPROJECT* eventproject, int index, FMOD_BOOL cacheevents, FMOD_EVENTGROUP** group ), "_FMOD_EventProject_GetGroupByIndex@16" ) +FMOD_EVENT_FUNCTION( FMOD_EventProject_GetGroup, ( FMOD_EVENTPROJECT* eventproject, const char* name, FMOD_BOOL cacheevents, FMOD_EVENTGROUP** group ), "_FMOD_EventProject_GetGroup@16" ) + +FMOD_EVENT_FUNCTION( FMOD_EventGroup_GetInfo, ( FMOD_EVENTGROUP* eventgroup, int* index, char** name ), "_FMOD_EventGroup_GetInfo@12" ) +FMOD_EVENT_FUNCTION( FMOD_EventGroup_LoadEventData, ( FMOD_EVENTGROUP* eventgroup, FMOD_EVENT_RESOURCE resource, FMOD_EVENT_MODE mode ), "_FMOD_EventGroup_LoadEventData@12" ) +FMOD_EVENT_FUNCTION( FMOD_EventGroup_FreeEventData, ( FMOD_EVENTGROUP* eventgroup, FMOD_EVENT* event, FMOD_BOOL waituntilready ), "_FMOD_EventGroup_FreeEventData@12" ) +FMOD_EVENT_FUNCTION( FMOD_EventGroup_GetNumEvents, ( FMOD_EVENTGROUP* eventgroup, int* numevents ), "_FMOD_EventGroup_GetNumEvents@8" ) +FMOD_EVENT_FUNCTION( FMOD_EventGroup_GetNumGroups, ( FMOD_EVENTGROUP* eventgroup, int* numgroups ), "_FMOD_EventGroup_GetNumGroups@8" ) +FMOD_EVENT_FUNCTION( FMOD_EventGroup_GetEventByIndex, ( FMOD_EVENTGROUP* eventgroup, int index, FMOD_EVENT_MODE mode, FMOD_EVENT** event ), "_FMOD_EventGroup_GetEventByIndex@16" ) +FMOD_EVENT_FUNCTION( FMOD_EventGroup_GetEvent, ( FMOD_EVENTGROUP* eventgroup, const char* name, FMOD_EVENT_MODE mode, FMOD_EVENT** event ), "_FMOD_EventGroup_GetEvent@16" ) +FMOD_EVENT_FUNCTION( FMOD_EventGroup_GetGroupByIndex, ( FMOD_EVENTGROUP* eventgroup, int index, FMOD_BOOL cacheevents, FMOD_EVENTGROUP** group ), "_FMOD_EventGroup_GetGroupByIndex@16" ) +FMOD_EVENT_FUNCTION( FMOD_EventGroup_GetGroup, ( FMOD_EVENTGROUP* eventgroup, const char* name, FMOD_BOOL cacheevents, FMOD_EVENTGROUP** group ), "_FMOD_EventGroup_GetGroup@16" ) + +FMOD_EVENT_FUNCTION( FMOD_Event_GetInfo, ( FMOD_EVENT* event, int* index, char** name, FMOD_EVENT_INFO* info ), "_FMOD_Event_GetInfo@16" ) +FMOD_EVENT_FUNCTION( FMOD_Event_Release, ( FMOD_EVENT* event, FMOD_BOOL freeeventdata, FMOD_BOOL waituntilready ), "_FMOD_Event_Release@12" ) +FMOD_EVENT_FUNCTION( FMOD_Event_Start, ( FMOD_EVENT* event ), "_FMOD_Event_Start@4" ) +FMOD_EVENT_FUNCTION( FMOD_Event_Stop, ( FMOD_EVENT* event, FMOD_BOOL immediate ), "_FMOD_Event_Stop@8" ) +FMOD_EVENT_FUNCTION( FMOD_Event_SetPaused, ( FMOD_EVENT* event, FMOD_BOOL paused ), "_FMOD_Event_SetPaused@8" ) +FMOD_EVENT_FUNCTION( FMOD_Event_SetVolume, ( FMOD_EVENT* event, float volume ), "_FMOD_Event_SetVolume@8" ) +FMOD_EVENT_FUNCTION( FMOD_Event_SetPitch, ( FMOD_EVENT* event, float pitch, FMOD_EVENT_PITCHUNITS units ), "_FMOD_Event_SetPitch@12" ) +FMOD_EVENT_FUNCTION( FMOD_Event_Set3DAttributes, ( FMOD_EVENT* event, const FMOD_VECTOR* position, const FMOD_VECTOR* velocity, const FMOD_VECTOR* orientation ), "_FMOD_Event_Set3DAttributes@16" ) +FMOD_EVENT_FUNCTION( FMOD_Event_GetState, ( FMOD_EVENT* event, FMOD_EVENT_STATE* state ), "_FMOD_Event_GetState@8" ) +FMOD_EVENT_FUNCTION( FMOD_Event_GetNumParameters, ( FMOD_EVENT* event, int* numparameters ), "_FMOD_Event_GetNumParameters@8" ) +FMOD_EVENT_FUNCTION( FMOD_Event_GetParameter, ( FMOD_EVENT* event, const char* name, FMOD_EVENTPARAMETER** parameter ), "_FMOD_Event_GetParameter@12" ) +FMOD_EVENT_FUNCTION( FMOD_Event_GetParameterByIndex, ( FMOD_EVENT* event, int index, FMOD_EVENTPARAMETER** parameter ), "_FMOD_Event_GetParameterByIndex@12" ) +FMOD_EVENT_FUNCTION( FMOD_Event_GetPropertyByIndex, ( FMOD_EVENT* event, int propertyidex, void* value, FMOD_BOOL this_instance ), "_FMOD_Event_GetPropertyByIndex@16" ) +FMOD_EVENT_FUNCTION( FMOD_Event_SetPropertyByIndex, ( FMOD_EVENT* event, int propertyidex, void* value, FMOD_BOOL this_instance ), "_FMOD_Event_SetPropertyByIndex@16" ) +FMOD_EVENT_FUNCTION( FMOD_Event_GetProperty, ( FMOD_EVENT* event, const char* propertyname, void* value, FMOD_BOOL this_instance ), "_FMOD_Event_GetProperty@16" ) +FMOD_EVENT_FUNCTION( FMOD_Event_GetPropertyInfo, ( FMOD_EVENT* event, int* propertyindex, char** propertyname, FMOD_EVENTPROPERTY_TYPE* type ), "_FMOD_Event_GetPropertyInfo@16" ) + +FMOD_EVENT_FUNCTION( FMOD_EventParameter_GetInfo, ( FMOD_EVENTPARAMETER* eventparameter, int* index, char** name ), "_FMOD_EventParameter_GetInfo@12" ) +FMOD_EVENT_FUNCTION( FMOD_EventParameter_GetValue, ( FMOD_EVENTPARAMETER* eventparameter, float* value ), "_FMOD_EventParameter_GetValue@8" ) +FMOD_EVENT_FUNCTION( FMOD_EventParameter_SetValue, ( FMOD_EVENTPARAMETER* eventparameter, float value ), "_FMOD_EventParameter_SetValue@8" ) +FMOD_EVENT_FUNCTION( FMOD_EventParameter_GetRange, ( FMOD_EVENTPARAMETER* eventparameter, float* rangemin, float* rangemax ), "_FMOD_EventParameter_GetRange@12" ) diff --git a/Engine/source/sfx/fmod/sfxFMODBuffer.cpp b/Engine/source/sfx/fmod/sfxFMODBuffer.cpp new file mode 100644 index 000000000..07cbb8a0f --- /dev/null +++ b/Engine/source/sfx/fmod/sfxFMODBuffer.cpp @@ -0,0 +1,322 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/fmod/sfxFMODBuffer.h" +#include "sfx/fmod/sfxFMODDevice.h" +#include "sfx/sfxDescription.h" +#include "core/util/safeDelete.h" +#include "core/volume.h" + + +//----------------------------------------------------------------------------- + +static const char* sExtensions[] = +{ + "", // First try without doing anything with the given path. + "", // Then try it without an extension but by expanding it through Torque::FS. + "aiff", + "asf", + "asx", + "dls", + "flac", + "fsb", + "it", + "m3u", + "mid", + "mod", + "mp2", + "mp3", + "ogg", + "pls", + "s3m", + "vag", + "wav", + "wax", + "wma", + "xm", + +#ifdef TORQUE_OS_XENON + ".xma", +#endif + + NULL +}; + +//----------------------------------------------------------------------------- + +SFXFMODBuffer* SFXFMODBuffer::create( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ) +{ + SFXFMODBuffer *buffer = new SFXFMODBuffer( stream, description ); + if( !buffer->mSound ) + SAFE_DELETE( buffer ); + + return buffer; +} + +//----------------------------------------------------------------------------- + +SFXFMODBuffer* SFXFMODBuffer::create( const String& filename, SFXDescription* description ) +{ + if( Con::getBoolVariable( "$pref::SFX::FMOD::noCustomFileLoading", false ) ) + return NULL; + + SFXFMODBuffer *buffer = new SFXFMODBuffer( filename, description ); + if( !buffer->mSound ) + SAFE_DELETE( buffer ); + + return buffer; +} + +//----------------------------------------------------------------------------- + +SFXFMODBuffer::SFXFMODBuffer( const String& filename, SFXDescription* description ) + : Parent( description ), + mSound( NULL ) +{ + FMOD_MODE fMode = ( description->mUseHardware ? FMOD_HARDWARE : FMOD_SOFTWARE ) + | ( description->mIs3D ? FMOD_3D : FMOD_2D ); + + if( description->mIsStreaming ) + { + fMode |= FMOD_CREATESTREAM; + mIsUnique = true; + } + + // Go through the extensions and try each with the given path. The + // first two are special. First we try without touching the filename at all + // so FMOD gets a chance to handle URLs and whatever, and then second we + // try by expanding the path but without adding an extension. + + Torque::Path path = filename; + for( U32 i = 0; sExtensions[ i ]; ++ i ) + { + path.setExtension( sExtensions[ i ] ); + + if( !i || Torque::FS::IsFile( path ) ) + { + // Translate to full path. + //TODO: Remove this when hooking up the file system functions in sfxFMODDevice.cpp + + String fullPath; + if( !i ) + fullPath = filename; + else + { + Torque::Path realPath; + if( !Torque::FS::GetFSPath( path, realPath ) ) + continue; + + fullPath = realPath.getFullPath().c_str(); + } + + mSound = NULL; + FMOD_RESULT result = SFXFMODDevice::smFunc->FMOD_System_CreateSound( + SFXFMODDevice::smSystem, + fullPath.c_str(), + fMode, + ( FMOD_CREATESOUNDEXINFO* ) NULL, + &mSound ); + + if( result == FMOD_OK ) + { + SFXFMODDevice::smFunc->FMOD_Sound_GetMode( mSound, &mMode ); + + // Read out format. + + int numChannels; + int bitsPerSample; + unsigned int length; + float frequency; + + SFXFMODDevice::smFunc->FMOD_Sound_GetFormat( mSound, ( FMOD_SOUND_TYPE* ) NULL, ( FMOD_SOUND_FORMAT* ) NULL, &numChannels, &bitsPerSample ); + SFXFMODDevice::smFunc->FMOD_Sound_GetLength( mSound, &length, FMOD_TIMEUNIT_MS ); + SFXFMODDevice::smFunc->FMOD_Sound_GetDefaults( mSound, &frequency, ( float* ) NULL, ( float* ) NULL, ( int* ) NULL ); + + mDuration = length; + mFormat = SFXFormat( numChannels, numChannels * bitsPerSample, frequency ); + + break; + } + } + } + + if( !mSound ) + Con::errorf( "SFXFMODBuffer::SFXFMODBuffer - failed to load '%s' through FMOD", filename.c_str() ); +} + +//----------------------------------------------------------------------------- + +SFXFMODBuffer::SFXFMODBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ) + : Parent( stream, description ), + mSound( NULL ) +{ + FMOD_MODE fMode = ( description->mUseHardware ? FMOD_HARDWARE : FMOD_SOFTWARE ) + | ( description->mIs3D ? FMOD_3D : FMOD_2D ); + + FMOD_CREATESOUNDEXINFO* pCreatesoundexinfo = NULL; + FMOD_CREATESOUNDEXINFO createsoundexinfo; + + fMode |= FMOD_OPENUSER; // this tells fmod we are supplying the data directly + if( isStreaming() ) + fMode |= FMOD_LOOP_NORMAL | FMOD_UNIQUE; + + const SFXFormat& format = getFormat(); + U32 channels = format.getChannels(); + U32 frequency = format.getSamplesPerSecond(); + U32 bitsPerChannel = format.getBitsPerSample() / channels; + U32 dataSize = mBufferSize; + + FMOD_SOUND_FORMAT sfxFmt = FMOD_SOUND_FORMAT_NONE; + switch(bitsPerChannel) + { + case 8: + sfxFmt = FMOD_SOUND_FORMAT_PCM8; + break; + case 16: + sfxFmt = FMOD_SOUND_FORMAT_PCM16; + break; + case 24: + sfxFmt = FMOD_SOUND_FORMAT_PCM24; + break; + case 32: + sfxFmt = FMOD_SOUND_FORMAT_PCM32; + break; + default: + AssertISV(false, "SFXFMODBuffer::SFXFMODBuffer() - unsupported bits-per-sample (what format is it in, 15bit PCM?)"); + break; + } + + dMemset(&createsoundexinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO)); + createsoundexinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO); /* required. */ + createsoundexinfo.decodebuffersize = frequency; /* Chunk size of stream update in samples. This will be the amount of data passed to the user callback. */ + createsoundexinfo.length = dataSize; /* Length of PCM data in bytes of whole sound (for Sound::getLength) */ + createsoundexinfo.numchannels = channels; /* Number of channels in the sound. */ + createsoundexinfo.defaultfrequency = frequency; /* Default playback rate of sound. */ + createsoundexinfo.format = sfxFmt; /* Data format of sound. */ + createsoundexinfo.pcmreadcallback = NULL; /* User callback for reading. */ + createsoundexinfo.pcmsetposcallback = NULL; /* User callback for seeking. */ + pCreatesoundexinfo = &createsoundexinfo; + + FMOD_RESULT result = SFXFMODDevice::smFunc->FMOD_System_CreateSound( + SFXFMODDevice::smSystem, + ( const char* ) NULL, + fMode, + pCreatesoundexinfo, + &mSound ); + + if( result != FMOD_OK ) + { + mSound = NULL; + Con::errorf( "SFXFMODBuffer::SFXFMODBuffer - failed to create buffer (%i)", result ); + } + else + SFXFMODDevice::smFunc->FMOD_Sound_GetMode( mSound, &mMode ); +} + +//----------------------------------------------------------------------------- + +SFXFMODBuffer::~SFXFMODBuffer() +{ + if( mSound ) + FModAssert( SFXFMODDevice::smFunc->FMOD_Sound_Release( mSound ), + "SFXFMODBuffer::~SFXFMODBuffer - Failed to release a sound!" ); + + mSound = NULL; +} + +//----------------------------------------------------------------------------- + +void SFXFMODBuffer::_flush() +{ + AssertFatal( isStreaming(), "SFXFMODBuffer::_flush() - not a streaming buffer" ); + AssertFatal( SFXInternal::isSFXThread(), "SFXFMODBuffer::_flush() - not on SFX thread" ); + + Parent::_flush(); + SFXFMODDevice::smFunc->FMOD_Channel_SetPosition + ( ( ( SFXFMODVoice* ) mUniqueVoice.getPointer() )->mChannel, 0, FMOD_TIMEUNIT_PCM ); +} + +//----------------------------------------------------------------------------- + +bool SFXFMODBuffer::_copyData( U32 offset, const U8* data, U32 length ) +{ + AssertFatal( data != NULL && length > 0, "Must have data!" ); + + // Fill the buffer with the resource data. + void* lpvWrite; + U32 dwLength; + void* lpvWrite2; + U32 dwLength2; + int res = SFXFMODDevice::smFunc->FMOD_Sound_Lock( + mSound, + offset, // Offset at which to start lock. + length, // Size of lock. + &lpvWrite, // Gets address of first part of lock. + &lpvWrite2, // Address of wraparound not needed. + &dwLength, // Gets size of first part of lock. + &dwLength2 // Size of wraparound not needed. + ); + + if ( res != FMOD_OK ) + { + // You can remove this if it gets spammy. However since we can + // safely fail in this case it doesn't seem right to assert... + // at the same time it can be very annoying not to know why + // an upload fails! + Con::errorf("SFXFMODBuffer::_copyData - failed to lock a sound buffer! (%d)", this); + return false; + } + + // Copy the first part. + dMemcpy( lpvWrite, data, dwLength ); + + // Do we have a wrap? + if ( lpvWrite2 ) + dMemcpy( lpvWrite2, data + dwLength, dwLength2 ); + + // And finally, unlock. + FModAssert( SFXFMODDevice::smFunc->FMOD_Sound_Unlock( + mSound, + lpvWrite, // Address of lock start. + lpvWrite2, // No wraparound portion. + dwLength, // Size of lock. + dwLength2 ), // No wraparound size. + "Failed to unlock sound buffer!" ); + + return true; +} + +//----------------------------------------------------------------------------- + +U32 SFXFMODBuffer::getMemoryUsed() const +{ + unsigned int memoryUsed; + + SFXFMODDevice::smFunc->FMOD_Sound_GetMemoryInfo( + mSound, + FMOD_MEMBITS_ALL, + FMOD_EVENT_MEMBITS_ALL, + &memoryUsed, + ( unsigned int* ) NULL ); + + return memoryUsed; +} diff --git a/Engine/source/sfx/fmod/sfxFMODBuffer.h b/Engine/source/sfx/fmod/sfxFMODBuffer.h new file mode 100644 index 000000000..1ce76ecb5 --- /dev/null +++ b/Engine/source/sfx/fmod/sfxFMODBuffer.h @@ -0,0 +1,63 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXFMODBUFFER_H_ +#define _SFXFMODBUFFER_H_ + +#include "fmod.h" + +#ifndef _SFXINTERNAL_H_ +# include "sfx/sfxInternal.h" +#endif + + +class SFXFMODBuffer : public SFXInternal::SFXWrapAroundBuffer +{ + typedef SFXInternal::SFXWrapAroundBuffer Parent; + + friend class SFXFMODDevice; + friend class SFXFMODVoice; + + protected: + + FMOD_SOUND *mSound; + FMOD_MODE mMode; + + SFXFMODBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ); + SFXFMODBuffer( const String& filename, SFXDescription* description ); + + // SFXWrapAroundBuffer. + virtual bool _copyData( U32 offset, const U8* data, U32 length ); + virtual void _flush(); + + virtual ~SFXFMODBuffer(); + + public: + + /// + static SFXFMODBuffer* create( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ); + static SFXFMODBuffer* create( const String& filename, SFXDescription* description ); + + virtual U32 getMemoryUsed() const; +}; + +#endif // _SFXFMODBUFFER_H_ \ No newline at end of file diff --git a/Engine/source/sfx/fmod/sfxFMODDevice.cpp b/Engine/source/sfx/fmod/sfxFMODDevice.cpp new file mode 100644 index 000000000..1ca461b0a --- /dev/null +++ b/Engine/source/sfx/fmod/sfxFMODDevice.cpp @@ -0,0 +1,581 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "platform/threads/mutex.h" +#include "sfx/fmod/sfxFMODDevice.h" +#include "sfx/fmod/sfxFMODBuffer.h" +#include "sfx/sfxSystem.h" +#include "platform/async/asyncUpdate.h" +#include "console/consoleTypes.h" +#include "core/volume.h" + + +bool SFXFMODDevice::smPrefDisableSoftware = false; +bool SFXFMODDevice::smPrefUseSoftwareOcclusion = true; +bool SFXFMODDevice::smPrefUseSoftwareHRTF = true; +bool SFXFMODDevice::smPrefUseSoftwareReverbLowmem = false; +bool SFXFMODDevice::smPrefEnableProfile = false; +bool SFXFMODDevice::smPrefGeometryUseClosest = false; +const char* SFXFMODDevice::smPrefDSoundHRTF = "full"; +const char* SFXFMODDevice::smPrefPluginPath = ""; +U32 SFXFMODDevice::smStatMemUsageCore; +U32 SFXFMODDevice::smStatMemUsageEvents; +U32 SFXFMODDevice::smStatNumEventSources; +SFXFMODDevice* SFXFMODDevice::smInstance; +FMOD_SYSTEM* SFXFMODDevice::smSystem; +FMOD_EVENTSYSTEM* SFXFMODDevice::smEventSystem; +FModFNTable* SFXFMODDevice::smFunc; +Mutex* FModFNTable::mutex; + + +//----------------------------------------------------------------------------- + +String FMODResultToString( FMOD_RESULT result ) +{ + switch( result ) + { + #define FMOD_ERROR( n ) case n: return #n; + #include "fmodErrors.h" + #undef FMOD_ERROR + + default: + break; + } + + return String(); +} + +//------------------------------------------------------------------------------ +// FMOD filesystem wrappers. +//FIXME: these are not thread-safe and cannot be used as such + +FMOD_RESULT F_CALLBACK fmodFileOpenCallback( const char* name, int unicode, unsigned int* filesize, void** handle, void** userdata ) +{ + String fileName; + if( unicode ) + fileName = String( ( UTF16* ) name ); + else + fileName = String( name ); + + Torque::FS::FileRef file = Torque::FS::OpenFile( fileName, Torque::FS::File::Read ); + if( !file ) + return FMOD_ERR_FILE_NOTFOUND; + else if( file->getStatus() != Torque::FS::File::Open ) + return FMOD_ERR_FILE_BAD; + + // Add a reference so we can pass it into FMOD. + file->incRefCount(); + + *filesize = U32( file->getSize() ); + *handle = file.getPointer(); + + return FMOD_OK; +} + +FMOD_RESULT F_CALLBACK fmodFileCloseCallback( void* handle, void* userdata ) +{ + Torque::FS::File* file = reinterpret_cast< Torque::FS::File* >( handle ); + file->decRefCount(); + return FMOD_OK; +} + +FMOD_RESULT F_CALLBACK fmodFileReadCallback( void* handle, void* buffer, unsigned int sizebytes, unsigned int* bytesread, void* userdata ) +{ + Torque::FS::File* file = reinterpret_cast< Torque::FS::File* >( handle ); + + U32 numRead = file->read( buffer, sizebytes ); + *bytesread = numRead; + + if( file->getStatus() == Torque::FS::File::EndOfFile ) + return FMOD_ERR_FILE_EOF; + else if( file->getStatus() != Torque::FS::File::Open ) + return FMOD_ERR_FILE_BAD; + + return FMOD_OK; +} + +FMOD_RESULT F_CALLBACK fmodFileSeekCallback( void* handle, unsigned int pos, void* userdata ) +{ + Torque::FS::File* file = reinterpret_cast< Torque::FS::File* >( handle ); + + if( file->setPosition( pos, Torque::FS::File::Begin ) != pos ) + return FMOD_ERR_FILE_COULDNOTSEEK; + + return FMOD_OK; +} + +//----------------------------------------------------------------------------- + +SFXFMODDevice::SFXFMODDevice( SFXProvider* provider, + FModFNTable *fmodFnTbl, + int deviceIdx, + String name ) + : SFXDevice( name, provider, false, 32 ), + m3drolloffmode( FMOD_3D_INVERSEROLLOFF ), + mDeviceIndex( deviceIdx ) +{ + // Store off the function pointers for later use. + smFunc = fmodFnTbl; + + smStatMemUsageCore = 0; + smStatMemUsageEvents = 0; + smStatNumEventSources = 0; + + // Register our SFXSystem plugin. + + SFX->addPlugin( &mPlugin ); + + smInstance = this; +} + +//----------------------------------------------------------------------------- + +SFXFMODDevice::~SFXFMODDevice() +{ + _releaseAllResources(); + + SFX->removePlugin( &mPlugin ); + + if( smEventSystem ) + { + smFunc->FMOD_EventSystem_Release( smEventSystem ); + smEventSystem = NULL; + smSystem = NULL; + } + else + smFunc->FMOD_System_Close( smSystem ); + + smInstance = NULL; +} + +//----------------------------------------------------------------------------- + +bool SFXFMODDevice::_init() +{ + #define FMOD_CHECK( message ) \ + if( result != FMOD_OK ) \ + { \ + Con::errorf( "SFXFMODDevice::_init() - %s (%s)", \ + message, \ + FMOD_ErrorString( result ) ); \ + return false; \ + } + + AssertISV(smSystem, + "SFXFMODDevice::_init() - can't init w/o an existing FMOD system handle!"); + + FMOD_RESULT result; + + // Get some prefs. + + if( smPrefPluginPath && smPrefPluginPath[ 0 ] ) + { + char fullPath[ 4096 ]; + Platform::makeFullPathName( smPrefPluginPath, fullPath, sizeof( fullPath ) ); + + smFunc->FMOD_System_SetPluginPath( smSystem, fullPath ); + } + else + { + smFunc->FMOD_System_SetPluginPath( smSystem, Platform::getExecutablePath() ); + } + + // Initialize everything from fmod. + FMOD_SPEAKERMODE speakermode; + FMOD_CAPS caps; + result = smFunc->FMOD_System_GetDriverCaps(smSystem, 0, &caps, ( int* ) 0, &speakermode); + FMOD_CHECK( "SFXFMODDevice::init - Failed to get driver caps" ); + + result = smFunc->FMOD_System_SetDriver(smSystem, mDeviceIndex); + FMOD_CHECK( "SFXFMODDevice::init - Failed to set driver" ); + + result = smFunc->FMOD_System_SetSpeakerMode(smSystem, speakermode); + FMOD_CHECK( "SFXFMODDevice::init - Failed to set the user selected speaker mode" ); + + if (caps & FMOD_CAPS_HARDWARE_EMULATED) /* The user has the 'Acceleration' slider set to off! This is really bad for latency!. */ + { /* You might want to warn the user about this. */ + result = smFunc->FMOD_System_SetDSPBufferSize(smSystem, 1024, 10); + FMOD_CHECK( "SFXFMODDevice::init - Failed to set DSP buffer size" ); + } + + Con::printf( "\nFMOD Device caps:" ); + #define PRINT_CAP( name ) \ + if( caps & FMOD_CAPS_ ## name ) \ + Con::printf( #name ); + + PRINT_CAP( HARDWARE ); + PRINT_CAP( HARDWARE_EMULATED ); + PRINT_CAP( OUTPUT_MULTICHANNEL ); + PRINT_CAP( OUTPUT_FORMAT_PCM8 ); + PRINT_CAP( OUTPUT_FORMAT_PCM16 ); + PRINT_CAP( OUTPUT_FORMAT_PCM24 ); + PRINT_CAP( OUTPUT_FORMAT_PCM32 ); + PRINT_CAP( OUTPUT_FORMAT_PCMFLOAT ); + PRINT_CAP( REVERB_LIMITED ); + + Con::printf( "" ); + + bool tryAgain; + do + { + tryAgain = false; + + FMOD_INITFLAGS flags = FMOD_INIT_NORMAL | FMOD_INIT_VOL0_BECOMES_VIRTUAL; + + if( smPrefDisableSoftware ) + flags |= FMOD_INIT_SOFTWARE_DISABLE; + if( smPrefUseSoftwareOcclusion ) + flags |= FMOD_INIT_SOFTWARE_OCCLUSION; + if( smPrefUseSoftwareHRTF ) + flags |= FMOD_INIT_SOFTWARE_HRTF; + if( smPrefUseSoftwareReverbLowmem ) + flags |= FMOD_INIT_SOFTWARE_REVERB_LOWMEM; + if( smPrefEnableProfile ) + flags |= FMOD_INIT_ENABLE_PROFILE; + if( smPrefGeometryUseClosest ) + flags |= FMOD_INIT_GEOMETRY_USECLOSEST; + + if( smEventSystem ) + result = smFunc->FMOD_EventSystem_Init( smEventSystem, 100, flags, ( void* ) 0, FMOD_EVENT_INIT_NORMAL ); + else + result = smFunc->FMOD_System_Init( smSystem, 100, flags, ( void* ) 0 ); + + if( result == FMOD_ERR_OUTPUT_CREATEBUFFER ) /* Ok, the speaker mode selected isn't supported by this soundcard. Switch it back to stereo... */ + { + result = smFunc->FMOD_System_SetSpeakerMode( smSystem, FMOD_SPEAKERMODE_STEREO ); + FMOD_CHECK( "SFXFMODDevice::init - failed on fallback speaker mode setup" ); + tryAgain = true; + } + } while( tryAgain ); + FMOD_CHECK( "SFXFMODDevice::init - failed to init system" ); + + // Print hardware channel info. + + if( caps & FMOD_CAPS_HARDWARE ) + { + int num3D, num2D, numTotal; + + if( smFunc->FMOD_System_GetHardwareChannels( smSystem, &num2D, &num3D, &numTotal ) == FMOD_OK ) + Con::printf( "FMOD Hardware channels: 2d=%i, 3d=%i, total=%i", num2D, num3D, numTotal ); + } + + // Set up filesystem. + + //FIXME: Don't do this for now. Crashes on Windows. + #if 0 + smFunc->FMOD_System_SetFileSystem( smSystem, fmodFileOpenCallback, fmodFileCloseCallback, fmodFileReadCallback, fmodFileSeekCallback, -1 ); + #endif + + // Set capabilities. + + mCaps = CAPS_Reverb | CAPS_VoiceManagement; + if( smEventSystem ) + mCaps |= CAPS_FMODDesigner; + + // Start the update thread. + + #ifndef TORQUE_DEDICATED // Avoid dependency on platform/async for Linx dedicated. + + if( !Con::getBoolVariable( "$_forceAllMainThread" ) ) + { + SFXInternal::gUpdateThread = new AsyncPeriodicUpdateThread + ( "FMOD Update Thread", SFXInternal::gBufferUpdateList, + Con::getIntVariable( "$pref::SFX::updateInterval", SFXInternal::DEFAULT_UPDATE_INTERVAL ) ); + SFXInternal::gUpdateThread->start(); + } + + #endif + + return true; +} + +//----------------------------------------------------------------------------- + +SFXBuffer* SFXFMODDevice::createBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ) +{ + AssertFatal( stream, "SFXFMODDevice::createBuffer() - Got a null stream!" ); + AssertFatal( description, "SFXFMODDevice::createBuffer() - Got null description!" ); + + SFXFMODBuffer *buffer = SFXFMODBuffer::create( stream, description ); + if ( buffer ) + _addBuffer( buffer ); + + return buffer; +} + +//----------------------------------------------------------------------------- + +SFXBuffer* SFXFMODDevice::createBuffer( const String& filename, SFXDescription* description ) +{ + AssertFatal( filename.isNotEmpty(), "SFXFMODDevice::createBuffer() - Got an empty filename!" ); + AssertFatal( description, "SFXFMODDevice::createBuffer() - Got null description!" ); + + SFXFMODBuffer* buffer = SFXFMODBuffer::create( filename, description ); + if( buffer ) + _addBuffer( buffer ); + + return buffer; +} + +//----------------------------------------------------------------------------- + +SFXVoice* SFXFMODDevice::createVoice( bool is3D, SFXBuffer* buffer ) +{ + AssertFatal( buffer, "SFXFMODDevice::createVoice() - Got null buffer!" ); + + SFXFMODBuffer* fmodBuffer = dynamic_cast( buffer ); + AssertFatal( fmodBuffer, "SFXFMODDevice::createVoice() - Got bad buffer!" ); + + SFXFMODVoice* voice = SFXFMODVoice::create( this, fmodBuffer ); + if ( !voice ) + return NULL; + + _addVoice( voice ); + return voice; +} + +//----------------------------------------------------------------------------- + +void SFXFMODDevice::update() +{ + Parent::update(); + + if( smEventSystem ) + { + FModAssert( smFunc->FMOD_EventSystem_Update( smEventSystem ), "Failed to update event system!" ); + } + else + { + FModAssert(smFunc->FMOD_System_Update(smSystem), "Failed to update system!"); + } +} + +//----------------------------------------------------------------------------- + +void SFXFMODDevice::setNumListeners( U32 num ) +{ + smFunc->FMOD_System_Set3DNumListeners( smSystem, num ); +} + +//----------------------------------------------------------------------------- + +void SFXFMODDevice::setListener( U32 index, const SFXListenerProperties& listener ) +{ + FMOD_VECTOR position, forward, up, velocity; + + TorqueTransformToFMODVectors( listener.getTransform(), position, forward, up ); + TorqueVectorToFMODVector( listener.getVelocity(), velocity ); + + // Do the listener state update, then update! + smFunc->FMOD_System_Set3DListenerAttributes( smSystem, index, &position, &velocity, &forward, &up ); +} + +//----------------------------------------------------------------------------- + +void SFXFMODDevice::setDistanceModel( SFXDistanceModel model ) +{ + switch( model ) + { + case SFXDistanceModelLinear: + m3drolloffmode = FMOD_3D_LINEARROLLOFF; + break; + + case SFXDistanceModelLogarithmic: + m3drolloffmode = FMOD_3D_INVERSEROLLOFF; + break; + + default: + AssertWarn( false, "SFXFMODDevice::setDistanceModel - model not implemented" ); + } +} + +//----------------------------------------------------------------------------- + +void SFXFMODDevice::setDopplerFactor( F32 factor ) +{ + F32 dopplerFactor; + F32 distanceFactor; + F32 rolloffFactor; + + smFunc->FMOD_System_Get3DSettings( smSystem, &dopplerFactor, &distanceFactor, &rolloffFactor ); + dopplerFactor = factor; + smFunc->FMOD_System_Set3DSettings( smSystem, dopplerFactor, distanceFactor, rolloffFactor ); +} + +//----------------------------------------------------------------------------- + +void SFXFMODDevice::setRolloffFactor( F32 factor ) +{ + F32 dopplerFactor; + F32 distanceFactor; + F32 rolloffFactor; + + smFunc->FMOD_System_Get3DSettings( smSystem, &dopplerFactor, &distanceFactor, &rolloffFactor ); + rolloffFactor = factor; + smFunc->FMOD_System_Set3DSettings( smSystem, dopplerFactor, distanceFactor, rolloffFactor ); +} + +//----------------------------------------------------------------------------- + +void SFXFMODDevice::setReverb( const SFXReverbProperties& reverb ) +{ + FMOD_REVERB_PROPERTIES prop = FMOD_PRESET_GENERIC; + + prop.Environment = 0; + prop.EnvDiffusion = reverb.mEnvDiffusion; + prop.Room = reverb.mRoom; + prop.RoomHF = reverb.mRoomHF; + prop.RoomLF = reverb.mRoomLF; + prop.DecayTime = reverb.mDecayTime; + prop.DecayLFRatio = reverb.mDecayLFRatio; + prop.DecayHFRatio = reverb.mDecayHFRatio; + prop.Reflections = reverb.mReflections; + prop.ReflectionsDelay = reverb.mReflectionsDelay; + prop.Reverb = reverb.mReverb; + prop.ReverbDelay = reverb.mReverbDelay; + prop.ModulationTime = reverb.mModulationTime; + prop.ModulationDepth = reverb.mModulationDepth; + prop.HFReference = reverb.mHFReference; + prop.LFReference = reverb.mLFReference; + prop.Diffusion = reverb.mDiffusion; + prop.Density = reverb.mDensity; + prop.Flags = reverb.mFlags; + + // Here we only want to affect 3D sounds. While not quite obvious from the docs, + // SetReverbProperties sets the global reverb environment for 2D sounds whereas + // SetAmbientReverbProperties sets the global reverb environment for 3D sounds. + + FMOD_RESULT result = smFunc->FMOD_System_SetReverbAmbientProperties( smSystem, &prop ); + if( result != FMOD_OK ) + Con::errorf( "SFXFMODDevice::setReverb - Failed to set reverb (%s)", FMODResultToString( result ).c_str() ); +} + +//----------------------------------------------------------------------------- + +void SFXFMODDevice::resetReverb() +{ + FMOD_REVERB_PROPERTIES prop = FMOD_PRESET_OFF; + smFunc->FMOD_System_SetReverbProperties( smSystem, &prop ); +} + +//----------------------------------------------------------------------------- + +void SFXFMODDevice::updateMemUsageStats() +{ + smFunc->FMOD_System_GetMemoryInfo( smSystem, ( unsigned int ) FMOD_MEMBITS_ALL, + ( unsigned int ) 0, ( unsigned int* ) &smStatMemUsageCore, ( unsigned int* ) 0 ); + + if( smEventSystem ) + smFunc->FMOD_EventSystem_GetMemoryInfo( smEventSystem, ( unsigned int ) 0, + ( unsigned int ) FMOD_EVENT_MEMBITS_ALL, ( unsigned int* ) &smStatMemUsageEvents, ( unsigned int* ) 0 ); +} + +//============================================================================= +// Console Functions. +//============================================================================= +// MARK: ---- Console Functions ---- + +//------------------------------------------------------------------------------ + +ConsoleFunction( fmodDumpDSPInfo, void, 1, 1, "()" + "@brief Dump information about the standard DSP effects.\n\n" + "@ingroup SFXFMOD") +{ + if( !SFXFMODDevice::smFunc ) + return; + + const U32 firstDSPType = FMOD_DSP_TYPE_MIXER; + const U32 lastDSPType = FMOD_DSP_TYPE_TREMOLO; + + for( U32 i = firstDSPType; i <= lastDSPType; ++ i ) + { + FMOD_DSP* dsp; + if( SFXFMODDevice::smFunc->FMOD_System_CreateDSPByType( SFXFMODDevice::smSystem, ( FMOD_DSP_TYPE ) i, &dsp ) == FMOD_OK ) + { + // Print general info. + + char name[ 33 ]; + unsigned int version; + int channels; + int numParameters; + + dMemset( name, 0, sizeof( name ) ); + SFXFMODDevice::smFunc->FMOD_DSP_GetInfo( dsp, name, &version, &channels, ( int* ) NULL, ( int* ) NULL ); + SFXFMODDevice::smFunc->FMOD_DSP_GetNumParameters( dsp, &numParameters ); + + Con::printf( "----------------------------------------------------------------" ); + Con::printf( "DSP: %s", name ); + Con::printf( "Version: %i.%i", ( version & 0xffff0000 ) >> 16, version & 0xffff ); + Con::printf( "Channels: %i", channels ); + Con::printf( "Parameters: %i", numParameters ); + Con::printf( "" ); + + // Print parameter info. + + for( U32 n = 0; n < numParameters; ++ n ) + { + char name[ 17 ]; + char label[ 17 ]; + char description[ 1024 ]; + float minValue, maxValue; + float value; + char valueString[ 256 ]; + + dMemset( name, 0, sizeof( name ) ); + dMemset( label, 0, sizeof( label ) ); + dMemset( description, 0, sizeof( description ) ); + dMemset( valueString, 0, sizeof( valueString ) ); + + SFXFMODDevice::smFunc->FMOD_DSP_GetParameterInfo( dsp, n, name, label, description, sizeof( description ) - 1, &minValue, &maxValue ); + SFXFMODDevice::smFunc->FMOD_DSP_GetParameter( dsp, n, &value, valueString, sizeof( valueString ) - 1 ); + + Con::printf( "* Parameter %i", n ); + Con::printf( "Name: %s", name ); + Con::printf( "Label: %s", label ); + Con::printf( "Description: %s", description ); + Con::printf( "Min: %f", minValue ); + Con::printf( "Max: %f", maxValue ); + Con::printf( "Value: %f (%s)", value, valueString ); + Con::printf( "" ); + } + + // Release the DSP. + + SFXFMODDevice::smFunc->FMOD_DSP_Release( dsp ); + } + } +} + +//----------------------------------------------------------------------------- + +ConsoleFunction( fmodDumpMemoryStats, void, 1, 1, "()" + "@return Prints the current memory consumption of the FMOD module\n\n" + "@ingroup SFXFMOD") +{ + int current = 0; + int max = 0; + + if (SFXFMODDevice::smFunc && SFXFMODDevice::smFunc->FMOD_Memory_GetStats.fn) + SFXFMODDevice::smFunc->FMOD_Memory_GetStats(¤t, &max); + Con::printf("Fmod current: %d, max: %d", current, max); +} diff --git a/Engine/source/sfx/fmod/sfxFMODDevice.h b/Engine/source/sfx/fmod/sfxFMODDevice.h new file mode 100644 index 000000000..f18559ce0 --- /dev/null +++ b/Engine/source/sfx/fmod/sfxFMODDevice.h @@ -0,0 +1,345 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXFMODDEVICE_H_ +#define _SFXFMODDEVICE_H_ + +#ifndef _SFXDEVICE_H_ + #include "sfx/sfxDevice.h" +#endif +#ifndef _SFXFMODVOICE_H_ + #include "sfx/fmod/sfxFMODVoice.h" +#endif +#ifndef _SFXFMODBUFFER_H_ + #include "sfx/fmod/sfxFMODBuffer.h" +#endif +#ifndef _SFXFMODPLUGIN_H_ + #include "sfx/fmod/sfxFMODPlugin.h" +#endif + +#include "core/util/tDictionary.h" + + +// Disable warning for unused static functions. +#ifdef TORQUE_COMPILER_VISUALC + #pragma warning( disable : 4505 ) +#endif + +#if defined( TORQUE_OS_XENON ) || defined( TORQUE_OS_PS3 ) + #define TORQUE_FMOD_STATIC + #define TORQUE_FMOD_NO_EVENTS //TEMP +#endif + + +#include "fmod.h" +#include "fmod_errors.h" +#include "fmod_event.h" + +#include "platform/platformDlibrary.h" +#include "platform/threads/mutex.h" + + +// This doesn't appear to exist in some contexts, so let's just add it. +#if defined(TORQUE_OS_WIN32) || defined(TORQUE_OS_XENON) +#ifndef WINAPI +#define WINAPI __stdcall +#endif +#else +#define WINAPI +#endif + + +#define FModAssert(x, msg) \ + { FMOD_RESULT result = ( x ); \ + AssertISV( result == FMOD_OK, String::ToString( "%s: %s", msg, FMOD_ErrorString( result ) ) ); } + +#define FMOD_FN_FILE "sfx/fmod/fmodFunctions.h" + + +// Typedefs +#define FMOD_FUNCTION(fn_name, fn_args) \ + typedef FMOD_RESULT (WINAPI *FMODFNPTR##fn_name)fn_args; +#define FMOD_EVENT_FUNCTION(fn_name, fn_args, dllexport) \ + typedef FMOD_RESULT (WINAPI *FMODFNPTR##fn_name)fn_args; +#include FMOD_FN_FILE +#undef FMOD_FUNCTION +#undef FMOD_EVENT_FUNCTION + + +/// FMOD API function table. +/// +/// FMOD doesn't want to be called concurrently so in order to +/// not force everything to the main thread (where sound updates +/// would just stall during loading), we thunk all the API +/// calls and lock all API entry points to a single mutex. +struct FModFNTable +{ + FModFNTable() + : isLoaded( false ), + eventIsLoaded( false ), + dllRef( NULL ), + eventDllRef( NULL ) + { + AssertFatal( mutex == NULL, + "FModFNTable::FModFNTable() - this should be a singleton" ); + mutex = new Mutex; + } + ~FModFNTable() + { + eventDllRef = NULL; + dllRef = NULL; + delete mutex; + } + + bool isLoaded; + bool eventIsLoaded; + DLibraryRef dllRef; + DLibraryRef eventDllRef; + static Mutex* mutex; + + template< typename FN > + struct Thunk + { + FN fn; + + template< typename A > + FMOD_RESULT operator()( A a ) + { + mutex->lock(); + FMOD_RESULT result = fn( a ); + mutex->unlock(); + return result; + } + template< typename A, typename B > + FMOD_RESULT operator()( A a, B b ) + { + mutex->lock(); + FMOD_RESULT result = fn( a, b ); + mutex->unlock(); + return result; + } + template< typename A, typename B, typename C > + FMOD_RESULT operator()( A a, B b, C c ) + { + mutex->lock(); + FMOD_RESULT result = fn( a, b, c ); + mutex->unlock(); + return result; + } + template< typename A, typename B, typename C, typename D > + FMOD_RESULT operator()( A a, B b, C c, D d ) + { + mutex->lock(); + FMOD_RESULT result = fn( a, b, c, d ); + mutex->unlock(); + return result; + } + template< typename A, typename B, typename C, typename D, typename E > + FMOD_RESULT operator()( A a, B b, C c, D d, E e ) + { + mutex->lock(); + FMOD_RESULT result = fn( a, b, c, d, e ); + mutex->unlock(); + return result; + } + template< typename A, typename B, typename C, typename D, typename E, typename F > + FMOD_RESULT operator()( A a, B b, C c, D d, E e, F f ) + { + mutex->lock(); + FMOD_RESULT result = fn( a, b, c, d, e, f ); + mutex->unlock(); + return result; + } + template< typename A, typename B, typename C, typename D, typename E, typename F, typename G > + FMOD_RESULT operator()( A a, B b, C c, D d, E e, F f, G g ) + { + mutex->lock(); + FMOD_RESULT result = fn( a, b, c, d, e, f, g ); + mutex->unlock(); + return result; + } + template< typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H > + FMOD_RESULT operator()( A a, B b, C c, D d, E e, F f, G g, H h ) + { + mutex->lock(); + FMOD_RESULT result = fn( a, b, c, d, e, f, g, h ); + mutex->unlock(); + return result; + } + }; + +#define FMOD_FUNCTION(fn_name, fn_args) \ + Thunk< FMODFNPTR##fn_name > fn_name; +#define FMOD_EVENT_FUNCTION(fn_name, fn_args, dllexport) \ + Thunk< FMODFNPTR##fn_name > fn_name; +#include FMOD_FN_FILE +#undef FMOD_FUNCTION +#undef FMOD_EVENT_FUNCTION +}; + + +inline void TorqueVectorToFMODVector( const Point3F& torque, FMOD_VECTOR& fmod ) +{ + fmod.x = torque.x; + fmod.y = torque.z; + fmod.z = torque.y; +} + +inline void TorqueTransformToFMODVectors( const MatrixF& transform, FMOD_VECTOR& position, FMOD_VECTOR& forward, FMOD_VECTOR& up ) +{ + Point3F _pos, _fwd, _up; + + transform.getColumn( 3, &_pos ); + transform.getColumn( 1, &_fwd ); + transform.getColumn( 2, &_up ); + + TorqueVectorToFMODVector( _pos, position ); + TorqueVectorToFMODVector( _fwd, forward ); + TorqueVectorToFMODVector( _up, up ); +} + +inline int TorquePriorityToFMODPriority( F32 priority ) +{ + // Map [-2,2] to [256,0]. + + F32 n = mClampF( priority, -2.0f, 2.0f ) + 2.0f; + return ( n * 256.0f / 4.0f ); +} + +inline String FMODEventPathToTorqueName( const String& path ) +{ + String p = path; + p.replace( '/', '_' ); + p.replace( '-', '_' ); + p.replace( ' ', '_' ); + p.replace( '(', '_' ); + p.replace( ')', '_' ); + p.replace( '%', '_' ); + p.replace( '$', '_' ); + return p; +} + +inline String FMODEventPathToTorqueName( const String& projectName, const String& path ) +{ + return String::ToString( "%s_%s", projectName.c_str(), FMODEventPathToTorqueName( path ).c_str() ); +} + +extern String FMODResultToString( FMOD_RESULT result ); + + +class SFXProvider; +class SFXFMODPlugin; + + + +class SFXFMODDevice : public SFXDevice +{ + public: + + typedef SFXDevice Parent; + friend class SFXFMODProvider; // _init + friend class SFXFMODEventSource; // smStatNumEventSources + + explicit SFXFMODDevice(); + + SFXFMODDevice( SFXProvider* provider, FModFNTable *fmodFnTbl, int deviceIdx, String name ); + + virtual ~SFXFMODDevice(); + + protected: + + FMOD_MODE m3drolloffmode; + int mDeviceIndex; + + /// The FMOD SFXSystemPlugin instance. + SFXFMODPlugin mPlugin; + + /// @name Console Variables + /// @{ + + /// Current core FMOD memory usage in bytes. + static U32 smStatMemUsageCore; + + /// Current FMOD Event DLL memory usage in bytes. + static U32 smStatMemUsageEvents; + + /// Current number of SFXFMODEventSource instances. + static U32 smStatNumEventSources; + + /// + static bool smPrefDisableSoftware; + + /// + static bool smPrefUseSoftwareOcclusion; + + /// + static bool smPrefUseSoftwareHRTF; + + /// + static bool smPrefUseSoftwareReverbLowmem; + + /// + static bool smPrefEnableProfile; + + /// + static bool smPrefGeometryUseClosest; + + /// + static const char* smPrefDSoundHRTF; + + /// + static const char* smPrefPluginPath; + + /// @} + + bool _init(); + + static SFXFMODDevice* smInstance; + + public: + + static SFXFMODDevice* instance() { return smInstance; } + + FMOD_MODE get3dRollOffMode() { return m3drolloffmode; } + + static FMOD_SYSTEM* smSystem; + static FMOD_EVENTSYSTEM* smEventSystem; + static FModFNTable* smFunc; + + // Update memory usage stats for metrics display. + void updateMemUsageStats(); + + // SFXDevice. + virtual SFXBuffer* createBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ); + virtual SFXBuffer* createBuffer( const String& filename, SFXDescription* description ); + virtual SFXVoice* createVoice( bool is3D, SFXBuffer* buffer ); + virtual void update(); + virtual void setNumListeners( U32 num ); + virtual void setListener( U32 index, const SFXListenerProperties& listener ); + virtual void setDistanceModel( SFXDistanceModel model ); + virtual void setDopplerFactor( F32 factor ); + virtual void setRolloffFactor( F32 factor ); + virtual void setReverb( const SFXReverbProperties& reverb ); + virtual void resetReverb(); +}; + +#endif // _SFXFMODDEVICE_H_ diff --git a/Engine/source/sfx/fmod/sfxFMODEvent.cpp b/Engine/source/sfx/fmod/sfxFMODEvent.cpp new file mode 100644 index 000000000..196ef9dda --- /dev/null +++ b/Engine/source/sfx/fmod/sfxFMODEvent.cpp @@ -0,0 +1,339 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "sfx/fmod/sfxFMODEvent.h" +#include "sfx/fmod/sfxFMODEventGroup.h" +#include "sfx/fmod/sfxFMODProject.h" +#include "sfx/fmod/sfxFMODDevice.h" +#include "sfx/sfxParameter.h" +#include "sfx/sfxDescription.h" +#include "core/stream/bitStream.h" + + +IMPLEMENT_CO_DATABLOCK_V1( SFXFMODEvent ); + + +ConsoleDocClass( SFXFMODEvent, + "@brief A playable sound event in an FMOD Designer audio project.\n\n" + + "@ingroup SFXFMOD\n" + "@ingroup Datablocks" +); + + +//----------------------------------------------------------------------------- + +SFXFMODEvent::SFXFMODEvent() + : mGroup( NULL ), + mHandle( NULL ), + mGroupId( 0 ), + mSibling( NULL ) +{ + dMemset( mParameterRanges, 0, sizeof( mParameterRanges ) ); + dMemset( mParameterValues, 0, sizeof( mParameterValues ) ); +} + +//----------------------------------------------------------------------------- + +SFXFMODEvent::SFXFMODEvent( SFXFMODEventGroup* group, FMOD_EVENT* handle ) + : mGroup( group ), + mHandle( handle ), + mGroupId( 0 ), + mSibling( NULL ) +{ + dMemset( mParameterRanges, 0, sizeof( mParameterRanges ) ); + dMemset( mParameterValues, 0, sizeof( mParameterValues ) ); + + // Fetch name. + + int index; + char* name = NULL; + + SFXFMODDevice::smFunc->FMOD_Event_GetInfo( mHandle, &index, &name, ( FMOD_EVENT_INFO* ) 0 ); + + mName = name; + + // Read out the parameter info so we can immediately create + // the events on the client-side without having to open and + // read all the project info there. + + int numParameters; + SFXFMODDevice::smFunc->FMOD_Event_GetNumParameters( mHandle, &numParameters ); + if( numParameters > MaxNumParameters ) + { + Con::errorf( "SFXFMODEvent::SFXFMODEvent - event '%s' has more parameters (%i) than supported per SFXTrack (%i)", + getQualifiedName().c_str(), + numParameters, + MaxNumParameters ); + numParameters = MaxNumParameters; + } + + for( U32 i = 0; i < numParameters; ++ i ) + { + FMOD_EVENTPARAMETER* parameter; + SFXFMODDevice::smFunc->FMOD_Event_GetParameterByIndex( mHandle, i, ¶meter ); + + SFXFMODDevice::smFunc->FMOD_EventParameter_GetInfo( parameter, &index, &name ); + setParameter( i, name ); + + // Get value and range of parameter. + + SFXFMODDevice::smFunc->FMOD_EventParameter_GetValue( parameter, &mParameterValues[ i ] ); + SFXFMODDevice::smFunc->FMOD_EventParameter_GetRange( parameter, &mParameterRanges[ i ].x, &mParameterRanges[ i ].y ); + } + + // Read out the properties and create a custom SFXDescription for the event. + + mDescription = new SFXDescription; + if( !group->isClientOnly() ) + mDescription->assignId(); + + mDescription->registerObject( + String::ToString( "%s_%s_Description", + group->getName(), + FMODEventPathToTorqueName( mName ).c_str() + ) + ); + if( group->isClientOnly() ) + Sim::getRootGroup()->addObject( mDescription ); + + int intValue; + float floatValue; + + if( SFXFMODDevice::smFunc->FMOD_Event_GetPropertyByIndex( mHandle, FMOD_EVENTPROPERTY_MODE, &intValue, true ) == FMOD_OK ) + mDescription->mIs3D = ( intValue == FMOD_3D ); + if( SFXFMODDevice::smFunc->FMOD_Event_GetPropertyByIndex( mHandle, FMOD_EVENTPROPERTY_VOLUME, &floatValue, true ) == FMOD_OK ) + mDescription->mVolume = floatValue; + if( SFXFMODDevice::smFunc->FMOD_Event_GetPropertyByIndex( mHandle, FMOD_EVENTPROPERTY_PITCH, &floatValue, true ) == FMOD_OK ) + mDescription->mPitch = floatValue; + if( SFXFMODDevice::smFunc->FMOD_Event_GetPropertyByIndex( mHandle, FMOD_EVENTPROPERTY_3D_MINDISTANCE, &floatValue, true ) == FMOD_OK ) + mDescription->mMinDistance = floatValue; + if( SFXFMODDevice::smFunc->FMOD_Event_GetPropertyByIndex( mHandle, FMOD_EVENTPROPERTY_3D_MAXDISTANCE, &floatValue, true ) == FMOD_OK ) + mDescription->mMaxDistance = floatValue; + if( SFXFMODDevice::smFunc->FMOD_Event_GetPropertyByIndex( mHandle, FMOD_EVENTPROPERTY_3D_CONEINSIDEANGLE, &floatValue, true ) == FMOD_OK ) + mDescription->mConeInsideAngle = floatValue; + if( SFXFMODDevice::smFunc->FMOD_Event_GetPropertyByIndex( mHandle, FMOD_EVENTPROPERTY_3D_CONEOUTSIDEANGLE, &floatValue, true ) == FMOD_OK ) + mDescription->mConeOutsideAngle = floatValue; + if( SFXFMODDevice::smFunc->FMOD_Event_GetPropertyByIndex( mHandle, FMOD_EVENTPROPERTY_3D_CONEOUTSIDEVOLUME, &floatValue, true ) == FMOD_OK ) + mDescription->mConeOutsideVolume = floatValue; + + // Don't read out fade values as we want to leave fade-effects to + // FMOD rather than having the fading system built into SFX pick + // these values up. +} + +//----------------------------------------------------------------------------- + +SFXFMODEvent::~SFXFMODEvent() +{ +} + +//----------------------------------------------------------------------------- + +void SFXFMODEvent::initPersistFields() +{ + addGroup( "DO NOT MODIFY!!" ); + addField( "fmodGroup", TYPEID< SFXFMODEventGroup >(), Offset( mGroup, SFXFMODEvent ), "DO NOT MODIFY!!" ); + addField( "fmodName", TypeRealString, Offset( mName, SFXFMODEvent ), "DO NOT MODIFY!!" ); + addField( "fmodParameterRanges", TypePoint2F, Offset( mParameterRanges, SFXFMODEvent ), MaxNumParameters, "DO NOT MODIFY!!" ); + addField( "fmodParameterValues", TypeF32, Offset( mParameterValues, SFXFMODEvent ), MaxNumParameters, "DO NOT MODIFY!!" ); + endGroup( "DO NOT MODIFY!!" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +bool SFXFMODEvent::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + if( !mGroup ) + { + Con::errorf( "SFXFMODEvent::onAdd - no group set; this event was not properly constructed" ); + return false; + } + + mGroup->_addEvent( this ); + mGroup->mProject->_addEvent( this ); + + // For non-networked event datablocks, create the parameter + // instances now. + + if( isClientOnly() ) + _createParameters(); + + return true; +} + +//----------------------------------------------------------------------------- + +void SFXFMODEvent::onRemove() +{ + Parent::onRemove(); + + if( !mGroup ) + return; + + release(); + mGroup->_removeEvent( this ); +} + +//----------------------------------------------------------------------------- + +bool SFXFMODEvent::preload( bool server, String& errorStr ) +{ + if( !Parent::preload( server, errorStr ) ) + return false; + + if( !server ) + { + if( !Sim::findObject( mGroupId, mGroup ) ) + { + errorStr = String::ToString( "SFXFMODEvent - group '%i' does not exist", mGroupId ); + return false; + } + + // Create parameters. + + _createParameters(); + } + + return true; +} + +//----------------------------------------------------------------------------- + +void SFXFMODEvent::packData( BitStream* stream ) +{ + Parent::packData( stream ); + + stream->write( mName ); + stream->writeRangedS32( mGroup->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); + + for( U32 i = 0; i < MaxNumParameters; ++ i ) + if( stream->writeFlag( mParameters[ i ] ) ) + { + stream->write( mParameterValues[ i ] ); + stream->write( mParameterRanges[ i ].x ); + stream->write( mParameterRanges[ i ].y ); + } +} + +//----------------------------------------------------------------------------- + +void SFXFMODEvent::unpackData( BitStream* stream ) +{ + Parent::unpackData( stream ); + + stream->read( &mName ); + mGroupId = stream->readRangedS32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); + + for( U32 i = 0; i < MaxNumParameters; ++ i ) + if( stream->readFlag() ) + { + stream->read( &mParameterValues[ i ] ); + stream->read( &mParameterRanges[ i ].x ); + stream->read( &mParameterRanges[ i ].y ); + } + else + { + mParameterValues[ i ] = 0.f; + mParameterRanges[ i ].x = 0.f; + mParameterRanges[ i ].y = 0.f; + } +} + +//----------------------------------------------------------------------------- + +void SFXFMODEvent::acquire() +{ + if( mHandle ) + return; + + mGroup->acquire(); + if( SFXFMODDevice::smFunc->FMOD_EventGroup_GetEvent( + mGroup->mHandle, mName.c_str(), FMOD_EVENT_INFOONLY, &mHandle ) != FMOD_OK ) + { + Con::errorf( "SFXFMODEvent::acquire() - failed to acquire event '%s'", getQualifiedName().c_str() ); + return; + } +} + +//----------------------------------------------------------------------------- + +void SFXFMODEvent::release() +{ + if( !mHandle ) + return; + + SFXFMODDevice::smFunc->FMOD_Event_Release( mHandle, true, false ); + mHandle = NULL; +} + +//----------------------------------------------------------------------------- + +String SFXFMODEvent::getQualifiedName() const +{ + return String::ToString( "%s/%s", getEventGroup()->getQualifiedName().c_str(), mName.c_str() ); +} + +//----------------------------------------------------------------------------- + +void SFXFMODEvent::_createParameters() +{ + const String& projectFileName = getEventGroup()->getProject()->getFileName(); + const String qualifiedGroupName = getEventGroup()->getQualifiedName(); + const String description = String::ToString( "FMOD Event Parameter (%s)", projectFileName.c_str() ); + + for( U32 i = 0; i < MaxNumParameters; ++ i ) + { + StringTableEntry name = getParameter( i ); + if( !name ) + continue; + + SFXParameter* parameter = SFXParameter::find( name ); + if( !parameter ) + { + parameter = new SFXParameter(); + parameter->setInternalName( name ); + parameter->registerObject(); + + // Set up parameter. + + parameter->setChannel( SFXChannelUser0 ); + parameter->setRange( mParameterRanges[ i ] ); + parameter->setDefaultValue( mParameterValues[ i ] ); + parameter->setValue( mParameterValues[ i ] ); + parameter->setDescription( description ); + + // Set categories for easy filtering. + + static StringTableEntry sCategories = StringTable->insert( "categories" ); + parameter->setDataField( sCategories, "0", "FMOD" ); + parameter->setDataField( sCategories, "1", avar( "FMOD Project: %s", projectFileName.c_str() ) ); + parameter->setDataField( sCategories, "2", avar( "FMOD Group: %s", qualifiedGroupName.c_str() ) ); + } + } +} diff --git a/Engine/source/sfx/fmod/sfxFMODEvent.h b/Engine/source/sfx/fmod/sfxFMODEvent.h new file mode 100644 index 000000000..1e4c53e00 --- /dev/null +++ b/Engine/source/sfx/fmod/sfxFMODEvent.h @@ -0,0 +1,135 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXFMODEVENT_H_ +#define _SFXFMODEVENT_H_ + +#ifndef _SFXTRACK_H_ + #include "sfx/sfxTrack.h" +#endif +#ifndef _CONSOLETYPES_H_ + #include "console/consoleTypes.h" +#endif +#ifndef _MPOINT2_H_ + #include "math/mPoint2.h" +#endif + +#include "fmod_event.h" + + +class SFXFMODProject; +class SFXFMODEventGroup; + + +/// An event in an FMOD Designer project. +/// +/// This class must not be manually instanced by the user. Instead, SFXFMODEvents +/// are automatically created when an SFXFMODProject is loaded. +/// +/// Be aware that as all the playback happens internally within FMOD's event system, +/// this bypasses the SFX layer and will thus not work with features that rely the +/// structures there. Namely, sound occlusion (except for FMOD's own occlusion) will +/// not work with FMOD events. +/// +/// The parameters of an FMOD event are automatically created and designed using the +/// information in the project. +/// +class SFXFMODEvent : public SFXTrack +{ + public: + + typedef SFXTrack Parent; + friend class SFXFMODEventGroup; + friend class SFXFMODEventSource; + + protected: + + /// Name of the event in the Designer project. + String mName; + + /// Event group that this event belongs to. + SFXFMODEventGroup* mGroup; + + /// Next event in the group's event chain. + SFXFMODEvent* mSibling; + + /// FMOD event handle when event is open. Client-side only. + FMOD_EVENT* mHandle; + + /// + Point2F mParameterRanges[ MaxNumParameters ]; + + /// + F32 mParameterValues[ MaxNumParameters ]; + + /// Group ID for client net sync. + S32 mGroupId; + + /// + void _createParameters(); + + public: + + /// + SFXFMODEvent(); + + /// + SFXFMODEvent( SFXFMODEventGroup* group, const String& name ); + + /// + SFXFMODEvent( SFXFMODEventGroup* group, FMOD_EVENT* handle ); + + ~SFXFMODEvent(); + + /// Create the event object on the FMOD device. + void acquire(); + + /// Release the event object on the FMOD device. + void release(); + + /// + const String& getEventName() const { return mName; } + + /// + SFXFMODEventGroup* getEventGroup() const { return mGroup; } + + /// + String getQualifiedName() const; + + /// + bool isDataLoaded() const; + + // SFXTrack. + virtual bool onAdd(); + virtual void onRemove(); + virtual bool preload( bool server, String& errorStr ); + virtual void packData( BitStream* stream ); + virtual void unpackData( BitStream* stream ); + + static void initPersistFields(); + + DECLARE_CONOBJECT( SFXFMODEvent ); + DECLARE_CATEGORY( "SFX FMOD" ); + DECLARE_DESCRIPTION( "An FMOD Designer event." ); +}; + +#endif // !_SFXFMODEVENT_H_ diff --git a/Engine/source/sfx/fmod/sfxFMODEventGroup.cpp b/Engine/source/sfx/fmod/sfxFMODEventGroup.cpp new file mode 100644 index 000000000..a42f08bde --- /dev/null +++ b/Engine/source/sfx/fmod/sfxFMODEventGroup.cpp @@ -0,0 +1,510 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/fmod/sfxFMODEventGroup.h" +#include "sfx/fmod/sfxFMODDevice.h" +#include "sfx/fmod/sfxFMODEvent.h" +#include "sfx/fmod/sfxFMODProject.h" +#include "core/stream/bitStream.h" +#include "console/engineAPI.h" + + +IMPLEMENT_CO_DATABLOCK_V1( SFXFMODEventGroup ); + +ConsoleDocClass( SFXFMODEventGroup, + "@brief A group of events in an imported FMOD Designer project.\n\n" + + "" + + "@note Instances of this class \n\n" + + "@ingroup SFXFMOD\n" + "@ingroup Datablocks" +); + +//----------------------------------------------------------------------------- + +SFXFMODEventGroup::SFXFMODEventGroup() + : mProject( NULL ), + mHandle( NULL ), + mParent( NULL ), + mChildren( NULL ), + mSibling( NULL ), + mLoadCount( 0 ), + mEvents( NULL ), + mNumEvents( 0 ), + mNumGroups( 0 ), + mParentId( 0 ), + mProjectId( 0 ) +{ +} + +//----------------------------------------------------------------------------- + +SFXFMODEventGroup::SFXFMODEventGroup( SFXFMODProject* project, FMOD_EVENTGROUP* handle, SFXFMODEventGroup* parent ) + : mProject( project ), + mHandle( handle ), + mParent( parent ), + mChildren( NULL ), + mSibling( NULL ), + mLoadCount( 0 ), + mEvents( NULL ), + mNumEvents( 0 ), + mNumGroups( 0 ), + mParentId( 0 ), + mProjectId( 0 ) +{ + AssertFatal( project != NULL, "SFXFMODEventGroup::SFXFMODEventGroup - got a NULL project!" ); + AssertFatal( handle != NULL, "SFXFMODEventGroup::SFXFMODEventGroup - got a NULL group handle!" ); + + // Fetch the name. + + int index; + char* name = NULL; + + SFXFMODDevice::smFunc->FMOD_EventGroup_GetInfo( handle, &index, &name ); + + mName = name; +} + +//----------------------------------------------------------------------------- + +SFXFMODEventGroup::~SFXFMODEventGroup() +{ + AssertFatal( mEvents == NULL, "SFXFMODEventGroup::~SFXFMODEventGroup - group still has events attached" ); + AssertFatal( mChildren == NULL, "SFXFMODEventGroup::~SFXFMODEventGroup - group still has subgroups attached" ); +} + +//----------------------------------------------------------------------------- + +void SFXFMODEventGroup::initPersistFields() +{ + addGroup( "DO NOT MODIFY!!" ); + addField( "fmodProject", TYPEID< SFXFMODProject >(), Offset( mProject, SFXFMODEventGroup ), "DO NOT MODIFY!!" ); + addField( "fmodGroup", TYPEID< SFXFMODEventGroup >(), Offset( mParent, SFXFMODEventGroup ), "DO NOT MODIFY!!" ); + addField( "fmodName", TypeRealString, Offset( mName, SFXFMODEventGroup ), "DO NOT MODIFY!!" ); + endGroup( "DO NOT MODIFY!!" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +bool SFXFMODEventGroup::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + if( !mProject ) + { + Con::errorf( "SFXFMODEventGroup - not part of a project" ); + return false; + } + + if( mParent ) + mParent->_addGroup( this ); + + mProject->_addGroup( this ); + + return true; +} + +//----------------------------------------------------------------------------- + +void SFXFMODEventGroup::onRemove() +{ + Parent::onRemove(); + + if( !mProject ) + return; + + release(); + + while( mEvents ) + mEvents->deleteObject(); + while( mChildren ) + mChildren->deleteObject(); + + if( mParent ) + mParent->_removeGroup( this ); + + mProject->_removeGroup( this ); +} + +//----------------------------------------------------------------------------- + +bool SFXFMODEventGroup::preload( bool server, String& errorStr ) +{ + if( !Parent::preload( server, errorStr ) ) + return false; + + if( !server ) + { + if( mParentId != 0 && !Sim::findObject( mParentId, mParent ) ) + { + errorStr = String::ToString( "SFXFMODEventGroup - parent group '%i' does not exist", mParentId ); + return false; + } + if( !Sim::findObject( mProjectId, mProject ) ) + { + errorStr = String::ToString( "SFXFMODEventGroup - project '%i' does not exist", mProjectId ); + return false; + } + } + + return true; +} + +//----------------------------------------------------------------------------- + +void SFXFMODEventGroup::packData( BitStream* stream ) +{ + Parent::packData( stream ); + + stream->write( mName ); + stream->writeRangedS32( mProject->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); + if( stream->writeFlag( mParent ) ) + stream->writeRangedS32( mParent->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); +} + +//----------------------------------------------------------------------------- + +void SFXFMODEventGroup::unpackData( BitStream* stream ) +{ + Parent::unpackData( stream ); + + stream->read( &mName ); + + mProjectId = stream->readRangedS32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); + if( stream->readFlag() ) + mParentId = stream->readRangedS32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); + else + mParentId = 0; +} + +//----------------------------------------------------------------------------- + +String SFXFMODEventGroup::getQualifiedName() const +{ + if( mParent ) + return String::ToString( "%s/%s", mParent->getQualifiedName().c_str(), mName.c_str() ); + else + return mName; +} + +//----------------------------------------------------------------------------- + +bool SFXFMODEventGroup::isDataLoaded() const +{ + // Check whether we or any of our parents has triggered a load. + + for( const SFXFMODEventGroup* group = this; group != NULL; group = group->mParent ) + if( group->mLoadCount > 0 ) + return true; + + return false; +} + +//----------------------------------------------------------------------------- + +bool SFXFMODEventGroup::loadData( bool samples, bool streams ) +{ + if( !mHandle ) + acquire(); + + if( !mLoadCount ) + { + FMOD_EVENT_RESOURCE resource; + if( samples && streams ) + resource = FMOD_EVENT_RESOURCE_STREAMS_AND_SAMPLES; + else if( samples ) + resource = FMOD_EVENT_RESOURCE_SAMPLES; + else if( streams ) + resource = FMOD_EVENT_RESOURCE_STREAMS; + else + return true; + + FMOD_RESULT result = SFXFMODDevice::smFunc->FMOD_EventGroup_LoadEventData( mHandle, resource, FMOD_EVENT_DEFAULT ); + if( result != FMOD_OK ) + { + Con::errorf( "SFXFMODEventGroup::loadData - could not load data: %s", FMODResultToString( result ).c_str() ); + return false; + } + + SFXFMODDevice::instance()->updateMemUsageStats(); + Con::printf( "SFXFMODProject - %s: Loaded data for group '%s'", mProject->getName(), getQualifiedName().c_str() ); + } + + mLoadCount ++; + return true; +} + +//----------------------------------------------------------------------------- + +void SFXFMODEventGroup::freeData( bool force ) +{ + bool isLoaded = ( mLoadCount > 0 ); + + if( !isLoaded ) + isLoaded = ( mParent ? mParent->isDataLoaded() : false ); + else + { + if( force ) + mLoadCount = 0; + else + -- mLoadCount; + } + + if( !mLoadCount && isLoaded ) + { + FMOD_RESULT result = SFXFMODDevice::smFunc->FMOD_EventGroup_FreeEventData( mHandle, ( FMOD_EVENT* ) NULL, false ); + if( result != FMOD_OK ) + Con::errorf( "SFXFMODEventGroup - failed freeing event data: %s", FMODResultToString( result ).c_str() ); + + SFXFMODDevice::instance()->updateMemUsageStats(); + Con::printf( "SFXFMODProject - %s: Cleared data for group '%s'", mProject->getName(), getQualifiedName().c_str() ); + } +} + +//----------------------------------------------------------------------------- + +void SFXFMODEventGroup::acquire( bool recursive ) +{ + // Make sure the project is acquired. + + mProject->acquire(); + + // Acquire the group. + + if( !mHandle ) + { + if( mParent ) + { + mParent->acquire(); + SFXFMODDevice::smFunc->FMOD_EventGroup_GetGroup( mParent->mHandle, mName, true, &mHandle ); + } + else + { + mProject->acquire(); + SFXFMODDevice::smFunc->FMOD_EventProject_GetGroup( mProject->mHandle, mName, true, &mHandle ); + } + } + + // Acquite events and subgroups. + + if( recursive ) + { + for( SFXFMODEvent* event = mEvents; event != NULL; event = event->mSibling ) + event->acquire(); + + for( SFXFMODEventGroup* group = mChildren; group != NULL; group = group->mSibling ) + group->acquire( true ); + } +} + +//----------------------------------------------------------------------------- + +void SFXFMODEventGroup::release() +{ + if( !mHandle ) + return; + + // Free the event data if we still have it loaded. + + if( isDataLoaded() ) + freeData( true ); + + // Release events. + + for( SFXFMODEvent* event = mEvents; event != NULL; event = event->mSibling ) + event->release(); + + // Release children. + + for( SFXFMODEventGroup* child = mChildren; child != NULL; child = child->mSibling ) + child->release(); + + // Release our handle. + + freeData(); + mHandle = NULL; +} + +//----------------------------------------------------------------------------- + +void SFXFMODEventGroup::_load() +{ + // Make sure we have the group open. + + if( !mHandle ) + acquire(); + + // Fetch info. + + int numEvents; + int numGroups; + + SFXFMODDevice::smFunc->FMOD_EventGroup_GetNumEvents( mHandle, &numEvents ); + SFXFMODDevice::smFunc->FMOD_EventGroup_GetNumGroups( mHandle, &numGroups ); + + // Load events. + + for( U32 i = 0; i < numEvents; ++ i ) + { + FMOD_EVENT* handle; + if( SFXFMODDevice::smFunc->FMOD_EventGroup_GetEventByIndex( mHandle, i, FMOD_EVENT_INFOONLY, &handle ) == FMOD_OK ) + { + SFXFMODEvent* event = new SFXFMODEvent( this, handle ); + if( !isClientOnly() ) + event->assignId(); + + event->registerObject( String::ToString( "%s_%s", getName(), FMODEventPathToTorqueName( event->getEventName() ).c_str() ) ); + if( isClientOnly() ) + Sim::getRootGroup()->addObject( event ); + } + } + + // Load subgroups. + + for( U32 i = 0; i < numGroups; ++ i ) + { + FMOD_EVENTGROUP* handle; + if( SFXFMODDevice::smFunc->FMOD_EventGroup_GetGroupByIndex( mHandle, i, true, &handle ) == FMOD_OK ) + { + SFXFMODEventGroup* group = new SFXFMODEventGroup( mProject, handle, this ); + if( !isClientOnly() ) + group->assignId(); + + group->registerObject( String::ToString( "%s_%s", getName(), FMODEventPathToTorqueName( group->getGroupName() ).c_str() ) ); + if( isClientOnly() ) + Sim::getRootGroup()->addObject( group ); + + group->_load(); + } + } +} + +//----------------------------------------------------------------------------- + +void SFXFMODEventGroup::_addEvent( SFXFMODEvent* event ) +{ + event->mSibling = mEvents; + mEvents = event; + mNumEvents ++; +} + +//----------------------------------------------------------------------------- + +void SFXFMODEventGroup::_removeEvent( SFXFMODEvent* event ) +{ + if( mEvents == event ) + { + mEvents = event->mSibling; + event->mSibling = NULL; + mNumEvents --; + } + else + { + SFXFMODEvent* p = mEvents; + while( p != NULL && p->mSibling != event ) + p = p->mSibling; + + if( p ) + { + p->mSibling = event->mSibling; + event->mSibling = NULL; + mNumEvents --; + } + } +} + +//----------------------------------------------------------------------------- + +void SFXFMODEventGroup::_addGroup( SFXFMODEventGroup* group ) +{ + group->mSibling = mChildren; + mChildren = group; + mNumGroups ++; +} + +//----------------------------------------------------------------------------- + +void SFXFMODEventGroup::_removeGroup( SFXFMODEventGroup* group ) +{ + if( mChildren == group ) + { + mChildren = group->mSibling; + group->mSibling = NULL; + mNumGroups --; + } + else + { + SFXFMODEventGroup* p = mChildren; + while( p != NULL && p->mSibling != group ) + p = p->mSibling; + + if( p ) + { + p->mSibling = group->mSibling; + group->mSibling = NULL; + mNumGroups --; + } + } +} + +//============================================================================= +// Console Methods. +//============================================================================= +// MARK: ---- Console Methods ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXFMODEventGroup, isDataLoaded, bool, (),, + "Test whether the resource data for this group has been loaded.\n\n" + "@return True if the resource data for this group is currently loaded.\n" ) +{ + return object->isDataLoaded(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXFMODEventGroup, loadData, bool, ( bool loadStreams, bool loadSamples ), ( true, true ), + "Load the resource data for this group, if it has not already been loaded (either directly " + "or indirectly through a parent group).\n" + "This method works recursively and thus data for direct and indirect child groups to this group will be " + "loaded as well.\n\n" + "@param loadStreams Whether to open streams.\n" + "@param loadSamples Whether to load sample banks.\n" + "@return True if the data has been successfully loaded; false otherwise.\n\n" + "@see SFXFMODProject_resources" ) +{ + return object->loadData( loadSamples, loadStreams ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXFMODEventGroup, freeData, void, (),, + "Release the resource data for this group and its subgroups.\n\n" + "@see SFXFMODProject_resources" ) +{ + object->freeData(); +} diff --git a/Engine/source/sfx/fmod/sfxFMODEventGroup.h b/Engine/source/sfx/fmod/sfxFMODEventGroup.h new file mode 100644 index 000000000..12140c2bf --- /dev/null +++ b/Engine/source/sfx/fmod/sfxFMODEventGroup.h @@ -0,0 +1,159 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXFMODEVENTGROUP_H_ +#define _SFXFMODEVENTGROUP_H_ + +#ifndef _SIMDATABLOCK_H_ + #include "console/simDatablock.h" +#endif +#ifndef _TVECTOR_H_ + #include "core/util/tVector.h" +#endif +#ifndef _CONSOLETYPES_H_ + #include "console/consoleTypes.h" +#endif + +#include "fmod_event.h" + + +class SFXFMODProject; +class SFXFMODEvent; + + +/// +class SFXFMODEventGroup : public SimDataBlock +{ + public: + + typedef SimDataBlock Parent; + friend class SFXFMODProject; + friend class SFXFMODEvent; // mHandle + friend class SFXFMODEventSource; // mHandle + + protected: + + /// + String mName; + + /// + U32 mNumEvents; + + /// + U32 mNumGroups; + + /// + SFXFMODProject* mProject; + + /// + SFXFMODEventGroup* mParent; + + /// + SFXFMODEventGroup* mChildren; + + /// + SFXFMODEventGroup* mSibling; + + /// + SFXFMODEvent* mEvents; + + /// + FMOD_EVENTGROUP* mHandle; + + /// + U32 mLoadCount; + + /// Project ID for client net sync. + S32 mParentId; + + /// Project ID for client net sync. + S32 mProjectId; + + /// + void _load(); + + /// + void _addEvent( SFXFMODEvent* event ); + + /// + void _addGroup( SFXFMODEventGroup* group ); + + /// + void _removeEvent( SFXFMODEvent* event ); + + /// + void _removeGroup( SFXFMODEventGroup* group ); + + public: + + /// + SFXFMODEventGroup(); + + /// + SFXFMODEventGroup( SFXFMODProject* project, const String& name, SFXFMODEventGroup* parent = NULL ); + + /// + SFXFMODEventGroup( SFXFMODProject* project, FMOD_EVENTGROUP* handle, SFXFMODEventGroup* parent = NULL ); + + ~SFXFMODEventGroup(); + + /// Create the event group object on the FMOD device. + void acquire( bool recursive = false ); + + /// Release the event group object on the FMOD device. + void release(); + + /// + const String& getGroupName() const { return mName; } + + /// + String getQualifiedName() const; + + /// + SFXFMODProject* getProject() const { return mProject; } + + /// Return true if the event data for this group has been loaded. + bool isDataLoaded() const; + + /// Load the event data for this group. + /// + /// @note Loading is reference-counted. + bool loadData( bool samples = true, bool streams = true ); + + /// + void freeData( bool force = false ); + + // SimDataBlock. + virtual bool onAdd(); + virtual void onRemove(); + virtual bool preload( bool server, String& errorStr ); + virtual void packData( BitStream* stream ); + virtual void unpackData( BitStream* stream ); + + static void initPersistFields(); + + DECLARE_CONOBJECT( SFXFMODEventGroup ); + DECLARE_CATEGORY( "SFX FMOD" ); + DECLARE_DESCRIPTION( "An event group in an FMOD Designer project." ); +}; + +#endif // !_SFXFMODEVENTGROUP_H_ diff --git a/Engine/source/sfx/fmod/sfxFMODEventSource.cpp b/Engine/source/sfx/fmod/sfxFMODEventSource.cpp new file mode 100644 index 000000000..747bcd56f --- /dev/null +++ b/Engine/source/sfx/fmod/sfxFMODEventSource.cpp @@ -0,0 +1,337 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "sfx/fmod/sfxFMODEventSource.h" +#include "sfx/fmod/sfxFMODEvent.h" +#include "sfx/fmod/sfxFMODEventGroup.h" +#include "sfx/fmod/sfxFMODDevice.h" +#include "sfx/sfxDescription.h" + + +IMPLEMENT_CONOBJECT( SFXFMODEventSource ); + +ConsoleDocClass( SFXFMODEventSource, + "@brief A sound source controller playing an %FMOD Designer event (SFXFMODEvent).\n\n" + + "%FMOD event sources are internally created by the sound system to play events from imported %FMOD Designer projects.\n\n" + + "@note This class cannot be instantiated directly by the user. Instead, instances of SFXFMODEventSource will be " + "implicitly created by the sound system when playing an SFXFMODEvent.\n\n" + + "@ingroup SFXFMOD\n" +); + + +//----------------------------------------------------------------------------- + +SFXFMODEventSource::SFXFMODEventSource() + : mHandle( NULL ) +{ + SFXFMODDevice::instance()->smStatNumEventSources ++; +} + +//----------------------------------------------------------------------------- + +SFXFMODEventSource::SFXFMODEventSource( SFXFMODEvent* event ) + : Parent( event ), + mHandle( NULL ) +{ + SFXFMODDevice::instance()->smStatNumEventSources ++; + + // Make sure the group has its data loaded. + + SFXFMODEventGroup* group = event->getEventGroup(); + if( !group->loadData() ) + return; + + // Create an event instance. + + if( SFXFMODDevice::smFunc->FMOD_EventGroup_GetEvent( + event->getEventGroup()->mHandle, + event->getEventName(), + FMOD_EVENT_DEFAULT, + &mHandle ) != FMOD_OK ) + { + Con::errorf( "SFXFMODEventSource::SFXFMODEventSource - failed to open event '%s'", event->getQualifiedName().c_str() ); + mHandle = NULL; + } +} + +//----------------------------------------------------------------------------- + +SFXFMODEventSource::~SFXFMODEventSource() +{ + SFXFMODDevice::instance()->smStatNumEventSources --; + + if( mHandle ) + SFXFMODDevice::smFunc->FMOD_Event_Release( mHandle, true, true ); + + if( getEvent() ) + getEvent()->getEventGroup()->freeData(); +} + +//----------------------------------------------------------------------------- + +SFXFMODEventSource* SFXFMODEventSource::create( SFXFMODEvent* event ) +{ + AssertFatal( event != NULL, "SFXFMODEventSource::create - got a NULL event!" ); + + // Create the source. + + SFXFMODEventSource* source = new SFXFMODEventSource( event ); + if( source->mHandle ) + source->registerObject(); + else + { + delete source; + source = NULL; + } + + return source; +} + +//----------------------------------------------------------------------------- + +void SFXFMODEventSource::play( F32 fadeInTime ) +{ + if( getStatus() == SFXStatusPlaying ) + return; + + if( isPaused() ) + SFXFMODDevice::smFunc->FMOD_Event_SetPaused( mHandle, false ); + else + { + AssertFatal( getEvent()->getEventGroup()->isDataLoaded(), "SFXFMODEventSource::play() - event data for group not loaded" ); + + if( fadeInTime != -1.f ) + { + U32 fade = U32( fadeInTime * 1000.f ); + SFXFMODDevice::smFunc->FMOD_Event_SetPropertyByIndex( + mHandle, FMOD_EVENTPROPERTY_FADEIN, + &fade, true + ); + } + + FMOD_RESULT result = SFXFMODDevice::smFunc->FMOD_Event_Start( mHandle ); + if( result != FMOD_OK ) + { + Con::errorf( "SFXFMODEventSoure::play() - failed to start event: %s", FMODResultToString( result ).c_str() ); + return; + } + } + + mPlayTimer.start(); + _setStatus( SFXStatusPlaying ); + + _play(); +} + +//----------------------------------------------------------------------------- + +void SFXFMODEventSource::stop( F32 fadeOutTime ) +{ + if( getStatus() == SFXStatusStopped ) + return; + + AssertFatal( mHandle, "SFXFMODEvent::stop() - event not acquired" ); + + bool immediate = ( fadeOutTime == 0.f ); + + FMOD_RESULT result = SFXFMODDevice::smFunc->FMOD_Event_Stop( mHandle, immediate ); + if( result != FMOD_OK ) + Con::errorf( "SFXFMODEventSource::stop() - failed to stop event: %s", FMODResultToString( result ).c_str() ); + + mPlayTimer.stop(); + _setStatus( SFXStatusStopped ); + + // Reset fade-in to default in case it got overwritten + // in play(). + + U32 fade = U32( mFadeInTime * 1000.f ); + SFXFMODDevice::smFunc->FMOD_Event_SetPropertyByIndex( + mHandle, FMOD_EVENTPROPERTY_FADEIN, + &fade, true + ); + + _stop(); +} + +//----------------------------------------------------------------------------- + +void SFXFMODEventSource::pause( F32 fadeOutTime ) +{ + if( getStatus() != SFXStatusPlaying ) + return; + + SFXFMODDevice::smFunc->FMOD_Event_SetPaused( mHandle, true ); + + mPlayTimer.pause(); + _setStatus( SFXStatusPaused ); + + _pause(); +} + +//----------------------------------------------------------------------------- + +void SFXFMODEventSource::setTransform( const MatrixF& transform ) +{ + Parent::setTransform( transform ); + _update3DAttributes(); +} + +//----------------------------------------------------------------------------- + +void SFXFMODEventSource::setVelocity( const VectorF& velocity ) +{ + Parent::setVelocity( velocity ); + _update3DAttributes(); +} + +//----------------------------------------------------------------------------- + +void SFXFMODEventSource::_update3DAttributes() +{ + FMOD_VECTOR position; + FMOD_VECTOR velocity; + FMOD_VECTOR orientation; + + Point3F direction; + getTransform().getColumn( 1, &direction ); + + TorqueVectorToFMODVector( getTransform().getPosition(), position ); + TorqueVectorToFMODVector( getVelocity(), velocity ); + TorqueVectorToFMODVector( direction, orientation ); + + SFXFMODDevice::smFunc->FMOD_Event_Set3DAttributes( mHandle, &position, &velocity, &orientation ); +} + +//----------------------------------------------------------------------------- + +void SFXFMODEventSource::_updateStatus() +{ + if( mStatus == SFXStatusPlaying ) + { + if( !getEvent() ) + _setStatus( SFXStatusStopped ); + else + { + FMOD_EVENT_STATE state; + SFXFMODDevice::smFunc->FMOD_Event_GetState( mHandle, &state ); + + if( !( state & FMOD_EVENT_STATE_PLAYING ) ) + _setStatus( SFXStatusStopped ); + } + } +} + +//----------------------------------------------------------------------------- + +void SFXFMODEventSource::_updateVolume( const MatrixF& listener ) +{ + F32 oldPreAttenuatedVolume = mPreAttenuatedVolume; + Parent::_updateVolume( listener ); + + if( oldPreAttenuatedVolume != mPreAttenuatedVolume ) + SFXFMODDevice::smFunc->FMOD_Event_SetVolume( mHandle, mPreAttenuatedVolume ); +} + +//----------------------------------------------------------------------------- + +void SFXFMODEventSource::_updatePitch() +{ + F32 oldEffectivePitch = mEffectivePitch; + Parent::_updatePitch(); + + if( mEffectivePitch != oldEffectivePitch ) + SFXFMODDevice::smFunc->FMOD_Event_SetPitch( mHandle, mEffectivePitch - 1.0f, FMOD_EVENT_PITCHUNITS_RAW ); +} + +//----------------------------------------------------------------------------- + +void SFXFMODEventSource::_updatePriority() +{ + //TODO + Parent::_updatePriority(); +} + +//----------------------------------------------------------------------------- + +void SFXFMODEventSource::_setMinMaxDistance( F32 min, F32 max ) +{ + Parent::_setMinMaxDistance( min, max ); + _update3DAttributes(); +} + +//----------------------------------------------------------------------------- + +void SFXFMODEventSource::_setFadeTimes( F32 fadeInTime, F32 fadeOutTime ) +{ + Parent::_setFadeTimes( fadeInTime, fadeOutTime ); + + U32 fadeIn = U32( mFadeInTime * 1000.f ); + SFXFMODDevice::smFunc->FMOD_Event_SetPropertyByIndex( + mHandle, FMOD_EVENTPROPERTY_FADEIN, + &fadeIn, true + ); + + U32 fadeOut = U32( mFadeOutTime * 1000.f ); + SFXFMODDevice::smFunc->FMOD_Event_SetPropertyByIndex( + mHandle, FMOD_EVENTPROPERTY_FADEOUT, + &fadeOut, true + ); +} + +//----------------------------------------------------------------------------- + +void SFXFMODEventSource::_setCone( F32 innerAngle, F32 outerAngle, F32 outerVolume ) +{ + Parent::_setCone( innerAngle, outerAngle, outerVolume ); + _update3DAttributes(); +} + +//----------------------------------------------------------------------------- + +void SFXFMODEventSource::_onParameterEvent( SFXParameter* parameter, SFXParameterEvent event ) +{ + Parent::_onParameterEvent( parameter, event ); + + // If it's a value-change on a custom parameter, + // pass it along to FMOD. + + if( getEvent() + && event == SFXParameterEvent_ValueChanged + && parameter->getChannel() == SFXChannelUser0 ) + { + const char* name = parameter->getInternalName(); + + FMOD_EVENTPARAMETER* fmodParameter; + if( SFXFMODDevice::smFunc->FMOD_Event_GetParameter( mHandle, name, &fmodParameter ) != FMOD_OK ) + { + Con::errorf( "SFXFMODEventSource::_onParameterEvent - could not access parameter '%s' of event '%s'", + name, getEvent()->getQualifiedName().c_str() ); + return; + } + + SFXFMODDevice::smFunc->FMOD_EventParameter_SetValue( fmodParameter, parameter->getValue() ); + } +} diff --git a/Engine/source/sfx/fmod/sfxFMODEventSource.h b/Engine/source/sfx/fmod/sfxFMODEventSource.h new file mode 100644 index 000000000..0e3493592 --- /dev/null +++ b/Engine/source/sfx/fmod/sfxFMODEventSource.h @@ -0,0 +1,101 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXFMODEVENTSOURCE_H_ +#define _SFXFMODEVENTSOURCE_H_ + +#ifndef _SFXSOURCE_H_ + #include "sfx/sfxSource.h" +#endif + +#include "fmod_event.h" + + +class SFXFMODEvent; + + +/// An SFXSource that controls the playback of an SFXFMODEvent. +/// +/// SFXFMODEvents can be played back directly through their console methods. +/// However, this class integrates them with the remaining SFX system and makes +/// events usable wherever SFX tracks are usable though with the important +/// distinction that there can only ever be a single source for a given event. +/// +/// Note that calling playback methods directly on an event will cause a source +/// for the event to be created if there is not already one. +/// +/// Be aware that using fade-outs in events in combination with play-once sources +/// does not work well at the moment. +/// +class SFXFMODEventSource : public SFXSource +{ + public: + + typedef SFXSource Parent; + + protected: + + /// The event instance handle for this source. + FMOD_EVENT* mHandle; + + /// + SFXFMODEventSource( SFXFMODEvent* event ); + + /// Update 3D position, velocity, and orientation from current source transform. + void _update3DAttributes(); + + // SFXSource. + virtual void _updateStatus(); + virtual void _updateVolume( const MatrixF& listener ); + virtual void _updatePitch(); + virtual void _updatePriority(); + virtual void _onParameterEvent( SFXParameter* parameter, SFXParameterEvent event ); + virtual void _setMinMaxDistance( F32 min, F32 max ); + virtual void _setCone( F32 innerAngle, F32 outerAngle, F32 outerVolume ); + virtual void _setFadeTimes( F32 fadeInTime, F32 fadeOutTime ); + + public: + + /// + SFXFMODEventSource(); + + virtual ~SFXFMODEventSource(); + + /// Return the FMOD event object that is being played back by this source. + SFXFMODEvent* getEvent() const { return ( SFXFMODEvent* ) mTrack.getPointer(); } + + /// Create a new source for the given event. + static SFXFMODEventSource* create( SFXFMODEvent* event ); + + // SFXSource. + virtual void play( F32 fadeInTime = -1.f ); // fadeInTime ignored when resuming from paused + virtual void stop( F32 fadeOutTime = -1.f ); // fadeOutTime!=0 ignored + virtual void pause( F32 fadeOutTime = -1.f ); // fadeOutTime currently ignored + virtual void setTransform( const MatrixF& transform ); + virtual void setVelocity( const VectorF& velocity ); + + DECLARE_CONOBJECT( SFXFMODEventSource ); + DECLARE_CATEGORY( "SFX FMOD" ); + DECLARE_DESCRIPTION( "An SFX source controlling the playback of an FMOD Designer event." ); +}; + +#endif // !_SFXFMODEVENTSOURCE_H_ diff --git a/Engine/source/sfx/fmod/sfxFMODPlugin.cpp b/Engine/source/sfx/fmod/sfxFMODPlugin.cpp new file mode 100644 index 000000000..9db0af7ff --- /dev/null +++ b/Engine/source/sfx/fmod/sfxFMODPlugin.cpp @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/fmod/sfxFMODPlugin.h" +#include "sfx/fmod/sfxFMODEvent.h" +#include "sfx/fmod/sfxFMODEventSource.h" + + +//----------------------------------------------------------------------------- + +SFXSource* SFXFMODPlugin::createSource( SFXTrack* track ) +{ + SFXFMODEvent* event = dynamic_cast< SFXFMODEvent* >( track ); + if( !event ) + return NULL; + + return SFXFMODEventSource::create( event ); +} diff --git a/Engine/source/sfx/fmod/sfxFMODPlugin.h b/Engine/source/sfx/fmod/sfxFMODPlugin.h new file mode 100644 index 000000000..488cde742 --- /dev/null +++ b/Engine/source/sfx/fmod/sfxFMODPlugin.h @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXFMODPLUGIN_H_ +#define _SFXFMODPLUGIN_H_ + +#ifndef _SFXSYSTEM_H_ + #include "sfx/sfxSystem.h" +#endif + + +/// SFXSystem plugin that adds the capability to create SFXSources for +/// Designer SFXFMODEvents. +/// +/// The plugin will only be installed if an FMOD device has been created. +/// While SFXFMODEvents may be constructed without an FMOD device, trying +/// to play such an event then will result in an error. +/// +class SFXFMODPlugin : public SFXSystemPlugin +{ + public: + + typedef SFXSystemPlugin Parent; + + /// + virtual SFXSource* createSource( SFXTrack* track ); +}; + +#endif // !_SFXFMODPLUGIN_H_ diff --git a/Engine/source/sfx/fmod/sfxFMODProject.cpp b/Engine/source/sfx/fmod/sfxFMODProject.cpp new file mode 100644 index 000000000..1f270492c --- /dev/null +++ b/Engine/source/sfx/fmod/sfxFMODProject.cpp @@ -0,0 +1,493 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/fmod/sfxFMODProject.h" +#include "sfx/fmod/sfxFMODDevice.h" +#include "sfx/fmod/sfxFMODEvent.h" +#include "sfx/fmod/sfxFMODEventGroup.h" +#include "sfx/sfxDescription.h" +#include "core/stringTable.h" +#include "core/volume.h" +#include "core/util/path.h" +#include "core/stream/fileStream.h" +#include "core/stream/bitStream.h" +#include "core/util/safeDelete.h" + + +IMPLEMENT_CO_DATABLOCK_V1( SFXFMODProject ); + + +ConsoleDocClass( SFXFMODProject, + "@brief An FMOD Designer project loaded into Torque.\n\n" + + "@section SFXFMODProject_resources Resource Loading\n\n" + + "@ingroup SFXFMOD\n" + "@ingroup Datablocks" +); + + +//----------------------------------------------------------------------------- + +SFXFMODProject::SFXFMODProject() + : mHandle( NULL ), + mRootGroups( NULL ) +{ + VECTOR_SET_ASSOCIATION( mGroups ); + VECTOR_SET_ASSOCIATION( mEvents ); + + SFX->getEventSignal().notify( this, &SFXFMODProject::_onSystemEvent ); +} + +//----------------------------------------------------------------------------- + +SFXFMODProject::~SFXFMODProject() +{ + AssertFatal( mGroups.empty(), "SFXFMODProject::~SFXFMODProject - project still has groups attached" ); + AssertFatal( mEvents.empty(), "SFXFMODProject::~SFXFMODProject - project still has events attached" ); + + if( SFX ) + SFX->getEventSignal().remove( this, &SFXFMODProject::_onSystemEvent ); +} + +//----------------------------------------------------------------------------- + +void SFXFMODProject::initPersistFields() +{ + addGroup( "FMOD" ); + + addField( "fileName", TypeStringFilename, Offset( mFileName, SFXFMODProject ), "The compiled .fev file from FMOD Designer." ); + addField( "mediaPath", TypeStringFilename, Offset( mMediaPath, SFXFMODProject ), "Path to the media files; if unset, defaults to project directory." ); + + endGroup( "FMOD" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +bool SFXFMODProject::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + // If this is a non-networked datablock, load the + // project data now. + + if( isClientOnly() && !_load() ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- + +void SFXFMODProject::onRemove() +{ + Parent::onRemove(); + + _clear(); +} + +//----------------------------------------------------------------------------- + +bool SFXFMODProject::preload( bool server, String& errorStr ) +{ + if( !Parent::preload( server, errorStr ) ) + return false; + + if( server ) + { + if( mFileName.isEmpty() ) + { + errorStr = String::ToString( "SFXFMODProject::preload - no filename set on %i (%s)", + getId(), getName() ); + return false; + } + + if( mGroups.empty() || mEvents.empty() ) + _load(); + + release(); + } + + return true; +} + +//----------------------------------------------------------------------------- + +void SFXFMODProject::packData( BitStream* stream ) +{ + Parent::packData( stream ); + + stream->write( mFileName ); + stream->write( mMediaPath ); +} + +//----------------------------------------------------------------------------- + +void SFXFMODProject::unpackData( BitStream* stream ) +{ + Parent::unpackData( stream ); + + stream->read( &mFileName ); + stream->read( &mMediaPath ); +} + +//----------------------------------------------------------------------------- + +void SFXFMODProject::_onSystemEvent( SFXSystemEventType event ) +{ + switch( event ) + { + case SFXSystemEvent_DestroyDevice: + + // If the FMOD device is being destroyed, + // release all our data. + + if( SFXFMODDevice::instance() ) + release(); + + break; + + default: + break; + } +} + +//----------------------------------------------------------------------------- + +void SFXFMODProject::_clear() +{ + release(); + + for( U32 i = 0; i < mGroups.size(); ++ i ) + if( !mGroups[ i ]->isRemoved() ) + mGroups[ i ]->deleteObject(); + + mGroups.clear(); + mEvents.clear(); + + mRootGroups = NULL; +} + +//----------------------------------------------------------------------------- + +bool SFXFMODProject::_load() +{ + const Torque::Path eventScriptFileName = mFileName + ".cs"; + const Torque::Path eventScriptFileNameDSO = eventScriptFileName + ".dso"; + const bool eventScriptFileExists = Torque::FS::IsFile( eventScriptFileName ); + const bool eventScriptFileDSOExists = Torque::FS::IsFile( eventScriptFileNameDSO ); + + // Check if we need to (re-)generate the event script file. + + bool needToGenerateEventScriptFile = false; + if( ( !eventScriptFileExists && !eventScriptFileDSOExists ) + || ( Torque::FS::CompareModifiedTimes( mFileName, eventScriptFileName ) > 0 + || Torque::FS::CompareModifiedTimes( mFileName, eventScriptFileNameDSO ) > 0 ) ) + needToGenerateEventScriptFile = true; + + // If we need to generate, check if we can. + + SFXFMODDevice* fmodDevice = SFXFMODDevice::instance(); + if( needToGenerateEventScriptFile && !fmodDevice ) + { + // If we have neither FMOD nor the event scripts (even if outdated), + // there's nothing we can do. + + if( !eventScriptFileExists && !eventScriptFileDSOExists ) + { + Con::errorf( "SFXFMODProject::_load() - event script for '%s' does not exist and device is not FMOD; load this project under FMOD first", + mFileName.c_str() ); + return false; + } + + // Use the oudated versions. + + Con::warnf( "SFXMODProject::_load() - event script for '%s' is outdated and device is not FMOD; event data may not match .fev contents", + mFileName.c_str() ); + needToGenerateEventScriptFile = false; + } + + // If we don't need to regenerate, try executing the event script now. + + if( !needToGenerateEventScriptFile ) + { + if( ( eventScriptFileExists || eventScriptFileDSOExists ) + && !Con::evaluatef( "exec( \"%s\" );", eventScriptFileName.getFullPath().c_str() ) ) + { + Con::errorf( "SFXFMODProject::_load() - failed to execute event script for '%s'%s", + mFileName.c_str(), + fmodDevice != NULL ? "; trying to regenerate" : "" + ); + + if( !fmodDevice ) + return false; + + needToGenerateEventScriptFile = true; + } + else + Con::printf( "SFXFMODProject - %s: Loaded event script", getName() ); + } + + // If we need to generate the event script file, + // load the FMOD project now and then emit the file. + + if( needToGenerateEventScriptFile ) + { + // Try to load the project. + + acquire(); + + if( !mHandle ) + return false; + + // Get the project info. + + FMOD_EVENT_PROJECTINFO info; + + int numEvents; + int numGroups; + + SFXFMODDevice::smFunc->FMOD_EventProject_GetInfo( mHandle, &info ); + SFXFMODDevice::smFunc->FMOD_EventProject_GetNumEvents( mHandle, &numEvents ); + SFXFMODDevice::smFunc->FMOD_EventProject_GetNumGroups( mHandle, &numGroups ); + + Con::printf( "SFXFMODProject - %s: Loading '%s' from '%s' (index: %i, events: %i, groups: %i)", + getName(), info.name, mFileName.c_str(), info.index, numEvents, numGroups ); + + // Load the root groups. + + for( U32 i = 0; i < numGroups; ++ i ) + { + FMOD_EVENTGROUP* group; + if( SFXFMODDevice::smFunc->FMOD_EventProject_GetGroupByIndex( mHandle, i, true, &group ) == FMOD_OK ) + { + SFXFMODEventGroup* object = new SFXFMODEventGroup( this, group ); + + object->mSibling = mRootGroups; + mRootGroups = object; + + String qualifiedName = FMODEventPathToTorqueName( object->getQualifiedName() ); + + if( !isClientOnly() ) + object->assignId(); + + object->registerObject( String::ToString( "%s_%s", getName(), qualifiedName.c_str() ) ); + if( isClientOnly() ) + Sim::getRootGroup()->addObject( object ); + + object->_load(); + } + } + + // Create the event script file. + + FileStream stream; + if( !stream.open( eventScriptFileName.getFullPath(), Torque::FS::File::Write ) ) + { + Con::errorf( "SFXFMODProject::_load - could not create event script file for '%s'", mFileName.c_str() ); + return true; // Don't treat as failure. + } + + // Write a header. + + stream.writeText( String::ToString( "// This file has been auto-generated from '%s'\n", mFileName.c_str() ) ); + stream.writeText( "// Do not edit this file manually and do not move it away from the Designer file.\n\n" ); + + // Write the group objects. + + for( U32 i = 0; i < mGroups.size(); ++ i ) + { + mGroups[ i ]->write( stream, 0 ); + stream.writeText( "\n" ); + } + + // Write the event objects along with their + // SFXDescriptions. + + for( U32 i = 0; i < mEvents.size(); ++ i ) + { + mEvents[ i ]->getDescription()->write( stream, 0 ); + mEvents[ i ]->write( stream, 0 ); + stream.writeText( "\n" ); + } + + Con::printf( "SFXFMODProject - %s: Generated event script '%s'", getName(), eventScriptFileName.getFullPath().c_str() ); + } + + return true; +} + +//----------------------------------------------------------------------------- + +void SFXFMODProject::acquire( bool recursive ) +{ + // Load the project file. + + if( !mHandle ) + { + FMOD_RESULT result = SFXFMODDevice::smFunc->FMOD_EventSystem_Load( + SFXFMODDevice::smEventSystem, + mFileName.c_str(), + ( FMOD_EVENT_LOADINFO* ) 0, + &mHandle + ); + + if( result != FMOD_OK ) + { + Con::errorf( "SFXFMODProject::acquire - could not load '%s' (%s)", + mFileName.c_str(), FMODResultToString( result ).c_str() ); + mHandle = NULL; + return; + } + + Con::printf( "SFXFMODProject - %s: Opened project '%s'", getName(), mFileName.c_str() ); + + // Set the media path. + + String mediaPath; + if( !mMediaPath.isEmpty() ) + { + mediaPath = mMediaPath; + if( mediaPath[ mediaPath.length() - 1 ] != '/' ) + mediaPath += '/'; + } + else + { + // Set to project directory. + + Torque::Path path = mFileName; + if( path.getRoot().isEmpty() ) + path.setRoot( "game" ); + path.setFileName( "" ); + path.setExtension( "" ); + + mediaPath = path.getFullPath() + '/'; + } + + SFXFMODDevice::smFunc->FMOD_EventSystem_SetMediaPath( + SFXFMODDevice::smEventSystem, + mediaPath.c_str() + ); + } + + // Acquire the root groups. + + if( recursive ) + for( SFXFMODEventGroup* group = mRootGroups; group != NULL; group = group->mSibling ) + group->acquire( true ); + + SFXFMODDevice::instance()->updateMemUsageStats(); +} + +//----------------------------------------------------------------------------- + +void SFXFMODProject::release() +{ + if( !mHandle ) + return; + + Con::printf( "SFXFMODProject - %s: Closing project '%s'", + getName(), mFileName.c_str() ); + + // Clear media path. + + SFXFMODDevice::smFunc->FMOD_EventSystem_SetMediaPath( + SFXFMODDevice::smEventSystem, "" ); + + // Release the root groups. + + for( SFXFMODEventGroup* group = mRootGroups; group != NULL; group = group->mSibling ) + group->release(); + + // Release the project. + + SFXFMODDevice::smFunc->FMOD_EventProject_Release( mHandle ); + mHandle = NULL; + + SFXFMODDevice::instance()->updateMemUsageStats(); +} + +//----------------------------------------------------------------------------- + +void SFXFMODProject::_addEvent( SFXFMODEvent* event ) +{ + mEvents.push_back( event ); +} + +//----------------------------------------------------------------------------- + +void SFXFMODProject::_addGroup( SFXFMODEventGroup* group ) +{ + mGroups.push_back( group ); +} + +//----------------------------------------------------------------------------- + +void SFXFMODProject::_removeEvent( SFXFMODEvent* event ) +{ + for( U32 i = 0; i < mEvents.size(); ++ i ) + if( mEvents[ i ] == event ) + { + mEvents.erase( i ); + break; + } +} + +//----------------------------------------------------------------------------- + +void SFXFMODProject::_removeGroup( SFXFMODEventGroup* group ) +{ + // Remove from group array. + + for( U32 i = 0; i < mGroups.size(); ++ i ) + if( mGroups[ i ] == group ) + { + mGroups.erase( i ); + break;; + } + + // Unlink if it's a root group. + + if( !group->mParent ) + { + if( group == mRootGroups ) + { + mRootGroups = group->mSibling; + group->mSibling = NULL; + } + else + { + SFXFMODEventGroup* p = mRootGroups; + while( p && p->mSibling != group ) + p = p->mSibling; + + if( p ) + { + p->mSibling = group->mSibling; + group->mSibling = NULL; + } + } + } +} diff --git a/Engine/source/sfx/fmod/sfxFMODProject.h b/Engine/source/sfx/fmod/sfxFMODProject.h new file mode 100644 index 000000000..0160e321b --- /dev/null +++ b/Engine/source/sfx/fmod/sfxFMODProject.h @@ -0,0 +1,162 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXFMODPROJECT_H_ +#define _SFXFMODPROJECT_H_ + +#ifndef _SIMDATABLOCK_H_ + #include "console/simDatablock.h" +#endif +#ifndef _CONSOLETYPES_H_ + #include "console/consoleTypes.h" +#endif +#ifndef _TVECTOR_H_ + #include "core/util/tVector.h" +#endif +#ifndef _SFXSYSTEM_H_ + #include "sfx/sfxSystem.h" +#endif + +#include "fmod_event.h" + + +class SFXFMODEvent; +class SFXFMODEventGroup; +class SimGroup; + + + +/// Datablock that loads an FMOD Designer project. +/// +/// All events in the project are automatically made available as SFXFMODEvent track +/// datablock instances. Each event object is automatically named by substituting +/// the slashes in its fully qualified name with underscores and preprending the project +/// name to this; event 'group1/group2/event' in the SFXFMODProject instance called +/// 'project', for example, will be available as a TorqueScript object called +/// 'project_group1_group2_event'. +/// +/// This class also works in a client-server environment where the server is +/// not running FMOD. The event objects are cached in an auto-generated TorqueScript +/// file alongside the .fev project file (x/y.fev -> x/y.fev.cs) which, when available +/// and up-to-date, does not require FMOD for the server-side objects to correctly +/// initialize. +/// +/// To establish good loading behavior and for good memory management, it is necessary to +/// wisely distribute events to groups and to manually pre-load groups. The best solution +/// probably is to have one group of common events that is loaded during game startup and +/// then have one event group for each level in the game that is only loaded for the +/// duration of its particular level. +/// +/// SFXFMODProject will propagate it's networking model to all its contents. This means +/// that if the project is a non-networked datablock, then all event groups, events, and +/// descriptions contained in the project will also be non-networked datablocks. +/// +/// It usually makes the most sense to use non-networked ("client-only") datablocks as +/// otherwise the FMOD datablocks will be purged on each mission load. +/// +/// @note Only one project's music data can ever be loaded at any one time. +/// Usually you wouldn't want more than a single SFXFMODProject instance in your game +/// data. Also, only a single media path can be set through the designer API so when +/// loading multiple projects, note that each project will set the media path to its +/// own directory. For data loading to work, all project thus need to be placed in +/// the same directory. +/// +class SFXFMODProject : public SimDataBlock +{ + public: + + typedef SimDataBlock Parent; + friend class SFXFMODEventGroup; // _addGroup + friend class SFXFMODEvent; // _addEvent + + protected: + + /// + String mFileName; + + /// + String mMediaPath; + + /// + SFXFMODEventGroup* mRootGroups; + + /// A flat list of all the groups in this projet. + Vector< SFXFMODEventGroup* > mGroups; + + /// A flat list of all the events in the project. + Vector< SFXFMODEvent* > mEvents; + + /// + FMOD_EVENTPROJECT* mHandle; + + /// + void _onSystemEvent( SFXSystemEventType event ); + + /// + void _clear(); + + /// + bool _load(); + + /// + void _addEvent( SFXFMODEvent* event ); + + /// + void _addGroup( SFXFMODEventGroup* group ); + + /// + void _removeEvent( SFXFMODEvent* event ); + + /// + void _removeGroup( SFXFMODEventGroup* group ); + + public: + + /// + SFXFMODProject(); + + virtual ~SFXFMODProject(); + + /// + void acquire( bool recursive = false ); + + /// + void release(); + + /// + const String& getFileName() const { return mFileName; } + + // SimDataBlock. + virtual bool onAdd(); + virtual void onRemove(); + virtual bool preload( bool server, String& errorStr ); + virtual void packData( BitStream* stream ); + virtual void unpackData( BitStream* stream ); + + static void initPersistFields(); + + DECLARE_CONOBJECT( SFXFMODProject ); + DECLARE_CATEGORY( "SFX FMOD" ); + DECLARE_DESCRIPTION( "An FMOD Designer project." ); +}; + +#endif // !_SFXFMODPROJECT_H_ diff --git a/Engine/source/sfx/fmod/sfxFMODProvider.cpp b/Engine/source/sfx/fmod/sfxFMODProvider.cpp new file mode 100644 index 000000000..b503616ab --- /dev/null +++ b/Engine/source/sfx/fmod/sfxFMODProvider.cpp @@ -0,0 +1,403 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/sfxProvider.h" +#include "sfx/fmod/sfxFMODDevice.h" +#include "core/util/safeRelease.h" +#include "console/console.h" +#include "core/util/safeDelete.h" +#include "core/module.h" +#include "console/consoleTypes.h" + + +class SFXFMODProvider : public SFXProvider +{ +public: + + SFXFMODProvider() + : SFXProvider( "FMOD" ) + { + Con::addVariable( "$SFX::Device::fmodNumEventSources", TypeS32, &SFXFMODDevice::smStatNumEventSources, + "The current number of SFXFMODEventSource instances in the system.\n" + "This tells the number of sounds in the system that are currently playing FMOD Designer events.\n\n" + "@note Only relevant if an %FMOD sound device is used.\n\n" + "@ingroup SFXFMOD" ); + Con::addVariable( "$SFX::Device::fmodCoreMem", TypeS32, &SFXFMODDevice::smStatMemUsageCore, + "Current number of bytes allocated by the core %FMOD sound system.\n\n" + "@note Only relevant if an %FMOD sound device is used.\n\n" + "@ingroup SFXFMOD" ); + Con::addVariable( "$SFX::Device::fmodEventMem", TypeS32, &SFXFMODDevice::smStatMemUsageEvents, + "Current number of bytes allocated by the %FMOD Designer event system.\n\n" + "@note Only relevant if an %FMOD sound device is used and the FMOD event DLL is loaded.\n\n" + "@ingroup SFXFMOD" ); + + Con::addVariable( "$pref::SFX::FMOD::disableSoftware", TypeBool, &SFXFMODDevice::smPrefDisableSoftware, + "Whether to disable the %FMOD software mixer to conserve memory.\n" + "All sounds not created with SFXDescription::useHardware or using DSP effects will fail to load.\n\n" + "@note Only applies when using an %FMOD sound device.\n\n" + "@ingroup SFXFMOD" ); + Con::addVariable( "$pref::SFX::FMOD::useSoftwareHRTF", TypeBool, &SFXFMODDevice::smPrefUseSoftwareHRTF, + "Whether to enable HRTF in %FMOD's software mixer.\n" + "This will add a lowpass filter effect to the DSP effect chain of all sounds mixed in software.\n\n" + "@note Only applies when using an %FMOD sound device.\n\n" + "@ingroup SFXFMOD" ); + Con::addVariable( "$pref::SFX::FMOD::useSoftwareReverbLowmem", TypeBool, &SFXFMODDevice::smPrefUseSoftwareReverbLowmem, + "If true, %FMOD's SFX reverb is run using 22/24kHz delay buffers, halving the memory required.\n\n" + "@note Only applies when using an %FMOD sound device.\n\n" + "@ingroup SFXFMOD" ); + Con::addVariable( "$pref::SFX::FMOD::enableProfile", TypeBool, &SFXFMODDevice::smPrefEnableProfile, + "Whether to enable support for %FMOD's profiler.\n\n" + "@note Only applies when using an %FMOD sound device.\n\n" + "@ref FMOD_profiler\n\n" + "@ingroup SFXFMOD" ); + Con::addVariable( "$pref::SFX::FMOD::DSoundHRTF", TypeString, &SFXFMODDevice::smPrefDSoundHRTF, + "The type of HRTF to use for hardware-mixed 3D sounds when %FMOD is using DirectSound for sound output " + "and hardware-acceleration is not available.\n\n" + "Options are\n" + "- \"none\": simple stereo panning/doppler/attenuation\n" + "- \"light\": slightly higher quality than \"none\"\n" + "- \"full\": full quality 3D playback\n\n" + "@note Only applies when using an %FMOD sound device.\n\n" + "@ingroup SFXFMOD" ); + Con::addVariable( "$pref::SFX::FMOD::pluginPath", TypeString, &SFXFMODDevice::smPrefPluginPath, + "%Path to additional %FMOD plugins.\n\n" + "@note Only applies when using an %FMOD sound device.\n\n" + "@ingroup SFXFMOD" ); + } + virtual ~SFXFMODProvider(); + +protected: + FModFNTable mFMod; + + struct FModDeviceInfo : SFXDeviceInfo + { + FMOD_CAPS mCaps; + FMOD_SPEAKERMODE mSpeakerMode; + }; + + void init(); + + bool _createSystem(); + +public: + + SFXDevice* createDevice( const String& deviceName, bool useHardware, S32 maxBuffers ); + +}; + +MODULE_BEGIN( FMOD ) + + MODULE_INIT_BEFORE( SFX ) + MODULE_SHUTDOWN_AFTER( SFX ) + + SFXFMODProvider* mProvider; + + MODULE_INIT + { + mProvider = new SFXFMODProvider; + } + + MODULE_SHUTDOWN + { + delete mProvider; + } + +MODULE_END; + + +//------------------------------------------------------------------------------ +// Helper + +bool fmodBindFunction( DLibrary *dll, void *&fnAddress, const char* name ) +{ + if( !dll ) + return false; + + fnAddress = dll->bind( name ); + + if (!fnAddress) + Con::warnf( "FMOD Loader: DLL bind failed for %s", name ); + + return fnAddress != 0; +} + +//------------------------------------------------------------------------------ + +void SFXFMODProvider::init() +{ +#ifdef TORQUE_FMOD_STATIC + + // FMOD statically linked. + + mFMod.isLoaded = true; + #define FMOD_FUNCTION(fn_name, fn_args) \ + (*(void**)&mFMod.fn_name.fn) = &fn_name; + + #ifndef TORQUE_FMOD_NO_EVENTS + mFMod.eventIsLoaded = true; + #define FMOD_EVENT_FUNCTION(fn_name, fn_args) \ + (*(void**)&mFMod.fn_name.fn) = &fn_name; + #else + #define FMOD_EVENT_FUNCTION( fn_name, fn_args ) + #endif + + #include FMOD_FN_FILE + + #undef FMOD_FUNCTION + #undef FMOD_EVENT_FUNCTION + +#else + + // FMOD dynamically linked. + + const char* dllName; + const char* pDllName; // plugin-based DLL + const char* eventDllName; + +#ifdef TORQUE_OS_WIN32 + dllName = "fmodex.dll"; + pDllName = "fmodexp.dll"; + eventDllName = "fmod_event.dll"; +#elif defined( TORQUE_OS_MAC ) + dllName = "libfmodex.dylib"; + pDllName = "libfmodexp.dylib"; + eventDllName = "libfmodevent.dylib"; +#else +# warning Need to set FMOD DLL filename for platform. + return; +#endif + + // Grab the functions we'll want from the fmod DLL. + mFMod.dllRef = OsLoadLibrary( dllName ); + + // Try the plugin-based version. + if( !mFMod.dllRef ) + mFMod.dllRef = OsLoadLibrary( pDllName ); + + if(!mFMod.dllRef) + { + Con::warnf( "SFXFMODProvider - Could not locate '%s' or '%s' - FMOD not available.", dllName, pDllName ); + return; + } + + mFMod.eventDllRef = OsLoadLibrary( eventDllName ); + if(!mFMod.eventDllRef) + Con::warnf( "SFXFMODProvider - Could not locate %s - FMOD Designer intergration not available.", eventDllName ); + + mFMod.isLoaded = true; + mFMod.eventIsLoaded = true; + + #define FMOD_FUNCTION(fn_name, fn_args) \ + mFMod.isLoaded &= fmodBindFunction(mFMod.dllRef, *(void**)&mFMod.fn_name.fn, #fn_name); + #ifdef TORQUE_OS_WIN32 + #define FMOD_EVENT_FUNCTION(fn_name, fn_args, export) \ + mFMod.eventIsLoaded &= fmodBindFunction(mFMod.eventDllRef, *(void**)&mFMod.fn_name.fn, export); + #else + #define FMOD_EVENT_FUNCTION(fn_name, fn_args, export) \ + mFMod.eventIsLoaded &= fmodBindFunction(mFMod.eventDllRef, *(void**)&mFMod.fn_name.fn, #fn_name); + #endif + + #include FMOD_FN_FILE + + #undef FMOD_FUNCTION + #undef FMOD_EVENT_FUNCTION + + if(mFMod.isLoaded == false) + { + Con::warnf("SFXFMODProvider - Could not locate %s - FMOD not available.", dllName); + return; + } + if( !mFMod.eventIsLoaded && mFMod.eventDllRef ) + Con::warnf("SFXFMODProvider - Could not load the %s - FMOD Designer integration not available.", eventDllName); + +#endif + + FMOD_RESULT res; + + // Create the FMOD system object. + + if( !_createSystem() ) + return; + + // Check that the Ex API version is OK. + + unsigned int version; + res = mFMod.FMOD_System_GetVersion(SFXFMODDevice::smSystem, &version); + FModAssert(res, "SFXFMODProvider - Failed to get FMOD version!"); + + Con::printf( "SFXFMODProvider - FMOD Ex API version: %x.%x.%x", + ( version & 0xffff0000 ) >> 16, + ( version & 0x0000ff00 ) >> 8, + ( version & 0x000000ff ) + ); + + if(version < FMOD_VERSION) + { + Con::warnf("SFXFMODProvider - FMOD Ex API version in DLL is too old - FMOD not available."); + return; + } + + // Check that the Designer API version is ok. + + if( mFMod.eventIsLoaded ) + { + res = mFMod.FMOD_EventSystem_GetVersion( SFXFMODDevice::smEventSystem, &version ); + FModAssert(res, "SFXFMODProvider - Failed to get FMOD version!"); + + Con::printf( "SFXFMODProvider - FMOD Designer API version: %x.%x.%x", + ( version & 0xffff0000 ) >> 16, + ( version & 0x0000ff00 ) >> 8, + ( version & 0x000000ff ) + ); + + if( version < FMOD_EVENT_VERSION ) + { + Con::errorf( "SFXFMODProvider - FMOD Designer API version in DLL is too old!" ); + return; + } + } + + // Now, enumerate our devices. + int numDrivers; + res = mFMod.FMOD_System_GetNumDrivers(SFXFMODDevice::smSystem, &numDrivers); + FModAssert(res, "SFXFMODProvider - Failed to get driver count - FMOD not available."); + + char nameBuff[256]; + + for(S32 i=0; iname = String( nameBuff ); + fmodInfo->hasHardware = caps & FMOD_CAPS_HARDWARE; + fmodInfo->maxBuffers = 32; + fmodInfo->driver = String(); + fmodInfo->mCaps = caps; + fmodInfo->mSpeakerMode = speakerMode; + + mDeviceInfo.push_back(fmodInfo); + } + + // Did we get any devices? + if ( mDeviceInfo.empty() ) + { + Con::warnf( "SFXFMODProvider - No valid devices found - FMOD not available." ); + return; + } + + // TODO: FMOD_Memory_Initialize +#ifdef TORQUE_OS_XENON + const dsize_t memSz = 5 * 1024 * 1024; + void *memBuffer = XPhysicalAlloc( memSz, MAXULONG_PTR, 0, PAGE_READWRITE ); + mFMod.FMOD_Memory_Initialize( memBuffer, memSz, FMOD_MEMORY_ALLOCCALLBACK(NULL), FMOD_MEMORY_REALLOCCALLBACK(NULL), FMOD_MEMORY_FREECALLBACK(NULL) ); +#endif + + // Wow, we made it - register the provider. + regProvider( this ); +} + +SFXFMODProvider::~SFXFMODProvider() +{ + if( SFXFMODDevice::smEventSystem ) + { + mFMod.FMOD_EventSystem_Release( SFXFMODDevice::smEventSystem ); + SFXFMODDevice::smEventSystem = NULL; + SFXFMODDevice::smSystem = NULL; + } + else if( SFXFMODDevice::smSystem ) + { + mFMod.FMOD_System_Release( SFXFMODDevice::smSystem ); + SFXFMODDevice::smSystem = NULL; + } +} + +SFXDevice* SFXFMODProvider::createDevice( const String& deviceName, bool useHardware, S32 maxBuffers ) +{ + FModDeviceInfo* info = dynamic_cast< FModDeviceInfo* > + ( _findDeviceInfo( deviceName ) ); + + if( !info ) + return NULL; + + if( !SFXFMODDevice::smSystem && !_createSystem() ) + return false; + + SFXFMODDevice* device = new SFXFMODDevice(this, &mFMod, 0, info->name ); + if( !device->_init() ) + SAFE_DELETE( device ); + + return device; +} + +bool SFXFMODProvider::_createSystem() +{ + AssertFatal( !SFXFMODDevice::smEventSystem, "SFXFMODProvider::_createSystem() - event system already created!" ); + AssertFatal( !SFXFMODDevice::smSystem, "SFXFMODProvider::_createSystem() - system already created!" ); + + if( mFMod.eventIsLoaded ) + { + FMOD_RESULT res = mFMod.FMOD_EventSystem_Create( &SFXFMODDevice::smEventSystem ); + if( res != FMOD_OK ) + { + Con::errorf( "SFXFMODProvider - could not create the FMOD event system." ); + return false; + } + + res = mFMod.FMOD_EventSystem_GetSystemObject( SFXFMODDevice::smEventSystem, &SFXFMODDevice::smSystem ); + if( res != FMOD_OK ) + { + Con::errorf( "SFXFMODProvider - could not retrieve the FMOD system object." ); + return false; + } + } + else + { + // Allocate the FMod system. + + FMOD_RESULT res = mFMod.FMOD_System_Create( &SFXFMODDevice::smSystem ); + if( res != FMOD_OK ) + { + // Failed - deal with it! + Con::errorf("SFXFMODProvider - could not create the FMOD system."); + return false; + } + } + + return true; +} diff --git a/Engine/source/sfx/fmod/sfxFMODVoice.cpp b/Engine/source/sfx/fmod/sfxFMODVoice.cpp new file mode 100644 index 000000000..05ace6e2b --- /dev/null +++ b/Engine/source/sfx/fmod/sfxFMODVoice.cpp @@ -0,0 +1,303 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "sfx/fmod/sfxFMODVoice.h" + +#include "sfx/fmod/sfxFMODBuffer.h" +#include "sfx/fmod/sfxFMODDevice.h" +#include "core/tAlgorithm.h" + + +SFXFMODVoice* SFXFMODVoice::create( SFXFMODDevice *device, + SFXFMODBuffer *buffer ) +{ + AssertFatal( device, "SFXFMODVoice::create() - Got null device!" ); + AssertFatal( buffer, "SFXFMODVoice::create() - Got null buffer!" ); + + return new SFXFMODVoice( device, buffer ); +} + +SFXFMODVoice::SFXFMODVoice( SFXFMODDevice *device, + SFXFMODBuffer *buffer ) + : Parent( buffer ), + mDevice( device ), + mChannel( NULL ) +{ + AssertFatal( device, "SFXFMODVoice::SFXFMODVoice() - No device assigned!" ); + AssertFatal( buffer, "SFXFMODVoice::SFXFMODVoice() - No buffer assigned!" ); + AssertFatal( _getBuffer()->mSound != NULL, "SFXFMODVoice::SFXFMODVoice() - No sound assigned!" ); +} + +SFXFMODVoice::~SFXFMODVoice() +{ + _stop(); +} + +SFXStatus SFXFMODVoice::_status() const +{ + if( mChannel ) + { + FMOD_BOOL isTrue = false; + SFXFMODDevice::smFunc->FMOD_Channel_GetPaused( mChannel, &isTrue ); + if ( isTrue ) + return SFXStatusPaused; + + SFXFMODDevice::smFunc->FMOD_Channel_IsPlaying( mChannel, &isTrue ); + if ( isTrue ) + return SFXStatusPlaying; + } + + SFXFMODDevice::smFunc->FMOD_Channel_Stop( mChannel ); + mChannel = NULL; + + return SFXStatusStopped; +} + +void SFXFMODVoice::_play() +{ + if( !mChannel ) + _assignChannel(); + + SFXFMODDevice::smFunc->FMOD_Channel_SetPaused( mChannel, false ); +} + +void SFXFMODVoice::_pause() +{ + if( mChannel ) + SFXFMODDevice::smFunc->FMOD_Channel_SetPaused( mChannel, true ); +} + +void SFXFMODVoice::_stop() +{ + if( mChannel ) + SFXFMODDevice::smFunc->FMOD_Channel_Stop(mChannel); + + mChannel = NULL; +} + +void SFXFMODVoice::_seek( U32 sample ) +{ + if( !mChannel ) + _assignChannel(); + + SFXFMODDevice::smFunc->FMOD_Channel_SetPosition + ( mChannel, sample, FMOD_TIMEUNIT_PCM ); +} + +bool SFXFMODVoice::_assignChannel() +{ + AssertFatal( _getBuffer()->mSound != NULL, "SFXFMODVoice::_assignChannel() - No sound assigned!" ); + + // we start playing it now in the paused state, so that we can immediately set attributes that + // depend on having a channel (position, volume, etc). According to the FMod docs + // it is ok to do this. + bool success = SFXFMODDevice::smFunc->FMOD_System_PlaySound( + SFXFMODDevice::smSystem, + FMOD_CHANNEL_FREE, + _getBuffer()->mSound, + true, + &mChannel ) == FMOD_OK; + + if( success ) + { + SFXFMODDevice::smFunc->FMOD_Channel_SetMode( mChannel, mMode ); + SFXFMODDevice::smFunc->FMOD_Channel_SetLoopCount( mChannel, mMode & FMOD_LOOP_NORMAL ? -1 : 0 ); + + if( mSetFlags.test( SET_Velocity ) ) + SFXFMODDevice::smFunc->FMOD_Channel_Set3DAttributes( mChannel, ( const FMOD_VECTOR* ) NULL, &mVelocity ); + if( mSetFlags.test( SET_MinMaxDistance ) ) + SFXFMODDevice::smFunc->FMOD_Channel_Set3DMinMaxDistance(mChannel, mMinDistance, mMaxDistance); + if( mSetFlags.test( SET_Transform ) ) + { + SFXFMODDevice::smFunc->FMOD_Channel_Set3DAttributes( mChannel, &mPosition, ( const FMOD_VECTOR* ) NULL ); + SFXFMODDevice::smFunc->FMOD_Channel_Set3DConeOrientation( mChannel, &mDirection ); + } + if( mSetFlags.test( SET_Volume ) ) + SFXFMODDevice::smFunc->FMOD_Channel_SetVolume(mChannel, mVolume); + if( mSetFlags.test( SET_Pitch ) ) + SFXFMODDevice::smFunc->FMOD_Channel_SetFrequency( mChannel, mFrequency ); + if( mSetFlags.test( SET_Cone ) ) + SFXFMODDevice::smFunc->FMOD_Channel_Set3DConeSettings( + mChannel, + mConeInnerAngle, + mConeOuterAngle, + mConeOuterVolume ); + if( mSetFlags.test( SET_Priority ) ) + SFXFMODDevice::smFunc->FMOD_Channel_SetPriority( mChannel, TorquePriorityToFMODPriority( mPriority ) ); + if( mSetFlags.test( SET_Reverb ) ) + SFXFMODDevice::smFunc->FMOD_Channel_SetReverbProperties( mChannel, &mReverb ); + } + + return success; +} + +U32 SFXFMODVoice::_tell() const +{ + if( !mChannel ) + return 0; + + U32 pos; + SFXFMODDevice::smFunc->FMOD_Channel_GetPosition( mChannel, &pos, ( FMOD_TIMEUNIT ) FMOD_TIMEUNIT_PCMBYTES ); + return _getBuffer()->getSamplePos( pos ); +} + +void SFXFMODVoice::setMinMaxDistance( F32 min, F32 max ) +{ + if ( !( _getBuffer()->mMode & FMOD_3D ) ) + return; + + mMinDistance = min; + mMaxDistance = max; + + mSetFlags.set( SET_MinMaxDistance ); + + if( mChannel ) + SFXFMODDevice::smFunc->FMOD_Channel_Set3DMinMaxDistance(mChannel, mMinDistance, mMaxDistance); +} + +void SFXFMODVoice::play( bool looping ) +{ + if( mBuffer->isStreaming() ) + looping = true; + + mMode = mDevice->get3dRollOffMode(); + mMode |= (looping ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF); + + Parent::play( looping ); +} + +void SFXFMODVoice::setVelocity( const VectorF& velocity ) +{ + if( !( _getBuffer()->mMode & FMOD_3D ) ) + return; + + // Note we have to do a handedness swap; see the + // listener update code in SFXFMODDevice for details. + mVelocity.x = velocity.x; + mVelocity.y = velocity.z; + mVelocity.z = velocity.y; + + mSetFlags.set( SET_Velocity ); + + if( mChannel ) + SFXFMODDevice::smFunc->FMOD_Channel_Set3DAttributes( mChannel, ( const FMOD_VECTOR* ) NULL, &mVelocity ); +} + +void SFXFMODVoice::setTransform( const MatrixF& transform ) +{ + if ( !( _getBuffer()->mMode & FMOD_3D ) ) + return; + + transform.getColumn( 3, (Point3F*)&mPosition ); + transform.getColumn( 1, (Point3F*)&mDirection ); + + // Note we have to do a handedness swap; see the + // listener update code in SFXFMODDevice for details. + swap( mPosition.y, mPosition.z ); + swap( mDirection.y, mDirection.z ); + + mSetFlags.set( SET_Transform ); + + if( mChannel ) + { + // This can fail safe, so don't assert if it fails. + SFXFMODDevice::smFunc->FMOD_Channel_Set3DAttributes( mChannel, &mPosition, ( const FMOD_VECTOR* ) NULL ); + SFXFMODDevice::smFunc->FMOD_Channel_Set3DConeOrientation( mChannel, &mDirection ); + } +} + +void SFXFMODVoice::setVolume( F32 volume ) +{ + mVolume = volume; + mSetFlags.set( SET_Volume ); + + if( mChannel ) + SFXFMODDevice::smFunc->FMOD_Channel_SetVolume( mChannel, volume ); +} + +void SFXFMODVoice::setPriority( F32 priority ) +{ + mPriority = priority; + mSetFlags.set( SET_Priority ); + + if( mChannel ) + SFXFMODDevice::smFunc->FMOD_Channel_SetPriority( mChannel, TorquePriorityToFMODPriority( priority ) ); +} + +void SFXFMODVoice::setPitch( F32 pitch ) +{ + // if we do not know the frequency, we cannot change the pitch + F32 frequency = _getBuffer()->getFormat().getSamplesPerSecond(); + if ( frequency == 0 ) + return; + + mFrequency = frequency * pitch; + + mSetFlags.set( SET_Pitch ); + + // Scale the original frequency by the pitch factor. + if( mChannel ) + SFXFMODDevice::smFunc->FMOD_Channel_SetFrequency(mChannel, mFrequency); +} + +void SFXFMODVoice::setCone( F32 innerAngle, F32 outerAngle, F32 outerVolume ) +{ + mConeInnerAngle = innerAngle; + mConeOuterAngle = outerAngle; + mConeOuterVolume = outerVolume; + + mSetFlags.set( SET_Cone ); + + if( mChannel ) + SFXFMODDevice::smFunc->FMOD_Channel_Set3DConeSettings( + mChannel, + mConeInnerAngle, + mConeOuterAngle, + mConeOuterVolume ); +} + +void SFXFMODVoice::setReverb( const SFXSoundReverbProperties& reverb ) +{ + dMemset( &mReverb, 0, sizeof( mReverb ) ); + + mReverb.Direct = reverb.mDirect; + mReverb.Room = reverb.mRoom; + mReverb.Flags = reverb.mFlags; + + mSetFlags.set( SET_Reverb ); + + if( mChannel ) + SFXFMODDevice::smFunc->FMOD_Channel_SetReverbProperties( mChannel, &mReverb ); +} + +bool SFXFMODVoice::isVirtual() const +{ + if( mChannel ) + { + FMOD_BOOL result; + SFXFMODDevice::smFunc->FMOD_Channel_IsVirtual( mChannel, &result ); + return result; + } + else + return false; +} diff --git a/Engine/source/sfx/fmod/sfxFMODVoice.h b/Engine/source/sfx/fmod/sfxFMODVoice.h new file mode 100644 index 000000000..14a5f7ced --- /dev/null +++ b/Engine/source/sfx/fmod/sfxFMODVoice.h @@ -0,0 +1,121 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXFMODVOICE_H_ +#define _SFXFMODVOICE_H_ + +#ifndef _SFXDEVICE_H_ + #include "sfx/sfxDevice.h" +#endif +#ifndef _SFXVOICE_H_ + #include "sfx/sfxVoice.h" +#endif +#ifndef _BITSET_H_ + #include "core/bitSet.h" +#endif + +#include "fmod.h" + +class SFXSource; +class SFXFMODBuffer; +class SFXFMODDevice; + + +class SFXFMODVoice : public SFXVoice +{ + typedef SFXVoice Parent; + friend class SFXFMODBuffer; + + protected: + + SFXFMODDevice *mDevice; + + mutable FMOD_CHANNEL *mChannel; + + enum ESettings + { + SET_MinMaxDistance = BIT( 0 ), + SET_Velocity = BIT( 1 ), + SET_Transform = BIT( 2 ), + SET_Volume = BIT( 3 ), + SET_Pitch = BIT( 4 ), + SET_Cone = BIT( 5 ), + SET_Priority = BIT( 6 ), + SET_Reverb = BIT( 7 ), + }; + + BitSet32 mSetFlags; + + FMOD_MODE mMode; + F32 mMinDistance; + F32 mMaxDistance; + F32 mVolume; + F32 mPriority; + F32 mFrequency; + F32 mConeInnerAngle; + F32 mConeOuterAngle; + F32 mConeOuterVolume; + FMOD_VECTOR mVelocity; + FMOD_VECTOR mPosition; + FMOD_VECTOR mDirection; + FMOD_REVERB_CHANNELPROPERTIES mReverb; + + /// + SFXFMODVoice( SFXFMODDevice *device, + SFXFMODBuffer *buffer ); + + // prep for playback + bool _assignChannel(); + + SFXFMODBuffer* _getBuffer() const { return ( SFXFMODBuffer* ) mBuffer.getPointer(); } + + // SFXVoice. + virtual SFXStatus _status() const; + virtual void _play(); + virtual void _pause(); + virtual void _stop(); + virtual void _seek( U32 sample ); + virtual U32 _tell() const; + + public: + + /// + static SFXFMODVoice* create( SFXFMODDevice *device, + SFXFMODBuffer *buffer ); + + /// + virtual ~SFXFMODVoice(); + + /// SFXVoice + void setMinMaxDistance( F32 min, F32 max ); + void play( bool looping ); + void setVelocity( const VectorF& velocity ); + void setTransform( const MatrixF& transform ); + void setVolume( F32 volume ); + void setPriority( F32 priority ); + void setPitch( F32 pitch ); + void setCone( F32 innerAngle, F32 outerAngle, F32 outerVolume ); + void setReverb( const SFXSoundReverbProperties& reverb ); + bool isVirtual() const; +}; + +#endif // _SFXFMODBUFFER_H_ \ No newline at end of file diff --git a/Engine/source/sfx/media/sfxVorbisStream.cpp b/Engine/source/sfx/media/sfxVorbisStream.cpp new file mode 100644 index 000000000..7ed1c51ec --- /dev/null +++ b/Engine/source/sfx/media/sfxVorbisStream.cpp @@ -0,0 +1,283 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_OGGVORBIS + + +#include "sfx/media/sfxVorbisStream.h" +#include "core/stream/stream.h" +#include "console/console.h" + + +SFXVorbisStream* SFXVorbisStream::create( Stream *stream ) +{ + SFXVorbisStream *sfxStream = new SFXVorbisStream(); + if ( sfxStream->open( stream, true ) ) + return sfxStream; + + delete sfxStream; + return NULL; +} + +SFXVorbisStream::SFXVorbisStream() + : mVF( NULL ), + mBytesRead( 0 ) +{ +} + +SFXVorbisStream::SFXVorbisStream( const SFXVorbisStream& cloneFrom ) + : Parent( cloneFrom ) +{ + if( !mStream->hasCapability( Stream::StreamPosition ) ) + { + Con::errorf( "SFXVorbisStream::SFXVorbisStream() - Source stream does not allow seeking" ); + return; + } + + mStream->setPosition( 0 ); + if( !_readHeader() ) + { + Con::errorf( "SFXVorbisStream::SFXVorbisStream() - Opening Vorbis stream failed" ); + return; + } + + ov_pcm_seek( mVF, ov_pcm_tell( cloneFrom.mVF ) ); + + mBitstream = cloneFrom.mBitstream; + mBytesRead = cloneFrom.mBytesRead; +} + +SFXVorbisStream::~SFXVorbisStream() +{ + // We must call close from our own destructor + // and not the base class... as it causes a + // pure virtual runtime assertion. + close(); +} + +size_t SFXVorbisStream::_read_func( void *ptr, size_t size, size_t nmemb, void *datasource ) +{ + Stream *stream = reinterpret_cast( datasource ); + + // Stream::read() returns true if any data was + // read, so we must track the read bytes ourselves. + U32 startByte = stream->getPosition(); + stream->read( size * nmemb, ptr ); + U32 endByte = stream->getPosition(); + + // How many did we actually read? + U32 readBytes = ( endByte - startByte ); + U32 readItems = readBytes / size; + + return readItems; +} + +int SFXVorbisStream::_seek_func( void *datasource, ogg_int64_t offset, int whence ) +{ + Stream *stream = reinterpret_cast( datasource ); + + U32 newPos = 0; + if ( whence == SEEK_CUR ) + newPos = stream->getPosition() + (U32)offset; + else if ( whence == SEEK_END ) + newPos = stream->getStreamSize() - (U32)offset; + else + newPos = (U32)offset; + + return stream->setPosition( newPos ) ? 0 : -1; +} + +long SFXVorbisStream::_tell_func( void *datasource ) +{ + Stream *stream = reinterpret_cast( datasource ); + return stream->getPosition(); +} + +bool SFXVorbisStream::_openVorbis() +{ +#if defined(TORQUE_OS_XENON) + // For some reason the datasource pointer passed to the callbacks is not the + // same as it is when passed in to ov_open_callbacks +#pragma message("There is a strange bug in ov_open_callbacks as it compiles on the Xbox360. Use FMOD resource loading.") + AssertWarn(false, "There is a strange bug in ov_open_callbacks as it compiles on the Xbox360. Use FMOD resource loading."); + return false; +#endif + mVF = new OggVorbis_File; + dMemset( mVF, 0, sizeof( OggVorbis_File ) ); + + const bool canSeek = mStream->hasCapability( Stream::StreamPosition ); + + ov_callbacks cb; + cb.read_func = _read_func; + cb.seek_func = canSeek ? _seek_func : NULL; + cb.close_func = NULL; + cb.tell_func = canSeek ? _tell_func : NULL; + + // Open it. + int ovResult = ov_open_callbacks( mStream, mVF, NULL, 0, cb ); + if( ovResult != 0 ) + return false; + + return true; +} + +bool SFXVorbisStream::_readHeader() +{ + if( !_openVorbis() ) + return false; + + // Fill in the format info. + const vorbis_info *vi = getInfo(); + mFormat.set( vi->channels, vi->channels * 16, vi->rate ); + + // Set the sample count. + mSamples = getPcmTotal(); + + // Reset the bitstream. + mBitstream = 0; + + return true; +} + +void SFXVorbisStream::_close() +{ + if ( !mVF ) + return; + + ov_clear( mVF ); + delete mVF; + mVF = NULL; + mBitstream = -1; +} + +const vorbis_info* SFXVorbisStream::getInfo( S32 link ) +{ + AssertFatal( mVF, "SFXVorbisStream::getInfo() - Stream is closed!" ); + return ov_info( mVF, link ); +} + +const vorbis_comment* SFXVorbisStream::getComment( S32 link ) +{ + AssertFatal( mVF, "SFXVorbisStream::getComment() - Stream is closed!" ); + return ov_comment( mVF, link ); +} + +U64 SFXVorbisStream::getPcmTotal( S32 link ) +{ + AssertFatal( mVF, "SFXVorbisStream::getInfo() - Stream is closed!" ); + return ov_pcm_total( mVF, link ); +} + +S32 SFXVorbisStream::read( U8 *buffer, + U32 length, + S32 *bitstream ) +{ + AssertFatal( mVF, "SFXVorbisStream::read() - Stream is closed!" ); + + mBitstream = *bitstream; + + #ifdef TORQUE_BIG_ENDIAN + static const int isBigEndian = 1; + #else + static const int isBigEndian = 0; + #endif + + // Vorbis doesn't seem to like reading + // requests longer than this. + const U32 MAXREAD = 4096; + + U32 bytesRead = 0; + U32 offset = 0; + U32 bytesToRead = 0; + + // Since it only returns the result of one packet + // per call, you generally have to loop to read it all. + while( offset < length ) + { + if ( ( length - offset ) < MAXREAD ) + bytesToRead = length - offset; + else + bytesToRead = MAXREAD; + + bytesRead = ov_read( mVF, (char*)buffer, bytesToRead, isBigEndian, 2, 1, bitstream ); + if( bytesRead == 0 ) // EOF + return offset; + else if( bytesRead < 0 ) + { + // We got an error... return the result. + return bytesRead; + } + + offset += bytesRead; + buffer += bytesRead; + mBytesRead += bytesRead; + } + + // Return the total data read. + return offset; +} + +bool SFXVorbisStream::isEOS() const +{ + return ( Parent::isEOS() || ( mStream && ov_pcm_tell( mVF ) == mSamples ) ); +} + +void SFXVorbisStream::reset() +{ + AssertFatal( mVF, "SFXVorbisStream::reset() - Stream is closed!" ); + + // There's a bug in libvorbis 1.2.0 that will cause the + // ov_pcm_seek* functions to go into an infinite loop on + // some files (apparently if they contain Theora streams). + // Avoiding to seek when not necessary seems to do the trick + // but if it deadlocks here, that's why. + + if( mBytesRead > 0 ) + { + ov_pcm_seek_page( mVF, 0 ); + mBitstream = 0; + mBytesRead = 0; + } +} + +U32 SFXVorbisStream::getPosition() const +{ + AssertFatal( mVF, "SFXVorbisStream::getPosition() - Stream is closed!" ); + return U32( ov_pcm_tell( mVF ) ) * mFormat.getBytesPerSample(); +} + +void SFXVorbisStream::setPosition( U32 offset ) +{ + AssertFatal( mVF, "SFXVorbisStream::setPosition() - Stream is closed!" ); + ov_pcm_seek( mVF, offset / mFormat.getBytesPerSample() ); +} + +U32 SFXVorbisStream::read( U8 *buffer, U32 length ) +{ + S32 result = read( buffer, length, &mBitstream ); + if ( result < 0 ) + return 0; + + return result; +} + +#endif // TORQUE_OGGVORBIS diff --git a/Engine/source/sfx/media/sfxVorbisStream.h b/Engine/source/sfx/media/sfxVorbisStream.h new file mode 100644 index 000000000..680d550fa --- /dev/null +++ b/Engine/source/sfx/media/sfxVorbisStream.h @@ -0,0 +1,112 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXVORBISSTREAM_H_ +#define _SFXVORBISSTREAM_H_ + +#ifdef TORQUE_OGGVORBIS + +#ifndef _SFXFILESTREAM_H_ +# include "sfx/sfxFileStream.h" +#endif +#include "core/util/safeDelete.h" +#include "vorbis/vorbisfile.h" + + +/// An SFXFileStream that loads sample data from a Vorbis file. +class SFXVorbisStream : public SFXFileStream, + public IPositionable< U32 > +{ + public: + + typedef SFXFileStream Parent; + + protected: + + /// The vorbis file. + OggVorbis_File *mVF; + + /// The current bitstream index. + S32 mBitstream; + + /// Total number of bytes read from the Vorbis stream so far. + U32 mBytesRead; + + /// + bool _openVorbis(); + + // The ov_callbacks. + static size_t _read_func( void *ptr, size_t size, size_t nmemb, void *datasource ); + static int _seek_func( void *datasource, ogg_int64_t offset, int whence ); + static long _tell_func( void *datasource ); + + // SFXStream + virtual bool _readHeader(); + virtual void _close(); + + public: + + /// + static SFXVorbisStream* create( Stream *stream ); + + /// + SFXVorbisStream(); + + /// + SFXVorbisStream( const SFXVorbisStream& cloneFrom ); + + virtual ~SFXVorbisStream(); + + /// + const vorbis_info* getInfo( S32 link = -1 ); + + /// + const vorbis_comment* getComment( S32 link = -1 ); + + /// + // TODO: Deal with error cases... like for unseekable streams! + U64 getPcmTotal( S32 link = -1 ); + + /// + S32 read( U8 *buffer, + U32 length, + S32 *bitstream ); + + // SFXStream + virtual void reset(); + virtual U32 read( U8 *buffer, U32 length ); + virtual bool isEOS() const; + virtual SFXStream* clone() const + { + SFXVorbisStream* stream = new SFXVorbisStream( *this ); + if( !stream->mVF ) + SAFE_DELETE( stream ); + return stream; + } + + // IPositionable + virtual U32 getPosition() const; + virtual void setPosition( U32 offset ); +}; + +#endif // TORQUE_OGGVORBIS +#endif // _SFXVORBISSTREAM_H_ diff --git a/Engine/source/sfx/media/sfxWavStream.cpp b/Engine/source/sfx/media/sfxWavStream.cpp new file mode 100644 index 000000000..3e5f4b983 --- /dev/null +++ b/Engine/source/sfx/media/sfxWavStream.cpp @@ -0,0 +1,299 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/media/sfxWavStream.h" +#include "core/stream/stream.h" +#include "core/strings/stringFunctions.h" + + +/// WAV File-header +struct WAVFileHdr +{ + U8 id[4]; + U32 size; + U8 type[4]; +}; + +//// WAV Fmt-header +struct WAVFmtHdr +{ + U16 format; + U16 channels; + U32 samplesPerSec; + U32 bytesPerSec; + U16 blockAlign; + U16 bitsPerSample; +}; + +/// WAV FmtEx-header +struct WAVFmtExHdr +{ + U16 size; + U16 samplesPerBlock; +}; + +/// WAV Smpl-header +struct WAVSmplHdr +{ + U32 manufacturer; + U32 product; + U32 samplePeriod; + U32 note; + U32 fineTune; + U32 SMPTEFormat; + U32 SMPTEOffest; + U32 loops; + U32 samplerData; + + struct + { + U32 identifier; + U32 type; + U32 start; + U32 end; + U32 fraction; + U32 count; + } loop[1]; +}; + +/// WAV Chunk-header +struct WAVChunkHdr +{ + U8 id[4]; + U32 size; +}; + + +SFXWavStream* SFXWavStream::create( Stream *stream ) +{ + SFXWavStream *sfxStream = new SFXWavStream(); + if ( sfxStream->open( stream, true ) ) + return sfxStream; + + delete sfxStream; + return NULL; +} + +SFXWavStream::SFXWavStream() +{ +} + +SFXWavStream::SFXWavStream( const SFXWavStream& cloneFrom ) + : Parent( cloneFrom ), + mDataStart( cloneFrom.mDataStart ) +{ +} + +SFXWavStream::~SFXWavStream() +{ + // We must call close from our own destructor + // and not the base class... as it causes a + // pure virtual runtime assertion. + close(); +} + +void SFXWavStream::_close() +{ + mDataStart = -1; +} + +bool SFXWavStream::_readHeader() +{ + // We read the wav chunks to gather than header info + // and find the start and end position of the data chunk. + mDataStart = -1; + + WAVFileHdr fileHdr; + mStream->read( 4, &fileHdr.id[0] ); + mStream->read( &fileHdr.size ); + mStream->read( 4, &fileHdr.type[0] ); + + fileHdr.size=((fileHdr.size+1)&~1)-4; + + WAVChunkHdr chunkHdr; + mStream->read( 4, &chunkHdr.id[0] ); + mStream->read( &chunkHdr.size ); + + // Unread chunk data rounded up to nearest WORD. + S32 chunkRemaining = chunkHdr.size + ( chunkHdr.size & 1 ); + + WAVFmtHdr fmtHdr; + WAVFmtExHdr fmtExHdr; + WAVSmplHdr smplHdr; + + dMemset(&fmtHdr, 0, sizeof(fmtHdr)); + + while ((fileHdr.size!=0) && (mStream->getStatus() != Stream::EOS)) + { + // WAV format header chunk. + if ( !dStrncmp( (const char*)chunkHdr.id, "fmt ", 4 ) ) + { + mStream->read(&fmtHdr.format); + mStream->read(&fmtHdr.channels); + mStream->read(&fmtHdr.samplesPerSec); + mStream->read(&fmtHdr.bytesPerSec); + mStream->read(&fmtHdr.blockAlign); + mStream->read(&fmtHdr.bitsPerSample); + + if ( fmtHdr.format == 0x0001 ) + { + mFormat.set( fmtHdr.channels, fmtHdr.bitsPerSample * fmtHdr.channels, fmtHdr.samplesPerSec ); + chunkRemaining -= sizeof( WAVFmtHdr ); + } + else + { + mStream->read(sizeof(WAVFmtExHdr), &fmtExHdr); + chunkRemaining -= sizeof(WAVFmtExHdr); + } + } + + // WAV data chunk + else if (!dStrncmp((const char*)chunkHdr.id,"data",4)) + { + // TODO: Handle these other formats in a more graceful manner! + + if (fmtHdr.format==0x0001) + { + mDataStart = mStream->getPosition(); + mStream->setPosition( mDataStart + chunkHdr.size ); + chunkRemaining -= chunkHdr.size; + mSamples = chunkHdr.size / mFormat.getBytesPerSample(); + } + else if (fmtHdr.format==0x0011) + { + //IMA ADPCM + } + else if (fmtHdr.format==0x0055) + { + //MP3 WAVE + } + } + + // WAV sample header + else if (!dStrncmp((const char*)chunkHdr.id,"smpl",4)) + { + // this struct read is NOT endian safe but it is ok because + // we are only testing the loops field against ZERO + mStream->read(sizeof(WAVSmplHdr), &smplHdr); + + // This has never been hooked up and its usefulness is + // dubious. Do we really want the audio file overriding + // the SFXDescription setting? + //mLooping = ( smplHdr.loops ? true : false ); + + chunkRemaining -= sizeof(WAVSmplHdr); + } + + // either we have unread chunk data or we found an unknown chunk type + // loop and read up to 1K bytes at a time until we have + // read to the end of this chunk + AssertFatal(chunkRemaining >= 0, "AudioBuffer::readWAV: remaining chunk data should never be less than zero."); + if ( chunkRemaining > 0 ) + { + U32 pos = mStream->getPosition(); + mStream->setPosition( pos + chunkRemaining ); + chunkRemaining = 0; + } + + fileHdr.size-=(((chunkHdr.size+1)&~1)+8); + + // read next chunk header... + mStream->read(4, &chunkHdr.id[0]); + mStream->read(&chunkHdr.size); + // unread chunk data rounded up to nearest WORD + chunkRemaining = chunkHdr.size + (chunkHdr.size&1); + } + + return ( mDataStart != -1 ); +} + +void SFXWavStream::reset() +{ + AssertFatal( mStream, "SFXWavStream::reset() - Stream is null!" ); + AssertFatal( mDataStart != -1, "SFXWavStream::seek() - Data start offset is invalid!" ); + mStream->setPosition( mDataStart ); +} + +U32 SFXWavStream::getPosition() const +{ + AssertFatal( mStream, "SFXWavStream::getPosition() - Stream is null!" ); + return ( mStream->getPosition() - mDataStart ); +} + +void SFXWavStream::setPosition( U32 offset ) +{ + AssertFatal( mStream, "SFXWavStream::setPosition() - Stream is null!" ); + + offset -= offset % mFormat.getBytesPerSample(); + const U32 dataLength = mSamples * mFormat.getBytesPerSample(); + if( offset > dataLength ) + offset = dataLength; + + AssertFatal( mDataStart != -1, "SFXWavStream::getPosition() - Data start offset is invalid!" ); + + U32 byte = mDataStart + offset; + + mStream->setPosition( byte ); +} + +U32 SFXWavStream::read( U8 *buffer, U32 bytes ) +{ + AssertFatal( mStream, "SFXWavStream::seek() - Stream is null!" ); + + // Read in even sample chunks. + + bytes -= bytes % mFormat.getBytesPerSample(); + + // Read the data and determine how much we've read. + // FileStreams apparently report positions past + // the actual stream length, so manually cap the + // numbers here. + + const U32 oldPosition = mStream->getPosition(); + mStream->read( bytes, buffer ); + U32 newPosition = mStream->getPosition(); + const U32 maxPosition = getDataLength() + mDataStart; + if( newPosition > maxPosition ) + newPosition = maxPosition; + + const U32 numBytesRead = newPosition - oldPosition; + + // TODO: Is it *just* 16 bit samples that needs to + // be flipped? What about 32 bit samples? + #ifdef TORQUE_BIG_ENDIAN + + // We need to endian-flip 16-bit data. + if ( getFormat().getBytesPerChannel() == 2 ) + { + U16 *ds = (U16*)buffer; + U16 *de = (U16*)(buffer+bytes); + while (ds +{ + public: + + typedef SFXFileStream Parent; + + protected: + + /// The file position of the start of + /// the PCM data for fast reset(). + U32 mDataStart; + + // SFXFileStream + virtual bool _readHeader(); + virtual void _close(); + + public: + + /// + static SFXWavStream* create( Stream *stream ); + + /// + SFXWavStream(); + + /// + SFXWavStream( const SFXWavStream& cloneFrom ); + + /// Destructor. + virtual ~SFXWavStream(); + + // SFXStream + virtual void reset(); + virtual U32 read( U8 *buffer, U32 length ); + virtual SFXStream* clone() const + { + SFXWavStream* stream = new SFXWavStream( *this ); + if( !stream->mStream ) + SAFE_DELETE( stream ); + return stream; + } + + // IPositionable + virtual U32 getPosition() const; + virtual void setPosition( U32 offset ); +}; + +#endif // _SFXWAVSTREAM_H_ diff --git a/Engine/source/sfx/null/sfxNullBuffer.cpp b/Engine/source/sfx/null/sfxNullBuffer.cpp new file mode 100644 index 000000000..d812b288b --- /dev/null +++ b/Engine/source/sfx/null/sfxNullBuffer.cpp @@ -0,0 +1,43 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/null/sfxNullBuffer.h" +#include "sfx/sfxInternal.h" + + +SFXNullBuffer::SFXNullBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ) + : Parent( stream, description, false ) +{ + mStatus = STATUS_Ready; +} + +SFXNullBuffer::~SFXNullBuffer() +{ +} + +void SFXNullBuffer::write( SFXInternal::SFXStreamPacket* const* packets, U32 num ) +{ + // Should never really be called, but to be safe... + + for( U32 i = 0; i < num; ++ i ) + destructSingle( packets[ i ] ); +} diff --git a/Engine/source/sfx/null/sfxNullBuffer.h b/Engine/source/sfx/null/sfxNullBuffer.h new file mode 100644 index 000000000..8ad77c714 --- /dev/null +++ b/Engine/source/sfx/null/sfxNullBuffer.h @@ -0,0 +1,49 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXNULLBUFFER_H_ +#define _SFXNULLBUFFER_H_ + +#ifndef _SFXBUFFER_H_ + #include "sfx/sfxBuffer.h" +#endif + + +class SFXNullBuffer : public SFXBuffer +{ + friend class SFXNullDevice; + typedef SFXBuffer Parent; + + protected: + + SFXNullBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ); + + // SFXBuffer. + virtual void write( SFXInternal::SFXStreamPacket* const* packets, U32 num ); + virtual void _flush() {} + + public: + + virtual ~SFXNullBuffer(); +}; + +#endif // _SFXNULLBUFFER_H_ \ No newline at end of file diff --git a/Engine/source/sfx/null/sfxNullDevice.cpp b/Engine/source/sfx/null/sfxNullDevice.cpp new file mode 100644 index 000000000..7b318caad --- /dev/null +++ b/Engine/source/sfx/null/sfxNullDevice.cpp @@ -0,0 +1,74 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/null/sfxNullDevice.h" +#include "sfx/null/sfxNullBuffer.h" +#include "sfx/sfxInternal.h" + + +SFXNullDevice::SFXNullDevice( SFXProvider* provider, + String name, + bool useHardware, + S32 maxBuffers ) + + : SFXDevice( name, provider, useHardware, maxBuffers ) +{ + mMaxBuffers = getMax( maxBuffers, 8 ); +} + +SFXNullDevice::~SFXNullDevice() +{ +} + +SFXBuffer* SFXNullDevice::createBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ) +{ + SFXNullBuffer* buffer = new SFXNullBuffer( stream, description ); + _addBuffer( buffer ); + + return buffer; +} + +SFXVoice* SFXNullDevice::createVoice( bool is3D, SFXBuffer *buffer ) +{ + // Don't bother going any further if we've + // exceeded the maximum voices. + if ( mVoices.size() >= mMaxBuffers ) + return NULL; + + AssertFatal( buffer, "SFXNullDevice::createVoice() - Got null buffer!" ); + + SFXNullBuffer* nullBuffer = dynamic_cast( buffer ); + AssertFatal( nullBuffer, "SFXNullDevice::createVoice() - Got bad buffer!" ); + + SFXNullVoice* voice = new SFXNullVoice( nullBuffer ); + if ( !voice ) + return NULL; + + _addVoice( voice ); + return voice; +} + +void SFXNullDevice::update() +{ + // Do nothing. Prevent SFXDevice from running + // its thing. +} diff --git a/Engine/source/sfx/null/sfxNullDevice.h b/Engine/source/sfx/null/sfxNullDevice.h new file mode 100644 index 000000000..d3b36ae7f --- /dev/null +++ b/Engine/source/sfx/null/sfxNullDevice.h @@ -0,0 +1,63 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXNULLDEVICE_H_ +#define _SFXNULLDEVICE_H_ + +class SFXProvider; + +#ifndef _SFXDEVICE_H_ + #include "sfx/sfxDevice.h" +#endif +#ifndef _SFXPROVIDER_H_ + #include "sfx/sfxProvider.h" +#endif +#ifndef _SFXNULLBUFFER_H_ + #include "sfx/null/sfxNullBuffer.h" +#endif +#ifndef _SFXNULLVOICE_H_ + #include "sfx/null/sfxNullVoice.h" +#endif + + +class SFXNullDevice : public SFXDevice +{ + typedef SFXDevice Parent; + + public: + + SFXNullDevice( SFXProvider* provider, + String name, + bool useHardware, + S32 maxBuffers ); + + virtual ~SFXNullDevice(); + + public: + + // SFXDevice. + virtual SFXBuffer* createBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ); + virtual SFXVoice* createVoice( bool is3D, SFXBuffer *buffer ); + virtual void update(); +}; + +#endif // _SFXNULLDEVICE_H_ \ No newline at end of file diff --git a/Engine/source/sfx/null/sfxNullProvider.cpp b/Engine/source/sfx/null/sfxNullProvider.cpp new file mode 100644 index 000000000..821413954 --- /dev/null +++ b/Engine/source/sfx/null/sfxNullProvider.cpp @@ -0,0 +1,97 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/sfxProvider.h" +#include "sfx/null/sfxNullDevice.h" +#include "core/strings/stringFunctions.h" +#include "core/module.h" + + +class SFXNullProvider : public SFXProvider +{ +public: + + SFXNullProvider() + : SFXProvider( "Null" ) {} + virtual ~SFXNullProvider(); + +protected: + void addDeviceDesc( const String& name, const String& desc ); + void init(); + +public: + + SFXDevice* createDevice( const String& deviceName, bool useHardware, S32 maxBuffers ); + +}; + +MODULE_BEGIN( SFXNull ) + + MODULE_INIT_BEFORE( SFX ) + MODULE_SHUTDOWN_AFTER( SFX ) + + SFXNullProvider* mProvider; + + MODULE_INIT + { + mProvider = new SFXNullProvider; + } + + MODULE_SHUTDOWN + { + delete mProvider; + } + +MODULE_END; + +void SFXNullProvider::init() +{ + regProvider( this ); + addDeviceDesc( "Null", "SFX Null Device" ); +} + +SFXNullProvider::~SFXNullProvider() +{ +} + + +void SFXNullProvider::addDeviceDesc( const String& name, const String& desc ) +{ + SFXDeviceInfo* info = new SFXDeviceInfo; + info->name = desc; + info->driver = name; + info->hasHardware = false; + info->maxBuffers = 8; + + mDeviceInfo.push_back( info ); +} + +SFXDevice* SFXNullProvider::createDevice( const String& deviceName, bool useHardware, S32 maxBuffers ) +{ + SFXDeviceInfo* info = _findDeviceInfo( deviceName ); + + // Do we find one to create? + if ( info ) + return new SFXNullDevice( this, info->name, useHardware, maxBuffers ); + + return NULL; +} diff --git a/Engine/source/sfx/null/sfxNullVoice.cpp b/Engine/source/sfx/null/sfxNullVoice.cpp new file mode 100644 index 000000000..8f9e0f2a3 --- /dev/null +++ b/Engine/source/sfx/null/sfxNullVoice.cpp @@ -0,0 +1,121 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/null/sfxNullVoice.h" +#include "sfx/null/sfxNullBuffer.h" +#include "sfx/sfxInternal.h" + + +SFXNullVoice::SFXNullVoice( SFXNullBuffer* buffer ) + : Parent( buffer ), + mIsLooping( false ) +{ +} + +SFXNullVoice::~SFXNullVoice() +{ +} + +SFXStatus SFXNullVoice::_status() const +{ + if( !mIsLooping + && mPlayTimer.isStarted() + && !mPlayTimer.isPaused() + && mPlayTimer.getPosition() >= mBuffer->getDuration() ) + mPlayTimer.stop(); + + if( mPlayTimer.isPaused() ) + return SFXStatusPaused; + else if( mPlayTimer.isStarted() ) + return SFXStatusPlaying; + else + return SFXStatusStopped; +} + +void SFXNullVoice::_play() +{ + mPlayTimer.start(); +} + +void SFXNullVoice::_pause() +{ + mPlayTimer.pause(); +} + +void SFXNullVoice::_stop() +{ + mPlayTimer.stop(); +} + +void SFXNullVoice::_seek( U32 sample ) +{ + const U32 sampleTime = mBuffer->getFormat().getDuration( sample ); + mPlayTimer.setPosition( sampleTime ); +} + +void SFXNullVoice::play( bool looping ) +{ + mIsLooping = looping; + mPlayTimer.start(); +} + +U32 SFXNullVoice::_tell() const +{ + U32 ms = _getPlayTime(); + + const SFXFormat& format = mBuffer->getFormat(); + return ( format.getDataLength( ms ) / format.getBytesPerSample() ); +} + +SFXStatus SFXNullVoice::getStatus() const +{ + return _status(); +} + +void SFXNullVoice::setPosition( U32 sample ) +{ + _seek( sample ); +} + +void SFXNullVoice::setMinMaxDistance( F32 min, F32 max ) +{ +} + +void SFXNullVoice::setVelocity( const VectorF& velocity ) +{ +} + +void SFXNullVoice::setTransform( const MatrixF& transform ) +{ +} + +void SFXNullVoice::setVolume( F32 volume ) +{ +} + +void SFXNullVoice::setPitch( F32 pitch ) +{ +} + +void SFXNullVoice::setCone( F32 innerAngle, F32 outerAngle, F32 outerVolume ) +{ +} diff --git a/Engine/source/sfx/null/sfxNullVoice.h b/Engine/source/sfx/null/sfxNullVoice.h new file mode 100644 index 000000000..02582542d --- /dev/null +++ b/Engine/source/sfx/null/sfxNullVoice.h @@ -0,0 +1,86 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXNULLVOICE_H_ +#define _SFXNULLVOICE_H_ + +#ifndef _SFXVOICE_H_ + #include "sfx/sfxVoice.h" +#endif +#ifndef _TIMESOURCE_H_ + #include "core/util/timeSource.h" +#endif + + +class SFXNullBuffer; + + +class SFXNullVoice : public SFXVoice +{ + public: + + typedef SFXVoice Parent; + friend class SFXNullDevice; + + protected: + + typedef GenericTimeSource< VirtualMSTimer > TimeSource; + + SFXNullVoice( SFXNullBuffer* buffer ); + + /// The virtual play timer. + mutable TimeSource mPlayTimer; + + /// + bool mIsLooping; + + // SFXVoice. + virtual SFXStatus _status() const; + virtual void _play(); + virtual void _pause(); + virtual void _stop(); + virtual void _seek( U32 sample ); + virtual U32 _tell() const; + + /// + U32 _getPlayTime() const + { + return mPlayTimer.getPosition(); + } + + public: + + virtual ~SFXNullVoice(); + + /// SFXVoice + SFXStatus getStatus() const; + void setPosition( U32 sample ); + void play( bool looping ); + void setMinMaxDistance( F32 min, F32 max ); + void setVelocity( const VectorF& velocity ); + void setTransform( const MatrixF& transform ); + void setVolume( F32 volume ); + void setPitch( F32 pitch ); + void setCone( F32 innerAngle, F32 outerAngle, F32 outerVolume ); +}; + +#endif // _SFXNULLVOICE_H_ \ No newline at end of file diff --git a/Engine/source/sfx/openal/LoadOAL.h b/Engine/source/sfx/openal/LoadOAL.h new file mode 100644 index 000000000..a2a11b829 --- /dev/null +++ b/Engine/source/sfx/openal/LoadOAL.h @@ -0,0 +1,209 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _LOADOAL_H_ +#define _LOADOAL_H_ + +#ifndef _PLATFORM_H_ +# include "platform/platform.h" +#endif + +#if defined(TORQUE_OS_MAC) +# include +# include +#else +# include +# include +#endif + +#ifndef ALAPIENTRY +#define ALAPIENTRY +#endif + +#ifndef ALCAPIENTRY +#define ALCAPIENTRY +#endif + +// Open AL Function table definition + +#ifndef _OPENALFNTABLE +#define _OPENALFNTABLE + +// AL 1.0 did not define the ALchar and ALCchar types, so define them here +// if they don't exist + +#ifndef ALchar +#define ALchar char +#endif + +#ifndef ALCchar +#define ALCchar char +#endif + +// Complete list of functions available in AL 1.0 implementations + +typedef void (ALAPIENTRY *LPALENABLE)( ALenum capability ); +typedef void (ALAPIENTRY *LPALDISABLE)( ALenum capability ); +typedef ALboolean (ALAPIENTRY *LPALISENABLED)( ALenum capability ); +typedef const ALchar* (ALAPIENTRY *LPALGETSTRING)( ALenum param ); +typedef void (ALAPIENTRY *LPALGETBOOLEANV)( ALenum param, ALboolean* data ); +typedef void (ALAPIENTRY *LPALGETINTEGERV)( ALenum param, ALint* data ); +typedef void (ALAPIENTRY *LPALGETFLOATV)( ALenum param, ALfloat* data ); +typedef void (ALAPIENTRY *LPALGETDOUBLEV)( ALenum param, ALdouble* data ); +typedef ALboolean (ALAPIENTRY *LPALGETBOOLEAN)( ALenum param ); +typedef ALint (ALAPIENTRY *LPALGETINTEGER)( ALenum param ); +typedef ALfloat (ALAPIENTRY *LPALGETFLOAT)( ALenum param ); +typedef ALdouble (ALAPIENTRY *LPALGETDOUBLE)( ALenum param ); +typedef ALenum (ALAPIENTRY *LPALGETERROR)( void ); +typedef ALboolean (ALAPIENTRY *LPALISEXTENSIONPRESENT)(const ALchar* extname ); +typedef void* (ALAPIENTRY *LPALGETPROCADDRESS)( const ALchar* fname ); +typedef ALenum (ALAPIENTRY *LPALGETENUMVALUE)( const ALchar* ename ); +typedef void (ALAPIENTRY *LPALLISTENERF)( ALenum param, ALfloat value ); +typedef void (ALAPIENTRY *LPALLISTENER3F)( ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ); +typedef void (ALAPIENTRY *LPALLISTENERFV)( ALenum param, const ALfloat* values ); +typedef void (ALAPIENTRY *LPALLISTENERI)( ALenum param, ALint value ); +typedef void (ALAPIENTRY *LPALGETLISTENERF)( ALenum param, ALfloat* value ); +typedef void (ALAPIENTRY *LPALGETLISTENER3F)( ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3 ); +typedef void (ALAPIENTRY *LPALGETLISTENERFV)( ALenum param, ALfloat* values ); +typedef void (ALAPIENTRY *LPALGETLISTENERI)( ALenum param, ALint* value ); +typedef void (ALAPIENTRY *LPALGENSOURCES)( ALsizei n, ALuint* sources ); +typedef void (ALAPIENTRY *LPALDELETESOURCES)( ALsizei n, const ALuint* sources ); +typedef ALboolean (ALAPIENTRY *LPALISSOURCE)( ALuint sid ); +typedef void (ALAPIENTRY *LPALSOURCEF)( ALuint sid, ALenum param, ALfloat value); +typedef void (ALAPIENTRY *LPALSOURCE3F)( ALuint sid, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ); +typedef void (ALAPIENTRY *LPALSOURCEFV)( ALuint sid, ALenum param, const ALfloat* values ); +typedef void (ALAPIENTRY *LPALSOURCEI)( ALuint sid, ALenum param, ALint value); +typedef void (ALAPIENTRY *LPALGETSOURCEF)( ALuint sid, ALenum param, ALfloat* value ); +typedef void (ALAPIENTRY *LPALGETSOURCE3F)( ALuint sid, ALenum param, ALfloat* value1, ALfloat* value2, ALfloat* value3); +typedef void (ALAPIENTRY *LPALGETSOURCEFV)( ALuint sid, ALenum param, ALfloat* values ); +typedef void (ALAPIENTRY *LPALGETSOURCEI)( ALuint sid, ALenum param, ALint* value ); +typedef void (ALAPIENTRY *LPALSOURCEPLAYV)( ALsizei ns, const ALuint *sids ); +typedef void (ALAPIENTRY *LPALSOURCESTOPV)( ALsizei ns, const ALuint *sids ); +typedef void (ALAPIENTRY *LPALSOURCEREWINDV)( ALsizei ns, const ALuint *sids ); +typedef void (ALAPIENTRY *LPALSOURCEPAUSEV)( ALsizei ns, const ALuint *sids ); +typedef void (ALAPIENTRY *LPALSOURCEPLAY)( ALuint sid ); +typedef void (ALAPIENTRY *LPALSOURCESTOP)( ALuint sid ); +typedef void (ALAPIENTRY *LPALSOURCEREWIND)( ALuint sid ); +typedef void (ALAPIENTRY *LPALSOURCEPAUSE)( ALuint sid ); +typedef void (ALAPIENTRY *LPALSOURCEQUEUEBUFFERS)(ALuint sid, ALsizei numEntries, const ALuint *bids ); +typedef void (ALAPIENTRY *LPALSOURCEUNQUEUEBUFFERS)(ALuint sid, ALsizei numEntries, ALuint *bids ); +typedef void (ALAPIENTRY *LPALGENBUFFERS)( ALsizei n, ALuint* buffers ); +typedef void (ALAPIENTRY *LPALDELETEBUFFERS)( ALsizei n, const ALuint* buffers ); +typedef ALboolean (ALAPIENTRY *LPALISBUFFER)( ALuint bid ); +typedef void (ALAPIENTRY *LPALBUFFERDATA)( ALuint bid, ALenum format, const ALvoid* data, ALsizei size, ALsizei freq ); +typedef void (ALAPIENTRY *LPALGETBUFFERF)( ALuint bid, ALenum param, ALfloat* value ); +typedef void (ALAPIENTRY *LPALGETBUFFERI)( ALuint bid, ALenum param, ALint* value ); +typedef void (ALAPIENTRY *LPALDOPPLERFACTOR)( ALfloat value ); +typedef void (ALAPIENTRY *LPALDOPPLERVELOCITY)( ALfloat value ); +typedef void (ALAPIENTRY *LPALDISTANCEMODEL)( ALenum distanceModel ); + +typedef ALCcontext * (ALCAPIENTRY *LPALCCREATECONTEXT) (ALCdevice *device, const ALCint *attrlist); +typedef ALCboolean (ALCAPIENTRY *LPALCMAKECONTEXTCURRENT)( ALCcontext *context ); +typedef void (ALCAPIENTRY *LPALCPROCESSCONTEXT)( ALCcontext *context ); +typedef void (ALCAPIENTRY *LPALCSUSPENDCONTEXT)( ALCcontext *context ); +typedef void (ALCAPIENTRY *LPALCDESTROYCONTEXT)( ALCcontext *context ); +typedef ALCcontext * (ALCAPIENTRY *LPALCGETCURRENTCONTEXT)( void ); +typedef ALCdevice * (ALCAPIENTRY *LPALCGETCONTEXTSDEVICE)( ALCcontext *context ); +typedef ALCdevice * (ALCAPIENTRY *LPALCOPENDEVICE)( const ALCchar *devicename ); +typedef ALCboolean (ALCAPIENTRY *LPALCCLOSEDEVICE)( ALCdevice *device ); +typedef ALCenum (ALCAPIENTRY *LPALCGETERROR)( ALCdevice *device ); +typedef ALCboolean (ALCAPIENTRY *LPALCISEXTENSIONPRESENT)( ALCdevice *device, const ALCchar *extname ); +typedef void * (ALCAPIENTRY *LPALCGETPROCADDRESS)(ALCdevice *device, const ALCchar *funcname ); +typedef ALCenum (ALCAPIENTRY *LPALCGETENUMVALUE)(ALCdevice *device, const ALCchar *enumname ); +typedef const ALCchar* (ALCAPIENTRY *LPALCGETSTRING)( ALCdevice *device, ALCenum param ); +typedef void (ALCAPIENTRY *LPALCGETINTEGERV)( ALCdevice *device, ALCenum param, ALCsizei size, ALCint *dest ); + +typedef struct +{ + LPALENABLE alEnable; + LPALDISABLE alDisable; + LPALISENABLED alIsEnabled; + LPALGETBOOLEAN alGetBoolean; + LPALGETINTEGER alGetInteger; + LPALGETFLOAT alGetFloat; + LPALGETDOUBLE alGetDouble; + LPALGETBOOLEANV alGetBooleanv; + LPALGETINTEGERV alGetIntegerv; + LPALGETFLOATV alGetFloatv; + LPALGETDOUBLEV alGetDoublev; + LPALGETSTRING alGetString; + LPALGETERROR alGetError; + LPALISEXTENSIONPRESENT alIsExtensionPresent; + LPALGETPROCADDRESS alGetProcAddress; + LPALGETENUMVALUE alGetEnumValue; + LPALLISTENERI alListeneri; + LPALLISTENERF alListenerf; + LPALLISTENER3F alListener3f; + LPALLISTENERFV alListenerfv; + LPALGETLISTENERI alGetListeneri; + LPALGETLISTENERF alGetListenerf; + LPALGETLISTENER3F alGetListener3f; + LPALGETLISTENERFV alGetListenerfv; + LPALGENSOURCES alGenSources; + LPALDELETESOURCES alDeleteSources; + LPALISSOURCE alIsSource; + LPALSOURCEI alSourcei; + LPALSOURCEF alSourcef; + LPALSOURCE3F alSource3f; + LPALSOURCEFV alSourcefv; + LPALGETSOURCEI alGetSourcei; + LPALGETSOURCEF alGetSourcef; + LPALGETSOURCEFV alGetSourcefv; + LPALSOURCEPLAYV alSourcePlayv; + LPALSOURCESTOPV alSourceStopv; + LPALSOURCEPLAY alSourcePlay; + LPALSOURCEPAUSE alSourcePause; + LPALSOURCESTOP alSourceStop; + LPALSOURCEREWIND alSourceRewind; + LPALGENBUFFERS alGenBuffers; + LPALDELETEBUFFERS alDeleteBuffers; + LPALISBUFFER alIsBuffer; + LPALBUFFERDATA alBufferData; + LPALGETBUFFERI alGetBufferi; + LPALGETBUFFERF alGetBufferf; + LPALSOURCEQUEUEBUFFERS alSourceQueueBuffers; + LPALSOURCEUNQUEUEBUFFERS alSourceUnqueueBuffers; + LPALDISTANCEMODEL alDistanceModel; + LPALDOPPLERFACTOR alDopplerFactor; + LPALDOPPLERVELOCITY alDopplerVelocity; + LPALCGETSTRING alcGetString; + LPALCGETINTEGERV alcGetIntegerv; + LPALCOPENDEVICE alcOpenDevice; + LPALCCLOSEDEVICE alcCloseDevice; + LPALCCREATECONTEXT alcCreateContext; + LPALCMAKECONTEXTCURRENT alcMakeContextCurrent; + LPALCPROCESSCONTEXT alcProcessContext; + LPALCGETCURRENTCONTEXT alcGetCurrentContext; + LPALCGETCONTEXTSDEVICE alcGetContextsDevice; + LPALCSUSPENDCONTEXT alcSuspendContext; + LPALCDESTROYCONTEXT alcDestroyContext; + LPALCGETERROR alcGetError; + LPALCISEXTENSIONPRESENT alcIsExtensionPresent; + LPALCGETPROCADDRESS alcGetProcAddress; + LPALCGETENUMVALUE alcGetEnumValue; +} OPENALFNTABLE, *LPOPENALFNTABLE; +#endif + +ALboolean LoadOAL10Library(char *szOALFullPathName, LPOPENALFNTABLE lpOALFnTable); +ALvoid UnloadOAL10Library(); + +#endif // _LOADOAL_H_ diff --git a/Engine/source/sfx/openal/aldlist.cpp b/Engine/source/sfx/openal/aldlist.cpp new file mode 100644 index 000000000..e3991a7ee --- /dev/null +++ b/Engine/source/sfx/openal/aldlist.cpp @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2006, Creative Labs Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of conditions and + * the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the distribution. + * * Neither the name of Creative Labs Inc. nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "core/strings/stringFunctions.h" + +#include "aldlist.h" +#if defined(TORQUE_OS_MAC) +#include +#else +#include +#endif + +/* + * Init call + */ +ALDeviceList::ALDeviceList( const OPENALFNTABLE &oalft ) +{ + VECTOR_SET_ASSOCIATION( vDeviceInfo ); + + ALDEVICEINFO ALDeviceInfo; + char *devices; + int index; + const char *defaultDeviceName; + const char *actualDeviceName; + + dMemcpy( &ALFunction, &oalft, sizeof( OPENALFNTABLE ) ); + + // DeviceInfo vector stores, for each enumerated device, it's device name, selection status, spec version #, and extension support + vDeviceInfo.clear(); + vDeviceInfo.reserve(10); + + defaultDeviceIndex = 0; + + // grab function pointers for 1.0-API functions, and if successful proceed to enumerate all devices + if (ALFunction.alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT")) { + devices = (char *)ALFunction.alcGetString(NULL, ALC_DEVICE_SPECIFIER); + defaultDeviceName = (char *)ALFunction.alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); + index = 0; + // go through device list (each device terminated with a single NULL, list terminated with double NULL) + while (*devices != 0) { + if (dStrcmp(defaultDeviceName, devices) == 0) { + defaultDeviceIndex = index; + } + ALCdevice *device = ALFunction.alcOpenDevice(devices); + if (device) { + ALCcontext *context = ALFunction.alcCreateContext(device, NULL); + if (context) { + ALFunction.alcMakeContextCurrent(context); + // if new actual device name isn't already in the list, then add it... + actualDeviceName = ALFunction.alcGetString(device, ALC_DEVICE_SPECIFIER); + bool bNewName = true; + for (int i = 0; i < GetNumDevices(); i++) { + if (dStrcmp(GetDeviceName(i), actualDeviceName) == 0) { + bNewName = false; + } + } + if ((bNewName) && (actualDeviceName != NULL) && (dStrlen(actualDeviceName) > 0)) { + dMemset(&ALDeviceInfo, 0, sizeof(ALDEVICEINFO)); + ALDeviceInfo.bSelected = true; + dStrncpy(ALDeviceInfo.strDeviceName, actualDeviceName, sizeof(ALDeviceInfo.strDeviceName)); + ALFunction.alcGetIntegerv(device, ALC_MAJOR_VERSION, sizeof(int), &ALDeviceInfo.iMajorVersion); + ALFunction.alcGetIntegerv(device, ALC_MINOR_VERSION, sizeof(int), &ALDeviceInfo.iMinorVersion); + + ALDeviceInfo.iCapsFlags = 0; + + // Check for ALC Extensions + if (ALFunction.alcIsExtensionPresent(device, "ALC_EXT_CAPTURE") == AL_TRUE) + ALDeviceInfo.iCapsFlags |= SFXALCapture; + if (ALFunction.alcIsExtensionPresent(device, "ALC_EXT_EFX") == AL_TRUE) + ALDeviceInfo.iCapsFlags |= SFXALEFX; + + // Check for AL Extensions + if (ALFunction.alIsExtensionPresent("AL_EXT_OFFSET") == AL_TRUE) + ALDeviceInfo.iCapsFlags |= SFXALOffset; + + if (ALFunction.alIsExtensionPresent("AL_EXT_LINEAR_DISTANCE") == AL_TRUE) + ALDeviceInfo.iCapsFlags |= SFXALLinearDistance; + if (ALFunction.alIsExtensionPresent("AL_EXT_EXPONENT_DISTANCE") == AL_TRUE) + ALDeviceInfo.iCapsFlags |= SFXALExponentDistance; + + if (ALFunction.alIsExtensionPresent("EAX2.0") == AL_TRUE) + ALDeviceInfo.iCapsFlags |= SFXALEAX2; + if (ALFunction.alIsExtensionPresent("EAX3.0") == AL_TRUE) + ALDeviceInfo.iCapsFlags |= SFXALEAX3; + if (ALFunction.alIsExtensionPresent("EAX4.0") == AL_TRUE) + ALDeviceInfo.iCapsFlags |= SFXALEAX4; + if (ALFunction.alIsExtensionPresent("EAX5.0") == AL_TRUE) + ALDeviceInfo.iCapsFlags |= SFXALEAX5; + + if (ALFunction.alIsExtensionPresent("EAX-RAM") == AL_TRUE) + ALDeviceInfo.iCapsFlags |= SFXALEAXRAM; + + // Get Source Count + ALDeviceInfo.uiSourceCount = GetMaxNumSources(); + + vDeviceInfo.push_back(ALDeviceInfo); + } + ALFunction.alcMakeContextCurrent(NULL); + ALFunction.alcDestroyContext(context); + } + ALFunction.alcCloseDevice(device); + } + devices += dStrlen(devices) + 1; + index += 1; + } + } + + ResetFilters(); +} + +/* + * Exit call + */ +ALDeviceList::~ALDeviceList() +{ +} + +/* + * Returns the number of devices in the complete device list + */ +int ALDeviceList::GetNumDevices() +{ + return (int)vDeviceInfo.size(); +} + +/* + * Returns the device name at an index in the complete device list + */ +const char *ALDeviceList::GetDeviceName(int index) +{ + if (index < GetNumDevices()) + return vDeviceInfo[index].strDeviceName; + else + return NULL; +} + +/* + * Returns the major and minor version numbers for a device at a specified index in the complete list + */ +void ALDeviceList::GetDeviceVersion(int index, int *major, int *minor) +{ + if (index < GetNumDevices()) { + if (*major) + *major = vDeviceInfo[index].iMajorVersion; + if (*minor) + *minor = vDeviceInfo[index].iMinorVersion; + } + return; +} + +/* + * Returns the maximum number of Sources that can be generate on the given device + */ +unsigned int ALDeviceList::GetMaxNumSources(int index) +{ + if (index < GetNumDevices()) + return vDeviceInfo[index].uiSourceCount; + else + return 0; +} + +/* + * Checks if the extension is supported on the given device + */ +bool ALDeviceList::IsExtensionSupported(int index, SFXALCaps cap) +{ + bool bReturn = false; + + if (index < GetNumDevices()) + bReturn = vDeviceInfo[index].iCapsFlags & cap; + + return bReturn; +} + +/* + * returns the index of the default device in the complete device list + */ +int ALDeviceList::GetDefaultDevice() +{ + return defaultDeviceIndex; +} + +/* + * Deselects devices which don't have the specified minimum version + */ +void ALDeviceList::FilterDevicesMinVer(int major, int minor) +{ + int dMajor, dMinor; + for (unsigned int i = 0; i < vDeviceInfo.size(); i++) { + GetDeviceVersion(i, &dMajor, &dMinor); + if ((dMajor < major) || ((dMajor == major) && (dMinor < minor))) { + vDeviceInfo[i].bSelected = false; + } + } +} + +/* + * Deselects devices which don't have the specified maximum version + */ +void ALDeviceList::FilterDevicesMaxVer(int major, int minor) +{ + int dMajor, dMinor; + for (unsigned int i = 0; i < vDeviceInfo.size(); i++) { + GetDeviceVersion(i, &dMajor, &dMinor); + if ((dMajor > major) || ((dMajor == major) && (dMinor > minor))) { + vDeviceInfo[i].bSelected = false; + } + } +} + +/* + * Deselects device which don't support the given extension name + */ +void ALDeviceList::FilterDevicesExtension(SFXALCaps cap) +{ + for (unsigned int i = 0; i < vDeviceInfo.size(); i++) + vDeviceInfo[i].bSelected = vDeviceInfo[i].iCapsFlags & cap; +} + +/* + * Resets all filtering, such that all devices are in the list + */ +void ALDeviceList::ResetFilters() +{ + for (int i = 0; i < GetNumDevices(); i++) { + vDeviceInfo[i].bSelected = true; + } + filterIndex = 0; +} + +/* + * Gets index of first filtered device + */ +int ALDeviceList::GetFirstFilteredDevice() +{ + int i; + + for (i = 0; i < GetNumDevices(); i++) { + if (vDeviceInfo[i].bSelected == true) { + break; + } + } + filterIndex = i + 1; + return i; +} + +/* + * Gets index of next filtered device + */ +int ALDeviceList::GetNextFilteredDevice() +{ + int i; + + for (i = filterIndex; i < GetNumDevices(); i++) { + if (vDeviceInfo[i].bSelected == true) { + break; + } + } + filterIndex = i + 1; + return i; +} + +/* + * Internal function to detemine max number of Sources that can be generated + */ +unsigned int ALDeviceList::GetMaxNumSources() +{ + ALuint uiSources[256]; + unsigned int iSourceCount = 0; + + // Clear AL Error Code + ALFunction.alGetError(); + + // Generate up to 256 Sources, checking for any errors + for (iSourceCount = 0; iSourceCount < 256; iSourceCount++) + { + ALFunction.alGenSources(1, &uiSources[iSourceCount]); + if (ALFunction.alGetError() != AL_NO_ERROR) + break; + } + + // Release the Sources + ALFunction.alDeleteSources(iSourceCount, uiSources); + if (ALFunction.alGetError() != AL_NO_ERROR) + { + for (unsigned int i = 0; i < 256; i++) + { + ALFunction.alDeleteSources(1, &uiSources[i]); + } + } + + return iSourceCount; +} \ No newline at end of file diff --git a/Engine/source/sfx/openal/aldlist.h b/Engine/source/sfx/openal/aldlist.h new file mode 100644 index 000000000..00b0eaec3 --- /dev/null +++ b/Engine/source/sfx/openal/aldlist.h @@ -0,0 +1,70 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef ALDEVICELIST_H +#define ALDEVICELIST_H + +#pragma warning(disable: 4786) //disable warning "identifier was truncated to '255' characters in the browser information" +#include "core/util/tVector.h" +#include "core/stringTable.h" +#include "sfx/openal/sfxALCaps.h" +#include "LoadOAL.h" + +typedef struct +{ + char strDeviceName[256]; + int iMajorVersion; + int iMinorVersion; + unsigned int uiSourceCount; + int iCapsFlags; + bool bSelected; +} ALDEVICEINFO, *LPALDEVICEINFO; + +class ALDeviceList +{ +private: + OPENALFNTABLE ALFunction; + Vector vDeviceInfo; + int defaultDeviceIndex; + int filterIndex; + +public: + ALDeviceList ( const OPENALFNTABLE &oalft ); + ~ALDeviceList (); + int GetNumDevices(); + const char *GetDeviceName(int index); + void GetDeviceVersion(int index, int *major, int *minor); + unsigned int GetMaxNumSources(int index); + bool IsExtensionSupported(int index, SFXALCaps caps); + int GetDefaultDevice(); + void FilterDevicesMinVer(int major, int minor); + void FilterDevicesMaxVer(int major, int minor); + void FilterDevicesExtension(SFXALCaps caps); + void ResetFilters(); + int GetFirstFilteredDevice(); + int GetNextFilteredDevice(); + +private: + unsigned int GetMaxNumSources(); +}; + +#endif // ALDEVICELIST_H diff --git a/Engine/source/sfx/openal/mac/LoadOAL.mac.cpp b/Engine/source/sfx/openal/mac/LoadOAL.mac.cpp new file mode 100644 index 000000000..65852d2ed --- /dev/null +++ b/Engine/source/sfx/openal/mac/LoadOAL.mac.cpp @@ -0,0 +1,445 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// TODO: Implement OpenAL loading code which is currently stubbed out. + +#if defined(__MACOSX__) && !defined(TORQUE_OS_MAC) +#define TORQUE_OS_MAC +#endif + +#include +#include +#include "sfx/openal/LoadOAL.h" + +ALboolean LoadOAL10Library(char *szOALFullPathName, LPOPENALFNTABLE lpOALFnTable) +{ + // TODO: Implement this. + if (!lpOALFnTable) + return AL_FALSE; + + memset(lpOALFnTable, 0, sizeof(OPENALFNTABLE)); + + lpOALFnTable->alEnable = (LPALENABLE)alEnable; + if (lpOALFnTable->alEnable == NULL) + { + warn("Failed to retrieve 'alEnable' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alDisable = (LPALDISABLE)alDisable; + if (lpOALFnTable->alDisable == NULL) + { + warn("Failed to retrieve 'alDisable' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alIsEnabled = (LPALISENABLED)alIsEnabled; + if (lpOALFnTable->alIsEnabled == NULL) + { + warn("Failed to retrieve 'alIsEnabled' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetBoolean = (LPALGETBOOLEAN)alGetBoolean; + if (lpOALFnTable->alGetBoolean == NULL) + { + warn("Failed to retrieve 'alGetBoolean' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetInteger = (LPALGETINTEGER)alGetInteger; + if (lpOALFnTable->alGetInteger == NULL) + { + warn("Failed to retrieve 'alGetInteger' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetFloat = (LPALGETFLOAT)alGetFloat; + if (lpOALFnTable->alGetFloat == NULL) + { + warn("Failed to retrieve 'alGetFloat' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetDouble = (LPALGETDOUBLE)alGetDouble; + if (lpOALFnTable->alGetDouble == NULL) + { + warn("Failed to retrieve 'alGetDouble' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetBooleanv = (LPALGETBOOLEANV)alGetBooleanv; + if (lpOALFnTable->alGetBooleanv == NULL) + { + warn("Failed to retrieve 'alGetBooleanv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetIntegerv = (LPALGETINTEGERV)alGetIntegerv; + if (lpOALFnTable->alGetIntegerv == NULL) + { + warn("Failed to retrieve 'alGetIntegerv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetFloatv = (LPALGETFLOATV)alGetFloatv; + if (lpOALFnTable->alGetFloatv == NULL) + { + warn("Failed to retrieve 'alGetFloatv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetDoublev = (LPALGETDOUBLEV)alGetDoublev; + if (lpOALFnTable->alGetDoublev == NULL) + { + warn("Failed to retrieve 'alGetDoublev' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetString = (LPALGETSTRING)alGetString; + if (lpOALFnTable->alGetString == NULL) + { + warn("Failed to retrieve 'alGetString' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetError = (LPALGETERROR)alGetError; + if (lpOALFnTable->alGetError == NULL) + { + warn("Failed to retrieve 'alGetError' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alIsExtensionPresent = (LPALISEXTENSIONPRESENT)alIsExtensionPresent; + if (lpOALFnTable->alIsExtensionPresent == NULL) + { + warn("Failed to retrieve 'alIsExtensionPresent' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetProcAddress = (LPALGETPROCADDRESS)alGetProcAddress; + if (lpOALFnTable->alGetProcAddress == NULL) + { + warn("Failed to retrieve 'alGetProcAddress' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetEnumValue = (LPALGETENUMVALUE)alGetEnumValue; + if (lpOALFnTable->alGetEnumValue == NULL) + { + warn("Failed to retrieve 'alGetEnumValue' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alListeneri = (LPALLISTENERI)alListeneri; + if (lpOALFnTable->alListeneri == NULL) + { + warn("Failed to retrieve 'alListeneri' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alListenerf = (LPALLISTENERF)alListenerf; + if (lpOALFnTable->alListenerf == NULL) + { + warn("Failed to retrieve 'alListenerf' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alListener3f = (LPALLISTENER3F)alListener3f; + if (lpOALFnTable->alListener3f == NULL) + { + warn("Failed to retrieve 'alListener3f' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alListenerfv = (LPALLISTENERFV)alListenerfv; + if (lpOALFnTable->alListenerfv == NULL) + { + warn("Failed to retrieve 'alListenerfv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetListeneri = (LPALGETLISTENERI)alGetListeneri; + if (lpOALFnTable->alGetListeneri == NULL) + { + warn("Failed to retrieve 'alGetListeneri' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetListenerf =(LPALGETLISTENERF)alGetListenerf; + if (lpOALFnTable->alGetListenerf == NULL) + { + warn("Failed to retrieve 'alGetListenerf' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetListener3f = (LPALGETLISTENER3F)alGetListener3f; + if (lpOALFnTable->alGetListener3f == NULL) + { + warn("Failed to retrieve 'alGetListener3f' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetListenerfv = (LPALGETLISTENERFV)alGetListenerfv; + if (lpOALFnTable->alGetListenerfv == NULL) + { + warn("Failed to retrieve 'alGetListenerfv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGenSources = (LPALGENSOURCES)alGenSources; + if (lpOALFnTable->alGenSources == NULL) + { + warn("Failed to retrieve 'alGenSources' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alDeleteSources = (LPALDELETESOURCES)alDeleteSources; + if (lpOALFnTable->alDeleteSources == NULL) + { + warn("Failed to retrieve 'alDeleteSources' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alIsSource = (LPALISSOURCE)alIsSource; + if (lpOALFnTable->alIsSource == NULL) + { + warn("Failed to retrieve 'alIsSource' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourcei = (LPALSOURCEI)alSourcei; + if (lpOALFnTable->alSourcei == NULL) + { + warn("Failed to retrieve 'alSourcei' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourcef = (LPALSOURCEF)alSourcef; + if (lpOALFnTable->alSourcef == NULL) + { + warn("Failed to retrieve 'alSourcef' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSource3f = (LPALSOURCE3F)alSource3f; + if (lpOALFnTable->alSource3f == NULL) + { + warn("Failed to retrieve 'alSource3f' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourcefv = (LPALSOURCEFV)alSourcefv; + if (lpOALFnTable->alSourcefv == NULL) + { + warn("Failed to retrieve 'alSourcefv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetSourcei = (LPALGETSOURCEI)alGetSourcei; + if (lpOALFnTable->alGetSourcei == NULL) + { + warn("Failed to retrieve 'alGetSourcei' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetSourcef = (LPALGETSOURCEF)alGetSourcef; + if (lpOALFnTable->alGetSourcef == NULL) + { + warn("Failed to retrieve 'alGetSourcef' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetSourcefv = (LPALGETSOURCEFV)alGetSourcefv; + if (lpOALFnTable->alGetSourcefv == NULL) + { + warn("Failed to retrieve 'alGetSourcefv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourcePlayv = (LPALSOURCEPLAYV)alSourcePlayv; + if (lpOALFnTable->alSourcePlayv == NULL) + { + warn("Failed to retrieve 'alSourcePlayv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourceStopv = (LPALSOURCESTOPV)alSourceStopv; + if (lpOALFnTable->alSourceStopv == NULL) + { + warn("Failed to retrieve 'alSourceStopv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourcePlay = (LPALSOURCEPLAY)alSourcePlay; + if (lpOALFnTable->alSourcePlay == NULL) + { + warn("Failed to retrieve 'alSourcePlay' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourcePause = (LPALSOURCEPAUSE)alSourcePause; + if (lpOALFnTable->alSourcePause == NULL) + { + warn("Failed to retrieve 'alSourcePause' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourceStop = (LPALSOURCESTOP)alSourceStop; + if (lpOALFnTable->alSourceStop == NULL) + { + warn("Failed to retrieve 'alSourceStop' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourceRewind = (LPALSOURCEREWIND)alSourceRewind; + if (lpOALFnTable->alSourceRewind == NULL) + { + warn("Failed to retrieve 'alSourceRewind' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGenBuffers = (LPALGENBUFFERS)alGenBuffers; + if (lpOALFnTable->alGenBuffers == NULL) + { + warn("Failed to retrieve 'alGenBuffers' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alDeleteBuffers = (LPALDELETEBUFFERS)alDeleteBuffers; + if (lpOALFnTable->alDeleteBuffers == NULL) + { + warn("Failed to retrieve 'alDeleteBuffers' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alIsBuffer = (LPALISBUFFER)alIsBuffer; + if (lpOALFnTable->alIsBuffer == NULL) + { + warn("Failed to retrieve 'alIsBuffer' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alBufferData = (LPALBUFFERDATA)alBufferData; + if (lpOALFnTable->alBufferData == NULL) + { + warn("Failed to retrieve 'alBufferData' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetBufferi = (LPALGETBUFFERI)alGetBufferi; + if (lpOALFnTable->alGetBufferi == NULL) + { + warn("Failed to retrieve 'alGetBufferi' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetBufferf = (LPALGETBUFFERF)alGetBufferf; + if (lpOALFnTable->alGetBufferf == NULL) + { + warn("Failed to retrieve 'alGetBufferf' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourceQueueBuffers = (LPALSOURCEQUEUEBUFFERS)alSourceQueueBuffers; + if (lpOALFnTable->alSourceQueueBuffers == NULL) + { + warn("Failed to retrieve 'alSourceQueueBuffers' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourceUnqueueBuffers = (LPALSOURCEUNQUEUEBUFFERS)alSourceUnqueueBuffers; + if (lpOALFnTable->alSourceUnqueueBuffers == NULL) + { + warn("Failed to retrieve 'alSourceUnqueueBuffers' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alDistanceModel = (LPALDISTANCEMODEL)alDistanceModel; + if (lpOALFnTable->alDistanceModel == NULL) + { + warn("Failed to retrieve 'alDistanceModel' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alDopplerFactor = (LPALDOPPLERFACTOR)alDopplerFactor; + if (lpOALFnTable->alDopplerFactor == NULL) + { + warn("Failed to retrieve 'alDopplerFactor' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alDopplerVelocity = (LPALDOPPLERVELOCITY)alDopplerVelocity; + if (lpOALFnTable->alDopplerVelocity == NULL) + { + warn("Failed to retrieve 'alDopplerVelocity' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcGetString = (LPALCGETSTRING)alcGetString; + if (lpOALFnTable->alcGetString == NULL) + { + warn("Failed to retrieve 'alcGetString' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcGetIntegerv = (LPALCGETINTEGERV)alcGetIntegerv; + if (lpOALFnTable->alcGetIntegerv == NULL) + { + warn("Failed to retrieve 'alcGetIntegerv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcOpenDevice = (LPALCOPENDEVICE)alcOpenDevice; + if (lpOALFnTable->alcOpenDevice == NULL) + { + warn("Failed to retrieve 'alcOpenDevice' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcCloseDevice = (LPALCCLOSEDEVICE)alcCloseDevice; + if (lpOALFnTable->alcCloseDevice == NULL) + { + warn("Failed to retrieve 'alcCloseDevice' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcCreateContext = (LPALCCREATECONTEXT)alcCreateContext; + if (lpOALFnTable->alcCreateContext == NULL) + { + warn("Failed to retrieve 'alcCreateContext' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcMakeContextCurrent = (LPALCMAKECONTEXTCURRENT)alcMakeContextCurrent; + if (lpOALFnTable->alcMakeContextCurrent == NULL) + { + warn("Failed to retrieve 'alcMakeContextCurrent' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcProcessContext = (LPALCPROCESSCONTEXT)alcProcessContext; + if (lpOALFnTable->alcProcessContext == NULL) + { + warn("Failed to retrieve 'alcProcessContext' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcGetCurrentContext = (LPALCGETCURRENTCONTEXT)alcGetCurrentContext; + if (lpOALFnTable->alcGetCurrentContext == NULL) + { + warn("Failed to retrieve 'alcGetCurrentContext' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcGetContextsDevice = (LPALCGETCONTEXTSDEVICE)alcGetContextsDevice; + if (lpOALFnTable->alcGetContextsDevice == NULL) + { + warn("Failed to retrieve 'alcGetContextsDevice' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcSuspendContext = (LPALCSUSPENDCONTEXT)alcSuspendContext; + if (lpOALFnTable->alcSuspendContext == NULL) + { + warn("Failed to retrieve 'alcSuspendContext' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcDestroyContext = (LPALCDESTROYCONTEXT)alcDestroyContext; + if (lpOALFnTable->alcDestroyContext == NULL) + { + warn("Failed to retrieve 'alcDestroyContext' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcGetError = (LPALCGETERROR)alcGetError; + if (lpOALFnTable->alcGetError == NULL) + { + warn("Failed to retrieve 'alcGetError' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcIsExtensionPresent = (LPALCISEXTENSIONPRESENT)alcIsExtensionPresent; + if (lpOALFnTable->alcIsExtensionPresent == NULL) + { + warn("Failed to retrieve 'alcIsExtensionPresent' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcGetProcAddress = (LPALCGETPROCADDRESS)alcGetProcAddress; + if (lpOALFnTable->alcGetProcAddress == NULL) + { + warn("Failed to retrieve 'alcGetProcAddress' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcGetEnumValue = (LPALCGETENUMVALUE)alcGetEnumValue; + if (lpOALFnTable->alcGetEnumValue == NULL) + { + warn("Failed to retrieve 'alcGetEnumValue' function address\n"); + return AL_FALSE; + } + + + return AL_TRUE; +} + +ALvoid UnloadOAL10Library() +{ +// TODO: Implement this. +} \ No newline at end of file diff --git a/Engine/source/sfx/openal/sfxALBuffer.cpp b/Engine/source/sfx/openal/sfxALBuffer.cpp new file mode 100644 index 000000000..80c012607 --- /dev/null +++ b/Engine/source/sfx/openal/sfxALBuffer.cpp @@ -0,0 +1,238 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/openal/sfxALBuffer.h" +#include "sfx/openal/sfxALVoice.h" +#include "sfx/openal/sfxALDevice.h" +#include "sfx/sfxDescription.h" +#include "console/console.h" + + +//#define DEBUG_SPEW + + +SFXALBuffer* SFXALBuffer::create( const OPENALFNTABLE &oalft, + const ThreadSafeRef< SFXStream >& stream, + SFXDescription* description, + bool useHardware ) +{ + if( !_sfxFormatToALFormat( stream->getFormat() ) ) + { + Con::errorf( "SFXALBuffer::create() - SFXFormat not supported by OpenAL" ); + return NULL; + } + + SFXALBuffer *buffer = new SFXALBuffer( oalft, + stream, + description, + useHardware ); + + return buffer; +} + +SFXALBuffer::SFXALBuffer( const OPENALFNTABLE &oalft, + const ThreadSafeRef< SFXStream >& stream, + SFXDescription* description, + bool useHardware ) + : Parent( stream, description ), + mOpenAL( oalft ), + mUseHardware( useHardware ), + mIs3d( description->mIs3D ) +{ + // Set up device buffers. + + if( !isStreaming() ) + mOpenAL.alGenBuffers( 1, &mALBuffer ); +} + +SFXALBuffer::~SFXALBuffer() +{ + if( _getUniqueVoice() ) + _getUniqueVoice()->stop(); + + // Release buffers. + if ( mOpenAL.alIsBuffer( mALBuffer )) + mOpenAL.alDeleteBuffers( 1, &mALBuffer ); + + while( mFreeBuffers.size() ) + { + ALuint buffer = mFreeBuffers.last(); + mOpenAL.alDeleteBuffers( 1, &buffer ); + mFreeBuffers.pop_back(); + } +} + +void SFXALBuffer::write( SFXInternal::SFXStreamPacket* const* packets, U32 num ) +{ + using namespace SFXInternal; + + if( !num ) + return; + + // If this is not a streaming buffer, just load the data into our single + // static buffer. + + if( !isStreaming() ) + { + SFXStreamPacket* packet = packets[ num - 1 ]; + + ALenum alFormat = _sfxFormatToALFormat( getFormat() ); + AssertFatal( alFormat != 0, "SFXALBuffer::write() - format unsupported" ); + + mOpenAL.alBufferData( mALBuffer, alFormat, + packet->data, packet->mSizeActual, getFormat().getSamplesPerSecond() ); + + destructSingle( packet ); + return; + } + + MutexHandle mutex; + mutex.lock( &_getUniqueVoice()->mMutex, true ); + + // Unqueue processed packets. + + ALuint source = _getUniqueVoice()->mSourceName; + ALint numProcessed; + mOpenAL.alGetSourcei( source, AL_BUFFERS_PROCESSED, &numProcessed ); + + for( U32 i = 0; i < numProcessed; ++ i ) + { + // Unqueue the buffer. + + ALuint buffer; + mOpenAL.alSourceUnqueueBuffers( source, 1, &buffer ); + + // Update the sample offset on the voice. + + ALint size; + mOpenAL.alGetBufferi( buffer, AL_SIZE, &size ); + _getUniqueVoice()->mSampleOffset += size / getFormat().getBytesPerSample(); + + // Push the buffer onto the freelist. + + mFreeBuffers.push_back( buffer ); + } + + // Queue buffers. + + for( U32 i = 0; i < num; ++ i ) + { + SFXStreamPacket* packet = packets[ i ]; + + // Allocate a buffer. + + ALuint buffer; + if( mFreeBuffers.size() ) + { + buffer = mFreeBuffers.last(); + mFreeBuffers.pop_back(); + } + else + mOpenAL.alGenBuffers( 1, &buffer ); + + // Upload the data. + + ALenum alFormat = _sfxFormatToALFormat( getFormat() ); + AssertFatal( alFormat != 0, "SFXALBuffer::write() - format unsupported" ); + AssertFatal( mOpenAL.alIsBuffer( buffer ), "SFXALBuffer::write() - buffer invalid" ); + + mOpenAL.alBufferData( buffer, alFormat, + packet->data, packet->mSizeActual, getFormat().getSamplesPerSecond() ); + + destructSingle( packet ); + + // Queue the buffer. + + mOpenAL.alSourceQueueBuffers( source, 1, &buffer ); + } +} + +void SFXALBuffer::_flush() +{ + AssertFatal( isStreaming(), "SFXALBuffer::_flush() - not a streaming buffer" ); + AssertFatal( SFXInternal::isSFXThread(), "SFXALBuffer::_flush() - not on SFX thread" ); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXALBuffer] Flushing buffer" ); + #endif + + _getUniqueVoice()->_stop(); + + MutexHandle mutex; + mutex.lock( &_getUniqueVoice()->mMutex, true ); + + ALuint source = _getUniqueVoice()->mSourceName; + + ALint numQueued; + mOpenAL.alGetSourcei( source, AL_BUFFERS_QUEUED, &numQueued ); + + for( U32 i = 0; i < numQueued; ++ i ) + { + ALuint buffer; + mOpenAL.alSourceUnqueueBuffers( source, 1, &buffer ); + mFreeBuffers.push_back( buffer ); + } + + _getUniqueVoice()->mSampleOffset = 0; + + //RD: disabling hack for now; rewritten queueing should be able to cope + #if 0 //def TORQUE_OS_MAC + + //WORKAROUND: Ugly hack on Mac. Apparently there's a bug in the OpenAL implementation + // that will cause AL_BUFFERS_PROCESSED to not be reset as it should be causing write() + // to fail. Brute-force this and just re-create the source. Let's pray that nobody + // issues any concurrent state changes on the voice resulting in us losing state here. + + ALuint newSource; + mOpenAL.alGenSources( 1, &newSource ); + + #define COPY_F( name ) \ + { \ + F32 val; \ + mOpenAL.alGetSourcef( source, name, &val ); \ + mOpenAL.alSourcef( source, name, val ); \ + } + + #define COPY_FV( name ) \ + { \ + VectorF val; \ + mOpenAL.alGetSourcefv( source, name, val ); \ + mOpenAL.alSourcefv( source, name, val ); \ + } + + COPY_F( AL_REFERENCE_DISTANCE ); + COPY_F( AL_MAX_DISTANCE ); + COPY_F( AL_GAIN ); + COPY_F( AL_PITCH ); + COPY_F( AL_CONE_INNER_ANGLE ); + COPY_F( AL_CONE_OUTER_ANGLE ); + COPY_F( AL_CONE_OUTER_GAIN ); + + COPY_FV( AL_VELOCITY ); + COPY_FV( AL_POSITION ); + COPY_FV( AL_DIRECTION ); + + _getUniqueVoice()->mSourceName = newSource; + mOpenAL.alDeleteSources( 1, &source ); + + #endif +} diff --git a/Engine/source/sfx/openal/sfxALBuffer.h b/Engine/source/sfx/openal/sfxALBuffer.h new file mode 100644 index 000000000..4ed44e133 --- /dev/null +++ b/Engine/source/sfx/openal/sfxALBuffer.h @@ -0,0 +1,119 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXALBUFFER_H_ +#define _SFXALBUFFER_H_ + +#ifndef _LOADOAL_H + #include "sfx/openal/LoadOAL.h" +#endif +#ifndef _SFXINTERNAL_H_ + #include "sfx/sfxInternal.h" +#endif +#ifndef _TVECTOR_H_ + #include "core/util/tVector.h" +#endif + + +class SFXALVoice; + + +class SFXALBuffer : public SFXBuffer +{ + public: + + typedef SFXBuffer Parent; + + friend class SFXALDevice; + friend class SFXALVoice; + + protected: + + /// AL buffer in case this is a static, non-streaming buffer. + ALuint mALBuffer; + + /// Free buffers for use in queuing in case this is a streaming buffer. + Vector< ALuint > mFreeBuffers; + + /// + SFXALBuffer( const OPENALFNTABLE &oalft, + const ThreadSafeRef< SFXStream >& stream, + SFXDescription* description, + bool useHardware ); + + /// + bool mIs3d; + + /// + bool mUseHardware; + + const OPENALFNTABLE &mOpenAL; + + /// + ALenum _getALFormat() const + { + return _sfxFormatToALFormat( getFormat() ); + } + + /// + static ALenum _sfxFormatToALFormat( const SFXFormat& format ) + { + if( format.getChannels() == 2 ) + { + const U32 bps = format.getBitsPerSample(); + if( bps == 16 ) + return AL_FORMAT_STEREO8; + else if( bps == 32 ) + return AL_FORMAT_STEREO16; + } + else if( format.getChannels() == 1 ) + { + const U32 bps = format.getBitsPerSample(); + if( bps == 8 ) + return AL_FORMAT_MONO8; + else if( bps == 16 ) + return AL_FORMAT_MONO16; + } + return 0; + } + + /// + SFXALVoice* _getUniqueVoice() const + { + return ( SFXALVoice* ) mUniqueVoice.getPointer(); + } + + // SFXBuffer. + virtual void write( SFXInternal::SFXStreamPacket* const* packets, U32 num ); + void _flush(); + + public: + + static SFXALBuffer* create( const OPENALFNTABLE &oalft, + const ThreadSafeRef< SFXStream >& stream, + SFXDescription* description, + bool useHardware ); + + virtual ~SFXALBuffer(); +}; + +#endif // _SFXALBUFFER_H_ \ No newline at end of file diff --git a/Engine/source/sfx/openal/sfxALCaps.h b/Engine/source/sfx/openal/sfxALCaps.h new file mode 100644 index 000000000..6a13e788e --- /dev/null +++ b/Engine/source/sfx/openal/sfxALCaps.h @@ -0,0 +1,40 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXALCAPS_H_ +#define _SFXALCAPS_H_ + +enum SFXALCaps +{ + SFXALCapture = 0, + SFXALEFX, + SFXALOffset, + SFXALLinearDistance, + SFXALExponentDistance, + SFXALEAX2, + SFXALEAX3, + SFXALEAX4, + SFXALEAX5, + SFXALEAXRAM +}; + +#endif \ No newline at end of file diff --git a/Engine/source/sfx/openal/sfxALDevice.cpp b/Engine/source/sfx/openal/sfxALDevice.cpp new file mode 100644 index 000000000..f2e00356c --- /dev/null +++ b/Engine/source/sfx/openal/sfxALDevice.cpp @@ -0,0 +1,199 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/openal/sfxALDevice.h" +#include "sfx/openal/sfxALBuffer.h" +#include "platform/async/asyncUpdate.h" + + +//----------------------------------------------------------------------------- + +SFXALDevice::SFXALDevice( SFXProvider *provider, + const OPENALFNTABLE &openal, + String name, + bool useHardware, + S32 maxBuffers ) + : Parent( name, provider, useHardware, maxBuffers ), + mOpenAL( openal ), + mDevice( NULL ), + mContext( NULL ) +{ + mMaxBuffers = getMax( maxBuffers, 8 ); + + // TODO: The OpenAL device doesn't set the primary buffer + // $pref::SFX::frequency or $pref::SFX::bitrate! + + mDevice = mOpenAL.alcOpenDevice( name ); + mOpenAL.alcGetError( mDevice ); + if( mDevice ) + { + mContext = mOpenAL.alcCreateContext( mDevice, NULL ); + + if( mContext ) + mOpenAL.alcMakeContextCurrent( mContext ); + + U32 err = mOpenAL.alcGetError( mDevice ); + + if( err != ALC_NO_ERROR ) + Con::errorf( "SFXALDevice - Initialization Error: %s", mOpenAL.alcGetString( mDevice, err ) ); + } + + AssertFatal( mDevice != NULL && mContext != NULL, "Failed to create OpenAL device and/or context!" ); + + // Start the update thread. + + if( !Con::getBoolVariable( "$_forceAllMainThread" ) ) + { + SFXInternal::gUpdateThread = new AsyncPeriodicUpdateThread + ( "OpenAL Update Thread", SFXInternal::gBufferUpdateList, + Con::getIntVariable( "$pref::SFX::updateInterval", SFXInternal::DEFAULT_UPDATE_INTERVAL ) ); + SFXInternal::gUpdateThread->start(); + } +} + +//----------------------------------------------------------------------------- + +SFXALDevice::~SFXALDevice() +{ + _releaseAllResources(); + + mOpenAL.alcMakeContextCurrent( NULL ); + mOpenAL.alcDestroyContext( mContext ); + mOpenAL.alcCloseDevice( mDevice ); +} + +//----------------------------------------------------------------------------- + +SFXBuffer* SFXALDevice::createBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ) +{ + AssertFatal( stream, "SFXALDevice::createBuffer() - Got null stream!" ); + AssertFatal( description, "SFXALDevice::createBuffer() - Got null description!" ); + + SFXALBuffer* buffer = SFXALBuffer::create( mOpenAL, + stream, + description, + mUseHardware ); + if ( !buffer ) + return NULL; + + _addBuffer( buffer ); + return buffer; +} + +//----------------------------------------------------------------------------- + +SFXVoice* SFXALDevice::createVoice( bool is3D, SFXBuffer *buffer ) +{ + // Don't bother going any further if we've + // exceeded the maximum voices. + if ( mVoices.size() >= mMaxBuffers ) + return NULL; + + AssertFatal( buffer, "SFXALDevice::createVoice() - Got null buffer!" ); + + SFXALBuffer* alBuffer = dynamic_cast( buffer ); + AssertFatal( alBuffer, "SFXALDevice::createVoice() - Got bad buffer!" ); + + SFXALVoice* voice = SFXALVoice::create( this, alBuffer ); + if ( !voice ) + return NULL; + + _addVoice( voice ); + return voice; +} + +//----------------------------------------------------------------------------- + +void SFXALDevice::setListener( U32 index, const SFXListenerProperties& listener ) +{ + if( index != 0 ) + return; + + // Torque and OpenAL are both right handed + // systems, so no coordinate flipping is needed. + + const MatrixF &transform = listener.getTransform(); + Point3F pos, tupple[2]; + transform.getColumn( 3, &pos ); + transform.getColumn( 1, &tupple[0] ); + transform.getColumn( 2, &tupple[1] ); + + const VectorF &velocity = listener.getVelocity(); + + mOpenAL.alListenerfv( AL_POSITION, pos ); + mOpenAL.alListenerfv( AL_VELOCITY, velocity ); + mOpenAL.alListenerfv( AL_ORIENTATION, (const F32 *)&tupple[0] ); +} + +//----------------------------------------------------------------------------- + +void SFXALDevice::setDistanceModel( SFXDistanceModel model ) +{ + switch( model ) + { + case SFXDistanceModelLinear: + mOpenAL.alDistanceModel( AL_LINEAR_DISTANCE_CLAMPED ); + if( mRolloffFactor != 1.0f ) + _setRolloffFactor( 1.0f ); // No rolloff on linear. + break; + + case SFXDistanceModelLogarithmic: + mOpenAL.alDistanceModel( AL_INVERSE_DISTANCE_CLAMPED ); + if( mUserRolloffFactor != mRolloffFactor ) + _setRolloffFactor( mUserRolloffFactor ); + break; + + default: + AssertWarn( false, "SFXALDevice::setDistanceModel - distance model not implemented" ); + } + + mDistanceModel = model; +} + +//----------------------------------------------------------------------------- + +void SFXALDevice::setDopplerFactor( F32 factor ) +{ + mOpenAL.alDopplerFactor( factor ); +} + +//----------------------------------------------------------------------------- + +void SFXALDevice::_setRolloffFactor( F32 factor ) +{ + mRolloffFactor = factor; + + for( U32 i = 0, num = mVoices.size(); i < num; ++ i ) + mOpenAL.alSourcef( ( ( SFXALVoice* ) mVoices[ i ] )->mSourceName, AL_ROLLOFF_FACTOR, factor ); +} + +//----------------------------------------------------------------------------- + +void SFXALDevice::setRolloffFactor( F32 factor ) +{ + if( mDistanceModel == SFXDistanceModelLinear && factor != 1.0f ) + Con::errorf( "SFXALDevice::setRolloffFactor - rolloff factor <> 1.0f ignored in linear distance model" ); + else + _setRolloffFactor( factor ); + + mUserRolloffFactor = factor; +} diff --git a/Engine/source/sfx/openal/sfxALDevice.h b/Engine/source/sfx/openal/sfxALDevice.h new file mode 100644 index 000000000..ee6f1ccdb --- /dev/null +++ b/Engine/source/sfx/openal/sfxALDevice.h @@ -0,0 +1,90 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXALDEVICE_H_ +#define _SFXALDEVICE_H_ + +class SFXProvider; + +#ifndef _SFXDEVICE_H_ +# include "sfx/sfxDevice.h" +#endif + +#ifndef _SFXPROVIDER_H_ +# include "sfx/sfxProvider.h" +#endif + +#ifndef _SFXALBUFFER_H_ +# include "sfx/openal/sfxALBuffer.h" +#endif + +#ifndef _SFXALVOICE_H_ +# include "sfx/openal/sfxALVoice.h" +#endif + +#ifndef _OPENALFNTABLE +# include "sfx/openal/LoadOAL.h" +#endif + + +class SFXALDevice : public SFXDevice +{ + public: + + typedef SFXDevice Parent; + friend class SFXALVoice; // mDistanceFactor, mRolloffFactor + + SFXALDevice( SFXProvider *provider, + const OPENALFNTABLE &openal, + String name, + bool useHardware, + S32 maxBuffers ); + + virtual ~SFXALDevice(); + + protected: + + OPENALFNTABLE mOpenAL; + + ALCcontext *mContext; + + ALCdevice *mDevice; + + SFXDistanceModel mDistanceModel; + F32 mDistanceFactor; + F32 mRolloffFactor; + F32 mUserRolloffFactor; + + void _setRolloffFactor( F32 factor ); + + public: + + // SFXDevice. + virtual SFXBuffer* createBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ); + virtual SFXVoice* createVoice( bool is3D, SFXBuffer *buffer ); + virtual void setListener( U32 index, const SFXListenerProperties& listener ); + virtual void setDistanceModel( SFXDistanceModel model ); + virtual void setDopplerFactor( F32 factor ); + virtual void setRolloffFactor( F32 factor ); +}; + +#endif // _SFXALDEVICE_H_ \ No newline at end of file diff --git a/Engine/source/sfx/openal/sfxALProvider.cpp b/Engine/source/sfx/openal/sfxALProvider.cpp new file mode 100644 index 000000000..7570500d6 --- /dev/null +++ b/Engine/source/sfx/openal/sfxALProvider.cpp @@ -0,0 +1,146 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#include "platform/platform.h" + +#include "sfx/sfxProvider.h" +#include "sfx/openal/sfxALDevice.h" +#include "sfx/openal/aldlist.h" + +#include "core/strings/stringFunctions.h" +#include "console/console.h" +#include "core/module.h" + + +class SFXALProvider : public SFXProvider +{ +public: + + SFXALProvider() + : SFXProvider( "OpenAL" ) { mALDL = NULL; } + virtual ~SFXALProvider(); + +protected: + OPENALFNTABLE mOpenAL; + ALDeviceList *mALDL; + + struct ALDeviceInfo : SFXDeviceInfo + { + + }; + + void init(); + +public: + SFXDevice *createDevice( const String& deviceName, bool useHardware, S32 maxBuffers ); + +}; + +MODULE_BEGIN( OpenAL ) + + MODULE_INIT_BEFORE( SFX ) + MODULE_SHUTDOWN_AFTER( SFX ) + + SFXALProvider* mProvider; + + MODULE_INIT + { + mProvider = new SFXALProvider; + } + + MODULE_SHUTDOWN + { + delete mProvider; + } + +MODULE_END; + +void SFXALProvider::init() +{ + if( LoadOAL10Library( NULL, &mOpenAL ) != AL_TRUE ) + { + Con::printf( "SFXALProvider - OpenAL not available." ); + return; + } + mALDL = new ALDeviceList( mOpenAL ); + + // Did we get any devices? + if ( mALDL->GetNumDevices() < 1 ) + { + Con::printf( "SFXALProvider - No valid devices found!" ); + return; + } + + // Cool, loop through them, and caps em + const char *deviceFormat = "OpenAL v%d.%d %s"; + + char temp[256]; + for( int i = 0; i < mALDL->GetNumDevices(); i++ ) + { + ALDeviceInfo* info = new ALDeviceInfo; + + info->name = String( mALDL->GetDeviceName( i ) ); + + int major, minor, eax = 0; + + mALDL->GetDeviceVersion( i, &major, &minor ); + + // Apologies for the blatent enum hack -patw + for( int j = SFXALEAX2; j < SFXALEAXRAM; j++ ) + eax += (int)mALDL->IsExtensionSupported( i, (SFXALCaps)j ); + + if( eax > 0 ) + { + eax += 2; // EAX support starts at 2.0 + dSprintf( temp, sizeof( temp ), "[EAX %d.0] %s", eax, ( mALDL->IsExtensionSupported( i, SFXALEAXRAM ) ? "EAX-RAM" : "" ) ); + } + else + dStrcpy( temp, "" ); + + info->driver = String::ToString( deviceFormat, major, minor, temp ); + info->hasHardware = eax > 0; + info->maxBuffers = mALDL->GetMaxNumSources( i ); + + mDeviceInfo.push_back( info ); + } + + regProvider( this ); +} + +SFXALProvider::~SFXALProvider() +{ + UnloadOAL10Library(); + + if (mALDL) + delete mALDL; +} + +SFXDevice *SFXALProvider::createDevice( const String& deviceName, bool useHardware, S32 maxBuffers ) +{ + ALDeviceInfo *info = dynamic_cast< ALDeviceInfo* > + ( _findDeviceInfo( deviceName) ); + + // Do we find one to create? + if ( info ) + return new SFXALDevice( this, mOpenAL, info->name, useHardware, maxBuffers ); + + return NULL; +} \ No newline at end of file diff --git a/Engine/source/sfx/openal/sfxALVoice.cpp b/Engine/source/sfx/openal/sfxALVoice.cpp new file mode 100644 index 000000000..aa90e5c85 --- /dev/null +++ b/Engine/source/sfx/openal/sfxALVoice.cpp @@ -0,0 +1,257 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "sfx/openal/sfxALVoice.h" +#include "sfx/openal/sfxALBuffer.h" +#include "sfx/openal/sfxALDevice.h" + + +#ifdef TORQUE_DEBUG +# define AL_SANITY_CHECK() \ + AssertFatal( mOpenAL.alIsSource( mSourceName ), "AL Source Sanity Check Failed!" ); +#else +# define AL_SANITY_CHECK() +#endif + + +//#define DEBUG_SPEW + + +SFXALVoice* SFXALVoice::create( SFXALDevice* device, SFXALBuffer *buffer ) +{ + AssertFatal( buffer, "SFXALVoice::create() - Got null buffer!" ); + + ALuint sourceName; + device->mOpenAL.alGenSources( 1, &sourceName ); + AssertFatal( device->mOpenAL.alIsSource( sourceName ), "AL Source Sanity Check Failed!" ); + + // Is this 3d? + // Okay, this looks odd, but bear with me for a moment. AL_SOURCE_RELATIVE does NOT indicate + // whether or not the volume of the sound should change depending on the position of the listener. + // OpenAL assumes that the volume will ALWAYS depend on the position of the listener. What AL_SOURCE_RELATIVE + // does do is dictate if the position of THIS SOURCE is relative to the listener. If AL_SOURCE_RELATIVE is AL_TRUE + // and the source's position is 0, 0, 0, then the source is directly on top of the listener at all times, which is what + // we want for non-3d sounds. + device->mOpenAL.alSourcei( sourceName, AL_SOURCE_RELATIVE, ( buffer->mIs3d ? AL_FALSE : AL_TRUE ) ); + + if( buffer->mIs3d ) + device->mOpenAL.alSourcef( sourceName, AL_ROLLOFF_FACTOR, device->mRolloffFactor ); + + SFXALVoice *voice = new SFXALVoice( device->mOpenAL, + buffer, + sourceName ); + + return voice; +} + +SFXALVoice::SFXALVoice( const OPENALFNTABLE &oalft, + SFXALBuffer *buffer, + ALuint sourceName ) + + : Parent( buffer ), + mOpenAL( oalft ), + mResumeAtSampleOffset( -1.0f ), + mSourceName( sourceName ), + mSampleOffset( 0 ) +{ + AL_SANITY_CHECK(); +} + +SFXALVoice::~SFXALVoice() +{ + mOpenAL.alDeleteSources( 1, &mSourceName ); +} + +void SFXALVoice::_lateBindStaticBufferIfNecessary() +{ + if( !mBuffer->isStreaming() ) + { + ALint bufferId; + mOpenAL.alGetSourcei( mSourceName, AL_BUFFER, &bufferId ); + if( !bufferId ) + mOpenAL.alSourcei( mSourceName, AL_BUFFER, _getBuffer()->mALBuffer ); + } +} + + +SFXStatus SFXALVoice::_status() const +{ + AL_SANITY_CHECK(); + + ALint state; + mOpenAL.alGetSourcei( mSourceName, AL_SOURCE_STATE, &state ); + + switch( state ) + { + case AL_PLAYING: return SFXStatusPlaying; + case AL_PAUSED: return SFXStatusPaused; + default: return SFXStatusStopped; + } +} + +void SFXALVoice::_play() +{ + AL_SANITY_CHECK(); + + _lateBindStaticBufferIfNecessary(); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXALVoice] Starting playback" ); + #endif + + mOpenAL.alSourcePlay( mSourceName ); + + //WORKAROUND: Adjust play cursor for buggy OAL when resuming playback. Do this after alSourcePlay + // as it is the play function that will cause the cursor to jump. + + if( mResumeAtSampleOffset != -1.0f ) + { + mOpenAL.alSourcef( mSourceName, AL_SAMPLE_OFFSET, mResumeAtSampleOffset ); + mResumeAtSampleOffset = -1.0f; + } +} + +void SFXALVoice::_pause() +{ + AL_SANITY_CHECK(); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXALVoice] Pausing playback" ); + #endif + + mOpenAL.alSourcePause( mSourceName ); + + //WORKAROUND: Another workaround for buggy OAL. Resuming playback of a paused source will cause the + // play cursor to jump. Save the cursor so we can manually move it into position in _play(). Sigh. + + mOpenAL.alGetSourcef( mSourceName, AL_SAMPLE_OFFSET, &mResumeAtSampleOffset ); +} + +void SFXALVoice::_stop() +{ + AL_SANITY_CHECK(); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXALVoice] Stopping playback" ); + #endif + + mOpenAL.alSourceStop( mSourceName ); + mSampleOffset = 0; + + mResumeAtSampleOffset = -1.0f; +} + +void SFXALVoice::_seek( U32 sample ) +{ + AL_SANITY_CHECK(); + + _lateBindStaticBufferIfNecessary(); + mOpenAL.alSourcei( mSourceName, AL_SAMPLE_OFFSET, sample ); + + mResumeAtSampleOffset = -1.0f; +} + +U32 SFXALVoice::_tell() const +{ + // Flush processed buffers as AL_SAMPLE_OFFSET will snap back to zero as soon + // as the queue is processed in whole. + + if( mBuffer->isStreaming() ) + mBuffer->write( NULL, 0 ); + + ALint pos; + mOpenAL.alGetSourcei( mSourceName, AL_SAMPLE_OFFSET, &pos ); + return ( pos + mSampleOffset ); +} + +void SFXALVoice::setMinMaxDistance( F32 min, F32 max ) +{ + AL_SANITY_CHECK(); + + mOpenAL.alSourcef( mSourceName, AL_REFERENCE_DISTANCE, min ); + mOpenAL.alSourcef( mSourceName, AL_MAX_DISTANCE, max ); +} + +void SFXALVoice::play( bool looping ) +{ + AL_SANITY_CHECK(); + + mOpenAL.alSourceStop( mSourceName ); + if( !mBuffer->isStreaming() ) + mOpenAL.alSourcei( mSourceName, AL_LOOPING, ( looping ? AL_TRUE : AL_FALSE ) ); + + Parent::play( looping ); +} + +void SFXALVoice::setVelocity( const VectorF& velocity ) +{ + AL_SANITY_CHECK(); + + // Torque and OpenAL are both right handed + // systems, so no coordinate flipping is needed. + + mOpenAL.alSourcefv( mSourceName, AL_VELOCITY, velocity ); +} + +void SFXALVoice::setTransform( const MatrixF& transform ) +{ + AL_SANITY_CHECK(); + + // Torque and OpenAL are both right handed + // systems, so no coordinate flipping is needed. + + Point3F pos, dir; + transform.getColumn( 3, &pos ); + transform.getColumn( 1, &dir ); + + mOpenAL.alSourcefv( mSourceName, AL_POSITION, pos ); + mOpenAL.alSourcefv( mSourceName, AL_DIRECTION, dir ); +} + +void SFXALVoice::setVolume( F32 volume ) +{ + AL_SANITY_CHECK(); + + mOpenAL.alSourcef( mSourceName, AL_GAIN, volume ); +} + +void SFXALVoice::setPitch( F32 pitch ) +{ + AL_SANITY_CHECK(); + + mOpenAL.alSourcef( mSourceName, AL_PITCH, pitch ); +} + +void SFXALVoice::setCone( F32 innerAngle, F32 outerAngle, F32 outerVolume ) +{ + AL_SANITY_CHECK(); + + mOpenAL.alSourcef( mSourceName, AL_CONE_INNER_ANGLE, innerAngle ); + mOpenAL.alSourcef( mSourceName, AL_CONE_OUTER_ANGLE, outerAngle ); + mOpenAL.alSourcef( mSourceName, AL_CONE_OUTER_GAIN, outerVolume ); +} + +void SFXALVoice::setRolloffFactor( F32 factor ) +{ + mOpenAL.alSourcef( mSourceName, AL_ROLLOFF_FACTOR, factor ); +} diff --git a/Engine/source/sfx/openal/sfxALVoice.h b/Engine/source/sfx/openal/sfxALVoice.h new file mode 100644 index 000000000..6360da843 --- /dev/null +++ b/Engine/source/sfx/openal/sfxALVoice.h @@ -0,0 +1,107 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXALVOICE_H_ +#define _SFXALVOICE_H_ + +#ifndef _SFXVOICE_H_ + #include "sfx/sfxVoice.h" +#endif +#ifndef _OPENALFNTABLE + #include "sfx/openal/LoadOAL.h" +#endif +#ifndef _PLATFORM_THREADS_MUTEX_H_ + #include "platform/threads/mutex.h" +#endif + + +class SFXALBuffer; +class SFXALDevice; + +class SFXALVoice : public SFXVoice +{ + public: + + typedef SFXVoice Parent; + friend class SFXALDevice; + friend class SFXALBuffer; + + protected: + + SFXALVoice( const OPENALFNTABLE &oalft, + SFXALBuffer *buffer, + ALuint sourceName ); + + ALuint mSourceName; + + /// Buggy OAL jumps around when pausing. Save playback cursor here. + F32 mResumeAtSampleOffset; + + /// Amount by which OAL's reported sample position is offset. + /// + /// OAL's sample position is relative to the current queue state, + /// so we manually need to keep track of how far into the total + /// queue we are. + U32 mSampleOffset; + + Mutex mMutex; + + const OPENALFNTABLE &mOpenAL; + + /// + SFXALBuffer* _getBuffer() const + { + return ( SFXALBuffer* ) mBuffer.getPointer(); + } + + /// For non-streaming buffers, late-bind the audio buffer + /// to the source as OAL will not accept writes to buffers + /// already bound. + void _lateBindStaticBufferIfNecessary(); + + // SFXVoice. + virtual SFXStatus _status() const; + virtual void _play(); + virtual void _pause(); + virtual void _stop(); + virtual void _seek( U32 sample ); + virtual U32 _tell() const; + + public: + + static SFXALVoice* create( SFXALDevice* device, + SFXALBuffer *buffer ); + + virtual ~SFXALVoice(); + + /// SFXVoice + void setMinMaxDistance( F32 min, F32 max ); + void play( bool looping ); + void setVelocity( const VectorF& velocity ); + void setTransform( const MatrixF& transform ); + void setVolume( F32 volume ); + void setPitch( F32 pitch ); + void setCone( F32 innerAngle, F32 outerAngle, F32 outerVolume ); + void setRolloffFactor( F32 factor ); +}; + +#endif // _SFXALVOICE_H_ \ No newline at end of file diff --git a/Engine/source/sfx/openal/win32/LoadOAL.cpp b/Engine/source/sfx/openal/win32/LoadOAL.cpp new file mode 100644 index 000000000..a3a31a0f7 --- /dev/null +++ b/Engine/source/sfx/openal/win32/LoadOAL.cpp @@ -0,0 +1,454 @@ +/* + * Copyright (c) 2006, Creative Labs Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this list of conditions and + * the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the distribution. + * * Neither the name of Creative Labs Inc. nor the names of its contributors may be used to endorse or + * promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include "sfx/openal/LoadOAL.h" + +HINSTANCE g_hOpenALDLL = NULL; + +ALboolean LoadOAL10Library(char *szOALFullPathName, LPOPENALFNTABLE lpOALFnTable) +{ + if (!lpOALFnTable) + return AL_FALSE; + + if (szOALFullPathName) + g_hOpenALDLL = LoadLibraryA(szOALFullPathName); + else + g_hOpenALDLL = LoadLibraryA("openal32.dll"); + + if (!g_hOpenALDLL) + return AL_FALSE; + + memset(lpOALFnTable, 0, sizeof(OPENALFNTABLE)); + + // Get function pointers + lpOALFnTable->alEnable = (LPALENABLE)GetProcAddress(g_hOpenALDLL, "alEnable"); + if (lpOALFnTable->alEnable == NULL) + { + OutputDebugStringA("Failed to retrieve 'alEnable' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alDisable = (LPALDISABLE)GetProcAddress(g_hOpenALDLL, "alDisable"); + if (lpOALFnTable->alDisable == NULL) + { + OutputDebugStringA("Failed to retrieve 'alDisable' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alIsEnabled = (LPALISENABLED)GetProcAddress(g_hOpenALDLL, "alIsEnabled"); + if (lpOALFnTable->alIsEnabled == NULL) + { + OutputDebugStringA("Failed to retrieve 'alIsEnabled' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetBoolean = (LPALGETBOOLEAN)GetProcAddress(g_hOpenALDLL, "alGetBoolean"); + if (lpOALFnTable->alGetBoolean == NULL) + { + OutputDebugStringA("Failed to retrieve 'alGetBoolean' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetInteger = (LPALGETINTEGER)GetProcAddress(g_hOpenALDLL, "alGetInteger"); + if (lpOALFnTable->alGetInteger == NULL) + { + OutputDebugStringA("Failed to retrieve 'alGetInteger' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetFloat = (LPALGETFLOAT)GetProcAddress(g_hOpenALDLL, "alGetFloat"); + if (lpOALFnTable->alGetFloat == NULL) + { + OutputDebugStringA("Failed to retrieve 'alGetFloat' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetDouble = (LPALGETDOUBLE)GetProcAddress(g_hOpenALDLL, "alGetDouble"); + if (lpOALFnTable->alGetDouble == NULL) + { + OutputDebugStringA("Failed to retrieve 'alGetDouble' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetBooleanv = (LPALGETBOOLEANV)GetProcAddress(g_hOpenALDLL, "alGetBooleanv"); + if (lpOALFnTable->alGetBooleanv == NULL) + { + OutputDebugStringA("Failed to retrieve 'alGetBooleanv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetIntegerv = (LPALGETINTEGERV)GetProcAddress(g_hOpenALDLL, "alGetIntegerv"); + if (lpOALFnTable->alGetIntegerv == NULL) + { + OutputDebugStringA("Failed to retrieve 'alGetIntegerv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetFloatv = (LPALGETFLOATV)GetProcAddress(g_hOpenALDLL, "alGetFloatv"); + if (lpOALFnTable->alGetFloatv == NULL) + { + OutputDebugStringA("Failed to retrieve 'alGetFloatv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetDoublev = (LPALGETDOUBLEV)GetProcAddress(g_hOpenALDLL, "alGetDoublev"); + if (lpOALFnTable->alGetDoublev == NULL) + { + OutputDebugStringA("Failed to retrieve 'alGetDoublev' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetString = (LPALGETSTRING)GetProcAddress(g_hOpenALDLL, "alGetString"); + if (lpOALFnTable->alGetString == NULL) + { + OutputDebugStringA("Failed to retrieve 'alGetString' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetError = (LPALGETERROR)GetProcAddress(g_hOpenALDLL, "alGetError"); + if (lpOALFnTable->alGetError == NULL) + { + OutputDebugStringA("Failed to retrieve 'alGetError' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alIsExtensionPresent = (LPALISEXTENSIONPRESENT)GetProcAddress(g_hOpenALDLL, "alIsExtensionPresent"); + if (lpOALFnTable->alIsExtensionPresent == NULL) + { + OutputDebugStringA("Failed to retrieve 'alIsExtensionPresent' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetProcAddress = (LPALGETPROCADDRESS)GetProcAddress(g_hOpenALDLL, "alGetProcAddress"); + if (lpOALFnTable->alGetProcAddress == NULL) + { + OutputDebugStringA("Failed to retrieve 'alGetProcAddress' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetEnumValue = (LPALGETENUMVALUE)GetProcAddress(g_hOpenALDLL, "alGetEnumValue"); + if (lpOALFnTable->alGetEnumValue == NULL) + { + OutputDebugStringA("Failed to retrieve 'alGetEnumValue' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alListeneri = (LPALLISTENERI)GetProcAddress(g_hOpenALDLL, "alListeneri"); + if (lpOALFnTable->alListeneri == NULL) + { + OutputDebugStringA("Failed to retrieve 'alListeneri' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alListenerf = (LPALLISTENERF)GetProcAddress(g_hOpenALDLL, "alListenerf"); + if (lpOALFnTable->alListenerf == NULL) + { + OutputDebugStringA("Failed to retrieve 'alListenerf' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alListener3f = (LPALLISTENER3F)GetProcAddress(g_hOpenALDLL, "alListener3f"); + if (lpOALFnTable->alListener3f == NULL) + { + OutputDebugStringA("Failed to retrieve 'alListener3f' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alListenerfv = (LPALLISTENERFV)GetProcAddress(g_hOpenALDLL, "alListenerfv"); + if (lpOALFnTable->alListenerfv == NULL) + { + OutputDebugStringA("Failed to retrieve 'alListenerfv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetListeneri = (LPALGETLISTENERI)GetProcAddress(g_hOpenALDLL, "alGetListeneri"); + if (lpOALFnTable->alGetListeneri == NULL) + { + OutputDebugStringA("Failed to retrieve 'alGetListeneri' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetListenerf =(LPALGETLISTENERF)GetProcAddress(g_hOpenALDLL, "alGetListenerf"); + if (lpOALFnTable->alGetListenerf == NULL) + { + OutputDebugStringA("Failed to retrieve 'alGetListenerf' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetListener3f = (LPALGETLISTENER3F)GetProcAddress(g_hOpenALDLL, "alGetListener3f"); + if (lpOALFnTable->alGetListener3f == NULL) + { + OutputDebugStringA("Failed to retrieve 'alGetListener3f' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetListenerfv = (LPALGETLISTENERFV)GetProcAddress(g_hOpenALDLL, "alGetListenerfv"); + if (lpOALFnTable->alGetListenerfv == NULL) + { + OutputDebugStringA("Failed to retrieve 'alGetListenerfv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGenSources = (LPALGENSOURCES)GetProcAddress(g_hOpenALDLL, "alGenSources"); + if (lpOALFnTable->alGenSources == NULL) + { + OutputDebugStringA("Failed to retrieve 'alGenSources' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alDeleteSources = (LPALDELETESOURCES)GetProcAddress(g_hOpenALDLL, "alDeleteSources"); + if (lpOALFnTable->alDeleteSources == NULL) + { + OutputDebugStringA("Failed to retrieve 'alDeleteSources' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alIsSource = (LPALISSOURCE)GetProcAddress(g_hOpenALDLL, "alIsSource"); + if (lpOALFnTable->alIsSource == NULL) + { + OutputDebugStringA("Failed to retrieve 'alIsSource' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourcei = (LPALSOURCEI)GetProcAddress(g_hOpenALDLL, "alSourcei"); + if (lpOALFnTable->alSourcei == NULL) + { + OutputDebugStringA("Failed to retrieve 'alSourcei' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourcef = (LPALSOURCEF)GetProcAddress(g_hOpenALDLL, "alSourcef"); + if (lpOALFnTable->alSourcef == NULL) + { + OutputDebugStringA("Failed to retrieve 'alSourcef' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSource3f = (LPALSOURCE3F)GetProcAddress(g_hOpenALDLL, "alSource3f"); + if (lpOALFnTable->alSource3f == NULL) + { + OutputDebugStringA("Failed to retrieve 'alSource3f' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourcefv = (LPALSOURCEFV)GetProcAddress(g_hOpenALDLL, "alSourcefv"); + if (lpOALFnTable->alSourcefv == NULL) + { + OutputDebugStringA("Failed to retrieve 'alSourcefv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetSourcei = (LPALGETSOURCEI)GetProcAddress(g_hOpenALDLL, "alGetSourcei"); + if (lpOALFnTable->alGetSourcei == NULL) + { + OutputDebugStringA("Failed to retrieve 'alGetSourcei' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetSourcef = (LPALGETSOURCEF)GetProcAddress(g_hOpenALDLL, "alGetSourcef"); + if (lpOALFnTable->alGetSourcef == NULL) + { + OutputDebugStringA("Failed to retrieve 'alGetSourcef' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetSourcefv = (LPALGETSOURCEFV)GetProcAddress(g_hOpenALDLL, "alGetSourcefv"); + if (lpOALFnTable->alGetSourcefv == NULL) + { + OutputDebugStringA("Failed to retrieve 'alGetSourcefv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourcePlayv = (LPALSOURCEPLAYV)GetProcAddress(g_hOpenALDLL, "alSourcePlayv"); + if (lpOALFnTable->alSourcePlayv == NULL) + { + OutputDebugStringA("Failed to retrieve 'alSourcePlayv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourceStopv = (LPALSOURCESTOPV)GetProcAddress(g_hOpenALDLL, "alSourceStopv"); + if (lpOALFnTable->alSourceStopv == NULL) + { + OutputDebugStringA("Failed to retrieve 'alSourceStopv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourcePlay = (LPALSOURCEPLAY)GetProcAddress(g_hOpenALDLL, "alSourcePlay"); + if (lpOALFnTable->alSourcePlay == NULL) + { + OutputDebugStringA("Failed to retrieve 'alSourcePlay' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourcePause = (LPALSOURCEPAUSE)GetProcAddress(g_hOpenALDLL, "alSourcePause"); + if (lpOALFnTable->alSourcePause == NULL) + { + OutputDebugStringA("Failed to retrieve 'alSourcePause' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourceStop = (LPALSOURCESTOP)GetProcAddress(g_hOpenALDLL, "alSourceStop"); + if (lpOALFnTable->alSourceStop == NULL) + { + OutputDebugStringA("Failed to retrieve 'alSourceStop' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourceRewind = (LPALSOURCEREWIND)GetProcAddress(g_hOpenALDLL, "alSourceRewind"); + if (lpOALFnTable->alSourceRewind == NULL) + { + OutputDebugStringA("Failed to retrieve 'alSourceRewind' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGenBuffers = (LPALGENBUFFERS)GetProcAddress(g_hOpenALDLL, "alGenBuffers"); + if (lpOALFnTable->alGenBuffers == NULL) + { + OutputDebugStringA("Failed to retrieve 'alGenBuffers' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alDeleteBuffers = (LPALDELETEBUFFERS)GetProcAddress(g_hOpenALDLL, "alDeleteBuffers"); + if (lpOALFnTable->alDeleteBuffers == NULL) + { + OutputDebugStringA("Failed to retrieve 'alDeleteBuffers' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alIsBuffer = (LPALISBUFFER)GetProcAddress(g_hOpenALDLL, "alIsBuffer"); + if (lpOALFnTable->alIsBuffer == NULL) + { + OutputDebugStringA("Failed to retrieve 'alIsBuffer' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alBufferData = (LPALBUFFERDATA)GetProcAddress(g_hOpenALDLL, "alBufferData"); + if (lpOALFnTable->alBufferData == NULL) + { + OutputDebugStringA("Failed to retrieve 'alBufferData' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetBufferi = (LPALGETBUFFERI)GetProcAddress(g_hOpenALDLL, "alGetBufferi"); + if (lpOALFnTable->alGetBufferi == NULL) + { + OutputDebugStringA("Failed to retrieve 'alGetBufferi' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alGetBufferf = (LPALGETBUFFERF)GetProcAddress(g_hOpenALDLL, "alGetBufferf"); + if (lpOALFnTable->alGetBufferf == NULL) + { + OutputDebugStringA("Failed to retrieve 'alGetBufferf' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourceQueueBuffers = (LPALSOURCEQUEUEBUFFERS)GetProcAddress(g_hOpenALDLL, "alSourceQueueBuffers"); + if (lpOALFnTable->alSourceQueueBuffers == NULL) + { + OutputDebugStringA("Failed to retrieve 'alSourceQueueBuffers' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alSourceUnqueueBuffers = (LPALSOURCEUNQUEUEBUFFERS)GetProcAddress(g_hOpenALDLL, "alSourceUnqueueBuffers"); + if (lpOALFnTable->alSourceUnqueueBuffers == NULL) + { + OutputDebugStringA("Failed to retrieve 'alSourceUnqueueBuffers' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alDistanceModel = (LPALDISTANCEMODEL)GetProcAddress(g_hOpenALDLL, "alDistanceModel"); + if (lpOALFnTable->alDistanceModel == NULL) + { + OutputDebugStringA("Failed to retrieve 'alDistanceModel' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alDopplerFactor = (LPALDOPPLERFACTOR)GetProcAddress(g_hOpenALDLL, "alDopplerFactor"); + if (lpOALFnTable->alDopplerFactor == NULL) + { + OutputDebugStringA("Failed to retrieve 'alDopplerFactor' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alDopplerVelocity = (LPALDOPPLERVELOCITY)GetProcAddress(g_hOpenALDLL, "alDopplerVelocity"); + if (lpOALFnTable->alDopplerVelocity == NULL) + { + OutputDebugStringA("Failed to retrieve 'alDopplerVelocity' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcGetString = (LPALCGETSTRING)GetProcAddress(g_hOpenALDLL, "alcGetString"); + if (lpOALFnTable->alcGetString == NULL) + { + OutputDebugStringA("Failed to retrieve 'alcGetString' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcGetIntegerv = (LPALCGETINTEGERV)GetProcAddress(g_hOpenALDLL, "alcGetIntegerv"); + if (lpOALFnTable->alcGetIntegerv == NULL) + { + OutputDebugStringA("Failed to retrieve 'alcGetIntegerv' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcOpenDevice = (LPALCOPENDEVICE)GetProcAddress(g_hOpenALDLL, "alcOpenDevice"); + if (lpOALFnTable->alcOpenDevice == NULL) + { + OutputDebugStringA("Failed to retrieve 'alcOpenDevice' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcCloseDevice = (LPALCCLOSEDEVICE)GetProcAddress(g_hOpenALDLL, "alcCloseDevice"); + if (lpOALFnTable->alcCloseDevice == NULL) + { + OutputDebugStringA("Failed to retrieve 'alcCloseDevice' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcCreateContext = (LPALCCREATECONTEXT)GetProcAddress(g_hOpenALDLL, "alcCreateContext"); + if (lpOALFnTable->alcCreateContext == NULL) + { + OutputDebugStringA("Failed to retrieve 'alcCreateContext' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcMakeContextCurrent = (LPALCMAKECONTEXTCURRENT)GetProcAddress(g_hOpenALDLL, "alcMakeContextCurrent"); + if (lpOALFnTable->alcMakeContextCurrent == NULL) + { + OutputDebugStringA("Failed to retrieve 'alcMakeContextCurrent' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcProcessContext = (LPALCPROCESSCONTEXT)GetProcAddress(g_hOpenALDLL, "alcProcessContext"); + if (lpOALFnTable->alcProcessContext == NULL) + { + OutputDebugStringA("Failed to retrieve 'alcProcessContext' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcGetCurrentContext = (LPALCGETCURRENTCONTEXT)GetProcAddress(g_hOpenALDLL, "alcGetCurrentContext"); + if (lpOALFnTable->alcGetCurrentContext == NULL) + { + OutputDebugStringA("Failed to retrieve 'alcGetCurrentContext' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcGetContextsDevice = (LPALCGETCONTEXTSDEVICE)GetProcAddress(g_hOpenALDLL, "alcGetContextsDevice"); + if (lpOALFnTable->alcGetContextsDevice == NULL) + { + OutputDebugStringA("Failed to retrieve 'alcGetContextsDevice' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcSuspendContext = (LPALCSUSPENDCONTEXT)GetProcAddress(g_hOpenALDLL, "alcSuspendContext"); + if (lpOALFnTable->alcSuspendContext == NULL) + { + OutputDebugStringA("Failed to retrieve 'alcSuspendContext' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcDestroyContext = (LPALCDESTROYCONTEXT)GetProcAddress(g_hOpenALDLL, "alcDestroyContext"); + if (lpOALFnTable->alcDestroyContext == NULL) + { + OutputDebugStringA("Failed to retrieve 'alcDestroyContext' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcGetError = (LPALCGETERROR)GetProcAddress(g_hOpenALDLL, "alcGetError"); + if (lpOALFnTable->alcGetError == NULL) + { + OutputDebugStringA("Failed to retrieve 'alcGetError' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcIsExtensionPresent = (LPALCISEXTENSIONPRESENT)GetProcAddress(g_hOpenALDLL, "alcIsExtensionPresent"); + if (lpOALFnTable->alcIsExtensionPresent == NULL) + { + OutputDebugStringA("Failed to retrieve 'alcIsExtensionPresent' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcGetProcAddress = (LPALCGETPROCADDRESS)GetProcAddress(g_hOpenALDLL, "alcGetProcAddress"); + if (lpOALFnTable->alcGetProcAddress == NULL) + { + OutputDebugStringA("Failed to retrieve 'alcGetProcAddress' function address\n"); + return AL_FALSE; + } + lpOALFnTable->alcGetEnumValue = (LPALCGETENUMVALUE)GetProcAddress(g_hOpenALDLL, "alcGetEnumValue"); + if (lpOALFnTable->alcGetEnumValue == NULL) + { + OutputDebugStringA("Failed to retrieve 'alcGetEnumValue' function address\n"); + return AL_FALSE; + } + + return AL_TRUE; +} + +ALvoid UnloadOAL10Library() +{ + // Unload the dll + if (g_hOpenALDLL) + { + FreeLibrary(g_hOpenALDLL); + g_hOpenALDLL = NULL; + } +} \ No newline at end of file diff --git a/Engine/source/sfx/sfxAmbience.cpp b/Engine/source/sfx/sfxAmbience.cpp new file mode 100644 index 000000000..131ddedc3 --- /dev/null +++ b/Engine/source/sfx/sfxAmbience.cpp @@ -0,0 +1,212 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/sfxAmbience.h" +#include "sfx/sfxEnvironment.h" +#include "sfx/sfxTrack.h" +#include "sfx/sfxSystem.h" +#include "sfx/sfxTypes.h" +#include "math/mathTypes.h" +#include "core/stream/bitStream.h" + + +IMPLEMENT_CO_DATABLOCK_V1( SFXAmbience ); + + +ConsoleDocClass( SFXAmbience, + "@brief A datablock that describes an ambient sound space.\n\n" + + "Each ambience datablock captures the properties of a unique ambient sound space. A sound space is comprised of:\n" + + "- an ambient audio track that is played when the listener is inside the space,\n" + "- a reverb environment that is active inside the space, and\n" + "- a number of SFXStates that are activated when entering the space and deactivated when exiting it.\n" + "\n" + + "Each of these properties is optional.\n\n" + + "An important characteristic of ambient audio spaces is that their unique nature is not determined by their location " + "in space but rather by their SFXAmbience datablock. This means that the same SFXAmbience datablock assigned to " + "multiple locations in a level represents the same unique audio space to the sound system.\n\n" + + "This is an important distinction for the ambient sound mixer which will activate a given ambient audio space only " + "once at any one time regardless of how many intersecting audio spaces with the same SFXAmbience datablock assigned " + "the listener may currently be in.\n\n" + + "All SFXAmbience instances are automatically added to the global @c SFXAmbienceSet.\n\n" + + "At the moment, transitions between reverb environments are not blended and different reverb environments from multiple " + "active SFXAmbiences will not be blended together. This will be added in a future version.\n\n" + + "@tsexample\n" + "singleton SFXAmbience( Underwater )\n" + "{\n" + " environment = AudioEnvUnderwater;\n" + " soundTrack = ScubaSoundList;\n" + " states[ 0 ] = AudioLocationUnderwater;\n" + "};\n" + "@endtsexample\n\n" + + "@see SFXEnvironment\n" + "@see SFXTrack\n" + "@see SFXState\n" + "@see LevelInfo::soundAmbience\n" + "@see Zone::soundAmbience\n\n" + "@ref Datablock_Networking\n" + "@ingroup SFX\n" + "@ingroup Datablocks\n" +); + + +SFXAmbience::ChangeSignal SFXAmbience::smChangeSignal; + + +//----------------------------------------------------------------------------- + +SFXAmbience::SFXAmbience() + : mEnvironment( NULL ), + mSoundTrack( NULL ), + mRolloffFactor( 1.f ), + mDopplerFactor( 0.5f ) +{ + dMemset( mState, 0, sizeof( mState ) ); +} + +//----------------------------------------------------------------------------- + +void SFXAmbience::initPersistFields() +{ + addGroup( "Sound" ); + + addField( "environment", TypeSFXEnvironmentName, Offset( mEnvironment, SFXAmbience ), + "Reverb environment active in the ambience zone.\n" + "@ref SFX_reverb" ); + addField( "soundTrack", TypeSFXTrackName, Offset( mSoundTrack, SFXAmbience ), + "Sound track to play in the ambience zone." ); + addField( "rolloffFactor", TypeF32, Offset( mRolloffFactor, SFXAmbience ), + "The rolloff factor to apply to distance-based volume attenuation in this space.\n" + "Defaults to 1.0.\n\n" + "@note This applies to the logarithmic distance model only.\n\n" + "@ref SFXSource_volume" ); + addField( "dopplerFactor", TypeF32, Offset( mDopplerFactor, SFXAmbience ), + "The factor to apply to the doppler affect in this space.\n" + "Defaults to 0.5.\n\n" + "@ref SFXSource_doppler" ); + addField( "states", TypeSFXStateName, Offset( mState, SFXAmbience ), + MaxStates, + "States to activate when the ambient zone is entered.\n" + "When the ambient sound state is entered, all states associated with the state will " + "be activated (given that they are not disabled) and deactivated when the space " + "is exited again." ); + + endGroup( "Sound" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +bool SFXAmbience::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + Sim::getSFXAmbienceSet()->addObject( this ); + + return true; +} + +//----------------------------------------------------------------------------- + +bool SFXAmbience::preload( bool server, String& errorStr ) +{ + if( !Parent::preload( server, errorStr ) ) + return false; + + validate(); + + // Resolve datablocks on client. + + if( !server ) + { + if( !sfxResolve( &mEnvironment, errorStr ) ) + return false; + + if( !sfxResolve( &mSoundTrack, errorStr ) ) + return false; + + for( U32 i = 0; i < MaxStates; ++ i ) + if( !sfxResolve( &mState[ i ], errorStr ) ) + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- + +void SFXAmbience::packData( BitStream* stream ) +{ + Parent::packData( stream ); + + sfxWrite( stream, mEnvironment ); + sfxWrite( stream, mSoundTrack ); + + stream->write( mRolloffFactor ); + stream->write( mDopplerFactor ); + + for( U32 i = 0; i < MaxStates; ++ i ) + sfxWrite( stream, mState[ i ] ); +} + +//----------------------------------------------------------------------------- + +void SFXAmbience::unpackData( BitStream* stream ) +{ + Parent::unpackData( stream ); + + sfxRead( stream, &mEnvironment ); + sfxRead( stream, &mSoundTrack ); + + stream->read( &mRolloffFactor ); + stream->read( &mDopplerFactor ); + + for( U32 i = 0; i < MaxStates; ++ i ) + sfxRead( stream, &mState[ i ] ); +} + +//----------------------------------------------------------------------------- + +void SFXAmbience::inspectPostApply() +{ + Parent::inspectPostApply(); + + validate(); + + smChangeSignal.trigger( this ); +} + +//----------------------------------------------------------------------------- + +void SFXAmbience::validate() +{ +} diff --git a/Engine/source/sfx/sfxAmbience.h b/Engine/source/sfx/sfxAmbience.h new file mode 100644 index 000000000..22d91be4e --- /dev/null +++ b/Engine/source/sfx/sfxAmbience.h @@ -0,0 +1,120 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXAMBIENCE_H_ +#define _SFXAMBIENCE_H_ + +#ifndef _SIMDATABLOCK_H_ + #include "console/simDatablock.h" +#endif +#ifndef _CONSOLETYPES_H_ + #include "console/consoleTypes.h" +#endif +#ifndef _TSIGNAL_H_ + #include "core/util/tSignal.h" +#endif + + +class SFXEnvironment; +class SFXTrack; +class SFXState; + + +/// Datablock for describing an ambient audio space. +/// +class SFXAmbience : public SimDataBlock +{ + public: + + typedef SimDataBlock Parent; + typedef Signal< void( SFXAmbience* ) > ChangeSignal; + + enum + { + /// Maximum number of states that can be tied to an ambient space. + MaxStates = 4 + }; + + protected: + + /// Doppler shift factor for this space. + F32 mDopplerFactor; + + /// Rolloff factor for this space. Only applies to logarithmic distance model. + F32 mRolloffFactor; + + /// Sound track to play when inside the ambient space. + SFXTrack* mSoundTrack; + + /// Reverb environment to apply when inside the ambient space. + SFXEnvironment* mEnvironment; + + /// SFXStates to activate when in this ambient space. + SFXState* mState[ MaxStates ]; + + /// + static ChangeSignal smChangeSignal; + + public: + + SFXAmbience(); + + /// Ensure all properties of this ambience adhere to their value contraints. + void validate(); + + /// Return the rolloff factor to apply to distance-based volume attenuation in this space (logarithmic distance model only). + F32 getRolloffFactor() const { return mRolloffFactor; } + + /// Return the doppler shift factor to apply in this space. + F32 getDopplerFactor() const { return mDopplerFactor; } + + /// Return the reverb environment of the ambient space. + SFXEnvironment* getEnvironment() const { return mEnvironment; } + + /// Return the ambient soundtrack of this ambient space. + SFXTrack* getSoundTrack() const { return mSoundTrack; } + + /// Return the given state bound to this ambient space. + SFXState* getState( U32 i ) const + { + AssertFatal( i < MaxStates, "SFXState::getState() - index out of range" ); + return mState[ i ]; + } + + /// + static ChangeSignal& getChangeSignal() { return smChangeSignal; } + + // SimDataBlock. + virtual bool onAdd(); + virtual void packData( BitStream* stream ); + virtual void unpackData( BitStream* stream ); + virtual bool preload( bool server, String& errorStr ); + virtual void inspectPostApply(); + + static void initPersistFields(); + + DECLARE_CONOBJECT( SFXAmbience ); + DECLARE_CATEGORY( "SFX" ); + DECLARE_DESCRIPTION( "An ambient sound environment." ); +}; + +#endif // !_SFXAMBIENCE_H_ diff --git a/Engine/source/sfx/sfxBuffer.cpp b/Engine/source/sfx/sfxBuffer.cpp new file mode 100644 index 000000000..ebcd09958 --- /dev/null +++ b/Engine/source/sfx/sfxBuffer.cpp @@ -0,0 +1,291 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/sfxBuffer.h" +#include "sfx/sfxVoice.h" +#include "sfx/sfxDescription.h" +#include "sfx/sfxInternal.h" + + +//#define DEBUG_SPEW + + +Signal< void( SFXBuffer* ) > SFXBuffer::smBufferDestroyedSignal; + + +//----------------------------------------------------------------------------- + +SFXBuffer::SFXBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description, bool createAsyncState ) + : mStatus( STATUS_Null ), + mIsStreaming( description->mIsStreaming ), + mFormat( stream->getFormat() ), + mDuration( stream->getDuration() ), + mUniqueVoice( NULL ), + mIsDead( false ), + mIsLooping( description->mIsLooping ), + mIsUnique( description->mIsStreaming ) +{ + using namespace SFXInternal; + + if( createAsyncState ) + { + U32 packetLength = description->mStreamPacketSize; + U32 readAhead = description->mStreamReadAhead; + + ThreadSafeRef< SFXStream > streamRef( stream ); + mAsyncState = new AsyncState( + new SFXAsyncStream + ( streamRef, mIsStreaming, packetLength, readAhead, + mIsStreaming ? description->mIsLooping : false ) ); + } +} + +//----------------------------------------------------------------------------- + +SFXBuffer::SFXBuffer( SFXDescription* description ) + : mStatus( STATUS_Ready ), + mIsStreaming( false ), // Not streaming through our system. + mDuration( 0 ), // Must be set by subclass. + mUniqueVoice( NULL ), + mIsDead( false ), + mIsLooping( description->mIsLooping ), + mIsUnique( false ) // Must be set by subclass. +{ +} + +//----------------------------------------------------------------------------- + +SFXBuffer::~SFXBuffer() +{ + smBufferDestroyedSignal.trigger( this ); +} + +//----------------------------------------------------------------------------- + +void SFXBuffer::load() +{ + if( getStatus() == STATUS_Null ) + { + AssertFatal( mAsyncState != NULL, "SFXBuffer::load() - no async state!" ); + + _setStatus( STATUS_Loading ); + SFXInternal::UPDATE_LIST().add( this ); + mAsyncState->mStream->start(); + } +} + +//----------------------------------------------------------------------------- + +bool SFXBuffer::update() +{ + using namespace SFXInternal; + + if( isDead() ) + { + // Buffer requested to finish its async operations. + // Kill our async state and put us on the dead buffer list. + + mAsyncState->mStream->stop(); + mAsyncState = NULL; + gDeadBufferList.pushFront( this ); + return false; + } + else if( isAtEnd() && isStreaming() ) + { + // Streaming buffers remain in the update loop even if they + // have played in full to allow for stream seeks. + return true; + } + + AssertFatal( mAsyncState != NULL, "SFXBuffer::update() - async state has already been released" ); + + bool needFurtherUpdates = true; + if( !isStreaming() ) + { + // Not a streaming buffer. If the async stream has its data + // ready, we load it and finish up on our async work. + + SFXStreamPacket* packet; + while( mAsyncState->mStream->read( &packet, 1 ) ) + { + bool isLast = packet->mIsLast; + + // Write packet data into buffer. + write( &packet, 1 ); + packet = NULL; + + if( isLast ) + { + // Release async state. + mAsyncState = NULL; + + // Once loaded, non-streaming buffers disappear from the SFX + // update thread. + needFurtherUpdates = false; + + // Signal that we are ready. + _setStatus( STATUS_Ready ); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXBuffer] Buffer ready" ); + #endif + + break; + } + } + } + else + { + // A streaming buffer. + // + // If we don't have a queue, construct one now. Note that when doing + // a stream seek on us, SFXVoice will drop our async stream and queue. + // Work on local copies of the pointers to allow having the async state + // be switched in parallel. + // + // Note that despite us being a streamed buffer, our unique voice may + // not yet have been assigned to us. + + AsyncStatePtr state = mAsyncState; + if( !state->mQueue && !mUniqueVoice.isNull() ) + { + // Make sure we have no data currently submitted to the device. + // This will stop and discard an outdated feed if we've been + // switching streams. + + _setStatus( STATUS_Loading ); + _flush(); + + // Create a new queue. + + state->mQueue = new SFXAsyncQueue( mUniqueVoice, this, mIsLooping ); + } + + // Check the queue. + + if( state->mQueue != NULL ) + { + // Feed the queue, if necessary and possible. + + while( state->mQueue->needPacket() ) + { + // Try to read a packet. + + SFXStreamPacket* packet; + if( !state->mStream->read( &packet, 1 ) ) + break; + + // Submit it. + + state->mQueue->submitPacket( packet, packet->getSampleCount() ); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXBuffer] Stream packet queued" ); + #endif + + // Signal that the buffer has data ready. + + _setStatus( STATUS_Ready ); + } + + // Detect buffer underrun and end-of-stream. + + if( !isReady() && state->mQueue->isEmpty() ) + { + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXBuffer] Stream blocked" ); + #endif + + _setStatus( STATUS_Blocked ); + } + else if( state->mQueue->isAtEnd() ) + { + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXBuffer] Stream at end" ); + #endif + + _setStatus( STATUS_AtEnd ); + _flush(); + } + } + } + + return needFurtherUpdates; +} + +//----------------------------------------------------------------------------- + +void SFXBuffer::destroySelf() +{ + AssertFatal( !isDead(), "SFXBuffer::destroySelf() - buffer already dead" ); + + mIsDead = true; + if( !mAsyncState ) + { + // Easy way. This buffer has finished all its async + // processing, so we can just kill it. + + delete this; + } + else + { + // Hard way. We will have to make the buffer finish + // all its concurrent stuff, so we mark it dead, make sure + // to see an update, and then wait for the buffer to surface + // on the dead buffer list. + + SFXInternal::TriggerUpdate(); + } +} + +//----------------------------------------------------------------------------- + +void SFXBuffer::_setStatus( Status status ) +{ + if( mStatus != status ) + { + mOnStatusChange.trigger( this, status ); + mStatus = status; + } +} + +//----------------------------------------------------------------------------- + +SFXBuffer::AsyncState::AsyncState() + : mQueue( NULL ) +{ +} + +//----------------------------------------------------------------------------- + +SFXBuffer::AsyncState::AsyncState( SFXInternal::SFXAsyncStream* stream ) + : mStream( stream ), mQueue( NULL ) +{ +} + +//----------------------------------------------------------------------------- + +SFXBuffer::AsyncState::~AsyncState() +{ + if( mQueue ) + SAFE_DELETE( mQueue ); +} diff --git a/Engine/source/sfx/sfxBuffer.h b/Engine/source/sfx/sfxBuffer.h new file mode 100644 index 000000000..5c2307022 --- /dev/null +++ b/Engine/source/sfx/sfxBuffer.h @@ -0,0 +1,236 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXBUFFER_H_ +#define _SFXBUFFER_H_ + +#ifndef _REFBASE_H_ +#include "core/util/refBase.h" +#endif + +#ifndef _TSIGNAL_H_ +#include "core/util/tSignal.h" +#endif + +#ifndef _TSTREAM_H_ +#include "core/stream/tStream.h" +#endif + +#ifndef _SFXCOMMON_H_ +#include "sfx/sfxCommon.h" +#endif + +#ifndef _THREADSAFEREFCOUNT_H_ +#include "platform/threads/threadSafeRefCount.h" +#endif + + +class SFXStream; +class SFXDescription; +class SFXVoice; + +namespace SFXInternal { + class SFXStreamPacket; + class SFXAsyncStream; + class SFXAsyncQueue; + void PurgeDeadBuffers(); +} + + +/// The buffer interface hides the details of how the device holds sound data for playback. +/// +/// A sound buffer may either be loaded once completely and then played as needed or it may +/// be progressively streamed from an SFXStream. In the latter case, there can only be a single +/// voice tied to the buffer. +/// +/// @note SFXDevice is the last instance when it comes to ownership +/// of SFXBuffers. If the SFXDevice goes away, it will take all +/// SFXBuffers with it, regardless of whether there are still strong +/// refs to it. Use StrongWeakRefPtrs to keep pointers to +/// SFXBuffers! +/// +/// @see SFXStream +class SFXBuffer : public StrongRefBase, + public IPolled, // SFXBuffers are periodically updated on the SFX thread. + public IOutputStream< SFXInternal::SFXStreamPacket* > // Interface for writing sound data to the buffer. +{ + friend class SFXVoice; // mUniqueVoice + friend void SFXInternal::PurgeDeadBuffers(); // dtor + + public: + + typedef void Parent; + + /// Status indicators for sound buffers. + enum Status + { + STATUS_Null, ///< Initial state. + STATUS_Loading, ///< Buffer has requested data and is waiting for queue to fill up. + STATUS_Ready, ///< Playback queue is fed and ready (non-stream buffers will stop at this state). + STATUS_Blocked, ///< Queue is starved and playback thus held until further data is available (streaming buffers only). + STATUS_AtEnd, ///< Buffer has read all its streaming data (streaming buffers only). + }; + + /// This signal is triggered from SFXBuffer's destructor so the sound system + /// can keep track of buffers being released on the device. + static Signal< void( SFXBuffer* ) > smBufferDestroyedSignal; + + protected: + + typedef ThreadSafeRef< SFXInternal::SFXAsyncStream > SFXAsyncStreamPtr; + typedef SFXInternal::SFXAsyncQueue* SFXAsyncQueuePtr; + + /// Encapsulates the async I/O state of the sound buffer. + struct AsyncState : public ThreadSafeRefCount< AsyncState > + { + /// The sound packet stream. + SFXAsyncStreamPtr mStream; + + /// The packet queue that feeds into the actual device buffer. + /// Only used for streaming buffers; non-streaming buffers directly receive + /// and upload sound packets without queuing. + SFXAsyncQueuePtr mQueue; + + AsyncState(); + AsyncState( SFXInternal::SFXAsyncStream* stream ); + ~AsyncState(); + }; + + typedef ThreadSafeRef< AsyncState > AsyncStatePtr; + + /// Create a new buffer from @a stream using the parameters in @a description. + /// + /// @param stream Sound stream from which to read sound data into the buffer. + /// @param description Sound setup description. + /// @param createAsyncState If true, the asynchronous loading state for the buffer will be set up + /// in the constructor. This is mainly useful for the null device which creates dummy buffers that + /// do not need the async state to be in place. All other buffers do. + SFXBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description, bool createAsyncState = true ); + + /// Create a buffer with just a description. This is used by devices who fully take over loading + /// and streaming. + SFXBuffer( SFXDescription* description ); + + virtual ~SFXBuffer(); + + /// The buffer readiness status. + Status mStatus; + + /// The sound sample format used by the buffer. + SFXFormat mFormat; + + /// Total playback time of the associated sound stream in milliseconds. + /// @note For streaming buffers, this will not correspond to the actual + /// playtime of the device buffer. + U32 mDuration; + + /// If true, this is a continuously streaming buffer. + bool mIsStreaming; + + /// For streaming buffers, tells whether the source stream loops. + bool mIsLooping; + + /// If true, this buffer can only have a single SFXVoice attached. + bool mIsUnique; + + /// If true, the buffer is dead and will be deleted. Can't be in status + /// for synchronization reasons. + bool mIsDead; + + /// Pointer to structure keeping the asynchronous I/O state of the buffer. + /// For non-streaming buffers, this is released as soon as all data is loaded. + /// + /// To allow seeking in streaming buffers even after playback has ended, + /// we do not release the async state of these buffers until the buffer is + /// actually released itself. This allows to always access the associated + /// input stream. + /// + /// @note For devices that handle loading/streaming on their own, this will + /// not be set. + AsyncStatePtr mAsyncState; + + /// If this is a unique buffer (i.e. a streaming buffer), then this holds + /// the reference to the unique voice. + StrongWeakRefPtr< SFXVoice > mUniqueVoice; + + /// Set the buffer status and trigger mOnStatusChange if the status changes. + /// @note Called on both the SFX update thread and the main thread. + void _setStatus( Status status ); + + /// Flush all queue state for this buffer on the device. + /// @note Called on the SFX update thread. + virtual void _flush() = 0; + + public: + + /// Signal that is triggered when the buffer status changes. + /// @note This signal is triggered on the same thread that the buffer update + /// code runs on. + Signal< void( SFXBuffer* buffer, Status newStatus ) > mOnStatusChange; + + /// @return The current buffer loading/queue status. + Status getStatus() const { return mStatus; } + + /// @return The sound sample format used by the buffer. + const SFXFormat& getFormat() const { return mFormat; } + + /// @return The total playback time of the buffer in milliseconds. + U32 getDuration() const { return mDuration; } + + /// @return The total number of samples in the buffer. + U32 getNumSamples() const { return getFormat().getSampleCount( mDuration ); } + + /// @return The number of bytes consumed by this sound buffer. + virtual U32 getMemoryUsed() const { return 0; } + + /// @return True if the buffer does continuous sound streaming. + bool isStreaming() const { return mIsStreaming; } + + /// @return True if the buffer is pending deletion. + bool isDead() const { return mIsDead; } + + /// @return True if the buffer's packet queue is loaded and ready for playback. + bool isReady() const { return ( getStatus() == STATUS_Ready ); } + + /// @return True if the buffer's packet queue is still loading. + bool isLoading() const { return ( getStatus() == STATUS_Loading ); } + + /// @return True if the buffer's packet queue has been starved and is waiting for data. + bool isBlocked() const { return ( getStatus() == STATUS_Blocked ); } + + /// @return True if the buffer has exhausted its source stream + bool isAtEnd() const { return ( getStatus() == STATUS_AtEnd ); } + + /// @return True if the buffer can only have a single SFXVoice attached to it. + bool isUnique() const { return mIsUnique; } + + /// Start the async request chain for the buffer. + void load(); + + // IPolled. + virtual bool update(); + + // WeakRefBase. + virtual void destroySelf(); +}; + +#endif // _SFXBUFFER_H_ diff --git a/Engine/source/sfx/sfxCommon.h b/Engine/source/sfx/sfxCommon.h new file mode 100644 index 000000000..587672ca0 --- /dev/null +++ b/Engine/source/sfx/sfxCommon.h @@ -0,0 +1,594 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXCOMMON_H_ +#define _SFXCOMMON_H_ + +#ifndef _PLATFORM_H_ + #include "platform/platform.h" +#endif +#ifndef _MMATHFN_H_ + #include "math/mMathFn.h" +#endif +#ifndef _MRANDOM_H_ + #include "math/mRandom.h" +#endif +#ifndef _MMATRIX_H_ + #include "math/mMatrix.h" +#endif +#ifndef _MPOINT3_H_ + #include "math/mPoint3.h" +#endif +#ifndef _TYPETRAITS_H_ + #include "platform/typetraits.h" +#endif +#ifndef _DYNAMIC_CONSOLETYPES_H_ + #include "console/dynamicTypes.h" +#endif + + + +class SFXEnvironment; +class SFXPlayList; + + +//----------------------------------------------------------------------------- +// SFXStatus. +//----------------------------------------------------------------------------- + + +/// The sound playback state. +enum SFXStatus +{ + /// Initial state; no operation yet performed on sound. + SFXStatusNull, + + /// Sound is playing. + SFXStatusPlaying, + + /// Sound has been stopped. + SFXStatusStopped, + + /// Sound is paused. + SFXStatusPaused, + + /// Sound stream is starved and playback blocked. + SFXStatusBlocked, + + /// Temporary state while transitioning to another state. This is used when multiple + /// threads concurrently maintain a status and need to perform a sequence of actions before + /// being able to fully go from a previous to a new current state. In this case, the + /// transition state marks the status as being under update on another thread. + /// + /// @note Not all places that use SFXStatus actually use this state. + SFXStatusTransition, +}; + +DefineEnumType( SFXStatus ); + + +inline const char* SFXStatusToString( SFXStatus status ) +{ + switch ( status ) + { + case SFXStatusPlaying: return "playing"; + case SFXStatusStopped: return "stopped"; + case SFXStatusPaused: return "paused"; + case SFXStatusBlocked: return "blocked"; + case SFXStatusTransition: return "transition"; + + case SFXStatusNull: + default: ; + } + + return "null"; +} + + +//----------------------------------------------------------------------------- +// SFXChannel. +//----------------------------------------------------------------------------- + + +/// Animatable channels in the SFX system. +enum SFXChannel +{ + SFXChannelVolume, + SFXChannelPitch, + SFXChannelPriority, + SFXChannelPositionX, + SFXChannelPositionY, + SFXChannelPositionZ, + SFXChannelRotationX, + SFXChannelRotationY, + SFXChannelRotationZ, + SFXChannelVelocityX, + SFXChannelVelocityY, + SFXChannelVelocityZ, + SFXChannelMinDistance, + SFXChannelMaxDistance, + SFXChannelConeInsideAngle, + SFXChannelConeOutsideAngle, + SFXChannelConeOutsideVolume, + SFXChannelCursor, + SFXChannelStatus, + SFXChannelUser0, + SFXChannelUser1, + SFXChannelUser2, + SFXChannelUser3, + + /// Total number of animatable channels. + SFX_NUM_CHANNELS +}; + +DefineEnumType( SFXChannel ); + + +//----------------------------------------------------------------------------- +// SFXDistanceModel. +//----------------------------------------------------------------------------- + + +/// Rolloff curve used for distance volume attenuation of 3D sounds. +enum SFXDistanceModel +{ + SFXDistanceModelLinear, ///< Volume decreases linearly from min to max where it reaches zero. + SFXDistanceModelLogarithmic, ///< Volume halves every min distance steps starting from min distance; attenuation stops at max distance. +}; + +DefineEnumType( SFXDistanceModel ); + +/// Compute the distance attenuation based on the given distance model. +/// +/// @param minDistance Reference distance; attenuation starts here. +/// @param maxDistance +/// @param distance Actual distance of sound from listener. +/// @param volume Unattenuated volume. +/// @param rolloffFactor Rolloff curve scale factor. +/// +/// @return The attenuated volume. +inline F32 SFXDistanceAttenuation( SFXDistanceModel model, F32 minDistance, F32 maxDistance, F32 distance, F32 volume, F32 rolloffFactor ) +{ + F32 gain = 1.0f; + + switch( model ) + { + case SFXDistanceModelLinear: + + distance = getMax( distance, minDistance ); + distance = getMin( distance, maxDistance ); + + gain = ( 1 - ( distance - minDistance ) / ( maxDistance - minDistance ) ); + break; + + case SFXDistanceModelLogarithmic: + + distance = getMax( distance, minDistance ); + distance = getMin( distance, maxDistance ); + + gain = minDistance / ( minDistance + rolloffFactor * ( distance - minDistance ) ); + break; + + } + + return ( volume * gain ); +} + + +//----------------------------------------------------------------------------- +// SFXFormat. +//----------------------------------------------------------------------------- + + +/// This class defines the various types of sound data that may be +/// used in the sound system. +/// +/// Unlike with most sound APIs, we consider each sample point to comprise +/// all channels in a sound stream rather than only one value for a single +/// channel. +class SFXFormat +{ + protected: + + /// The number of sound channels in the data. + U8 mChannels; + + /// The number of bits per sound sample. + U8 mBitsPerSample; + + /// The frequency in samples per second. + U32 mSamplesPerSecond; + + public: + + SFXFormat( U8 channels = 0, + U8 bitsPerSample = 0, + U32 samplesPerSecond = 0 ) + : mChannels( channels ), + mSamplesPerSecond( samplesPerSecond ), + mBitsPerSample( bitsPerSample ) + {} + + /// Copy constructor. + SFXFormat( const SFXFormat &format ) + : mChannels( format.mChannels ), + mBitsPerSample( format.mBitsPerSample ), + mSamplesPerSecond( format.mSamplesPerSecond ) + {} + + public: + + /// Sets the format. + void set( U8 channels, + U8 bitsPerSample, + U32 samplesPerSecond ) + { + mChannels = channels; + mBitsPerSample = bitsPerSample; + mSamplesPerSecond = samplesPerSecond; + } + + /// Comparision between formats. + bool operator == ( const SFXFormat& format ) const + { + return mChannels == format.mChannels && + mBitsPerSample == format.mBitsPerSample && + mSamplesPerSecond == format.mSamplesPerSecond; + } + + /// Returns the number of sound channels. + U8 getChannels() const { return mChannels; } + + /// Returns true if there is a single sound channel. + bool isMono() const { return mChannels == 1; } + + /// Is true if there are two sound channels. + bool isStereo() const { return mChannels == 2; } + + /// Is true if there are more than two sound channels. + bool isMultiChannel() const { return mChannels > 2; } + + /// + U32 getSamplesPerSecond() const { return mSamplesPerSecond; } + + /// The bits of data per channel. + U8 getBitsPerChannel() const { return mBitsPerSample / mChannels; } + + /// The number of bytes of data per channel. + U8 getBytesPerChannel() const { return getBitsPerChannel() / 8; } + + /// The number of bits per sound sample. + U8 getBitsPerSample() const { return mBitsPerSample; } + + /// The number of bytes of data per sample. + /// @note Be aware that this comprises all channels. + U8 getBytesPerSample() const { return mBitsPerSample / 8; } + + /// Returns the duration from the sample count. + U32 getDuration( U32 samples ) const + { + // Use 64bit types to avoid overflow during division. + return ( (U64)samples * (U64)1000 ) / (U64)mSamplesPerSecond; + } + + /// + U32 getSampleCount( U32 ms ) const + { + return U64( mSamplesPerSecond ) * U64( ms ) / U64( 1000 ); + } + + /// Returns the data length in bytes. + U32 getDataLength( U32 ms ) const + { + U32 bytes = ( ( (U64)ms * (U64)mSamplesPerSecond ) * (U64)getBytesPerSample() ) / (U64)1000; + return bytes; + } +}; + + +//----------------------------------------------------------------------------- +// SFXReverb. +//----------------------------------------------------------------------------- + + +/// Reverb environment properties. +/// +/// @note A given device may not implement all properties. +class SFXReverbProperties +{ + public: + + typedef void Parent; + + F32 mEnvSize; + F32 mEnvDiffusion; + S32 mRoom; + S32 mRoomHF; + S32 mRoomLF; + F32 mDecayTime; + F32 mDecayHFRatio; + F32 mDecayLFRatio; + S32 mReflections; + F32 mReflectionsDelay; + F32 mReflectionsPan[ 3 ]; + S32 mReverb; + F32 mReverbDelay; + F32 mReverbPan[ 3 ]; + F32 mEchoTime; + F32 mEchoDepth; + F32 mModulationTime; + F32 mModulationDepth; + F32 mAirAbsorptionHF; + F32 mHFReference; + F32 mLFReference; + F32 mRoomRolloffFactor; + F32 mDiffusion; + F32 mDensity; + S32 mFlags; + + SFXReverbProperties() + : mEnvSize( 7.5f ), + mEnvDiffusion( 1.0f ), + mRoom( -1000 ), + mRoomHF( -100 ), + mRoomLF( 0 ), + mDecayTime( 1.49f ), + mDecayHFRatio( 0.83f ), + mDecayLFRatio( 1.0f ), + mReflections( -2602 ), + mReflectionsDelay( 0.007f ), + mReverb( 200 ), + mReverbDelay( 0.011f ), + mEchoTime( 0.25f ), + mEchoDepth( 0.0f ), + mModulationTime( 0.25f ), + mModulationDepth( 0.0f ), + mAirAbsorptionHF( -5.0f ), + mHFReference( 5000.0f ), + mLFReference( 250.0f ), + mRoomRolloffFactor( 0.0f ), + mDiffusion( 100.0f ), + mDensity( 100.0f ), + mFlags( 0 ) + { + mReflectionsPan[ 0 ] = 0.0f; + mReflectionsPan[ 1 ] = 0.0f; + mReflectionsPan[ 2 ] = 0.0f; + + mReverbPan[ 0 ] = 0.0f; + mReverbPan[ 1 ] = 0.0f; + mReverbPan[ 2 ] = 0.0f; + } + + void validate() + { + mEnvSize = mClampF( mEnvSize, 1.0f, 100.0f ); + mEnvDiffusion = mClampF( mEnvDiffusion, 0.0f, 1.0f ); + mRoom = mClamp( mRoom, -10000, 0 ); + mRoomHF = mClamp( mRoomHF, -10000, 0 ); + mRoomLF = mClamp( mRoomLF, -10000, 0 ); + mDecayTime = mClampF( mDecayTime, 0.1f, 20.0f ); + mDecayHFRatio = mClampF( mDecayHFRatio, 0.1f, 2.0f ); + mDecayLFRatio = mClampF( mDecayLFRatio, 0.1f, 2.0f ); + mReflections = mClamp( mReflections, -10000, 1000 ); + mReflectionsDelay = mClampF( mReflectionsDelay, 0.0f, 0.3f ); + mReverb = mClamp( mReverb, -10000, 2000 ); + mReverbDelay = mClampF( mReverbDelay, 0.0f, 0.1f ); + mEchoTime = mClampF( mEchoTime, 0.075f, 0.25f ); + mEchoDepth = mClampF( mEchoDepth, 0.0f, 1.0f ); + mModulationTime = mClampF( mModulationTime, 0.04f, 4.0f ); + mModulationDepth = mClampF( mModulationDepth, 0.0f, 1.0f ); + mAirAbsorptionHF = mClampF( mAirAbsorptionHF, -100.0f, 0.0f ); + mHFReference = mClampF( mHFReference, 1000.0f, 20000.0f ); + mLFReference = mClampF( mLFReference, 20.0f, 1000.0f ); + mRoomRolloffFactor = mClampF( mRoomRolloffFactor, 0.0f, 10.0f ); + mDiffusion = mClampF( mDiffusion, 0.0f, 100.0f ); + mDensity = mClampF( mDensity, 0.0f, 100.0f ); + } +}; + + +//----------------------------------------------------------------------------- +// SFXSoundReverbProperties. +//----------------------------------------------------------------------------- + + +/// Sound reverb properties. +/// +/// @note A given SFX device may not implement all properties. +class SFXSoundReverbProperties +{ + public: + + typedef void Parent; + + S32 mDirect; + S32 mDirectHF; + S32 mRoom; + S32 mRoomHF; + S32 mObstruction; + F32 mObstructionLFRatio; + S32 mOcclusion; + F32 mOcclusionLFRatio; + F32 mOcclusionRoomRatio; + F32 mOcclusionDirectRatio; + S32 mExclusion; + F32 mExclusionLFRatio; + S32 mOutsideVolumeHF; + F32 mDopplerFactor; + F32 mRolloffFactor; + F32 mRoomRolloffFactor; + F32 mAirAbsorptionFactor; + S32 mFlags; + + SFXSoundReverbProperties() + : mDirect( 0 ), + mDirectHF( 0 ), + mRoom( 0 ), + mRoomHF( 0 ), + mObstruction( 0 ), + mObstructionLFRatio( 0.0f ), + mOcclusion( 0 ), + mOcclusionLFRatio( 0.25f ), + mOcclusionRoomRatio( 1.5f ), + mOcclusionDirectRatio( 1.0f ), + mExclusion( 0 ), + mExclusionLFRatio( 1.0f ), + mOutsideVolumeHF( 0 ), + mDopplerFactor( 0.0f ), + mRolloffFactor( 0.0f ), + mRoomRolloffFactor( 0.0f ), + mAirAbsorptionFactor( 1.0f ), + mFlags( 0 ) + { + } + + void validate() + { + mDirect = mClamp( mDirect, -10000, 1000 ); + mDirectHF = mClamp( mDirectHF, -10000, 0 ); + mRoom = mClamp( mRoom, -10000, 1000 ); + mRoomHF = mClamp( mRoomHF, -10000, 0 ); + mObstruction = mClamp( mObstruction, -10000, 0 ); + mObstructionLFRatio = mClampF( mObstructionLFRatio, 0.0f, 1.0f ); + mOcclusion = mClamp( mOcclusion, -10000, 0 ); + mOcclusionLFRatio = mClampF( mOcclusionLFRatio, 0.0f, 1.0f ); + mOcclusionRoomRatio = mClampF( mOcclusionRoomRatio, 0.0f, 10.0f ); + mOcclusionDirectRatio= mClampF( mOcclusionDirectRatio, 0.0f, 10.0f ); + mExclusion = mClamp( mExclusion, -10000, 0 ); + mExclusionLFRatio = mClampF( mExclusionLFRatio, 0.0f, 1.0f ); + mOutsideVolumeHF = mClamp( mOutsideVolumeHF, -10000, 0 ); + mDopplerFactor = mClampF( mDopplerFactor, 0.0f, 10.0f ); + mRolloffFactor = mClampF( mRolloffFactor, 0.0f, 10.0f ); + mRoomRolloffFactor = mClampF( mRoomRolloffFactor, 0.0f, 10.0f ); + mAirAbsorptionFactor = mClampF( mAirAbsorptionFactor, 0.0f, 10.0f ); + } +}; + + +//----------------------------------------------------------------------------- +// SFXListenerProperties. +//----------------------------------------------------------------------------- + + +/// +class SFXListenerProperties +{ + public: + + typedef void Parent; + + /// Position and orientation of the listener. + MatrixF mTransform; + + /// + Point3F mVelocity; + + SFXListenerProperties() + : mTransform( true ), + mVelocity( 0.0f, 0.0f, 0.0f ) {} + + SFXListenerProperties( const MatrixF& transform, const Point3F& velocity ) + : mTransform( transform ), + mVelocity( velocity ) {} + + /// + const MatrixF& getTransform() const { return mTransform; } + MatrixF& getTransform() { return mTransform; } + + /// + const Point3F& getVelocity() const { return mVelocity; } + Point3F& getVelocity() { return mVelocity; } +}; + + +//----------------------------------------------------------------------------- +// SFXMaterialProperties. +//----------------------------------------------------------------------------- + + +/// +class SFXMaterialProperties +{ + public: + + typedef void Parent; + + /// + bool mDoubleSided; + + /// + F32 mDirectOcclusion; + + /// + F32 mReverbOcclusion; + + SFXMaterialProperties() + : mDoubleSided( false ), + mDirectOcclusion( 0.5f ), + mReverbOcclusion( 0.5f ) {} + + void validate() + { + mDirectOcclusion = mClampF( mDirectOcclusion, 0.0f, 1.0f ); + mReverbOcclusion = mClampF( mReverbOcclusion, 0.0f, 1.0f ); + } +}; + + +//----------------------------------------------------------------------------- +// SFXVariantFloat. +//----------------------------------------------------------------------------- + + +/// An array of float values with optional random variances. +template< int NUM_VALUES > +struct SFXVariantFloat +{ + /// Base value. + F32 mValue[ NUM_VALUES ]; + + /// Variance of value. Final value will be + /// + /// mClampF( randF( mValue + mVariance[ 0 ], mValue + mVariance[ 1 ] ), min, max ) + /// + /// with min and max being dependent on the context of the value. + F32 mVariance[ NUM_VALUES ][ 2 ]; + + F32 getValue( U32 index = 0, F32 min = TypeTraits< F32 >::MIN, F32 max = TypeTraits< F32 >::MAX ) const + { + AssertFatal( index < NUM_VALUES, "SFXVariantFloat::getValue() - index out of range!" ); + + return mClampF( gRandGen.randF( mValue[ index ] + mVariance[ index ][ 0 ], + mValue[ index ] + mVariance[ index ][ 1 ] ), + min, max ); + } + + void validate() + { + for( U32 i = 0; i < NUM_VALUES; ++ i ) + mVariance[ i ][ 0 ] = getMin( mVariance[ i ][ 0 ], mVariance[ i ][ 1 ] ); + } +}; + + +#endif // _SFXCOMMON_H_ diff --git a/Engine/source/sfx/sfxController.cpp b/Engine/source/sfx/sfxController.cpp new file mode 100644 index 000000000..bb35bcd8d --- /dev/null +++ b/Engine/source/sfx/sfxController.cpp @@ -0,0 +1,943 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/sfxController.h" +#include "sfx/sfxPlayList.h" +#include "sfx/sfxProfile.h" +#include "sfx/sfxSource.h" +#include "sfx/sfxSystem.h" +#include "sfx/sfxState.h" +#include "sfx/sfxDescription.h" +#include "console/engineAPI.h" +#include "math/mRandom.h" + + + +IMPLEMENT_CONOBJECT( SFXController ); + + +ConsoleDocClass( SFXController, + "@brief A sound source that drives multi-source playback.\n\n" + + "This class acts as an interpreter for SFXPlayLists. It goes through the slots of the playlist it is " + "attached to and performs the actions described by each of the slots in turn.\n" + + "As SFXControllers are created implicitly by the SFX system when instantiating a source for a play list it is " + "in most cases not necessary to directly deal with the class.\n" + + "The following example demonstrates how a controller would commonly be created.\n" + + "@tsexample\n" + "// Create a play list from two SFXProfiles.\n" + "%playList = new SFXPlayList()\n" + "{\n" + " // Use a looped description so the list playback will loop.\n" + " description = AudioMusicLoop2D;\n" + "\n" + " track[ 0 ] = Profile1;\n" + " track[ 1 ] = Profile2;\n" + "};\n" + "\n" + "// Play the list. This will implicitly create a controller.\n" + "sfxPlayOnce( %playList );\n" + "@endtsexample\n\n" + + "@note Play lists are updated at regular intervals by the sound system. This processing determines the granularity at " + "which playlist action timing takes place.\n" + "@note This class cannot be instantiated directly. Use sfxPlayOnce() or sfxCreateSource() with the playlist " + "you want to play to create an instance of this class.\n" + + "@see SFXPlayList\n" + "@ingroup SFX\n" +); + + +//----------------------------------------------------------------------------- + +SFXController::SFXController( SFXPlayList* playList ) + : Parent( playList ), + mTrace( playList->trace() ) +{ + VECTOR_SET_ASSOCIATION( mInsns ); + VECTOR_SET_ASSOCIATION( mSources ); + VECTOR_SET_ASSOCIATION( mParameters ); + + _compileList( playList ); +} + +//----------------------------------------------------------------------------- + +SFXController::~SFXController() +{ +} + +//----------------------------------------------------------------------------- + +void SFXController::initPersistFields() +{ + addGroup( "Debug" ); + addField( "trace", TypeBool, Offset( mTrace, SFXController ), + "If true, the controller logs its operation to the console.\n" + "This is a non-networked field that will work locally only." ); + endGroup( "Debug" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +SFXController* SFXController::_create( SFXPlayList* playList ) +{ + AssertFatal( playList != NULL, "SFXController::_create() - got a NULL playlist!" ); + + SFXController* controller = new SFXController( playList ); + controller->registerObject(); + + return controller; +} + +//----------------------------------------------------------------------------- + +void SFXController::_compileList( SFXPlayList* playList ) +{ + mInsns.clear(); + const bool isLooping = playList->getDescription()->mIsLooping; + + // Create a slot list that determines the order the slots will be + // played in. + + U32 slotList[ SFXPlayList::NUM_SLOTS ]; + bool isOrderedRandom = false; + switch( playList->getRandomMode() ) + { + case SFXPlayList::RANDOM_OrderedRandom: + isOrderedRandom = true; + /* fallthrough */ + + case SFXPlayList::RANDOM_NotRandom: + // Generate sequence 1-NUM_SLOTS. + for( U32 i = 0; i < SFXPlayList::NUM_SLOTS; ++ i ) + slotList[ i ] = i; + + if( isOrderedRandom ) + { + // Randomly exchange slots in the list. + for( U32 i = 0; i < SFXPlayList::NUM_SLOTS; ++ i ) + swap( slotList[ gRandGen.randI( 0, SFXPlayList::NUM_SLOTS - 1 ) ], slotList[ i ] ); + } + break; + + case SFXPlayList::RANDOM_StrictRandom: + // Randomly generate NUM_SLOTS slot indices. + for( U32 i = 0; i < SFXPlayList::NUM_SLOTS; ++ i ) + slotList[ i ] = gRandGen.randI( 0, SFXPlayList::NUM_SLOTS - 1 ); + break; + } + + // Generate the instruction list. + + U32 slotCount = 0; + for( U32 i = 0; i < SFXPlayList::NUM_SLOTS; ++ i ) + { + const U32 slotIndex = slotList[ i ]; + const U32 slotStartIp = mInsns.size(); + + SFXState* state = playList->getSlots().mState[ slotIndex ]; + + // If there's no track in this slot, ignore it. + + if( !playList->getSlots().mTrack[ slotIndex ] ) + continue; + + // If this is a looped slot and the list is not set to loop + // indefinitly on single slots, start a loop. + + S32 loopStartIp = -1; + if( playList->getSlots().mRepeatCount[ slotIndex ] > 0 + && ( !isLooping || playList->getLoopMode() != SFXPlayList::LOOP_Single ) ) + { + Insn insn( OP_LoopBegin, slotIndex, state ); + insn.mArg.mLoopCount = playList->getSlots().mRepeatCount[ slotIndex ]; + mInsns.push_back( insn ); + + loopStartIp = mInsns.size(); + } + + // Add in-delay, if any. + + if( playList->getSlots().mDelayTimeIn.mValue[ slotIndex ] > 0.0f ) + { + Insn insn( OP_Delay, slotIndex, state ); + insn.mArg.mDelayTime.mValue[ 0 ] = playList->getSlots().mDelayTimeIn.mValue[ slotIndex ]; + insn.mArg.mDelayTime.mVariance[ 0 ][ 0 ] = playList->getSlots().mDelayTimeIn.mVariance[ slotIndex ][ 0 ]; + insn.mArg.mDelayTime.mVariance[ 0 ][ 1 ] = playList->getSlots().mDelayTimeIn.mVariance[ slotIndex ][ 1 ]; + + mInsns.push_back( insn ); + } + + // Add the in-transition. + + const SFXPlayList::ETransitionMode transitionIn = playList->getSlots().mTransitionIn[ slotIndex ]; + if( transitionIn != SFXPlayList::TRANSITION_None ) + { + Insn insn( slotIndex, state ); + _genTransition( insn, transitionIn ); + mInsns.push_back( insn ); + } + + // Add the play instruction. + + { + Insn insn( OP_Play, slotIndex, state ); + mInsns.push_back( insn ); + } + + // Add out-delay, if any. + + if( playList->getSlots().mDelayTimeOut.mValue[ slotIndex ] > 0.0f ) + { + Insn insn( OP_Delay, slotIndex, state ); + insn.mArg.mDelayTime.mValue[ 0 ] = playList->getSlots().mDelayTimeOut.mValue[ slotIndex ]; + insn.mArg.mDelayTime.mVariance[ 0 ][ 0 ] = playList->getSlots().mDelayTimeOut.mVariance[ slotIndex ][ 0 ]; + insn.mArg.mDelayTime.mVariance[ 0 ][ 1 ] = playList->getSlots().mDelayTimeOut.mVariance[ slotIndex ][ 1 ]; + + mInsns.push_back( insn ); + } + + // Add the out-transition. + + const SFXPlayList::ETransitionMode transitionOut = playList->getSlots().mTransitionOut[ slotIndex ]; + if( transitionOut != SFXPlayList::TRANSITION_None ) + { + Insn insn( slotIndex, state ); + _genTransition( insn, transitionOut ); + mInsns.push_back( insn ); + } + + // Loop, if necessary. + + if( loopStartIp != -1 ) + { + Insn insn( OP_LoopEnd, slotIndex, state ); + insn.mArg.mJumpIp = loopStartIp; + mInsns.push_back( insn ); + } + + // If the list is on repeat-single, unconditionally + // loop over the instruction sequence of each slot. + + if( isLooping && playList->getLoopMode() == SFXPlayList::LOOP_Single ) + { + Insn insn( OP_Jump, slotIndex, state ); + insn.mArg.mJumpIp = slotStartIp; + mInsns.push_back( insn ); + } + + // If we have reached the limit of slots to play, + // stop generating. + + slotCount ++; + if( playList->getNumSlotsToPlay() == slotCount ) + break; + } + + // Set up for execution. + + mIp = 0; + if( !mInsns.empty() ) + _initInsn(); +} + +//----------------------------------------------------------------------------- + +void SFXController::_genTransition( Insn& insn, SFXPlayList::ETransitionMode transition ) +{ + switch( transition ) + { + case SFXPlayList::TRANSITION_Wait: + insn.mOpcode = OP_WaitSingle; + break; + + case SFXPlayList::TRANSITION_WaitAll: + insn.mOpcode = OP_WaitAll; + break; + + case SFXPlayList::TRANSITION_Stop: + insn.mOpcode = OP_StopSingle; + break; + + case SFXPlayList::TRANSITION_StopAll: + insn.mOpcode = OP_StopAll; + break; + + default: + AssertFatal( false, "SFXController::_addTransition() - should not reach here" ); + } +} + +//----------------------------------------------------------------------------- + +void SFXController::_initInsn() +{ + Insn& insn = mInsns[ mIp ]; + switch( insn.mOpcode ) + { + case OP_Delay: + mDelayEndTime = Platform::getVirtualMilliseconds() + + U32( insn.mArg.mDelayTime.getValue( 0, 0.0f ) * 1000.f ); + break; + + default: + break; + } + + if( mTrace ) + _printInsn(insn ); +} + +//----------------------------------------------------------------------------- + +void SFXController::_printInsn( Insn& insn) +{ + switch( insn.mOpcode ) + { + case OP_Delay: + Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: Delay %f:%f:%f", + mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "", + insn.mArg.mDelayTime.mValue[ 0 ], + insn.mArg.mDelayTime.mVariance[ 0 ], + insn.mArg.mDelayTime.mVariance[ 1 ] + ); + break; + + case OP_WaitSingle: + Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: WaitSingle", + mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "" ); + break; + + case OP_WaitAll: + Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: WaitAll", + mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "" ); + break; + + case OP_StopSingle: + Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: StopSingle", + mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "" ); + break; + + case OP_StopAll: + Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: StopAll", + mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "" ); + break; + + case OP_Play: + Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: Play", + mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "" ); + break; + + case OP_Jump: + Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: Jump %i", + mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "", insn.mArg.mJumpIp ); + break; + + case OP_LoopBegin: + Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: LoopBegin %i", + mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "", insn.mArg.mLoopCount ); + break; + + case OP_LoopEnd: + Con::printf( "[SFXController] ip=%d: slot=%d: state=%s: LoopEnd", + mIp, insn.mSlotIndex, insn.mState ? insn.mState->getName() : "" ); + break; + } +} + +//----------------------------------------------------------------------------- + +bool SFXController::_execInsn() +{ + bool endUpdate = false; + Insn& insn = mInsns[ mIp ]; + + switch( insn.mOpcode ) + { + case OP_Delay: + { + if( Platform::getVirtualMilliseconds() < mDelayEndTime ) + endUpdate = true; + else + _advanceIp(); + break; + } + + case OP_Play: + { + SFXPlayList* playList = getPlayList(); + SFXTrack* track = playList->getSlots().mTrack[ insn.mSlotIndex ]; + + // Handle existing sources playing on this slot and find + // whether we need to start a new source. + // + // Go through the list top-down so we can push sources we re-use + // to the top of the list. A side-effect of doing it this way is + // that the order of the sources that are preserved gets reversed, + // i.e. older sources will end up higher up the stack. + + bool startNew = true; + SFXPlayList::EReplayMode replayMode = playList->getSlots().mReplayMode[ insn.mSlotIndex ]; + if( replayMode != SFXPlayList::REPLAY_IgnorePlaying ) + for( S32 i = mSources.size() - 1; i >= 0; -- i ) + { + Source& source = mSources[ i ]; + if( source.mSlotIndex != insn.mSlotIndex ) + continue; + + // If the play-once source has expired, remove the entry + // and go on. + + if( source.mPtr == NULL ) + { + mSources.erase( i ); + ++ i; + continue; + } + + // Decide what to do with the still-playing source. + + if( replayMode == SFXPlayList::REPLAY_RestartPlaying + || replayMode == SFXPlayList::REPLAY_KeepPlaying ) + { + // Restart the source or keep playing it. + // Either way, move it to the top of the stack. + + startNew = false; + + Source src = mSources[ i ]; + mSources.erase( i ); + + //RDTODO: add a method to restart cleanly in the presence of fades; this here + // just cuts the current playback short + + if( replayMode == SFXPlayList::REPLAY_RestartPlaying ) + src.mPtr->stop( 0.f ); + + src.mPtr->play(); + + // Move the source to the top of the stack. + + mSources.increment(); + mSources.last() = src; + } + else if( replayMode == SFXPlayList::REPLAY_StartNew ) + { + // Kill off existing source. + + source.mPtr->stop(); + mSources.erase( i ); + ++ i; + } + else if( replayMode == SFXPlayList::REPLAY_SkipIfPlaying ) + { + startNew = false; + break; + } + } + + if( startNew ) + { + // Create a new source. + + SFXSource* source = SFX->createSource( + track, + &getTransform(), + &getVelocity() + ); + + // Append the source to the list of playing sources. + + if( source ) + { + mSources.increment(); + Source& src = mSources.last(); + + // Determine fade times. + + F32 fadeInTime = -1; + F32 fadeOutTime = -1; + + if( playList->getSlots().mFadeTimeIn.mValue[ insn.mSlotIndex ] != -1 ) + fadeInTime = playList->getSlots().mFadeTimeIn.getValue( insn.mSlotIndex, 0.f ); + if( playList->getSlots().mFadeTimeOut.mValue[ insn.mSlotIndex ] != -1 ) + fadeOutTime = playList->getSlots().mFadeTimeOut.getValue( insn.mSlotIndex, 0.f ); + + if( fadeInTime != -1 || fadeOutTime != -1 ) + source->setFadeTimes( fadeInTime, fadeOutTime ); + + // Set up source record. + + src.mPtr = source; + src.mSlotIndex = insn.mSlotIndex; + src.mVolumeScale = playList->getSlots().mVolumeScale.getValue( insn.mSlotIndex, 0.f, 1.f ); + src.mPitchScale = playList->getSlots().mPitchScale.getValue( insn.mSlotIndex ); + src.mFadeInTime = fadeInTime; + src.mFadeOutTime = fadeOutTime; + + SFXPlayList::EStateMode stateMode = playList->getSlots().mStateMode[ insn.mSlotIndex ]; + if( stateMode != SFXPlayList::STATE_IgnoreInactive ) + src.mState = insn.mState; + + // Set the source's volume and pitch. Either is scaled by our own + // assigned value and the scale factors from the playlist slot. + + source->setModulativeVolume( mAttenuatedVolume * src.mVolumeScale ); + source->setModulativePitch( mEffectivePitch * src.mPitchScale ); + + // Set min and max range. + + const SFXPlayList::VariantFloat& minDistance = playList->getSlots().mMinDistance; + const SFXPlayList::VariantFloat& maxDistance = playList->getSlots().mMaxDistance; + + if( minDistance.mValue[ insn.mSlotIndex ] >= 0.f + && maxDistance.mValue[ insn.mSlotIndex ] >= 0.f ) + source->setMinMaxDistance( + minDistance.getValue( insn.mSlotIndex, 0.f ), + maxDistance.getValue( insn.mSlotIndex, 0.f ) + ); + + // Start the source. + + source->play(); + SFX->deleteWhenStopped( source ); + } + } + + _advanceIp(); + break; + } + + case OP_WaitSingle: + { + if( !mSources.empty() && mSources.last().mPtr != NULL && mSources.last().mPtr->isPlaying() ) + endUpdate = true; + else + { + if( !mSources.empty() ) + mSources.decrement(); + _advanceIp(); + } + break; + } + + case OP_WaitAll: + { + for( U32 i = 0; i < mSources.size(); ++ i ) + if( mSources[ i ].mPtr != NULL && mSources[ i ].mPtr->isStopped() ) + { + mSources.erase( i ); + -- i; + } + + if( !mSources.empty() ) + endUpdate = true; + else + _advanceIp(); + + break; + } + + case OP_StopSingle: + { + if( !mSources.empty() ) + { + if( mSources.last().mPtr != NULL ) + mSources.last().mPtr->stop(); + mSources.decrement(); + } + + _advanceIp(); + break; + } + + case OP_StopAll: + { + while( !mSources.empty() ) + { + if( mSources.last().mPtr != NULL ) + mSources.last().mPtr->stop(); + mSources.decrement(); + } + + _advanceIp(); + break; + } + + case OP_Jump: + { + mIp = insn.mArg.mJumpIp; + _initInsn(); + break; + } + + case OP_LoopBegin: + { + mLoopCounter = insn.mArg.mLoopCount; + _advanceIp(); + break; + } + + case OP_LoopEnd: + { + -- mLoopCounter; + if( mLoopCounter > 0 ) + { + mIp = insn.mArg.mJumpIp; + _initInsn(); + } + else + _advanceIp(); + + break; + } + } + + return endUpdate; +} + +//----------------------------------------------------------------------------- + +void SFXController::_advanceIp() +{ + mIp ++; + if( mIp < mInsns.size() ) + _initInsn(); +} + +//----------------------------------------------------------------------------- + +void SFXController::_onParameterEvent( SFXParameter* parameter, SFXParameterEvent event ) +{ + Parent::_onParameterEvent( parameter, event ); + + // Implement cursor semantic. + + if( event == SFXParameterEvent_ValueChanged + && parameter->getChannel() == SFXChannelCursor ) + { + U32 slot = U32( mFloor( parameter->getValue() ) ); + if( slot != getCurrentSlot() ) + setCurrentSlot( slot ); + } +} + +//----------------------------------------------------------------------------- + +SFXPlayList* SFXController::getPlayList() const +{ + return static_cast< SFXPlayList* >( mTrack.getPointer() ); +} + +//----------------------------------------------------------------------------- + +U32 SFXController::getCurrentSlot() const +{ + if( mIp >= mInsns.size() ) + return 0; + else + return mInsns[ mIp ].mSlotIndex; +} + +//----------------------------------------------------------------------------- + +void SFXController::setCurrentSlot( U32 index ) +{ + mIp = 0; + while( mIp < mInsns.size() && mInsns[ mIp ].mSlotIndex != index ) + ++ mIp; + + if( mIp >= mInsns.size() ) + mIp = 0; + + if( !mInsns.empty() ) + _initInsn(); +} + +//----------------------------------------------------------------------------- + +void SFXController::_play() +{ + Parent::_play(); + + // Unpause sources, if we are paused. + + if( mStatus == SFXStatusPaused ) + for( U32 i = 0; i < mSources.size(); ++ i ) + if( mSources[ i ].mPtr != NULL ) + mSources[ i ].mPtr->play( 0.f ); // We want our fade values to take effect. + else + { + mSources.erase( i ); + -- i; + } +} + +//----------------------------------------------------------------------------- + +void SFXController::_pause() +{ + Parent::_pause(); + + // Pause all playing sources. + + for( U32 i = 0; i < mSources.size(); ++ i ) + if( mSources[ i ].mPtr != NULL ) + mSources[ i ].mPtr->pause( 0.f ); // We want our fade values to take effect. + else + { + mSources.erase( i ); + -- i; + } +} + +//----------------------------------------------------------------------------- + +void SFXController::_stop() +{ + Parent::_stop(); + + // Stop all playing sources. + + while( !mSources.empty() ) + { + if( mSources.last().mPtr != NULL ) + mSources.last().mPtr->stop( 0.f ); // We want our fade values to take effect. + mSources.decrement(); + } + + // Reset execution. + + mIp = 0; + if( !mInsns.empty() ) + _initInsn(); +} + +//----------------------------------------------------------------------------- + +void SFXController::_updateVolume( const MatrixF& listener ) +{ + F32 oldAttenuatedVolume = mAttenuatedVolume; + Parent::_updateVolume( listener ); + + // If the attenuated volume has changed, pass it off + // as the modulative volume to all our sources. + + if( oldAttenuatedVolume != mAttenuatedVolume ) + for( U32 i = 0; i < mSources.size(); ++ i ) + { + Source& source = mSources[ i ]; + if( source.mPtr != NULL ) + source.mPtr->setModulativeVolume( mAttenuatedVolume * source.mVolumeScale ); + else + { + mSources.erase( i ); + -- i; + } + } +} + +//----------------------------------------------------------------------------- + +void SFXController::_updatePitch() +{ + F32 oldEffectivePitch = mEffectivePitch; + Parent::_updatePitch(); + + if( mEffectivePitch != oldEffectivePitch ) + for( U32 i = 0; i < mSources.size(); ++ i ) + { + Source& source = mSources[ i ]; + if( source.mPtr != NULL ) + source.mPtr->setModulativePitch( mEffectivePitch * source.mPitchScale ); + else + { + mSources.erase( i ); + -- i; + } + } +} + +//----------------------------------------------------------------------------- + +void SFXController::_updatePriority() +{ + F32 oldEffectivePriority = mEffectivePriority; + Parent::_updatePriority(); + + if( mEffectivePriority != oldEffectivePriority ) + for( U32 i = 0; i < mSources.size(); ++ i ) + { + Source& source = mSources[ i ]; + if( source.mPtr != NULL ) + source.mPtr->setModulativePriority( mEffectivePriority ); + else + { + mSources.erase( i ); + -- i; + } + } +} + +//----------------------------------------------------------------------------- + +void SFXController::_update() +{ + Parent::_update(); + + SFXPlayList* playList = getPlayList(); + + // Check all sources against the current state setup and + // take appropriate actions. + + for( U32 i = 0; i < mSources.size(); ++ i ) + { + Source& source = mSources[ i ]; + + // If the source has already stopped playing, + // remove it. + + if( !source.mPtr ) + { + mSources.erase( i ); + -- i; + continue; + } + + if( !source.mState ) + continue; + + SFXPlayList::EStateMode stateMode = playList->getSlots().mStateMode[ mSources[ i ].mSlotIndex ]; + if( !source.mState->isActive() ) + { + if( source.mPtr->isPlaying() ) + { + // The source is playing in an incompatible state. + + if( stateMode == SFXPlayList::STATE_PauseInactive ) + source.mPtr->pause(); + else if( stateMode == SFXPlayList::STATE_StopInactive ) + { + source.mPtr->stop(); + mSources.erase( i ); + + -- i; + } + } + } + else + { + // Unpause a source that had its state become active again. + + if( source.mPtr->isPaused() && stateMode == SFXPlayList::STATE_PauseInactive ) + source.mPtr->play(); + } + } + + // Update interpreter. + + bool endUpdate = false; + while( !endUpdate ) + { + if( mIp >= mInsns.size() ) + { + // End of list reached. + + if( playList->getDescription()->mIsLooping && + playList->getLoopMode() == SFXPlayList::LOOP_All ) + { + // The play list is set to repeat-all. + // If it is also random, generate a new instruction list + // so we get a new playing order. Otherwise just reset. + + if( playList->getRandomMode() != SFXPlayList::RANDOM_NotRandom ) + _compileList( playList ); + else + { + mIp = 0; + if( !mInsns.empty() ) + _initInsn(); + } + + // Reset play timer. + + mPlayTimer.reset(); + mPlayTimer.start(); + } + else + { + // Moved to stopped state. + + mPlayTimer.stop(); + _setStatus( SFXStatusStopped ); + mIp = 0; + } + + // End this update. This limits playlist to at most one complete + // cycle per update. + + break; + } + + Insn& insn = mInsns[ mIp ]; + + if( insn.mState && !insn.mState->isActive() ) + { + // The state associated with the slot is inactive. Skip + // the instructions. + _advanceIp(); + } + else + endUpdate = _execInsn(); + } +} + +//============================================================================= +// Console Methods. +//============================================================================= +// MARK: ---- Console Methods ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXController, getCurrentSlot, S32, (),, + "Get the index of the playlist slot currently processed by the controller.\n" + "@return The slot index currently being played.\n" + "@see SFXPlayList" ) +{ + return object->getCurrentSlot(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXController, setCurrentSlot, void, ( S32 index ),, + "Set the index of the playlist slot to play by the controller. This can be used to seek in the playlist.\n" + "@param index Index of the playlist slot." ) +{ + object->setCurrentSlot( index ); +} diff --git a/Engine/source/sfx/sfxController.h b/Engine/source/sfx/sfxController.h new file mode 100644 index 000000000..32039d7c4 --- /dev/null +++ b/Engine/source/sfx/sfxController.h @@ -0,0 +1,220 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXCONTROLLER_H_ +#define _SFXCONTROLLER_H_ + +#ifndef _SFXSOURCE_H_ + #include "sfx/sfxSource.h" +#endif +#ifndef _SFXCOMMON_H_ + #include "sfx/sfxCommon.h" +#endif +#ifndef _SFXSOURCE_H_ + #include "sfx/sfxSource.h" +#endif +#ifndef _SFXPLAYLIST_H_ + #include "sfx/sfxPlayList.h" +#endif +#ifndef _TVECTOR_H_ + #include "core/util/tVector.h" +#endif + + +class SFXTrack; +class SFXProfile; +class SFXState; + + +/// SFXSource that drives multi-source playback. +/// +/// Basically, this class is an interpreter for the instruction slots in +/// SFXPlayLists. +/// +/// Controllers can be switched between states. When no state is set, all +/// tracks from playlists that do not have a state set will be played. When +/// setting a state, only tracks with the given state will be played. If +/// currently tracks with a different state are playing, the respective +/// controllers will transition out of their respective slots. +/// +class SFXController : public SFXSource +{ + public: + + typedef SFXSource Parent; + friend class SFXSystem; // _create + + protected: + + typedef SFXVariantFloat< 1 > VariantFloat; + + enum EOp + { + OP_Delay, + OP_WaitSingle, + OP_WaitAll, + OP_StopSingle, + OP_StopAll, + OP_Play, + OP_Jump, + OP_LoopBegin, + OP_LoopEnd, + }; + + struct Insn + { + EOp mOpcode; + U32 mSlotIndex; + SFXState* mState; + + union + { + VariantFloat mDelayTime; + U32 mJumpIp; + U32 mLoopCount; + } mArg; + + Insn() {} + Insn( EOp opcode ) + : mOpcode( opcode ), mSlotIndex( U32_MAX ), mState( NULL ) {} + Insn( U32 slotIndex, SFXState* state ) + : mSlotIndex( slotIndex ), mState( state ) {} + Insn( EOp opcode, U32 slotIndex, SFXState* state ) + : mOpcode( opcode ), mSlotIndex( slotIndex ), mState( state ) {} + }; + + /// + struct Source + { + /// The play-once source. + SimObjectPtr< SFXSource > mPtr; + + /// The state to which the source is tied. Only taken over from + /// the instruction if the state mode is not set to ignored. + SFXState* mState; + + /// Index of slot in playlist that this source was spawned on. + U32 mSlotIndex; + + /// Volume scale factor to apply to the source. Saved as it may have been + /// randomly generated. + F32 mVolumeScale; + + /// Pitch scale factor to apply to the source. Saved as it may have been + /// randomly generated. + F32 mPitchScale; + + /// + F32 mFadeInTime; + + /// + F32 mFadeOutTime; + + Source() + : mState( 0 ) {} + }; + + /// The current instruction in "mInsns". + U32 mIp; + + /// The instruction list. This is compiled from the playlist and then executed + /// in the controller's update. + Vector< Insn > mInsns; + + /// The stack of currently playing sources. + /// + /// All sources on this list are play-once sources so we can leave their lifetime + /// management to the SFX system. This is especially convenient in combination + /// with fade-outs where a source cannot be immediately deleted. + Vector< Source > mSources; + + /// + bool mTrace; + + /// + U32 mDelayEndTime; + + /// + U32 mLoopCounter; + + /// + SFXController( SFXPlayList* playList ); + + /// + void _printInsn( Insn& insn ); + + /// + void _compileList( SFXPlayList* playList ); + + /// + void _genTransition( Insn& insn, SFXPlayList::ETransitionMode transition ); + + /// + void _dumpInsns(); + + /// + void _initInsn(); + + /// + bool _execInsn(); + + /// + void _advanceIp(); + + /// + static SFXController* _create( SFXPlayList* playList ); + + // SFXSource. + virtual void _play(); + virtual void _pause(); + virtual void _stop(); + virtual void _onParameterEvent( SFXParameter* parameter, SFXParameterEvent event ); + virtual void _updateVolume( const MatrixF& listener ); + virtual void _updatePitch(); + virtual void _updatePriority(); + virtual void _update(); + + public: + + ~SFXController(); + + /// Constructor for the sake of ConsoleObject. + explicit SFXController() {} + + /// Return the playlist being played back by the controller. + SFXPlayList* getPlayList() const; + + /// Return the index of the playlist slot being processed by the controller. + U32 getCurrentSlot() const; + + /// Set the index of the playlist slot to process. + void setCurrentSlot( U32 index ); + + // SFXSource. + static void initPersistFields(); + + DECLARE_CONOBJECT( SFXController ); + DECLARE_DESCRIPTION( "Controls the playback of an SFXPlayList." ); +}; + +#endif // !_SFXCONTROLLER_H_ + diff --git a/Engine/source/sfx/sfxDescription.cpp b/Engine/source/sfx/sfxDescription.cpp new file mode 100644 index 000000000..4bf06d622 --- /dev/null +++ b/Engine/source/sfx/sfxDescription.cpp @@ -0,0 +1,654 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "sfx/sfxDescription.h" +#include "sfx/sfxSystem.h" +#include "sfx/sfxInternal.h" +#include "sfx/sfxSource.h" +#include "sfx/sfxTypes.h" +#include "sim/netConnection.h" +#include "core/stream/bitStream.h" +#include "core/stringTable.h" +#include "core/module.h" +#include "math/mathIO.h" +#include "math/mathTypes.h" + + +IMPLEMENT_CO_DATABLOCK_V1( SFXDescription ); + +ConsoleDocClass( SFXDescription, + "@brief A description for how a sound should be played.\n\n" + + "SFXDescriptions are used by the sound system to collect all parameters needed to set up a given " + "sound for playback. This includes information like its volume level, its pitch shift, etc. as well " + "as more complex information like its fade behavior, 3D properties, and per-sound reverb properties.\n\n" + + "Any sound playback will require a valid SFXDescription.\n\n" + + "As datablocks, SFXDescriptions can be set up as either networked datablocks or non-networked datablocks, " + "though it generally makes sense to keep all descriptions non-networked since they will be used exclusively " + "by clients.\n\n" + + "@tsexample\n" + "// A description for a 3D sound with a reasonable default range setting.\n" + "// The description is set up to assign sounds to the AudioChannelEffects source group\n" + "// (defined in the core scripts). An alternative means to achieve this is to use the\n" + "// AudioEffects description as a copy source (\": AudioEffects\").\n" + "\n" + "singleton SFXDescription( Audio3DSound )\n" + "{\n" + " sourceGroup = AudioChannelEffects;\n" + " is3D = true;\n" + " referenceDistance = 20.0;\n" + " maxDistance = 100.0;\n" + "};\n" + "@endtsexample\n\n" + + "@ingroup SFX\n" + "@ingroup Datablocks\n" +); + + +// Constants. +static const U32 sReverbSoundFlagDirectHFAuto = 0x01; +static const U32 sReverbSoundFlagRoomAuto = 0x02; +static const U32 sReverbSoundFlagRoomHFAuto = 0x04; +static const U32 sReverbSoundFlagInstance0 = 0x10; +static const U32 sReverbSoundFlagInstance1 = 0x20; +static const U32 sReverbSoundFlagInstance2 = 0x40; +static const U32 sReverbSoundFlagInstance3 = 0x80; + +AFTER_MODULE_INIT( SFX ) +{ + Con::addConstant( "SFXDescription::REVERB_DIRECTHFAUTO", TypeS32, &sReverbSoundFlagDirectHFAuto, + "Automatic setting of SFXDescription::reverbDirect due to distance to listener.\n" + "@see SFXDescription::flags\n\n" + "@ingroup SFXDescription" ); + Con::addConstant( "SFXDescription::REVERB_ROOMAUTO", TypeS32, &sReverbSoundFlagRoomAuto, + "Automatic setting of SFXDescription::reverbRoom due to distance to listener.\n" + "@see SFXDescription::flags\n\n" + "@ingroup SFXDescription" ); + Con::addConstant( "SFXDescription::REVERB_ROOMHFAUTO", TypeS32, &sReverbSoundFlagRoomHFAuto, + "Automatic setting of SFXDescription::reverbRoomHF due to distance to listener.\n" + "@see SFXDescription::flags\n\n" + "@ingroup SFXDescription" ); + Con::addConstant( "SFXDescription::REVERB_INSTANCE0", TypeS32, &sReverbSoundFlagInstance0, + "EAX4/SFX/GameCube/Wii: Specify channel to target reverb instance 0. Default target.\n" + "@see SFXDescription::flags\n\n" + "@ingroup SFXDescription" ); + Con::addConstant( "SFXDescription::REVERB_INSTANCE1", TypeS32, &sReverbSoundFlagInstance1, + "EAX4/SFX/GameCube/Wii: Specify channel to target reverb instance 1.\n" + "@see SFXDescription::flags\n\n" + "@ingroup SFXDescription" ); + Con::addConstant( "SFXDescription::REVERB_INSTANCE2", TypeS32, &sReverbSoundFlagInstance2, + "EAX4/SFX/GameCube/Wii: Specify channel to target reverb instance 2.\n" + "@see SFXDescription::flags\n\n" + "@ingroup SFXDescription" ); + Con::addConstant( "SFXDescription::REVERB_INSTANCE3", TypeS32, &sReverbSoundFlagInstance3, + "EAX4/SFX/GameCube/Wii: Specify channel to target reverb instance 3.\n" + "@see SFXDescription::flags\n\n" + "@ingroup SFXDescription" ); +} + + +//----------------------------------------------------------------------------- + +SFXDescription::SFXDescription() + : SimDataBlock(), + mVolume( 1 ), + mPitch( 1 ), + mIsLooping( false ), + mIsStreaming( false ), + mIs3D( false ), + mUseHardware( false ), + mUseReverb( false ), + mMinDistance( 1 ), + mMaxDistance( 100 ), + mConeInsideAngle( 360 ), + mConeOutsideAngle( 360 ), + mConeOutsideVolume( 1 ), + mRolloffFactor( -1.f ), // Deactivated + mFadeInTime( 0.0f ), + mFadeOutTime( 0.0f ), + mFadeLoops( false ), + mStreamPacketSize( SFXInternal::SFXAsyncStream::DEFAULT_STREAM_PACKET_LENGTH ), + mStreamReadAhead( SFXInternal::SFXAsyncStream::DEFAULT_STREAM_LOOKAHEAD ), + mPriority( 1.0f ), + mScatterDistance( 0.f, 0.f, 0.f ), + mSourceGroup( NULL ) +{ + dMemset( mParameters, 0, sizeof( mParameters ) ); +} + +//----------------------------------------------------------------------------- + +SFXDescription::SFXDescription( const SFXDescription& desc ) + : SimDataBlock(), + mVolume( desc.mVolume ), + mPitch( desc.mPitch ), + mIsLooping( desc.mIsLooping ), + mIsStreaming( desc.mIsStreaming ), + mIs3D( desc.mIs3D ), + mUseHardware( desc.mUseHardware ), + mMinDistance( desc.mMinDistance ), + mMaxDistance( desc.mMaxDistance ), + mConeInsideAngle( desc.mConeInsideAngle ), + mConeOutsideAngle( desc.mConeOutsideAngle ), + mConeOutsideVolume( desc.mConeOutsideVolume ), + mRolloffFactor( desc.mRolloffFactor ), + mSourceGroup( desc.mSourceGroup ), + mFadeInTime( desc.mFadeInTime ), + mFadeOutTime( desc.mFadeOutTime ), + mFadeInEase( desc.mFadeInEase ), + mFadeOutEase( desc.mFadeOutEase ), + mFadeLoops( desc.mFadeLoops ), + mStreamPacketSize( desc.mStreamPacketSize ), + mStreamReadAhead( desc.mStreamReadAhead ), + mUseReverb( desc.mUseReverb ), + mReverb( desc.mReverb ), + mPriority( desc.mPriority ), + mScatterDistance( desc.mScatterDistance ) +{ + for( U32 i = 0; i < MaxNumParameters; ++ i ) + mParameters[ i ] = desc.mParameters[ i ]; +} + +//----------------------------------------------------------------------------- + +void SFXDescription::initPersistFields() +{ + addGroup( "Playback" ); + + addField( "sourceGroup", TypeSFXSourceName, Offset( mSourceGroup, SFXDescription ), + "Group that sources playing with this description should be put into.\n\n" + "When a sound source is allocated, it will be made a child of the source group that is listed in its \n" + "description. This group will then modulate several properties of the sound as it is played.\n\n" + "For example, one use of groups is to segregate sounds so that volume levels of different sound " + "groups such as interface audio and game audio can be controlled independently.\n\n" + "@ref SFXSource_hierarchies" ); + addField( "volume", TypeF32, Offset( mVolume, SFXDescription ), + "Base volume level for the sound.\n\n" + "This will be the starting point for volume attenuation on the sound. The final effective volume of " + "a sound will be dependent on a number of parameters.\n\n" + "Must be between 0 (mute) and 1 (full volume). Default is 1.\n\n" + "@ref SFXSource_volume" ); + addField( "pitch", TypeF32, Offset( mPitch, SFXDescription ), + "Pitch shift to apply to playback.\n\n" + "The pitch assigned to a sound determines the speed at which it is played back. A pitch shift of 1 plays the " + "sound at its default speed. A greater shift factor speeds up playback and a smaller shift factor slows it down.\n\n" + "Must be >0. Default is 1." ); + addField( "isLooping", TypeBool, Offset( mIsLooping, SFXDescription ), + "If true, the sound will be played in an endless loop.\n\n" + "Default is false." ); + addField( "priority", TypeF32, Offset( mPriority, SFXDescription ), + "Priority level for virtualization of sounds (1 = base level).\n" + "When there are more concurrently active sounds than supported by the audio mixer, some of the sounds " + "need to be culled. Which sounds are culled first depends primarily on total audibility of individual sounds. " + "However, the priority of invidual sounds may be decreased or decreased through this field.\n\n" + "@ref SFXSound_virtualization" ); + addField( "useHardware", TypeBool, Offset( mUseHardware, SFXDescription ), + "Whether the sound is allowed to be mixed in hardware.\n" + "If true, the sound system will try to allocate the voice for the sound directly " + "on the sound hardware for mixing by the hardware mixer. Be aware that a hardware mixer " + "may not provide all features available to sounds mixed in software.\n\n" + "@note This flag currently only takes effect when using FMOD.\n\n" + "@note Generally, it is preferable to let sounds be mixed in software.\n\n" ); + addField( "parameters", TypeSFXParameterName, Offset( mParameters, SFXDescription ), MaxNumParameters, + "Names of the parameters to which sources using this description will automatically be linked.\n\n" + "Individual parameters are identified by their #internalName.\n\n" + "@ref SFX_interactive" ); + + endGroup( "Playback" ); + + addGroup( "Fading" ); + + addField( "fadeInTime", TypeF32, Offset( mFadeInTime, SFXDescription ), + "Number of seconds to gradually fade in volume from zero when playback starts.\n" + "Must be >= 0.\n\n" + "@ref SFXSource_fades" ); + addField( "fadeOutTime", TypeF32, Offset( mFadeOutTime, SFXDescription ), + "Number of seconds to gradually fade out volume down to zero when playback is stopped or paused.\n" + "Must be >=0.\n\n" + "@ref SFXSource_fades" ); + addField( "fadeInEase", TypeEaseF, Offset( mFadeInEase, SFXDescription ), + "Easing curve for fade-in transition.\n" + "Volume fade-ins will interpolate volume along this curve.\n\n" + "@ref SFXSource_fades" ); + addField( "fadeOutEase", TypeEaseF, Offset( mFadeOutEase, SFXDescription ), + "Easing curve for fade-out transition.\n" + "Volume fade-outs will interpolate volume along this curve.\n\n" + "@ref SFXSource_fades" ); + addField( "fadeLoops", TypeBool, Offset( mFadeLoops, SFXDescription ), + "Fade each cycle of a loop in and/or out; otherwise only fade-in first cycle.\n" + "By default, volume fading is applied to the beginning and end of the playback range, i.e. a fade-in segment " + "is placed at the beginning of the sound and a fade-out segment is paced at the end of a sound. However, " + "when looping playback, this may be undesirable as each iteration of the sound will then have a fade-in and " + "fade-out effect.\n\n" + "To set up looping sounds such that a fade-in is applied only when the sound is first started (or playback resumed) " + "and a fade-out is only applied when the sound is explicitly paused or stopped, set this field to true.\n\n" + "Default is false.\n\n" + "@ref SFXSource_fades" ); + + endGroup( "Fading" ); + + addGroup( "3D" ); + + addField( "is3D", TypeBool, Offset( mIs3D, SFXDescription ), + "If true, sounds played with this description will have a position and orientation in space.\n" + "Unlike a non-positional sound, a 3D sound will have its volume attenuated depending on the distance to the " + "listener in space. The farther the sound moves away from the listener, the less audible it will be.\n\n" + "Non-positional sounds, in contrast, will remain at their original volume regardless of where the listener is.\n\n" + "@note Whether a sound is positional or non-positional cannot be changed once the sound was created so this field " + "determines up front which is the case for a given sound.\n\n" + "@ref SFX_3d\n" + "@ref SFXSource_volume" ); + addField( "referenceDistance", TypeF32, Offset( mMinDistance, SFXDescription ), + "Distance at which volume attenuation begins.\n" + "Up to this distance, the sound retains its base volume.\n\n" + "In the linear distance model, the volume will linearly from this distance onwards up to maxDistance where it " + "reaches zero.\n\n" + "In the logarithmic distance model, the reference distance determine how fast the sound volume decreases " + "with distance. Each referenceDistance steps (scaled by the rolloff factor), the volume halves.\n\n" + "A rule of thumb is that for sounds that require you to be close to hear them in the real world, set the reference " + "distance to small values whereas for sounds that are widely audible set it to larger values.\n\n" + "Only applies to 3D sounds.\n" + "@see LevelInfo::soundDistanceModel\n\n" + "@ref SFX_3d\n" + "@ref SFXSource_volume" ); + addField( "maxDistance", TypeF32, Offset( mMaxDistance, SFXDescription ), + "The distance at which attenuation stops.\n" + "In the linear distance model, the attenuated volume will be zero at this distance.\n\n" + "In the logarithmic model, attenuation will simply stop at this distance and the sound will keep its attenuated " + "volume from there on out. As such, it primarily functions as a cutoff factor to exponential distance attentuation " + "to limit the number of voices relevant to updates.\n\n" + "Only applies to 3D sounds.\n" + "@see LevelInfo::soundDistanceModel\n\n" + "@ref SFX_3d\n" + "@ref SFXSource_volume" ); + addField( "scatterDistance", TypePoint3F,Offset( mScatterDistance, SFXDescription ), + "Bounds on random displacement of 3D sound positions.\n" + "When a 3D sound is created and given its initial position in space, this field is used to determine " + "the amount of randomization applied to the actual position given to the sound system.\n\n" + "The randomization uses the following scheme:" + "@verbatim\n" + "x += rand( - scatterDistance[ 0 ], scatterDistance[ 0 ] );\n" + "y += rand( - scatterDistance[ 1 ], scatterDistance[ 1 ] );\n" + "z += rand( - scatterDistance[ 2 ], scatterDistance[ 2 ] );\n" + "@endverbatim\n" ); + addField( "coneInsideAngle", TypeS32, Offset( mConeInsideAngle, SFXDescription ), + "Inner sound cone angle in degrees.\n" + "This value determines the angle of the inner volume cone that protrudes out in the direction " + "of a sound. Within this cone, the sound source retains full volume that is unaffected by sound cone " + "settings (though still affected by distance attenuation.)\n\n" + "Valid values are from 0 to 360. Must be less than coneOutsideAngle. Default is 360. Only for 3D sounds.\n\n" + "@ref SFXSource_cones" ); + addField( "coneOutsideAngle", TypeS32, Offset( mConeOutsideAngle, SFXDescription ), + "Outer sound cone angle in degrees.\n" + "This value determines the angle of the outer volume cone that protrudes out in the direction of a sound " + "and surrounds the inner volume cone. Within this cone, volume will linearly interpolate from the outer cone " + "hull inwards to the inner coner hull starting with the base volume scaled by coneOutsideVolume and ramping " + "up/down to the full base volume.\n\n" + "Valid values are from 0 to 360. Must be >= coneInsideAngle. Default is 360. Only for 3D sounds.\n\n" + "@ref SFXSource_cones" ); + addField( "coneOutsideVolume", TypeF32, Offset( mConeOutsideVolume, SFXDescription ), + "Determines the volume scale factor applied the a source's base volume level outside of the outer cone.\n" + "In the outer cone, starting from outside the inner cone, the scale factor smoothly interpolates from 1.0 (within the inner cone) " + "to this value. At the moment, the allowed range is 0.0 (silence) to 1.0 (no attenuation) as amplification is only supported on " + "XAudio2 but not on the other devices.\n\n" + "Only for 3D sound.\n\n" + "@ref SFXSource_cones" ); + addField( "rolloffFactor", TypeF32, Offset( mRolloffFactor, SFXDescription ), + "Scale factor to apply to logarithmic distance attenuation curve. If -1, the global rolloff setting is used.\n\n" + "@note Per-sound rolloff is only supported on OpenAL and FMOD at the moment. With other divices, the global rolloff setting " + "is used for all sounds.\n" + "@see LevelInfo::soundDistanceModel" ); + + endGroup( "3D" ); + + addGroup( "Streaming" ); + + addField( "isStreaming", TypeBool, Offset( mIsStreaming, SFXDescription ), + "If true, incrementally stream sounds; otherwise sounds are loaded in full.\n\n" + "@ref SFX_streaming" ); + addField( "streamPacketSize", TypeS32, Offset( mStreamPacketSize, SFXDescription ), + "Number of seconds of sample data per single streaming packet.\n" + "This field allows to fine-tune streaming for individual sounds. The streaming system " + "processes streamed sounds in batches called packets. Each packet will contain a set amount " + "of sample data determined by this field. The greater its value, the more sample data each " + "packet contains, the more work is done per packet.\n\n" + "@note This field only takes effect when Torque's own sound system performs the streaming. " + "When FMOD is used, this field is ignored and streaming is performed by FMOD.\n\n" + "@ref SFX_streaming" ); + addField( "streamReadAhead", TypeS32, Offset( mStreamReadAhead, SFXDescription ), + "Number of sample packets to read and buffer in advance.\n" + "This field determines the number of packets that the streaming system will try to keep buffered " + "in advance. As such it determines the number of packets that can be consumed by the sound " + "device before the playback queue is running dry. Greater values thus allow for more lag " + "in the streaming pipeline.\n\n" + "@note This field only takes effect when Torque's own sound system performs the streaming. " + "When FMOD is used, this field is ignored and streaming is performed by FMOD.\n\n" + "@ref SFX_streaming" ); + + endGroup( "Streaming" ); + + addGroup( "Reverb" ); + + addField( "useCustomReverb", TypeBool, Offset( mUseReverb, SFXDescription ), + "If true, use the reverb properties defined here on sounds.\n" + "By default, sounds will be assigned a generic reverb profile. By setting this flag to true, " + "a custom reverb setup can be defined using the \"Reverb\" properties that will then be assigned " + "to sounds playing with the description.\n\n" + "@ref SFX_reverb" ); + addField( "reverbDirect", TypeS32, Offset( mReverb.mDirect, SFXDescription ), + "Direct path level (at low and mid frequencies).\n" + "@note SUPPORTED: EAX/I3DL2/FMODSFX\n\n" + "@see http://www.atc.creative.com/algorithms/eax20.pdf" ); + addField( "reverbDirectHF", TypeS32, Offset( mReverb.mDirectHF, SFXDescription ), + "Relative direct path level at high frequencies.\n" + "@note SUPPORTED: EAX/I3DL2\n\n" + "@see http://www.atc.creative.com/algorithms/eax20.pdf" ); + addField( "reverbRoom", TypeS32, Offset( mReverb.mRoom, SFXDescription ), + "Room effect level (at low and mid frequencies).\n" + "@note SUPPORTED: EAX/I3DL2/FMODSFX\n\n" + "@see http://www.atc.creative.com/algorithms/eax20.pdf" ); + addField( "reverbRoomHF", TypeS32, Offset( mReverb.mRoomHF, SFXDescription ), + "Relative room effect level at high frequencies.\n" + "@note SUPPORTED: EAX/I3DL2\n\n" + "@see http://www.atc.creative.com/algorithms/eax20.pdf" ); + addField( "reverbObstruction", TypeS32, Offset( mReverb.mObstruction, SFXDescription ), + "Main obstruction control (attenuation at high frequencies).\n" + "@note SUPPORTED: EAX/I3DL2\n\n" + "@see http://www.atc.creative.com/algorithms/eax20.pdf" ); + addField( "reverbObstructionLFRatio", TypeF32, Offset( mReverb.mObstructionLFRatio, SFXDescription ), + "Obstruction low-frequency level re. main control.\n" + "@note SUPPORTED: EAX/I3DL2\n\n" + "@see http://www.atc.creative.com/algorithms/eax20.pdf" ); + addField( "reverbOcclusion", TypeS32, Offset( mReverb.mOcclusion, SFXDescription ), + "Main occlusion control (attenuation at high frequencies)." + "@note SUPPORTED: EAX/I3DL2\n\n" + "\n@see http://www.atc.creative.com/algorithms/eax20.pdf" ); + addField( "reverbOcclusionLFRatio", TypeF32, Offset( mReverb.mOcclusionLFRatio, SFXDescription ), + "Occlusion low-frequency level re. main control.\n" + "@note SUPPORTED: EAX/I3DL2\n\n" + "@see http://www.atc.creative.com/algorithms/eax20.pdf" ); + addField( "reverbOcclusionRoomRatio", TypeF32, Offset( mReverb.mOcclusionRoomRatio, SFXDescription ), + "Relative occlusion control for room effect.\n" + "@note SUPPORTED: EAX Only\n\n" + "@see http://www.atc.creative.com/algorithms/eax20.pdf" ); + addField( "reverbOcclusionDirectRatio",TypeF32, Offset( mReverb.mOcclusionDirectRatio, SFXDescription ), + "Relative occlusion control for direct path.\n" + "@note SUPPORTED: EAX Only\n\n" + "@see http://www.atc.creative.com/algorithms/eax20.pdf" ); + addField( "reverbExclusion", TypeS32, Offset( mReverb.mExclusion, SFXDescription ), + "Main exclusion control (attenuation at high frequencies).\n" + "@note SUPPORTED: EAX Only\n\n" + "@see http://www.atc.creative.com/algorithms/eax20.pdf" ); + addField( "reverbExclusionLFRatio", TypeF32, Offset( mReverb.mExclusionLFRatio, SFXDescription ), + "Exclusion low-frequency level re. main control.\n" + "@note SUPPORTED: EAX Only\n\n" + "@see http://www.atc.creative.com/algorithms/eax20.pdf" ); + addField( "reverbOutsideVolumeHF", TypeS32, Offset( mReverb.mOutsideVolumeHF, SFXDescription ), + "Outside sound cone level at high frequencies.\n" + "@note SUPPORTED: EAX Only\n\n" + "@see http://www.atc.creative.com/algorithms/eax20.pdf" ); + addField( "reverbDopplerFactor", TypeF32, Offset( mReverb.mDopplerFactor, SFXDescription ), + "Per-source doppler factor.\n" + "@note SUPPORTED: EAX Only\n\n" + "@see http://www.atc.creative.com/algorithms/eax20.pdf" ); + addField( "reverbReverbRolloffFactor", TypeF32, Offset( mReverb.mRolloffFactor, SFXDescription ), + "Per-source logarithmic falloff factor.\n" + "@note SUPPORTED: EAX Only\n\n" + "@see http://www.atc.creative.com/algorithms/eax20.pdf" ); + addField( "reverbRoomRolloffFactor", TypeF32, Offset( mReverb.mRoomRolloffFactor, SFXDescription ), + "Room effect falloff factor.\n" + "@note SUPPORTED: EAX/I3DL2\n\n" + "@see http://www.atc.creative.com/algorithms/eax20.pdf" ); + addField( "reverbAirAbsorptionFactor", TypeF32, Offset( mReverb.mAirAbsorptionFactor, SFXDescription ), + "Multiplies SFXEnvironment::airAbsorptionHR.\n" + "@note SUPPORTED: EAX Only\n\n" + "@see http://www.atc.creative.com/algorithms/eax20.pdf" ); + addField( "reverbFlags", TypeS32, Offset( mReverb.mFlags, SFXDescription ), + "Bitfield combination of per-sound reverb flags.\n" + "@see REVERB_DIRECTHFAUTO\n" + "@see REVERB_ROOMAUTO\n" + "@see REVERB_ROOMHFAUTO\n" + "@see REVERB_INSTANCE0\n" + "@see REVERB_INSTANCE1\n" + "@see REVERB_INSTANCE2\n" + "@see REVERB_INSTANCE3\n" ); + + endGroup( "Reverb" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +bool SFXDescription::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + Sim::getSFXDescriptionSet()->addObject( this ); + + // Convert a legacy 'channel' field, if we have one. + + static const char* sChannel = StringTable->insert( "channel" ); + const char* channelValue = getDataField( sChannel, NULL ); + if( channelValue && channelValue[ 0 ] ) + { + const char* group = Con::evaluatef( "return sfxOldChannelToGroup( %s );", channelValue ); + if( !Sim::findObject( group, mSourceGroup ) ) + Con::errorf( "SFXDescription::onAdd - could not resolve channel '%s' to SFXSource", channelValue ); + } + + // Validate the data we'll be passing to + // the audio layer. + validate(); + + return true; +} + +//----------------------------------------------------------------------------- + +void SFXDescription::validate() +{ + // Validate the data we'll be passing to the audio layer. + mVolume = mClampF( mVolume, 0, 1 ); + + if( mPitch <= 0.0f ) + mPitch = 1.0f; + if( mFadeInTime < 0.0f ) + mFadeInTime = 0.0f; + if( mFadeOutTime < 0.0f ) + mFadeOutTime = 0.0f; + if( mRolloffFactor < 0.f ) + mRolloffFactor = -1.f; + + if( mMinDistance < 0.f ) + mMinDistance = 0.f; + if( mMaxDistance <= mMinDistance ) + mMaxDistance = mMinDistance + 0.01f; + + mConeInsideAngle = mClamp( mConeInsideAngle, 0, 360 ); + mConeOutsideAngle = mClamp( mConeOutsideAngle, mConeInsideAngle, 360 ); + mConeOutsideVolume = mClampF( mConeOutsideVolume, 0, 1 ); + + if( !mIs3D ) + mUseReverb = false; + + mReverb.validate(); +} + +//----------------------------------------------------------------------------- + +void SFXDescription::packData( BitStream *stream ) +{ + Parent::packData( stream ); + + stream->writeFloat( mVolume, 6 ); + stream->writeFloat( mPitch, 6 ); + stream->writeFloat( mPriority, 6 ); + + stream->writeFlag( mIsLooping ); + stream->writeFlag( mFadeLoops ); + + stream->writeFlag( mIsStreaming ); + stream->writeFlag( mIs3D ); + stream->writeFlag( mUseReverb ); + stream->writeFlag( mUseHardware ); + + sfxWrite( stream, mSourceGroup ); + + if( mIs3D ) + { + stream->write( mMinDistance ); + stream->write( mMaxDistance ); + stream->write( mRolloffFactor ); + mathWrite( *stream, mScatterDistance ); + + stream->writeInt( mConeInsideAngle, 9 ); + stream->writeInt( mConeOutsideAngle, 9 ); + + stream->writeFloat( mConeOutsideVolume, 6 ); + + if( mUseReverb ) + { + stream->writeRangedS32( mReverb.mDirect, -10000, 1000 ); + stream->writeRangedS32( mReverb.mDirectHF, -10000, 0 ); + stream->writeRangedS32( mReverb.mRoom, -10000, 1000 ); + stream->writeRangedS32( mReverb.mRoomHF, -10000, 0 ); + stream->writeRangedS32( mReverb.mObstruction, -10000, 0 ); + stream->writeRangedF32( mReverb.mObstructionLFRatio, 0.0, 1.0, 7 ); + stream->writeRangedS32( mReverb.mOcclusion, -10000, 0 ); + stream->writeRangedF32( mReverb.mOcclusionLFRatio, 0.0, 1.0, 7 ); + stream->writeRangedF32( mReverb.mOcclusionRoomRatio, 0.0, 10.0, 7 ); + stream->writeRangedF32( mReverb.mOcclusionDirectRatio, 0.0, 10.0, 7 ); + stream->writeRangedS32( mReverb.mExclusion, -10000, 0 ); + stream->writeRangedF32( mReverb.mExclusionLFRatio, 0.0, 1.0, 7 ); + stream->writeRangedS32( mReverb.mOutsideVolumeHF, -10000, 0 ); + stream->writeRangedF32( mReverb.mDopplerFactor, 0.0, 10.0, 7 ); + stream->writeRangedF32( mReverb.mRolloffFactor, 0.0, 10.0, 7 ); + stream->writeRangedF32( mReverb.mRoomRolloffFactor, 0.0, 10.0, 7 ); + stream->writeRangedF32( mReverb.mAirAbsorptionFactor, 0.0, 10.0, 7 ); + stream->writeInt( mReverb.mFlags, 6 ); + } + } + + stream->write( mFadeInTime ); + stream->write( mFadeOutTime ); + stream->writeInt( mStreamPacketSize, 8 ); + stream->writeInt( mStreamReadAhead, 8 ); + + mathWrite( *stream, mFadeInEase ); + mathWrite( *stream, mFadeOutEase ); + + for( U32 i = 0; i < MaxNumParameters; ++ i ) + if( stream->writeFlag( mParameters[ i ] ) ) + stream->writeString( mParameters[ i ] ); +} + +//----------------------------------------------------------------------------- + +void SFXDescription::unpackData( BitStream *stream ) +{ + Parent::unpackData( stream ); + + mVolume = stream->readFloat( 6 ); + mPitch = stream->readFloat( 6 ); + mPriority = stream->readFloat( 6 ); + + mIsLooping = stream->readFlag(); + mFadeLoops = stream->readFlag(); + + mIsStreaming = stream->readFlag(); + mIs3D = stream->readFlag(); + mUseReverb = stream->readFlag(); + mUseHardware = stream->readFlag(); + + String errorStr; + if( !sfxReadAndResolve( stream, &mSourceGroup, errorStr ) ) + Con::errorf( "SFXDescription::unpackData: %s", errorStr.c_str() ); + + if( mIs3D ) + { + stream->read( &mMinDistance ); + stream->read( &mMaxDistance ); + stream->read( &mRolloffFactor ); + mathRead( *stream, &mScatterDistance ); + + mConeInsideAngle = stream->readInt( 9 ); + mConeOutsideAngle = stream->readInt( 9 ); + + mConeOutsideVolume = stream->readFloat( 6 ); + + if( mUseReverb ) + { + mReverb.mDirect = stream->readRangedS32( -10000, 1000 ); + mReverb.mDirectHF = stream->readRangedS32( -10000, 0 ); + mReverb.mRoom = stream->readRangedS32( -10000, 1000 ); + mReverb.mRoomHF = stream->readRangedS32( -10000, 0 ); + mReverb.mObstruction = stream->readRangedS32( -10000, 0 ); + mReverb.mObstructionLFRatio = stream->readRangedF32( 0.0, 1.0, 7 ); + mReverb.mOcclusion = stream->readRangedS32( -10000, 0 ); + mReverb.mOcclusionLFRatio = stream->readRangedF32( 0.0, 1.0, 7 ); + mReverb.mOcclusionRoomRatio = stream->readRangedF32( 0.0, 10.0, 7 ); + mReverb.mOcclusionDirectRatio = stream->readRangedF32( 0.0, 10.0, 7 ); + mReverb.mExclusion = stream->readRangedS32( -10000, 0 ); + mReverb.mExclusionLFRatio = stream->readRangedF32( 0.0, 1.0, 7 ); + mReverb.mOutsideVolumeHF = stream->readRangedS32( -10000, 0 ); + mReverb.mDopplerFactor = stream->readRangedF32( 0.0, 10.0, 7 ); + mReverb.mRolloffFactor = stream->readRangedF32( 0.0, 10.0, 7 ); + mReverb.mRoomRolloffFactor = stream->readRangedF32( 0.0, 10.0, 7 ); + mReverb.mAirAbsorptionFactor = stream->readRangedF32( 0.0, 10.0, 7 ); + mReverb.mFlags = stream->readInt( 6 ); + } + } + + stream->read( &mFadeInTime ); + stream->read( &mFadeOutTime ); + mStreamPacketSize = stream->readInt( 8 ); + mStreamReadAhead = stream->readInt( 8 ); + + mathRead( *stream, &mFadeInEase ); + mathRead( *stream, &mFadeOutEase ); + + for( U32 i = 0; i < MaxNumParameters; ++ i ) + if( stream->readFlag() ) + mParameters[ i ] = stream->readSTString(); + else + mParameters[ i ] = NULL; +} + +//----------------------------------------------------------------------------- + +void SFXDescription::inspectPostApply() +{ + Parent::inspectPostApply(); + + validate(); + + if( SFX ) + SFX->notifyDescriptionChanged( this ); +} diff --git a/Engine/source/sfx/sfxDescription.h b/Engine/source/sfx/sfxDescription.h new file mode 100644 index 000000000..8bac79467 --- /dev/null +++ b/Engine/source/sfx/sfxDescription.h @@ -0,0 +1,198 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXDESCRIPTION_H_ +#define _SFXDESCRIPTION_H_ + +#ifndef _CONSOLETYPES_H_ + #include "console/consoleTypes.h" +#endif +#ifndef _SIMDATABLOCK_H_ + #include "console/simDatablock.h" +#endif +#ifndef _MPOINT3_H_ + #include "math/mPoint3.h" +#endif +#ifndef _SFXCOMMON_H_ + #include "sfx/sfxCommon.h" +#endif +#ifndef _MEASE_H_ + #include "math/mEase.h" +#endif + + +class SFXSource; + + +/// The SFXDescription defines how a sound should be played. +/// +/// If mConeInsideAngle and mConeOutsideAngle are not both +/// 360 then the sound will be directional and facing out +/// the Y axis. To reorient the cones, reorient the emitter +/// object. +/// +/// A few tips: +/// +/// Make sure that server SFXDescription are defined with the +/// datablock keyword, and that client SFXDescription are defined +/// with the 'new' or 'singleton' keyword. +/// +class SFXDescription : public SimDataBlock +{ + typedef SimDataBlock Parent; + + public: + + enum + { + MaxNumParameters = 8, + }; + + /// The 0 to 1 volume scale. + F32 mVolume; + + /// The pitch scale. + F32 mPitch; + + /// If true the sound will loop. + bool mIsLooping; + + /// If true the sound data will be streamed from + /// disk and not loaded completely into memory. + bool mIsStreaming; + + /// If true the sound will be 3D positional. + bool mIs3D; + + /// If this sound is allowed to be mixed in hardware. + bool mUseHardware; + + /// If true the sound uses custom reverb properties. + bool mUseReverb; + + /// The distance from the emitter at which the + /// sound volume is unchanged. Beyond this distance + /// the volume begins to falloff. + /// + /// This is only valid for 3D sounds. + F32 mMinDistance; + + /// The distance from the emitter at which the + /// sound volume becomes zero. + /// + /// This is only valid for 3D sounds. + F32 mMaxDistance; + + /// The angle in degrees of the inner part of + /// the cone. It must be within 0 to 360. + /// + /// This is only valid for 3D sounds. + U32 mConeInsideAngle; + + /// The angle in degrees of the outer part of + /// the cone. It must be greater than mConeInsideAngle + /// and less than to 360. + /// + /// This is only valid for 3D sounds. + U32 mConeOutsideAngle; + + /// The volume scalar for on/beyond the outside angle. + /// + /// This is only valid for 3D sounds. + F32 mConeOutsideVolume; + + /// Local logarithmic distance attenuation rolloff factor. -1 to use global setting. + /// Per-sound rolloff settings only supported with some SFX providers. + F32 mRolloffFactor; + + /// Max distance in both directions along each axis by which the + /// sound position of a 3D sound will be randomly scattered. + /// + /// Example: if you set x=5, then the final x coordinate of the 3D + /// sound will be (OriginalX + randF( -5, +5 )). + Point3F mScatterDistance; + + /// The source to which sources playing with this description will + /// be added. + SFXSource* mSourceGroup; + + /// Number of seconds until playback reaches full volume after starting/resuming. + /// Zero to deactivate (default). + F32 mFadeInTime; + + /// Number of seconds to fade out fading before stopping/pausing. + /// Zero to deactivate (default). + F32 mFadeOutTime; + + /// Easing curve for fade-in. + EaseF mFadeInEase; + + /// Easing curve for fade-out. + EaseF mFadeOutEase; + + /// When mIsLooping is true, the fades will apply to each cycle. Otherwise, only + /// the first cycle will have a fade-in applied and no fade-out happens when a cycle + /// ends. + /// + /// This also affects the playtime that is used to place fades. If mFadeLoops is + /// false, the total playtime so far will be used rather than the playtime of the + /// current cycle. This makes it possible to apply fades that extend across two or + /// more loops of the sound, i.e. are longer than the actual sound duration. + bool mFadeLoops; + + /// The number of seconds of sound data to read per streaming + /// packet. Only relevant if "isStreaming" is true. + U32 mStreamPacketSize; + + /// The number of streaming packets to read and buffer in advance. + /// Only relevant if "isStreaming" is true. + U32 mStreamReadAhead; + + /// Reverb properties for sound playback. + SFXSoundReverbProperties mReverb; + + /// Parameters to which sources playing with this description should automatically + /// connect when created. + StringTableEntry mParameters[ MaxNumParameters ]; + + /// Priority level for sounds. Higher priority sounds are less likely to be + /// culled. + F32 mPriority; + + SFXDescription(); + SFXDescription( const SFXDescription& desc ); + DECLARE_CONOBJECT( SFXDescription ); + static void initPersistFields(); + + // SimDataBlock. + virtual bool onAdd(); + virtual void packData( BitStream* stream ); + virtual void unpackData( BitStream* stream ); + virtual void inspectPostApply(); + + /// Validates the description fixing any + /// parameters that are out of range. + void validate(); +}; + + +#endif // _SFXDESCRIPTION_H_ \ No newline at end of file diff --git a/Engine/source/sfx/sfxDevice.cpp b/Engine/source/sfx/sfxDevice.cpp new file mode 100644 index 000000000..7d0c91b36 --- /dev/null +++ b/Engine/source/sfx/sfxDevice.cpp @@ -0,0 +1,210 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/sfxDevice.h" +#include "sfx/sfxBuffer.h" +#include "sfx/sfxVoice.h" +#include "sfx/sfxInternal.h" +#include "core/tAlgorithm.h" +#include "console/console.h" +#include "console/consoleTypes.h" + + +//----------------------------------------------------------------------------- + +SFXDevice::SFXDevice( const String& name, SFXProvider* provider, bool useHardware, S32 maxBuffers ) + : mName( name ), + mProvider( provider ), + mUseHardware( useHardware ), + mMaxBuffers( maxBuffers ), + mStatNumBufferBytes( 0 ), + mStatNumBuffers( 0 ), + mStatNumVoices( 0 ), + mCaps( 0 ) +{ + AssertFatal( provider, "We must have a provider pointer on device creation!" ); + + VECTOR_SET_ASSOCIATION( mBuffers ); + VECTOR_SET_ASSOCIATION( mVoices ); + + SFXBuffer::smBufferDestroyedSignal.notify( this, &SFXDevice::_removeBuffer ); + SFXVoice::smVoiceDestroyedSignal.notify( this, &SFXDevice::_removeVoice ); + + Con::addVariable( "SFX::Device::numBuffers", TypeS32, &mStatNumBuffers ); + Con::addVariable( "SFX::Device::numVoices", TypeS32, &mStatNumVoices ); + Con::addVariable( "SFX::Device::numBufferBytes", TypeS32, &mStatNumBufferBytes ); +} + +//----------------------------------------------------------------------------- + +SFXDevice::~SFXDevice() +{ + Con::removeVariable( "SFX::Device::numBuffers" ); + Con::removeVariable( "SFX::Device::numVoices" ); + Con::removeVariable( "SFX::Device::numBufferBytes" ); + + _releaseAllResources(); +} + +//----------------------------------------------------------------------------- + +void SFXDevice::_releaseAllResources() +{ + using namespace SFXInternal; + + // Kill the update thread, if there is one. + // Do this first so that further buffer processing + // can be done synchronously by us. + + ThreadSafeRef< SFXUpdateThread > sfxThread = UPDATE_THREAD(); + if( sfxThread != NULL ) + { + gUpdateThread = NULL; // Kill the global reference. + + sfxThread->stop(); + sfxThread->triggerUpdate(); + sfxThread->join(); + + sfxThread = NULL; + } + + // Clean up voices. Do this before cleaning up buffers so that + // resources held by voices that are tied to resources held by buffers + // get released properly. + + SFXVoice::smVoiceDestroyedSignal.remove( this, &SFXDevice::_removeVoice ); + for( VoiceIterator voice = mVoices.begin(); + voice != mVoices.end(); voice++ ) + ( *voice )->destroySelf(); + mVoices.clear(); + + // Clean up buffers. + + SFXBuffer::smBufferDestroyedSignal.remove( this, &SFXDevice::_removeBuffer ); + for( BufferIterator buffer = mBuffers.begin(); + buffer != mBuffers.end(); ++ buffer ) + if( !( *buffer )->isDead() ) + ( *buffer )->destroySelf(); + mBuffers.clear(); + + // Flush all asynchronous requests. + + THREAD_POOL().flushWorkItems(); + + // Clean out the buffer update list and kill + // all buffers that surfaced on the dead list. + // Now the sound buffers are really gone. + + UPDATE_LIST().process(); + PurgeDeadBuffers(); + + // Clean out stats. + + mStatNumBuffers = 0; + mStatNumVoices = 0; + mStatNumBufferBytes = 0; +} + +//----------------------------------------------------------------------------- + +void SFXDevice::update() +{ + using namespace SFXInternal; + + // If we don't have an update thread, do the + // updates now on the main thread. + + if( !UPDATE_THREAD() ) + UPDATE_LIST().process( MAIN_THREAD_PROCESS_TIMEOUT ); + + // Clean out buffers that have surfaced on the dead + // buffer list. + + PurgeDeadBuffers(); +} + +//----------------------------------------------------------------------------- + +void SFXDevice::_addBuffer( SFXBuffer* buffer ) +{ + AssertFatal( buffer, "SFXDevice::_addBuffer() - Got a null buffer!" ); + + // Register the buffer. + + mBuffers.push_back( buffer ); + mStatNumBuffers ++; + mStatNumBufferBytes += buffer->getMemoryUsed(); + + // Start loading the buffer. + + buffer->load(); +} + +//----------------------------------------------------------------------------- + +void SFXDevice::_removeBuffer( SFXBuffer* buffer ) +{ + AssertFatal( buffer, "SFXDevice::_removeBuffer() - Got a null buffer!" ); + + BufferIterator iter = find( mBuffers.begin(), mBuffers.end(), buffer ); + if( iter != mBuffers.end() ) + { + SFXBuffer* buffer = *iter; + + mStatNumBufferBytes -= buffer->getMemoryUsed(); + mStatNumBuffers --; + + mBuffers.erase( iter ); + } +} + +//----------------------------------------------------------------------------- + +void SFXDevice::_addVoice( SFXVoice* voice ) +{ + AssertFatal( voice, "SFXDevice::_addVoice() - Got a null voice!" ); + using namespace SFXInternal; + + // Bind the voice to its buffer. This is deferred up to here in order + // to only bind voices that have been successfully constructed. + + voice->_attachToBuffer(); + + // Register the voice. + + mVoices.push_back( voice ); + mStatNumVoices ++; +} + +//----------------------------------------------------------------------------- + +void SFXDevice::_removeVoice( SFXVoice* voice ) +{ + AssertFatal( voice, "SFXDevice::_removeVoice() - Got null voice!" ); + + VoiceIterator iter = find( mVoices.begin(), mVoices.end(), voice ); + if( iter != mVoices.end() ) + { + mStatNumVoices --; + mVoices.erase( iter ); + } +} diff --git a/Engine/source/sfx/sfxDevice.h b/Engine/source/sfx/sfxDevice.h new file mode 100644 index 000000000..b4d6485e3 --- /dev/null +++ b/Engine/source/sfx/sfxDevice.h @@ -0,0 +1,211 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXDEVICE_H_ +#define _SFXDEVICE_H_ + +#ifndef _PLATFORM_H_ + #include "platform/platform.h" +#endif +#ifndef _TVECTOR_H_ + #include "core/util/tVector.h" +#endif +#ifndef _SFXCOMMON_H_ + #include "sfx/sfxCommon.h" +#endif +#ifndef _THREADSAFEREF_H_ + #include "platform/threads/threadSafeRefCount.h" +#endif + + +class SFXProvider; +class SFXListener; +class SFXBuffer; +class SFXVoice; +class SFXProfile; +class SFXDevice; +class SFXStream; +class SFXDescription; + + + +/// Abstract base class for back-end sound API implementations. +class SFXDevice +{ + public: + + typedef void Parent; + + /// Device capability flags. + enum ECaps + { + CAPS_Reverb = BIT( 0 ), ///< Device supports reverb environments. + CAPS_VoiceManagement = BIT( 1 ), ///< Device manages voices on its own; deactivates virtualization code in SFX system. + CAPS_Occlusion = BIT( 2 ), ///< Device has its own sound occlusion handling (SFXOcclusionManager). + CAPS_DSPEffects = BIT( 3 ), ///< Device implements DSP effects (SFXDSPManager). + CAPS_MultiListener = BIT( 4 ), ///< Device supports multiple listeners. + CAPS_FMODDesigner = BIT( 5 ), ///< FMOD Designer support. + }; + + protected: + + typedef Vector< SFXBuffer* > BufferVector; + typedef Vector< SFXVoice* > VoiceVector; + + typedef BufferVector::iterator BufferIterator; + typedef VoiceVector::iterator VoiceIterator; + + SFXDevice( const String& name, SFXProvider* provider, bool useHardware, S32 maxBuffers ); + + /// The name of this device. + String mName; + + /// The provider which created this device. + SFXProvider* mProvider; + + /// Should the device try to use hardware processing. + bool mUseHardware; + + /// The maximum playback buffers this device will use. + S32 mMaxBuffers; + + /// Current set of sound buffers. + BufferVector mBuffers; + + /// Current set of voices. + VoiceVector mVoices; + + /// Device capabilities. + U32 mCaps; + + /// Current number of buffers. Reflected in $SFX::Device::numBuffers. + U32 mStatNumBuffers; + + /// Current number of voices. Reflected in $SFX::Device::numVoices. + U32 mStatNumVoices; + + /// Current total memory size of sound buffers. Reflected in $SFX::Device::numBufferBytes. + U32 mStatNumBufferBytes; + + /// Register a buffer with the device. + /// This also triggers the buffer's stream packet request chain. + void _addBuffer( SFXBuffer* buffer ); + + /// Unregister the given buffer. + void _removeBuffer( SFXBuffer* buffer ); + + /// Register a voice with the device. + void _addVoice( SFXVoice* voice ); + + /// Unregister the given voice. + virtual void _removeVoice( SFXVoice* buffer ); + + /// Release all resources tied to the device. Can be called repeatedly + /// without harm. It is meant for device destructors that will severe + /// the connection to the sound API and thus need all resources freed + /// before the base destructor is called. + void _releaseAllResources(); + +public: + + virtual ~SFXDevice(); + + /// Returns the provider which created this device. + SFXProvider* getProvider() const { return mProvider; } + + /// Is the device set to use hardware processing. + bool getUseHardware() const { return mUseHardware; } + + /// The maximum number of playback buffers this device will use. + S32 getMaxBuffers() const { return mMaxBuffers; } + + /// Returns the name of this device. + const String& getName() const { return mName; } + + /// Return the device capability flags. + U32 getCaps() const { return mCaps; } + + /// Tries to create a new sound buffer. If creation fails + /// freeing another buffer will usually allow a new one to + /// be created. + /// + /// @param stream The sound data stream. + /// @param description The playback configuration. + /// + /// @return Returns a new buffer or NULL if one cannot be created. + /// + virtual SFXBuffer* createBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ) = 0; + + /// Create a sound buffer directly for a file. This is for + /// devices that implemented their own custom file loading. + /// + /// @note Only implemented on specific SFXDevices. + /// @return Return a new buffer or NULL. + virtual SFXBuffer* createBuffer( const String& fileName, SFXDescription* description ) { return NULL; } + + /// Tries to create a new voice. + /// + /// @param is3d True if the voice should have 3D sound enabled. + /// @param buffer The sound data to play by the voice. + /// + /// @return Returns a new voice or NULL if one cannot be created. + virtual SFXVoice* createVoice( bool is3D, SFXBuffer* buffer ) = 0; + + /// Set the rolloff curve to be used by distance attenuation of 3D sounds. + virtual void setDistanceModel( SFXDistanceModel model ) {} + + /// Set the scale factor to use for doppler effects on 3D sounds. + virtual void setDopplerFactor( F32 factor ) {} + + /// Set the rolloff scale factor for distance attenuation of 3D sounds. + virtual void setRolloffFactor( F32 factor ) {} + + /// Set the global reverb environment. + virtual void setReverb( const SFXReverbProperties& reverb ) {} + + /// Reset the global reverb environment to its default. + virtual void resetReverb() {} + + /// Set the number of concurrent listeners on the device. + /// + /// @note On devices that do not support multiple listeners, any value + /// other than 1 will be ignored. + virtual void setNumListeners( U32 num ) {} + + /// Set the properties of the given listener. + /// + /// @note On devices that do not support multiple listeners, only setting + /// the properties on index=0 will have a effect. + virtual void setListener( U32 index, const SFXListenerProperties& listener ) {} + + /// Return the current total number of sound buffers. + U32 getBufferCount() const { return mBuffers.size(); } + + /// Return the current total number of voices. + U32 getVoiceCount() const { return mVoices.size(); } + + /// Called from SFXSystem to do any updates the device may need to make. + virtual void update(); +}; + + +#endif // _SFXDEVICE_H_ diff --git a/Engine/source/sfx/sfxEnvironment.cpp b/Engine/source/sfx/sfxEnvironment.cpp new file mode 100644 index 000000000..6850c28df --- /dev/null +++ b/Engine/source/sfx/sfxEnvironment.cpp @@ -0,0 +1,326 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/sfxEnvironment.h" +#include "sim/netConnection.h" +#include "core/stream/bitStream.h" +#include "core/module.h" + + +IMPLEMENT_CO_DATABLOCK_V1( SFXEnvironment ); + + +// Reverb flags. +static const U32 sReverbFlagDecayTimeScale = 0x001; +static const U32 sReverbFlagReflectionsScale = 0x002; +static const U32 sReverbFlagReflectionsDelayScale = 0x004; +static const U32 sReverbFlagReverbScale = 0x008; +static const U32 sReverbFlagReverbDelayScale = 0x010; +static const U32 sReverbFlagDecayHFLimit = 0x020; +static const U32 sReverbFlagEchoTimeScale = 0x040; +static const U32 sReverbFlagModulationTimeScale = 0x080; +static const U32 sReverbFlagCore0 = 0x100; +static const U32 sReverbFlagCore1 = 0x200; +static const U32 sReverbFlagHighQualityReverb = 0x400; +static const U32 sReverbFlagHighQualityDPL2Reverb = 0x800; + +AFTER_MODULE_INIT( SFX ) +{ + Con::addConstant( "SFXEnvironment::REVERB_DECAYTIMESCALE", TypeS32, &sReverbFlagDecayTimeScale, + "SFXEnvironment::envSize affects reverberation decay time.\n" + "@see SFXEnvironment::flags\n\n" + "@ingroup SFXEnvironment" ); + Con::addConstant( "SFXEnvironment::REVERB_REFLECTIONSSCALE", TypeS32, &sReverbFlagReflectionsScale, + "SFXEnvironment::envSize affects reflection level.\n" + "@see SFXEnvironment::flags\n\n" + "@ingroup SFXEnvironment" ); + Con::addConstant( "SFXEnvironment::REVERB_REFLECTIONSDELAYSCALE", TypeS32, &sReverbFlagReflectionsScale, + "SFXEnvironment::envSize affects initial reflection delay time.\n" + "@see SFXEnvironment::flags\n\n" + "@ingroup SFXEnvironment" ); + Con::addConstant( "SFXEnvironment::REVERB_REVERBSCALE", TypeS32, &sReverbFlagReverbScale, + "SFXEnvironment::envSize affects reflections level.\n" + "@see SFXEnvironment::flags\n\n" + "@ingroup SFXEnvironment" ); + Con::addConstant( "SFXEnvironment::REVERB_REVERBDELAYSCALE", TypeS32, &sReverbFlagReverbDelayScale, + "SFXEnvironment::envSize affects late reverberation delay time.\n" + "@see SFXEnvironment::flags\n\n" + "@ingroup SFXEnvironment" ); + Con::addConstant( "SFXEnvironment::REVERB_DECAYHFLIMIT", TypeS32, &sReverbFlagDecayHFLimit, + "SFXEnvironment::airAbsorptionHF affects SFXEnvironment::decayHFRatio.\n" + "@see SFXEnvironment::flags\n\n" + "@ingroup SFXEnvironment" ); + Con::addConstant( "SFXEnvironment::REVERB_ECHOTIMESCALE", TypeS32, &sReverbFlagEchoTimeScale, + "SFXEnvironment::envSize affects echo time.\n" + "@see SFXEnvironment::flags\n\n" + "@ingroup SFXEnvironment" ); + Con::addConstant( "SFXEnvironment::REVERB_MODULATIONTIMESCALE", TypeS32, &sReverbFlagModulationTimeScale, + "SFXEnvironment::envSize affects modulation time.\n" + "@see SFXEnvironment::flags\n\n" + "@ingroup SFXEnvironment" ); + Con::addConstant( "SFXEnvironment::REVERB_CORE0", TypeS32, &sReverbFlagCore0, + "PS2 Only - Reverb is applied to CORE0 (hw voices 0-23).\n" + "@see SFXEnvironment::flags\n\n" + "@ingroup SFXEnvironment" ); + Con::addConstant( "SFXEnvironment::REVERB_CORE1", TypeS32, &sReverbFlagCore1, + "PS2 Only - Reverb is applied to CORE1 (hw voices 24-47).\n" + "@see SFXEnvironment::flags\n\n" + "@ingroup SFXEnvironment" ); + Con::addConstant( "SFXEnvironment::REVERB_HIGHQUALITYREVERB", TypeS32, &sReverbFlagHighQualityReverb, + "GameCube/Wii Only - Use high-quality reverb.\n" + "@see SFXEnvironment::flags\n\n" + "@ingroup SFXEnvironment" ); + Con::addConstant( "SFXEnvironment::REVERB_HIGHQUALITYDPL2REVERB", TypeS32, &sReverbFlagHighQualityDPL2Reverb, + "GameCube/Wii Only - Use high-quality DPL2 reverb.\n" + "@see SFXEnvironment::flags\n\n" + "@ingroup SFXEnvironment" ); +} + + +ConsoleDocClass( SFXEnvironment, + "@brief Description of a reverb environment.\n\n" + + "A reverb environment specifies how the audio mixer should render advanced environmental audio " + "effects. \n\n" + + "To use reverb environments in your level, set up one or more ambient audio spaces, assign " + "reverb environments appropriately, and then attach the SFXAmbiences to your LevelInfo (taking effect " + "globally) or Zone objects (taking effect locally).\n\n" + + "To define your own custom reverb environments, it is usually easiest to adapt one of the pre-existing " + "reverb definitions:\n" + + "@tsexample_nopar\n" + "singleton SFXEnvironment( AudioEnvCustomUnderwater : AudioEnvUnderwater )\n" + "{\n" + " // Override select properties from AudioEnvUnderwater here.\n" + "};\n" + "@endtsexample\n\n" + + "In the Datablock Editor, this can be done by selecting an existing environment to copy from when creating " + "the SFXEnvironment datablock.\n\n" + + "For a precise description of reverb audio and the properties of this class, please consult the EAX " + "documentation.\n\n" + + "All SFXEnvironment instances are automatically added to the global @c SFXEnvironmentSet.\n\n" + + "@see http://www.atc.creative.com/algorithms/eax20.pdf\n" + "@see http://connect.creativelabs.com/developer/Gaming/Forms/AllItems.aspx\n" + "@see SFXAmbience::environment\n\n" + "@ref SFX_reverb\n" + "@ingroup SFX\n" +); + + +//----------------------------------------------------------------------------- + +SFXEnvironment::SFXEnvironment() +{ +} + +//----------------------------------------------------------------------------- + +void SFXEnvironment::initPersistFields() +{ + addGroup( "Reverb" ); + + addField( "envSize", TypeF32, Offset( mReverb.mEnvSize, SFXEnvironment ), + "Environment size in meters." ); + addField( "envDiffusion", TypeF32, Offset( mReverb.mEnvDiffusion, SFXEnvironment ), + "Environment diffusion." ); + addField( "room", TypeS32, Offset( mReverb.mRoom, SFXEnvironment ), + "Room effect level at mid-frequencies." ); + addField( "roomHF", TypeS32, Offset( mReverb.mRoomHF, SFXEnvironment ), + "Relative room effect level at high frequencies." ); + addField( "roomLF", TypeS32, Offset( mReverb.mRoomLF, SFXEnvironment ), + "Relative room effect level at low frequencies." ); + addField( "decayTime", TypeF32, Offset( mReverb.mDecayTime, SFXEnvironment ), + "Reverberation decay time at mid frequencies." ); + addField( "decayHFRatio", TypeF32, Offset( mReverb.mDecayHFRatio, SFXEnvironment ), + "High-frequency to mid-frequency decay time ratio." ); + addField( "decayLFRatio", TypeF32, Offset( mReverb.mDecayLFRatio, SFXEnvironment ), + "Low-frequency to mid-frequency decay time ratio." ); + addField( "reflections", TypeS32, Offset( mReverb.mReflections, SFXEnvironment ), + "Early reflections level relative to room effect." ); + addField( "reflectionsDelay", TypeF32, Offset( mReverb.mReflectionsDelay, SFXEnvironment ), + "Initial reflection delay time." ); + addField( "reflectionsPan", TypeF32, Offset( mReverb.mReflectionsPan, SFXEnvironment ), 3, + "Early reflections panning vector." ); + addField( "reverb", TypeS32, Offset( mReverb.mReverb, SFXEnvironment ), + "Late reverberation level relative to room effect." ); + addField( "reverbDelay", TypeF32, Offset( mReverb.mReverbDelay, SFXEnvironment ), + "Late reverberation delay time relative to initial reflection." ); + addField( "reverbPan", TypeF32, Offset( mReverb.mReverbPan, SFXEnvironment ), 3, + "Late reverberation panning vector." ); + addField( "echoTime", TypeF32, Offset( mReverb.mEchoTime, SFXEnvironment ), + "Echo time." ); + addField( "echoDepth", TypeF32, Offset( mReverb.mEchoDepth, SFXEnvironment ), + "Echo depth." ); + addField( "modulationTime", TypeF32, Offset( mReverb.mModulationTime, SFXEnvironment ), + "Modulation time." ); + addField( "modulationDepth", TypeF32, Offset( mReverb.mModulationDepth, SFXEnvironment ), + "Modulation depth." ); + addField( "airAbsorptionHF", TypeF32, Offset( mReverb.mAirAbsorptionHF, SFXEnvironment ), + "Change in level per meter at high frequencies." ); + addField( "HFReference", TypeF32, Offset( mReverb.mHFReference, SFXEnvironment ), + "Reference high frequency in Hertz." ); + addField( "LFReference", TypeF32, Offset( mReverb.mLFReference, SFXEnvironment ), + "Reference low frequency in Hertz." ); + addField( "roomRolloffFactor", TypeF32, Offset( mReverb.mRoomRolloffFactor, SFXEnvironment ), + "Logarithmic distance attenuation rolloff scale factor for reverb room size effect." ); + addField( "diffusion", TypeF32, Offset( mReverb.mDiffusion, SFXEnvironment ), + "Value that controls the echo density in the late reverberation decay." ); + addField( "density", TypeF32, Offset( mReverb.mDensity, SFXEnvironment ), + "Value that controls the modal density in the late reverberation decay." ); + addField( "flags", TypeS32, Offset( mReverb.mFlags, SFXEnvironment ), + "A bitfield of reverb flags.\n" + "@see REVERB_DECAYTIMESCALE\n" + "@see REVERB_REFLECTIONSSCALE\n" + "@see REVERB_REFLECTIONSDELAYSCALE\n" + "@see REVERB_REVERBSCALE\n" + "@see REVERB_REVERBDELAYSCALE\n" + "@see REVERB_DECAYHFLIMIT\n" + "@see REVERB_ECHOTIMESCALE\n" + "@see REVERB_MODULATIONTIMESCALE\n" + "@see REVERB_CORE0\n" + "@see REVERB_CORE1\n" + "@see REVERB_HIGHQUALITYREVERB\n" + "@see REVERB_HIGHQUALITYDPL2REVERB\n" ); + + endGroup( "Reverb" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +bool SFXEnvironment::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + Sim::getSFXEnvironmentSet()->addObject( this ); + + return true; +} + +//----------------------------------------------------------------------------- + +bool SFXEnvironment::preload( bool server, String& errorStr ) +{ + if( !Parent::preload( server, errorStr ) ) + return false; + + validate(); + + return true; +} + +//----------------------------------------------------------------------------- + +void SFXEnvironment::inspectPostApply() +{ + Parent::inspectPostApply(); + validate(); +} + +//----------------------------------------------------------------------------- + +void SFXEnvironment::validate() +{ + mReverb.validate(); +} + +//----------------------------------------------------------------------------- + +void SFXEnvironment::packData( BitStream* stream ) +{ + Parent::packData( stream ); + + stream->write( mReverb.mEnvSize ); + stream->write( mReverb.mEnvDiffusion ); + stream->write( mReverb.mRoom ); + stream->write( mReverb.mRoomHF ); + stream->write( mReverb.mRoomLF ); + stream->write( mReverb.mDecayTime ); + stream->write( mReverb.mDecayHFRatio ); + stream->write( mReverb.mDecayLFRatio ); + stream->write( mReverb.mReflections ); + stream->write( mReverb.mReflectionsDelay ); + stream->write( mReverb.mReflectionsPan[ 0 ] ); + stream->write( mReverb.mReflectionsPan[ 1 ] ); + stream->write( mReverb.mReflectionsPan[ 2 ] ); + stream->write( mReverb.mReverb ); + stream->write( mReverb.mReverbDelay ); + stream->write( mReverb.mReverbPan[ 0 ] ); + stream->write( mReverb.mReverbPan[ 1 ] ); + stream->write( mReverb.mReverbPan[ 2 ] ); + stream->write( mReverb.mEchoTime ); + stream->write( mReverb.mEchoDepth ); + stream->write( mReverb.mModulationTime ); + stream->write( mReverb.mModulationDepth ); + stream->write( mReverb.mAirAbsorptionHF ); + stream->write( mReverb.mHFReference ); + stream->write( mReverb.mLFReference ); + stream->write( mReverb.mRoomRolloffFactor ); + stream->write( mReverb.mDiffusion ); + stream->write( mReverb.mDensity ); + stream->write( mReverb.mFlags ); +} + +//----------------------------------------------------------------------------- + +void SFXEnvironment::unpackData( BitStream* stream ) +{ + Parent::unpackData( stream ); + + stream->read( &mReverb.mEnvSize ); + stream->read( &mReverb.mEnvDiffusion ); + stream->read( &mReverb.mRoom ); + stream->read( &mReverb.mRoomHF ); + stream->read( &mReverb.mRoomLF ); + stream->read( &mReverb.mDecayTime ); + stream->read( &mReverb.mDecayHFRatio ); + stream->read( &mReverb.mDecayLFRatio ); + stream->read( &mReverb.mReflections ); + stream->read( &mReverb.mReflectionsDelay ); + stream->read( &mReverb.mReflectionsPan[ 0 ] ); + stream->read( &mReverb.mReflectionsPan[ 1 ] ); + stream->read( &mReverb.mReflectionsPan[ 2 ] ); + stream->read( &mReverb.mReverb ); + stream->read( &mReverb.mReverbDelay ); + stream->read( &mReverb.mReverbPan[ 0 ] ); + stream->read( &mReverb.mReverbPan[ 1 ] ); + stream->read( &mReverb.mReverbPan[ 2 ] ); + stream->read( &mReverb.mEchoTime ); + stream->read( &mReverb.mEchoDepth ); + stream->read( &mReverb.mModulationTime ); + stream->read( &mReverb.mModulationDepth ); + stream->read( &mReverb.mAirAbsorptionHF ); + stream->read( &mReverb.mHFReference ); + stream->read( &mReverb.mLFReference ); + stream->read( &mReverb.mRoomRolloffFactor ); + stream->read( &mReverb.mDiffusion ); + stream->read( &mReverb.mDensity ); + stream->read( &mReverb.mFlags ); +} diff --git a/Engine/source/sfx/sfxEnvironment.h b/Engine/source/sfx/sfxEnvironment.h new file mode 100644 index 000000000..372d4138b --- /dev/null +++ b/Engine/source/sfx/sfxEnvironment.h @@ -0,0 +1,73 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXENVIRONMENT_H_ +#define _SFXENVIRONMENT_H_ + +#ifndef _SIMDATABLOCK_H_ + #include "console/simDatablock.h" +#endif +#ifndef _CONSOLETYPES_H_ + #include "console/consoleTypes.h" +#endif +#ifndef _SFXCOMMON_H_ + #include "sfx/sfxCommon.h" +#endif + + +/// A datablock that defines a reverb environment. +class SFXEnvironment : public SimDataBlock +{ + public: + + typedef SimDataBlock Parent; + + protected: + + /// The reverb properties. + SFXReverbProperties mReverb; + + public: + + SFXEnvironment(); + + /// + void validate(); + + DECLARE_CONOBJECT( SFXEnvironment ); + DECLARE_CATEGORY( "SFX" ); + DECLARE_DESCRIPTION( "A reverb environment." ); + + static void initPersistFields(); + + virtual bool onAdd(); + virtual bool preload( bool server, String& errorStr ); + virtual void packData( BitStream* stream ); + virtual void unpackData( BitStream* stream ); + virtual void inspectPostApply(); + + /// @return The reverb properties of the sound environment. + const SFXReverbProperties& getReverb() const { return mReverb; } +}; + + +#endif // _SFXENVIRONMENT_H_ \ No newline at end of file diff --git a/Engine/source/sfx/sfxFileStream.cpp b/Engine/source/sfx/sfxFileStream.cpp new file mode 100644 index 000000000..9d23a5691 --- /dev/null +++ b/Engine/source/sfx/sfxFileStream.cpp @@ -0,0 +1,173 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/sfxFileStream.h" +#include "core/stream/fileStream.h" +#include "console/console.h" +#include "core/util/safeDelete.h" + + +SFXFileStream::ExtensionsVector SFXFileStream::smExtensions( __FILE__, __LINE__ ); +SFXFileStream::CreateFnsVector SFXFileStream::smCreateFns( __FILE__, __LINE__ ); + + +void SFXFileStream::registerExtension( String ext, SFXFILESTREAM_CREATE_FN create_fn ) +{ + // Register the stream creation first. + smExtensions.push_back( ext ); + smCreateFns.push_back( create_fn ); +} + +void SFXFileStream::unregisterExtension( String ext ) +{ + for( ExtensionsVector::iterator iter = smExtensions.begin(); + iter != smExtensions.end(); ++ iter ) + if( ( *iter ).equal( ext, String::NoCase ) ) + { + smExtensions.erase( iter ); + return; + } +} + +SFXFileStream* SFXFileStream::create( String filename ) +{ + //RDTODO: if original file has an extension, we should try that first + + // First strip off our current extension (validating + // against a list of known extensions so that we don't + // strip off the last part of a file name with a dot in it. + + String noExtension = Platform::stripExtension( filename, smExtensions ); + + SFXFileStream *sfxStream = NULL; + + for( U32 i = 0; i < smExtensions.size(); i++ ) + { + String testName = noExtension + smExtensions[ i ]; + + Stream *stream = FileStream::createAndOpen( testName, Torque::FS::File::Read ); + if ( !stream ) + continue; + + // Note that the creation function swallows up the + // resource stream and will take care of deleting it. + sfxStream = smCreateFns[i]( stream ); + if ( sfxStream ) + return sfxStream; + } + + return NULL; +} + +bool SFXFileStream::exists( String filename ) +{ + // First strip off our current extension (validating + // against a list of known extensions so that we don't + // strip off the last part of a file name with a dot in it. + + String noExtension = Platform::stripExtension( filename, smExtensions ); + + for( U32 i = 0; i < smExtensions.size(); i++ ) + { + String testName = noExtension + smExtensions[ i ]; + if( Torque::FS::IsFile( testName ) ) + return true; + } + + return false; +} + +SFXFileStream::SFXFileStream() + : mStream( NULL ), + mOwnStream( false ), + mFormat( 0, 0, 0 ), + mSamples( 0 ) +{ +} + +SFXFileStream::SFXFileStream( const SFXFileStream& cloneFrom ) +{ + mStream = cloneFrom.mStream->clone(); + if( !mStream ) + { + Con::errorf( "SFXFileStream::SFXFileStream() - Failed to clone source stream" ); + return; + } + + mOwnStream = true; + mFormat = cloneFrom.mFormat; + mSamples = cloneFrom.mSamples; +} + + +SFXFileStream::~SFXFileStream() +{ + // If the stream is still open, close it now. _close() + // should usually be called by the destructor of derived classes, + // but it their constructor fails, these won't even run. + + if( mStream && mOwnStream ) + SAFE_DELETE( mStream ); +} + +bool SFXFileStream::open( Stream *stream, bool ownStream ) +{ + AssertFatal( stream, "SFXFileStream::open() - Got null stream!" ); + + close(); + + mStream = stream; + mOwnStream = ownStream; + + if( _readHeader() ) + { + reset(); // Make sure we're set to read sample data. + return true; + } + else + return false; +} + +void SFXFileStream::close() +{ + if ( !mStream ) + return; + + // Let the overloaded class cleanup. + _close(); + + // We only close it if we own it. + if ( mOwnStream ) + SAFE_DELETE( mStream ); + + // Reset these to make it easier to detect bugs. + mFormat.set( 0, 0, 0 ); + mSamples = 0; +} + +bool SFXFileStream::isEOS() const +{ + if ( !mStream ) + return true; + + return mStream->getStatus() != Stream::Ok; +} diff --git a/Engine/source/sfx/sfxFileStream.h b/Engine/source/sfx/sfxFileStream.h new file mode 100644 index 000000000..54c578081 --- /dev/null +++ b/Engine/source/sfx/sfxFileStream.h @@ -0,0 +1,115 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXFILESTREAM_H_ +#define _SFXFILESTREAM_H_ + +#ifndef _SFXSTREAM_H_ +# include "sfx/sfxStream.h" +#endif +#ifndef _TVECTOR_H_ +# include "core/util/tVector.h" +#endif +#ifndef _TORQUE_STRING_H_ +# include "core/util/str.h" +#endif + + +class Stream; +class SFXFileStream; + +/// +typedef SFXFileStream* ( *SFXFILESTREAM_CREATE_FN )( Stream *stream ); + +/// An SFXStream that streams from a file. +class SFXFileStream : public SFXStream +{ + protected: + typedef Vector< String > ExtensionsVector; + typedef Vector< SFXFILESTREAM_CREATE_FN > CreateFnsVector; + + static ExtensionsVector smExtensions; + static CreateFnsVector smCreateFns; + + /// The file stream we're reading from. + Stream *mStream; + + /// If true then we're responsible for closing the stream. + bool mOwnStream; + + /// The format of the data in the stream. + SFXFormat mFormat; + + /// The number of samples in the data stream. + U32 mSamples; + + /// Constructs the stream in an uninitilized state. + SFXFileStream(); + + /// + SFXFileStream( const SFXFileStream& cloneFrom ); + + /// Overloaded in the derived classes to read + /// the file header. It should initialize + /// mFormat and mSamples. + virtual bool _readHeader() = 0; + + /// Overloaded for cleanup of file format + /// specific structures. + virtual void _close() = 0; + + public: + + /// + static void registerExtension( String ext, SFXFILESTREAM_CREATE_FN create_fn ); + + /// + static void unregisterExtension( String ext ); + + /// This is a helper function used to create an appropriate SFXStream + /// for the requested sound file. + /// + /// @param filename The sound file path with or without extension. + /// + static SFXFileStream* create( String filename ); + + /// + static bool exists( String filename ); + + /// Destructor. + virtual ~SFXFileStream(); + + /// Opens and optionally takes ownership of the stream. + bool open( Stream *stream, bool ownStream = false ); + + /// Closes the stream. + void close(); + + // SFXStream. + const SFXFormat& getFormat() const { return mFormat; } + U32 getSampleCount() const { return mSamples; } + U32 getDataLength() const { return mSamples * mFormat.getBytesPerSample(); } + U32 getDuration() const { return mFormat.getDuration( mSamples ); } + bool isEOS() const; +}; + +#endif // _SFXFILESTREAM_H_ diff --git a/Engine/source/sfx/sfxInternal.cpp b/Engine/source/sfx/sfxInternal.cpp new file mode 100644 index 000000000..37aea5d26 --- /dev/null +++ b/Engine/source/sfx/sfxInternal.cpp @@ -0,0 +1,200 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/sfxInternal.h" +#include "sfx/sfxDescription.h" +#include "core/util/safeDelete.h" +#include "platform/threads/threadPoolAsyncIO.h" + + +/// @file +/// Implementation of async sound I/O. + + +//#define DEBUG_SPEW + + +namespace SFXInternal { + + +ThreadSafeRef< SFXUpdateThread > gUpdateThread; +ThreadSafeRef< SFXBufferProcessList > gBufferUpdateList = new SFXBufferProcessList; +ThreadSafeDeque< SFXBuffer* > gDeadBufferList; + + +//========================================================================== +// SFXAsyncStream implementation. +//========================================================================== + +//-------------------------------------------------------------------------- + +SFXAsyncStream::SFXAsyncStream( const SFXStreamRef& stream, + bool isIncremental, + U32 streamPacketLength, + U32 numReadAhead, + bool isLooping ) + : Parent( stream, + isIncremental + ? streamPacketLength + * stream->getFormat().getSamplesPerSecond() + * stream->getFormat().getBytesPerSample() // Streamed buffer; read in incremental packets. + : stream->getDataLength(), // Non-streamed buffer; read entire stream in one packet. + stream->getDataLength() // Read all remaining data in stream. + - ( dynamic_cast< IPositionable< U32 >* >( stream.ptr() ) + ? dynamic_cast< IPositionable< U32 >* >( stream.ptr() )->getPosition() + : 0 ), + numReadAhead, + isLooping, + &THREAD_POOL() ), + mReadSilenceAtEnd( false ) +{ +} + +//-------------------------------------------------------------------------- + +void SFXAsyncStream::_onArrival( SFXStreamPacket* const& packet ) +{ + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXAsyncStream] Packet arrived" ); + #endif + + Parent::_onArrival( packet ); + + // Some buffer may be waiting for this data so trigger + // an update. + + if( !mIsStopped ) + TriggerUpdate(); +} + +//-------------------------------------------------------------------------- + +void SFXAsyncStream::_requestNext() +{ + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXAsyncStream] Next packet requested" ); + #endif + + if( !mNumRemainingSourceElements && mReadSilenceAtEnd ) + { + // Push an artificial packet of silence. + + SFXStreamPacket* packet = _newPacket( mPacketSize ); + packet->mIndex = mNextPacketIndex; + mNextPacketIndex ++; + mReadSilenceAtEnd = false; + dMemset( packet->data, 0, packet->size ); + packet->mIsLast = true; + + _onArrival( packet ); + } + else + Parent::_requestNext(); +} + +//========================================================================== +// SFXWrapAroundBuffer implementation. +//========================================================================== + +//-------------------------------------------------------------------------- + +SFXWrapAroundBuffer::SFXWrapAroundBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ) + : Parent( stream, description ), + mWriteOffset( 0 ) +{ + // Determine the device buffer metrics. + + const U32 maxQueuedPackets = isStreaming() ? SFXAsyncQueue::DEFAULT_STREAM_QUEUE_LENGTH : 1; + const U32 packetSize = mAsyncState->mStream->getPacketSize(); + + mBufferSize = maxQueuedPackets * packetSize; + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXWrapAroundBuffer] size=%i, packets=%i", + mBufferSize, maxQueuedPackets ); + #endif + + // For streaming buffers that are not looping, add a packet of silence to the + // source stream. + + if( isStreaming() && !description->mIsLooping ) + mAsyncState->mStream->setReadSilenceAtEnd( true ); +} + +//-------------------------------------------------------------------------- + +void SFXWrapAroundBuffer::write( SFXStreamPacket* const* packets, U32 num ) +{ + AssertFatal( SFXInternal::isSFXThread(), "SFXWrapAroundBuffer::write() - not on SFX thread" ); + + for( U32 i = 0; i < num; ++ i ) + { + const SFXStreamPacket* packet = packets[ i ]; + + // Determine where in the buffer to copy the data to. In case we are crossing over + // the wrap-around point, we need to copy in two slices. + + U32 offset1 = 0; + U32 offset2 = 0; + U32 numBytes1 = 0; + U32 numBytes2 = 0; + + offset1 = mWriteOffset % mBufferSize; + numBytes1 = packet->size; + + if( offset1 + numBytes1 > mBufferSize ) + { + // Crossing wrap-around point. + + numBytes1 = mBufferSize - offset1; + numBytes2 = packet->size - numBytes1; + } + + offset2 = offset1 + numBytes1; + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXWrapAroundBuffer] writing %i bytes from packet #%i at %i (stream offset: %i)", + numBytes1, packet->mIndex, offset1, mWriteOffset ); + #endif + + // Copy the packet data. + + _copyData( offset1, packet->data, numBytes1 ); + if( numBytes2 > 0 ) + { + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXWrapAroundBuffer] writing %i more bytes at %i", + numBytes2, offset2 ); + #endif + + _copyData( offset2, &packet->data[ numBytes1 ], numBytes2 ); + } + + dFetchAndAdd( mWriteOffset, packet->size ); + + // Free the packet. + + destructSingle( packet ); + } +} + +} // namespace SFXInternal diff --git a/Engine/source/sfx/sfxInternal.h b/Engine/source/sfx/sfxInternal.h new file mode 100644 index 000000000..137456eac --- /dev/null +++ b/Engine/source/sfx/sfxInternal.h @@ -0,0 +1,456 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXINTERNAL_H_ +#define _SFXINTERNAL_H_ + +#ifndef _THREADPOOL_H_ + #include "platform/threads/threadPool.h" +#endif +#ifndef _ASYNCUPDATE_H_ + #include "platform/async/asyncUpdate.h" +#endif +#ifndef _ASYNCPACKETSTREAM_H_ + #include "platform/async/asyncPacketStream.h" +#endif +#ifndef _ASYNCPACKETQUEUE_H_ + #include "platform/async/asyncPacketQueue.h" +#endif +#ifndef _SFXSTREAM_H_ + #include "sfx/sfxStream.h" +#endif +#ifndef _SFXBUFFER_H_ + #include "sfx/sfxBuffer.h" +#endif +#ifndef _SFXVOICE_H_ + #include "sfx/sfxVoice.h" +#endif +#ifndef _CONSOLE_H_ + #include "console/console.h" +#endif +#ifndef _TSINGLETON_H_ + #include "core/util/tSingleton.h" +#endif + + +/// @file +/// Mostly internal definitions for sound stream handling. +/// The code here is used by SFXBuffer for asynchronously loading +/// sample data from sound files, both for streaming buffers +/// as well as for "normal" buffers. +/// +/// This is all pretty low-level code here. + + +namespace SFXInternal { + +typedef AsyncUpdateThread SFXUpdateThread; +typedef AsyncUpdateList SFXBufferProcessList; + +//-------------------------------------------------------------------------- +// Async sound packets. +//-------------------------------------------------------------------------- + +/// Sound stream packets are raw byte buffers containing PCM sample data. +class SFXStreamPacket : public AsyncPacket< U8 > +{ + public: + + typedef AsyncPacket< U8 > Parent; + + SFXStreamPacket() {} + SFXStreamPacket( U8* data, U32 size, bool ownMemory = false ) + : Parent( data, size, ownMemory ) {} + + /// The format of the sound samples in the packet. + SFXFormat mFormat; + + /// @return the number of samples contained in the packet. + U32 getSampleCount() const { return ( mSizeActual / mFormat.getBytesPerSample() ); } +}; + +//-------------------------------------------------------------------------- +// Async SFXStream I/O. +//-------------------------------------------------------------------------- + +/// Asynchronous sound data stream that delivers sound data +/// in discrete packets. +class SFXAsyncStream : public AsyncPacketBufferedInputStream< SFXStreamRef, SFXStreamPacket > +{ + public: + + typedef AsyncPacketBufferedInputStream< SFXStreamRef, SFXStreamPacket > Parent; + + enum + { + /// The number of seconds of sample data to load per streaming packet by default. + /// Set this reasonably high to ensure the system is able to cope with latencies + /// in the buffer update chain. + DEFAULT_STREAM_PACKET_LENGTH = 8 + }; + + protected: + + /// If true, the stream reads one packet of silence beyond the + /// sound streams actual sound data. This is to avoid wrap-around + /// playback queues running into old data when there is a delay + /// in playback being stopped. + /// + /// @note The silence packet is not counting towards stream + /// playback time. + bool mReadSilenceAtEnd; + + // AsyncPacketStream. + virtual SFXStreamPacket* _newPacket( U32 packetSize ) + { + SFXStreamPacket* packet = Parent::_newPacket( packetSize ); + packet->mFormat = getSourceStream()->getFormat(); + return packet; + } + virtual void _requestNext(); + virtual void _onArrival( SFXStreamPacket* const& packet ); + virtual void _newReadItem( PacketReadItemRef& outRef, SFXStreamPacket* packet, U32 numElements ) + { + if( !this->mNumRemainingSourceElements && mReadSilenceAtEnd ) + packet->mIsLast = false; + Parent::_newReadItem( outRef, packet, numElements ); + } + + public: + + /// Construct a new async sound stream reading data from "stream". + /// + /// @param stream The sound data source stream. + /// @param isIncremental If true, "stream" is read in packets of "streamPacketLength" size + /// each; otherwise the stream is read in a single packet containing the entire stream. + /// @param streamPacketLength Seconds of sample data to read per streaming packet. Only + /// relevant if "isIncremental" is true. + /// @param numReadAhead Number of stream packets to read and buffer in advance. + /// @param isLooping If true, the packet stream infinitely loops over "stream". + SFXAsyncStream( const SFXStreamRef& stream, + bool isIncremental, + U32 streamPacketLength = DEFAULT_STREAM_PACKET_LENGTH, + U32 numReadAhead = DEFAULT_STREAM_LOOKAHEAD, + bool isLooping = false ); + + /// Returns true if the stream will read a packet of silence after the actual sound data. + U32 getReadSilenceAtEnd() const { return mReadSilenceAtEnd; } + + /// Set whether the stream should read one packet of silence past the + /// actual sound data. This is useful for situations where continued + /// playback may run into old data. + void setReadSilenceAtEnd( bool value ) { mReadSilenceAtEnd = value; } + + /// Return the playback time of a single sound packet in milliseconds. + /// For non-incremental streams, this will be the duration of the + /// entire stream. + U32 getPacketDuration() const + { + const SFXFormat& format = getSourceStream()->getFormat(); + return format.getDuration( mPacketSize / format.getBytesPerSample() ); + } +}; + +//-------------------------------------------------------------------------- +// Voice time source wrapper. +//-------------------------------------------------------------------------- + +/// Wrapper around SFXVoice that yields the raw underlying sample position +/// rather than the virtualized position returned by SFXVoice::getPosition(). +class SFXVoiceTimeSource +{ + public: + + typedef void Parent; + + protected: + + /// The voice to sample the position from. + SFXVoice* mVoice; + + /// Last position returned by voice. + mutable U32 mLastPos; + + public: + + SFXVoiceTimeSource( SFXVoice* voice ) + : mVoice( voice ), mLastPos( 0 ) {} + + U32 getPosition() const + { + U32 samplePos = mVoice->_tell(); + + // The device playback cursor may snap back to an undefined value as soon + // as all the data has been consumed. However, for us to be a reliable + // time source, we can't let that happen so as soon as the device play cursor + // goes back to a sample position we have already passed, we start reporting an + // end-of-stream position. + + if( samplePos < mLastPos && mVoice->mBuffer != NULL ) + samplePos = mVoice->mBuffer->getNumSamples(); + else + mLastPos = samplePos; + + return samplePos; + } +}; + +//-------------------------------------------------------------------------- +// Async sound packet queue. +//-------------------------------------------------------------------------- + +/// An async stream queue that writes sound packets to SFXBuffers in sync +/// to the playback of an SFXVoice. +/// +/// Sound packet queues use sample counts as tick counts. +class SFXAsyncQueue : public AsyncPacketQueue< SFXStreamPacket*, SFXVoiceTimeSource, SFXBuffer* > +{ + public: + + typedef AsyncPacketQueue< SFXStreamPacket*, SFXVoiceTimeSource, SFXBuffer* > Parent; + + enum + { + /// The number of stream packets that the playback queue for streaming + /// sounds will be sliced into. This should generally be left at + /// three since there is an overhead incurred for each additional + /// segment. Having three segments gives one segment for current + /// immediate playback, one segment as intermediate buffer, and one segment + /// for stream writes. + DEFAULT_STREAM_QUEUE_LENGTH = 3, + }; + + /// Construct a new sound queue that pushes sound packets to "buffer" in sync + /// to the playback of "voice". + /// + /// @param voice The SFXVoice to synchronize to. + /// @param buffer The sound buffer to push sound packets to. + SFXAsyncQueue( SFXVoice* voice, + SFXBuffer* buffer, + bool looping = false ) + : Parent( DEFAULT_STREAM_QUEUE_LENGTH, + voice, + buffer, + ( looping + ? 0 + : ( buffer->getDuration() * ( buffer->getFormat().getSamplesPerSecond() / 1000 ) ) - voice->mOffset ) ) {} +}; + +//-------------------------------------------------------------------------- +// SFXBuffer with a wrap-around buffering scheme. +//-------------------------------------------------------------------------- + +/// Buffer that uses wrap-around packet buffering. +/// +/// This class automatically coordinates retrieval and submitting of +/// sound packets and also protects against play cursors running beyond +/// the last packet by making sure some silence is submitted after the +/// last packet (does not count towards playback time). +/// +/// @note Note that the reaction times of this class depend on the SFXDevice +/// triggering timely SFXBuffer:update() calls. +class SFXWrapAroundBuffer : public SFXBuffer +{ + public: + + typedef SFXBuffer Parent; + + protected: + + /// Absolute byte offset into the sound stream that the next packet write + /// will occur at. This is not an offset into the device buffer + /// in order to allow us to track how far in the source stream we are. + U32 mWriteOffset; + + /// Size of the device buffer in bytes. + U32 mBufferSize; + + // SFXBuffer. + virtual void _flush() + { + mWriteOffset = 0; + } + + /// Copy "length" bytes from "data" into the device at "offset". + virtual bool _copyData( U32 offset, const U8* data, U32 length ) = 0; + + // SFXBuffer. + virtual void write( SFXStreamPacket* const* packets, U32 num ); + + /// @return the sample position in the sound stream as determined from the + /// given buffer offset. + U32 getSamplePos( U32 bufferOffset ) const + { + if( !mBufferSize ) + return ( bufferOffset / getFormat().getBytesPerSample() ); + + const U32 writeOffset = mWriteOffset; // Concurrent writes on this one. + const U32 writeOffsetRelative = writeOffset % mBufferSize; + + U32 numBufferedBytes; + if( !writeOffset ) + numBufferedBytes = 0; + else if( writeOffsetRelative > bufferOffset ) + numBufferedBytes = writeOffsetRelative - bufferOffset; + else + // Wrap-around. + numBufferedBytes = mBufferSize - bufferOffset + writeOffsetRelative; + + const U32 bytePos = writeOffset - numBufferedBytes; + + return ( bytePos / getFormat().getBytesPerSample() ); + } + + public: + + SFXWrapAroundBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ); + SFXWrapAroundBuffer( SFXDescription* description ) + : Parent( description ), mBufferSize( 0 ) {} + + virtual U32 getMemoryUsed() const { return mBufferSize; } +}; + +//-------------------------------------------------------------------------- +// Global state. +//-------------------------------------------------------------------------- + +enum +{ + /// Soft limit on milliseconds to spend on updating sound buffers + /// when doing buffer updates on the main thread. + MAIN_THREAD_PROCESS_TIMEOUT = 512, + + /// Default time interval between periodic sound updates in milliseconds. + /// Only relevant for devices that perform periodic updates. + DEFAULT_UPDATE_INTERVAL = 512, +}; + +/// Thread pool for sound I/O. +/// +/// We are using a separate pool for sound packets in order to be +/// able to submit packet items from different threads. This would +/// violate the invariant of the global thread pool that only the +/// main thread is feeding the queues. +/// +/// Note that this also means that only at certain very well-defined +/// points is it possible to safely flush the work item queue on this +/// pool. +/// +/// @note Don't use this directly but rather use THREAD_POOL() instead. +/// This way, the sound code may be easily switched to using a common +/// pool later on. +class SFXThreadPool : public ThreadPool, public ManagedSingleton< SFXThreadPool > +{ + public: + + typedef ThreadPool Parent; + + /// Create a ThreadPool called "SFX" with two threads. + SFXThreadPool() + : Parent( "SFX", 2 ) {} + + // For ManagedSingleton. + static const char* getSingletonName() { return "SFXThreadPool"; } +}; + +/// Dedicated thread that does sound buffer updates. +/// May be NULL if sound API used does not do asynchronous buffer +/// updates but rather uses per-frame polling. +/// +/// @note SFXDevice automatically polls if this is NULL. +extern ThreadSafeRef< AsyncUpdateThread > gUpdateThread; + +/// List of buffers that need updating. +/// +/// It depends on the actual device whether this list is processed +/// on a stream update thread or on the main thread. +extern ThreadSafeRef< SFXBufferProcessList > gBufferUpdateList; + +/// List of buffers that are pending deletion. +/// +/// This is a messy issue. Buffers with live async states cannot be instantly +/// deleted since they may still be running concurrent updates. However, they +/// also cannot be deleted on the update thread since the StrongRefBase stuff +/// isn't thread-safe (i.e weak references kept by client code would cause trouble). +/// +/// So, what we do is mark buffers for deletion, wait till they surface on the +/// process list and then ping them back to this list to have them deleted by the +/// SFXDevice itself on the main thread. A bit of overhead but only a fraction of +/// the buffers will ever undergo this procedure. +extern ThreadSafeDeque< SFXBuffer* > gDeadBufferList; + +/// Return the thread pool used for SFX work. +inline ThreadPool& THREAD_POOL() +{ + return *( SFXThreadPool::instance() ); +} + +/// Return the dedicated SFX update thread; NULL if updating on the main thread. +inline ThreadSafeRef< SFXUpdateThread > UPDATE_THREAD() +{ + return gUpdateThread; +} + +/// Return the processing list for SFXBuffers that need updating. +inline SFXBufferProcessList& UPDATE_LIST() +{ + return *gBufferUpdateList; +} + +/// Trigger an SFX update. +inline bool TriggerUpdate() +{ + ThreadSafeRef< SFXUpdateThread > sfxThread = UPDATE_THREAD(); + if( sfxThread != NULL ) + { + sfxThread->triggerUpdate(); + return true; + } + else + return false; +} + +/// Delete all buffers currently on the dead buffer list. +inline void PurgeDeadBuffers() +{ + SFXBuffer* buffer; + while( gDeadBufferList.tryPopFront( buffer ) ) + delete buffer; +} + +/// Return true if the current thread is the one responsible for doing SFX updates. +inline bool isSFXThread() +{ + ThreadSafeRef< SFXUpdateThread > sfxThread = UPDATE_THREAD(); + + U32 threadId; + if( sfxThread != NULL ) + threadId = sfxThread->getId(); + else + threadId = ThreadManager::getMainThreadId(); + + return ThreadManager::compare( ThreadManager::getCurrentThreadId(), threadId ); +} + +} // namespace SFXInternal + +#endif // !_SFXINTERNAL_H_ diff --git a/Engine/source/sfx/sfxMemoryStream.cpp b/Engine/source/sfx/sfxMemoryStream.cpp new file mode 100644 index 000000000..d7e04785f --- /dev/null +++ b/Engine/source/sfx/sfxMemoryStream.cpp @@ -0,0 +1,117 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/sfxMemoryStream.h" +#include "platform/typetraits.h" +#include "console/console.h" + + +SFXMemoryStream::SFXMemoryStream( const SFXFormat& format, + SourceStreamType* stream, + U32 numSamples ) + : IInputStreamFilter< U8, SourceStreamType* >( stream ), + mFormat( format ), + mNumSamplesTotal( numSamples ), + mNumSamplesLeft( numSamples ), + mCurrentPacket( NULL ), + mCurrentPacketOffset( 0 ) +{ +} + +void SFXMemoryStream::reset() +{ + if( dynamic_cast< IResettable* >( getSourceStream() ) ) + { + reinterpret_cast< IResettable* >( getSourceStream() )->reset(); + + if( mCurrentPacket ) + destructSingle( mCurrentPacket ); + + mCurrentPacket = NULL; + mCurrentPacketOffset = 0; + mNumSamplesLeft = mNumSamplesTotal; + } + else + Con::errorf( "SFXMemoryStream - cannot reset source stream" ); +} + +U32 SFXMemoryStream::read( U8* buffer, U32 length ) +{ + U32 bufferOffset = 0; + + // Determine how much we're supposed to read. + + U32 numBytesToCopy = length; + if( mNumSamplesLeft != U32_MAX ) + numBytesToCopy = getMin( length, mNumSamplesLeft * mFormat.getBytesPerSample() ); + numBytesToCopy -= numBytesToCopy % mFormat.getBytesPerSample(); + + // Copy the data. + + U32 numBytesLeftToCopy = numBytesToCopy; + while( numBytesLeftToCopy ) + { + // If we have a current packet, use its data. + + if( mCurrentPacket ) + { + U32 numBytesLeftInCurrentPacket = mCurrentPacket->size - mCurrentPacketOffset; + + // Copy data. + + if( numBytesLeftInCurrentPacket ) + { + const U32 numBytesToCopy = getMin( numBytesLeftInCurrentPacket, numBytesLeftToCopy ); + dMemcpy( &buffer[ bufferOffset ], &mCurrentPacket->data[ mCurrentPacketOffset ], numBytesToCopy ); + + bufferOffset += numBytesToCopy; + mCurrentPacketOffset += numBytesToCopy; + numBytesLeftInCurrentPacket -= numBytesToCopy; + numBytesLeftToCopy -= numBytesToCopy; + } + + // Discard the packet if there's no data left. + + if( !numBytesLeftInCurrentPacket ) + { + destructSingle( mCurrentPacket ); + mCurrentPacket = NULL; + mCurrentPacketOffset = 0; + } + } + else + { + // Read a new packet. + + if( !getSourceStream()->read( &mCurrentPacket, 1 ) ) + break; + } + } + + // Update count of remaining samples. + + U32 numBytesCopied = numBytesToCopy - numBytesLeftToCopy; + if( mNumSamplesLeft != U32_MAX ) + mNumSamplesLeft -= ( numBytesCopied / mFormat.getBytesPerSample() ); + + return numBytesCopied; +} diff --git a/Engine/source/sfx/sfxMemoryStream.h b/Engine/source/sfx/sfxMemoryStream.h new file mode 100644 index 000000000..dddfcaf01 --- /dev/null +++ b/Engine/source/sfx/sfxMemoryStream.h @@ -0,0 +1,84 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXMEMORYSTREAM_H_ +#define _SFXMEMORYSTREAM_H_ + +#ifndef _SFXSTREAM_H_ + #include "sfx/sfxStream.h" +#endif +#ifndef _TSTREAM_H_ + #include "core/stream/tStream.h" +#endif +#ifndef _RAWDATA_H_ + #include "core/util/rawData.h" +#endif + + +/// A stream filter that converts sample packets from its source stream +/// to a continuous sample stream. Useful for feeding sound from a source +/// that pushes sample data in discrete packets. +/// +/// @note For the SFXMemoryStream to allow a reset(), the source input +/// stream must implement IResettable. +class SFXMemoryStream : public SFXStream, + public IInputStreamFilter< U8, IInputStream< RawData* >* > +{ + public: + + typedef SFXStream Parent; + + protected: + + /// + SFXFormat mFormat; + + /// Total number of samples in the stream. If this is U32_MAX, the stream + /// is considered to be of indefinite size. + U32 mNumSamplesTotal; + + /// Number of samples left to be read from stream. Locked to U32_MAX for + /// stream of indefinite size. + U32 mNumSamplesLeft; + + /// The current sample data packet. + RawData* mCurrentPacket; + + /// Read offset in the current sample data packet. + U32 mCurrentPacketOffset; + + public: + + /// + SFXMemoryStream( const SFXFormat& format, SourceStreamType* stream, U32 numSamples = U32_MAX ); + + // SFXStream. + const SFXFormat& getFormat() const { return mFormat; } + U32 getSampleCount() const { return mNumSamplesTotal; } + U32 getDataLength() const { return ( mNumSamplesTotal == U32_MAX ? U32_MAX : mFormat.getDataLength( getDuration() ) ); } + U32 getDuration() const { return ( mNumSamplesTotal == U32_MAX ? U32_MAX : mFormat.getDuration( mNumSamplesTotal ) ); } + bool isEOS() const { return ( mNumSamplesLeft != 0 ); } + void reset(); + U32 read( U8 *buffer, U32 length ); +}; + +#endif // !_SFXMEMORYSTREAM_H_ diff --git a/Engine/source/sfx/sfxModifier.cpp b/Engine/source/sfx/sfxModifier.cpp new file mode 100644 index 000000000..1931be1d3 --- /dev/null +++ b/Engine/source/sfx/sfxModifier.cpp @@ -0,0 +1,194 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/sfxModifier.h" +#include "sfx/sfxSource.h" + + +//============================================================================= +// SFXOneShotModifier. +//============================================================================= + +//----------------------------------------------------------------------------- + +SFXOneShotModifier::SFXOneShotModifier( SFXSource* source, F32 triggerPos, bool removeWhenDone ) + : Parent( source, removeWhenDone ), + mTriggerPos( triggerPos ) +{ +} + +//----------------------------------------------------------------------------- + +bool SFXOneShotModifier::update() +{ + if( mSource->getElapsedPlayTimeCurrentCycle() >= mTriggerPos ) + { + _onTrigger(); + return mRemoveWhenDone; + } + else + return true; +} + +//============================================================================= +// SFXRangeModifier. +//============================================================================= + +//----------------------------------------------------------------------------- + +SFXRangeModifier::SFXRangeModifier( SFXSource* source, F32 startTime, F32 endTime, bool removeWhenDone ) + : Parent( source, removeWhenDone ), + mStartTime( startTime ), + mEndTime( endTime ), + mIsActive( false ) +{ +} + +//----------------------------------------------------------------------------- + +bool SFXRangeModifier::update() +{ + if( !isActive() ) + { + SFXStatus status = mSource->getStatus(); + if( ( status == SFXStatusPlaying || status == SFXStatusBlocked ) + && mSource->getElapsedPlayTimeCurrentCycle() >= mStartTime ) + { + mIsActive = true; + _onStart(); + } + } + + if( isActive() ) + _onUpdate(); + + if( isActive() ) + { + SFXStatus status = mSource->getStatus(); + if( ( status == SFXStatusPlaying || status == SFXStatusBlocked ) + && mSource->getElapsedPlayTimeCurrentCycle() > mEndTime ) + { + _onEnd(); + mIsActive = false; + + return mRemoveWhenDone; + } + } + + return true; +} + +//============================================================================= +// SFXFadeModifier. +//============================================================================= + +//----------------------------------------------------------------------------- + +SFXFadeModifier::SFXFadeModifier( SFXSource* source, F32 time, F32 endVolume, F32 startTime, EOnEnd onEndDo, bool removeWhenDone ) + : Parent( source, startTime, startTime + time, removeWhenDone ), + mEndVolume( endVolume ), + mOnEnd( onEndDo ) +{ + +} + +//----------------------------------------------------------------------------- + +SFXFadeModifier::~SFXFadeModifier() +{ + // If the fade is still ongoing, restore the source's volume. + // For fade-in, set to end volume. For fade-out, set to start volume. + + if( isActive() ) + { + if( mStartVolume > mEndVolume ) + mSource->setVolume( mStartVolume ); + else + mSource->setVolume( mEndVolume ); + } +} + +//----------------------------------------------------------------------------- + +void SFXFadeModifier::_onStart() +{ + mStartVolume = mSource->getVolume(); + mCurrentVolume = mStartVolume; +} + +//----------------------------------------------------------------------------- + +void SFXFadeModifier::_onUpdate() +{ + F32 multiplier = ( mSource->getElapsedPlayTimeCurrentCycle() - mStartTime ) / ( mEndTime - mStartTime ); + + F32 newVolume; + if( mStartVolume > mEndVolume ) + newVolume = mStartVolume - ( ( mStartVolume - mEndVolume ) * multiplier ); + else + newVolume = mStartVolume + ( ( mEndVolume - mStartVolume ) * multiplier ); + + if( newVolume != mCurrentVolume ) + { + mCurrentVolume = newVolume; + mSource->setVolume( mCurrentVolume ); + } +} + +//----------------------------------------------------------------------------- + +void SFXFadeModifier::_onEnd() +{ + mSource->setVolume( mEndVolume ); + + switch( mOnEnd ) + { + case ON_END_Pause: + mSource->pause( 0.f ); // Pause without fade. + break; + + case ON_END_Stop: + mSource->stop( 0.f ); // Stop without fade. + break; + + case ON_END_Nop: ; + } +} + +//============================================================================= +// SFXMarkerModifier. +//============================================================================= + +//----------------------------------------------------------------------------- + +SFXMarkerModifier::SFXMarkerModifier( SFXSource* source, const String& name, F32 pos, bool removeWhenDone ) + : Parent( source, pos, removeWhenDone ), + mMarkerName( name ) +{ +} + +//----------------------------------------------------------------------------- + +void SFXMarkerModifier::_onTrigger() +{ + Con::executef( mSource, "onMarkerPassed", mMarkerName.c_str() ); +} diff --git a/Engine/source/sfx/sfxModifier.h b/Engine/source/sfx/sfxModifier.h new file mode 100644 index 000000000..182609964 --- /dev/null +++ b/Engine/source/sfx/sfxModifier.h @@ -0,0 +1,191 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXMODIFIER_H_ +#define _SFXMODIFIER_H_ + +#ifndef _TSTREAM_H_ + #include "core/stream/tStream.h" +#endif + + +class SFXSource; + + +/// An SFXModifier modifies the properties of an SFXSource while its playback +/// is running. +class SFXModifier : public IPolled +{ + protected: + + /// The source that this effect works on. + SFXSource* mSource; + + /// If true, the effect is removed from the effects stack + bool mRemoveWhenDone; + + public: + + /// Create an effect that operates on "source". + SFXModifier( SFXSource* source, bool removeWhenDone = false ) + : mSource( source ) {} + + virtual ~SFXModifier() {} +}; + +/// An SFXModifier that is triggered once after passing a certain playback position. +class SFXOneShotModifier : public SFXModifier +{ + public: + + typedef SFXModifier Parent; + + protected: + + /// Playback position that triggers the effect. + F32 mTriggerPos; + + /// + virtual void _onTrigger() = 0; + + public: + + /// Create an effect that triggers when playback of "source" passes "triggerPos". + SFXOneShotModifier( SFXSource* source, F32 triggerPos, bool removeWhenDone = false ); + + // IPolled. + virtual bool update(); +}; + +/// An SFXModifier that is spans a certain range of playback time. +class SFXRangeModifier : public SFXModifier +{ + public: + + typedef SFXModifier Parent; + + protected: + + /// If true, the effect is currently being applied to the source. + bool mIsActive; + + /// Playback position in milliseconds when this effect becomes active. + F32 mStartTime; + + /// Playback position in milliseconds when this effect becomes inactive. + F32 mEndTime; + + /// Called when the play cursor passes mStartTime. + /// @note There may be latency between the cursor actually passing mStartTime + /// and this method being called. + virtual void _onStart() {} + + /// Called on each update() while the play cursor is in range. + virtual void _onUpdate() {} + + /// Called when the play cursor passes mEndTime. + /// @note There may be latency between the cursor actually passing mEndTime + /// and this method being called. + virtual void _onEnd() {} + + public: + + /// Create an effect that operates on "source" between "startTime" seconds + /// (inclusive) and "endTime" seconds (exclusive). + SFXRangeModifier( SFXSource* source, F32 startTime, F32 endTime, bool removeWhenDone = false ); + + /// + bool isActive() const { return mIsActive; } + + // IPolled. + virtual bool update(); +}; + +/// A volume fade effect (fade-in or fade-out). +class SFXFadeModifier : public SFXRangeModifier +{ + public: + + typedef SFXRangeModifier Parent; + + enum EOnEnd + { + ON_END_Nop, ///< Do nothing with source when fade is complete. + ON_END_Stop, ///< Stop source when fade is complete. + ON_END_Pause, ///< Pause source when fade is complete. + }; + + protected: + + /// Volume when beginning fade. Set when effect is activated. + F32 mStartVolume; + + /// Volume when ending fade. + F32 mEndVolume; + + /// Current volume level. + F32 mCurrentVolume; + + /// Action to perform when the fade has been completed. Defaults to no action. + EOnEnd mOnEnd; + + // SFXModifier. + virtual void _onStart(); + virtual void _onUpdate(); + virtual void _onEnd(); + + public: + + /// Create an effect that fades the volume of "source" to "endVolume" over the + /// period of "time" seconds. The fade will start at "referenceTime" using the + /// source's current volume at the time as the start. + SFXFadeModifier( SFXSource* source, F32 time, F32 endVolume, F32 startTime, EOnEnd onEndDo = ON_END_Nop, bool removeWhenDone = false ); + + virtual ~SFXFadeModifier(); +}; + +/// A modifer that calls a method on the SFXSource when a particular playback position +/// is passed. +/// +/// @note At the moment, doing a setPosition() on a source will not cause markers that have +/// been jumped over in the operation to be ignored. Instead they will trigger on the +/// next update. +class SFXMarkerModifier : public SFXOneShotModifier +{ + public: + + typedef SFXOneShotModifier Parent; + + protected: + + /// Symbolic marker name that is passed to the "onMarkerPassed" callback. + String mMarkerName; + + // SFXOneShotModifier + virtual void _onTrigger(); + + public: + + SFXMarkerModifier( SFXSource* source, const String& name, F32 pos, bool removeWhenDone = false ); +}; + +#endif // !_SFXMODIFIER_H_ diff --git a/Engine/source/sfx/sfxParameter.cpp b/Engine/source/sfx/sfxParameter.cpp new file mode 100644 index 000000000..a7c6f24b0 --- /dev/null +++ b/Engine/source/sfx/sfxParameter.cpp @@ -0,0 +1,350 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/sfxParameter.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "console/simSet.h" +#include "math/mMathFn.h" +#include "math/mathTypes.h" +#include "math/mathIO.h" +#include "core/stream/bitStream.h" +#include "platform/typetraits.h" + + +IMPLEMENT_CONOBJECT( SFXParameter ); + +ConsoleDocClass( SFXParameter, + "@brief A sound channel value that can be bound to multiple sound sources.\n\n" + + "Parameters are special objects that isolate a specific property that sound sources can have and allows to bind " + "this isolated instance to multiple sound sources such that when the value of the parameter changes, all sources " + "bound to the parameter will have their respective property changed.\n\n" + + "Parameters are identified and referenced by their #internalName. This means that the SFXDescription::parameters and " + "SFXTrack::parameters fields should contain the #internalNames of the SFXParameter objects which should be attached to " + "the SFXSources when they are created. No two SFXParameters should have the same #internalName.\n\n" + + "All SFXParameter instances are automatically made children of the SFXParameterGroup.\n\n" + + "@note To simply control the volume and/or pitch levels of a group of sounds, it is easier and more efficient to use " + "a sound source group rather than SFXParameters (see @ref SFXSource_hierarchies). Simply create a SFXSource object representing the group, assign " + "SFXDescription::sourceGroup of the sounds appropriately, and then set the volume and/or pitch level of the group to " + "modulate the respective properties of all children.\n\n" + + "@section SFXParameter_updates Parameter Updates\n" + + "Parameters are periodically allowed to update their own values. This makes it possible to attach custom logic to a parameter " + "and have individual parameters synchronize their values autonomously. Use the onUpdate() callback to attach " + "script code to a parameter update.\n\n" + + "@tsexample\n" + "new SFXParameter( EngineRPMLevel )\n" + "{\n" + " // Set the name by which this parameter is identified.\n" + " internalName = \"EngineRPMLevel\";\n" + "\n" + " // Let this parameter control the pitch of attached sources to simulate engine RPM ramping up and down.\n" + " channel = \"Pitch\";\n" + "\n" + " // Start out with unmodified pitch.\n" + " defaultValue = 1;\n" + "\n" + " // Add a texture description of what this parameter does.\n" + " description = \"Engine RPM Level\";\n" + "};\n" + "\n" + "// Create a description that automatically attaches the engine RPM parameter.\n" + "singleton SFXDescription( EngineRPMSound : AudioLoop2D )\n" + "{\n" + " parameters[ 0 ] = \"EngineRPMLevel\";\n" + "};\n" + "\n" + "// Create sound sources for the engine.\n" + "sfxCreateSource( EngineRPMSound, \"art/sound/engine/enginePrimary\" );\n" + "sfxCreateSource( EngineRPMSound, \"art/sound/engine/engineSecondary\" );\n" + "\n" + "// Setting the parameter value will now affect the pitch level of both sound sources.\n" + "EngineRPMLevel.value = 0.5;\n" + "EngineRPMLevel.value = 1.5;\n" + "@endtsexample\n\n" + + "@ref SFX_interactive\n\n" + "@ingroup SFX" +); + + +IMPLEMENT_CALLBACK( SFXParameter, onUpdate, void, (), (), + "Called when the sound system triggers an update on the parameter.\n" + "This occurs periodically during system operation." ); + + +//----------------------------------------------------------------------------- + +SFXParameter::SFXParameter() + : mValue( 1.f ), + mRange( 0.f, 1.f ), + mChannel( SFXChannelVolume ), + mDefaultValue( 1.f ) +{ +} + +//----------------------------------------------------------------------------- + +SFXParameter::~SFXParameter() +{ +} + +//----------------------------------------------------------------------------- + +void SFXParameter::initPersistFields() +{ + addGroup( "Sound" ); + + addProtectedField( "value", TypeF32, Offset( mValue, SFXParameter ), + &SFXParameter::_setValue, &defaultProtectedGetFn, + "Current value of the audio parameter.\n" + "All attached sources are notified when this value changes." ); + addProtectedField( "range", TypePoint2F, Offset( mRange, SFXParameter ), + &SFXParameter::_setRange, &defaultProtectedGetFn, + "Permitted range for #value.\n" + "Minimum and maximum allowed value for the parameter. Both inclusive.\n\n" + "For all but the User0-3 channels, this property is automatically set up by SFXParameter." ); + addProtectedField( "channel", TYPEID< SFXChannel >(), Offset( mChannel, SFXParameter ), + &SFXParameter::_setChannel, &defaultProtectedGetFn, + "Channel that the parameter controls.\n" + "This controls which property of the sources it is attached to the parameter controls." ); + addProtectedField( "defaultValue", TypeF32, Offset( mDefaultValue, SFXParameter ), + &SFXParameter::_setDefaultValue, &defaultProtectedGetFn, + "Value to which the parameter is initially set.\n" + "When the parameter is first added to the system, #value will be set to #defaultValue." ); + addField( "description", TypeRealString,Offset( mDescription, SFXParameter ), + "Textual description of the parameter.\n" + "Primarily for use in the Audio Parameters dialog of the editor to allow for easier identification " + "of parameters." ); + + endGroup( "Sound" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +bool SFXParameter::_setValue( void *object, const char *index, const char *data ) +{ + reinterpret_cast< SFXParameter* >( object )->setValue( dAtof( data ) ); + return false; +} + +//----------------------------------------------------------------------------- + +bool SFXParameter::_setRange( void *object, const char *index, const char *data ) +{ + Point2F range = EngineUnmarshallData< Point2F >()( data ); + reinterpret_cast< SFXParameter* >( object )->setRange( range ); + return false; +} + +//----------------------------------------------------------------------------- + +bool SFXParameter::_setChannel( void *object, const char *index, const char *data ) +{ + SFXChannel channel = EngineUnmarshallData< SFXChannel >()( data ); + reinterpret_cast< SFXParameter* >( object )->setChannel( channel ); + return false; +} + +//----------------------------------------------------------------------------- + +bool SFXParameter::_setDefaultValue( void *object, const char *index, const char *data ) +{ + reinterpret_cast< SFXParameter* >( object )->setDefaultValue( dAtof( data ) ); + return false; +} + +//----------------------------------------------------------------------------- + +bool SFXParameter::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + mValue = mDefaultValue; + + // Make sure the parameter has a name. + + if( !getInternalName() || !getInternalName()[ 0 ] ) + { + Con::errorf( "SFXParameter::onAdd - %i (%s): parameter object does not have a name", getId(), getName() ); + return false; + } + + // Make sure the parameter has a unique name. + + if( find( getInternalName() ) ) + { + Con::errorf( "SFXParameter::onAdd - %i (%s): a parameter called '%s' already exists", getId(), getName(), getInternalName() ); + return false; + } + + // Add us to the SFXParameter group. + + Sim::getSFXParameterGroup()->addObject( this ); + + return true; +} + +//----------------------------------------------------------------------------- + +void SFXParameter::onRemove() +{ + mEventSignal.trigger( this, SFXParameterEvent_Deleted ); + Parent::onRemove(); +} + +//----------------------------------------------------------------------------- + +void SFXParameter::update() +{ + onUpdate_callback(); +} + +//----------------------------------------------------------------------------- + +void SFXParameter::reset() +{ + setValue( mDefaultValue ); +} + +//----------------------------------------------------------------------------- + +void SFXParameter::setValue( F32 value ) +{ + if( value == mValue ) + return; + + mValue = mClampF( value, mRange.x, mRange.y ); + mEventSignal.trigger( this, SFXParameterEvent_ValueChanged ); +} + +//----------------------------------------------------------------------------- + +void SFXParameter::setDefaultValue( F32 value ) +{ + mDefaultValue = mClampF( value, mRange.x, mRange.y ); +} + +//----------------------------------------------------------------------------- + +void SFXParameter::setRange( const Point2F& range ) +{ + if( range == mRange ) + return; + + mRange = range; + + F32 value = mClampF( mValue, mRange.x, mRange.y ); + if( value != mValue ) + setValue( value ); + + mDefaultValue = mClampF( mDefaultValue, mRange.x, mRange.y ); +} + +//----------------------------------------------------------------------------- + +void SFXParameter::setChannel( SFXChannel channel ) +{ + if( mChannel == channel ) + return; + + mChannel = channel; + + F32 value = mValue; + switch( channel ) + { + case SFXChannelVolume: + case SFXChannelConeOutsideVolume: + setRange( 0.f, 1.0f ); + break; + + case SFXChannelConeInsideAngle: + case SFXChannelConeOutsideAngle: + setRange( 0.f, 360.f ); + break; + + case SFXChannelPitch: + case SFXChannelMinDistance: + case SFXChannelMaxDistance: + case SFXChannelCursor: + setRange( 0.f, TypeTraits< F32 >::MAX ); + break; + + case SFXChannelStatus: + setRange( F32( SFXStatusPlaying ), F32( SFXStatusStopped ) ); + break; + + default: + setRange( TypeTraits< F32 >::MIN, TypeTraits< F32 >::MAX ); + break; + } + + // If the range setting did not result in the value already + // being changed, fire a value-change signal now so that sources + // can catch on to the new semantics. Unfortunately, we can't + // do something about the old semantic's value having been + // changed by us. + + if( mValue == value ) + mEventSignal.trigger( this, SFXParameterEvent_ValueChanged ); +} + +//----------------------------------------------------------------------------- + +SFXParameter* SFXParameter::find( StringTableEntry name ) +{ + return dynamic_cast< SFXParameter* >( + Sim::getSFXParameterGroup()->findObjectByInternalName( name ) + ); +} + +//============================================================================= +// Console Methods. +//============================================================================= +// MARK: ---- Console Methods ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXParameter, getParameterName, String, (),, + "Get the name of the parameter.\n" + "@return The paramete name." ) +{ + return object->getInternalName(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXParameter, reset, void, (),, + "Reset the parameter's value to its default.\n" + "@see SFXParameter::defaultValue\n" ) +{ + object->reset(); +} diff --git a/Engine/source/sfx/sfxParameter.h b/Engine/source/sfx/sfxParameter.h new file mode 100644 index 000000000..32be7d01c --- /dev/null +++ b/Engine/source/sfx/sfxParameter.h @@ -0,0 +1,171 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXPARAMETER_H_ +#define _SFXPARAMETER_H_ + +#ifndef _SIMOBJECT_H_ + #include "console/simObject.h" +#endif +#ifndef _SFXCOMMON_H_ + #include "sfx/sfxCommon.h" +#endif +#ifndef _TSIGNAL_H_ + #include "core/util/tSignal.h" +#endif +#ifndef _MPOINT2_H_ + #include "math/mPoint2.h" +#endif + + +/// Enumeration of events triggered by SFXParameters. +enum SFXParameterEvent +{ + /// The parameter value has changed. + SFXParameterEvent_ValueChanged, + + /// The parameter is about to be deleted. + SFXParameterEvent_Deleted, +}; + + +/// Parameter for interactive audio. +/// +/// Parameters are tied to sound sources and will signal value changes so that +/// sound sources may react. +/// +/// All parameters are global. The name of a parameter is its internal object name. +/// +/// Like sources, parameters are exclusively client-side. +/// +class SFXParameter : public SimObject +{ + public: + + typedef SimObject Parent; + typedef Signal< void( SFXParameter* parameter, SFXParameterEvent event ) > EventSignal; + + protected: + + /// The current value. + F32 mValue; + + /// The min/max range of the parameter's value. Both inclusive. + Point2F mRange; + + /// The channel being controlled by this parameter. + SFXChannel mChannel; + + /// Value assigned to the parameter on creation and reset. + F32 mDefaultValue; + + /// Help text. + String mDescription; + + /// The signal used to notify attached sources of parameter events. + EventSignal mEventSignal; + + /// @name Callbacks + /// @{ + + DECLARE_CALLBACK( void, onUpdate, () ); + + /// @} + + static bool _setValue( void *object, const char *index, const char *data ); + static bool _setRange( void *object, const char *index, const char *data ); + static bool _setChannel( void *object, const char *index, const char *data ); + static bool _setDefaultValue( void *object, const char *index, const char *data ); + + public: + + SFXParameter(); + + ~SFXParameter(); + + /// Look up a parameter by the given name. + static SFXParameter* find( StringTableEntry name ); + + /// Update the parameter's value. The default implementation will invoke a script + /// 'onUpdate' method if it is defined and do nothing otherwise. + virtual void update(); + + /// Reset the parameter's value to its default. + void reset(); + + /// Return the current value of this parameter. + F32 getValue() const { return mValue; } + + /// Set the parameter's current value. Will be clamped against the parameter's valid + /// value range. If a value change occurs, a SFXParameterEvent_ValueChange event + /// is fired. + void setValue( F32 value ); + + /// Return the default value of this parameter. This is the value the parameter + /// will be set to when it is added to the system. + F32 getDefaultValue() const { return mDefaultValue; } + + /// Set the default value of this parameter. This is the value the parameter + /// is set to when it is added to the system. + void setDefaultValue( F32 value ); + + /// Return the range of valid values that this parameter may take. + const Point2F& getRange() const { return mRange; } + + /// Set the valid range for the value of this parameter. Note that both min + /// and max are inclusive. + void setRange( const Point2F& range ); + + /// Set the valid range for the value of this parameter. Note that both min + /// and max are inclusive. + void setRange( F32 minValue, F32 maxValue ) { setRange( Point2F( minValue, maxValue ) ); } + + /// Return the parameter channel that is being affected by this parameter. + SFXChannel getChannel() const { return mChannel; } + + /// Set the parameter channel that is being affected by this parameter. + void setChannel( SFXChannel channel ); + + /// Return the description text supplied for this parameter. This is used to help + /// identify the purpose of a parameter. + const String& getDescription() const { return mDescription; } + + /// Set the description text for this parameter. This may be used to help identify + /// the purpose of a parameter. + void setDescription( const String& str ) { mDescription = str; } + + /// Return the event signal for this parameter. + EventSignal& getEventSignal() { return mEventSignal; } + + // SimObject. + virtual bool onAdd(); + virtual void onRemove(); + + static void initPersistFields(); + + DECLARE_CONOBJECT( SFXParameter ); + DECLARE_CATEGORY( "SFX" ); + DECLARE_DESCRIPTION( "" ); +}; + +#endif // !_SFXPARAMETER_H_ + diff --git a/Engine/source/sfx/sfxPlayList.cpp b/Engine/source/sfx/sfxPlayList.cpp new file mode 100644 index 000000000..3714b821a --- /dev/null +++ b/Engine/source/sfx/sfxPlayList.cpp @@ -0,0 +1,468 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/sfxPlayList.h" +#include "sfx/sfxState.h" +#include "sfx/sfxTypes.h" +#include "core/stream/bitStream.h" +#include "math/mRandom.h" +#include "math/mathTypes.h" + + +IMPLEMENT_CO_DATABLOCK_V1( SFXPlayList ); + + +ConsoleDocClass( SFXPlayList, + "@brief A datablock describing a playback pattern of sounds.\n\n" + + "Playlists allow to define intricate playback patterns of invidual tracks and thus allow the sound system to be " + "easily used for playing multiple sounds in single operations.\n\n" + + "As playlists are %SFXTracks, they can thus be used anywhere in the engine where sound data can be assigned.\n\n" + + "Each playlist can hold a maximum of 16 tracks. Longer playlists may be constructed by cascading lists, i.e. " + "by creating a playlist that references other playlists.\n\n" + + "Processing of a single playlist slot progresses in a fixed set of steps that are invariably " + "iterated through for each slot (except the slot is assigned a state and its state is deactivated; in " + "this case, the controller will exit out of the slot directly):\n\n" + + "
        \n" + "
      1. delayIn:

        Waits a set amount of time before processing the slot. This is 0 by default and " + "is determined by the #delayTimeIn (seconds to wait) and #delayTimeInVariance (bounds on randomization) " + "properties.

      2. \n" + "
      3. #transitionIn:

        Decides what to do @b before playing the slot. Defaults to @c None which makes " + "this stage a no-operation. Alternatively, the slot can be configured to wait for playback of other " + "slots to finish (@c Wait and @c WaitAll) or to stop playback of other slots (@c Stop and @c StopAll). " + "Note that @c Wait and @c Stop always refer to the source that was last started by the list.

      4. \n" + "
      5. play:

        Finally, the #track attached to the slot is played. However, this will only @b start " + "playback of the track and then immediately move on to the next stage. It will @b not wait for the " + "track to finish playing. Note also that depending on the @c replay setting for the slot, this " + "stage may pick up a source that is already playing on the slot rather than starting a new one.

        " + "

        Several slot properties (fade times, min/max distance, and volume/pitch scale) are used in this stage.

      6. \n" + "
      7. delayOut:

        Waits a set amount of time before transitioning out of the slot. This works the " + "same as @c delayIn and is set to 0 by default (i.e. no delay).

      8. \n" + "
      9. #transitionOut:

        Decides what to do @b after playing the slot. This works like #transitionIn.

      10. \n" + "
      \n\n" + + "This is a key difference to playlists in normal music players where upon reaching a certain slot, the slot " + "will immediately play and the player then wait for playback to finish before moving on to the next slot.\n\n" + + "@note Be aware that time limits set on slot delays are soft limits. The sound system updates sound sources in discrete " + "(and equally system update frequency dependent) intervals which thus determines the granularity at which " + "time-outs can be handled.\n\n" + + "@section SFXPlayList_randomization Value Randomization\n\n" + + "For greater variety, many of the values for individual slots may be given a randomization limit that will " + "trigger a dynamic variance of the specified base value.\n\n" + + "Any given field @c xyz that may be randomized has a corresponding field @c xyzVariance which is a two-dimensional " + "vector. The first number specifies the greatest value that may be subtracted from the given base value (i.e. the @c xyz field) " + "whereas the second number specifies the greatest value that may be added to the base value. Between these two limits, " + "a random number is generated.\n\n" + + "The default variance settings of \"0 0\" will thus not allow to add or subtract anything from the base value and " + "effectively disable randomization.\n\n" + + "Randomization is re-evaluated on each cycle through a list.\n\n" + + "@section SFXPlayList_states Playlists and States\n\n" + + "A unique aspect of playlists is that they allow their playback to be tied to the changing set of active sound states. " + "This feature enables playlists to basically connect to an extensible state machine that can be leveraged by the game " + "code to signal a multitude of different gameplay states with the audio system then automatically reacting to state " + "transitions.\n\n" + + "Playlists react to states in three ways:\n" + + "- Before a controller starts processing a slot it checks whether the slot is assigned a #state. If this is the " + "case, the controller checks whether the particular state is active. If it is not, the entire slot is skipped. " + "If it is, the controller goes on to process the slot.\n" + "- If a controller is in one of the delay stages for a slot that has a #state assigned and the state is deactivated, " + "the controller will stop the delay and skip any of the remaining processing stages for the slot.\n" + "- Once the play stage has been processed for a slot that has a #state assigned, the slot's #stateMode will determine " + "what happens with the playing sound source if the slot's state is deactivated while the sound is still playing.\n" + "\n" + + "A simple example of how to make use of states in combination with playlists would be to set up a playlist for background " + "music that reacts to the mood of the current gameplay situation. For example, during combat, tenser music could play than " + "during normal exploration. To set this up, different %SFXStates would represent different moods in the game and the " + "background music playlist would have one slot set up for each such mood. By making use of volume fades and the " + "@c PauseWhenDeactivated #stateMode, smooth transitions between the various audio tracks can be produced.\n\n" + + "@tsexample\n" + "// Create a play list from two SFXProfiles.\n" + "%playList = new SFXPlayList()\n" + "{\n" + " // Use a looped description so the list playback will loop.\n" + " description = AudioMusicLoop2D;\n" + "\n" + " track[ 0 ] = Profile1;\n" + " track[ 1 ] = Profile2;\n" + "};\n" + "\n" + "// Play the list.\n" + "sfxPlayOnce( %playList );\n" + "@endtsexample\n\n" + + "@ref SFX_interactive\n\n" + + "@ingroup SFX" +); + + +ImplementEnumType( SFXPlayListLoopMode, + "Playlist behavior when description is set to loop.\n\n" + "@see SFXDescription::isLooping\n\n" + "@see SFXPlayList::loopMode\n\n" + "@ingroup SFX" ) + { SFXPlayList::LOOP_All, "All", + "Loop over all slots, i.e. jump from last to first slot after all slots have played." }, + { SFXPlayList::LOOP_Single, "Single", + "Loop infinitely over the current slot. Only useful in combination with either states or manual playlist control." }, +EndImplementEnumType; + +ImplementEnumType( SFXPlayListRandomMode, + "Randomization pattern to apply to playlist slot playback order.\n\n" + "@see SFXPlayList::random\n\n" + "@ingroup SFX" ) + { SFXPlayList::RANDOM_NotRandom, "NotRandom", + "Play slots in sequential order. No randomization." }, + { SFXPlayList::RANDOM_StrictRandom, "StrictRandom", + "Play a strictly random selection of slots.\n\n" + "In this mode, a set of numSlotsToPlay random numbers between 0 and numSlotsToPlay-1 (inclusive), i.e. in the range of valid slot indices, is " + "generated and playlist slots are played back in the order of this list. This allows the same slot to occur multiple times in a list and, " + "consequentially, allows for other slots to not appear at all in a given slot ordering." }, + { SFXPlayList::RANDOM_OrderedRandom, "OrderedRandom", + "Play all slots in the list in a random order.\n\n" + "In this mode, the @c numSlotsToPlay slots from the list with valid tracks assigned are put into a random order and played. This guarantees " + "that each slots is played exactly once albeit at a random position in the total ordering." }, +EndImplementEnumType; + +ImplementEnumType( SFXPlayListTransitionMode, + "Playlist behavior when transitioning in and out of invididual slots.\n\n" + "Transition behaviors apply when the playback controller starts processing a playlist slot and when it ends processing a slot. Using transition " + "behaviors, playback can be synchronized.\n\n" + "@see SFXPlayList::transitionIn\n\n" + "@see SFXPlayList::transitionOut\n\n" + "@ingroup SFX" ) + { SFXPlayList::TRANSITION_None, "None", + "No transition. Immediately move on to processing the slot or immediately move on to the next slot." }, + { SFXPlayList::TRANSITION_Wait, "Wait", + "Wait for the sound source spawned last by this playlist to finish playing. Then proceed." }, + { SFXPlayList::TRANSITION_WaitAll, "WaitAll", + "Wait for all sound sources currently spawned by the playlist to finish playing. Then proceed." }, + { SFXPlayList::TRANSITION_Stop, "Stop", + "Stop the sound source spawned last by this playlist. Then proceed." }, + { SFXPlayList::TRANSITION_StopAll, "StopAll", + "Stop all sound sources spawned by the playlist. Then proceed." }, +EndImplementEnumType; + +ImplementEnumType( SFXPlayListReplayMode, + "Behavior when hitting the play stage of a slot that is still playing from a previous cycle.\n\n" + "@see SFXPlayList::replay\n\n" + "@ingroup SFX" ) + { SFXPlayList::REPLAY_IgnorePlaying, "IgnorePlaying", + "Ignore any sources that may already be playing on the slot and just create a new source." }, + { SFXPlayList::REPLAY_RestartPlaying, "RestartPlaying", + "Restart all sources that was last created for the slot." }, + { SFXPlayList::REPLAY_KeepPlaying, "KeepPlaying", + "Keep playing the current source(s) as if the source started last on the slot was created in this cycle. For this, " + "the sources associated with the slot are brought to the top of the play stack." }, + { SFXPlayList::REPLAY_StartNew, "StartNew", + "Stop all sources currently playing on the slot and then create a new source." }, + { SFXPlayList::REPLAY_SkipIfPlaying, "SkipIfPlaying", + "If there are sources already playing on the slot, skip the play stage." }, +EndImplementEnumType; + +ImplementEnumType( SFXPlayListStateMode, + "Reaction behavior when a state is changed incompatibly on a slot that has already started playing.\n\n" + "@see SFXPlayList::stateMode\n\n" + "@ingroup SFX" ) + { SFXPlayList::STATE_StopInactive, "StopWhenDeactivated", + "Stop the sources playing on the slot when a state changes to a setting that is incompatible with " + "the slot's state setting." }, + { SFXPlayList::STATE_PauseInactive, "PauseWhenDeactivated", + "Pause all sources playing on the slot when a state changes to a setting that is incompatible with the " + "slot's state setting.\n\n" + "When the slot's state is reactivated again, the sources will resume playback." }, + { SFXPlayList::STATE_IgnoreInactive, "IgnoreWhenDeactivated", + "Ignore when a state changes to a setting incompatible with the slot's state setting and just keep " + "playing sources attached to the slot." }, +EndImplementEnumType; + + +//----------------------------------------------------------------------------- + +SFXPlayList::SFXPlayList() + : mRandomMode( RANDOM_NotRandom ), + mLoopMode( LOOP_All ), + mNumSlotsToPlay( NUM_SLOTS ), + mTrace( false ) +{ +} + +//----------------------------------------------------------------------------- + +void SFXPlayList::initPersistFields() +{ + addGroup( "Sound" ); + + addField( "random", TYPEID< ERandomMode >(), Offset( mRandomMode, SFXPlayList ), + "Slot playback order randomization pattern.\n" + "By setting this field to something other than \"NotRandom\" to order in which slots of the " + "playlist are processed can be changed from sequential to a random pattern. This allows to " + "to create more varied playback patterns.\n" + "Defaults to \"NotRandom\"." ); + addField( "loopMode", TYPEID< ELoopMode >(), Offset( mLoopMode, SFXPlayList ), + "Behavior when description has looping enabled.\n" + "The loop mode determines whether the list will loop over a single slot or loop over " + "all the entire list of slots being played.\n\n" + "@see SFXDescription::isLooping" ); + addField( "numSlotsToPlay", TypeS32, Offset( mNumSlotsToPlay, SFXPlayList ), + "Number of slots to play.\n" + "Up to a maximum of 16, this field determines the number of slots that are taken from the " + "list for playback. Only slots that have a valid #track assigned will be considered for " + "this." ); + + addArray( "slots", NUM_SLOTS ); + + addField( "track", TypeSFXTrackName, Offset( mSlots.mTrack, SFXPlayList ), NUM_SLOTS, + "Track to play in this slot.\n" + "This must be set for the slot to be considered for playback. Other settings for a slot " + "will not take effect except this field is set." ); + addField( "replay", TYPEID< EReplayMode >(), Offset( mSlots.mReplayMode, SFXPlayList ), NUM_SLOTS, + "Behavior when an already playing sound is encountered on this slot from a previous cycle.\n" + "Each slot can have an arbitrary number of sounds playing on it from previous cycles. This field determines " + "how SFXController will handle these sources." ); + addField( "transitionIn", TYPEID< ETransitionMode >(), Offset( mSlots.mTransitionIn, SFXPlayList ), NUM_SLOTS, + "Behavior when moving into this slot.\n" + "After the delayIn time has expired (if any), this slot determines what the controller " + "will do before actually playing the slot." ); + addField( "transitionOut", TYPEID< ETransitionMode >(), Offset( mSlots.mTransitionOut, SFXPlayList ), NUM_SLOTS, + "Behavior when moving out of this slot.\n" + "After the #detailTimeOut has expired (if any), this slot determines what the controller " + "will do before moving on to the next slot." ); + addField( "delayTimeIn", TypeF32, Offset( mSlots.mDelayTimeIn.mValue, SFXPlayList ), NUM_SLOTS, + "Seconds to wait after moving into slot before #transitionIn." ); + addField( "delayTimeInVariance", TypePoint2F, Offset( mSlots.mDelayTimeIn.mVariance, SFXPlayList ), NUM_SLOTS, + "Bounds on randomization of #delayTimeIn.\n\n" + "@ref SFXPlayList_randomization\n" ); + addField( "delayTimeOut", TypeF32, Offset( mSlots.mDelayTimeOut.mValue, SFXPlayList ), NUM_SLOTS, + "Seconds to wait before moving out of slot after #transitionOut." ); + addField( "delayTimeOutVariance", TypePoint2F, Offset( mSlots.mDelayTimeOut.mVariance, SFXPlayList ), NUM_SLOTS, + "Bounds on randomization of #delayTimeOut.\n\n" + "@ref SFXPlayList_randomization\n" ); + addField( "fadeTimeIn", TypeF32, Offset( mSlots.mFadeTimeIn.mValue, SFXPlayList ), NUM_SLOTS, + "Seconds to fade sound in (-1 to use the track's own fadeInTime.)\n" + "@see SFXDescription::fadeTimeIn" ); + addField( "fadeTimeInVariance", TypePoint2F, Offset( mSlots.mFadeTimeIn.mVariance, SFXPlayList ), NUM_SLOTS, + "Bounds on randomization of #fadeInTime.\n\n" + "@ref SFXPlayList_randomization\n" ); + addField( "fadeTimeOut", TypeF32, Offset( mSlots.mFadeTimeOut.mValue, SFXPlayList ), NUM_SLOTS, + "Seconds to fade sound out (-1 to use the track's own fadeOutTime.)\n" + "@see SFXDescription::fadeTimeOut" ); + addField( "fadeTimeOutVariance", TypePoint2F, Offset( mSlots.mFadeTimeOut.mVariance, SFXPlayList ), NUM_SLOTS, + "Bounds on randomization of #fadeOutTime\n\n" + "@ref SFXPlayList_randomization\n" ); + addField( "referenceDistance", TypeF32, Offset( mSlots.mMinDistance.mValue, SFXPlayList ), NUM_SLOTS, + "@c referenceDistance to set for 3D sounds in this slot (<1 to use @c referenceDistance of track's own description).\n" + "@see SFXDescription::referenceDistance" ); + addField( "referenceDistanceVariance", TypePoint2F, Offset( mSlots.mMinDistance.mVariance, SFXPlayList ), NUM_SLOTS, + "Bounds on randomization of #referenceDistance.\n\n" + "@ref SFXPlayList_randomization\n" ); + addField( "maxDistance", TypeF32, Offset( mSlots.mMaxDistance.mValue, SFXPlayList ), NUM_SLOTS, + "@c maxDistance to apply to 3D sounds in this slot (<1 to use @c maxDistance of track's own description).\n" + "@see SFXDescription::maxDistance" ); + addField( "maxDistanceVariance", TypePoint2F, Offset( mSlots.mMaxDistance.mVariance, SFXPlayList ), NUM_SLOTS, + "Bounds on randomization of #maxDistance.\n\n" + "@ref SFXPlayList_randomization\n" ); + addField( "volumeScale", TypeF32, Offset( mSlots.mVolumeScale.mValue, SFXPlayList ), NUM_SLOTS, + "Scale factor to apply to volume of sounds played on this list slot.\n" + "This value will scale the actual volume level set on the track assigned to the slot, i.e. a value of 0.5 will " + "cause the track to play at half-volume." ); + addField( "volumeScaleVariance", TypePoint2F, Offset( mSlots.mVolumeScale.mVariance, SFXPlayList ), NUM_SLOTS, + "Bounds on randomization of #volumeScale.\n\n" + "@ref SFXPlayList_randomization\n" ); + addField( "pitchScale", TypeF32, Offset( mSlots.mPitchScale.mValue, SFXPlayList ), NUM_SLOTS, + "Scale factor to apply to pitch of sounds played on this list slot.\n" + "This value will scale the actual pitch set on the track assigned to the slot, i.e. a value of 0.5 will " + "cause the track to play at half its assigned speed." ); + addField( "pitchScaleVariance", TypePoint2F, Offset( mSlots.mPitchScale.mVariance, SFXPlayList ), NUM_SLOTS, + "Bounds on randomization of #pitchScale.\n\n" + "@ref SFXPlayList_randomization\n" ); + addField( "repeatCount", TypeS32, Offset( mSlots.mRepeatCount, SFXPlayList ), NUM_SLOTS, + "Number of times to loop this slot." ); + addField( "state", TypeSFXStateName, Offset( mSlots.mState, SFXPlayList ), NUM_SLOTS, + "State that must be active for this slot to play.\n\n" + "@ref SFXPlayList_states" ); + addField( "stateMode", TYPEID< EStateMode >(), Offset( mSlots.mStateMode, SFXPlayList ), NUM_SLOTS, + "Behavior when assigned state is deactivated while slot is playing.\n\n" + "@ref SFXPlayList_states" ); + + endArray( "slots" ); + + endGroup( "Sound" ); + + addGroup( "Debug" ); + + addField( "trace", TypeBool, Offset( mTrace, SFXPlayList ), + "Enable/disable execution tracing for this playlist (local only).\n" + "If this is true, SFXControllers attached to the list will automatically run in trace mode." ); + + endGroup( "Debug" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +bool SFXPlayList::preload( bool server, String& errorStr ) +{ + if( !Parent::preload( server, errorStr ) ) + return false; + + validate(); + + // Resolve SFXTracks and SFXStates on client. + + if( !server ) + { + for( U32 i = 0; i < NUM_SLOTS; ++ i ) + { + if( !sfxResolve( &mSlots.mTrack[ i ], errorStr ) ) + return false; + + if( !sfxResolve( &mSlots.mState[ i ], errorStr ) ) + return false; + } + } + + return true; +} + +//----------------------------------------------------------------------------- + +void SFXPlayList::packData( BitStream* stream ) +{ + Parent::packData( stream ); + + stream->writeInt( mRandomMode, NUM_RANDOM_MODE_BITS ); + stream->writeInt( mLoopMode, NUM_LOOP_MODE_BITS ); + stream->writeInt( mNumSlotsToPlay, NUM_SLOTS_TO_PLAY_BITS ); + + #define FOR_EACH_SLOT \ + for( U32 i = 0; i < NUM_SLOTS; ++ i ) + + FOR_EACH_SLOT stream->writeInt( mSlots.mReplayMode[ i ], NUM_REPLAY_MODE_BITS ); + FOR_EACH_SLOT stream->writeInt( mSlots.mTransitionIn[ i ], NUM_TRANSITION_MODE_BITS ); + FOR_EACH_SLOT stream->writeInt( mSlots.mTransitionOut[ i ], NUM_TRANSITION_MODE_BITS ); + FOR_EACH_SLOT stream->writeInt( mSlots.mStateMode[ i ], NUM_STATE_MODE_BITS ); + + FOR_EACH_SLOT stream->write( mSlots.mFadeTimeIn.mValue[ i ] ); + FOR_EACH_SLOT stream->write( mSlots.mFadeTimeIn.mVariance[ i ][ 0 ] ); + FOR_EACH_SLOT stream->write( mSlots.mFadeTimeIn.mVariance[ i ][ 1 ] ); + FOR_EACH_SLOT stream->write( mSlots.mFadeTimeOut.mValue[ i ] ); + FOR_EACH_SLOT stream->write( mSlots.mFadeTimeOut.mVariance[ i ][ 0 ] ); + FOR_EACH_SLOT stream->write( mSlots.mFadeTimeOut.mVariance[ i ][ 1 ] ); + FOR_EACH_SLOT stream->write( mSlots.mDelayTimeIn.mValue[ i ] ); + FOR_EACH_SLOT stream->write( mSlots.mDelayTimeIn.mVariance[ i ][ 0 ] ); + FOR_EACH_SLOT stream->write( mSlots.mDelayTimeIn.mVariance[ i ][ 1 ] ); + FOR_EACH_SLOT stream->write( mSlots.mDelayTimeOut.mValue[ i ] ); + FOR_EACH_SLOT stream->write( mSlots.mDelayTimeOut.mVariance[ i ][ 0 ] ); + FOR_EACH_SLOT stream->write( mSlots.mDelayTimeOut.mVariance[ i ][ 1 ] ); + FOR_EACH_SLOT stream->write( mSlots.mVolumeScale.mValue[ i ] ); + FOR_EACH_SLOT stream->write( mSlots.mVolumeScale.mVariance[ i ][ 0 ] ); + FOR_EACH_SLOT stream->write( mSlots.mVolumeScale.mVariance[ i ][ 1 ] ); + FOR_EACH_SLOT stream->write( mSlots.mPitchScale.mValue[ i ] ); + FOR_EACH_SLOT stream->write( mSlots.mPitchScale.mVariance[ i ][ 0 ] ); + FOR_EACH_SLOT stream->write( mSlots.mPitchScale.mVariance[ i ][ 1 ] ); + FOR_EACH_SLOT stream->write( mSlots.mRepeatCount[ i ] ); + + FOR_EACH_SLOT sfxWrite( stream, mSlots.mState[ i ] ); + FOR_EACH_SLOT sfxWrite( stream, mSlots.mTrack[ i ] ); +} + +//----------------------------------------------------------------------------- + +void SFXPlayList::unpackData( BitStream* stream ) +{ + Parent::unpackData( stream ); + + mRandomMode = ( ERandomMode ) stream->readInt( NUM_RANDOM_MODE_BITS ); + mLoopMode = ( ELoopMode ) stream->readInt( NUM_LOOP_MODE_BITS ); + mNumSlotsToPlay = stream->readInt( NUM_SLOTS_TO_PLAY_BITS ); + + FOR_EACH_SLOT mSlots.mReplayMode[ i ] = ( EReplayMode ) stream->readInt( NUM_REPLAY_MODE_BITS ); + FOR_EACH_SLOT mSlots.mTransitionIn[ i ] = ( ETransitionMode ) stream->readInt( NUM_TRANSITION_MODE_BITS ); + FOR_EACH_SLOT mSlots.mTransitionOut[ i ] = ( ETransitionMode ) stream->readInt( NUM_TRANSITION_MODE_BITS ); + FOR_EACH_SLOT mSlots.mStateMode[ i ] = ( EStateMode ) stream->readInt( NUM_STATE_MODE_BITS ); + + FOR_EACH_SLOT stream->read( &mSlots.mFadeTimeIn.mValue[ i ] ); + FOR_EACH_SLOT stream->read( &mSlots.mFadeTimeIn.mVariance[ i ][ 0 ] ); + FOR_EACH_SLOT stream->read( &mSlots.mFadeTimeIn.mVariance[ i ][ 1 ] ); + FOR_EACH_SLOT stream->read( &mSlots.mFadeTimeOut.mValue[ i ] ); + FOR_EACH_SLOT stream->read( &mSlots.mFadeTimeOut.mVariance[ i ][ 0 ] ); + FOR_EACH_SLOT stream->read( &mSlots.mFadeTimeOut.mVariance[ i ][ 1 ] ); + FOR_EACH_SLOT stream->read( &mSlots.mDelayTimeIn.mValue[ i ] ); + FOR_EACH_SLOT stream->read( &mSlots.mDelayTimeIn.mVariance[ i ][ 0 ] ); + FOR_EACH_SLOT stream->read( &mSlots.mDelayTimeIn.mVariance[ i ][ 1 ] ); + FOR_EACH_SLOT stream->read( &mSlots.mDelayTimeOut.mValue[ i ] ); + FOR_EACH_SLOT stream->read( &mSlots.mDelayTimeOut.mVariance[ i ][ 0 ] ); + FOR_EACH_SLOT stream->read( &mSlots.mDelayTimeOut.mVariance[ i ][ 1 ] ); + FOR_EACH_SLOT stream->read( &mSlots.mVolumeScale.mValue[ i ] ); + FOR_EACH_SLOT stream->read( &mSlots.mVolumeScale.mVariance[ i ][ 0 ] ); + FOR_EACH_SLOT stream->read( &mSlots.mVolumeScale.mVariance[ i ][ 1 ] ); + FOR_EACH_SLOT stream->read( &mSlots.mPitchScale.mValue[ i ] ); + FOR_EACH_SLOT stream->read( &mSlots.mPitchScale.mVariance[ i ][ 0 ] ); + FOR_EACH_SLOT stream->read( &mSlots.mPitchScale.mVariance[ i ][ 1 ] ); + FOR_EACH_SLOT stream->read( &mSlots.mRepeatCount[ i ] ); + + FOR_EACH_SLOT sfxRead( stream, &mSlots.mState[ i ] ); + FOR_EACH_SLOT sfxRead( stream, &mSlots.mTrack[ i ] ); + + #undef FOR_EACH_SLOT +} + +//----------------------------------------------------------------------------- + +void SFXPlayList::inspectPostApply() +{ + Parent::inspectPostApply(); + validate(); +} + +//----------------------------------------------------------------------------- + +void SFXPlayList::validate() +{ + if( mNumSlotsToPlay > NUM_SLOTS ) + mNumSlotsToPlay = NUM_SLOTS; + + mSlots.mFadeTimeIn.validate(); + mSlots.mFadeTimeOut.validate(); + mSlots.mDelayTimeIn.validate(); + mSlots.mDelayTimeOut.validate(); + mSlots.mVolumeScale.validate(); + mSlots.mPitchScale.validate(); +} diff --git a/Engine/source/sfx/sfxPlayList.h b/Engine/source/sfx/sfxPlayList.h new file mode 100644 index 000000000..0d9acf5c3 --- /dev/null +++ b/Engine/source/sfx/sfxPlayList.h @@ -0,0 +1,356 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXPLAYLIST_H_ +#define _SFXPLAYLIST_H_ + +#ifndef _SFXCOMMON_H_ + #include "sfx/sfxCommon.h" +#endif +#ifndef _SFXTRACK_H_ + #include "sfx/sfxTrack.h" +#endif + + +class SFXState; + + +/// A playback list of SFXTracks. +/// +/// Note that since SFXPlayLists are SFXTracks, play lists can be cascaded. +/// +/// Play lists are comprised of a sequence of slots. Each slot can be assigned +/// a track (SFXProfile or another SFXPlayList) as well as a number of options +/// that determine how the particular slot should behave. +/// +/// In addition to playing a track, each slot can do an arbitrary combination +/// of the following operations: +/// +/// - Wait: wait for the previous or all sources to stop playing +/// - Stop: stop the previous or all sources from playing +/// - Delay: wait some (optionally randomized) amount of time +/// - Shift Pitch: scale pitch when playing by optionally randomized amount +/// - Shift Volume: scale volume when playing by optionally randomized amount +/// - Fade: perform volume fade-in/out +/// - Distance: only start playing track when listener is within a certain range +/// - Loop: loop a set number of times +/// - State: play only when the given SFXState is active; transitions out of +/// slot when state is deactivated +/// +/// The order in which slots are played is either sequential (NotRandom), +/// or a random selection (StrictRandom), or a random ordering (OrderedRandom). +/// +/// Additionally, the list may be looped over in entirety (All) or looped +/// on single slots (useful for either manual playback control or lists that +/// exclusively use states). +/// +/// Be aware that playlists are affected by SFXDescriptions the same way that an +/// SFXProfile is, i.e. fades, looping, 3D sound, etc. all take effect. +/// +/// @note Playlists offer a lot of control but unfortunately they also make it +/// pretty easy at the moment to shoot yourself in the foot. +/// +class SFXPlayList : public SFXTrack +{ + public: + + typedef SFXTrack Parent; + + enum + { + /// Number of slots in a playlist. + /// + /// @note To have longer playlists, simply cascade playlists and use + /// wait behaviors. + NUM_SLOTS = 16, + + NUM_TRANSITION_MODE_BITS = 3, + NUM_LOOP_MODE_BITS = 1, + NUM_RANDOM_MODE_BITS = 2, + NUM_SLOTS_TO_PLAY_BITS = 5, + NUM_REPLAY_MODE_BITS = 3, + NUM_STATE_MODE_BITS = 2, + }; + + /// Behavior when description is set to loop. + enum ELoopMode + { + /// Start over after completing a cycle. + LOOP_All, + + /// Loop a single slot over and over. + /// + /// @note This behavior is only useful in combination with states or manual + /// playback control. To just loop over a slot for some time, set its loop + /// count instead. + LOOP_Single, + }; + + /// Random playback mode. + enum ERandomMode + { + /// No randomization of playback order. + RANDOM_NotRandom, + + /// Playback order that jumps to a random slot after completing + /// a given slot. The slot being jumped to, however, may be any + /// slot in the list including the slot that has just played. + /// + /// @note In order to ensure cycles are always finite, this mode will + /// also just do NUM_SLOTS number of transitions and then stop the + /// current cycle whether all slots have played or not. Otherwise, + /// it would be dependent on the random number sequence generated when + /// and whether at all a given cycle finishes. + RANDOM_StrictRandom, + + /// Before a cycle over the playlist starts, a random total ordering of + /// the slots is established and then played. No slot will be played + /// twice in a single cycle. + RANDOM_OrderedRandom, + }; + + /// Transitioning behavior when moving in and out of slots. + enum ETransitionMode + { + /// No specific behavior for transitioning between slots. + TRANSITION_None, + + /// Wait for single slot to stop playing. If transitioning into slot, + /// this is the slot being transitioned from. If transitioning out of slot, + /// this is the current slot. + TRANSITION_Wait, + + /// Wait for all slots to stop playing. + TRANSITION_WaitAll, + + /// Stop single slot before proceeding. If transitioning into slot, this + /// is the slot being transitioned from. If transitioning out of slot, + /// this is the current slot. + TRANSITION_Stop, + + /// Stop all playing slots before proceeding. + TRANSITION_StopAll, + }; + + /// Behavior when hitting play() on a slot that is still playing from + /// a previous cycle. + enum EReplayMode + { + /// Do not check if a source is already playing on the slot. + REPLAY_IgnorePlaying, + + /// Stop the currently playing source and start playing it from the + /// beginning. + REPLAY_RestartPlaying, + + /// Move the currently playing source to the top of the stack and pretend + /// it was started by this cycle. + /// + /// When using STATE_PauseInactive, it is usally best to also use REPLAY_KeepPlaying + /// as otherwise a new source will be spawned when the state becomes active again. + /// + /// @note When the currently playing source is paused, KeepPlaying will + /// resume playback. + REPLAY_KeepPlaying, + + /// Let the old source play and start a new one on the same slot. + REPLAY_StartNew, + + /// If there is a source currently playing on this slot, skip the play() stage. + REPLAY_SkipIfPlaying, + }; + + /// State-reaction behavior of slot once a source has started playing. + enum EStateMode + { + /// Stop and remove source when state becomes inactive. + STATE_StopInactive, + + /// Pause source when state becomes inactive and resume playback + /// when state becomes active again. + STATE_PauseInactive, + + /// Once a source has started to play, it will not be stopped due to + /// state changes. A source will, however, still be prevented from starting + /// to play when its assigned state is not active. + STATE_IgnoreInactive, + }; + + // All structures here are laid out as structures of arrays instead of arrays of structures + // to allow them to be used as fixed-size TorqueScript arrays. + + struct VariantFloat : SFXVariantFloat< NUM_SLOTS > + { + VariantFloat() + { + dMemset( mValue, 0, sizeof( mValue ) ); + dMemset( mVariance, 0, sizeof( mVariance ) ); + } + }; + + /// Settings for the playback slots. + struct SlotData + { + /// Behavior when a sound is already playing on a slot from a previous cycle. + EReplayMode mReplayMode[ NUM_SLOTS ]; + + /// Behavior when transitioning into the slot. + ETransitionMode mTransitionIn[ NUM_SLOTS ]; + + /// Behavior when transitioning out of the slot. + ETransitionMode mTransitionOut[ NUM_SLOTS ]; + + /// Seconds to fade sound in. -1 to leave at default. + VariantFloat mFadeTimeIn; + + /// Seconds to fade sound out. -1 to leave at default. + VariantFloat mFadeTimeOut; + + /// Time to delay before mTransitionIn. + VariantFloat mDelayTimeIn; + + /// Time to delay before mTransitionOut. + VariantFloat mDelayTimeOut; + + /// Volume scale factor. + VariantFloat mVolumeScale; + + /// Pitch scale factor. + VariantFloat mPitchScale; + + /// Min distance for 3D sounds. + VariantFloat mMinDistance; + + /// Max distance for 3D sounds. + VariantFloat mMaxDistance; + + /// Number of times to loop over this slot. + /// @note Each iteration will do a full transition as if proceeding + /// to a different slot. + U32 mRepeatCount[ NUM_SLOTS ]; + + /// State restriction for this slot. Slot will only play when the given + /// state is active and will be automatically transitioned from + /// if the state becomes inactive. + SFXState* mState[ NUM_SLOTS ]; + + /// Bahavior when state of this slot is deactivated and the slot's track + /// is playing. + EStateMode mStateMode[ NUM_SLOTS ]; + + /// Track to play in this slot. + SFXTrack* mTrack[ NUM_SLOTS ]; + + SlotData() + { + dMemset( mReplayMode, 0, sizeof( mReplayMode ) ); + dMemset( mTransitionIn, 0, sizeof( mTransitionIn ) ); + dMemset( mTransitionOut, 0, sizeof( mTransitionOut ) ); + dMemset( mRepeatCount, 0, sizeof( mRepeatCount ) ); + dMemset( mState, 0, sizeof( mState ) ); + dMemset( mTrack, 0, sizeof( mTrack ) ); + dMemset( mStateMode, 0, sizeof( mStateMode ) ); + + for( U32 i = 0; i < NUM_SLOTS; ++ i ) + { + mTransitionOut[ i ] = TRANSITION_Wait; + mVolumeScale.mValue[ i ] = 1.f; + mPitchScale.mValue[ i ] = 1.f; + mFadeTimeIn.mValue[ i ] = -1.f; // Don't touch by default. + mFadeTimeOut.mValue[ i ] = -1.f; // Don't touch by default. + mMinDistance.mValue[ i ] = -1.f; // Don't touch by default. + mMaxDistance.mValue[ i ] = -1.f; // Don't touch by default. + } + } + }; + + protected: + + /// Trace interpreter execution. This field is not networked. + bool mTrace; + + /// Select slots at random. + ERandomMode mRandomMode; + + /// Loop over slots in this list. + ELoopMode mLoopMode; + + /// Number of slots to play from list. This can be used, for example, + /// to create a list of tracks where only a single track is selected and + /// played for each cycle. + U32 mNumSlotsToPlay; + + /// Data for each of the playlist slots. + SlotData mSlots; + + public: + + SFXPlayList(); + + /// Make all settings conform to constraints. + void validate(); + + /// Return true if execution tracing is enabled on this list. + bool trace() const { return mTrace; } + + /// Return the number of slots to play from this list in a single cycle. + U32 getNumSlotsToPlay() const { return mNumSlotsToPlay; } + + /// Return the slot order randomization behavior. + ERandomMode getRandomMode() const { return mRandomMode; } + + /// Return the loop mode (only relevant if this is a looped playlist). + ELoopMode getLoopMode() const { return mLoopMode; } + + /// Return the total number of slots in the list. + U32 getNumSlots() const { return NUM_SLOTS; } + + /// Return the slot data for this list. + const SlotData& getSlots() const { return mSlots; } + + DECLARE_CONOBJECT( SFXPlayList ); + DECLARE_CATEGORY( "SFX" ); + DECLARE_DESCRIPTION( "A playback list of SFXProfiles or nested SFXPlayLists." ); + + // SimDataBlock. + virtual bool preload( bool server, String& errorStr ); + virtual void packData( BitStream* stream ); + virtual void unpackData( BitStream* stream ); + virtual void inspectPostApply(); + + static void initPersistFields(); +}; + + +typedef SFXPlayList::ELoopMode SFXPlayListLoopMode; +typedef SFXPlayList::ETransitionMode SFXPlayListTransitionMode; +typedef SFXPlayList::EStateMode SFXPlayListStateMode; +typedef SFXPlayList::ERandomMode SFXPlayListRandomMode; +typedef SFXPlayList::EReplayMode SFXPlayListReplayMode; + +DefineEnumType( SFXPlayListLoopMode ); +DefineEnumType( SFXPlayListTransitionMode ); +DefineEnumType( SFXPlayListStateMode ); +DefineEnumType( SFXPlayListRandomMode ); +DefineEnumType( SFXPlayListReplayMode ); + +#endif // _SFXPLAYLIST_H_ diff --git a/Engine/source/sfx/sfxProfile.cpp b/Engine/source/sfx/sfxProfile.cpp new file mode 100644 index 000000000..80bc5a698 --- /dev/null +++ b/Engine/source/sfx/sfxProfile.cpp @@ -0,0 +1,374 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "sfx/sfxProfile.h" +#include "sfx/sfxDescription.h" +#include "sfx/sfxSystem.h" +#include "sfx/sfxStream.h" +#include "sim/netConnection.h" +#include "core/stream/bitStream.h" +#include "core/resourceManager.h" +#include "console/engineAPI.h" + + +IMPLEMENT_CO_DATABLOCK_V1( SFXProfile ); + + +ConsoleDocClass( SFXProfile, + "@brief Encapsulates a single sound file for playback by the sound system.\n\n" + + "SFXProfile combines a sound description (SFXDescription) with a sound file such that it can be played " + "by the sound system. To be able to play a sound file, the sound system will always require a profile " + "for it to be created. However, several of the SFX functions (sfxPlayOnce(), sfxCreateSource()) perform " + "this creation internally for convenience using temporary profile objects.\n\n" + + "Sound files can be in either OGG or WAV format. However, extended format support is available when using FMOD. " + "See @ref SFX_formats.\n\n" + + "@section SFXProfile_loading Profile Loading\n\n" + + "By default, the sound data referenced by a profile will be loaded when the profile is first played and the " + "data then kept until either the profile is deleted or until the sound device on which the sound data is held " + "is deleted.\n\n" + + "This initial loading my incur a small delay when the sound is first played. To avoid this, a profile may be " + "expicitly set to load its sound data immediately when the profile is added to the system. This is done by " + "setting the #preload property to true.\n\n" + + "@note Sounds using streamed playback (SFXDescription::isStreaming) cannot be preloaded and will thus " + "ignore the #preload flag.\n\n" + + "@tsexample\n" + "datablock SFXProfile( Shore01Snd )\n" + "{\n" + " fileName = \"art/sound/Lakeshore_mono_01\";\n" + " description = Shore01Looping3d;\n" + " preload = true;\n" + "};\n" + "@endtsexample\n\n" + + "@ingroup SFX\n" + "@ingroup Datablocks\n" +); + + +//----------------------------------------------------------------------------- + +SFXProfile::SFXProfile() + : mPreload( false ) +{ +} + +//----------------------------------------------------------------------------- + +SFXProfile::SFXProfile( SFXDescription* desc, const String& filename, bool preload ) + : Parent( desc ), + mFilename( filename ), + mPreload( preload ) +{ +} + +//----------------------------------------------------------------------------- + +SFXProfile::~SFXProfile() +{ +} + +//----------------------------------------------------------------------------- + +void SFXProfile::initPersistFields() +{ + addGroup( "Sound" ); + + addField( "filename", TypeStringFilename, Offset( mFilename, SFXProfile ), + "%Path to the sound file.\n" + "If the extension is left out, it will be inferred by the sound system. This allows to " + "easily switch the sound format without having to go through the profiles and change the " + "filenames there, too.\n" ); + addField( "preload", TypeBool, Offset( mPreload, SFXProfile ), + "Whether to preload sound data when the profile is added to system.\n" + "@note This flag is ignored by streamed sounds.\n\n" + "@ref SFXProfile_loading" ); + + endGroup( "Sound" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +bool SFXProfile::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + // If we're a streaming profile we don't preload + // or need device events. + if( SFX && !mDescription->mIsStreaming ) + { + // If preload is enabled we load the resource + // and device buffer now to avoid a delay on + // first playback. + if( mPreload && !_preloadBuffer() ) + Con::errorf( "SFXProfile(%s)::onAdd: The preload failed!", getName() ); + } + + _registerSignals(); + + return true; +} + +//----------------------------------------------------------------------------- + +void SFXProfile::onRemove() +{ + _unregisterSignals(); + + Parent::onRemove(); +} + +//----------------------------------------------------------------------------- + +bool SFXProfile::preload( bool server, String &errorStr ) +{ + if ( !Parent::preload( server, errorStr ) ) + return false; + + // TODO: Investigate how NetConnection::filesWereDownloaded() + // effects the system. + + // Validate the datablock... has nothing to do with mPreload. + if( !server && + NetConnection::filesWereDownloaded() && + ( mFilename.isEmpty() || !SFXResource::exists( mFilename ) ) ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- + +void SFXProfile::packData(BitStream* stream) +{ + Parent::packData( stream ); + + char buffer[256]; + if ( mFilename.isEmpty() ) + buffer[0] = 0; + else + dStrncpy( buffer, mFilename.c_str(), 256 ); + stream->writeString( buffer ); + + stream->writeFlag( mPreload ); +} + +//----------------------------------------------------------------------------- + +void SFXProfile::unpackData(BitStream* stream) +{ + Parent::unpackData( stream ); + + char buffer[256]; + stream->readString( buffer ); + mFilename = buffer; + + mPreload = stream->readFlag(); +} + +//----------------------------------------------------------------------------- + +bool SFXProfile::isLooping() const +{ + return getDescription()->mIsLooping; +} + +//----------------------------------------------------------------------------- + +void SFXProfile::_registerSignals() +{ + SFX->getEventSignal().notify( this, &SFXProfile::_onDeviceEvent ); + ResourceManager::get().getChangedSignal().notify( this, &SFXProfile::_onResourceChanged ); +} + +//----------------------------------------------------------------------------- + +void SFXProfile::_unregisterSignals() +{ + ResourceManager::get().getChangedSignal().remove( this, &SFXProfile::_onResourceChanged ); + if( SFX ) + SFX->getEventSignal().remove( this, &SFXProfile::_onDeviceEvent ); +} + +//----------------------------------------------------------------------------- + +void SFXProfile::_onDeviceEvent( SFXSystemEventType evt ) +{ + switch( evt ) + { + case SFXSystemEvent_CreateDevice: + { + if( mPreload && !mDescription->mIsStreaming && !_preloadBuffer() ) + Con::errorf( "SFXProfile::_onDeviceEvent: The preload failed! %s", getName() ); + break; + } + + default: + break; + } +} + +//----------------------------------------------------------------------------- + +void SFXProfile::_onResourceChanged( const Torque::Path& path ) +{ + if( path != Path( mFilename ) ) + return; + + // Let go of the old resource and buffer. + + mResource = NULL; + mBuffer = NULL; + + // Load the new resource. + + getResource(); + + if( mPreload && !mDescription->mIsStreaming ) + { + if( !_preloadBuffer() ) + Con::errorf( "SFXProfile::_onResourceChanged() - failed to preload '%s'", mFilename.c_str() ); + } + + mChangedSignal.trigger( this ); +} + +//----------------------------------------------------------------------------- + +bool SFXProfile::_preloadBuffer() +{ + AssertFatal( !mDescription->mIsStreaming, "SFXProfile::_preloadBuffer() - must not be called for streaming profiles" ); + + mBuffer = _createBuffer(); + return ( !mBuffer.isNull() ); +} + +//----------------------------------------------------------------------------- + +Resource& SFXProfile::getResource() +{ + if( !mResource && !mFilename.isEmpty() ) + mResource = SFXResource::load( mFilename ); + + return mResource; +} + +//----------------------------------------------------------------------------- + +SFXBuffer* SFXProfile::getBuffer() +{ + if ( mDescription->mIsStreaming ) + { + // Streaming requires unique buffers per + // source, so this creates a new buffer. + if ( SFX ) + return _createBuffer(); + + return NULL; + } + + if ( mBuffer.isNull() ) + _preloadBuffer(); + + return mBuffer; +} + +//----------------------------------------------------------------------------- + +SFXBuffer* SFXProfile::_createBuffer() +{ + SFXBuffer* buffer = 0; + + // Try to create through SFXDevie. + + if( !mFilename.isEmpty() && SFX ) + { + buffer = SFX->_createBuffer( mFilename, mDescription ); + if( buffer ) + { + #ifdef TORQUE_DEBUG + const SFXFormat& format = buffer->getFormat(); + Con::printf( "%s SFX: %s (%i channels, %i kHz, %.02f sec, %i kb)", + mDescription->mIsStreaming ? "Streaming" : "Loaded", mFilename.c_str(), + format.getChannels(), + format.getSamplesPerSecond() / 1000, + F32( buffer->getDuration() ) / 1000.0f, + format.getDataLength( buffer->getDuration() ) / 1024 ); + #endif + } + } + + // If that failed, load through SFXResource. + + if( !buffer ) + { + Resource< SFXResource >& resource = getResource(); + if( resource != NULL && SFX ) + { + #ifdef TORQUE_DEBUG + const SFXFormat& format = resource->getFormat(); + Con::printf( "%s SFX: %s (%i channels, %i kHz, %.02f sec, %i kb)", + mDescription->mIsStreaming ? "Streaming" : "Loading", resource->getFileName().c_str(), + format.getChannels(), + format.getSamplesPerSecond() / 1000, + F32( resource->getDuration() ) / 1000.0f, + format.getDataLength( resource->getDuration() ) / 1024 ); + #endif + + ThreadSafeRef< SFXStream > sfxStream = resource->openStream(); + buffer = SFX->_createBuffer( sfxStream, mDescription ); + } + } + + return buffer; +} + +//----------------------------------------------------------------------------- + +U32 SFXProfile::getSoundDuration() +{ + Resource< SFXResource >& resource = getResource(); + if( resource != NULL ) + return mResource->getDuration(); + else + return 0; +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXProfile, getSoundDuration, F32, (),, + "Return the length of the sound data in seconds.\n\n" + "@return The length of the sound data in seconds or 0 if the sound referenced by the profile could not be found." ) +{ + return ( F32 ) object->getSoundDuration() * 0.001f; +} diff --git a/Engine/source/sfx/sfxProfile.h b/Engine/source/sfx/sfxProfile.h new file mode 100644 index 000000000..70ac4f097 --- /dev/null +++ b/Engine/source/sfx/sfxProfile.h @@ -0,0 +1,181 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXPROFILE_H_ +#define _SFXPROFILE_H_ + +#ifndef _SFXTRACK_H_ + #include "sfx/sfxTrack.h" +#endif +#ifndef _SFXRESOURCE_H_ + #include "sfx/sfxResource.h" +#endif +#ifndef _SFXBUFFER_H_ + #include "sfx/sfxBuffer.h" +#endif +#ifndef _SFXSYSTEM_H_ + #include "sfx/sfxSystem.h" +#endif +#ifndef _TSIGNAL_H_ + #include "core/util/tSignal.h" +#endif + + +class SFXDescription; + + +/// The SFXProfile is used to define a sound for playback. +/// +/// An SFXProfile will first try to load its file directly through the SFXDevice. +/// Only if this fails (which is the case for most SFXDevices as these do not +/// implement their own custom sound format loading), the file is loaded through +/// SFXResource. +/// +/// A few tips: +/// +/// Make sure each of the defined SFXProfile's fileName doesn't specify +/// an extension. An extension does not need to be specified and by not +/// explicitly saying .ogg or .wav it will allow you to change from one +/// format to the other without having to change the scripts. +/// +/// Make sure that server SFXProfiles are defined with the datablock +/// keyword, and that client SFXProfiles are defined with the 'new' +/// keyword. +/// +/// Make sure SFXDescriptions exist for your SFXProfiles. Also make sure +/// that SFXDescriptions are defined BEFORE SFXProfiles. This is especially +/// important if your SFXProfiles are located in different files than your +/// SFXDescriptions. In this case, make sure the files containing SFXDescriptions +/// are exec'd before the files containing the SFXProfiles. +/// +/// @note Live asset update will not work with files loaded directly through +/// the SFXDevice. +class SFXProfile : public SFXTrack +{ + public: + + friend class SFXEmitter; // For access to mFilename + + typedef SFXTrack Parent; + + typedef Signal< void( SFXProfile* ) > ChangedSignal; + + protected: + + /// The sound data. + /// @note ATM only valid if loaded through SFX's loading system rather than + /// through the SFXDevice's loading system. + Resource< SFXResource > mResource; + + /// The sound filename. If no extension is specified + /// the system will try .wav first then other formats. + String mFilename; + + /// If true the sound data will be loaded from + /// disk and possibly cached with the active + /// device before the first call for playback. + bool mPreload; + + /// The device specific data buffer. + /// This is only used if for non-streaming sounds. + StrongWeakRefPtr< SFXBuffer > mBuffer; + + /// + ChangedSignal mChangedSignal; + + /// Called when the buffer needs to be preloaded. + bool _preloadBuffer(); + + /// Callback for device events. + void _onDeviceEvent( SFXSystemEventType evt ); + + /// + SFXBuffer* _createBuffer(); + + /// + void _onResourceChanged( const Torque::Path& path ); + + /// + void _registerSignals(); + + /// + void _unregisterSignals(); + + public: + + /// This is only here to allow DECLARE_CONOBJECT + /// to create us from script. You shouldn't use + /// this constructor from C++. + explicit SFXProfile(); + + /// The constructor. + SFXProfile( SFXDescription* desc, + const String& filename = String(), + bool preload = false ); + + /// The destructor. + virtual ~SFXProfile(); + + DECLARE_CONOBJECT( SFXProfile ); + + static void initPersistFields(); + + // SFXTrack. + virtual bool isLooping() const; + + // SimObject + bool onAdd(); + void onRemove(); + void packData( BitStream* stream ); + void unpackData( BitStream* stream ); + + /// Returns the sound filename. + const String& getSoundFileName() const { return mFilename; } + + /// @note This has nothing to do with mPreload. + /// @see SimDataBlock::preload + bool preload( bool server, String &errorStr ); + + /// Returns the sound resource loading it from + /// disk if it hasn't been preloaded. + /// + /// @note May be NULL if file is loaded directly through SFXDevice. + Resource& getResource(); + + /// Returns the device specific buffer for this for this + /// sound. If it hasn't been preloaded it will be loaded + /// at this time. + /// + /// If this is a streaming profile then the buffer + /// returned must be deleted by the caller. + SFXBuffer* getBuffer(); + + /// Gets the sound duration in milliseconds or + /// returns 0 if the resource was not found. + U32 getSoundDuration(); + + /// + ChangedSignal& getChangedSignal() { return mChangedSignal; } +}; + + +#endif // _SFXPROFILE_H_ \ No newline at end of file diff --git a/Engine/source/sfx/sfxProvider.cpp b/Engine/source/sfx/sfxProvider.cpp new file mode 100644 index 000000000..22949e8a1 --- /dev/null +++ b/Engine/source/sfx/sfxProvider.cpp @@ -0,0 +1,95 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/strings/stringFunctions.h" +#include "sfx/sfxProvider.h" + +SFXProvider* SFXProvider::smProviders = NULL; +Vector SFXProvider::sAllProviders( __FILE__, __LINE__ ); + +SFXProvider* SFXProvider::findProvider( String providerName ) +{ + if( providerName.isEmpty() ) + return NULL; + + SFXProvider* curr = smProviders; + for ( ; curr != NULL; curr = curr->mNextProvider ) + { + if( curr->getName().equal( providerName, String::NoCase ) ) + return curr; + } + + return NULL; +} + +void SFXProvider::regProvider( SFXProvider* provider ) +{ + AssertFatal( provider, "Got null provider!" ); + AssertFatal( findProvider( provider->getName() ) == NULL, "Can't register provider twice!" ); + AssertFatal( provider->mNextProvider == NULL, "Can't register provider twice!" ); + + SFXProvider* oldHead = smProviders; + smProviders = provider; + provider->mNextProvider = oldHead; +} + +SFXProvider::SFXProvider( const String& name ) + : mName( name ), + mNextProvider( NULL ) +{ + VECTOR_SET_ASSOCIATION( mDeviceInfo ); + + sAllProviders.push_back( this ); +} + +void SFXProvider::initializeAllProviders() +{ + + for (U32 i = 0; i < sAllProviders.size(); i++) + sAllProviders[i]->init(); + +} + +SFXProvider::~SFXProvider() +{ + SFXDeviceInfoVector::iterator iter = mDeviceInfo.begin(); + for ( ; iter != mDeviceInfo.end(); iter++ ) + delete *iter; +} + +SFXDeviceInfo* SFXProvider::_findDeviceInfo( const String& deviceName ) +{ + SFXDeviceInfoVector::iterator iter = mDeviceInfo.begin(); + for ( ; iter != mDeviceInfo.end(); iter++ ) + { + if( deviceName.equal( ( *iter )->name, String::NoCase ) ) + return *iter; + } + + // If not found and deviceName is empty, + // return first (default) device. + + if( deviceName.isEmpty() && mDeviceInfo.size() > 0 ) + return mDeviceInfo[ 0 ]; + + return NULL; +} diff --git a/Engine/source/sfx/sfxProvider.h b/Engine/source/sfx/sfxProvider.h new file mode 100644 index 000000000..7cf3408db --- /dev/null +++ b/Engine/source/sfx/sfxProvider.h @@ -0,0 +1,124 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXPROVIDER_H_ +#define _SFXPROVIDER_H_ + +#ifndef _TVECTOR_H_ + #include "core/util/tVector.h" +#endif + + +class SFXDevice; + + + +struct SFXDeviceInfo +{ + String driver; + String name; + bool hasHardware; + S32 maxBuffers; + + virtual ~SFXDeviceInfo() {} +}; + +typedef Vector SFXDeviceInfoVector; + +class SFXProvider +{ + friend class SFXSystem; + + private: + + /// The head of the linked list of avalible providers. + static SFXProvider* smProviders; + + /// The next provider in the linked list of available providers. + SFXProvider* mNextProvider; + + /// The provider name which is passed by the concrete provider + /// class to the SFXProvider constructor. + String mName; + + static Vector sAllProviders; + + protected: + + /// The array of avaIlable devices from this provider. The + /// concrete provider class will fill this on construction. + SFXDeviceInfoVector mDeviceInfo; + + /// This registers the provider to the available provider list. It should be called + /// for providers that are properly initialized and available for device enumeration and creation. + /// the add and registration process is 2 steps to avoid issues when TGEA is used as a shared library (specifically on Windows) + static void regProvider( SFXProvider* provider ); + + virtual void init() = 0; + + SFXProvider( const String& name ); + ~SFXProvider(); + + /// Look up the SFXDeviceInfo for the given device in mDeviceInfo. + /// Return default device (first in list) if no other device matches (or null if device list is empty). + SFXDeviceInfo* _findDeviceInfo( const String& deviceName ); + + /// This is called from SFXSystem to create a new device. Must be implemented + /// by all contrete provider classes. + /// + /// @param deviceName The case sensitive name of the device or NULL to create the + // default device. + /// @param useHardware Toggles the use of hardware processing when available. + /// @param maxBuffers The maximum buffers for this device to use or -1 + /// for the device to pick a reasonable default for that device. + /// + /// @return Returns the created device or NULL for failure. + /// + virtual SFXDevice* createDevice( const String& deviceName, bool useHardware, S32 maxBuffers ) = 0; + + public: + + /// Returns a specific provider by searching the provider list + /// for the first provider with the case sensitive name. + static SFXProvider* findProvider( String providerName ); + + /// Returns the first provider in the provider list. Use + /// getNextProvider() to iterate over list. + static SFXProvider* getFirstProvider() { return smProviders; } + + /// Returns the next provider in the provider list or NULL + /// when the end of the list is reached. + SFXProvider* getNextProvider() const { return mNextProvider; } + + /// The case sensitive name of this provider. + const String& getName() const { return mName; } + + /// Returns a read only vector with device information for + /// all creatable devices available from this provider. + const SFXDeviceInfoVector& getDeviceInfo() const { return mDeviceInfo; } + + static void initializeAllProviders(); + +}; + + +#endif // _SFXPROVIDER_H_ \ No newline at end of file diff --git a/Engine/source/sfx/sfxResource.cpp b/Engine/source/sfx/sfxResource.cpp new file mode 100644 index 000000000..d563068ad --- /dev/null +++ b/Engine/source/sfx/sfxResource.cpp @@ -0,0 +1,82 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/sfxResource.h" +#include "sfx/sfxFileStream.h" +#include "core/util/fourcc.h" +#include "core/resourceManager.h" + + + +// Ugly workaround to keep the constructor protected. +struct SFXResource::_NewHelper +{ + static SFXResource* New( String fileName, const ThreadSafeRef< SFXStream >& stream ) + { + return new SFXResource( fileName, stream ); + } +}; + + +template<> +void* Resource< SFXResource >::create( const Torque::Path& path ) +{ + String fullPath = path.getFullPath(); + + // Try to open the stream. + ThreadSafeRef< SFXStream > stream = SFXFileStream::create( fullPath ); + if( !stream ) + return NULL; + + // We have a valid stream... create the resource. + SFXResource* res = SFXResource::_NewHelper::New( fullPath, stream ); + + return res; +} + +template<> +ResourceBase::Signature Resource< SFXResource >::signature() +{ + return MakeFourCC( 's', 'f', 'x', 'r' ); +} + +Resource< SFXResource > SFXResource::load( String filename ) +{ + return ResourceManager::get().load( filename ); +} + +SFXResource::SFXResource( String fileName, SFXStream *stream ) + : mFileName( fileName ), + mFormat( stream->getFormat() ), + mDuration( stream->getDuration() ) +{ +} + +bool SFXResource::exists( String filename ) +{ + return SFXFileStream::exists( filename ); +} + +SFXStream* SFXResource::openStream() +{ + return SFXFileStream::create( mFileName ); +} diff --git a/Engine/source/sfx/sfxResource.h b/Engine/source/sfx/sfxResource.h new file mode 100644 index 000000000..1fc4ba902 --- /dev/null +++ b/Engine/source/sfx/sfxResource.h @@ -0,0 +1,114 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXRESOURCE_H_ +#define _SFXRESOURCE_H_ + +#ifndef _SFXCOMMON_H_ + #include "sfx/sfxCommon.h" +#endif +#ifndef __RESOURCE_H__ + #include "core/resource.h" +#endif + + +class SFXStream; + + +/// This is the base class for all sound file resources including +/// streamed sound files. It acts much like an always in-core +/// header to the actual sound data which is read through an SFXStream. +/// +/// The first step occurs at ResourceManager::load() time at which +/// only the header information, the format, size frequency, and +/// looping flag, are loaded from the sound file. This provides +/// just the nessasary information to simulate sound playback for +/// sounds playing just out of the users hearing range. +/// +/// The second step loads the actual sound data or begins filling +/// the stream buffer. This is triggered by a call to openStream(). +/// SFXProfile, for example, does this when mPreload is enabled. +/// +class SFXResource +{ + public: + + typedef void Parent; + + protected: + + /// The constructor is protected. + /// @see SFXResource::load() + SFXResource(); + + /// Path to the sound file. + String mFileName; + + /// The format of the sample data. + SFXFormat mFormat; + + /// The length of the sample in milliseconds. + U32 mDuration; + + /// Construct a resource instance for the given file. Format and duration + /// are read from the given stream. + SFXResource( String fileName, SFXStream* stream ); + + public: + + /// The destructor. + virtual ~SFXResource() {} + + /// This is a helper function used by SFXProfile for load + /// a sound resource. It takes care of trying different + /// types for extension-less filenames. + /// + /// @param filename The sound file path with or without extension. + /// + static Resource< SFXResource > load( String filename ); + + /// A helper function which returns true if the + /// sound resource exists. + /// + /// @param filename The sound file path with or without extension. + /// + static bool exists( String filename ); + + /// Return the path to the sound file. + const String& getFileName() { return mFileName; } + + /// Returns the total playback time milliseconds. + U32 getDuration() const { return mDuration; } + + /// The format of the data in the resource. + const SFXFormat& getFormat() const { return mFormat; } + + /// Open a stream for reading the resource's sample data. + SFXStream* openStream(); + + // Internal. + struct _NewHelper; + friend struct _NewHelper; +}; + + +#endif // _SFXRESOURCE_H_ diff --git a/Engine/source/sfx/sfxSound.cpp b/Engine/source/sfx/sfxSound.cpp new file mode 100644 index 000000000..b2fd1059a --- /dev/null +++ b/Engine/source/sfx/sfxSound.cpp @@ -0,0 +1,708 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/sfxSound.h" +#include "sfx/sfxDevice.h" +#include "sfx/sfxVoice.h" +#include "sfx/sfxSystem.h" +#include "sfx/sfxBuffer.h" +#include "sfx/sfxStream.h" +#include "sfx/sfxDescription.h" +#include "core/util/safeDelete.h" +#include "console/engineAPI.h" + + +//#define DEBUG_SPEW + + +IMPLEMENT_CONOBJECT( SFXSound ); + +ConsoleDocClass( SFXSound, + "@brief A sound controller that directly plays a single sound file.\n\n" + + "When playing individual audio files, SFXSounds are implicitly created by the sound system.\n\n" + + "Each sound source has an associated play cursor that can be queried and explicitly positioned " + "by the user. The cursor is a floating-point value measured in seconds.\n\n" + + "For streamed sources, playback may not be continuous in case the streaming queue is interrupted.\n\n" + + "@note This class cannot be instantiated directly by the user but rather is implicitly created by the sound " + "system when sfxCreateSource() or sfxPlayOnce() is called on a SFXProfile instance.\n\n" + + "@section SFXSound_virtualization Sounds and Voices\n\n" + + "To actually emit an audible signal, a sound must allocate a resource on the sound device through " + "which the sound data is being played back. This resource is called 'voice'.\n\n" + + "As with other types of resources, the availability of these resources may be restricted, i.e. a given " + "sound device will usually only support a fixed number of voices that are playing at the same time. Since, " + "however, there may be arbitrary many SFXSounds instantiated and playing at the same time, this needs to be " + "solved. \n\n" + + "@see SFXDescription::priority\n" + + "@ingroup SFX" +); + + +//----------------------------------------------------------------------------- + +SFXSound::SFXSound() + : mVoice( NULL ) +{ + // NOTE: This should never be used directly + // and is only here to satisfy satisfy the + // construction needs of IMPLEMENT_CONOBJECT. +} + +//----------------------------------------------------------------------------- + +SFXSound::SFXSound( SFXProfile *profile, SFXDescription* desc ) + : Parent( profile, desc ), + mVoice( NULL ) +{ +} + +//----------------------------------------------------------------------------- + +SFXSound* SFXSound::_create( SFXDevice *device, SFXProfile *profile ) +{ + AssertFatal( profile, "SFXSound::_create() - Got a null profile!" ); + + SFXDescription* desc = profile->getDescription(); + if ( !desc ) + { + Con::errorf( "SFXSound::_create() - Profile has null description!" ); + return NULL; + } + + // Create the sound and register it. + + SFXSound* sound = new SFXSound( profile, desc ); + sound->registerObject(); + + // Initialize the buffer. + + SFXBuffer* buffer = profile->getBuffer(); + if( !buffer ) + { + sound->deleteObject(); + + Con::errorf( "SFXSound::_create() - Could not create device buffer!" ); + return NULL; + } + + sound->_setBuffer( buffer ); + + // The sound is a console object... register it. + + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXSound] new sound '%i' with profile '%i' (\"%s\")", + sound->getId(), profile->getId(), profile->getName() ); + #endif + + // Hook up reloading. + + profile->getChangedSignal().notify( sound, &SFXSound::_onProfileChanged ); + + return sound; +} + +//----------------------------------------------------------------------------- + +SFXSound* SFXSound::_create( SFXDevice* device, + const ThreadSafeRef< SFXStream >& stream, + SFXDescription* description ) +{ + AssertFatal( stream.ptr() != NULL, "SFXSound::_create() - Got a null stream!" ); + AssertFatal( description, "SFXSound::_create() - Got a null description!" ); + + // Create the source and register it. + + SFXSound* source = new SFXSound( NULL, description ); + source->registerObject(); + + // Create the buffer. + + SFXBuffer* buffer = SFX->_createBuffer( stream, description ); + if( !buffer ) + { + source->deleteObject(); + + Con::errorf( "SFXSound::_create() - Could not create device buffer!" ); + return NULL; + } + + source->_setBuffer( buffer ); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXSound] new source '%i' for stream", source->getId() ); + #endif + + return source; +} + +//----------------------------------------------------------------------------- + +void SFXSound::_reloadBuffer() +{ + SFXProfile* profile = getProfile(); + if( profile != NULL && _releaseVoice() ) + { + SFXBuffer* buffer = profile->getBuffer(); + if( !buffer ) + { + Con::errorf( "SFXSound::_reloadBuffer() - Could not create device buffer!" ); + return; + } + + _setBuffer( buffer ); + + if( getLastStatus() == SFXStatusPlaying ) + SFX->_assignVoice( this ); + } +} + +//----------------------------------------------------------------------------- + +void SFXSound::_setBuffer( SFXBuffer* buffer ) +{ + mBuffer = buffer; + + // There is no telling when the device will be + // destroyed and the buffers deleted. + // + // By caching the duration now we can allow sources + // to continue virtual playback until the device + // is restored. + mDuration = mBuffer->getDuration(); +} + +//----------------------------------------------------------------------------- + +bool SFXSound::_allocVoice( SFXDevice* device ) +{ + // We shouldn't have any existing voice! + AssertFatal( !mVoice, "SFXSound::_allocVoice() - Already had a voice!" ); + + // Must not assign voice to source that isn't playing. + AssertFatal( getLastStatus() == SFXStatusPlaying, + "SFXSound::_allocVoice() - Source is not playing!" ); + + // The buffer can be lost when the device is reset + // or changed, so initialize it if we have to. If + // that fails then we cannot create the voice. + + if( mBuffer.isNull() ) + { + SFXProfile* profile = getProfile(); + if( profile != NULL ) + { + SFXBuffer* buffer = profile->getBuffer(); + if( buffer ) + _setBuffer( buffer ); + } + + if( mBuffer.isNull() ) + return false; + } + + // Ask the device for a voice based on this buffer. + mVoice = device->createVoice( is3d(), mBuffer ); + if( !mVoice ) + return false; + + // Set initial properties. + + mVoice->setVolume( mPreAttenuatedVolume ); + mVoice->setPitch( mEffectivePitch ); + mVoice->setPriority( mEffectivePriority ); + if( mDescription->mRolloffFactor != -1.f ) + mVoice->setRolloffFactor( mDescription->mRolloffFactor ); + + // Set 3D parameters. + + if( is3d() ) + { + // Scatter the position, if requested. Do this only once so + // we don't change position when resuming from virtualized + // playback. + + if( !mTransformScattered ) + _scatterTransform(); + + // Set the 3D attributes. + + setTransform( mTransform ); + setVelocity( mVelocity ); + _setMinMaxDistance( mMinDistance, mMaxDistance ); + _setCone( mConeInsideAngle, mConeOutsideAngle, mConeOutsideVolume ); + } + + // Set reverb, if enabled. + + if( mDescription->mUseReverb ) + mVoice->setReverb( mDescription->mReverb ); + + // Update the duration... it shouldn't have changed, but + // its probably better that we're accurate if it did. + mDuration = mBuffer->getDuration(); + + // If virtualized playback has been started, we transfer its position to the + // voice and stop virtualization. + + const U32 playTime = mPlayTimer.getPosition(); + + if( playTime > 0 ) + { + const U32 pos = mBuffer->getFormat().getSampleCount( playTime ); + mVoice->setPosition( pos); + } + + mVoice->play( isLooping() ); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXSound] allocated voice for source '%i' (pos=%i, 3d=%i, vol=%f)", + getId(), playTime, is3d(), mPreAttenuatedVolume ); + #endif + + return true; +} + +//----------------------------------------------------------------------------- + +void SFXSound::_onParameterEvent( SFXParameter* parameter, SFXParameterEvent event ) +{ + Parent::_onParameterEvent( parameter, event ); + + switch( event ) + { + case SFXParameterEvent_ValueChanged: + switch( parameter->getChannel() ) + { + case SFXChannelCursor: + setPosition( parameter->getValue() * 1000.f ); + break; + + default: + break; + } + break; + + default: + break; + } +} + +//----------------------------------------------------------------------------- + +void SFXSound::onRemove() +{ + SFXProfile* profile = getProfile(); + if( profile != NULL ) + profile->getChangedSignal().remove( this, &SFXSound::_onProfileChanged ); + + Parent::onRemove(); +} + +//----------------------------------------------------------------------------- + +void SFXSound::onDeleteNotify( SimObject* object ) +{ + if( object == mDescription ) + { + deleteObject(); + return; + } + + Parent::onDeleteNotify( object ); +} + +//----------------------------------------------------------------------------- + +bool SFXSound::_releaseVoice() +{ + if( !mVoice ) + return true; + + // Refuse to release a voice for a streaming buffer that + // is not coming from a profile. For streaming buffers, we will + // have to release the buffer, too, and without a profile we don't + // know how to recreate the stream. + + if( isStreaming() && !mTrack ) + return false; + + // If we're currently playing, transfer our playback position + // to the playtimer so we can virtualize playback while not + // having a voice. + + SFXStatus status = getLastStatus(); + if( status == SFXStatusPlaying || status == SFXStatusBlocked ) + { + // Sync up the play timer with the voice's current position to make + // sure we handle any lag that's cropped up. + + mPlayTimer.setPosition( mVoice->getPosition() ); + + if( status == SFXStatusBlocked ) + status = SFXStatusPlaying; + } + + mVoice = NULL; + + // If this is a streaming source, release our buffer, too. + // Otherwise the voice will stick around as it is uniquely assigned to + // the buffer. When we get reassigned a voice, we will have to do + // a full stream seek anyway, so it's no real loss here. + + if( isStreaming() ) + mBuffer = NULL; + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXSound] release voice for source '%i' (status: %s)", + getId(), SFXStatusToString( status ) ); + #endif + + return true; +} + +//----------------------------------------------------------------------------- + +void SFXSound::_play() +{ + Parent::_play(); + + if( mVoice ) + mVoice->play( isLooping() ); + else + { + // To ensure the fastest possible reaction + // to this playback let the system reassign + // voices immediately. + SFX->_assignVoice( this ); + + // If we did not get assigned a voice, we'll be + // running virtualized. + + #ifdef DEBUG_SPEW + if( !mVoice ) + Platform::outputDebugString( "[SFXSound] virtualizing playback of source '%i'", getId() ); + #endif + } +} + +//----------------------------------------------------------------------------- + +void SFXSound::_stop() +{ + Parent::_stop(); + + if( mVoice ) + mVoice->stop(); +} + +//----------------------------------------------------------------------------- + +void SFXSound::_pause() +{ + Parent::_pause(); + + if( mVoice ) + mVoice->pause(); +} + +//----------------------------------------------------------------------------- + +void SFXSound::_updateStatus() +{ + // If we have a voice, use its status. + + if( mVoice ) + { + SFXStatus voiceStatus = mVoice->getStatus(); + + // Filter out SFXStatusBlocked. + + if( voiceStatus == SFXStatusBlocked ) + _setStatus( SFXStatusPlaying ); + else + _setStatus( voiceStatus ); + + return; + } + + // If we're not in a playing state or we're a looping + // sound then we don't need to calculate the status. + + if( isLooping() || mStatus != SFXStatusPlaying ) + return; + + // If we're playing and don't have a voice we + // need to decide if the sound is done playing + // to ensure proper virtualization of the sound. + + if( mPlayTimer.getPosition() > mDuration ) + { + _stop(); + _setStatus( SFXStatusStopped ); + } +} + +//----------------------------------------------------------------------------- + +void SFXSound::_updateVolume( const MatrixF& listener ) +{ + F32 oldPreAttenuatedVolume = mPreAttenuatedVolume; + Parent::_updateVolume( listener ); + + // If we have a voice and the pre-attenuated volume has + // changed, pass it on to the voice. Attenuation itself will + // happen on the device. + + if( mVoice != NULL && oldPreAttenuatedVolume != mPreAttenuatedVolume ) + mVoice->setVolume( mPreAttenuatedVolume ); +} + +//----------------------------------------------------------------------------- + +void SFXSound::_updatePitch() +{ + F32 oldEffectivePitch = mEffectivePitch; + Parent::_updatePitch(); + + if( mVoice != NULL && oldEffectivePitch != mEffectivePitch ) + mVoice->setPitch( mEffectivePitch ); +} + +//----------------------------------------------------------------------------- + +void SFXSound::_updatePriority() +{ + F32 oldEffectivePriority = mEffectivePriority; + Parent::_updatePriority(); + + if( mVoice != NULL && oldEffectivePriority != mEffectivePriority ) + mVoice->setPriority( mEffectivePriority ); +} + +//----------------------------------------------------------------------------- + +U32 SFXSound::getPosition() const +{ + if( mVoice ) + return mVoice->getFormat().getDuration( mVoice->getPosition() ); + else + return ( mPlayTimer.getPosition() % mDuration ); // Clamp for looped sounds. +} + +//----------------------------------------------------------------------------- + +void SFXSound::setPosition( U32 ms ) +{ + AssertFatal( ms < getDuration(), "SFXSound::setPosition() - position out of range" ); + if( mVoice ) + mVoice->setPosition( mVoice->getFormat().getSampleCount( ms ) ); + else + mPlayTimer.setPosition( ms ); +} + +//----------------------------------------------------------------------------- + +void SFXSound::setVelocity( const VectorF& velocity ) +{ + Parent::setVelocity( velocity ); + + if( mVoice && is3d() ) + mVoice->setVelocity( velocity ); +} + +//----------------------------------------------------------------------------- + +void SFXSound::setTransform( const MatrixF& transform ) +{ + Parent::setTransform( transform ); + + if( mVoice && is3d() ) + mVoice->setTransform( mTransform ); +} + +//----------------------------------------------------------------------------- + +void SFXSound::_setMinMaxDistance( F32 min, F32 max ) +{ + Parent::_setMinMaxDistance( min, max ); + + if( mVoice && is3d() ) + mVoice->setMinMaxDistance( mMinDistance, mMaxDistance ); +} + +//----------------------------------------------------------------------------- + +void SFXSound::_setCone( F32 innerAngle, + F32 outerAngle, + F32 outerVolume ) +{ + Parent::_setCone( innerAngle, outerAngle, outerVolume ); + + if( mVoice && is3d() ) + mVoice->setCone( mConeInsideAngle, + mConeOutsideAngle, + mConeOutsideVolume ); +} + +//----------------------------------------------------------------------------- + +bool SFXSound::isReady() const +{ + return ( mBuffer != NULL && mBuffer->isReady() ); +} + +//----------------------------------------------------------------------------- + +bool SFXSound::isVirtualized() const +{ + return ( ( mVoice == NULL && isPlaying() ) || + ( mVoice != NULL && mVoice->isVirtual() ) ); +} + +//----------------------------------------------------------------------------- + +SFXProfile* SFXSound::getProfile() const +{ + return dynamic_cast< SFXProfile* >( mTrack.getPointer() ); +} + +//----------------------------------------------------------------------------- + +F32 SFXSound::getElapsedPlayTimeCurrentCycle() const +{ + return F32( getPosition() ) / 1000.f; +} + +//----------------------------------------------------------------------------- + +F32 SFXSound::getTotalPlayTime() const +{ + return F32( mDuration ) / 1000.f; +} + +//----------------------------------------------------------------------------- + +// Let the user define a priority value for each channel +// in script. We assign it in the system init and use +// it when doleing out hardware handles. + +S32 QSORT_CALLBACK SFXSound::qsortCompare( const void* item1, const void* item2 ) +{ + const SFXSound* source1 = *( ( SFXSound** ) item1 ); + const SFXSound* source2 = *( ( SFXSound** ) item2 ); + + // Sounds that are playing are always sorted + // closer than non-playing sounds. + + const bool source1IsPlaying = source1->isPlaying(); + const bool source2IsPlaying = source2->isPlaying(); + + if( !source1IsPlaying && !source2IsPlaying ) + return 0; + else if( !source1IsPlaying && source1IsPlaying ) + return 1; + else if( source1IsPlaying && !source2IsPlaying ) + return -1; + + // Louder attenuated volumes take precedence but adjust them + // by priority so that less audible sounds with higher priority + // become more important. + + F32 volume1 = source1->getAttenuatedVolume(); + F32 volume2 = source2->getAttenuatedVolume(); + + volume1 += volume1 * source1->mEffectivePriority; + volume2 += volume2 * source2->mEffectivePriority; + + if( volume1 < volume2 ) + return 1; + if( volume1 > volume2 ) + return -1; + + // If we got this far then the source that was + // played last has the higher priority. + + if( source1->mPlayStartTick > source2->mPlayStartTick ) + return -1; + if( source1->mPlayStartTick < source2->mPlayStartTick ) + return 1; + + // These are sorted the same! + return 0; +} + +//============================================================================= +// Console Methods. +//============================================================================= +// MARK: ---- Console Methods ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXSound, isReady, bool, (),, + "Test whether the sound data associated with the sound has been fully loaded and is ready for playback.\n" + "For streamed sounds, this will be false during playback when the stream queue for the sound is starved and " + "waiting for data. For buffered sounds, only an initial loading phase will potentially cause isReady to " + "return false.\n\n" + "@return True if the sound is ready for playback." ) +{ + return object->isReady(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXSound, getPosition, F32, (),, + "Get the current playback position in seconds.\n" + "@return The current play cursor offset." ) +{ + return F32( object->getPosition() ) * 0.001f; +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXSound, setPosition, void, ( F32 position ),, + "Set the current playback position in seconds.\n" + "If the source is currently playing, playback will jump to the new position. If playback is stopped or paused, " + "playback will resume at the given position when play() is called.\n\n" + "@param position The new position of the play cursor (in seconds).\n" ) +{ + if( position >= 0 && position <= object->getDuration() ) + object->setPosition( position * 1000.0f ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXSound, getDuration, F32, (),, + "Get the total play time (in seconds) of the sound data attached to the sound.\n" + "@return \n\n" + "@note Be aware that for looped sounds, this will not return the total playback time of the sound.\n" ) +{ + return F32( object->getDuration() ) * 0.001f; +} diff --git a/Engine/source/sfx/sfxSound.h b/Engine/source/sfx/sfxSound.h new file mode 100644 index 000000000..16c8b0b28 --- /dev/null +++ b/Engine/source/sfx/sfxSound.h @@ -0,0 +1,166 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXSOUND_H_ +#define _SFXSOUND_H_ + +#ifndef _SFXSOURCE_H_ + #include "sfx/sfxSource.h" +#endif +#ifndef _SFXVOICE_H_ + #include "sfx/sfxVoice.h" +#endif +#ifndef _SIMBASE_H_ + #include "console/simBase.h" +#endif +#ifndef _MPOINT3_H_ + #include "math/mPoint3.h" +#endif +#ifndef _MMATRIX_H_ + #include "math/mMatrix.h" +#endif +#ifndef _TSTREAM_H_ + #include "core/stream/tStream.h" +#endif +#ifndef _SFXPROFILE_H_ + #include "sfx/sfxProfile.h" +#endif + + +class SFXBuffer; +class SFXDevice; + + + +/// A scriptable controller playing a specific single sound file. +class SFXSound : public SFXSource, + public IPositionable< U32 > +{ + friend class SFXSystem; + + typedef SFXSource Parent; + + protected: + + /// Used by SFXSystem to create sources. + static SFXSound* _create( SFXDevice* device, SFXProfile* profile ); + static SFXSound* _create( SFXDevice* device, const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ); + + /// Internal constructor used for sources. + SFXSound( SFXProfile* profile, SFXDescription* description ); + + /// The device specific voice which is used during + /// playback. By making it a SafePtr it will NULL + /// automatically when the device is deleted. + StrongWeakRefPtr< SFXVoice > mVoice; + + /// The reference counted device specific buffer used by + /// the voice for playback. + StrongWeakRefPtr< SFXBuffer > mBuffer; + + /// The duration of the sound cached from the buffer in + /// _initBuffer() used for managing virtual sources. + U32 mDuration; + + /// Create a new voice for this source. + bool _allocVoice( SFXDevice* device ); + + /// Release the voice if the source has one. + bool _releaseVoice(); + + /// + void _setBuffer( SFXBuffer* buffer ); + + /// Reload the sound buffer. Temporarily goes to virtualized playback when necessary. + void _reloadBuffer(); + + /// + void _onProfileChanged( SFXProfile* profile ) + { + if( profile == mTrack ) + _reloadBuffer(); + } + + // SFXSource. + virtual void _play(); + virtual void _pause(); + virtual void _stop(); + virtual void _updateStatus(); + virtual void _onParameterEvent( SFXParameter* parameter, SFXParameterEvent event ); + virtual void _updateVolume( const MatrixF& listener ); + virtual void _updatePitch(); + virtual void _updatePriority(); + virtual void _setMinMaxDistance( F32 min, F32 max ); + virtual void _setCone( F32 innerAngle, F32 outerAngle, F32 outerVolume ); + + public: + + DECLARE_CONOBJECT( SFXSound ); + + /// The default constructor is *only* here to satisfy the + /// construction needs of IMPLEMENT_CONOBJECT. It does not + /// create a valid source! + explicit SFXSound(); + + /// This is normally called from the system to + /// detect if this source has been assigned a + /// voice for playback. + bool hasVoice() const { return mVoice != NULL; } + + /// Return the current playback position in milliseconds. + /// @note For looping sources, this returns the position in the current cycle. + U32 getPosition() const; + + /// Set the current playback position in milliseconds. + void setPosition( U32 ms ); + + /// Returns the source's total playback time in milliseconds. + U32 getDuration() const { return mDuration; } + + /// Return true if the sound stream tied to the source is currently in a buffer underrun situation. + bool isBlocked() const { return ( mVoice && mVoice->getStatus() == SFXStatusBlocked ); } + + /// Returns true if this is a continuously streaming source. + bool isStreaming() const { return mDescription->mIsStreaming; } + + /// Returns true if the source's associated data is ready for playback. + bool isReady() const; + + /// Return the SFXProfile datablock attached to this sound. + SFXProfile* getProfile() const; + + /// Used to sort sources by attenuated volume and channel priority. + static S32 QSORT_CALLBACK qsortCompare( const void* item1, const void* item2 ); + + // SFXSource. + virtual void setTransform( const MatrixF& transform ); + virtual void setVelocity( const VectorF& velocity ); + virtual bool isVirtualized() const; + virtual F32 getElapsedPlayTimeCurrentCycle() const; + virtual F32 getTotalPlayTime() const; + + // SimObject. + virtual void onRemove(); + virtual void onDeleteNotify( SimObject* object ); +}; + +#endif // !_SFXSOUND_H_ diff --git a/Engine/source/sfx/sfxSoundscape.cpp b/Engine/source/sfx/sfxSoundscape.cpp new file mode 100644 index 000000000..afd41f842 --- /dev/null +++ b/Engine/source/sfx/sfxSoundscape.cpp @@ -0,0 +1,450 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/sfxSoundscape.h" +#include "sfx/sfxAmbience.h" +#include "sfx/sfxEnvironment.h" +#include "sfx/sfxState.h" +#include "sfx/sfxSource.h" +#include "sfx/sfxSystem.h" + + +//#define DEBUG_SPEW + +//FIXME: fix reverb resetting + +//============================================================================= +// SFXSoundscape. +//============================================================================= +// MARK: ---- SFXSoundscape ---- + +//----------------------------------------------------------------------------- + +SFXSoundscape::SFXSoundscape( SFXAmbience* ambience ) + : mAmbience( ambience ) +{ + mDirtyBits.set( AmbienceDirty ); + mFlags.set( FlagUnique ); + + dMemset( mStates, 0, sizeof( mStates ) ); +} + +//----------------------------------------------------------------------------- + +void SFXSoundscape::setAmbience( SFXAmbience* ambience ) +{ + AssertFatal( ambience != NULL, "SFXSoundscape::setAmbience - ambience cannot be NULL!" ); + mDirtyBits.set( AmbienceDirty ); + mAmbience = ambience; +} + +//============================================================================= +// SFXSoundscapeManager. +//============================================================================= +// MARK: ---- SFXSoundscapeManager ---- + +//----------------------------------------------------------------------------- + +SFXSoundscapeManager::SFXSoundscapeManager() + : mCurrentReverbIndex( -1 ) +{ + VECTOR_SET_ASSOCIATION( mStack ); + VECTOR_SET_ASSOCIATION( mFadeStack ); + +#ifndef TORQUE_SHIPPING + + // Hook on to the ambience change signal (used + // to respond to editing of ambiences). + + SFXAmbience::getChangeSignal().notify + ( this, &SFXSoundscapeManager::_notifyAmbienceChanged ); + +#endif + + // Push the global ambience. + + mDefaultGlobalAmbience = new SFXAmbience; + SFXSoundscape* global = mChunker.alloc(); + constructInPlace( global, mDefaultGlobalAmbience ); + mStack.push_back( global ); +} + +//----------------------------------------------------------------------------- + +SFXSoundscapeManager::~SFXSoundscapeManager() +{ +#ifndef TORQUE_SHIPPING + + // Remove the hook on the ambience change signal. + + SFXAmbience::getChangeSignal().remove + ( this, &SFXSoundscapeManager::_notifyAmbienceChanged ); + +#endif + + mDefaultGlobalAmbience->deleteObject(); +} + +//----------------------------------------------------------------------------- + +void SFXSoundscapeManager::update() +{ + // Make sure the topmost reverb on the stack is active. + + S32 reverbIndex = _findTopmostReverbOnStack( mStack ); + if( mCurrentReverbIndex != reverbIndex ) + { + if( reverbIndex == -1 ) + { + // No ambience on the stack has reverb settings so reset + // to default. + SFX->setReverb( SFXReverbProperties() ); + } + else + { + SFXAmbience* ambience = mStack[ reverbIndex ]->getAmbience(); + AssertFatal( ambience->getEnvironment(), "SFXSoundscapeManager::update - Reverb lookup return ambience without reverb!" ); + + SFX->setRolloffFactor( ambience->getRolloffFactor() ); + SFX->setDopplerFactor( ambience->getDopplerFactor() ); + SFX->setReverb( ambience->getEnvironment()->getReverb() ); + } + + mCurrentReverbIndex = reverbIndex; + } + + // Update the active soundscapes. + + for( U32 i = 0; i < mStack.size(); ++ i ) + { + SFXSoundscape* soundscape = mStack[ i ]; + + // If the soundscape's associated ambience has changed + + if( soundscape->mDirtyBits.test( SFXSoundscape::AmbienceDirty ) ) + { + SFXAmbience* ambience = soundscape->getAmbience(); + + // Start playing the ambient audio track if it isn't + // already playing and if the soundscape isn't overridden + // by an instance lower down the stack. + + if( !soundscape->_isOverridden() ) + { + SFXTrack* track = ambience->getSoundTrack(); + if( !soundscape->mSource || soundscape->mSource->getTrack() != track ) + { + if( soundscape->mSource != NULL ) + { + soundscape->mSource->stop(); + soundscape->mSource = NULL; + } + + if( track ) + soundscape->mSource = SFX->playOnce( track ); + } + else if( soundscape->mSource != NULL ) + { + // Make sure to revert a fade-out running on the source + // if it has been taken from the fade stack. + + soundscape->mSource->play(); + } + } + + // Activate SFXStates on the ambience. For state slots that + // have changed, deactivate states that we have already activated. + + for( U32 i = 0; i < SFXAmbience::MaxStates; ++ i ) + { + SFXState* state = ambience->getState( i ); + if( soundscape->mStates[ i ] != state ) + { + if( soundscape->mStates[ i ] ) + soundscape->mStates[ i ]->deactivate(); + + if( state ) + state->activate(); + + soundscape->mStates[ i ] = state; + } + } + + soundscape->mDirtyBits.clear( SFXSoundscape::AmbienceDirty ); + } + } + + // Clean out the fade stack. + + for( U32 i = 0; i < mFadeStack.size(); ) + if( mFadeStack[ i ]->mSource == NULL ) + { + SFXSoundscape* soundscape = mFadeStack[ i ]; + mFadeStack.erase_fast( i ); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXSoundscapeManager] Deleting faded instance of '%s'", + soundscape->getAmbience()->getName() ); + #endif + + // Free the soundscape. + + destructInPlace( soundscape ); + mChunker.free( soundscape ); + } + else + ++ i; +} + +//----------------------------------------------------------------------------- + +SFXSoundscape* SFXSoundscapeManager::insertSoundscape( U32 index, SFXAmbience* ambience ) +{ + AssertFatal( index <= mStack.size(), "SFXSoundscapeManager::insertSoundscape - index out of range" ); + AssertFatal( index != 0, "SFXSoundscapeManager::insertSoundscape - cannot insert before global soundscape" ); + AssertFatal( ambience != NULL, "SFXSoundscapeManager::insertSoundscape - got a NULL ambience" ); + + // Look for an existing soundscape that is assigned the + // same ambience. + + S32 ambientInstanceIndex = _findOnStack( ambience, mStack ); + + // Push a soundscape unto the stack. If there is an instance + // on the fade stack that is tied to the given ambience, bring that + // soundscape over to the active stack. Otherwise create a new + // soundscape instance. + + SFXSoundscape* soundscape = NULL; + if( ambientInstanceIndex == -1 ) + { + S32 fadeIndex = _findOnStack( ambience, mFadeStack ); + if( fadeIndex != -1 ) + { + soundscape = mFadeStack[ fadeIndex ]; + mFadeStack.erase_fast( fadeIndex ); + + // Make sure the soundscape will get re-evaluated + // on the next update. + + soundscape->mDirtyBits.set( SFXSoundscape::AmbienceDirty ); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXSoundscapeManager] Moving ambience '%s' from fade stack to #%i (total: %i)", + ambience->getName(), index, mStack.size() + 1 ); + #endif + } + } + + if( !soundscape ) + { + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXSoundscapeManager] Adding new instance for '%s' at #%i (total: %i)", + ambience->getName(), index, mStack.size() + 1 ); + #endif + + soundscape = mChunker.alloc(); + constructInPlace( soundscape, ambience ); + } + + mStack.insert( index, soundscape ); + + // If there is an existing soundscape that is assigned the + // same ambience and it is lower on the stack, steal its sound + // source if it has one. If it is higher up the stack, simply + // mark this soundscape as being overridden. + + if( ambientInstanceIndex != -1 ) + { + SFXSoundscape* existingSoundscape = mStack[ ambientInstanceIndex ]; + + existingSoundscape->mFlags.clear( SFXSoundscape::FlagUnique ); + soundscape->mFlags.clear( SFXSoundscape::FlagUnique ); + + if( ambientInstanceIndex < index ) + { + existingSoundscape->mFlags.set( SFXSoundscape::FlagOverridden ); + + SFXSource* source = existingSoundscape->mSource; + existingSoundscape->mSource = NULL; + + if( source && source->isPlaying() ) + soundscape->mSource = source; + } + else + { + soundscape->mFlags.set( SFXSoundscape::FlagOverridden ); + } + } + + return soundscape; +} + +//----------------------------------------------------------------------------- + +void SFXSoundscapeManager::removeSoundscape( SFXSoundscape* soundscape ) +{ + AssertFatal( soundscape != getGlobalSoundscape(), + "SFXSoundscapeManager::removeSoundscape() - trying to remove the global soundscape" ); + + // Find the soundscape on the stack. + + U32 index = 1; + for( ; index < mStack.size(); ++ index ) + if( mStack[ index ] == soundscape ) + break; + + AssertFatal( index < mStack.size(), + "SFXSoundscapeManager::removeSoundscape() - soundscape not on stack" ); + + // Find out if the soundscape has the current reverb + // environment. If so, we need to change the reverb to + // the next one higher up the stack. + + const bool isCurrentReverb = ( _findTopmostReverbOnStack( mStack ) == index ); + + // Remove the soundscape from the stack. + + mStack.erase( index ); + + // Update reverb, if necessary. + + if( isCurrentReverb ) + { + S32 reverbIndex = _findTopmostReverbOnStack( mStack ); + if( reverbIndex != -1 ) + SFX->setReverb( mStack[ reverbIndex ]->getAmbience()->getEnvironment()->getReverb() ); + } + + // Deactivate states. + + for( U32 i = 0; i < SFXAmbience::MaxStates; ++ i ) + if( soundscape->mStates[ i ] ) + { + soundscape->mStates[ i ]->deactivate(); + soundscape->mStates[ i ] = NULL; + } + + // If the soundscape is the only instance of its ambience, move + // it to the fade stack. Otherwise delete the soundscape. + + if( soundscape->_isUnique() && soundscape->mSource != NULL ) + { + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXSoundscapeManager] Moving ambience '%s' at index #%i to fade stack", + soundscape->getAmbience()->getName(), index ); + #endif + + soundscape->mSource->stop(); + mFadeStack.push_back( soundscape ); + } + else + { + if( !soundscape->_isUnique() ) + { + // If this is the overriding soundscape, transfer its state + // to the ambient instance lower down the stack. + + if( !soundscape->_isOverridden() ) + { + S32 overrideeIndex = index - 1; + for( ; overrideeIndex >= 0; -- overrideeIndex ) + if( soundscape->getAmbience() == mStack[ overrideeIndex ]->getAmbience() ) + break; + + AssertFatal( overrideeIndex >= 0, + "SFXSoundscapeManager::removeSoundscape() - could not find ambience being overridden on stack" ); + + // Pass the source on to the previous soundscape. + + mStack[ overrideeIndex ]->mSource = soundscape->mSource; + soundscape->mSource = NULL; + } + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXSoundscapeManager] Removing instance of '%s' at index #%i", + soundscape->getAmbience()->getName(), index ); + #endif + + // If there's only one instance of this ambience is + // left, mark it as being unique now. + + U32 numInstances = 0; + for( U32 i = 0; i < mStack.size(); ++ i ) + if( mStack[ i ]->getAmbience() == soundscape->getAmbience() ) + ++ numInstances; + + if( numInstances == 1 ) + mStack[ _findOnStack( soundscape->getAmbience(), mStack ) ]->mFlags.set( SFXSoundscape::FlagUnique ); + } + + // Free the soundscape. + + destructInPlace( soundscape ); + mChunker.free( soundscape ); + } +} + +//----------------------------------------------------------------------------- + +S32 SFXSoundscapeManager::_findOnStack( SFXAmbience* ambience, const Vector< SFXSoundscape* >& stack ) +{ + // Search the stack top to bottom so we always find + // the uppermost instance of the ambience on the stack. + + for( S32 i = stack.size() - 1; i >= 0; -- i ) + if( stack[ i ]->getAmbience() == ambience ) + return i; + + return -1; +} + +//----------------------------------------------------------------------------- + +S32 SFXSoundscapeManager::_findTopmostReverbOnStack( const Vector< SFXSoundscape* >& stack ) +{ + for( S32 i = stack.size() - 1; i >= 0; -- i ) + if( stack[ i ]->getAmbience()->getEnvironment() ) + return i; + + return -1; +} + +//----------------------------------------------------------------------------- + +void SFXSoundscapeManager::_notifyAmbienceChanged( SFXAmbience* ambience ) +{ + //RDTODO: fade stack? + + // Set the ambience dirty bit on all soundscapes + // tied to the given ambience. + + for( U32 i = 0; i < mStack.size(); ++ i ) + if( mStack[ i ]->getAmbience() == ambience ) + { + mStack[ i ]->mDirtyBits.set( SFXSoundscape::AmbienceDirty ); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXSoundscapeManager] Ambience '%s' at #%i changed", + ambience->getName(), i ); + #endif + } +} diff --git a/Engine/source/sfx/sfxSoundscape.h b/Engine/source/sfx/sfxSoundscape.h new file mode 100644 index 000000000..274854777 --- /dev/null +++ b/Engine/source/sfx/sfxSoundscape.h @@ -0,0 +1,195 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXSOUNDSCAPE_H_ +#define _SFXSOUNDSCAPE_H_ + +#ifndef _SFXCOMMON_H_ +#include "sfx/sfxCommon.h" +#endif + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +#ifndef _DATACHUNKER_H_ +#include "core/dataChunker.h" +#endif + +#ifndef _BITSET_H_ +#include "core/bitSet.h" +#endif + +#ifndef _TRESPONSECURVE_H_ +#include "math/util/tResponseCurve.h" +#endif + +#ifndef _SFXAMBIENCE_H_ +#include "sfx/sfxAmbience.h" +#endif + + +/// @file +/// The soundscape system is responsible for controlling ambient audio. +/// It is largely driven by SFXWorld's listener tracking. + + +class SFXSource; + + + + +/// An instance of an SFXAmbience on the soundscape mixer stack. +/// +class SFXSoundscape +{ + public: + + typedef void Parent; + friend class SFXSoundscapeManager; + + enum Flags + { + FlagOverridden = BIT( 0 ), ///< Same ambience is pushed lower down onto the stack. + FlagUnique = BIT( 1 ), ///< No other instance of this ambience on stack. + }; + + enum DirtyBits + { + AmbienceDirty = BIT( 0 ), ///< Associated ambience has changed. + AllDirty = 0xFFFFFFFF + }; + + protected: + + /// + BitSet32 mFlags; + + /// + BitSet32 mDirtyBits; + + /// The current soundtrack playing in this soundscape. This is either the + /// ambient track or the surround sound track depending on whether + SimObjectPtr< SFXSource > mSource; + + /// The ambient space assigned to this soundscape. + SFXAmbience* mAmbience; + + /// States activated by this soundscape. + SFXState* mStates[ SFXAmbience::MaxStates ]; + + /// Return true if another soundscape lower down the stack is using the same + /// ambient space as this soundscape. + bool _isOverridden() const { return mFlags.test( FlagOverridden ); } + + /// Return true if this soundscape is the only soundscape using the assigned + /// ambient space. + bool _isUnique() const { return mFlags.test( FlagUnique ); } + + public: + + /// Create a soundscape associated with the given ambient space. + SFXSoundscape( SFXAmbience* ambience ); + + /// Return the ambient space associated with this soundscape. + SFXAmbience* getAmbience() const { return mAmbience; } + + /// Set the ambient space associated with this soundscape. Triggers corresponding + /// recomputations on the next soundscape manager update. + void setAmbience( SFXAmbience* ambience ); +}; + + +/// The soundscape manager produces a dynamic mix between multiple active soundscapes. +/// +/// @note The more layered soundscapes there are, the costlier ambient sound updates will get. +/// +class SFXSoundscapeManager +{ + public: + + typedef void Parent; + + protected: + + /// Linear stack of soundscapes. Evaluated bottom to top. Scapes + /// being faded out are always last on the stack. + Vector< SFXSoundscape* > mStack; + + /// Stack of soundscape that are currently being faded out. These are + /// kept around so that when their ambient spaces are activated again, + /// they can be brought back with their fades simply being reversed. + /// + /// All soundscapes on this stack are unique. + Vector< SFXSoundscape* > mFadeStack; + + /// Index into #mStack for the soundscape that defines the current + /// reverb settings. -1 if no current ambience has reverb settings. + S32 mCurrentReverbIndex; + + /// Memory manager of soundscape instances. + FreeListChunker< SFXSoundscape > mChunker; + + /// Default global SFXAmbience. Not a registered object. + SFXAmbience* mDefaultGlobalAmbience; + + /// Found the topmost instance on the given stack associated with the given + /// ambient space. + S32 _findOnStack( SFXAmbience* ambience, const Vector< SFXSoundscape* >& stack ); + + /// Find the topmost soundscape on the given stack that has a reverb environment + /// defined on its ambient space. + S32 _findTopmostReverbOnStack( const Vector< SFXSoundscape* >& stack ); + + /// Method hooked up to the SFXAmbience change signal to automatically + /// make soundscapes using the given ambience as dirty and trigger + /// a recomputation of their properties. + void _notifyAmbienceChanged( SFXAmbience* ambience ); + + public: + + /// + SFXSoundscapeManager(); + + /// + ~SFXSoundscapeManager(); + + /// Update the current soundscape mix. + void update(); + + /// Return the total number of soundscape instances currently on the stack. + /// Always >=1. + U32 getNumTotalSoundscapes() const { return mStack.size(); } + + /// Insert a new soundscape instance associated with the given ambient space + /// at the given stack index. + SFXSoundscape* insertSoundscape( U32 index, SFXAmbience* ambience ); + + /// Remove the given soundscape from the stack. + void removeSoundscape( SFXSoundscape* soundscape ); + + /// Return the topmost soundscape. This soundscape is always defined and cannot + /// be removed. + SFXSoundscape* getGlobalSoundscape() const { return mStack[ 0 ]; } +}; + +#endif // !_SFXSOUNDSCAPE_H_ diff --git a/Engine/source/sfx/sfxSource.cpp b/Engine/source/sfx/sfxSource.cpp new file mode 100644 index 000000000..f6f5bf600 --- /dev/null +++ b/Engine/source/sfx/sfxSource.cpp @@ -0,0 +1,1720 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/sfxSource.h" +#include "sfx/sfxSystem.h" +#include "sfx/sfxTrack.h" +#include "sfx/sfxTypes.h" +#include "sfx/sfxDescription.h" +#include "sfx/sfxModifier.h" +#include "console/engineAPI.h" +#include "math/mRandom.h" +#include "math/mEase.h" + + + +//#define DEBUG_SPEW + + +IMPLEMENT_CONOBJECT( SFXSource ); + +ConsoleDocClass( SFXSource, + "@brief Playback controller for a sound source.\n\n" + + "All sound playback is driven by SFXSources. Each such source represents an independent playback controller " + "that directly or indirectly affects sound output.\n\n" + + "While this class itself is instantiable, such an instance will not by itself emit any sound. This is the " + "responsibility of its subclasses. Note, however, that none of these subclasses must be instantiated directly " + "but must instead be instantiated indirectly through the SFX interface.\n\n" + + "@section SFXSource_playonce Play-Once Sources\n\n" + + "Often, a sound source need only exist for the duration of the sound it is playing. In this case " + "so-called \"play-once\" sources simplify the bookkeeping involved by leaving the deletion of " + "sources that have expired their playtime to the sound system.\n\n" + + "Play-once sources can be created in either of two ways:\n" + + "- sfxPlayOnce(): Directly create a play-once source from a SFXTrack or file.\n" + "- sfxDeleteWhenStopped(): Retroactively turn any source into a play-once source that will automatically " + "be deleted when moving into stopped state.\n\n" + + "@see sfxPlayOnce\n" + "@see sfxDeleteWhenStopped\n\n" + + "@section SFXSource_hierarchies Source Hierarchies\n\n" + + "Source are arranged into playback hierarchies where a parent source will scale some of the properties of its " + "children and also hand on any play(), pause(), and stop() commands to them. This allows to easily group sounds " + "into logical units that can then be operated on as a whole.\n\n" + + "An example of this is the segregation of sounds according to their use in the game. Volume levels of background " + "music, in-game sound effects, and character voices will usually be controlled independently and putting their sounds " + "into different hierarchies allows to achieve that easily.\n\n" + + "The source properties that are scaled by parent values are:\n" + + "- volume,\n" + "- pitch, and\n" + "- priority\n\n" + + "This means that if a parent has a volume of 0.5, the child will play at half the effective volume it would otherwise " + "have.\n\n" + + "Additionally, parents affect the playback state of their children:\n\n" + + "- A parent that is in stopped state will force all its direct and indirect children into stopped state.\n" + "- A parent that is in paused state will force all its direct and indirect children that are playing into paused state. However, " + "children that are in stopped state will not be affected.\n" + "- A parent that is in playing state will not affect the playback state of its children.\n\n" + + "Each source maintains a state that is wants to be in which may differ from the state that is enforced on it by its " + "parent. If a parent changes its states in a way that allows a child to move into its desired state, the child will do " + "so.\n\n" + + "For logically grouping sources, instantiate the SFXSource class directly and make other sources children to it. A " + "source thus instantiated will not effect any real sound output on its own but will influence the sound output of its " + "direct and indirect children.\n\n" + + "@note Be aware that the property values used to scale child property values are the @b effective values. For example, " + "the value used to scale the volume of a child is the @b effective volume of the parent, i.e. the volume after fades, " + "distance attenuation, etc. has been applied.\n\n" + + "@see SFXDescription::sourceGroup\n" + + "@section SFXSource_volume Volume Attenuation\n\n" + + "During its lifetime, the volume of a source will be continually updated. This update process always progresses " + "in a fixed set of steps to compute the final effective volume of the source based on the base volume level " + "that was either assigned from the SFXDescription associated with the source (SFXDescription::volume) or manually " + "set by the user. The process of finding a source's final effective volume is called \"volume attenuation\". The " + "steps involved in attenuating a source's volume are (in order):\n" + + "
      \n" + "
      Fading
      \n" + "
      If the source currently has a fade-effect applied, the volume is interpolated along the currently active fade curve.
      \n" + "
      Modulation
      \n" + "
      If the source is part of a hierarchy, it's volume is scaled according to the effective volume of its parent.
      \n" + "
      Distance Attenuation
      \n" + "
      If the source is a 3D sound source, then the volume is interpolated according to the distance model in effect and " + "current listener position and orientation (see @ref SFX_3d).
      \n" + "
      \n\n" + + "@see SFXDescription::volume\n" + "@see SFXDescription::is3d\n" + + "@section SFXSource_fades Volume Fades\n\n" + + "To ease-in and ease-out playback of a sound, fade effects may be applied to sources. A fade will either go from " + "zero volume to full effective volume (fade-in) or from full effective volume to zero volume (fade-out).\n\n" + + "Fading is coupled to the play(), pause(), and stop() methods as well as to loop iterations when SFXDescription::fadeLoops " + "is true for the source. play() and the start of a loop iteration will trigger a fade-in whereas pause(), stop() and the " + "end of loop iterations will trigger fade-outs.\n\n" + + "For looping sources, if SFXDescription::fadeLoops is false, only the initial play() will trigger a fade-in and no further " + "fading will be applied to loop iterations.\n\n" + + "By default, the fade durations will be governed by the SFXDescription::fadeInTime and SFXDescription::fadeOutTime properties " + "of the SFXDescription attached to the source. However, these may be overridden on a per-source basis by setting fade times " + "explicitly with setFadeTimes(). Additionally, the set values may be overridden for individual play(), pause(), and stop() " + "calls by supplying appropriate fadeInTime/fadeOutTime parameters.\n\n" + + "By default, volume will interpolate linearly during fades. However, custom interpolation curves can be assigned through the " + "SFXDescription::fadeInEase and SFXDescription::fadeOutTime properties.\n\n" + + "@see SFXDescription::fadeInTime\n" + "@see SFXDescription::fadeOutTime\n" + "@see SFXDescription::fadeInEase\n" + "@see SFXDescription::fadeOutEase\n" + "@see SFXDescription::fadeLoops\n" + + "@section SFXSource_cones Sound Cones\n\n" + + "@see SFXDescription::coneInsideAngle\n" + "@see SFXDescription::coneOutsideAngle\n" + "@see SFXDescription::coneOutsideVolume\n" + + "@section SFXSource_doppler Doppler Effect\n\n" + + "@see sfxGetDopplerFactor\n" + "@see sfxSetDopplerFactor\n" + "@see SFXAmbience::dopplerFactor\n" + + "@section SFXSource_markers Playback Markers\n\n" + + "Playback markers allow to attach notification triggers to specific playback positions. Once the " + "play cursor crosses a position for which a marker is defined, the #onMarkerPassed callback will " + "be triggered on the SFXSource thus allowing to couple script logic to .\n\n" + + "Be aware that the precision with which marker callbacks are triggered are bound by global source " + "update frequency. Thus there may be a delay between the play cursor actually passing a marker position " + "and the callback being triggered.\n\n" + + "@ingroup SFX\n" +); + + +IMPLEMENT_CALLBACK( SFXSource, onStatusChange, void, ( SFXStatus newStatus ), ( newStatus ), + "Called when the playback status of the source changes.\n" + "@param newStatus The new playback status." ); +IMPLEMENT_CALLBACK( SFXSource, onParameterValueChange, void, ( SFXParameter* parameter ), ( parameter ), + "Called when a parameter attached to the source changes value.\n" + "This callback will be triggered before the value change has actually been applied to the source.\n" + "@param parameter The parameter that has changed value.\n" + "@note This is also triggered when the parameter is first attached to the source." ); + + +//----------------------------------------------------------------------------- + +SFXSource::SFXSource() + : mStatus( SFXStatusStopped ), + mSavedStatus( SFXStatusNull ), + mStatusCallback( NULL ), + mPitch( 1.f ), + mModulativePitch( 1.f ), + mEffectivePitch( 1.f ), + mVolume( 1.f ), + mPreFadeVolume( 1.f ), + mFadedVolume( 1.f ), + mModulativeVolume( 1.f ), + mPreAttenuatedVolume( 1.f ), + mAttenuatedVolume( 1.f ), + mPriority( 0 ), + mModulativePriority( 1.f ), + mEffectivePriority( 0 ), + mVelocity( 0, 0, 0 ), + mTransform( true ), + mMinDistance( 1 ), + mMaxDistance( 100 ), + mConeInsideAngle( 360 ), + mConeOutsideAngle( 360 ), + mConeOutsideVolume( 1 ), + mDescription( NULL ), + mTransformScattered( false ), + mPlayStartTick( 0 ), + mFadeSegmentEase( NULL ), + mFadeInTime( 0.f ), + mFadeOutTime( 0.f ), + mFadeInPoint( -1.f ), + mFadeOutPoint( -1.f ), + mFadeSegmentType( FadeSegmentNone ), + mFadeSegmentStartPoint( 0.f ), + mFadeSegmentEndPoint( 0.f ), + mSavedFadeTime( -1.f ), + mDistToListener( 0.f ) +{ + VECTOR_SET_ASSOCIATION( mParameters ); +} + +//----------------------------------------------------------------------------- + +SFXSource::SFXSource( SFXTrack* track, SFXDescription* description ) + : mStatus( SFXStatusStopped ), + mSavedStatus( SFXStatusNull ), + mTrack( track ), + mDescription( description ), + mStatusCallback( NULL ), + mPitch( 1.f ), + mModulativePitch( 1.f ), + mEffectivePitch( 1.f ), + mVolume( 1.f ), + mPreFadeVolume( 1.f ), + mFadedVolume( 1.f ), + mModulativeVolume( 1.f ), + mPreAttenuatedVolume( 1.f ), + mAttenuatedVolume( 1.f ), + mPriority( 0 ), + mModulativePriority( 1.f ), + mEffectivePriority( 0 ), + mVelocity( 0, 0, 0 ), + mTransform( true ), + mMinDistance( 1 ), + mMaxDistance( 100 ), + mConeInsideAngle( 360 ), + mConeOutsideAngle( 360 ), + mConeOutsideVolume( 1 ), + mTransformScattered( false ), + mPlayStartTick( 0 ), + mFadeInTime( 0.f ), + mFadeOutTime( 0.f ), + mFadeSegmentEase( NULL ), + mFadeInPoint( -1.f ), + mFadeOutPoint( -1.f ), + mFadeSegmentType( FadeSegmentNone ), + mFadeSegmentStartPoint( 0.f ), + mFadeSegmentEndPoint( 0.f ), + mSavedFadeTime( -1.f ), + mDistToListener( 0.f ) +{ + VECTOR_SET_ASSOCIATION( mParameters ); + + if( !description && track ) + mDescription = track->getDescription(); + + // Make sure we get notified when our datablocks go away + // so we can kill this source. + + if( mTrack != NULL ) + deleteNotify( mTrack ); + deleteNotify( mDescription ); +} + +//----------------------------------------------------------------------------- + +SFXSource::~SFXSource() +{ + // Disconnect from any remaining parameters. + + while( !mParameters.empty() ) + { + mParameters.last()->getEventSignal().remove( this, &SFXSource::_onParameterEvent ); + mParameters.decrement(); + } +} + +//----------------------------------------------------------------------------- + +void SFXSource::initPersistFields() +{ + addGroup( "Sound" ); + + addProtectedField( "description", TypeSFXDescriptionName, Offset( mDescription, SFXSource ), + &_setDescription, &_getDescription, + "The playback configuration that determines the initial sound properties and setup.\n" + "Any SFXSource must have an associated SFXDescription." ); + + addField( "statusCallback", TypeString, Offset( mStatusCallback, SFXSource ), + "Name of function to call when the status of the source changes.\n\n" + "The source that had its status changed is passed as the first argument to the function and the " + "new status of the source is passed as the second argument." ); + + endGroup( "Sound" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +bool SFXSource::processArguments( S32 argc, const char **argv ) +{ + // Don't allow subclasses of this to be created via script. Force + // usage of the SFXSystem functions. + + if( typeid( *this ) != typeid( SFXSource ) ) + { + Con::errorf( ConsoleLogEntry::Script, "Use sfxCreateSource, sfxPlay, or sfxPlayOnce!" ); + return false; + } + else + return Parent::processArguments( argc, argv ); +} + +//----------------------------------------------------------------------------- + +bool SFXSource::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + // Make sure we have a description. + + if( !mDescription ) + { + Con::errorf( "SFXSource::onAdd() - no description set on source %i (%s)", getId(), getName() ); + return false; + } + + // Set our initial properties. + + _setVolume( mDescription->mVolume ); + _setPitch( mDescription->mPitch ); + _setPriority( mDescription->mPriority ); + _setFadeTimes( mDescription->mFadeInTime, mDescription->mFadeOutTime ); + + _setMinMaxDistance( mDescription->mMinDistance, mDescription->mMaxDistance ); + _setCone( F32( mDescription->mConeInsideAngle ), + F32( mDescription->mConeOutsideAngle ), + mDescription->mConeOutsideVolume ); + + // Add the parameters from the description. + + for( U32 i = 0; i < SFXDescription::MaxNumParameters; ++ i ) + { + StringTableEntry name = mDescription->mParameters[ i ]; + if( name && name[ 0 ] ) + _addParameter( name ); + } + + // Add the parameters from the track. + + if( mTrack != NULL ) + for( U32 i = 0; i < SFXTrack::MaxNumParameters; ++ i ) + { + StringTableEntry name = mTrack->getParameter( i ); + if( name && name[ 0 ] ) + _addParameter( name ); + } + + // Add us to the description's source group. + + if( mDescription->mSourceGroup ) + mDescription->mSourceGroup->addObject( this ); + + // Add to source set. + + if( Sim::getSFXSourceSet() ) + Sim::getSFXSourceSet()->addObject( this ); + + // Register with SFX system. + + SFX->_onAddSource( this ); + + return true; +} + +//----------------------------------------------------------------------------- + +void SFXSource::onRemove() +{ + stop(); + + // Let the system know. + SFX->_onRemoveSource( this ); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXSource] removed source '%i'", getId() ); + #endif + + Parent::onRemove(); +} + +//----------------------------------------------------------------------------- + +void SFXSource::onDeleteNotify( SimObject* object ) +{ + if( object == mTrack ) + { + deleteObject(); + return; + } + + Parent::onDeleteNotify( object ); +} + +//----------------------------------------------------------------------------- + +bool SFXSource::acceptsAsChild( SimObject* object ) +{ + return ( dynamic_cast< SFXSource* >( object ) != NULL ); +} + +//----------------------------------------------------------------------------- + +void SFXSource::onGroupAdd() +{ + Parent::onGroupAdd(); + + SFXSource* source = dynamic_cast< SFXSource* >( getGroup() ); + if( source ) + { + if( source != mDescription->mSourceGroup ) + mFlags.set( CustomGroupFlag ); + else + mFlags.clear( CustomGroupFlag ); + } + + //DRTODO: sync up playback state +} + +//----------------------------------------------------------------------------- + +SFXSource* SFXSource::getSourceGroup() const +{ + return dynamic_cast< SFXSource* >( getGroup() ); +} + +//----------------------------------------------------------------------------- + +F32 SFXSource::getElapsedPlayTime() const +{ + return F32( mPlayTimer.getPosition() ) / 1000.f; +} + +//----------------------------------------------------------------------------- + +F32 SFXSource::getElapsedPlayTimeCurrentCycle() const +{ + // In this base class, we can't make assumptions about total playtimes + // and thus cannot clamp the playtimer into range for the current cycle. + // This needs to be done by subclasses. + + return F32( mPlayTimer.getPosition() ) / 1000.f; +} + +//----------------------------------------------------------------------------- + +F32 SFXSource::getTotalPlayTime() const +{ + return Float_Inf; +} + +//----------------------------------------------------------------------------- + +void SFXSource::play( F32 fadeInTime ) +{ + SFXStatus status = getStatus(); + + // Return if the source is already playing. + + if( status == SFXStatusPlaying ) + { + // Revert fade-out if there is one. + + if( mFadeSegmentType != FadeSegmentNone && mFadeSegmentType != FadeSegmentPlay ) + { + // Let easing curve remain in place. Otherwise we would have to + // search the fade-in's easing curve for the point matching the + // current fade volume in order to prevent volume pops. + + mFadeSegmentType = FadeSegmentPlay; + } + + return; + } + + // First check the parent source. If it is not playing, + // only save our non-inherited state and return. + + SFXSource* sourceGroup = getSourceGroup(); + if( sourceGroup != NULL && !sourceGroup->isPlaying() ) + { + mSavedStatus = SFXStatusPlaying; + mSavedFadeTime = fadeInTime; + + return; + } + + // Add fades, if required. + + if( fadeInTime == -1.f ) + fadeInTime = mFadeInTime; + + if( status == SFXStatusPaused && fadeInTime > 0.f ) + { + // Source is paused. Set up temporary fade-in segment. + + mFadeSegmentEase = &mDescription->mFadeInEase; + mFadeSegmentType = FadeSegmentPlay; + mFadeSegmentStartPoint = getElapsedPlayTimeCurrentCycle(); + mFadeSegmentEndPoint = mFadeSegmentStartPoint + fadeInTime; + } + else if( fadeInTime > 0.f ) + { + mFadeInPoint = fadeInTime; + + // Add fade-out if play-time of the source is finite and + // if it is either not looping or has fading enabled on loops. + + F32 totalPlayTime = getTotalPlayTime(); + if( !mIsInf_F( totalPlayTime ) && mDescription->mFadeOutTime > 0.f && ( !mDescription->mIsLooping || mDescription->mFadeLoops ) ) + { + mFadeOutPoint = totalPlayTime - mDescription->mFadeOutTime; + + // If there's an intersection between fade-in and fade-out, move them + // to the midpoint. + + if( mFadeOutPoint < mFadeInPoint ) + { + F32 midPoint = mFadeOutPoint + ( mFadeInPoint - mFadeOutPoint / 2 ); + + mFadeInPoint = midPoint; + mFadeOutPoint = midPoint; + } + } + } + else + { + mFadeInPoint = -1.f; + mFadeOutPoint = -1.f; + } + + // Start playback. + + if( status != SFXStatusPaused ) + { + mPlayStartTick = Platform::getVirtualMilliseconds(); + mPlayTimer.reset(); + } + + _setStatus( SFXStatusPlaying ); + _play(); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXSource] Started playback of source '%i'", getId() ); + #endif +} + +//----------------------------------------------------------------------------- + +void SFXSource::pause( F32 fadeOutTime ) +{ + SFXStatus status = getStatus(); + if( status != SFXStatusPlaying ) + return; + + // Pause playback. + + if( fadeOutTime == -1.f ) + fadeOutTime = mFadeOutTime; + + if( fadeOutTime > 0.f ) + _setupFadeOutSegment( FadeSegmentPause, fadeOutTime ); + else + { + _setStatus( SFXStatusPaused ); + _pause(); + + mFadeSegmentType = FadeSegmentNone; + mPreFadeVolume = mVolume; + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXSource] Paused playback of source '%i'", getId() ); + #endif + } +} + +//----------------------------------------------------------------------------- + +void SFXSource::stop( F32 fadeOutTime ) +{ + SFXStatus status = getStatus(); + if( status == SFXStatusStopped ) + return; + + if( status == SFXStatusPaused ) + { + _setStatus( SFXStatusStopped ); + _stop(); + return; + } + + if( fadeOutTime == -1.f ) + fadeOutTime = mFadeOutTime; + + if( fadeOutTime > 0.f ) + _setupFadeOutSegment( FadeSegmentStop, fadeOutTime ); + else + { + _setStatus( SFXStatusStopped ); + _stop(); + + mFadeSegmentType = FadeSegmentNone; + mPreFadeVolume = mVolume; + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXSource] Stopped playback of source '%i'", getId() ); + #endif + } +} + +//----------------------------------------------------------------------------- + +void SFXSource::update() +{ + if( !isPlaying() ) + return; + + _update(); + + // Update our modifiers, if any. + + for( ModifierList::Iterator iter = mModifiers.begin(); + iter != mModifiers.end(); ) + { + ModifierList::Iterator next = iter; + next ++; + + if( !( *iter )->update() ) + { + delete *iter; + mModifiers.erase( iter ); + } + + iter = next; + } +} + +//----------------------------------------------------------------------------- + +void SFXSource::_play() +{ + mPlayTimer.start(); + + // Resume playback of children that want to be in + // playing state. + + for( iterator iter = begin(); iter != end(); ++ iter ) + { + SFXSource* source = dynamic_cast< SFXSource* >( *iter ); + if( source && source->mSavedStatus == SFXStatusPlaying ) + source->play( source->mSavedFadeTime ); + } +} + +//----------------------------------------------------------------------------- + +void SFXSource::_pause() +{ + // Pause playing child sources. + + for( iterator iter = begin(); iter != end(); ++ iter ) + { + SFXSource* source = dynamic_cast< SFXSource* >( *iter ); + if( source && source->isPlaying() ) + { + source->pause( 0.f ); + + // Save info for resuming playback. + + source->mSavedStatus = SFXStatusPlaying; + source->mSavedFadeTime = 0.f; + } + } + + mPlayTimer.pause(); +} + +//----------------------------------------------------------------------------- + +void SFXSource::_stop() +{ + // Stop all child sources. + + for( iterator iter = begin(); iter != end(); ++ iter ) + { + SFXSource* source = dynamic_cast< SFXSource* >( *iter ); + if( source ) + source->stop( 0.f ); + } + + mPlayTimer.stop(); +} + +//----------------------------------------------------------------------------- + +void SFXSource::_update() +{ + /// Update our modulated properties. + + _updateVolume( SFX->getListener().getTransform() ); + _updatePitch(); + _updatePriority(); +} + +//----------------------------------------------------------------------------- + +bool SFXSource::_setDescription( void* obj, const char* index, const char* data ) +{ + SFXSource* source = reinterpret_cast< SFXSource* >( obj ); + + source->mDescription = EngineUnmarshallData< SFXDescription* >()( data ); + if( !source->mDescription ) + { + Con::errorf( "SFXSource::_setDescription - No SFXDescription '%s'", data ); + return false; + } + + source->notifyDescriptionChanged(); + + return false; +} + +//----------------------------------------------------------------------------- + +const char* SFXSource::_getDescription( void* obj, const char* data ) +{ + SFXSource* source = reinterpret_cast< SFXSource* >( obj ); + SFXDescription* description = source->mDescription; + + if( !description ) + return ""; + + return description->getName(); +} + +//----------------------------------------------------------------------------- + +void SFXSource::_setStatus( SFXStatus status ) +{ + if( mStatus == status ) + return; + + mStatus = status; + + // Clear saved status. + + mSavedStatus = SFXStatusNull; + + // Do the callback if we have it. + + if( mStatusCallback && mStatusCallback[0] ) + { + const char* statusString = SFXStatusToString( mStatus ); + Con::executef( mStatusCallback, getIdString(), statusString ); + } + else if( getNamespace() ) + onStatusChange_callback( status ); +} + +//----------------------------------------------------------------------------- + +void SFXSource::_updateVolume( const MatrixF& listener ) +{ + // Handle fades (compute mFadedVolume). + + mFadedVolume = mPreFadeVolume; + if( mFadeSegmentType != FadeSegmentNone ) + { + // We have a temporary fade segment. + + F32 elapsed; + if( mDescription->mIsLooping && !mDescription->mFadeLoops ) + elapsed = getElapsedPlayTime(); + else + elapsed = getElapsedPlayTimeCurrentCycle(); + + if( elapsed < mFadeSegmentEndPoint ) + { + const F32 duration = mFadeSegmentEndPoint - mFadeSegmentStartPoint; + + if( mFadeSegmentType == FadeSegmentPlay ) + { + const F32 time = elapsed - mFadeSegmentStartPoint; + mFadedVolume = mFadeSegmentEase->getValue + ( time, 0.f, mPreFadeVolume, duration ); + } + else + { + // If the end-point to the ease functions is 0, + // we'll always get out the start point. Thus do the whole + // thing backwards here. + + const F32 time = mFadeSegmentEndPoint - elapsed; + mFadedVolume = mFadeSegmentEase->getValue + ( time, 0.f, mPreFadeVolume, duration ); + } + } + else + { + // The fade segment has played. Remove it. + + switch( mFadeSegmentType ) + { + case FadeSegmentStop: + stop( 0.f ); + break; + + case FadeSegmentPause: + pause( 0.f ); + break; + + case FadeSegmentPlay: // Nothing to do. + default: + break; + } + + mFadeSegmentType = FadeSegmentNone; + mPreFadeVolume = mVolume; + } + } + else if( mFadeInPoint != -1 || mFadeOutPoint != -1 ) + { + F32 elapsed; + if( mDescription->mIsLooping && !mDescription->mFadeLoops ) + elapsed = getElapsedPlayTime(); + else + elapsed = getElapsedPlayTimeCurrentCycle(); + + // Check for fade-in. + + if( mFadeInPoint != -1 ) + { + if( elapsed < mFadeInPoint ) + mFadedVolume = mDescription->mFadeInEase.getValue( elapsed, 0.f, mPreFadeVolume, mFadeInPoint ); + else if( mDescription->mIsLooping && !mDescription->mFadeLoops ) + { + // Deactivate fade-in so we don't see it on further loops. + mFadeInPoint = -1; + } + } + + // Check for fade-out. + + if( mFadeOutPoint != -1 && elapsed > mFadeOutPoint ) + { + const F32 totalPlayTime = getTotalPlayTime(); + const F32 duration = totalPlayTime - mFadeOutPoint; + const F32 time = totalPlayTime - elapsed; + + mFadedVolume = mDescription->mFadeOutEase.getValue( time, 0.f, mPreFadeVolume, duration ); + } + } + + // Compute the pre-attenuated volume. + + mPreAttenuatedVolume = + mFadedVolume + * mModulativeVolume; + + SFXSource* group = getSourceGroup(); + if( group ) + mPreAttenuatedVolume *= group->getAttenuatedVolume(); + + if( !is3d() ) + { + mDistToListener = 0.0f; + mAttenuatedVolume = mPreAttenuatedVolume; + } + else + { + // For 3D sounds, compute distance attenuation. + + Point3F pos, lpos; + mTransform.getColumn( 3, &pos ); + listener.getColumn( 3, &lpos ); + + mDistToListener = ( pos - lpos ).len(); + mAttenuatedVolume = SFXDistanceAttenuation( + SFX->getDistanceModel(), + mMinDistance, + mMaxDistance, + mDistToListener, + mPreAttenuatedVolume, + SFX->getRolloffFactor() ); + } +} + +//----------------------------------------------------------------------------- + +void SFXSource::_updatePitch() +{ + mEffectivePitch = mModulativePitch * mPitch; +} + +//----------------------------------------------------------------------------- + +void SFXSource::_updatePriority() +{ + mEffectivePriority = mPriority * mModulativePriority; + + SFXSource* group = getSourceGroup(); + if( group ) + mEffectivePriority *= group->getEffectivePriority(); +} + +//----------------------------------------------------------------------------- + +void SFXSource::setTransform( const MatrixF& transform ) +{ + mTransform = transform; +} + +//----------------------------------------------------------------------------- + +void SFXSource::setVelocity( const VectorF& velocity ) +{ + mVelocity = velocity; +} + +//----------------------------------------------------------------------------- + +void SFXSource::setFadeTimes( F32 fadeInTime, F32 fadeOutTime ) +{ + _setFadeTimes( fadeInTime, fadeOutTime ); + + if( mFadeInTime >= 0.f || mFadeOutTime >= 0.f ) + mFlags.set( CustomFadeFlag ); +} + +//----------------------------------------------------------------------------- + +void SFXSource::_setFadeTimes( F32 fadeInTime, F32 fadeOutTime ) +{ + if( fadeInTime >= 0.f ) + mFadeInTime = getMax( 0.f, fadeInTime ); + else + mFadeInTime = mDescription->mFadeInTime; + + if( fadeOutTime >= 0.f ) + mFadeOutTime = getMax( 0.f, fadeOutTime ); + else + mFadeOutTime = mDescription->mFadeOutTime; +} + +//----------------------------------------------------------------------------- + +void SFXSource::_setupFadeOutSegment( FadeSegmentType type, F32 fadeOutTime ) +{ + // Get the current faded volume as the start volume so + // that we correctly fade when starting in a fade. + + _updateVolume( SFX->getListener().getTransform() ); + mPreFadeVolume = mFadedVolume; + + mFadeSegmentEase = &mDescription->mFadeOutEase; + mFadeSegmentType = type; + + if( mDescription->mIsLooping && mDescription->mFadeLoops ) + { + mFadeSegmentStartPoint = getElapsedPlayTimeCurrentCycle(); + mFadeSegmentEndPoint = getMin( mFadeSegmentStartPoint + fadeOutTime, getTotalPlayTime() ); + } + else + { + mFadeSegmentStartPoint = getElapsedPlayTime(); + mFadeSegmentEndPoint = mFadeSegmentStartPoint + fadeOutTime; + } +} + +//----------------------------------------------------------------------------- + +void SFXSource::_setMinMaxDistance( F32 min, F32 max ) +{ + mMinDistance = getMax( 0.0f, min ); + mMaxDistance = getMax( mMinDistance, max ); +} + +//----------------------------------------------------------------------------- + +void SFXSource::_setCone( F32 innerAngle, F32 outerAngle, F32 outerVolume ) +{ + mConeInsideAngle = mClampF( innerAngle, 0.0, 360.0 ); + mConeOutsideAngle = mClampF( outerAngle, mConeInsideAngle, 360.0 ); + mConeOutsideVolume = mClampF( outerVolume, 0.0, 1.0 ); +} + +//----------------------------------------------------------------------------- + +void SFXSource::_setVolume( F32 volume ) +{ + mVolume = mClampF( volume, 0.f, 1.f ); + mPreFadeVolume = mVolume; + _updateVolume( SFX->getListener( 0 ).getTransform() ); +} + +//----------------------------------------------------------------------------- + +void SFXSource::setModulativeVolume( F32 value ) +{ + mModulativeVolume = mClampF( value, 0.f, 1.f ); + _updateVolume( SFX->getListener( 0 ).getTransform() ); +} + +//----------------------------------------------------------------------------- + +void SFXSource::_setPitch( F32 pitch ) +{ + mPitch = mClampF( pitch, 0.001f, 2.0f ); + _updatePitch(); +} + +//----------------------------------------------------------------------------- + +void SFXSource::setModulativePitch( F32 value ) +{ + mModulativePitch = mClampF( value, 0.001f, 2.0f ); + _updatePitch(); +} + +//----------------------------------------------------------------------------- + +void SFXSource::_setPriority( F32 priority ) +{ + mPriority = priority; + _updatePriority(); +} + +//----------------------------------------------------------------------------- + +void SFXSource::setModulativePriority( F32 value ) +{ + mModulativePriority = value; + _updatePriority(); +} + +//----------------------------------------------------------------------------- + +SFXTrack* SFXSource::getTrack() const +{ + return mTrack; +} + +//----------------------------------------------------------------------------- + +void SFXSource::notifyDescriptionChanged() +{ + if( !mFlags.test( CustomVolumeFlag ) ) + _setVolume( mDescription->mVolume ); + if( !mFlags.test( CustomPitchFlag ) ) + _setPitch( mDescription->mPitch ); + if( !mFlags.test( CustomPriorityFlag ) ) + _setPriority( mDescription->mPriority ); + if( !mFlags.test( CustomRadiusFlag ) ) + _setMinMaxDistance( mDescription->mMinDistance, mDescription->mMaxDistance ); + if( !mFlags.test( CustomConeFlag ) ) + _setCone( mDescription->mConeInsideAngle, mDescription->mConeOutsideAngle, mDescription->mConeOutsideVolume ); + if( !mFlags.test( CustomFadeFlag ) ) + _setFadeTimes( mDescription->mFadeInTime, mDescription->mFadeOutTime ); + if( !mFlags.test( CustomGroupFlag ) && mDescription->mSourceGroup != NULL && getGroup() != mDescription->mSourceGroup ) + mDescription->mSourceGroup->addObject( this ); +} + +//----------------------------------------------------------------------------- + +void SFXSource::notifyTrackChanged() +{ + //RDTODO +} + +//----------------------------------------------------------------------------- + +template< class T > +void SFXSource::_clearModifiers() +{ + for( ModifierList::Iterator iter = mModifiers.begin(); + iter != mModifiers.end(); ) + { + ModifierList::Iterator next = iter; + next ++; + + if( dynamic_cast< T* >( *iter ) ) + { + delete *iter; + mModifiers.erase( iter ); + } + + iter = next; + } +} + +//----------------------------------------------------------------------------- + +void SFXSource::addModifier( SFXModifier* modifier ) +{ + mModifiers.pushBack( modifier ); +} + +//----------------------------------------------------------------------------- + +void SFXSource::addMarker( const String& name, F32 pos ) +{ + addModifier( new SFXMarkerModifier( this, name, pos ) ); +} + +//----------------------------------------------------------------------------- + +void SFXSource::addParameter( SFXParameter* parameter ) +{ + for( U32 i = 0; i < mParameters.size(); ++ i ) + if( mParameters[ i ] == parameter ) + return; + + mParameters.push_back( parameter ); + parameter->getEventSignal().notify( this, &SFXSource::_onParameterEvent ); + + // Trigger an initial value-changed event so the + // current parameter value becomes effective. + + _onParameterEvent( parameter, SFXParameterEvent_ValueChanged ); +} + +//----------------------------------------------------------------------------- + +void SFXSource::removeParameter( SFXParameter* parameter ) +{ + for( U32 i = 0; i < mParameters.size(); ++ i ) + if( mParameters[ i ] == parameter ) + { + mParameters[ i ]->getEventSignal().remove( this, &SFXSource::_onParameterEvent ); + mParameters.erase( i ); + break; + } +} + +//----------------------------------------------------------------------------- + +void SFXSource::_addParameter( StringTableEntry name ) +{ + SFXParameter* parameter = dynamic_cast< SFXParameter* >( + Sim::getSFXParameterGroup()->findObjectByInternalName( name ) + ); + + if( !parameter ) + { + Con::errorf( "SFXSource::_addParameter - no parameter called '%s'", name ); + return; + } + + addParameter( parameter ); +} + +//----------------------------------------------------------------------------- + +void SFXSource::_onParameterEvent( SFXParameter* parameter, SFXParameterEvent event ) +{ + switch( event ) + { + case SFXParameterEvent_ValueChanged: + + onParameterValueChange_callback( parameter ); + + switch( parameter->getChannel() ) + { + case SFXChannelStatus: + { + F32 value = parameter->getValue(); + if( value >= F32( SFXStatusPlaying ) && value <= F32( SFXStatusStopped ) ) + { + SFXStatus status = ( SFXStatus ) U32( value ); + switch( status ) + { + case SFXStatusPlaying: play(); break; + case SFXStatusPaused: pause(); break; + case SFXStatusStopped: stop(); break; + + default: + break; + } + } + break; + } + + case SFXChannelVolume: + setVolume( parameter->getValue() ); + break; + + case SFXChannelPitch: + setPitch( parameter->getValue() ); + break; + + case SFXChannelPriority: + setPriority( parameter->getValue() ); + break; + + case SFXChannelMinDistance: + setMinMaxDistance( parameter->getValue(), mMaxDistance ); + break; + + case SFXChannelMaxDistance: + setMinMaxDistance( mMinDistance, parameter->getValue() ); + break; + + case SFXChannelPositionX: + { + Point3F position = mTransform.getPosition(); + position.x = parameter->getValue(); + mTransform.setPosition( position ); + setTransform( mTransform ); + break; + } + + case SFXChannelPositionY: + { + Point3F position = mTransform.getPosition(); + position.y = parameter->getValue(); + mTransform.setPosition( position ); + setTransform( mTransform ); + break; + } + + case SFXChannelPositionZ: + { + Point3F position = mTransform.getPosition(); + position.z = parameter->getValue(); + mTransform.setPosition( position ); + setTransform( mTransform ); + break; + } + + case SFXChannelRotationX: + { + EulerF angles = mTransform.toEuler(); + angles.x = parameter->getValue(); + MatrixF transform( angles ); + transform.setPosition( mTransform.getPosition() ); + setTransform( transform ); + break; + } + + case SFXChannelRotationY: + { + EulerF angles = mTransform.toEuler(); + angles.y = parameter->getValue(); + MatrixF transform( angles ); + transform.setPosition( mTransform.getPosition() ); + setTransform( transform ); + break; + } + + case SFXChannelRotationZ: + { + EulerF angles = mTransform.toEuler(); + angles.z = parameter->getValue(); + MatrixF transform( angles ); + transform.setPosition( mTransform.getPosition() ); + setTransform( transform ); + break; + } + + case SFXChannelVelocityX: + { + mVelocity.x = parameter->getValue(); + setVelocity( mVelocity ); + break; + } + + case SFXChannelVelocityY: + { + mVelocity.y = parameter->getValue(); + setVelocity( mVelocity ); + break; + } + + case SFXChannelVelocityZ: + { + mVelocity.z = parameter->getValue(); + setVelocity( mVelocity ); + break; + } + + default: + break; + } + break; + + case SFXParameterEvent_Deleted: + removeParameter( parameter ); + break; + } +} + +//----------------------------------------------------------------------------- + +void SFXSource::_scatterTransform() +{ + if( mDescription ) + { + Point3F position = mTransform.getPosition(); + for( U32 i = 0; i < 3; ++ i ) + { + F32 scatterDist = mDescription->mScatterDistance[ i ]; + if( scatterDist != 0.f ) + position[ 0 ] += gRandGen.randF( - scatterDist, scatterDist ); + } + mTransform.setPosition( position ); + } + + mTransformScattered = true; +} + +//============================================================================= +// Console Methods. +//============================================================================= +// MARK: ---- Console Methods ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXSource, play, void, ( F32 fadeInTime ), ( -1.f ), + "Start playback of the source.\n" + "If the sound data for the source has not yet been fully loaded, there will be a delay after calling " + "play and playback will start after the data has become available.\n\n" + "@param fadeInTime Seconds for the sound to reach full volume. If -1, the SFXDescription::fadeInTime " + "set in the source's associated description is used. Pass 0 to disable a fade-in effect that may " + "be configured on the description." ) +{ + object->play( fadeInTime ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXSource, stop, void, ( F32 fadeOutTime ), ( -1.f ), + "Stop playback of the source.\n" + "@param fadeOutTime Seconds for the sound to fade down to zero volume. If -1, the SFXDescription::fadeOutTime " + "set in the source's associated description is used. Pass 0 to disable a fade-out effect that may be " + "configured on the description.\n" + "Be aware that if a fade-out effect is used, the source will not immediately transtion to stopped state but " + "will rather remain in playing state until the fade-out time has expired." ) +{ + object->stop( fadeOutTime ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXSource, pause, void, ( F32 fadeOutTime ), ( -1.f ), + "Pause playback of the source.\n" + "@param fadeOutTime Seconds for the sound to fade down to zero volume. If -1, the SFXDescription::fadeOutTime " + "set in the source's associated description is used. Pass 0 to disable a fade-out effect that may be " + "configured on the description.\n" + "Be aware that if a fade-out effect is used, the source will not immediately to paused state but will " + "rather remain in playing state until the fade-out time has expired.." ) +{ + object->pause( fadeOutTime ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXSource, isPlaying, bool, (),, + "Test whether the source is currently playing.\n" + "@return True if the source is in playing state, false otherwise.\n\n" + "@see play\n" + "@see getStatus\n" + "@see SFXStatus" ) +{ + return object->isPlaying(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXSource, isPaused, bool, (),, + "Test whether the source is currently paused.\n" + "@return True if the source is in paused state, false otherwise.\n\n" + "@see pause\n" + "@see getStatus\n" + "@see SFXStatus" ) +{ + return object->isPaused(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXSource, isStopped, bool, (),, + "Test whether the source is currently stopped.\n" + "@return True if the source is in stopped state, false otherwise.\n\n" + "@see stop\n" + "@see getStatus\n" + "@see SFXStatus" ) +{ + return object->isStopped(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXSource, getStatus, SFXStatus, (),, + "Get the current playback status.\n" + "@return Te current playback status\n" ) +{ + return object->getStatus(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXSource, getVolume, F32, (),, + "Get the current base volume level of the source.\n" + "This is not the final effective volume that the source is playing at but rather the starting " + "volume level before source group modulation, fades, or distance-based volume attenuation are applied.\n\n" + "@return The current base volume level.\n\n" + "@see setVolume\n" + "@see SFXDescription::volume\n\n" + "@ref SFXSource_volume" ) +{ + return object->getVolume(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXSource, setVolume, void, ( F32 volume ),, + "Set the base volume level for the source.\n" + "This volume will be the starting point for source group volume modulation, fades, and distance-based " + "volume attenuation.\n\n" + "@param volume The new base volume level for the source. Must be 0>=volume<=1.\n\n" + "@see getVolume\n\n" + "@ref SFXSource_volume" ) +{ + object->setVolume( volume ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXSource, getAttenuatedVolume, F32, (),, + "Get the final effective volume level of the source.\n\n" + "This method returns the volume level as it is after source group volume modulation, fades, and distance-based " + "volume attenuation have been applied to the base volume level.\n\n" + "@return The effective volume of the source.\n\n" + "@ref SFXSource_volume" ) +{ + return object->getAttenuatedVolume(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXSource, getFadeInTime, F32, (),, + "Get the fade-in time set on the source.\n" + "This will initially be SFXDescription::fadeInTime.\n\n" + "@return The fade-in time set on the source in seconds.\n\n" + "@see SFXDescription::fadeInTime\n\n" + "@ref SFXSource_fades" ) +{ + return object->getFadeInTime(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXSource, getFadeOutTime, F32, (),, + "Get the fade-out time set on the source.\n" + "This will initially be SFXDescription::fadeOutTime.\n\n" + "@return The fade-out time set on the source in seconds.\n\n" + "@see SFXDescription::fadeOutTime\n\n" + "@ref SFXSource_fades" ) +{ + return object->getFadeOutTime(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXSource, setFadeTimes, void, ( F32 fadeInTime, F32 fadeOutTime ),, + "Set the fade time parameters of the source.\n" + "@param fadeInTime The new fade-in time in seconds.\n" + "@param fadeOutTime The new fade-out time in seconds.\n\n" + "@see SFXDescription::fadeInTime\n" + "@see SFXDescription::fadeOutTime\n\n" + "@ref SFXSource_fades" ) +{ + object->setFadeTimes( fadeInTime, fadeOutTime ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXSource, getPitch, F32, (),, + "Get the pitch scale of the source.\n" + "Pitch determines the playback speed of the source (default: 1).\n\n" + "@return The current pitch scale factor of the source.\n\n" + "@see setPitch\n" + "@see SFXDescription::pitch" ) +{ + return object->getPitch(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXSource, setPitch, void, ( F32 pitch ),, + "Set the pitch scale of the source.\n" + "Pitch determines the playback speed of the source (default: 1).\n\n" + "@param pitch The new pitch scale factor.\n\n" + "@see getPitch\n" + "@see SFXDescription::pitch" ) +{ + object->setPitch( pitch ); +} + +//----------------------------------------------------------------------------- + +// Need an overload here as we can't use a default parameter to signal omission of the direction argument +// and we need to properly detect the omission to leave the currently set direction on the source as is. + +DEFINE_CALLIN( fnSFXSoure_setTransform1, setTransform, SFXSource, void, ( SFXSource* source, const VectorF& position ),,, + "Set the position of the source's 3D sound.\n\n" + "@param position The new position in world space.\n" + "@note This method has no effect if the source is not a 3D source." ) +{ + MatrixF mat = source->getTransform(); + mat.setPosition( position ); + source->setTransform( mat ); +} +DEFINE_CALLIN( fnSFXSoure_setTransform2, setTransform, SFXSource, void, ( SFXSource* source, const VectorF& position, const VectorF& direction ),,, + "Set the position and orientation of the source's 3D sound.\n\n" + "@param position The new position in world space.\n" + "@param direction The forward vector." ) +{ + MatrixF mat = source->getTransform(); + mat.setPosition( position ); + mat.setColumn( 1, direction ); + source->setTransform( mat ); +} + +// Console interop version. + +static ConsoleDocFragment _sSetTransform1( + "Set the position of the source's 3D sound.\n\n" + "@param position The new position in world space.\n" + "@note This method has no effect if the source is not a 3D source.", + "SFXSource", + "void setTransform( Point3F position )" +); +static ConsoleDocFragment _sSetTransform2( + "Set the position and orientation of the source's 3D sound.\n\n" + "@param position The new position in world space.\n" + "@param direction The forward vector.", + "SFXSource", + "void setTransform( Point3F position, Point3F direction )" +); + +ConsoleMethod( SFXSource, setTransform, void, 3, 4, + "( vector position [, vector direction ] ) " + "Set the position and orientation of a 3D sound source.\n" + "@hide" ) +{ + MatrixF mat = object->getTransform(); + + Point3F pos; + dSscanf( argv[2], "%g %g %g", &pos.x, &pos.y, &pos.z ); + mat.setPosition( pos ); + + if( argc > 3 ) + { + Point3F dir; + dSscanf( argv[ 3 ], "%g %g %g", &dir.x, &dir.y, &dir.z ); + mat.setColumn( 1, dir ); + } + + object->setTransform( mat ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXSource, setCone, void, ( F32 innerAngle, F32 outerAngle, F32 outsideVolume ),, + "Set up the 3D volume cone for the source.\n\n" + "@param innerAngle Angle of the inner sound cone in degrees (@ref SFXDescription::coneInsideAngle). Must be 0<=innerAngle<=360.\n" + "@param outerAngle Angle of the outer sound cone in degrees (@ref SFXDescription::coneOutsideAngle). Must be 0<=outerAngle<=360.\n" + "@param outsideVolume Volume scale factor outside of outer cone (@ref SFXDescription::coneOutsideVolume). Must be 0<=outsideVolume<=1.\n" + "@note This method has no effect on the source if the source is not 3D.\n\n" ) +{ + bool isValid = true; + + if( innerAngle < 0.0 || innerAngle > 360.0 ) + { + Con::errorf( "SFXSource.setCone() - 'innerAngle' must be between 0 and 360" ); + isValid = false; + } + if( outerAngle < 0.0 || outerAngle > 360.0 ) + { + Con::errorf( "SFXSource.setCone() - 'outerAngle' must be between 0 and 360" ); + isValid = false; + } + if( outsideVolume < 0.0 || outsideVolume > 1.0 ) + { + Con::errorf( "SFXSource.setCone() - 'outsideVolume' must be between 0 and 1" ); + isValid = false; + } + + if( !isValid ) + return; + + object->setCone( innerAngle, outerAngle, outsideVolume ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXSource, getParameterCount, S32, (),, + "Get the number of SFXParameters that are attached to the source.\n" + "@return The number of parameters attached to the source.\n\n" + "@tsexample\n" + "// Print the name ofo each parameter attached to %source.\n" + "%numParams = %source.getParameterCount();\n" + "for( %i = 0; %i < %numParams; %i ++ )\n" + " echo( %source.getParameter( %i ).getParameterName() );\n" + "@endtsexample\n\n" + "@see getParameter\n" + "@see addParameter\n" ) +{ + return object->getNumParameters(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXSource, addParameter, void, ( SFXParameter* parameter ),, + "Attach @a parameter to the source,\n\n" + "Once attached, the source will react to value changes of the given @a parameter. Attaching a parameter " + "will also trigger an initial read-out of the parameter's current value.\n\n" + "@param parameter The parameter to attach to the source." ) +{ + if( parameter ) + object->addParameter( parameter ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXSource, removeParameter, void, ( SFXParameter* parameter ),, + "Detach @a parameter from the source.\n\n" + "Once detached, the source will no longer react to value changes of the given @a parameter.\n\n" + "If the parameter is not attached to the source, the method will do nothing.\n\n" + "@param parameter The parameter to detach from the source.\n" ) +{ + if( parameter ) + object->removeParameter( parameter ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXSource, getParameter, SFXParameter*, ( S32 index ),, + "Get the parameter at the given index.\n" + "@param index Index of the parameter to fetch. Must be 0<=index<=getParameterCount().\n" + "@return The parameter at the given @a index or null if @a index is out of range.\n\n" + "@tsexample\n" + "// Print the name ofo each parameter attached to %source.\n" + "%numParams = %source.getParameterCount();\n" + "for( %i = 0; %i < %numParams; %i ++ )\n" + " echo( %source.getParameter( %i ).getParameterName() );\n" + "@endtsexample\n\n" + "@see getParameterCount" ) +{ + if( index >= object->getNumParameters() ) + { + Con::errorf( "SFXSource::getParameter - index out of range: %i", index ); + return 0; + } + + return object->getParameter( index ); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXSource, addMarker, void, ( String name, F32 pos ),, + "Add a notification marker called @a name at @a pos seconds of playback.\n\n" + "@param name Symbolic name for the marker that will be passed to the onMarkerPassed() callback.\n" + "@param pos Playback position in seconds when the notification should trigger. Note that this is a soft limit and there " + "may be a delay between the play cursor actually passing the position and the callback being triggered.\n\n" + "@note For looped sounds, the marker will trigger on each iteration.\n\n" + "@tsexample\n" + "// Create a new source.\n" + "$source = sfxCreateSource( AudioMusicLoop2D, \"art/sound/backgroundMusic\" );\n" + "\n" + "// Assign a class to the source.\n" + "$source.class = \"BackgroundMusic\";\n" + "\n" + "// Add a playback marker at one minute into playback.\n" + "$source.addMarker( \"first\", 60 );\n" + "\n" + "// Define the callback function. This function will be called when the playback position passes the one minute mark.\n" + "function BackgroundMusic::onMarkerPassed( %this, %markerName )\n" + "{\n" + " if( %markerName $= \"first\" )\n" + " echo( \"Playback has passed the 60 seconds mark.\" );\n" + "}\n" + "\n" + "// Play the sound.\n" + "$source.play();\n" + "@endtsexample" ) +{ + object->addMarker( name, pos ); +} diff --git a/Engine/source/sfx/sfxSource.h b/Engine/source/sfx/sfxSource.h new file mode 100644 index 000000000..83e31d0d2 --- /dev/null +++ b/Engine/source/sfx/sfxSource.h @@ -0,0 +1,671 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXSOURCE_H_ +#define _SFXSOURCE_H_ + +#ifndef _SIMSET_H_ + #include "console/simSet.h" +#endif +#ifndef _SFXCOMMON_H_ + #include "sfx/sfxCommon.h" +#endif +#ifndef _SFXDESCRIPTION_H_ + #include "sfx/sfxDescription.h" +#endif +#ifndef _SFXPARAMETER_H_ + #include "sfx/sfxParameter.h" +#endif +#ifndef _TVECTOR_H_ + #include "core/util/tVector.h" +#endif +#ifndef _TIMESOURCE_H_ + #include "core/util/timeSource.h" +#endif +#ifndef _BITSET_H_ + #include "core/bitSet.h" +#endif +#ifndef _TORQUE_LIST_ + #include "core/util/tList.h" +#endif + + +class SFXTrack; +class SFXDescription; +class SFXSourceGroup; +class SFXModifier; +class EaseF; + + +/// Baseclass for sources and controllers. +class SFXSource : public SimGroup +{ + public: + + typedef SimGroup Parent; + + friend class SFXSystem; // _init + + protected: + + typedef Torque::List< SFXModifier* > ModifierList; + typedef GenericTimeSource< RealMSTimer > TimeSource; + + enum Flags + { + CustomVolumeFlag = BIT( 0 ), + CustomPitchFlag = BIT( 1 ), + CustomPriorityFlag = BIT( 2 ), + CustomRadiusFlag = BIT( 3 ), + CustomConeFlag = BIT( 4 ), + CustomFadeFlag = BIT( 5 ), + CustomGroupFlag = BIT( 6 ), + }; + + /// + BitSet32 mFlags; + + /// @name Status + /// @{ + + /// Current playback status. + SFXStatus mStatus; + + /// The playback status that the source actually wants to be in. + SFXStatus mSavedStatus; + + /// Console functions to call when the source status changes. + /// If not set, "onStatusChange" will be called on the source object. + StringTableEntry mStatusCallback; + + /// Used internally for setting the sound status. + virtual void _setStatus( SFXStatus newStatus ); + + /// Update the playback status of the source. Meant for subclasses + /// that need to poll a connected resource. + virtual void _updateStatus() {} + + /// @} + + /// @name Datablocks + /// + /// The track datablock is optional but the description datablock is required. + /// If either of the two goes away, the source will auto-delete itself. + /// + /// These members are SimObjectPtr so that temporary track and description + /// objects that are set to auto-delete will work. + /// + /// @{ + + /// The track being played by this source. + /// @note Will be null for sources that have been created from SFXStreams. + SimObjectPtr< SFXTrack > mTrack; + + /// The description holding the SFX sound configuration. + SimObjectPtr< SFXDescription > mDescription; + + /// @} + + /// @name Volume + /// + /// Volume processing goes through a series of stages. The last stage, distance attenuation, + /// happens only for 3D sounds and is done on the device (though the attenuated volume is also + /// computed within SFX). + /// + /// The succession of stages is: + /// + /// 1. Fade (Apply fade-in/out if currently active) + /// 2. Modulate (Apply scale factor set on source) + /// 3. Modulate (Apply attenuated volume of source group as scale factor) + /// 4. Attenuate (Apply 3D distance attenuation based on current listener position) + /// + /// @{ + + /// The desired sound volume. + F32 mVolume; + + /// Volume before fade stage. Used as input to volume computation. Usually this + /// corresponds to mVolume but when fading out from an already faded start volume, + /// this contains the starting mFadedVolume. + F32 mPreFadeVolume; + + /// "mVolume" after fade stage. Same as "mPreFadeVolume" if no fade + /// is active. + F32 mFadedVolume; + + /// Volume scale factor imposed on this source by controller. + F32 mModulativeVolume; + + /// Effective volume after fade and modulation but before distance attenuation. + /// For non-3D sounds, this is the final effective volume. + F32 mPreAttenuatedVolume; + + /// Effective volume after distance attenuation. Continuously updated + /// to match listener position. For non-3D sounds, this is the same + /// as mPreAttenuatedVolume. + /// + /// @note The distance attenuation that is computed here does not take + /// sound cones into account so the computed attenuated volume may be + /// higher than the actual effective volume on the device (never + /// lower though). + F32 mAttenuatedVolume; + + /// Set volume without affecting CustomVolumeFlag. + void _setVolume( F32 volume ); + + /// Update the effective volume of the source. + virtual void _updateVolume( const MatrixF& listener ); + + /// @} + + /// @name Virtualization + /// @{ + + /// The desired sound priority. + F32 mPriority; + + /// The priority scale factor imposed by controllers. + F32 mModulativePriority; + + /// The final priority level. + F32 mEffectivePriority; + + /// Set priority without affecting CustomPriorityFlag. + void _setPriority( F32 priority ); + + /// Update the effective priority of the source. + virtual void _updatePriority(); + + /// @} + + /// @name Pitch + /// @{ + + /// The desired sound pitch. + F32 mPitch; + + /// The pitch scale factor imposed by controllers. + F32 mModulativePitch; + + /// The final effective pitch. + F32 mEffectivePitch; + + /// Set pitch without affecting CustomPitchFlag. + void _setPitch( F32 pitch ); + + /// Update the effective pitch of the source. + virtual void _updatePitch(); + + /// + + /// @} + + /// @name 3D Sound + /// @{ + + /// The transform if this is a 3d source. + MatrixF mTransform; + + /// The last set velocity. + VectorF mVelocity; + + /// Distance at which to begin distanced-based volume attenuation. + F32 mMinDistance; + + /// Distance at which to stop distance-based volume attenuation. + F32 mMaxDistance; + + /// Inside cone angle in degrees. + F32 mConeInsideAngle; + + /// Outside cone angle in degrees. + F32 mConeOutsideAngle; + + /// Outside cone volume. + F32 mConeOutsideVolume; + + /// The distance of this source to the last + /// listener position. + F32 mDistToListener; + + /// If true, the transform position has been randomized. + bool mTransformScattered; + + /// Randomize transform based on scatter settings. + void _scatterTransform(); + + /// Set 3D min/max distance without affecting CustomRadiusFlag. + virtual void _setMinMaxDistance( F32 min, F32 max ); + + /// Set 3D cone without affecting CustomConeFlag. + virtual void _setCone( F32 innerAngle, F32 outerAngle, F32 outerVolume ); + + /// @} + + /// @name Fading + /// + /// The fade system consists of "persistent" fades placed at the beginning + /// and end of the playback range and of "temporary" fade segments placed in the + /// playback range when stopping/pausing/resuming a source in midst of playback. + /// + /// @{ + + /// The current "persistent" fade-in time in seconds. Taken initially from the + /// SFXDescription and as long as not being manually set on the source, will + /// stay with the description's "fadeInTime" property. + F32 mFadeInTime; + + /// The current "persistent" fade-out time in seconds. Taken initially from the + /// SFXDescription and as long as not being manually set on the source, will + /// stay with the description's "fadeOutTime" property. + F32 mFadeOutTime; + + /// Type for temporary fade segments. + enum FadeSegmentType + { + FadeSegmentNone, ///< No temporary fade segment set. + FadeSegmentPlay, ///< Temporary fade-in segment. + FadeSegmentStop, ///< Temporary fade-out segment ending in stop(). + FadeSegmentPause ///< Temporary fade-out segment ending in pause(). + }; + + /// Playtime at which persistent fade-in ends. -1 if no fade-in. + F32 mFadeInPoint; + + /// Playtime at which persistent fade-out starts. -1 if no fade-out. + F32 mFadeOutPoint; + + /// Type of the current temporary fade segment. No temporary fade segment + /// is in place when this is FadeSegmentNone. + FadeSegmentType mFadeSegmentType; + + /// Easing curve for the current fade segment. + EaseF* mFadeSegmentEase; + + /// Playback position where the current temporary fade segment starts. + F32 mFadeSegmentStartPoint; + + /// Playback position where the current temporary fade segment ends. + F32 mFadeSegmentEndPoint; + + /// Fade time to apply when transitioning to mSavedStatus. + F32 mSavedFadeTime; + + /// + virtual void _setFadeTimes( F32 fadeInTime, F32 fadeOutTime ); + + /// Set up a temporary fade-out segment. + void _setupFadeOutSegment( FadeSegmentType type, F32 fadeOutTime ); + + /// @} + + /// @name Parameters + /// @{ + + /// + Vector< SFXParameter* > mParameters; + + /// + void _addParameter( StringTableEntry name ); + + /// + virtual void _onParameterEvent( SFXParameter* parameter, SFXParameterEvent event ); + + /// @} + + /// @name Playback + /// @{ + + /// The simulation tick count that playback was started at for this source. + U32 mPlayStartTick; + + /// Time object used to keep track of playback. + TimeSource mPlayTimer; + + /// Start playback. For implementation by concrete subclasses. + /// @note This method should not take fading into account. + virtual void _play(); + + /// Pause playback. For implementation by concrete subclasses. + /// @note This method should not take fading into account. + virtual void _pause(); + + /// Stop playback. For implementation by concrete subclasses. + /// @note This method should not take fading into account. + virtual void _stop(); + + /// @} + + /// @name Modifiers + /// @{ + + /// List of modifiers that are active on this source. + ModifierList mModifiers; + + /// Delete all modifiers of the given type. + template< class T > void _clearModifiers(); + + /// @} + + /// @name Callbacks + /// @{ + + DECLARE_CALLBACK( void, onStatusChange, ( SFXStatus newStatus ) ); + DECLARE_CALLBACK( void, onParameterValueChange, ( SFXParameter* parameter ) ); + + /// @} + + /// + SFXSource( SFXTrack* track, SFXDescription* description = NULL ); + + /// + virtual void _update(); + + /// We overload this to disable creation of + /// a source via script 'new'. + virtual bool processArguments( S32 argc, const char **argv ); + + // Console getters/setters. + static bool _setDescription( void *obj, const char *index, const char *data ); + static const char* _getDescription( void* obj, const char* data ); + + public: + + /// + SFXSource(); + + ~SFXSource(); + + /// + void update(); + + /// Returns the track played by this source; NULL for sources playing directly + /// from streams. + SFXTrack* getTrack() const; + + /// Return the SFX description associated with this source. Never NULL. + SFXDescription* getDescription() const { return mDescription; } + + /// Return the source group that this source has been assigned to. + SFXSource* getSourceGroup() const; + + /// @name Playback Status + /// @{ + + /// Returns the last known status without doing an update. + SFXStatus getLastStatus() const { return mStatus; } + + /// Return the status that the source wants to be in. Used for playback + /// control in combination with source groups. + SFXStatus getSavedStatus() const { return mSavedStatus; } + + /// Returns the sound status. + SFXStatus getStatus() const { const_cast< SFXSource* >( this )->_updateStatus(); return mStatus; } + + /// Returns true if the source is playing. + bool isPlaying() const { return getStatus() == SFXStatusPlaying; } + + /// Returns true if the source is stopped. + bool isStopped() const { return getStatus() == SFXStatusStopped; } + + /// Returns true if the source has been paused. + bool isPaused() const { return getStatus() == SFXStatusPaused; } + + /// Returns true if the source is currently being virtualized. + virtual bool isVirtualized() const { return false; } + + /// Returns true if this is a looping source. + bool isLooping() const { return mDescription->mIsLooping; } + + /// @} + + /// @name Playback Control + /// @{ + + /// Starts the sound from the current playback position. + /// + /// @param fadeInTime Seconds for sound to fade in. If -1, fadeInTime from + /// SFXDescription is used. Note that certain SFXSource classes may not + /// support values other than 0 and -1. + virtual void play( F32 fadeInTime = -1.f ); + + /// Stops playback and resets the playback position. + /// + /// @note This method is also required to release all playback-related + /// resources on the device. + /// + /// @param fadeOutTime Seconds for sound to fade out. If -1, fadeOutTime from + /// SFXDescription is used. Note that certain SFXSource classes may not support + /// values other than 0 and -1. + virtual void stop( F32 fadeOutTime = -1.f ); + + /// Pauses the sound playback. + /// + /// @param fadeOutTime Seconds for sound to fade out. If -1, fadeOutTime from + /// SFXDescription is used. Note that certain SFXSource clsases may not support + /// values other than 0 and -1. + virtual void pause( F32 fadeOutTime = -1.f ); + + /// Return the elapsed play time of the current loop cycle so far in seconds. + virtual F32 getElapsedPlayTimeCurrentCycle() const; + + /// Return the total elapsed play time so far in seconds. + virtual F32 getElapsedPlayTime() const; + + /// Return the total play time of the source in seconds. Positive infinity by default. + /// + /// @note For looping sounds, this must include only the playtime of a single cycle. + virtual F32 getTotalPlayTime() const; + + /// @} + + /// @name 3D Sound + /// @{ + + /// Returns true if this is a 3D source. + bool is3d() const { return mDescription->mIs3D; } + + /// Returns the last set velocity. + const VectorF& getVelocity() const { return mVelocity; } + + /// Returns the last set transform. + const MatrixF& getTransform() const { return mTransform; } + + /// Sets the position and orientation for a 3d buffer. + virtual void setTransform( const MatrixF& transform ); + + /// Sets the velocity for a 3d buffer. + virtual void setVelocity( const VectorF& velocity ); + + /// Sets the minimum and maximum distances for 3d falloff. + void setMinMaxDistance( F32 min, F32 max ) { _setMinMaxDistance( min, max ); mFlags.set( CustomRadiusFlag ); } + + /// Set sound cone of a 3D sound. + /// + /// @param innerAngle Inner cone angle in degrees. + /// @param outerAngle Outer cone angle in degrees. + /// @param outerVolume Outer volume factor. + void setCone( F32 innerAngle, F32 outerAngle, F32 outerVolume ) { _setCone( innerAngle, outerAngle, outerVolume ); mFlags.set( CustomConeFlag ); } + + /// Returns the last distance to the listener. + /// @note Only works when distance attenuation calculations are being triggered by SFX and + /// are not left exclusively to the SFX device. + F32 getDistToListener() const { return mDistToListener; } + + /// @} + + /// @name Volume + /// @{ + + /// Returns the source volume at its unaltered initial setting, + /// i.e. prior to fading, modulation, and attenuation. + F32 getVolume() const { return mVolume; } + + /// Sets the source volume which will still be + /// scaled by the master and group volumes. + /// + /// @note Note that if you set an explicit volume on a source + void setVolume( F32 volume ) { _setVolume( volume ); mFlags.set( CustomVolumeFlag ); } + + /// + F32 getModulativeVolume() const { return mModulativeVolume; } + + /// Set the per-source volume scale factor. + void setModulativeVolume( F32 value ); + + /// + F32 getPreAttenuatedVolume() const { return mPreAttenuatedVolume; } + + /// Returns the volume with respect to the master + /// and group volumes and the listener. + F32 getAttenuatedVolume() const { return mAttenuatedVolume; } + + /// + F32 getFadeInTime() const { return mFadeInTime; } + + /// + F32 getFadeOutTime() const { return mFadeOutTime; } + + /// + void setFadeTimes( F32 fadeInTime, F32 fadeOutTime ); + + /// @} + + /// @name Pitch + /// @{ + + /// Returns the source pitch scale. + F32 getPitch() const { return mPitch; } + + /// Sets the source pitch scale. + void setPitch( F32 pitch ) { _setPitch( pitch ); mFlags.set( CustomPitchFlag ); } + + /// + F32 getModulativePitch() const { return mModulativePitch; } + + /// + void setModulativePitch( F32 value ); + + /// + F32 getEffectivePitch() const { return mEffectivePitch; } + + /// @} + + /// @name Dynamic Parameters + /// + /// Dynamic parameters allow to pass on values from the game system to the sound system + /// and thus implement interactive audio. + /// + /// It is dependent on the back-end source implementation how it will react to parameter + /// settings. + /// + /// @{ + + /// + U32 getNumParameters() const { return mParameters.size(); } + + /// + SFXParameter* getParameter( U32 index ) + { + AssertFatal( index < getNumParameters(), "SFXSource::getParameter() - index out of range" ); + return mParameters[ index ]; + } + + /// + virtual void addParameter( SFXParameter* parameter ); + + /// + virtual void removeParameter( SFXParameter* parameter ); + + /// @} + + /// @name Modifiers + /// @{ + + /// + void addModifier( SFXModifier* modifier ); + + /// + void addMarker( const String& name, F32 pos ); + + /// @} + + /// @name Virtualization + /// @{ + + /// Returns the source priority. + F32 getPriority() const { return mPriority; } + + /// + void setPriority( F32 priority ) { _setPriority( priority ); mFlags.set( CustomPriorityFlag ); } + + /// + F32 getModulativePriority() const { return mModulativePriority; } + + /// + void setModulativePriority( F32 value ); + + /// + F32 getEffectivePriority() const { return mEffectivePriority; } + + /// @} + + /// @} + + /// @name Change Notifications + /// @{ + + /// Notify the source that its attached SFXDescription has changed. + virtual void notifyDescriptionChanged(); + + /// Notify the source that its attached SFXTrack has changed. + virtual void notifyTrackChanged(); + + /// @} + + // SimGroup. + virtual bool onAdd(); + virtual void onRemove(); + virtual void onDeleteNotify( SimObject* object ); + virtual bool acceptsAsChild( SimObject* object ); + virtual void onGroupAdd(); + + static void initPersistFields(); + + DECLARE_CONOBJECT( SFXSource ); + DECLARE_CATEGORY( "SFX" ); + DECLARE_DESCRIPTION( "SFX sound playback controller." ); +}; + +/// A simple macro to automate the deletion of a source. +/// +/// @see SFXSource +/// +#undef SFX_DELETE +#define SFX_DELETE( source ) \ + if( source ) \ + { \ + source->deleteObject(); \ + source = NULL; \ + } \ + +#endif // !_SFXSOURCE_H_ diff --git a/Engine/source/sfx/sfxState.cpp b/Engine/source/sfx/sfxState.cpp new file mode 100644 index 000000000..555f03511 --- /dev/null +++ b/Engine/source/sfx/sfxState.cpp @@ -0,0 +1,397 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/sfxState.h" +#include "sfx/sfxTypes.h" +#include "core/stream/bitStream.h" +#include "console/engineAPI.h" + + +//#define DEBUG_SPEW + + +IMPLEMENT_CO_DATABLOCK_V1( SFXState ); + + +ConsoleDocClass( SFXState, + "@brief A boolean switch used to modify playlist behavior.\n\n" + + "Sound system states are used to allow playlist controllers to make decisions based on global state. This is useful, for " + "example, to couple audio playback to gameplay state. Certain states may, for example, represent different locations that the " + "listener can be in, like underwater, in open space, or indoors. Other states could represent moods of the current gameplay " + "situation, like, for example, an aggressive mood during combat.\n\n" + + "By activating and deactivating sound states according to gameplay state, a set of concurrently running playlists may " + "react and adapt to changes in the game.\n\n" + + "@section SFXState_activation Activation and Deactivation\n" + + "At any time, a given state can be either active or inactive. Calling activate() on a state increases an internal " + "counter and calling deactivate() decreases the counter. Only when the count reaches zero will the state be " + "deactivated.\n\n" + + "In addition to the activation count, states also maintain a disabling count. Calling disable() increases this count " + "and calling enable() decreases it. As long as this count is greater than zero, a given state will not be activated " + "even if its activation count is non-zero. Calling disable() on an active state will not only increase the disabling " + "count but also deactivate the state. Calling enable() on a state with a positive activation count will re-activate " + "the state when the disabling count reaches zero.\n\n" + + "@section SFXState_dependencies State Dependencies\n" + + "By listing other states in in its #includedStates and #excludedStates fields, a state may automatically trigger the " + "activation or disabling of other states in the sytem when it is activated. This allows to form dependency chains " + "between individual states.\n\n" + + "@tsexample\n" + "// State indicating that the listener is submerged.\n" + "singleton SFXState( AudioLocationUnderwater )\n" + "{\n" + " parentGroup = AudioLocation;\n" + " // AudioStateExclusive is a class defined in the core scripts that will automatically\n" + " // ensure for a state to deactivate all the sibling SFXStates in its parentGroup when it\n" + " // is activated.\n" + " className = \"AudioStateExclusive\";\n" + "};\n" + "\n" + "// State suitable e.g. for combat.\n" + "singleton SFXState( AudioMoodAggressive )\n" + "{\n" + " parentGroup = AudioMood;\n" + " className = \"AudioStateExclusive\";\n" + "};\n" + "@endtsexample\n\n" + + "@see SFXPlayList\n" + "@see SFXController\n" + "@see SFXPlayList::state\n" + "@see SFXPlayList::stateMode\n\n" + "@ref SFX_interactive\n\n" + + "@ingroup SFX\n" + "@ingroup Datablocks" +); + + +IMPLEMENT_CALLBACK( SFXState, onActivate, void, (), (), + "Called when the state goes from inactive to active." ); +IMPLEMENT_CALLBACK( SFXState, onDeactivate, void, (), (), + "called when the state goes from active to deactive." ); + + +static Vector< SFXState* > sgActiveStates( __FILE__, __LINE__ ); + + +//----------------------------------------------------------------------------- + +SFXState::SFXState() + : mActiveCount( 0 ), + mDisableCount( 0 ) +{ + dMemset( mIncludedStates, 0, sizeof( mIncludedStates ) ); + dMemset( mExcludedStates, 0, sizeof( mExcludedStates ) ); +} + +//----------------------------------------------------------------------------- + +void SFXState::initPersistFields() +{ + addGroup( "State" ); + + addField( "includedStates", TypeSFXStateName, Offset( mIncludedStates, SFXState ), + MaxIncludedStates, + "States that will automatically be activated when this state is activated.\n\n" + "@ref SFXState_activation" ); + addField( "excludedStates", TypeSFXStateName, Offset( mExcludedStates, SFXState ), + MaxExcludedStates, + "States that will automatically be disabled when this state is activated.\n\n" + "@ref SFXState_activation" ); + + endGroup( "State" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +void SFXState::activate() +{ + mActiveCount ++; + if( mActiveCount == 1 && !isDisabled() ) + _onActivate(); +} + +//----------------------------------------------------------------------------- + +void SFXState::deactivate() +{ + if( !mActiveCount ) + return; + + mActiveCount --; + if( !mActiveCount && !isDisabled() ) + _onDeactivate(); +} + +//----------------------------------------------------------------------------- + +void SFXState::enable() +{ + if( !mDisableCount ) + return; + + mDisableCount --; + + if( !mDisableCount && mActiveCount > 0 ) + _onActivate(); +} + +//----------------------------------------------------------------------------- + +void SFXState::disable() +{ + mDisableCount ++; + + if( mDisableCount == 1 && mActiveCount > 0 ) + _onDeactivate(); +} + +//----------------------------------------------------------------------------- + +bool SFXState::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + Sim::getSFXStateSet()->addObject( this ); + + return true; +} + +//----------------------------------------------------------------------------- + +bool SFXState::preload( bool server, String& errorStr ) +{ + if( !Parent::preload( server, errorStr ) ) + return false; + + if( !server ) + { + for( U32 i = 0; i < MaxIncludedStates; ++ i ) + if( !sfxResolve( &mIncludedStates[ i ], errorStr ) ) + return false; + + for( U32 i = 0; i < MaxExcludedStates; ++ i ) + if( !sfxResolve( &mExcludedStates[ i ], errorStr ) ) + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- + +void SFXState::packData( BitStream* stream ) +{ + Parent::packData( stream ); + + for( U32 i = 0; i < MaxIncludedStates; ++ i ) + sfxWrite( stream, mIncludedStates[ i ] ); + for( U32 i = 0; i < MaxExcludedStates; ++ i ) + sfxWrite( stream, mExcludedStates[ i ] ); +} + +//----------------------------------------------------------------------------- + +void SFXState::unpackData( BitStream* stream ) +{ + Parent::unpackData( stream ); + + for( U32 i = 0; i < MaxIncludedStates; ++ i ) + sfxRead( stream, &mIncludedStates[ i ] ); + for( U32 i = 0; i < MaxExcludedStates; ++ i ) + sfxRead( stream, &mExcludedStates[ i ] ); +} + +//----------------------------------------------------------------------------- + +void SFXState::_onActivate() +{ + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXState] Activating '%s'", getName() ); + #endif + + onActivate_callback(); + + // Add the state to the list. + + sgActiveStates.push_back( this ); + + // Activate included states. + + for( U32 i = 0; i < MaxIncludedStates; ++ i ) + if( mIncludedStates[ i ] ) + mIncludedStates[ i ]->activate(); + + // Disable excluded states. + + for( U32 i = 0; i < MaxExcludedStates; ++ i ) + if( mExcludedStates[ i ] ) + mExcludedStates[ i ]->disable(); +} + +//----------------------------------------------------------------------------- + +void SFXState::_onDeactivate() +{ + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXState] Deactivating '%s'", getName() ); + #endif + + onDeactivate_callback(); + + // Remove the state from the list. + + for( U32 i = 0; i < sgActiveStates.size(); ++ i ) + if( sgActiveStates[ i ] == this ) + { + sgActiveStates.erase_fast( i ); + break; + } + + // Deactivate included states. + + for( U32 i = 0; i < MaxIncludedStates; ++ i ) + if( mIncludedStates[ i ] ) + mIncludedStates[ i ]->deactivate(); + + // Enable excluded states. + + for( U32 i = 0; i < MaxExcludedStates; ++ i ) + if( mExcludedStates[ i ] ) + mExcludedStates[ i ]->enable(); +} + +//============================================================================= +// Console Methods. +//============================================================================= +// MARK: ---- Console Methods ---- + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXState, isActive, bool, (),, + "Test whether the state is currently active.\n" + "This is true when the activation count is >0 and the disabling count is =0.\n" + "@return True if the state is currently active.\n" + "@see activate" ) +{ + return object->isActive(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXState, activate, void, (),, + "Increase the activation count on the state.\n" + "If the state isn't already active and it is not disabled, the state will be activated.\n" + "@see isActive\n" + "@see deactivate\n" ) +{ + object->activate(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXState, deactivate, void, (),, + "Decrease the activation count on the state.\n" + "If the count reaches zero and the state was not disabled, the state will be deactivated.\n" + "@see isActive\n" + "@see activate\n" ) +{ + object->deactivate(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXState, isDisabled, bool, (),, + "Test whether the state is currently disabled.\n" + "This is true when the disabling count of the state is non-zero.\n" + "@return True if the state is disabled.\n\n" + "@see disable\n" ) +{ + return object->isDisabled(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXState, disable, void, (),, + "Increase the disabling count of the state.\n" + "If the state is currently active, it will be deactivated.\n" + "@see isDisabled\n" ) +{ + object->disable(); +} + +//----------------------------------------------------------------------------- + +DefineEngineMethod( SFXState, enable, void, (),, + "Decrease the disabling count of the state.\n" + "If the disabling count reaches zero while the activation count is still non-zero, " + "the state will be reactivated again.\n" + "@see isDisabled\n" ) +{ + object->enable(); +} + +//============================================================================= +// Console Functions. +//============================================================================= +// MARK: ---- Console Functions ---- + +//----------------------------------------------------------------------------- + +DefineEngineFunction( sfxGetActiveStates, const char*, (),, + "Return a newline-separated list of all active states.\n" + "@return A list of the form\n" + "@verbatim\n" + "stateName1 NL stateName2 NL stateName3 ...\n" + "@endverbatim\n" + "where each element is the name of an active state object.\n\n" + "@tsexample\n" + "// Disable all active states.\n" + "foreach$( %state in sfxGetActiveStates() )\n" + " %state.disable();\n" + "@endtsexample\n\n" + "@ingroup SFX" ) +{ + StringBuilder str; + + bool isFirst = true; + for( U32 i = 0; i < sgActiveStates.size(); ++ i ) + { + if( !isFirst ) + str.append( ' ' ); + + str.append( sgActiveStates[ i ]->getName() ); + isFirst = false; + } + + return Con::getReturnBuffer( str ); +} diff --git a/Engine/source/sfx/sfxState.h b/Engine/source/sfx/sfxState.h new file mode 100644 index 000000000..320586e51 --- /dev/null +++ b/Engine/source/sfx/sfxState.h @@ -0,0 +1,120 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXSTATE_H_ +#define _SFXSTATE_H_ + +#ifndef _SIMDATABLOCK_H_ + #include "console/simDatablock.h" +#endif +#ifndef _CONSOLETYPES_H_ + #include "console/consoleTypes.h" +#endif +#ifndef _TVECTOR_H_ + #include "core/util/tVector.h" +#endif +#ifndef _SFXCOMMON_H_ + #include "sfx/sfxCommon.h" +#endif + + +/// A boolean switch used to modify playlist behavior. +/// +class SFXState : public SimDataBlock +{ + public: + + typedef SimDataBlock Parent; + + enum + { + MaxIncludedStates = 4, + MaxExcludedStates = 4 + }; + + protected: + + /// Reference count for activation. + U32 mActiveCount; + + /// Reference count for disabling. + U32 mDisableCount; + + /// States that will be activated when this state is activated. + SFXState* mIncludedStates[ MaxIncludedStates ]; + + /// States that will be disabled when this state is activated. + SFXState* mExcludedStates[ MaxExcludedStates ]; + + /// Call when state has become active. + void _onActivate(); + + /// Call when state has gone back to being deactive. + void _onDeactivate(); + + /// @name Callbacks + /// @{ + + DECLARE_CALLBACK( void, onActivate, () ); + DECLARE_CALLBACK( void, onDeactivate, () ); + + /// @} + + public: + + /// + SFXState(); + + /// Return true if the state is currently active (activated and not disabled). + bool isActive() const { return ( !isDisabled() && mActiveCount > 0 ); } + + /// Return true if the state is currently disabled. + bool isDisabled() const { return ( mDisableCount > 0 ); } + + /// Activate this state. activate/deactivate calls balance each other. + /// Activating a disabled state will not make it active. + void activate(); + + /// + void deactivate(); + + /// Re-enable this state. + void enable(); + + /// Disable this state so that it cannot be activated. This is used + /// by state exclusion. + void disable(); + + // SimDataBlock. + virtual bool onAdd(); + virtual bool preload( bool server, String& errorStr ); + virtual void packData( BitStream* stream ); + virtual void unpackData( BitStream* stream ); + + static void initPersistFields(); + + DECLARE_CONOBJECT( SFXState ); + DECLARE_CATEGORY( "SFX" ); + DECLARE_DESCRIPTION( "A datablock describing a particular state for the SFX system." ); +}; + +#endif // !_SFXSTATE_H_ diff --git a/Engine/source/sfx/sfxStream.h b/Engine/source/sfx/sfxStream.h new file mode 100644 index 000000000..6d52db3d6 --- /dev/null +++ b/Engine/source/sfx/sfxStream.h @@ -0,0 +1,86 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXSTREAM_H_ +#define _SFXSTREAM_H_ + +#ifndef _THREADSAFEREFCOUNT_H_ +# include "platform/threads/threadSafeRefCount.h" +#endif +#ifndef _SFXCOMMON_H_ +# include "sfx/sfxCommon.h" +#endif +#ifndef _TSTREAM_H_ +# include "core/stream/tStream.h" +#endif + + +/// The base sound data streaming interface. +/// +/// @note Streams that support seeking should implement the IPositionable interface. +/// @note Since SFXStreams are byte streams, all offset/size information is in bytes and +/// not in number of samples. +class SFXStream : public ThreadSafeRefCount< SFXStream >, + public IInputStream< U8 >, + public IResettable +{ + public: + + typedef void Parent; + + /// Destructor. + virtual ~SFXStream() {} + + /// Make a copy of this stream with its own private state, so + /// new independent read() operations can be issued on the same + /// data stream. + /// + /// @return Returns a copy of the stream or NULL. + virtual SFXStream* clone() const { return NULL; } + + /// The format of the data in the stream. + virtual const SFXFormat& getFormat() const = 0; + + /// The number of samples in the data stream. + virtual U32 getSampleCount() const = 0; + + /// The data size in bytes of the decompressed PCM data. + virtual U32 getDataLength() const = 0; + + /// The length of the sound in milliseconds. + virtual U32 getDuration() const = 0; + + /// Returns true if we've reached the end of the stream. + virtual bool isEOS() const = 0; + + /// Resets the stream to restart reading data from the begining. + virtual void reset() = 0; + + /// Reads data from the stream and decompresses it into PCM samples. + /// + /// @param length Number of bytes to read. + virtual U32 read( U8 *buffer, U32 length ) = 0; +}; + +typedef ThreadSafeRef< SFXStream > SFXStreamRef; + +#endif // _SFXSTREAM_H_ diff --git a/Engine/source/sfx/sfxSystem.cpp b/Engine/source/sfx/sfxSystem.cpp new file mode 100644 index 000000000..c43a44b2f --- /dev/null +++ b/Engine/source/sfx/sfxSystem.cpp @@ -0,0 +1,1883 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "sfx/sfxSystem.h" + +#include "sfx/sfxProvider.h" +#include "sfx/sfxDevice.h" +#include "sfx/sfxInternal.h" +#include "sfx/sfxSource.h" +#include "sfx/sfxProfile.h" +#include "sfx/sfxDescription.h" +#include "sfx/sfxTrack.h" +#include "sfx/sfxPlayList.h" +#include "sfx/sfxSound.h" +#include "sfx/sfxController.h" +#include "sfx/sfxSoundscape.h" + +#include "console/console.h" +#include "console/engineAPI.h" +#include "T3D/gameBase/processList.h" +#include "platform/profiler.h" +#include "platform/platformTimer.h" +#include "core/util/autoPtr.h" +#include "core/module.h" + +#include "sfx/media/sfxWavStream.h" +#ifdef TORQUE_OGGVORBIS + #include "sfx/media/sfxVorbisStream.h" +#endif + + +MODULE_BEGIN( SFX ) + + MODULE_INIT_BEFORE( Sim ) + MODULE_SHUTDOWN_BEFORE( Sim ) // Make sure all SimObjects disappear in time. + + MODULE_INIT + { + SFXSystem::init(); + } + + MODULE_SHUTDOWN + { + SFXSystem::destroy(); + } + +MODULE_END; + + +SFXSystem* SFXSystem::smSingleton = NULL; + + +// Excludes Null and Blocked as these are not passed out to the control layer. +ImplementEnumType( SFXStatus, + "Playback status of sound source.\n" + "@ingroup SFX" ) + { SFXStatusPlaying, "Playing", + "The source is currently playing." }, + { SFXStatusStopped, "Stopped", + "Playback of the source is stopped. When transitioning to Playing state, playback will start at the beginning " + "of the source." }, + { SFXStatusPaused, "Paused", + "Playback of the source is paused. Resuming playback will play from the current playback position." }, +EndImplementEnumType; + +ImplementEnumType( SFXDistanceModel, + "Type of volume distance attenuation curve.\n" + "The distance model determines the falloff curve applied to the volume of 3D sounds over distance.\n\n" + "@ref SFXSource_volume\n\n" + "@ref SFX_3d\n\n" + "@ingroup SFX" ) + { SFXDistanceModelLinear, "Linear", + "Volume attenuates linearly from the references distance onwards to max distance where it reaches zero." }, + { SFXDistanceModelLogarithmic, "Logarithmic", + "Volume attenuates logarithmically starting from the reference distance and halving every reference distance step from there on. " + "Attenuation stops at max distance but volume won't reach zero." }, +EndImplementEnumType; + +ImplementEnumType( SFXChannel, + "Channels are individual properties of sound sources that may be animated over time.\n\n" + "@see SFXParameter\n\n" + "@ref SFX_interactive\n\n" + "@ingroup SFX" ) + { SFXChannelVolume, "Volume", + "Channel controls volume level of attached sound sources.\n" + "@see SFXDescription::volume" }, + { SFXChannelPitch, "Pitch", + "Channel controls pitch of attached sound sources.\n" + "@see SFXDescription::pitch" }, + { SFXChannelPriority, "Priority", + "Channel controls virtualizaton priority level of attached sound sources.\n" + "@see SFXDescription::priority" }, + { SFXChannelPositionX, "PositionX", + "Channel controls X coordinate of 3D sound position of attached sources." }, + { SFXChannelPositionY, "PositionY", + "Channel controls Y coordinate of 3D sound position of attached sources." }, + { SFXChannelPositionZ, "PositionZ", + "Channel controls Z coordinate of 3D sound position of attached sources." }, + { SFXChannelRotationX, "RotationX", + "Channel controls X rotation (in degrees) of 3D sound orientation of attached sources." }, + { SFXChannelRotationY, "RotationY", + "Channel controls Y rotation (in degrees) of 3D sound orientation of attached sources." }, + { SFXChannelRotationZ, "RotationZ", + "Channel controls Z rotation (in degrees) of 3D sound orientation of attached sources." }, + { SFXChannelVelocityX, "VelocityX", + "Channel controls X coordinate of 3D sound velocity vector of attached sources." }, + { SFXChannelVelocityY, "VelocityY", + "Channel controls Y coordinate of 3D sound velocity vector of attached sources." }, + { SFXChannelVelocityZ, "VelocityZ", + "Channel controls Z coordinate of 3D sound velocity vector of attached sources." }, + { SFXChannelMinDistance, "ReferenceDistance", + "Channel controls reference distance of 3D sound of attached sources.\n" + "@see SFXDescription::referenceDistance" }, + { SFXChannelMaxDistance, "MaxDistance", + "Channel controls max volume attenuation distance of 3D sound of attached sources.\n" + "@see SFXDescription::maxDistance" }, + { SFXChannelConeInsideAngle, "ConeInsideAngle", + "Channel controls angle (in degrees) of 3D sound inner volume cone of attached sources.\n" + "@see SFXDescription::coneInsideAngle" }, + { SFXChannelConeOutsideAngle, "ConeOutsideAngle", + "Channel controls angle (in degrees) of 3D sound outer volume cone of attached sources.\n" + "@see SFXDescription::coneOutsideAngle" }, + { SFXChannelConeOutsideVolume, "ConeOutsideVolume", + "Channel controls volume outside of 3D sound outer cone of attached sources.\n" + "@see SFXDescription::coneOutsideVolume" }, + { SFXChannelCursor, "Cursor", + "Channel controls playback cursor of attached sound sources.\n\n" + "@note Be aware that different types of sound sources interpret play cursor positions differently " + "or do not actually have play cursors (these sources will ignore the channel)." }, + { SFXChannelStatus, "Status", + "Channel controls playback status of attached sound sources.\n\n" + "The channel's value is rounded down to the nearest integer and interpreted in the following way:\n" + "- 1: Play\n" + "- 2: Stop\n" + "- 3: Pause\n\n" }, + { SFXChannelUser0, "User0", + "Channel available for custom use. By default ignored by sources.\n\n" + "@note For FMOD Designer event sources (SFXFMODEventSource), this channel is used for event parameters " + "defined in FMOD Designer and should not be used otherwise.\n\n" + "@see SFXSource::onParameterValueChange" }, + { SFXChannelUser1, "User1", + "Channel available for custom use. By default ignored by sources.\n\n" + "@see SFXSource::onParameterValueChange" }, + { SFXChannelUser2, "User2", + "Channel available for custom use. By default ignored by sources.\n\n" + "@see SFXSource::onParameterValueChange" }, + { SFXChannelUser3, "User3", + "Channel available for custom use. By default ignored by sources.\n\n" + "@see SFXSource::onParameterValueChange" }, +EndImplementEnumType; + + +// Constants. +static const U32 sDeviceCapsReverb = SFXDevice::CAPS_Reverb; +static const U32 sDeviceCapsVoiceManagement = SFXDevice::CAPS_VoiceManagement; +static const U32 sDeviceCapsOcclusion = SFXDevice::CAPS_Occlusion; +static const U32 sDeviceCapsDSPEffects = SFXDevice::CAPS_DSPEffects; +static const U32 sDeviceCapsMultiListener = SFXDevice::CAPS_MultiListener; +static const U32 sDeviceCapsFMODDesigner = SFXDevice::CAPS_FMODDesigner; + +static const U32 sDeviceInfoProvider = 0; +static const U32 sDeviceInfoName = 1; +static const U32 sDeviceInfoUseHardware = 2; +static const U32 sDeviceInfoMaxBuffers = 3; +static const U32 sDeviceInfoCaps = 4; + + +//----------------------------------------------------------------------------- + +SFXSystem::SFXSystem() + : mDevice( NULL ), + mLastSourceUpdateTime( 0 ), + mLastAmbientUpdateTime( 0 ), + mLastParameterUpdateTime( 0 ), + mStatNumSources( 0 ), + mStatNumSounds( 0 ), + mStatNumPlaying( 0 ), + mStatNumCulled( 0 ), + mStatNumVoices( 0 ), + mStatSourceUpdateTime( 0 ), + mStatParameterUpdateTime( 0 ), + mStatAmbientUpdateTime( 0 ), + mDistanceModel( SFXDistanceModelLinear ), + mDopplerFactor( 0.5 ), + mRolloffFactor( 1.0 ), + mSoundscapeMgr( NULL ) +{ + VECTOR_SET_ASSOCIATION( mSounds ); + VECTOR_SET_ASSOCIATION( mPlayOnceSources ); + VECTOR_SET_ASSOCIATION( mPlugins ); + VECTOR_SET_ASSOCIATION( mListeners ); + + // Always at least one listener. + + mListeners.increment(); + + // Register stat variables. + + Con::addVariable( "SFX::numSources", TypeS32, &mStatNumSources, + "Number of SFXSource type objects that are currently instantiated.\n" + "@ingroup SFX" ); + Con::addVariable( "SFX::numSounds", TypeS32, &mStatNumSounds, + "Number of SFXSound type objects (i.e. actual single-file sounds) that are currently instantiated.\n" + "@ingroup SFX" ); + Con::addVariable( "SFX::numPlaying", TypeS32, &mStatNumPlaying, + "Number of SFXSources that are currently in playing state.\n" + "@ingroup SFX" ); + Con::addVariable( "SFX::numCulled", TypeS32, &mStatNumCulled, + "Number of SFXSounds that are currently in virtualized playback mode.\n" + "@ref SFXSound_virtualization\n\n" + "@ingroup SFX" ); + Con::addVariable( "SFX::numVoices", TypeS32, &mStatNumVoices, + "Number of voices that are currently allocated on the sound device.\n" + "@ingroup SFX" ); + Con::addVariable( "SFX::sourceUpdateTime", TypeS32, &mStatSourceUpdateTime, + "Milliseconds spent on the last SFXSource update loop.\n" + "@ref SFX_updating\n\n" + "@ingroup SFX" ); + Con::addVariable( "SFX::parameterUpdateTime", TypeS32, &mStatParameterUpdateTime, + "Milliseconds spent on the last SFXParameter update loop.\n" + "@ref SFX_updating\n\n" + "@ref SFX_interactive\n\n" + "@ingroup SFX" ); + Con::addVariable( "SFX::ambientUpdateTime", TypeS32, &mStatAmbientUpdateTime, + "Milliseconds spent on the last ambient audio update.\n" + "@ref SFX_updating\n\n" + "@ref SFX_ambient\n\n" + "@ingroup SFX" ); + + // Register constants. + + Con::addConstant( "$SFX::DEVICE_CAPS_REVERB", TypeS32, &sDeviceCapsReverb, + "Sound device capability flag indicating that the sound device supports reverb.\n\n" + "@note Currently only FMOD implements this.\n\n" + "@see sfxGetDeviceInfo\n\n" + "@ref SFX_reverb\n\n" + "@ingroup SFX" ); + Con::addConstant( "$SFX::DEVICE_CAPS_VOICEMANAGEMENT", TypeS32, &sDeviceCapsVoiceManagement, + "Sound device capability flag indicating that the sound device implements its own voice virtualization.\n\n" + "For these devices, the sound system will deactivate its own voice management and leave voice " + "virtualization entirely to the device.\n\n" + "@note Currently only FMOD implements this.\n\n" + "@see sfxGetDeviceInfo\n\n" + "@ref SFXSound_virtualization\n\n" + "@ingroup SFX" ); + Con::addConstant( "$SFX::DEVICE_CAPS_OCCLUSION", TypeS32, &sDeviceCapsOcclusion, + "Sound device capability flag indicating that the sound device implements sound occlusion.\n\n" + "@note This is not yet used by the sound system.\n\n" + "@see sfxGetDeviceInfo\n\n" + "@ingroup SFX" ); + Con::addConstant( "$SFX::DEVICE_CAPS_DSPEFFECTS", TypeS32, &sDeviceCapsDSPEffects, + "Sound device capability flag indicating that the sound device supports adding DSP effect chains to sounds.\n\n" + "@see sfxGetDeviceInfo\n\n" + "@note This is not yet used by the sound system.\n\n" + "@see sfxGetDeviceInfo\n\n" + "@ingroup SFX" ); + Con::addConstant( "$SFX::DEVICE_CAPS_MULTILISTENER", TypeS32, &sDeviceCapsMultiListener, + "Sound device capability flag indicating that the sound device supports multiple concurrent listeners.\n\n" + "@note Currently only FMOD implements this.\n\n" + "@see sfxGetDeviceInfo\n\n" + "@ingroup SFX" ); + Con::addConstant( "$SFX::DEVICE_CAPS_FMODDESIGNER", TypeS32, &sDeviceCapsFMODDesigner, + "Sound device capability flag indicating that the sound device supports FMOD Designer audio projects.\n\n" + "@note This is exclusive to FMOD. If the FMOD Event DLLs are in place and could be successfully loaded, this " + "flag will be set after initializating an FMOD audio device.\n\n" + "@see sfxGetDeviceInfo\n\n" + "@ref FMOD_designer\n\n" + "@ingroup SFX" ); + + Con::addConstant( "$SFX::DEVICE_INFO_PROVIDER", TypeS32, &sDeviceInfoProvider, + "Index of sound provider field in device info string.\n\n" + "@see sfxGetDeviceInfo\n\n" + "@see sfxGetAvailableDevices\n\n" + "@ingroup SFX" ); + Con::addConstant( "$SFX::DEVICE_INFO_NAME", TypeS32, &sDeviceInfoName, + "Index of device name field in device info string.\n\n" + "@see sfxGetDeviceInfo\n\n" + "@see sfxGetAvailableDevices\n\n" + "@ingroup SFX" ); + Con::addConstant( "$SFX::DEVICE_INFO_USEHARDWARE", TypeS32, &sDeviceInfoUseHardware, + "Index of use hardware flag in device info string.\n\n" + "@see sfxGetDeviceInfo\n\n" + "@see sfxGetAvailableDevices\n\n" + "@ingroup SFX" ); + Con::addConstant( "$SFX::DEVICE_INFO_MAXBUFFERS", TypeS32, &sDeviceInfoMaxBuffers, + "Index of buffer limit number in device info string.\n\n" + "@see sfxGetDeviceInfo\n\n" + "@see sfxGetAvailableDevices\n\n" + "@ingroup SFX" ); + Con::addConstant( "$SFX::DEVICE_INFO_CAPS", TypeS32, &sDeviceInfoMaxBuffers, + "Index of device capability flags in device info string.\n\n" + "@see sfxGetDeviceInfo\n\n" + "@see sfxGetAvailableDevices\n\n" + "@ingroup SFX" ); + + // Create subsystems. + + mSoundscapeMgr = new SFXSoundscapeManager(); +} + +//----------------------------------------------------------------------------- + +SFXSystem::~SFXSystem() +{ + // Unregister stat variables. + + Con::removeVariable( "SFX::numSources" ); + Con::removeVariable( "SFX::numSounds" ); + Con::removeVariable( "SFX::numPlaying" ); + Con::removeVariable( "SFX::numCulled" ); + Con::removeVariable( "SFX::numVoices" ); + Con::removeVariable( "SFX::sourceUpdateTime" ); + Con::removeVariable( "SFX::parameterUpdateTime" ); + Con::removeVariable( "SFX::ambientUpdateTime" ); + + // Cleanup any remaining sources. + + if( Sim::getSFXSourceSet() ) + Sim::getSFXSourceSet()->deleteAllObjects(); + + mSounds.clear(); + mPlayOnceSources.clear(); + mListeners.clear(); + + // Delete subsystems. + + if( mSoundscapeMgr ) + SAFE_DELETE( mSoundscapeMgr ); + + // Delete device if we still have one. + + deleteDevice(); +} + +//----------------------------------------------------------------------------- + +void SFXSystem::init() +{ + AssertWarn( smSingleton == NULL, "SFX has already been initialized!" ); + + SFXProvider::initializeAllProviders(); + + // Register the streams and resources. Note that + // the order here does matter! + SFXFileStream::registerExtension( ".wav", ( SFXFILESTREAM_CREATE_FN ) SFXWavStream::create ); + #ifdef TORQUE_OGGVORBIS + SFXFileStream::registerExtension( ".ogg", ( SFXFILESTREAM_CREATE_FN ) SFXVorbisStream::create ); + #endif + + // Create the stream thread pool. + + SFXInternal::SFXThreadPool::createSingleton(); + + // Note: If you have provider specific file types you should + // register them in the provider initialization. + + // Create the system. + smSingleton = new SFXSystem(); +} + +//----------------------------------------------------------------------------- + +void SFXSystem::destroy() +{ + AssertWarn( smSingleton != NULL, "SFX has not been initialized!" ); + + SFXFileStream::unregisterExtension( ".wav" ); + #ifdef TORQUE_OGGVORBIS + SFXFileStream::unregisterExtension( ".ogg" ); + #endif + + delete smSingleton; + smSingleton = NULL; + + // Destroy the stream thread pool + + SFXInternal::SFXThreadPool::deleteSingleton(); +} + +//----------------------------------------------------------------------------- + +void SFXSystem::addPlugin( SFXSystemPlugin* plugin ) +{ + for( U32 i = 0; i < mPlugins.size(); ++ i ) + AssertFatal( mPlugins[ i ] != plugin, "SFXSystem::addPlugin - plugin already added to the system!" ); + + mPlugins.push_back( plugin ); +} + +//----------------------------------------------------------------------------- + +void SFXSystem::removePlugin( SFXSystemPlugin* plugin ) +{ + for( U32 i = 0; i < mPlugins.size(); ++ i ) + if( mPlugins[ i ] == plugin ) + { + mPlugins.erase_fast( i ); + break; + } +} + +//----------------------------------------------------------------------------- + +bool SFXSystem::createDevice( const String& providerName, const String& deviceName, bool useHardware, S32 maxBuffers, bool changeDevice ) +{ + // Make sure we don't have a device already. + + if( mDevice && !changeDevice ) + return false; + + // Lookup the provider. + + SFXProvider* provider = SFXProvider::findProvider( providerName ); + if( !provider ) + return false; + + // If we have already created this device and are using it then no need to do anything. + + if( mDevice + && providerName.equal( mDevice->getProvider()->getName(), String::NoCase ) + && deviceName.equal( mDevice->getName(), String::NoCase ) + && useHardware == mDevice->getUseHardware() ) + return true; + + // If we have an existing device remove it. + + if( mDevice ) + deleteDevice(); + + // Create the new device.. + + mDevice = provider->createDevice( deviceName, useHardware, maxBuffers ); + if( !mDevice ) + { + Con::errorf( "SFXSystem::createDevice - failed creating %s device '%s'", providerName.c_str(), deviceName.c_str() ); + return false; + } + + // Print capabilities. + + Con::printf( "SFXSystem::createDevice - created %s device '%s'", providerName.c_str(), deviceName.c_str() ); + if( mDevice->getCaps() & SFXDevice::CAPS_Reverb ) + Con::printf( " CAPS_Reverb" ); + if( mDevice->getCaps() & SFXDevice::CAPS_VoiceManagement ) + Con::printf( " CAPS_VoiceManagement" ); + if( mDevice->getCaps() & SFXDevice::CAPS_Occlusion ) + Con::printf( "\tCAPS_Occlusion" ); + if( mDevice->getCaps() & SFXDevice::CAPS_MultiListener ) + Con::printf( "\tCAPS_MultiListener" ); + + // Set defaults. + + mDevice->setNumListeners( getNumListeners() ); + mDevice->setDistanceModel( mDistanceModel ); + mDevice->setDopplerFactor( mDopplerFactor ); + mDevice->setRolloffFactor( mRolloffFactor ); + mDevice->setReverb( mReverb ); + + // Signal system. + + getEventSignal().trigger( SFXSystemEvent_CreateDevice ); + + return true; +} + +//----------------------------------------------------------------------------- + +String SFXSystem::getDeviceInfoString() +{ + // Make sure we have a valid device. + if( !mDevice ) + return String(); + + return String::ToString( "%s\t%s\t%s\t%d\t%d", + mDevice->getProvider()->getName().c_str(), + mDevice->getName().c_str(), + mDevice->getUseHardware() ? "1" : "0", + mDevice->getMaxBuffers(), + mDevice->getCaps() ); +} + +//----------------------------------------------------------------------------- + +void SFXSystem::deleteDevice() +{ + // Make sure we have a valid device. + if ( !mDevice ) + return; + + // Put all playing sounds into virtualized playback mode. Where this fails, + // stop the source. + + for( U32 i = 0; i < mSounds.size(); ++ i ) + { + SFXSound* sound = mSounds[ i ]; + if( sound->hasVoice() && !sound->_releaseVoice() ) + sound->stop(); + } + + // Signal everyone who cares that the + // device is being deleted. + getEventSignal().trigger( SFXSystemEvent_DestroyDevice ); + + // Free the device which should delete all + // the active voices and buffers. + delete mDevice; + mDevice = NULL; +} + +//----------------------------------------------------------------------------- + +SFXSource* SFXSystem::createSource( SFXTrack* track, + const MatrixF* transform, + const VectorF* velocity ) +{ + if( !track ) + return NULL; + + SFXSource* source = NULL; + + // Try creating through a plugin first so that they + // always get the first shot and may override our + // logic here. + + for( U32 i = 0; !source && i < mPlugins.size(); ++ i ) + source = mPlugins[ i ]->createSource( track ); + + // If that failed, try our own logic using the track + // types that we know about. + + if( !source ) + { + if( !mDevice ) + { + Con::errorf( "SFXSystem::createSource() - no device initialized!" ); + return NULL; + } + + if( dynamic_cast< SFXPlayList* >( track ) ) + { + // Create a playback controller for the track. + + SFXPlayList* playList = static_cast< SFXPlayList* >( track ); + source = SFXController::_create( playList ); + } + else if( dynamic_cast< SFXProfile* >( track ) ) + { + // Create a sound. + + SFXProfile* profile = static_cast< SFXProfile* >( track ); + source = SFXSound::_create( mDevice, profile ); + if( !source ) + { + Con::errorf( + "SFXSystem::createSource() - Failed to create sound!\n" + " Profile: %s\n" + " Filename: %s", + profile->getName(), + profile->getSoundFileName().c_str() ); + } + } + else + { + Con::errorf( "SFXSystem::createSource - cannot create source for %i (%s) of type '%s'", + track->getId(), track->getName(), track->getClassName() ); + Con::errorf( "SFXSystem::createSource - maybe you are using the wrong SFX provider for this type of track" ); + return NULL; + } + } + + if( source ) + { + if( transform ) + source->setTransform( *transform ); + if( velocity ) + source->setVelocity( *velocity ); + } + + return source; +} + +//----------------------------------------------------------------------------- + +SFXSound* SFXSystem::createSourceFromStream( const ThreadSafeRef< SFXStream >& stream, + SFXDescription* description ) +{ + AssertFatal( mDevice, "SFXSystem::createSourceFromStream() - no device initialized!" ); + + // We sometimes get null values from script... fail in that case. + + if( !stream || !description ) + return NULL; + + // Create the sound. + + SFXSound* sound = SFXSound::_create( mDevice, stream, description ); + if( !sound ) + return NULL; + + return sound; +} + +//----------------------------------------------------------------------------- + +void SFXSystem::stopAndDeleteSource( SFXSource* source ) +{ + if( source->getFadeOutTime() > 0.f ) + { + // Fade-out. Stop and put on play-once list to + // ensure deletion when the source actually stops. + + source->stop(); + deleteWhenStopped( source ); + } + else + { + // No fade-out. Just stop and delete the source. + + source->stop(); + SFX_DELETE( source ); + } +} + +//----------------------------------------------------------------------------- + +void SFXSystem::deleteWhenStopped( SFXSource* source ) +{ + // If the source isn't already on the play-once source list, + // put it there now. + + Vector< SFXSource* >::iterator iter = find( mPlayOnceSources.begin(), mPlayOnceSources.end(), source ); + if( iter == mPlayOnceSources.end() ) + mPlayOnceSources.push_back( source ); +} + +//----------------------------------------------------------------------------- + +void SFXSystem::_onAddSource( SFXSource* source ) +{ + if( dynamic_cast< SFXSound* >( source ) ) + { + SFXSound* sound = static_cast< SFXSound* >( source ); + mSounds.push_back( sound ); + + mStatNumSounds = mSounds.size(); + } + + // Update the stats. + mStatNumSources ++; +} + +//----------------------------------------------------------------------------- + +void SFXSystem::_onRemoveSource( SFXSource* source ) +{ + // Check if it was a play once source. + + Vector< SFXSource* >::iterator iter = find( mPlayOnceSources.begin(), mPlayOnceSources.end(), source ); + if ( iter != mPlayOnceSources.end() ) + mPlayOnceSources.erase_fast( iter ); + + // Update the stats. + + mStatNumSources --; + + if( dynamic_cast< SFXSound* >( source ) ) + { + SFXSoundVector::iterator iter = find( mSounds.begin(), mSounds.end(), static_cast< SFXSound* >( source ) ); + mSounds.erase_fast( iter ); + + mStatNumSounds = mSounds.size(); + } +} + +//----------------------------------------------------------------------------- + +SFXBuffer* SFXSystem::_createBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ) +{ + // The buffers are created by the active + // device... without one we cannot do anything. + if ( !mDevice ) + { + Con::errorf( "SFXSystem::_createBuffer - No sound device initialized!!" ); + return NULL; + } + + // Some sanity checking for streaming. If the stream isn't at least three packets + // in size given the current settings in "description", then turn off streaming. + // The device code *will* mess up if the stream input fails to match certain metrics. + // Just disabling streaming when it doesn't make sense is easier than complicating the + // device logic to account for bad metrics. + + bool streamFlag = description->mIsStreaming; + if( description->mIsStreaming + && stream->getDuration() < description->mStreamPacketSize * 1000 * SFXInternal::SFXAsyncQueue::DEFAULT_STREAM_QUEUE_LENGTH ) + description->mIsStreaming = false; + + SFXBuffer* buffer = mDevice->createBuffer( stream, description ); + + description->mIsStreaming = streamFlag; // restore in case we stomped it + return buffer; +} + +//----------------------------------------------------------------------------- + +SFXBuffer* SFXSystem::_createBuffer( const String& filename, SFXDescription* description ) +{ + if( !mDevice ) + { + Con::errorf( "SFXSystem::_createBuffer - No sound device initialized!!" ); + return NULL; + } + + return mDevice->createBuffer( filename, description ); +} + +//----------------------------------------------------------------------------- + +SFXSource* SFXSystem::playOnce( SFXTrack* track, + const MatrixF *transform, + const VectorF *velocity, + F32 fadeInTime ) +{ + // We sometimes get null profiles... nothing to play without a profile! + if( !track ) + return NULL; + + SFXSource *source = createSource( track, transform, velocity ); + if( source ) + { + mPlayOnceSources.push_back( source ); + source->play( fadeInTime ); + } + + return source; +} + +//----------------------------------------------------------------------------- + +void SFXSystem::_update() +{ + PROFILE_SCOPE( SFXSystem_Update ); + + getEventSignal().trigger( SFXSystemEvent_Update ); + + for( U32 i = 0; i < mPlugins.size(); ++ i ) + mPlugins[ i ]->update(); + + const U32 SOURCE_UPDATE_MS = TickMs * 2; + const U32 PARAMETER_UPDATE_MS = TickMs * 3; + const U32 AMBIENT_UPDATE_MS = TickMs * 4; + + static AutoPtr< PlatformTimer > sTimer; + if( sTimer.isNull() ) + sTimer = PlatformTimer::create(); + + // The update of the sources can be a bit expensive + // and it does not need to be updated every frame. + const U32 currentTime = Platform::getRealMilliseconds(); + if( ( currentTime - mLastSourceUpdateTime ) >= SOURCE_UPDATE_MS ) + { + S32 tick = sTimer->getElapsedMs(); + + _updateSources(); + mLastSourceUpdateTime = currentTime; + + mStatSourceUpdateTime = ( sTimer->getElapsedMs() - tick ); + } + if( ( currentTime - mLastParameterUpdateTime ) >= PARAMETER_UPDATE_MS ) + { + S32 tick = sTimer->getElapsedMs(); + + SimSet* set = Sim::getSFXParameterGroup(); + for( SimSet::iterator iter = set->begin(); iter != set->end(); ++ iter ) + { + SFXParameter* parameter = dynamic_cast< SFXParameter* >( *iter ); + if( parameter ) + parameter->update(); + } + + mLastParameterUpdateTime = currentTime; + mStatParameterUpdateTime = ( sTimer->getElapsedMs() - tick ); + } + if( mSoundscapeMgr && ( currentTime - mLastAmbientUpdateTime ) >= AMBIENT_UPDATE_MS ) + { + S32 tick = sTimer->getElapsedMs(); + + mSoundscapeMgr->update(); + mLastAmbientUpdateTime = currentTime; + + mStatAmbientUpdateTime = ( sTimer->getElapsedMs() - tick ); + } + + // If we have a device then update it. + if( mDevice ) + mDevice->update(); +} + +//----------------------------------------------------------------------------- + +void SFXSystem::_updateSources() +{ + PROFILE_SCOPE( SFXSystem_UpdateSources ); + + SimSet* sources = Sim::getSFXSourceSet(); + if( !sources ) + return; + + // Check the status of the sources here once. + // + // NOTE: We do not use iterators in this loop because + // SFXControllers can add to the source list during the + // loop. + // + mStatNumPlaying = 0; + for( S32 i=0; i < sources->size(); i++ ) + { + SFXSource *source = dynamic_cast< SFXSource* >( sources->at( i ) ); + if ( source ) + { + source->update(); + if( source->getStatus() == SFXStatusPlaying ) + ++ mStatNumPlaying; + } + } + + // First check to see if any play once sources have + // finished playback... delete them. + + for( SFXSourceVector::iterator iter = mPlayOnceSources.begin(); iter != mPlayOnceSources.end(); ) + { + SFXSource* source = *iter; + + if( source->getLastStatus() == SFXStatusStopped && + source->getSavedStatus() != SFXStatusPlaying ) + { + int index = iter - mPlayOnceSources.begin(); + + // Erase it from the vector first, so that onRemoveSource + // doesn't do it during cleanup and screw up our loop here! + mPlayOnceSources.erase_fast( iter ); + source->deleteObject(); + + iter = mPlayOnceSources.begin() + index; + continue; + } + + ++ iter; + } + + + if( mDevice ) + { + // Reassign buffers to the sounds (if voices are managed by + // us instead of by the device). + + if( !( mDevice->getCaps() & SFXDevice::CAPS_VoiceManagement ) ) + _assignVoices(); + + // Update the voice count stat. + mStatNumVoices = mDevice->getVoiceCount(); + } +} + +//----------------------------------------------------------------------------- + +void SFXSystem::_sortSounds( const SFXListenerProperties& listener ) +{ + PROFILE_SCOPE( SFXSystem_SortSounds ); + + // Sort the source vector by the attenuated + // volume and priorities. This leaves us + // with the loudest and highest priority sounds + // at the front of the vector. + + dQsort( ( void* ) mSounds.address(), mSounds.size(), sizeof( SFXSound* ), SFXSound::qsortCompare ); +} + +//----------------------------------------------------------------------------- + +void SFXSystem::_assignVoices() +{ + AssertFatal( getNumListeners() == 1, "SFXSystem::_assignVoices() - must only have a single listener" ); + PROFILE_SCOPE( SFXSystem_AssignVoices ); + + mStatNumVoices = 0; + mStatNumCulled = 0; + + if( !mDevice ) + return; + + // Sort the sources in the SFX source set by priority. This also + // updates each soures effective volume first. + + _sortSounds( getListener() ); + + // We now make sure that the sources closest to the + // listener, the ones at the top of the source list, + // have a device buffer to play thru. + + mStatNumCulled = 0; + for( SFXSoundVector::iterator iter = mSounds.begin(); iter != mSounds.end(); ++ iter ) + { + SFXSound* sound = *iter; + + // Non playing sources (paused or stopped) are at the + // end of the vector, so when i encounter one i know + // that nothing else in the vector needs buffer assignment. + + if( !sound->isPlaying() ) + break; + + // If the source is outside it's max range we can + // skip it as well, so that we don't waste cycles + // setting up a buffer for something we won't hear. + + if( sound->getAttenuatedVolume() <= 0.0f ) + { + ++ mStatNumCulled; + continue; + } + + // If the source has a voice then we can skip it. + + if( sound->hasVoice() ) + continue; + + // Ok let the device try to assign a new voice for + // this source... this may fail if we're out of voices. + + if( sound->_allocVoice( mDevice ) ) + continue; + + // The device couldn't assign a new voice, so we go through + // local priority sounds and try to steal a voice. + + for( SFXSoundVector::iterator hijack = mSounds.end() - 1; hijack != iter; -- hijack ) + { + SFXSound* other = *hijack; + + if( other->hasVoice() ) + { + // If the sound is a suitable candidate, try to steal + // its voice. While the sound definitely is lower down the chain + // in the total priority ordering, we don't want to steal voices + // from sounds that are clearly audible as that results in noticable + // sound pops. + + if( ( other->getAttenuatedVolume() < 0.1 // Very quiet or maybe not even audible. + || !other->isPlaying() // Not playing so not audible anyways. + || other->getPosition() == 0 ) // Not yet started playing. + && other->_releaseVoice() ) + break; + } + } + + // Ok try to assign a voice once again! + + if( sound->_allocVoice( mDevice ) ) + continue; + + // If the source still doesn't have a buffer... well + // tough cookies. It just cannot be heard yet, maybe + // it can in the next update. + + mStatNumCulled ++; + } + + // Update the voice count stat. + mStatNumVoices = mDevice->getVoiceCount(); +} + +//----------------------------------------------------------------------------- + +void SFXSystem::_assignVoice( SFXSound* sound ) +{ + if( !mDevice ) + return; + + // Make sure all properties are up-to-date. + + sound->_update(); + + // If voices are managed by the device, just let the sound + // allocate a voice on it. Otherwise, do a voice allocation pass + // on all our active sounds. + + if( mDevice->getCaps() & SFXDevice::CAPS_VoiceManagement ) + sound->_allocVoice( mDevice ); + else + _assignVoices(); + + // Update the voice count stat. + mStatNumVoices = mDevice->getVoiceCount(); +} + +//----------------------------------------------------------------------------- + +void SFXSystem::setDistanceModel( SFXDistanceModel model ) +{ + const bool changed = ( model != mDistanceModel ); + + mDistanceModel = model; + if( mDevice && changed ) + mDevice->setDistanceModel( model ); +} + +//----------------------------------------------------------------------------- + +void SFXSystem::setDopplerFactor( F32 factor ) +{ + const bool changed = ( factor != mDopplerFactor ); + + mDopplerFactor = factor; + if( mDevice && changed ) + mDevice->setDopplerFactor( factor ); +} + +//----------------------------------------------------------------------------- + +void SFXSystem::setRolloffFactor( F32 factor ) +{ + const bool changed = ( factor != mRolloffFactor ); + + mRolloffFactor = factor; + if( mDevice && changed ) + mDevice->setRolloffFactor( factor ); +} + +//----------------------------------------------------------------------------- + +void SFXSystem::setReverb( const SFXReverbProperties& reverb ) +{ + mReverb = reverb; + + // Allow the plugins to adjust the reverb. + + for( U32 i = 0; i < mPlugins.size(); ++ i ) + mPlugins[ i ]->filterReverb( mReverb ); + + // Pass it on to the device. + + if( mDevice ) + mDevice->setReverb( mReverb ); +} + +//----------------------------------------------------------------------------- + +void SFXSystem::setNumListeners( U32 num ) +{ + // If we are set to a single listener, just accept this as + // we always support this no matter what. + + if( num == 1 ) + { + mListeners.setSize( 1 ); + if( mDevice ) + mDevice->setNumListeners( 1 ); + return; + } + + // If setting to multiple listeners, make sure that the device + // both supports multiple listeners and implements its own voice + // management (as our voice virtualization does not work with more + // than a single listener). + + if( !mDevice || !( mDevice->getCaps() & SFXDevice::CAPS_MultiListener ) + || !( mDevice->getCaps() & SFXDevice::CAPS_VoiceManagement ) ) + { + Con::errorf( "SFXSystem::setNumListeners() - multiple listeners not supported on current configuration" ); + return; + } + + mListeners.setSize( num ); + if( mDevice ) + mDevice->setNumListeners( num ); +} + +//----------------------------------------------------------------------------- + +void SFXSystem::setListener( U32 index, const MatrixF& transform, const Point3F& velocity ) +{ + if( index >= mListeners.size() ) + return; + + mListeners[ index ] = SFXListenerProperties( transform, velocity ); + + if( mDevice ) + mDevice->setListener( index, mListeners[ index ] ); +} + +//----------------------------------------------------------------------------- + +void SFXSystem::notifyDescriptionChanged( SFXDescription* description ) +{ + SimSet* set = Sim::getSFXSourceSet(); + for( SimSet::iterator iter = set->begin(); iter != set->end(); ++ iter ) + { + SFXSource* source = dynamic_cast< SFXSource* >( *iter ); + if( source && source->getDescription() == description ) + source->notifyDescriptionChanged(); + } +} + +//----------------------------------------------------------------------------- + +void SFXSystem::notifyTrackChanged( SFXTrack* track ) +{ + SimSet* set = Sim::getSFXSourceSet(); + for( SimSet::iterator iter = set->begin(); iter != set->end(); ++ iter ) + { + SFXSource* source = dynamic_cast< SFXSource* >( *iter ); + if( source && source->getTrack() == track ) + source->notifyTrackChanged(); + } +} + +//----------------------------------------------------------------------------- + +void SFXSystem::dumpSources( StringBuilder* toString, bool excludeGroups ) +{ + SimSet* sources = Sim::getSFXSourceSet(); + if( !sources ) + return; + + bool isFirst = true; + for( SimSet::iterator iter = sources->begin(); iter != sources->end(); ++ iter ) + { + SFXSource* source = dynamic_cast< SFXSource* >( *iter ); + if( !source ) + continue; + + bool isGroup = typeid( *source ) == typeid( SFXSource ); + if( isGroup && excludeGroups ) + continue; + + bool isPlayOnce = false; + for( U32 j = 0; j < mPlayOnceSources.size(); ++ j ) + if( mPlayOnceSources[ j ] == source ) + { + isPlayOnce = true; + break; + } + + SFXSource* sourceGroup = source->getSourceGroup(); + + SFXSound* sound = dynamic_cast< SFXSound* >( source ); + SFXController* controller = dynamic_cast< SFXController* >( source ); + + if( toString ) + toString->format( "%s%5i: type=%s, status=%s, blocked=%s, volume=%.2f, priority=%.2f, virtual=%s, looping=%s, 3d=%s, group=%s, playtime=%.2f, playOnce=%s, streaming=%s, hasVoice=%s, track=%s", + ( isFirst ? "" : "\n" ), + source->getId(), + ( isGroup ? "group" : sound ? "sound" : controller ? "list" : "other" ), + source->isPlaying() + ? "playing" + : source->isPaused() + ? "paused" + : source->isStopped() + ? "stopped" + : "unknown", + ( sound && sound->isBlocked() ? "1" : "0" ), + source->getAttenuatedVolume(), + source->getEffectivePriority(), + ( sound && sound->isVirtualized() ? "1" : "0" ), + ( sound && sound->isLooping() ) ? "1" : "0", + source->getDescription()->mIs3D ? "1" : "0", + sourceGroup ? sourceGroup->getName() : "", + source->getElapsedPlayTimeCurrentCycle(), + isPlayOnce ? "1" : "0", + ( sound && sound->isStreaming() ? "1" : "0" ), + ( sound && sound->hasVoice() ? "1" : "0" ), + source->getTrack() ? source->getTrack()->getName() : "" + ); + else + Con::printf( "%5i: type=%s, status=%s, blocked=%s, volume=%.2f, priority=%.2f, virtual=%s, looping=%s, 3d=%s, group=%s, playtime=%.2f, playOnce=%s, streaming=%s, hasVoice=%s, track=%s", + source->getId(), + ( isGroup ? "group" : sound ? "sound" : controller ? "list" : "other" ), + source->isPlaying() + ? "playing" + : source->isPaused() + ? "paused" + : source->isStopped() + ? "stopped" + : "unknown", + ( sound && sound->isBlocked() ? "1" : "0" ), + source->getAttenuatedVolume(), + source->getEffectivePriority(), + ( sound && sound->isVirtualized() ? "1" : "0" ), + ( sound && sound->isLooping() ) ? "1" : "0", + source->getDescription()->mIs3D ? "1" : "0", + sourceGroup ? sourceGroup->getName() : "", + source->getElapsedPlayTimeCurrentCycle(), + isPlayOnce ? "1" : "0", + ( sound && sound->isStreaming() ? "1" : "0" ), + ( sound && sound->hasVoice() ? "1" : "0" ), + source->getTrack() ? source->getTrack()->getName() : "" + ); + + isFirst = false; + } +} + +//============================================================================= +// Console Functions. +//============================================================================= +// MARK: ---- Console Functions ---- + +//----------------------------------------------------------------------------- + +DefineEngineFunction( sfxGetAvailableDevices, const char*, (),, + "Get a list of all available sound devices.\n" + "The return value will be a newline-separated list of entries where each line describes one available sound " + "device. Each such line will have the following format:" + "@verbatim\n" + "provider TAB device TAB hasHardware TAB numMaxBuffers\n" + "@endverbatim\n" + "- provider: The name of the device provider (e.g. \"FMOD\").\n" + "- device: The name of the device as returned by the device layer.\n" + "- hasHardware: Whether the device supports hardware mixing or not.\n" + "- numMaxBuffers: The maximum number of concurrent voices supported by the device's mixer. If this limit " + "limit is exceeded, i.e. if there are more active sounds playing at any one time, then voice virtualization " + "will start culling voices and put them into virtualized playback mode. Voice virtualization may or may not " + "be provided by the device itself; if not provided by the device, it will be provided by Torque's sound system.\n\n" + "@return A newline-separated list of information about all available sound devices.\n" + "@see sfxCreateDevice\n" + "@see sfxGetDeviceInfo\n\n" + "@see $SFX::DEVICE_INFO_PROVIDER\n\n" + "@see $SFX::DEVICE_INFO_NAME\n\n" + "@see $SFX::DEVICE_INFO_USEHARDWARE\n\n" + "@see $SFX::DEVICE_INFO_MAXBUFFERS\n\n" + "@ref SFX_devices\n" + "@ingroup SFX" ) +{ + char* deviceList = Con::getReturnBuffer( 2048 ); + deviceList[0] = 0; + + SFXProvider* provider = SFXProvider::getFirstProvider(); + while ( provider ) + { + // List the devices in this provider. + const SFXDeviceInfoVector& deviceInfo = provider->getDeviceInfo(); + for ( S32 d=0; d < deviceInfo.size(); d++ ) + { + const SFXDeviceInfo* info = deviceInfo[d]; + dStrcat( deviceList, provider->getName() ); + dStrcat( deviceList, "\t" ); + dStrcat( deviceList, info->name ); + dStrcat( deviceList, "\t" ); + dStrcat( deviceList, info->hasHardware ? "1" : "0" ); + dStrcat( deviceList, "\t" ); + dStrcat( deviceList, Con::getIntArg( info->maxBuffers ) ); + dStrcat( deviceList, "\n" ); + + //TODO: caps + } + + provider = provider->getNextProvider(); + } + + return deviceList; +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( sfxCreateDevice, bool, ( const char* provider, const char* device, bool useHardware, S32 maxBuffers ),, + "Try to create a new sound device using the given properties.\n" + "If a sound device is currently initialized, it will be uninitialized first. However, be aware that in this case, " + "if this function fails, it will not restore the previously active device but rather leave the sound system in an " + "uninitialized state.\n\n" + "Sounds that are already playing while the new device is created will be temporarily transitioned to virtualized " + "playback and then resume normal playback once the device has been created.\n\n" + "In the core scripts, sound is automatically set up during startup in the sfxStartup() function.\n\n" + "@param provider The name of the device provider as returned by sfxGetAvailableDevices().\n" + "@param device The name of the device as returned by sfxGetAvailableDevices().\n" + "@param useHardware Whether to enabled hardware mixing on the device or not. Only relevant if supported by the given device.\n" + "@param maxBuffers The maximum number of concurrent voices for this device to use or -1 for the device to pick its own reasonable default." + "@return True if the initialization was successful, false if not.\n" + "@note This function must be called before any of the sound playback functions can be used.\n" + "@see sfxGetAvailableDevices\n" + "@see sfxGetDeviceInfo\n" + "@see sfxDeleteDevice\n\n" + "@ref SFX_devices\n" + "@ingroup SFX" ) +{ + return SFX->createDevice( provider, device, useHardware, maxBuffers, true ); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( sfxDeleteDevice, void, (),, + "Delete the currently active sound device and release all its resources.\n" + "SFXSources that are still playing will be transitioned to virtualized playback mode. " + "When creating a new device, they will automatically transition back to normal playback.\n\n" + "In the core scripts, this is done automatically for you during shutdown in the sfxShutdown() function.\n\n" + "@see sfxCreateDevice\n\n" + "@ref SFX_devices\n" + "@ingroup SFX" ) +{ + SFX->deleteDevice(); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( sfxGetDeviceInfo, const char*, (),, + "Return information about the currently active sound device.\n" + "The return value is a tab-delimited string of the following format:\n" + "@verbatim\n" + "provider TAB device TAB hasHardware TAB numMaxBuffers TAB caps\n" + "@endverbatim\n" + "- provider: The name of the device provider (e.g. \"FMOD\").\n" + "- device: The name of the device as returned by the device layer.\n" + "- hasHardware: Whether the device supports hardware mixing or not.\n" + "- numMaxBuffers: The maximum number of concurrent voices supported by the device's mixer. If this limit " + "limit is exceeded, i.e. if there are more active sounds playing at any one time, then voice virtualization " + "will start culling voices and put them into virtualized playback mode. Voice virtualization may or may not " + "be provided by the device itself; if not provided by the device, it will be provided by Torque's sound system.\n" + "- caps: A bitfield of capability flags.\n\n" + "@return A tab-separated list of properties of the currently active sound device or the empty string if no sound device has been initialized.\n" + "@see sfxCreateDevice\n" + "@see sfxGetAvailableDevices\n\n" + "@see $SFX::DEVICE_INFO_PROVIDER\n\n" + "@see $SFX::DEVICE_INFO_NAME\n\n" + "@see $SFX::DEVICE_INFO_USEHARDWARE\n\n" + "@see $SFX::DEVICE_INFO_MAXBUFFERS\n\n" + "@see $SFX::DEVICE_INFO_CAPS\n\n" + "@see $SFX::DEVICE_CAPS_REVERB\n\n" + "@see $SFX::DEVICE_CAPS_VOICEMANAGEMENT\n\n" + "@see $SFX::DEVICE_CAPS_OCCLUSION\n\n" + "@see $SFX::DEVICE_CAPS_DSPEFFECTS\n\n" + "@see $SFX::DEVICE_CAPS_MULTILISTENER\n\n" + "@see $SFX::DEVICE_CAPS_FMODDESIGNER\n\n" + "@ref SFX_devices\n" + "@ingroup SFX" ) +{ + String deviceInfo = SFX->getDeviceInfoString(); + if( deviceInfo.isEmpty() ) + return ""; + + return Con::getReturnBuffer( deviceInfo ); +} + +//----------------------------------------------------------------------------- + +static ConsoleDocFragment _sfxCreateSource1( + "@brief Create a new source that plays the given track.\n\n" + "The source will be returned in stopped state. Call SFXSource::play() to start playback.\n\n" + "In contrast to play-once sources, the source object will not be automatically deleted once playback stops. " + "Call delete() to release the source object.\n\n" + "This function will automatically create the right SFXSource type for the given SFXTrack.\n\n" + "@param track The track the source should play.\n" + "@return A new SFXSource for playback of the given track or 0 if no source could be created from the given track.\n\n" + "@note Trying to create a source for a device-specific track type will fail if the currently selected device " + "does not support the type. Example: trying to create a source for an FMOD Designer event when not running FMOD.\n\n" + "@tsexample\n" + "// Create and play a source from a pre-existing profile:\n" + "%source = sfxCreateSource( SoundFileProfile );\n" + "%source.play();\n" + "@endtsexample\n\n" + "@ingroup SFX", + NULL, + "SFXSource sfxCreateSource( SFXTrack track );" ); +static ConsoleDocFragment _sfxCreateSource2( + "@brief Create a new source that plays the given track and position its 3D sounds source at the given coordinates (if it is a 3D sound).\n\n" + "The source will be returned in stopped state. Call SFXSource::play() to start playback.\n\n" + "In contrast to play-once sources, the source object will not be automatically deleted once playback stops. " + "Call delete() to release the source object.\n\n" + "This function will automatically create the right SFXSource type for the given SFXTrack.\n\n" + "@param track The track the source should play.\n" + "@param x The X coordinate of the 3D sound position.\n" + "@param y The Y coordinate of the 3D sound position.\n" + "@param z The Z coordinate of the 3D sound position.\n" + "@return A new SFXSource for playback of the given track or 0 if no source could be created from the given track.\n\n" + "@note Trying to create a source for a device-specific track type will fail if the currently selected device " + "does not support the type. Example: trying to create a source for an FMOD Designer event when not running FMOD.\n\n" + "@tsexample\n" + "// Create and play a source from a pre-existing profile and position it at (100, 200, 300):\n" + "%source = sfxCreateSource( SoundFileProfile, 100, 200, 300 );\n" + "%source.play();\n" + "@endtsexample\n\n" + "@ingroup SFX", + NULL, + "SFXSource sfxCreateSource( SFXTrack track, float x, float y, float z );" ); +static ConsoleDocFragment _sfxCreateSource3( + "@brief Create a temporary SFXProfile from the given @a description and @a filename and then create and return a new source that plays the profile.\n\n" + "The source will be returned in stopped state. Call SFXSource::play() to start playback.\n\n" + "In contrast to play-once sources, the source object will not be automatically deleted once playback stops. " + "Call delete() to release the source object.\n\n" + "@param description The description to use for setting up the temporary SFXProfile.\n" + "@param filename Path to the sound file to play.\n" + "@return A new SFXSource for playback of the given track or 0 if no source or no temporary profile could be created.\n\n" + "@tsexample\n" + "// Create a source for a music track:\n" + "%source = sfxCreateSource( AudioMusicLoop2D, \"art/sound/backgroundMusic\" );\n" + "%source.play();\n" + "@endtsexample\n\n" + "@see SFXProfile\n\n" + "@ingroup SFX", + NULL, + "SFXSound sfxCreateSource( SFXDescription description, string filename );" ); +static ConsoleDocFragment _sfxCreateSource4( + "@brief Create a temporary SFXProfile from the given @a description and @a filename and then create and return a new source that plays the profile. " + "Position the sound source at the given coordinates (if it is a 3D sound).\n\n" + "The source will be returned in stopped state. Call SFXSource::play() to start playback.\n\n" + "In contrast to play-once sources, the source object will not be automatically deleted once playback stops. " + "Call delete() to release the source object.\n\n" + "@param description The description to use for setting up the temporary SFXProfile.\n" + "@param filename Path to the sound file to play.\n" + "@param x The X coordinate of the 3D sound position.\n" + "@param y The Y coordinate of the 3D sound position.\n" + "@param z The Z coordinate of the 3D sound position.\n" + "@return A new SFXSource for playback of the given track or 0 if no source or no temporary profile could be created.\n\n" + "@tsexample\n" + "// Create a source for a music track and position it at (100, 200, 300):\n" + "%source = sfxCreateSource( AudioMusicLoop3D, \"art/sound/backgroundMusic\", 100, 200, 300 );\n" + "%source.play();\n" + "@endtsexample\n\n" + "@see SFXProfile\n\n" + "@ingroup SFX", + NULL, + "SFXSound sfxCreateSource( SFXDescription description, string filename, float x, float y, float z );" ); + +ConsoleFunction( sfxCreateSource, S32, 2, 6, + "( SFXTrack track | ( SFXDescription description, string filename ) [, float x, float y, float z ] ) " + "Creates a new paused sound source using a profile or a description " + "and filename. The return value is the source which must be " + "released by delete().\n" + "@hide" ) +{ + SFXDescription* description = NULL; + SFXTrack* track = dynamic_cast< SFXTrack* >( Sim::findObject( argv[1] ) ); + if ( !track ) + { + description = dynamic_cast< SFXDescription* >( Sim::findObject( argv[1] ) ); + if ( !description ) + { + Con::printf( "Unable to locate sound track/description '%s'", argv[1] ); + return 0; + } + } + + SFXSource* source = NULL; + + if ( track ) + { + if ( argc == 2 ) + { + source = SFX->createSource( track ); + } + else + { + MatrixF transform; + transform.set( EulerF(0,0,0), Point3F( dAtof(argv[2]), dAtof(argv[3]), dAtof(argv[4])) ); + source = SFX->createSource( track, &transform ); + } + } + else if ( description ) + { + SFXProfile* tempProfile = new SFXProfile( description, StringTable->insert( argv[2] ), true ); + if( !tempProfile->registerObject() ) + { + Con::errorf( "sfxCreateSource - unable to create profile" ); + delete tempProfile; + } + else + { + if ( argc == 3 ) + { + source = SFX->createSource( tempProfile ); + } + else + { + MatrixF transform; + transform.set(EulerF(0,0,0), Point3F( dAtof(argv[3]),dAtof(argv[4]),dAtof(argv[5]) )); + source = SFX->createSource( tempProfile, &transform ); + } + + tempProfile->setAutoDelete( true ); + } + } + + if ( source ) + return source->getId(); + + return 0; +} + +//----------------------------------------------------------------------------- + +static ConsoleDocFragment _sfxPlay1( + "@brief Start playback of the given source.\n\n" + "This is the same as calling SFXSource::play() directly.\n\n" + "@param source The source to start playing.\n\n" + "@return @a source.\n\n" + "@tsexample\n" + "// Create and play a source from a pre-existing profile:\n" + "%source = sfxCreateSource( SoundFileProfile );\n" + "%source.play();\n" + "@endtsexample\n\n" + "@ingroup SFX", + NULL, + "SFXSource sfxPlay( SFXSource source );" ); +static ConsoleDocFragment _sfxPlay2( + "@brief Create a new play-once source for the given @a track and start playback of the source.\n\n" + "This is equivalent to calling sfxCreateSource() on @track and SFXSource::play() on the resulting source.\n\n" + "@param track The sound datablock to play.\n\n" + "@return The newly created play-once source or 0 if the creation failed.\n\n" + "@ref SFXSource_playonce\n\n" + "@ingroup SFX", + NULL, + "void sfxPlay( SFXTrack track );" ); +static ConsoleDocFragment _sfxPlay3( + "@brief Create a new play-once source for the given @a track, position its 3D sound at the given coordinates (if the track's description " + "is set up for 3D sound) and start playback of the source.\n\n" + "This is equivalent to calling sfxCreateSource() on @track and SFXSource::play() on the resulting source.\n\n" + "@param track The sound datablock to play.\n\n" + "@param x The X coordinate of the 3D sound position.\n" + "@param y The Y coordinate of the 3D sound position.\n" + "@param z The Z coordinate of the 3D sound position.\n" + "@return The newly created play-once source or 0 if the creation failed.\n\n" + "@ref SFXSource_playonce\n\n" + "@ingroup SFX", + NULL, + "void sfxPlay( SFXTrack track, float x, float y, float z );" ); + +ConsoleFunction( sfxPlay, S32, 2, 5, "( SFXSource source | ( SFXTrack track [, float x, float y, float z ] ) ) " + "Start playing the given source or create a new source for the given track and play it.\n" + "@hide" ) +{ + if ( argc == 2 ) + { + SFXSource* source = dynamic_cast( Sim::findObject( argv[1] ) ); + if ( source ) + { + source->play(); + return source->getId(); + } + } + + SFXTrack* track = dynamic_cast( Sim::findObject( argv[1] ) ); + if ( !track ) + { + Con::printf( "Unable to locate sfx track '%s'", argv[1] ); + return 0; + } + + Point3F pos(0.f, 0.f, 0.f); + if ( argc == 3 ) + dSscanf( argv[2], "%g %g %g", &pos.x, &pos.y, &pos.z ); + else if(argc == 5) + pos.set( dAtof(argv[2]), dAtof(argv[3]), dAtof(argv[4]) ); + + MatrixF transform; + transform.set( EulerF(0,0,0), pos ); + + SFXSource* source = SFX->playOnce( track, &transform ); + if ( source ) + return source->getId(); + + return 0; +} + +//----------------------------------------------------------------------------- + +static ConsoleDocFragment _sPlayOnce1( + "@brief Create a play-once source for the given @a track.\n\n" + "Once playback has finished, the source will be automatically deleted in the next sound system update.\n" + "@param track The sound datablock.\n" + "@return A newly created temporary source in \"Playing\" state or 0 if the operation failed.\n\n" + "@ref SFXSource_playonce\n\n" + "@ingroup SFX", + NULL, + "SFXSource sfxPlayOnce( SFXTrack track );" +); +static ConsoleDocFragment _sPlayOnce2( + "@brief Create a play-once source for the given given @a track and position the source's 3D sound at the given coordinates " + "only if the track's description is set up for 3D sound).\n\n" + "Once playback has finished, the source will be automatically deleted in the next sound system update.\n" + "@param track The sound datablock.\n" + "@param x The X coordinate of the 3D sound position.\n" + "@param y The Y coordinate of the 3D sound position.\n" + "@param z The Z coordinate of the 3D sound position.\n" + "@param fadeInTime If >=0, this overrides the SFXDescription::fadeInTime value on the track's description.\n" + "@return A newly created temporary source in \"Playing\" state or 0 if the operation failed.\n\n" + "@tsexample\n" + "// Immediately start playing the given track. Fade it in to full volume over 5 seconds.\n" + "sfxPlayOnce( MusicTrack, 0, 0, 0, 5.f );\n" + "@endtsexample\n\n" + "@ref SFXSource_playonce\n\n" + "@ingroup SFX", + NULL, + "SFXSource sfxPlayOnce( SFXTrack track, float x, float y, float z, float fadeInTime=-1 );" +); +static ConsoleDocFragment _sPlayOnce3( + "@brief Create a new temporary SFXProfile from the given @a description and @a filename, then create a play-once source " + "for it and start playback.\n\n" + "Once playback has finished, the source will be automatically deleted in the next sound system update. If not referenced " + "otherwise by then, the temporary SFXProfile will also be deleted.\n" + "@param description The description to use for playback.\n" + "@param filename Path to the sound file to play.\n" + "@return A newly created temporary source in \"Playing\" state or 0 if the operation failed.\n\n" + "@tsexample\n" + "// Play a sound effect file once.\n" + "sfxPlayOnce( AudioEffects, \"art/sound/weapons/Weapon_pickup\" );\n" + "@endtsexample\n\n" + "@ref SFXSource_playonce\n\n" + "@ingroup SFX", + NULL, + "SFXSource sfxPlayOnce( SFXDescription description, string filename );" +); +static ConsoleDocFragment _sPlayOnce4( + "@brief Create a new temporary SFXProfile from the given @a description and @a filename, then create a play-once source " + "for it and start playback. Position the source's 3D sound at the given coordinates (only if the description " + "is set up for 3D sound).\n\n" + "Once playback has finished, the source will be automatically deleted in the next sound system update. If not referenced " + "otherwise by then, the temporary SFXProfile will also be deleted.\n" + "@param description The description to use for playback.\n" + "@param filename Path to the sound file to play.\n" + "@param x The X coordinate of the 3D sound position.\n" + "@param y The Y coordinate of the 3D sound position.\n" + "@param z The Z coordinate of the 3D sound position.\n" + "@param fadeInTime If >=0, this overrides the SFXDescription::fadeInTime value on the track's description.\n" + "@return A newly created temporary source in \"Playing\" state or 0 if the operation failed.\n\n" + "@tsexample\n" + "// Play a sound effect file once using a 3D sound with a default falloff placed at the origin.\n" + "sfxPlayOnce( AudioDefault3D, \"art/sound/weapons/Weapon_pickup\", 0, 0, 0 );\n" + "@endtsexample\n\n" + "@ref SFXSource_playonce\n\n" + "@ingroup SFX", + NULL, + "SFXSource sfxPlayOnce( SFXDescription description, string filename, float x, float y, float z, float fadeInTime=-1 );" +); + +ConsoleFunction( sfxPlayOnce, S32, 2, 6, + "SFXSource sfxPlayOnce( ( SFXTrack track | SFXDescription description, string filename ) [, float x, float y, float z, float fadeInTime=-1 ] ) " + "Create a new play-once source for the given profile or description+filename and start playback of the source.\n" + "@hide" ) +{ + SFXDescription* description = NULL; + SFXTrack* track = dynamic_cast< SFXTrack* >( Sim::findObject( argv[1] ) ); + if( !track ) + { + description = dynamic_cast< SFXDescription* >( Sim::findObject( argv[1] ) ); + if( !description ) + { + Con::errorf( "sfxPlayOnce - Unable to locate sound track/description '%s'", argv[1] ); + return 0; + } + } + + SFXSource* source = NULL; + if( track ) + { + if( argc == 2 ) + source = SFX->playOnce( track ); + else + { + MatrixF transform; + transform.set( EulerF( 0, 0, 0 ), Point3F( dAtof( argv[ 2 ] ), dAtof( argv[ 3 ] ),dAtof( argv[ 4 ] ) ) ); + F32 fadeInTime = -1.f; + if( argc > 5 ) + fadeInTime = dAtof( argv[ 5 ] ); + source = SFX->playOnce( track, &transform, NULL, fadeInTime ); + } + } + else if( description ) + { + SFXProfile* tempProfile = new SFXProfile( description, StringTable->insert( argv[2] ), true ); + if( !tempProfile->registerObject() ) + { + Con::errorf( "sfxPlayOnce - unable to create profile" ); + delete tempProfile; + } + else + { + if ( argc == 3 ) + source = SFX->playOnce( tempProfile ); + else + { + MatrixF transform; + transform.set(EulerF(0,0,0), Point3F( dAtof(argv[3]),dAtof(argv[4]),dAtof(argv[5]) )); + F32 fadeInTime = -1.f; + if( argc > 6 ) + fadeInTime = dAtof( argv[ 6 ] ); + source = SFX->playOnce( tempProfile, &transform, NULL, fadeInTime ); + } + + // Set profile to auto-delete when SFXSource releases its reference. + // Also add to root group so the profile will get deleted when the + // Sim system is shut down before the SFXSource has played out. + + tempProfile->setAutoDelete( true ); + Sim::getRootGroup()->addObject( tempProfile ); + } + } + + if( !source ) + return 0; + + return source->getId(); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( sfxStop, void, ( SFXSource* source ),, + "Stop playback of the given @a source.\n" + "This is equivalent to calling SFXSource::stop().\n\n" + "@param source The source to put into stopped state.\n\n" + "@ingroup SFX" ) +{ + if( source ) + source->stop(); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( sfxStopAndDelete, void, ( SFXSource* source ),, + "Stop playback of the given @a source (if it is not already stopped) and delete the @a source.\n\n" + "The advantage of this function over directly calling delete() is that it will correctly " + "handle volume fades that may be configured on the source. Whereas calling delete() would immediately " + "stop playback and delete the source, this functionality will wait for the fade-out to play and only then " + "stop the source and delete it.\n\n" + "@param source A sound source.\n\n" + "@ref SFXSource_fades\n\n" + "@ingroup SFX" ) +{ + if( source ) + SFX->stopAndDeleteSource( source ); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( sfxDeleteWhenStopped, void, ( SFXSource* source ),, + "Mark the given @a source for deletion as soon as it moves into stopped state.\n\n" + "This function will retroactively turn the given @a source into a play-once source (see @ref SFXSource_playonce).\n\n" + "@param source A sound source.\n\n" + "@ingroup SFX" ) +{ + if( source ) + SFX->deleteWhenStopped( source ); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( sfxGetDistanceModel, SFXDistanceModel, (),, + "Get the falloff curve type currently being applied to 3D sounds.\n\n" + "@return The current distance model type.\n\n" + "@ref SFXSource_volume\n\n" + "@ref SFX_3d\n\n" + "@ingroup SFX" ) +{ + return SFX->getDistanceModel(); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( sfxSetDistanceModel, void, ( SFXDistanceModel model ),, + "Set the falloff curve type to use for distance-based volume attenuation of 3D sounds.\n\n" + "@param model The distance model to use for 3D sound.\n\n" + "@note This setting takes effect globally and is applied to all 3D sounds.\n\n" + "@ingroup SFX" ) +{ + SFX->setDistanceModel( model ); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( sfxGetDopplerFactor, F32, (),, + "Get the current global doppler effect setting.\n\n" + "@return The current global doppler effect scale factor (>=0).\n\n" + "@see sfxSetDopplerFactor\n\n" + "@ref SFXSource_doppler\n\n" + "@ingroup SFX" ) +{ + return SFX->getDopplerFactor(); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( sfxSetDopplerFactor, void, ( F32 value ),, + "Set the global doppler effect scale factor.\n" + "@param value The new doppler shift scale factor.\n" + "@pre @a value must be >= 0.\n" + "@see sfxGetDopplerFactor\n\n" + "@ref SFXSource_doppler\n\n" + "@ingroup SFX" ) +{ + if( value < 0.0f ) + { + Con::errorf( "sfxSetDopplerFactor - factor must be >0" ); + return; + } + + SFX->setDopplerFactor( value ); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( sfxGetRolloffFactor, F32, (),, + "Get the current global scale factor applied to volume attenuation of 3D sounds in the logarithmic model.\n" + "@return The current scale factor for logarithmic 3D sound falloff curves.\n\n" + "@see sfxGetDistanceModel\n" + "@see SFXDistanceModel\n\n" + "@ref SFXSource_volume\n" + "@ref SFX_3d\n" + "@ingroup SFX" ) +{ + return SFX->getRolloffFactor(); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( sfxSetRolloffFactor, void, ( F32 value ),, + "Set the global scale factor to apply to volume attenuation of 3D sounds in the logarithmic model.\n" + "@param value The new scale factor for logarithmic 3D sound falloff curves.\n\n" + "@pre @a value must be > 0.\n" + "@note This function has no effect if the currently distance model is set to SFXDistanceModel::Linear.\n\n" + "@see sfxGetDistanceModel\n" + "@see SFXDistanceModel\n\n" + "@ref SFXSource_volume\n" + "@ref SFX_3d\n" + "@ingroup SFX" ) +{ + if( value <= 0.0f ) + { + Con::errorf( "sfxSetRolloffFactor - factor must be >0" ); + return; + } + + SFX->setRolloffFactor( value ); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( sfxDumpSources, void, ( bool includeGroups ), ( false ), + "Dump information about all current SFXSource instances to the console.\n" + "The dump includes information about the playback status for each source, volume levels, virtualization, etc.\n" + "@param includeGroups If true, direct instances of SFXSources (which represent logical sound groups) will be included. " + "Otherwise only instances of subclasses of SFXSources are included in the dump.\n" + "@see SFXSource\n" + "@see sfxDumpSourcesToString\n" + "@ingroup SFX" ) +{ + SFX->dumpSources( NULL, !includeGroups ); +} + +//----------------------------------------------------------------------------- + +DefineEngineFunction( sfxDumpSourcesToString, const char*, ( bool includeGroups ), ( false ), + "Dump information about all current SFXSource instances to a string.\n" + "The dump includes information about the playback status for each source, volume levels, virtualization, etc.\n" + "@param includeGroups If true, direct instances of SFXSources (which represent logical sound groups) will be included. " + "Otherwise only instances of subclasses of SFXSources are included in the dump.\n" + "@return A string containing a dump of information about all currently instantiated SFXSources.\n" + "@see SFXSource\n" + "@see sfxDumpSources\n" + "@ingroup SFX" ) +{ + StringBuilder str; + SFX->dumpSources( &str, !includeGroups ); + + return Con::getReturnBuffer( str ); +} diff --git a/Engine/source/sfx/sfxSystem.h b/Engine/source/sfx/sfxSystem.h new file mode 100644 index 000000000..7ac0b6f24 --- /dev/null +++ b/Engine/source/sfx/sfxSystem.h @@ -0,0 +1,450 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXSYSTEM_H_ +#define _SFXSYSTEM_H_ + +#ifndef _SFXCOMMON_H_ + #include "sfx/sfxCommon.h" +#endif +#ifndef _TSIGNAL_H_ + #include "core/util/tSignal.h" +#endif +#ifndef _TVECTOR_H_ + #include "core/util/tVector.h" +#endif +#ifndef _THREADSAFEREFCOUNT_H_ + #include "platform/threads/threadSafeRefCount.h" +#endif + + +class SFXTrack; +class SFXDevice; +class SFXProfile; +class SFXStream; +class SFXAmbience; +class SFXSoundscapeManager; +class SFXSource; +class SFXSound; +class SFXBuffer; +class SFXDescription; + + +/// SFX system events that can be received notifications on. +enum SFXSystemEventType +{ + /// SFX is being updated. + SFXSystemEvent_Update, + + /// New SFXDevice has been created. + SFXSystemEvent_CreateDevice, + + /// SFXDevice is about to be destroyed. + SFXSystemEvent_DestroyDevice, +}; + +/// SFXSystemPlugins are used to allow other subsystems hook into core functionality +/// of the sound system. +class SFXSystemPlugin +{ + public: + + /// + virtual void update() {} + + /// + virtual SFXSource* createSource( SFXTrack* track ) { return NULL; } + + /// Filter the given reverb setup before it is set up on the device. This + /// allows to, for example, modify the current reverb depending on listener + /// location. + virtual void filterReverb( SFXReverbProperties& reverb ) {} +}; + + +/// This class provides access to the sound system. +/// +/// There are a few script preferences that are used by +/// the sound providers. +/// +/// $pref::SFX::frequency - This is the playback frequency +/// for the primary sound buffer used for mixing. Although +/// most providers will reformat on the fly, for best quality +/// and performance match your sound files to this setting. +/// +/// $pref::SFX::bitrate - This is the playback bitrate for +/// the primary sound buffer used for mixing. Although most +/// providers will reformat on the fly, for best quality +/// and performance match your sound files to this setting. +/// +class SFXSystem +{ + friend class SFXSound; // _assignVoices + friend class SFXSource; // _onAddSource, _onRemoveSource. + friend class SFXProfile; // _createBuffer. + + public: + + typedef Signal< void( SFXSystemEventType event ) > EventSignalType; + typedef Vector< SFXSource* > SFXSourceVector; + typedef Vector< SFXSound* > SFXSoundVector; + + protected: + + /// The one and only instance of the SFXSystem. + static SFXSystem* smSingleton; + + /// The protected constructor. + /// + /// @see SFXSystem::init() + /// + SFXSystem(); + + /// The non-virtual destructor. You shouldn't + /// ever need to overload this class. + ~SFXSystem(); + + /// The current output sound device initialized + /// and ready to play back. + SFXDevice* mDevice; + + /// + SFXSoundVector mSounds; + + /// This is used to keep track of play once sources + /// that must be released when they stop playing. + SFXSourceVector mPlayOnceSources; + + /// The last time the sources got an update. + U32 mLastSourceUpdateTime; + + /// + U32 mLastAmbientUpdateTime; + + /// + U32 mLastParameterUpdateTime; + + /// The distance model used for rolloff curve computation on 3D sounds. + SFXDistanceModel mDistanceModel; + + /// The current doppler scale factor. + F32 mDopplerFactor; + + /// The current curve rolloff factor. + F32 mRolloffFactor; + + /// The current position and orientation of all listeners. + Vector< SFXListenerProperties > mListeners; + + /// Current global reverb properties. + SFXReverbProperties mReverb; + + /// SFX system event signal. + EventSignalType mEventSignal; + + /// Ambient soundscape manager. + SFXSoundscapeManager* mSoundscapeMgr; + + /// List of plugins currently linked to the SFX system. + Vector< SFXSystemPlugin* > mPlugins; + + /// @name Stats + /// + /// Stats reported back to the console for tracking performance. + /// + /// @{ + + S32 mStatNumSources; + S32 mStatNumSounds; + S32 mStatNumPlaying; + S32 mStatNumCulled; + S32 mStatNumVoices; + S32 mStatSourceUpdateTime; + S32 mStatParameterUpdateTime; + S32 mStatAmbientUpdateTime; + + /// @} + + /// Called to reprioritize and reassign buffers as + /// sources change state, volumes are adjusted, and + /// the listener moves around. + /// + /// @see SFXSource::_update() + /// + void _updateSources(); + + /// This called to reprioritize and reassign + /// voices to sources. + void _assignVoices(); + + /// + void _assignVoice( SFXSound* sound ); + + /// + void _sortSounds( const SFXListenerProperties& listener ); + + /// Called from SFXSource::onAdd to register the source. + void _onAddSource( SFXSource* source ); + + /// Called from SFXSource::onRemove to unregister the source. + void _onRemoveSource( SFXSource* source ); + + /// Called from SFXProfile to create a device specific + /// sound buffer used in conjunction with a voice in playback. + SFXBuffer* _createBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ); + + /// Load file directly through SFXDevice. Depends on + /// availability with selected SFXDevice. + /// + /// @return Return new buffer or NULL. + SFXBuffer* _createBuffer( const String& filename, SFXDescription* description ); + + /// + SFXDevice* _getDevice() const { return mDevice; } + + public: + + /// Returns the one an only instance of the SFXSystem + /// unless it hasn't been initialized or its been disabled + /// in your build. + /// + /// For convienence you can use the SFX-> macro as well. + /// + /// @see SFXSystem::init() + /// @see SFX + static SFXSystem* getSingleton() { return smSingleton; } + + /// This is called from initialization to prepare the + /// sound system singleton. This also includes registering + /// common resource types and initializing available sound + /// providers. + static void init(); + + /// This is called after Sim::shutdown() in shutdownLibraries() + /// to free the sound system singlton. After this the SFX + /// singleton is null and any call to it will crash. + static void destroy(); + + /// This is only public so that it can be called by + /// the game update loop. It updates the current + /// device and all sources. + void _update(); + + /// Register the given plugin with the system. + void addPlugin( SFXSystemPlugin* plugin ); + + /// Unregister the given plugin with the system. + void removePlugin( SFXSystemPlugin* plugin ); + + /// @name Device Management + /// @{ + + /// This initializes a new device. + /// + /// @param providerName The name of the provider. + /// @param deviceName The name of the provider device. + /// @param useHardware Toggles the use of hardware processing when available. + /// @param maxBuffers The maximum buffers for this device to use or -1 + /// for the device to pick its own reasonable default. + /// @param changeDevice Allows this to change the current device to a new one + /// @return Returns true if the device was created. + bool createDevice( const String& providerName, + const String& deviceName, + bool useHardware, + S32 maxBuffers, + bool changeDevice = false); + + /// Returns the current device information or NULL if no + /// device is present. The information string is in the + /// following format: + /// + /// Provider Name\tDevice Name\tUse Hardware\tMax Buffers + String getDeviceInfoString(); + + /// This destroys the current device. All sources loose their + /// playback buffers, but otherwise continue to function. + void deleteDevice(); + + /// Returns true if a device is allocated. + bool hasDevice() const { return mDevice != NULL; } + + /// @} + + /// @name Source Creation + /// @{ + + /// Used to create new sound sources from a sound profile. The + /// returned source is in a stopped state and ready for playback. + /// Use the SFX_DELETE macro to free the source when your done. + /// + /// @note The track must have at least the same lifetime as the + /// source. If the description disappears while the source is still + /// there, the source will go with it. + /// + /// @param profile The sound profile for the created source. + /// @param transform The optional transform if creating a 3D source. + /// @param velocity The optional doppler velocity if creating a 3D source. + /// + /// @return The sound source or NULL if an error occured. + SFXSource* createSource( SFXTrack* track, + const MatrixF* transform = NULL, + const VectorF* velocity = NULL ); + + /// Used to create a streaming sound source from a user supplied + /// stream object. + /// + /// It is only intended for memory based streams. For sound file + /// streaming use createSource() with a streaming SFXProfile. + /// + /// Use the SFX_DELETE macro to free the source when your done. + /// + /// @note The description must have at least the same lifetime as the + /// sound. If the description disappears while the source is still + /// there, the sound will go with it. + /// + /// @param stream The stream used to create the sound buffer. It + /// must exist for the lifetime of the source and will + /// have its reference count decremented when the source + /// is destroyed. + /// + /// @param description The sound description to apply to the source. + /// + /// @return The sound source or NULL if an error occured. + SFXSound* createSourceFromStream( const ThreadSafeRef< SFXStream >& stream, + SFXDescription* description ); + + /// Creates a source which when it finishes playing will auto delete + /// itself. Be aware that the returned SFXSource pointer should only + /// be used for error checking or immediate setting changes. It may + /// be deleted as soon as the next system tick. + /// + /// @param profile The sound profile for the created source. + /// @param transform The optional transform if creating a 3D source. + /// @param velocity The optional doppler velocity if creating a 3D source. + /// + /// @return The sound source or NULL if an error occured. + SFXSource* playOnce( SFXTrack* track, + const MatrixF* transform = NULL, + const VectorF* velocity = NULL, + F32 fadeInTime = -1.f ); + SFXSource* playOnce( SFXProfile* profile, + const MatrixF* transform = NULL, + const VectorF* velocity = NULL, + F32 fadeInTime = -1.f ) + { // Avoids having to require inclusion of sfxProfile.h + return playOnce( ( SFXTrack* ) profile, transform, velocity, fadeInTime ); + } + + /// Stop the source and delete it. This method will take care of + /// the fade-out time that the source may need before it will actually + /// stop and may be deleted. + void stopAndDeleteSource( SFXSource* source ); + + /// Mark source for deletion when it is moving into stopped state. + /// This method is useful to basically make a source a play-once source + /// after the fact. + void deleteWhenStopped( SFXSource* source ); + + /// @} + + /// @} + + /// @name Listeners + /// @{ + + /// Return the number of listeners currently configured. + U32 getNumListeners() const { return mListeners.size(); } + + /// Set the number of concurrent listeners. + /// @note It depends on the selected device if more than one listener is actually supported. + void setNumListeners( U32 num ); + + /// Set the property of the given listener. + const SFXListenerProperties& getListener( U32 index = 0 ) const { return mListeners[ index ]; } + + /// Set the 3D attributes of the given listener. + void setListener( U32 index, const MatrixF& transform, const Point3F& velocity ); + void setListener( U32 index, const SFXListenerProperties& properties ) + { + setListener( index, properties.getTransform(), properties.getVelocity() ); + } + + /// @} + + /// @name 3D Sound Configuration + /// { + + /// Return the curve model currently used distance attenuation of positional sounds. + SFXDistanceModel getDistanceModel() const { return mDistanceModel; } + + /// + void setDistanceModel( SFXDistanceModel model ); + + /// + F32 getDopplerFactor() const { return mDopplerFactor; } + + /// + void setDopplerFactor( F32 factor ); + + /// + F32 getRolloffFactor() const { return mRolloffFactor; } + + /// + void setRolloffFactor( F32 factor ); + + /// + const SFXReverbProperties& getReverb() const { return mReverb; } + + /// + void setReverb( const SFXReverbProperties& reverb ); + + /// @} + + /// + SFXSoundscapeManager* getSoundscapeManager() const { return mSoundscapeMgr; } + + /// Dump information about all current SFXSources to the console or + /// to the given StringBuilder. + void dumpSources( StringBuilder* toString = NULL, bool excludeGroups = true ); + + /// Return the SFX system event signal. + EventSignalType& getEventSignal() { return mEventSignal; } + + /// Notify the SFX system that the given description has changed. + /// All sources currently using the description will be updated. + void notifyDescriptionChanged( SFXDescription* description); + + /// + void notifyTrackChanged( SFXTrack* track ); +}; + + +/// Less verbose macro for accessing the SFX singleton. This +/// should be the prefered method for accessing the system. +/// +/// @see SFXSystem +/// @see SFXSystem::getSingleton() +/// +#define SFX SFXSystem::getSingleton() + + +#endif // _SFXSYSTEM_H_ diff --git a/Engine/source/sfx/sfxTrack.cpp b/Engine/source/sfx/sfxTrack.cpp new file mode 100644 index 000000000..2461b39b7 --- /dev/null +++ b/Engine/source/sfx/sfxTrack.cpp @@ -0,0 +1,188 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/sfxTrack.h" +#include "sfx/sfxTypes.h" +#include "sfx/sfxDescription.h" +#include "sfx/sfxSystem.h" +#include "core/stream/bitStream.h" + + +IMPLEMENT_CO_DATABLOCK_V1( SFXTrack ); + + +ConsoleDocClass( SFXTrack, + "@brief Abstract base class for sound data that can be played back by the sound system.\n\n" + + "The term \"track\" is used in the sound system to refer to any entity that can be played " + "back as a sound source. These can be individual files (SFXProfile), patterns of other tracks " + "(SFXPlayList), or special sound data defined by a device layer (SFXFMODEvent).\n\n" + + "Any track must be paired with a SFXDescription that tells the sound system how to set up " + "playback for the track.\n\n" + + "All objects that are of type SFXTrack will automatically be added to @c SFXTrackSet.\n\n" + + "@note This class cannot be instantiated directly.\n\n" + + "@ingroup SFX\n" + "@ingroup Datablocks\n" +); + + +//----------------------------------------------------------------------------- + +SFXTrack::SFXTrack() + : mDescription( NULL ) +{ + dMemset( mParameters, 0, sizeof( mParameters ) ); +} + +//----------------------------------------------------------------------------- + +SFXTrack::SFXTrack( SFXDescription* description ) + : mDescription( description ) +{ + dMemset( mParameters, 0, sizeof( mParameters ) ); +} + +//----------------------------------------------------------------------------- + +void SFXTrack::initPersistFields() +{ + addGroup( "Sound" ); + + addField( "description", TypeSFXDescriptionName, Offset( mDescription, SFXTrack ), + "Playback setup description for this track.\n\n" + "If unassigned, the description named \"AudioEffects\" will automatically be assigned to the track. If this description " + "is not defined, track creation will fail." ); + addField( "parameters", TypeSFXParameterName, Offset( mParameters, SFXTrack ), MaxNumParameters, + "Parameters to automatically attach to SFXSources created from this track.\n" + "Individual parameters are identified by their #internalName." ); + + endGroup( "Sound" ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- + +bool SFXTrack::processArguments( S32 argc, const char **argv ) +{ + if( typeid( *this ) == typeid( SFXTrack ) ) + { + Con::errorf( ConsoleLogEntry::Script, "SFXTrack is an abstract base class that cannot be instantiated directly!" ); + return false; + } + + return Parent::processArguments( argc, argv ); +} + +//----------------------------------------------------------------------------- + +void SFXTrack::setParameter( U32 index, const char* name ) +{ + AssertFatal( index < MaxNumParameters, "SFXTrack::setParameter() - index out of range" ); + mParameters[ index ] = StringTable->insert( name ); +} + +//----------------------------------------------------------------------------- + +void SFXTrack::packData( BitStream* stream ) +{ + Parent::packData( stream ); + + sfxWrite( stream, mDescription ); + + for( U32 i = 0; i < MaxNumParameters; ++ i ) + if( stream->writeFlag( mParameters[ i ] ) ) + stream->writeString( mParameters[ i ] ); +} + +//----------------------------------------------------------------------------- + +void SFXTrack::unpackData( BitStream* stream ) +{ + Parent::unpackData( stream ); + + sfxRead( stream, &mDescription ); + + for( U32 i = 0; i < MaxNumParameters; ++ i ) + if( stream->readFlag() ) + mParameters[ i ] = stream->readSTString(); + else + mParameters[ i ] = NULL; +} + +//----------------------------------------------------------------------------- + +bool SFXTrack::preload( bool server, String& errorStr ) +{ + if( !Parent::preload( server, errorStr ) ) + return false; + + if( !server ) + { + if( !sfxResolve( &mDescription, errorStr ) ) + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- + +bool SFXTrack::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + // If we have no SFXDescription, try to grab a default. + + if( !mDescription ) + { + if( !Sim::findObject( "AudioEffects", mDescription ) && Sim::getSFXDescriptionSet()->size() > 0 ) + mDescription = dynamic_cast< SFXDescription* >( Sim::getSFXDescriptionSet()->at( 0 ) ); + + if( !mDescription ) + { + Con::errorf( + "SFXTrack(%s)::onAdd: The profile is missing a description!", + getName() ); + return false; + } + } + + Sim::getSFXTrackSet()->addObject( this ); + + return true; +} + +//----------------------------------------------------------------------------- + +void SFXTrack::inspectPostApply() +{ + Parent::inspectPostApply(); + + if( SFX ) + SFX->notifyTrackChanged( this ); +} diff --git a/Engine/source/sfx/sfxTrack.h b/Engine/source/sfx/sfxTrack.h new file mode 100644 index 000000000..2a1d68614 --- /dev/null +++ b/Engine/source/sfx/sfxTrack.h @@ -0,0 +1,97 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXTRACK_H_ +#define _SFXTRACK_H_ + +#ifndef _SIMDATABLOCK_H_ + #include "console/simDatablock.h" +#endif +#ifndef _CONSOLETYPES_H_ + #include "console/consoleTypes.h" +#endif + + +class SFXDescription; + + +/// A datablock that describes sound data for playback. +class SFXTrack : public SimDataBlock +{ + public: + + typedef SimDataBlock Parent; + + enum + { + /// Maximum numbers of parameters that can be pre-assigned to tracks. + MaxNumParameters = 8 + }; + + protected: + + /// The description which controls playback settings. + SFXDescription *mDescription; + + /// Name of the parameters to which sources playing this track should + /// connect. + StringTableEntry mParameters[ MaxNumParameters ]; + + /// Overload this to disable direct instantiation of this class via script 'new'. + virtual bool processArguments( S32 argc, const char **argv ); + + public: + + /// + SFXTrack(); + + /// + SFXTrack( SFXDescription* description ); + + /// Returns the description object for this sound profile. + SFXDescription* getDescription() const { return mDescription; } + + /// + StringTableEntry getParameter( U32 index ) const + { + AssertFatal( index < MaxNumParameters, "SFXTrack::getParameter() - index out of range" ); + return mParameters[ index ]; + } + + /// + void setParameter( U32 index, const char* name ); + + // SimDataBlock. + virtual void packData( BitStream* stream ); + virtual void unpackData( BitStream* stream ); + virtual bool preload( bool server, String& errorStr ); + virtual bool onAdd(); + virtual void inspectPostApply(); + + static void initPersistFields(); + + DECLARE_CONOBJECT( SFXTrack ); + DECLARE_CATEGORY( "SFX" ); + DECLARE_DESCRIPTION( "Abstract base class for any kind of data that can be turned into SFXSources." ); +}; + +#endif // !_SFXTRACK_H_ diff --git a/Engine/source/sfx/sfxTypes.cpp b/Engine/source/sfx/sfxTypes.cpp new file mode 100644 index 000000000..911a23f24 --- /dev/null +++ b/Engine/source/sfx/sfxTypes.cpp @@ -0,0 +1,429 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/sfxTypes.h" +#include "sfx/sfxDescription.h" +#include "sfx/sfxTrack.h" +#include "sfx/sfxEnvironment.h" +#include "sfx/sfxState.h" +#include "sfx/sfxAmbience.h" +#include "sfx/sfxSource.h" +#include "core/stringTable.h" +#include "core/stream/bitStream.h" +#include "platform/typetraits.h" + + +//RDTODO: find a more optimal way to transmit names rather than using full +// strings; don't know how to facilitate NetStrings for this as we don't +// have access to the connection + + +template< class T > +inline void sWrite( BitStream* stream, T* ptr ) +{ + if( stream->writeFlag( ptr != NULL ) ) + { + if( stream->writeFlag( ptr->isClientOnly() ) ) + stream->writeString( ptr->getName() ); + else + stream->writeRangedU32( ptr->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); + } +} +template< class T > +inline void sRead( BitStream* stream, T** ptr ) +{ + if( !stream->readFlag() ) + *ptr = NULL; + else + { + if( stream->readFlag() ) + { + StringTableEntry name = stream->readSTString(); + + AssertFatal( !( U32( name ) & 0x1 ), "sRead - misaligned pointer" ); // StringTableEntry pointers are always word-aligned. + + *( ( StringTableEntry* ) ptr ) = name; + } + else + *reinterpret_cast< U32* >( ptr ) = + ( stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ) << 1 ) | 0x1; + } +} +template< class T > +inline bool sResolve( T** ptr, String& errorString ) +{ + if( !*ptr ) + return true; + else if( *reinterpret_cast< U32* >( ptr ) & 0x1 ) + { + U32 id = *reinterpret_cast< U32* >( ptr ) >> 1; + + T* p; + if( !Sim::findObject( id, p ) ) + { + errorString = String::ToString( "sfxResolve - Could not resolve networked %s datalock with id '%i'", + T::getStaticClassRep()->getClassName(), id ); + *ptr = NULL; + return false; + } + + *ptr = p; + } + else + { + StringTableEntry name = *( ( StringTableEntry* ) ptr ); + + T* p; + if( !Sim::findObject( name, p ) ) + { + errorString = String::ToString( "sfxResolve - Could not resolve local %s datablock with name '%s'", + T::getStaticClassRep()->getClassName(), name ); + *ptr = NULL; + return false; + } + + *ptr = p; + } + + return true; +} + + +//============================================================================= +// TypeSFXSourceName. +//============================================================================= + +ConsoleType( SFXSource, TypeSFXSourceName, SFXSource* ) + +ConsoleGetType( TypeSFXSourceName ) +{ + SFXSource** obj = ( SFXSource** ) dptr; + if( !*obj ) + return ""; + else + return Con::getReturnBuffer( ( *obj )->getName() ); +} + +ConsoleSetType( TypeSFXSourceName ) +{ + if( argc == 1 ) + { + SFXSource** obj = ( SFXSource**) dptr; + Sim::findObject( argv[ 0 ], *obj ); + } + else + Con::printf("(TypeSFXSourceName) Cannot set multiple args to a single SFXSource."); +} + +//============================================================================= +// TypeSFXParameterName. +//============================================================================= + +ConsoleType( string, TypeSFXParameterName, StringTableEntry ) + +ConsoleGetType( TypeSFXParameterName ) +{ + return *( ( const char** ) dptr ); +} + +ConsoleSetType( TypeSFXParameterName ) +{ + if( argc == 1 ) + *( ( const char** ) dptr ) = StringTable->insert( argv[ 0 ] ); + else + Con::errorf( "(TypeSFXParameterName) Cannot set multiple args to a single SFXParameter." ); +} + +//============================================================================= +// TypeSFXDescriptionName. +//============================================================================= + +ConsoleType( SFXDescription, TypeSFXDescriptionName, SFXDescription* ) + +ConsoleSetType( TypeSFXDescriptionName ) +{ + if( argc == 1 ) + { + SFXDescription* description; + Sim::findObject( argv[ 0 ], description ); + *( ( SFXDescription** ) dptr ) = description; + } + else + Con::errorf( "(TypeSFXDescriptionName) Cannot set multiple args to a single SFXDescription."); +} + +ConsoleGetType( TypeSFXDescriptionName ) +{ + SFXDescription* description = *( ( SFXDescription** ) dptr ); + if( !description || !description->getName() ) + return ""; + else + return description->getName(); +} + +//============================================================================= +// TypeSFXTrackName. +//============================================================================= + +ConsoleType( SFXTrack, TypeSFXTrackName, SFXTrack* ) + +ConsoleSetType( TypeSFXTrackName ) +{ + if( argc == 1 ) + { + SFXTrack* track; + Sim::findObject( argv[ 0 ], track ); + *( ( SFXTrack** ) dptr ) = track; + } + else + Con::errorf( "(TypeSFXTrackName) Cannot set multiple args to a single SFXTrack."); +} + +ConsoleGetType( TypeSFXTrackName ) +{ + SFXTrack* track = *( ( SFXTrack** ) dptr ); + if( !track || !track->getName() ) + return ""; + else + return track->getName(); +} + +//============================================================================= +// TypeSFXEnvironmentName. +//============================================================================= + +ConsoleType( SFXEnvironment, TypeSFXEnvironmentName, SFXEnvironment* ) + +ConsoleSetType( TypeSFXEnvironmentName ) +{ + if( argc == 1 ) + { + SFXEnvironment* environment; + Sim::findObject( argv[ 0 ], environment ); + *( ( SFXEnvironment** ) dptr ) = environment; + } + else + Con::errorf( "(TypeSFXEnvironmentName) Cannot set multiple args to a single SFXEnvironment."); +} + +ConsoleGetType( TypeSFXEnvironmentName ) +{ + SFXEnvironment* environment = *( ( SFXEnvironment** ) dptr ); + if( !environment || !environment->getName() ) + return ""; + else + return environment->getName(); +} + +//============================================================================= +// TypeSFXStateName. +//============================================================================= + +ConsoleType( SFXState, TypeSFXStateName, SFXState* ) + +ConsoleSetType( TypeSFXStateName ) +{ + if( argc == 1 ) + { + SFXState* state; + Sim::findObject( argv[ 0 ], state ); + *( ( SFXState** ) dptr ) = state; + } + else + Con::errorf( "(TypeSFXStateName) Cannot set multiple args to a single SFXState."); +} + +ConsoleGetType( TypeSFXStateName ) +{ + SFXState* state = *( ( SFXState** ) dptr ); + if( !state || !state->getName() ) + return ""; + else + return state->getName(); +} + +//============================================================================= +// TypeSFXAmbienceName. +//============================================================================= + +ConsoleType( SFXAmbience, TypeSFXAmbienceName, SFXAmbience* ) + +ConsoleSetType( TypeSFXAmbienceName ) +{ + if( argc == 1 ) + { + SFXAmbience* ambience; + Sim::findObject( argv[ 0 ], ambience ); + *( ( SFXAmbience** ) dptr ) = ambience; + } + else + Con::errorf( "(TypeSFXAmbienceName) Cannot set multiple args to a single SFXAmbience."); +} + +ConsoleGetType( TypeSFXAmbienceName ) +{ + SFXAmbience* ambience = *( ( SFXAmbience** ) dptr ); + if( !ambience || !ambience->getName() ) + return ""; + else + return ambience->getName(); +} + +//============================================================================= +// I/O. +//============================================================================= + +//----------------------------------------------------------------------------- + +void sfxWrite( BitStream* stream, SFXSource* source ) +{ + if( stream->writeFlag( source != NULL ) ) + stream->writeString( source->getName() ); +} + +//----------------------------------------------------------------------------- + +void sfxWrite( BitStream* stream, SFXDescription* description ) +{ + sWrite( stream, description ); +} + +//----------------------------------------------------------------------------- + +void sfxWrite( BitStream* stream, SFXTrack* track ) +{ + sWrite( stream, track ); +} + +//----------------------------------------------------------------------------- + +void sfxWrite( BitStream* stream, SFXEnvironment* environment ) +{ + sWrite( stream, environment ); +} + +//----------------------------------------------------------------------------- + +void sfxWrite( BitStream* stream, SFXState* state ) +{ + sWrite( stream, state ); +} + +//----------------------------------------------------------------------------- + +void sfxWrite( BitStream* stream, SFXAmbience* ambience ) +{ + sWrite( stream, ambience ); +} + +//----------------------------------------------------------------------------- + +void sfxRead( BitStream* stream, SFXDescription** description ) +{ + sRead( stream, description ); +} + +//----------------------------------------------------------------------------- + +void sfxRead( BitStream* stream, SFXTrack** track ) +{ + sRead( stream, track ); +} + +//----------------------------------------------------------------------------- + +void sfxRead( BitStream* stream, SFXEnvironment** environment ) +{ + sRead( stream, environment ); +} + +//----------------------------------------------------------------------------- + +void sfxRead( BitStream* stream, SFXState** state ) +{ + sRead( stream, state ); +} + +//----------------------------------------------------------------------------- + +void sfxRead( BitStream* stream, SFXAmbience** ambience ) +{ + sRead( stream, ambience ); +} + +//----------------------------------------------------------------------------- + +bool sfxResolve( SFXDescription** description, String& errorString ) +{ + return sResolve( description, errorString ); +} + +//----------------------------------------------------------------------------- + +bool sfxResolve( SFXTrack** track, String& errorString ) +{ + return sResolve( track, errorString ); +} + +//----------------------------------------------------------------------------- + +bool sfxResolve( SFXEnvironment** environment, String& errorString ) +{ + return sResolve( environment, errorString ); +} + +//----------------------------------------------------------------------------- + +bool sfxResolve( SFXState** state, String& errorString ) +{ + return sResolve( state, errorString ); +} + +//----------------------------------------------------------------------------- + +bool sfxResolve( SFXAmbience** ambience, String& errorString ) +{ + return sResolve( ambience, errorString ); +} + +//----------------------------------------------------------------------------- + +bool sfxReadAndResolve( BitStream* stream, SFXSource** source, String& errorString ) +{ + if( !stream->readFlag() ) + { + *source = NULL; + return true; + } + + const char* name = stream->readSTString(); + + SFXSource* object; + if( !Sim::findObject( name, object ) ) + { + errorString = String::ToString( "sfxReadAndResolve - no SFXSource '%s'", name ); + return false; + } + + *source = object; + return true; +} diff --git a/Engine/source/sfx/sfxTypes.h b/Engine/source/sfx/sfxTypes.h new file mode 100644 index 000000000..b8d605a46 --- /dev/null +++ b/Engine/source/sfx/sfxTypes.h @@ -0,0 +1,140 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXTYPES_H_ +#define _SFXTYPES_H_ + +#ifndef _CONSOLETYPES_H_ + #include "console/consoleTypes.h" +#endif + + +/// @file +/// Additional SFX console types. + + +class BitStream; +class SFXTrack; +class SFXEnvironment; +class SFXDescription; +class SFXState; +class SFXAmbience; +class SFXSource; + + +/// +DefineConsoleType( TypeSFXSourceName, SFXSource* ); + +/// Name of an SFXParameter. Resolved to SFXParamter instances on the +/// client when creating a source. +/// +/// This is a separate type so that the inspector can provide meaningful +/// popups. +DefineConsoleType( TypeSFXParameterName, StringTableEntry ); + +/// SFXDescription datablock reference. May be client-only. +DefineConsoleType( TypeSFXDescriptionName, SFXDescription* ); + +/// SFXTrack datablock reference. May be client-only. +DefineConsoleType( TypeSFXTrackName, SFXTrack* ); + +/// SFXEnvironment datablock reference. May be client-only. +DefineConsoleType( TypeSFXEnvironmentName, SFXEnvironment* ); + +/// SFXState datablock reference. May be client-only. +DefineConsoleType( TypeSFXStateName, SFXState* ); + +/// SFXAmbience datablock reference. May be client-only. +DefineConsoleType( TypeSFXAmbienceName, SFXAmbience* ); + + +/// @name Datablock Network I/O +/// +/// The following functions allow to transmit datablock references over the network +/// even when the referenced datablocks are not networked ("client-only" datablocks). +/// +/// The write functions write a reference to the bitstream. +/// +/// The read functions read a reference from the bitstream and store it in a non-resolved +/// form, i.e. the resulting pointer will not yet be valid. This is necessary as during +/// datablock transmission, the referenced datablock may actually only be transmitted +/// later in the stream and cannot thus be immediately looked up on the client. +/// +/// The resolve functions take an unresolved datablock reference and resolve them to +/// valid pointers. +/// +/// @{ + +void sfxWrite( BitStream* stream, SFXSource* source ); +void sfxWrite( BitStream* stream, SFXDescription* description ); +void sfxWrite( BitStream* stream, SFXTrack* track ); +void sfxWrite( BitStream* stream, SFXEnvironment* environment ); +void sfxWrite( BitStream* stream, SFXState* state ); +void sfxWrite( BitStream* stream, SFXAmbience* ambience ); + +void sfxRead( BitStream* stream, SFXDescription** description ); +void sfxRead( BitStream* stream, SFXTrack** track ); +void sfxRead( BitStream* stream, SFXEnvironment** environment ); +void sfxRead( BitStream* stream, SFXState** state ); +void sfxRead( BitStream* stream, SFXAmbience** ambience ); + +bool sfxResolve( SFXDescription** description, String& errorString ); +bool sfxResolve( SFXTrack** track, String& errorString ); +bool sfxResolve( SFXEnvironment** environment, String& errorString ); +bool sfxResolve( SFXState** state, String& errorString ); +bool sfxResolve( SFXAmbience** ambience, String& errorString ); + +bool sfxReadAndResolve( BitStream* stream, SFXSource** source, String& errorString ); + +inline bool sfxReadAndResolve( BitStream* stream, SFXDescription** description, String& errorString ) +{ + sfxRead( stream, description ); + return sfxResolve( description, errorString ); +} + +inline bool sfxReadAndResolve( BitStream* stream, SFXTrack** track, String& errorString ) +{ + sfxRead( stream, track ); + return sfxResolve( track, errorString ); +} + +inline bool sfxReadAndResolve( BitStream* stream, SFXEnvironment** environment, String& errorString ) +{ + sfxRead( stream, environment ); + return sfxResolve( environment, errorString ); +} + +inline bool sfxReadAndResolve( BitStream* stream, SFXState** state, String& errorString ) +{ + sfxRead( stream, state ); + return sfxResolve( state, errorString ); +} + +inline bool sfxReadAndResolve( BitStream* stream, SFXAmbience** ambience, String& errorString ) +{ + sfxRead( stream, ambience ); + return sfxResolve( ambience, errorString ); +} + +/// @} + +#endif // !_SFXTYPES_H_ diff --git a/Engine/source/sfx/sfxVoice.cpp b/Engine/source/sfx/sfxVoice.cpp new file mode 100644 index 000000000..f02244209 --- /dev/null +++ b/Engine/source/sfx/sfxVoice.cpp @@ -0,0 +1,420 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/sfxVoice.h" +#include "sfx/sfxBuffer.h" +#include "sfx/sfxInternal.h" +#include "console/console.h" + + +// [rene, 07-May-11] The interplay between SFXBuffer and SFXVoice here isn't good. +// Too complex, and while it works reliably in most cases, when doing seeks +// on streaming sources, it is prone to subtle timing dependencies. + + +//#define DEBUG_SPEW + + +Signal< void( SFXVoice* voice ) > SFXVoice::smVoiceCreatedSignal; +Signal< void( SFXVoice* voice ) > SFXVoice::smVoiceDestroyedSignal; + + +//----------------------------------------------------------------------------- + +SFXVoice::SFXVoice( SFXBuffer* buffer ) + : mBuffer( buffer ), + mStatus( SFXStatusNull ), + mOffset( 0 ) +{ +} + +//----------------------------------------------------------------------------- + +SFXVoice::~SFXVoice() +{ + smVoiceDestroyedSignal.trigger( this ); + + if( mBuffer ) + mBuffer->mOnStatusChange.remove( this, &SFXVoice::_onBufferStatusChange ); +} + +//----------------------------------------------------------------------------- + +void SFXVoice::_attachToBuffer() +{ + using namespace SFXInternal; + + // If the buffer is unique, attach us as its unique voice. + + if( mBuffer->isUnique() ) + { + AssertFatal( !mBuffer->mUniqueVoice, + "SFXVoice::SFXVoice - streaming buffer already is assigned a voice" ); + + mBuffer->mUniqueVoice = this; + + // The buffer can start its queuing now so give it a chance + // to run an update. + SFXInternal::TriggerUpdate(); + } + + mBuffer->mOnStatusChange.notify( this, &SFXVoice::_onBufferStatusChange ); + + smVoiceCreatedSignal.trigger( this ); +} + +//----------------------------------------------------------------------------- + +void SFXVoice::_onBufferStatusChange( SFXBuffer* buffer, SFXBuffer::Status newStatus ) +{ + AssertFatal( buffer == mBuffer, "SFXVoice::_onBufferStatusChange() - got an invalid buffer" ); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXVoice] Buffer changes status to: %s", + newStatus == SFXBuffer::STATUS_Null ? "STATUS_Null" : + newStatus == SFXBuffer::STATUS_Loading ? "STATUS_Loading" : + newStatus == SFXBuffer::STATUS_Ready ? "STATUS_Ready" : + newStatus == SFXBuffer::STATUS_Blocked ? "STATUS_Blocked" : + newStatus == SFXBuffer::STATUS_AtEnd ? "STATUS_AtEnd" : "unknown" ); + #endif + + // This is called concurrently! + + switch( newStatus ) + { + case SFXBuffer::STATUS_Loading: + // Can ignore this. Buffer simply lets us know it has started + // its initial stream load. + break; + + case SFXBuffer::STATUS_AtEnd: + + // Streaming voice has played to end of stream. + + if( dCompareAndSwap( ( U32& ) mStatus, SFXStatusPlaying, SFXStatusTransition ) ) + { + _stop(); + mOffset = 0; + + dCompareAndSwap( ( U32& ) mStatus, SFXStatusTransition, SFXStatusStopped ); + } + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXVoice] Voice stopped as end of stream reached" ); + #endif + break; + + case SFXBuffer::STATUS_Blocked: + + // Streaming has fallen behind. + + if( dCompareAndSwap( ( U32& ) mStatus, SFXStatusPlaying, SFXStatusTransition ) ) + { + _pause(); + dCompareAndSwap( ( U32& ) mStatus, SFXStatusTransition, SFXStatusBlocked ); + } + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXVoice] Voice waiting for buffer to catch up" ); + #endif + break; + + case SFXBuffer::STATUS_Ready: + if( dCompareAndSwap( ( U32& ) mStatus, SFXStatusBlocked, SFXStatusTransition ) ) + { + // Get the playback going again. + + _play(); + dCompareAndSwap( ( U32& ) mStatus, SFXStatusTransition, SFXStatusPlaying ); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXVoice] Buffer caught up with voice" ); + #endif + } + break; + + case SFXBuffer::STATUS_Null: + AssertFatal( false, "SFXVoice::_onBufferStatusChange - Buffer changed to invalid NULL status" ); + break; + } +} + +//----------------------------------------------------------------------------- + +SFXStatus SFXVoice::getStatus() const +{ + // Detect when the device has finished playback. Only for + // non-streaming voices. For streaming voices, we rely on + // the buffer to send us a STATUS_AtEnd signal when it is + // done playing. + + if( mStatus == SFXStatusPlaying && + !mBuffer->isStreaming() && + _status() == SFXStatusStopped ) + mStatus = SFXStatusStopped; + + return mStatus; +} + +//----------------------------------------------------------------------------- + +void SFXVoice::play( bool looping ) +{ + AssertFatal( mBuffer != NULL, "SFXVoice::play() - no buffer" ); + using namespace SFXInternal; + + // For streaming, check whether we have played previously. + // If so, reset the buffer's stream. + + if( mBuffer->isStreaming() && + mStatus == SFXStatusStopped ) + _resetStream( 0 ); + + // Now switch state. + + while( mStatus != SFXStatusPlaying && + mStatus != SFXStatusBlocked ) + { + if( !mBuffer->isReady() && + ( dCompareAndSwap( ( U32& ) mStatus, SFXStatusNull, SFXStatusBlocked ) || + dCompareAndSwap( ( U32& ) mStatus, SFXStatusStopped, SFXStatusBlocked ) || + dCompareAndSwap( ( U32& ) mStatus, SFXStatusPaused, SFXStatusBlocked ) ) ) + { + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXVoice] Wanted to start playback but buffer isn't ready" ); + #endif + + break; + } + + else if( dCompareAndSwap( ( U32& ) mStatus, SFXStatusNull, SFXStatusTransition ) || + dCompareAndSwap( ( U32& ) mStatus, SFXStatusStopped, SFXStatusTransition ) || + dCompareAndSwap( ( U32& ) mStatus, SFXStatusPaused, SFXStatusTransition ) ) + { + _play(); + dCompareAndSwap( ( U32& ) mStatus, SFXStatusTransition, SFXStatusPlaying ); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXVoice] Started playback" ); + #endif + + break; + } + } +} + +//----------------------------------------------------------------------------- + +void SFXVoice::pause() +{ + while( mStatus != SFXStatusPaused && + mStatus != SFXStatusNull && + mStatus != SFXStatusStopped ) + { + if( dCompareAndSwap( ( U32& ) mStatus, SFXStatusPlaying, SFXStatusTransition ) || + dCompareAndSwap( ( U32& ) mStatus, SFXStatusBlocked, SFXStatusTransition ) ) + { + _pause(); + dCompareAndSwap( ( U32& ) mStatus, SFXStatusTransition, SFXStatusPaused ); + + break; + } + } +} + +//----------------------------------------------------------------------------- + +void SFXVoice::stop() +{ + while( mStatus != SFXStatusStopped && + mStatus != SFXStatusNull ) + { + if( dCompareAndSwap( ( U32& ) mStatus, ( U32 ) SFXStatusPlaying, ( U32 ) SFXStatusTransition ) || + dCompareAndSwap( ( U32& ) mStatus, SFXStatusPaused, SFXStatusTransition ) || + dCompareAndSwap( ( U32& ) mStatus, SFXStatusBlocked, SFXStatusTransition ) ) + { + _stop(); + dCompareAndSwap( ( U32& ) mStatus, SFXStatusTransition, SFXStatusStopped ); + + break; + } + } +} + +//----------------------------------------------------------------------------- + +U32 SFXVoice::getPosition() const +{ + // When stopped, always return 0. + + if( getStatus() == SFXStatusStopped ) + return 0; + + // It depends on the device if and when it will return a count of the total samples + // played so far. With streaming buffers, all devices will do that. With non-streaming + // buffers, some may do for looping voices thus returning a number that exceeds the actual + // source stream size. So, clamp things into range here and also take care of any offsetting + // resulting from a setPosition() call. + + U32 pos = _tell() + mOffset; + const U32 numStreamSamples = mBuffer->getFormat().getSampleCount( mBuffer->getDuration() ); + + if( mBuffer->mIsLooping ) + pos %= numStreamSamples; + else if( pos > numStreamSamples ) + { + // Ensure we never report out-of-range positions even if the device does. + + pos = numStreamSamples; + } + + return pos; +} + +//----------------------------------------------------------------------------- + +void SFXVoice::setPosition( U32 inSample ) +{ + // Clamp to sample range. + const U32 sample = inSample % ( mBuffer->getFormat().getSampleCount( mBuffer->getDuration() ) - 1 ); + + // Don't perform a seek when we already are at the + // given position. Especially avoids a costly stream + // clone when seeking on a streamed voice. + + if( getPosition() == sample ) + return; + + if( !mBuffer->isStreaming() ) + { + // Non-streaming sound. Just seek in the device buffer. + + _seek( sample ); + } + else + { + // Streaming sound. Reset the stream and playback. + // + // Unfortunately, the logic here is still prone to subtle timing dependencies + // in relation to the buffer updates. In retrospect, I feel that solving all issues + // of asynchronous operation on a per-voice/buffer level has greatly complicated + // the system. It seems now that it would have been a lot simpler to have a single + // asynchronous buffer/voice manager that manages the updates of all voices and buffers + // currently in the system in one spot. Packet reads could still be pushed out to + // the thread pool but queue updates would all be handled centrally in one spot. This + // would do away with problems like those (mostly) solved by the multi-step procedure + // here. + + // Go into transition. + + SFXStatus oldStatus; + while( true ) + { + oldStatus = mStatus; + if( oldStatus != SFXStatusTransition && + dCompareAndSwap( ( U32& ) mStatus, oldStatus, SFXStatusTransition ) ) + break; + } + + // Switch the stream. + + _resetStream( sample, false ); + + // Come out of transition. + + SFXStatus newStatus = oldStatus; + if( oldStatus == SFXStatusPlaying ) + newStatus = SFXStatusBlocked; + + dCompareAndSwap( ( U32& ) mStatus, SFXStatusTransition, newStatus ); + + // Trigger an update. + + SFXInternal::TriggerUpdate(); + } +} + +//----------------------------------------------------------------------------- + +void SFXVoice::_resetStream( U32 sampleStartPos, bool triggerUpdate ) +{ + AssertFatal( mBuffer->isStreaming(), "SFXVoice::_resetStream - Not a streaming voice!" ); + + ThreadSafeRef< SFXBuffer::AsyncState > oldState = mBuffer->mAsyncState; + AssertFatal( oldState != NULL, + "SFXVoice::_resetStream() - streaming buffer must have valid async state" ); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXVoice] Resetting stream to %i", sampleStartPos ); + #endif + + // Rather than messing up the async code by adding repositioning (which + // further complicates synchronizing the various parts), just construct + // a complete new AsyncState and discard the old one. The only problem + // here is the stateful sound streams. We can't issue a new packet as long + // as we aren't sure there's no request pending, so we just clone the stream + // and leave the old one to the old AsyncState. + + ThreadSafeRef< SFXStream > sfxStream = oldState->mStream->getSourceStream()->clone(); + if( sfxStream == NULL ) + { + Con::errorf( "SFXVoice::_resetStream - could not clone SFXStream" ); + return; + } + + IPositionable< U32 >* sfxPositionable = dynamic_cast< IPositionable< U32 >* >( sfxStream.ptr() ); + if( !sfxPositionable ) + { + Con::errorf( "SFXVoice::_resetStream - could not seek in SFXStream" ); + return; + } + + sfxPositionable->setPosition( sampleStartPos * sfxStream->getFormat().getBytesPerSample() ); + + ThreadSafeRef< SFXInternal::SFXAsyncStream > newStream = + new SFXInternal::SFXAsyncStream + ( sfxStream, + true, + oldState->mStream->getPacketDuration() / 1000, + oldState->mStream->getReadAhead(), + oldState->mStream->isLooping() ); + newStream->setReadSilenceAtEnd( oldState->mStream->getReadSilenceAtEnd() ); + + AssertFatal( newStream->getPacketSize() == oldState->mStream->getPacketSize(), + "SFXVoice::setPosition() - packet size mismatch with new stream" ); + + ThreadSafeRef< SFXBuffer::AsyncState > newState = + new SFXBuffer::AsyncState( newStream ); + newStream->start(); + + // Switch the states. + + mOffset = sampleStartPos; + mBuffer->mAsyncState = newState; + + // Stop the old state from reading more data. + + oldState->mStream->stop(); + + // Trigger update. + + if( triggerUpdate ) + SFXInternal::TriggerUpdate(); +} diff --git a/Engine/source/sfx/sfxVoice.h b/Engine/source/sfx/sfxVoice.h new file mode 100644 index 000000000..f199a633f --- /dev/null +++ b/Engine/source/sfx/sfxVoice.h @@ -0,0 +1,226 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXVOICE_H_ +#define _SFXVOICE_H_ + +#ifndef _REFBASE_H_ +#include "core/util/refBase.h" +#endif + +#ifndef _TSTREAM_H_ +#include "core/stream/tStream.h" +#endif + +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif + +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif + +#ifndef _SFXBUFFER_H_ +#include "sfx/sfxBuffer.h" +#endif + + +namespace SFXInternal { + class SFXVoiceTimeSource; + class SFXAynscQueue; +} + + +/// The voice interface provides for playback of sound buffers and positioning +/// of 3D sounds. +/// +/// This abstract class is derived from in the different device layers to implement +/// device-specific playback control. +/// +/// The primary responsibility of this class is to mediate between the user requests +/// (play(), stop(), pause(), setPosition()), the buffer (which may change state +/// asynchronously), and the underlying device playback control (_play(), _stop(), +/// _pause(), _seek()). +class SFXVoice : public StrongRefBase, + public IPositionable< U32 > +{ + public: + + typedef void Parent; + + friend class SFXDevice; // _attachToBuffer + friend class SFXInternal::SFXVoiceTimeSource; // _tell + friend class SFXInternal::SFXAsyncQueue; // mOffset + + protected: + + /// Current playback status. + /// @note This is maintained on both the sound update thread as well + /// as the main thread. + mutable volatile SFXStatus mStatus; + + /// Sound data played back by the voice. + WeakRefPtr< SFXBuffer > mBuffer; + + /// For streaming voices, this keeps track of play start offset + /// after seeking. Expressed in number of samples. + U32 mOffset; + + explicit SFXVoice( SFXBuffer* buffer ); + + /// @name Device Control Methods + /// @{ + + /// Return the current playback status (playing, paused, or stopped). Default + /// status is stopped. + virtual SFXStatus _status() const = 0; + + /// Stop playback on the device. + /// @note Called from both the SFX update thread and the main thread. + virtual void _stop() = 0; + + /// Start playback on the device. + /// @note Called from both the SFX update thread and the main thread. + virtual void _play() = 0; + + /// Pause playback on the device. + /// @note Called from both the SFX update thread and the main thread. + virtual void _pause() = 0; + + /// Set the playback cursor on the device. + /// @note Only used for non-streaming voices. + virtual void _seek( U32 sample ) = 0; + + /// Get the playback cursor on the device. + /// + /// When the voice is playing or paused, this method must return a valid sample position. + /// When the voice is stopped, the result of this method is undefined. + /// + /// For streaming voices that are looping, the sample position must be a total count of the + /// number of samples played so far which thus includes the count of all cycles before the + /// current one. For non-looping voices, this behavior is optional. + /// + /// @note This is called for both streaming and non-streaming voices. + virtual U32 _tell() const = 0; + + /// @} + + /// Hooked up to SFXBuffer::mOnStatusChange of #mBuffer. + /// @note Called on the SFX update thread. + virtual void _onBufferStatusChange( SFXBuffer* buffer, SFXBuffer::Status newStatus ); + + /// + void _attachToBuffer(); + + /// @name Streaming + /// The following methods are for streaming voices only. + /// @{ + + /// Reset streaming of the voice by cloning the current streaming source and + /// letting the resulting stream start from @a sampleStartPos. + void _resetStream( U32 sampleStartPos, bool triggerUpdate = true ); + + /// @} + + public: + + static Signal< void( SFXVoice* ) > smVoiceCreatedSignal; + static Signal< void( SFXVoice* ) > smVoiceDestroyedSignal; + + /// The destructor. + virtual ~SFXVoice(); + + /// + const SFXFormat& getFormat() const { return mBuffer->getFormat(); } + + /// Return the current playback position (in number of samples). + /// + /// @note For looping sounds, this will return the position in the + /// current cycle and not the total number of samples played so far. + virtual U32 getPosition() const; + + /// Sets the playback position to the given sample count. + /// + /// @param sample Offset in number of samples. This is allowed to use an offset + /// accumulated from multiple cycles. Each cycle will wrap around back to the + /// beginning of the buffer. + virtual void setPosition( U32 sample ); + + /// @return the current playback status. + /// @note For streaming voices, the reaction to for the voice to update its status + /// to SFXStatusStopped after the voice has stopped playing depends on the synchronization + /// of the underlying device. If, for example, the underlying device uses periodic updates + /// and doesn't have notifications, then a delay up to the total length of the period time + /// may occur before the status changes. Note that in-between the actual playback stopping + /// and the voice updating its status, the result of getPosition() is undefined. + virtual SFXStatus getStatus() const; + + /// Starts playback from the current position. + virtual void play( bool looping ); + + /// Stops playback and moves the position to the start. + virtual void stop(); + + /// Pauses playback. + virtual void pause(); + + /// Sets the position and orientation for a 3d voice. + virtual void setTransform( const MatrixF &transform ) = 0; + + /// Sets the velocity for a 3d voice. + virtual void setVelocity( const VectorF &velocity ) = 0; + + /// Sets the minimum and maximum distances for 3d falloff. + virtual void setMinMaxDistance( F32 min, F32 max ) = 0; + + /// Set the distance attenuation rolloff factor. Support by device optional. + virtual void setRolloffFactor( F32 factor ) {} + + /// Sets the volume. + virtual void setVolume( F32 volume ) = 0; + + /// Sets the pitch scale. + virtual void setPitch( F32 pitch ) = 0; + + /// Set sound cone of a 3D sound. + /// + /// @param innerAngle Inner cone angle in degrees. + /// @param outerAngle Outer cone angle in degrees. + /// @param outerVolume Outer volume factor. + virtual void setCone( F32 innerAngle, + F32 outerAngle, + F32 outerVolume ) = 0; + + /// Set the reverb properties for playback of this sound. + /// @note Has no effect on devices that do not support reverb. + virtual void setReverb( const SFXSoundReverbProperties& reverb ) {} + + /// Set the priority of this voice. Default 1.0. + /// @note Has no effect on devices that do not support voice management. + virtual void setPriority( F32 priority ) {} + + /// Returns true if the voice is virtualized on the device. + /// @note Always false on devices that do not support voice management. + virtual bool isVirtual() const { return false; } +}; + +#endif // _SFXVOICE_H_ diff --git a/Engine/source/sfx/sfxWorld.h b/Engine/source/sfx/sfxWorld.h new file mode 100644 index 000000000..2806cc4c8 --- /dev/null +++ b/Engine/source/sfx/sfxWorld.h @@ -0,0 +1,427 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXWORLD_H_ +#define _SFXWORLD_H_ + +#ifndef _SCOPETRACKER_H_ + #include "util/scopeTracker.h" +#endif +#ifndef _TVECTOR_H_ + #include "core/util/tVector.h" +#endif +#ifndef _SFXSYSTEM_H_ + #include "sfx/sfxSystem.h" +#endif +#ifndef _SFXSOUNDSCAPE_H_ + #include "sfx/sfxSoundscape.h" +#endif +#ifndef _SFXAMBIENCE_H_ + #include "sfx/sfxAmbience.h" +#endif + +// Disable warning about unreferenced +// local function on VC. +#ifdef TORQUE_COMPILER_VISUALC + #pragma warning( disable: 4505 ) +#endif + + +//#define DEBUG_SPEW + + +/// @file +/// System for representing and tracking changes to the SFX world of +/// sounds, sound spaces, occluders, and receivers. +/// +/// This is system is abstracted over the number of dimensions it will +/// work with, so it is usable for both 2D and 3D. +/// +/// To put it to use, it has to be connected to the engine's scene +/// graph structure by deriving suitable types from SFXObject. + + +class SFXAmbience; + + +/// Property flags for SFXObjects. +enum SFXObjectFlags +{ + /// Object occludes sound. + SFXObjectOccluder = BIT( 0 ), + + /// Object connects two ambient zones. Results in a continuous blend + /// between the two ambient zones as the listener travels through the + /// portal. + SFXObjectPortal = BIT( 1 ), + + /// + SFXObjectZone = BIT( 2 ), + + /// Object is currently used as listener. + /// + /// @note An object used as a listener will automatically have all + /// its other sound-related functionality (occlusion, zoning) ignored. + SFXObjectListener = BIT( 3 ), + + /// + SFXObjectEmitter = BIT( 4 ), +}; + + +/// Information about an object in the SFX world. +/// +/// SFXObjects are: +/// +/// - Occluders: blocking sound +/// - Zones: ambient sound spaces +/// - Portals: connectors between zones +/// - Listeners: receiving sound +/// +/// Note that emitters are not managed by the SFX world. Instead, the set of +/// 3D voices active on the device at any one point is defined as the set of +/// current sound sources. +/// +template< int NUM_DIMENSIONS > +class SFXObject : public ScopeTrackerObject< NUM_DIMENSIONS > +{ + public: + + typedef ScopeTrackerObject< NUM_DIMENSIONS > Parent; + + protected: + + /// + BitSet32 mFlags; + + public: + + /// + SFXObject() + : Parent() {} + + /// Return true if this object is only a sound occluder without any more ambient + /// sound properties. These kind of objects are not put into the tracking system. + bool isOccluderOnly() const { return ( isOccluder() && !isZone() ); } + + /// Return true if this object is occluding sound. + bool isOccluder() const { return mFlags.test( SFXObjectOccluder ); } + + /// Return true if this object connects two ambient spaces. + bool isPortal() const { return mFlags.test( SFXObjectPortal ); } + + /// + bool isZone() const { return mFlags.test( SFXObjectZone ); } + + /// Return true if this object is currently being used as the listener. + /// @note All other sound-related properties (occlusion, ambience, etc.) will be ignored + /// on the listener object. + bool isListener() const { return mFlags.test( SFXObjectListener ); } + + /// + bool isEmitter() const { return mFlags.test( SFXObjectEmitter ); } + + /// + void setFlags( U32 bits ) { mFlags.set( bits ); } + + /// + void clearFlags( U32 bits ) { mFlags.clear( bits ); } + + /// @name Implementor Interface + /// + /// The following methods must be implemented by the client. They are defined here + /// just for reference. If you don't override them, you'll get link errors. + /// + /// @{ + + /// Return the world-space position of the ears on this object. + /// This will only be called on objects that are made listeners. + void getReferenceCenter( F32 pos[ NUM_DIMENSIONS ] ) const; + + /// Return the object's bounding box in world-space including its surround zone. + void getBounds( F32 minBounds[ NUM_DIMENSIONS ], F32 maxBounds[ NUM_DIMENSIONS ] ) const; + + /// Return the object's bounding box in world-space excluding its surround zone. + void getRealBounds( F32 minBounds[ NUM_DIMENSIONS ], F32 maxBounds[ NUM_DIMENSIONS ] ) const; + + /// Return true if the given point is contained in the object's volume. + bool containsPoint( const F32 point[ NUM_DIMENSIONS ] ) const; + + /// Return the ambient space active within this object. + /// @note Portals cannot have their own ambient spaces. + SFXAmbience* getAmbience() const; + + /// + String describeSelf() const; + + /// @} +}; + + +/// SFXWorld tracks an N-dimensional world of SFXObjects. +/// +/// +/// This class uses two systems as back-ends: occlusion handling is passed on to the +/// occlusion manager installed on the system and tracking the listener traveling through +/// the ambient spaces is +/// +template< int NUM_DIMENSIONS, typename Object > +class SFXWorld : public ScopeTracker< NUM_DIMENSIONS, Object > +{ + public: + + typedef ScopeTracker< NUM_DIMENSIONS, Object > Parent; + + protected: + + /// Record for objects that are currently in scope. + struct Scope + { + /// Sort key on scope stack. This is used to establish an ordering between + /// the ambient spaces that the listener is in concurrently. + F32 mSortValue; + + /// The soundscape instance. Only objects for which the listener actually + /// is in one of the sound zones, will have an associated soundscape. + SFXSoundscape* mSoundscape; + + /// The object defining this scope. If this is a portal, we transition + /// between this space and the space above us in the stack. + Object mObject; + + Scope() {} + Scope( F32 sortValue, Object object ) + : mSortValue( sortValue ), + mSoundscape( NULL ), + mObject( object ) {} + }; + + /// A top-down ordering of all objects that are currently in scope. + Vector< Scope > mScopeStack; + + /// Return the index on the scope stack that is tied to the given object or -1 if + /// the object is not on the stack. + S32 _findScope( Object object ); + + /// + void _sortScopes(); + + /// + F32 _getSortValue( Object object ); + + // ScopeTracker. + virtual void _onScopeIn( Object object ); + virtual void _onScopeOut( Object object ); + + public: + + /// + SFXWorld(); + + /// Update the state of the SFX world. + /// + /// @note This method may potentially be costly; don't update every frame. + void update(); + + // ScopeTracker. + void notifyChanged( Object object ); +}; + + + +//----------------------------------------------------------------------------- + +template< int NUM_DIMENSIONS, class Object > +SFXWorld< NUM_DIMENSIONS, Object >::SFXWorld() +{ + VECTOR_SET_ASSOCIATION( mScopeStack ); +} + +//----------------------------------------------------------------------------- + +template< int NUM_DIMENSIONS, class Object > +void SFXWorld< NUM_DIMENSIONS, Object >::update() +{ + if( !this->mReferenceObject ) + return; + + // Get the reference center (listener) pos. + + F32 listenerPos[ NUM_DIMENSIONS ]; + for( U32 n = 0; n < NUM_DIMENSIONS; n ++ ) + listenerPos[ n ] = Deref( this->mReferenceObject ).getMinTrackingNode( n )->getPosition(); + + // Check all current scopes. + + SFXSoundscapeManager* soundscapeMgr = SFX->getSoundscapeManager(); + for( U32 i = 0; i < mScopeStack.size(); ++ i ) + { + Scope& scope = mScopeStack[ i ]; + + if( Deref( scope.mObject ).containsPoint( listenerPos ) ) + { + // Listener is in the object. + + if( !scope.mSoundscape ) + { + // Add the object's ambient space to the soundscape mix. + + SFXAmbience* ambience = Deref( scope.mObject ).getAmbience(); + AssertFatal( ambience != NULL, "SFXWorld::update() - object on stack that does not have an ambient space!" ); + + // Find the index at which to insert the ambient space. 0 is always + // the global space and each active soundscape lower down our stack + // increments by one. + + U32 index = 1; + for( U32 j = 0; j < i; ++ j ) + if( mScopeStack[ j ].mSoundscape ) + ++ index; + + scope.mSoundscape = soundscapeMgr->insertSoundscape( index, ambience ); + } + } + else + { + SFXAmbience* ambience = Deref( scope.mObject ).getAmbience(); + AssertFatal( ambience != NULL, "SFXWorld::update() - object on stack that does not have an ambient space!" ); + + // Listener is neither inside object nor in its + // proximity zone. Kill its ambient soundscape if it + // has one. + + if( scope.mSoundscape ) + { + soundscapeMgr->removeSoundscape( scope.mSoundscape ); + scope.mSoundscape = NULL; + } + } + } +} + +//----------------------------------------------------------------------------- + +template< int NUM_DIMENSIONS, class Object > +void SFXWorld< NUM_DIMENSIONS, Object >::notifyChanged( Object object ) +{ + SFXAmbience* ambience = Deref( object ).getAmbience(); + if( !ambience ) + return; + + for( U32 i = 0; i < mScopeStack.size(); ++ i ) + { + Scope& scope = mScopeStack[ i ]; + if( scope.mObject == object ) + { + if( scope.mSoundscape ) + scope.mSoundscape->setAmbience( ambience ); + break; + } + } +} + +//----------------------------------------------------------------------------- + +template< int NUM_DIMENSIONS, class Object > +void SFXWorld< NUM_DIMENSIONS, Object >::_onScopeIn( Object object ) +{ + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXWorld] Object 0x%x in scope now", object ); + #endif + + // Get the object's ambience properties. If it has + // none, ignore the object. + + SFXAmbience* ambience = Deref( object ).getAmbience(); + if( !ambience ) + return; + + // Find where to insert the object into the stack. + + typename Vector< Scope >::iterator iter = mScopeStack.begin(); + F32 sortValue = _getSortValue( object ); + while( iter != mScopeStack.end() && iter->mSortValue >= sortValue ) + ++ iter; + + // Insert the object into the stack. + + mScopeStack.insert( iter, Scope( sortValue, object ) ); +} + +//----------------------------------------------------------------------------- + +template< int NUM_DIMENSIONS, class Object > +void SFXWorld< NUM_DIMENSIONS, Object >::_onScopeOut( Object object ) +{ + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXWorld] Object 0x%x out of scope now", object ); + #endif + + // Find the object on the stack. + + S32 index = _findScope( object ); + if( index == -1 ) + return; + + // Remove its soundscape. + + Scope& scope = mScopeStack[ index ]; + if( scope.mSoundscape ) + SFX->getSoundscapeManager()->removeSoundscape( scope.mSoundscape ); + + mScopeStack.erase( index ); +} + +//----------------------------------------------------------------------------- + +template< int NUM_DIMENSIONS, class Object > +F32 SFXWorld< NUM_DIMENSIONS, Object >::_getSortValue( Object object ) +{ + //RDTODO: probably need to work with the overlap here instead of the full volumes + + // Get the real object bounds (without the surround zone). + + F32 minBounds[ NUM_DIMENSIONS ], maxBounds[ NUM_DIMENSIONS ]; + Deref( object ).getRealBounds( minBounds, maxBounds ); + + // Get the minimum extent. + + F32 minExtent = maxBounds[ 0 ] - minBounds[ 0 ]; + for( U32 n = 1; n < NUM_DIMENSIONS; ++ n ) + minExtent = getMin( minExtent, maxBounds[ n ] - minBounds[ n ] ); + + return minExtent; +} + +//----------------------------------------------------------------------------- + +template< int NUM_DIMENSIONS, class Object > +S32 SFXWorld< NUM_DIMENSIONS, Object >::_findScope( Object object ) +{ + for( U32 i = 0; i < mScopeStack.size(); ++ i ) + if( mScopeStack[ i ].mObject == object ) + return i; + + return -1; +} + +#endif // !_SFXWORLD_H_ diff --git a/Engine/source/sfx/xaudio/sfxXAudioBuffer.cpp b/Engine/source/sfx/xaudio/sfxXAudioBuffer.cpp new file mode 100644 index 000000000..166815ee7 --- /dev/null +++ b/Engine/source/sfx/xaudio/sfxXAudioBuffer.cpp @@ -0,0 +1,125 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/xaudio/sfxXAudioBuffer.h" +#include "sfx/xaudio/sfxXAudioVoice.h" + + +//#define DEBUG_SPEW + + +SFXXAudioBuffer* SFXXAudioBuffer::create( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ) +{ + SFXXAudioBuffer *buffer = new SFXXAudioBuffer( stream, description ); + return buffer; +} + +SFXXAudioBuffer::SFXXAudioBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ) + : Parent( stream, description ) +{ + VECTOR_SET_ASSOCIATION( mBufferQueue ); +} + +SFXXAudioBuffer::~SFXXAudioBuffer() +{ + _flush(); +} + +void SFXXAudioBuffer::write( SFXInternal::SFXStreamPacket* const* packets, U32 num ) +{ + AssertFatal( SFXInternal::isSFXThread(), "SFXXAudioBuffer::write() - not on SFX thread" ); + + using namespace SFXInternal; + + // Unqueue processed packets. + + if( isStreaming() ) + { + EnterCriticalSection( &_getUniqueVoice()->mLock ); + + XAUDIO2_VOICE_STATE state; + _getUniqueVoice()->mXAudioVoice->GetState( &state ); + + U32 numProcessed = mBufferQueue.size() - state.BuffersQueued; + for( U32 i = 0; i < numProcessed; ++ i ) + { + destructSingle< SFXStreamPacket* >( mBufferQueue.first().mPacket ); + mBufferQueue.pop_front(); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXXAudioBuffer] Unqueued packet" ); + #endif + } + + LeaveCriticalSection( &_getUniqueVoice()->mLock ); + } + + // Queue new packets. + + for( U32 i = 0; i < num; ++ i ) + { + SFXStreamPacket* packet = packets[ i ]; + Buffer buffer; + + if( packet->mIsLast ) + buffer.mData.Flags = XAUDIO2_END_OF_STREAM; + + buffer.mPacket = packet; + buffer.mData.AudioBytes = packet->mSizeActual; + buffer.mData.pAudioData = packet->data; + + mBufferQueue.push_back( buffer ); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXXAudioBuffer] Queued packet" ); + #endif + + // If this is a streaming buffer, submit the packet to the + // voice queue right away. + + if( isStreaming() ) + { + EnterCriticalSection( &_getUniqueVoice()->mLock ); + + IXAudio2SourceVoice* voice = _getUniqueVoice()->mXAudioVoice; + voice->SubmitSourceBuffer( &buffer.mData ); + + LeaveCriticalSection( &_getUniqueVoice()->mLock ); + } + } +} + +void SFXXAudioBuffer::_flush() +{ + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXXAudioBuffer] Flushing buffer" ); + #endif + + if( _getUniqueVoice() ) + _getUniqueVoice()->_stop(); + + while( !mBufferQueue.empty() ) + { + destructSingle( mBufferQueue.last().mPacket ); + mBufferQueue.pop_back(); + } +} diff --git a/Engine/source/sfx/xaudio/sfxXAudioBuffer.h b/Engine/source/sfx/xaudio/sfxXAudioBuffer.h new file mode 100644 index 000000000..a1bcee139 --- /dev/null +++ b/Engine/source/sfx/xaudio/sfxXAudioBuffer.h @@ -0,0 +1,83 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXXAUDIOBUFFER_H_ +#define _SFXXAUDIOBUFFER_H_ + +#include + +#ifndef _SFXINTERNAL_H_ +#include "sfx/sfxInternal.h" +#endif + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + + +/// Audio data buffer for the XAudio device layer. +class SFXXAudioBuffer : public SFXBuffer +{ + public: + + typedef SFXBuffer Parent; + + friend class SFXXAudioDevice; + friend class SFXXAudioVoice; + + protected: + + struct Buffer + { + XAUDIO2_BUFFER mData; + SFXInternal::SFXStreamPacket* mPacket; + + Buffer() + : mPacket( 0 ) + { + dMemset( &mData, 0, sizeof( mData ) ); + } + }; + + typedef Vector< Buffer > QueueType; + + QueueType mBufferQueue; + + /// If this is a streaming buffer, return the unique voice associated + /// with the buffer. + SFXXAudioVoice* _getUniqueVoice() { return ( SFXXAudioVoice* ) mUniqueVoice.getPointer(); } + + /// + SFXXAudioBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ); + virtual ~SFXXAudioBuffer(); + + // SFXBuffer. + virtual void write( SFXInternal::SFXStreamPacket* const* packets, U32 num ); + void _flush(); + + public: + + /// + static SFXXAudioBuffer* create( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ); +}; + +#endif // _SFXXAUDIOBUFFER_H_ \ No newline at end of file diff --git a/Engine/source/sfx/xaudio/sfxXAudioDevice.cpp b/Engine/source/sfx/xaudio/sfxXAudioDevice.cpp new file mode 100644 index 000000000..eca8e64ea --- /dev/null +++ b/Engine/source/sfx/xaudio/sfxXAudioDevice.cpp @@ -0,0 +1,245 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sfx/xaudio/sfxXAudioDevice.h" +#include "platform/async/asyncUpdate.h" +#include "core/stringTable.h" +#include "console/console.h" +#include "core/util/safeRelease.h" +#include "core/tAlgorithm.h" +#include "platform/profiler.h" + + +SFXXAudioDevice::SFXXAudioDevice( SFXProvider* provider, + const String& name, + IXAudio2 *xaudio, + U32 deviceIndex, + U32 speakerChannelMask, + U32 maxBuffers ) + : Parent( name, provider, false, maxBuffers ), + mXAudio( xaudio ), + mMasterVoice( NULL ) +{ + dMemset( &mListener, 0, sizeof( mListener ) ); + + // If mMaxBuffers is negative then use some default value. + // to decide on a good maximum value... or set 8. + // + // TODO: We should change the terminology to voices! + if ( mMaxBuffers < 0 ) + mMaxBuffers = 64; + + // Create the mastering voice. + HRESULT hr = mXAudio->CreateMasteringVoice( &mMasterVoice, + XAUDIO2_DEFAULT_CHANNELS, + XAUDIO2_DEFAULT_SAMPLERATE, + 0, + deviceIndex, + NULL ); + if ( FAILED( hr ) || !mMasterVoice ) + { + Con::errorf( "SFXXAudioDevice - Failed creating master voice!" ); + return; + } + + mMasterVoice->GetVoiceDetails( &mMasterVoiceDetails ); + + // Init X3DAudio. + X3DAudioInitialize( speakerChannelMask, + X3DAUDIO_SPEED_OF_SOUND, + mX3DAudio ); + + // Start the update thread. + + if( !Con::getBoolVariable( "$_forceAllMainThread" ) ) + { + SFXInternal::gUpdateThread = new AsyncUpdateThread + ( "XAudio Update Thread", SFXInternal::gBufferUpdateList ); + SFXInternal::gUpdateThread->start(); + } +} + + +SFXXAudioDevice::~SFXXAudioDevice() +{ + _releaseAllResources(); + + if ( mMasterVoice ) + { + mMasterVoice->DestroyVoice(); + mMasterVoice = NULL; + } + + // Kill the engine. + SAFE_RELEASE( mXAudio ); +} + + +SFXBuffer* SFXXAudioDevice::createBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ) +{ + SFXXAudioBuffer* buffer = SFXXAudioBuffer::create( stream, description ); + if ( !buffer ) + return NULL; + + _addBuffer( buffer ); + return buffer; +} + +SFXVoice* SFXXAudioDevice::createVoice( bool is3D, SFXBuffer *buffer ) +{ + // Don't bother going any further if we've + // exceeded the maximum voices. + if ( mVoices.size() >= mMaxBuffers ) + return NULL; + + AssertFatal( buffer, "SFXXAudioDevice::createVoice() - Got null buffer!" ); + + SFXXAudioBuffer* xaBuffer = dynamic_cast( buffer ); + AssertFatal( xaBuffer, "SFXXAudioDevice::createVoice() - Got bad buffer!" ); + + SFXXAudioVoice* voice = SFXXAudioVoice::create( mXAudio, is3D, xaBuffer ); + if ( !voice ) + return NULL; + + voice->mXAudioDevice = this; + + _addVoice( voice ); + return voice; +} + +void SFXXAudioDevice::_setOutputMatrix( SFXXAudioVoice *voice ) +{ + X3DAUDIO_DSP_SETTINGS dspSettings = {0}; + FLOAT32 matrix[12] = { 0 }; + dspSettings.DstChannelCount = mMasterVoiceDetails.InputChannels; + dspSettings.pMatrixCoefficients = matrix; + + const X3DAUDIO_EMITTER &emitter = voice->getEmitter(); + dspSettings.SrcChannelCount = emitter.ChannelCount; + + // Calculate the output volumes and doppler. + X3DAudioCalculate( mX3DAudio, + &mListener, + &emitter, + X3DAUDIO_CALCULATE_MATRIX | + X3DAUDIO_CALCULATE_DOPPLER, + &dspSettings ); + + voice->mXAudioVoice->SetOutputMatrix( mMasterVoice, + dspSettings.SrcChannelCount, + dspSettings.DstChannelCount, + dspSettings.pMatrixCoefficients, + 4321 ); + + voice->mXAudioVoice->SetFrequencyRatio( dspSettings.DopplerFactor * voice->mPitch, + 4321 ); + + // Commit the changes. + mXAudio->CommitChanges( 4321 ); +} + +void SFXXAudioDevice::update() +{ + PROFILE_SCOPE( SFXXAudioDevice_Update ); + + Parent::update(); + + X3DAUDIO_DSP_SETTINGS dspSettings = {0}; + FLOAT32 matrix[12] = { 0 }; + dspSettings.DstChannelCount = mMasterVoiceDetails.InputChannels; + dspSettings.pMatrixCoefficients = matrix; + + dspSettings.DopplerFactor = mDopplerFactor; + + // Now update the volume and frequency of + // all the active 3D voices. + VoiceVector::iterator voice = mVoices.begin(); + for ( ; voice != mVoices.end(); voice++ ) + { + SFXXAudioVoice* xaVoice = ( SFXXAudioVoice* ) *voice; + + // Skip 2D or stopped voices. + if ( !xaVoice->is3D() || + xaVoice->getStatus() != SFXStatusPlaying ) + continue; + + const X3DAUDIO_EMITTER &emitter = xaVoice->getEmitter(); + dspSettings.SrcChannelCount = emitter.ChannelCount; + + // Calculate the output volumes and doppler. + X3DAudioCalculate( mX3DAudio, + &mListener, + &emitter, + X3DAUDIO_CALCULATE_MATRIX | + X3DAUDIO_CALCULATE_DOPPLER, + &dspSettings ); + + xaVoice->mXAudioVoice->SetOutputMatrix( mMasterVoice, + dspSettings.SrcChannelCount, + dspSettings.DstChannelCount, + dspSettings.pMatrixCoefficients, + 4321 ) ; + + xaVoice->mXAudioVoice->SetFrequencyRatio( dspSettings.DopplerFactor * xaVoice->mPitch, + 4321 ); + } + + // Commit the changes. + mXAudio->CommitChanges( 4321 ); +} + +void SFXXAudioDevice::setListener( U32 index, const SFXListenerProperties& listener ) +{ + // Get the transform from the listener. + const MatrixF& transform = listener.getTransform(); + transform.getColumn( 3, (Point3F*)&mListener.Position ); + transform.getColumn( 1, (Point3F*)&mListener.OrientFront ); + transform.getColumn( 2, (Point3F*)&mListener.OrientTop ); + + // And the velocity... + const VectorF& velocity = listener.getVelocity(); + mListener.Velocity.x = velocity.x; + mListener.Velocity.y = velocity.y; + mListener.Velocity.z = velocity.z; + + // XAudio and Torque use opposite handedness, so + // flip the z coord to account for that. + mListener.Position.z *= -1.0f; + mListener.OrientFront.z *= -1.0f; + mListener.OrientTop.z *= -1.0f; + mListener.Velocity.z *= -1.0f; +} + +void SFXXAudioDevice::setDistanceModel( SFXDistanceModel model ) +{ + mDistanceModel = model; +} + +void SFXXAudioDevice::setDopplerFactor( F32 factor ) +{ + mDopplerFactor = factor; +} + +void SFXXAudioDevice::setRolloffFactor( F32 factor ) +{ + mRolloffFactor = factor; +} diff --git a/Engine/source/sfx/xaudio/sfxXAudioDevice.h b/Engine/source/sfx/xaudio/sfxXAudioDevice.h new file mode 100644 index 000000000..7e1d8a7ee --- /dev/null +++ b/Engine/source/sfx/xaudio/sfxXAudioDevice.h @@ -0,0 +1,101 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXXAUDIODEVICE_H_ +#define _SFXXAUDIODEVICE_H_ + +class SFXProvider; + +#ifndef _SFXDEVICE_H_ +#include "sfx/sfxDevice.h" +#endif + +#ifndef _SFXPROVIDER_H_ +#include "sfx/sfxProvider.h" +#endif + +#ifndef _SFXXAUDIOVOICE_H_ +#include "sfx/xaudio/sfxXAudioVoice.h" +#endif + +#ifndef _SFXXAUDIOBUFFER_H_ +#include "sfx/xaudio/sfxXAudioBuffer.h" +#endif + +#include +#include + + +class SFXXAudioDevice : public SFXDevice +{ + public: + + typedef SFXDevice Parent; + friend class SFXXAudioVoice; // mXAudio + + protected: + + /// The XAudio engine interface passed + /// on creation from the provider. + IXAudio2 *mXAudio; + + /// The X3DAudio instance. + X3DAUDIO_HANDLE mX3DAudio; + + /// The one and only mastering voice. + IXAudio2MasteringVoice* mMasterVoice; + + /// The details of the master voice. + XAUDIO2_VOICE_DETAILS mMasterVoiceDetails; + + /// The one listener. + X3DAUDIO_LISTENER mListener; + + SFXDistanceModel mDistanceModel; + F32 mRolloffFactor; + F32 mDopplerFactor; + + public: + + SFXXAudioDevice( SFXProvider* provider, + const String& name, + IXAudio2 *xaudio, + U32 deviceIndex, + U32 speakerChannelMask, + U32 maxBuffers ); + + virtual ~SFXXAudioDevice(); + + // SFXDevice + virtual SFXBuffer* createBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description ); + virtual SFXVoice* createVoice( bool is3D, SFXBuffer *buffer ); + virtual void update(); + virtual void setListener( U32 index, const SFXListenerProperties& listener ); + virtual void setDistanceModel( SFXDistanceModel model ); + virtual void setRolloffFactor( F32 factor ); + virtual void setDopplerFactor( F32 factor ); + + /// Called from the voice when its about to start playback. + void _setOutputMatrix( SFXXAudioVoice *voice ); +}; + +#endif // _SFXXAUDIODEVICE_H_ \ No newline at end of file diff --git a/Engine/source/sfx/xaudio/sfxXAudioProvider.cpp b/Engine/source/sfx/xaudio/sfxXAudioProvider.cpp new file mode 100644 index 000000000..0390c9957 --- /dev/null +++ b/Engine/source/sfx/xaudio/sfxXAudioProvider.cpp @@ -0,0 +1,193 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// Note: This must be defined before platform.h so that +// CoInitializeEx is properly included. +#define _WIN32_DCOM +#include + +#include "sfx/xaudio/sfxXAudioDevice.h" +#include "sfx/sfxProvider.h" +#include "core/util/safeRelease.h" +#include "core/strings/unicode.h" +#include "core/strings/stringFunctions.h" +#include "console/console.h" +#include "core/module.h" + + +class SFXXAudioProvider : public SFXProvider +{ +public: + + SFXXAudioProvider() + : SFXProvider( "XAudio" ) {} + virtual ~SFXXAudioProvider(); + +protected: + + /// Extended SFXDeviceInfo to also store some + /// extra XAudio specific data. + struct XADeviceInfo : SFXDeviceInfo + { + UINT32 deviceIndex; + + XAUDIO2_DEVICE_ROLE role; + + WAVEFORMATEXTENSIBLE format; + }; + + /// Helper for creating the XAudio engine. + static bool _createXAudio( IXAudio2 **xaudio ); + +public: + + // SFXProvider + void init(); + SFXDevice* createDevice( const String& deviceName, bool useHardware, S32 maxBuffers ); + +}; + +MODULE_BEGIN( XAudio ) + + MODULE_INIT_BEFORE( SFX ) + MODULE_SHUTDOWN_AFTER( SFX ) + + SFXXAudioProvider* mProvider; + + MODULE_INIT + { + mProvider = new SFXXAudioProvider; + } + + MODULE_SHUTDOWN + { + delete mProvider; + } + +MODULE_END; + +SFXXAudioProvider::~SFXXAudioProvider() +{ +} + +void SFXXAudioProvider::init() +{ + // Create a temp XAudio object for device enumeration. + IXAudio2 *xAudio = NULL; + if ( !_createXAudio( &xAudio ) ) + { + Con::errorf( "SFXXAudioProvider::init() - XAudio2 failed to load!" ); + return; + } + + // Add the devices to the info list. + UINT32 count = 0; + xAudio->GetDeviceCount( &count ); + for ( UINT32 i = 0; i < count; i++ ) + { + XAUDIO2_DEVICE_DETAILS details; + HRESULT hr = xAudio->GetDeviceDetails( i, &details ); + if ( FAILED( hr ) ) + continue; + + // Add a device to the info list. + XADeviceInfo* info = new XADeviceInfo; + info->deviceIndex = i; + info->driver = String( "XAudio" ); + info->name = String( details.DisplayName ); + info->hasHardware = false; + info->maxBuffers = 64; + info->role = details.Role; + info->format = details.OutputFormat; + mDeviceInfo.push_back( info ); + } + + // We're done with XAudio for now. + SAFE_RELEASE( xAudio ); + + // If we have no devices... we're done. + if ( mDeviceInfo.empty() ) + { + Con::errorf( "SFXXAudioProvider::init() - No valid XAudio2 devices found!" ); + return; + } + + // If we got this far then we should be able to + // safely create a device for XAudio. + regProvider( this ); +} + +bool SFXXAudioProvider::_createXAudio( IXAudio2 **xaudio ) +{ + // In debug builds enable the debug version + // of the XAudio engine. + #ifdef TORQUE_DEBUG + #define XAUDIO_FLAGS XAUDIO2_DEBUG_ENGINE + #else + #define XAUDIO_FLAGS 0 + #endif + +#ifndef TORQUE_OS_XENON + // This must be called first... it doesn't hurt to + // call it more than once. + CoInitialize( NULL ); +#endif + + // Try creating the xaudio engine. + HRESULT hr = XAudio2Create( xaudio, XAUDIO_FLAGS, XAUDIO2_DEFAULT_PROCESSOR ); + + return SUCCEEDED( hr ) && (*xaudio); +} + +SFXDevice* SFXXAudioProvider::createDevice( const String& deviceName, bool useHardware, S32 maxBuffers ) +{ + String devName; + + // On the 360, ignore what the prefs say, and create the only audio device +#ifndef TORQUE_OS_XENON + devName = deviceName; +#endif + + XADeviceInfo* info = dynamic_cast< XADeviceInfo* >( _findDeviceInfo( devName ) ); + + // Do we find one to create? + if ( info ) + { + // Create the XAudio object to pass to the device. + IXAudio2 *xAudio = NULL; + if ( !_createXAudio( &xAudio ) ) + { + Con::errorf( "SFXXAudioProvider::createDevice() - XAudio2 failed to load!" ); + return NULL; + } + + return new SFXXAudioDevice( this, + devName, + xAudio, + info->deviceIndex, + info->format.dwChannelMask, + maxBuffers ); + } + + // We didn't find a matching valid device. + return NULL; +} diff --git a/Engine/source/sfx/xaudio/sfxXAudioVoice.cpp b/Engine/source/sfx/xaudio/sfxXAudioVoice.cpp new file mode 100644 index 000000000..80ba7745c --- /dev/null +++ b/Engine/source/sfx/xaudio/sfxXAudioVoice.cpp @@ -0,0 +1,445 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "sfx/xaudio/sfxXAudioVoice.h" +#include "sfx/xaudio/sfxXAudioDevice.h" +#include "sfx/xaudio/sfxXAudioBuffer.h" +#include "core/util/safeDelete.h" +#include "math/mMathFn.h" + + +//#define DEBUG_SPEW + + +static void sfxFormatToWAVEFORMATEX( const SFXFormat& format, WAVEFORMATEX *wfx ) +{ + dMemset( wfx, 0, sizeof( WAVEFORMATEX ) ); + wfx->wFormatTag = WAVE_FORMAT_PCM; + wfx->nChannels = format.getChannels(); + wfx->nSamplesPerSec = format.getSamplesPerSecond(); + wfx->wBitsPerSample = format.getBitsPerChannel(); + wfx->nBlockAlign = wfx->nChannels * wfx->wBitsPerSample / 8; + wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign; +} + + +SFXXAudioVoice* SFXXAudioVoice::create( IXAudio2 *xaudio, + bool is3D, + SFXXAudioBuffer *buffer, + SFXXAudioVoice* inVoice ) +{ + AssertFatal( xaudio, "SFXXAudioVoice::create() - Got null XAudio!" ); + AssertFatal( buffer, "SFXXAudioVoice::create() - Got null buffer!" ); + + // Create the voice object first as it also the callback object. + SFXXAudioVoice* voice = inVoice; + if( !voice ) + voice = new SFXXAudioVoice( buffer ); + + // Get the buffer format. + WAVEFORMATEX wfx; + sfxFormatToWAVEFORMATEX( buffer->getFormat(), &wfx ); + + // We don't support multi-channel 3d sounds! + if ( is3D && wfx.nChannels > 1 ) + return NULL; + + // Create the voice. + IXAudio2SourceVoice *xaVoice; + HRESULT hr = xaudio->CreateSourceVoice( &xaVoice, + (WAVEFORMATEX*)&wfx, + 0, + XAUDIO2_DEFAULT_FREQ_RATIO, + voice, + NULL, + NULL ); + + if( FAILED( hr ) || !voice ) + { + if( !inVoice ) + delete voice; + return NULL; + } + + voice->mIs3D = is3D; + voice->mEmitter.ChannelCount = wfx.nChannels; + voice->mXAudioVoice = xaVoice; + + return voice; +} + +SFXXAudioVoice::SFXXAudioVoice( SFXXAudioBuffer* buffer ) + : Parent( buffer ), + mXAudioDevice( NULL ), + mXAudioVoice( NULL ), + mIs3D( false ), + mPitch( 1.0f ), + mHasStopped( false ), + mHasStarted( false ), + mIsLooping( false ), + mIsPlaying( false ), + mNonStreamSampleStartPos( 0 ), + mNonStreamBufferLoaded( false ), + mSamplesPlayedOffset( 0 ) +{ + dMemset( &mEmitter, 0, sizeof( mEmitter ) ); + mEmitter.DopplerScaler = 1.0f; + + InitializeCriticalSection( &mLock ); +} + +SFXXAudioVoice::~SFXXAudioVoice() +{ + if ( mEmitter.pVolumeCurve ) + { + SAFE_DELETE_ARRAY( mEmitter.pVolumeCurve->pPoints ); + SAFE_DELETE( mEmitter.pVolumeCurve ); + } + + SAFE_DELETE( mEmitter.pCone ); + + if ( mXAudioVoice ) + mXAudioVoice->DestroyVoice(); + + DeleteCriticalSection( &mLock ); +} + +SFXStatus SFXXAudioVoice::_status() const +{ + if( mHasStopped ) + return SFXStatusStopped; + else if( mHasStarted ) + { + if( !mIsPlaying ) + return SFXStatusPaused; + else + return SFXStatusPlaying; + } + else + return SFXStatusStopped; +} + +void SFXXAudioVoice::_flush() +{ + AssertFatal( mXAudioVoice != NULL, + "SFXXAudioVoice::_flush() - invalid voice" ); + + EnterCriticalSection( &mLock ); + + mXAudioVoice->Stop( 0 ); + mXAudioVoice->FlushSourceBuffers(); + mNonStreamBufferLoaded = false; + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXXAudioVoice] Flushed state" ); + #endif + + mIsPlaying = false; + mHasStarted = false; + mHasStopped = true; + + //WORKAROUND: According to the docs, SamplesPlayed reported by the + // voice should get reset as soon as we submit a new buffer to the voice. + // Alas it won't. So, save the current value here and offset our future + // play cursors. + + XAUDIO2_VOICE_STATE state; + mXAudioVoice->GetState( &state ); + mSamplesPlayedOffset = - S32( state.SamplesPlayed ); + + LeaveCriticalSection( &mLock ); +} + +void SFXXAudioVoice::_play() +{ + AssertFatal( mXAudioVoice != NULL, + "SFXXAudioVoice::_play() - invalid voice" ); + + // For non-streaming voices queue the data if we haven't yet. + + if( !mBuffer->isStreaming() && !mNonStreamBufferLoaded ) + _loadNonStreamed(); + + // Start playback. + + mXAudioVoice->Start( 0, 0 ); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXXAudioVoice] Started playback" ); + #endif + + mIsPlaying = true; + mHasStarted = true; + mHasStopped = false; +} + +void SFXXAudioVoice::_pause() +{ + AssertFatal( mXAudioVoice != NULL, + "SFXXAudioVoice::_pause() - invalid voice" ); + + mXAudioVoice->Stop( 0 ); + mIsPlaying = false; + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXXAudioVoice] Paused playback" ); + #endif +} + +void SFXXAudioVoice::_stop() +{ + AssertFatal( mXAudioVoice != NULL, + "SFXXAudioVoice::_stop() - invalid voice" ); + + _flush(); + + mIsPlaying = false; + mHasStarted = false; + mHasStopped = true; + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXXAudioVoice] Stopped playback" ); + #endif +} + +void SFXXAudioVoice::_seek( U32 sample ) +{ + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXXAudioVoice] Seeking to %i", sample ); + #endif + + mNonStreamSampleStartPos = sample; + + bool wasPlaying = mIsPlaying; + + _stop(); + if( wasPlaying ) + _play(); +} + +void SFXXAudioVoice::_loadNonStreamed() +{ + AssertFatal( !mBuffer->isStreaming(), "SFXXAudioVoice::_loadNonStreamed - must not be called on streaming voices" ); + AssertFatal( mXAudioVoice != NULL, "SFXXAudioVoice::_loadNonStreamed - invalid voice" ); + AssertWarn( !mNonStreamBufferLoaded, "SFXXAudioVoice::_nonStreamNonstreamed - Data already loaded" ); + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[SFXXAudioVoice] Loading non-stream buffer at %i", mNonStreamSampleStartPos ); + #endif + + EnterCriticalSection( &mLock ); + + const XAUDIO2_BUFFER& orgBuffer = _getBuffer()->mBufferQueue.front().mData; + + mNonStreamBuffer = orgBuffer; + + if( mNonStreamSampleStartPos ) + { + mNonStreamBuffer.PlayBegin = mNonStreamSampleStartPos; + mNonStreamBuffer.PlayLength = _getBuffer()->getNumSamples() - mNonStreamSampleStartPos; + mSamplesPlayedOffset += mNonStreamSampleStartPos; // Add samples that we are skipping. + mNonStreamSampleStartPos = 0; + } + + if( mIsLooping ) + { + mNonStreamBuffer.LoopCount = XAUDIO2_LOOP_INFINITE; + mNonStreamBuffer.LoopLength = _getBuffer()->getNumSamples(); + } + + // Submit buffer. + + mXAudioVoice->SubmitSourceBuffer( &mNonStreamBuffer ); + mNonStreamBufferLoaded = true; + + LeaveCriticalSection( &mLock ); +} + +U32 SFXXAudioVoice::_tell() const +{ + XAUDIO2_VOICE_STATE state; + mXAudioVoice->GetState( &state ); + + // Workaround SamplesPlayed not getting reset. + return ( state.SamplesPlayed + mSamplesPlayedOffset ); +} + +void SFXXAudioVoice::setMinMaxDistance( F32 min, F32 max ) +{ + // Set the overall volume curve scale. + mEmitter.CurveDistanceScaler = max; + + // The curve uses normalized distances, so + // figure out the normalized min distance. + F32 normMin = 0.0f; + if ( min > 0.0f ) + normMin = min / max; + + // See what type of curve we are supposed to generate. + const bool linear = ( mXAudioDevice->mDistanceModel == SFXDistanceModelLinear ); + + // Have we setup the curve yet? + if( !mEmitter.pVolumeCurve + || ( linear && mEmitter.pVolumeCurve->PointCount != 2 ) + || ( !linear && mEmitter.pVolumeCurve->PointCount != 6 ) ) + { + if( !mEmitter.pVolumeCurve ) + mEmitter.pVolumeCurve = new X3DAUDIO_DISTANCE_CURVE; + else + SAFE_DELETE_ARRAY( mEmitter.pVolumeCurve->pPoints ); + + // We use 6 points for logarithmic volume curves and 2 for linear volume curves. + if( linear ) + { + mEmitter.pVolumeCurve->pPoints = new X3DAUDIO_DISTANCE_CURVE_POINT[ 2 ]; + mEmitter.pVolumeCurve->PointCount = 2; + } + else + { + mEmitter.pVolumeCurve->pPoints = new X3DAUDIO_DISTANCE_CURVE_POINT[ 6 ]; + mEmitter.pVolumeCurve->PointCount = 6; + } + + // The first and last points are known + // and will not change. + mEmitter.pVolumeCurve->pPoints[ 0 ].Distance = 0.0f; + mEmitter.pVolumeCurve->pPoints[ 0 ].DSPSetting = 1.0f; + mEmitter.pVolumeCurve->pPoints[ linear ? 1 : 5 ].Distance = 1.0f; + mEmitter.pVolumeCurve->pPoints[ linear ? 1 : 5 ].DSPSetting = 0.0f; + } + + if( !linear ) + { + // Set the second point of the curve. + mEmitter.pVolumeCurve->pPoints[1].Distance = normMin; + mEmitter.pVolumeCurve->pPoints[1].DSPSetting = 1.0f; + + // The next three points are calculated to + // give the sound a rough logarithmic falloff. + F32 distStep = ( 1.0f - normMin ) / 4.0f; + for ( U32 i=0; i < 3; i++ ) + { + U32 index = 2 + i; + F32 dist = normMin + ( distStep * (F32)( i + 1 ) ); + + mEmitter.pVolumeCurve->pPoints[index].Distance = dist; + mEmitter.pVolumeCurve->pPoints[index].DSPSetting = 1.0f - log10( dist * 10.0f ); + } + } +} + +void SFXXAudioVoice::OnBufferEnd( void* bufferContext ) +{ + if( mBuffer->isStreaming() ) + SFXInternal::TriggerUpdate(); +} + +void SFXXAudioVoice::OnStreamEnd() +{ + // Warning: This is being called within the XAudio + // thread, so be sure you're thread safe! + + mHasStopped = true; + + if( mBuffer->isStreaming() ) + SFXInternal::TriggerUpdate(); + else + _stop(); +} + +void SFXXAudioVoice::play( bool looping ) +{ + // Give the device a chance to calculate our positional + // audio settings before we start playback... this is + // important else we get glitches. + if( mIs3D ) + mXAudioDevice->_setOutputMatrix( this ); + + mIsLooping = looping; + Parent::play( looping ); +} + +void SFXXAudioVoice::setVelocity( const VectorF& velocity ) +{ + mEmitter.Velocity.x = velocity.x; + mEmitter.Velocity.y = velocity.y; + + // XAudio and Torque use opposite handedness, so + // flip the z coord to account for that. + mEmitter.Velocity.z = -velocity.z; +} + +void SFXXAudioVoice::setTransform( const MatrixF& transform ) +{ + transform.getColumn( 3, (Point3F*)&mEmitter.Position ); + transform.getColumn( 1, (Point3F*)&mEmitter.OrientFront ); + transform.getColumn( 2, (Point3F*)&mEmitter.OrientTop ); + + // XAudio and Torque use opposite handedness, so + // flip the z coord to account for that. + mEmitter.Position.z *= -1.0f; + mEmitter.OrientFront.z *= -1.0f; + mEmitter.OrientTop.z *= -1.0f; +} + +void SFXXAudioVoice::setVolume( F32 volume ) +{ + mXAudioVoice->SetVolume( volume ); +} + +void SFXXAudioVoice::setPitch( F32 pitch ) +{ + mPitch = mClampF( pitch, XAUDIO2_MIN_FREQ_RATIO, XAUDIO2_DEFAULT_FREQ_RATIO ); + mXAudioVoice->SetFrequencyRatio( mPitch ); +} + +void SFXXAudioVoice::setCone( F32 innerAngle, F32 outerAngle, F32 outerVolume ) +{ + // If the cone is set to 360 then the + // cone is null and doesn't need to be + // set on the voice. + if ( mIsEqual( innerAngle, 360 ) ) + { + SAFE_DELETE( mEmitter.pCone ); + return; + } + + if ( !mEmitter.pCone ) + { + mEmitter.pCone = new X3DAUDIO_CONE; + + // The inner volume is always 1... the overall + // volume is what scales it. + mEmitter.pCone->InnerVolume = 1.0f; + + // We don't use these yet. + mEmitter.pCone->InnerLPF = 0.0f; + mEmitter.pCone->OuterLPF = 0.0f; + mEmitter.pCone->InnerReverb = 0.0f; + mEmitter.pCone->OuterReverb = 0.0f; + } + + mEmitter.pCone->InnerAngle = mDegToRad( innerAngle ); + mEmitter.pCone->OuterAngle = mDegToRad( outerAngle ); + mEmitter.pCone->OuterVolume = outerVolume; +} diff --git a/Engine/source/sfx/xaudio/sfxXAudioVoice.h b/Engine/source/sfx/xaudio/sfxXAudioVoice.h new file mode 100644 index 000000000..93500fcc4 --- /dev/null +++ b/Engine/source/sfx/xaudio/sfxXAudioVoice.h @@ -0,0 +1,154 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SFXXAUDIOVOICE_H_ +#define _SFXXAUDIOVOICE_H_ + +#include +#include + +#include "sfx/sfxVoice.h" + + +class SFXXAudioBuffer; + + +class SFXXAudioVoice : public SFXVoice, + public IXAudio2VoiceCallback +{ + public: + + typedef SFXVoice Parent; + + friend class SFXXAudioDevice; + friend class SFXXAudioBuffer; + + protected: + + /// This constructor does not create a valid voice. + /// @see SFXXAudioVoice::create + SFXXAudioVoice( SFXXAudioBuffer* buffer ); + + /// The device that created us. + SFXXAudioDevice *mXAudioDevice; + + /// The XAudio voice. + IXAudio2SourceVoice *mXAudioVoice; + + /// + CRITICAL_SECTION mLock; + + /// Used to know what sounds need positional updates. + bool mIs3D; + + /// Whether the voice has stopped playing. + mutable bool mHasStopped; + + /// Whether we have started playback. + bool mHasStarted; + + /// Whether playback is currently running. + bool mIsPlaying; + + /// Whether playback is looping. + bool mIsLooping; + + /// Since 3D sounds are pitch shifted for doppler + /// effect we need to track the users base pitch. + F32 mPitch; + + /// The cached X3DAudio emitter data. + X3DAUDIO_EMITTER mEmitter; + + /// XAudio does not reset the SamplesPlayed count as is stated in the docs. To work around + /// that, we offset the values reported by XAudio through this field. + S32 mSamplesPlayedOffset; + + /// @name Data for Non-Streaming Voices + /// @{ + + /// Whether we have loaded our non-streaming data. We use an explicit + /// flag here as we really can't rely on XAUDIO2_VOICE_STATE. + bool mNonStreamBufferLoaded; + + /// Audio buffer for non-streaming voice. + XAUDIO2_BUFFER mNonStreamBuffer; + + /// Sample to start playing at when seting up #mNonStreamBuffer. + U32 mNonStreamSampleStartPos; + + /// @} + + // IXAudio2VoiceCallback + void STDMETHODCALLTYPE OnStreamEnd(); + void STDMETHODCALLTYPE OnVoiceProcessingPassStart( UINT32 BytesRequired ) {} + void STDMETHODCALLTYPE OnVoiceProcessingPassEnd() {} + void STDMETHODCALLTYPE OnBufferEnd( void *bufferContext ); + void STDMETHODCALLTYPE OnBufferStart( void *bufferContext ) {} + void STDMETHODCALLTYPE OnLoopEnd( void *bufferContext ) {} + void STDMETHODCALLTYPE OnVoiceError( void * bufferContext, HRESULT error ) {} + + /// @deprecated This is only here for compatibility with + /// the March 2008 SDK version of IXAudio2VoiceCallback. + void STDMETHODCALLTYPE OnVoiceProcessingPassStart() {} + + void _flush(); + void _loadNonStreamed(); + + // SFXVoice. + virtual SFXStatus _status() const; + virtual void _play(); + virtual void _pause(); + virtual void _stop(); + virtual void _seek( U32 sample ); + virtual U32 _tell() const; + + SFXXAudioBuffer* _getBuffer() const { return ( SFXXAudioBuffer* ) mBuffer.getPointer(); } + + public: + + /// + static SFXXAudioVoice* create( IXAudio2 *xaudio, + bool is3D, + SFXXAudioBuffer *buffer, + SFXXAudioVoice* inVoice = NULL ); + + /// + virtual ~SFXXAudioVoice(); + + // SFXVoice + void setMinMaxDistance( F32 min, F32 max ); + void play( bool looping ); + void setVelocity( const VectorF& velocity ); + void setTransform( const MatrixF& transform ); + void setVolume( F32 volume ); + void setPitch( F32 pitch ); + void setCone( F32 innerAngle, F32 outerAngle, F32 outerVolume ); + + /// Is this a 3D positional voice. + bool is3D() const { return mIs3D; } + + /// + const X3DAUDIO_EMITTER& getEmitter() const { return mEmitter; } +}; + +#endif // _SFXXAUDIOVOICE_H_ \ No newline at end of file diff --git a/Engine/source/shaderGen/GLSL/bumpGLSL.cpp b/Engine/source/shaderGen/GLSL/bumpGLSL.cpp new file mode 100644 index 000000000..737ddcbaf --- /dev/null +++ b/Engine/source/shaderGen/GLSL/bumpGLSL.cpp @@ -0,0 +1,448 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "shaderGen/GLSL/bumpGLSL.h" + +#include "shaderGen/shaderOp.h" +#include "gfx/gfxDevice.h" +#include "materials/matInstance.h" +#include "materials/processedMaterial.h" +#include "materials/materialFeatureTypes.h" +#include "shaderGen/shaderGenVars.h" + + +void BumpFeatGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + output = meta; + + const bool useTexAnim = fd.features[MFT_TexAnim]; + + // Output the texture coord. + getOutTexCoord( "texCoord", + "vec2", + true, + useTexAnim, + meta, + componentList ); + + if ( fd.features.hasFeature( MFT_DetailNormalMap ) ) + addOutDetailTexCoord( componentList, + meta, + useTexAnim ); + + // Also output the worldToTanget transform which + // we use to create the world space normal. + getOutWorldToTangent( componentList, meta, fd ); +} + +void BumpFeatGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + output = meta; + + // Get the texture coord. + Var *texCoord = getInTexCoord( "out_texCoord", "vec2", true, componentList ); + + // Sample the bumpmap. + Var *bumpMap = getNormalMapTex(); + + LangElement *texOp = NULL; + + //Handle atlased textures + if(fd.features[MFT_NormalMapAtlas]) + { + // This is a big block of code, so put a comment in the shader code + meta->addStatement( new GenOp( " // Atlased texture coordinate calculation (see BumpFeat*LSL for details)\r\n") ); + + Var *atlasedTex = new Var; + atlasedTex->setName("atlasedBumpCoord"); + atlasedTex->setType("vec2"); + LangElement *atDecl = new DecOp(atlasedTex); + + // Parameters of the texture atlas + Var *atParams = new Var; + atParams->setType("vec4"); + atParams->setName("bumpAtlasParams"); + atParams->uniform = true; + atParams->constSortPos = cspPotentialPrimitive; + + // Parameters of the texture (tile) this object is using in the atlas + Var *tileParams = new Var; + tileParams->setType("vec4"); + tileParams->setName("bumpAtlasTileParams"); + tileParams->uniform = true; + tileParams->constSortPos = cspPotentialPrimitive; + + const bool is_sm3 = (GFX->getPixelShaderVersion() > 2.0f); + // getPixelShaderVersion() on Mac currently returns 2.0, + // or 3.0 if Advanced Lighting is enabled + if(is_sm3) + { + // Figure out the mip level + meta->addStatement(new GenOp(" vec2 _dx_bump = dFdx(@ * @.z);\r\n", texCoord, atParams)); + meta->addStatement(new GenOp(" vec2 _dy_bump = dFdy(@ * @.z);\r\n", texCoord, atParams)); + meta->addStatement(new GenOp(" float mipLod_bump = 0.5 * log2(max(dot(_dx_bump, _dx_bump), dot(_dy_bump, _dy_bump)));\r\n")); + meta->addStatement(new GenOp(" mipLod_bump = clamp(mipLod_bump, 0.0, @.w);\r\n", atParams)); + + // And the size of the mip level + meta->addStatement(new GenOp(" float mipPixSz_bump = pow(2.0, @.w - mipLod_bump);\r\n", atParams)); + meta->addStatement(new GenOp(" vec2 mipSz_bump = mipPixSz_bump / @.xy;\r\n", atParams)); + } + else + { + meta->addStatement(new GenOp(" vec2 mipSz = float2(1.0, 1.0);\r\n")); + } + + // Tiling mode + // TODO: Select wrap or clamp somehow + if( true ) // Wrap + meta->addStatement(new GenOp(" @ = fract(@);\r\n", atDecl, texCoord)); + else // Clamp + meta->addStatement(new GenOp(" @ = saturate(@);\r\n", atDecl, texCoord)); + + // Finally scale/offset, and correct for filtering + meta->addStatement(new GenOp(" @ = @ * ((mipSz_bump * @.xy - 1.0) / mipSz_bump) + 0.5 / mipSz_bump + @.xy * @.xy;\r\n", + atlasedTex, atlasedTex, atParams, atParams, tileParams)); + + // Add a newline + meta->addStatement(new GenOp( "\r\n")); + + if(is_sm3) + { + texOp = new GenOp( "texture2DLod(@, vec4(@, 0.0, mipLod_bump)", bumpMap, texCoord ); + } + else + { + texOp = new GenOp( "texture2D(@, @)", bumpMap, texCoord ); + } + } + else + { + texOp = new GenOp( "texture2D(@, @)", bumpMap, texCoord ); + } + + Var *bumpNorm = new Var( "bumpNormal", "vec4" ); + meta->addStatement( expandNormalMap( texOp, new DecOp( bumpNorm ), bumpNorm, fd ) ); + + // If we have a detail normal map we add the xy coords of + // it to the base normal map. This gives us the effect we + // want with few instructions and minial artifacts. + if ( fd.features.hasFeature( MFT_DetailNormalMap ) ) + { + bumpMap = new Var; + bumpMap->setType( "sampler2D" ); + bumpMap->setName( "detailBumpMap" ); + bumpMap->uniform = true; + bumpMap->sampler = true; + bumpMap->constNum = Var::getTexUnitNum(); + + texCoord = getInTexCoord( "detCoord", "vec2", true, componentList ); + texOp = new GenOp( "texture2D(@, @)", bumpMap, texCoord ); + + Var *detailBump = new Var; + detailBump->setName( "detailBump" ); + detailBump->setType( "vec4" ); + meta->addStatement( expandNormalMap( texOp, new DecOp( detailBump ), detailBump, fd ) ); + + Var *detailBumpScale = new Var; + detailBumpScale->setType( "float" ); + detailBumpScale->setName( "detailBumpStrength" ); + detailBumpScale->uniform = true; + detailBumpScale->constSortPos = cspPass; + meta->addStatement( new GenOp( " @.xy += @.xy * @;\r\n", bumpNorm, detailBump, detailBumpScale ) ); + } + + + // We transform it into world space by reversing the + // multiplication by the worldToTanget transform. + Var *wsNormal = new Var( "wsNormal", "vec3" ); + Var *worldToTanget = getInWorldToTangent( componentList ); + meta->addStatement( new GenOp( " @ = normalize( vec3( @.xyz * @ ) );\r\n", new DecOp( wsNormal ), bumpNorm, worldToTanget ) ); + +} + +ShaderFeature::Resources BumpFeatGLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + + // If we have no parallax then we bring on the normal tex. + if ( !fd.features[MFT_Parallax] ) + res.numTex = 1; + + // Only the parallax or diffuse map will add texture + // coords other than us. + if ( !fd.features[MFT_Parallax] && + !fd.features[MFT_DiffuseMap] && + !fd.features[MFT_OverlayMap] && + !fd.features[MFT_DetailMap] ) + res.numTexReg++; + + // We pass the world to tanget space transform. + res.numTexReg += 3; + + // Do we have detail normal mapping? + if ( fd.features[MFT_DetailNormalMap] ) + { + res.numTex++; + if ( !fd.features[MFT_DetailMap] ) + res.numTexReg++; + } + + return res; +} + +void BumpFeatGLSL::setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ) +{ + // If we had a parallax feature then it takes + // care of hooking up the normal map texture. + if ( fd.features[MFT_Parallax] ) + return; + + if ( fd.features[MFT_NormalMap] ) + { + passData.mTexType[ texIndex ] = Material::Bump; + passData.mTexSlot[ texIndex++ ].texObject = stageDat.getTex( MFT_NormalMap ); + } + + + if ( fd.features[ MFT_DetailNormalMap ] ) + { + passData.mTexType[ texIndex ] = Material::DetailBump; + passData.mTexSlot[ texIndex++ ].texObject = stageDat.getTex( MFT_DetailNormalMap ); + } +} + + +// +Var* ParallaxFeatGLSL::_getUniformVar( const char *name, const char *type ) +{ + Var *theVar = (Var*)LangElement::find( name ); + if ( !theVar ) + { + theVar = new Var; + theVar->setType( type ); + theVar->setName( name ); + theVar->uniform = true; + theVar->constSortPos = cspPass; + } + + return theVar; +} + +void ParallaxFeatGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + AssertFatal( GFX->getPixelShaderVersion() >= 2.0, + "ParallaxFeatGLSL::processVert - We don't support SM 1.x!" ); + + MultiLine *meta = new MultiLine; + + // Add the texture coords. + getOutTexCoord( "texCoord", + "vec2", + true, + fd.features[MFT_TexAnim], + meta, + componentList ); + + // Grab the input position. + Var *inPos = (Var*)LangElement::find( "inPosition" ); + if ( !inPos ) + inPos = (Var*)LangElement::find( "position" ); + + // Get the object space eye position and the world + // to tangent transform. + Var *eyePos = _getUniformVar( "eyePos", "vec3" ); + Var *objToTangentSpace = getOutObjToTangentSpace( componentList, meta, fd ); + + // send transform to pixel shader + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outViewTS = connectComp->getElement( RT_TEXCOORD, 1 ); + outViewTS->setName( "outViewTS" ); + outViewTS->setType( "vec3" ); + meta->addStatement( new GenOp( " @ = ( @ - @.xyz ) * transpose( @ );\r\n", + outViewTS, inPos, eyePos, objToTangentSpace ) ); + + output = meta; +} + +void ParallaxFeatGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + AssertFatal( GFX->getPixelShaderVersion() >= 2.0, + "ParallaxFeatGLSL::processPix - We don't support SM 1.x!" ); + + MultiLine *meta = new MultiLine; + + // Order matters... get this first! + Var *texCoord = getInTexCoord( "texCoord", "vec2", true, componentList ); + + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + // We need the negative tangent space view vector + // as in parallax mapping we step towards the camera. + Var *negViewTS = (Var*)LangElement::find( "negViewTS" ); + if ( !negViewTS ) + { + Var *inViewTS = (Var*)LangElement::find( "outViewTS" ); + if ( !inViewTS ) + { + inViewTS = connectComp->getElement( RT_TEXCOORD, 1 ); + inViewTS->setName( "outViewTS" ); + inViewTS->setType( "vec3" ); + } + + negViewTS = new Var( "negViewTS", "vec3" ); + meta->addStatement( new GenOp( " @ = -normalize( @ );\r\n", new DecOp( negViewTS ), inViewTS ) ); + } + + // Get the rest of our inputs. + Var *parallaxInfo = _getUniformVar( "parallaxInfo", "float" ); + Var *normalMap = getNormalMapTex(); + + // Do 3 parallax samples to get acceptable + // quality without too much overhead. + Var *pdepth = findOrCreateLocal( "pdepth", "float", meta ); + Var *poffset = findOrCreateLocal( "poffset", "vec2", meta ); + meta->addStatement( new GenOp( " @ = texture2D( @, @.xy ).a;\r\n", pdepth, normalMap, texCoord ) ); + meta->addStatement( new GenOp( " @ = @.xy * ( @ * @ );\r\n", poffset, negViewTS, pdepth, parallaxInfo ) ); + + meta->addStatement( new GenOp( " @ = ( @ + texture2D( @, @.xy + @ ).a ) * 0.5;\r\n", pdepth, pdepth, normalMap, texCoord, poffset ) ); + meta->addStatement( new GenOp( " @ = @.xy * ( @ * @ );\r\n", poffset, negViewTS, pdepth, parallaxInfo ) ); + + meta->addStatement( new GenOp( " @ = ( @ + texture2D( @, @.xy + @ ).a ) * 0.5;\r\n", pdepth, pdepth, normalMap, texCoord, poffset ) ); + meta->addStatement( new GenOp( " @ = @.xy * ( @ * @ );\r\n", poffset, negViewTS, pdepth, parallaxInfo ) ); + + meta->addStatement( new GenOp( " @.xy += @;\r\n", texCoord, poffset ) ); + + // TODO: Fix second UV. + + output = meta; +} + +ShaderFeature::Resources ParallaxFeatGLSL::getResources( const MaterialFeatureData &fd ) +{ + AssertFatal( GFX->getPixelShaderVersion() >= 2.0, + "ParallaxFeatGLSL::getResources - We don't support SM 1.x!" ); + + Resources res; + + // We add the outViewTS to the outputstructure. + res.numTexReg = 1; + + // If this isn't a prepass then we will be + // creating the normal map here. + if ( !fd.features.hasFeature( MFT_PrePassConditioner ) ) + res.numTex = 1; + + return res; +} + +void ParallaxFeatGLSL::setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ) +{ + AssertFatal( GFX->getPixelShaderVersion() >= 2.0, + "ParallaxFeatGLSL::setTexData - We don't support SM 1.x!" ); + + GFXTextureObject *tex = stageDat.getTex( MFT_NormalMap ); + if ( tex ) + { + passData.mTexType[ texIndex ] = Material::Bump; + passData.mTexSlot[ texIndex++ ].texObject = tex; + } +} + + +// +void NormalsOutFeatGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // If we have normal maps then we can count + // on it to generate the world space normal. + if ( fd.features[MFT_NormalMap] ) + return; + + MultiLine *meta = new MultiLine; + output = meta; + + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + Var *outNormal = connectComp->getElement( RT_TEXCOORD ); + outNormal->setName( "wsNormal" ); + outNormal->setType( "vec3" ); + outNormal->mapsToSampler = false; + + // Find the incoming vertex normal. + Var *inNormal = (Var*)LangElement::find( "normal" ); + if ( inNormal ) + { + // Transform the normal to world space. + Var *objTrans = getObjTrans( componentList, fd.features[MFT_UseInstancing], meta ); + meta->addStatement( new GenOp( " @ = @ * normalize( @ );\r\n", outNormal, objTrans, inNormal ) ); + } + else + { + // If we don't have a vertex normal... just pass the + // camera facing normal to the pixel shader. + meta->addStatement( new GenOp( " @ = vec3( 0.0, 0.0, 1.0 );\r\n", outNormal ) ); + } +} + +void NormalsOutFeatGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + output = meta; + + Var *wsNormal = (Var*)LangElement::find( "wsNormal" ); + if ( !wsNormal ) + { + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + wsNormal = connectComp->getElement( RT_TEXCOORD ); + wsNormal->setName( "wsNormal" ); + wsNormal->setType( "vec3" ); + + // If we loaded the normal its our resposibility + // to normalize it... the interpolators won't. + // + meta->addStatement( new GenOp( " @ = normalize( @ );\r\n", wsNormal, wsNormal ) ); + } + + LangElement *normalOut; + Var *outColor = (Var*)LangElement::find( "col" ); + if ( outColor ) + normalOut = new GenOp( "vec4( ( -@ + 1 ) * 0.5, @.a )", wsNormal, outColor ); + else + normalOut = new GenOp( "vec4( ( -@ + 1 ) * 0.5, 1 )", wsNormal ); + + meta->addStatement( new GenOp( " @;\r\n", + assignColor( normalOut, Material::None ) ) ); +} diff --git a/Engine/source/shaderGen/GLSL/bumpGLSL.h b/Engine/source/shaderGen/GLSL/bumpGLSL.h new file mode 100644 index 000000000..899434f15 --- /dev/null +++ b/Engine/source/shaderGen/GLSL/bumpGLSL.h @@ -0,0 +1,99 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _BUMP_GLSL_H_ +#define _BUMP_GLSL_H_ + +#ifndef _SHADERGEN_GLSL_SHADERFEATUREGLSL_H_ +#include "shaderGen/GLSL/shaderFeatureGLSL.h" +#endif + +struct RenderPassData; +class MultiLine; + + +/// The Bumpmap feature will read the normal map and +/// transform it by the inverse of the worldToTanget +/// matrix. This normal is then used by subsequent +/// shader features. +class BumpFeatGLSL : public ShaderFeatureGLSL +{ +public: + + // ShaderFeatureGLSL + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + virtual Material::BlendOp getBlendOp(){ return Material::LerpAlpha; } + virtual Resources getResources( const MaterialFeatureData &fd ); + virtual void setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ); + + virtual String getName() { return "Bumpmap"; } +}; + + +/// This feature either generates the cheap yet effective offset +/// mapping style parallax or the much more expensive occlusion +/// mapping technique based on the enabled feature flags. +class ParallaxFeatGLSL : public ShaderFeatureGLSL +{ +protected: + + static Var* _getUniformVar( const char *name, const char *type ); + +public: + + // ShaderFeatureGLSL + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + virtual Resources getResources( const MaterialFeatureData &fd ); + virtual void setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ); + virtual String getName() { return "Parallax"; } +}; + + + +/// This feature is used to render normals to the +/// diffuse target for imposter rendering. +class NormalsOutFeatGLSL : public ShaderFeatureGLSL +{ +public: + + // ShaderFeatureGLSL + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + virtual Material::BlendOp getBlendOp(){ return Material::LerpAlpha; } + virtual String getName() { return "NormalsOut"; } +}; + +#endif // _BUMP_GLSL_H_ \ No newline at end of file diff --git a/Engine/source/shaderGen/GLSL/depthGLSL.cpp b/Engine/source/shaderGen/GLSL/depthGLSL.cpp new file mode 100644 index 000000000..4c5f1b837 --- /dev/null +++ b/Engine/source/shaderGen/GLSL/depthGLSL.cpp @@ -0,0 +1,155 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "shaderGen/GLSL/depthGLSL.h" + +#include "materials/materialFeatureTypes.h" + + +void EyeSpaceDepthOutGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + + MultiLine *meta = new MultiLine; + output = meta; + + // grab output + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outWSEyeVec = connectComp->getElement( RT_TEXCOORD ); + outWSEyeVec->setName( "outWSEyeVec" ); + + + // grab incoming vert position + Var *wsPosition = new Var( "depthPos", "vec3" ); + getWsPosition( componentList, fd.features[MFT_UseInstancing], meta, new DecOp( wsPosition ) ); + + Var *eyePos = (Var*)LangElement::find( "eyePosWorld" ); + if( !eyePos ) + { + eyePos = new Var; + eyePos->setType("vec3"); + eyePos->setName("eyePosWorld"); + eyePos->uniform = true; + eyePos->constSortPos = cspPass; + } + +meta->addStatement( new GenOp( " @ = vec4( @.xyz - @, 1 );\r\n", outWSEyeVec, wsPosition, eyePos ) ); +} + +void EyeSpaceDepthOutGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + + // grab connector position + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *wsEyeVec = connectComp->getElement( RT_TEXCOORD ); + wsEyeVec->setName( "outWSEyeVec" ); + wsEyeVec->setType( "vec4" ); + wsEyeVec->mapsToSampler = false; + wsEyeVec->uniform = false; + + // get shader constants + Var *vEye = new Var; + vEye->setType("vec3"); + vEye->setName("vEye"); + vEye->uniform = true; + vEye->constSortPos = cspPass; + + // Expose the depth to the depth format feature + Var *depthOut = new Var; + depthOut->setType("float"); + depthOut->setName(getOutputVarName()); + + LangElement *depthOutDecl = new DecOp( depthOut ); + + meta->addStatement( new GenOp( " @ = dot(@, (@.xyz / @.w));\r\n", depthOutDecl, vEye, wsEyeVec, wsEyeVec ) ); + + // If there isn't an output conditioner for the pre-pass, than just write + // out the depth to rgba and return. + if( !fd.features[MFT_PrePassConditioner] ) + meta->addStatement( new GenOp( " @;\r\n", assignColor( new GenOp( "vec4(@)", depthOut ), Material::None ) ) ); + + output = meta; +} + +ShaderFeature::Resources EyeSpaceDepthOutGLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources temp; + + // Passing from VS->PS: + // - world space position (wsPos) + temp.numTexReg = 1; + + return temp; +} + + +void DepthOutGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + // Grab the output vert. + Var *outPosition = (Var*)LangElement::find( "gl_Position" ); + + // Grab our output depth. + Var *outDepth = connectComp->getElement( RT_TEXCOORD ); + outDepth->setName( "outDepth" ); + outDepth->setType( "float" ); + + output = new GenOp( " @ = @.z / @.w;\r\n", outDepth, outPosition, outPosition ); +} + +void DepthOutGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + // grab connector position + Var *depthVar = connectComp->getElement( RT_TEXCOORD ); + depthVar->setName( "outDepth" ); + depthVar->setType( "float" ); + depthVar->mapsToSampler = false; + depthVar->uniform = false; + + /* + // Expose the depth to the depth format feature + Var *depthOut = new Var; + depthOut->setType("float"); + depthOut->setName(getOutputVarName()); + */ + + LangElement *depthOut = new GenOp( "vec4( @, @ * @, 0, 1 )", depthVar, depthVar, depthVar ); + + output = new GenOp( " @;\r\n", assignColor( depthOut, Material::None ) ); +} + +ShaderFeature::Resources DepthOutGLSL::getResources( const MaterialFeatureData &fd ) +{ + // We pass the depth to the pixel shader. + Resources temp; + temp.numTexReg = 1; + + return temp; +} diff --git a/Engine/source/shaderGen/GLSL/depthGLSL.h b/Engine/source/shaderGen/GLSL/depthGLSL.h new file mode 100644 index 000000000..97cb62095 --- /dev/null +++ b/Engine/source/shaderGen/GLSL/depthGLSL.h @@ -0,0 +1,60 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _DEPTH_GLSL_H_ +#define _DEPTH_GLSL_H_ + +#ifndef _SHADERGEN_GLSL_SHADERFEATUREGLSL_H_ +#include "shaderGen/GLSL/shaderFeatureGLSL.h" +#endif +#ifndef _SHADEROP_H_ +#include "shaderGen/shaderOp.h" +#endif + + +class EyeSpaceDepthOutGLSL : public ShaderFeatureGLSL +{ +public: + + // ShaderFeature + virtual void processVert( Vector &componentList, const MaterialFeatureData &fd ); + virtual void processPix( Vector &componentList, const MaterialFeatureData &fd ); + virtual Resources getResources( const MaterialFeatureData &fd ); + virtual String getName() { return "Eye Space Depth (Out)"; } + virtual Material::BlendOp getBlendOp() { return Material::None; } + virtual const char* getOutputVarName() const { return "eyeSpaceDepth"; } +}; + + +class DepthOutGLSL : public ShaderFeatureGLSL +{ +public: + + // ShaderFeature + virtual void processVert( Vector &componentList, const MaterialFeatureData &fd ); + virtual void processPix( Vector &componentList, const MaterialFeatureData &fd ); + virtual Resources getResources( const MaterialFeatureData &fd ); + virtual String getName() { return "Depth (Out)"; } + virtual Material::BlendOp getBlendOp() { return Material::None; } + virtual const char* getOutputVarName() const { return "outDepth"; } +}; + +#endif // _DEPTH_GLSL_H_ \ No newline at end of file diff --git a/Engine/source/shaderGen/GLSL/paraboloidGLSL.cpp b/Engine/source/shaderGen/GLSL/paraboloidGLSL.cpp new file mode 100644 index 000000000..373712c36 --- /dev/null +++ b/Engine/source/shaderGen/GLSL/paraboloidGLSL.cpp @@ -0,0 +1,158 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "shaderGen/GLSL/paraboloidGLSL.h" + +#include "lighting/lightInfo.h" +#include "materials/sceneData.h" +#include "materials/materialFeatureTypes.h" +#include "materials/materialFeatureData.h" +#include "gfx/gfxShader.h" + + +void ParaboloidVertTransformGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + + // First check for an input position from a previous feature + // then look for the default vertex position. + Var *inPosition = (Var*)LangElement::find( "inPosition" ); + if ( !inPosition ) + inPosition = (Var*)LangElement::find( "position" ); + + const bool isSinglePass = fd.features[ MFT_IsSinglePassParaboloid ]; + + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + // Grab connector out position. + Var *outPosition = connectComp->getElement( RT_POSITION ); + outPosition->setName( "gl_Position" ); + + // Get the atlas scale. + Var *atlasScale = new Var; + atlasScale->setType( "vec2" ); + atlasScale->setName( "atlasScale" ); + atlasScale->uniform = true; + atlasScale->constSortPos = cspPass; + + // Transform into camera space + Var *worldViewOnly = getWorldView( componentList, fd.features[MFT_UseInstancing], meta ); + + // So what we're doing here is transforming into camera space, and + // then directly manipulate into shadowmap space. + // + // http://www.gamedev.net/reference/articles/article2308.asp + + // Swizzle z and y post-transform + meta->addStatement( new GenOp( " @ = vec4(@ * vec4(@.xyz,1)).xzyw;\r\n", outPosition, worldViewOnly, inPosition ) ); + meta->addStatement( new GenOp( " float L = length(@.xyz);\r\n", outPosition ) ); + + if ( isSinglePass ) + { + // Flip the z in the back case + Var *outIsBack = connectComp->getElement( RT_TEXCOORD ); + outIsBack->setType( "float" ); + outIsBack->setName( "outIsBack" ); + + meta->addStatement( new GenOp( " bool isBack = @.z < 0.0;\r\n", outPosition ) ); + meta->addStatement( new GenOp( " @ = isBack ? -1.0 : 1.0;\r\n", outIsBack ) ); + meta->addStatement( new GenOp( " if ( isBack ) @.z = -@.z;\r\n", outPosition, outPosition ) ); + } + + meta->addStatement( new GenOp( " @ /= L;\r\n", outPosition ) ); + meta->addStatement( new GenOp( " @.z = @.z + 1.0;\r\n", outPosition, outPosition ) ); + meta->addStatement( new GenOp( " @.xy /= @.z;\r\n", outPosition, outPosition ) ); + + // Get the light parameters. + Var *lightParams = new Var; + lightParams->setType( "vec4" ); + lightParams->setName( "lightParams" ); + lightParams->uniform = true; + lightParams->constSortPos = cspPass; + + // TODO: If we change other shadow shaders to write out + // linear depth, than fix this as well! + // + // (L - 1.0)/(lightParams.x - 1.0); + // + meta->addStatement( new GenOp( " @.z = L / @.x;\r\n", outPosition, lightParams ) ); + meta->addStatement( new GenOp( " @.w = 1.0;\r\n", outPosition ) ); + + // Pass unmodified to pixel shader to allow it to clip properly. + Var *outPosXY = connectComp->getElement( RT_TEXCOORD ); + outPosXY->setType( "vec2" ); + outPosXY->setName( "outPosXY" ); + meta->addStatement( new GenOp( " @ = @.xy;\r\n", outPosXY, outPosition ) ); + + // Scale and offset so it shows up in the atlas properly. + meta->addStatement( new GenOp( " @.xy *= @.xy;\r\n", outPosition, atlasScale ) ); + + if ( isSinglePass ) + meta->addStatement( new GenOp( " @.x += isBack ? 0.5 : -0.5;\r\n", outPosition ) ); + else + { + Var *atlasOffset = new Var; + atlasOffset->setType( "vec2" ); + atlasOffset->setName( "atlasXOffset" ); + atlasOffset->uniform = true; + atlasOffset->constSortPos = cspPass; + + meta->addStatement( new GenOp( " @.xy += @;\r\n", outPosition, atlasOffset ) ); + } + + output = meta; +} + +void ParaboloidVertTransformGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + MultiLine *meta = new MultiLine; + + const bool isSinglePass = fd.features[ MFT_IsSinglePassParaboloid ]; + if ( isSinglePass ) + { + // Cull things on the back side of the map. + Var *isBack = connectComp->getElement( RT_TEXCOORD ); + isBack->setName( "outIsBack" ); + isBack->setType( "float" ); + meta->addStatement( new GenOp( " if ( ( abs( @ ) - 0.999 ) < 0 ) discard;\r\n", isBack ) ); + } + + // Cull pixels outside of the valid paraboloid. + Var *posXY = connectComp->getElement( RT_TEXCOORD ); + posXY->setName( "outPosXY" ); + posXY->setType( "vec2" ); + meta->addStatement( new GenOp( " if ( ( 1.0 - length( @ ) ) < 0 ) discard;\r\n", posXY ) ); + + output = meta; +} + +ShaderFeature::Resources ParaboloidVertTransformGLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources temp; + temp.numTexReg = 2; + return temp; +} diff --git a/Engine/source/shaderGen/GLSL/paraboloidGLSL.h b/Engine/source/shaderGen/GLSL/paraboloidGLSL.h new file mode 100644 index 000000000..811b19324 --- /dev/null +++ b/Engine/source/shaderGen/GLSL/paraboloidGLSL.h @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _PARABOLOID_GLSL_H_ +#define _PARABOLOID_GLSL_H_ + +#ifndef _SHADERGEN_GLSL_SHADERFEATUREGLSL_H_ +#include "shaderGen/GLSL/shaderFeatureGLSL.h" +#endif +#ifndef _SHADEROP_H_ +#include "shaderGen/shaderOp.h" +#endif + +class GFXShaderConstHandle; + + +class ParaboloidVertTransformGLSL : public ShaderFeatureGLSL +{ +public: + + // ShaderFeature + virtual void processVert( Vector &componentList, const MaterialFeatureData &fd ); + virtual void processPix( Vector &componentList, const MaterialFeatureData &fd ); + virtual Resources getResources( const MaterialFeatureData &fd ); + virtual String getName() { return "Paraboloid Vert Transform"; } + virtual Material::BlendOp getBlendOp() { return Material::None; } + +}; + +#endif // _PARABOLOID_GLSL_H_ \ No newline at end of file diff --git a/Engine/source/shaderGen/GLSL/pixSpecularGLSL.cpp b/Engine/source/shaderGen/GLSL/pixSpecularGLSL.cpp new file mode 100644 index 000000000..ac313a103 --- /dev/null +++ b/Engine/source/shaderGen/GLSL/pixSpecularGLSL.cpp @@ -0,0 +1,208 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "shaderGen/GLSL/pixSpecularGLSL.h" +#include "materials/processedMaterial.h" +#include "materials/materialFeatureTypes.h" +#include "shaderGen/shaderOp.h" +#include "shaderGen/shaderGenVars.h" +#include "gfx/gfxStructs.h" + + +PixelSpecularGLSL::PixelSpecularGLSL() + : mDep( "shaders/common/gl/lighting.glsl" ) +{ + addDependency( &mDep ); +} + +void PixelSpecularGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + /* + AssertFatal( fd.features[MFT_RTLighting], + "PixelSpecularHLSL requires RTLighting to be enabled!" ); + + MultiLine *meta = new MultiLine; + + // Get the eye world position. + Var *eyePos = (Var*)LangElement::find( "eyePosWorld" ); + if( !eyePos ) + { + eyePos = new Var; + eyePos->setType( "float3" ); + eyePos->setName( "eyePosWorld" ); + eyePos->uniform = true; + eyePos->constSortPos = cspPass; + } + + // Grab a register for passing the + // world space view vector. + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *wsView = connectComp->getElement( RT_TEXCOORD ); + wsView->setName( "wsView" ); + wsView->setStructName( "OUT" ); + wsView->setType( "float3" ); + + // Get the input position. + Var *position = (Var*)LangElement::find( "inPosition" ); + if ( !position ) + position = (Var*)LangElement::find( "position" ); + + // Get the object to world transform. + Var *objTrans = (Var*) LangElement::find( "objTrans" ); + if ( !objTrans ) + { + objTrans = new Var; + objTrans->setType( "float4x4" ); + objTrans->setName( "objTrans" ); + objTrans->uniform = true; + objTrans->constSortPos = cspPrimitive; + } + + meta->addStatement( new GenOp( " @ = @ - mul( @, float4( @.xyz,1 ) ).xyz;\r\n", + wsView, eyePos, objTrans, position ) ); + + output = meta; + */ +} + +void PixelSpecularGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + /* + AssertFatal( fd.features[MFT_RTLighting], + "PixelSpecularHLSL requires RTLighting to be enabled!" ); + + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + MultiLine *meta = new MultiLine; + + // Get the normal and light vectors from which the + // RTLighting feature should have already setup. + Var *wsNormal = (Var*)LangElement::find( "wsNormal" ); + Var *inLightVec = (Var*)LangElement::find( "inLightVec" ); + + // Grab the world space position to eye vector. + Var *wsView = connectComp->getElement( RT_TEXCOORD ); + wsView->setName( "wsView" ); + wsView->setStructName( "IN" ); + wsView->setType( "float3" ); + + // Get the specular power and color. + Var *specPow = new Var( "specularPower", "float" ); + specPow->uniform = true; + specPow->constSortPos = cspPass; + Var *specCol = (Var*)LangElement::find("specularColor"); + if(specCol == NULL) + { + specCol = new Var( "specularColor", "vec4" ); + specCol->uniform = true; + specCol->constSortPos = cspPass; + } + + // Calcuate the specular factor. + Var *specular = new Var( "specular", "float" ); + meta->addStatement( new GenOp( " @ = calcSpecular( -@, normalize( @ ), normalize( @ ), @ );\r\n", + new DecOp( specular ), inLightVec, wsNormal, wsView, specPow ) ); + + LangElement *specMul = new GenOp( "float4(@.rgb,0) * @", specCol, specular ); + LangElement *final = specMul; + + // mask out with lightmap if present + if( fd.features[MFT_LightMap] ) + { + LangElement *lmColor = NULL; + + // find lightmap color + lmColor = LangElement::find( "lmColor" ); + + if ( !lmColor ) + { + LangElement * lightMap = LangElement::find( "lightMap" ); + LangElement * lmCoord = LangElement::find( "texCoord2" ); + + lmColor = new GenOp( "tex2D(@, @)", lightMap, lmCoord ); + } + + final = new GenOp( "@ * float4(@.rgb,0)", specMul, lmColor ); + } + + // We we have a normal map then mask the specular + if ( !fd.features[MFT_SpecularMap] && fd.features[MFT_NormalMap] ) + { + Var *bumpColor = (Var*)LangElement::find( "bumpNormal" ); + final = new GenOp( "@ * @.a", final, bumpColor ); + } + + // Add the specular to the final color. + meta->addStatement( new GenOp( " @;\r\n", assignColor( final, Material::Add ) ) ); + + output = meta; + */ +} + +ShaderFeature::Resources PixelSpecularGLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + res.numTexReg = 1; + return res; +} + +void SpecularMapGLSL::processPix( Vector &componentList, const MaterialFeatureData &fd ) +{ + // Get the texture coord. + Var *texCoord = getInTexCoord( "out_texCoord", "vec2", true, componentList ); + + // create texture var + Var *specularMap = new Var; + specularMap->setType( "sampler2D" ); + specularMap->setName( "specularMap" ); + specularMap->uniform = true; + specularMap->sampler = true; + specularMap->constNum = Var::getTexUnitNum(); + LangElement *texOp = new GenOp( "texture2D(@, @)", specularMap, texCoord ); + + Var *specularColor = new Var( "specularColor", "vec4" ); + + output = new GenOp( " @ = @;\r\n", new DecOp( specularColor ), texOp ); +} + +ShaderFeature::Resources SpecularMapGLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + res.numTex = 1; + return res; +} + +void SpecularMapGLSL::setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ) +{ + GFXTextureObject *tex = stageDat.getTex( MFT_SpecularMap ); + if ( tex ) + { + passData.mTexType[ texIndex ] = Material::Standard; + passData.mTexSlot[ texIndex++ ].texObject = tex; + } +} \ No newline at end of file diff --git a/Engine/source/shaderGen/GLSL/pixSpecularGLSL.h b/Engine/source/shaderGen/GLSL/pixSpecularGLSL.h new file mode 100644 index 000000000..eb49b4504 --- /dev/null +++ b/Engine/source/shaderGen/GLSL/pixSpecularGLSL.h @@ -0,0 +1,79 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PIXSPECULAR_GLSL_H_ +#define _PIXSPECULAR_GLSL_H_ + +#ifndef _SHADERGEN_GLSL_SHADERFEATUREGLSL_H_ +#include "shaderGen/GLSL/shaderFeatureGLSL.h" +#endif + + +/// A per-pixel specular feature. +class PixelSpecularGLSL : public ShaderFeatureGLSL +{ +protected: + + ShaderIncludeDependency mDep; + +public: + + PixelSpecularGLSL(); + + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual String getName() + { + return "Pixel Specular"; + } +}; + + +/// A texture source for the PixSpecular feature +class SpecularMapGLSL : public ShaderFeatureGLSL +{ + +public: + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual void setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ); + + virtual String getName() + { + return "Specular Map"; + } +}; + + +#endif // _PIXSPECULAR_GLSL_H_ \ No newline at end of file diff --git a/Engine/source/shaderGen/GLSL/shaderCompGLSL.cpp b/Engine/source/shaderGen/GLSL/shaderCompGLSL.cpp new file mode 100644 index 000000000..7a476ff3b --- /dev/null +++ b/Engine/source/shaderGen/GLSL/shaderCompGLSL.cpp @@ -0,0 +1,263 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "shaderGen/GLSL/shaderCompGLSL.h" + +#include "shaderGen/shaderComp.h" +#include "shaderGen/langElement.h" +#include "gfx/gfxDevice.h" + + +Var * AppVertConnectorGLSL::getElement( RegisterType type, + U32 numElements, + U32 numRegisters ) +{ + switch( type ) + { + case RT_POSITION: + { + Var *newVar = new Var; + mElementList.push_back( newVar ); + newVar->setConnectName( "gl_Vertex" ); + return newVar; + } + + case RT_NORMAL: + { + Var *newVar = new Var; + mElementList.push_back( newVar ); + newVar->setConnectName( "gl_Normal" ); + return newVar; + } + + + case RT_COLOR: + { + Var *newVar = new Var; + mElementList.push_back( newVar ); + newVar->setConnectName( "gl_Color" ); + return newVar; + } + + case RT_TEXCOORD: + case RT_BINORMAL: + case RT_TANGENT: + { + Var *newVar = new Var; + mElementList.push_back( newVar ); + + char out[32]; + dSprintf( (char*)out, sizeof(out), "gl_MultiTexCoord%d", mCurTexElem ); + newVar->setConnectName( out ); + newVar->constNum = mCurTexElem; + newVar->arraySize = numElements; + + if ( numRegisters != -1 ) + mCurTexElem += numRegisters; + else + mCurTexElem += numElements; + + return newVar; + } + + default: + break; + } + + return NULL; +} + +void AppVertConnectorGLSL::sortVars() +{ + // Not required in GLSL +} + +void AppVertConnectorGLSL::setName( char *newName ) +{ + dStrcpy( (char*)mName, newName ); +} + +void AppVertConnectorGLSL::reset() +{ + for( U32 i=0; itype, "float")) + swizzle = "x"; + else if(!dStrcmp((const char*)var->type, "vec2")) + swizzle = "xy"; + else if(!dStrcmp((const char*)var->type, "vec3")) + swizzle = "xyz"; + else + swizzle = "xyzw"; + + // This is ugly. We use #defines to match user defined names with + // built in vars. There is no cleaner way to do this. + dSprintf( (char*)output, sizeof(output), "#define %s %s.%s\r\n", var->name, var->connectName, swizzle ); + + stream.write( dStrlen((char*)output), output ); + } +} + +Var * VertPixelConnectorGLSL::getElement( RegisterType type, + U32 numElements, + U32 numRegisters ) +{ + switch( type ) + { + case RT_POSITION: + case RT_NORMAL: + case RT_COLOR: + { + Var *newVar = new Var; + mElementList.push_back( newVar ); + return newVar; + } + + case RT_TEXCOORD: + case RT_BINORMAL: + case RT_TANGENT: + { + Var *newVar = new Var; + newVar->arraySize = numElements; + + if ( numRegisters != -1 ) + mCurTexElem += numRegisters; + else + mCurTexElem += numElements; + + mElementList.push_back( newVar ); + return newVar; + } + + default: + break; + } + + return NULL; +} + +void VertPixelConnectorGLSL::sortVars() +{ + // Not needed in GLSL +} + +void VertPixelConnectorGLSL::setName( char *newName ) +{ + dStrcpy( (char*)mName, newName ); +} + +void VertPixelConnectorGLSL::reset() +{ + for( U32 i=0; iname, "gl_Position")) + continue; + + if(var->arraySize <= 1) + dSprintf((char*)output, sizeof(output), "varying %s %s;\r\n", var->type, var->name); + else + dSprintf((char*)output, sizeof(output), "varying %s %s[%d];\r\n", var->type, var->name, var->arraySize); + + stream.write( dStrlen((char*)output), output ); + } +} + +void VertexParamsDefGLSL::print( Stream &stream ) +{ + // find all the uniform variables and print them out + for( U32 i=0; i(LangElement::elementList[i]); + if( var ) + { + if( var->uniform ) + { + U8 output[256]; + if(var->arraySize <= 1) + dSprintf((char*)output, sizeof(output), "uniform %-8s %-15s;\r\n", var->type, var->name); + else + dSprintf((char*)output, sizeof(output), "uniform %-8s %-15s[%d];\r\n", var->type, var->name, var->arraySize); + + stream.write( dStrlen((char*)output), output ); + } + } + } + + const char *closer = "\r\n\r\nvoid main()\r\n{\r\n"; + stream.write( dStrlen(closer), closer ); +} + +void PixelParamsDefGLSL::print( Stream &stream ) +{ + // find all the uniform variables and print them out + for( U32 i=0; i(LangElement::elementList[i]); + if( var ) + { + if( var->uniform ) + { + U8 output[256]; + if(var->arraySize <= 1) + dSprintf((char*)output, sizeof(output), "uniform %-8s %-15s;\r\n", var->type, var->name); + else + dSprintf((char*)output, sizeof(output), "uniform %-8s %-15s[%d];\r\n", var->type, var->name, var->arraySize); + + stream.write( dStrlen((char*)output), output ); + } + } + } + + const char *closer = "\r\nvoid main()\r\n{\r\n"; + stream.write( dStrlen(closer), closer ); +} diff --git a/Engine/source/shaderGen/GLSL/shaderCompGLSL.h b/Engine/source/shaderGen/GLSL/shaderCompGLSL.h new file mode 100644 index 000000000..5b306c0c8 --- /dev/null +++ b/Engine/source/shaderGen/GLSL/shaderCompGLSL.h @@ -0,0 +1,73 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SHADERCOMP_GLSL_H_ +#define _SHADERCOMP_GLSL_H_ + +#ifndef _SHADERCOMP_H_ +#include "shaderGen/shaderComp.h" +#endif + + +class VertPixelConnectorGLSL : public ShaderConnector +{ +public: + + // ShaderConnector + virtual Var* getElement( RegisterType type, + U32 numElements = 1, + U32 numRegisters = -1 ); + virtual void setName( char *newName ); + virtual void reset(); + virtual void sortVars(); + + virtual void print( Stream &stream ); +}; + +class AppVertConnectorGLSL : public ShaderConnector +{ +public: + virtual Var* getElement( RegisterType type, + U32 numElements = 1, + U32 numRegisters = -1 ); + virtual void setName( char *newName ); + virtual void reset(); + virtual void sortVars(); + + virtual void print( Stream &stream ); +}; + + +class VertexParamsDefGLSL : public ParamsDef +{ +public: + virtual void print( Stream &stream ); +}; + + +class PixelParamsDefGLSL : public ParamsDef +{ +public: + virtual void print( Stream &stream ); +}; + +#endif // _SHADERCOMP_GLSL_H_ \ No newline at end of file diff --git a/Engine/source/shaderGen/GLSL/shaderFeatureGLSL.cpp b/Engine/source/shaderGen/GLSL/shaderFeatureGLSL.cpp new file mode 100644 index 000000000..0e0c1ac46 --- /dev/null +++ b/Engine/source/shaderGen/GLSL/shaderFeatureGLSL.cpp @@ -0,0 +1,2458 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "shaderGen/GLSL/shaderFeatureGLSL.h" + +#include "shaderGen/langElement.h" +#include "shaderGen/shaderOp.h" +#include "shaderGen/shaderGenVars.h" +#include "gfx/gfxDevice.h" +#include "materials/matInstance.h" +#include "materials/processedMaterial.h" +#include "materials/materialFeatureTypes.h" +#include "core/util/autoPtr.h" + +#include "lighting/advanced/advancedLightBinManager.h" + +LangElement * ShaderFeatureGLSL::setupTexSpaceMat( Vector &, // componentList + Var **texSpaceMat ) +{ + Var *N = (Var*) LangElement::find( "normal" ); + Var *B = (Var*) LangElement::find( "B" ); + Var *T = (Var*) LangElement::find( "T" ); + + // setup matrix var + *texSpaceMat = new Var; + (*texSpaceMat)->setType( "mat3" ); + (*texSpaceMat)->setName( "objToTangentSpace" ); + + MultiLine * meta = new MultiLine; + + // Recreate the binormal if we don't have one. + if ( !B ) + { + B = new Var; + B->setType( "vec3" ); + B->setName( "B" ); + meta->addStatement( new GenOp( " @ = cross( @, normalize(@) );\r\n", new DecOp( B ), T, N ) ); + } + + meta->addStatement( new GenOp( " @;\r\n", new DecOp( *texSpaceMat ) ) ); + meta->addStatement( new GenOp( " @[0] = vec3(@.x, @.x, normalize(@).x);\r\n", *texSpaceMat, T, B, N ) ); + meta->addStatement( new GenOp( " @[1] = vec3(@.y, @.y, normalize(@).y);\r\n", *texSpaceMat, T, B, N ) ); + meta->addStatement( new GenOp( " @[2] = vec3(@.z, @.z, normalize(@).z);\r\n", *texSpaceMat, T, B, N ) ); + + return meta; +} + +LangElement* ShaderFeatureGLSL::assignColor( LangElement *elem, + Material::BlendOp blend, + LangElement *lerpElem, + ShaderFeature::OutputTarget outputTarget ) +{ + + // search for color var + Var *color = (Var*) LangElement::find( getOutputTargetVarName(outputTarget) ); + + if ( !color ) + { + // create color var + color = new Var; + color->setName( getOutputTargetVarName( outputTarget ) ); + color->setType( "vec4" ); + + return new GenOp( "@ = @", new DecOp(color), elem ); + } + + LangElement *assign; + + switch ( blend ) + { + case Material::Add: + assign = new GenOp( "@ += @", color, elem ); + break; + + case Material::Sub: + assign = new GenOp( "@ -= @", color, elem ); + break; + + case Material::Mul: + assign = new GenOp( "@ *= @", color, elem ); + break; + + case Material::AddAlpha: + assign = new GenOp( "@ += @ * @.a", color, elem, elem ); + break; + + case Material::LerpAlpha: + if ( !lerpElem ) + lerpElem = elem; + assign = new GenOp( "@.rgb = mix( @.rgb, (@).rgb, (@).a )", color, elem, color, lerpElem ); + break; + + case Material::ToneMap: + assign = new GenOp( "@ = 1.0 - exp(-1.0 * @ * @)", color, color, elem ); + break; + + default: + AssertFatal(false, "Unrecognized color blendOp"); + // Fallthru + + case Material::None: + assign = new GenOp( "@ = @", color, elem ); + break; + } + + return assign; +} + + +LangElement *ShaderFeatureGLSL::expandNormalMap( LangElement *sampleNormalOp, + LangElement *normalDecl, + LangElement *normalVar, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + + if ( fd.features.hasFeature( MFT_IsDXTnm, getProcessIndex() ) ) + { + // DXT Swizzle trick + meta->addStatement( new GenOp( " @ = vec4( @.ag * 2.0 - 1.0, 0.0, 0.0 ); // DXTnm\r\n", normalDecl, sampleNormalOp ) ); + meta->addStatement( new GenOp( " @.z = sqrt( 1.0 - dot( @.xy, @.xy ) ); // DXTnm\r\n", normalVar, normalVar, normalVar ) ); + } + else + { + meta->addStatement( new GenOp( " @ = @;\r\n", normalDecl, sampleNormalOp ) ); + meta->addStatement( new GenOp( " @.xyz = @.xyz * 2.0 - 1.0;\r\n", normalVar, normalVar ) ); + } + + return meta; +} + +ShaderFeatureGLSL::ShaderFeatureGLSL() +{ + output = NULL; +} + +Var * ShaderFeatureGLSL::getVertTexCoord( const String &name ) +{ + Var *inTex = NULL; + + for( U32 i=0; iname, name.c_str() ) ) + { + inTex = dynamic_cast( LangElement::elementList[i] ); + if ( inTex ) + { + break; + } + } + } + + return inTex; +} + +Var* ShaderFeatureGLSL::getOutObjToTangentSpace( Vector &componentList, + MultiLine *meta, + const MaterialFeatureData &fd ) +{ + Var *outObjToTangentSpace = (Var*)LangElement::find( "objToTangentSpace" ); + if ( !outObjToTangentSpace ) + meta->addStatement( setupTexSpaceMat( componentList, &outObjToTangentSpace ) ); + + return outObjToTangentSpace; +} + +Var* ShaderFeatureGLSL::getOutWorldToTangent( Vector &componentList, + MultiLine *meta, + const MaterialFeatureData &fd ) +{ + Var *outWorldToTangent = (Var*)LangElement::find( "outWorldToTangent" ); + if ( outWorldToTangent ) + return outWorldToTangent; + + Var *worldToTangent = (Var*)LangElement::find( "worldToTangent" ); + if ( !worldToTangent ) + { + Var *texSpaceMat = getOutObjToTangentSpace( componentList, meta, fd ); + + if(!fd.features[MFT_ParticleNormal]) + { + // turn obj->tangent into world->tangent + worldToTangent = new Var; + worldToTangent->setType( "mat3x3" ); + worldToTangent->setName( "worldToTangent" ); + LangElement *worldToTangentDecl = new DecOp( worldToTangent ); + + // Get the world->obj transform + Var *worldToObj = (Var*)LangElement::find( "worldToObj" ); + if ( !worldToObj ) + { + worldToObj = new Var; + worldToObj->setName( "worldToObj" ); + worldToObj->setType( "mat4x4" ); + worldToObj->uniform = true; + worldToObj->constSortPos = cspPrimitive; + } + + // assign world->tangent transform + meta->addStatement( new GenOp( " @ = @ * mat3x3( @[0].xyz, @[1].xyz, @[2].xyz );\r\n", worldToTangentDecl, texSpaceMat, worldToObj, worldToObj, worldToObj ) ); + } + else + { + worldToTangent = texSpaceMat; + } + } + + // send transform to pixel shader + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + outWorldToTangent = connectComp->getElement( RT_TEXCOORD, 1, 3 ); + outWorldToTangent->setName( "outWorldToTangent" ); + outWorldToTangent->setType( "mat3x3" ); + meta->addStatement( new GenOp( " @ = @;\r\n", outWorldToTangent, worldToTangent ) ); + + return outWorldToTangent; +} + +Var* ShaderFeatureGLSL::getOutViewToTangent( Vector &componentList, + MultiLine *meta, + const MaterialFeatureData &fd ) +{ + Var *outViewToTangent = (Var*)LangElement::find( "outViewToTangent" ); + if ( outViewToTangent ) + return outViewToTangent; + + Var *viewToTangent = (Var*)LangElement::find( "viewToTangent" ); + if ( !viewToTangent ) + { + Var *texSpaceMat = getOutObjToTangentSpace( componentList, meta, fd ); + + if(!fd.features[MFT_ParticleNormal]) + { + + // turn obj->tangent into world->tangent + viewToTangent = new Var; + viewToTangent->setType( "mat3" ); + viewToTangent->setName( "viewToTangent" ); + LangElement *viewToTangentDecl = new DecOp( viewToTangent ); + + // Get the view->obj transform + Var *viewToObj = getInvWorldView( componentList, fd.features[MFT_UseInstancing], meta ); + + // assign world->tangent transform + meta->addStatement( new GenOp( " mat3 mat3ViewToObj;\r\n" ) ); + meta->addStatement( new GenOp( " mat3ViewToObj[0] = @[0].xyz;\r\n", viewToObj ) ); + meta->addStatement( new GenOp( " mat3ViewToObj[1] = @[1].xyz;\r\n", viewToObj ) ); + meta->addStatement( new GenOp( " mat3ViewToObj[2] = @[2].xyz;\r\n", viewToObj ) ); + meta->addStatement( new GenOp( " @ = @ * mat3ViewToObj;\r\n", viewToTangentDecl, texSpaceMat ) ); + } + else + { + // Assume particle normal generation has set this up in the proper space + viewToTangent = texSpaceMat; + } + } + + // send transform to pixel shader + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + outViewToTangent = connectComp->getElement( RT_TEXCOORD, 1, 3 ); + outViewToTangent->setName( "outViewToTangent" ); + outViewToTangent->setType( "mat3" ); + meta->addStatement( new GenOp( " @ = @;\r\n", outViewToTangent, viewToTangent ) ); + + return outViewToTangent; +} + +Var* ShaderFeatureGLSL::getOutTexCoord( const char *name, + const char *type, + bool mapsToSampler, + bool useTexAnim, + MultiLine *meta, + Vector &componentList ) +{ + String outTexName = String::ToString( "out_%s", name ); + Var *texCoord = (Var*)LangElement::find( outTexName ); + if ( !texCoord ) + { + Var *inTex = getVertTexCoord( name ); + AssertFatal( inTex, "ShaderFeatureGLSL::getOutTexCoord - Unknown vertex input coord!" ); + + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + texCoord = connectComp->getElement( RT_TEXCOORD ); + texCoord->setName( outTexName ); + texCoord->setType( type ); + texCoord->mapsToSampler = mapsToSampler; + + if( useTexAnim ) + { + inTex->setType( "vec4" ); + + // create texture mat var + Var *texMat = new Var; + texMat->setType( "mat4" ); + texMat->setName( "texMat" ); + texMat->uniform = true; + texMat->constSortPos = cspPass; + + // Statement allows for casting of different types which + // eliminates vector truncation problems. + String statement = String::ToString( " @ = %s(@ * @);\r\n", type ); + meta->addStatement( new GenOp( statement , texCoord, texMat, inTex ) ); + } + else + { + // Statement allows for casting of different types which + // eliminates vector truncation problems. + String statement = String::ToString( " @ = %s(@);\r\n", type ); + meta->addStatement( new GenOp( statement, texCoord, inTex ) ); + } + } + + AssertFatal( dStrcmp( type, (const char*)texCoord->type ) == 0, + "ShaderFeatureGLSL::getOutTexCoord - Type mismatch!" ); + + return texCoord; +} + +Var* ShaderFeatureGLSL::getInTexCoord( const char *name, + const char *type, + bool mapsToSampler, + Vector &componentList ) +{ + Var* texCoord = (Var*)LangElement::find( name ); + if ( !texCoord ) + { + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + texCoord = connectComp->getElement( RT_TEXCOORD ); + texCoord->setName( name ); + texCoord->setType( type ); + texCoord->mapsToSampler = mapsToSampler; + } + + AssertFatal( dStrcmp( type, (const char*)texCoord->type ) == 0, + "ShaderFeatureGLSL::getInTexCoord - Type mismatch!" ); + + return texCoord; +} + +Var* ShaderFeatureGLSL::getInWorldToTangent( Vector &componentList ) +{ + Var *worldToTangent = (Var*)LangElement::find( "outWorldToTangent" ); + if ( !worldToTangent ) + { + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + worldToTangent = connectComp->getElement( RT_TEXCOORD, 1, 3 ); + worldToTangent->setName( "outWorldToTangent" ); + worldToTangent->setType( "mat3x3" ); + } + + return worldToTangent; +} + +Var* ShaderFeatureGLSL::getInViewToTangent( Vector &componentList ) +{ + Var *viewToTangent = (Var*)LangElement::find( "outViewToTangent" ); + if ( !viewToTangent ) + { + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + viewToTangent = connectComp->getElement( RT_TEXCOORD, 1, 3 ); + viewToTangent->setName( "outViewToTangent" ); + viewToTangent->setType( "mat3" ); + } + + return viewToTangent; +} + +Var* ShaderFeatureGLSL::getNormalMapTex() +{ + Var *normalMap = (Var*)LangElement::find( "bumpMap" ); + if ( !normalMap ) + { + normalMap = new Var; + normalMap->setType( "sampler2D" ); + normalMap->setName( "bumpMap" ); + normalMap->uniform = true; + normalMap->sampler = true; + normalMap->constNum = Var::getTexUnitNum(); + } + + return normalMap; +} + +Var* ShaderFeatureGLSL::getObjTrans( Vector &componentList, + bool useInstancing, + MultiLine *meta ) +{ + Var *objTrans = (Var*) LangElement::find( "objTrans" ); if ( objTrans ) + return objTrans; + objTrans = new Var; objTrans->setType( "mat4x4" ); + objTrans->setName( "objTrans" ); + objTrans->uniform = true; + objTrans->constSortPos = cspPrimitive; + + return objTrans; +} + +Var* ShaderFeatureGLSL::getModelView( Vector &componentList, + bool useInstancing, + MultiLine *meta ) +{ + Var *modelview = (Var*)LangElement::find( "modelview" ); + if ( modelview ) + return modelview; + + // create modelview variable + modelview = new Var; + modelview->setType( "mat4" ); + modelview->setName( "modelview" ); + modelview->uniform = true; + modelview->constSortPos = cspPrimitive; + + return modelview; +} + +Var* ShaderFeatureGLSL::getWorldView( Vector &componentList, + bool useInstancing, + MultiLine *meta ) +{ + Var *worldView = (Var*)LangElement::find( "worldViewOnly" ); + if ( worldView ) + return worldView; + + worldView = new Var; + worldView->setType( "mat4x4" ); + worldView->setName( "worldViewOnly" ); + worldView->uniform = true; + worldView->constSortPos = cspPrimitive; + + return worldView; +} + +Var* ShaderFeatureGLSL::getInvWorldView( Vector &componentList, + bool useInstancing, + MultiLine *meta ) +{ + Var *viewToObj = (Var*)LangElement::find( "viewToObj" ); + if ( viewToObj ) + return viewToObj; + + viewToObj = new Var; + viewToObj->setType( "mat4" ); + viewToObj->setName( "viewToObj" ); + viewToObj->uniform = true; + viewToObj->constSortPos = cspPrimitive; + + return viewToObj; +} + +void ShaderFeatureGLSL::getWsPosition( Vector &componentList, + bool useInstancing, + MultiLine *meta, + LangElement *wsPosition ) +{ + Var *inPosition = (Var*)LangElement::find( "wsPosition" ); + if ( inPosition ) + { + meta->addStatement( new GenOp( " @ = @.xyz;\r\n", + wsPosition, inPosition ) ); + return; + } + + // Get the input position. + inPosition = (Var*)LangElement::find( "inPosition" ); + if ( !inPosition ) + inPosition = (Var*)LangElement::find( "position" ); + + AssertFatal( inPosition, "ShaderFeatureGLSL::getWsPosition - The vertex position was not found!" ); + + Var *objTrans = getObjTrans( componentList, useInstancing, meta ); + + meta->addStatement( new GenOp( " @ = ( @ * vec4( @.xyz, 1 ) ).xyz;\r\n", + wsPosition, objTrans, inPosition ) ); +} + +Var* ShaderFeatureGLSL::addOutWsPosition( Vector &componentList, + bool useInstancing, + MultiLine *meta ) +{ + Var *outWsPosition = (Var*)LangElement::find( "outWsPosition" ); + if ( !outWsPosition ) + { + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + outWsPosition = connectComp->getElement( RT_TEXCOORD ); + outWsPosition->setName( "outWsPosition" ); + outWsPosition->setType( "vec3" ); + outWsPosition->mapsToSampler = false; + + getWsPosition( componentList, useInstancing, meta, outWsPosition ); + } + + return outWsPosition; +} + +Var* ShaderFeatureGLSL::getInWsPosition( Vector &componentList ) +{ + Var *wsPosition = (Var*)LangElement::find( "outWsPosition" ); + if ( !wsPosition ) + { + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + wsPosition = connectComp->getElement( RT_TEXCOORD ); + wsPosition->setName( "outWsPosition" ); + wsPosition->setType( "vec3" ); + } + + return wsPosition; +} + +Var* ShaderFeatureGLSL::getWsView( Var *wsPosition, MultiLine *meta ) +{ + Var *wsView = (Var*)LangElement::find( "wsView" ); + if ( !wsView ) + { + wsView = new Var( "wsView", "vec3" ); + + Var *eyePos = (Var*)LangElement::find( "eyePosWorld" ); + if ( !eyePos ) + { + eyePos = new Var; + eyePos->setType( "vec3" ); + eyePos->setName( "eyePosWorld" ); + eyePos->uniform = true; + eyePos->constSortPos = cspPass; + } + + meta->addStatement( new GenOp( " @ = normalize( @ - @ );\r\n", + new DecOp( wsView ), eyePos, wsPosition ) ); + } + + return wsView; +} + +Var* ShaderFeatureGLSL::addOutDetailTexCoord( Vector &componentList, + MultiLine *meta, + bool useTexAnim ) +{ + // Check if its already added. + Var *outTex = (Var*)LangElement::find( "detCoord" ); + if ( outTex ) + return outTex; + + // Grab incoming texture coords. + Var *inTex = getVertTexCoord( "texCoord" ); + + // create detail variable + Var *detScale = new Var; + detScale->setType( "vec2" ); + detScale->setName( "detailScale" ); + detScale->uniform = true; + detScale->constSortPos = cspPotentialPrimitive; + + // grab connector texcoord register + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + outTex = connectComp->getElement( RT_TEXCOORD ); + outTex->setName( "detCoord" ); + outTex->setType( "vec2" ); + outTex->mapsToSampler = true; + + if ( useTexAnim ) + { + inTex->setType( "vec4" ); + + // Find or create the texture matrix. + Var *texMat = (Var*)LangElement::find( "texMat" ); + if ( !texMat ) + { + texMat = new Var; + texMat->setType( "mat4x4" ); + texMat->setName( "texMat" ); + texMat->uniform = true; + texMat->constSortPos = cspPass; + } + + meta->addStatement( new GenOp( " @ = (@ * @) * @;\r\n", outTex, texMat, inTex, detScale ) ); + } + else + { + // setup output to mul texCoord by detail scale + meta->addStatement( new GenOp( " @ = @ * @;\r\n", outTex, inTex, detScale ) ); + } + + return outTex; +} + +//**************************************************************************** +// Base Texture +//**************************************************************************** + +void DiffuseMapFeatGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + getOutTexCoord( "texCoord", + "vec2", + true, + fd.features[MFT_TexAnim], + meta, + componentList ); + output = meta; +} + +void DiffuseMapFeatGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // grab connector texcoord register + Var *inTex = getInTexCoord( "out_texCoord", "vec2", true, componentList ); + + // create texture var + Var *diffuseMap = new Var; + diffuseMap->setType( "sampler2D" ); + diffuseMap->setName( "diffuseMap" ); + diffuseMap->uniform = true; + diffuseMap->sampler = true; + diffuseMap->constNum = Var::getTexUnitNum(); // used as texture unit num here + + if ( fd.features[MFT_CubeMap] ) + { + MultiLine * meta = new MultiLine; + + // create sample color + Var *diffColor = new Var; + diffColor->setType( "vec4" ); + diffColor->setName( "diffuseColor" ); + LangElement *colorDecl = new DecOp( diffColor ); + + meta->addStatement( new GenOp( " @ = texture2D(@, @);\r\n", + colorDecl, + diffuseMap, + inTex ) ); + + meta->addStatement( new GenOp( " @;\r\n", assignColor( diffColor, Material::Mul ) ) ); + output = meta; + } + else if(fd.features[MFT_DiffuseMapAtlas]) + { + // Handle atlased textures + // http://www.infinity-universe.com/Infinity/index.php?option=com_content&task=view&id=65&Itemid=47 + MultiLine * meta = new MultiLine; + output = meta; + + Var *atlasedTex = new Var; + atlasedTex->setName("atlasedTexCoord"); + atlasedTex->setType("float2"); + LangElement *atDecl = new DecOp(atlasedTex); + + // Parameters of the texture atlas + Var *atParams = new Var; + atParams->setType("float4"); + atParams->setName("diffuseAtlasParams"); + atParams->uniform = true; + atParams->constSortPos = cspPotentialPrimitive; + + // Parameters of the texture (tile) this object is using in the atlas + Var *tileParams = new Var; + tileParams->setType("float4"); + tileParams->setName("diffuseAtlasTileParams"); + tileParams->uniform = true; + tileParams->constSortPos = cspPotentialPrimitive; + + const bool is_sm3 = (GFX->getPixelShaderVersion() > 2.0f); + if(is_sm3) + { + // Figure out the mip level + meta->addStatement(new GenOp(" vec2 _dx = ddx(@ * @.z);\r\n", inTex, atParams)); + meta->addStatement(new GenOp(" vec2 _dy = ddy(@ * @.z);\r\n", inTex, atParams)); + meta->addStatement(new GenOp(" float mipLod = 0.5 * log2(max(dot(_dx, _dx), dot(_dy, _dy)));\r\n")); + meta->addStatement(new GenOp(" mipLod = clamp(mipLod, 0.0, @.w);\r\n", atParams)); + + // And the size of the mip level + meta->addStatement(new GenOp(" float mipPixSz = pow(2.0, @.w - mipLod);\r\n", atParams)); + meta->addStatement(new GenOp(" vec2 mipSz = mipPixSz / @.xy;\r\n", atParams)); + } + else + { + meta->addStatement(new GenOp(" vec2 mipSz = float2(1.0, 1.0);\r\n")); + } + + // Tiling mode + // TODO: Select wrap or clamp somehow + if( true ) // Wrap + meta->addStatement(new GenOp(" @ = fract(@);\r\n", atDecl, inTex)); + else // Clamp + meta->addStatement(new GenOp(" @ = saturate(@);\r\n", atDecl, inTex)); + + // Finally scale/offset, and correct for filtering + meta->addStatement(new GenOp(" @ = @ * ((mipSz * @.xy - 1.0) / mipSz) + 0.5 / mipSz + @.xy * @.xy;\r\n", + atlasedTex, atlasedTex, atParams, atParams, tileParams)); + + // Add a newline + meta->addStatement(new GenOp( "\r\n")); + + // For the rest of the feature... + inTex = atlasedTex; + + // create sample color var + Var *diffColor = new Var; + diffColor->setType("vec4"); + diffColor->setName("diffuseColor"); + + // To dump out UV coords... + //#define DEBUG_ATLASED_UV_COORDS +#ifdef DEBUG_ATLASED_UV_COORDS + if(!fd.features[MFT_PrePassConditioner]) + { + meta->addStatement(new GenOp(" @ = vec4(@.xy, mipLod / @.w, 1.0);\r\n", new DecOp(diffColor), inTex, atParams)); + meta->addStatement(new GenOp(" @; return OUT;\r\n", assignColor(diffColor, Material::Mul))); + return; + } +#endif + + if(is_sm3) + { + meta->addStatement(new GenOp( " @ = texture2Dlod(@, float4(@, 0.0, mipLod));\r\n", + new DecOp(diffColor), diffuseMap, inTex)); + } + else + { + meta->addStatement(new GenOp( " @ = texture2D(@, @);\r\n", + new DecOp(diffColor), diffuseMap, inTex)); + } + + meta->addStatement(new GenOp( " @;\r\n", assignColor(diffColor, Material::Mul))); + } + else + { + LangElement *statement = new GenOp( "texture2D(@, @)", diffuseMap, inTex ); + output = new GenOp( " @;\r\n", assignColor( statement, Material::Mul ) ); + } + +} + +ShaderFeature::Resources DiffuseMapFeatGLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + res.numTex = 1; + res.numTexReg = 1; + + return res; +} + +void DiffuseMapFeatGLSL::setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ) +{ + GFXTextureObject *tex = stageDat.getTex( MFT_DiffuseMap ); + if ( tex ) + passData.mTexSlot[ texIndex++ ].texObject = tex; +} + + +//**************************************************************************** +// Overlay Texture +//**************************************************************************** + +void OverlayTexFeatGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + Var *inTex = getVertTexCoord( "texCoord2" ); + AssertFatal( inTex, "OverlayTexFeatGLSL::processVert() - The second UV set was not found!" ); + + // grab connector texcoord register + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outTex = connectComp->getElement( RT_TEXCOORD ); + outTex->setName( "outTexCoord2" ); + outTex->setType( "vec2" ); + outTex->mapsToSampler = true; + + if( fd.features[MFT_TexAnim] ) + { + inTex->setType( "vec4" ); + + // Find or create the texture matrix. + Var *texMat = (Var*)LangElement::find( "texMat" ); + if ( !texMat ) + { + texMat = new Var; + texMat->setType( "mat4x4" ); + texMat->setName( "texMat" ); + texMat->uniform = true; + texMat->constSortPos = cspPass; + } + + output = new GenOp( " @ = @ * @;\r\n", outTex, texMat, inTex ); + return; + } + + // setup language elements to output incoming tex coords to output + output = new GenOp( " @ = @;\r\n", outTex, inTex ); +} + +void OverlayTexFeatGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + + // grab connector texcoord register + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *inTex = connectComp->getElement( RT_TEXCOORD ); + inTex->setName( "outTexCoord2" ); + inTex->setType( "vec2" ); + inTex->mapsToSampler = true; + + // create texture var + Var *diffuseMap = new Var; + diffuseMap->setType( "sampler2D" ); + diffuseMap->setName( "overlayMap" ); + diffuseMap->uniform = true; + diffuseMap->sampler = true; + diffuseMap->constNum = Var::getTexUnitNum(); // used as texture unit num here + + LangElement *statement = new GenOp( "texture2D(@, @)", diffuseMap, inTex ); + output = new GenOp( " @;\r\n", assignColor( statement, Material::LerpAlpha ) ); +} + +ShaderFeature::Resources OverlayTexFeatGLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + res.numTex = 1; + res.numTexReg = 1; + return res; +} + +void OverlayTexFeatGLSL::setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ) +{ + GFXTextureObject *tex = stageDat.getTex( MFT_OverlayMap ); + if ( tex ) + passData.mTexSlot[ texIndex++ ].texObject = tex; +} + + +//**************************************************************************** +// Diffuse color +//**************************************************************************** + +void DiffuseFeatureGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + Var* diffuseMaterialColor = new Var; + diffuseMaterialColor->setType( "vec4" ); + diffuseMaterialColor->setName( "diffuseMaterialColor" ); + diffuseMaterialColor->uniform = true; + diffuseMaterialColor->constSortPos = cspPotentialPrimitive; + + MultiLine* meta = new MultiLine; + meta->addStatement( new GenOp( " @;\r\n", assignColor( diffuseMaterialColor, Material::Mul ) ) ); + output = meta; +} + + +//**************************************************************************** +// Diffuse vertex color +//**************************************************************************** + +void DiffuseVertColorFeatureGLSL::processVert( Vector< ShaderComponent* >& componentList, + const MaterialFeatureData& fd ) +{ + // Create vertex color connector if it doesn't exist. + + Var* outColor = dynamic_cast< Var* >( LangElement::find( "vertColor" ) ); + if( !outColor ) + { + // Search for vert color. + + Var* inColor = dynamic_cast< Var* >( LangElement::find( "diffuse" ) ); + if( !inColor ) + { + output = NULL; + return; + } + + // Create connector. + + ShaderConnector* connectComp = dynamic_cast< ShaderConnector* >( componentList[ C_CONNECTOR ] ); + AssertFatal( connectComp, "DiffuseVertColorFeatureGLSL::processVert - C_CONNECTOR is not a ShaderConnector" ); + Var* outColor = connectComp->getElement( RT_COLOR ); + outColor->setName( "vertColor" ); + outColor->setType( "vec4" ); + + output = new GenOp( " @ = @;\r\n", outColor, inColor ); + } + else + output = NULL; // Nothing we need to do. +} + +void DiffuseVertColorFeatureGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + Var* vertColor = dynamic_cast< Var* >( LangElement::find( "vertColor" ) ); + if( !vertColor ) + { + ShaderConnector* connectComp = dynamic_cast< ShaderConnector* >( componentList[ C_CONNECTOR ] ); + AssertFatal( connectComp, "DiffuseVertColorFeatureGLSL::processVert - C_CONNECTOR is not a ShaderConnector" ); + vertColor = connectComp->getElement( RT_COLOR ); + vertColor->setName( "vertColor" ); + vertColor->setType( "vec4" ); + } + + MultiLine* meta = new MultiLine; + meta->addStatement( new GenOp( " @;\r\n", assignColor( vertColor, Material::Mul ) ) ); + output = meta; +} + + +//**************************************************************************** +// Lightmap +//**************************************************************************** + +void LightmapFeatGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // grab tex register from incoming vert + Var *inTex = (Var*) LangElement::find( "texCoord2" ); + + // grab connector texcoord register + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outTex = connectComp->getElement( RT_TEXCOORD ); + outTex->setName( "outTexCoord2" ); + outTex->setType( "vec2" ); + outTex->mapsToSampler = true; + + // setup language elements to output incoming tex coords to output + output = new GenOp( " @ = @;\r\n", outTex, inTex ); +} + +void LightmapFeatGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // grab connector texcoord register + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *inTex = connectComp->getElement( RT_TEXCOORD ); + inTex->setName( "outTexCoord2" ); + inTex->setType( "vec2" ); + inTex->mapsToSampler = true; + + // create texture var + Var *lightMap = new Var; + lightMap->setType( "sampler2D" ); + lightMap->setName( "lightMap" ); + lightMap->uniform = true; + lightMap->sampler = true; + lightMap->constNum = Var::getTexUnitNum(); // used as texture unit num here + + + // argh, pixel specular should prob use this too + if( fd.features[MFT_NormalMap] ) + { + Var *lmColor = new Var; + lmColor->setName( "lmColor" ); + lmColor->setType( "vec4" ); + LangElement *lmColorDecl = new DecOp( lmColor ); + + output = new GenOp( " @ = texture2D(@, @);\r\n", lmColorDecl, lightMap, inTex ); + return; + } + + // Add realtime lighting, if it is available + LangElement *statement = NULL; + if( fd.features[MFT_RTLighting] ) + { + // Advanced lighting is the only dynamic lighting supported right now + Var *inColor = (Var*) LangElement::find( "d_lightcolor" ); + if(inColor != NULL) + { + // Find out if RTLighting should be added or substituted + bool bPreProcessedLighting = false; + AdvancedLightBinManager *lightBin; + if ( Sim::findObject( "AL_LightBinMgr", lightBin ) ) + bPreProcessedLighting = lightBin->MRTLightmapsDuringPrePass(); + + // Lightmap has already been included in the advanced light bin, so + // no need to do any sampling or anything + if(bPreProcessedLighting) + statement = new GenOp( "vec4(@, 1.0)", inColor ); + else + statement = new GenOp( "texture2D(@, @) + vec4(@.rgb, 0.0)", lightMap, inTex, inColor ); + } + } + else + { + statement = new GenOp( "texture2D(@, @)", lightMap, inTex ); + } + + // Assign to proper render target + if( fd.features[MFT_LightbufferMRT] ) + output = new GenOp( " @;\r\n", assignColor( statement, Material::None, NULL, ShaderFeature::RenderTarget1 ) ); + else + output = new GenOp( " @;\r\n", assignColor( statement, Material::Mul ) ); +} + +ShaderFeature::Resources LightmapFeatGLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + res.numTex = 1; + res.numTexReg = 1; + + return res; +} + +void LightmapFeatGLSL::setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ) +{ + GFXTextureObject *tex = stageDat.getTex( MFT_LightMap ); + if ( tex ) + passData.mTexSlot[ texIndex++ ].texObject = tex; + else + passData.mTexType[ texIndex++ ] = Material::Lightmap; +} + +U32 LightmapFeatGLSL::getOutputTargets( const MaterialFeatureData &fd ) const +{ + return fd.features[MFT_LightbufferMRT] ? ShaderFeature::RenderTarget1 : ShaderFeature::DefaultTarget; +} + +//**************************************************************************** +// Tonemap +//**************************************************************************** + +void TonemapFeatGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // Grab the connector + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + // Set up the second set of texCoords + Var *inTex2 = getVertTexCoord( "texCoord2" ); + + if ( inTex2 ) + { + Var *outTex2 = connectComp->getElement( RT_TEXCOORD ); + outTex2->setName( "toneMapCoord" ); + outTex2->setType( "vec2" ); + outTex2->mapsToSampler = true; + + output = new GenOp( " @ = @;\r\n", outTex2, inTex2 ); + } +} + +void TonemapFeatGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // Grab connector + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + Var *inTex2 = connectComp->getElement( RT_TEXCOORD ); + inTex2->setName( "toneMapCoord" ); + inTex2->setType( "vec2" ); + inTex2->mapsToSampler = true; + + // create texture var + Var *toneMap = new Var; + toneMap->setType( "sampler2D" ); + toneMap->setName( "toneMap" ); + toneMap->uniform = true; + toneMap->sampler = true; + toneMap->constNum = Var::getTexUnitNum(); // used as texture unit num here + + MultiLine * meta = new MultiLine; + + // First get the toneMap color + Var *toneMapColor = new Var; + toneMapColor->setType( "vec4" ); + toneMapColor->setName( "toneMapColor" ); + LangElement *toneMapColorDecl = new DecOp( toneMapColor ); + + meta->addStatement( new GenOp( " @ = texture2D(@, @);\r\n", toneMapColorDecl, toneMap, inTex2 ) ); + + // We do a different calculation if there is a diffuse map or not + Material::BlendOp blendOp = Material::Mul; + if ( fd.features[MFT_DiffuseMap] ) + { + // Reverse the tonemap + meta->addStatement( new GenOp( " @ = -1.0 * log(1.0 - @);\r\n", toneMapColor, toneMapColor ) ); + + // Re-tonemap with the current color factored in + blendOp = Material::ToneMap; + } + + // Find out if RTLighting should be added + bool bPreProcessedLighting = false; + AdvancedLightBinManager *lightBin; + if ( Sim::findObject( "AL_LightBinMgr", lightBin ) ) + bPreProcessedLighting = lightBin->MRTLightmapsDuringPrePass(); + + // Add in the realtime lighting contribution + if ( fd.features[MFT_RTLighting] ) + { + // Right now, only Advanced Lighting is supported + Var *inColor = (Var*) LangElement::find( "d_lightcolor" ); + if(inColor != NULL) + { + // Assign value in d_lightcolor to toneMapColor if it exists. This is + // the dynamic light buffer, and it already has the tonemap included + if(bPreProcessedLighting) + meta->addStatement( new GenOp( " @.rgb = @;\r\n", toneMapColor, inColor ) ); + else + meta->addStatement( new GenOp( " @.rgb += @.rgb;\r\n", toneMapColor, inColor ) ); + } + } + + // Assign to proper render target + if( fd.features[MFT_LightbufferMRT] ) + meta->addStatement( new GenOp( " @;\r\n", assignColor( toneMapColor, Material::None, NULL, ShaderFeature::RenderTarget1 ) ) ); + else + meta->addStatement( new GenOp( " @;\r\n", assignColor( toneMapColor, blendOp ) ) ); + + output = meta; +} + +ShaderFeature::Resources TonemapFeatGLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + res.numTex = 1; + res.numTexReg = 1; + + return res; +} + +void TonemapFeatGLSL::setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ) +{ + GFXTextureObject *tex = stageDat.getTex( MFT_ToneMap ); + if ( tex ) + { + passData.mTexType[ texIndex ] = Material::ToneMapTex; + passData.mTexSlot[ texIndex++ ].texObject = tex; + } +} + +U32 TonemapFeatGLSL::getOutputTargets( const MaterialFeatureData &fd ) const +{ + return fd.features[MFT_LightbufferMRT] ? ShaderFeature::RenderTarget1 : ShaderFeature::DefaultTarget; +} + +//**************************************************************************** +// pureLIGHT Lighting +//**************************************************************************** + +void VertLitGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // If we have a lightMap or toneMap then our lighting will be + // handled by the MFT_LightMap or MFT_ToneNamp feature instead + if ( fd.features[MFT_LightMap] || fd.features[MFT_ToneMap] ) + { + output = NULL; + return; + } + + // Create vertex color connector if it doesn't exist. + + Var* outColor = dynamic_cast< Var* >( LangElement::find( "vertColor" ) ); + if( !outColor ) + { + // Search for vert color + Var *inColor = (Var*) LangElement::find( "diffuse" ); + + // If there isn't a vertex color then we can't do anything + if( !inColor ) + { + output = NULL; + return; + } + + // Grab the connector color + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outColor = connectComp->getElement( RT_COLOR ); + outColor->setName( "vertColor" ); + outColor->setType( "vec4" ); + + output = new GenOp( " @ = @;\r\n", outColor, inColor ); + } + else + output = NULL; // Nothing we need to do. +} + +void VertLitGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // If we have a lightMap or toneMap then our lighting will be + // handled by the MFT_LightMap or MFT_ToneNamp feature instead + if ( fd.features[MFT_LightMap] || fd.features[MFT_ToneMap] ) + { + output = NULL; + return; + } + + // Grab the connector color register + Var* vertColor = dynamic_cast< Var* >( LangElement::find( "vertColor" ) ); + if( !vertColor ) + { + ShaderConnector* connectComp = dynamic_cast< ShaderConnector* >( componentList[ C_CONNECTOR ] ); + AssertFatal( connectComp, "VertLitGLSL::processVert - C_CONNECTOR is not a ShaderConnector" ); + vertColor = connectComp->getElement( RT_COLOR ); + vertColor->setName( "vertColor" ); + vertColor->setType( "vec4" ); + } + + MultiLine * meta = new MultiLine; + + // Defaults (no diffuse map) + Material::BlendOp blendOp = Material::Mul; + LangElement *outColor = vertColor; + + // We do a different calculation if there is a diffuse map or not + if ( fd.features[MFT_DiffuseMap] || fd.features[MFT_VertLitTone] ) + { + Var * finalVertColor = new Var; + finalVertColor->setName( "finalVertColor" ); + finalVertColor->setType( "vec4" ); + LangElement *finalVertColorDecl = new DecOp( finalVertColor ); + + // Reverse the tonemap + meta->addStatement( new GenOp( " @ = -1.0 * log(1.0 - @);\r\n", finalVertColorDecl, vertColor ) ); + + // Set the blend op to tonemap + blendOp = Material::ToneMap; + outColor = finalVertColor; + + } + + // Add in the realtime lighting contribution, if applicable + if ( fd.features[MFT_RTLighting] ) + { + Var *rtLightingColor = (Var*) LangElement::find( "d_lightcolor" ); + if(rtLightingColor != NULL) + { + // Find out if RTLighting should be added or substituted + bool bPreProcessedLighting = false; + AdvancedLightBinManager *lightBin; + if ( Sim::findObject( "AL_LightBinMgr", lightBin ) ) + bPreProcessedLighting = lightBin->MRTLightmapsDuringPrePass(); + + // Assign value in d_lightcolor to toneMapColor if it exists. This is + // the dynamic light buffer, and it already has the baked-vertex-color + // included in it + if(bPreProcessedLighting) + outColor = new GenOp( "vec4(@.rgb, 1.0)", rtLightingColor ); + else + outColor = new GenOp( "vec4(@.rgb, 0.0) + @", rtLightingColor, outColor ); + } + } + + // Output the color + if ( fd.features[MFT_LightbufferMRT] ) + meta->addStatement( new GenOp( " @;\r\n", assignColor( outColor, Material::None, NULL, ShaderFeature::RenderTarget1 ) ) ); + else + meta->addStatement( new GenOp( " @;\r\n", assignColor( outColor, blendOp ) ) ); + + output = meta; +} + +U32 VertLitGLSL::getOutputTargets( const MaterialFeatureData &fd ) const +{ + return fd.features[MFT_LightbufferMRT] ? ShaderFeature::RenderTarget1 : ShaderFeature::DefaultTarget; +} + + +//**************************************************************************** +// Detail map +//**************************************************************************** + +void DetailFeatGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + addOutDetailTexCoord( componentList, + meta, + fd.features[MFT_TexAnim] ); + output = meta; +} + +void DetailFeatGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // Get the detail texture coord. + Var *inTex = getInTexCoord( "detCoord", "vec2", true, componentList ); + + // create texture var + Var *detailMap = new Var; + detailMap->setType( "sampler2D" ); + detailMap->setName( "detailMap" ); + detailMap->uniform = true; + detailMap->sampler = true; + detailMap->constNum = Var::getTexUnitNum(); // used as texture unit num here + + // We're doing the standard greyscale detail map + // technique which can darken and lighten the + // diffuse texture. + + // TODO: We could add a feature to toggle between this + // and a simple multiplication with the detail map. + + LangElement *statement = new GenOp( "( texture2D(@, @) * 2.0 ) - 1.0", detailMap, inTex ); + output = new GenOp( " @;\r\n", assignColor( statement, Material::Add ) ); +} + +ShaderFeature::Resources DetailFeatGLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + res.numTex = 1; + res.numTexReg = 1; + + return res; +} + +void DetailFeatGLSL::setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ) +{ + GFXTextureObject *tex = stageDat.getTex( MFT_DetailMap ); + if ( tex ) + passData.mTexSlot[ texIndex++ ].texObject = tex; +} + + +//**************************************************************************** +// Vertex position +//**************************************************************************** + +void VertPositionGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // First check for an input position from a previous feature + // then look for the default vertex position. + Var *inPosition = (Var*)LangElement::find( "inPosition" ); + if ( !inPosition ) + inPosition = (Var*)LangElement::find( "position" ); + + // grab connector position + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outPosition = connectComp->getElement( RT_POSITION ); + outPosition->setName( "gl_Position" ); + + MultiLine *meta = new MultiLine; + + Var *modelview = getModelView( componentList, fd.features[MFT_UseInstancing], meta ); + + meta->addStatement( new GenOp( " @ = @ * vec4(@.xyz,1);\r\n", outPosition, modelview, inPosition ) ); + + output = meta; +} + + +//**************************************************************************** +// Reflect Cubemap +//**************************************************************************** + +void ReflectCubeFeatGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine * meta = new MultiLine; + + // If a base or bump tex is present in the material, but not in the + // current pass - we need to add one to the current pass to use + // its alpha channel as a gloss map. Here we just need the tex coords. + if( !fd.features[MFT_DiffuseMap] && + !fd.features[MFT_NormalMap] ) + { + if( fd.materialFeatures[MFT_DiffuseMap] || + fd.materialFeatures[MFT_NormalMap] ) + { + // find incoming texture var + Var *inTex = getVertTexCoord( "texCoord" ); + + // grab connector texcoord register + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outTex = connectComp->getElement( RT_TEXCOORD ); + outTex->setName( "outTexCoord" ); + outTex->setType( "vec2" ); + outTex->mapsToSampler = true; + + // setup language elements to output incoming tex coords to output + meta->addStatement( new GenOp( " @ = @;\r\n", outTex, inTex ) ); + } + } + + // create cubeTrans + Var *cubeTrans = new Var; + cubeTrans->setType( "mat3" ); + cubeTrans->setName( "cubeTrans" ); + cubeTrans->uniform = true; + cubeTrans->constSortPos = cspPrimitive; + + // create cubeEye position + Var *cubeEyePos = new Var; + cubeEyePos->setType( "vec3" ); + cubeEyePos->setName( "cubeEyePos" ); + cubeEyePos->uniform = true; + cubeEyePos->constSortPos = cspPrimitive; + + // search for vert normal + Var *inNormal = (Var*) LangElement::find( "normal" ); + + // cube vert position + Var * cubeVertPos = new Var; + cubeVertPos->setName( "cubeVertPos" ); + cubeVertPos->setType( "vec3" ); + LangElement *cubeVertPosDecl = new DecOp( cubeVertPos ); + + meta->addStatement( new GenOp( " @ = @ * @.xyz;\r\n", + cubeVertPosDecl, cubeTrans, LangElement::find( "position" ) ) ); + + // cube normal + Var * cubeNormal = new Var; + cubeNormal->setName( "cubeNormal" ); + cubeNormal->setType( "vec3" ); + LangElement *cubeNormDecl = new DecOp( cubeNormal ); + + meta->addStatement( new GenOp( " @ = normalize( @ * normalize(@).xyz );\r\n", + cubeNormDecl, cubeTrans, inNormal ) ); + + // eye to vert + Var * eyeToVert = new Var; + eyeToVert->setName( "eyeToVert" ); + eyeToVert->setType( "vec3" ); + LangElement *e2vDecl = new DecOp( eyeToVert ); + + meta->addStatement( new GenOp( " @ = @ - @;\r\n", + e2vDecl, cubeVertPos, cubeEyePos ) ); + + // grab connector texcoord register + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *reflectVec = connectComp->getElement( RT_TEXCOORD ); + reflectVec->setName( "reflectVec" ); + reflectVec->setType( "vec3" ); + reflectVec->mapsToSampler = true; + + meta->addStatement( new GenOp( " @ = reflect(@, @);\r\n", reflectVec, eyeToVert, cubeNormal ) ); + + output = meta; +} + +void ReflectCubeFeatGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine * meta = new MultiLine; + Var *glossColor = NULL; + + // If a base or bump tex is present in the material, but not in the + // current pass - we need to add one to the current pass to use + // its alpha channel as a gloss map. + if( !fd.features[MFT_DiffuseMap] && + !fd.features[MFT_NormalMap] ) + { + if( fd.materialFeatures[MFT_DiffuseMap] || + fd.materialFeatures[MFT_NormalMap] ) + { + // grab connector texcoord register + Var *inTex = getInTexCoord( "outTexCoord", "vec2", true, componentList ); + + // create texture var + Var *newMap = new Var; + newMap->setType( "sampler2D" ); + newMap->setName( "glossMap" ); + newMap->uniform = true; + newMap->sampler = true; + newMap->constNum = Var::getTexUnitNum(); // used as texture unit num here + + // create sample color + Var *color = new Var; + color->setType( "vec4" ); + color->setName( "diffuseColor" ); + LangElement *colorDecl = new DecOp( color ); + + glossColor = color; + + meta->addStatement( new GenOp( " @ = texture2D( @, @ );\r\n", colorDecl, newMap, inTex ) ); + } + } + else + { + glossColor = (Var*) LangElement::find( "diffuseColor" ); + if( !glossColor ) + glossColor = (Var*) LangElement::find( "bumpNormal" ); + } + + // grab connector texcoord register + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *reflectVec = connectComp->getElement( RT_TEXCOORD ); + reflectVec->setName( "reflectVec" ); + reflectVec->setType( "vec3" ); + reflectVec->mapsToSampler = true; + + // create cubemap var + Var *cubeMap = new Var; + cubeMap->setType( "samplerCube" ); + cubeMap->setName( "cubeMap" ); + cubeMap->uniform = true; + cubeMap->sampler = true; + cubeMap->constNum = Var::getTexUnitNum(); // used as texture unit num here + + // TODO: Restore the lighting attenuation here! + Var *attn = NULL; + //if ( fd.materialFeatures[MFT_DynamicLight] ) + //attn = (Var*)LangElement::find("attn"); + //else + if ( fd.materialFeatures[MFT_RTLighting] ) + attn =(Var*)LangElement::find("d_NL_Att"); + + LangElement *texCube = new GenOp( "textureCube( @, @ )", cubeMap, reflectVec ); + LangElement *lerpVal = NULL; + Material::BlendOp blendOp = Material::LerpAlpha; + + // Note that the lerpVal needs to be a float4 so that + // it will work with the LerpAlpha blend. + + if ( glossColor ) + { + if ( attn ) + lerpVal = new GenOp( "@ * saturate( @ )", glossColor, attn ); + else + lerpVal = glossColor; + } + else + { + if ( attn ) + lerpVal = new GenOp( "saturate( @ ).xxxx", attn ); + else + blendOp = Material::Mul; + } + + meta->addStatement( new GenOp( " @;\r\n", assignColor( texCube, blendOp, lerpVal ) ) ); + output = meta; +} + +ShaderFeature::Resources ReflectCubeFeatGLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + + if( fd.features[MFT_DiffuseMap] || + fd.features[MFT_NormalMap] ) + { + res.numTex = 1; + res.numTexReg = 1; + } + else + { + res.numTex = 2; + res.numTexReg = 2; + } + + return res; +} + +void ReflectCubeFeatGLSL::setTexData( Material::StageData &stageDat, + const MaterialFeatureData &stageFeatures, + RenderPassData &passData, + U32 &texIndex ) +{ + // set up a gloss map if one is not present in the current pass + // but is present in the current material stage + if( !passData.mFeatureData.features[MFT_DiffuseMap] && + !passData.mFeatureData.features[MFT_NormalMap] ) + { + GFXTextureObject *tex = stageDat.getTex( MFT_DetailMap ); + if ( tex && + stageFeatures.features[MFT_DiffuseMap] ) + passData.mTexSlot[ texIndex++ ].texObject = tex; + else + { + tex = stageDat.getTex( MFT_NormalMap ); + + if ( tex && + stageFeatures.features[ MFT_NormalMap ] ) + passData.mTexSlot[ texIndex++ ].texObject = tex; + } + } + + if( stageDat.getCubemap() ) + { + passData.mCubeMap = stageDat.getCubemap(); + passData.mTexType[texIndex++] = Material::Cube; + } + else + { + if( stageFeatures.features[MFT_CubeMap] ) + { + // assuming here that it is a scenegraph cubemap + passData.mTexType[texIndex++] = Material::SGCube; + } + } + +} + + +//**************************************************************************** +// RTLighting +//**************************************************************************** + +RTLightingFeatGLSL::RTLightingFeatGLSL() + : mDep( "shaders/common/gl/lighting.glsl" ) +{ + addDependency( &mDep ); +} + +void RTLightingFeatGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + // Special case for lighting imposters. We dont have a vert normal and may not + // have a normal map. Generate and pass the normal data the pixel shader needs. + if ( fd.features[MFT_ImposterVert] ) + { + if ( !fd.features[MFT_NormalMap] ) + { + Var *eyePos = (Var*)LangElement::find( "eyePosWorld" ); + if ( !eyePos ) + { + eyePos = new Var( "eyePosWorld", "float3" ); + eyePos->uniform = true; + eyePos->constSortPos = cspPass; + } + + //TODO: should this be the same as "Find the incoming vertex normal" below? + Var *inPosition = (Var*)LangElement::find( "position" ); + + Var *outNormal = connectComp->getElement( RT_TEXCOORD ); + outNormal->setName( "wsNormal" ); + outNormal->setStructName( "OUT" ); + outNormal->setType( "float3" ); + outNormal->mapsToSampler = false; + + // Transform the normal to world space. + meta->addStatement( new GenOp( " @ = normalize( @ - @.xyz );\r\n", outNormal, eyePos, inPosition ) ); + } + + addOutWsPosition( componentList, fd.features[MFT_UseInstancing], meta ); + + output = meta; + + return; + } + + // Find the incoming vertex normal. + Var *inNormal = (Var*)LangElement::find( "inNormal" ); + if ( !inNormal ) + inNormal = (Var*)LangElement::find( "normal" ); + + // Skip out on realtime lighting if we don't have a normal + // or we're doing some sort of baked lighting. + if ( !inNormal || + fd.features[MFT_LightMap] || + fd.features[MFT_ToneMap] || + fd.features[MFT_VertLit] ) + return; + + // Get the transform to world space. + Var *objTrans = getObjTrans( componentList, fd.features[MFT_UseInstancing], meta ); + + // If there isn't a normal map then we need to pass + // the world space normal to the pixel shader ourselves. + if ( !fd.features[MFT_NormalMap] ) + { + Var *outNormal = connectComp->getElement( RT_TEXCOORD ); + outNormal->setName( "outWsNormal" ); + outNormal->setType( "vec3" ); + outNormal->mapsToSampler = false; + + // Transform the normal to world space. + meta->addStatement( new GenOp( " @ = ( @ * vec4( normalize( @ ), 0.0 ) ).xyz;\r\n", outNormal, objTrans, inNormal ) ); + } + + // Get the input position. + Var *inPosition = (Var*)LangElement::find( "inPosition" ); + if ( !inPosition ) + inPosition = (Var*)LangElement::find( "position" ); + + addOutWsPosition( componentList, fd.features[MFT_UseInstancing], meta ); + + output = meta; +} + +void RTLightingFeatGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // Skip out on realtime lighting if we don't have a normal + // or we're doing some sort of baked lighting. + // + // TODO: We can totally detect for this in the material + // feature setup... we should move it out of here! + // + if ( fd.features[MFT_LightMap] || fd.features[MFT_ToneMap] || fd.features[MFT_VertLit] ) + return; + + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + MultiLine *meta = new MultiLine; + + // Look for a wsNormal or grab it from the connector. + Var *wsNormal = (Var*)LangElement::find( "wsNormal" ); + if ( !wsNormal ) + { + Var *outWsNormal = connectComp->getElement( RT_TEXCOORD ); + outWsNormal->setName( "outWsNormal" ); + outWsNormal->setType( "vec3" ); + + wsNormal = new Var( "wsNormal", "vec3" ); + + // If we loaded the normal its our resposibility + // to normalize it... the interpolators won't. + meta->addStatement( new GenOp( " @ = normalize( @ );\r\n", + new DecOp( wsNormal ), outWsNormal ) ); + } + + Var *wsPosition = getInWsPosition( componentList ); + + // If we have a specular feature then we need to + // get the world space view vector to pass to the + // lighting calculation. + Var *wsView = new Var( "wsView", "vec3" ); + if ( fd.features[MFT_PixSpecular] ) + { + Var *eyePos = (Var*)LangElement::find( "eyePosWorld" ); + if ( !eyePos ) + { + eyePos = new Var; + eyePos->setType( "vec3" ); + eyePos->setName( "eyePosWorld" ); + eyePos->uniform = true; + eyePos->constSortPos = cspPass; + } + + meta->addStatement( new GenOp( " @ = normalize( @ - @ );\r\n", + new DecOp( wsView ), eyePos, wsPosition ) ); + } + else + meta->addStatement( new GenOp( " @ = vec3( 0 );\r\n", new DecOp( wsView ) ) ); + + // Create temporaries to hold results of lighting. + Var *rtShading = new Var( "rtShading", "vec4" ); + Var *specular = new Var( "specular", "vec4" ); + meta->addStatement( new GenOp( " @; @;\r\n", + new DecOp( rtShading ), new DecOp( specular ) ) ); + + // Calculate the diffuse shading and specular powers. + meta->addStatement( new GenOp( " compute4Lights( @, @, @, @, @ );\r\n", + wsView, wsPosition, wsNormal, rtShading, specular ) ); + + // Look for a light mask generated from a previous + // feature (this is done for BL terrain lightmaps). + Var *lightMask = (Var*)LangElement::find( "lightMask" ); + if ( lightMask ) + meta->addStatement( new GenOp( " @.rgb *= @;\r\n", rtShading, lightMask ) ); + + // Apply the lighting to the diffuse color. + LangElement *lighting = new GenOp( "vec4( @.rgb + ambient.rgb, 1 )", rtShading ); + meta->addStatement( new GenOp( " @;\r\n", assignColor( lighting, Material::Mul ) ) ); + output = meta; +} + +ShaderFeature::Resources RTLightingFeatGLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + + // These features disable realtime lighting. + if ( !fd.features[MFT_LightMap] && + !fd.features[MFT_ToneMap] && + !fd.features[MFT_VertLit] ) + { + // If enabled we pass the position. + res.numTexReg = 1; + + // If there isn't a bump map then we pass the + // world space normal as well. + if ( !fd.features[MFT_NormalMap] ) + res.numTexReg++; + } + + return res; +} + + +//**************************************************************************** +// Fog +//**************************************************************************** + +FogFeatGLSL::FogFeatGLSL() + : mFogDep( "shaders/common/gl/torque.glsl" ) +{ + addDependency( &mFogDep ); +} + +void FogFeatGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + + const bool vertexFog = Con::getBoolVariable( "$useVertexFog", false ); + if ( vertexFog || GFX->getPixelShaderVersion() < 3.0 ) + { + // Grab the eye position. + Var *eyePos = (Var*)LangElement::find( "eyePosWorld" ); + if ( !eyePos ) + { + eyePos = new Var( "eyePosWorld", "vec3" ); + eyePos->uniform = true; + eyePos->constSortPos = cspPass; + } + + Var *fogData = new Var( "fogData", "vec3" ); + fogData->uniform = true; + fogData->constSortPos = cspPass; + + Var *wsPosition = new Var( "fogPos", "vec3" ); + getWsPosition( componentList, + fd.features[MFT_UseInstancing], + meta, + new DecOp( wsPosition ) ); + + // We pass the fog amount to the pixel shader. + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *fogAmount = connectComp->getElement( RT_TEXCOORD ); + fogAmount->setName( "fogAmount" ); + fogAmount->setType( "float" ); + fogAmount->mapsToSampler = false; + + meta->addStatement( new GenOp( " @ = saturate( computeSceneFog( @, @, @.r, @.g, @.b ) );\r\n", + fogAmount, eyePos, wsPosition, fogData, fogData, fogData ) ); + } + else + { + // We fog in world space... make sure the world space + // position is passed to the pixel shader. This is + // often already passed for lighting, so it takes up + // no extra output registers. + addOutWsPosition( componentList, fd.features[MFT_UseInstancing], meta ); + } + + output = meta; +} + +void FogFeatGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + + Var *fogColor = new Var; + fogColor->setType( "vec4" ); + fogColor->setName( "fogColor" ); + fogColor->uniform = true; + fogColor->constSortPos = cspPass; + + // Get the out color. + Var *color = (Var*) LangElement::find( "col" ); + if ( !color ) + { + color = new Var; + color->setType( "vec4" ); + color->setName( "col" ); + } + + Var *fogAmount; + + const bool vertexFog = Con::getBoolVariable( "$useVertexFog", false ); + if ( vertexFog || GFX->getPixelShaderVersion() < 3.0 ) + { + // Per-vertex.... just get the fog amount. + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + fogAmount = connectComp->getElement( RT_TEXCOORD ); + fogAmount->setName( "fogAmount" ); + fogAmount->setType( "float" ); + } + else + { + Var *wsPosition = getInWsPosition( componentList ); + + // grab the eye position + Var *eyePos = (Var*)LangElement::find( "eyePosWorld" ); + if ( !eyePos ) + { + eyePos = new Var( "eyePosWorld", "vec3" ); + eyePos->uniform = true; + eyePos->constSortPos = cspPass; + } + + Var *fogData = new Var( "fogData", "vec3" ); + fogData->uniform = true; + fogData->constSortPos = cspPass; + + /// Get the fog amount. + fogAmount = new Var( "fogAmount", "float" ); + meta->addStatement( new GenOp( " @ = saturate( computeSceneFog( @, @, @.r, @.g, @.b ) );\r\n", + new DecOp( fogAmount ), eyePos, wsPosition, fogData, fogData, fogData ) ); + } + + // Lerp between the fog color and diffuse color. + LangElement *fogLerp = new GenOp( "mix( @.rgb, @.rgb, @ )", fogColor, color, fogAmount ); + meta->addStatement( new GenOp( " @.rgb = @;\r\n", color, fogLerp ) ); + + output = meta; +} + +ShaderFeature::Resources FogFeatGLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + res.numTexReg = 1; + return res; +} + + +//**************************************************************************** +// Visibility +//**************************************************************************** + +void VisibilityFeatGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // Pass screen space position to pixel shader to compute a full screen buffer uv + Var* ssPos = ( Var* ) LangElement::find( "screenspacePos" ); + if( !ssPos ) + { + ShaderConnector* connectComp = dynamic_cast< ShaderConnector* >( componentList[C_CONNECTOR] ); + AssertFatal( connectComp, "VisibilityFeatGLSL::processVert - invalid ShaderConnector" ); + + Var* ssPos = connectComp->getElement( RT_TEXCOORD ); + ssPos->setName( "screenspacePos" ); + ssPos->setType( "vec4" ); + + Var* outPosition = ( Var* ) LangElement::find( "gl_Position" ); + AssertFatal( outPosition, "VisibilityFeatGLSL::processVert - No gl_Position" ); + + output = new GenOp( " @ = @;\r\n", ssPos, outPosition ); + } +} + +void VisibilityFeatGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // Look up output color. + + Var* color = ( Var* ) LangElement::find( "col" ); + if( !color ) + { + output = NULL; + return; + } + + Var* visibility = (Var*)LangElement::find( "visibility" ); + + // Create visibility var. + if ( !visibility ) + { + visibility = new Var(); + visibility->setType( "float" ); + visibility->setName( "visibility" ); + visibility->uniform = true; + visibility->constSortPos = cspPotentialPrimitive; + } + + Var* ssPos = ( Var* ) LangElement::find( "screenspacePos" ); + if( !ssPos ) + { + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + AssertFatal( connectComp, "VisibilityFeatGLSL::processPix - invalid ShaderConnector" ); + + ssPos = connectComp->getElement( RT_TEXCOORD ); + ssPos->setName( "screenspacePos" ); + ssPos->setType( "vec4" ); + } + + MultiLine* meta = new MultiLine; + output = meta; + + //Compute final visibility from incoming visibility and strength of fade effects + Var *finalVisibility = new Var( "finalVisibility", "float" ); + meta->addStatement(new GenOp( " @ = @;\r\n", new DecOp( finalVisibility ), visibility ) ); + Var *imposterFade = (Var*) LangElement::find( "imposterFade" ); + if ( imposterFade ) + meta->addStatement( new GenOp( " @ *= @;\r\n", finalVisibility, imposterFade ) ); + Var *foliageFade = (Var*) LangElement::find( "foliageFade" ); + if ( foliageFade ) + meta->addStatement( new GenOp( " @ *= @;\r\n", finalVisibility, foliageFade ) ); + + if( !fd.features[ MFT_IsTranslucent ] ) + { + // Create fizzMap sampler. + + Var* fizzMap = ( Var* ) LangElement::find( "fizzMap" ); + if( !fizzMap ) + { + fizzMap = new Var; + fizzMap->setType( "sampler2D" ); + fizzMap->setName( "fizzMap" ); + fizzMap->uniform = true; + fizzMap->sampler = true; + fizzMap->constNum = Var::getTexUnitNum(); + } + + // Create fizzScale uniform. + + Var* fizzScale = new Var; + fizzScale->setType( "float2" ); + fizzScale->setName( "fizzScale" ); + fizzScale->uniform = true; + fizzScale->constSortPos = cspPass; + + meta->addStatement( new GenOp( " float fizz = tex2D( @, (@.xy / @.w) * fizzScale ).r + 0.15;\r\n", fizzMap, ssPos, ssPos ) ); + meta->addStatement( new GenOp( " if( ( fizz * @ - 0.329 ) < 0.0 )\r\n" + " discard\r\n;", finalVisibility ) ); + } + else if( color ) + { + meta->addStatement( new GenOp( " @.w *= @;\r\n", color, finalVisibility ) ); + } +} + +ShaderFeature::Resources VisibilityFeatGLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + + if ( !fd.features[ MFT_IsTranslucent ] ) + { + res.numTex = 1; + res.numTexReg = 1; + } + + return res; +} + +void VisibilityFeatGLSL::setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ) +{ + if ( !fd.features[ MFT_IsTranslucent ] ) + { + GFXTexHandle texHandle( "core/art/fizz_noise.dds", &GFXDefaultStaticDiffuseProfile, "VisibilityFeatHLSL_fizz_noise" ); + stageDat.setTex( MFT_Visibility, texHandle ); + + GFXTextureObject *tex = stageDat.getTex( MFT_Visibility ); + if ( tex ) + passData.mTexSlot[ texIndex++ ].texObject = tex; + } +} + + +//**************************************************************************** +// AlphaTest +//**************************************************************************** + +void AlphaTestGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // If we're below SM3 and don't have a depth output + // feature then don't waste an instruction here. + if ( GFX->getPixelShaderVersion() < 3.0 && + !fd.features[ MFT_EyeSpaceDepthOut ] && + !fd.features[ MFT_DepthOut ] ) + { + output = NULL; + return; + } + + // If we don't have a color var then we cannot do an alpha test. + Var *color = (Var*)LangElement::find( "col" ); + if ( !color ) + { + output = NULL; + return; + } + + // Now grab the alpha test value. + Var *alphaTestVal = new Var; + alphaTestVal->setType( "float" ); + alphaTestVal->setName( "alphaTestValue" ); + alphaTestVal->uniform = true; + alphaTestVal->constSortPos = cspPotentialPrimitive; + + // Do the clip. + output = new GenOp( " if ( ( @.a - @ ) < 0 ) discard;\r\n", color, alphaTestVal ); +} + + +//**************************************************************************** +// GlowMask +//**************************************************************************** + +void GlowMaskGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + output = NULL; + + // Get the output color... and make it black to mask out + // glow passes rendered before us. + // + // The shader compiler will optimize out all the other + // code above that doesn't contribute to the alpha mask. + Var *color = (Var*)LangElement::find( "col" ); + if ( color ) + output = new GenOp( " @.rgb = 0;\r\n", color ); +} + +//**************************************************************************** +// RenderTargetZero +//**************************************************************************** + +void RenderTargetZeroGLSL::processPix( Vector &componentList, const MaterialFeatureData &fd ) +{ + // Do not actually assign zero, but instead a number so close to zero it may as well be zero. + // This will prevent a divide by zero causing an FP special on float render targets + output = new GenOp( " @;\r\n", assignColor( new GenOp( "0.00001" ), Material::None, NULL, mOutputTargetMask ) ); +} + + +//**************************************************************************** +// HDR Output +//**************************************************************************** + +HDROutGLSL::HDROutGLSL() +: mTorqueDep( "shaders/common/gl/torque.glsl" ) +{ + addDependency( &mTorqueDep ); +} +void HDROutGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // Let the helper function do the work. + Var *color = (Var*)LangElement::find( "col" ); + if ( color ) + output = new GenOp( " @ = hdrEncode( @ );\r\n", color, color ); +} + + +//**************************************************************************** +// FoliageFeatureGLSL +//**************************************************************************** + +#include "T3D/fx/groundCover.h" + +FoliageFeatureGLSL::FoliageFeatureGLSL() +: mDep( "shaders/common/gl/foliage.glsl" ) +{ + addDependency( &mDep ); +} + +void FoliageFeatureGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + + MultiLine *meta = new MultiLine; + + // Get the input variables we need. + + Var *inPosition = (Var*)LangElement::find( "inPosition" ); + if ( !inPosition ) { + // inPosition = (Var*)LangElement::find( "position" ); + inPosition = new Var( "inPosition", "vec3" ); + meta->addStatement( new GenOp( " @ = @;\n", new DecOp( inPosition ), (Var*)LangElement::find( "position" ) ) ); + } + + Var *inColor = (Var*)LangElement::find( "diffuse" ); + Var *outColor = new Var( "inDiffuse", "vec4" ); + meta->addStatement( new GenOp( " @ = @;\n", new DecOp( outColor ), inColor ) ); + + Var *inParams = (Var*)LangElement::find( "texCoord" ); + Var *outParams = getOutTexCoord( "texCoord", + "vec2", + true, + fd.features[MFT_TexAnim], + meta, + componentList ); + + + + // Declare the normal and tangent variables since they do not exist + // in this vert type, but we do need to set them up for others. + + Var *inNormal = (Var*)LangElement::find( "inNormal" ); + if ( !inNormal ) { + inNormal = new Var( "inNormal", "vec3" ); + meta->addStatement( new GenOp( " @ = @;\n", new DecOp( inNormal ), (Var*)LangElement::find( "normal" ) ) ); + } + //Var *normal = (Var*)LangElement::find( "normal" ); + AssertFatal( inNormal, "FoliageFeatureHLSL requires vert normal!" ); + + Var *tangent = new Var; + tangent->setType( "vec3" ); + tangent->setName( "T" ); + LangElement *tangentDec = new DecOp( tangent ); + meta->addStatement( new GenOp( " @;\n", tangentDec ) ); + + // We add a float foliageFade to the OUT structure. + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *fade = connectComp->getElement( RT_TEXCOORD ); + fade->setName( "foliageFade" ); + fade->setType( "float" ); + + // grab the eye position + Var *eyePos = (Var*)LangElement::find( "eyePosWorld" ); + if ( !eyePos ) + { + eyePos = new Var( "eyePosWorld", "vec3" ); + eyePos->uniform = true; + eyePos->constSortPos = cspPass; + } + + + // All actual work is offloaded to this method. + meta->addStatement( new GenOp( " foliageProcessVert( @, @, @, @, @, @, @ );\r\n", inPosition, outColor, inParams, outParams, inNormal, tangent, eyePos ) ); + + // Assign to foliageFade. InColor.a was set to the correct value inside foliageProcessVert. + meta->addStatement( new GenOp( " @ = @.a;\r\n", fade, outColor ) ); + + output = meta; +} + +void FoliageFeatureGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // Find / create IN.foliageFade + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *fade = connectComp->getElement( RT_TEXCOORD ); + fade->setName( "foliageFade" ); + fade->setType( "float" ); +} + +void FoliageFeatureGLSL::determineFeature( Material *material, const GFXVertexFormat *vertexFormat, U32 stageNum, const FeatureType &type, const FeatureSet &features, MaterialFeatureData *outFeatureData ) +{ + // This isn't really necessary since the outFeatureData will be filtered after + // this call. + if ( features.hasFeature( MFT_Foliage ) ) + outFeatureData->features.addFeature( type ); +} + +ShaderFeatureConstHandles* FoliageFeatureGLSL::createConstHandles( GFXShader *shader, SimObject *userObject ) +{ + GroundCover *gcover = dynamic_cast< GroundCover* >( userObject ); + AssertFatal( gcover != NULL, "FoliageFeatureGLSL::createConstHandles - userObject was not valid!" ); + + GroundCoverShaderConstHandles *handles = new GroundCoverShaderConstHandles(); + handles->mGroundCover = gcover; + + handles->init( shader ); + + return handles; +} + + +//**************************************************************************** +// ParticleNormal +//**************************************************************************** + +void ParticleNormalFeatureGLSL::processVert(Vector &componentList, const MaterialFeatureData &fd) +{ + MultiLine *meta = new MultiLine; + output = meta; + + // Calculate normal and tangent values since we want to keep particle verts + // as light-weight as possible + + Var *normal = (Var*) LangElement::find("normal"); + if(normal == NULL) + { + normal = new Var; + normal->setType( "vec3" ); + normal->setName( "normal" ); + + // These values are not accidental. It is slightly adjusted from facing straight into the + // screen because there is a discontinuity at (0, 1, 0) for gbuffer encoding. Do not + // cause this value to be (0, -1, 0) or interlaced normals will be discontinuous. + // [11/23/2009 Pat] + meta->addStatement(new GenOp(" @ = vec3(0.0, -0.97, 0.14);\r\n", new DecOp(normal))); + } + + Var *T = (Var*) LangElement::find( "T" ); + if(T == NULL) + { + T = new Var; + T->setType( "vec3" ); + T->setName( "T" ); + meta->addStatement(new GenOp(" @ = vec3(0.0, 0.0, -1.0);\r\n", new DecOp(T))); + } +} + + +//**************************************************************************** +// ImposterVertFeatureGLSL +//**************************************************************************** + +ImposterVertFeatureGLSL::ImposterVertFeatureGLSL() +: mDep( "shaders/common/gl/imposter.glsl" ) +{ + addDependency( &mDep ); +} + +void ImposterVertFeatureGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + output = meta; + + // Get the input vertex variables. + Var *inPosition = (Var*)LangElement::find( "position" ); + Var *inMiscParams = (Var*)LangElement::find( "tcImposterParams" ); + Var *inUpVec = (Var*)LangElement::find( "tcImposterUpVec" ); + Var *inRightVec = (Var*)LangElement::find( "tcImposterRightVec" ); + + // Get the input shader constants. + Var *imposterLimits = new Var; + imposterLimits->setType( "vec4" ); + imposterLimits->setName( "imposterLimits" ); + imposterLimits->uniform = true; + imposterLimits->constSortPos = cspPotentialPrimitive; + + Var *imposterUVs = new Var; + imposterUVs->setType( "vec4" ); + imposterUVs->setName( "imposterUVs" ); + imposterUVs->arraySize = 64; // See imposter.hlsl + imposterUVs->uniform = true; + imposterUVs->constSortPos = cspPotentialPrimitive; + + Var *eyePos = (Var*)LangElement::find( "eyePosWorld" ); + if ( !eyePos ) + { + eyePos = new Var( "eyePosWorld", "vec3" ); + eyePos->uniform = true; + eyePos->constSortPos = cspPass; + } + + // Declare the outputs from this feature. + Var *outInPosition = new Var; + outInPosition->setType( "vec3" ); + outInPosition->setName( "inPosition" ); + meta->addStatement( new GenOp( " @;\r\n", new DecOp( outInPosition ) ) ); + + Var *outTexCoord = new Var; + outTexCoord->setType( "vec2" ); + outTexCoord->setName( "texCoord" ); + meta->addStatement( new GenOp( " @;\r\n", new DecOp( outTexCoord ) ) ); + + Var *outWorldToTangent = new Var; + outWorldToTangent->setType( "mat3" ); + outWorldToTangent->setName( "worldToTangent" ); + meta->addStatement( new GenOp( " @;\r\n", new DecOp( outWorldToTangent ) ) ); + //Var *outWorldToTangent = getOutWorldToTangent( componentList, meta, fd ); + + // Add imposterFade to the OUT structure. + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outFade = connectComp->getElement( RT_TEXCOORD ); + outFade->setName( "imposterFade" ); + outFade->setType( "float" ); + + // Assign OUT.imposterFade + meta->addStatement( new GenOp( " @ = @.y;\r\n", outFade, inMiscParams ) ); + + // All actual work is done in this method. + meta->addStatement( new GenOp( " imposter_v( @.xyz, int(@.w), @.x * length(@), normalize(@), normalize(@), int(@.y), int(@.x), @.z, bool(@.w), @, @, @, @, @ );\r\n", + + inPosition, + inPosition, + + inMiscParams, + inRightVec, + + inUpVec, + inRightVec, + + imposterLimits, + imposterLimits, + imposterLimits, + imposterLimits, + + eyePos, + imposterUVs, + + outInPosition, + outTexCoord, + outWorldToTangent ) ); + + // Copy the position to wsPosition for use in shaders + // down stream instead of looking for objTrans. + Var *wsPosition = new Var; + wsPosition->setType( "vec3" ); + wsPosition->setName( "wsPosition" ); + meta->addStatement( new GenOp( " @ = @.xyz;\r\n", new DecOp( wsPosition ), outInPosition ) ); + + // If we new viewToTangent... its the same as the + // world to tangent for an imposter. + Var *viewToTangent = new Var; + viewToTangent->setType( "mat3" ); + viewToTangent->setName( "viewToTangent" ); + meta->addStatement( new GenOp( " @ = @;\r\n", new DecOp( viewToTangent ), outWorldToTangent ) ); +} + +void ImposterVertFeatureGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + + // Find / create IN.imposterFade + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *fade = connectComp->getElement( RT_TEXCOORD ); + fade->setName( "imposterFade" ); + fade->setType( "float" ); + + /* + // Find / create visibility + Var *visibility = (Var*) LangElement::find( "visibility" ); + if ( !visibility ) + { + visibility = new Var(); + visibility->setType( "float" ); + visibility->setName( "visibility" ); + visibility->uniform = true; + visibility->constSortPos = cspPotentialPrimitive; + } + + MultiLine *meta = new MultiLine; + + // Multiply foliageFade into visibility. + //meta->addStatement( new GenOp( " @ *= @;\r\n", visibility, fade ) ); + + output = meta; + */ +} + +void ImposterVertFeatureGLSL::determineFeature( Material *material, + const GFXVertexFormat *vertexFormat, + U32 stageNum, + const FeatureType &type, + const FeatureSet &features, + MaterialFeatureData *outFeatureData ) +{ + if ( features.hasFeature( MFT_ImposterVert ) ) + outFeatureData->features.addFeature( MFT_ImposterVert ); +} \ No newline at end of file diff --git a/Engine/source/shaderGen/GLSL/shaderFeatureGLSL.h b/Engine/source/shaderGen/GLSL/shaderFeatureGLSL.h new file mode 100644 index 000000000..14af0300f --- /dev/null +++ b/Engine/source/shaderGen/GLSL/shaderFeatureGLSL.h @@ -0,0 +1,643 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _SHADERGEN_GLSL_SHADERFEATUREGLSL_H_ +#define _SHADERGEN_GLSL_SHADERFEATUREGLSL_H_ + +#ifndef _SHADERFEATURE_H_ + #include "shaderGen/shaderFeature.h" +#endif +#ifndef _MATERIALFEATUREDATA_H_ + #include "materials/materialFeatureData.h" +#endif + +struct LangElement; +struct MaterialFeatureData; +struct RenderPassData; + + +class ShaderFeatureGLSL : public ShaderFeature +{ +public: + ShaderFeatureGLSL(); + + /// + Var* getOutTexCoord( const char *name, + const char *type, + bool mapsToSampler, + bool useTexAnim, + MultiLine *meta, + Vector &componentList ); + + /// Returns an input texture coord by name adding it + /// to the input connector if it doesn't exist. + static Var* getInTexCoord( const char *name, + const char *type, + bool mapsToSampler, + Vector &componentList ); + + /// Returns the "objToTangentSpace" transform or creates one if this + /// is the first feature to need it. + Var* getOutObjToTangentSpace( Vector &componentList, + MultiLine *meta, + const MaterialFeatureData &fd ); + + /// Returns the existing output "worldToTangent" transform or + /// creates one if this is the first feature to need it. + Var* getOutWorldToTangent( Vector &componentList, + MultiLine *meta, + const MaterialFeatureData &fd ); + + /// Returns the input "worldToTanget" space transform + /// adding it to the input connector if it doesn't exist. + static Var* getInWorldToTangent( Vector &componentList ); + + /// Returns the existing output "viewToTangent" transform or + /// creates one if this is the first feature to need it. + Var* getOutViewToTangent( Vector &componentList, + MultiLine *meta, + const MaterialFeatureData &fd ); + + /// Returns the input "viewToTangent" space transform + /// adding it to the input connector if it doesn't exist. + static Var* getInViewToTangent( Vector &componentList ); + + /// Calculates the world space position in the vertex shader and + /// assigns it to the passed language element. It does not pass /// it across the connector to the pixel shader. + /// @see addOutWsPosition + void getWsPosition( Vector &componentList, + + bool useInstancing, + MultiLine *meta, + LangElement *wsPosition ); + + /// Adds the "wsPosition" to the input connector if it doesn't exist. + Var* addOutWsPosition( Vector &componentList, + + bool useInstancing, + MultiLine *meta ); + + /// Returns the input world space position from the connector. + static Var* getInWsPosition( Vector &componentList ); + + /// Returns the world space view vector from the wsPosition. + static Var* getWsView( Var *wsPosition, MultiLine *meta ); + + /// Returns the input normal map texture. + static Var* getNormalMapTex(); + + /// + Var* addOutDetailTexCoord( Vector &componentList, + MultiLine *meta, + bool useTexAnim ); + + /// + Var* getObjTrans( Vector &componentList, + bool useInstancing, + MultiLine *meta ); + + /// + Var* getModelView( Vector &componentList, + bool useInstancing, + MultiLine *meta ); + + /// + Var* getWorldView( Vector &componentList, + bool useInstancing, + MultiLine *meta ); + + /// + Var* getInvWorldView( Vector &componentList, + bool useInstancing, + MultiLine *meta ); + + + // ShaderFeature + Var* getVertTexCoord( const String &name ); + LangElement* setupTexSpaceMat( Vector &componentList, Var **texSpaceMat ); + LangElement* assignColor( LangElement *elem, Material::BlendOp blend, LangElement *lerpElem = NULL, ShaderFeature::OutputTarget outputTarget = ShaderFeature::DefaultTarget ); + LangElement* expandNormalMap( LangElement *sampleNormalOp, LangElement *normalDecl, LangElement *normalVar, const MaterialFeatureData &fd ); +}; + + +class NamedFeatureGLSL : public ShaderFeatureGLSL +{ +protected: + String mName; + +public: + NamedFeatureGLSL( const String &name ) + : mName( name ) + {} + + virtual String getName() { return mName; } +}; + + +class RenderTargetZeroGLSL : public ShaderFeatureGLSL +{ +protected: ShaderFeature::OutputTarget mOutputTargetMask; + String mFeatureName; + +public: + RenderTargetZeroGLSL( const ShaderFeature::OutputTarget target ) + : mOutputTargetMask( target ) + { + char buffer[256]; dSprintf(buffer, sizeof(buffer), "Render Target Output = 0.0, output mask %04b", mOutputTargetMask); + mFeatureName = buffer; } + + virtual String getName() { return mFeatureName; } + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + virtual U32 getOutputTargets( const MaterialFeatureData &fd ) const { return + mOutputTargetMask; } +}; + + +/// Vertex position +class VertPositionGLSL : public ShaderFeatureGLSL +{ +public: + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual String getName() + { + return "Vert Position"; + } + + virtual void determineFeature( Material *material, + const GFXVertexFormat *vertexFormat, + U32 stageNum, + const FeatureType &type, + const FeatureSet &features, + MaterialFeatureData *outFeatureData ) + { + // This feature is always on! + outFeatureData->features.addFeature( type ); + } + +}; + + +/// Vertex lighting based on the normal and the light +/// direction passed through the vertex color. +class RTLightingFeatGLSL : public ShaderFeatureGLSL +{ +protected: + + ShaderIncludeDependency mDep; + +public: + + RTLightingFeatGLSL(); + + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp(){ return Material::None; } + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual String getName() + { + return "RT Lighting"; + } +}; + + +/// Base texture +class DiffuseMapFeatGLSL : public ShaderFeatureGLSL +{ +public: + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp(){ return Material::LerpAlpha; } + + virtual Resources getResources( const MaterialFeatureData &fd ); + + // Sets textures and texture flags for current pass + virtual void setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ); + + virtual String getName() + { + return "Base Texture"; + } +}; + + +/// Overlay texture +class OverlayTexFeatGLSL : public ShaderFeatureGLSL +{ +public: + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp(){ return Material::LerpAlpha; } + + virtual Resources getResources( const MaterialFeatureData &fd ); + + // Sets textures and texture flags for current pass + virtual void setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ); + + virtual String getName() + { + return "Overlay Texture"; + } +}; + + +/// Diffuse color +class DiffuseFeatureGLSL : public ShaderFeatureGLSL +{ +public: + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp(){ return Material::None; } + + virtual String getName() + { + return "Diffuse Color"; + } +}; + + +/// Diffuse vertex color +class DiffuseVertColorFeatureGLSL : public ShaderFeatureGLSL +{ +public: + + virtual void processVert( Vector< ShaderComponent* >& componentList, + const MaterialFeatureData& fd ); + virtual void processPix( Vector< ShaderComponent* >&componentList, + const MaterialFeatureData& fd ); + + virtual Material::BlendOp getBlendOp(){ return Material::None; } + + virtual String getName() + { + return "Diffuse Vertex Color"; + } +}; + + +/// Lightmap +class LightmapFeatGLSL : public ShaderFeatureGLSL +{ +public: + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp(){ return Material::LerpAlpha; } + + virtual Resources getResources( const MaterialFeatureData &fd ); + + // Sets textures and texture flags for current pass + virtual void setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ); + + virtual String getName() + { + return "Lightmap"; + } + + virtual U32 getOutputTargets( const MaterialFeatureData &fd ) const; +}; + + +/// Tonemap +class TonemapFeatGLSL : public ShaderFeatureGLSL +{ +public: + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp(){ return Material::LerpAlpha; } + + virtual Resources getResources( const MaterialFeatureData &fd ); + + // Sets textures and texture flags for current pass + virtual void setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ); + + virtual String getName() + { + return "Tonemap"; + } + + virtual U32 getOutputTargets( const MaterialFeatureData &fd ) const; +}; + + +/// Baked lighting stored on the vertex color +class VertLitGLSL : public ShaderFeatureGLSL +{ +public: + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp(){ return Material::None; } + + virtual String getName() + { + return "Vert Lit"; + } + + virtual U32 getOutputTargets( const MaterialFeatureData &fd ) const; +}; + + +/// Detail map +class DetailFeatGLSL : public ShaderFeatureGLSL +{ +public: + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp(){ return Material::Mul; } + + // Sets textures and texture flags for current pass + virtual void setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ); + + virtual String getName() + { + return "Detail"; + } +}; + + +/// Reflect Cubemap +class ReflectCubeFeatGLSL : public ShaderFeatureGLSL +{ +public: + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Resources getResources( const MaterialFeatureData &fd ); + + // Sets textures and texture flags for current pass + virtual void setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ); + + virtual String getName() + { + return "Reflect Cube"; + } +}; + + +/// Fog +class FogFeatGLSL : public ShaderFeatureGLSL +{ +protected: + + ShaderIncludeDependency mFogDep; + +public: + FogFeatGLSL(); + + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp() { return Material::LerpAlpha; } + + virtual String getName() + { + return "Fog"; + } +}; + + +/// Tex Anim +class TexAnimGLSL : public ShaderFeatureGLSL +{ +public: + virtual Material::BlendOp getBlendOp() { return Material::None; } + + virtual String getName() + { + return "Texture Animation"; + } +}; + + +/// Visibility +class VisibilityFeatGLSL : public ShaderFeatureGLSL +{ +public: + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual void setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ); + + virtual Material::BlendOp getBlendOp() { return Material::None; } + + virtual String getName() + { + return "Visibility"; + } +}; + + +/// +class AlphaTestGLSL : public ShaderFeatureGLSL +{ +public: + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp() { return Material::None; } + + virtual String getName() + { + return "Alpha Test"; + } +}; + + +/// Special feature used to mask out the RGB color for +/// non-glow passes of glow materials. +/// @see RenderGlowMgr +class GlowMaskGLSL : public ShaderFeatureGLSL +{ +public: + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp() { return Material::None; } + + virtual String getName() + { + return "Glow Mask"; + } +}; + + +/// This should be the final feature on most pixel shaders which +/// encodes the color for the current HDR target format. +/// @see HDRPostFx/// @see LightManager +/// @see torque.glsl +class HDROutGLSL : public ShaderFeatureGLSL +{ +protected: + + ShaderIncludeDependency mTorqueDep; + +public: + + HDROutGLSL(); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp() { return Material::None; } + + virtual String getName() { return "HDR Output"; } +}; + + +/// +class FoliageFeatureGLSL : public ShaderFeatureGLSL{ +protected: + + ShaderIncludeDependency mDep; + +public: + + FoliageFeatureGLSL(); + + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual String getName() + { + return "Foliage Feature"; + } + + virtual void determineFeature( Material *material, + const GFXVertexFormat *vertexFormat, + U32 stageNum, + const FeatureType &type, + const FeatureSet &features, + MaterialFeatureData *outFeatureData ); + virtual ShaderFeatureConstHandles* createConstHandles( GFXShader *shader, SimObject *userObject ); +}; + + +/// +class ParticleNormalFeatureGLSL : public ShaderFeatureGLSL +{ +public: + + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual String getName() + { + return "Particle Normal Generation Feature"; + } + +}; + +class ImposterVertFeatureGLSL : public ShaderFeatureGLSL +{ +protected: + + ShaderIncludeDependency mDep; + +public: + ImposterVertFeatureGLSL(); + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual String getName() { return "Imposter Vert"; } + + virtual void determineFeature( Material *material, + const GFXVertexFormat *vertexFormat, + U32 stageNum, + const FeatureType &type, + const FeatureSet &features, + MaterialFeatureData *outFeatureData ); +}; + + +#endif // _SHADERGEN_GLSL_SHADERFEATUREGLSL_H_ diff --git a/Engine/source/shaderGen/GLSL/shaderGenGLSL.cpp b/Engine/source/shaderGen/GLSL/shaderGenGLSL.cpp new file mode 100644 index 000000000..ebbf2b3d8 --- /dev/null +++ b/Engine/source/shaderGen/GLSL/shaderGenGLSL.cpp @@ -0,0 +1,179 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "shaderGen/GLSL/shaderGenGLSL.h" + +#include "shaderGen/GLSL/shaderCompGLSL.h" + + +void ShaderGenPrinterGLSL::printShaderHeader( Stream& stream ) +{ + const char *header1 = "//*****************************************************************************\r\n"; + const char *header2 = "// Torque -- GLSL procedural shader\r\n"; + + stream.write( dStrlen(header1), header1 ); + stream.write( dStrlen(header2), header2 ); + stream.write( dStrlen(header1), header1 ); + + // Cheap HLSL compatibility. + const char* header3 = "#include \"shaders/common/gl/hlslCompat.glsl\"\r\n"; + stream.write( dStrlen(header3), header3 ); + + const char* header4 = "\r\n"; + stream.write( dStrlen(header4), header4 ); +} + +void ShaderGenPrinterGLSL::printMainComment( Stream& stream ) +{ + // Print out main function definition + const char * header5 = "// Main \r\n"; + const char * line = "//-----------------------------------------------------------------------------\r\n"; + + stream.write( dStrlen(line), line ); + stream.write( dStrlen(header5), header5 ); + stream.write( dStrlen(line), line ); +} + +void ShaderGenPrinterGLSL::printVertexShaderCloser( Stream& stream ) +{ + const char *closer = "}\r\n"; + stream.write( dStrlen(closer), closer ); +} + +void ShaderGenPrinterGLSL::printPixelShaderOutputStruct( Stream& stream, const MaterialFeatureData &featureData ) +{ + // Nothing here +} + +void ShaderGenPrinterGLSL::printPixelShaderCloser( Stream& stream ) +{ + const char *closer = " gl_FragColor = col;\r\n}\r\n"; + stream.write( dStrlen(closer), closer ); +} + +void ShaderGenPrinterGLSL::printLine(Stream& stream, const String& line) +{ + stream.write(line.length(), line.c_str()); + const char* end = "\r\n"; + stream.write(dStrlen(end), end); +} + +const char* ShaderGenComponentFactoryGLSL::typeToString( GFXDeclType type ) +{ + switch ( type ) + { + default: + case GFXDeclType_Float: + return "float"; + + case GFXDeclType_Float2: + return "vec2"; + + case GFXDeclType_Float3: + return "vec3"; + + case GFXDeclType_Float4: + case GFXDeclType_Color: + return "vec4"; + } +} + +ShaderComponent* ShaderGenComponentFactoryGLSL::createVertexInputConnector( const GFXVertexFormat &vertexFormat ) +{ + AppVertConnectorGLSL *vertComp = new AppVertConnectorGLSL; + + // Loop thru the vertex format elements. + for ( U32 i=0; i < vertexFormat.getElementCount(); i++ ) + { + const GFXVertexElement &element = vertexFormat.getElement( i ); + + Var *var = NULL; + + if ( element.isSemantic( GFXSemantic::POSITION ) ) + { + var = vertComp->getElement( RT_POSITION ); + var->setName( "position" ); + } + else if ( element.isSemantic( GFXSemantic::NORMAL ) ) + { + var = vertComp->getElement( RT_NORMAL ); + var->setName( "normal" ); + } + else if ( element.isSemantic( GFXSemantic::TANGENT ) ) + { + var = vertComp->getElement( RT_TANGENT ); + var->setName( "T" ); + } + else if ( element.isSemantic( GFXSemantic::BINORMAL ) ) + { + var = vertComp->getElement( RT_BINORMAL ); + var->setName( "B" ); + } + else if ( element.isSemantic( GFXSemantic::COLOR ) ) + { + var = vertComp->getElement( RT_COLOR ); + var->setName( "diffuse" ); + } + else if ( element.isSemantic( GFXSemantic::TEXCOORD ) ) + { + var = vertComp->getElement( RT_TEXCOORD ); + if ( element.getSemanticIndex() == 0 ) + var->setName( "texCoord" ); + else + var->setName( String::ToString( "texCoord%d", element.getSemanticIndex() + 1 ) ); + } + else + { + // Everything else is a texcoord! + var = vertComp->getElement( RT_TEXCOORD ); + var->setName( "tc" + element.getSemantic() ); + } + + if ( !var ) + continue; + + var->setStructName( "" ); + var->setType( typeToString( element.getType() ) ); + } + + return vertComp; +} + +ShaderComponent* ShaderGenComponentFactoryGLSL::createVertexPixelConnector() +{ + VertPixelConnectorGLSL* comp = new VertPixelConnectorGLSL; + return comp; +} + +ShaderComponent* ShaderGenComponentFactoryGLSL::createVertexParamsDef() +{ + VertexParamsDefGLSL* comp = new VertexParamsDefGLSL; + return comp; +} + +ShaderComponent* ShaderGenComponentFactoryGLSL::createPixelParamsDef() +{ + PixelParamsDefGLSL* comp = new PixelParamsDefGLSL; + return comp; +} + diff --git a/Engine/source/shaderGen/GLSL/shaderGenGLSL.h b/Engine/source/shaderGen/GLSL/shaderGenGLSL.h new file mode 100644 index 000000000..c14402875 --- /dev/null +++ b/Engine/source/shaderGen/GLSL/shaderGenGLSL.h @@ -0,0 +1,59 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SHADERGEN_GLSL_H_ +#define _SHADERGEN_GLSL_H_ + +#ifndef _SHADERGEN_H_ +#include "shaderGen/shaderGen.h" +#endif + + +class ShaderGenPrinterGLSL : public ShaderGenPrinter +{ +public: + + // ShaderGenPrinter + virtual void printShaderHeader(Stream& stream); + virtual void printMainComment(Stream& stream); + virtual void printVertexShaderCloser(Stream& stream); + virtual void printPixelShaderOutputStruct(Stream& stream, const MaterialFeatureData &featureData); + virtual void printPixelShaderCloser(Stream& stream); + virtual void printLine(Stream& stream, const String& line); +}; + +class ShaderGenComponentFactoryGLSL : public ShaderGenComponentFactory +{ +public: + + /// Helper function for converting a vertex decl + /// type to an GLSL type string. + static const char* typeToString( GFXDeclType type ); + + // ShaderGenComponentFactory + virtual ShaderComponent* createVertexInputConnector( const GFXVertexFormat &vertexFormat ); + virtual ShaderComponent* createVertexPixelConnector(); + virtual ShaderComponent* createVertexParamsDef(); + virtual ShaderComponent* createPixelParamsDef(); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/shaderGen/GLSL/shaderGenGLSLInit.cpp b/Engine/source/shaderGen/GLSL/shaderGenGLSLInit.cpp new file mode 100644 index 000000000..1318137bb --- /dev/null +++ b/Engine/source/shaderGen/GLSL/shaderGenGLSLInit.cpp @@ -0,0 +1,105 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "shaderGen/shaderGen.h" +#include "shaderGen/GLSL/shaderGenGLSL.h" +#include "shaderGen/GLSL/shaderFeatureGLSL.h" +#include "shaderGen/featureMgr.h" +#include "shaderGen/GLSL/bumpGLSL.h" +#include "shaderGen/GLSL/pixSpecularGLSL.h" +#include "shaderGen/GLSL/depthGLSL.h" +#include "shaderGen/GLSL/paraboloidGLSL.h" +#include "materials/materialFeatureTypes.h" +#include "core/module.h" + + +static ShaderGen::ShaderGenInitDelegate sInitDelegate; + +void _initShaderGenGLSL( ShaderGen *shaderGen ) +{ + shaderGen->setPrinter( new ShaderGenPrinterGLSL ); + shaderGen->setComponentFactory( new ShaderGenComponentFactoryGLSL ); + shaderGen->setFileEnding( "glsl" ); + + FEATUREMGR->registerFeature( MFT_VertTransform, new VertPositionGLSL ); + FEATUREMGR->registerFeature( MFT_RTLighting, new RTLightingFeatGLSL ); + FEATUREMGR->registerFeature( MFT_IsDXTnm, new NamedFeatureGLSL( "DXTnm" ) ); + FEATUREMGR->registerFeature( MFT_TexAnim, new TexAnimGLSL ); + FEATUREMGR->registerFeature( MFT_DiffuseMap, new DiffuseMapFeatGLSL ); + FEATUREMGR->registerFeature( MFT_OverlayMap, new OverlayTexFeatGLSL ); + FEATUREMGR->registerFeature( MFT_DiffuseColor, new DiffuseFeatureGLSL ); + FEATUREMGR->registerFeature( MFT_DiffuseVertColor, new DiffuseVertColorFeatureGLSL ); + FEATUREMGR->registerFeature( MFT_AlphaTest, new AlphaTestGLSL ); + FEATUREMGR->registerFeature( MFT_GlowMask, new GlowMaskGLSL ); + FEATUREMGR->registerFeature( MFT_LightMap, new LightmapFeatGLSL ); + FEATUREMGR->registerFeature( MFT_ToneMap, new TonemapFeatGLSL ); + FEATUREMGR->registerFeature( MFT_VertLit, new VertLitGLSL ); + FEATUREMGR->registerFeature( MFT_Parallax, new ParallaxFeatGLSL ); + FEATUREMGR->registerFeature( MFT_NormalMap, new BumpFeatGLSL ); + FEATUREMGR->registerFeature( MFT_DetailNormalMap, new NamedFeatureGLSL( "Detail Normal Map" ) ); + FEATUREMGR->registerFeature( MFT_DetailMap, new DetailFeatGLSL ); + FEATUREMGR->registerFeature( MFT_CubeMap, new ReflectCubeFeatGLSL ); + FEATUREMGR->registerFeature( MFT_PixSpecular, new PixelSpecularGLSL ); + FEATUREMGR->registerFeature( MFT_SpecularMap, new SpecularMapGLSL ); + FEATUREMGR->registerFeature( MFT_GlossMap, new NamedFeatureGLSL( "Gloss Map" ) ); + FEATUREMGR->registerFeature( MFT_IsTranslucent, new NamedFeatureGLSL( "Translucent" ) ); + FEATUREMGR->registerFeature( MFT_Visibility, new VisibilityFeatGLSL ); + FEATUREMGR->registerFeature( MFT_Fog, new FogFeatGLSL ); + + FEATUREMGR->registerFeature( MFT_NormalsOut, new NormalsOutFeatGLSL ); + + FEATUREMGR->registerFeature( MFT_DepthOut, new DepthOutGLSL ); + FEATUREMGR->registerFeature(MFT_EyeSpaceDepthOut, new EyeSpaceDepthOutGLSL()); + + FEATUREMGR->registerFeature( MFT_HDROut, new HDROutGLSL ); + + FEATUREMGR->registerFeature( MFT_ParaboloidVertTransform, new ParaboloidVertTransformGLSL ); + FEATUREMGR->registerFeature( MFT_IsSinglePassParaboloid, new NamedFeatureGLSL( "Single Pass Paraboloid" ) ); + FEATUREMGR->registerFeature( MFT_RenderTarget1_Zero, new RenderTargetZeroGLSL + ( ShaderFeature::RenderTarget1 ) ); + + FEATUREMGR->registerFeature( MFT_DiffuseMapAtlas, new NamedFeatureGLSL( "Diffuse Map Atlas" ) ); + FEATUREMGR->registerFeature( MFT_NormalMapAtlas, new NamedFeatureGLSL( "Normal Map Atlas" ) ); + + FEATUREMGR->registerFeature( MFT_Foliage, new FoliageFeatureGLSL ); + + FEATUREMGR->registerFeature( MFT_ParticleNormal, new ParticleNormalFeatureGLSL ); + FEATUREMGR->registerFeature( MFT_ForwardShading, new NamedFeatureGLSL( "Forward Shaded Material" ) ); + + FEATUREMGR->registerFeature( MFT_ImposterVert, new ImposterVertFeatureGLSL ); + +} + +MODULE_BEGIN( ShaderGenGLSL ) + + MODULE_INIT_AFTER( ShaderGen ) + MODULE_INIT_AFTER( ShaderGenFeatureMgr ) + + MODULE_INIT + { + sInitDelegate.bind( &_initShaderGenGLSL ); + SHADERGEN->registerInitDelegate(OpenGL, sInitDelegate); + } + +MODULE_END; diff --git a/Engine/source/shaderGen/HLSL/bumpHLSL.cpp b/Engine/source/shaderGen/HLSL/bumpHLSL.cpp new file mode 100644 index 000000000..2d6dc8464 --- /dev/null +++ b/Engine/source/shaderGen/HLSL/bumpHLSL.cpp @@ -0,0 +1,461 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "shaderGen/HLSL/bumpHLSL.h" + +#include "shaderGen/shaderOp.h" +#include "gfx/gfxDevice.h" +#include "materials/matInstance.h" +#include "materials/processedMaterial.h" +#include "materials/materialFeatureTypes.h" +#include "shaderGen/shaderGenVars.h" + + +void BumpFeatHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + output = meta; + + const bool useTexAnim = fd.features[MFT_TexAnim]; + + // Output the texture coord. + getOutTexCoord( "texCoord", + "float2", + true, + useTexAnim, + meta, + componentList ); + + if ( fd.features.hasFeature( MFT_DetailNormalMap ) ) + addOutDetailTexCoord( componentList, + meta, + useTexAnim ); + + // Also output the worldToTanget transform which + // we use to create the world space normal. + getOutWorldToTangent( componentList, meta, fd ); +} + +void BumpFeatHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + output = meta; + + // Get the texture coord. + Var *texCoord = getInTexCoord( "texCoord", "float2", true, componentList ); + + // Sample the bumpmap. + Var *bumpMap = getNormalMapTex(); + LangElement *texOp = NULL; + + // Handle atlased textures + // http://www.infinity-universe.com/Infinity/index.php?option=com_content&task=view&id=65&Itemid=47 + if(fd.features[MFT_NormalMapAtlas]) + { + // This is a big block of code, so put a comment in the shader code + meta->addStatement( new GenOp( " // Atlased texture coordinate calculation (see BumpFeat*LSL for details)\r\n") ); + + Var *atlasedTex = new Var; + atlasedTex->setName("atlasedBumpCoord"); + atlasedTex->setType( "float2" ); + LangElement *atDecl = new DecOp( atlasedTex ); + + // Parameters of the texture atlas + Var *atParams = new Var; + atParams->setType( "float4" ); + atParams->setName("bumpAtlasParams"); + atParams->uniform = true; + atParams->constSortPos = cspPotentialPrimitive; + + // Parameters of the texture (tile) this object is using in the atlas + Var *tileParams = new Var; + tileParams->setType( "float4" ); + tileParams->setName("bumpAtlasTileParams"); + tileParams->uniform = true; + tileParams->constSortPos = cspPotentialPrimitive; + + const bool is_sm3 = (GFX->getPixelShaderVersion() > 2.0f); + if(is_sm3) + { + // Figure out the mip level + meta->addStatement( new GenOp( " float2 _dx_bump = ddx(@ * @.z);\r\n", texCoord, atParams ) ); + meta->addStatement( new GenOp( " float2 _dy_bump = ddy(@ * @.z);\r\n", texCoord, atParams ) ); + meta->addStatement( new GenOp( " float mipLod_bump = 0.5 * log2(max(dot(_dx_bump, _dx_bump), dot(_dy_bump, _dy_bump)));\r\n" ) ); + meta->addStatement( new GenOp( " mipLod_bump = clamp(mipLod_bump, 0.0, @.w);\r\n", atParams ) ); + + // And the size of the mip level + meta->addStatement( new GenOp( " float mipPixSz_bump = pow(2.0, @.w - mipLod_bump);\r\n", atParams ) ); + meta->addStatement( new GenOp( " float2 mipSz_bump = mipPixSz_bump / @.xy;\r\n", atParams ) ); + } + else + { + meta->addStatement(new GenOp(" float2 mipSz = float2(1.0, 1.0);\r\n")); + } + + // Tiling mode + if( true ) // Wrap + meta->addStatement( new GenOp( " @ = frac(@);\r\n", atDecl, texCoord ) ); + else // Clamp + meta->addStatement( new GenOp( " @ = saturate(@);\r\n", atDecl, texCoord ) ); + + // Finally scale/offset, and correct for filtering + meta->addStatement( new GenOp( " @ = @ * ((mipSz_bump * @.xy - 1.0) / mipSz_bump) + 0.5 / mipSz_bump + @.xy * @.xy;\r\n", + atlasedTex, atlasedTex, atParams, atParams, tileParams ) ); + + // Add a newline + meta->addStatement( new GenOp( "\r\n" ) ); + + if(is_sm3) + { + texOp = new GenOp( "tex2Dlod(@, float4(@, 0.0, mipLod_bump))", bumpMap, texCoord ); + } + else + { + texOp = new GenOp( "tex2D(@, @)", bumpMap, texCoord ); + } + } + else + { + texOp = new GenOp( "tex2D(@, @)", bumpMap, texCoord ); + } + + Var *bumpNorm = new Var( "bumpNormal", "float4" ); + meta->addStatement( expandNormalMap( texOp, new DecOp( bumpNorm ), bumpNorm, fd ) ); + + // If we have a detail normal map we add the xy coords of + // it to the base normal map. This gives us the effect we + // want with few instructions and minial artifacts. + if ( fd.features.hasFeature( MFT_DetailNormalMap ) ) + { + bumpMap = new Var; + bumpMap->setType( "sampler2D" ); + bumpMap->setName( "detailBumpMap" ); + bumpMap->uniform = true; + bumpMap->sampler = true; + bumpMap->constNum = Var::getTexUnitNum(); + + texCoord = getInTexCoord( "detCoord", "float2", true, componentList ); + texOp = new GenOp( "tex2D(@, @)", bumpMap, texCoord ); + + Var *detailBump = new Var; + detailBump->setName( "detailBump" ); + detailBump->setType( "float4" ); + meta->addStatement( expandNormalMap( texOp, new DecOp( detailBump ), detailBump, fd ) ); + + Var *detailBumpScale = new Var; + detailBumpScale->setType( "float" ); + detailBumpScale->setName( "detailBumpStrength" ); + detailBumpScale->uniform = true; + detailBumpScale->constSortPos = cspPass; + meta->addStatement( new GenOp( " @.xy += @.xy * @;\r\n", bumpNorm, detailBump, detailBumpScale ) ); + } + + // We transform it into world space by reversing the + // multiplication by the worldToTanget transform. + Var *wsNormal = new Var( "wsNormal", "float3" ); + Var *worldToTanget = getInWorldToTangent( componentList ); + meta->addStatement( new GenOp( " @ = normalize( mul( @.xyz, @ ) );\r\n", new DecOp( wsNormal ), bumpNorm, worldToTanget ) ); +} + +ShaderFeature::Resources BumpFeatHLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + + // If we have no parallax then we bring on the normal tex. + if ( !fd.features[MFT_Parallax] ) + res.numTex = 1; + + // Only the parallax or diffuse map will add texture + // coords other than us. + if ( !fd.features[MFT_Parallax] && + !fd.features[MFT_DiffuseMap] && + !fd.features[MFT_OverlayMap] && + !fd.features[MFT_DetailMap] ) + res.numTexReg++; + + // We pass the world to tanget space transform. + res.numTexReg += 3; + + // Do we have detail normal mapping? + if ( fd.features[MFT_DetailNormalMap] ) + { + res.numTex++; + if ( !fd.features[MFT_DetailMap] ) + res.numTexReg++; + } + + return res; +} + +void BumpFeatHLSL::setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ) +{ + // If we had a parallax feature then it takes + // care of hooking up the normal map texture. + if ( fd.features[MFT_Parallax] ) + return; + + if ( fd.features[MFT_NormalMap] ) + { + passData.mTexType[ texIndex ] = Material::Bump; + passData.mTexSlot[ texIndex++ ].texObject = stageDat.getTex( MFT_NormalMap ); + } + + if ( fd.features[ MFT_DetailNormalMap ] ) + { + passData.mTexType[ texIndex ] = Material::DetailBump; + passData.mTexSlot[ texIndex++ ].texObject = stageDat.getTex( MFT_DetailNormalMap ); + } +} + + +ParallaxFeatHLSL::ParallaxFeatHLSL() + : mIncludeDep( "shaders/common/torque.hlsl" ) +{ + addDependency( &mIncludeDep ); +} + +Var* ParallaxFeatHLSL::_getUniformVar( const char *name, const char *type, ConstantSortPosition csp ) +{ + Var *theVar = (Var*)LangElement::find( name ); + if ( !theVar ) + { + theVar = new Var; + theVar->setType( type ); + theVar->setName( name ); + theVar->uniform = true; + theVar->constSortPos = csp; + } + + return theVar; +} + +void ParallaxFeatHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + AssertFatal( GFX->getPixelShaderVersion() >= 2.0, + "ParallaxFeatHLSL::processVert - We don't support SM 1.x!" ); + + MultiLine *meta = new MultiLine; + + // Add the texture coords. + getOutTexCoord( "texCoord", + "float2", + true, + fd.features[MFT_TexAnim], + meta, + componentList ); + + // Grab the input position. + Var *inPos = (Var*)LangElement::find( "inPosition" ); + if ( !inPos ) + inPos = (Var*)LangElement::find( "position" ); + + // Get the object space eye position and the + // object to tangent space transform. + Var *eyePos = _getUniformVar( "eyePos", "float3", cspPrimitive ); + Var *objToTangentSpace = getOutObjToTangentSpace( componentList, meta, fd ); + + // Now send the negative view vector in tangent space to the pixel shader. + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outNegViewTS = connectComp->getElement( RT_TEXCOORD ); + outNegViewTS->setName( "outNegViewTS" ); + outNegViewTS->setStructName( "OUT" ); + outNegViewTS->setType( "float3" ); + meta->addStatement( new GenOp( " @ = mul( @, float3( @.xyz - @ ) );\r\n", + outNegViewTS, objToTangentSpace, inPos, eyePos ) ); + + // TODO: I'm at a loss at why i need to flip the binormal/y coord + // to get a good view vector for parallax. Lighting works properly + // with the TS matrix as is... but parallax does not. + // + // Someone figure this out! + // + meta->addStatement( new GenOp( " @.y = -@.y;\r\n", outNegViewTS, outNegViewTS ) ); + + // If we have texture anim matrix the tangent + // space view vector may need to be rotated. + Var *texMat = (Var*)LangElement::find( "texMat" ); + if ( texMat ) + { + meta->addStatement( new GenOp( " @ = mul(@, float4(@,0)).xyz;\r\n", + outNegViewTS, texMat, outNegViewTS ) ); + } + + output = meta; +} + +void ParallaxFeatHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + AssertFatal( GFX->getPixelShaderVersion() >= 2.0, + "ParallaxFeatHLSL::processPix - We don't support SM 1.x!" ); + + MultiLine *meta = new MultiLine; + + // Order matters... get this first! + Var *texCoord = getInTexCoord( "texCoord", "float2", true, componentList ); + + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + // We need the negative tangent space view vector + // as in parallax mapping we step towards the camera. + Var *negViewTS = (Var*)LangElement::find( "negViewTS" ); + if ( !negViewTS ) + { + Var *inNegViewTS = (Var*)LangElement::find( "outNegViewTS" ); + if ( !inNegViewTS ) + { + inNegViewTS = connectComp->getElement( RT_TEXCOORD ); + inNegViewTS->setName( "outNegViewTS" ); + inNegViewTS->setStructName( "IN" ); + inNegViewTS->setType( "float3" ); + } + + negViewTS = new Var( "negViewTS", "float3" ); + meta->addStatement( new GenOp( " @ = normalize( @ );\r\n", new DecOp( negViewTS ), inNegViewTS ) ); + } + + // Get the rest of our inputs. + Var *parallaxInfo = _getUniformVar( "parallaxInfo", "float", cspPotentialPrimitive ); + Var *normalMap = getNormalMapTex(); + + // Call the library function to do the rest. + meta->addStatement( new GenOp( " @.xy += parallaxOffset( @, @.xy, @, @ );\r\n", + texCoord, normalMap, texCoord, negViewTS, parallaxInfo ) ); + + // TODO: Fix second UV maybe? + + output = meta; +} + +ShaderFeature::Resources ParallaxFeatHLSL::getResources( const MaterialFeatureData &fd ) +{ + AssertFatal( GFX->getPixelShaderVersion() >= 2.0, + "ParallaxFeatHLSL::getResources - We don't support SM 1.x!" ); + + Resources res; + + // We add the outViewTS to the outputstructure. + res.numTexReg = 1; + + // If this isn't a prepass then we will be + // creating the normal map here. + if ( !fd.features.hasFeature( MFT_PrePassConditioner ) ) + res.numTex = 1; + + return res; +} + +void ParallaxFeatHLSL::setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ) +{ + AssertFatal( GFX->getPixelShaderVersion() >= 2.0, + "ParallaxFeatHLSL::setTexData - We don't support SM 1.x!" ); + + GFXTextureObject *tex = stageDat.getTex( MFT_NormalMap ); + if ( tex ) + { + passData.mTexType[ texIndex ] = Material::Bump; + passData.mTexSlot[ texIndex++ ].texObject = tex; + } +} + + +void NormalsOutFeatHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // If we have normal maps then we can count + // on it to generate the world space normal. + if ( fd.features[MFT_NormalMap] ) + return; + + MultiLine *meta = new MultiLine; + output = meta; + + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + Var *outNormal = connectComp->getElement( RT_TEXCOORD ); + outNormal->setName( "wsNormal" ); + outNormal->setStructName( "OUT" ); + outNormal->setType( "float3" ); + outNormal->mapsToSampler = false; + + // Find the incoming vertex normal. + Var *inNormal = (Var*)LangElement::find( "normal" ); + if ( inNormal ) + { + // Transform the normal to world space. + Var *objTrans = getObjTrans( componentList, fd.features[MFT_UseInstancing], meta ); + meta->addStatement( new GenOp( " @ = mul( @, normalize( @ ) );\r\n", outNormal, objTrans, inNormal ) ); + } + else + { + // If we don't have a vertex normal... just pass the + // camera facing normal to the pixel shader. + meta->addStatement( new GenOp( " @ = float3( 0.0, 0.0, 1.0 );\r\n", outNormal ) ); + } +} + +void NormalsOutFeatHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + output = meta; + + Var *wsNormal = (Var*)LangElement::find( "wsNormal" ); + if ( !wsNormal ) + { + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + wsNormal = connectComp->getElement( RT_TEXCOORD ); + wsNormal->setName( "wsNormal" ); + wsNormal->setStructName( "IN" ); + wsNormal->setType( "float3" ); + + // If we loaded the normal its our resposibility + // to normalize it... the interpolators won't. + // + // Note we cast to half here to get partial precision + // optimized code which is an acceptable loss of + // precision for normals and performs much better + // on older Geforce cards. + // + meta->addStatement( new GenOp( " @ = normalize( half3( @ ) );\r\n", wsNormal, wsNormal ) ); + } + + LangElement *normalOut; + Var *outColor = (Var*)LangElement::find( "col" ); + if ( outColor && !fd.features[MFT_AlphaTest] ) + normalOut = new GenOp( "float4( ( -@ + 1 ) * 0.5, @.a )", wsNormal, outColor ); + else + normalOut = new GenOp( "float4( ( -@ + 1 ) * 0.5, 1 )", wsNormal ); + + meta->addStatement( new GenOp( " @;\r\n", + assignColor( normalOut, Material::None ) ) ); +} diff --git a/Engine/source/shaderGen/HLSL/bumpHLSL.h b/Engine/source/shaderGen/HLSL/bumpHLSL.h new file mode 100644 index 000000000..79e5af370 --- /dev/null +++ b/Engine/source/shaderGen/HLSL/bumpHLSL.h @@ -0,0 +1,106 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _BUMP_HLSL_H_ +#define _BUMP_HLSL_H_ + +#ifndef _SHADERGEN_HLSL_SHADERFEATUREHLSL_H_ +#include "shaderGen/HLSL/shaderFeatureHLSL.h" +#endif +#ifndef _LANG_ELEMENT_H_ +#include "shaderGen/langElement.h" +#endif + +struct RenderPassData; +class MultiLine; + + +/// The Bumpmap feature will read the normal map and +/// transform it by the inverse of the worldToTanget +/// matrix. This normal is then used by subsequent +/// shader features. +class BumpFeatHLSL : public ShaderFeatureHLSL +{ +public: + + // ShaderFeatureHLSL + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + virtual Material::BlendOp getBlendOp(){ return Material::LerpAlpha; } + virtual Resources getResources( const MaterialFeatureData &fd ); + virtual void setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ); + virtual String getName() { return "Bumpmap"; } +}; + + +/// This feature either generates the cheap yet effective offset +/// mapping style parallax or the much more expensive occlusion +/// mapping technique based on the enabled feature flags. +class ParallaxFeatHLSL : public ShaderFeatureHLSL +{ +protected: + + static Var* _getUniformVar( const char *name, + const char *type, + ConstantSortPosition csp ); + + ShaderIncludeDependency mIncludeDep; + +public: + + ParallaxFeatHLSL(); + + // ShaderFeatureHLSL + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + virtual Resources getResources( const MaterialFeatureData &fd ); + virtual void setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ); + virtual String getName() { return "Parallax"; } +}; + + +/// This feature is used to render normals to the +/// diffuse target for imposter rendering. +class NormalsOutFeatHLSL : public ShaderFeatureHLSL +{ +public: + + // ShaderFeatureHLSL + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + virtual Material::BlendOp getBlendOp(){ return Material::LerpAlpha; } + virtual String getName() { return "NormalsOut"; } +}; + +#endif // _BUMP_HLSL_H_ \ No newline at end of file diff --git a/Engine/source/shaderGen/HLSL/depthHLSL.cpp b/Engine/source/shaderGen/HLSL/depthHLSL.cpp new file mode 100644 index 000000000..9f5726a51 --- /dev/null +++ b/Engine/source/shaderGen/HLSL/depthHLSL.cpp @@ -0,0 +1,173 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "shaderGen/HLSL/depthHLSL.h" + +#include "materials/materialFeatureTypes.h" +#include "materials/materialFeatureData.h" + + +void EyeSpaceDepthOutHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + output = meta; + + // grab output + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outWSEyeVec = connectComp->getElement( RT_TEXCOORD ); + outWSEyeVec->setName( "wsEyeVec" ); + outWSEyeVec->setStructName( "OUT" ); + + // grab incoming vert position + Var *wsPosition = new Var( "depthPos", "float3" ); + getWsPosition( componentList, fd.features[MFT_UseInstancing], meta, new DecOp( wsPosition ) ); + + Var *eyePos = (Var*)LangElement::find( "eyePosWorld" ); + if( !eyePos ) + { + eyePos = new Var; + eyePos->setType("float3"); + eyePos->setName("eyePosWorld"); + eyePos->uniform = true; + eyePos->constSortPos = cspPass; + } + + meta->addStatement( new GenOp( " @ = float4( @.xyz - @, 1 );\r\n", outWSEyeVec, wsPosition, eyePos ) ); +} + +void EyeSpaceDepthOutHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + + // grab connector position + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *wsEyeVec = connectComp->getElement( RT_TEXCOORD ); + wsEyeVec->setName( "wsEyeVec" ); + wsEyeVec->setStructName( "IN" ); + wsEyeVec->setType( "float4" ); + wsEyeVec->mapsToSampler = false; + wsEyeVec->uniform = false; + + // get shader constants + Var *vEye = new Var; + vEye->setType("float3"); + vEye->setName("vEye"); + vEye->uniform = true; + vEye->constSortPos = cspPass; + + // Expose the depth to the depth format feature + Var *depthOut = new Var; + depthOut->setType("float"); + depthOut->setName(getOutputVarName()); + + LangElement *depthOutDecl = new DecOp( depthOut ); + + meta->addStatement( new GenOp( "#ifndef CUBE_SHADOW_MAP\r\n" ) ); + meta->addStatement( new GenOp( " @ = dot(@, (@.xyz / @.w));\r\n", depthOutDecl, vEye, wsEyeVec, wsEyeVec ) ); + meta->addStatement( new GenOp( "#else\r\n" ) ); + + Var *farDist = (Var*)Var::find( "oneOverFarplane" ); + if ( !farDist ) + { + farDist = new Var; + farDist->setType("float4"); + farDist->setName("oneOverFarplane"); + farDist->uniform = true; + farDist->constSortPos = cspPass; + } + + meta->addStatement( new GenOp( " @ = length( @.xyz / @.w ) * @.x;\r\n", depthOutDecl, wsEyeVec, wsEyeVec, farDist ) ); + meta->addStatement( new GenOp( "#endif\r\n" ) ); + + // If there isn't an output conditioner for the pre-pass, than just write + // out the depth to rgba and return. + if( !fd.features[MFT_PrePassConditioner] ) + meta->addStatement( new GenOp( " @;\r\n", assignColor( new GenOp( "float4(@.rrr,1)", depthOut ), Material::None ) ) ); + + output = meta; +} + +ShaderFeature::Resources EyeSpaceDepthOutHLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources temp; + + // Passing from VS->PS: + // - world space position (wsPos) + temp.numTexReg = 1; + + return temp; +} + + +void DepthOutHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + // Grab the output vert. + Var *outPosition = (Var*)LangElement::find( "hpos" ); + + // Grab our output depth. + Var *outDepth = connectComp->getElement( RT_TEXCOORD ); + outDepth->setName( "depth" ); + outDepth->setStructName( "OUT" ); + outDepth->setType( "float" ); + + output = new GenOp( " @ = @.z / @.w;\r\n", outDepth, outPosition, outPosition ); +} + +void DepthOutHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + // grab connector position + Var *depthVar = connectComp->getElement( RT_TEXCOORD ); + depthVar->setName( "depth" ); + depthVar->setStructName( "IN" ); + depthVar->setType( "float" ); + depthVar->mapsToSampler = false; + depthVar->uniform = false; + + /* + // Expose the depth to the depth format feature + Var *depthOut = new Var; + depthOut->setType("float"); + depthOut->setName(getOutputVarName()); + */ + + LangElement *depthOut = new GenOp( "float4( @, 0, 0, 1 )", depthVar ); + + output = new GenOp( " @;\r\n", assignColor( depthOut, Material::None ) ); +} + +ShaderFeature::Resources DepthOutHLSL::getResources( const MaterialFeatureData &fd ) +{ + // We pass the depth to the pixel shader. + Resources temp; + temp.numTexReg = 1; + + return temp; +} diff --git a/Engine/source/shaderGen/HLSL/depthHLSL.h b/Engine/source/shaderGen/HLSL/depthHLSL.h new file mode 100644 index 000000000..c174ee1c8 --- /dev/null +++ b/Engine/source/shaderGen/HLSL/depthHLSL.h @@ -0,0 +1,60 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _DEPTH_HLSL_H_ +#define _DEPTH_HLSL_H_ + +#ifndef _SHADERGEN_HLSL_SHADERFEATUREHLSL_H_ +#include "shaderGen/HLSL/shaderFeatureHLSL.h" +#endif +#ifndef _SHADEROP_H_ +#include "shaderGen/shaderOp.h" +#endif + + +class EyeSpaceDepthOutHLSL : public ShaderFeatureHLSL +{ +public: + + // ShaderFeature + virtual void processVert( Vector &componentList, const MaterialFeatureData &fd ); + virtual void processPix( Vector &componentList, const MaterialFeatureData &fd ); + virtual Resources getResources( const MaterialFeatureData &fd ); + virtual String getName() { return "Eye Space Depth (Out)"; } + virtual Material::BlendOp getBlendOp() { return Material::None; } + virtual const char* getOutputVarName() const { return "eyeSpaceDepth"; } +}; + + +class DepthOutHLSL : public ShaderFeatureHLSL +{ +public: + + // ShaderFeature + virtual void processVert( Vector &componentList, const MaterialFeatureData &fd ); + virtual void processPix( Vector &componentList, const MaterialFeatureData &fd ); + virtual Resources getResources( const MaterialFeatureData &fd ); + virtual String getName() { return "Depth (Out)"; } + virtual Material::BlendOp getBlendOp() { return Material::None; } + virtual const char* getOutputVarName() const { return "IN.depth"; } +}; + +#endif // _DEPTH_HLSL_H_ \ No newline at end of file diff --git a/Engine/source/shaderGen/HLSL/paraboloidHLSL.cpp b/Engine/source/shaderGen/HLSL/paraboloidHLSL.cpp new file mode 100644 index 000000000..a5afa00e0 --- /dev/null +++ b/Engine/source/shaderGen/HLSL/paraboloidHLSL.cpp @@ -0,0 +1,163 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "shaderGen/HLSL/paraboloidHLSL.h" + +#include "lighting/lightInfo.h" +#include "materials/sceneData.h" +#include "materials/materialFeatureTypes.h" +#include "materials/materialFeatureData.h" +#include "gfx/gfxShader.h" + + +void ParaboloidVertTransformHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + + // First check for an input position from a previous feature + // then look for the default vertex position. + Var *inPosition = (Var*)LangElement::find( "inPosition" ); + if ( !inPosition ) + inPosition = (Var*)LangElement::find( "position" ); + + const bool isSinglePass = fd.features[ MFT_IsSinglePassParaboloid ]; + + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + // Grab connector out position. + Var *outPosition = connectComp->getElement( RT_POSITION ); + outPosition->setName( "hpos" ); + outPosition->setStructName( "OUT" ); + + // Get the atlas scale. + Var *atlasScale = new Var; + atlasScale->setType( "float2" ); + atlasScale->setName( "atlasScale" ); + atlasScale->uniform = true; + atlasScale->constSortPos = cspPass; + + // Transform into camera space + Var *worldViewOnly = getWorldView( componentList, fd.features[MFT_UseInstancing], meta ); + + // So what we're doing here is transforming into camera space, and + // then directly manipulate into shadowmap space. + // + // http://www.gamedev.net/reference/articles/article2308.asp + + // Swizzle z and y post-transform + meta->addStatement( new GenOp( " @ = mul(@, float4(@.xyz,1)).xzyw;\r\n", outPosition, worldViewOnly, inPosition ) ); + meta->addStatement( new GenOp( " float L = length(@.xyz);\r\n", outPosition ) ); + + if ( isSinglePass ) + { + // Flip the z in the back case + Var *outIsBack = connectComp->getElement( RT_TEXCOORD ); + outIsBack->setType( "float" ); + outIsBack->setName( "isBack" ); + outIsBack->setStructName( "OUT" ); + + meta->addStatement( new GenOp( " bool isBack = @.z < 0.0;\r\n", outPosition ) ); + meta->addStatement( new GenOp( " @ = isBack ? -1.0 : 1.0;\r\n", outIsBack ) ); + meta->addStatement( new GenOp( " if ( isBack ) @.z = -@.z;\r\n", outPosition, outPosition ) ); + } + + meta->addStatement( new GenOp( " @ /= L;\r\n", outPosition ) ); + meta->addStatement( new GenOp( " @.z = @.z + 1.0;\r\n", outPosition, outPosition ) ); + meta->addStatement( new GenOp( " @.xy /= @.z;\r\n", outPosition, outPosition ) ); + + // Get the light parameters. + Var *lightParams = new Var; + lightParams->setType( "float4" ); + lightParams->setName( "lightParams" ); + lightParams->uniform = true; + lightParams->constSortPos = cspPass; + + // TODO: If we change other shadow shaders to write out + // linear depth, than fix this as well! + // + // (L - zNear)/(lightParams.x - zNear); + // + meta->addStatement( new GenOp( " @.z = L / @.x;\r\n", outPosition, lightParams ) ); + meta->addStatement( new GenOp( " @.w = 1.0;\r\n", outPosition ) ); + + // Pass unmodified to pixel shader to allow it to clip properly. + Var *outPosXY = connectComp->getElement( RT_TEXCOORD ); + outPosXY->setType( "float2" ); + outPosXY->setName( "posXY" ); + outPosXY->setStructName( "OUT" ); + meta->addStatement( new GenOp( " @ = @.xy;\r\n", outPosXY, outPosition ) ); + + // Scale and offset so it shows up in the atlas properly. + meta->addStatement( new GenOp( " @.xy *= @.xy;\r\n", outPosition, atlasScale ) ); + + if ( isSinglePass ) + meta->addStatement( new GenOp( " @.x += isBack ? 0.5 : -0.5;\r\n", outPosition ) ); + else + { + Var *atlasOffset = new Var; + atlasOffset->setType( "float2" ); + atlasOffset->setName( "atlasXOffset" ); + atlasOffset->uniform = true; + atlasOffset->constSortPos = cspPass; + + meta->addStatement( new GenOp( " @.xy += @;\r\n", outPosition, atlasOffset ) ); + } + + output = meta; +} + +void ParaboloidVertTransformHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + MultiLine *meta = new MultiLine; + + const bool isSinglePass = fd.features[ MFT_IsSinglePassParaboloid ]; + if ( isSinglePass ) + { + // Cull things on the back side of the map. + Var *isBack = connectComp->getElement( RT_TEXCOORD ); + isBack->setName( "isBack" ); + isBack->setStructName( "IN" ); + isBack->setType( "float" ); + meta->addStatement( new GenOp( " clip( abs( @ ) - 0.999 );\r\n", isBack ) ); + } + + // Cull pixels outside of the valid paraboloid. + Var *posXY = connectComp->getElement( RT_TEXCOORD ); + posXY->setName( "posXY" ); + posXY->setStructName( "IN" ); + posXY->setType( "float2" ); + meta->addStatement( new GenOp( " clip( 1.0 - abs(@.x) );\r\n", posXY ) ); + + output = meta; +} + +ShaderFeature::Resources ParaboloidVertTransformHLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources temp; + temp.numTexReg = 2; + return temp; +} diff --git a/Engine/source/shaderGen/HLSL/paraboloidHLSL.h b/Engine/source/shaderGen/HLSL/paraboloidHLSL.h new file mode 100644 index 000000000..43ee84239 --- /dev/null +++ b/Engine/source/shaderGen/HLSL/paraboloidHLSL.h @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _PARABOLOID_HLSL_H_ +#define _PARABOLOID_HLSL_H_ + +#ifndef _SHADERGEN_HLSL_SHADERFEATUREHLSL_H_ +#include "shaderGen/HLSL/shaderFeatureHLSL.h" +#endif +#ifndef _SHADEROP_H_ +#include "shaderGen/shaderOp.h" +#endif + +class GFXShaderConstHandle; + + +class ParaboloidVertTransformHLSL : public ShaderFeatureHLSL +{ +public: + + // ShaderFeature + virtual void processVert( Vector &componentList, const MaterialFeatureData &fd ); + virtual void processPix( Vector &componentList, const MaterialFeatureData &fd ); + virtual Resources getResources( const MaterialFeatureData &fd ); + virtual String getName() { return "Paraboloid Vert Transform"; } + virtual Material::BlendOp getBlendOp() { return Material::None; } + +}; + +#endif // _PARABOLOID_HLSL_H_ \ No newline at end of file diff --git a/Engine/source/shaderGen/HLSL/pixSpecularHLSL.cpp b/Engine/source/shaderGen/HLSL/pixSpecularHLSL.cpp new file mode 100644 index 000000000..930e702c8 --- /dev/null +++ b/Engine/source/shaderGen/HLSL/pixSpecularHLSL.cpp @@ -0,0 +1,152 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "shaderGen/HLSL/pixSpecularHLSL.h" +#include "materials/processedMaterial.h" +#include "materials/materialFeatureTypes.h" +#include "shaderGen/shaderOp.h" +#include "shaderGen/shaderGenVars.h" +#include "gfx/gfxStructs.h" + + +PixelSpecularHLSL::PixelSpecularHLSL() + : mDep( "shaders/common/lighting.hlsl" ) +{ + addDependency( &mDep ); +} + +void PixelSpecularHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + AssertFatal( fd.features[MFT_RTLighting], + "PixelSpecularHLSL requires RTLighting to be enabled!" ); + + // Nothing to do here... MFT_RTLighting should have + // taken care of passing everything to the pixel shader. +} + +void PixelSpecularHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + AssertFatal( fd.features[MFT_RTLighting], + "PixelSpecularHLSL requires RTLighting to be enabled!" ); + + // RTLighting should have spit out the 4 specular + // powers for the 4 potential lights on this pass. + // + // This can sometimes be NULL if RTLighting skips out + // on us for lightmaps or missing normals. + Var *specular = (Var*)LangElement::find( "specular" ); + if ( !specular ) + return; + + MultiLine *meta = new MultiLine; + + LangElement *specMul = new GenOp( "@", specular ); + LangElement *final = specMul; + + // mask out with lightmap if present + if ( fd.features[MFT_LightMap] ) + { + LangElement *lmColor = NULL; + + // find lightmap color + lmColor = LangElement::find( "lmColor" ); + + if ( !lmColor ) + { + LangElement * lightMap = LangElement::find( "lightMap" ); + LangElement * lmCoord = LangElement::find( "texCoord2" ); + + lmColor = new GenOp( "tex2D(@, @)", lightMap, lmCoord ); + } + + final = new GenOp( "@ * float4(@.rgb,0)", specMul, lmColor ); + } + + // If we have a normal map then mask the specular + if ( fd.features[MFT_SpecularMap] ) + { + Var *specularColor = (Var*)LangElement::find( "specularColor" ); + if (specularColor) + final = new GenOp( "@ * @", final, specularColor ); + } + else if ( fd.features[MFT_NormalMap] && !fd.features[MFT_IsDXTnm] ) + { + Var *bumpColor = (Var*)LangElement::find( "bumpNormal" ); + final = new GenOp( "@ * @.a", final, bumpColor ); + } + + // Add the specular to the final color. + // search for color var + Var *color = (Var*)LangElement::find( "col" ); + meta->addStatement( new GenOp( " @.rgb += ( @ ).rgb;\r\n", color, final ) ); + + output = meta; +} + +ShaderFeature::Resources PixelSpecularHLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + return res; +} + + +void SpecularMapHLSL::processPix( Vector &componentList, const MaterialFeatureData &fd ) +{ + // Get the texture coord. + Var *texCoord = getInTexCoord( "texCoord", "float2", true, componentList ); + + // create texture var + Var *specularMap = new Var; + specularMap->setType( "sampler2D" ); + specularMap->setName( "specularMap" ); + specularMap->uniform = true; + specularMap->sampler = true; + specularMap->constNum = Var::getTexUnitNum(); + LangElement *texOp = new GenOp( "tex2D(@, @)", specularMap, texCoord ); + + Var *specularColor = new Var( "specularColor", "float4" ); + + output = new GenOp( " @ = @;\r\n", new DecOp( specularColor ), texOp ); +} + +ShaderFeature::Resources SpecularMapHLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + res.numTex = 1; + return res; +} + +void SpecularMapHLSL::setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ) +{ + GFXTextureObject *tex = stageDat.getTex( MFT_SpecularMap ); + if ( tex ) + { + passData.mTexType[ texIndex ] = Material::Standard; + passData.mTexSlot[ texIndex++ ].texObject = tex; + } +} \ No newline at end of file diff --git a/Engine/source/shaderGen/HLSL/pixSpecularHLSL.h b/Engine/source/shaderGen/HLSL/pixSpecularHLSL.h new file mode 100644 index 000000000..0afea87ed --- /dev/null +++ b/Engine/source/shaderGen/HLSL/pixSpecularHLSL.h @@ -0,0 +1,77 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PIXSPECULAR_HLSL_H_ +#define _PIXSPECULAR_HLSL_H_ + +#ifndef _SHADERGEN_HLSL_SHADERFEATUREHLSL_H_ +#include "shaderGen/HLSL/shaderFeatureHLSL.h" +#endif + + +/// A per-pixel specular feature. +class PixelSpecularHLSL : public ShaderFeatureHLSL +{ +protected: + + ShaderIncludeDependency mDep; + +public: + + PixelSpecularHLSL(); + + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual String getName() + { + return "Pixel Specular"; + } +}; + +/// A texture source for the PixSpecular feature +class SpecularMapHLSL : public ShaderFeatureHLSL +{ + +public: + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual void setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ); + + virtual String getName() + { + return "Specular Map"; + } +}; + +#endif // _PIXSPECULAR_HLSL_H_ \ No newline at end of file diff --git a/Engine/source/shaderGen/HLSL/shaderCompHLSL.cpp b/Engine/source/shaderGen/HLSL/shaderCompHLSL.cpp new file mode 100644 index 000000000..7830e76a0 --- /dev/null +++ b/Engine/source/shaderGen/HLSL/shaderCompHLSL.cpp @@ -0,0 +1,349 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "shaderGen/HLSL/shaderCompHLSL.h" + +#include "shaderGen/shaderComp.h" +#include "shaderGen/langElement.h" +#include "gfx/gfxDevice.h" + + +Var * ShaderConnectorHLSL::getElement( RegisterType type, + U32 numElements, + U32 numRegisters ) +{ + Var *ret = getIndexedElement( mCurTexElem, type, numElements, numRegisters ); + + // Adjust texture offset if this is a texcoord type + if( type == RT_TEXCOORD ) + { + if ( numRegisters != -1 ) + mCurTexElem += numRegisters; + else + mCurTexElem += numElements; + } + + return ret; +} + +Var * ShaderConnectorHLSL::getIndexedElement( U32 index, RegisterType type, U32 numElements /*= 1*/, U32 numRegisters /*= -1 */ ) +{ + switch( type ) + { + case RT_POSITION: + { + Var *newVar = new Var; + mElementList.push_back( newVar ); + newVar->setConnectName( "POSITION" ); + return newVar; + } + + case RT_NORMAL: + { + Var *newVar = new Var; + mElementList.push_back( newVar ); + newVar->setConnectName( "NORMAL" ); + return newVar; + } + + case RT_BINORMAL: + { + Var *newVar = new Var; + mElementList.push_back( newVar ); + newVar->setConnectName( "BINORMAL" ); + return newVar; + } + + case RT_TANGENT: + { + Var *newVar = new Var; + mElementList.push_back( newVar ); + newVar->setConnectName( "TANGENT" ); + return newVar; + } + + case RT_COLOR: + { + Var *newVar = new Var; + mElementList.push_back( newVar ); + newVar->setConnectName( "COLOR" ); + return newVar; + } + + case RT_VPOS: + { + Var *newVar = new Var; + mElementList.push_back( newVar ); + newVar->setConnectName( "VPOS" ); + return newVar; + } + + case RT_TEXCOORD: + { + Var *newVar = new Var; + mElementList.push_back( newVar ); + + // This was needed for hardware instancing, but + // i don't really remember why right now. + if ( index > mCurTexElem ) + mCurTexElem = index + 1; + + char out[32]; + dSprintf( (char*)out, sizeof(out), "TEXCOORD%d", index ); + newVar->setConnectName( out ); + newVar->constNum = index; + newVar->arraySize = numElements; + + return newVar; + } + + default: + break; + } + + return NULL; +} + +void ShaderConnectorHLSL::sortVars() +{ + if ( GFX->getPixelShaderVersion() >= 2.0 ) + return; + + // Sort connector variables - They must be sorted on hardware that is running + // ps 1.4 and below. The reason is that texture coordinate registers MUST + // map exactly to their respective texture stage. Ie. if you have fog + // coordinates being passed into a pixel shader in texture coordinate register + // number 4, the fog texture MUST reside in texture stage 4 for it to work. + // The problem is solved by pushing non-texture coordinate data to the end + // of the structure so that the texture coodinates are all at the "top" of the + // structure in the order that the features are processed. + + // create list of just the texCoords, sorting by 'mapsToSampler' + Vector< Var * > texCoordList; + + // - first pass is just coords mapped to a sampler + for( U32 i=0; imapsToSampler ) + { + texCoordList.push_back( var ); + } + } + + // - next pass is for the others + for( U32 i=0; iconnectName, "TEX" ) && + !var->mapsToSampler ) + { + texCoordList.push_back( var ); + } + } + + // rename the connectNames + for( U32 i=0; isetConnectName( out ); + } + + // write new, sorted list over old one + if( texCoordList.size() ) + { + U32 index = 0; + + for( U32 i=0; iconnectName, "TEX" ) ) + { + mElementList[i] = texCoordList[index]; + index++; + } + } + } +} + +void ShaderConnectorHLSL::setName( char *newName ) +{ + dStrcpy( (char*)mName, newName ); +} + +void ShaderConnectorHLSL::reset() +{ + for( U32 i=0; iarraySize <= 1) + dSprintf( (char*)output, sizeof(output), " %s %-15s : %s;\r\n", var->type, var->name, var->connectName ); + else + dSprintf( (char*)output, sizeof(output), " %s %s[%d] : %s;\r\n", var->type, var->name, var->arraySize, var->connectName ); + + stream.write( dStrlen((char*)output), output ); + } + + stream.write( dStrlen(footer), footer ); +} + +void ParamsDefHLSL::assignConstantNumbers() +{ + + // Here we assign constant number to uniform vars, sorted + // by their update frequency. + + U32 mCurrConst = 0; + for (U32 bin = cspUninit+1; bin < csp_Count; bin++) + { + // Find all the uniform variables that are part of this group and assign constant numbers + for( U32 i=0; i(LangElement::elementList[i]); + if( var ) + { + bool shaderConst = var->uniform && !var->sampler; + AssertFatal((!shaderConst) || var->constSortPos != cspUninit, "Const sort position has not been set, variable will not receive a constant number!!"); + if( shaderConst && var->constSortPos == bin) + { + var->constNum = mCurrConst; + // Increment our constant number based on the variable type + if (dStrcmp((const char*)var->type, "float4x4") == 0) + { + mCurrConst += (4 * var->arraySize); + } else { + if (dStrcmp((const char*)var->type, "float3x3") == 0) + { + mCurrConst += (3 * var->arraySize); + } else { + mCurrConst += var->arraySize; + } + } + } + } + } + } +} + +void VertexParamsDefHLSL::print( Stream &stream ) +{ + assignConstantNumbers(); + + const char *opener = "ConnectData main( VertData IN"; + stream.write( dStrlen(opener), opener ); + + // find all the uniform variables and print them out + for( U32 i=0; i(LangElement::elementList[i]); + if( var ) + { + if( var->uniform ) + { + const char* nextVar = ",\r\n "; + stream.write( dStrlen(nextVar), nextVar ); + + U8 varNum[64]; + dSprintf( (char*)varNum, sizeof(varNum), "register(C%d)", var->constNum ); + + U8 output[256]; + if (var->arraySize <= 1) + dSprintf( (char*)output, sizeof(output), "uniform %-8s %-15s : %s", var->type, var->name, varNum ); + else + dSprintf( (char*)output, sizeof(output), "uniform %-8s %s[%d] : %s", var->type, var->name, var->arraySize, varNum ); + + stream.write( dStrlen((char*)output), output ); + } + } + } + + const char *closer = "\r\n)\r\n{\r\n ConnectData OUT;\r\n\r\n"; + stream.write( dStrlen(closer), closer ); +} + +void PixelParamsDefHLSL::print( Stream &stream ) +{ + assignConstantNumbers(); + + const char * opener = "Fragout main( ConnectData IN"; + stream.write( dStrlen(opener), opener ); + + // find all the sampler & uniform variables and print them out + for( U32 i=0; i(LangElement::elementList[i]); + if( var ) + { + if( var->uniform ) + { + WRITESTR( ",\r\n " ); + + U8 varNum[32]; + + if( var->sampler ) + { + dSprintf( (char*)varNum, sizeof(varNum), "register(S%d)", var->constNum ); + } + else + { + dSprintf( (char*)varNum, sizeof(varNum), "register(C%d)", var->constNum ); + } + + U8 output[256]; + if (var->arraySize <= 1) + dSprintf( (char*)output, sizeof(output), "uniform %-9s %-15s : %s", var->type, var->name, varNum ); + else + dSprintf( (char*)output, sizeof(output), "uniform %-9s %s[%d] : %s", var->type, var->name, var->arraySize, varNum ); + + WRITESTR( (char*) output ); + } + } + } + + const char *closer = "\r\n)\r\n{\r\n Fragout OUT;\r\n\r\n"; + stream.write( dStrlen(closer), closer ); +} diff --git a/Engine/source/shaderGen/HLSL/shaderCompHLSL.h b/Engine/source/shaderGen/HLSL/shaderCompHLSL.h new file mode 100644 index 000000000..e24ed8992 --- /dev/null +++ b/Engine/source/shaderGen/HLSL/shaderCompHLSL.h @@ -0,0 +1,72 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SHADERCOMP_HLSL_H_ +#define _SHADERCOMP_HLSL_H_ + +#ifndef _SHADERCOMP_H_ +#include "shaderGen/shaderComp.h" +#endif + + +class ShaderConnectorHLSL : public ShaderConnector +{ +public: + + // ShaderConnector + virtual Var* getElement( RegisterType type, + U32 numElements = 1, + U32 numRegisters = -1 ); + virtual Var* getIndexedElement( U32 index, + RegisterType type, + U32 numElements = 1, + U32 numRegisters = -1 ); + + virtual void setName( char *newName ); + virtual void reset(); + virtual void sortVars(); + + virtual void print( Stream &stream ); +}; + + +class ParamsDefHLSL : public ParamsDef +{ +protected: + virtual void assignConstantNumbers(); +}; + + +class VertexParamsDefHLSL : public ParamsDefHLSL +{ +public: + virtual void print( Stream &stream ); +}; + + +class PixelParamsDefHLSL : public ParamsDefHLSL +{ +public: + virtual void print( Stream &stream ); +}; + +#endif // _SHADERCOMP_HLSL_H_ \ No newline at end of file diff --git a/Engine/source/shaderGen/HLSL/shaderFeatureHLSL.cpp b/Engine/source/shaderGen/HLSL/shaderFeatureHLSL.cpp new file mode 100644 index 000000000..6da26c455 --- /dev/null +++ b/Engine/source/shaderGen/HLSL/shaderFeatureHLSL.cpp @@ -0,0 +1,2696 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "shaderGen/HLSL/shaderFeatureHLSL.h" + +#include "shaderGen/langElement.h" +#include "shaderGen/shaderOp.h" +#include "shaderGen/shaderGenVars.h" +#include "gfx/gfxDevice.h" +#include "materials/matInstance.h" +#include "materials/processedMaterial.h" +#include "materials/materialFeatureTypes.h" +#include "core/util/autoPtr.h" + +#include "lighting/advanced/advancedLightBinManager.h" + +LangElement * ShaderFeatureHLSL::setupTexSpaceMat( Vector &, // componentList + Var **texSpaceMat ) +{ + Var *N = (Var*) LangElement::find( "normal" ); + Var *B = (Var*) LangElement::find( "B" ); + Var *T = (Var*) LangElement::find( "T" ); + + Var *tangentW = (Var*) LangElement::find( "tangentW" ); + + // setup matrix var + *texSpaceMat = new Var; + (*texSpaceMat)->setType( "float3x3" ); + (*texSpaceMat)->setName( "objToTangentSpace" ); + + MultiLine *meta = new MultiLine; + meta->addStatement( new GenOp( " @;\r\n", new DecOp( *texSpaceMat ) ) ); + + // Protect against missing normal and tangent. + if ( !N || !T ) + { + meta->addStatement( new GenOp( " @[0] = float3( 1, 0, 0 ); @[1] = float3( 0, 1, 0 ); @[2] = float3( 0, 0, 1 );\r\n", + *texSpaceMat, *texSpaceMat, *texSpaceMat ) ); + return meta; + } + + meta->addStatement( new GenOp( " @[0] = @;\r\n", *texSpaceMat, T ) ); + if ( B ) + meta->addStatement( new GenOp( " @[1] = @;\r\n", *texSpaceMat, B ) ); + else + { + if(dStricmp((char*)T->type, "float4") == 0) + meta->addStatement( new GenOp( " @[1] = cross( @, normalize(@) ) * @.w;\r\n", *texSpaceMat, T, N, T ) ); + else if(tangentW) + meta->addStatement( new GenOp( " @[1] = cross( @, normalize(@) ) * @;\r\n", *texSpaceMat, T, N, tangentW ) ); + else + meta->addStatement( new GenOp( " @[1] = cross( @, normalize(@) );\r\n", *texSpaceMat, T, N ) ); + } + meta->addStatement( new GenOp( " @[2] = normalize(@);\r\n", *texSpaceMat, N ) ); + + return meta; +} + +LangElement* ShaderFeatureHLSL::assignColor( LangElement *elem, + Material::BlendOp blend, + LangElement *lerpElem, + ShaderFeature::OutputTarget outputTarget ) +{ + + // search for color var + Var *color = (Var*) LangElement::find( getOutputTargetVarName(outputTarget) ); + + if ( !color ) + { + // create color var + color = new Var; + color->setType( "fragout" ); + color->setName( getOutputTargetVarName(outputTarget) ); + color->setStructName( "OUT" ); + + return new GenOp( "@ = @", color, elem ); + } + + LangElement *assign; + + switch ( blend ) + { + case Material::Add: + assign = new GenOp( "@ += @", color, elem ); + break; + + case Material::Sub: + assign = new GenOp( "@ -= @", color, elem ); + break; + + case Material::Mul: + assign = new GenOp( "@ *= @", color, elem ); + break; + + case Material::AddAlpha: + assign = new GenOp( "@ += @ * @.a", color, elem, elem ); + break; + + case Material::LerpAlpha: + if ( !lerpElem ) + lerpElem = elem; + assign = new GenOp( "@.rgb = lerp( @.rgb, (@).rgb, (@).a )", color, color, elem, lerpElem ); + break; + + case Material::ToneMap: + assign = new GenOp( "@ = 1.0 - exp(-1.0 * @ * @)", color, color, elem ); + break; + + default: + AssertFatal(false, "Unrecognized color blendOp"); + // Fallthru + + case Material::None: + assign = new GenOp( "@ = @", color, elem ); + break; + } + + return assign; +} + + +LangElement *ShaderFeatureHLSL::expandNormalMap( LangElement *sampleNormalOp, + LangElement *normalDecl, + LangElement *normalVar, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + + if ( fd.features.hasFeature( MFT_IsDXTnm, getProcessIndex() ) ) + { + if ( fd.features[MFT_ImposterVert] ) + { + // The imposter system uses object space normals and + // encodes them with the z axis in the alpha component. + meta->addStatement( new GenOp( " @ = float4( normalize( @.xyw * 2.0 - 1.0 ), 0.0 ); // Obj DXTnm\r\n", normalDecl, sampleNormalOp ) ); + } + else + { + // DXT Swizzle trick + meta->addStatement( new GenOp( " @ = float4( @.ag * 2.0 - 1.0, 0.0, 0.0 ); // DXTnm\r\n", normalDecl, sampleNormalOp ) ); + meta->addStatement( new GenOp( " @.z = sqrt( 1.0 - dot( @.xy, @.xy ) ); // DXTnm\r\n", normalVar, normalVar, normalVar ) ); + } + } + else + { + meta->addStatement( new GenOp( " @ = @;\r\n", normalDecl, sampleNormalOp ) ); + meta->addStatement( new GenOp( " @.xyz = @.xyz * 2.0 - 1.0;\r\n", normalVar, normalVar ) ); + } + + return meta; +} + +ShaderFeatureHLSL::ShaderFeatureHLSL() +{ + output = NULL; +} + +Var * ShaderFeatureHLSL::getVertTexCoord( const String &name ) +{ + Var *inTex = NULL; + + for( U32 i=0; iname, name.c_str() ) ) + { + inTex = dynamic_cast( LangElement::elementList[i] ); + if ( inTex ) + { + // NOTE: This used to do this check... + // + // dStrcmp( (char*)inTex->structName, "IN" ) + // + // ... to ensure that the var was from the input + // vertex structure, but this kept some features + // ( ie. imposter vert ) from decoding their own + // coords for other features to use. + // + // If we run into issues with collisions between + // IN vars and local vars we may need to revise. + + break; + } + } + } + + return inTex; +} + +Var* ShaderFeatureHLSL::getOutObjToTangentSpace( Vector &componentList, + MultiLine *meta, + const MaterialFeatureData &fd ) +{ + Var *outObjToTangentSpace = (Var*)LangElement::find( "objToTangentSpace" ); + if ( !outObjToTangentSpace ) + meta->addStatement( setupTexSpaceMat( componentList, &outObjToTangentSpace ) ); + + return outObjToTangentSpace; +} + +Var* ShaderFeatureHLSL::getOutWorldToTangent( Vector &componentList, + MultiLine *meta, + const MaterialFeatureData &fd ) +{ + Var *outWorldToTangent = (Var*)LangElement::find( "outWorldToTangent" ); + if ( outWorldToTangent ) + return outWorldToTangent; + + Var *worldToTangent = (Var*)LangElement::find( "worldToTangent" ); + if ( !worldToTangent ) + { + Var *texSpaceMat = getOutObjToTangentSpace( componentList, meta, fd ); + + if (!fd.features[MFT_ParticleNormal] ) + { + // turn obj->tangent into world->tangent + worldToTangent = new Var; + worldToTangent->setType( "float3x3" ); + worldToTangent->setName( "worldToTangent" ); + LangElement *worldToTangentDecl = new DecOp( worldToTangent ); + + // Get the world->obj transform + Var *worldToObj = (Var*)LangElement::find( "worldToObj" ); + if ( !worldToObj ) + { + worldToObj = new Var; + worldToObj->setName( "worldToObj" ); + + if ( fd.features[MFT_UseInstancing] ) + { + // We just use transpose to convert the 3x3 portion of + // the object transform to its inverse. + worldToObj->setType( "float3x3" ); + Var *objTrans = getObjTrans( componentList, true, meta ); + meta->addStatement( new GenOp( " @ = transpose( (float3x3)@ ); // Instancing!\r\n", new DecOp( worldToObj ), objTrans ) ); + } + else + { + worldToObj->setType( "float4x4" ); + worldToObj->uniform = true; + worldToObj->constSortPos = cspPrimitive; + } + } + + // assign world->tangent transform + meta->addStatement( new GenOp( " @ = mul( @, (float3x3)@ );\r\n", worldToTangentDecl, texSpaceMat, worldToObj ) ); + } + else + { + // Assume particle normal generation has set this up in the proper space + worldToTangent = texSpaceMat; + } + } + + // send transform to pixel shader + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + outWorldToTangent = connectComp->getElement( RT_TEXCOORD, 1, 3 ); + outWorldToTangent->setName( "outWorldToTangent" ); + outWorldToTangent->setStructName( "OUT" ); + outWorldToTangent->setType( "float3x3" ); + meta->addStatement( new GenOp( " @ = @;\r\n", outWorldToTangent, worldToTangent ) ); + + return outWorldToTangent; +} + +Var* ShaderFeatureHLSL::getOutViewToTangent( Vector &componentList, + MultiLine *meta, + const MaterialFeatureData &fd ) +{ + Var *outViewToTangent = (Var*)LangElement::find( "outViewToTangent" ); + if ( outViewToTangent ) + return outViewToTangent; + + Var *viewToTangent = (Var*)LangElement::find( "viewToTangent" ); + if ( !viewToTangent ) + { + + Var *texSpaceMat = getOutObjToTangentSpace( componentList, meta, fd ); + + if ( !fd.features[MFT_ParticleNormal] ) + { + // turn obj->tangent into world->tangent + viewToTangent = new Var; + viewToTangent->setType( "float3x3" ); + viewToTangent->setName( "viewToTangent" ); + LangElement *viewToTangentDecl = new DecOp( viewToTangent ); + + // Get the view->obj transform + Var *viewToObj = getInvWorldView( componentList, fd.features[MFT_UseInstancing], meta ); + + // assign world->tangent transform + meta->addStatement( new GenOp( " @ = mul( @, (float3x3)@ );\r\n", viewToTangentDecl, texSpaceMat, viewToObj ) ); + } + else + { + // Assume particle normal generation has set this up in the proper space + viewToTangent = texSpaceMat; + } + } + + // send transform to pixel shader + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + outViewToTangent = connectComp->getElement( RT_TEXCOORD, 1, 3 ); + outViewToTangent->setName( "outViewToTangent" ); + outViewToTangent->setStructName( "OUT" ); + outViewToTangent->setType( "float3x3" ); + meta->addStatement( new GenOp( " @ = @;\r\n", outViewToTangent, viewToTangent ) ); + + return outViewToTangent; +} + +Var* ShaderFeatureHLSL::getOutTexCoord( const char *name, + const char *type, + bool mapsToSampler, + bool useTexAnim, + MultiLine *meta, + Vector &componentList ) +{ + String outTexName = String::ToString( "out_%s", name ); + Var *texCoord = (Var*)LangElement::find( outTexName ); + if ( !texCoord ) + { + Var *inTex = getVertTexCoord( name ); + AssertFatal( inTex, "ShaderFeatureHLSL::getOutTexCoord - Unknown vertex input coord!" ); + + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + texCoord = connectComp->getElement( RT_TEXCOORD ); + texCoord->setName( outTexName ); + texCoord->setStructName( "OUT" ); + texCoord->setType( type ); + texCoord->mapsToSampler = mapsToSampler; + + if ( useTexAnim ) + { + inTex->setType( "float4" ); + + // create texture mat var + Var *texMat = new Var; + texMat->setType( "float4x4" ); + texMat->setName( "texMat" ); + texMat->uniform = true; + texMat->constSortPos = cspPass; + + // Statement allows for casting of different types which + // eliminates vector truncation problems. + String statement = String::ToString( " @ = (%s)mul(@, @);\r\n", type ); + meta->addStatement( new GenOp( statement, texCoord, texMat, inTex ) ); + } + else + { + // Statement allows for casting of different types which + // eliminates vector truncation problems. + String statement = String::ToString( " @ = (%s)@;\r\n", type ); + meta->addStatement( new GenOp( statement, texCoord, inTex ) ); + } + } + + AssertFatal( dStrcmp( type, (const char*)texCoord->type ) == 0, + "ShaderFeatureHLSL::getOutTexCoord - Type mismatch!" ); + + return texCoord; +} + +Var* ShaderFeatureHLSL::getInTexCoord( const char *name, + const char *type, + bool mapsToSampler, + Vector &componentList ) +{ + Var* texCoord = (Var*)LangElement::find( name ); + if ( !texCoord ) + { + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + texCoord = connectComp->getElement( RT_TEXCOORD ); + texCoord->setName( name ); + texCoord->setStructName( "IN" ); + texCoord->setType( type ); + texCoord->mapsToSampler = mapsToSampler; + } + + AssertFatal( dStrcmp( type, (const char*)texCoord->type ) == 0, + "ShaderFeatureHLSL::getInTexCoord - Type mismatch!" ); + + return texCoord; +} + +Var* ShaderFeatureHLSL::getInColor( const char *name, + const char *type, + Vector &componentList ) +{ + Var *inColor = (Var*)LangElement::find( name ); + if ( !inColor ) + { + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + inColor = connectComp->getElement( RT_COLOR ); + inColor->setName( name ); + inColor->setStructName( "IN" ); + inColor->setType( type ); + } + + AssertFatal( dStrcmp( type, (const char*)inColor->type ) == 0, + "ShaderFeatureHLSL::getInColor - Type mismatch!" ); + + return inColor; +} + +Var* ShaderFeatureHLSL::addOutVpos( MultiLine *meta, + Vector &componentList ) +{ + // Nothing to do if we're on SM 3.0... we use the real vpos. + if ( GFX->getPixelShaderVersion() >= 3.0f ) + return NULL; + + // For SM 2.x we need to generate the vpos in the vertex shader + // and pass it as a texture coord to the pixel shader. + + Var *outVpos = (Var*)LangElement::find( "outVpos" ); + if ( !outVpos ) + { + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + outVpos = connectComp->getElement( RT_TEXCOORD ); + outVpos->setName( "outVpos" ); + outVpos->setStructName( "OUT" ); + outVpos->setType( "float4" ); + outVpos->mapsToSampler = false; + + Var *outPosition = (Var*) LangElement::find( "hpos" ); + AssertFatal( outPosition, "ShaderFeatureHLSL::addOutVpos - Didn't find the output position." ); + + meta->addStatement( new GenOp( " @ = @;\r\n", outVpos, outPosition ) ); + } + + return outVpos; +} + +Var* ShaderFeatureHLSL::getInVpos( MultiLine *meta, + Vector &componentList ) +{ + Var *inVpos = (Var*)LangElement::find( "vpos" ); + if ( inVpos ) + return inVpos; + + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + if ( GFX->getPixelShaderVersion() >= 3.0f ) + { + inVpos = connectComp->getElement( RT_VPOS ); + inVpos->setName( "vpos" ); + inVpos->setStructName( "IN" ); + inVpos->setType( "float2" ); + return inVpos; + } + + inVpos = connectComp->getElement( RT_TEXCOORD ); + inVpos->setName( "inVpos" ); + inVpos->setStructName( "IN" ); + inVpos->setType( "float4" ); + + Var *vpos = new Var( "vpos", "float2" ); + meta->addStatement( new GenOp( " @ = @.xy / @.w;\r\n", new DecOp( vpos ), inVpos, inVpos ) ); + + return vpos; +} + +Var* ShaderFeatureHLSL::getInWorldToTangent( Vector &componentList ) +{ + Var *worldToTangent = (Var*)LangElement::find( "worldToTangent" ); + if ( !worldToTangent ) + { + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + worldToTangent = connectComp->getElement( RT_TEXCOORD, 1, 3 ); + worldToTangent->setName( "worldToTangent" ); + worldToTangent->setStructName( "IN" ); + worldToTangent->setType( "float3x3" ); + } + + return worldToTangent; +} + +Var* ShaderFeatureHLSL::getInViewToTangent( Vector &componentList ) +{ + Var *viewToTangent = (Var*)LangElement::find( "viewToTangent" ); + if ( !viewToTangent ) + { + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + viewToTangent = connectComp->getElement( RT_TEXCOORD, 1, 3 ); + viewToTangent->setName( "viewToTangent" ); + viewToTangent->setStructName( "IN" ); + viewToTangent->setType( "float3x3" ); + } + + return viewToTangent; +} + +Var* ShaderFeatureHLSL::getNormalMapTex() +{ + Var *normalMap = (Var*)LangElement::find( "bumpMap" ); + if ( !normalMap ) + { + normalMap = new Var; + normalMap->setType( "sampler2D" ); + normalMap->setName( "bumpMap" ); + normalMap->uniform = true; + normalMap->sampler = true; + normalMap->constNum = Var::getTexUnitNum(); + } + + return normalMap; +} + +Var* ShaderFeatureHLSL::getObjTrans( Vector &componentList, + bool useInstancing, + MultiLine *meta ) +{ + Var *objTrans = (Var*)LangElement::find( "objTrans" ); + if ( objTrans ) + return objTrans; + + if ( useInstancing ) + { + ShaderConnector *vertStruct = dynamic_cast( componentList[C_VERT_STRUCT] ); + Var *instObjTrans = vertStruct->getElement( RT_TEXCOORD, 4, 4 ); + instObjTrans->setStructName( "IN" ); + instObjTrans->setName( "inst_objectTrans" ); + + mInstancingFormat->addElement( "objTrans", GFXDeclType_Float4, instObjTrans->constNum+0 ); + mInstancingFormat->addElement( "objTrans", GFXDeclType_Float4, instObjTrans->constNum+1 ); + mInstancingFormat->addElement( "objTrans", GFXDeclType_Float4, instObjTrans->constNum+2 ); + mInstancingFormat->addElement( "objTrans", GFXDeclType_Float4, instObjTrans->constNum+3 ); + + objTrans = new Var; + objTrans->setType( "float4x4" ); + objTrans->setName( "objTrans" ); + meta->addStatement( new GenOp( " @ = { // Instancing!\r\n", new DecOp( objTrans ), instObjTrans ) ); + meta->addStatement( new GenOp( " @[0],\r\n", instObjTrans ) ); + meta->addStatement( new GenOp( " @[1],\r\n", instObjTrans ) ); + meta->addStatement( new GenOp( " @[2],\r\n",instObjTrans ) ); + meta->addStatement( new GenOp( " @[3] };\r\n", instObjTrans ) ); + } + else + { + objTrans = new Var; + objTrans->setType( "float4x4" ); + objTrans->setName( "objTrans" ); + objTrans->uniform = true; + objTrans->constSortPos = cspPrimitive; + } + + return objTrans; +} + +Var* ShaderFeatureHLSL::getModelView( Vector &componentList, + bool useInstancing, + MultiLine *meta ) +{ + Var *modelview = (Var*)LangElement::find( "modelview" ); + if ( modelview ) + return modelview; + + if ( useInstancing ) + { + Var *objTrans = getObjTrans( componentList, useInstancing, meta ); + + Var *viewProj = (Var*)LangElement::find( "viewProj" ); + if ( !viewProj ) + { + viewProj = new Var; + viewProj->setType( "float4x4" ); + viewProj->setName( "viewProj" ); + viewProj->uniform = true; + viewProj->constSortPos = cspPass; + } + + modelview = new Var; + modelview->setType( "float4x4" ); + modelview->setName( "modelview" ); + meta->addStatement( new GenOp( " @ = mul( @, @ ); // Instancing!\r\n", new DecOp( modelview ), viewProj, objTrans ) ); + } + else + { + modelview = new Var; + modelview->setType( "float4x4" ); + modelview->setName( "modelview" ); + modelview->uniform = true; + modelview->constSortPos = cspPrimitive; + } + + return modelview; +} + +Var* ShaderFeatureHLSL::getWorldView( Vector &componentList, + bool useInstancing, + MultiLine *meta ) +{ + Var *worldView = (Var*)LangElement::find( "worldViewOnly" ); + if ( worldView ) + return worldView; + + if ( useInstancing ) + { + Var *objTrans = getObjTrans( componentList, useInstancing, meta ); + + Var *worldToCamera = (Var*)LangElement::find( "worldToCamera" ); + if ( !worldToCamera ) + { + worldToCamera = new Var; + worldToCamera->setType( "float4x4" ); + worldToCamera->setName( "worldToCamera" ); + worldToCamera->uniform = true; + worldToCamera->constSortPos = cspPass; + } + + worldView = new Var; + worldView->setType( "float4x4" ); + worldView->setName( "worldViewOnly" ); + + meta->addStatement( new GenOp( " @ = mul( @, @ ); // Instancing!\r\n", new DecOp( worldView ), worldToCamera, objTrans ) ); + } + else + { + worldView = new Var; + worldView->setType( "float4x4" ); + worldView->setName( "worldViewOnly" ); + worldView->uniform = true; + worldView->constSortPos = cspPrimitive; + } + + return worldView; +} + + +Var* ShaderFeatureHLSL::getInvWorldView( Vector &componentList, + bool useInstancing, + MultiLine *meta ) +{ + Var *viewToObj = (Var*)LangElement::find( "viewToObj" ); + if ( viewToObj ) + return viewToObj; + + if ( useInstancing ) + { + Var *worldView = getWorldView( componentList, useInstancing, meta ); + + viewToObj = new Var; + viewToObj->setType( "float3x3" ); + viewToObj->setName( "viewToObj" ); + + // We just use transpose to convert the 3x3 portion + // of the world view transform into its inverse. + + meta->addStatement( new GenOp( " @ = transpose( (float3x3)@ ); // Instancing!\r\n", new DecOp( viewToObj ), worldView ) ); + } + else + { + viewToObj = new Var; + viewToObj->setType( "float4x4" ); + viewToObj->setName( "viewToObj" ); + viewToObj->uniform = true; + viewToObj->constSortPos = cspPrimitive; + } + + return viewToObj; +} + +void ShaderFeatureHLSL::getWsPosition( Vector &componentList, + bool useInstancing, + MultiLine *meta, + LangElement *wsPosition ) +{ + Var *inPosition = (Var*)LangElement::find( "wsPosition" ); + if ( inPosition ) + { + meta->addStatement( new GenOp( " @ = @.xyz;\r\n", + wsPosition, inPosition ) ); + return; + } + + // Get the input position. + inPosition = (Var*)LangElement::find( "inPosition" ); + if ( !inPosition ) + inPosition = (Var*)LangElement::find( "position" ); + + AssertFatal( inPosition, "ShaderFeatureHLSL::getWsPosition - The vertex position was not found!" ); + + Var *objTrans = getObjTrans( componentList, useInstancing, meta ); + + meta->addStatement( new GenOp( " @ = mul( @, float4( @.xyz, 1 ) ).xyz;\r\n", + wsPosition, objTrans, inPosition ) ); +} + +Var* ShaderFeatureHLSL::addOutWsPosition( Vector &componentList, + bool useInstancing, + MultiLine *meta ) +{ + Var *outWsPosition = (Var*)LangElement::find( "outWsPosition" ); + if ( !outWsPosition ) + { + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + outWsPosition = connectComp->getElement( RT_TEXCOORD ); + outWsPosition->setName( "outWsPosition" ); + outWsPosition->setStructName( "OUT" ); + outWsPosition->setType( "float3" ); + outWsPosition->mapsToSampler = false; + + getWsPosition( componentList, useInstancing, meta, outWsPosition ); + } + + return outWsPosition; +} + +Var* ShaderFeatureHLSL::getInWsPosition( Vector &componentList ) +{ + Var *wsPosition = (Var*)LangElement::find( "wsPosition" ); + if ( !wsPosition ) + { + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + wsPosition = connectComp->getElement( RT_TEXCOORD ); + wsPosition->setName( "wsPosition" ); + wsPosition->setStructName( "IN" ); + wsPosition->setType( "float3" ); + } + + return wsPosition; +} + +Var* ShaderFeatureHLSL::getWsView( Var *wsPosition, MultiLine *meta ) +{ + Var *wsView = (Var*)LangElement::find( "wsView" ); + if ( !wsView ) + { + wsView = new Var( "wsView", "float3" ); + + Var *eyePos = (Var*)LangElement::find( "eyePosWorld" ); + if ( !eyePos ) + { + eyePos = new Var; + eyePos->setType( "float3" ); + eyePos->setName( "eyePosWorld" ); + eyePos->uniform = true; + eyePos->constSortPos = cspPass; + } + + meta->addStatement( new GenOp( " @ = normalize( @ - @ );\r\n", + new DecOp( wsView ), eyePos, wsPosition ) ); + } + + return wsView; +} + +Var* ShaderFeatureHLSL::addOutDetailTexCoord( Vector &componentList, + MultiLine *meta, + bool useTexAnim ) +{ + // Check if its already added. + Var *outTex = (Var*)LangElement::find( "detCoord" ); + if ( outTex ) + return outTex; + + // Grab incoming texture coords. + Var *inTex = getVertTexCoord( "texCoord" ); + + // create detail variable + Var *detScale = new Var; + detScale->setType( "float2" ); + detScale->setName( "detailScale" ); + detScale->uniform = true; + detScale->constSortPos = cspPotentialPrimitive; + + // grab connector texcoord register + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + outTex = connectComp->getElement( RT_TEXCOORD ); + outTex->setName( "detCoord" ); + outTex->setStructName( "OUT" ); + outTex->setType( "float2" ); + outTex->mapsToSampler = true; + + if ( useTexAnim ) + { + inTex->setType( "float4" ); + + // Find or create the texture matrix. + Var *texMat = (Var*)LangElement::find( "texMat" ); + if ( !texMat ) + { + texMat = new Var; + texMat->setType( "float4x4" ); + texMat->setName( "texMat" ); + texMat->uniform = true; + texMat->constSortPos = cspPass; + } + + meta->addStatement( new GenOp( " @ = mul(@, @) * @;\r\n", outTex, texMat, inTex, detScale ) ); + } + else + { + // setup output to mul texCoord by detail scale + meta->addStatement( new GenOp( " @ = @ * @;\r\n", outTex, inTex, detScale ) ); + } + + return outTex; +} + +//**************************************************************************** +// Base Texture +//**************************************************************************** + +void DiffuseMapFeatHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + getOutTexCoord( "texCoord", + "float2", + true, + fd.features[MFT_TexAnim], + meta, + componentList ); + output = meta; +} + +void DiffuseMapFeatHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // grab connector texcoord register + Var *inTex = getInTexCoord( "texCoord", "float2", true, componentList ); + + // create texture var + Var *diffuseMap = new Var; + diffuseMap->setType( "sampler2D" ); + diffuseMap->setName( "diffuseMap" ); + diffuseMap->uniform = true; + diffuseMap->sampler = true; + diffuseMap->constNum = Var::getTexUnitNum(); // used as texture unit num here + + if ( fd.features[MFT_CubeMap] ) + { + MultiLine * meta = new MultiLine; + + // create sample color + Var *diffColor = new Var; + diffColor->setType( "float4" ); + diffColor->setName( "diffuseColor" ); + LangElement *colorDecl = new DecOp( diffColor ); + + meta->addStatement( new GenOp( " @ = tex2D(@, @);\r\n", + colorDecl, + diffuseMap, + inTex ) ); + + meta->addStatement( new GenOp( " @;\r\n", assignColor( diffColor, Material::Mul ) ) ); + output = meta; + } + else if(fd.features[MFT_DiffuseMapAtlas]) + { + // Handle atlased textures + // http://www.infinity-universe.com/Infinity/index.php?option=com_content&task=view&id=65&Itemid=47 + MultiLine * meta = new MultiLine; + output = meta; + + Var *atlasedTex = new Var; + atlasedTex->setName("atlasedTexCoord"); + atlasedTex->setType("float2"); + LangElement *atDecl = new DecOp(atlasedTex); + + // Parameters of the texture atlas + Var *atParams = new Var; + atParams->setType("float4"); + atParams->setName("diffuseAtlasParams"); + atParams->uniform = true; + atParams->constSortPos = cspPotentialPrimitive; + + // Parameters of the texture (tile) this object is using in the atlas + Var *tileParams = new Var; + tileParams->setType("float4"); + tileParams->setName("diffuseAtlasTileParams"); + tileParams->uniform = true; + tileParams->constSortPos = cspPotentialPrimitive; + + const bool is_sm3 = (GFX->getPixelShaderVersion() > 2.0f); + if(is_sm3) + { + // Figure out the mip level + meta->addStatement(new GenOp(" float2 _dx = ddx(@ * @.z);\r\n", inTex, atParams)); + meta->addStatement(new GenOp(" float2 _dy = ddy(@ * @.z);\r\n", inTex, atParams)); + meta->addStatement(new GenOp(" float mipLod = 0.5 * log2(max(dot(_dx, _dx), dot(_dy, _dy)));\r\n")); + meta->addStatement(new GenOp(" mipLod = clamp(mipLod, 0.0, @.w);\r\n", atParams)); + + // And the size of the mip level + meta->addStatement(new GenOp(" float mipPixSz = pow(2.0, @.w - mipLod);\r\n", atParams)); + meta->addStatement(new GenOp(" float2 mipSz = mipPixSz / @.xy;\r\n", atParams)); + } + else + { + meta->addStatement(new GenOp(" float2 mipSz = float2(1.0, 1.0);\r\n")); + } + + // Tiling mode + // TODO: Select wrap or clamp somehow + if( true ) // Wrap + meta->addStatement(new GenOp(" @ = frac(@);\r\n", atDecl, inTex)); + else // Clamp + meta->addStatement(new GenOp(" @ = saturate(@);\r\n", atDecl, inTex)); + + // Finally scale/offset, and correct for filtering + meta->addStatement(new GenOp(" @ = @ * ((mipSz * @.xy - 1.0) / mipSz) + 0.5 / mipSz + @.xy * @.xy;\r\n", + atlasedTex, atlasedTex, atParams, atParams, tileParams)); + + // Add a newline + meta->addStatement(new GenOp( "\r\n")); + + // For the rest of the feature... + inTex = atlasedTex; + + // create sample color var + Var *diffColor = new Var; + diffColor->setType("float4"); + diffColor->setName("diffuseColor"); + + // To dump out UV coords... +//#define DEBUG_ATLASED_UV_COORDS +#ifdef DEBUG_ATLASED_UV_COORDS + if(!fd.features[MFT_PrePassConditioner]) + { + meta->addStatement(new GenOp(" @ = float4(@.xy, mipLod / @.w, 1.0);\r\n", new DecOp(diffColor), inTex, atParams)); + meta->addStatement(new GenOp(" @; return OUT;\r\n", assignColor(diffColor, Material::Mul))); + return; + } +#endif + + if(is_sm3) + { + meta->addStatement(new GenOp( " @ = tex2Dlod(@, float4(@, 0.0, mipLod));\r\n", + new DecOp(diffColor), diffuseMap, inTex)); + } + else + { + meta->addStatement(new GenOp( " @ = tex2D(@, @);\r\n", + new DecOp(diffColor), diffuseMap, inTex)); + } + + meta->addStatement(new GenOp( " @;\r\n", assignColor(diffColor, Material::Mul))); + } + else + { + LangElement *statement = new GenOp( "tex2D(@, @)", diffuseMap, inTex ); + output = new GenOp( " @;\r\n", assignColor( statement, Material::Mul ) ); + } + +} + +ShaderFeature::Resources DiffuseMapFeatHLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + res.numTex = 1; + res.numTexReg = 1; + + return res; +} + +void DiffuseMapFeatHLSL::setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ) +{ + GFXTextureObject *tex = stageDat.getTex( MFT_DiffuseMap ); + if ( tex ) + passData.mTexSlot[ texIndex++ ].texObject = tex; +} + + +//**************************************************************************** +// Overlay Texture +//**************************************************************************** + +void OverlayTexFeatHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + Var *inTex = getVertTexCoord( "texCoord2" ); + AssertFatal( inTex, "OverlayTexFeatHLSL::processVert() - The second UV set was not found!" ); + + // grab connector texcoord register + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outTex = connectComp->getElement( RT_TEXCOORD ); + outTex->setName( "outTexCoord2" ); + outTex->setStructName( "OUT" ); + outTex->setType( "float2" ); + outTex->mapsToSampler = true; + + if( fd.features[MFT_TexAnim] ) + { + inTex->setType( "float4" ); + + // Find or create the texture matrix. + Var *texMat = (Var*)LangElement::find( "texMat" ); + if ( !texMat ) + { + texMat = new Var; + texMat->setType( "float4x4" ); + texMat->setName( "texMat" ); + texMat->uniform = true; + texMat->constSortPos = cspPass; + } + + output = new GenOp( " @ = mul(@, @);\r\n", outTex, texMat, inTex ); + return; + } + + // setup language elements to output incoming tex coords to output + output = new GenOp( " @ = @;\r\n", outTex, inTex ); +} + +void OverlayTexFeatHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + + // grab connector texcoord register + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *inTex = connectComp->getElement( RT_TEXCOORD ); + inTex->setName( "texCoord2" ); + inTex->setStructName( "IN" ); + inTex->setType( "float2" ); + inTex->mapsToSampler = true; + + // create texture var + Var *diffuseMap = new Var; + diffuseMap->setType( "sampler2D" ); + diffuseMap->setName( "overlayMap" ); + diffuseMap->uniform = true; + diffuseMap->sampler = true; + diffuseMap->constNum = Var::getTexUnitNum(); // used as texture unit num here + + LangElement *statement = new GenOp( "tex2D(@, @)", diffuseMap, inTex ); + output = new GenOp( " @;\r\n", assignColor( statement, Material::LerpAlpha ) ); +} + +ShaderFeature::Resources OverlayTexFeatHLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + res.numTex = 1; + res.numTexReg = 1; + return res; +} + +void OverlayTexFeatHLSL::setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ) +{ + GFXTextureObject *tex = stageDat.getTex( MFT_OverlayMap ); + if ( tex ) + passData.mTexSlot[ texIndex++ ].texObject = tex; +} + + +//**************************************************************************** +// Diffuse color +//**************************************************************************** + +void DiffuseFeatureHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + Var *diffuseMaterialColor = new Var; + diffuseMaterialColor->setType( "float4" ); + diffuseMaterialColor->setName( "diffuseMaterialColor" ); + diffuseMaterialColor->uniform = true; + diffuseMaterialColor->constSortPos = cspPotentialPrimitive; + + MultiLine * meta = new MultiLine; + meta->addStatement( new GenOp( " @;\r\n", assignColor( diffuseMaterialColor, Material::Mul ) ) ); + output = meta; +} + + +//**************************************************************************** +// Diffuse vertex color +//**************************************************************************** + +void DiffuseVertColorFeatureHLSL::processVert( Vector< ShaderComponent* >& componentList, + const MaterialFeatureData& fd ) +{ + // Create vertex color connector if it doesn't exist. + + Var* outColor = dynamic_cast< Var* >( LangElement::find( "vertColor" ) ); + if( !outColor ) + { + // Search for vert color. + + Var* inColor = dynamic_cast< Var* >( LangElement::find( "diffuse" ) ); + if( !inColor ) + { + output = NULL; + return; + } + + // Create connector. + + ShaderConnector* connectComp = dynamic_cast< ShaderConnector* >( componentList[ C_CONNECTOR ] ); + AssertFatal( connectComp, "DiffuseVertColorFeatureGLSL::processVert - C_CONNECTOR is not a ShaderConnector" ); + Var* outColor = connectComp->getElement( RT_COLOR ); + outColor->setName( "vertColor" ); + outColor->setStructName( "OUT" ); + outColor->setType( "float4" ); + + output = new GenOp( " @ = @;\r\n", outColor, inColor ); + } + else + output = NULL; // Nothing we need to do. +} + +void DiffuseVertColorFeatureHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + Var* vertColor = dynamic_cast< Var* >( LangElement::find( "vertColor" ) ); + if( !vertColor ) + { + ShaderConnector* connectComp = dynamic_cast< ShaderConnector* >( componentList[ C_CONNECTOR ] ); + AssertFatal( connectComp, "DiffuseVertColorFeatureGLSL::processVert - C_CONNECTOR is not a ShaderConnector" ); + vertColor = connectComp->getElement( RT_COLOR ); + vertColor->setName( "vertColor" ); + vertColor->setStructName( "IN" ); + vertColor->setType( "float4" ); + } + + MultiLine* meta = new MultiLine; + meta->addStatement( new GenOp( " @;\r\n", assignColor( vertColor, Material::Mul ) ) ); + output = meta; +} + + +//**************************************************************************** +// Lightmap +//**************************************************************************** + +void LightmapFeatHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // grab tex register from incoming vert + Var *inTex = getVertTexCoord( "texCoord2" ); + + // grab connector texcoord register + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outTex = connectComp->getElement( RT_TEXCOORD ); + outTex->setName( "texCoord2" ); + outTex->setStructName( "OUT" ); + outTex->setType( "float2" ); + outTex->mapsToSampler = true; + + // setup language elements to output incoming tex coords to output + output = new GenOp( " @ = @;\r\n", outTex, inTex ); +} + +void LightmapFeatHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // grab connector texcoord register + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *inTex = connectComp->getElement( RT_TEXCOORD ); + inTex->setName( "texCoord2" ); + inTex->setStructName( "IN" ); + inTex->setType( "float2" ); + inTex->mapsToSampler = true; + + // create texture var + Var *lightMap = new Var; + lightMap->setType( "sampler2D" ); + lightMap->setName( "lightMap" ); + lightMap->uniform = true; + lightMap->sampler = true; + lightMap->constNum = Var::getTexUnitNum(); // used as texture unit num here + + + // argh, pixel specular should prob use this too + if( fd.features[MFT_NormalMap] ) + { + Var *lmColor = new Var; + lmColor->setName( "lmColor" ); + lmColor->setType( "float4" ); + LangElement *lmColorDecl = new DecOp( lmColor ); + + output = new GenOp( " @ = tex2D(@, @);\r\n", lmColorDecl, lightMap, inTex ); + return; + } + + // Add realtime lighting, if it is available + LangElement *statement = NULL; + if( fd.features[MFT_RTLighting] ) + { + // Advanced lighting is the only dynamic lighting supported right now + Var *inColor = (Var*) LangElement::find( "d_lightcolor" ); + if(inColor != NULL) + { + // Find out if RTLighting should be added or substituted + bool bPreProcessedLighting = false; + AdvancedLightBinManager *lightBin; + if ( Sim::findObject( "AL_LightBinMgr", lightBin ) ) + bPreProcessedLighting = lightBin->MRTLightmapsDuringPrePass(); + + // Lightmap has already been included in the advanced light bin, so + // no need to do any sampling or anything + if(bPreProcessedLighting) + statement = new GenOp( "float4(@, 1.0)", inColor ); + else + statement = new GenOp( "tex2D(@, @) + float4(@.rgb, 0.0)", lightMap, inTex, inColor ); + } + } + + // If we still don't have it... then just sample the lightmap. + if ( !statement ) + statement = new GenOp( "tex2D(@, @)", lightMap, inTex ); + + // Assign to proper render target + MultiLine *meta = new MultiLine; + if( fd.features[MFT_LightbufferMRT] ) + { + meta->addStatement( new GenOp( " @;\r\n", assignColor( statement, Material::None, NULL, ShaderFeature::RenderTarget1 ) ) ); + meta->addStatement( new GenOp( " @.a = 0.0001;\r\n", LangElement::find( getOutputTargetVarName(ShaderFeature::RenderTarget1) ) ) ); + } + else + meta->addStatement( new GenOp( " @;\r\n", assignColor( statement, Material::Mul ) ) ); + + output = meta; +} + +ShaderFeature::Resources LightmapFeatHLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + res.numTex = 1; + res.numTexReg = 1; + + return res; +} + +void LightmapFeatHLSL::setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ) +{ + GFXTextureObject *tex = stageDat.getTex( MFT_LightMap ); + if ( tex ) + passData.mTexSlot[ texIndex++ ].texObject = tex; + else + passData.mTexType[ texIndex++ ] = Material::Lightmap; +} + +U32 LightmapFeatHLSL::getOutputTargets( const MaterialFeatureData &fd ) const +{ + return fd.features[MFT_LightbufferMRT] ? ShaderFeature::RenderTarget1 : ShaderFeature::DefaultTarget; +} + +//**************************************************************************** +// Tonemap +//**************************************************************************** + +void TonemapFeatHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // Grab the connector + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + // Set up the second set of texCoords + Var *inTex2 = getVertTexCoord( "texCoord2" ); + + if ( inTex2 ) + { + Var *outTex2 = connectComp->getElement( RT_TEXCOORD ); + outTex2->setName( "texCoord2" ); + outTex2->setStructName( "OUT" ); + outTex2->setType( "float2" ); + outTex2->mapsToSampler = true; + + output = new GenOp( " @ = @;\r\n", outTex2, inTex2 ); + } +} + +void TonemapFeatHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // Grab connector + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + Var *inTex2 = connectComp->getElement( RT_TEXCOORD ); + inTex2->setName( "texCoord2" ); + inTex2->setStructName( "IN" ); + inTex2->setType( "float2" ); + inTex2->mapsToSampler = true; + + // create texture var + Var *toneMap = new Var; + toneMap->setType( "sampler2D" ); + toneMap->setName( "toneMap" ); + toneMap->uniform = true; + toneMap->sampler = true; + toneMap->constNum = Var::getTexUnitNum(); // used as texture unit num here + + MultiLine * meta = new MultiLine; + + // First get the toneMap color + Var *toneMapColor = new Var; + toneMapColor->setType( "float4" ); + toneMapColor->setName( "toneMapColor" ); + LangElement *toneMapColorDecl = new DecOp( toneMapColor ); + + meta->addStatement( new GenOp( " @ = tex2D(@, @);\r\n", toneMapColorDecl, toneMap, inTex2 ) ); + + // We do a different calculation if there is a diffuse map or not + Material::BlendOp blendOp = Material::Mul; + if ( fd.features[MFT_DiffuseMap] ) + { + // Reverse the tonemap + meta->addStatement( new GenOp( " @ = -1.0f * log(1.0f - @);\r\n", toneMapColor, toneMapColor ) ); + + // Re-tonemap with the current color factored in + blendOp = Material::ToneMap; + } + + // Find out if RTLighting should be added + bool bPreProcessedLighting = false; + AdvancedLightBinManager *lightBin; + if ( Sim::findObject( "AL_LightBinMgr", lightBin ) ) + bPreProcessedLighting = lightBin->MRTLightmapsDuringPrePass(); + + // Add in the realtime lighting contribution + if ( fd.features[MFT_RTLighting] ) + { + // Right now, only Advanced Lighting is supported + Var *inColor = (Var*) LangElement::find( "d_lightcolor" ); + if(inColor != NULL) + { + // Assign value in d_lightcolor to toneMapColor if it exists. This is + // the dynamic light buffer, and it already has the tonemap included + if(bPreProcessedLighting) + meta->addStatement( new GenOp( " @.rgb = @;\r\n", toneMapColor, inColor ) ); + else + meta->addStatement( new GenOp( " @.rgb += @.rgb;\r\n", toneMapColor, inColor ) ); + } + } + + // Assign to proper render target + if( fd.features[MFT_LightbufferMRT] ) + { + meta->addStatement( new GenOp( " @;\r\n", assignColor( toneMapColor, Material::None, NULL, ShaderFeature::RenderTarget1 ) ) ); + meta->addStatement( new GenOp( " @.a = 0.0001;\r\n", LangElement::find( getOutputTargetVarName(ShaderFeature::RenderTarget1) ) ) ); + } + else + meta->addStatement( new GenOp( " @;\r\n", assignColor( toneMapColor, blendOp ) ) ); + + output = meta; +} + +ShaderFeature::Resources TonemapFeatHLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + res.numTex = 1; + res.numTexReg = 1; + + return res; +} + +void TonemapFeatHLSL::setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ) +{ + GFXTextureObject *tex = stageDat.getTex( MFT_ToneMap ); + if ( tex ) + { + passData.mTexType[ texIndex ] = Material::ToneMapTex; + passData.mTexSlot[ texIndex++ ].texObject = tex; + } +} + +U32 TonemapFeatHLSL::getOutputTargets( const MaterialFeatureData &fd ) const +{ + return fd.features[MFT_LightbufferMRT] ? ShaderFeature::RenderTarget1 : ShaderFeature::DefaultTarget; +} + +//**************************************************************************** +// pureLIGHT Lighting +//**************************************************************************** + +void VertLitHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // If we have a lightMap or toneMap then our lighting will be + // handled by the MFT_LightMap or MFT_ToneNamp feature instead + if ( fd.features[MFT_LightMap] || fd.features[MFT_ToneMap] ) + { + output = NULL; + return; + } + + // Create vertex color connector if it doesn't exist. + + Var* outColor = dynamic_cast< Var* >( LangElement::find( "vertColor" ) ); + if( !outColor ) + { + // Search for vert color + Var *inColor = (Var*) LangElement::find( "diffuse" ); + + // If there isn't a vertex color then we can't do anything + if( !inColor ) + { + output = NULL; + return; + } + + // Grab the connector color + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outColor = connectComp->getElement( RT_COLOR ); + outColor->setName( "vertColor" ); + outColor->setStructName( "OUT" ); + outColor->setType( "float4" ); + + output = new GenOp( " @ = @;\r\n", outColor, inColor ); + } + else + output = NULL; // Nothing we need to do. +} + +void VertLitHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // If we have a lightMap or toneMap then our lighting will be + // handled by the MFT_LightMap or MFT_ToneNamp feature instead + if ( fd.features[MFT_LightMap] || fd.features[MFT_ToneMap] ) + { + output = NULL; + return; + } + + // Grab the connector color register + Var* vertColor = dynamic_cast< Var* >( LangElement::find( "vertColor" ) ); + if( !vertColor ) + { + ShaderConnector* connectComp = dynamic_cast< ShaderConnector* >( componentList[ C_CONNECTOR ] ); + AssertFatal( connectComp, "VertLitGLSL::processVert - C_CONNECTOR is not a ShaderConnector" ); + vertColor = connectComp->getElement( RT_COLOR ); + vertColor->setName( "vertColor" ); + vertColor->setStructName( "IN" ); + vertColor->setType( "float4" ); + } + + MultiLine * meta = new MultiLine; + + // Defaults (no diffuse map) + Material::BlendOp blendOp = Material::Mul; + LangElement *outColor = vertColor; + + // We do a different calculation if there is a diffuse map or not + if ( fd.features[MFT_DiffuseMap] || fd.features[MFT_VertLitTone] ) + { + Var * finalVertColor = new Var; + finalVertColor->setName( "finalVertColor" ); + finalVertColor->setType( "float4" ); + LangElement *finalVertColorDecl = new DecOp( finalVertColor ); + + // Reverse the tonemap + meta->addStatement( new GenOp( " @ = -1.0f * log(1.0f - @);\r\n", finalVertColorDecl, vertColor ) ); + + // Set the blend op to tonemap + blendOp = Material::ToneMap; + outColor = finalVertColor; + } + + // Add in the realtime lighting contribution, if applicable + if ( fd.features[MFT_RTLighting] ) + { + Var *rtLightingColor = (Var*) LangElement::find( "d_lightcolor" ); + if(rtLightingColor != NULL) + { + bool bPreProcessedLighting = false; + AdvancedLightBinManager *lightBin; + if ( Sim::findObject( "AL_LightBinMgr", lightBin ) ) + bPreProcessedLighting = lightBin->MRTLightmapsDuringPrePass(); + + // Assign value in d_lightcolor to toneMapColor if it exists. This is + // the dynamic light buffer, and it already has the baked-vertex-color + // included in it + if(bPreProcessedLighting) + outColor = new GenOp( "float4(@.rgb, 1.0)", rtLightingColor ); + else + outColor = new GenOp( "float4(@.rgb + @.rgb, 1.0)", rtLightingColor, outColor ); + } + } + + // Output the color + if ( fd.features[MFT_LightbufferMRT] ) + { + meta->addStatement( new GenOp( " @;\r\n", assignColor( outColor, Material::None, NULL, ShaderFeature::RenderTarget1 ) ) ); + meta->addStatement( new GenOp( " @.a = 0.0001;\r\n", LangElement::find( getOutputTargetVarName(ShaderFeature::RenderTarget1) ) ) ); + } + else + meta->addStatement( new GenOp( " @;\r\n", assignColor( outColor, blendOp ) ) ); + + output = meta; +} + +U32 VertLitHLSL::getOutputTargets( const MaterialFeatureData &fd ) const +{ + return fd.features[MFT_LightbufferMRT] ? ShaderFeature::RenderTarget1 : ShaderFeature::DefaultTarget; +} + +//**************************************************************************** +// Detail map +//**************************************************************************** + +void DetailFeatHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + addOutDetailTexCoord( componentList, + meta, + fd.features[MFT_TexAnim] ); + output = meta; +} + +void DetailFeatHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // Get the detail texture coord. + Var *inTex = getInTexCoord( "detCoord", "float2", true, componentList ); + + // create texture var + Var *detailMap = new Var; + detailMap->setType( "sampler2D" ); + detailMap->setName( "detailMap" ); + detailMap->uniform = true; + detailMap->sampler = true; + detailMap->constNum = Var::getTexUnitNum(); // used as texture unit num here + + // We're doing the standard greyscale detail map + // technique which can darken and lighten the + // diffuse texture. + + // TODO: We could add a feature to toggle between this + // and a simple multiplication with the detail map. + + LangElement *statement = new GenOp( "( tex2D(@, @) * 2.0 ) - 1.0", detailMap, inTex ); + output = new GenOp( " @;\r\n", assignColor( statement, Material::Add ) ); +} + +ShaderFeature::Resources DetailFeatHLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + res.numTex = 1; + res.numTexReg = 1; + + return res; +} + +void DetailFeatHLSL::setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ) +{ + GFXTextureObject *tex = stageDat.getTex( MFT_DetailMap ); + if ( tex ) + passData.mTexSlot[ texIndex++ ].texObject = tex; +} + + +//**************************************************************************** +// Vertex position +//**************************************************************************** + +void VertPositionHLSL::determineFeature( Material *material, + const GFXVertexFormat *vertexFormat, + U32 stageNum, + const FeatureType &type, + const FeatureSet &features, + MaterialFeatureData *outFeatureData ) +{ + // This feature is always on! + outFeatureData->features.addFeature( type ); +} + +void VertPositionHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // First check for an input position from a previous feature + // then look for the default vertex position. + Var *inPosition = (Var*)LangElement::find( "inPosition" ); + if ( !inPosition ) + inPosition = (Var*)LangElement::find( "position" ); + + // grab connector position + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outPosition = connectComp->getElement( RT_POSITION ); + outPosition->setName( "hpos" ); + outPosition->setStructName( "OUT" ); + + MultiLine *meta = new MultiLine; + + Var *modelview = getModelView( componentList, fd.features[MFT_UseInstancing], meta ); + + meta->addStatement( new GenOp( " @ = mul(@, float4(@.xyz,1));\r\n", + outPosition, modelview, inPosition ) ); + + output = meta; +} + + +//**************************************************************************** +// Reflect Cubemap +//**************************************************************************** + +void ReflectCubeFeatHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // search for vert normal + Var *inNormal = (Var*) LangElement::find( "normal" ); + if ( !inNormal ) + return; + + MultiLine * meta = new MultiLine; + + // If a base or bump tex is present in the material, but not in the + // current pass - we need to add one to the current pass to use + // its alpha channel as a gloss map. Here we just need the tex coords. + if( !fd.features[MFT_DiffuseMap] && + !fd.features[MFT_NormalMap] ) + { + if( fd.materialFeatures[MFT_DiffuseMap] || + fd.materialFeatures[MFT_NormalMap] ) + { + // find incoming texture var + Var *inTex = getVertTexCoord( "texCoord" ); + + // grab connector texcoord register + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outTex = connectComp->getElement( RT_TEXCOORD ); + outTex->setName( "texCoord" ); + outTex->setStructName( "OUT" ); + outTex->setType( "float2" ); + outTex->mapsToSampler = true; + + // setup language elements to output incoming tex coords to output + meta->addStatement( new GenOp( " @ = @;\r\n", outTex, inTex ) ); + } + } + + // create cubeTrans + Var *cubeTrans = new Var; + cubeTrans->setType( "float3x3" ); + cubeTrans->setName( "cubeTrans" ); + cubeTrans->uniform = true; + cubeTrans->constSortPos = cspPrimitive; + + // create cubeEye position + Var *cubeEyePos = new Var; + cubeEyePos->setType( "float3" ); + cubeEyePos->setName( "cubeEyePos" ); + cubeEyePos->uniform = true; + cubeEyePos->constSortPos = cspPrimitive; + + // cube vert position + Var * cubeVertPos = new Var; + cubeVertPos->setName( "cubeVertPos" ); + cubeVertPos->setType( "float3" ); + LangElement *cubeVertPosDecl = new DecOp( cubeVertPos ); + + meta->addStatement( new GenOp( " @ = mul(@, @).xyz;\r\n", + cubeVertPosDecl, cubeTrans, LangElement::find( "position" ) ) ); + + // cube normal + Var * cubeNormal = new Var; + cubeNormal->setName( "cubeNormal" ); + cubeNormal->setType( "float3" ); + LangElement *cubeNormDecl = new DecOp( cubeNormal ); + + meta->addStatement( new GenOp( " @ = normalize( mul(@, normalize(@)).xyz );\r\n", + cubeNormDecl, cubeTrans, inNormal ) ); + + // eye to vert + Var * eyeToVert = new Var; + eyeToVert->setName( "eyeToVert" ); + eyeToVert->setType( "float3" ); + LangElement *e2vDecl = new DecOp( eyeToVert ); + + meta->addStatement( new GenOp( " @ = @ - @;\r\n", + e2vDecl, cubeVertPos, cubeEyePos ) ); + + // grab connector texcoord register + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *reflectVec = connectComp->getElement( RT_TEXCOORD ); + reflectVec->setName( "reflectVec" ); + reflectVec->setStructName( "OUT" ); + reflectVec->setType( "float3" ); + reflectVec->mapsToSampler = true; + + meta->addStatement( new GenOp( " @ = reflect(@, @);\r\n", reflectVec, eyeToVert, cubeNormal ) ); + + output = meta; +} + +void ReflectCubeFeatHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine * meta = new MultiLine; + Var *glossColor = NULL; + + // If a base or bump tex is present in the material, but not in the + // current pass - we need to add one to the current pass to use + // its alpha channel as a gloss map. + if( !fd.features[MFT_DiffuseMap] && + !fd.features[MFT_NormalMap] ) + { + if( fd.materialFeatures[MFT_DiffuseMap] || + fd.materialFeatures[MFT_NormalMap] ) + { + // grab connector texcoord register + Var *inTex = getInTexCoord( "texCoord", "float2", true, componentList ); + + // create texture var + Var *newMap = new Var; + newMap->setType( "sampler2D" ); + newMap->setName( "glossMap" ); + newMap->uniform = true; + newMap->sampler = true; + newMap->constNum = Var::getTexUnitNum(); // used as texture unit num here + + // create sample color + Var *color = new Var; + color->setType( "float4" ); + color->setName( "diffuseColor" ); + LangElement *colorDecl = new DecOp( color ); + + glossColor = color; + + meta->addStatement( new GenOp( " @ = tex2D( @, @ );\r\n", colorDecl, newMap, inTex ) ); + } + } + else + { + glossColor = (Var*) LangElement::find( "diffuseColor" ); + if( !glossColor ) + glossColor = (Var*) LangElement::find( "bumpNormal" ); + } + + // grab connector texcoord register + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *reflectVec = connectComp->getElement( RT_TEXCOORD ); + reflectVec->setName( "reflectVec" ); + reflectVec->setStructName( "IN" ); + reflectVec->setType( "float3" ); + reflectVec->mapsToSampler = true; + + // create cubemap var + Var *cubeMap = new Var; + cubeMap->setType( "samplerCUBE" ); + cubeMap->setName( "cubeMap" ); + cubeMap->uniform = true; + cubeMap->sampler = true; + cubeMap->constNum = Var::getTexUnitNum(); // used as texture unit num here + + // TODO: Restore the lighting attenuation here! + Var *attn = NULL; + //if ( fd.materialFeatures[MFT_DynamicLight] ) + //attn = (Var*)LangElement::find("attn"); + //else + if ( fd.materialFeatures[MFT_RTLighting] ) + attn =(Var*)LangElement::find("d_NL_Att"); + + LangElement *texCube = new GenOp( "texCUBE( @, @ )", cubeMap, reflectVec ); + LangElement *lerpVal = NULL; + Material::BlendOp blendOp = Material::LerpAlpha; + + // Note that the lerpVal needs to be a float4 so that + // it will work with the LerpAlpha blend. + + if ( glossColor ) + { + if ( attn ) + lerpVal = new GenOp( "@ * saturate( @ )", glossColor, attn ); + else + lerpVal = glossColor; + } + else + { + if ( attn ) + lerpVal = new GenOp( "saturate( @ ).xxxx", attn ); + else + blendOp = Material::Mul; + } + + meta->addStatement( new GenOp( " @;\r\n", assignColor( texCube, blendOp, lerpVal ) ) ); + output = meta; +} + +ShaderFeature::Resources ReflectCubeFeatHLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + + if( fd.features[MFT_DiffuseMap] || + fd.features[MFT_NormalMap] ) + { + res.numTex = 1; + res.numTexReg = 1; + } + else + { + res.numTex = 2; + res.numTexReg = 2; + } + + return res; +} + +void ReflectCubeFeatHLSL::setTexData( Material::StageData &stageDat, + const MaterialFeatureData &stageFeatures, + RenderPassData &passData, + U32 &texIndex ) +{ + // set up a gloss map if one is not present in the current pass + // but is present in the current material stage + if( !passData.mFeatureData.features[MFT_DiffuseMap] && + !passData.mFeatureData.features[MFT_NormalMap] ) + { + GFXTextureObject *tex = stageDat.getTex( MFT_DetailMap ); + if ( tex && + stageFeatures.features[MFT_DiffuseMap] ) + passData.mTexSlot[ texIndex++ ].texObject = tex; + else + { + tex = stageDat.getTex( MFT_NormalMap ); + + if ( tex && + stageFeatures.features[ MFT_NormalMap ] ) + passData.mTexSlot[ texIndex++ ].texObject = tex; + } + } + + if( stageDat.getCubemap() ) + { + passData.mCubeMap = stageDat.getCubemap(); + passData.mTexType[texIndex++] = Material::Cube; + } + else + { + if( stageFeatures.features[MFT_CubeMap] ) + { + // assuming here that it is a scenegraph cubemap + passData.mTexType[texIndex++] = Material::SGCube; + } + } + +} + + +//**************************************************************************** +// RTLighting +//**************************************************************************** + +RTLightingFeatHLSL::RTLightingFeatHLSL() + : mDep( "shaders/common/lighting.hlsl" ) +{ + addDependency( &mDep ); +} + +void RTLightingFeatHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + // Special case for lighting imposters. We dont have a vert normal and may not + // have a normal map. Generate and pass the normal data the pixel shader needs. + if ( fd.features[MFT_ImposterVert] ) + { + if ( !fd.features[MFT_NormalMap] ) + { + Var *eyePos = (Var*)LangElement::find( "eyePosWorld" ); + if ( !eyePos ) + { + eyePos = new Var( "eyePosWorld", "float3" ); + eyePos->uniform = true; + eyePos->constSortPos = cspPass; + } + + Var *inPosition = (Var*)LangElement::find( "position" ); + + Var *outNormal = connectComp->getElement( RT_TEXCOORD ); + outNormal->setName( "wsNormal" ); + outNormal->setStructName( "OUT" ); + outNormal->setType( "float3" ); + outNormal->mapsToSampler = false; + + // Transform the normal to world space. + meta->addStatement( new GenOp( " @ = normalize( @ - @.xyz );\r\n", outNormal, eyePos, inPosition ) ); + } + + addOutWsPosition( componentList, fd.features[MFT_UseInstancing], meta ); + + output = meta; + + return; + } + + // Find the incoming vertex normal. + Var *inNormal = (Var*)LangElement::find( "normal" ); + + // Skip out on realtime lighting if we don't have a normal + // or we're doing some sort of baked lighting. + if ( !inNormal || + fd.features[MFT_LightMap] || + fd.features[MFT_ToneMap] || + fd.features[MFT_VertLit] ) + return; + + // If there isn't a normal map then we need to pass + // the world space normal to the pixel shader ourselves. + if ( !fd.features[MFT_NormalMap] ) + { + Var *outNormal = connectComp->getElement( RT_TEXCOORD ); + outNormal->setName( "wsNormal" ); + outNormal->setStructName( "OUT" ); + outNormal->setType( "float3" ); + outNormal->mapsToSampler = false; + + // Get the transform to world space. + Var *objTrans = getObjTrans( componentList, fd.features[MFT_UseInstancing], meta ); + + // Transform the normal to world space. + meta->addStatement( new GenOp( " @ = mul( @, float4( normalize( @ ), 0.0 ) ).xyz;\r\n", outNormal, objTrans, inNormal ) ); + } + + addOutWsPosition( componentList, fd.features[MFT_UseInstancing], meta ); + + output = meta; +} + +void RTLightingFeatHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // Skip out on realtime lighting if we don't have a normal + // or we're doing some sort of baked lighting. + // + // TODO: We can totally detect for this in the material + // feature setup... we should move it out of here! + // + if ( fd.features[MFT_LightMap] || fd.features[MFT_ToneMap] || fd.features[MFT_VertLit] ) + return; + + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + MultiLine *meta = new MultiLine; + + // Look for a wsNormal or grab it from the connector. + Var *wsNormal = (Var*)LangElement::find( "wsNormal" ); + if ( !wsNormal ) + { + wsNormal = connectComp->getElement( RT_TEXCOORD ); + wsNormal->setName( "wsNormal" ); + wsNormal->setStructName( "IN" ); + wsNormal->setType( "float3" ); + + // If we loaded the normal its our responsibility + // to normalize it... the interpolators won't. + // + // Note we cast to half here to get partial precision + // optimized code which is an acceptable loss of + // precision for normals and performs much better + // on older Geforce cards. + // + meta->addStatement( new GenOp( " @ = normalize( half3( @ ) );\r\n", wsNormal, wsNormal ) ); + } + + // Now the wsPosition and wsView. + Var *wsPosition = getInWsPosition( componentList ); + Var *wsView = getWsView( wsPosition, meta ); + + // Create temporaries to hold results of lighting. + Var *rtShading = new Var( "rtShading", "float4" ); + Var *specular = new Var( "specular", "float4" ); + meta->addStatement( new GenOp( " @; @;\r\n", + new DecOp( rtShading ), new DecOp( specular ) ) ); + + // Look for a light mask generated from a previous + // feature (this is done for BL terrain lightmaps). + LangElement *lightMask = LangElement::find( "lightMask" ); + if ( !lightMask ) + lightMask = new GenOp( "float4( 1, 1, 1, 1 )" ); + + // Get all the light constants. + Var *inLightPos = new Var( "inLightPos", "float4" ); + inLightPos->uniform = true; + inLightPos->arraySize = 3; + inLightPos->constSortPos = cspPotentialPrimitive; + + Var *inLightInvRadiusSq = new Var( "inLightInvRadiusSq", "float4" ); + inLightInvRadiusSq->uniform = true; + inLightInvRadiusSq->constSortPos = cspPotentialPrimitive; + + Var *inLightColor = new Var( "inLightColor", "float4" ); + inLightColor->uniform = true; + inLightColor->arraySize = 4; + inLightColor->constSortPos = cspPotentialPrimitive; + + Var *inLightSpotDir = new Var( "inLightSpotDir", "float4" ); + inLightSpotDir->uniform = true; + inLightSpotDir->arraySize = 3; + inLightSpotDir->constSortPos = cspPotentialPrimitive; + + Var *inLightSpotAngle = new Var( "inLightSpotAngle", "float4" ); + inLightSpotAngle->uniform = true; + inLightSpotAngle->constSortPos = cspPotentialPrimitive; + + Var *lightSpotFalloff = new Var( "inLightSpotFalloff", "float4" ); + lightSpotFalloff->uniform = true; + lightSpotFalloff->constSortPos = cspPotentialPrimitive; + + Var *specularPower = new Var( "specularPower", "float" ); + specularPower->uniform = true; + specularPower->constSortPos = cspPotentialPrimitive; + + Var *specularColor = (Var*)LangElement::find( "specularColor" ); + if ( !specularColor ) + { + specularColor = new Var( "specularColor", "float4" ); + specularColor->uniform = true; + specularColor->constSortPos = cspPotentialPrimitive; + } + + Var *ambient = new Var( "ambient", "float4" ); + ambient->uniform = true; + ambient->constSortPos = cspPass; + + // Calculate the diffuse shading and specular powers. + meta->addStatement( new GenOp( " compute4Lights( @, @, @, @,\r\n" + " @, @, @, @, @, @, @, @,\r\n" + " @, @ );\r\n", + wsView, wsPosition, wsNormal, lightMask, + inLightPos, inLightInvRadiusSq, inLightColor, inLightSpotDir, inLightSpotAngle, lightSpotFalloff, specularPower, specularColor, + rtShading, specular ) ); + + // Apply the lighting to the diffuse color. + LangElement *lighting = new GenOp( "float4( @.rgb + @.rgb, 1 )", rtShading, ambient ); + meta->addStatement( new GenOp( " @;\r\n", assignColor( lighting, Material::Mul ) ) ); + output = meta; +} + +ShaderFeature::Resources RTLightingFeatHLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + + // These features disable realtime lighting. + if ( !fd.features[MFT_LightMap] && + !fd.features[MFT_ToneMap] && + !fd.features[MFT_VertLit] ) + { + // If enabled we pass the position. + res.numTexReg = 1; + + // If there isn't a bump map then we pass the + // world space normal as well. + if ( !fd.features[MFT_NormalMap] ) + res.numTexReg++; + } + + return res; +} + + +//**************************************************************************** +// Fog +//**************************************************************************** + +FogFeatHLSL::FogFeatHLSL() + : mFogDep( "shaders/common/torque.hlsl" ) +{ + addDependency( &mFogDep ); +} + +void FogFeatHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + + const bool vertexFog = Con::getBoolVariable( "$useVertexFog", false ); + if ( vertexFog || GFX->getPixelShaderVersion() < 3.0 ) + { + // Grab the eye position. + Var *eyePos = (Var*)LangElement::find( "eyePosWorld" ); + if ( !eyePos ) + { + eyePos = new Var( "eyePosWorld", "float3" ); + eyePos->uniform = true; + eyePos->constSortPos = cspPass; + } + + Var *fogData = new Var( "fogData", "float3" ); + fogData->uniform = true; + fogData->constSortPos = cspPass; + + Var *wsPosition = new Var( "fogPos", "float3" ); + getWsPosition( componentList, + fd.features[MFT_UseInstancing], + meta, + new DecOp( wsPosition ) ); + + // We pass the fog amount to the pixel shader. + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *fogAmount = connectComp->getElement( RT_TEXCOORD ); + fogAmount->setName( "fogAmount" ); + fogAmount->setStructName( "OUT" ); + fogAmount->setType( "float" ); + fogAmount->mapsToSampler = false; + + meta->addStatement( new GenOp( " @ = saturate( computeSceneFog( @, @, @.r, @.g, @.b ) );\r\n", + fogAmount, eyePos, wsPosition, fogData, fogData, fogData ) ); + } + else + { + // We fog in world space... make sure the world space + // position is passed to the pixel shader. This is + // often already passed for lighting, so it takes up + // no extra output registers. + addOutWsPosition( componentList, fd.features[MFT_UseInstancing], meta ); + } + + output = meta; +} + +void FogFeatHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + + Var *fogColor = new Var; + fogColor->setType( "float4" ); + fogColor->setName( "fogColor" ); + fogColor->uniform = true; + fogColor->constSortPos = cspPass; + + // Get the out color. + Var *color = (Var*) LangElement::find( "col" ); + if ( !color ) + { + color = new Var; + color->setType( "fragout" ); + color->setName( "col" ); + color->setStructName( "OUT" ); + } + + Var *fogAmount; + + const bool vertexFog = Con::getBoolVariable( "$useVertexFog", false ); + if ( vertexFog || GFX->getPixelShaderVersion() < 3.0 ) + { + // Per-vertex.... just get the fog amount. + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + fogAmount = connectComp->getElement( RT_TEXCOORD ); + fogAmount->setName( "fogAmount" ); + fogAmount->setStructName( "IN" ); + fogAmount->setType( "float" ); + } + else + { + Var *wsPosition = getInWsPosition( componentList ); + + // grab the eye position + Var *eyePos = (Var*)LangElement::find( "eyePosWorld" ); + if ( !eyePos ) + { + eyePos = new Var( "eyePosWorld", "float3" ); + eyePos->uniform = true; + eyePos->constSortPos = cspPass; + } + + Var *fogData = new Var( "fogData", "float3" ); + fogData->uniform = true; + fogData->constSortPos = cspPass; + + /// Get the fog amount. + fogAmount = new Var( "fogAmount", "float" ); + meta->addStatement( new GenOp( " @ = saturate( computeSceneFog( @, @, @.r, @.g, @.b ) );\r\n", + new DecOp( fogAmount ), eyePos, wsPosition, fogData, fogData, fogData ) ); + } + + // Lerp between the fog color and diffuse color. + LangElement *fogLerp = new GenOp( "lerp( @.rgb, @.rgb, @ )", fogColor, color, fogAmount ); + meta->addStatement( new GenOp( " @.rgb = @;\r\n", color, fogLerp ) ); + + output = meta; +} + +ShaderFeature::Resources FogFeatHLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + res.numTexReg = 1; + return res; +} + + +//**************************************************************************** +// Visibility +//**************************************************************************** + +VisibilityFeatHLSL::VisibilityFeatHLSL() + : mTorqueDep( "shaders/common/torque.hlsl" ) +{ + addDependency( &mTorqueDep ); +} + +void VisibilityFeatHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + output = meta; + + if ( fd.features[ MFT_UseInstancing ] ) + { + // We pass the visibility to the pixel shader via + // another output register. + // + // TODO: We should see if we can share this register + // with some other common instanced data. + // + ShaderConnector *conn = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outVisibility = conn->getElement( RT_TEXCOORD ); + outVisibility->setStructName( "OUT" ); + outVisibility->setName( "visibility" ); + outVisibility->setType( "float" ); + + ShaderConnector *vertStruct = dynamic_cast( componentList[C_VERT_STRUCT] ); + Var *instVisibility = vertStruct->getElement( RT_TEXCOORD, 1 ); + instVisibility->setStructName( "IN" ); + instVisibility->setName( "inst_visibility" ); + instVisibility->setType( "float" ); + mInstancingFormat->addElement( "visibility", GFXDeclType_Float, instVisibility->constNum ); + + meta->addStatement( new GenOp( " @ = @; // Instancing!\r\n", outVisibility, instVisibility ) ); + } + + if ( fd.features[ MFT_IsTranslucent ] ) + return; + + addOutVpos( meta, componentList ); +} + +void VisibilityFeatHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // Get the visibility constant. + Var *visibility = NULL; + if ( fd.features[ MFT_UseInstancing ] ) + visibility = getInTexCoord( "visibility", "float", false, componentList ); + else + { + visibility = (Var*)LangElement::find( "visibility" ); + + if ( !visibility ) + { + visibility = new Var(); + visibility->setType( "float" ); + visibility->setName( "visibility" ); + visibility->uniform = true; + visibility->constSortPos = cspPotentialPrimitive; + } + } + + MultiLine *meta = new MultiLine; + output = meta; + + // Translucent objects do a simple alpha fade. + if ( fd.features[ MFT_IsTranslucent ] ) + { + Var *color = (Var*)LangElement::find( "col" ); + meta->addStatement( new GenOp( " @.a *= @;\r\n", color, visibility ) ); + return; + } + + // Everything else does a fizzle. + Var *vPos = getInVpos( meta, componentList ); + meta->addStatement( new GenOp( " fizzle( @, @ );\r\n", vPos, visibility ) ); +} + +ShaderFeature::Resources VisibilityFeatHLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + + // TODO: Fix for instancing. + + if ( !fd.features[ MFT_IsTranslucent ] ) + res.numTexReg = 1; + + return res; +} + +//**************************************************************************** +// AlphaTest +//**************************************************************************** + +void AlphaTestHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // If we're below SM3 and don't have a depth output + // feature then don't waste an instruction here. + if ( GFX->getPixelShaderVersion() < 3.0 && + !fd.features[ MFT_EyeSpaceDepthOut ] && + !fd.features[ MFT_DepthOut ] ) + { + output = NULL; + return; + } + + // If we don't have a color var then we cannot do an alpha test. + Var *color = (Var*)LangElement::find( "col" ); + if ( !color ) + { + output = NULL; + return; + } + + // Now grab the alpha test value. + Var *alphaTestVal = new Var; + alphaTestVal->setType( "float" ); + alphaTestVal->setName( "alphaTestValue" ); + alphaTestVal->uniform = true; + alphaTestVal->constSortPos = cspPotentialPrimitive; + + // Do the clip. + output = new GenOp( " clip( @.a - @ );\r\n", color, alphaTestVal ); +} + + +//**************************************************************************** +// GlowMask +//**************************************************************************** + +void GlowMaskHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + output = NULL; + + // Get the output color... and make it black to mask out + // glow passes rendered before us. + // + // The shader compiler will optimize out all the other + // code above that doesn't contribute to the alpha mask. + Var *color = (Var*)LangElement::find( "col" ); + if ( color ) + output = new GenOp( " @.rgb = 0;\r\n", color ); +} + + +//**************************************************************************** +// RenderTargetZero +//**************************************************************************** + +void RenderTargetZeroHLSL::processPix( Vector &componentList, const MaterialFeatureData &fd ) +{ + // Do not actually assign zero, but instead a number so close to zero it may as well be zero. + // This will prevent a divide by zero causing an FP special on float render targets + output = new GenOp( " @;\r\n", assignColor( new GenOp( "0.00001" ), Material::None, NULL, mOutputTargetMask ) ); +} + + +//**************************************************************************** +// HDR Output +//**************************************************************************** + +HDROutHLSL::HDROutHLSL() + : mTorqueDep( "shaders/common/torque.hlsl" ) +{ + addDependency( &mTorqueDep ); +} + +void HDROutHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // Let the helper function do the work. + Var *color = (Var*)LangElement::find( "col" ); + if ( color ) + output = new GenOp( " @ = hdrEncode( @ );\r\n", color, color ); +} + +//**************************************************************************** +// FoliageFeatureHLSL +//**************************************************************************** + +#include "T3D/fx/groundCover.h" + +FoliageFeatureHLSL::FoliageFeatureHLSL() +: mDep( "shaders/common/foliage.hlsl" ) +{ + addDependency( &mDep ); +} + +void FoliageFeatureHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // Get the input variables we need. + + Var *inPosition = (Var*)LangElement::find( "inPosition" ); + if ( !inPosition ) + inPosition = (Var*)LangElement::find( "position" ); + + Var *inColor = (Var*)LangElement::find( "diffuse" ); + + Var *inParams = (Var*)LangElement::find( "texCoord" ); + + MultiLine *meta = new MultiLine; + + // Declare the normal and tangent variables since they do not exist + // in this vert type, but we do need to set them up for others. + + Var *normal = (Var*)LangElement::find( "normal" ); + AssertFatal( normal, "FoliageFeatureHLSL requires vert normal!" ); + + Var *tangent = new Var; + tangent->setType( "float3" ); + tangent->setName( "T" ); + LangElement *tangentDec = new DecOp( tangent ); + meta->addStatement( new GenOp( " @;\n", tangentDec ) ); + + // We add a float foliageFade to the OUT structure. + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *fade = connectComp->getElement( RT_TEXCOORD ); + fade->setName( "foliageFade" ); + fade->setStructName( "OUT" ); + fade->setType( "float" ); + + // grab the eye position + Var *eyePos = (Var*)LangElement::find( "eyePosWorld" ); + if ( !eyePos ) + { + eyePos = new Var( "eyePosWorld", "float3" ); + eyePos->uniform = true; + eyePos->constSortPos = cspPass; + } + + // All actual work is offloaded to this method. + meta->addStatement( new GenOp( " foliageProcessVert( @, @, @, @, @, @ );\r\n", inPosition, inColor, inParams, normal, tangent, eyePos ) ); + + // Assign to foliageFade. InColor.a was set to the correct value inside foliageProcessVert. + meta->addStatement( new GenOp( " @ = @.a;\r\n", fade, inColor ) ); + + output = meta; +} + +void FoliageFeatureHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // Find / create IN.foliageFade + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *fade = connectComp->getElement( RT_TEXCOORD ); + fade->setName( "foliageFade" ); + fade->setStructName( "IN" ); + fade->setType( "float" ); + + // Find / create visibility + Var *visibility = (Var*) LangElement::find( "visibility" ); + if ( !visibility ) + { + visibility = new Var(); + visibility->setType( "float" ); + visibility->setName( "visibility" ); + visibility->uniform = true; + visibility->constSortPos = cspPotentialPrimitive; + } + + MultiLine *meta = new MultiLine; + + // Multiply foliageFade into visibility. + meta->addStatement( new GenOp( " @ *= @;\r\n", visibility, fade ) ); + + output = meta; +} + +void FoliageFeatureHLSL::determineFeature( Material *material, const GFXVertexFormat *vertexFormat, U32 stageNum, const FeatureType &type, const FeatureSet &features, MaterialFeatureData *outFeatureData ) +{ + // This isn't really necessary since the outFeatureData will be filtered after + // this call. + if ( features.hasFeature( MFT_Foliage ) ) + outFeatureData->features.addFeature( type ); +} + + +ShaderFeatureConstHandles* FoliageFeatureHLSL::createConstHandles( GFXShader *shader, SimObject *userObject ) +{ + GroundCover *gcover = dynamic_cast< GroundCover* >( userObject ); + AssertFatal( gcover != NULL, "FoliageFeatureHLSL::createConstHandles - userObject was not valid!" ); + + GroundCoverShaderConstHandles *handles = new GroundCoverShaderConstHandles(); + handles->mGroundCover = gcover; + + handles->init( shader ); + + return handles; +} + + +void ParticleNormalFeatureHLSL::processVert(Vector &componentList, const MaterialFeatureData &fd) +{ + MultiLine *meta = new MultiLine; + output = meta; + + // Calculate normal and tangent values since we want to keep particle verts + // as light-weight as possible + + Var *normal = (Var*) LangElement::find("normal"); + if(normal == NULL) + { + normal = new Var; + normal->setType( "float3" ); + normal->setName( "normal" ); + + // These values are not accidental. It is slightly adjusted from facing straight into the + // screen because there is a discontinuity at (0, 1, 0) for gbuffer encoding. Do not + // cause this value to be (0, -1, 0) or interlaced normals will be discontinuous. + // [11/23/2009 Pat] + meta->addStatement(new GenOp(" @ = float3(0.0, -0.97, 0.14);\r\n", new DecOp(normal))); + } + + Var *T = (Var*) LangElement::find( "T" ); + if(T == NULL) + { + T = new Var; + T->setType( "float3" ); + T->setName( "T" ); + meta->addStatement(new GenOp(" @ = float3(0.0, 0.0, -1.0);\r\n", new DecOp(T))); + } +} + +//**************************************************************************** +// ImposterVertFeatureHLSL +//**************************************************************************** + +ImposterVertFeatureHLSL::ImposterVertFeatureHLSL() + : mDep( "shaders/common/imposter.hlsl" ) +{ + addDependency( &mDep ); +} + +void ImposterVertFeatureHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + output = meta; + + // Get the input vertex variables. + Var *inPosition = (Var*)LangElement::find( "position" ); + Var *inMiscParams = (Var*)LangElement::find( "tcImposterParams" ); + Var *inUpVec = (Var*)LangElement::find( "tcImposterUpVec" ); + Var *inRightVec = (Var*)LangElement::find( "tcImposterRightVec" ); + + // Get the input shader constants. + Var *imposterLimits = new Var; + imposterLimits->setType( "float4" ); + imposterLimits->setName( "imposterLimits" ); + imposterLimits->uniform = true; + imposterLimits->constSortPos = cspPotentialPrimitive; + + Var *imposterUVs = new Var; + imposterUVs->setType( "float4" ); + imposterUVs->setName( "imposterUVs" ); + imposterUVs->arraySize = 64; // See imposter.hlsl + imposterUVs->uniform = true; + imposterUVs->constSortPos = cspPotentialPrimitive; + + Var *eyePos = (Var*)LangElement::find( "eyePosWorld" ); + if ( !eyePos ) + { + eyePos = new Var( "eyePosWorld", "float3" ); + eyePos->uniform = true; + eyePos->constSortPos = cspPass; + } + + // Declare the outputs from this feature. + Var *outInPosition = new Var; + outInPosition->setType( "float3" ); + outInPosition->setName( "inPosition" ); + meta->addStatement( new GenOp( " @;\r\n", new DecOp( outInPosition ) ) ); + + Var *outTexCoord = new Var; + outTexCoord->setType( "float2" ); + outTexCoord->setName( "texCoord" ); + meta->addStatement( new GenOp( " @;\r\n", new DecOp( outTexCoord ) ) ); + + Var *outWorldToTangent = new Var; + outWorldToTangent->setType( "float3x3" ); + outWorldToTangent->setName( "worldToTangent" ); + meta->addStatement( new GenOp( " @;\r\n", new DecOp( outWorldToTangent ) ) ); + + // Add imposterFade to the OUT structure. + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outFade = connectComp->getElement( RT_TEXCOORD ); + outFade->setName( "imposterFade" ); + outFade->setStructName( "OUT" ); + outFade->setType( "float" ); + + // Assign OUT.imposterFade + meta->addStatement( new GenOp( " @ = @.y;\r\n", outFade, inMiscParams ) ); + + // All actual work is done in this method. + meta->addStatement( new GenOp( " imposter_v( @.xyz, @.w, @.x * length(@), normalize(@), normalize(@), @.y, @.x, @.z, @.w, @, @, @, @, @ );\r\n", + + inPosition, + inPosition, + + inMiscParams, + inRightVec, + + inUpVec, + inRightVec, + + imposterLimits, + imposterLimits, + imposterLimits, + imposterLimits, + + eyePos, + imposterUVs, + + outInPosition, + outTexCoord, + outWorldToTangent ) ); + + // Copy the position to wsPosition for use in shaders + // down stream instead of looking for objTrans. + Var *wsPosition = new Var; + wsPosition->setType( "float3" ); + wsPosition->setName( "wsPosition" ); + meta->addStatement( new GenOp( " @ = @.xyz;\r\n", new DecOp( wsPosition ), outInPosition ) ); + + // If we new viewToTangent... its the same as the + // world to tangent for an imposter. + Var *viewToTangent = new Var; + viewToTangent->setType( "float3x3" ); + viewToTangent->setName( "viewToTangent" ); + meta->addStatement( new GenOp( " @ = @;\r\n", new DecOp( viewToTangent ), outWorldToTangent ) ); +} + +void ImposterVertFeatureHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // Find / create IN.imposterFade + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *fade = connectComp->getElement( RT_TEXCOORD ); + fade->setName( "imposterFade" ); + fade->setStructName( "IN" ); + fade->setType( "float" ); + + // Find / create visibility + Var *visibility = (Var*) LangElement::find( "visibility" ); + if ( !visibility ) + { + visibility = new Var(); + visibility->setType( "float" ); + visibility->setName( "visibility" ); + visibility->uniform = true; + visibility->constSortPos = cspPotentialPrimitive; + } + + MultiLine *meta = new MultiLine; + + // Multiply foliageFade into visibility. + meta->addStatement( new GenOp( " @ *= @;\r\n", visibility, fade ) ); + + output = meta; +} + +void ImposterVertFeatureHLSL::determineFeature( Material *material, + const GFXVertexFormat *vertexFormat, + U32 stageNum, + const FeatureType &type, + const FeatureSet &features, + MaterialFeatureData *outFeatureData ) +{ + if ( features.hasFeature( MFT_ImposterVert ) ) + outFeatureData->features.addFeature( MFT_ImposterVert ); +} + diff --git a/Engine/source/shaderGen/HLSL/shaderFeatureHLSL.h b/Engine/source/shaderGen/HLSL/shaderFeatureHLSL.h new file mode 100644 index 000000000..3c2fc821e --- /dev/null +++ b/Engine/source/shaderGen/HLSL/shaderFeatureHLSL.h @@ -0,0 +1,654 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _SHADERGEN_HLSL_SHADERFEATUREHLSL_H_ +#define _SHADERGEN_HLSL_SHADERFEATUREHLSL_H_ + +#ifndef _SHADERFEATURE_H_ +#include "shaderGen/shaderFeature.h" +#endif + +struct LangElement; +struct MaterialFeatureData; +struct RenderPassData; + + +class ShaderFeatureHLSL : public ShaderFeature +{ +public: + ShaderFeatureHLSL(); + + /// + Var* getOutTexCoord( const char *name, + const char *type, + bool mapsToSampler, + bool useTexAnim, + MultiLine *meta, + Vector &componentList ); + + /// Returns an input texture coord by name adding it + /// to the input connector if it doesn't exist. + static Var* getInTexCoord( const char *name, + const char *type, + bool mapsToSampler, + Vector &componentList ); + + static Var* getInColor( const char *name, + const char *type, + Vector &componentList ); + + /// + static Var* addOutVpos( MultiLine *meta, + Vector &componentList ); + + /// Returns the VPOS input register for the pixel shader. + static Var* getInVpos( MultiLine *meta, + Vector &componentList ); + + /// Returns the "objToTangentSpace" transform or creates one if this + /// is the first feature to need it. + Var* getOutObjToTangentSpace( Vector &componentList, + MultiLine *meta, + const MaterialFeatureData &fd ); + + /// Returns the existing output "outWorldToTangent" transform or + /// creates one if this is the first feature to need it. + Var* getOutWorldToTangent( Vector &componentList, + MultiLine *meta, + const MaterialFeatureData &fd ); + + /// Returns the input "worldToTanget" space transform + /// adding it to the input connector if it doesn't exist. + static Var* getInWorldToTangent( Vector &componentList ); + + /// Returns the existing output "outViewToTangent" transform or + /// creates one if this is the first feature to need it. + Var* getOutViewToTangent( Vector &componentList, + MultiLine *meta, + const MaterialFeatureData &fd ); + + /// Returns the input "viewToTangent" space transform + /// adding it to the input connector if it doesn't exist. + static Var* getInViewToTangent( Vector &componentList ); + + /// Calculates the world space position in the vertex shader and + /// assigns it to the passed language element. It does not pass + /// it across the connector to the pixel shader. + /// @see addOutWsPosition + void getWsPosition( Vector &componentList, + bool useInstancing, + MultiLine *meta, + LangElement *wsPosition ); + + /// Adds the "wsPosition" to the input connector if it doesn't exist. + Var* addOutWsPosition( Vector &componentList, + bool useInstancing, + MultiLine *meta ); + + /// Returns the input world space position from the connector. + static Var* getInWsPosition( Vector &componentList ); + + /// Returns the world space view vector from the wsPosition. + static Var* getWsView( Var *wsPosition, MultiLine *meta ); + + /// Returns the input normal map texture. + static Var* getNormalMapTex(); + + /// + Var* addOutDetailTexCoord( Vector &componentList, + MultiLine *meta, + bool useTexAnim ); + + /// + Var* getObjTrans( Vector &componentList, + bool useInstancing, + MultiLine *meta ); + + /// + Var* getModelView( Vector &componentList, + bool useInstancing, + MultiLine *meta ); + + /// + Var* getWorldView( Vector &componentList, + bool useInstancing, + MultiLine *meta ); + + /// + Var* getInvWorldView( Vector &componentList, + bool useInstancing, + MultiLine *meta ); + + // ShaderFeature + Var* getVertTexCoord( const String &name ); + LangElement* setupTexSpaceMat( Vector &componentList, Var **texSpaceMat ); + LangElement* assignColor( LangElement *elem, Material::BlendOp blend, LangElement *lerpElem = NULL, ShaderFeature::OutputTarget outputTarget = ShaderFeature::DefaultTarget ); + LangElement* expandNormalMap( LangElement *sampleNormalOp, LangElement *normalDecl, LangElement *normalVar, const MaterialFeatureData &fd ); +}; + + +class NamedFeatureHLSL : public ShaderFeatureHLSL +{ +protected: + String mName; + +public: + NamedFeatureHLSL( const String &name ) + : mName( name ) + {} + + virtual String getName() { return mName; } +}; + +class RenderTargetZeroHLSL : public ShaderFeatureHLSL +{ +protected: + ShaderFeature::OutputTarget mOutputTargetMask; + String mFeatureName; + +public: + RenderTargetZeroHLSL( const ShaderFeature::OutputTarget target ) + : mOutputTargetMask( target ) + { + char buffer[256]; + dSprintf(buffer, sizeof(buffer), "Render Target Output = 0.0, output mask %04b", mOutputTargetMask); + mFeatureName = buffer; + } + + virtual String getName() { return mFeatureName; } + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual U32 getOutputTargets( const MaterialFeatureData &fd ) const { return mOutputTargetMask; } +}; + + +/// Vertex position +class VertPositionHLSL : public ShaderFeatureHLSL +{ +public: + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual String getName() + { + return "Vert Position"; + } + + virtual void determineFeature( Material *material, + const GFXVertexFormat *vertexFormat, + U32 stageNum, + const FeatureType &type, + const FeatureSet &features, + MaterialFeatureData *outFeatureData ); + +}; + + +/// Vertex lighting based on the normal and the light +/// direction passed through the vertex color. +class RTLightingFeatHLSL : public ShaderFeatureHLSL +{ +protected: + + ShaderIncludeDependency mDep; + +public: + + RTLightingFeatHLSL(); + + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp(){ return Material::None; } + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual String getName() + { + return "RT Lighting"; + } +}; + + +/// Base texture +class DiffuseMapFeatHLSL : public ShaderFeatureHLSL +{ +public: + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp(){ return Material::LerpAlpha; } + + virtual Resources getResources( const MaterialFeatureData &fd ); + + // Sets textures and texture flags for current pass + virtual void setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ); + + virtual String getName() + { + return "Base Texture"; + } +}; + + +/// Overlay texture +class OverlayTexFeatHLSL : public ShaderFeatureHLSL +{ +public: + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp(){ return Material::LerpAlpha; } + + virtual Resources getResources( const MaterialFeatureData &fd ); + + // Sets textures and texture flags for current pass + virtual void setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ); + + virtual String getName() + { + return "Overlay Texture"; + } +}; + + +/// Diffuse color +class DiffuseFeatureHLSL : public ShaderFeatureHLSL +{ +public: + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp(){ return Material::None; } + + virtual String getName() + { + return "Diffuse Color"; + } +}; + +/// Diffuse vertex color +class DiffuseVertColorFeatureHLSL : public ShaderFeatureHLSL +{ +public: + + virtual void processVert( Vector< ShaderComponent* >& componentList, + const MaterialFeatureData& fd ); + virtual void processPix( Vector< ShaderComponent* >&componentList, + const MaterialFeatureData& fd ); + + virtual Material::BlendOp getBlendOp(){ return Material::None; } + + virtual String getName() + { + return "Diffuse Vertex Color"; + } +}; + +/// Lightmap +class LightmapFeatHLSL : public ShaderFeatureHLSL +{ +public: + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp(){ return Material::LerpAlpha; } + + virtual Resources getResources( const MaterialFeatureData &fd ); + + // Sets textures and texture flags for current pass + virtual void setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ); + + virtual String getName() + { + return "Lightmap"; + } + + virtual U32 getOutputTargets( const MaterialFeatureData &fd ) const; +}; + + +/// Tonemap +class TonemapFeatHLSL : public ShaderFeatureHLSL +{ +public: + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp(){ return Material::LerpAlpha; } + + virtual Resources getResources( const MaterialFeatureData &fd ); + + // Sets textures and texture flags for current pass + virtual void setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ); + + virtual String getName() + { + return "Tonemap"; + } + + virtual U32 getOutputTargets( const MaterialFeatureData &fd ) const; +}; + + +/// Baked lighting stored on the vertex color +class VertLitHLSL : public ShaderFeatureHLSL +{ +public: + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp(){ return Material::None; } + + virtual String getName() + { + return "Vert Lit"; + } + + virtual U32 getOutputTargets( const MaterialFeatureData &fd ) const; +}; + + +/// Detail map +class DetailFeatHLSL : public ShaderFeatureHLSL +{ +public: + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp(){ return Material::Mul; } + + // Sets textures and texture flags for current pass + virtual void setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ); + + virtual String getName() + { + return "Detail"; + } +}; + + +/// Reflect Cubemap +class ReflectCubeFeatHLSL : public ShaderFeatureHLSL +{ +public: + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Resources getResources( const MaterialFeatureData &fd ); + + // Sets textures and texture flags for current pass + virtual void setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ); + + virtual String getName() + { + return "Reflect Cube"; + } +}; + + +/// Fog +class FogFeatHLSL : public ShaderFeatureHLSL +{ +protected: + + ShaderIncludeDependency mFogDep; + +public: + FogFeatHLSL(); + + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp() { return Material::LerpAlpha; } + + virtual String getName() + { + return "Fog"; + } +}; + + +/// Tex Anim +class TexAnimHLSL : public ShaderFeatureHLSL +{ +public: + virtual Material::BlendOp getBlendOp() { return Material::None; } + + virtual String getName() + { + return "Texture Animation"; + } +}; + + +/// Visibility +class VisibilityFeatHLSL : public ShaderFeatureHLSL +{ +protected: + + ShaderIncludeDependency mTorqueDep; + +public: + + VisibilityFeatHLSL(); + + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp() { return Material::None; } + + virtual String getName() + { + return "Visibility"; + } +}; + + +/// +class AlphaTestHLSL : public ShaderFeatureHLSL +{ +public: + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp() { return Material::None; } + + virtual String getName() + { + return "Alpha Test"; + } +}; + + +/// Special feature used to mask out the RGB color for +/// non-glow passes of glow materials. +/// @see RenderGlowMgr +class GlowMaskHLSL : public ShaderFeatureHLSL +{ +public: + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp() { return Material::None; } + + virtual String getName() + { + return "Glow Mask"; + } +}; + +/// This should be the final feature on most pixel shaders which +/// encodes the color for the current HDR target format. +/// @see HDRPostFx +/// @see LightManager +/// @see torque.hlsl +class HDROutHLSL : public ShaderFeatureHLSL +{ +protected: + + ShaderIncludeDependency mTorqueDep; + +public: + + HDROutHLSL(); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Material::BlendOp getBlendOp() { return Material::None; } + + virtual String getName() { return "HDR Output"; } +}; + +/// +class FoliageFeatureHLSL : public ShaderFeatureHLSL +{ +protected: + + ShaderIncludeDependency mDep; + +public: + + FoliageFeatureHLSL(); + + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual String getName() + { + return "Foliage Feature"; + } + + virtual void determineFeature( Material *material, + const GFXVertexFormat *vertexFormat, + U32 stageNum, + const FeatureType &type, + const FeatureSet &features, + MaterialFeatureData *outFeatureData ); + + virtual ShaderFeatureConstHandles* createConstHandles( GFXShader *shader, SimObject *userObject ); +}; + +class ParticleNormalFeatureHLSL : public ShaderFeatureHLSL +{ +public: + + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual String getName() + { + return "Particle Normal Generation Feature"; + } + +}; + + +/// Special feature for unpacking imposter verts. +/// @see RenderImposterMgr +class ImposterVertFeatureHLSL : public ShaderFeatureHLSL +{ +protected: + + ShaderIncludeDependency mDep; + +public: + + ImposterVertFeatureHLSL(); + + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual String getName() { return "Imposter Vert"; } + + virtual void determineFeature( Material *material, + const GFXVertexFormat *vertexFormat, + U32 stageNum, + const FeatureType &type, + const FeatureSet &features, + MaterialFeatureData *outFeatureData ); +}; + + +#endif // _SHADERGEN_HLSL_SHADERFEATUREHLSL_H_ diff --git a/Engine/source/shaderGen/HLSL/shaderGenHLSL.cpp b/Engine/source/shaderGen/HLSL/shaderGenHLSL.cpp new file mode 100644 index 000000000..23ca8137d --- /dev/null +++ b/Engine/source/shaderGen/HLSL/shaderGenHLSL.cpp @@ -0,0 +1,199 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "shaderGen/HLSL/shaderGenHLSL.h" + +#include "shaderGen/HLSL/shaderCompHLSL.h" +#include "shaderGen/featureMgr.h" + + +void ShaderGenPrinterHLSL::printShaderHeader(Stream& stream) +{ + const char *header1 = "//*****************************************************************************\r\n"; + const char *header2 = "// Torque -- HLSL procedural shader\r\n"; + + stream.write( dStrlen(header1), header1 ); + stream.write( dStrlen(header2), header2 ); + stream.write( dStrlen(header1), header1 ); + + const char* header3 = "\r\n"; + stream.write( dStrlen(header3), header3 ); +} + +void ShaderGenPrinterHLSL::printMainComment(Stream& stream) +{ + const char * header5 = "// Main\r\n"; + const char * line = "//-----------------------------------------------------------------------------\r\n"; + + stream.write( dStrlen(line), line ); + stream.write( dStrlen(header5), header5 ); + stream.write( dStrlen(line), line ); +} + +void ShaderGenPrinterHLSL::printVertexShaderCloser(Stream& stream) +{ + const char *closer = " return OUT;\r\n}\r\n"; + stream.write( dStrlen(closer), closer ); +} + +void ShaderGenPrinterHLSL::printPixelShaderOutputStruct(Stream& stream, const MaterialFeatureData &featureData) +{ + // Determine the number of output targets we need + U32 numMRTs = 0; + for( U32 i = 0; i < FEATUREMGR->getFeatureCount(); i++ ) + { + const FeatureInfo &info = FEATUREMGR->getAt( i ); + if( featureData.features.hasFeature( *info.type ) ) + numMRTs |= info.feature->getOutputTargets( featureData ); + } + + WRITESTR( "struct Fragout\r\n" ); + WRITESTR( "{\r\n" ); + WRITESTR( " float4 col : COLOR0;\r\n" ); + for( U32 i = 1; i < 4; i++ ) + { + if( numMRTs & 1 << i ) + WRITESTR( avar( " float4 col%d : COLOR%d;\r\n", i, i ) ); + } + WRITESTR( "};\r\n" ); + WRITESTR( "\r\n" ); + WRITESTR( "\r\n" ); +} + +void ShaderGenPrinterHLSL::printPixelShaderCloser(Stream& stream) +{ + WRITESTR( "\r\n return OUT;\r\n}\r\n" ); +} + +void ShaderGenPrinterHLSL::printLine(Stream& stream, const String& line) +{ + stream.write(line.length(), line.c_str()); + const char* end = "\r\n"; + stream.write(dStrlen(end), end); +} + +const char* ShaderGenComponentFactoryHLSL::typeToString( GFXDeclType type ) +{ + switch ( type ) + { + default: + case GFXDeclType_Float: + return "float"; + + case GFXDeclType_Float2: + return "float2"; + + case GFXDeclType_Float3: + return "float3"; + + case GFXDeclType_Float4: + case GFXDeclType_Color: + return "float4"; + } +} + +ShaderComponent* ShaderGenComponentFactoryHLSL::createVertexInputConnector( const GFXVertexFormat &vertexFormat ) +{ + ShaderConnectorHLSL *vertComp = new ShaderConnectorHLSL; + vertComp->setName( "VertData" ); + + // Loop thru the vertex format elements. + for ( U32 i=0; i < vertexFormat.getElementCount(); i++ ) + { + const GFXVertexElement &element = vertexFormat.getElement( i ); + + Var *var = NULL; + + if ( element.isSemantic( GFXSemantic::POSITION ) ) + { + var = vertComp->getElement( RT_POSITION ); + var->setName( "position" ); + } + else if ( element.isSemantic( GFXSemantic::NORMAL ) ) + { + var = vertComp->getElement( RT_NORMAL ); + var->setName( "normal" ); + } + else if ( element.isSemantic( GFXSemantic::TANGENT ) ) + { + var = vertComp->getElement( RT_TANGENT ); + var->setName( "T" ); + } + else if ( element.isSemantic( GFXSemantic::TANGENTW ) ) + { + var = vertComp->getIndexedElement( element.getSemanticIndex(), RT_TEXCOORD ); + var->setName( "tangentW" ); + } + else if ( element.isSemantic( GFXSemantic::BINORMAL ) ) + { + var = vertComp->getElement( RT_BINORMAL ); + var->setName( "B" ); + } + else if ( element.isSemantic( GFXSemantic::COLOR ) ) + { + var = vertComp->getElement( RT_COLOR ); + var->setName( "diffuse" ); + } + else if ( element.isSemantic( GFXSemantic::TEXCOORD ) ) + { + var = vertComp->getIndexedElement( element.getSemanticIndex(), RT_TEXCOORD ); + if ( element.getSemanticIndex() == 0 ) + var->setName( "texCoord" ); + else + var->setName( String::ToString( "texCoord%d", element.getSemanticIndex() + 1 ) ); + } + else + { + // Everything else is a texcoord! + var = vertComp->getIndexedElement( element.getSemanticIndex(), RT_TEXCOORD ); + var->setName( "tc" + element.getSemantic() ); + } + + if ( !var ) + continue; + + var->setStructName( "IN" ); + var->setType( typeToString( element.getType() ) ); + } + + return vertComp; +} + +ShaderComponent* ShaderGenComponentFactoryHLSL::createVertexPixelConnector() +{ + ShaderComponent* comp = new ShaderConnectorHLSL; + ((ShaderConnector*)comp)->setName("ConnectData"); + return comp; +} + +ShaderComponent* ShaderGenComponentFactoryHLSL::createVertexParamsDef() +{ + ShaderComponent* comp = new VertexParamsDefHLSL; + return comp; +} + +ShaderComponent* ShaderGenComponentFactoryHLSL::createPixelParamsDef() +{ + ShaderComponent* comp = new PixelParamsDefHLSL; + return comp; +} diff --git a/Engine/source/shaderGen/HLSL/shaderGenHLSL.h b/Engine/source/shaderGen/HLSL/shaderGenHLSL.h new file mode 100644 index 000000000..c14354f39 --- /dev/null +++ b/Engine/source/shaderGen/HLSL/shaderGenHLSL.h @@ -0,0 +1,60 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SHADERGEN_HLSL_H_ +#define _SHADERGEN_HLSL_H_ + +#ifndef _SHADERGEN_H_ +#include "shaderGen/shaderGen.h" +#endif + + +class ShaderGenPrinterHLSL : public ShaderGenPrinter +{ +public: + + // ShaderGenPrinter + virtual void printShaderHeader(Stream& stream); + virtual void printMainComment(Stream& stream); + virtual void printVertexShaderCloser(Stream& stream); + virtual void printPixelShaderOutputStruct(Stream& stream, const MaterialFeatureData &featureData); + virtual void printPixelShaderCloser(Stream& stream); + virtual void printLine(Stream& stream, const String& line); +}; + +class ShaderGenComponentFactoryHLSL : public ShaderGenComponentFactory +{ +public: + + /// Helper function for converting a vertex decl + /// type to an HLSL type string. + static const char* typeToString( GFXDeclType type ); + + // ShaderGenComponentFactory + virtual ShaderComponent* createVertexInputConnector( const GFXVertexFormat &vertexFormat ); + virtual ShaderComponent* createVertexPixelConnector(); + virtual ShaderComponent* createVertexParamsDef(); + virtual ShaderComponent* createPixelParamsDef(); +}; + + +#endif // _SHADERGEN_HLSL_H_ \ No newline at end of file diff --git a/Engine/source/shaderGen/HLSL/shaderGenHLSLInit.cpp b/Engine/source/shaderGen/HLSL/shaderGenHLSLInit.cpp new file mode 100644 index 000000000..3b2e34c41 --- /dev/null +++ b/Engine/source/shaderGen/HLSL/shaderGenHLSLInit.cpp @@ -0,0 +1,110 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "shaderGen/shaderGen.h" +#include "shaderGen/HLSL/shaderGenHLSL.h" +#include "shaderGen/HLSL/shaderFeatureHLSL.h" +#include "shaderGen/featureMgr.h" +#include "shaderGen/HLSL/bumpHLSL.h" +#include "shaderGen/HLSL/pixSpecularHLSL.h" +#include "shaderGen/HLSL/depthHLSL.h" +#include "shaderGen/HLSL/paraboloidHLSL.h" +#include "materials/materialFeatureTypes.h" +#include "core/module.h" + + +static ShaderGen::ShaderGenInitDelegate sInitDelegate; + +void _initShaderGenHLSL( ShaderGen *shaderGen ) +{ + shaderGen->setPrinter( new ShaderGenPrinterHLSL ); + shaderGen->setComponentFactory( new ShaderGenComponentFactoryHLSL ); + shaderGen->setFileEnding( "hlsl" ); + + FEATUREMGR->registerFeature( MFT_VertTransform, new VertPositionHLSL ); + FEATUREMGR->registerFeature( MFT_RTLighting, new RTLightingFeatHLSL ); + FEATUREMGR->registerFeature( MFT_IsDXTnm, new NamedFeatureHLSL( "DXTnm" ) ); + FEATUREMGR->registerFeature( MFT_TexAnim, new TexAnimHLSL ); + FEATUREMGR->registerFeature( MFT_DiffuseMap, new DiffuseMapFeatHLSL ); + FEATUREMGR->registerFeature( MFT_OverlayMap, new OverlayTexFeatHLSL ); + FEATUREMGR->registerFeature( MFT_DiffuseColor, new DiffuseFeatureHLSL ); + FEATUREMGR->registerFeature( MFT_DiffuseVertColor, new DiffuseVertColorFeatureHLSL ); + FEATUREMGR->registerFeature( MFT_AlphaTest, new AlphaTestHLSL ); + FEATUREMGR->registerFeature( MFT_GlowMask, new GlowMaskHLSL ); + FEATUREMGR->registerFeature( MFT_LightMap, new LightmapFeatHLSL ); + FEATUREMGR->registerFeature( MFT_ToneMap, new TonemapFeatHLSL ); + FEATUREMGR->registerFeature( MFT_VertLit, new VertLitHLSL ); + FEATUREMGR->registerFeature( MFT_Parallax, new ParallaxFeatHLSL ); + FEATUREMGR->registerFeature( MFT_NormalMap, new BumpFeatHLSL ); + FEATUREMGR->registerFeature( MFT_DetailNormalMap, new NamedFeatureHLSL( "Detail Normal Map" ) ); + FEATUREMGR->registerFeature( MFT_DetailMap, new DetailFeatHLSL ); + FEATUREMGR->registerFeature( MFT_CubeMap, new ReflectCubeFeatHLSL ); + FEATUREMGR->registerFeature( MFT_PixSpecular, new PixelSpecularHLSL ); + FEATUREMGR->registerFeature( MFT_IsTranslucent, new NamedFeatureHLSL( "Translucent" ) ); + FEATUREMGR->registerFeature( MFT_IsTranslucentZWrite, new NamedFeatureHLSL( "Translucent ZWrite" ) ); + FEATUREMGR->registerFeature( MFT_Visibility, new VisibilityFeatHLSL ); + FEATUREMGR->registerFeature( MFT_Fog, new FogFeatHLSL ); + FEATUREMGR->registerFeature( MFT_SpecularMap, new SpecularMapHLSL ); + FEATUREMGR->registerFeature( MFT_GlossMap, new NamedFeatureHLSL( "Gloss Map" ) ); + FEATUREMGR->registerFeature( MFT_LightbufferMRT, new NamedFeatureHLSL( "Lightbuffer MRT" ) ); + FEATUREMGR->registerFeature( MFT_RenderTarget1_Zero, new RenderTargetZeroHLSL( ShaderFeature::RenderTarget1 ) ); + + FEATUREMGR->registerFeature( MFT_DiffuseMapAtlas, new NamedFeatureHLSL( "Diffuse Map Atlas" ) ); + FEATUREMGR->registerFeature( MFT_NormalMapAtlas, new NamedFeatureHLSL( "Normal Map Atlas" ) ); + + FEATUREMGR->registerFeature( MFT_NormalsOut, new NormalsOutFeatHLSL ); + + FEATUREMGR->registerFeature( MFT_DepthOut, new DepthOutHLSL ); + FEATUREMGR->registerFeature( MFT_EyeSpaceDepthOut, new EyeSpaceDepthOutHLSL() ); + + FEATUREMGR->registerFeature( MFT_HDROut, new HDROutHLSL ); + + FEATUREMGR->registerFeature( MFT_ParaboloidVertTransform, new ParaboloidVertTransformHLSL ); + FEATUREMGR->registerFeature( MFT_IsSinglePassParaboloid, new NamedFeatureHLSL( "Single Pass Paraboloid" ) ); + FEATUREMGR->registerFeature( MFT_UseInstancing, new NamedFeatureHLSL( "Hardware Instancing" ) ); + + FEATUREMGR->registerFeature( MFT_Foliage, new FoliageFeatureHLSL ); + + FEATUREMGR->registerFeature( MFT_ParticleNormal, new ParticleNormalFeatureHLSL ); + + FEATUREMGR->registerFeature( MFT_InterlacedPrePass, new NamedFeatureHLSL( "Interlaced Pre Pass" ) ); + + FEATUREMGR->registerFeature( MFT_ForwardShading, new NamedFeatureHLSL( "Forward Shaded Material" ) ); + + FEATUREMGR->registerFeature( MFT_ImposterVert, new ImposterVertFeatureHLSL ); +} + +MODULE_BEGIN( ShaderGenHLSL ) + + MODULE_INIT_AFTER( ShaderGen ) + MODULE_INIT_AFTER( ShaderGenFeatureMgr ) + + MODULE_INIT + { + sInitDelegate.bind(_initShaderGenHLSL); + SHADERGEN->registerInitDelegate(Direct3D9, sInitDelegate); + SHADERGEN->registerInitDelegate(Direct3D9_360, sInitDelegate); + } + +MODULE_END; diff --git a/Engine/source/shaderGen/conditionerFeature.cpp b/Engine/source/shaderGen/conditionerFeature.cpp new file mode 100644 index 000000000..8f7888869 --- /dev/null +++ b/Engine/source/shaderGen/conditionerFeature.cpp @@ -0,0 +1,241 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "shaderGen/conditionerFeature.h" + +#include "shaderGen/shaderOp.h" +#include "shaderGen/featureMgr.h" +#include "gfx/gfxDevice.h" +#include "gfx/gfxStringEnumTranslate.h" +#include "core/stream/fileStream.h" +#include "materials/shaderData.h" +#include "core/util/safeDelete.h" + +const String ConditionerFeature::ConditionerIncludeFileName = "autogenConditioners.h"; + +bool ConditionerFeature::smDirtyConditioners = true; + +Vector ConditionerFeature::smConditioners; + + +ConditionerFeature::ConditionerFeature( const GFXFormat bufferFormat ) + : mBufferFormat(bufferFormat) +{ + dMemset( mMethodDependency, 0, sizeof( mMethodDependency ) ); + + smConditioners.push_back( this ); + smDirtyConditioners = true; +} + +ConditionerFeature::~ConditionerFeature() +{ + for( U32 i = 0; i < NumMethodTypes; i++ ) + SAFE_DELETE( mMethodDependency[i] ); + + smConditioners.remove( this ); + smDirtyConditioners = true; +} + +LangElement *ConditionerFeature::assignOutput( Var *unconditionedOutput, ShaderFeature::OutputTarget outputTarget /* = ShaderFeature::DefaultTarget*/ ) +{ + LangElement *assign; + MultiLine *meta = new MultiLine; + + meta->addStatement( new GenOp( avar( "\r\n\r\n // output buffer format: %s\r\n", GFXStringTextureFormat[getBufferFormat()] ) ) ); + + // condition the output + Var *conditionedOutput = _conditionOutput( unconditionedOutput, meta ); + + // search for color var + Var *color = (Var*) LangElement::find( getOutputTargetVarName(outputTarget) ); + + if ( !color ) + { + // create color var + color = new Var; + + if(GFX->getAdapterType() == OpenGL) + { + color->setName( getOutputTargetVarName(outputTarget) ); + color->setType( "vec4" ); + DecOp* colDecl = new DecOp(color); + + assign = new GenOp( "@ = vec4(@)", colDecl, conditionedOutput ); + } + else + { + color->setType( "fragout" ); + color->setName( getOutputTargetVarName(outputTarget) ); + color->setStructName( "OUT" ); + + assign = new GenOp( "@ = @", color, conditionedOutput ); + } + } + else + { + if (GFX->getAdapterType() == OpenGL) + assign = new GenOp( "@ = vec4(@)", color, conditionedOutput); + else + assign = new GenOp( "@ = @", color, conditionedOutput ); + } + + meta->addStatement( new GenOp( " @;\r\n", assign ) ); + + return meta; +} + +Var *ConditionerFeature::_conditionOutput( Var *unconditionedOutput, MultiLine *meta ) +{ + meta->addStatement( new GenOp( " // generic conditioner: no conditioning performed\r\n" ) ); + return unconditionedOutput; +} + +Var *ConditionerFeature::_unconditionInput( Var *conditionedInput, MultiLine *meta ) +{ + meta->addStatement( new GenOp( " // generic conditioner: no conditioning performed\r\n" ) ); + return conditionedInput; +} + +const String &ConditionerFeature::getShaderMethodName( MethodType methodType ) +{ + if ( mConditionMethodName.isEmpty() ) + { + const U32 hash = getName().getHashCaseInsensitive(); + mUnconditionMethodName = avar("autogen%s_%08x", "Uncondition", hash ); + mConditionMethodName = avar("autogen%s_%08x", "Condition", hash ); + } + + return methodType == UnconditionMethod ? mUnconditionMethodName : mConditionMethodName; +} + +ConditionerMethodDependency* ConditionerFeature::getConditionerMethodDependency( MethodType methodType ) +{ + if ( mMethodDependency[methodType] == NULL ) + mMethodDependency[methodType] = new ConditionerMethodDependency( this, methodType ); + + return mMethodDependency[methodType]; +} + +void ConditionerFeature::_print( Stream *stream ) +{ + _printMethod( ConditionMethod, getShaderMethodName( ConditionMethod ), *stream ); + LangElement::deleteElements(); + + _printMethod( UnconditionMethod, getShaderMethodName( UnconditionMethod ), *stream ); + LangElement::deleteElements(); +} + +void ConditionerFeature::_updateConditioners() +{ + smDirtyConditioners = false; + + String includePath = "shadergen:/" + ConditionerIncludeFileName; + + FileStream stream; + if ( !stream.open( includePath, Torque::FS::File::Write ) ) + return; + + for ( U32 i=0; i < smConditioners.size(); i++ ) + smConditioners[i]->_print( &stream ); +} + +Var *ConditionerFeature::printMethodHeader( MethodType methodType, const String &methodName, Stream &stream, MultiLine *meta ) +{ + Var *methodVar = new Var; + methodVar->setName(methodName); + DecOp *methodDecl = new DecOp(methodVar); + + const bool isCondition = (methodType == ConditionerFeature::ConditionMethod); + + Var *paramVar = new Var; + paramVar->setName(avar("%sconditioned%sput", isCondition ? "un" : "", isCondition ? "Out" : "In")); + DecOp *paramDecl = new DecOp(paramVar); + + if(GFX->getAdapterType() == OpenGL) + { + methodVar->setType("vec4"); + paramVar->setType("vec4"); + } + else + { + methodVar->setType("inline float4"); + paramVar->setType("in float4"); + } + + // Method header and opening bracket + meta->addStatement( new GenOp( "@(@)\r\n", methodDecl, paramDecl ) ); + meta->addStatement( new GenOp( "{\r\n" ) ); + + return paramVar; +} + +void ConditionerFeature::printMethodFooter( MethodType methodType, Var *retVar, Stream &stream, MultiLine *meta ) +{ + // Return and closing bracket + meta->addStatement( new GenOp( "\r\n return @;\r\n", retVar ) ); + meta->addStatement( new GenOp( "}\r\n" ) ); +} + +void ConditionerFeature::_printMethod( MethodType methodType, const String &methodName, Stream &stream ) +{ + MultiLine *meta = new MultiLine; + + printHeaderComment( methodType, methodName, stream, meta ); + Var *paramVar = printMethodHeader( methodType, methodName, stream, meta ); + Var *unconditionedInput = NULL; + if( methodType == UnconditionMethod ) + unconditionedInput = _unconditionInput( paramVar, meta ); + else + unconditionedInput = _conditionOutput( paramVar, meta ); + + printMethodFooter( methodType, unconditionedInput, stream, meta ); + printFooterComment( methodType, methodName, stream, meta ); + + meta->print(stream); +} + +void ConditionerFeature::printHeaderComment( MethodType methodType, const String &methodName, Stream &stream, MultiLine *meta ) +{ + meta->addStatement( new GenOp( "//------------------------------------------------------------------------------\r\n" ) ); + meta->addStatement( new GenOp( avar( "// Autogenerated '%s' %s Method\r\n", getName().c_str(), + methodType == ConditionMethod ? "Condition" : "Uncondition" ) ) ); + meta->addStatement( new GenOp( "//------------------------------------------------------------------------------\r\n" ) ); +} + +void ConditionerFeature::printFooterComment( MethodType methodType, const String &methodName, Stream &stream, MultiLine *meta ) +{ + meta->addStatement( new GenOp( "\r\n\r\n" ) ); +} + +void ConditionerMethodDependency::print( Stream &s ) const +{ + mConditioner->_printMethod(mMethodType, mConditioner->getShaderMethodName(mMethodType), s); +} + +void ConditionerMethodDependency::createMethodMacro( const String &methodName, Vector ¯os ) +{ + GFXShaderMacro conditionerMethodMacro; + conditionerMethodMacro.name = methodName; + conditionerMethodMacro.value = mConditioner->getShaderMethodName(mMethodType); + macros.push_back(conditionerMethodMacro); +} diff --git a/Engine/source/shaderGen/conditionerFeature.h b/Engine/source/shaderGen/conditionerFeature.h new file mode 100644 index 000000000..bacdd300b --- /dev/null +++ b/Engine/source/shaderGen/conditionerFeature.h @@ -0,0 +1,137 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _CONDITIONER_BASE_H_ +#define _CONDITIONER_BASE_H_ + +#ifndef _SHADERFEATURE_H_ +#include "shaderGen/shaderFeature.h" +#endif +#ifndef _SHADER_DEPENDENCY_H_ +#include "shaderGen/shaderDependency.h" +#endif + +class MultiLine; +class ConditionerMethodDependency; + + +class ConditionerFeature : public ShaderFeature +{ + friend class ConditionerMethodDependency; + + typedef ShaderFeature Parent; + +public: + + enum MethodType + { + ConditionMethod = 0, ///< Method used to take unconditioned data, and turn it into a format that can be written to the conditioned buffer + UnconditionMethod, ///< Method used to take conditioned data from a buffer, and extract what is stored + NumMethodTypes, + }; + + ConditionerFeature( const GFXFormat bufferFormat ); + virtual ~ConditionerFeature(); + + virtual Material::BlendOp getBlendOp() + { + return Material::None; + } + + virtual GFXFormat getBufferFormat() const { return mBufferFormat; } + virtual bool setBufferFormat(const GFXFormat bufferFormat) { bool ret = mBufferFormat == bufferFormat; mBufferFormat = bufferFormat; return ret; } + + // zero-out these methods + virtual Var* getVertTexCoord( const String &name ) { AssertFatal( false, "don't use this." ); return NULL; } + virtual LangElement *setupTexSpaceMat( Vector &componentList, Var **texSpaceMat ) { AssertFatal( false, "don't use this." ); return NULL; } + virtual LangElement *expandNormalMap( LangElement *sampleNormalOp, LangElement *normalDecl, LangElement *normalVar, const MaterialFeatureData &fd ) { AssertFatal( false, "don't use this." ); return NULL; } + virtual LangElement *assignColor( LangElement *elem, Material::BlendOp blend, LangElement *lerpElem = NULL, ShaderFeature::OutputTarget outputTarget = ShaderFeature::DefaultTarget ) { AssertFatal( false, "don't use this." ); return NULL; } + + // conditioned output + virtual LangElement *assignOutput( Var *unconditionedOutput, ShaderFeature::OutputTarget outputTarget = ShaderFeature::DefaultTarget ); + + // Get an HLSL/GLSL method name that will be available for the + // shader to read or write data to a conditioned buffer. + virtual const String &getShaderMethodName( MethodType methodType ); + + // Get the Method Dependency for ShaderGen, for this conditioner + virtual ConditionerMethodDependency *getConditionerMethodDependency( MethodType methodType ); + + static const String ConditionerIncludeFileName; + + static void updateConditioners() { if ( smDirtyConditioners ) _updateConditioners(); } + +protected: + + static void _updateConditioners(); + + static bool smDirtyConditioners; + + ConditionerMethodDependency *mMethodDependency[NumMethodTypes]; + + static Vector smConditioners; + + GFXFormat mBufferFormat; + + String mUnconditionMethodName; + + String mConditionMethodName; + + String mShaderIncludePath; + + void _print( Stream *stream ); + + virtual Var *_conditionOutput( Var *unconditionedOutput, MultiLine *meta ); + virtual Var *_unconditionInput( Var *conditionedInput, MultiLine *meta ); + + // Print method header, return primary parameter + virtual Var *printMethodHeader( MethodType methodType, const String &methodName, Stream &stream, MultiLine *meta ); + virtual void printMethodFooter( MethodType methodType, Var *retVar, Stream &stream, MultiLine *meta ); + + // Print comments + virtual void printHeaderComment( MethodType methodType, const String &methodName, Stream &stream, MultiLine *meta ); + virtual void printFooterComment( MethodType methodType, const String &methodName, Stream &stream, MultiLine *meta ); + + // print a HLSL/GLSL method to a stream, which can be used by a custom shader + // to read conditioned data + virtual void _printMethod( MethodType methodType, const String &methodName, Stream &stream ); +}; + +//------------------------------------------------------------------------------ + +// ShaderDependancy that allows shadergen features to add a dependency on a conditioner method +class ConditionerMethodDependency : public ShaderDependency +{ +protected: + ConditionerFeature *mConditioner; + ConditionerFeature::MethodType mMethodType; + +public: + ConditionerMethodDependency( ConditionerFeature *conditioner, const ConditionerFeature::MethodType methodType ) : + mConditioner(conditioner), mMethodType(methodType) {} + + virtual void print( Stream &s ) const; + + // Auto insert information into a macro + virtual void createMethodMacro( const String &methodName, Vector ¯os ); +}; + +#endif // _CONDITIONER_BASE_H_ diff --git a/Engine/source/shaderGen/featureMgr.cpp b/Engine/source/shaderGen/featureMgr.cpp new file mode 100644 index 000000000..e0dca3cfd --- /dev/null +++ b/Engine/source/shaderGen/featureMgr.cpp @@ -0,0 +1,143 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "shaderGen/featureMgr.h" + +#include "shaderGen/featureType.h" +#include "shaderGen/shaderFeature.h" +#include "core/util/safeDelete.h" +#include "core/module.h" + + +MODULE_BEGIN( ShaderGenFeatureMgr ) + + MODULE_INIT_BEFORE( ShaderGen ) + MODULE_SHUTDOWN_AFTER( Sim ) // allow registered features to be removed before destroying singleton + + MODULE_INIT + { + ManagedSingleton< FeatureMgr >::createSingleton(); + } + + MODULE_SHUTDOWN + { + ManagedSingleton< FeatureMgr >::deleteSingleton(); + } + +MODULE_END; + + +FeatureMgr::FeatureMgr() + : mNeedsSort( false ) +{ + VECTOR_SET_ASSOCIATION( mFeatures ); +} + +FeatureMgr::~FeatureMgr() +{ + unregisterAll(); +} + +void FeatureMgr::unregisterAll() +{ + FeatureInfoVector::iterator iter = mFeatures.begin(); + for ( ; iter != mFeatures.end(); iter++ ) + { + if ( iter->feature ) + delete iter->feature; + } + + mFeatures.clear(); + mNeedsSort = false; +} + +const FeatureInfo& FeatureMgr::getAt( U32 index ) +{ + if ( mNeedsSort ) + { + mFeatures.sort( _featureInfoCompare ); + mNeedsSort = false; + } + + AssertFatal( index < mFeatures.size(), "FeatureMgr::getAt() - Index out of range!" ); + + return mFeatures[index]; +} + +ShaderFeature* FeatureMgr::getByType( const FeatureType &type ) +{ + FeatureInfoVector::iterator iter = mFeatures.begin(); + for ( ; iter != mFeatures.end(); iter++ ) + { + if ( *iter->type == type ) + return iter->feature; + } + + return NULL; +} + +void FeatureMgr::registerFeature( const FeatureType &type, + ShaderFeature *feature ) +{ + // Remove any existing feature first. + unregisterFeature( type ); + + // Now add the new feature. + mFeatures.increment(); + mFeatures.last().type = &type; + mFeatures.last().feature = feature; + + // Make sure we resort the features. + mNeedsSort = true; +} + +S32 QSORT_CALLBACK FeatureMgr::_featureInfoCompare( const FeatureInfo* a, const FeatureInfo* b ) +{ + const FeatureType *typeA = a->type; + const FeatureType *typeB = b->type; + + if ( typeA->getGroup() < typeB->getGroup() ) + return -1; + else if ( typeA->getGroup() > typeB->getGroup() ) + return 1; + else if ( typeA->getOrder() < typeB->getOrder() ) + return -1; + else if ( typeA->getOrder() > typeB->getOrder() ) + return 1; + else + return 0; +} + +void FeatureMgr::unregisterFeature( const FeatureType &type ) +{ + FeatureInfoVector::iterator iter = mFeatures.begin(); + for ( ; iter != mFeatures.end(); iter++ ) + { + if ( *iter->type != type ) + continue; + + delete iter->feature; + mFeatures.erase( iter ); + return; + } +} diff --git a/Engine/source/shaderGen/featureMgr.h b/Engine/source/shaderGen/featureMgr.h new file mode 100644 index 000000000..4c897cf53 --- /dev/null +++ b/Engine/source/shaderGen/featureMgr.h @@ -0,0 +1,89 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _FEATUREMGR_H_ +#define _FEATUREMGR_H_ + +#ifndef _TSINGLETON_H_ +#include "core/util/tSingleton.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +class FeatureType; +class ShaderFeature; + +/// Used by the feature manager. +struct FeatureInfo +{ + const FeatureType *type; + ShaderFeature *feature; +}; + + +/// +class FeatureMgr +{ +protected: + + bool mNeedsSort; + + typedef Vector FeatureInfoVector; + + FeatureInfoVector mFeatures; + + static S32 QSORT_CALLBACK _featureInfoCompare( const FeatureInfo *a, const FeatureInfo *b ); + +public: + + FeatureMgr(); + ~FeatureMgr(); + + /// Returns the count of registered features. + U32 getFeatureCount() const { return mFeatures.size(); } + + /// Returns the feature info at the index. + const FeatureInfo& getAt( U32 index ); + + /// + ShaderFeature* getByType( const FeatureType &type ); + + // Allows other systems to add features. index is + // the enum in GFXMaterialFeatureData. + void registerFeature( const FeatureType &type, + ShaderFeature *feature ); + + // Unregister a feature. + void unregisterFeature( const FeatureType &type ); + + + /// Removes all features. + void unregisterAll(); + + // For ManagedSingleton. + static const char* getSingletonName() { return "FeatureMgr"; } +}; + +// Helper for accessing the feature manager singleton. +#define FEATUREMGR ManagedSingleton::instance() + +#endif // FEATUREMGR \ No newline at end of file diff --git a/Engine/source/shaderGen/featureSet.cpp b/Engine/source/shaderGen/featureSet.cpp new file mode 100644 index 000000000..7189beacc --- /dev/null +++ b/Engine/source/shaderGen/featureSet.cpp @@ -0,0 +1,225 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "shaderGen/featureSet.h" + +#include "shaderGen/featureType.h" +#include "platform/profiler.h" +#include "core/util/hashFunction.h" + + +const FeatureSet FeatureSet::EmptySet; + + +S32 QSORT_CALLBACK FeatureSet::_typeCmp( const FeatureInfo* a, const FeatureInfo* b ) +{ + if ( a->type->getGroup() < b->type->getGroup() ) + return -1; + else if ( a->type->getGroup() > b->type->getGroup() ) + return 1; + else if ( a->index < b->index ) + return -1; + else if ( a->index > b->index ) + return 1; + else if ( a->type->getOrder() < b->type->getOrder() ) + return -1; + else if ( a->type->getOrder() > b->type->getOrder() ) + return 1; + else + return 0; +} + +void FeatureSet::_rebuildDesc() +{ + PROFILE_SCOPE( FeatureSet_RebuildDesc ); + + // First get the features in the proper order. + mFeatures.sort( _typeCmp ); + + String desc; + + for ( U32 i=0; i < mFeatures.size(); i++ ) + desc += String::ToString( "%s,%d\n", + mFeatures[i].type->getName().c_str(), + mFeatures[i].index ); + + // By interning the description we have only + // one instance in the system and we get fast + // pointer compares for equality. + mDescription = desc.intern(); +} + +const FeatureType& FeatureSet::getAt( U32 index, S32 *outIndex ) const +{ + // We want to make sure we access the features in the + // correct order. By asking for the description we ensure + // the feature set is properly sorted. + getDescription(); + + if ( outIndex ) + *outIndex = mFeatures[index].index; + + return *mFeatures[index].type; +} + +void FeatureSet::clear() +{ + mDescription.clear(); + mFeatures.clear(); +} + +FeatureSet& FeatureSet::operator =( const FeatureSet &h ) +{ + clear(); + merge( h ); + return *this; +} + +bool FeatureSet::hasFeature( const FeatureType &type, S32 index ) const +{ + PROFILE_SCOPE(FeatureSet_hasFeature); + + for ( U32 i = 0; i < mFeatures.size(); i++) + { + if ( mFeatures[i].type == &type && + ( index < 0 || mFeatures[i].index == index ) ) + return true; + } + + return false; +} + +void FeatureSet::setFeature( const FeatureType &type, bool set, S32 index ) +{ + for ( U32 i=0; i < mFeatures.size(); i++ ) + { + const FeatureInfo &info = mFeatures[i]; + if ( info.type == &type && info.index == index ) + { + if ( set ) + return; + else + { + mFeatures.erase_fast( i ); + mDescription.clear(); + return; + } + } + } + + if ( !set ) + return; + + FeatureInfo info; + info.type = &type; + info.index = index; + mFeatures.push_back( info ); + + mDescription.clear(); +} + +void FeatureSet::addFeature( const FeatureType &type, S32 index ) +{ + for ( U32 i=0; i < mFeatures.size(); i++ ) + { + const FeatureInfo &info = mFeatures[i]; + if ( info.type == &type && + info.index == index ) + return; + } + + FeatureInfo info; + info.type = &type; + info.index = index; + mFeatures.push_back( info ); + + mDescription.clear(); +} + +void FeatureSet::removeFeature( const FeatureType &type ) +{ + for ( U32 i=0; i < mFeatures.size(); i++ ) + { + const FeatureInfo &info = mFeatures[i]; + if ( info.type == &type ) + { + mFeatures.erase_fast( i ); + mDescription.clear(); + return; + } + } +} + +U32 FeatureSet::getNextFeatureIndex( const FeatureType &type, S32 index ) const +{ + for ( U32 i=0; i < mFeatures.size(); i++ ) + { + const FeatureInfo &info = mFeatures[i]; + if ( info.type == &type && info.index > index ) + return i; + } + + return -1; +} + +void FeatureSet::filter( const FeatureSet &features ) +{ + PROFILE_SCOPE( FeatureSet_Filter ); + + for ( U32 i=0; i < mFeatures.size(); ) + { + if ( !features.hasFeature( *mFeatures[i].type ) ) + mFeatures.erase_fast( i ); + else + i++; + } + + mDescription.clear(); +} + +void FeatureSet::exclude( const FeatureSet &features ) +{ + PROFILE_SCOPE( FeatureSet_Exclude ); + + for ( U32 i=0; i < features.mFeatures.size(); i++ ) + removeFeature( *features.mFeatures[i].type ); + + mDescription.clear(); +} + +void FeatureSet::merge( const FeatureSet &features ) +{ + PROFILE_SCOPE( FeatureSet_Merge ); + + if ( mFeatures.empty() ) + { + mFeatures.merge( features.mFeatures ); + mDescription = features.mDescription; + return; + } + + for ( U32 i=0; i < features.mFeatures.size(); i++ ) + addFeature( *features.mFeatures[i].type, + features.mFeatures[i].index ); +} + diff --git a/Engine/source/shaderGen/featureSet.h b/Engine/source/shaderGen/featureSet.h new file mode 100644 index 000000000..e8920dc67 --- /dev/null +++ b/Engine/source/shaderGen/featureSet.h @@ -0,0 +1,138 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FEATURESET_H_ +#define _FEATURESET_H_ + +#ifndef _TORQUE_STRING_H_ +#include "core/util/str.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +class FeatureType; + + +// +class FeatureSet +{ +protected: + + struct FeatureInfo + { + const FeatureType* type; + S32 index; + }; + + /// The list of featurs. + Vector mFeatures; + + /// A string representation of all the + /// features used for comparisons. + String mDescription; + + /// + static S32 _typeCmp( const FeatureInfo* a, const FeatureInfo *b ); + + /// + void _rebuildDesc(); + +public: + + FeatureSet() + { + } + + FeatureSet( const FeatureSet &h ) + : mFeatures( h.mFeatures ), + mDescription( h.mDescription ) + { + } + + FeatureSet& operator =( const FeatureSet &h ); + + /// Equality operators. + inline bool operator != ( const FeatureSet &h ) const { return h.getDescription() != getDescription(); } + inline bool operator == ( const FeatureSet &h ) const { return h.getDescription() == getDescription(); } + + bool operator []( const FeatureType &type ) const { return hasFeature( type ); } + + /// Returns true if the feature set is empty. + bool isEmpty() const { return mFeatures.empty(); } + + /// Returns true if the feature set is not empty. + bool isNotEmpty() const { return !mFeatures.empty(); } + + /// Return the description string which uniquely identifies this feature set. + const String& getDescription() const; + + /// Returns the feature count. + U32 getCount() const { return mFeatures.size(); } + + /// Returns the feature at the index and optionally + /// the feature index when it was added. + const FeatureType& getAt( U32 index, S32 *outIndex = NULL ) const; + + /// Returns true if this handle has this feature. + bool hasFeature( const FeatureType &type, S32 index = -1 ) const; + + /// + void setFeature( const FeatureType &type, bool set, S32 index = -1 ); + + /// + void addFeature( const FeatureType &type, S32 index = -1 ); + + /// + void removeFeature( const FeatureType &type ); + + /// + U32 getNextFeatureIndex( const FeatureType &type, S32 index ) const; + + /// Removes features that are not in the input set. + void filter( const FeatureSet &features ); + + /// Removes features that are in the input set. + void exclude( const FeatureSet &features ); + + /// + void merge( const FeatureSet &features ); + + /// Clears all features. + void clear(); + + /// Default empty feature set. + static const FeatureSet EmptySet; + +}; + + +inline const String& FeatureSet::getDescription() const +{ + // Update the description if its empty and we have features. + if ( mDescription.isEmpty() && !mFeatures.empty() ) + const_cast(this)->_rebuildDesc(); + + return mDescription; +} + +#endif // _FEATURESET_H_ diff --git a/Engine/source/shaderGen/featureType.cpp b/Engine/source/shaderGen/featureType.cpp new file mode 100644 index 000000000..1cbd91dce --- /dev/null +++ b/Engine/source/shaderGen/featureType.cpp @@ -0,0 +1,62 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "shaderGen/featureType.h" + +#include "shaderGen/featureSet.h" + +FeatureTypeVector& FeatureType::_getTypes() +{ + // We create it as a method static so that + // its available to other statics regardless + // of initialization order. + static FeatureTypeVector theTypes; + return theTypes; +} + +void FeatureType::addDefaultTypes( FeatureSet *outFeatures ) +{ + const FeatureTypeVector &types = _getTypes(); + for ( U32 i=0; i < types.size(); i++ ) + { + if ( types[i]->isDefault() ) + outFeatures->addFeature( *types[i] ); + } +} + +FeatureType::FeatureType( const char *name, U32 group, F32 order, bool isDefault ) + : mName( name ), + mGroup( group ), + mOrder( order ), + mIsDefault( isDefault ) +{ + FeatureTypeVector &types = _getTypes(); + + #ifdef TORQUE_DEBUG + for ( U32 i=0; i < types.size(); i++ ) + AssertFatal( !mName.equal( types[i]->getName() ), "FeatureType - This feature already exists!" ); + #endif + + mId = types.size(); + types.push_back( this ); +} diff --git a/Engine/source/shaderGen/featureType.h b/Engine/source/shaderGen/featureType.h new file mode 100644 index 000000000..eaa8d1506 --- /dev/null +++ b/Engine/source/shaderGen/featureType.h @@ -0,0 +1,118 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _FEATURETYPE_H_ +#define _FEATURETYPE_H_ + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _TORQUE_STRING_H_ +#include "core/util/str.h" +#endif + +class FeatureType; +class FeatureSet; + + +/// A vector of feature types. +typedef Vector FeatureTypeVector; + + +/// +class FeatureType +{ +protected: + + /// Returns the map of all the types. + static FeatureTypeVector& _getTypes(); + + /// The feature type name. + String mName; + + /// A unique feature id value. + U32 mId; + + /// The group is used to orginize the types. + U32 mGroup; + + /// The sort order of this feature type + /// within its group. + F32 mOrder; + + /// + bool mIsDefault; + + /// + FeatureType( const FeatureType &type ); + +public: + + /// Adds all the default features types to the set. + static void addDefaultTypes( FeatureSet *outFeatures ); + + /// You should not use this constructor directly. + /// @see DeclareFeatureType + /// @see ImplementFeatureType + FeatureType( const char *type, + U32 group = -1, + F32 order = -1.0f, + bool isDefault = true ); + + bool operator !=( const FeatureType &type ) const { return this != &type; } + + bool operator ==( const FeatureType &type ) const { return this == &type; } + + const String& getName() const { return mName; } + + U32 getId() const { return mId; } + + U32 getGroup() const { return mGroup; } + + F32 getOrder() const { return mOrder; } + + bool isDefault() const { return mIsDefault; } + +}; + + +/// +#define DeclareFeatureType( name ) \ + extern const FeatureType name + + +/// +#define ImplementFeatureType( name, group, order, isDefault ) \ + const FeatureType name( #name, group, order, isDefault ) + +/* +#define ImplementFeatureType( name, group, order ) \ + const FeatureType name( #name, group, order ); + +#define ImplementFeatureType( name, group ) \ + const FeatureType name( #name, group ); + +#define ImplementFeatureType( name ) \ + const FeatureType name( #name ); +*/ + +#endif // _FEATURETYPE_H_ \ No newline at end of file diff --git a/Engine/source/shaderGen/langElement.cpp b/Engine/source/shaderGen/langElement.cpp new file mode 100644 index 000000000..60db59a0a --- /dev/null +++ b/Engine/source/shaderGen/langElement.cpp @@ -0,0 +1,204 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/strings/stringFunctions.h" +#include "core/util/str.h" + +#include "langElement.h" + +//************************************************************************** +// Language element +//************************************************************************** +Vector LangElement::elementList( __FILE__, __LINE__ ); + +//-------------------------------------------------------------------------- +// Constructor +//-------------------------------------------------------------------------- +LangElement::LangElement() +{ + elementList.push_back( this ); + + static U32 tempNum = 0; + dSprintf( (char*)name, sizeof(name), "tempName%d", tempNum++ ); +} + +//-------------------------------------------------------------------------- +// Find element of specified name +//-------------------------------------------------------------------------- +LangElement * LangElement::find( const char *name ) +{ + for( U32 i=0; iname, name ) ) + { + return elementList[i]; + } + } + + return NULL; +} + +//-------------------------------------------------------------------------- +// Delete existing elements +//-------------------------------------------------------------------------- +void LangElement::deleteElements() +{ + for( U32 i=0; iprint( stream ); + } +} \ No newline at end of file diff --git a/Engine/source/shaderGen/langElement.h b/Engine/source/shaderGen/langElement.h new file mode 100644 index 000000000..c338aacb8 --- /dev/null +++ b/Engine/source/shaderGen/langElement.h @@ -0,0 +1,183 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _LANG_ELEMENT_H_ +#define _LANG_ELEMENT_H_ + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +#ifndef _STREAM_H_ +#include "core/stream/stream.h" +#endif + +#define WRITESTR( a ){ stream.write( dStrlen(a), a ); } + + +//************************************************************************** +/*! + The LangElement class is the base building block for procedurally + generated shader code. LangElement and its subclasses are strung + together using the static Vector 'elementList'. + When a shader needs to be written to disk, the elementList is + traversed and print() is called on each LangElement and the shader + is output. elementList is cleared after each shader is printed out. +*/ +//************************************************************************** + + +//************************************************************************** +// Language element +//************************************************************************** +struct LangElement +{ + static Vector elementList; + static LangElement * find( const char *name ); + static void deleteElements(); + + U8 name[32]; + + LangElement(); + virtual ~LangElement() {}; + virtual void print( Stream &stream ){}; + void setName(const char *newName ); + +}; + +enum ConstantSortPosition +{ + /// Default / unset + cspUninit = 0, + /// Updated before every draw primitive call. + cspPrimitive, + /// Potentially updated every draw primitive call, but not necessarily (lights for example) + cspPotentialPrimitive, + /// Updated one per pass + cspPass, + /// Count var, do not use + csp_Count +}; + +//---------------------------------------------------------------------------- +/*! + Var - Variable - used to specify a variable to be used in a shader. + Var stores information such that when it is printed out, its context + can be identified and the proper information will automatically be printed. + For instance, if a variable is created with 'uniform' set to true, when the + shader function definition is printed, it will automatically add that + variable to the incoming parameters of the shader. There are several + similar cases such as when a new variable is declared within a shader. + + example: + + @code + + Var *modelview = new Var; + modelview->setType( "float4x4" ); + modelview->setName( "modelview" ); + modelview->uniform = true; + modelview->constSortPos = cspPass; + + @endcode + + it prints out in the shader declaration as: + + @code + ConnectData main( VertData IN, + uniform float4x4 modelview : register(C0) ) + @endcode + +*/ +//---------------------------------------------------------------------------- +struct Var : public LangElement +{ + U8 type[32]; + U8 structName[32]; + char connectName[32]; + ConstantSortPosition constSortPos; // used to calculate constant number + U32 constNum; + U32 texCoordNum; + bool uniform; // argument passed in through constant registers + bool vertData; // argument coming from vertex data + bool connector; // variable that will be passed to pixel shader + bool sampler; // texture + bool mapsToSampler; // for ps 1.x shaders - texcoords must be mapped to same sampler stage + U32 arraySize; // 1 = no array, > 1 array of "type" + + static U32 texUnitCount; + static U32 getTexUnitNum(U32 numElements = 1); + static void reset(); + + // Default + Var(); + Var( const char *name, const char *type ); + + void setStructName(const char *newName ); + void setConnectName(const char *newName ); + void setType(const char *newType ); + + virtual void print( Stream &stream ); + + // Construct a uniform / shader const var + void setUniform(const String& constType, const String& constName, ConstantSortPosition sortPos); +}; + +//---------------------------------------------------------------------------- +/*! + MultiLine - Multi Line Statement - This class simply ties multiple + + example: + + @code + + MultiLine *meta = new MultiLine; + meta->addStatement( new GenOp( "foo = true;\r\n" ) ); + meta->addStatement( new GenOp( "bar = false;\r\n ) ); + + @endcode + + it prints out in the shader declaration as: + + @code + foo = true; + bar = false; + @endcode + +*/ +//---------------------------------------------------------------------------- +class MultiLine : public LangElement +{ + Vector mStatementList; + +public: + MultiLine() + { + VECTOR_SET_ASSOCIATION( mStatementList ); + } + + void addStatement( LangElement *elem ); + virtual void print( Stream &stream ); +}; + + + +#endif // _LANG_ELEMENT_H_ diff --git a/Engine/source/shaderGen/shaderComp.cpp b/Engine/source/shaderGen/shaderComp.cpp new file mode 100644 index 000000000..d976fb7b9 --- /dev/null +++ b/Engine/source/shaderGen/shaderComp.cpp @@ -0,0 +1,43 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#include "shaderComp.h" + +//************************************************************************** +// Connector Struct Component +//************************************************************************** + +//---------------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------------- +ShaderConnector::ShaderConnector() +{ + mCurTexElem = 0; +} + +//---------------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------------- +ShaderConnector::~ShaderConnector() +{ + // Elements freed by LangElement::freeElements() +} + diff --git a/Engine/source/shaderGen/shaderComp.h b/Engine/source/shaderGen/shaderComp.h new file mode 100644 index 000000000..bae47112e --- /dev/null +++ b/Engine/source/shaderGen/shaderComp.h @@ -0,0 +1,99 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _SHADERCOMP_H_ +#define _SHADERCOMP_H_ + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +#ifndef _MISCSHDRDAT_H_ +#include "materials/miscShdrDat.h" +#endif + +class Stream; +struct Var; + +//************************************************************************** +// Shader Component - these objects are the main logical breakdown of a +// high level shader. They represent the various data structures +// and the main() procedure necessary to create a shader. +//************************************************************************** +class ShaderComponent +{ +public: + virtual ~ShaderComponent() {} + + virtual void print( Stream &stream ){}; +}; + + +//************************************************************************** +// Connector Struct Component - used for incoming Vertex struct and also the +// "connection" struct shared by the vertex and pixel shader +//************************************************************************** +class ShaderConnector : public ShaderComponent +{ +protected: + enum Consts + { + NUM_TEX_REGS = 8, + }; + + enum Elements + { + POSITION = 0, + NORMAL, + COLOR, + NUM_BASIC_ELEMS + }; + + Vector mElementList; + + U32 mCurTexElem; + U8 mName[32]; + +public: + + ShaderConnector(); + virtual ~ShaderConnector(); + + /// + virtual Var* getElement( RegisterType type, + U32 numElements = 1, + U32 numRegisters = -1 ) = 0; + + virtual void setName( char *newName ) = 0; + virtual void reset() = 0; + virtual void sortVars() = 0; + + virtual void print( Stream &stream ) = 0; +}; + +/// This is to provide common functionalty needed by vertex and pixel main defs +class ParamsDef : public ShaderComponent +{ +protected: + virtual void assignConstantNumbers() {} +}; + +#endif // _SHADERCOMP_H_ diff --git a/Engine/source/shaderGen/shaderDependency.cpp b/Engine/source/shaderGen/shaderDependency.cpp new file mode 100644 index 000000000..22d30572f --- /dev/null +++ b/Engine/source/shaderGen/shaderDependency.cpp @@ -0,0 +1,47 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "shaderGen/shaderDependency.h" + +#include "core/stream/fileStream.h" +#include "core/frameAllocator.h" + + +ShaderIncludeDependency::ShaderIncludeDependency( const Torque::Path &pathToInclude ) + : mIncludePath( pathToInclude ) +{ +} + +bool ShaderIncludeDependency::operator==( const ShaderDependency &cmpTo ) const +{ + return this == &cmpTo || + ( dynamic_cast( &cmpTo ) && + static_cast( &cmpTo )->mIncludePath == mIncludePath ); +} + +void ShaderIncludeDependency::print( Stream &s ) const +{ + // Print the include... all shaders support #includes. + String include = String::ToString( "#include \"%s\"\r\n", mIncludePath.getFullPath().c_str() ); + s.write( include.length(), include.c_str() ); +} diff --git a/Engine/source/shaderGen/shaderDependency.h b/Engine/source/shaderGen/shaderDependency.h new file mode 100644 index 000000000..78960de37 --- /dev/null +++ b/Engine/source/shaderGen/shaderDependency.h @@ -0,0 +1,66 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SHADER_DEPENDENCY_H_ +#define _SHADER_DEPENDENCY_H_ + +#ifndef _PATH_H_ +#include "core/util/path.h" +#endif + + +class Stream; + + +/// The base class for shader dependencies +class ShaderDependency +{ +public: + virtual ~ShaderDependency() {} + + /// Compare this dependency to another one. + virtual bool operator==( const ShaderDependency &cmpTo ) const + { + return this == &cmpTo; + } + + /// Print the dependency into the header of a shader. + virtual void print( Stream &s ) const = 0; +}; + + +/// A shader dependency for adding #include's into shadergen shaders. +class ShaderIncludeDependency : public ShaderDependency +{ +protected: + + Torque::Path mIncludePath; + +public: + + ShaderIncludeDependency( const Torque::Path &pathToInclude ); + + virtual bool operator==( const ShaderDependency &cmpTo ) const; + virtual void print( Stream &s ) const; +}; + +#endif // _SHADER_DEPENDENCY_H_ \ No newline at end of file diff --git a/Engine/source/shaderGen/shaderFeature.cpp b/Engine/source/shaderGen/shaderFeature.cpp new file mode 100644 index 000000000..9c1d8d955 --- /dev/null +++ b/Engine/source/shaderGen/shaderFeature.cpp @@ -0,0 +1,73 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "shaderGen/shaderFeature.h" + +#include "shaderGen/langElement.h" +#include "shaderGen/shaderOp.h" + + +void ShaderFeature::addDependency( const ShaderDependency *dependsOn ) +{ + for ( U32 i = 0; i < mDependencies.size(); i++ ) + { + if ( *mDependencies[i] == *dependsOn ) + return; + } + + mDependencies.push_back( dependsOn ); +} + +ShaderFeature::Resources ShaderFeature::getResources( const MaterialFeatureData &fd ) +{ + Resources temp; + return temp; +} + +const char* ShaderFeature::getOutputTargetVarName( OutputTarget target ) const +{ + const char* targName = "col"; + if ( target != DefaultTarget ) + { + targName = "col1"; + AssertFatal(target == RenderTarget1, "yeah Pat is lame and didn't want to do bit math stuff, TODO"); + } + + return targName; +} + +Var* ShaderFeature::findOrCreateLocal( const char *name, + const char *type, + MultiLine *multi ) +{ + Var *outVar = (Var*)LangElement::find( name ); + if ( !outVar ) + { + outVar = new Var; + outVar->setType( type ); + outVar->setName( name ); + multi->addStatement( new GenOp( " @;\r\n", new DecOp( outVar ) ) ); + } + + return outVar; +} \ No newline at end of file diff --git a/Engine/source/shaderGen/shaderFeature.h b/Engine/source/shaderGen/shaderFeature.h new file mode 100644 index 000000000..a5f0a7d89 --- /dev/null +++ b/Engine/source/shaderGen/shaderFeature.h @@ -0,0 +1,296 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _SHADERFEATURE_H_ +#define _SHADERFEATURE_H_ + +#ifndef _MATERIALDEFINITION_H_ +#include "materials/materialDefinition.h" +#endif +#ifndef _SHADERCOMP_H_ +#include "shaderGen/shaderComp.h" +#endif +#ifndef _SHADER_DEPENDENCY_H_ +#include "shaderGen/shaderDependency.h" +#endif + +class MultiLine; +struct LangElement; +struct MaterialFeatureData; +class GFXShaderConstBuffer; +struct RenderPassData; +struct SceneData; +class SceneRenderState; +class GFXShader; +class GFXVertexFormat; + + +/// +class ShaderFeatureConstHandles +{ +public: + + virtual void init( GFXShader *shader ) = 0; + + virtual void setConsts( SceneRenderState *state, + const SceneData &sgData, + GFXShaderConstBuffer *buffer ) = 0; +}; + +//************************************************************************** +/*! + The ShaderFeature class is the base class for every procedurally generated + feature. Each feature the engine recognizes is part of the MaterialFeatureType + enum. That structure is used to indicate which features are present in a shader + to be generated. This is useful as many ShaderFeatures will output different + code depending on what other features are going to be in the shader. + + Shaders are generated using the ShaderFeature interface, so all of the + descendants interact pretty much the same way. + +*/ +//************************************************************************** + + +//************************************************************************** +// Shader Feature +//************************************************************************** +class ShaderFeature +{ +public: + + // Bitfield which allows a shader feature to say which render targets it outputs + // data to (could be more than one). + enum OutputTarget + { + DefaultTarget = 1 << 0, + RenderTarget1 = 1 << 1, + RenderTarget2 = 1 << 2, + RenderTarget3 = 1 << 3, + }; + +protected: + + LangElement *output; + + /// The list of unique shader dependencies. + Vector mDependencies; + + /// + S32 mProcessIndex; + +public: + + // TODO: Make this protected and give it a proper API. + GFXVertexFormat *mInstancingFormat; + + //************************************************************************** + /*! + The Resources structure is used by ShaderFeature to indicate how many + hardware "resources" it needs. Resources are things such as how + many textures it uses and how many texture registers it needs to pass + information from the vertex to the pixel shader. + + The Resources data can change depending what hardware is available. For + instance, pixel 1.x level hardware may need more texture registers than + pixel 2.0+ hardware because each texture register can only be used with + its respective texture sampler. + + The data in Resources is used to determine how many features can be + squeezed into a singe shader. If a feature requires too many resources + to fit into the current shader, it will be put into another pass. + */ + //************************************************************************** + struct Resources + { + U32 numTex; + U32 numTexReg; + + Resources() + { + dMemset( this, 0, sizeof( Resources ) ); + } + }; + + + //----------------------------------------------------------------------- + // Base functions + //----------------------------------------------------------------------- + + ShaderFeature() + : output( NULL ), + mProcessIndex( 0 ), + mInstancingFormat( NULL ) + { + } + + virtual ~ShaderFeature() {} + + /// returns output from a processed vertex or pixel shader + LangElement* getOutput() const { return output; } + + /// + void setProcessIndex( S32 index ) { mProcessIndex = index; } + + /// + U32 getProcessIndex() const { return mProcessIndex; } + + //----------------------------------------------------------------------- + // Virtual Functions + //----------------------------------------------------------------------- + + /// Get the incoming base texture coords - useful for bumpmap and detail maps + virtual Var* getVertTexCoord( const String &name ) = 0; + + /// Set up a texture space matrix - to pass into pixel shader + virtual LangElement * setupTexSpaceMat( Vector &componentList, + Var **texSpaceMat ) = 0; + + /// Expand and assign a normal map. This takes care of compressed normal maps as well. + virtual LangElement * expandNormalMap( LangElement *sampleNormalOp, + LangElement *normalDecl, LangElement *normalVar, const MaterialFeatureData &fd ) = 0; + + /// Helper function for applying the color to shader output. + /// + /// @param elem The rbg or rgba color to assign. + /// + /// @param blend The type of blending to perform. + /// + /// @param lerpElem The optional lerp parameter when doing a LerpAlpha blend, + /// if not set then the elem is used. + /// + virtual LangElement* assignColor( LangElement *elem, + Material::BlendOp blend, + LangElement *lerpElem = NULL, + ShaderFeature::OutputTarget outputTarget = ShaderFeature::DefaultTarget ) = 0; + + + //----------------------------------------------------------------------- + /*! + Process vertex shader - This function is used by each feature to + generate a list of LangElements that can be traversed and "printed" + to generate the actual shader code. The 'output' member is the head + of that list. + + The componentList is used mostly for access to the "Connector" + structure which is used to pass data from the vertex to the pixel + shader. + + The MaterialFeatureData parameter is used to determine what other + features are present for the shader being generated. + */ + //----------------------------------------------------------------------- + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ) + { output = NULL; } + + //----------------------------------------------------------------------- + /*! + Process pixel shader - This function is used by each feature to + generate a list of LangElements that can be traversed and "printed" + to generate the actual shader code. The 'output' member is the head + of that list. + + The componentList is used mostly for access to the "Connector" + structure which is used to pass data from the vertex to the pixel + shader. + + The MaterialFeatureData parameter is used to determine what other + features are present for the shader being generated. + */ + //----------------------------------------------------------------------- + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ) + { output = NULL; } + + /// Allows the feature to add macros to pixel shader compiles. + virtual void processPixMacros( Vector ¯os, const MaterialFeatureData &fd ) {}; + + /// Allows the feature to add macros to vertex shader compiles. + virtual void processVertMacros( Vector ¯os, const MaterialFeatureData &fd ) {}; + + /// Identifies what type of blending a feature uses. This is used to + /// group features with the same blend operation together in a multipass + /// situation. + virtual Material::BlendOp getBlendOp() { return Material::Add; } + + /// Returns the resource requirements of this feature based on what + /// other features are present. The "resources" are things such as + /// texture units, and texture registers of which there can be + /// very limited numbers. The resources can vary depending on hardware + /// and what other features are present. + virtual Resources getResources( const MaterialFeatureData &fd ); + + /// Fills texture related info in RenderPassData for this feature. It + /// takes into account the current pass (passData) as well as what other + /// data is available to the material stage (stageDat). + /// + /// For instance, ReflectCubeFeatHLSL would like to modulate its output + /// by the alpha channel of another texture. If the current pass does + /// not contain a diffuse or bump texture, but the Material does, then + /// this function allows it to use one of those textures in the current + /// pass. + virtual void setTexData( Material::StageData &stageDat, + const MaterialFeatureData &fd, + RenderPassData &passData, + U32 &texIndex ){}; + + /// Returns the name of this feature. + virtual String getName() = 0; + + /// Adds a dependency to this shader feature. + virtual void addDependency( const ShaderDependency *depends ); + + /// Gets the dependency list for this shader feature. + virtual const Vector &getDependencies() const { return mDependencies; } + + /// Returns the output variable name for this feature if it applies. + virtual const char* getOutputVarName() const { return NULL; } + + /// Gets the render target this shader feature is assigning data to. + virtual U32 getOutputTargets( const MaterialFeatureData &fd ) const { return DefaultTarget; } + + /// Returns the name of output targer var. + const char* getOutputTargetVarName( OutputTarget target = DefaultTarget ) const; + + // Called from ProcessedShaderMaterial::determineFeatures to enable/disable features. + virtual void determineFeature( Material *material, + const GFXVertexFormat *vertexFormat, + U32 stageNum, + const FeatureType &type, + const FeatureSet &features, + MaterialFeatureData *outFeatureData ) { } + + // + virtual ShaderFeatureConstHandles* createConstHandles( GFXShader *shader, SimObject *userObject ) { return NULL; } + + /// Called after processing the vertex and processing the pixel + /// to cleanup any temporary structures stored in the feature. + virtual void reset() { output = NULL; mProcessIndex = 0; mInstancingFormat = NULL; } + + /// A simpler helper function which either finds + /// the existing local var or creates one. + static Var* findOrCreateLocal( const char *name, + const char *type, + MultiLine *multi ); +}; + +#endif // _SHADERFEATURE_H_ diff --git a/Engine/source/shaderGen/shaderGen.cpp b/Engine/source/shaderGen/shaderGen.cpp new file mode 100644 index 000000000..6c7775353 --- /dev/null +++ b/Engine/source/shaderGen/shaderGen.cpp @@ -0,0 +1,507 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "shaderGen/shaderGen.h" + +#include "shaderGen/conditionerFeature.h" +#include "core/stream/fileStream.h" +#include "shaderGen/featureMgr.h" +#include "shaderGen/shaderOp.h" +#include "gfx/gfxDevice.h" +#include "core/memVolume.h" +#include "core/module.h" + + +MODULE_BEGIN( ShaderGen ) + + MODULE_INIT_BEFORE( GFX ) + MODULE_SHUTDOWN_AFTER( GFX ) + + MODULE_INIT + { + ManagedSingleton< ShaderGen >::createSingleton(); + } + + MODULE_SHUTDOWN + { + ManagedSingleton< ShaderGen >::deleteSingleton(); + } + +MODULE_END; + + +ShaderGen::ShaderGen() +{ + mInit = false; + GFXDevice::getDeviceEventSignal().notify(this, &ShaderGen::_handleGFXEvent); + mOutput = NULL; +} + +ShaderGen::~ShaderGen() +{ + GFXDevice::getDeviceEventSignal().remove(this, &ShaderGen::_handleGFXEvent); + _uninit(); +} + +void ShaderGen::registerInitDelegate(GFXAdapterType adapterType, ShaderGenInitDelegate& initDelegate) +{ + mInitDelegates[(U32)adapterType] = initDelegate; +} + +bool ShaderGen::_handleGFXEvent(GFXDevice::GFXDeviceEventType event) +{ + switch (event) + { + case GFXDevice::deInit : + initShaderGen(); + break; + case GFXDevice::deDestroy : + { + flushProceduralShaders(); + } + break; + default : + break; + } + return true; +} + +void ShaderGen::initShaderGen() +{ + if (mInit) + return; + + const GFXAdapterType adapterType = GFX->getAdapterType(); + if (!mInitDelegates[adapterType]) + return; + + mInitDelegates[adapterType](this); + mFeatureInitSignal.trigger( adapterType ); + mInit = true; + + String shaderPath = Con::getVariable( "$shaderGen::cachePath"); +#if defined(TORQUE_SHADERGEN) && ( defined(TORQUE_OS_XENON) || defined(TORQUE_OS_PS3) ) + // If this is a console build, and TORQUE_SHADERGEN is defined + // (signifying that new shaders should be generated) then clear the shader + // path so that the MemFileSystem is used instead. + shaderPath.clear(); +#endif + + if (!shaderPath.equal( "shadergen:" ) && !shaderPath.isEmpty() ) + { + // this is necessary, especially under Windows with UAC enabled + if (!Torque::FS::VerifyWriteAccess(shaderPath)) + { + // we don't have write access so enable the virtualized memory store + Con::warnf("ShaderGen: Write permission unavailable, switching to virtualized memory storage"); + shaderPath.clear(); + } + + } + + if ( shaderPath.equal( "shadergen:" ) || shaderPath.isEmpty() ) + { + // If we didn't get a path then we're gonna cache the shaders to + // a virtualized memory file system. + mMemFS = new Torque::Mem::MemFileSystem( "shadergen:/" ); + Torque::FS::Mount( "shadergen", mMemFS ); + } + else + Torque::FS::Mount( "shadergen", shaderPath + "/" ); + + // Delete the auto-generated conditioner include file. + Torque::FS::Remove( "shadergen:/" + ConditionerFeature::ConditionerIncludeFileName ); +} + +void ShaderGen::generateShader( const MaterialFeatureData &featureData, + char *vertFile, + char *pixFile, + F32 *pixVersion, + const GFXVertexFormat *vertexFormat, + const char* cacheName, + Vector ¯os ) +{ + PROFILE_SCOPE( ShaderGen_GenerateShader ); + + mFeatureData = featureData; + mVertexFormat = vertexFormat; + + _uninit(); + _init(); + + char vertShaderName[256]; + char pixShaderName[256]; + + // Note: We use a postfix of _V/_P here so that it sorts the matching + // vert and pixel shaders together when listed alphabetically. + dSprintf( vertShaderName, sizeof(vertShaderName), "shadergen:/%s_V.%s", cacheName, mFileEnding.c_str() ); + dSprintf( pixShaderName, sizeof(pixShaderName), "shadergen:/%s_P.%s", cacheName, mFileEnding.c_str() ); + + dStrcpy( vertFile, vertShaderName ); + dStrcpy( pixFile, pixShaderName ); + + // this needs to change - need to optimize down to ps v.1.1 + *pixVersion = GFX->getPixelShaderVersion(); + + if ( !Con::getBoolVariable( "ShaderGen::GenNewShaders", true ) ) + { + // If we are not regenerating the shader we will return here. + // But we must fill in the shader macros first! + + _processVertFeatures( macros, true ); + _processPixFeatures( macros, true ); + + return; + } + + // create vertex shader + //------------------------ + FileStream* s = new FileStream(); + if(!s->open(vertShaderName, Torque::FS::File::Write )) + { + AssertFatal(false, "Failed to open Shader Stream" ); + return; + } + + mOutput = new MultiLine; + mInstancingFormat.clear(); + _processVertFeatures(macros); + _printVertShader( *s ); + delete s; + + ((ShaderConnector*)mComponents[C_CONNECTOR])->reset(); + LangElement::deleteElements(); + + // create pixel shader + //------------------------ + s = new FileStream(); + if(!s->open(pixShaderName, Torque::FS::File::Write )) + { + AssertFatal(false, "Failed to open Shader Stream" ); + return; + } + + mOutput = new MultiLine; + _processPixFeatures(macros); + _printPixShader( *s ); + + delete s; + LangElement::deleteElements(); +} + +void ShaderGen::_init() +{ + _createComponents(); +} + +void ShaderGen::_uninit() +{ + for( U32 i=0; icreateVertexInputConnector( *mVertexFormat ); + mComponents.push_back(vertComp); + + ShaderComponent* vertPixelCon = mComponentFactory->createVertexPixelConnector(); + mComponents.push_back(vertPixelCon); + + ShaderComponent* vertParamDef = mComponentFactory->createVertexParamsDef(); + mComponents.push_back(vertParamDef); + + ShaderComponent* pixParamDef = mComponentFactory->createPixelParamsDef(); + mComponents.push_back(pixParamDef); +} + +//---------------------------------------------------------------------------- +// Process features +//---------------------------------------------------------------------------- +void ShaderGen::_processVertFeatures( Vector ¯os, bool macrosOnly ) +{ + const FeatureSet &features = mFeatureData.features; + + for( U32 i=0; i < features.getCount(); i++ ) + { + S32 index; + const FeatureType &type = features.getAt( i, &index ); + ShaderFeature* feature = FEATUREMGR->getByType( type ); + if ( feature ) + { + feature->setProcessIndex( index ); + + feature->processVertMacros( macros, mFeatureData ); + + if ( macrosOnly ) + continue; + + feature->mInstancingFormat = &mInstancingFormat; + feature->processVert( mComponents, mFeatureData ); + + String line; + if ( index > -1 ) + line = String::ToString( " // %s %d\r\n", feature->getName().c_str(), index ); + else + line = String::ToString( " // %s\r\n", feature->getName().c_str() ); + mOutput->addStatement( new GenOp( line ) ); + + if ( feature->getOutput() ) + mOutput->addStatement( feature->getOutput() ); + + feature->reset(); + mOutput->addStatement( new GenOp( " \r\n" ) ); + } + } + + ShaderConnector *connect = dynamic_cast( mComponents[C_CONNECTOR] ); + connect->sortVars(); +} + +void ShaderGen::_processPixFeatures( Vector ¯os, bool macrosOnly ) +{ + const FeatureSet &features = mFeatureData.features; + + for( U32 i=0; i < features.getCount(); i++ ) + { + S32 index; + const FeatureType &type = features.getAt( i, &index ); + ShaderFeature* feature = FEATUREMGR->getByType( type ); + if ( feature ) + { + feature->setProcessIndex( index ); + + feature->processPixMacros( macros, mFeatureData ); + + if ( macrosOnly ) + continue; + + feature->mInstancingFormat = &mInstancingFormat; + feature->processPix( mComponents, mFeatureData ); + + String line; + if ( index > -1 ) + line = String::ToString( " // %s %d\r\n", feature->getName().c_str(), index ); + else + line = String::ToString( " // %s\r\n", feature->getName().c_str() ); + mOutput->addStatement( new GenOp( line ) ); + + if ( feature->getOutput() ) + mOutput->addStatement( feature->getOutput() ); + + feature->reset(); + mOutput->addStatement( new GenOp( " \r\n" ) ); + } + } + + ShaderConnector *connect = dynamic_cast( mComponents[C_CONNECTOR] ); + connect->sortVars(); +} + +void ShaderGen::_printFeatureList(Stream &stream) +{ + mPrinter->printLine(stream, "// Features:"); + + const FeatureSet &features = mFeatureData.features; + + for( U32 i=0; i < features.getCount(); i++ ) + { + S32 index; + const FeatureType &type = features.getAt( i, &index ); + ShaderFeature* feature = FEATUREMGR->getByType( type ); + if ( feature ) + { + String line; + if ( index > -1 ) + line = String::ToString( "// %s %d", feature->getName().c_str(), index ); + else + line = String::ToString( "// %s", feature->getName().c_str() ); + + mPrinter->printLine( stream, line ); + } + } + + mPrinter->printLine(stream, ""); +} + +void ShaderGen::_printDependencies(Stream &stream) +{ + Vector dependencies; + + for( U32 i=0; i < FEATUREMGR->getFeatureCount(); i++ ) + { + const FeatureInfo &info = FEATUREMGR->getAt( i ); + if ( mFeatureData.features.hasFeature( *info.type ) ) + dependencies.merge( info.feature->getDependencies() ); + } + + // Do a quick loop removing any duplicate dependancies. + for( U32 i=0; i < dependencies.size(); ) + { + bool dup = false; + + for( U32 j=0; j < dependencies.size(); j++ ) + { + if ( j != i && + *dependencies[i] == *dependencies[j] ) + { + dup = true; + break; + } + } + + if ( dup ) + dependencies.erase( i ); + else + i++; + } + + // Print dependencies + if( dependencies.size() > 0 ) + { + mPrinter->printLine(stream, "// Dependencies:"); + + for( int i = 0; i < dependencies.size(); i++ ) + dependencies[i]->print( stream ); + + mPrinter->printLine(stream, ""); + } +} + +void ShaderGen::_printFeatures( Stream &stream ) +{ + mOutput->print( stream ); +} + +void ShaderGen::_printVertShader( Stream &stream ) +{ + mPrinter->printShaderHeader(stream); + + _printDependencies(stream); // TODO: Split into vert and pix dependencies? + _printFeatureList(stream); + + // print out structures + mComponents[C_VERT_STRUCT]->print( stream ); + mComponents[C_CONNECTOR]->print( stream ); + + mPrinter->printMainComment(stream); + + mComponents[C_VERT_MAIN]->print( stream ); + + + // print out the function + _printFeatures( stream ); + + mPrinter->printVertexShaderCloser(stream); +} + +void ShaderGen::_printPixShader( Stream &stream ) +{ + mPrinter->printShaderHeader(stream); + + _printDependencies(stream); // TODO: Split into vert and pix dependencies? + _printFeatureList(stream); + + mComponents[C_CONNECTOR]->print( stream ); + + mPrinter->printPixelShaderOutputStruct(stream, mFeatureData); + mPrinter->printMainComment(stream); + + mComponents[C_PIX_MAIN]->print( stream ); + + // print out the function + _printFeatures( stream ); + + mPrinter->printPixelShaderCloser(stream); +} + +GFXShader* ShaderGen::getShader( const MaterialFeatureData &featureData, const GFXVertexFormat *vertexFormat, const Vector *macros ) +{ + PROFILE_SCOPE( ShaderGen_GetShader ); + + const FeatureSet &features = featureData.codify(); + + // Build a description string from the features + // and vertex format combination ( and macros ). + String shaderDescription = vertexFormat->getDescription() + features.getDescription(); + if ( macros && !macros->empty() ) + { + String macroStr; + GFXShaderMacro::stringize( *macros, ¯oStr ); + shaderDescription += macroStr; + } + + // Generate a single 64bit hash from the description string. + // + // Don't get paranoid! This has 1 in 18446744073709551616 + // chance for collision... it won't happen in this lifetime. + // + U64 hash = Torque::hash64( (const U8*)shaderDescription.c_str(), shaderDescription.length(), 0 ); + hash = convertHostToLEndian(hash); + U32 high = (U32)( hash >> 32 ); + U32 low = (U32)( hash & 0x00000000FFFFFFFF ); + String cacheKey = String::ToString( "%x%x", high, low ); + + // return shader if exists + GFXShader *match = mProcShaders[cacheKey]; + if ( match ) + return match; + + // if not, then create it + char vertFile[256]; + char pixFile[256]; + F32 pixVersion; + + Vector shaderMacros; + shaderMacros.push_back( GFXShaderMacro( "TORQUE_SHADERGEN" ) ); + if ( macros ) + shaderMacros.merge( *macros ); + generateShader( featureData, vertFile, pixFile, &pixVersion, vertexFormat, cacheKey, shaderMacros ); + + GFXShader *shader = GFX->createShader(); + shader->mInstancingFormat.copy( mInstancingFormat ); // TODO: Move to init() below! + if ( !shader->init( vertFile, pixFile, pixVersion, shaderMacros ) ) + { + delete shader; + return NULL; + } + + mProcShaders[cacheKey] = shader; + + return shader; +} + +void ShaderGen::flushProceduralShaders() +{ + // The shaders are reference counted, so we + // just need to clear the map. + mProcShaders.clear(); +} diff --git a/Engine/source/shaderGen/shaderGen.h b/Engine/source/shaderGen/shaderGen.h new file mode 100644 index 000000000..dab3a29e2 --- /dev/null +++ b/Engine/source/shaderGen/shaderGen.h @@ -0,0 +1,233 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _SHADERGEN_H_ +#define _SHADERGEN_H_ + +#ifndef _LANG_ELEMENT_H_ +#include "shaderGen/langElement.h" +#endif +#ifndef _SHADERFEATURE_H_ +#include "shaderGen/shaderFeature.h" +#endif +#ifndef _SHADERCOMP_H_ +#include "shaderGen/shaderComp.h" +#endif +#ifndef _GFXDEVICE_H_ +#include "gfx/gfxDevice.h" +#endif +#ifndef _AUTOPTR_H_ +#include "core/util/autoPtr.h" +#endif +#ifndef _TSINGLETON_H_ +#include "core/util/tSingleton.h" +#endif +#ifndef _VOLUME_H_ +#include "core/volume.h" +#endif +#ifndef _MATERIALFEATUREDATA_H_ +#include "materials/materialFeatureData.h" +#endif + + +/// Base class used by shaderGen to be API agnostic. Subclasses implement the various methods +/// in an API specific way. +class ShaderGenPrinter +{ +public: + virtual ~ShaderGenPrinter() {} + + /// Prints a simple header, including the engine name, language type, and + /// the fact that the shader was procedurally generated + virtual void printShaderHeader(Stream& stream) = 0; + + /// Prints a comment block specifying the beginning of the main() function (or equivalent) + virtual void printMainComment(Stream& stream) = 0; + + /// Prints the final line of the vertex shader, e.g. return OUT; }, }, END + virtual void printVertexShaderCloser(Stream& stream) = 0; + + /// Prints the output struct for the pixel shader. Probably only used in HLSL/Cg. + virtual void printPixelShaderOutputStruct(Stream& stream, const MaterialFeatureData &featureData) = 0; + + /// Prints the final line of the pixel shader. + virtual void printPixelShaderCloser(Stream& stream) = 0; + + // Prints a line into the shader adding the proper terminator. + virtual void printLine(Stream& stream, const String& line) = 0; +}; + +/// Abstract factory for created (and initializating, if necessary) shader components. +class ShaderGenComponentFactory +{ +public: + virtual ~ShaderGenComponentFactory() {} + + /// Creates and initializes a vertex input connector with the specified flags + virtual ShaderComponent* createVertexInputConnector( const GFXVertexFormat &vertexFormat ) = 0; + + /// Creates and names a vertex/pixel connector + virtual ShaderComponent* createVertexPixelConnector() = 0; + + /// Creates an instance of VertexParamsDef + virtual ShaderComponent* createVertexParamsDef() = 0; + + /// Creates an instance of PixelParamsDef + virtual ShaderComponent* createPixelParamsDef() = 0; +}; + +//************************************************************************** +/*! + The ShaderGen class takes shader feature data (usually created by + MatInstance) and creates a vertex/pixel shader pair in text files + to be later compiled by a shader manager. + + It accomplishes this task by creating a group of shader "components" and + "features" that output bits of high level shader code. Shader components + translate to structures in HLSL that indicate incoming vertex data, + data that is output from the vertex shader to the pixel shader, and data + such as constants and textures that are passed directly to the shader + from the app. + + Shader features are separable shader functions that can be turned on or + off. Examples would be bumpmapping and specular highlights. See + MaterialFeatureData for the current list of features supported. + + ShaderGen processes all of the features that are present for a desired + shader, and then prints them out to the respective vertex or pixel + shader file. + + For more information on shader features and components see the + ShaderFeature and ShaderComponent classes. +*/ +//************************************************************************** + + +//************************************************************************** +// Shader generator +//************************************************************************** +class ShaderGen +{ +public: + virtual ~ShaderGen(); + + /// Parameter 1 is the ShaderGen instance to initialize. + typedef Delegate ShaderGenInitDelegate; + + /// Register an initialization delegate for adapterType. This should setPrinter/ComponentFactory/etc, and register + /// shader features. + void registerInitDelegate(GFXAdapterType adapterType, ShaderGenInitDelegate& initDelegate); + + /// Signal used to notify systems to register features. + typedef Signal FeatureInitSignal; + + /// Returns the signal used to notify systems to register features. + FeatureInitSignal& getFeatureInitSignal() { return mFeatureInitSignal; } + + /// vertFile and pixFile are filled in by this function. They point to + /// the vertex and pixel shader files. pixVersion is also filled in by + /// this function. + /// @param assignNum used to assign a specific number as the filename + void generateShader( const MaterialFeatureData &featureData, + char *vertFile, + char *pixFile, + F32 *pixVersion, + const GFXVertexFormat *vertexFormat, + const char* cacheName, + Vector ¯os ); + + // Returns a shader that implements the features listed by dat. + GFXShader* getShader( const MaterialFeatureData &dat, const GFXVertexFormat *vertexFormat, const Vector *macros ); + + // This will delete all of the procedural shaders that we have. Used to regenerate shaders when + // the ShaderFeatures have changed (due to lighting system change, or new plugin) + virtual void flushProceduralShaders(); + + void setPrinter(ShaderGenPrinter* printer) { mPrinter = printer; } + void setComponentFactory(ShaderGenComponentFactory* factory) { mComponentFactory = factory; } + void setFileEnding(String ending) { mFileEnding = ending; } + +protected: + + friend class ManagedSingleton; + + // Shader generation + MaterialFeatureData mFeatureData; + const GFXVertexFormat *mVertexFormat; + + Vector< ShaderComponent *> mComponents; + + AutoPtr mPrinter; + AutoPtr mComponentFactory; + + String mFileEnding; + + /// The currently processing output. + MultiLine *mOutput; + GFXVertexFormat mInstancingFormat; + + /// Init + bool mInit; + ShaderGenInitDelegate mInitDelegates[GFXAdapterType_Count]; + FeatureInitSignal mFeatureInitSignal; + bool mRegisteredWithGFX; + Torque::FS::FileSystemRef mMemFS; + + /// Map of cache string -> shaders + typedef Map ShaderMap; + ShaderMap mProcShaders; + + ShaderGen(); + + bool _handleGFXEvent(GFXDevice::GFXDeviceEventType event); + + /// Causes the init delegate to be called. + void initShaderGen(); + + void _init(); + void _uninit(); + + /// Creates all the various shader components that will be filled in when + /// the shader features are processed. + void _createComponents(); + + void _printFeatureList(Stream &stream); + + /// print out the processed features to the file stream + void _printFeatures( Stream &stream ); + + void _printDependencies( Stream &stream ); + + void _processPixFeatures( Vector ¯os, bool macrosOnly = false ); + void _printPixShader( Stream &stream ); + + void _processVertFeatures( Vector ¯os, bool macrosOnly = false ); + void _printVertShader( Stream &stream ); + + // For ManagedSingleton. + static const char* getSingletonName() { return "ShaderGen"; } +}; + + +/// Returns the ShaderGen singleton. +#define SHADERGEN ManagedSingleton::instance() + +#endif // _SHADERGEN_H_ diff --git a/Engine/source/shaderGen/shaderGenVars.cpp b/Engine/source/shaderGen/shaderGenVars.cpp new file mode 100644 index 000000000..cf01e5aab --- /dev/null +++ b/Engine/source/shaderGen/shaderGenVars.cpp @@ -0,0 +1,82 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "shaderGen/shaderGenVars.h" + +const String ShaderGenVars::modelview("$modelview"); +const String ShaderGenVars::worldViewOnly("$worldViewOnly"); +const String ShaderGenVars::worldToCamera("$worldToCamera"); +const String ShaderGenVars::worldToObj("$worldToObj"); +const String ShaderGenVars::viewToObj("$viewToObj"); +const String ShaderGenVars::cubeTrans("$cubeTrans"); +const String ShaderGenVars::objTrans("$objTrans"); +const String ShaderGenVars::cubeEyePos("$cubeEyePos"); +const String ShaderGenVars::eyePos("$eyePos"); +const String ShaderGenVars::eyePosWorld("$eyePosWorld"); +const String ShaderGenVars::vEye("$vEye"); +const String ShaderGenVars::eyeMat("$eyeMat"); +const String ShaderGenVars::oneOverFarplane("$oneOverFarplane"); +const String ShaderGenVars::nearPlaneWorld("$nearPlaneWorld"); +const String ShaderGenVars::fogData("$fogData"); +const String ShaderGenVars::fogColor("$fogColor"); +const String ShaderGenVars::detailScale("$detailScale"); +const String ShaderGenVars::visibility("$visibility"); +const String ShaderGenVars::colorMultiply("$colorMultiply"); +const String ShaderGenVars::alphaTestValue("$alphaTestValue"); +const String ShaderGenVars::texMat("$texMat"); +const String ShaderGenVars::accumTime("$accumTime"); +const String ShaderGenVars::minnaertConstant("$minnaertConstant"); +const String ShaderGenVars::subSurfaceParams("$subSurfaceParams"); + +const String ShaderGenVars::diffuseAtlasParams("$diffuseAtlasParams"); +const String ShaderGenVars::diffuseAtlasTileParams("$diffuseAtlasTileParams"); +const String ShaderGenVars::bumpAtlasParams("$bumpAtlasParams"); +const String ShaderGenVars::bumpAtlasTileParams("$bumpAtlasTileParams"); + +const String ShaderGenVars::targetSize("$targetSize"); +const String ShaderGenVars::oneOverTargetSize("$oneOverTargetSize"); + +const String ShaderGenVars::lightPosition("$inLightPos"); +const String ShaderGenVars::lightDiffuse("$inLightColor"); +const String ShaderGenVars::lightAmbient("$ambient"); +const String ShaderGenVars::lightInvRadiusSq("$inLightInvRadiusSq"); +const String ShaderGenVars::lightSpotDir("$inLightSpotDir"); +const String ShaderGenVars::lightSpotAngle("$inLightSpotAngle"); +const String ShaderGenVars::lightSpotFalloff("$inLightSpotFalloff"); +const String ShaderGenVars::specularColor("$specularColor"); +const String ShaderGenVars::specularPower("$specularPower"); + +// These are ignored by the D3D layers. +const String ShaderGenVars::fogMap("$fogMap"); +const String ShaderGenVars::dlightMap("$dlightMap"); +const String ShaderGenVars::dlightMask("$dlightMask"); +const String ShaderGenVars::dlightMapSec("$dlightMapSec"); +const String ShaderGenVars::blackfogMap("$blackfogMap"); +const String ShaderGenVars::bumpMap("$bumpMap"); +const String ShaderGenVars::lightMap("$lightMap"); +const String ShaderGenVars::lightNormMap("$lightNormMap"); +const String ShaderGenVars::cubeMap("$cubeMap"); +const String ShaderGenVars::dLightMap("$dlightMap"); +const String ShaderGenVars::dLightMapSec("$dlightMapSec"); +const String ShaderGenVars::dLightMask("$dlightMask"); +const String ShaderGenVars::toneMap("$toneMap"); \ No newline at end of file diff --git a/Engine/source/shaderGen/shaderGenVars.h b/Engine/source/shaderGen/shaderGenVars.h new file mode 100644 index 000000000..80a308909 --- /dev/null +++ b/Engine/source/shaderGen/shaderGenVars.h @@ -0,0 +1,98 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _SHADERGENVARS_H_ +#define _SHADERGENVARS_H_ + +#ifndef _TORQUE_STRING_H_ +#include "core/util/str.h" +#endif + +/// +/// ShaderGenVars, predefined string names for variables that shadergen based shaders use, this avoids +/// misspelling and string creation issues +/// +struct ShaderGenVars +{ + const static String modelview; + const static String worldViewOnly; + const static String worldToCamera; + const static String worldToObj; + const static String viewToObj; + const static String cubeTrans; + const static String objTrans; + const static String cubeEyePos; + const static String eyePos; + const static String eyePosWorld; + const static String vEye; + const static String eyeMat; + const static String oneOverFarplane; + const static String nearPlaneWorld; + const static String fogData; + const static String fogColor; + const static String detailScale; + const static String visibility; + const static String colorMultiply; + const static String alphaTestValue; + const static String texMat; + const static String accumTime; + const static String minnaertConstant; + const static String subSurfaceParams; + + // Texture atlasing parameters + const static String diffuseAtlasParams; + const static String diffuseAtlasTileParams; + const static String bumpAtlasParams; + const static String bumpAtlasTileParams; + + // Render target parameters + const static String targetSize; + const static String oneOverTargetSize; + + // Lighting parameters used by the default + // RTLighting shader feature. + const static String lightPosition; + const static String lightDiffuse; + const static String lightAmbient; + const static String lightInvRadiusSq; + const static String lightSpotDir; + const static String lightSpotAngle; + const static String lightSpotFalloff; + const static String specularColor; + const static String specularPower; + + // Textures + const static String fogMap; + const static String dlightMap; + const static String dlightMask; + const static String dlightMapSec; + const static String blackfogMap; + const static String bumpMap; + const static String lightMap; + const static String lightNormMap; + const static String cubeMap; + const static String dLightMap; + const static String dLightMapSec; + const static String dLightMask; + const static String toneMap; +}; + +#endif diff --git a/Engine/source/shaderGen/shaderOp.cpp b/Engine/source/shaderGen/shaderOp.cpp new file mode 100644 index 000000000..7df224ed9 --- /dev/null +++ b/Engine/source/shaderGen/shaderOp.cpp @@ -0,0 +1,158 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/strings/stringFunctions.h" +#include + + +#include "shaderOp.h" + +//************************************************************************** +// Shader Operations +//************************************************************************** +ShaderOp::ShaderOp( LangElement *in1, LangElement *in2 ) +{ + mInput[0] = in1; + mInput[1] = in2; +} + +//************************************************************************** +// Declaration Operation - for variables +//************************************************************************** +DecOp::DecOp( Var *in1 ) : Parent( in1, NULL ) +{ + mInput[0] = in1; +} + +//-------------------------------------------------------------------------- +// Print +//-------------------------------------------------------------------------- +void DecOp::print( Stream &stream ) +{ + Var *var = dynamic_cast( mInput[0] ); + + WRITESTR( (char*)var->type ); + WRITESTR( " " ); + + mInput[0]->print( stream ); +} + +//************************************************************************** +// Echo operation - deletes incoming statement! +//************************************************************************** +EchoOp::EchoOp( const char * statement ) : Parent( NULL, NULL ) +{ + mStatement = statement; +} + +//-------------------------------------------------------------------------- +// Destructor +//-------------------------------------------------------------------------- +EchoOp::~EchoOp() +{ + delete [] mStatement; +} + +//-------------------------------------------------------------------------- +// Print +//-------------------------------------------------------------------------- +void EchoOp::print( Stream &stream ) +{ + WRITESTR( mStatement ); +} + + +//************************************************************************** +// General operation +//************************************************************************** +GenOp::GenOp( const char * statement, ... ) : Parent( NULL, NULL ) +{ + VECTOR_SET_ASSOCIATION( mElemList ); + + va_list args; + va_start(args, statement); + + char* lastEntry = (char*)statement; + + while( 1 ) + { + // search 'statement' for @ symbol + char * str = dStrstr( lastEntry, (char *)"@" ); + + if( !str ) + { + // not found, handle end of line + str = (char*)&statement[ dStrlen( (char*)statement ) ]; + + U32 diff = str - lastEntry + 1; + if( diff == 1 ) break; + + char * newStr = new char[diff]; + + dMemcpy( (void*)newStr, lastEntry, diff ); + + mElemList.push_back( new EchoOp( newStr ) ); + + break; + } + + // create and store statement fragment + U32 diff = str - lastEntry + 1; + + if( diff == 1 ) + { + // store langElement + LangElement *elem = va_arg(args, LangElement* ); + AssertFatal( elem, "NULL arguement." ); + mElemList.push_back( elem ); + lastEntry++; + continue; + } + + char * newStr = new char[diff]; + + dMemcpy( (void*)newStr, lastEntry, diff ); + newStr[diff-1] = '\0'; + + lastEntry = str + 1; + + mElemList.push_back( new EchoOp( newStr ) ); + + // store langElement + LangElement *elem = va_arg(args, LangElement* ); + AssertFatal( elem, "NULL argument." ); + mElemList.push_back( elem ); + } + + va_end( args ); +} + +//-------------------------------------------------------------------------- +// Print +//-------------------------------------------------------------------------- +void GenOp::print( Stream &stream ) +{ + for( U32 i=0; iprint( stream ); + } +} diff --git a/Engine/source/shaderGen/shaderOp.h b/Engine/source/shaderGen/shaderOp.h new file mode 100644 index 000000000..6e8170d9a --- /dev/null +++ b/Engine/source/shaderGen/shaderOp.h @@ -0,0 +1,151 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _SHADEROP_H_ +#define _SHADEROP_H_ + +#ifndef _LANG_ELEMENT_H_ +#include "shaderGen/langElement.h" +#endif + +//************************************************************************** +/*! + This file contains "shader operation" classes. Originally they were + to represent basic language operations like adding, assignment, etc. + That proved to be far too verbose when implementing shader features, + so they became more generalized helper classes. Along with LangElement + classes, they are the building blocks for the procedurally generated + shaders. + + Each shader is a linked list of LangElements. The list is generated + when the features of the shader are processed. When all the features + are processed, then ShaderGen prints them out by traversing the linked + list of LangElement and calling their print() function. + + The ShaderOp classes are just extensions of LangElement. + +*/ +//************************************************************************** + + + +///************************************************************************** +/// Shader operation base class +///************************************************************************** +class ShaderOp : public LangElement +{ +protected: + LangElement * mInput[2]; + +public: + ShaderOp( LangElement *in1, LangElement *in2 ); +}; + +//---------------------------------------------------------------------------- +/*! + DecOp - Declaration Operation - Used when declaring a variable in a shader + feature. It will automatically print the type of the variable and then + the variable name. If a shader feature set up code like: + + @code + + Var *foo = new Var; + foo->setType( "float" ); + foo->setName( "foo" ); + LangElement *fooDecl = new DecOp( foo ); + + LangElement *statement = new GenOp( " @ = 8.0 * 5.0;", fooDecl ); + + @endcode + + The output in the shader file would be: + + @code + float foo = 8.0 * 5.0; + @endcode +*/ +//---------------------------------------------------------------------------- +class DecOp : public ShaderOp +{ + typedef ShaderOp Parent; + +public: + DecOp( Var *in1 ); + virtual void print( Stream &stream ); +}; + + +//---------------------------------------------------------------------------- +/*! + Like the name suggests, EchoOp merely echoes back whatever string it is + assigned. +*/ +//---------------------------------------------------------------------------- +class EchoOp : public ShaderOp +{ + typedef ShaderOp Parent; + const char * mStatement; + +public: + EchoOp( const char * statement ); + ~EchoOp(); + virtual void print( Stream &stream ); +}; + + +//---------------------------------------------------------------------------- +/*! + GenOp - General Operation - Very useful for combining several variables + into one LangElement statement. It uses an elipses as a parameter, so it can + take as many variables as you can throw at it. It takes a string and parses + it for the '@' symbol which it replaces with passed in parameters. Similar + to the C statement printf(). Here's an example: + + @code + ( assuming three variables var1, var2, var3 exist and their assigned names + are var1Name, var2Name, and var3Name ) + + LangElement *statement = new GenOp( " @ = @ * @.x + @.y;", var1, var1, var2, var3 ); + + @endcode + + The output in the shader file would be: + + @code + + var1Name = var1Name * var2Name.x + var3Name.y; + + @endcode +*/ +//---------------------------------------------------------------------------- +class GenOp : public ShaderOp +{ + typedef ShaderOp Parent; + + Vector mElemList; + +public: + GenOp( const char * statement, ... ); + virtual void print( Stream &stream ); + +}; + +#endif // _SHADEROP_H_ diff --git a/Engine/source/sim/actionMap.cpp b/Engine/source/sim/actionMap.cpp new file mode 100644 index 000000000..175ee875b --- /dev/null +++ b/Engine/source/sim/actionMap.cpp @@ -0,0 +1,2293 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "sim/actionMap.h" +#include "platform/event.h" +#include "console/console.h" +#include "platform/platform.h" +#include "platform/platformInput.h" +#include "platform/platformAssert.h" +#include "core/stream/fileStream.h" +#include "math/mMathFn.h" +#include "console/engineAPI.h" + +#define CONST_E 2.7182818284590452353602874f + +IMPLEMENT_CONOBJECT(ActionMap); + +ConsoleDocClass( ActionMap, + "@brief ActionMaps assign platform input events to console commands.\n\n" + + "Any platform input event can be bound in a single, generic way. In theory, the game doesn't need to know if the event came from the keyboard, mouse, joystick " + "or some other input device. This allows users of the game to map keys and actions according to their own preferences. " + "Game action maps are arranged in a stack for processing so individual parts of the game can define specific " + "actions. For example, when the player jumps into a vehicle it could push a vehicle action map and pop the default player action map.\n\n" + + "@section ActionMap_creation Creating an ActionMap\n" + + "The input system allows for the creation of multiple ActionMaps, so long as they have unique names and do not already exist. It's a simple " + "three step process.\n\n" + "1. Check to see if the ActionMap exists\n" + "2. Delete it if it exists\n" + "3. Instantiate the ActionMap\n\n" + + "The following is an example of how to create a new ActionMap:\n" + + "@tsexample\n" + "if ( isObject( moveMap ) )\n" + " moveMap.delete();\n" + "new ActionMap(moveMap);" + "@endtsexample\n\n\n" + + "@section ActionMap_binding Binding Functions\n" + "Once you have created an ActionMap, you can start binding functionality to events. Currently, Torque 3D supports the following devices out of the box\n\n" + "* Mouse\n\n" + "* Keyboard\n\n" + "* Joystick/Gamepad\n\n" + "* Xbox 360 Controller\n\n" + + "The two most commonly used binding methods are bind() and bindCmd(). Both are similar in that they will bind functionality to a device and event, " + "but different in how the event is interpreted. With bind(), " + "you specify a device, action to bind, then a function to be called when the event happens.\n\n" + + "@tsexample\n" + "// Simple function that prints to console\n" + "// %val - Sent by the device letting the user know\n" + "// if an input was pressed (true) or released (false)\n" + "function testInput(%val)\n" + "{\n" + " if(%val)\n" + " echo(\"Key is down\");\n" + " else\n" + " echo(\"Key was released\");\n" + "}\n\n" + "// Bind the \'K\' key to the testInput function\n" + "moveMap.bind(keyboard, \"k\", testInput);\n\n" + "@endtsexample\n\n\n" + + "bindCmd is an alternative method for binding commands. This function is similar to bind(), " + "except two functions are set to be called when the event is processed.\n\n" + "One will be called when the event is activated (input down), while the other is activated when the event is broken (input release). " + "When using bindCmd(), pass the functions as strings rather than the function names.\n\n" + + "@tsexample\n" + "// Print to the console when the spacebar is pressed\n" + "function onSpaceDown()\n" + "{\n" + " echo(\"Space bar down!\");\n" + "}\n\n" + + "// Print to the console when the spacebar is released\n" + "function onSpaceUp()\n" + "{\n" + " echo(\"Space bar up!\");\n" + "}\n\n" + + "// Bind the commands onSpaceDown and onSpaceUp to spacebar events\n" + "moveMap.bindCmd(keyboard, \"space\", \"onSpaceDown();\", \"onSpaceUp();\");\n" + "@endtsexample\n\n" + + "@section ActionMap_switching Switching ActionMaps\n" + "Let's say you want to have different ActionMaps activated based on game play situations. A classic example would be first person shooter controls and racing controls " + "in the same game. On foot, spacebar may cause your player to jump. In a vehicle, it may cause some kind of \"turbo charge\". You simply need to push/pop the ActionMaps appropriately:\n\n" + + "First, create two separate ActionMaps:\n\n" + "@tsexample\n" + "// Create the two ActionMaps\n" + "if ( isObject( moveMap ) )\n" + " moveMap.delete();\n" + "new ActionMap(moveMap);\n\n" + "if ( isObject( carMap ) )\n" + " carMap.delete();\n" + "new ActionMap(carMap);\n\n" + "@endtsexample\n\n" + + "Next, create the two separate functions. Both will be bound to spacebar, but not the same ActionMap:\n\n" + "@tsexample\n" + "// Print to the console the player is jumping\n" + "function playerJump(%val)\n" + "{\n" + " if(%val)\n" + " echo(\"Player jumping!\");\n" + "}\n\n" + "// Print to the console the vehicle is charging\n" + "function turboCharge()\n" + "{\n" + " if(%val)\n" + " echo(\"Vehicle turbo charging!\");\n" + "}\n" + "@endtsexample\n\n" + + "You are now ready to bind functions to your ActionMaps' devices:\n\n" + + "@tsexample\n" + "// Bind the spacebar to the playerJump function\n" + "// when moveMap is the active ActionMap\n" + "moveMap.bind(keyboard, \"space\", playerJump);\n\n" + "// Bind the spacebar to the turboCharge function\n" + "// when carMap is the active ActionMap\n" + "carMap.bind(keyboard, \"space\", turboCharge);\n" + "@endtsexample\n" + + "Finally, you can use the push() and pop() commands on each ActionMap to toggle activation. To activate an ActionMap, use push():\n\n" + + "@tsexample\n" + "// Make moveMap the active action map\n" + "// You should now be able to activate playerJump with spacebar\n" + "moveMap.push();\n" + "@endtsexample\n\n" + + "To switch ActionMaps, first pop() the old one. Then you can push() the new one:\n\n" + + "@tsexample\n" + "// Deactivate moveMap\n" + "moveMap.pop();\n\n" + "// Activate carMap\n" + "carMap.push();\n\n" + "@endtsexample\n\n\n" + + "@ingroup Input" + +); + +// This is used for determing keys that have ascii codes for the foreign keyboards. IsAlpha doesn't work on foreign keys. +static inline bool dIsDecentChar(U8 c) +{ + return ((U8(0xa0) <= c) || (( U8(0x21) <= c) && (c <= U8(0x7e))) || ((U8(0x91) <= c) && (c <= U8(0x92)))); +} + +struct CodeMapping +{ + const char* pDescription; + InputEventType type; + InputObjectInstances code; +}; + +struct AsciiMapping +{ + const char* pDescription; + U16 asciiCode; +}; + +extern CodeMapping gVirtualMap[]; +extern AsciiMapping gAsciiMap[]; + +//------------------------------------------------------------------------------ +//-------------------------------------- Action maps +// +Vector ActionMap::smBreakTable(__FILE__, __LINE__); + + +//------------------------------------------------------------------------------ +ActionMap::ActionMap() +{ + VECTOR_SET_ASSOCIATION(mDeviceMaps); +} + +//------------------------------------------------------------------------------ +ActionMap::~ActionMap() +{ + for (U32 i = 0; i < mDeviceMaps.size(); i++) + delete mDeviceMaps[i]; + mDeviceMaps.clear(); +} + +//------------------------------------------------------------------------------ +ActionMap::DeviceMap::~DeviceMap() +{ + for(U32 i = 0; i < nodeMap.size(); i++) + { + dFree(nodeMap[i].makeConsoleCommand); + dFree(nodeMap[i].breakConsoleCommand); + } +} + +//------------------------------------------------------------------------------ +bool ActionMap::onAdd() +{ + if (Parent::onAdd() == false) + return false; + + Sim::getActionMapGroup()->addObject(this); + + return true; +} + +//-------------------------------------------------------------------------- +void ActionMap::dumpActionMap(const char* fileName, const bool append) const +{ + if (fileName != NULL) { + // Dump the deletion, and creation script commands, followed by all the binds + // to a script. + + FileStream *iostrm; + if((iostrm = FileStream::createAndOpen( fileName, append ? Torque::FS::File::WriteAppend : Torque::FS::File::Write )) == NULL) + { + Con::errorf( "Unable to open file '%s' for writing.", fileName ); + return; + } + + char lineBuffer[1024]; + if ( append ) + iostrm->setPosition( iostrm->getStreamSize() ); + else + { + // IMPORTANT -- do NOT change the following line, it identifies the file as an input map file + dStrcpy( lineBuffer, "// Torque Input Map File\n" ); + iostrm->write( dStrlen( lineBuffer ), lineBuffer ); + } + + dSprintf(lineBuffer, 1023, "if (isObject(%s)) %s.delete();\n" + "new ActionMap(%s);\n", getName(), getName(), getName()); + iostrm->write(dStrlen(lineBuffer), lineBuffer); + + // Dump all the binds to the console... + for (S32 i = 0; i < mDeviceMaps.size(); i++) { + const DeviceMap* pDevMap = mDeviceMaps[i]; + + char devbuffer[32]; + getDeviceName(pDevMap->deviceType, pDevMap->deviceInst, devbuffer); + + for (S32 j = 0; j < pDevMap->nodeMap.size(); j++) { + const Node& rNode = pDevMap->nodeMap[j]; + + const char* pModifierString = getModifierString(rNode.modifiers); + + char objectbuffer[64]; + if (getKeyString(rNode.action, objectbuffer) == false) + continue; + + const char* command = (rNode.flags & Node::BindCmd) ? "bindCmd" : "bind"; + + dSprintf(lineBuffer, 1023, "%s.%s(%s, \"%s%s\"", + getName(), + command, + devbuffer, + pModifierString, objectbuffer); + + if (rNode.flags & (Node::HasScale|Node::HasDeadZone|Node::Ranged|Node::Inverted)) { + char buff[10]; + U32 curr = 0; + buff[curr++] = ','; + buff[curr++] = ' '; + if (rNode.flags & Node::HasScale) + buff[curr++] = 'S'; + if (rNode.flags & Node::Ranged) + buff[curr++] = 'R'; + if (rNode.flags & Node::HasDeadZone) + buff[curr++] = 'D'; + if (rNode.flags & Node::Inverted) + buff[curr++] = 'I'; + buff[curr] = '\0'; + + dStrcat(lineBuffer, buff); + } + + if (rNode.flags & Node::HasDeadZone) { + char buff[64]; + dSprintf(buff, 63, ", \"%g %g\"", rNode.deadZoneBegin, rNode.deadZoneEnd); + dStrcat(lineBuffer, buff); + } + + if (rNode.flags & Node::HasScale) { + char buff[64]; + dSprintf(buff, 63, ", %g", rNode.scaleFactor); + dStrcat(lineBuffer, buff); + } + + if (rNode.flags & Node::BindCmd) { + if (rNode.makeConsoleCommand) { + dStrcat(lineBuffer, ", \""); + U32 pos = dStrlen(lineBuffer); + expandEscape(lineBuffer + pos, rNode.makeConsoleCommand); + dStrcat(lineBuffer, "\""); + } else { + dStrcat(lineBuffer, ", \"\""); + } + if (rNode.breakConsoleCommand) { + dStrcat(lineBuffer, ", \""); + U32 pos = dStrlen(lineBuffer); + expandEscape(lineBuffer + pos, rNode.breakConsoleCommand); + dStrcat(lineBuffer, "\""); + } + else + dStrcat(lineBuffer, ", \"\""); + } else { + dStrcat(lineBuffer, ", "); + dStrcat(lineBuffer, rNode.consoleFunction); + } + + dStrcat(lineBuffer, ");\n"); + iostrm->write(dStrlen(lineBuffer), lineBuffer); + } + } + + delete iostrm; + } + else { + // Dump all the binds to the console... + for (S32 i = 0; i < mDeviceMaps.size(); i++) { + const DeviceMap* pDevMap = mDeviceMaps[i]; + + char devbuffer[32]; + getDeviceName(pDevMap->deviceType, pDevMap->deviceInst, devbuffer); + + for (S32 j = 0; j < pDevMap->nodeMap.size(); j++) { + const Node& rNode = pDevMap->nodeMap[j]; + + const char* pModifierString = getModifierString(rNode.modifiers); + + char keybuffer[64]; + if (getKeyString(rNode.action, keybuffer) == false) + continue; + + const char* command = (rNode.flags & Node::BindCmd) ? "bindCmd" : "bind"; + + char finalBuffer[1024]; + dSprintf(finalBuffer, 1023, "%s.%s(%s, \"%s%s\"", + getName(), + command, + devbuffer, + pModifierString, keybuffer); + + if (rNode.flags & (Node::HasScale|Node::HasDeadZone|Node::Ranged|Node::Inverted)) { + char buff[10]; + U32 curr = 0; + buff[curr++] = ','; + buff[curr++] = ' '; + if (rNode.flags & Node::HasScale) + buff[curr++] = 'S'; + if (rNode.flags & Node::Ranged) + buff[curr++] = 'R'; + if (rNode.flags & Node::HasDeadZone) + buff[curr++] = 'D'; + if (rNode.flags & Node::Inverted) + buff[curr++] = 'I'; + buff[curr] = '\0'; + + dStrcat(finalBuffer, buff); + } + + if (rNode.flags & Node::HasDeadZone) { + char buff[64]; + dSprintf(buff, 63, ", \"%g %g\"", rNode.deadZoneBegin, rNode.deadZoneEnd); + dStrcat(finalBuffer, buff); + } + + if (rNode.flags & Node::HasScale) { + char buff[64]; + dSprintf(buff, 63, ", %g", rNode.scaleFactor); + dStrcat(finalBuffer, buff); + } + + if (rNode.flags & Node::BindCmd) { + if (rNode.makeConsoleCommand) { + dStrcat(finalBuffer, ", \""); + dStrcat(finalBuffer, rNode.makeConsoleCommand); + dStrcat(finalBuffer, "\""); + } else { + dStrcat(finalBuffer, ", \"\""); + } + if (rNode.breakConsoleCommand) { + dStrcat(finalBuffer, ", \""); + dStrcat(finalBuffer, rNode.breakConsoleCommand); + dStrcat(finalBuffer, "\""); + } + else + dStrcat(finalBuffer, ", \"\""); + } else { + dStrcat(finalBuffer, ", "); + dStrcat(finalBuffer, rNode.consoleFunction); + } + + dStrcat(finalBuffer, ");"); + Con::printf(finalBuffer); + } + } + } +} + +//-------------------------------------------------------------------------- +bool ActionMap::createEventDescriptor(const char* pEventString, EventDescriptor* pDescriptor) +{ + char copyBuffer[256]; + dStrcpy(copyBuffer, pEventString); + + // Do we have modifiers? + char* pSpace = dStrchr(copyBuffer, ' '); + char* pObjectString; + if (pSpace != NULL) { + // Yes. Parse them out... + // + pDescriptor->flags = 0; + pObjectString = pSpace + 1; + pSpace[0] = '\0'; + + char* pModifier = dStrtok(copyBuffer, "-"); + while (pModifier != NULL) { + if (dStricmp(pModifier, "shift") == 0) { + pDescriptor->flags |= SI_SHIFT; + } else if (dStricmp(pModifier, "ctrl") == 0) { + pDescriptor->flags |= SI_CTRL; + } else if (dStricmp(pModifier, "alt") == 0) { + pDescriptor->flags |= SI_ALT; + } else if (dStricmp(pModifier, "cmd") == 0) { + pDescriptor->flags |= SI_ALT; + } else if (dStricmp(pModifier, "opt") == 0) { + pDescriptor->flags |= SI_MAC_OPT; + } + + pModifier = dStrtok(NULL, "-"); + } + } else { + // No. + pDescriptor->flags = 0; + pObjectString = copyBuffer; + } + + // Now we need to map the key string to the proper KEY code from event.h + // + AssertFatal(dStrlen(pObjectString) != 0, "Error, no key was specified!"); + + if (dStrlen(pObjectString) == 1) + { + if (dIsDecentChar(*pObjectString)) // includes foreign chars + { + U16 asciiCode = (*pObjectString); + // clear out the FF in upper 8bits for foreign keys?? + asciiCode &= 0xFF; + U16 keyCode = Input::getKeyCode(asciiCode); + if ( keyCode >= KEY_0 ) + { + pDescriptor->eventType = SI_KEY; + pDescriptor->eventCode = keyCode; + return true; + } + else if (dIsalpha(*pObjectString) == true) + { + pDescriptor->eventType = SI_KEY; + pDescriptor->eventCode = KEY_A+dTolower(*pObjectString)-'a'; + return true; + } + else if (dIsdigit(*pObjectString) == true) + { + pDescriptor->eventType = SI_KEY; + pDescriptor->eventCode = KEY_0+(*pObjectString)-'0'; + return true; + } + } + return false; + } + else + { + pDescriptor->eventCode = 0; + // Gotta search through the Ascii table... + for (U16 i = 0; gAsciiMap[i].asciiCode != 0xFFFF; i++) + { + if (dStricmp(pObjectString, gAsciiMap[i].pDescription) == 0) + { + U16 asciiCode = gAsciiMap[i].asciiCode; + U16 keyCode = Input::getKeyCode(asciiCode); + if ( keyCode >= KEY_0 ) + { + pDescriptor->eventType = SI_KEY; + pDescriptor->eventCode = keyCode; + return(true); + + } + else + { + break; + } + } + } + // Didn't find an ascii match. Check the virtual map table + for (U32 j = 0; gVirtualMap[j].code != 0xFFFFFFFF; j++) + { + if (dStricmp(pObjectString, gVirtualMap[j].pDescription) == 0) + { + pDescriptor->eventType = gVirtualMap[j].type; + pDescriptor->eventCode = gVirtualMap[j].code; + return true; + } + } + } + return false; +} + +//------------------------------------------------------------------------------ +ActionMap::Node* ActionMap::getNode(const U32 inDeviceType, const U32 inDeviceInst, + const U32 inModifiers, const U32 inAction,SimObject* object /*= NULL*/) +{ + // DMMTODO - Slow INITIAL implementation. Replace with a faster version... + // + DeviceMap* pDeviceMap = NULL; + U32 i; + for (i = 0; i < mDeviceMaps.size(); i++) + { + if (mDeviceMaps[i]->deviceType == inDeviceType && + mDeviceMaps[i]->deviceInst == inDeviceInst) { + pDeviceMap = mDeviceMaps[i]; + break; + } + } + if (pDeviceMap == NULL) + { + mDeviceMaps.increment(); + mDeviceMaps.last() = new DeviceMap; + pDeviceMap = mDeviceMaps.last(); + + pDeviceMap->deviceInst = inDeviceInst; + pDeviceMap->deviceType = inDeviceType; + } + + for (i = 0; i < pDeviceMap->nodeMap.size(); i++) + { + if (pDeviceMap->nodeMap[i].modifiers == inModifiers && + pDeviceMap->nodeMap[i].action == inAction && + ( (object != NULL) ? object == pDeviceMap->nodeMap[i].object : true )) // Check for an object match if the object exists + { + return &pDeviceMap->nodeMap[i]; + } + } + + // If we're here, the node doesn't exist. create it. + pDeviceMap->nodeMap.increment(); + + Node* pRetNode = &pDeviceMap->nodeMap.last(); + pRetNode->modifiers = inModifiers; + pRetNode->action = inAction; + + pRetNode->flags = 0; + pRetNode->deadZoneBegin = 0.0; + pRetNode->deadZoneEnd = 0.0; + pRetNode->scaleFactor = 1.0; + + pRetNode->consoleFunction = NULL; + pRetNode->makeConsoleCommand = NULL; + pRetNode->breakConsoleCommand = NULL; + + //[neob, 5/7/2007 - #2975] + pRetNode->object = 0; + + return pRetNode; +} + +//------------------------------------------------------------------------------ +void ActionMap::removeNode(const U32 inDeviceType, const U32 inDeviceInst, const U32 inModifiers, const U32 inAction, SimObject* object /*= NULL*/) +{ + // DMMTODO - Slow INITIAL implementation. Replace with a faster version... + // + DeviceMap* pDeviceMap = NULL; + U32 i; + for (i = 0; i < mDeviceMaps.size(); i++) { + if (mDeviceMaps[i]->deviceType == inDeviceType && + mDeviceMaps[i]->deviceInst == inDeviceInst) { + pDeviceMap = mDeviceMaps[i]; + break; + } + } + + if (pDeviceMap == NULL) + return; + + U32 realMods = inModifiers; + if (realMods & SI_SHIFT) + realMods |= SI_SHIFT; + if (realMods & SI_CTRL) + realMods |= SI_CTRL; + if (realMods & SI_ALT) + realMods |= SI_ALT; + if (realMods & SI_MAC_OPT) + realMods |= SI_MAC_OPT; + + for (i = 0; i < pDeviceMap->nodeMap.size(); i++) { + if (pDeviceMap->nodeMap[i].modifiers == realMods && + pDeviceMap->nodeMap[i].action == inAction && + ( (object != NULL) ? object == pDeviceMap->nodeMap[i].object : true )) + { + dFree(pDeviceMap->nodeMap[i].makeConsoleCommand); + dFree(pDeviceMap->nodeMap[i].breakConsoleCommand); + pDeviceMap->nodeMap.erase(i); + } + } +} + +//------------------------------------------------------------------------------ +const ActionMap::Node* ActionMap::findNode(const U32 inDeviceType, const U32 inDeviceInst, + const U32 inModifiers, const U32 inAction) +{ + // DMMTODO - Slow INITIAL implementation. Replace with a faster version... + // + DeviceMap* pDeviceMap = NULL; + U32 i; + for (i = 0; i < mDeviceMaps.size(); i++) + { + if (mDeviceMaps[i]->deviceType == inDeviceType && mDeviceMaps[i]->deviceInst == inDeviceInst) + { + pDeviceMap = mDeviceMaps[i]; + break; + } + } + + if (pDeviceMap == NULL) + return NULL; + + U32 realMods = inModifiers; + if (realMods & SI_SHIFT) + realMods |= SI_SHIFT; + if (realMods & SI_CTRL) + realMods |= SI_CTRL; + if (realMods & SI_ALT) + realMods |= SI_ALT; + if (realMods & SI_MAC_OPT) + realMods |= SI_MAC_OPT; + + for (i = 0; i < pDeviceMap->nodeMap.size(); i++) + { + // Special case for an ANYKEY bind... + if (pDeviceMap->nodeMap[i].action == KEY_ANYKEY + && pDeviceMap->nodeMap[i].modifiers == realMods + && dIsDecentChar(inAction) + && inAction <= U8_MAX) + return &pDeviceMap->nodeMap[i]; + + if (pDeviceMap->nodeMap[i].modifiers == realMods + && pDeviceMap->nodeMap[i].action == inAction) + return &pDeviceMap->nodeMap[i]; + } + + return NULL; +} + +//------------------------------------------------------------------------------ +bool ActionMap::findBoundNode( const char* function, U32 &devMapIndex, U32 &nodeIndex ) +{ + devMapIndex = 0; + nodeIndex = 0; + return nextBoundNode( function, devMapIndex, nodeIndex ); +} + +bool ActionMap::nextBoundNode( const char* function, U32 &devMapIndex, U32 &nodeIndex ) +{ + // Loop through all of the existing nodes to find the one mapped to the + // given function: + for ( U32 i = devMapIndex; i < mDeviceMaps.size(); i++ ) + { + const DeviceMap* dvcMap = mDeviceMaps[i]; + + for ( U32 j = nodeIndex; j < dvcMap->nodeMap.size(); j++ ) + { + const Node* node = &dvcMap->nodeMap[j]; + if ( !( node->flags & Node::BindCmd ) && ( dStricmp( function, node->consoleFunction ) == 0 ) ) + { + devMapIndex = i; + nodeIndex = j; + return( true ); + } + } + + nodeIndex = 0; + } + + return( false ); +} + +//------------------------------------------------------------------------------ +bool ActionMap::processUnbind(const char *device, const char *action, SimObject* object /*= NULL*/) +{ + U32 deviceType; + U32 deviceInst; + + if(!getDeviceTypeAndInstance(device, deviceType, deviceInst)) + return false; + EventDescriptor eventDescriptor; + if (!createEventDescriptor(action, &eventDescriptor)) + return false; + + removeNode(deviceType, deviceInst, eventDescriptor.flags,eventDescriptor.eventCode, object); + return true; +} + +//------------------------------------------------------------------------------ +// This function is for the use of the control remapper. +// It will only check against the console function (since all remappable commands are +// bound using bind and not bindCmd). +// +const char* ActionMap::getBinding( const char* command ) +{ + char* returnString = Con::getReturnBuffer( 1024 ); + returnString[0] = 0; + + char buffer[256]; + char deviceBuffer[32]; + char keyBuffer[64]; + + U32 devMapIndex = 0, nodeIndex = 0; + while ( nextBoundNode( command, devMapIndex, nodeIndex ) ) + { + const DeviceMap* deviceMap = mDeviceMaps[devMapIndex]; + + if ( getDeviceName( deviceMap->deviceType, deviceMap->deviceInst, deviceBuffer ) ) + { + const Node* node = &deviceMap->nodeMap[nodeIndex]; + const char* modifierString = getModifierString( node->modifiers ); + + if ( getKeyString( node->action, keyBuffer ) ) + { + dSprintf( buffer, sizeof( buffer ), "%s\t%s%s", deviceBuffer, modifierString, keyBuffer ); + if ( returnString[0] ) + dStrcat( returnString, "\t" ); + dStrcat( returnString, buffer ); + } + } + + ++nodeIndex; + } + + return returnString; +} + +//------------------------------------------------------------------------------ +// This function is for the use of the control remapper. +// The intent of this function is to determine if the given event descriptor is already +// bound in this action map. If so, this function returns the command it is bound to. +// If not, it returns NULL. +// +const char* ActionMap::getCommand( const char* device, const char* action ) +{ + U32 deviceType; + U32 deviceInst; + if ( getDeviceTypeAndInstance( device, deviceType, deviceInst ) ) + { + EventDescriptor eventDescriptor; + if ( createEventDescriptor( action, &eventDescriptor ) ) + { + const ActionMap::Node* mapNode = findNode( deviceType, deviceInst, eventDescriptor.flags, eventDescriptor.eventCode ); + if ( mapNode ) + { + if ( mapNode->flags & Node::BindCmd ) + { + S32 bufferLen = dStrlen( mapNode->makeConsoleCommand ) + dStrlen( mapNode->breakConsoleCommand ) + 2; + char* returnString = Con::getReturnBuffer( bufferLen ); + dSprintf( returnString, bufferLen, "%s\t%s", + ( mapNode->makeConsoleCommand ? mapNode->makeConsoleCommand : "" ), + ( mapNode->breakConsoleCommand ? mapNode->breakConsoleCommand : "" ) ); + return( returnString ); + } + else + return( mapNode->consoleFunction ); + } + } + } + + return( "" ); +} + +//------------------------------------------------------------------------------ +// This function returns whether or not the mapping specified is inverted. +// Obviously, this should only be used for axes. +bool ActionMap::isInverted( const char* device, const char* action ) +{ + U32 deviceType; + U32 deviceInst; + if ( getDeviceTypeAndInstance( device, deviceType, deviceInst ) ) + { + EventDescriptor eventDescriptor; + if ( createEventDescriptor( action, &eventDescriptor ) ) + { + const ActionMap::Node* mapNode = findNode( deviceType, deviceInst, eventDescriptor.flags, eventDescriptor.eventCode ); + if ( mapNode ) + return( mapNode->flags & Node::Inverted ); + } + } + + Con::errorf( "The input event specified by %s %s is not in this action map!", device, action ); + return( false ); +} + +//------------------------------------------------------------------------------ +F32 ActionMap::getScale( const char* device, const char* action ) +{ + U32 deviceType; + U32 deviceInst; + if ( getDeviceTypeAndInstance( device, deviceType, deviceInst ) ) + { + EventDescriptor eventDescriptor; + if ( createEventDescriptor( action, &eventDescriptor ) ) + { + const ActionMap::Node* mapNode = findNode( deviceType, deviceInst, eventDescriptor.flags, eventDescriptor.eventCode ); + if ( mapNode ) + { + if ( mapNode->flags & Node::HasScale ) + return( mapNode->scaleFactor ); + else + return( 1.0f ); + } + } + } + + Con::errorf( "The input event specified by %s %s is not in this action map!", device, action ); + return( 1.0f ); +} + +//------------------------------------------------------------------------------ +const char* ActionMap::getDeadZone( const char* device, const char* action ) +{ + U32 deviceType; + U32 deviceInst; + if ( getDeviceTypeAndInstance( device, deviceType, deviceInst ) ) + { + EventDescriptor eventDescriptor; + if ( createEventDescriptor( action, &eventDescriptor ) ) + { + const ActionMap::Node* mapNode = findNode( deviceType, deviceInst, eventDescriptor.flags, eventDescriptor.eventCode ); + if ( mapNode ) + { + if ( mapNode->flags & Node::HasDeadZone ) + { + char buf[64]; + dSprintf( buf, sizeof( buf ), "%g %g", mapNode->deadZoneBegin, mapNode->deadZoneEnd ); + char* returnString = Con::getReturnBuffer( dStrlen( buf ) + 1 ); + dStrcpy( returnString, buf ); + return( returnString ); + } + else + return( "0 0" ); + } + } + } + + Con::errorf( "The input event specified by %s %s is not in this action map!", device, action ); + return( "" ); +} + +//------------------------------------------------------------------------------ +const char* ActionMap::buildActionString( const InputEventInfo* event ) +{ + const char* modifierString = getModifierString( event->modifier ); + + char objectBuffer[64]; + if ( !getKeyString( event->objInst, objectBuffer ) ) + return( "" ); + + U32 returnLen = dStrlen( modifierString ) + dStrlen( objectBuffer ) + 2; + char* returnString = Con::getReturnBuffer( returnLen ); + dSprintf( returnString, returnLen - 1, "%s%s", modifierString, objectBuffer ); + return( returnString ); +} + +//------------------------------------------------------------------------------ +bool ActionMap::getDeviceTypeAndInstance(const char *pDeviceName, U32 &deviceType, U32 &deviceInstance) +{ + U32 offset = 0; + + if (dStrnicmp(pDeviceName, "keyboard", dStrlen("keyboard")) == 0) + { + deviceType = KeyboardDeviceType; + offset = dStrlen("keyboard"); + } + else if (dStrnicmp(pDeviceName, "mouse", dStrlen("mouse")) == 0) + { + deviceType = MouseDeviceType; + offset = dStrlen("mouse"); + } + else if (dStrnicmp(pDeviceName, "joystick", dStrlen("joystick")) == 0) + { + deviceType = JoystickDeviceType; + offset = dStrlen("joystick"); + } + else if (dStrnicmp(pDeviceName, "gamepad", dStrlen("gamepad")) == 0) + { + deviceType = GamepadDeviceType; + offset = dStrlen("gamepad"); + } + else + { + return false; + } + + if (dStrlen(pDeviceName) > offset) + { + const char* pInst = pDeviceName + offset; + S32 instNum = dAtoi(pInst); + + if (instNum < 0) + deviceInstance = 0; + else + deviceInstance = instNum; + } + else + { + deviceInstance = 0; + } + + return true; +} + +//------------------------------------------------------------------------------ +bool ActionMap::getDeviceName(const U32 deviceType, const U32 deviceInstance, char* buffer) +{ + switch (deviceType) { + case KeyboardDeviceType: + dStrcpy(buffer, "keyboard"); + break; + + case MouseDeviceType: + dSprintf(buffer, 16, "mouse%d", deviceInstance); + break; + + case JoystickDeviceType: + dSprintf(buffer, 16, "joystick%d", deviceInstance); + break; + + case GamepadDeviceType: + dSprintf(buffer, 16, "gamepad%d", deviceInstance); + break; + + default: + Con::errorf( "ActionMap::getDeviceName: unknown device type specified, %d (inst: %d)", deviceType, deviceInstance); + return false; + } + + return true; +} + +//------------------------------------------------------------------------------ +const char* ActionMap::getModifierString(const U32 modifiers) +{ + U32 realModifiers = modifiers; + if ( modifiers & SI_LSHIFT || modifiers & SI_RSHIFT ) + realModifiers |= SI_SHIFT; + if ( modifiers & SI_LCTRL || modifiers & SI_RCTRL ) + realModifiers |= SI_CTRL; + if ( modifiers & SI_LALT || modifiers & SI_RALT ) + realModifiers |= SI_ALT; + if ( modifiers & SI_MAC_LOPT || modifiers & SI_MAC_ROPT ) + realModifiers |= SI_MAC_OPT; + + switch (realModifiers & (SI_SHIFT|SI_CTRL|SI_ALT|SI_MAC_OPT)) + { +#if defined(TORQUE_OS_MAC) + // optional code, to output alt as cmd on mac. + // interpreter sees them as the same... + case (SI_SHIFT|SI_CTRL|SI_ALT): + return "cmd-shift-ctrl "; + + case (SI_SHIFT|SI_ALT): + return "cmd-shift "; + + case (SI_CTRL|SI_ALT): + return "cmd-ctrl "; + + case (SI_ALT): + return "cmd "; +#else + case (SI_SHIFT|SI_CTRL|SI_ALT): + return "shift-ctrl-alt "; + + case (SI_SHIFT|SI_ALT): + return "shift-alt "; + + case (SI_CTRL|SI_ALT): + return "ctrl-alt "; + + case (SI_ALT): + return "alt "; +#endif + case (SI_SHIFT|SI_CTRL): + return "shift-ctrl "; + + case (SI_SHIFT): + return "shift "; + + case (SI_CTRL): + return "ctrl "; + +// plus new mac cases: + case (SI_ALT|SI_SHIFT|SI_CTRL|SI_MAC_OPT): + return "cmd-shift-ctrl-opt "; + + case (SI_ALT|SI_SHIFT|SI_MAC_OPT): + return "cmd-shift-opt "; + + case (SI_ALT|SI_CTRL|SI_MAC_OPT): + return "cmd-ctrl-opt "; + + case (SI_ALT|SI_MAC_OPT): + return "cmd-opt "; + + case (SI_SHIFT|SI_CTRL|SI_MAC_OPT): + return "shift-ctrl-opt "; + + case (SI_SHIFT|SI_MAC_OPT): + return "shift-opt "; + + case (SI_CTRL|SI_MAC_OPT): + return "ctrl-opt "; + + case (SI_MAC_OPT): + return "opt "; + + case 0: + return ""; + + default: + AssertFatal(false, "Error, should never reach the default case in getModifierString"); + return ""; + } +} + +//------------------------------------------------------------------------------ +bool ActionMap::getKeyString(const U32 action, char* buffer) +{ + U16 asciiCode = 0; + + // This is a special case.... numpad keys do have ascii values + // but for the purposes of this method we want to return the + // description from the gVirtualMap. + if ( !( KEY_NUMPAD0 <= action && action <= KEY_NUMPAD9 ) ) + asciiCode = Input::getAscii( action, STATE_LOWER ); + +// if (action >= KEY_A && action <= KEY_Z) { +// buffer[0] = char(action - KEY_A + 'a'); +// buffer[1] = '\0'; +// return true; +// } else if (action >= KEY_0 && action <= KEY_9) { +// buffer[0] = char(action - KEY_0 + '0'); +// buffer[1] = '\0'; + if ( (asciiCode != 0) && dIsDecentChar((char)asciiCode)) + { + for (U32 i = 0; gAsciiMap[i].asciiCode != 0xFFFF; i++) { + if (gAsciiMap[i].asciiCode == asciiCode) + { + dStrcpy(buffer, gAsciiMap[i].pDescription); + return true; + } + } + // Must not have found a string for that ascii code just record the char + buffer[0] = char(asciiCode); + buffer[1] = '\0'; + return true; + } + else + { + if (action >= KEY_A && action <= KEY_Z) + { + buffer[0] = char(action - KEY_A + 'a'); + buffer[1] = '\0'; + return true; + } + else if (action >= KEY_0 && action <= KEY_9) { + buffer[0] = char(action - KEY_0 + '0'); + buffer[1] = '\0'; + return true; + } + for (U32 i = 0; gVirtualMap[i].code != 0xFFFFFFFF; i++) { + if (gVirtualMap[i].code == action) { + dStrcpy(buffer, gVirtualMap[i].pDescription); + return true; + } + } + } + + Con::errorf( "ActionMap::getKeyString: no string for action %d", action ); + return false; +} + +//-------------------------------------------------------------------------- +bool ActionMap::processBindCmd(const char *device, const char *action, const char *makeCmd, const char *breakCmd) +{ + U32 deviceType; + U32 deviceInst; + + if(!getDeviceTypeAndInstance(device, deviceType, deviceInst)) + { + Con::printf("processBindCmd: unknown device: %s", device); + return false; + } + + // Ok, we now have the deviceType and instance. Create an event descriptor + // for the bind... + // + EventDescriptor eventDescriptor; + if (createEventDescriptor(action, &eventDescriptor) == false) { + Con::printf("Could not create a description for binding: %s", action); + return false; + } + + // SI_POV == SI_MOVE, and the POV works fine with bindCmd, so we have to add these manually. + if( ( eventDescriptor.eventCode == SI_XAXIS ) || + ( eventDescriptor.eventCode == SI_YAXIS ) || + ( eventDescriptor.eventCode == SI_ZAXIS ) || + ( eventDescriptor.eventCode == SI_RXAXIS ) || + ( eventDescriptor.eventCode == SI_RYAXIS ) || + ( eventDescriptor.eventCode == SI_RZAXIS ) || + ( eventDescriptor.eventCode == SI_SLIDER ) || + ( eventDescriptor.eventCode == SI_XPOV ) || + ( eventDescriptor.eventCode == SI_YPOV ) || + ( eventDescriptor.eventCode == SI_XPOV2 ) || + ( eventDescriptor.eventCode == SI_YPOV2 ) ) + { + Con::warnf( "ActionMap::processBindCmd - Cannot use 'bindCmd' with a move event type. Use 'bind' instead." ); + return false; + } + + // Create the full bind entry, and place it in the map + // + // DMMTODO + Node* pBindNode = getNode(deviceType, deviceInst, + eventDescriptor.flags, + eventDescriptor.eventCode); + + pBindNode->flags = Node::BindCmd; + pBindNode->deadZoneBegin = 0; + pBindNode->deadZoneEnd = 0; + pBindNode->scaleFactor = 1; + + if( pBindNode->makeConsoleCommand ) + dFree( pBindNode->makeConsoleCommand ); + if( pBindNode->breakConsoleCommand ) + dFree( pBindNode->breakConsoleCommand ); + + if(makeCmd[0]) + pBindNode->makeConsoleCommand = dStrdup(makeCmd); + else + pBindNode->makeConsoleCommand = dStrdup(""); + + if(breakCmd[0]) + pBindNode->breakConsoleCommand = dStrdup(breakCmd); + else + pBindNode->breakConsoleCommand = dStrdup(""); + return true; +} + +//------------------------------------------------------------------------------ +bool ActionMap::processBind(const U32 argc, const char** argv, SimObject* object) +{ + // Ok, the bind will come in the following format: + // [device] [key or button] <[param spec] [param] ...> [fnName] + // + const char* pDeviceName = argv[0]; + const char* pEvent = argv[1]; + const char* pFnName = argv[argc - 1]; + + // Determine the device + U32 deviceType; + U32 deviceInst; + + if(!getDeviceTypeAndInstance(argv[0], deviceType, deviceInst)) + { + Con::printf("processBind: unknown device: %s", pDeviceName); + return false; + } + + // Ok, we now have the deviceType and instance. Create an event descriptor + // for the bind... + // + EventDescriptor eventDescriptor; + if (createEventDescriptor(pEvent, &eventDescriptor) == false) + { + Con::printf("Could not create a description for binding: %s", pEvent); + return false; + } + + // Event has now been described, and device determined. we need now to extract + // any modifiers that the action map will apply to incoming events before + // calling the bound function... + // + // DMMTODO + U32 assignedFlags = 0; + F32 deadZoneBegin = 0.0f; + F32 deadZoneEnd = 0.0f; + F32 scaleFactor = 1.0f; + + if (argc != 3) { + // We have the following: "[DSIR]" [deadZone] [scale] + // + const char* pSpec = argv[2]; + + for (U32 i = 0; pSpec[i] != '\0'; i++) { + switch (pSpec[i]) { + case 'r': case 'R': + assignedFlags |= Node::Ranged; + break; + case 's': case 'S': + assignedFlags |= Node::HasScale; + break; + case 'd': case 'D': + assignedFlags |= Node::HasDeadZone; + break; + case 'i': case 'I': + assignedFlags |= Node::Inverted; + break; + case 'n': case 'N': + assignedFlags |= Node::NonLinear; + break; + + default: + AssertFatal(false, avar("Misunderstood specifier in bind (spec string: %s)", + pSpec)); + } + } + + // Ok, we have the flags. Scan the dead zone and scale, if any. + // + U32 curArg = 3; + if (assignedFlags & Node::HasDeadZone) { + dSscanf(argv[curArg], "%g %g", &deadZoneBegin, &deadZoneEnd); + curArg++; + } + if (assignedFlags & Node::HasScale) { + scaleFactor = dAtof(argv[curArg]); + curArg++; + } + + if (curArg != (argc - 1)) { + AssertFatal(curArg == (argc - 1), "error in bind spec somewhere..."); + Con::printf("Improperly specified bind for key: %s", argv[2]); + return false; + } + } + + // Ensure that the console function is properly specified? + // + // DMMTODO + + // Create the full bind entry, and place it in the map + // + // DMMTODO + Node* pBindNode = getNode(deviceType, deviceInst, + eventDescriptor.flags, + eventDescriptor.eventCode, object); + + pBindNode->flags = assignedFlags; + pBindNode->deadZoneBegin = deadZoneBegin; + pBindNode->deadZoneEnd = deadZoneEnd; + pBindNode->scaleFactor = scaleFactor; + pBindNode->object = object; + pBindNode->consoleFunction = StringTable->insert(pFnName); + + return true; +} + +//------------------------------------------------------------------------------ +bool ActionMap::processAction(const InputEventInfo* pEvent) +{ + // Suppress excluded input events, like alt-tab. + if(Platform::checkKeyboardInputExclusion(pEvent)) + return false; + + static const char *argv[2]; + if (pEvent->action == SI_MAKE) { + const Node* pNode = findNode(pEvent->deviceType, pEvent->deviceInst, + pEvent->modifier, pEvent->objInst); + + if( pNode == NULL ) + return false; + + // Enter the break into the table if this is a make event... + // Do this now rather than after command is processed because + // command might add a binding which can move the vector of nodes. + enterBreakEvent(pEvent, pNode); + + // Whadda ya know, we have this bound. Set up, and call the console + // function associated with it... + // + F32 value = pEvent->fValue; + if (pNode->flags & Node::Ranged) { + value = (value * 2.0f) - 1.0f; + if (pNode->flags & Node::Inverted) + value *= -1.0f; + } else { + if (pNode->flags & Node::Inverted) + value = 1.0f - value; + } + + if (pNode->flags & Node::HasScale) + value *= pNode->scaleFactor; + + if ( pNode->flags & Node::HasDeadZone ) + { + if ( value >= pNode->deadZoneBegin && value <= pNode->deadZoneEnd ) + value = 0.0f; + else + { + if( value > 0 ) + value = ( value - pNode->deadZoneBegin ) * ( 1.f / ( 1.f - pNode->deadZoneBegin ) ); + else + value = ( value + pNode->deadZoneBegin ) * ( 1.f / ( 1.f - pNode->deadZoneBegin ) ); + } + } + + if( pNode->flags & Node::NonLinear ) + value = ( value < 0.f ? -1.f : 1.f ) * mPow( mFabs( value ), CONST_E ); + + // Ok, we're all set up, call the function. + if(pNode->flags & Node::BindCmd) + { + // it's a bind command + if(pNode->makeConsoleCommand) + Con::evaluate(pNode->makeConsoleCommand); + } + else if ( pNode->consoleFunction[0] ) + { + argv[0] = pNode->consoleFunction; + argv[1] = Con::getFloatArg(value); + if (pNode->object) + Con::executef(pNode->object, argv[0], argv[1]); + else + Con::execute(2, argv); + } + return true; + } else if (pEvent->action == SI_MOVE) { + if (pEvent->deviceType == MouseDeviceType) { + const Node* pNode = findNode(pEvent->deviceType, pEvent->deviceInst, + pEvent->modifier, pEvent->objInst); + + if( pNode == NULL ) + return false; + + // "Do nothing" bind: + if ( !pNode->consoleFunction[0] ) + return( true ); + + // Whadda ya know, we have this bound. Set up, and call the console + // function associated with it. Mouse events ignore range and dead + // zone params. + // + F32 value = pEvent->fValue; + if (pNode->flags & Node::Inverted) + value *= -1.0f; + if (pNode->flags & Node::HasScale) + value *= pNode->scaleFactor; + + // Ok, we're all set up, call the function. + argv[0] = pNode->consoleFunction; + argv[1] = Con::getFloatArg(value); + if (pNode->object) + Con::executef(pNode->object, argv[0], argv[1]); + else + Con::execute(2, argv); + + return true; + } + else if ( pEvent->deviceType == JoystickDeviceType + || pEvent->deviceType == GamepadDeviceType + ) + { + // Joystick events... + const Node* pNode = findNode( pEvent->deviceType, pEvent->deviceInst, + pEvent->modifier, pEvent->objInst ); + + if( pNode == NULL ) + return false; + + // "Do nothing" bind: + if ( !pNode->consoleFunction[0] ) + return( true ); + + // Whadda ya know, we have this bound. Set up, and call the console + // function associated with it. Joystick move events are the same as mouse + // move events except that they don't ignore dead zone. + // + F32 value = pEvent->fValue; + if ( pNode->flags & Node::Inverted ) + value *= -1.0f; + + if ( pNode->flags & Node::HasScale ) + value *= pNode->scaleFactor; + + if ( pNode->flags & Node::HasDeadZone ) + { + if ( value >= pNode->deadZoneBegin && + value <= pNode->deadZoneEnd ) + value = 0.0f; + else + { + if( value > 0 ) + value = ( value - pNode->deadZoneBegin ) * ( 1.f / ( 1.f - pNode->deadZoneBegin ) ); + else + value = ( value + pNode->deadZoneBegin ) * ( 1.f / ( 1.f - pNode->deadZoneBegin ) ); + } + } + + if( pNode->flags & Node::NonLinear ) + value = ( value < 0.f ? -1.f : 1.f ) * mPow( mFabs( value ), CONST_E ); + + // Ok, we're all set up, call the function. + argv[0] = pNode->consoleFunction; + argv[1] = Con::getFloatArg( value ); + if (pNode->object) + Con::executef(pNode->object, argv[0], argv[1]); + else + Con::execute(2, argv); + + return true; + } + } + else if (pEvent->action == SI_BREAK) + { + return checkBreakTable(pEvent); + } + + return false; +} + +//------------------------------------------------------------------------------ +bool ActionMap::isAction( U32 deviceType, U32 deviceInst, U32 modifiers, U32 action ) +{ + return ( findNode( deviceType, deviceInst, modifiers, action ) != NULL ); +} + +//------------------------------------------------------------------------------ +ActionMap* ActionMap::getGlobalMap() +{ + SimSet* pActionMapSet = Sim::getActiveActionMapSet(); + AssertFatal( pActionMapSet && pActionMapSet->size() != 0, + "error, no ActiveMapSet or no global action map..."); + + return ( ( ActionMap* ) pActionMapSet->first() ); +} + +//------------------------------------------------------------------------------ +void ActionMap::enterBreakEvent(const InputEventInfo* pEvent, const Node* pNode) +{ + // There aren't likely to be many breaks outstanding at any one given time, + // so a simple linear search is probably sufficient. Note that the break table + // is static to the class, all breaks are directed to the action map that received + // the make. + // + S32 entry = -1; + for (U32 i = 0; i < smBreakTable.size(); i++) { + if (smBreakTable[i].deviceType == U32(pEvent->deviceType) && + smBreakTable[i].deviceInst == U32(pEvent->deviceInst) && + smBreakTable[i].objInst == U32(pEvent->objInst)) { + // Match. + entry = i; + break; + } + } + if (entry == -1) { + smBreakTable.increment(); + entry = smBreakTable.size() - 1; + + smBreakTable[entry].deviceType = pEvent->deviceType; + smBreakTable[entry].deviceInst = pEvent->deviceInst; + smBreakTable[entry].objInst = pEvent->objInst; + } + + // Ok, we now have the entry, and know that the device desc. and the objInst match. + // Copy out the node information... + // + smBreakTable[entry].object = pNode->object; + smBreakTable[entry].consoleFunction = pNode->consoleFunction; + if(pNode->breakConsoleCommand) + smBreakTable[entry].breakConsoleCommand = dStrdup(pNode->breakConsoleCommand); + else + smBreakTable[entry].breakConsoleCommand = NULL; + + smBreakTable[entry].flags = pNode->flags; + smBreakTable[entry].deadZoneBegin = pNode->deadZoneBegin; + smBreakTable[entry].deadZoneEnd = pNode->deadZoneEnd; + smBreakTable[entry].scaleFactor = pNode->scaleFactor; +} + +//------------------------------------------------------------------------------ +bool ActionMap::checkBreakTable(const InputEventInfo* pEvent) +{ + for (U32 i = 0; i < smBreakTable.size(); i++) + { + if (smBreakTable[i].deviceType == U32(pEvent->deviceType) && + smBreakTable[i].deviceInst == U32(pEvent->deviceInst) && + smBreakTable[i].objInst == U32(pEvent->objInst)) + { + fireBreakEvent(i, pEvent->fValue); + return true; + } + } + + return false; +} + +//------------------------------------------------------------------------------ +bool ActionMap::handleEvent(const InputEventInfo* pEvent) +{ + // Interate through the ActionMapSet until we get a map that + // handles the event or we run out of maps... + // + SimSet* pActionMapSet = Sim::getActiveActionMapSet(); + AssertFatal(pActionMapSet && pActionMapSet->size() != 0, + "error, no ActiveMapSet or no global action map..."); + + for (SimSet::iterator itr = pActionMapSet->end() - 1; + itr > pActionMapSet->begin(); itr--) { + ActionMap* pMap = static_cast(*itr); + if (pMap->processAction(pEvent) == true) + return true; + } + + // Found no matching action. Try with the modifiers stripped. + + InputEventInfo eventNoModifiers = *pEvent; + eventNoModifiers.modifier = ( InputModifiers ) 0; + + for (SimSet::iterator itr = pActionMapSet->end() - 1; + itr > pActionMapSet->begin(); itr--) { + ActionMap* pMap = static_cast(*itr); + if( pMap->processAction( &eventNoModifiers ) ) + return true; + } + + return false; +} + +//------------------------------------------------------------------------------ +bool ActionMap::handleEventGlobal(const InputEventInfo* pEvent) +{ + return getGlobalMap()->processAction( pEvent ); +} + +//------------------------------------------------------------------------------ +bool ActionMap::checkAsciiGlobal( U16 key, U32 modifiers ) +{ + // Does this ascii map to a key? + U16 keyCode = Input::getKeyCode(key); + if(keyCode == 0) + return false; + + // Grab the action map set. + SimSet* pActionMapSet = Sim::getActiveActionMapSet(); + AssertFatal(pActionMapSet && pActionMapSet->size() != 0, + "error, no ActiveMapSet or no global action map..."); + + // Grab the device maps for the first ActionMap. + Vector &maps = ((ActionMap*)pActionMapSet->first())->mDeviceMaps; + + // Find the keyboard. + DeviceMap *keyMap = NULL; + for(S32 i=0; ideviceType == KeyboardDeviceType) + { + keyMap = maps[i]; + break; + } + } + + if(!keyMap) + return false; + + // Normalize modifiers. + U32 realMods = modifiers; + if (realMods & SI_SHIFT) + realMods |= SI_SHIFT; + if (realMods & SI_CTRL) + realMods |= SI_CTRL; + if (realMods & SI_ALT) + realMods |= SI_ALT; + if (realMods & SI_MAC_OPT) + realMods |= SI_MAC_OPT; + + // Now find a matching node, if there is one. + for(S32 i=0; inodeMap.size(); i++) + { + Node &n = keyMap->nodeMap[i]; + + if(n.action == keyCode && (n.modifiers == modifiers)) + return true; + } + + return false; +} + +void ActionMap::clearAllBreaks() +{ + while(smBreakTable.size()) + fireBreakEvent(smBreakTable.size()-1); +} + +void ActionMap::fireBreakEvent( U32 i, F32 fValue ) +{ + // Match. Issue the break event... + // + F32 value = fValue; + if (smBreakTable[i].flags & Node::Ranged) { + value = (value * 2.0f) - 1.0f; + if (smBreakTable[i].flags & Node::Inverted) + value *= -1.0f; + } else { + if (smBreakTable[i].flags & Node::Inverted) + value = 1.0f - value; + } + + if (smBreakTable[i].flags & Node::HasScale) + value *= smBreakTable[i].scaleFactor; + + if (smBreakTable[i].flags & Node::HasDeadZone) + { + if (value >= smBreakTable[i].deadZoneBegin && + value <= smBreakTable[i].deadZoneEnd) + value = 0.0f; + else + { + if( value > 0 ) + value = ( value - smBreakTable[i].deadZoneBegin ) * ( 1.f / ( 1.f - smBreakTable[i].deadZoneBegin ) ); + else + value = ( value + smBreakTable[i].deadZoneBegin ) * ( 1.f / ( 1.f - smBreakTable[i].deadZoneBegin ) ); + } + } + + if( smBreakTable[i].flags & Node::NonLinear ) + value = ( value < 0.f ? -1.f : 1.f ) * mPow( mFabs( value ), CONST_E ); + + // Ok, we're all set up, call the function. + if(smBreakTable[i].consoleFunction) + { + if ( smBreakTable[i].consoleFunction[0] ) + { + static const char *argv[2]; + argv[0] = smBreakTable[i].consoleFunction; + argv[1] = Con::getFloatArg(value); + if (smBreakTable[i].object) + Con::executef(smBreakTable[i].object, argv[0], argv[1]); + else + Con::execute(2, argv); + } + } + else if(smBreakTable[i].breakConsoleCommand) + { + Con::evaluate(smBreakTable[i].breakConsoleCommand); + dFree(smBreakTable[i].breakConsoleCommand); + } + smBreakTable.erase(i); +} + +//------------------------------------------------------------------------------ + +// Console interop version. + +static ConsoleDocFragment _ActionMapbind1( + "@brief Associates a function to an input event.\n\n" + "When the input event is raised, the specified function will be called.\n\n" + "@param device The input device, such as mouse or keyboard.\n" + "@param action The input event, such as space, button0, etc.\n" + "@param command The function to bind to the action. Function must have a single boolean argument.\n" + "@return True if the binding was successful, false if the device was unknown or description failed.\n\n" + "@tsexample\n" + "// Simple function that prints to console\n" + "// %val - Sent by the device letting the user know\n" + "// if an input was pressed (true) or released (false)\n" + "function testInput(%val)\n" + "{\n" + " if(%val)\n" + " echo(\"Key is down\");\n" + " else\n" + " echo(\"Key was released\");\n" + "}\n\n" + "// Bind the \'K\' key to the testInput function\n" + "moveMap.bind(keyboard, k, testInput);\n\n" + "@endtsexample\n\n\n", + "ActionMap", + "bool bind( string device, string action, string command );"); + +static ConsoleDocFragment _ActionMapbind2( + "@brief Associates a function and input parameters to an input event.\n\n" + "When the input event is raised, the specified function will be called. Modifier flags may be specified to process " + "dead zones, input inversion, and more.\n\n" + "Valid modifier flags:\n\n" + " - R - Input is Ranged.\n" + " - S - Input is Scaled.\n" + " - I - Input is inverted.\n" + " - D - Dead zone is present.\n" + " - N - Input should be re-fit to a non-linear scale.\n\n" + "@param device The input device, such as mouse or keyboard.\n" + "@param action The input event, such as space, button0, etc.\n" + "@param flag Modifier flag assigned during binding, letting event know there are additional parameters to consider. \n" + "@param deadZone Restricted region in which device motion will not be acknowledged.\n" + "@param scale Modifies the deadZone region.\n" + "@param command The function bound to the action. Must take in a single argument.\n" + "@return True if the binding was successful, false if the device was unknown or description failed.\n\n" + "@tsexample\n" + "// Simple function that adjusts the pitch of the camera based on the " + "mouse's movement along the X axis.\n" + "function testPitch(%val)\n" + "{\n" + " %pitchAdj = getMouseAdjustAmount(%val);\n" + " $mvPitch += %pitchAdj;\n" + "}\n\n" + "// Bind the mouse's X axis to the testPitch function\n" + "// DI is flagged, meaning input is inverted and has a deadzone\n" + "%this.bind( mouse, \"xaxis\", \"DI\", \"-0.23 0.23\", testPitch );\n" + "@endtsexample\n\n\n", + "ActionMap", + "bool bind( string device, string action, string flag, string deadZone, string scale, string command );"); + +ConsoleMethod( ActionMap, bind, bool, 5, 10, "actionMap.bind( device, action, [modifier spec, mod...], command )" + "@hide") +{ + return object->processBind( argc - 2, argv + 2, NULL ); +} + +static ConsoleDocFragment _ActionMapbindObj1( + "@brief Associates a function to an input event for a specified class or object.\n\n" + "You must specify a device, the action to bind, a function, and an object to be called when the event happens. " + "The function specified must be set to receive a single boolean value passed.\n\n" + "@param device The input device, such as mouse or keyboard.\n" + "@param action The input event, such as space, button0, etc.\n" + "@param command The function bound to the action.\n" + "@param object The object or class bound to the action.\n" + "@return True if the binding was successful, false if the device was unknown or description failed.\n\n" + "@tsexample\n" + "moveMap.bindObj(keyboard, \"numpad1\", \"rangeChange\", %player);" + "@endtsexample\n\n", + "ActionMap", + "bool bindObj( string device, string action, string command, SimObjectID object );"); + +static ConsoleDocFragment _ActionMapbindObj2( + "@brief Associates a function to an input event for a specified class or object.\n\n" + "You must specify a device, the action to bind, a function, and an object to be called when the event happens. " + "The function specified must be set to receive a single boolean value passed. Modifier flags may be specified to process " + "dead zones, input inversion, and more.\n\n" + "Valid modifier flags:\n\n" + " - R - Input is Ranged.\n" + " - S - Input is Scaled.\n" + " - I - Input is inverted.\n" + " - D - Dead zone is present.\n" + " - N - Input should be re-fit to a non-linear scale.\n\n" + "@param device The input device, such as mouse or keyboard.\n" + "@param action The input event, such as space, button0, etc.\n" + "@param flag Modifier flag assigned during binding, letting event know there are additional parameters to consider.\n" + "@param deadZone [Required only when flag is set] Restricted region in which device motion will not be acknowledged.\n" + "@param scale [Required only when flag is set] Modifies the deadZone region.\n" + "@param command The function bound to the action.\n" + "@param object The object or class bound to the action.\n" + "@return True if the binding was successful, false if the device was unknown or description failed.\n\n" + "@tsexample\n" + "// Bind the mouse's movement along the x-axis to the testInput function of the Player class\n" + "// DSI is flagged, meaning input is inverted, has scale and has a deadzone\n" + "%this.bindObj( mouse, \"xaxis\", \"DSI\", %deadZone, %scale, \"testInput\", %player );\n" + "@endtsexample\n\n\n", + "ActionMap", + "bool bindObj( string device, string action, string flag, string deadZone, string scale, string command, SimObjectID object );"); + +ConsoleMethod( ActionMap, bindObj, bool, 6, 11, "(device, action, [modifier spec, mod...], command, object)" + "@hide") +{ + SimObject* simObject = Sim::findObject(argv[argc - 1]); + if ( simObject == NULL ) + { + Con::warnf("ActionMap::bindObj() - Cannot bind, specified object was not found!"); + return false; + } + + return object->processBind( argc - 3, argv + 2, simObject ); +} + +//------------------------------------------------------------------------------ + +DefineEngineMethod( ActionMap, bindCmd, bool, ( const char* device, const char* action, const char* makeCmd, const char* breakCmd ), ( "" ), + "@brief Associates a make command and optional break command to a specified input device action.\n\n" + "Must include parenthesis and semicolon in the make and break command strings.\n\n" + "@param device The device to bind to. Can be a keyboard, mouse, joystick or gamepad.\n" + "@param action The device action to bind to. The action is dependant upon the device. Specify a key for keyboards.\n" + "@param makeCmd The command to execute when the device/action is made.\n" + "@param breakCmd [optional] The command to execute when the device or action is unmade.\n" + "@return True the bind was successful, false if the device was unknown or description failed.\n" + "@tsexample\n" + "// Print to the console when the spacebar is pressed\n" + "function onSpaceDown()\n" + "{\n" + " echo(\"Space bar down!\");\n" + "}\n\n" + "// Print to the console when the spacebar is released\n" + "function onSpaceUp()\n" + "{\n" + " echo(\"Space bar up!\");\n" + "}\n\n" + "// Bind the commands onSpaceDown() and onSpaceUp() to spacebar events\n\n" + "moveMap.bindCmd(keyboard, \"space\", \"onSpaceDown();\", \"onSpaceUp();\");\n" + "@endtsexample\n\n") +{ + return object->processBindCmd( device, action, makeCmd, breakCmd ); +} + +DefineEngineMethod( ActionMap, unbind, bool, ( const char* device, const char* action ),, + "@brief Removes the binding on an input device and action.\n" + "@param device The device to unbind from. Can be a keyboard, mouse, joystick or a gamepad.\n" + "@param action The device action to unbind from. The action is dependant upon the device. Specify a key for keyboards.\n" + "@return True if the unbind was successful, false if the device was unknown or description failed.\n\n" + "@tsexample\n" + "moveMap.unbind(\"keyboard\", \"space\");\n" + "@endtsexample\n\n") +{ + return object->processUnbind( device, action ); +} + +DefineEngineMethod( ActionMap, unbindObj, bool, ( const char* device, const char* action, const char* obj ),, + "@brief Remove any object-binding on an input device and action.\n" + "@param device The device to bind to. Can be keyboard, mouse, joystick or gamepad.\n" + "@param action The device action to unbind from. The action is dependant upon the device. Specify a key for keyboards.\n" + "@param obj The object to perform unbind against.\n" + "@return True if the unbind was successful, false if the device was unknown or description failed.\n" + "@tsexample\n" + "moveMap.unbindObj(\"keyboard\", \"numpad1\", \"rangeChange\", %player);" + "@endtsexample\n\n\n") +{ + SimObject* simObject = Sim::findObject(obj); + if ( simObject == NULL ) + { + Con::warnf("ActionMap::unbindObj() - Cannot unbind, specified object was not found!"); + return false; + } + + return object->processUnbind( device, action, simObject ); +} + +DefineEngineMethod( ActionMap, save, void, ( const char* fileName, bool append ), ( NULL, false ), + "@brief Saves the ActionMap to a file or dumps it to the console.\n\n" + "@param fileName The file path to save the ActionMap to. If a filename is not specified " + " the ActionMap will be dumped to the console.\n" + "@param append Whether to write the ActionMap at the end of the file or overwrite it.\n" + "@tsexample\n" + "// Write out the actionmap into the config.cs file\n" + "moveMap.save( \"scripts/client/config.cs\" );" + "@endtsexample\n\n") +{ + char buffer[1024]; + + if(fileName) + { + if(Con::expandScriptFilename(buffer, sizeof(buffer), fileName)) + fileName = buffer; + } + + object->dumpActionMap( fileName, append ); +} + +DefineEngineFunction( getCurrentActionMap, ActionMap*, (),, + "@brief Returns the current %ActionMap.\n" + "@see ActionMap" + "@ingroup Input") +{ + SimSet* pActionMapSet = Sim::getActiveActionMapSet(); + return dynamic_cast< ActionMap* >( pActionMapSet->last() ); +} + +DefineEngineMethod( ActionMap, push, void, (),, + "@brief Push the ActionMap onto the %ActionMap stack.\n\n" + "Activates an ActionMap and placees it at the top of the ActionMap stack.\n\n" + "@tsexample\n" + "// Make moveMap the active action map\n" + "moveMap.push();\n" + "@endtsexample\n\n" + "@see ActionMap") +{ + SimSet* pActionMapSet = Sim::getActiveActionMapSet(); + pActionMapSet->pushObject( object ); +} + +DefineEngineMethod( ActionMap, pop, void, (),, + "@brief Pop the ActionMap off the %ActionMap stack.\n\n" + "Deactivates an %ActionMap and removes it from the @ActionMap stack.\n" + "@tsexample\n" + "// Deactivate moveMap\n" + "moveMap.pop();\n" + "@endtsexample\n\n" + "@see ActionMap") +{ + SimSet* pActionMapSet = Sim::getActiveActionMapSet(); + pActionMapSet->removeObject( object ); +} + +DefineEngineMethod( ActionMap, getBinding, const char*, ( const char* command ),, + "@brief Gets the ActionMap binding for the specified command.\n\n" + "Use getField() on the return value to get the device and action of the binding.\n" + "@param command The function to search bindings for.\n" + "@return The binding against the specified command. Returns an empty string(\"\") " + "if a binding wasn't found.\n" + "@tsexample\n" + "// Find what the function \"jump()\" is bound to in moveMap\n" + "%bind = moveMap.getBinding( \"jump\" );\n\n" + "if ( %bind !$= \"\" )\n" + "{\n" + "// Find out what device is used in the binding\n" + " %device = getField( %bind, 0 );\n\n" + "// Find out what action (such as a key) is used in the binding\n" + " %action = getField( %bind, 1 );\n" + "}\n" + "@endtsexample\n\n" + "@see getField") +{ + return object->getBinding( command ); +} + +DefineEngineMethod( ActionMap, getCommand, const char*, ( const char* device, const char* action ),, + "@brief Gets ActionMap command for the device and action.\n\n" + "@param device The device that was bound. Can be a keyboard, mouse, joystick or a gamepad.\n" + "@param action The device action that was bound. The action is dependant upon the device. Specify a key for keyboards.\n" + "@return The command against the specified device and action.\n" + "@tsexample\n" + "// Find what function is bound to a device\'s action\n" + "// In this example, \"jump()\" was assigned to the space key in another script\n" + "%command = moveMap.getCommand(\"keyboard\", \"space\");\n\n" + "// Should print \"jump\" in the console\n" + "echo(%command)\n" + "@endtsexample\n\n") +{ + return object->getCommand( device, action ); +} + +DefineEngineMethod( ActionMap, isInverted, bool, ( const char* device, const char* action ),, + "@brief Determines if the specified device and action is inverted.\n\n" + "Should only be used for scrolling devices or gamepad/joystick axes." + "@param device The device that was bound. Can be a keyboard, mouse, joystick or a gamepad.\n" + "@param action The device action that was bound. The action is dependant upon the device. Specify a key for keyboards.\n" + "@return True if the specified device and action is inverted.\n" + "@tsexample\n" + "%if ( moveMap.isInverted( \"mouse\", \"xaxis\"))\n" + " echo(\"Mouse's xAxis is inverted\");" + "@endtsexample\n\n") +{ + return object->isInverted( device, action ); +} + +DefineEngineMethod( ActionMap, getScale, F32, ( const char* device, const char* action ),, + "@brief Get any scaling on the specified device and action.\n\n" + "@param device The device that was bound. Can be keyboard, mouse, joystick or gamepad.\n" + "@param action The device action that was bound. The action is dependant upon the device. Specify a key for keyboards.\n" + "@return Any scaling applied to the specified device and action.\n" + "@tsexample\n" + "%scale = %moveMap.getScale( \"gamepad\", \"thumbrx\");\n" + "@endtsexample\n\n") +{ + return object->getScale( device, action ); +} + +DefineEngineMethod( ActionMap, getDeadZone, const char*, ( const char* device, const char* action ),, + "@brief Gets the Dead zone for the specified device and action.\n\n" + "@param device The device that was bound. Can be a keyboard, mouse, joystick or a gamepad.\n" + "@param action The device action that was bound. The action is dependant upon the device. Specify a key for keyboards.\n" + "@return The dead zone for the specified device and action. Returns \"0 0\" if there is no dead zone " + "or an empty string(\"\") if the mapping was not found.\n" + "@tsexample\n" + "%deadZone = moveMap.getDeadZone( \"gamepad\", \"thumbrx\");\n" + "@endtsexample\n\n") +{ + return object->getDeadZone( device, action ); +} + +//------------------------------------------------------------------------------ +//-------------------------------------- Key code to string mapping +// TODO: Add most obvious aliases... +// +CodeMapping gVirtualMap[] = +{ + //-------------------------------------- KEYBOARD EVENTS + // + { "backspace", SI_KEY, KEY_BACKSPACE }, + { "tab", SI_KEY, KEY_TAB }, + + { "return", SI_KEY, KEY_RETURN }, + { "enter", SI_KEY, KEY_RETURN }, + + { "shift", SI_KEY, KEY_SHIFT }, + { "ctrl", SI_KEY, KEY_CONTROL }, + { "alt", SI_KEY, KEY_ALT }, + { "pause", SI_KEY, KEY_PAUSE }, + { "capslock", SI_KEY, KEY_CAPSLOCK }, + + { "escape", SI_KEY, KEY_ESCAPE }, + + { "space", SI_KEY, KEY_SPACE }, + { "pagedown", SI_KEY, KEY_PAGE_DOWN }, + { "pageup", SI_KEY, KEY_PAGE_UP }, + { "end", SI_KEY, KEY_END }, + { "home", SI_KEY, KEY_HOME }, + { "left", SI_KEY, KEY_LEFT }, + { "up", SI_KEY, KEY_UP }, + { "right", SI_KEY, KEY_RIGHT }, + { "down", SI_KEY, KEY_DOWN }, + { "print", SI_KEY, KEY_PRINT }, + { "insert", SI_KEY, KEY_INSERT }, + { "delete", SI_KEY, KEY_DELETE }, + { "help", SI_KEY, KEY_HELP }, + + { "win_lwindow", SI_KEY, KEY_WIN_LWINDOW }, + { "win_rwindow", SI_KEY, KEY_WIN_RWINDOW }, + { "win_apps", SI_KEY, KEY_WIN_APPS }, + + { "cmd", SI_KEY, KEY_ALT }, + { "opt", SI_KEY, KEY_MAC_OPT }, + { "lopt", SI_KEY, KEY_MAC_LOPT }, + { "ropt", SI_KEY, KEY_MAC_ROPT }, + + { "numpad0", SI_KEY, KEY_NUMPAD0 }, + { "numpad1", SI_KEY, KEY_NUMPAD1 }, + { "numpad2", SI_KEY, KEY_NUMPAD2 }, + { "numpad3", SI_KEY, KEY_NUMPAD3 }, + { "numpad4", SI_KEY, KEY_NUMPAD4 }, + { "numpad5", SI_KEY, KEY_NUMPAD5 }, + { "numpad6", SI_KEY, KEY_NUMPAD6 }, + { "numpad7", SI_KEY, KEY_NUMPAD7 }, + { "numpad8", SI_KEY, KEY_NUMPAD8 }, + { "numpad9", SI_KEY, KEY_NUMPAD9 }, + { "numpadmult", SI_KEY, KEY_MULTIPLY }, + { "numpadadd", SI_KEY, KEY_ADD }, + { "numpadsep", SI_KEY, KEY_SEPARATOR }, + { "numpadminus", SI_KEY, KEY_SUBTRACT }, + { "numpaddecimal", SI_KEY, KEY_DECIMAL }, + { "numpaddivide", SI_KEY, KEY_DIVIDE }, + { "numpadenter", SI_KEY, KEY_NUMPADENTER }, + + { "f1", SI_KEY, KEY_F1 }, + { "f2", SI_KEY, KEY_F2 }, + { "f3", SI_KEY, KEY_F3 }, + { "f4", SI_KEY, KEY_F4 }, + { "f5", SI_KEY, KEY_F5 }, + { "f6", SI_KEY, KEY_F6 }, + { "f7", SI_KEY, KEY_F7 }, + { "f8", SI_KEY, KEY_F8 }, + { "f9", SI_KEY, KEY_F9 }, + { "f10", SI_KEY, KEY_F10 }, + { "f11", SI_KEY, KEY_F11 }, + { "f12", SI_KEY, KEY_F12 }, + { "f13", SI_KEY, KEY_F13 }, + { "f14", SI_KEY, KEY_F14 }, + { "f15", SI_KEY, KEY_F15 }, + { "f16", SI_KEY, KEY_F16 }, + { "f17", SI_KEY, KEY_F17 }, + { "f18", SI_KEY, KEY_F18 }, + { "f19", SI_KEY, KEY_F19 }, + { "f20", SI_KEY, KEY_F20 }, + { "f21", SI_KEY, KEY_F21 }, + { "f22", SI_KEY, KEY_F22 }, + { "f23", SI_KEY, KEY_F23 }, + { "f24", SI_KEY, KEY_F24 }, + + { "numlock", SI_KEY, KEY_NUMLOCK }, + { "scrolllock", SI_KEY, KEY_SCROLLLOCK }, + + { "lshift", SI_KEY, KEY_LSHIFT }, + { "rshift", SI_KEY, KEY_RSHIFT }, + { "lcontrol", SI_KEY, KEY_LCONTROL }, + { "rcontrol", SI_KEY, KEY_RCONTROL }, + { "lalt", SI_KEY, KEY_LALT }, + { "ralt", SI_KEY, KEY_RALT }, + { "tilde", SI_KEY, KEY_TILDE }, + + { "minus", SI_KEY, KEY_MINUS }, + { "equals", SI_KEY, KEY_EQUALS }, + { "lbracket", SI_KEY, KEY_LBRACKET }, + { "rbracket", SI_KEY, KEY_RBRACKET }, + { "backslash", SI_KEY, KEY_BACKSLASH }, + { "semicolon", SI_KEY, KEY_SEMICOLON }, + { "apostrophe", SI_KEY, KEY_APOSTROPHE }, + { "comma", SI_KEY, KEY_COMMA }, + { "period", SI_KEY, KEY_PERIOD }, + { "slash", SI_KEY, KEY_SLASH }, + { "lessthan", SI_KEY, KEY_OEM_102 }, + + //-------------------------------------- BUTTON EVENTS + // Joystick/Mouse buttons + { "button0", SI_BUTTON, KEY_BUTTON0 }, + { "button1", SI_BUTTON, KEY_BUTTON1 }, + { "button2", SI_BUTTON, KEY_BUTTON2 }, + { "button3", SI_BUTTON, KEY_BUTTON3 }, + { "button4", SI_BUTTON, KEY_BUTTON4 }, + { "button5", SI_BUTTON, KEY_BUTTON5 }, + { "button6", SI_BUTTON, KEY_BUTTON6 }, + { "button7", SI_BUTTON, KEY_BUTTON7 }, + { "button8", SI_BUTTON, KEY_BUTTON8 }, + { "button9", SI_BUTTON, KEY_BUTTON9 }, + { "button10", SI_BUTTON, KEY_BUTTON10 }, + { "button11", SI_BUTTON, KEY_BUTTON11 }, + { "button12", SI_BUTTON, KEY_BUTTON12 }, + { "button13", SI_BUTTON, KEY_BUTTON13 }, + { "button14", SI_BUTTON, KEY_BUTTON14 }, + { "button15", SI_BUTTON, KEY_BUTTON15 }, + { "button16", SI_BUTTON, KEY_BUTTON16 }, + { "button17", SI_BUTTON, KEY_BUTTON17 }, + { "button18", SI_BUTTON, KEY_BUTTON18 }, + { "button19", SI_BUTTON, KEY_BUTTON19 }, + { "button20", SI_BUTTON, KEY_BUTTON20 }, + { "button21", SI_BUTTON, KEY_BUTTON21 }, + { "button22", SI_BUTTON, KEY_BUTTON22 }, + { "button23", SI_BUTTON, KEY_BUTTON23 }, + { "button24", SI_BUTTON, KEY_BUTTON24 }, + { "button25", SI_BUTTON, KEY_BUTTON25 }, + { "button26", SI_BUTTON, KEY_BUTTON26 }, + { "button27", SI_BUTTON, KEY_BUTTON27 }, + { "button28", SI_BUTTON, KEY_BUTTON28 }, + { "button29", SI_BUTTON, KEY_BUTTON29 }, + { "button30", SI_BUTTON, KEY_BUTTON30 }, + { "button31", SI_BUTTON, KEY_BUTTON31 }, + + //-------------------------------------- MOVE EVENTS + // Mouse/Joystick axes: + { "xaxis", SI_AXIS, SI_XAXIS }, + { "yaxis", SI_AXIS, SI_YAXIS }, + { "zaxis", SI_AXIS, SI_ZAXIS }, + { "rxaxis", SI_AXIS, SI_RXAXIS }, + { "ryaxis", SI_AXIS, SI_RYAXIS }, + { "rzaxis", SI_AXIS, SI_RZAXIS }, + { "slider", SI_AXIS, SI_SLIDER }, + + //-------------------------------------- POV EVENTS + // Joystick POV: + { "xpov", SI_POV, SI_XPOV }, + { "ypov", SI_POV, SI_YPOV }, + { "upov", SI_POV, SI_UPOV }, + { "dpov", SI_POV, SI_DPOV }, + { "lpov", SI_POV, SI_LPOV }, + { "rpov", SI_POV, SI_RPOV }, + { "xpov2", SI_POV, SI_XPOV2 }, + { "ypov2", SI_POV, SI_YPOV2 }, + { "upov2", SI_POV, SI_UPOV2 }, + { "dpov2", SI_POV, SI_DPOV2 }, + { "lpov2", SI_POV, SI_LPOV2 }, + { "rpov2", SI_POV, SI_RPOV2 }, + +#if defined( TORQUE_OS_WIN32 ) || defined( TORQUE_OS_XENON ) + //-------------------------------------- XINPUT EVENTS + // Controller connect / disconnect: + { "connect", SI_BUTTON, XI_CONNECT }, + + // L & R Thumbsticks: + { "thumblx", SI_AXIS, XI_THUMBLX }, + { "thumbly", SI_AXIS, XI_THUMBLY }, + { "thumbrx", SI_AXIS, XI_THUMBRX }, + { "thumbry", SI_AXIS, XI_THUMBRY }, + + // L & R Triggers: + { "triggerl", SI_AXIS, XI_LEFT_TRIGGER }, + { "triggerr", SI_AXIS, XI_RIGHT_TRIGGER }, + + // DPAD Buttons: + { "dpadu", SI_BUTTON, SI_UPOV }, + { "dpadd", SI_BUTTON, SI_DPOV }, + { "dpadl", SI_BUTTON, SI_LPOV }, + { "dpadr", SI_BUTTON, SI_RPOV }, + + // START & BACK Buttons: + { "btn_start", SI_BUTTON, XI_START }, + { "btn_back", SI_BUTTON, XI_BACK }, + + // L & R Thumbstick Buttons: + { "btn_lt", SI_BUTTON, XI_LEFT_THUMB }, + { "btn_rt", SI_BUTTON, XI_RIGHT_THUMB }, + + // L & R Shoulder Buttons: + { "btn_l", SI_BUTTON, XI_LEFT_SHOULDER }, + { "btn_r", SI_BUTTON, XI_RIGHT_SHOULDER }, + + // Primary buttons: + { "btn_a", SI_BUTTON, XI_A }, + { "btn_b", SI_BUTTON, XI_B }, + { "btn_x", SI_BUTTON, XI_X }, + { "btn_y", SI_BUTTON, XI_Y }, +#endif + + //-------------------------------------- MISCELLANEOUS EVENTS + // + + { "anykey", SI_KEY, KEY_ANYKEY }, + { "nomatch", SI_UNKNOWN, (InputObjectInstances)0xFFFFFFFF } +}; + +AsciiMapping gAsciiMap[] = +{ + //--- KEYBOARD EVENTS + // + { "space", 0x0020 }, + //{ "exclamation", 0x0021 }, + { "doublequote", 0x0022 }, + //{ "pound", 0x0023 }, + //{ "ampersand", 0x0026 }, + { "apostrophe", 0x0027 }, + //{ "lparen", 0x0028 }, + //{ "rparen", 0x0029 }, + { "comma", 0x002c }, + { "minus", 0x002d }, + { "period", 0x002e }, + //{ "slash", 0x002f }, + //{ "colon", 0x003a }, + //{ "semicolon", 0x003b }, + //{ "lessthan", 0x003c }, + //{ "equals", 0x003d }, + //{ "morethan", 0x003e }, + //{ "lbracket", 0x005b }, + { "backslash", 0x005c }, + //{ "rbracket", 0x005d }, + //{ "circumflex", 0x005e }, + //{ "underscore", 0x005f }, + { "grave", 0x0060 }, + //{ "tilde", 0x007e }, + //{ "vertbar", 0x007c }, + //{ "exclamdown", 0x00a1 }, + //{ "cent", 0x00a2 }, + //{ "sterling", 0x00a3 }, + //{ "currency", 0x00a4 }, + //{ "brokenbar", 0x00a6 }, + //{ "ring", 0x00b0 }, + //{ "plusminus", 0x00b1 }, + { "super2", 0x00b2 }, + { "super3", 0x00b3 }, + { "acute", 0x00b4 }, + //{ "mu", 0x00b5 }, + //{ "ordmasculine", 0x00ba }, + //{ "questiondown", 0x00bf }, + //{ "gemandbls", 0x00df }, + //{ "agrave", 0x00e0 }, + //{ "aacute", 0x00e1 }, + //{ "acircumflex", 0x00e2 }, + //{ "atilde", 0x00e3 }, + //{ "adieresis", 0x00e4 }, + //{ "aring", 0x00e5 }, + //{ "ae", 0x00e6 }, + //{ "ccedille", 0x00e7 }, + //{ "egrave", 0x00e8 }, + //{ "eacute", 0x00e9 }, + //{ "ecircumflex", 0x00ea }, + //{ "edieresis", 0x00eb }, + //{ "igrave", 0x00ec }, + //{ "iacute", 0x00ed }, + //{ "icircumflex", 0x00ee }, + //{ "idieresis", 0x00ef }, + //{ "ntilde", 0x00f1 }, + //{ "ograve", 0x00f2 }, + //{ "oacute", 0x00f3 }, + //{ "ocircumflex", 0x00f4 }, + //{ "otilde", 0x00f5 }, + //{ "odieresis", 0x00f6 }, + //{ "divide", 0x00f7 }, + //{ "oslash", 0x00f8 }, + //{ "ugrave", 0x00f9 }, + //{ "uacute", 0x00fa }, + //{ "ucircumflex", 0x00fb }, + //{ "udieresis", 0x00fc }, + //{ "ygrave", 0x00fd }, + //{ "thorn", 0x00fe }, + //{ "ydieresis", 0x00ff }, + { "nomatch", 0xFFFF } +}; diff --git a/Engine/source/sim/actionMap.h b/Engine/source/sim/actionMap.h new file mode 100644 index 000000000..7b3d55261 --- /dev/null +++ b/Engine/source/sim/actionMap.h @@ -0,0 +1,188 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _ACTIONMAP_H_ +#define _ACTIONMAP_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _SIMBASE_H_ +#include "console/simBase.h" +#endif + +struct InputEventInfo; + +struct EventDescriptor +{ + U8 flags; ///< Combination of any modifier flags. + U8 eventType; ///< SI_KEY, etc. + U16 eventCode; ///< From event.h +}; + +/// Map raw inputs to a variety of actions. This is used for all keymapping +/// in the engine. +/// @see ActionMap::Node +class ActionMap : public SimObject +{ + typedef SimObject Parent; + + protected: + bool onAdd(); + + struct Node { + U32 modifiers; + U32 action; + + enum Flags { + Ranged = BIT(0), ///< Ranged input. + HasScale = BIT(1), ///< Scaled input. + HasDeadZone = BIT(2), ///< Dead zone is present. + Inverted = BIT(3), ///< Input is inverted. + NonLinear = BIT(4), ///< Input should be re-fit to a non-linear scale + BindCmd = BIT(5) ///< Bind a console command to this. + }; + + U32 flags; ///< @see Node::Flags + F32 deadZoneBegin; + F32 deadZoneEnd; + F32 scaleFactor; + + SimObject* object; ///< Object to call consoleFunction on. + StringTableEntry consoleFunction; ///< Console function to call with new values. + + char *makeConsoleCommand; ///< Console command to execute when we make this command. + char *breakConsoleCommand; ///< Console command to execute when we break this command. + }; + + /// Used to represent a devices. + struct DeviceMap + { + U32 deviceType; + U32 deviceInst; + + Vector nodeMap; + DeviceMap() { + VECTOR_SET_ASSOCIATION(nodeMap); + } + ~DeviceMap(); + }; + struct BreakEntry + { + U32 deviceType; + U32 deviceInst; + U32 objInst; + SimObject* object; + StringTableEntry consoleFunction; + char *breakConsoleCommand; + + // It's possible that the node could be deleted (unlikely, but possible, + // so we replicate the node flags here... + // + U32 flags; + F32 deadZoneBegin; + F32 deadZoneEnd; + F32 scaleFactor; + }; + + + Vector mDeviceMaps; + static Vector smBreakTable; + + // Find: return NULL if not found in current map, Get: create if not + // found. + const Node* findNode(const U32 inDeviceType, const U32 inDeviceInst, + const U32 inModifiers, const U32 inAction); + bool findBoundNode( const char* function, U32 &devMapIndex, U32 &nodeIndex ); + bool nextBoundNode( const char* function, U32 &devMapIndex, U32 &nodeIndex ); + Node* getNode(const U32 inDeviceType, const U32 inDeviceInst, + const U32 inModifiers, const U32 inAction, + SimObject* object = NULL); + + void removeNode(const U32 inDeviceType, const U32 inDeviceInst, + const U32 inModifiers, const U32 inAction, + SimObject* object = NULL); + + void enterBreakEvent(const InputEventInfo* pEvent, const Node* pNode); + + static const char* getModifierString(const U32 modifiers); + + /// Pass index to a break entry, and this function will fire it off. + static void fireBreakEvent(U32 idx, F32 value = 0.f); + + public: + ActionMap(); + ~ActionMap(); + + void dumpActionMap(const char* fileName, const bool append) const; + + static bool createEventDescriptor(const char* pEventString, EventDescriptor* pDescriptor); + + bool processBind(const U32 argc, const char** argv, SimObject* object = NULL); + bool processBindCmd(const char *device, const char *action, const char *makeCmd, const char *breakCmd); + bool processUnbind(const char *device, const char *action, SimObject* object = NULL); + + /// @name Console Interface Functions + /// @{ + const char* getBinding ( const char* command ); ///< Find what the given command is bound to. + const char* getCommand ( const char* device, const char* action ); ///< Find what command is bound to the given event descriptor . + bool isInverted ( const char* device, const char* action ); + F32 getScale ( const char* device, const char* action ); + const char* getDeadZone( const char* device, const char* action ); + /// @} + + + static bool getKeyString(const U32 action, char* buffer); + static bool getDeviceName(const U32 deviceType, const U32 deviceInstance, char* buffer); + static const char* buildActionString( const InputEventInfo* event ); + + bool processAction(const InputEventInfo*); + + /// Return true if the given event triggers is bound to an action in this map. + bool isAction( U32 deviceType, U32 deviceInst, U32 modifiers, U32 action ); + + /// Returns the global ActionMap. + static ActionMap* getGlobalMap(); + + static bool checkBreakTable(const InputEventInfo*); + static bool handleEvent(const InputEventInfo*); + static bool handleEventGlobal(const InputEventInfo*); + + /// Called when we lose focus, to make sure we have no dangling inputs. + /// + /// This fires a break event for every currently pending item in the break + /// table. + static void clearAllBreaks(); + + /// Returns true if the specified key + modifiers are bound to something + /// on the global action map. + static bool checkAsciiGlobal(U16 key, U32 modifiers); + + static bool getDeviceTypeAndInstance(const char *device, U32 &deviceType, U32 &deviceInstance); + + DECLARE_CONOBJECT(ActionMap); +}; + +#endif // _ACTIONMAP_H_ diff --git a/Engine/source/sim/connectionStringTable.cpp b/Engine/source/sim/connectionStringTable.cpp new file mode 100644 index 000000000..dddd935b2 --- /dev/null +++ b/Engine/source/sim/connectionStringTable.cpp @@ -0,0 +1,198 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/dnet.h" +#include "console/simBase.h" +#include "sim/netConnection.h" +#include "core/stream/bitStream.h" +#include "console/consoleTypes.h" + +class NetStringEvent : public NetEvent +{ + NetStringHandle mString; + U32 mIndex; +public: + typedef NetEvent Parent; + NetStringEvent(U32 index = 0, NetStringHandle string = NetStringHandle()) + { + mIndex = index; + mString = string; + } + virtual void pack(NetConnection* /*ps*/, BitStream *bstream) + { + bstream->writeInt(mIndex, ConnectionStringTable::EntryBitSize); + bstream->writeString(mString.getString()); + } + virtual void write(NetConnection* /*ps*/, BitStream *bstream) + { + bstream->writeInt(mIndex, ConnectionStringTable::EntryBitSize); + bstream->writeString(mString.getString()); + } + virtual void unpack(NetConnection* /*con*/, BitStream *bstream) + { + char buf[256]; + mIndex = bstream->readInt(ConnectionStringTable::EntryBitSize); + bstream->readString(buf); + mString = NetStringHandle(buf); + } + virtual void notifyDelivered(NetConnection *ps, bool madeit) + { + if(madeit) + ps->confirmStringReceived(mString, mIndex); + } + virtual void process(NetConnection *connection) + { + Con::printf("Mapping string: %s to index: %d", mString.getString(), mIndex); + connection->mapString(mIndex, mString); + } +#ifdef TORQUE_DEBUG_NET + const char *getDebugName() + { + static char buffer[512]; + dSprintf(buffer, sizeof(buffer), "%s - \"", getClassName()); + expandEscape(buffer + dStrlen(buffer), mString.getString()); + dStrcat(buffer, "\""); + return buffer; + } +#endif + DECLARE_CONOBJECT(NetStringEvent); +}; + +IMPLEMENT_CO_NETEVENT_V1(NetStringEvent); + +ConsoleDocClass( NetStringEvent, + "@brief Internal event used for transmitting strings across the server.\n\n" + + "For internal use only, not intended for TorqueScript or game development\n\n" + + "@internal\n" +); + +//-------------------------------------------------------------------- + + +ConnectionStringTable::ConnectionStringTable(NetConnection *parent) +{ + mParent = parent; + for(U32 i = 0; i < EntryCount; i++) + { + mEntryTable[i].nextHash = NULL; + mEntryTable[i].nextLink = &mEntryTable[i+1]; + mEntryTable[i].prevLink = &mEntryTable[i-1]; + mEntryTable[i].index = i; + + mHashTable[i] = NULL; + } + mLRUHead.nextLink = &mEntryTable[0]; + mEntryTable[0].prevLink = &mLRUHead; + mLRUTail.prevLink = &mEntryTable[EntryCount-1]; + mEntryTable[EntryCount-1].nextLink = &mLRUTail; +} + +U32 ConnectionStringTable::getNetSendId(NetStringHandle &string) +{ + // see if the entry is in the hash table right now + U32 hashIndex = string.getIndex() % EntryCount; + for(Entry *walk = mHashTable[hashIndex]; walk; walk = walk->nextHash) + if(walk->string == string) + return walk->index; + AssertFatal(0, "Net send id is not in the table. Error!"); + return 0; +} + +U32 ConnectionStringTable::checkString(NetStringHandle &string, bool *isOnOtherSide) +{ + // see if the entry is in the hash table right now + U32 hashIndex = string.getIndex() % EntryCount; + for(Entry *walk = mHashTable[hashIndex]; walk; walk = walk->nextHash) + { + if(walk->string == string) + { + pushBack(walk); + if(isOnOtherSide) + *isOnOtherSide = walk->receiveConfirmed; + return walk->index; + } + } + + // not in the hash table, means we have to add it + Entry *newEntry; + + // pull the new entry from the LRU list. + newEntry = mLRUHead.nextLink; + pushBack(newEntry); + + // remove the string from the hash table + Entry **hashWalk; + for (hashWalk = &mHashTable[newEntry->string.getIndex() % EntryCount]; *hashWalk; hashWalk = &((*hashWalk)->nextHash)) + { + if(*hashWalk == newEntry) + { + *hashWalk = newEntry->nextHash; + break; + } + } + + newEntry->string = string; + newEntry->receiveConfirmed = false; + newEntry->nextHash = mHashTable[hashIndex]; + mHashTable[hashIndex] = newEntry; + + mParent->postNetEvent(new NetStringEvent(newEntry->index, string)); + if(isOnOtherSide) + *isOnOtherSide = false; + return newEntry->index; +} + +void ConnectionStringTable::mapString(U32 netId, NetStringHandle &string) +{ + // the netId is sent by the other side... throw it in our mapping table: + mRemoteStringTable[netId] = string; +} + +void ConnectionStringTable::readDemoStartBlock(BitStream *stream) +{ + // ok, reading the demo start block + for(U32 i = 0; i < EntryCount; i++) + { + if(stream->readFlag()) + { + char buffer[256]; + stream->readString(buffer); + mRemoteStringTable[i] = NetStringHandle(buffer); + } + } +} + +void ConnectionStringTable::writeDemoStartBlock(ResizeBitStream *stream) +{ + // ok, writing the demo start block + for(U32 i = 0; i < EntryCount; i++) + { + if(stream->writeFlag(mRemoteStringTable[i].isValidString())) + { + stream->writeString(mRemoteStringTable[i].getString()); + stream->validate(); + } + } +} diff --git a/Engine/source/sim/connectionStringTable.h b/Engine/source/sim/connectionStringTable.h new file mode 100644 index 000000000..a9805d4b3 --- /dev/null +++ b/Engine/source/sim/connectionStringTable.h @@ -0,0 +1,109 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _H_CONNECTIONSTRINGTABLE +#define _H_CONNECTIONSTRINGTABLE + +/// Maintain a table of strings which are shared across the network. +/// +/// This allows us to reference strings in our network streams more efficiently. +class ConnectionStringTable +{ +public: + enum Constants { + EntryCount = 32, + EntryBitSize = 5, + InvalidEntryId = 32, + }; +private: + struct Entry { + NetStringHandle string; ///< Global string table entry of this string + /// will be 0 if this string is unused. + + U32 index; ///< index of this entry + Entry *nextHash; ///< the next hash entry for this id + Entry *nextLink; ///< the next in the LRU list + Entry *prevLink; ///< the prev entry in the LRU list + bool receiveConfirmed; ///< The other side now has this string. + }; + + Entry mEntryTable[EntryCount]; + Entry *mHashTable[EntryCount]; + NetStringHandle mRemoteStringTable[EntryCount]; + Entry mLRUHead, mLRUTail; + + /// Connection over which we are maintaining this string table. + NetConnection *mParent; + + inline void pushBack(Entry *entry) // pushes an entry to the back of the LRU list + { + entry->prevLink->nextLink = entry->nextLink; + entry->nextLink->prevLink = entry->prevLink; + entry->nextLink = &mLRUTail; + entry->prevLink = mLRUTail.prevLink; + entry->nextLink->prevLink = entry; + entry->prevLink->nextLink = entry; + } + +public: + /// Initialize the connection string table. + /// + /// @param parent Connection over which we are maintaining this string table. + ConnectionStringTable(NetConnection *parent); + + /// Has the specified string been received on the other side? + inline void confirmStringReceived(NetStringHandle &string, U32 index) + { + if(mEntryTable[index].string == string) + mEntryTable[index].receiveConfirmed = true; + } + + U32 checkString(NetStringHandle &stringTableId, bool *stringOnOtherSide = NULL); ///< Checks if the global string ID is + /// currently valid for this connection + /// and returns the table ID. + /// Sends a string event to the other side + /// if it is not active. + /// It will fill in stringOnOtherSide. + + U32 getNetSendId(NetStringHandle &stringTableId); ///< Same return value as checkString + /// but will assert if the string is not + /// valid. + + void mapString(U32 netId, NetStringHandle &string); ///< Maps a string that + /// was just sent over the net + /// to the corresponding net ID. + + inline NetStringHandle lookupString(U32 netId) ///< looks up the string ID and returns + { /// the global string table ID for that string. + return mRemoteStringTable[netId]; + } + + /// @name Demo functionality + /// @{ + + void readDemoStartBlock(BitStream *stream); + void writeDemoStartBlock(ResizeBitStream *stream); + /// @} +}; + +#endif + diff --git a/Engine/source/sim/netConnection.cpp b/Engine/source/sim/netConnection.cpp new file mode 100644 index 000000000..ee1815e82 --- /dev/null +++ b/Engine/source/sim/netConnection.cpp @@ -0,0 +1,1494 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/dnet.h" +#include "console/simBase.h" +#include "sim/netConnection.h" +#include "core/stream/bitStream.h" +#include "core/stream/fileStream.h" +#ifndef TORQUE_TGB_ONLY +#include "scene/pathManager.h" +#endif +#include "console/consoleTypes.h" +#include "sim/netInterface.h" +#include "console/engineAPI.h" +#include + + +IMPLEMENT_SCOPE( NetAPI, Net,, "Networking functionality." ); + +IMPLEMENT_NONINSTANTIABLE_CLASS( NetEvent, + "An event to be sent over the network." ) +END_IMPLEMENT_CLASS; + + +S32 gNetBitsSent = 0; +extern S32 gNetBitsReceived; +U32 gGhostUpdates = 0; + +enum NetConnectionConstants { + PingTimeout = 4500, ///< milliseconds + DefaultPingRetryCount = 15, +}; + +SimObjectPtr NetConnection::mServerConnection; +SimObjectPtr NetConnection::mLocalClientConnection; + +//---------------------------------------------------------------------- +/// ConnectionMessageEvent +/// +/// This event is used inside by the connection and subclasses to message +/// itself when sequencing events occur. Right now, the message event +/// only uses 6 bits to transmit the message, so +class ConnectionMessageEvent : public NetEvent +{ + U32 sequence; + U32 message; + U32 ghostCount; +public: + typedef NetEvent Parent; + ConnectionMessageEvent(U32 msg=0, U32 seq=0, U32 gc=0) + { message = msg; sequence = seq; ghostCount = gc;} + void pack(NetConnection *, BitStream *bstream) + { + bstream->write(sequence); + bstream->writeInt(message, 3); + bstream->writeInt(ghostCount, NetConnection::GhostIdBitSize + 1); + } + void write(NetConnection *, BitStream *bstream) + { + bstream->write(sequence); + bstream->writeInt(message, 3); + bstream->writeInt(ghostCount, NetConnection::GhostIdBitSize + 1); + } + void unpack(NetConnection *, BitStream *bstream) + { + bstream->read(&sequence); + message = bstream->readInt(3); + ghostCount = bstream->readInt(NetConnection::GhostIdBitSize + 1); + } + void process(NetConnection *ps) + { + ps->handleConnectionMessage(message, sequence, ghostCount); + } + DECLARE_CONOBJECT(ConnectionMessageEvent); +}; + +IMPLEMENT_CO_NETEVENT_V1(ConnectionMessageEvent); + +ConsoleDocClass( ConnectionMessageEvent, + "@brief This event is used inside by the connection and subclasses to message itself when sequencing events occur.\n\n" + "Not intended for game development, for editors or internal use only.\n\n " + "@internal"); + +void NetConnection::sendConnectionMessage(U32 message, U32 sequence, U32 ghostCount) +{ + postNetEvent(new ConnectionMessageEvent(message, sequence, ghostCount)); +} + +//-------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(NetConnection); + +ConsoleDocClass( NetConnection, + "@brief Provides the basis for implementing a multiplayer game protocol.\n\n" + + "NetConnection combines a low-level notify protocol implemented in ConnectionProtocol with a SimGroup, " + "and implements several distinct subsystems:\n\n" + + "- Event Manager This is responsible for transmitting NetEvents over the wire. " + "It deals with ensuring that the various types of NetEvents are delivered appropriately, " + "and with notifying the event of its delivery status.\n\n" + "- Move Manager This is responsible for transferring a Move to the server 32 " + "times a second (on the client) and applying it to the control object (on the server).\n\n" + "- Ghost Manager This is responsible for doing scoping calculations (on the server " + "side) and transmitting most-recent ghost information to the client.\n\n" + "- File Transfer It is often the case that clients will lack important files when " + "connecting to a server which is running a mod or new map. This subsystem allows the " + "server to transfer such files to the client.\n\n" + "- Networked String Table String data can easily soak up network bandwidth, so for " + "efficiency, we implement a networked string table. We can then notify the connection " + "of strings we will reference often, such as player names, and transmit only a tag, " + "instead of the whole string.\n\n" + "- Demo Recording A demo in Torque is a log of the network traffic between client " + "and server; when a NetConnection records a demo, it simply logs this data to a file. When " + "it plays a demo back, it replays the logged data.\n\n" + "- Connection Database This is used to keep track of all the NetConnections; it can " + "be iterated over (for instance, to send an event to all active connections), or queried " + "by address.\n\n" + + "The NetConnection is a SimGroup. On the client side, it contains all the objects which have been " + "ghosted to that client. On the server side, it is empty; it can be used (typically in script) to " + "hold objects related to the connection. For instance, you might place an observation camera in the " + "NetConnnection. In both cases, when the connection is destroyed, so are the contained objects.\n\n" + + "The NetConnection also has the concept of local connections. These are used when the client and " + "server reside in the same process. A local connection is typically required to use the standard " + "Torque world building tools. A local connection is also required when building a single player " + "game.\n\n" + + "@see @ref Networking, @ref ghosting_scoping, @ref netconnection_simgroup, @ref local_connections, GameConnection, AIConnection, and AIClient.\n\n" + + "@ingroup Networking\n"); + +NetConnection* NetConnection::mConnectionList = NULL; +NetConnection* NetConnection::mHashTable[NetConnection::HashTableSize] = { NULL, }; + +bool NetConnection::mFilesWereDownloaded = false; + +static inline U32 HashNetAddress(const NetAddress *addr) +{ + return *((U32 *)addr->netNum) % NetConnection::HashTableSize; +} + +NetConnection *NetConnection::lookup(const NetAddress *addr) +{ + U32 hashIndex = HashNetAddress(addr); + for(NetConnection *walk = mHashTable[hashIndex]; walk; walk = walk->mNextTableHash) + if(Net::compareAddresses(addr, walk->getNetAddress())) + return walk; + return NULL; +} + +void NetConnection::netAddressTableInsert() +{ + U32 hashIndex = HashNetAddress(&mNetAddress); + mNextTableHash = mHashTable[hashIndex]; + mHashTable[hashIndex] = this; +} + +void NetConnection::netAddressTableRemove() +{ + U32 hashIndex = HashNetAddress(&mNetAddress); + NetConnection **walk = &mHashTable[hashIndex]; + while(*walk) + { + if(*walk == this) + { + *walk = mNextTableHash; + mNextTableHash = NULL; + return; + } + walk = &((*walk)->mNextTableHash); + } +} + +void NetConnection::setNetAddress(const NetAddress *addr) +{ + mNetAddress = *addr; +} + +const NetAddress *NetConnection::getNetAddress() +{ + return &mNetAddress; +} + +void NetConnection::setSequence(U32 sequence) +{ + mConnectSequence = sequence; +} + +U32 NetConnection::getSequence() +{ + return mConnectSequence; +} + +static U32 gPacketRateToServer = 32; +static U32 gPacketUpdateDelayToServer = 32; +static U32 gPacketRateToClient = 10; +static U32 gPacketSize = 200; + +void NetConnection::consoleInit() +{ + Con::addVariable("$pref::Net::PacketRateToServer", TypeS32, &gPacketRateToServer, + "@brief Sets how often packets are sent from the client to the server.\n\n" + + "It is possible to control how often packets may be sent to the server. This may be " + "used to throttle the amount of bandwidth being used, but should be adjusted with " + "caution.\n\n" + + "The actual formula used to calculate the delay between sending packets to the server is:\n" + + "@code\n" + "Packet Update Delay To Server = 1024 / $pref::Net::PacketRateToServer" + "@endcode\n" + + "with the result in ms. A minimum rate of 8 is enforced in the source code. The default " + "value is 32.\n\n" + + "@note When using a local connection (@ref local_connections) be aware that this variable " + "is always forced to 128.\n\n" + + "@ingroup Networking"); + + Con::addVariable("$pref::Net::PacketRateToClient", TypeS32, &gPacketRateToClient, + "@brief Sets how often packets are sent from the server to a client.\n\n" + + "It is possible to control how often packets may be sent to the clients. This may be " + "used to throttle the amount of bandwidth being used, but should be adjusted with " + "caution.\n\n" + + "The actual formula used to calculate the delay between sending packets to a client is:\n" + + "@code\n" + "Packet Update Delay To Client = 1024 / $pref::Net::PacketRateToClient" + "@endcode\n" + + "with the result in ms. A minimum rate of 1 is enforced in the source code. The default " + "value is 10.\n\n" + + "@note When using a local connection (@ref local_connections) be aware that this variable " + "is always forced to 128.\n\n" + + "@ingroup Networking"); + + Con::addVariable("$pref::Net::PacketSize", TypeS32, &gPacketSize, + "@brief Sets the maximum size in bytes an individual network packet may be.\n\n" + + "It is possible to control how large each individual network packet may be. Increasing " + "its size from the default allows for more data to be sent on each network send. " + "However, this value should be changed with caution as too large a value will cause " + "packets to be split up by the networking platform or hardware, which is something " + "Torque cannot handle.\n\n" + + "A minimum packet size of 100 bytes is enforced in the source code. There is no " + "enforced maximum. The default value is 200 bytes.\n\n" + + "@note When using a local connection (@ref local_connections) be aware that this variable " + "is always forced to 1024 bytes.\n\n" + + "@ingroup Networking"); + + Con::addVariable("$Stats::netBitsSent", TypeS32, &gNetBitsSent, + "@brief The number of bytes sent during the last packet send operation.\n\n" + + "@note Even though this variable has 'Bits' in it, the value is indeed reported in bytes. This name " + "is a legacy holdover and remains for compatibility reasons.\n" + + "@ingroup Networking"); + + Con::addVariable("$Stats::netBitsReceived", TypeS32, &gNetBitsReceived, + "@brief The number of bytes received during the last packet process operation.\n\n" + + "@note Even though this variable has 'Bits' in it, the value is indeed reported in bytes. This name " + "is a legacy holdover and remains for compatibility reasons.\n" + + "@ingroup Networking"); + + Con::addVariable("$Stats::netGhostUpdates", TypeS32, &gGhostUpdates, + "@brief The total number of ghosts added, removed, and/or updated on the client " + "during the last packet process operation.\n\n" + + "@ingroup Networking"); +} + +void NetConnection::checkMaxRate() +{ + // Enforce some minimum limits to the network settings. + gPacketRateToServer = getMax( gPacketRateToServer, (U32)8 ); + gPacketRateToClient = getMax( gPacketRateToClient, (U32)1 ); + gPacketSize = getMax( gPacketSize, (U32)100 ); + + U32 packetRateToServer = gPacketRateToServer; + U32 packetRateToClient = gPacketRateToClient; + U32 packetSize = gPacketSize; + + if (isLocalConnection()) + { + packetRateToServer = 128; + packetRateToClient = 128; + packetSize = 1024; + } + + gPacketUpdateDelayToServer = 1024 / packetRateToServer; + U32 toClientUpdateDelay = 1024 / packetRateToClient; + + if(mMaxRate.updateDelay != toClientUpdateDelay || mMaxRate.packetSize != packetSize) + { + mMaxRate.updateDelay = toClientUpdateDelay; + mMaxRate.packetSize = packetSize; + mMaxRate.changed = true; + } +} + +void NetConnection::setSendingEvents(bool sending) +{ + AssertFatal(!mEstablished, "Error, cannot change event behavior after a connection has been established."); + mSendingEvents = sending; +} + +void NetConnection::setTranslatesStrings(bool xl) +{ + AssertFatal(!mEstablished, "Error, cannot change event behavior after a connection has been established."); + mTranslateStrings = xl; + if(mTranslateStrings) + mStringTable = new ConnectionStringTable(this); +} + +void NetConnection::setNetClassGroup(U32 grp) +{ + AssertFatal(!mEstablished, "Error, cannot change net class group after a connection has been established."); + mNetClassGroup = grp; +} + +NetConnection::NetConnection() +{ + mTranslateStrings = false; + mConnectSequence = 0; + + mStringTable = NULL; + mSendingEvents = true; + mNetClassGroup = NetClassGroupGame; + AssertFatal(mNetClassGroup >= NetClassGroupGame && mNetClassGroup < NetClassGroupsCount, + "Invalid net event class type."); + + mSimulatedPing = 0; + mSimulatedPacketLoss = 0; +#ifdef TORQUE_DEBUG_NET + mLogging = false; +#endif + mEstablished = false; + mLastUpdateTime = 0; + mRoundTripTime = 0; + mPacketLoss = 0; + mNextTableHash = NULL; + mSendDelayCredit = 0; + mConnectionState = NotConnected; + + mCurrentDownloadingFile = NULL; + mCurrentFileBuffer = NULL; + + mNextConnection = NULL; + mPrevConnection = NULL; + + mNotifyQueueHead = NULL; + mNotifyQueueTail = NULL; + + mCurRate.updateDelay = 102; + mCurRate.packetSize = 200; + mCurRate.changed = false; + mMaxRate.updateDelay = 102; + mMaxRate.packetSize = 200; + mMaxRate.changed = false; + checkMaxRate(); + + // event management data: + + mNotifyEventList = NULL; + mSendEventQueueHead = NULL; + mSendEventQueueTail = NULL; + mUnorderedSendEventQueueHead = NULL; + mUnorderedSendEventQueueTail = NULL; + mWaitSeqEvents = NULL; + + mNextSendEventSeq = FirstValidSendEventSeq; + mNextRecvEventSeq = FirstValidSendEventSeq; + mLastAckedEventSeq = -1; + + // ghost management data: + + mScopeObject = NULL; + mGhostingSequence = 0; + mGhosting = false; + mScoping = false; + mGhostArray = NULL; + mGhostRefs = NULL; + mGhostLookupTable = NULL; + mLocalGhosts = NULL; + + mGhostsActive = 0; + + mMissionPathsSent = false; + mDemoWriteStream = NULL; + mDemoReadStream = NULL; + + mPingSendCount = 0; + mPingRetryCount = DefaultPingRetryCount; + mLastPingSendTime = Platform::getVirtualMilliseconds(); + + mCurrentDownloadingFile = NULL; + mCurrentFileBuffer = NULL; + mCurrentFileBufferSize = 0; + mCurrentFileBufferOffset = 0; + mNumDownloadedFiles = 0; + + // Disable starting a new journal recording or playback from here on + Journal::Disable(); +} + +NetConnection::~NetConnection() +{ + AssertFatal(mNotifyQueueHead == NULL, "Uncleared notifies remain."); + netAddressTableRemove(); + + dFree(mCurrentFileBuffer); + if(mCurrentDownloadingFile) + delete mCurrentDownloadingFile; + + delete[] mLocalGhosts; + delete[] mGhostLookupTable; + delete[] mGhostRefs; + delete[] mGhostArray; + delete mStringTable; + if(mDemoWriteStream) + delete mDemoWriteStream; + if(mDemoReadStream) + delete mDemoReadStream; +} + +NetConnection::PacketNotify::PacketNotify() +{ + rateChanged = false; + maxRateChanged = false; + sendTime = 0; + eventList = 0; + ghostList = 0; +} + +bool NetConnection::checkTimeout(U32 time) +{ + if(!isNetworkConnection()) + return false; + + if(time > mLastPingSendTime + PingTimeout) + { + if(mPingSendCount >= mPingRetryCount) + return true; + mLastPingSendTime = time; + mPingSendCount++; + sendPingPacket(); + } + return false; +} + +void NetConnection::keepAlive() +{ + mLastPingSendTime = Platform::getVirtualMilliseconds(); + mPingSendCount = 0; +} + +void NetConnection::handleConnectionEstablished() +{ +} + +//-------------------------------------------------------------------------- +#ifndef TORQUE_TGB_ONLY +DefineEngineMethod( NetConnection, transmitPaths, void, (),, + "@brief Sent by the server during phase 2 of the mission download to update motion spline paths.\n\n" + + "The server transmits all spline motion paths that are within the mission (Path) separate from " + "other objects. This is due to the potentially large number of nodes within each path, which may " + "saturate a packet sent to the client. By managing this step separately, Torque has finer control " + "over how packets are organised vs. doing it during the ghosting stage.\n\n" + + "Internally a PathManager is used to track all paths defined within a mission on the server, and each " + "one is transmitted using a PathManagerEvent. The client side collects these events and builds the " + "given paths within its own PathManager. This is typically done during the standard mission start " + "phase 2 when following Torque's example mission startup sequence.\n\n" + + "When a mission is ended, all paths need to be cleared from their respective path managers." + + "@tsexample\n" + "function serverCmdMissionStartPhase2Ack(%client, %seq, %playerDB)\n" + "{\n" + " // Make sure to ignore calls from a previous mission load\n" + " if (%seq != $missionSequence || !$MissionRunning)\n" + " return;\n" + " if (%client.currentPhase != 1.5)\n" + " return;\n" + " %client.currentPhase = 2;\n" + "\n" + " // Set the player datablock choice\n" + " %client.playerDB = %playerDB;\n" + "\n" + " // Update mission paths (SimPath), this needs to get there before the objects.\n" + " %client.transmitPaths();\n" + "\n" + " // Start ghosting objects to the client\n" + " %client.activateGhosting();\n" + "}\n" + "@endtsexample\n" + + "@see clearPaths()\n" + "@see Path\n") +{ + gServerPathManager->transmitPaths(object); + object->setMissionPathsSent(true); +} + +DefineEngineMethod( NetConnection, clearPaths, void, (),, + "@brief On the server, resets the connection to indicate that motion spline paths have not been transmitted.\n\n" + + "Typically when a mission has ended on the server, all connected clients are informed of this change " + "and their connections are reset back to a starting state. This method resets a connection on the " + "server to indicate that motion spline paths have not been transmitted.\n\n" + + "@tsexample\n" + " // Inform the clients\n" + " for (%clientIndex = 0; %clientIndex < ClientGroup.getCount(); %clientIndex++)\n" + " {\n" + " // clear ghosts and paths from all clients\n" + " %cl = ClientGroup.getObject(%clientIndex);\n" + " %cl.endMission();\n" + " %cl.resetGhosting();\n" + " %cl.clearPaths();\n" + " }\n" + "@endtsexample\n" + + "@see transmitPaths()\n" + "@see Path\n") +{ + object->setMissionPathsSent(false); +} +#endif + +DefineEngineMethod( NetConnection, getAddress, const char *, (),, + "@brief Returns the far end network address for the connection.\n\n" + + "The address will be in one of the following forms:\n" + "- IP:Broadcast:<port> for broadcast type addresses\n" + "- IP:<address>:<port> for IP addresses\n" + "- local when connected locally (server and client running in same process\n") +{ + if(object->isLocalConnection()) + return "local"; + char *buffer = Con::getReturnBuffer(256); + Net::addressToString(object->getNetAddress(), buffer); + return buffer; +} + +DefineEngineMethod( NetConnection, setSimulatedNetParams, void, (F32 packetLoss, S32 delay),, + "@brief Simulate network issues on the connection for testing.\n\n" + + "@param packetLoss The fraction of packets that will be lost. Ranges from 0.0 (no loss) to 1.0 (complete loss)\n" + "@param delay Delays packets being transmitted by simulating a particular ping. This is an absolute " + "integer, measured in ms.\n") +{ + object->setSimulatedNetParams(packetLoss, delay); +} + +DefineEngineMethod( NetConnection, getPing, S32, (),, + "@brief Returns the average round trip time (in ms) for the connection.\n\n" + + "The round trip time is recalculated every time a notify packet is received. Notify " + "packets are used to information the connection that the far end successfully received " + "the sent packet.\n") +{ + return( S32( object->getRoundTripTime() ) ); +} + +DefineEngineMethod( NetConnection, getPacketLoss, S32, (),, + "@brief Returns the percentage of packets lost per tick.\n\n" + + "@note This method is not yet hooked up.\n") +{ + return( S32( 100 * object->getPacketLoss() ) ); +} + +DefineEngineMethod( NetConnection, checkMaxRate, void, (),, + "@brief Ensures that all configured packet rates and sizes meet minimum requirements.\n\n" + + "This method is normally only called when a NetConnection class is first constructed. It need " + "only be manually called if the global variables that set the packet rate or size have changed.\n\n" + + "@note If @$pref::Net::PacketRateToServer, @$pref::Net::PacketRateToClient or @$pref::Net::PacketSize " + "have been changed since a NetConnection has been created, this method must be called on " + "all connections for them to follow the new rates or size.\n") +{ + object->checkMaxRate(); +} + +#ifdef TORQUE_DEBUG_NET + +DefineEngineMethod( NetConnection, setLogging, void, (bool state),, + "@brief Sets if debug statements should be written to the console log.\n\n" + "@note Only valid if the executable has been compiled with TORQUE_DEBUG_NET.\n") +{ + object->setLogging(state); +} + +#endif + +//-------------------------------------------------------------------- + +void NetConnection::setEstablished() +{ + AssertFatal(!mEstablished, "NetConnection::setEstablished - Error, this NetConnection has already been established."); + + mEstablished = true; + mNextConnection = mConnectionList; + if(mConnectionList) + mConnectionList->mPrevConnection = this; + mConnectionList = this; + + if(isNetworkConnection()) + netAddressTableInsert(); + +} + +void NetConnection::onRemove() +{ + // delete any ghosts that may exist for this connection, but aren't added + while(mGhostAlwaysSaveList.size()) + { + delete mGhostAlwaysSaveList[0].ghost; + mGhostAlwaysSaveList.pop_front(); + } + if(mNextConnection) + mNextConnection->mPrevConnection = mPrevConnection; + if(mPrevConnection) + mPrevConnection->mNextConnection = mNextConnection; + if(mConnectionList == this) + mConnectionList = mNextConnection; + while(mNotifyQueueHead) + handleNotify(false); + + ghostOnRemove(); + eventOnRemove(); + + Parent::onRemove(); +} + +String NetConnection::mErrorBuffer; + +void NetConnection::setLastError(const char *fmt, ...) +{ + va_list argptr; + va_start(argptr, fmt); + mErrorBuffer = String::ToString(fmt, argptr); + va_end(argptr); + +#ifdef TORQUE_DEBUG_NET + // setLastErrors assert in net_debug builds + AssertFatal(false, mErrorBuffer.c_str()); +#endif + +} + +//-------------------------------------------------------------------- + +void NetConnection::handleNotify(bool recvd) +{ +// Con::printf("NET %d: NOTIFY - %d %s", getId(), gPacketId, recvd ? "RECVD" : "DROPPED"); + + PacketNotify *note = mNotifyQueueHead; + AssertFatal(note != NULL, "Error: got a notify with a null notify head."); + mNotifyQueueHead = mNotifyQueueHead->nextPacket; + + if(note->rateChanged && !recvd) + mCurRate.changed = true; + if(note->maxRateChanged && !recvd) + mMaxRate.changed = true; + + if(recvd) + { + // Running average of roundTrip time + U32 curTime = Platform::getVirtualMilliseconds(); + mRoundTripTime = (mRoundTripTime + (curTime - note->sendTime)) * 0.5; + packetReceived(note); + } + else + packetDropped(note); + + delete note; +} + +void NetConnection::processRawPacket(BitStream *bstream) +{ + if(mDemoWriteStream) + recordBlock(BlockTypePacket, bstream->getReadByteSize(), bstream->getBuffer()); + + ConnectionProtocol::processRawPacket(bstream); +} + +void NetConnection::handlePacket(BitStream *bstream) +{ +// Con::printf("NET %d: RECV - %d", getId(), mLastSeqRecvd); + // clear out any errors + + mErrorBuffer = String(); + + if(bstream->readFlag()) + { + mCurRate.updateDelay = bstream->readInt(12); + mCurRate.packetSize = bstream->readInt(12); + } + + if(bstream->readFlag()) + { + U32 omaxDelay = bstream->readInt(12); + S32 omaxSize = bstream->readInt(12); + if(omaxDelay < mMaxRate.updateDelay) + omaxDelay = mMaxRate.updateDelay; + if(omaxSize > mMaxRate.packetSize) + omaxSize = mMaxRate.packetSize; + if(omaxDelay != mCurRate.updateDelay || omaxSize != mCurRate.packetSize) + { + mCurRate.updateDelay = omaxDelay; + mCurRate.packetSize = omaxSize; + mCurRate.changed = true; + } + } + readPacket(bstream); + + if(mErrorBuffer.isNotEmpty()) + connectionError(mErrorBuffer); +} + +void NetConnection::connectionError(const char *errorString) +{ + TORQUE_UNUSED(errorString); +} + +//-------------------------------------------------------------------- + +NetConnection::PacketNotify *NetConnection::allocNotify() +{ + return new PacketNotify; +} + +/// Used when simulating lag. +/// +/// We post this SimEvent when we want to send a packet; it delays for a bit, then +/// sends the actual packet. +class NetDelayEvent : public SimEvent +{ + U8 buffer[Net::MaxPacketDataSize]; + BitStream stream; +public: + NetDelayEvent(BitStream *inStream) : stream(NULL, 0) + { + dMemcpy(buffer, inStream->getBuffer(), inStream->getPosition()); + stream.setBuffer(buffer, inStream->getPosition()); + stream.setPosition(inStream->getPosition()); + } + void process(SimObject *object) + { + ((NetConnection *) object)->sendPacket(&stream); + } +}; + +void NetConnection::checkPacketSend(bool force) +{ + U32 curTime = Platform::getVirtualMilliseconds(); + U32 delay = isConnectionToServer() ? gPacketUpdateDelayToServer : mCurRate.updateDelay; + + if(!force) + { + if(curTime < mLastUpdateTime + delay - mSendDelayCredit) + return; + + mSendDelayCredit = curTime - (mLastUpdateTime + delay - mSendDelayCredit); + if(mSendDelayCredit > 1000) + mSendDelayCredit = 1000; + + if(mDemoWriteStream) + recordBlock(BlockTypeSendPacket, 0, 0); + } + if(windowFull()) + return; + + BitStream *stream = BitStream::getPacketStream(mCurRate.packetSize); + buildSendPacketHeader(stream); + + mLastUpdateTime = curTime; + + PacketNotify *note = allocNotify(); + if(!mNotifyQueueHead) + mNotifyQueueHead = note; + else + mNotifyQueueTail->nextPacket = note; + mNotifyQueueTail = note; + note->nextPacket = NULL; + note->sendTime = curTime; + + note->rateChanged = mCurRate.changed; + note->maxRateChanged = mMaxRate.changed; + + if(stream->writeFlag(mCurRate.changed)) + { + stream->writeInt(mCurRate.updateDelay, 12); + stream->writeInt(mCurRate.packetSize, 12); + mCurRate.changed = false; + } + if(stream->writeFlag(mMaxRate.changed)) + { + stream->writeInt(mMaxRate.updateDelay, 12); + stream->writeInt(mMaxRate.packetSize, 12); + mMaxRate.changed = false; + } +#ifdef TORQUE_DEBUG_NET + U32 start = stream->getCurPos(); +#endif + + DEBUG_LOG(("PKLOG %d START", getId()) ); + writePacket(stream, note); + DEBUG_LOG(("PKLOG %d END - %d", getId(), stream->getCurPos() - start) ); + if(mSimulatedPacketLoss && Platform::getRandom() < mSimulatedPacketLoss) + { + //Con::printf("NET %d: SENDDROP - %d", getId(), mLastSendSeq); + return; + } + if(mSimulatedPing) + { + Sim::postEvent(getId(), new NetDelayEvent(stream), Sim::getCurrentTime() + mSimulatedPing); + return; + } + sendPacket(stream); +} + +Net::Error NetConnection::sendPacket(BitStream *stream) +{ + //Con::printf("NET %d: SEND - %d", getId(), mLastSendSeq); + // do nothing on send if this is a demo replay. + if(mDemoReadStream) + return Net::NoError; + + gNetBitsSent = stream->getPosition(); + + if(isLocalConnection()) + { + // short circuit connection to the other side. + // handle the packet, then force a notify. + stream->setBuffer(stream->getBuffer(), stream->getPosition(), stream->getPosition()); + mRemoteConnection->processRawPacket(stream); + + return Net::NoError; + } + else + { + return Net::sendto(getNetAddress(), stream->getBuffer(), stream->getPosition()); + } +} + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- + +// these are the virtual function defs for Connection - +// if your subclass has additional data to read / write / notify, add it in these functions. + +void NetConnection::readPacket(BitStream *bstream) +{ + eventReadPacket(bstream); + ghostReadPacket(bstream); +} + +void NetConnection::writePacket(BitStream *bstream, PacketNotify *note) +{ + eventWritePacket(bstream, note); + ghostWritePacket(bstream, note); +} + +void NetConnection::packetReceived(PacketNotify *note) +{ + eventPacketReceived(note); + ghostPacketReceived(note); +} + +void NetConnection::packetDropped(PacketNotify *note) +{ + eventPacketDropped(note); + ghostPacketDropped(note); +} + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- + +void NetConnection::writeDemoStartBlock(ResizeBitStream* stream) +{ + ConnectionProtocol::writeDemoStartBlock(stream); + + stream->write(mRoundTripTime); + stream->write(mPacketLoss); +#ifndef TORQUE_TGB_ONLY + // Write all the current paths to the stream... + gClientPathManager->dumpState(stream); +#endif + stream->validate(); + mStringTable->writeDemoStartBlock(stream); + + U32 start = 0; + PacketNotify *note = mNotifyQueueHead; + while(note) + { + start++; + note = note->nextPacket; + } + stream->write(start); + + eventWriteStartBlock(stream); + ghostWriteStartBlock(stream); +} + +bool NetConnection::readDemoStartBlock(BitStream* stream) +{ + ConnectionProtocol::readDemoStartBlock(stream); + + stream->read(&mRoundTripTime); + stream->read(&mPacketLoss); + +#ifndef TORQUE_TGB_ONLY + // Read + gClientPathManager->readState(stream); +#endif + + mStringTable->readDemoStartBlock(stream); + U32 pos; + stream->read(&pos); // notify count + for(U32 i = 0; i < pos; i++) + { + PacketNotify *note = allocNotify(); + note->nextPacket = NULL; + if(!mNotifyQueueHead) + mNotifyQueueHead = note; + else + mNotifyQueueTail->nextPacket = note; + mNotifyQueueTail = note; + } + eventReadStartBlock(stream); + ghostReadStartBlock(stream); + return true; +} + +bool NetConnection::startDemoRecord(const char *fileName) +{ + FileStream *fs = new FileStream; + + if((fs = FileStream::createAndOpen( fileName, Torque::FS::File::Write )) == NULL) + return false; + + mDemoWriteStream = fs; + mDemoWriteStream->write(mProtocolVersion); + ResizeBitStream bs; + + // then write out the start block + writeDemoStartBlock(&bs); + U32 size = bs.getPosition() + 1; + mDemoWriteStream->write(size); + mDemoWriteStream->write(size, bs.getBuffer()); + return true; +} + +bool NetConnection::replayDemoRecord(const char *fileName) +{ + Stream *fs; + if((fs = FileStream::createAndOpen( fileName, Torque::FS::File::Read )) == NULL) + return false; + + mDemoReadStream = fs; + mDemoReadStream->read(&mProtocolVersion); + U32 size; + mDemoReadStream->read(&size); + U8 *block = new U8[size]; + mDemoReadStream->read(size, block); + BitStream bs(block, size); + + bool res = readDemoStartBlock(&bs); + delete[] block; + if(!res) + return false; + + // prep for first block read + // type/size stored in U16: [type:4][size:12] + U16 typeSize; + mDemoReadStream->read(&typeSize); + + mDemoNextBlockType = typeSize >> 12; + mDemoNextBlockSize = typeSize & 0xFFF; + + if(mDemoReadStream->getStatus() != Stream::Ok) + return false; + return true; +} + +void NetConnection::stopRecording() +{ + if(mDemoWriteStream) + { + delete mDemoWriteStream; + mDemoWriteStream = NULL; + } +} + +void NetConnection::recordBlock(U32 type, U32 size, void *data) +{ + AssertFatal(type < MaxNumBlockTypes, "NetConnection::recordBlock: invalid type"); + AssertFatal(size < MaxBlockSize, "NetConnection::recordBlock: invalid size"); + if((type >= MaxNumBlockTypes) || (size >= MaxBlockSize)) + return; + + if(mDemoWriteStream) + { + // store type/size in U16: [type:4][size:12] + U16 typeSize = (type << 12) | size; + mDemoWriteStream->write(typeSize); + if(size) + mDemoWriteStream->write(size, data); + } +} + +void NetConnection::handleRecordedBlock(U32 type, U32 size, void *data) +{ + switch(type) + { + case BlockTypePacket: { + BitStream bs(data, size); + processRawPacket(&bs); + break; + } + case BlockTypeSendPacket: + checkPacketSend(true); + break; + } +} + +void NetConnection::demoPlaybackComplete() +{ +} + +void NetConnection::stopDemoPlayback() +{ + demoPlaybackComplete(); + deleteObject(); +} + +bool NetConnection::processNextBlock() +{ + U8 buffer[Net::MaxPacketDataSize]; + + // read in and handle + if(mDemoReadStream->read(mDemoNextBlockSize, buffer)) + handleRecordedBlock(mDemoNextBlockType, mDemoNextBlockSize, buffer); + + U16 typeSize; + mDemoReadStream->read(&typeSize); + + mDemoNextBlockType = typeSize >> 12; + mDemoNextBlockSize = typeSize & 0xFFF; + + if(mDemoReadStream->getStatus() != Stream::Ok) + { + stopDemoPlayback(); + return false; + } + return true; +} + +//-------------------------------------------------------------------- +//-------------------------------------------------------------------- + +// some handy string functions for compressing strings over a connection: +enum NetStringConstants +{ + NullString = 0, + CString, + TagString, + Integer +}; + +void NetConnection::validateSendString(const char *str) +{ + if(U8(*str) == StringTagPrefixByte) + { + NetStringHandle strHandle(dAtoi(str + 1)); + checkString(strHandle); + } +} + +void NetConnection::packString(BitStream *stream, const char *str) +{ + char buf[16]; + if(!*str) + { + stream->writeInt(NullString, 2); + return; + } + if(U8(str[0]) == StringTagPrefixByte) + { + stream->writeInt(TagString, 2); + stream->writeInt(dAtoi(str + 1), ConnectionStringTable::EntryBitSize); + return; + } + if(str[0] == '-' || (str[0] >= '0' && str[0] <= '9')) + { + S32 num = dAtoi(str); + dSprintf(buf, sizeof(buf), "%d", num); + if(!dStrcmp(buf, str)) + { + stream->writeInt(Integer, 2); + if(stream->writeFlag(num < 0)) + num = -num; + if(stream->writeFlag(num < 128)) + { + stream->writeInt(num, 7); + return; + } + if(stream->writeFlag(num < 32768)) + { + stream->writeInt(num, 15); + return; + } + else + { + stream->writeInt(num, 31); + return; + } + } + } + stream->writeInt(CString, 2); + stream->writeString(str); +} + +void NetConnection::unpackString(BitStream *stream, char readBuffer[1024]) +{ + U32 code = stream->readInt(2); + switch(code) + { + case NullString: + readBuffer[0] = 0; + return; + case CString: + { + stream->readString(readBuffer); + return; + } + case TagString: + U32 tag; + tag = stream->readInt(ConnectionStringTable::EntryBitSize); + readBuffer[0] = StringTagPrefixByte; + dSprintf(readBuffer+1, 1023, "%d", tag); + return; + case Integer: + bool neg; + neg = stream->readFlag(); + S32 num; + if(stream->readFlag()) + num = stream->readInt(7); + else if(stream->readFlag()) + num = stream->readInt(15); + else + num = stream->readInt(31); + if(neg) + num = -num; + dSprintf(readBuffer, 1024, "%d", num); + } +} + +void NetConnection::packNetStringHandleU(BitStream *stream, NetStringHandle &h) +{ + if(stream->writeFlag(h.isValidString() )) + { + bool isReceived; + U32 netIndex = checkString(h, &isReceived); + if(stream->writeFlag(isReceived)) + stream->writeInt(netIndex, ConnectionStringTable::EntryBitSize); + else + stream->writeString(h.getString()); + } +} + +NetStringHandle NetConnection::unpackNetStringHandleU(BitStream *stream) +{ + NetStringHandle ret; + if(stream->readFlag()) + { + if(stream->readFlag()) + ret = mStringTable->lookupString(stream->readInt(ConnectionStringTable::EntryBitSize)); + else + { + char buf[256]; + stream->readString(buf); + ret = NetStringHandle(buf); + } + } + return ret; +} + +//---------------------------------------------------------------------------- + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +void NetConnection::setAddressDigest(U32 digest[4]) +{ + mAddressDigest[0] = digest[0]; + mAddressDigest[1] = digest[1]; + mAddressDigest[2] = digest[2]; + mAddressDigest[3] = digest[3]; +} + +void NetConnection::getAddressDigest(U32 digest[4]) +{ + digest[0] = mAddressDigest[0]; + digest[1] = mAddressDigest[1]; + digest[2] = mAddressDigest[2]; + digest[3] = mAddressDigest[3]; +} + +bool NetConnection::canRemoteCreate() +{ + return false; +} + +void NetConnection::onTimedOut() +{ + +} + +void NetConnection::connect(const NetAddress *address) +{ + mNetAddress = *address; + GNet->startConnection(this); +} + +void NetConnection::onConnectTimedOut() +{ + +} + +void NetConnection::sendDisconnectPacket(const char *reason) +{ + GNet->sendDisconnectPacket(this, reason); +} + +void NetConnection::onDisconnect(const char *reason) +{ + TORQUE_UNUSED(reason); +} + +void NetConnection::onConnectionRejected(const char *reason) +{ +} + +void NetConnection::onConnectionEstablished(bool isInitiator) +{ + +} + +void NetConnection::handleStartupError(const char *errorString) +{ + +} + +void NetConnection::writeConnectRequest(BitStream *stream) +{ + stream->write(mNetClassGroup); + stream->write(U32(AbstractClassRep::getClassCRC(mNetClassGroup))); +} + +bool NetConnection::readConnectRequest(BitStream *stream, const char **errorString) +{ + U32 classGroup, classCRC; + stream->read(&classGroup); + stream->read(&classCRC); + + if(classGroup == mNetClassGroup && classCRC == AbstractClassRep::getClassCRC(mNetClassGroup)) + return true; + + *errorString = "CHR_INVALID"; + return false; +} + +void NetConnection::writeConnectAccept(BitStream *stream) +{ + TORQUE_UNUSED(stream); +} + +bool NetConnection::readConnectAccept(BitStream *stream, const char **errorString) +{ + TORQUE_UNUSED(stream); + TORQUE_UNUSED(errorString); + return true; +} + +DefineEngineMethod( NetConnection, resolveGhostID, S32, (S32 ghostID),, + "@brief On the client, convert a ghost ID from this connection to a real SimObject ID.\n\n" + + "Torque's network ghosting system only exchanges ghost ID's between the server and client. Use " + "this method on the client to discover an object's local SimObject ID when you only have a " + "ghost ID.\n" + + "@param ghostID The ghost ID of the object as sent by the server.\n" + "@returns The SimObject ID of the object, or 0 if it could not be resolved.\n\n" + + "@tsexample\n" + "%object = ServerConnection.resolveGhostID( %ghostId );\n" + "@endtsexample\n\n" + + "@see @ref ghosting_scoping for a description of the ghosting system.\n\n") +{ + // Safety check + if(ghostID < 0 || ghostID > NetConnection::MaxGhostCount) return 0; + + NetObject *foo = object->resolveGhost(ghostID); + + if(foo) + return foo->getId(); + else + return 0; +} + +DefineEngineMethod( NetConnection, resolveObjectFromGhostIndex, S32, (S32 ghostID),, + "@brief On the server, convert a ghost ID from this connection to a real SimObject ID.\n\n" + + "Torque's network ghosting system only exchanges ghost ID's between the server and client. Use " + "this method on the server to discover an object's local SimObject ID when you only have a " + "ghost ID.\n" + + "@param ghostID The ghost ID of the object as sent by the server.\n" + "@returns The SimObject ID of the object, or 0 if it could not be resolved.\n\n" + + "@tsexample\n" + "%object = %client.resolveObjectFromGhostIndex( %ghostId );\n" + "@endtsexample\n\n" + + "@see @ref ghosting_scoping for a description of the ghosting system.\n\n") +{ + // Safety check + if(ghostID < 0 || ghostID > NetConnection::MaxGhostCount) return 0; + + NetObject *foo = object->resolveObjectFromGhostIndex(ghostID); + + if(foo) + return foo->getId(); + else + return 0; +} + +DefineEngineMethod( NetConnection, getGhostID, S32, (S32 realID),, + "@brief On server or client, convert a real id to the ghost id for this connection.\n\n" + + "Torque's network ghosting system only exchanges ghost ID's between the server and client. Use " + "this method on the server or client to discover an object's ghost ID based on its real SimObject ID.\n" + + "@param realID The real SimObject ID of the object.\n" + "@returns The ghost ID of the object for this connection, or -1 if it could not be resolved.\n\n" + + "@see @ref ghosting_scoping for a description of the ghosting system.\n\n") +{ + NetObject * foo; + + if(Sim::findObject(realID, foo)) + { + return object->getGhostIndex(foo); + } + else + { + Con::errorf("NetConnection::serverToGhostID - could not find specified object"); + return -1; + } +} + +DefineEngineMethod( NetConnection, connect, void, (const char* remoteAddress),, + "@brief Connects to the remote address.\n\n" + + "Attempts to connect with another NetConnection on the given address. Typically once " + "connected, a game's information is passed along from the server to the client, followed " + "by the player entering the game world. The actual procedure is dependent on " + "the NetConnection subclass that is used. i.e. GameConnection.\n" + + "@param remoteAddress The address to connect to in the form of IP:<address>:<port&rt; " + "although the IP: portion is optional. The address portion may be in the form " + "of w.x.y.z or as a host name, in which case a DNS lookup will be performed. You may also " + "substitue the word broadcast for the address to broadcast the connect request over " + "the local subnet.\n\n" + + "@see NetConnection::connectLocal() to connect to a server running within the same process " + "as the client.\n" + ) +{ + NetAddress addr; + if(!Net::stringToAddress(remoteAddress, &addr)) + { + Con::errorf("NetConnection::connect: invalid address - %s", remoteAddress); + return; + } + object->connect(&addr); +} + +DefineEngineMethod( NetConnection, connectLocal, const char*, (),, + "@brief Connects with the server that is running within the same process as the client.\n\n" + + "@returns An error text message upon failure, or an empty string when successful.\n\n" + + "@see See @ref local_connections for a description of local connections and their use. See " + "NetConnection::connect() to connect to a server running in another process (on the same machine or not).\n") +{ + ConsoleObject *co = ConsoleObject::create(object->getClassName()); + NetConnection *client = object; + NetConnection *server = dynamic_cast(co); + BitStream *stream = BitStream::getPacketStream(); + + if(!server || !server->canRemoteCreate()) + { + delete co; + return "error"; + } + + server->registerObject(); + server->setIsLocalClientConnection(); + + server->setSequence(0); + client->setSequence(0); + client->setRemoteConnectionObject(server); + server->setRemoteConnectionObject(client); + + //We need to reset the maxrate's here, because we + // can't test if it is a local connection until RemoteConnectionObject + // has been set + server->checkMaxRate(); + client->checkMaxRate(); + + stream->setPosition(0); + client->writeConnectRequest(stream); + stream->setPosition(0); + + const char* error; + if( !server->readConnectRequest( stream, &error ) ) + { + client->onConnectionRejected( error ); + server->deleteObject(); + return "error"; + } + + stream->setPosition(0); + server->writeConnectAccept(stream); + stream->setPosition(0); + + if( !client->readConnectAccept( stream, &error ) ) + { + client->handleStartupError( error ); + server->deleteObject(); + return "error"; + } + + client->onConnectionEstablished(true); + server->onConnectionEstablished(false); + client->setEstablished(); + server->setEstablished(); + client->setConnectSequence(0); + server->setConnectSequence(0); + NetConnection::setLocalClientConnection(server); + server->assignName("LocalClientConnection"); + + return ""; +} diff --git a/Engine/source/sim/netConnection.h b/Engine/source/sim/netConnection.h new file mode 100644 index 000000000..914c11274 --- /dev/null +++ b/Engine/source/sim/netConnection.h @@ -0,0 +1,1160 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _NETCONNECTION_H_ +#define _NETCONNECTION_H_ + +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _NETOBJECT_H_ +#include "sim/netObject.h" +#endif +#ifndef _NETSTRINGTABLE_H_ +#include "sim/netStringTable.h" +#endif +#ifndef _EVENT_H_ +#include "platform/event.h" +#endif +#ifndef _DNET_H_ +#include "core/dnet.h" +#endif + +#ifndef _H_CONNECTIONSTRINGTABLE +#include "sim/connectionStringTable.h" +#endif + +class NetConnection; +class NetObject; +class BitStream; +class ResizeBitStream; +class Stream; +class Point3F; + +struct GhostInfo; +struct SubPacketRef; // defined in NetConnection subclass + +//#define DEBUG_NET + +#ifdef TORQUE_DEBUG_NET +#define DEBUG_LOG(x) if(mLogging){Con::printf x;} +#else +#define DEBUG_LOG(x) +#endif + +DECLARE_SCOPE( NetAPI ); + +//---------------------------------------------------------------------------- + +class NetEvent; + +struct NetEventNote +{ + NetEvent *mEvent; + S32 mSeqCount; + NetEventNote *mNextEvent; +}; + +/// An event to be sent over the network. +/// +/// @note Torque implements two methods of network data passing; this is one of them. +/// See NetConnection for details of the other, which is referred to as ghosting. +/// +/// Torque's network layer lets you pass events to/from the server. There are three +/// types of events: +/// - Unguaranteed events are events which are sent once. If they don't +/// make it through the link, they are not resent. This is good for quick, +/// frequent status updates which are of transient interest, like position +/// updates or voice communication. +/// - Guaranteed events are events which are guaranteed to be +/// delivered. If they don't make it through the link, they are sent as +/// needed. This is good for important, one-time information, +/// like which team a user wants to play on, or the current weather. +/// - GuaranteedOrdered events are events which are guaranteed not +/// only to be delivered, but to be delivered in order. This is good for +/// information which is not only important, but also order-critical, like +/// chat messages. +/// +/// There are 6 methods that you need to implement if you want to make a +/// basic NetEvent subclass, and 2 macros you need to call. +/// +/// @code +/// // A simple NetEvent to transmit a string over the network. +/// // This is based on the code in netTest.cc +/// class SimpleMessageEvent : public NetEvent +/// { +/// typedef NetEvent Parent; +/// char *msg; +/// public: +/// SimpleMessageEvent(const char *message = NULL); +/// ~SimpleMessageEvent(); +/// +/// virtual void pack (NetConnection *conn, BitStream *bstream); +/// virtual void write (NetConnection *conn, BitStream *bstream); +/// virtual void unpack (NetConnection *conn, BitStream *bstream); +/// virtual void process(NetConnection *conn); +/// +/// DECLARE_CONOBJECT(SimpleMessageEvent); +/// }; +/// +/// IMPLEMENT_CO_NETEVENT_V1(SimpleMessageEvent); +/// @endcode +/// +/// Notice the two macros which we call. The first, DECLARE_CONOBJECT() is there +/// because we're a ConsoleObject. The second, IMPLEMENT_CO_NETEVENT_V1(), is there +/// to register this event type with Torque's networking layer, so that it can be +/// properly transmitted over the wire. There are three macros which you might use: +/// - IMPLEMENT_CO_NETEVENT_V1, which indicates an event which may be sent +/// in either direction, from the client to the server, or from the server to the +/// client. +/// - IMPLEMENT_CO_CLIENTEVENT_V1, which indicates an event which may only +/// be sent to the client. +/// - IMPLEMENT_CO_SERVEREVENT_V1, which indicates an event which may only +/// be sent to the server. +/// +/// Choosing the right macro is a good way to make your game more resistant to hacking; for instance, +/// PathManager events are marked as CLIENTEVENTs, because they would cause the server to crash if +/// a client sent them. +/// +/// @note Torque allows you to call NetConnection::setLastError() on the NetConnection passed to +/// your NetEvent. You can cause the connection to abort if invalid data is received, specifying +/// a reason to the user. +/// +/// Now, the 6 methods which we have above; the constructor and destructor need only do +/// whatever book-keeping is needed for your specific implementation. In our case, we +/// just need to allocate/deallocate the space for our string: +/// +/// @code +/// SimpleMessageEvent::SimpleMessageEvent(const char *message = NULL) +/// { +/// // If we wanted to make this not be a GuaranteedOrdered event, we'd +/// // put a line like this in the constructor: +/// // mGuaranteeType = Guaranteed; +/// // (or whatever type you wanted.) +/// if(message) +/// msg = dStrdup(message); +/// else +/// msg = NULL; +/// } +/// +/// SimpleMessageEvent::~SimpleMessageEvent() +/// { +/// dFree(msg); +/// } +/// @endcode +/// +/// Simple as that! Now, onto pack(), write(), unpack(), process(). +/// +/// pack() is responsible for packing the event over the wire: +/// +/// @code +/// void SimpleMessageEvent::pack(NetConnection* conn, BitStream *bstream) +/// { +/// bstream->writeString(msg); +/// } +/// @endcode +/// +/// unpack() is responsible for unpacking the event on the other end: +/// +/// @code +/// // The networking layer takes care of instantiating a new +/// // SimpleMessageEvent, which saves us a bit of effort. +/// void SimpleMessageEvent::unpack(NetConnection *conn, BitStream *bstream) +/// { +/// char buf[256]; +/// bstream->readString(buf); +/// msg = dStrdup(buf); +/// } +/// @endcode +/// +/// process() is called when the network layer is finished with things. +/// A typical case is that a GuaranteedOrdered event is unpacked and stored, but +/// not processed until the events preceding it in the sequence have also been +/// dealt with. +/// +/// @code +/// // This just prints the event in the console. You might +/// // want to do something more clever here -- BJG +/// void SimpleMessageEvent::process(NetConnection *conn) +/// { +/// Con::printf("RMSG %d %s", mSourceId, msg); +/// } +/// @endcode +/// +/// write() is called if a demo recording is started, and the event has not yet been +/// processed, but it has been unpacked. It should be identical in its output to the bitstream +/// compared to pack(), but since it is called after unpack() some lookups may not need to be +/// performed. In normal demo recording, whole network packets are recorded, meaning that most +/// of the time write() will not be called. +/// +/// In our case, it's entirely identical to pack(): +/// +/// @code +/// virtual void write(NetConnection*, BitStream *bstream) +/// { +/// bstream->writeString(msg); +/// } +/// @endcode +/// +/// The NetEvent is sent over the wire in a straightforward way (assuming you have a +/// handle to a NetConnection): +/// +/// @code +/// NetConnection *conn; // We assume you have filled this in. +/// +/// con->postNetEvent(new SimpleMessageEvent("This is a test!")); +/// @endcode +/// +/// @see GhostAlwaysObjectEvent for an example of dissimilar write()/pack() methods. +/// +/// Finally, for more advanced applications, notifySent() is called whenever the event is +/// sent over the wire, in NetConnection::eventWritePacket(). notifyDelivered() is called +/// when the packet is finally received or (in the case of Unguaranteed packets) dropped. +/// +/// @note IMPLEMENT_CO_NETEVENT_V1 and co. have sibling macros which allow you to specify a +/// groupMask; see ConsoleObject for a further discussion of this. +class NetEvent : public ConsoleObject +{ +public: + + DECLARE_ABSTRACT_CLASS( NetEvent, ConsoleObject ); + DECLARE_INSCOPE( NetAPI ); + + /// @name Implementation Details + /// + /// These are internal fields which you won't need to manipulate, except for mGuaranteeType. + /// @{ + + /// + typedef ConsoleObject Parent; + enum { + GuaranteedOrdered = 0, + Guaranteed = 1, + Unguaranteed = 2 + } mGuaranteeType; + NetConnectionId mSourceId; + + void incRef() + { + incRefCount(); + } + void decRef() + { + decRefCount(); + } + +#ifdef TORQUE_DEBUG_NET + virtual const char *getDebugName(); +#endif + /// @} + + /// @name Things To Subclass + /// @{ + + /// + NetEvent() { mGuaranteeType = GuaranteedOrdered; } + virtual ~NetEvent(); + + virtual void write(NetConnection *ps, BitStream *bstream) = 0; + virtual void pack(NetConnection *ps, BitStream *bstream) = 0; + virtual void unpack(NetConnection *ps, BitStream *bstream) = 0; + virtual void process(NetConnection *ps) = 0; + virtual void notifySent(NetConnection *ps); + virtual void notifyDelivered(NetConnection *ps, bool madeit); + /// @} +}; + +#define IMPLEMENT_CO_NETEVENT_V1(className) \ + IMPLEMENT_CLASS( className, NULL ) \ + END_IMPLEMENT_CLASS; \ + S32 className::_smTypeId; \ + AbstractClassRep* className::getClassRep() const { return &className::dynClassRep; } \ + AbstractClassRep* className::getStaticClassRep() { return &dynClassRep; } \ + AbstractClassRep* className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ + ConcreteClassRep className::dynClassRep( #className, "Type" #className, &_smTypeId, NetClassGroupGameMask, NetClassTypeEvent, NetEventDirAny, className::getParentStaticClassRep(), &Parent::__description) + +#define IMPLEMENT_CO_CLIENTEVENT_V1(className) \ + IMPLEMENT_CLASS( className, NULL ) \ + END_IMPLEMENT_CLASS; \ + S32 className::_smTypeId; \ + AbstractClassRep* className::getClassRep() const { return &className::dynClassRep; } \ + AbstractClassRep* className::getStaticClassRep() { return &dynClassRep; } \ + AbstractClassRep* className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ + ConcreteClassRep className::dynClassRep(#className, "Type" #className, &_smTypeId,NetClassGroupGameMask, NetClassTypeEvent, NetEventDirServerToClient, className::getParentStaticClassRep(), &Parent::__description) + +#define IMPLEMENT_CO_SERVEREVENT_V1(className) \ + IMPLEMENT_CLASS( className, NULL ) \ + END_IMPLEMENT_CLASS; \ + S32 className::_smTypeId; \ + AbstractClassRep* className::getClassRep() const { return &className::dynClassRep; } \ + AbstractClassRep* className::getStaticClassRep() { return &dynClassRep; } \ + AbstractClassRep* className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ + ConcreteClassRep className::dynClassRep(#className, "Type" #className, &_smTypeId, NetClassGroupGameMask, NetClassTypeEvent, NetEventDirClientToServer, className::getParentStaticClassRep(), &Parent::__description) + +#define IMPLEMENT_CO_NETEVENT(className,groupMask) \ + IMPLEMENT_CLASS( className, NULL ) \ + END_IMPLEMENT_CLASS; \ + S32 className::_smTypeId; \ + AbstractClassRep* className::getClassRep() const { return &className::dynClassRep; } \ + AbstractClassRep* className::getStaticClassRep() { return &dynClassRep; } \ + AbstractClassRep* className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ + ConcreteClassRep className::dynClassRep(#className, "Type" #className, &_smTypeId, groupMask, NetClassTypeEvent, NetEventDirAny, className::getParentStaticClassRep(), &Parent::__description) + +#define IMPLEMENT_CO_CLIENTEVENT(className,groupMask) \ + IMPLEMENT_CLASS( className, NULL ) \ + END_IMPLEMENT_CLASS; \ + S32 className::_smTypeId; \ + AbstractClassRep* className::getClassRep() const { return &className::dynClassRep; } \ + AbstractClassRep* className::getStaticClassRep() { return &dynClassRep; } \ + AbstractClassRep* className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ + ConcreteClassRep className::dynClassRep(#className, "Type" #className, &_smTypeId, groupMask, NetClassTypeEvent, NetEventDirServerToClient, className::getParentStaticClassRep(), &Parent::__description) + +#define IMPLEMENT_CO_SERVEREVENT(className,groupMask) \ + IMPLEMENT_CLASS( className, className, __scope, NULL ) \ + END_IMPLEMENT_CLASS; \ + S32 className::_smTypeId; \ + AbstractClassRep* className::getClassRep() const { return &className::dynClassRep; } \ + AbstractClassRep* className::getStaticClassRep() { return &dynClassRep; } \ + AbstractClassRep* className::getParentStaticClassRep() { return Parent::getStaticClassRep(); } \ + ConcreteClassRep className::dynClassRep(#className, "Type" #className, &_smTypeId, groupMask, NetClassTypeEvent, NetEventDirClientToServer, className::getParentStaticClassRep(), &Parent::__description) + + +//---------------------------------------------------------------------------- + +/// Torque network connection. +/// +/// @section NetConnection_intro Introduction +/// +/// NetConnection is the glue that binds a networked Torque game together. It combines +/// the low-level notify protocol implemented in ConnectionProtocol with a SimGroup to +/// provide a powerful basis for implementing a multiplayer game protocol. +/// +/// On top of this basis it implements several distinct subsystems: +/// - Event manager, which is responsible for transmitting NetEvents over the wire. +/// It deals with ensuring that the various types of NetEvents are delivered appropriately, +/// and with notifying the event of its delivery status. +/// - Move manager, which is responsible for transferring a Move to the server 32 +/// times a second (on the client) and applying it to the control object (on the server). +/// - Ghost manager, which is responsible for doing scoping calculations (on the server +/// side) and transmitting most-recent ghost information to the client. +/// - File transfer; it is often the case that clients will lack important files when +/// connecting to a server which is running a mod or new map. This subsystem allows the +/// server to transfer such files to the client. +/// - Networked String Table; string data can easily soak up network bandwidth, so for +/// efficiency, we implement a networked string table. We can then notify the connection +/// of strings we will reference often, such as player names, and transmit only a tag, +/// instead of the whole string. +/// - Demo Recording is also implemented in NetConnection. A demo in Torque is a log +/// of the network traffic between client and server; when a NetConnection records a demo, +/// it simply logs this data to a file. When it plays a demo back, it replays the logged +/// data. +/// - The Connection Database is used to keep track of all the NetConnections; it can +/// be iterated over (for instance, to send an event to all active connections), or queried +/// by address. +/// +/// @section NetConnection_events On Events +/// +/// The Event Manager is exposed to the outside world via postNetEvent(), which accepts NetEvents. +/// +/// @see NetEvent for a more thorough explanation of how to use events. +/// +/// @section NetConnection_ghosting On Ghosting and Scoping +/// +/// Ghosting is the most complex, and most powerful, part of Torque's networking capabilities. It +/// allows the information sent to clients to be very precisely matched to what they need, so that +/// no excess bandwidth is wasted. The control object's onCameraScopeQuery() is called, to determine +/// scoping information for the client; then objects which are in scope are then transmitted to the +/// client, prioritized by the results of their getPriority() method. +/// +/// There is a cap on the maximum number of ghosts; ghost IDs are currently sent via a 12-bit field, +/// ergo, there is a cap of 4096 objects ghosted per client. This can be easily raised; see the +/// GhostConstants enum. +/// +/// Each object ghosted is assigned a ghost ID; the client is _only_ aware of the ghost ID. This acts +/// to enhance game security, as it becomes difficult to map objects from one connection to another, or +/// to reliably identify objects from ID alone. IDs are also reassigned based on need, making it hard +/// to track objects that have fallen out of scope (as any object which the player shouldn't see would). +/// +/// resolveGhost() is used on the client side, and resolveObjectFromGhostIndex() on the server side, to +/// turn ghost IDs into object references. +/// +/// The NetConnection is a SimGroup. On the client side, it contains all the objects which have been +/// ghosted to that client. On the server side, it is empty; it can be used (typically in script) to +/// hold objects related to the connection. For instance, you might place an observation camera in the +/// NetConnnection. In both cases, when the connection is destroyed, so are the contained objects. +/// +/// @see NetObject, which is the superclass for ghostable objects, and ShapeBase, which is the base +/// for player and vehicle classes. +/// +/// @nosubgrouping +class NetConnection : public SimGroup, public ConnectionProtocol +{ + friend class NetInterface; + + typedef SimGroup Parent; + +public: + /// Structure to track ghost references in packets. + /// + /// Every packet we send out with an update from a ghost causes one of these to be + /// allocated. mask is used to track what states were sent; that way if a packet is + /// dropped, we can easily manipulate the stored states and figure out what if any data + /// we need to resend. + /// + struct GhostRef + { + U32 mask; ///< States we transmitted. + U32 ghostInfoFlags; ///< Flags from GhostInfo::Flags + GhostInfo *ghost; ///< Reference to the GhostInfo we're from. + GhostRef *nextRef; ///< Next GhostRef in this packet. + GhostRef *nextUpdateChain; ///< Next update we sent for this ghost. + }; + + enum Constants + { + HashTableSize = 127, + }; + + void sendDisconnectPacket(const char *reason); + + virtual bool canRemoteCreate(); + + virtual void onTimedOut(); + virtual void onConnectTimedOut(); + virtual void onDisconnect(const char *reason); + virtual void onConnectionRejected(const char *reason); + virtual void onConnectionEstablished(bool isInitiator); + virtual void handleStartupError(const char *errorString); + + virtual void writeConnectRequest(BitStream *stream); + virtual bool readConnectRequest(BitStream *stream, const char **errorString); + + virtual void writeConnectAccept(BitStream *stream); + virtual bool readConnectAccept(BitStream *stream, const char **errorString); + + void connect(const NetAddress *address); + + //---------------------------------------------------------------- + /// @name Global Connection List + /// @{ + +private: + /// + NetConnection *mNextConnection; ///< Next item in list. + NetConnection *mPrevConnection; ///< Previous item in list. + static NetConnection *mConnectionList; ///< Head of list. +public: + static NetConnection *getConnectionList() { return mConnectionList; } + NetConnection *getNext() { return mNextConnection; } + /// @} + //---------------------------------------------------------------- + + enum NetConnectionFlags + { + ConnectionToServer = BIT(0), + ConnectionToClient = BIT(1), + LocalClientConnection = BIT(2), + NetworkConnection = BIT(3), + }; + +private: + BitSet32 mTypeFlags; + + U32 mNetClassGroup; ///< The NetClassGroup of this connection. + + /// @name Statistics + /// @{ + + /// Last time a packet was sent in milliseconds. + /// @see Platform::getVirtualMilliseconds() + U32 mLastUpdateTime; + + F32 mRoundTripTime; + F32 mPacketLoss; + U32 mSimulatedPing; + F32 mSimulatedPacketLoss; + + /// @} + + /// @name State + /// @{ + + U32 mProtocolVersion; + U32 mSendDelayCredit; + U32 mConnectSequence; + U32 mAddressDigest[4]; + + bool mEstablished; + bool mMissionPathsSent; + + struct NetRate + { + U32 updateDelay; + S32 packetSize; + bool changed; + }; + + NetRate mCurRate; + NetRate mMaxRate; + + /// If we're doing a "short circuited" connection, this stores + /// a pointer to the other side. + SimObjectPtr mRemoteConnection; + + NetAddress mNetAddress; + + /// @} + + + /// @name Timeout Management + /// @{ + + U32 mPingSendCount; + U32 mPingRetryCount; + U32 mLastPingSendTime; + /// @} + + /// @name Connection Table + /// + /// We store our connections on a hash table so we can + /// quickly find them. + /// @{ + + NetConnection *mNextTableHash; + static NetConnection *mHashTable[HashTableSize]; + + /// @} + +protected: + static SimObjectPtr mServerConnection; + static SimObjectPtr mLocalClientConnection; + + static bool mFilesWereDownloaded; + + U32 mConnectSendCount; + U32 mConnectLastSendTime; + + SimObjectPtr getRemoteConnection() { return mRemoteConnection; } + +public: + static NetConnection *getConnectionToServer() { return mServerConnection; } + + static NetConnection *getLocalClientConnection() { return mLocalClientConnection; } + static void setLocalClientConnection(NetConnection *conn) { mLocalClientConnection = conn; } + + U32 getNetClassGroup() { return mNetClassGroup; } + static bool filesWereDownloaded() { return mFilesWereDownloaded; } + static String &getErrorBuffer() { return mErrorBuffer; } + +#ifdef TORQUE_DEBUG_NET + bool mLogging; + void setLogging(bool logging) { mLogging = logging; } +#endif + + void setSimulatedNetParams(F32 packetLoss, U32 ping) + { mSimulatedPacketLoss = packetLoss; mSimulatedPing = ping; } + + bool isConnectionToServer() { return mTypeFlags.test(ConnectionToServer); } + bool isLocalConnection() { return !mRemoteConnection.isNull() ; } + bool isNetworkConnection() { return mTypeFlags.test(NetworkConnection); } + + void setIsConnectionToServer() { mTypeFlags.set(ConnectionToServer); } + void setIsLocalClientConnection() { mTypeFlags.set(LocalClientConnection); } + void setNetworkConnection(bool net) { mTypeFlags.set(BitSet32(NetworkConnection), net); } + + virtual void setEstablished(); + + /// Call this if the "connection" is local to this app. This short-circuits the protocol layer. + void setRemoteConnectionObject(NetConnection *connection) { mRemoteConnection = connection; }; + + void setSequence(U32 connectSequence); + + void setAddressDigest(U32 digest[4]); + void getAddressDigest(U32 digest[4]); + + U32 getSequence(); + + void setProtocolVersion(U32 protocolVersion) { mProtocolVersion = protocolVersion; } + U32 getProtocolVersion() { return mProtocolVersion; } + F32 getRoundTripTime() { return mRoundTripTime; } + F32 getPacketLoss() { return( mPacketLoss ); } + + static String mErrorBuffer; + static void setLastError(const char *fmt,...); + + void checkMaxRate(); + void handlePacket(BitStream *stream); + void processRawPacket(BitStream *stream); + void handleNotify(bool recvd); + void handleConnectionEstablished(); + void keepAlive(); + + const NetAddress *getNetAddress(); + void setNetAddress(const NetAddress *address); + Net::Error sendPacket(BitStream *stream); + +private: + void netAddressTableInsert(); + void netAddressTableRemove(); + +public: + /// Find a NetConnection, if any, with the specified address. + static NetConnection *lookup(const NetAddress *remoteAddress); + + bool checkTimeout(U32 time); ///< returns true if the connection timed out + + void checkPacketSend(bool force); + + bool missionPathsSent() const { return mMissionPathsSent; } + void setMissionPathsSent(const bool s) { mMissionPathsSent = s; } + + static void consoleInit(); + + void onRemove(); + + NetConnection(); + ~NetConnection(); + +public: + enum NetConnectionState + { + NotConnected, + AwaitingChallengeResponse, ///< We've sent a challenge request, awaiting the response. + AwaitingConnectRequest, ///< We've received a challenge request and sent a challenge response. + AwaitingConnectResponse, ///< We've received a challenge response and sent a connect request. + Connected, ///< We've accepted a connect request, or we've received a connect response accept. + }; + + U32 mConnectionSendCount; ///< number of connection messages we've sent. + U32 mConnectionState; ///< State of the connection, from NetConnectionState. + + void setConnectionState(U32 state) { mConnectionState = state; } + U32 getConnectionState() { return mConnectionState; } + + + void setGhostFrom(bool ghostFrom); ///< Sets whether ghosts transmit from this side of the connection. + void setGhostTo(bool ghostTo); ///< Sets whether ghosts are allowed from the other side of the connection. + void setSendingEvents(bool sending); ///< Sets whether this side actually sends the events that are posted to it. + void setTranslatesStrings(bool xl); ///< Sets whether this connection is capable of translating strings. + void setNetClassGroup(U32 group); ///< Sets the group of NetClasses this connection traffics in. + bool isEstablished() { return mEstablished; } ///< Is the connection established? + + DECLARE_CONOBJECT(NetConnection); + DECLARE_INSCOPE( NetAPI ); + + /// Structure to track packets and what we sent over them. + /// + /// We need to know what is sent in each packet, so that if a packet is + /// dropped, we know what to resend. This is the structure we use to track + /// this data. + struct PacketNotify + { + bool rateChanged; ///< Did the rate change on this packet? + bool maxRateChanged; ///< Did the max rate change on this packet? + U32 sendTime; ///< Timestampe, when we sent this packet. + + NetEventNote *eventList; ///< Linked list of events sent over this packet. + GhostRef *ghostList; ///< Linked list of ghost updates we sent in this packet. + SubPacketRef *subList; ///< Defined by subclass - used as desired. + + PacketNotify *nextPacket; ///< Next packet sent. + PacketNotify(); + }; + virtual PacketNotify *allocNotify(); + PacketNotify *mNotifyQueueHead; ///< Head of packet notify list. + PacketNotify *mNotifyQueueTail; ///< Tail of packet notify list. + +protected: + virtual void readPacket(BitStream *bstream); + virtual void writePacket(BitStream *bstream, PacketNotify *note); + virtual void packetReceived(PacketNotify *note); + virtual void packetDropped(PacketNotify *note); + virtual void connectionError(const char *errorString); + +//---------------------------------------------------------------- +/// @name Event Manager +/// @{ + +private: + NetEventNote *mSendEventQueueHead; + NetEventNote *mSendEventQueueTail; + NetEventNote *mUnorderedSendEventQueueHead; + NetEventNote *mUnorderedSendEventQueueTail; + NetEventNote *mWaitSeqEvents; + NetEventNote *mNotifyEventList; + + static FreeListChunker mEventNoteChunker; + + bool mSendingEvents; + + S32 mNextSendEventSeq; + S32 mNextRecvEventSeq; + S32 mLastAckedEventSeq; + + enum NetEventConstants { + InvalidSendEventSeq = -1, + FirstValidSendEventSeq = 0 + }; + + void eventOnRemove(); + + void eventPacketDropped(PacketNotify *notify); + void eventPacketReceived(PacketNotify *notify); + + void eventWritePacket(BitStream *bstream, PacketNotify *notify); + void eventReadPacket(BitStream *bstream); + + void eventWriteStartBlock(ResizeBitStream *stream); + void eventReadStartBlock(BitStream *stream); +public: + /// Post an event to this connection. + bool postNetEvent(NetEvent *event); + +/// @} + +//---------------------------------------------------------------- +/// @name Networked string table +/// @{ + +private: + bool mTranslateStrings; + ConnectionStringTable *mStringTable; +public: + void mapString(U32 netId, NetStringHandle &string) + { mStringTable->mapString(netId, string); } + U32 checkString(NetStringHandle &string, bool *isOnOtherSide = NULL) + { if(mStringTable) return mStringTable->checkString(string, isOnOtherSide); else return 0; } + U32 getNetSendId(NetStringHandle &string) + { if(mStringTable) return mStringTable->getNetSendId(string); else return 0;} + void confirmStringReceived(NetStringHandle &string, U32 index) + { if(!isRemoved()) mStringTable->confirmStringReceived(string, index); } + + NetStringHandle translateRemoteStringId(U32 id) { return mStringTable->lookupString(id); } + void validateSendString(const char *str); + + void packString(BitStream *stream, const char *str); + void unpackString(BitStream *stream, char readBuffer[1024]); + + void packNetStringHandleU(BitStream *stream, NetStringHandle &h); + NetStringHandle unpackNetStringHandleU(BitStream *stream); +/// @} + +//---------------------------------------------------------------- +/// @name Ghost manager +/// @{ + +protected: + enum GhostStates + { + GhostAlwaysDone, + ReadyForNormalGhosts, + EndGhosting, + GhostAlwaysStarting, + SendNextDownloadRequest, + FileDownloadSizeMessage, + NumConnectionMessages, + }; + GhostInfo **mGhostArray; ///< Linked list of ghostInfos ghosted by this side of the connection + + U32 mGhostZeroUpdateIndex; ///< Index in mGhostArray of first ghost with 0 update mask. + U32 mGhostFreeIndex; ///< Index in mGhostArray of first free ghost. + + U32 mGhostsActive; ///- Track actve ghosts on client side + + bool mGhosting; ///< Am I currently ghosting objects? + bool mScoping; ///< am I currently scoping objects? + U32 mGhostingSequence; ///< Sequence number describing this ghosting session. + + NetObject **mLocalGhosts; ///< Local ghost for remote object. + /// + /// mLocalGhosts pointer is NULL if mGhostTo is false + + GhostInfo *mGhostRefs; ///< Allocated array of ghostInfos. Null if ghostFrom is false. + GhostInfo **mGhostLookupTable; ///< Table indexed by object id to GhostInfo. Null if ghostFrom is false. + + /// The object around which we are scoping this connection. + /// + /// This is usually the player object, or a related object, like a vehicle + /// that the player is driving. + SimObjectPtr mScopeObject; + + void clearGhostInfo(); + bool validateGhostArray(); + + void ghostPacketDropped(PacketNotify *notify); + void ghostPacketReceived(PacketNotify *notify); + + void ghostWritePacket(BitStream *bstream, PacketNotify *notify); + void ghostReadPacket(BitStream *bstream); + void freeGhostInfo(GhostInfo *); + + void ghostWriteStartBlock(ResizeBitStream *stream); + void ghostReadStartBlock(BitStream *stream); + + virtual void ghostWriteExtra(NetObject *,BitStream *) {} + virtual void ghostReadExtra(NetObject *,BitStream *, bool newGhost) {} + virtual void ghostPreRead(NetObject *, bool newGhost) {} + + /// Called when 'EndGhosting' message is received from server. + virtual void onEndGhosting() {} + +public: + /// Some configuration values. + enum GhostConstants + { + GhostIdBitSize = 12, + MaxGhostCount = 1 << GhostIdBitSize, //4096, + GhostLookupTableSize = 1 << GhostIdBitSize, //4096 + GhostIndexBitSize = 4 // number of bits GhostIdBitSize-3 fits into + }; + + U32 getGhostsActive() { return mGhostsActive;}; + + /// Are we ghosting to someone? + bool isGhostingTo() { return mLocalGhosts != NULL; }; + + /// Are we ghosting from someone? + bool isGhostingFrom() { return mGhostArray != NULL; }; + + /// Called by onRemove, to shut down the ghost subsystem. + void ghostOnRemove(); + + /// Called when we're done with normal scoping. + /// + /// This gives subclasses a chance to shove things into scope, such as + /// the results of a sensor network calculation, that would otherwise + /// be awkward to add. + virtual void doneScopingScene() { /* null */ } + + /// Set the object around which we are currently scoping network traffic. + void setScopeObject(NetObject *object); + + /// Get the object around which we are currently scoping network traffic. + NetObject *getScopeObject(); + + /// Add an object to scope. + void objectInScope(NetObject *object); + + /// Add an object to scope, marking that it should always be scoped to this connection. + void objectLocalScopeAlways(NetObject *object); + + /// Mark an object that is being ghosted as not always needing to be scoped. + /// + /// This undoes objectLocalScopeAlways(), but doesn't immediately flush it from scope. + /// + /// Instead, the standard scoping mechanisms will clear it from scope when it is appropos + /// to do so. + void objectLocalClearAlways(NetObject *object); + + /// Get a NetObject* from a ghost ID (on client side). + NetObject *resolveGhost(S32 id); + + /// Get a NetObject* from a ghost index (on the server side). + NetObject *resolveObjectFromGhostIndex(S32 id); + + /// Get the ghost index corresponding to a given NetObject. This is only + /// meaningful on the server side. + S32 getGhostIndex(NetObject *object); + + /// Move a GhostInfo into the nonzero portion of the list (so that we know to update it). + void ghostPushNonZero(GhostInfo *gi); + + /// Move a GhostInfo into the zero portion of the list (so that we know not to update it). + void ghostPushToZero(GhostInfo *gi); + + /// Move a GhostInfo from the zero portion of the list to the free portion. + void ghostPushZeroToFree(GhostInfo *gi); + + /// Move a GhostInfo from the free portion of the list to the zero portion. + inline void ghostPushFreeToZero(GhostInfo *info); + + /// Stop all ghosting activity and inform the other side about this. + /// + /// Turns off ghosting. + void resetGhosting(); + + /// Activate ghosting, once it's enabled. + void activateGhosting(); + + /// Are we ghosting? + bool isGhosting() { return mGhosting; } + + /// Begin to stop ghosting an object. + void detachObject(GhostInfo *info); + + /// Mark an object to be always ghosted. Index is the ghost index of the object. + void setGhostAlwaysObject(NetObject *object, U32 index); + + + /// Send ghost connection handshake message. + /// + /// As part of the ghost connection process, extensive hand-shaking must be performed. + /// + /// This is done by passing ConnectionMessageEvents; this is a helper function + /// to more effectively perform this task. Messages are dealt with by + /// handleConnectionMessage(). + /// + /// @param message One of GhostStates + /// @param sequence A sequence number, if any. + /// @param ghostCount A count of ghosts relating to this message. + void sendConnectionMessage(U32 message, U32 sequence = 0, U32 ghostCount = 0); + + /// Handle message from sendConnectionMessage(). + /// + /// This is called to handle messages sent via sendConnectionMessage. + /// + /// @param message One of GhostStates + /// @param sequence A sequence number, if any. + /// @param ghostCount A count of ghosts relating to this message. + virtual void handleConnectionMessage(U32 message, U32 sequence, U32 ghostCount); + + /// Sends a signal to any object that needs to wait till everything has been ghosted + /// before performing an operation. + static Signal smGhostAlwaysDone; + + /// @} +public: +//---------------------------------------------------------------- +/// @name File transfer +/// @{ + +protected: + /// List of files missing for this connection. + /// + /// The currently downloading file is always first in the list (ie, [0]). + Vector mMissingFileList; + + /// Stream for currently uploading file (if any). + Stream *mCurrentDownloadingFile; + + /// Storage for currently downloading file. + void *mCurrentFileBuffer; + + /// Size of currently downloading file in bytes. + U32 mCurrentFileBufferSize; + + /// Our position in the currently downloading file in bytes. + U32 mCurrentFileBufferOffset; + + /// Number of files we have downloaded. + U32 mNumDownloadedFiles; + + /// Error storage for file transfers. + String mLastFileErrorBuffer; + + /// Structure to track ghost-always objects and their ghost indices. + struct GhostSave { + NetObject *ghost; + U32 index; + }; + + /// List of objects to ghost-always. + Vector mGhostAlwaysSaveList; + +public: + /// Start sending the specified file over the link. + bool startSendingFile(const char *fileName); + + /// Called when we receive a FileChunkEvent. + void chunkReceived(U8 *chunkData, U32 chunkLen); + + /// Get the next file... + void sendNextFileDownloadRequest(); + + /// Post the next FileChunkEvent. + void sendFileChunk(); + + /// Called when we finish downloading file data. + virtual void fileDownloadSegmentComplete(); + + /// This is part of the file transfer logic; basically, we call this + /// every time we finish downloading new files. It attempts to load + /// the GhostAlways objects; if they fail, it marks an error and we + /// have chance to retry. + void loadNextGhostAlwaysObject(bool hadNewFiles); +/// @} + +//---------------------------------------------------------------- +/// @name Demo Recording +/// @{ + +private: + Stream *mDemoWriteStream; + Stream *mDemoReadStream; + U32 mDemoNextBlockType; + U32 mDemoNextBlockSize; + + U32 mDemoWriteStartTime; + U32 mDemoReadStartTime; + U32 mDemoLastWriteTime; + + U32 mDemoRealStartTime; + +public: + enum DemoBlockTypes { + BlockTypePacket, + BlockTypeSendPacket, + NetConnectionBlockTypeCount + }; + + enum DemoConstants { + MaxNumBlockTypes = 16, + MaxBlockSize = 0x1000, + }; + + bool isRecording() + { return mDemoWriteStream != NULL; } + bool isPlayingBack() + { return mDemoReadStream != NULL; } + + U32 getNextBlockType() { return mDemoNextBlockType; } + void recordBlock(U32 type, U32 size, void *data); + virtual void handleRecordedBlock(U32 type, U32 size, void *data); + bool processNextBlock(); + + bool startDemoRecord(const char *fileName); + bool replayDemoRecord(const char *fileName); + void startDemoRead(); + void stopRecording(); + void stopDemoPlayback(); + + virtual void writeDemoStartBlock(ResizeBitStream *stream); + virtual bool readDemoStartBlock(BitStream *stream); + virtual void demoPlaybackComplete(); +/// @} +}; + + +//---------------------------------------------------------------------------- +/// Information about a ghosted object. +/// +/// @note If the size of this structure changes, the +/// NetConnection::getGhostIndex function MUST be changed +/// to reflect the new size. +struct GhostInfo +{ +public: // required for MSVC + NetObject *obj; ///< The object being ghosted. + U32 updateMask; ///< Flags indicating what state data needs to be transferred. + + U32 updateSkipCount; ///< How many updates have we skipped this guy? + U32 flags; ///< Flags from GhostInfo::Flags + F32 priority; ///< A float value indicating the priority of this object for + /// updates. + + /// @name References + /// + /// The GhostInfo structure is used in several linked lists; these members are + /// the implementation for this. + /// @{ + + NetConnection::GhostRef *updateChain; ///< List of references in NetConnections to us. + + GhostInfo *nextObjectRef; ///< Next ghosted object. + GhostInfo *prevObjectRef; ///< Previous ghosted object. + NetConnection *connection; ///< Connection that we're ghosting over. + GhostInfo *nextLookupInfo; ///< GhostInfo references are stored in a hash; this is the bucket + /// implementation. + + /// @} + + U32 index; + U32 arrayIndex; + + /// Flags relating to the state of the object. + enum Flags + { + Valid = BIT(0), + InScope = BIT(1), + ScopeAlways = BIT(2), + NotYetGhosted = BIT(3), + Ghosting = BIT(4), + KillGhost = BIT(5), + KillingGhost = BIT(6), + ScopedEvent = BIT(7), + ScopeLocalAlways = BIT(8), + }; +}; + +inline void NetConnection::ghostPushNonZero(GhostInfo *info) +{ + AssertFatal(info->arrayIndex >= mGhostZeroUpdateIndex && info->arrayIndex < mGhostFreeIndex, "Out of range arrayIndex."); + AssertFatal(mGhostArray[info->arrayIndex] == info, "Invalid array object."); + if(info->arrayIndex != mGhostZeroUpdateIndex) + { + mGhostArray[mGhostZeroUpdateIndex]->arrayIndex = info->arrayIndex; + mGhostArray[info->arrayIndex] = mGhostArray[mGhostZeroUpdateIndex]; + mGhostArray[mGhostZeroUpdateIndex] = info; + info->arrayIndex = mGhostZeroUpdateIndex; + } + mGhostZeroUpdateIndex++; + //AssertFatal(validateGhostArray(), "Invalid ghost array!"); +} + +inline void NetConnection::ghostPushToZero(GhostInfo *info) +{ + AssertFatal(info->arrayIndex < mGhostZeroUpdateIndex, "Out of range arrayIndex."); + AssertFatal(mGhostArray[info->arrayIndex] == info, "Invalid array object."); + mGhostZeroUpdateIndex--; + if(info->arrayIndex != mGhostZeroUpdateIndex) + { + mGhostArray[mGhostZeroUpdateIndex]->arrayIndex = info->arrayIndex; + mGhostArray[info->arrayIndex] = mGhostArray[mGhostZeroUpdateIndex]; + mGhostArray[mGhostZeroUpdateIndex] = info; + info->arrayIndex = mGhostZeroUpdateIndex; + } + //AssertFatal(validateGhostArray(), "Invalid ghost array!"); +} + +inline void NetConnection::ghostPushZeroToFree(GhostInfo *info) +{ + AssertFatal(info->arrayIndex >= mGhostZeroUpdateIndex && info->arrayIndex < mGhostFreeIndex, "Out of range arrayIndex."); + AssertFatal(mGhostArray[info->arrayIndex] == info, "Invalid array object."); + mGhostFreeIndex--; + if(info->arrayIndex != mGhostFreeIndex) + { + mGhostArray[mGhostFreeIndex]->arrayIndex = info->arrayIndex; + mGhostArray[info->arrayIndex] = mGhostArray[mGhostFreeIndex]; + mGhostArray[mGhostFreeIndex] = info; + info->arrayIndex = mGhostFreeIndex; + } + //AssertFatal(validateGhostArray(), "Invalid ghost array!"); +} + +inline void NetConnection::ghostPushFreeToZero(GhostInfo *info) +{ + AssertFatal(info->arrayIndex >= mGhostFreeIndex, "Out of range arrayIndex."); + AssertFatal(mGhostArray[info->arrayIndex] == info, "Invalid array object."); + if(info->arrayIndex != mGhostFreeIndex) + { + mGhostArray[mGhostFreeIndex]->arrayIndex = info->arrayIndex; + mGhostArray[info->arrayIndex] = mGhostArray[mGhostFreeIndex]; + mGhostArray[mGhostFreeIndex] = info; + info->arrayIndex = mGhostFreeIndex; + } + mGhostFreeIndex++; + //AssertFatal(validateGhostArray(), "Invalid ghost array!"); +} + +#endif + + diff --git a/Engine/source/sim/netDownload.cpp b/Engine/source/sim/netDownload.cpp new file mode 100644 index 000000000..a53d633ef --- /dev/null +++ b/Engine/source/sim/netDownload.cpp @@ -0,0 +1,273 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/dnet.h" +#include "console/simBase.h" +#include "sim/netConnection.h" +#include "core/stream/bitStream.h" +#include "core/stream/fileStream.h" +#include "sim/netObject.h" + +class FileDownloadRequestEvent : public NetEvent +{ +public: + typedef NetEvent Parent; + enum + { + MaxFileNames = 31, + }; + + U32 nameCount; + char mFileNames[MaxFileNames][256]; + + FileDownloadRequestEvent(Vector *nameList = NULL) + { + nameCount = 0; + if(nameList) + { + nameCount = nameList->size(); + + if(nameCount > MaxFileNames) + nameCount = MaxFileNames; + + for(U32 i = 0; i < nameCount; i++) + { + dStrcpy(mFileNames[i], (*nameList)[i]); + //Con::printf("Sending request for file %s", mFileNames[i]); + } + } + } + + virtual void pack(NetConnection *, BitStream *bstream) + { + bstream->writeRangedU32(nameCount, 0, MaxFileNames); + for(U32 i = 0; i < nameCount; i++) + bstream->writeString(mFileNames[i]); + } + + virtual void write(NetConnection *, BitStream *bstream) + { + bstream->writeRangedU32(nameCount, 0, MaxFileNames); + for(U32 i = 0; i < nameCount; i++) + bstream->writeString(mFileNames[i]); + } + + virtual void unpack(NetConnection *, BitStream *bstream) + { + nameCount = bstream->readRangedU32(0, MaxFileNames); + for(U32 i = 0; i < nameCount; i++) + bstream->readString(mFileNames[i]); + } + + virtual void process(NetConnection *connection) + { + U32 i; + for(i = 0; i < nameCount; i++) + if(connection->startSendingFile(mFileNames[i])) + break; + if(i == nameCount) + connection->startSendingFile(NULL); // none of the files were sent + } + + DECLARE_CONOBJECT(FileDownloadRequestEvent); + +}; + +IMPLEMENT_CO_NETEVENT_V1(FileDownloadRequestEvent); + +ConsoleDocClass( FileDownloadRequestEvent, + "@brief Used by NetConnection for transmitting requests to obtain files from server during loading.\n\n" + "Not intended for game development, for editors or internal use only.\n\n " + "@internal"); + +class FileChunkEvent : public NetEvent +{ +public: + typedef NetEvent Parent; + enum + { + ChunkSize = 63, + }; + + U8 chunkData[ChunkSize]; + U32 chunkLen; + + FileChunkEvent(U8 *data = NULL, U32 len = 0) + { + if(data) + dMemcpy(chunkData, data, len); + chunkLen = len; + } + + virtual void pack(NetConnection *, BitStream *bstream) + { + bstream->writeRangedU32(chunkLen, 0, ChunkSize); + bstream->write(chunkLen, chunkData); + } + + virtual void write(NetConnection *, BitStream *bstream) + { + bstream->writeRangedU32(chunkLen, 0, ChunkSize); + bstream->write(chunkLen, chunkData); + } + + virtual void unpack(NetConnection *, BitStream *bstream) + { + chunkLen = bstream->readRangedU32(0, ChunkSize); + bstream->read(chunkLen, chunkData); + } + + virtual void process(NetConnection *connection) + { + connection->chunkReceived(chunkData, chunkLen); + } + + virtual void notifyDelivered(NetConnection *nc, bool madeIt) + { + if(!nc->isRemoved()) + nc->sendFileChunk(); + } + + DECLARE_CONOBJECT(FileChunkEvent); +}; + +IMPLEMENT_CO_NETEVENT_V1(FileChunkEvent); + +ConsoleDocClass( FileChunkEvent, + "@brief Used by NetConnection for sending/receiving chunks of data.\n\n" + "Not intended for game development, for editors or internal use only.\n\n " + "@internal"); + +void NetConnection::sendFileChunk() +{ + U8 buffer[FileChunkEvent::ChunkSize]; + U32 len = FileChunkEvent::ChunkSize; + if(len + mCurrentFileBufferOffset > mCurrentFileBufferSize) + len = mCurrentFileBufferSize - mCurrentFileBufferOffset; + + if(!len) + { + delete mCurrentDownloadingFile; + mCurrentDownloadingFile = NULL; + return; + } + + mCurrentFileBufferOffset += len; + mCurrentDownloadingFile->read(len, buffer); + postNetEvent(new FileChunkEvent(buffer, len)); +} + +bool NetConnection::startSendingFile(const char *fileName) +{ + if(!fileName || Con::getBoolVariable("$NetConnection::neverUploadFiles")) + { + sendConnectionMessage(SendNextDownloadRequest); + return false; + } + + mCurrentDownloadingFile = FileStream::createAndOpen( fileName, Torque::FS::File::Read ); + if(!mCurrentDownloadingFile) + { + // the server didn't have the file, so send a 0 byte chunk: + Con::printf("No such file '%s'.", fileName); + postNetEvent(new FileChunkEvent(NULL, 0)); + return false; + } + + Con::printf("Sending file '%s'.", fileName); + mCurrentFileBufferSize = mCurrentDownloadingFile->getStreamSize(); + mCurrentFileBufferOffset = 0; + + // always have 32 file chunks (64 bytes each) in transit + sendConnectionMessage(FileDownloadSizeMessage, mCurrentFileBufferSize); + for(U32 i = 0; i < 32; i++) + sendFileChunk(); + return true; +} + +void NetConnection::sendNextFileDownloadRequest() +{ + // see if we've already downloaded this file... + while(mMissingFileList.size() && (Torque::FS::IsFile(mMissingFileList[0]) || Con::getBoolVariable("$NetConnection::neverDownloadFiles"))) + { + dFree(mMissingFileList[0]); + mMissingFileList.pop_front(); + } + + if(mMissingFileList.size()) + { + postNetEvent(new FileDownloadRequestEvent(&mMissingFileList)); + } + else + { + fileDownloadSegmentComplete(); + } +} + + +void NetConnection::chunkReceived(U8 *chunkData, U32 chunkLen) +{ + if(chunkLen == 0) + { + // the server didn't have the file... apparently it's one we don't need... + dFree(mCurrentFileBuffer); + mCurrentFileBuffer = NULL; + dFree(mMissingFileList[0]); + mMissingFileList.pop_front(); + return; + } + if(chunkLen + mCurrentFileBufferOffset > mCurrentFileBufferSize) + { + setLastError("Invalid file chunk from server."); + return; + } + dMemcpy(((U8 *) mCurrentFileBuffer) + mCurrentFileBufferOffset, chunkData, chunkLen); + mCurrentFileBufferOffset += chunkLen; + if(mCurrentFileBufferOffset == mCurrentFileBufferSize) + { + // this file's done... + // save it to disk: + FileStream *stream; + + Con::printf("Saving file %s.", mMissingFileList[0]); + if((stream = FileStream::createAndOpen( mMissingFileList[0], Torque::FS::File::Write )) == NULL) + { + setLastError("Couldn't open file downloaded by server."); + return; + } + + dFree(mMissingFileList[0]); + mMissingFileList.pop_front(); + stream->write(mCurrentFileBufferSize, mCurrentFileBuffer); + delete stream; + mNumDownloadedFiles++; + dFree(mCurrentFileBuffer); + mCurrentFileBuffer = NULL; + sendNextFileDownloadRequest(); + } + else + { + Con::executef("onFileChunkReceived", mMissingFileList[0], Con::getIntArg(mCurrentFileBufferOffset), Con::getIntArg(mCurrentFileBufferSize)); + } +} + diff --git a/Engine/source/sim/netEvent.cpp b/Engine/source/sim/netEvent.cpp new file mode 100644 index 000000000..e73a7ed4a --- /dev/null +++ b/Engine/source/sim/netEvent.cpp @@ -0,0 +1,453 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/dnet.h" +#include "console/simBase.h" +#include "sim/netConnection.h" +#include "core/stream/bitStream.h" + +#define DebugChecksum 0xF00DBAAD + +FreeListChunker NetConnection::mEventNoteChunker; + +NetEvent::~NetEvent() +{ +} + +void NetEvent::notifyDelivered(NetConnection *, bool) +{ +} + +void NetEvent::notifySent(NetConnection *) +{ +} + +#ifdef TORQUE_DEBUG_NET +const char *NetEvent::getDebugName() +{ + return getClassName(); +} +#endif + +void NetConnection::eventOnRemove() +{ + while(mNotifyEventList) + { + NetEventNote *temp = mNotifyEventList; + mNotifyEventList = temp->mNextEvent; + + temp->mEvent->notifyDelivered(this, true); + temp->mEvent->decRef(); + mEventNoteChunker.free(temp); + } + + while(mUnorderedSendEventQueueHead) + { + NetEventNote *temp = mUnorderedSendEventQueueHead; + mUnorderedSendEventQueueHead = temp->mNextEvent; + + temp->mEvent->notifyDelivered(this, true); + temp->mEvent->decRef(); + mEventNoteChunker.free(temp); + } + + while(mSendEventQueueHead) + { + NetEventNote *temp = mSendEventQueueHead; + mSendEventQueueHead = temp->mNextEvent; + + temp->mEvent->notifyDelivered(this, true); + temp->mEvent->decRef(); + mEventNoteChunker.free(temp); + } +} + +void NetConnection::eventPacketDropped(PacketNotify *notify) +{ + NetEventNote *walk = notify->eventList; + NetEventNote **insertList = &mSendEventQueueHead; + NetEventNote *temp; + + while(walk) + { + switch(walk->mEvent->mGuaranteeType) + { + // It was a guaranteed ordered packet, reinsert it back into + // mSendEventQueueHead in the right place (based on seq numbers) + case NetEvent::GuaranteedOrdered: + + //Con::printf("EVT %d: DROP - %d", getId(), walk->mSeqCount); + + while(*insertList && (*insertList)->mSeqCount < walk->mSeqCount) + insertList = &((*insertList)->mNextEvent); + + temp = walk->mNextEvent; + walk->mNextEvent = *insertList; + if(!walk->mNextEvent) + mSendEventQueueTail = walk; + *insertList = walk; + insertList = &(walk->mNextEvent); + walk = temp; + break; + + // It was a guaranteed packet, put it at the top of + // mUnorderedSendEventQueueHead. + case NetEvent::Guaranteed: + temp = walk->mNextEvent; + walk->mNextEvent = mUnorderedSendEventQueueHead; + mUnorderedSendEventQueueHead = walk; + if(!walk->mNextEvent) + mUnorderedSendEventQueueTail = walk; + walk = temp; + break; + + // Or else it was an unguaranteed packet, notify that + // it was _not_ delivered and blast it. + case NetEvent::Unguaranteed: + walk->mEvent->notifyDelivered(this, false); + walk->mEvent->decRef(); + temp = walk->mNextEvent; + mEventNoteChunker.free(walk); + walk = temp; + } + } +} + +void NetConnection::eventPacketReceived(PacketNotify *notify) +{ + NetEventNote *walk = notify->eventList; + NetEventNote **noteList = &mNotifyEventList; + + while(walk) + { + NetEventNote *next = walk->mNextEvent; + if(walk->mEvent->mGuaranteeType != NetEvent::GuaranteedOrdered) + { + walk->mEvent->notifyDelivered(this, true); + walk->mEvent->decRef(); + mEventNoteChunker.free(walk); + walk = next; + } + else + { + while(*noteList && (*noteList)->mSeqCount < walk->mSeqCount) + noteList = &((*noteList)->mNextEvent); + + walk->mNextEvent = *noteList; + *noteList = walk; + noteList = &walk->mNextEvent; + walk = next; + } + } + while(mNotifyEventList && mNotifyEventList->mSeqCount == mLastAckedEventSeq + 1) + { + mLastAckedEventSeq++; + NetEventNote *next = mNotifyEventList->mNextEvent; + //Con::printf("EVT %d: ACK - %d", getId(), mNotifyEventList->mSeqCount); + mNotifyEventList->mEvent->notifyDelivered(this, true); + mNotifyEventList->mEvent->decRef(); + mEventNoteChunker.free(mNotifyEventList); + mNotifyEventList = next; + } +} + +void NetConnection::eventWritePacket(BitStream *bstream, PacketNotify *notify) +{ +#ifdef TORQUE_DEBUG_NET + bstream->writeInt(DebugChecksum, 32); +#endif + + NetEventNote *packQueueHead = NULL, *packQueueTail = NULL; + + while(mUnorderedSendEventQueueHead) + { + if(bstream->isFull()) + break; + // dequeue the first event + NetEventNote *ev = mUnorderedSendEventQueueHead; + mUnorderedSendEventQueueHead = ev->mNextEvent; +#ifdef TORQUE_DEBUG_NET + U32 start = bstream->getCurPos(); +#endif + + bstream->writeFlag(true); + S32 classId = ev->mEvent->getClassId(getNetClassGroup()); + AssertFatal(classId>=0, "NetConnection::eventWritePacket - event not in group!"); + bstream->writeClassId(classId, NetClassTypeEvent, getNetClassGroup()); + +#ifdef TORQUE_NET_STATS + U32 beginSize = bstream->getBitPosition(); +#endif + ev->mEvent->pack(this, bstream); +#ifdef TORQUE_NET_STATS + ev->mEvent->getClassRep()->updateNetStatPack(0, bstream->getBitPosition() - beginSize); +#endif + DEBUG_LOG(("PKLOG %d EVENT %d: %s", getId(), bstream->getBitPosition() - start, ev->mEvent->getDebugName()) ); + +#ifdef TORQUE_DEBUG_NET + bstream->writeInt(classId ^ DebugChecksum, 32); +#endif + // add this event onto the packet queue + ev->mNextEvent = NULL; + if(!packQueueHead) + packQueueHead = ev; + else + packQueueTail->mNextEvent = ev; + packQueueTail = ev; + } + + bstream->writeFlag(false); + S32 prevSeq = -2; + + while(mSendEventQueueHead) + { + if(bstream->isFull()) + break; + + // if the event window is full, stop processing + if(mSendEventQueueHead->mSeqCount > mLastAckedEventSeq + 126) + break; + + // dequeue the first event + NetEventNote *ev = mSendEventQueueHead; + mSendEventQueueHead = ev->mNextEvent; + + //Con::printf("EVT %d: SEND - %d", getId(), ev->mSeqCount); + + bstream->writeFlag(true); + + ev->mNextEvent = NULL; + if(!packQueueHead) + packQueueHead = ev; + else + packQueueTail->mNextEvent = ev; + packQueueTail = ev; + if(!bstream->writeFlag(ev->mSeqCount == prevSeq + 1)) + bstream->writeInt(ev->mSeqCount, 7); + + prevSeq = ev->mSeqCount; + +#ifdef TORQUE_DEBUG_NET + U32 start = bstream->getCurPos(); +#endif + + S32 classId = ev->mEvent->getClassId(getNetClassGroup()); + bstream->writeClassId(classId, NetClassTypeEvent, getNetClassGroup()); +#ifdef TORQUE_NET_STATS + U32 beginSize = bstream->getBitPosition(); +#endif + ev->mEvent->pack(this, bstream); +#ifdef TORQUE_NET_STATS + ev->mEvent->getClassRep()->updateNetStatPack(0, bstream->getBitPosition() - beginSize); +#endif + DEBUG_LOG(("PKLOG %d EVENT %d: %s", getId(), bstream->getBitPosition() - start, ev->mEvent->getDebugName()) ); +#ifdef TORQUE_DEBUG_NET + bstream->writeInt(classId ^ DebugChecksum, 32); +#endif + } + for(NetEventNote *ev = packQueueHead; ev; ev = ev->mNextEvent) + ev->mEvent->notifySent(this); + + notify->eventList = packQueueHead; + bstream->writeFlag(false); +} + +void NetConnection::eventReadPacket(BitStream *bstream) +{ +#ifdef TORQUE_DEBUG_NET + U32 sum = bstream->readInt(32); + AssertISV(sum == DebugChecksum, "Invalid checksum."); +#endif + + S32 prevSeq = -2; + NetEventNote **waitInsert = &mWaitSeqEvents; + bool unguaranteedPhase = true; + + while(true) + { + bool bit = bstream->readFlag(); + if(unguaranteedPhase && !bit) + { + unguaranteedPhase = false; + bit = bstream->readFlag(); + } + if(!unguaranteedPhase && !bit) + break; + + S32 seq = -1; + + if(!unguaranteedPhase) // get the sequence + { + if(bstream->readFlag()) + seq = (prevSeq + 1) & 0x7f; + else + seq = bstream->readInt(7); + prevSeq = seq; + } + S32 classId = bstream->readClassId(NetClassTypeEvent, getNetClassGroup()); + if(classId == -1) + { + setLastError("Invalid packet. (bad event class id)"); + return; + } + NetEvent *evt = (NetEvent *) ConsoleObject::create(getNetClassGroup(), NetClassTypeEvent, classId); + if(!evt) + { + setLastError("Invalid packet. (bad ghost class id)"); + return; + } + AbstractClassRep *rep = evt->getClassRep(); + if((rep->mNetEventDir == NetEventDirServerToClient && !isConnectionToServer()) + || (rep->mNetEventDir == NetEventDirClientToServer && isConnectionToServer()) ) + { + setLastError("Invalid Packet. (invalid direction)"); + return; + } + + + evt->mSourceId = getId(); +#ifdef TORQUE_NET_STATS + U32 beginSize = bstream->getBitPosition(); +#endif + evt->unpack(this, bstream); +#ifdef TORQUE_NET_STATS + evt->getClassRep()->updateNetStatUnpack(bstream->getBitPosition() - beginSize); +#endif + if(mErrorBuffer.isNotEmpty()) + return; +#ifdef TORQUE_DEBUG_NET + U32 checksum = bstream->readInt(32); + AssertISV( (checksum ^ DebugChecksum) == (U32)classId, + avar("unpack did not match pack for event of class %s.", + evt->getClassName()) ); +#endif + if(unguaranteedPhase) + { + evt->process(this); + evt->decRef(); + if(mErrorBuffer.isNotEmpty()) + return; + continue; + } + seq |= (mNextRecvEventSeq & ~0x7F); + if(seq < mNextRecvEventSeq) + seq += 128; + + NetEventNote *note = mEventNoteChunker.alloc(); + note->mEvent = evt; + note->mEvent->incRef(); + + note->mSeqCount = seq; + //Con::printf("EVT %d: RECV - %d", getId(), evt->mSeqCount); + while(*waitInsert && (*waitInsert)->mSeqCount < seq) + waitInsert = &((*waitInsert)->mNextEvent); + + note->mNextEvent = *waitInsert; + *waitInsert = note; + waitInsert = &(note->mNextEvent); + } + while(mWaitSeqEvents && mWaitSeqEvents->mSeqCount == mNextRecvEventSeq) + { + mNextRecvEventSeq++; + NetEventNote *temp = mWaitSeqEvents; + mWaitSeqEvents = temp->mNextEvent; + + //Con::printf("EVT %d: PROCESS - %d", getId(), temp->mSeqCount); + temp->mEvent->process(this); + temp->mEvent->decRef(); + mEventNoteChunker.free(temp); + if(mErrorBuffer.isNotEmpty()) + return; + } +} + +bool NetConnection::postNetEvent(NetEvent *theEvent) +{ + if(!mSendingEvents) + { + theEvent->decRef(); + return false; + } + NetEventNote *event = mEventNoteChunker.alloc(); + event->mEvent = theEvent; + theEvent->incRef(); + + event->mNextEvent = NULL; + if(theEvent->mGuaranteeType == NetEvent::GuaranteedOrdered) + { + event->mSeqCount = mNextSendEventSeq++; + if(!mSendEventQueueHead) + mSendEventQueueHead = event; + else + mSendEventQueueTail->mNextEvent = event; + mSendEventQueueTail = event; + } + else + { + event->mSeqCount = InvalidSendEventSeq; + if(!mUnorderedSendEventQueueHead) + mUnorderedSendEventQueueHead = event; + else + mUnorderedSendEventQueueTail->mNextEvent = event; + mUnorderedSendEventQueueTail = event; + } + return true; +} + + +void NetConnection::eventWriteStartBlock(ResizeBitStream *stream) +{ + stream->write(mNextRecvEventSeq); + for(NetEventNote *walk = mWaitSeqEvents; walk; walk = walk->mNextEvent) + { + stream->writeFlag(true); + S32 classId = walk->mEvent->getClassId(getNetClassGroup()); + stream->writeClassId(classId, NetClassTypeEvent, getNetClassGroup()); + walk->mEvent->write(this, stream); + stream->validate(); + } + stream->writeFlag(false); +} + +void NetConnection::eventReadStartBlock(BitStream *stream) +{ + stream->read(&mNextRecvEventSeq); + + NetEventNote *lastEvent = NULL; + while(stream->readFlag()) + { + S32 classTag = stream->readClassId(NetClassTypeEvent, getNetClassGroup()); + NetEvent *evt = (NetEvent *) ConsoleObject::create(getNetClassGroup(), NetClassTypeEvent, classTag); + evt->unpack(this, stream); + NetEventNote *add = mEventNoteChunker.alloc(); + add->mEvent = evt; + evt->incRef(); + add->mNextEvent = NULL; + + if(!lastEvent) + mWaitSeqEvents = add; + else + lastEvent->mNextEvent = add; + lastEvent = add; + } +} diff --git a/Engine/source/sim/netGhost.cpp b/Engine/source/sim/netGhost.cpp new file mode 100644 index 000000000..13acfc7e3 --- /dev/null +++ b/Engine/source/sim/netGhost.cpp @@ -0,0 +1,1294 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "core/dnet.h" +#include "console/simBase.h" +#include "sim/netConnection.h" +#include "core/stream/bitStream.h" +#include "sim/netObject.h" +//#include "core/resManager.h" +#include "console/console.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" + +#define DebugChecksum 0xF00DBAAD + +Signal NetConnection::smGhostAlwaysDone; + +extern U32 gGhostUpdates; + +class GhostAlwaysObjectEvent : public NetEvent +{ + SimObjectId objectId; + U32 ghostIndex; + NetObject *object; + bool validObject; +public: + typedef NetEvent Parent; + GhostAlwaysObjectEvent(NetObject *obj = NULL, U32 index = 0) + { + if(obj) + { + objectId = obj->getId(); + ghostIndex = index; + } + object = NULL; + } + ~GhostAlwaysObjectEvent() + { delete object; } + + void pack(NetConnection *ps, BitStream *bstream) + { + bstream->writeInt(ghostIndex, NetConnection::GhostIdBitSize); + + NetObject *obj = (NetObject *) Sim::findObject(objectId); + if(bstream->writeFlag(obj != NULL)) + { + S32 classId = obj->getClassId(ps->getNetClassGroup()); + bstream->writeClassId(classId, NetClassTypeObject, ps->getNetClassGroup()); +#ifdef TORQUE_NET_STATS + U32 beginSize = bstream->getBitPosition(); +#endif + U32 retMask = obj->packUpdate(ps, 0xFFFFFFFF, bstream); + if ( retMask != 0 ) obj->setMaskBits( retMask ); + +#ifdef TORQUE_NET_STATS + obj->getClassRep()->updateNetStatPack(0xFFFFFFFF, bstream->getBitPosition() - beginSize); +#endif + } + } + void write(NetConnection *ps, BitStream *bstream) + { + bstream->writeInt(ghostIndex, NetConnection::GhostIdBitSize); + if(bstream->writeFlag(validObject)) + { + S32 classId = object->getClassId(ps->getNetClassGroup()); + bstream->writeClassId(classId, NetClassTypeObject, ps->getNetClassGroup()); +#ifdef TORQUE_NET_STATS + U32 beginSize = bstream->getBitPosition(); +#endif + U32 retMask = object->packUpdate(ps, 0xFFFFFFFF, bstream); + if ( retMask != 0 ) object->setMaskBits( retMask ); +#ifdef TORQUE_NET_STATS + object->getClassRep()->updateNetStatPack(0xFFFFFFFF, bstream->getBitPosition() - beginSize); +#endif + } + } + void unpack(NetConnection *ps, BitStream *bstream) + { + ghostIndex = bstream->readInt(NetConnection::GhostIdBitSize); + + if(bstream->readFlag()) + { + S32 classId = bstream->readClassId(NetClassTypeObject, ps->getNetClassGroup()); + if(classId == -1) + { + ps->setLastError("Invalid packet. (invalid ghost class id)"); + return; + } + object = (NetObject *) ConsoleObject::create(ps->getNetClassGroup(), NetClassTypeObject, classId); + if(!object) + { + ps->setLastError("Invalid packet. (failed to created from class id)"); + return; + } + object->mNetFlags = NetObject::IsGhost; + object->mNetIndex = ghostIndex; + +#ifdef TORQUE_NET_STATS + U32 beginSize = bstream->getBitPosition(); +#endif + object->unpackUpdate(ps, bstream); +#ifdef TORQUE_NET_STATS + object->getClassRep()->updateNetStatUnpack(bstream->getBitPosition() - beginSize); +#endif + validObject = true; + } + else + { + object = new NetObject; + validObject = false; + } + } + void process(NetConnection *ps) + { + Con::executef("onGhostAlwaysObjectReceived"); + + ps->setGhostAlwaysObject(object, ghostIndex); + object = NULL; + } + DECLARE_CONOBJECT(GhostAlwaysObjectEvent); +}; + +IMPLEMENT_CO_NETEVENT_V1(GhostAlwaysObjectEvent); + +ConsoleDocClass( GhostAlwaysObjectEvent, + "@brief Legacy or soon to be locked down object.\n\n" + "Not intended for game development, for editors or internal use only.\n\n " + "@internal"); + +DefineEngineMethod( NetConnection, getGhostsActive, S32, (),, + "@brief Provides the number of active ghosts on the connection.\n\n" + "@returns The number of active ghosts.\n" + "@see @ref ghosting_scoping for a description of the ghosting system.\n\n") +{ + return object->getGhostsActive(); +} + +void NetConnection::setGhostTo(bool ghostTo) +{ + if(mLocalGhosts) // if ghosting to this is already enabled, silently return + return; + + if(ghostTo) + { + mLocalGhosts = new NetObject *[MaxGhostCount]; + for(S32 i = 0; i < MaxGhostCount; i++) + mLocalGhosts[i] = NULL; + } +} + +void NetConnection::setGhostFrom(bool ghostFrom) +{ + if(mGhostArray) + return; + + if(ghostFrom) + { + mGhostFreeIndex = mGhostZeroUpdateIndex = 0; + mGhostArray = new GhostInfo *[MaxGhostCount]; + mGhostRefs = new GhostInfo[MaxGhostCount]; + S32 i; + for(i = 0; i < MaxGhostCount; i++) + { + mGhostRefs[i].obj = NULL; + mGhostRefs[i].index = i; + mGhostRefs[i].updateMask = 0; + } + mGhostLookupTable = new GhostInfo *[GhostLookupTableSize]; + for(i = 0; i < GhostLookupTableSize; i++) + mGhostLookupTable[i] = 0; + } +} + +void NetConnection::ghostOnRemove() +{ + if(mGhostArray) + clearGhostInfo(); +} + +void NetConnection::ghostPacketDropped(PacketNotify *notify) +{ + GhostRef *packRef = notify->ghostList; + // loop through all the packRefs in the packet + + while(packRef) + { + GhostRef *temp = packRef->nextRef; + + U32 orFlags = 0; + AssertFatal(packRef->nextUpdateChain == NULL, "Out of order notify!!"); + + // clear out the ref for this object, plus or together all + // flags from updates after this + + GhostRef **walk = &(packRef->ghost->updateChain); + while(*walk != packRef) + { + orFlags |= (*walk)->mask; + walk = &((*walk)->nextUpdateChain); + } + *walk = 0; + + // for any flags we haven't updated since this (dropped) packet + // or them into the mask so they'll get updated soon + + orFlags = packRef->mask & ~orFlags; + + if(orFlags) + { + if(!packRef->ghost->updateMask) + { + packRef->ghost->updateMask = orFlags; + ghostPushNonZero(packRef->ghost); + } + else + packRef->ghost->updateMask |= orFlags; + } + + // if this packet was ghosting an object, set it + // to re ghost at it's earliest convenience + + if(packRef->ghostInfoFlags & GhostInfo::Ghosting) + { + packRef->ghost->flags |= GhostInfo::NotYetGhosted; + packRef->ghost->flags &= ~GhostInfo::Ghosting; + } + + // otherwise, if it was being deleted, + // set it to re-delete + + else if(packRef->ghostInfoFlags & GhostInfo::KillingGhost) + { + packRef->ghost->flags |= GhostInfo::KillGhost; + packRef->ghost->flags &= ~GhostInfo::KillingGhost; + } + + delete packRef; + packRef = temp; + } +} + +void NetConnection::ghostPacketReceived(PacketNotify *notify) +{ + GhostRef *packRef = notify->ghostList; + + // loop through all the notifies in this packet + + while(packRef) + { + GhostRef *temp = packRef->nextRef; + + AssertFatal(packRef->nextUpdateChain == NULL, "Out of order notify!!"); + + // clear this notify from the end of the object's notify + // chain + + GhostRef **walk = &(packRef->ghost->updateChain); + while(*walk != packRef) + walk = &((*walk)->nextUpdateChain); + + *walk = 0; + + // if this object was ghosting , it is now ghosted + + if(packRef->ghostInfoFlags & GhostInfo::Ghosting) + packRef->ghost->flags &= ~GhostInfo::Ghosting; + + // otherwise, if it was dieing, free the ghost + + else if(packRef->ghostInfoFlags & GhostInfo::KillingGhost) + freeGhostInfo(packRef->ghost); + + delete packRef; + packRef = temp; + } +} + +struct UpdateQueueEntry +{ + F32 priority; + GhostInfo *obj; + + UpdateQueueEntry(F32 in_priority, GhostInfo *in_obj) + { priority = in_priority; obj = in_obj; } +}; + +static S32 QSORT_CALLBACK UQECompare(const void *a,const void *b) +{ + GhostInfo *ga = *((GhostInfo **) a); + GhostInfo *gb = *((GhostInfo **) b); + + F32 ret = ga->priority - gb->priority; + return (ret < 0) ? -1 : ((ret > 0) ? 1 : 0); +} + +void NetConnection::ghostWritePacket(BitStream *bstream, PacketNotify *notify) +{ +#ifdef TORQUE_DEBUG_NET + bstream->writeInt(DebugChecksum, 32); +#endif + + notify->ghostList = NULL; + + if(!isGhostingFrom()) + return; + + if(!bstream->writeFlag(mGhosting)) + return; + + // fill a packet (or two) with ghosting data + + // first step is to check all our polled ghosts: + + // 1. Scope query - find if any new objects have come into + // scope and if any have gone out. + // 2. call scoped objects' priority functions if the flag set is nonzero + // A removed ghost is assumed to have a high priority + // 3. call updates based on sorted priority until the packet is + // full. set flags to zero for all updated objects + + CameraScopeQuery camInfo; + + camInfo.camera = NULL; + camInfo.pos.set(0,0,0); + camInfo.orientation.set(0,1,0); + camInfo.visibleDistance = 1; + camInfo.fov = (F32)(3.1415f / 4.0f); + camInfo.sinFov = 0.7071f; + camInfo.cosFov = 0.7071f; + + GhostInfo *walk; + + // only need to worry about the ghosts that have update masks set... + S32 maxIndex = 0; + S32 i; + for(i = 0; i < mGhostZeroUpdateIndex; i++) + { + // increment the updateSkip for everyone... it's all good + walk = mGhostArray[i]; + walk->updateSkipCount++; + if(!(walk->flags & (GhostInfo::ScopeAlways | GhostInfo::ScopeLocalAlways))) + walk->flags &= ~GhostInfo::InScope; + } + + if( mScopeObject ) + mScopeObject->onCameraScopeQuery( this, &camInfo ); + doneScopingScene(); + + for(i = mGhostZeroUpdateIndex - 1; i >= 0; i--) + { + // [rene, 07-Mar-11] Killing ghosts depending on the camera scope queries + // seems like a bad thing to me and something that definitely has the potential + // of causing scoping to eat into bandwidth rather than preserve it. As soon + // as an object comes back into scope, it will have to completely retransmit its + // full server-side state from scratch. + + if(!(mGhostArray[i]->flags & GhostInfo::InScope)) + detachObject(mGhostArray[i]); + } + + for(i = mGhostZeroUpdateIndex - 1; i >= 0; i--) + { + walk = mGhostArray[i]; + if(walk->index > maxIndex) + maxIndex = walk->index; + + // clear out any kill objects that haven't been ghosted yet + if((walk->flags & GhostInfo::KillGhost) && (walk->flags & GhostInfo::NotYetGhosted)) + { + freeGhostInfo(walk); + continue; + } + // don't do any ghost processing on objects that are being killed + // or in the process of ghosting + else if(!(walk->flags & (GhostInfo::KillingGhost | GhostInfo::Ghosting))) + { + if(walk->flags & GhostInfo::KillGhost) + walk->priority = 10000; + else + walk->priority = walk->obj->getUpdatePriority(&camInfo, walk->updateMask, walk->updateSkipCount); + } + else + walk->priority = 0; + } + GhostRef *updateList = NULL; + dQsort(mGhostArray, mGhostZeroUpdateIndex, sizeof(GhostInfo *), UQECompare); + + // reset the array indices... + for(i = mGhostZeroUpdateIndex - 1; i >= 0; i--) + mGhostArray[i]->arrayIndex = i; + + S32 sendSize = 1; + while(maxIndex >>= 1) + sendSize++; + + if(sendSize < 3) + sendSize = 3; + + bstream->writeInt(sendSize - 3, GhostIndexBitSize); + + U32 count = 0; + // + for(i = mGhostZeroUpdateIndex - 1; i >= 0 && !bstream->isFull(); i--) + { + GhostInfo *walk = mGhostArray[i]; + if(walk->flags & (GhostInfo::KillingGhost | GhostInfo::Ghosting)) + continue; + + bstream->writeFlag(true); + + bstream->writeInt(walk->index, sendSize); + U32 updateMask = walk->updateMask; + + GhostRef *upd = new GhostRef; + + upd->nextRef = updateList; + updateList = upd; + upd->nextUpdateChain = walk->updateChain; + walk->updateChain = upd; + + upd->ghost = walk; + upd->ghostInfoFlags = 0; + + if(walk->flags & GhostInfo::KillGhost) + { + walk->flags &= ~GhostInfo::KillGhost; + walk->flags |= GhostInfo::KillingGhost; + walk->updateMask = 0; + upd->mask = updateMask; + ghostPushToZero(walk); + upd->ghostInfoFlags = GhostInfo::KillingGhost; + bstream->writeFlag(true); // killing ghost + } + else + { + bstream->writeFlag(false); +#ifdef TORQUE_DEBUG_NET + U32 startPos = bstream->getCurPos(); +#endif + if(walk->flags & GhostInfo::NotYetGhosted) + { + S32 classId = walk->obj->getClassId(getNetClassGroup()); + bstream->writeClassId(classId, NetClassTypeObject, getNetClassGroup()); +#ifdef TORQUE_DEBUG_NET + bstream->writeInt(classId ^ DebugChecksum, 32); +#endif + + walk->flags &= ~GhostInfo::NotYetGhosted; + walk->flags |= GhostInfo::Ghosting; + upd->ghostInfoFlags = GhostInfo::Ghosting; + } +#ifdef TORQUE_DEBUG_NET + else { + S32 classId = walk->obj->getClassId(getNetClassGroup()); + bstream->writeClassId(classId, NetClassTypeObject, getNetClassGroup()); + bstream->writeInt(classId ^ DebugChecksum, 32); + } +#endif + // update the object +#ifdef TORQUE_NET_STATS + U32 beginSize = bstream->getBitPosition(); +#endif + U32 retMask = walk->obj->packUpdate(this, updateMask, bstream); +#ifdef TORQUE_NET_STATS + walk->obj->getClassRep()->updateNetStatPack(updateMask, bstream->getBitPosition() - beginSize); +#endif + DEBUG_LOG(("PKLOG %d GHOST %d: %s", getId(), bstream->getBitPosition() - 16 - startPos, walk->obj->getClassName())); + + AssertFatal((retMask & (~updateMask)) == 0, "Cannot set new bits in packUpdate return"); + + ghostWriteExtra(walk->obj,bstream); + + walk->updateMask = retMask; + if(!retMask) + ghostPushToZero(walk); + + upd->mask = updateMask & ~retMask; + + //PacketStream::getStats()->addBits(PacketStats::Send, bstream->getCurPos() - startPos, walk->obj->getPersistTag()); +#ifdef TORQUE_DEBUG_NET + bstream->writeInt(walk->index ^ DebugChecksum, 32); +#endif + } + walk->updateSkipCount = 0; + count++; + } + //Con::printf("Ghosts updated: %d (%d remain)", count, mGhostZeroUpdateIndex); + // no more objects... + bstream->writeFlag(false); + notify->ghostList = updateList; +} + +void NetConnection::ghostReadPacket(BitStream *bstream) +{ +#ifdef TORQUE_DEBUG_NET + U32 sum = bstream->readInt(32); + AssertISV(sum == DebugChecksum, "Invalid checksum."); +#endif + + if(!isGhostingTo()) + return; + if(!bstream->readFlag()) + return; + + S32 idSize; + idSize = bstream->readInt( GhostIndexBitSize); + idSize += 3; + + // while there's an object waiting... + gGhostUpdates = 0; + + while(bstream->readFlag()) + { + + gGhostUpdates++; + + U32 index; + //S32 startPos = bstream->getCurPos(); + index = (U32) bstream->readInt(idSize); + if(bstream->readFlag()) // is this ghost being deleted? + { + mGhostsActive--; + AssertFatal(mLocalGhosts[index] != NULL, "Error, NULL ghost encountered."); + mLocalGhosts[index]->deleteObject(); + mLocalGhosts[index] = NULL; + } + else + { + if(!mLocalGhosts[index]) // it's a new ghost... cool + { + mGhostsActive++; + S32 classId = bstream->readClassId(NetClassTypeObject, getNetClassGroup()); + if(classId == -1) + { + setLastError("Invalid packet. (invalid new ghost class id)"); + return; + } + + NetObject *obj = (NetObject *) ConsoleObject::create(getNetClassGroup(), NetClassTypeObject, classId); + if(!obj) + { + setLastError("Invalid packet. (failed to create new ghost)"); + return; + } + // remove all flags associated with netobject + obj->mNetFlags &= ~(BIT(NetObject::MaxNetFlagBit+1)-1); + // we're a ghost... + obj->mNetFlags |= NetObject::IsGhost; + + // object gets initial update before adding to the manager + + obj->mNetIndex = index; + mLocalGhosts[index] = obj; +#ifdef TORQUE_DEBUG_NET + U32 checksum = bstream->readInt(32); + S32 origId = checksum ^ DebugChecksum; + AssertISV(mLocalGhosts[index] != NULL, "Invalid dest ghost."); + AssertISV(origId == mLocalGhosts[index]->getClassId(getNetClassGroup()), + avar("class id mismatch for dest class %s.", + mLocalGhosts[index]->getClassName()) ); +#endif + + // give derived classes a chance to prepare ghost for reading + ghostPreRead(mLocalGhosts[index],true); + +#ifdef TORQUE_NET_STATS + U32 beginSize = bstream->getBitPosition(); +#endif + mLocalGhosts[index]->unpackUpdate(this, bstream); +#ifdef TORQUE_NET_STATS + mLocalGhosts[index]->getClassRep()->updateNetStatUnpack(bstream->getBitPosition() - beginSize); +#endif + // Setup the remote object pointers before + // we register so that it can be used from onAdd. + if( mRemoteConnection ) + { + obj->mServerObject = mRemoteConnection->resolveObjectFromGhostIndex(index); + if ( obj->mServerObject ) + { + obj->mServerObject->mClientObject = obj; + + // Sync selection flag as otherwise the editor will end up setting only + // server-side flags when selecting an object that hasn't been ghosted yet + // (usually the case when creating new objects). + + if( obj->mServerObject->isSelected() ) + obj->setSelected( true ); + } + } + + if(!obj->registerObject()) + { + if(mErrorBuffer.isEmpty()) + setLastError("Invalid packet. (failed to register ghost)"); + return; + } + + addObject(obj); + ghostReadExtra(mLocalGhosts[index],bstream,true); + } + else + { +#ifdef TORQUE_DEBUG_NET + S32 classId = bstream->readClassId(NetClassTypeObject, getNetClassGroup()); + U32 checksum = bstream->readInt(32); + S32 origId = checksum ^ DebugChecksum; + AssertISV(mLocalGhosts[index] != NULL, "Invalid dest ghost."); + AssertISV(origId == mLocalGhosts[index]->getClassId(getNetClassGroup()), + avar("class id mismatch for dest class %s.", + mLocalGhosts[index]->getClassName()) ); +#endif + // give derived classes a chance to prepare ghost for reading + ghostPreRead(mLocalGhosts[index],false); + +#ifdef TORQUE_NET_STATS + U32 beginSize = bstream->getBitPosition(); +#endif + mLocalGhosts[index]->unpackUpdate(this, bstream); +#ifdef TORQUE_NET_STATS + mLocalGhosts[index]->getClassRep()->updateNetStatUnpack(bstream->getBitPosition() - beginSize); +#endif + ghostReadExtra(mLocalGhosts[index],bstream,false); + } + //PacketStream::getStats()->addBits(PacketStats::Receive, bstream->getCurPos() - startPos, ghostRefs[index].localGhost->getPersistTag()); +#ifdef TORQUE_DEBUG_NET + U32 checksum = bstream->readInt(32); + S32 origIndex = checksum ^ DebugChecksum; + AssertISV(origIndex == index, + avar("unpackUpdate did not match packUpdate for object of class %s.", + mLocalGhosts[index]->getClassName()) ); +#endif + if(mErrorBuffer.isNotEmpty()) + return; + } + } +} + +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- + +void NetConnection::setScopeObject(NetObject *obj) +{ + if(((NetObject *) mScopeObject) == obj) + return; + mScopeObject = obj; +} + +void NetConnection::detachObject(GhostInfo *info) +{ + // mark it for ghost killin' + info->flags |= GhostInfo::KillGhost; + + // if the mask is in the zero range, we've got to move it up... + if(!info->updateMask) + { + info->updateMask = 0xFFFFFFFF; + ghostPushNonZero(info); + } + if(info->obj) + { + if(info->prevObjectRef) + info->prevObjectRef->nextObjectRef = info->nextObjectRef; + else + info->obj->mFirstObjectRef = info->nextObjectRef; + if(info->nextObjectRef) + info->nextObjectRef->prevObjectRef = info->prevObjectRef; + + // remove it from the lookup table + U32 id = info->obj->getId(); + for(GhostInfo **walk = &mGhostLookupTable[id & (GhostLookupTableSize - 1)]; *walk; walk = &((*walk)->nextLookupInfo)) + { + GhostInfo *temp = *walk; + if(temp == info) + { + *walk = temp->nextLookupInfo; + break; + } + } + info->prevObjectRef = info->nextObjectRef = NULL; + info->obj = NULL; + } +} + +void NetConnection::freeGhostInfo(GhostInfo *ghost) +{ + AssertFatal(ghost->arrayIndex < mGhostFreeIndex, "Ghost already freed."); + if(ghost->arrayIndex < mGhostZeroUpdateIndex) + { + AssertFatal(ghost->updateMask != 0, "Invalid ghost mask."); + ghost->updateMask = 0; + ghostPushToZero(ghost); + } + ghostPushZeroToFree(ghost); + AssertFatal(ghost->updateChain == NULL, "Ack!"); +} + +//----------------------------------------------------------------------------- + +void NetConnection::objectLocalScopeAlways(NetObject *obj) +{ + if(!isGhostingFrom()) + return; + objectInScope(obj); + for(GhostInfo *walk = mGhostLookupTable[obj->getId() & (GhostLookupTableSize - 1)]; walk; walk = walk->nextLookupInfo) + { + if(walk->obj != obj) + continue; + walk->flags |= GhostInfo::ScopeLocalAlways; + return; + } +} + +void NetConnection::objectLocalClearAlways(NetObject *obj) +{ + if(!isGhostingFrom()) + return; + for(GhostInfo *walk = mGhostLookupTable[obj->getId() & (GhostLookupTableSize - 1)]; walk; walk = walk->nextLookupInfo) + { + if(walk->obj != obj) + continue; + walk->flags &= ~GhostInfo::ScopeLocalAlways; + return; + } +} + +bool NetConnection::validateGhostArray() +{ + AssertFatal(mGhostZeroUpdateIndex >= 0 && mGhostZeroUpdateIndex <= mGhostFreeIndex, "Invalid update index range."); + AssertFatal(mGhostFreeIndex <= MaxGhostCount, "Invalid free index range."); + U32 i; + for(i = 0; i < mGhostZeroUpdateIndex; i ++) + { + AssertFatal(mGhostArray[i]->arrayIndex == i, "Invalid array index."); + AssertFatal(mGhostArray[i]->updateMask != 0, "Invalid ghost mask."); + } + for(; i < mGhostFreeIndex; i ++) + { + AssertFatal(mGhostArray[i]->arrayIndex == i, "Invalid array index."); + AssertFatal(mGhostArray[i]->updateMask == 0, "Invalid ghost mask."); + } + for(; i < MaxGhostCount; i++) + { + AssertFatal(mGhostArray[i]->arrayIndex == i, "Invalid array index."); + } + return true; +} + +void NetConnection::objectInScope(NetObject *obj) +{ + if (!mScoping || !isGhostingFrom()) + return; + if (obj->isScopeLocal() && !isLocalConnection()) + return; + + S32 index = obj->getId() & (GhostLookupTableSize - 1); + + // check if it's already in scope + // the object may have been cleared out without the lookupTable being cleared + // so validate that the object pointers are the same. + + for(GhostInfo *walk = mGhostLookupTable[index ]; walk; walk = walk->nextLookupInfo) + { + if(walk->obj != obj) + continue; + walk->flags |= GhostInfo::InScope; + + // Make sure scope always if reflected on the ghostinfo too + if (obj->mNetFlags.test(NetObject::ScopeAlways)) + walk->flags |= GhostInfo::ScopeAlways; + + return; + } + + if (mGhostFreeIndex == MaxGhostCount) + { + AssertWarn(0,"NetConnection::objectInScope: too many ghosts"); + return; + } + + GhostInfo *giptr = mGhostArray[mGhostFreeIndex]; + ghostPushFreeToZero(giptr); + giptr->updateMask = 0xFFFFFFFF; + ghostPushNonZero(giptr); + + giptr->flags = GhostInfo::NotYetGhosted | GhostInfo::InScope; + + if(obj->mNetFlags.test(NetObject::ScopeAlways)) + giptr->flags |= GhostInfo::ScopeAlways; + + giptr->obj = obj; + giptr->updateChain = NULL; + giptr->updateSkipCount = 0; + + giptr->connection = this; + + giptr->nextObjectRef = obj->mFirstObjectRef; + if(obj->mFirstObjectRef) + obj->mFirstObjectRef->prevObjectRef = giptr; + giptr->prevObjectRef = NULL; + obj->mFirstObjectRef = giptr; + + giptr->nextLookupInfo = mGhostLookupTable[index]; + mGhostLookupTable[index] = giptr; + //AssertFatal(validateGhostArray(), "Invalid ghost array!"); +} + +//----------------------------------------------------------------------------- + +void NetConnection::handleConnectionMessage(U32 message, U32 sequence, U32 ghostCount) +{ + if(( message == SendNextDownloadRequest + || message == FileDownloadSizeMessage + || message == GhostAlwaysStarting + || message == GhostAlwaysDone + || message == EndGhosting) && !isGhostingTo()) + { + setLastError("Invalid packet. (not ghosting)"); + return; + } + + S32 i; + GhostSave sv; + switch(message) + { + case GhostAlwaysDone: + mGhostingSequence = sequence; + NetConnection::smGhostAlwaysDone.trigger(); + // ok, all the ghost always objects are now on the client... but! + // it's possible that there were some file load errors... + // if so, we need to indicate to the server to restart ghosting, after + // we download all the files... + sv.ghost = NULL; + sv.index = -1; + mGhostAlwaysSaveList.push_back(sv); + if(mGhostAlwaysSaveList.size() == 1) + loadNextGhostAlwaysObject(true); + break; + case ReadyForNormalGhosts: + if(sequence != mGhostingSequence) + return; + Con::executef(this, "onGhostAlwaysObjectsReceived"); + Con::printf("Ghost Always objects received."); + mGhosting = true; + for(i = 0; i < mGhostFreeIndex; i++) + { + if(mGhostArray[i]->flags & GhostInfo::ScopedEvent) + mGhostArray[i]->flags &= ~(GhostInfo::Ghosting | GhostInfo::ScopedEvent); + } + break; + case EndGhosting: + onEndGhosting(); + // just delete all the local ghosts, + // and delete all the ghosts in the current save list + for(i = 0; i < MaxGhostCount; i++) + { + if(mLocalGhosts[i]) + { + mLocalGhosts[i]->deleteObject(); + mLocalGhosts[i] = NULL; + } + } + while(mGhostAlwaysSaveList.size()) + { + delete mGhostAlwaysSaveList[0].ghost; + mGhostAlwaysSaveList.pop_front(); + } + break; + case GhostAlwaysStarting: + Con::executef("onGhostAlwaysStarted", Con::getIntArg(ghostCount)); + break; + case SendNextDownloadRequest: + sendNextFileDownloadRequest(); + break; + case FileDownloadSizeMessage: + mCurrentFileBufferSize = sequence; + mCurrentFileBuffer = dRealloc(mCurrentFileBuffer, mCurrentFileBufferSize); + mCurrentFileBufferOffset = 0; + break; + } +} + +void NetConnection::activateGhosting() +{ + if(!isGhostingFrom()) + return; + + mGhostingSequence++; + + // iterate through the ghost always objects and InScope them... + // also post em all to the other side. + + SimSet* ghostAlwaysSet = Sim::getGhostAlwaysSet(); + + SimSet::iterator i; + + AssertFatal((mGhostFreeIndex == 0) && (mGhostZeroUpdateIndex == 0), "Error: ghosts in the ghost list before activate."); + + U32 sz = ghostAlwaysSet->size(); + S32 j; + + for(j = 0; j < sz; j++) + { + U32 idx = MaxGhostCount - sz + j; + mGhostArray[j] = mGhostRefs + idx; + mGhostArray[j]->arrayIndex = j; + } + for(j = sz; j < MaxGhostCount; j++) + { + U32 idx = j - sz; + mGhostArray[j] = mGhostRefs + idx; + mGhostArray[j]->arrayIndex = j; + } + mScoping = true; // so that objectInScope will work + for(i = ghostAlwaysSet->begin(); i != ghostAlwaysSet->end(); i++) + { + AssertFatal(dynamic_cast(*i) != NULL, avar("Non NetObject in GhostAlwaysSet: %s", (*i)->getClassName())); + NetObject *obj = (NetObject *)(*i); + if(obj->mNetFlags.test(NetObject::Ghostable)) + objectInScope(obj); + } + // Send the initial ghosting connection message. + sendConnectionMessage(GhostAlwaysStarting, mGhostingSequence, ghostAlwaysSet->size()); + + // If this is the connection to the local client... + if (getLocalClientConnection() == this) + { + // Get a pointer to the local client. + NetConnection* pClient = NetConnection::getConnectionToServer(); + + Con::executef("onGhostAlwaysStarted", Con::getIntArg(mGhostZeroUpdateIndex)); + + // Set up a buffer for the object send. + U8 iBuffer[4096]; + BitStream mStream(iBuffer, 4096); + + // Iterate through the scope always objects... + for (j = mGhostZeroUpdateIndex - 1; j >= 0; j--) + { + AssertFatal((mGhostArray[j]->flags & GhostInfo::ScopeAlways) != 0, "NetConnection::activateGhosting: Non-scope always in the scope always list.") + + // Clear the ghost update mask and flags appropriately. + mGhostArray[j]->updateMask = 0; + ghostPushToZero(mGhostArray[j]); + mGhostArray[j]->flags &= ~GhostInfo::NotYetGhosted; + mGhostArray[j]->flags |= GhostInfo::ScopedEvent; + + // Set up a pointer to the new object. + NetObject* pObject = 0; + + // If there's a valid ghost object... + if (mGhostArray[j]->obj) + { + // Pack the server object's update. + mStream.setPosition(0); + mStream.clearCompressionPoint(); + U32 retMask = mGhostArray[j]->obj->packUpdate(this, 0xFFFFFFFF, &mStream); + if ( retMask != 0 ) + mGhostArray[j]->obj->setMaskBits( retMask ); + + // Create a new object instance for the client. + pObject = (NetObject*)ConsoleObject::create(pClient->getNetClassGroup(), NetClassTypeObject, mGhostArray[j]->obj->getClassId(getNetClassGroup())); + + // Set the client object networking flags. + pObject->mNetFlags = NetObject::IsGhost; + pObject->mNetIndex = mGhostArray[j]->index; + + // Unpack the client object's update. + mStream.setPosition(0); + mStream.clearCompressionPoint(); + pObject->unpackUpdate(pClient, &mStream); + } + else + { + // Otherwise, create a new dummy netobject. + pObject = new NetObject; + } + + // Execute the appropriate console callback. + Con::executef("onGhostAlwaysObjectReceived"); + + // Set the ghost always object for the client. + pClient->setGhostAlwaysObject(pObject, mGhostArray[j]->index); + } + } + else + { + // Iterate through the scope always objects... + for (j = mGhostZeroUpdateIndex - 1; j >= 0; j--) + { + AssertFatal((mGhostArray[j]->flags & GhostInfo::ScopeAlways) != 0, "NetConnection::activateGhosting: Non-scope always in the scope always list.") + + // Clear the ghost update mask and flags appropriately. + mGhostArray[j]->updateMask = 0; + ghostPushToZero(mGhostArray[j]); + mGhostArray[j]->flags &= ~GhostInfo::NotYetGhosted; + mGhostArray[j]->flags |= GhostInfo::ScopedEvent; + + // Post a network event to ghost the scope always object. + postNetEvent(new GhostAlwaysObjectEvent(mGhostArray[j]->obj, mGhostArray[j]->index)); + } + } + + // Send the ghosting always done message. + sendConnectionMessage(GhostAlwaysDone, mGhostingSequence); //AssertFatal(validateGhostArray(), "Invalid ghost array!"); +} + +void NetConnection::clearGhostInfo() +{ + // gotta clear out the ghosts... + for(PacketNotify *walk = mNotifyQueueHead; walk; walk = walk->nextPacket) + { + ghostPacketReceived(walk); + walk->ghostList = NULL; + } + for(S32 i = 0; i < MaxGhostCount; i++) + { + if(mGhostRefs[i].arrayIndex < mGhostFreeIndex) + { + detachObject(&mGhostRefs[i]); + freeGhostInfo(&mGhostRefs[i]); + } + } + AssertFatal((mGhostFreeIndex == 0) && (mGhostZeroUpdateIndex == 0), "Invalid indices."); +} + +void NetConnection::resetGhosting() +{ + if(!isGhostingFrom()) + return; + // stop all ghosting activity + // send a message to the other side notifying of this + + mGhosting = false; + mScoping = false; + sendConnectionMessage(EndGhosting, mGhostingSequence); + mGhostingSequence++; + clearGhostInfo(); + //AssertFatal(validateGhostArray(), "Invalid ghost array!"); +} + +void NetConnection::setGhostAlwaysObject(NetObject *object, U32 index) +{ + if(!isGhostingTo()) + { + object->deleteObject(); + setLastError("Invalid packet. (unexpected ghostalways)"); + return; + } + object->mNetFlags = NetObject::IsGhost; + object->mNetIndex = index; + + // while there's an object waiting... + if ( isLocalConnection() ) + { + object->mServerObject = mRemoteConnection->resolveObjectFromGhostIndex(index); + if ( object->mServerObject ) + object->mServerObject->mClientObject = object; + } + + GhostSave sv; + sv.ghost = object; + sv.index = index; + mGhostAlwaysSaveList.push_back(sv); + + // check if we are already downloading files for a previous object: + if(mGhostAlwaysSaveList.size() == 1) + loadNextGhostAlwaysObject(true); // the initial call always has "new" files + +} + +void NetConnection::fileDownloadSegmentComplete() +{ + // this is called when a the file list has finished processing... + // at this point we can try again to add the object + // subclasses can override this to do, for example, datablock redos. + if(mGhostAlwaysSaveList.size()) + loadNextGhostAlwaysObject(mNumDownloadedFiles != 0); +} + +void NetConnection::loadNextGhostAlwaysObject(bool hadNewFiles) +{ + if(!mGhostAlwaysSaveList.size()) + return; + + while(mGhostAlwaysSaveList.size()) + { + + if (isLocalConnection()) hadNewFiles = false; + + // only check for new files if this is the first load, or if new + // files were downloaded from the server. +// if(hadNewFiles) +// gResourceManager->setMissingFileLogging(true); +// +// gResourceManager->clearMissingFileList(); + NetObject *object = mGhostAlwaysSaveList[0].ghost; + U32 index = mGhostAlwaysSaveList[0].index; + + if(!object) + { + // a null object is used to signify that the last ghost in the list is down + mGhostAlwaysSaveList.pop_front(); + AssertFatal(mGhostAlwaysSaveList.size() == 0, "Error! Ghost save list should be empty!"); + sendConnectionMessage(ReadyForNormalGhosts, mGhostingSequence); +// gResourceManager->setMissingFileLogging(false); + return; + } + mFilesWereDownloaded = hadNewFiles; + + if(!object->registerObject()) + { + mFilesWereDownloaded = false; + // make sure there's an error message if necessary + if(mErrorBuffer.isEmpty()) + setLastError("Invalid packet. (failed to register ghost always)"); + + // if there were no new files, make sure the error message + // is the one from the last time we tried to add this object + if(!hadNewFiles) + { + mErrorBuffer = mLastFileErrorBuffer; +// gResourceManager->setMissingFileLogging(false); + return; + } + + // object failed to load, let's see if it had any missing files +// if(!gResourceManager->getMissingFileList(mMissingFileList)) +// { +// // no missing files, must be an error +// // connection will automagically delete the ghost always list +// // when this error is reported. +// gResourceManager->setMissingFileLogging(false); +// return; +// } + + // ok, copy the error buffer out to a scratch pad for now + mLastFileErrorBuffer = mErrorBuffer; + mErrorBuffer = String(); + + // request the missing files... + mNumDownloadedFiles = 0; + sendNextFileDownloadRequest(); + break; + } + mFilesWereDownloaded = false; +// gResourceManager->setMissingFileLogging(false); + addObject(object); + mGhostAlwaysSaveList.pop_front(); + + AssertFatal(mLocalGhosts[index] == NULL, "Ghost already in table!"); + mLocalGhosts[index] = object; + hadNewFiles = true; + } +} + +//----------------------------------------------------------------------------- + +NetObject *NetConnection::resolveGhost(S32 id) +{ + return mLocalGhosts[id]; +} + +NetObject *NetConnection::resolveObjectFromGhostIndex(S32 id) +{ + return mGhostRefs[id].obj; +} + +S32 NetConnection::getGhostIndex(NetObject *obj) +{ + if(!isGhostingFrom()) + return obj->mNetIndex; + S32 index = obj->getId() & (GhostLookupTableSize - 1); + + for(GhostInfo *gptr = mGhostLookupTable[index]; gptr; gptr = gptr->nextLookupInfo) + { + if(gptr->obj == obj && (gptr->flags & (GhostInfo::KillingGhost | GhostInfo::Ghosting | GhostInfo::NotYetGhosted | GhostInfo::KillGhost)) == 0) + return gptr->index; + } + return -1; +} + +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- + +void NetConnection::ghostWriteStartBlock(ResizeBitStream *stream) +{ + // Ok, writing the start block for the ghosts: + // here's how it goes. + // + // First we record out all the indices and class ids for all the objects + // This is so when the objects are read in, all the objects are instantiated + // before they are unpacked. The unpack code may reference other + // existing ghosts, so we want to make sure that all the ghosts are in the + // table with the correct pointers before any of the unpacks are called. + + stream->write(mGhostingSequence); + + // first write out the indices and ids: + for(U32 i = 0; i < MaxGhostCount; i++) + { + if(mLocalGhosts[i]) + { + stream->writeFlag(true); + stream->writeInt(i, GhostIdBitSize); + stream->writeClassId(mLocalGhosts[i]->getClassId(getNetClassGroup()), NetClassTypeObject, getNetClassGroup()); + stream->validate(); + } + } + // mark off the end of the ghost list: + // it would be more space efficient to write out a count of active ghosts followed + // by index run lengths, but hey, what's a few bits here and there? + + stream->writeFlag(false); + + // then, for each ghost written into the start block, write the full pack update + // into the start block. For demos to work properly, packUpdate must + // be callable from client objects. + for(U32 i = 0; i < MaxGhostCount; i++) + { + if(mLocalGhosts[i]) + { + U32 retMask = mLocalGhosts[i]->packUpdate(this, 0xFFFFFFFF, stream); + if ( retMask != 0 ) mLocalGhosts[i]->setMaskBits( retMask ); + stream->validate(); + } + } +} + +void NetConnection::ghostReadStartBlock(BitStream *stream) +{ + stream->read(&mGhostingSequence); + + // read em back in. + // first, read in the index/class id, construct the object, and place it in mLocalGhosts[i] + + while(stream->readFlag()) + { + U32 index = stream->readInt(GhostIdBitSize); + S32 tag = stream->readClassId(NetClassTypeObject, getNetClassGroup()); + NetObject *obj = (NetObject *) ConsoleObject::create(getNetClassGroup(), NetClassTypeObject, tag); + if(!obj) + { + setLastError("Invalid packet. (failed to create ghost from demo block)"); + return; + } + obj->mNetFlags = NetObject::IsGhost; + obj->mNetIndex = index; + mLocalGhosts[index] = obj; + } + + // now, all the ghosts are in the mLocalGhosts, so we loop + // through all non-null mLocalGhosts, unpacking the objects + // as we go: + + for(U32 i = 0; i < MaxGhostCount; i++) + { + if(mLocalGhosts[i]) + { + mLocalGhosts[i]->unpackUpdate(this, stream); + if(!mLocalGhosts[i]->registerObject()) + { + if(mErrorBuffer.isEmpty()) + setLastError("Invalid packet. (failed to register ghost from demo block)"); + return; + } + addObject(mLocalGhosts[i]); + } + } + // MARKF - TODO - looks like we could have memory leaks here + // if there are errors. +} diff --git a/Engine/source/sim/netInterface.cpp b/Engine/source/sim/netInterface.cpp new file mode 100644 index 000000000..2ec7cbff7 --- /dev/null +++ b/Engine/source/sim/netInterface.cpp @@ -0,0 +1,656 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "platform/event.h" +#include "sim/netConnection.h" +#include "sim/netInterface.h" +#include "core/stream/bitStream.h" +#include "math/mRandom.h" +#include "core/util/journal/journal.h" + +#ifdef GGC_PLUGIN +#include "GGCNatTunnel.h" +extern void HandleGGCPacket(NetAddress* addr, unsigned char* data, U32 dataSize); +#endif + +NetInterface *GNet = NULL; + +NetInterface::NetInterface() +{ + AssertFatal(GNet == NULL, "ERROR: Multiple net interfaces declared."); + GNet = this; + + mLastTimeoutCheckTime = 0; + mAllowConnections = true; + +} + +void NetInterface::initRandomData() +{ + mRandomDataInitialized = true; + U32 seed = Platform::getRealMilliseconds(); + + if(Journal::IsPlaying()) + Journal::Read(&seed); + else if(Journal::IsRecording()) + Journal::Write(seed); + + MRandomR250 myRandom(seed); + for(U32 i = 0; i < 12; i++) + mRandomHashData[i] = myRandom.randI(); +} + +void NetInterface::addPendingConnection(NetConnection *connection) +{ + Con::printf("Adding a pending connection"); + mPendingConnections.push_back(connection); +} + +void NetInterface::removePendingConnection(NetConnection *connection) +{ + for(U32 i = 0; i < mPendingConnections.size(); i++) + if(mPendingConnections[i] == connection) + mPendingConnections.erase(i); +} + +NetConnection *NetInterface::findPendingConnection(const NetAddress *address, U32 connectSequence) +{ + for(U32 i = 0; i < mPendingConnections.size(); i++) + if(Net::compareAddresses(address, mPendingConnections[i]->getNetAddress()) && + connectSequence == mPendingConnections[i]->getSequence()) + return mPendingConnections[i]; + return NULL; +} + +void NetInterface::processPacketReceiveEvent(NetAddress srcAddress, RawData packetData) +{ + + U32 dataSize = packetData.size; + BitStream pStream(packetData.data, dataSize); + + // Determine what to do with this packet: + + if(packetData.data[0] & 0x01) // it's a protocol packet... + { + // if the LSB of the first byte is set, it's a game data packet + // so pass it to the appropriate connection. + + // lookup the connection in the addressTable + NetConnection *conn = NetConnection::lookup(&srcAddress); + if(conn) + conn->processRawPacket(&pStream); + } + else + { + // Otherwise, it's either a game info packet or a + // connection handshake packet. + + U8 packetType; + pStream.read(&packetType); + NetAddress *addr = &srcAddress; + + if(packetType <= GameHeartbeat) + handleInfoPacket(addr, packetType, &pStream); +#ifdef GGC_PLUGIN + else if (packetType == GGCPacket) + { + HandleGGCPacket(addr, (U8*)packetData.data, dataSize); + } +#endif + else + { + // check if there's a connection already: + switch(packetType) + { + case ConnectChallengeRequest: + handleConnectChallengeRequest(addr, &pStream); + break; + case ConnectRequest: + handleConnectRequest(addr, &pStream); + break; + case ConnectChallengeResponse: + handleConnectChallengeResponse(addr, &pStream); + break; + case ConnectAccept: + handleConnectAccept(addr, &pStream); + break; + case Disconnect: + handleDisconnect(addr, &pStream); + break; + case ConnectReject: + handleConnectReject(addr, &pStream); + break; + } + } + } +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// Connection handshaking basic overview: +// The torque engine does a two phase connect handshake to +// prevent a spoofed source address Denial-of-Service (DOS) attack +// +// Basically, the initiator of a connection (client) sends a +// Connect Challenge Request packet to the server to initiate the connection +// The server then hashes the source address of the client request +// with some random magic server data to come up with a 16-byte key that +// the client can then use to gain entry to the server. +// This way there are no partially active connection records on the +// server at all. +// +// The client then sends a Connect Request packet to the server, +// including any game specific data necessary to start a connection (a +// server password, for instance), along with the key the server sent +// on the Connect Challenge Response packet. +// +// The server, on receipt of the Connect Request, compares the +// entry key with a computed key, makes sure it can create the requested +// NetConnection subclass, and then passes all processing on to the connection +// instance. +// +// If the subclass reads and accepts he connect request successfully, the +// server sends a Connect Accept packet - otherwise the connection +// is rejected with the sendConnectReject function +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + + +void NetInterface::sendConnectChallengeRequest(NetConnection *conn) +{ + Con::printf("Sending Connect challenge Request"); + BitStream *out = BitStream::getPacketStream(); + + out->write(U8(ConnectChallengeRequest)); + out->write(conn->getSequence()); + + conn->mConnectSendCount++; + conn->mConnectLastSendTime = Platform::getVirtualMilliseconds(); + + BitStream::sendPacketStream(conn->getNetAddress()); +} + +void NetInterface::handleConnectChallengeRequest(const NetAddress *addr, BitStream *stream) +{ + char buf[256]; + Net::addressToString(addr, buf); + Con::printf("Got Connect challenge Request from %s", buf); + if(!mAllowConnections) + return; + + U32 connectSequence; + stream->read(&connectSequence); + + if(!mRandomDataInitialized) + initRandomData(); + + U32 addressDigest[4]; + computeNetMD5(addr, connectSequence, addressDigest); + + BitStream *out = BitStream::getPacketStream(); + out->write(U8(ConnectChallengeResponse)); + out->write(connectSequence); + out->write(addressDigest[0]); + out->write(addressDigest[1]); + out->write(addressDigest[2]); + out->write(addressDigest[3]); + + BitStream::sendPacketStream(addr); +} + +//----------------------------------------------------------------------------- + +void NetInterface::handleConnectChallengeResponse(const NetAddress *address, BitStream *stream) +{ + Con::printf("Got Connect challenge Response"); + U32 connectSequence; + stream->read(&connectSequence); + + NetConnection *conn = findPendingConnection(address, connectSequence); + if(!conn || conn->getConnectionState() != NetConnection::AwaitingChallengeResponse) + return; + + U32 addressDigest[4]; + stream->read(&addressDigest[0]); + stream->read(&addressDigest[1]); + stream->read(&addressDigest[2]); + stream->read(&addressDigest[3]); + conn->setAddressDigest(addressDigest); + + conn->setConnectionState(NetConnection::AwaitingConnectResponse); + conn->mConnectSendCount = 0; + Con::printf("Sending Connect Request"); + sendConnectRequest(conn); +} + +//----------------------------------------------------------------------------- + +void NetInterface::sendConnectRequest(NetConnection *conn) +{ + BitStream *out = BitStream::getPacketStream(); + out->write(U8(ConnectRequest)); + out->write(conn->getSequence()); + + U32 addressDigest[4]; + conn->getAddressDigest(addressDigest); + out->write(addressDigest[0]); + out->write(addressDigest[1]); + out->write(addressDigest[2]); + out->write(addressDigest[3]); + + out->writeString(conn->getClassName()); + conn->writeConnectRequest(out); + conn->mConnectSendCount++; + conn->mConnectLastSendTime = Platform::getVirtualMilliseconds(); + + BitStream::sendPacketStream(conn->getNetAddress()); +} + +//----------------------------------------------------------------------------- + +void NetInterface::handleConnectRequest(const NetAddress *address, BitStream *stream) +{ + if(!mAllowConnections) + return; + Con::printf("Got Connect Request"); + U32 connectSequence; + stream->read(&connectSequence); + + // see if the connection is in the main connection table: + + NetConnection *connect = NetConnection::lookup(address); + if(connect && connect->getSequence() == connectSequence) + { + sendConnectAccept(connect); + return; + } + U32 addressDigest[4]; + U32 computedAddressDigest[4]; + + stream->read(&addressDigest[0]); + stream->read(&addressDigest[1]); + stream->read(&addressDigest[2]); + stream->read(&addressDigest[3]); + + computeNetMD5(address, connectSequence, computedAddressDigest); + if(addressDigest[0] != computedAddressDigest[0] || + addressDigest[1] != computedAddressDigest[1] || + addressDigest[2] != computedAddressDigest[2] || + addressDigest[3] != computedAddressDigest[3]) + return; // bogus connection attempt + + if(connect) + { + if(connect->getSequence() > connectSequence) + return; // the existing connection should be kept - the incoming request is stale. + else + connect->deleteObject(); // disconnect this one, and allow the new one to be created. + } + + char connectionClass[255]; + stream->readString(connectionClass); + + ConsoleObject *co = ConsoleObject::create(connectionClass); + NetConnection *conn = dynamic_cast(co); + if(!conn || !conn->canRemoteCreate()) + { + delete co; + return; + } + conn->registerObject(); + conn->setNetAddress(address); + conn->setNetworkConnection(true); + conn->setSequence(connectSequence); + + const char *errorString = NULL; + if(!conn->readConnectRequest(stream, &errorString)) + { + sendConnectReject(conn, errorString); + conn->deleteObject(); + return; + } + conn->setNetworkConnection(true); + conn->onConnectionEstablished(false); + conn->setEstablished(); + conn->setConnectSequence(connectSequence); + sendConnectAccept(conn); +} + +//----------------------------------------------------------------------------- + +void NetInterface::sendConnectAccept(NetConnection *conn) +{ + BitStream *out = BitStream::getPacketStream(); + out->write(U8(ConnectAccept)); + out->write(conn->getSequence()); + conn->writeConnectAccept(out); + BitStream::sendPacketStream(conn->getNetAddress()); +} + +void NetInterface::handleConnectAccept(const NetAddress *address, BitStream *stream) +{ + U32 connectSequence; + stream->read(&connectSequence); + NetConnection *conn = findPendingConnection(address, connectSequence); + if(!conn || conn->getConnectionState() != NetConnection::AwaitingConnectResponse) + return; + const char *errorString = NULL; + if(!conn->readConnectAccept(stream, &errorString)) + { + conn->handleStartupError(errorString); + removePendingConnection(conn); + conn->deleteObject(); + return; + } + + removePendingConnection(conn); // remove from the pending connection list + conn->setNetworkConnection(true); + conn->onConnectionEstablished(true); // notify the connection that it has been established + conn->setEstablished(); // installs the connection in the connection table, and causes pings/timeouts to happen + conn->setConnectSequence(connectSequence); +} + +void NetInterface::sendConnectReject(NetConnection *conn, const char *reason) +{ + if(!reason) + return; // if the stream is NULL, we reject silently + + BitStream *out = BitStream::getPacketStream(); + out->write(U8(ConnectReject)); + out->write(conn->getSequence()); + out->writeString(reason); + BitStream::sendPacketStream(conn->getNetAddress()); +} + +void NetInterface::handleConnectReject(const NetAddress *address, BitStream *stream) +{ + U32 connectSequence; + stream->read(&connectSequence); + NetConnection *conn = findPendingConnection(address, connectSequence); + if(!conn || (conn->getConnectionState() != NetConnection::AwaitingChallengeResponse && + conn->getConnectionState() != NetConnection::AwaitingConnectResponse)) + return; + removePendingConnection(conn); + char reason[256]; + stream->readString(reason); + conn->onConnectionRejected(reason); + conn->deleteObject(); +} + +void NetInterface::handleDisconnect(const NetAddress *address, BitStream *stream) +{ + NetConnection *conn = NetConnection::lookup(address); + if(!conn) + return; + + U32 connectSequence; + char reason[256]; + + stream->read(&connectSequence); + stream->readString(reason); + + if(conn->getSequence() != connectSequence) + return; + + conn->onDisconnect(reason); + conn->deleteObject(); +} + +void NetInterface::handleInfoPacket(const NetAddress *address, U8 packetType, BitStream *stream) +{ +} + +void NetInterface::processClient() +{ + NetObject::collapseDirtyList(); // collapse all the mask bits... + for(NetConnection *walk = NetConnection::getConnectionList(); + walk; walk = walk->getNext()) + { + if(walk->isConnectionToServer() && (walk->isLocalConnection() || walk->isNetworkConnection())) + walk->checkPacketSend(false); + } +} + +void NetInterface::processServer() +{ + NetObject::collapseDirtyList(); // collapse all the mask bits... + for(NetConnection *walk = NetConnection::getConnectionList(); + walk; walk = walk->getNext()) + { + if(!walk->isConnectionToServer() && (walk->isLocalConnection() || walk->isNetworkConnection())) + walk->checkPacketSend(false); + } +} + +void NetInterface::startConnection(NetConnection *conn) +{ + addPendingConnection(conn); + conn->mConnectionSendCount = 0; + conn->setConnectSequence(Platform::getVirtualMilliseconds()); + conn->setConnectionState(NetConnection::AwaitingChallengeResponse); + + // This is a the client side of the connection, so set the connection to + // server flag. We need to set this early so that if the connection times + // out, its onRemove() will handle the cleanup properly. + conn->setIsConnectionToServer(); + + // Everything set, so send off the request. + sendConnectChallengeRequest(conn); +} + +void NetInterface::sendDisconnectPacket(NetConnection *conn, const char *reason) +{ + Con::printf("Issuing Disconnect packet."); + + // send a disconnect packet... + U32 connectSequence = conn->getSequence(); + + BitStream *out = BitStream::getPacketStream(); + out->write(U8(Disconnect)); + out->write(connectSequence); + out->writeString(reason); + + BitStream::sendPacketStream(conn->getNetAddress()); +} + +void NetInterface::checkTimeouts() +{ + U32 time = Platform::getVirtualMilliseconds(); + if(time > mLastTimeoutCheckTime + TimeoutCheckInterval) + { + for(U32 i = 0; i < mPendingConnections.size();) + { + NetConnection *pending = mPendingConnections[i]; + + if(pending->getConnectionState() == NetConnection::AwaitingChallengeResponse && + time > pending->mConnectLastSendTime + ChallengeRetryTime) + { + if(pending->mConnectSendCount > ChallengeRetryCount) + { + pending->onConnectTimedOut(); + removePendingConnection(pending); + pending->deleteObject(); + continue; + } + else + sendConnectChallengeRequest(pending); + } + else if(pending->getConnectionState() == NetConnection::AwaitingConnectResponse && + time > pending->mConnectLastSendTime + ConnectRetryTime) + { + if(pending->mConnectSendCount > ConnectRetryCount) + { + pending->onConnectTimedOut(); + removePendingConnection(pending); + pending->deleteObject(); + continue; + } + else + sendConnectRequest(pending); + } + i++; + } + mLastTimeoutCheckTime = time; + NetConnection *walk = NetConnection::getConnectionList(); + + while(walk) + { + NetConnection *next = walk->getNext(); + if(walk->checkTimeout(time)) + { + // this baddie timed out + walk->onTimedOut(); + walk->deleteObject(); + } + walk = next; + } + } +} + +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +inline U32 rotlFixed(U32 x, unsigned int y) +{ + return (x >> y) | (x << (32 - y)); +} + +#define MD5STEP(f, w, x, y, z, data, s) w = rotlFixed(w + f(x, y, z) + data, s) + x + +void NetInterface::computeNetMD5(const NetAddress *address, U32 connectSequence, U32 digest[4]) +{ + digest[0] = 0x67452301L; + digest[1] = 0xefcdab89L; + digest[2] = 0x98badcfeL; + digest[3] = 0x10325476L; + + + U32 a, b, c, d; + + a=digest[0]; + b=digest[1]; + c=digest[2]; + d=digest[3]; + + U32 in[16]; + in[0] = address->type; + in[1] = (U32(address->netNum[0]) << 24) | + (U32(address->netNum[1]) << 16) | + (U32(address->netNum[2]) << 8) | + (U32(address->netNum[3])); + in[2] = address->port; + in[3] = connectSequence; + for(U32 i = 0; i < 12; i++) + in[i + 4] = mRandomHashData[i]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + digest[0]+=a; + digest[1]+=b; + digest[2]+=c; + digest[3]+=d; +} + +ConsoleFunctionGroupBegin(NetInterface, "Global control functions for the netInterfaces."); + +ConsoleFunction(allowConnections,void,2,2,"allowConnections(bool allow);" + "@brief Sets whether or not the global NetInterface allows connections from remote hosts.\n\n" + + "@param allow Set to true to allow remote connections.\n" + + "@ingroup Networking\n") +{ + TORQUE_UNUSED(argc); + GNet->setAllowsConnections(dAtob(argv[1])); +} + +ConsoleFunctionGroupEnd(NetInterface); + diff --git a/Engine/source/sim/netInterface.h b/Engine/source/sim/netInterface.h new file mode 100644 index 000000000..2776c890a --- /dev/null +++ b/Engine/source/sim/netInterface.h @@ -0,0 +1,142 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _H_NETINTERFACE +#define _H_NETINTERFACE + +/// NetInterface class. Manages all valid and pending notify protocol connections. +/// +/// @see NetConnection, GameConnection, NetObject, NetEvent +class NetInterface +{ +public: + /// PacketType is encoded as the first byte of each packet. If the LSB of + /// the first byte is set (i.e. if the type number is odd), then the packet + /// is a data protocol packet, otherwise it's an OOB packet, suitable for + /// use in strange protocols, like game querying or connection initialization. + enum PacketTypes + { + MasterServerGameTypesRequest = 2, + MasterServerGameTypesResponse = 4, + MasterServerListRequest = 6, + MasterServerListResponse = 8, + GameMasterInfoRequest = 10, + GameMasterInfoResponse = 12, + GamePingRequest = 14, + GamePingResponse = 16, + GameInfoRequest = 18, + GameInfoResponse = 20, + GameHeartbeat = 22, + GGCPacket = 24, + ConnectChallengeRequest = 26, + ConnectChallengeReject = 28, + ConnectChallengeResponse = 30, + ConnectRequest = 32, + ConnectReject = 34, + ConnectAccept = 36, + Disconnect = 38, + }; +protected: + + Vector mPendingConnections; ///< List of connections that are in the startup phase. + U32 mLastTimeoutCheckTime; ///< Last time all the active connections were checked for timeouts. + U32 mRandomHashData[12]; ///< Data that gets hashed with connect challenge requests to prevent connection spoofing. + bool mRandomDataInitialized; ///< Have we initialized our random number generator? + bool mAllowConnections; ///< Is this NetInterface allowing connections at this time? + + enum NetInterfaceConstants + { + MaxPendingConnects = 20, ///< Maximum number of pending connections. If new connection requests come in before + ChallengeRetryCount = 4, ///< Number of times to send connect challenge requests before giving up. + ChallengeRetryTime = 2500, ///< Timeout interval in milliseconds before retrying connect challenge. + + ConnectRetryCount = 4, ///< Number of times to send connect requests before giving up. + ConnectRetryTime = 2500, ///< Timeout interval in milliseconds before retrying connect request. + TimeoutCheckInterval = 1500, ///< Interval in milliseconds between checking for connection timeouts. + }; + + /// Initialize random data. + void initRandomData(); + + /// @name Connection management + /// Most of these are pretty self-explanatory. + /// @{ + + void addPendingConnection(NetConnection *conn); + NetConnection *findPendingConnection(const NetAddress *address, U32 packetSequence); + void removePendingConnection(NetConnection *conn); + + void sendConnectChallengeRequest(NetConnection *conn); + void handleConnectChallengeRequest(const NetAddress *addr, BitStream *stream); + + void handleConnectChallengeResponse(const NetAddress *address, BitStream *stream); + + void sendConnectRequest(NetConnection *conn); + void handleConnectRequest(const NetAddress *address, BitStream *stream); + + void sendConnectAccept(NetConnection *conn); + void handleConnectAccept(const NetAddress *address, BitStream *stream); + + void sendConnectReject(NetConnection *conn, const char *reason); + void handleConnectReject(const NetAddress *address, BitStream *stream); + + void handleDisconnect(const NetAddress *address, BitStream *stream); + + /// @} + + /// Calculate an MD5 sum representing a connection, and store it into addressDigest. + void computeNetMD5(const NetAddress *address, U32 connectSequence, U32 addressDigest[4]); + +public: + NetInterface(); + + /// Returns whether or not this NetInterface allows connections from remote hosts. + bool doesAllowConnections() { return mAllowConnections; } + + /// Sets whether or not this NetInterface allows connections from remote hosts. + void setAllowsConnections(bool conn) { mAllowConnections = conn; } + + /// Dispatch function for processing all network packets through this NetInterface. + virtual void processPacketReceiveEvent(NetAddress srcAddress, RawData packetData); + + /// Handles all packets that don't fall into the category of connection handshake or game data. + virtual void handleInfoPacket(const NetAddress *address, U8 packetType, BitStream *stream); + + /// Checks all connections marked as client to server for packet sends. + void processClient(); + + /// Checks all connections marked as server to client for packet sends. + void processServer(); + + /// Begins the connection handshaking process for a connection. + void startConnection(NetConnection *conn); + + /// Checks for timeouts on all valid and pending connections. + void checkTimeouts(); + + /// Send a disconnect packet on a connection, along with a reason. + void sendDisconnectPacket(NetConnection *conn, const char *reason); +}; + +/// The global net interface instance. +extern NetInterface *GNet; +#endif diff --git a/Engine/source/sim/netObject.cpp b/Engine/source/sim/netObject.cpp new file mode 100644 index 000000000..cd432d717 --- /dev/null +++ b/Engine/source/sim/netObject.cpp @@ -0,0 +1,462 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/simBase.h" +#include "core/dnet.h" +#include "sim/netConnection.h" +#include "sim/netObject.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" + +IMPLEMENT_CONOBJECT(NetObject); + +// More information can be found in the Torque Manual (CHM) +ConsoleDocClass( NetObject, + "@brief Superclass for all ghostable networked objects.\n\n" + "@ingroup Networking\n"); + +//---------------------------------------------------------------------------- +NetObject *NetObject::mDirtyList = NULL; + +NetObject::NetObject() +{ + // netFlags will clear itself to 0 + mNetIndex = U32(-1); + mFirstObjectRef = NULL; + mPrevDirtyList = NULL; + mNextDirtyList = NULL; + mDirtyMaskBits = 0; +} + +NetObject::~NetObject() +{ + if(mDirtyMaskBits) + { + if(mPrevDirtyList) + mPrevDirtyList->mNextDirtyList = mNextDirtyList; + else + mDirtyList = mNextDirtyList; + if(mNextDirtyList) + mNextDirtyList->mPrevDirtyList = mPrevDirtyList; + } +} + +String NetObject::describeSelf() const +{ + String desc = Parent::describeSelf(); + + if( isClientObject() ) + desc += "|net: client"; + else + desc += "|net: server"; + + return desc; +} + +void NetObject::setMaskBits(U32 orMask) +{ + AssertFatal(orMask != 0, "Invalid net mask bits set."); + AssertFatal(mDirtyMaskBits == 0 || (mPrevDirtyList != NULL || mNextDirtyList != NULL || mDirtyList == this), "Invalid dirty list state."); + if(!mDirtyMaskBits) + { + AssertFatal(mNextDirtyList == NULL && mPrevDirtyList == NULL, "Object with zero mask already in list."); + if(mDirtyList) + { + mNextDirtyList = mDirtyList; + mDirtyList->mPrevDirtyList = this; + } + mDirtyList = this; + } + mDirtyMaskBits |= orMask; + AssertFatal(mDirtyMaskBits == 0 || (mPrevDirtyList != NULL || mNextDirtyList != NULL || mDirtyList == this), "Invalid dirty list state."); +} + +void NetObject::clearMaskBits(U32 orMask) +{ + if(isDeleted()) + return; + if(mDirtyMaskBits) + { + mDirtyMaskBits &= ~orMask; + if(!mDirtyMaskBits) + { + if(mPrevDirtyList) + mPrevDirtyList->mNextDirtyList = mNextDirtyList; + else + mDirtyList = mNextDirtyList; + if(mNextDirtyList) + mNextDirtyList->mPrevDirtyList = mPrevDirtyList; + mNextDirtyList = mPrevDirtyList = NULL; + } + } + + for(GhostInfo *walk = mFirstObjectRef; walk; walk = walk->nextObjectRef) + { + if(walk->updateMask && walk->updateMask == orMask) + { + walk->updateMask = 0; + walk->connection->ghostPushToZero(walk); + } + else + walk->updateMask &= ~orMask; + } +} + +void NetObject::collapseDirtyList() +{ +#ifdef TORQUE_DEBUG + Vector tempV; + for(NetObject *t = mDirtyList; t; t = t->mNextDirtyList) + tempV.push_back(t); +#endif + + for(NetObject *obj = mDirtyList; obj; ) + { + NetObject *next = obj->mNextDirtyList; + U32 dirtyMask = obj->mDirtyMaskBits; + + obj->mNextDirtyList = NULL; + obj->mPrevDirtyList = NULL; + obj->mDirtyMaskBits = 0; + + if(!obj->isDeleted() && dirtyMask) + { + for(GhostInfo *walk = obj->mFirstObjectRef; walk; walk = walk->nextObjectRef) + { + U32 orMask = obj->filterMaskBits(dirtyMask,walk->connection); + if(!walk->updateMask && orMask) + { + walk->updateMask = orMask; + walk->connection->ghostPushNonZero(walk); + } + else + walk->updateMask |= orMask; + } + } + obj = next; + } + mDirtyList = NULL; +#ifdef TORQUE_DEBUG + for(U32 i = 0; i < tempV.size(); i++) + { + AssertFatal(tempV[i]->mNextDirtyList == NULL && tempV[i]->mPrevDirtyList == NULL && tempV[i]->mDirtyMaskBits == 0, "Error in collapse"); + } +#endif +} + +//----------------------------------------------------------------------------- +DefineEngineMethod( NetObject, scopeToClient, void, ( NetConnection* client),, + "@brief Cause the NetObject to be forced as scoped on the specified NetConnection.\n\n" + + "@param client The connection this object will always be scoped to\n\n" + + "@tsexample\n" + "// Called to create new cameras in TorqueScript\n" + "// %this - The active GameConnection\n" + "// %spawnPoint - The spawn point location where we creat the camera\n" + "function GameConnection::spawnCamera(%this, %spawnPoint)\n" + "{\n" + " // If this connection's camera exists\n" + " if(isObject(%this.camera))\n" + " {\n" + " // Add it to the mission group to be cleaned up later\n" + " MissionCleanup.add( %this.camera );\n\n" + " // Force it to scope to the client side\n" + " %this.camera.scopeToClient(%this);\n" + " }\n" + "}\n" + "@endtsexample\n\n" + + "@see clearScopeToClient()\n") +{ + if(!client) + { + Con::errorf(ConsoleLogEntry::General, "NetObject::scopeToClient: Couldn't find connection %s", client); + return; + } + client->objectLocalScopeAlways(object); +} + +//ConsoleMethod(NetObject,scopeToClient,void,3,3,"(NetConnection %client)" +// "Cause the NetObject to be forced as scoped on the specified NetConnection.") +//{ +// TORQUE_UNUSED(argc); +// NetConnection *conn; +// if(!Sim::findObject(argv[2], conn)) +// { +// Con::errorf(ConsoleLogEntry::General, "NetObject::scopeToClient: Couldn't find connection %s", argv[2]); +// return; +// } +// conn->objectLocalScopeAlways(object); +//} + +DefineEngineMethod( NetObject, clearScopeToClient, void, ( NetConnection* client),, + "@brief Undo the effects of a scopeToClient() call.\n\n" + + "@param client The connection to remove this object's scoping from \n\n" + + "@see scopeToClient()\n") +{ + if(!client) + { + Con::errorf(ConsoleLogEntry::General, "NetObject::clearScopeToClient: Couldn't find connection %s", client); + return; + } + client->objectLocalClearAlways(object); +} + +//ConsoleMethod(NetObject,clearScopeToClient,void,3,3,"clearScopeToClient(%client)" +// "Undo the effects of a scopeToClient() call.") +//{ +// TORQUE_UNUSED(argc); +// NetConnection *conn; +// if(!Sim::findObject(argv[2], conn)) +// { +// Con::errorf(ConsoleLogEntry::General, "NetObject::clearScopeToClient: Couldn't find connection %s", argv[2]); +// return; +// } +// conn->objectLocalClearAlways(object); +//} + +DefineEngineMethod( NetObject, setScopeAlways, void, (),, + "@brief Always scope this object on all connections.\n\n" + + "The object is marked as ScopeAlways and is immediately ghosted to " + "all active connections. This function has no effect if the object " + "is not marked as Ghostable.\n\n") +{ + object->setScopeAlways(); +} + +//ConsoleMethod(NetObject,setScopeAlways,void,2,2,"Always scope this object on all connections.") +//{ +// TORQUE_UNUSED(argc); TORQUE_UNUSED(argv); +// object->setScopeAlways(); +//} + +void NetObject::setScopeAlways() +{ + if(mNetFlags.test(Ghostable) && !mNetFlags.test(IsGhost)) + { + mNetFlags.set(ScopeAlways); + + // if it's a ghost always object, add it to the ghost always set + // for ClientReps created later. + + Sim::getGhostAlwaysSet()->addObject(this); + + // add it to all Connections that already exist. + + SimGroup *clientGroup = Sim::getClientGroup(); + SimGroup::iterator i; + for(i = clientGroup->begin(); i != clientGroup->end(); i++) + { + NetConnection *con = (NetConnection *) (*i); + if(con->isGhosting()) + con->objectInScope(this); + } + } +} + +void NetObject::clearScopeAlways() +{ + if(!mNetFlags.test(IsGhost)) + { + mNetFlags.clear(ScopeAlways); + Sim::getGhostAlwaysSet()->removeObject(this); + + // Un ghost this object from all the connections + while(mFirstObjectRef) + mFirstObjectRef->connection->detachObject(mFirstObjectRef); + } +} + +bool NetObject::onAdd() +{ + if(mNetFlags.test(ScopeAlways)) + setScopeAlways(); + + return Parent::onAdd(); +} + +void NetObject::onRemove() +{ + while(mFirstObjectRef) + mFirstObjectRef->connection->detachObject(mFirstObjectRef); + + Parent::onRemove(); +} + +//----------------------------------------------------------------------------- + +F32 NetObject::getUpdatePriority(CameraScopeQuery*, U32, S32 updateSkips) +{ + return F32(updateSkips) * 0.1; +} + +U32 NetObject::packUpdate(NetConnection* conn, U32 mask, BitStream* stream) +{ + return 0; +} + +void NetObject::unpackUpdate(NetConnection*, BitStream*) +{ +} + +void NetObject::onCameraScopeQuery(NetConnection *cr, CameraScopeQuery* /*camInfo*/) +{ + // default behavior - + // ghost everything that is ghostable + + for (SimSetIterator obj(Sim::getRootGroup()); *obj; ++obj) + { + NetObject* nobj = dynamic_cast(*obj); + if (nobj) + { + AssertFatal(!nobj->mNetFlags.test(NetObject::Ghostable) || !nobj->mNetFlags.test(NetObject::IsGhost), + "NetObject::onCameraScopeQuery: object marked both ghostable and as ghost"); + + // Some objects don't ever want to be ghosted + if (!nobj->mNetFlags.test(NetObject::Ghostable)) + continue; + if (!nobj->mNetFlags.test(NetObject::ScopeAlways)) + { + // it's in scope... + cr->objectInScope(nobj); + } + } + } +} + +//----------------------------------------------------------------------------- + +void NetObject::initPersistFields() +{ + Parent::initPersistFields(); +} + +DefineEngineMethod( NetObject, getGhostID, S32, (),, + "@brief Get the ghost index of this object from the server.\n\n" + + "@returns The ghost ID of this NetObject on the server\n" + + "@tsexample\n" + "%ghostID = LocalClientConnection.getGhostId( %serverObject );\n" + "@endtsexample\n\n") +{ + return object->getNetIndex(); +} + +//ConsoleMethod( NetObject, getGhostID, S32, 2, 2, "") +//{ +// return object->getNetIndex(); +//} + +DefineEngineMethod( NetObject, getClientObject, S32, (),, + "@brief Returns a pointer to the client object when on a local connection.\n\n" + + "Short-Circuit-Networking: this is only valid for a local-client / singleplayer situation.\n\n" + + "@returns the SimObject ID of the client object.\n" + + "@tsexample\n" + "// Psuedo-code, some values left out for this example\n" + "%node = new ParticleEmitterNode(){};\n" + "%clientObject = %node.getClientObject();\n" + "if(isObject(%clientObject)\n" + " %clientObject.setTransform(\"0 0 0\");\n\n" + "@endtsexample\n\n" + + "@see @ref local_connections") +{ + NetObject *obj = object->getClientObject(); + if ( obj ) + return obj->getId(); + + return NULL; +} + +//ConsoleMethod( NetObject, getClientObject, S32, 2, 2, "Short-Circuit-Netorking: this is only valid for a local-client / singleplayer situation." ) +//{ +// NetObject *obj = object->getClientObject(); +// if ( obj ) +// return obj->getId(); +// +// return NULL; +//} + +DefineEngineMethod( NetObject, getServerObject, S32, (),, + "@brief Returns a pointer to the client object when on a local connection.\n\n" + + "Short-Circuit-Netorking: this is only valid for a local-client / singleplayer situation.\n\n" + + "@returns The SimObject ID of the server object.\n" + "@tsexample\n" + "// Psuedo-code, some values left out for this example\n" + "%node = new ParticleEmitterNode(){};\n" + "%serverObject = %node.getServerObject();\n" + "if(isObject(%serverObject)\n" + " %serverObject.setTransform(\"0 0 0\");\n\n" + "@endtsexample\n\n" + + "@see @ref local_connections") +{ + NetObject *obj = object->getServerObject(); + if ( obj ) + return obj->getId(); + + return NULL; +} + +//ConsoleMethod( NetObject, getServerObject, S32, 2, 2, "Short-Circuit-Netorking: this is only valid for a local-client / singleplayer situation." ) +//{ +// NetObject *obj = object->getServerObject(); +// if ( obj ) +// return obj->getId(); +// +// return NULL; +//} + +DefineEngineMethod( NetObject, isClientObject, bool, (),, + "@brief Called to check if an object resides on the clientside.\n\n" + "@return True if the object resides on the client, false otherwise.") +{ + return object->isClientObject(); +} + +//ConsoleMethod( NetObject, isClientObject, bool, 2, 2, "Return true for client-side objects." ) +//{ +// return object->isClientObject(); +//} + +DefineEngineMethod( NetObject, isServerObject, bool, (),, + "@brief Checks if an object resides on the server.\n\n" + "@return True if the object resides on the server, false otherwise.") +{ + return object->isServerObject(); +} + +//ConsoleMethod( NetObject, isServerObject, bool, 2, 2, "Return true for client-side objects." ) +//{ +// return object->isServerObject(); +//} diff --git a/Engine/source/sim/netObject.h b/Engine/source/sim/netObject.h new file mode 100644 index 000000000..c6a9ca99e --- /dev/null +++ b/Engine/source/sim/netObject.h @@ -0,0 +1,449 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _NETOBJECT_H_ +#define _NETOBJECT_H_ + +#ifndef _SIMBASE_H_ +#include "console/simBase.h" +#endif +#ifndef _MMATH_H_ +#include "math/mMath.h" +#endif + + +//----------------------------------------------------------------------------- +class NetConnection; +class NetObject; + +//----------------------------------------------------------------------------- + +struct CameraScopeQuery +{ + NetObject *camera; ///< Pointer to the viewing object. + Point3F pos; ///< Position in world space + Point3F orientation; ///< Viewing vector in world space + F32 fov; ///< Viewing angle/2 + F32 sinFov; ///< sin(fov/2); + F32 cosFov; ///< cos(fov/2); + F32 visibleDistance; ///< Visible distance. +}; + +struct GhostInfo; + + +//----------------------------------------------------------------------------- +/// Superclass for ghostable networked objects. +/// +/// @section NetObject_intro Introduction To NetObject And Ghosting +/// +/// One of the most powerful aspects of Torque's networking code is its support +/// for ghosting and prioritized, most-recent-state network updates. The way +/// this works is a bit complex, but it is immensely efficient. Let's run +/// through the steps that the server goes through for each client in this part +/// of Torque's networking: +/// - First, the server determines what objects are in-scope for the client. +/// This is done by calling onCameraScopeQuery() on the object which is +/// considered the "scope" object. This is usually the player object, but +/// it can be something else. (For instance, the current vehicle, or a +/// object we're remote controlling.) +/// - Second, it ghosts them to the client; this is implemented in netGhost.cc. +/// - Finally, it sends updates as needed, by checking the dirty list and packing +/// updates. +/// +/// There several significant advantages to using this networking system: +/// - Efficient network usage, since we only send data that has changed. In addition, +/// since we only care about most-recent data, if a packet is dropped, we don't waste +/// effort trying to deliver stale data. +/// - Cheating protection; since we don't deliver information about game objects which +/// aren't in scope, we dramatically reduce the ability of clients to hack the game and +/// gain a meaningful advantage. (For instance, they can't find out about things behind +/// them, since objects behind them don't fall in scope.) In addition, since ghost IDs are +/// assigned per-client, it's difficult for any sort of co-ordination between cheaters to +/// occur. +/// +/// NetConnection contains the Ghost Manager implementation, which deals with transferring data to +/// the appropriate clients and keeping state in synch. +/// +/// @section NetObject_Implementation An Example Implementation +/// +/// The basis of the ghost implementation in Torque is NetObject. It tracks the dirty flags for the +/// various states that the object trackers, and does some other book-keeping to allow more efficient +/// operation of the networking layer. +/// +/// Using a NetObject is very simple; let's go through a simple example implementation: +/// +/// @code +/// class SimpleNetObject : public NetObject +/// { +/// public: +/// typedef NetObject Parent; +/// DECLARE_CONOBJECT(SimpleNetObject); +/// @endcode +/// +/// Above is the standard boilerplate code for a Torque class. You can find out more about this in SimObject. +/// +/// @code +/// char message1[256]; +/// char message2[256]; +/// enum States { +/// Message1Mask = BIT(0), +/// Message2Mask = BIT(1), +/// }; +/// @endcode +/// +/// For our example, we're having two "states" that we keep track of, message1 and message2. In a real +/// object, we might map our states to health and position, or some other set of fields. You have 32 +/// bits to work with, so it's possible to be very specific when defining states. In general, you +/// should try to use as few states as possible (you never know when you'll need to expand your object's +/// functionality!), and in fact, most of your fields will end up changing all at once, so it's not worth +/// it to be too fine-grained. (As an example, position and velocity on Player are controlled by the same +/// bit, as one rarely changes without the other changing, too.) +/// +/// @code +/// SimpleNetObject() +/// { +/// // in order for an object to be considered by the network system, +/// // the Ghostable net flag must be set. +/// // the ScopeAlways flag indicates that the object is always scoped +/// // on all active connections. +/// mNetFlags.set(ScopeAlways | Ghostable); +/// dStrcpy(message1, "Hello World 1!"); +/// dStrcpy(message2, "Hello World 2!"); +/// } +/// @endcode +/// +/// Here is the constructor. Here, you see that we initialize our net flags to show that +/// we should always be scoped, and that we're to be taken into consideration for ghosting. We +/// also provide some initial values for the message fields. +/// +/// @code +/// U32 packUpdate(NetConnection *, U32 mask, BitStream *stream) +/// { +/// // check which states need to be updated, and update them +/// if(stream->writeFlag(mask & Message1Mask)) +/// stream->writeString(message1); +/// if(stream->writeFlag(mask & Message2Mask)) +/// stream->writeString(message2); +/// +/// // the return value from packUpdate can set which states still +/// // need to be updated for this object. +/// return 0; +/// } +/// @endcode +/// +/// Here's half of the meat of the networking code, the packUpdate() function. (The other half, unpackUpdate(), +/// we'll get to in a second.) The comments in the code pretty much explain everything, however, notice that the +/// code follows a pattern of if(writeFlag(mask & StateMask)) { ... write data ... }. The packUpdate()/unpackUpdate() +/// functions are responsible for reading and writing the dirty bits to the bitstream by themselves. +/// +/// @code +/// void unpackUpdate(NetConnection *, BitStream *stream) +/// { +/// // the unpackUpdate function must be symmetrical to packUpdate +/// if(stream->readFlag()) +/// { +/// stream->readString(message1); +/// Con::printf("Got message1: %s", message1); +/// } +/// if(stream->readFlag()) +/// { +/// stream->readString(message2); +/// Con::printf("Got message2: %s", message2); +/// } +/// } +/// @endcode +/// +/// The other half of the networking code in any NetObject, unpackUpdate(). In our simple example, all that +/// the code does is print the new messages to the console; however, in a more advanced object, you might +/// trigger animations, update complex object properties, or even spawn new objects, based on what packet +/// data you unpack. +/// +/// @code +/// void setMessage1(const char *msg) +/// { +/// setMaskBits(Message1Mask); +/// dStrcpy(message1, msg); +/// } +/// void setMessage2(const char *msg) +/// { +/// setMaskBits(Message2Mask); +/// dStrcpy(message2, msg); +/// } +/// @endcode +/// +/// Here are the accessors for the two properties. It is good to encapsulate your state +/// variables, so that you don't have to remember to make a call to setMaskBits every time you change +/// anything; the accessors can do it for you. In a more complex object, you might need to set +/// multiple mask bits when you change something; this can be done using the | operator, for instance, +/// setMaskBits( Message1Mask | Message2Mask ); if you changed both messages. +/// +/// @code +/// IMPLEMENT_CO_NETOBJECT_V1(SimpleNetObject); +/// +/// ConsoleMethod(SimpleNetObject, setMessage1, void, 3, 3, "(string msg) Set message 1.") +/// { +/// object->setMessage1(argv[2]); +/// } +/// +/// ConsoleMethod(SimpleNetObject, setMessage2, void, 3, 3, "(string msg) Set message 2.") +/// { +/// object->setMessage2(argv[2]); +/// } +/// @endcode +/// +/// Finally, we use the NetObject implementation macro, IMPLEMENT_CO_NETOBJECT_V1(), to implement our +/// NetObject. It is important that we use this, as it makes Torque perform certain initialization tasks +/// that allow us to send the object over the network. IMPLEMENT_CONOBJECT() doesn't perform these tasks, see +/// the documentation on AbstractClassRep for more details. +/// +/// @nosubgrouping +class NetObject: public SimObject +{ + // The Ghost Manager needs read/write access + friend class NetConnection; + friend struct GhostInfo; + friend class ProcessList; + + // Not the best way to do this, but the event needs access to mNetFlags + friend class GhostAlwaysObjectEvent; + +private: + typedef SimObject Parent; + + /// Mask indicating which states are dirty and need to be retransmitted on this + /// object. + U32 mDirtyMaskBits; + + /// @name Dirty List + /// + /// Whenever a NetObject becomes "dirty", we add it to the dirty list. + /// We also remove ourselves on the destructor. + /// + /// This is done so that when we want to send updates (in NetConnection), + /// it's very fast to find the objects that need to be updated. + /// @{ + + /// Static pointer to the head of the dirty NetObject list. + static NetObject *mDirtyList; + + /// Next item in the dirty list... + NetObject *mPrevDirtyList; + + /// Previous item in the dirty list... + NetObject *mNextDirtyList; + + /// @} +protected: + + /// Pointer to the server object on a local connection. + /// @see getServerObject + SimObjectPtr mServerObject; + + /// Pointer to the client object on a local connection. + /// @see getClientObject + SimObjectPtr mClientObject; + + enum NetFlags + { + IsGhost = BIT(1), ///< This is a ghost. + ScopeAlways = BIT(6), ///< Object always ghosts to clients. + ScopeLocal = BIT(7), ///< Ghost only to local client. + Ghostable = BIT(8), ///< Set if this object CAN ghost. + + MaxNetFlagBit = 15 + }; + + BitSet32 mNetFlags; ///< Flag values from NetFlags + U32 mNetIndex; ///< The index of this ghost in the GhostManager on the server. + + GhostInfo *mFirstObjectRef; ///< Head of a linked list storing GhostInfos referencing this NetObject. + +public: + NetObject(); + ~NetObject(); + + virtual String describeSelf() const; + + /// @name Miscellaneous + /// @{ + DECLARE_CONOBJECT(NetObject); + static void initPersistFields(); + bool onAdd(); + void onRemove(); + /// @} + + static void collapseDirtyList(); + + /// Used to mark a bit as dirty; ie, that its corresponding set of fields need to be transmitted next update. + /// + /// @param orMask Bit(s) to set + virtual void setMaskBits(U32 orMask); + + /// Clear the specified bits from the dirty mask. + /// + /// @param orMask Bits to clear + virtual void clearMaskBits(U32 orMask); + virtual U32 filterMaskBits(U32 mask, NetConnection * connection) { return mask; } + + /// Scope the object to all connections. + /// + /// The object is marked as ScopeAlways and is immediately ghosted to + /// all active connections. This function has no effect if the object + /// is not marked as Ghostable. + void setScopeAlways(); + + /// Stop scoping the object to all connections. + /// + /// The object's ScopeAlways flag is cleared and the object is removed from + /// all current active connections. + void clearScopeAlways(); + + /// This returns a value which is used to prioritize which objects need to be updated. + /// + /// In NetObject, our returned priority is 0.1 * updateSkips, so that less recently + /// updated objects are more likely to be updated. + /// + /// In subclasses, this can be adjusted. For instance, ShapeBase provides priority + /// based on proximity to the camera. + /// + /// @param focusObject Information from a previous call to onCameraScopeQuery. + /// @param updateMask Current update mask. + /// @param updateSkips Number of ticks we haven't been updated for. + /// @returns A floating point value indicating priority. These are typically < 5.0. + virtual F32 getUpdatePriority(CameraScopeQuery *focusObject, U32 updateMask, S32 updateSkips); + + /// Instructs this object to pack its state for transfer over the network. + /// + /// @param conn Net connection being used + /// @param mask Mask indicating fields to transmit. + /// @param stream Bitstream to pack data to + /// + /// @returns Any bits which were not dealt with. The value is stored by the networking + /// system. Don't set bits you weren't passed. + virtual U32 packUpdate(NetConnection * conn, U32 mask, BitStream *stream); + + /// Instructs this object to read state data previously packed with packUpdate. + /// + /// @param conn Net connection being used + /// @param stream stream to read from + virtual void unpackUpdate(NetConnection * conn, BitStream *stream); + + /// Queries the object about information used to determine scope. + /// + /// Something that is 'in scope' is somehow interesting to the client. + /// + /// If we are a NetConnection's scope object, it calls this method to determine + /// how things should be scoped; basically, we tell it our field of view with camInfo, + /// and have the opportunity to manually mark items as "in scope" as we see fit. + /// + /// By default, we just mark all ghostable objects as in scope. + /// + /// @param cr Net connection requesting scope information. + /// @param camInfo Information about what this object can see. + virtual void onCameraScopeQuery(NetConnection *cr, CameraScopeQuery *camInfo); + + /// Get the ghost index of this object. + U32 getNetIndex() { return mNetIndex; } + + bool isServerObject() const; ///< Is this a server object? + bool isClientObject() const; ///< Is this a client object? + + bool isGhost() const; ///< Is this is a ghost? + bool isScopeLocal() const; ///< Should this object only be visible to the client which created it? + bool isScopeable() const; ///< Is this object subject to scoping? + bool isGhostable() const; ///< Is this object ghostable? + bool isGhostAlways() const; ///< Should this object always be ghosted? + + + /// @name Short-Circuited Networking + /// + /// When we are running with client and server on the same system (which can happen be either + /// when we are doing a single player game, or if we're hosting a multiplayer game and having + /// someone playing on the same instance), we can do some short circuited code to enhance + /// performance. + /// + /// These variables are used to make it simpler; if we are running in short-circuited mode, + /// the ghosted client gets the server object while the server gets the client object. + /// + /// @note "Premature optimization is the root of all evil" - Donald Knuth. The current codebase + /// uses this feature in three small places, mostly for non-speed-related purposes. + /// + /// @{ + + /// Returns a pointer to the server object when on a local connection. + NetObject* getServerObject() const { return mServerObject; } + + /// Returns a pointer to the client object when on a local connection. + NetObject* getClientObject() const { return mClientObject; } + + /// Template form for the callers convenience. + template < class T > + static T* getServerObject( T *netObj ) { return static_cast( netObj->getServerObject() ); } + + /// Template form for the callers convenience. + template < class T > + static T* getClientObject( T *netObj ) { return static_cast( netObj->getClientObject() ); } + + /// @} +}; + +//----------------------------------------------------------------------------- + +inline bool NetObject::isGhost() const +{ + return mNetFlags.test(IsGhost); +} + +inline bool NetObject::isClientObject() const +{ + return mNetFlags.test(IsGhost); +} + +inline bool NetObject::isServerObject() const +{ + return !mNetFlags.test(IsGhost); +} + +inline bool NetObject::isScopeLocal() const +{ + return mNetFlags.test(ScopeLocal); +} + +inline bool NetObject::isScopeable() const +{ + return mNetFlags.test(Ghostable) && !mNetFlags.test(ScopeAlways); +} + +inline bool NetObject::isGhostable() const +{ + return mNetFlags.test(Ghostable); +} + +inline bool NetObject::isGhostAlways() const +{ + AssertFatal(mNetFlags.test(Ghostable) || mNetFlags.test(ScopeAlways) == false, + "That's strange, a ScopeAlways non-ghostable object? Something wrong here"); + return mNetFlags.test(Ghostable) && mNetFlags.test(ScopeAlways); +} + +#endif diff --git a/Engine/source/sim/netStringTable.cpp b/Engine/source/sim/netStringTable.cpp new file mode 100644 index 000000000..880c72e1e --- /dev/null +++ b/Engine/source/sim/netStringTable.cpp @@ -0,0 +1,281 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/dnet.h" +#include "core/strings/stringFunctions.h" +#include "core/stringTable.h" +#include "console/engineAPI.h" +#include "sim/netStringTable.h" + + +NetStringTable *gNetStringTable = NULL; + +NetStringTable::NetStringTable() +{ + firstFree = 1; + firstValid = 1; + + table = (Entry *) dMalloc(sizeof(Entry) * InitialSize); + size = InitialSize; + for(U32 i = 0; i < InitialSize; i++) + { + table[i].next = i + 1; + table[i].refCount = 0; + table[i].scriptRefCount = 0; + } + table[InitialSize-1].next = InvalidEntry; + for(U32 j = 0; j < HashTableSize; j++) + hashTable[j] = 0; + allocator = new DataChunker(DataChunkerSize); +} + +NetStringTable::~NetStringTable() +{ + delete allocator; + dFree( table ); +} + +void NetStringTable::incStringRef(U32 id) +{ + AssertFatal(table[id].refCount != 0 || table[id].scriptRefCount != 0 , "Cannot inc ref count from zero."); + table[id].refCount++; +} + +void NetStringTable::incStringRefScript(U32 id) +{ + AssertFatal(table[id].refCount != 0 || table[id].scriptRefCount != 0 , "Cannot inc ref count from zero."); + table[id].scriptRefCount++; +} + +U32 NetStringTable::addString(const char *string) +{ + U32 hash = _StringTable::hashString(string); + U32 bucket = hash % HashTableSize; + for(U32 walk = hashTable[bucket];walk; walk = table[walk].next) + { + if(!dStrcmp(table[walk].string, string)) + { + table[walk].refCount++; + return walk; + } + } + U32 e = firstFree; + firstFree = table[e].next; + if(firstFree == InvalidEntry) + { + // in this case, we should expand the table for next time... + U32 newSize = size * 2; + table = (Entry *) dRealloc(table, newSize * sizeof(Entry)); + for(U32 i = size; i < newSize; i++) + { + table[i].next = i + 1; + table[i].refCount = 0; + table[i].scriptRefCount = 0; + } + firstFree = size; + table[newSize - 1].next = InvalidEntry; + size = newSize; + } + table[e].refCount++; + table[e].string = (char *) allocator->alloc(dStrlen(string) + 1); + dStrcpy(table[e].string, string); + table[e].next = hashTable[bucket]; + hashTable[bucket] = e; + table[e].link = firstValid; + table[firstValid].prevLink = e; + firstValid = e; + table[e].prevLink = 0; + return e; +} + +U32 GameAddTaggedString(const char *string) +{ + return gNetStringTable->addString(string); +} + +const char *NetStringTable::lookupString(U32 id) +{ + if(table[id].refCount == 0 && table[id].scriptRefCount == 0) + return NULL; + return table[id].string; +} + +void NetStringTable::removeString(U32 id, bool script) +{ + if(!script) + { + AssertFatal(table[id].refCount != 0, "Error, ref count is already 0!!"); + if(--table[id].refCount) + return; + if(table[id].scriptRefCount) + return; + } + else + { + // If both ref counts are already 0, this id is not valid. Ignore + // the remove + if (table[id].scriptRefCount == 0 && table[id].refCount == 0) + return; + + if(table[id].scriptRefCount == 0 && table[id].refCount) + { + Con::errorf("removeTaggedString failed! Ref count is already 0 for string: %s", table[id].string); + return; + } + if(--table[id].scriptRefCount) + return; + if(table[id].refCount) + return; + } + // unlink first: + U32 prev = table[id].prevLink; + U32 next = table[id].link; + if(next) + table[next].prevLink = prev; + if(prev) + table[prev].link = next; + else + firstValid = next; + // remove it from the hash table + U32 hash = _StringTable::hashString(table[id].string); + U32 bucket = hash % HashTableSize; + for(U32 *walk = &hashTable[bucket];*walk; walk = &table[*walk].next) + { + if(*walk == id) + { + *walk = table[id].next; + break; + } + } + table[id].next = firstFree; + firstFree = id; +} + +void NetStringTable::repack() +{ + DataChunker *newAllocator = new DataChunker(DataChunkerSize); + for(U32 walk = firstValid; walk; walk = table[walk].link) + { + const char *prevStr = table[walk].string; + + + table[walk].string = (char *) newAllocator->alloc(dStrlen(prevStr) + 1); + dStrcpy(table[walk].string, prevStr); + } + delete allocator; + allocator = newAllocator; +} + +void NetStringTable::create() +{ + AssertFatal(gNetStringTable == NULL, "Error, calling NetStringTable::create twice."); + gNetStringTable = new NetStringTable(); +} + +void NetStringTable::destroy() +{ + AssertFatal(gNetStringTable != NULL, "Error, not calling NetStringTable::create."); + delete gNetStringTable; + gNetStringTable = NULL; +} + +void NetStringTable::expandString(NetStringHandle &inString, char *buf, U32 bufSize, U32 argc, const char **argv) +{ + buf[0] = StringTagPrefixByte; + dSprintf(buf + 1, bufSize - 1, "%d ", inString.getIndex()); + + const char *string = inString.getString(); + if (string != NULL) { + U32 index = dStrlen(buf); + while(index < bufSize) + { + char c = *string++; + if(c == '%') + { + c = *string++; + if(c >= '1' && c <= '9') + { + U32 strIndex = c - '1'; + if(strIndex >= argc) + continue; + // start copying out of arg index + const char *copy = argv[strIndex]; + // skip past any tags: + if(*copy == StringTagPrefixByte) + { + while(*copy && *copy != ' ') + copy++; + if(*copy) + copy++; + } + + while(*copy && index < bufSize) + buf[index++] = *copy++; + continue; + } + } + buf[index++] = c; + if(!c) + break; + } + buf[bufSize - 1] = 0; + } else { + dStrcat(buf, ""); + } +} + +#ifdef TORQUE_DEBUG + +void NetStringTable::dumpToConsole() +{ + U32 count = 0; + S32 maxIndex = -1; + for ( U32 i = 0; i < size; i++ ) + { + if ( table[i].refCount > 0 || table[i].scriptRefCount > 0) + { + Con::printf( "%d: \"%c%s%c\" REF: %d", i, 0x10, table[i].string, 0x11, table[i].refCount ); + if ( maxIndex == -1 || table[i].refCount > table[maxIndex].refCount ) + maxIndex = i; + count++; + } + } + Con::printf( ">> STRINGS: %d MAX REF COUNT: %d \"%c%s%c\" <<", + count, + ( maxIndex == -1 ) ? 0 : table[maxIndex].refCount, + 0x10, + ( maxIndex == -1 ) ? "" : table[maxIndex].string, + 0x11 ); +} + +DefineEngineFunction( dumpNetStringTable, void, (),, + "@brief Dump the current contents of the networked string table to the console.\n\n" + "The results are returned in three columns. The first column is the network string ID. " + "The second column is the string itself. The third column is the reference count to the " + "network string.\n\n" + "@note This function is available only in debug builds.\n\n" + "@ingroup Networking" ) +{ + gNetStringTable->dumpToConsole(); +} + +#endif // DEBUG diff --git a/Engine/source/sim/netStringTable.h b/Engine/source/sim/netStringTable.h new file mode 100644 index 000000000..629d691bb --- /dev/null +++ b/Engine/source/sim/netStringTable.h @@ -0,0 +1,161 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _NETSTRINGTABLE_H_ +#define _NETSTRINGTABLE_H_ + +#ifndef _DATACHUNKER_H_ +#include "core/dataChunker.h" +#endif +#ifndef _CONSOLE_H_ +#include "console/console.h" +#endif + +class NetConnection; + +class NetStringHandle; +extern U32 GameAddTaggedString(const char *string); + +class NetStringTable +{ + friend class NetStringHandle; + friend U32 GameAddTaggedString(const char *string); + +#ifdef TORQUE_DEBUG_NET + friend class RemoteCommandEvent; +#endif + + enum Constants { + InitialSize = 16, + InvalidEntry = 0xFFFFFFFF, + HashTableSize = 2128, + DataChunkerSize = 65536 + }; + struct Entry + { + char *string; + U32 refCount; + U32 scriptRefCount; + U32 next; + U32 link; + U32 prevLink; + U32 seq; + }; + U32 size; + U32 firstFree; + U32 firstValid; + U32 sequenceCount; + + Entry *table; + U32 hashTable[HashTableSize]; + DataChunker *allocator; + + NetStringTable(); + ~NetStringTable(); + + U32 addString(const char *string); + +// XA: Moved this ones to public to avoid using the friend_ConsoleMethod hack. +public: + const char *lookupString(U32 id); + void removeString(U32 id, bool script = false); + void incStringRefScript(U32 id); + +private: + void incStringRef(U32 id); + + void repack(); +public: + static void create(); + static void destroy(); + + static void expandString(NetStringHandle &string, char *buf, U32 bufSize, U32 argc, const char **argv); + +#if defined(TORQUE_DEBUG) + void dumpToConsole(); +#endif // DEBUG +}; + +extern NetStringTable *gNetStringTable; + +// This class represents what is known as a "tagged string" in script. +// It is essentially a networked version of the string table. The full string +// is only transmitted once; then future references only need to transmit +// the id. + +class NetStringHandle +{ + U32 index; +public: + NetStringHandle() { index = 0; } + NetStringHandle(const NetStringHandle &string) { + index = string.index; + if(index) + gNetStringTable->incStringRef(index); + } + NetStringHandle(const char *string) { + index = gNetStringTable->addString(string); + } + NetStringHandle(U32 initIndex) + { + index = initIndex; + if(index) + gNetStringTable->incStringRef(index); + } + ~NetStringHandle() + { + if(index) + gNetStringTable->removeString(index); + } + + void setFromIndex(U32 newIndex) + { + if(index) + gNetStringTable->removeString(index); + index = newIndex; + } + + bool operator==(const NetStringHandle &s) const { return index == s.index; } + bool operator!=(const NetStringHandle &s) const { return index != s.index; } + + NetStringHandle &operator=(const NetStringHandle &s) + { + if(index) + gNetStringTable->removeString(index); + index = s.index; + if(index) + gNetStringTable->incStringRef(index); + return *this; + } + const char *getString() const + { + if(index) + return gNetStringTable->lookupString(index); + else + return NULL; + } + bool isNull() const { return index == 0; } + bool isValidString() const { return index != 0; } + U32 getIndex() const { return index; } +}; + +#endif diff --git a/Engine/source/terrain/glsl/terrFeatureGLSL.cpp b/Engine/source/terrain/glsl/terrFeatureGLSL.cpp new file mode 100644 index 000000000..c23d4d85d --- /dev/null +++ b/Engine/source/terrain/glsl/terrFeatureGLSL.cpp @@ -0,0 +1,728 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "terrain/glsl/terrFeatureGLSL.h" + +#include "terrain/terrFeatureTypes.h" +#include "materials/materialFeatureTypes.h" +#include "materials/materialFeatureData.h" +#include "gfx/gfxDevice.h" +#include "shaderGen/langElement.h" +#include "shaderGen/shaderOp.h" +#include "shaderGen/featureMgr.h" +#include "core/module.h" + + +MODULE_BEGIN( TerrainFeatGLSL ) + + MODULE_INIT_AFTER( ShaderGenFeatureMgr ) + + MODULE_INIT + { + FEATUREMGR->registerFeature( MFT_TerrainBaseMap, new TerrainBaseMapFeatGLSL ); + FEATUREMGR->registerFeature( MFT_TerrainParallaxMap, new TerrainParallaxMapFeatGLSL ); + FEATUREMGR->registerFeature( MFT_TerrainDetailMap, new TerrainDetailMapFeatGLSL ); + FEATUREMGR->registerFeature( MFT_TerrainNormalMap, new TerrainNormalMapFeatGLSL ); + FEATUREMGR->registerFeature( MFT_TerrainLightMap, new TerrainLightMapFeatGLSL ); + FEATUREMGR->registerFeature( MFT_TerrainSideProject, new NamedFeatureGLSL( "Terrain Side Projection" ) ); + FEATUREMGR->registerFeature( MFT_TerrainAdditive, new TerrainAdditiveFeatGLSL ); + } + +MODULE_END; + + +Var* TerrainFeatGLSL::_getUniformVar( const char *name, const char *type, ConstantSortPosition csp ) +{ + Var *theVar = (Var*)LangElement::find( name ); + if ( !theVar ) + { + theVar = new Var; + theVar->setType( type ); + theVar->setName( name ); + theVar->uniform = true; + theVar->constSortPos = csp; + } + + return theVar; +} + +Var* TerrainFeatGLSL::_getInDetailCoord( Vector &componentList ) +{ + String name( String::ToString( "outDetCoord%d", getProcessIndex() ) ); + Var *inDet = (Var*)LangElement::find( name ); + + if ( !inDet ) + { + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + inDet = connectComp->getElement( RT_TEXCOORD ); + inDet->setName( name ); + inDet->setType( "vec4" ); + inDet->mapsToSampler = true; + } + + return inDet; +} + +Var* TerrainFeatGLSL::_getNormalMapTex() +{ + String name( String::ToString( "normalMap%d", getProcessIndex() ) ); + Var *normalMap = (Var*)LangElement::find( name ); + + if ( !normalMap ) + { + normalMap = new Var; + normalMap->setType( "sampler2D" ); + normalMap->setName( name ); + normalMap->uniform = true; + normalMap->sampler = true; + normalMap->constNum = Var::getTexUnitNum(); + } + + return normalMap; +} + +Var* TerrainFeatGLSL::_getDetailIdStrengthParallax() +{ + String name( String::ToString( "detailIdStrengthParallax%d", getProcessIndex() ) ); + + Var *detailInfo = (Var*)LangElement::find( name ); + if ( !detailInfo ) + { + detailInfo = new Var; + detailInfo->setType( "vec3" ); + detailInfo->setName( name ); + detailInfo->uniform = true; + detailInfo->constSortPos = cspPotentialPrimitive; + } + + return detailInfo; +} + +void TerrainBaseMapFeatGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + output = meta; + + // Generate the incoming texture var. + Var *inTex; + { + Var *inPos = (Var*)LangElement::find( "inPosition" ); + if ( !inPos ) + inPos = (Var*)LangElement::find( "position" ); + + inTex = new Var( "texCoord", "vec3" ); + + Var *oneOverTerrainSize = _getUniformVar( "oneOverTerrainSize", "float", cspPass ); + + // NOTE: The y coord here should be negative to have + // the texture maps not end up flipped which also caused + // normal and parallax mapping to be incorrect. + // + // This mistake early in development means that the layer + // id bilinear blend depends on it being that way. + // + // So instead i fixed this by flipping the base and detail + // coord y scale to compensate when rendering. + // + meta->addStatement( new GenOp( " @ = @.xyz * vec3( @, @, -@ );\r\n", + new DecOp( inTex ), inPos, oneOverTerrainSize, oneOverTerrainSize, oneOverTerrainSize ) ); + } + + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + // Pass the texture coord to the pixel shader. + Var *outTex = connectComp->getElement( RT_TEXCOORD ); + outTex->setName( "outTexCoord" ); + outTex->setType( "vec3" ); + outTex->mapsToSampler = true; + meta->addStatement( new GenOp( " @.xy = @.xy;\r\n", outTex, inTex ) ); + + // If this shader has a side projected layer then we + // pass the dot product between the +Y and the normal + // thru outTexCoord.z for use in blending the textures. + if ( fd.features.hasFeature( MFT_TerrainSideProject ) ) + { + Var *inNormal = (Var*)LangElement::find( "normal" ); + meta->addStatement( + new GenOp( " @.z = pow( abs( dot( normalize( vec3( @.x, @.y, 0.0 ) ), vec3( 0, 1, 0 ) ) ), 10.0 );\r\n", + outTex, inNormal, inNormal ) ); + } + else + meta->addStatement( new GenOp( " @.z = 0;\r\n", outTex ) ); + + // HACK: This is sort of lazy... we generate the tanget + // vector here so that we're sure it exists in the parallax + // and normal features which will expect "T" to exist. + // + // If this shader doesn't use it the shader compiler will + // optimize away this code. + // + Var *inTangentZ = getVertTexCoord( "tcTangentZ" ); + Var *inTanget = new Var( "T", "vec3" ); + Var *squareSize = _getUniformVar( "squareSize", "float", cspPass ); + meta->addStatement( new GenOp( " @ = normalize( vec3( @, 0.0, @ ) );\r\n", + new DecOp( inTanget ), squareSize, inTangentZ ) ); +} + +void TerrainBaseMapFeatGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // grab connector texcoord register + Var *texCoord = getInTexCoord( "outTexCoord", "vec3", true, componentList ); + + // We do nothing more if this is a prepass. + if ( fd.features.hasFeature( MFT_PrePassConditioner ) ) + return; + + // create texture var + Var *diffuseMap = new Var; + diffuseMap->setType( "sampler2D" ); + diffuseMap->setName( "baseTexMap" ); + diffuseMap->uniform = true; + diffuseMap->sampler = true; + diffuseMap->constNum = Var::getTexUnitNum(); // used as texture unit num here + + MultiLine *meta = new MultiLine; + + Var *baseColor = new Var; + baseColor->setType( "vec4" ); + baseColor->setName( "baseColor" ); + meta->addStatement( new GenOp( " @ = texture2D( @, @.xy );\r\n", new DecOp( baseColor ), diffuseMap, texCoord ) ); + meta->addStatement( new GenOp( " @;\r\n", assignColor( baseColor, Material::Mul ) ) ); + + output = meta; +} + +ShaderFeature::Resources TerrainBaseMapFeatGLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + res.numTexReg = 1; + + // We only sample from the base map during a diffuse pass. + if ( !fd.features.hasFeature( MFT_PrePassConditioner ) ) + res.numTex = 1; + + return res; +} + +TerrainDetailMapFeatGLSL::TerrainDetailMapFeatGLSL() + : mTerrainDep( "shaders/common/terrain/terrain.glsl" ) +{ + addDependency( &mTerrainDep ); +} + +void TerrainDetailMapFeatGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + const U32 detailIndex = getProcessIndex(); + + + // If this is a prepass and we don't have a + // matching normal map... we have nothing to do. + if ( fd.features.hasFeature( MFT_PrePassConditioner ) && + !fd.features.hasFeature( MFT_TerrainNormalMap, detailIndex ) ) + return; + + // Grab incoming texture coords... the base map feature + // made sure this was created. + Var *inTex = (Var*)LangElement::find( "texCoord" ); + AssertFatal( inTex, "The texture coord is missing!" ); + + // Grab the input position. + Var *inPos = (Var*)LangElement::find( "inPosition" ); + if ( !inPos ) + inPos = (Var*)LangElement::find( "position" ); + + // Get the object space eye position. + Var *eyePos = _getUniformVar( "eyePos", "vec3", cspPotentialPrimitive ); + + MultiLine *meta = new MultiLine; + + // Get the distance from the eye to this vertex. + Var *dist = (Var*)LangElement::find( "dist" ); + if ( !dist ) + { + dist = new Var; + dist->setType( "float" ); + dist->setName( "dist" ); + + meta->addStatement( new GenOp( " @ = distance( @.xyz, @ );\r\n", + new DecOp( dist ), inPos, eyePos ) ); + } + + // grab connector texcoord register + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outTex = connectComp->getElement( RT_TEXCOORD ); + outTex->setName( String::ToString( "outDetCoord%d", detailIndex ) ); + outTex->setType( "vec4" ); + outTex->mapsToSampler = true; + + // Get the detail scale and fade info. + Var *detScaleAndFade = new Var; + detScaleAndFade->setType( "vec4" ); + detScaleAndFade->setName( String::ToString( "detailScaleAndFade%d", detailIndex ) ); + detScaleAndFade->uniform = true; + detScaleAndFade->constSortPos = cspPotentialPrimitive; + + // Setup the detail coord. + // + // NOTE: You see here we scale the texture coord by 'xyx' + // to generate the detail coord. This y is here because + // its scale is flipped to correct for the non negative y + // in texCoord. + // + // See TerrainBaseMapFeatHLSL::processVert(). + // + meta->addStatement( new GenOp( " @.xyz = @ * @.xyx;\r\n", outTex, inTex, detScaleAndFade ) ); + + // And sneak the detail fade thru the w detailCoord. + meta->addStatement( new GenOp( " @.w = clamp( ( @.z - @ ) * @.w, 0.0, 1.0 );\r\n", + outTex, detScaleAndFade, dist, detScaleAndFade ) ); + + output = meta; +} + +void TerrainDetailMapFeatGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + const U32 detailIndex = getProcessIndex(); + + // If this is a prepass and we don't have a + // matching normal map... we have nothing to do. + if ( fd.features.hasFeature( MFT_PrePassConditioner ) && + !fd.features.hasFeature( MFT_TerrainNormalMap, detailIndex ) ) + return; + + Var *inTex = getVertTexCoord( "outTexCoord" ); + + MultiLine *meta = new MultiLine; + + // Get the layer samples. + Var *layerSample = (Var*)LangElement::find( "layerSample" ); + if ( !layerSample ) + { + layerSample = new Var; + layerSample->setType( "vec4" ); + layerSample->setName( "layerSample" ); + + // Get the layer texture var + Var *layerTex = new Var; + layerTex->setType( "sampler2D" ); + layerTex->setName( "layerTex" ); + layerTex->uniform = true; + layerTex->sampler = true; + layerTex->constNum = Var::getTexUnitNum(); + + // Read the layer texture to get the samples. + meta->addStatement( new GenOp( " @ = round( texture2D( @, @.xy ) * 255.0f );\r\n", + new DecOp( layerSample ), layerTex, inTex ) ); + } + + Var *layerSize = (Var*)LangElement::find( "layerSize" ); + if ( !layerSize ) + { + layerSize = new Var; + layerSize->setType( "float" ); + layerSize->setName( "layerSize" ); + layerSize->uniform = true; + layerSize->constSortPos = cspPass; + } + + // Grab the incoming detail coord. + Var *inDet = _getInDetailCoord( componentList ); + + // Get the detail id. + Var *detailInfo = _getDetailIdStrengthParallax(); + + // Create the detail blend var. + Var *detailBlend = new Var; + detailBlend->setType( "float" ); + detailBlend->setName( String::ToString( "detailBlend%d", detailIndex ) ); + + // Calculate the blend for this detail texture. + meta->addStatement( new GenOp( " @ = calcBlend( @.x, @.xy, @, @ );\r\n", + new DecOp( detailBlend ), detailInfo, inTex, layerSize, layerSample ) ); + + // Get a var and accumulate the blend amount. + Var *blendTotal = (Var*)LangElement::find( "blendTotal" ); + if ( !blendTotal ) + { + blendTotal = new Var; + blendTotal->setName( "blendTotal" ); + blendTotal->setType( "float" ); + meta->addStatement( new GenOp( " @ = 0.0;\r\n", new DecOp( blendTotal ) ) ); + } + + // Add to the blend total. + meta->addStatement( new GenOp( " @ += @;\r\n", blendTotal, detailBlend ) ); + //meta->addStatement( new GenOp( " @ += @ * @.y * @.w;\r\n", + //blendTotal, detailBlend, detailInfo, inDet ) ); + + // Nothing more to do for a detail texture in prepass. + if ( fd.features.hasFeature( MFT_PrePassConditioner ) ) + { + output = meta; + return; + } + + Var *detailColor = (Var*)LangElement::find( "detailColor" ); + if ( !detailColor ) + { + detailColor = new Var; + detailColor->setType( "vec4" ); + detailColor->setName( "detailColor" ); + meta->addStatement( new GenOp( " @;\r\n", new DecOp( detailColor ) ) ); + } + + // Get the detail texture. + Var *detailMap = new Var; + detailMap->setType( "sampler2D" ); + detailMap->setName( String::ToString( "detailMap%d", detailIndex ) ); + detailMap->uniform = true; + detailMap->sampler = true; + detailMap->constNum = Var::getTexUnitNum(); // used as texture unit num here + + // If we're using SM 3.0 then take advantage of + // dynamic branching to skip layers per-pixel. + + if ( GFX->getPixelShaderVersion() >= 3.0f ) + meta->addStatement( new GenOp( " if ( @ > 0.0f )\r\n", detailBlend ) ); + + meta->addStatement( new GenOp( " {\r\n" ) ); + + // Note that we're doing the standard greyscale detail + // map technique here which can darken and lighten the + // diffuse texture. + // + // We take two color samples and lerp between them for + // side projection layers... else a single sample. + // + if ( fd.features.hasFeature( MFT_TerrainSideProject, detailIndex ) ) + { + meta->addStatement( new GenOp( " @ = ( mix( texture2D( @, @.yz ), texture2D( @, @.xz ), @.z ) * 2.0 ) - 1.0;\r\n", + detailColor, detailMap, inDet, detailMap, inDet, inTex ) ); + } + else + { + meta->addStatement( new GenOp( " @ = ( texture2D( @, @.xy ) * 2.0 ) - 1.0;\r\n", + detailColor, detailMap, inDet ) ); + } + + meta->addStatement( new GenOp( " @ *= @.y * @.w;\r\n", + detailColor, detailInfo, inDet ) ); + + Var *baseColor = (Var*)LangElement::find( "baseColor" ); + Var *outColor = (Var*)LangElement::find( "col" ); + + meta->addStatement( new GenOp( " @ = mix( @, @ + @, @ );\r\n", + outColor, outColor, baseColor, detailColor, detailBlend ) ); + + meta->addStatement( new GenOp( " }\r\n" ) ); + + output = meta; +} + +ShaderFeature::Resources TerrainDetailMapFeatGLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + + if ( fd.features.hasFeature( MFT_PrePassConditioner ) ) + { + // If this is a prepass and we don't have a + // matching normal map... we use no resources. + if ( !fd.features.hasFeature( MFT_TerrainNormalMap, getProcessIndex() ) ) + return res; + + // If this is the first matching normal map then + // it also samples from the layer tex. + if ( !fd.features.hasFeature( MFT_TerrainNormalMap, getProcessIndex() - 1 ) ) + res.numTex += 1; + } + else + { + // If this is the first detail pass then it + // also samples from the layer tex. + if ( !fd.features.hasFeature( MFT_TerrainDetailMap, getProcessIndex() - 1 ) ) + res.numTex += 1; + + res.numTex += 1; + } + + res.numTexReg += 1; + + return res; +} + +void TerrainNormalMapFeatGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // We only need to process normals during the prepass. + if ( !fd.features.hasFeature( MFT_PrePassConditioner ) ) + return; + + MultiLine *meta = new MultiLine; + + // Make sure the world to tangent transform + // is created and available for the pixel shader. + getOutViewToTangent( componentList, meta, fd ); + + output = meta; +} + +void TerrainNormalMapFeatGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // We only need to process normals during the prepass. + if ( !fd.features.hasFeature( MFT_PrePassConditioner ) ) + return; + + MultiLine *meta = new MultiLine; + + Var *viewToTangent = getInViewToTangent( componentList ); + + // This var is read from GBufferConditionerGLSL and + // used in the prepass output. + Var *gbNormal = (Var*)LangElement::find( "gbNormal" ); + if ( !gbNormal ) + { + gbNormal = new Var; + gbNormal->setName( "gbNormal" ); + gbNormal->setType( "vec3" ); + meta->addStatement( new GenOp( " @ = @[2];\r\n", new DecOp( gbNormal ), viewToTangent ) ); + } + + const U32 normalIndex = getProcessIndex(); + + Var *detailBlend = (Var*)LangElement::find( String::ToString( "detailBlend%d", normalIndex ) ); + AssertFatal( detailBlend, "The detail blend is missing!" ); + + // If we're using SM 3.0 then take advantage of + // dynamic branching to skip layers per-pixel. + + if ( GFX->getPixelShaderVersion() >= 3.0f ) + meta->addStatement( new GenOp( " if ( @ > 0.0f )\r\n", detailBlend ) ); + + meta->addStatement( new GenOp( " {\r\n" ) ); + + // Get the normal map texture. + Var *normalMap = _getNormalMapTex(); + + /// Get the texture coord. + Var *inDet = _getInDetailCoord( componentList ); + Var *inTex = getVertTexCoord( "outTexCoord" ); + + // Sample the normal map. + // + // We take two normal samples and lerp between them for + // side projection layers... else a single sample. + LangElement *texOp; + if ( fd.features.hasFeature( MFT_TerrainSideProject, normalIndex ) ) + { + texOp = new GenOp( "mix( texture2D( @, @.yz ), texture2D( @, @.xz ), @.z )", + normalMap, inDet, normalMap, inDet, inTex ); + } + else + texOp = new GenOp( "texture2D(@, @.xy)", normalMap, inDet ); + + // create bump normal + Var *bumpNorm = new Var; + bumpNorm->setName( "bumpNormal" ); + bumpNorm->setType( "vec4" ); + + LangElement *bumpNormDecl = new DecOp( bumpNorm ); + meta->addStatement( expandNormalMap( texOp, bumpNormDecl, bumpNorm, fd ) ); + + // Normalize is done later... + // Note: The reverse mul order is intentional. Affine matrix. + meta->addStatement( new GenOp( " @ = mix( @, @.xyz * @, min( @, @.w ) );\r\n", + gbNormal, gbNormal, bumpNorm, viewToTangent, detailBlend, inDet ) ); + + // End the conditional block. + meta->addStatement( new GenOp( " }\r\n" ) ); + + // If this is the last normal map then we + // can test to see the total blend value + // to see if we should clip the result. + //if ( fd.features.getNextFeatureIndex( MFT_TerrainNormalMap, normalIndex ) == -1 ) + //meta->addStatement( new GenOp( " clip( @ - 0.0001f );\r\n", blendTotal ) ); + + output = meta; +} + +ShaderFeature::Resources TerrainNormalMapFeatGLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + + // We only need to process normals during the prepass. + if ( fd.features.hasFeature( MFT_PrePassConditioner ) ) + { + // If this is the first normal map then it + // will generate the worldToTanget transform. + if ( !fd.features.hasFeature( MFT_TerrainNormalMap, getProcessIndex() - 1 ) ) + res.numTexReg = 3; + + res.numTex = 1; + } + + return res; +} + +TerrainParallaxMapFeatGLSL::TerrainParallaxMapFeatGLSL() + : mIncludeDep( "shaders/common/gl/torque.glsl" ) +{ + addDependency( &mIncludeDep ); +} + +void TerrainParallaxMapFeatGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + if ( LangElement::find( "outNegViewTS" ) ) + return; + + MultiLine *meta = new MultiLine; + + // Grab the input position. + Var *inPos = (Var*)LangElement::find( "inPosition" ); + if ( !inPos ) + inPos = (Var*)LangElement::find( "position" ); + + // Get the object space eye position and the + // object to tangent transform. + Var *eyePos = _getUniformVar( "eyePos", "vec3" , cspPotentialPrimitive ); + Var *objToTangentSpace = getOutObjToTangentSpace( componentList, meta,fd ); + + // Now send the negative view vector in tangent space to the pixel shader. + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outNegViewTS = connectComp->getElement( RT_TEXCOORD ); + outNegViewTS->setName( "outNegViewTS" ); + outNegViewTS->setType( "vec3" ); + meta->addStatement( new GenOp( " @ = @ * vec3( @ - @.xyz );\r\n", + outNegViewTS, objToTangentSpace, eyePos, inPos ) ); + + output = meta; +} + +void TerrainParallaxMapFeatGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + // We need the negative tangent space view vector + // as in parallax mapping we step towards the camera. + Var *negViewTS = (Var*)LangElement::find( "negViewTS" ); + if ( !negViewTS ) + { + Var *inNegViewTS = (Var*)LangElement::find( "outNegViewTS" ); + if ( !inNegViewTS ) + { + inNegViewTS = connectComp->getElement( RT_TEXCOORD ); + inNegViewTS->setName( "outNegViewTS" ); + inNegViewTS->setType( "vec3" ); + } + + negViewTS = new Var( "negViewTS", "vec3" ); + meta->addStatement( new GenOp( " @ = normalize( @ );\r\n", new DecOp( negViewTS ), inNegViewTS ) ); + } + + // Get the rest of our inputs. + Var *detailInfo = _getDetailIdStrengthParallax(); + Var *normalMap = _getNormalMapTex(); + Var *texCoord = _getInDetailCoord( componentList ); + + // Call the library function to do the rest. + meta->addStatement( new GenOp( " @.xy += parallaxOffset( @, @.xy, @, @.z );\r\n", + texCoord, normalMap, texCoord, negViewTS, detailInfo ) ); + + output = meta; +} + +ShaderFeature::Resources TerrainParallaxMapFeatGLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + + // If this is the first parallax feature then + // it will generate the tangetEye vector and + // the worldToTanget transform. + if ( getProcessIndex() == 0 || !fd.features.hasFeature( MFT_TerrainParallaxMap, getProcessIndex() - 1 ) ) + res.numTexReg = 4; + + // If this isn't the prepass then we will + // be adding a normal map. + if ( !fd.features.hasFeature( MFT_PrePassConditioner ) ) + res.numTex = 1; + + return res; +} + +void TerrainLightMapFeatGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // grab connector texcoord register + Var *inTex = (Var*)LangElement::find( "outTexCoord" ); + if ( !inTex ) + return; + + // Get the lightmap texture. + Var *lightMap = new Var; + lightMap->setType( "sampler2D" ); + lightMap->setName( "lightMapTex" ); + lightMap->uniform = true; + lightMap->sampler = true; + lightMap->constNum = Var::getTexUnitNum(); + + // Create a 'lightMask' value which is read by + // RTLighting to mask out the directional lighting. + Var *lightMask = new Var; + lightMask->setType( "vec3" ); + lightMask->setName( "lightMask" ); + + output = new GenOp( " @ = texture2D( @, @.xy ).rgb;\r\n", new DecOp( lightMask ), lightMap, inTex ); +} + +ShaderFeature::Resources TerrainLightMapFeatGLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + res.numTex = 1; + return res; +} + + +void TerrainAdditiveFeatGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + Var *color = (Var*) LangElement::find( "col" ); + Var *blendTotal = (Var*)LangElement::find( "blendTotal" ); + if ( !color || !blendTotal ) + return; + + MultiLine *meta = new MultiLine; + + meta->addStatement( new GenOp( " if ( @ - 0.0001 < 0.0 ) discard;\r\n", blendTotal ) ); + meta->addStatement( new GenOp( " @.a = @;\r\n", color, blendTotal ) ); + + output = meta; +} diff --git a/Engine/source/terrain/glsl/terrFeatureGLSL.h b/Engine/source/terrain/glsl/terrFeatureGLSL.h new file mode 100644 index 000000000..c181ef493 --- /dev/null +++ b/Engine/source/terrain/glsl/terrFeatureGLSL.h @@ -0,0 +1,145 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TERRFEATUREGLSL_H_ +#define _TERRFEATUREGLSL_H_ + +#ifndef _SHADERGEN_GLSL_SHADERFEATUREGLSL_H_ +#include "shaderGen/GLSL/shaderFeatureGLSL.h" +#endif +#ifndef _LANG_ELEMENT_H_ +#include "shaderGen/langElement.h" +#endif + +/// A shared base class for terrain features which +/// includes some helper functions. +class TerrainFeatGLSL : public ShaderFeatureGLSL +{ +protected: + + Var* _getInDetailCoord(Vector &componentList ); + + Var* _getNormalMapTex(); + + static Var* _getUniformVar( const char *name, const char *type, ConstantSortPosition csp ); + + Var* _getDetailIdStrengthParallax(); + +}; + +class TerrainBaseMapFeatGLSL : public TerrainFeatGLSL +{ +public: + + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual String getName() { return "Terrain Base Texture"; } +}; + +class TerrainDetailMapFeatGLSL : public TerrainFeatGLSL +{ +protected: + + ShaderIncludeDependency mTerrainDep; + +public: + + TerrainDetailMapFeatGLSL(); + + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual String getName() { return "Terrain Detail Texture"; } +}; + + +class TerrainNormalMapFeatGLSL : public TerrainFeatGLSL +{ +public: + + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual String getName() { return "Terrain Normal Texture"; } +}; + +class TerrainParallaxMapFeatGLSL : public TerrainFeatGLSL +{ +protected: + + ShaderIncludeDependency mIncludeDep; + +public: + + TerrainParallaxMapFeatGLSL(); + + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual String getName() { return "Terrain Parallax Texture"; } +}; + +class TerrainLightMapFeatGLSL : public TerrainFeatGLSL +{ +public: + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual String getName() { return "Terrain Lightmap Texture"; } +}; + + +class TerrainAdditiveFeatGLSL : public TerrainFeatGLSL +{ +public: + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual String getName() { return "Terrain Additive"; } +}; + +#endif // _TERRFEATUREGLSL_H_ diff --git a/Engine/source/terrain/hlsl/terrFeatureHLSL.cpp b/Engine/source/terrain/hlsl/terrFeatureHLSL.cpp new file mode 100644 index 000000000..6fe0c3c9d --- /dev/null +++ b/Engine/source/terrain/hlsl/terrFeatureHLSL.cpp @@ -0,0 +1,712 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "terrain/hlsl/terrFeatureHLSL.h" + +#include "terrain/terrFeatureTypes.h" +#include "materials/materialFeatureTypes.h" +#include "materials/materialFeatureData.h" +#include "gfx/gfxDevice.h" +#include "shaderGen/langElement.h" +#include "shaderGen/shaderOp.h" +#include "shaderGen/featureMgr.h" +#include "core/module.h" + + +MODULE_BEGIN( TerrainFeatHLSL ) + + MODULE_INIT_AFTER( ShaderGenFeatureMgr ) + + MODULE_INIT + { + FEATUREMGR->registerFeature( MFT_TerrainBaseMap, new TerrainBaseMapFeatHLSL ); + FEATUREMGR->registerFeature( MFT_TerrainParallaxMap, new NamedFeatureHLSL( "Terrain Parallax Texture" ) ); + FEATUREMGR->registerFeature( MFT_TerrainDetailMap, new TerrainDetailMapFeatHLSL ); + FEATUREMGR->registerFeature( MFT_TerrainNormalMap, new TerrainNormalMapFeatHLSL ); + FEATUREMGR->registerFeature( MFT_TerrainLightMap, new TerrainLightMapFeatHLSL ); + FEATUREMGR->registerFeature( MFT_TerrainSideProject, new NamedFeatureHLSL( "Terrain Side Projection" ) ); + FEATUREMGR->registerFeature( MFT_TerrainAdditive, new TerrainAdditiveFeatHLSL ); + } + +MODULE_END; + + +Var* TerrainFeatHLSL::_getUniformVar( const char *name, const char *type, ConstantSortPosition csp ) +{ + Var *theVar = (Var*)LangElement::find( name ); + if ( !theVar ) + { + theVar = new Var; + theVar->setType( type ); + theVar->setName( name ); + theVar->uniform = true; + theVar->constSortPos = csp; + } + + return theVar; +} + +Var* TerrainFeatHLSL::_getInDetailCoord( Vector &componentList ) +{ + String name( String::ToString( "detCoord%d", getProcessIndex() ) ); + Var *inDet = (Var*)LangElement::find( name ); + + if ( !inDet ) + { + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + inDet = connectComp->getElement( RT_TEXCOORD ); + inDet->setName( name ); + inDet->setStructName( "IN" ); + inDet->setType( "float4" ); + inDet->mapsToSampler = true; + } + + return inDet; +} + +Var* TerrainFeatHLSL::_getNormalMapTex() +{ + String name( String::ToString( "normalMap%d", getProcessIndex() ) ); + Var *normalMap = (Var*)LangElement::find( name ); + + if ( !normalMap ) + { + normalMap = new Var; + normalMap->setType( "sampler2D" ); + normalMap->setName( name ); + normalMap->uniform = true; + normalMap->sampler = true; + normalMap->constNum = Var::getTexUnitNum(); + } + + return normalMap; +} + +Var* TerrainFeatHLSL::_getDetailIdStrengthParallax() +{ + String name( String::ToString( "detailIdStrengthParallax%d", getProcessIndex() ) ); + + Var *detailInfo = (Var*)LangElement::find( name ); + if ( !detailInfo ) + { + detailInfo = new Var; + detailInfo->setType( "float3" ); + detailInfo->setName( name ); + detailInfo->uniform = true; + detailInfo->constSortPos = cspPotentialPrimitive; + } + + return detailInfo; +} + +void TerrainBaseMapFeatHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + MultiLine *meta = new MultiLine; + output = meta; + + // Generate the incoming texture var. + Var *inTex; + { + Var *inPos = (Var*)LangElement::find( "inPosition" ); + if ( !inPos ) + inPos = (Var*)LangElement::find( "position" ); + + inTex = new Var( "texCoord", "float3" ); + + Var *oneOverTerrainSize = _getUniformVar( "oneOverTerrainSize", "float", cspPass ); + + // NOTE: The y coord here should be negative to have + // the texture maps not end up flipped which also caused + // normal and parallax mapping to be incorrect. + // + // This mistake early in development means that the layer + // id bilinear blend depends on it being that way. + // + // So instead i fixed this by flipping the base and detail + // coord y scale to compensate when rendering. + // + meta->addStatement( new GenOp( " @ = @.xyz * float3( @, @, -@ );\r\n", + new DecOp( inTex ), inPos, oneOverTerrainSize, oneOverTerrainSize, oneOverTerrainSize ) ); + } + + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + // Pass the texture coord to the pixel shader. + Var *outTex = connectComp->getElement( RT_TEXCOORD ); + outTex->setName( "outTexCoord" ); + outTex->setStructName( "OUT" ); + outTex->setType( "float3" ); + outTex->mapsToSampler = true; + meta->addStatement( new GenOp( " @.xy = @.xy;\r\n", outTex, inTex ) ); + + // If this shader has a side projected layer then we + // pass the dot product between the +Y and the normal + // thru outTexCoord.z for use in blending the textures. + if ( fd.features.hasFeature( MFT_TerrainSideProject ) ) + { + Var *inNormal = (Var*)LangElement::find( "normal" ); + meta->addStatement( + new GenOp( " @.z = pow( abs( dot( normalize( float3( @.x, @.y, 0 ) ), float3( 0, 1, 0 ) ) ), 10.0 );\r\n", + outTex, inNormal, inNormal ) ); + } + else + meta->addStatement( new GenOp( " @.z = 0;\r\n", outTex ) ); + + // HACK: This is sort of lazy... we generate the tanget + // vector here so that we're sure it exists in the parallax + // and normal features which will expect "T" to exist. + // + // If this shader doesn't use it the shader compiler will + // optimize away this code. + // + Var *inTangentZ = getVertTexCoord( "tcTangentZ" ); + Var *inTanget = new Var( "T", "float3" ); + Var *squareSize = _getUniformVar( "squareSize", "float", cspPass ); + meta->addStatement( new GenOp( " @ = normalize( float3( @, 0, @ ) );\r\n", + new DecOp( inTanget ), squareSize, inTangentZ ) ); +} + +void TerrainBaseMapFeatHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // grab connector texcoord register + Var *texCoord = getInTexCoord( "texCoord", "float3", true, componentList ); + + // We do nothing more if this is a prepass. + if ( fd.features.hasFeature( MFT_PrePassConditioner ) ) + return; + + // create texture var + Var *diffuseMap = new Var; + diffuseMap->setType( "sampler2D" ); + diffuseMap->setName( "baseTexMap" ); + diffuseMap->uniform = true; + diffuseMap->sampler = true; + diffuseMap->constNum = Var::getTexUnitNum(); // used as texture unit num here + + MultiLine *meta = new MultiLine; + + Var *baseColor = new Var; + baseColor->setType( "float4" ); + baseColor->setName( "baseColor" ); + meta->addStatement( new GenOp( " @ = tex2D( @, @.xy );\r\n", new DecOp( baseColor ), diffuseMap, texCoord ) ); + meta->addStatement( new GenOp( " @;\r\n", assignColor( baseColor, Material::Mul ) ) ); + + output = meta; +} + +ShaderFeature::Resources TerrainBaseMapFeatHLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + res.numTexReg = 1; + + // We only sample from the base map during a diffuse pass. + if ( !fd.features.hasFeature( MFT_PrePassConditioner ) ) + res.numTex = 1; + + return res; +} + +TerrainDetailMapFeatHLSL::TerrainDetailMapFeatHLSL() + : mTorqueDep( "shaders/common/torque.hlsl" ), + mTerrainDep( "shaders/common/terrain/terrain.hlsl" ) + +{ + addDependency( &mTorqueDep ); + addDependency( &mTerrainDep ); +} + +void TerrainDetailMapFeatHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + const U32 detailIndex = getProcessIndex(); + + // Grab incoming texture coords... the base map feature + // made sure this was created. + Var *inTex = (Var*)LangElement::find( "texCoord" ); + AssertFatal( inTex, "The texture coord is missing!" ); + + // Grab the input position. + Var *inPos = (Var*)LangElement::find( "inPosition" ); + if ( !inPos ) + inPos = (Var*)LangElement::find( "position" ); + + // Get the object space eye position. + Var *eyePos = _getUniformVar( "eyePos", "float3", cspPotentialPrimitive ); + + MultiLine *meta = new MultiLine; + + // If we have parallax mapping then make sure we've sent + // the negative view vector to the pixel shader. + if ( fd.features.hasFeature( MFT_TerrainParallaxMap ) && + !LangElement::find( "outNegViewTS" ) ) + { + // Get the object to tangent transform which + // will consume 3 output registers. + Var *objToTangentSpace = getOutObjToTangentSpace( componentList, meta, fd ); + + // Now use a single output register to send the negative + // view vector in tangent space to the pixel shader. + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outNegViewTS = connectComp->getElement( RT_TEXCOORD ); + outNegViewTS->setName( "outNegViewTS" ); + outNegViewTS->setStructName( "OUT" ); + outNegViewTS->setType( "float3" ); + meta->addStatement( new GenOp( " @ = mul( @, float3( @ - @.xyz ) );\r\n", + outNegViewTS, objToTangentSpace, eyePos, inPos ) ); + } + + // Get the distance from the eye to this vertex. + Var *dist = (Var*)LangElement::find( "dist" ); + if ( !dist ) + { + dist = new Var; + dist->setType( "float" ); + dist->setName( "dist" ); + + meta->addStatement( new GenOp( " @ = distance( @.xyz, @ );\r\n", + new DecOp( dist ), inPos, eyePos ) ); + } + + // grab connector texcoord register + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outTex = connectComp->getElement( RT_TEXCOORD ); + outTex->setName( String::ToString( "detCoord%d", detailIndex ) ); + outTex->setStructName( "OUT" ); + outTex->setType( "float4" ); + outTex->mapsToSampler = true; + + // Get the detail scale and fade info. + Var *detScaleAndFade = new Var; + detScaleAndFade->setType( "float4" ); + detScaleAndFade->setName( String::ToString( "detailScaleAndFade%d", detailIndex ) ); + detScaleAndFade->uniform = true; + detScaleAndFade->constSortPos = cspPotentialPrimitive; + + // Setup the detail coord. + // + // NOTE: You see here we scale the texture coord by 'xyx' + // to generate the detail coord. This y is here because + // its scale is flipped to correct for the non negative y + // in texCoord. + // + // See TerrainBaseMapFeatHLSL::processVert(). + // + meta->addStatement( new GenOp( " @.xyz = @ * @.xyx;\r\n", outTex, inTex, detScaleAndFade ) ); + + // And sneak the detail fade thru the w detailCoord. + meta->addStatement( new GenOp( " @.w = clamp( ( @.z - @ ) * @.w, 0.0, 1.0 );\r\n", + outTex, detScaleAndFade, dist, detScaleAndFade ) ); + + output = meta; +} + +void TerrainDetailMapFeatHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + const U32 detailIndex = getProcessIndex(); + Var *inTex = getVertTexCoord( "texCoord" ); + + MultiLine *meta = new MultiLine; + + // We need the negative tangent space view vector + // as in parallax mapping we step towards the camera. + Var *negViewTS = (Var*)LangElement::find( "negViewTS" ); + if ( !negViewTS && + fd.features.hasFeature( MFT_TerrainParallaxMap ) ) + { + Var *inNegViewTS = (Var*)LangElement::find( "outNegViewTS" ); + if ( !inNegViewTS ) + { + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + inNegViewTS = connectComp->getElement( RT_TEXCOORD ); + inNegViewTS->setName( "outNegViewTS" ); + inNegViewTS->setStructName( "IN" ); + inNegViewTS->setType( "float3" ); + } + + negViewTS = new Var( "negViewTS", "float3" ); + meta->addStatement( new GenOp( " @ = normalize( @ );\r\n", new DecOp( negViewTS ), inNegViewTS ) ); + } + + // Get the layer samples. + Var *layerSample = (Var*)LangElement::find( "layerSample" ); + if ( !layerSample ) + { + layerSample = new Var; + layerSample->setType( "float4" ); + layerSample->setName( "layerSample" ); + + // Get the layer texture var + Var *layerTex = new Var; + layerTex->setType( "sampler2D" ); + layerTex->setName( "layerTex" ); + layerTex->uniform = true; + layerTex->sampler = true; + layerTex->constNum = Var::getTexUnitNum(); + + // Read the layer texture to get the samples. + meta->addStatement( new GenOp( " @ = round( tex2D( @, @.xy ) * 255.0f );\r\n", + new DecOp( layerSample ), layerTex, inTex ) ); + } + + Var *layerSize = (Var*)LangElement::find( "layerSize" ); + if ( !layerSize ) + { + layerSize = new Var; + layerSize->setType( "float" ); + layerSize->setName( "layerSize" ); + layerSize->uniform = true; + layerSize->constSortPos = cspPass; + } + + // Grab the incoming detail coord. + Var *inDet = _getInDetailCoord( componentList ); + + // Get the detail id. + Var *detailInfo = _getDetailIdStrengthParallax(); + + // Create the detail blend var. + Var *detailBlend = new Var; + detailBlend->setType( "float" ); + detailBlend->setName( String::ToString( "detailBlend%d", detailIndex ) ); + + // Calculate the blend for this detail texture. + meta->addStatement( new GenOp( " @ = calcBlend( @.x, @.xy, @, @ );\r\n", + new DecOp( detailBlend ), detailInfo, inTex, layerSize, layerSample ) ); + + // Get a var and accumulate the blend amount. + Var *blendTotal = (Var*)LangElement::find( "blendTotal" ); + if ( !blendTotal ) + { + blendTotal = new Var; + blendTotal->setName( "blendTotal" ); + blendTotal->setType( "float" ); + meta->addStatement( new GenOp( " @ = 0;\r\n", new DecOp( blendTotal ) ) ); + } + + // Add to the blend total. + meta->addStatement( new GenOp( " @ = max( @, @ );\r\n", blendTotal, blendTotal, detailBlend ) ); + + // If we had a parallax feature... then factor in the parallax + // amount so that it fades out with the layer blending. + if ( fd.features.hasFeature( MFT_TerrainParallaxMap, detailIndex ) ) + { + // Get the rest of our inputs. + Var *normalMap = _getNormalMapTex(); + + // Call the library function to do the rest. + meta->addStatement( new GenOp( " @.xy += parallaxOffset( @, @.xy, @, @.z * @ );\r\n", + inDet, normalMap, inDet, negViewTS, detailInfo, detailBlend ) ); + } + + // If this is a prepass then we skip color. + if ( fd.features.hasFeature( MFT_PrePassConditioner ) ) + { + // Check to see if we have a gbuffer normal. + Var *gbNormal = (Var*)LangElement::find( "gbNormal" ); + + // If we have a gbuffer normal and we don't have a + // normal map feature then we need to lerp in a + // default normal else the normals below this layer + // will show thru. + if ( gbNormal && + !fd.features.hasFeature( MFT_TerrainNormalMap, detailIndex ) ) + { + Var *viewToTangent = getInViewToTangent( componentList ); + + meta->addStatement( new GenOp( " @ = lerp( @, @[2], min( @, @.w ) );\r\n", + gbNormal, gbNormal, viewToTangent, detailBlend, inDet ) ); + } + + output = meta; + return; + } + + Var *detailColor = (Var*)LangElement::find( "detailColor" ); + if ( !detailColor ) + { + detailColor = new Var; + detailColor->setType( "float4" ); + detailColor->setName( "detailColor" ); + meta->addStatement( new GenOp( " @;\r\n", new DecOp( detailColor ) ) ); + } + + // Get the detail texture. + Var *detailMap = new Var; + detailMap->setType( "sampler2D" ); + detailMap->setName( String::ToString( "detailMap%d", detailIndex ) ); + detailMap->uniform = true; + detailMap->sampler = true; + detailMap->constNum = Var::getTexUnitNum(); // used as texture unit num here + + // If we're using SM 3.0 then take advantage of + // dynamic branching to skip layers per-pixel. + + + if ( GFX->getPixelShaderVersion() >= 3.0f ) + meta->addStatement( new GenOp( " if ( @ > 0.0f )\r\n", detailBlend ) ); + + meta->addStatement( new GenOp( " {\r\n" ) ); + + // Note that we're doing the standard greyscale detail + // map technique here which can darken and lighten the + // diffuse texture. + // + // We take two color samples and lerp between them for + // side projection layers... else a single sample. + // + if ( fd.features.hasFeature( MFT_TerrainSideProject, detailIndex ) ) + { + meta->addStatement( new GenOp( " @ = ( lerp( tex2D( @, @.yz ), tex2D( @, @.xz ), @.z ) * 2.0 ) - 1.0;\r\n", + detailColor, detailMap, inDet, detailMap, inDet, inTex ) ); + } + else + { + meta->addStatement( new GenOp( " @ = ( tex2D( @, @.xy ) * 2.0 ) - 1.0;\r\n", + detailColor, detailMap, inDet ) ); + } + + meta->addStatement( new GenOp( " @ *= @.y * @.w;\r\n", + detailColor, detailInfo, inDet ) ); + + Var *baseColor = (Var*)LangElement::find( "baseColor" ); + Var *outColor = (Var*)LangElement::find( "col" ); + + meta->addStatement( new GenOp( " @ = lerp( @, @ + @, @ );\r\n", + outColor, outColor, baseColor, detailColor, detailBlend ) ); + + meta->addStatement( new GenOp( " }\r\n" ) ); + + output = meta; +} + +ShaderFeature::Resources TerrainDetailMapFeatHLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + + if ( getProcessIndex() == 0 ) + { + // If this is the first detail pass then we + // samples from the layer tex. + res.numTex += 1; + + // If this material also does parallax then it + // will generate the negative view vector and the + // worldToTanget transform. + if ( fd.features.hasFeature( MFT_TerrainParallaxMap ) ) + res.numTexReg += 4; + } + + // If this isn't the prepass then we sample + // from the detail texture for diffuse coloring. + if ( !fd.features.hasFeature( MFT_PrePassConditioner ) ) + res.numTex += 1; + + // If we have parallax for this layer then we'll also + // be sampling the normal map for the parallax heightmap. + if ( fd.features.hasFeature( MFT_TerrainParallaxMap, getProcessIndex() ) ) + res.numTex += 1; + + // Finally we always send the detail texture + // coord to the pixel shader. + res.numTexReg += 1; + + return res; +} + +void TerrainNormalMapFeatHLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // We only need to process normals during the prepass. + if ( !fd.features.hasFeature( MFT_PrePassConditioner ) ) + return; + + MultiLine *meta = new MultiLine; + + // Make sure the world to tangent transform + // is created and available for the pixel shader. + getOutViewToTangent( componentList, meta, fd ); + + output = meta; +} + +void TerrainNormalMapFeatHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // We only need to process normals during the prepass. + if ( !fd.features.hasFeature( MFT_PrePassConditioner ) ) + return; + + MultiLine *meta = new MultiLine; + + Var *viewToTangent = getInViewToTangent( componentList ); + + // This var is read from GBufferConditionerHLSL and + // used in the prepass output. + Var *gbNormal = (Var*)LangElement::find( "gbNormal" ); + if ( !gbNormal ) + { + gbNormal = new Var; + gbNormal->setName( "gbNormal" ); + gbNormal->setType( "float3" ); + meta->addStatement( new GenOp( " @ = @[2];\r\n", new DecOp( gbNormal ), viewToTangent ) ); + } + + const U32 normalIndex = getProcessIndex(); + + Var *detailBlend = (Var*)LangElement::find( String::ToString( "detailBlend%d", normalIndex ) ); + AssertFatal( detailBlend, "The detail blend is missing!" ); + + // If we're using SM 3.0 then take advantage of + // dynamic branching to skip layers per-pixel. + if ( GFX->getPixelShaderVersion() >= 3.0f ) + meta->addStatement( new GenOp( " if ( @ > 0.0f )\r\n", detailBlend ) ); + + meta->addStatement( new GenOp( " {\r\n" ) ); + + // Get the normal map texture. + Var *normalMap = _getNormalMapTex(); + + /// Get the texture coord. + Var *inDet = _getInDetailCoord( componentList ); + Var *inTex = getVertTexCoord( "texCoord" ); + + // Sample the normal map. + // + // We take two normal samples and lerp between them for + // side projection layers... else a single sample. + LangElement *texOp; + if ( fd.features.hasFeature( MFT_TerrainSideProject, normalIndex ) ) + { + texOp = new GenOp( "lerp( tex2D( @, @.yz ), tex2D( @, @.xz ), @.z )", + normalMap, inDet, normalMap, inDet, inTex ); + } + else + texOp = new GenOp( "tex2D(@, @.xy)", normalMap, inDet ); + + // create bump normal + Var *bumpNorm = new Var; + bumpNorm->setName( "bumpNormal" ); + bumpNorm->setType( "float4" ); + + LangElement *bumpNormDecl = new DecOp( bumpNorm ); + meta->addStatement( expandNormalMap( texOp, bumpNormDecl, bumpNorm, fd ) ); + + // Normalize is done later... + // Note: The reverse mul order is intentional. Affine matrix. + meta->addStatement( new GenOp( " @ = lerp( @, mul( @.xyz, @ ), min( @, @.w ) );\r\n", + gbNormal, gbNormal, bumpNorm, viewToTangent, detailBlend, inDet ) ); + + // End the conditional block. + meta->addStatement( new GenOp( " }\r\n" ) ); + + // If this is the last normal map then we + // can test to see the total blend value + // to see if we should clip the result. + //if ( fd.features.getNextFeatureIndex( MFT_TerrainNormalMap, normalIndex ) == -1 ) + //meta->addStatement( new GenOp( " clip( @ - 0.0001f );\r\n", blendTotal ) ); + + output = meta; +} + +ShaderFeature::Resources TerrainNormalMapFeatHLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + + // We only need to process normals during the prepass. + if ( fd.features.hasFeature( MFT_PrePassConditioner ) ) + { + // If this is the first normal map and there + // are no parallax features then we will + // generate the worldToTanget transform. + if ( !fd.features.hasFeature( MFT_TerrainParallaxMap ) && + ( getProcessIndex() == 0 || !fd.features.hasFeature( MFT_TerrainNormalMap, getProcessIndex() - 1 ) ) ) + res.numTexReg = 3; + + res.numTex = 1; + } + + return res; +} + +void TerrainLightMapFeatHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + // grab connector texcoord register + Var *inTex = (Var*)LangElement::find( "texCoord" ); + if ( !inTex ) + return; + + // Get the lightmap texture. + Var *lightMap = new Var; + lightMap->setType( "sampler2D" ); + lightMap->setName( "lightMapTex" ); + lightMap->uniform = true; + lightMap->sampler = true; + lightMap->constNum = Var::getTexUnitNum(); + + MultiLine *meta = new MultiLine; + + // Find or create the lightMask value which is read by + // RTLighting to mask out the lights. + // + // The first light is always the sunlight so we apply + // the shadow mask to only the first channel. + // + Var *lightMask = (Var*)LangElement::find( "lightMask" ); + if ( !lightMask ) + { + lightMask = new Var( "lightMask", "float4" ); + meta->addStatement( new GenOp( " @ = 1;\r\n", new DecOp( lightMask ) ) ); + } + + meta->addStatement( new GenOp( " @[0] = tex2D( @, @.xy ).r;\r\n", lightMask, lightMap, inTex ) ); + output = meta; +} + +ShaderFeature::Resources TerrainLightMapFeatHLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + res.numTex = 1; + return res; +} + + +void TerrainAdditiveFeatHLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + Var *color = (Var*) LangElement::find( "col" ); + Var *blendTotal = (Var*)LangElement::find( "blendTotal" ); + if ( !color || !blendTotal ) + return; + + MultiLine *meta = new MultiLine; + + meta->addStatement( new GenOp( " clip( @ - 0.0001 );\r\n", blendTotal ) ); + meta->addStatement( new GenOp( " @.a = @;\r\n", color, blendTotal ) ); + + output = meta; +} diff --git a/Engine/source/terrain/hlsl/terrFeatureHLSL.h b/Engine/source/terrain/hlsl/terrFeatureHLSL.h new file mode 100644 index 000000000..a1de7a002 --- /dev/null +++ b/Engine/source/terrain/hlsl/terrFeatureHLSL.h @@ -0,0 +1,128 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TERRFEATUREHLSL_H_ +#define _TERRFEATUREHLSL_H_ + +#ifndef _SHADERGEN_HLSL_SHADERFEATUREHLSL_H_ +#include "shaderGen/HLSL/shaderFeatureHLSL.h" +#endif +#ifndef _LANG_ELEMENT_H_ +#include "shaderGen/langElement.h" +#endif + + +/// A shared base class for terrain features which +/// includes some helper functions. +class TerrainFeatHLSL : public ShaderFeatureHLSL +{ +protected: + + Var* _getInDetailCoord(Vector &componentList ); + + Var* _getNormalMapTex(); + + static Var* _getUniformVar( const char *name, const char *type, ConstantSortPosition csp ); + + Var* _getDetailIdStrengthParallax(); + +}; + + +class TerrainBaseMapFeatHLSL : public TerrainFeatHLSL +{ +public: + + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual String getName() { return "Terrain Base Texture"; } +}; + + +class TerrainDetailMapFeatHLSL : public TerrainFeatHLSL +{ +protected: + + ShaderIncludeDependency mTorqueDep; + ShaderIncludeDependency mTerrainDep; + +public: + + TerrainDetailMapFeatHLSL(); + + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual String getName() { return "Terrain Detail Texture"; } +}; + + +class TerrainNormalMapFeatHLSL : public TerrainFeatHLSL +{ +public: + + virtual void processVert( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual String getName() { return "Terrain Normal Texture"; } +}; + +class TerrainLightMapFeatHLSL : public TerrainFeatHLSL +{ +public: + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual Resources getResources( const MaterialFeatureData &fd ); + + virtual String getName() { return "Terrain Lightmap Texture"; } +}; + + +class TerrainAdditiveFeatHLSL : public TerrainFeatHLSL +{ +public: + + virtual void processPix( Vector &componentList, + const MaterialFeatureData &fd ); + + virtual String getName() { return "Terrain Additive"; } +}; + +#endif // _TERRFEATUREHLSL_H_ diff --git a/Engine/source/terrain/terrCell.cpp b/Engine/source/terrain/terrCell.cpp new file mode 100644 index 000000000..dac6c910a --- /dev/null +++ b/Engine/source/terrain/terrCell.cpp @@ -0,0 +1,1091 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "terrain/terrCell.h" + +#include "math/util/frustum.h" +#include "terrain/terrData.h" +#include "terrain/terrCellMaterial.h" +#include "scene/sceneRenderState.h" +#include "lighting/lightManager.h" +#include "gfx/gfxDrawUtil.h" + + +GFXImplementVertexFormat( TerrVertex ) +{ + addElement( "POSITION", GFXDeclType_Float3 ); + addElement( "NORMAL", GFXDeclType_Float3 ); + addElement( "TangentZ", GFXDeclType_Float, 0 ); + addElement( "Empty", GFXDeclType_Float, 1 ); +}; + +const U32 TerrCell::smMinCellSize = 64; +const U32 TerrCell::smVBStride = TerrCell::smMinCellSize + 1; // 129 +const U32 TerrCell::smVBSize = ( TerrCell::smVBStride * TerrCell::smVBStride ) + + ( TerrCell::smVBStride * 4 ); // 17,157 +const U32 TerrCell::smPBSize = ( TerrCell::smMinCellSize * TerrCell::smMinCellSize * 6 ) + + ( TerrCell::smMinCellSize * 4 * 6 ); // 101,376 +const U32 TerrCell::smTriCount = TerrCell::smPBSize / 3; // 33,792 + + +TerrCell::TerrCell() + : mMaterials( 0 ), + mMaterial( NULL ), + mIsInteriorOnly( false ), + mTriCount( 0 ), + mHasEmpty( false ) +{ + dMemset( mChildren, 0, sizeof( mChildren ) ); +} + +TerrCell::~TerrCell() +{ + SAFE_DELETE( mMaterial ); + + for ( U32 i=0; i < 4; i++ ) + SAFE_DELETE( mChildren[i] ); +} + +void TerrCell::createPrimBuffer( GFXPrimitiveBufferHandle *primBuffer ) +{ + PROFILE_SCOPE( TerrCell_AllocPrimBuffer ); + + primBuffer->set( GFX, smPBSize, 1, GFXBufferTypeStatic, "TerrCell" ); + + // We don't use the primitive for normal clipmap + // rendering, but it is used for the shadow pass. + GFXPrimitive *prim = primBuffer->getPointer()->mPrimitiveArray; + prim->type = GFXTriangleList; + prim->numPrimitives = smTriCount; + prim->numVertices = smVBSize; + + // + // The vertex pattern for the terrain is as + // follows... + // + // 0----1----2.....n + // |\ | /| + // | \ | / | + // | \ | / | + // | \|/ | + // n----n----n + // | /|\ | + // | / | \ | + // | / | \ | + // |/ | \| + // n----n----n + // + + // Lock and fill it up! + U16 *idxBuff; + primBuffer->lock( &idxBuff ); + U32 counter = 0; + U32 maxIndex = 0; + + for ( U32 y = 0; y < smMinCellSize; y++ ) + { + const U32 yTess = y % 2; + + for ( U32 x = 0; x < smMinCellSize; x++ ) + { + U32 index = ( y * smVBStride ) + x; + + const U32 xTess = x % 2; + + if ( ( xTess == 0 && yTess == 0 ) || + ( xTess != 0 && yTess != 0 ) ) + { + idxBuff[0] = index + 0; + idxBuff[1] = index + smVBStride; + idxBuff[2] = index + smVBStride + 1; + + idxBuff[3] = index + 0; + idxBuff[4] = index + smVBStride + 1; + idxBuff[5] = index + 1; + } + else + { + idxBuff[0] = index + 1; + idxBuff[1] = index; + idxBuff[2] = index + smVBStride; + + idxBuff[3] = index + 1; + idxBuff[4] = index + smVBStride; + idxBuff[5] = index + smVBStride + 1; + } + + idxBuff += 6; + maxIndex = index + 1 + smVBStride; + counter += 6; + } + } + + // Now add indices for the 'skirts'. + // These could probably be reduced to a loop. + + // Temporaries that hold triangle indices. + // Top/Bottom - 0,1 + U32 t0, t1, b0, b1; + + // Top edge skirt... + + // Index to the first vert of the top row. + U32 startIndex = 0; + // Index to the first vert of the skirt under the top row. + U32 skirtStartIdx = smVBStride * smVBStride; + // Step to go one vert to the right. + U32 step = 1; + + for ( U32 i = 0; i < smMinCellSize; i++ ) + { + t0 = startIndex + i * step; + t1 = t0 + step; + b0 = skirtStartIdx + i; + b1 = skirtStartIdx + i + 1; + + idxBuff[0] = b0; + idxBuff[1] = t0; + idxBuff[2] = t1; + + idxBuff[3] = b1; + idxBuff[4] = b0; + idxBuff[5] = t1; + + idxBuff += 6; + maxIndex = b1; + counter += 6; + } + + // Bottom edge skirt... + + // Index to the first vert of the bottom row. + startIndex = smVBStride * smVBStride - smVBStride; + // Index to the first vert of the skirt under the bottom row. + skirtStartIdx = startIndex + smVBStride * 2; + // Step to go one vert to the right. + step = 1; + + for ( U32 i = 0; i < smMinCellSize; i++ ) + { + t0 = startIndex + ( i * step ); + t1 = t0 + step; + b0 = skirtStartIdx + i; + b1 = skirtStartIdx + i + 1; + + idxBuff[0] = t1; + idxBuff[1] = t0; + idxBuff[2] = b0; + + idxBuff[3] = t1; + idxBuff[4] = b0; + idxBuff[5] = b1; + + idxBuff += 6; + maxIndex = b1; + counter += 6; + } + + // Left edge skirt... + + // Index to the first vert of the left column. + startIndex = 0; + // Index to the first vert of the skirt under the left column. + skirtStartIdx = smVBStride * smVBStride + smVBStride * 2; + // Step to go one vert down. + step = smVBStride; + + for ( U32 i = 0; i < smMinCellSize; i++ ) + { + t0 = startIndex + ( i * step ); + t1 = t0 + step; + b0 = skirtStartIdx + i; + b1 = skirtStartIdx + i + 1; + + idxBuff[0] = t1; + idxBuff[1] = t0; + idxBuff[2] = b0; + + idxBuff[3] = t1; + idxBuff[4] = b0; + idxBuff[5] = b1; + + idxBuff += 6; + maxIndex = b1; + counter += 6; + } + + // Right edge skirt... + + // Index to the first vert of the right column. + startIndex = smVBStride - 1; + // Index to the first vert of the skirt under the right column. + skirtStartIdx = smVBStride * smVBStride + smVBStride * 3; + // Step to go one vert down. + step = smVBStride; + + for ( U32 i = 0; i < smMinCellSize; i++ ) + { + t0 = startIndex + ( i * step ); + t1 = t0 + step; + b0 = skirtStartIdx + i; + b1 = skirtStartIdx + i + 1; + + idxBuff[0] = b0; + idxBuff[1] = t0; + idxBuff[2] = t1; + + idxBuff[3] = b1; + idxBuff[4] = b0; + idxBuff[5] = t1; + + idxBuff += 6; + maxIndex = b1; + counter += 6; + } + + primBuffer->unlock(); +} + +TerrCell* TerrCell::init( TerrainBlock *terrain ) +{ + // Just create the root cell and call the inner init. + TerrCell *root = new TerrCell; + root->_init( terrain, + Point2I( 0, 0 ), + terrain->getBlockSize(), + 0 ); + + // Set initial states of OBBs. + root->updateOBBs(); + + return root; +} + + +void TerrCell::_init( TerrainBlock *terrain, + const Point2I &point, + U32 size, + U32 level ) +{ + PROFILE_SCOPE( TerrCell_Init ); + + mTerrain = terrain; + mPoint = point; + mSize = size; + mLevel = level; + + // Generate a VB (and maybe a PB) for this cell, unless we are the Root cell. + if ( level > 0 ) + { + _updateVertexBuffer(); + _updatePrimitiveBuffer(); + } + + if ( mSize <= smMinCellSize ) + { + // Update our bounds and materials... the + // parent will use it to update itself. + _updateBounds(); + _updateMaterials(); + return; + } + + // Create our children and update our + // bounds and materials from them. + + const U32 childSize = mSize / 2; + const U32 childLevel = mLevel + 1; + + mChildren[0] = new TerrCell; + mChildren[0]->_init( mTerrain, + Point2I( mPoint.x, mPoint.y ), + childSize, + childLevel ); + mBounds = mChildren[0]->getBounds(); + mMaterials = mChildren[0]->getMaterials(); + + mChildren[1] = new TerrCell; + mChildren[1]->_init( mTerrain, + Point2I( mPoint.x + childSize, mPoint.y ), + childSize, + childLevel ); + mBounds.intersect( mChildren[1]->getBounds() ); + mMaterials |= mChildren[1]->getMaterials(); + + mChildren[2] = new TerrCell; + mChildren[2]->_init( mTerrain, + Point2I( mPoint.x, mPoint.y + childSize ), + childSize, + childLevel ); + mBounds.intersect( mChildren[2]->getBounds() ); + mMaterials |= mChildren[2]->getMaterials(); + + mChildren[3] = new TerrCell; + mChildren[3]->_init( mTerrain, + Point2I( mPoint.x + childSize, mPoint.y + childSize ), + childSize, + childLevel ); + mBounds.intersect( mChildren[3]->getBounds() ); + mMaterials |= mChildren[3]->getMaterials(); + + mRadius = mBounds.len() * 0.5f; + + _updateOBB(); +} + +void TerrCell::updateGrid( const RectI &gridRect, bool opacityOnly ) +{ + PROFILE_SCOPE( TerrCell_UpdateGrid ); + + // If we have a VB... then update it. + if ( mVertexBuffer.isValid() && !opacityOnly ) + _updateVertexBuffer(); + + // Update our PB, if any + _updatePrimitiveBuffer(); + + // If we don't have children... then we're + // a leaf at the bottom of the cell quadtree + // and we should just update our bounds. + if ( !mChildren[0] ) + { + if ( !opacityOnly ) + _updateBounds(); + + _updateMaterials(); + return; + } + + // Otherwise, we must call updateGrid on our children + // and then update our bounds/materials AFTER to contain them. + + mMaterials = 0; + + for ( U32 i = 0; i < 4; i++ ) + { + TerrCell *cell = mChildren[i]; + + // The overlap test doesn't hit shared edges + // so grow it a bit when we create it. + const RectI cellRect( cell->mPoint.x - 1, + cell->mPoint.y - 1, + cell->mSize + 2, + cell->mSize + 2 ); + + // We do an overlap and containment test as it + // properly handles zero sized rects. + if ( cellRect.contains( gridRect ) || + cellRect.overlaps( gridRect ) ) + cell->updateGrid( gridRect, opacityOnly ); + + // Update the bounds from our children. + if ( !opacityOnly ) + { + if ( i == 0 ) + mBounds = mChildren[i]->getBounds(); + else + mBounds.intersect( mChildren[i]->getBounds() ); + + mRadius = mBounds.len() * 0.5f; + } + + // Update the material flags. + mMaterials |= mChildren[i]->getMaterials(); + } + + if ( mMaterial ) + mMaterial->init( mTerrain, mMaterials ); +} + +void TerrCell::_updateVertexBuffer() +{ + PROFILE_SCOPE( TerrCell_UpdateVertexBuffer ); + + // Start off with no empty squares + mHasEmpty = false; + mEmptyVertexList.clear(); + + mVertexBuffer.set( GFX, smVBSize, GFXBufferTypeStatic ); + + const F32 squareSize = mTerrain->getSquareSize(); + const U32 blockSize = mTerrain->getBlockSize(); + const U32 stepSize = mSize / smMinCellSize; + + U32 vbcounter = 0; + + TerrVertex *vert = mVertexBuffer.lock(); + + Point2I gridPt; + Point2F point; + F32 height; + Point3F normal; + + const TerrainFile *file = mTerrain->getFile(); + + for ( U32 y = 0; y < smVBStride; y++ ) + { + for ( U32 x = 0; x < smVBStride; x++ ) + { + // We clamp here to keep the geometry from reading across + // one side of the height map to the other causing walls + // around the edges of the terrain. + gridPt.x = mClamp( mPoint.x + x * stepSize, 0, blockSize - 1 ); + gridPt.y = mClamp( mPoint.y + y * stepSize, 0, blockSize - 1 ); + + // Setup this point. + point.x = (F32)gridPt.x * squareSize; + point.y = (F32)gridPt.y * squareSize; + height = fixedToFloat( file->getHeight( gridPt.x, gridPt.y ) ); + vert->point.x = point.x; + vert->point.y = point.y; + vert->point.z = height; + + // Get the normal. + mTerrain->getSmoothNormal( point, &normal, true, false ); + vert->normal = normal; + + // Get the tangent z. + vert->tangentZ = fixedToFloat( file->getHeight( gridPt.x + 1, gridPt.y ) ) - height; + + // Test the empty state for this vert. + if ( file->isEmptyAt( gridPt.x, gridPt.y ) ) + { + mHasEmpty = true; + mEmptyVertexList.push_back( vbcounter ); + } + + vbcounter++; + ++vert; + } + } + + // Add verts for 'skirts' around/beneath the edge verts of this cell. + // This could probably be reduced to a loop... + + const F32 skirtDepth = mSize / smMinCellSize * mTerrain->getSquareSize(); + + // Top edge skirt + for ( U32 i = 0; i < smVBStride; i++ ) + { + gridPt.x = mClamp( mPoint.x + i * stepSize, 0, blockSize - 1 ); + gridPt.y = mClamp( mPoint.y, 0, blockSize - 1 ); + + point.x = (F32)gridPt.x * squareSize; + point.y = (F32)gridPt.y * squareSize; + height = fixedToFloat( file->getHeight( gridPt.x, gridPt.y ) ); + vert->point.x = point.x; + vert->point.y = point.y; + vert->point.z = height - skirtDepth; + + // Get the normal. + mTerrain->getNormal( point, &normal, true, false ); + vert->normal = normal; + + // Get the tangent. + vert->tangentZ = height - fixedToFloat( file->getHeight( gridPt.x + 1, gridPt.y ) ); + + vbcounter++; + ++vert; + } + + // Bottom edge skirt + for ( U32 i = 0; i < smVBStride; i++ ) + { + gridPt.x = mClamp( mPoint.x + i * stepSize, 0, blockSize - 1 ); + gridPt.y = mClamp( mPoint.y + smMinCellSize * stepSize, 0, blockSize - 1 ); + + point.x = (F32)gridPt.x * squareSize; + point.y = (F32)gridPt.y * squareSize; + height = fixedToFloat( file->getHeight( gridPt.x, gridPt.y ) ); + vert->point.x = point.x; + vert->point.y = point.y; + vert->point.z = height - skirtDepth; + + // Get the normal. + mTerrain->getNormal( point, &normal, true, false ); + vert->normal = normal; + + // Get the tangent. + vert->tangentZ = height - fixedToFloat( file->getHeight( gridPt.x + 1, gridPt.y ) ); + + vbcounter++; + ++vert; + } + + // Left edge skirt + for ( U32 i = 0; i < smVBStride; i++ ) + { + gridPt.x = mClamp( mPoint.x, 0, blockSize - 1 ); + gridPt.y = mClamp( mPoint.y + i * stepSize, 0, blockSize - 1 ); + + point.x = (F32)gridPt.x * squareSize; + point.y = (F32)gridPt.y * squareSize; + height = fixedToFloat( file->getHeight( gridPt.x, gridPt.y ) ); + vert->point.x = point.x; + vert->point.y = point.y; + vert->point.z = height - skirtDepth; + + // Get the normal. + mTerrain->getNormal( point, &normal, true, false ); + vert->normal = normal; + + // Get the tangent. + vert->tangentZ = height - fixedToFloat( file->getHeight( gridPt.x + 1, gridPt.y ) ); + + vbcounter++; + ++vert; + } + + // Right edge skirt + for ( U32 i = 0; i < smVBStride; i++ ) + { + gridPt.x = mClamp( mPoint.x + smMinCellSize * stepSize, 0, blockSize - 1 ); + gridPt.y = mClamp( mPoint.y + i * stepSize, 0, blockSize - 1 ); + + point.x = (F32)gridPt.x * squareSize; + point.y = (F32)gridPt.y * squareSize; + height = fixedToFloat( file->getHeight( gridPt.x, gridPt.y ) ); + vert->point.x = point.x; + vert->point.y = point.y; + vert->point.z = height - skirtDepth; + + // Get the normal. + mTerrain->getNormal( point, &normal, true, false ); + vert->normal = normal; + + // Get the tangent. + vert->tangentZ = height - fixedToFloat( file->getHeight( gridPt.x + 1, gridPt.y ) ); + + vbcounter++; + ++vert; + } + + AssertFatal( vbcounter == smVBSize, "bad" ); + mVertexBuffer.unlock(); +} + +void TerrCell::_updatePrimitiveBuffer() +{ + PROFILE_SCOPE( TerrCell_UpdatePrimitiveBuffer ); + + if ( !mHasEmpty ) + { + if ( mPrimBuffer.isValid() ) + { + // There are no more empty squares for this cell, so + // get rid of the primitive buffer to use the standard one. + mPrimBuffer = NULL; + } + + return; + } + + // Build our custom primitive buffer. We're setting it to the maximum allowed + // size, but should be just shy of it depending on the number of empty squares + // in this cell. We could calculate it, but note that it would be different + // from mEmptyVertexList.size() as that can include vertices on the edges that + // are really considered part of another cell's squares. So we take a slightly + // larger buffer over running through the calculation. + mPrimBuffer.set( GFX, smPBSize, 1, GFXBufferTypeStatic, "TerrCell" ); + + GFXPrimitive *prim = mPrimBuffer.getPointer()->mPrimitiveArray; + prim->type = GFXTriangleList; + prim->numVertices = smVBSize; + + mTriCount = 0; + + // Lock and fill it up! + U16 *idxBuff; + mPrimBuffer.lock( &idxBuff ); + U32 counter = 0; + U32 maxIndex = 0; + + for ( U32 y = 0; y < smMinCellSize; y++ ) + { + const U32 yTess = y % 2; + + for ( U32 x = 0; x < smMinCellSize; x++ ) + { + U32 index = ( y * smVBStride ) + x; + + // Should this square be skipped? + if ( _isVertIndexEmpty(index) ) + continue; + + const U32 xTess = x % 2; + + if ( ( xTess == 0 && yTess == 0 ) || + ( xTess != 0 && yTess != 0 ) ) + { + idxBuff[0] = index + 0; + idxBuff[1] = index + smVBStride; + idxBuff[2] = index + smVBStride + 1; + + idxBuff[3] = index + 0; + idxBuff[4] = index + smVBStride + 1; + idxBuff[5] = index + 1; + } + else + { + idxBuff[0] = index + 1; + idxBuff[1] = index; + idxBuff[2] = index + smVBStride; + + idxBuff[3] = index + 1; + idxBuff[4] = index + smVBStride; + idxBuff[5] = index + smVBStride + 1; + } + + idxBuff += 6; + maxIndex = index + 1 + smVBStride; + counter += 6; + + mTriCount += 2; + } + } + + // Now add indices for the 'skirts'. + // These could probably be reduced to a loop. + + // Temporaries that hold triangle indices. + // Top/Bottom - 0,1 + U32 t0, t1, b0, b1; + + // Top edge skirt... + + // Index to the first vert of the top row. + U32 startIndex = 0; + // Index to the first vert of the skirt under the top row. + U32 skirtStartIdx = smVBStride * smVBStride; + // Step to go one vert to the right. + U32 step = 1; + + for ( U32 i = 0; i < smMinCellSize; i++ ) + { + t0 = startIndex + i * step; + + // Should this square be skipped? + if ( _isVertIndexEmpty(t0) ) + continue; + + t1 = t0 + step; + b0 = skirtStartIdx + i; + b1 = skirtStartIdx + i + 1; + + idxBuff[0] = b0; + idxBuff[1] = t0; + idxBuff[2] = t1; + + idxBuff[3] = b1; + idxBuff[4] = b0; + idxBuff[5] = t1; + + idxBuff += 6; + maxIndex = b1; + counter += 6; + + mTriCount += 2; + } + + // Bottom edge skirt... + + // Index to the first vert of the bottom row. + startIndex = smVBStride * smVBStride - smVBStride; + // Index to the first vert of the skirt under the bottom row. + skirtStartIdx = startIndex + smVBStride * 2; + // Step to go one vert to the right. + step = 1; + + for ( U32 i = 0; i < smMinCellSize; i++ ) + { + t0 = startIndex + ( i * step ); + + // Should this square be skipped? We actually need to test + // the vertex one row down as it defines the empty state + // for this square. + if ( _isVertIndexEmpty( t0 - smVBStride ) ) + continue; + + t1 = t0 + step; + b0 = skirtStartIdx + i; + b1 = skirtStartIdx + i + 1; + + idxBuff[0] = t1; + idxBuff[1] = t0; + idxBuff[2] = b0; + + idxBuff[3] = t1; + idxBuff[4] = b0; + idxBuff[5] = b1; + + idxBuff += 6; + maxIndex = b1; + counter += 6; + + mTriCount += 2; + } + + // Left edge skirt... + + // Index to the first vert of the left column. + startIndex = 0; + // Index to the first vert of the skirt under the left column. + skirtStartIdx = smVBStride * smVBStride + smVBStride * 2; + // Step to go one vert down. + step = smVBStride; + + for ( U32 i = 0; i < smMinCellSize; i++ ) + { + t0 = startIndex + ( i * step ); + + // Should this square be skipped? + if ( _isVertIndexEmpty(t0) ) + continue; + + t1 = t0 + step; + b0 = skirtStartIdx + i; + b1 = skirtStartIdx + i + 1; + + idxBuff[0] = t1; + idxBuff[1] = t0; + idxBuff[2] = b0; + + idxBuff[3] = t1; + idxBuff[4] = b0; + idxBuff[5] = b1; + + idxBuff += 6; + maxIndex = b1; + counter += 6; + + mTriCount += 2; + } + + // Right edge skirt... + + // Index to the first vert of the right column. + startIndex = smVBStride - 1; + // Index to the first vert of the skirt under the right column. + skirtStartIdx = smVBStride * smVBStride + smVBStride * 3; + // Step to go one vert down. + step = smVBStride; + + for ( U32 i = 0; i < smMinCellSize; i++ ) + { + t0 = startIndex + ( i * step ); + + // Should this square be skipped? We actually need to test + // the vertex one column to the left as it defines the empty + // state for this square. + if ( _isVertIndexEmpty( t0 - 1 ) ) + continue; + + t1 = t0 + step; + b0 = skirtStartIdx + i; + b1 = skirtStartIdx + i + 1; + + idxBuff[0] = b0; + idxBuff[1] = t0; + idxBuff[2] = t1; + + idxBuff[3] = b1; + idxBuff[4] = b0; + idxBuff[5] = t1; + + idxBuff += 6; + maxIndex = b1; + counter += 6; + + mTriCount += 2; + } + + mPrimBuffer.unlock(); + prim->numPrimitives = mTriCount; +} + +void TerrCell::_updateMaterials() +{ + PROFILE_SCOPE( TerrCell_UpdateMaterials ); + + // This should really only be called for cells of smMinCellSize, + // in which case stepSize is always one. + U32 stepSize = mSize / smMinCellSize; + mMaterials = 0; + U8 index; + U32 x, y; + + const TerrainFile *file = mTerrain->getFile(); + + // Step thru the samples in the map then. + for ( y = 0; y < smVBStride; y++ ) + { + for ( x = 0; x < smVBStride; x++ ) + { + index = file->getLayerIndex( mPoint.x + x * stepSize, + mPoint.y + y * stepSize ); + + // Skip empty layers and anything that doesn't fit + // the 64bit material flags. + if ( index == U8_MAX || index > 63 ) + continue; + + mMaterials |= (U64)(1<init( mTerrain, mMaterials ); +} + +void TerrCell::_updateBounds() +{ + PROFILE_SCOPE( TerrCell_UpdateBounds ); + + const F32 squareSize = mTerrain->getSquareSize(); + + // This should really only be called for cells of smMinCellSize, + // in which case stepSize is always one. + const U32 stepSize = mSize / smMinCellSize; + + // Prepare to expand the bounds. + mBounds.minExtents.set( F32_MAX, F32_MAX, F32_MAX ); + mBounds.maxExtents.set( -F32_MAX, -F32_MAX, -F32_MAX ); + + Point3F vert; + Point2F texCoord; + + const TerrainFile *file = mTerrain->getFile(); + + for ( U32 y = 0; y < smVBStride; y++ ) + { + for ( U32 x = 0; x < smVBStride; x++ ) + { + // Setup this point. + vert.x = (F32)( mPoint.x + x * stepSize ) * squareSize; + vert.y = (F32)( mPoint.y + y * stepSize ) * squareSize; + vert.z = fixedToFloat( file->getHeight( mPoint.x + x, + mPoint.y + y ) ); + + // HACK: Call it twice to deal with the inverted + // inital bounds state... shouldn't be a perf issue. + mBounds.extend( vert ); + mBounds.extend( vert ); + } + } + + mRadius = mBounds.len() * 0.5; + + _updateOBB(); +} + +void TerrCell::_updateOBB() +{ + mOBB.set( mTerrain->getTransform(), mBounds ); +} + +void TerrCell::updateOBBs() +{ + _updateOBB(); + + // Update children. + + if( mChildren[ 0 ] ) + for( U32 i = 0; i < 4; ++ i ) + mChildren[ i ]->updateOBBs(); +} + +void TerrCell::updateZoning( const SceneZoneSpaceManager *zoneManager ) +{ + PROFILE_SCOPE( TerrCell_UpdateZoning ); + + mZoneOverlap.setSize( zoneManager->getNumZones() ); + mZoneOverlap.clear(); + mIsInteriorOnly = true; + + if ( mChildren[0] == NULL ) + { + Box3F worldBounds( mBounds ); + mTerrain->getTransform().mul( worldBounds ); + + Vector zones; + zoneManager->findZones( worldBounds, zones ); + + for ( U32 i=0; i < zones.size(); i++ ) + { + // Set overlap bit for zone except it's the outdoor zone. + if( zones[ i ] != SceneZoneSpaceManager::RootZoneId ) + mZoneOverlap.set( zones[i] ); + else + mIsInteriorOnly = false; + } + + return; + } + + for ( U32 i = 0; i < 4; i++ ) + { + TerrCell *cell = mChildren[i]; + cell->updateZoning( zoneManager ); + mZoneOverlap.combineOR( cell->getZoneOverlap() ); + mIsInteriorOnly &= cell->mIsInteriorOnly; + } +} + +void TerrCell::cullCells( const SceneRenderState *state, + const Point3F &objLodPos, + Vector *outCells ) +{ + // If we have a VB and no children then just add + // ourselves to the results and return. + if ( mVertexBuffer.isValid() && !mChildren[0] ) + { + outCells->push_back( this ); + return; + } + + const F32 screenError = mTerrain->getScreenError(); + const BitVector &zoneState = state->getCullingState().getZoneVisibilityFlags(); + + for ( U32 i = 0; i < 4; i++ ) + { + TerrCell *cell = mChildren[i]; + + // Test cell visibility for interior zones. + + const bool visibleInside = !cell->getZoneOverlap().empty() ? zoneState.testAny( cell->getZoneOverlap() ) : false; + + // Test cell visibility for outdoor zone, but only + // if we need to. + + bool visibleOutside = false; + if( !mIsInteriorOnly && !visibleInside ) + { + U32 outdoorZone = SceneZoneSpaceManager::RootZoneId; + visibleOutside = !state->getCullingState().isCulled( cell->mOBB, &outdoorZone, 1 ); + } + + // Skip cell if neither visible indoors nor outdoors. + + if( !visibleInside && !visibleOutside ) + continue; + + // Lod based on screen error... + // If far enough, just add this child cells vb ( skipping its children ). + F32 dist = cell->getDistanceTo( objLodPos ); + F32 errorMeters = ( cell->mSize / smMinCellSize ) * mTerrain->getSquareSize(); + U32 errorPixels = mCeil( state->projectRadius( dist, errorMeters ) ); + + if ( errorPixels < screenError ) + { + if ( cell->mVertexBuffer.isValid() ) + outCells->push_back( cell ); + } + else + cell->cullCells( state, objLodPos, outCells ); + } +} + +void TerrCell::getRenderPrimitive( GFXPrimitive *prim, + GFXVertexBufferHandleBase *vertBuff, + GFXPrimitiveBufferHandle *primBuff ) const +{ + *vertBuff = mVertexBuffer; + + // Only supply a primitive buffer if we're using our own + // due to empty squares. + bool useStaticPrimBuffer = true; + if ( mPrimBuffer.isValid() ) + { + useStaticPrimBuffer = false; + *primBuff = mPrimBuffer; + } + + prim->type = GFXTriangleList; + prim->startVertex = 0; + prim->minIndex = 0; + prim->startIndex = 0; + prim->numVertices = smVBSize; + + if ( useStaticPrimBuffer ) + { + // Use the standard primitive buffer count + prim->numPrimitives = smTriCount; + } + else + { + // Use our triangle count that matches out primitive buffer + prim->numPrimitives = mTriCount; + } +} + +void TerrCell::renderBounds() const +{ + ColorI color; + color.interpolate( ColorI::RED, ColorI::GREEN, (F32)mLevel / 3.0f ); + + GFXStateBlockDesc desc; + desc.setZReadWrite( true, false ); + desc.fillMode = GFXFillWireframe; + + GFX->getDrawUtil()->drawCube( desc, mBounds, color ); +} + +void TerrCell::preloadMaterials() +{ + PROFILE_SCOPE( TerrCell_PreloadMaterials ); + + // If we have a VB then we need a material. + if ( mVertexBuffer.isValid() ) + { + TerrainCellMaterial *material = getMaterial(); + material->getReflectMat(); + + if ( GFX->getPixelShaderVersion() > 2.0f && + dStrcmp( LIGHTMGR->getId(), "BLM" ) != 0) + material->getPrePassMat(); + } + + for ( U32 i = 0; i < 4; i++ ) + if ( mChildren[i] ) + mChildren[i]->preloadMaterials(); +} + +TerrainCellMaterial* TerrCell::getMaterial() +{ + if ( !mMaterial ) + { + mMaterial = new TerrainCellMaterial; + mMaterial->init( mTerrain, mMaterials ); + } + + return mMaterial; +} + +void TerrCell::deleteMaterials() +{ + SAFE_DELETE( mMaterial ); + + for ( U32 i = 0; i < 4; i++ ) + if ( mChildren[i] ) + mChildren[i]->deleteMaterials(); +} diff --git a/Engine/source/terrain/terrCell.h b/Engine/source/terrain/terrCell.h new file mode 100644 index 000000000..f736c2915 --- /dev/null +++ b/Engine/source/terrain/terrCell.h @@ -0,0 +1,248 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TERRCELL_H_ +#define _TERRCELL_H_ + +#ifndef _GFXVERTEXBUFFER_H_ +#include "gfx/gfxVertexBuffer.h" +#endif +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif +#ifndef _TDICTIONARY_H_ +#include "core/util/tDictionary.h" +#endif +#ifndef _MORIENTEDBOX_H_ +#include "math/mOrientedBox.h" +#endif +#ifndef _BITVECTOR_H_ +#include "core/bitVector.h" +#endif + +class TerrainBlock; +class TerrainCellMaterial; +class Frustum; +class SceneRenderState; +class SceneZoneSpaceManager; + + +/// The TerrainCell vertex format optimized to +/// 32 bytes for optimal vertex cache performance. +GFXDeclareVertexFormat( TerrVertex ) +{ + /// The position. + Point3F point; + + /// The normal. + Point3F normal; + + /// The height for calculating the + /// tangent vector on the GPU. + F32 tangentZ; + + /// The empty flag state which is either + /// -1 or 1 so we can do the special + /// interpolation trick. + F32 empty; +}; + + +/// The TerrCell is a single quadrant of the terrain geometry quadtree. +class TerrCell +{ +protected: + + /// The handle to the static vertex buffer which holds the + /// vertices for this cell. + GFXVertexBufferHandle mVertexBuffer; + + /// The handle to the static primitive buffer for this cell. + /// It is only used if this cell has any empty squares + GFXPrimitiveBufferHandle mPrimBuffer; + + /// + Point2I mPoint; + + /// + U32 mSize; + + /// The level of this cell within the quadtree (of cells) where + /// zero is the root and one is a direct child of the root, etc. + U32 mLevel; + + /// Statics used in VB and PB generation. + static const U32 smVBStride; + static const U32 smMinCellSize; + static const U32 smVBSize; + static const U32 smPBSize; + static const U32 smTriCount; + + /// Triangle count for our own primitive buffer, if any + U32 mTriCount; + + /// Indicates if this cell has any empty squares + bool mHasEmpty; + + /// A list of all empty vertices for this cell + Vector mEmptyVertexList; + + /// The terrain this cell is based on. + TerrainBlock *mTerrain; + + /// The material used to render the cell. + TerrainCellMaterial *mMaterial; + + /// The bounding box of this cell in + /// TerrainBlock object space. + Box3F mBounds; + + /// The OBB of this cell in world space. + OrientedBox3F mOBB; + + /// The bounding radius of this cell. + F32 mRadius; + + /// The child cells of this one. + TerrCell *mChildren[4]; + + /// This bit flag tells us which materials effect + /// this cell and is used for optimizing rendering. + /// @see TerrainFile::mMaterialAlphaMap + U64 mMaterials; + + /// Whether this cell is fully contained inside interior zones. + bool mIsInteriorOnly; + + /// The zone overlap for this cell. + /// @note The bit for the outdoor zone is never set. + BitVector mZoneOverlap; + + /// + void _updateBounds(); + + /// Update #mOBB from the current terrain transform state. + void _updateOBB(); + + // + void _init( TerrainBlock *terrain, + const Point2I &point, + U32 size, + U32 level ); + + // + void _updateVertexBuffer(); + + // + void _updatePrimitiveBuffer(); + + // + void _updateMaterials(); + + // + bool _isVertIndexEmpty( U32 index ) const; + +public: + + TerrCell(); + virtual ~TerrCell(); + + static TerrCell* init( TerrainBlock *terrain ); + + void getRenderPrimitive( GFXPrimitive *prim, + GFXVertexBufferHandleBase *vertBuff, + GFXPrimitiveBufferHandle *primBuff ) const; + + void updateGrid( const RectI &gridRect, bool opacityOnly = false ); + + /// Update the world-space OBBs used for culling. + void updateOBBs(); + + /// + void updateZoning( const SceneZoneSpaceManager *zoneManager ); + + void cullCells( const SceneRenderState *state, + const Point3F &objLodPos, + Vector *outCells ); + + const Box3F& getBounds() const { return mBounds; } + + /// Returns the object space sphere bounds. + SphereF getSphereBounds() const { return SphereF( mBounds.getCenter(), mRadius ); } + + F32 getSqDistanceTo( const Point3F &pt ) const; + + F32 getDistanceTo( const Point3F &pt ) const; + + U64 getMaterials() const { return mMaterials; } + + /// Returns a bit vector of what zones overlap this cell. + const BitVector& getZoneOverlap() const { return mZoneOverlap; } + + /// Forces the loading of the materials for this + /// cell and all its child cells. + void preloadMaterials(); + + TerrainCellMaterial* getMaterial(); + + /// Return true if this is a leaf cell, i.e. a cell without children. + bool isLeaf() const { return !mChildren[ 0 ]; } + + /// Deletes the materials for this cell + /// and all its children. They will be + /// recreate on the next request. + void deleteMaterials(); + + U32 getSize() const { return mSize; } + + Point2I getPoint() const { return mPoint; } + + /// Initializes a primitive buffer for rendering any cell. + static void createPrimBuffer( GFXPrimitiveBufferHandle *primBuffer ); + + /// Debug Rendering + /// @{ + + /// Renders the debug bounds for this cell. + void renderBounds() const; + + /// @} +}; + +inline F32 TerrCell::getDistanceTo( const Point3F &pt ) const +{ + return ( mBounds.getCenter() - pt ).len() - mRadius; +} + +inline bool TerrCell::_isVertIndexEmpty( U32 index ) const +{ + for ( U32 i = 0; i < mEmptyVertexList.size(); ++i ) + { + if ( mEmptyVertexList[i] == index ) + { + return true; + } + } + return false; +} + +#endif // _TERRCELL_H_ diff --git a/Engine/source/terrain/terrCellMaterial.cpp b/Engine/source/terrain/terrCellMaterial.cpp new file mode 100644 index 000000000..eff8cd416 --- /dev/null +++ b/Engine/source/terrain/terrCellMaterial.cpp @@ -0,0 +1,780 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "terrain/terrCellMaterial.h" + +#include "terrain/terrData.h" +#include "terrain/terrCell.h" +#include "materials/materialFeatureTypes.h" +#include "materials/materialManager.h" +#include "terrain/terrFeatureTypes.h" +#include "terrain/terrMaterial.h" +#include "renderInstance/renderPrePassMgr.h" +#include "shaderGen/shaderGen.h" +#include "shaderGen/featureMgr.h" +#include "scene/sceneRenderState.h" +#include "materials/sceneData.h" +#include "gfx/util/screenspace.h" +#include "lighting/advanced/advancedLightBinManager.h" + + +AFTER_MODULE_INIT( MaterialManager ) +{ + Con::NotifyDelegate callabck( &TerrainCellMaterial::_updateDefaultAnisotropy ); + Con::addVariableNotify( "$pref::Video::defaultAnisotropy", callabck ); +} + +Vector TerrainCellMaterial::smAllMaterials; + +TerrainCellMaterial::TerrainCellMaterial() + : mCurrPass( 0 ), + mTerrain( NULL ), + mPrePassMat( NULL ), + mReflectMat( NULL ) +{ + smAllMaterials.push_back( this ); +} + +TerrainCellMaterial::~TerrainCellMaterial() +{ + SAFE_DELETE( mPrePassMat ); + SAFE_DELETE( mReflectMat ); + smAllMaterials.remove( this ); +} + +void TerrainCellMaterial::_updateDefaultAnisotropy() +{ + // TODO: We need to split the stateblock initialization + // from the shader constant lookup and pass setup in a + // future version of terrain materials. + // + // For now use some custom code in a horrible loop to + // change the anisotropy directly and fast. + // + + const U32 maxAnisotropy = MATMGR->getDefaultAnisotropy(); + + Vector::iterator iter = smAllMaterials.begin(); + for ( ; iter != smAllMaterials.end(); iter++ ) + { + for ( U32 p=0; p < (*iter)->mPasses.size(); p++ ) + { + Pass &pass = (*iter)->mPasses[p]; + + // Start from the existing state block. + GFXStateBlockDesc desc = pass.stateBlock->getDesc(); + + for ( U32 m=0; m < pass.materials.size(); m++ ) + { + const MaterialInfo *matInfo = pass.materials[m]; + + if ( matInfo->detailTexConst->isValid() ) + { + const S32 sampler = matInfo->detailTexConst->getSamplerRegister(); + + if ( maxAnisotropy > 1 ) + { + desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic; + desc.samplers[sampler].maxAnisotropy = maxAnisotropy; + } + else + desc.samplers[sampler].minFilter = GFXTextureFilterLinear; + } + + if ( matInfo->normalTexConst->isValid() ) + { + const S32 sampler = matInfo->normalTexConst->getSamplerRegister(); + + if ( maxAnisotropy > 1 ) + { + desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic; + desc.samplers[sampler].maxAnisotropy = maxAnisotropy; + } + else + desc.samplers[sampler].minFilter = GFXTextureFilterLinear; + } + + } // for ( U32 m=0; m < pass.materials.size(); m++ ) + + // Set the updated stateblock. + pass.stateBlock = GFX->createStateBlock( desc ); + + // Create the wireframe state blocks. + GFXStateBlockDesc wireframe( desc ); + wireframe.fillMode = GFXFillWireframe; + pass.wireframeStateBlock = GFX->createStateBlock( wireframe ); + + } // for ( U32 p=0; i < (*iter)->mPasses.size(); p++ ) + } + +} + +void TerrainCellMaterial::setTransformAndEye( const MatrixF &modelXfm, + const MatrixF &viewXfm, + const MatrixF &projectXfm, + F32 farPlane ) +{ + PROFILE_SCOPE( TerrainCellMaterial_SetTransformAndEye ); + + MatrixF modelViewProj = projectXfm * viewXfm * modelXfm; + + MatrixF invViewXfm( viewXfm ); + invViewXfm.inverse(); + Point3F eyePos = invViewXfm.getPosition(); + + MatrixF invModelXfm( modelXfm ); + invModelXfm.inverse(); + + Point3F objEyePos = eyePos; + invModelXfm.mulP( objEyePos ); + + VectorF vEye = invViewXfm.getForwardVector(); + vEye.normalize( 1.0f / farPlane ); + + for ( U32 i=0; i < mPasses.size(); i++ ) + { + Pass &pass = mPasses[i]; + + pass.consts->setSafe( pass.modelViewProjConst, modelViewProj ); + + if( pass.viewToObj->isValid() || pass.worldViewOnly->isValid() ) + { + MatrixF worldViewOnly = viewXfm * modelXfm; + + pass.consts->setSafe( pass.worldViewOnly, worldViewOnly ); + + if( pass.viewToObj->isValid() ) + { + worldViewOnly.affineInverse(); + pass.consts->set( pass.viewToObj, worldViewOnly); + } + } + + pass.consts->setSafe( pass.eyePosWorldConst, eyePos ); + pass.consts->setSafe( pass.eyePosConst, objEyePos ); + pass.consts->setSafe( pass.objTransConst, modelXfm ); + pass.consts->setSafe( pass.worldToObjConst, invModelXfm ); + pass.consts->setSafe( pass.vEyeConst, vEye ); + } +} + +TerrainCellMaterial* TerrainCellMaterial::getPrePassMat() +{ + if ( !mPrePassMat ) + { + mPrePassMat = new TerrainCellMaterial(); + mPrePassMat->init( mTerrain, mMaterials, true, false, mMaterials == 0 ); + } + + return mPrePassMat; +} + +TerrainCellMaterial* TerrainCellMaterial::getReflectMat() +{ + if ( !mReflectMat ) + { + mReflectMat = new TerrainCellMaterial(); + mReflectMat->init( mTerrain, mMaterials, false, true, true ); + } + + return mReflectMat; +} + +void TerrainCellMaterial::init( TerrainBlock *block, + U64 activeMaterials, + bool prePassMat, + bool reflectMat, + bool baseOnly ) +{ + // This isn't allowed for now. + AssertFatal( !( prePassMat && reflectMat ), "TerrainCellMaterial::init - We shouldn't get prepass and reflection in the same material!" ); + + mTerrain = block; + mMaterials = activeMaterials; + + Vector materials; + + for ( U32 i = 0; i < 64; i++ ) + { + if ( !( mMaterials & ((U64)1 << i ) ) ) + continue; + + TerrainMaterial *mat = block->getMaterial( i ); + + MaterialInfo *info = new MaterialInfo(); + info->layerId = i; + info->mat = mat; + materials.push_back( info ); + } + + mCurrPass = 0; + mPasses.clear(); + + // Ok... loop till we successfully generate all + // the shader passes for the materials. + while ( materials.size() > 0 || baseOnly ) + { + mPasses.increment(); + + if ( !_createPass( &materials, + &mPasses.last(), + mPasses.size() == 1, + prePassMat, + reflectMat, + baseOnly ) ) + { + Con::errorf( "TerrainCellMaterial::init - Failed to create pass!" ); + + // The pass failed to be generated... give up. + mPasses.last().materials.clear(); + mPasses.clear(); + for_each( materials.begin(), materials.end(), delete_pointer() ); + return; + } + + if ( baseOnly ) + break; + } + + // Cleanup any remaining matinfo. + for_each( materials.begin(), materials.end(), delete_pointer() ); + + // If we have attached mats then update them too. + if ( mPrePassMat ) + mPrePassMat->init( mTerrain, mMaterials, true, false, baseOnly ); + if ( mReflectMat ) + mReflectMat->init( mTerrain, mMaterials, false, true, baseOnly ); +} + +bool TerrainCellMaterial::_createPass( Vector *materials, + Pass *pass, + bool firstPass, + bool prePassMat, + bool reflectMat, + bool baseOnly ) +{ + if ( GFX->getPixelShaderVersion() < 3.0f ) + baseOnly = true; + + // NOTE: At maximum we only try to combine 3 materials + // into a single pass. This is sub-optimal for the simplest + // cases, but the most common case results in much fewer + // shader generation failures and permutations leading to + // faster load time and less hiccups during gameplay. + U32 matCount = getMin( 3, materials->size() ); + + Vector normalMaps; + + // See if we're currently running under the + // basic lighting manager. + // + // TODO: This seems ugly... we should trigger + // features like this differently in the future. + // + bool useBLM = dStrcmp( LIGHTMGR->getId(), "BLM" ) == 0; + + // Do we need to disable normal mapping? + const bool disableNormalMaps = MATMGR->getExclusionFeatures().hasFeature( MFT_NormalMap ) || useBLM; + + // How about parallax? + const bool disableParallaxMaps = GFX->getPixelShaderVersion() < 3.0f || + MATMGR->getExclusionFeatures().hasFeature( MFT_Parallax ); + + // Has advanced lightmap support been enabled for prepass. + bool advancedLightmapSupport = false; + if ( prePassMat ) + { + // This sucks... but it works. + AdvancedLightBinManager *lightBin; + if ( Sim::findObject( "AL_LightBinMgr", lightBin ) ) + advancedLightmapSupport = lightBin->MRTLightmapsDuringPrePass(); + } + + // Loop till we create a valid shader! + while( true ) + { + FeatureSet features; + features.addFeature( MFT_VertTransform ); + features.addFeature( MFT_TerrainBaseMap ); + + if ( prePassMat ) + { + features.addFeature( MFT_EyeSpaceDepthOut ); + features.addFeature( MFT_PrePassConditioner ); + + if ( advancedLightmapSupport ) + features.addFeature( MFT_RenderTarget1_Zero ); + } + else + { + features.addFeature( MFT_RTLighting ); + + // The HDR feature is always added... it will compile out + // if HDR is not enabled in the engine. + features.addFeature( MFT_HDROut ); + } + + // Enable lightmaps and fogging if we're in BL. + if ( reflectMat || useBLM ) + { + features.addFeature( MFT_Fog ); + features.addFeature( MFT_ForwardShading ); + } + if ( useBLM ) + features.addFeature( MFT_TerrainLightMap ); + + // The additional passes need to be lerp blended into the + // target to maintain the results of the previous passes. + if ( !firstPass ) + features.addFeature( MFT_TerrainAdditive ); + + normalMaps.clear(); + pass->materials.clear(); + + // Now add all the material layer features. + + for ( U32 i=0; i < matCount && !baseOnly; i++ ) + { + TerrainMaterial *mat = (*materials)[i]->mat; + + if ( mat == NULL ) + continue; + + // We only include materials that + // have more than a base texture. + if ( mat->getDetailSize() <= 0 || + mat->getDetailDistance() <= 0 || + mat->getDetailMap().isEmpty() ) + continue; + + S32 featureIndex = pass->materials.size(); + + features.addFeature( MFT_TerrainDetailMap, featureIndex ); + + pass->materials.push_back( (*materials)[i] ); + normalMaps.increment(); + + // Skip normal maps if we need to. + if ( !disableNormalMaps && mat->getNormalMap().isNotEmpty() ) + { + features.addFeature( MFT_TerrainNormalMap, featureIndex ); + + normalMaps.last().set( mat->getNormalMap(), + &GFXDefaultStaticNormalMapProfile, "TerrainCellMaterial::_createPass() - NormalMap" ); + + if ( normalMaps.last().getFormat() == GFXFormatDXT5 ) + features.addFeature( MFT_IsDXTnm, featureIndex ); + + // Do we need and can we do parallax mapping? + if ( !disableParallaxMaps && + mat->getParallaxScale() > 0.0f && + !mat->useSideProjection() ) + features.addFeature( MFT_TerrainParallaxMap, featureIndex ); + } + + // Is this layer got side projection? + if ( mat->useSideProjection() ) + features.addFeature( MFT_TerrainSideProject, featureIndex ); + } + + MaterialFeatureData featureData; + featureData.features = features; + featureData.materialFeatures = features; + + // Check to see how many vertex shader output + // registers we're gonna need. + U32 numTex = 0; + U32 numTexReg = 0; + for ( U32 i=0; i < features.getCount(); i++ ) + { + S32 index; + const FeatureType &type = features.getAt( i, &index ); + ShaderFeature* sf = FEATUREMGR->getByType( type ); + if ( !sf ) + continue; + + sf->setProcessIndex( index ); + ShaderFeature::Resources res = sf->getResources( featureData ); + + numTex += res.numTex; + numTexReg += res.numTexReg; + } + + // Can we build the shader? + // + // NOTE: The 10 is sort of an abitrary SM 3.0 + // limit. Its really supposed to be 11, but that + // always fails to compile so far. + // + if ( numTex < GFX->getNumSamplers() && + numTexReg <= 10 ) + { + // NOTE: We really shouldn't be getting errors building the shaders, + // but we can generate more instructions than the ps_2_x will allow. + // + // There is no way to deal with this case that i know of other than + // letting the compile fail then recovering by trying to build it + // with fewer materials. + // + // We normally disable the shader error logging so that the user + // isn't fooled into thinking there is a real bug. That is until + // we get down to a single material. If a single material case + // fails it means it cannot generate any passes at all! + const bool logErrors = matCount == 1; + GFXShader::setLogging( logErrors, true ); + + pass->shader = SHADERGEN->getShader( featureData, getGFXVertexFormat(), NULL ); + } + + // If we got a shader then we can continue. + if ( pass->shader ) + break; + + // If the material count is already 1 then this + // is a real shader error... give up! + if ( matCount <= 1 ) + return false; + + // If we failed we next try half the input materials + // so that we can more quickly arrive at a valid shader. + matCount -= matCount / 2; + } + + // Setup the constant buffer. + pass->consts = pass->shader->allocConstBuffer(); + + // Prepare the basic constants. + pass->modelViewProjConst = pass->shader->getShaderConstHandle( "$modelview" ); + pass->worldViewOnly = pass->shader->getShaderConstHandle( "$worldViewOnly" ); + pass->viewToObj = pass->shader->getShaderConstHandle( "$viewToObj" ); + pass->eyePosWorldConst = pass->shader->getShaderConstHandle( "$eyePosWorld" ); + pass->eyePosConst = pass->shader->getShaderConstHandle( "$eyePos" ); + pass->vEyeConst = pass->shader->getShaderConstHandle( "$vEye" ); + pass->layerSizeConst = pass->shader->getShaderConstHandle( "$layerSize" ); + pass->objTransConst = pass->shader->getShaderConstHandle( "$objTrans" ); + pass->worldToObjConst = pass->shader->getShaderConstHandle( "$worldToObj" ); + pass->lightInfoBufferConst = pass->shader->getShaderConstHandle( "$lightInfoBuffer" ); + pass->baseTexMapConst = pass->shader->getShaderConstHandle( "$baseTexMap" ); + pass->layerTexConst = pass->shader->getShaderConstHandle( "$layerTex" ); + pass->fogDataConst = pass->shader->getShaderConstHandle( "$fogData" ); + pass->fogColorConst = pass->shader->getShaderConstHandle( "$fogColor" ); + pass->lightMapTexConst = pass->shader->getShaderConstHandle( "$lightMapTex" ); + pass->oneOverTerrainSize = pass->shader->getShaderConstHandle( "$oneOverTerrainSize" ); + pass->squareSize = pass->shader->getShaderConstHandle( "$squareSize" ); + + // NOTE: We're assuming rtParams0 here as we know its the only + // render target we currently get in a terrain material and the + // DeferredRTLightingFeatHLSL will always use 0. + // + // This could change in the future and we would need to fix + // the ShaderFeature API to allow us to do this right. + // + pass->lightParamsConst = pass->shader->getShaderConstHandle( "$rtParams0" ); + + // Now prepare the basic stateblock. + GFXStateBlockDesc desc; + if ( !firstPass ) + { + desc.setBlend( true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha ); + + // If this is the prepass then we don't want to + // write to the last two color channels (where + // depth is usually encoded). + // + // This trick works in combination with the + // MFT_TerrainAdditive feature to lerp the + // output normal with the previous pass. + // + if ( prePassMat ) + desc.setColorWrites( true, true, false, false ); + } + + // We write to the zbuffer if this is a prepass + // material or if the prepass is disabled. + // We also write the zbuffer if we're using OpenGL, because in OpenGL the prepass + // cannot share the same zbuffer as the backbuffer. + desc.setZReadWrite( true, !MATMGR->getPrePassEnabled() || + GFX->getAdapterType() == OpenGL || + prePassMat || + reflectMat ); + + desc.samplersDefined = true; + if ( pass->baseTexMapConst->isValid() ) + desc.samplers[pass->baseTexMapConst->getSamplerRegister()] = GFXSamplerStateDesc::getWrapLinear(); + + if ( pass->layerTexConst->isValid() ) + desc.samplers[pass->layerTexConst->getSamplerRegister()] = GFXSamplerStateDesc::getClampPoint(); + + if ( pass->lightInfoBufferConst->isValid() ) + desc.samplers[pass->lightInfoBufferConst->getSamplerRegister()] = GFXSamplerStateDesc::getClampPoint(); + + if ( pass->lightMapTexConst->isValid() ) + desc.samplers[pass->lightMapTexConst->getSamplerRegister()] = GFXSamplerStateDesc::getWrapLinear(); + + const U32 maxAnisotropy = MATMGR->getDefaultAnisotropy(); + + // Finally setup the material specific shader + // constants and stateblock state. + // + // NOTE: If this changes be sure to check TerrainCellMaterial::_updateDefaultAnisotropy + // to see if it needs the same changes. + // + for ( U32 i=0; i < pass->materials.size(); i++ ) + { + MaterialInfo *matInfo = pass->materials[i]; + + matInfo->detailInfoVConst = pass->shader->getShaderConstHandle( avar( "$detailScaleAndFade%d", i ) ); + matInfo->detailInfoPConst = pass->shader->getShaderConstHandle( avar( "$detailIdStrengthParallax%d", i ) ); + + matInfo->detailTexConst = pass->shader->getShaderConstHandle( avar( "$detailMap%d", i ) ); + if ( matInfo->detailTexConst->isValid() ) + { + const S32 sampler = matInfo->detailTexConst->getSamplerRegister(); + + desc.samplers[sampler] = GFXSamplerStateDesc::getWrapLinear(); + desc.samplers[sampler].magFilter = GFXTextureFilterLinear; + desc.samplers[sampler].mipFilter = GFXTextureFilterLinear; + + if ( maxAnisotropy > 1 ) + { + desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic; + desc.samplers[sampler].maxAnisotropy = maxAnisotropy; + } + else + desc.samplers[sampler].minFilter = GFXTextureFilterLinear; + + matInfo->detailTex.set( matInfo->mat->getDetailMap(), + &GFXDefaultStaticDiffuseProfile, "TerrainCellMaterial::_createPass() - DetailMap" ); + } + + matInfo->normalTexConst = pass->shader->getShaderConstHandle( avar( "$normalMap%d", i ) ); + if ( matInfo->normalTexConst->isValid() ) + { + const S32 sampler = matInfo->normalTexConst->getSamplerRegister(); + + desc.samplers[sampler] = GFXSamplerStateDesc::getWrapLinear(); + desc.samplers[sampler].magFilter = GFXTextureFilterLinear; + desc.samplers[sampler].mipFilter = GFXTextureFilterLinear; + + if ( maxAnisotropy > 1 ) + { + desc.samplers[sampler].minFilter = GFXTextureFilterAnisotropic; + desc.samplers[sampler].maxAnisotropy = maxAnisotropy; + } + else + desc.samplers[sampler].minFilter = GFXTextureFilterLinear; + + matInfo->normalTex = normalMaps[i]; + } + } + + // Remove the materials we processed and leave the + // ones that remain for the next pass. + for ( U32 i=0; i < matCount; i++ ) + { + MaterialInfo *matInfo = materials->first(); + if ( baseOnly || pass->materials.find_next( matInfo ) == -1 ) + delete matInfo; + materials->pop_front(); + } + + // If we're doing prepass it requires some + // special stencil settings for it to work. + if ( prePassMat ) + desc.addDesc( RenderPrePassMgr::getOpaqueStenciWriteDesc( false ) ); + + // Flip the cull for reflection materials. + if ( reflectMat ) + desc.setCullMode( GFXCullCW ); + + pass->stateBlock = GFX->createStateBlock( desc ); + + // Create the wireframe state blocks. + GFXStateBlockDesc wireframe( desc ); + wireframe.fillMode = GFXFillWireframe; + pass->wireframeStateBlock = GFX->createStateBlock( wireframe ); + + return true; +} + +void TerrainCellMaterial::_updateMaterialConsts( Pass *pass ) +{ + PROFILE_SCOPE( TerrainCellMaterial_UpdateMaterialConsts ); + + for ( U32 j=0; j < pass->materials.size(); j++ ) + { + MaterialInfo *matInfo = pass->materials[j]; + + F32 detailSize = matInfo->mat->getDetailSize(); + F32 detailScale = 1.0f; + if ( !mIsZero( detailSize ) ) + detailScale = mTerrain->getWorldBlockSize() / detailSize; + + // Scale the distance by the global scalar. + const F32 distance = mTerrain->smDetailScale * matInfo->mat->getDetailDistance(); + + // NOTE: The negation of the y scale is to make up for + // my mistake early in development and passing the wrong + // y texture coord into the system. + // + // This negation fixes detail, normal, and parallax mapping + // without harming the layer id blending code. + // + // Eventually we should rework this to correct this little + // mistake, but there isn't really a hurry to. + // + Point4F detailScaleAndFade( detailScale, + -detailScale, + distance, + 0 ); + + if ( !mIsZero( distance ) ) + detailScaleAndFade.w = 1.0f / distance; + + Point3F detailIdStrengthParallax( matInfo->layerId, + matInfo->mat->getDetailStrength(), + matInfo->mat->getParallaxScale() ); + + pass->consts->setSafe( matInfo->detailInfoVConst, detailScaleAndFade ); + pass->consts->setSafe( matInfo->detailInfoPConst, detailIdStrengthParallax ); + } +} + +bool TerrainCellMaterial::setupPass( const SceneRenderState *state, + const SceneData &sceneData ) +{ + PROFILE_SCOPE( TerrainCellMaterial_SetupPass ); + + if ( mCurrPass >= mPasses.size() ) + { + mCurrPass = 0; + return false; + } + + Pass &pass = mPasses[mCurrPass]; + + _updateMaterialConsts( &pass ); + + if ( pass.baseTexMapConst->isValid() ) + GFX->setTexture( pass.baseTexMapConst->getSamplerRegister(), mTerrain->mBaseTex.getPointer() ); + + if ( pass.layerTexConst->isValid() ) + GFX->setTexture( pass.layerTexConst->getSamplerRegister(), mTerrain->mLayerTex.getPointer() ); + + if ( pass.lightMapTexConst->isValid() ) + GFX->setTexture( pass.lightMapTexConst->getSamplerRegister(), mTerrain->getLightMapTex() ); + + if ( sceneData.wireframe ) + GFX->setStateBlock( pass.wireframeStateBlock ); + else + GFX->setStateBlock( pass.stateBlock ); + + GFX->setShader( pass.shader ); + GFX->setShaderConstBuffer( pass.consts ); + + // Let the light manager prepare any light stuff it needs. + LIGHTMGR->setLightInfo( NULL, + NULL, + sceneData, + state, + mCurrPass, + pass.consts ); + + for ( U32 i=0; i < pass.materials.size(); i++ ) + { + MaterialInfo *matInfo = pass.materials[i]; + + if ( matInfo->detailTexConst->isValid() ) + GFX->setTexture( matInfo->detailTexConst->getSamplerRegister(), matInfo->detailTex ); + if ( matInfo->normalTexConst->isValid() ) + GFX->setTexture( matInfo->normalTexConst->getSamplerRegister(), matInfo->normalTex ); + } + + pass.consts->setSafe( pass.layerSizeConst, (F32)mTerrain->mLayerTex.getWidth() ); + + if ( pass.oneOverTerrainSize->isValid() ) + { + F32 oneOverTerrainSize = 1.0f / mTerrain->getWorldBlockSize(); + pass.consts->set( pass.oneOverTerrainSize, oneOverTerrainSize ); + } + + pass.consts->setSafe( pass.squareSize, mTerrain->getSquareSize() ); + + if ( pass.fogDataConst->isValid() ) + { + Point3F fogData; + fogData.x = sceneData.fogDensity; + fogData.y = sceneData.fogDensityOffset; + fogData.z = sceneData.fogHeightFalloff; + pass.consts->set( pass.fogDataConst, fogData ); + } + + pass.consts->setSafe( pass.fogColorConst, sceneData.fogColor ); + + if ( pass.lightInfoBufferConst->isValid() && + pass.lightParamsConst->isValid() ) + { + if ( !mLightInfoTarget ) + mLightInfoTarget = NamedTexTarget::find( "lightinfo" ); + + GFXTextureObject *texObject = mLightInfoTarget->getTexture(); + + // TODO: Sometimes during reset of the light manager we get a + // NULL texture here. This is corrected on the next frame, but + // we should still investigate why that happens. + + if ( texObject ) + { + GFX->setTexture( pass.lightInfoBufferConst->getSamplerRegister(), texObject ); + + const Point3I &targetSz = texObject->getSize(); + const RectI &targetVp = mLightInfoTarget->getViewport(); + Point4F rtParams; + ScreenSpace::RenderTargetParameters(targetSz, targetVp, rtParams); + pass.consts->setSafe( pass.lightParamsConst, rtParams ); + } + } + + ++mCurrPass; + return true; +} + +BaseMatInstance* TerrainCellMaterial::getShadowMat() +{ + // Find our material which has some settings + // defined on it in script. + Material *mat = MATMGR->getMaterialDefinitionByName( "AL_DefaultShadowMaterial" ); + + // Create the material instance adding the feature which + // handles rendering terrain cut outs. + FeatureSet features = MATMGR->getDefaultFeatures(); + BaseMatInstance *matInst = mat->createMatInstance(); + if ( !matInst->init( features, getGFXVertexFormat() ) ) + { + delete matInst; + matInst = NULL; + } + + return matInst; +} + diff --git a/Engine/source/terrain/terrCellMaterial.h b/Engine/source/terrain/terrCellMaterial.h new file mode 100644 index 000000000..8fa395673 --- /dev/null +++ b/Engine/source/terrain/terrCellMaterial.h @@ -0,0 +1,199 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TERRCELLMATERIAL_H_ +#define _TERRCELLMATERIAL_H_ + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _MATTEXTURETARGET_H_ +#include "materials/matTextureTarget.h" +#endif +#ifndef _GFXTEXTUREHANDLE_H_ +#include "gfx/gfxTextureHandle.h" +#endif +#ifndef _GFXSHADER_H_ +#include "gfx/gfxShader.h" +#endif +#ifndef _GFXSTATEBLOCK_H_ +#include "gfx/gfxStateBlock.h" +#endif + + +class SceneRenderState; +struct SceneData; +class TerrainMaterial; +class TerrainBlock; +class BaseMatInstance; + + +/// This is a complex material which holds one or more +/// optimized shaders for rendering a single cell. +class TerrainCellMaterial +{ +protected: + + class MaterialInfo + { + public: + + MaterialInfo() + { + } + + ~MaterialInfo() + { + } + + TerrainMaterial *mat; + U32 layerId; + + GFXShaderConstHandle *detailTexConst; + GFXTexHandle detailTex; + + GFXShaderConstHandle *normalTexConst; + GFXTexHandle normalTex; + + GFXShaderConstHandle *detailInfoVConst; + GFXShaderConstHandle *detailInfoPConst; + }; + + class Pass + { + public: + + Pass() + : shader( NULL ) + { + } + + ~Pass() + { + for ( U32 i=0; i < materials.size(); i++ ) + delete materials[i]; + } + + Vector materials; + + /// + GFXShader *shader; + + GFXShaderConstBufferRef consts; + + GFXStateBlockRef stateBlock; + GFXStateBlockRef wireframeStateBlock; + + GFXShaderConstHandle *modelViewProjConst; + GFXShaderConstHandle *worldViewOnly; + GFXShaderConstHandle *viewToObj; + + GFXShaderConstHandle *eyePosWorldConst; + GFXShaderConstHandle *eyePosConst; + + GFXShaderConstHandle *objTransConst; + GFXShaderConstHandle *worldToObjConst; + GFXShaderConstHandle *vEyeConst; + + GFXShaderConstHandle *layerSizeConst; + GFXShaderConstHandle *lightParamsConst; + GFXShaderConstHandle *lightInfoBufferConst; + + GFXShaderConstHandle *baseTexMapConst; + GFXShaderConstHandle *layerTexConst; + + GFXShaderConstHandle *lightMapTexConst; + + GFXShaderConstHandle *squareSize; + GFXShaderConstHandle *oneOverTerrainSize; + + GFXShaderConstHandle *fogDataConst; + GFXShaderConstHandle *fogColorConst; + }; + + TerrainBlock *mTerrain; + + U64 mMaterials; + + Vector mPasses; + + U32 mCurrPass; + + GFXTexHandle mBaseMapTexture; + + GFXTexHandle mLayerMapTexture; + + NamedTexTargetRef mLightInfoTarget; + + /// The prepass material for this material. + TerrainCellMaterial *mPrePassMat; + + /// The reflection material for this material. + TerrainCellMaterial *mReflectMat; + + /// A vector of all terrain cell materials loaded in the system. + static Vector smAllMaterials; + + bool _createPass( Vector *materials, + Pass *pass, + bool firstPass, + bool prePassMat, + bool reflectMat, + bool baseOnly ); + + void _updateMaterialConsts( Pass *pass ); + +public: + + TerrainCellMaterial(); + ~TerrainCellMaterial(); + + void init( TerrainBlock *block, + U64 activeMaterials, + bool prePassMat = false, + bool reflectMat = false, + bool baseOnly = false ); + + /// Returns a prepass material from this material. + TerrainCellMaterial* getPrePassMat(); + + /// Returns the reflection material from this material. + TerrainCellMaterial* getReflectMat(); + + void setTransformAndEye( const MatrixF &modelXfm, + const MatrixF &viewXfm, + const MatrixF &projectXfm, + F32 farPlane ); + + /// + bool setupPass( const SceneRenderState *state, + const SceneData &sceneData ); + + /// + static BaseMatInstance* getShadowMat(); + + /// + static void _updateDefaultAnisotropy(); +}; + +#endif // _TERRCELLMATERIAL_H_ + diff --git a/Engine/source/terrain/terrCollision.cpp b/Engine/source/terrain/terrCollision.cpp new file mode 100644 index 000000000..9d6f68ee8 --- /dev/null +++ b/Engine/source/terrain/terrCollision.cpp @@ -0,0 +1,983 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "terrain/terrCollision.h" + +#include "terrain/terrData.h" +#include "collision/abstractPolyList.h" +#include "collision/collision.h" + + +const F32 TerrainThickness = 0.5f; +static const U32 MaxExtent = 256; +#define MAX_FLOAT 1e20f + + +//---------------------------------------------------------------------------- + +Convex sTerrainConvexList; + +// Number of vertices followed by point index +S32 sVertexList[5][5] = { + { 3, 1,2,3 }, // 135 B + { 3, 0,1,3 }, // 135 A + { 3, 0,2,3 }, // 45 B + { 3, 0,1,2 }, // 45 A + { 4, 0,1,2,3 } // Convex square +}; + +// Number of edges followed by edge index pairs +S32 sEdgeList45[16][11] = { + { 0 }, // + { 0 }, + { 0 }, + { 1, 0,1 }, // 0-1 + { 0 }, + { 1, 0,1 }, // 0-2 + { 1, 0,1 }, // 1-2 + { 3, 0,1,1,2,2,0 }, // 0-1,1-2,2-0 + { 0 }, + { 0,}, // + { 0 }, + { 1, 0,1 }, // 0-1, + { 0, }, // + { 1, 0,1 }, // 0-2, + { 1, 0,1 }, // 1-2 + { 3, 0,1,1,2,0,2 }, +}; + +S32 sEdgeList135[16][11] = { + { 0 }, + { 0 }, + { 0 }, + { 1, 0,1 }, // 0-1 + { 0 }, + { 0 }, + { 1, 0,1 }, // 1-2 + { 2, 0,1,1,2 }, // 0-1,1-2 + { 0 }, + { 0, }, // + { 1, 0,1 }, // 1-3 + { 2, 0,1,1,2 }, // 0-1,1-3, + { 0 }, // + { 0 }, // + { 2, 0,1,2,0 }, // 1-2,3-1 + { 3, 0,1,1,2,1,3 }, +}; + +// On split squares, the FaceA diagnal is also removed +S32 sEdgeList45A[16][11] = { + { 0 }, // + { 0 }, + { 0 }, + { 1, 0,1 }, // 0-1 + { 0 }, + { 0 }, // + { 1, 0,1 }, // 1-2 + { 2, 0,1,1,2 }, // 0-1,1-2 + { 0 }, + { 0,}, // + { 0 }, + { 1, 0,1 }, // 0-1 + { 0, }, // + { 0, 0,1 }, // + { 1, 0,1 }, // 1-2 + { 3, 0,1,1,2 }, +}; + +S32 sEdgeList135A[16][11] = { + { 0 }, + { 0 }, + { 0 }, + { 1, 0,1 }, // 0-1 + { 0 }, + { 0 }, + { 1, 0,1 }, // 1-2 + { 2, 0,1,1,2 }, // 0-1,1-2 + { 0 }, + { 0 }, // + { 0 }, // + { 1, 0,1 }, // 0-1 + { 0 }, // + { 0 }, // + { 1, 0,1 }, // 1-2 + { 3, 0,1,1,2 }, +}; + + +// Number of faces followed by normal index and vertices +S32 sFaceList45[16][9] = { + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 1, 0,0,1,2 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 1, 1,0,1,2 }, + { 0 }, + { 2, 0,0,1,2, 1,0,2,3 }, +}; + +S32 sFaceList135[16][9] = { + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 1, 0,0,1,2 }, + { 0 }, + { 0 }, + { 1, 1,0,1,2 }, + { 2, 0,0,1,3, 1,1,2,3 }, +}; + + +TerrainConvex::TerrainConvex() +{ + mType = TerrainConvexType; +} + +TerrainConvex::TerrainConvex( const TerrainConvex &cv ) +{ + mType = TerrainConvexType; + + // Only a partial copy... + mObject = cv.mObject; + split45 = cv.split45; + squareId = cv.squareId; + material = cv.material; + point[0] = cv.point[0]; + point[1] = cv.point[1]; + point[2] = cv.point[2]; + point[3] = cv.point[3]; + normal[0] = cv.normal[0]; + normal[1] = cv.normal[1]; + box = cv.box; +} + +Box3F TerrainConvex::getBoundingBox() const +{ + return box; +} + +Box3F TerrainConvex::getBoundingBox(const MatrixF&, const Point3F& ) const +{ + // Function should not be called.... + return box; +} + +Point3F TerrainConvex::support(const VectorF& v) const +{ + S32 *vp; + if (halfA) + vp = square ? sVertexList[(split45 << 1) | 1]: sVertexList[4]; + else + vp = square ? sVertexList[(split45 << 1)] : sVertexList[4]; + + S32 *ve = vp + vp[0] + 1; + const Point3F *bp = &point[vp[1]]; + F32 bd = mDot(*bp,v); + for (vp += 2; vp < ve; vp++) { + const Point3F* cp = &point[*vp]; + F32 dd = mDot(*cp,v); + if (dd > bd) { + bd = dd; + bp = cp; + } + } + return *bp; +} + +inline bool isOnPlane(Point3F& p,PlaneF& plane) +{ + F32 dist = mDot(plane,p) + plane.d; + return dist < 0.1 && dist > -0.1; +} + +void TerrainConvex::getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf) +{ + U32 i; + cf->material = 0; + cf->object = mObject; + + // Plane is normal n + support point + PlaneF plane; + plane.set(support(n),n); + S32 vertexCount = cf->mVertexList.size(); + + // Emit vertices on the plane + S32* vertexListPointer; + if (halfA) + vertexListPointer = square ? sVertexList[(split45 << 1) | 1]: sVertexList[4]; + else + vertexListPointer = square ? sVertexList[(split45 << 1)] : sVertexList[4]; + + S32 pm = 0; + S32 numVerts = *vertexListPointer; + vertexListPointer += 1; + for (i = 0; i < numVerts; i++) + { + Point3F& cp = point[vertexListPointer[i]]; + cf->mVertexList.increment(); + mat.mulP(cp,&cf->mVertexList.last()); + pm |= 1 << vertexListPointer[i]; + } + + // Emit Edges + S32* ep = (square && halfA)? + (split45 ? sEdgeList45A[pm]: sEdgeList135A[pm]): + (split45 ? sEdgeList45[pm]: sEdgeList135[pm]); + + S32 numEdges = *ep; + S32 edgeListStart = cf->mEdgeList.size(); + cf->mEdgeList.increment(numEdges); + ep += 1; + for (i = 0; i < numEdges; i++) + { + cf->mEdgeList[edgeListStart + i].vertex[0] = vertexCount + ep[i * 2 + 0]; + cf->mEdgeList[edgeListStart + i].vertex[1] = vertexCount + ep[i * 2 + 1]; + } + + // Emit faces + S32* fp = split45 ? sFaceList45[pm]: sFaceList135[pm]; + S32 numFaces = *fp; + fp += 1; + S32 faceListStart = cf->mFaceList.size(); + cf->mFaceList.increment(numFaces); + for (i = 0; i < numFaces; i++) + { + cf->mFaceList[faceListStart + i].normal = normal[fp[i * 4 + 0]]; + cf->mFaceList[faceListStart + i].vertex[0] = vertexCount + fp[i * 4 + 1]; + cf->mFaceList[faceListStart + i].vertex[1] = vertexCount + fp[i * 4 + 2]; + cf->mFaceList[faceListStart + i].vertex[2] = vertexCount + fp[i * 4 + 3]; + } +} + + +void TerrainConvex::getPolyList(AbstractPolyList* list) +{ + list->setTransform(&mObject->getTransform(), mObject->getScale()); + list->setObject(mObject); + + // Emit vertices + U32 array[4]; + U32 curr = 0; + + S32 numVerts; + S32* vertsStart; + if (halfA) + { + numVerts = square ? sVertexList[(split45 << 1) | 1][0] : sVertexList[4][0]; + vertsStart = square ? &sVertexList[(split45 << 1) | 1][1] : &sVertexList[4][1]; + } + else + { + numVerts = square ? sVertexList[(split45 << 1)][0] : sVertexList[4][0]; + vertsStart = square ? &sVertexList[(split45 << 1)][1] : &sVertexList[4][1]; + } + + S32 pointMask = 0; + for (U32 i = 0; i < numVerts; i++) { + const Point3F& cp = point[vertsStart[i]]; + array[curr++] = list->addPoint(cp); + pointMask |= (1 << vertsStart[i]); + } + + S32 numFaces = split45 ? sFaceList45[pointMask][0] : sFaceList135[pointMask][0]; + S32* faceStart = split45 ? &sFaceList45[pointMask][1] : &sFaceList135[pointMask][1]; + for (U32 j = 0; j < numFaces; j++) { + S32 plane = faceStart[0]; + S32 v0 = faceStart[1]; + S32 v1 = faceStart[2]; + S32 v2 = faceStart[3]; + + list->begin(0, plane); + list->vertex(array[v0]); + list->vertex(array[v1]); + list->vertex(array[v2]); + list->plane(array[v0], array[v1], array[v2]); + list->end(); + + faceStart += 4; + } +} + + +//---------------------------------------------------------------------------- + +void TerrainBlock::buildConvex(const Box3F& box,Convex* convex) +{ + PROFILE_SCOPE( TerrainBlock_buildConvex ); + + sTerrainConvexList.collectGarbage(); + + // First check to see if the query misses the + // terrain elevation range. + const Point3F &terrainPos = getPosition(); + if ( box.maxExtents.z - terrainPos.z < -TerrainThickness || + box.minExtents.z - terrainPos.z > fixedToFloat( mFile->getMaxHeight() ) ) + return; + + // Transform the bounding sphere into the object's coord space. Note that this + // not really optimal. + Box3F osBox = box; + mWorldToObj.mul(osBox); + AssertWarn(mObjScale == Point3F(1, 1, 1), "Error, handle the scale transform on the terrain"); + + S32 xStart = (S32)mFloor( osBox.minExtents.x / mSquareSize ); + S32 xEnd = (S32)mCeil ( osBox.maxExtents.x / mSquareSize ); + S32 yStart = (S32)mFloor( osBox.minExtents.y / mSquareSize ); + S32 yEnd = (S32)mCeil ( osBox.maxExtents.y / mSquareSize ); + S32 xExt = xEnd - xStart; + if (xExt > MaxExtent) + xExt = MaxExtent; + + U16 heightMax = floatToFixed(osBox.maxExtents.z); + U16 heightMin = (osBox.minExtents.z < 0)? 0: floatToFixed(osBox.minExtents.z); + + const U32 BlockMask = mFile->mSize - 1; + + for ( S32 y = yStart; y < yEnd; y++ ) + { + S32 yi = y & BlockMask; + + // + for ( S32 x = xStart; x < xEnd; x++ ) + { + S32 xi = x & BlockMask; + + const TerrainSquare *sq = mFile->findSquare( 0, xi, yi ); + + if ( x != xi || y != yi ) + continue; + + // holes only in the primary terrain block + if ( ( ( sq->flags & TerrainSquare::Empty ) && x == xi && y == yi ) || + sq->minHeight > heightMax || + sq->maxHeight < heightMin ) + continue; + + U32 sid = (x << 16) + (y & ((1 << 16) - 1)); + Convex *cc = 0; + + // See if the square already exists as part of the working set. + CollisionWorkingList& wl = convex->getWorkingList(); + for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) + if (itr->mConvex->getType() == TerrainConvexType && + static_cast(itr->mConvex)->squareId == sid) { + cc = itr->mConvex; + break; + } + + if (cc) + continue; + + // Create a new convex. + TerrainConvex* cp = new TerrainConvex; + sTerrainConvexList.registerObject(cp); + convex->addToWorkingList(cp); + cp->halfA = true; + cp->square = 0; + cp->mObject = this; + cp->squareId = sid; + cp->material = mFile->getLayerIndex( xi, yi ); + cp->box.minExtents.set((F32)(x * mSquareSize), (F32)(y * mSquareSize), fixedToFloat( sq->minHeight )); + cp->box.maxExtents.x = cp->box.minExtents.x + mSquareSize; + cp->box.maxExtents.y = cp->box.minExtents.y + mSquareSize; + cp->box.maxExtents.z = fixedToFloat( sq->maxHeight ); + mObjToWorld.mul(cp->box); + + // Build points + Point3F* pos = cp->point; + for (int i = 0; i < 4 ; i++,pos++) { + S32 dx = i >> 1; + S32 dy = dx ^ (i & 1); + pos->x = (F32)((x + dx) * mSquareSize); + pos->y = (F32)((y + dy) * mSquareSize); + pos->z = fixedToFloat( mFile->getHeight(xi + dx, yi + dy) ); + } + + // Build normals, then split into two Convex objects if the + // square is concave + if ((cp->split45 = sq->flags & TerrainSquare::Split45) == true) { + VectorF *vp = cp->point; + mCross(vp[0] - vp[1],vp[2] - vp[1],&cp->normal[0]); + cp->normal[0].normalize(); + mCross(vp[2] - vp[3],vp[0] - vp[3],&cp->normal[1]); + cp->normal[1].normalize(); + if (mDot(vp[3] - vp[1],cp->normal[0]) > 0) { + TerrainConvex* nc = new TerrainConvex(*cp); + sTerrainConvexList.registerObject(nc); + convex->addToWorkingList(nc); + nc->halfA = false; + nc->square = cp; + cp->square = nc; + } + } + else { + VectorF *vp = cp->point; + mCross(vp[3] - vp[0],vp[1] - vp[0],&cp->normal[0]); + cp->normal[0].normalize(); + mCross(vp[1] - vp[2],vp[3] - vp[2],&cp->normal[1]); + cp->normal[1].normalize(); + if (mDot(vp[2] - vp[0],cp->normal[0]) > 0) { + TerrainConvex* nc = new TerrainConvex(*cp); + sTerrainConvexList.registerObject(nc); + convex->addToWorkingList(nc); + nc->halfA = false; + nc->square = cp; + cp->square = nc; + } + } + } + } +} + +static inline void swap(U32*& a,U32*& b) +{ + U32* t = b; + b = a; + a = t; +} + +static void clrbuf(U32* p, U32 s) +{ + U32* e = p + s; + while (p != e) + *p++ = U32_MAX; +} + +bool TerrainBlock::buildPolyList(PolyListContext, AbstractPolyList* polyList, const Box3F &box, const SphereF&) +{ + PROFILE_SCOPE( TerrainBlock_buildPolyList ); + + // First check to see if the query misses the + // terrain elevation range. + const Point3F &terrainPos = getPosition(); + if ( box.maxExtents.z - terrainPos.z < -TerrainThickness || + box.minExtents.z - terrainPos.z > fixedToFloat( mFile->getMaxHeight() ) ) + return false; + + // Transform the bounding sphere into the object's coord + // space. Note that this is really optimal. + Box3F osBox = box; + mWorldToObj.mul(osBox); + AssertWarn(mObjScale == Point3F::One, "Error, handle the scale transform on the terrain"); + + // Setup collision state data + polyList->setTransform(&getTransform(), getScale()); + polyList->setObject(this); + + S32 xStart = (S32)mFloor( osBox.minExtents.x / mSquareSize ); + S32 xEnd = (S32)mCeil ( osBox.maxExtents.x / mSquareSize ); + S32 yStart = (S32)mFloor( osBox.minExtents.y / mSquareSize ); + S32 yEnd = (S32)mCeil ( osBox.maxExtents.y / mSquareSize ); + if ( xStart < 0 ) + xStart = 0; + S32 xExt = xEnd - xStart; + if ( xExt > MaxExtent ) + xExt = MaxExtent; + xEnd = xStart + xExt; + + U32 heightMax = floatToFixed(osBox.maxExtents.z); + U32 heightMin = (osBox.minExtents.z < 0.0f)? 0.0f: floatToFixed(osBox.minExtents.z); + + // Index of shared points + U32 bp[(MaxExtent + 1) * 2],*vb[2]; + vb[0] = &bp[0]; + vb[1] = &bp[xExt + 1]; + clrbuf(vb[1],xExt + 1); + + const U32 BlockMask = mFile->mSize - 1; + + bool emitted = false; + for (S32 y = yStart; y < yEnd; y++) + { + S32 yi = y & BlockMask; + + swap(vb[0],vb[1]); + clrbuf(vb[1],xExt + 1); + // + for (S32 x = xStart; x < xEnd; x++) + { + S32 xi = x & BlockMask; + const TerrainSquare *sq = mFile->findSquare( 0, xi, yi ); + + if ( x != xi || y != yi ) + continue; + + // holes only in the primary terrain block + if ( ( ( sq->flags & TerrainSquare::Empty ) && x == xi && y == yi ) || + sq->minHeight > heightMax || + sq->maxHeight < heightMin ) + continue; + + emitted = true; + + // Add the missing points + U32 vi[5]; + for (int i = 0; i < 4 ; i++) + { + S32 dx = i >> 1; + S32 dy = dx ^ (i & 1); + U32* vp = &vb[dy][x - xStart + dx]; + if (*vp == U32_MAX) + { + Point3F pos; + pos.x = (F32)((x + dx) * mSquareSize); + pos.y = (F32)((y + dy) * mSquareSize); + pos.z = fixedToFloat( mFile->getHeight(xi + dx, yi + dy) ); + *vp = polyList->addPoint(pos); + } + vi[i] = *vp; + } + + U32* vp = &vi[0]; + if ( !( sq->flags & TerrainSquare::Split45 ) ) + vi[4] = vi[0], vp++; + + BaseMatInstance *material = NULL; //getMaterialInst( xi, yi ); + U32 surfaceKey = ((xi << 16) + yi) << 1; + polyList->begin(material,surfaceKey); + polyList->vertex(vp[0]); + polyList->vertex(vp[1]); + polyList->vertex(vp[2]); + polyList->plane(vp[0],vp[1],vp[2]); + polyList->end(); + polyList->begin(material,surfaceKey + 1); + polyList->vertex(vp[0]); + polyList->vertex(vp[2]); + polyList->vertex(vp[3]); + polyList->plane(vp[0],vp[2],vp[3]); + polyList->end(); + } + } + + return emitted; +} + +//---------------------------------------------------------------------------- + +static F32 calcInterceptV(F32 vStart, F32 invDeltaV, F32 intercept) +{ + return (intercept - vStart) * invDeltaV; +} + +static F32 calcInterceptNone(F32, F32, F32) +{ + return MAX_FLOAT; +} + +static F32 (*calcInterceptX)(F32, F32, F32); +static F32 (*calcInterceptY)(F32, F32, F32); + +static U32 lineCount; +static Point3F lineStart, lineEnd; + +bool TerrainBlock::castRay(const Point3F &start, const Point3F &end, RayInfo *info) +{ + PROFILE_SCOPE( TerrainBlock_castRay ); + + if ( !castRayI(start, end, info, false) ) + return false; + + // Set intersection point. + info->setContactPoint( start, end ); + + // Set material at contact point. + Point2I gridPos = getGridPos( info->point ); + U8 layer = mFile->getLayerIndex( gridPos.x, gridPos.y ); + info->material = mFile->getMaterialMapping( layer ); + + return true; +} + +bool TerrainBlock::castRayI(const Point3F &start, const Point3F &end, RayInfo *info, bool collideEmpty) +{ + lineCount = 0; + lineStart = start; + lineEnd = end; + + info->object = this; + + if(start.x == end.x && start.y == end.y) + { + if (end.z == start.z) + return false; + + F32 height; + if(!getNormalAndHeight(Point2F(start.x, start.y), &info->normal, &height, true)) + return false; + + F32 t = (height - start.z) / (end.z - start.z); + if(t < 0 || t > 1) + return false; + info->t = t; + + return true; + } + + F32 invBlockWorldSize = 1 / getWorldBlockSize(); + + Point3F pStart(start.x * invBlockWorldSize, start.y * invBlockWorldSize, start.z); + Point3F pEnd(end.x * invBlockWorldSize, end.y * invBlockWorldSize, end.z); + + int blockX = (S32)mFloor(pStart.x); + int blockY = (S32)mFloor(pStart.y); + + int dx, dy; + + F32 invDeltaX; + if(pEnd.x == pStart.x) + { + calcInterceptX = calcInterceptNone; + invDeltaX = 0; + dx = 0; + } + else + { + invDeltaX = 1 / (pEnd.x - pStart.x); + calcInterceptX = calcInterceptV; + if(pEnd.x < pStart.x) + dx = -1; + else + dx = 1; + } + + F32 invDeltaY; + if(pEnd.y == pStart.y) + { + calcInterceptY = calcInterceptNone; + invDeltaY = 0; + dy = 0; + } + else + { + invDeltaY = 1 / (pEnd.y - pStart.y); + calcInterceptY = calcInterceptV; + if(pEnd.y < pStart.y) + dy = -1; + else + dy = 1; + } + + const U32 BlockSquareWidth = mFile->mSize; + const U32 GridLevels = mFile->mGridLevels; + + F32 startT = 0; + for(;;) + { + F32 nextXInt = calcInterceptX(pStart.x, invDeltaX, (F32)(blockX + (dx == 1))); + F32 nextYInt = calcInterceptY(pStart.y, invDeltaY, (F32)(blockY + (dy == 1))); + + F32 intersectT = 1; + + if(nextXInt < intersectT) + intersectT = nextXInt; + if(nextYInt < intersectT) + intersectT = nextYInt; + + if ( castRayBlock( pStart, + pEnd, + Point2I( blockX * BlockSquareWidth, + blockY * BlockSquareWidth ), + GridLevels, + invDeltaX, + invDeltaY, + startT, + intersectT, + info, + collideEmpty ) ) + { + info->normal.z *= BlockSquareWidth * mSquareSize; + info->normal.normalize(); + return true; + } + + startT = intersectT; + if(intersectT >= 1) + break; + if(nextXInt < nextYInt) + blockX += dx; + else if(nextYInt < nextXInt) + blockY += dy; + else + { + blockX += dx; + blockY += dy; + } + } + + return false; +} + +struct TerrLOSStackNode +{ + F32 startT; + F32 endT; + Point2I blockPos; + U32 level; +}; + +bool TerrainBlock::castRayBlock( const Point3F &pStart, + const Point3F &pEnd, + const Point2I &aBlockPos, + U32 aLevel, + F32 invDeltaX, + F32 invDeltaY, + F32 aStartT, + F32 aEndT, + RayInfo *info, + bool collideEmpty ) +{ + const U32 BlockSquareWidth = mFile->mSize; + const U32 GridLevels = mFile->mGridLevels; + const U32 BlockMask = mFile->mSize - 1; + + F32 invBlockSize = 1 / F32( BlockSquareWidth ); + + static Vector stack; + stack.setSize( GridLevels * 3 + 1 ); + U32 stackSize = 1; + + stack[0].startT = aStartT; + stack[0].endT = aEndT; + stack[0].blockPos = aBlockPos; + stack[0].level = aLevel; + + if( !aBlockPos.isZero() ) + return false; + + while(stackSize--) + { + TerrLOSStackNode *sn = stack.address() + stackSize; + U32 level = sn->level; + F32 startT = sn->startT; + F32 endT = sn->endT; + Point2I blockPos = sn->blockPos; + + const TerrainSquare *sq = mFile->findSquare( level, blockPos.x, blockPos.y ); + + F32 startZ = startT * (pEnd.z - pStart.z) + pStart.z; + F32 endZ = endT * (pEnd.z - pStart.z) + pStart.z; + + F32 minHeight = fixedToFloat(sq->minHeight); + if(startZ <= minHeight && endZ <= minHeight) + continue; + + F32 maxHeight = fixedToFloat(sq->maxHeight); + if(startZ >= maxHeight && endZ >= maxHeight) + continue; + + if ( !collideEmpty && ( sq->flags & TerrainSquare::Empty ) && + blockPos.x == ( blockPos.x & BlockMask ) && blockPos.y == ( blockPos.y & BlockMask )) + continue; + + if(level == 0) + { + F32 xs = blockPos.x * invBlockSize; + F32 ys = blockPos.y * invBlockSize; + + F32 zBottomLeft = fixedToFloat( mFile->getHeight(blockPos.x, blockPos.y) ); + F32 zBottomRight= fixedToFloat( mFile->getHeight(blockPos.x + 1, blockPos.y) ); + F32 zTopLeft = fixedToFloat( mFile->getHeight(blockPos.x, blockPos.y + 1) ); + F32 zTopRight = fixedToFloat( mFile->getHeight(blockPos.x + 1, blockPos.y + 1) ); + + PlaneF p1, p2; + PlaneF divider; + Point3F planePoint; + + if(sq->flags & TerrainSquare::Split45) + { + p1.set(zBottomLeft - zBottomRight, zBottomRight - zTopRight, invBlockSize); + p2.set(zTopLeft - zTopRight, zBottomLeft - zTopLeft, invBlockSize); + planePoint.set(xs, ys, zBottomLeft); + divider.x = 1; + divider.y = -1; + divider.z = 0; + } + else + { + p1.set(zTopLeft - zTopRight, zBottomRight - zTopRight, invBlockSize); + p2.set(zBottomLeft - zBottomRight, zBottomLeft - zTopLeft, invBlockSize); + planePoint.set(xs + invBlockSize, ys, zBottomRight); + divider.x = 1; + divider.y = 1; + divider.z = 0; + } + p1.setPoint(planePoint); + p2.setPoint(planePoint); + divider.setPoint(planePoint); + + F32 t1 = p1.intersect(pStart, pEnd); + F32 t2 = p2.intersect(pStart, pEnd); + F32 td = divider.intersect(pStart, pEnd); + + F32 dStart = divider.distToPlane(pStart); + F32 dEnd = divider.distToPlane(pEnd); + + // see if the line crosses the divider + if((dStart >= 0 && dEnd < 0) || (dStart < 0 && dEnd >= 0)) + { + if(dStart < 0) + { + F32 temp = t1; + t1 = t2; + t2 = temp; + } + if(t1 >= startT && t1 && t1 <= td && t1 <= endT) + { + info->t = t1; + info->normal = p1; + return true; + } + if(t2 >= td && t2 >= startT && t2 <= endT) + { + info->t = t2; + info->normal = p2; + return true; + } + } + else + { + F32 t; + if(dStart >= 0) { + t = t1; + info->normal = p1; + } + else { + t = t2; + info->normal = p2; + } + if(t >= startT && t <= endT) + { + info->t = t; + return true; + } + } + continue; + } + int subSqWidth = 1 << (level - 1); + F32 xIntercept = (blockPos.x + subSqWidth) * invBlockSize; + F32 xInt = calcInterceptX(pStart.x, invDeltaX, xIntercept); + F32 yIntercept = (blockPos.y + subSqWidth) * invBlockSize; + F32 yInt = calcInterceptY(pStart.y, invDeltaY, yIntercept); + + F32 startX = startT * (pEnd.x - pStart.x) + pStart.x; + F32 startY = startT * (pEnd.y - pStart.y) + pStart.y; + + if(xInt < startT) + xInt = MAX_FLOAT; + if(yInt < startT) + yInt = MAX_FLOAT; + + U32 x0 = (startX > xIntercept) * subSqWidth; + U32 y0 = (startY > yIntercept) * subSqWidth; + U32 x1 = subSqWidth - x0; + U32 y1 = subSqWidth - y0; + U32 nextLevel = level - 1; + + // push the items on the stack in reverse order of processing + if(xInt > endT && yInt > endT) + { + // only test the square the point started in: + stack[stackSize].blockPos.set(blockPos.x + x0, blockPos.y + y0); + stack[stackSize].level = nextLevel; + stackSize++; + } + else if(xInt < yInt) + { + F32 nextIntersect = endT; + if(yInt <= endT) + { + stack[stackSize].blockPos.set(blockPos.x + x1, blockPos.y + y1); + stack[stackSize].startT = yInt; + stack[stackSize].endT = endT; + stack[stackSize].level = nextLevel; + nextIntersect = yInt; + stackSize++; + } + stack[stackSize].blockPos.set(blockPos.x + x1, blockPos.y + y0); + stack[stackSize].startT = xInt; + stack[stackSize].endT = nextIntersect; + stack[stackSize].level = nextLevel; + + stack[stackSize+1].blockPos.set(blockPos.x + x0, blockPos.y + y0); + stack[stackSize+1].startT = startT; + stack[stackSize+1].endT = xInt; + stack[stackSize+1].level = nextLevel; + stackSize += 2; + } + else if(yInt < xInt) + { + F32 nextIntersect = endT; + if(xInt <= endT) + { + stack[stackSize].blockPos.set(blockPos.x + x1, blockPos.y + y1); + stack[stackSize].startT = xInt; + stack[stackSize].endT = endT; + stack[stackSize].level = nextLevel; + nextIntersect = xInt; + stackSize++; + } + stack[stackSize].blockPos.set(blockPos.x + x0, blockPos.y + y1); + stack[stackSize].startT = yInt; + stack[stackSize].endT = nextIntersect; + stack[stackSize].level = nextLevel; + + stack[stackSize+1].blockPos.set(blockPos.x + x0, blockPos.y + y0); + stack[stackSize+1].startT = startT; + stack[stackSize+1].endT = yInt; + stack[stackSize+1].level = nextLevel; + stackSize += 2; + } + else + { + stack[stackSize].blockPos.set(blockPos.x + x1, blockPos.y + y1); + stack[stackSize].startT = xInt; + stack[stackSize].endT = endT; + stack[stackSize].level = nextLevel; + + stack[stackSize+1].blockPos.set(blockPos.x + x0, blockPos.y + y0); + stack[stackSize+1].startT = startT; + stack[stackSize+1].endT = xInt; + stack[stackSize+1].level = nextLevel; + stackSize += 2; + } + } + + return false; +} diff --git a/Engine/source/terrain/terrCollision.h b/Engine/source/terrain/terrCollision.h new file mode 100644 index 000000000..7313b9054 --- /dev/null +++ b/Engine/source/terrain/terrCollision.h @@ -0,0 +1,57 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TERRCOLL_H_ +#define _TERRCOLL_H_ + +#ifndef _CONVEX_H_ +#include "collision/convex.h" +#endif + + +class TerrainConvex : public Convex +{ + friend class TerrainBlock; + TerrainConvex *square; ///< Alternate convex if square is concave + bool halfA; ///< Which half of square + bool split45; ///< Square split pattern + U32 squareId; ///< Used to match squares + U32 material; + Point3F point[4]; ///< 3-4 vertices + VectorF normal[2]; + Box3F box; ///< Bounding box + + public: + + TerrainConvex(); + + TerrainConvex( const TerrainConvex& cv ); + + // Convex + Box3F getBoundingBox() const; + Box3F getBoundingBox(const MatrixF& mat, const Point3F& scale) const; + Point3F support(const VectorF& v) const; + void getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf); + void getPolyList(AbstractPolyList* list); +}; + +#endif // _TERRCOLL_H_ diff --git a/Engine/source/terrain/terrData.cpp b/Engine/source/terrain/terrData.cpp new file mode 100644 index 000000000..e22a8e776 --- /dev/null +++ b/Engine/source/terrain/terrData.cpp @@ -0,0 +1,1365 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "terrain/terrData.h" + +#include "terrain/terrCollision.h" +#include "terrain/terrCell.h" +#include "terrain/terrRender.h" +#include "terrain/terrMaterial.h" +#include "terrain/terrCellMaterial.h" +#include "gui/worldEditor/terrainEditor.h" +#include "math/mathIO.h" +#include "core/stream/fileStream.h" +#include "core/stream/bitStream.h" +#include "console/consoleTypes.h" +#include "sim/netConnection.h" +#include "core/util/safeDelete.h" +#include "T3D/objectTypes.h" +#include "renderInstance/renderPassManager.h" +#include "scene/sceneRenderState.h" +#include "materials/materialManager.h" +#include "materials/baseMatInstance.h" +#include "gfx/gfxTextureManager.h" +#include "gfx/gfxCardProfile.h" +#include "core/resourceManager.h" +#include "T3D/physics/physicsPlugin.h" +#include "T3D/physics/physicsBody.h" +#include "T3D/physics/physicsCollision.h" +#include "console/engineAPI.h" + +using namespace Torque; + +IMPLEMENT_CO_NETOBJECT_V1(TerrainBlock); + +ConsoleDocClass( TerrainBlock, + "@brief Represent a terrain object in a Torque 3D level\n\n" + + "@tsexample\n" + "new TerrainBlock(theTerrain)\n" + "{\n" + " terrainFile = \"art/terrains/Deathball Desert_0.ter\";\n" + " squareSize = \"2\";\n" + " tile = \"0\";\n" + " baseTexSize = \"1024\";\n" + " screenError = \"16\";\n" + " position = \"-1024 -1024 179.978\";\n" + " rotation = \"1 0 0 0\";\n" + " scale = \"1 1 1\";\n" + " isRenderEnabled = \"true\";\n" + " canSaveDynamicFields = \"1\";\n" + "};\n" + "@endtsexample\n\n" + + "@see TerrainMaterial\n\n" + + "@ingroup Terrain\n" +); + + +Signal TerrainBlock::smUpdateSignal; + +F32 TerrainBlock::smLODScale = 1.0f; +F32 TerrainBlock::smDetailScale = 1.0f; + + +//RBP - Global function declared in Terrdata.h +TerrainBlock* getTerrainUnderWorldPoint(const Point3F & wPos) +{ + // Cast a ray straight down from the world position and see which + // Terrain is the closest to our starting point + Point3F startPnt = wPos; + Point3F endPnt = wPos + Point3F(0.0f, 0.0f, -10000.0f); + + S32 blockIndex = -1; + F32 nearT = 1.0f; + + SimpleQueryList queryList; + gServerContainer.findObjects( TerrainObjectType, SimpleQueryList::insertionCallback, &queryList); + + for (U32 i = 0; i < queryList.mList.size(); i++) + { + Point3F tStartPnt, tEndPnt; + TerrainBlock* terrBlock = dynamic_cast(queryList.mList[i]); + terrBlock->getWorldTransform().mulP(startPnt, &tStartPnt); + terrBlock->getWorldTransform().mulP(endPnt, &tEndPnt); + + RayInfo ri; + if (terrBlock->castRayI(tStartPnt, tEndPnt, &ri, true)) + { + if (ri.t < nearT) + { + blockIndex = i; + nearT = ri.t; + } + } + } + + if (blockIndex > -1) + return (TerrainBlock*)(queryList.mList[blockIndex]); + + return NULL; +} + + +ConsoleDocFragment _getTerrainUnderWorldPoint1( + "@brief Gets the terrain block that is located under the given world point\n\n" + "@param position The world space coordinate you wish to query at. Formatted as (\"x y z\")\n\n" + "@return Returns the ID of the requested terrain block (0 if not found).\n\n" + "@ingroup Terrain", + NULL, + "bool getTerrainUnderWorldPoint( Point3F position );" +); +ConsoleDocFragment _getTerrainUnderWorldPoint2( + "@brief Takes a world point and find the \"highest\" terrain underneath it\n\n" + "@param x The X coordinate in world space\n" + "@param y The Y coordinate in world space\n\n" + "@param z The Z coordinate in world space\n\n" + "@return Returns the ID of the requested terrain block (0 if not found).\n\n" + "@ingroup Terrain", + NULL, + "bool getTerrainUnderWorldPoint( F32 x, F32 y, F32 z);" +); + +ConsoleFunction(getTerrainUnderWorldPoint, S32, 2, 4, "(Point3F x/y/z) Gets the terrain block that is located under the given world point.\n" + "@param x/y/z The world coordinates (floating point values) you wish to query at. " + "These can be formatted as either a string (\"x y z\") or separately as (x, y, z)\n" + "@return Returns the ID of the requested terrain block (0 if not found).\n\n" + "@hide") +{ + Point3F pos; + if(argc == 2) + dSscanf(argv[1], "%f %f %f", &pos.x, &pos.y, &pos.z); + else if(argc == 4) + { + pos.x = dAtof(argv[1]); + pos.y = dAtof(argv[2]); + pos.z = dAtof(argv[3]); + } + + else + { + Con::errorf("getTerrainUnderWorldPoint(Point3F): Invalid argument count! Valid arguments are either \"x y z\" or x,y,z\n"); + return 0; + } + + TerrainBlock* terrain = getTerrainUnderWorldPoint(pos); + if(terrain != NULL) + { + return terrain->getId(); + } + + return 0; + +} + + +TerrainBlock::TerrainBlock() + : mSquareSize( 1.0f ), + mCastShadows( true ), + mScreenError( 16 ), + mDetailsDirty( false ), + mLayerTexDirty( false ), + mLightMap( NULL ), + mLightMapSize( 256 ), + mMaxDetailDistance( 0.0f ), + mCell( NULL ), + mCRC( 0 ), + mBaseTexSize( 1024 ), + mBaseMaterial( NULL ), + mDefaultMatInst( NULL ), + mBaseTexScaleConst( NULL ), + mBaseTexIdConst( NULL ), + mPhysicsRep( NULL ), + mZoningDirty( false ) +{ + mTypeMask = TerrainObjectType | StaticObjectType | StaticShapeObjectType; + mNetFlags.set(Ghostable | ScopeAlways); +} + + +extern Convex sTerrainConvexList; + +TerrainBlock::~TerrainBlock() +{ + // Kill collision + sTerrainConvexList.nukeList(); + + SAFE_DELETE(mLightMap); + mLightMapTex = NULL; + +#ifdef TORQUE_TOOLS + TerrainEditor* editor = dynamic_cast(Sim::findObject("ETerrainEditor")); + if (editor) + editor->detachTerrain(this); +#endif +} + +void TerrainBlock::_onTextureEvent( GFXTexCallbackCode code ) +{ + if ( code == GFXZombify ) + { + if ( mBaseTex.isValid() && + mBaseTex->isRenderTarget() ) + mBaseTex = NULL; + + mLayerTex = NULL; + mLightMapTex = NULL; + } +} + +bool TerrainBlock::_setSquareSize( void *obj, const char *index, const char *data ) +{ + TerrainBlock *terrain = static_cast( obj ); + + F32 newSqaureSize = dAtof( data ); + if ( !mIsEqual( terrain->mSquareSize, newSqaureSize ) ) + { + terrain->mSquareSize = newSqaureSize; + + if ( terrain->isServerObject() && terrain->isProperlyAdded() ) + terrain->_updateBounds(); + + terrain->setMaskBits( HeightMapChangeMask | SizeMask ); + } + + return false; +} + +bool TerrainBlock::_setBaseTexSize( void *obj, const char *index, const char *data ) +{ + TerrainBlock *terrain = static_cast( obj ); + + // NOTE: We're limiting the base texture size to + // 2048 as anything greater in size becomes too + // large to generate for many cards. + // + // If you want to remove this limit feel free, but + // prepare for problems if you don't ship the baked + // base texture with your installer. + // + + S32 texSize = mClamp( dAtoi( data ), 0, 2048 ); + if ( terrain->mBaseTexSize != texSize ) + { + terrain->mBaseTexSize = texSize; + terrain->setMaskBits( MaterialMask ); + } + + return false; +} + +bool TerrainBlock::_setLightMapSize( void *obj, const char *index, const char *data ) +{ + TerrainBlock *terrain = static_cast(obj); + + // Handle inspector value decrements correctly + U32 mapSize = dAtoi( data ); + if ( mapSize == terrain->mLightMapSize-1 ) + mapSize = terrain->mLightMapSize/2; + + // Limit the lightmap size, and ensure it is a power of 2 + const U32 maxTextureSize = GFX->getCardProfiler()->queryProfile( "maxTextureSize", 1024 ); + mapSize = mClamp( getNextPow2( mapSize ), 0, maxTextureSize ); + + if ( terrain->mLightMapSize != mapSize ) + { + terrain->mLightMapSize = mapSize; + terrain->setMaskBits( MaterialMask ); + } + + return false; +} + +bool TerrainBlock::setFile( const FileName &terrFileName ) +{ + if ( terrFileName == mTerrFileName ) + return mFile != NULL; + + Resource file = ResourceManager::get().load( terrFileName ); + if( !file ) + return false; + + setFile( file ); + setMaskBits( FileMask | HeightMapChangeMask ); + + return true; +} + +void TerrainBlock::setFile( Resource terr ) +{ + mFile = terr; + mTerrFileName = terr.getPath(); +} + +bool TerrainBlock::save(const char *filename) +{ + return mFile->save(filename); +} + +bool TerrainBlock::_setTerrainFile( void *obj, const char *index, const char *data ) +{ + static_cast( obj )->setFile( FileName( data ) ); + return false; +} + +void TerrainBlock::_updateBounds() +{ + if ( !mFile ) + return; // quick fix to stop crashing when deleting terrainblocks + + // Setup our object space bounds. + mBounds.minExtents.set( 0.0f, 0.0f, 0.0f ); + mBounds.maxExtents.set( getWorldBlockSize(), getWorldBlockSize(), 0.0f ); + getMinMaxHeight( &mBounds.minExtents.z, &mBounds.maxExtents.z ); + + // Set our mObjBox to be equal to mBounds + if ( mObjBox.maxExtents != mBounds.maxExtents || + mObjBox.minExtents != mBounds.minExtents ) + { + mObjBox = mBounds; + resetWorldBox(); + } +} + +void TerrainBlock::_onZoningChanged( SceneZoneSpaceManager *zoneManager ) +{ + if ( mCell == NULL || zoneManager != getSceneManager()->getZoneManager() ) + return; + + mZoningDirty = true; +} + +void TerrainBlock::setHeight( const Point2I &pos, F32 height ) +{ + U16 ht = floatToFixed( height ); + mFile->setHeight( pos.x, pos.y, ht ); + + // Note: We do not update the grid here as this could + // be called several times in a loop. We depend on the + // caller doing a grid update when he is done. +} + +F32 TerrainBlock::getHeight( const Point2I &pos ) +{ + U16 ht = mFile->getHeight( pos.x, pos.y ); + return fixedToFloat( ht ); +} + +void TerrainBlock::updateGridMaterials( const Point2I &minPt, const Point2I &maxPt ) +{ + if ( mCell ) + { + // Tell the terrain cell that something changed. + const RectI gridRect( minPt, maxPt - minPt ); + mCell->updateGrid( gridRect, true ); + } + + // We mark us as dirty... it will be updated + // before the next time we render the terrain. + mLayerTexDirty = true; + + // Signal anyone that cares that the opacity was changed. + smUpdateSignal.trigger( LayersUpdate, this, minPt, maxPt ); +} + + +Point2I TerrainBlock::getGridPos( const Point3F &worldPos ) const +{ + Point3F terrainPos = worldPos; + getWorldTransform().mulP( terrainPos ); + + F32 squareSize = ( F32 ) getSquareSize(); + F32 halfSquareSize = squareSize / 2.0; + + F32 x = ( terrainPos.x + halfSquareSize ) / squareSize; + F32 y = ( terrainPos.y + halfSquareSize ) / squareSize; + + Point2I gridPos( ( S32 ) mFloor( x ), ( S32 ) mFloor( y ) ); + return gridPos; +} + +void TerrainBlock::updateGrid( const Point2I &minPt, const Point2I &maxPt, bool updateClient ) +{ + // On the client we just signal everyone that the height + // map has changed... the server does the actual changes. + if ( isClientObject() ) + { + PROFILE_SCOPE( TerrainBlock_updateGrid_Client ); + + // This depends on the client getting this call 'after' the server. + // Which is currently the case. + _updateBounds(); + mZoningDirty = true; + + smUpdateSignal.trigger( HeightmapUpdate, this, minPt, maxPt ); + + // Tell the terrain cell that the height changed. + const RectI gridRect( minPt, maxPt - minPt ); + mCell->updateGrid( gridRect ); + + // Rebuild the physics representation. + if ( mPhysicsRep ) + { + // Delay the update by a few milliseconds so + // that we're not rebuilding during an active + // editing operation. + mPhysicsRep->queueCallback( 500, Delegate( this, &TerrainBlock::_updatePhysics ) ); + } + + return; + } + + // Now on the server we rebuild the + // affected area of the grid map. + mFile->updateGrid( minPt, maxPt ); + + // Fix up the bounds. + _updateBounds(); + + // Rebuild the physics representation. + if ( mPhysicsRep ) + { + // Delay the update by a few milliseconds so + // that we're not rebuilding during an active + // editing operation. + mPhysicsRep->queueCallback( 500, Delegate( this, &TerrainBlock::_updatePhysics ) ); + } + + // Signal again here for any server side observers. + smUpdateSignal.trigger( HeightmapUpdate, this, minPt, maxPt ); + + // If this is a server object and the client update + // was requested then try to use the local connection + // pointer to do it. + if ( updateClient && getClientObject() ) + ((TerrainBlock*)getClientObject())->updateGrid( minPt, maxPt, false ); +} + +bool TerrainBlock::getHeight( const Point2F &pos, F32 *height ) const +{ + PROFILE_SCOPE( TerrainBlock_getHeight ); + + F32 invSquareSize = 1.0f / mSquareSize; + F32 xp = pos.x * invSquareSize; + F32 yp = pos.y * invSquareSize; + S32 x = S32(xp); + S32 y = S32(yp); + xp -= (F32)x; + yp -= (F32)y; + + const U32 blockMask = mFile->mSize - 1; + + if ( x & ~blockMask || y & ~blockMask ) + return false; + + x &= blockMask; + y &= blockMask; + + const TerrainSquare *sq = mFile->findSquare( 0, x, y ); + if ( sq->flags & TerrainSquare::Empty ) + return false; + + F32 zBottomLeft = fixedToFloat( mFile->getHeight( x, y ) ); + F32 zBottomRight = fixedToFloat( mFile->getHeight( x + 1, y ) ); + F32 zTopLeft = fixedToFloat( mFile->getHeight( x, y + 1 ) ); + F32 zTopRight = fixedToFloat( mFile->getHeight( x + 1, y + 1 ) ); + + if ( sq->flags & TerrainSquare::Split45 ) + { + if (xp>yp) + // bottom half + *height = zBottomLeft + xp * (zBottomRight-zBottomLeft) + yp * (zTopRight-zBottomRight); + else + // top half + *height = zBottomLeft + xp * (zTopRight-zTopLeft) + yp * (zTopLeft-zBottomLeft); + } + else + { + if (1.0f-xp>yp) + // bottom half + *height = zBottomRight + (1.0f-xp) * (zBottomLeft-zBottomRight) + yp * (zTopLeft-zBottomLeft); + else + // top half + *height = zBottomRight + (1.0f-xp) * (zTopLeft-zTopRight) + yp * (zTopRight-zBottomRight); + } + + return true; +} + +bool TerrainBlock::getNormal( const Point2F &pos, Point3F *normal, bool normalize, bool skipEmpty ) const +{ + PROFILE_SCOPE( TerrainBlock_getNormal ); + + F32 invSquareSize = 1.0f / mSquareSize; + F32 xp = pos.x * invSquareSize; + F32 yp = pos.y * invSquareSize; + S32 x = S32(xp); + S32 y = S32(yp); + xp -= (F32)x; + yp -= (F32)y; + + const U32 blockMask = mFile->mSize - 1; + + if ( x & ~blockMask || y & ~blockMask ) + return false; + + x &= blockMask; + y &= blockMask; + + const TerrainSquare *sq = mFile->findSquare( 0, x, y ); + if ( skipEmpty && sq->flags & TerrainSquare::Empty ) + return false; + + F32 zBottomLeft = fixedToFloat( mFile->getHeight( x, y ) ); + F32 zBottomRight = fixedToFloat( mFile->getHeight( x + 1, y ) ); + F32 zTopLeft = fixedToFloat( mFile->getHeight( x, y + 1 ) ); + F32 zTopRight = fixedToFloat( mFile->getHeight( x + 1, y + 1 ) ); + + if ( sq->flags & TerrainSquare::Split45 ) + { + if (xp>yp) + // bottom half + normal->set(zBottomLeft-zBottomRight, zBottomRight-zTopRight, mSquareSize); + else + // top half + normal->set(zTopLeft-zTopRight, zBottomLeft-zTopLeft, mSquareSize); + } + else + { + if (1.0f-xp>yp) + // bottom half + normal->set(zBottomLeft-zBottomRight, zBottomLeft-zTopLeft, mSquareSize); + else + // top half + normal->set(zTopLeft-zTopRight, zBottomRight-zTopRight, mSquareSize); + } + + if (normalize) + normal->normalize(); + + return true; +} + +bool TerrainBlock::getSmoothNormal( const Point2F &pos, + Point3F *normal, + bool normalize, + bool skipEmpty ) const +{ + PROFILE_SCOPE( TerrainBlock_getSmoothNormal ); + + F32 invSquareSize = 1.0f / mSquareSize; + F32 xp = pos.x * invSquareSize; + F32 yp = pos.y * invSquareSize; + S32 x = S32(xp); + S32 y = S32(yp); + + const U32 blockMask = mFile->mSize - 1; + + if ( x & ~blockMask || y & ~blockMask ) + return false; + + x &= blockMask; + y &= blockMask; + + const TerrainSquare *sq = mFile->findSquare( 0, x, y ); + if ( skipEmpty && sq->flags & TerrainSquare::Empty ) + return false; + + F32 h1 = fixedToFloat( mFile->getHeight( x + 1, y ) ); + F32 h2 = fixedToFloat( mFile->getHeight( x, y + 1 ) ); + F32 h3 = fixedToFloat( mFile->getHeight( x - 1, y ) ); + F32 h4 = fixedToFloat( mFile->getHeight( x, y - 1 ) ); + + normal->set( h3 - h1, h4 - h2, mSquareSize * 2.0f ); + + if ( normalize ) + normal->normalize(); + + return true; +} + +bool TerrainBlock::getNormalAndHeight( const Point2F &pos, Point3F *normal, F32 *height, bool normalize ) const +{ + PROFILE_SCOPE( TerrainBlock_getNormalAndHeight ); + + F32 invSquareSize = 1.0f / mSquareSize; + F32 xp = pos.x * invSquareSize; + F32 yp = pos.y * invSquareSize; + S32 x = S32(xp); + S32 y = S32(yp); + xp -= (F32)x; + yp -= (F32)y; + + const U32 blockMask = mFile->mSize - 1; + + if ( x & ~blockMask || y & ~blockMask ) + return false; + + x &= blockMask; + y &= blockMask; + + const TerrainSquare *sq = mFile->findSquare( 0, x, y ); + if ( sq->flags & TerrainSquare::Empty ) + return false; + + F32 zBottomLeft = fixedToFloat( mFile->getHeight(x, y) ); + F32 zBottomRight = fixedToFloat( mFile->getHeight(x + 1, y) ); + F32 zTopLeft = fixedToFloat( mFile->getHeight(x, y + 1) ); + F32 zTopRight = fixedToFloat( mFile->getHeight(x + 1, y + 1) ); + + if ( sq->flags & TerrainSquare::Split45 ) + { + if (xp>yp) + { + // bottom half + normal->set(zBottomLeft-zBottomRight, zBottomRight-zTopRight, mSquareSize); + *height = zBottomLeft + xp * (zBottomRight-zBottomLeft) + yp * (zTopRight-zBottomRight); + } + else + { + // top half + normal->set(zTopLeft-zTopRight, zBottomLeft-zTopLeft, mSquareSize); + *height = zBottomLeft + xp * (zTopRight-zTopLeft) + yp * (zTopLeft-zBottomLeft); + } + } + else + { + if (1.0f-xp>yp) + { + // bottom half + normal->set(zBottomLeft-zBottomRight, zBottomLeft-zTopLeft, mSquareSize); + *height = zBottomRight + (1.0f-xp) * (zBottomLeft-zBottomRight) + yp * (zTopLeft-zBottomLeft); + } + else + { + // top half + normal->set(zTopLeft-zTopRight, zBottomRight-zTopRight, mSquareSize); + *height = zBottomRight + (1.0f-xp) * (zTopLeft-zTopRight) + yp * (zTopRight-zBottomRight); + } + } + + if (normalize) + normal->normalize(); + + return true; +} + + +bool TerrainBlock::getNormalHeightMaterial( const Point2F &pos, + Point3F *normal, + F32 *height, + StringTableEntry &matName ) const +{ + PROFILE_SCOPE( TerrainBlock_getNormalHeightMaterial ); + + F32 invSquareSize = 1.0f / mSquareSize; + F32 xp = pos.x * invSquareSize; + F32 yp = pos.y * invSquareSize; + S32 x = S32(xp); + S32 y = S32(yp); + S32 xm = S32(mFloor( xp + 0.5f )); + S32 ym = S32(mFloor( yp + 0.5f )); + xp -= (F32)x; + yp -= (F32)y; + + const U32 blockMask = mFile->mSize - 1; + + if ( x & ~blockMask || y & ~blockMask ) + return false; + + x &= blockMask; + y &= blockMask; + + const TerrainSquare *sq = mFile->findSquare( 0, x, y ); + if ( sq->flags & TerrainSquare::Empty ) + return false; + + F32 zBottomLeft = fixedToFloat( mFile->getHeight(x, y) ); + F32 zBottomRight = fixedToFloat( mFile->getHeight(x + 1, y) ); + F32 zTopLeft = fixedToFloat( mFile->getHeight(x, y + 1) ); + F32 zTopRight = fixedToFloat( mFile->getHeight(x + 1, y + 1) ); + + matName = mFile->getMaterialName( xm, ym ); + + if ( sq->flags & TerrainSquare::Split45 ) + { + if (xp>yp) + { + // bottom half + normal->set(zBottomLeft-zBottomRight, zBottomRight-zTopRight, mSquareSize); + *height = zBottomLeft + xp * (zBottomRight-zBottomLeft) + yp * (zTopRight-zBottomRight); + } + else + { + // top half + normal->set(zTopLeft-zTopRight, zBottomLeft-zTopLeft, mSquareSize); + *height = zBottomLeft + xp * (zTopRight-zTopLeft) + yp * (zTopLeft-zBottomLeft); + } + } + else + { + if (1.0f-xp>yp) + { + // bottom half + normal->set(zBottomLeft-zBottomRight, zBottomLeft-zTopLeft, mSquareSize); + *height = zBottomRight + (1.0f-xp) * (zBottomLeft-zBottomRight) + yp * (zTopLeft-zBottomLeft); + } + else + { + // top half + normal->set(zTopLeft-zTopRight, zBottomRight-zTopRight, mSquareSize); + *height = zBottomRight + (1.0f-xp) * (zTopLeft-zTopRight) + yp * (zTopRight-zBottomRight); + } + } + + normal->normalize(); + + return true; +} + +U32 TerrainBlock::getMaterialCount() const +{ + return mFile->mMaterials.size(); +} + +void TerrainBlock::addMaterial( const String &name, U32 insertAt ) +{ + TerrainMaterial *mat = TerrainMaterial::findOrCreate( name ); + + if ( insertAt == -1 ) + { + mFile->mMaterials.push_back( mat ); + mFile->_initMaterialInstMapping(); + } + else + { + + // TODO: Insert and reindex! + + } + + mDetailsDirty = true; + mLayerTexDirty = true; +} + +void TerrainBlock::removeMaterial( U32 index ) +{ + // Cannot delete if only one layer. + if ( mFile->mMaterials.size() == 1 ) + return; + + mFile->mMaterials.erase( index ); + mFile->_initMaterialInstMapping(); + + for ( S32 i = 0; i < mFile->mLayerMap.size(); i++ ) + { + if ( mFile->mLayerMap[i] >= index && + mFile->mLayerMap[i] != 0 ) + { + mFile->mLayerMap[i]--; + } + } + + mDetailsDirty = true; + mLayerTexDirty = true; +} + +void TerrainBlock::updateMaterial( U32 index, const String &name ) +{ + if ( index >= mFile->mMaterials.size() ) + return; + + mFile->mMaterials[ index ] = TerrainMaterial::findOrCreate( name ); + mFile->_initMaterialInstMapping(); + + mDetailsDirty = true; + mLayerTexDirty = true; +} + +TerrainMaterial* TerrainBlock::getMaterial( U32 index ) const +{ + if ( index >= mFile->mMaterials.size() ) + return NULL; + + return mFile->mMaterials[ index ]; +} + +void TerrainBlock::deleteAllMaterials() +{ + mFile->mMaterials.clear(); + mFile->mMaterialInstMapping.clearMatInstList(); +} + +const char* TerrainBlock::getMaterialName( U32 index ) const +{ + if ( index < mFile->mMaterials.size() ) + return mFile->mMaterials[ index ]->getInternalName(); + + return NULL; +} + +void TerrainBlock::setLightMap( GBitmap *newLightMap ) +{ + SAFE_DELETE( mLightMap ); + mLightMap = newLightMap; + mLightMapTex = NULL; +} + +void TerrainBlock::clearLightMap() +{ + if ( !mLightMap ) + mLightMap = new GBitmap( mLightMapSize, mLightMapSize, 0, GFXFormatR8G8B8 ); + + mLightMap->fillWhite(); + mLightMapTex = NULL; +} + +GFXTextureObject* TerrainBlock::getLightMapTex() +{ + if ( mLightMapTex.isNull() && mLightMap ) + { + mLightMapTex.set( mLightMap, + &GFXDefaultStaticDiffuseProfile, + false, + "TerrainBlock::getLightMapTex()" ); + } + + return mLightMapTex; +} + +void TerrainBlock::onEditorEnable() +{ +} + +void TerrainBlock::onEditorDisable() +{ +} + +bool TerrainBlock::onAdd() +{ + if(!Parent::onAdd()) + return false; + + if ( mTerrFileName.isEmpty() ) + { + mTerrFileName = Con::getVariable( "$Client::MissionFile" ); + mTerrFileName.replace("tools/levels/", "art/terrains/"); + mTerrFileName.replace("levels/", "art/terrains/"); + + Vector materials; + materials.push_back( "warning_material" ); + TerrainFile::create( &mTerrFileName, 256, materials ); + } + + Resource terr = ResourceManager::get().load( mTerrFileName ); + if(terr == NULL) + { + if(isClientObject()) + NetConnection::setLastError("You are missing a file needed to play this mission: %s", mTerrFileName.c_str()); + return false; + } + + setFile( terr ); + + if ( terr->mNeedsResaving ) + { + if (Platform::messageBox("Update Terrain File", "You appear to have a Terrain file in an older format. Do you want Torque to update it?", MBOkCancel, MIQuestion) == MROk) + { + terr->save(terr->mFilePath.getFullPath()); + terr->mNeedsResaving = false; + } + } + + if (terr->mFileVersion != TerrainFile::FILE_VERSION || terr->mNeedsResaving) + { + Con::errorf(" *********************************************************"); + Con::errorf(" *********************************************************"); + Con::errorf(" *********************************************************"); + Con::errorf(" PLEASE RESAVE THE TERRAIN FILE FOR THIS MISSION! THANKS!"); + Con::errorf(" *********************************************************"); + Con::errorf(" *********************************************************"); + Con::errorf(" *********************************************************"); + } + + _updateBounds(); + + resetWorldBox(); + setRenderTransform(mObjToWorld); + + if (isClientObject()) + { + if ( mCRC != terr.getChecksum() ) + { + NetConnection::setLastError("Your terrain file doesn't match the version that is running on the server."); + return false; + } + + clearLightMap(); + + // Init the detail layer rendering helper. + _updateMaterials(); + _updateLayerTexture(); + + // If the cached base texture is older that the terrain file or + // it doesn't exist then generate and cache it. + String baseCachePath = _getBaseTexCacheFileName(); + if ( Platform::compareModifiedTimes( baseCachePath, mTerrFileName ) < 0 ) + _updateBaseTexture( true ); + + // The base texture should have been cached by now... so load it. + mBaseTex.set( baseCachePath, &GFXDefaultStaticDiffuseProfile, "TerrainBlock::mBaseTex" ); + + GFXTextureManager::addEventDelegate( this, &TerrainBlock::_onTextureEvent ); + MATMGR->getFlushSignal().notify( this, &TerrainBlock::_onFlushMaterials ); + + // Build the terrain quadtree. + _rebuildQuadtree(); + + // Preload all the materials. + mCell->preloadMaterials(); + + mZoningDirty = true; + SceneZoneSpaceManager::getZoningChangedSignal().notify( this, &TerrainBlock::_onZoningChanged ); + } + else + mCRC = terr.getChecksum(); + + addToScene(); + + _updatePhysics(); + + return true; +} + +String TerrainBlock::_getBaseTexCacheFileName() const +{ + Torque::Path basePath( mTerrFileName ); + basePath.setFileName( basePath.getFileName() + "_basetex" ); + basePath.setExtension( "dds" ); + return basePath.getFullPath(); +} + +void TerrainBlock::_rebuildQuadtree() +{ + SAFE_DELETE( mCell ); + + // Recursively build the cells. + mCell = TerrCell::init( this ); + + // Build the shared PrimitiveBuffer. + mCell->createPrimBuffer( &mPrimBuffer ); +} + +void TerrainBlock::_updatePhysics() +{ + if ( !PHYSICSMGR ) + return; + + SAFE_DELETE( mPhysicsRep ); + + PhysicsCollision *colShape; + + // If we can steal the collision shape from the local server + // object then do so as it saves us alot of cpu time and memory. + // + // TODO: We should move this sharing down into TerrFile where + // it probably belongs. + // + if ( getServerObject() ) + { + TerrainBlock *serverTerrain = (TerrainBlock*)getServerObject(); + colShape = serverTerrain->mPhysicsRep->getColShape(); + } + else + { + // Get empty state of each vert + bool *holes = new bool[ getBlockSize() * getBlockSize() ]; + for ( U32 row = 0; row < getBlockSize(); row++ ) + for ( U32 column = 0; column < getBlockSize(); column++ ) + holes[ row + (column * getBlockSize()) ] = mFile->isEmptyAt( row, column ); + + colShape = PHYSICSMGR->createCollision(); + colShape->addHeightfield( mFile->getHeightMap().address(), holes, getBlockSize(), mSquareSize, MatrixF::Identity ); + + delete [] holes; + } + + PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" ); + mPhysicsRep = PHYSICSMGR->createBody(); + mPhysicsRep->init( colShape, 0, 0, this, world ); + mPhysicsRep->setTransform( getTransform() ); +} + +void TerrainBlock::onRemove() +{ + removeFromScene(); + SceneZoneSpaceManager::getZoningChangedSignal().remove( this, &TerrainBlock::_onZoningChanged ); + + SAFE_DELETE( mPhysicsRep ); + + if ( isClientObject() ) + { + mBaseTex = NULL; + mLayerTex = NULL; + SAFE_DELETE( mBaseMaterial ); + SAFE_DELETE( mDefaultMatInst ); + SAFE_DELETE( mCell ); + mPrimBuffer = NULL; + mBaseShader = NULL; + GFXTextureManager::removeEventDelegate( this, &TerrainBlock::_onTextureEvent ); + MATMGR->getFlushSignal().remove( this, &TerrainBlock::_onFlushMaterials ); + } + + Parent::onRemove(); +} + +void TerrainBlock::prepRenderImage( SceneRenderState* state ) +{ + PROFILE_SCOPE(TerrainBlock_prepRenderImage); + + // If we need to update our cached + // zone state then do it now. + if ( mZoningDirty ) + { + mZoningDirty = false; + mCell->updateZoning( getSceneManager()->getZoneManager() ); + } + + _renderBlock( state ); +} + +void TerrainBlock::setTransform(const MatrixF & mat) +{ + Parent::setTransform( mat ); + + // Update world-space OBBs. + if( mCell ) + { + mCell->updateOBBs(); + mZoningDirty = true; + } + + if ( mPhysicsRep ) + mPhysicsRep->setTransform( mat ); + + setRenderTransform( mat ); + setMaskBits( TransformMask ); +} + +void TerrainBlock::setScale( const VectorF &scale ) +{ + // We disable scaling... we never scale! + Parent::setScale( VectorF::One ); +} + +void TerrainBlock::initPersistFields() +{ + addGroup( "Media" ); + + addProtectedField( "terrainFile", TypeStringFilename, Offset( mTerrFileName, TerrainBlock ), + &TerrainBlock::_setTerrainFile, &defaultProtectedGetFn, + "The source terrain data file." ); + + endGroup( "Media" ); + + addGroup( "Misc" ); + + addField( "castShadows", TypeBool, Offset( mCastShadows, TerrainBlock ), + "Allows the terrain to cast shadows onto itself and other objects."); + + addProtectedField( "squareSize", TypeF32, Offset( mSquareSize, TerrainBlock ), + &TerrainBlock::_setSquareSize, &defaultProtectedGetFn, + "Indicates the spacing between points on the XY plane on the terrain." ); + + addProtectedField( "baseTexSize", TypeS32, Offset( mBaseTexSize, TerrainBlock ), + &TerrainBlock::_setBaseTexSize, &defaultProtectedGetFn, + "Size of base texture size per meter." ); + + addProtectedField( "lightMapSize", TypeS32, Offset( mLightMapSize, TerrainBlock ), + &TerrainBlock::_setLightMapSize, &defaultProtectedGetFn, + "Light map dimensions in pixels." ); + + addField( "screenError", TypeS32, Offset( mScreenError, TerrainBlock ), "Not yet implemented." ); + + endGroup( "Misc" ); + + Parent::initPersistFields(); + + removeField( "scale" ); + + Con::addVariable( "$TerrainBlock::debugRender", TypeBool, &smDebugRender, "Triggers debug rendering of terrain cells\n\n" + "@ingroup Terrain"); + + Con::addVariable( "$pref::Terrain::lodScale", TypeF32, &smLODScale, "A global LOD scale used to tweak the default terrain screen error value.\n\n" + "@ingroup Terrain"); + + Con::addVariable( "$pref::Terrain::detailScale", TypeF32, &smDetailScale, "A global detail scale used to tweak the material detail distances.\n\n" + "@ingroup Terrain"); +} + +void TerrainBlock::inspectPostApply() +{ + Parent::inspectPostApply(); + setMaskBits( MiscMask ); +} + +U32 TerrainBlock::packUpdate(NetConnection* con, U32 mask, BitStream *stream) +{ + U32 retMask = Parent::packUpdate( con, mask, stream ); + + if ( stream->writeFlag( mask & TransformMask ) ) + mathWrite( *stream, getTransform() ); + + if ( stream->writeFlag( mask & FileMask ) ) + { + stream->write( mTerrFileName ); + stream->write( mCRC ); + } + + if ( stream->writeFlag( mask & SizeMask ) ) + stream->write( mSquareSize ); + + stream->writeFlag( mCastShadows ); + + if ( stream->writeFlag( mask & MaterialMask ) ) + { + stream->write( mBaseTexSize ); + stream->write( mLightMapSize ); + } + + stream->writeFlag( mask & HeightMapChangeMask ); + + if ( stream->writeFlag( mask & MiscMask ) ) + stream->write( mScreenError ); + + return retMask; +} + +void TerrainBlock::unpackUpdate(NetConnection* con, BitStream *stream) +{ + Parent::unpackUpdate( con, stream ); + + if ( stream->readFlag() ) // TransformMask + { + MatrixF mat; + mathRead( *stream, &mat ); + setTransform( mat ); + } + + if ( stream->readFlag() ) // FileMask + { + FileName terrFile; + stream->read( &terrFile ); + stream->read( &mCRC ); + + if ( isProperlyAdded() ) + setFile( terrFile ); + else + mTerrFileName = terrFile; + } + + if ( stream->readFlag() ) // SizeMask + stream->read( &mSquareSize ); + + mCastShadows = stream->readFlag(); + + if ( stream->readFlag() ) // MaterialMask + { + U32 baseTexSize; + stream->read( &baseTexSize ); + if ( mBaseTexSize != baseTexSize ) + { + mBaseTexSize = baseTexSize; + if ( isProperlyAdded() ) + _updateBaseTexture( false ); + } + + U32 lightMapSize; + stream->read( &lightMapSize ); + if ( mLightMapSize != lightMapSize ) + { + mLightMapSize = lightMapSize; + if ( isProperlyAdded() ) + { + SAFE_DELETE( mLightMap ); + clearLightMap(); + } + } + } + + if ( stream->readFlag() && isProperlyAdded() ) // HeightMapChangeMask + { + _updateBounds(); + _rebuildQuadtree(); + _updatePhysics(); + mDetailsDirty = true; + mLayerTexDirty = true; + } + + if ( stream->readFlag() ) // MiscMask + stream->read( &mScreenError ); +} + +void TerrainBlock::getMinMaxHeight( F32 *minHeight, F32 *maxHeight ) const +{ + // We can get the bound height from the last grid level. + const TerrainSquare *sq = mFile->findSquare( mFile->mGridLevels, 0, 0 ); + *minHeight = fixedToFloat( sq->minHeight ); + *maxHeight = fixedToFloat( sq->maxHeight ); +} + +//----------------------------------------------------------------------------- +// Console Methods +//----------------------------------------------------------------------------- + +DefineEngineMethod( TerrainBlock, save, bool, ( const char* fileName),, + "@brief Saves the terrain block's terrain file to the specified file name.\n\n" + + "@param fileName Name and path of file to save terrain data to.\n\n" + + "@return True if file save was successful, false otherwise") +{ + char filename[256]; + dStrcpy(filename,fileName); + char *ext = dStrrchr(filename, '.'); + if (!ext || dStricmp(ext, ".ter") != 0) + dStrcat(filename, ".ter"); + return static_cast(object)->save(filename); +} + +//ConsoleMethod(TerrainBlock, save, bool, 3, 3, "(string fileName) - saves the terrain block's terrain file to the specified file name.") +//{ +// char filename[256]; +// dStrcpy(filename,argv[2]); +// char *ext = dStrrchr(filename, '.'); +// if (!ext || dStricmp(ext, ".ter") != 0) +// dStrcat(filename, ".ter"); +// return static_cast(object)->save(filename); +//} + +ConsoleDocFragment _getTerrainHeight1( + "@brief Gets the terrain height at the specified position\n\n" + "@param position The world space point, minus the z (height) value. Formatted as (\"x y\")\n\n" + "@return Returns the terrain height at the given point as an F32 value.\n\n" + "@ingroup Terrain", + NULL, + "bool getTerrainHeight( Point2I position );" +); +ConsoleDocFragment _getTerrainHeight2( + "@brief Gets the terrain height at the specified position\n\n" + "@param x The X coordinate in world space\n" + "@param y The Y coordinate in world space\n\n" + "@return Returns the terrain height at the given point as an F32 value.\n\n" + "@ingroup Terrain", + NULL, + "bool getTerrainHeight( F32 x, F32 y);" +); + +ConsoleFunction(getTerrainHeight, F32, 2, 3, "(Point2 pos) - gets the terrain height at the specified position." + "@param pos The world space point, minus the z (height) value\n Can be formatted as either (\"x y\") or (x,y)\n" + "@return Returns the terrain height at the given point as an F32 value.\n" + "@hide") +{ + Point2F pos; + F32 height = 0.0f; + + if(argc == 2) + dSscanf(argv[1],"%f %f",&pos.x,&pos.y); + else if(argc == 3) + { + pos.x = dAtof(argv[1]); + pos.y = dAtof(argv[2]); + } + + TerrainBlock * terrain = getTerrainUnderWorldPoint(Point3F(pos.x, pos.y, 5000.0f)); + if(terrain) + if(terrain->isServerObject()) + { + Point3F offset; + terrain->getTransform().getColumn(3, &offset); + pos -= Point2F(offset.x, offset.y); + terrain->getHeight(pos, &height); + } + return height; +} + +ConsoleDocFragment _getTerrainHeightBelowPosition1( + "@brief Takes a world point and find the \"highest\" terrain underneath it\n\n" + "@param position The world space point, minus the z (height) value. Formatted as (\"x y\")\n\n" + "@return Returns the closest terrain height below the given point as an F32 value.\n\n" + "@ingroup Terrain", + NULL, + "bool getTerrainHeightBelowPosition( Point2I position );" +); +ConsoleDocFragment _getTerrainHeightBelowPosition2( + "@brief Takes a world point and find the \"highest\" terrain underneath it\n\n" + "@param x The X coordinate in world space\n" + "@param y The Y coordinate in world space\n\n" + "@return Returns the closest terrain height below the given point as an F32 value.\n\n" + "@ingroup Terrain", + NULL, + "bool getTerrainHeightBelowPosition( F32 x, F32 y);" +); + +ConsoleFunction(getTerrainHeightBelowPosition, F32, 2, 4, "(Point3F pos) - gets the terrain height at the specified position." + "@param pos The world space point. Can be formatted as either (\"x y z\") or (x,y,z)\n" + "@note This function is useful if you simply want to grab the terrain height underneath an object.\n" + "@return Returns the terrain height at the given point as an F32 value.\n" + "@hide") +{ + Point3F pos; + F32 height = 0.0f; + + if(argc == 2) + dSscanf(argv[1], "%f %f %f", &pos.x, &pos.y, &pos.z); + else if(argc == 4) + { + pos.x = dAtof(argv[1]); + pos.y = dAtof(argv[2]); + pos.z = dAtof(argv[3]); + } + + else + { + Con::errorf("getTerrainHeightBelowPosition(Point3F): Invalid argument count! Valid arguments are either \"x y z\" or x,y,z\n"); + return 0; + } + + TerrainBlock * terrain = getTerrainUnderWorldPoint(pos); + + Point2F nohghtPos(pos.x, pos.y); + + if(terrain) + { + if(terrain->isServerObject()) + { + Point3F offset; + terrain->getTransform().getColumn(3, &offset); + nohghtPos -= Point2F(offset.x, offset.y); + terrain->getHeight(nohghtPos, &height); + } + } + + return height; +} diff --git a/Engine/source/terrain/terrData.h b/Engine/source/terrain/terrData.h new file mode 100644 index 000000000..20588ccc0 --- /dev/null +++ b/Engine/source/terrain/terrData.h @@ -0,0 +1,436 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TERRDATA_H_ +#define _TERRDATA_H_ + +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _SCENEOBJECT_H_ +#include "scene/sceneObject.h" +#endif +#ifndef __RESOURCE_H__ +#include "core/resource.h" +#endif +#ifndef _RENDERPASSMANAGER_H_ +#include "renderInstance/renderPassManager.h" +#endif +#ifndef _TSIGNAL_H_ +#include "core/util/tSignal.h" +#endif +#ifndef _TERRFILE_H_ +#include "terrain/terrFile.h" +#endif +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif + + + +class GBitmap; +class TerrainBlock; +class TerrCell; +class PhysicsBody; +class TerrainCellMaterial; + +class TerrainBlock : public SceneObject +{ + typedef SceneObject Parent; + + friend class TerrainEditor; + friend class TerrainCellMaterial; + +protected: + + enum + { + TransformMask = Parent::NextFreeMask, + FileMask = Parent::NextFreeMask << 1, + SizeMask = Parent::NextFreeMask << 2, + MaterialMask = Parent::NextFreeMask << 3, + HeightMapChangeMask = Parent::NextFreeMask << 4, + MiscMask = Parent::NextFreeMask << 5, + + NextFreeMask = Parent::NextFreeMask << 6, + }; + + Box3F mBounds; + + /// + GBitmap *mLightMap; + + /// The lightmap dimensions in pixels. + U32 mLightMapSize; + + /// The lightmap texture. + GFXTexHandle mLightMapTex; + + /// The terrain data file. + Resource mFile; + + /// The TerrainFile CRC sent from the server. + U32 mCRC; + + /// + FileName mTerrFileName; + + /// The maximum detail distance found in the material list. + F32 mMaxDetailDistance; + + /// + Vector mBaseTextures; + + /// + GFXTexHandle mLayerTex; + + /// The shader used to generate the base texture map. + GFXShaderRef mBaseShader; + + /// + GFXStateBlockRef mBaseShaderSB; + + /// + GFXShaderConstBufferRef mBaseShaderConsts; + + /// + GFXShaderConstHandle *mBaseTexScaleConst; + GFXShaderConstHandle *mBaseTexIdConst; + GFXShaderConstHandle *mBaseLayerSizeConst; + + /// + GFXTextureTargetRef mBaseTarget; + + /// The base texture. + GFXTexHandle mBaseTex; + + /// + bool mDetailsDirty; + + /// + bool mLayerTexDirty; + + /// The desired size for the base texture. + U32 mBaseTexSize; + + /// + TerrCell *mCell; + + /// The shared base material which is used to render + /// cells that are outside the detail map range. + TerrainCellMaterial *mBaseMaterial; + + /// A dummy material only used for shadow + /// material generation. + BaseMatInstance *mDefaultMatInst; + + F32 mSquareSize; + + PhysicsBody *mPhysicsRep; + + U32 mScreenError; + + /// The shared primitive buffer used in rendering. + GFXPrimitiveBufferHandle mPrimBuffer; + + /// The cells used in the last render pass + /// when doing debug rendering. + /// @see _renderDebug + Vector mDebugCells; + + /// Set to enable debug rendering of the terrain. It + /// is exposed to the console via $terrain::debugRender. + static bool smDebugRender; + + /// Allows the terrain to cast shadows onto itself and other objects. + bool mCastShadows; + + /// A global LOD scale used to tweak the default + /// terrain screen error value. + static F32 smLODScale; + + /// A global detail scale used to tweak the + /// material detail distances. + static F32 smDetailScale; + + /// True if the zoning needs to be recalculated for the terrain. + bool mZoningDirty; + + String _getBaseTexCacheFileName() const; + + void _rebuildQuadtree(); + + void _updatePhysics(); + + void _renderBlock( SceneRenderState *state ); + void _renderDebug( ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat ); + + /// The callback used to get texture events. + /// @see GFXTextureManager::addEventDelegate + void _onTextureEvent( GFXTexCallbackCode code ); + + /// Used to release terrain materials when + /// the material manager flushes them. + /// @see MaterialManager::getFlushSignal + void _onFlushMaterials(); + + /// + bool _initBaseShader(); + + /// + void _updateMaterials(); + + /// + void _updateBaseTexture( bool writeToCache ); + + void _updateLayerTexture(); + + void _updateBounds(); + + void _onZoningChanged( SceneZoneSpaceManager *zoneManager ); + + void _updateZoning(); + + // Protected fields + static bool _setTerrainFile( void *obj, const char *index, const char *data ); + static bool _setSquareSize( void *obj, const char *index, const char *data ); + static bool _setBaseTexSize( void *obj, const char *index, const char *data ); + static bool _setLightMapSize( void *obj, const char *index, const char *data ); + +public: + + enum + { + LightmapUpdate = BIT(0), + HeightmapUpdate = BIT(1), + LayersUpdate = BIT(2), + EmptyUpdate = BIT(3) + }; + + static Signal smUpdateSignal; + + /// + bool import( const GBitmap &heightMap, + F32 heightScale, + F32 metersPerPixel, + const Vector &layerMap, + const Vector &materials ); + +#ifdef TORQUE_TOOLS + bool exportHeightMap( const UTF8 *filePath, const String &format ) const; + bool exportLayerMaps( const UTF8 *filePrefix, const String &format ) const; +#endif + +public: + + TerrainBlock(); + virtual ~TerrainBlock(); + + U32 getCRC() const { return(mCRC); } + + Resource getFile() const { return mFile; }; + + bool onAdd(); + void onRemove(); + + void onEditorEnable(); + void onEditorDisable(); + + /// Adds a new material as the top layer or + /// inserts it at the specified index. + void addMaterial( const String &name, U32 insertAt = -1 ); + + /// Removes the material at the index. + void removeMaterial( U32 index ); + + /// Updates the material at the index. + void updateMaterial( U32 index, const String &name ); + + /// Deletes all the materials on the terrain. + void deleteAllMaterials(); + + //void setMaterialName( U32 index, const String &name ); + + /// Accessors and mutators for TerrainMaterialUndoAction. + /// @{ + const Vector& getMaterials() const { return mFile->mMaterials; } + const Vector& getLayerMap() const { return mFile->mLayerMap; } + void setMaterials( const Vector &materials ) { mFile->mMaterials = materials; } + void setLayerMap( const Vector &layers ) { mFile->mLayerMap = layers; } + /// @} + + TerrainMaterial* getMaterial( U32 index ) const; + + const char* getMaterialName( U32 index ) const; + + U32 getMaterialCount() const; + + //BaseMatInstance* getMaterialInst( U32 x, U32 y ); + + void setHeight( const Point2I &pos, F32 height ); + F32 getHeight( const Point2I &pos ); + + // Performs an update to the selected range of the terrain + // grid including the collision and rendering structures. + void updateGrid( const Point2I &minPt, + const Point2I &maxPt, + bool updateClient = false ); + + void updateGridMaterials( const Point2I &minPt, const Point2I &maxPt ); + + Point2I getGridPos( const Point3F &worldPos ) const; + + /// This returns true and the terrain z height for + /// a 2d position in the terrains object space. + /// + /// If the terrain at that point is within an empty block + /// or the 2d position is outside of the terrain area then + /// it returns false. + /// + bool getHeight( const Point2F &pos, F32 *height ) const; + + void getMinMaxHeight( F32 *minHeight, F32 *maxHeight ) const; + + /// This returns true and the terrain normal for a + /// 2d position in the terrains object space. + /// + /// If the terrain at that point is within an empty block + /// or the 2d position is outside of the terrain area then + /// it returns false. + /// + bool getNormal( const Point2F &pos, + Point3F *normal, + bool normalize = true, + bool skipEmpty = true ) const; + + /// This returns true and the smoothed terrain normal + // for a 2d position in the terrains object space. + /// + /// If the terrain at that point is within an empty block + /// or the 2d position is outside of the terrain area then + /// it returns false. + /// + bool getSmoothNormal( const Point2F &pos, + Point3F *normal, + bool normalize = true, + bool skipEmpty = true ) const; + + /// This returns true and the terrain normal and z height + /// for a 2d position in the terrains object space. + /// + /// If the terrain at that point is within an empty block + /// or the 2d position is outside of the terrain area then + /// it returns false. + /// + bool getNormalAndHeight( const Point2F &pos, + Point3F *normal, + F32 *height, + bool normalize = true ) const; + + /// This returns true and the terrain normal, z height, and + /// material name for a 2d position in the terrains object + /// space. + /// + /// If the terrain at that point is within an empty block + /// or the 2d position is outside of the terrain area then + /// it returns false. + /// + bool getNormalHeightMaterial( const Point2F &pos, + Point3F *normal, + F32 *height, + StringTableEntry &matName ) const; + + // only the editor currently uses this method - should always be using a ray to collide with + bool collideBox( const Point3F &start, const Point3F &end, RayInfo* info ) + { + return castRay( start, end, info ); + } + + /// + void setLightMap( GBitmap *newLightMap ); + + /// Fills the lightmap with white. + void clearLightMap(); + + /// Retuns the dimensions of the light map. + U32 getLightMapSize() const { return mLightMapSize; } + + const GBitmap* getLightMap() const { return mLightMap; } + + GBitmap* getLightMap() { return mLightMap; } + + /// + GFXTextureObject* getLightMapTex(); + +public: + + bool setFile( const FileName& terrFileName ); + + void setFile( Resource file ); + + bool save(const char* filename); + + F32 getSquareSize() const { return mSquareSize; } + + /// Returns the dimensions of the terrain in world space. + F32 getWorldBlockSize() const { return mSquareSize * (F32)mFile->mSize; } + + /// Retuns the dimensions of the terrain in samples. + U32 getBlockSize() const { return mFile->mSize; } + + U32 getScreenError() const { return smLODScale * mScreenError; } + + // SceneObject + void setTransform( const MatrixF &mat ); + void setScale( const VectorF &scale ); + + void prepRenderImage ( SceneRenderState* state ); + + void buildConvex(const Box3F& box,Convex* convex); + bool buildPolyList(PolyListContext context, AbstractPolyList* polyList, const Box3F &box, const SphereF &sphere); + bool castRay(const Point3F &start, const Point3F &end, RayInfo* info); + bool castRayI(const Point3F &start, const Point3F &end, RayInfo* info, bool emptyCollide); + + bool castRayBlock( const Point3F &pStart, + const Point3F &pEnd, + const Point2I &blockPos, + U32 level, + F32 invDeltaX, + F32 invDeltaY, + F32 startT, + F32 endT, + RayInfo *info, + bool collideEmpty ); + + const FileName& getTerrainFile() const { return mTerrFileName; } + + void postLight(Vector &terrBlocks) {}; + + + DECLARE_CONOBJECT(TerrainBlock); + static void initPersistFields(); + U32 packUpdate (NetConnection *conn, U32 mask, BitStream *stream); + void unpackUpdate(NetConnection *conn, BitStream *stream); + void inspectPostApply(); +}; + +#endif // _TERRDATA_H_ diff --git a/Engine/source/terrain/terrExport.cpp b/Engine/source/terrain/terrExport.cpp new file mode 100644 index 000000000..378452085 --- /dev/null +++ b/Engine/source/terrain/terrExport.cpp @@ -0,0 +1,162 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "terrain/terrData.h" +#include "gfx/bitmap/gBitmap.h" +#include "terrain/terrMaterial.h" +#include "core/stream/fileStream.h" + +#ifdef TORQUE_TOOLS + +bool TerrainBlock::exportHeightMap( const UTF8 *filePath, const String &format ) const +{ + + GBitmap output( mFile->mSize, + mFile->mSize, + false, + GFXFormatR5G6B5 ); + + // First capture the max height... we'll normalize + // everything to this value. + U16 maxHeight = 0; + + Vector::iterator iBits = mFile->mHeightMap.begin(); + for ( S32 y = 0; y < mFile->mSize; y++ ) + { + for ( S32 x = 0; x < mFile->mSize; x++ ) + { + if ( *iBits > maxHeight ) + maxHeight = *iBits; + ++iBits; + } + } + + // Now write out the map. + iBits = mFile->mHeightMap.begin(); + U16 *oBits = (U16*)output.getWritableBits(); + for ( S32 y = 0; y < mFile->mSize; y++ ) + { + for ( S32 x = 0; x < mFile->mSize; x++ ) + { + // PNG expects big endian. + U16 height = (U16)( ( (F32)(*iBits) / (F32)maxHeight ) * (F32)U16_MAX ); + *oBits = convertHostToBEndian( height ); + ++oBits; + ++iBits; + } + } + + FileStream stream; + if ( !stream.open( filePath, Torque::FS::File::Write ) ) + { + Con::errorf( "TerrainBlock::exportHeightMap() - Error opening file for writing: %s !", filePath ); + return false; + } + + if ( !output.writeBitmap( format, stream ) ) + { + Con::errorf( "TerrainBlock::exportHeightMap() - Error writing %s: %s !", format.c_str(), filePath ); + return false; + } + + // Print out the map size in meters, so that the user + // knows what values to use when importing it into + // another terrain tool. + S32 dim = mSquareSize * mFile->mSize; + S32 height = fixedToFloat( maxHeight ); + Con::printf( "Saved heightmap with dimensions %d x %d x %d.", dim, dim, height ); + + return true; +} + +bool TerrainBlock::exportLayerMaps( const UTF8 *filePrefix, const String &format ) const +{ + for(S32 i = 0; i < mFile->mMaterials.size(); i++) + { + Vector::iterator iBits = mFile->mLayerMap.begin(); + + GBitmap output( mFile->mSize, + mFile->mSize, + false, + GFXFormatA8 ); + + // Copy the layer data. + U8 *oBits = (U8*)output.getWritableBits(); + dMemset( oBits, 0, mFile->mSize * mFile->mSize ); + + for ( S32 y = 0; y < mFile->mSize; y++ ) + { + for ( S32 x = 0; x < mFile->mSize; x++ ) + { + if(*iBits == i) + *oBits = 0xFF; + ++iBits; + ++oBits; + } + } + + // Whats the full file name for this layer. + UTF8 filePath[1024]; + dSprintf( filePath, 1024, "%s_%d_%s.%s", filePrefix, i, mFile->mMaterials[i]->getInternalName(), format.c_str() ); + + FileStream stream; + if ( !stream.open( filePath, Torque::FS::File::Write ) ) + { + Con::errorf( "TerrainBlock::exportLayerMaps() - Error opening file for writing: %s !", filePath ); + return false; + } + + if ( !output.writeBitmap( format, stream ) ) + { + Con::errorf( "TerrainBlock::exportLayerMaps() - Error writing %s: %s !", format.c_str(), filePath ); + return false; + } + } + + return true; +} + +ConsoleMethod( TerrainBlock, exportHeightMap, bool, 3, 4, "(string filename, [string format]) - export the terrain block's heightmap to a bitmap file (default: png)" ) +{ + UTF8 fileName[1024]; + String format = "png"; + if( argc > 3 ) + format = argv[ 3 ]; + + Con::expandScriptFilename( fileName, sizeof( fileName ), argv[2] ); + + return object->exportHeightMap( fileName, format ); +} + +ConsoleMethod( TerrainBlock, exportLayerMaps, bool, 3, 4, "(string filePrefix, [string format]) - export the terrain block's layer maps to bitmap files (default: png)" ) +{ + UTF8 filePrefix[1024]; + String format = "png"; + if( argc > 3 ) + format = argv[3]; + + Con::expandScriptFilename( filePrefix, sizeof( filePrefix ), argv[2] ); + + return object->exportLayerMaps( filePrefix, format ); +} +#endif diff --git a/Engine/source/terrain/terrFeatureTypes.cpp b/Engine/source/terrain/terrFeatureTypes.cpp new file mode 100644 index 000000000..af445c22c --- /dev/null +++ b/Engine/source/terrain/terrFeatureTypes.cpp @@ -0,0 +1,36 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "terrain/terrFeatureTypes.h" + +#include "materials/materialFeatureTypes.h" + + +ImplementFeatureType( MFT_TerrainBaseMap, MFG_Texture, 100.0f, false ); +ImplementFeatureType( MFT_TerrainParallaxMap, MFG_Texture, 101.0f, false ); +ImplementFeatureType( MFT_TerrainDetailMap, MFG_Texture, 102.0f, false ); +ImplementFeatureType( MFT_TerrainNormalMap, MFG_Texture, 103.0f, false ); +ImplementFeatureType( MFT_TerrainLightMap, MFG_Texture, 104.0f, false ); +ImplementFeatureType( MFT_TerrainSideProject, MFG_Texture, 105.0f, false ); +ImplementFeatureType( MFT_TerrainAdditive, MFG_PostProcess, 999.0f, false ); + diff --git a/Engine/source/terrain/terrFeatureTypes.h b/Engine/source/terrain/terrFeatureTypes.h new file mode 100644 index 000000000..b50a0a22d --- /dev/null +++ b/Engine/source/terrain/terrFeatureTypes.h @@ -0,0 +1,39 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TERRFEATURETYPES_H_ +#define _TERRFEATURETYPES_H_ + +#ifndef _FEATURETYPE_H_ +#include "shaderGen/featureType.h" +#endif + +DeclareFeatureType( MFT_TerrainBaseMap ); +DeclareFeatureType( MFT_TerrainDetailMap ); +DeclareFeatureType( MFT_TerrainNormalMap ); +DeclareFeatureType( MFT_TerrainParallaxMap ); +DeclareFeatureType( MFT_TerrainLightMap ); +DeclareFeatureType( MFT_TerrainSideProject ); +DeclareFeatureType( MFT_TerrainAdditive ); + +#endif // _TERRFEATURETYPES_H_ + diff --git a/Engine/source/terrain/terrFile.cpp b/Engine/source/terrain/terrFile.cpp new file mode 100644 index 000000000..8f7168291 --- /dev/null +++ b/Engine/source/terrain/terrFile.cpp @@ -0,0 +1,852 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "terrain/terrFile.h" + +#include "core/stream/fileStream.h" +#include "core/resourceManager.h" +#include "terrain/terrMaterial.h" +#include "gfx/gfxTextureHandle.h" +#include "gfx/bitmap/gBitmap.h" +#include "platform/profiler.h" +#include "math/mPlane.h" + + +template<> +void* Resource::create( const Torque::Path &path ) +{ + return TerrainFile::load( path ); +} + +template<> ResourceBase::Signature Resource::signature() +{ + return MakeFourCC('t','e','r','d'); +} + + +TerrainFile::TerrainFile() + : mNeedsResaving( false ), + mFileVersion( FILE_VERSION ), + mSize( 256 ) +{ + mLayerMap.setSize( mSize * mSize ); + dMemset( mLayerMap.address(), 0, mLayerMap.memSize() ); + + mHeightMap.setSize( mSize * mSize ); + dMemset( mHeightMap.address(), 0, mHeightMap.memSize() ); +} + +TerrainFile::~TerrainFile() +{ +} + +static U16 calcDev( const PlaneF &pl, const Point3F &pt ) +{ + F32 z = (pl.d + pl.x * pt.x + pl.y * pt.y) / -pl.z; + F32 diff = z - pt.z; + if(diff < 0.0f) + diff = -diff; + + if(diff > 0xFFFF) + return 0xFFFF; + else + return U16(diff); +} + +static U16 Umax( U16 u1, U16 u2 ) +{ + return u1 > u2 ? u1 : u2; +} + + +inline U32 getMostSignificantBit( U32 v ) +{ + U32 bit = 0; + + while ( v >>= 1 ) + bit++; + + return bit; +} + +void TerrainFile::_buildGridMap() +{ + // The grid level count is the same as the + // most significant bit of the size. While + // we loop we take the time to calculate the + // grid memory pool size. + mGridLevels = 0; + U32 size = mSize; + U32 poolSize = size * size; + while ( size >>= 1 ) + { + poolSize += size * size; + mGridLevels++; + } + + mGridMapPool.setSize( poolSize ); + mGridMapPool.compact(); + mGridMap.setSize( mGridLevels + 1 ); + mGridMap.compact(); + + // Assign memory from the pool to each grid level. + TerrainSquare *sq = mGridMapPool.address(); + for ( S32 i = mGridLevels; i >= 0; i-- ) + { + mGridMap[i] = sq; + sq += 1 << ( 2 * ( mGridLevels - i ) ); + } + + for( S32 i = mGridLevels; i >= 0; i-- ) + { + S32 squareCount = 1 << ( mGridLevels - i ); + S32 squareSize = mSize / squareCount; + + for ( S32 squareX = 0; squareX < squareCount; squareX++ ) + { + for ( S32 squareY = 0; squareY < squareCount; squareY++ ) + { + U16 min = 0xFFFF; + U16 max = 0; + U16 mindev45 = 0; + U16 mindev135 = 0; + + // determine max error for both possible splits. + + const Point3F p1(0, 0, getHeight(squareX * squareSize, squareY * squareSize)); + const Point3F p2(0, (F32)squareSize, getHeight(squareX * squareSize, squareY * squareSize + squareSize)); + const Point3F p3((F32)squareSize, (F32)squareSize, getHeight(squareX * squareSize + squareSize, squareY * squareSize + squareSize)); + const Point3F p4((F32)squareSize, 0, getHeight(squareX * squareSize + squareSize, squareY * squareSize)); + + // pl1, pl2 = split45, pl3, pl4 = split135 + const PlaneF pl1(p1, p2, p3); + const PlaneF pl2(p1, p3, p4); + const PlaneF pl3(p1, p2, p4); + const PlaneF pl4(p2, p3, p4); + + bool parentSplit45 = false; + TerrainSquare *parent = NULL; + if ( i < mGridLevels ) + { + parent = findSquare( i+1, squareX * squareSize, squareY * squareSize ); + parentSplit45 = parent->flags & TerrainSquare::Split45; + } + + bool empty = true; + bool hasEmpty = false; + + for ( S32 sizeX = 0; sizeX <= squareSize; sizeX++ ) + { + for ( S32 sizeY = 0; sizeY <= squareSize; sizeY++ ) + { + S32 x = squareX * squareSize + sizeX; + S32 y = squareY * squareSize + sizeY; + + if(sizeX != squareSize && sizeY != squareSize) + { + if ( !isEmptyAt( x, y ) ) + empty = false; + else + hasEmpty = true; + } + + U16 ht = getHeight( x, y ); + if ( ht < min ) + min = ht; + if( ht > max ) + max = ht; + + Point3F pt( (F32)sizeX, (F32)sizeY, (F32)ht ); + U16 dev; + + if(sizeX < sizeY) + dev = calcDev(pl1, pt); + else if(sizeX > sizeY) + dev = calcDev(pl2, pt); + else + dev = Umax(calcDev(pl1, pt), calcDev(pl2, pt)); + + if(dev > mindev45) + mindev45 = dev; + + if(sizeX + sizeY < squareSize) + dev = calcDev(pl3, pt); + else if(sizeX + sizeY > squareSize) + dev = calcDev(pl4, pt); + else + dev = Umax(calcDev(pl3, pt), calcDev(pl4, pt)); + + if(dev > mindev135) + mindev135 = dev; + } + } + + TerrainSquare *sq = findSquare( i, squareX * squareSize, squareY * squareSize ); + sq->minHeight = min; + sq->maxHeight = max; + + sq->flags = empty ? TerrainSquare::Empty : 0; + if ( hasEmpty ) + sq->flags |= TerrainSquare::HasEmpty; + + bool shouldSplit45 = ((squareX ^ squareY) & 1) == 0; + bool split45; + + //split45 = shouldSplit45; + if ( i == 0 ) + split45 = shouldSplit45; + else if( i < 4 && shouldSplit45 == parentSplit45 ) + split45 = shouldSplit45; + else + split45 = mindev45 < mindev135; + + //split45 = shouldSplit45; + if(split45) + { + sq->flags |= TerrainSquare::Split45; + sq->heightDeviance = mindev45; + } + else + sq->heightDeviance = mindev135; + + if( parent ) + if ( parent->heightDeviance < sq->heightDeviance ) + parent->heightDeviance = sq->heightDeviance; + } + } + } + + /* + for ( S32 y = 0; y < mSize; y += 2 ) + { + for ( S32 x=0; x < mSize; x += 2 ) + { + GridSquare *sq = findSquare(1, Point2I(x, y)); + GridSquare *s1 = findSquare(0, Point2I(x, y)); + GridSquare *s2 = findSquare(0, Point2I(x+1, y)); + GridSquare *s3 = findSquare(0, Point2I(x, y+1)); + GridSquare *s4 = findSquare(0, Point2I(x+1, y+1)); + sq->flags |= (s1->flags | s2->flags | s3->flags | s4->flags) & ~(GridSquare::MaterialStart -1); + } + } + */ +} + +void TerrainFile::_initMaterialInstMapping() +{ + mMaterialInstMapping.clearMatInstList(); + + for( U32 i = 0; i < mMaterials.size(); ++ i ) + { + Torque::Path path( mMaterials[ i ]->getDiffuseMap() ); + mMaterialInstMapping.push_back( path.getFileName() ); + } + + mMaterialInstMapping.mapMaterials(); +} + +bool TerrainFile::save( const char *filename ) +{ + FileStream stream; + stream.open( filename, Torque::FS::File::Write ); + if ( stream.getStatus() != Stream::Ok ) + return false; + + stream.write( (U8)FILE_VERSION ); + + stream.write( mSize ); + + // Write out the height map. + for ( U32 i=0; i < mHeightMap.size(); i++) + stream.write( mHeightMap[i] ); + + // Write out the layer map. + for ( U32 i=0; i < mLayerMap.size(); i++) + stream.write( mLayerMap[i] ); + + // Write out the material names. + stream.write( (U32)mMaterials.size() ); + for ( U32 i=0; i < mMaterials.size(); i++ ) + stream.write( String( mMaterials[i]->getInternalName() ) ); + + return stream.getStatus() == FileStream::Ok; +} + +TerrainFile* TerrainFile::load( const Torque::Path &path ) +{ + FileStream stream; + + stream.open( path.getFullPath(), Torque::FS::File::Read ); + if ( stream.getStatus() != Stream::Ok ) + { + Con::errorf( "Resource::create - could not open '%s'", path.getFullPath().c_str() ); + return NULL; + } + + U8 version; + stream.read(&version); + if (version > TerrainFile::FILE_VERSION) + { + Con::errorf( "Resource::create - file version '%i' is newer than engine version '%i'", version, TerrainFile::FILE_VERSION ); + return NULL; + } + + TerrainFile *ret = new TerrainFile; + ret->mFileVersion = version; + ret->mFilePath = path; + + if ( version >= 7 ) + ret->_load( stream ); + else + ret->_loadLegacy( stream ); + + // Update the collision structures. + ret->_buildGridMap(); + + // Do the material mapping. + ret->_initMaterialInstMapping(); + + return ret; +} + +void TerrainFile::_load( FileStream &stream ) +{ + // NOTE: We read using a loop instad of in one large chunk + // because the stream will do endian conversions for us when + // reading one type at a time. + + stream.read( &mSize ); + + // Load the heightmap. + mHeightMap.setSize( mSize * mSize ); + for ( U32 i=0; i < mHeightMap.size(); i++ ) + stream.read( &mHeightMap[i] ); + + // Load the layer index map. + mLayerMap.setSize( mSize * mSize ); + for ( U32 i=0; i < mLayerMap.size(); i++ ) + stream.read( &mLayerMap[i] ); + + // Get the material name count. + U32 materialCount; + stream.read( &materialCount ); + Vector materials; + materials.setSize( materialCount ); + + // Load the material names. + for ( U32 i=0; i < materialCount; i++ ) + stream.read( &materials[i] ); + + // Resolve the TerrainMaterial objects from the names. + _resolveMaterials( materials ); +} + +void TerrainFile::_loadLegacy( FileStream &stream ) +{ + // Some legacy constants. + enum + { + MaterialGroups = 8, + BlockSquareWidth = 256, + }; + + const U32 sampleCount = BlockSquareWidth * BlockSquareWidth; + mSize = BlockSquareWidth; + + // Load the heightmap. + mHeightMap.setSize( sampleCount ); + for ( U32 i=0; i < mHeightMap.size(); i++ ) + stream.read( &mHeightMap[i] ); + + // Prior to version 7 we stored this weird material struct. + const U32 MATERIAL_GROUP_MASK = 0x7; + struct Material + { + enum Flags + { + Plain = 0, + Rotate = 1, + FlipX = 2, + FlipXRotate = 3, + FlipY = 4, + FlipYRotate = 5, + FlipXY = 6, + FlipXYRotate = 7, + RotateMask = 7, + Empty = 8, + Modified = BIT(7), + + // must not clobber TerrainFile::MATERIAL_GROUP_MASK bits! + PersistMask = BIT(7) + }; + + U8 flags; + U8 index; + }; + + // Temp locals for loading before we convert to the new + // version 7+ format. + U8 baseMaterialMap[sampleCount] = { 0 }; + U8 *materialAlphaMap[MaterialGroups] = { 0 }; + Material materialMap[BlockSquareWidth * BlockSquareWidth]; + + // read the material group map and flags... + dMemset(materialMap, 0, sizeof(materialMap)); + + AssertFatal(!(Material::PersistMask & MATERIAL_GROUP_MASK), + "Doh! We have flag clobberage..."); + + for (S32 j=0; j < sampleCount; j++) + { + U8 val; + stream.read(&val); + + // + baseMaterialMap[j] = val & MATERIAL_GROUP_MASK; + materialMap[j].flags = val & Material::PersistMask; + } + + // Load the material names. + Vector materials; + for ( U32 i=0; i < MaterialGroups; i++ ) + { + String matName; + stream.read( &matName ); + if ( matName.isEmpty() ) + continue; + + if ( mFileVersion > 3 && mFileVersion < 6 ) + { + // Between version 3 and 5 we store the texture file names + // relative to the terrain file. We restore the full path + // here so that we can create a TerrainMaterial from it. + materials.push_back( Torque::Path::CompressPath( mFilePath.getRoot() + mFilePath.getPath() + '/' + matName ) ); + } + else + materials.push_back( matName ); + } + + if ( mFileVersion <= 3 ) + { + GFXTexHandle terrainMat; + Torque::Path matRelPath; + + // Try to automatically fix up our material file names + for (U32 i = 0; i < materials.size(); i++) + { + if ( materials[i].isEmpty() ) + continue; + + terrainMat.set( materials[i], &GFXDefaultPersistentProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ) ); + if ( terrainMat ) + continue; + + matRelPath = materials[i]; + + String path = matRelPath.getPath(); + + String::SizeType n = path.find( '/', 0, String::NoCase ); + if ( n != String::NPos ) + { + matRelPath.setPath( String(Con::getVariable( "$defaultGame" )) + path.substr( n, path.length() - n ) ); + + terrainMat.set( matRelPath, &GFXDefaultPersistentProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ) ); + if ( terrainMat ) + { + materials[i] = matRelPath.getFullPath(); + mNeedsResaving = true; + } + } + + } // for (U32 i = 0; i < TerrainBlock::MaterialGroups; i++) + + } // if ( mFileVersion <= 3 ) + + if ( mFileVersion == 1 ) + { + for( S32 j = 0; j < sampleCount; j++ ) + { + if ( materialAlphaMap[baseMaterialMap[j]] == NULL ) + { + materialAlphaMap[baseMaterialMap[j]] = new U8[sampleCount]; + dMemset(materialAlphaMap[baseMaterialMap[j]], 0, sampleCount); + } + + materialAlphaMap[baseMaterialMap[j]][j] = 255; + } + } + else + { + for( S32 k=0; k < materials.size(); k++ ) + { + AssertFatal(materialAlphaMap[k] == NULL, "Bad assumption. There should be no alpha map at this point..."); + materialAlphaMap[k] = new U8[sampleCount]; + stream.read(sampleCount, materialAlphaMap[k]); + } + } + + // Throw away the old texture and heightfield scripts. + if ( mFileVersion >= 3 ) + { + U32 len; + stream.read(&len); + char *textureScript = (char *)dMalloc(len + 1); + stream.read(len, textureScript); + dFree( textureScript ); + + stream.read(&len); + char *heightfieldScript = (char *)dMalloc(len + 1); + stream.read(len, heightfieldScript); + dFree( heightfieldScript ); + } + + // Load and throw away the old edge terrain paths. + if ( mFileVersion >= 5 ) + { + stream.readSTString(true); + stream.readSTString(true); + } + + U32 layerCount = materials.size() - 1; + + // Ok... time to convert all this mess to the layer index map! + for ( U32 i=0; i < sampleCount; i++ ) + { + // Find the greatest layer. + U32 layer = 0; + U32 lastValue = 0; + for ( U32 k=0; k < MaterialGroups; k++ ) + { + if ( materialAlphaMap[k] && materialAlphaMap[k][i] > lastValue ) + { + layer = k; + lastValue = materialAlphaMap[k][i]; + } + } + + // Set the layer index. + mLayerMap[i] = getMin( layer, layerCount ); + } + + // Cleanup. + for ( U32 i=0; i < MaterialGroups; i++ ) + delete [] materialAlphaMap[i]; + + // Force resaving on these old file versions. + //mNeedsResaving = false; + + // Resolve the TerrainMaterial objects from the names. + _resolveMaterials( materials ); +} + +void TerrainFile::_resolveMaterials( const Vector &materials ) +{ + mMaterials.clear(); + + for ( U32 i=0; i < materials.size(); i++ ) + mMaterials.push_back( TerrainMaterial::findOrCreate( materials[i] ) ); + + // If we didn't get any materials then at least + // add a warning material so we will render. + if ( mMaterials.empty() ) + mMaterials.push_back( TerrainMaterial::getWarningMaterial() ); +} + +void TerrainFile::setSize( U32 newSize, bool clear ) +{ + // Make sure the resolution is a power of two. + newSize = getNextPow2( newSize ); + + // + if ( clear ) + { + mLayerMap.setSize( newSize * newSize ); + mLayerMap.compact(); + dMemset( mLayerMap.address(), 0, mLayerMap.memSize() ); + + // Initialize the elevation to something above + // zero so that we have room to excavate by default. + U16 elev = floatToFixed( 512.0f ); + + mHeightMap.setSize( newSize * newSize ); + mHeightMap.compact(); + for ( U32 i = 0; i < mHeightMap.size(); i++ ) + mHeightMap[i] = elev; + } + else + { + // We're resizing here! + + + + } + + mSize = newSize; + + _buildGridMap(); +} + +void TerrainFile::smooth( F32 factor, U32 steps, bool updateCollision ) +{ + const U32 blockSize = mSize * mSize; + + // Grab some temp buffers for our smoothing results. + Vector h1, h2; + h1.setSize( blockSize ); + h2.setSize( blockSize ); + + // Fill the first buffer with the current heights. + for ( U32 i=0; i < blockSize; i++ ) + h1[i] = (F32)mHeightMap[i]; + + // factor of 0.0 = NO Smoothing + // factor of 1.0 = MAX Smoothing + const F32 matrixM = 1.0f - getMax(0.0f, getMin(1.0f, factor)); + const F32 matrixE = (1.0f-matrixM) * (1.0f/12.0f) * 2.0f; + const F32 matrixC = matrixE * 0.5f; + + // Now loop for our interations. + F32 *src = h1.address(); + F32 *dst = h2.address(); + for ( U32 s=0; s < steps; s++ ) + { + for ( S32 y=0; y < mSize; y++ ) + { + for ( S32 x=0; x < mSize; x++ ) + { + F32 samples[9]; + + S32 c = 0; + for (S32 i = y-1; i < y+2; i++) + for (S32 j = x-1; j < x+2; j++) + { + if ( i < 0 || j < 0 || i >= mSize || j >= mSize ) + samples[c++] = src[ x + ( y * mSize ) ]; + else + samples[c++] = src[ j + ( i * mSize ) ]; + } + + // 0 1 2 + // 3 x,y 5 + // 6 7 8 + + dst[ x + ( y * mSize ) ] = + ((samples[0]+samples[2]+samples[6]+samples[8]) * matrixC) + + ((samples[1]+samples[3]+samples[5]+samples[7]) * matrixE) + + (samples[4] * matrixM); + } + } + + // Swap! + F32 *tmp = dst; + dst = src; + src = tmp; + } + + // Copy the results back to the height map. + for ( U32 i=0; i < blockSize; i++ ) + mHeightMap[i] = (U16)mCeil( (F32)src[i] ); + + if ( updateCollision ) + _buildGridMap(); +} + +void TerrainFile::setHeightMap( const Vector &heightmap, bool updateCollision ) +{ + AssertFatal( mHeightMap.size() == heightmap.size(), "TerrainFile::setHeightMap - Incorrect heightmap size!" ); + dMemcpy( mHeightMap.address(), heightmap.address(), mHeightMap.size() ); + + if ( updateCollision ) + _buildGridMap(); +} + +void TerrainFile::import( const GBitmap &heightMap, + F32 heightScale, + const Vector &layerMap, + const Vector &materials ) +{ + AssertFatal( heightMap.getWidth() == heightMap.getHeight(), "TerrainFile::import - Height map is not square!" ); + AssertFatal( isPow2( heightMap.getWidth() ), "TerrainFile::import - Height map is not power of two!" ); + + const U32 newSize = heightMap.getWidth(); + if ( newSize != mSize ) + { + mHeightMap.setSize( newSize * newSize ); + mHeightMap.compact(); + mSize = newSize; + } + + // Convert the height map to heights. + U16 *oBits = mHeightMap.address(); + if ( heightMap.getFormat() == GFXFormatR5G6B5 ) + { + const F32 toFixedPoint = ( 1.0f / (F32)U16_MAX ) * floatToFixed( heightScale ); + const U16 *iBits = (const U16*)heightMap.getBits(); + for ( U32 i = 0; i < mSize * mSize; i++ ) + { + U16 height = convertBEndianToHost( *iBits ); + *oBits = (U16)mCeil( (F32)height * toFixedPoint ); + ++oBits; + ++iBits; + } + } + else + { + const F32 toFixedPoint = ( 1.0f / (F32)U8_MAX ) * floatToFixed( heightScale ); + const U8 *iBits = heightMap.getBits(); + for ( U32 i = 0; i < mSize * mSize; i++ ) + { + *oBits = (U16)mCeil( ((F32)*iBits) * toFixedPoint ); + ++oBits; + iBits += heightMap.getBytesPerPixel(); + } + } + + // Copy over the layer map. + AssertFatal( layerMap.size() == mHeightMap.size(), "TerrainFile::import - Layer map is the wrong size!" ); + mLayerMap = layerMap; + mLayerMap.compact(); + + // Resolve the materials. + _resolveMaterials( materials ); + + // Rebuild the collision grid map. + _buildGridMap(); +} + + +void TerrainFile::create( String *inOutFilename, + U32 newSize, + const Vector &materials ) +{ + // Determine the path and basename - first try using the input filename (mission name) + Torque::Path basePath( *inOutFilename ); + if ( !basePath.getExtension().equal("mis") ) + { + // Use the default path and filename + basePath.setPath( "art/terrains" ); + basePath.setFileName( "terrain" ); + } + + // Construct a default file name + (*inOutFilename) = Torque::FS::MakeUniquePath( basePath.getRootAndPath(), basePath.getFileName(), "ter" ); + + // Create the file + TerrainFile *file = new TerrainFile; + + for ( U32 i=0; i < materials.size(); i++ ) + file->mMaterials.push_back( TerrainMaterial::findOrCreate( materials[i] ) ); + + file->setSize( newSize, true ); + file->save( *inOutFilename ); + + delete file; +} + +inline void getMinMax( U16 &inMin, U16 &inMax, U16 height ) +{ + if ( height < inMin ) + inMin = height; + if ( height > inMax ) + inMax = height; +} + +inline void checkSquare( TerrainSquare *parent, const TerrainSquare *child ) +{ + if(parent->minHeight > child->minHeight) + parent->minHeight = child->minHeight; + if(parent->maxHeight < child->maxHeight) + parent->maxHeight = child->maxHeight; + + if ( child->flags & (TerrainSquare::Empty | TerrainSquare::HasEmpty) ) + parent->flags |= TerrainSquare::HasEmpty; +} + +void TerrainFile::updateGrid( const Point2I &minPt, const Point2I &maxPt ) +{ + // here's how it works: + // for the current terrain renderer we only care about + // the minHeight and maxHeight on the GridSquare + // so we do one pass through, updating minHeight and maxHeight + // on the level 0 squares, then we loop up the grid map from 1 to + // the top, expanding the bounding boxes as necessary. + // this should end up being way, way, way, way faster for the terrain + // editor + + PROFILE_SCOPE( TerrainFile_UpdateGrid ); + + for ( S32 y = minPt.y - 1; y < maxPt.y + 1; y++ ) + { + for ( S32 x = minPt.x - 1; x < maxPt.x + 1; x++ ) + { + S32 px = x; + S32 py = y; + if ( px < 0 ) + px += mSize; + if ( py < 0 ) + py += mSize; + + TerrainSquare *sq = findSquare( 0, px, py ); + sq->minHeight = 0xFFFF; + sq->maxHeight = 0; + + // Update the empty state. + if ( isEmptyAt( x, y ) ) + sq->flags |= TerrainSquare::Empty; + else + sq->flags &= ~TerrainSquare::Empty; + + getMinMax( sq->minHeight, sq->maxHeight, getHeight( x, y ) ); + getMinMax( sq->minHeight, sq->maxHeight, getHeight( x+1, y ) ); + getMinMax( sq->minHeight, sq->maxHeight, getHeight( x, y+1 ) ); + getMinMax( sq->minHeight, sq->maxHeight, getHeight( x+1, y+1 ) ); + } + } + + // ok, all the level 0 grid squares are updated: + // now update all the parent grid squares that need to be updated: + for( S32 level = 1; level <= mGridLevels; level++ ) + { + S32 size = 1 << level; + S32 halfSize = size >> 1; + + for( S32 y = (minPt.y - 1) >> level; y < (maxPt.y + size) >> level; y++ ) + { + for ( S32 x = (minPt.x - 1) >> level; x < (maxPt.x + size) >> level; x++ ) + { + S32 px = x << level; + S32 py = y << level; + + TerrainSquare *sq = findSquare(level, px, py); + sq->minHeight = 0xFFFF; + sq->maxHeight = 0; + sq->flags &= ~( TerrainSquare::Empty | TerrainSquare::HasEmpty ); + + checkSquare( sq, findSquare( level - 1, px, py ) ); + checkSquare( sq, findSquare( level - 1, px + halfSize, py ) ); + checkSquare( sq, findSquare( level - 1, px, py + halfSize ) ); + checkSquare( sq, findSquare( level - 1, px + halfSize, py + halfSize ) ); + } + } + } +} diff --git a/Engine/source/terrain/terrFile.h b/Engine/source/terrain/terrFile.h new file mode 100644 index 000000000..04bac3d9b --- /dev/null +++ b/Engine/source/terrain/terrFile.h @@ -0,0 +1,282 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TERRFILE_H_ +#define _TERRFILE_H_ + +#ifndef _TVECTOR_H_ +#include +#endif +#ifndef _PATH_H_ +#include +#endif +#ifndef _MATERIALLIST_H_ +#include "materials/materialList.h" +#endif +#ifndef _TERRMATERIAL_H_ +#include "terrain/terrMaterial.h" +#endif + +class TerrainMaterial; +class FileStream; +class GBitmap; + + +/// +struct TerrainSquare +{ + U16 minHeight; + + U16 maxHeight; + + U16 heightDeviance; + + U16 flags; + + enum + { + Split45 = BIT(0), + + Empty = BIT(1), + + HasEmpty = BIT(2), + }; +}; + + +/// NOTE: The terrain uses 11.5 fixed point which gives +/// us a height range from 0->2048 in 1/32 increments. +typedef U16 TerrainHeight; + + +/// +class TerrainFile +{ +protected: + + friend class TerrainBlock; + + /// The materials used to render the terrain. + Vector mMaterials; + + /// The dimensions of the layer and height maps. + U32 mSize; + + /// The layer index at each height map sample. + Vector mLayerMap; + + /// The fixed point height map. + /// @see fixedToFloat + Vector mHeightMap; + + /// The memory pool used by the grid map layers. + Vector mGridMapPool; + + /// + U32 mGridLevels; + + /// The grid map layers used to accelerate collision + /// queries for the height map data. + Vector mGridMap; + + /// MaterialList used to map terrain materials to material instances for the + /// sake of collision (physics, etc.). + MaterialList mMaterialInstMapping; + + /// The file version. + U32 mFileVersion; + + /// The dirty flag. + bool mNeedsResaving; + + /// The full path and name of the TerrainFile + Torque::Path mFilePath; + + /// The internal loading function. + void _load( FileStream &stream ); + + /// The legacy file loading code. + void _loadLegacy( FileStream &stream ); + + /// Used to populate the materail vector by finding the + /// TerrainMaterial objects by name. + void _resolveMaterials( const Vector &materials ); + + /// + void _buildGridMap(); + + /// + void _initMaterialInstMapping(); + +public: + + enum Constants + { + FILE_VERSION = 7 + }; + + TerrainFile(); + + virtual ~TerrainFile(); + + /// + static void create( String *inOutFilename, + U32 newSize, + const Vector &materials ); + + /// + static TerrainFile* load( const Torque::Path &path ); + + bool save( const char *filename ); + + /// + void import( const GBitmap &heightMap, + F32 heightScale, + const Vector &layerMap, + const Vector &materials ); + + /// Updates the terrain grid for the specified area. + void updateGrid( const Point2I &minPt, const Point2I &maxPt ); + + /// Performs multiple smoothing steps on the heightmap. + void smooth( F32 factor, U32 steps, bool updateCollision ); + + void setSize( U32 newResolution, bool clear ); + + TerrainSquare* findSquare( U32 level, U32 x, U32 y ) const; + + BaseMatInstance* getMaterialMapping( U32 index ) const; + + StringTableEntry getMaterialName( U32 x, U32 y) const; + + void setLayerIndex( U32 x, U32 y, U8 index ); + + U8 getLayerIndex( U32 x, U32 y ) const; + + bool isEmptyAt( U32 x, U32 y ) const { return getLayerIndex( x, y ) == U8_MAX; } + + void setHeight( U32 x, U32 y, U16 height ); + + const U16* getHeightAddress( U32 x, U32 y ) const; + + U16 getHeight( U32 x, U32 y ) const; + + U16 getMaxHeight() const { return mGridMap[mGridLevels]->maxHeight; } + + /// Returns the constant heightmap vector. + const Vector& getHeightMap() const { return mHeightMap; } + + /// Sets a new heightmap state. + void setHeightMap( const Vector &heightmap, bool updateCollision ); + + /// Check if the given point is valid within the (non-tiled) terrain file. + bool isPointInTerrain( U32 x, U32 y ) const; +}; + + +inline TerrainSquare* TerrainFile::findSquare( U32 level, U32 x, U32 y ) const +{ + x %= mSize; + y %= mSize; + x >>= level; + y >>= level; + + return mGridMap[level] + x + ( y << ( mGridLevels - level ) ); +} + +inline void TerrainFile::setHeight( U32 x, U32 y, U16 height ) +{ + x %= mSize; + y %= mSize; + mHeightMap[ x + ( y * mSize ) ] = height; +} + +inline const U16* TerrainFile::getHeightAddress( U32 x, U32 y ) const +{ + x %= mSize; + y %= mSize; + return &mHeightMap[ x + ( y * mSize ) ]; +} + +inline U16 TerrainFile::getHeight( U32 x, U32 y ) const +{ + x %= mSize; + y %= mSize; + return mHeightMap[ x + ( y * mSize ) ]; +} + +inline U8 TerrainFile::getLayerIndex( U32 x, U32 y ) const +{ + x %= mSize; + y %= mSize; + return mLayerMap[ x + ( y * mSize ) ]; +} + +inline void TerrainFile::setLayerIndex( U32 x, U32 y, U8 index ) +{ + x %= mSize; + y %= mSize; + mLayerMap[ x + ( y * mSize ) ] = index; +} + +inline BaseMatInstance* TerrainFile::getMaterialMapping( U32 index ) const +{ + if ( index < mMaterialInstMapping.size() ) + return mMaterialInstMapping.getMaterialInst( index ); + else + return NULL; +} + +inline StringTableEntry TerrainFile::getMaterialName( U32 x, U32 y) const +{ + x %= mSize; + y %= mSize; + const U8 &index = mLayerMap[ x + ( y * mSize ) ]; + + if ( index < mMaterials.size() ) + return mMaterials[ index ]->getInternalName(); + + return StringTable->EmptyString(); +} + + +/// Conversion from 11.5 fixed point to floating point. +inline F32 fixedToFloat( U16 val ) +{ + return F32(val) * 0.03125f; +} + +/// Conversion from floating point to 11.5 fixed point. +inline U16 floatToFixed( F32 val ) +{ + return U16(val * 32.0); +} + +inline bool TerrainFile::isPointInTerrain( U32 x, U32 y ) const +{ + if ( x < mSize && y < mSize) + return true; + + return false; +} + +#endif // _TERRFILE_H_ diff --git a/Engine/source/terrain/terrImport.cpp b/Engine/source/terrain/terrImport.cpp new file mode 100644 index 000000000..bbc36a5d6 --- /dev/null +++ b/Engine/source/terrain/terrImport.cpp @@ -0,0 +1,313 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "terrain/terrData.h" +#include "gfx/bitmap/gBitmap.h" +#include "sim/netConnection.h" +#include "core/strings/stringUnit.h" +#include "core/resourceManager.h" +#include "gui/worldEditor/terrainEditor.h" +#include "util/noise2d.h" +#include "core/volume.h" + + +ConsoleStaticMethod( TerrainBlock, createNew, S32, 5, 5, + "TerrainBlock.create( String terrainName, U32 resolution, String materialName, bool genNoise )\n" + "" ) +{ + const UTF8 *terrainName = argv[1]; + U32 resolution = dAtoi( argv[2] ); + const UTF8 *materialName = argv[3]; + bool genNoise = dAtob( argv[4] ); + + Vector materials; + materials.push_back( materialName ); + + TerrainBlock *terrain = new TerrainBlock(); + + // We create terrains based on level name. If the user wants to rename the terrain names; they have to + // rename it themselves in their file browser. The main reason for this is so we can easily increment for ourselves; + // and because its too easy to rename the terrain object and forget to take care of the terrain filename afterwards. + FileName terrFileName( Con::getVariable("$Client::MissionFile") ); + terrFileName.replace("tools/levels/", "art/terrains/"); + terrFileName.replace("levels/", "art/terrains/"); + + TerrainFile::create( &terrFileName, resolution, materials ); + + if( !terrain->setFile( terrFileName ) ) + { + Con::errorf( "TerrainBlock::createNew - error creating '%s'", terrFileName.c_str() ); + return 0; + } + + terrain->setPosition( Point3F( 0, 0, 0 ) ); + + const U32 blockSize = terrain->getBlockSize(); + + if ( genNoise ) + { + TerrainFile *file = terrain->getFile(); + + Vector floatHeights; + floatHeights.setSize( blockSize * blockSize ); + + Noise2D noise; + noise.setSeed( 134208587 ); + + // Set up some defaults. + F32 octaves = 3.0f; + U32 freq = 4; + F32 roughness = 0.0f; + noise.fBm( &floatHeights, blockSize, freq, 1.0f - roughness, octaves ); + + F32 height = 0; + + F32 omax, omin; + noise.getMinMax( &floatHeights, &omin, &omax, blockSize ); + + F32 terrscale = 300.0f / (omax - omin); + for ( S32 y = 0; y < blockSize; y++ ) + { + for ( S32 x = 0; x < blockSize; x++ ) + { + // Very important to subtract the min + // noise value when using the noise functions + // for terrain, otherwise floatToFixed() will + // wrap negative values to U16_MAX, creating + // a very ugly terrain. + height = (floatHeights[ x + (y * blockSize) ] - omin) * terrscale + 30.0f; + file->setHeight( x, y, floatToFixed( height ) ); + } + } + + terrain->updateGrid( Point2I::Zero, Point2I( blockSize, blockSize ) ); + terrain->updateGridMaterials( Point2I::Zero, Point2I( blockSize, blockSize ) ); + } + + terrain->registerObject( terrainName ); + + // Add to mission group! + SimGroup *missionGroup; + if( Sim::findObject( "MissionGroup", missionGroup ) ) + missionGroup->addObject( terrain ); + + return terrain->getId(); +} + +ConsoleStaticMethod( TerrainBlock, import, S32, 7, 7, + "( String terrainName, String heightMap, F32 metersPerPixel, F32 heightScale, String materials, String opacityLayers )\n" + "" ) +{ + // Get the parameters. + const UTF8 *terrainName = argv[1]; + const UTF8 *hmap = argv[2]; + F32 metersPerPixel = dAtof(argv[3]); + F32 heightScale = dAtof(argv[4]); + const UTF8 *opacityFiles = argv[5]; + const UTF8 *materialsStr = argv[6]; + + // First load the height map and validate it. + Resource heightmap = GBitmap::load( hmap ); + if ( !heightmap ) + { + Con::errorf( "Heightmap failed to load!" ); + return 0; + } + + U32 terrSize = heightmap->getWidth(); + U32 hheight = heightmap->getHeight(); + if ( terrSize != hheight || !isPow2( terrSize ) ) + { + Con::errorf( "Height map must be square and power of two in size!" ); + return 0; + } + else if ( terrSize < 128 || terrSize > 4096 ) + { + Con::errorf( "Height map must be between 128 and 4096 in size!" ); + return 0; + } + + U32 fileCount = StringUnit::getUnitCount( opacityFiles, "\n" ); + Vector layerMap; + layerMap.setSize( terrSize * terrSize ); + { + Vector bitmaps; + + for ( U32 i = 0; i < fileCount; i++ ) + { + String fileNameWithChannel = StringUnit::getUnit( opacityFiles, i, "\n" ); + String fileName = StringUnit::getUnit( fileNameWithChannel, 0, "\t" ); + String channel = StringUnit::getUnit( fileNameWithChannel, 1, "\t" ); + + if ( fileName.isEmpty() ) + continue; + + if ( !channel.isEmpty() ) + { + // Load and push back the bitmap here. + Resource opacityMap = ResourceManager::get().load( fileName ); + if ( terrSize != opacityMap->getWidth() || terrSize != opacityMap->getHeight() ) + { + Con::errorf( "The opacity map '%s' doesn't match height map size!", fileName.c_str() ); + return 0; + } + + // Always going to be one channel. + GBitmap *opacityMapChannel = new GBitmap( terrSize, + terrSize, + false, + GFXFormatA8 ); + + if ( opacityMap->getBytesPerPixel() > 1 ) + { + if ( channel.equal( "R", 1 ) ) + opacityMap->copyChannel( 0, opacityMapChannel ); + else if ( channel.equal( "G", 1 ) ) + opacityMap->copyChannel( 1, opacityMapChannel ); + else if ( channel.equal( "B", 1 ) ) + opacityMap->copyChannel( 2, opacityMapChannel ); + else if ( channel.equal( "A", 1 ) ) + opacityMap->copyChannel( 3, opacityMapChannel ); + + bitmaps.push_back( opacityMapChannel ); + } + else + { + opacityMapChannel->copyRect( opacityMap, RectI( 0, 0, terrSize, terrSize ), Point2I( 0, 0 ) ); + bitmaps.push_back( opacityMapChannel ); + } + } + } + + // Ok... time to convert all this opacity layer + // mess to the layer index map! + U32 layerCount = bitmaps.size() - 1; + U32 layer, lastValue; + U8 value; + for ( U32 i = 0; i < terrSize * terrSize; i++ ) + { + // Find the greatest layer. + layer = lastValue = 0; + for ( U32 k=0; k < bitmaps.size(); k++ ) + { + value = bitmaps[k]->getBits()[i]; + if ( value >= lastValue ) + { + layer = k; + lastValue = value; + } + } + + // Set the layer index. + layerMap[i] = getMin( layer, layerCount ); + } + + // Cleanup the bitmaps. + for ( U32 i=0; i < bitmaps.size(); i++ ) + delete bitmaps[i]; + } + + U32 matCount = StringUnit::getUnitCount( materialsStr, "\t\n" ); + if( matCount != fileCount) + { + Con::errorf("Number of Materials and Layer maps must be equal."); + return 0; + } + + Vector materials; + for ( U32 i = 0; i < matCount; i++ ) + { + String matStr = StringUnit::getUnit( materialsStr, i, "\t\n" ); + // even if matStr is empty, insert it as a placeholder (will be replaced with warning material later) + materials.push_back( matStr ); + } + + // Do we have an existing terrain with that name... then update it! + TerrainBlock *terrain = dynamic_cast( Sim::findObject( terrainName ) ); + if ( terrain ) + terrain->import( (*heightmap), heightScale, metersPerPixel, layerMap, materials ); + else + { + terrain = new TerrainBlock(); + terrain->assignName( terrainName ); + terrain->import( (*heightmap), heightScale, metersPerPixel, layerMap, materials ); + terrain->registerObject(); + + // Add to mission group! + SimGroup *missionGroup; + if ( Sim::findObject( "MissionGroup", missionGroup ) ) + missionGroup->addObject( terrain ); + } + + return terrain->getId(); +} + +bool TerrainBlock::import( const GBitmap &heightMap, + F32 heightScale, + F32 metersPerPixel, + const Vector &layerMap, + const Vector &materials ) +{ + AssertFatal( isServerObject(), "TerrainBlock::import - This should only be called on the server terrain!" ); + + AssertFatal( heightMap.getWidth() == heightMap.getHeight(), "TerrainBlock::import - Height map is not square!" ); + AssertFatal( isPow2( heightMap.getWidth() ), "TerrainBlock::import - Height map is not power of two!" ); + + // If we don't have a terrain file then add one. + if ( !mFile ) + { + // Get a unique file name for the terrain. + String fileName( getName() ); + if ( fileName.isEmpty() ) + fileName = "terrain"; + mTerrFileName = FS::MakeUniquePath( "levels", fileName, "ter" ); + + // TODO: We have to save and reload the file to get + // it into the resource system. This creates lots + // of temporary unused files when the terrain is + // discarded because of undo or quit. + TerrainFile *file = new TerrainFile; + file->save( mTerrFileName ); + delete file; + mFile = ResourceManager::get().load( mTerrFileName ); + } + + // The file does a bunch of the work. + mFile->import( heightMap, heightScale, layerMap, materials ); + + // Set the square size. + mSquareSize = metersPerPixel; + + if ( isProperlyAdded() ) + { + // Update the server bounds. + _updateBounds(); + + // Make sure the client gets updated. + setMaskBits( HeightMapChangeMask | SizeMask ); + } + + return true; +} + diff --git a/Engine/source/terrain/terrLighting.cpp b/Engine/source/terrain/terrLighting.cpp new file mode 100644 index 000000000..6c15f0dc4 --- /dev/null +++ b/Engine/source/terrain/terrLighting.cpp @@ -0,0 +1,92 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "terrain/terrRender.h" +#include "lighting/lightInfo.h" +#include "scene/sceneRenderState.h" + +/* + +U32 TerrainRender::testSquareLights(GridSquare *sq, S32 level, const Point2I &pos, U32 lightMask) +{ + + // Calculate our Box3F for this GridSquare + Point3F boxMin(pos.x * mSquareSize + mBlockPos.x, pos.y * mSquareSize + mBlockPos.y, fixedToFloat(sq->minHeight)); + F32 blockSize = F32(mSquareSize * (1 << level)); + F32 blockHeight = fixedToFloat(sq->maxHeight - sq->minHeight); + Point3F boxMax(boxMin); + boxMax += Point3F(blockSize, blockSize, blockHeight); + Box3F gridBox(boxMin, boxMax); + + U32 retMask = 0; + + for(S32 i = 0; (lightMask >> i) != 0; i++) + { + if(lightMask & (1 << i)) + { + if (mTerrainLights[i].light->mType != LightInfo::Vector) + { + // test the visibility of this light to box + F32 dist = gridBox.getDistanceFromPoint(mTerrainLights[i].pos); + static F32 minDist = 1e14f; + minDist = getMin(minDist, dist); + if(dist < mTerrainLights[i].radius) + retMask |= (1 << i); + } else { + retMask |= (1 << i); + } + } + } + return retMask; +} + +void TerrainRender::buildLightArray(SceneState * state) +{ + PROFILE_SCOPE(TerrainRender_buildLightArray); + + mDynamicLightCount = 0; + if ((mTerrainLighting == NULL) || (!TerrainRender::mEnableTerrainDynLights)) + return; + + static LightInfoList lights; + lights.clear(); + + LIGHTMGR->getBestLights(lights); + // create terrain lights from these... + U32 curIndex = 0; + for(U32 i = 0; i < lights.size(); i++) + { + LightInfo* light = lights[i]; + if((light->mType != LightInfo::Point) && (light->mType != LightInfo::Spot)) + continue; + + // set the 'fo + TerrLightInfo & info = mTerrainLights[curIndex++]; + mCurrentBlock->getWorldTransform().mulP(light->mPos, &info.pos); + info.radius = light->getRadius(); + info.light = light; + } + + mDynamicLightCount = curIndex; +} + +*/ diff --git a/Engine/source/terrain/terrMaterial.cpp b/Engine/source/terrain/terrMaterial.cpp new file mode 100644 index 000000000..ee166f8c8 --- /dev/null +++ b/Engine/source/terrain/terrMaterial.cpp @@ -0,0 +1,165 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "terrain/terrMaterial.h" +#include "console/consoleTypes.h" +#include "gfx/bitmap/gBitmap.h" + + +IMPLEMENT_CONOBJECT( TerrainMaterial ); + +ConsoleDocClass( TerrainMaterial, + "@brief The TerrainMaterial class orginizes the material settings " + "for a single terrain material layer.\n\n" + + "@note You should not be creating TerrainMaterials by hand in code. " + "All TerrainMaterials should be created in the editors, as intended " + "by the system.\n\n" + + "@tsexample\n" + "// Created by the Terrain Painter tool in the World Editor\n" + "new TerrainMaterial()\n" + "{\n" + " internalName = \"grass1\";\n" + " diffuseMap = \"art/terrains/Test/grass1\";\n" + " detailMap = \"art/terrains/Test/grass1_d\";\n" + " detailSize = \"10\";\n" + " isManaged = \"1\";\n" + " detailBrightness = \"1\";\n" + " Enabled = \"1\";\n" + " diffuseSize = \"200\";\n" + "};\n" + "@endtsexample\n\n" + + "@see Materials\n" + + "@ingroup enviroMisc\n"); + +TerrainMaterial::TerrainMaterial() + : mSideProjection( false ), + mDiffuseSize( 500.0f ), + mDetailSize( 5.0f ), + mDetailStrength( 1.0f ), + mDetailDistance( 50.0f ), + mParallaxScale( 0.0f ) +{ +} + +TerrainMaterial::~TerrainMaterial() +{ +} + +void TerrainMaterial::initPersistFields() +{ + addField( "diffuseMap", TypeStringFilename, Offset( mDiffuseMap, TerrainMaterial ), "Base texture for the material" ); + addField( "diffuseSize", TypeF32, Offset( mDiffuseSize, TerrainMaterial ), "Used to scale the diffuse map to the material square" ); + + addField( "normalMap", TypeStringFilename, Offset( mNormalMap, TerrainMaterial ), "Bump map for the material" ); + + addField( "detailMap", TypeStringFilename, Offset( mDetailMap, TerrainMaterial ), "Detail map for the material" ); + addField( "detailSize", TypeF32, Offset( mDetailSize, TerrainMaterial ), "Used to scale the detail map to the material square" ); + + addField( "detailStrength", TypeF32, Offset( mDetailStrength, TerrainMaterial ), "Exponentially sharpens or lightens the detail map rendering on the material" ); + addField( "detailDistance", TypeF32, Offset( mDetailDistance, TerrainMaterial ), "Changes how far camera can see the detail map rendering on the material" ); + addField( "useSideProjection", TypeBool, Offset( mSideProjection, TerrainMaterial ),"Makes that terrain material project along the sides of steep " + "slopes instead of projected downwards"); + addField( "parallaxScale", TypeF32, Offset( mParallaxScale, TerrainMaterial ), "Used to scale the height from the normal map to give some self " + "occlusion effect (aka parallax) to the terrain material" ); + + Parent::initPersistFields(); + + // Gotta call this at least once or it won't get created! + Sim::getTerrainMaterialSet(); +} + +bool TerrainMaterial::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + SimSet *set = Sim::getTerrainMaterialSet(); + + // Make sure we have an internal name set. + if ( !mInternalName || !mInternalName[0] ) + Con::warnf( "TerrainMaterial::onAdd() - No internal name set!" ); + else + { + SimObject *object = set->findObjectByInternalName( mInternalName ); + if ( object ) + Con::warnf( "TerrainMaterial::onAdd() - Internal name collision; '%s' already exists!", mInternalName ); + } + + set->addObject( this ); + + return true; +} + +TerrainMaterial* TerrainMaterial::getWarningMaterial() +{ + return findOrCreate( NULL ); +} + +TerrainMaterial* TerrainMaterial::findOrCreate( const char *nameOrPath ) +{ + SimSet *set = Sim::getTerrainMaterialSet(); + + if ( !nameOrPath || !nameOrPath[0] ) + nameOrPath = "warning_material"; + + // See if we can just find it. + TerrainMaterial *mat = dynamic_cast( set->findObjectByInternalName( StringTable->insert( nameOrPath ) ) ); + if ( mat ) + return mat; + + // We didn't find it... so see if its a path to a + // file. If it is lets assume its the texture. + if ( GBitmap::sFindFiles( nameOrPath, NULL ) ) + { + mat = new TerrainMaterial(); + mat->setInternalName( nameOrPath ); + mat->mDiffuseMap = nameOrPath; + mat->registerObject(); + Sim::getRootGroup()->addObject( mat ); + return mat; + } + + // Ok... return a debug material then. + mat = dynamic_cast( set->findObjectByInternalName( StringTable->insert( "warning_material" ) ) ); + if ( !mat ) + { + // This shouldn't happen.... the warning_texture should + // have already been defined in script, but we put this + // fallback here just in case it gets "lost". + mat = new TerrainMaterial(); + mat->setInternalName( "warning_material" ); + mat->mDiffuseMap = "core/art/warnMat.png"; + mat->mDiffuseSize = 500; + mat->mDetailMap = "core/art/warnMat.png"; + mat->mDetailSize = 5; + mat->registerObject(); + + Sim::getRootGroup()->addObject( mat ); + } + + return mat; +} diff --git a/Engine/source/terrain/terrMaterial.h b/Engine/source/terrain/terrMaterial.h new file mode 100644 index 000000000..19d63d509 --- /dev/null +++ b/Engine/source/terrain/terrMaterial.h @@ -0,0 +1,111 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TERRMATERIAL_H_ +#define _TERRMATERIAL_H_ + +#ifndef _SIMBASE_H_ +#include "console/simBase.h" +#endif + + +/// The TerrainMaterial class orginizes the material settings +/// for a single terrain material layer. +class TerrainMaterial : public SimObject +{ + typedef SimObject Parent; + +protected: + + /// + FileName mDiffuseMap; + + /// The size of the diffuse base map in meters + /// used to generate its texture coordinates. + F32 mDiffuseSize; + + /// + FileName mNormalMap; + + /// + FileName mDetailMap; + + /// The size of the detail map in meters used + /// to generate the texture coordinates for the + /// detail and normal maps. + F32 mDetailSize; + + /// + F32 mDetailStrength; + + /// + F32 mDetailDistance; + + /// Normally the detail is projected on to the xy + /// coordinates of the terrain. If this flag is true + /// then this detail is projected along the xz and yz + /// planes. + bool mSideProjection; + + /// + F32 mParallaxScale; + +public: + + TerrainMaterial(); + virtual ~TerrainMaterial(); + + bool onAdd(); + static void initPersistFields(); + + DECLARE_CONOBJECT( TerrainMaterial ); + + /// This method locates the TerrainMaterial if it exists, tries + /// to create a new one if a valid texture path was passed, or + /// returns a debug material if all else fails. + static TerrainMaterial* findOrCreate( const char *nameOrPath ); + + /// Returns the default warning terrain material used when + /// a material is not found or defined. + static TerrainMaterial* getWarningMaterial(); + + const String& getDiffuseMap() const { return mDiffuseMap; } + + F32 getDiffuseSize() const { return mDiffuseSize; } + + const String& getNormalMap() const { return mNormalMap; } + + const String& getDetailMap() const { return mDetailMap; } + + F32 getDetailSize() const { return mDetailSize; } + + F32 getDetailStrength() const { return mDetailStrength; } + + F32 getDetailDistance() const { return mDetailDistance; } + + bool useSideProjection() const { return mSideProjection; } + + F32 getParallaxScale() const { return mParallaxScale; } + +}; + +#endif // _TERRMATERIAL_H_ diff --git a/Engine/source/terrain/terrRender.cpp b/Engine/source/terrain/terrRender.cpp new file mode 100644 index 000000000..ac71f656f --- /dev/null +++ b/Engine/source/terrain/terrRender.cpp @@ -0,0 +1,493 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "terrain/terrRender.h" + +#include "terrain/terrData.h" +#include "terrain/terrCell.h" +#include "terrain/terrMaterial.h" +#include "terrain/terrCellMaterial.h" +#include "materials/shaderData.h" + +#include "platform/profiler.h" +#include "scene/sceneRenderState.h" +#include "math/util/frustum.h" +#include "renderInstance/renderPassManager.h" +#include "renderInstance/renderTerrainMgr.h" + +#include "lighting/lightInfo.h" +#include "lighting/lightManager.h" + +#include "materials/matInstance.h" +#include "materials/materialManager.h" +#include "materials/matTextureTarget.h" +#include "shaderGen/conditionerFeature.h" + +#include "gfx/gfxDrawUtil.h" + +#include "gfx/gfxTransformSaver.h" +#include "gfx/bitmap/gBitmap.h" +#include "gfx/bitmap/ddsFile.h" +#include "gfx/bitmap/ddsUtils.h" +#include "terrain/terrMaterial.h" +#include "gfx/gfxDebugEvent.h" +#include "gfx/gfxCardProfile.h" +#include "core/stream/fileStream.h" + + +bool TerrainBlock::smDebugRender = false; + + +GFX_ImplementTextureProfile( TerrainLayerTexProfile, + GFXTextureProfile::DiffuseMap, + GFXTextureProfile::PreserveSize | + GFXTextureProfile::Dynamic, + GFXTextureProfile::None ); + + +void TerrainBlock::_onFlushMaterials() +{ + if ( mCell ) + mCell->deleteMaterials(); + + SAFE_DELETE( mBaseMaterial ); +} + +void TerrainBlock::_updateMaterials() +{ + mBaseTextures.setSize( mFile->mMaterials.size() ); + + mMaxDetailDistance = 0.0f; + + for ( U32 i=0; i < mFile->mMaterials.size(); i++ ) + { + TerrainMaterial *mat = mFile->mMaterials[i]; + + if( !mat->getDiffuseMap().isEmpty() ) + mBaseTextures[i].set( mat->getDiffuseMap(), + &GFXDefaultStaticDiffuseProfile, + "TerrainBlock::_updateMaterials() - DiffuseMap" ); + else + mBaseTextures[ i ] = GFXTexHandle(); + + // Find the maximum detail distance. + if ( mat->getDetailMap().isNotEmpty() && + mat->getDetailDistance() > mMaxDetailDistance ) + mMaxDetailDistance = mat->getDetailDistance(); + } + + if ( mCell ) + mCell->deleteMaterials(); +} + +void TerrainBlock::_updateLayerTexture() +{ + const U32 layerSize = mFile->mSize; + const Vector &layerMap = mFile->mLayerMap; + const U32 pixelCount = layerMap.size(); + + if ( mLayerTex.isNull() || + mLayerTex.getWidth() != layerSize || + mLayerTex.getHeight() != layerSize ) + mLayerTex.set( layerSize, layerSize, GFXFormatR8G8B8A8, &TerrainLayerTexProfile, "" ); + + AssertFatal( mLayerTex.getWidth() == layerSize && + mLayerTex.getHeight() == layerSize, + "TerrainBlock::_updateLayerTexture - The texture size doesn't match the requested size!" ); + + // Update the layer texture. + GFXLockedRect *lock = mLayerTex.lock(); + + for ( U32 i=0; i < pixelCount; i++ ) + { + lock->bits[0] = layerMap[i]; + + if ( i + 1 >= pixelCount ) + lock->bits[1] = lock->bits[0]; + else + lock->bits[1] = layerMap[i+1]; + + if ( i + layerSize >= pixelCount ) + lock->bits[2] = lock->bits[0]; + else + lock->bits[2] = layerMap[i + layerSize]; + + if ( i + layerSize + 1 >= pixelCount ) + lock->bits[3] = lock->bits[0]; + else + lock->bits[3] = layerMap[i + layerSize + 1]; + + lock->bits += 4; + } + + mLayerTex.unlock(); + //mLayerTex->dumpToDisk( "png", "./layerTex.png" ); +} + +bool TerrainBlock::_initBaseShader() +{ + ShaderData *shaderData = NULL; + if ( !Sim::findObject( "TerrainBlendShader", shaderData ) || !shaderData ) + return false; + + mBaseShader = shaderData->getShader(); + + mBaseShaderConsts = mBaseShader->allocConstBuffer(); + mBaseTexScaleConst = mBaseShader->getShaderConstHandle( "$texScale" ); + mBaseTexIdConst = mBaseShader->getShaderConstHandle( "$texId" ); + mBaseLayerSizeConst = mBaseShader->getShaderConstHandle( "$layerSize" ); + + mBaseTarget = GFX->allocRenderToTextureTarget(); + + GFXStateBlockDesc desc; + desc.samplersDefined = true; + desc.samplers[0] = GFXSamplerStateDesc::getClampPoint(); + desc.samplers[1] = GFXSamplerStateDesc::getWrapLinear(); + desc.zDefined = true; + desc.zWriteEnable = false; + desc.zEnable = false; + desc.setBlend( true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha ); + desc.cullDefined = true; + desc.cullMode = GFXCullNone; + mBaseShaderSB = GFX->createStateBlock( desc ); + + return true; +} + +void TerrainBlock::_updateBaseTexture( bool writeToCache ) +{ + if ( !mBaseShader && !_initBaseShader() ) + return; + + // This can sometimes occur outside a begin/end scene. + const bool sceneBegun = GFX->canCurrentlyRender(); + if ( !sceneBegun ) + GFX->beginScene(); + + GFXDEBUGEVENT_SCOPE( TerrainBlock_UpdateBaseTexture, ColorI::GREEN ); + + PROFILE_SCOPE( TerrainBlock_UpdateBaseTexture ); + + GFXTransformSaver saver; + + const U32 maxTextureSize = GFX->getCardProfiler()->queryProfile( "maxTextureSize", 1024 ); + + U32 baseTexSize = getNextPow2( mBaseTexSize ); + baseTexSize = getMin( maxTextureSize, baseTexSize ); + Point2I destSize( baseTexSize, baseTexSize ); + + // Setup geometry + GFXVertexBufferHandle vb; + { + F32 copyOffsetX = 2.0f * GFX->getFillConventionOffset() / (F32)destSize.x; + F32 copyOffsetY = 2.0f * GFX->getFillConventionOffset() / (F32)destSize.y; + + const bool needsYFlip = GFX->getAdapterType() == OpenGL; + + GFXVertexPT points[4]; + points[0].point = Point3F( -1.0 - copyOffsetX, -1.0 + copyOffsetY, 0.0 ); + points[0].texCoord = Point2F( 0.0, needsYFlip ? 0.0f : 1.0f ); + points[1].point = Point3F( -1.0 - copyOffsetX, 1.0 + copyOffsetY, 0.0 ); + points[1].texCoord = Point2F( 0.0, needsYFlip ? 1.0f : 0.0f ); + points[2].point = Point3F( 1.0 - copyOffsetX, 1.0 + copyOffsetY, 0.0 ); + points[2].texCoord = Point2F( 1.0, needsYFlip ? 1.0f : 0.0f ); + points[3].point = Point3F( 1.0 - copyOffsetX, -1.0 + copyOffsetY, 0.0 ); + points[3].texCoord = Point2F( 1.0, needsYFlip ? 0.0f : 1.0f ); + + vb.set( GFX, 4, GFXBufferTypeVolatile ); + dMemcpy( vb.lock(), points, sizeof(GFXVertexPT) * 4 ); + vb.unlock(); + } + + GFXTexHandle blendTex; + + // If the base texture is already a valid render target then + // use it to render to else we create one. + if ( mBaseTex.isValid() && + mBaseTex->isRenderTarget() && + mBaseTex->getFormat() == GFXFormatR8G8B8A8 && + mBaseTex->getWidth() == destSize.x && + mBaseTex->getHeight() == destSize.y ) + blendTex = mBaseTex; + else + blendTex.set( destSize.x, destSize.y, GFXFormatR8G8B8A8, &GFXDefaultRenderTargetProfile, "" ); + + GFX->pushActiveRenderTarget(); + + // Set our shader stuff + GFX->setShader( mBaseShader ); + GFX->setShaderConstBuffer( mBaseShaderConsts ); + GFX->setStateBlock( mBaseShaderSB ); + GFX->setVertexBuffer( vb ); + + mBaseTarget->attachTexture( GFXTextureTarget::Color0, blendTex ); + GFX->setActiveRenderTarget( mBaseTarget ); + + GFX->setTexture( 0, mLayerTex ); + mBaseShaderConsts->setSafe( mBaseLayerSizeConst, (F32)mLayerTex->getWidth() ); + + for ( U32 i=0; i < mBaseTextures.size(); i++ ) + { + GFXTextureObject *tex = mBaseTextures[i]; + if ( !tex ) + continue; + + GFX->setTexture( 1, tex ); + + F32 baseSize = mFile->mMaterials[i]->getDiffuseSize(); + F32 scale = 1.0f; + if ( !mIsZero( baseSize ) ) + scale = getWorldBlockSize() / baseSize; + + // A mistake early in development means that texture + // coords are not flipped correctly. To compensate + // we flip the y scale here. + mBaseShaderConsts->setSafe( mBaseTexScaleConst, Point2F( scale, -scale ) ); + mBaseShaderConsts->setSafe( mBaseTexIdConst, (F32)i ); + + GFX->drawPrimitive( GFXTriangleFan, 0, 2 ); + } + + mBaseTarget->resolve(); + + GFX->setShader( NULL ); + //GFX->setStateBlock( NULL ); // WHY NOT? + GFX->setShaderConstBuffer( NULL ); + GFX->setVertexBuffer( NULL ); + + GFX->popActiveRenderTarget(); + + // End it if we begun it... Yeehaw! + if ( !sceneBegun ) + GFX->endScene(); + + /// Do we cache this sucker? + if ( writeToCache ) + { + String cachePath = _getBaseTexCacheFileName(); + + FileStream fs; + if ( fs.open( _getBaseTexCacheFileName(), Torque::FS::File::Write ) ) + { + // Read back the render target, dxt compress it, and write it to disk. + GBitmap blendBmp( destSize.x, destSize.y, false, GFXFormatR8G8B8A8 ); + blendTex.copyToBmp( &blendBmp ); + + /* + // Test code for dumping uncompressed bitmap to disk. + { + FileStream fs; + if ( fs.open( "./basetex.png", Torque::FS::File::Write ) ) + { + blendBmp.writeBitmap( "png", fs ); + fs.close(); + } + } + */ + + blendBmp.extrudeMipLevels(); + + DDSFile *blendDDS = DDSFile::createDDSFileFromGBitmap( &blendBmp ); + DDSUtil::squishDDS( blendDDS, GFXFormatDXT1 ); + + // Write result to file stream + blendDDS->write( fs ); + + delete blendDDS; + } + fs.close(); + } + else + { + // We didn't cache the result, so set the base texture + // to the render target we updated. This should be good + // for realtime painting cases. + mBaseTex = blendTex; + } +} + +void TerrainBlock::_renderBlock( SceneRenderState *state ) +{ + PROFILE_SCOPE( TerrainBlock_RenderBlock ); + + // Prevent rendering shadows if feature is disabled + if ( !mCastShadows && state->isShadowPass() ) + return; + + MatrixF worldViewXfm = state->getWorldViewMatrix(); + worldViewXfm.mul( getRenderTransform() ); + + MatrixF worldViewProjXfm = state->getProjectionMatrix(); + worldViewProjXfm.mul( worldViewXfm ); + + const MatrixF &objectXfm = getRenderWorldTransform(); + + Point3F objCamPos = state->getDiffuseCameraPosition(); + objectXfm.mulP( objCamPos ); + + // Get the shadow material. + if ( !mDefaultMatInst ) + mDefaultMatInst = TerrainCellMaterial::getShadowMat(); + + // Make sure we have a base material. + if ( !mBaseMaterial ) + { + mBaseMaterial = new TerrainCellMaterial(); + mBaseMaterial->init( this, 0, false, false, true ); + } + + // Did the detail layers change? + if ( mDetailsDirty ) + { + _updateMaterials(); + mDetailsDirty = false; + } + + // If the layer texture has been cleared or is + // dirty then update it. + if ( mLayerTex.isNull() || mLayerTexDirty ) + _updateLayerTexture(); + + // If the layer texture is dirty or we lost the base + // texture then regenerate it. + if ( mLayerTexDirty || mBaseTex.isNull() ) + { + _updateBaseTexture( false ); + mLayerTexDirty = false; + } + + static Vector renderCells; + renderCells.clear(); + + mCell->cullCells( state, + objCamPos, + &renderCells ); + + RenderPassManager *renderPass = state->getRenderPass(); + + MatrixF *riObjectToWorldXfm = renderPass->allocUniqueXform( getRenderTransform() ); + + const bool isColorDrawPass = state->isDiffusePass() || state->isReflectPass(); + + // This is here for shadows mostly... it allows the + // proper shadow material to be generated. + BaseMatInstance *defaultMatInst = state->getOverrideMaterial( mDefaultMatInst ); + + // Only pass and use the light manager if this is not a shadow pass. + LightManager *lm = NULL; + if ( isColorDrawPass ) + lm = LIGHTMGR; + + for ( U32 i=0; i < renderCells.size(); i++ ) + { + TerrCell *cell = renderCells[i]; + + // Ok this cell is fit to render. + TerrainRenderInst *inst = renderPass->allocInst(); + + // Setup lights for this cell. + if ( lm ) + { + SphereF bounds = cell->getSphereBounds(); + getRenderTransform().mulP( bounds.center ); + + LightQuery query; + query.init( bounds ); + query.getLights( inst->lights, 8 ); + } + + GFXVertexBufferHandleBase vertBuff; + GFXPrimitiveBufferHandle primBuff; + + cell->getRenderPrimitive( &inst->prim, &vertBuff, &primBuff ); + + inst->mat = defaultMatInst; + inst->vertBuff = vertBuff.getPointer(); + + if ( primBuff.isValid() ) + { + // Use the cell's custom primitive buffer + inst->primBuff = primBuff.getPointer(); + } + else + { + // Use the standard primitive buffer for this cell + inst->primBuff = mPrimBuffer.getPointer(); + } + + inst->objectToWorldXfm = riObjectToWorldXfm; + + // If we're not drawing to the shadow map then we need + // to include the normal rendering materials. + if ( isColorDrawPass ) + { + const SphereF &bounds = cell->getSphereBounds(); + + F32 sqDist = ( bounds.center - objCamPos ).lenSquared(); + + F32 radiusSq = mSquared( ( mMaxDetailDistance + bounds.radius ) * smDetailScale ); + + // If this cell is near enough to get detail textures then + // use the full detail mapping material. Else we use the + // simple base only material. + if ( !state->isReflectPass() && sqDist < radiusSq ) + inst->cellMat = cell->getMaterial(); + else if ( state->isReflectPass() ) + inst->cellMat = mBaseMaterial->getReflectMat(); + else + inst->cellMat = mBaseMaterial; + } + + inst->defaultKey = (U32)cell->getMaterials(); + + // Submit it for rendering. + renderPass->addInst( inst ); + } + + // Trigger the debug rendering. + if ( state->isDiffusePass() && + !renderCells.empty() && + smDebugRender ) + { + // Store the render cells for later. + mDebugCells = renderCells; + + ObjectRenderInst *ri = state->getRenderPass()->allocInst(); + ri->renderDelegate.bind( this, &TerrainBlock::_renderDebug ); + ri->type = RenderPassManager::RIT_Editor; + state->getRenderPass()->addInst( ri ); + } +} + +void TerrainBlock::_renderDebug( ObjectRenderInst *ri, + SceneRenderState *state, + BaseMatInstance *overrideMat ) +{ + GFXTransformSaver saver; + GFX->multWorld( getRenderTransform() ); + + for ( U32 i=0; i < mDebugCells.size(); i++ ) + mDebugCells[i]->renderBounds(); + + mDebugCells.clear(); +} \ No newline at end of file diff --git a/Engine/source/terrain/terrRender.h b/Engine/source/terrain/terrRender.h new file mode 100644 index 000000000..58fa3a00c --- /dev/null +++ b/Engine/source/terrain/terrRender.h @@ -0,0 +1,59 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TERRRENDER_H_ +#define _TERRRENDER_H_ + +#ifndef _TERRDATA_H_ +#include "terrain/terrData.h" +#endif + +enum TerrConstants +{ + MaxClipPlanes = 8, ///< left, right, top, bottom - don't need far tho... + //MaxTerrainMaterials = 256, + + MaxTerrainLights = 64, + MaxVisibleLights = 31, + ClipPlaneMask = (1 << MaxClipPlanes) - 1, + FarSphereMask = 0x80000000, + FogPlaneBoxMask = 0x40000000, +}; + +class SceneRenderState; + + +// Allows a lighting system to plug into terrain rendering +class TerrainLightingPlugin +{ +public: + virtual ~TerrainLightingPlugin() {} + + virtual void setupLightStage(LightManager * lm, LightInfo* light, SceneData& sgData, BaseMatInstance* basemat, BaseMatInstance** dmat) = 0; + virtual void cleanupLights(LightManager * lm) {} +}; + + +/// A special texture profile used for the terrain layer id map. +GFX_DeclareTextureProfile( TerrainLayerTexProfile ); + +#endif // _TERRRENDER_H_ diff --git a/Engine/source/ts/arch/tsMeshIntrinsics.arch.h b/Engine/source/ts/arch/tsMeshIntrinsics.arch.h new file mode 100644 index 000000000..ee3373c61 --- /dev/null +++ b/Engine/source/ts/arch/tsMeshIntrinsics.arch.h @@ -0,0 +1,49 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TSMESHINTRINSICS_ARCH_H_ +#define _TSMESHINTRINSICS_ARCH_H_ + +#if defined(TORQUE_CPU_X86) +# // x86 CPU family implementations +extern void zero_vert_normal_bulk_SSE(const dsize_t count, U8 * __restrict const outPtr, const dsize_t outStride); +extern void m_matF_x_BatchedVertWeightList_SSE(const MatrixF &mat, const dsize_t count, const TSSkinMesh::BatchData::BatchedVertWeight * __restrict batch, U8 * const __restrict outPtr, const dsize_t outStride); +#if (_MSC_VER >= 1500) +extern void m_matF_x_BatchedVertWeightList_SSE4(const MatrixF &mat, const dsize_t count, const TSSkinMesh::BatchData::BatchedVertWeight * __restrict batch, U8 * const __restrict outPtr, const dsize_t outStride); +#endif +# +#elif defined(TORQUE_CPU_PPC) +# // PPC CPU family implementations +# if defined(TORQUE_OS_XENON) +extern void zero_vert_normal_bulk_X360(const dsize_t count, U8 * __restrict const outPtr, const dsize_t outStride); +extern void m_matF_x_BatchedVertWeightList_X360(const MatrixF &mat, const dsize_t count, const TSSkinMesh::BatchData::BatchedVertWeight * __restrict batch, U8 * const __restrict outPtr, const dsize_t outStride); +# else +extern void zero_vert_normal_bulk_gccvec(const dsize_t count, U8 * __restrict const outPtr, const dsize_t outStride); +extern void m_matF_x_BatchedVertWeightList_gccvec(const MatrixF &mat, const dsize_t count, const TSSkinMesh::BatchData::BatchedVertWeight * __restrict batch, U8 * const __restrict outPtr, const dsize_t outStride); +# endif +# +#else +# // Other CPU types go here... +#endif + +#endif // _TSMESHINTRINSICS_ARCH_H_ + diff --git a/Engine/source/ts/arch/tsMeshIntrinsics.sse.cpp b/Engine/source/ts/arch/tsMeshIntrinsics.sse.cpp new file mode 100644 index 000000000..c83add0a1 --- /dev/null +++ b/Engine/source/ts/arch/tsMeshIntrinsics.sse.cpp @@ -0,0 +1,183 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#include "ts/tsMesh.h" + +#if defined(TORQUE_CPU_X86) +#include "ts/tsMeshIntrinsics.h" +#include + +void zero_vert_normal_bulk_SSE(const dsize_t count, U8 * __restrict const outPtr, const dsize_t outStride) +{ + // A U8 * version of the in/out pointer + register char *outData = reinterpret_cast(outPtr); + + register __m128 vPos; + register __m128 vNrm; + register __m128 vMask; + + const __m128 _point3f_zero_mask = { 0.0f, 0.0f, 0.0f, 1.0f }; + vMask = _mm_load_ps((const F32*)&_point3f_zero_mask); + + // pre-populate cache + for(int i = 0; i < 8; i++) + _mm_prefetch(reinterpret_cast(outData + outStride * i), _MM_HINT_T0); + + for(int i = 0; i < count; i++) + { + TSMesh::__TSMeshVertexBase *curElem = reinterpret_cast(outData); + + // prefetch 8 items ahead (should really detect cache size or something) + _mm_prefetch(reinterpret_cast(outData + outStride * 8), _MM_HINT_T0); + + // load + vPos = _mm_load_ps(curElem->_vert); + vNrm = _mm_load_ps(curElem->_normal); + + // mask + vPos = _mm_mul_ps(vPos, _point3f_zero_mask); + vNrm = _mm_mul_ps(vNrm, _point3f_zero_mask); + + // store + _mm_store_ps(curElem->_vert, vPos); + _mm_store_ps(curElem->_normal, vNrm); + + // update output pointer + outData += outStride; + } +} + +//------------------------------------------------------------------------------ + +void m_matF_x_BatchedVertWeightList_SSE(const MatrixF &mat, + const dsize_t count, + const TSSkinMesh::BatchData::BatchedVertWeight * __restrict batch, + U8 * const __restrict outPtr, + const dsize_t outStride) +{ + const char * __restrict iPtr = reinterpret_cast(batch); + const dsize_t inStride = sizeof(TSSkinMesh::BatchData::BatchedVertWeight); + + // SSE intrinsic version + // Based on: http://www.cortstratton.org/articles/HugiCode.html + + // Load matrix, transposed, into registers + MatrixF transMat; + mat.transposeTo(transMat); + register __m128 sseMat[4]; + + sseMat[0] = _mm_loadu_ps(&transMat[0]); + sseMat[1] = _mm_loadu_ps(&transMat[4]); + sseMat[2] = _mm_loadu_ps(&transMat[8]); + sseMat[3] = _mm_loadu_ps(&transMat[12]); + + // mask + const __m128 _w_mask = { 1.0f, 1.0f, 1.0f, 0.0f }; + + // temp registers + register __m128 tempPos; + register __m128 tempNrm; + register __m128 scratch0; + register __m128 scratch1; + register __m128 inPos; + register __m128 inNrm; + + // pre-populate cache + const TSSkinMesh::BatchData::BatchedVertWeight &firstElem = batch[0]; + for(int i = 0; i < 8; i++) + { + _mm_prefetch(reinterpret_cast(iPtr + inStride * i), _MM_HINT_T0); + _mm_prefetch(reinterpret_cast(outPtr + outStride * (i + firstElem.vidx)), _MM_HINT_T0); + } + + for(register int i = 0; i < count; i++) + { + const TSSkinMesh::BatchData::BatchedVertWeight &inElem = batch[i]; + TSMesh::__TSMeshVertexBase *outElem = reinterpret_cast(outPtr + inElem.vidx * outStride); + + // process x (hiding the prefetches in the delays) + inPos = _mm_load_ps(inElem.vert); + inNrm = _mm_load_ps(inElem.normal); + + // prefetch input +#define INPUT_PREFETCH_LOOKAHEAD 64 + const char *prefetchInput = reinterpret_cast(batch) + inStride * (i + INPUT_PREFETCH_LOOKAHEAD); + _mm_prefetch(prefetchInput, _MM_HINT_T0); + + // propagate the .x elements across the vectors + tempPos = _mm_shuffle_ps(inPos, inPos, _MM_SHUFFLE(0, 0, 0, 0)); + tempNrm = _mm_shuffle_ps(inNrm, inNrm, _MM_SHUFFLE(0, 0, 0, 0)); + + // prefetch ouput with half the lookahead distance of the input +#define OUTPUT_PREFETCH_LOOKAHEAD (INPUT_PREFETCH_LOOKAHEAD >> 1) + const char *outPrefetch = reinterpret_cast(outPtr) + outStride * (inElem.vidx + OUTPUT_PREFETCH_LOOKAHEAD); + _mm_prefetch(outPrefetch, _MM_HINT_T0); + + // mul by column 0 + tempPos = _mm_mul_ps(tempPos, sseMat[0]); + tempNrm = _mm_mul_ps(tempNrm, sseMat[0]); + + // process y + scratch0 = _mm_shuffle_ps(inPos, inPos, _MM_SHUFFLE(1, 1, 1, 1)); + scratch1 = _mm_shuffle_ps(inNrm, inNrm, _MM_SHUFFLE(1, 1, 1, 1)); + + scratch0 = _mm_mul_ps(scratch0, sseMat[1]); + scratch1 = _mm_mul_ps(scratch1, sseMat[1]); + + tempPos = _mm_add_ps(tempPos, scratch0); + tempNrm = _mm_add_ps(tempNrm, scratch1); + + + // process z + scratch0 = _mm_shuffle_ps(inPos, inPos, _MM_SHUFFLE(2, 2, 2, 2)); + scratch1 = _mm_shuffle_ps(inNrm, inNrm, _MM_SHUFFLE(2, 2, 2, 2)); + + scratch0 = _mm_mul_ps(scratch0, sseMat[2]); + scratch1 = _mm_mul_ps(scratch1, sseMat[2]); + + tempPos = _mm_add_ps(tempPos, scratch0); + + inNrm = _mm_load_ps(outElem->_normal); //< load normal for accumulation + scratch0 = _mm_shuffle_ps(inPos, inPos, _MM_SHUFFLE(3, 3, 3, 3));//< load bone weight across all elements of scratch0 + + tempNrm = _mm_add_ps(tempNrm, scratch1); + + scratch0 = _mm_mul_ps(scratch0, _w_mask); //< mask off last + + // Translate the position by adding the 4th column of the matrix to it + tempPos = _mm_add_ps(tempPos, sseMat[3]); + + // now multiply by the blend weight, and mask out the W component of both vectors + tempPos = _mm_mul_ps(tempPos, scratch0); + tempNrm = _mm_mul_ps(tempNrm, scratch0); + + inPos = _mm_load_ps(outElem->_vert); //< load position for accumulation + + // accumulate with previous values + tempNrm = _mm_add_ps(tempNrm, inNrm); + tempPos = _mm_add_ps(tempPos, inPos); + + _mm_store_ps(outElem->_vert, tempPos); //< output position + _mm_store_ps(outElem->_normal, tempNrm); //< output normal + } +} + +#endif // TORQUE_CPU_X86 \ No newline at end of file diff --git a/Engine/source/ts/arch/tsMeshIntrinsics.sse4.cpp b/Engine/source/ts/arch/tsMeshIntrinsics.sse4.cpp new file mode 100644 index 000000000..8dba501c5 --- /dev/null +++ b/Engine/source/ts/arch/tsMeshIntrinsics.sse4.cpp @@ -0,0 +1,108 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#include "ts/tsMesh.h" + +#if defined(TORQUE_CPU_X86) && (_MSC_VER >= 1500) +#include "ts/tsMeshIntrinsics.h" +#include + +void m_matF_x_BatchedVertWeightList_SSE4(const MatrixF &mat, + const dsize_t count, + const TSSkinMesh::BatchData::BatchedVertWeight * __restrict batch, + U8 * const __restrict outPtr, + const dsize_t outStride) +{ + const char * __restrict iPtr = reinterpret_cast(batch); + const dsize_t inStride = sizeof(TSSkinMesh::BatchData::BatchedVertWeight); + + __m128 sseMat[3]; + sseMat[0] = _mm_loadu_ps(&mat[0]); + sseMat[1] = _mm_loadu_ps(&mat[4]); + sseMat[2] = _mm_loadu_ps(&mat[8]); + + // temp registers + __m128 inPos, tempPos; + __m128 inNrm, tempNrm; + __m128 temp0, temp1, temp2, temp3; + + // pre-populate cache + const TSSkinMesh::BatchData::BatchedVertWeight &firstElem = batch[0]; + for(int i = 0; i < 8; i++) + { + _mm_prefetch(reinterpret_cast(iPtr + inStride * i), _MM_HINT_T0); + _mm_prefetch(reinterpret_cast(outPtr + outStride * (i + firstElem.vidx)), _MM_HINT_T0); + } + + for(int i = 0; i < count; i++) + { + const TSSkinMesh::BatchData::BatchedVertWeight &inElem = batch[i]; + TSMesh::__TSMeshVertexBase *outElem = reinterpret_cast(outPtr + inElem.vidx * outStride); + + // process x (hiding the prefetches in the delays) + inPos = _mm_load_ps(inElem.vert); + inNrm = _mm_load_ps(inElem.normal); + + // prefetch input +#define INPUT_PREFETCH_LOOKAHEAD 64 + const char *prefetchInput = reinterpret_cast(batch) + inStride * (i + INPUT_PREFETCH_LOOKAHEAD); + _mm_prefetch(prefetchInput, _MM_HINT_T0); + + // prefetch ouput with half the lookahead distance of the input +#define OUTPUT_PREFETCH_LOOKAHEAD (INPUT_PREFETCH_LOOKAHEAD >> 1) + const char *outPrefetch = reinterpret_cast(outPtr) + outStride * (inElem.vidx + OUTPUT_PREFETCH_LOOKAHEAD); + _mm_prefetch(outPrefetch, _MM_HINT_T0); + + // Multiply position + tempPos = _mm_dp_ps(inPos, sseMat[0], 0xF1); + temp0 = _mm_dp_ps(inPos, sseMat[1], 0xF2); + temp1 = _mm_dp_ps(inPos, sseMat[2], 0xF4); + + temp0 = _mm_or_ps(temp0, temp1); + tempPos = _mm_or_ps(tempPos, temp0); + + // Multiply normal + tempNrm = _mm_dp_ps(inNrm, sseMat[0], 0x71); + temp2 = _mm_dp_ps(inNrm, sseMat[1], 0x72); + temp3 = _mm_dp_ps(inNrm, sseMat[2], 0x74); + + temp2 = _mm_or_ps(temp2, temp3); + tempNrm = _mm_or_ps(tempNrm, temp2); + + // Load bone weight and multiply + temp3 = _mm_shuffle_ps(inPos, inPos, _MM_SHUFFLE(3, 3, 3, 3)); + + tempPos = _mm_mul_ps(tempPos, temp3); + tempNrm = _mm_mul_ps(tempNrm, temp3); + + inPos = _mm_load_ps(outElem->_vert); //< load position for accumulation + inNrm = _mm_load_ps(outElem->_normal); //< load normal for accumulation + + // accumulate with previous values + tempNrm = _mm_add_ps(tempNrm, inNrm); + tempPos = _mm_add_ps(tempPos, inPos); + + _mm_store_ps(outElem->_vert, tempPos); //< output position + _mm_store_ps(outElem->_normal, tempNrm); //< output normal + } +} + +#endif // TORQUE_CPU_X86 \ No newline at end of file diff --git a/Engine/source/ts/collada/colladaAppMaterial.cpp b/Engine/source/ts/collada/colladaAppMaterial.cpp new file mode 100644 index 000000000..a29964ad0 --- /dev/null +++ b/Engine/source/ts/collada/colladaAppMaterial.cpp @@ -0,0 +1,225 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "ts/loader/tsShapeLoader.h" +#include "ts/collada/colladaAppMaterial.h" +#include "ts/collada/colladaUtils.h" +#include "ts/tsMaterialList.h" +#include "materials/materialManager.h" + +using namespace ColladaUtils; + +String cleanString(const String& str) +{ + String cleanStr(str); + + // Replace invalid characters with underscores + const String badChars(" -,.+=*/"); + for (String::SizeType i = 0; i < badChars.length(); i++) + cleanStr.replace(badChars[i], '_'); + + // Prefix with an underscore if string starts with a number + if ((cleanStr[0] >= '0') && (cleanStr[0] <= '9')) + cleanStr.insert(0, '_'); + + return cleanStr; +} + +//------------------------------------------------------------------------------ + +ColladaAppMaterial::ColladaAppMaterial(const char* matName) +: mat(0), + effect(0), + effectExt(0) +{ + name = matName; + + // Set some defaults + flags |= TSMaterialList::S_Wrap; + flags |= TSMaterialList::T_Wrap; + + diffuseColor = ColorF::ONE; + specularColor = ColorF::ONE; + specularPower = 8.0f; + doubleSided = false; +} + +ColladaAppMaterial::ColladaAppMaterial(const domMaterial *pMat) +: mat(pMat), + diffuseColor(ColorF::ONE), + specularColor(ColorF::ONE), + specularPower(8.0f), + doubleSided(false) +{ + // Get the effect element for this material + effect = daeSafeCast(mat->getInstance_effect()->getUrl().getElement()); + effectExt = new ColladaExtension_effect(effect); + + // Get the , and elements + const domProfile_COMMON* commonProfile = ColladaUtils::findEffectCommonProfile(effect); + const domCommon_color_or_texture_type_complexType* domDiffuse = findEffectDiffuse(effect); + const domCommon_color_or_texture_type_complexType* domSpecular = findEffectSpecular(effect); + + // Wrap flags + if (effectExt->wrapU) + flags |= TSMaterialList::S_Wrap; + if (effectExt->wrapV) + flags |= TSMaterialList::T_Wrap; + + // Set material attributes + if (commonProfile) { + + F32 transparency = 0.0f; + if (commonProfile->getTechnique()->getConstant()) { + const domProfile_COMMON::domTechnique::domConstant* constant = commonProfile->getTechnique()->getConstant(); + diffuseColor.set(1.0f, 1.0f, 1.0f, 1.0f); + resolveColor(constant->getReflective(), &specularColor); + resolveFloat(constant->getReflectivity(), &specularPower); + resolveTransparency(constant, &transparency); + } + else if (commonProfile->getTechnique()->getLambert()) { + const domProfile_COMMON::domTechnique::domLambert* lambert = commonProfile->getTechnique()->getLambert(); + resolveColor(lambert->getDiffuse(), &diffuseColor); + resolveColor(lambert->getReflective(), &specularColor); + resolveFloat(lambert->getReflectivity(), &specularPower); + resolveTransparency(lambert, &transparency); + } + else if (commonProfile->getTechnique()->getPhong()) { + const domProfile_COMMON::domTechnique::domPhong* phong = commonProfile->getTechnique()->getPhong(); + resolveColor(phong->getDiffuse(), &diffuseColor); + resolveColor(phong->getSpecular(), &specularColor); + resolveFloat(phong->getShininess(), &specularPower); + resolveTransparency(phong, &transparency); + } + else if (commonProfile->getTechnique()->getBlinn()) { + const domProfile_COMMON::domTechnique::domBlinn* blinn = commonProfile->getTechnique()->getBlinn(); + resolveColor(blinn->getDiffuse(), &diffuseColor); + resolveColor(blinn->getSpecular(), &specularColor); + resolveFloat(blinn->getShininess(), &specularPower); + resolveTransparency(blinn, &transparency); + } + + // Normalize specularPower (1-128). Values > 1 are assumed to be + // already normalized. + if (specularPower <= 1.0f) + specularPower *= 128; + specularPower = mClampF(specularPower, 1.0f, 128.0f); + + // Set translucency + if (transparency != 0.0f) { + flags |= TSMaterialList::Translucent; + if (transparency > 1.0f) { + flags |= TSMaterialList::Additive; + diffuseColor.alpha = transparency - 1.0f; + } + else if (transparency < 0.0f) { + flags |= TSMaterialList::Subtractive; + diffuseColor.alpha = -transparency; + } + else { + diffuseColor.alpha = transparency; + } + } + else + diffuseColor.alpha = 1.0f; + } + + // Double-sided flag + doubleSided = effectExt->double_sided; + + // Get the paths for the various textures => Collada indirection at its finest! + // ........ + diffuseMap = getSamplerImagePath(effect, getTextureSampler(effect, domDiffuse)); + specularMap = getSamplerImagePath(effect, getTextureSampler(effect, domSpecular)); + normalMap = getSamplerImagePath(effect, effectExt->bumpSampler); + + // Set the material name + name = ColladaUtils::getOptions().matNamePrefix; + if ( ColladaUtils::getOptions().useDiffuseNames ) + { + Torque::Path diffusePath( diffuseMap ); + name += diffusePath.getFileName(); + } + else + { + name += _GetNameOrId(mat); + } +} + +void ColladaAppMaterial::resolveFloat(const domCommon_float_or_param_type* value, F32* dst) +{ + if (value && value->getFloat()) { + *dst = value->getFloat()->getValue(); + } +} + +void ColladaAppMaterial::resolveColor(const domCommon_color_or_texture_type* value, ColorF* dst) +{ + if (value && value->getColor()) { + dst->red = value->getColor()->getValue()[0]; + dst->green = value->getColor()->getValue()[1]; + dst->blue = value->getColor()->getValue()[2]; + dst->alpha = value->getColor()->getValue()[3]; + } +} + +// Generate a new Material object +Material *ColladaAppMaterial::createMaterial(const Torque::Path& path) const +{ + // The filename and material name are used as TorqueScript identifiers, so + // clean them up first + String cleanFile = cleanString(TSShapeLoader::getShapePath().getFileName()); + String cleanName = cleanString(getName()); + + // Prefix the material name with the filename (if not done already by TSShapeConstructor prefix) + if (!cleanName.startsWith(cleanFile)) + cleanName = cleanFile + "_" + cleanName; + + // Determine the blend operation for this material + Material::BlendOp blendOp = (flags & TSMaterialList::Translucent) ? Material::LerpAlpha : Material::None; + if (flags & TSMaterialList::Additive) + blendOp = Material::Add; + else if (flags & TSMaterialList::Subtractive) + blendOp = Material::Sub; + + // Create the Material definition + const String oldScriptFile = Con::getVariable("$Con::File"); + Con::setVariable("$Con::File", path.getFullPath()); // modify current script path so texture lookups are correct + Material *newMat = MATMGR->allocateAndRegister( cleanName, getName() ); + Con::setVariable("$Con::File", oldScriptFile); // restore script path + + newMat->mDiffuseMapFilename[0] = diffuseMap; + newMat->mNormalMapFilename[0] = normalMap; + newMat->mSpecularMapFilename[0] = specularMap; + + newMat->mDiffuse[0] = diffuseColor; + newMat->mSpecular[0] = specularColor; + newMat->mSpecularPower[0] = specularPower; + + newMat->mDoubleSided = doubleSided; + newMat->mTranslucent = (bool)(flags & TSMaterialList::Translucent); + newMat->mTranslucentBlendOp = blendOp; + + return newMat; +} diff --git a/Engine/source/ts/collada/colladaAppMaterial.h b/Engine/source/ts/collada/colladaAppMaterial.h new file mode 100644 index 000000000..1be1e5b3e --- /dev/null +++ b/Engine/source/ts/collada/colladaAppMaterial.h @@ -0,0 +1,89 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _COLLADA_APP_MATERIAL_H_ +#define _COLLADA_APP_MATERIAL_H_ + +#ifndef _APPMATERIAL_H_ +#include "ts/loader/appMaterial.h" +#endif +#ifndef _COLLADA_EXTENSIONS_H_ +#include "ts/collada/colladaExtensions.h" +#endif + +class Material; + +class ColladaAppMaterial : public AppMaterial +{ +public: + const domMaterial* mat; ///< Collada element + domEffect* effect; ///< Collada element + ColladaExtension_effect* effectExt; ///< effect extension + String name; ///< Name of this material (cleaned) + + // Settings extracted from the Collada file, and optionally saved to materials.cs + String diffuseMap; + String normalMap; + String specularMap; + ColorF diffuseColor; + ColorF specularColor; + F32 specularPower; + bool doubleSided; + + ColladaAppMaterial(const char* matName); + ColladaAppMaterial(const domMaterial* pMat); + ~ColladaAppMaterial() { delete effectExt; } + + String getName() const { return name; } + + void resolveFloat(const domCommon_float_or_param_type* value, F32* dst); + void resolveColor(const domCommon_color_or_texture_type* value, ColorF* dst); + + // Determine the material transparency + template void resolveTransparency(const T shader, F32* dst) + { + // Start out by getting the value + *dst = 1.0f; + resolveFloat(shader->getTransparency(), dst); + + // Multiply the transparency by the transparent color + ColorF transColor(1.0f, 1.0f, 1.0f, 1.0f); + if (shader->getTransparent() && shader->getTransparent()->getColor()) { + const domCommon_color_or_texture_type::domColor* color = shader->getTransparent()->getColor(); + transColor.set(color->getValue()[0], color->getValue()[1], color->getValue()[2], color->getValue()[3]); + } + + if (!shader->getTransparent() || (shader->getTransparent()->getOpaque() == FX_OPAQUE_ENUM_A_ONE)) { + // multiply by alpha value and invert (so 1.0 is fully opaque) + *dst = 1.0f - (*dst * transColor.alpha); + } + else { + // multiply by average of the RGB values + F32 avg = (transColor.red + transColor.blue + transColor.green) / 3; + *dst *= avg; + } + } + + Material *createMaterial(const Torque::Path& path) const; +}; + +#endif // _COLLADA_APP_MATERIAL_H_ diff --git a/Engine/source/ts/collada/colladaAppMesh.cpp b/Engine/source/ts/collada/colladaAppMesh.cpp new file mode 100644 index 000000000..f9c178c61 --- /dev/null +++ b/Engine/source/ts/collada/colladaAppMesh.cpp @@ -0,0 +1,1099 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +// Make GCC happy. Needs to have seen this before processing the +// hash table template. +struct VertTuple; +namespace DictHash +{ + inline U32 hash( const VertTuple& data ); +} + +#include "ts/collada/colladaExtensions.h" +#include "ts/collada/colladaAppMesh.h" +#include "ts/collada/colladaAppNode.h" +#include "ts/collada/colladaAppMaterial.h" + +#include "core/util/tDictionary.h" +#include "core/stringTable.h" + +using namespace ColladaUtils; + +bool ColladaAppMesh::fixedSizeEnabled = false; +S32 ColladaAppMesh::fixedSize = 2; + +//----------------------------------------------------------------------------- +// Define a VertTuple dictionary to allow fast tuple lookups +namespace DictHash +{ + inline U32 hash(const VertTuple& data) + { + return (U32)data.vertex; + } +} + +typedef Map VertTupleMap; + +//----------------------------------------------------------------------------- +// Find a source with matching ID. Cannot use the DOM .getElement method since +// some lame Collada exporters generate s with non-unique IDs. +daeElement* findInputSource(const daeElement* input) +{ + // Try using the DOM .getElement method => the resolved element's parent + // should be the input's grandparent + daeElement* parent = ((daeElement*)input)->getParentElement(); + daeElement* grandparent = parent ? parent->getParentElement() : 0; + if (!grandparent) + return NULL; + + const domURIFragmentType* uri = 0; + if (input->getElementType() == COLLADA_TYPE::INPUTLOCAL) + uri = &daeSafeCast((daeElement*)input)->getSource(); + else if (input->getElementType() == COLLADA_TYPE::INPUTLOCALOFFSET) + uri = &daeSafeCast((daeElement*)input)->getSource(); + if (!uri) + return NULL; + + daeElement* element = uri->getElement(); + if (element && element->getParentElement() == grandparent) + return element; + else + { + // Probably a non-unique ID => search for the matching element manually + + // Skip the leading '#' on source IDs + const char* id = uri->originalStr().c_str(); + if (id && (id[0] == '#')) + id++; + + for (S32 iChild = 0; iChild < grandparent->getChildren().getCount(); iChild++) + { + element = grandparent->getChildren()[iChild]; + if ((element->getElementType() != COLLADA_TYPE::SOURCE) && + (element->getElementType() != COLLADA_TYPE::VERTICES)) + continue; + + if (dStrEqual(id, element->getAttribute("id").c_str())) + return element; + } + } + return NULL; +} + +//----------------------------------------------------------------------------- +// Collada scatters the data required for geometry all over the place; this class +// helps to group it all together. +class MeshStreams +{ +public: + // The sources we want to read from the mesh stream. Can be any order, but + // sources of the same type (eg. UVs and UV2s) must be sequential (to allow + // ordering by set index) + enum eSourceType { + Points, + Normals, + Colors, + UVs, + UV2s, + Joints, + Weights, + InvBindMatrices, + NumStreams + }; + + const char* SourceTypeToSemantic(eSourceType type) + { + switch ( type ) + { + case Points: return "POSITION"; + case Normals: return "NORMAL"; + case Colors: return "COLOR"; + case UVs: + case UV2s: return "TEXCOORD"; + case Joints: return "JOINT"; + case Weights: return "WEIGHT"; + case InvBindMatrices: return "INV_BIND_MATRIX"; + default: return ""; + } + } + +private: + /// Classify a single input + template + static void selectInput(T input, T sortedInputs[], S32 start, S32 end=-1) + { + if (end == -1) + end = start; + + // Get the set for this input + const domInputLocalOffset* localOffset = daeSafeCast(input); + domUint newSet = localOffset ? localOffset->getSet() : 0; + + // Add the input to the right place in the list (somewhere between start and end) + for (S32 i = start; i <= end; i++) { + const domInputLocalOffset* localOffset = daeSafeCast(sortedInputs[i]); + domUint set = localOffset ? localOffset->getSet() : 0xFFFFFFFF; + if (newSet < set) { + for (S32 j = i + 1; j <= end; j++) + sortedInputs[j] = sortedInputs[j-1]; + sortedInputs[i] = input; + return; + } + } + } + + /// Attempt to initialise a _SourceReader + template + bool initSourceReader(T input, eSourceType type, _SourceReader& reader, const char* params[]) + { + if (!input) + return false; + + // Try to get the source element + const domSource* source = 0; + daeElement *element = findInputSource(input); + if (element->getElementType() == COLLADA_TYPE::SOURCE) + source = daeSafeCast(element); + else if (element->getElementType() == COLLADA_TYPE::VERTICES) { + const domVertices* vertices = daeSafeCast(element); + // Search for the input with the desired semantic + const char* semantic = SourceTypeToSemantic( type ); + for (int iInput = 0; iInput < vertices->getInput_array().getCount(); iInput++) + { + domInputLocal* input = vertices->getInput_array().get(iInput); + if (dStrEqual(input->getSemantic(), semantic)) + { + source = daeSafeCast(findInputSource(input)); + break; + } + } + } + if (!source) + return false; + + return reader.initFromSource(source, params); + } + +public: + + _SourceReader points; + _SourceReader normals; + _SourceReader colors; + _SourceReader uvs; + _SourceReader uv2s; + + _SourceReader joints; + _SourceReader weights; + _SourceReader invBindMatrices; + + /// Clear the mesh streams + void reset() + { + points.reset(); + normals.reset(); + colors.reset(); + uvs.reset(); + uv2s.reset(); + joints.reset(); + weights.reset(); + invBindMatrices.reset(); + } + + /// Classify a set of inputs by type and set number (needs to be a template + /// because Collada has two forms of input arrays that may be accessed in + /// an identical fashion, but the classes are unrelated. Sigh. + template + static void classifyInputs(const daeTArray& inputs, T sortedInputs[], U32 *maxOffset=0) + { + if (maxOffset) + *maxOffset = 0; + + // Clear output array + for (int i = 0; i < NumStreams; i++) + sortedInputs[i] = 0; + + // Separate inputs by type, and sort by set (ie. lowest TEXCOORD set becomes UV, + // next TEXCOORD set becomes UV2 etc) + for (int iInput = 0; iInput < inputs.getCount(); iInput++) { + + const T& input = inputs[iInput]; + const daeString semantic = input->getSemantic(); + + if (dStrEqual(semantic, "VERTEX")) + { + domVertices* vertices = daeSafeCast(findInputSource(input)); + + // The element may contain multiple inputs (eg. POSITION, NORMAL etc) + domInputLocalRef verticesInputs[NumStreams]; + classifyInputs(vertices->getInput_array(), verticesInputs); + for (int iStream = 0; iStream < NumStreams; iStream++) + { + if (verticesInputs[iStream] != 0) + sortedInputs[iStream] = input; + } + } + else if (dStrEqual(semantic, "POSITION")) selectInput(input, sortedInputs, Points); + else if (dStrEqual(semantic, "NORMAL")) selectInput(input, sortedInputs, Normals); + else if (dStrEqual(semantic, "COLOR")) selectInput(input, sortedInputs, Colors); + else if (dStrEqual(semantic, "TEXCOORD")) selectInput(input, sortedInputs, UVs, UV2s); + else if (dStrEqual(semantic, "JOINT")) selectInput(input, sortedInputs, Joints); + else if (dStrEqual(semantic, "WEIGHT")) selectInput(input, sortedInputs, Weights); + else if (dStrEqual(semantic, "INV_BIND_MATRIX")) selectInput(input, sortedInputs, InvBindMatrices); + + if (maxOffset) + { + const domInputLocalOffset* localOffset = daeSafeCast(input); + domUint offset = localOffset ? localOffset->getOffset() : 0; + if (offset > (*maxOffset)) + *maxOffset = offset; + } + } + } + + /// Read a set of inputs into the named sources. There may be multiple 'sets' + /// of COLOR or TEXCOORD (uvs) streams, but we are only interested in the + /// first COLOR set (ie. smallest set value), and the first 2 TEXCOORDS sets. + template + bool readInputs(const daeTArray& inputs) + { + // Sort inputs by type and set to find the ones we are interested in + T sortedInputs[NumStreams]; + classifyInputs(inputs, sortedInputs); + + // Attempt to initialise the SourceReaders + const char* vertex_params[] = { "X", "Y", "Z", "" }; + initSourceReader(sortedInputs[Points], Points, points, vertex_params); + + const char* normal_params[] = { "X", "Y", "Z", "" }; + initSourceReader(sortedInputs[Normals], Normals, normals, normal_params); + + const char* color_params[] = { "R", "G", "B", "A", "" }; + initSourceReader(sortedInputs[Colors], Colors, colors, color_params); + + const char* uv_params[] = { "S", "T", "" }; + const char* uv_params2[] = { "U", "V", "" }; // some files use the nonstandard U,V or X,Y param names + const char* uv_params3[] = { "X", "Y", "" }; + if (!initSourceReader(sortedInputs[UVs], UVs, uvs, uv_params)) + if (!initSourceReader(sortedInputs[UVs], UVs, uvs, uv_params2)) + initSourceReader(sortedInputs[UVs], UVs, uvs, uv_params3); + if (!initSourceReader(sortedInputs[UV2s], UV2s, uv2s, uv_params)) + if (!initSourceReader(sortedInputs[UV2s], UV2s, uv2s, uv_params2)) + initSourceReader(sortedInputs[UV2s], UV2s, uv2s, uv_params3); + + const char* joint_params[] = { "JOINT", "" }; + initSourceReader(sortedInputs[Joints], Joints, joints, joint_params); + + const char* weight_params[] = { "WEIGHT", "" }; + initSourceReader(sortedInputs[Weights], Weights, weights, weight_params); + + const char* matrix_params[] = { "TRANSFORM", "" }; + initSourceReader(sortedInputs[InvBindMatrices], InvBindMatrices, invBindMatrices, matrix_params); + + return true; + } +}; + +//------------------------------------------------------------------------------ + +ColladaAppMesh::ColladaAppMesh(const domInstance_geometry* instance, ColladaAppNode* node) + : instanceGeom(instance), instanceCtrl(0), appNode(node), geomExt(0) +{ + flags = 0; + numFrames = 0; + numMatFrames = 0; +} + +ColladaAppMesh::ColladaAppMesh(const domInstance_controller* instance, ColladaAppNode* node) + : instanceGeom(0), instanceCtrl(instance), appNode(node), geomExt(0) +{ + flags = 0; + numFrames = 0; + numMatFrames = 0; +} + +const char* ColladaAppMesh::getName(bool allowFixed) +{ + // Some exporters add a 'PIVOT' or unnamed node between the mesh and the + // actual object node. Detect this and return the object node name instead + // of the pivot node. + const char* nodeName = appNode->getName(); + if ( dStrEqual(nodeName, "null") || dStrEndsWith(nodeName, "PIVOT") ) + nodeName = appNode->getParentName(); + + // If all geometry is being fixed to the same size, append the size + // to the name + return allowFixed && fixedSizeEnabled ? avar("%s %d", nodeName, fixedSize) : nodeName; +} + +MatrixF ColladaAppMesh::getMeshTransform(F32 time) +{ + return appNode->getNodeTransform(time); +} + +bool ColladaAppMesh::animatesVis(const AppSequence* appSeq) +{ + #define IS_VIS_ANIMATED(node) \ + (dynamic_cast(node)->nodeExt->visibility.isAnimated(appSeq->getStart(), appSeq->getEnd())) + + // Check if the node visibility is animated within the sequence interval + return IS_VIS_ANIMATED(appNode) || (appNode->appParent ? IS_VIS_ANIMATED(appNode->appParent) : false); +} + +bool ColladaAppMesh::animatesMatFrame(const AppSequence* appSeq) +{ + // Texture coordinates may be animated in two ways: + // - by animating the MAYA profile texture transform (diffuse texture) + // - by animating the morph weights for morph targets with different UVs + + // Check if the MAYA profile texture transform is animated + for (int iMat = 0; iMat < appMaterials.size(); iMat++) { + ColladaAppMaterial* appMat = static_cast(appMaterials[iMat]); + if (appMat->effectExt && + appMat->effectExt->animatesTextureTransform(appSeq->getStart(), appSeq->getEnd())) + return true; + } + + // Check that the morph weights are animated within the sequence interval, + // and that the morph targets have different UVs to the base geometry. + bool animated = false; + bool differentUVs = false; + if (const domMorph* morph = getMorph()) { + for (int iInput = 0; iInput < morph->getTargets()->getInput_array().getCount(); iInput++) { + const domInputLocal* input = morph->getTargets()->getInput_array()[iInput]; + if (dStrEqual(input->getSemantic(), "MORPH_TARGET")) { + // @todo: Check if morph targets have different UVs to base geometry + differentUVs = false; + } + if (dStrEqual(input->getSemantic(), "MORPH_WEIGHT")) { + const domSource* source = daeSafeCast(findInputSource(input)); + AnimatedFloatList weights(source ? source->getFloat_array() : 0); + animated = weights.isAnimated(appSeq->getStart(), appSeq->getEnd()); + } + } + } + + return (animated && differentUVs); +} + +bool ColladaAppMesh::animatesFrame(const AppSequence* appSeq) +{ + // Collada s ALWAYS contain vert positions, so just need to check if + // the morph weights are animated within the sequence interval + bool animated = false; + if (const domMorph* morph = getMorph()) { + for (int iInput = 0; iInput < morph->getTargets()->getInput_array().getCount(); iInput++) { + const domInputLocal* input = morph->getTargets()->getInput_array()[iInput]; + if (dStrEqual(input->getSemantic(), "MORPH_WEIGHT")) { + const domSource* source = daeSafeCast(findInputSource(input)); + AnimatedFloatList weights(source ? source->getFloat_array() : 0); + animated = weights.isAnimated(appSeq->getStart(), appSeq->getEnd()); + break; + } + } + } + return animated; +} + +F32 ColladaAppMesh::getVisValue(F32 t) +{ + #define GET_VIS(node) \ + (dynamic_cast(node)->nodeExt->visibility.getValue(t)) + + // Get the visibility of the mesh's node at time, 't' + return GET_VIS(appNode) * (appNode->appParent ? GET_VIS(appNode->appParent) : 1.0f); +} + +S32 ColladaAppMesh::addMaterial(const char* symbol) +{ + if (!symbol) + return TSDrawPrimitive::NoMaterial; + + // Lookup the symbol in the materials already bound to this geometry/controller + // instance + Map::Iterator itr = boundMaterials.find(symbol); + if (itr != boundMaterials.end()) + return itr->value; + + // Find the Collada material that this symbol maps to + U32 matIndex = TSDrawPrimitive::NoMaterial; + const domBind_material* binds = instanceGeom ? instanceGeom->getBind_material() : + instanceCtrl->getBind_material(); + if (binds) { + const domInstance_material_Array& matArray = binds->getTechnique_common()->getInstance_material_array(); + for (int iBind = 0; iBind < matArray.getCount(); iBind++) { + if (dStrEqual(matArray[iBind]->getSymbol(), symbol)) { + + // Find the index of the bound material in the shape global list + const domMaterial* mat = daeSafeCast(matArray[iBind]->getTarget().getElement()); + for (matIndex = 0; matIndex < appMaterials.size(); matIndex++) { + if (static_cast(appMaterials[matIndex])->mat == mat) + break; + } + + // Check if this material needs to be added to the shape global list + if (matIndex == appMaterials.size()) { + if (mat) + appMaterials.push_back(new ColladaAppMaterial(mat)); + else + appMaterials.push_back(new ColladaAppMaterial(symbol)); + } + + break; + } + } + } + + // Add this symbol to the bound list for the mesh + boundMaterials.insert(StringTable->insert(symbol), matIndex); + return matIndex; +} + +void ColladaAppMesh::getPrimitives(const domGeometry* geometry) +{ + // Only do this once + if (primitives.size()) + return; + + // Read the extension + if (!geomExt) + geomExt = new ColladaExtension_geometry(geometry); + + // Get the supported primitive elements for this geometry, and warn + // about unsupported elements + Vector meshPrims; + const daeElementRefArray& contents = geometry->getMesh()->getContents(); + for (int iElem = 0; iElem < contents.getCount(); iElem++) { + + if (BasePrimitive::isPrimitive(contents[iElem])) { + if (BasePrimitive::isSupportedPrimitive(contents[iElem])) + meshPrims.push_back(BasePrimitive::get(contents[iElem])); + else { + daeErrorHandler::get()->handleWarning(avar("Collada <%s> element " + "in %s is not supported.", contents[iElem]->getElementName(), + _GetNameOrId(geometry))); + } + } + } + + MeshStreams streams; + VertTupleMap tupleMap; + + // Create Torque primitives + for (int iPrim = 0; iPrim < meshPrims.size(); iPrim++) { + + // Primitive element must have at least 1 triangle + const domListOfUInts* pTriData = meshPrims[iPrim]->getTriangleData(); + if (!pTriData) + continue; + + U32 numTriangles = pTriData->getCount() / meshPrims[iPrim]->getStride() / 3; + if (!numTriangles) + continue; + + // Create TSMesh primitive + primitives.increment(); + TSDrawPrimitive& primitive = primitives.last(); + primitive.start = indices.size(); + primitive.matIndex = (TSDrawPrimitive::Triangles | TSDrawPrimitive::Indexed) | + addMaterial(meshPrims[iPrim]->getMaterial()); + + // Get the AppMaterial associated with this primitive + ColladaAppMaterial* appMat = 0; + if (!(primitive.matIndex & TSDrawPrimitive::NoMaterial)) + appMat = static_cast(appMaterials[primitive.matIndex & TSDrawPrimitive::MaterialMask]); + + // Force the material to be double-sided if this geometry is double-sided. + if (geomExt->double_sided && appMat && appMat->effectExt) + appMat->effectExt->double_sided = true; + + // Pre-allocate triangle indices + primitive.numElements = numTriangles * 3; + indices.setSize(indices.size() + primitive.numElements); + U32* dstIndex = indices.end() - primitive.numElements; + + // Determine the offset for each element type in the stream, and also the + // maximum input offset, which will be the number of indices per vertex we + // need to skip. + domInputLocalOffsetRef sortedInputs[MeshStreams::NumStreams]; + MeshStreams::classifyInputs(meshPrims[iPrim]->getInputs(), sortedInputs); + + S32 offsets[MeshStreams::NumStreams]; + for (S32 i = 0; i < MeshStreams::NumStreams; i++) + offsets[i] = sortedInputs[i] ? sortedInputs[i]->getOffset() : -1; + + // Loop through indices + const domUint* pSrcData = &(pTriData->get(0)); + + for (U32 iTri = 0; iTri < numTriangles; iTri++) { + + // If the next triangle could cause us to index across a 16-bit + // boundary, split this primitive and clear the tuple map to + // ensure primitives only index verts within a 16-bit range. + if (vertTuples.size() && + (((vertTuples.size()-1) ^ (vertTuples.size()+2)) & 0x10000)) + { + // Pad vertTuples up to the next 16-bit boundary + while (vertTuples.size() & 0xFFFF) + vertTuples.push_back(VertTuple(vertTuples.last())); + + // Split the primitive at the current triangle + S32 indicesRemaining = (numTriangles - iTri) * 3; + if (iTri > 0) + { + daeErrorHandler::get()->handleWarning(avar("Splitting primitive " + "in %s: too many verts for 16-bit indices.", _GetNameOrId(geometry))); + + primitives.last().numElements -= indicesRemaining; + primitives.push_back(TSDrawPrimitive(primitives.last())); + } + + primitives.last().numElements = indicesRemaining; + primitives.last().start = indices.size() - indicesRemaining; + + tupleMap.clear(); + } + + streams.reset(); + streams.readInputs(meshPrims[iPrim]->getInputs()); + + for (U32 v = 0; v < 3; v++) { + // Collect vert tuples into a single array so we can easily grab + // vertex data later. + VertTuple tuple; + tuple.prim = iPrim; + tuple.vertex = offsets[MeshStreams::Points] >= 0 ? pSrcData[offsets[MeshStreams::Points]] : -1; + tuple.normal = offsets[MeshStreams::Normals] >= 0 ? pSrcData[offsets[MeshStreams::Normals]] : -1; + tuple.color = offsets[MeshStreams::Colors] >= 0 ? pSrcData[offsets[MeshStreams::Colors]] : -1; + tuple.uv = offsets[MeshStreams::UVs] >= 0 ? pSrcData[offsets[MeshStreams::UVs]] : -1; + tuple.uv2 = offsets[MeshStreams::UV2s] >= 0 ? pSrcData[offsets[MeshStreams::UV2s]] : -1; + + tuple.dataVertex = tuple.vertex > -1 ? streams.points.getPoint3FValue(tuple.vertex) : Point3F::Max; + tuple.dataNormal = tuple.normal > -1 ? streams.normals.getPoint3FValue(tuple.normal) : Point3F::Max; + tuple.dataColor = tuple.color > -1 ? streams.colors.getColorIValue(tuple.color) : ColorI(0,0,0); + tuple.dataUV = tuple.uv > -1 ? streams.uvs.getPoint2FValue(tuple.uv) : Point2F::Max; + tuple.dataUV2 = tuple.uv2 > -1 ? streams.uv2s.getPoint2FValue(tuple.uv2) : Point2F::Max; + + VertTupleMap::Iterator itr = tupleMap.find(tuple); + if (itr == tupleMap.end()) + { + itr = tupleMap.insert(tuple, vertTuples.size()); + vertTuples.push_back(tuple); + } + + // Collada uses CCW for front face and Torque uses the opposite, so + // for normal (non-inverted) meshes, the indices are flipped. + if (appNode->invertMeshes) + dstIndex[v] = itr->value; + else + dstIndex[2 - v] = itr->value; + + pSrcData += meshPrims[iPrim]->getStride(); + } + dstIndex += 3; + } + } + + for (int iPrim = 0; iPrim < meshPrims.size(); iPrim++) + delete meshPrims[iPrim]; +} + +void ColladaAppMesh::getVertexData(const domGeometry* geometry, F32 time, const MatrixF& objectOffset, + Vector& v_points, + Vector& v_norms, + Vector& v_colors, + Vector& v_uvs, + Vector& v_uv2s, + bool appendValues) +{ + if (!primitives.size()) + return; + + MeshStreams streams; + S32 lastPrimitive = -1; + ColladaAppMaterial* appMat = 0; + + // Get the supported primitive elements for this geometry + Vector meshPrims; + const daeElementRefArray& contents = geometry->getMesh()->getContents(); + for (int iElem = 0; iElem < contents.getCount(); iElem++) { + if (BasePrimitive::isSupportedPrimitive(contents[iElem])) + meshPrims.push_back(BasePrimitive::get(contents[iElem])); + } + + // If appending values, pre-allocate the arrays + if (appendValues) { + v_points.setSize(v_points.size() + vertTuples.size()); + v_uvs.setSize(v_uvs.size() + vertTuples.size()); + } + + // Get pointers to arrays + Point3F* points_array = &v_points[v_points.size() - vertTuples.size()]; + Point2F* uvs_array = &v_uvs[v_uvs.size() - vertTuples.size()]; + Point3F* norms_array = NULL; + ColorI* colors_array = NULL; + Point2F* uv2s_array = NULL; + + for (int iVert = 0; iVert < vertTuples.size(); iVert++) { + + const VertTuple& tuple = vertTuples[iVert]; + + // Change primitives? + if (tuple.prim != lastPrimitive) { + if (meshPrims.size() <= tuple.prim) { + daeErrorHandler::get()->handleError(avar("Failed to get vertex data " + "for %s. Primitives do not match base geometry.", geometry->getID())); + break; + } + + // Update vertex/normal/UV streams and get the new material index + streams.reset(); + streams.readInputs(meshPrims[tuple.prim]->getInputs()); + S32 matIndex = addMaterial(meshPrims[tuple.prim]->getMaterial()); + if (matIndex != TSDrawPrimitive::NoMaterial) + appMat = static_cast(appMaterials[matIndex]); + else + appMat = 0; + + lastPrimitive = tuple.prim; + } + + // If we are NOT appending values, only set the value if it actually exists + // in the mesh data stream. + + if (appendValues || ((tuple.vertex >= 0) && (tuple.vertex < streams.points.size()))) { + points_array[iVert] = streams.points.getPoint3FValue(tuple.vertex); + + // Flip verts for inverted meshes + if (appNode->invertMeshes) + points_array[iVert].z = -points_array[iVert].z; + + objectOffset.mulP(points_array[iVert]); + } + + if (appendValues || ((tuple.uv >= 0) && (tuple.uv < streams.uvs.size()))) { + uvs_array[iVert] = streams.uvs.getPoint2FValue(tuple.uv); + if (appMat && appMat->effectExt) + appMat->effectExt->applyTextureTransform(uvs_array[iVert], time); + uvs_array[iVert].y = 1.0f - uvs_array[iVert].y; // Collada texcoords are upside down compared to TGE + } + + // The rest is non-required data... if it doesn't exist then don't append it. + + if ( (tuple.normal >= 0) && (tuple.normal < streams.normals.size()) ) { + if ( !norms_array && iVert == 0 ) + { + v_norms.setSize(v_norms.size() + vertTuples.size()); + norms_array = &v_norms[v_norms.size() - vertTuples.size()]; + } + + if ( norms_array ) { + norms_array[iVert] = streams.normals.getPoint3FValue(tuple.normal); + + // Flip normals for inverted meshes + if (appNode->invertMeshes) + norms_array[iVert].z = -norms_array[iVert].z; + } + } + + if ( (tuple.color >= 0) && (tuple.color < streams.colors.size())) + { + if ( !colors_array && iVert == 0 ) + { + v_colors.setSize(v_colors.size() + vertTuples.size()); + colors_array = &v_colors[v_colors.size() - vertTuples.size()]; + } + + if ( colors_array ) + colors_array[iVert] = streams.colors.getColorIValue(tuple.color); + } + + if ( (tuple.uv2 >= 0) && (tuple.uv2 < streams.uv2s.size()) ) + { + if ( !uv2s_array && iVert == 0 ) + { + v_uv2s.setSize(v_uv2s.size() + vertTuples.size()); + uv2s_array = &v_uv2s[v_uv2s.size() - vertTuples.size()]; + } + + if ( uv2s_array ) + { + uv2s_array[iVert] = streams.uv2s.getPoint2FValue(tuple.uv2); + if (appMat && appMat->effectExt) + appMat->effectExt->applyTextureTransform(uv2s_array[iVert], time); + uv2s_array[iVert].y = 1.0f - uv2s_array[iVert].y; // Collada texcoords are upside down compared to TGE + } + } + } + + for (int iPrim = 0; iPrim < meshPrims.size(); iPrim++) + delete meshPrims[iPrim]; +} + +void ColladaAppMesh::getMorphVertexData(const domMorph* morph, F32 time, const MatrixF& objectOffset, + Vector& v_points, + Vector& v_norms, + Vector& v_colors, + Vector& v_uvs, + Vector& v_uv2s) +{ + // @todo: Could the base geometry (or any target geometry) also be a morph? + + // Get the target geometries and weights (could be animated) + Vector targetGeoms; + domListOfFloats targetWeights; + + for (int iInput = 0; iInput < morph->getTargets()->getInput_array().getCount(); iInput++) { + const domInputLocal* input = morph->getTargets()->getInput_array()[iInput]; + const domSource* source = daeSafeCast(findInputSource(input)); + + if (dStrEqual(input->getSemantic(), "MORPH_TARGET")) { + // Get the morph targets + _SourceReader srcTargets; + srcTargets.initFromSource(source); + + for (int iTarget = 0; iTarget < srcTargets.size(); iTarget++) { + // Lookup the element and add to the targets list + daeIDRef idref(srcTargets.getStringValue(iTarget)); + idref.setContainer(morph->getDocument()->getDomRoot()); + targetGeoms.push_back(daeSafeCast(idref.getElement())); + } + } + else if (dStrEqual(input->getSemantic(), "MORPH_WEIGHT")) { + // Get the (possibly animated) morph weight + targetWeights = AnimatedFloatList(source->getFloat_array()).getValue(time); + } + } + + // Check that we have a weight for each target + if (targetGeoms.size() != targetWeights.getCount()) + { + domController* ctrl = daeSafeCast(const_cast(morph)->getParent()); + Con::warnf("Mismatched morph targets and weights in %s.", _GetNameOrId(ctrl)); + + // Set unused targets to zero weighting (unused weights are ignored) + while (targetGeoms.size() > targetWeights.getCount()) + targetWeights.append(0.0f); + } + + // Get the base geometry and vertex data + const domGeometry* baseGeometry = daeSafeCast(morph->getSource().getElement()); + if (!baseGeometry) + return; + + getPrimitives(baseGeometry); + getVertexData(baseGeometry, time, objectOffset, v_points, v_norms, v_colors, v_uvs, v_uv2s, true); + + // Get pointers to the arrays of base geometry data + Point3F* points_array = &v_points[v_points.size() - vertTuples.size()]; + Point3F* norms_array = &v_norms[v_norms.size() - vertTuples.size()]; + Point2F* uvs_array = &v_uvs[v_uvs.size() - vertTuples.size()]; + ColorI* colors_array = v_colors.size() ? &v_colors[v_colors.size() - vertTuples.size()] : 0; + Point2F* uv2s_array = v_uv2s.size() ? &v_uv2s[v_uv2s.size() - vertTuples.size()] : 0; + + // Normalize base vertex data? + if (morph->getMethod() == MORPHMETHODTYPE_NORMALIZED) { + + F32 weightSum = 0.0f; + for (int iWeight = 0; iWeight < targetWeights.getCount(); iWeight++) { + weightSum += targetWeights[iWeight]; + } + + // Result = Base*(1.0-w1-w2 ... -wN) + w1*Target1 + w2*Target2 ... + wN*TargetN + weightSum = mClampF(1.0f - weightSum, 0.0f, 1.0f); + + for (int iVert = 0; iVert < vertTuples.size(); iVert++) { + points_array[iVert] *= weightSum; + norms_array[iVert] *= weightSum; + uvs_array[iVert] *= weightSum; + } + + if (uv2s_array) { + for (int iVert = 0; iVert < vertTuples.size(); iVert++) + uv2s_array[iVert] *= weightSum; + } + } + + // Interpolate using the target geometry and weights + for (int iTarget = 0; iTarget < targetGeoms.size(); iTarget++) { + + // Ignore empty weights + if (targetWeights[iTarget] == 0.0f) + continue; + + // Get target geometry data into temporary arrays + Vector targetPoints; + Vector targetNorms; + Vector targetUvs; + Vector targetColors; + Vector targetUv2s; + + // Copy base geometry into target geometry (will be used if target does + // not define normals or uvs) + targetPoints.set(points_array, vertTuples.size()); + targetNorms.set(norms_array, vertTuples.size()); + targetUvs.set(uvs_array, vertTuples.size()); + if (colors_array) + targetColors.set(colors_array, vertTuples.size()); + if (uv2s_array) + targetUv2s.set(uv2s_array, vertTuples.size()); + + getVertexData(targetGeoms[iTarget], time, objectOffset, targetPoints, targetNorms, targetColors, targetUvs, targetUv2s, false); + + // Combine with base geometry + for (int iVert = 0; iVert < vertTuples.size(); iVert++) { + points_array[iVert] += targetPoints[iVert] * targetWeights[iTarget]; + norms_array[iVert] += targetNorms[iVert] * targetWeights[iTarget]; + uvs_array[iVert] += targetUvs[iVert] * targetWeights[iTarget]; + } + + if (uv2s_array) { + for (int iVert = 0; iVert < vertTuples.size(); iVert++) + uv2s_array[iVert] += targetUv2s[iVert] * targetWeights[iTarget]; + } + if (colors_array) { + for (int iVert = 0; iVert < vertTuples.size(); iVert++) + colors_array[iVert] += targetColors[iVert] * (F32)targetWeights[iTarget]; + } + } +} + +void ColladaAppMesh::lockMesh(F32 t, const MatrixF& objectOffset) +{ + // Find the geometry element for this mesh. Could be one of 3 things: + // 1) a simple static mesh (Collada element) + // 2) a simple morph (some combination of static meshes) + // 3) a skin (skin geometry could also be a morph!) + daeElement* geometry = 0; + + if (instanceGeom) { + // Simple, static mesh + geometry = instanceGeom->getUrl().getElement(); + } + else if (instanceCtrl) { + const domController* ctrl = daeSafeCast(instanceCtrl->getUrl().getElement()); + if (!ctrl) { + daeErrorHandler::get()->handleWarning(avar("Failed to find " + "element for %s", getName())); + return; + } + else if (ctrl->getMorph()) { + // Morph controller + geometry = ctrl->getMorph(); + } + else { + // Skinned mesh: source geometry could be static geometry or a morph controller. + geometry = ctrl->getSkin()->getSource().getElement(); + if (geometry && geometry->getElementType() == COLLADA_TYPE::CONTROLLER) + geometry = daeSafeCast(geometry)->getMorph(); + } + } + + if (!geometry) { + daeErrorHandler::get()->handleWarning(avar("Failed to find source geometry " + "for %s", getName())); + return; + } + + // Now get the vertex data at the specified time + if (geometry->getElementType() == COLLADA_TYPE::GEOMETRY) { + getPrimitives(daeSafeCast(geometry)); + getVertexData(daeSafeCast(geometry), t, objectOffset, points, normals, colors, uvs, uv2s, true); + } + else if (geometry->getElementType() == COLLADA_TYPE::MORPH) { + getMorphVertexData(daeSafeCast(geometry), t, objectOffset, points, normals, colors, uvs, uv2s); + } + else { + daeErrorHandler::get()->handleWarning(avar("Unsupported geometry type " + "'<%s>' for %s", geometry->getElementName(), getName())); + } +} + +void ColladaAppMesh::lookupSkinData() +{ + // Only lookup skin data once + if (!isSkin() || weight.size()) + return; + + // Get the skin and vertex weight data + const domSkin* skin = daeSafeCast(instanceCtrl->getUrl().getElement())->getSkin(); + const domSkin::domVertex_weights& weightIndices = *(skin->getVertex_weights()); + const domListOfInts& weights_v = weightIndices.getV()->getValue(); + const domListOfUInts& weights_vcount = weightIndices.getVcount()->getValue(); + + MeshStreams streams; + streams.readInputs(skin->getJoints()->getInput_array()); + streams.readInputs(weightIndices.getInput_array()); + + MatrixF invObjOffset(objectOffset); + invObjOffset.inverse(); + + // Get the bind shape matrix + MatrixF bindShapeMatrix(true); + if (skin->getBind_shape_matrix()) + bindShapeMatrix = vecToMatrixF(skin->getBind_shape_matrix()->getValue()); + bindShapeMatrix.mul(invObjOffset); + + // Determine the offset into the vindices array for each vertex (since each + // vertex may have multiple [bone, weight] pairs in the array) + Vector vindicesOffset; + const domInt* vindices = (domInt*)weights_v.getRaw(0); + for (int iWeight = 0; iWeight < weights_vcount.getCount(); iWeight++) { + // Store the offset into the vindices array for this vertex + vindicesOffset.push_back(vindices - (domInt*)weights_v.getRaw(0)); + vindices += (weights_vcount[iWeight]*2); // 2 indices [bone, weight] per vert + } + + // Set vertex weights + bool tooManyWeightsWarning = false; + for (int iVert = 0; iVert < vertsPerFrame; iVert++) { + const domUint* vcount = (domUint*)weights_vcount.getRaw(0); + const domInt* vindices = (domInt*)weights_v.getRaw(0); + vindices += vindicesOffset[vertTuples[iVert].vertex]; + + S32 nonZeroWeightCount = 0; + + for (int iWeight = 0; iWeight < vcount[vertTuples[iVert].vertex]; iWeight++) { + + S32 bIndex = vindices[iWeight*2]; + F32 bWeight = streams.weights.getFloatValue( vindices[iWeight*2 + 1] ); + + // Ignore empty weights + if ( bIndex < 0 || bWeight == 0 ) + continue; + + // Limit the number of weights per bone (keep the N largest influences) + if ( nonZeroWeightCount >= TSSkinMesh::BatchData::maxBonePerVert ) + { + if (vcount[vertTuples[iVert].vertex] > TSSkinMesh::BatchData::maxBonePerVert) + { + if (!tooManyWeightsWarning) + { + tooManyWeightsWarning = true; + daeErrorHandler::get()->handleWarning(avar("At least one vertex has " + "too many bone weights. Limiting to the largest %d influences.", + TSSkinMesh::BatchData::maxBonePerVert)); + } + } + + // Too many weights => find and replace the smallest one + S32 minIndex = weight.size() - TSSkinMesh::BatchData::maxBonePerVert; + F32 minWeight = weight[minIndex]; + for (S32 i = minIndex + 1; i < weight.size(); i++) + { + if (weight[i] < minWeight) + { + minWeight = weight[i]; + minIndex = i; + } + } + + boneIndex[minIndex] = bIndex; + weight[minIndex] = bWeight; + } + else + { + vertexIndex.push_back( iVert ); + boneIndex.push_back( bIndex ); + weight.push_back( bWeight ); + nonZeroWeightCount++; + } + } + } + + // Normalize vertex weights (force weights for each vert to sum to 1) + int iWeight = 0; + while (iWeight < weight.size()) { + // Find the last weight with the same vertex number, and sum all weights for + // that vertex + F32 invTotalWeight = 0; + int iLast; + for (iLast = iWeight; iLast < weight.size(); iLast++) { + if (vertexIndex[iLast] != vertexIndex[iWeight]) + break; + invTotalWeight += weight[iLast]; + } + + // Then normalize the vertex weights + invTotalWeight = 1.0f / invTotalWeight; + for (; iWeight < iLast; iWeight++) + weight[iWeight] *= invTotalWeight; + } + + // Add dummy AppNodes to allow Collada joints to be mapped to 3space nodes + bones.setSize(streams.joints.size()); + initialTransforms.setSize(streams.joints.size()); + for (int iJoint = 0; iJoint < streams.joints.size(); iJoint++) + { + const char* jointName = streams.joints.getStringValue(iJoint); + + // Lookup the joint element + const domNode* joint = 0; + if (instanceCtrl->getSkeleton_array().getCount()) { + // Search for the node using the as the base element + for (int iSkel = 0; iSkel < instanceCtrl->getSkeleton_array().getCount(); iSkel++) { + xsAnyURI skeleton = instanceCtrl->getSkeleton_array()[iSkel]->getValue(); + daeSIDResolver resolver(skeleton.getElement(), jointName); + joint = daeSafeCast(resolver.getElement()); + if (joint) + break; + } + } + else { + // Search for the node from the root level + daeSIDResolver resolver(skin->getDocument()->getDomRoot(), jointName); + joint = daeSafeCast(resolver.getElement()); + } + + if (!joint) { + daeErrorHandler::get()->handleWarning(avar("Failed to find bone '%s', " + "defaulting to instance_controller parent node '%s'", jointName, appNode->getName())); + joint = appNode->getDomNode(); + } + bones[iJoint] = new ColladaAppNode(joint); + + initialTransforms[iJoint] = objectOffset; + + // Bone scaling is generally ignored during import, since 3space only + // stores default node transform and rotation. Compensate for this by + // removing the scaling from the inverse bind transform as well + MatrixF invBind = streams.invBindMatrices.getMatrixFValue(iJoint); + if (!ColladaUtils::getOptions().ignoreNodeScale) + { + Point3F invScale = invBind.getScale(); + invScale.x = invScale.x ? (1.0f / invScale.x) : 0; + invScale.y = invScale.y ? (1.0f / invScale.y) : 0; + invScale.z = invScale.z ? (1.0f / invScale.z) : 0; + initialTransforms[iJoint].scale(invScale); + } + + // Inverted node coordinate spaces (negative scale factor) are corrected + // in ColladaAppNode::getNodeTransform, so need to apply the same operation + // here to match + if (m_matF_determinant(invBind) < 0.0f) + initialTransforms[iJoint].scale(Point3F(1, 1, -1)); + + initialTransforms[iJoint].mul(invBind); + initialTransforms[iJoint].mul(bindShapeMatrix); + } +} diff --git a/Engine/source/ts/collada/colladaAppMesh.h b/Engine/source/ts/collada/colladaAppMesh.h new file mode 100644 index 000000000..e7d07c376 --- /dev/null +++ b/Engine/source/ts/collada/colladaAppMesh.h @@ -0,0 +1,232 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _COLLADA_APPMESH_H_ +#define _COLLADA_APPMESH_H_ + +#ifndef _TDICTIONARY_H_ +#include "core/tDictionary.h" +#endif +#ifndef _APPMESH_H_ +#include "ts/loader/appMesh.h" +#endif +#ifndef _TSSHAPELOADER_H_ +#include "ts/loader/tsShapeLoader.h" +#endif +#ifndef _COLLADA_APPNODE_H_ +#include "ts/collada/colladaAppNode.h" +#endif +#ifndef _COLLADA_EXTENSIONS_H_ +#include "ts/collada/colladaExtensions.h" +#endif + +//----------------------------------------------------------------------------- +// Torque unifies the vert position, normal and UV values, so that a single index +// uniquely identifies all 3 elements. A triangle then contains just 3 indices, +// and from that we can get the 3 positions, 3 normals and 3 UVs. +// +// for i=1:3 +// index = indices[triangle.start + i] +// points[index], normals[index], uvs[index] +// +// Collada does not use unified vertex streams, (each triangle needs 9 indices), +// so this structure is used to map a single VertTuple index to 3 indices into +// the Collada streams. The Collada (and Torque) primitive index is also stored +// because the Collada document may use different streams for different primitives. +// +// For morph geometry, we can use the same array of VertTuples to access the base +// AND all of the target geometries because they MUST have the same topology. +struct VertTuple +{ + S32 prim, vertex, normal, color, uv, uv2; + + Point3F dataVertex, dataNormal; + ColorI dataColor; + Point2F dataUV, dataUV2; + + VertTuple(): prim(-1), vertex(-1), normal(-1), color(-1), uv(-1), uv2(-1) {} + bool operator==(const VertTuple& p) const + { + return dataVertex == p.dataVertex && + dataColor == p.dataColor && + dataNormal == p.dataNormal && + dataUV == p.dataUV && + dataUV2 == p.dataUV2; + } +}; + +class ColladaAppMesh : public AppMesh +{ + typedef AppMesh Parent; + +protected: + class ColladaAppNode* appNode; ///< Pointer to the node that owns this mesh + const domInstance_geometry* instanceGeom; + const domInstance_controller* instanceCtrl; + ColladaExtension_geometry* geomExt; ///< geometry extension + + Vector vertTuples; ///< + Map boundMaterials; ///< Local map of symbols to materials + + static bool fixedSizeEnabled; ///< Set to true to fix the detail size to a particular value for all geometry + static S32 fixedSize; ///< The fixed detail size value for all geometry + + //----------------------------------------------------------------------- + + /// Get the morph controller for this mesh (if any) + const domMorph* getMorph() + { + if (instanceCtrl) { + const domController* ctrl = daeSafeCast(instanceCtrl->getUrl().getElement()); + if (ctrl && ctrl->getSkin()) + ctrl = daeSafeCast(ctrl->getSkin()->getSource().getElement()); + return ctrl ? ctrl->getMorph() : NULL; + } + return NULL; + } + + S32 addMaterial(const char* symbol); + + bool checkGeometryType(const daeElement* element); + void getPrimitives(const domGeometry* geometry); + + void getVertexData( const domGeometry* geometry, F32 time, const MatrixF& objectOffset, + Vector& points, Vector& norms, Vector& colors, + Vector& uvs, Vector& uv2s, bool appendValues); + + void getMorphVertexData( const domMorph* morph, F32 time, const MatrixF& objectOffset, + Vector& points, Vector& norms, Vector& colors, + Vector& uvs, Vector& uv2s ); + +public: + + ColladaAppMesh(const domInstance_geometry* instance, ColladaAppNode* node); + ColladaAppMesh(const domInstance_controller* instance, ColladaAppNode* node); + ~ColladaAppMesh() + { + delete geomExt; + } + + static void fixDetailSize(bool fixed, S32 size=2) + { + fixedSizeEnabled = fixed; + fixedSize = size; + } + + /// Get the name of this mesh + /// + /// @return A string containing the name of this mesh + const char *getName(bool allowFixed=true); + + //----------------------------------------------------------------------- + + /// Get a floating point property value + /// + /// @param propName Name of the property to get + /// @param defaultVal Reference to variable to hold return value + /// + /// @return True if a value was set, false if not + bool getFloat(const char *propName, F32 &defaultVal) + { + return appNode->getFloat(propName,defaultVal); + } + + /// Get an integer property value + /// + /// @param propName Name of the property to get + /// @param defaultVal Reference to variable to hold return value + /// + /// @return True if a value was set, false if not + bool getInt(const char *propName, S32 &defaultVal) + { + return appNode->getInt(propName,defaultVal); + } + + /// Get a boolean property value + /// + /// @param propName Name of the property to get + /// @param defaultVal Reference to variable to hold return value + /// + /// @return True if a value was set, false if not + bool getBool(const char *propName, bool &defaultVal) + { + return appNode->getBool(propName,defaultVal); + } + + /// Return true if this mesh is a skin + bool isSkin() + { + if (instanceCtrl) { + const domController* ctrl = daeSafeCast(instanceCtrl->getUrl().getElement()); + if (ctrl && ctrl->getSkin() && + (ctrl->getSkin()->getVertex_weights()->getV()->getValue().getCount() > 0)) + return true; + } + return false; + } + + /// Get the skin data: bones, vertex weights etc + void lookupSkinData(); + + /// Check if the mesh visibility is animated + /// + /// @param appSeq Start/end time to check + /// + /// @return True if the mesh visibility is animated, false if not + bool animatesVis(const AppSequence* appSeq); + + /// Check if the material used by this mesh is animated + /// + /// @param appSeq Start/end time to check + /// + /// @return True if the material is animated, false if not + bool animatesMatFrame(const AppSequence* appSeq); + + /// Check if the mesh is animated + /// + /// @param appSeq Start/end time to check + /// + /// @return True if the mesh is animated, false if not + bool animatesFrame(const AppSequence* appSeq); + + /// Generate the vertex, normal and triangle data for the mesh. + /// + /// @param time Time at which to generate the mesh data + /// @param objectOffset Transform to apply to the generated data (bounds transform) + void lockMesh(F32 time, const MatrixF& objectOffset); + + /// Get the transform of this mesh at a certain time + /// + /// @param time Time at which to get the transform + /// + /// @return The mesh transform at the specified time + MatrixF getMeshTransform(F32 time); + + /// Get the visibility of this mesh at a certain time + /// + /// @param time Time at which to get visibility info + /// + /// @return Visibility from 0 (invisible) to 1 (opaque) + F32 getVisValue(F32 time); +}; + +#endif // _COLLADA_APPMESH_H_ diff --git a/Engine/source/ts/collada/colladaAppNode.cpp b/Engine/source/ts/collada/colladaAppNode.cpp new file mode 100644 index 000000000..8a0dcacab --- /dev/null +++ b/Engine/source/ts/collada/colladaAppNode.cpp @@ -0,0 +1,260 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#ifdef _MSC_VER +#pragma warning(disable : 4706) // disable warning about assignment within conditional +#endif + +#include "ts/loader/appSequence.h" +#include "ts/collada/colladaExtensions.h" +#include "ts/collada/colladaAppNode.h" +#include "ts/collada/colladaAppMesh.h" +#include "ts/collada/colladaAppMesh.h" + +#include "core/stringTable.h" + +// Trim leading and trailing whitespace from the first word in the string +// Note that the string is modified. +static char* TrimFirstWord(char* str) +{ + char* value = str; + + // Trim leading whitespace + while ( value && *value && dIsspace( *value ) ) + value++; + + // Trim trailing whitespace + if ( value && *value ) + { + char* end = value + 1; + while ( *end && !dIsspace( *end ) ) + end++; + *end = '\0'; + } + + return value; +} + +ColladaAppNode::ColladaAppNode(const domNode* node, ColladaAppNode* parent) + : p_domNode(node), appParent(parent), nodeExt(new ColladaExtension_node(node)), + lastTransformTime(TSShapeLoader::DefaultTime-1), defaultTransformValid(false), + invertMeshes(false) +{ + mName = dStrdup(_GetNameOrId(node)); + mParentName = dStrdup(parent ? parent->getName() : "ROOT"); + + // Extract user properties from the extension as whitespace separated + // "name=value" pairs + char* properties = dStrdup(nodeExt->user_properties); + char* pos = properties; + char* end = properties + dStrlen( properties ); + while ( pos < end ) + { + // Find the '=' character to separate the name and value pair + char* split = dStrchr( pos, '=' ); + if ( !split ) + break; + + // Get the name (whitespace trimmed string up to the '=') + // and value (whitespace trimmed string after the '=') + *split = '\0'; + char* name = TrimFirstWord( pos ); + char* value = TrimFirstWord( split + 1 ); + + mProps.insert(StringTable->insert(name), dAtof(value)); + + pos = value + dStrlen( value ) + 1; + } + + dFree( properties ); + + // Create vector of transform elements + for (int iChild = 0; iChild < node->getContents().getCount(); iChild++) { + switch (node->getContents()[iChild]->getElementType()) { + case COLLADA_TYPE::TRANSLATE: + case COLLADA_TYPE::ROTATE: + case COLLADA_TYPE::SCALE: + case COLLADA_TYPE::SKEW: + case COLLADA_TYPE::MATRIX: + case COLLADA_TYPE::LOOKAT: + nodeTransforms.increment(); + nodeTransforms.last().element = node->getContents()[iChild]; + break; + } + } +} + +// Get all child nodes +void ColladaAppNode::buildChildList() +{ + // Process children: collect and elements + for (int iChild = 0; iChild < p_domNode->getContents().getCount(); iChild++) { + + daeElement* child = p_domNode->getContents()[iChild]; + switch (child->getElementType()) { + + case COLLADA_TYPE::NODE: + { + domNode* node = daeSafeCast(child); + mChildNodes.push_back(new ColladaAppNode(node, this)); + break; + } + + case COLLADA_TYPE::INSTANCE_NODE: + { + domInstance_node* instanceNode = daeSafeCast(child); + domNode* node = daeSafeCast(instanceNode->getUrl().getElement()); + if (node) + mChildNodes.push_back(new ColladaAppNode(node, this)); + else + Con::warnf("Failed to resolve instance_node with url=%s", instanceNode->getUrl().originalStr().c_str()); + break; + } + } + } +} + +// Get all geometry attached to this node +void ColladaAppNode::buildMeshList() +{ + // Process children: collect and elements + for (int iChild = 0; iChild < p_domNode->getContents().getCount(); iChild++) { + + daeElement* child = p_domNode->getContents()[iChild]; + switch (child->getElementType()) { + + case COLLADA_TYPE::INSTANCE_GEOMETRY: + { + // Only . instances are supported + domInstance_geometry* instanceGeom = daeSafeCast(child); + if (instanceGeom) { + domGeometry* geometry = daeSafeCast(instanceGeom->getUrl().getElement()); + if (geometry && geometry->getMesh()) + mMeshes.push_back(new ColladaAppMesh(instanceGeom, this)); + } + break; + } + + case COLLADA_TYPE::INSTANCE_CONTROLLER: + mMeshes.push_back(new ColladaAppMesh(daeSafeCast(child), this)); + break; + } + } +} + +bool ColladaAppNode::animatesTransform(const AppSequence* appSeq) +{ + // Check if any of this node's transform elements are animated during the + // sequence interval + for (int iTxfm = 0; iTxfm < nodeTransforms.size(); iTxfm++) { + if (nodeTransforms[iTxfm].isAnimated(appSeq->getStart(), appSeq->getEnd())) + return true; + } + return false; +} + +/// Get the world transform of the node at the specified time +MatrixF ColladaAppNode::getNodeTransform(F32 time) +{ + // Avoid re-computing the default transform if possible + if (defaultTransformValid && time == TSShapeLoader::DefaultTime) + { + return defaultNodeTransform; + } + else + { + MatrixF nodeTransform = getTransform(time); + + // Check for inverted node coordinate spaces => can happen when modelers + // use the 'mirror' tool in their 3d app. Shows up as negative + // transforms in the collada model. + if (m_matF_determinant(nodeTransform) < 0.0f) + { + // Mark this node as inverted so we can mirror mesh geometry, then + // de-invert the transform matrix + invertMeshes = true; + nodeTransform.scale(Point3F(1, 1, -1)); + } + + // Cache the default transform + if (time == TSShapeLoader::DefaultTime) + { + defaultTransformValid = true; + defaultNodeTransform = nodeTransform; + } + + return nodeTransform; + } +} + +MatrixF ColladaAppNode::getTransform(F32 time) +{ + // Check if we can use the last computed transform + if (time == lastTransformTime) + return lastTransform; + + if (appParent) { + // Get parent node's transform + lastTransform = appParent->getTransform(time); + } + else { + // no parent (ie. root level) => scale by global shape + lastTransform.identity(); + lastTransform.scale(ColladaUtils::getOptions().unit); + if (!isBounds()) + ColladaUtils::convertTransform(lastTransform); // don't convert bounds node transform (or upAxis won't work!) + } + + // Multiply by local node transform elements + for (int iTxfm = 0; iTxfm < nodeTransforms.size(); iTxfm++) { + + MatrixF mat(true); + + // Convert the transform element to a MatrixF + switch (nodeTransforms[iTxfm].element->getElementType()) { + case COLLADA_TYPE::TRANSLATE: mat = vecToMatrixF(nodeTransforms[iTxfm].getValue(time)); break; + case COLLADA_TYPE::SCALE: mat = vecToMatrixF(nodeTransforms[iTxfm].getValue(time)); break; + case COLLADA_TYPE::ROTATE: mat = vecToMatrixF(nodeTransforms[iTxfm].getValue(time)); break; + case COLLADA_TYPE::MATRIX: mat = vecToMatrixF(nodeTransforms[iTxfm].getValue(time)); break; + case COLLADA_TYPE::SKEW: mat = vecToMatrixF(nodeTransforms[iTxfm].getValue(time)); break; + case COLLADA_TYPE::LOOKAT: mat = vecToMatrixF(nodeTransforms[iTxfm].getValue(time)); break; + } + + // Remove node scaling (but keep reflections) if desired + if (ColladaUtils::getOptions().ignoreNodeScale) + { + Point3F invScale = mat.getScale(); + invScale.x = invScale.x ? (1.0f / invScale.x) : 0; + invScale.y = invScale.y ? (1.0f / invScale.y) : 0; + invScale.z = invScale.z ? (1.0f / invScale.z) : 0; + mat.scale(invScale); + } + + // Post multiply the animated transform + lastTransform.mul(mat); + } + + lastTransformTime = time; + return lastTransform; +} diff --git a/Engine/source/ts/collada/colladaAppNode.h b/Engine/source/ts/collada/colladaAppNode.h new file mode 100644 index 000000000..472ec505c --- /dev/null +++ b/Engine/source/ts/collada/colladaAppNode.h @@ -0,0 +1,110 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _COLLADA_APPNODE_H_ +#define _COLLADA_APPNODE_H_ + +#ifndef _TDICTIONARY_H_ +#include "core/tDictionary.h" +#endif +#ifndef _APPNODE_H_ +#include "ts/loader/appNode.h" +#endif +#ifndef _COLLADA_EXTENSIONS_H_ +#include "ts/collada/colladaExtensions.h" +#endif + +class ColladaAppNode : public AppNode +{ + typedef AppNode Parent; + friend class ColladaAppMesh; + + MatrixF getTransform(F32 time); + void buildMeshList(); + void buildChildList(); + +protected: + + const domNode* p_domNode; ///< Pointer to the node in the Collada DOM + ColladaAppNode* appParent; ///< Parent node in Collada-space + ColladaExtension_node* nodeExt; ///< node extension + Vector nodeTransforms; ///< Ordered vector of node transform elements (scale, translate etc) + bool invertMeshes; ///< True if this node's coordinate space is inverted (left handed) + + Map mProps; ///< Hash of float properties (converted to int or bool as needed) + + F32 lastTransformTime; ///< Time of the last transform lookup (getTransform) + MatrixF lastTransform; ///< Last transform lookup (getTransform) + bool defaultTransformValid; ///< Flag indicating whether the defaultNodeTransform is valid + MatrixF defaultNodeTransform; ///< Transform at DefaultTime + +public: + + ColladaAppNode(const domNode* node, ColladaAppNode* parent = 0); + virtual ~ColladaAppNode() + { + delete nodeExt; + mProps.clear(); + } + + const domNode* getDomNode() const { return p_domNode; } + + //----------------------------------------------------------------------- + const char *getName() { return mName; } + const char *getParentName() { return mParentName; } + + bool isEqual(AppNode* node) + { + const ColladaAppNode* appNode = dynamic_cast(node); + return (appNode && (appNode->p_domNode == p_domNode)); + } + + // Property look-ups: only float properties are stored, the rest are + // converted from floats as needed + bool getFloat(const char* propName, F32& defaultVal) + { + Map::Iterator itr = mProps.find(propName); + if (itr != mProps.end()) + defaultVal = itr->value; + return false; + } + bool getInt(const char* propName, S32& defaultVal) + { + F32 value = defaultVal; + bool ret = getFloat(propName, value); + defaultVal = (S32)value; + return ret; + } + bool getBool(const char* propName, bool& defaultVal) + { + F32 value = defaultVal; + bool ret = getFloat(propName, value); + defaultVal = (value != 0); + return ret; + } + + MatrixF getNodeTransform(F32 time); + bool animatesTransform(const AppSequence* appSeq); + bool isParentRoot() { return (appParent == NULL); } +}; + +#endif // _COLLADA_APPNODE_H_ diff --git a/Engine/source/ts/collada/colladaAppSequence.cpp b/Engine/source/ts/collada/colladaAppSequence.cpp new file mode 100644 index 000000000..3422fe029 --- /dev/null +++ b/Engine/source/ts/collada/colladaAppSequence.cpp @@ -0,0 +1,97 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "ts/collada/colladaExtensions.h" +#include "ts/collada/colladaAppSequence.h" + + +ColladaAppSequence::ColladaAppSequence(const domAnimation_clip* clip) + : pClip(clip), clipExt(new ColladaExtension_animation_clip(clip)) +{ + seqStart = pClip->getStart(); + seqEnd = pClip->getEnd(); +} + +ColladaAppSequence::~ColladaAppSequence() +{ + delete clipExt; +} + +const char* ColladaAppSequence::getName() const +{ + return _GetNameOrId(pClip); +} + +S32 ColladaAppSequence::getNumTriggers() +{ + return clipExt->triggers.size(); +} + +void ColladaAppSequence::getTrigger(S32 index, TSShape::Trigger& trigger) +{ + trigger.pos = clipExt->triggers[index].time; + trigger.state = clipExt->triggers[index].state; +} + +U32 ColladaAppSequence::getFlags() const +{ + U32 flags = 0; + if (clipExt->cyclic) flags |= TSShape::Cyclic; + if (clipExt->blend) flags |= TSShape::Blend; + return flags; +} + +F32 ColladaAppSequence::getPriority() +{ + return clipExt->priority; +} + +F32 ColladaAppSequence::getBlendRefTime() +{ + return clipExt->blendReferenceTime; +} + +void ColladaAppSequence::setActive(bool active) +{ + for (int iAnim = 0; iAnim < getClip()->getInstance_animation_array().getCount(); iAnim++) { + domAnimation* anim = daeSafeCast(getClip()->getInstance_animation_array()[iAnim]->getUrl().getElement()); + if (anim) + setAnimationActive(anim, active); + } +} + +void ColladaAppSequence::setAnimationActive(const domAnimation* anim, bool active) +{ + // Enabled/disable data channels for this animation + for (int iChannel = 0; iChannel < anim->getChannel_array().getCount(); iChannel++) { + domChannel* channel = anim->getChannel_array()[iChannel]; + AnimData* animData = reinterpret_cast(channel->getUserData()); + if (animData) + animData->enabled = active; + } + + // Recurse into child animations + for (int iAnim = 0; iAnim < anim->getAnimation_array().getCount(); iAnim++) + setAnimationActive(anim->getAnimation_array()[iAnim], active); +} diff --git a/Engine/source/ts/collada/colladaAppSequence.h b/Engine/source/ts/collada/colladaAppSequence.h new file mode 100644 index 000000000..83be18be4 --- /dev/null +++ b/Engine/source/ts/collada/colladaAppSequence.h @@ -0,0 +1,65 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _COLLADA_APPSEQUENCE_H_ +#define _COLLADA_APPSEQUENCE_H_ + +#ifndef _APPSEQUENCE_H_ +#include "ts/loader/appSequence.h" +#endif + +class domAnimation_clip; +class ColladaExtension_animation_clip; + +class ColladaAppSequence : public AppSequence +{ + const domAnimation_clip* pClip; + ColladaExtension_animation_clip* clipExt; + + F32 seqStart; + F32 seqEnd; + + void setAnimationActive(const domAnimation* anim, bool active); + +public: + ColladaAppSequence(const domAnimation_clip* clip); + ~ColladaAppSequence(); + + void setActive(bool active); + + const domAnimation_clip* getClip() const { return pClip; } + + S32 getNumTriggers(); + void getTrigger(S32 index, TSShape::Trigger& trigger); + + const char* getName() const; + + F32 getStart() const { return seqStart; } + F32 getEnd() const { return seqEnd; } + void setEnd(F32 end) { seqEnd = end; } + + U32 getFlags() const; + F32 getPriority(); + F32 getBlendRefTime(); +}; + +#endif // _COLLADA_APPSEQUENCE_H_ diff --git a/Engine/source/ts/collada/colladaExtensions.cpp b/Engine/source/ts/collada/colladaExtensions.cpp new file mode 100644 index 000000000..aed8dc581 --- /dev/null +++ b/Engine/source/ts/collada/colladaExtensions.cpp @@ -0,0 +1,62 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "math/mRandom.h" +#include "ts/collada/colladaExtensions.h" + +/// Check if any of the MAYA texture transform elements are animated within +/// the interval +bool ColladaExtension_effect::animatesTextureTransform(F32 start, F32 end) +{ + return repeatU.isAnimated(start, end) || repeatV.isAnimated(start, end) || + offsetU.isAnimated(start, end) || offsetV.isAnimated(start, end) || + rotateUV.isAnimated(start, end) || noiseU.isAnimated(start, end) || + noiseV.isAnimated(start, end); +} + +/// Apply the MAYA texture transform to the given UV coordinates +void ColladaExtension_effect::applyTextureTransform(Point2F& uv, F32 time) +{ + // This function will be called for every tvert, every frame. So cache the + // texture transform parameters to avoid interpolating them every call (since + // they are constant for all tverts for a given 't') + if (time != lastAnimTime) { + // Update texture transform + textureTransform.set(EulerF(0, 0, rotateUV.getValue(time))); + textureTransform.setPosition(Point3F( + offsetU.getValue(time) + noiseU.getValue(time)*gRandGen.randF(), + offsetV.getValue(time) + noiseV.getValue(time)*gRandGen.randF(), + 0)); + textureTransform.scale(Point3F(repeatU.getValue(time), repeatV.getValue(time), 1.0f)); + + lastAnimTime = time; + } + + // Apply texture transform + Point3F result; + textureTransform.mulP(Point3F(uv.x, uv.y, 0), &result); + + uv.x = result.x; + uv.y = result.y; +} diff --git a/Engine/source/ts/collada/colladaExtensions.h b/Engine/source/ts/collada/colladaExtensions.h new file mode 100644 index 000000000..ab6d69289 --- /dev/null +++ b/Engine/source/ts/collada/colladaExtensions.h @@ -0,0 +1,305 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _COLLADA_EXTENSIONS_H_ +#define _COLLADA_EXTENSIONS_H_ + +#ifndef _TSSHAPE_LOADER_H_ +#include "ts/loader/tsShapeLoader.h" +#endif +#ifndef _COLLADA_UTILS_H_ +#include "ts/collada/colladaUtils.h" +#endif + +//----------------------------------------------------------------------------- +// Collada allows custom data to be included with many elements using the +// tag, followed by one or more named technique profiles. eg. +// +// +// +// value0 +// value1 +// ... +// +// value0 +// value1 +// ... +// +// This class provides an easy way to read the custom parameters into a strongly +// typed subclass. +class ColladaExtension +{ + // Helper macro to simplify getting named parameters + #define GET_EXTRA_PARAM(param, defaultVal) \ + get(#param, param, defaultVal) + +protected: + const domTechnique* pTechnique; + + /// Find the technique with the named profile + template const domTechnique* findExtraTechnique(const T* element, const char* name) const + { + if (element) { + for (int iExt = 0; iExt < element->getExtra_array().getCount(); iExt++) { + for (int iTech = 0; iTech < element->getExtra_array()[iExt]->getTechnique_array().getCount(); iTech++) { + if (dStrEqual(element->getExtra_array()[iExt]->getTechnique_array()[iTech]->getProfile(), name)) + return element->getExtra_array()[iExt]->getTechnique_array()[iTech]; + } + } + } + return NULL; + } + + /// The element does not define an extra_array, so need a specialized + /// version of the template + const domTechnique* findExtraTechnique( + const domCommon_color_or_texture_type_complexType::domTexture* element, const char* name) const + { + if (element && element->getExtra()) { + for (int iTech = 0; iTech < element->getExtra()->getTechnique_array().getCount(); iTech++) { + if (dStrEqual(element->getExtra()->getTechnique_array()[iTech]->getProfile(), name)) + return element->getExtra()->getTechnique_array()[iTech]; + } + } + return NULL; + } + + /// Find the parameter with the given name + const domAny* findParam(const char* name) + { + if (pTechnique) { + // search the technique contents for the desired parameter + for (int iParam = 0; iParam < pTechnique->getContents().getCount(); iParam++) { + const domAny* param = daeSafeCast(pTechnique->getContents()[iParam]); + if (param && !dStrcmp(param->getElementName(), name)) + return param; + } + } + return NULL; + } + + /// Get the value of the named parameter (use defaultVal if parameter not found) + template void get(const char* name, T& value, T defaultVal) + { + value = defaultVal; + if (const domAny* param = findParam(name)) + value = convert(param->getValue()); + } + + /// Get the value of the named animated parameter (use defaultVal if parameter not found) + template void get(const char* name, AnimatedElement& value, T defaultVal) + { + value.defaultVal = defaultVal; + if (const domAny* param = findParam(name)) + value.element = param; + } + +public: + ColladaExtension() : pTechnique(0) { } + virtual ~ColladaExtension() { } +}; + +/// Extensions for the element (and its children) +class ColladaExtension_effect : public ColladaExtension +{ + // Cached texture transform + F32 lastAnimTime; + MatrixF textureTransform; + +public: + //---------------------------------- + // + // MAX3D profile elements + bool double_sided; + + //---------------------------------- + // . + // GOOGLEEARTH profile elements + //bool double_sided; + + //---------------------------------- + // ..... + // MAYA profile elements + bool wrapU, wrapV; + bool mirrorU, mirrorV; + AnimatedFloat coverageU, coverageV; + AnimatedFloat translateFrameU, translateFrameV; + AnimatedFloat rotateFrame; + AnimatedBool stagger; // @todo: not supported yet + AnimatedFloat repeatU, repeatV; + AnimatedFloat offsetU, offsetV; + AnimatedFloat rotateUV; + AnimatedFloat noiseU, noiseV; + + //---------------------------------- + // .. + // FCOLLADA profile elements + domFx_sampler2D_common_complexType* bumpSampler; + +public: + ColladaExtension_effect(const domEffect* effect) + : lastAnimTime(TSShapeLoader::DefaultTime-1), textureTransform(true), bumpSampler(0) + { + //---------------------------------- + // + // MAX3D profile + pTechnique = findExtraTechnique(effect, "MAX3D"); + GET_EXTRA_PARAM(double_sided, false); + + //---------------------------------- + // . + const domProfile_COMMON* profileCommon = ColladaUtils::findEffectCommonProfile(effect); + + // GOOGLEEARTH profile (same double_sided element) + pTechnique = findExtraTechnique(profileCommon, "GOOGLEEARTH"); + GET_EXTRA_PARAM(double_sided, double_sided); + + //---------------------------------- + // ..... + const domCommon_color_or_texture_type_complexType* domDiffuse = ColladaUtils::findEffectDiffuse(effect); + const domFx_sampler2D_common_complexType* sampler2D = ColladaUtils::getTextureSampler(effect, domDiffuse); + + // Use the sampler2D to set default values for wrap/mirror flags + wrapU = wrapV = true; + mirrorU = mirrorV = false; + if (sampler2D) { + domFx_sampler2D_common_complexType::domWrap_s* wrap_s = sampler2D->getWrap_s(); + domFx_sampler2D_common_complexType::domWrap_t* wrap_t = sampler2D->getWrap_t(); + + mirrorU = (wrap_s && wrap_s->getValue() == FX_SAMPLER_WRAP_COMMON_MIRROR); + wrapU = (mirrorU || !wrap_s || (wrap_s->getValue() == FX_SAMPLER_WRAP_COMMON_WRAP)); + mirrorV = (wrap_t && wrap_t->getValue() == FX_SAMPLER_WRAP_COMMON_MIRROR); + wrapV = (mirrorV || !wrap_t || (wrap_t->getValue() == FX_SAMPLER_WRAP_COMMON_WRAP)); + } + + // MAYA profile + pTechnique = findExtraTechnique(domDiffuse ? domDiffuse->getTexture() : 0, "MAYA"); + GET_EXTRA_PARAM(wrapU, wrapU); GET_EXTRA_PARAM(wrapV, wrapV); + GET_EXTRA_PARAM(mirrorU, mirrorU); GET_EXTRA_PARAM(mirrorV, mirrorV); + GET_EXTRA_PARAM(coverageU, 1.0); GET_EXTRA_PARAM(coverageV, 1.0); + GET_EXTRA_PARAM(translateFrameU, 0.0); GET_EXTRA_PARAM(translateFrameV, 0.0); + GET_EXTRA_PARAM(rotateFrame, 0.0); + GET_EXTRA_PARAM(stagger, false); + GET_EXTRA_PARAM(repeatU, 1.0); GET_EXTRA_PARAM(repeatV, 1.0); + GET_EXTRA_PARAM(offsetU, 0.0); GET_EXTRA_PARAM(offsetV, 0.0); + GET_EXTRA_PARAM(rotateUV, 0.0); + GET_EXTRA_PARAM(noiseU, 0.0); GET_EXTRA_PARAM(noiseV, 0.0); + + // FCOLLADA profile + if (profileCommon) { + pTechnique = findExtraTechnique((const domProfile_COMMON::domTechnique*)profileCommon->getTechnique(), "FCOLLADA"); + if (pTechnique) { + domAny* bump = daeSafeCast(const_cast(pTechnique)->getChild("bump")); + if (bump) { + domAny* bumpTexture = daeSafeCast(bump->getChild("texture")); + if (bumpTexture) { + daeSIDResolver resolver(const_cast(effect), bumpTexture->getAttribute("texture").c_str()); + domCommon_newparam_type* param = daeSafeCast(resolver.getElement()); + if (param) + bumpSampler = param->getSampler2D(); + } + } + } + } + } + + /// Check if any of the MAYA texture transform elements are animated within + /// the interval + bool animatesTextureTransform(F32 start, F32 end); + + /// Apply the MAYA texture transform to the given UV coordinates + void applyTextureTransform(Point2F& uv, F32 time); +}; + +/// Extensions for the element +class ColladaExtension_node : public ColladaExtension +{ +public: + // FCOLLADA or OpenCOLLADA profile elements + AnimatedFloat visibility; + const char* user_properties; + + ColladaExtension_node(const domNode* node) + { + // FCOLLADA profile + pTechnique = findExtraTechnique(node, "FCOLLADA"); + GET_EXTRA_PARAM(visibility, 1.0); + GET_EXTRA_PARAM(user_properties, ""); + + // OpenCOLLADA profile + pTechnique = findExtraTechnique(node, "OpenCOLLADA"); + if (!visibility.element) + GET_EXTRA_PARAM(visibility, 1.0); + GET_EXTRA_PARAM(user_properties, user_properties); + } +}; + +/// Extensions for the element +class ColladaExtension_geometry : public ColladaExtension +{ +public: + // MAYA profile elements + bool double_sided; + + ColladaExtension_geometry(const domGeometry* geometry) + { + // MAYA profile + pTechnique = findExtraTechnique(geometry, "MAYA"); + GET_EXTRA_PARAM(double_sided, false); + } +}; + +// Extensions for the element +class ColladaExtension_animation_clip : public ColladaExtension +{ +public: + struct Trigger { + F32 time; + S32 state; + }; + + // Torque profile elements (none of these are animatable) + S32 num_triggers; + Vector triggers; + bool cyclic; + bool blend; + F32 blendReferenceTime; + F32 priority; + + ColladaExtension_animation_clip(const domAnimation_clip* clip) + { + // Torque profile + pTechnique = findExtraTechnique(clip, "Torque"); + GET_EXTRA_PARAM(num_triggers, 0); + for (int iTrigger = 0; iTrigger < num_triggers; iTrigger++) { + triggers.increment(); + get(avar("trigger_time%d", iTrigger), triggers.last().time, 0.0f); + get(avar("trigger_state%d", iTrigger), triggers.last().state, 0); + } + GET_EXTRA_PARAM(cyclic, false); + GET_EXTRA_PARAM(blend, false); + GET_EXTRA_PARAM(blendReferenceTime, 0.0f); + GET_EXTRA_PARAM(priority, 5.0f); + } +}; + +#endif // _COLLADA_EXTENSIONS_H_ diff --git a/Engine/source/ts/collada/colladaImport.cpp b/Engine/source/ts/collada/colladaImport.cpp new file mode 100644 index 000000000..7692dcc77 --- /dev/null +++ b/Engine/source/ts/collada/colladaImport.cpp @@ -0,0 +1,259 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "core/volume.h" +#include "ts/collada/colladaUtils.h" +#include "ts/collada/colladaAppNode.h" +#include "ts/collada/colladaShapeLoader.h" + +#include "gui/controls/guiTreeViewCtrl.h" + +// Helper struct for counting nodes, meshes and polygons down through the scene +// hierarchy +struct SceneStats +{ + S32 numNodes; + S32 numMeshes; + S32 numPolygons; + S32 numMaterials; + S32 numLights; + S32 numClips; + + SceneStats() : numNodes(0), numMeshes(0), numPolygons(0), numMaterials(0), numLights(0), numClips(0) { } +}; + +// Recurse through the adding nodes and geometry to the GuiTreeView control +static void processNode(GuiTreeViewCtrl* tree, domNode* node, S32 parentID, SceneStats& stats) +{ + stats.numNodes++; + S32 nodeID = tree->insertItem(parentID, _GetNameOrId(node), "node", "", 0, 0); + + // Update mesh and poly counts + for (int i = 0; i < node->getContents().getCount(); i++) + { + domGeometry* geom = 0; + const char* elemName = ""; + + daeElement* child = node->getContents()[i]; + switch (child->getElementType()) + { + case COLLADA_TYPE::INSTANCE_GEOMETRY: + { + domInstance_geometry* instgeom = daeSafeCast(child); + if (instgeom) + { + geom = daeSafeCast(instgeom->getUrl().getElement()); + elemName = _GetNameOrId(geom); + } + break; + } + + case COLLADA_TYPE::INSTANCE_CONTROLLER: + { + domInstance_controller* instctrl = daeSafeCast(child); + if (instctrl) + { + domController* ctrl = daeSafeCast(instctrl->getUrl().getElement()); + elemName = _GetNameOrId(ctrl); + if (ctrl && ctrl->getSkin()) + geom = daeSafeCast(ctrl->getSkin()->getSource().getElement()); + else if (ctrl && ctrl->getMorph()) + geom = daeSafeCast(ctrl->getMorph()->getSource().getElement()); + } + break; + } + + case COLLADA_TYPE::INSTANCE_LIGHT: + stats.numLights++; + tree->insertItem(nodeID, _GetNameOrId(node), "light", "", 0, 0); + break; + } + + if (geom && geom->getMesh()) + { + const char* name = _GetNameOrId(node); + if ( dStrEqual( name, "null" ) || dStrEndsWith( name, "PIVOT" ) ) + name = _GetNameOrId( daeSafeCast(node->getParent()) ); + + stats.numMeshes++; + tree->insertItem(nodeID, name, "mesh", "", 0, 0); + + for (S32 j = 0; j < geom->getMesh()->getTriangles_array().getCount(); j++) + stats.numPolygons += geom->getMesh()->getTriangles_array()[j]->getCount(); + for (S32 j = 0; j < geom->getMesh()->getTristrips_array().getCount(); j++) + stats.numPolygons += geom->getMesh()->getTristrips_array()[j]->getCount(); + for (S32 j = 0; j < geom->getMesh()->getTrifans_array().getCount(); j++) + stats.numPolygons += geom->getMesh()->getTrifans_array()[j]->getCount(); + for (S32 j = 0; j < geom->getMesh()->getPolygons_array().getCount(); j++) + stats.numPolygons += geom->getMesh()->getPolygons_array()[j]->getCount(); + for (S32 j = 0; j < geom->getMesh()->getPolylist_array().getCount(); j++) + stats.numPolygons += geom->getMesh()->getPolylist_array()[j]->getCount(); + } + } + + // Recurse into child nodes + for (S32 i = 0; i < node->getNode_array().getCount(); i++) + processNode(tree, node->getNode_array()[i], nodeID, stats); + + for (S32 i = 0; i < node->getInstance_node_array().getCount(); i++) + { + domInstance_node* instnode = node->getInstance_node_array()[i]; + domNode* node = daeSafeCast(instnode->getUrl().getElement()); + if (node) + processNode(tree, node, nodeID, stats); + } +} + +ConsoleFunction( enumColladaForImport, bool, 3, 3, + "(string shapePath, GuiTreeViewCtrl ctrl) Collect scene information from " + "a COLLADA file and store it in a GuiTreeView control. This function is " + "used by the COLLADA import gui to show a preview of the scene contents " + "prior to import, and is probably not much use for anything else.\n" + "@param shapePath COLLADA filename\n" + "@param ctrl GuiTreeView control to add elements to\n" + "@return true if successful, false otherwise\n" + "@ingroup Editors\n" + "@internal") +{ + GuiTreeViewCtrl* tree; + if (!Sim::findObject(argv[2], tree)) + { + Con::errorf("enumColladaScene::Could not find GuiTreeViewCtrl '%s'", argv[2]); + return false; + } + + // Check if a cached DTS is available => no need to import the collada file + // if we can load the DTS instead + Torque::Path path(argv[1]); + if (ColladaShapeLoader::canLoadCachedDTS(path)) + return false; + + // Check if this is a Sketchup file (.kmz) and if so, mount the zip filesystem + // and get the path to the DAE file. + String mountPoint; + Torque::Path daePath; + bool isSketchup = ColladaShapeLoader::checkAndMountSketchup(path, mountPoint, daePath); + + // Load the Collada file into memory + domCOLLADA* root = ColladaShapeLoader::getDomCOLLADA(daePath); + if (!root) + { + TSShapeLoader::updateProgress(TSShapeLoader::Load_Complete, "Load complete"); + return false; + } + + if (isSketchup) + { + // Unmount the zip if we mounted it + Torque::FS::Unmount(mountPoint); + } + + // Initialize tree + tree->removeItem(0); + S32 nodesID = tree->insertItem(0, "Shape", "", "", 0, 0); + S32 matsID = tree->insertItem(0, "Materials", "", "", 0, 0); + S32 animsID = tree->insertItem(0, "Animations", "", "", 0, 0); + + SceneStats stats; + + // Query DOM for shape summary details + for (int i = 0; i < root->getLibrary_visual_scenes_array().getCount(); i++) + { + const domLibrary_visual_scenes* libScenes = root->getLibrary_visual_scenes_array()[i]; + for (int j = 0; j < libScenes->getVisual_scene_array().getCount(); j++) + { + const domVisual_scene* visualScene = libScenes->getVisual_scene_array()[j]; + for (int k = 0; k < visualScene->getNode_array().getCount(); k++) + processNode(tree, visualScene->getNode_array()[k], nodesID, stats); + } + } + + // Get material count + for (S32 i = 0; i < root->getLibrary_materials_array().getCount(); i++) + { + const domLibrary_materials* libraryMats = root->getLibrary_materials_array()[i]; + stats.numMaterials += libraryMats->getMaterial_array().getCount(); + for (S32 j = 0; j < libraryMats->getMaterial_array().getCount(); j++) + { + domMaterial* mat = libraryMats->getMaterial_array()[j]; + tree->insertItem(matsID, _GetNameOrId(mat), _GetNameOrId(mat), "", 0, 0); + } + } + + // Get animation count + for (S32 i = 0; i < root->getLibrary_animation_clips_array().getCount(); i++) + { + const domLibrary_animation_clips* libraryClips = root->getLibrary_animation_clips_array()[i]; + stats.numClips += libraryClips->getAnimation_clip_array().getCount(); + for (S32 j = 0; j < libraryClips->getAnimation_clip_array().getCount(); j++) + { + domAnimation_clip* clip = libraryClips->getAnimation_clip_array()[j]; + tree->insertItem(animsID, _GetNameOrId(clip), "animation", "", 0, 0); + } + } + if (stats.numClips == 0) + { + // No clips => check if there are any animations (these will be added to a default clip) + for (S32 i = 0; i < root->getLibrary_animations_array().getCount(); i++) + { + const domLibrary_animations* libraryAnims = root->getLibrary_animations_array()[i]; + if (libraryAnims->getAnimation_array().getCount()) + { + stats.numClips = 1; + tree->insertItem(animsID, "ambient", "animation", "", 0, 0); + break; + } + } + } + + // Extract the global scale and up_axis from the top level element, + F32 unit = 1.0f; + domUpAxisType upAxis = UPAXISTYPE_Z_UP; + if (root->getAsset()) { + if (root->getAsset()->getUnit()) + unit = root->getAsset()->getUnit()->getMeter(); + if (root->getAsset()->getUp_axis()) + upAxis = root->getAsset()->getUp_axis()->getValue(); + } + + TSShapeLoader::updateProgress(TSShapeLoader::Load_Complete, "Load complete"); + + // Store shape information in the tree control + tree->setDataField(StringTable->insert("_nodeCount"), 0, avar("%d", stats.numNodes)); + tree->setDataField(StringTable->insert("_meshCount"), 0, avar("%d", stats.numMeshes)); + tree->setDataField(StringTable->insert("_polygonCount"), 0, avar("%d", stats.numPolygons)); + tree->setDataField(StringTable->insert("_materialCount"), 0, avar("%d", stats.numMaterials)); + tree->setDataField(StringTable->insert("_lightCount"), 0, avar("%d", stats.numLights)); + tree->setDataField(StringTable->insert("_animCount"), 0, avar("%d", stats.numClips)); + tree->setDataField(StringTable->insert("_unit"), 0, avar("%g", unit)); + + if (upAxis == UPAXISTYPE_X_UP) + tree->setDataField(StringTable->insert("_upAxis"), 0, "X_AXIS"); + else if (upAxis == UPAXISTYPE_Y_UP) + tree->setDataField(StringTable->insert("_upAxis"), 0, "Y_AXIS"); + else + tree->setDataField(StringTable->insert("_upAxis"), 0, "Z_AXIS"); + + return true; +} diff --git a/Engine/source/ts/collada/colladaLights.cpp b/Engine/source/ts/collada/colladaLights.cpp new file mode 100644 index 000000000..1388278d0 --- /dev/null +++ b/Engine/source/ts/collada/colladaLights.cpp @@ -0,0 +1,236 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "ts/collada/colladaUtils.h" +#include "ts/collada/colladaAppNode.h" +#include "ts/collada/colladaShapeLoader.h" + +#include "T3D/pointLight.h" +#include "T3D/spotLight.h" + + +//----------------------------------------------------------------------------- +// Collada elements are very similar, but are arranged as separate, unrelated +// classes. These template functions are used to provide a simple way to access the +// common elements. +template static void resolveLightColor(T* light, ColorF& color) +{ + if (light->getColor()) + { + color.red = light->getColor()->getValue()[0]; + color.green = light->getColor()->getValue()[1]; + color.blue = light->getColor()->getValue()[2]; + } +} + +template static void resolveLightAttenuation(T* light, Point3F& attenuationRatio) +{ + if (light->getConstant_attenuation()) + attenuationRatio.x = light->getConstant_attenuation()->getValue(); + if (light->getLinear_attenuation()) + attenuationRatio.y = light->getLinear_attenuation()->getValue(); + if (light->getQuadratic_attenuation()) + attenuationRatio.z = light->getQuadratic_attenuation()->getValue(); +} + +//----------------------------------------------------------------------------- +// Recurse through the collada scene to add s to the Torque scene +static void processNodeLights(AppNode* appNode, const MatrixF& offset, SimGroup* group) +{ + const domNode* node = dynamic_cast(appNode)->getDomNode(); + + for (S32 iLight = 0; iLight < node->getInstance_light_array().getCount(); iLight++) { + + domInstance_light* instLight = node->getInstance_light_array()[iLight]; + domLight* p_domLight = daeSafeCast(instLight->getUrl().getElement()); + if (!p_domLight) { + Con::warnf("Failed to find light for URL \"%s\"", instLight->getUrl().getOriginalURI()); + continue; + } + + String lightName = Sim::getUniqueName(_GetNameOrId(node)); + const char* lightType = ""; + + domLight::domTechnique_common* technique = p_domLight->getTechnique_common(); + if (!technique) { + Con::warnf("No for light \"%s\"", lightName.c_str()); + continue; + } + + LightBase* pLight = 0; + ColorF color(ColorF::WHITE); + Point3F attenuation(0, 1, 1); + + if (technique->getAmbient()) { + domLight::domTechnique_common::domAmbient* ambient = technique->getAmbient(); + // No explicit support for ambient lights, so use a PointLight instead + lightType = "ambient"; + pLight = new PointLight; + resolveLightColor(ambient, color); + } + else if (technique->getDirectional()) { + domLight::domTechnique_common::domDirectional* directional = technique->getDirectional(); + // No explicit support for directional lights, so use a SpotLight instead + lightType = "directional"; + pLight = new SpotLight; + resolveLightColor(directional, color); + } + else if (technique->getPoint()) { + domLight::domTechnique_common::domPoint* point = technique->getPoint(); + lightType = "point"; + pLight = new PointLight; + resolveLightColor(point, color); + resolveLightAttenuation(point, attenuation); + } + else if (technique->getSpot()) { + domLight::domTechnique_common::domSpot* spot = technique->getSpot(); + lightType = "spot"; + pLight = new SpotLight; + resolveLightColor(spot, color); + resolveLightAttenuation(spot, attenuation); + } + else + continue; + + Con::printf("Adding <%s> light \"%s\" as a %s", lightType, lightName.c_str(), pLight->getClassName()); + + MatrixF mat(offset); + mat.mul(appNode->getNodeTransform(TSShapeLoader::DefaultTime)); + + pLight->setDataField(StringTable->insert("color"), 0, + avar("%f %f %f %f", color.red, color.green, color.blue, color.alpha)); + pLight->setDataField(StringTable->insert("attenuationRatio"), 0, + avar("%f %f %f", attenuation.x, attenuation.y, attenuation.z)); + pLight->setTransform(mat); + + if (!pLight->registerObject(lightName)) { + Con::errorf(ConsoleLogEntry::General, "Failed to register light for \"%s\"", lightName.c_str()); + delete pLight; + } + + if (group) + group->addObject(pLight); + } + + // Recurse child nodes + for (S32 iChild = 0; iChild < appNode->getNumChildNodes(); iChild++) + processNodeLights(appNode->getChildNode(iChild), offset, group); +} + +// Load lights from a collada file and add to the scene. +ConsoleFunction( loadColladaLights, bool, 2, 4, + "(string filename, SimGroup parentGroup=MissionGroup, SimObject baseObject=-1)" + "Load all light instances from a COLLADA (.dae) file and add to the scene.\n" + "@param filename COLLADA filename to load lights from\n" + "@param parentGroup (optional) name of an existing simgroup to add the new " + "lights to (defaults to MissionGroup)\n" + "@param baseObject (optional) name of an object to use as the origin (useful " + "if you are loading the lights for a collada scene and have moved or rotated " + "the geometry)\n" + "@return true if successful, false otherwise\n\n" + "@tsexample\n" + "// load the lights in room.dae\n" + "loadColladaLights( \"art/shapes/collada/room.dae\" );\n\n" + "// load the lights in room.dae and add them to the RoomLights group\n" + "loadColladaLights( \"art/shapes/collada/room.dae\", \"RoomLights\" );\n\n" + "// load the lights in room.dae and use the transform of the \"Room\"\n" + "object as the origin\n" + "loadColladaLights( \"art/shapes/collada/room.dae\", \"\", \"Room\" );\n" + "@endtsexample\n\n" + "@note Currently for editor use only\n" + "@ingroup Editors\n" + "@internal") +{ + Torque::Path path(argv[1]); + + // Optional group to add the lights to. Create if it does not exist, and use + // the MissionGroup if not specified. + SimGroup* missionGroup = dynamic_cast(Sim::findObject("MissionGroup")); + SimGroup* group = 0; + if ((argc > 2) && (argv[2][0])) { + if (!Sim::findObject(argv[2], group)) { + // Create the group if it could not be found + group = new SimGroup; + if (group->registerObject(argv[2])) { + if (missionGroup) + missionGroup->addObject(group); + } + else { + delete group; + group = 0; + } + } + } + if (!group) + group = missionGroup; + + // Optional object to provide the base transform + MatrixF offset(true); + if (argc > 3) { + SceneObject *obj; + if (Sim::findObject(argv[3], obj)) + offset = obj->getTransform(); + } + + // Load the Collada file into memory + domCOLLADA* root = ColladaShapeLoader::getDomCOLLADA(path); + if (!root) { + TSShapeLoader::updateProgress(TSShapeLoader::Load_Complete, "Load complete"); + return false; + } + + // Extract the global scale and up_axis from the top level element, + F32 unit = 1.0f; + domUpAxisType upAxis = UPAXISTYPE_Z_UP; + if (root->getAsset()) { + if (root->getAsset()->getUnit()) + unit = root->getAsset()->getUnit()->getMeter(); + if (root->getAsset()->getUp_axis()) + upAxis = root->getAsset()->getUp_axis()->getValue(); + } + + ColladaUtils::getOptions().unit = unit; + ColladaUtils::getOptions().upAxis = upAxis; + + // First grab all of the top-level nodes + Vector sceneNodes; + for (int iSceneLib = 0; iSceneLib < root->getLibrary_visual_scenes_array().getCount(); iSceneLib++) { + const domLibrary_visual_scenes* libScenes = root->getLibrary_visual_scenes_array()[iSceneLib]; + for (int iScene = 0; iScene < libScenes->getVisual_scene_array().getCount(); iScene++) { + const domVisual_scene* visualScene = libScenes->getVisual_scene_array()[iScene]; + for (int iNode = 0; iNode < visualScene->getNode_array().getCount(); iNode++) + sceneNodes.push_back(new ColladaAppNode(visualScene->getNode_array()[iNode])); + } + } + + // Recurse the scene tree looking for s + for (S32 iNode = 0; iNode < sceneNodes.size(); iNode++) { + processNodeLights(sceneNodes[iNode], offset, group); + delete sceneNodes[iNode]; + } + + TSShapeLoader::updateProgress(TSShapeLoader::Load_Complete, "Load complete"); + + return true; +} diff --git a/Engine/source/ts/collada/colladaShapeLoader.cpp b/Engine/source/ts/collada/colladaShapeLoader.cpp new file mode 100644 index 000000000..3822e9e01 --- /dev/null +++ b/Engine/source/ts/collada/colladaShapeLoader.cpp @@ -0,0 +1,725 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +/* + Resource stream -> Buffer + Buffer -> Collada DOM + Collada DOM -> TSShapeLoader + TSShapeLoader installed into TSShape +*/ + +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "ts/collada/colladaShapeLoader.h" +#include "ts/collada/colladaUtils.h" +#include "ts/collada/colladaAppNode.h" +#include "ts/collada/colladaAppMesh.h" +#include "ts/collada/colladaAppMaterial.h" +#include "ts/collada/colladaAppSequence.h" + +#include "core/util/tVector.h" +#include "core/strings/findMatch.h" +#include "core/stream/fileStream.h" +#include "core/fileObject.h" +#include "ts/tsShape.h" +#include "ts/tsShapeInstance.h" +#include "materials/materialManager.h" +#include "console/persistenceManager.h" +#include "ts/tsShapeConstruct.h" +#include "core/util/zip/zipVolume.h" +#include "gfx/bitmap/gBitmap.h" + +// +static DAE sDAE; // Collada model database (holds the last loaded file) +static Torque::Path sLastPath; // Path of the last loaded Collada file +static FileTime sLastModTime; // Modification time of the last loaded Collada file + +//----------------------------------------------------------------------------- +// Custom warning/error message handler +class myErrorHandler : public daeErrorHandler +{ + void handleError( daeString msg ) + { + Con::errorf("Error: %s", msg); + } + + void handleWarning( daeString msg ) + { + Con::errorf("Warning: %s", msg); + } +} sErrorHandler; + +//----------------------------------------------------------------------------- + +ColladaShapeLoader::ColladaShapeLoader(domCOLLADA* _root) + : root(_root) +{ + // Extract the global scale and up_axis from the top level element, + F32 unit = 1.0f; + domUpAxisType upAxis = UPAXISTYPE_Z_UP; + if (root->getAsset()) { + if (root->getAsset()->getUnit()) + unit = root->getAsset()->getUnit()->getMeter(); + if (root->getAsset()->getUp_axis()) + upAxis = root->getAsset()->getUp_axis()->getValue(); + } + + // Set import options (if they are not set to override) + if (ColladaUtils::getOptions().unit <= 0.0f) + ColladaUtils::getOptions().unit = unit; + + if (ColladaUtils::getOptions().upAxis == UPAXISTYPE_COUNT) + ColladaUtils::getOptions().upAxis = upAxis; +} + +ColladaShapeLoader::~ColladaShapeLoader() +{ + // Delete all of the animation channels + for (int iAnim = 0; iAnim < animations.size(); iAnim++) { + for (int iChannel = 0; iChannel < animations[iAnim]->size(); iChannel++) + delete (*animations[iAnim])[iChannel]; + delete animations[iAnim]; + } + animations.clear(); +} + +void ColladaShapeLoader::processAnimation(const domAnimation* anim, F32& maxEndTime, F32& minFrameTime) +{ + const char* sRGBANames[] = { ".R", ".G", ".B", ".A", "" }; + const char* sXYZNames[] = { ".X", ".Y", ".Z", "" }; + const char* sXYZANames[] = { ".X", ".Y", ".Z", ".ANGLE" }; + const char* sLOOKATNames[] = { ".POSITIONX", ".POSITIONY", ".POSITIONZ", ".TARGETX", ".TARGETY", ".TARGETZ", ".UPX", ".UPY", ".UPZ", "" }; + const char* sSKEWNames[] = { ".ROTATEX", ".ROTATEY", ".ROTATEZ", ".AROUNDX", ".AROUNDY", ".AROUNDZ", ".ANGLE", "" }; + const char* sNullNames[] = { "" }; + + for (int iChannel = 0; iChannel < anim->getChannel_array().getCount(); iChannel++) { + + // Get the animation elements: , + domChannel* channel = anim->getChannel_array()[iChannel]; + domSampler* sampler = daeSafeCast(channel->getSource().getElement()); + if (!sampler) + continue; + + // Find the animation channel target + daeSIDResolver resolver(channel, channel->getTarget()); + daeElement* target = resolver.getElement(); + if (!target) { + daeErrorHandler::get()->handleWarning(avar("Failed to resolve animation " + "target: %s", channel->getTarget())); + continue; + } +/* + // If the target is a , point it at the array instead + // @todo:Only support targeting float arrays for now... + if (target->getElementType() == COLLADA_TYPE::SOURCE) + { + domSource* source = daeSafeCast(target); + if (source->getFloat_array()) + target = source->getFloat_array(); + } +*/ + // Get the target's animation channels (create them if not already) + if (!AnimData::getAnimChannels(target)) { + animations.push_back(new AnimChannels(target)); + } + AnimChannels* targetChannels = AnimData::getAnimChannels(target); + + // Add a new animation channel to the target + targetChannels->push_back(new AnimData()); + channel->setUserData(targetChannels->last()); + AnimData& data = *targetChannels->last(); + + for (int iInput = 0; iInput < sampler->getInput_array().getCount(); iInput++) { + + const domInputLocal* input = sampler->getInput_array()[iInput]; + const domSource* source = daeSafeCast(input->getSource().getElement()); + if (!source) + continue; + + // @todo:don't care about the input param names for now. Could + // validate against the target type.... + if (dStrEqual(input->getSemantic(), "INPUT")) { + data.input.initFromSource(source); + // Adjust the maximum sequence end time + maxEndTime = getMax(maxEndTime, data.input.getFloatValue((S32)data.input.size()-1)); + + // Detect the frame rate (minimum time between keyframes) + for (S32 iFrame = 1; iFrame < data.input.size(); iFrame++) + { + F32 delta = data.input.getFloatValue( iFrame ) - data.input.getFloatValue( iFrame-1 ); + if ( delta < 0 ) + { + daeErrorHandler::get()->handleError(avar(" INPUT '%s' " + "has non-monotonic keys. Animation is unlikely to be imported correctly.", source->getID())); + break; + } + minFrameTime = getMin( minFrameTime, delta ); + } + } + else if (dStrEqual(input->getSemantic(), "OUTPUT")) + data.output.initFromSource(source); + else if (dStrEqual(input->getSemantic(), "IN_TANGENT")) + data.inTangent.initFromSource(source); + else if (dStrEqual(input->getSemantic(), "OUT_TANGENT")) + data.outTangent.initFromSource(source); + else if (dStrEqual(input->getSemantic(), "INTERPOLATION")) + data.interpolation.initFromSource(source); + } + + // Set initial value for visibility targets that were added automatically (in colladaUtils.cpp + if (dStrEqual(target->getElementName(), "visibility")) + { + domAny* visTarget = daeSafeCast(target); + if (visTarget && dStrEqual(visTarget->getValue(), "")) + visTarget->setValue(avar("%g", data.output.getFloatValue(0))); + } + + // Ignore empty animations + if (data.input.size() == 0) { + channel->setUserData(0); + delete targetChannels->last(); + targetChannels->pop_back(); + continue; + } + + // Determine the number and offset the elements of the target value + // targeted by this animation + switch (target->getElementType()) { + case COLLADA_TYPE::COLOR: data.parseTargetString(channel->getTarget(), 4, sRGBANames); break; + case COLLADA_TYPE::TRANSLATE: data.parseTargetString(channel->getTarget(), 3, sXYZNames); break; + case COLLADA_TYPE::ROTATE: data.parseTargetString(channel->getTarget(), 4, sXYZANames); break; + case COLLADA_TYPE::SCALE: data.parseTargetString(channel->getTarget(), 3, sXYZNames); break; + case COLLADA_TYPE::LOOKAT: data.parseTargetString(channel->getTarget(), 3, sLOOKATNames); break; + case COLLADA_TYPE::SKEW: data.parseTargetString(channel->getTarget(), 3, sSKEWNames); break; + case COLLADA_TYPE::MATRIX: data.parseTargetString(channel->getTarget(), 16, sNullNames); break; + case COLLADA_TYPE::FLOAT_ARRAY: data.parseTargetString(channel->getTarget(), daeSafeCast(target)->getCount(), sNullNames); break; + default: data.parseTargetString(channel->getTarget(), 1, sNullNames); break; + } + } + + // Process child animations + for (int iAnim = 0; iAnim < anim->getAnimation_array().getCount(); iAnim++) + processAnimation(anim->getAnimation_array()[iAnim], maxEndTime, minFrameTime); +} + +void ColladaShapeLoader::enumerateScene() +{ + // Get animation clips + Vector animationClips; + for (int iClipLib = 0; iClipLib < root->getLibrary_animation_clips_array().getCount(); iClipLib++) { + const domLibrary_animation_clips* libraryClips = root->getLibrary_animation_clips_array()[iClipLib]; + for (int iClip = 0; iClip < libraryClips->getAnimation_clip_array().getCount(); iClip++) + appSequences.push_back(new ColladaAppSequence(libraryClips->getAnimation_clip_array()[iClip])); + } + + // Process all animations => this attaches animation channels to the targeted + // Collada elements, and determines the length of the sequence if it is not + // already specified in the Collada element + for (int iSeq = 0; iSeq < appSequences.size(); iSeq++) { + ColladaAppSequence* appSeq = dynamic_cast(appSequences[iSeq]); + F32 maxEndTime = 0; + F32 minFrameTime = 1000.0f; + for (int iAnim = 0; iAnim < appSeq->getClip()->getInstance_animation_array().getCount(); iAnim++) { + domAnimation* anim = daeSafeCast(appSeq->getClip()->getInstance_animation_array()[iAnim]->getUrl().getElement()); + if (anim) + processAnimation(anim, maxEndTime, minFrameTime); + } + if (appSeq->getEnd() == 0) + appSeq->setEnd(maxEndTime); + + // Collada animations can be stored as sampled frames or true keyframes. For + // sampled frames, use the same frame rate as the DAE file. For true keyframes, + // resample at a fixed frame rate. + appSeq->fps = mClamp(1.0f / minFrameTime + 0.5f, TSShapeLoader::MinFrameRate, TSShapeLoader::MaxFrameRate); + } + + // First grab all of the top-level nodes + Vector sceneNodes; + for (int iSceneLib = 0; iSceneLib < root->getLibrary_visual_scenes_array().getCount(); iSceneLib++) { + const domLibrary_visual_scenes* libScenes = root->getLibrary_visual_scenes_array()[iSceneLib]; + for (int iScene = 0; iScene < libScenes->getVisual_scene_array().getCount(); iScene++) { + const domVisual_scene* visualScene = libScenes->getVisual_scene_array()[iScene]; + for (int iNode = 0; iNode < visualScene->getNode_array().getCount(); iNode++) + sceneNodes.push_back(visualScene->getNode_array()[iNode]); + } + } + + // Set LOD option + bool singleDetail = true; + switch (ColladaUtils::getOptions().lodType) + { + case ColladaUtils::ImportOptions::DetectDTS: + // Check for a baseXX->startXX hierarchy at the top-level, if we find + // one, use trailing numbers for LOD, otherwise use a single size + for (int iNode = 0; singleDetail && (iNode < sceneNodes.size()); iNode++) { + domNode* node = sceneNodes[iNode]; + if (dStrStartsWith(_GetNameOrId(node), "base")) { + for (int iChild = 0; iChild < node->getNode_array().getCount(); iChild++) { + domNode* child = node->getNode_array()[iChild]; + if (dStrStartsWith(_GetNameOrId(child), "start")) { + singleDetail = false; + break; + } + } + } + } + break; + + case ColladaUtils::ImportOptions::SingleSize: + singleDetail = true; + break; + + case ColladaUtils::ImportOptions::TrailingNumber: + singleDetail = false; + break; + + default: + break; + } + + ColladaAppMesh::fixDetailSize( singleDetail, ColladaUtils::getOptions().singleDetailSize ); + + // Process the top level nodes + for (S32 iNode = 0; iNode < sceneNodes.size(); iNode++) { + ColladaAppNode* node = new ColladaAppNode(sceneNodes[iNode], 0); + if (!processNode(node)) + delete node; + } + + // Make sure that the scene has a bounds node (for getting the root scene transform) + if (!boundsNode) + { + domVisual_scene* visualScene = root->getLibrary_visual_scenes_array()[0]->getVisual_scene_array()[0]; + domNode* dombounds = daeSafeCast( visualScene->createAndPlace( "node" ) ); + dombounds->setName( "bounds" ); + ColladaAppNode *appBounds = new ColladaAppNode(dombounds, 0); + if (!processNode(appBounds)) + delete appBounds; + } +} + +bool ColladaShapeLoader::ignoreNode(const String& name) +{ + if (FindMatch::isMatchMultipleExprs(ColladaUtils::getOptions().alwaysImport, name, false)) + return false; + else + return FindMatch::isMatchMultipleExprs(ColladaUtils::getOptions().neverImport, name, false); +} + +bool ColladaShapeLoader::ignoreMesh(const String& name) +{ + if (FindMatch::isMatchMultipleExprs(ColladaUtils::getOptions().alwaysImportMesh, name, false)) + return false; + else + return FindMatch::isMatchMultipleExprs(ColladaUtils::getOptions().neverImportMesh, name, false); +} + +void ColladaShapeLoader::computeBounds(Box3F& bounds) +{ + TSShapeLoader::computeBounds(bounds); + + // Check if the model origin needs adjusting + if ( bounds.isValidBox() && + (ColladaUtils::getOptions().adjustCenter || + ColladaUtils::getOptions().adjustFloor) ) + { + // Compute shape offset + Point3F shapeOffset = Point3F::Zero; + if ( ColladaUtils::getOptions().adjustCenter ) + { + bounds.getCenter( &shapeOffset ); + shapeOffset = -shapeOffset; + } + if ( ColladaUtils::getOptions().adjustFloor ) + shapeOffset.z = -bounds.minExtents.z; + + // Adjust bounds + bounds.minExtents += shapeOffset; + bounds.maxExtents += shapeOffset; + + // Now adjust all positions for root level nodes (nodes with no parent) + for (S32 iNode = 0; iNode < shape->nodes.size(); iNode++) + { + if ( !appNodes[iNode]->isParentRoot() ) + continue; + + // Adjust default translation + shape->defaultTranslations[iNode] += shapeOffset; + + // Adjust animated translations + for (S32 iSeq = 0; iSeq < shape->sequences.size(); iSeq++) + { + const TSShape::Sequence& seq = shape->sequences[iSeq]; + if ( seq.translationMatters.test(iNode) ) + { + for (S32 iFrame = 0; iFrame < seq.numKeyframes; iFrame++) + { + S32 index = seq.baseTranslation + seq.translationMatters.count(iNode)*seq.numKeyframes + iFrame; + shape->nodeTranslations[index] += shapeOffset; + } + } + } + } + } +} + +//----------------------------------------------------------------------------- +/// Find the file extension for an extensionless texture +String findTextureExtension(const Torque::Path &texPath) +{ + Torque::Path path(texPath); + for(S32 i = 0;i < GBitmap::sRegistrations.size();++i) + { + GBitmap::Registration ® = GBitmap::sRegistrations[i]; + for(S32 j = 0;j < reg.extensions.size();++j) + { + path.setExtension(reg.extensions[j]); + if (Torque::FS::IsFile(path)) + return path.getExtension(); + } + } + + return String(); +} + +//----------------------------------------------------------------------------- +/// Copy a texture from a KMZ to a cache. Note that the texture filename is modified +void copySketchupTexture(const Torque::Path &path, String &textureFilename) +{ + if (textureFilename.isEmpty()) + return; + + Torque::Path texturePath(textureFilename); + texturePath.setExtension(findTextureExtension(texturePath)); + + String cachedTexFilename = String::ToString("%s_%s.cached", + TSShapeLoader::getShapePath().getFileName().c_str(), texturePath.getFileName().c_str()); + + Torque::Path cachedTexPath; + cachedTexPath.setRoot(path.getRoot()); + cachedTexPath.setPath(path.getPath()); + cachedTexPath.setFileName(cachedTexFilename); + cachedTexPath.setExtension(texturePath.getExtension()); + + FileStream *source; + FileStream *dest; + if ((source = FileStream::createAndOpen(texturePath.getFullPath(), Torque::FS::File::Read)) == NULL) + return; + + if ((dest = FileStream::createAndOpen(cachedTexPath.getFullPath(), Torque::FS::File::Write)) == NULL) + { + delete source; + return; + } + + dest->copyFrom(source); + + delete dest; + delete source; + + // Update the filename in the material + cachedTexPath.setExtension(""); + textureFilename = cachedTexPath.getFullPath(); +} + +//----------------------------------------------------------------------------- +/// Add collada materials to materials.cs +void updateMaterialsScript(const Torque::Path &path, bool copyTextures = false) +{ +#ifdef DAE2DTS_TOOL + if (!ColladaUtils::getOptions().forceUpdateMaterials) + return; +#endif + + Torque::Path scriptPath(path); + scriptPath.setFileName("materials"); + scriptPath.setExtension("cs"); + + // First see what materials we need to update + PersistenceManager persistMgr; + for ( U32 iMat = 0; iMat < AppMesh::appMaterials.size(); iMat++ ) + { + ColladaAppMaterial *mat = dynamic_cast( AppMesh::appMaterials[iMat] ); + if ( mat ) + { + Material *mappedMat; + if ( Sim::findObject( MATMGR->getMapEntry( mat->getName() ), mappedMat ) ) + { + // Only update existing materials if forced to + if ( ColladaUtils::getOptions().forceUpdateMaterials ) + persistMgr.setDirty( mappedMat ); + } + else + { + // Create a new material definition + persistMgr.setDirty( mat->createMaterial( scriptPath ), scriptPath.getFullPath() ); + } + } + } + + if ( persistMgr.getDirtyList().empty() ) + return; + + // If importing a sketchup file, the paths will point inside the KMZ so we need to cache them. + if (copyTextures) + { + for (int iMat = 0; iMat < persistMgr.getDirtyList().size(); iMat++) + { + Material *mat = dynamic_cast( persistMgr.getDirtyList()[iMat].getObject() ); + + copySketchupTexture(path, mat->mDiffuseMapFilename[0]); + copySketchupTexture(path, mat->mNormalMapFilename[0]); + copySketchupTexture(path, mat->mSpecularMapFilename[0]); + } + } + + persistMgr.saveDirty(); +} + +//----------------------------------------------------------------------------- +/// Check if an up-to-date cached DTS is available for this DAE file +bool ColladaShapeLoader::canLoadCachedDTS(const Torque::Path& path) +{ + // Generate the cached filename + Torque::Path cachedPath(path); + cachedPath.setExtension("cached.dts"); + + // Check if a cached DTS newer than this file is available + FileTime cachedModifyTime; + if (Platform::getFileTimes(cachedPath.getFullPath(), NULL, &cachedModifyTime)) + { + bool forceLoadDAE = Con::getBoolVariable("$collada::forceLoadDAE", false); + + FileTime daeModifyTime; + if (!Platform::getFileTimes(path.getFullPath(), NULL, &daeModifyTime) || + (!forceLoadDAE && (Platform::compareFileTimes(cachedModifyTime, daeModifyTime) >= 0) )) + { + // DAE not found, or cached DTS is newer + return true; + } + } + + return false; +} + +bool ColladaShapeLoader::checkAndMountSketchup(const Torque::Path& path, String& mountPoint, Torque::Path& daePath) +{ + bool isSketchup = path.getExtension().equal("kmz", String::NoCase); + if (isSketchup) + { + // Mount the zip so files can be found (it will be unmounted before we return) + mountPoint = String("sketchup_") + path.getFileName(); + String zipPath = path.getFullPath(); + if (!Torque::FS::Mount(mountPoint, new Torque::ZipFileSystem(zipPath))) + return false; + + Vector daeFiles; + Torque::Path findPath; + findPath.setRoot(mountPoint); + S32 results = Torque::FS::FindByPattern(findPath, "*.dae", true, daeFiles); + if (results == 0 || daeFiles.size() == 0) + { + Torque::FS::Unmount(mountPoint); + return false; + } + + daePath = daeFiles[0]; + } + else + { + daePath = path; + } + + return isSketchup; +} + +//----------------------------------------------------------------------------- +/// Get the root collada DOM element for the given DAE file +domCOLLADA* ColladaShapeLoader::getDomCOLLADA(const Torque::Path& path) +{ + daeErrorHandler::setErrorHandler(&sErrorHandler); + + TSShapeLoader::updateProgress(TSShapeLoader::Load_ReadFile, path.getFullFileName().c_str()); + + // Check if we can use the last loaded file + FileTime daeModifyTime; + if (Platform::getFileTimes(path.getFullPath(), NULL, &daeModifyTime)) + { + if ((path == sLastPath) && (Platform::compareFileTimes(sLastModTime, daeModifyTime) >= 0)) + return sDAE.getRoot(path.getFullPath().c_str()); + } + + sDAE.clear(); + sDAE.setBaseURI(""); + + TSShapeLoader::updateProgress(TSShapeLoader::Load_ParseFile, "Parsing XML..."); + domCOLLADA* root = readColladaFile(path.getFullPath()); + if (!root) + { + TSShapeLoader::updateProgress(TSShapeLoader::Load_Complete, "Import failed"); + sDAE.clear(); + return NULL; + } + + sLastPath = path; + sLastModTime = daeModifyTime; + + return root; +} + +domCOLLADA* ColladaShapeLoader::readColladaFile(const String& path) +{ + // Check if this file is already loaded into the database + domCOLLADA* root = sDAE.getRoot(path.c_str()); + if (root) + return root; + + // Load the Collada file into memory + FileObject fo; + if (!fo.readMemory(path)) + { + daeErrorHandler::get()->handleError(avar("Could not read %s into memory", path.c_str())); + return NULL; + } + + root = sDAE.openFromMemory(path.c_str(), (const char*)fo.buffer()); + if (!root || !root->getLibrary_visual_scenes_array().getCount()) { + daeErrorHandler::get()->handleError(avar("Could not parse %s", path.c_str())); + return NULL; + } + + // Fixup issues in the model + ColladaUtils::applyConditioners(root); + + // Recursively load external DAE references + TSShapeLoader::updateProgress(TSShapeLoader::Load_ExternalRefs, "Loading external references..."); + for (S32 iRef = 0; iRef < root->getDocument()->getReferencedDocuments().getCount(); iRef++) { + String refPath = (daeString)root->getDocument()->getReferencedDocuments()[iRef]; + if (refPath.endsWith(".dae") && !readColladaFile(refPath)) + daeErrorHandler::get()->handleError(avar("Failed to load external reference: %s", refPath.c_str())); + } + return root; +} + +//----------------------------------------------------------------------------- +/// This function is invoked by the resource manager based on file extension. +TSShape* loadColladaShape(const Torque::Path &path) +{ +#ifndef DAE2DTS_TOOL + // Generate the cached filename + Torque::Path cachedPath(path); + cachedPath.setExtension("cached.dts"); + + // Check if an up-to-date cached DTS version of this file exists, and + // if so, use that instead. + if (ColladaShapeLoader::canLoadCachedDTS(path)) + { + FileStream cachedStream; + cachedStream.open(cachedPath.getFullPath(), Torque::FS::File::Read); + if (cachedStream.getStatus() == Stream::Ok) + { + TSShape *shape = new TSShape; + bool readSuccess = shape->read(&cachedStream); + cachedStream.close(); + + if (readSuccess) + { + #ifdef TORQUE_DEBUG + Con::printf("Loaded cached Collada shape from %s", cachedPath.getFullPath().c_str()); + #endif + return shape; + } + else + delete shape; + } + + Con::warnf("Failed to load cached COLLADA shape from %s", cachedPath.getFullPath().c_str()); + } +#endif // DAE2DTS_TOOL + + if (!Torque::FS::IsFile(path)) + { + // DAE file does not exist, bail. + return NULL; + } + +#ifdef DAE2DTS_TOOL + ColladaUtils::ImportOptions cmdLineOptions = ColladaUtils::getOptions(); +#endif + + // Allow TSShapeConstructor object to override properties + ColladaUtils::getOptions().reset(); + TSShapeConstructor* tscon = TSShapeConstructor::findShapeConstructor(path.getFullPath()); + if (tscon) + { + ColladaUtils::getOptions() = tscon->mOptions; + +#ifdef DAE2DTS_TOOL + // Command line overrides certain options + ColladaUtils::getOptions().forceUpdateMaterials = cmdLineOptions.forceUpdateMaterials; + ColladaUtils::getOptions().useDiffuseNames = cmdLineOptions.useDiffuseNames; +#endif + } + + // Check if this is a Sketchup file (.kmz) and if so, mount the zip filesystem + // and get the path to the DAE file. + String mountPoint; + Torque::Path daePath; + bool isSketchup = ColladaShapeLoader::checkAndMountSketchup(path, mountPoint, daePath); + + // Load Collada model and convert to 3space + TSShape* tss = 0; + domCOLLADA* root = ColladaShapeLoader::getDomCOLLADA(daePath); + if (root) + { + ColladaShapeLoader loader(root); + tss = loader.generateShape(daePath); + if (tss) + { +#ifndef DAE2DTS_TOOL + // Cache the Collada model to a DTS file for faster loading next time. + FileStream dtsStream; + if (dtsStream.open(cachedPath.getFullPath(), Torque::FS::File::Write)) + { + Con::printf("Writing cached COLLADA shape to %s", cachedPath.getFullPath().c_str()); + tss->write(&dtsStream); + } +#endif // DAE2DTS_TOOL + + // Add collada materials to materials.cs + updateMaterialsScript(path, isSketchup); + } + } + + // Close progress dialog + TSShapeLoader::updateProgress(TSShapeLoader::Load_Complete, "Import complete"); + + if (isSketchup) + { + // Unmount the zip if we mounted it + Torque::FS::Unmount(mountPoint); + } + + return tss; +} diff --git a/Engine/source/ts/collada/colladaShapeLoader.h b/Engine/source/ts/collada/colladaShapeLoader.h new file mode 100644 index 000000000..b5cfd3198 --- /dev/null +++ b/Engine/source/ts/collada/colladaShapeLoader.h @@ -0,0 +1,61 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _COLLADA_SHAPELOADER_H_ +#define _COLLADA_SHAPELOADER_H_ + +#ifndef _TSSHAPELOADER_H_ +#include "ts/loader/tsShapeLoader.h" +#endif + +class domCOLLADA; +class domAnimation; +struct AnimChannels; + +//----------------------------------------------------------------------------- +class ColladaShapeLoader : public TSShapeLoader +{ + friend TSShape* loadColladaShape(const Torque::Path &path); + + domCOLLADA* root; + Vector animations; ///< Holds all animation channels for deletion after loading + + void processAnimation(const domAnimation* anim, F32& maxEndTime, F32& minFrameTime); + + void cleanup(); + +public: + ColladaShapeLoader(domCOLLADA* _root); + ~ColladaShapeLoader(); + + void enumerateScene(); + bool ignoreNode(const String& name); + bool ignoreMesh(const String& name); + void computeBounds(Box3F& bounds); + + static bool canLoadCachedDTS(const Torque::Path& path); + static bool checkAndMountSketchup(const Torque::Path& path, String& mountPoint, Torque::Path& daePath); + static domCOLLADA* getDomCOLLADA(const Torque::Path& path); + static domCOLLADA* readColladaFile(const String& path); +}; + +#endif // _COLLADA_SHAPELOADER_H_ diff --git a/Engine/source/ts/collada/colladaUtils.cpp b/Engine/source/ts/collada/colladaUtils.cpp new file mode 100644 index 000000000..c816f2e98 --- /dev/null +++ b/Engine/source/ts/collada/colladaUtils.cpp @@ -0,0 +1,1495 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include +#include "console/console.h" +#include "gfx/bitmap/gBitmap.h" +#include "ts/collada/colladaUtils.h" +#include "materials/matInstance.h" + +using namespace ColladaUtils; + +#define MAX_PATH_LENGTH 256 + +// Helper macro to create Collada elements +#define CREATE_ELEMENT(container, name, type) \ + type* name = daeSafeCast(container->createAndPlace(#name)); + +ColladaUtils::ImportOptions& ColladaUtils::getOptions() +{ + static ImportOptions options; + return options; +} + +//------------------------------------------------------------------------------ +// Utility functions + +// Convert a transform from the Collada model coordinate system to the DTS coordinate +// system +void ColladaUtils::convertTransform(MatrixF& mat) +{ + MatrixF rot(true); + + switch (ColladaUtils::getOptions().upAxis) + { + case UPAXISTYPE_X_UP: + // rotate 90 around Y-axis, then 90 around Z-axis + rot(0,0) = 0.0f; rot(1,0) = 1.0f; + rot(1,1) = 0.0f; rot(2,1) = 1.0f; + rot(0,2) = 1.0f; rot(2,2) = 0.0f; + + // pre-multiply the transform by the rotation matrix + mat.mulL(rot); + break; + + case UPAXISTYPE_Y_UP: + // rotate 180 around Y-axis, then 90 around X-axis + rot(0,0) = -1.0f; + rot(1,1) = 0.0f; rot(2,1) = 1.0f; + rot(1,2) = 1.0f; rot(2,2) = 0.0f; + + // pre-multiply the transform by the rotation matrix + mat.mulL(rot); + break; + + case UPAXISTYPE_Z_UP: + default: + // nothing to do + break; + } +} + +/// Find the COMMON profile element in an effect +const domProfile_COMMON* ColladaUtils::findEffectCommonProfile(const domEffect* effect) +{ + if (effect) { + // Find the COMMON profile + const domFx_profile_abstract_Array& profiles = effect->getFx_profile_abstract_array(); + for (int iProfile = 0; iProfile < profiles.getCount(); iProfile++) { + if (profiles[iProfile]->getElementType() == COLLADA_TYPE::PROFILE_COMMON) + return daeSafeCast(profiles[iProfile]); + } + } + return NULL; +} + +/// Find the element in the COMMON profile of an effect +const domCommon_color_or_texture_type_complexType* ColladaUtils::findEffectDiffuse(const domEffect* effect) +{ + const domProfile_COMMON* profile = findEffectCommonProfile(effect); + if (profile) { + + if (profile->getTechnique()->getLambert()) + return profile->getTechnique()->getLambert()->getDiffuse(); + else if (profile->getTechnique()->getPhong()) + return profile->getTechnique()->getPhong()->getDiffuse(); + else if (profile->getTechnique()->getBlinn()) + return profile->getTechnique()->getBlinn()->getDiffuse(); + } + + return NULL; +} + +/// Find the element in the COMMON profile of an effect +const domCommon_color_or_texture_type_complexType* ColladaUtils::findEffectSpecular(const domEffect* effect) +{ + const domProfile_COMMON* profile = findEffectCommonProfile(effect); + if (profile) { + + if (profile->getTechnique()->getLambert()) + return NULL; // no element for Lambert shader + else if (profile->getTechnique()->getPhong()) + return profile->getTechnique()->getPhong()->getSpecular(); + else if (profile->getTechnique()->getBlinn()) + return profile->getTechnique()->getBlinn()->getSpecular(); + } + + return NULL; +} + +const domFx_sampler2D_common_complexType* ColladaUtils::getTextureSampler(const domEffect* effect, + const domCommon_color_or_texture_type_complexType* texture) +{ + // .. + if (texture) { + const domCommon_color_or_texture_type_complexType::domTexture* domTex = texture->getTexture(); + if (domTex && domTex->getTexture()) { + daeSIDResolver resolver(const_cast(effect), domTex->getTexture()); + const domCommon_newparam_type* param = daeSafeCast(resolver.getElement()); + if (param) + return param->getSampler2D(); + } + } + + return NULL; +} + +String ColladaUtils::getSamplerImagePath(const domEffect* effect, + const domFx_sampler2D_common_complexType* sampler2D) +{ + // ...... + const domProfile_COMMON* profile = findEffectCommonProfile(effect); + if (profile && sampler2D && sampler2D->getSource()) { + + // Resolve the SID to get the param + daeSIDResolver resolver(const_cast(profile), sampler2D->getSource()->getValue()); + domCommon_newparam_type* surfaceParam = daeSafeCast(resolver.getElement()); + + // Get the surface element + if (surfaceParam && surfaceParam->getSurface()) { + + const domFx_surface_init_common* surfaceInit = surfaceParam->getSurface()->getFx_surface_init_common(); + if (surfaceInit && surfaceInit->getInit_from_array().getCount()) { + // Resolve the ID to get the , then read the texture path + xsIDREF& idRef = surfaceInit->getInit_from_array()[0]->getValue(); + const domImage* image = daeSafeCast(idRef.getElement()); + if (image && image->getInit_from()) + return resolveImagePath(image); + } + } + } + + return ""; +} + +// Resolve image path into something we can use. +String ColladaUtils::resolveImagePath(const domImage* image) +{ + // 1. If the URI string contains an absolute path, use it if + // it is inside the Torque folder, otherwise force textures + // to be in the same folder as the shape. + // 2. If the URI string contains a relative path, append it + // to the shape path (since materials.cs cannot handle + // relative paths). + + Torque::Path imagePath; + String imageStr(image->getInit_from()->getValue().originalStr().c_str()); + + // Trim leading "file://" + if (imageStr.compare("file://", 7) == 0) + imageStr.erase(0, 7); + + // Trim leading slash from absolute windows paths. eg. /D:/ + if ((imageStr.compare("/", 1) == 0) && (imageStr.find(':') == 2)) + imageStr.erase(0, 1); + + // Replace %20 with space + imageStr.replace("%20", " "); + + if (Platform::isFullPath(imageStr)) + { + // Absolute path => check for outside the Torque game folder + imagePath = String( Platform::makeRelativePathName(imageStr, Platform::getMainDotCsDir()) ); + if ( !imagePath.getRoot().isEmpty() || // different drive (eg. C:/ vs D:/) + (imagePath.getPath().find("/") == 0) || // different OS (eg. /home vs C:/home) + (imagePath.getPath().find("../") == 0) ) // same drive, outside Torque game folder + { + // Force these to the shape folder + imagePath.setRoot(""); + imagePath.setPath(""); + } + } + else + { + // Relative path => prepend with shape path + Torque::Path tempPath(imageStr); + imagePath = TSShapeLoader::getShapePath(); + imagePath.appendPath(tempPath); + imagePath.setFileName(tempPath.getFileName()); + } + + // No need to specify the path if it is in the same folder as the model + if (imagePath.getPath() == TSShapeLoader::getShapePath().getPath()) + imagePath.setPath(""); + + // Don't care about the extension + imagePath.setExtension(""); + + return imagePath.getFullPath(); +} + +//----------------------------------------------------------------------------- +// Construct the appropriate child class +BasePrimitive* BasePrimitive::get(const daeElement* element) +{ + switch (element->getElementType()) { + case COLLADA_TYPE::TRIANGLES: return new ColladaPrimitive(element); + case COLLADA_TYPE::TRISTRIPS: return new ColladaPrimitive(element); + case COLLADA_TYPE::TRIFANS: return new ColladaPrimitive(element); + case COLLADA_TYPE::POLYGONS: return new ColladaPrimitive(element); + case COLLADA_TYPE::POLYLIST: return new ColladaPrimitive(element); + default: return 0; + } +} + +//------------------------------------------------------------------------------ +// Collada animation curves + +/// Determine which elements are being targeted +void AnimData::parseTargetString(const char* target, int fullCount, const char* elements[]) +{ + // Assume targeting all elements at offset 0 + targetValueCount = fullCount; + targetValueOffset = 0; + + // Check for array syntax: (n) or (n)(m) + if (const char* p = dStrchr(target, '(')) { + int indN, indM; + if (dSscanf(p, "(%d)(%d)", &indN, &indM) == 2) { + targetValueOffset = (indN * 4) + indM; // @todo: 4x4 matrix only + targetValueCount = 1; + } + else if (dSscanf(p, "(%d)", &indN) == 1) { + targetValueOffset = indN; + targetValueCount = 1; + } + } + else if (const char* p = dStrrchr(target, '.')) { + // Check for named elements + for (int iElem = 0; elements[iElem][0] != 0; iElem++) { + if (!dStrcmp(p, elements[iElem])) { + targetValueOffset = iElem; + targetValueCount = 1; + break; + } + } + } +} + +/// Solve the cubic spline B(s) = param for s +F32 AnimData::invertParamCubic(F32 param, F32 x0, F32 x1, F32 x2, F32 x3) const +{ + const double INVERTPARAMCUBIC_TOL = 1.0e-09; + const double INVERTPARAMCUBIC_SMALLERTOL = 1.0e-20; + const double INVERTPARAMCUBIC_MAXIT = 100; + + // check input value for outside range + if ((param - x0) < INVERTPARAMCUBIC_SMALLERTOL) + return 0.0f; + else if ((x3 - param) < INVERTPARAMCUBIC_SMALLERTOL) + return 1.0f; + + U32 iterations = 0; + + // de Casteljau Subdivision. + F32 u = 0.0f; + F32 v = 1.0f; + + while (iterations < INVERTPARAMCUBIC_MAXIT) { + double a = (x0 + x1)*0.5f; + double b = (x1 + x2)*0.5f; + double c = (x2 + x3)*0.5f; + double d = (a + b)*0.5f; + double e = (b + c)*0.5f; + double f = (d + e)*0.5f; + + if (mFabs(f - param) < INVERTPARAMCUBIC_TOL) + break; + + if (f < param) { + x0 = f; + x1 = e; + x2 = c; + u = (u + v)*0.5f; + } + else { + x1 = a; + x2 = d; + x3 = f; + v = (u + v)*0.5f; + } + iterations++; + } + + return mClampF((u+v)*0.5f, 0.0f, 1.0f); +} + +/// Get the interpolated value at time 't' +void AnimData::interpValue(F32 t, U32 offset, double* value) const +{ + // handle degenerate animation data + if (input.size() == 0) + { + *value = 0.0f; + return; + } + else if (input.size() == 1) + { + *value = output.getStringArrayData(0)[offset]; + return; + } + + // clamp time to valid range + F32 curveStart = input.getFloatValue(0); + F32 curveEnd = input.getFloatValue(input.size()-1); + t = mClampF(t, curveStart, curveEnd); + + // find the index of the input keyframe BEFORE 't' + int index; + for (index = 0; index < input.size()-2; index++) { + if (input.getFloatValue(index + 1) > t) + break; + } + + // get the data for the two control points either side of 't' + Point2F v0; + v0.x = input.getFloatValue(index); + v0.y = output.getStringArrayData(index)[offset]; + + Point2F v3; + v3.x = input.getFloatValue(index + 1); + v3.y = output.getStringArrayData(index + 1)[offset]; + + // If spline interpolation is specified but the tangents are not available, + // default to LINEAR. + const char* interp_method = interpolation.getStringValue(index); + if (dStrEqual(interp_method, "BEZIER") || + dStrEqual(interp_method, "HERMITE") || + dStrEqual(interp_method, "CARDINAL")) { + + const double* inArray = inTangent.getStringArrayData(index + 1); + const double* outArray = outTangent.getStringArrayData(index); + if (!inArray || !outArray) + interp_method = "LINEAR"; + } + + if (dStrEqual(interp_method, "STEP")) { + // STEP interpolation + *value = v0.y; + } + else if (dStrEqual(interp_method, "BEZIER") || + dStrEqual(interp_method, "HERMITE") || + dStrEqual(interp_method, "CARDINAL") || + dStrEqual(interp_method, "BSPLINE")) + { + // Cubic spline interpolation. The only difference between the 4 supported + // forms is in the calculation of the other 2 control points: + // BEZIER: control points are specified explicitly + // HERMITE: tangents are specified, need to offset to get the control points + // CARDINAL: (baked) tangents are specified, need to offset to get the control points + // BSPLINE: control points are based on previous and next points + + // Get the 2 extra control points + Point2F v1, v2; + + if (dStrEqual(interp_method, "BSPLINE")) { + // v0 and v3 are the center points => need to + // get the control points before and after them + v1 = v0; + v2 = v3; + + if (index > 0) { + v0.x = input.getFloatValue(index-1); + v0.y = output.getStringArrayData(index-1)[offset]; + } + else { + // mirror P1 through P0 + v0 = v1 + (v1 - v2); + } + + if (index < (input.size()-2)) { + v3.x = input.getFloatValue(index+2); + v3.y = output.getStringArrayData(index+2)[offset]; + } + else { + // mirror P0 through P1 + v3 = v2 + (v2 - v1); + } + } + else { + const double* inArray = inTangent.getStringArrayData(index + 1); + const double* outArray = outTangent.getStringArrayData(index); + + if (output.stride() == inTangent.stride()) { + // This degenerate form (1D control points) does 2 things wrong: + // 1) it does not specify the key (time) value + // 2) the control point is specified as a tangent for both bezier and hermite + // => interpolate to get the key values, and offset the tangent values + v1.set((v0.x*2 + v3.x)/3, v0.y + outArray[offset]); + v2.set((v0.x + v3.x*2)/3, v3.y - inArray[offset]); + } + else { + // the expected form (2D control points) + v1.set(outArray[offset*2], outArray[offset*2+1]); + v2.set(inArray[offset*2], inArray[offset*2+1]); + + // if this is a hermite or cardinal spline, treat the values as tangents + if (dStrEqual(interp_method, "HERMITE") || dStrEqual(interp_method, "CARDINAL")) { + v1.set(v0.x + v1.x, v3.y - v1.y); + v2.set(v0.x + v2.x, v3.x - v2.y); + } + } + } + + // find 's' that gives the desired 't' value + F32 s = invertParamCubic(t, v0.x, v1.x, v2.x, v3.x); + + // Calculate the output value using Bernstein evaluation and the + // computed 's' value + F32 c = 3.0f*(v1.y - v0.y); + F32 e = 3.0f*(v2.y - v1.y); + *value = (((v3.y - v0.y - e)*s + e - c)*s + c)*s + v0.y; + } + else { + // default to LINEAR interpolation + F32 s = mClampF((t - v0.x) / (v3.x - v0.x), 0.0f, 1.0f); + *value = v0.y + (v3.y - v0.y) * s; + } +} + +void AnimData::interpValue(F32 t, U32 offset, const char** value) const +{ + if (input.size() == 0) + *value = ""; + else if (input.size() == 1) + *value = output.getStringValue(0); + else + { + // clamp time to valid range + F32 curveStart = input.getFloatValue(0); + F32 curveEnd = input.getFloatValue(input.size()-1); + t = mClampF(t, curveStart, curveEnd); + + // find the index of the input keyframe BEFORE 't' + int index; + for (index = 0; index < input.size()-2; index++) { + if (input.getFloatValue(index + 1) > t) + break; + } + + // String values only support STEP interpolation, so just get the + // value at the input keyframe + *value = output.getStringValue(index); + } +} + +//------------------------------------------------------------------------------ +// Collada document conditioners + +static void conditioner_fixupTextureSIDs(domCOLLADA* root) +{ + for (int iLib = 0; iLib < root->getLibrary_effects_array().getCount(); iLib++) { + domLibrary_effects* lib = root->getLibrary_effects_array()[iLib]; + for (int iEffect = 0; iEffect < lib->getEffect_array().getCount(); iEffect++) { + domEffect* effect = lib->getEffect_array()[iEffect]; + const domCommon_color_or_texture_type_complexType* diffuse = findEffectDiffuse(effect); + if (!diffuse || !diffuse->getTexture()) + continue; + + // Resolve the SID => if it is an , add and + // elements to conform to the Collada spec. + const char *image_sid = diffuse->getTexture()->getTexture(); + daeSIDResolver resolver(effect, image_sid); + if (!daeSafeCast(resolver.getElement())) + continue; + + daeErrorHandler::get()->handleWarning(avar("Fixup %s . " + "pointing at instead of ", effect->getID())); + + // Generate SIDs for the new sampler2D and surface elements + std::string sampler_sid(std::string(image_sid) + "-sampler"); + std::string surface_sid(std::string(image_sid) + "-surface"); + + domProfile_COMMON* profile = const_cast(findEffectCommonProfile(effect)); + + // Create .. + { + CREATE_ELEMENT(profile, newparam, domCommon_newparam_type) + CREATE_ELEMENT(newparam, sampler2D, domFx_sampler2D_common) + CREATE_ELEMENT(sampler2D, source, domFx_sampler2D_common_complexType::domSource) + + newparam->setSid(sampler_sid.c_str()); + source->setValue(surface_sid.c_str()); + } + + // Create .. + { + CREATE_ELEMENT(profile, newparam, domCommon_newparam_type) + CREATE_ELEMENT(newparam, surface, domFx_surface_common) + CREATE_ELEMENT(surface, init_from, domFx_surface_init_from_common) + CREATE_ELEMENT(surface, format, domFx_surface_common_complexType::domFormat) + + newparam->setSid(surface_sid.c_str()); + surface->setType(FX_SURFACE_TYPE_ENUM_2D); + format->setValue("A8R8G8B8"); + init_from->setValue(image_sid); + } + + // Store sampler2D sid in the . "texture" attribute + diffuse->getTexture()->setTexture(sampler_sid.c_str()); + } + } +} + +static void conditioner_fixupImageURIs(domCOLLADA* root) +{ + for (int iLib = 0; iLib < root->getLibrary_images_array().getCount(); iLib++) { + domLibrary_images* lib = root->getLibrary_images_array()[iLib]; + for (int iImage = 0; iImage < lib->getImage_array().getCount(); iImage++) { + domImage* image = lib->getImage_array()[iImage]; + if (image->getInit_from()) { + xsAnyURI& uri = image->getInit_from()->getValue(); + + // Replace '\' with '/' + if (uri.originalStr().find("\\") != std::string::npos) { + daeErrorHandler::get()->handleWarning(avar("Fixup invalid URI " + "in %s: \"%s\"", image->getID(), uri.originalStr().c_str())); + + std::string str(uri.originalStr()); + std::replace(str.begin(), str.end(), '\\', '/'); + uri.set(str); + } + + // Detect file://texture.jpg => this is an invalid URI and will + // not be parsed correctly + if (uri.scheme() == "file" && + uri.pathFile().empty() && + !uri.authority().empty()) { + daeErrorHandler::get()->handleWarning(avar("Fixup invalid URI " + "in %s: \"%s\"", image->getID(), uri.originalStr().c_str())); + + uri.set(uri.authority()); + } + } + } + } +} + +static void conditioner_fixupTransparency(domCOLLADA* root) +{ + // Transparency is another example of something simple made complicated by + // Collada. There are two (optional) elements that determine transparency: + // + // : a color + // : a percentage applied to the color values + // + // Additionally, has an optional "opaque" attribute that changes + // the way transparency is determined. If set to A_ONE (the default), only the + // alpha value of the transparent color is used, and a value of "1" means fully + // opaque. If set to RGB_ZERO, only the RGB values of transparent are used, and + // a value of "0" means fully opaque. + // + // To further complicate matters, Google Sketchup (all versions) and FeelingSoftware + // ColladaMax (pre 3.03) export materials with the transparency element inverted + // (1-transparency) + + // Get the string + const char *authoringTool = ""; + if (const domAsset* asset = root->getAsset()) { + for (int iContrib = 0; iContrib < asset->getContributor_array().getCount(); iContrib++) { + const domAsset::domContributor* contrib = asset->getContributor_array()[iContrib]; + if (contrib->getAuthoring_tool()) { + authoringTool = contrib->getAuthoring_tool()->getValue(); + break; + } + } + } + + // Check for a match with the known problem-tools + bool invertTransparency = false; + const char *toolNames[] = { "FBX COLLADA exporter", "Google SketchUp", + "Illusoft Collada Exporter", "FCollada" }; + for (int iName = 0; iName < (sizeof(toolNames)/sizeof(toolNames[0])); iName++) { + if (dStrstr(authoringTool, toolNames[iName])) { + invertTransparency = true; + break; + } + } + + if (!invertTransparency) + return; + + // Invert transparency as required for each effect + for (int iLib = 0; iLib < root->getLibrary_effects_array().getCount(); iLib++) { + domLibrary_effects* lib = root->getLibrary_effects_array()[iLib]; + for (int iEffect = 0; iEffect < lib->getEffect_array().getCount(); iEffect++) { + domEffect* effect = lib->getEffect_array()[iEffect]; + + // Find the common profile + const domProfile_COMMON* commonProfile = findEffectCommonProfile(effect); + if (!commonProfile) + continue; + + domCommon_transparent_type* transparent = 0; + if (commonProfile->getTechnique()->getConstant()) + transparent = commonProfile->getTechnique()->getConstant()->getTransparent(); + else if (commonProfile->getTechnique()->getLambert()) + transparent = commonProfile->getTechnique()->getLambert()->getTransparent(); + else if (commonProfile->getTechnique()->getPhong()) + transparent = commonProfile->getTechnique()->getPhong()->getTransparent(); + else if (commonProfile->getTechnique()->getBlinn()) + transparent = commonProfile->getTechnique()->getBlinn()->getTransparent(); + + if (!transparent) + continue; + + // If the shader "opaque" attribute is not specified, set it to + // RGB_ZERO (the opposite of the Collada default), as this is what + // the bad exporter tools seem to assume. + if (!transparent->isAttributeSet("opaque")) { + + daeErrorHandler::get()->handleWarning(avar("Setting " + "\"opaque\" attribute to RGB_ZERO for %s ", effect->getID())); + + transparent->setOpaque(FX_OPAQUE_ENUM_RGB_ZERO); + } + } + } +} + +static void conditioner_checkBindShapeMatrix(domCOLLADA* root) +{ + for (int iLib = 0; iLib < root->getLibrary_controllers_array().getCount(); iLib++) { + domLibrary_controllers* lib = root->getLibrary_controllers_array().get(iLib); + for (int iCon = 0; iCon < lib->getController_array().getCount(); iCon++) { + domController* con = lib->getController_array().get(iCon); + if (con->getSkin() && con->getSkin()->getBind_shape_matrix()) { + + MatrixF mat = vecToMatrixF(con->getSkin()->getBind_shape_matrix()->getValue()); + if (!mat.fullInverse()) { + daeErrorHandler::get()->handleWarning(avar(" " + "in %s is not invertible (may cause problems with " + "skinning)", con->getID())); + } + } + } + } +} + +static void conditioner_fixupVertexWeightJoints(domCOLLADA* root) +{ + for (int iLib = 0; iLib < root->getLibrary_controllers_array().getCount(); iLib++) { + domLibrary_controllers* lib = root->getLibrary_controllers_array().get(iLib); + for (int iCon = 0; iCon < lib->getController_array().getCount(); iCon++) { + domController* con = lib->getController_array().get(iCon); + if (con->getSkin() && con->getSkin()->getVertex_weights()) + { + domInputLocalOffset_Array& vw_inputs = con->getSkin()->getVertex_weights()->getInput_array(); + for (int vInput = 0; vInput < vw_inputs.getCount(); vInput++) { + + domInputLocalOffset *vw_input = vw_inputs.get(vInput); + if (dStrEqual(vw_input->getSemantic(), "JOINT")) { + + // Check if this input points at a float array (bad) + domSource* vw_source = daeSafeCast(vw_input->getSource().getElement()); + if (vw_source->getFloat_array()) { + + // Copy the value from the JOINTS input instead + domInputLocal_Array& joint_inputs = con->getSkin()->getJoints()->getInput_array(); + for (int jInput = 0; jInput < joint_inputs.getCount(); jInput++) { + + domInputLocal *joint_input = joint_inputs.get(jInput); + if (dStrEqual(joint_input->getSemantic(), "JOINT")) { + vw_input->setSource(joint_input->getSource()); + break; + } + } + } + } + } + } + } + } +} + +static void conditioner_createDefaultClip(domCOLLADA* root) +{ + // Check if the document has any s + for (int iLib = 0; iLib < root->getLibrary_animation_clips_array().getCount(); iLib++) { + if (root->getLibrary_animation_clips_array()[iLib]->getAnimation_clip_array().getCount()) + return; + } + + // Get all top-level s into an array + domAnimation_Array animations; + for (int iAnimLib = 0; iAnimLib < root->getLibrary_animations_array().getCount(); iAnimLib++) { + const domLibrary_animations* libraryAnims = root->getLibrary_animations_array()[iAnimLib]; + for (int iAnim = 0; iAnim < libraryAnims->getAnimation_array().getCount(); iAnim++) + animations.append(libraryAnims->getAnimation_array()[iAnim]); + } + + if (!animations.getCount()) + return; + + daeErrorHandler::get()->handleWarning("Creating cyclic animation clip to " + "hold all animations"); + + // Get animation_clip library (create one if necessary) + if (!root->getLibrary_animation_clips_array().getCount()) { + root->createAndPlace("library_animation_clips"); + } + domLibrary_animation_clips* libraryClips = root->getLibrary_animation_clips_array()[0]; + + // Create new animation_clip for the default sequence + CREATE_ELEMENT(libraryClips, animation_clip, domAnimation_clip) + animation_clip->setName("ambient"); + animation_clip->setId("dummy_ambient_clip"); + animation_clip->setStart(0); + animation_clip->setEnd(0); + + // Add all top_level animations to the clip (sub-animations will be included + // when the clip is procesed) + for (int iAnim = 0; iAnim < animations.getCount(); iAnim++) { + if (!animations[iAnim]->getId()) + animations[iAnim]->setId(avar("dummy-animation-id%d", iAnim)); + CREATE_ELEMENT(animation_clip, instance_animation, domInstanceWithExtra) + std::string url(std::string("#") + animations[iAnim]->getId()); + instance_animation->setUrl(url.c_str()); + } + + // Add the 'Torque' profile to specify the 'Cyclic' flag + CREATE_ELEMENT(animation_clip, extra, domExtra) + CREATE_ELEMENT(extra, technique, domTechnique) + CREATE_ELEMENT(technique, any, domAny) + technique->setProfile("Torque"); + any->setElementName("cyclic"); + any->setValue("1"); +} + +static void conditioner_fixupAnimation(domAnimation* anim) +{ + for (int iChannel = 0; iChannel < anim->getChannel_array().getCount(); iChannel++) { + + // Get the animation elements: , + domChannel* channel = anim->getChannel_array()[iChannel]; + domSampler* sampler = daeSafeCast(channel->getSource().getElement()); + if (!sampler) + continue; +/* + // If using a spline interpolation type but no tangents are specified, + // fall back to LINEAR interpolation. + bool isSpline = false; + bool foundInTangent = false; + bool foundOutTangent = false; + for (int iInput = 0; iInput < sampler->getInput_array().getCount(); iInput++) { + const char *semantic = sampler->getInput_array()[iInput]->getSemantic(); + if (dStrEqual(semantic, "INTERPOLATION")) { + if ( + } + if (dStrEqual(semantic, "IN_TANGENT")) + foundInTangent = true; + if (dStrEqual(semantic, "OUT_TANGENT")) + foundOutTangent = true; + } + + if (isSpline && (!foundInTangent || !foundOutTangent)) { + daeErrorHandler::get()->handleWarning(avar("%s type interpolation " + "specified for %s, but IN/OUT TANGENTS are not provided. Using " + "LINEAR interpolation instead."); + + } +*/ + + // Find the animation channel target + daeSIDResolver resolver(channel, channel->getTarget()); + daeElement* target = resolver.getElement(); + if (!target) { + + // Some exporters generate visibility animations but don't add the + // FCOLLADA extension, so the target doesn't actually exist! Detect + // this situation and add the extension manually so the animation + // still works. + if (dStrEndsWith(channel->getTarget(), "/visibility")) { + + // Get parent SID string + char *parentSID = dStrdup(channel->getTarget()); + parentSID[dStrlen(parentSID) - dStrlen("/visibility")] = '\0'; + + // Find the parent element (should be a ) + daeSIDResolver parentResolver(channel, parentSID); + daeElement* parent = parentResolver.getElement(); + delete [] parentSID; + + if (parent && (parent->getElementType() == COLLADA_TYPE::NODE)) { + + // Create the FCOLLADA extension + daeErrorHandler::get()->handleWarning(avar("Creating missing " + "visibility animation target: %s", channel->getTarget())); + + // Check if the element exists but is missing the SID + daeElement* vis = parent->getDescendant("visibility"); + if (vis) + { + vis->setAttribute("sid", "visibility"); + } + else + { + CREATE_ELEMENT(parent, extra, domExtra) + CREATE_ELEMENT(extra, technique, domTechnique) + CREATE_ELEMENT(technique, any, domAny) + + technique->setProfile("FCOLLADA"); + any->setElementName("visibility"); + any->setAttribute("sid", "visibility"); + any->setValue(""); // real initial value will be set when animation is processed in ColladaShapeLoader::processAnimation + } + } + } + } + } + + // Process child animations + for (int iAnim = 0; iAnim < anim->getAnimation_array().getCount(); iAnim++) + conditioner_fixupAnimation(anim->getAnimation_array()[iAnim]); +} + +/// Apply the set of model conditioners +void ColladaUtils::applyConditioners(domCOLLADA* root) +{ + //-------------------------------------------------------------------------- + // The built-in MAX FBX exporter specifies an SID in the + // "texture" attribute instead of a SID. Detect and fix this. + conditioner_fixupTextureSIDs(root); + + //-------------------------------------------------------------------------- + // The built-in MAX FBX exporter also generates invalid URI paths in the + // . tag, so fix that up too. + conditioner_fixupImageURIs(root); + + //-------------------------------------------------------------------------- + // Many exporters get transparency backwards. Check if the model was exported + // by one with a known issue and correct it. + conditioner_fixupTransparency(root); + + //-------------------------------------------------------------------------- + // Some exporters (AutoDesk) generate invalid bind_shape matrices. Warn if + // the bind_shape_matrix is not invertible. + conditioner_checkBindShapeMatrix(root); + + //-------------------------------------------------------------------------- + // The PoserPro exporter points the JOINT input to the + // inverse bind matrices instead of the joint names array. Detect and fix it. + conditioner_fixupVertexWeightJoints(root); + + //-------------------------------------------------------------------------- + // If the model contains s but no s, just put all + // top level animations into a single clip. + conditioner_createDefaultClip(root); + + //-------------------------------------------------------------------------- + // Apply some animation fixups: + // 1) Some exporters (eg. Blender) generate "BEZIER" type animation curves, + // but do not specify the IN and OUT tangent data arrays. Detect this and + // fall back to LINEAR interpolation. + // 2) Some exporters generate visibility animations but don't add the FCOLLADA + // extension, so the target doesn't actually exist! Detect this situation + // and add the extension manually so the animation still works. + for (int iLib = 0; iLib < root->getLibrary_animations_array().getCount(); iLib++) { + const domLibrary_animations* lib = root->getLibrary_animations_array()[iLib]; + for (int iAnim = 0; iAnim < lib->getAnimation_array().getCount(); iAnim++) + conditioner_fixupAnimation(lib->getAnimation_array()[iAnim]); + } +} + +Torque::Path ColladaUtils::findTexture(const Torque::Path& diffuseMap) +{ + Vector foundPaths; + + GBitmap::sFindFiles(diffuseMap, &foundPaths); + + if (foundPaths.size() > 0) + return Torque::Path(foundPaths[0]); + + // If unable to load texture in current directory + // look in the parent directory. But never look in the root. + Torque::Path newPath(diffuseMap); + + String filePath = newPath.getPath(); + + String::SizeType slash = filePath.find('/', filePath.length(), String::Right); + + if (slash != String::NPos) + { + slash = filePath.find('/', filePath.length(), String::Right); + + if (slash != String::NPos) + { + String truncPath = filePath.substr(0, slash); + newPath.setPath(truncPath); + + return findTexture(newPath); + } + } + + return String::EmptyString; +} + +void ColladaUtils::exportColladaHeader(TiXmlElement* rootNode) +{ + TiXmlElement* assetNode = new TiXmlElement("asset"); + rootNode->LinkEndChild(assetNode); + + TiXmlElement* contributorNode = new TiXmlElement("contributor"); + assetNode->LinkEndChild(contributorNode); + + TiXmlElement* authorNode = new TiXmlElement("author"); + contributorNode->LinkEndChild(authorNode); + + TiXmlElement* authoringToolNode = new TiXmlElement("authoring_tool"); + contributorNode->LinkEndChild(authoringToolNode); + TiXmlText* authorText = new TiXmlText(avar("%s %s Interior Exporter", getEngineProductString(), getVersionString())); + authoringToolNode->LinkEndChild(authorText); + + TiXmlElement* commentsNode = new TiXmlElement("comments"); + contributorNode->LinkEndChild(commentsNode); + + // Get the current time + Platform::LocalTime lt; + Platform::getLocalTime(lt); + String localTime = Platform::localTimeToString(lt); + + localTime.replace('\t', ' '); + + TiXmlElement* createdNode = new TiXmlElement("created"); + assetNode->LinkEndChild(createdNode); + TiXmlText* createdText = new TiXmlText(avar("%s", localTime.c_str())); + createdNode->LinkEndChild(createdText); + + TiXmlElement* modifiedNode = new TiXmlElement("modified"); + assetNode->LinkEndChild(modifiedNode); + TiXmlText* modifiedText = new TiXmlText(avar("%s", localTime.c_str())); + modifiedNode->LinkEndChild(modifiedText); + + TiXmlElement* revisionNode = new TiXmlElement("revision"); + assetNode->LinkEndChild(revisionNode); + + TiXmlElement* titleNode = new TiXmlElement("title"); + assetNode->LinkEndChild(titleNode); + + TiXmlElement* subjectNode = new TiXmlElement("subject"); + assetNode->LinkEndChild(subjectNode); + + TiXmlElement* keywordsNode = new TiXmlElement("keywords"); + assetNode->LinkEndChild(keywordsNode); + + // Torque uses Z_UP with 1 unit equal to 1 meter by default + TiXmlElement* unitNode = new TiXmlElement("unit"); + assetNode->LinkEndChild(unitNode); + unitNode->SetAttribute("meter", "1.000000"); + + TiXmlElement* axisNode = new TiXmlElement("up_axis"); + assetNode->LinkEndChild(axisNode); + TiXmlText* axisText = new TiXmlText("Z_UP"); + axisNode->LinkEndChild(axisText); +} + +void ColladaUtils::exportColladaMaterials(TiXmlElement* rootNode, const OptimizedPolyList& mesh, Vector& matNames, const Torque::Path& colladaFile) +{ + // First the image library + TiXmlElement* imgLibNode = new TiXmlElement("library_images"); + rootNode->LinkEndChild(imgLibNode); + + for (U32 i = 0; i < mesh.mMaterialList.size(); i++) + { + BaseMatInstance* baseInst = mesh.mMaterialList[i]; + + matNames.push_back(String::ToString("Material%d", i)); + + Material* mat = dynamic_cast(baseInst->getMaterial()); + if (!mat) + continue; + + String diffuseMap; + + if (mat->getName() && mat->getName()[0]) + matNames.last() = String(mat->getName()); + + // Handle an auto-generated "Default Material" specially + if (mat->isAutoGenerated()) + { + Torque::Path diffusePath; + + if (mat->mDiffuseMapFilename[0].isNotEmpty()) + diffusePath = mat->mDiffuseMapFilename[0]; + else + diffusePath = String("warningMat"); + + matNames.last() = diffusePath.getFileName(); + diffuseMap += diffusePath.getFullFileName(); + } + else + { + if (mat->mDiffuseMapFilename[0].isNotEmpty()) + diffuseMap += mat->mDiffuseMapFilename[0]; + else + diffuseMap += "warningMat"; + } + + Torque::Path diffusePath = findTexture(colladaFile.getPath() + "/" + diffuseMap); + + // If we didn't get a path + if (diffusePath.getFullPath().isNotEmpty()) + diffuseMap = Torque::Path::MakeRelativePath(diffusePath, colladaFile); + + TiXmlElement* imageNode = new TiXmlElement("image"); + imgLibNode->LinkEndChild(imageNode); + imageNode->SetAttribute("id", avar("%s-Diffuse", matNames.last().c_str())); + imageNode->SetAttribute("name", avar("%s-Diffuse", matNames.last().c_str())); + + TiXmlElement* initNode = new TiXmlElement("init_from"); + imageNode->LinkEndChild(initNode); + TiXmlText* initText = new TiXmlText(avar("file://%s", diffuseMap.c_str())); + initNode->LinkEndChild(initText); + } + + // Next the material library + TiXmlElement* matLibNode = new TiXmlElement("library_materials"); + rootNode->LinkEndChild(matLibNode); + + for (U32 i = 0; i < mesh.mMaterialList.size(); i++) + { + BaseMatInstance* baseInst = mesh.mMaterialList[i]; + + Material* mat = dynamic_cast(baseInst->getMaterial()); + if (!mat) + continue; + + TiXmlElement* materialNode = new TiXmlElement("material"); + matLibNode->LinkEndChild(materialNode); + materialNode->SetAttribute("id", matNames[i].c_str()); + materialNode->SetAttribute("name", matNames[i].c_str()); + + TiXmlElement* instEffectNode = new TiXmlElement("instance_effect"); + materialNode->LinkEndChild(instEffectNode); + instEffectNode->SetAttribute("url", avar("#%s-fx", matNames[i].c_str())); + } + + // Finally the effects library + TiXmlElement* effectLibNode = new TiXmlElement("library_effects"); + rootNode->LinkEndChild(effectLibNode); + + for (U32 i = 0; i < mesh.mMaterialList.size(); i++) + { + BaseMatInstance* baseInst = mesh.mMaterialList[i]; + + Material* mat = dynamic_cast(baseInst->getMaterial()); + if (!mat) + continue; + + TiXmlElement* effectNode = new TiXmlElement("effect"); + effectLibNode->LinkEndChild(effectNode); + effectNode->SetAttribute("id", avar("%s-fx", matNames[i].c_str())); + effectNode->SetAttribute("name", avar("%s-fx", matNames[i].c_str())); + + TiXmlElement* profileNode = new TiXmlElement("profile_COMMON"); + effectNode->LinkEndChild(profileNode); + + TiXmlElement* techniqueNode = new TiXmlElement("technique"); + profileNode->LinkEndChild(techniqueNode); + techniqueNode->SetAttribute("sid", "standard"); + + TiXmlElement* phongNode = new TiXmlElement("phong"); + techniqueNode->LinkEndChild(phongNode); + + TiXmlElement* diffuseNode = new TiXmlElement("diffuse"); + phongNode->LinkEndChild(diffuseNode); + + TiXmlElement* textureNode = new TiXmlElement("texture"); + diffuseNode->LinkEndChild(textureNode); + textureNode->SetAttribute("texture", avar("%s-Diffuse", matNames[i].c_str())); + textureNode->SetAttribute("texcoord", "CHANNEL0"); + + // Extra info useful for getting the texture to show up correctly in some apps + TiXmlElement* extraNode = new TiXmlElement("extra"); + textureNode->LinkEndChild(extraNode); + + TiXmlElement* extraTechNode = new TiXmlElement("technique"); + extraNode->LinkEndChild(extraTechNode); + extraTechNode->SetAttribute("profile", "MAYA"); + + TiXmlElement* extraWrapUNode = new TiXmlElement("wrapU"); + extraTechNode->LinkEndChild(extraWrapUNode); + extraWrapUNode->SetAttribute("sid", "wrapU0"); + + TiXmlText* extraWrapUText = new TiXmlText("TRUE"); + extraWrapUNode->LinkEndChild(extraWrapUText); + + TiXmlElement* extraWrapVNode = new TiXmlElement("wrapV"); + extraTechNode->LinkEndChild(extraWrapVNode); + extraWrapVNode->SetAttribute("sid", "wrapV0"); + + TiXmlText* extraWrapVText = new TiXmlText("TRUE"); + extraWrapVNode->LinkEndChild(extraWrapVText); + + TiXmlElement* extraBlendNode = new TiXmlElement("blend_mode"); + extraTechNode->LinkEndChild(extraBlendNode); + + TiXmlText* extraBlendText = new TiXmlText("ADD"); + extraBlendNode->LinkEndChild(extraBlendText); + } +} + +void ColladaUtils::exportColladaTriangles(TiXmlElement* meshNode, const OptimizedPolyList& mesh, const String& meshName, const Vector& matNames) +{ + // Start at -1 so we will export polygons that do not have a material. + for (S32 i = -1; i < matNames.size(); i++) + { + // Calculate the number of triangles that uses this Material + U32 triangleCount = 0; + + for (U32 j = 0; j < mesh.mPolyList.size(); j++) + { + const OptimizedPolyList::Poly& poly = mesh.mPolyList[j]; + + if (poly.material != i) + continue; + + if (poly.vertexCount < 3) + continue; + + if (poly.type == OptimizedPolyList::TriangleList || + poly.type == OptimizedPolyList::TriangleFan || + poly.type == OptimizedPolyList::TriangleStrip) + { + triangleCount += poly.vertexCount - 2; + } + else + AssertISV(false, "ColladaUtils::exportColladaTriangles(): Unknown Poly type!"); + } + + // Make sure that we are actually using this Material + if (triangleCount == 0) + continue; + + TiXmlElement* trianglesNode = new TiXmlElement("triangles"); + meshNode->LinkEndChild(trianglesNode); + trianglesNode->SetAttribute("material", ( i > -1 ) ? matNames[i].c_str() : "" ); + trianglesNode->SetAttribute("count", avar("%d", triangleCount)); + + TiXmlElement* trianglesVertInputNode = new TiXmlElement("input"); + trianglesNode->LinkEndChild(trianglesVertInputNode); + trianglesVertInputNode->SetAttribute("semantic", "VERTEX"); + trianglesVertInputNode->SetAttribute("offset", "0"); + trianglesVertInputNode->SetAttribute("source", avar("#%s-Vertex", meshName.c_str())); + + TiXmlElement* trianglesNormalInputNode = new TiXmlElement("input"); + trianglesNode->LinkEndChild(trianglesNormalInputNode); + trianglesNormalInputNode->SetAttribute("semantic", "NORMAL"); + trianglesNormalInputNode->SetAttribute("offset", "1"); + trianglesNormalInputNode->SetAttribute("source", avar("#%s-Normal", meshName.c_str())); + + TiXmlElement* trianglesUV0InputNode = new TiXmlElement("input"); + trianglesNode->LinkEndChild(trianglesUV0InputNode); + trianglesUV0InputNode->SetAttribute("semantic", "TEXCOORD"); + trianglesUV0InputNode->SetAttribute("offset", "2"); + trianglesUV0InputNode->SetAttribute("set", "0"); + trianglesUV0InputNode->SetAttribute("source", avar("#%s-UV0", meshName.c_str())); + + TiXmlElement* polyNode = new TiXmlElement("p"); + trianglesNode->LinkEndChild(polyNode); + + Vector tempIndices; + tempIndices.reserve(4); + + for (U32 j = 0; j < mesh.mPolyList.size(); j++) + { + const OptimizedPolyList::Poly& poly = mesh.mPolyList[j]; + + if (poly.vertexCount < 3) + continue; + + if (poly.material != i) + continue; + + tempIndices.setSize(poly.vertexCount); + dMemset(tempIndices.address(), 0, poly.vertexCount); + + if (poly.type == OptimizedPolyList::TriangleStrip) + { + tempIndices[0] = 0; + U32 idx = 1; + + for (U32 k = 1; k < poly.vertexCount; k += 2) + tempIndices[idx++] = k; + + for (U32 k = ((poly.vertexCount - 1) & (~0x1)); k > 0; k -= 2) + tempIndices[idx++] = k; + } + else if (poly.type == OptimizedPolyList::TriangleList || + poly.type == OptimizedPolyList::TriangleFan) + { + for (U32 k = 0; k < poly.vertexCount; k++) + tempIndices[k] = k; + } + else + AssertISV(false, "ColladaUtils::exportColladaTriangles(): Unknown Poly type!"); + + const U32& firstIdx = mesh.mIndexList[poly.vertexStart]; + const OptimizedPolyList::VertIndex& firstVertIdx = mesh.mVertexList[firstIdx]; + + for (U32 k = 1; k < poly.vertexCount - 1; k++) + { + const U32& secondIdx = mesh.mIndexList[poly.vertexStart + tempIndices[k]]; + const U32& thirdIdx = mesh.mIndexList[poly.vertexStart + tempIndices[k + 1]]; + + const OptimizedPolyList::VertIndex& secondVertIdx = mesh.mVertexList[secondIdx]; + const OptimizedPolyList::VertIndex& thirdVertIdx = mesh.mVertexList[thirdIdx]; + + // Note the reversed winding on the triangles + const char* tri = avar("%d %d %d %d %d %d %d %d %d", + thirdVertIdx.vertIdx, thirdVertIdx.normalIdx, thirdVertIdx.uv0Idx, + secondVertIdx.vertIdx, secondVertIdx.normalIdx, secondVertIdx.uv0Idx, + firstVertIdx.vertIdx, firstVertIdx.normalIdx, firstVertIdx.uv0Idx); + + TiXmlText* triangleText = new TiXmlText(tri); + polyNode->LinkEndChild(triangleText); + } + } + } +} + +void ColladaUtils::exportColladaMesh(TiXmlElement* rootNode, const OptimizedPolyList& mesh, const String& meshName, const Vector& matNames) +{ + TiXmlElement* libGeomsNode = new TiXmlElement("library_geometries"); + rootNode->LinkEndChild(libGeomsNode); + + TiXmlElement* geometryNode = new TiXmlElement("geometry"); + libGeomsNode->LinkEndChild(geometryNode); + geometryNode->SetAttribute("id", avar("%s-lib", meshName.c_str())); + geometryNode->SetAttribute("name", avar("%sMesh", meshName.c_str())); + + TiXmlElement* meshNode = new TiXmlElement("mesh"); + geometryNode->LinkEndChild(meshNode); + + // Save out the vertices + TiXmlElement* vertsSourceNode = new TiXmlElement("source"); + meshNode->LinkEndChild(vertsSourceNode); + vertsSourceNode->SetAttribute("id", avar("%s-Position", meshName.c_str())); + + TiXmlElement* vertsNode = new TiXmlElement("float_array"); + vertsSourceNode->LinkEndChild(vertsNode); + vertsNode->SetAttribute("id", avar("%s-Position-array", meshName.c_str())); + vertsNode->SetAttribute("count", avar("%d", mesh.mPoints.size() * 3)); + + for (U32 i = 0; i < mesh.mPoints.size(); i++) + { + const Point3F& vert = mesh.mPoints[i]; + + TiXmlText* vertText = new TiXmlText(avar("%.4f %.4f %.4f", vert.x, vert.y, vert.z)); + vertsNode->LinkEndChild(vertText); + } + + // Save the vertex accessor + TiXmlElement* vertsTechNode = new TiXmlElement("technique_common"); + vertsSourceNode->LinkEndChild(vertsTechNode); + + TiXmlElement* vertsAccNode = new TiXmlElement("accessor"); + vertsTechNode->LinkEndChild(vertsAccNode); + vertsAccNode->SetAttribute("source", avar("#%s-Position-array", meshName.c_str())); + vertsAccNode->SetAttribute("count", avar("%d", mesh.mPoints.size())); + vertsAccNode->SetAttribute("stride", "3"); + + TiXmlElement* vertsAccXNode = new TiXmlElement("param"); + vertsAccNode->LinkEndChild(vertsAccXNode); + vertsAccXNode->SetAttribute("name", "X"); + vertsAccXNode->SetAttribute("type", "float"); + + TiXmlElement* vertsAccYNode = new TiXmlElement("param"); + vertsAccNode->LinkEndChild(vertsAccYNode); + vertsAccYNode->SetAttribute("name", "Y"); + vertsAccYNode->SetAttribute("type", "float"); + + TiXmlElement* vertsAccZNode = new TiXmlElement("param"); + vertsAccNode->LinkEndChild(vertsAccZNode); + vertsAccZNode->SetAttribute("name", "Z"); + vertsAccZNode->SetAttribute("type", "float"); + + // Save out the normals + TiXmlElement* normalsSourceNode = new TiXmlElement("source"); + meshNode->LinkEndChild(normalsSourceNode); + normalsSourceNode->SetAttribute("id", avar("%s-Normal", meshName.c_str())); + + TiXmlElement* normalsNode = new TiXmlElement("float_array"); + normalsSourceNode->LinkEndChild(normalsNode); + normalsNode->SetAttribute("id", avar("%s-Normal-array", meshName.c_str())); + normalsNode->SetAttribute("count", avar("%d", mesh.mNormals.size() * 3)); + + for (U32 i = 0; i < mesh.mNormals.size(); i++) + { + const Point3F& normal = mesh.mNormals[i]; + + TiXmlText* normalText = new TiXmlText(avar("%.4f %.4f %.4f", normal.x, normal.y, normal.z)); + normalsNode->LinkEndChild(normalText); + } + + // Save the normals accessor + TiXmlElement* normalsTechNode = new TiXmlElement("technique_common"); + normalsSourceNode->LinkEndChild(normalsTechNode); + + TiXmlElement* normalsAccNode = new TiXmlElement("accessor"); + normalsTechNode->LinkEndChild(normalsAccNode); + normalsAccNode->SetAttribute("source", avar("#%s-Normal-array", meshName.c_str())); + normalsAccNode->SetAttribute("count", avar("%d", mesh.mNormals.size())); + normalsAccNode->SetAttribute("stride", "3"); + + TiXmlElement* normalsAccXNode = new TiXmlElement("param"); + normalsAccNode->LinkEndChild(normalsAccXNode); + normalsAccXNode->SetAttribute("name", "X"); + normalsAccXNode->SetAttribute("type", "float"); + + TiXmlElement* normalsAccYNode = new TiXmlElement("param"); + normalsAccNode->LinkEndChild(normalsAccYNode); + normalsAccYNode->SetAttribute("name", "Y"); + normalsAccYNode->SetAttribute("type", "float"); + + TiXmlElement* normalsAccZNode = new TiXmlElement("param"); + normalsAccNode->LinkEndChild(normalsAccZNode); + normalsAccZNode->SetAttribute("name", "Z"); + normalsAccZNode->SetAttribute("type", "float"); + + // Save out the uvs + TiXmlElement* uv0SourceNode = new TiXmlElement("source"); + meshNode->LinkEndChild(uv0SourceNode); + uv0SourceNode->SetAttribute("id", avar("%s-UV0", meshName.c_str())); + + TiXmlElement* uv0Node = new TiXmlElement("float_array"); + uv0SourceNode->LinkEndChild(uv0Node); + uv0Node->SetAttribute("id", avar("%s-UV0-array", meshName.c_str())); + uv0Node->SetAttribute("count", avar("%d", mesh.mUV0s.size() * 2)); + + for (U32 i = 0; i < mesh.mUV0s.size(); i++) + { + const Point2F& uv0 = mesh.mUV0s[i]; + + TiXmlText* uv0Text = new TiXmlText(avar("%.4f %.4f", uv0.x, 1.0f - uv0.y)); // COLLADA uvs are upside down compared to Torque + uv0Node->LinkEndChild(uv0Text); + } + + // Save the uv0 accessor + TiXmlElement* uv0TechNode = new TiXmlElement("technique_common"); + uv0SourceNode->LinkEndChild(uv0TechNode); + + TiXmlElement* uv0AccNode = new TiXmlElement("accessor"); + uv0TechNode->LinkEndChild(uv0AccNode); + uv0AccNode->SetAttribute("source", avar("#%s-UV0-array", meshName.c_str())); + uv0AccNode->SetAttribute("count", avar("%d", mesh.mUV0s.size())); + uv0AccNode->SetAttribute("stride", "2"); + + TiXmlElement* uv0AccSNode = new TiXmlElement("param"); + uv0AccNode->LinkEndChild(uv0AccSNode); + uv0AccSNode->SetAttribute("name", "S"); + uv0AccSNode->SetAttribute("type", "float"); + + TiXmlElement* uv0AccTNode = new TiXmlElement("param"); + uv0AccNode->LinkEndChild(uv0AccTNode); + uv0AccTNode->SetAttribute("name", "T"); + uv0AccTNode->SetAttribute("type", "float"); + + // Define the vertices position array + TiXmlElement* verticesNode = new TiXmlElement("vertices"); + meshNode->LinkEndChild(verticesNode); + verticesNode->SetAttribute("id", avar("%s-Vertex", meshName.c_str())); + + TiXmlElement* verticesInputNode = new TiXmlElement("input"); + verticesNode->LinkEndChild(verticesInputNode); + verticesInputNode->SetAttribute("semantic", "POSITION"); + verticesInputNode->SetAttribute("source", avar("#%s-Position", meshName.c_str())); + + exportColladaTriangles(meshNode, mesh, meshName, matNames); +} + +void ColladaUtils::exportColladaScene(TiXmlElement* rootNode, const String& meshName, const Vector& matNames) +{ + TiXmlElement* libSceneNode = new TiXmlElement("library_visual_scenes"); + rootNode->LinkEndChild(libSceneNode); + + TiXmlElement* visSceneNode = new TiXmlElement("visual_scene"); + libSceneNode->LinkEndChild(visSceneNode); + visSceneNode->SetAttribute("id", "RootNode"); + visSceneNode->SetAttribute("name", "RootNode"); + + TiXmlElement* nodeNode = new TiXmlElement("node"); + visSceneNode->LinkEndChild(nodeNode); + nodeNode->SetAttribute("id", avar("%s", meshName.c_str())); + nodeNode->SetAttribute("name", avar("%s", meshName.c_str())); + + TiXmlElement* instanceGeomNode = new TiXmlElement("instance_geometry"); + nodeNode->LinkEndChild(instanceGeomNode); + instanceGeomNode->SetAttribute("url", avar("#%s-lib", meshName.c_str())); + + TiXmlElement* bindMatNode = new TiXmlElement("bind_material"); + instanceGeomNode->LinkEndChild(bindMatNode); + + TiXmlElement* techniqueNode = new TiXmlElement("technique_common"); + bindMatNode->LinkEndChild(techniqueNode); + + // Bind the materials + for (U32 i = 0; i < matNames.size(); i++) + { + TiXmlElement* instMatNode = new TiXmlElement("instance_material"); + techniqueNode->LinkEndChild(instMatNode); + instMatNode->SetAttribute("symbol", avar("%s", matNames[i].c_str())); + instMatNode->SetAttribute("target", avar("#%s", matNames[i].c_str())); + } + + TiXmlElement* sceneNode = new TiXmlElement("scene"); + rootNode->LinkEndChild(sceneNode); + + TiXmlElement* instVisSceneNode = new TiXmlElement("instance_visual_scene"); + sceneNode->LinkEndChild(instVisSceneNode); + instVisSceneNode->SetAttribute("url", "#RootNode"); +} + +void ColladaUtils::exportToCollada(const Torque::Path& colladaFile, const OptimizedPolyList& mesh, const String& meshName) +{ + // Get the mesh name + String outMeshName = meshName; + + if (outMeshName.isEmpty()) + outMeshName = colladaFile.getFileName(); + + // The XML document that will hold all of our data + TiXmlDocument doc; + + // Add a standard XML declaration to the top + TiXmlDeclaration* xmlDecl = new TiXmlDeclaration("1.0", "utf-8", ""); + doc.LinkEndChild(xmlDecl); + + // Create our Collada root node and populate a couple standard attributes + TiXmlElement* rootNode = new TiXmlElement("COLLADA"); + rootNode->SetAttribute("xmlns", "http://www.collada.org/2005/11/COLLADASchema"); + rootNode->SetAttribute("version", "1.4.0"); + + // Add the root node to the document + doc.LinkEndChild(rootNode); + + // Save out our header info + exportColladaHeader(rootNode); + + // Save out the materials + Vector mapNames; + + exportColladaMaterials(rootNode, mesh, mapNames, colladaFile); + + // Save out our geometry + exportColladaMesh(rootNode, mesh, outMeshName, mapNames); + + // Save out our scene nodes + exportColladaScene(rootNode, outMeshName, mapNames); + + // Write out the actual Collada file + char fullPath[MAX_PATH_LENGTH]; + Platform::makeFullPathName(colladaFile.getFullPath(), fullPath, MAX_PATH_LENGTH); + + if (!doc.SaveFile(fullPath)) + Con::errorf("ColladaUtils::exportToCollada(): Unable to export to %s", fullPath); +} \ No newline at end of file diff --git a/Engine/source/ts/collada/colladaUtils.h b/Engine/source/ts/collada/colladaUtils.h new file mode 100644 index 000000000..e2ee4b377 --- /dev/null +++ b/Engine/source/ts/collada/colladaUtils.h @@ -0,0 +1,810 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _COLLADA_UTILS_H_ +#define _COLLADA_UTILS_H_ + +#ifdef _MSC_VER +#pragma warning(disable : 4786) // disable warning about long debug symbol names +#pragma warning(disable : 4355) // disable "'this' : used in base member initializer list" warnings +#endif + +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif +#ifndef _MQUAT_H_ +#include "math/mQuat.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _TSSHAPE_LOADER_H_ +#include "ts/loader/tsShapeLoader.h" +#endif +#ifndef _OPTIMIZEDPOLYLIST_H_ +#include "collision/optimizedPolyList.h" +#endif +#ifndef TINYXML_INCLUDED +#include "tinyxml.h" +#endif +#ifndef _CONSOLE_H_ +#include "console/console.h" +#endif + +#include "platform/tmm_off.h" + +#include "dae.h" +#include "dae/daeErrorHandler.h" +#include "dae/domAny.h" +#include "dom/domProfile_COMMON.h" +#include "dom/domMaterial.h" +#include "dom/domGeometry.h" +#include "dom/domMorph.h" +#include "dom/domNode.h" +#include "dom/domCOLLADA.h" + +#include "platform/tmm_on.h" + +namespace ColladaUtils +{ + struct ImportOptions + { + enum eLodType + { + DetectDTS = 0, + SingleSize, + TrailingNumber, + NumLodTypes + }; + + domUpAxisType upAxis; // Override for the collada element + F32 unit; // Override for the collada element + eLodType lodType; // LOD type option + S32 singleDetailSize; // Detail size for all meshes in the model + String matNamePrefix; // Prefix to apply to collada material names + String alwaysImport; // List of node names (with wildcards) to import, even if in the neverImport list + String neverImport; // List of node names (with wildcards) to ignore on loading + String alwaysImportMesh; // List of mesh names (with wildcards) to import, even if in the neverImportMesh list + String neverImportMesh; // List of mesh names (with wildcards) to ignore on loading + bool ignoreNodeScale; // Ignore elements in s + bool adjustCenter; // Translate model so origin is at the center + bool adjustFloor; // Translate model so origin is at the bottom + bool forceUpdateMaterials; // Force update of materials.cs + bool useDiffuseNames; // Use diffuse texture as the material name + + ImportOptions() + { + reset(); + } + + void reset() + { + upAxis = UPAXISTYPE_COUNT; + unit = -1.0f; + lodType = DetectDTS; + singleDetailSize = 2; + matNamePrefix = ""; + alwaysImport = ""; + neverImport = ""; + alwaysImportMesh = ""; + neverImportMesh = ""; + ignoreNodeScale = false; + adjustCenter = false; + adjustFloor = false; + forceUpdateMaterials = false; + useDiffuseNames = false; + } + }; + + ImportOptions& getOptions(); + + void convertTransform(MatrixF& m); + + void collapsePath(std::string& path); + + // Apply the set of Collada conditioners (suited for loading Collada models into Torque) + void applyConditioners(domCOLLADA* root); + + const domProfile_COMMON* findEffectCommonProfile(const domEffect* effect); + const domCommon_color_or_texture_type_complexType* findEffectDiffuse(const domEffect* effect); + const domCommon_color_or_texture_type_complexType* findEffectSpecular(const domEffect* effect); + const domFx_sampler2D_common_complexType* getTextureSampler(const domEffect* effect, const domCommon_color_or_texture_type_complexType* texture); + String getSamplerImagePath(const domEffect* effect, const domFx_sampler2D_common_complexType* sampler2D); + String resolveImagePath(const domImage* image); + + // Collada export helper functions + Torque::Path findTexture(const Torque::Path& diffuseMap); + void exportColladaHeader(TiXmlElement* rootNode); + void exportColladaMaterials(TiXmlElement* rootNode, const OptimizedPolyList& mesh, Vector& matNames, const Torque::Path& colladaFile); + void exportColladaTriangles(TiXmlElement* meshNode, const OptimizedPolyList& mesh, const String& meshName, const Vector& matNames); + void exportColladaMesh(TiXmlElement* rootNode, const OptimizedPolyList& mesh, const String& meshName, const Vector& matNames); + void exportColladaScene(TiXmlElement* rootNode, const String& meshName, const Vector& matNames); + + // Export an OptimizedPolyList to a simple Collada file + void exportToCollada(const Torque::Path& colladaFile, const OptimizedPolyList& mesh, const String& meshName = String::EmptyString); +}; + +//----------------------------------------------------------------------------- +// Helper Classes +// +// The Collada DOM uses a different class for each XML element, and there is very +// little class inheritance, even though many elements have the same attributes +// and children. This makes the DOM a bit ugly to work with, and the following +// templates attempt to make this situation a bit nicer by providing a common way +// to access common elements, while retaining the strong typing of the DOM classes. +//----------------------------------------------------------------------------- + +/// Convert from the Collada transform types to a Torque MatrixF +template inline MatrixF vecToMatrixF(const domListOfFloats& vec) { return MatrixF(true); } + +/// Collada : [x_translate, y_translate, z_translate] +template<> inline MatrixF vecToMatrixF(const domListOfFloats& vec) +{ + MatrixF mat(true); + mat.setPosition(Point3F(vec[0], vec[1], vec[2])); + return mat; +} + +/// Collada : [x_scale, y_scale, z_scale] +template<> inline MatrixF vecToMatrixF(const domListOfFloats& vec) +{ + MatrixF mat(true); + mat.scale(Point3F(vec[0], vec[1], vec[2])); + return mat; +} + +/// Collada : [rotation_axis, angle_in_degrees] +template<> inline MatrixF vecToMatrixF(const domListOfFloats& vec) +{ + AngAxisF aaxis(Point3F(vec[0], vec[1], vec[2]), -(vec[3] * M_PI) / 180.0f); + MatrixF mat(true); + aaxis.setMatrix(&mat); + return mat; +} + +/// Collada : same form as TGE (woohoo!) +template<> inline MatrixF vecToMatrixF(const domListOfFloats& vec) +{ + MatrixF mat; + for (int i = 0; i < 16; i++) + mat[i] = vec[i]; + return mat; +} + +/// Collada : [angle_in_degrees, rotation_axis, translation_axis] +/// skew transform code adapted from GMANMatrix4 implementation +template<> inline MatrixF vecToMatrixF(const domListOfFloats& vec) +{ + F32 angle = -(vec[0] * M_PI) / 180.0f; + Point3F rotAxis(vec[1], vec[2], vec[3]); + Point3F transAxis(vec[4], vec[5], vec[6]); + + transAxis.normalize(); + + Point3F a1 = transAxis * mDot(rotAxis, transAxis); + Point3F a2 = rotAxis - a1; + a2.normalize(); + + F32 an1 = mDot(rotAxis, a2); + F32 an2 = mDot(rotAxis, transAxis); + + F32 rx = an1 * mCos(angle) - an2 * mSin(angle); + F32 ry = an1 * mSin(angle) + an2 * mCos(angle); + + // Check for rotation parallel to translation + F32 alpha = (an1 == 0) ? 0 : (ry/rx - an2/an1); + + MatrixF mat(true); + mat(0,0) = a2.x * transAxis.x * alpha + 1.0; + mat(1,0) = a2.y * transAxis.x * alpha; + mat(2,0) = a2.z * transAxis.x * alpha; + + mat(0,1) = a2.x * transAxis.y * alpha; + mat(1,1) = a2.y * transAxis.y * alpha + 1.0; + mat(2,1) = a2.z * transAxis.y * alpha; + + mat(0,2) = a2.x * transAxis.z * alpha; + mat(1,2) = a2.y * transAxis.z * alpha; + mat(2,2) = a2.z * transAxis.z * alpha + 1.0; + return mat; +} + +/// Collada : [eye, target, up] +template<> inline MatrixF vecToMatrixF(const domListOfFloats& vec) +{ + Point3F eye(vec[0], vec[1], vec[2]); + Point3F target(vec[3], vec[4], vec[5]); + Point3F up(vec[6], vec[7], vec[8]); + + Point3F fwd = target - eye; + fwd.normalizeSafe(); + + Point3F right = mCross(fwd, up); + right.normalizeSafe(); + + up = mCross(right, fwd); + up.normalizeSafe(); + + MatrixF mat(true); + mat.setColumn(0, right); + mat.setColumn(1, fwd); + mat.setColumn(2, up); + mat.setColumn(3, eye); + return mat; +} + +//----------------------------------------------------------------------------- + +/// Try to get a name for the element using the following attributes (in order): +/// name, sid, id, "null" +template inline const char* _GetNameOrId(const T* element) +{ + return element ? (element->getName() ? element->getName() : (element->getId() ? element->getId() : "null")) : "null"; +} + +template<> inline const char* _GetNameOrId(const domInstance_geometry* element) +{ + return element ? (element->getName() ? element->getName() : (element->getSid() ? element->getSid() : "null")) : "null"; +} + +template<> inline const char* _GetNameOrId(const domInstance_controller* element) +{ + return element ? (element->getName() ? element->getName() : (element->getSid() ? element->getSid() : "null")) : "null"; +} + +//----------------------------------------------------------------------------- +// Collada s are extremely flexible, and thus difficult to access in a nice +// way. This class attempts to provide a clean interface to convert Collada source +// data to the appropriate Torque data structure without losing any of the flexibility +// of the underlying Collada DOM. +// +// Some of the conversions we need to handle are: +// - daeString to const char* +// - daeIDRef to const char* +// - double to F32 +// - double to Point2F +// - double to Point3F +// - double to MatrixF +// +// The _SourceReader object is initialized with a list of parameter names that it +// tries to match to elements in the source accessor to figure out how to +// pull values out of the 1D source array. Note that no type checking of any kind +// is done until we actually try to extract values from the source. +class _SourceReader +{ + const domSource* source; // the wrapped Collada source + const domAccessor* accessor; // shortcut to the source accessor + Vector offsets; // offset of each of the desired values to pull from the source array + +public: + _SourceReader() : source(0), accessor(0) {} + + void reset() + { + source = 0; + accessor = 0; + offsets.clear(); + } + + //------------------------------------------------------ + // Initialize the _SourceReader object + bool initFromSource(const domSource* src, const char* paramNames[] = 0) + { + source = src; + accessor = source->getTechnique_common()->getAccessor(); + offsets.clear(); + + // The source array has groups of values in a 1D stream => need to map the + // input param names to source params to determine the offset within the + // group for each desired value + U32 paramCount = 0; + while (paramNames && paramNames[paramCount][0]) { + // lookup the index of the source param that matches the input param + offsets.push_back(paramCount); + for (U32 iParam = 0; iParam < accessor->getParam_array().getCount(); iParam++) { + if (accessor->getParam_array()[iParam]->getName() && + dStrEqual(accessor->getParam_array()[iParam]->getName(), paramNames[paramCount])) { + offsets.last() = iParam; + break; + } + } + paramCount++; + } + + // If no input params were specified, just map the source params directly + if (!offsets.size()) { + for (int iParam = 0; iParam < accessor->getParam_array().getCount(); iParam++) + offsets.push_back(iParam); + } + + return true; + } + + //------------------------------------------------------ + // Shortcut to the size of the array (should be the number of destination objects) + S32 size() const { return accessor ? accessor->getCount() : 0; } + + // Get the number of elements per group in the source + S32 stride() const { return accessor ? accessor->getStride() : 0; } + + //------------------------------------------------------ + // Get a pointer to the start of a group of values (index advances by stride) + //template T getArrayData(int index) const { return 0; } + + const double* getStringArrayData(int index) const + { + if ((index >= 0) && (index < size())) { + if (source->getFloat_array()) + return &source->getFloat_array()->getValue()[index*stride()]; + } + return 0; + } + + //------------------------------------------------------ + // Read a single value from the source array + //template T getValue(int index) const { return T; } + + const char* getStringValue(int index) const + { + if ((index >= 0) && (index < size())) { + // could be plain strings or IDREFs + if (source->getName_array()) + return source->getName_array()->getValue()[index*stride()]; + else if (source->getIDREF_array()) + return source->getIDREF_array()->getValue()[index*stride()].getID(); + } + return ""; + } + + F32 getFloatValue(int index) const + { + F32 value(0); + if (const double* data = getStringArrayData(index)) + return data[offsets[0]]; + return value; + } + + Point2F getPoint2FValue(int index) const + { + Point2F value(0, 0); + if (const double* data = getStringArrayData(index)) + value.set(data[offsets[0]], data[offsets[1]]); + return value; + } + + Point3F getPoint3FValue(int index) const + { + Point3F value(1, 0, 0); + if (const double* data = getStringArrayData(index)) + value.set(data[offsets[0]], data[offsets[1]], data[offsets[2]]); + return value; + } + + ColorI getColorIValue(int index) const + { + ColorI value(255, 255, 255, 255); + if (const double* data = getStringArrayData(index)) + { + value.red = data[offsets[0]] * 255.0; + value.green = data[offsets[1]] * 255.0; + value.blue = data[offsets[2]] * 255.0; + if ( stride() == 4 ) + value.alpha = data[offsets[3]] * 255.0; + } + return value; + } + + MatrixF getMatrixFValue(int index) const + { + MatrixF value(true); + if (const double* data = getStringArrayData(index)) { + for (int i = 0; i < 16; i++) + value[i] = data[i]; + } + return value; + } +}; + +//----------------------------------------------------------------------------- +// Collada geometric primitives: Use the BasePrimitive class to access the +// different primitive types in a nice way. +class BasePrimitive +{ +public: + /// Return true if the element is a geometric primitive type + static bool isPrimitive(const daeElement* element) + { + switch (element->getElementType()) { + case COLLADA_TYPE::TRIANGLES: case COLLADA_TYPE::POLYLIST: + case COLLADA_TYPE::POLYGONS: case COLLADA_TYPE::TRIFANS: + case COLLADA_TYPE::TRISTRIPS: case COLLADA_TYPE::CAPSULE: + case COLLADA_TYPE::CYLINDER: case COLLADA_TYPE::LINES: + case COLLADA_TYPE::LINESTRIPS: case COLLADA_TYPE::PLANE: + case COLLADA_TYPE::SPLINE: case COLLADA_TYPE::SPHERE: + case COLLADA_TYPE::TAPERED_CAPSULE: case COLLADA_TYPE::TAPERED_CYLINDER: + return true; + } + return false; + } + + /// Return true if the element is a supported primitive type + static bool isSupportedPrimitive(const daeElement* element) + { + switch (element->getElementType()) { + case COLLADA_TYPE::TRIANGLES: + case COLLADA_TYPE::TRISTRIPS: + case COLLADA_TYPE::TRIFANS: + case COLLADA_TYPE::POLYLIST: + case COLLADA_TYPE::POLYGONS: + return true; + } + return false; + } + + /// Construct a child class based on the type of Collada element + static BasePrimitive* get(const daeElement* element); + + /// Methods to be implemented for each supported Collada geometric element + virtual const char* getElementName() = 0; + virtual const char* getMaterial() = 0; + virtual const domInputLocalOffset_Array& getInputs() = 0; + + virtual S32 getStride() const = 0; + virtual const domListOfUInts *getTriangleData() = 0; +}; + +/// Template child class for supported Collada primitive elements +template class ColladaPrimitive : public BasePrimitive +{ + T* primitive; + domListOfUInts *pTriangleData; + S32 stride; +public: + ColladaPrimitive(const daeElement* e) : pTriangleData(0) + { + // Cast to geometric primitive element + primitive = daeSafeCast(const_cast(e)); + + // Determine stride + stride = 0; + for (int iInput = 0; iInput < getInputs().getCount(); iInput++) { + if (getInputs()[iInput]->getOffset() >= stride) + stride = getInputs()[iInput]->getOffset() + 1; + } + } + ~ColladaPrimitive() + { + delete pTriangleData; + } + + /// Most primitives can use these common implementations + const char* getElementName() { return primitive->getElementName(); } + const char* getMaterial() { return primitive->getMaterial(); } + const domInputLocalOffset_Array& getInputs() { return primitive->getInput_array(); } + S32 getStride() const { return stride; } + + /// Each supported primitive needs to implement this method (and convert + /// to triangles if required) + const domListOfUInts *getTriangleData() { return NULL; } +}; + +//----------------------------------------------------------------------------- +// +template<> inline const domListOfUInts *ColladaPrimitive::getTriangleData() +{ + // Return the

      integer list directly + return (primitive->getP() ? &(primitive->getP()->getValue()) : NULL); +} + +//----------------------------------------------------------------------------- +// +template<> inline const domListOfUInts *ColladaPrimitive::getTriangleData() +{ + if (!pTriangleData) + { + // Convert strips to triangles + pTriangleData = new domListOfUInts(); + + for (int iStrip = 0; iStrip < primitive->getCount(); iStrip++) { + + domP* P = primitive->getP_array()[iStrip]; + + // Ignore invalid P arrays + if (!P || !P->getValue().getCount()) + continue; + + domUint* pSrcData = &(P->getValue()[0]); + S32 numTriangles = (P->getValue().getCount() / stride) - 2; + + // Convert the strip back to a triangle list + domUint* v0 = pSrcData; + for (int iTri = 0; iTri < numTriangles; iTri++, v0 += stride) { + if (iTri & 0x1) + { + // CW triangle + pTriangleData->appendArray(stride, v0); + pTriangleData->appendArray(stride, v0 + 2*stride); + pTriangleData->appendArray(stride, v0 + stride); + } + else + { + // CCW triangle + pTriangleData->appendArray(stride*3, v0); + } + } + } + } + return pTriangleData; +} + +//----------------------------------------------------------------------------- +// +template<> inline const domListOfUInts *ColladaPrimitive::getTriangleData() +{ + if (!pTriangleData) + { + // Convert strips to triangles + pTriangleData = new domListOfUInts(); + + for (int iStrip = 0; iStrip < primitive->getCount(); iStrip++) { + + domP* P = primitive->getP_array()[iStrip]; + + // Ignore invalid P arrays + if (!P || !P->getValue().getCount()) + continue; + + domUint* pSrcData = &(P->getValue()[0]); + S32 numTriangles = (P->getValue().getCount() / stride) - 2; + + // Convert the fan back to a triangle list + domUint* v0 = pSrcData + stride; + for (int iTri = 0; iTri < numTriangles; iTri++, v0 += stride) { + pTriangleData->appendArray(stride, pSrcData); // shared vertex + pTriangleData->appendArray(stride, v0); // previous vertex + pTriangleData->appendArray(stride, v0+stride); // current vertex + } + } + } + return pTriangleData; +} + +//----------------------------------------------------------------------------- +// +template<> inline const domListOfUInts *ColladaPrimitive::getTriangleData() +{ + if (!pTriangleData) + { + // Convert polygons to triangles + pTriangleData = new domListOfUInts(); + + for (int iPoly = 0; iPoly < primitive->getCount(); iPoly++) { + + domP* P = primitive->getP_array()[iPoly]; + + // Ignore invalid P arrays + if (!P || !P->getValue().getCount()) + continue; + + domUint* pSrcData = &(P->getValue()[0]); + S32 numPoints = P->getValue().getCount() / stride; + + // Use a simple tri-fan (centered at the first point) method of + // converting the polygon to triangles. + domUint* v0 = pSrcData; + pSrcData += stride; + for (int iTri = 0; iTri < numPoints-2; iTri++) { + pTriangleData->appendArray(stride, v0); + pTriangleData->appendArray(stride*2, pSrcData); + pSrcData += stride; + } + } + } + return pTriangleData; +} + +//----------------------------------------------------------------------------- +// +template<> inline const domListOfUInts *ColladaPrimitive::getTriangleData() +{ + if (!pTriangleData) + { + // Convert polygons to triangles + pTriangleData = new domListOfUInts(); + + // Check that the P element has the right number of values (this + // has been seen with certain models exported using COLLADAMax) + const domListOfUInts& vcount = primitive->getVcount()->getValue(); + + U32 expectedCount = 0; + for (int iPoly = 0; iPoly < vcount.getCount(); iPoly++) + expectedCount += vcount[iPoly]; + expectedCount *= stride; + + if (!primitive->getP() || !primitive->getP()->getValue().getCount() || + (primitive->getP()->getValue().getCount() != expectedCount) ) + { + Con::warnf(" element found with invalid

      array. This primitive will be ignored."); + return pTriangleData; + } + + domUint* pSrcData = &(primitive->getP()->getValue()[0]); + for (int iPoly = 0; iPoly < vcount.getCount(); iPoly++) { + + // Use a simple tri-fan (centered at the first point) method of + // converting the polygon to triangles. + domUint* v0 = pSrcData; + pSrcData += stride; + for (int iTri = 0; iTri < vcount[iPoly]-2; iTri++) { + pTriangleData->appendArray(stride, v0); + pTriangleData->appendArray(stride*2, pSrcData); + pSrcData += stride; + } + pSrcData += stride; + } + } + return pTriangleData; +} + +//----------------------------------------------------------------------------- + +/// Convert a custom parameter string to a particular type +template inline T convert(const char* value) { return value; } +template<> inline bool convert(const char* value) { return dAtob(value); } +template<> inline S32 convert(const char* value) { return dAtoi(value); } +template<> inline double convert(const char* value) { return dAtof(value); } +template<> inline F32 convert(const char* value) { return convert(value); } + +//----------------------------------------------------------------------------- +/// Collada animation data +struct AnimChannels : public Vector +{ + daeElement *element; + AnimChannels(daeElement* el) : element(el) + { + element->setUserData(this); + } + ~AnimChannels() + { + if (element) + element->setUserData(0); + } +}; + +struct AnimData +{ + bool enabled; ///!< Used to select animation channels for the current clip + + _SourceReader input; + _SourceReader output; + + _SourceReader inTangent; + _SourceReader outTangent; + + _SourceReader interpolation; + + U32 targetValueOffset; ///< Offset into the target element (for arrays of values) + U32 targetValueCount; ///< Number of values animated (from OUTPUT source array) + + /// Get the animation channels for the Collada element (if any) + static AnimChannels* getAnimChannels(const daeElement* element) + { + return element ? (AnimChannels*)const_cast(element)->getUserData() : 0; + } + + AnimData() : enabled(false) { } + + void parseTargetString(const char* target, int fullCount, const char* elements[]); + + F32 invertParamCubic(F32 param, F32 x0, F32 x1, F32 x2, F32 x3) const; + void interpValue(F32 t, U32 offset, double* value) const; + void interpValue(F32 t, U32 offset, const char** value) const; +}; + +//----------------------------------------------------------------------------- +// Collada allows any element with an SID or ID attribute to be the target of +// an animation channel, which is very flexible, but awkward to work with. Some +// examples of animated values are: +// - single float +// - single int +// - single bool +// - single string +// - list of floats (transform elements or morph weights) +// +// This class provides a generic way to check if an element is animated, and +// to get the value of the element at a given time. +template +struct AnimatedElement +{ + const daeElement* element; ///< The Collada element (can be NULL) + T defaultVal; ///< Default value (used when element is NULL) + + AnimatedElement(const daeElement* e=0) : element(e) { } + + /// Check if the element has any animations channels + bool isAnimated() { return (AnimData::getAnimChannels(element) != 0); } + bool isAnimated(F32 start, F32 end) { return isAnimated(); } + + /// Get the value of the element at the specified time + T getValue(F32 time) + { + // If the element is NULL, just use the default (handy for profiles which + // may or may not be present in the document) + T value(defaultVal); + if (const domAny* param = daeSafeCast(const_cast(element))) { + // If the element is not animated, just use its current value + value = convert(param->getValue()); + + // Animate the value + const AnimChannels* channels = AnimData::getAnimChannels(element); + if (channels && (time >= 0)) { + for (int iChannel = 0; iChannel < channels->size(); iChannel++) { + const AnimData* animData = (*channels)[iChannel]; + if (animData->enabled) + animData->interpValue(time, 0, &value); + } + } + } + return value; + } +}; + +template struct AnimatedElementList : public AnimatedElement +{ + AnimatedElementList(const daeElement* e=0) : AnimatedElement(e) { } + + // @todo: Disable morph animations for now since they are not supported by T3D + bool isAnimated() { return false; } + bool isAnimated(F32 start, F32 end) { return false; } + + // Get the value of the element list at the specified time + T getValue(F32 time) + { + T vec(this->defaultVal); + if (this->element) { + // Get a copy of the vector + vec = *(T*)const_cast(this->element)->getValuePointer(); + + // Animate the vector + const AnimChannels* channels = AnimData::getAnimChannels(this->element); + if (channels && (time >= 0)) { + for (int iChannel = 0; iChannel < channels->size(); iChannel++) { + const AnimData* animData = (*channels)[iChannel]; + if (animData->enabled) { + for (int iValue = 0; iValue < animData->targetValueCount; iValue++) + animData->interpValue(time, iValue, &vec[animData->targetValueOffset + iValue]); + } + } + } + } + return vec; + } +}; + +// Strongly typed animated values +typedef AnimatedElement AnimatedFloat; +typedef AnimatedElement AnimatedBool; +typedef AnimatedElement AnimatedInt; +typedef AnimatedElement AnimatedString; +typedef AnimatedElementList AnimatedFloatList; + +#endif // _COLLADA_UTILS_H_ diff --git a/Engine/source/ts/instancingMatHook.cpp b/Engine/source/ts/instancingMatHook.cpp new file mode 100644 index 000000000..701651698 --- /dev/null +++ b/Engine/source/ts/instancingMatHook.cpp @@ -0,0 +1,67 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "ts/instancingMatHook.h" + +#include "platform/profiler.h" +#include "materials/materialFeatureTypes.h" +#include "materials/matInstance.h" + + +const MatInstanceHookType InstancingMaterialHook::Type( "Instancing" ); + +InstancingMaterialHook::InstancingMaterialHook() : + mMatInst( NULL ) +{ +} + +InstancingMaterialHook::~InstancingMaterialHook() +{ + SAFE_DELETE( mMatInst ); +} + +BaseMatInstance* InstancingMaterialHook::getInstancingMat( BaseMatInstance *matInst ) +{ + PROFILE_SCOPE( InstancingMaterialHook_GetInstancingMat ); + + if ( matInst == NULL ) + return NULL; + + InstancingMaterialHook *hook = matInst->getHook(); + if ( hook == NULL ) + { + hook = new InstancingMaterialHook(); + matInst->addHook( hook ); + + BaseMatInstance *instMat = matInst->getMaterial()->createMatInstance(); + FeatureSet features( matInst->getRequestedFeatures() ); + features.addFeature( MFT_UseInstancing ); + + if ( !instMat->init( features, matInst->getVertexFormat() ) ) + SAFE_DELETE( instMat ); + + hook->mMatInst = instMat; + } + + return hook->mMatInst; +} diff --git a/Engine/source/ts/instancingMatHook.h b/Engine/source/ts/instancingMatHook.h new file mode 100644 index 000000000..fe1c72659 --- /dev/null +++ b/Engine/source/ts/instancingMatHook.h @@ -0,0 +1,57 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _INSTANCINGMATHOOK_H_ +#define _INSTANCINGMATHOOK_H_ + +#ifndef _MATINSTANCEHOOK_H_ +#include "materials/matInstanceHook.h" +#endif + +class BaseMatInstance; + + +class InstancingMaterialHook : public MatInstanceHook +{ +public: + + /// The material hook type. + static const MatInstanceHookType Type; + + InstancingMaterialHook(); + virtual ~InstancingMaterialHook(); + + // MatInstanceHook + virtual const MatInstanceHookType& getType() const { return Type; } + + /// Returns the instancing material instance or the input material + /// instance if one could not be created. + static BaseMatInstance* getInstancingMat( BaseMatInstance *matInst ); + +protected: + + /// The instancing material. + BaseMatInstance *mMatInst; + +}; + +#endif // _INSTANCINGMATHOOK_H_ diff --git a/Engine/source/ts/loader/appMaterial.h b/Engine/source/ts/loader/appMaterial.h new file mode 100644 index 000000000..23cff5999 --- /dev/null +++ b/Engine/source/ts/loader/appMaterial.h @@ -0,0 +1,40 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _APPMATERIAL_H_ +#define _APPMATERIAL_H_ + + +struct AppMaterial +{ + U32 flags; + F32 reflectance; + + AppMaterial() : flags(0), reflectance(1.0f) { } + virtual ~AppMaterial() {} + + virtual String getName() const { return "unnamed"; } + virtual U32 getFlags() { return flags; } + virtual F32 getReflectance() { return reflectance; } +}; + +#endif // _APPMATERIAL_H_ diff --git a/Engine/source/ts/loader/appMesh.cpp b/Engine/source/ts/loader/appMesh.cpp new file mode 100644 index 000000000..8db105689 --- /dev/null +++ b/Engine/source/ts/loader/appMesh.cpp @@ -0,0 +1,178 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "ts/loader/appMesh.h" +#include "ts/loader/tsShapeLoader.h" + +Vector AppMesh::appMaterials; + +AppMesh::AppMesh() + : flags(0), numFrames(0), numMatFrames(0), vertsPerFrame(0) +{ +} + +AppMesh::~AppMesh() +{ +} + +void AppMesh::computeBounds(Box3F& bounds) +{ + bounds = Box3F::Invalid; + + if ( isSkin() ) + { + // Need to skin the mesh before we can compute the bounds + + // Setup bone transforms + Vector boneTransforms; + boneTransforms.setSize( nodeIndex.size() ); + for (S32 iBone = 0; iBone < boneTransforms.size(); iBone++) + { + MatrixF nodeMat = bones[iBone]->getNodeTransform( TSShapeLoader::DefaultTime ); + TSShapeLoader::zapScale(nodeMat); + boneTransforms[iBone].mul( nodeMat, initialTransforms[iBone] ); + } + + // Multiply verts by weighted bone transforms + for (S32 iVert = 0; iVert < initialVerts.size(); iVert++) + points[iVert].set( Point3F::Zero ); + + for (S32 iWeight = 0; iWeight < vertexIndex.size(); iWeight++) + { + const S32& vertIndex = vertexIndex[iWeight]; + const MatrixF& deltaTransform = boneTransforms[ boneIndex[iWeight] ]; + + Point3F v; + deltaTransform.mulP( initialVerts[vertIndex], &v ); + v *= weight[iWeight]; + + points[vertIndex] += v; + } + + // compute bounds for the skinned mesh + for (S32 iVert = 0; iVert < initialVerts.size(); iVert++) + bounds.extend( points[iVert] ); + } + else + { + MatrixF transform = getMeshTransform(TSShapeLoader::DefaultTime); + TSShapeLoader::zapScale(transform); + + for (S32 iVert = 0; iVert < points.size(); iVert++) + { + Point3F p; + transform.mulP(points[iVert], &p); + bounds.extend(p); + } + } +} + +void AppMesh::computeNormals() +{ + // Clear normals + normals.setSize( points.size() ); + for (S32 iNorm = 0; iNorm < normals.size(); iNorm++) + normals[iNorm] = Point3F::Zero; + + // Sum triangle normals for each vertex + for (S32 iPrim = 0; iPrim < primitives.size(); iPrim++) + { + const TSDrawPrimitive& prim = primitives[iPrim]; + + for (S32 iInd = 0; iInd < prim.numElements; iInd += 3) + { + // Compute the normal for this triangle + S32 idx0 = indices[prim.start + iInd + 0]; + S32 idx1 = indices[prim.start + iInd + 1]; + S32 idx2 = indices[prim.start + iInd + 2]; + + const Point3F& v0 = points[idx0]; + const Point3F& v1 = points[idx1]; + const Point3F& v2 = points[idx2]; + + Point3F n; + mCross(v2 - v0, v1 - v0, &n); + n.normalize(); // remove this to use 'weighted' normals (large triangles will have more effect) + + normals[idx0] += n; + normals[idx1] += n; + normals[idx2] += n; + } + } + + // Normalize the vertex normals (this takes care of averaging the triangle normals) + for (S32 iNorm = 0; iNorm < normals.size(); iNorm++) + normals[iNorm].normalize(); +} + +TSMesh* AppMesh::constructTSMesh() +{ + TSMesh* tsmesh; + if (isSkin()) + { + TSSkinMesh* tsskin = new TSSkinMesh(); + tsmesh = tsskin; + + // Copy skin elements + tsskin->weight = weight; + tsskin->boneIndex = boneIndex; + tsskin->vertexIndex = vertexIndex; + tsskin->batchData.nodeIndex = nodeIndex; + tsskin->batchData.initialTransforms = initialTransforms; + tsskin->batchData.initialVerts = initialVerts; + tsskin->batchData.initialNorms = initialNorms; + } + else + { + tsmesh = new TSMesh(); + } + + // Copy mesh elements + tsmesh->verts = points; + tsmesh->norms = normals; + tsmesh->tverts = uvs; + tsmesh->primitives = primitives; + tsmesh->indices = indices; + tsmesh->colors = colors; + tsmesh->tverts2 = uv2s; + + // Finish initializing the shape + tsmesh->setFlags(flags); + tsmesh->computeBounds(); + tsmesh->numFrames = numFrames; + tsmesh->numMatFrames = numMatFrames; + tsmesh->vertsPerFrame = vertsPerFrame; + tsmesh->createTangents(tsmesh->verts, tsmesh->norms); + tsmesh->encodedNorms.set(NULL,0); + + return tsmesh; +} + +bool AppMesh::isBillboard() +{ + return !dStrnicmp(getName(),"BB::",4) || !dStrnicmp(getName(),"BB_",3) || isBillboardZAxis(); +} + +bool AppMesh::isBillboardZAxis() +{ + return !dStrnicmp(getName(),"BBZ::",5) || !dStrnicmp(getName(),"BBZ_",4); +} diff --git a/Engine/source/ts/loader/appMesh.h b/Engine/source/ts/loader/appMesh.h new file mode 100644 index 000000000..dc6b193c6 --- /dev/null +++ b/Engine/source/ts/loader/appMesh.h @@ -0,0 +1,105 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _APPMESH_H_ +#define _APPMESH_H_ + +#ifndef _MMATH_H_ +#include "math/mMath.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _APPMATERIAL_H_ +#include "ts/loader/appMaterial.h" +#endif +#ifndef _APPSEQUENCE_H_ +#include "ts/loader/appSequence.h" +#endif + +class AppNode; + +class AppMesh +{ +public: + // Mesh and skin elements + Vector points; + Vector normals; + Vector uvs; + Vector uv2s; + Vector colors; + Vector primitives; + Vector indices; + + // Skin elements + Vector weight; + Vector boneIndex; + Vector vertexIndex; + Vector nodeIndex; + Vector initialTransforms; + Vector initialVerts; + Vector initialNorms; + + U32 flags; + U32 vertsPerFrame; + S32 numFrames; + S32 numMatFrames; + + // Loader elements (can be discarded after loading) + S32 detailSize; + MatrixF objectOffset; + Vector bones; + static Vector appMaterials; + +public: + AppMesh(); + virtual ~AppMesh(); + + void computeBounds(Box3F& bounds); + void computeNormals(); + + // Create a TSMesh object + TSMesh* constructTSMesh(); + + virtual const char * getName(bool allowFixed=true) = 0; + + virtual MatrixF getMeshTransform(F32 time) = 0; + virtual F32 getVisValue(F32 time) = 0; + + virtual bool getFloat(const char* propName, F32& defaultVal) = 0; + virtual bool getInt(const char* propName, S32& defaultVal) = 0; + virtual bool getBool(const char* propName, bool& defaultVal) = 0; + + virtual bool animatesVis(const AppSequence* appSeq) { return false; } + virtual bool animatesMatFrame(const AppSequence* appSeq) { return false; } + virtual bool animatesFrame(const AppSequence* appSeq) { return false; } + + virtual bool isBillboard(); + virtual bool isBillboardZAxis(); + + virtual bool isSkin() { return false; } + virtual void lookupSkinData() = 0; + + virtual void lockMesh(F32 t, const MatrixF& objectOffset) { } +}; + +#endif // _APPMESH_H_ diff --git a/Engine/source/ts/loader/appNode.cpp b/Engine/source/ts/loader/appNode.cpp new file mode 100644 index 000000000..6616d8120 --- /dev/null +++ b/Engine/source/ts/loader/appNode.cpp @@ -0,0 +1,104 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "ts/loader/appNode.h" + +AppNode::AppNode() +{ + mName = NULL; + mParentName = NULL; +} + +AppNode::~AppNode() +{ + dFree( mName ); + dFree( mParentName ); + + // delete children and meshes + for (S32 i = 0; i < mChildNodes.size(); i++) + delete mChildNodes[i]; + for (S32 i = 0; i < mMeshes.size(); i++) + delete mMeshes[i]; +} + +S32 AppNode::getNumMesh() +{ + if (mMeshes.size() == 0) + buildMeshList(); + return mMeshes.size(); +} + +AppMesh* AppNode::getMesh(S32 idx) +{ + return (idx < getNumMesh() && idx >= 0) ? mMeshes[idx] : NULL; +} + +S32 AppNode::getNumChildNodes() +{ + if (mChildNodes.size() == 0) + buildChildList(); + return mChildNodes.size(); +} + +AppNode * AppNode::getChildNode(S32 idx) +{ + return (idx < getNumChildNodes() && idx >= 0) ? mChildNodes[idx] : NULL; +} + +bool AppNode::isBillboard() +{ + return !dStrnicmp(getName(),"BB::",4) || !dStrnicmp(getName(),"BB_",3) || isBillboardZAxis(); +} + +bool AppNode::isBillboardZAxis() +{ + return !dStrnicmp(getName(),"BBZ::",5) || !dStrnicmp(getName(),"BBZ_",4); +} + +bool AppNode::isDummy() +{ + // naming convention should work well enough... + // ...but can override this method if one wants more + return !dStrnicmp(getName(), "dummy", 5); +} + +bool AppNode::isBounds() +{ + // naming convention should work well enough... + // ...but can override this method if one wants more + return !dStricmp(getName(), "bounds"); +} + +bool AppNode::isSequence() +{ + // naming convention should work well enough... + // ...but can override this method if one wants more + return !dStrnicmp(getName(), "Sequence", 8); +} + +bool AppNode::isRoot() +{ + // we assume root node isn't added, so this is never true + // but allow for possibility (by overriding this method) + // so that isParentRoot still works. + return false; +} diff --git a/Engine/source/ts/loader/appNode.h b/Engine/source/ts/loader/appNode.h new file mode 100644 index 000000000..5b6e707b5 --- /dev/null +++ b/Engine/source/ts/loader/appNode.h @@ -0,0 +1,88 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _APPNODE_H_ +#define _APPNODE_H_ + +#ifndef _MMATH_H_ +#include "math/mMath.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _APPMESH_H_ +#include "ts/loader/appMesh.h" +#endif + +class AppNode +{ + friend class TSShapeLoader; + + // add attached meshes and child nodes to app node + // the reason these are tracked by AppNode is that + // AppNode is responsible for deleting all it's children + // and attached meshes. + virtual void buildMeshList() = 0; + virtual void buildChildList() = 0; + +protected: + + S32 mParentIndex; + Vector mMeshes; + Vector mChildNodes; + char* mName; + char* mParentName; + +public: + + AppNode(); + virtual ~AppNode(); + + S32 getNumMesh(); + AppMesh* getMesh(S32 idx); + + S32 getNumChildNodes(); + AppNode* getChildNode(S32 idx); + + virtual MatrixF getNodeTransform(F32 time) = 0; + + virtual bool isEqual(AppNode* node) = 0; + + virtual bool animatesTransform(const AppSequence* appSeq) = 0; + + virtual const char* getName() = 0; + virtual const char* getParentName() = 0; + + virtual bool getFloat(const char* propName, F32& defaultVal) = 0; + virtual bool getInt(const char* propName, S32& defaultVal) = 0; + virtual bool getBool(const char* propName, bool& defaultVal) = 0; + + virtual bool isBillboard(); + virtual bool isBillboardZAxis(); + virtual bool isParentRoot() = 0; + virtual bool isDummy(); + virtual bool isBounds(); + virtual bool isSequence(); + virtual bool isRoot(); +}; + +#endif // _APPNODE_H_ diff --git a/Engine/source/ts/loader/appSequence.h b/Engine/source/ts/loader/appSequence.h new file mode 100644 index 000000000..8a5cf7f08 --- /dev/null +++ b/Engine/source/ts/loader/appSequence.h @@ -0,0 +1,60 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _APPSEQUENCE_H_ +#define _APPSEQUENCE_H_ + +#ifndef _MMATH_H_ +#include "math/mMath.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _TSSHAPE_H_ +#include "ts/tsShape.h" +#endif + +class AppSequence +{ +public: + S32 fps; + +public: + AppSequence() { } + virtual ~AppSequence() { } + + virtual void setActive(bool active) { } + + virtual S32 getNumTriggers() const { return 0; } + virtual void getTrigger(S32 index, TSShape::Trigger& trigger) const { trigger.state = 0;} + + virtual const char* getName() const { return "ambient"; } + + virtual F32 getStart() const { return 0.0f; } + virtual F32 getEnd() const { return 0.0f; } + + virtual U32 getFlags() const { return 0; } + virtual F32 getPriority() const { return 5; } + virtual F32 getBlendRefTime() const { return 0.0f; } +}; + +#endif // _APPSEQUENCE_H_ diff --git a/Engine/source/ts/loader/tsShapeLoader.cpp b/Engine/source/ts/loader/tsShapeLoader.cpp new file mode 100644 index 000000000..5170867df --- /dev/null +++ b/Engine/source/ts/loader/tsShapeLoader.cpp @@ -0,0 +1,1272 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "ts/loader/tsShapeLoader.h" + +#include "core/volume.h" +#include "materials/materialList.h" +#include "materials/matInstance.h" +#include "materials/materialManager.h" +#include "ts/tsShapeInstance.h" +#include "ts/tsMaterialList.h" + + +const F32 TSShapeLoader::DefaultTime = -1.0f; +const double TSShapeLoader::MinFrameRate = 15.0f; +const double TSShapeLoader::MaxFrameRate = 60.0f; +const double TSShapeLoader::AppGroundFrameRate = 10.0f; +Torque::Path TSShapeLoader::shapePath; + +//------------------------------------------------------------------------------ +// Utility functions + +void TSShapeLoader::zapScale(MatrixF& mat) +{ + Point3F invScale = mat.getScale(); + invScale.x = invScale.x ? (1.0f / invScale.x) : 0; + invScale.y = invScale.y ? (1.0f / invScale.y) : 0; + invScale.z = invScale.z ? (1.0f / invScale.z) : 0; + mat.scale(invScale); +} + +//------------------------------------------------------------------------------ +// Shape utility functions + +MatrixF TSShapeLoader::getLocalNodeMatrix(AppNode* node, F32 t) +{ + MatrixF m1 = node->getNodeTransform(t); + + // multiply by inverse scale at t=0 + MatrixF m10 = node->getNodeTransform(DefaultTime); + m1.scale(Point3F(1.0f/m10.getScale().x, 1.0f/m10.getScale().y, 1.0f/m10.getScale().z)); + + if (node->mParentIndex >= 0) + { + AppNode *parent = appNodes[node->mParentIndex]; + + MatrixF m2 = parent->getNodeTransform(t); + + // multiply by inverse scale at t=0 + MatrixF m20 = parent->getNodeTransform(DefaultTime); + m2.scale(Point3F(1.0f/m20.getScale().x, 1.0f/m20.getScale().y, 1.0f/m20.getScale().z)); + + // get local transform by pre-multiplying by inverted parent transform + m1 = m2.inverse() * m1; + } + else if (boundsNode && node != boundsNode) + { + // make transform relative to bounds node transform at time=t + MatrixF mb = boundsNode->getNodeTransform(t); + zapScale(mb); + m1 = mb.inverse() * m1; + } + + return m1; +} + +void TSShapeLoader::generateNodeTransform(AppNode* node, F32 t, bool blend, F32 referenceTime, + QuatF& rot, Point3F& trans, QuatF& srot, Point3F& scale) +{ + MatrixF m1 = getLocalNodeMatrix(node, t); + if (blend) + { + MatrixF m0 = getLocalNodeMatrix(node, referenceTime); + m1 = m0.inverse() * m1; + } + + rot.set(m1); + trans = m1.getPosition(); + srot.identity(); //@todo: srot not supported yet + scale = m1.getScale(); +} + +//----------------------------------------------------------------------------- + +void TSShapeLoader::updateProgress(int major, const char* msg, int numMinor, int minor) +{ + // Calculate progress value + F32 progress = (F32)major / NumLoadPhases; + const char *progressMsg = msg; + + if (numMinor) + { + progress += (minor * (1.0f / NumLoadPhases) / numMinor); + progressMsg = avar("%s (%d of %d)", msg, minor + 1, numMinor); + } + + Con::executef("updateTSShapeLoadProgress", Con::getFloatArg(progress), progressMsg); +} + +//----------------------------------------------------------------------------- +// Shape creation entry point + +TSShape* TSShapeLoader::generateShape(const Torque::Path& path) +{ + shapePath = path; + shape = new TSShape(); + + shape->mExporterVersion = 124; + shape->mSmallestVisibleSize = 999999; + shape->mSmallestVisibleDL = 0; + shape->mReadVersion = 24; + shape->mFlags = 0; + shape->mSequencesConstructed = 0; + + // Get all nodes, objects and sequences in the shape + updateProgress(Load_EnumerateScene, "Enumerating scene..."); + enumerateScene(); + if (!subshapes.size()) + { + delete shape; + Con::errorf("Failed to load shape \"%s\", no subshapes found", path.getFullPath().c_str()); + return NULL; + } + + // Create the TSShape::Node hierarchy + generateSubshapes(); + + // Create objects (meshes and details) + generateObjects(); + + // Generate initial object states and node transforms + generateDefaultStates(); + + // Generate skins + generateSkins(); + + // Generate material list + generateMaterialList(); + + // Generate animation sequences + generateSequences(); + + // Sort detail levels and meshes + updateProgress(Load_InitShape, "Initialising shape..."); + sortDetails(); + + // Install the TS memory helper into a TSShape object. + install(); + + return shape; +} + +bool TSShapeLoader::processNode(AppNode* node) +{ + // Detect bounds node + if ( node->isBounds() ) + { + if ( boundsNode ) + { + Con::warnf( "More than one bounds node found" ); + return false; + } + boundsNode = node; + + // Process bounds geometry + MatrixF boundsMat(boundsNode->getNodeTransform(DefaultTime)); + boundsMat.inverse(); + zapScale(boundsMat); + for (S32 iMesh = 0; iMesh < boundsNode->getNumMesh(); iMesh++) + { + AppMesh* mesh = boundsNode->getMesh(iMesh); + MatrixF transform = mesh->getMeshTransform(DefaultTime); + transform.mulL(boundsMat); + mesh->lockMesh(DefaultTime, transform); + } + return true; + } + + // Detect sequence markers + if ( node->isSequence() ) + { + //appSequences.push_back(new AppSequence(node)); + return false; + } + + // Add this node to the subshape (create one if needed) + if ( subshapes.size() == 0 ) + subshapes.push_back( new TSShapeLoader::Subshape ); + + subshapes.last()->branches.push_back( node ); + + return true; +} + +//----------------------------------------------------------------------------- +// Nodes, meshes and skins + +typedef bool (*NameCmpFunc)(const String&, const Vector&, void*, void*); + +bool cmpShapeName(const String& key, const Vector& names, void* arg1, void* arg2) +{ + for (S32 i = 0; i < names.size(); i++) + { + if (names[i].compare(key, 0, String::NoCase) == 0) + return false; + } + return true; +} + +String getUniqueName(const char* name, NameCmpFunc isNameUnique, const Vector& names, void* arg1=0, void* arg2=0) +{ + const int MAX_ITERATIONS = 0x10000; // maximum of 4 characters (A-P) will be appended + + String suffix; + for (S32 i = 0; i < MAX_ITERATIONS; i++) + { + // Generate a suffix using the first 16 characters of the alphabet + suffix.clear(); + for (S32 value = i; value != 0; value >>= 4) + suffix = suffix + (char)('A' + (value & 0xF)); + + String uname = name + suffix; + if (isNameUnique(uname, names, arg1, arg2)) + return uname; + } + return name; +} + +void TSShapeLoader::recurseSubshape(AppNode* appNode, S32 parentIndex, bool recurseChildren) +{ + // Ignore local bounds nodes + if (appNode->isBounds()) + return; + + S32 subShapeNum = shape->subShapeFirstNode.size()-1; + Subshape* subshape = subshapes[subShapeNum]; + + // Check if we should collapse this node + S32 myIndex; + if (ignoreNode(appNode->getName())) + { + myIndex = parentIndex; + } + else + { + // Check that adding this node will not exceed the maximum node count + if (shape->nodes.size() >= MAX_TS_SET_SIZE) + return; + + myIndex = shape->nodes.size(); + String nodeName = getUniqueName(appNode->getName(), cmpShapeName, shape->names); + + // Create the 3space node + shape->nodes.increment(); + shape->nodes.last().nameIndex = shape->addName(nodeName); + shape->nodes.last().parentIndex = parentIndex; + shape->nodes.last().firstObject = -1; + shape->nodes.last().firstChild = -1; + shape->nodes.last().nextSibling = -1; + + // Add the AppNode to a matching list (so AppNodes can be accessed using 3space + // node indices) + appNodes.push_back(appNode); + appNodes.last()->mParentIndex = parentIndex; + + // Check for NULL detail or AutoBillboard nodes (no children or geometry) + if ((appNode->getNumChildNodes() == 0) && + (appNode->getNumMesh() == 0)) + { + S32 size = 0x7FFFFFFF; + String dname(String::GetTrailingNumber(appNode->getName(), size)); + + if (dStrEqual(dname, "nulldetail") && (size != 0x7FFFFFFF)) + { + shape->addDetail("detail", size, subShapeNum); + } + else if (appNode->isBillboard() && (size != 0x7FFFFFFF)) + { + // AutoBillboard detail + S32 numEquatorSteps = 4; + S32 numPolarSteps = 0; + F32 polarAngle = 0.0f; + S32 dl = 0; + S32 dim = 64; + bool includePoles = true; + + appNode->getInt("BB::EQUATOR_STEPS", numEquatorSteps); + appNode->getInt("BB::POLAR_STEPS", numPolarSteps); + appNode->getFloat("BB::POLAR_ANGLE", polarAngle); + appNode->getInt("BB::DL", dl); + appNode->getInt("BB::DIM", dim); + appNode->getBool("BB::INCLUDE_POLES", includePoles); + + S32 detIndex = shape->addDetail( "bbDetail", size, -1 ); + shape->details[detIndex].bbEquatorSteps = numEquatorSteps; + shape->details[detIndex].bbPolarSteps = numPolarSteps; + shape->details[detIndex].bbDetailLevel = dl; + shape->details[detIndex].bbDimension = dim; + shape->details[detIndex].bbIncludePoles = includePoles; + shape->details[detIndex].bbPolarAngle = polarAngle; + } + } + } + + // Collect geometry + for (U32 iMesh = 0; iMesh < appNode->getNumMesh(); iMesh++) + { + AppMesh* mesh = appNode->getMesh(iMesh); + if (!ignoreMesh(mesh->getName())) + { + subshape->objMeshes.push_back(mesh); + subshape->objNodes.push_back(mesh->isSkin() ? -1 : myIndex); + } + } + + // Create children + if (recurseChildren) + { + for (int iChild = 0; iChild < appNode->getNumChildNodes(); iChild++) + recurseSubshape(appNode->getChildNode(iChild), myIndex, true); + } +} + +void TSShapeLoader::generateSubshapes() +{ + for (U32 iSub = 0; iSub < subshapes.size(); iSub++) + { + updateProgress(Load_GenerateSubshapes, "Generating subshapes...", subshapes.size(), iSub); + + Subshape* subshape = subshapes[iSub]; + + // Recurse through the node hierarchy, adding 3space nodes and + // collecting geometry + S32 firstNode = shape->nodes.size(); + shape->subShapeFirstNode.push_back(firstNode); + + for (U32 iBranch = 0; iBranch < subshape->branches.size(); iBranch++) + recurseSubshape(subshape->branches[iBranch], -1, true); + + shape->subShapeNumNodes.push_back(shape->nodes.size() - firstNode); + + if (shape->nodes.size() >= MAX_TS_SET_SIZE) + { + Con::warnf("Shape exceeds the maximum node count (%d). Ignoring additional nodes.", + MAX_TS_SET_SIZE); + } + } +} + +// Custom name comparison function to compare mesh name and detail size +bool cmpMeshNameAndSize(const String& key, const Vector& names, void* arg1, void* arg2) +{ + const Vector& meshes = *(Vector*)arg1; + S32 meshSize = (S32)arg2; + + for (S32 i = 0; i < names.size(); i++) + { + if (names[i].compare(key, 0, String::NoCase) == 0) + { + if (meshes[i]->detailSize == meshSize) + return false; + } + } + return true; +} + +void TSShapeLoader::generateObjects() +{ + for (S32 iSub = 0; iSub < subshapes.size(); iSub++) + { + Subshape* subshape = subshapes[iSub]; + shape->subShapeFirstObject.push_back(shape->objects.size()); + + // Get the names and sizes of the meshes for this subshape + Vector meshNames; + for (S32 iMesh = 0; iMesh < subshape->objMeshes.size(); iMesh++) + { + AppMesh* mesh = subshape->objMeshes[iMesh]; + mesh->detailSize = 2; + String name = String::GetTrailingNumber( mesh->getName(), mesh->detailSize ); + name = getUniqueName( name, cmpMeshNameAndSize, meshNames, &(subshape->objMeshes), (void*)mesh->detailSize ); + meshNames.push_back( name ); + + // Fix up any collision details that don't have a negative detail level. + if ( dStrStartsWith(meshNames[iMesh], "Collision") || + dStrStartsWith(meshNames[iMesh], "LOSCol") ) + { + if (mesh->detailSize > 0) + mesh->detailSize = -mesh->detailSize; + } + } + + // An 'object' is a collection of meshes with the same base name and + // different detail sizes. The object is attached to the node of the + // highest detail mesh. + + // Sort the 3 arrays (objMeshes, objNodes, meshNames) by name and size + for (S32 i = 0; i < subshape->objMeshes.size()-1; i++) + { + for (S32 j = i+1; j < subshape->objMeshes.size(); j++) + { + if ((meshNames[i].compare(meshNames[j]) < 0) || + ((meshNames[i].compare(meshNames[j]) == 0) && + (subshape->objMeshes[i]->detailSize < subshape->objMeshes[j]->detailSize))) + { + { + AppMesh* tmp = subshape->objMeshes[i]; + subshape->objMeshes[i] = subshape->objMeshes[j]; + subshape->objMeshes[j] = tmp; + } + { + S32 tmp = subshape->objNodes[i]; + subshape->objNodes[i] = subshape->objNodes[j]; + subshape->objNodes[j] = tmp; + } + { + String tmp = meshNames[i]; + meshNames[i] = meshNames[j]; + meshNames[j] = tmp; + } + } + } + } + + // Now create objects + const String* lastName = 0; + for (S32 iMesh = 0; iMesh < subshape->objMeshes.size(); iMesh++) + { + AppMesh* mesh = subshape->objMeshes[iMesh]; + + if (!lastName || (meshNames[iMesh] != *lastName)) + { + shape->objects.increment(); + shape->objects.last().nameIndex = shape->addName(meshNames[iMesh]); + shape->objects.last().nodeIndex = subshape->objNodes[iMesh]; + shape->objects.last().startMeshIndex = appMeshes.size(); + shape->objects.last().numMeshes = 0; + lastName = &meshNames[iMesh]; + } + + // Add this mesh to the object + appMeshes.push_back(mesh); + shape->objects.last().numMeshes++; + + // Set mesh flags + mesh->flags = 0; + if (mesh->isBillboard()) + { + mesh->flags |= TSMesh::Billboard; + if (mesh->isBillboardZAxis()) + mesh->flags |= TSMesh::BillboardZAxis; + } + + // Set the detail name... do fixups for collision details. + const char* detailName = "detail"; + if ( mesh->detailSize < 0 ) + { + if ( dStrStartsWith(meshNames[iMesh], "Collision") || + dStrStartsWith(meshNames[iMesh], "Col") ) + detailName = "Collision"; + else if (dStrStartsWith(meshNames[iMesh], "LOSCol")) + detailName = "LOS"; + } + + // Attempt to add the detail (will fail if it already exists) + S32 oldNumDetails = shape->details.size(); + shape->addDetail(detailName, mesh->detailSize, iSub); + if (shape->details.size() > oldNumDetails) + { + Con::warnf("Object mesh \"%s\" has no matching detail (\"%s%d\" has" + " been added automatically)", mesh->getName(false), detailName, mesh->detailSize); + } + } + + // Get object count for this subshape + shape->subShapeNumObjects.push_back(shape->objects.size() - shape->subShapeFirstObject.last()); + } +} + +void TSShapeLoader::generateSkins() +{ + Vector skins; + for (int iObject = 0; iObject < shape->objects.size(); iObject++) + { + for (int iMesh = 0; iMesh < shape->objects[iObject].numMeshes; iMesh++) + { + AppMesh* mesh = appMeshes[shape->objects[iObject].startMeshIndex + iMesh]; + if (mesh->isSkin()) + skins.push_back(mesh); + } + } + + for (int iSkin = 0; iSkin < skins.size(); iSkin++) + { + updateProgress(Load_GenerateSkins, "Generating skins...", skins.size(), iSkin); + + // Get skin data (bones, vertex weights etc) + AppMesh* skin = skins[iSkin]; + skin->lookupSkinData(); + + // Just copy initial verts and norms for now + skin->initialVerts.set(skin->points.address(), skin->vertsPerFrame); + skin->initialNorms.set(skin->normals.address(), skin->vertsPerFrame); + + // Map bones to nodes + skin->nodeIndex.setSize(skin->bones.size()); + for (int iBone = 0; iBone < skin->bones.size(); iBone++) + { + // Find the node that matches this bone + skin->nodeIndex[iBone] = -1; + for (int iNode = 0; iNode < appNodes.size(); iNode++) + { + if (appNodes[iNode]->isEqual(skin->bones[iBone])) + { + delete skin->bones[iBone]; + skin->bones[iBone] = appNodes[iNode]; + skin->nodeIndex[iBone] = iNode; + break; + } + } + + if (skin->nodeIndex[iBone] == -1) + { + Con::warnf("Could not find bone %d. Defaulting to first node", iBone); + skin->nodeIndex[iBone] = 0; + } + } + } +} + +void TSShapeLoader::generateDefaultStates() +{ + // Generate default object states (includes initial geometry) + for (int iObject = 0; iObject < shape->objects.size(); iObject++) + { + updateProgress(Load_GenerateDefaultStates, "Generating initial mesh and node states...", + shape->objects.size(), iObject); + + TSShape::Object& obj = shape->objects[iObject]; + + // Calculate the objectOffset for each mesh at T=0 + for (int iMesh = 0; iMesh < obj.numMeshes; iMesh++) + { + AppMesh* appMesh = appMeshes[obj.startMeshIndex + iMesh]; + AppNode* appNode = obj.nodeIndex >= 0 ? appNodes[obj.nodeIndex] : boundsNode; + + MatrixF meshMat(appMesh->getMeshTransform(DefaultTime)); + MatrixF nodeMat(appMesh->isSkin() ? meshMat : appNode->getNodeTransform(DefaultTime)); + + zapScale(nodeMat); + + appMesh->objectOffset = nodeMat.inverse() * meshMat; + } + + generateObjectState(shape->objects[iObject], DefaultTime, true, true); + } + + // Generate default node transforms + for (int iNode = 0; iNode < appNodes.size(); iNode++) + { + // Determine the default translation and rotation for the node + QuatF rot, srot; + Point3F trans, scale; + generateNodeTransform(appNodes[iNode], DefaultTime, false, 0, rot, trans, srot, scale); + + // Add default node translation and rotation + addNodeRotation(rot, true); + addNodeTranslation(trans, true); + } +} + +void TSShapeLoader::generateObjectState(TSShape::Object& obj, F32 t, bool addFrame, bool addMatFrame) +{ + shape->objectStates.increment(); + TSShape::ObjectState& state = shape->objectStates.last(); + + state.frameIndex = 0; + state.matFrameIndex = 0; + state.vis = mClampF(appMeshes[obj.startMeshIndex]->getVisValue(t), 0.0f, 1.0f); + + if (addFrame || addMatFrame) + { + generateFrame(obj, t, addFrame, addMatFrame); + + // set the frame number for the object state + state.frameIndex = appMeshes[obj.startMeshIndex]->numFrames - 1; + state.matFrameIndex = appMeshes[obj.startMeshIndex]->numMatFrames - 1; + } +} + +void TSShapeLoader::generateFrame(TSShape::Object& obj, F32 t, bool addFrame, bool addMatFrame) +{ + for (int iMesh = 0; iMesh < obj.numMeshes; iMesh++) + { + AppMesh* appMesh = appMeshes[obj.startMeshIndex + iMesh]; + + U32 oldNumPoints = appMesh->points.size(); + U32 oldNumUvs = appMesh->uvs.size(); + + // Get the mesh geometry at time, 't' + // Geometry verts, normals and tverts can be animated (different set for + // each frame), but the TSDrawPrimitives stay the same, so the way lockMesh + // works is that it will only generate the primitives once, then after that + // will just append verts, normals and tverts each time it is called. + appMesh->lockMesh(t, appMesh->objectOffset); + + // Calculate vertex normals if required + if (appMesh->normals.size() != appMesh->points.size()) + appMesh->computeNormals(); + + // If this is the first call, set the number of points per frame + if (appMesh->numFrames == 0) + { + appMesh->vertsPerFrame = appMesh->points.size(); + } + else + { + // Check frame topology => ie. that the right number of points, normals + // and tverts was added + if ((appMesh->points.size() - oldNumPoints) != appMesh->vertsPerFrame) + { + Con::warnf("Wrong number of points (%d) added at time=%f (expected %d)", + appMesh->points.size() - oldNumPoints, t, appMesh->vertsPerFrame); + addFrame = false; + } + if ((appMesh->normals.size() - oldNumPoints) != appMesh->vertsPerFrame) + { + Con::warnf("Wrong number of normals (%d) added at time=%f (expected %d)", + appMesh->normals.size() - oldNumPoints, t, appMesh->vertsPerFrame); + addFrame = false; + } + if ((appMesh->uvs.size() - oldNumUvs) != appMesh->vertsPerFrame) + { + Con::warnf("Wrong number of tverts (%d) added at time=%f (expected %d)", + appMesh->uvs.size() - oldNumUvs, t, appMesh->vertsPerFrame); + addMatFrame = false; + } + } + + // Because lockMesh adds points, normals AND tverts each call, if we didn't + // actually want another frame or matFrame, we need to remove them afterwards. + // In the common case (we DO want the frame), we can do nothing => the + // points/normals/tverts are already in place! + if (addFrame) + { + appMesh->numFrames++; + } + else + { + appMesh->points.setSize(oldNumPoints); + appMesh->normals.setSize(oldNumPoints); + } + + if (addMatFrame) + { + appMesh->numMatFrames++; + } + else + { + appMesh->uvs.setSize(oldNumPoints); + } + } +} + +//----------------------------------------------------------------------------- +// Materials + +/// Convert all Collada materials into a single TSMaterialList +void TSShapeLoader::generateMaterialList() +{ + // Install the materials into the material list + shape->materialList = new TSMaterialList; + for (int iMat = 0; iMat < AppMesh::appMaterials.size(); iMat++) + { + updateProgress(Load_GenerateMaterials, "Generating materials...", AppMesh::appMaterials.size(), iMat); + + AppMaterial* appMat = AppMesh::appMaterials[iMat]; + shape->materialList->push_back(appMat->getName(), appMat->getFlags(), U32(-1), U32(-1), U32(-1), 1.0f, appMat->getReflectance()); + } +} + + +//----------------------------------------------------------------------------- +// Animation Sequences + +void TSShapeLoader::generateSequences() +{ + for (int iSeq = 0; iSeq < appSequences.size(); iSeq++) + { + updateProgress(Load_GenerateSequences, "Generating sequences...", appSequences.size(), iSeq); + + // Initialize the sequence + appSequences[iSeq]->setActive(true); + + shape->sequences.increment(); + TSShape::Sequence& seq = shape->sequences.last(); + + seq.nameIndex = shape->addName(appSequences[iSeq]->getName()); + seq.toolBegin = appSequences[iSeq]->getStart(); + seq.priority = appSequences[iSeq]->getPriority(); + seq.flags = appSequences[iSeq]->getFlags(); + + // Compute duration and number of keyframes (then adjust time between frames to match) + seq.duration = appSequences[iSeq]->getEnd() - appSequences[iSeq]->getStart(); + seq.numKeyframes = (S32)(seq.duration * appSequences[iSeq]->fps + 0.5f) + 1; + + seq.sourceData.start = 0; + seq.sourceData.end = seq.numKeyframes-1; + seq.sourceData.total = seq.numKeyframes; + + // Set membership arrays (ie. which nodes and objects are affected by this sequence) + setNodeMembership(seq, appSequences[iSeq]); + setObjectMembership(seq, appSequences[iSeq]); + + // Generate keyframes + generateNodeAnimation(seq); + generateObjectAnimation(seq, appSequences[iSeq]); + generateGroundAnimation(seq, appSequences[iSeq]); + generateFrameTriggers(seq, appSequences[iSeq]); + + // Set sequence flags + seq.dirtyFlags = 0; + if (seq.rotationMatters.testAll() || seq.translationMatters.testAll() || seq.scaleMatters.testAll()) + seq.dirtyFlags |= TSShapeInstance::TransformDirty; + if (seq.visMatters.testAll()) + seq.dirtyFlags |= TSShapeInstance::VisDirty; + if (seq.frameMatters.testAll()) + seq.dirtyFlags |= TSShapeInstance::FrameDirty; + if (seq.matFrameMatters.testAll()) + seq.dirtyFlags |= TSShapeInstance::MatFrameDirty; + + // Set shape flags (only the most significant scale type) + U32 curVal = shape->mFlags & TSShape::AnyScale; + shape->mFlags &= ~(TSShape::AnyScale); + shape->mFlags |= getMax(curVal, seq.flags & TSShape::AnyScale); // take the larger value (can only convert upwards) + + appSequences[iSeq]->setActive(false); + } +} + +void TSShapeLoader::setNodeMembership(TSShape::Sequence& seq, const AppSequence* appSeq) +{ + seq.rotationMatters.clearAll(); // node rotation (size = nodes.size()) + seq.translationMatters.clearAll(); // node translation (size = nodes.size()) + seq.scaleMatters.clearAll(); // node scale (size = nodes.size()) + + // This shouldn't be allowed, but check anyway... + if (seq.numKeyframes < 2) + return; + + // Note: this fills the cache with current sequence data. Methods that get + // called later (e.g. generateNodeAnimation) use this info (and assume it's set). + fillNodeTransformCache(seq, appSeq); + + // Test to see if the transform changes over the interval in order to decide + // whether to animate the transform in 3space. We don't use app's mechanism + // for doing this because it functions different in different apps and we do + // some special stuff with scale. + setRotationMembership(seq); + setTranslationMembership(seq); + setScaleMembership(seq); +} + +void TSShapeLoader::setRotationMembership(TSShape::Sequence& seq) +{ + for (int iNode = 0; iNode < appNodes.size(); iNode++) + { + // Check if any of the node rotations are different to + // the default rotation + QuatF defaultRot; + shape->defaultRotations[iNode].getQuatF(&defaultRot); + + for (int iFrame = 0; iFrame < seq.numKeyframes; iFrame++) + { + if (nodeRotCache[iNode][iFrame] != defaultRot) + { + seq.rotationMatters.set(iNode); + break; + } + } + } +} + +void TSShapeLoader::setTranslationMembership(TSShape::Sequence& seq) +{ + for (int iNode = 0; iNode < appNodes.size(); iNode++) + { + // Check if any of the node translations are different to + // the default translation + Point3F& defaultTrans = shape->defaultTranslations[iNode]; + + for (int iFrame = 0; iFrame < seq.numKeyframes; iFrame++) + { + if (!nodeTransCache[iNode][iFrame].equal(defaultTrans)) + { + seq.translationMatters.set(iNode); + break; + } + } + } +} + +void TSShapeLoader::setScaleMembership(TSShape::Sequence& seq) +{ + Point3F unitScale(1,1,1); + + U32 arbitraryScaleCount = 0; + U32 alignedScaleCount = 0; + U32 uniformScaleCount = 0; + + for (int iNode = 0; iNode < appNodes.size(); iNode++) + { + // Check if any of the node scales are not the unit scale + for (int iFrame = 0; iFrame < seq.numKeyframes; iFrame++) + { + Point3F& scale = nodeScaleCache[iNode][iFrame]; + if (!unitScale.equal(scale)) + { + // Determine what type of scale this is + if (!nodeScaleRotCache[iNode][iFrame].isIdentity()) + arbitraryScaleCount++; + else if (scale.x != scale.y || scale.y != scale.z) + alignedScaleCount++; + else + uniformScaleCount++; + + seq.scaleMatters.set(iNode); + break; + } + } + } + + // Only one type of scale is animated + if (arbitraryScaleCount) + seq.flags |= TSShape::ArbitraryScale; + else if (alignedScaleCount) + seq.flags |= TSShape::AlignedScale; + else if (uniformScaleCount) + seq.flags |= TSShape::UniformScale; +} + +void TSShapeLoader::setObjectMembership(TSShape::Sequence& seq, const AppSequence* appSeq) +{ + seq.visMatters.clearAll(); // object visibility (size = objects.size()) + seq.frameMatters.clearAll(); // vert animation (morph) (size = objects.size()) + seq.matFrameMatters.clearAll(); // UV animation (size = objects.size()) + + for (int iObject = 0; iObject < shape->objects.size(); iObject++) + { + if (!appMeshes[shape->objects[iObject].startMeshIndex]) + continue; + + if (appMeshes[shape->objects[iObject].startMeshIndex]->animatesVis(appSeq)) + seq.visMatters.set(iObject); + // Morph and UV animation has been deprecated + //if (appMeshes[shape->objects[iObject].startMeshIndex]->animatesFrame(appSeq)) + //seq.frameMatters.set(iObject); + //if (appMeshes[shape->objects[iObject].startMeshIndex]->animatesMatFrame(appSeq)) + //seq.matFrameMatters.set(iObject); + } +} + +void TSShapeLoader::clearNodeTransformCache() +{ + // clear out the transform caches + for (int i = 0; i < nodeRotCache.size(); i++) + delete [] nodeRotCache[i]; + nodeRotCache.clear(); + for (int i = 0; i < nodeTransCache.size(); i++) + delete [] nodeTransCache[i]; + nodeTransCache.clear(); + for (int i = 0; i < nodeScaleRotCache.size(); i++) + delete [] nodeScaleRotCache[i]; + nodeScaleRotCache.clear(); + for (int i = 0; i < nodeScaleCache.size(); i++) + delete [] nodeScaleCache[i]; + nodeScaleCache.clear(); +} + +void TSShapeLoader::fillNodeTransformCache(TSShape::Sequence& seq, const AppSequence* appSeq) +{ + // clear out the transform caches and set it up for this sequence + clearNodeTransformCache(); + + nodeRotCache.setSize(appNodes.size()); + for (int i = 0; i < nodeRotCache.size(); i++) + nodeRotCache[i] = new QuatF[seq.numKeyframes]; + nodeTransCache.setSize(appNodes.size()); + for (int i = 0; i < nodeTransCache.size(); i++) + nodeTransCache[i] = new Point3F[seq.numKeyframes]; + nodeScaleRotCache.setSize(appNodes.size()); + for (int i = 0; i < nodeScaleRotCache.size(); i++) + nodeScaleRotCache[i] = new QuatF[seq.numKeyframes]; + nodeScaleCache.setSize(appNodes.size()); + for (int i = 0; i < nodeScaleCache.size(); i++) + nodeScaleCache[i] = new Point3F[seq.numKeyframes]; + + // get the node transforms for every frame + for (int iFrame = 0; iFrame < seq.numKeyframes; iFrame++) + { + F32 time = appSeq->getStart() + seq.duration * iFrame / getMax(1, seq.numKeyframes - 1); + for (int iNode = 0; iNode < appNodes.size(); iNode++) + { + generateNodeTransform(appNodes[iNode], time, seq.isBlend(), appSeq->getBlendRefTime(), + nodeRotCache[iNode][iFrame], nodeTransCache[iNode][iFrame], + nodeScaleRotCache[iNode][iFrame], nodeScaleCache[iNode][iFrame]); + } + } +} + +void TSShapeLoader::addNodeRotation(QuatF& rot, bool defaultVal) +{ + Quat16 rot16; + rot16.set(rot); + + if (!defaultVal) + shape->nodeRotations.push_back(rot16); + else + shape->defaultRotations.push_back(rot16); +} + +void TSShapeLoader::addNodeTranslation(Point3F& trans, bool defaultVal) +{ + if (!defaultVal) + shape->nodeTranslations.push_back(trans); + else + shape->defaultTranslations.push_back(trans); +} + +void TSShapeLoader::addNodeUniformScale(F32 scale) +{ + shape->nodeUniformScales.push_back(scale); +} + +void TSShapeLoader::addNodeAlignedScale(Point3F& scale) +{ + shape->nodeAlignedScales.push_back(scale); +} + +void TSShapeLoader::addNodeArbitraryScale(QuatF& qrot, Point3F& scale) +{ + Quat16 rot16; + rot16.set(qrot); + shape->nodeArbitraryScaleRots.push_back(rot16); + shape->nodeArbitraryScaleFactors.push_back(scale); +} + +void TSShapeLoader::generateNodeAnimation(TSShape::Sequence& seq) +{ + seq.baseRotation = shape->nodeRotations.size(); + seq.baseTranslation = shape->nodeTranslations.size(); + seq.baseScale = (seq.flags & TSShape::ArbitraryScale) ? shape->nodeArbitraryScaleRots.size() : + (seq.flags & TSShape::AlignedScale) ? shape->nodeAlignedScales.size() : + shape->nodeUniformScales.size(); + + for (int iNode = 0; iNode < appNodes.size(); iNode++) + { + for (int iFrame = 0; iFrame < seq.numKeyframes; iFrame++) + { + if (seq.rotationMatters.test(iNode)) + addNodeRotation(nodeRotCache[iNode][iFrame], false); + if (seq.translationMatters.test(iNode)) + addNodeTranslation(nodeTransCache[iNode][iFrame], false); + if (seq.scaleMatters.test(iNode)) + { + QuatF& rot = nodeScaleRotCache[iNode][iFrame]; + Point3F scale = nodeScaleCache[iNode][iFrame]; + + if (seq.flags & TSShape::ArbitraryScale) + addNodeArbitraryScale(rot, scale); + else if (seq.flags & TSShape::AlignedScale) + addNodeAlignedScale(scale); + else if (seq.flags & TSShape::UniformScale) + addNodeUniformScale((scale.x+scale.y+scale.z)/3.0f); + } + } + } +} + +void TSShapeLoader::generateObjectAnimation(TSShape::Sequence& seq, const AppSequence* appSeq) +{ + seq.baseObjectState = shape->objectStates.size(); + + for (int iObject = 0; iObject < shape->objects.size(); iObject++) + { + bool visMatters = seq.visMatters.test(iObject); + bool frameMatters = seq.frameMatters.test(iObject); + bool matFrameMatters = seq.matFrameMatters.test(iObject); + + if (visMatters || frameMatters || matFrameMatters) + { + for (int iFrame = 0; iFrame < seq.numKeyframes; iFrame++) + { + F32 time = appSeq->getStart() + seq.duration * iFrame / getMax(1, seq.numKeyframes - 1); + generateObjectState(shape->objects[iObject], time, frameMatters, matFrameMatters); + } + } + } +} + +void TSShapeLoader::generateGroundAnimation(TSShape::Sequence& seq, const AppSequence* appSeq) +{ + seq.firstGroundFrame = shape->groundTranslations.size(); + seq.numGroundFrames = 0; + + if (!boundsNode) + return; + + // Check if the bounds node is animated by this sequence + seq.numGroundFrames = (S32)((seq.duration + 0.25f/AppGroundFrameRate) * AppGroundFrameRate); + + seq.flags |= TSShape::MakePath; + + // Get ground transform at the start of the sequence + MatrixF invStartMat = boundsNode->getNodeTransform(appSeq->getStart()); + zapScale(invStartMat); + invStartMat.inverse(); + + for (int iFrame = 0; iFrame < seq.numGroundFrames; iFrame++) + { + F32 time = appSeq->getStart() + seq.duration * iFrame / getMax(1, seq.numGroundFrames - 1); + + // Determine delta bounds node transform at 't' + MatrixF mat = boundsNode->getNodeTransform(time); + zapScale(mat); + mat = invStartMat * mat; + + // Add ground transform + Quat16 rotation; + rotation.set(QuatF(mat)); + shape->groundTranslations.push_back(mat.getPosition()); + shape->groundRotations.push_back(rotation); + } +} + +void TSShapeLoader::generateFrameTriggers(TSShape::Sequence& seq, const AppSequence* appSeq) +{ + // Initialize triggers + seq.firstTrigger = shape->triggers.size(); + seq.numTriggers = appSeq->getNumTriggers(); + if (!seq.numTriggers) + return; + + seq.flags |= TSShape::MakePath; + + // Add triggers + for (int iTrigger = 0; iTrigger < seq.numTriggers; iTrigger++) + { + shape->triggers.increment(); + appSeq->getTrigger(iTrigger, shape->triggers.last()); + } + + // Track the triggers that get turned off by this shape...normally, triggers + // aren't turned on/off, just on...if we are a trigger that does both then we + // need to mark ourselves as such so that on/off can become off/on when sequence + // is played in reverse... + U32 offTriggers = 0; + for (int iTrigger = 0; iTrigger < seq.numTriggers; iTrigger++) + { + U32 state = shape->triggers[seq.firstTrigger+iTrigger].state; + if ((state & TSShape::Trigger::StateOn) == 0) + offTriggers |= (state & TSShape::Trigger::StateMask); + } + + // We now know which states are turned off, set invert on all those (including when turned on) + for (int iTrigger = 0; iTrigger < seq.numTriggers; iTrigger++) + { + if (shape->triggers[seq.firstTrigger + iTrigger].state & offTriggers) + shape->triggers[seq.firstTrigger + iTrigger].state |= TSShape::Trigger::InvertOnReverse; + } +} + +//----------------------------------------------------------------------------- + +void TSShapeLoader::sortDetails() +{ + // Sort objects by: transparency, material index and node index + + + // Insert NULL meshes where required + for (int iSub = 0; iSub < subshapes.size(); iSub++) + { + Vector validDetails; + shape->getSubShapeDetails(iSub, validDetails); + + for (int iDet = 0; iDet < validDetails.size(); iDet++) + { + TSShape::Detail &detail = shape->details[validDetails[iDet]]; + if (detail.subShapeNum >= 0) + detail.objectDetailNum = iDet; + + for (int iObj = shape->subShapeFirstObject[iSub]; + iObj < (shape->subShapeFirstObject[iSub] + shape->subShapeNumObjects[iSub]); + iObj++) + { + TSShape::Object &object = shape->objects[iObj]; + + // Insert a NULL mesh for this detail level if required (ie. if the + // object does not already have a mesh with an equal or higher detail) + S32 meshIndex = (iDet < object.numMeshes) ? iDet : object.numMeshes-1; + + if (appMeshes[object.startMeshIndex + meshIndex]->detailSize < shape->details[iDet].size) + { + // Add a NULL mesh + appMeshes.insert(object.startMeshIndex + iDet, NULL); + object.numMeshes++; + + // Fixup the start index for the other objects + for (int k = iObj+1; k < shape->objects.size(); k++) + shape->objects[k].startMeshIndex++; + } + } + } + } +} + +// Install into the TSShape, the shape is expected to be empty. +// Data is not copied, the TSShape is modified to point to memory +// managed by this object. This object is also bound to the TSShape +// object and will be deleted when it's deleted. +void TSShapeLoader::install() +{ + // Arrays that are filled in by ts shape init, but need + // to be allocated beforehand. + shape->subShapeFirstTranslucentObject.setSize(shape->subShapeFirstObject.size()); + + // Construct TS sub-meshes + shape->meshes.setSize(appMeshes.size()); + for (U32 m = 0; m < appMeshes.size(); m++) + shape->meshes[m] = appMeshes[m] ? appMeshes[m]->constructTSMesh() : NULL; + + // Remove empty meshes and objects + for (S32 iObj = shape->objects.size()-1; iObj >= 0; iObj--) + { + TSShape::Object& obj = shape->objects[iObj]; + for (S32 iMesh = obj.numMeshes-1; iMesh >= 0; iMesh--) + { + TSMesh *mesh = shape->meshes[obj.startMeshIndex + iMesh]; + + if (mesh && !mesh->primitives.size()) + { + S32 oldMeshCount = obj.numMeshes; + destructInPlace(mesh); + shape->removeMeshFromObject(iObj, iMesh); + iMesh -= (oldMeshCount - obj.numMeshes - 1); // handle when more than one mesh is removed + } + } + + if (!obj.numMeshes) + shape->removeObject(shape->getName(obj.nameIndex)); + } + + // Add a dummy object if needed so the shape loads and renders ok + if (!shape->details.size()) + { + shape->addDetail("detail", 2, 0); + shape->subShapeNumObjects.last() = 1; + + shape->meshes.push_back(NULL); + + shape->objects.increment(); + shape->objects.last().nameIndex = shape->addName("dummy"); + shape->objects.last().nodeIndex = 0; + shape->objects.last().startMeshIndex = 0; + shape->objects.last().numMeshes = 1; + + shape->objectStates.increment(); + shape->objectStates.last().frameIndex = 0; + shape->objectStates.last().matFrameIndex = 0; + shape->objectStates.last().vis = 1.0f; + } + + // Update smallest visible detail + shape->mSmallestVisibleDL = -1; + shape->mSmallestVisibleSize = 999999; + for (S32 i = 0; i < shape->details.size(); i++) + { + if ((shape->details[i].size >= 0) && + (shape->details[i].size < shape->mSmallestVisibleSize)) + { + shape->mSmallestVisibleDL = i; + shape->mSmallestVisibleSize = shape->details[i].size; + } + } + + computeBounds(shape->bounds); + if (!shape->bounds.isValidBox()) + shape->bounds = Box3F(1.0f); + + shape->bounds.getCenter(&shape->center); + shape->radius = (shape->bounds.maxExtents - shape->center).len(); + shape->tubeRadius = shape->radius; + + shape->init(); +} + +void TSShapeLoader::computeBounds(Box3F& bounds) +{ + // Compute the box that encloses the model geometry + bounds = Box3F::Invalid; + + // Use bounds node geometry if present + if ( boundsNode && boundsNode->getNumMesh() ) + { + for (S32 iMesh = 0; iMesh < boundsNode->getNumMesh(); iMesh++) + { + AppMesh* mesh = boundsNode->getMesh( iMesh ); + if ( !mesh ) + continue; + + Box3F meshBounds; + mesh->computeBounds( meshBounds ); + if ( meshBounds.isValidBox() ) + bounds.intersect( meshBounds ); + } + } + else + { + // Compute bounds based on all geometry in the model + for (S32 iMesh = 0; iMesh < appMeshes.size(); iMesh++) + { + AppMesh* mesh = appMeshes[iMesh]; + if ( !mesh ) + continue; + + Box3F meshBounds; + mesh->computeBounds( meshBounds ); + if ( meshBounds.isValidBox() ) + bounds.intersect( meshBounds ); + } + } +} + +TSShapeLoader::~TSShapeLoader() +{ + clearNodeTransformCache(); + + // Clear shared AppMaterial list + for (int iMat = 0; iMat < AppMesh::appMaterials.size(); iMat++) + delete AppMesh::appMaterials[iMat]; + AppMesh::appMaterials.clear(); + + // Delete Subshapes + delete boundsNode; + for (int iSub = 0; iSub < subshapes.size(); iSub++) + delete subshapes[iSub]; + + // Delete AppSequences + for (int iSeq = 0; iSeq < appSequences.size(); iSeq++) + delete appSequences[iSeq]; + appSequences.clear(); +} diff --git a/Engine/source/ts/loader/tsShapeLoader.h b/Engine/source/ts/loader/tsShapeLoader.h new file mode 100644 index 000000000..83e9db0ca --- /dev/null +++ b/Engine/source/ts/loader/tsShapeLoader.h @@ -0,0 +1,183 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TSSHAPE_LOADER_H_ +#define _TSSHAPE_LOADER_H_ + +#ifndef _MMATH_H_ +#include "math/mMath.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _TSSHAPE_H_ +#include "ts/tsShape.h" +#endif +#ifndef _APPNODE_H_ +#include "ts/loader/appNode.h" +#endif +#ifndef _APPMESH_H_ +#include "ts/loader/appMesh.h" +#endif +#ifndef _APPSEQUENCE_H_ +#include "ts/loader/appSequence.h" +#endif + +class TSShapeLoader +{ + +public: + enum eLoadPhases + { + Load_ReadFile = 0, + Load_ParseFile, + Load_ExternalRefs, + Load_EnumerateScene, + Load_GenerateSubshapes, + Load_GenerateObjects, + Load_GenerateDefaultStates, + Load_GenerateSkins, + Load_GenerateMaterials, + Load_GenerateSequences, + Load_InitShape, + NumLoadPhases, + Load_Complete = NumLoadPhases + }; + + static void updateProgress(int major, const char* msg, int numMinor=0, int minor=0); + +protected: + struct Subshape + { + Vector branches; ///< Shape branches + Vector objMeshes; ///< Object meshes for this subshape + Vector objNodes; ///< AppNode indices with objects attached + + ~Subshape() + { + // Delete children + for (S32 i = 0; i < branches.size(); i++) + delete branches[i]; + } + }; + +public: + static const F32 DefaultTime; + static const double MinFrameRate; + static const double MaxFrameRate; + static const double AppGroundFrameRate; + +protected: + // Variables used during loading that must be held until the shape is deleted + TSShape* shape; + Vector appMeshes; + + // Variables used during loading, but that can be discarded afterwards + static Torque::Path shapePath; + + AppNode* boundsNode; + Vector appNodes; ///< Nodes in the loaded shape + Vector appSequences; + + Vector subshapes; + + Vector nodeRotCache; + Vector nodeTransCache; + Vector nodeScaleRotCache; + Vector nodeScaleCache; + + Point3F shapeOffset; ///< Offset used to translate the shape origin + + //-------------------------------------------------------------------------- + + // Collect the nodes, objects and sequences for the scene + virtual void enumerateScene() = 0; + bool processNode(AppNode* node); + virtual bool ignoreNode(const String& name) { return false; } + virtual bool ignoreMesh(const String& name) { return false; } + + void addSkin(AppMesh* mesh); + void addDetailMesh(AppMesh* mesh); + void addSubshape(AppNode* node); + void addObject(AppMesh* mesh, S32 nodeIndex, S32 subShapeNum); + + // Node transform methods + MatrixF getLocalNodeMatrix(AppNode* node, F32 t); + void generateNodeTransform(AppNode* node, F32 t, bool blend, F32 referenceTime, + QuatF& rot, Point3F& trans, QuatF& srot, Point3F& scale); + + virtual void computeBounds(Box3F& bounds); + + // Create objects, materials and sequences + void recurseSubshape(AppNode* appNode, S32 parentIndex, bool recurseChildren); + + void generateSubshapes(); + void generateObjects(); + void generateSkins(); + void generateDefaultStates(); + void generateObjectState(TSShape::Object& obj, F32 t, bool addFrame, bool addMatFrame); + void generateFrame(TSShape::Object& obj, F32 t, bool addFrame, bool addMatFrame); + + void generateMaterialList(); + + void generateSequences(); + + // Determine what is actually animated in the sequence + void setNodeMembership(TSShape::Sequence& seq, const AppSequence* appSeq); + void setRotationMembership(TSShape::Sequence& seq); + void setTranslationMembership(TSShape::Sequence& seq); + void setScaleMembership(TSShape::Sequence& seq); + void setObjectMembership(TSShape::Sequence& seq, const AppSequence* appSeq); + + // Manage a cache of all node transform elements for the sequence + void clearNodeTransformCache(); + void fillNodeTransformCache(TSShape::Sequence& seq, const AppSequence* appSeq); + + // Add node transform elements + void addNodeRotation(QuatF& rot, bool defaultVal); + void addNodeTranslation(Point3F& trans, bool defaultVal); + void addNodeUniformScale(F32 scale); + void addNodeAlignedScale(Point3F& scale); + void addNodeArbitraryScale(QuatF& qrot, Point3F& scale); + + // Generate animation data + void generateNodeAnimation(TSShape::Sequence& seq); + void generateObjectAnimation(TSShape::Sequence& seq, const AppSequence* appSeq); + void generateGroundAnimation(TSShape::Sequence& seq, const AppSequence* appSeq); + void generateFrameTriggers(TSShape::Sequence& seq, const AppSequence* appSeq); + + // Shape construction + void sortDetails(); + void install(); + +public: + TSShapeLoader() : boundsNode(0) { } + virtual ~TSShapeLoader(); + + static const Torque::Path& getShapePath() { return shapePath; } + + static void zapScale(MatrixF& mat); + + TSShape* generateShape(const Torque::Path& path); +}; + +#endif // _TSSHAPE_LOADER_H_ diff --git a/Engine/source/ts/tsAnimate.cpp b/Engine/source/ts/tsAnimate.cpp new file mode 100644 index 000000000..e2332b699 --- /dev/null +++ b/Engine/source/ts/tsAnimate.cpp @@ -0,0 +1,1123 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "ts/tsShapeInstance.h" + +//---------------------------------------------------------------------------------- +// some utility functions +//------------------------------------------------------------------------------------- + +S32 QSORT_CALLBACK FN_CDECL compareThreads( const void* e1, const void* e2) +{ + const TSThread * th1 = *(const TSThread**)e1; + const TSThread * th2 = *(const TSThread**)e2; + return (*th1 < *th2); +} + +void TSShapeInstance::sortThreads() +{ + PROFILE_SCOPE( TSShapeInstance_sortThreads ); + dQsort(mThreadList.address(),mThreadList.size(),sizeof(TSThread*),compareThreads); + dQsort(mTransitionThreads.address(),mTransitionThreads.size(),sizeof(TSThread*),compareThreads); +} + +void TSShapeInstance::setDirty(U32 dirty) +{ + AssertFatal((dirty & AllDirtyMask) == dirty,"TSShapeInstance::setDirty: illegal dirty flags"); + for (S32 i=0; isubShapeFirstNode.size(); i++) + mDirtyFlags[i] |= dirty; +} + +void TSShapeInstance::clearDirty(U32 dirty) +{ + AssertFatal((dirty & AllDirtyMask) == dirty,"TSShapeInstance::clearDirty: illegal dirty flags"); + for (S32 i=0; isubShapeFirstNode.size(); i++) + mDirtyFlags[i] &= ~dirty; +} + +//------------------------------------------------------------------------------------- +// Animate nodes +//------------------------------------------------------------------------------------- + +void TSShapeInstance::animateNodes(S32 ss) +{ + PROFILE_SCOPE( TSShapeInstance_animateNodes ); + + if (!mShape->nodes.size()) + return; + + // @todo: When a node is added, we need to make sure to resize the nodeTransforms array as well + mNodeTransforms.setSize(mShape->nodes.size()); + + // temporary storage for node transforms + smNodeCurrentRotations.setSize(mShape->nodes.size()); + smNodeCurrentTranslations.setSize(mShape->nodes.size()); + smNodeLocalTransforms.setSize(mShape->nodes.size()); + smRotationThreads.setSize(mShape->nodes.size()); + smTranslationThreads.setSize(mShape->nodes.size()); + + TSIntegerSet rotBeenSet; + TSIntegerSet tranBeenSet; + TSIntegerSet scaleBeenSet; + rotBeenSet.setAll(mShape->nodes.size()); + tranBeenSet.setAll(mShape->nodes.size()); + scaleBeenSet.setAll(mShape->nodes.size()); + smNodeLocalTransformDirty.clearAll(); + + S32 i,j,nodeIndex,a,b,start,end,firstBlend = mThreadList.size(); + for (i=0; igetSequence()->isBlend()) + { + // blend sequences need default (if not set by other sequence) + // break rather than continue because the rest will be blends too + firstBlend = i; + break; + } + rotBeenSet.takeAway(th->getSequence()->rotationMatters); + tranBeenSet.takeAway(th->getSequence()->translationMatters); + scaleBeenSet.takeAway(th->getSequence()->scaleMatters); + } + rotBeenSet.takeAway(mCallbackNodes); + rotBeenSet.takeAway(mHandsOffNodes); + rotBeenSet.overlap(mMaskRotationNodes); + + TSIntegerSet maskPosNodes=mMaskPosXNodes; + maskPosNodes.overlap(mMaskPosYNodes); + maskPosNodes.overlap(mMaskPosZNodes); + tranBeenSet.overlap(maskPosNodes); + + tranBeenSet.takeAway(mCallbackNodes); + tranBeenSet.takeAway(mHandsOffNodes); + // can't add masked nodes since x, y, & z masked separately... + // we'll set default regardless of mask status + + // all the nodes marked above need to have the default transform + a = mShape->subShapeFirstNode[ss]; + b = a + mShape->subShapeNumNodes[ss]; + for (i=a; idefaultRotations[i].getQuatF(&smNodeCurrentRotations[i]); + smRotationThreads[i] = NULL; + } + if (tranBeenSet.test(i)) + { + smNodeCurrentTranslations[i] = mShape->defaultTranslations[i]; + smTranslationThreads[i] = NULL; + } + } + + // don't want a transform in these cases... + rotBeenSet.overlap(mHandsOffNodes); + rotBeenSet.overlap(mCallbackNodes); + tranBeenSet.takeAway(maskPosNodes); + tranBeenSet.overlap(mHandsOffNodes); + tranBeenSet.overlap(mCallbackNodes); + + // default scale + if (scaleCurrentlyAnimated()) + handleDefaultScale(a,b,scaleBeenSet); + + // handle non-blend sequences + for (i=0; igetSequence()->rotationMatters.start(); + end = b; + for (nodeIndex=start; nodeIndexgetSequence()->rotationMatters.next(nodeIndex), j++) + { + // skip nodes outside of this detail + if (nodeIndexgetRotation(*th->getSequence(),th->keyNum1,j,&q1); + mShape->getRotation(*th->getSequence(),th->keyNum2,j,&q2); + TSTransform::interpolate(q1,q2,th->keyPos,&smNodeCurrentRotations[nodeIndex]); + rotBeenSet.set(nodeIndex); + smRotationThreads[nodeIndex] = th; + } + } + + j=0; + start = th->getSequence()->translationMatters.start(); + end = b; + for (nodeIndex=start; nodeIndexgetSequence()->translationMatters.next(nodeIndex), j++) + { + if (nodeIndexgetTranslation(*th->getSequence(),th->keyNum1,j); + const Point3F & p2 = mShape->getTranslation(*th->getSequence(),th->keyNum2,j); + TSTransform::interpolate(p1,p2,th->keyPos,&smNodeCurrentTranslations[nodeIndex]); + smTranslationThreads[nodeIndex] = th; + } + tranBeenSet.set(nodeIndex); + } + } + + if (scaleCurrentlyAnimated()) + handleAnimatedScale(th,a,b,scaleBeenSet); + } + + // compute transforms + for (i=a; i=start && nodeIndexsetNodeTransform(this, nodeIndex, smNodeLocalTransforms[nodeIndex]); + smNodeLocalTransformDirty.set(nodeIndex); + } + } + + // handle blend sequences + for (i=firstBlend; iblendDisabled) + continue; + + handleBlendSequence(th,a,b); + } + + // transitions... + if (inTransition()) + handleTransitionNodes(a,b); + + // multiply transforms... + for (i=a; inodes[i].parentIndex; + if (parentIdx < 0) + mNodeTransforms[i] = smNodeLocalTransforms[i]; + else + mNodeTransforms[i].mul(mNodeTransforms[parentIdx],smNodeLocalTransforms[i]); + } +} + +void TSShapeInstance::handleDefaultScale(S32 a, S32 b, TSIntegerSet & scaleBeenSet) +{ + // set default scale values (i.e., identity) and do any initialization + // relating to animated scale (since scale normally not animated) + + smScaleThreads.setSize(mShape->nodes.size()); + scaleBeenSet.takeAway(mCallbackNodes); + scaleBeenSet.takeAway(mHandsOffNodes); + if (animatesUniformScale()) + { + smNodeCurrentUniformScales.setSize(mShape->nodes.size()); + for (S32 i=a; inodes.size()); + for (S32 i=a; inodes.size()); + for (S32 i=a; i need to do proper affine decomposition here + smNodeCurrentTranslations[i] = smNodeLocalTransforms[i].getPosition(); + smNodeCurrentRotations[i].set(smNodeLocalTransforms[i]); + } + else + { + // Scale is identity => can do a cheap decomposition + smNodeCurrentTranslations[i] = smNodeLocalTransforms[i].getPosition(); + smNodeCurrentRotations[i].set(smNodeLocalTransforms[i]); + } + } + } +} + +void TSShapeInstance::handleTransitionNodes(S32 a, S32 b) +{ + TSIntegerSet transitionNodes; + updateTransitionNodeTransforms(transitionNodes); + + S32 nodeIndex; + S32 start = mTransitionRotationNodes.start(); + S32 end = b; + for (nodeIndex=start; nodeIndextransitionData.inTransition ? thread : NULL; + if (!thread) + { + // if not controlled by a sequence in transition then there must be + // some other thread out there that used to control us that is in + // transition now...use that thread to control interpolation + for (S32 i=0; itransitionData.oldRotationNodes.test(nodeIndex) || mTransitionThreads[i]->getSequence()->rotationMatters.test(nodeIndex)) + { + thread = mTransitionThreads[i]; + break; + } + } + AssertFatal(thread!=NULL,"TSShapeInstance::handleRotTransitionNodes (rotation)"); + } + QuatF tmpQ; + TSTransform::interpolate(mNodeReferenceRotations[nodeIndex].getQuatF(&tmpQ),smNodeCurrentRotations[nodeIndex],thread->transitionData.pos,&smNodeCurrentRotations[nodeIndex]); + } + + // then translation + start = mTransitionTranslationNodes.start(); + end = b; + for (nodeIndex=start; nodeIndextransitionData.inTransition ? thread : NULL; + if (!thread) + { + // if not controlled by a sequence in transition then there must be + // some other thread out there that used to control us that is in + // transition now...use that thread to control interpolation + for (S32 i=0; itransitionData.oldTranslationNodes.test(nodeIndex) || mTransitionThreads[i]->getSequence()->translationMatters.test(nodeIndex)) + { + thread = mTransitionThreads[i]; + break; + } + } + AssertFatal(thread!=NULL,"TSShapeInstance::handleTransitionNodes (translation)."); + } + Point3F & p = smNodeCurrentTranslations[nodeIndex]; + Point3F & p1 = mNodeReferenceTranslations[nodeIndex]; + Point3F & p2 = p; + F32 k = thread->transitionData.pos; + p.x = p1.x + k * (p2.x-p1.x); + p.y = p1.y + k * (p2.y-p1.y); + p.z = p1.z + k * (p2.z-p1.z); + } + + // then scale... + if (scaleCurrentlyAnimated()) + { + start = mTransitionScaleNodes.start(); + end = b; + for (nodeIndex=start; nodeIndextransitionData.inTransition ? thread : NULL; + if (!thread) + { + // if not controlled by a sequence in transition then there must be + // some other thread out there that used to control us that is in + // transition now...use that thread to control interpolation + for (S32 i=0; itransitionData.oldScaleNodes.test(nodeIndex) || mTransitionThreads[i]->getSequence()->scaleMatters.test(nodeIndex)) + { + thread = mTransitionThreads[i]; + break; + } + } + AssertFatal(thread!=NULL,"TSShapeInstance::handleTransitionNodes (scale)."); + } + if (animatesUniformScale()) + smNodeCurrentUniformScales[nodeIndex] += thread->transitionData.pos * (mNodeReferenceUniformScales[nodeIndex]-smNodeCurrentUniformScales[nodeIndex]); + else if (animatesAlignedScale()) + TSTransform::interpolate(mNodeReferenceScaleFactors[nodeIndex],smNodeCurrentAlignedScales[nodeIndex],thread->transitionData.pos,&smNodeCurrentAlignedScales[nodeIndex]); + else + { + QuatF q; + TSTransform::interpolate(mNodeReferenceScaleFactors[nodeIndex],smNodeCurrentArbitraryScales[nodeIndex].mScale,thread->transitionData.pos,&smNodeCurrentArbitraryScales[nodeIndex].mScale); + TSTransform::interpolate(mNodeReferenceArbitraryScaleRots[nodeIndex].getQuatF(&q),smNodeCurrentArbitraryScales[nodeIndex].mRotate,thread->transitionData.pos,&smNodeCurrentArbitraryScales[nodeIndex].mRotate); + } + } + } + + // update transforms for transition nodes + start = transitionNodes.start(); + end = b; + for (nodeIndex=start; nodeIndexgetSequence()->scaleMatters.start(); + S32 end = b; + + // code the scale conversion (might need to "upgrade" from uniform to arbitrary, e.g.) + // code uniform, aligned, and arbitrary as 0,1, and 2, respectively, + // with sequence coding in first two bits, shape coding in next two bits + S32 code = 0; + if (thread->getSequence()->animatesAlignedScale()) + code += 1; + else if (thread->getSequence()->animatesArbitraryScale()) + code += 2; + if (animatesAlignedScale()) + code += 4; + if (animatesArbitraryScale()) + code += 8; + + F32 uniformScale = 1.0f; + Point3F alignedScale(0.0f, 0.0f, 0.0f); + TSScale arbitraryScale; + for (S32 nodeIndex=start; nodeIndexgetSequence()->scaleMatters.next(nodeIndex), j++) + { + if (nodeIndex uniform + case 4: // uniform -> aligned + case 8: // uniform -> arbitrary + { + F32 s1 = mShape->getUniformScale(*thread->getSequence(),thread->keyNum1,j); + F32 s2 = mShape->getUniformScale(*thread->getSequence(),thread->keyNum2,j); + uniformScale = TSTransform::interpolate(s1,s2,thread->keyPos); + alignedScale.set(uniformScale,uniformScale,uniformScale); + break; + } + case 5: // aligned -> aligned + case 9: // aligned -> arbitrary + { + const Point3F & s1 = mShape->getAlignedScale(*thread->getSequence(),thread->keyNum1,j); + const Point3F & s2 = mShape->getAlignedScale(*thread->getSequence(),thread->keyNum2,j); + TSTransform::interpolate(s1,s2,thread->keyPos,&alignedScale); + break; + } + case 10: // arbitrary -> arbitary + { + TSScale s1,s2; + mShape->getArbitraryScale(*thread->getSequence(),thread->keyNum1,j,&s1); + mShape->getArbitraryScale(*thread->getSequence(),thread->keyNum2,j,&s2); + TSTransform::interpolate(s1,s2,thread->keyPos,&arbitraryScale); + break; + } + default: AssertFatal(0,"TSShapeInstance::handleAnimatedScale"); break; + } + + switch (code) + { + case 0: // uniform -> uniform + { + smNodeCurrentUniformScales[nodeIndex] = uniformScale; + break; + } + case 4: // uniform -> aligned + case 5: // aligned -> aligned + smNodeCurrentAlignedScales[nodeIndex] = alignedScale; + break; + case 8: // uniform -> arbitrary + case 9: // aligned -> arbitrary + { + smNodeCurrentArbitraryScales[nodeIndex].identity(); + smNodeCurrentArbitraryScales[nodeIndex].mScale = alignedScale; + break; + } + case 10: // arbitrary -> arbitary + { + smNodeCurrentArbitraryScales[nodeIndex] = arbitraryScale; + break; + } + default: AssertFatal(0,"TSShapeInstance::handleAnimatedScale"); break; + } + smScaleThreads[nodeIndex] = thread; + scaleBeenSet.set(nodeIndex); + } + } +} + +void TSShapeInstance::handleMaskedPositionNode(TSThread * th, S32 nodeIndex, S32 offset) +{ + const Point3F & p1 = mShape->getTranslation(*th->getSequence(),th->keyNum1,offset); + const Point3F & p2 = mShape->getTranslation(*th->getSequence(),th->keyNum2,offset); + Point3F p; + TSTransform::interpolate(p1,p2,th->keyPos,&p); + + if (!mMaskPosXNodes.test(nodeIndex)) + smNodeCurrentTranslations[nodeIndex].x = p.x; + + if (!mMaskPosYNodes.test(nodeIndex)) + smNodeCurrentTranslations[nodeIndex].y = p.y; + + if (!mMaskPosZNodes.test(nodeIndex)) + smNodeCurrentTranslations[nodeIndex].z = p.z; +} + +void TSShapeInstance::handleBlendSequence(TSThread * thread, S32 a, S32 b) +{ + S32 jrot=0; + S32 jtrans=0; + S32 jscale=0; + TSIntegerSet nodeMatters = thread->getSequence()->translationMatters; + nodeMatters.overlap(thread->getSequence()->rotationMatters); + nodeMatters.overlap(thread->getSequence()->scaleMatters); + S32 start = nodeMatters.start(); + S32 end = b; + for (S32 nodeIndex=start; nodeIndexgetSequence()->rotationMatters.test(nodeIndex)) + jrot++; + if (thread->getSequence()->translationMatters.test(nodeIndex)) + jtrans++; + if (thread->getSequence()->scaleMatters.test(nodeIndex)) + jscale++; + continue; + } + + MatrixF mat(true); + if (thread->getSequence()->rotationMatters.test(nodeIndex)) + { + QuatF q1,q2; + mShape->getRotation(*thread->getSequence(),thread->keyNum1,jrot,&q1); + mShape->getRotation(*thread->getSequence(),thread->keyNum2,jrot,&q2); + QuatF quat; + TSTransform::interpolate(q1,q2,thread->keyPos,&quat); + TSTransform::setMatrix(quat,&mat); + jrot++; + } + + if (thread->getSequence()->translationMatters.test(nodeIndex)) + { + const Point3F & p1 = mShape->getTranslation(*thread->getSequence(),thread->keyNum1,jtrans); + const Point3F & p2 = mShape->getTranslation(*thread->getSequence(),thread->keyNum2,jtrans); + Point3F p; + TSTransform::interpolate(p1,p2,thread->keyPos,&p); + mat.setColumn(3,p); + jtrans++; + } + + if (thread->getSequence()->scaleMatters.test(nodeIndex)) + { + if (thread->getSequence()->animatesUniformScale()) + { + F32 s1 = mShape->getUniformScale(*thread->getSequence(),thread->keyNum1,jscale); + F32 s2 = mShape->getUniformScale(*thread->getSequence(),thread->keyNum2,jscale); + F32 scale = TSTransform::interpolate(s1,s2,thread->keyPos); + TSTransform::applyScale(scale,&mat); + } + else if (animatesAlignedScale()) + { + Point3F s1 = mShape->getAlignedScale(*thread->getSequence(),thread->keyNum1,jscale); + Point3F s2 = mShape->getAlignedScale(*thread->getSequence(),thread->keyNum2,jscale); + Point3F scale; + TSTransform::interpolate(s1,s2,thread->keyPos,&scale); + TSTransform::applyScale(scale,&mat); + } + else + { + TSScale s1,s2; + mShape->getArbitraryScale(*thread->getSequence(),thread->keyNum1,jscale,&s1); + mShape->getArbitraryScale(*thread->getSequence(),thread->keyNum2,jscale,&s2); + TSScale scale; + TSTransform::interpolate(s1,s2,thread->keyPos,&scale); + TSTransform::applyScale(scale,&mat); + } + jscale++; + } + + // apply blend transform + smNodeLocalTransforms[nodeIndex].mul(mat); + smNodeLocalTransformDirty.set(nodeIndex); + } +} + +//------------------------------------------------------------------------------------- +// Other Animation: +//------------------------------------------------------------------------------------- + +void TSShapeInstance::animateVisibility(S32 ss) +{ + PROFILE_SCOPE( TSShapeInstance_animateVisibility ); + + S32 i; + if (!mMeshObjects.size()) + return; + + // find out who needs default values set + TSIntegerSet beenSet; + beenSet.setAll(mMeshObjects.size()); + for (i=0; igetSequence()->visMatters); + + // set defaults + S32 a = mShape->subShapeFirstObject[ss]; + S32 b = a + mShape->subShapeNumObjects[ss]; + for (i=a; iobjectStates[i].vis; + } + + // go through each thread and set visibility on those objects that + // are not set yet and are controlled by that thread + for (i=0; igetSequence()->frameMatters; + objectMatters.overlap(th->getSequence()->matFrameMatters); + objectMatters.overlap(th->getSequence()->visMatters); + + // skip to beginning of this sub-shape + S32 j=0; + S32 start = objectMatters.start(); + S32 end = b; + for (S32 objectIndex = start; objectIndexgetSequence()->visMatters.test(objectIndex)) + { + F32 state1 = mShape->getObjectState(*th->getSequence(),th->keyNum1,j).vis; + F32 state2 = mShape->getObjectState(*th->getSequence(),th->keyNum2,j).vis; + if ((state1-state2) * (state1-state2) > 0.99f) + // goes from 0 to 1 -- discreet jump + mMeshObjects[objectIndex].visible = th->keyPos<0.5f ? state1 : state2; + else + // interpolate between keyframes when visibility change is gradual + mMeshObjects[objectIndex].visible = (1.0f-th->keyPos) * state1 + th->keyPos * state2; + + // record change so that later threads don't over-write us... + beenSet.set(objectIndex); + } + } + } +} + +void TSShapeInstance::animateFrame(S32 ss) +{ + PROFILE_SCOPE( TSShapeInstance_animateFrame ); + + S32 i; + if (!mMeshObjects.size()) + return; + + // find out who needs default values set + TSIntegerSet beenSet; + beenSet.setAll(mMeshObjects.size()); + for (i=0; igetSequence()->frameMatters); + + // set defaults + S32 a = mShape->subShapeFirstObject[ss]; + S32 b = a + mShape->subShapeNumObjects[ss]; + for (i=a; iobjectStates[i].frameIndex; + + // go through each thread and set frame on those objects that + // are not set yet and are controlled by that thread + for (i=0; igetSequence()->frameMatters; + objectMatters.overlap(th->getSequence()->matFrameMatters); + objectMatters.overlap(th->getSequence()->visMatters); + + // skip to beginning of this sub-shape + S32 j=0; + S32 start = objectMatters.start(); + S32 end = b; + for (S32 objectIndex = start; objectIndexgetSequence()->frameMatters.test(objectIndex)) + { + S32 key = (th->keyPos<0.5f) ? th->keyNum1 : th->keyNum2; + mMeshObjects[objectIndex].frame = mShape->getObjectState(*th->getSequence(),key,j).frameIndex; + + // record change so that later threads don't over-write us... + beenSet.set(objectIndex); + } + } + } +} + +void TSShapeInstance::animateMatFrame(S32 ss) +{ + PROFILE_SCOPE( TSShapeInstance_animateMatFrame ); + + S32 i; + if (!mMeshObjects.size()) + return; + + // find out who needs default values set + TSIntegerSet beenSet; + beenSet.setAll(mMeshObjects.size()); + for (i=0; igetSequence()->matFrameMatters); + + // set defaults + S32 a = mShape->subShapeFirstObject[ss]; + S32 b = a + mShape->subShapeNumObjects[ss]; + for (i=a; iobjectStates[i].matFrameIndex; + + // go through each thread and set matFrame on those objects that + // are not set yet and are controlled by that thread + for (i=0; igetSequence()->frameMatters; + objectMatters.overlap(th->getSequence()->matFrameMatters); + objectMatters.overlap(th->getSequence()->visMatters); + + // skip to beginining of this sub-shape + S32 j=0; + S32 start = objectMatters.start(); + S32 end = b; + for (S32 objectIndex = start; objectIndexgetSequence()->matFrameMatters.test(objectIndex)) + { + S32 key = (th->keyPos<0.5f) ? th->keyNum1 : th->keyNum2; + mMeshObjects[objectIndex].matFrame = mShape->getObjectState(*th->getSequence(),key,j).matFrameIndex; + + // record change so that later threads don't over-write us... + beenSet.set(objectIndex); + } + } + } +} + +//------------------------------------------------------------------------------------- +// Animate (and initialize detail levels) +//------------------------------------------------------------------------------------- + +void TSShapeInstance::animate(S32 dl) +{ + PROFILE_SCOPE( TSShapeInstance_animate ); + + if (dl==-1) + // nothing to do + return; + + S32 ss = mShape->details[dl].subShapeNum; + + // this is a billboard detail... + if (ss<0) + return; + + U32 dirtyFlags = mDirtyFlags[ss]; + + if (dirtyFlags & ThreadDirty) + sortThreads(); + + // animate nodes? + if (dirtyFlags & TransformDirty) + animateNodes(ss); + + // animate objects? + if (dirtyFlags & VisDirty) + animateVisibility(ss); + + if (dirtyFlags & FrameDirty) + animateFrame(ss); + + if (dirtyFlags & MatFrameDirty) + animateMatFrame(ss); + + mDirtyFlags[ss] = 0; +} + +void TSShapeInstance::animateNodeSubtrees(bool forceFull) +{ + // animate all the nodes for all the detail levels... + + if (forceFull) + // force transforms to animate + setDirty(TransformDirty); + + for (S32 i=0; isubShapeNumNodes.size(); i++) + { + if (mDirtyFlags[i] & TransformDirty) + { + animateNodes(i); + mDirtyFlags[i] &= ~TransformDirty; + } + } +} + +void TSShapeInstance::animateSubtrees(bool forceFull) +{ + // animate all the subtrees + + if (forceFull) + // force full animate + setDirty(AllDirtyMask); + + for (S32 i=0; isubShapeNumNodes.size(); i++) + { + if (mDirtyFlags[i] & TransformDirty) + { + animate(i); + mDirtyFlags[i] = 0; + } + } +} + +void TSShapeInstance::addPath(TSThread *gt, F32 start, F32 end, MatrixF *mat) +{ + // never get here while in transition... + AssertFatal(!gt->transitionData.inTransition,"TSShapeInstance::addPath"); + + if (!mat) + mat = &mGroundTransform; + + MatrixF startInvM; + gt->getGround(start,&startInvM); + startInvM.inverse(); + + MatrixF endM; + gt->getGround(end,&endM); + + MatrixF addM; + addM.mul(startInvM,endM); + endM.mul(*mat,addM); + *mat = endM; +} + +bool TSShapeInstance::initGround() +{ + for (S32 i=0; itransitionData.inTransition && th->getSequence()->numGroundFrames>0) + { + mGroundThread = th; + return true; + } + } + return false; +} + +void TSShapeInstance::animateGround() +{ + mGroundTransform.identity(); + + // pick thread which controlls ground transform + // if we haven't already... + if (!mGroundThread && !initGround()) + return; + + S32 & loop = mGroundThread->path.loop; + F32 & start = mGroundThread->path.start; + F32 & end = mGroundThread->path.end; + + // accumulate path transform + if (loop>0) + { + addPath(mGroundThread,start,1.0f); + while (--loop) + addPath(mGroundThread,0.0f,1.0f); + addPath(mGroundThread,0.0f,end); + } + else if (loop<0) + { + addPath(mGroundThread,start,0.0f); + while (++loop) + addPath(mGroundThread,1.0f,0.0f); + addPath(mGroundThread,1.0f,end); + } + else + addPath(mGroundThread,start,end); + start = end; // in case user tries to animateGround twice +} + +void TSShapeInstance::deltaGround(TSThread * thread, F32 start, F32 end, MatrixF * mat) +{ + if (!mat) + mat = &mGroundTransform; + + mat->identity(); + if (thread->transitionData.inTransition) + return; + + F32 invDuration = 1.0f / thread->getDuration(); + start *= invDuration; + end *= invDuration; + + addPath(thread,start,end,mat); +} + +// Simple case of above- get ground delta at given position in unit range +void TSShapeInstance::deltaGround1(TSThread * thread, F32 start, F32 end, MatrixF& mat) +{ + mat.identity(); + if (thread->transitionData.inTransition) + return; + addPath(thread, start, end, &mat); +} + +void TSShapeInstance::setTriggerState(U32 stateNum, bool on) +{ + AssertFatal(stateNum<=32 && stateNum>0,"TSShapeInstance::setTriggerState: state index out of range"); + + stateNum--; // stateNum externally 1..32, internally 0..31 + U32 bit = 1 << stateNum; + if (on) + mTriggerStates |= bit; + else + mTriggerStates &= ~bit; +} + +void TSShapeInstance::setTriggerStateBit(U32 stateBit, bool on) +{ + if (on) + mTriggerStates |= stateBit; + else + mTriggerStates &= ~stateBit; +} + +bool TSShapeInstance::getTriggerState(U32 stateNum, bool clearState) +{ + AssertFatal(stateNum<=32 && stateNum>0,"TSShapeInstance::getTriggerState: state index out of range"); + + stateNum--; // stateNum externally 1..32, internally 0..31 + U32 bit = 1 << stateNum; + bool ret = ((mTriggerStates & bit)!=0); + if (clearState) + mTriggerStates &= ~bit; + return ret; +} + +void TSShapeInstance::setNodeAnimationState(S32 nodeIndex, U32 animationState, TSCallback * callback) +{ + AssertFatal((animationState & ~(MaskNodeAll|MaskNodeHandsOff|MaskNodeCallback)) == 0,"TSShapeInstance::setNodeAnimationState (1)"); + + // don't handle callback nodes in this method + if (callback) + animationState |= MaskNodeCallback; + else + animationState &= ~MaskNodeCallback; + + // hands-off takes precedance + if (animationState & MaskNodeHandsOff) + animationState = MaskNodeHandsOff | MaskNodeBlend; + else if (animationState & MaskNodeCallback) + animationState = MaskNodeCallback | MaskNodeBlend; + + // if we're not changing anything then get out of here now + if (animationState == getNodeAnimationState(nodeIndex)) + return; + + setDirty(AllDirtyMask); + + if (animationState & MaskNodeAllButBlend) + { + if (animationState & MaskNodeRotation) + mMaskRotationNodes.set(nodeIndex); + if (animationState & MaskNodePosX) + mMaskPosXNodes.set(nodeIndex); + if (animationState & MaskNodePosY) + mMaskPosYNodes.set(nodeIndex); + if (animationState & MaskNodePosZ) + mMaskPosZNodes.set(nodeIndex); + } + else + { + // no masking clear out all the masking lists + mMaskRotationNodes.clear(nodeIndex); + mMaskPosXNodes.clear(nodeIndex); + mMaskPosYNodes.clear(nodeIndex); + mMaskPosZNodes.clear(nodeIndex); + } + + if (animationState & MaskNodeBlend) + mDisableBlendNodes.set(nodeIndex); + else + mDisableBlendNodes.clear(nodeIndex); + + if (animationState & MaskNodeHandsOff) + mHandsOffNodes.set(nodeIndex); + else + mHandsOffNodes.clear(nodeIndex); + + // clear out of node callbacks + for (S32 i=0; i=0 && dldetails.size(),"TSShapeInstance::buildPolyList"); + + // get subshape and object detail + const TSDetail * detail = &mShape->details[dl]; + S32 ss = detail->subShapeNum; + S32 od = detail->objectDetailNum; + + // This detail does not have any geometry. + if ( ss < 0 ) + return false; + + // nothing emitted yet... + bool emitted = false; + U32 surfaceKey = 0; + + S32 start = mShape->subShapeFirstObject[ss]; + S32 end = mShape->subShapeNumObjects[ss] + start; + if (startgetTransform(&initialMat,&initialScale); + + // set up for first object's node + MatrixF mat; + MatrixF scaleMat(true); + F32* p = scaleMat; + p[0] = initialScale.x; + p[5] = initialScale.y; + p[10] = initialScale.z; + const MatrixF *previousMat = &mMeshObjects[start].getTransform(); + mat.mul(initialMat,scaleMat); + mat.mul(*previousMat); + polyList->setTransform(&mat,Point3F(1, 1, 1)); + + // run through objects and collide + for (S32 i=start; i= mesh->object->numMeshes) + continue; + + if (&mesh->getTransform() != previousMat) + { + // different node from before, set up for this node + previousMat = &mesh->getTransform(); + + if (previousMat != NULL) + { + mat.mul(initialMat,scaleMat); + mat.mul(*previousMat); + polyList->setTransform(&mat,Point3F(1, 1, 1)); + } + } + // collide... + emitted |= mesh->buildPolyList(od,polyList,surfaceKey,mMaterialList); + } + + // restore original transform... + polyList->setTransform(&initialMat,initialScale); + } + + return emitted; +} + +bool TSShapeInstance::getFeatures(const MatrixF& mat, const Point3F& n, ConvexFeature* cf, S32 dl) +{ + // if dl==-1, nothing to do + if (dl==-1) + return false; + + AssertFatal(dl>=0 && dldetails.size(),"TSShapeInstance::buildPolyList"); + + // get subshape and object detail + const TSDetail * detail = &mShape->details[dl]; + S32 ss = detail->subShapeNum; + S32 od = detail->objectDetailNum; + + // nothing emitted yet... + bool emitted = false; + U32 surfaceKey = 0; + + S32 start = mShape->subShapeFirstObject[ss]; + S32 end = mShape->subShapeNumObjects[ss] + start; + if (start= mesh->object->numMeshes) + continue; + + if (&mesh->getTransform() != previousMat) + { + previousMat = &mesh->getTransform(); + final.mul(mat, *previousMat); + } + emitted |= mesh->getFeatures(od, final, n, cf, surfaceKey); + } + } + return emitted; +} + +bool TSShapeInstance::castRay(const Point3F & a, const Point3F & b, RayInfo * rayInfo, S32 dl) +{ + // if dl==-1, nothing to do + if (dl==-1) + return false; + + AssertFatal(dl>=0 && dldetails.size(),"TSShapeInstance::castRay"); + + // get subshape and object detail + const TSDetail * detail = &mShape->details[dl]; + S32 ss = detail->subShapeNum; + S32 od = detail->objectDetailNum; + + // This detail has no geometry to hit. + if ( ss < 0 ) + return false; + + S32 start = mShape->subShapeFirstObject[ss]; + S32 end = mShape->subShapeNumObjects[ss] + start; + RayInfo saveRay; + saveRay.t = 1.0f; + const MatrixF * saveMat = NULL; + bool found = false; + if (start= mesh->object->numMeshes) + continue; + + if (&mesh->getTransform() != previousMat) + { + // different node from before, set up for this node + previousMat = &mesh->getTransform(); + + if (previousMat != NULL) + { + mat = *previousMat; + mat.inverse(); + mat.mulP(a,&ta); + mat.mulP(b,&tb); + } + } + // collide... + if (mesh->castRay(od,ta,tb,rayInfo, mMaterialList)) + { + if (!rayInfo) + return true; + + if (rayInfo->t <= saveRay.t) + { + saveRay = *rayInfo; + saveMat = previousMat; + } + found = true; + } + } + } + + // collide with any skins for this detail level... + // TODO: if ever... + + // finalize the deal... + if (found) + { + *rayInfo = saveRay; + saveMat->mulV(rayInfo->normal); + rayInfo->point = b-a; + rayInfo->point *= rayInfo->t; + rayInfo->point += a; + } + return found; +} + +bool TSShapeInstance::castRayRendered(const Point3F & a, const Point3F & b, RayInfo * rayInfo, S32 dl) +{ + // if dl==-1, nothing to do + if (dl==-1) + return false; + + AssertFatal(dl>=0 && dldetails.size(),"TSShapeInstance::castRayRendered"); + + // get subshape and object detail + const TSDetail * detail = &mShape->details[dl]; + S32 ss = detail->subShapeNum; + S32 od = detail->objectDetailNum; + + if ( ss == -1 ) + return false; + + S32 start = mShape->subShapeFirstObject[ss]; + S32 end = mShape->subShapeNumObjects[ss] + start; + RayInfo saveRay; + saveRay.t = 1.0f; + const MatrixF * saveMat = NULL; + bool found = false; + if (start= mesh->object->numMeshes) + continue; + + if (&mesh->getTransform() != previousMat) + { + // different node from before, set up for this node + previousMat = &mesh->getTransform(); + + if (previousMat != NULL) + { + mat = *previousMat; + mat.inverse(); + mat.mulP(a,&ta); + mat.mulP(b,&tb); + } + } + // collide... + if (mesh->castRayRendered(od,ta,tb,rayInfo, mMaterialList)) + { + if (!rayInfo) + return true; + + if (rayInfo->t <= saveRay.t) + { + saveRay = *rayInfo; + saveMat = previousMat; + } + found = true; + } + } + } + + // collide with any skins for this detail level... + // TODO: if ever... + + // finalize the deal... + if (found) + { + *rayInfo = saveRay; + saveMat->mulV(rayInfo->normal); + rayInfo->point = b-a; + rayInfo->point *= rayInfo->t; + rayInfo->point += a; + } + return found; +} + +Point3F TSShapeInstance::support(const Point3F & v, S32 dl) +{ + // if dl==-1, nothing to do + AssertFatal(dl != -1, "Error, should never try to collide with a non-existant detail level!"); + AssertFatal(dl>=0 && dldetails.size(),"TSShapeInstance::support"); + + // get subshape and object detail + const TSDetail * detail = &mShape->details[dl]; + S32 ss = detail->subShapeNum; + S32 od = detail->objectDetailNum; + + S32 start = mShape->subShapeFirstObject[ss]; + S32 end = mShape->subShapeNumObjects[ss] + start; + + F32 currMaxDP = -1e9f; + Point3F currSupport = Point3F(0, 0, 0); + const MatrixF * previousMat = NULL; + MatrixF mat; + if (start= mesh->object->numMeshes) + continue; + + TSMesh* physMesh = mesh->getMesh(od); + if (physMesh && !mesh->forceHidden && mesh->visible > 0.01f) + { + // collide... + if (&mesh->getTransform() != previousMat) + { + // different node from before, set up for this node + previousMat = &mesh->getTransform(); + mat = *previousMat; + mat.inverse(); + } + mat.mulV(v, &va); + physMesh->support(mesh->frame, va, &currMaxDP, &currSupport); + } + } + } + + if (currMaxDP != -1e9f) + { + previousMat->mulP(currSupport); + return currSupport; + } + else + { + return Point3F(0, 0, 0); + } +} + +void TSShapeInstance::computeBounds(S32 dl, Box3F & bounds) +{ + // if dl==-1, nothing to do + if (dl==-1) + return; + + AssertFatal(dl>=0 && dldetails.size(),"TSShapeInstance::computeBounds"); + + // get subshape and object detail + const TSDetail * detail = &mShape->details[dl]; + S32 ss = detail->subShapeNum; + S32 od = detail->objectDetailNum; + + // use shape bounds for imposter details + if (ss < 0) + { + bounds = mShape->bounds; + return; + } + + S32 start = mShape->subShapeFirstObject[ss]; + S32 end = mShape->subShapeNumObjects[ss] + start; + + // run through objects and updating bounds as we go + bounds.minExtents.set( 10E30f, 10E30f, 10E30f); + bounds.maxExtents.set(-10E30f,-10E30f,-10E30f); + Box3F box; + for (S32 i=start; i= mesh->object->numMeshes) + continue; + + if (mesh->getMesh(od)) + { + mesh->getMesh(od)->computeBounds(mesh->getTransform(),box, 0); // use frame 0 so TSSkinMesh uses skinned verts to compute bounds + bounds.minExtents.setMin(box.minExtents); + bounds.maxExtents.setMax(box.maxExtents); + } + } +} + +//------------------------------------------------------------------------------------- +// Object (MeshObjectInstance & PluginObjectInstance) collision methods +//------------------------------------------------------------------------------------- + +bool TSShapeInstance::ObjectInstance::buildPolyList(S32 objectDetail, AbstractPolyList *polyList, U32 &surfaceKey, TSMaterialList *materials ) +{ + TORQUE_UNUSED( objectDetail ); + TORQUE_UNUSED( polyList ); + TORQUE_UNUSED( surfaceKey ); + TORQUE_UNUSED( materials ); + + AssertFatal(0,"TSShapeInstance::ObjectInstance::buildPolyList: no default method."); + return false; +} + +bool TSShapeInstance::ObjectInstance::getFeatures(S32 objectDetail, const MatrixF& mat, const Point3F& n, ConvexFeature* cf, U32& surfaceKey) +{ + TORQUE_UNUSED( objectDetail ); + TORQUE_UNUSED( mat ); + TORQUE_UNUSED( n ); + TORQUE_UNUSED( cf ); + TORQUE_UNUSED( surfaceKey ); + + AssertFatal(0,"TSShapeInstance::ObjectInstance::buildPolyList: no default method."); + return false; +} + +void TSShapeInstance::ObjectInstance::support(S32, const Point3F&, F32*, Point3F*) +{ + AssertFatal(0,"TSShapeInstance::ObjectInstance::supprt: no default method."); +} + +bool TSShapeInstance::ObjectInstance::castRay( S32 objectDetail, const Point3F &start, const Point3F &end, RayInfo *rayInfo, TSMaterialList *materials ) +{ + TORQUE_UNUSED( objectDetail ); + TORQUE_UNUSED( start ); + TORQUE_UNUSED( end ); + TORQUE_UNUSED( rayInfo ); + + AssertFatal(0,"TSShapeInstance::ObjectInstance::castRay: no default method."); + return false; +} + +bool TSShapeInstance::MeshObjectInstance::buildPolyList( S32 objectDetail, AbstractPolyList *polyList, U32 &surfaceKey, TSMaterialList *materials ) +{ + TSMesh * mesh = getMesh(objectDetail); + if (mesh && !forceHidden && visible>0.01f) + return mesh->buildPolyList(frame,polyList,surfaceKey,materials); + return false; +} + +bool TSShapeInstance::MeshObjectInstance::getFeatures(S32 objectDetail, const MatrixF& mat, const Point3F& n, ConvexFeature* cf, U32& surfaceKey) +{ + TSMesh* mesh = getMesh(objectDetail); + if (mesh && !forceHidden && visible > 0.01f) + return mesh->getFeatures(frame, mat, n, cf, surfaceKey); + return false; +} + +void TSShapeInstance::MeshObjectInstance::support(S32 objectDetail, const Point3F& v, F32* currMaxDP, Point3F* currSupport) +{ + TSMesh* mesh = getMesh(objectDetail); + if (mesh && !forceHidden && visible > 0.01f) + mesh->support(frame, v, currMaxDP, currSupport); +} + + +bool TSShapeInstance::MeshObjectInstance::castRay( S32 objectDetail, const Point3F & start, const Point3F & end, RayInfo * rayInfo, TSMaterialList* materials ) +{ + TSMesh* mesh = getMesh( objectDetail ); + if( mesh && !forceHidden && visible > 0.01f ) + return mesh->castRay( frame, start, end, rayInfo, materials ); + return false; +} + +bool TSShapeInstance::MeshObjectInstance::castRayRendered( S32 objectDetail, const Point3F & start, const Point3F & end, RayInfo * rayInfo, TSMaterialList* materials ) +{ + TSMesh* mesh = getMesh( objectDetail ); + if( mesh && !forceHidden && visible > 0.01f ) + return mesh->castRayRendered( frame, start, end, rayInfo, materials ); + return false; +} + +bool TSShapeInstance::ObjectInstance::castRayOpcode( S32 objectDetail, const Point3F & start, const Point3F & end, RayInfo *rayInfo, TSMaterialList* materials ) +{ + TORQUE_UNUSED( objectDetail ); + TORQUE_UNUSED( start ); + TORQUE_UNUSED( end ); + TORQUE_UNUSED( rayInfo ); + TORQUE_UNUSED( materials ); + + return false; +} + +bool TSShapeInstance::ObjectInstance::buildPolyListOpcode( S32 objectDetail, AbstractPolyList *polyList, U32 &surfaceKey, TSMaterialList *materials ) +{ + TORQUE_UNUSED( objectDetail ); + TORQUE_UNUSED( polyList ); + TORQUE_UNUSED( surfaceKey ); + TORQUE_UNUSED( materials ); + + return false; +} + +bool TSShapeInstance::ObjectInstance::buildConvexOpcode( const MatrixF &mat, S32 objectDetail, const Box3F &bounds, Convex *c, Convex *list ) +{ + TORQUE_UNUSED( mat ); + TORQUE_UNUSED( objectDetail ); + TORQUE_UNUSED( bounds ); + TORQUE_UNUSED( c ); + TORQUE_UNUSED( list ); + + return false; +} + +bool TSShapeInstance::MeshObjectInstance::castRayOpcode( S32 objectDetail, const Point3F & start, const Point3F & end, RayInfo *info, TSMaterialList* materials ) +{ + TSMesh * mesh = getMesh(objectDetail); + if (mesh && !forceHidden && visible>0.01f) + return mesh->castRayOpcode(start, end, info, materials); + return false; +} + +bool TSShapeInstance::MeshObjectInstance::buildPolyListOpcode( S32 objectDetail, AbstractPolyList *polyList, const Box3F &box, TSMaterialList *materials ) +{ + TSMesh * mesh = getMesh(objectDetail); + if ( mesh && !forceHidden && visible > 0.01f && box.isOverlapped( mesh->getBounds() ) ) + return mesh->buildPolyListOpcode(frame,polyList,box,materials); + return false; +} + +bool TSShapeInstance::MeshObjectInstance::buildConvexOpcode( const MatrixF &mat, S32 objectDetail, const Box3F &bounds, Convex *c, Convex *list) +{ + TSMesh * mesh = getMesh(objectDetail); + if ( mesh && !forceHidden && visible > 0.01f && bounds.isOverlapped( mesh->getBounds() ) ) + return mesh->buildConvexOpcode(mat, bounds, c, list); + return false; +} + +bool TSShapeInstance::buildPolyListOpcode( S32 dl, AbstractPolyList *polyList, const Box3F &box ) +{ + PROFILE_SCOPE( TSShapeInstance_buildPolyListOpcode_MeshObjInst ); + + if (dl==-1) + return false; + + AssertFatal(dl>=0 && dldetails.size(),"TSShapeInstance::buildPolyListOpcode"); + + // get subshape and object detail + const TSDetail * detail = &mShape->details[dl]; + S32 ss = detail->subShapeNum; + if ( ss < 0 ) + return false; + + S32 od = detail->objectDetailNum; + + // nothing emitted yet... + bool emitted = false; + + S32 start = mShape->subShapeFirstObject[ss]; + S32 end = mShape->subShapeNumObjects[ss] + start; + if (startgetTransform(&initialMat,&initialScale); + + // set up for first object's node + MatrixF mat; + MatrixF scaleMat(true); + F32* p = scaleMat; + p[0] = initialScale.x; + p[5] = initialScale.y; + p[10] = initialScale.z; + const MatrixF * previousMat = &mMeshObjects[start].getTransform(); + mat.mul(initialMat,scaleMat); + mat.mul(*previousMat); + polyList->setTransform(&mat,Point3F(1, 1, 1)); + + // Update our bounding box... + Box3F localBox = box; + MatrixF otherMat = mat; + otherMat.inverse(); + otherMat.mul(localBox); + + // run through objects and collide + for (S32 i=start; i= mesh->object->numMeshes) + continue; + + if (&mesh->getTransform() != previousMat) + { + // different node from before, set up for this node + previousMat = &mesh->getTransform(); + + if (previousMat != NULL) + { + mat.mul(initialMat,scaleMat); + mat.mul(*previousMat); + polyList->setTransform(&mat,Point3F(1, 1, 1)); + + // Update our bounding box... + otherMat = mat; + otherMat.inverse(); + localBox = box; + otherMat.mul(localBox); + } + } + + // collide... + emitted |= mesh->buildPolyListOpcode(od,polyList,localBox,mMaterialList); + } + + // restore original transform... + polyList->setTransform(&initialMat,initialScale); + } + + return emitted; +} + +bool TSShapeInstance::castRayOpcode( S32 dl, const Point3F & startPos, const Point3F & endPos, RayInfo *info) +{ + // if dl==-1, nothing to do + if (dl==-1) + return false; + + AssertFatal(dl>=0 && dldetails.size(),"TSShapeInstance::castRayOpcode"); + + info->t = 100.f; + + // get subshape and object detail + const TSDetail * detail = &mShape->details[dl]; + S32 ss = detail->subShapeNum; + if ( ss < 0 ) + return false; + + S32 od = detail->objectDetailNum; + + // nothing emitted yet... + bool emitted = false; + + const MatrixF* saveMat = NULL; + S32 start = mShape->subShapeFirstObject[ss]; + S32 end = mShape->subShapeNumObjects[ss] + start; + if (start= mesh->object->numMeshes) + continue; + + if (&mesh->getTransform() != previousMat) + { + // different node from before, set up for this node + previousMat = &mesh->getTransform(); + + if (previousMat != NULL) + { + mat = *previousMat; + mat.inverse(); + mat.mulP(startPos, &localStart); + mat.mulP(endPos, &localEnd); + } + } + + // collide... + if ( mesh->castRayOpcode(od,localStart, localEnd, info, mMaterialList) ) + { + saveMat = previousMat; + emitted = true; + } + } + } + + if ( emitted ) + { + saveMat->mulV(info->normal); + info->point = endPos - startPos; + info->point *= info->t; + info->point += startPos; + } + + return emitted; +} + +bool TSShapeInstance::buildConvexOpcode( const MatrixF &objMat, const Point3F &objScale, S32 dl, const Box3F &bounds, Convex *c, Convex *list ) +{ + AssertFatal(dl>=0 && dldetails.size(),"TSShapeInstance::buildConvexOpcode"); + + // get subshape and object detail + const TSDetail * detail = &mShape->details[dl]; + S32 ss = detail->subShapeNum; + S32 od = detail->objectDetailNum; + + // nothing emitted yet... + bool emitted = false; + + S32 start = mShape->subShapeFirstObject[ss]; + S32 end = mShape->subShapeNumObjects[ss] + start; + if (start= mesh->object->numMeshes) + continue; + + if (&mesh->getTransform() != previousMat) + { + // different node from before, set up for this node + previousMat = &mesh->getTransform(); + + if (previousMat != NULL) + { + mat.mul(initialMat,scaleMat); + mat.mul(*previousMat); + + // Update our bounding box... + otherMat = mat; + otherMat.inverse(); + localBox = bounds; + otherMat.mul(localBox); + } + } + + // collide... note we pass the original mech transform + // here so that the convex data returned is in mesh space. + emitted |= mesh->buildConvexOpcode(*previousMat,od,localBox,c, list); + } + } + + return emitted; +} + +void TSShape::findColDetails( bool useVisibleMesh, Vector *outDetails, Vector *outLOSDetails ) const +{ + PROFILE_SCOPE( TSShape_findColDetails ); + + if ( useVisibleMesh ) + { + // If we're using the visible mesh for collision then + // find the highest detail and use that. + + U32 highestDetail = -1; + F32 highestSize = -F32_MAX; + + for ( U32 i = 0; i < details.size(); i++ ) + { + // Make sure we skip any details that shouldn't be rendered + if ( details[i].size < 0 ) + continue; + + /* + // Also make sure we skip any collision details with a size. + const String &name = names[details[i].nameIndex]; + if ( dStrStartsWith( name, "Collision" ) || + dStrStartsWith( name, "LOS" ) ) + continue; + */ + + // Otherwise test against the current highest size + if ( details[i].size > highestSize ) + { + highestDetail = i; + highestSize = details[i].size; + } + } + + // We use the same detail for both raycast and collisions. + if ( highestDetail != -1 ) + { + outDetails->push_back( highestDetail ); + if ( outLOSDetails ) + outLOSDetails->push_back( highestDetail ); + } + + return; + } + + // Detail meshes starting with COL or LOS is considered + // to be a collision mesh. + // + // The LOS (light of sight) details are used for raycasts. + + for ( U32 i = 0; i < details.size(); i++ ) + { + const String &name = names[ details[i].nameIndex ]; + if ( !dStrStartsWith( name, "Collision" ) ) + continue; + + outDetails->push_back(i); + + // If we're not returning LOS details then skip out. + if ( !outLOSDetails ) + continue; + + // The way LOS works is that it will check to see if there is a LOS detail that matches + // the the collision detail + 1 + MaxCollisionShapes (this variable name should change in + // the future). If it can't find a matching LOS it will simply use the collision instead. + // We check for any "unmatched" LOS's further down. + + // Extract the detail number from the name. + S32 number = 0; + String::GetTrailingNumber( name, number ); + + // Look for a matching LOS collision detail. + // + // TODO: Fix the old 9 detail offset which is there + // because you cannot have two detail markers with + // the same detail number. + // + const S32 LOSOverrideOffset = 9; + String buff = String::ToString( "LOS-%d", mAbs( number ) + LOSOverrideOffset ); + S32 los = findDetail( buff ); + + // If we didn't find the lod detail then use the + // normal collision detail for LOS tests. + if ( los == -1 ) + los = i; + + outLOSDetails->push_back( los ); + } + + // If we're not returning LOS details then skip out. + if ( !outLOSDetails ) + return; + + // Snag any "unmatched" LOS details and put + // them at the end of the list. + for ( U32 i = 0; i < details.size(); i++ ) + { + const String &name = names[ details[i].nameIndex ]; + if ( !dStrStartsWith( name, "LOS" ) ) + continue; + + // See if we already have this LOS + bool found = false; + for (U32 j = 0; j < outLOSDetails->size(); j++) + { + if ( (*outLOSDetails)[j] == i ) + { + found = true; + break; + } + } + + if ( !found ) + outLOSDetails->push_back(i); + } +} + +PhysicsCollision* TSShape::buildColShape( bool useVisibleMesh, const Point3F &scale ) +{ + return _buildColShapes( useVisibleMesh, scale, NULL, false ); +} + +void TSShape::buildColShapes( bool useVisibleMesh, const Point3F &scale, Vector< CollisionShapeInfo > *list ) +{ + _buildColShapes( useVisibleMesh, scale, list, true ); +} + +PhysicsCollision* TSShape::_buildColShapes( bool useVisibleMesh, const Point3F &scale, Vector< CollisionShapeInfo > *list, bool perMesh ) +{ + PROFILE_SCOPE( TSShape_buildColShapes ); + + if ( !PHYSICSMGR ) + return NULL; + + PhysicsCollision *colShape = NULL; + U32 surfaceKey = 0; + + if ( useVisibleMesh ) + { + // Here we build triangle collision meshes from the + // visible detail levels. + + // A negative subshape on the detail means we don't have geometry. + const TSShape::Detail &detail = details[0]; + if ( detail.subShapeNum < 0 ) + return NULL; + + // We don't try to optimize the triangles we're given + // and assume the art was created properly for collision. + ConcretePolyList polyList; + polyList.setTransform( &MatrixF::Identity, scale ); + + // Create the collision meshes. + S32 start = subShapeFirstObject[ detail.subShapeNum ]; + S32 end = start + subShapeNumObjects[ detail.subShapeNum ]; + for ( S32 o=start; o < end; o++ ) + { + const TSShape::Object &object = objects[o]; + if ( detail.objectDetailNum >= object.numMeshes ) + continue; + + // No mesh or no verts.... nothing to do. + TSMesh *mesh = meshes[ object.startMeshIndex + detail.objectDetailNum ]; + if ( !mesh || mesh->mNumVerts == 0 ) + continue; + + // Gather the mesh triangles. + polyList.clear(); + mesh->buildPolyList( 0, &polyList, surfaceKey, NULL ); + + // Create the collision shape if we haven't already. + if ( !colShape ) + colShape = PHYSICSMGR->createCollision(); + + // Get the object space mesh transform. + MatrixF localXfm; + getNodeWorldTransform( object.nodeIndex, &localXfm ); + + colShape->addTriangleMesh( polyList.mVertexList.address(), + polyList.mVertexList.size(), + polyList.mIndexList.address(), + polyList.mIndexList.size() / 3, + localXfm ); + + if ( perMesh ) + { + list->increment(); + list->last().colNode = -1; + list->last().colShape = colShape; + colShape = NULL; + } + } + + // Return what we built... if anything. + return colShape; + } + + + // Scan out the collision hulls... + // + // TODO: We need to support LOS collision for physics. + // + for ( U32 i = 0; i < details.size(); i++ ) + { + const TSShape::Detail &detail = details[i]; + const String &name = names[detail.nameIndex]; + + // Is this a valid collision detail. + if ( !dStrStartsWith( name, "Collision" ) || detail.subShapeNum < 0 ) + continue; + + // Now go thru the meshes for this detail. + S32 start = subShapeFirstObject[ detail.subShapeNum ]; + S32 end = start + subShapeNumObjects[ detail.subShapeNum ]; + if ( start >= end ) + continue; + + for ( S32 o=start; o < end; o++ ) + { + const TSShape::Object &object = objects[o]; + const String &meshName = names[ object.nameIndex ]; + + if ( object.numMeshes <= detail.objectDetailNum ) + continue; + + // No mesh, a flat bounds, or no verts.... nothing to do. + TSMesh *mesh = meshes[ object.startMeshIndex + detail.objectDetailNum ]; + if ( !mesh || mesh->getBounds().isEmpty() || mesh->mNumVerts == 0 ) + continue; + + // We need the default mesh transform. + MatrixF localXfm; + getNodeWorldTransform( object.nodeIndex, &localXfm ); + + // We have some sort of collision shape... so allocate it. + if ( !colShape ) + colShape = PHYSICSMGR->createCollision(); + + // We have geometry... what is it? + if ( dStrStartsWith( meshName, "Colbox" ) ) + { + // The bounds define the box extents directly. + Point3F halfWidth = mesh->getBounds().getExtents() * 0.5f; + + // Add the offset to the center of the bounds + // into the local space transform. + MatrixF centerXfm( true ); + centerXfm.setPosition( mesh->getBounds().getCenter() ); + localXfm.mul( centerXfm ); + + colShape->addBox( halfWidth, localXfm ); + } + else if ( dStrStartsWith( meshName, "Colsphere" ) ) + { + // Get a sphere inscribed to the bounds. + F32 radius = mesh->getBounds().len_min() * 0.5f; + + // Add the offset to the center of the bounds + // into the local space transform. + MatrixF primXfm( true ); + primXfm.setPosition( mesh->getBounds().getCenter() ); + localXfm.mul( primXfm ); + + colShape->addSphere( radius, localXfm ); + } + else if ( dStrStartsWith( meshName, "Colcapsule" ) ) + { + // Use the smallest extent as the radius for the capsule. + Point3F extents = mesh->getBounds().getExtents(); + F32 radius = extents.least() * 0.5f; + + // We need to center the capsule and align it to the Y axis. + MatrixF primXfm( true ); + primXfm.setPosition( mesh->getBounds().getCenter() ); + + // Use the longest axis as the capsule height. + F32 height = -radius * 2.0f; + if ( extents.x > extents.y && extents.x > extents.z ) + { + primXfm.setColumn( 0, Point3F( 0, 0, 1 ) ); + primXfm.setColumn( 1, Point3F( 1, 0, 0 ) ); + primXfm.setColumn( 2, Point3F( 0, 1, 0 ) ); + height += extents.x; + } + else if ( extents.z > extents.x && extents.z > extents.y ) + { + primXfm.setColumn( 0, Point3F( 0, 1, 0 ) ); + primXfm.setColumn( 1, Point3F( 0, 0, 1 ) ); + primXfm.setColumn( 2, Point3F( 1, 0, 0 ) ); + height += extents.z; + } + else + height += extents.y; + + // Add the primitive transform into the local transform. + localXfm.mul( primXfm ); + + // If we didn't find a positive height then fallback to + // creating a sphere which is better than nothing. + if ( height > 0.0f ) + colShape->addCapsule( radius, height, localXfm ); + else + colShape->addSphere( radius, localXfm ); + } + else if ( dStrStartsWith( meshName, "Colmesh" ) ) + { + // For a triangle mesh we gather the triangles raw from the + // mesh and don't do any optimizations or cleanup. + ConcretePolyList polyList; + polyList.setTransform( &MatrixF::Identity, scale ); + mesh->buildPolyList( 0, &polyList, surfaceKey, NULL ); + colShape->addTriangleMesh( polyList.mVertexList.address(), + polyList.mVertexList.size(), + polyList.mIndexList.address(), + polyList.mIndexList.size() / 3, + localXfm ); + } + else + { + // Any other mesh name we assume as a generic convex hull. + // + // Collect the verts using the vertex polylist which will + // filter out duplicates. This is importaint as the convex + // generators can sometimes fail with duplicate verts. + // + VertexPolyList polyList; + MatrixF meshMat( localXfm ); + + Point3F t = meshMat.getPosition(); + t.convolve( scale ); + meshMat.setPosition( t ); + + polyList.setTransform( &MatrixF::Identity, scale ); + mesh->buildPolyList( 0, &polyList, surfaceKey, NULL ); + colShape->addConvex( polyList.getVertexList().address(), + polyList.getVertexList().size(), + meshMat ); + } + + if ( perMesh ) + { + list->increment(); + + S32 detailNum; + String::GetTrailingNumber( name, detailNum ); + + String str = String::ToString( "%s%i", meshName.c_str(), detailNum ); + S32 found = findNode( str ); + + if ( found == -1 ) + { + str = str.replace('-','_'); + found = findNode( str ); + } + + list->last().colNode = found; + list->last().colShape = colShape; + + colShape = NULL; + } + + } // objects + + } // details + + return colShape; +} + +bool TSMesh::buildPolyListOpcode( const S32 od, AbstractPolyList *polyList, const Box3F &nodeBox, TSMaterialList *materials ) +{ + PROFILE_SCOPE( TSMesh_buildPolyListOpcode ); + + // This is small... there is no win for preallocating it. + Opcode::AABBCollider opCollider; + opCollider.SetPrimitiveTests( true ); + + // This isn't really needed within the AABBCollider as + // we don't use temporal coherance... use a static to + // remove the allocation overhead. + static Opcode::AABBCache opCache; + + IceMaths::AABB opBox; + opBox.SetMinMax( Point( nodeBox.minExtents.x, nodeBox.minExtents.y, nodeBox.minExtents.z ), + Point( nodeBox.maxExtents.x, nodeBox.maxExtents.y, nodeBox.maxExtents.z ) ); + Opcode::CollisionAABB opCBox(opBox); + + if ( !opCollider.Collide( opCache, opCBox, *mOptTree ) ) + return false; + + U32 count = opCollider.GetNbTouchedPrimitives(); + const udword *idx = opCollider.GetTouchedPrimitives(); + + Opcode::VertexPointers vp; + U32 plIdx[3]; + S32 j; + Point3F tmp; + const IceMaths::Point **verts; + const Opcode::MeshInterface *mi = mOptTree->GetMeshInterface(); + + for ( S32 i=0; i < count; i++ ) + { + // Get the triangle... + mi->GetTriangle( vp, idx[i] ); + verts = vp.Vertex; + + // And register it in the polylist... + polyList->begin( NULL, i ); + + for( j = 2; j > -1; j-- ) + { + tmp.set( verts[j]->x, verts[j]->y, verts[j]->z ); + plIdx[j] = polyList->addPoint( tmp ); + polyList->vertex( plIdx[j] ); + } + + polyList->plane( plIdx[0], plIdx[2], plIdx[1] ); + + polyList->end(); + } + + // TODO: Add a polyList->getCount() so we can see if we + // got clipped polys and didn't really emit anything. + return count > 0; +} + +bool TSMesh::buildConvexOpcode( const MatrixF &meshToObjectMat, const Box3F &nodeBox, Convex *convex, Convex *list ) +{ + PROFILE_SCOPE( TSMesh_buildConvexOpcode ); + + // This is small... there is no win for preallocating it. + Opcode::AABBCollider opCollider; + opCollider.SetPrimitiveTests( true ); + + // This isn't really needed within the AABBCollider as + // we don't use temporal coherance... use a static to + // remove the allocation overhead. + static Opcode::AABBCache opCache; + + IceMaths::AABB opBox; + opBox.SetMinMax( Point( nodeBox.minExtents.x, nodeBox.minExtents.y, nodeBox.minExtents.z ), + Point( nodeBox.maxExtents.x, nodeBox.maxExtents.y, nodeBox.maxExtents.z ) ); + Opcode::CollisionAABB opCBox(opBox); + + if( !opCollider.Collide( opCache, opCBox, *mOptTree ) ) + return false; + + U32 cnt = opCollider.GetNbTouchedPrimitives(); + const udword *idx = opCollider.GetTouchedPrimitives(); + + Opcode::VertexPointers vp; + for ( S32 i = 0; i < cnt; i++ ) + { + // First, check our active convexes for a potential match (and clean things + // up, too.) + const U32 curIdx = idx[i]; + + // See if the square already exists as part of the working set. + bool gotMatch = false; + CollisionWorkingList& wl = convex->getWorkingList(); + for ( CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext ) + { + if( itr->mConvex->getType() != TSPolysoupConvexType ) + continue; + + const TSStaticPolysoupConvex *chunkc = static_cast( itr->mConvex ); + + if( chunkc->getObject() != TSStaticPolysoupConvex::smCurObject ) + continue; + + if( chunkc->mesh != this ) + continue; + + if( chunkc->idx != curIdx ) + continue; + + // A match! Don't need to add it. + gotMatch = true; + break; + } + + if( gotMatch ) + continue; + + // Get the triangle... + mOptTree->GetMeshInterface()->GetTriangle( vp, idx[i] ); + + Point3F a( vp.Vertex[0]->x, vp.Vertex[0]->y, vp.Vertex[0]->z ); + Point3F b( vp.Vertex[1]->x, vp.Vertex[1]->y, vp.Vertex[1]->z ); + Point3F c( vp.Vertex[2]->x, vp.Vertex[2]->y, vp.Vertex[2]->z ); + + // Transform the result into object space! + meshToObjectMat.mulP( a ); + meshToObjectMat.mulP( b ); + meshToObjectMat.mulP( c ); + + PlaneF p( c, b, a ); + Point3F peak = ((a + b + c) / 3.0f) - (p * 0.15f); + + // Set up the convex... + TSStaticPolysoupConvex *cp = new TSStaticPolysoupConvex(); + + list->registerObject( cp ); + convex->addToWorkingList( cp ); + + cp->mesh = this; + cp->idx = curIdx; + cp->mObject = TSStaticPolysoupConvex::smCurObject; + + cp->normal = p; + cp->verts[0] = a; + cp->verts[1] = b; + cp->verts[2] = c; + cp->verts[3] = peak; + + // Update the bounding box. + Box3F &bounds = cp->box; + bounds.minExtents.set( F32_MAX, F32_MAX, F32_MAX ); + bounds.maxExtents.set( -F32_MAX, -F32_MAX, -F32_MAX ); + + bounds.minExtents.setMin( a ); + bounds.minExtents.setMin( b ); + bounds.minExtents.setMin( c ); + bounds.minExtents.setMin( peak ); + + bounds.maxExtents.setMax( a ); + bounds.maxExtents.setMax( b ); + bounds.maxExtents.setMax( c ); + bounds.maxExtents.setMax( peak ); + } + + return true; +} + +void TSMesh::prepOpcodeCollision() +{ + // Make sure opcode is loaded! + if( !gOpcodeInitialized ) + { + Opcode::InitOpcode(); + gOpcodeInitialized = true; + } + + // Don't re init if we already have something... + if ( mOptTree ) + return; + + // Ok, first set up a MeshInterface + Opcode::MeshInterface *mi = new Opcode::MeshInterface(); + mOpMeshInterface = mi; + + // Figure out how many triangles we have... + U32 triCount = 0; + const U32 base = 0; + for ( U32 i = 0; i < primitives.size(); i++ ) + { + TSDrawPrimitive & draw = primitives[i]; + const U32 start = draw.start; + + AssertFatal( draw.matIndex & TSDrawPrimitive::Indexed,"TSMesh::buildPolyList (1)" ); + + // gonna depend on what kind of primitive it is... + if ( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles ) + triCount += draw.numElements / 3; + else + { + // Have to walk the tristrip to get a count... may have degenerates + U32 idx0 = base + indices[start + 0]; + U32 idx1; + U32 idx2 = base + indices[start + 1]; + U32 * nextIdx = &idx1; + for ( S32 j = 2; j < draw.numElements; j++ ) + { + *nextIdx = idx2; + // nextIdx = (j%2)==0 ? &idx0 : &idx1; + nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1); + idx2 = base + indices[start + j]; + if ( idx0 == idx1 || idx0 == idx2 || idx1 == idx2 ) + continue; + + triCount++; + } + } + } + + // Just do the first trilist for now. + mi->SetNbVertices( mVertexData.isReady() ? mNumVerts : verts.size() ); + mi->SetNbTriangles( triCount ); + + // Stuff everything into appropriate arrays. + IceMaths::IndexedTriangle *its = new IceMaths::IndexedTriangle[ mi->GetNbTriangles() ], *curIts = its; + IceMaths::Point *pts = new IceMaths::Point[ mi->GetNbVertices() ]; + + mOpTris = its; + mOpPoints = pts; + + // add the polys... + for ( U32 i = 0; i < primitives.size(); i++ ) + { + TSDrawPrimitive & draw = primitives[i]; + const U32 start = draw.start; + + AssertFatal( draw.matIndex & TSDrawPrimitive::Indexed,"TSMesh::buildPolyList (1)" ); + + const U32 matIndex = draw.matIndex & TSDrawPrimitive::MaterialMask; + + // gonna depend on what kind of primitive it is... + if ( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles ) + { + for ( S32 j = 0; j < draw.numElements; ) + { + curIts->mVRef[2] = base + indices[start + j + 0]; + curIts->mVRef[1] = base + indices[start + j + 1]; + curIts->mVRef[0] = base + indices[start + j + 2]; + curIts->mMatIdx = matIndex; + curIts++; + + j += 3; + } + } + else + { + AssertFatal( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Strip,"TSMesh::buildPolyList (2)" ); + + U32 idx0 = base + indices[start + 0]; + U32 idx1; + U32 idx2 = base + indices[start + 1]; + U32 * nextIdx = &idx1; + for ( S32 j = 2; j < draw.numElements; j++ ) + { + *nextIdx = idx2; + // nextIdx = (j%2)==0 ? &idx0 : &idx1; + nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1); + idx2 = base + indices[start + j]; + if ( idx0 == idx1 || idx0 == idx2 || idx1 == idx2 ) + continue; + + curIts->mVRef[2] = idx0; + curIts->mVRef[1] = idx1; + curIts->mVRef[0] = idx2; + curIts->mMatIdx = matIndex; + curIts++; + } + } + } + + AssertFatal( (curIts - its) == mi->GetNbTriangles(), "Triangle count mismatch!" ); + + for( S32 i = 0; i < mi->GetNbVertices(); i++ ) + if( mVertexData.isReady() ) + pts[i].Set( mVertexData[i].vert().x, mVertexData[i].vert().y, mVertexData[i].vert().z ); + else + pts[i].Set( verts[i].x, verts[i].y, verts[i].z ); + + mi->SetPointers( its, pts ); + + // Ok, we've got a mesh interface populated, now let's build a thingy to collide against. + mOptTree = new Opcode::Model(); + + Opcode::OPCODECREATE opcc; + + opcc.mCanRemap = true; + opcc.mIMesh = mi; + opcc.mKeepOriginal = false; + opcc.mNoLeaf = false; + opcc.mQuantized = false; + opcc.mSettings.mLimit = 1; + + mOptTree->Build( opcc ); +} + +static Point3F texGenAxis[18] = +{ + Point3F(0,0,1), Point3F(1,0,0), Point3F(0,-1,0), + Point3F(0,0,-1), Point3F(1,0,0), Point3F(0,1,0), + Point3F(1,0,0), Point3F(0,1,0), Point3F(0,0,1), + Point3F(-1,0,0), Point3F(0,1,0), Point3F(0,0,-1), + Point3F(0,1,0), Point3F(1,0,0), Point3F(0,0,1), + Point3F(0,-1,0), Point3F(-1,0,0), Point3F(0,0,-1) +}; + + +bool TSMesh::castRayOpcode( const Point3F &s, const Point3F &e, RayInfo *info, TSMaterialList *materials ) +{ + Opcode::RayCollider ray; + Opcode::CollisionFaces cfs; + + IceMaths::Point dir( e.x - s.x, e.y - s.y, e.z - s.z ); + const F32 rayLen = dir.Magnitude(); + IceMaths::Ray vec( Point(s.x, s.y, s.z), dir.Normalize() ); + + ray.SetDestination( &cfs); + ray.SetFirstContact( false ); + ray.SetClosestHit( true ); + ray.SetPrimitiveTests( true ); + ray.SetCulling( true ); + ray.SetMaxDist( rayLen ); + + AssertFatal( ray.ValidateSettings() == NULL, "invalid ray settings" ); + + // Do collision. + bool safety = ray.Collide( vec, *mOptTree ); + AssertFatal( safety, "TSMesh::castRayOpcode - no good ray collide!" ); + + // If no hit, just skip out. + if( cfs.GetNbFaces() == 0 ) + return false; + + // Got a hit! + AssertFatal( cfs.GetNbFaces() == 1, "bad" ); + const Opcode::CollisionFace &face = cfs.GetFaces()[0]; + + // If the cast was successful let's check if the t value is less than what we had + // and toggle the collision boolean + // Stupid t... i prefer coffee + const F32 t = face.mDistance / rayLen; + + if( t < 0.0f || t > 1.0f ) + return false; + + if( t <= info->t ) + { + info->t = t; + + // Calculate the normal. + Opcode::VertexPointers vp; + mOptTree->GetMeshInterface()->GetTriangle( vp, face.mFaceID ); + + if ( materials && vp.MatIdx >= 0 && vp.MatIdx < materials->size() ) + info->material = materials->getMaterialInst( vp.MatIdx ); + + // Get the two edges. + IceMaths::Point baseVert = *vp.Vertex[0]; + IceMaths::Point a = *vp.Vertex[1] - baseVert; + IceMaths::Point b = *vp.Vertex[2] - baseVert; + + IceMaths::Point n; + n.Cross( a, b ); + n.Normalize(); + + info->normal.set( n.x, n.y, n.z ); + + // generate UV coordinate across mesh based on + // matching normals, this isn't done by default and is + // primarily of interest in matching a collision point to + // either a GUI control coordinate or finding a hit pixel in texture space + if (info->generateTexCoord) + { + baseVert = *vp.Vertex[0]; + a = *vp.Vertex[1]; + b = *vp.Vertex[2]; + + Point3F facePoint = (1.0f - face.mU - face.mV) * Point3F(baseVert.x, baseVert.y, baseVert.z) + + face.mU * Point3F(a.x, a.y, a.z) + face.mV * Point3F(b.x, b.y, b.z); + + U32 faces[1024]; + U32 numFaces = 0; + for (U32 i = 0; i < mOptTree->GetMeshInterface()->GetNbTriangles(); i++) + { + if ( i == face.mFaceID ) + { + faces[numFaces++] = i; + } + else + { + IceMaths::Point n2; + + mOptTree->GetMeshInterface()->GetTriangle( vp, i ); + + baseVert = *vp.Vertex[0]; + a = *vp.Vertex[1] - baseVert; + b = *vp.Vertex[2] - baseVert; + n2.Cross( a, b ); + n2.Normalize(); + + F32 eps = .01f; + if ( mFabs(n.x - n2.x) < eps && mFabs(n.y - n2.y) < eps && mFabs(n.z - n2.z) < eps) + { + faces[numFaces++] = i; + } + } + + if (numFaces == 1024) + { + // too many faces in this collision mesh for UV generation + return true; + } + + } + + Point3F min(F32_MAX, F32_MAX, F32_MAX); + Point3F max(-F32_MAX, -F32_MAX, -F32_MAX); + + for (U32 i = 0; i < numFaces; i++) + { + mOptTree->GetMeshInterface()->GetTriangle( vp, faces[i] ); + + for ( U32 j =0; j < 3; j++) + { + a = *vp.Vertex[j]; + + if (a.x < min.x) + min.x = a.x; + if (a.y < min.y) + min.y = a.y; + if (a.z < min.z) + min.z = a.z; + + if (a.x > max.x) + max.x = a.x; + if (a.y > max.y) + max.y = a.y; + if (a.z > max.z) + max.z = a.z; + + } + + } + + // slerp + Point3F s = ( (max - min) - (facePoint - min) ) / (max - min); + + // compute axis + S32 bestAxis = 0; + F32 best = 0.f; + + for (U32 i = 0 ; i < 6 ; i++) + { + F32 dot = mDot (info->normal, texGenAxis[i*3]); + if (dot > best) + { + best = dot; + bestAxis = i; + } + } + + Point3F xv = texGenAxis[bestAxis*3+1]; + Point3F yv = texGenAxis[bestAxis*3+2]; + + S32 sv, tv; + + if (xv.x) + sv = 0; + else if (xv.y) + sv = 1; + else + sv = 2; + + if (yv.x) + tv = 0; + else if (yv.y) + tv = 1; + else + tv = 2; + + // handle coord translation + if (bestAxis == 2 || bestAxis == 3) + { + S32 x = sv; + sv = tv; + tv = x; + + if (yv.z < 0) + s[sv] = 1.f - s[sv]; + } + + if (bestAxis < 2) + { + if (yv.y < 0) + s[sv] = 1.f - s[sv]; + } + + if (bestAxis > 3) + { + s[sv] = 1.f - s[sv]; + if (yv.z > 0) + s[tv] = 1.f - s[tv]; + + } + + // done! + info->texCoord.set(s[sv], s[tv]); + + } + + return true; + } + + return false; +} diff --git a/Engine/source/ts/tsDecal.cpp b/Engine/source/ts/tsDecal.cpp new file mode 100644 index 000000000..515e19e41 --- /dev/null +++ b/Engine/source/ts/tsDecal.cpp @@ -0,0 +1,107 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "ts/tsDecal.h" +#include "math/mMath.h" +#include "math/mathIO.h" +#include "ts/tsShapeInstance.h" +#include "core/frameAllocator.h" + +//----------------------------------------------------- +// TSDecalMesh assembly/dissembly methods +// used for transfer to/from memory buffers +//----------------------------------------------------- + +#define tsalloc TSShape::smTSAlloc + +void TSDecalMesh::assemble(bool) +{ + if (TSShape::smReadVersion<20) + { + // read empty mesh...decals used to be derived from meshes + tsalloc.checkGuard(); + tsalloc.getPointer32(15); + } + + S32 sz = tsalloc.get32(); + S32 * ptr32 = tsalloc.copyToShape32(0); // get current shape address w/o doing anything + for (S32 i=0; i=19) + { + ptr32 = tsalloc.getPointer32(sz*4); + texgenS.set(ptr32,startPrimitive.size()); + ptr32 = tsalloc.getPointer32(sz*4); + texgenT.set(ptr32,startPrimitive.size()); + } + else + { + texgenS.set(NULL,0); + texgenT.set(NULL,0); + } + + materialIndex = tsalloc.get32(); + + tsalloc.checkGuard(); +} + +void TSDecalMesh::disassemble() +{ + tsalloc.set32(primitives.size()); + tsalloc.copyToBuffer32((S32*)primitives.address(),primitives.size()); + + tsalloc.set32(indices.size()); + tsalloc.copyToBuffer32((S32*)indices.address(),indices.size()); + + tsalloc.set32(startPrimitive.size()); + tsalloc.copyToBuffer32((S32*)startPrimitive.address(),startPrimitive.size()); + + tsalloc.copyToBuffer32((S32*)texgenS.address(),texgenS.size()*4); + tsalloc.copyToBuffer32((S32*)texgenT.address(),texgenT.size()*4); + + tsalloc.set32(materialIndex); + + tsalloc.setGuard(); +} + diff --git a/Engine/source/ts/tsDecal.h b/Engine/source/ts/tsDecal.h new file mode 100644 index 000000000..7715850f6 --- /dev/null +++ b/Engine/source/ts/tsDecal.h @@ -0,0 +1,66 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TSDECAL_H_ +#define _TSDECAL_H_ + +#ifndef _TSMESH_H_ +#include "ts/tsMesh.h" +#endif + +/// Decals! The lovely detailing thingies, e.g. bullet hole marks. +/// DEPRECATED: This class is here for compatibility with old files only. +/// Performs no actual rendering. +class TSDecalMesh +{ +public: + + /// The mesh that we are decaling + TSMesh * targetMesh; + + /// @name Topology + /// @{ + Vector primitives; + Vector indices; + /// @} + + /// @name Render Data + /// indexed by decal frame... + /// @{ + Vector startPrimitive; + Vector texgenS; + Vector texgenT; + /// @} + + /// We only allow 1 material per decal... + S32 materialIndex; + + /// DEPRECATED + // void render(S32 frame, S32 decalFrame, TSMaterialList *); + + void disassemble(); + void assemble(bool skip); +}; + + +#endif + diff --git a/Engine/source/ts/tsDump.cpp b/Engine/source/ts/tsDump.cpp new file mode 100644 index 000000000..9d37faa81 --- /dev/null +++ b/Engine/source/ts/tsDump.cpp @@ -0,0 +1,231 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "ts/tsShapeInstance.h" +#include "ts/tsMaterialList.h" +#include "core/strings/stringFunctions.h" + + +//------------------------------------------------------------------------------------- +// Dump shape structure: +//------------------------------------------------------------------------------------- + +#define dumpLine(buffer) {str = buffer; stream.write((int)dStrlen(str),str);} + +void TSShapeInstance::dumpNode(Stream & stream ,S32 level, S32 nodeIndex, Vector & detailSizes) +{ + if (nodeIndex < 0) + return; + + // limit level to prevent overflow + if (level > 160) + level = 160; + + S32 i; + const char * str; + char space[512]; + for (i = 0; i < level*3; i++) + space[i] = ' '; + space[level*3] = '\0'; + + const char *nodeName = ""; + const TSShape::Node & node = mShape->nodes[nodeIndex]; + if (node.nameIndex != -1) + nodeName = mShape->getName(node.nameIndex); + dumpLine(avar("%s%s", space, nodeName)); + + // find all the objects that hang off this node... + Vector objectList; + for (i=0; iobject->nameIndex!=-1) + objectName = mShape->getName(obj->object->nameIndex); + + // more spaces if this is the second object on this node + if (spaceCount>0) + { + char buf[2048]; + dMemset(buf,' ',spaceCount); + buf[spaceCount] = '\0'; + dumpLine(buf); + } + + // dump object name + dumpLine(avar(" --> Object %s with following details: ",objectName)); + + // dump object detail levels + for (S32 k=0; kobject->numMeshes; k++) + { + S32 f = obj->object->startMeshIndex; + if (mShape->meshes[f+k]) + dumpLine(avar(" %i",detailSizes[k])); + } + + dumpLine("\r\n"); + + // how many spaces should we prepend if we have another object on this node + if (spaceCount<0) + spaceCount = (S32)(dStrlen(space) + dStrlen(nodeName)); + + if(spaceCount > 2000) + spaceCount = 2000; + } + + // search for children + for (S32 k=nodeIndex+1; knodes.size(); k++) + { + if (mShape->nodes[k].parentIndex == nodeIndex) + // this is our child + dumpNode(stream, level+1, k, detailSizes); + } +} + +void TSShapeInstance::dump(Stream & stream) +{ + S32 i,j,ss,od,sz; + const char * name; + const char * str; + + dumpLine("\r\nShape Hierarchy:\r\n"); + + dumpLine("\r\n Details:\r\n"); + + for (i=0; idetails.size(); i++) + { + const TSDetail & detail = mShape->details[i]; + name = mShape->getName(detail.nameIndex); + ss = detail.subShapeNum; + od = detail.objectDetailNum; + sz = (S32)detail.size; + if (ss >= 0) + { + dumpLine(avar(" %s, Subtree %i, objectDetail %i, size %i\r\n",name,ss,od,sz)); + } + else + { + dumpLine(avar(" %s, AutoBillboard, size %i\r\n", name, sz)); + } + } + + dumpLine("\r\n Subtrees:\r\n"); + + for (i=0; isubShapeFirstNode.size(); i++) + { + S32 a = mShape->subShapeFirstNode[i]; + S32 b = a + mShape->subShapeNumNodes[i]; + dumpLine(avar(" Subtree %i\r\n",i)); + + // compute detail sizes for each subshape + Vector detailSizes; + for (S32 l=0;ldetails.size(); l++) + { + if ((mShape->details[l].subShapeNum==i) || (mShape->details[l].subShapeNum==-1)) + detailSizes.push_back((S32)mShape->details[l].size); + } + + for (j=a; jnodes[j]; + // if the node has a parent, it'll get dumped via the parent + if (node.parentIndex<0) + dumpNode(stream,3,j,detailSizes); + } + } + + bool foundSkin = false; + for (i=0; iobjects.size(); i++) + { + if (mShape->objects[i].nodeIndex<0) // must be a skin + { + if (!foundSkin) + dumpLine("\r\n Skins:\r\n"); + foundSkin=true; + const char * skinName = ""; + S32 nameIndex = mShape->objects[i].nameIndex; + if (nameIndex>=0) + skinName = mShape->getName(nameIndex); + dumpLine(avar(" Skin %s with following details: ",skinName)); + for (S32 num=0; numobjects[i].numMeshes; num++) + { + if (mShape->meshes[mShape->objects[i].startMeshIndex + num]) + dumpLine(avar(" %i",(S32)mShape->details[num].size)); + } + dumpLine("\r\n"); + } + } + if (foundSkin) + dumpLine("\r\n"); + + dumpLine("\r\n Sequences:\r\n"); + for (i = 0; i < mShape->sequences.size(); i++) + { + const char *name = "(none)"; + if (mShape->sequences[i].nameIndex != -1) + name = mShape->getName(mShape->sequences[i].nameIndex); + dumpLine(avar(" %3d: %s%s%s\r\n", i, name, + mShape->sequences[i].isCyclic() ? " (cyclic)" : "", + mShape->sequences[i].isBlend() ? " (blend)" : "")); + } + + if (mShape->materialList) + { + TSMaterialList * ml = mShape->materialList; + dumpLine("\r\n Material list:\r\n"); + for (i=0; i<(S32)ml->size(); i++) + { + U32 flags = ml->getFlags(i); + const String& name = ml->getMaterialName(i); + dumpLine(avar( + " material #%i: '%s'%s.", i, name.c_str(), + flags & (TSMaterialList::S_Wrap|TSMaterialList::T_Wrap) ? "" : " not tiled") + ); + if (flags & TSMaterialList::Translucent) + { + if (flags & TSMaterialList::Additive) + dumpLine(" Additive-translucent.") + else if (flags & TSMaterialList::Subtractive) + dumpLine(" Subtractive-translucent.") + else + dumpLine(" Translucent.") + } + dumpLine("\r\n"); + } + } +} + diff --git a/Engine/source/ts/tsIntegerSet.cpp b/Engine/source/ts/tsIntegerSet.cpp new file mode 100644 index 000000000..dc8285e61 --- /dev/null +++ b/Engine/source/ts/tsIntegerSet.cpp @@ -0,0 +1,306 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "ts/tsIntegerSet.h" +#include "platform/platform.h" +#include "core/stream/stream.h" + +#define SETUPTO(upto) ( ((1<<(upto&31))-1)*2+1 ) // careful not to shift more than 31 times + +void TSIntegerSet::clearAll(S32 upto) +{ + AssertFatal(upto<=MAX_TS_SET_SIZE,"TSIntegerSet::clearAll: out of range"); + + dMemset(bits,0,(upto>>5)*4); + if (upto&31) + bits[upto>>5] &= ~SETUPTO(upto); +} + +void TSIntegerSet::setAll(S32 upto) +{ + AssertFatal(upto<=MAX_TS_SET_SIZE,"TSIntegerSet::setAll: out of range"); + + dMemset(bits,0xFFFFFFFF,(upto>>5)*4); + if (upto&31) + bits[upto>>5] |= SETUPTO(upto); +} + +bool TSIntegerSet::testAll(S32 upto) const +{ + AssertFatal(upto<=MAX_TS_SET_SIZE,"TSIntegerSet::testAll: out of range"); + S32 i; + for (i=0; i<(upto>>5); i++) + if (bits[i]) + return true; + if (upto&31) + return (bits[upto>>5] & SETUPTO(upto)) != 0; + return false; +} + +S32 TSIntegerSet::count(S32 upto) const +{ + AssertFatal(upto<=MAX_TS_SET_SIZE,"TSIntegerSet::count: out of range"); + + S32 count = 0; + U32 dword = bits[0]; + for (S32 i = 0; i < upto; i++) + { + // get next word + if (!(i & 0x1F)) + dword = bits[i>>5]; + // test bits in word + if (dword & (1 << (i & 0x1F))) + count++; + } + return count; +} + +void TSIntegerSet::intersect(const TSIntegerSet & otherSet) +{ + for (S32 i=0; i=0; i--) + { + // search for set bit one dword at a time + U32 dword = bits[i]; + if (bits[i]) + { + // got dword, now search one byte at a time + S32 j = 31; + U32 mask = 0xFF000000; + do + { + if (dword&mask) + { + // got byte, now one bit at a time + U32 bit = mask & ~(mask>>1); // grabs the highest bit + do + { + if (dword&bit) + return (i<<5)+j+1; + j--; + bit >>= 1; + } while (1); + } + mask >>= 8; + j -= 8; + } while (1); + } + } + + return 0; +} + +void TSIntegerSet::next(S32 & i) const +{ + i++; + U32 idx = i>>5; + U32 bit = 1 << (i&31); + U32 dword = bits[idx] & ~(bit-1); + while (dword==0) + { + i = (i+32) & ~31; + if (i>=MAX_TS_SET_SIZE) + return; + dword=bits[++idx]; + bit = 1; + } + dword = bits[idx]; + while ( (bit & dword) == 0) + { + bit <<= 1; + i++; + } +} + +/* Or would one byte at a time be better... +void TSIntegerSet::next(S32 & i) +{ + U32 idx = i>>3; + U8 bit = 1 << (i&7); + U8 byte = ((U8*)bits)[idx] & ~(bit*2-1); + while (byte==0) + { + i = (i+8) & ~7; + if (i>=MAX_TS_SET_SIZE) + return; + byte=((U8*)bits)[++idx]; + bit = 1; + } + byte = ((U8*)bits)[idx]; + while (bit & byte == 0) + { + bit <<= 1; + i++; + } +} +*/ + +void TSIntegerSet::copy(const TSIntegerSet & otherSet) +{ + dMemcpy(bits,otherSet.bits,MAX_TS_SET_DWORDS*4); +} + +void TSIntegerSet::insert(S32 index, bool value) +{ + AssertFatal(index> 5) + 1; + if (endWord >= MAX_TS_SET_DWORDS) + endWord = MAX_TS_SET_DWORDS-1; + + for (S32 i = endWord; i > (index >> 5); i--) + { + bits[i] = bits[i] << 1; + if (bits[i-1] & 0x80000000) + bits[i] |= 0x1; + } + + // shift to create space in target word + U32 lowMask = (1 << (index & 0x1f)) - 1; // bits below the insert point + U32 highMask = ~(lowMask | (1 << (index & 0x1f))); // bits above the insert point + + S32 word = index >> 5; + bits[word] = ((bits[word] << 1) & highMask) | (bits[word] & lowMask); + + // insert new value + if (value) + set(index); +} + +void TSIntegerSet::erase(S32 index) +{ + AssertFatal(index> 5; + U32 lowMask = (1 << (index & 0x1f)) - 1; // bits below the erase point + + bits[word] = ((bits[word] >> 1) & ~lowMask) | (bits[word] & lowMask); + + // shift bits in words after the erase point + U32 endWord = (end() >> 5) + 1; + if (endWord >= MAX_TS_SET_DWORDS) + endWord = MAX_TS_SET_DWORDS-1; + + for (S32 i = (index >> 5) + 1; i <= endWord; i++) + { + if (bits[i] & 0x1) + bits[i-1] |= 0x80000000; + bits[i] = bits[i] >> 1; + } +} + +TSIntegerSet::TSIntegerSet() +{ + clearAll(); +} + +TSIntegerSet::TSIntegerSet(const TSIntegerSet & otherSet) +{ + copy(otherSet); +} + +void TSIntegerSet::read(Stream * s) +{ + clearAll(); + + S32 numInts; + s->read(&numInts); // don't care about this + + S32 sz; + s->read(&sz); + AssertFatal(sz<=MAX_TS_SET_DWORDS,"TSIntegerSet:: set too large...increase max set size and re-compile"); + + for (S32 i=0; iread(&(bits[i])); +} + +void TSIntegerSet::write(Stream * s) const +{ + s->write((S32)0); // don't do this anymore, keep in to avoid versioning + S32 i,sz=0; + for (i=0; iwrite(sz); + for (i=0; iwrite(bits[i]); +} + diff --git a/Engine/source/ts/tsIntegerSet.h b/Engine/source/ts/tsIntegerSet.h new file mode 100644 index 000000000..ab5098f01 --- /dev/null +++ b/Engine/source/ts/tsIntegerSet.h @@ -0,0 +1,119 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TSINTEGERSET_H_ +#define _TSINTEGERSET_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + + +#if defined(TORQUE_MAX_LIB) +#define MAX_TS_SET_DWORDS 32 +#else +#define MAX_TS_SET_DWORDS 64 +#endif + +#define MAX_TS_SET_SIZE (32*MAX_TS_SET_DWORDS) + +class Stream; + +/// The standard mathmatical set, where there are no duplicates. However, +/// this set uses bits instead of numbers. +class TSIntegerSet +{ + /// The bits! + U32 bits[MAX_TS_SET_DWORDS]; + +public: + + /// Sets this bit to false + void clear(S32 index); + /// Set this bit to true + void set(S32 index); + /// Is this bit true? + bool test(S32 index) const; + + /// Sets all bits to false + void clearAll(S32 upto = MAX_TS_SET_SIZE); + /// Sets all bits to true + void setAll(S32 upto = MAX_TS_SET_SIZE); + /// Tests all bits for true + bool testAll(S32 upto = MAX_TS_SET_SIZE) const; + + /// Counts set bits + S32 count(S32 upto = MAX_TS_SET_SIZE) const; + + /// intersection (a & b) + void intersect(const TSIntegerSet&); + /// union (a | b) + void overlap(const TSIntegerSet&); + /// xor (a | b) & ( !(a & b) ) + void difference(const TSIntegerSet&); + /// subtraction (a - b) + void takeAway(const TSIntegerSet&); + + /// copy one integer set into another + void copy(const TSIntegerSet&); + + void insert(S32 index, bool value); + void erase(S32 index); + + void operator=(const TSIntegerSet& otherSet) { copy(otherSet); } + + S32 start() const; + S32 end() const; + void next(S32 & i) const; + + void read(Stream *); + void write(Stream *) const; + + TSIntegerSet(); + TSIntegerSet(const TSIntegerSet&); +}; + +inline void TSIntegerSet::clear(S32 index) +{ + AssertFatal(index>=0 && index>5] &= ~(1 << (index & 31)); +} + +inline void TSIntegerSet::set(S32 index) +{ + AssertFatal(index>=0 && index>5] |= 1 << (index & 31); +} + +inline bool TSIntegerSet::test(S32 index) const +{ + AssertFatal(index>=0 && index>5] & (1 << (index & 31)))!=0); +} + +#endif diff --git a/Engine/source/ts/tsLastDetail.cpp b/Engine/source/ts/tsLastDetail.cpp new file mode 100644 index 000000000..1e9ec9726 --- /dev/null +++ b/Engine/source/ts/tsLastDetail.cpp @@ -0,0 +1,553 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "ts/tsLastDetail.h" + +#include "renderInstance/renderPassManager.h" +#include "ts/tsShapeInstance.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "lighting/lightInfo.h" +#include "renderInstance/renderImposterMgr.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/bitmap/ddsFile.h" +#include "gfx/bitmap/ddsUtils.h" +#include "gfx/gfxTextureManager.h" +#include "math/mRandom.h" +#include "core/stream/fileStream.h" +#include "util/imposterCapture.h" +#include "materials/materialManager.h" +#include "materials/materialFeatureTypes.h" +#include "console/consoleTypes.h" + + +GFXImplementVertexFormat( ImposterState ) +{ + addElement( "POSITION", GFXDeclType_Float4 ); + + addElement( "ImposterParams", GFXDeclType_Float2, 0 ); + + addElement( "ImposterUpVec", GFXDeclType_Float3, 1 ); + addElement( "ImposterRightVec", GFXDeclType_Float3, 2 ); +}; + + +Vector TSLastDetail::smLastDetails; + +bool TSLastDetail::smCanShadow = true; + + +AFTER_MODULE_INIT( Sim ) +{ + Con::addVariable( "$pref::imposter::canShadow", TypeBool, &TSLastDetail::smCanShadow, + "User preference which toggles shadows from imposters. Defaults to true.\n" + "@ingroup Rendering\n" ); +} + + +TSLastDetail::TSLastDetail( TSShape *shape, + const String &cachePath, + U32 numEquatorSteps, + U32 numPolarSteps, + F32 polarAngle, + bool includePoles, + S32 dl, S32 dim ) +{ + mNumEquatorSteps = getMax( numEquatorSteps, (U32)1 ); + mNumPolarSteps = numPolarSteps; + mPolarAngle = polarAngle; + mIncludePoles = includePoles; + mShape = shape; + mDl = dl; + mDim = getMax( dim, (S32)32 ); + + mRadius = mShape->radius; + mCenter = mShape->center; + + mCachePath = cachePath; + + mMaterial = NULL; + mMatInstance = NULL; + + // Store this in the static list. + smLastDetails.push_back( this ); +} + +TSLastDetail::~TSLastDetail() +{ + SAFE_DELETE( mMatInstance ); + if ( mMaterial ) + mMaterial->deleteObject(); + + // Remove ourselves from the list. + Vector::iterator iter = find( smLastDetails.begin(), smLastDetails.end(), this ); + smLastDetails.erase( iter ); +} + +void TSLastDetail::render( const TSRenderState &rdata, F32 alpha ) +{ + // Early out if we have nothing to render. + if ( alpha < 0.01f || + !mMatInstance || + mMaterial->mImposterUVs.size() == 0 ) + return; + + const MatrixF &mat = GFX->getWorldMatrix(); + + // Post a render instance for this imposter... the special + // imposter render manager will do the magic! + RenderPassManager *renderPass = rdata.getSceneState()->getRenderPass(); + + ImposterRenderInst *ri = renderPass->allocInst(); + ri->mat = rdata.getSceneState()->getOverrideMaterial( mMatInstance ); + + ri->state.alpha = alpha; + + // Store the up and right vectors of the rotation + // and we'll generate the up vector in the shader. + // + // This is faster than building a quat on the + // CPU and then rebuilding the matrix on the GPU. + // + // NOTE: These vector include scale. + // + mat.getColumn( 2, &ri->state.upVec ); + mat.getColumn( 0, &ri->state.rightVec ); + + // We send the unscaled size and the vertex shader + // will use the orientation vectors above to scale it. + ri->state.halfSize = mRadius; + + // We use the center of the object bounds for + // the center of the billboard quad. + mat.mulP( mCenter, &ri->state.center ); + + // We sort by the imposter type first so that RIT_Imposter and s + // RIT_ImposterBatches do not get mixed together. + // + // We then sort by material. + // + ri->defaultKey = 1; + ri->defaultKey2 = ri->mat->getStateHint(); + + renderPass->addInst( ri ); +} + +void TSLastDetail::update( bool forceUpdate ) +{ + // This should never be called on a dedicated server or + // anywhere else where we don't have a GFX device! + AssertFatal( GFXDevice::devicePresent(), "TSLastDetail::update() - Cannot update without a GFX device!" ); + + // Clear the materialfirst. + SAFE_DELETE( mMatInstance ); + if ( mMaterial ) + { + mMaterial->deleteObject(); + mMaterial = NULL; + } + + // Make sure imposter textures have been flushed (and not just queued for deletion) + TEXMGR->cleanupCache(); + + // Get the real path to the source shape for doing modified time + // comparisons... this might be different if the DAEs have been + // deleted from the install. + String shapeFile( mCachePath ); + if ( !Platform::isFile( shapeFile ) ) + { + Torque::Path path(shapeFile); + path.setExtension("cached.dts"); + shapeFile = path.getFullPath(); + if ( !Platform::isFile( shapeFile ) ) + { + Con::errorf( "TSLastDetail::update - '%s' could not be found!", mCachePath.c_str() ); + return; + } + } + + // Do we need to update the imposter? + const String diffuseMapPath = _getDiffuseMapPath(); + if ( forceUpdate || + Platform::compareModifiedTimes( diffuseMapPath, shapeFile ) <= 0 ) + _update(); + + // If the time check fails now then the update must have not worked. + if ( Platform::compareModifiedTimes( diffuseMapPath, shapeFile ) < 0 ) + { + Con::errorf( "TSLastDetail::update - Failed to create imposters for '%s'!", mCachePath.c_str() ); + return; + } + + // Figure out what our vertex format will be. + // + // If we're on SM 3.0 we can do multiple vertex streams + // and the performance win is big as we send 3x less data + // on each imposter instance. + // + // The problem is SM 2.0 won't do this, so we need to + // support fallback to regular single stream imposters. + // + //mImposterVertDecl.copy( *getGFXVertexFormat() ); + //mImposterVertDecl.append( *getGFXVertexFormat(), 1 ); + //mImposterVertDecl.getDecl(); + mImposterVertDecl.clear(); + mImposterVertDecl.copy( *getGFXVertexFormat() ); + + // Setup the material for this imposter. + mMaterial = MATMGR->allocateAndRegister( String::EmptyString ); + mMaterial->mAutoGenerated = true; + mMaterial->mDiffuseMapFilename[0] = diffuseMapPath; + mMaterial->mNormalMapFilename[0] = _getNormalMapPath(); + mMaterial->mImposterLimits.set( (mNumPolarSteps * 2) + 1, mNumEquatorSteps, mPolarAngle, mIncludePoles ); + mMaterial->mTranslucent = true; + mMaterial->mTranslucentBlendOp = Material::None; + mMaterial->mTranslucentZWrite = true; + mMaterial->mDoubleSided = true; + mMaterial->mAlphaTest = true; + mMaterial->mAlphaRef = 84; + + // Create the material instance. + FeatureSet features = MATMGR->getDefaultFeatures(); + features.addFeature( MFT_ImposterVert ); + mMatInstance = mMaterial->createMatInstance(); + if ( !mMatInstance->init( features, &mImposterVertDecl ) ) + { + delete mMatInstance; + mMatInstance = NULL; + } + + // Get the diffuse texture and from its size and + // the imposter dimensions we can generate the UVs. + GFXTexHandle diffuseTex( diffuseMapPath, &GFXDefaultStaticDiffuseProfile, String::EmptyString ); + Point2I texSize( diffuseTex->getWidth(), diffuseTex->getHeight() ); + + _validateDim(); + + S32 downscaledDim = mDim >> GFXTextureManager::getTextureDownscalePower(&GFXDefaultStaticDiffuseProfile); + + // Ok... pack in bitmaps till we run out. + Vector imposterUVs; + for ( S32 y=0; y+downscaledDim <= texSize.y; ) + { + for ( S32 x=0; x+downscaledDim <= texSize.x; ) + { + // Store the uv for later lookup. + RectF info; + info.point.set( (F32)x / (F32)texSize.x, (F32)y / (F32)texSize.y ); + info.extent.set( (F32)downscaledDim / (F32)texSize.x, (F32)downscaledDim / (F32)texSize.y ); + imposterUVs.push_back( info ); + + x += downscaledDim; + } + + y += downscaledDim; + } + + AssertFatal( imposterUVs.size() != 0, "hey" ); + + mMaterial->mImposterUVs = imposterUVs; +} + +void TSLastDetail::_validateDim() +{ + // Loop till they fit. + S32 newDim = mDim; + while ( true ) + { + S32 maxImposters = ( smMaxTexSize / newDim ) * ( smMaxTexSize / newDim ); + S32 imposterCount = ( ((2*mNumPolarSteps) + 1 ) * mNumEquatorSteps ) + ( mIncludePoles ? 2 : 0 ); + if ( imposterCount <= maxImposters ) + break; + + // There are too many imposters to fit a single + // texture, so we fail. These imposters are for + // rendering small distant objects. If you need + // a really high resolution imposter or many images + // around the equator and poles, maybe you need a + // custom solution. + + newDim /= 2; + } + + if ( newDim != mDim ) + { + Con::printf( "TSLastDetail::_validateDim - '%s' detail dimensions too big! Reduced from %d to %d.", + mCachePath.c_str(), + mDim, newDim ); + + mDim = newDim; + } +} + +void TSLastDetail::_update() +{ + // We're gonna render... make sure we can. + bool sceneBegun = GFX->canCurrentlyRender(); + if ( !sceneBegun ) + GFX->beginScene(); + + _validateDim(); + + Vector bitmaps; + Vector normalmaps; + + // We need to create our own instance to render with. + TSShapeInstance *shape = new TSShapeInstance( mShape, true ); + + // Animate the shape once. + shape->animate( mDl ); + + // So we don't have to change it everywhere. + const GFXFormat format = GFXFormatR8G8B8A8; + + S32 imposterCount = ( ((2*mNumPolarSteps) + 1 ) * mNumEquatorSteps ) + ( mIncludePoles ? 2 : 0 ); + + // Figure out the optimal texture size. + Point2I texSize( smMaxTexSize, smMaxTexSize ); + while ( true ) + { + Point2I halfSize( texSize.x / 2, texSize.y / 2 ); + U32 count = ( halfSize.x / mDim ) * ( halfSize.y / mDim ); + if ( count < imposterCount ) + { + // Try half of the height. + count = ( texSize.x / mDim ) * ( halfSize.y / mDim ); + if ( count >= imposterCount ) + texSize.y = halfSize.y; + break; + } + + texSize = halfSize; + } + + GBitmap *imposter = NULL; + GBitmap *normalmap = NULL; + GBitmap destBmp( texSize.x, texSize.y, true, format ); + GBitmap destNormal( texSize.x, texSize.y, true, format ); + + U32 mipLevels = destBmp.getNumMipLevels(); + + ImposterCapture *imposterCap = new ImposterCapture(); + + F32 equatorStepSize = M_2PI_F / (F32)mNumEquatorSteps; + + static const MatrixF topXfm( EulerF( -M_PI_F / 2.0f, 0, 0 ) ); + static const MatrixF bottomXfm( EulerF( M_PI_F / 2.0f, 0, 0 ) ); + + MatrixF angMat; + + F32 polarStepSize = 0.0f; + if ( mNumPolarSteps > 0 ) + polarStepSize = -( 0.5f * M_PI_F - mDegToRad( mPolarAngle ) ) / (F32)mNumPolarSteps; + + PROFILE_START(TSLastDetail_snapshots); + + S32 currDim = mDim; + for ( S32 mip = 0; mip < mipLevels; mip++ ) + { + if ( currDim < 1 ) + currDim = 1; + + dMemset( destBmp.getWritableBits(mip), 0, destBmp.getWidth(mip) * destBmp.getHeight(mip) * GFXFormat_getByteSize( format ) ); + dMemset( destNormal.getWritableBits(mip), 0, destNormal.getWidth(mip) * destNormal.getHeight(mip) * GFXFormat_getByteSize( format ) ); + + bitmaps.clear(); + normalmaps.clear(); + + F32 rotX = 0.0f; + if ( mNumPolarSteps > 0 ) + rotX = -( mDegToRad( mPolarAngle ) - 0.5f * M_PI_F ); + + // We capture the images in a particular order which must + // match the order expected by the imposter renderer. + + imposterCap->begin( shape, mDl, currDim, mRadius, mCenter ); + + for ( U32 j=0; j < (2 * mNumPolarSteps + 1); j++ ) + { + F32 rotZ = -M_PI_F / 2.0f; + + for ( U32 k=0; k < mNumEquatorSteps; k++ ) + { + angMat.mul( MatrixF( EulerF( rotX, 0, 0 ) ), + MatrixF( EulerF( 0, 0, rotZ ) ) ); + + imposterCap->capture( angMat, &imposter, &normalmap ); + + bitmaps.push_back( imposter ); + normalmaps.push_back( normalmap ); + + rotZ += equatorStepSize; + } + + rotX += polarStepSize; + + if ( mIncludePoles ) + { + imposterCap->capture( topXfm, &imposter, &normalmap ); + + bitmaps.push_back(imposter); + normalmaps.push_back( normalmap ); + + imposterCap->capture( bottomXfm, &imposter, &normalmap ); + + bitmaps.push_back( imposter ); + normalmaps.push_back( normalmap ); + } + } + + imposterCap->end(); + + Point2I texSize( destBmp.getWidth(mip), destBmp.getHeight(mip) ); + + // Ok... pack in bitmaps till we run out. + for ( S32 y=0; y+currDim <= texSize.y; ) + { + for ( S32 x=0; x+currDim <= texSize.x; ) + { + // Copy the next bitmap to the dest texture. + GBitmap* bmp = bitmaps.first(); + bitmaps.pop_front(); + destBmp.copyRect( bmp, RectI( 0, 0, currDim, currDim ), Point2I( x, y ), 0, mip ); + delete bmp; + + // Copy the next normal to the dest texture. + GBitmap* normalmap = normalmaps.first(); + normalmaps.pop_front(); + destNormal.copyRect( normalmap, RectI( 0, 0, currDim, currDim ), Point2I( x, y ), 0, mip ); + delete normalmap; + + // Did we finish? + if ( bitmaps.empty() ) + break; + + x += currDim; + } + + // Did we finish? + if ( bitmaps.empty() ) + break; + + y += currDim; + } + + // Next mip... + currDim /= 2; + } + + PROFILE_END(); // TSLastDetail_snapshots + + delete imposterCap; + delete shape; + + + // Should we dump the images? + if ( Con::getBoolVariable( "$TSLastDetail::dumpImposters", false ) ) + { + String imposterPath = mCachePath + ".imposter.png"; + String normalsPath = mCachePath + ".imposter_normals.png"; + + FileStream stream; + if ( stream.open( imposterPath, Torque::FS::File::Write ) ) + destBmp.writeBitmap( "png", stream ); + stream.close(); + + if ( stream.open( normalsPath, Torque::FS::File::Write ) ) + destNormal.writeBitmap( "png", stream ); + stream.close(); + } + + // DEBUG: Some code to force usage of a test image. + //GBitmap* tempMap = GBitmap::load( "./forest/data/test1234.png" ); + //tempMap->extrudeMipLevels(); + //mTexture.set( tempMap, &GFXDefaultStaticDiffuseProfile, false ); + //delete tempMap; + + DDSFile *ddsDest = DDSFile::createDDSFileFromGBitmap( &destBmp ); + DDSUtil::squishDDS( ddsDest, GFXFormatDXT3 ); + + DDSFile *ddsNormals = DDSFile::createDDSFileFromGBitmap( &destNormal ); + DDSUtil::squishDDS( ddsNormals, GFXFormatDXT5 ); + + // Finally save the imposters to disk. + FileStream fs; + if ( fs.open( _getDiffuseMapPath(), Torque::FS::File::Write ) ) + { + ddsDest->write( fs ); + fs.close(); + } + if ( fs.open( _getNormalMapPath(), Torque::FS::File::Write ) ) + { + ddsNormals->write( fs ); + fs.close(); + } + + delete ddsDest; + delete ddsNormals; + + // If we did a begin then end it now. + if ( !sceneBegun ) + GFX->endScene(); +} + +void TSLastDetail::deleteImposterCacheTextures() +{ + const String diffuseMap = _getDiffuseMapPath(); + if ( diffuseMap.length() ) + dFileDelete( diffuseMap ); + + const String normalMap = _getNormalMapPath(); + if ( normalMap.length() ) + dFileDelete( normalMap ); +} + +void TSLastDetail::updateImposterImages( bool forceUpdate ) +{ + // Can't do it without GFX! + if ( !GFXDevice::devicePresent() ) + return; + + //D3DPERF_SetMarker( D3DCOLOR_RGBA( 0, 255, 0, 255 ), L"TSLastDetail::makeImposter" ); + + bool sceneBegun = GFX->canCurrentlyRender(); + if ( !sceneBegun ) + GFX->beginScene(); + + Vector::iterator iter = smLastDetails.begin(); + for ( ; iter != smLastDetails.end(); iter++ ) + (*iter)->update( forceUpdate ); + + if ( !sceneBegun ) + GFX->endScene(); +} + +ConsoleFunction(tsUpdateImposterImages, void, 1, 2, "tsUpdateImposterImages( bool forceupdate )") +{ + if ( argc > 1 ) + TSLastDetail::updateImposterImages( dAtob( argv[1] ) ); + else + TSLastDetail::updateImposterImages(); +} + + diff --git a/Engine/source/ts/tsLastDetail.h b/Engine/source/ts/tsLastDetail.h new file mode 100644 index 000000000..adfd6b5ac --- /dev/null +++ b/Engine/source/ts/tsLastDetail.h @@ -0,0 +1,207 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TSLASTDETAIL_H_ +#define _TSLASTDETAIL_H_ + +#ifndef _MATHTYPES_H_ +#include "math/mathTypes.h" +#endif +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef __RESOURCE_H__ +#include "core/resource.h" +#endif +#ifndef _TSRENDERDATA_H_ +#include "ts/tsRenderState.h" +#endif +#ifndef _GFXVERTEXFORMAT_H_ +#include "gfx/gfxVertexFormat.h" +#endif +#ifndef _SIM_H_ +#include "console/simObject.h" +#endif + + +class TSShape; +class TSRenderState; +class SceneRenderState; +class Material; +class BaseMatInstance; + + +/// The imposter state vertex format. +GFXDeclareVertexFormat( ImposterState ) +{ + /// .xyz = imposter center + /// .w = billboard corner... damn SM 2.0 + Point3F center; + float corner; + + /// .x = scaled half size + /// .y = alpha fade out + float halfSize; + float alpha; + + /// The rotation encoded as the up + /// and right vectors... cross FTW. + Point3F upVec; + Point3F rightVec; +}; + + +/// This neat little class renders the object to a texture so that when the object +/// is far away, it can be drawn as a billboard instead of a mesh. This happens +/// when the model is first loaded as to keep the realtime render as fast as possible. +/// It also renders the model from a few different perspectives so that it would actually +/// pass as a model instead of a silly old billboard. In other words, this is an imposter. +class TSLastDetail +{ +protected: + + /// The shape which we're impostering. + TSShape *mShape; + + /// This is the path of the object, which is + /// where we'll be storing our cache for rendered imposters. + String mCachePath; + + /// The shape detail level to capture into + /// the imposters. + S32 mDl; + + /// The bounding radius of the shape + /// used to size the billboard. + F32 mRadius; + + /// The center offset for the bounding + /// sphere used to render the imposter. + Point3F mCenter; + + /// The square dimensions of each + /// captured imposter image. + S32 mDim; + + /// The number steps around the equator of + /// the globe at which we capture an imposter. + U32 mNumEquatorSteps; + + /// The number of steps to go from equator to + /// each polar region (0 means equator only) at + /// which we capture an imposter. + U32 mNumPolarSteps; + + /// The angle in radians of sub-polar regions. + F32 mPolarAngle; + + /// If true we captures polar images in the + /// imposter texture. + bool mIncludePoles; + + /// The combined imposter state and corner data vertex + /// format used for rendering with multiple streams. + GFXVertexFormat mImposterVertDecl; + + /// The material for this imposter. + SimObjectPtr mMaterial; + + /// The material instance used to render this imposter. + BaseMatInstance *mMatInstance; + + /// This is a global list of all the TSLastDetail + /// objects in the system. + static Vector smLastDetails; + + /// The maximum texture size for a billboard texture. + static const U32 smMaxTexSize = 2048; + + /// This update actually regenerates the imposter images. + void _update(); + + /// + void _validateDim(); + + /// Helper which returns the imposter diffuse map path. + String _getDiffuseMapPath() const { return mCachePath + ".imposter.dds"; } + + /// Helper which returns the imposter normal map path. + String _getNormalMapPath() const { return mCachePath + ".imposter_normals.dds"; } + +public: + + TSLastDetail( TSShape *shape, + const String &cachePath, + U32 numEquatorSteps, + U32 numPolarSteps, + F32 polarAngle, + bool includePoles, + S32 dl, + S32 dim ); + + ~TSLastDetail(); + + /// Global preference for rendering imposters to shadows. + static bool smCanShadow; + + /// Calls update on all TSLastDetail objects in the system. + /// @see update() + static void updateImposterImages( bool forceUpdate = false ); + + /// Loads the imposter images by reading them from the disk + /// or generating them if the TSShape is more recient than the + /// cached imposter textures. + /// + /// This should not be called from within any rendering code. + /// + /// @param forceUpdate If true the disk cache is invalidated and + /// new imposter images are rendered. + /// + void update( bool forceUpdate = false ); + + + /// Internal function called from TSShapeInstance to + /// submit an imposter render instance. + void render( const TSRenderState &rdata, F32 alpha ); + + /// Returns the material instance used to render this imposter. + BaseMatInstance* getMatInstance() const { return mMatInstance; } + + /// Helper function which deletes the cached imposter + /// texture files from disk. + void deleteImposterCacheTextures(); + + /// Returns the radius. + /// @see mRadius + F32 getRadius() const { return mRadius; } +}; + + +#endif // _TSLASTDETAIL_H_ + + diff --git a/Engine/source/ts/tsMaterialList.cpp b/Engine/source/ts/tsMaterialList.cpp new file mode 100644 index 000000000..78a8a720a --- /dev/null +++ b/Engine/source/ts/tsMaterialList.cpp @@ -0,0 +1,323 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "ts/tsMaterialList.h" + +#include "ts/tsShape.h" +#include "materials/matInstance.h" +#include "materials/materialManager.h" + + +TSMaterialList::TSMaterialList(U32 materialCount, + const char **materialNames, + const U32 * materialFlags, + const U32 * reflectanceMaps, + const U32 * bumpMaps, + const U32 * detailMaps, + const F32 * detailScales, + const F32 * reflectionAmounts) + : MaterialList(materialCount,materialNames), + mNamesTransformed(false) +{ + VECTOR_SET_ASSOCIATION(mFlags); + VECTOR_SET_ASSOCIATION(mReflectanceMaps); + VECTOR_SET_ASSOCIATION(mBumpMaps); + VECTOR_SET_ASSOCIATION(mDetailMaps); + VECTOR_SET_ASSOCIATION(mDetailScales); + VECTOR_SET_ASSOCIATION(mReflectionAmounts); + + allocate(materialCount); + + dMemcpy(mFlags.address(),materialFlags,materialCount*sizeof(U32)); + dMemcpy(mReflectanceMaps.address(),reflectanceMaps,materialCount*sizeof(U32)); + dMemcpy(mBumpMaps.address(),bumpMaps,materialCount*sizeof(U32)); + dMemcpy(mDetailMaps.address(),detailMaps,materialCount*sizeof(U32)); + dMemcpy(mDetailScales.address(),detailScales,materialCount*sizeof(F32)); + dMemcpy(mReflectionAmounts.address(),reflectionAmounts,materialCount*sizeof(F32)); +} + +TSMaterialList::TSMaterialList() + : mNamesTransformed(false) +{ + VECTOR_SET_ASSOCIATION(mFlags); + VECTOR_SET_ASSOCIATION(mReflectanceMaps); + VECTOR_SET_ASSOCIATION(mBumpMaps); + VECTOR_SET_ASSOCIATION(mDetailMaps); + VECTOR_SET_ASSOCIATION(mDetailScales); + VECTOR_SET_ASSOCIATION(mReflectionAmounts); +} + +TSMaterialList::TSMaterialList(const TSMaterialList* pCopy) + : MaterialList(pCopy) +{ + VECTOR_SET_ASSOCIATION(mFlags); + VECTOR_SET_ASSOCIATION(mReflectanceMaps); + VECTOR_SET_ASSOCIATION(mBumpMaps); + VECTOR_SET_ASSOCIATION(mDetailMaps); + VECTOR_SET_ASSOCIATION(mDetailScales); + VECTOR_SET_ASSOCIATION(mReflectionAmounts); + + mFlags = pCopy->mFlags; + mReflectanceMaps = pCopy->mReflectanceMaps; + mBumpMaps = pCopy->mBumpMaps; + mDetailMaps = pCopy->mDetailMaps; + mDetailScales = pCopy->mDetailScales; + mReflectionAmounts = pCopy->mReflectionAmounts; + mNamesTransformed = pCopy->mNamesTransformed; +} + +TSMaterialList::~TSMaterialList() +{ + free(); +} + +void TSMaterialList::free() +{ + // these aren't found on our parent, clear them out here to keep in synch + mFlags.clear(); + mReflectanceMaps.clear(); + mBumpMaps.clear(); + mDetailMaps.clear(); + mDetailScales.clear(); + mReflectionAmounts.clear(); + + Parent::free(); +} + +void TSMaterialList::push_back(const String &name, U32 flags, U32 rMap, U32 bMap, U32 dMap, F32 dScale, F32 emapAmount) +{ + Parent::push_back(name); + mFlags.push_back(flags); + if (rMap==0xFFFFFFFF) + mReflectanceMaps.push_back(size()-1); + else + mReflectanceMaps.push_back(rMap); + mBumpMaps.push_back(bMap); + mDetailMaps.push_back(dMap); + mDetailScales.push_back(dScale); + mReflectionAmounts.push_back(emapAmount); +} + +void TSMaterialList::push_back(const char * name, U32 flags, Material* mat) +{ + Parent::push_back(name, mat); + mFlags.push_back(flags); + mReflectanceMaps.push_back(size()-1); + mBumpMaps.push_back(0xFFFFFFFF); + mDetailMaps.push_back(0xFFFFFFFF); + mDetailScales.push_back(1.0f); + mReflectionAmounts.push_back(1.0f); +} + +void TSMaterialList::allocate(U32 sz) +{ + mFlags.setSize(sz); + mReflectanceMaps.setSize(sz); + mBumpMaps.setSize(sz); + mDetailMaps.setSize(sz); + mDetailScales.setSize(sz); + mReflectionAmounts.setSize(sz); +} + +U32 TSMaterialList::getFlags(U32 index) +{ + AssertFatal(index < size(),"TSMaterialList::getFlags: index out of range"); + return mFlags[index]; +} + +void TSMaterialList::setFlags(U32 index, U32 value) +{ + AssertFatal(index < size(),"TSMaterialList::getFlags: index out of range"); + mFlags[index] = value; +} + +bool TSMaterialList::write(Stream & s) +{ + if (!Parent::write(s)) + return false; + + U32 i; + for (i=0; i 11) + { + for (i=0; i 20) + { + for (i=0; i size() || newName.isEmpty()) + return false; + + // Check if already using this name + if (newName.equal(mMaterialNames[i], String::NoCase)) + { + // same material, return true since we aren't changing it + return true; + } + + // Allow the rename if the new name is mapped, or if there is a diffuse texture + // available (for which a simple Material can be generated in mapMaterial) + String mappedName = MATMGR->getMapEntry(newName); + if (mappedName.isEmpty()) + { + GFXTexHandle texHandle; + if (mLookupPath.isEmpty()) + { + texHandle.set( newName, &GFXDefaultStaticDiffuseProfile, avar("%s() - handle (line %d)", __FUNCTION__, __LINE__) ); + } + else + { + String fullPath = String::ToString( "%s/%s", mLookupPath.c_str(), newName.c_str() ); + texHandle.set( fullPath, &GFXDefaultStaticDiffuseProfile, avar("%s() - handle (line %d)", __FUNCTION__, __LINE__) ); + } + if (!texHandle.isValid()) + return false; + } + + // change material name + mMaterialNames[i] = newName; + + // Dump the old mat instance and remap the material. + if( mMatInstList[ i ] ) + SAFE_DELETE( mMatInstList[ i ] ); + mapMaterial( i ); + + return true; +} + +void TSMaterialList::mapMaterial( U32 i ) +{ + Parent::mapMaterial( i ); + + BaseMatInstance* matInst = mMatInstList[i]; + if (matInst && matInst->getMaterial()->isTranslucent()) + mFlags[i] |= Translucent; +} diff --git a/Engine/source/ts/tsMaterialList.h b/Engine/source/ts/tsMaterialList.h new file mode 100644 index 000000000..15294f334 --- /dev/null +++ b/Engine/source/ts/tsMaterialList.h @@ -0,0 +1,98 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TSMATERIALLIST_H_ +#define _TSMATERIALLIST_H_ + +#ifndef _MATERIALLIST_H_ +#include "materials/materialList.h" +#endif +#ifndef _PATH_H_ +#include "core/util/path.h" +#endif + + +/// Specialized material list for 3space objects. +class TSMaterialList : public MaterialList +{ + typedef MaterialList Parent; + + Vector mFlags; + Vector mReflectanceMaps; + Vector mBumpMaps; + Vector mDetailMaps; + Vector mDetailScales; + Vector mReflectionAmounts; + + bool mNamesTransformed; + + void allocate(U32 sz); + + public: + + enum + { + S_Wrap = BIT(0), + T_Wrap = BIT(1), + Translucent = BIT(2), + Additive = BIT(3), + Subtractive = BIT(4), + SelfIlluminating = BIT(5), + NeverEnvMap = BIT(6), + NoMipMap = BIT(7), + MipMap_ZeroBorder = BIT(8), + AuxiliaryMap = BIT(27) | BIT(28) | BIT(29) | BIT(30) | BIT(31) // DEPRECATED + }; + + TSMaterialList(U32 materialCount, const char **materialNames, const U32 * materialFlags, + const U32 * reflectanceMaps, const U32 * bumpMaps, const U32 * detailMaps, + const F32 * detailScales, const F32 * reflectionAmounts); + TSMaterialList(); + TSMaterialList(const TSMaterialList*); + ~TSMaterialList(); + void free(); + + U32 getFlags(U32 index); + void setFlags(U32 index, U32 value); + + bool renameMaterial(U32 index, const String& newName); // use to support reskinning + + /// pre-load only + void push_back(const String &name, U32 flags, + U32 a=0xFFFFFFFF, U32 b=0xFFFFFFFF, U32 c=0xFFFFFFFF, + F32 dm=1.0f, F32 em=1.0f); + void push_back(const char * name, U32 flags, Material* mat); + + /// @name IO + /// Functions for reading/writing to/from streams + /// @{ + + bool write(Stream &); + bool read(Stream &); + /// @} + +protected: + virtual void mapMaterial( U32 index ); +}; + + +#endif // _TSMATERIALLIST_H_ diff --git a/Engine/source/ts/tsMesh.cpp b/Engine/source/ts/tsMesh.cpp new file mode 100644 index 000000000..ec0ca6168 --- /dev/null +++ b/Engine/source/ts/tsMesh.cpp @@ -0,0 +1,3043 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "ts/tsMesh.h" + +#include "ts/tsMeshIntrinsics.h" +#include "ts/tsDecal.h" +#include "ts/tsSortedMesh.h" +#include "ts/tsShape.h" +#include "ts/tsShapeInstance.h" +#include "ts/tsRenderState.h" +#include "ts/tsMaterialList.h" +#include "ts/instancingMatHook.h" +#include "math/mMath.h" +#include "math/mathIO.h" +#include "math/mathUtils.h" +#include "console/console.h" +#include "scene/sceneObject.h" +#include "core/bitRender.h" +#include "collision/convex.h" +#include "collision/optimizedPolyList.h" +#include "core/frameAllocator.h" +#include "platform/profiler.h" +#include "materials/sceneData.h" +#include "materials/materialManager.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "materials/matInstance.h" +#include "renderInstance/renderPassManager.h" +#include "materials/customMaterialDefinition.h" +#include "gfx/util/triListOpt.h" +#include "util/triRayCheck.h" + +#include "opcode/Opcode.h" + +#if defined(TORQUE_OS_XENON) +# include "platformXbox/platformXbox.h" +#endif + +GFXPrimitiveType drawTypes[] = { GFXTriangleList, GFXTriangleStrip, GFXTriangleFan }; +#define getDrawType(a) (drawTypes[a]) + + +// structures used to share data between detail levels... +// used (and valid) during load only +Vector TSMesh::smVertsList; +Vector TSMesh::smNormsList; +Vector TSMesh::smEncodedNormsList; +Vector TSMesh::smTVertsList; +Vector TSMesh::smTVerts2List; +Vector TSMesh::smColorsList; + +Vector TSMesh::smDataCopied; + +Vector TSSkinMesh::smInitTransformList; +Vector TSSkinMesh::smVertexIndexList; +Vector TSSkinMesh::smBoneIndexList; +Vector TSSkinMesh::smWeightList; +Vector TSSkinMesh::smNodeIndexList; + +Vector gNormalStore; + +bool TSMesh::smUseTriangles = false; // convert all primitives to triangle lists on load +bool TSMesh::smUseOneStrip = true; // join triangle strips into one long strip on load +S32 TSMesh::smMinStripSize = 1; // smallest number of _faces_ allowed per strip (all else put in tri list) +bool TSMesh::smUseEncodedNormals = false; + +const F32 TSMesh::VISIBILITY_EPSILON = 0.0001f; + +S32 TSMesh::smMaxInstancingVerts = 200; + +// quick function to force object to face camera -- currently throws out roll :( +void tsForceFaceCamera( MatrixF *mat, const Point3F *objScale ) +{ + Point4F p; + mat->getColumn( 3, &p ); + mat->identity(); + mat->setColumn( 3, p ); + + if ( objScale ) + { + MatrixF scale( true ); + scale.scale( *objScale ); + mat->mul( scale ); + } +} + +//----------------------------------------------------- +// TSMesh render methods +//----------------------------------------------------- + +void TSMesh::render( TSVertexBufferHandle &instanceVB, GFXPrimitiveBufferHandle &instancePB ) +{ + // A TSMesh never uses the instanceVB. + TORQUE_UNUSED( instanceVB ); + TORQUE_UNUSED( instancePB ); + + innerRender( mVB, mPB ); +} + +void TSMesh::innerRender( TSVertexBufferHandle &vb, GFXPrimitiveBufferHandle &pb ) +{ + if ( !vb.isValid() || !pb.isValid() ) + return; + + GFX->setVertexBuffer( vb ); + GFX->setPrimitiveBuffer( pb ); + + for( U32 p = 0; p < primitives.size(); p++ ) + GFX->drawPrimitive( p ); +} + + +void TSMesh::render( TSMaterialList *materials, + const TSRenderState &rdata, + bool isSkinDirty, + const Vector &transforms, + TSVertexBufferHandle &vertexBuffer, + GFXPrimitiveBufferHandle &primitiveBuffer ) +{ + // These are only used by TSSkinMesh. + TORQUE_UNUSED( isSkinDirty ); + TORQUE_UNUSED( transforms ); + TORQUE_UNUSED( vertexBuffer ); + TORQUE_UNUSED( primitiveBuffer ); + + // Pass our shared VB. + innerRender( materials, rdata, mVB, mPB ); +} + +void TSMesh::innerRender( TSMaterialList *materials, const TSRenderState &rdata, TSVertexBufferHandle &vb, GFXPrimitiveBufferHandle &pb ) +{ + PROFILE_SCOPE( TSMesh_InnerRender ); + + if( vertsPerFrame <= 0 ) + return; + + F32 meshVisibility = rdata.getFadeOverride() * mVisibility; + if ( meshVisibility < VISIBILITY_EPSILON ) + return; + + const SceneRenderState *state = rdata.getSceneState(); + RenderPassManager *renderPass = state->getRenderPass(); + + MeshRenderInst *coreRI = renderPass->allocInst(); + coreRI->type = RenderPassManager::RIT_Mesh; + + const MatrixF &objToWorld = GFX->getWorldMatrix(); + + // Sort by the center point or the bounds. + if ( rdata.useOriginSort() ) + coreRI->sortDistSq = ( objToWorld.getPosition() - state->getCameraPosition() ).lenSquared(); + else + { + Box3F rBox = mBounds; + objToWorld.mul( rBox ); + coreRI->sortDistSq = rBox.getSqDistanceToPoint( state->getCameraPosition() ); + } + + if (getFlags(Billboard)) + { + Point3F camPos = state->getDiffuseCameraPosition(); + Point3F objPos; + objToWorld.getColumn(3, &objPos); + Point3F targetVector = camPos - objPos; + if(getFlags(BillboardZAxis)) + targetVector.z = 0.0f; + targetVector.normalize(); + MatrixF orient = MathUtils::createOrientFromDir(targetVector); + orient.setPosition(objPos); + orient.scale(objToWorld.getScale()); + + coreRI->objectToWorld = renderPass->allocUniqueXform( orient ); + } + else + coreRI->objectToWorld = renderPass->allocUniqueXform( objToWorld ); + + coreRI->worldToCamera = renderPass->allocSharedXform(RenderPassManager::View); + coreRI->projection = renderPass->allocSharedXform(RenderPassManager::Projection); + + AssertFatal( vb.isValid(), "TSMesh::innerRender() - Got invalid vertex buffer!" ); + AssertFatal( pb.isValid(), "TSMesh::innerRender() - Got invalid primitive buffer!" ); + + coreRI->vertBuff = &vb; + coreRI->primBuff = &pb; + coreRI->defaultKey2 = (U32) coreRI->vertBuff; + + coreRI->materialHint = rdata.getMaterialHint(); + + coreRI->visibility = meshVisibility; + coreRI->cubemap = rdata.getCubemap(); + + // NOTICE: SFXBB is removed and refraction is disabled! + //coreRI->backBuffTex = GFX->getSfxBackBuffer(); + + for ( S32 i = 0; i < primitives.size(); i++ ) + { + const TSDrawPrimitive &draw = primitives[i]; + + // We need to have a material. + if ( draw.matIndex & TSDrawPrimitive::NoMaterial ) + continue; + +#ifdef TORQUE_DEBUG + // for inspection if you happen to be running in a debugger and can't do bit + // operations in your head. + S32 triangles = draw.matIndex & TSDrawPrimitive::Triangles; + S32 strip = draw.matIndex & TSDrawPrimitive::Strip; + S32 fan = draw.matIndex & TSDrawPrimitive::Fan; + S32 indexed = draw.matIndex & TSDrawPrimitive::Indexed; + S32 type = draw.matIndex & TSDrawPrimitive::TypeMask; + TORQUE_UNUSED(triangles); + TORQUE_UNUSED(strip); + TORQUE_UNUSED(fan); + TORQUE_UNUSED(indexed); + TORQUE_UNUSED(type); +#endif + + const U32 matIndex = draw.matIndex & TSDrawPrimitive::MaterialMask; + BaseMatInstance *matInst = materials->getMaterialInst( matIndex ); + +#ifndef TORQUE_OS_MAC + + // Get the instancing material if this mesh qualifies. + if ( meshType != SkinMeshType && pb->mPrimitiveArray[i].numVertices < smMaxInstancingVerts ) + matInst = InstancingMaterialHook::getInstancingMat( matInst ); + +#endif + + // If we don't have a material instance after the overload then + // there is nothing to render... skip this primitive. + matInst = state->getOverrideMaterial( matInst ); + if ( !matInst || !matInst->isValid()) + continue; + + // If the material needs lights then gather them + // here once and set them on the core render inst. + if ( matInst->isForwardLit() && !coreRI->lights[0] && rdata.getLightQuery() ) + rdata.getLightQuery()->getLights( coreRI->lights, 8 ); + + MeshRenderInst *ri = renderPass->allocInst(); + *ri = *coreRI; + + ri->matInst = matInst; + ri->defaultKey = matInst->getStateHint(); + ri->primBuffIndex = i; + + // Translucent materials need the translucent type. + if ( matInst->getMaterial()->isTranslucent() ) + { + ri->type = RenderPassManager::RIT_Translucent; + ri->translucentSort = true; + } + + renderPass->addInst( ri ); + } +} + +const Point3F * TSMesh::getNormals( S32 firstVert ) +{ + if ( getFlags( UseEncodedNormals ) ) + { + gNormalStore.setSize( vertsPerFrame ); + for ( S32 i = 0; i < encodedNorms.size(); i++ ) + gNormalStore[i] = decodeNormal( encodedNorms[ i + firstVert ] ); + + return gNormalStore.address(); + } + + return &norms[firstVert]; +} + +//----------------------------------------------------- +// TSMesh collision methods +//----------------------------------------------------- + +bool TSMesh::buildPolyList( S32 frame, AbstractPolyList *polyList, U32 &surfaceKey, TSMaterialList *materials ) +{ + S32 firstVert = vertsPerFrame * frame, i, base = 0; + + // add the verts... + if ( vertsPerFrame ) + { + if ( mVertexData.isReady() ) + { + OptimizedPolyList* opList = dynamic_cast(polyList); + if ( opList ) + { + base = opList->mVertexList.size(); + for ( i = 0; i < vertsPerFrame; i++ ) + { + // Don't use vertex() method as we want to retain the original indices + OptimizedPolyList::VertIndex vert; + vert.vertIdx = opList->insertPoint( mVertexData[ i + firstVert ].vert() ); + vert.normalIdx = opList->insertNormal( mVertexData[ i + firstVert ].normal() ); + vert.uv0Idx = opList->insertUV0( mVertexData[ i + firstVert ].tvert() ); + if ( mHasTVert2 ) + vert.uv1Idx = opList->insertUV1( mVertexData[ i + firstVert ].tvert2() ); + + opList->mVertexList.push_back( vert ); + } + } + else + { + base = polyList->addPoint( mVertexData[firstVert].vert() ); + for ( i = 1; i < vertsPerFrame; i++ ) + polyList->addPoint( mVertexData[ i + firstVert ].vert() ); + } + } + else + { + OptimizedPolyList* opList = dynamic_cast(polyList); + if ( opList ) + { + base = opList->mVertexList.size(); + for ( i = 0; i < vertsPerFrame; i++ ) + { + // Don't use vertex() method as we want to retain the original indices + OptimizedPolyList::VertIndex vert; + vert.vertIdx = opList->insertPoint( verts[ i + firstVert ] ); + vert.normalIdx = opList->insertNormal( norms[ i + firstVert ] ); + vert.uv0Idx = opList->insertUV0( tverts[ i + firstVert ] ); + if ( mHasTVert2 ) + vert.uv1Idx = opList->insertUV1( tverts2[ i + firstVert ] ); + + opList->mVertexList.push_back( vert ); + } + } + else + { + base = polyList->addPoint( verts[firstVert] ); + for ( i = 1; i < vertsPerFrame; i++ ) + polyList->addPoint( verts[ i + firstVert ] ); + } + } + } + + // add the polys... + for ( i = 0; i < primitives.size(); i++ ) + { + TSDrawPrimitive & draw = primitives[i]; + U32 start = draw.start; + + AssertFatal( draw.matIndex & TSDrawPrimitive::Indexed,"TSMesh::buildPolyList (1)" ); + + U32 matIndex = draw.matIndex & TSDrawPrimitive::MaterialMask; + BaseMatInstance* material = ( materials ? materials->getMaterialInst( matIndex ) : 0 ); + + // gonna depend on what kind of primitive it is... + if ( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles ) + { + for ( S32 j = 0; j < draw.numElements; ) + { + U32 idx0 = base + indices[start + j + 0]; + U32 idx1 = base + indices[start + j + 1]; + U32 idx2 = base + indices[start + j + 2]; + polyList->begin(material,surfaceKey++); + polyList->vertex( idx0 ); + polyList->vertex( idx1 ); + polyList->vertex( idx2 ); + polyList->plane( idx0, idx1, idx2 ); + polyList->end(); + j += 3; + } + } + else + { + AssertFatal( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Strip,"TSMesh::buildPolyList (2)" ); + + U32 idx0 = base + indices[start + 0]; + U32 idx1; + U32 idx2 = base + indices[start + 1]; + U32 * nextIdx = &idx1; + for ( S32 j = 2; j < draw.numElements; j++ ) + { + *nextIdx = idx2; + // nextIdx = (j%2)==0 ? &idx0 : &idx1; + nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1); + idx2 = base + indices[start + j]; + if ( idx0 == idx1 || idx0 == idx2 || idx1 == idx2 ) + continue; + + polyList->begin( material, surfaceKey++ ); + polyList->vertex( idx0 ); + polyList->vertex( idx1 ); + polyList->vertex( idx2 ); + polyList->plane( idx0, idx1, idx2 ); + polyList->end(); + } + } + } + return true; +} + +bool TSMesh::getFeatures( S32 frame, const MatrixF& mat, const VectorF&, ConvexFeature* cf, U32& ) +{ + S32 firstVert = vertsPerFrame * frame; + S32 i; + S32 base = cf->mVertexList.size(); + + for ( i = 0; i < vertsPerFrame; i++ ) + { + cf->mVertexList.increment(); + mat.mulP( mVertexData[firstVert + i].vert(), &cf->mVertexList.last() ); + } + + // add the polys... + for ( i = 0; i < primitives.size(); i++ ) + { + TSDrawPrimitive & draw = primitives[i]; + U32 start = draw.start; + + AssertFatal( draw.matIndex & TSDrawPrimitive::Indexed,"TSMesh::buildPolyList (1)" ); + + // gonna depend on what kind of primitive it is... + if ( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles) + { + for ( S32 j = 0; j < draw.numElements; j += 3 ) + { + PlaneF plane( cf->mVertexList[base + indices[start + j + 0]], + cf->mVertexList[base + indices[start + j + 1]], + cf->mVertexList[base + indices[start + j + 2]]); + + cf->mFaceList.increment(); + cf->mFaceList.last().normal = plane; + + cf->mFaceList.last().vertex[0] = base + indices[start + j + 0]; + cf->mFaceList.last().vertex[1] = base + indices[start + j + 1]; + cf->mFaceList.last().vertex[2] = base + indices[start + j + 2]; + + for ( U32 l = 0; l < 3; l++ ) + { + U32 newEdge0, newEdge1; + U32 zero = base + indices[start + j + l]; + U32 one = base + indices[start + j + ((l+1)%3)]; + newEdge0 = getMin( zero, one ); + newEdge1 = getMax( zero, one ); + bool found = false; + for ( S32 k = 0; k < cf->mEdgeList.size(); k++ ) + { + if ( cf->mEdgeList[k].vertex[0] == newEdge0 && + cf->mEdgeList[k].vertex[1] == newEdge1) + { + found = true; + break; + } + } + + if ( !found ) + { + cf->mEdgeList.increment(); + cf->mEdgeList.last().vertex[0] = newEdge0; + cf->mEdgeList.last().vertex[1] = newEdge1; + } + } + } + } + else + { + AssertFatal( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Strip,"TSMesh::buildPolyList (2)" ); + + U32 idx0 = base + indices[start + 0]; + U32 idx1; + U32 idx2 = base + indices[start + 1]; + U32 * nextIdx = &idx1; + for ( S32 j = 2; j < draw.numElements; j++ ) + { + *nextIdx = idx2; + nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1); + idx2 = base + indices[start + j]; + if ( idx0 == idx1 || idx0 == idx2 || idx1 == idx2 ) + continue; + + PlaneF plane( cf->mVertexList[idx0], + cf->mVertexList[idx1], + cf->mVertexList[idx2] ); + + cf->mFaceList.increment(); + cf->mFaceList.last().normal = plane; + + cf->mFaceList.last().vertex[0] = idx0; + cf->mFaceList.last().vertex[1] = idx1; + cf->mFaceList.last().vertex[2] = idx2; + + U32 newEdge0, newEdge1; + newEdge0 = getMin( idx0, idx1 ); + newEdge1 = getMax( idx0, idx1 ); + bool found = false; + S32 k; + for ( k = 0; k < cf->mEdgeList.size(); k++ ) + { + if ( cf->mEdgeList[k].vertex[0] == newEdge0 && + cf->mEdgeList[k].vertex[1] == newEdge1) + { + found = true; + break; + } + } + + if ( !found ) + { + cf->mEdgeList.increment(); + cf->mEdgeList.last().vertex[0] = newEdge0; + cf->mEdgeList.last().vertex[1] = newEdge1; + } + + newEdge0 = getMin( idx1, idx2 ); + newEdge1 = getMax( idx1, idx2 ); + found = false; + for ( k = 0; k < cf->mEdgeList.size(); k++ ) + { + if ( cf->mEdgeList[k].vertex[0] == newEdge0 && + cf->mEdgeList[k].vertex[1] == newEdge1 ) + { + found = true; + break; + } + } + + if ( !found ) + { + cf->mEdgeList.increment(); + cf->mEdgeList.last().vertex[0] = newEdge0; + cf->mEdgeList.last().vertex[1] = newEdge1; + } + + newEdge0 = getMin(idx0, idx2); + newEdge1 = getMax(idx0, idx2); + found = false; + for ( k = 0; k < cf->mEdgeList.size(); k++ ) + { + if ( cf->mEdgeList[k].vertex[0] == newEdge0 && + cf->mEdgeList[k].vertex[1] == newEdge1 ) + { + found = true; + break; + } + } + + if ( !found ) + { + cf->mEdgeList.increment(); + cf->mEdgeList.last().vertex[0] = newEdge0; + cf->mEdgeList.last().vertex[1] = newEdge1; + } + } + } + } + + return false; +} + + +void TSMesh::support( S32 frame, const Point3F &v, F32 *currMaxDP, Point3F *currSupport ) +{ + if ( vertsPerFrame == 0 ) + return; + + U32 waterMark = FrameAllocator::getWaterMark(); + F32* pDots = (F32*)FrameAllocator::alloc( sizeof(F32) * vertsPerFrame ); + + S32 firstVert = vertsPerFrame * frame; + m_point3F_bulk_dot( &v.x, + &mVertexData[firstVert].vert().x, + vertsPerFrame, + mVertexData.vertSize(), + pDots ); + + F32 localdp = *currMaxDP; + S32 index = -1; + + for ( S32 i = 0; i < vertsPerFrame; i++ ) + { + if ( pDots[i] > localdp ) + { + localdp = pDots[i]; + index = i; + } + } + + FrameAllocator::setWaterMark(waterMark); + + if ( index != -1 ) + { + *currMaxDP = localdp; + *currSupport = mVertexData[index + firstVert].vert(); + } +} + +bool TSMesh::castRay( S32 frame, const Point3F & start, const Point3F & end, RayInfo * rayInfo, TSMaterialList* materials ) +{ + if ( planeNormals.empty() ) + buildConvexHull(); // if haven't done it yet... + + // Keep track of startTime and endTime. They start out at just under 0 and just over 1, respectively. + // As we check against each plane, prune start and end times back to represent current intersection of + // line with all the planes (or rather with all the half-spaces defined by the planes). + // But, instead of explicitly keeping track of startTime and endTime, keep track as numerator and denominator + // so that we can avoid as many divisions as possible. + + // F32 startTime = -0.01f; + F32 startNum = -0.01f; + F32 startDen = 1.00f; + // F32 endTime = 1.01f; + F32 endNum = 1.01f; + F32 endDen = 1.00f; + + S32 curPlane = 0; + U32 curMaterial = 0; + bool found = false; + + // the following block of code is an optimization... + // it isn't necessary if the longer version of the main loop is used + bool tmpFound; + S32 tmpPlane; + F32 sgn = -1.0f; + F32 * pnum = &startNum; + F32 * pden = &startDen; + S32 * pplane = &curPlane; + bool * pfound = &found; + + S32 startPlane = frame * planesPerFrame; + for ( S32 i = startPlane; i < startPlane + planesPerFrame; i++ ) + { + // if start & end outside, no collision + // if start & end inside, continue + // if start outside, end inside, or visa versa, find intersection of line with plane + // then update intersection of line with hull (using startTime and endTime) + F32 dot1 = mDot( planeNormals[i], start ) - planeConstants[i]; + F32 dot2 = mDot( planeNormals[i], end) - planeConstants[i]; + if ( dot1 * dot2 > 0.0f ) + { + // same side of the plane...which side -- dot==0 considered inside + if ( dot1 > 0.0f ) + return false; // start and end outside of this plane, no collision + + // start and end inside plane, continue + continue; + } + + //AssertFatal( dot1 / ( dot1 - dot2 ) >= 0.0f && dot1 / ( dot1 - dot2 ) <= 1.0f,"TSMesh::castRay (1)" ); + + // find intersection (time) with this plane... + // F32 time = dot1 / (dot1-dot2); + F32 num = mFabs( dot1 ); + F32 den = mFabs( dot1 - dot2 ); + + // the following block of code is an optimized version... + // this can be commented out and the following block of code used instead + // if debugging a problem in this code, that should probably be done + // if you want to see how this works, look at the following block of code, + // not this one... + // Note that this does not get optimized appropriately...it is included this way + // as an idea for future optimization. + if ( sgn * dot1 >= 0 ) + { + sgn *= -1.0f; + pnum = (F32*) ((dsize_t)pnum ^ (dsize_t)&endNum ^ (dsize_t)&startNum); + pden = (F32*) ((dsize_t)pden ^ (dsize_t)&endDen ^ (dsize_t)&startDen); + pplane = (S32*) ((dsize_t)pplane ^ (dsize_t)&tmpPlane ^ (dsize_t)&curPlane); + pfound = (bool*) ((dsize_t)pfound ^ (dsize_t)&tmpFound ^ (dsize_t)&found); + } + + bool noCollision = num * endDen * sgn < endNum * den * sgn && num * startDen * sgn < startNum * den * sgn; + if (num * *pden * sgn < *pnum * den * sgn && !noCollision) + { + *pnum = num; + *pden = den; + *pplane = i; + *pfound = true; + } + else if ( noCollision ) + return false; + +// if (dot1<=0.0f) +// { +// // start is inside plane, end is outside...chop off end +// if (num*endDenstartNum*den) // if (time>startTime) +// { +// if (num*endDen>endNum*den) //if (time>endTime) +// // no intersection of line and hull +// return false; +// // startTime = time; +// startNum = num; +// startDen = den; +// curPlane = i; +// curMaterial = planeMaterials[i-startPlane]; +// found = true; +// } +// // else, no need to do anything, just continue (we've been more inside than this) +// } + } + + // setup rayInfo + if ( found && rayInfo ) + { + curMaterial = planeMaterials[ curPlane - startPlane ]; + + rayInfo->t = (F32)startNum/(F32)startDen; // finally divide... + rayInfo->normal = planeNormals[curPlane]; + + if (materials && materials->size() > 0) + rayInfo->material = materials->getMaterialInst( curMaterial ); + else + rayInfo->material = NULL; + + rayInfo->setContactPoint( start, end ); + + return true; + } + else if ( found ) + return true; + + // only way to get here is if start is inside hull... + // we could return null and just plug in garbage for the material and normal... + return false; +} + +bool TSMesh::castRayRendered( S32 frame, const Point3F & start, const Point3F & end, RayInfo * rayInfo, TSMaterialList* materials ) +{ + if( vertsPerFrame <= 0 ) + return false; + + if( mNumVerts == 0 ) + return false; + + S32 firstVert = vertsPerFrame * frame; + + bool found = false; + F32 best_t = F32_MAX; + U32 bestIdx0 = 0, bestIdx1 = 0, bestIdx2 = 0; + BaseMatInstance* bestMaterial = NULL; + Point3F dir = end - start; + + for ( S32 i = 0; i < primitives.size(); i++ ) + { + TSDrawPrimitive & draw = primitives[i]; + U32 drawStart = draw.start; + + AssertFatal( draw.matIndex & TSDrawPrimitive::Indexed,"TSMesh::castRayRendered (1)" ); + + U32 matIndex = draw.matIndex & TSDrawPrimitive::MaterialMask; + BaseMatInstance* material = ( materials ? materials->getMaterialInst( matIndex ) : 0 ); + + U32 idx0, idx1, idx2; + + // gonna depend on what kind of primitive it is... + if ( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles ) + { + for ( S32 j = 0; j < draw.numElements-2; j++) + { + idx0 = indices[drawStart + j + 0]; + idx1 = indices[drawStart + j + 1]; + idx2 = indices[drawStart + j + 2]; + + F32 cur_t = 0; + Point2F b; + + if(castRayTriangle(start, dir, mVertexData[firstVert + idx0].vert(), + mVertexData[firstVert + idx1].vert(), mVertexData[firstVert + idx2].vert(), cur_t, b)) + { + if(cur_t < best_t) + { + best_t = cur_t; + bestIdx0 = idx0; + bestIdx1 = idx1; + bestIdx2 = idx2; + bestMaterial = material; + found = true; + } + } + } + } + else + { + AssertFatal( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Strip,"TSMesh::castRayRendered (2)" ); + + idx0 = indices[drawStart + 0]; + idx2 = indices[drawStart + 1]; + U32 * nextIdx = &idx1; + for ( S32 j = 2; j < draw.numElements; j++ ) + { + *nextIdx = idx2; + // nextIdx = (j%2)==0 ? &idx0 : &idx1; + nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1); + idx2 = indices[drawStart + j]; + if ( idx0 == idx1 || idx0 == idx2 || idx1 == idx2 ) + continue; + + F32 cur_t = 0; + Point2F b; + + if(castRayTriangle(start, dir, mVertexData[firstVert + idx0].vert(), + mVertexData[firstVert + idx1].vert(), mVertexData[firstVert + idx2].vert(), cur_t, b)) + { + if(cur_t < best_t) + { + best_t = cur_t; + bestIdx0 = firstVert + idx0; + bestIdx1 = firstVert + idx1; + bestIdx2 = firstVert + idx2; + bestMaterial = material; + found = true; + } + } + } + } + } + + // setup rayInfo + if ( found && rayInfo ) + { + rayInfo->t = best_t; + + Point3F normal; + mCross(mVertexData[bestIdx2].vert()-mVertexData[bestIdx0].vert(),mVertexData[bestIdx1].vert()-mVertexData[bestIdx0].vert(),&normal); + if ( mDot( normal, normal ) < 0.001f ) + { + mCross( mVertexData[bestIdx0].vert() - mVertexData[bestIdx1].vert(), mVertexData[bestIdx2].vert() - mVertexData[bestIdx1].vert(), &normal ); + if ( mDot( normal, normal ) < 0.001f ) + { + mCross( mVertexData[bestIdx1].vert() - mVertexData[bestIdx2].vert(), mVertexData[bestIdx0].vert() - mVertexData[bestIdx2].vert(), &normal ); + } + } + normal.normalize(); + rayInfo->normal = normal; + + rayInfo->material = bestMaterial; + + rayInfo->setContactPoint( start, end ); + + return true; + } + else if ( found ) + return true; + + return false; +} + +bool TSMesh::addToHull( U32 idx0, U32 idx1, U32 idx2 ) +{ + Point3F normal; + mCross(mVertexData[idx2].vert()-mVertexData[idx0].vert(),mVertexData[idx1].vert()-mVertexData[idx0].vert(),&normal); + if ( mDot( normal, normal ) < 0.001f ) + { + mCross( mVertexData[idx0].vert() - mVertexData[idx1].vert(), mVertexData[idx2].vert() - mVertexData[idx1].vert(), &normal ); + if ( mDot( normal, normal ) < 0.001f ) + { + mCross( mVertexData[idx1].vert() - mVertexData[idx2].vert(), mVertexData[idx0].vert() - mVertexData[idx2].vert(), &normal ); + if ( mDot( normal, normal ) < 0.001f ) + return false; + } + } + normal.normalize(); + F32 k = mDot( normal, mVertexData[idx0].vert() ); + for ( S32 i = 0; i < planeNormals.size(); i++ ) + { + if ( mDot( planeNormals[i], normal ) > 0.99f && mFabs( k-planeConstants[i] ) < 0.01f ) + return false; // this is a repeat... + } + // new plane, add it to the list... + planeNormals.push_back( normal ); + planeConstants.push_back( k ); + return true; +} + +bool TSMesh::buildConvexHull() +{ + // already done, return without error + if ( planeNormals.size() ) + return true; + + bool error = false; + + // should probably only have 1 frame, but just in case... + planesPerFrame = 0; + S32 frame, i, j; + for ( frame = 0; frame < numFrames; frame++ ) + { + S32 firstVert = vertsPerFrame * frame; + S32 firstPlane = planeNormals.size(); + for ( i = 0; i < primitives.size(); i++ ) + { + TSDrawPrimitive & draw = primitives[i]; + U32 start = draw.start; + + AssertFatal( draw.matIndex & TSDrawPrimitive::Indexed,"TSMesh::buildConvexHull (1)" ); + + // gonna depend on what kind of primitive it is... + if ( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles ) + { + for ( j = 0; j < draw.numElements; j += 3 ) + if ( addToHull( indices[start + j + 0] + firstVert, + indices[start + j + 1] + firstVert, + indices[start + j + 2] + firstVert ) && frame == 0 ) + planeMaterials.push_back( draw.matIndex & TSDrawPrimitive::MaterialMask ); + } + else + { + AssertFatal( (draw.matIndex&TSDrawPrimitive::Strip) == TSDrawPrimitive::Strip,"TSMesh::buildConvexHull (2)" ); + + U32 idx0 = indices[start + 0] + firstVert; + U32 idx1; + U32 idx2 = indices[start + 1] + firstVert; + U32 * nextIdx = &idx1; + for ( j = 2; j < draw.numElements; j++ ) + { + *nextIdx = idx2; +// nextIdx = (j%2)==0 ? &idx0 : &idx1; + nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1 ); + idx2 = indices[start + j] + firstVert; + if ( addToHull( idx0, idx1, idx2 ) && frame == 0 ) + planeMaterials.push_back( draw.matIndex & TSDrawPrimitive::MaterialMask ); + } + } + } + // make sure all the verts on this frame are inside all the planes + for ( i = 0; i < vertsPerFrame; i++ ) + for ( j = firstPlane; j < planeNormals.size(); j++ ) + if ( mDot( mVertexData[firstVert + i].vert(), planeNormals[j] ) - planeConstants[j] < 0.01 ) // .01 == a little slack + error = true; + + if ( frame == 0 ) + planesPerFrame = planeNormals.size(); + + if ( (frame + 1) * planesPerFrame != planeNormals.size() ) + { + // eek, not all frames have same number of planes... + while ( (frame + 1) * planesPerFrame > planeNormals.size() ) + { + // we're short, duplicate last plane till we match + U32 sz = planeNormals.size(); + planeNormals.increment(); + planeNormals.last() = planeNormals[sz-1]; + planeConstants.increment(); + planeConstants.last() = planeConstants[sz-1]; + } + while ( (frame + 1) * planesPerFrame < planeNormals.size() ) + { + // harsh -- last frame has more than other frames + // duplicate last plane in each frame + for ( S32 k = frame - 1; k >= 0; k-- ) + { + planeNormals.insert( k * planesPerFrame + planesPerFrame ); + planeNormals[k * planesPerFrame + planesPerFrame] = planeNormals[k * planesPerFrame + planesPerFrame - 1]; + planeConstants.insert( k * planesPerFrame + planesPerFrame ); + planeConstants[k * planesPerFrame + planesPerFrame] = planeConstants[k * planesPerFrame + planesPerFrame - 1]; + if ( k == 0 ) + { + planeMaterials.increment(); + planeMaterials.last() = planeMaterials[planeMaterials.size() - 2]; + } + } + planesPerFrame++; + } + } + AssertFatal( (frame + 1) * planesPerFrame == planeNormals.size(),"TSMesh::buildConvexHull (3)" ); + } + return !error; +} + +//----------------------------------------------------- +// TSMesh bounds methods +//----------------------------------------------------- + +void TSMesh::computeBounds() +{ + MatrixF mat(true); + computeBounds( mat, mBounds, -1, &mCenter, &mRadius ); +} + +void TSMesh::computeBounds( const MatrixF &transform, Box3F &bounds, S32 frame, Point3F *center, F32 *radius ) +{ + const Point3F *baseVert = NULL; + S32 stride = 0; + S32 numVerts = 0; + + if(mVertexData.isReady()) + { + baseVert = &mVertexData[0].vert(); + stride = mVertexData.vertSize(); + + if ( frame < 0 ) + numVerts = mNumVerts; + else + { + baseVert = &mVertexData[frame * vertsPerFrame].vert(); + numVerts = vertsPerFrame; + } + } + else + { + baseVert = verts.address(); + stride = sizeof(Point3F); + + if ( frame < 0 ) + numVerts = verts.size(); + else + { + baseVert += frame * vertsPerFrame; + numVerts = vertsPerFrame; + } + } + computeBounds( baseVert, numVerts, stride, transform, bounds, center, radius ); +} + +void TSMesh::computeBounds( const Point3F *v, S32 numVerts, S32 stride, const MatrixF &transform, Box3F &bounds, Point3F *center, F32 *radius ) +{ + const U8 *_vb = reinterpret_cast(v); + + if ( !numVerts ) + { + bounds.minExtents = Point3F::Zero; + bounds.maxExtents = Point3F::Zero; + if ( center ) + *center = Point3F::Zero; + if ( radius ) + *radius = 0; + return; + } + + S32 i; + Point3F p; + transform.mulP( *v, &bounds.minExtents ); + bounds.maxExtents = bounds.minExtents; + for ( i = 0; i < numVerts; i++ ) + { + const Point3F &curVert = *reinterpret_cast(_vb + i * stride); + transform.mulP( curVert, &p ); + bounds.maxExtents.setMax( p ); + bounds.minExtents.setMin( p ); + } + Point3F c; + if ( !center ) + center = &c; + center->x = 0.5f * (bounds.minExtents.x + bounds.maxExtents.x); + center->y = 0.5f * (bounds.minExtents.y + bounds.maxExtents.y); + center->z = 0.5f * (bounds.minExtents.z + bounds.maxExtents.z); + if ( radius ) + { + *radius = 0.0f; + for ( i = 0; i < numVerts; i++ ) + { + const Point3F &curVert = *reinterpret_cast(_vb + i * stride); + transform.mulP( curVert, &p ); + p -= *center; + *radius = getMax( *radius, mDot( p, p ) ); + } + *radius = mSqrt( *radius ); + } +} + +//----------------------------------------------------- + +S32 TSMesh::getNumPolys() const +{ + S32 count = 0; + for ( S32 i = 0; i < primitives.size(); i++ ) + { + switch (primitives[i].matIndex & TSDrawPrimitive::TypeMask) + { + case TSDrawPrimitive::Triangles: + count += primitives[i].numElements / 3; + break; + + case TSDrawPrimitive::Fan: + count += primitives[i].numElements - 2; + break; + + case TSDrawPrimitive::Strip: + // Don't count degenerate triangles + for ( S32 j = primitives[i].start; + j < primitives[i].start+primitives[i].numElements-2; + j++ ) + { + if ((indices[j] != indices[j+1]) && + (indices[j] != indices[j+2]) && + (indices[j+1] != indices[j+2])) + count++; + } + break; + } + } + return count; +} + +//----------------------------------------------------- + +TSMesh::TSMesh() : meshType( StandardMeshType ) +{ + VECTOR_SET_ASSOCIATION( planeNormals ); + VECTOR_SET_ASSOCIATION( planeConstants ); + VECTOR_SET_ASSOCIATION( planeMaterials ); + parentMesh = -1; + + mOptTree = NULL; + mOpMeshInterface = NULL; + mOpTris = NULL; + mOpPoints = NULL; + + mDynamic = false; + mVisibility = 1.0f; + mHasTVert2 = false; + mHasColor = false; + + mNumVerts = 0; +} + +//----------------------------------------------------- +// TSMesh destructor +//----------------------------------------------------- + +TSMesh::~TSMesh() +{ + SAFE_DELETE( mOptTree ); + SAFE_DELETE( mOpMeshInterface ); + SAFE_DELETE_ARRAY( mOpTris ); + SAFE_DELETE_ARRAY( mOpPoints ); + + mNumVerts = 0; +} + +//----------------------------------------------------- +// TSSkinMesh methods +//----------------------------------------------------- + +void TSSkinMesh::updateSkin( const Vector &transforms, TSVertexBufferHandle &instanceVB, GFXPrimitiveBufferHandle &instancePB ) +{ + PROFILE_SCOPE( TSSkinMesh_UpdateSkin ); + + AssertFatal(batchDataInitialized, "Batch data not initialized. Call createBatchData() before any skin update is called."); + + // set arrays +#if defined(TORQUE_MAX_LIB) + verts.setSize(batchData.initialVerts.size()); + norms.setSize(batchData.initialNorms.size()); +#else + if ( !batchDataInitialized && encodedNorms.size() ) + { + // we co-opt responsibility for updating encoded normals from mesh + gNormalStore.setSize( vertsPerFrame ); + for ( S32 i = 0; i < vertsPerFrame; i++ ) + gNormalStore[i] = decodeNormal( encodedNorms[i] ); + + batchData.initialNorms.set( gNormalStore.address(), vertsPerFrame ); + } +#endif + + static Vector sBoneTransforms; + sBoneTransforms.setSize( batchData.nodeIndex.size() ); + + // set up bone transforms + PROFILE_START(TSSkinMesh_UpdateTransforms); + for( int i=0; i::const_iterator itr = batchData.vertexBatchOperations.begin(); + itr != batchData.vertexBatchOperations.end(); itr++ ) + { + const BatchData::BatchedVertex &curVert = *itr; + + skinnedVert.zero(); + skinnedNorm.zero(); + + for( int tOp = 0; tOp < curVert.transformCount; tOp++ ) + { + const BatchData::TransformOp &transformOp = curVert.transform[tOp]; + + const MatrixF& deltaTransform = matrices[transformOp.transformIndex]; + + deltaTransform.mulP( inVerts[curVert.vertexIndex], &srcVtx ); + skinnedVert += ( srcVtx * transformOp.weight ); + + deltaTransform.mulV( inNorms[curVert.vertexIndex], &srcNrm ); + skinnedNorm += srcNrm * transformOp.weight; + } + + // Assign results + __TSMeshVertexBase &dest = mVertexData[curVert.vertexIndex]; + dest.vert(skinnedVert); + dest.normal(skinnedNorm); + } + } + else // Batch by transform + { + U8 *outPtr = reinterpret_cast(mVertexData.address()); + dsize_t outStride = mVertexData.vertSize(); + +#if defined(USE_MEM_VERTEX_BUFFERS) + // Initialize it if NULL. + // Skinning includes readbacks from memory (argh) so don't allocate with PAGE_WRITECOMBINE + if( instanceVB.isNull() ) + instanceVB.set( GFX, outStride, mVertexFormat, mNumVerts, GFXBufferTypeDynamic ); + + // Grow if needed + if( instanceVB.getPointer()->mNumVerts < mNumVerts ) + instanceVB.resize( mNumVerts ); + + // Lock, and skin directly into the final memory destination + outPtr = (U8 *)instanceVB.lock(); +#endif + // Set position/normal to zero so we can accumulate + zero_vert_normal_bulk(mNumVerts, outPtr, outStride); + + // Iterate over transforms, and perform batch transform x skin_vert + for(Vector::const_iterator itr = batchData.transformKeys.begin(); + itr != batchData.transformKeys.end(); itr++) + { + const S32 boneXfmIdx = *itr; + const BatchData::BatchedTransform &curTransform = *batchData.transformBatchOperations.retreive(boneXfmIdx); + const MatrixF &curBoneMat = matrices[boneXfmIdx]; + const S32 numVerts = curTransform.numElements; + + // Bulk transform points/normals by this transform + m_matF_x_BatchedVertWeightList(curBoneMat, numVerts, curTransform.alignedMem, + outPtr, outStride); + } +#if defined(USE_MEM_VERTEX_BUFFERS) + instanceVB.unlock(); +#endif + } +} + +S32 QSORT_CALLBACK _sort_BatchedVertWeight( const void *a, const void *b ) +{ + // Sort by vertex index + const TSSkinMesh::BatchData::BatchedVertWeight &_a = *reinterpret_cast(a); + const TSSkinMesh::BatchData::BatchedVertWeight &_b = *reinterpret_cast(b); + return ( _a.vidx - _b.vidx ); +} + +// Batch by vertex is useful to emulate the old skinning, or to build batch data +// sutable for GPU skinning. +//#define _BATCH_BY_VERTEX + +void TSSkinMesh::createBatchData() +{ + if(batchDataInitialized) + return; + + batchDataInitialized = true; + S32 * curVtx = vertexIndex.begin(); + S32 * curBone = boneIndex.begin(); + F32 * curWeight = weight.begin(); + const S32 * endVtx = vertexIndex.end(); + + // Temp vector to build batch operations + Vector batchOperations; + + // Build the batch operations + while( curVtx != endVtx ) + { + const S32 vidx = *curVtx; + ++curVtx; + + const S32 midx = *curBone; + ++curBone; + + const F32 w = *curWeight; + ++curWeight; + + if( !batchOperations.empty() && + batchOperations.last().vertexIndex == vidx ) + { + AssertFatal( batchOperations.last().transformCount > 0, "Not sure how this happened!" ); + + const int opIdx = batchOperations.last().transformCount++; + AssertISV( BatchData::maxBonePerVert > opIdx, "Too many bones affecting the same vertex, increase the size of 'TSMesh::BatchData::maxBonePerVert'" ); + + batchOperations.last().transform[opIdx].transformIndex = midx; + batchOperations.last().transform[opIdx].weight = w; + } + else + { + batchOperations.increment(); + batchOperations.last().vertexIndex = vidx; + batchOperations.last().transformCount = 1; + + batchOperations.last().transform[0].transformIndex = midx; + batchOperations.last().transform[0].weight = w; + } + //Con::printf( "[%d] transform idx %d, weight %1.5f", vidx, midx, w ); + } + //Con::printf("End skin update"); + +#ifdef _BATCH_BY_VERTEX + // Copy data to member, and be done + batchData.vertexBatchOperations.set(batchOperations.address(), batchOperations.size()); + + // Convert to batch-by-transform, which is better for CPU skinning, + // where-as GPU skinning would data for batch-by-vertex operation +#else + // Iterate the batch-by-vertex, and populate the batch-by-transform structs + for( Vector::const_iterator itr = batchOperations.begin(); + itr != batchOperations.end(); itr++ ) + { + const BatchData::BatchedVertex &curTransform = *itr; + for( int i = 0; i < curTransform.transformCount; i++ ) + { + const BatchData::TransformOp &transformOp = curTransform.transform[i]; + + // Find the proper batched transform, and add this vertex/weight to the + // list of verts affected by the transform + BatchData::BatchedTransform *bt = batchData.transformBatchOperations.retreive(transformOp.transformIndex); + if(!bt) + { + bt = new BatchData::BatchedTransform; + batchData.transformBatchOperations.insert(bt, transformOp.transformIndex); + bt->_tmpVec = new Vector; + batchData.transformKeys.push_back(transformOp.transformIndex); + } + + bt->_tmpVec->increment(); + bt->_tmpVec->last().vert = batchData.initialVerts[curTransform.vertexIndex]; + bt->_tmpVec->last().normal = batchData.initialNorms[curTransform.vertexIndex]; + bt->_tmpVec->last().weight = transformOp.weight; + bt->_tmpVec->last().vidx = curTransform.vertexIndex; + } + } + + // Now iterate the resulting operations and convert the vectors to aligned + // memory locations + const int numBatchOps = batchData.transformKeys.size(); + for(int i = 0; i < numBatchOps; i++) + { + BatchData::BatchedTransform &curTransform = *batchData.transformBatchOperations.retreive(batchData.transformKeys[i]); + const S32 numVerts = curTransform._tmpVec->size(); + + // Allocate a chunk of aligned memory and copy in values + curTransform.numElements = numVerts; + curTransform.alignedMem = reinterpret_cast(dMalloc_aligned(sizeof(BatchData::BatchedVertWeight) * numVerts, 16)); + AssertFatal(curTransform.alignedMem, "Aligned malloc failed! Debug!"); + constructArrayInPlace(curTransform.alignedMem, numVerts); + dMemcpy(curTransform.alignedMem, curTransform._tmpVec->address(), numVerts * sizeof(BatchData::BatchedVertWeight)); + + // Now free the vector memory + delete curTransform._tmpVec; + curTransform._tmpVec = NULL; + } + + // Now sort the batch data so that the skin function writes close to linear output + for(int i = 0; i < numBatchOps; i++) + { + BatchData::BatchedTransform &curTransform = *batchData.transformBatchOperations.retreive(batchData.transformKeys[i]); + dQsort(curTransform.alignedMem, curTransform.numElements, sizeof(BatchData::BatchedVertWeight), _sort_BatchedVertWeight); + } +#endif +} + +void TSSkinMesh::render( TSVertexBufferHandle &instanceVB, GFXPrimitiveBufferHandle &instancePB ) +{ + innerRender( instanceVB, instancePB ); +} + +void TSSkinMesh::render( TSMaterialList *materials, + const TSRenderState &rdata, + bool isSkinDirty, + const Vector &transforms, + TSVertexBufferHandle &vertexBuffer, + GFXPrimitiveBufferHandle &primitiveBuffer ) +{ + PROFILE_SCOPE(TSSkinMesh_render); + + if( mNumVerts == 0 ) + return; + + // Initialize the vertex data if it needs it + if(!mVertexData.isReady() ) + _convertToAlignedMeshData(mVertexData, batchData.initialVerts, batchData.initialNorms); + AssertFatal(mVertexData.size() == mNumVerts, "Vert # mismatch"); + + // Initialize the skin batch if that isn't ready + if(!batchDataInitialized) + createBatchData(); + + const bool vertsChanged = vertexBuffer.isNull() || vertexBuffer->mNumVerts != mNumVerts; + const bool primsChanged = primitiveBuffer.isNull() || primitiveBuffer->mIndexCount != indices.size(); + + if ( primsChanged || vertsChanged || isSkinDirty ) + { + // Perform skinning + updateSkin( transforms, vertexBuffer, primitiveBuffer ); + + // Update GFX vertex buffer + _createVBIB( vertexBuffer, primitiveBuffer ); + } + + // render... + innerRender( materials, rdata, vertexBuffer, primitiveBuffer ); +} + +bool TSSkinMesh::buildPolyList( S32 frame, AbstractPolyList *polyList, U32 &surfaceKey, TSMaterialList *materials ) +{ + // UpdateSkin() here may not be needed... + // we don't capture skinned + // verts in the polylist. + + // update verts and normals... + //if( !smGlowPass && !smRefractPass ) + // updateSkin(); + + // render... + //Parent::buildPolyList( frame,polyList,surfaceKey, materials ); + return false; +} + +bool TSSkinMesh::castRay( S32 frame, const Point3F &start, const Point3F &end, RayInfo *rayInfo, TSMaterialList *materials ) +{ + TORQUE_UNUSED(frame); + TORQUE_UNUSED(start); + TORQUE_UNUSED(end); + TORQUE_UNUSED(rayInfo); + TORQUE_UNUSED(materials); + + return false; +} + +bool TSSkinMesh::buildConvexHull() +{ + return false; // no error, but we don't do anything either... +} + +void TSSkinMesh::computeBounds( const MatrixF &transform, Box3F &bounds, S32 frame, Point3F *center, F32 *radius ) +{ + TORQUE_UNUSED(frame); + + if (frame < 0) + { + // Use unskinned verts + TSMesh::computeBounds( batchData.initialVerts.address(), batchData.initialVerts.size(), sizeof(Point3F), transform, bounds, center, radius ); + } + else + { + Point3F *vertStart = reinterpret_cast(mVertexData.address()); + TSMesh::computeBounds( vertStart, mVertexData.size(), mVertexData.vertSize(), transform, bounds, center, radius ); + } +} + +//----------------------------------------------------- +// encoded normals +//----------------------------------------------------- + +const Point3F TSMesh::smU8ToNormalTable[] = +{ + Point3F( 0.565061f, -0.270644f, -0.779396f ), + Point3F( -0.309804f, -0.731114f, 0.607860f ), + Point3F( -0.867412f, 0.472957f, 0.154619f ), + Point3F( -0.757488f, 0.498188f, -0.421925f ), + Point3F( 0.306834f, -0.915340f, 0.260778f ), + Point3F( 0.098754f, 0.639153f, -0.762713f ), + Point3F( 0.713706f, -0.558862f, -0.422252f ), + Point3F( -0.890431f, -0.407603f, -0.202466f ), + Point3F( 0.848050f, -0.487612f, -0.207475f ), + Point3F( -0.232226f, 0.776855f, 0.585293f ), + Point3F( -0.940195f, 0.304490f, -0.152706f ), + Point3F( 0.602019f, -0.491878f, -0.628991f ), + Point3F( -0.096835f, -0.494354f, -0.863850f ), + Point3F( 0.026630f, -0.323659f, -0.945799f ), + Point3F( 0.019208f, 0.909386f, 0.415510f ), + Point3F( 0.854440f, 0.491730f, 0.167731f ), + Point3F( -0.418835f, 0.866521f, -0.271512f ), + Point3F( 0.465024f, 0.409667f, 0.784809f ), + Point3F( -0.674391f, -0.691087f, -0.259992f ), + Point3F( 0.303858f, -0.869270f, -0.389922f ), + Point3F( 0.991333f, 0.090061f, -0.095640f ), + Point3F( -0.275924f, -0.369550f, 0.887298f ), + Point3F( 0.426545f, -0.465962f, 0.775202f ), + Point3F( -0.482741f, -0.873278f, -0.065920f ), + Point3F( 0.063616f, 0.932012f, -0.356800f ), + Point3F( 0.624786f, -0.061315f, 0.778385f ), + Point3F( -0.530300f, 0.416850f, 0.738253f ), + Point3F( 0.312144f, -0.757028f, -0.573999f ), + Point3F( 0.399288f, -0.587091f, -0.704197f ), + Point3F( -0.132698f, 0.482877f, 0.865576f ), + Point3F( 0.950966f, 0.306530f, 0.041268f ), + Point3F( -0.015923f, -0.144300f, 0.989406f ), + Point3F( -0.407522f, -0.854193f, 0.322925f ), + Point3F( -0.932398f, 0.220464f, 0.286408f ), + Point3F( 0.477509f, 0.876580f, 0.059936f ), + Point3F( 0.337133f, 0.932606f, -0.128796f ), + Point3F( -0.638117f, 0.199338f, 0.743687f ), + Point3F( -0.677454f, 0.445349f, 0.585423f ), + Point3F( -0.446715f, 0.889059f, -0.100099f ), + Point3F( -0.410024f, 0.909168f, 0.072759f ), + Point3F( 0.708462f, 0.702103f, -0.071641f ), + Point3F( -0.048801f, -0.903683f, -0.425411f ), + Point3F( -0.513681f, -0.646901f, 0.563606f ), + Point3F( -0.080022f, 0.000676f, -0.996793f ), + Point3F( 0.066966f, -0.991150f, -0.114615f ), + Point3F( -0.245220f, 0.639318f, -0.728793f ), + Point3F( 0.250978f, 0.855979f, 0.452006f ), + Point3F( -0.123547f, 0.982443f, -0.139791f ), + Point3F( -0.794825f, 0.030254f, -0.606084f ), + Point3F( -0.772905f, 0.547941f, 0.319967f ), + Point3F( 0.916347f, 0.369614f, -0.153928f ), + Point3F( -0.388203f, 0.105395f, 0.915527f ), + Point3F( -0.700468f, -0.709334f, 0.078677f ), + Point3F( -0.816193f, 0.390455f, 0.425880f ), + Point3F( -0.043007f, 0.769222f, -0.637533f ), + Point3F( 0.911444f, 0.113150f, 0.395560f ), + Point3F( 0.845801f, 0.156091f, -0.510153f ), + Point3F( 0.829801f, -0.029340f, 0.557287f ), + Point3F( 0.259529f, 0.416263f, 0.871418f ), + Point3F( 0.231128f, -0.845982f, 0.480515f ), + Point3F( -0.626203f, -0.646168f, 0.436277f ), + Point3F( -0.197047f, -0.065791f, 0.978184f ), + Point3F( -0.255692f, -0.637488f, -0.726794f ), + Point3F( 0.530662f, -0.844385f, -0.073567f ), + Point3F( -0.779887f, 0.617067f, -0.104899f ), + Point3F( 0.739908f, 0.113984f, 0.662982f ), + Point3F( -0.218801f, 0.930194f, -0.294729f ), + Point3F( -0.374231f, 0.818666f, 0.435589f ), + Point3F( -0.720250f, -0.028285f, 0.693137f ), + Point3F( 0.075389f, 0.415049f, 0.906670f ), + Point3F( -0.539724f, -0.106620f, 0.835063f ), + Point3F( -0.452612f, -0.754669f, -0.474991f ), + Point3F( 0.682822f, 0.581234f, -0.442629f ), + Point3F( 0.002435f, -0.618462f, -0.785811f ), + Point3F( -0.397631f, 0.110766f, -0.910835f ), + Point3F( 0.133935f, -0.985438f, 0.104754f ), + Point3F( 0.759098f, -0.608004f, 0.232595f ), + Point3F( -0.825239f, -0.256087f, 0.503388f ), + Point3F( 0.101693f, -0.565568f, 0.818408f ), + Point3F( 0.386377f, 0.793546f, -0.470104f ), + Point3F( -0.520516f, -0.840690f, 0.149346f ), + Point3F( -0.784549f, -0.479672f, 0.392935f ), + Point3F( -0.325322f, -0.927581f, -0.183735f ), + Point3F( -0.069294f, -0.428541f, 0.900861f ), + Point3F( 0.993354f, -0.115023f, -0.004288f ), + Point3F( -0.123896f, -0.700568f, 0.702747f ), + Point3F( -0.438031f, -0.120880f, -0.890795f ), + Point3F( 0.063314f, 0.813233f, 0.578484f ), + Point3F( 0.322045f, 0.889086f, -0.325289f ), + Point3F( -0.133521f, 0.875063f, -0.465228f ), + Point3F( 0.637155f, 0.564814f, 0.524422f ), + Point3F( 0.260092f, -0.669353f, 0.695930f ), + Point3F( 0.953195f, 0.040485f, -0.299634f ), + Point3F( -0.840665f, -0.076509f, 0.536124f ), + Point3F( -0.971350f, 0.202093f, 0.125047f ), + Point3F( -0.804307f, -0.396312f, -0.442749f ), + Point3F( -0.936746f, 0.069572f, 0.343027f ), + Point3F( 0.426545f, -0.465962f, 0.775202f ), + Point3F( 0.794542f, -0.227450f, 0.563000f ), + Point3F( -0.892172f, 0.091169f, -0.442399f ), + Point3F( -0.312654f, 0.541264f, 0.780564f ), + Point3F( 0.590603f, -0.735618f, -0.331743f ), + Point3F( -0.098040f, -0.986713f, 0.129558f ), + Point3F( 0.569646f, 0.283078f, -0.771603f ), + Point3F( 0.431051f, -0.407385f, -0.805129f ), + Point3F( -0.162087f, -0.938749f, -0.304104f ), + Point3F( 0.241533f, -0.359509f, 0.901341f ), + Point3F( -0.576191f, 0.614939f, 0.538380f ), + Point3F( -0.025110f, 0.085740f, 0.996001f ), + Point3F( -0.352693f, -0.198168f, 0.914515f ), + Point3F( -0.604577f, 0.700711f, 0.378802f ), + Point3F( 0.465024f, 0.409667f, 0.784809f ), + Point3F( -0.254684f, -0.030474f, -0.966544f ), + Point3F( -0.604789f, 0.791809f, 0.085259f ), + Point3F( -0.705147f, -0.399298f, 0.585943f ), + Point3F( 0.185691f, 0.017236f, -0.982457f ), + Point3F( 0.044588f, 0.973094f, 0.226052f ), + Point3F( -0.405463f, 0.642367f, 0.650357f ), + Point3F( -0.563959f, 0.599136f, -0.568319f ), + Point3F( 0.367162f, -0.072253f, -0.927347f ), + Point3F( 0.960429f, -0.213570f, -0.178783f ), + Point3F( -0.192629f, 0.906005f, 0.376893f ), + Point3F( -0.199718f, -0.359865f, -0.911378f ), + Point3F( 0.485072f, 0.121233f, -0.866030f ), + Point3F( 0.467163f, -0.874294f, 0.131792f ), + Point3F( -0.638953f, -0.716603f, 0.279677f ), + Point3F( -0.622710f, 0.047813f, -0.780990f ), + Point3F( 0.828724f, -0.054433f, -0.557004f ), + Point3F( 0.130241f, 0.991080f, 0.028245f ), + Point3F( 0.310995f, -0.950076f, -0.025242f ), + Point3F( 0.818118f, 0.275336f, 0.504850f ), + Point3F( 0.676328f, 0.387023f, 0.626733f ), + Point3F( -0.100433f, 0.495114f, -0.863004f ), + Point3F( -0.949609f, -0.240681f, -0.200786f ), + Point3F( -0.102610f, 0.261831f, -0.959644f ), + Point3F( -0.845732f, -0.493136f, 0.203850f ), + Point3F( 0.672617f, -0.738838f, 0.041290f ), + Point3F( 0.380465f, 0.875938f, 0.296613f ), + Point3F( -0.811223f, 0.262027f, -0.522742f ), + Point3F( -0.074423f, -0.775670f, -0.626736f ), + Point3F( -0.286499f, 0.755850f, -0.588735f ), + Point3F( 0.291182f, -0.276189f, -0.915933f ), + Point3F( -0.638117f, 0.199338f, 0.743687f ), + Point3F( 0.439922f, -0.864433f, -0.243359f ), + Point3F( 0.177649f, 0.206919f, 0.962094f ), + Point3F( 0.277107f, 0.948521f, 0.153361f ), + Point3F( 0.507629f, 0.661918f, -0.551523f ), + Point3F( -0.503110f, -0.579308f, -0.641313f ), + Point3F( 0.600522f, 0.736495f, -0.311364f ), + Point3F( -0.691096f, -0.715301f, -0.103592f ), + Point3F( -0.041083f, -0.858497f, 0.511171f ), + Point3F( 0.207773f, -0.480062f, -0.852274f ), + Point3F( 0.795719f, 0.464614f, 0.388543f ), + Point3F( -0.100433f, 0.495114f, -0.863004f ), + Point3F( 0.703249f, 0.065157f, -0.707951f ), + Point3F( -0.324171f, -0.941112f, 0.096024f ), + Point3F( -0.134933f, -0.940212f, 0.312722f ), + Point3F( -0.438240f, 0.752088f, -0.492249f ), + Point3F( 0.964762f, -0.198855f, 0.172311f ), + Point3F( -0.831799f, 0.196807f, 0.519015f ), + Point3F( -0.508008f, 0.819902f, 0.263986f ), + Point3F( 0.471075f, -0.001146f, 0.882092f ), + Point3F( 0.919512f, 0.246162f, -0.306435f ), + Point3F( -0.960050f, 0.279828f, -0.001187f ), + Point3F( 0.110232f, -0.847535f, -0.519165f ), + Point3F( 0.208229f, 0.697360f, 0.685806f ), + Point3F( -0.199680f, -0.560621f, 0.803637f ), + Point3F( 0.170135f, -0.679985f, -0.713214f ), + Point3F( 0.758371f, -0.494907f, 0.424195f ), + Point3F( 0.077734f, -0.755978f, 0.649965f ), + Point3F( 0.612831f, -0.672475f, 0.414987f ), + Point3F( 0.142776f, 0.836698f, -0.528726f ), + Point3F( -0.765185f, 0.635778f, 0.101382f ), + Point3F( 0.669873f, -0.419737f, 0.612447f ), + Point3F( 0.593549f, 0.194879f, 0.780847f ), + Point3F( 0.646930f, 0.752173f, 0.125368f ), + Point3F( 0.837721f, 0.545266f, -0.030127f ), + Point3F( 0.541505f, 0.768070f, 0.341820f ), + Point3F( 0.760679f, -0.365715f, -0.536301f ), + Point3F( 0.381516f, 0.640377f, 0.666605f ), + Point3F( 0.565794f, -0.072415f, -0.821361f ), + Point3F( -0.466072f, -0.401588f, 0.788356f ), + Point3F( 0.987146f, 0.096290f, 0.127560f ), + Point3F( 0.509709f, -0.688886f, -0.515396f ), + Point3F( -0.135132f, -0.988046f, -0.074192f ), + Point3F( 0.600499f, 0.476471f, -0.642166f ), + Point3F( -0.732326f, -0.275320f, -0.622815f ), + Point3F( -0.881141f, -0.470404f, 0.048078f ), + Point3F( 0.051548f, 0.601042f, 0.797553f ), + Point3F( 0.402027f, -0.763183f, 0.505891f ), + Point3F( 0.404233f, -0.208288f, 0.890624f ), + Point3F( -0.311793f, 0.343843f, 0.885752f ), + Point3F( 0.098132f, -0.937014f, 0.335223f ), + Point3F( 0.537158f, 0.830585f, -0.146936f ), + Point3F( 0.725277f, 0.298172f, -0.620538f ), + Point3F( -0.882025f, 0.342976f, -0.323110f ), + Point3F( -0.668829f, 0.424296f, -0.610443f ), + Point3F( -0.408835f, -0.476442f, -0.778368f ), + Point3F( 0.809472f, 0.397249f, -0.432375f ), + Point3F( -0.909184f, -0.205938f, -0.361903f ), + Point3F( 0.866930f, -0.347934f, -0.356895f ), + Point3F( 0.911660f, -0.141281f, -0.385897f ), + Point3F( -0.431404f, -0.844074f, -0.318480f ), + Point3F( -0.950593f, -0.073496f, 0.301614f ), + Point3F( -0.719716f, 0.626915f, -0.298305f ), + Point3F( -0.779887f, 0.617067f, -0.104899f ), + Point3F( -0.475899f, -0.542630f, 0.692151f ), + Point3F( 0.081952f, -0.157248f, -0.984153f ), + Point3F( 0.923990f, -0.381662f, -0.024025f ), + Point3F( -0.957998f, 0.120979f, -0.260008f ), + Point3F( 0.306601f, 0.227975f, -0.924134f ), + Point3F( -0.141244f, 0.989182f, 0.039601f ), + Point3F( 0.077097f, 0.186288f, -0.979466f ), + Point3F( -0.630407f, -0.259801f, 0.731499f ), + Point3F( 0.718150f, 0.637408f, 0.279233f ), + Point3F( 0.340946f, 0.110494f, 0.933567f ), + Point3F( -0.396671f, 0.503020f, -0.767869f ), + Point3F( 0.636943f, -0.245005f, 0.730942f ), + Point3F( -0.849605f, -0.518660f, -0.095724f ), + Point3F( -0.388203f, 0.105395f, 0.915527f ), + Point3F( -0.280671f, -0.776541f, -0.564099f ), + Point3F( -0.601680f, 0.215451f, -0.769131f ), + Point3F( -0.660112f, -0.632371f, -0.405412f ), + Point3F( 0.921096f, 0.284072f, 0.266242f ), + Point3F( 0.074850f, -0.300846f, 0.950731f ), + Point3F( 0.943952f, -0.067062f, 0.323198f ), + Point3F( -0.917838f, -0.254589f, 0.304561f ), + Point3F( 0.889843f, -0.409008f, 0.202219f ), + Point3F( -0.565849f, 0.753721f, -0.334246f ), + Point3F( 0.791460f, 0.555918f, -0.254060f ), + Point3F( 0.261936f, 0.703590f, -0.660568f ), + Point3F( -0.234406f, 0.952084f, 0.196444f ), + Point3F( 0.111205f, 0.979492f, -0.168014f ), + Point3F( -0.869844f, -0.109095f, -0.481113f ), + Point3F( -0.337728f, -0.269701f, -0.901777f ), + Point3F( 0.366793f, 0.408875f, -0.835634f ), + Point3F( -0.098749f, 0.261316f, 0.960189f ), + Point3F( -0.272379f, -0.847100f, 0.456324f ), + Point3F( -0.319506f, 0.287444f, -0.902935f ), + Point3F( 0.873383f, -0.294109f, 0.388203f ), + Point3F( -0.088950f, 0.710450f, 0.698104f ), + Point3F( 0.551238f, -0.786552f, 0.278340f ), + Point3F( 0.724436f, -0.663575f, -0.186712f ), + Point3F( 0.529741f, -0.606539f, 0.592861f ), + Point3F( -0.949743f, -0.282514f, 0.134809f ), + Point3F( 0.155047f, 0.419442f, -0.894443f ), + Point3F( -0.562653f, -0.329139f, -0.758346f ), + Point3F( 0.816407f, -0.576953f, 0.024576f ), + Point3F( 0.178550f, -0.950242f, -0.255266f ), + Point3F( 0.479571f, 0.706691f, 0.520192f ), + Point3F( 0.391687f, 0.559884f, -0.730145f ), + Point3F( 0.724872f, -0.205570f, -0.657496f ), + Point3F( -0.663196f, -0.517587f, -0.540624f ), + Point3F( -0.660054f, -0.122486f, -0.741165f ), + Point3F( -0.531989f, 0.374711f, -0.759328f ), + Point3F( 0.194979f, -0.059120f, 0.979024f ) +}; + +U8 TSMesh::encodeNormal( const Point3F &normal ) +{ + U8 bestIndex = 0; + F32 bestDot = -10E30f; + for ( U32 i = 0; i < 256; i++ ) + { + F32 dot = mDot( normal, smU8ToNormalTable[i] ); + if ( dot > bestDot ) + { + bestIndex = i; + bestDot = dot; + } + } + return bestIndex; +} + +//----------------------------------------------------- +// TSMesh assemble from/ dissemble to memory buffer +//----------------------------------------------------- + +#define tsalloc TSShape::smTSAlloc + +TSMesh* TSMesh::assembleMesh( U32 meshType, bool skip ) +{ + static TSMesh tempStandardMesh; + static TSSkinMesh tempSkinMesh; + static TSDecalMesh tempDecalMesh; + static TSSortedMesh tempSortedMesh; + + bool justSize = skip || !tsalloc.allocShape32(0); // if this returns NULL, we're just sizing memory block + + // a little funny business because we pretend decals are derived from meshes + S32 * ret = NULL; + TSMesh * mesh = NULL; + TSDecalMesh * decal = NULL; + + if ( justSize ) + { + switch ( meshType ) + { + case StandardMeshType : + { + ret = (S32*)&tempStandardMesh; + mesh = &tempStandardMesh; + tsalloc.allocShape32( sizeof(TSMesh) >> 2 ); + break; + } + case SkinMeshType : + { + ret = (S32*)&tempSkinMesh; + mesh = &tempSkinMesh; + tsalloc.allocShape32( sizeof(TSSkinMesh) >> 2 ); + break; + } + case DecalMeshType : + { + ret = (S32*)&tempDecalMesh; + decal = &tempDecalMesh; + tsalloc.allocShape32( sizeof(TSDecalMesh) >> 2 ); + break; + } + case SortedMeshType : + { + ret = (S32*)&tempSortedMesh; + mesh = &tempSortedMesh; + tsalloc.allocShape32( sizeof(TSSortedMesh) >> 2 ); + break; + } + } + } + else + { + switch ( meshType ) + { + case StandardMeshType : + { + ret = tsalloc.allocShape32( sizeof(TSMesh) >> 2 ); + constructInPlace( (TSMesh*)ret ); + mesh = (TSMesh*)ret; + break; + } + case SkinMeshType : + { + ret = tsalloc.allocShape32( sizeof(TSSkinMesh) >> 2 ); + constructInPlace( (TSSkinMesh*)ret ); + mesh = (TSSkinMesh*)ret; + break; + } + case DecalMeshType : + { + ret = tsalloc.allocShape32( sizeof(TSDecalMesh) >> 2 ); + constructInPlace((TSDecalMesh*)ret); + decal = (TSDecalMesh*)ret; + break; + } + case SortedMeshType : + { + ret = tsalloc.allocShape32( sizeof(TSSortedMesh) >> 2 ); + constructInPlace( (TSSortedMesh*)ret ); + mesh = (TSSortedMesh*)ret; + break; + } + } + } + + tsalloc.setSkipMode( skip ); + + if ( mesh ) + mesh->assemble( skip ); + + if ( decal ) + decal->assemble( skip ); + + tsalloc.setSkipMode( false ); + + return (TSMesh*)ret; +} + +void TSMesh::convertToTris( const TSDrawPrimitive *primitivesIn, + const S32 *indicesIn, + S32 numPrimIn, + S32 &numPrimOut, + S32 &numIndicesOut, + TSDrawPrimitive *primitivesOut, + S32 *indicesOut ) const +{ + S32 prevMaterial = -99999; + TSDrawPrimitive * newDraw = NULL; + numPrimOut = 0; + numIndicesOut = 0; + for ( S32 i = 0; i < numPrimIn; i++ ) + { + S32 newMat = primitivesIn[i].matIndex; + newMat &= ~TSDrawPrimitive::TypeMask; + + U32 start = primitivesIn[i].start; + U32 prevStart = (i > 0) ? primitivesIn[i-1].start : start; + U32 numElements = primitivesIn[i].numElements; + + // Add a new primitive if changing materials, or if this primitive + // indexes vertices in a different 16-bit range + if ( ( newMat != prevMaterial ) || + ((indicesIn[prevStart] ^ indicesIn[start]) & 0xFFFF0000) ) + { + if ( primitivesOut ) + { + newDraw = &primitivesOut[numPrimOut]; + newDraw->start = numIndicesOut; + newDraw->numElements = 0; + newDraw->matIndex = newMat | TSDrawPrimitive::Triangles; + } + numPrimOut++; + prevMaterial = newMat; + } + + // gonna depend on what kind of primitive it is... + if ( (primitivesIn[i].matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles) + { + for ( S32 j = 0; j < numElements; j += 3 ) + { + if ( indicesOut ) + { + indicesOut[numIndicesOut + 0] = indicesIn[start + j + 0]; + indicesOut[numIndicesOut + 1] = indicesIn[start + j + 1]; + indicesOut[numIndicesOut + 2] = indicesIn[start + j + 2]; + } + if ( newDraw ) + newDraw->numElements += 3; + + numIndicesOut += 3; + } + } + else + { + U32 idx0 = indicesIn[start + 0]; + U32 idx1; + U32 idx2 = indicesIn[start + 1]; + U32 * nextIdx = &idx1; + for ( S32 j = 2; j < numElements; j++ ) + { + *nextIdx = idx2; + nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1); + idx2 = indicesIn[start + j]; + if ( idx0 == idx1 || idx1 == idx2 || idx2 == idx0 ) + continue; + + if ( indicesOut ) + { + indicesOut[numIndicesOut+0] = idx0; + indicesOut[numIndicesOut+1] = idx1; + indicesOut[numIndicesOut+2] = idx2; + } + + if ( newDraw ) + newDraw->numElements += 3; + numIndicesOut += 3; + } + } + } +} + +void unwindStrip( const S32 * indices, S32 numElements, Vector &triIndices ) +{ + U32 idx0 = indices[0]; + U32 idx1; + U32 idx2 = indices[1]; + U32 * nextIdx = &idx1; + for ( S32 j = 2; j < numElements; j++ ) + { + *nextIdx = idx2; + nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1); + idx2 = indices[j]; + if ( idx0 == idx1 || idx1 == idx2 || idx2 == idx0 ) + continue; + + triIndices.push_back( idx0 ); + triIndices.push_back( idx1 ); + triIndices.push_back( idx2 ); + } +} + +void TSMesh::convertToSingleStrip( const TSDrawPrimitive *primitivesIn, + const S32 *indicesIn, + S32 numPrimIn, + S32 &numPrimOut, + S32 &numIndicesOut, + TSDrawPrimitive *primitivesOut, + S32 *indicesOut ) const +{ + S32 prevMaterial = -99999; + TSDrawPrimitive * newDraw = NULL; + TSDrawPrimitive * newTris = NULL; + Vector triIndices; + S32 curDrawOut = 0; + numPrimOut = 0; + numIndicesOut = 0; + for ( S32 i = 0; i < numPrimIn; i++ ) + { + S32 newMat = primitivesIn[i].matIndex; + + U32 start = primitivesIn[i].start; + U32 prevStart = (i > 0) ? primitivesIn[i-1].start : start; + U32 numElements = primitivesIn[i].numElements; + + // Add a new primitive if changing materials, or if this primitive + // indexes vertices in a different 16-bit range + if ( ( newMat != prevMaterial ) || + ((indicesIn[prevStart] ^ indicesIn[start]) & 0xFFFF0000) ) + { + // before adding the new primitive, transfer triangle indices + if ( triIndices.size() ) + { + if ( newTris && indicesOut ) + { + newTris->start = numIndicesOut; + newTris->numElements = triIndices.size(); + dMemcpy(&indicesOut[numIndicesOut],triIndices.address(),triIndices.size()*sizeof(U32)); + } + numIndicesOut += triIndices.size(); + triIndices.clear(); + newTris = NULL; + } + + if ( primitivesOut ) + { + newDraw = &primitivesOut[numPrimOut]; + newDraw->start = numIndicesOut; + newDraw->numElements = 0; + newDraw->matIndex = newMat; + } + + numPrimOut++; + curDrawOut = 0; + prevMaterial = newMat; + } + + // gonna depend on what kind of primitive it is... + // from above we know it's the same kind as the one we're building... + if ( (primitivesIn[i].matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles) + { + // triangles primitive...add to it + for ( S32 j = 0; j < numElements; j += 3 ) + { + if ( indicesOut ) + { + indicesOut[numIndicesOut + 0] = indicesIn[start + j + 0]; + indicesOut[numIndicesOut + 1] = indicesIn[start + j + 1]; + indicesOut[numIndicesOut + 2] = indicesIn[start + j + 2]; + } + + if ( newDraw ) + newDraw->numElements += 3; + + numIndicesOut += 3; + } + } + else + { + // strip primitive... + // if numElements less than smSmallestStripSize, add to triangles... + if ( numElements < smMinStripSize + 2 ) + { + // put triangle indices aside until material changes... + if ( triIndices.empty() ) + { + // set up for new triangle primitive and add it if we are copying data right now + if ( primitivesOut ) + { + newTris = &primitivesOut[numPrimOut]; + newTris->matIndex = newMat; + newTris->matIndex &= ~(TSDrawPrimitive::Triangles|TSDrawPrimitive::Strip); + newTris->matIndex |= TSDrawPrimitive::Triangles; + } + + numPrimOut++; + } + unwindStrip( indicesIn + start, numElements, triIndices ); + } + else + { + // strip primitive...add to it + if ( indicesOut ) + { + if ( curDrawOut & 1 ) + { + indicesOut[numIndicesOut + 0] = indicesOut[numIndicesOut - 1]; + indicesOut[numIndicesOut + 1] = indicesOut[numIndicesOut - 1]; + indicesOut[numIndicesOut + 2] = indicesIn[start]; + dMemcpy(indicesOut+numIndicesOut+3,indicesIn+start,numElements*sizeof(U32)); + } + else if ( curDrawOut ) + { + indicesOut[numIndicesOut + 0] = indicesOut[numIndicesOut - 1]; + indicesOut[numIndicesOut + 1] = indicesIn[start]; + dMemcpy(indicesOut+numIndicesOut+2,indicesIn+start,numElements*sizeof(U32)); + } + else + dMemcpy(indicesOut+numIndicesOut,indicesIn+start,numElements*sizeof(U32)); + } + S32 added = numElements; + added += curDrawOut ? (curDrawOut&1 ? 3 : 2) : 0; + if ( newDraw ) + newDraw->numElements += added; + + numIndicesOut += added; + curDrawOut += added; + } + } + } + // spit out tris before leaving + // before adding the new primitive, transfer triangle indices + if ( triIndices.size() ) + { + if ( newTris && indicesOut ) + { + newTris->start = numIndicesOut; + newTris->numElements = triIndices.size(); + dMemcpy(&indicesOut[numIndicesOut],triIndices.address(),triIndices.size()*sizeof(U32)); + } + + numIndicesOut += triIndices.size(); + triIndices.clear(); + newTris = NULL; + } +} + +// this method does none of the converting that the above methods do, except that small strips are converted +// to triangle lists... +void TSMesh::leaveAsMultipleStrips( const TSDrawPrimitive *primitivesIn, + const S32 *indicesIn, + S32 numPrimIn, + S32 &numPrimOut, + S32 &numIndicesOut, + TSDrawPrimitive *primitivesOut, + S32 *indicesOut ) const +{ + S32 prevMaterial = -99999; + TSDrawPrimitive * newDraw = NULL; + Vector triIndices; + numPrimOut = 0; + numIndicesOut = 0; + for ( S32 i = 0; i < numPrimIn; i++ ) + { + S32 newMat = primitivesIn[i].matIndex; + + U32 start = primitivesIn[i].start; + U32 prevStart = (i > 0) ? primitivesIn[i-1].start : start; + U32 numElements = primitivesIn[i].numElements; + + // Add a new primitive if changing materials, or if this primitive + // indexes vertices in a different 16-bit range + if ( triIndices.size() && + (( newMat != prevMaterial ) || + ((indicesIn[prevStart] ^ indicesIn[start]) & 0xFFFF0000) )) + { + // material just changed and we have triangles lying around + // add primitive and indices for triangles and clear triIndices + if ( indicesOut ) + { + TSDrawPrimitive * newTris = &primitivesOut[numPrimOut]; + newTris->matIndex = prevMaterial; + newTris->matIndex &= ~(TSDrawPrimitive::Triangles|TSDrawPrimitive::Strip); + newTris->matIndex |= TSDrawPrimitive::Triangles; + newTris->start = numIndicesOut; + newTris->numElements = triIndices.size(); + dMemcpy(&indicesOut[numIndicesOut],triIndices.address(),triIndices.size()*sizeof(U32)); + } + numPrimOut++; + numIndicesOut += triIndices.size(); + triIndices.clear(); + } + + // this is a little convoluted because this code was adapted from convertToSingleStrip + // but we will need a new primitive only if it is a triangle primitive coming in + // or we have more elements than the min strip size... + if ( (primitivesIn[i].matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles || numElements>=smMinStripSize+2) + { + if ( primitivesOut ) + { + newDraw = &primitivesOut[numPrimOut]; + newDraw->start = numIndicesOut; + newDraw->numElements = 0; + newDraw->matIndex = newMat; + } + numPrimOut++; + } + prevMaterial = newMat; + + // gonna depend on what kind of primitive it is... + // from above we know it's the same kind as the one we're building... + if ( (primitivesIn[i].matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles) + { + // triangles primitive...add to it + for ( S32 j = 0; j < numElements; j += 3 ) + { + if ( indicesOut ) + { + indicesOut[numIndicesOut + 0] = indicesIn[start + j + 0]; + indicesOut[numIndicesOut + 1] = indicesIn[start + j + 1]; + indicesOut[numIndicesOut + 2] = indicesIn[start + j + 2]; + } + if ( newDraw ) + newDraw->numElements += 3; + + numIndicesOut += 3; + } + } + else + { + // strip primitive... + // if numElements less than smSmallestStripSize, add to triangles... + if ( numElements < smMinStripSize + 2 ) + // put triangle indices aside until material changes... + unwindStrip( indicesIn + start, numElements, triIndices ); + else + { + // strip primitive...add to it + if ( indicesOut ) + dMemcpy(indicesOut+numIndicesOut,indicesIn+start,numElements*sizeof(U32)); + if ( newDraw ) + newDraw->numElements = numElements; + + numIndicesOut += numElements; + } + } + } + // spit out tris before leaving + if ( triIndices.size() ) + { + // material just changed and we have triangles lying around + // add primitive and indices for triangles and clear triIndices + if ( indicesOut ) + { + TSDrawPrimitive *newTris = &primitivesOut[numPrimOut]; + newTris->matIndex = prevMaterial; + newTris->matIndex &= ~(TSDrawPrimitive::Triangles|TSDrawPrimitive::Strip); + newTris->matIndex |= TSDrawPrimitive::Triangles; + newTris->start = numIndicesOut; + newTris->numElements = triIndices.size(); + dMemcpy(&indicesOut[numIndicesOut],triIndices.address(),triIndices.size()*sizeof(U32)); + } + numPrimOut++; + numIndicesOut += triIndices.size(); + triIndices.clear(); + } +} + +// This method retrieves data that is shared (or possibly shared) between different meshes. +// This adds an extra step to the copying of data from the memory buffer to the shape data buffer. +// If we have no parentMesh, then we either return a pointer to the data in the memory buffer +// (in the case that we skip this mesh) or copy the data into the shape data buffer and return +// that pointer (in the case that we don't skip this mesh). +// If we do have a parent mesh, then we return a pointer to the data in the shape buffer, +// copying the data in there ourselves if our parent didn't already do it (i.e., if it was skipped). +S32 * TSMesh::getSharedData32( S32 parentMesh, S32 size, S32 **source, bool skip ) +{ + S32 * ptr; + if( parentMesh < 0 ) + ptr = skip ? tsalloc.getPointer32( size ) : tsalloc.copyToShape32( size ); + else + { + ptr = source[parentMesh]; + // if we skipped the previous mesh (and we're not skipping this one) then + // we still need to copy points into the shape... + if ( !smDataCopied[parentMesh] && !skip ) + { + S32 * tmp = ptr; + ptr = tsalloc.allocShape32( size ); + if ( ptr && tmp ) + dMemcpy(ptr, tmp, size * sizeof(S32) ); + } + } + return ptr; +} + +S8 * TSMesh::getSharedData8( S32 parentMesh, S32 size, S8 **source, bool skip ) +{ + S8 * ptr; + if( parentMesh < 0 ) + ptr = skip ? tsalloc.getPointer8( size ) : tsalloc.copyToShape8( size ); + else + { + ptr = source[parentMesh]; + // if we skipped the previous mesh (and we're not skipping this one) then + // we still need to copy points into the shape... + if ( !smDataCopied[parentMesh] && !skip ) + { + S8 * tmp = ptr; + ptr = tsalloc.allocShape8( size ); + if ( ptr && tmp ) + dMemcpy( ptr, tmp, size * sizeof(S32) ); + } + } + return ptr; +} + +void TSMesh::createVBIB() +{ + AssertFatal( getMeshType() != SkinMeshType, "TSMesh::createVBIB() - Invalid call for skinned mesh type!" ); + _createVBIB( mVB, mPB ); +} + +void TSMesh::_createVBIB( TSVertexBufferHandle &vb, GFXPrimitiveBufferHandle &pb ) +{ + AssertFatal(mVertexData.isReady(), "Call convertToAlignedMeshData() before calling _createVBIB()"); + + if ( mNumVerts == 0 || !GFXDevice::devicePresent() ) + return; + + PROFILE_SCOPE( TSMesh_CreateVBIB ); + + // Number of verts can change in LOD skinned mesh + const bool vertsChanged = ( vb && vb->mNumVerts < mNumVerts ); + +#if defined(USE_MEM_VERTEX_BUFFERS) + if(!mDynamic) + { +#endif + // Create the vertex buffer + if( vertsChanged || vb == NULL ) + vb.set( GFX, mVertSize, mVertexFormat, mNumVerts, mDynamic ? +#if defined(TORQUE_OS_XENON) + // Skinned meshes still will occasionally re-skin more than once per frame. + // This cannot happen on the Xbox360. Until this issue is resolved, use + // type volatile instead. [1/27/2010 Pat] + GFXBufferTypeVolatile : GFXBufferTypeStatic ); +#else + GFXBufferTypeDynamic : GFXBufferTypeStatic ); +#endif + + // Copy from aligned memory right into GPU memory + U8 *vertData = (U8*)vb.lock(); +#if defined(TORQUE_OS_XENON) + XMemCpyStreaming_WriteCombined( vertData, mVertexData.address(), mVertexData.mem_size() ); +#else + dMemcpy( vertData, mVertexData.address(), mVertexData.mem_size() ); +#endif + vb.unlock(); +#if defined(USE_MEM_VERTEX_BUFFERS) + } +#endif + + const bool primsChanged = ( pb.isValid() && pb->mIndexCount != indices.size() ); + if( primsChanged || pb.isNull() ) + { + // go through and create PrimitiveInfo array + Vector piArray; + GFXPrimitive pInfo; + + U32 primitivesSize = primitives.size(); + for ( U32 i = 0; i < primitivesSize; i++ ) + { + const TSDrawPrimitive & draw = primitives[i]; + + GFXPrimitiveType drawType = getDrawType( draw.matIndex >> 30 ); + + switch( drawType ) + { + case GFXTriangleList: + pInfo.type = drawType; + pInfo.numPrimitives = draw.numElements / 3; + pInfo.startIndex = draw.start; + // Use the first index to determine which 16-bit address space we are operating in + pInfo.startVertex = indices[draw.start] & 0xFFFF0000; + pInfo.minIndex = pInfo.startVertex; + pInfo.numVertices = getMin((U32)0x10000, mNumVerts - pInfo.startVertex); + break; + + case GFXTriangleStrip: + case GFXTriangleFan: + pInfo.type = drawType; + pInfo.numPrimitives = draw.numElements - 2; + pInfo.startIndex = draw.start; + // Use the first index to determine which 16-bit address space we are operating in + pInfo.startVertex = indices[draw.start] & 0xFFFF0000; + pInfo.minIndex = pInfo.startVertex; + pInfo.numVertices = getMin((U32)0x10000, mNumVerts - pInfo.startVertex); + break; + + default: + AssertFatal( false, "WTF?!" ); + } + + piArray.push_back( pInfo ); + } + + pb.set( GFX, indices.size(), piArray.size(), GFXBufferTypeStatic ); + + U16 *ibIndices = NULL; + GFXPrimitive *piInput = NULL; + pb.lock( &ibIndices, &piInput ); + + dCopyArray( ibIndices, indices.address(), indices.size() ); + dMemcpy( piInput, piArray.address(), piArray.size() * sizeof(GFXPrimitive) ); + + pb.unlock(); + } +} + +void TSMesh::assemble( bool skip ) +{ + tsalloc.checkGuard(); + + numFrames = tsalloc.get32(); + numMatFrames = tsalloc.get32(); + parentMesh = tsalloc.get32(); + tsalloc.get32( (S32*)&mBounds, 6 ); + tsalloc.get32( (S32*)&mCenter, 3 ); + mRadius = (F32)tsalloc.get32(); + + S32 numVerts = tsalloc.get32(); + S32 *ptr32 = getSharedData32( parentMesh, 3 * numVerts, (S32**)smVertsList.address(), skip ); + verts.set( (Point3F*)ptr32, numVerts ); + + S32 numTVerts = tsalloc.get32(); + ptr32 = getSharedData32( parentMesh, 2 * numTVerts, (S32**)smTVertsList.address(), skip ); + tverts.set( (Point2F*)ptr32, numTVerts ); + + if ( TSShape::smReadVersion > 25 ) + { + numTVerts = tsalloc.get32(); + ptr32 = getSharedData32( parentMesh, 2 * numTVerts, (S32**)smTVerts2List.address(), skip ); + tverts2.set( (Point2F*)ptr32, numTVerts ); + + S32 numVColors = tsalloc.get32(); + ptr32 = getSharedData32( parentMesh, numVColors, (S32**)smColorsList.address(), skip ); + colors.set( (ColorI*)ptr32, numVColors ); + } + + S8 *ptr8; + if ( TSShape::smReadVersion > 21 && TSMesh::smUseEncodedNormals) + { + // we have encoded normals and we want to use them... + if ( parentMesh < 0 ) + tsalloc.getPointer32( numVerts * 3 ); // advance past norms, don't use + norms.set( NULL, 0 ); + + ptr8 = getSharedData8( parentMesh, numVerts, (S8**)smEncodedNormsList.address(), skip ); + encodedNorms.set( ptr8, numVerts ); + } + else if ( TSShape::smReadVersion > 21 ) + { + // we have encoded normals but we don't want to use them... + ptr32 = getSharedData32( parentMesh, 3 * numVerts, (S32**)smNormsList.address(), skip ); + norms.set( (Point3F*)ptr32, numVerts ); + + if ( parentMesh < 0 ) + tsalloc.getPointer8( numVerts ); // advance past encoded normls, don't use + encodedNorms.set( NULL, 0 ); + } + else + { + // no encoded normals... + ptr32 = getSharedData32( parentMesh, 3 * numVerts, (S32**)smNormsList.address(), skip ); + norms.set( (Point3F*)ptr32, numVerts ); + encodedNorms.set( NULL, 0 ); + } + + // copy the primitives and indices...how we do this depends on what + // form we want them in when copied...just get pointers to data for now + S32 szPrimIn, szIndIn; + TSDrawPrimitive *primIn; + S32 *indIn; + bool deleteInputArrays = false; + + if (TSShape::smReadVersion > 25) + { + // mesh primitives (start, numElements) and indices are stored as 32 bit values + szPrimIn = tsalloc.get32(); + primIn = (TSDrawPrimitive*)tsalloc.getPointer32(szPrimIn*3); + szIndIn = tsalloc.get32(); + indIn = tsalloc.getPointer32(szIndIn); + } + else + { + // mesh primitives (start, numElements) indices are stored as 16 bit values + szPrimIn = tsalloc.get32(); + S16 *prim16 = tsalloc.getPointer16(szPrimIn*2); // primitive: start, numElements + S32 *prim32 = tsalloc.getPointer32(szPrimIn); // primitive: matIndex + szIndIn = tsalloc.get32(); + + // warn about non-addressable indices + if ( !skip && szIndIn >= 0x10000 ) + { + Con::warnf("Mesh contains non-addressable indices, and may not render " + "correctly. Either split this mesh into pieces of no more than 65k " + "unique verts prior to export, or use COLLADA."); + } + + S16 *ind16 = tsalloc.getPointer16(szIndIn); + + // need to copy to temporary arrays + deleteInputArrays = true; + primIn = new TSDrawPrimitive[szPrimIn]; + for (int i = 0; i < szPrimIn; i++) + { + primIn[i].start = prim16[i*2]; + primIn[i].numElements = prim16[i*2+1]; + primIn[i].matIndex = prim32[i]; + } + + indIn = new S32[szIndIn]; + dCopyArray(indIn, ind16, szIndIn); + } + + // count the number of output primitives and indices + S32 szPrimOut = szPrimIn, szIndOut = szIndIn; + if (smUseTriangles) + convertToTris(primIn, indIn, szPrimIn, szPrimOut, szIndOut, NULL, NULL); + else if (smUseOneStrip) + convertToSingleStrip(primIn, indIn, szPrimIn, szPrimOut, szIndOut, NULL, NULL); + else + leaveAsMultipleStrips(primIn, indIn, szPrimIn, szPrimOut, szIndOut, NULL, NULL); + + // allocate enough space for the new primitives and indices (all 32 bits) + TSDrawPrimitive *primOut = (TSDrawPrimitive*)tsalloc.allocShape32(3*szPrimOut); + S32 *indOut = tsalloc.allocShape32(szIndOut); + + // copy output primitives and indices + S32 chkPrim = szPrimOut, chkInd = szIndOut; + if (smUseTriangles) + convertToTris(primIn, indIn, szPrimIn, chkPrim, chkInd, primOut, indOut); + else if (smUseOneStrip) + convertToSingleStrip(primIn, indIn, szPrimIn, chkPrim, chkInd, primOut, indOut); + else + leaveAsMultipleStrips(primIn, indIn, szPrimIn, chkPrim, chkInd, primOut, indOut); + AssertFatal(chkPrim==szPrimOut && chkInd==szIndOut,"TSMesh::primitive conversion"); + + // store output + primitives.set(primOut, szPrimOut); + indices.set(indOut, szIndOut); + + // delete temporary arrays if necessary + if (deleteInputArrays) + { + delete [] primIn; + delete [] indIn; + } + + S32 sz = tsalloc.get32(); + tsalloc.getPointer16( sz ); // skip deprecated merge indices + tsalloc.align32(); + + vertsPerFrame = tsalloc.get32(); + U32 flags = (U32)tsalloc.get32(); + if ( encodedNorms.size() ) + flags |= UseEncodedNormals; + + setFlags( flags ); + + tsalloc.checkGuard(); + + if ( tsalloc.allocShape32( 0 ) && TSShape::smReadVersion < 19 ) + computeBounds(); // only do this if we copied the data... + + if(getMeshType() != SkinMeshType) + createTangents(verts, norms); +} + +void TSMesh::disassemble() +{ + tsalloc.setGuard(); + + tsalloc.set32( numFrames ); + tsalloc.set32( numMatFrames ); + tsalloc.set32( parentMesh ); + tsalloc.copyToBuffer32( (S32*)&mBounds, 6 ); + tsalloc.copyToBuffer32( (S32*)&mCenter, 3 ); + tsalloc.set32( (S32)mRadius ); + + // Re-create the vectors + if(mVertexData.isReady()) + { + verts.setSize(mNumVerts); + tverts.setSize(mNumVerts); + norms.setSize(mNumVerts); + + if(mHasColor) + colors.setSize(mNumVerts); + if(mHasTVert2) + tverts2.setSize(mNumVerts); + + // Fill arrays + for(U32 i = 0; i < mNumVerts; i++) + { + const __TSMeshVertexBase &cv = mVertexData[i]; + verts[i] = cv.vert(); + tverts[i] = cv.tvert(); + norms[i] = cv.normal(); + + if(mHasColor) + cv.color().getColor(&colors[i]); + if(mHasTVert2) + tverts2[i] = cv.tvert2(); + } + } + + // verts... + tsalloc.set32( verts.size() ); + if ( parentMesh < 0 ) + tsalloc.copyToBuffer32( (S32*)verts.address(), 3 * verts.size() ); // if no parent mesh, then save off our verts + + // tverts... + tsalloc.set32( tverts.size() ); + if ( parentMesh < 0 ) + tsalloc.copyToBuffer32( (S32*)tverts.address(), 2 * tverts.size() ); // if no parent mesh, then save off our tverts + + if (TSShape::smVersion > 25) + { + // tverts2... + tsalloc.set32( tverts2.size() ); + if ( parentMesh < 0 ) + tsalloc.copyToBuffer32( (S32*)tverts2.address(), 2 * tverts2.size() ); // if no parent mesh, then save off our tverts + + // colors + tsalloc.set32( colors.size() ); + if ( parentMesh < 0 ) + tsalloc.copyToBuffer32( (S32*)colors.address(), colors.size() ); // if no parent mesh, then save off our tverts + } + + // norms... + if ( parentMesh < 0 ) // if no parent mesh, then save off our norms + tsalloc.copyToBuffer32( (S32*)norms.address(), 3 * norms.size() ); // norms.size()==verts.size() or error... + + // encoded norms... + if ( parentMesh < 0 ) + { + // if no parent mesh, compute encoded normals and copy over + for ( S32 i = 0; i < norms.size(); i++ ) + { + U8 normIdx = encodedNorms.size() ? encodedNorms[i] : encodeNormal( norms[i] ); + tsalloc.copyToBuffer8( (S8*)&normIdx, 1 ); + } + } + + // optimize triangle draw order during disassemble + { + FrameTemp tmpIdxs(indices.size()); + for ( S32 i = 0; i < primitives.size(); i++ ) + { + const TSDrawPrimitive& prim = primitives[i]; + + TriListOpt::OptimizeTriangleOrdering(verts.size(), prim.numElements, + indices.address() + prim.start, tmpIdxs.address()); + dCopyArray(indices.address() + prim.start, tmpIdxs.address(), + prim.numElements); + } + } + + if (TSShape::smVersion > 25) + { + // primitives... + tsalloc.set32( primitives.size() ); + tsalloc.copyToBuffer32((S32*)primitives.address(),3*primitives.size()); + + // indices... + tsalloc.set32(indices.size()); + tsalloc.copyToBuffer32((S32*)indices.address(),indices.size()); + } + else + { + // primitives + tsalloc.set32( primitives.size() ); + for (S32 i=0; i s16_indices(indices.size()); + for (S32 i=0; i21 && TSMesh::smUseEncodedNormals ) + { + // we have encoded normals and we want to use them... + if ( parentMesh < 0 ) + tsalloc.getPointer32( numVerts * 3 ); // advance past norms, don't use + batchData.initialNorms.set( NULL, 0 ); + + ptr8 = getSharedData8( parentMesh, numVerts, (S8**)smEncodedNormsList.address(), skip ); + encodedNorms.set( ptr8, numVerts ); + // Note: we don't set the encoded normals flag because we handle them in updateSkin and + // hide the fact that we are using them from base class (TSMesh) + } + else if ( TSShape::smReadVersion > 21 ) + { + // we have encoded normals but we don't want to use them... + ptr32 = getSharedData32( parentMesh, 3 * numVerts, (S32**)smNormsList.address(), skip ); + batchData.initialNorms.set( (Point3F*)ptr32, numVerts ); + + if ( parentMesh < 0 ) + tsalloc.getPointer8( numVerts ); // advance past encoded normls, don't use + + encodedNorms.set( NULL, 0 ); + } + else + { + // no encoded normals... + ptr32 = getSharedData32( parentMesh, 3 * numVerts, (S32**)smNormsList.address(), skip ); + batchData.initialNorms.set( (Point3F*)ptr32, numVerts ); + encodedNorms.set( NULL, 0 ); + } + + sz = tsalloc.get32(); + ptr32 = getSharedData32( parentMesh, 16 * sz, (S32**)smInitTransformList.address(), skip ); + batchData.initialTransforms.set( ptr32, sz ); + + sz = tsalloc.get32(); + ptr32 = getSharedData32( parentMesh, sz, (S32**)smVertexIndexList.address(), skip ); + vertexIndex.set( ptr32, sz ); + + ptr32 = getSharedData32( parentMesh, sz, (S32**)smBoneIndexList.address(), skip ); + boneIndex.set( ptr32, sz ); + + ptr32 = getSharedData32( parentMesh, sz, (S32**)smWeightList.address(), skip ); + weight.set( (F32*)ptr32, sz ); + + sz = tsalloc.get32(); + ptr32 = getSharedData32( parentMesh, sz, (S32**)smNodeIndexList.address(), skip ); + batchData.nodeIndex.set( ptr32, sz ); + + tsalloc.checkGuard(); + + if ( tsalloc.allocShape32( 0 ) && TSShape::smReadVersion < 19 ) + TSMesh::computeBounds(); // only do this if we copied the data... + + createTangents(batchData.initialVerts, batchData.initialNorms); +} + +//----------------------------------------------------------------------------- +// disassemble +//----------------------------------------------------------------------------- +void TSSkinMesh::disassemble() +{ + TSMesh::disassemble(); + + tsalloc.set32( batchData.initialVerts.size() ); + // if we have no parent mesh, then save off our verts & norms + if ( parentMesh < 0 ) + { + tsalloc.copyToBuffer32( (S32*)batchData.initialVerts.address(), 3 * batchData.initialVerts.size() ); + + // no longer do this here...let tsmesh handle this + tsalloc.copyToBuffer32( (S32*)batchData.initialNorms.address(), 3 * batchData.initialNorms.size() ); + + // if no parent mesh, compute encoded normals and copy over + for ( S32 i = 0; i < batchData.initialNorms.size(); i++ ) + { + U8 normIdx = encodedNorms.size() ? encodedNorms[i] : encodeNormal( batchData.initialNorms[i] ); + tsalloc.copyToBuffer8( (S8*)&normIdx, 1 ); + } + } + + tsalloc.set32( batchData.initialTransforms.size() ); + if ( parentMesh < 0 ) + tsalloc.copyToBuffer32( (S32*)batchData.initialTransforms.address(), batchData.initialTransforms.size() * 16 ); + + tsalloc.set32( vertexIndex.size() ); + if ( parentMesh < 0 ) + { + tsalloc.copyToBuffer32( (S32*)vertexIndex.address(), vertexIndex.size() ); + + tsalloc.copyToBuffer32( (S32*)boneIndex.address(), boneIndex.size() ); + + tsalloc.copyToBuffer32( (S32*)weight.address(), weight.size() ); + } + + tsalloc.set32( batchData.nodeIndex.size() ); + if ( parentMesh < 0 ) + tsalloc.copyToBuffer32( (S32*)batchData.nodeIndex.address(), batchData.nodeIndex.size() ); + + tsalloc.setGuard(); +} + +TSSkinMesh::TSSkinMesh() +{ + meshType = SkinMeshType; + mDynamic = true; + batchDataInitialized = false; +} + +//----------------------------------------------------------------------------- +// find tangent vector +//----------------------------------------------------------------------------- +inline void TSMesh::findTangent( U32 index1, + U32 index2, + U32 index3, + Point3F *tan0, + Point3F *tan1, + const Vector &_verts) +{ + const Point3F &v1 = _verts[index1]; + const Point3F &v2 = _verts[index2]; + const Point3F &v3 = _verts[index3]; + + const Point2F &w1 = tverts[index1]; + const Point2F &w2 = tverts[index2]; + const Point2F &w3 = tverts[index3]; + + F32 x1 = v2.x - v1.x; + F32 x2 = v3.x - v1.x; + F32 y1 = v2.y - v1.y; + F32 y2 = v3.y - v1.y; + F32 z1 = v2.z - v1.z; + F32 z2 = v3.z - v1.z; + + F32 s1 = w2.x - w1.x; + F32 s2 = w3.x - w1.x; + F32 t1 = w2.y - w1.y; + F32 t2 = w3.y - w1.y; + + F32 denom = (s1 * t2 - s2 * t1); + + if( mFabs( denom ) < 0.0001f ) + return; // handle degenerate triangles from strips + + F32 r = 1.0f / denom; + + Point3F sdir( (t2 * x1 - t1 * x2) * r, + (t2 * y1 - t1 * y2) * r, + (t2 * z1 - t1 * z2) * r ); + + Point3F tdir( (s1 * x2 - s2 * x1) * r, + (s1 * y2 - s2 * y1) * r, + (s1 * z2 - s2 * z1) * r ); + + + tan0[index1] += sdir; + tan1[index1] += tdir; + + tan0[index2] += sdir; + tan1[index2] += tdir; + + tan0[index3] += sdir; + tan1[index3] += tdir; +} + +//----------------------------------------------------------------------------- +// create array of tangent vectors +//----------------------------------------------------------------------------- +void TSMesh::createTangents(const Vector &_verts, const Vector &_norms) +{ + U32 numVerts = _verts.size(); + if ( numVerts == 0 ) + return; + + Vector tan0; + tan0.setSize( numVerts * 2 ); + + Point3F *tan1 = tan0.address() + numVerts; + dMemset( tan0.address(), 0, sizeof(Point3F) * 2 * numVerts ); + + U32 numPrimatives = primitives.size(); + + for (S32 i = 0; i < numPrimatives; i++ ) + { + const TSDrawPrimitive & draw = primitives[i]; + GFXPrimitiveType drawType = getDrawType( draw.matIndex >> 30 ); + + U32 p1Index = 0; + U32 p2Index = 0; + + U32 *baseIdx = &indices[draw.start]; + + const U32 numElements = (U32)draw.numElements; + + switch( drawType ) + { + case GFXTriangleList: + { + for( U32 j = 0; j < numElements; j += 3 ) + findTangent( baseIdx[j], baseIdx[j + 1], baseIdx[j + 2], tan0.address(), tan1, _verts ); + break; + } + + case GFXTriangleStrip: + { + p1Index = baseIdx[0]; + p2Index = baseIdx[1]; + for( U32 j = 2; j < numElements; j++ ) + { + findTangent( p1Index, p2Index, baseIdx[j], tan0.address(), tan1, _verts ); + p1Index = p2Index; + p2Index = baseIdx[j]; + } + break; + } + case GFXTriangleFan: + { + p1Index = baseIdx[0]; + p2Index = baseIdx[1]; + for( U32 j = 2; j < numElements; j++ ) + { + findTangent( p1Index, p2Index, baseIdx[j], tan0.address(), tan1, _verts ); + p2Index = baseIdx[j]; + } + break; + } + + default: + AssertFatal( false, "TSMesh::createTangents: unknown primitive type!" ); + } + } + + tangents.setSize( numVerts ); + + // fill out final info from accumulated basis data + for( U32 i = 0; i < numVerts; i++ ) + { + const Point3F &n = _norms[i]; + const Point3F &t = tan0[i]; + const Point3F &b = tan1[i]; + + Point3F tempPt = t - n * mDot( n, t ); + tempPt.normalize(); + tangents[i] = tempPt; + + Point3F cp; + mCross( n, t, &cp ); + + tangents[i].w = (mDot( cp, b ) < 0.0f) ? -1.0f : 1.0f; + } +} + +void TSMesh::convertToAlignedMeshData() +{ + if(!mVertexData.isReady()) + _convertToAlignedMeshData(mVertexData, verts, norms); +} + + +void TSSkinMesh::convertToAlignedMeshData() +{ + if(!mVertexData.isReady()) + _convertToAlignedMeshData(mVertexData, batchData.initialVerts, batchData.initialNorms); +} + +void TSMesh::_convertToAlignedMeshData( TSMeshVertexArray &vertexData, const Vector &_verts, const Vector &_norms ) +{ + // If mVertexData is ready, and the input array is different than mVertexData + // use mVertexData to quickly initialize the input array + if(mVertexData.isReady() && vertexData.address() != mVertexData.address()) + { + AssertFatal(mVertexData.size() == mNumVerts, "Vertex data length mismatch; no idea how this happened."); + + // There doesn't seem to be an _mm_realloc, even though there is an _aligned_realloc + // We really shouldn't be re-allocating anyway. Should TSShapeInstance be + // storing an array of the data structures? That would certainly bloat memory. + void *aligned_mem = dMalloc_aligned(mVertSize * mNumVerts, 16); + AssertFatal(aligned_mem, "Aligned malloc failed! Debug!"); + + vertexData.set(aligned_mem, mVertSize, mNumVerts); + vertexData.setReady(true); + +#if defined(TORQUE_OS_XENON) + XMemCpyStreaming(vertexData.address(), mVertexData.address(), vertexData.mem_size() ); +#else + dMemcpy(vertexData.address(), mVertexData.address(), vertexData.mem_size()); +#endif + return; + } + + + AssertFatal(!vertexData.isReady(), "Mesh already converted to aligned data! Re-check code!"); + AssertFatal(_verts.size() == _norms.size() && + _verts.size() == tangents.size(), + "Vectors: verts, norms, tangents must all be the same size"); + mNumVerts = _verts.size(); + + // Initialize the vertex data + vertexData.set(NULL, 0, 0); + vertexData.setReady(true); + + if(mNumVerts == 0) + return; + + mHasColor = !colors.empty(); + AssertFatal(!mHasColor || colors.size() == _verts.size(), "Vector of color elements should be the same size as other vectors"); + + mHasTVert2 = !tverts2.empty(); + AssertFatal(!mHasTVert2 || tverts2.size() == _verts.size(), "Vector of tvert2 elements should be the same size as other vectors"); + + // Create the proper array type + void *aligned_mem = dMalloc_aligned(mVertSize * mNumVerts, 16); + AssertFatal(aligned_mem, "Aligned malloc failed! Debug!"); + + dMemset(aligned_mem, 0, mNumVerts * mVertSize); + vertexData.set(aligned_mem, mVertSize, mNumVerts); + + for(U32 i = 0; i < mNumVerts; i++) + { + __TSMeshVertexBase &v = vertexData[i]; + v.vert(_verts[i]); + v.normal(_norms[i]); + v.tangent(tangents[i]); + + if(i < tverts.size()) + v.tvert(tverts[i]); + if(mHasTVert2 && i < tverts2.size()) + v.tvert2(tverts2[i]); + if(mHasColor && i < colors.size()) + v.color(colors[i]); + } + + // Now that the data is in the aligned struct, free the Vector memory + verts.free_memory(); + norms.free_memory(); + tangents.free_memory(); + tverts.free_memory(); + tverts2.free_memory(); + colors.free_memory(); +} \ No newline at end of file diff --git a/Engine/source/ts/tsMesh.h b/Engine/source/ts/tsMesh.h new file mode 100644 index 000000000..0db76ecc8 --- /dev/null +++ b/Engine/source/ts/tsMesh.h @@ -0,0 +1,547 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TSMESH_H_ +#define _TSMESH_H_ + +#ifndef _STREAM_H_ +#include "core/stream/stream.h" +#endif +#ifndef _MMATH_H_ +#include "math/mMath.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _ABSTRACTPOLYLIST_H_ +#include "collision/abstractPolyList.h" +#endif +#ifndef _GFXDEVICE_H_ +#include "gfx/gfxDevice.h" +#endif +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif +#ifndef _TSPARSEARRAY_H_ +#include "core/tSparseArray.h" +#endif + +#include "core/util/safeDelete.h" + +#if defined(TORQUE_OS_XENON) +//# define USE_MEM_VERTEX_BUFFERS +#endif + +#if defined(USE_MEM_VERTEX_BUFFERS) +# include "gfx/D3D9/360/gfx360MemVertexBuffer.h" +#endif + +namespace Opcode { class Model; class MeshInterface; } +namespace IceMaths { class IndexedTriangle; class Point; } + +class Convex; + +class SceneRenderState; +class SceneObject; +struct MeshRenderInst; +class TSRenderState; +class RenderPassManager; +class TSMaterialList; +class TSShapeInstance; +struct RayInfo; +class ConvexFeature; +class ShapeBase; + +struct TSDrawPrimitive +{ + enum + { + Triangles = 0 << 30, ///< bits 30 and 31 index element type + Strip = 1 << 30, ///< bits 30 and 31 index element type + Fan = 2 << 30, ///< bits 30 and 31 index element type + Indexed = BIT(29), ///< use glDrawElements if indexed, glDrawArrays o.w. + NoMaterial = BIT(28), ///< set if no material (i.e., texture missing) + MaterialMask = ~(Strip|Fan|Triangles|Indexed|NoMaterial), + TypeMask = Strip|Fan|Triangles + }; + + S32 start; + S32 numElements; + S32 matIndex; ///< holds material index & element type (see above enum) +}; + +#if defined(USE_MEM_VERTEX_BUFFERS) +struct __NullVertexStruct {}; +typedef GFX360MemVertexBufferHandle<__NullVertexStruct> TSVertexBufferHandle; +#else +typedef GFXVertexBufferDataHandle TSVertexBufferHandle; +#endif + + +/// +class TSMesh +{ + friend class TSShape; + public: + struct TSMeshVertexArray; + protected: + + U32 meshType; + Box3F mBounds; + Point3F mCenter; + F32 mRadius; + F32 mVisibility; + bool mDynamic; + + const GFXVertexFormat *mVertexFormat; + + U32 mVertSize; + + TSVertexBufferHandle mVB; + GFXPrimitiveBufferHandle mPB; + + void _convertToAlignedMeshData( TSMeshVertexArray &vertexData, const Vector &_verts, const Vector &_norms ); + void _createVBIB( TSVertexBufferHandle &vb, GFXPrimitiveBufferHandle &pb ); + + public: + + enum + { + /// types... + StandardMeshType = 0, + SkinMeshType = 1, + DecalMeshType = 2, + SortedMeshType = 3, + NullMeshType = 4, + TypeMask = StandardMeshType|SkinMeshType|DecalMeshType|SortedMeshType|NullMeshType, + + /// flags (stored with meshType)... + Billboard = BIT(31), HasDetailTexture = BIT(30), + BillboardZAxis = BIT(29), UseEncodedNormals = BIT(28), + FlagMask = Billboard|BillboardZAxis|HasDetailTexture|UseEncodedNormals + }; + + U32 getMeshType() const { return meshType & TypeMask; } + void setFlags(U32 flag) { meshType |= flag; } + void clearFlags(U32 flag) { meshType &= ~flag; } + U32 getFlags( U32 flag = 0xFFFFFFFF ) const { return meshType & flag; } + + const Point3F* getNormals( S32 firstVert ); + + S32 parentMesh; ///< index into shapes mesh list + S32 numFrames; + S32 numMatFrames; + S32 vertsPerFrame; + + /// @name Aligned Vertex Data + /// @{ + #pragma pack(1) + struct __TSMeshVertexBase + { + Point3F _vert; + F32 _tangentW; + Point3F _normal; + Point3F _tangent; + Point2F _tvert; + + const Point3F &vert() const { return _vert; } + void vert(const Point3F &v) { _vert = v; } + + const Point3F &normal() const { return _normal; } + void normal(const Point3F &n) { _normal = n; } + + Point4F tangent() const { return Point4F(_tangent.x, _tangent.y, _tangent.z, _tangentW); } + void tangent(const Point4F &t) { _tangent = t.asPoint3F(); _tangentW = t.w; } + + const Point2F &tvert() const { return _tvert; } + void tvert(const Point2F &tv) { _tvert = tv;} + + // Don't call these unless it's actually a __TSMeshVertex_3xUVColor, for real. + // We don't want a vftable for virtual methods. + Point2F &tvert2() const { return *reinterpret_cast(reinterpret_cast(const_cast<__TSMeshVertexBase *>(this)) + 0x30); } + void tvert2(const Point2F &tv) { (*reinterpret_cast(reinterpret_cast(this) + 0x30)) = tv; } + + GFXVertexColor &color() const { return *reinterpret_cast(reinterpret_cast(const_cast<__TSMeshVertexBase *>(this)) + 0x38); } + void color(const GFXVertexColor &c) { (*reinterpret_cast(reinterpret_cast(this) + 0x38)) = c; } + }; + + struct __TSMeshVertex_3xUVColor : public __TSMeshVertexBase + { + Point2F _tvert2; + GFXVertexColor _color; + F32 _tvert3; // Unused, but needed for alignment purposes + }; +#pragma pack() + + struct TSMeshVertexArray + { + protected: + U8 *base; + dsize_t vertSz; + bool vertexDataReady; + U32 numElements; + + public: + TSMeshVertexArray() : base(NULL), vertexDataReady(false), numElements(0) {} + virtual ~TSMeshVertexArray() { set(NULL, 0, 0); } + + virtual void set(void *b, dsize_t s, U32 n, bool autoFree = true ) + { + if(base && autoFree) + dFree_aligned(base); + base = reinterpret_cast(b); + vertSz = s; + numElements = n; + } + + // Vector-like interface + __TSMeshVertexBase &operator[](int idx) const { AssertFatal(idx < numElements, "Out of bounds access!"); return *reinterpret_cast<__TSMeshVertexBase *>(base + idx * vertSz); } + __TSMeshVertexBase *address() const { return reinterpret_cast<__TSMeshVertexBase *>(base); } + U32 size() const { return numElements; } + dsize_t mem_size() const { return numElements * vertSz; } + dsize_t vertSize() const { return vertSz; } + bool isReady() const { return vertexDataReady; } + void setReady(bool r) { vertexDataReady = r; } + }; + + bool mHasColor; + bool mHasTVert2; + + TSMeshVertexArray mVertexData; + dsize_t mNumVerts; + virtual void convertToAlignedMeshData(); + /// @} + + /// @name Vertex data + /// @{ + + template + class FreeableVector : public Vector + { + public: + bool free_memory() { return Vector::resize(0); } + + FreeableVector& operator=(const Vector& p) { Vector::operator=(p); return *this; } + FreeableVector& operator=(const FreeableVector& p) { Vector::operator=(p); return *this; } + }; + + FreeableVector verts; + FreeableVector norms; + FreeableVector tverts; + FreeableVector tangents; + + // Optional second texture uvs. + FreeableVector tverts2; + + // Optional vertex colors data. + FreeableVector colors; + /// @} + + Vector primitives; + Vector encodedNorms; + Vector indices; + + /// billboard data + Point3F billboardAxis; + + /// @name Convex Hull Data + /// Convex hulls are convex (no angles >= 180ş) meshes used for collision + /// @{ + + Vector planeNormals; + Vector planeConstants; + Vector planeMaterials; + S32 planesPerFrame; + U32 mergeBufferStart; + /// @} + + /// @name Render Methods + /// @{ + + /// This is used by sgShadowProjector to render the + /// mesh directly, skipping the render manager. + virtual void render( TSVertexBufferHandle &vb, GFXPrimitiveBufferHandle &pb ); + void innerRender( TSVertexBufferHandle &vb, GFXPrimitiveBufferHandle &pb ); + virtual void render( TSMaterialList *, + const TSRenderState &data, + bool isSkinDirty, + const Vector &transforms, + TSVertexBufferHandle &vertexBuffer, + GFXPrimitiveBufferHandle &primitiveBuffer ); + + void innerRender( TSMaterialList *, const TSRenderState &data, TSVertexBufferHandle &vb, GFXPrimitiveBufferHandle &pb ); + + /// @} + + /// @name Material Methods + /// @{ + void setFade( F32 fade ) { mVisibility = fade; } + void clearFade() { setFade( 1.0f ); } + /// @} + + /// @name Collision Methods + /// @{ + + virtual bool buildPolyList( S32 frame, AbstractPolyList * polyList, U32 & surfaceKey, TSMaterialList* materials ); + virtual bool getFeatures( S32 frame, const MatrixF&, const VectorF&, ConvexFeature*, U32 &surfaceKey ); + virtual void support( S32 frame, const Point3F &v, F32 *currMaxDP, Point3F *currSupport ); + virtual bool castRay( S32 frame, const Point3F & start, const Point3F & end, RayInfo * rayInfo, TSMaterialList* materials ); + virtual bool castRayRendered( S32 frame, const Point3F & start, const Point3F & end, RayInfo * rayInfo, TSMaterialList* materials ); + virtual bool buildConvexHull(); ///< returns false if not convex (still builds planes) + bool addToHull( U32 idx0, U32 idx1, U32 idx2 ); + /// @} + + /// @name Bounding Methods + /// calculate and get bounding information + /// @{ + + void computeBounds(); + virtual void computeBounds( const MatrixF &transform, Box3F &bounds, S32 frame = 0, Point3F *center = NULL, F32 *radius = NULL ); + void computeBounds( const Point3F *, S32 numVerts, S32 stride, const MatrixF &transform, Box3F &bounds, Point3F *center, F32 *radius ); + const Box3F& getBounds() const { return mBounds; } + const Point3F& getCenter() const { return mCenter; } + F32 getRadius() const { return mRadius; } + virtual S32 getNumPolys() const; + + static U8 encodeNormal( const Point3F &normal ); + static const Point3F& decodeNormal( U8 ncode ) { return smU8ToNormalTable[ncode]; } + /// @} + + /// persist methods... + virtual void assemble( bool skip ); + static TSMesh* assembleMesh( U32 meshType, bool skip ); + virtual void disassemble(); + + void createVBIB(); + void createTangents(const Vector &_verts, const Vector &_norms); + void findTangent( U32 index1, + U32 index2, + U32 index3, + Point3F *tan0, + Point3F *tan1, + const Vector &_verts); + + /// on load...optionally convert primitives to other form + static bool smUseTriangles; + static bool smUseOneStrip; + static S32 smMinStripSize; + static bool smUseEncodedNormals; + + /// Enables mesh instancing on non-skin meshes that + /// have less that this count of verts. + static S32 smMaxInstancingVerts; + + /// convert primitives on load... + void convertToTris(const TSDrawPrimitive *primitivesIn, const S32 *indicesIn, + S32 numPrimIn, S32 & numPrimOut, S32 & numIndicesOut, + TSDrawPrimitive *primitivesOut, S32 *indicesOut) const; + void convertToSingleStrip(const TSDrawPrimitive *primitivesIn, const S32 *indicesIn, + S32 numPrimIn, S32 &numPrimOut, S32 &numIndicesOut, + TSDrawPrimitive *primitivesOut, S32 *indicesOut) const; + void leaveAsMultipleStrips(const TSDrawPrimitive *primitivesIn, const S32 *indicesIn, + S32 numPrimIn, S32 &numPrimOut, S32 &numIndicesOut, + TSDrawPrimitive *primitivesOut, S32 *indicesOut) const; + + /// methods used during assembly to share vertexand other info + /// between meshes (and for skipping detail levels on load) + S32* getSharedData32( S32 parentMesh, S32 size, S32 **source, bool skip ); + S8* getSharedData8( S32 parentMesh, S32 size, S8 **source, bool skip ); + + /// @name Assembly Variables + /// variables used during assembly (for skipping mesh detail levels + /// on load and for sharing verts between meshes) + /// @{ + + static Vector smVertsList; + static Vector smNormsList; + static Vector smEncodedNormsList; + + static Vector smTVertsList; + + // Optional second texture uvs. + static Vector smTVerts2List; + + // Optional vertex colors. + static Vector smColorsList; + + static Vector smDataCopied; + + static const Point3F smU8ToNormalTable[]; + /// @} + + + TSMesh(); + virtual ~TSMesh(); + + Opcode::Model *mOptTree; + Opcode::MeshInterface* mOpMeshInterface; + IceMaths::IndexedTriangle* mOpTris; + IceMaths::Point* mOpPoints; + + void prepOpcodeCollision(); + bool buildConvexOpcode( const MatrixF &mat, const Box3F &bounds, Convex *c, Convex *list ); + bool buildPolyListOpcode( const S32 od, AbstractPolyList *polyList, const Box3F &nodeBox, TSMaterialList *materials ); + bool castRayOpcode( const Point3F &start, const Point3F &end, RayInfo *rayInfo, TSMaterialList *materials ); + + static const F32 VISIBILITY_EPSILON; +}; + + +class TSSkinMesh : public TSMesh +{ +public: + struct BatchData + { + enum Constants + { + maxBonePerVert = 16, // Abitrarily chosen + }; + + /// @name Batch by vertex + /// These are used for batches where each element is a vertex, built by + /// iterating over 0..maxBonePerVert bone transforms + /// @{ + struct TransformOp + { + S32 transformIndex; + F32 weight; + + TransformOp() : transformIndex( -1 ), weight( -1.0f ) {} + TransformOp( const S32 tIdx, const F32 w ) : transformIndex( tIdx ), weight( w ) {}; + }; + + struct BatchedVertex + { + S32 vertexIndex; + S32 transformCount; + TransformOp transform[maxBonePerVert]; + + BatchedVertex() : vertexIndex( -1 ), transformCount( -1 ) {} + }; + + Vector vertexBatchOperations; + /// @} + + /// @name Batch by Bone Transform + /// These are used for batches where each element is a bone transform, + /// and verts/normals are batch transformed against each element + /// @{ + + + #pragma pack(1) + + dALIGN( + + struct BatchedVertWeight + { + Point3F vert; // Do not change the ordering of these members + F32 weight; + Point3F normal; + S32 vidx; + } + + ); // dALIGN + + #pragma pack() + + struct BatchedTransform + { + public: + BatchedVertWeight *alignedMem; + dsize_t numElements; + Vector *_tmpVec; + + BatchedTransform() : alignedMem(NULL), numElements(0), _tmpVec(NULL) {} + virtual ~BatchedTransform() + { + if(alignedMem) + dFree_aligned(alignedMem); + alignedMem = NULL; + SAFE_DELETE(_tmpVec); + } + }; + SparseArray transformBatchOperations; + Vector transformKeys; + /// @} + + // # = num bones + Vector nodeIndex; + Vector initialTransforms; + + // # = numverts + Vector initialVerts; + Vector initialNorms; + }; + + /// This method will build the batch operations and prepare the BatchData + /// for use. + void createBatchData(); + virtual void convertToAlignedMeshData(); + +public: + typedef TSMesh Parent; + + /// Structure containing data needed to batch skinning + BatchData batchData; + bool batchDataInitialized; + + /// vectors that define the vertex, weight, bone tuples + Vector weight; + Vector boneIndex; + Vector vertexIndex; + + /// set verts and normals... + void updateSkin( const Vector &transforms, TSVertexBufferHandle &instanceVB, GFXPrimitiveBufferHandle &instancePB ); + + // render methods.. + void render( TSVertexBufferHandle &instanceVB, GFXPrimitiveBufferHandle &instancePB ); + void render( TSMaterialList *, + const TSRenderState &data, + bool isSkinDirty, + const Vector &transforms, + TSVertexBufferHandle &vertexBuffer, + GFXPrimitiveBufferHandle &primitiveBuffer ); + + // collision methods... + bool buildPolyList( S32 frame, AbstractPolyList *polyList, U32 &surfaceKey, TSMaterialList *materials ); + bool castRay( S32 frame, const Point3F &start, const Point3F &end, RayInfo *rayInfo, TSMaterialList *materials ); + bool buildConvexHull(); // does nothing, skins don't use this + + void computeBounds( const MatrixF &transform, Box3F &bounds, S32 frame, Point3F *center, F32 *radius ); + + /// persist methods... + void assemble( bool skip ); + void disassemble(); + + /// variables used during assembly (for skipping mesh detail levels + /// on load and for sharing verts between meshes) + static Vector smInitTransformList; + static Vector smVertexIndexList; + static Vector smBoneIndexList; + static Vector smWeightList; + static Vector smNodeIndexList; + + TSSkinMesh(); +}; + + +#endif // _TSMESH_H_ diff --git a/Engine/source/ts/tsMeshFit.cpp b/Engine/source/ts/tsMeshFit.cpp new file mode 100644 index 000000000..469b74d01 --- /dev/null +++ b/Engine/source/ts/tsMeshFit.cpp @@ -0,0 +1,940 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "console/consoleTypes.h" +#include "core/resourceManager.h" +#include "ts/tsShapeConstruct.h" +#include "console/engineAPI.h" + +// define macros required for ConvexDecomp headers +#if defined( _WIN32 ) +#define WIN32 +#elif defined( __MACOSX__ ) +#define APPLE +#endif + +#include "convexDecomp/NvFloatMath.h" +#include "convexDecomp/NvConvexDecomposition.h" +#include "convexDecomp/NvStanHull.h" + +//----------------------------------------------------------------------------- +static const Point3F sFacePlanes[] = { + Point3F( -1.0f, 0.0f, 0.0f ), + Point3F( 1.0f, 0.0f, 0.0f ), + Point3F( 0.0f, -1.0f, 0.0f ), + Point3F( 0.0f, 1.0f, 0.0f ), + Point3F( 0.0f, 0.0f, -1.0f ), + Point3F( 0.0f, 0.0f, 1.0f ), +}; + +static const Point3F sXEdgePlanes[] = { + Point3F( 0.0f, -0.7071f, -0.7071f ), + Point3F( 0.0f, -0.7071f, 0.7071f ), + Point3F( 0.0f, 0.7071f, -0.7071f ), + Point3F( 0.0f, 0.7071f, 0.7071f ), +}; + +static const Point3F sYEdgePlanes[] = { + Point3F( -0.7071f, 0.0f, -0.7071f ), + Point3F( -0.7071f, 0.0f, 0.7071f ), + Point3F( 0.7071f, 0.0f, -0.7071f ), + Point3F( 0.7071f, 0.0f, 0.7071f ), +}; + +static const Point3F sZEdgePlanes[] = { + Point3F( -0.7071f, -0.7071f, 0.0f ), + Point3F( -0.7071f, 0.7071f, 0.0f ), + Point3F( 0.7071f, -0.7071f, 0.0f ), + Point3F( 0.7071f, 0.7071f, 0.0f ), +}; + +static const Point3F sCornerPlanes[] = { + Point3F( -0.5774f, -0.5774f, -0.5774f ), + Point3F( -0.5774f, -0.5774f, 0.5774f ), + Point3F( -0.5774f, 0.5774f, -0.5774f ), + Point3F( -0.5774f, 0.5774f, 0.5774f ), + Point3F( 0.5774f, -0.5774f, -0.5774f ), + Point3F( 0.5774f, -0.5774f, 0.5774f ), + Point3F( 0.5774f, 0.5774f, -0.5774f ), + Point3F( 0.5774f, 0.5774f, 0.5774f ), +}; + +//----------------------------------------------------------------------------- + +/** A helper class for fitting primitives (Box, Sphere, Capsule) to a triangulated mesh */ +struct PrimFit +{ + MatrixF mBoxTransform; + Point3F mBoxSides; + + Point3F mSphereCenter; + F32 mSphereRadius; + + MatrixF mCapTransform; + F32 mCapRadius; + F32 mCapHeight; + +public: + PrimFit() : + mBoxTransform(true), mBoxSides(1,1,1), + mSphereCenter(0,0,0), mSphereRadius(1), + mCapTransform(true), mCapRadius(1), mCapHeight(1) + { + } + + inline F32 getBoxVolume() const { return mBoxSides.x * mBoxSides.y * mBoxSides.z; } + inline F32 getSphereVolume() const { return 4.0f / 3.0f * M_PI * mPow( mSphereRadius, 3 ); } + inline F32 getCapsuleVolume() const { return 2 * M_PI * mPow( mCapRadius, 2 ) * (4.0f / 3.0f * mCapRadius + mCapHeight); } + + void fitBox( U32 vertCount, const F32* verts ) + { + CONVEX_DECOMPOSITION::fm_computeBestFitOBB( vertCount, verts, sizeof(F32)*3, (F32*)mBoxSides, (F32*)mBoxTransform ); + mBoxTransform.transpose(); + } + + void fitSphere( U32 vertCount, const F32* verts ) + { + mSphereRadius = CONVEX_DECOMPOSITION::fm_computeBestFitSphere( vertCount, verts, sizeof(F32)*3, (F32*)mSphereCenter ); + } + + void fitCapsule( U32 vertCount, const F32* verts ) + { + CONVEX_DECOMPOSITION::fm_computeBestFitCapsule( vertCount, verts, sizeof(F32)*3, mCapRadius, mCapHeight, (F32*)mCapTransform ); + mCapTransform.transpose(); + } +}; + +class MeshFit +{ +public: + enum eMeshType + { + Box = 0, + Sphere, + Capsule, + Hull, + }; + + struct Mesh + { + eMeshType type; + MatrixF transform; + TSMesh *tsmesh; + }; + +private: + TSShape *mShape; ///!< Source geometry shape + Vector mVerts; ///!< Source geometry verts (all meshes) + Vector mIndices; ///!< Source geometry indices (triangle lists, all meshes) + + bool mIsReady; ///!< Flag indicating whether we are ready to fit/create meshes + + Vector mMeshes; ///!< Fitted meshes + + void addSourceMesh( const TSShape::Object& obj, const TSMesh* mesh ); + TSMesh* initMeshFromFile( const String& filename ) const; + TSMesh* createTriMesh( F32* verts, S32 numVerts, U32* indices, S32 numTris ) const; + F32 maxDot( const VectorF& v ) const; + void fitK_DOP( const Vector& planes ); + +public: + MeshFit(TSShape* shape) : mShape(shape), mIsReady(false) { } + + void setReady() { mIsReady = true; } + bool isReady() const { return mIsReady; } + + void initSourceGeometry( const String& target ); + + S32 getMeshCount() const { return mMeshes.size(); } + Mesh* getMesh( S32 index ) { return &(mMeshes[index]); } + + // Box + void addBox( const Point3F& sides, const MatrixF& mat ); + void fitOBB(); + + // Sphere + void addSphere( F32 radius, const Point3F& center ); + void fitSphere(); + + // Capsule + void addCapsule( F32 radius, F32 height, const MatrixF& mat ); + void fitCapsule(); + + // k-DOP + void fit10_DOP_X(); + void fit10_DOP_Y(); + void fit10_DOP_Z(); + void fit18_DOP(); + void fit26_DOP(); + + // Convex Hulls + void fitConvexHulls( U32 depth, F32 mergeThreshold, F32 concavityThreshold, U32 maxHullVerts, + F32 boxMaxError, F32 sphereMaxError, F32 capsuleMaxError ); +}; + + +void MeshFit::initSourceGeometry( const String& target ) +{ + mMeshes.clear(); + mVerts.clear(); + mIndices.clear(); + + if ( target.equal( "bounds", String::NoCase ) ) + { + // Add all geometry in the highest detail level + S32 dl = 0; + S32 ss = mShape->details[dl].subShapeNum; + if ( ss < 0 ) + return; + + S32 od = mShape->details[dl].objectDetailNum; + S32 start = mShape->subShapeFirstObject[ss]; + S32 end = start + mShape->subShapeNumObjects[ss]; + + for ( S32 i = start; i < end; i++ ) + { + const TSShape::Object &obj = mShape->objects[i]; + const TSMesh* mesh = ( od < obj.numMeshes ) ? mShape->meshes[obj.startMeshIndex + od] : NULL; + if ( mesh ) + addSourceMesh( obj, mesh ); + } + } + else + { + // Add highest detail mesh from this object + S32 objIndex = mShape->findObject( target ); + if ( objIndex == -1 ) + return; + + const TSShape::Object &obj = mShape->objects[objIndex]; + for ( S32 i = 0; i < obj.numMeshes; i++ ) + { + const TSMesh* mesh = mShape->meshes[obj.startMeshIndex + i]; + if ( mesh ) + { + addSourceMesh( obj, mesh ); + break; + } + } + } + + mIsReady = ( !mVerts.empty() && !mIndices.empty() ); +} + +void MeshFit::addSourceMesh( const TSShape::Object& obj, const TSMesh* mesh ) +{ + // Add indices + S32 indicesBase = mIndices.size(); + for ( S32 i = 0; i < mesh->primitives.size(); i++ ) + { + const TSDrawPrimitive& draw = mesh->primitives[i]; + if ( (draw.matIndex & TSDrawPrimitive::TypeMask) == TSDrawPrimitive::Triangles ) + { + mIndices.merge( &mesh->indices[draw.start], draw.numElements ); + } + else + { + U32 idx0 = mesh->indices[draw.start + 0]; + U32 idx1; + U32 idx2 = mesh->indices[draw.start + 1]; + U32 *nextIdx = &idx1; + for ( S32 j = 2; j < draw.numElements; j++ ) + { + *nextIdx = idx2; + nextIdx = (U32*) ( (dsize_t)nextIdx ^ (dsize_t)&idx0 ^ (dsize_t)&idx1); + idx2 = mesh->indices[draw.start + j]; + if ( idx0 == idx1 || idx0 == idx2 || idx1 == idx2 ) + continue; + + mIndices.push_back( idx0 ); + mIndices.push_back( idx1 ); + mIndices.push_back( idx2 ); + } + } + } + + // Offset indices for already added verts + for ( S32 j = indicesBase; j < mIndices.size(); j++ ) + mIndices[j] += mVerts.size(); + + // Add verts + S32 count, stride; + U8* pVert; + + if ( mesh->mVertexData.isReady() ) + { + count = mesh->mVertexData.size(); + stride = mesh->mVertexData.vertSize(); + pVert = (U8*)mesh->mVertexData.address(); + } + else + { + count = mesh->verts.size(); + stride = sizeof(Point3F); + pVert = (U8*)mesh->verts.address(); + } + + MatrixF objMat; + mShape->getNodeWorldTransform( obj.nodeIndex, &objMat ); + + mVerts.reserve( mVerts.size() + count ); + for ( S32 j = 0; j < count; j++, pVert += stride ) + { + mVerts.increment(); + objMat.mulP( *(Point3F*)pVert, &mVerts.last() ); + } +} + +TSMesh* MeshFit::initMeshFromFile( const String& filename ) const +{ + // Open the source shape file and make a copy of the mesh + Resource hShape = ResourceManager::get().load(filename); + if (!bool(hShape) || !((TSShape*)hShape)->meshes.size()) + { + Con::errorf("TSShape::createMesh: Could not load source mesh from %s", filename.c_str()); + return NULL; + } + + TSMesh* srcMesh = ((TSShape*)hShape)->meshes[0]; + return mShape->copyMesh( srcMesh ); +} + +TSMesh* MeshFit::createTriMesh( F32* verts, S32 numVerts, U32* indices, S32 numTris ) const +{ + TSMesh* mesh = mShape->copyMesh( NULL ); + mesh->numFrames = 1; + mesh->numMatFrames = 1; + mesh->vertsPerFrame = numVerts; + mesh->setFlags(0); + mesh->mHasColor = false; + mesh->mHasTVert2 = false; + mesh->mNumVerts = numVerts; + + mesh->indices.reserve( numTris * 3 ); + for ( S32 i = 0; i < numTris; i++ ) + { + mesh->indices.push_back( indices[i*3 + 0] ); + mesh->indices.push_back( indices[i*3 + 2] ); + mesh->indices.push_back( indices[i*3 + 1] ); + } + + mesh->verts.set( verts, numVerts ); + + // Compute mesh normals + mesh->norms.setSize( mesh->verts.size() ); + for (S32 iNorm = 0; iNorm < mesh->norms.size(); iNorm++) + mesh->norms[iNorm] = Point3F::Zero; + + // Sum triangle normals for each vertex + for (S32 iInd = 0; iInd < mesh->indices.size(); iInd += 3) + { + // Compute the normal for this triangle + S32 idx0 = mesh->indices[iInd + 0]; + S32 idx1 = mesh->indices[iInd + 1]; + S32 idx2 = mesh->indices[iInd + 2]; + + const Point3F& v0 = mesh->verts[idx0]; + const Point3F& v1 = mesh->verts[idx1]; + const Point3F& v2 = mesh->verts[idx2]; + + Point3F n; + mCross(v2 - v0, v1 - v0, &n); + n.normalize(); // remove this to use 'weighted' normals (large triangles will have more effect) + + mesh->norms[idx0] += n; + mesh->norms[idx1] += n; + mesh->norms[idx2] += n; + } + + // Normalize the vertex normals (this takes care of averaging the triangle normals) + for (S32 iNorm = 0; iNorm < mesh->norms.size(); iNorm++) + mesh->norms[iNorm].normalize(); + + // Set some dummy UVs + mesh->tverts.setSize( numVerts ); + for ( S32 j = 0; j < mesh->tverts.size(); j++ ) + mesh->tverts[j].set( 0, 0 ); + + // Add a single triangle-list primitive + mesh->primitives.increment(); + mesh->primitives.last().start = 0; + mesh->primitives.last().numElements = mesh->indices.size(); + mesh->primitives.last().matIndex = TSDrawPrimitive::Triangles | + TSDrawPrimitive::Indexed | + TSDrawPrimitive::NoMaterial; + + mesh->createTangents( mesh->verts, mesh->norms ); + mesh->encodedNorms.set( NULL,0 ); + + return mesh; +} + +F32 MeshFit::maxDot( const VectorF& v ) const +{ + F32 maxDot = -FLT_MAX; + for ( S32 i = 0; i < mVerts.size(); i++ ) + maxDot = getMax( maxDot, mDot( v, mVerts[i] ) ); + return maxDot; +} + +//--------------------------- +// Best-fit oriented bounding box +void MeshFit::addBox( const Point3F& sides, const MatrixF& mat ) +{ + TSMesh* mesh = initMeshFromFile( "core/art/shapes/unit_cube.dts" ); + if ( !mesh ) + return; + + for ( S32 i = 0; i < mesh->mVertexData.size(); i++ ) + { + Point3F v = mesh->mVertexData[i].vert(); + v.convolve( sides ); + mesh->mVertexData[i].vert( v ); + } + mesh->computeBounds(); + + mMeshes.increment(); + mMeshes.last().type = MeshFit::Box; + mMeshes.last().transform = mat; + mMeshes.last().tsmesh = mesh; +} + +void MeshFit::fitOBB() +{ + PrimFit primFitter; + primFitter.fitBox( mVerts.size(), (F32*)mVerts.address() ); + addBox( primFitter.mBoxSides, primFitter.mBoxTransform ); +} + +//--------------------------- +// Best-fit sphere +void MeshFit::addSphere( F32 radius, const Point3F& center ) +{ + TSMesh* mesh = initMeshFromFile( "core/art/shapes/unit_sphere.dts" ); + if ( !mesh ) + return; + + for ( S32 i = 0; i < mesh->mVertexData.size(); i++ ) + { + Point3F v = mesh->mVertexData[i].vert(); + mesh->mVertexData[i].vert( v * radius ); + } + mesh->computeBounds(); + + mMeshes.increment(); + mMeshes.last().type = MeshFit::Sphere; + mMeshes.last().transform.identity(); + mMeshes.last().transform.setPosition( center ); + mMeshes.last().tsmesh = mesh; +} + +void MeshFit::fitSphere() +{ + PrimFit primFitter; + primFitter.fitSphere( mVerts.size(), (F32*)mVerts.address() ); + addSphere( primFitter.mSphereRadius, primFitter.mSphereCenter ); +} + +//--------------------------- +// Best-fit capsule +void MeshFit::addCapsule( F32 radius, F32 height, const MatrixF& mat ) +{ + TSMesh* mesh = initMeshFromFile( "core/art/shapes/unit_capsule.dts" ); + if ( !mesh ) + return; + + // Translate and scale the mesh verts + height = mMax( 0, height ); + F32 offset = ( height / ( 2 * radius ) ) - 0.5f; + for ( S32 i = 0; i < mesh->mVertexData.size(); i++ ) + { + Point3F v = mesh->mVertexData[i].vert(); + v.y += ( ( v.y > 0 ) ? offset : -offset ); + mesh->mVertexData[i].vert( v * radius ); + } + mesh->computeBounds(); + + mMeshes.increment(); + mMeshes.last().type = MeshFit::Capsule; + mMeshes.last().transform = mat; + mMeshes.last().tsmesh = mesh; +} + +void MeshFit::fitCapsule() +{ + PrimFit primFitter; + primFitter.fitCapsule( mVerts.size(), (F32*)mVerts.address() ); + addCapsule( primFitter.mCapRadius, primFitter.mCapHeight, primFitter.mCapTransform ); +} + +//--------------------------- +// Best-fit k-discrete-oriented-polytope (where k is the number of axis-aligned planes) + +// All faces + 4 edges (aligned to X axis) of the unit cube +void MeshFit::fit10_DOP_X() +{ + Vector planes; + planes.setSize( 10 ); + dCopyArray( planes.address(), sFacePlanes, 6 ); + dCopyArray( planes.address()+6, sXEdgePlanes, 4 ); + fitK_DOP( planes ); +} + +// All faces + 4 edges (aligned to Y axis) of the unit cube +void MeshFit::fit10_DOP_Y() +{ + Vector planes; + planes.setSize( 10 ); + dCopyArray( planes.address(), sFacePlanes, 6 ); + dCopyArray( planes.address()+6, sYEdgePlanes, 4 ); + fitK_DOP( planes ); +} + +// All faces + 4 edges (aligned to Z axis) of the unit cube +void MeshFit::fit10_DOP_Z() +{ + Vector planes; + planes.setSize( 10 ); + dCopyArray( planes.address(), sFacePlanes, 6 ); + dCopyArray( planes.address()+6, sZEdgePlanes, 4 ); + fitK_DOP( planes ); +} + +// All faces and edges of the unit cube +void MeshFit::fit18_DOP() +{ + Vector planes; + planes.setSize( 18 ); + dCopyArray( planes.address(), sFacePlanes, 6 ); + dCopyArray( planes.address()+6, sXEdgePlanes, 4 ); + dCopyArray( planes.address()+10, sYEdgePlanes, 4 ); + dCopyArray( planes.address()+14, sZEdgePlanes, 4 ); + fitK_DOP( planes ); +} + +// All faces, edges and corners of the unit cube +void MeshFit::fit26_DOP() +{ + Vector planes; + planes.setSize( 26 ); + dCopyArray( planes.address(), sFacePlanes, 6 ); + dCopyArray( planes.address()+6, sXEdgePlanes, 4 ); + dCopyArray( planes.address()+10, sYEdgePlanes, 4 ); + dCopyArray( planes.address()+14, sZEdgePlanes, 4 ); + dCopyArray( planes.address()+18, sCornerPlanes, 8 ); + fitK_DOP( planes ); +} + +void MeshFit::fitK_DOP( const Vector& planes ) +{ + // Push the planes up against the mesh + Vector planeDs; + for ( S32 i = 0; i < planes.size(); i++ ) + planeDs.push_back( maxDot( planes[i] ) ); + + // Collect the intersection points of any 3 planes that lie inside + // the maximum distances found above + Vector points; + for ( S32 i = 0; i < planes.size()-2; i++ ) + { + for ( S32 j = i+1; j < planes.size()-1; j++ ) + { + for ( S32 k = j+1; k < planes.size(); k++ ) + { + Point3F v23 = mCross( planes[j], planes[k] ); + F32 denom = mDot( planes[i], v23 ); + if ( denom == 0 ) + continue; + + Point3F v31 = mCross( planes[k], planes[i] ); + Point3F v12 = mCross( planes[i], planes[j] ); + Point3F p = ( planeDs[i]*v23 + planeDs[j]*v31 + planeDs[k]*v12 ) / denom; + + // Ignore intersection points outside the volume + // described by the planes + bool addPoint = true; + for ( S32 n = 0; n < planes.size(); n++ ) + { + if ( ( mDot( p, planes[n] ) - planeDs[n] ) > 0.005f ) + { + addPoint = false; + break; + } + } + + if ( addPoint ) + points.push_back( p ); + } + } + } + + // Create a convex hull from the point set + CONVEX_DECOMPOSITION::HullDesc hd; + hd.mVcount = points.size(); + hd.mVertices = (F32*)points.address(); + hd.mVertexStride = sizeof(Point3F); + hd.mMaxVertices = 64; + hd.mSkinWidth = 0.0f; + + CONVEX_DECOMPOSITION::HullLibrary hl; + CONVEX_DECOMPOSITION::HullResult result; + hl.CreateConvexHull( hd, result ); + + // Create TSMesh from convex hull + mMeshes.increment(); + mMeshes.last().type = MeshFit::Hull; + mMeshes.last().transform.identity(); + mMeshes.last().tsmesh = createTriMesh( result.mOutputVertices, result.mNumOutputVertices, + result.mIndices, result.mNumFaces ); + mMeshes.last().tsmesh->computeBounds(); +} + +//--------------------------- +// Best-fit set of convex hulls +void MeshFit::fitConvexHulls( U32 depth, F32 mergeThreshold, F32 concavityThreshold, U32 maxHullVerts, + F32 boxMaxError, F32 sphereMaxError, F32 capsuleMaxError ) +{ + const F32 SkinWidth = 0.0f; + const F32 SplitThreshold = 2.0f; + + CONVEX_DECOMPOSITION::iConvexDecomposition *ic = CONVEX_DECOMPOSITION::createConvexDecomposition(); + + for ( S32 i = 0; i < mIndices.size(); i += 3 ) + { + ic->addTriangle( (F32*)mVerts[mIndices[i]], + (F32*)mVerts[mIndices[i+1]], + (F32*)mVerts[mIndices[i+2]] ); + } + + ic->computeConvexDecomposition( + SkinWidth, + depth, + maxHullVerts, + concavityThreshold, + mergeThreshold, + SplitThreshold, + true, + false, + false ); + + // Add a TSMesh for each hull + for ( S32 i = 0; i < ic->getHullCount(); i++ ) + { + CONVEX_DECOMPOSITION::ConvexHullResult result; + ic->getConvexHullResult( i, result ); + + eMeshType meshType = MeshFit::Hull; + + // Check if we can use a box, sphere or capsule primitive for this hull + if (( boxMaxError > 0 ) || ( sphereMaxError > 0 ) || ( capsuleMaxError > 0 )) + { + // Compute error between actual mesh and fitted primitives + F32 meshVolume = CONVEX_DECOMPOSITION::fm_computeMeshVolume( result.mVertices, result.mTcount, result.mIndices ); + PrimFit primFitter; + + F32 boxError = 100.0f, sphereError = 100.0f, capsuleError = 100.0f; + if ( boxMaxError > 0 ) + { + primFitter.fitBox( result.mVcount, result.mVertices ); + boxError = 100.0f * ( 1.0f - ( meshVolume / primFitter.getBoxVolume() ) ); + } + if ( sphereMaxError > 0 ) + { + primFitter.fitSphere( result.mVcount, result.mVertices ); + sphereError = 100.0f * ( 1.0f - ( meshVolume / primFitter.getSphereVolume() ) ); + } + if ( capsuleMaxError > 0 ) + { + primFitter.fitCapsule( result.mVcount, result.mVertices ); + capsuleError = 100.0f * ( 1.0f - ( meshVolume / primFitter.getCapsuleVolume() ) ); + } + + // Use the primitive type with smallest error less than the respective + // max error, or Hull if none + F32 minError = FLT_MAX; + if ( ( boxError < boxMaxError ) && ( boxError < minError ) ) + { + meshType = MeshFit::Box; + minError = boxError; + } + if ( ( sphereError < sphereMaxError ) && ( sphereError < minError ) ) + { + meshType = MeshFit::Sphere; + minError = sphereError; + } + if ( ( capsuleError < capsuleMaxError ) && ( capsuleError < minError ) ) + { + meshType = MeshFit::Capsule; + minError = capsuleError; + } + + if ( meshType == MeshFit::Box ) + addBox( primFitter.mBoxSides, primFitter.mBoxTransform ); + else if ( meshType == MeshFit::Sphere ) + addSphere( primFitter.mSphereRadius, primFitter.mSphereCenter ); + else if ( meshType == MeshFit::Capsule ) + addCapsule( primFitter.mCapRadius, primFitter.mCapHeight, primFitter.mCapTransform ); + // else fall through to Hull processing + } + + if ( meshType == MeshFit::Hull ) + { + // Create TSMesh from convex hull + mMeshes.increment(); + mMeshes.last().type = MeshFit::Hull; + mMeshes.last().transform.identity(); + mMeshes.last().tsmesh = createTriMesh( result.mVertices, result.mVcount, result.mIndices, result.mTcount ); + mMeshes.last().tsmesh->computeBounds(); + } + } + + CONVEX_DECOMPOSITION::releaseConvexDecomposition( ic ); +} + +//----------------------------------------------------------------------------- +DefineTSShapeConstructorMethod( addPrimitive, bool, ( const char* meshName, const char* type, const char* params, TransformF txfm, const char* nodeName ),, + ( meshName, type, params, txfm, nodeName ), false, + "Add a new mesh primitive to the shape.\n" + "@param meshName full name (object name + detail size) of the new mesh. If " + "no detail size is present at the end of the name, a value of 2 is used.
      " + "An underscore before the number at the end of the name will be interpreted as " + "a negative sign. eg. \"MyMesh_4\" will be interpreted as \"MyMesh-4\".\n" + "@param type one of: \"box\", \"sphere\", \"capsule\"\n" + "@param params mesh primitive parameters:\n" + "

        " + "
      • for box: \"size_x size_y size_z\"
      • " + "
      • for sphere: \"radius\"
      • " + "
      • for capsule: \"height radius\"
      • " + "
      " + "\n" + "@param txfm local transform offset from the node for this mesh\n" + "@param nodeName name of the node to attach the new mesh to (will change the " + "object's node if adding a new mesh to an existing object)\n" + "@return true if successful, false otherwise\n\n" + "@tsexample\n" + "%this.addMesh( \"Box4\", \"box\", \"2 4 2\", \"0 2 0 0 0 1 0\", \"eye\" );\n" + "%this.addMesh( \"Sphere256\", \"sphere\", \"2\", \"0 0 0 0 0 1 0\", \"root\" );\n" + "%this.addMesh( \"MyCapsule-1\", \"capsule\", \"2 5\", \"0 0 2 0 0 1 0\", \"base01\" );\n" + "@endtsexample\n" ) +{ + MeshFit fit( mShape ); + if ( !dStricmp( type, "box" ) ) + { + // Parse box parameters + Point3F sides; + if ( dSscanf( params, "%g %g %g", &sides.x, &sides.y, &sides.z ) == 3 ) + { + fit.addBox( sides, MatrixF::Identity ); + fit.setReady(); + } + } + else if ( !dStricmp( type, "sphere" ) ) + { + // Parse sphere parameters + F32 radius; + if ( dSscanf( params, "%g", &radius ) == 1) + { + fit.addSphere( radius, Point3F::Zero ); + fit.setReady(); + } + } + else if ( !dStricmp( type, "capsule" ) ) + { + // Parse capsule parameters + F32 radius, height; + if ( dSscanf( params, "%g %g", &radius, &height ) == 1) + { + fit.addCapsule( radius, height, MatrixF::Identity ); + fit.setReady(); + } + } + + if ( !fit.isReady() ) + { + Con::errorf( "TSShapeConstructor::addPrimitive: Invalid params: '%s' for type '%s'", + params, type ); + return false; + } + + TSMesh* mesh = fit.getMesh( 0 )->tsmesh; + MatrixF mat( txfm.getMatrix() ); + + // Transform the mesh vertices + if ( mesh->mVertexData.isReady() ) + { + for (S32 i = 0; i < mesh->mVertexData.size(); i++) + { + Point3F v; + mat.mulP( mesh->mVertexData[i].vert(), &v ); + mesh->mVertexData[i].vert( v ); + } + } + else + { + for (S32 i = 0; i < mesh->verts.size(); i++) + { + Point3F v(mesh->verts[i]); + mat.mulP( v, &mesh->verts[i] ); + } + } + + // Add the mesh to the shape at the right node + mShape->addMesh( mesh, meshName ); + + S32 dummy; + String objName = String::GetTrailingNumber( meshName, dummy ); + setObjectNode( objName, nodeName ); + + mShape->init(); + + ADD_TO_CHANGE_SET(); + return true; +}} + +DefineTSShapeConstructorMethod( addCollisionDetail, bool, ( S32 size, const char* type, const char* target, S32 depth, F32 merge, F32 concavity, S32 maxVerts, F32 boxMaxError, F32 sphereMaxError, F32 capsuleMaxError ), ( 4, 30, 30, 32, 0, 0, 0 ), + ( size, type, target, depth, merge, concavity, maxVerts, boxMaxError, sphereMaxError, capsuleMaxError ), false, + "Autofit a mesh primitive or set of convex hulls to the shape geometry. Hulls " + "may optionally be converted to boxes, spheres and/or capsules based on their " + "volume.\n" + "@param size size for this detail level\n" + "@param type one of: box, sphere, capsule, 10-dop x, 10-dop y, 10-dop z, 18-dop, " + "26-dop, convex hulls. See the Shape Editor documentation for more details " + "about these types.\n" + "@param target geometry to fit collision mesh(es) to; either \"bounds\" (for the " + "whole shape), or the name of an object in the shape\n" + "@param depth maximum split recursion depth (hulls only)\n" + "@param merge volume % threshold used to merge hulls together (hulls only)\n" + "@param concavity volume % threshold used to detect concavity (hulls only)\n" + "@param maxVerts maximum number of vertices per hull (hulls only)\n" + "@param boxMaxError max % volume difference for a hull to be converted to a " + "box (hulls only)\n" + "@param sphereMaxError max % volume difference for a hull to be converted to " + "a sphere (hulls only)\n" + "@param capsuleMaxError max % volume difference for a hull to be converted to " + "a capsule (hulls only)\n" + "@return true if successful, false otherwise\n\n" + "@tsexample\n" + "%this.addCollisionDetail( -1, \"box\", \"bounds\" );\n" + "%this.addCollisionDetail( -1, \"convex hulls\", \"bounds\", 4, 30, 30, 32, 0, 0, 0 );\n" + "%this.addCollisionDetail( -1, \"convex hulls\", \"bounds\", 4, 30, 30, 32, 50, 50, 50 );\n" + "@endtsexample\n" ) +{ + MeshFit fit( mShape ); + fit.initSourceGeometry( target ); + if ( !fit.isReady() ) + { + Con::errorf( "TSShapeConstructor::addCollisionDetail: Failed to initialise mesh fitter " + "using target: %s", target ); + return false; + } + + if ( !dStricmp( type, "box" ) ) + fit.fitOBB(); + else if ( !dStricmp( type, "sphere" ) ) + fit.fitSphere(); + else if ( !dStricmp( type, "capsule" ) ) + fit.fitCapsule(); + else if ( !dStricmp( type, "10-dop x" ) ) + fit.fit10_DOP_X(); + else if ( !dStricmp( type, "10-dop y" ) ) + fit.fit10_DOP_Y(); + else if ( !dStricmp( type, "10-dop z" ) ) + fit.fit10_DOP_Z(); + else if ( !dStricmp( type, "18-dop" ) ) + fit.fit18_DOP(); + else if ( !dStricmp( type, "26-dop" ) ) + fit.fit26_DOP(); + else if ( !dStricmp( type, "convex hulls" ) ) + { + fit.fitConvexHulls( depth, merge, concavity, maxVerts, + boxMaxError, sphereMaxError, capsuleMaxError ); + } + else + { + Con::errorf( "TSShape::addCollisionDetail: Invalid type: '%s'", type ); + return false; + } + + // Now add the fitted meshes to the shape: + // - primitives (box, sphere, capsule) need their own node (with appropriate + // transform set) so that we can use the mesh bounds to compute the real + // collision primitive at load time without having to examine the geometry. + // - convex meshes may be added at the default node, with identity transform + // - since all meshes are in the same detail level, they all get a unique + // object name + + const String colNodeName( String::ToString( "Col%d", size ) ); + + // Add the default node with identity transform + S32 nodeIndex = mShape->findNode( colNodeName ); + if ( nodeIndex == -1 ) + { + addNode( colNodeName, "" ); + } + else + { + MatrixF mat; + mShape->getNodeWorldTransform( nodeIndex, &mat ); + if ( !mat.isIdentity() ) + setNodeTransform( colNodeName, TransformF::Identity ); + } + + // Add the meshes to the shape => + for ( S32 i = 0; i < fit.getMeshCount(); i++ ) + { + MeshFit::Mesh* mesh = fit.getMesh( i ); + + // Determine a unique name for this mesh + String objName; + switch ( mesh->type ) + { + case MeshFit::Box: objName = "ColBox"; break; + case MeshFit::Sphere: objName = "ColSphere"; break; + case MeshFit::Capsule: objName = "ColCapsule"; break; + default: objName = "ColConvex"; break; + } + + for ( S32 suffix = i; suffix != 0; suffix /= 26 ) + objName += ('A' + ( suffix % 26 ) ); + String meshName = objName + String::ToString( "%d", size ); + + mShape->addMesh( mesh->tsmesh, meshName ); + + // Add a node for this object if needed (non-identity transform) + if ( mesh->transform.isIdentity() ) + { + mShape->setObjectNode( objName, colNodeName ); + } + else + { + addNode( meshName, colNodeName, TransformF( mesh->transform ) ); + mShape->setObjectNode( objName, meshName ); + } + } + + mShape->init(); + + ADD_TO_CHANGE_SET(); + return true; +}} diff --git a/Engine/source/ts/tsMeshIntrinsics.cpp b/Engine/source/ts/tsMeshIntrinsics.cpp new file mode 100644 index 000000000..fbc93fbe8 --- /dev/null +++ b/Engine/source/ts/tsMeshIntrinsics.cpp @@ -0,0 +1,121 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#include "ts/tsMesh.h" +#include "ts/tsMeshIntrinsics.h" +#include "ts/arch/tsMeshIntrinsics.arch.h" +#include "core/module.h" + + +void (*zero_vert_normal_bulk)(const dsize_t count, U8 * __restrict const outPtr, const dsize_t outStride) = NULL; +void (*m_matF_x_BatchedVertWeightList)(const MatrixF &mat, const dsize_t count, const TSSkinMesh::BatchData::BatchedVertWeight * __restrict batch, U8 * const __restrict outPtr, const dsize_t outStride) = NULL; + +//------------------------------------------------------------------------------ +// Default C++ Implementations (pretty slow) +//------------------------------------------------------------------------------ + +void zero_vert_normal_bulk_C(const dsize_t count, U8 * __restrict const outPtr, const dsize_t outStride) +{ + register char *outData = reinterpret_cast(outPtr); + + // TODO: Try prefetch w/ ptr de-reference + + for(register int i = 0; i < count; i++) + { + TSMesh::__TSMeshVertexBase *outElem = reinterpret_cast(outData); + outElem->_vert.zero(); + outElem->_normal.zero(); + outData += outStride; + } +} + +//------------------------------------------------------------------------------ + +void m_matF_x_BatchedVertWeightList_C(const MatrixF &mat, + const dsize_t count, + const TSSkinMesh::BatchData::BatchedVertWeight * __restrict batch, + U8 * const __restrict outPtr, + const dsize_t outStride) +{ + const register MatrixF m = mat; + + register Point3F tempPt; + register Point3F tempNrm; + + for(register int i = 0; i < count; i++) + { + const TSSkinMesh::BatchData::BatchedVertWeight &inElem = batch[i]; + + TSMesh::__TSMeshVertexBase *outElem = reinterpret_cast(outPtr + inElem.vidx * outStride); + + m.mulP( inElem.vert, &tempPt ); + m.mulV( inElem.normal, &tempNrm ); + + outElem->_vert += ( tempPt * inElem.weight ); + outElem->_normal += ( tempNrm * inElem.weight ); + } +} + +//------------------------------------------------------------------------------ +// Initializer. +//------------------------------------------------------------------------------ + +MODULE_BEGIN( TSMeshIntrinsics ) + + MODULE_INIT_AFTER( 3D ) + + MODULE_INIT + { + // Assign defaults (C++ versions) + zero_vert_normal_bulk = zero_vert_normal_bulk_C; + m_matF_x_BatchedVertWeightList = m_matF_x_BatchedVertWeightList_C; + + #if defined(TORQUE_OS_XENON) + zero_vert_normal_bulk = zero_vert_normal_bulk_X360; + m_matF_x_BatchedVertWeightList = m_matF_x_BatchedVertWeightList_X360; + #else + // Find the best implementation for the current CPU + if(Platform::SystemInfo.processor.properties & CPU_PROP_SSE) + { + #if defined(TORQUE_CPU_X86) + + zero_vert_normal_bulk = zero_vert_normal_bulk_SSE; + m_matF_x_BatchedVertWeightList = m_matF_x_BatchedVertWeightList_SSE; + + /* This code still has a bug left in it + #if (_MSC_VER >= 1500) + if(Platform::SystemInfo.processor.properties & CPU_PROP_SSE4_1) + m_matF_x_BatchedVertWeightList = m_matF_x_BatchedVertWeightList_SSE4; + #endif + */ + #endif + } + else if(Platform::SystemInfo.processor.properties & CPU_PROP_ALTIVEC) + { + #if !defined(TORQUE_OS_XENON) && defined(TORQUE_CPU_PPC) + zero_vert_normal_bulk = zero_vert_normal_bulk_gccvec; + m_matF_x_BatchedVertWeightList = m_matF_x_BatchedVertWeightList_gccvec; + #endif + } + #endif + } + +MODULE_END; diff --git a/Engine/source/ts/tsMeshIntrinsics.h b/Engine/source/ts/tsMeshIntrinsics.h new file mode 100644 index 000000000..5bf7e61d4 --- /dev/null +++ b/Engine/source/ts/tsMeshIntrinsics.h @@ -0,0 +1,51 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TSMESHINTRINSICS_H_ +#define _TSMESHINTRINSICS_H_ + +/// This is the batch-by-transform skin loop +/// +/// @param mat Bone transform +/// @param count Number of input elements in the batch +/// @param batch Pointer to the first element in an aligned array of input elements +/// @param outPtr Pointer to index 0 of a TSMesh aligned vertex buffer +/// @param outStride Size, in bytes, of one entry in the vertex buffer +extern void (*m_matF_x_BatchedVertWeightList) + (const MatrixF &mat, + const dsize_t count, + const TSSkinMesh::BatchData::BatchedVertWeight * __restrict batch, + U8 * const __restrict outPtr, + const dsize_t outStride); + +/// Set the vertex position and normal to (0, 0, 0) +/// +/// @param count Number of elements +/// @param outPtr Pointer to a TSMesh aligned vertex buffer +/// @param outStride Size, in bytes, of one entry in the vertex buffer +extern void (*zero_vert_normal_bulk) + (const dsize_t count, + U8 * __restrict const outPtr, + const dsize_t outStride); + +#endif + diff --git a/Engine/source/ts/tsPartInstance.cpp b/Engine/source/ts/tsPartInstance.cpp new file mode 100644 index 000000000..32e1a6fe3 --- /dev/null +++ b/Engine/source/ts/tsPartInstance.cpp @@ -0,0 +1,371 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "ts/tsPartInstance.h" +#include "math/mMath.h" + +//------------------------------------------------------------------------------------- +// Constructors +//------------------------------------------------------------------------------------- + +MRandomR250 TSPartInstance::smRandom; + +TSPartInstance::TSPartInstance(TSShapeInstance * sourceShape) +{ + VECTOR_SET_ASSOCIATION(mMeshObjects); + + init(sourceShape); +} + +TSPartInstance::TSPartInstance(TSShapeInstance * sourceShape, S32 objectIndex) +{ + init(sourceShape); + addObject(objectIndex); +} + +void TSPartInstance::init(TSShapeInstance * sourceShape) +{ + mSourceShape = sourceShape; + mSizeCutoffs = NULL; + mPolyCount = NULL; + mNumDetails = 0; + mCurrentObjectDetail = 0; + mCurrentIntraDL = 1.0f; + mData = 0; +} + +TSPartInstance::~TSPartInstance() +{ + delete [] mPolyCount; +} + +//------------------------------------------------------------------------------------- +// Methods for updating PartInstances +//------------------------------------------------------------------------------------- + +void TSPartInstance::addObject(S32 objectIndex) +{ + if (mSourceShape->mMeshObjects[objectIndex].forceHidden || + mSourceShape->mMeshObjects[objectIndex].visible < 0.01f) + // not visible, don't bother + return; + + mMeshObjects.push_back(&mSourceShape->mMeshObjects[objectIndex]); +} + +void TSPartInstance::updateBounds() +{ + // run through meshes and brute force it? + Box3F bounds; + mBounds.minExtents.set( 10E30f, 10E30f, 10E30f); + mBounds.maxExtents.set(-10E30f,-10E30f,-10E30f); + for (S32 i=0; igetMesh(0)) + mMeshObjects[i]->getMesh(0)->computeBounds(mMeshObjects[i]->getTransform(),bounds,mMeshObjects[i]->frame); + mBounds.minExtents.setMin(bounds.minExtents); + mBounds.maxExtents.setMax(bounds.maxExtents); + } + mCenter = mBounds.minExtents + mBounds.maxExtents; + mCenter *= 0.5f; + Point3F r = mBounds.maxExtents-mCenter; + mRadius = mSqrt(mDot(r,r)); +} + +//------------------------------------------------------------------------------------- +// Methods for breaking shapes into pieces +//------------------------------------------------------------------------------------- + +void TSPartInstance::breakShape(TSShapeInstance * shape, S32 subShape, Vector & partList, F32 * probShatter, F32 * probBreak, S32 probDepth) +{ + AssertFatal(subShape>=0 && subShapemShape->subShapeFirstNode.size(),"TSPartInstance::breakShape: subShape out of range."); + + S32 start = shape->mShape->subShapeFirstNode[subShape]; + + TSPartInstance::breakShape(shape, NULL, start, partList, probShatter, probBreak, probDepth); + + // update bounds (and get rid of empty parts) + for (S32 i=0; imMeshObjects.size()) + partList[i]->updateBounds(); + else + { + partList.erase(i); + i--; + } + } +} + +void TSPartInstance::breakShape(TSShapeInstance * shape, TSPartInstance * currentPart, S32 currentNode, Vector & partList, F32 * probShatter, F32 * probBreak, S32 probDepth) +{ + AssertFatal( !probDepth || (probShatter && probBreak),"TSPartInstance::breakShape: probabilities improperly specified."); + + const TSShape::Node * node = &shape->mShape->nodes[currentNode]; + S32 object = node->firstObject; + S32 child = node->firstChild; + + // copy off probabilities and update probability lists for next level + F32 ps = probShatter ? *probShatter : 1.0f; + F32 pb = probBreak ? *probBreak : 1.0f; + if (probDepth>1 && probShatter && probBreak) + { + probShatter++; + probBreak++; + probDepth--; + } + + // what to do...depending on how the die roll, we can: + // a) shatter the shape at this level -- meaning we make a part out of each object on this node and + // we make parts out of all the children (perhaps breaking them up further still) + // b) break the shape off at this level -- meaning we make a part out of the intact piece from here + // on down (again, we might break the result further as we iterate through the nodes...what breaking + // the shape really does is separate this piece from the parent piece). + // c) add this piece to the parent -- meaning all objects on this node are added to the parent, and children + // are also added (but children will be recursively sent through this routine, so if a parent gets option + // (c) and the child option (a) or (b), then the child will be ripped from the parents grasp. Cruel + // people us coders are. + // Note: (a) is the only way that two objects on the same node can be separated...that is why both + // option a and option b are needed. + if (!probShatter || smRandom.randF() < ps) + { + // option a -- shatter the shape at this level + + // iterate through the objects, make part out of each one + while (object>=0) + { + partList.increment(); + partList.last() = new TSPartInstance(shape,object); + object = shape->mShape->objects[object].nextSibling; + } + + // iterate through the child nodes, call ourselves on each one with currentPart = NULL + while (child>=0) + { + TSPartInstance::breakShape(shape,NULL,child,partList,probShatter,probBreak,probDepth); + child = shape->mShape->nodes[child].nextSibling; + } + + return; + } + + if (!probBreak || smRandom.randF() < pb) + // option b -- break the shape off at this level + currentPart = NULL; // fall through to option C + + // option c -- add this piece to the parent + + if (!currentPart) + { + currentPart = new TSPartInstance(shape); + partList.push_back(currentPart); + } + + // iterate through objects, add to currentPart + while (object>=0) + { + currentPart->addObject(object); + object = shape->mShape->objects[object].nextSibling; + } + + // iterate through child nodes, call ourselves on each one with currentPart as is + while (child>=0) + { + TSPartInstance::breakShape(shape,currentPart,child,partList,probShatter,probBreak,probDepth); + child = shape->mShape->nodes[child].nextSibling; + } +} + +//------------------------------------------------------------------------------------- +// render methods -- we use TSShapeInstance code as much as possible +// issues: setupTexturing expects a detail level, we give it an object detail level +//------------------------------------------------------------------------------------- + +void TSPartInstance::render(S32 od, const TSRenderState &rdata) +{ + S32 i; + + // render mesh objects + for (i=0; irender(od,mSourceShape->getMaterialList(),rdata,1.0); +} + +//------------------------------------------------------------------------------------- +// Detail selection +// 2 methods: +// method 1: use source shapes detail levels... +// method 2: pass in our own table... +// In either case, you can compute the pixel size on your own or let open gl do it. +// If you want to use method 2, you have to call setDetailData sometime before selecting detail +//------------------------------------------------------------------------------------- + +void TSPartInstance::setDetailData(F32 * sizeCutoffs, S32 numDetails) +{ + if (mSizeCutoffs == sizeCutoffs && mNumDetails==numDetails) + return; + + mSizeCutoffs = sizeCutoffs; + mNumDetails = numDetails; + delete [] mPolyCount; + mPolyCount = NULL; +} + +/* +void TSPartInstance::selectCurrentDetail(bool ignoreScale) +{ + if (mSizeCutoffs) + { + selectCurrentDetail(mSizeCutoffs,mNumDetails,ignoreScale); + return; + } + + mSourceShape->selectCurrentDetail(ignoreScale); + mCurrentObjectDetail = mSourceShape->getCurrentDetail(); + mCurrentIntraDL = mSourceShape->getCurrentIntraDetail(); +} + +void TSPartInstance::selectCurrentDetail(F32 pixelSize) +{ + if (mSizeCutoffs) + { + selectCurrentDetail(pixelSize,mSizeCutoffs,mNumDetails); + return; + } + + mSourceShape->selectCurrentDetail(pixelSize); + mCurrentObjectDetail = mSourceShape->getCurrentDetail(); + mCurrentIntraDL = mSourceShape->getCurrentIntraDetail(); +} + +void TSPartInstance::selectCurrentDetail(F32 dist, F32 invScale) +{ + if (mSizeCutoffs) + { + const RectI &viewport = GFX->getViewport(); + F32 pixelScale = viewport.extent.x * 1.6f / 640.0f; + F32 pixelSize = GFX->projectRadius(dist*invScale,mSourceShape->getShape()->radius) * pixelScale * TSShapeInstance::smDetailAdjust; + selectCurrentDetail(pixelSize,mSizeCutoffs,mNumDetails); + return; + } + + mSourceShape->selectCurrentDetail(dist, invScale); + mCurrentObjectDetail = mSourceShape->getCurrentDetail(); + mCurrentIntraDL = mSourceShape->getCurrentIntraDetail(); +} + +void TSPartInstance::selectCurrentDetail(F32 * sizeCutoffs, S32 numDetails, bool ignoreScale) +{ + // compute pixel size + Point3F p; + MatrixF toCam = GFX->getWorldMatrix(); + toCam.mulP(mCenter,&p); + F32 dist = mDot(p,p); + F32 scale = 1.0f; + if (!ignoreScale) + { + // any scale? + Point3F x,y,z; + toCam.getRow(0,&x); + toCam.getRow(1,&y); + toCam.getRow(2,&z); + F32 scalex = mDot(x,x); + F32 scaley = mDot(y,y); + F32 scalez = mDot(z,z); + scale = scalex; + if (scaley > scale) + scale = scaley; + if (scalez > scale) + scale = scalez; + } + dist /= scale; + dist = mSqrt(dist); + + const RectI &viewport = GFX->getViewport(); + // JMQMERGE: is this using a hardcoded res/aspect ? (and the code above) + F32 pixelScale = viewport.extent.x * 1.6f / 640.0f; + F32 pixelRadius = GFX->projectRadius(dist,mRadius) * pixelScale * TSShapeInstance::smDetailAdjust; + + selectCurrentDetail(pixelRadius,sizeCutoffs,numDetails); +} + +void TSPartInstance::selectCurrentDetail(F32 pixelSize, F32 * sizeCutoffs, S32 numDetails) +{ + mCurrentObjectDetail = 0; + while (numDetails) + { + if (pixelSize > *sizeCutoffs) + return; + mCurrentObjectDetail++; + numDetails--; + sizeCutoffs++; + } + mCurrentObjectDetail = -1; +} +*/ + +//------------------------------------------------------------------------------------- +// Detail query methods...complicated because there are two ways that detail information +// can be determined...1) using source shape, or 2) using mSizeCutoffs +//------------------------------------------------------------------------------------- + +F32 TSPartInstance::getDetailSize(S32 dl) const +{ + if (dl<0) + return 0; + else if (mSizeCutoffs && dlgetShape()->mSmallestVisibleDL) + return mSourceShape->getShape()->details[dl].size; + else return 0; +} + +S32 TSPartInstance::getPolyCount(S32 dl) +{ + if (!mPolyCount) + computePolyCount(); + + if (dl<0 || dl>=mNumDetails) + return 0; + else + return mPolyCount[dl]; +} + +void TSPartInstance::computePolyCount() +{ + if (!mSizeCutoffs) + mNumDetails = mSourceShape->getShape()->mSmallestVisibleDL+1; + + delete [] mPolyCount; + mPolyCount = new S32[mNumDetails]; + + for (S32 i=0; igetMesh(i)) + mPolyCount[i] += mMeshObjects[j]->getMesh(i)->getNumPolys(); + } + } +} + + diff --git a/Engine/source/ts/tsPartInstance.h b/Engine/source/ts/tsPartInstance.h new file mode 100644 index 000000000..ef523d6a0 --- /dev/null +++ b/Engine/source/ts/tsPartInstance.h @@ -0,0 +1,138 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TSPARTINSTANCE_H_ +#define _TSPARTINSTANCE_H_ + +#ifndef _TSSHAPEINSTANCE_H_ +#include "ts/tsShapeInstance.h" +#endif + +class TSPartInstance +{ + /// TSPartInstance assumes ownership (or shared ownership) of the source shape. This means that the source + /// shape cannot be deleted so long as the part instance is still around. This also means that any change + /// to source shapes's transforms or other animation properties will affect how the part instance displays. + /// It is ok (even expected), however, to have many part instances accessing the same shape. + TSShapeInstance * mSourceShape; + + /// @name Bounding info + /// @{ + + Box3F mBounds; + Point3F mCenter; + F32 mRadius; + /// @} + + /// detail selection uses the table pointed to by this member + /// + /// if this member is blank, then it uses source shape to determine detail... + /// + /// detail 0 draws up until size of shape is less than mSizeCutoffs[0], detail 1 until mSizeCutoffs[1], etc. + F32 * mSizeCutoffs; + S32 * mPolyCount; + S32 mNumDetails; + + /// @name Detail Levels + /// detail levels on part instance correspond directly + /// to object details on objects -- this is different + /// from shape instances where dl corresponds to a + /// subtree number and object detail. The reason + /// for this is that partinstances are derived from + /// a single subtree of a shape instance, so the subtree + /// is implied (implied by which objects are in the part instance)... + /// @{ + S32 mCurrentObjectDetail; + F32 mCurrentIntraDL; + /// @} + + Vector mMeshObjects; + + static MRandomR250 smRandom; + + void addObject(S32 objectIndex); + void updateBounds(); + + void renderDetailMap(S32 od); + void renderEnvironmentMap(S32 od); + void renderFog(S32 od); + + void init(TSShapeInstance *); + + static void breakShape(TSShapeInstance *, TSPartInstance *, S32 currentNode, + Vector & partList, F32 * probShatter, + F32 * probBreak, S32 probDepth); + + /// @name Private Detail Selection Methods + /// @{ + void selectCurrentDetail(F32 * sizeCutoffs, S32 numDetails, bool ignoreScale); + void selectCurrentDetail(F32 pixelSize, F32 * sizeCutoffs, S32 numDetails); + void computePolyCount(); + /// @} + +public: + + TSPartInstance(TSShapeInstance * source); + TSPartInstance(TSShapeInstance * source, S32 objectIndex); + ~TSPartInstance(); + + const TSShape * getShape() { return mSourceShape->getShape(); } + TSShapeInstance * getSourceShapeInstance(){ return mSourceShape; } + + static void breakShape(TSShapeInstance *, S32 subShape, Vector & partList, F32 * probShatter, F32 * probBreak, S32 probDepth); + + Point3F & getCenter() { return mCenter; } + Box3F & getBounds() { return mBounds; } + F32 & getRadius() { return mRadius; } + + void render( const TSRenderState &rdata ) { render( mCurrentObjectDetail, rdata ); } + void render( S32 dl, const TSRenderState &rdata ); + + /// choose detail method -- pass in NULL for first parameter to just use shapes data + void setDetailData(F32 * sizeCutoffs, S32 numDetails); + + /// @name Detail Selection + /// @{ + /* + void selectCurrentDetail(bool ignoreScale = false); + void selectCurrentDetail(F32 pixelSize); + void selectCurrentDetail2(F32 adjustedDist); + */ + /// @} + + /// @name Detail Information Accessors + /// @{ + F32 getDetailSize( S32 dl ) const; + S32 getPolyCount( S32 dl ); + S32 getNumDetails() const { return mSizeCutoffs ? mNumDetails : mSourceShape->getShape()->mSmallestVisibleDL+1; } + + S32 getCurrentObjectDetail() const { return mCurrentObjectDetail; } + void setCurrentObjectDetail(S32 od) { mCurrentObjectDetail = od; } + F32 getCurrentIntraDetail() const { return mCurrentIntraDL; } + void setCurrentIntraDetail(F32 intra) { mCurrentIntraDL = intra; } + /// @} + + void *mData; ///< for use by app +}; + +#endif + diff --git a/Engine/source/ts/tsRenderState.cpp b/Engine/source/ts/tsRenderState.cpp new file mode 100644 index 000000000..fab8f213c --- /dev/null +++ b/Engine/source/ts/tsRenderState.cpp @@ -0,0 +1,51 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "ts/tsRenderState.h" + + +TSRenderState::TSRenderState() + : mState( NULL ), + mCubemap( NULL ), + mFadeOverride( 1.0f ), + mNoRenderTranslucent( false ), + mNoRenderNonTranslucent( false ), + mMaterialHint( NULL ), + mCuller( NULL ), + mLightQuery( NULL ), + mUseOriginSort( false ) +{ +} + +TSRenderState::TSRenderState( const TSRenderState &state ) + : mState( state.mState ), + mCubemap( state.mCubemap ), + mFadeOverride( state.mFadeOverride ), + mNoRenderTranslucent( state.mNoRenderTranslucent ), + mNoRenderNonTranslucent( state.mNoRenderNonTranslucent ), + mMaterialHint( state.mMaterialHint ), + mCuller( state.mCuller ), + mLightQuery( state.mLightQuery ), + mUseOriginSort( state.mUseOriginSort ) +{ +} diff --git a/Engine/source/ts/tsRenderState.h b/Engine/source/ts/tsRenderState.h new file mode 100644 index 000000000..3d74def15 --- /dev/null +++ b/Engine/source/ts/tsRenderState.h @@ -0,0 +1,153 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TSRENDERDATA_H_ +#define _TSRENDERDATA_H_ + +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif + + + +class SceneRenderState; +class GFXCubemap; +class Frustum; +class LightQuery; + + +/// A simple class for passing render state through the pre-render pipeline. +/// +/// @section TSRenderState_intro Introduction +/// +/// TSRenderState holds on to certain pieces of data that may be +/// set at the preparation stage of rendering (prepRengerImage etc.) +/// which are needed further along in the process of submitting +/// a render instance for later rendering by the RenderManager. +/// +/// It was created to clean up and refactor the DTS rendering +/// from having a large number of static data that would be used +/// in varying places. These statics were confusing and would often +/// cause problems when not properly cleaned up by various objects after +/// submitting their RenderInstances. +/// +/// @section TSRenderState_functionality What Does TSRenderState Do? +/// +/// TSRenderState is a simple class that performs the function of passing along +/// (from the prep function(s) to the actual submission) the data +/// needed for the desired state of rendering. +/// +/// @section TSRenderState_example Usage Example +/// +/// TSRenderState is very easy to use. Merely create a TSRenderState object (in prepRenderImage usually) +/// and set any of the desired data members (SceneRenderState, camera transform etc.), and pass the address of +/// your TSRenderState to your render function. +/// +class TSRenderState +{ +protected: + + const SceneRenderState *mState; + + GFXCubemap *mCubemap; + + /// Used to override the normal + /// fade value of an object. + /// This is multiplied by the current + /// fade value of the instance + /// to gain the resulting visibility fade (see TSMesh::render()). + F32 mFadeOverride; + + /// These are used in some places + /// TSShapeInstance::render, however, + /// it appears they are never set to anything + /// other than false. We provide methods + /// for setting them regardless. + bool mNoRenderTranslucent; + bool mNoRenderNonTranslucent; + + /// A generic hint value passed from the game + /// code down to the material for use by shader + /// features. + void *mMaterialHint; + + /// An optional object space frustum used to cull + /// subobjects within the shape. + const Frustum *mCuller; + + /// Use the origin point of the mesh for distance + /// sorting for transparency instead of the nearest + /// bounding box point. + bool mUseOriginSort; + + /// The lighting query object used if any materials + /// are forward lit and need lights. + LightQuery *mLightQuery; + +public: + + TSRenderState(); + TSRenderState( const TSRenderState &state ); + + /// @name Get/Set methods. + /// @{ + + ///@see mState + const SceneRenderState* getSceneState() const { return mState; } + void setSceneState( const SceneRenderState *state ) { mState = state; } + + ///@see mCubemap + GFXCubemap* getCubemap() const { return mCubemap; } + void setCubemap( GFXCubemap *cubemap ) { mCubemap = cubemap; } + + ///@see mFadeOverride + F32 getFadeOverride() const { return mFadeOverride; } + void setFadeOverride( F32 fade ) { mFadeOverride = fade; } + + ///@see mNoRenderTranslucent + bool isNoRenderTranslucent() const { return mNoRenderTranslucent; } + void setNoRenderTranslucent( bool noRenderTrans ) { mNoRenderTranslucent = noRenderTrans; } + + ///@see mNoRenderNonTranslucent + bool isNoRenderNonTranslucent() const { return mNoRenderNonTranslucent; } + void setNoRenderNonTranslucent( bool noRenderNonTrans ) { mNoRenderNonTranslucent = noRenderNonTrans; } + + ///@see mMaterialHint + void* getMaterialHint() const { return mMaterialHint; } + void setMaterialHint( void *materialHint ) { mMaterialHint = materialHint; } + + ///@see mCuller + const Frustum* getCuller() const { return mCuller; } + void setCuller( const Frustum *culler ) { mCuller = culler; } + + ///@see mUseOriginSort + void setOriginSort( bool enable ) { mUseOriginSort = enable; } + bool useOriginSort() const { return mUseOriginSort; } + + ///@see mLightQuery + void setLightQuery( LightQuery *query ) { mLightQuery = query; } + LightQuery* getLightQuery() const { return mLightQuery; } + + /// @} +}; + +#endif // _TSRENDERDATA_H_ diff --git a/Engine/source/ts/tsShape.cpp b/Engine/source/ts/tsShape.cpp new file mode 100644 index 000000000..b7f791bed --- /dev/null +++ b/Engine/source/ts/tsShape.cpp @@ -0,0 +1,2289 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "ts/tsShape.h" + +#include "ts/tsLastDetail.h" +#include "ts/tsMaterialList.h" +#include "core/stringTable.h" +#include "console/console.h" +#include "ts/tsShapeInstance.h" +#include "collision/convex.h" +#include "materials/matInstance.h" +#include "materials/materialManager.h" +#include "math/mathIO.h" +#include "core/util/endian.h" +#include "core/stream/fileStream.h" +#include "console/compiler.h" +#include "core/fileObject.h" + +#ifdef TORQUE_COLLADA +extern TSShape* loadColladaShape(const Torque::Path &path); +#endif + +/// most recent version -- this is the version we write +S32 TSShape::smVersion = 26; +/// the version currently being read...valid only during a read +S32 TSShape::smReadVersion = -1; +const U32 TSShape::smMostRecentExporterVersion = DTS_EXPORTER_CURRENT_VERSION; + +F32 TSShape::smAlphaOutLastDetail = -1.0f; +F32 TSShape::smAlphaInBillboard = 0.15f; +F32 TSShape::smAlphaOutBillboard = 0.15f; +F32 TSShape::smAlphaInDefault = -1.0f; +F32 TSShape::smAlphaOutDefault = -1.0f; + +// don't bother even loading this many of the highest detail levels (but +// always load last renderable detail) +S32 TSShape::smNumSkipLoadDetails = 0; + +bool TSShape::smInitOnRead = true; + + +TSShape::TSShape() +{ + materialList = NULL; + mReadVersion = -1; // -1 means constructed from scratch (e.g., in exporter or no read yet) + mHasSkinMesh = false; + mSequencesConstructed = false; + mShapeData = NULL; + mShapeDataSize = 0; + + mUseDetailFromScreenError = false; + + mDetailLevelLookup.setSize( 1 ); + mDetailLevelLookup[0].set( -1, 0 ); + + VECTOR_SET_ASSOCIATION(sequences); + VECTOR_SET_ASSOCIATION(nodeRotations); + VECTOR_SET_ASSOCIATION(nodeTranslations); + VECTOR_SET_ASSOCIATION(nodeUniformScales); + VECTOR_SET_ASSOCIATION(nodeAlignedScales); + VECTOR_SET_ASSOCIATION(nodeArbitraryScaleRots); + VECTOR_SET_ASSOCIATION(nodeArbitraryScaleFactors); + VECTOR_SET_ASSOCIATION(groundRotations); + VECTOR_SET_ASSOCIATION(groundTranslations); + VECTOR_SET_ASSOCIATION(triggers); + VECTOR_SET_ASSOCIATION(billboardDetails); + VECTOR_SET_ASSOCIATION(detailCollisionAccelerators); + VECTOR_SET_ASSOCIATION(names); + + VECTOR_SET_ASSOCIATION( nodes ); + VECTOR_SET_ASSOCIATION( objects ); + VECTOR_SET_ASSOCIATION( objectStates ); + VECTOR_SET_ASSOCIATION( subShapeFirstNode ); + VECTOR_SET_ASSOCIATION( subShapeFirstObject ); + VECTOR_SET_ASSOCIATION( detailFirstSkin ); + VECTOR_SET_ASSOCIATION( subShapeNumNodes ); + VECTOR_SET_ASSOCIATION( subShapeNumObjects ); + VECTOR_SET_ASSOCIATION( details ); + VECTOR_SET_ASSOCIATION( defaultRotations ); + VECTOR_SET_ASSOCIATION( defaultTranslations ); + + VECTOR_SET_ASSOCIATION( subShapeFirstTranslucentObject ); + VECTOR_SET_ASSOCIATION( meshes ); + + VECTOR_SET_ASSOCIATION( alphaIn ); + VECTOR_SET_ASSOCIATION( alphaOut ); +} + +TSShape::~TSShape() +{ + delete materialList; + + S32 i; + + // everything left over here is a legit mesh + for (i=0; i= mShapeData) && ((S8*)meshes[i] < (mShapeData + mShapeDataSize))) + destructInPlace(meshes[i]); + else + delete meshes[i]; + } + + for (i=0; ivertexList; + delete [] accel->normalList; + for (S32 j = 0; j < accel->numVerts; j++) + delete [] accel->emitStrings[j]; + delete [] accel->emitStrings; + delete accel; + } + } + for (dca = 0; dca < detailCollisionAccelerators.size(); dca++) + detailCollisionAccelerators[dca] = NULL; + + if( mShapeData ) + delete[] mShapeData; +} + +const String& TSShape::getName( S32 nameIndex ) const +{ + AssertFatal(nameIndex>=0 && nameIndex= 0 && seqIndexgetMaterialNameList().size(); + + if(mapToNameIndex < 0 || mapToNameIndex >= targetCount) + return String::EmptyString; + + return materialList->getMaterialNameList()[mapToNameIndex]; +} + +S32 TSShape::getTargetCount() const +{ + if(!this) + return -1; + + return materialList->getMaterialNameList().size(); + +} + +S32 TSShape::findNode(S32 nameIndex) const +{ + for (S32 i=0; i validDetails; + getSubShapeDetails(subShapeIndex, validDetails); + + // Find the detail with the correct size + for (meshIndex = 0; meshIndex < validDetails.size(); meshIndex++) + { + const TSShape::Detail& det = details[validDetails[meshIndex]]; + if (detailSize == det.size) + return true; + } + + return false; +} + +TSMesh* TSShape::findMesh(const String& meshName) +{ + S32 objIndex, meshIndex; + if (!findMeshIndex(meshName, objIndex, meshIndex)) + return 0; + return meshes[objects[objIndex].startMeshIndex + meshIndex]; +} + +S32 TSShape::getSubShapeForNode(S32 nodeIndex) +{ + for (S32 i = 0; i < subShapeFirstNode.size(); i++) + { + S32 start = subShapeFirstNode[i]; + S32 end = start + subShapeNumNodes[i]; + if ((nodeIndex >= start) && (nodeIndex < end)) + return i;; + } + return -1; +} + +S32 TSShape::getSubShapeForObject(S32 objIndex) +{ + for (S32 i = 0; i < subShapeFirstObject.size(); i++) + { + S32 start = subShapeFirstObject[i]; + S32 end = start + subShapeNumObjects[i]; + if ((objIndex >= start) && (objIndex < end)) + return i; + } + return -1; +} + +void TSShape::getSubShapeDetails(S32 subShapeIndex, Vector& validDetails) +{ + validDetails.clear(); + for (S32 i = 0; i < details.size(); i++) + { + if ((details[i].subShapeNum == subShapeIndex) || + (details[i].subShapeNum < 0)) + validDetails.push_back(i); + } +} + +void TSShape::getNodeWorldTransform(S32 nodeIndex, MatrixF* mat) const +{ + if ( nodeIndex == -1 ) + { + mat->identity(); + } + else + { + // Calculate the world transform of the given node + defaultRotations[nodeIndex].getQuatF().setMatrix(mat); + mat->setPosition(defaultTranslations[nodeIndex]); + + S32 parentIndex = nodes[nodeIndex].parentIndex; + while (parentIndex != -1) + { + MatrixF mat2(*mat); + defaultRotations[parentIndex].getQuatF().setMatrix(mat); + mat->setPosition(defaultTranslations[parentIndex]); + mat->mul(mat2); + + parentIndex = nodes[parentIndex].parentIndex; + } + } +} + +void TSShape::getNodeObjects(S32 nodeIndex, Vector& nodeObjects) +{ + for (S32 i = 0; i < objects.size(); i++) + { + if ((nodeIndex == -1) || (objects[i].nodeIndex == nodeIndex)) + nodeObjects.push_back(i); + } +} + +void TSShape::getNodeChildren(S32 nodeIndex, Vector& nodeChildren) +{ + for (S32 i = 0; i < nodes.size(); i++) + { + if (nodes[i].parentIndex == nodeIndex) + nodeChildren.push_back(i); + } +} + +void TSShape::getObjectDetails(S32 objIndex, Vector& objDetails) +{ + // Get the detail levels for this subshape + Vector validDetails; + getSubShapeDetails(getSubShapeForObject(objIndex), validDetails); + + // Get the non-null details for this object + const TSShape::Object& obj = objects[objIndex]; + for (S32 i = 0; i < obj.numMeshes; i++) + { + if (meshes[obj.startMeshIndex + i]) + objDetails.push_back(validDetails[i]); + } +} + +void TSShape::init() +{ + S32 numSubShapes = subShapeFirstNode.size(); + AssertFatal(numSubShapes==subShapeFirstObject.size(),"TSShape::init"); + + S32 i,j; + + // set up parent/child relationships on nodes and objects + for (i=0; i=0) + { + if (nodes[parentIndex].firstChild<0) + nodes[parentIndex].firstChild=i; + else + { + S32 child = nodes[parentIndex].firstChild; + while (nodes[child].nextSibling>=0) + child = nodes[child].nextSibling; + nodes[child].nextSibling = i; + } + } + } + for (i=0; i=0) + { + if (nodes[nodeIndex].firstObject<0) + nodes[nodeIndex].firstObject = i; + else + { + S32 objectIndex = nodes[nodeIndex].firstObject; + while (objects[objectIndex].nextSibling>=0) + objectIndex = objects[objectIndex].nextSibling; + objects[objectIndex].nextSibling = i; + } + } + } + + mFlags = 0; + for (i=0; i=0; i--) + { + if (igetNumPolys() : 0; + } + } + details[i].polyCount = count; + } + + // Init the collision accelerator array. Note that we don't compute the + // accelerators until the app requests them + { + S32 dca; + for (dca = 0; dca < detailCollisionAccelerators.size(); dca++) + { + ConvexHullAccelerator* accel = detailCollisionAccelerators[dca]; + if (accel != NULL) { + delete [] accel->vertexList; + delete [] accel->normalList; + for (S32 j = 0; j < accel->numVerts; j++) + delete [] accel->emitStrings[j]; + delete [] accel->emitStrings; + delete accel; + } + } + + detailCollisionAccelerators.setSize(details.size()); + for (dca = 0; dca < detailCollisionAccelerators.size(); dca++) + detailCollisionAccelerators[dca] = NULL; + } + + initVertexFeatures(); + initMaterialList(); +} + +void TSShape::initVertexFeatures() +{ + bool hasColors = false; + bool hasTexcoord2 = false; + + Vector::iterator iter = meshes.begin(); + for ( ; iter != meshes.end(); iter++ ) + { + TSMesh *mesh = *iter; + if ( mesh && + ( mesh->getMeshType() == TSMesh::StandardMeshType || + mesh->getMeshType() == TSMesh::SkinMeshType ) ) + { + if ( mesh->mVertexData.isReady() ) + { + hasColors |= mesh->mHasColor; + hasTexcoord2 |= mesh->mHasTVert2; + } + else + { + hasColors |= !mesh->colors.empty(); + hasTexcoord2 |= !mesh->tverts2.empty(); + } + } + } + + mVertSize = ( hasTexcoord2 || hasColors ) ? sizeof(TSMesh::__TSMeshVertex_3xUVColor) : sizeof(TSMesh::__TSMeshVertexBase); + mVertexFormat.clear(); + + mVertexFormat.addElement( GFXSemantic::POSITION, GFXDeclType_Float3 ); + mVertexFormat.addElement( GFXSemantic::TANGENTW, GFXDeclType_Float, 3 ); + mVertexFormat.addElement( GFXSemantic::NORMAL, GFXDeclType_Float3 ); + mVertexFormat.addElement( GFXSemantic::TANGENT, GFXDeclType_Float3 ); + + mVertexFormat.addElement( GFXSemantic::TEXCOORD, GFXDeclType_Float2, 0 ); + + if(hasTexcoord2 || hasColors) + { + mVertexFormat.addElement( GFXSemantic::TEXCOORD, GFXDeclType_Float2, 1 ); + mVertexFormat.addElement( GFXSemantic::COLOR, GFXDeclType_Color ); + mVertexFormat.addElement( GFXSemantic::TEXCOORD, GFXDeclType_Float, 2 ); + } + + // Go fix up meshes to include defaults for optional features + // and initialize them if they're not a skin mesh. + iter = meshes.begin(); + for ( ; iter != meshes.end(); iter++ ) + { + TSMesh *mesh = *iter; + if ( !mesh || + ( mesh->getMeshType() != TSMesh::StandardMeshType && + mesh->getMeshType() != TSMesh::SkinMeshType ) ) + continue; + + // Set the flags. + mesh->mVertexFormat = &mVertexFormat; + mesh->mVertSize = mVertSize; + + // Create and fill aligned data structure + mesh->convertToAlignedMeshData(); + + // Init the vertex buffer. + if ( mesh->getMeshType() == TSMesh::StandardMeshType ) + mesh->createVBIB(); + } +} + +void TSShape::setupBillboardDetails( const String &cachePath ) +{ + // set up billboard details -- only do this once, meaning that + // if we add a sequence to the shape we don't redo the billboard + // details... + if ( !billboardDetails.empty() ) + return; + + for ( U32 i=0; i < details.size(); i++ ) + { + const Detail &det = details[i]; + + if ( det.subShapeNum >= 0 ) + continue; // not a billboard detail + + while (billboardDetails.size() <= i ) + billboardDetails.push_back(NULL); + + billboardDetails[i] = new TSLastDetail( this, + cachePath, + det.bbEquatorSteps, + det.bbPolarSteps, + det.bbPolarAngle, + det.bbIncludePoles, + det.bbDetailLevel, + det.bbDimension ); + + billboardDetails[i]->update(); + } +} + +void TSShape::initMaterialList() +{ + S32 numSubShapes = subShapeFirstObject.size(); + #if defined(TORQUE_MAX_LIB) + subShapeFirstTranslucentObject.setSize(numSubShapes); + #endif + + mHasSkinMesh = false; + + S32 i,j,k; + // for each subshape, find the first translucent object + // also, while we're at it, set mHasTranslucency + for (S32 ss = 0; ssgetMeshType() == TSMesh::SkinMeshType; + + for (k=0; kprimitives.size(); k++) + { + if (mesh->primitives[k].matIndex & TSDrawPrimitive::NoMaterial) + continue; + S32 flags = materialList->getFlags(mesh->primitives[k].matIndex & TSDrawPrimitive::MaterialMask); + if (flags & TSMaterialList::AuxiliaryMap) + continue; + if (flags & TSMaterialList::Translucent) + { + mFlags |= HasTranslucency; + subShapeFirstTranslucentObject[ss] = i; + break; + } + } + if (k!=mesh->primitives.size()) + break; + } + if (j!=obj.numMeshes) + break; + } + if (i!=end) + break; + } + +} + +bool TSShape::preloadMaterialList(const Torque::Path &path) +{ + if (materialList) + materialList->setTextureLookupPath(path.getPath()); + return true; +} + +bool TSShape::buildConvexHull(S32 dl) const +{ + AssertFatal(dl>=0 && dlbuildConvexHull(); + } + return ok; +} + +Vector gTempNodeTransforms(__FILE__, __LINE__); + +void TSShape::computeBounds(S32 dl, Box3F & bounds) const +{ + // if dl==-1, nothing to do + if (dl==-1) + return; + + AssertFatal(dl>=0 && dlsubShapeNum; + S32 od = detail->objectDetailNum; + + // If we have no subshapes then there is + // no valid bounds for this detail level. + if ( ss < 0 ) + return; + + // set up temporary storage for non-local transforms... + S32 i; + S32 start = subShapeFirstNode[ss]; + S32 end = subShapeNumNodes[ss] + start; + gTempNodeTransforms.setSize(end-start); + for (i=start; i=0) + gTempNodeTransforms[i-start].mul(gTempNodeTransforms[nodes[i].parentIndex-start],mat); + else + gTempNodeTransforms[i-start] = mat; + } + + // run through objects and updating bounds as we go + bounds.minExtents.set( 10E30f, 10E30f, 10E30f); + bounds.maxExtents.set(-10E30f,-10E30f,-10E30f); + Box3F box; + start = subShapeFirstObject[ss]; + end = subShapeNumObjects[ss] + start; + for (i=start; inumMeshes ? meshes[object->startMeshIndex+od] : NULL; + if (mesh) + { + static MatrixF idMat(true); + if (object->nodeIndex<0) + mesh->computeBounds(idMat,box); + else + mesh->computeBounds(gTempNodeTransforms[object->nodeIndex-start],box); + bounds.minExtents.setMin(box.minExtents); + bounds.maxExtents.setMax(box.maxExtents); + } + } +} + +TSShapeAlloc TSShape::smTSAlloc; + +#define tsalloc TSShape::smTSAlloc + + +// messy stuff: check to see if we should "skip" meshNum +// this assumes that meshes for a given object are in a row +// skipDL is the lowest detail number we keep (i.e., the # of details we skip) +bool TSShape::checkSkip(S32 meshNum, S32 & curObject, S32 skipDL) +{ + if (skipDL==0) + // easy out... + return false; + + // skip detail level exists on this subShape + S32 skipSS = details[skipDL].subShapeNum; + + if (curObject=start) + { + // we are either from this object, the next object, or a decal + if (meshNum < start + objects[curObject].numMeshes) + { + // this object... + if (subShapeFirstObject[skipSS]>curObject) + // haven't reached this subshape yet + return true; + if (skipSS+1==subShapeFirstObject.size() || curObject23) + numGroundFrames = tsalloc.get32(); + S32 numObjectStates = tsalloc.get32(); + S32 numDecalStates = tsalloc.get32(); + S32 numTriggers = tsalloc.get32(); + S32 numDetails = tsalloc.get32(); + S32 numMeshes = tsalloc.get32(); + S32 numSkins = 0; + if (smReadVersion<23) + // in later versions, skins are kept with other meshes + numSkins = tsalloc.get32(); + S32 numNames = tsalloc.get32(); + + // Note that we are recalculating these values later on for safety. + mSmallestVisibleSize = (F32)tsalloc.get32(); + mSmallestVisibleDL = tsalloc.get32(); + + tsalloc.checkGuard(); + + // get bounds... + tsalloc.get32((S32*)&radius,1); + tsalloc.get32((S32*)&tubeRadius,1); + tsalloc.get32((S32*)¢er,3); + tsalloc.get32((S32*)&bounds,6); + + tsalloc.checkGuard(); + + // copy various vectors... + S32 * ptr32 = tsalloc.copyToShape32(numNodes*5); + nodes.set(ptr32,numNodes); + + tsalloc.checkGuard(); + + ptr32 = tsalloc.copyToShape32(numObjects*6,true); + if (!ptr32) + ptr32 = tsalloc.allocShape32(numSkins*6); // pre v23 shapes store skins and meshes separately...no longer + else + tsalloc.allocShape32(numSkins*6); + objects.set(ptr32,numObjects); + + tsalloc.checkGuard(); + + // DEPRECATED decals + ptr32 = tsalloc.getPointer32(numDecals*5); + + tsalloc.checkGuard(); + + // DEPRECATED ifl materials + ptr32 = tsalloc.copyToShape32(numIflMaterials*5); + + tsalloc.checkGuard(); + + ptr32 = tsalloc.copyToShape32(numSubShapes,true); + subShapeFirstNode.set(ptr32,numSubShapes); + ptr32 = tsalloc.copyToShape32(numSubShapes,true); + subShapeFirstObject.set(ptr32,numSubShapes); + // DEPRECATED subShapeFirstDecal + ptr32 = tsalloc.getPointer32(numSubShapes); + + tsalloc.checkGuard(); + + ptr32 = tsalloc.copyToShape32(numSubShapes); + subShapeNumNodes.set(ptr32,numSubShapes); + ptr32 = tsalloc.copyToShape32(numSubShapes); + subShapeNumObjects.set(ptr32,numSubShapes); + // DEPRECATED subShapeNumDecals + ptr32 = tsalloc.getPointer32(numSubShapes); + + tsalloc.checkGuard(); + + ptr32 = tsalloc.allocShape32(numSubShapes); + subShapeFirstTranslucentObject.set(ptr32,numSubShapes); + + // get default translation and rotation + S16 * ptr16 = tsalloc.allocShape16(0); + for (i=0;i21) + { + // more node sequence data...scale + nodeUniformScales.setSize(numNodeUniformScales); + for (i=0;i23) + { + groundTranslations.setSize(numGroundFrames); + for (i=0;i= 26 ) + { + U32 alignedSize32 = sizeof( Detail ) / 4; + ptr32 = tsalloc.copyToShape32( numDetails * alignedSize32, true ); + details.set( ptr32, numDetails ); + } + else + { + // Previous to version 26 the Detail structure + // only contained the first 7 values... + // + // struct Detail + // { + // S32 nameIndex; + // S32 subShapeNum; + // S32 objectDetailNum; + // F32 size; + // F32 averageError; + // F32 maxError; + // S32 polyCount; + // }; + // + // In the code below we're reading just these 7 values and + // copying them to the new larger structure. + + ptr32 = tsalloc.copyToShape32( numDetails * 7, true ); + + details.setSize( numDetails ); + for ( U32 i = 0; i < details.size(); i++, ptr32 += 7 ) + { + Detail *det = &(details[i]); + + // Clear the struct... we don't want to leave + // garbage in the parts that are unfilled. + U32 alignedSize32 = sizeof( Detail ); + dMemset( det, 0, alignedSize32 ); + + // Copy the old struct values over. + dMemcpy( det, ptr32, 7 * 4 ); + + // If this is an autobillboard then we need to + // fill in the new part of the struct. + if ( det->subShapeNum >= 0 ) + continue; + + S32 lastDetailOpts = det->objectDetailNum; + det->bbEquatorSteps = lastDetailOpts & 0x7F; // bits 0..6 + det->bbPolarSteps = (lastDetailOpts >> 7) & 0x3F; // bits 7..12 + det->bbPolarAngle = 0.5f * M_PI_F * (1.0f/64.0f) * (F32) (( lastDetailOpts >>13 ) & 0x3F); // bits 13..18 + det->bbDetailLevel = (lastDetailOpts >> 19) & 0x0F; // 19..22 + det->bbDimension = (lastDetailOpts >> 23) & 0xFF; // 23..30 + det->bbIncludePoles = (lastDetailOpts & 0x80000000)!=0; // bit 31 + } + } + + // Some DTS exporters (MAX - I'm looking at you!) write garbage into the + // averageError and maxError values which stops LOD from working correctly. + // Try to detect and fix it + for ( U32 i = 0; i < details.size(); i++ ) + { + if ( ( details[i].averageError == 0 ) || ( details[i].averageError > 10000 ) || + ( details[i].maxError == 0 ) || ( details[i].maxError > 10000 ) ) + { + details[i].averageError = details[i].maxError = -1.0f; + } + } + + // We don't trust the value of mSmallestVisibleDL loaded from the dts + // since some legacy meshes seem to have the wrong value. Recalculate it + // now that we have the details loaded. + updateSmallestVisibleDL(); + + S32 skipDL = getMin(mSmallestVisibleDL,smNumSkipLoadDetails); + if (skipDL < 0) + skipDL = 0; + + + tsalloc.checkGuard(); + + // about to read in the meshes...first must allocate some scratch space + S32 scratchSize = getMax(numSkins,numMeshes); + TSMesh::smVertsList.setSize(scratchSize); + TSMesh::smTVertsList.setSize(scratchSize); + + if ( smReadVersion >= 26 ) + { + TSMesh::smTVerts2List.setSize(scratchSize); + TSMesh::smColorsList.setSize(scratchSize); + } + + TSMesh::smNormsList.setSize(scratchSize); + TSMesh::smEncodedNormsList.setSize(scratchSize); + TSMesh::smDataCopied.setSize(scratchSize); + TSSkinMesh::smInitTransformList.setSize(scratchSize); + TSSkinMesh::smVertexIndexList.setSize(scratchSize); + TSSkinMesh::smBoneIndexList.setSize(scratchSize); + TSSkinMesh::smWeightList.setSize(scratchSize); + TSSkinMesh::smNodeIndexList.setSize(scratchSize); + for (i=0; i= 26 ) + { + TSMesh::smTVerts2List[i] = NULL; + TSMesh::smColorsList[i] = NULL; + } + + TSMesh::smNormsList[i]=NULL; + TSMesh::smEncodedNormsList[i]=NULL; + TSMesh::smDataCopied[i]=false; + TSSkinMesh::smInitTransformList[i] = NULL; + TSSkinMesh::smVertexIndexList[i] = NULL; + TSSkinMesh::smBoneIndexList[i] = NULL; + TSSkinMesh::smWeightList[i] = NULL; + TSSkinMesh::smNodeIndexList[i] = NULL; + } + + // read in the meshes (sans skins)...straightforward read one at a time + ptr32 = tsalloc.allocShape32(numMeshes + numSkins*numDetails); // leave room for skins on old shapes + S32 curObject = 0; // for tracking skipped meshes + for (i=0; iverts.address(); + TSMesh::smTVertsList[i] = mesh->tverts.address(); + TSMesh::smNormsList[i] = mesh->norms.address(); + TSMesh::smEncodedNormsList[i] = mesh->encodedNorms.address(); + TSMesh::smDataCopied[i] = !skip; // as long as we didn't skip this mesh, the data should be in shape now + if (meshType==TSMesh::SkinMeshType) + { + TSSkinMesh * skin = (TSSkinMesh*)mesh; + TSMesh::smVertsList[i] = skin->batchData.initialVerts.address(); + TSMesh::smNormsList[i] = skin->batchData.initialNorms.address(); + TSSkinMesh::smInitTransformList[i] = skin->batchData.initialTransforms.address(); + TSSkinMesh::smVertexIndexList[i] = skin->vertexIndex.address(); + TSSkinMesh::smBoneIndexList[i] = skin->boneIndex.address(); + TSSkinMesh::smWeightList[i] = skin->weight.address(); + TSSkinMesh::smNodeIndexList[i] = skin->batchData.nodeIndex.address(); + } + } + } + meshes.set(ptr32,numMeshes); + + tsalloc.checkGuard(); + + // names + char * nameBufferStart = (char*)tsalloc.getPointer8(0); + char * name = nameBufferStart; + S32 nameBufferSize = 0; + names.setSize(numNames); + for (i=0; ibatchData.initialVerts.address(); + TSMesh::smTVertsList[i] = skin->tverts.address(); + TSMesh::smNormsList[i] = skin->batchData.initialNorms.address(); + TSMesh::smEncodedNormsList[i] = skin->encodedNorms.address(); + TSMesh::smDataCopied[i] = !skip; // as long as we didn't skip this mesh, the data should be in shape now + TSSkinMesh::smInitTransformList[i] = skin->batchData.initialTransforms.address(); + TSSkinMesh::smVertexIndexList[i] = skin->vertexIndex.address(); + TSSkinMesh::smBoneIndexList[i] = skin->boneIndex.address(); + TSSkinMesh::smWeightList[i] = skin->weight.address(); + TSSkinMesh::smNodeIndexList[i] = skin->batchData.nodeIndex.address(); + } + } + + tsalloc.checkGuard(); + + // we now have skins in mesh list...add skin objects to object list and patch things up + fixupOldSkins(numMeshes,numSkins,numDetails,detailFirstSkin,detailNumSkins); + } + + // allocate storage space for some arrays (filled in during Shape::init)... + ptr32 = tsalloc.allocShape32(numDetails); + alphaIn.set(ptr32,numDetails); + ptr32 = tsalloc.allocShape32(numDetails); + alphaOut.set(ptr32,numDetails); +} + +void TSShape::disassembleShape() +{ + S32 i; + + // set counts... + S32 numNodes = tsalloc.set32(nodes.size()); + S32 numObjects = tsalloc.set32(objects.size()); + tsalloc.set32(0); // DEPRECATED decals + S32 numSubShapes = tsalloc.set32(subShapeFirstNode.size()); + tsalloc.set32(0); // DEPRECATED ifl materials + S32 numNodeRotations = tsalloc.set32(nodeRotations.size()); + S32 numNodeTranslations = tsalloc.set32(nodeTranslations.size()); + S32 numNodeUniformScales = tsalloc.set32(nodeUniformScales.size()); + S32 numNodeAlignedScales = tsalloc.set32(nodeAlignedScales.size()); + S32 numNodeArbitraryScales = tsalloc.set32(nodeArbitraryScaleFactors.size()); + S32 numGroundFrames = tsalloc.set32(groundTranslations.size()); + S32 numObjectStates = tsalloc.set32(objectStates.size()); + tsalloc.set32(0); // DEPRECATED decals + S32 numTriggers = tsalloc.set32(triggers.size()); + S32 numDetails = tsalloc.set32(details.size()); + S32 numMeshes = tsalloc.set32(meshes.size()); + S32 numNames = tsalloc.set32(names.size()); + tsalloc.set32((S32)mSmallestVisibleSize); + tsalloc.set32(mSmallestVisibleDL); + + tsalloc.setGuard(); + + // get bounds... + tsalloc.copyToBuffer32((S32*)&radius,1); + tsalloc.copyToBuffer32((S32*)&tubeRadius,1); + tsalloc.copyToBuffer32((S32*)¢er,3); + tsalloc.copyToBuffer32((S32*)&bounds,6); + + tsalloc.setGuard(); + + // copy various vectors... + tsalloc.copyToBuffer32((S32*)nodes.address(),numNodes*5); + tsalloc.setGuard(); + tsalloc.copyToBuffer32((S32*)objects.address(),numObjects*6); + tsalloc.setGuard(); + // DEPRECATED: no copy decals + tsalloc.setGuard(); + tsalloc.copyToBuffer32(0,0); // DEPRECATED: ifl materials! + tsalloc.setGuard(); + tsalloc.copyToBuffer32((S32*)subShapeFirstNode.address(),numSubShapes); + tsalloc.copyToBuffer32((S32*)subShapeFirstObject.address(),numSubShapes); + tsalloc.copyToBuffer32(0, numSubShapes); // DEPRECATED: no copy subShapeFirstDecal + tsalloc.setGuard(); + tsalloc.copyToBuffer32((S32*)subShapeNumNodes.address(),numSubShapes); + tsalloc.copyToBuffer32((S32*)subShapeNumObjects.address(),numSubShapes); + tsalloc.copyToBuffer32(0, numSubShapes); // DEPRECATED: no copy subShapeNumDecals + tsalloc.setGuard(); + + // default transforms... + tsalloc.copyToBuffer16((S16*)defaultRotations.address(),numNodes*4); + tsalloc.copyToBuffer32((S32*)defaultTranslations.address(),numNodes*3); + + // animated transforms... + tsalloc.copyToBuffer16((S16*)nodeRotations.address(),numNodeRotations*4); + tsalloc.copyToBuffer32((S32*)nodeTranslations.address(),numNodeTranslations*3); + + tsalloc.setGuard(); + + // ...with scale + tsalloc.copyToBuffer32((S32*)nodeUniformScales.address(),numNodeUniformScales); + tsalloc.copyToBuffer32((S32*)nodeAlignedScales.address(),numNodeAlignedScales*3); + tsalloc.copyToBuffer32((S32*)nodeArbitraryScaleFactors.address(),numNodeArbitraryScales*3); + tsalloc.copyToBuffer16((S16*)nodeArbitraryScaleRots.address(),numNodeArbitraryScales*4); + + tsalloc.setGuard(); + + tsalloc.copyToBuffer32((S32*)groundTranslations.address(),3*numGroundFrames); + tsalloc.copyToBuffer16((S16*)groundRotations.address(),4*numGroundFrames); + + tsalloc.setGuard(); + + // object states.. + tsalloc.copyToBuffer32((S32*)objectStates.address(),numObjectStates*3); + tsalloc.setGuard(); + + // decal states... + // DEPRECATED (numDecalStates = 0) + tsalloc.setGuard(); + + // frame triggers + tsalloc.copyToBuffer32((S32*)triggers.address(),numTriggers*2); + tsalloc.setGuard(); + + // details + if (TSShape::smVersion > 25) + { + U32 alignedSize32 = sizeof( Detail ) / 4; + tsalloc.copyToBuffer32((S32*)details.address(),numDetails * alignedSize32 ); + } + else + { + // Legacy details => no explicit autobillboard parameters + U32 legacyDetailSize32 = 7; // only store the first 7 4-byte values of each detail + for ( S32 i = 0; i < details.size(); i++ ) + tsalloc.copyToBuffer32( (S32*)&details[i], legacyDetailSize32 ); + } + tsalloc.setGuard(); + + // read in the meshes (sans skins)... + bool * isMesh = new bool[numMeshes]; // funny business because decals are pretend meshes (legacy issue) + for (i=0;igetMeshType() != TSMesh::DecalMeshType) ? mesh->getMeshType() : TSMesh::NullMeshType); + if (mesh) + mesh->disassemble(); + } + delete [] isMesh; + tsalloc.setGuard(); + + // names + for (i=0; itverts2.size() || meshes[i]->colors.size()) + return false; + + // Cannot use old format if any primitive has too many triangles + // (ie. cannot fit in a S16) + for (S32 j = 0; j < meshes[i]->primitives.size(); j++) + { + if ((meshes[i]->primitives[j].start + + meshes[i]->primitives[j].numElements) >= (1 << 15)) + { + return false; + } + } + } + + return true; +} + +void TSShape::write(Stream * s, bool saveOldFormat) +{ + S32 currentVersion = smVersion; + if (saveOldFormat) + smVersion = 24; + + // write version + s->write(smVersion | (mExporterVersion<<16)); + + tsalloc.setWrite(); + disassembleShape(); + + S32 * buffer32 = tsalloc.getBuffer32(); + S16 * buffer16 = tsalloc.getBuffer16(); + S8 * buffer8 = tsalloc.getBuffer8(); + + S32 size32 = tsalloc.getBufferSize32(); + S32 size16 = tsalloc.getBufferSize16(); + S32 size8 = tsalloc.getBufferSize8(); + + // convert sizes to dwords... + if (size16 & 1) + size16 += 2; + size16 >>= 1; + if (size8 & 3) + size8 += 4; + size8 >>= 2; + + S32 sizeMemBuffer, start16, start8; + sizeMemBuffer = size32 + size16 + size8; + start16 = size32; + start8 = start16+size16; + + // in dwords -- write will properly endian-flip. + s->write(sizeMemBuffer); + s->write(start16); + s->write(start8); + + // endian-flip the entire write buffers. + fixEndian(buffer32,buffer16,buffer8,size32,size16,size8); + + // now write buffers + s->write(size32*4,buffer32); + s->write(size16*4,buffer16); + s->write(size8 *4,buffer8); + + // write sequences - write will properly endian-flip. + s->write(sequences.size()); + for (S32 i=0; iwrite(*s); + + delete [] buffer32; + delete [] buffer16; + delete [] buffer8; + + smVersion = currentVersion; +} + +//------------------------------------------------- +// read whole shape +//------------------------------------------------- + +bool TSShape::read(Stream * s) +{ + // read version - read handles endian-flip + s->read(&smReadVersion); + mExporterVersion = smReadVersion >> 16; + smReadVersion &= 0xFF; + if (smReadVersion>smVersion) + { + // error -- don't support future versions yet :> + Con::errorf(ConsoleLogEntry::General, + "Error: attempt to load a version %i dts-shape, can currently only load version %i and before.", + smReadVersion,smVersion); + return false; + } + mReadVersion = smReadVersion; + + S32 * memBuffer32; + S16 * memBuffer16; + S8 * memBuffer8; + S32 count32, count16, count8; + if (mReadVersion<19) + { + Con::errorf("... Shape with old version."); + return false; + } + else + { + S32 i; + U32 sizeMemBuffer, startU16, startU8; + + // in dwords. - read handles endian-flip + s->read(&sizeMemBuffer); + s->read(&startU16); + s->read(&startU8); + + if (s->getStatus()!=Stream::Ok) + { + Con::errorf(ConsoleLogEntry::General, "Error: bad shape file."); + return false; + } + + S32 * tmp = new S32[sizeMemBuffer]; + s->read(sizeof(S32)*sizeMemBuffer,(U8*)tmp); + memBuffer32 = tmp; + memBuffer16 = (S16*)(tmp+startU16); + memBuffer8 = (S8*)(tmp+startU8); + + count32 = startU16; + count16 = startU8-startU16; + count8 = sizeMemBuffer-startU8; + + // read sequences + S32 numSequences; + s->read(&numSequences); + sequences.setSize(numSequences); + for (i=0; iread(*s); + } + + // since we read in the buffers, we need to endian-flip their entire contents... + fixEndian(memBuffer32,memBuffer16,memBuffer8,count32,count16,count8); + + tsalloc.setRead(memBuffer32,memBuffer16,memBuffer8,true); + assembleShape(); // determine size of buffer needed + mShapeDataSize = tsalloc.getSize(); + tsalloc.doAlloc(); + mShapeData = tsalloc.getBuffer(); + tsalloc.setRead(memBuffer32,memBuffer16,memBuffer8,false); + assembleShape(); // copy to buffer + AssertFatal(tsalloc.getSize()==mShapeDataSize,"TSShape::read: shape data buffer size mis-calculated"); + + delete [] memBuffer32; + + if (smInitOnRead) + init(); + + //if (names.size() == 3 && dStricmp(names[2], "Box") == 0) + //{ + // Con::errorf("\nnodes.set(dMalloc(%d * sizeof(Node)), %d);", nodes.size(), nodes.size()); + // for (U32 i = 0; i < nodes.size(); i++) + // { + // Node& obj = nodes[i]; + + // Con::errorf(" nodes[%d].nameIndex = %d;", i, obj.nameIndex); + // Con::errorf(" nodes[%d].parentIndex = %d;", i, obj.parentIndex); + // Con::errorf(" nodes[%d].firstObject = %d;", i, obj.firstObject); + // Con::errorf(" nodes[%d].firstChild = %d;", i, obj.firstChild); + // Con::errorf(" nodes[%d].nextSibling = %d;", i, obj.nextSibling); + // } + + // Con::errorf("\nobjects.set(dMalloc(%d * sizeof(Object)), %d);", objects.size(), objects.size()); + // for (U32 i = 0; i < objects.size(); i++) + // { + // Object& obj = objects[i]; + + // Con::errorf(" objects[%d].nameIndex = %d;", i, obj.nameIndex); + // Con::errorf(" objects[%d].numMeshes = %d;", i, obj.numMeshes); + // Con::errorf(" objects[%d].startMeshIndex = %d;", i, obj.startMeshIndex); + // Con::errorf(" objects[%d].nodeIndex = %d;", i, obj.nodeIndex); + // Con::errorf(" objects[%d].nextSibling = %d;", i, obj.nextSibling); + // Con::errorf(" objects[%d].firstDecal = %d;", i, obj.firstDecal); + // } + + // Con::errorf("\nobjectStates.set(dMalloc(%d * sizeof(ObjectState)), %d);", objectStates.size(), objectStates.size()); + // for (U32 i = 0; i < objectStates.size(); i++) + // { + // ObjectState& obj = objectStates[i]; + + // Con::errorf(" objectStates[%d].vis = %g;", i, obj.vis); + // Con::errorf(" objectStates[%d].frameIndex = %d;", i, obj.frameIndex); + // Con::errorf(" objectStates[%d].matFrameIndex = %d;", i, obj.matFrameIndex); + // } + // Con::errorf("\nsubShapeFirstNode.set(dMalloc(%d * sizeof(S32)), %d);", subShapeFirstNode.size(), subShapeFirstNode.size()); + // for (U32 i = 0; i < subShapeFirstNode.size(); i++) + // Con::errorf(" subShapeFirstNode[%d] = %d;", i, subShapeFirstNode[i]); + + // Con::errorf("\nsubShapeFirstObject.set(dMalloc(%d * sizeof(S32)), %d);", subShapeFirstObject.size(), subShapeFirstObject.size()); + // for (U32 i = 0; i < subShapeFirstObject.size(); i++) + // Con::errorf(" subShapeFirstObject[%d] = %d;", i, subShapeFirstObject[i]); + + // //Con::errorf("numDetailFirstSkins = %d", detailFirstSkin.size()); + // Con::errorf("\nsubShapeNumNodes.set(dMalloc(%d * sizeof(S32)), %d);", subShapeNumNodes.size(), subShapeNumNodes.size()); + // for (U32 i = 0; i < subShapeNumNodes.size(); i++) + // Con::errorf(" subShapeNumNodes[%d] = %d;", i, subShapeNumNodes[i]); + + // Con::errorf("\nsubShapeNumObjects.set(dMalloc(%d * sizeof(S32)), %d);", subShapeNumObjects.size(), subShapeNumObjects.size()); + // for (U32 i = 0; i < subShapeNumObjects.size(); i++) + // Con::errorf(" subShapeNumObjects[%d] = %d;", i, subShapeNumObjects[i]); + + // Con::errorf("\ndetails.set(dMalloc(%d * sizeof(Detail)), %d);", details.size(), details.size()); + // for (U32 i = 0; i < details.size(); i++) + // { + // Detail& obj = details[i]; + + // Con::errorf(" details[%d].nameIndex = %d;", i, obj.nameIndex); + // Con::errorf(" details[%d].subShapeNum = %d;", i, obj.subShapeNum); + // Con::errorf(" details[%d].objectDetailNum = %d;", i, obj.objectDetailNum); + // Con::errorf(" details[%d].size = %g;", i, obj.size); + // Con::errorf(" details[%d].averageError = %g;", i, obj.averageError); + // Con::errorf(" details[%d].maxError = %g;", i, obj.maxError); + // Con::errorf(" details[%d].polyCount = %d;", i, obj.polyCount); + // } + + // Con::errorf("\ndefaultRotations.set(dMalloc(%d * sizeof(Quat16)), %d);", defaultRotations.size(), defaultRotations.size()); + // for (U32 i = 0; i < defaultRotations.size(); i++) + // { + // Con::errorf(" defaultRotations[%d].x = %g;", i, defaultRotations[i].x); + // Con::errorf(" defaultRotations[%d].y = %g;", i, defaultRotations[i].y); + // Con::errorf(" defaultRotations[%d].z = %g;", i, defaultRotations[i].z); + // Con::errorf(" defaultRotations[%d].w = %g;", i, defaultRotations[i].w); + // } + + // Con::errorf("\ndefaultTranslations.set(dMalloc(%d * sizeof(Point3F)), %d);", defaultTranslations.size(), defaultTranslations.size()); + // for (U32 i = 0; i < defaultTranslations.size(); i++) + // Con::errorf(" defaultTranslations[%d].set(%g, %g, %g);", i, defaultTranslations[i].x, defaultTranslations[i].y, defaultTranslations[i].z); + + // Con::errorf("\nsubShapeFirstTranslucentObject.set(dMalloc(%d * sizeof(S32)), %d);", subShapeFirstTranslucentObject.size(), subShapeFirstTranslucentObject.size()); + // for (U32 i = 0; i < subShapeFirstTranslucentObject.size(); i++) + // Con::errorf(" subShapeFirstTranslucentObject[%d] = %d;", i, subShapeFirstTranslucentObject[i]); + + // Con::errorf("\nmeshes.set(dMalloc(%d * sizeof(TSMesh)), %d);", meshes.size(), meshes.size()); + // for (U32 i = 0; i < meshes.size(); i++) + // { + // TSMesh* obj = meshes[i]; + + // if (obj) + // { + // Con::errorf(" meshes[%d]->meshType = %d;", i, obj->meshType); + // Con::errorf(" meshes[%d]->mBounds.minExtents.set(%g, %g, %g);", i, obj->mBounds.minExtents.x, obj->mBounds.minExtents.y, obj->mBounds.minExtents.z); + // Con::errorf(" meshes[%d]->mBounds.maxExtents.set(%g, %g, %g);", i, obj->mBounds.maxExtents.x, obj->mBounds.maxExtents.y, obj->mBounds.maxExtents.z); + // Con::errorf(" meshes[%d]->mCenter.set(%g, %g, %g);", i, obj->mCenter.x, obj->mCenter.y, obj->mCenter.z); + // Con::errorf(" meshes[%d]->mRadius = %g;", i, obj->mRadius); + // Con::errorf(" meshes[%d]->mVisibility = %g;", i, obj->mVisibility); + // Con::errorf(" meshes[%d]->mDynamic = %d;", i, obj->mDynamic); + // Con::errorf(" meshes[%d]->parentMesh = %d;", i, obj->parentMesh); + // Con::errorf(" meshes[%d]->numFrames = %d;", i, obj->numFrames); + // Con::errorf(" meshes[%d]->numMatFrames = %d;", i, obj->numMatFrames); + // Con::errorf(" meshes[%d]->vertsPerFrame = %d;", i, obj->vertsPerFrame); + + // Con::errorf("\n meshes[%d]->verts.set(dMalloc(%d * sizeof(Point3F)), %d);", obj->verts.size(), obj->verts.size()); + // for (U32 j = 0; j < obj->verts.size(); j++) + // Con::errorf(" meshes[%d]->verts[%d].set(%g, %g, %g);", i, j, obj->verts[j].x, obj->verts[j].y, obj->verts[j].z); + + // Con::errorf("\n meshes[%d]->norms.set(dMalloc(%d * sizeof(Point3F)), %d);", obj->norms.size(), obj->norms.size()); + // for (U32 j = 0; j < obj->norms.size(); j++) + // Con::errorf(" meshes[%d]->norms[%d].set(%g, %g, %g);", i, j, obj->norms[j].x, obj->norms[j].y, obj->norms[j].z); + + // Con::errorf("\n meshes[%d]->tverts.set(dMalloc(%d * sizeof(Point2F)), %d);", obj->tverts.size(), obj->tverts.size()); + // for (U32 j = 0; j < obj->tverts.size(); j++) + // Con::errorf(" meshes[%d]->tverts[%d].set(%g, %g);", i, j, obj->tverts[j].x, obj->tverts[j].y); + + // Con::errorf("\n meshes[%d]->primitives.set(dMalloc(%d * sizeof(TSDrawPrimitive)), %d);", obj->primitives.size(), obj->primitives.size()); + // for (U32 j = 0; j < obj->primitives.size(); j++) + // { + // TSDrawPrimitive& prim = obj->primitives[j]; + + // Con::errorf(" meshes[%d]->primitives[%d].start = %d;", i, j, prim.start); + // Con::errorf(" meshes[%d]->primitives[%d].numElements = %d;", i, j, prim.numElements); + // Con::errorf(" meshes[%d]->primitives[%d].matIndex = %d;", i, j, prim.matIndex); + // } + + // Con::errorf("\n meshes[%d]->encodedNorms.set(dMalloc(%d * sizeof(U8)), %d);", obj->encodedNorms.size(), obj->encodedNorms.size()); + // for (U32 j = 0; j < obj->encodedNorms.size(); j++) + // Con::errorf(" meshes[%d]->encodedNorms[%d] = %c;", i, j, obj->encodedNorms[j]); + + // Con::errorf("\n meshes[%d]->indices.set(dMalloc(%d * sizeof(U16)), %d);", obj->indices.size(), obj->indices.size()); + // for (U32 j = 0; j < obj->indices.size(); j++) + // Con::errorf(" meshes[%d]->indices[%d] = %d;", i, j, obj->indices[j]); + + // Con::errorf("\n meshes[%d]->initialTangents.set(dMalloc(%d * sizeof(Point3F)), %d);", obj->initialTangents.size(), obj->initialTangents.size()); + // for (U32 j = 0; j < obj->initialTangents.size(); j++) + // Con::errorf(" meshes[%d]->initialTangents[%d].set(%g, %g, %g);", i, j, obj->initialTangents[j].x, obj->initialTangents[j].y, obj->initialTangents[j].z); + + // Con::errorf("\n meshes[%d]->tangents.set(dMalloc(%d * sizeof(Point4F)), %d);", obj->tangents.size(), obj->tangents.size()); + // for (U32 j = 0; j < obj->tangents.size(); j++) + // Con::errorf(" meshes[%d]->tangents[%d].set(%g, %g, %g, %g);", i, j, obj->tangents[j].x, obj->tangents[j].y, obj->tangents[j].z, obj->tangents[j].w); + + // Con::errorf(" meshes[%d]->billboardAxis.set(%g, %g, %g);", i, obj->billboardAxis.x, obj->billboardAxis.y, obj->billboardAxis.z); + + // Con::errorf("\n meshes[%d]->planeNormals.set(dMalloc(%d * sizeof(Point3F)), %d);", obj->planeNormals.size(), obj->planeNormals.size()); + // for (U32 j = 0; j < obj->planeNormals.size(); j++) + // Con::errorf(" meshes[%d]->planeNormals[%d].set(%g, %g, %g);", i, j, obj->planeNormals[j].x, obj->planeNormals[j].y, obj->planeNormals[j].z); + + // Con::errorf("\n meshes[%d]->planeConstants.set(dMalloc(%d * sizeof(F32)), %d);", obj->planeConstants.size(), obj->planeConstants.size()); + // for (U32 j = 0; j < obj->planeConstants.size(); j++) + // Con::errorf(" meshes[%d]->planeConstants[%d] = %g;", i, j, obj->planeConstants[j]); + + // Con::errorf("\n meshes[%d]->planeMaterials.set(dMalloc(%d * sizeof(U32)), %d);", obj->planeMaterials.size(), obj->planeMaterials.size()); + // for (U32 j = 0; j < obj->planeMaterials.size(); j++) + // Con::errorf(" meshes[%d]->planeMaterials[%d] = %d;", i, j, obj->planeMaterials[j]); + + // Con::errorf(" meshes[%d]->planesPerFrame = %d;", i, obj->planesPerFrame); + // Con::errorf(" meshes[%d]->mergeBufferStart = %d;", i, obj->mergeBufferStart); + // } + // } + + // Con::errorf("\nalphaIn.set(dMalloc(%d * sizeof(F32)), %d);", alphaIn.size(), alphaIn.size()); + // for (U32 i = 0; i < alphaIn.size(); i++) + // Con::errorf(" alphaIn[%d] = %g;", i, alphaIn[i]); + + // Con::errorf("\nalphaOut.set(dMalloc(%d * sizeof(F32)), %d);", alphaOut.size(), alphaOut.size()); + // for (U32 i = 0; i < alphaOut.size(); i++) + // Con::errorf(" alphaOut[%d] = %g;", i, alphaOut[i]); + + // //Con::errorf("numSequences = %d", sequences.size()); + // //Con::errorf("numNodeRotations = %d", nodeRotations.size()); + // //Con::errorf("numNodeTranslations = %d", nodeTranslations.size()); + // //Con::errorf("numNodeUniformScales = %d", nodeUniformScales.size()); + // //Con::errorf("numNodeAlignedScales = %d", nodeAlignedScales.size()); + // //Con::errorf("numNodeArbitraryScaleRots = %d", nodeArbitraryScaleRots.size()); + // //Con::errorf("numNodeArbitraryScaleFactors = %d", nodeArbitraryScaleFactors.size()); + // //Con::errorf("numGroundRotations = %d", groundRotations.size()); + // //Con::errorf("numGroundTranslations = %d", groundTranslations.size()); + // //Con::errorf("numTriggers = %d", triggers.size()); + // //Con::errorf("numBillboardDetails = %d", billboardDetails.size()); + + // //Con::errorf("\nnumDetailCollisionAccelerators = %d", detailCollisionAccelerators.size()); + // //for (U32 i = 0; i < detailCollisionAccelerators.size(); i++) + // //{ + // // ConvexHullAccelerator* obj = detailCollisionAccelerators[i]; + + // // if (obj) + // // { + // // Con::errorf(" detailCollisionAccelerators[%d].numVerts = %d", i, obj->numVerts); + + // // for (U32 j = 0; j < obj->numVerts; j++) + // // { + // // Con::errorf(" verts[%d](%g, %g, %g)", j, obj->vertexList[j].x, obj->vertexList[j].y, obj->vertexList[j].z); + // // Con::errorf(" norms[%d](%g, %g, %g)", j, obj->normalList[j].x, obj->normalList[j].y, obj->normalList[j].z); + // // //U8** emitStrings; + // // } + // // } + // //} + + // Con::errorf("\nnames.setSize(%d);", names.size()); + // for (U32 i = 0; i < names.size(); i++) + // Con::errorf(" names[%d] = StringTable->insert(\"%s\");", i, names[i]); + + // //TSMaterialList * materialList; + + // Con::errorf("\nradius = %g;", radius); + // Con::errorf("tubeRadius = %g;", tubeRadius); + // Con::errorf("center.set(%g, %g, %g);", center.x, center.y, center.z); + // Con::errorf("bounds.minExtents.set(%g, %g, %g);", bounds.minExtents.x, bounds.minExtents.y, bounds.minExtents.z); + // Con::errorf("bounds.maxExtents.set(%g, %g, %g);", bounds.maxExtents.x, bounds.maxExtents.y, bounds.maxExtents.z); + + // Con::errorf("\nmExporterVersion = %d;", mExporterVersion); + // Con::errorf("mSmallestVisibleSize = %g;", mSmallestVisibleSize); + // Con::errorf("mSmallestVisibleDL = %d;", mSmallestVisibleDL); + // Con::errorf("mReadVersion = %d;", mReadVersion); + // Con::errorf("mFlags = %d;", mFlags); + // //Con::errorf("data = %d", data); + // Con::errorf("mSequencesConstructed = %d;", mSequencesConstructed); + //} + + return true; +} + +void TSShape::createEmptyShape() +{ + nodes.set(dMalloc(1 * sizeof(Node)), 1); + nodes[0].nameIndex = 1; + nodes[0].parentIndex = -1; + nodes[0].firstObject = 0; + nodes[0].firstChild = -1; + nodes[0].nextSibling = -1; + + objects.set(dMalloc(1 * sizeof(Object)), 1); + objects[0].nameIndex = 2; + objects[0].numMeshes = 1; + objects[0].startMeshIndex = 0; + objects[0].nodeIndex = 0; + objects[0].nextSibling = -1; + objects[0].firstDecal = -1; + + objectStates.set(dMalloc(1 * sizeof(ObjectState)), 1); + objectStates[0].vis = 1; + objectStates[0].frameIndex = 0; + objectStates[0].matFrameIndex = 0; + + subShapeFirstNode.set(dMalloc(1 * sizeof(S32)), 1); + subShapeFirstNode[0] = 0; + + subShapeFirstObject.set(dMalloc(1 * sizeof(S32)), 1); + subShapeFirstObject[0] = 0; + + detailFirstSkin.set(NULL, 0); + + subShapeNumNodes.set(dMalloc(1 * sizeof(S32)), 1); + subShapeNumNodes[0] = 1; + + subShapeNumObjects.set(dMalloc(1 * sizeof(S32)), 1); + subShapeNumObjects[0] = 1; + + details.set(dMalloc(1 * sizeof(Detail)), 1); + details[0].nameIndex = 0; + details[0].subShapeNum = 0; + details[0].objectDetailNum = 0; + details[0].size = 2.0f; + details[0].averageError = -1.0f; + details[0].maxError = -1.0f; + details[0].polyCount = 0; + + defaultRotations.set(dMalloc(1 * sizeof(Quat16)), 1); + defaultRotations[0].x = 0.0f; + defaultRotations[0].y = 0.0f; + defaultRotations[0].z = 0.0f; + defaultRotations[0].w = 0.0f; + + defaultTranslations.set(dMalloc(1 * sizeof(Point3F)), 1); + defaultTranslations[0].set(0.0f, 0.0f, 0.0f); + + subShapeFirstTranslucentObject.set(dMalloc(1 * sizeof(S32)), 1); + subShapeFirstTranslucentObject[0] = 1; + + alphaIn.set(dMalloc(1 * sizeof(F32)), 1); + alphaIn[0] = 0; + + alphaOut.set(dMalloc(1 * sizeof(F32)), 1); + alphaOut[0] = -1; + + sequences.set(NULL, 0); + nodeRotations.set(NULL, 0); + nodeTranslations.set(NULL, 0); + nodeUniformScales.set(NULL, 0); + nodeAlignedScales.set(NULL, 0); + nodeArbitraryScaleRots.set(NULL, 0); + nodeArbitraryScaleFactors.set(NULL, 0); + groundRotations.set(NULL, 0); + groundTranslations.set(NULL, 0); + triggers.set(NULL, 0); + billboardDetails.set(NULL, 0); + + names.setSize(3); + names[0] = StringTable->insert("Detail2"); + names[1] = StringTable->insert("Mesh2"); + names[2] = StringTable->insert("Mesh"); + + radius = 0.866025f; + tubeRadius = 0.707107f; + center.set(0.0f, 0.5f, 0.0f); + bounds.minExtents.set(-0.5f, 0.0f, -0.5f); + bounds.maxExtents.set(0.5f, 1.0f, 0.5f); + + mExporterVersion = 124; + mSmallestVisibleSize = 2; + mSmallestVisibleDL = 0; + mReadVersion = 24; + mFlags = 0; + mSequencesConstructed = 0; + + mUseDetailFromScreenError = false; + + mDetailLevelLookup.setSize( 1 ); + mDetailLevelLookup[0].set( -1, 0 ); + + // Init the collision accelerator array. Note that we don't compute the + // accelerators until the app requests them + detailCollisionAccelerators.setSize(details.size()); + for (U32 i = 0; i < detailCollisionAccelerators.size(); i++) + detailCollisionAccelerators[i] = NULL; +} + +void TSShape::fixEndian(S32 * buff32, S16 * buff16, S8 *, S32 count32, S32 count16, S32) +{ + // if endian-ness isn't the same, need to flip the buffer contents. + if (0x12345678!=convertLEndianToHost(0x12345678)) + { + for (S32 i=0; i void *Resource::create(const Torque::Path &path) +{ + // Execute the shape script if it exists + Torque::Path scriptPath(path); + scriptPath.setExtension("cs"); + + // Don't execute the script if we're already doing so! + StringTableEntry currentScript = Platform::stripBasePath(CodeBlock::getCurrentCodeBlockFullPath()); + if (!scriptPath.getFullPath().equal(currentScript)) + { + Torque::Path scriptPathDSO(scriptPath); + scriptPathDSO.setExtension("cs.dso"); + + if (Torque::FS::IsFile(scriptPathDSO) || Torque::FS::IsFile(scriptPath)) + { + String evalCmd = "exec(\"" + scriptPath + "\");"; + + String instantGroup = Con::getVariable("InstantGroup"); + Con::setIntVariable("InstantGroup", RootGroupId); + Con::evaluate((const char*)evalCmd.c_str(), false, scriptPath.getFullPath()); + Con::setVariable("InstantGroup", instantGroup.c_str()); + } + } + + // Attempt to load the shape + TSShape * ret = 0; + bool readSuccess = false; + const String extension = path.getExtension(); + + if ( extension.equal( "dts", String::NoCase ) ) + { + FileStream stream; + stream.open( path.getFullPath(), Torque::FS::File::Read ); + if ( stream.getStatus() != Stream::Ok ) + { + Con::errorf( "Resource::create - Could not open '%s'", path.getFullPath().c_str() ); + return NULL; + } + + ret = new TSShape; + readSuccess = ret->read(&stream); + } + else if ( extension.equal( "dae", String::NoCase ) || extension.equal( "kmz", String::NoCase ) ) + { +#ifdef TORQUE_COLLADA + // Attempt to load the DAE file + ret = loadColladaShape(path); + readSuccess = (ret != NULL); +#else + // No COLLADA support => attempt to load the cached DTS file instead + Torque::Path cachedPath = path; + cachedPath.setExtension("cached.dts"); + + FileStream stream; + stream.open( cachedPath.getFullPath(), Torque::FS::File::Read ); + if ( stream.getStatus() != Stream::Ok ) + { + Con::errorf( "Resource::create - Could not open '%s'", cachedPath.getFullPath().c_str() ); + return NULL; + } + ret = new TSShape; + readSuccess = ret->read(&stream); +#endif + } + else + { + Con::errorf( "Resource::create - '%s' has an unknown file format", path.getFullPath().c_str() ); + delete ret; + return NULL; + } + + if( !readSuccess ) + { + Con::errorf( "Resource::create - Error reading '%s'", path.getFullPath().c_str() ); + delete ret; + ret = NULL; + } + + return ret; +} + +template<> ResourceBase::Signature Resource::signature() +{ + return MakeFourCC('t','s','s','h'); +} + +TSShape::ConvexHullAccelerator* TSShape::getAccelerator(S32 dl) +{ + AssertFatal(dl < details.size(), "Error, bad detail level!"); + if (dl == -1) + return NULL; + + AssertFatal( detailCollisionAccelerators.size() == details.size(), + "TSShape::getAccelerator() - mismatched array sizes!" ); + + if (detailCollisionAccelerators[dl] == NULL) + computeAccelerator(dl); + + AssertFatal(detailCollisionAccelerators[dl] != NULL, "This should be non-null after computing it!"); + return detailCollisionAccelerators[dl]; +} + + +void TSShape::computeAccelerator(S32 dl) +{ + AssertFatal(dl < details.size(), "Error, bad detail level!"); + + // Have we already computed this? + if (detailCollisionAccelerators[dl] != NULL) + return; + + // Create a bogus features list... + ConvexFeature cf; + MatrixF mat(true); + Point3F n(0, 0, 1); + + const TSDetail* detail = &details[dl]; + S32 ss = detail->subShapeNum; + S32 od = detail->objectDetailNum; + + S32 start = subShapeFirstObject[ss]; + S32 end = subShapeNumObjects[ss] + start; + if (start < end) + { + // run through objects and collide + // DMMNOTE: This assumes that the transform of the collision hulls is + // identity... + U32 surfaceKey = 0; + for (S32 i = start; i < end; i++) + { + const TSObject* obj = &objects[i]; + + if (obj->numMeshes && od < obj->numMeshes) { + TSMesh* mesh = meshes[obj->startMeshIndex + od]; + if (mesh) + mesh->getFeatures(0, mat, n, &cf, surfaceKey); + } + } + } + + Vector fixedVerts; + VECTOR_SET_ASSOCIATION(fixedVerts); + S32 i; + for (i = 0; i < cf.mVertexList.size(); i++) { + S32 j; + bool found = false; + for (j = 0; j < cf.mFaceList.size(); j++) { + if (cf.mFaceList[j].vertex[0] == i || + cf.mFaceList[j].vertex[1] == i || + cf.mFaceList[j].vertex[2] == i) { + found = true; + break; + } + } + if (!found) + continue; + + found = false; + for (j = 0; j < fixedVerts.size(); j++) { + if (fixedVerts[j] == cf.mVertexList[i]) { + found = true; + break; + } + } + if (found == true) { + // Ok, need to replace any references to vertex i in the facelists with + // a reference to vertex j in the fixed list + for (S32 k = 0; k < cf.mFaceList.size(); k++) { + for (S32 l = 0; l < 3; l++) { + if (cf.mFaceList[k].vertex[l] == i) + cf.mFaceList[k].vertex[l] = j; + } + } + } else { + for (S32 k = 0; k < cf.mFaceList.size(); k++) { + for (S32 l = 0; l < 3; l++) { + if (cf.mFaceList[k].vertex[l] == i) + cf.mFaceList[k].vertex[l] = fixedVerts.size(); + } + } + fixedVerts.push_back(cf.mVertexList[i]); + } + } + cf.mVertexList.setSize(0); + cf.mVertexList = fixedVerts; + + // Ok, so now we have a vertex list. Lets copy that out... + ConvexHullAccelerator* accel = new ConvexHullAccelerator; + detailCollisionAccelerators[dl] = accel; + accel->numVerts = cf.mVertexList.size(); + accel->vertexList = new Point3F[accel->numVerts]; + dMemcpy(accel->vertexList, cf.mVertexList.address(), sizeof(Point3F) * accel->numVerts); + + accel->normalList = new Point3F[cf.mFaceList.size()]; + for (i = 0; i < cf.mFaceList.size(); i++) + accel->normalList[i] = cf.mFaceList[i].normal; + + accel->emitStrings = new U8*[accel->numVerts]; + dMemset(accel->emitStrings, 0, sizeof(U8*) * accel->numVerts); + + for (i = 0; i < accel->numVerts; i++) { + S32 j; + + Vector faces; + VECTOR_SET_ASSOCIATION(faces); + for (j = 0; j < cf.mFaceList.size(); j++) { + if (cf.mFaceList[j].vertex[0] == i || + cf.mFaceList[j].vertex[1] == i || + cf.mFaceList[j].vertex[2] == i) { + faces.push_back(j); + } + } + AssertFatal(faces.size() != 0, "Huh? Vertex unreferenced by any faces"); + + // Insert all faces that didn't make the first cut, but share a plane with + // a face that's on the short list. + for (j = 0; j < cf.mFaceList.size(); j++) { + bool found = false; + S32 k; + for (k = 0; k < faces.size(); k++) { + if (faces[k] == j) + found = true; + } + if (found) + continue; + + found = false; + for (k = 0; k < faces.size(); k++) { + if (mDot(accel->normalList[faces[k]], accel->normalList[j]) > 0.999) { + found = true; + break; + } + } + if (found) + faces.push_back(j); + } + + Vector vertRemaps; + VECTOR_SET_ASSOCIATION(vertRemaps); + for (j = 0; j < faces.size(); j++) { + for (U32 k = 0; k < 3; k++) { + U32 insert = cf.mFaceList[faces[j]].vertex[k]; + bool found = false; + for (S32 l = 0; l < vertRemaps.size(); l++) { + if (insert == vertRemaps[l]) { + found = true; + break; + } + } + if (!found) + vertRemaps.push_back(insert); + } + } + + Vector edges; + VECTOR_SET_ASSOCIATION(edges); + for (j = 0; j < faces.size(); j++) { + for (U32 k = 0; k < 3; k++) { + U32 edgeStart = cf.mFaceList[faces[j]].vertex[(k + 0) % 3]; + U32 edgeEnd = cf.mFaceList[faces[j]].vertex[(k + 1) % 3]; + + U32 e0 = getMin(edgeStart, edgeEnd); + U32 e1 = getMax(edgeStart, edgeEnd); + + bool found = false; + for (S32 l = 0; l < edges.size(); l++) { + if (edges[l].x == e0 && edges[l].y == e1) { + found = true; + break; + } + } + if (!found) + edges.push_back(Point2I(e0, e1)); + } + } + + //AssertFatal(vertRemaps.size() < 256 && faces.size() < 256 && edges.size() < 256, + // "Error, ran over the shapebase assumptions about convex hulls."); + + U32 emitStringLen = 1 + vertRemaps.size() + + 1 + (edges.size() * 2) + + 1 + (faces.size() * 4); + accel->emitStrings[i] = new U8[emitStringLen]; + + U32 currPos = 0; + + accel->emitStrings[i][currPos++] = vertRemaps.size(); + for (j = 0; j < vertRemaps.size(); j++) + accel->emitStrings[i][currPos++] = vertRemaps[j]; + + accel->emitStrings[i][currPos++] = edges.size(); + for (j = 0; j < edges.size(); j++) { + S32 l; + U32 old = edges[j].x; + bool found = false; + for (l = 0; l < vertRemaps.size(); l++) { + if (vertRemaps[l] == old) { + found = true; + accel->emitStrings[i][currPos++] = l; + break; + } + } + AssertFatal(found, "Error, couldn't find the remap!"); + + old = edges[j].y; + found = false; + for (l = 0; l < vertRemaps.size(); l++) { + if (vertRemaps[l] == old) { + found = true; + accel->emitStrings[i][currPos++] = l; + break; + } + } + AssertFatal(found, "Error, couldn't find the remap!"); + } + + accel->emitStrings[i][currPos++] = faces.size(); + for (j = 0; j < faces.size(); j++) { + accel->emitStrings[i][currPos++] = faces[j]; + for (U32 k = 0; k < 3; k++) { + U32 old = cf.mFaceList[faces[j]].vertex[k]; + bool found = false; + for (S32 l = 0; l < vertRemaps.size(); l++) { + if (vertRemaps[l] == old) { + found = true; + accel->emitStrings[i][currPos++] = l; + break; + } + } + AssertFatal(found, "Error, couldn't find the remap!"); + } + } + AssertFatal(currPos == emitStringLen, "Error, over/underflowed the emission string!"); + } +} diff --git a/Engine/source/ts/tsShape.h b/Engine/source/ts/tsShape.h new file mode 100644 index 000000000..676b323fe --- /dev/null +++ b/Engine/source/ts/tsShape.h @@ -0,0 +1,692 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TSSHAPE_H_ +#define _TSSHAPE_H_ + +#ifndef _TSMESH_H_ +#include "ts/tsMesh.h" +#endif +#ifndef _TSINTEGERSET_H_ +#include "ts/tsIntegerSet.h" +#endif +#ifndef _TSTRANSFORM_H_ +#include "ts/tsTransform.h" +#endif +#ifndef _TSSHAPEALLOC_H_ +#include "ts/tsShapeAlloc.h" +#endif + + +#define DTS_EXPORTER_CURRENT_VERSION 124 + +class TSMaterialList; +class TSLastDetail; +class PhysicsCollision; + +// +struct CollisionShapeInfo +{ + S32 colNode; + PhysicsCollision *colShape; +}; + +/// TSShape stores generic data for a 3space model. +/// +/// TSShape and TSShapeInstance act in conjunction to allow the rendering and +/// manipulation of a three dimensional model. +/// +/// @note The material lists are the only thing that is not loaded in TSShape. +/// instead, they are loaded in TSShapeInstance because of a former restriction +/// on the resource manager where only one file could be opened at a time. +/// The resource manager restriction has been resolved, but the material +/// lists are still loaded in TSShapeInstance. +/// +/// @see TSShapeInstance for a further discussion of the 3space system. +class TSShape +{ + public: + enum + { + UniformScale = BIT(0), + AlignedScale = BIT(1), + ArbitraryScale = BIT(2), + Blend = BIT(3), + Cyclic = BIT(4), + MakePath = BIT(5), + HasTranslucency= BIT(6), + AnyScale = UniformScale | AlignedScale | ArbitraryScale + }; + + /// Nodes hold the transforms in the shape's tree. They are the bones of the skeleton. + struct Node + { + S32 nameIndex; + S32 parentIndex; + + // computed at runtime + S32 firstObject; + S32 firstChild; + S32 nextSibling; + }; + + /// Objects hold renderable items (in particular meshes). + /// + /// Each object has a number of meshes associated with it. + /// Each mesh corresponds to a different detail level. + /// + /// meshIndicesIndex points to numMeshes consecutive indices + /// into the meshList and meshType vectors. It indexes the + /// meshIndexList vector (meshIndexList is merely a clearinghouse + /// for the object's mesh lists). Some indices may correspond to + /// no mesh -- which means no mesh will be drawn for the part for + /// the given detail level. See comments on the meshIndexList + /// for how null meshes are coded. + /// + /// @note Things are stored this way so that there are no pointers. + /// This makes serialization to disk dramatically simpler. + struct Object + { + S32 nameIndex; + S32 numMeshes; + S32 startMeshIndex; ///< Index into meshes array. + S32 nodeIndex; + + // computed at load + S32 nextSibling; + S32 firstDecal; // DEPRECATED + }; + + /// A Sequence holds all the information necessary to perform a particular animation (sequence). + /// + /// Sequences index a range of keyframes. Keyframes are assumed to be equally spaced in time. + /// + /// Each node and object is either a member of the sequence or not. If not, they are set to + /// default values when we switch to the sequence unless they are members of some other active sequence. + /// Blended sequences "add" a transform to the current transform of a node. Any object animation of + /// a blended sequence over-rides any existing object state. Blended sequences are always + /// applied after non-blended sequences. + struct Sequence + { + S32 nameIndex; + S32 numKeyframes; + F32 duration; + S32 baseRotation; + S32 baseTranslation; + S32 baseScale; + S32 baseObjectState; + S32 baseDecalState; // DEPRECATED + S32 firstGroundFrame; + S32 numGroundFrames; + S32 firstTrigger; + S32 numTriggers; + F32 toolBegin; + + /// @name Bitsets + /// These bitsets code whether this sequence cares about certain aspects of animation + /// e.g., the rotation, translation, or scale of node transforms, + /// or the visibility, frame or material frame of objects. + /// @{ + + TSIntegerSet rotationMatters; ///< Set of nodes + TSIntegerSet translationMatters; ///< Set of nodes + TSIntegerSet scaleMatters; ///< Set of nodes + TSIntegerSet visMatters; ///< Set of objects + TSIntegerSet frameMatters; ///< Set of objects + TSIntegerSet matFrameMatters; ///< Set of objects + /// @} + + S32 priority; + U32 flags; + U32 dirtyFlags; ///< determined at load time + + /// @name Source Data + /// Store some information about where the sequence data came from (used by + /// TSShapeConstructor and the Shape Editor) + /// @{ + struct SeqSourceData + { + String from; // The source sequence (ie. a DSQ file) + S32 start; // The first frame in the source sequence + S32 end; // The last frame in the source sequence + S32 total; // The total number of frames in the source sequence + String blendSeq; // The blend reference sequence + S32 blendFrame; // The blend reference frame + SeqSourceData() : from("\t"), start(0), end(0), total(0), blendSeq(""), blendFrame(0) { } + } sourceData; + + /// @name Flag Tests + /// Each of these tests a different flag against the object's flag list + /// to determine the attributes of the given object. + /// @{ + + bool testFlags(U32 comp) const { return (flags&comp)!=0; } + bool animatesScale() const { return testFlags(AnyScale); } + bool animatesUniformScale() const { return testFlags(UniformScale); } + bool animatesAlignedScale() const { return testFlags(AlignedScale); } + bool animatesArbitraryScale() const { return testFlags(ArbitraryScale); } + bool isBlend() const { return testFlags(Blend); } + bool isCyclic() const { return testFlags(Cyclic); } + bool makePath() const { return testFlags(MakePath); } + /// @} + + /// @name IO + /// @{ + + void read(Stream *, bool readNameIndex = true); + void write(Stream *, bool writeNameIndex = true) const; + /// @} + }; + + /// Describes state of an individual object. Includes everything in an object that can be + /// controlled by animation. + struct ObjectState + { + F32 vis; + S32 frameIndex; + S32 matFrameIndex; + }; + + /// When time on a sequence advances past a certain point, a trigger takes effect and changes + /// one of the state variables to on or off. (State variables found in TSShapeInstance::mTriggerStates) + struct Trigger + { + enum TriggerStates + { + StateOn = BIT(31), + InvertOnReverse = BIT(30), + StateMask = BIT(30)-1 + }; + + U32 state; ///< One of TriggerStates + F32 pos; + }; + + /// Details are used for render detail selection. + /// + /// As the projected size of the shape changes, + /// a different node structure can be used (subShape) and a different objectDetail can be selected + /// for each object drawn. Either of these two parameters can also stay constant, but presumably + /// not both. If size is negative then the detail level will never be selected by the standard + /// detail selection process. It will have to be selected by name. Such details are "utility + /// details" because they exist to hold data (node positions or collision information) but not + /// normally to be drawn. By default there will always be a "Ground" utility detail. + /// + /// Note that this struct should always be 32bit aligned + /// as its required by assembleShape/disassembleShape. + struct Detail + { + S32 nameIndex; + S32 subShapeNum; + S32 objectDetailNum; + F32 size; + F32 averageError; + F32 maxError; + S32 polyCount; + + /// These values are new autobillboard settings stored + /// as part of the Detail struct in version 26 and above. + /// @{ + + S32 bbDimension; ///< The size of the autobillboard image. + S32 bbDetailLevel; ///< The detail to render as the autobillboard. + U32 bbEquatorSteps; ///< The number of autobillboard images to capture around the equator. + U32 bbPolarSteps; ///< The number of autobillboard images to capture along the pole. + F32 bbPolarAngle; ///< The angle in radians at which the top/bottom autobillboard images should be displayed. + U32 bbIncludePoles; ///< If non-zero then top and bottom images are generated for the autobillboard. + + /// @} + }; + + /// @name Collision Accelerators + /// + /// For speeding up buildpolylist and support calls. + /// @{ + struct ConvexHullAccelerator { + S32 numVerts; + Point3F* vertexList; + Point3F* normalList; + U8** emitStrings; + }; + ConvexHullAccelerator* getAccelerator(S32 dl); + /// @} + + + /// @name Shape Vector Data + /// @{ + + Vector nodes; + Vector objects; + Vector objectStates; + Vector subShapeFirstNode; + Vector subShapeFirstObject; + Vector detailFirstSkin; + Vector subShapeNumNodes; + Vector subShapeNumObjects; + Vector details; + Vector defaultRotations; + Vector defaultTranslations; + + /// @} + + /// These are set up at load time, but memory is allocated along with loaded data + /// @{ + + Vector subShapeFirstTranslucentObject; + Vector meshes; + + /// @} + + /// @name Alpha Vectors + /// these vectors describe how to transition between detail + /// levels using alpha. "alpha-in" next detail as intraDL goes + /// from alphaIn+alphaOut to alphaOut. "alpha-out" current + /// detail level as intraDL goes from alphaOut to 0. + /// @note + /// - intraDL is at 1 when if shape were any closer to us we'd be at dl-1 + /// - intraDL is at 0 when if shape were any farther away we'd be at dl+1 + /// @{ + + Vector alphaIn; + Vector alphaOut + ; + /// @} + + /// @name Resizeable vectors + /// @{ + + Vector sequences; + Vector nodeRotations; + Vector nodeTranslations; + Vector nodeUniformScales; + Vector nodeAlignedScales; + Vector nodeArbitraryScaleRots; + Vector nodeArbitraryScaleFactors; + Vector groundRotations; + Vector groundTranslations; + Vector triggers; + Vector billboardDetails; + Vector detailCollisionAccelerators; + Vector names; + + /// @} + + TSMaterialList * materialList; + + /// @name Bounding + /// @{ + + F32 radius; + F32 tubeRadius; + Point3F center; + Box3F bounds; + + /// @} + + // various... + U32 mExporterVersion; + F32 mSmallestVisibleSize; ///< Computed at load time from details vector. + S32 mSmallestVisibleDL; ///< @see mSmallestVisibleSize + S32 mReadVersion; ///< File version that this shape was read from. + U32 mFlags; ///< hasTranslucancy + U32 data; ///< User-defined data storage. + + /// If enabled detail selection will use the + /// legacy screen error method for lod. + /// @see setDetailFromScreenError + bool mUseDetailFromScreenError; + + // TODO: This would be nice as Tuple<> + struct LodPair + { + S8 level; // -1 to 128 + U8 intra; // encoded 0 to 1 + + inline void set( S32 dl, F32 intraDL ) + { + level = (S8)dl; + intra = (S8)( intraDL * 255.0f ); + } + + inline void get( S32 &dl, F32 &intraDL ) + { + dl = level; + intraDL = (F32)intra / 255.0f; + } + }; + + /// The lod lookup table where we mark down the detail + /// level and intra-detail level for each pixel size. + Vector mDetailLevelLookup; + + /// The GFX vertex format for all detail meshes in the shape. + /// @see initVertexFeatures() + GFXVertexFormat mVertexFormat; + + /// The GFX vertex size in bytes for all detail meshes in the shape. + /// @see initVertexFeatures() + U32 mVertSize; + + /// Is true if this shape contains skin meshes. + bool mHasSkinMesh; + + bool mSequencesConstructed; + + S8* mShapeData; + U32 mShapeDataSize; + + // shape class has few methods -- + // just constructor/destructor, io, and lookup methods + + // constructor/destructor + TSShape(); + ~TSShape(); + void init(); + void initMaterialList(); ///< you can swap in a new material list, but call this if you do + bool preloadMaterialList(const Torque::Path &path); ///< called to preload and validate the materials in the mat list + + void setupBillboardDetails( const String &cachePath ); + + /// Called from init() to calcuate the GFX vertex features for + /// all detail meshes in the shape. + void initVertexFeatures(); + + bool getSequencesConstructed() const { return mSequencesConstructed; } + void setSequencesConstructed(const bool c) { mSequencesConstructed = c; } + + /// @name Lookup Animation Info + /// indexed by keyframe number and offset (which object/node/decal + /// of the animated objects/nodes/decals you want information for). + /// @{ + + QuatF & getRotation(const Sequence & seq, S32 keyframeNum, S32 rotNum, QuatF *) const; + const Point3F & getTranslation(const Sequence & seq, S32 keyframeNum, S32 tranNum) const; + F32 getUniformScale(const Sequence & seq, S32 keyframeNum, S32 scaleNum) const; + const Point3F & getAlignedScale(const Sequence & seq, S32 keyframeNum, S32 scaleNum) const; + TSScale & getArbitraryScale(const Sequence & seq, S32 keyframeNum, S32 scaleNum, TSScale *) const; + const ObjectState & getObjectState(const Sequence & seq, S32 keyframeNum, S32 objectNum) const; + /// @} + + /// build LOS collision detail + void computeAccelerator(S32 dl); + bool buildConvexHull(S32 dl) const; + void computeBounds(S32 dl, Box3F & bounds) const; // uses default transforms to compute bounding box around a detail level + // see like named method on shapeInstance if you want to use animated transforms + + /// Used to find collision detail meshes in the DTS. + /// + /// @param useVisibleMesh If true return the highest visible detail level. + /// @param outDetails The output detail index vector. + /// @param outLOSDetails The optional output LOS detail vector. + /// + void findColDetails( bool useVisibleMesh, Vector *outDetails, Vector *outLOSDetails ) const; + + /// Builds a physics collision shape at the requested scale. + /// + /// If using the visible mesh one or more triangle meshes are created + /// from the first visible detail level. + /// + /// If using collision meshes we look for mesh names prefixed with the + /// following hints: + // + /// "colbox" + /// "colsphere" + /// "colcapsule" + /// "colmesh" + /// + /// In the case of the primitives the mesh bounding box is used to generate + /// a box, sphere, or capsule collision shape. The "colmesh" will create a + /// concave triangle mesh for collision. + /// + /// Any other named collision shape is interpreted as a regular convex hull. + /// + /// @return The collision object or NULL if no collision data could be generated. + /// + PhysicsCollision* buildColShape( bool useVisibleMesh, const Point3F &scale ); + + /// Like buildColShape except we build one PhysicsCollision object per + /// collision node. + /// + /// Results are returned by filling in the CollisionShapeInfo Vector, which also + /// specifies the collision node index for each PhysicsCollision built. + /// + void buildColShapes( bool useVisibleMesh, const Point3F &scale, Vector< CollisionShapeInfo > *list ); + + /// For internal use. + PhysicsCollision* _buildColShapes( bool useVisibleMesh, const Point3F &scale, Vector< CollisionShapeInfo > *list, bool perMesh ); + + /// @name Lookup Methods + /// @{ + + /// Returns index into the name vector that equals the passed name. + S32 findName( const String &name ) const; + + /// Returns name string at the passed name vector index. + const String& getName( S32 nameIndex ) const; + + /// Returns name string for mesh at the passed index. + const String& getMeshName( S32 meshIndex ) const; + + /// Returns name string for node at the passed index. + const String& getNodeName( S32 nodeIndex ) const; + + /// Returns name string for sequence at the passed index. + const String& getSequenceName( S32 seqIndex ) const; + + S32 getTargetCount() const; + const String& getTargetName( S32 mapToNameIndex ) const; + + S32 findNode(S32 nameIndex) const; + S32 findNode(const String &name) const { return findNode(findName(name)); } + + S32 findObject(S32 nameIndex) const; + S32 findObject(const String &name) const { return findObject(findName(name)); } + + S32 findDetail(S32 nameIndex) const; + S32 findDetail(const String &name) const { return findDetail(findName(name)); } + S32 findDetailBySize(S32 size) const; + + S32 findSequence(S32 nameIndex) const; + S32 findSequence(const String &name) const { return findSequence(findName(name)); } + + S32 getSubShapeForNode(S32 nodeIndex); + S32 getSubShapeForObject(S32 objIndex); + void getSubShapeDetails(S32 subShapeIndex, Vector& validDetails); + + void getNodeWorldTransform(S32 nodeIndex, MatrixF* mat) const; + void getNodeKeyframe(S32 nodeIndex, const TSShape::Sequence& seq, S32 keyframe, MatrixF* mat) const; + void getNodeObjects(S32 nodeIndex, Vector& nodeObjects); + void getNodeChildren(S32 nodeIndex, Vector& nodeChildren); + + void getObjectDetails(S32 objIndex, Vector& objDetails); + + bool findMeshIndex(const String &meshName, S32& objIndex, S32& meshIndex); + TSMesh* findMesh(const String &meshName); + + bool hasTranslucency() const { return (mFlags & HasTranslucency)!=0; } + + const GFXVertexFormat* getVertexFormat() const { return &mVertexFormat; } + + U32 getVertexSize() const { return mVertSize; } + + /// @} + + /// @name Alpha Transitions + /// These control default values for alpha transitions between detail levels + /// @{ + + static F32 smAlphaOutLastDetail; + static F32 smAlphaInBillboard; + static F32 smAlphaOutBillboard; + static F32 smAlphaInDefault; + static F32 smAlphaOutDefault; + /// @} + + /// don't load this many of the highest detail levels (although we always + /// load one renderable detail if there is one) + static S32 smNumSkipLoadDetails; + + /// by default we initialize shape when we read... + static bool smInitOnRead; + + /// @name Version Info + /// @{ + + /// Most recent version...the one we write + static S32 smVersion; + /// Version currently being read, only valid during read + static S32 smReadVersion; + static const U32 smMostRecentExporterVersion; + ///@} + + /// @name Persist Methods + /// Methods for saving/loading shapes to/from streams + /// @{ + + bool canWriteOldFormat() const; + void write(Stream *, bool saveOldFormat=false); + bool read(Stream *); + void readOldShape(Stream * s, S32 * &, S16 * &, S8 * &, S32 &, S32 &, S32 &); + void writeName(Stream *, S32 nameIndex); + S32 readName(Stream *, bool addName); + + /// Initializes our TSShape to be ready to receive put mesh data + void createEmptyShape(); + + void exportSequences(Stream *); + void exportSequence(Stream * s, const TSShape::Sequence& seq, bool saveOldFormat); + bool importSequences(Stream *, const String& sequencePath); + + /// @} + + /// @name Persist Helper Functions + /// @{ + + static TSShapeAlloc smTSAlloc; + + void fixEndian(S32 *, S16 *, S8 *, S32, S32, S32); + /// @} + + /// @name Memory Buffer Transfer Methods + /// uses TSShape::Alloc structure + /// @{ + + void assembleShape(); + void disassembleShape(); + ///@} + + /// mem buffer transfer helper (indicate when we don't want to include a particular mesh/decal) + bool checkSkip(S32 meshNum, S32 & curObject, S32 skipDL); + + void fixupOldSkins(S32 numMeshes, S32 numSkins, S32 numDetails, S32 * detailFirstSkin, S32 * detailNumSkins); + + /// @name Shape Editing + /// @{ + S32 addName(const String& name); + bool removeName(const String& name); + void updateSmallestVisibleDL(); + S32 addDetail(const String& dname, S32 size, S32 subShapeNum); + + S32 addImposter( const String& cachePath, + S32 size, + S32 numEquatorSteps, + S32 numPolarSteps, + S32 dl, + S32 dim, + bool includePoles, + F32 polarAngle ); + bool removeImposter(); + + bool renameNode(const String& oldName, const String& newName); + bool renameObject(const String& oldName, const String& newName); + bool renameDetail(const String& oldName, const String& newName); + bool renameSequence(const String& oldName, const String& newName); + + bool setNodeTransform(const String& name, const Point3F& pos, const QuatF& rot); + bool addNode(const String& name, const String& parentName, const Point3F& pos, const QuatF& rot); + bool removeNode(const String& name); + + S32 addObject(const String& objName, S32 subShapeIndex); + void addMeshToObject(S32 objIndex, S32 meshIndex, TSMesh* mesh); + void removeMeshFromObject(S32 objIndex, S32 meshIndex); + bool setObjectNode(const String& objName, const String& nodeName); + bool removeObject(const String& objName); + + TSMesh* copyMesh( const TSMesh* srcMesh ) const; + bool addMesh(TSShape* srcShape, const String& srcMeshName, const String& meshName); + bool addMesh(TSMesh* mesh, const String& meshName); + bool setMeshSize(const String& meshName, S32 size); + bool removeMesh(const String& meshName); + + S32 setDetailSize(S32 oldSize, S32 newSize); + bool removeDetail(S32 size); + + bool addSequence(const Torque::Path& path, const String& fromSeq, const String& name, S32 startFrame, S32 endFrame, bool padRotKeys, bool padTransKeys); + bool removeSequence(const String& name); + + bool addTrigger(const String& seqName, S32 keyframe, S32 state); + bool removeTrigger(const String& seqName, S32 keyframe, S32 state); + + bool setSequenceBlend(const String& seqName, bool blend, const String& blendRefSeqName, S32 blendRefFrame); + bool setSequenceGroundSpeed(const String& seqName, const Point3F& trans, const Point3F& rot); + /// @} +}; + + +#define TSNode TSShape::Node +#define TSObject TSShape::Object +#define TSSequence TSShape::Sequence +#define TSDetail TSShape::Detail + +inline QuatF & TSShape::getRotation(const Sequence & seq, S32 keyframeNum, S32 rotNum, QuatF * quat) const +{ + return nodeRotations[seq.baseRotation + rotNum*seq.numKeyframes + keyframeNum].getQuatF(quat); +} + +inline const Point3F & TSShape::getTranslation(const Sequence & seq, S32 keyframeNum, S32 tranNum) const +{ + return nodeTranslations[seq.baseTranslation + tranNum*seq.numKeyframes + keyframeNum]; +} + +inline F32 TSShape::getUniformScale(const Sequence & seq, S32 keyframeNum, S32 scaleNum) const +{ + return nodeUniformScales[seq.baseScale + scaleNum*seq.numKeyframes + keyframeNum]; +} + +inline const Point3F & TSShape::getAlignedScale(const Sequence & seq, S32 keyframeNum, S32 scaleNum) const +{ + return nodeAlignedScales[seq.baseScale + scaleNum*seq.numKeyframes + keyframeNum]; +} + +inline TSScale & TSShape::getArbitraryScale(const Sequence & seq, S32 keyframeNum, S32 scaleNum, TSScale * scale) const +{ + nodeArbitraryScaleRots[seq.baseScale + scaleNum*seq.numKeyframes + keyframeNum].getQuatF(&scale->mRotate); + scale->mScale = nodeArbitraryScaleFactors[seq.baseScale + scaleNum*seq.numKeyframes + keyframeNum]; + return *scale; +} + +inline const TSShape::ObjectState & TSShape::getObjectState(const Sequence & seq, S32 keyframeNum, S32 objectNum) const +{ + return objectStates[seq.baseObjectState + objectNum*seq.numKeyframes + keyframeNum]; +} + +#endif diff --git a/Engine/source/ts/tsShapeAlloc.cpp b/Engine/source/ts/tsShapeAlloc.cpp new file mode 100644 index 000000000..8908b84cf --- /dev/null +++ b/Engine/source/ts/tsShapeAlloc.cpp @@ -0,0 +1,229 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "ts/tsShapeAlloc.h" + +#define readOnly() AssertFatal(mMode==TSShapeAlloc::ReadMode, "TSShapeAlloc: write-only function called when reading") +#define writeOnly() AssertFatal(mMode==TSShapeAlloc::WriteMode,"TSShapeAlloc: read-only function called when writing") + +void TSShapeAlloc::setRead(S32 * memBuffer32, S16 * memBuffer16, S8 * memBuffer8, bool clear) +{ + mMemBuffer32 = memBuffer32; + mMemBuffer16 = memBuffer16; + mMemBuffer8 = memBuffer8 ; + + mMemGuard32 = 0; + mMemGuard16 = 0; + mMemGuard8 = 0; + + mSaveGuard32 = 0; + mSaveGuard16 = 0; + mSaveGuard8 = 0; + + if (clear) + { + mDest = NULL; + mSize = 0; + } + + setSkipMode(false); + mMode = TSShapeAlloc::ReadMode; +} + +void TSShapeAlloc::setWrite() +{ + mMemBuffer32 = 0; + mMemBuffer16 = 0; + mMemBuffer8 = 0; + + mSize32 = mFullSize32 = 0; + mSize16 = mFullSize16 = 0; + mSize8 = mFullSize8 = 0; + + mMemGuard32 = 0; + mMemGuard16 = 0; + mMemGuard8 = 0; + + setSkipMode(false); // doesn't really do anything here... + mMode = TSShapeAlloc::WriteMode; +} + +void TSShapeAlloc::doAlloc() +{ + readOnly(); + + mDest = new S8[mSize]; + mSize = 0; +} + +void TSShapeAlloc::align32() +{ + readOnly(); + + S32 aligned = mSize+3 & (~0x3); + allocShape8(aligned-mSize); +} + +#define IMPLEMENT_ALLOC(suffix,type) \ + \ +type TSShapeAlloc::get##suffix() \ +{ \ + readOnly(); \ + return *(mMemBuffer##suffix++); \ +} \ + \ +void TSShapeAlloc::get##suffix(type * dest, S32 num) \ +{ \ + readOnly(); \ + dMemcpy(dest,mMemBuffer##suffix,sizeof(type)*num); \ + mMemBuffer##suffix += num; \ +} \ + \ +type * TSShapeAlloc::allocShape##suffix(S32 num) \ +{ \ + readOnly(); \ + type * ret = (type*) mDest; \ + if (mDest) \ + mDest += mMult*num*sizeof(type); \ + mSize += sizeof(type)*mMult*num; \ + return ret; \ +} \ + \ +type * TSShapeAlloc::getPointer##suffix(S32 num) \ +{ \ + readOnly(); \ + type * ret = (type*)mMemBuffer##suffix; \ + mMemBuffer##suffix += num; \ + return ret; \ +} \ + \ +type * TSShapeAlloc::copyToShape##suffix(S32 num, bool returnSomething) \ +{ \ + readOnly(); \ + type * ret = (!returnSomething || mDest) ? (type*)mDest : mMemBuffer##suffix; \ + if (mDest) \ + { \ + dMemcpy((S8*)mDest,(S8*)mMemBuffer##suffix, \ + mMult*num*sizeof(type)); \ + mDest += mMult*num*sizeof(type); \ + } \ + mMemBuffer##suffix += num; \ + mSize += sizeof(type)*mMult*num; \ + return ret; \ +} \ + \ +bool TSShapeAlloc::checkGuard##suffix() \ +{ \ + readOnly(); \ + mSaveGuard##suffix=get##suffix(); \ + bool ret = (mSaveGuard##suffix==mMemGuard##suffix); \ + mMemGuard##suffix += 1; \ + return ret; \ +} \ + \ +type TSShapeAlloc::getPrevGuard##suffix() \ +{ \ + readOnly(); \ + return mMemGuard##suffix - 1; \ +} \ + \ +type TSShapeAlloc::getSaveGuard##suffix() \ +{ \ + readOnly(); \ + return mSaveGuard##suffix; \ +} \ + \ +type * TSShapeAlloc::getBuffer##suffix() \ +{ \ + writeOnly(); \ + return mMemBuffer##suffix; \ +} \ + \ +S32 TSShapeAlloc::getBufferSize##suffix() \ +{ \ + writeOnly(); \ + return mSize##suffix; \ +} \ + \ +type * TSShapeAlloc::extend##suffix(S32 add) \ +{ \ + writeOnly(); \ + if (mSize##suffix+add>mFullSize##suffix) \ + { \ + S32 numPages = 1+(mFullSize##suffix+add)/TSShapeAlloc::PageSize; \ + mFullSize##suffix = numPages*TSShapeAlloc::PageSize; \ + type * temp = new type[mFullSize##suffix]; \ + dMemcpy(temp,mMemBuffer##suffix, mSize##suffix * sizeof(type)); \ + delete [] mMemBuffer##suffix; \ + mMemBuffer##suffix = temp; \ + } \ + type * ret = mMemBuffer##suffix + mSize##suffix; \ + mSize##suffix += add; \ + return ret; \ +} \ + \ +type TSShapeAlloc::set##suffix(type entry) \ +{ \ + writeOnly(); \ + *extend##suffix(1) = entry; \ + return entry; \ +} \ + \ +void TSShapeAlloc::copyToBuffer##suffix(type * entries, S32 count) \ +{ \ + writeOnly(); \ + if (entries) \ + dMemcpy((U8*)extend##suffix(count),(U8*)entries,count*sizeof(type)); \ + else \ + dMemset((U8*)extend##suffix(count),0,count*sizeof(type)); \ +} \ + \ +void TSShapeAlloc::setGuard##suffix() \ +{ \ + writeOnly(); \ + set##suffix(mMemGuard##suffix); \ + mMemGuard##suffix += 1; \ +} + +IMPLEMENT_ALLOC(32,S32) +IMPLEMENT_ALLOC(16,S16) +IMPLEMENT_ALLOC(8,S8) + +void TSShapeAlloc::checkGuard() +{ + bool check32 = checkGuard32(); + bool check16 = checkGuard16(); + bool check8 = checkGuard8(); + + AssertFatal(check32,avar("TSShapeAlloc::checkGuard32: found %i, wanted %i",getSaveGuard32(),getPrevGuard32())); + AssertFatal(check16,avar("TSShapeAlloc::checkGuard16: found %i, wanted %i",getSaveGuard16(),getPrevGuard16())); + AssertFatal(check8 ,avar("TSShapeAlloc::checkGuard8: found %i, wanted %i",getSaveGuard8() ,getPrevGuard8())); +} + +void TSShapeAlloc::setGuard() +{ + setGuard32(); + setGuard16(); + setGuard8(); +} + + diff --git a/Engine/source/ts/tsShapeAlloc.h b/Engine/source/ts/tsShapeAlloc.h new file mode 100644 index 000000000..562e7d79f --- /dev/null +++ b/Engine/source/ts/tsShapeAlloc.h @@ -0,0 +1,156 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TSSHAPEALLOC_H_ +#define _TSSHAPEALLOC_H_ + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#ifndef _MMATH_H_ +#include "math/mMath.h" +#endif + +/// Alloc structure used in the reading/writing of shapes. +/// +/// In read mode we assemble contents of 32-bit, 16-bit, and 8-bit buffers +/// into a single destination buffer. +/// +/// In write mode we dissemble a stream of memory (which may be scattered in physical memory) +/// into 32-bit, 16-bit, 8-bit, Point3F, and Point2F buffers using function calls similar +/// to the read calls. +/// +/// Read usage: +/// 1. call "setRead" with each incoming memory buffers and clear=true. +/// 2. run through set of operations for allocating and transfering memory to target buffer +/// these are the operations under "DECLARE_ALLOC" that call readOnly in the .cc file. +/// 3. call "doAlloc" to create buffer exactly as large as needed. +/// 4. repeat step 1 & 2 to do the actual transfer of memory, except with clear=false +/// (note: first time through nothing was copied to the shape, we only kept track +/// of the size of the transfer). +/// 5. call getBuffer to get the target (destination buffer) +/// +/// write usage: +/// 1. call "setWrite" (no parameters). +/// 2. run through set of operations for allocating and transfering memory to internal buffers +/// these are the operations under "DECLARE_ALLOC" that call writeOnly in the .cc file. +/// 3. call getBuffer32 and getBufferSize32 to get 32-bit buffer and size. Similarly for +/// 16-bit, 8-bit (getBuffer16, getBuffer8). +/// +/// TSShape::assesmbleShape and TSShape::dissembleShape can be used as examples +class TSShapeAlloc +{ + S32 mMode; ///< read or write + + /// reading and writing (when reading these are the input; when writing these are the output) + S32 * mMemBuffer32; + S16 * mMemBuffer16; + S8 * mMemBuffer8; + + /// for writing only... + S32 mSize32; + S32 mSize16; + S32 mSize8; + S32 mFullSize32; + S32 mFullSize16; + S32 mFullSize8; + + /// reading and writing... + S32 mMemGuard32; + S16 mMemGuard16; + S8 mMemGuard8; + + /// reading + S32 mSaveGuard32; + S16 mSaveGuard16; + S8 mSaveGuard8; + + /// reading only...this is the output + S8 * mDest; + S32 mSize; + S32 mMult; ///< mult incoming sizes by this (when 0, then mDest doesn't grow --> skip mode) + + public: + + enum { ReadMode = 0, WriteMode = 1, PageSize = 1024 }; ///< PageSize must be multiple of 4 so that we can always + ///< "over-read" up to next dword + + void setRead(S32 * buff32, S16 * buff16, S8 * buff8, bool clear); + void setWrite(); + + // reading only... + void doAlloc(); + void align32(); ///< align on dword boundary + S8 * getBuffer() { return mDest; } + S32 getSize() { return mSize; } + void setSkipMode(bool skip) { mMult = skip ? 0 : 1; } + + /// @name Reading Operations: + /// + /// get(): reads one or more entries of type from input buffer (doesn't affect output buffer) + /// + /// copyToShape(): copies entries of type from input buffer to output buffer + /// + /// allocShape(): creates room for entries of type in output buffer (no effect on input buffer) + /// + /// getPointer(): gets pointer to next entries of type in input buffer (no effect on input buffer) + /// + /// @note all operations advance current "position" of input and output buffers + /// writing operations: + /// + /// set(): adds one entry to appropriate buffer + /// + /// copyToBuffer(): adds count entries to approrpiate buffer + /// + /// getBuffer(): returns associated buffer (i.e., getBuffer32 gets 32bit buffer) + /// + /// getBufferSize(): returns size of associated buffer + /// + /// @{ + + #define DECLARE_ALLOC(suffix,type) \ + type get##suffix(); \ + void get##suffix(type*,S32); \ + type * copyToShape##suffix(S32,bool returnSomething=false); \ + type * getPointer##suffix(S32); \ + type * allocShape##suffix(S32); \ + bool checkGuard##suffix(); \ + type getPrevGuard##suffix(); \ + type getSaveGuard##suffix(); \ + type * getBuffer##suffix(); \ + S32 getBufferSize##suffix(); \ + void setGuard##suffix(); \ + type * extend##suffix(S32); \ + type set##suffix(type); \ + void copyToBuffer##suffix(type*,S32); + + + DECLARE_ALLOC(32,S32) + DECLARE_ALLOC(16,S16) + DECLARE_ALLOC(8,S8) + /// @} + + void checkGuard(); + void setGuard(); +}; + +#endif // _H_TS_SHAPE_ALLOC_ diff --git a/Engine/source/ts/tsShapeConstruct.cpp b/Engine/source/ts/tsShapeConstruct.cpp new file mode 100644 index 000000000..b4036c9ce --- /dev/null +++ b/Engine/source/ts/tsShapeConstruct.cpp @@ -0,0 +1,3254 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "ts/tsShapeConstruct.h" + +#include "ts/tsShapeInstance.h" +#include "ts/tsMaterialList.h" +#include "console/consoleTypes.h" +#include "console/engineAPI.h" +#include "core/resourceManager.h" +#include "core/stream/bitStream.h" +#include "core/stream/fileStream.h" +#include "core/stream/memStream.h" +#include "core/fileObject.h" + +#define MAX_PATH_LENGTH 256 + + +//#define DEBUG_SPEW + +ConsoleDocClass( TSShapeConstructor, + "@brief An object used to modify a DTS or COLLADA shape model after it has " + "been loaded by Torque\n\n" + "@ingroup gameObjects\n" +); + +IMPLEMENT_CALLBACK( TSShapeConstructor, onLoad, void, (), (), + "Called immediately after the DTS or DAE file has been loaded; before the " + "shape data is available to any other object (StaticShape, Player etc). This " + "is where you should put any post-load commands to modify the shape in-memory " + "such as addNode, renameSequence etc." ) + +IMPLEMENT_CALLBACK( TSShapeConstructor, onUnload, void, (), (), + "Called when the DTS or DAE resource is flushed from memory. Not normally " + "required, but may be useful to perform cleanup." ) + +ImplementEnumType( TSShapeConstructorUpAxis, + "Axis to use for upwards direction when importing from Collada.\n\n" + "@ingroup TSShapeConstructor" ) + { UPAXISTYPE_X_UP, "X_AXIS" }, + { UPAXISTYPE_Y_UP, "Y_AXIS" }, + { UPAXISTYPE_Z_UP, "Z_AXIS" }, + { UPAXISTYPE_COUNT, "DEFAULT" } +EndImplementEnumType; + +ImplementEnumType( TSShapeConstructorLodType, + "\n\n" + "@ingroup TSShapeConstructor" ) + { ColladaUtils::ImportOptions::DetectDTS, "DetectDTS" }, + { ColladaUtils::ImportOptions::SingleSize, "SingleSize" }, + { ColladaUtils::ImportOptions::TrailingNumber, "TrailingNumber" }, +EndImplementEnumType; + + +//----------------------------------------------------------------------------- + +ResourceRegisterPostLoadSignal< TSShape > TSShapeConstructor::_smAutoLoad( &TSShapeConstructor::_onTSShapeLoaded ); +ResourceRegisterUnloadSignal< TSShape > TSShapeConstructor::_smAutoUnload( &TSShapeConstructor::_onTSShapeUnloaded ); + +void TSShapeConstructor::_onTSShapeLoaded( Resource< TSShape >& resource ) +{ + TSShapeConstructor* ctor = findShapeConstructor( resource.getPath().getFullPath() ); + if( ctor ) + ctor->_onLoad( resource ); +} + +void TSShapeConstructor::_onTSShapeUnloaded( const Torque::Path& path, TSShape* shape ) +{ + TSShapeConstructor* ctor = findShapeConstructor( path.getFullPath() ); + if( ctor && ( ctor->getShape() == shape ) ) + ctor->_onUnload(); +} + +// TSShape names are case insensitive +static inline bool namesEqual( const String& nameA, const String& nameB ) +{ + return nameA.equal( nameB, String::NoCase ); +} + +static void SplitSequencePathAndName( String& srcPath, String& srcName ) +{ + srcName = ""; + + // Determine if there is a sequence name at the end of the source string, and + // if so, split the filename from the sequence name + S32 split = srcPath.find(' ', 0, String::Right); + S32 split2 = srcPath.find('\t', 0, String::Right); + if ((split == String::NPos) || (split2 > split)) + split = split2; + if (split != String::NPos) + { + split2 = split + 1; + while ((srcPath[split2] != '\0') && dIsspace(srcPath[split2])) + split2++; + + // now 'split' is at the end of the path, and 'split2' is at the start of the sequence name + srcName = srcPath.substr(split2); + srcPath = srcPath.erase(split, srcPath.length()-split); + } +} + +//----------------------------------------------------------------------------- + +IMPLEMENT_CONOBJECT(TSShapeConstructor); + +TSShapeConstructor::TSShapeConstructor() + : mShapePath("") +{ + mShape = NULL; +} + +TSShapeConstructor::~TSShapeConstructor() +{ +} + +bool TSShapeConstructor::addSequenceFromField( void *obj, const char *index, const char *data ) +{ + TSShapeConstructor *pObj = static_cast( obj ); + + if ( data && data[0] ) + pObj->mSequences.push_back( FileName(data) ); + + return false; +} + +void TSShapeConstructor::initPersistFields() +{ + addGroup( "Media" ); + addField( "baseShape", TypeStringFilename, Offset(mShapePath, TSShapeConstructor), + "Specifies the path to the DTS or DAE file to be operated on by this object.\n" + "Since the TSShapeConstructor script must be in the same folder as the DTS or " + "DAE file, it is recommended to use a relative path so that the shape and " + "script files can be copied to another location without having to modify the " + "path." ); + endGroup( "Media" ); + + addGroup( "Collada" ); + addField( "upAxis", TYPEID< domUpAxisType >(), Offset(mOptions.upAxis, TSShapeConstructor), + "Override the element in the COLLADA (.dae) file. No effect for DTS files.\n" + "Set to one of the following values:\n" + "
      X_AXIS
      Positive X points up. Model will be rotated into Torque's coordinate system (Z up).
      " + "
      Y_AXIS
      Positive Y points up. Model will be rotated into Torque's coordinate system (Z up).
      " + "
      Z_AXIS
      Positive Z points up. No rotation will be applied to the model.
      " + "
      DEFAULT
      The default value. Use the value in the .dae file (defaults to Z_AXIS if the element is not present).
      " ); + + addField( "unit", TypeF32, Offset(mOptions.unit, TSShapeConstructor), + "Override the element in the COLLADA (.dae) file. No effect for DTS files.\n" + "COLLADA (.dae) files usually contain a element that indicates the " + "'real world' units that the model is described in. It means you can work " + "in sensible and meaningful units in your modeling app.
      \n" + "For example, if you were modeling a small object like a cup, it might make " + "sense to work in inches (1 MAX unit = 1 inch), but if you were modeling a " + "building, it might make more sense to work in feet (1 MAX unit = 1 foot). " + "If you export both models to COLLADA, T3D will automatically scale them " + "appropriately. 1 T3D unit = 1 meter, so the cup would be scaled down by 0.0254, " + "and the building scaled down by 0.3048, given them both the correct scale " + "relative to each other.
      \n" + "Omit the field or set to -1 to use the value in the .dae file (1.0 if the " + " element is not present)" ); + + addField( "lodType", TYPEID< ColladaUtils::ImportOptions::eLodType >(), Offset(mOptions.lodType, TSShapeConstructor), + "Control how the COLLADA (.dae) importer interprets LOD in the model. No effect for DTS files.\n" + "Set to one of the following values:\n" + "
      DetectDTS
      The default value. Instructs the importer to search for a 'baseXXX->startXXX' node hierarchy at the root level. If found, the importer acts as if ''TrailingNumber'' was set. Otherwise, all geometry is imported at a single detail size.
      " + "
      SingleSize
      All geometry is imported at a fixed detail size. Numbers at the end of geometry node's are ignored.
      " + "
      TrailingNumber
      Numbers at the end of geometry node's name are interpreted as the detail size (similar to DTS exporting). Geometry instances with the same base name but different trailing number are grouped into the same object.
      " + "
      DEFAULT
      The default value. Use the value in the .dae file (defaults to Z_AXIS if the element is not present).
      " ); + + addField( "singleDetailSize", TypeS32, Offset(mOptions.singleDetailSize, TSShapeConstructor), + "Sets the detail size when lodType is set to SingleSize. No effect otherwise, and no effect for DTS files.\n" + "@see lodType" ); + + addField( "matNamePrefix", TypeRealString, Offset(mOptions.matNamePrefix, TSShapeConstructor), + "Prefix to apply to all material map names in the COLLADA (.dae) file. No effect for DTS files.\n" + "This field is useful to avoid material name clashes for exporters that generate generic material " + "names like \"texture0\" or \"material1\"." ); + + addField( "alwaysImport", TypeRealString, Offset(mOptions.alwaysImport, TSShapeConstructor), + "TAB separated patterns of nodes to import even if in neverImport list. No effect for DTS files.\n" + "Torque allows unwanted nodes in COLLADA (.dae) files to to be ignored " + "during import. This field contains a TAB separated list of patterns to " + "match node names. Any node that matches one of the patterns in the list " + "will always be imported, even if it also matches the neverImport list\n" + "@see neverImport\n\n" + "@tsexample\n" + "singleton TSShapeConstructor(MyShapeDae)\n" + "{\n" + " baseShape = \"./myShape.dae\";\n" + " alwaysImport = \"mount*\" TAB \"eye\";\n" + " neverImport = \"*-PIVOT\";\n" + "}\n" + "@endtsexample" ); + + addField( "neverImport", TypeRealString, Offset(mOptions.neverImport, TSShapeConstructor), + "TAB separated patterns of nodes to ignore on loading. No effect for DTS files.\n" + "Torque allows unwanted nodes in COLLADA (.dae) files to to be ignored " + "during import. This field contains a TAB separated list of patterns to " + "match node names. Any node that matches one of the patterns in the list will " + "not be imported (unless it matches the alwaysImport list.\n" + "@see alwaysImport" ); + + addField( "alwaysImportMesh", TypeRealString, Offset(mOptions.alwaysImportMesh, TSShapeConstructor), + "TAB separated patterns of meshes to import even if in neverImportMesh list. No effect for DTS files.\n" + "Torque allows unwanted meshes in COLLADA (.dae) files to to be ignored " + "during import. This field contains a TAB separated list of patterns to " + "match mesh names. Any mesh that matches one of the patterns in the list " + "will always be imported, even if it also matches the neverImportMesh list\n" + "@see neverImportMesh\n\n" + "@tsexample\n" + "singleton TSShapeConstructor(MyShapeDae)\n" + "{\n" + " baseShape = \"./myShape.dae\";\n" + " alwaysImportMesh = \"body*\" TAB \"armor\" TAB \"bounds\";\n" + " neverImportMesh = \"*-dummy\";\n" + "}\n" + "@endtsexample" ); + + addField( "neverImportMesh", TypeRealString, Offset(mOptions.neverImportMesh, TSShapeConstructor), + "TAB separated patterns of meshes to ignore on loading. No effect for DTS files.\n" + "Torque allows unwanted meshes in COLLADA (.dae) files to to be ignored " + "during import. This field contains a TAB separated list of patterns to " + "match mesh names. Any mesh that matches one of the patterns in the list will " + "not be imported (unless it matches the alwaysImportMesh list.\n" + "@see alwaysImportMesh" ); + + addField( "ignoreNodeScale", TypeBool, Offset(mOptions.ignoreNodeScale, TSShapeConstructor), + "Ignore elements inside COLLADA s. No effect for DTS files.\n" + "This field is a workaround for certain exporters that generate bad node " + "scaling, and is not usually required." ); + + addField( "adjustCenter", TypeBool, Offset(mOptions.adjustCenter, TSShapeConstructor), + "Translate COLLADA model on import so the origin is at the center. No effect for DTS files." ); + + addField( "adjustFloor", TypeBool, Offset(mOptions.adjustFloor, TSShapeConstructor), + "Translate COLLADA model on import so origin is at the (Z axis) bottom of the model. No effect for DTS files.\n" + "This can be used along with adjustCenter to have the origin at the " + "center of the bottom of the model.\n" + "@see adjustCenter" ); + + addField( "forceUpdateMaterials", TypeBool, Offset(mOptions.forceUpdateMaterials, TSShapeConstructor), + "Forces update of the materials.cs file in the same folder as the COLLADA " + "(.dae) file, even if Materials already exist. No effect for DTS files.\n" + "Normally only Materials that are not already defined are written to materials.cs." ); + endGroup( "Collada" ); + + addGroup( "Sequences" ); + addProtectedField( "sequence", TypeStringFilename, NULL, &addSequenceFromField, &emptyStringProtectedGetFn, + "Legacy method of adding sequences to a DTS or DAE shape after loading.\n\n" + "@tsexample\n" + "singleton TSShapeConstructor(MyShapeDae)\n" + "{\n" + " baseShape = \"./myShape.dae\";\n" + " sequence = \"../anims/root.dae root\";\n" + " sequence = \"../anims/walk.dae walk\";\n" + " sequence = \"../anims/jump.dsq jump\";\n" + "}\n" + "@endtsexample" ); + endGroup( "Sequences" ); + + Parent::initPersistFields(); +} + +TSShapeConstructor* TSShapeConstructor::findShapeConstructor(const FileName& path) +{ + SimGroup *group; + if (Sim::findObject( "TSShapeConstructorGroup", group )) + { + // Find the TSShapeConstructor object for the given shape file + for (S32 i = 0; i < group->size(); i++) + { + TSShapeConstructor* tss = dynamic_cast( group->at(i) ); + if ( tss->mShapePath.equal( path, String::NoCase ) ) + return tss; + } + } + return NULL; +} + +//----------------------------------------------------------------------------- +bool TSShapeConstructor::onAdd() +{ + if ( !Parent::onAdd() ) + return false; + + // Prevent multiple objects pointing at the same shape file + TSShapeConstructor* tss = findShapeConstructor( mShapePath ); + if ( tss ) + { + Con::errorf("TSShapeConstructor::onAdd failed: %s is already referenced by " + "another TSShapeConstructor object (%s - %d)", mShapePath.c_str(), + tss->getName(), tss->getId()); + return false; + } + + // Add to the TSShapeConstructor group (for lookups) + SimGroup *group; + if ( !Sim::findObject( "TSShapeConstructorGroup", group ) ) + { + group = new SimGroup(); + if ( !group->registerObject("TSShapeConstructorGroup") ) + { + Con::errorf("TSShapeConstructor::onAdd failed: Could not register " + "TSShapeConstructorGroup"); + return false; + } + Sim::getRootGroup()->addObject( group ); + } + group->addObject( this ); + + // This is only here for backwards compatibility! + // + // If we have no sequences, it may be using the older sequence# syntax. + // Check for dynamic fields of that pattern and add them into the sequence vector. + if ( mSequences.empty() ) + { + for ( U32 idx = 0; idx < MaxLegacySequences; idx++ ) + { + String field = String::ToString( "sequence%d", idx ); + const char *data = getDataField( StringTable->insert(field.c_str()), NULL ); + + // Break at first field not used + if ( !data || !data[0] ) + break; + + // By pushing the field thru Con::setData for TypeStringFilename + // we get the default filename expansion. If we didn't do this + // then we would have unexpanded ~/ in the file paths. + String expanded; + Con::setData( TypeStringFilename, &expanded, 0, 1, &data ); + addSequenceFromField( this, NULL, expanded.c_str() ); + } + } + + // If an instance of this shape has already been loaded, call onLoad now + Resource shape = ResourceManager::get().find( mShapePath ); + if ( shape ) + _onLoad( shape ); + + return true; +} + +//----------------------------------------------------------------------------- + +void TSShapeConstructor::_onLoad(TSShape* shape) +{ + // Check if we should unload first + if ( mShape ) + _onUnload(); + + #ifdef DEBUG_SPEW + Con::printf( "[TSShapeConstructor] attaching to shape '%s'", mShapePath.c_str() ); + #endif + + mShape = shape; + mChangeSet.clear(); + + // Add sequences defined using field syntax + for ( S32 i = 0; i < mSequences.size(); i++ ) + { + if ( mSequences[i].isEmpty() ) + continue; + + // Split the sequence path from the target sequence name + String destName; + String srcPath( mSequences[i] ); + SplitSequencePathAndName( srcPath, destName ); + + addSequence( srcPath, destName ); + } + + // Call script function + onLoad_callback(); +} + +//----------------------------------------------------------------------------- + +void TSShapeConstructor::_onUnload() +{ + #ifdef DEBUG_SPEW + Con::printf( "[TSShapeConstructor] detaching from '%s'", mShapePath.c_str() ); + #endif + + onUnload_callback(); + + mShape = NULL; +} + +//----------------------------------------------------------------------------- +// Storage + +bool TSShapeConstructor::writeField(StringTableEntry fieldname, const char *value) +{ + // Ignore the sequence fields (these are written as 'addSequence' commands instead) + if ( dStrnicmp( fieldname, "sequence", 8 ) == 0 ) + return false; + else if ( dStrnicmp( fieldname, "baseShape", 9 ) == 0 ) + { + // Small hack to only write the base filename (no path) since the + // TSShapeConstructor script must be in the same folder as the model, and + // then we can easily copy both around without having to change the field + const char* filename = dStrrchr( value, '/' ); + if ( filename > value ) + { + S32 len = dStrlen( filename ); + dMemmove((void*)(value + 1), filename, len ); + ((char*)value)[0] = '.'; + ((char*)value)[len + 1] = '\0'; + } + return true; + } + + return Parent::writeField( fieldname, value ); +} + +//----------------------------------------------------------------------------- +// Console utility methods + +// These macros take care of doing node, sequence and object lookups, including +// printing error messages to the console if the element cannot be found. + +// Check that the given index is valid (0 - max-1). If not, generate an +// error and return. +#define CHECK_INDEX_IN_RANGE(func, index, maxIndex, ret) \ + if ( ( index < 0 ) || ( index >= maxIndex ) ) \ + { \ + Con::errorf( "TSShapeConstructor::" #func ": index out of " \ + "range (0-%d)", maxIndex-1); \ + return ret; \ + } + +// Do a node lookup and allow the root node name ("") +#define GET_NODE_INDEX_ALLOW_ROOT(func, var, name, ret) \ + S32 var##Index = -1; \ + if (name[0]) \ + { \ + var##Index = mShape->findNode(name); \ + if (var##Index < 0) \ + { \ + Con::errorf( "TSShapeConstructor::" #func ": Could not " \ + "find node '%s'", name); \ + return ret; \ + } \ + } \ + TSShape::Node* var = var##Index < 0 ? NULL : &(mShape->nodes[var##Index]); \ + TORQUE_UNUSED(var##Index); \ + TORQUE_UNUSED(var) + +// Do a node lookup, root node ("") is not allowed +#define GET_NODE_INDEX_NO_ROOT(func, var, name, ret) \ + S32 var##Index = mShape->findNode(name); \ + if (var##Index < 0) \ + { \ + Con::errorf( "TSShapeConstructor::" #func ": Could not find " \ + "node '%s'", name); \ + return ret; \ + } \ + TSShape::Node* var = &(mShape->nodes[var##Index]); \ + TORQUE_UNUSED(var##Index); \ + TORQUE_UNUSED(var) + +// Do an object lookup +#define GET_OBJECT(func, var, name, ret) \ + S32 var##Index = mShape->findObject(name); \ + if (var##Index < 0) \ + { \ + Con::errorf( "TSShapeConstructor::" #func ": Could not find " \ + "object '%s'", name); \ + return ret; \ + } \ + TSShape::Object* var = &(mShape->objects[var##Index]); \ + TORQUE_UNUSED(var##Index); \ + TORQUE_UNUSED(var) + +// Do a mesh lookup +#define GET_MESH(func, var, name, ret) \ + TSMesh* var = mShape->findMesh(name); \ + if (!var) \ + { \ + Con::errorf( "TSShapeConstructor::" #func ": Could not find " \ + "mesh '%s'", name); \ + return ret; \ + } + +// Do a sequence lookup +#define GET_SEQUENCE(func, var, name, ret) \ + S32 var##Index = mShape->findSequence(name); \ + if (var##Index < 0) \ + { \ + Con::errorf( "TSShapeConstructor::" #func ": Could not find " \ + "sequence named '%s'", name); \ + return ret; \ + } \ + TSShape::Sequence* var = &(mShape->sequences[var##Index]); \ + TORQUE_UNUSED(var##Index); \ + TORQUE_UNUSED(var); + + +//----------------------------------------------------------------------------- +// DUMP +DefineTSShapeConstructorMethod( dumpShape, void, ( const char* filename ), ( "" ), + ( filename ),, + "Dump the shape hierarchy to the console or to a file. Useful for reviewing " + "the result of a series of construction commands.\n" + "@param filename Destination filename. If not specified, dump to console.\n\n" + "@tsexample\n" + "%this.dumpShape(); // dump to console\n" + "%this.dumpShape( \"./dump.txt\" ); // dump to file\n" + "@endtsexample\n" ) +{ + TSShapeInstance* tsi = new TSShapeInstance( mShape, false ); + + if ( dStrEqual( filename, "" ) ) + { + // Dump the constructed shape to a memory stream + MemStream* dumpStream = new MemStream( 8192 ); + tsi->dump( *dumpStream ); + + // Write stream to the console + U32 end = dumpStream->getPosition(); + dumpStream->setPosition( 0 ); + while ( dumpStream->getPosition() < end ) + { + char line[1024]; + dumpStream->readLine( (U8*)line, sizeof(line) ); + Con::printf( line ); + } + + delete dumpStream; + } + else + { + // Dump constructed shape to file + char filenameBuf[1024]; + Con::expandScriptFilename( filenameBuf, sizeof(filenameBuf), filename ); + + FileStream* dumpStream = new FileStream; + if ( dumpStream->open( filenameBuf, Torque::FS::File::Write ) ) + { + tsi->dump( *dumpStream ); + dumpStream->close(); + } + else + Con::errorf( "dumpShape failed: Could not open file '%s' for writing", filenameBuf ); + + delete dumpStream; + } + + delete tsi; +}} + +DefineTSShapeConstructorMethod( saveShape, void, ( const char* filename ),, + ( filename ),, + "Save the shape (with all current changes) to a new DTS file.\n" + "@param filename Destination filename.\n\n" + "@tsexample\n" + "%this.saveShape( \"./myShape.dts\" );\n" + "@endtsexample\n" ) +{ + char filenameBuf[1024]; + Con::expandScriptFilename( filenameBuf, sizeof(filenameBuf), filename ); + + FileStream* dtsStream = new FileStream; + if ( dtsStream->open( filenameBuf, Torque::FS::File::Write ) ) + { + mShape->write( dtsStream ); + dtsStream->close(); + } + else + { + Con::errorf( "saveShape failed: Could not open '%s' for writing", filenameBuf ); + } + delete dtsStream; +}} + +DefineTSShapeConstructorMethod( writeChangeSet, void, (),, + (),, + "Write the current change set to a TSShapeConstructor script file. The " + "name of the script file is the same as the model, but with .cs extension. " + "eg. myShape.cs for myShape.dts or myShape.dae.\n" ) +{ + Torque::Path scriptPath( mShapePath ); + scriptPath.setExtension( "cs" ); + + // Read current file contents + FileObject f; + f.readMemory( scriptPath.getFullPath() ); + + // Write new file + FileStream *stream; + if ((stream = FileStream::createAndOpen( scriptPath.getFullPath(), Torque::FS::File::Write )) == NULL) + { + Con::errorf( "Failed to write TSShapeConstructor change set to %s", scriptPath.getFullPath().c_str() ); + return; + } + + // Write existing file contents up to the start of the onLoad function + String beginMessage( avar( "function %s::onLoad(%%this)", getName() ) ); + String endMessage( "}" ); + + while ( !f.isEOF() ) + { + const char* buffer = (const char *) f.readLine(); + if ( !dStrcmp( buffer, beginMessage )) + break; + stream->writeText( buffer ); + stream->writeText( "\r\n" ); + } + + // Write the new contents + if ( f.isEOF() ) + stream->writeText( "\r\n" ); + stream->writeText( beginMessage ); + stream->writeText( "\r\n{\r\n" ); + + mChangeSet.write( mShape, *stream, scriptPath.getPath() ); + + stream->writeText( endMessage ); + stream->writeText( "\r\n" ); + + // Now skip the contents of the function + while ( !f.isEOF() ) + { + const char* buffer = (const char *) f.readLine(); + if ( !dStrcmp( buffer, endMessage )) + break; + } + + // Write the remainder of the existing file contents + while( !f.isEOF() ) + { + const char* buffer = (const char *) f.readLine(); + stream->writeText( buffer ); + stream->writeText( "\r\n" ); + } + + delete stream; +}} + +DefineTSShapeConstructorMethod( notifyShapeChanged, void, (),, + (),, + "Notify game objects that this shape file has changed, allowing them to update " + "internal data if needed." ) +{ + ResourceManager::get().getChangedSignal().trigger( mShapePath ); +}} + +//----------------------------------------------------------------------------- +// NODES +DefineTSShapeConstructorMethod( getNodeCount, S32, (),, + (), 0, + "Get the total number of nodes in the shape.\n" + "@return the number of nodes in the shape.\n\n" + "@tsexample\n" + "%count = %this.getNodeCount();\n" + "@endtsexample\n" ) +{ + return mShape->nodes.size(); +}} + +DefineTSShapeConstructorMethod( getNodeIndex, S32, ( const char* name ),, + ( name ), -1, + "Get the index of the node.\n" + "@param name name of the node to lookup.\n" + "@return the index of the named node, or -1 if no such node exists.\n\n" + "@tsexample\n" + "// get the index of Bip01 Pelvis node in the shape\n" + "%index = %this.getNodeIndex( \"Bip01 Pelvis\" );\n" + "@endtsexample\n" ) +{ + return mShape->findNode( name ); +}} + +DefineTSShapeConstructorMethod( getNodeName, const char*, ( S32 index ),, + ( index ), "", + "Get the name of the indexed node.\n" + "@param index index of the node to lookup (valid range is 0 - getNodeCount()-1).\n" + "@return the name of the indexed node, or \"\" if no such node exists.\n\n" + "@tsexample\n" + "// print the names of all the nodes in the shape\n" + "%count = %this.getNodeCount();\n" + "for (%i = 0; %i < %count; %i++)\n" + " echo(%i SPC %this.getNodeName(%i));\n" + "@endtsexample\n" ) +{ + CHECK_INDEX_IN_RANGE( getNodeName, index, mShape->nodes.size(), "" ); + return mShape->getName( mShape->nodes[index].nameIndex ); +}} + +DefineTSShapeConstructorMethod( getNodeParentName, const char*, ( const char* name ),, + ( name ), "", + "Get the name of the node's parent. If the node has no parent (ie. it is at " + "the root level), return an empty string.\n" + "@param name name of the node to query.\n" + "@return the name of the node's parent, or \"\" if the node is at the root level\n\n" + "@tsexample\n" + "echo( \"Bip01 Pelvis parent = \" @ %this.getNodeParentName( \"Bip01 Pelvis \") );\n" + "@endtsexample\n" ) +{ + GET_NODE_INDEX_NO_ROOT( getNodeParentName, node, name, "" ); + + if ( node->parentIndex < 0 ) + return ""; + else + return mShape->getName( mShape->nodes[node->parentIndex].nameIndex ); +}} + +DefineTSShapeConstructorMethod( setNodeParent, bool, ( const char* name, const char* parentName ),, + ( name, parentName ), false, + "Set the parent of a node.\n" + "@param name name of the node to modify\n" + "@param parentName name of the parent node to set (use \"\" to move the node to the root level)\n" + "@return true if successful, false if failed\n\n" + "@tsexample\n" + "%this.setNodeParent( \"Bip01 Pelvis\", \"start01\" );\n" + "@endtsexample\n" ) +{ + GET_NODE_INDEX_NO_ROOT( setNodeParent, node, name, false ); + GET_NODE_INDEX_ALLOW_ROOT( setNodeParent, parent, parentName, false ); + + node->parentIndex = parentIndex; + ADD_TO_CHANGE_SET(); + + return true; +}} + +DefineTSShapeConstructorMethod( getNodeChildCount, S32, ( const char* name ),, + ( name ), 0, + "Get the number of children of this node.\n" + "@param name name of the node to query.\n" + "@return the number of child nodes.\n\n" + "@tsexample\n" + "%count = %this.getNodeChildCount( \"Bip01 Pelvis\" );\n" + "@endtsexample\n" ) +{ + GET_NODE_INDEX_ALLOW_ROOT( getNodeChildCount, node, name, 0 ); + + Vector nodeChildren; + mShape->getNodeChildren( nodeIndex, nodeChildren ); + return nodeChildren.size(); +}} + +DefineTSShapeConstructorMethod( getNodeChildName, const char*, ( const char* name, S32 index ),, + ( name, index ), "", + "Get the name of the indexed child node.\n" + "@param name name of the parent node to query.\n" + "@param index index of the child node (valid range is 0 - getNodeChildName()-1).\n" + "@return the name of the indexed child node.\n\n" + "@tsexample\n" + "function dumpNode( %shape, %name, %indent )\n" + "{\n" + " echo( %indent @ %name );\n" + " %count = %shape.getNodeChildCount( %name );\n" + " for ( %i = 0; %i < %count; %i++ )\n" + " dumpNode( %shape, %shape.getNodeChildName( %name, %i ), %indent @ \" \" );\n" + "}\n\n" + "function dumpShape( %shape )\n" + "{\n" + " // recursively dump node hierarchy\n" + " %count = %shape.getNodeCount();\n" + " for ( %i = 0; %i < %count; %i++ )\n" + " {\n" + " // dump top level nodes\n" + " %name = %shape.getNodeName( %i );\n" + " if ( %shape.getNodeParentName( %name ) $= "" )\n" + " dumpNode( %shape, %name, \"\" );\n" + " }\n" + "}\n" + "@endtsexample\n" ) +{ + GET_NODE_INDEX_ALLOW_ROOT( getNodeChildName, node, name, "" ); + + Vector nodeChildren; + mShape->getNodeChildren( nodeIndex, nodeChildren ); + CHECK_INDEX_IN_RANGE( getNodeChildName, index, nodeChildren.size(), "" ); + + return mShape->getName( mShape->nodes[nodeChildren[index]].nameIndex ); +}} + +DefineTSShapeConstructorMethod( getNodeObjectCount, S32, ( const char* name ),, + ( name ), 0, + "Get the number of geometry objects attached to this node.\n" + "@param name name of the node to query.\n" + "@return the number of attached objects.\n\n" + "@tsexample\n" + "%count = %this.getNodeObjectCount( \"Bip01 Head\" );\n" + "@endtsexample\n" ) +{ + GET_NODE_INDEX_ALLOW_ROOT( getNodeObjectCount, node, name, 0 ); + + Vector nodeObjects; + mShape->getNodeObjects( nodeIndex, nodeObjects ); + return nodeObjects.size(); +}} + +DefineTSShapeConstructorMethod( getNodeObjectName, const char*, ( const char* name, S32 index ),, + ( name, index ), "", + "Get the name of the indexed object.\n" + "@param name name of the node to query.\n" + "@param index index of the object (valid range is 0 - getNodeObjectCount()-1).\n" + "@return the name of the indexed object.\n\n" + "@tsexample\n" + "// print the names of all objects attached to the node\n" + "%count = %this.getNodeObjectCount( \"Bip01 Head\" );\n" + "for ( %i = 0; %i < %count; %i++ )\n" + " echo( %this.getNodeObjectName( \"Bip01 Head\", %i ) );\n" + "@endtsexample\n" ) +{ + GET_NODE_INDEX_ALLOW_ROOT( getNodeObjectName, node, name, "" ); + + Vector nodeObjects; + mShape->getNodeObjects( nodeIndex, nodeObjects ); + CHECK_INDEX_IN_RANGE( getNodeObjectName, index, nodeObjects.size(), "" ); + + return mShape->getName( mShape->objects[nodeObjects[index]].nameIndex ); +}} + +DefineTSShapeConstructorMethod( getNodeTransform, TransformF, ( const char* name, bool isWorld ), ( false ), + ( name, isWorld ), TransformF::Identity, + "Get the base (ie. not animated) transform of a node.\n" + "@param name name of the node to query.\n" + "@param isWorld true to get the global transform, false (or omitted) to get " + "the local-to-parent transform.\n" + "@return the node transform in the form \"pos.x pos.y pos.z rot.x rot.y rot.z rot.angle\".\n\n" + "@tsexample\n" + "%ret = %this.getNodeTransform( \"mount0\" );\n" + "%this.setNodeTransform( \"mount4\", %ret );\n" + "@endtsexample\n" ) +{ + GET_NODE_INDEX_NO_ROOT( getNodeTransform, node, name, TransformF::Identity ); + + // Get the node transform + Point3F pos; + AngAxisF aa; + + if ( isWorld ) + { + // World transform + MatrixF mat; + mShape->getNodeWorldTransform( nodeIndex, &mat ); + pos = mat.getPosition(); + aa.set( mat ); + } + else + { + // Local transform + pos = mShape->defaultTranslations[nodeIndex]; + const Quat16& q16 = mShape->defaultRotations[nodeIndex]; + aa.set( q16.getQuatF() ); + } + + return TransformF( pos, aa ); +}} + +DefineTSShapeConstructorMethod( setNodeTransform, bool, ( const char* name, TransformF txfm, bool isWorld ), ( false ), + ( name, txfm, isWorld ), false, + "Set the base transform of a node. That is, the transform of the node when " + "in the root (not-animated) pose.\n" + "@param name name of the node to modify\n" + "@param txfm transform string of the form: \"pos.x pos.y pos.z rot.x rot.y rot.z rot.angle\"\n" + "@param isworld (optional) flag to set the local-to-parent or the global " + "transform. If false, or not specified, the position and orientation are " + "treated as relative to the node's parent.\n" + "@return true if successful, false otherwise\n\n" + "@tsexample\n" + "%this.setNodeTransform( \"mount0\", \"0 0 1 0 0 1 0\" );\n" + "%this.setNodeTransform( \"mount0\", \"0 0 0 0 0 1 1.57\" );\n" + "%this.setNodeTransform( \"mount0\", \"1 0 0 0 0 1 0\", true );\n" + "@endtsexample\n" ) +{ + GET_NODE_INDEX_NO_ROOT( setNodeTransform, node, name, false ); + + Point3F pos( txfm.getPosition() ); + QuatF rot( txfm.getOrientation() ); + + if ( isWorld ) + { + // World transform + + // Get the node's parent (if any) + if ( node->parentIndex != -1 ) + { + MatrixF mat; + mShape->getNodeWorldTransform( node->parentIndex, &mat ); + + // Pre-multiply by inverse of parent's world transform to get + // local node transform + mat.inverse(); + mat.mul( txfm.getMatrix() ); + + rot.set( mat ); + pos = mat.getPosition(); + } + } + + if ( !mShape->setNodeTransform( name, pos, rot) ) + return false; + + ADD_TO_CHANGE_SET(); + return true; +}} + +DefineTSShapeConstructorMethod( renameNode, bool, ( const char* oldName, const char* newName ),, + ( oldName, newName ), false, + "Rename a node.\n" + "@note Note that node names must be unique, so this command will fail if " + "there is already a node with the desired name\n" + "@param oldName current name of the node\n" + "@param newName new name of the node\n" + "@return true if successful, false otherwise\n\n" + "@tsexample\n" + "%this.renameNode( \"Bip01 L Hand\", \"mount5\" );\n" + "@endtsexample\n" ) +{ + GET_NODE_INDEX_NO_ROOT( renameNode, node, oldName, false ); + + if ( !mShape->renameNode( oldName, newName ) ) + return false; + + ADD_TO_CHANGE_SET(); + return true; +}} + +DefineTSShapeConstructorMethod( addNode, bool, ( const char* name, const char* parentName, TransformF txfm, bool isWorld ), ( TransformF::Identity, false ), + ( name, parentName, txfm, isWorld ), false, + "Add a new node.\n" + "@param name name for the new node (must not already exist)\n" + "@param parentName name of an existing node to be the parent of the new node. " + "If empty (\"\"), the new node will be at the root level of the node hierarchy.\n" + "@param txfm (optional) transform string of the form: \"pos.x pos.y pos.z rot.x rot.y rot.z rot.angle\"\n" + "@param isworld (optional) flag to set the local-to-parent or the global " + "transform. If false, or not specified, the position and orientation are " + "treated as relative to the node's parent.\n" + "@return true if successful, false otherwise\n\n" + "@tsexample\n" + "%this.addNode( \"Nose\", \"Bip01 Head\", \"0 2 2 0 0 1 0\" );\n" + "%this.addNode( \"myRoot\", \"\", \"0 0 4 0 0 1 1.57\" );\n" + "%this.addNode( \"Nodes\", \"Bip01 Head\", \"0 2 0 0 0 1 0\", true );\n" + "@endtsexample\n" ) +{ + Point3F pos( txfm.getPosition() ); + QuatF rot( txfm.getOrientation() ); + + if ( isWorld ) + { + // World transform + + // Get the node's parent (if any) + S32 parentIndex = mShape->findNode( parentName ); + if ( parentIndex != -1 ) + { + MatrixF mat; + mShape->getNodeWorldTransform( parentIndex, &mat ); + + // Pre-multiply by inverse of parent's world transform to get + // local node transform + mat.inverse(); + mat.mul( txfm.getMatrix() ); + + rot.set( mat ); + pos = mat.getPosition(); + } + } + + if ( !mShape->addNode( name, parentName, pos, rot ) ) + return false; + + ADD_TO_CHANGE_SET(); + return true; +}} + +DefineTSShapeConstructorMethod( removeNode, bool, ( const char* name ),, + ( name ), false, + "Remove a node from the shape.\n" + "The named node is removed from the shape, including from any sequences that " + "use the node. Child nodes and objects attached to the node are re-assigned " + "to the node's parent.\n" + "@param name name of the node to remove.\n" + "@return true if successful, false otherwise.\n\n" + "@tsexample\n" + "%this.removeNode( \"Nose\" );\n" + "@endtsexample\n" ) +{ + GET_NODE_INDEX_NO_ROOT( removeNode, node, name, false ); + + if ( !mShape->removeNode( name ) ) + return false; + + ADD_TO_CHANGE_SET(); + return true; +}} + +//----------------------------------------------------------------------------- +// MATERIALS + +DefineTSShapeConstructorMethod( getTargetCount, S32, (),, (), 0, + "Get the number of materials in the shape.\n" + "@return the number of materials in the shape.\n\n" + "@tsexample\n" + "%count = %this.getTargetCount();\n" + "@endtsexample\n" ) +{ + return mShape->getTargetCount(); +}} + +DefineTSShapeConstructorMethod( getTargetName, const char*, ( S32 index ),, + ( index ), "", + "Get the name of the indexed shape material.\n" + "@param index index of the material to get (valid range is 0 - getTargetCount()-1).\n" + "@return the name of the indexed material.\n\n" + "@tsexample\n" + "%count = %this.getTargetCount();\n" + "for ( %i = 0; %i < %count; %i++ )\n" + " echo( \"Target \" @ %i @ \": \" @ %this.getTargetName( %i ) );\n" + "@endtsexample\n" ) +{ + return mShape->getTargetName( index ); +}} + +//----------------------------------------------------------------------------- +// OBJECTS + +DefineTSShapeConstructorMethod( getObjectCount, S32, (),, (), 0, + "Get the total number of objects in the shape.\n" + "@return the number of objects in the shape.\n\n" + "@tsexample\n" + "%count = %this.getObjectCount();\n" + "@endtsexample\n" ) +{ + return mShape->objects.size(); +}} + +DefineTSShapeConstructorMethod( getObjectName, const char*, ( S32 index ),, + ( index ), "", + "Get the name of the indexed object.\n" + "@param index index of the object to get (valid range is 0 - getObjectCount()-1).\n" + "@return the name of the indexed object.\n\n" + "@tsexample\n" + "// print the names of all objects in the shape\n" + "%count = %this.getObjectCount();\n" + "for ( %i = 0; %i < %count; %i++ )\n" + " echo( %i SPC %this.getObjectName( %i ) );\n" + "@endtsexample\n" ) +{ + CHECK_INDEX_IN_RANGE( getObjectName, index, mShape->objects.size(), "" ); + + return mShape->getName( mShape->objects[index].nameIndex ); +}} + +DefineTSShapeConstructorMethod( getObjectIndex, S32, ( const char* name ),, + ( name ), -1, + "Get the index of the first object with the given name.\n" + "@param name name of the object to get.\n" + "@return the index of the named object.\n\n" + "@tsexample\n" + "%index = %this.getObjectIndex( \"Head\" );\n" + "@endtsexample\n" ) +{ + return mShape->findObject( name ); +}} + +DefineTSShapeConstructorMethod( getObjectNode, const char*, ( const char* name ),, + ( name ), "", + "Get the name of the node this object is attached to.\n" + "@param name name of the object to get.\n" + "@return the name of the attached node, or an empty string if this " + "object is not attached to a node (usually the case for skinned meshes).\n\n" + "@tsexample\n" + "echo( \"Hand is attached to \" @ %this.getObjectNode( \"Hand\" ) );\n" + "@endtsexample\n" ) +{ + GET_OBJECT( getObjectNode, obj, name, 0 ); + if ( obj->nodeIndex < 0 ) + return ""; + else + return mShape->getName( mShape->nodes[obj->nodeIndex].nameIndex ); +}} + +DefineTSShapeConstructorMethod( setObjectNode, bool, ( const char* objName, const char* nodeName ),, + ( objName, nodeName ), false, + "Set the node an object is attached to.\n" + "When the shape is rendered, the object geometry is rendered at the node's " + "current transform.\n" + "@param objName name of the object to modify\n" + "@param nodeName name of the node to attach the object to\n" + "@return true if successful, false otherwise\n\n" + "@tsexample\n" + "%this.setObjectNode( \"Hand\", \"Bip01 LeftHand\" );\n" + "@endtsexample\n" ) +{ + if ( !mShape->setObjectNode( objName, nodeName ) ) + return false; + + ADD_TO_CHANGE_SET(); + return true; +}} + +DefineTSShapeConstructorMethod( renameObject, bool, ( const char* oldName, const char* newName ),, + ( oldName, newName ), false, + "Rename an object.\n" + "@note Note that object names must be unique, so this command will fail if " + "there is already an object with the desired name\n" + "@param oldName current name of the object\n" + "@param newName new name of the object\n" + "@return true if successful, false otherwise\n\n" + "@tsexample\n" + "%this.renameObject( \"MyBox\", \"Box\" );\n" + "@endtsexample\n" ) +{ + if ( !mShape->renameObject( oldName, newName ) ) + return false; + + ADD_TO_CHANGE_SET(); + return true; +}} + +DefineTSShapeConstructorMethod( removeObject, bool, ( const char* name ),, + ( name ), false, + "Remove an object (including all meshes for that object) from the shape.\n" + "@param name name of the object to remove.\n" + "@return true if successful, false otherwise.\n\n" + "@tsexample\n" + "// clear all objects in the shape\n" + "%count = %this.getObjectCount();\n" + "for ( %i = %count-1; %i >= 0; %i-- )\n" + " %this.removeObject( %this.getObjectName(%i) );\n" + "@endtsexample\n" ) +{ + if ( !mShape->removeObject( name ) ) + return false; + + ADD_TO_CHANGE_SET(); + return true; +}} + +//----------------------------------------------------------------------------- +// MESHES +DefineTSShapeConstructorMethod( getMeshCount, S32, ( const char* name ),, + ( name ), 0, + "Get the number of meshes (detail levels) for the specified object.\n" + "@param name name of the object to query\n" + "@return the number of meshes for this object.\n\n" + "@tsexample\n" + "%count = %this.getMeshCount( \"SimpleShape\" );\n" + "@endtsexample\n" ) +{ + GET_OBJECT( getMeshCount, obj, name, 0 ); + + Vector objectDetails; + mShape->getObjectDetails( objIndex, objectDetails ); + return objectDetails.size(); +}} + +DefineTSShapeConstructorMethod( getMeshName, const char*, ( const char* name, S32 index ),, + ( name, index ), "", + "Get the name of the indexed mesh (detail level) for the specified object.\n" + "@param name name of the object to query\n" + "@param index index of the mesh (valid range is 0 - getMeshCount()-1)\n" + "@return the mesh name.\n\n" + "@tsexample\n" + "// print the names of all meshes in the shape\n" + "%objCount = %this.getObjectCount();\n" + "for ( %i = 0; %i < %objCount; %i++ )\n" + "{\n" + " %objName = %this.getObjectName( %i );\n" + " %meshCount = %this.getMeshCount( %objName );\n" + " for ( %j = 0; %j < %meshCount; %j++ )\n" + " echo( %this.getMeshName( %objName, %j ) );\n" + "}\n" + "@endtsexample\n" ) +{ + GET_OBJECT( getMeshName, obj, name, "" ); + + Vector objectDetails; + mShape->getObjectDetails(objIndex, objectDetails); + + CHECK_INDEX_IN_RANGE( getMeshName, index, objectDetails.size(), "" ); + + char* returnBuffer = Con::getReturnBuffer(256); + dSprintf(returnBuffer, 256, "%s %d", name, (S32)mShape->details[objectDetails[index]].size); + return returnBuffer; +}} + +DefineTSShapeConstructorMethod( getMeshSize, S32, ( const char* name, S32 index ),, + ( name, index ), -1, + "Get the detail level size of the indexed mesh for the specified object.\n" + "@param name name of the object to query\n" + "@param index index of the mesh (valid range is 0 - getMeshCount()-1)\n" + "@return the mesh detail level size.\n\n" + "@tsexample\n" + "// print sizes for all detail levels of this object\n" + "%objName = \"trunk\";\n" + "%count = %this.getMeshCount( %objName );\n" + "for ( %i = 0; %i < %count; %i++ )\n" + " echo( %this.getMeshSize( %objName, %i ) );\n" + "@endtsexample\n" ) +{ + GET_OBJECT( getMeshName, obj, name, -1 ); + + Vector objectDetails; + mShape->getObjectDetails( objIndex, objectDetails ); + + CHECK_INDEX_IN_RANGE( getMeshName, index, objectDetails.size(), -1 ); + + return (S32)mShape->details[objectDetails[index]].size; +}} + +DefineTSShapeConstructorMethod( setMeshSize, bool, ( const char* name, S32 size ),, + ( name, size ), false, + "Change the detail level size of the named mesh.\n" + "@param name full name (object name + current size ) of the mesh to modify\n" + "@param size new detail level size\n" + "@return true if successful, false otherwise.\n\n" + "@tsexample\n" + "%this.setMeshSize( \"SimpleShape128\", 64 );\n" + "@endtsexample\n" ) +{ + if ( !mShape->setMeshSize( name, size ) ) + return false; + + ADD_TO_CHANGE_SET(); + return true; +}} + +DefineTSShapeConstructorMethod( getMeshType, const char*, ( const char* name ),, + ( name ), "", + "Get the display type of the mesh.\n" + "@param name name of the mesh to query\n" + "@return the string returned is one of:" + "
      normal
      a normal 3D mesh
      " + "
      billboard
      a mesh that always faces the camera
      " + "
      billboardzaxis
      a mesh that always faces the camera in the Z-axis
      \n\n" + "@tsexample\n" + "echo( \"Mesh type is \" @ %this.getMeshType( \"SimpleShape128\" ) );\n" + "@endtsexample\n" ) +{ + GET_MESH( getMeshType, mesh, name, "normal" ); + + if (mesh->getFlags(TSMesh::BillboardZAxis)) + return "billboardzaxis"; + else if (mesh->getFlags(TSMesh::Billboard)) + return "billboard"; + else + return "normal"; +}} + +DefineTSShapeConstructorMethod( setMeshType, bool, ( const char* name, const char* type ),, + ( name, type ), false, + "Set the display type for the mesh.\n" + "@param name full name (object name + detail size) of the mesh to modify\n" + "@param type the new type for the mesh: \"normal\", \"billboard\" or \"billboardzaxis\"\n" + "@return true if successful, false otherwise\n\n" + "@tsexample\n" + "// set the mesh to be a billboard\n" + "%this.setMeshType( \"SimpleShape64\", \"billboard\" );\n" + "@endtsexample\n" ) +{ + GET_MESH( setMeshType, mesh, name, false ); + + // Update the mesh flags + mesh->clearFlags( TSMesh::Billboard | TSMesh::BillboardZAxis ); + if ( dStrEqual( type, "billboard" ) ) + mesh->setFlags( TSMesh::Billboard ); + else if ( dStrEqual( type, "billboardzaxis" ) ) + mesh->setFlags( TSMesh::Billboard | TSMesh::BillboardZAxis ); + else if ( !dStrEqual( type, "normal" ) ) + { + Con::printf( "setMeshType: Unknown mesh type '%s'", type ); + return false; + } + + ADD_TO_CHANGE_SET(); + return true; +}} + +DefineTSShapeConstructorMethod( getMeshMaterial, const char*, ( const char* name ),, + ( name ), "", + "Get the name of the material attached to a mesh. Note that only the first " + "material used by the mesh is returned.\n" + "@param name full name (object name + detail size) of the mesh to query\n" + "@return name of the material attached to the mesh (suitable for use with the Material mapTo field)\n\n" + "@tsexample\n" + "echo( \"Mesh material is \" @ %this.sgetMeshMaterial( \"SimpleShape128\" ) );\n" + "@endtsexample\n" ) +{ + GET_MESH( getMeshMaterial, mesh, name, "" ); + + // Return the name of the first material attached to this mesh + S32 matIndex = mesh->primitives[0].matIndex & TSDrawPrimitive::MaterialMask; + if ((matIndex >= 0) && (matIndex < mShape->materialList->size())) + return mShape->materialList->getMaterialName( matIndex ); + else + return ""; +}} + +DefineTSShapeConstructorMethod( setMeshMaterial, bool, ( const char* meshName, const char* matName ),, + ( meshName, matName ), false, + "Set the name of the material attached to the mesh.\n" + "@param meshName full name (object name + detail size) of the mesh to modify\n" + "@param matName name of the material to attach. This could be the base name of " + "the diffuse texture (eg. \"test_mat\" for \"test_mat.jpg\"), or the name of a " + "Material object already defined in script.\n" + "@return true if successful, false otherwise\n\n" + "@tsexample\n" + "// set the mesh material\n" + "%this.setMeshMaterial( \"SimpleShape128\", \"test_mat\" );\n" + "@endtsexample\n" ) +{ + GET_MESH( setMeshMaterial, mesh, meshName, false ); + + // Check if this material is already in the shape + S32 matIndex; + for ( matIndex = 0; matIndex < mShape->materialList->size(); matIndex++ ) + { + if ( dStrEqual( matName, mShape->materialList->getMaterialName( matIndex ) ) ) + break; + } + if ( matIndex == mShape->materialList->size() ) + { + // Add a new material to the shape + U32 flags = TSMaterialList::S_Wrap | TSMaterialList::T_Wrap; + mShape->materialList->push_back( matName, flags ); + } + + // Set this material for all primitives in the mesh + for ( S32 i = 0; i < mesh->primitives.size(); i++ ) + { + U32 matType = mesh->primitives[i].matIndex & ( TSDrawPrimitive::TypeMask | TSDrawPrimitive::Indexed ); + mesh->primitives[i].matIndex = ( matType | matIndex ); + } + + ADD_TO_CHANGE_SET(); + return true; +}} + +DefineTSShapeConstructorMethod( addMesh, bool, ( const char* meshName, const char* srcShape, const char* srcMesh ),, + ( meshName, srcShape, srcMesh ), false, + "Add geometry from another DTS or DAE shape file into this shape.\n" + "Any materials required by the source mesh are also copied into this shape.
      \n" + "@param meshName full name (object name + detail size) of the new mesh. If " + "no detail size is present at the end of the name, a value of 2 is used.
      " + "An underscore before the number at the end of the name will be interpreted as " + "a negative sign. eg. \"MyMesh_4\" will be interpreted as \"MyMesh-4\".\n" + "@param srcShape name of a shape file (DTS or DAE) that contains the mesh\n" + "@param srcMesh the full name (object name + detail size) of the mesh to " + "copy from the DTS/DAE file into this shape" + "@return true if successful, false otherwise\n\n" + "@tsexample\n" + "%this.addMesh( \"ColMesh-1\", \"./collision.dts\", \"ColMesh\", \"Col-1\" );\n" + "%this.addMesh( \"SimpleShape10\", \"./testShape.dae\", \"MyMesh2\", "" );\n" + "@endtsexample\n" ) +{ + // Load the shape source file + char filenameBuf[1024]; + Con::expandScriptFilename(filenameBuf, sizeof(filenameBuf), srcShape); + + Resource hSrcShape = ResourceManager::get().load( filenameBuf ); + if ( !bool(hSrcShape) ) + { + Con::errorf( "addMesh failed: Could not load source shape: '%s'", filenameBuf ); + return false; + } + + TSShape* shape = const_cast( (const TSShape*)hSrcShape ); + if ( !mShape->addMesh( shape, srcMesh, meshName ) ) + return false; + + ADD_TO_CHANGE_SET(); + return true; +}} + +DefineTSShapeConstructorMethod( removeMesh, bool, ( const char* name ),, + ( name ), false, + "Remove a mesh from the shape.\n" + "If all geometry is removed from an object, the object is also removed.\n" + "@param name full name (object name + detail size) of the mesh to remove\n" + "@return true if successful, false otherwise\n\n" + "@tsexample\n" + "%this.removeMesh( \"SimpleShape128\" );\n" + "@endtsexample\n" ) +{ + if ( !mShape->removeMesh( name ) ) + return false; + + ADD_TO_CHANGE_SET(); + return true; +}} + +DefineTSShapeConstructorMethod( getBounds, Box3F, (),, + (), Box3F::Invalid, + "Get the bounding box for the shape.\n" + "@return Bounding box \"minX minY minZ maxX maxY maxZ\"" ) +{ + return mShape->bounds; +}} + +DefineTSShapeConstructorMethod( setBounds, bool, ( Box3F bbox ),, + ( bbox ), false, + "Set the shape bounds to the given bounding box.\n" + "@param Bounding box \"minX minY minZ maxX maxY maxZ\"\n" + "@return true if successful, false otherwise\n" ) +{ + // Set shape bounds + TSShape* shape = mShape; + + shape->bounds = bbox; + shape->bounds.getCenter( &shape->center ); + shape->radius = ( shape->bounds.maxExtents - shape->center ).len(); + shape->tubeRadius = shape->radius; + + ADD_TO_CHANGE_SET(); + return true; +}} + +//----------------------------------------------------------------------------- +// DETAILS +DefineTSShapeConstructorMethod( getDetailLevelCount, S32, (),, (), 0, + "Get the total number of detail levels in the shape.\n" + "@return the number of detail levels in the shape\n" ) +{ + return mShape->details.size(); +}} + +DefineTSShapeConstructorMethod( getDetailLevelName, const char*, ( S32 index ),, + ( index ), "", + "Get the name of the indexed detail level.\n" + "@param index detail level index (valid range is 0 - getDetailLevelCount()-1)\n" + "@return the detail level name\n\n" + "@tsexample\n" + "// print the names of all detail levels in the shape\n" + "%count = %this.getDetailLevelCount();\n" + "for ( %i = 0; %i < %count; %i++ )\n" + " echo( %i SPC %this.getDetailLevelName( %i ) );\n" + "@endtsexample\n" ) +{ + CHECK_INDEX_IN_RANGE( getDetailLevelName, index, mShape->details.size(), "" ); + + return mShape->getName(mShape->details[index].nameIndex); +}} + +DefineTSShapeConstructorMethod( getDetailLevelSize, S32, ( S32 index),, + ( index ), 0, + "Get the size of the indexed detail level.\n" + "@param index detail level index (valid range is 0 - getDetailLevelCount()-1)\n" + "@return the detail level size\n\n" + "@tsexample\n" + "// print the sizes of all detail levels in the shape\n" + "%count = %this.getDetailLevelCount();\n" + "for ( %i = 0; %i < %count; %i++ )\n" + " echo( \"Detail\" @ %i @ \" has size \" @ %this.getDetailLevelSize( %i ) );\n" + "@endtsexample\n" ) +{ + CHECK_INDEX_IN_RANGE( getDetailLevelSize, index, mShape->details.size(), 0 ); + + return (S32)mShape->details[index].size; +}} + +DefineTSShapeConstructorMethod( getDetailLevelIndex, S32, ( S32 size ),, + ( size ), -1, + "Get the index of the detail level with a given size.\n" + "@param size size of the detail level to lookup\n" + "@return index of the detail level with the desired size, or -1 if no such " + "detail exists\n\n" + "@tsexample\n" + "if ( %this.getDetailLevelSize( 32 ) == -1 )\n" + " echo( \"Error: This shape does not have a detail level at size 32\" );\n" + "@endtsexample\n" ) +{ + return mShape->findDetailBySize( size ); +}} + +DefineTSShapeConstructorMethod( renameDetailLevel, bool, ( const char* oldName, const char* newName ),, + ( oldName, newName ), false, + "Rename a detail level.\n" + "@note Note that detail level names must be unique, so this command will " + "fail if there is already a detail level with the desired name\n" + "@param oldName current name of the detail level\n" + "@param newName new name of the detail level\n" + "@return true if successful, false otherwise\n\n" + "@tsexample\n" + "%this.renameDetailLevel( \"detail-1\", \"collision-1\" );\n" + "@endtsexample\n" ) +{ + if ( !mShape->renameDetail( oldName, newName ) ) + return false; + + ADD_TO_CHANGE_SET(); + return true; +}} + +DefineTSShapeConstructorMethod( removeDetailLevel, bool, ( S32 index ),, + ( index ), false, + "Remove the detail level (including all meshes in the detail level)\n" + "@param size size of the detail level to remove\n" + "@return true if successful, false otherwise\n" + "@tsexample\n" + "%this.removeDetailLevel( 2 );\n" + "@endtsexample\n" ) +{ + if ( !mShape->removeDetail( index ) ) + return false; + + ADD_TO_CHANGE_SET(); + return true; +}} + +DefineTSShapeConstructorMethod( setDetailLevelSize, S32, ( S32 index, S32 newSize ),, + ( index, newSize ), index, + "Change the size of a detail level." + "@note Note that detail levels are always sorted in decreasing size order, " + "so this command may cause detail level indices to change.\n" + "@param index index of the detail level to modify\n" + "@param newSize new size for the detail level\n" + "@return new index for this detail level\n\n" + "@tsexample\n" + "%this.setDetailLevelSize( 2, 256 );\n" + "@endtsexample\n" ) +{ + S32 dl = mShape->setDetailSize( index, newSize ); + if ( dl >= 0 ) + ADD_TO_CHANGE_SET(); + return dl; +}} + +DefineTSShapeConstructorMethod( getImposterDetailLevel, S32, (),, (), -1, + "Get the index of the imposter (auto-billboard) detail level (if any).\n" + "@return imposter detail level index, or -1 if the shape does not use " + "imposters.\n\n" ) +{ + for ( S32 i = 0; i < mShape->details.size(); i++ ) + { + if ( mShape->details[i].subShapeNum < 0 ) + return i; + } + return -1; +}} + +DefineTSShapeConstructorMethod( getImposterSettings, const char*, ( S32 index ),, + ( index ), "", + "Get the settings used to generate imposters for the indexed detail level.\n" + "@param index index of the detail level to query (does not need to be an " + "imposter detail level\n" + "@return string of the form: \"valid eqSteps pSteps dl dim poles angle\", where:" + "
      " + "
      valid
      1 if this detail level generates imposters, 0 otherwise
      " + "
      eqSteps
      number of steps around the equator
      " + "
      pSteps
      number of steps between the poles
      " + "
      dl
      index of the detail level used to generate imposters
      " + "
      dim
      size (in pixels) of each imposter image
      " + "
      poles
      1 to include pole images, 0 otherwise
      " + "
      angle
      angle at which to display pole images
      " + "
      \n\n" + "@tsexample\n" + "// print the imposter detail level settings\n" + "%index = %this.getImposterDetailLevel();\n" + "if ( %index != -1 )\n" + " echo( \"Imposter settings: \" @ %this.getImposterSettings( %index ) );\n" + "@endtsexample\n" ) +{ + CHECK_INDEX_IN_RANGE( getImposterSettings, index, mShape->details.size(), "" ); + + // Return information about the detail level + const TSShape::Detail& det = mShape->details[index]; + + char* returnBuffer = Con::getReturnBuffer(512); + dSprintf(returnBuffer, 512, "%d\t%d\t%d\t%d\t%d\t%d\t%g", + (S32)( det.subShapeNum < 0 ), // isImposter + det.bbEquatorSteps, + det.bbPolarSteps, + det.bbDetailLevel, + det.bbDimension, + det.bbIncludePoles, + det.bbPolarAngle ); + + return returnBuffer; +}} + +DefineTSShapeConstructorMethod( addImposter, S32, ( S32 size, S32 equatorSteps, S32 polarSteps, S32 dl, S32 dim, bool includePoles, F32 polarAngle ),, + ( size, equatorSteps, polarSteps, dl, dim, includePoles, polarAngle ), -1, + "Add (or edit) an imposter detail level to the shape.\n" + "If the shape already contains an imposter detail level, this command will " + "simply change the imposter settings\n" + "@param size size of the imposter detail level\n" + "@param equatorSteps defines the number of snapshots to take around the " + "equator. Imagine the object being rotated around the vertical axis, then " + "a snapshot taken at regularly spaced intervals.\n" + "@param polarSteps defines the number of snapshots taken between the poles " + "(top and bottom), at each equator step. eg. At each equator snapshot, " + "snapshots are taken at regular intervals between the poles.\n" + "@param dl the detail level to use when generating the snapshots. Note that " + "this is an array index rather than a detail size. So if an object has detail " + "sizes of: 200, 150, and 40, then setting @a dl to 1 will generate the snapshots " + "using detail size 150.\n" + "@param dim defines the size of the imposter images in pixels. The larger the " + "number, the more detailed the billboard will be.\n" + "@param includePoles flag indicating whether to include the \"pole\" snapshots. " + "ie. the views from the top and bottom of the object.\n" + "@param polar_angle if pole snapshots are active (@a includePoles is true), this " + "parameter defines the camera angle (in degrees) within which to render the " + "pole snapshot. eg. if polar_angle is set to 25 degrees, then the snapshot " + "taken at the pole (looking directly down or up at the object) will be rendered " + "when the camera is within 25 degrees of the pole.\n" + "@return true if successful, false otherwise\n\n" + "@tsexample\n" + "%this.addImposter( 2, 4, 0, 0, 64, false, 0 );\n" + "%this.addImposter( 2, 4, 2, 0, 64, true, 10 ); // this command would edit the existing imposter detail level\n" + "@endtsexample\n" ) +{ + // Add the imposter detail level + dl = mShape->addImposter( getShapePath(), size, equatorSteps, polarSteps, dl, dim, includePoles, polarAngle); + if ( dl != -1 ) + ADD_TO_CHANGE_SET(); + return dl; +}} + +DefineTSShapeConstructorMethod( removeImposter, bool, (),, (), false, + "() Remove the imposter detail level (if any) from the shape.\n" + "@return true if successful, false otherwise\n\n" ) +{ + if ( !mShape->removeImposter() ) + return false; + + ADD_TO_CHANGE_SET(); + return true; +}} + +//----------------------------------------------------------------------------- +// SEQUENCES +DefineTSShapeConstructorMethod( getSequenceCount, S32, (),, (), 0, + "Get the total number of sequences in the shape.\n" + "@return the number of sequences in the shape\n\n" ) +{ + return mShape->sequences.size(); +}} + +DefineTSShapeConstructorMethod( getSequenceIndex, S32, ( const char* name),, + ( name ), -1, + "Find the index of the sequence with the given name.\n" + "@param name name of the sequence to lookup\n" + "@return index of the sequence with matching name, or -1 if not found\n\n" + "@tsexample\n" + "// Check if a given sequence exists in the shape\n" + "if ( %this.getSequenceIndex( \"walk\" ) == -1 )\n" + " echo( \"Could not find 'walk' sequence\" );\n" + "@endtsexample\n" ) +{ + return mShape->findSequence( name ); +}} + +DefineTSShapeConstructorMethod( getSequenceName, const char*, ( S32 index ),, + ( index ), "", + "Get the name of the indexed sequence.\n" + "@param index index of the sequence to query (valid range is 0 - getSequenceCount()-1)\n" + "@return the name of the sequence\n\n" + "@tsexample\n" + "// print the name of all sequences in the shape\n" + "%count = %this.getSequenceCount();\n" + "for ( %i = 0; %i < %count; %i++ )\n" + " echo( %i SPC %this.getSequenceName( %i ) );\n" + "@endtsexample\n" ) +{ + CHECK_INDEX_IN_RANGE( getSequenceName, index, mShape->sequences.size(), "" ); + + return mShape->getName( mShape->sequences[index].nameIndex ); +}} + +DefineTSShapeConstructorMethod( getSequenceSource, const char*, ( const char* name ),, + ( name ), "", + "Get information about where the sequence data came from.\n" + "For example, whether it was loaded from an external DSQ file.\n" + "@param name name of the sequence to query\n" + "@return TAB delimited string of the form: \"from reserved start end total\", where:" + "
      " + "
      from
      the source of the animation data, such as the path to " + "a DSQ file, or the name of an existing sequence in the shape. This field " + "will be empty for sequences already embedded in the DTS or DAE file.
      " + "
      reserved
      reserved value
      " + "
      start
      the first frame in the source sequence used to create this sequence
      " + "
      end
      the last frame in the source sequence used to create this sequence
      " + "
      total
      the total number of frames in the source sequence
      " + "
      \n\n" + "@tsexample\n" + "// print the source for the walk animation\n" + "echo( \"walk source:\" SPC getField( %this.getSequenceSource( \"walk\" ), 0 ) );\n" + "@endtsexample\n" ) +{ + GET_SEQUENCE( getSequenceSource, seq, name, "" ); + + // Return information about the source data for this sequence + char* returnBuffer = Con::getReturnBuffer(512); + dSprintf( returnBuffer, 512, "%s\t%d\t%d\t%d", + seq->sourceData.from.c_str(), seq->sourceData.start, + seq->sourceData.end, seq->sourceData.total ); + return returnBuffer; +}} + +DefineTSShapeConstructorMethod( getSequenceFrameCount, S32, ( const char* name ),, + ( name ), 0, + "Get the number of keyframes in the sequence.\n" + "@param name name of the sequence to query\n" + "@return number of keyframes in the sequence\n\n" + "@tsexample\n" + "echo( \"Run has \" @ %this.getSequenceFrameCount( \"run\" ) @ \" keyframes\" );\n" + "@endtsexample\n" ) +{ + GET_SEQUENCE( getSequenceFrameCount, seq, name, 0 ); + return seq->numKeyframes; +}} + +DefineTSShapeConstructorMethod( getSequencePriority, F32, ( const char* name ),, + ( name ), -1.0f, + "Get the priority setting of the sequence.\n" + "@param name name of the sequence to query\n" + "@return priority value of the sequence\n\n" ) +{ + GET_SEQUENCE( getSequencePriority, seq, name, 0.0f ); + return seq->priority; +}} + +DefineTSShapeConstructorMethod( setSequencePriority, bool, ( const char* name, F32 priority ),, + ( name, priority ), false, + "Set the sequence priority.\n" + "@param name name of the sequence to modify\n" + "@param priority new priority value\n" + "@return true if successful, false otherwise\n\n" ) +{ + GET_SEQUENCE( setSequencePriority, seq, name, false ); + + seq->priority = priority; + ADD_TO_CHANGE_SET(); + return true; +}} + +DefineTSShapeConstructorMethod( getSequenceGroundSpeed, const char*, ( const char* name ),, + ( name ), "", + "Get the ground speed of the sequence.\n" + "@note Note that only the first 2 ground frames of the sequence are " + "examined; the speed is assumed to be constant throughout the sequence.\n" + "@param name name of the sequence to query\n" + "@return string of the form: \"trans.x trans.y trans.z rot.x rot.y rot.z\"\n\n" + "@tsexample\n" + "%speed = VectorLen( getWords( %this.getSequenceGroundSpeed( \"run\" ), 0, 2 ) );\n" + " echo( \"Run moves at \" @ %speed @ \" units per frame\" );\n" + "@endtsexample\n" ) +{ + // Find the sequence and return the ground speed (assumed to be constant) + GET_SEQUENCE( getSequenceGroundSpeed, seq, name, "" ); + + Point3F trans(0,0,0), rot(0,0,0); + if ( seq->numGroundFrames > 0 ) + { + const Point3F& p1 = mShape->groundTranslations[seq->firstGroundFrame]; + const Point3F& p2 = mShape->groundTranslations[seq->firstGroundFrame + 1]; + trans = p2 - p1; + + QuatF r1 = mShape->groundRotations[seq->firstGroundFrame].getQuatF(); + QuatF r2 = mShape->groundRotations[seq->firstGroundFrame + 1].getQuatF(); + r2 -= r1; + + MatrixF mat; + r2.setMatrix(&mat); + rot = mat.toEuler(); + } + + char* returnBuffer = Con::getReturnBuffer(256); + dSprintf( returnBuffer, 256, "%g %g %g %g %g %g", + trans.x, trans.y, trans.z, rot.x, rot.y, rot.z ); + return returnBuffer; +}} + +DefineTSShapeConstructorMethod( setSequenceGroundSpeed, bool, ( const char* name, Point3F transSpeed, Point3F rotSpeed ), ( Point3F::Zero ), + ( name, transSpeed, rotSpeed ), false, + "Set the translation and rotation ground speed of the sequence.\n" + "The ground speed of the sequence is set by generating ground transform " + "keyframes. The ground translational and rotational speed is assumed to " + "be constant for the duration of the sequence. Existing ground frames for " + "the sequence (if any) will be replaced.\n" + "@param name name of the sequence to modify\n" + "@param transSpeed translational speed (trans.x trans.y trans.z) in " + "Torque units per frame\n" + "@param rotSpeed (optional) rotational speed (rot.x rot.y rot.z) in " + "radians per frame. Default is \"0 0 0\"\n" + "@return true if successful, false otherwise\n\n" + "@tsexample\n" + "%this.setSequenceGroundSpeed( \"run\", \"5 0 0\" );\n" + "%this.setSequenceGroundSpeed( \"spin\", \"0 0 0\", \"4 0 0\" );\n" + "@endtsexample\n" ) +{ + if ( !mShape->setSequenceGroundSpeed( name, transSpeed, rotSpeed ) ) + return false; + + ADD_TO_CHANGE_SET(); + return true; +}} + +DefineTSShapeConstructorMethod( getSequenceCyclic, bool, ( const char* name ),, + ( name ), false, + "Check if this sequence is cyclic (looping).\n" + "@param name name of the sequence to query\n" + "@return true if this sequence is cyclic, false if not\n\n" + "@tsexample\n" + "if ( !%this.getSequenceCyclic( \"ambient\" ) )\n" + " error( \"ambient sequence is not cyclic!\" );\n" + "@endtsexample\n" ) +{ + GET_SEQUENCE( getSequenceCyclic, seq, name, false ); + return seq->isCyclic(); +}} + +DefineTSShapeConstructorMethod( setSequenceCyclic, bool, ( const char* name, bool cyclic ),, + ( name, cyclic ), false, + "Mark a sequence as cyclic or non-cyclic.\n" + "@param name name of the sequence to modify\n" + "@param cyclic true to make the sequence cyclic, false for non-cyclic\n" + "@return true if successful, false otherwise\n\n" + "@tsexample\n" + "%this.setSequenceCyclic( \"ambient\", true );\n" + "%this.setSequenceCyclic( \"shoot\", false );\n" + "@endtsexample\n" ) +{ + GET_SEQUENCE( setSequenceCyclic, seq, name, false ); + + // update cyclic flag + if (cyclic != seq->isCyclic()) + { + if (cyclic && !seq->isCyclic()) + seq->flags |= TSShape::Cyclic; + else if (!cyclic && seq->isCyclic()) + seq->flags &= (~(TSShape::Cyclic)); + + ADD_TO_CHANGE_SET(); + } + return true; +}} + +DefineTSShapeConstructorMethod( getSequenceBlend, const char*, ( const char* name ),, + ( name ), "", + "Get information about blended sequences.\n" + "@param name name of the sequence to query\n" + "@return TAB delimited string of the form: \"isBlend blendSeq blendFrame\", where:" + "
      " + "
      blend_flag
      a boolean flag indicating whether this sequence is a blend
      " + "
      blend_seq_name
      the name of the sequence that contains the reference " + "frame (empty for blend sequences embedded in DTS files)
      " + "
      blend_seq_frame
      the blend reference frame (empty for blend sequences " + "embedded in DTS files)
      " + "
      \n" + "@note Note that only sequences set to be blends using the setSequenceBlend " + "command will contain the blendSeq and blendFrame information.\n\n" + "@tsexample\n" + "%blendData = %this.getSequenceBlend( \"look\" );\n" + "if ( getField( %blendData, 0 ) )\n" + " echo( \"look is a blend, reference: \" @ getField( %blendData, 1 ) );\n" + "@endtsexample\n" ) +{ + GET_SEQUENCE( getSequenceBlend, seq, name, "0" ); + + // Return the blend information (flag reference_sequence reference_frame) + char* returnBuffer = Con::getReturnBuffer(512); + dSprintf( returnBuffer, 512, "%d\t%s\t%d", (int)seq->isBlend(), + seq->sourceData.blendSeq.c_str(), seq->sourceData.blendFrame ); + return returnBuffer; +}} + +DefineTSShapeConstructorMethod( setSequenceBlend, bool, ( const char* name, bool blend, const char* blendSeq, S32 blendFrame ),, + ( name, blend, blendSeq, blendFrame ), false, + "Mark a sequence as a blend or non-blend.\n" + "A blend sequence is one that will be added on top of any other playing " + "sequences. This is done by storing the animated node transforms relative " + "to a reference frame, rather than as absolute transforms.\n" + "@param name name of the sequence to modify\n" + "@param blend true to make the sequence a blend, false for a non-blend\n" + "@param blendSeq the name of the sequence that contains the blend reference frame\n" + "@param blendFrame the reference frame in the blendSeq sequence\n" + "@return true if successful, false otherwise\n\n" + "@tsexample\n" + "%this.setSequenceBlend( \"look\", true, \"root\", 0 );\n" + "@endtsexample\n" ) +{ + GET_SEQUENCE( setSequenceBlend, seq, name, false ); + + if ( !mShape->setSequenceBlend( name, blend, blendSeq, blendFrame ) ) + return false; + + ADD_TO_CHANGE_SET(); + return true; +}} + +DefineTSShapeConstructorMethod( renameSequence, bool, ( const char* oldName, const char* newName ),, + ( oldName, newName ), false, + "Rename a sequence.\n" + "@note Note that sequence names must be unique, so this command will fail " + "if there is already a sequence with the desired name\n" + "@param oldName current name of the sequence\n" + "@param newName new name of the sequence\n" + "@return true if successful, false otherwise\n\n" + "@tsexample\n" + "%this.renameSequence( \"walking\", \"walk\" );\n" + "@endtsexample\n" ) +{ + GET_SEQUENCE( renameSequence, seq, oldName, false ); + + if ( !mShape->renameSequence( oldName, newName ) ) + return false; + + ADD_TO_CHANGE_SET(); + return true; +}} + +DefineTSShapeConstructorMethod( addSequence, bool, + ( const char* source, const char* name, S32 start, S32 end, bool padRot, bool padTrans ), + ( 0, -1, true, false ), ( source, name, start, end, padRot, padTrans ), false, + "Add a new sequence to the shape.\n" + "@param source the name of an existing sequence, or the name of a DTS or DAE " + "shape or DSQ sequence file. When the shape file contains more than one " + "sequence, the desired sequence can be specified by appending the name to the " + "end of the shape file. eg. \"myShape.dts run\" would select the \"run\" " + "sequence from the \"myShape.dts\" file.\n\n" + "@param name name of the new sequence\n" + "@param start (optional) first frame to copy. Defaults to 0, the first frame in the sequence.\n" + "@param end (optional) last frame to copy. Defaults to -1, the last frame in the sequence.\n" + "@param padRot (optional) copy root-pose rotation keys for non-animated nodes. This is useful if " + "the source sequence data has a different root-pose to the target shape, such as if one character was " + "in the T pose, and the other had arms at the side. Normally only nodes that are actually rotated by " + "the source sequence have keyframes added, but setting this flag will also add keyframes for nodes " + "that are not animated, but have a different root-pose rotation to the target shape root pose.\n" + "@param padTrans (optional) copy root-pose translation keys for non-animated nodes. This is useful if " + "the source sequence data has a different root-pose to the target shape, such as if one character was " + "in the T pose, and the other had arms at the side. Normally only nodes that are actually moved by " + "the source sequence have keyframes added, but setting this flag will also add keyframes for nodes " + "that are not animated, but have a different root-pose position to the target shape root pose.\n" + "@return true if successful, false otherwise\n\n" + "@tsexample\n" + "%this.addSequence( \"./testShape.dts ambient\", \"ambient\" );\n" + "%this.addSequence( \"./myPlayer.dae run\", \"run\" );\n" + "%this.addSequence( \"./player_look.dsq\", \"look\", 0, -1 ); // start to end\n" + "%this.addSequence( \"walk\", \"walk_shortA\", 0, 4 ); // start to frame 4\n" + "%this.addSequence( \"walk\", \"walk_shortB\", 4, -1 ); // frame 4 to end\n" + "@endtsexample\n" ) +{ + String srcName; + String srcPath( source ); + SplitSequencePathAndName( srcPath, srcName ); + + if ( !mShape->addSequence( srcPath, srcName, name, start, end, padRot, padTrans ) ) + return false; + + ADD_TO_CHANGE_SET(); + return true; +}} + +DefineTSShapeConstructorMethod( removeSequence, bool, ( const char* name ),, + ( name ), false, + "Remove the sequence from the shape.\n" + "@param name name of the sequence to remove\n" + "@return true if successful, false otherwise\n\n" ) +{ + if ( !mShape->removeSequence( name ) ) + return false; + + ADD_TO_CHANGE_SET(); + return true; +}} + +//----------------------------------------------------------------------------- +// TRIGGERS +DefineTSShapeConstructorMethod( getTriggerCount, S32, ( const char* name ),, + ( name ), 0, + "Get the number of triggers in the specified sequence.\n" + "@param name name of the sequence to query\n" + "@return number of triggers in the sequence\n\n" ) +{ + GET_SEQUENCE( getTriggerCount, seq, name, 0 ); + return seq->numTriggers; +}} + +DefineTSShapeConstructorMethod( getTrigger, const char*, ( const char* name, S32 index ),, + ( name, index ), "", + "Get information about the indexed trigger\n" + "@param name name of the sequence to query\n" + "@param index index of the trigger (valid range is 0 - getTriggerCount()-1)\n" + "@return string of the form \"frame state\"\n\n" + "@tsexample\n" + "// print all triggers in the sequence\n" + "%count = %this.getTriggerCount( \"back\" );\n" + "for ( %i = 0; %i < %count; %i++ )\n" + " echo( %i SPC %this.getTrigger( \"back\", %i ) );\n" + "@endtsexample\n" ) +{ + // Find the sequence and return the indexed trigger (frame and state) + GET_SEQUENCE( getTrigger, seq, name, "" ); + + CHECK_INDEX_IN_RANGE( getTrigger, index, seq->numTriggers, "" ); + + const TSShape::Trigger& trig = mShape->triggers[seq->firstTrigger + index]; + S32 frame = trig.pos * seq->numKeyframes; + S32 state = getBinLog2(trig.state & TSShape::Trigger::StateMask) + 1; + if (!(trig.state & TSShape::Trigger::StateOn)) + state = -state; + + char* returnBuffer = Con::getReturnBuffer(32); + dSprintf(returnBuffer, 32, "%d %d", frame, state); + return returnBuffer; +}} + +DefineTSShapeConstructorMethod( addTrigger, bool, ( const char* name, S32 keyframe, S32 state ),, + ( name, keyframe, state ), false, + "Add a new trigger to the sequence.\n" + "@param name name of the sequence to modify\n" + "@param keyframe keyframe of the new trigger\n" + "@param state of the new trigger\n" + "@return true if successful, false otherwise\n\n" + "@tsexample\n" + "%this.addTrigger( \"walk\", 3, 1 );\n" + "%this.addTrigger( \"walk\", 5, -1 );\n" + "@endtsexample\n" ) +{ + if ( !mShape->addTrigger( name, keyframe, state ) ) + return false; + + ADD_TO_CHANGE_SET(); + return true; +}} + +DefineTSShapeConstructorMethod( removeTrigger, bool, ( const char* name, S32 keyframe, S32 state ),, + ( name, keyframe, state ), false, + "Remove a trigger from the sequence.\n" + "@param name name of the sequence to modify\n" + "@param keyframe keyframe of the trigger to remove\n" + "@param state of the trigger to remove\n" + "@return true if successful, false otherwise\n\n" + "@tsexample\n" + "%this.removeTrigger( \"walk\", 3, 1 );\n" + "@endtsexample\n" ) +{ + if ( !mShape->removeTrigger( name, keyframe, state ) ) + return false; + + ADD_TO_CHANGE_SET(); + return true; +}} + + +//----------------------------------------------------------------------------- +// Change-Set manipulation +TSShapeConstructor::ChangeSet::eCommandType TSShapeConstructor::ChangeSet::getCmdType(const char* name) +{ + #define RETURN_IF_MATCH(type) if (!dStricmp(name, #type)) return Cmd##type + + RETURN_IF_MATCH(AddNode); + else RETURN_IF_MATCH(RemoveNode); + else RETURN_IF_MATCH(RenameNode); + else RETURN_IF_MATCH(SetNodeTransform); + else RETURN_IF_MATCH(SetNodeParent); + + else RETURN_IF_MATCH(AddMesh); + else RETURN_IF_MATCH(AddPrimitive); + else RETURN_IF_MATCH(SetMeshSize); + else RETURN_IF_MATCH(SetMeshType); + else RETURN_IF_MATCH(SetMeshMaterial); + else RETURN_IF_MATCH(RemoveMesh); + + else RETURN_IF_MATCH(SetObjectNode); + else RETURN_IF_MATCH(RenameObject); + else RETURN_IF_MATCH(RemoveObject); + else RETURN_IF_MATCH(SetBounds); + + else RETURN_IF_MATCH(SetDetailLevelSize); + else RETURN_IF_MATCH(RenameDetailLevel); + else RETURN_IF_MATCH(RemoveDetailLevel); + else RETURN_IF_MATCH(AddImposter); + else RETURN_IF_MATCH(RemoveImposter); + else RETURN_IF_MATCH(AddCollisionDetail); + + else RETURN_IF_MATCH(AddSequence); + else RETURN_IF_MATCH(RemoveSequence); + else RETURN_IF_MATCH(RenameSequence); + else RETURN_IF_MATCH(SetSequenceCyclic); + else RETURN_IF_MATCH(SetSequenceBlend); + else RETURN_IF_MATCH(SetSequencePriority); + else RETURN_IF_MATCH(SetSequenceGroundSpeed); + + else RETURN_IF_MATCH(AddTrigger); + else RETURN_IF_MATCH(RemoveTrigger); + + else return CmdInvalid; + + #undef RETURN_IF_MATCH +} + +void TSShapeConstructor::ChangeSet::write(TSShape* shape, Stream& stream, const String& savePath) +{ + // First make a copy of the change-set + ChangeSet output; + for ( S32 i = 0; i < mCommands.size(); i++ ) + output.add(mCommands[i]); + + // Remove all __backup__ sequences (used during Shape Editing) + if (shape) + { + for (S32 i = 0; i < shape->sequences.size(); i++) + { + const char* seqName = shape->getName( shape->sequences[i].nameIndex ); + if ( dStrStartsWith( seqName, "__backup__" ) ) + { + Command cmd( "removeSequence" ); + cmd.addArgs( seqName ); + output.add( cmd ); + } + } + } + + // Write the final change set to the stream + for (U32 i = 0; i < output.mCommands.size(); i++) + { + const Command& cmd = output.mCommands[i]; + + // Write the command + stream.writeTabs( 1 ); + stream.writeText( "%this." ); + + stream.writeText( cmd.name ); + stream.writeText( "(" ); + + if ( cmd.argc > 0 ) + { + // Use relative paths when possible + String str( cmd.argv[0] ); + if ( str.startsWith( savePath ) ) + { + // Need to add "./" to a local file for the script file system. Otherwise + // it will be assumed to be a full and complete path when it comes to loading. + str = "./" + str.substr( savePath.length() + 1 ); + } + + stream.writeText( "\"" ); + stream.write( str.length(), str.c_str() ); + stream.writeText( "\"" ); + + // Write remaining arguments and newline + for (U32 j = 1; j < cmd.argc; j++) + { + // Use relative paths when possible + String str( cmd.argv[j] ); + if ( str.startsWith( savePath ) ) + str = str.substr( savePath.length() + 1 ); + + stream.writeText( ", \"" ); + stream.write( str.length(), str.c_str() ); + stream.writeText( "\"" ); + } + } + stream.writeText( ");\r\n" ); + } +} + + +TiXmlElement *createNodeWithText( const char* name, const char* text ) +{ + TiXmlElement* node = new TiXmlElement( name ); + node->LinkEndChild( new TiXmlText( text ) ); + return node; +} + +void TSShapeConstructor::ChangeSet::add( TSShapeConstructor::ChangeSet::Command& cmd ) +{ + // Lookup the command type + cmd.type = getCmdType( cmd.name );if ( cmd.type == CmdInvalid ) + return; + + // Ignore operations on __proxy__ sequences (they are only used by the shape editor) + if ( cmd.argv[0].startsWith( "__proxy__" ) || ((cmd.type == CmdAddSequence) && cmd.argv[1].startsWith( "__proxy__") ) ) + return; + + // Add the command to the change set (apply command specific collapsing) + bool addCommand = true; + switch ( cmd.type ) + { + + // Node commands + case CmdSetNodeParent: addCommand = addCmd_setNodeParent( cmd ); break; + case CmdSetNodeTransform: addCommand = addCmd_setNodeTransform( cmd ); break; + case CmdRenameNode: addCommand = addCmd_renameNode( cmd ); break; + case CmdRemoveNode: addCommand = addCmd_removeNode( cmd ); break; + + // Mesh commands + case CmdSetMeshSize: addCommand = addCmd_setMeshSize( cmd ); break; + case CmdSetMeshType: addCommand = addCmd_setMeshType( cmd ); break; + case CmdSetMeshMaterial: addCommand = addCmd_setMeshMaterial( cmd ); break; + case CmdRemoveMesh: addCommand = addCmd_removeMesh( cmd ); break; + + // Object commands + case CmdSetObjectNode: addCommand = addCmd_setObjectNode( cmd ); break; + case CmdRenameObject: addCommand = addCmd_renameObject( cmd ); break; + case CmdRemoveObject: addCommand = addCmd_removeObject( cmd ); break; + case CmdSetBounds: addCommand = addCmd_setBounds( cmd ); break; + + // Detail level commands + case CmdRenameDetailLevel: addCommand = addCmd_renameDetailLevel( cmd ); break; + case CmdRemoveDetailLevel: addCommand = addCmd_removeDetailLevel( cmd ); break; + case CmdSetDetailLevelSize: addCommand = addCmd_setDetailSize( cmd ); break; + case CmdAddImposter: addCommand = addCmd_addImposter( cmd ); break; + case CmdRemoveImposter: addCommand = addCmd_removeImposter( cmd ); break; + + // Sequence commands + case CmdAddSequence: addCommand = addCmd_addSequence( cmd ); break; + case CmdSetSequencePriority: addCommand = addCmd_setSequencePriority( cmd ); break; + case CmdSetSequenceGroundSpeed: addCommand = addCmd_setSequenceGroundSpeed( cmd ); break; + case CmdSetSequenceCyclic: addCommand = addCmd_setSequenceCyclic( cmd ); break; + case CmdSetSequenceBlend: addCommand = addCmd_setSequenceBlend( cmd ); break; + case CmdRenameSequence: addCommand = addCmd_renameSequence( cmd ); break; + case CmdRemoveSequence: addCommand = addCmd_removeSequence( cmd ); break; + + case CmdAddTrigger: addCommand = addCmd_addTrigger( cmd ); break; + case CmdRemoveTrigger: addCommand = addCmd_removeTrigger( cmd ); break; + + // Other commands that do not have optimizations + default: + break; + } + + if ( addCommand ) + mCommands.push_back( cmd ); +} + +//----------------------------------------------------------------------------- +// NODE COMMANDS + +bool TSShapeConstructor::ChangeSet::addCmd_setNodeParent( const TSShapeConstructor::ChangeSet::Command& newCmd ) +{ + // No dependencies, replace the parent argument for any previous addNode or + // setNodeParent. + + for ( S32 index = mCommands.size()-1; index >= 0; index-- ) + { + Command& cmd = mCommands[index]; + switch ( cmd.type ) + { + case CmdAddNode: + case CmdSetNodeParent: + if ( namesEqual( cmd.argv[0], newCmd.argv[0] ) ) + { + cmd.argv[1] = newCmd.argv[1]; // Replace parent argument + return false; + } + break; + + default: + break; + } + } + + return true; + +} + +bool TSShapeConstructor::ChangeSet::addCmd_setNodeTransform( const TSShapeConstructor::ChangeSet::Command& newCmd ) +{ + // No dependencies, replace the parent argument for any previous addNode or + // setNodeParent. + + for ( S32 index = mCommands.size()-1; index >= 0; index-- ) + { + Command& cmd = mCommands[index]; + switch ( cmd.type ) + { + case CmdAddNode: + if ( namesEqual( cmd.argv[0], newCmd.argv[0] ) ) + { + cmd.argc = newCmd.argc + 1; // Replace transform argument + cmd.argv[2] = newCmd.argv[1]; + cmd.argv[3] = newCmd.argv[2]; + return false; + } + break; + + case CmdSetNodeTransform: + if ( namesEqual( cmd.argv[0], newCmd.argv[0] ) ) + { + cmd = newCmd; // Collapse successive set transform commands + return false; + } + break; + + default: + break; + } + } + + return true; +} + +bool TSShapeConstructor::ChangeSet::addCmd_renameNode( const TSShapeConstructor::ChangeSet::Command& newCmd ) +{ + // Replace name argument for previous addNode or renameNode, but stop + // if the new name is already in use (can occur if 2 nodes switch names). eg. + // A->C + // B->A + // C->B (cannot replace the previous A->C with A->B as 'B' is in use) + + for ( S32 index = mCommands.size()-1; index >= 0; index-- ) + { + Command& cmd = mCommands[index]; + switch ( cmd.type ) + { + case CmdAddNode: + if ( namesEqual( cmd.argv[0], newCmd.argv[0] ) ) + { + cmd.argv[0] = newCmd.argv[1]; // Replace initial name argument + return false; + } + break; + + case CmdRenameNode: + if ( namesEqual( cmd.argv[1], newCmd.argv[0] ) ) + { + cmd.argv[1] = newCmd.argv[1]; // Collapse successive renames + if ( namesEqual( cmd.argv[0], cmd.argv[1] ) ) + mCommands.erase(index); // Ignore empty renames + return false; + } + else if ( namesEqual( cmd.argv[0], newCmd.argv[1] ) ) + return true; // Name is in use, cannot go back further + break; + + default: + break; + } + } + + return true; +} + +bool TSShapeConstructor::ChangeSet::addCmd_removeNode( const TSShapeConstructor::ChangeSet::Command& newCmd ) +{ + // No dependencies. Remove any previous command that references the node + + String nodeName( newCmd.argv[0] ); + for ( S32 index = mCommands.size()-1; index >= 0; index-- ) + { + Command& cmd = mCommands[index]; + switch ( cmd.type ) + { + case CmdAddNode: + if ( namesEqual( cmd.argv[0], nodeName ) ) + { + mCommands.erase(index); // Remove the added node + return false; + } + break; + + case CmdSetNodeTransform: + case CmdSetNodeParent: + if ( namesEqual( cmd.argv[0], nodeName ) ) + mCommands.erase(index); // Remove any commands that reference the removed node + break; + + case CmdRenameNode: + if ( namesEqual( cmd.argv[1], nodeName ) ) + { + nodeName = cmd.argv[0]; // Node is renamed + mCommands.erase(index); + } + break; + + default: + break; + } + } + + return true; +} + +//----------------------------------------------------------------------------- +// SEQUENCE COMMANDS + +bool TSShapeConstructor::ChangeSet::addCmd_addSequence( TSShapeConstructor::ChangeSet::Command& newCmd ) +{ + // For sequences added from ShapeEditor __backup sequences, search backwards for + // any changes made to the source of the __backup sequence. If none are found, + // use the __backup source instead of the __backup. + const char* backupPrefix = "__backup__"; + if ( !newCmd.argv[0].startsWith( backupPrefix ) ) + return true; + + S32 start = dStrlen( backupPrefix ); + S32 end = newCmd.argv[0].find( '_', 0, String::Right ); + String sourceName = newCmd.argv[0].substr( start, end - start ); + + for ( S32 index = mCommands.size()-1; index >= 0; index-- ) + { + Command& cmd = mCommands[index]; + switch ( cmd.type ) + { + case CmdSetSequencePriority: + case CmdSetSequenceCyclic: + case CmdSetSequenceBlend: + case CmdSetSequenceGroundSpeed: + // __backup sequence source has been modified => cannot go back further + if ( namesEqual( cmd.argv[0], sourceName ) ) + return true; + + case CmdAddSequence: + if ( namesEqual( cmd.argv[1], newCmd.argv[0] ) ) + { + // No changes to the __backup sequence were found + newCmd.argv[0] = sourceName; + return true; + } + break; + + default: + break; + } + } + + return true; +} + +bool TSShapeConstructor::ChangeSet::addCmd_setSequencePriority( const TSShapeConstructor::ChangeSet::Command& newCmd ) +{ + // Replace any previous setSequencePriority command, but stop if the + // sequence is used as a source for addSequence (since the priority is + // copied). + + for ( S32 index = mCommands.size()-1; index >= 0; index-- ) + { + Command& cmd = mCommands[index]; + switch ( cmd.type ) + { + case CmdSetSequencePriority: + if ( namesEqual( cmd.argv[0], newCmd.argv[0] ) ) + { + cmd.argv[1] = newCmd.argv[1]; // Collapse successive set priority commands + return false; + } + break; + + case CmdAddSequence: + if ( namesEqual( cmd.argv[1], newCmd.argv[0] ) ) + return true; // Sequence is used as source => cannot go back further + break; + + default: + break; + } + } + + return true; +} + +bool TSShapeConstructor::ChangeSet::addCmd_setSequenceGroundSpeed( const TSShapeConstructor::ChangeSet::Command& newCmd ) +{ + // Replace any previous setSequenceGroundSpeed command, but stop if the + // sequence is used as a source for addSequence (since the priority is + // copied). + + for ( S32 index = mCommands.size()-1; index >= 0; index-- ) + { + Command& cmd = mCommands[index]; + switch ( cmd.type ) + { + case CmdSetSequenceGroundSpeed: + if ( namesEqual( cmd.argv[0], newCmd.argv[0] ) ) + { + cmd.argv[1] = newCmd.argv[1]; // Collapse successive set ground speed commands + return false; + } + break; + + case CmdAddSequence: + if ( namesEqual( cmd.argv[1], newCmd.argv[0] ) ) + return true; // Sequence is used as source => cannot go back further + break; + + default: + break; + } + } + + return true; +} + +bool TSShapeConstructor::ChangeSet::addCmd_setSequenceCyclic( const TSShapeConstructor::ChangeSet::Command& newCmd ) +{ + // Replace any previous setSequenceCyclic command, but stop if the + // sequence is used as a source for addSequence (since the priority is + // copied). + + for ( S32 index = mCommands.size()-1; index >= 0; index-- ) + { + Command& cmd = mCommands[index]; + switch ( cmd.type ) + { + case CmdSetSequenceCyclic: + if ( namesEqual( cmd.argv[0], newCmd.argv[0] ) && + dAtob( cmd.argv[1] ) != dAtob( newCmd.argv[1] ) ) + { + mCommands.erase(index); // ignore both setCyclic commands (1 undoes the other) + return false; + } + break; + + case CmdAddSequence: + if ( namesEqual( cmd.argv[1], newCmd.argv[0] ) ) + return true; // Sequence is used as source => cannot go back further + break; + + default: + break; + } + } + + return true; +} + +bool TSShapeConstructor::ChangeSet::addCmd_setSequenceBlend( const TSShapeConstructor::ChangeSet::Command& newCmd ) +{ + // Replace any previous setSequenceBlend command, but stop if the + // sequence is used as a source for addSequence (since the priority is + // copied). + + for ( S32 index = mCommands.size()-1; index >= 0; index-- ) + { + Command& cmd = mCommands[index]; + switch ( cmd.type ) + { + case CmdSetSequenceBlend: + if ( namesEqual( cmd.argv[0], newCmd.argv[0] ) && + dAtob( cmd.argv[1] ) != dAtob( newCmd.argv[1] ) && + namesEqual( cmd.argv[2], newCmd.argv[2] ) && + dAtoi( cmd.argv[3] ) == dAtoi( newCmd.argv[3] ) ) + { + mCommands.erase(index); // Ignore both setBlend commands (1 undoes the other) + return false; + } + break; + + case CmdAddSequence: + if ( namesEqual( cmd.argv[1], newCmd.argv[0] ) ) + return true; // Sequence is used as source => cannot go back further + break; + + default: + break; + } + } + + return true; +} + +bool TSShapeConstructor::ChangeSet::addCmd_renameSequence( const TSShapeConstructor::ChangeSet::Command& newCmd ) +{ + // Replace name argument for previous addSequence or renameSequence, but stop + // if the new name is already in use (can occur if 2 nodes switch names). eg. + // A->C + // B->A + // C->B (cannot replace the previous A->C with A->B as 'B' is in use) + // + // Once a previous command is found, go forward through the command list and + // update any references to the old name + + for ( S32 index = mCommands.size()-1; index >= 0; index-- ) + { + Command& cmd = mCommands[index]; + switch ( cmd.type ) + { + case CmdRenameSequence: + if ( namesEqual( cmd.argv[0], newCmd.argv[1] ) && !namesEqual( cmd.argv[1], newCmd.argv[0] ) ) + return true; // Name is in use => cannot go back further + // fall through to common processing + case CmdAddSequence: + if ( namesEqual( cmd.argv[1], newCmd.argv[0] ) ) + { + if ( cmd.type == CmdRenameSequence ) + { + cmd.argv[1] = newCmd.argv[1]; // Collapse successive renames + if ( namesEqual( cmd.argv[0], cmd.argv[1] ) ) + mCommands.erase(index); // Ignore empty renames + } + else if ( cmd.type == CmdAddSequence ) + { + cmd.argv[1] = newCmd.argv[1]; // Replace initial name argument + } + + // Update any references to the old name + for ( S32 j = index + 1; j < mCommands.size(); j++ ) + { + Command& cmd2 = mCommands[j]; + switch ( cmd2.type ) + { + case CmdSetSequencePriority: + case CmdSetSequenceCyclic: + case CmdSetSequenceBlend: + case CmdSetSequenceGroundSpeed: + if ( namesEqual( cmd2.argv[0], newCmd.argv[0] ) ) + cmd2.argv[0] = newCmd.argv[1]; + break; + } + } + return false; + } + break; + + default: + break; + } + } + + return true; +} + +bool TSShapeConstructor::ChangeSet::addCmd_removeSequence( const TSShapeConstructor::ChangeSet::Command& newCmd ) +{ + // Remove any previous command that references the sequence, but stop if the + // sequence is used as a source for addSequence + + String seqName( newCmd.argv[0] ); + for ( S32 index = mCommands.size()-1; index >= 0; index-- ) + { + Command& cmd = mCommands[index]; + switch ( cmd.type ) + { + case CmdAddSequence: + if ( namesEqual( cmd.argv[1], seqName ) ) + { + mCommands.erase( index ); // Remove the added sequence + return false; + } + else if ( namesEqual( cmd.argv[0], seqName ) ) + { + // Removed sequence is used as source for another sequence => can't + // go back any further + return true; + } + break; + + case CmdRenameSequence: + if ( namesEqual( cmd.argv[1], seqName ) ) + { + seqName = cmd.argv[0]; // Sequence is renamed + mCommands.erase( index ); + } + break; + + case CmdSetSequencePriority: + case CmdSetSequenceGroundSpeed: + case CmdSetSequenceCyclic: + case CmdSetSequenceBlend: + case CmdAddTrigger: + case CmdRemoveTrigger: + if ( namesEqual( cmd.argv[0], seqName ) ) + mCommands.erase( index ); // Remove any commands that reference the removed sequence + break; + + default: + break; + } + } + + return true; +} + +bool TSShapeConstructor::ChangeSet::addCmd_addTrigger( const TSShapeConstructor::ChangeSet::Command& newCmd ) +{ + // Remove a matching removeTrigger command, but stop if the sequence is used as + // a source for addSequence (since triggers are copied). + + for ( S32 index = mCommands.size()-1; index >= 0; index-- ) + { + Command& cmd = mCommands[index]; + switch ( cmd.type ) + { + case CmdRemoveTrigger: + if ( namesEqual( cmd.argv[0], newCmd.argv[0] ) && + cmd.argv[1] == newCmd.argv[1] && + cmd.argv[2] == newCmd.argv[2] ) + { + mCommands.erase(index); // Remove previous removeTrigger command + return false; + } + break; + + case CmdAddSequence: + if ( namesEqual( cmd.argv[1], newCmd.argv[0] ) ) + return true; // Sequence is used as a source => cannot go back further + break; + + default: + break; + } + } + + return true; +} + +bool TSShapeConstructor::ChangeSet::addCmd_removeTrigger( const TSShapeConstructor::ChangeSet::Command& newCmd ) +{ + // Remove a matching addTrigger command, but stop if the sequence is used as + // a source for addSequence (since triggers are copied). + + for ( S32 index = mCommands.size()-1; index >= 0; index-- ) + { + Command& cmd = mCommands[index]; + switch ( cmd.type ) + { + case CmdAddTrigger: + if ( namesEqual( cmd.argv[0], newCmd.argv[0] ) && + cmd.argv[1] == newCmd.argv[1] && + cmd.argv[2] == newCmd.argv[2] ) + { + mCommands.erase(index); // Remove previous addTrigger command + return false; + } + break; + + case CmdAddSequence: + if ( namesEqual( cmd.argv[1], newCmd.argv[0] ) ) + return true; // Sequence is used as a source => cannot go back further + break; + + default: + break; + } + } + + return true; +} + +//----------------------------------------------------------------------------- +// MESH COMMANDS + +bool TSShapeConstructor::ChangeSet::addCmd_setMeshSize( const TSShapeConstructor::ChangeSet::Command& newCmd ) +{ + // Replace size argument for previous addMesh or setMeshSize, but stop if the + // new name is already in use (can occur if 2 nodes switch names). eg. + // A->C + // B->A + // C->B (cannot replace the previous A->C with A->B as 'B' is in use) + + for ( S32 index = mCommands.size()-1; index >= 0; index-- ) + { + Command& cmd = mCommands[index]; + switch ( cmd.type ) + { + case CmdAddMesh: + if ( namesEqual( cmd.argv[0], newCmd.argv[0] ) ) + { + cmd.argv[0] = newCmd.argv[1]; // Replace initial size argument + return false; + } + break; + + case CmdSetMeshSize: + if ( cmd.argv[1] == newCmd.argv[0] ) + { + cmd.argv[1] = newCmd.argv[1]; // Collapse successive size sets + if ( cmd.argv[0] == cmd.argv[1] ) + mCommands.erase(index); // Ignore empty resizes + return false; + } + else if ( cmd.argv[0] == newCmd.argv[1] ) + return true; // Size is in use, cannot go back further + break; + + default: + break; + } + } + + return true; +} + +bool TSShapeConstructor::ChangeSet::addCmd_setMeshType( const TSShapeConstructor::ChangeSet::Command& newCmd ) +{ + // Replace any previous setMeshType command, but stop if the mesh is used as + // a source for addMesh (since the type is copied). + + for ( S32 index = mCommands.size()-1; index >= 0; index-- ) + { + Command& cmd = mCommands[index]; + switch ( cmd.type ) + { + case CmdSetMeshType: + if ( namesEqual( cmd.argv[0], newCmd.argv[0] ) ) + { + cmd.argv[1] = newCmd.argv[1]; // Collapse successive set type commands + return false; + } + break; + + case CmdAddMesh: + if ( namesEqual( cmd.argv[0], newCmd.argv[0] ) ) + return true; // Mesh is used as source => cannot go back further + break; + + default: + break; + } + } + + return true; +} + +bool TSShapeConstructor::ChangeSet::addCmd_setMeshMaterial( const TSShapeConstructor::ChangeSet::Command& newCmd ) +{ + // Replace any previous setMeshMaterial command, but stop if the mesh is used as + // a source for addMesh (since the materials are copied). + + for ( S32 index = mCommands.size()-1; index >= 0; index-- ) + { + Command& cmd = mCommands[index]; + switch ( cmd.type ) + { + case CmdSetMeshMaterial: + if ( namesEqual( cmd.argv[0], newCmd.argv[0] ) ) + { + cmd.argv[1] = newCmd.argv[1]; // Collapse successive set material commands + return false; + } + break; + + case CmdAddMesh: + if ( namesEqual( cmd.argv[0], newCmd.argv[0] ) ) + return true; // Mesh is used as source => cannot go back further + break; + + default: + break; + } + } + + return true; +} + +bool TSShapeConstructor::ChangeSet::addCmd_removeMesh( const TSShapeConstructor::ChangeSet::Command& newCmd ) +{ + // Remove any previous command that references the mesh, but stop if the mesh + // is used as a source for addMesh + + String meshName( newCmd.argv[0] ); + for ( S32 index = mCommands.size()-1; index >= 0; index-- ) + { + Command& cmd = mCommands[index]; + switch ( cmd.type ) + { + case CmdAddMesh: + if ( namesEqual( cmd.argv[0], meshName ) ) + { + mCommands.erase( index ); // Remove the added mesh + return false; + } + else if ( namesEqual( cmd.argv[2], meshName ) ) + { + // Removed mesh is used as source for another mesh => can't go back + // any further + return true; + } + break; + + case CmdAddPrimitive: + if ( namesEqual( cmd.argv[0], meshName ) ) + { + mCommands.erase( index ); // Remove the added primitive + return false; + } + break; + + case CmdSetMeshSize: + case CmdSetMeshType: + case CmdSetMeshMaterial: + if ( namesEqual( cmd.argv[0], meshName ) ) + mCommands.erase( index ); // Remove any commands that reference the removed mesh + break; + + default: + break; + } + } + + return true; +} + +//----------------------------------------------------------------------------- +// OBJECT COMMANDS + +bool TSShapeConstructor::ChangeSet::addCmd_setObjectNode( const TSShapeConstructor::ChangeSet::Command& newCmd ) +{ + // No dependencies, replace the node argument for any previous parent argument for any previous addNode or + // setNodeParent. + + for ( S32 index = mCommands.size()-1; index >= 0; index-- ) + { + Command& cmd = mCommands[index]; + switch ( cmd.type ) + { + case CmdAddMesh: + { + S32 dummy; + if ( namesEqual( String::GetTrailingNumber(cmd.argv[0], dummy), newCmd.argv[0] ) ) + { + cmd.argv[3] = newCmd.argv[1]; // Replace node argument + return false; + } + break; + } + + case CmdSetObjectNode: + if ( namesEqual( cmd.argv[0], newCmd.argv[0] ) ) + { + cmd.argv[1] = newCmd.argv[1]; + return false; + } + break; + + default: + break; + } + } + + return true; +} + +bool TSShapeConstructor::ChangeSet::addCmd_renameObject( const TSShapeConstructor::ChangeSet::Command& newCmd ) +{ + // Replace name argument for previous renameObject, but stop if the new name + // is already in use (can occur if 2 objects switch names). eg. + // A->C + // B->A + // C->B (cannot replace the previous A->C with A->B as 'B' is in use) + + for ( S32 index = mCommands.size()-1; index >= 0; index-- ) + { + Command& cmd = mCommands[index]; + switch ( cmd.type ) + { + case CmdRenameObject: + if ( namesEqual( cmd.argv[1], newCmd.argv[0] ) ) + { + cmd.argv[1] = newCmd.argv[1]; // Collapse successive renames + if ( namesEqual( cmd.argv[0], cmd.argv[1] ) ) + mCommands.erase(index); // Ignore empty renames + return false; + } + else if ( namesEqual( cmd.argv[0], newCmd.argv[1] ) ) + return true; // Name is in use, cannot go back further + break; + + default: + break; + } + } + + return true; +} + +bool TSShapeConstructor::ChangeSet::addCmd_removeObject( const TSShapeConstructor::ChangeSet::Command& newCmd ) +{ + // Remove any previous command that references the object, but stop if any + // object mesh is used as a source for addMesh + + S32 dummy; + String objName( newCmd.argv[0] ); + for ( S32 index = mCommands.size()-1; index >= 0; index-- ) + { + Command& cmd = mCommands[index]; + switch ( cmd.type ) + { + case CmdAddMesh: + if ( namesEqual( String::GetTrailingNumber(cmd.argv[0], dummy), objName ) ) + { + mCommands.erase( index ); // Remove the added mesh + // Must still add the removeObject command as there could be multiple + // meshes in the object + } + else if ( namesEqual( String::GetTrailingNumber(cmd.argv[2], dummy), objName ) ) + { + // Removed mesh is used as source for another mesh => can't go back + // any further + return true; + } + break; + + case CmdRenameObject: + if ( namesEqual( cmd.argv[1], objName ) ) + { + objName = cmd.argv[0]; // Object is renamed + mCommands.erase( index ); + } + break; + + case CmdSetObjectNode: + if ( namesEqual( cmd.argv[0], objName ) ) + mCommands.erase( index ); // Remove any commands that reference the removed object + break; + + case CmdSetMeshSize: + case CmdSetMeshType: + case CmdSetMeshMaterial: + case CmdRemoveMesh: + if ( namesEqual( String::GetTrailingNumber(cmd.argv[0], dummy), objName ) ) + mCommands.erase( index ); // Remove comands that reference the removed object + break; + + default: + break; + } + } + + return true; +} + +bool TSShapeConstructor::ChangeSet::addCmd_setBounds( const TSShapeConstructor::ChangeSet::Command& newCmd ) +{ + // Only the last bounds update applies, so replace any previous command. + + for ( S32 index = mCommands.size()-1; index >= 0; index-- ) + { + Command& cmd = mCommands[index]; + switch ( cmd.type ) + { + case CmdSetBounds: + mCommands.erase( index ); + break; + + default: + break; + } + } + + return true; +} + +//----------------------------------------------------------------------------- +// DETAIL COMMANDS + +bool TSShapeConstructor::ChangeSet::addCmd_renameDetailLevel( const TSShapeConstructor::ChangeSet::Command& newCmd ) +{ + // Replace name argument for previous renameDetailLevel, but stop if the new + // name is already in use (can occur if 2 objects switch names). eg. + // A->C + // B->A + // C->B (cannot replace the previous A->C with A->B as 'B' is in use) + + for ( S32 index = mCommands.size()-1; index >= 0; index-- ) + { + Command& cmd = mCommands[index]; + switch ( cmd.type ) + { + case CmdRenameDetailLevel: + if ( namesEqual( cmd.argv[1], newCmd.argv[0] ) ) + { + cmd.argv[1] = newCmd.argv[1]; // Collapse successive renames + if ( namesEqual( cmd.argv[0], cmd.argv[1] ) ) + mCommands.erase(index); // Ignore empty renames + return false; + } + else if ( namesEqual( cmd.argv[0], newCmd.argv[1] ) ) + return true; // Name is in use, cannot go back further + break; + + default: + break; + } + } + + return true; +} + +bool TSShapeConstructor::ChangeSet::addCmd_removeDetailLevel( const TSShapeConstructor::ChangeSet::Command& newCmd ) +{ + // Remove any previous command that references the detail, but stop if a mesh + // is used as a source for addMesh + + S32 detSize = dAtoi( newCmd.argv[0] ); + for ( S32 index = mCommands.size()-1; index >= 0; index-- ) + { + Command& cmd = mCommands[index]; + S32 size; + + switch ( cmd.type ) + { + case CmdAddMesh: + String::GetTrailingNumber( cmd.argv[2], size ); + if ( size == detSize ) + { + // Removed detail is used as source for another mesh => can't go back + // any further + return true; + } + // fall through + + case CmdAddPrimitive: + case CmdSetMeshSize: + case CmdSetMeshType: + case CmdSetMeshMaterial: + case CmdRemoveMesh: + String::GetTrailingNumber( cmd.argv[0], size ); + if ( size == detSize ) + mCommands.erase( index ); + break; + + case CmdAddImposter: + case CmdAddCollisionDetail: + if ( dAtoi(cmd.argv[0]) == detSize ) + { + mCommands.erase( index ); + return false; + } + break; + + default: + break; + } + } + + return true; +} + +bool TSShapeConstructor::ChangeSet::addCmd_setDetailSize( const TSShapeConstructor::ChangeSet::Command& newCmd ) +{ + // Similar to renameXXX. Replace size argument for previous addImposter or + // setDetailLevelSize, but stop if the new size is already in use (can occur + // if 2 details switch sizes). eg. + // A->C + // B->A + // C->B (cannot replace the previous A->C with A->B as 'B' is in use) + + for ( S32 index = mCommands.size()-1; index >= 0; index-- ) + { + Command& cmd = mCommands[index]; + switch ( cmd.type ) + { + case CmdAddImposter: + if ( cmd.argv[0] == newCmd.argv[0] ) + { + cmd.argv[0] = newCmd.argv[1]; // Change detail size argument + return false; + } + break; + + case CmdSetDetailLevelSize: + if ( cmd.argv[1] == newCmd.argv[0] ) + { + cmd.argv[1] = newCmd.argv[1]; // Collapse successive detail size changes + if ( cmd.argv[0] == cmd.argv[1] ) + mCommands.erase(index); // Ignore empty changes + return false; + } + else if ( cmd.argv[0] == newCmd.argv[1] ) + return true; // Detail size already in use => cannot go back further + break; + + default: + break; + } + } + + return true; +} + +bool TSShapeConstructor::ChangeSet::addCmd_addImposter( const TSShapeConstructor::ChangeSet::Command& newCmd ) +{ + // Remove previous removeImposter, and replace any previous addImposter. If + // replacing, also remove any setDetailLevelSize for the old imposter + + for ( S32 index = mCommands.size()-1; index >= 0; index-- ) + { + Command& cmd = mCommands[index]; + switch ( cmd.type ) + { + case CmdAddImposter: + // Replace the AddImposter command, but first remove any reference to + // the added detail level. + for ( S32 j = index + 1; j < mCommands.size(); j++ ) + { + Command& cmd2 = mCommands[j]; + if ( ( cmd2.type == CmdSetDetailLevelSize ) && + cmd2.argv[0] == cmd.argv[0] ) + { + mCommands.erase(j); + break; + } + } + // Replace previous addImposter command + cmd = newCmd; + return false; + + case CmdRemoveImposter: + mCommands.erase(index); // Remove previous removeImposter command + break; + + default: + break; + } + } + + return true; +} + +bool TSShapeConstructor::ChangeSet::addCmd_removeImposter( const TSShapeConstructor::ChangeSet::Command& newCmd ) +{ + // Remove any previous addImposter, and also remove any setDetailLevelSize + // for that imposter. + // Always need to return true, since we could be removing imposters already + // present in the shape (not added with addImposter). + + for ( S32 index = mCommands.size()-1; index >= 0; index-- ) + { + Command& cmd = mCommands[index]; + switch ( cmd.type ) + { + case CmdAddImposter: + // Remove the AddImposter command, but first remove any reference to + // the added detail level. + for ( S32 j = index + 1; j < mCommands.size(); j++ ) + { + Command& cmd2 = mCommands[j]; + if ( ( cmd2.type == CmdSetDetailLevelSize ) && + cmd2.argv[0] == cmd.argv[0] ) + { + mCommands.erase(j); + break; + } + } + mCommands.erase(index); + break; + + default: + break; + } + } + + return true; +} diff --git a/Engine/source/ts/tsShapeConstruct.h b/Engine/source/ts/tsShapeConstruct.h new file mode 100644 index 000000000..7d7c94cec --- /dev/null +++ b/Engine/source/ts/tsShapeConstruct.h @@ -0,0 +1,406 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TSSHAPECONSTRUCT_H_ +#define _TSSHAPECONSTRUCT_H_ + +#ifndef __RESOURCE_H__ +#include "core/resource.h" +#endif +#ifndef _MTRANSFORM_H_ +#include "math/mTransform.h" +#endif +#ifndef _TSSHAPE_H_ +#include "ts/tsShape.h" +#endif +#ifndef _SIMBASE_H_ +#include "console/simBase.h" +#endif +#ifndef _COLLADA_UTILS_H_ +#include "ts/collada/colladaUtils.h" +#endif + +/// This class allows an artist to export their animations for the model +/// into the .dsq format. This class in particular matches up the model +/// with the .dsqs to create a nice animated model. +class TSShapeConstructor : public SimObject +{ + typedef SimObject Parent; + +public: + struct ChangeSet + { + enum eCommandType + { + CmdAddNode, + CmdRemoveNode, + CmdRenameNode, + CmdSetNodeTransform, + CmdSetNodeParent, + + CmdAddMesh, + CmdAddPrimitive, + CmdRemoveMesh, + CmdSetMeshSize, + CmdSetMeshType, + CmdSetMeshMaterial, + + CmdRemoveObject, + CmdRenameObject, + CmdSetObjectNode, + CmdSetBounds, + + CmdRenameDetailLevel, + CmdRemoveDetailLevel, + CmdSetDetailLevelSize, + CmdAddImposter, + CmdRemoveImposter, + CmdAddCollisionDetail, + + CmdAddSequence, + CmdRemoveSequence, + CmdRenameSequence, + CmdSetSequenceCyclic, + CmdSetSequenceBlend, + CmdSetSequencePriority, + CmdSetSequenceGroundSpeed, + + CmdAddTrigger, + CmdRemoveTrigger, + + CmdInvalid + }; + + struct Command + { + eCommandType type; // Command type + StringTableEntry name; // Command name + String argv[10]; // Command arguments + S32 argc; // Number of arguments + Command() : type(CmdInvalid), name(0), argc(0) { } + Command( const char* _name ) + : type(CmdInvalid), argc(0) + { + name = StringTable->insert( _name ); + } + + // Helper functions to fill in the command arguments + inline void addArgs() { } + + template< typename A > + inline void addArgs( A a ) + { + argv[argc++] = EngineMarshallData( a ); + } + template< typename A, typename B > void addArgs( A a, B b ) + { + addArgs( a ); + addArgs( b ); + } + template< typename A, typename B, typename C > + inline void addArgs( A a, B b, C c ) + { + addArgs( a ); + addArgs( b, c ); + } + template< typename A, typename B, typename C, typename D > + inline void addArgs( A a, B b, C c, D d ) + { + addArgs( a ); + addArgs( b, c, d ); + } + template< typename A, typename B, typename C, typename D, typename E > + inline void addArgs( A a, B b, C c, D d, E e ) + { + addArgs( a ); + addArgs( b, c, d, e ); + } + template< typename A, typename B, typename C, typename D, typename E, typename F > + inline void addArgs( A a, B b, C c, D d, E e, F f ) + { + addArgs( a ); + addArgs( b, c, d, e, f ); + } + template< typename A, typename B, typename C, typename D, typename E, typename F, typename G > + inline void addArgs( A a, B b, C c, D d, E e, F f, G g ) + { + addArgs( a ); + addArgs( b, c, d, e, f, g ); + } + template< typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H > + inline void addArgs( A a, B b, C c, D d, E e, F f, G g, H h ) + { + addArgs( a ); + addArgs( b, c, d, e, f, g, h ); + } + template< typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I > + inline void addArgs( A a, B b, C c, D d, E e, F f, G g, H h, I i ) + { + addArgs( a ); + addArgs( b, c, d, e, f, g, h, i ); + } + template< typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J > + inline void addArgs( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j ) + { + addArgs( a ); + addArgs( b, c, d, e, f, g, h, i, j ); + } + }; + + Vector mCommands; + + eCommandType getCmdType(const char* name); + void clear() { mCommands.clear(); } + bool empty() { return mCommands.empty(); } + + void add( Command& cmd ); + + // These methods handle change set optimisation based on the newly added command + bool addCmd_setNodeParent( const Command& newCmd ); + bool addCmd_setNodeTransform( const Command& newCmd ); + bool addCmd_renameNode( const Command& newCmd ); + bool addCmd_removeNode( const Command& newCmd ); + + bool addCmd_setMeshSize( const Command& newCmd ); + bool addCmd_setMeshType( const Command& newCmd ); + bool addCmd_setMeshMaterial( const Command& newCmd ); + bool addCmd_removeMesh( const Command& newCmd ); + + bool addCmd_setObjectNode( const Command& newCmd ); + bool addCmd_renameObject( const Command& newCmd ); + bool addCmd_removeObject( const Command& newCmd ); + bool addCmd_setBounds( const Command& newCmd ); + + bool addCmd_renameDetailLevel( const Command& newCmd ); + bool addCmd_removeDetailLevel( const Command& newCmd ); + bool addCmd_setDetailSize( const Command& newCmd ); + bool addCmd_addImposter( const Command& newCmd ); + bool addCmd_removeImposter( const Command& newCmd ); + + bool addCmd_addSequence( Command& newCmd ); + bool addCmd_setSequencePriority( const Command& newCmd ); + bool addCmd_setSequenceGroundSpeed( const Command& newCmd ); + bool addCmd_setSequenceCyclic( const Command& newCmd ); + bool addCmd_setSequenceBlend( const Command& newCmd ); + bool addCmd_renameSequence( const Command& newCmd ); + bool addCmd_removeSequence( const Command& newCmd ); + + bool addCmd_addTrigger( const Command& newCmd ); + bool addCmd_removeTrigger( const Command& newCmd ); + + void write(TSShape* shape, Stream& stream, const String& savePath); + }; + + static const int MaxLegacySequences = 127; + +protected: + FileName mShapePath; + Vector mSequences; + ChangeSet mChangeSet; + + static bool addSequenceFromField( void *obj, const char *index, const char *data ); + + static void _onTSShapeLoaded( Resource< TSShape >& shape ); + static void _onTSShapeUnloaded( const Torque::Path& path, TSShape* shape ); + + static ResourceRegisterPostLoadSignal< TSShape > _smAutoLoad; + static ResourceRegisterUnloadSignal< TSShape > _smAutoUnload; + + /// @name Callbacks + ///@{ + DECLARE_CALLBACK( void, onLoad, () ); + DECLARE_CALLBACK( void, onUnload, () ); + ///@} + + virtual void _onLoad( TSShape* shape ); + virtual void _onUnload(); + +public: + + TSShape* mShape; // Edited shape; NULL while not loaded; not a Resource as we don't want it to prevent from unloading. + ColladaUtils::ImportOptions mOptions; + +public: + + TSShapeConstructor(); + TSShapeConstructor(const String& path) : mShapePath(path) { } + ~TSShapeConstructor(); + + DECLARE_CONOBJECT(TSShapeConstructor); + static void initPersistFields(); + static TSShapeConstructor* findShapeConstructor(const FileName& path); + + bool onAdd(); + + void onScriptChanged(const Torque::Path& path); + + bool writeField(StringTableEntry fieldname, const char *value); + void writeChangeSet(); + + void notifyShapeChanged(); + + TSShape* getShape() const { return mShape; } + const String& getShapePath() const { return mShapePath; } + + /// @name Dumping + ///@{ + void dumpShape( const char* filename ); + void saveShape( const char* filename ); + ///@} + + /// @name Nodes + ///@{ + S32 getNodeCount(); + S32 getNodeIndex( const char* name ); + const char* getNodeName( S32 index ); + const char* getNodeParentName( const char* name ); + bool setNodeParent( const char* name, const char* parentName ); + S32 getNodeChildCount( const char* name ); + const char* getNodeChildName( const char* name, S32 index ); + S32 getNodeObjectCount( const char* name ); + const char* getNodeObjectName( const char* name, S32 index ); + TransformF getNodeTransform( const char* name, bool isWorld=false ); + bool setNodeTransform( const char* name, TransformF txfm, bool isWorld=false ); + bool renameNode( const char* oldName, const char* newName ); + bool addNode( const char* name, const char* parentName, TransformF txfm=TransformF::Identity, bool isWorld=false); + bool removeNode( const char* name ); + ///@} + + /// @name Materials + ///@{ + S32 getTargetCount(); + const char* getTargetName( S32 index ); + ///@} + + ///@{ + S32 getObjectCount(); + const char* getObjectName( S32 index ); + S32 getObjectIndex( const char* name ); + const char* getObjectNode( const char* name ); + bool setObjectNode( const char* objName, const char* nodeName ); + bool renameObject( const char* oldName, const char* newName ); + bool removeObject( const char* name ); + ///@} + + /// @name Meshes + ///@{ + S32 getMeshCount( const char* name ); + const char* getMeshName( const char* name, S32 index ); + S32 getMeshSize( const char* name, S32 index ); + bool setMeshSize( const char* name, S32 size ); + const char* getMeshType( const char* name ); + bool setMeshType( const char* name, const char* type ); + const char* getMeshMaterial( const char* name ); + bool setMeshMaterial( const char* meshName, const char* matName ); + bool addMesh( const char* meshName, const char* srcShape, const char* srcMesh ); + bool addPrimitive( const char* meshName, const char* type, const char* params, TransformF txfm, const char* nodeName ); + bool removeMesh( const char* name ); + ///@} + + /// @name Detail Levels + ///@{ + Box3F getBounds(); + bool setBounds( Box3F bbox ); + S32 getDetailLevelCount(); + const char* getDetailLevelName( S32 index ); + S32 getDetailLevelSize( S32 index); + S32 getDetailLevelIndex( S32 size ); + bool renameDetailLevel( const char* oldName, const char* newName ); + bool removeDetailLevel( S32 index ); + S32 setDetailLevelSize( S32 index, S32 newSize ); + S32 getImposterDetailLevel(); + const char* getImposterSettings( S32 index ); + S32 addImposter( S32 size, S32 equatorSteps, S32 polarSteps, S32 dl, S32 dim, bool includePoles, F32 polarAngle ); + bool removeImposter(); + bool addCollisionDetail( S32 size, const char* type, const char* target, S32 depth=4, F32 merge=30.0f, F32 concavity=30.0f, S32 maxVerts=32, F32 boxMaxError=0, F32 sphereMaxError=0, F32 capsuleMaxError=0 ); + ///@} + + /// @name Sequences + ///@{ + S32 getSequenceCount(); + S32 getSequenceIndex( const char* name); + const char* getSequenceName( S32 index ); + const char* getSequenceSource( const char* name ); + S32 getSequenceFrameCount( const char* name ); + F32 getSequencePriority( const char* name ); + bool setSequencePriority( const char* name, F32 priority ); + const char* getSequenceGroundSpeed( const char* name ); + bool setSequenceGroundSpeed( const char* name, Point3F transSpeed, Point3F rotSpeed=Point3F::Zero ); + bool getSequenceCyclic( const char* name ); + bool setSequenceCyclic( const char* name, bool cyclic ); + const char* getSequenceBlend( const char* name ); + bool setSequenceBlend( const char* name, bool blend, const char* blendSeq, S32 blendFrame ); + bool renameSequence( const char* oldName, const char* newName ); + bool addSequence( const char* source, const char* name, S32 start=0, S32 end=-1, bool padRot=true, bool padTrans=false ); + bool removeSequence( const char* name ); + ///@} + + /// @name Triggers + ///@{ + S32 getTriggerCount( const char* name ); + const char* getTrigger( const char* name, S32 index ); + bool addTrigger( const char* name, S32 keyframe, S32 state ); + bool removeTrigger( const char* name, S32 keyframe, S32 state ); + ///@} +}; + +typedef domUpAxisType TSShapeConstructorUpAxis; +typedef ColladaUtils::ImportOptions::eLodType TSShapeConstructorLodType; + +DefineEnumType( TSShapeConstructorUpAxis ); +DefineEnumType( TSShapeConstructorLodType ); + + +/* This macro simplifies the definition of a TSShapeConstructor API method. It + wraps the actual EngineMethod definition and automatically calls the real + class method. It also creates a ChangeSet::Comand (with all arguments stored + as strings). The one drawback is that it includes the open brace for the real + class method, so to keep the code looking mostly normal, such methods start + with another open brace, and end with a double closing brace. Not perfect, + but a lot better than having to type out the argument list multiple times for + the 50 odd API functions. */ +#define DefineTSShapeConstructorMethod( name, retType, args, defArgs, rawArgs, defRet, usage ) \ + DefineEngineMethod( TSShapeConstructor, name, retType, args, defArgs, usage ) \ + { \ + /* Check that shape is loaded */ \ + if( !object->getShape() ) \ + { \ + Con::errorf( "TSShapeConstructor::" #name " - shape not loaded" ); \ + return defRet; \ + } \ + return object->name rawArgs ; \ + } \ + /* Define the real TSShapeConstructor method */ \ + retType TSShapeConstructor::name args \ + { \ + /* Initialise change set command (may or may not be added) */ \ + TSShapeConstructor::ChangeSet::Command newCmd( #name ); \ + newCmd.addArgs rawArgs ; \ + TORQUE_UNUSED(newCmd); + + +/* This macro just hides the name of the auto-created ChangeSet::Command from + above, so we are free to change the implementation later if needed */ +#define ADD_TO_CHANGE_SET() mChangeSet.add( newCmd ); + + +#endif diff --git a/Engine/source/ts/tsShapeEdit.cpp b/Engine/source/ts/tsShapeEdit.cpp new file mode 100644 index 000000000..bcfb34367 --- /dev/null +++ b/Engine/source/ts/tsShapeEdit.cpp @@ -0,0 +1,2133 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" + +#include "console/consoleTypes.h" +#include "core/resourceManager.h" +#include "ts/tsShape.h" +#include "ts/tsShapeInstance.h" +#include "ts/tsLastDetail.h" +#include "ts/tsMaterialList.h" +#include "core/stream/fileStream.h" +#include "core/volume.h" + + +//----------------------------------------------------------------------------- + +S32 TSShape::addName(const String& name) +{ + // Check for empty names + if (name.isEmpty()) + return -1; + + // Return the index of the new name (add if it is unique) + S32 index = findName(name); + if (index >= 0) + return index; + + names.push_back(StringTable->insert(name)); + return names.size()-1; +} + +void TSShape::updateSmallestVisibleDL() +{ + // Update smallest visible detail + mSmallestVisibleDL = -1; + mSmallestVisibleSize = F32_MAX; + F32 maxSize = 0.0f; + for (S32 i = 0; i < details.size(); i++) + { + maxSize = getMax( maxSize, details[i].size ); + + if ((details[i].size >= 0) && (details[i].size < mSmallestVisibleSize)) + { + mSmallestVisibleDL = i; + mSmallestVisibleSize = details[i].size; + } + } + + // Initialize the detail level lod lookup table. + mDetailLevelLookup.setSize( (U32)( maxSize * 2.0f ) + 2 ); + for ( U32 l=0; l < mDetailLevelLookup.size(); l++ ) + { + F32 pixelSize = (F32)l; + S32 dl = -1; + + for ( U32 d=0; d < details.size(); d++ ) + { + // Break when we get to hidden detail + // levels like collision shapes. + if ( details[d].size < 0 ) + break; + + if ( pixelSize > details[d].size ) + { + dl = d; + break; + } + + if ( d + 1 >= details.size() || details[d+1].size < 0 ) + { + // We've run out of details and haven't found anything? + // Let's just grab this one. + dl = d; + break; + } + } + + // Calculate the intra detail level. + F32 intraDL = 0; + if ( dl > -1 ) + { + F32 curSize = details[dl].size; + F32 nextSize = dl == 0 ? 2.0f * curSize : details[dl - 1].size; + intraDL = mClampF( nextSize - curSize > 0.01f ? (pixelSize - curSize) / (nextSize - curSize) : 1.0f, 0, 1 ); + } + + mDetailLevelLookup[l].set( dl, intraDL ); + } + + // Test for using the legacy screen error + // lod method here instead of runtime. + // + // See setDetailFromDistance(). + // + mUseDetailFromScreenError = mSmallestVisibleDL >= 0 && + details.first().maxError >= 0; +} + +S32 TSShape::addDetail(const String& dname, S32 size, S32 subShapeNum) +{ + S32 nameIndex = addName(avar("%s%d", dname.c_str(), size)); + + // Check if this detail size has already been added + S32 index; + for (index = 0; index < details.size(); index++) + { + if ((details[index].size == size) && + (details[index].subShapeNum == subShapeNum) && + (details[index].nameIndex == nameIndex)) + return index; + if (details[index].size < size) + break; + } + + // Create a new detail level at the right index, so array + // remains sorted by detail size (from largest to smallest) + details.insert(index); + TSShape::Detail &detail = details[index]; + + // Clear the detail to ensure no garbage values + // are left in any vars we don't set. + dMemset( &detail, 0, sizeof( Detail ) ); + + // Setup the detail. + detail.nameIndex = nameIndex; + detail.size = size; + detail.subShapeNum = subShapeNum; + detail.objectDetailNum = 0; + detail.averageError = -1; + detail.maxError = -1; + detail.polyCount = 0; + + // Resize alpha vectors + alphaIn.increment(); + alphaOut.increment(); + + // Fixup objectDetailNum in other detail levels + for (int i = index+1; i < details.size(); i++) + { + if ((details[i].subShapeNum >= 0) && + ((subShapeNum == -1) || (details[i].subShapeNum == subShapeNum))) + details[i].objectDetailNum++; + } + + // Update smallest visible detail + updateSmallestVisibleDL(); + + return index; +} + +S32 TSShape::addImposter(const String& cachePath, S32 size, S32 numEquatorSteps, + S32 numPolarSteps, S32 dl, S32 dim, bool includePoles, F32 polarAngle) +{ + // Check if the desired size is already in use + bool isNewDetail = false; + S32 detIndex = findDetailBySize( size ); + + if ( detIndex >= 0 ) + { + // Size is in use. If the detail is already an imposter, we can just change + // the settings, otherwise quit + if ( details[detIndex].subShapeNum >= 0 ) + { + Con::errorf( "TSShape::addImposter: A non-billboard detail already " + "exists at size %d", size ); + return -1; + } + } + else + { + // Size is not in use. If an imposter already exists, change its size, otherwise + // create a new detail + for ( detIndex = 0; detIndex < details.size(); ++detIndex ) + { + if ( details[detIndex].subShapeNum < 0 ) + { + // Change the imposter detail size + setDetailSize( details[detIndex].size, size ); + break; + } + } + if ( detIndex == details.size() ) + { + isNewDetail = true; + detIndex = addDetail( "bbDetail", size, -1 ); + } + } + + // Now set the billboard properties. + Detail &detail = details[detIndex]; + + // In prior to DTS version 26 we would pack the autobillboard + // into this single 32bit value. This was prone to overflows + // of parameters caused random bugs. + // + // Set the old autobillboard properties var to zero. + detail.objectDetailNum = 0; + + // We now use the new vars. + detail.bbEquatorSteps = numEquatorSteps; + detail.bbPolarSteps = numPolarSteps; + detail.bbPolarAngle = polarAngle; + detail.bbDetailLevel = dl; + detail.bbDimension = dim; + detail.bbIncludePoles = includePoles; + + // Rebuild billboard details or force an update of the modified detail + if ( isNewDetail ) + { + // Add NULL meshes for this detail + for ( S32 iObj = 0; iObj < objects.size(); ++iObj ) + { + if ( detIndex < objects[iObj].numMeshes ) + { + objects[iObj].numMeshes++; + meshes.insert( objects[iObj].startMeshIndex + detIndex, NULL ); + for (S32 j = iObj + 1; j < objects.size(); ++j ) + objects[j].startMeshIndex++; + } + } + + // Could be dedicated server. + if ( GFXDevice::devicePresent() ) + setupBillboardDetails( cachePath ); + + while ( detailCollisionAccelerators.size() < details.size() ) + detailCollisionAccelerators.push_back( NULL ); + } + else + { + if ( billboardDetails.size() && GFXDevice::devicePresent() ) + { + delete billboardDetails[detIndex]; + billboardDetails[detIndex] = new TSLastDetail( + this, + cachePath, + detail.bbEquatorSteps, + detail.bbPolarSteps, + detail.bbPolarAngle, + detail.bbIncludePoles, + detail.bbDetailLevel, + detail.bbDimension ); + + billboardDetails[detIndex]->update( true ); + } + } + + return detIndex; +} + +bool TSShape::removeImposter() +{ + // Find the imposter detail level + S32 detIndex; + for ( detIndex = 0; detIndex < details.size(); ++detIndex ) + { + if ( details[detIndex].subShapeNum < 0 ) + break; + } + + if ( detIndex == details.size() ) + { + Con::errorf( "TSShape::removeImposter: No imposter detail level found in shape" ); + return false; + } + + // Remove the detail level + details.erase( detIndex ); + + if ( detIndex < billboardDetails.size() ) + { + // Delete old textures + TSLastDetail* bb = billboardDetails[detIndex]; + bb->deleteImposterCacheTextures(); + delete billboardDetails[detIndex]; + } + billboardDetails.clear(); + + detailCollisionAccelerators.erase( detIndex ); + + // Remove the (NULL) meshes from each object + for ( S32 iObj = 0; iObj < objects.size(); ++iObj ) + { + if ( detIndex < objects[iObj].numMeshes ) + { + objects[iObj].numMeshes--; + meshes.erase( objects[iObj].startMeshIndex + detIndex ); + for (S32 j = iObj + 1; j < objects.size(); ++j ) + objects[j].startMeshIndex--; + } + } + + // Update smallest visible size + updateSmallestVisibleDL(); + + return true; +} + +//----------------------------------------------------------------------------- + +/// Get the index of the element in the group with a given name +template S32 findByName(Vector& group, S32 nameIndex) +{ + for (S32 i = 0; i < group.size(); i++) + if (group[i].nameIndex == nameIndex) + return i; + return -1; +} + +/// Adjust the nameIndex for elements in the group +template void adjustForNameRemoval(Vector& group, S32 nameIndex) +{ + for (S32 i = 0; i < group.size(); i++) + if (group[i].nameIndex > nameIndex) + group[i].nameIndex--; +} + +bool TSShape::removeName(const String& name) +{ + // Check if the name is still in use + S32 nameIndex = findName(name); + if ((findByName(nodes, nameIndex) >= 0) || + (findByName(objects, nameIndex) >= 0) || + (findByName(sequences, nameIndex) >= 0) || + (findByName(details, nameIndex) >= 0)) + return false; + + // Remove the name, then update nameIndex for affected elements + names.erase(nameIndex); + + adjustForNameRemoval(nodes, nameIndex); + adjustForNameRemoval(objects, nameIndex); + adjustForNameRemoval(sequences, nameIndex); + adjustForNameRemoval(details, nameIndex); + + return true; +} + +//----------------------------------------------------------------------------- + +template bool doRename(TSShape* shape, Vector& group, const String& oldName, const String& newName) +{ + // Find the element in the group with the oldName + S32 index = findByName(group, shape->findName(oldName)); + if (index < 0) + { + Con::errorf("TSShape::rename: Could not find '%s'", oldName.c_str()); + return false; + } + + // Ignore trivial renames + if (oldName.equal(newName, String::NoCase)) + return true; + + // Check that this name is not already in use + if (findByName(group, shape->findName(newName)) >= 0) + { + Con::errorf("TSShape::rename: '%s' is already in use", newName.c_str()); + return false; + } + + // Do the rename (the old name will be removed if it is no longer in use) + group[index].nameIndex = shape->addName(newName); + shape->removeName(oldName); + return true; +} + +bool TSShape::renameNode(const String& oldName, const String& newName) +{ + return doRename(this, nodes, oldName, newName); +} + +bool TSShape::renameObject(const String& oldName, const String& newName) +{ + return doRename(this, objects, oldName, newName); +} + +bool TSShape::renameDetail(const String& oldName, const String& newName) +{ + return doRename(this, details, oldName, newName); +} + +bool TSShape::renameSequence(const String& oldName, const String& newName) +{ + return doRename(this, sequences, oldName, newName); +} + +//----------------------------------------------------------------------------- + +bool TSShape::addNode(const String& name, const String& parentName, const Point3F& pos, const QuatF& rot) +{ + // Check that adding this node would not exceed the maximum count + if (nodes.size() >= MAX_TS_SET_SIZE) + { + Con::errorf("TSShape::addNode: Cannot add node, shape already has maximum (%d) nodes", MAX_TS_SET_SIZE); + return false; + } + + // Check that there is not already a node with this name + if (findNode(name) >= 0) + { + Con::errorf("TSShape::addNode: %s already exists!", name.c_str()); + return false; + } + + // Find the parent node (OK for name to be empty => node is at root level) + S32 parentIndex = -1; + if (dStrcmp(parentName, "")) + { + parentIndex = findNode(parentName); + if (parentIndex < 0) + { + Con::errorf("TSShape::addNode: Could not find parent node '%s'", parentName.c_str()); + return false; + } + } + + // Insert node at the end of the subshape + S32 subShapeIndex = (parentIndex >= 0) ? getSubShapeForNode(parentIndex) : 0; + S32 nodeIndex = subShapeNumNodes[subShapeIndex]; + + // Adjust subshape node indices + subShapeNumNodes[subShapeIndex]++; + for (S32 i = subShapeIndex + 1; i < subShapeFirstNode.size(); i++) + subShapeFirstNode[i]++; + + // Update animation sequences + for (S32 iSeq = 0; iSeq < sequences.size(); iSeq++) + { + // Update animation matters arrays (new node is not animated) + TSShape::Sequence& seq = sequences[iSeq]; + seq.translationMatters.insert(nodeIndex, false); + seq.rotationMatters.insert(nodeIndex, false); + seq.scaleMatters.insert(nodeIndex, false); + } + + // Insert the new node + TSShape::Node node; + node.nameIndex = addName(name); + node.parentIndex = parentIndex; + node.firstChild = -1; + node.firstObject = -1; + node.nextSibling = -1; + nodes.insert(nodeIndex, node); + + // Insert node default translation and rotation + Quat16 rot16; + rot16.set(rot); + defaultTranslations.insert(nodeIndex, pos); + defaultRotations.insert(nodeIndex, rot16); + + // Fixup node indices + for (S32 i = 0; i < nodes.size(); i++) + { + if (nodes[i].parentIndex >= nodeIndex) + nodes[i].parentIndex++; + } + for (S32 i = 0; i < objects.size(); i++) + { + if (objects[i].nodeIndex >= nodeIndex) + objects[i].nodeIndex++; + } + for (S32 i = 0; i < meshes.size(); i++) + { + if (meshes[i] && (meshes[i]->getMeshType() == TSMesh::SkinMeshType)) + { + TSSkinMesh* skin = dynamic_cast(meshes[i]); + for (S32 j = 0; j < skin->batchData.nodeIndex.size(); j++) + { + if (skin->batchData.nodeIndex[j] >= nodeIndex) + skin->batchData.nodeIndex[j]++; + } + } + } + + // Re-initialise the shape + init(); + + return true; +} + +/// Erase animation keyframes (translation, rotation etc) +template S32 eraseStates(Vector& vec, const TSIntegerSet& matters, S32 base, S32 numKeyframes, S32 index=-1) +{ + S32 dest, count; + if (index == -1) + { + // Erase for all nodes/objects + dest = base; + count = numKeyframes * matters.count(); + } + else + { + // Erase for the indexed node/object only + dest = base + matters.count(index)*numKeyframes; + count = numKeyframes; + } + + // Erase the values + if (count) + { + if ((dest + count) < vec.size()) + dCopyArray(&vec[dest], &vec[dest + count], vec.size() - (dest + count)); + vec.decrement(count); + } + return count; +} + +bool TSShape::removeNode(const String& name) +{ + // Find the node to be removed + S32 nodeIndex = findNode(name); + if (nodeIndex < 0) + { + Con::errorf("TSShape::removeNode: Could not find node '%s'", name.c_str()); + return false; + } + + S32 nodeParentIndex = nodes[nodeIndex].parentIndex; + + // Warn if there are objects attached to this node + Vector nodeObjects; + getNodeObjects(nodeIndex, nodeObjects); + if (nodeObjects.size()) + { + Con::warnf("TSShape::removeNode: Node '%s' has %d objects attached, these " + "will be reassigned to the node's parent ('%s')", name.c_str(), nodeObjects.size(), + ((nodeParentIndex >= 0) ? getName(nodes[nodeParentIndex].nameIndex).c_str() : "null")); + } + + // Update animation sequences + for (S32 iSeq = 0; iSeq < sequences.size(); iSeq++) + { + TSShape::Sequence& seq = sequences[iSeq]; + + // Remove animated node transforms + if (seq.translationMatters.test(nodeIndex)) + eraseStates(nodeTranslations, seq.translationMatters, seq.baseTranslation, seq.numKeyframes, nodeIndex); + if (seq.rotationMatters.test(nodeIndex)) + eraseStates(nodeRotations, seq.rotationMatters, seq.baseRotation, seq.numKeyframes, nodeIndex); + if (seq.scaleMatters.test(nodeIndex)) + { + if (seq.flags & TSShape::ArbitraryScale) + { + eraseStates(nodeArbitraryScaleRots, seq.scaleMatters, seq.baseScale, seq.numKeyframes, nodeIndex); + eraseStates(nodeArbitraryScaleFactors, seq.scaleMatters, seq.baseScale, seq.numKeyframes, nodeIndex); + } + else if (seq.flags & TSShape::AlignedScale) + eraseStates(nodeAlignedScales, seq.scaleMatters, seq.baseScale, seq.numKeyframes, nodeIndex); + else + eraseStates(nodeUniformScales, seq.scaleMatters, seq.baseScale, seq.numKeyframes, nodeIndex); + } + + seq.translationMatters.erase(nodeIndex); + seq.rotationMatters.erase(nodeIndex); + seq.scaleMatters.erase(nodeIndex); + } + + // Remove the node + nodes.erase(nodeIndex); + defaultTranslations.erase(nodeIndex); + defaultRotations.erase(nodeIndex); + + // Adjust subshape node indices + S32 subShapeIndex = getSubShapeForNode(nodeIndex); + subShapeNumNodes[subShapeIndex]--; + for (S32 i = subShapeIndex + 1; i < subShapeFirstNode.size(); i++) + subShapeFirstNode[i]--; + + // Fixup node parent indices + for (S32 i = 0; i < nodes.size(); i++) + { + if (nodes[i].parentIndex == nodeIndex) + nodes[i].parentIndex = -1; + else if (nodes[i].parentIndex > nodeIndex) + nodes[i].parentIndex--; + } + if (nodeParentIndex > nodeIndex) + nodeParentIndex--; + + // Fixup object node indices, and re-assign attached objects to node's parent + for (S32 i = 0; i < objects.size(); i++) + { + if (objects[i].nodeIndex == nodeIndex) + objects[i].nodeIndex = nodeParentIndex; + if (objects[i].nodeIndex > nodeIndex) + objects[i].nodeIndex--; + } + + // Fixup skin weight node indices, and re-assign weights for deleted node to its parent + for (S32 i = 0; i < meshes.size(); i++) + { + if (meshes[i] && (meshes[i]->getMeshType() == TSMesh::SkinMeshType)) + { + TSSkinMesh* skin = dynamic_cast(meshes[i]); + for (S32 j = 0; j < skin->batchData.nodeIndex.size(); j++) + { + if (skin->batchData.nodeIndex[j] == nodeIndex) + skin->batchData.nodeIndex[j] = nodeParentIndex; + if (skin->batchData.nodeIndex[j] > nodeIndex) + skin->batchData.nodeIndex[j]--; + } + } + } + + // Remove the sequence name if it is no longer in use + removeName(name); + + // Re-initialise the shape + init(); + + return true; +} + +//----------------------------------------------------------------------------- + +bool TSShape::setNodeTransform(const String& name, const Point3F& pos, const QuatF& rot) +{ + // Find the node to be transformed + S32 nodeIndex = findNode(name); + if (nodeIndex < 0) + { + Con::errorf("TSShape::setNodeTransform: Could not find node '%s'", name.c_str()); + return false; + } + + // Update initial node position and rotation + defaultTranslations[nodeIndex] = pos; + defaultRotations[nodeIndex].set(rot); + + return true; +} + +//----------------------------------------------------------------------------- + +S32 TSShape::addObject(const String& objName, S32 subShapeIndex) +{ + S32 objIndex = subShapeNumObjects[subShapeIndex]; + + // Add object to subshape + subShapeNumObjects[subShapeIndex]++; + for (S32 i = subShapeIndex + 1; i < subShapeFirstObject.size(); i++) + subShapeFirstObject[i]++; + + TSShape::Object obj; + obj.nameIndex = addName(objName); + obj.nodeIndex = 0; + obj.numMeshes = 0; + obj.startMeshIndex = (objIndex == 0) ? 0 : objects[objIndex-1].startMeshIndex + objects[objIndex-1].numMeshes; + obj.firstDecal = 0; + obj.nextSibling = 0; + objects.insert(objIndex, obj); + + // Add default object state + TSShape::ObjectState state; + state.frameIndex = 0; + state.matFrameIndex = 0; + state.vis = 1.0f; + objectStates.insert(objIndex, state); + + // Fixup sequences + for (S32 i = 0; i < sequences.size(); i++) + sequences[i].baseObjectState++; + + return objIndex; +} + +void TSShape::addMeshToObject(S32 objIndex, S32 meshIndex, TSMesh* mesh) +{ + TSShape::Object& obj = objects[objIndex]; + + // Pad with NULLs if required + S32 oldNumMeshes = obj.numMeshes; + if (mesh) + { + for (S32 i = obj.numMeshes; i < meshIndex; i++) + { + meshes.insert(obj.startMeshIndex + i, NULL); + obj.numMeshes++; + } + } + + // Insert the new mesh + meshes.insert(obj.startMeshIndex + meshIndex, mesh); + obj.numMeshes++; + + // Skinned meshes are not attached to any node + if (mesh && (mesh->getMeshType() == TSMesh::SkinMeshType)) + obj.nodeIndex = -1; + + // Fixup mesh indices for other objects + for (S32 i = 0; i < objects.size(); i++) + { + if ((i != objIndex) && (objects[i].startMeshIndex >= obj.startMeshIndex)) + objects[i].startMeshIndex += (obj.numMeshes - oldNumMeshes); + } +} + +void TSShape::removeMeshFromObject(S32 objIndex, S32 meshIndex) +{ + TSShape::Object& obj = objects[objIndex]; + + // Remove the mesh, but do not destroy it (this must be done by the caller) + meshes[obj.startMeshIndex + meshIndex] = NULL; + + // Check if there are any objects remaining that have a valid mesh at this + // detail size + bool removeDetail = true; + for (S32 i = 0; i < objects.size(); i++) + { + if ((meshIndex < objects[i].numMeshes) && meshes[objects[i].startMeshIndex + meshIndex]) + { + removeDetail = false; + break; + } + } + + // Remove detail level if possible + if (removeDetail) + { + for (S32 i = 0; i < objects.size(); i++) + { + if (meshIndex < objects[i].numMeshes) + { + meshes.erase(objects[i].startMeshIndex + meshIndex); + objects[i].numMeshes--; + + for (S32 j = 0; j < objects.size(); j++) + { + if (objects[j].startMeshIndex > objects[i].startMeshIndex) + objects[j].startMeshIndex--; + } + } + } + + Vector validDetails; + getSubShapeDetails(getSubShapeForObject(objIndex), validDetails); + + for (S32 i = 0; i < validDetails.size(); i++) + { + TSShape::Detail& detail = details[validDetails[i]]; + if (detail.objectDetailNum > meshIndex) + detail.objectDetailNum--; + } + + details.erase(validDetails[meshIndex]); + } + + // Remove trailing NULL meshes from the object + S32 oldNumMeshes = obj.numMeshes; + while (obj.numMeshes && !meshes[obj.startMeshIndex + obj.numMeshes - 1]) + { + meshes.erase(obj.startMeshIndex + obj.numMeshes - 1); + obj.numMeshes--; + } + + // Fixup mesh indices for other objects + for (S32 i = 0; i < objects.size(); i++) + { + if (objects[i].startMeshIndex > obj.startMeshIndex) + objects[i].startMeshIndex -= (oldNumMeshes - obj.numMeshes); + } +} + +bool TSShape::setObjectNode(const String& objName, const String& nodeName) +{ + // Find the object and node + S32 objIndex = findObject(objName); + if (objIndex < 0) + { + Con::errorf("TSShape::setObjectNode: Could not find object '%s'", objName.c_str()); + return false; + } + + S32 nodeIndex; + if (nodeName.isEmpty()) + nodeIndex = -1; + else + { + nodeIndex = findNode(nodeName); + if (nodeIndex < 0) + { + Con::errorf("TSShape::setObjectNode: Could not find node '%s'", nodeName.c_str()); + return false; + } + } + + objects[objIndex].nodeIndex = nodeIndex; + + return true; +} + +bool TSShape::removeObject(const String& name) +{ + // Find the object + S32 objIndex = findObject(name); + if (objIndex < 0) + { + Con::errorf("TSShape::removeObject: Could not find object '%s'", name.c_str()); + return false; + } + + // Destroy all meshes in the object + TSShape::Object& obj = objects[objIndex]; + while ( obj.numMeshes ) + { + destructInPlace(meshes[obj.startMeshIndex + obj.numMeshes - 1]); + removeMeshFromObject(objIndex, obj.numMeshes - 1); + } + + // Remove the object from the shape + objects.erase(objIndex); + S32 subShapeIndex = getSubShapeForObject(objIndex); + subShapeNumObjects[subShapeIndex]--; + for (S32 i = subShapeIndex + 1; i < subShapeFirstObject.size(); i++) + subShapeFirstObject[i]--; + + // Remove the object from all sequences + for (S32 i = 0; i < sequences.size(); i++) + { + TSShape::Sequence& seq = sequences[i]; + + TSIntegerSet objMatters(seq.frameMatters); + objMatters.overlap(seq.matFrameMatters); + objMatters.overlap(seq.visMatters); + + if (objMatters.test(objIndex)) + eraseStates(objectStates, objMatters, seq.baseObjectState, seq.numKeyframes, objIndex); + + seq.frameMatters.erase(objIndex); + seq.matFrameMatters.erase(objIndex); + seq.visMatters.erase(objIndex); + } + + // Remove the object name if it is no longer in use + removeName(name); + + // Update smallest visible detail + updateSmallestVisibleDL(); + + // Re-initialise the shape + init(); + + return true; +} + +//----------------------------------------------------------------------------- + +// Helper to copy a TSMesh ready for adding to this TSShape (with the right vertex format etc) +TSMesh* TSShape::copyMesh( const TSMesh* srcMesh ) const +{ + TSMesh * mesh = 0; + if ( srcMesh && ( srcMesh->getMeshType() == TSMesh::SkinMeshType ) ) + { + TSSkinMesh* skin = new TSSkinMesh; + + // Copy skin elements + const TSSkinMesh *srcSkin = dynamic_cast(srcMesh); + skin->weight = srcSkin->weight; + skin->vertexIndex = srcSkin->vertexIndex; + skin->boneIndex = srcSkin->boneIndex; + + skin->batchData.nodeIndex = srcSkin->batchData.nodeIndex; + skin->batchData.initialTransforms = srcSkin->batchData.initialTransforms; + skin->batchData.initialVerts = srcSkin->batchData.initialVerts; + skin->batchData.initialNorms = srcSkin->batchData.initialNorms; + + mesh = static_cast(skin); + } + else + { + mesh = new TSMesh; + } + + // Set vertex format (all meshes in this shape must share the same format) + mesh->mVertSize = mVertSize; + mesh->mVertexFormat = &mVertexFormat; + + if ( !srcMesh ) + return mesh; // return an empty mesh + + // Copy mesh elements + mesh->indices = srcMesh->indices; + mesh->primitives = srcMesh->primitives; + mesh->numFrames = srcMesh->numFrames; + mesh->numMatFrames = srcMesh->numMatFrames; + mesh->vertsPerFrame = srcMesh->vertsPerFrame; + mesh->setFlags(srcMesh->getFlags()); + mesh->mHasColor = srcMesh->mHasColor; + mesh->mHasTVert2 = srcMesh->mHasTVert2; + mesh->mNumVerts = srcMesh->mNumVerts; + + if ( srcMesh->mVertexData.isReady() ) + { + mesh->mVertexData.set( NULL, 0, 0, false ); + void *aligned_mem = dMalloc_aligned( mVertSize * srcMesh->mVertexData.size(), 16 ); + + // Copy the source data (note that the destination shape may have different vertex size) + if ( mVertSize == srcMesh->mVertexData.size() ) + { + dMemcpy( aligned_mem, srcMesh->mVertexData.address(), srcMesh->mVertexData.mem_size() ); + } + else + { + U8* src = (U8*)srcMesh->mVertexData.address(); + U8* dest = (U8*)aligned_mem; + for ( S32 i = 0; i < srcMesh->mVertexData.size(); i++ ) + { + dMemcpy( dest, src, srcMesh->mVertexData.vertSize() ); + src += srcMesh->mVertexData.vertSize(); + dest += mVertSize; + } + } + mesh->mVertexData.set( aligned_mem, mVertSize, srcMesh->mVertexData.size() ); + mesh->mVertexData.setReady( true ); + } + else + { + mesh->verts = srcMesh->verts; + mesh->tverts = srcMesh->tverts; + mesh->tverts2 = srcMesh->tverts2; + mesh->colors = srcMesh->colors; + mesh->norms = srcMesh->norms; + + mesh->createTangents(mesh->verts, mesh->norms); + mesh->encodedNorms.set(NULL,0); + + mesh->convertToAlignedMeshData(); + } + + mesh->computeBounds(); + + if ( mesh->getMeshType() != TSMesh::SkinMeshType ) + mesh->createVBIB(); + + return mesh; +} + +bool TSShape::addMesh(TSMesh* mesh, const String& meshName) +{ + // Determine the object name and detail size from the mesh name + S32 detailSize = 999; + String objName(String::GetTrailingNumber(meshName, detailSize)); + + // Find the destination object (create one if it does not exist) + S32 objIndex = findObject(objName); + if (objIndex < 0) + objIndex = addObject(objName, 0); + AssertFatal(objIndex >= 0 && objIndex < objects.size(), "Invalid object index!"); + + // Determine the subshape this object belongs to + S32 subShapeIndex = getSubShapeForObject(objIndex); + AssertFatal(subShapeIndex < subShapeFirstObject.size(), "Could not find subshape for object!"); + + // Get the existing detail levels for the subshape + Vector validDetails; + getSubShapeDetails(subShapeIndex, validDetails); + + // Determine where to add the new mesh, and whether this is a new detail + S32 detIndex; + bool newDetail = true; + for (detIndex = 0; detIndex < validDetails.size(); detIndex++) + { + const TSShape::Detail& det = details[validDetails[detIndex]]; + if (detailSize >= det.size) + { + newDetail = (det.size != detailSize); + break; + } + } + + // Insert the new detail level if required + if (newDetail) + { + // Determine a name for the detail level + const char* detailName; + if (dStrStartsWith(objName, "Col")) + detailName = "collision"; + else if (dStrStartsWith(objName, "loscol")) + detailName = "los"; + else + detailName = "detail"; + + S32 index = addDetail(detailName, detailSize, subShapeIndex); + details[index].objectDetailNum = detIndex; + } + + // Adding a new mesh or detail level is a bit tricky, since each + // object potentially stores a different number of meshes, including + // NULL meshes for higher detail levels where required. + // For example, the following table shows 3 objects. Note how NULLs + // must be inserted for detail levels higher than the first valid + // mesh, but details after the the last valid mesh are left empty. + // + // Detail | Object1 | Object2 | Object3 + // ---------+-----------+-----------+--------- + // 128 | 128 | NULL | NULL + // 64 | | NULL | 64 + // 32 | | 32 | NULL + // 2 | | | 2 + + // Add meshes as required for each object + for (S32 i = 0; i < subShapeNumObjects[subShapeIndex]; i++) + { + S32 index = subShapeFirstObject[subShapeIndex] + i; + const TSShape::Object& obj = objects[index]; + + if (index == objIndex) + { + // The target object: replace the existing mesh (if any) or add a new one + // if required. + if (!newDetail && (detIndex < obj.numMeshes)) + { + if ( meshes[obj.startMeshIndex + detIndex] ) + destructInPlace(meshes[obj.startMeshIndex + detIndex]); + meshes[obj.startMeshIndex + detIndex] = mesh; + } + else + addMeshToObject(index, detIndex, mesh); + } + else + { + // Other objects: add a NULL mesh only if inserting before a valid mesh + if (newDetail && (detIndex < obj.numMeshes)) + addMeshToObject(index, detIndex, NULL); + } + } + + // Re-initialise the shape + init(); + + return true; +} + +bool TSShape::addMesh(TSShape* srcShape, const String& srcMeshName, const String& meshName) +{ + // Find the mesh in the source shape + TSMesh* srcMesh = srcShape->findMesh(srcMeshName); + if (!srcMesh) + { + Con::errorf("TSShape::addMesh: Could not find mesh '%s' in shape", srcMeshName.c_str()); + return false; + } + + // Copy the source mesh + TSMesh *mesh = copyMesh( srcMesh ); + if (srcMesh->getMeshType() == TSMesh::SkinMeshType) + { + TSSkinMesh *srcSkin = dynamic_cast(srcMesh); + + // Check that the source skin is compatible with our skeleton + Vector nodeMap(srcShape->nodes.size()); + for (S32 i = 0; i < srcShape->nodes.size(); i++) + nodeMap.push_back( findNode( srcShape->getName(srcShape->nodes[i].nameIndex) ) ); + + for (S32 i = 0; i < srcSkin->boneIndex.size(); i++) + { + S32 srcNode = srcSkin->boneIndex[i]; + if (nodeMap[srcNode] == -1) + { + const char* name = srcShape->getName(srcShape->nodes[srcNode].nameIndex).c_str(); + Con::errorf("TSShape::addMesh: Skin is weighted to node (%s) that " + "does not exist in this shape", name); + return false; + } + } + + TSSkinMesh *skin = dynamic_cast(mesh); + + // Remap node indices + skin->batchData.nodeIndex = srcSkin->batchData.nodeIndex; + for (S32 i = 0; i < skin->batchData.nodeIndex.size(); i++) + skin->batchData.nodeIndex[i] = nodeMap[skin->batchData.nodeIndex[i]]; + } + + // Add the copied mesh to the shape + if (!addMesh(mesh, meshName)) + { + delete mesh; + return false; + } + + // Copy materials used by the source mesh (only if from a different shape) + if (srcShape != this) + { + for (S32 i = 0; i < mesh->primitives.size(); i++) + { + if (!(mesh->primitives[i].matIndex & TSDrawPrimitive::NoMaterial)) + { + S32 drawType = (mesh->primitives[i].matIndex & (~TSDrawPrimitive::MaterialMask)); + S32 srcMatIndex = mesh->primitives[i].matIndex & TSDrawPrimitive::MaterialMask; + const String& matName = srcShape->materialList->getMaterialName(srcMatIndex); + + // Add the material if it does not already exist + S32 destMatIndex = materialList->getMaterialNameList().find_next(matName); + if (destMatIndex < 0) + { + destMatIndex = materialList->size(); + materialList->push_back(matName, srcShape->materialList->getFlags(srcMatIndex)); + } + + mesh->primitives[i].matIndex = drawType | destMatIndex; + } + } + } + + return true; +} + +bool TSShape::setMeshSize(const String& meshName, S32 size) +{ + S32 objIndex, meshIndex; + if (!findMeshIndex(meshName, objIndex, meshIndex) || + !meshes[objects[objIndex].startMeshIndex + meshIndex]) + { + Con::errorf("TSShape::setMeshSize: Could not find mesh '%s'", meshName.c_str()); + return false; + } + + // Remove the mesh from the object, but don't destroy it + TSShape::Object& obj = objects[objIndex]; + TSMesh* mesh = meshes[obj.startMeshIndex + meshIndex]; + removeMeshFromObject(objIndex, meshIndex); + + // Add the mesh back at the new position + addMesh(mesh, avar("%s %d", getName(obj.nameIndex).c_str(), size)); + + // Update smallest visible detail + updateSmallestVisibleDL(); + + // Re-initialise the shape + init(); + + return true; +} + +bool TSShape::removeMesh(const String& meshName) +{ + S32 objIndex, meshIndex; + if (!findMeshIndex(meshName, objIndex, meshIndex) || + !meshes[objects[objIndex].startMeshIndex + meshIndex]) + { + Con::errorf("TSShape::removeMesh: Could not find mesh '%s'", meshName.c_str()); + return false; + } + + // Destroy and remove the mesh + TSShape::Object& obj = objects[objIndex]; + destructInPlace(meshes[obj.startMeshIndex + meshIndex]); + removeMeshFromObject(objIndex, meshIndex); + + // Remove the object if there are no meshes left + if (!obj.numMeshes) + removeObject(getName(obj.nameIndex)); + + // Update smallest visible detail + updateSmallestVisibleDL(); + + // Re-initialise the shape + init(); + + return true; +} + +//----------------------------------------------------------------------------- + +// Helper function for dealing with some of the Vectors used in a TSShape. 'meshes' +// for example contains a TSMesh* per-object, per-detail-level, with NULLs for +// undefined details for each object. Trailing NULLs are not added to the Vector, +// so you end up with a different number of pointers for each object, depending +// on which detail levels it defines. This makes it tricky to move meshes around +// since you have to know how many elements belong to each object. +// To simplify things, this function pads the Vector up to a certain length (so +// all objects can appear to have the same number of meshes), the moves a single +// element to a new index, then trims trailing NULLs again. +template +static void _PadMoveAndTrim(Vector& vec, S32 offset, S32 count, + S32 padLength, S32 oldIndex, S32 newIndex) +{ + // Pad the array with NULLs + for ( S32 i = count; i < padLength; ++i ) + vec.insert( offset + count, NULL ); + + // Move the element from the old to the new index + T* tmp = vec[offset + oldIndex]; + vec.erase( offset + oldIndex ); + vec.insert( offset + newIndex, tmp ); + + // Trim trailing NULLs from the vector + for ( S32 i = padLength - 1; i >= 0; --i ) + { + if ( vec[offset + i] ) + break; + else + vec.erase( offset + i ); + } +} + +S32 TSShape::setDetailSize(S32 oldSize, S32 newSize) +{ + S32 oldIndex = findDetailBySize( oldSize ); + if ( oldIndex < 0 ) + { + Con::errorf( "TSShape::setDetailSize: Cannot find detail with size %d", oldSize ); + return -1; + } + + // Remove this detail from the list + TSShape::Detail tmpDetail = details[oldIndex]; + tmpDetail.size = newSize; + details.erase(oldIndex); + + // Determine the new position for the detail (details are sorted by size) + S32 newIndex = 0; + for ( newIndex = 0; newIndex < details.size(); ++newIndex ) + { + if ( newSize > details[newIndex].size ) + break; + } + + // Add the detail at its new position + details.insert( newIndex, tmpDetail ); + + // Rename the detail so its trailing size value is correct + { + S32 tmp; + String oldName( getName( tmpDetail.nameIndex ) ); + String newName( String::GetTrailingNumber( oldName, tmp ) ); + newName += String::ToString( "%d", newSize ); + renameDetail(oldName, newName); + } + + if ( newIndex != oldIndex ) + { + // Fixup details + for ( S32 iDet = 0; iDet < details.size(); iDet++ ) + { + if ( details[iDet].subShapeNum < 0 ) + { + if ( details[iDet].bbDetailLevel == oldIndex ) + details[iDet].bbDetailLevel = newIndex; + } + else + { + details[iDet].objectDetailNum = iDet; + } + } + + // Fixup Billboard details + _PadMoveAndTrim( billboardDetails, 0, billboardDetails.size(), + details.size(), oldIndex, newIndex ); + + // Now move the mesh for each object in the subshape (adding and removing + // NULLs as appropriate) + for ( S32 iObj = 0; iObj < objects.size(); ++iObj ) + { + TSShape::Object& obj = objects[iObj]; + S32 oldMeshCount = meshes.size(); + + _PadMoveAndTrim( meshes, obj.startMeshIndex, obj.numMeshes, + details.size(), oldIndex, newIndex ); + + obj.numMeshes += ( meshes.size() - oldMeshCount ); + + // Fixup startMeshIndex for remaining objects + for ( S32 j = iObj + 1; j < objects.size(); ++j ) + objects[j].startMeshIndex += ( meshes.size() - oldMeshCount ); + } + } + + // Update smallest visible detail + updateSmallestVisibleDL(); + + // Re-initialise the shape + init(); + + return newIndex; +} + +bool TSShape::removeDetail( S32 size ) +{ + S32 dl = findDetailBySize( size ); + + if ( ( dl < 0 ) || ( dl >= details.size() ) ) + { + Con::errorf( "TSShape::removeDetail: Invalid detail index (%d)", dl ); + return false; + } + + // Destroy and remove each mesh in the detail level + for ( S32 objIndex = objects.size()-1; objIndex >= 0; objIndex-- ) + { + TSShape::Object& obj = objects[objIndex]; + if ( dl < obj.numMeshes ) + { + if ( meshes[obj.startMeshIndex + dl] ) + destructInPlace( meshes[obj.startMeshIndex + dl] ); + removeMeshFromObject(objIndex, dl); + + // Remove the object if there are no meshes left + if (!obj.numMeshes) + removeObject( getName( obj.nameIndex ) ); + } + } + + // Destroy billboard detail level + if ( dl < billboardDetails.size() ) + { + if ( billboardDetails[dl] ) + { + // Delete old textures + billboardDetails[dl]->deleteImposterCacheTextures(); + delete billboardDetails[dl]; + } + + billboardDetails.erase( dl ); + } + + // Update smallest visible detail + updateSmallestVisibleDL(); + + // Re-initialise the shape + init(); + + return true; +} + +//----------------------------------------------------------------------------- +bool TSShape::addSequence(const Torque::Path& path, const String& fromSeq, + const String& name, S32 startFrame, S32 endFrame, + bool padRotKeys, bool padTransKeys) +{ + String oldName(fromSeq); + + if (path.getExtension().equal("dsq", String::NoCase)) + { + S32 oldSeqCount = sequences.size(); + + // DSQ source file + char filenameBuf[1024]; + Con::expandScriptFilename(filenameBuf, sizeof(filenameBuf), path.getFullPath().c_str()); + + FileStream *f; + if((f = FileStream::createAndOpen( filenameBuf, Torque::FS::File::Read )) == NULL) + { + Con::errorf("TSShape::addSequence: Could not load DSQ file '%s'", filenameBuf); + return false; + } + if (!importSequences(f, filenameBuf) || (f->getStatus() != Stream::Ok)) + { + delete f; + Con::errorf("TSShape::addSequence: Load sequence file '%s' failed", filenameBuf); + return false; + } + delete f; + + // Rename the new sequence if required (avoid rename if name is not + // unique (this will be fixed up later, and we don't need 2 errors about it!) + if (oldName.isEmpty()) + oldName = getName(sequences.last().nameIndex); + if (!oldName.equal(name)) + { + if (findSequence(name) == -1) + { + // Use a dummy intermediate name since we might be renaming from an + // existing name (and we want to rename the right sequence!) + sequences.last().nameIndex = addName("__dummy__"); + renameSequence("__dummy__", name); + } + } + + // Check that sequences have unique names + bool lastSequenceRejected = false; + for (S32 i = sequences.size()-1; i >= oldSeqCount; i--) + { + S32 nameIndex = (i == sequences.size()-1) ? findName(name) : sequences[i].nameIndex; + S32 seqIndex = findSequence(nameIndex); + if ((seqIndex != -1) && (seqIndex != i)) + { + Con::errorf("TSShape::addSequence: Failed to add sequence '%s' " + "(name already exists)", getName(nameIndex).c_str()); + sequences[i].nameIndex = addName("__dummy__"); + removeSequence("__dummy__"); + if (i == sequences.size()) + lastSequenceRejected = true; + } + } + + // @todo:Need to remove keyframes if start!=0 and end!=-1 + TSShape::Sequence& seq = sequences.last(); + + // Store information about how this sequence was created + seq.sourceData.from = String::ToString("%s\t%s", filenameBuf, name.c_str()); + seq.sourceData.total = seq.numKeyframes; + seq.sourceData.start = ((startFrame < 0) || (startFrame >= seq.numKeyframes)) ? 0 : startFrame; + seq.sourceData.end = ((endFrame < 0) || (endFrame >= seq.numKeyframes)) ? seq.numKeyframes-1 : endFrame; + + return (sequences.size() != oldSeqCount); + } + + /* Check that sequence to be added does not already exist */ + if (findSequence(name) != -1) + { + Con::errorf("TSShape::addSequence: Cannot add sequence '%s' (name already exists)", name.c_str()); + return false; + } + + Resource hSrcShape; + TSShape* srcShape = this; // Assume we are copying an existing sequence + + if (path.getExtension().equal("dts", String::NoCase) || + path.getExtension().equal("dae", String::NoCase)) + { + // DTS or DAE source file + char filenameBuf[1024]; + Con::expandScriptFilename(filenameBuf, sizeof(filenameBuf), path.getFullPath().c_str()); + + hSrcShape = ResourceManager::get().load(filenameBuf); + if (!bool(hSrcShape)) + { + Con::errorf("TSShape::addSequence: Could not load source shape '%s'", path.getFullPath().c_str()); + return false; + } + srcShape = const_cast((const TSShape*)hSrcShape); + if (!srcShape->sequences.size()) + { + Con::errorf("TSShape::addSequence: Source shape '%s' does not contain any sequences", path.getFullPath().c_str()); + return false; + } + + // If no sequence name is specified, just use the first one + if (oldName.isEmpty()) + oldName = srcShape->getName(srcShape->sequences[0].nameIndex); + } + else + { + // Source is an existing sequence + oldName = path.getFullPath(); + } + + // Find the sequence + S32 seqIndex = srcShape->findSequence(oldName); + if (seqIndex < 0) + { + Con::errorf("TSShape::addSequence: Could not find sequence named '%s'", oldName.c_str()); + return false; + } + + // Check keyframe range + const TSShape::Sequence* srcSeq = &srcShape->sequences[seqIndex]; + if ((startFrame < 0) || (startFrame >= srcSeq->numKeyframes)) + { + Con::warnf("TSShape::addSequence: Start keyframe (%d) out of range (0-%d) for sequence '%s'", + startFrame, srcSeq->numKeyframes-1, oldName.c_str()); + startFrame = 0; + } + if (endFrame < 0) + endFrame = srcSeq->numKeyframes - 1; + else if (endFrame >= srcSeq->numKeyframes) + { + Con::warnf("TSShape::addSequence: End keyframe (%d) out of range (0-%d) for sequence '%s'", + endFrame, srcSeq->numKeyframes-1, oldName.c_str()); + endFrame = srcSeq->numKeyframes - 1; + } + + // Create array to map source nodes to our nodes + Vector nodeMap(srcShape->nodes.size()); + for (S32 i = 0; i < srcShape->nodes.size(); i++) + nodeMap.push_back(findNode(srcShape->getName(srcShape->nodes[i].nameIndex))); + + // Create array to map source objects to our objects + Vector objectMap(srcShape->objects.size()); + for (S32 i = 0; i < srcShape->objects.size(); i++) + objectMap.push_back(findObject(srcShape->getName(srcShape->objects[i].nameIndex))); + + // Copy the source sequence (need to do it this ugly way instead of just + // using push_back since srcSeq pointer may change if copying a sequence + // from inside the shape itself + sequences.increment(); + TSShape::Sequence& seq = sequences.last(); + srcSeq = &srcShape->sequences[seqIndex]; // update pointer as it may have changed! + seq = *srcSeq; + + seq.nameIndex = addName(name); + seq.numKeyframes = endFrame - startFrame + 1; + if (seq.duration > 0) + seq.duration *= ((F32)seq.numKeyframes / srcSeq->numKeyframes); + + // Add object states + // Note: only visibility animation is supported + seq.frameMatters.clearAll(); + seq.matFrameMatters.clearAll(); + seq.visMatters.clearAll(); + for (S32 i = 0; i < objectMap.size(); i++) + { + if (objectMap[i] < 0) + continue; + + if (srcSeq->visMatters.test(i)) + { + // Check if visibility is animated within the frames to be copied + const F32 defaultVis = srcShape->objectStates[i].vis; + S32 objNum = srcSeq->visMatters.count(i); + for (int iFrame = startFrame; iFrame <= endFrame; iFrame++) + { + if (srcShape->getObjectState(*srcSeq, iFrame, objNum).vis != defaultVis) + { + seq.visMatters.set(objectMap[i]); + break; + } + } + } + } + + TSIntegerSet srcObjectStateSet(srcSeq->frameMatters); + srcObjectStateSet.overlap(srcSeq->matFrameMatters); + srcObjectStateSet.overlap(srcSeq->visMatters); + + TSIntegerSet objectStateSet(seq.frameMatters); + objectStateSet.overlap(seq.matFrameMatters); + objectStateSet.overlap(seq.visMatters); + + seq.baseObjectState = objectStates.size(); + objectStates.increment(objectStateSet.count()*seq.numKeyframes); + for (S32 i = 0; i < objectMap.size(); i++) + { + if (objectMap[i] < 0) + continue; + + // Note: only visibility animation is supported + if (objectStateSet.test(objectMap[i])) + { + S32 src = srcSeq->baseObjectState + srcSeq->numKeyframes * srcObjectStateSet.count(i) + startFrame; + S32 dest = seq.baseObjectState + seq.numKeyframes * objectStateSet.count(objectMap[i]); + dCopyArray(&objectStates[dest], &srcShape->objectStates[src], seq.numKeyframes); + } + } + + // Add ground frames + F32 ratio = (F32)seq.numKeyframes / srcSeq->numKeyframes; + S32 groundBase = srcSeq->firstGroundFrame + startFrame*ratio; + + seq.numGroundFrames *= ratio; + seq.firstGroundFrame = groundTranslations.size(); + groundTranslations.reserve(groundTranslations.size() + seq.numGroundFrames); + groundRotations.reserve(groundRotations.size() + seq.numGroundFrames); + for (S32 i = 0; i < seq.numGroundFrames; i++) + { + groundTranslations.push_back(srcShape->groundTranslations[groundBase + i]); + groundRotations.push_back(srcShape->groundRotations[groundBase + i]); + } + + // Add triggers + seq.numTriggers = 0; + seq.firstTrigger = triggers.size(); + F32 seqStartPos = (F32)startFrame / seq.numKeyframes; + F32 seqEndPos = (F32)endFrame / seq.numKeyframes; + for (S32 i = 0; i < srcSeq->numTriggers; i++) + { + const TSShape::Trigger& srcTrig = srcShape->triggers[srcSeq->firstTrigger + i]; + if ((srcTrig.pos >= seqStartPos) && (srcTrig.pos <= seqEndPos)) + { + triggers.push_back(srcTrig); + triggers.last().pos -= seqStartPos; + seq.numTriggers++; + } + } + + // Fixup node matters arrays + seq.translationMatters.clearAll(); + seq.rotationMatters.clearAll(); + seq.scaleMatters.clearAll(); + for (S32 i = 0; i < nodeMap.size(); i++) + { + if (nodeMap[i] < 0) + continue; + + if (srcSeq->translationMatters.test(i)) + { + // Check if node position is animated within the frames to be copied + const Point3F& defaultTrans = srcShape->defaultTranslations[i]; + S32 tranNum = srcSeq->translationMatters.count(i); + for (int iFrame = startFrame; iFrame <= endFrame; iFrame++) + { + if (srcShape->getTranslation(*srcSeq, iFrame, tranNum) != defaultTrans) + { + seq.translationMatters.set(nodeMap[i]); + break; + } + } + } + + if (srcSeq->rotationMatters.test(i)) + { + // Check if node rotation is animated within the frames to be copied + const QuatF defaultRot = srcShape->defaultRotations[i].getQuatF(); + S32 rotNum = srcSeq->rotationMatters.count(i); + for (int iFrame = startFrame; iFrame <= endFrame; iFrame++) + { + QuatF temp; + if (srcShape->getRotation(*srcSeq, iFrame, rotNum, &temp) != defaultRot) + { + seq.rotationMatters.set(nodeMap[i]); + break; + } + } + } + if (srcSeq->scaleMatters.test(i)) + { + S32 scaleNum = srcSeq->scaleMatters.count(i); + + // Check if node scale is animated within the frames to be copied + if (srcSeq->animatesArbitraryScale()) + { + TSScale defaultScale; + defaultScale.identity(); + for (int iFrame = startFrame; iFrame <= endFrame; iFrame++) + { + TSScale temp; + if (!(srcShape->getArbitraryScale(*srcSeq, iFrame, scaleNum, &temp) == defaultScale)) + { + seq.scaleMatters.set(nodeMap[i]); + break; + } + } + } + else if (srcSeq->animatesAlignedScale()) + { + const Point3F defaultScale(Point3F::One); + for (int iFrame = startFrame; iFrame <= endFrame; iFrame++) + { + if (srcShape->getAlignedScale(*srcSeq, iFrame, scaleNum) != defaultScale) + { + seq.scaleMatters.set(nodeMap[i]); + break; + } + } + } + else if (srcSeq->animatesUniformScale()) + { + const F32 defaultScale = 1.0f; + for (int iFrame = startFrame; iFrame <= endFrame; iFrame++) + { + if (srcShape->getUniformScale(*srcSeq, iFrame, scaleNum) != defaultScale) + { + seq.scaleMatters.set(nodeMap[i]); + break; + } + } + } + } + } + + // Resize the node transform arrays + seq.baseTranslation = nodeTranslations.size(); + nodeTranslations.increment(seq.translationMatters.count()*seq.numKeyframes); + seq.baseRotation = nodeRotations.size(); + nodeRotations.increment(seq.rotationMatters.count()*seq.numKeyframes); + if (seq.flags & TSShape::ArbitraryScale) + { + S32 scaleCount = seq.scaleMatters.count(); + seq.baseScale = nodeArbitraryScaleRots.size(); + nodeArbitraryScaleRots.increment(scaleCount*seq.numKeyframes); + nodeArbitraryScaleFactors.increment(scaleCount*seq.numKeyframes); + } + else if (seq.flags & TSShape::AlignedScale) + { + seq.baseScale = nodeAlignedScales.size(); + nodeAlignedScales.increment(seq.scaleMatters.count()*seq.numKeyframes); + } + else + { + seq.baseScale = nodeUniformScales.size(); + nodeUniformScales.increment(seq.scaleMatters.count()*seq.numKeyframes); + } + + // Add node transforms (remap from source node indices to our node indices). As + // well as copying animated node translations and rotations, also handle when the + // default translation and rotation are different between the source and + // destination shapes. + for (S32 i = 0; i < nodeMap.size(); i++) + { + if (nodeMap[i] < 0) + continue; + + if (seq.translationMatters.test(nodeMap[i])) + { + S32 src = srcSeq->baseTranslation + srcSeq->numKeyframes * srcSeq->translationMatters.count(i) + startFrame; + S32 dest = seq.baseTranslation + seq.numKeyframes * seq.translationMatters.count(nodeMap[i]); + dCopyArray(&nodeTranslations[dest], &srcShape->nodeTranslations[src], seq.numKeyframes); + } + else if (padTransKeys && (defaultTranslations[nodeMap[i]] != srcShape->defaultTranslations[i])) + { + seq.translationMatters.set(nodeMap[i]); + S32 dest = seq.baseTranslation + seq.numKeyframes * seq.translationMatters.count(nodeMap[i]); + for (S32 j = 0; j < seq.numKeyframes; j++) + nodeTranslations.insert(dest, srcShape->defaultTranslations[i]); + } + + if (seq.rotationMatters.test(nodeMap[i])) + { + S32 src = srcSeq->baseRotation + srcSeq->numKeyframes * srcSeq->rotationMatters.count(i) + startFrame; + S32 dest = seq.baseRotation + seq.numKeyframes * seq.rotationMatters.count(nodeMap[i]); + dCopyArray(&nodeRotations[dest], &srcShape->nodeRotations[src], seq.numKeyframes); + } + else if (padRotKeys && (defaultRotations[nodeMap[i]] != srcShape->defaultRotations[i])) + { + seq.rotationMatters.set(nodeMap[i]); + S32 dest = seq.baseRotation + seq.numKeyframes * seq.rotationMatters.count(nodeMap[i]); + for (S32 j = 0; j < seq.numKeyframes; j++) + nodeRotations.insert(dest, srcShape->defaultRotations[i]); + } + + if (seq.scaleMatters.test(nodeMap[i])) + { + S32 src = srcSeq->baseScale + srcSeq->numKeyframes * srcSeq->scaleMatters.count(i)+ startFrame; + S32 dest = seq.baseScale + seq.numKeyframes * seq.scaleMatters.count(nodeMap[i]); + if (seq.flags & TSShape::ArbitraryScale) + { + dCopyArray(&nodeArbitraryScaleRots[dest], &srcShape->nodeArbitraryScaleRots[src], seq.numKeyframes); + dCopyArray(&nodeArbitraryScaleFactors[dest], &srcShape->nodeArbitraryScaleFactors[src], seq.numKeyframes); + } + else if (seq.flags & TSShape::AlignedScale) + dCopyArray(&nodeAlignedScales[dest], &srcShape->nodeAlignedScales[src], seq.numKeyframes); + else + dCopyArray(&nodeUniformScales[dest], &srcShape->nodeUniformScales[src], seq.numKeyframes); + } + } + + // Set shape flags (only the most significant scale type) + U32 curVal = mFlags & AnyScale; + mFlags &= ~(AnyScale); + mFlags |= getMax(curVal, seq.flags & AnyScale); // take the larger value (can only convert upwards) + + // Set sequence flags + seq.dirtyFlags = 0; + if (seq.rotationMatters.testAll() || seq.translationMatters.testAll() || seq.scaleMatters.testAll()) + seq.dirtyFlags |= TSShapeInstance::TransformDirty; + if (seq.visMatters.testAll()) + seq.dirtyFlags |= TSShapeInstance::VisDirty; + if (seq.frameMatters.testAll()) + seq.dirtyFlags |= TSShapeInstance::FrameDirty; + if (seq.matFrameMatters.testAll()) + seq.dirtyFlags |= TSShapeInstance::MatFrameDirty; + + // Store information about how this sequence was created + seq.sourceData.from = String::ToString("%s\t%s", path.getFullPath().c_str(), oldName.c_str()); + seq.sourceData.total = srcSeq->numKeyframes; + seq.sourceData.start = startFrame; + seq.sourceData.end = endFrame; + + return true; +} + +bool TSShape::removeSequence(const String& name) +{ + // Find the sequence to be removed + S32 seqIndex = findSequence(name); + if (seqIndex < 0) + { + Con::errorf("TSShape::removeSequence: Could not find sequence '%s'", name.c_str()); + return false; + } + + TSShape::Sequence& seq = sequences[seqIndex]; + + // Remove the node transforms for this sequence + S32 transCount = eraseStates(nodeTranslations, seq.translationMatters, seq.baseTranslation, seq.numKeyframes); + S32 rotCount = eraseStates(nodeRotations, seq.rotationMatters, seq.baseRotation, seq.numKeyframes); + S32 scaleCount = 0; + if (seq.flags & TSShape::ArbitraryScale) + { + scaleCount = eraseStates(nodeArbitraryScaleRots, seq.scaleMatters, seq.baseScale, seq.numKeyframes); + eraseStates(nodeArbitraryScaleFactors, seq.scaleMatters, seq.baseScale, seq.numKeyframes); + } + else if (seq.flags & TSShape::AlignedScale) + scaleCount = eraseStates(nodeAlignedScales, seq.scaleMatters, seq.baseScale, seq.numKeyframes); + else + scaleCount = eraseStates(nodeUniformScales, seq.scaleMatters, seq.baseScale, seq.numKeyframes); + + // Remove the object states for this sequence + TSIntegerSet objMatters(seq.frameMatters); + objMatters.overlap(seq.matFrameMatters); + objMatters.overlap(seq.visMatters); + S32 objCount = eraseStates(objectStates, objMatters, seq.baseObjectState, seq.numKeyframes); + + // Remove groundframes and triggers + TSIntegerSet dummy; + eraseStates(groundTranslations, dummy, seq.firstGroundFrame, seq.numGroundFrames, 0); + eraseStates(groundRotations, dummy, seq.firstGroundFrame, seq.numGroundFrames, 0); + eraseStates(triggers, dummy, seq.firstTrigger, seq.numTriggers, 0); + + // Fixup the base indices of the other sequences + for (S32 i = seqIndex + 1; i < sequences.size(); i++) + { + sequences[i].baseTranslation -= transCount; + sequences[i].baseRotation -= rotCount; + sequences[i].baseScale -= scaleCount; + sequences[i].baseObjectState -= objCount; + sequences[i].firstGroundFrame -= seq.numGroundFrames; + sequences[i].firstTrigger -= seq.numTriggers; + } + + // Remove the sequence itself + sequences.erase(seqIndex); + + // Remove the sequence name if it is no longer in use + removeName(name); + + return true; +} + +//----------------------------------------------------------------------------- + +bool TSShape::addTrigger(const String& seqName, S32 keyframe, S32 state) +{ + // Find the sequence + S32 seqIndex = findSequence(seqName); + if (seqIndex < 0) + { + Con::errorf("TSShape::addTrigger: Could not find sequence '%s'", seqName.c_str()); + return false; + } + + TSShape::Sequence& seq = sequences[seqIndex]; + if (keyframe >= seq.numKeyframes) + { + Con::errorf("TSShape::addTrigger: Keyframe out of range (0-%d for sequence '%s')", + seq.numKeyframes-1, seqName.c_str()); + return false; + } + + // Encode the trigger state + if (state < 0) + state = 1 << (-state-1); + else if (state > 0) + state = (1 << (state-1)) | TSShape::Trigger::StateOn; + + // Fixup seq.firstTrigger if this sequence does not have any triggers yet + if (seq.numTriggers == 0) + { + seq.firstTrigger = 0; + for (S32 i = 0; i < seqIndex; i++) + seq.firstTrigger += sequences[i].numTriggers; + } + + // Find where to insert the trigger (sorted by keyframe) + S32 trigIndex; + for (trigIndex = seq.firstTrigger; trigIndex < (seq.firstTrigger + seq.numTriggers); trigIndex++) + { + const TSShape::Trigger& trig = triggers[trigIndex]; + if ((S32)(trig.pos * seq.numKeyframes) > keyframe) + break; + } + + // Create the new trigger + TSShape::Trigger trig; + trig.pos = (F32)keyframe / getMax(1, seq.numKeyframes-1); + trig.state = state; + triggers.insert(trigIndex, trig); + seq.numTriggers++; + + // set invert for other triggers if needed + if ((trig.state & TSShape::Trigger::StateOn) == 0) + { + U32 offTrigger = (trig.state & TSShape::Trigger::StateMask); + for (S32 i = 0; i < seq.numTriggers; i++) + { + if (triggers[seq.firstTrigger + i].state & offTrigger) + triggers[seq.firstTrigger + i].state |= TSShape::Trigger::InvertOnReverse; + } + } + + // fixup firstTrigger index for other sequences + for (S32 i = seqIndex + 1; i < sequences.size(); i++) + { + if (sequences[i].numTriggers > 0) + sequences[i].firstTrigger++; + } + + // set MakePath flag so triggers will be animated + seq.flags |= TSShape::MakePath; + + return true; +} + +bool TSShape::removeTrigger(const String& seqName, S32 keyframe, S32 state) +{ + // Find the sequence + S32 seqIndex = findSequence(seqName); + if (seqIndex < 0) + { + Con::errorf("TSShape::removeTrigger: Could not find sequence '%s'", seqName.c_str()); + return false; + } + + TSShape::Sequence& seq = sequences[seqIndex]; + if (keyframe >= seq.numKeyframes) + { + Con::errorf("TSShape::removeTrigger: Keyframe out of range (0-%d for sequence '%s')", + seq.numKeyframes-1, seqName.c_str()); + return false; + } + + // Encode the trigger state + if (state < 0) + state = 1 << (-state-1); + else if (state > 0) + state = (1 << (state-1)) | TSShape::Trigger::StateOn; + + // Find and remove the trigger + for (S32 trigIndex = seq.firstTrigger; trigIndex < (seq.firstTrigger + seq.numTriggers); trigIndex++) + { + TSShape::Trigger& trig = triggers[trigIndex]; + S32 cmpFrame = (S32)(trig.pos * (seq.numKeyframes-1) + 0.5f); + S32 cmpState = trig.state & (~TSShape::Trigger::InvertOnReverse); + + if ((cmpFrame == keyframe) && (cmpState == state)) + { + triggers.erase(trigIndex); + seq.numTriggers--; + + // Fix up firstTrigger for other sequences + for (S32 i = seqIndex + 1; i < sequences.size(); i++) + { + if (sequences[i].numTriggers > 0) + sequences[i].firstTrigger--; + } + + // Clear MakePath flag if no more triggers + if ( seq.numTriggers == 0 ) + seq.flags &= (~TSShape::MakePath); + + return true; + } + } + + Con::errorf("TSShape::removeTrigger: Could not find trigger (%d, %d) for sequence '%s'", + keyframe, state, seqName.c_str()); + + return false; +} + +void TSShape::getNodeKeyframe(S32 nodeIndex, const TSShape::Sequence& seq, S32 keyframe, MatrixF* mat) const +{ + // Get the node rotation and translation + QuatF rot; + if (seq.rotationMatters.test(nodeIndex)) + { + S32 index = seq.rotationMatters.count(nodeIndex) * seq.numKeyframes + keyframe; + nodeRotations[seq.baseRotation + index].getQuatF(&rot); + } + else + defaultRotations[nodeIndex].getQuatF(&rot); + + Point3F trans; + if (seq.translationMatters.test(nodeIndex)) + { + S32 index = seq.translationMatters.count(nodeIndex) * seq.numKeyframes + keyframe; + trans = nodeTranslations[seq.baseTranslation + index]; + } + else + trans = defaultTranslations[nodeIndex]; + + // Set the keyframe matrix + rot.setMatrix(mat); + mat->setPosition(trans); +} + +bool TSShape::setSequenceBlend(const String& seqName, bool blend, const String& blendRefSeqName, S32 blendRefFrame) +{ + // Find the target sequence + S32 seqIndex = findSequence(seqName); + if (seqIndex < 0) + { + Con::errorf("TSShape::setSequenceBlend: Could not find sequence named '%s'", seqName.c_str()); + return false; + } + TSShape::Sequence& seq = sequences[seqIndex]; + + // Ignore if blend flag is already correct + if (seq.isBlend() == blend) + return true; + + // Find the sequence containing the reference frame + S32 blendRefSeqIndex = findSequence(blendRefSeqName); + if (blendRefSeqIndex < 0) + { + Con::errorf("TSShape::setSequenceBlend: Could not find reference sequence named '%s'", blendRefSeqName.c_str()); + return false; + } + TSShape::Sequence& blendRefSeq = sequences[blendRefSeqIndex]; + + if ((blendRefFrame < 0) || (blendRefFrame >= blendRefSeq.numKeyframes)) + { + Con::errorf("TSShape::setSequenceBlend: Reference frame out of range (0-%d)", blendRefSeq.numKeyframes-1); + return false; + } + + // Set the new flag + if (blend) + seq.flags |= TSShape::Blend; + else + seq.flags &= (~TSShape::Blend); + + // For each animated node in the target sequence, need to add or subtract the + // reference keyframe from each frame + TSIntegerSet nodeMatters(seq.rotationMatters); + nodeMatters.overlap(seq.translationMatters); + + S32 end = nodeMatters.end(); + for (S32 nodeIndex = nodeMatters.start(); nodeIndex < end; nodeMatters.next(nodeIndex)) + { + MatrixF refMat; + getNodeKeyframe(nodeIndex, blendRefSeq, blendRefFrame, &refMat); + + // Add or subtract the reference? + if (blend) + refMat.inverse(); + + bool updateRot(false), updateTrans(false); + S32 rotOffset(0), transOffset(0); + if (seq.rotationMatters.test(nodeIndex)) + { + updateRot = true; + rotOffset = seq.baseRotation + seq.rotationMatters.count(nodeIndex) * seq.numKeyframes; + } + if (seq.translationMatters.test(nodeIndex)) + { + updateTrans = true; + transOffset = seq.baseTranslation + seq.translationMatters.count(nodeIndex) * seq.numKeyframes; + } + + for (S32 frame = 0; frame < seq.numKeyframes; frame++) + { + MatrixF oldMat; + getNodeKeyframe(nodeIndex, seq, frame, &oldMat); + + MatrixF newMat; + newMat.mul(refMat, oldMat); + + if (updateRot) + nodeRotations[rotOffset + frame].set(QuatF(newMat)); + if (updateTrans) + nodeTranslations[transOffset + frame] = newMat.getPosition(); + } + } + + // Update sequence blend information + seq.sourceData.blendSeq = blendRefSeqName; + seq.sourceData.blendFrame = blendRefFrame; + + return true; +} + +bool TSShape::setSequenceGroundSpeed(const String& seqName, const Point3F& trans, const Point3F& rot) +{ + // Find the sequence + S32 seqIndex = findSequence(seqName); + if (seqIndex < 0) + { + Con::errorf("setSequenceGroundSpeed: Could not find sequence named '%s'", seqName.c_str()); + return false; + } + TSShape::Sequence& seq = sequences[seqIndex]; + + // Determine how many ground-frames to generate (FPS=10, at least 1 frame) + const F32 groundFrameRate = 10.0f; + S32 numFrames = getMax(1, (S32)(seq.duration * groundFrameRate)); + + // Allocate space for the frames (add/delete frames as required) + S32 frameAdjust = numFrames - seq.numGroundFrames; + for (S32 i = 0; i < mAbs(frameAdjust); i++) + { + if (frameAdjust > 0) + { + groundTranslations.insert(seq.firstGroundFrame); + groundRotations.insert(seq.firstGroundFrame); + } + else + { + groundTranslations.erase(seq.firstGroundFrame); + groundRotations.erase(seq.firstGroundFrame); + } + } + + // Fixup ground frame indices + seq.numGroundFrames += frameAdjust; + for (S32 i = seqIndex+1; i < sequences.size(); i++) + sequences[i].firstGroundFrame += frameAdjust; + + // Generate the ground-frames + Point3F adjTrans = trans; + Point3F adjRot = rot; + if (seq.numGroundFrames > 0) + { + adjTrans /= seq.numGroundFrames; + adjRot /= seq.numGroundFrames; + } + QuatF rotSpeed(adjRot); + QuatF groundRot(rotSpeed); + for (S32 i = 0; i < seq.numGroundFrames; i++) + { + groundTranslations[seq.firstGroundFrame + i] = adjTrans * (i + 1); + groundRotations[seq.firstGroundFrame + i].set(groundRot); + groundRot *= rotSpeed; + } + + // set MakePath flag so ground frames will be animated + seq.flags |= TSShape::MakePath; + + return true; +} diff --git a/Engine/source/ts/tsShapeInstance.cpp b/Engine/source/ts/tsShapeInstance.cpp new file mode 100644 index 000000000..beb1f7e98 --- /dev/null +++ b/Engine/source/ts/tsShapeInstance.cpp @@ -0,0 +1,778 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "ts/tsShapeInstance.h" + +#include "ts/tsLastDetail.h" +#include "ts/tsMaterialList.h" +#include "console/consoleTypes.h" +#include "ts/tsDecal.h" +#include "platform/profiler.h" +#include "core/frameAllocator.h" +#include "gfx/gfxDevice.h" +#include "materials/materialManager.h" +#include "materials/materialFeatureTypes.h" +#include "materials/sceneData.h" +#include "materials/matInstance.h" +#include "scene/sceneRenderState.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxDrawUtil.h" +#include "core/module.h" + + +MODULE_BEGIN( TSShapeInstance ) + + MODULE_INIT + { + Con::addVariable("$pref::TS::detailAdjust", TypeF32, &TSShapeInstance::smDetailAdjust, + "@brief User perference for scaling the TSShape level of detail.\n" + "The smaller the value the closer the camera must get to see the " + "highest LOD. This setting can have a huge impact on performance in " + "mesh heavy scenes. The default value is 1.\n" + "@ingroup Rendering\n" ); + + Con::addVariable("$pref::TS::skipLoadDLs", TypeS32, &TSShape::smNumSkipLoadDetails, + "@brief User perference which causes TSShapes to skip loading higher lods.\n" + "This potentialy reduces the GPU resources and materials generated as well as " + "limits the LODs rendered. The default value is 0.\n" + "@see $pref::TS::skipRenderDLs\n" + "@ingroup Rendering\n" ); + + Con::addVariable("$pref::TS::skipRenderDLs", TypeS32, &TSShapeInstance::smNumSkipRenderDetails, + "@brief User perference which causes TSShapes to skip rendering higher lods.\n" + "This will reduce the number of draw calls and triangles rendered and improve " + "rendering performance when proper LODs have been created for your models. " + "The default value is 0.\n" + "@see $pref::TS::skipLoadDLs\n" + "@ingroup Rendering\n" ); + + Con::addVariable("$pref::TS::smallestVisiblePixelSize", TypeF32, &TSShapeInstance::smSmallestVisiblePixelSize, + "@brief User perference which sets the smallest pixel size at which TSShapes will skip rendering.\n" + "This will force all shapes to stop rendering when they get smaller than this size. " + "The default value is -1 which disables it.\n" + "@ingroup Rendering\n" ); + + Con::addVariable("$pref::TS::maxInstancingVerts", TypeS32, &TSMesh::smMaxInstancingVerts, + "@brief Enables mesh instancing on non-skin meshes that have less that this count of verts.\n" + "The default value is 200. Higher values can degrade performance.\n" + "@ingroup Rendering\n" ); + } + +MODULE_END; + + +F32 TSShapeInstance::smDetailAdjust = 1.0f; +F32 TSShapeInstance::smSmallestVisiblePixelSize = -1.0f; +S32 TSShapeInstance::smNumSkipRenderDetails = 0; + +F32 TSShapeInstance::smLastScreenErrorTolerance = 0.0f; +F32 TSShapeInstance::smLastScaledDistance = 0.0f; +F32 TSShapeInstance::smLastPixelSize = 0.0f; + +Vector TSShapeInstance::smNodeCurrentRotations(__FILE__, __LINE__); +Vector TSShapeInstance::smNodeCurrentTranslations(__FILE__, __LINE__); +Vector TSShapeInstance::smNodeCurrentUniformScales(__FILE__, __LINE__); +Vector TSShapeInstance::smNodeCurrentAlignedScales(__FILE__, __LINE__); +Vector TSShapeInstance::smNodeCurrentArbitraryScales(__FILE__, __LINE__); +Vector TSShapeInstance::smNodeLocalTransforms(__FILE__, __LINE__); +TSIntegerSet TSShapeInstance::smNodeLocalTransformDirty; + +Vector TSShapeInstance::smRotationThreads(__FILE__, __LINE__); +Vector TSShapeInstance::smTranslationThreads(__FILE__, __LINE__); +Vector TSShapeInstance::smScaleThreads(__FILE__, __LINE__); + +//------------------------------------------------------------------------------------- +// constructors, destructors, initialization +//------------------------------------------------------------------------------------- + +TSShapeInstance::TSShapeInstance( const Resource &shape, bool loadMaterials ) +{ + VECTOR_SET_ASSOCIATION(mMeshObjects); + VECTOR_SET_ASSOCIATION(mNodeTransforms); + VECTOR_SET_ASSOCIATION(mNodeReferenceRotations); + VECTOR_SET_ASSOCIATION(mNodeReferenceTranslations); + VECTOR_SET_ASSOCIATION(mNodeReferenceUniformScales); + VECTOR_SET_ASSOCIATION(mNodeReferenceScaleFactors); + VECTOR_SET_ASSOCIATION(mNodeReferenceArbitraryScaleRots); + VECTOR_SET_ASSOCIATION(mThreadList); + VECTOR_SET_ASSOCIATION(mTransitionThreads); + + mShapeResource = shape; + mShape = mShapeResource; + buildInstanceData( mShape, loadMaterials ); +} + +TSShapeInstance::TSShapeInstance( TSShape *shape, bool loadMaterials ) +{ + VECTOR_SET_ASSOCIATION(mMeshObjects); + VECTOR_SET_ASSOCIATION(mNodeTransforms); + VECTOR_SET_ASSOCIATION(mNodeReferenceRotations); + VECTOR_SET_ASSOCIATION(mNodeReferenceTranslations); + VECTOR_SET_ASSOCIATION(mNodeReferenceUniformScales); + VECTOR_SET_ASSOCIATION(mNodeReferenceScaleFactors); + VECTOR_SET_ASSOCIATION(mNodeReferenceArbitraryScaleRots); + VECTOR_SET_ASSOCIATION(mThreadList); + VECTOR_SET_ASSOCIATION(mTransitionThreads); + + mShapeResource = NULL; + mShape = shape; + buildInstanceData( mShape, loadMaterials ); +} + +TSShapeInstance::~TSShapeInstance() +{ + mMeshObjects.clear(); + + while (mThreadList.size()) + destroyThread(mThreadList.last()); + + setMaterialList(NULL); + + delete [] mDirtyFlags; +} + +void TSShapeInstance::buildInstanceData(TSShape * _shape, bool loadMaterials) +{ + mShape = _shape; + + debrisRefCount = 0; + + mCurrentDetailLevel = 0; + mCurrentIntraDetailLevel = 1.0f; + + // all triggers off at start + mTriggerStates = 0; + + // + mAlphaAlways = false; + mAlphaAlwaysValue = 1.0f; + + // material list... + mMaterialList = NULL; + mOwnMaterialList = false; + + // + mData = 0; + mScaleCurrentlyAnimated = false; + + if(loadMaterials) + setMaterialList(mShape->materialList); + + // set up node data + initNodeTransforms(); + + // add objects to trees + initMeshObjects(); + + // set up subtree data + S32 ss = mShape->subShapeFirstNode.size(); // we have this many subtrees + mDirtyFlags = new U32[ss]; + + mGroundThread = NULL; + mCurrentDetailLevel = 0; + + animateSubtrees(); + + // Construct billboards if not done already + if ( loadMaterials && mShapeResource ) + mShape->setupBillboardDetails( mShapeResource.getPath().getFullPath() ); +} + +void TSShapeInstance::initNodeTransforms() +{ + // set up node data + S32 numNodes = mShape->nodes.size(); + mNodeTransforms.setSize(numNodes); +} + +void TSShapeInstance::initMeshObjects() +{ + // add objects to trees + S32 numObjects = mShape->objects.size(); + mMeshObjects.setSize(numObjects); + for (S32 i=0; iobjects[i]; + MeshObjectInstance * objInst = &mMeshObjects[i]; + + // hook up the object to it's node and transforms. + objInst->mTransforms = &mNodeTransforms; + objInst->nodeIndex = obj->nodeIndex; + + // set up list of meshes + if (obj->numMeshes) + objInst->meshList = &mShape->meshes[obj->startMeshIndex]; + else + objInst->meshList = NULL; + + objInst->object = obj; + objInst->forceHidden = false; + } +} + +void TSShapeInstance::setMaterialList( TSMaterialList *matList ) +{ + // get rid of old list + if ( mOwnMaterialList ) + delete mMaterialList; + + mMaterialList = matList; + mOwnMaterialList = false; + + // If the material list is already be mapped then + // don't bother doing the initializing a second time. + // Note: only check the last material instance as this will catch both + // uninitialised lists, as well as initialised lists that have had new + // materials appended + if ( mMaterialList && !mMaterialList->getMaterialInst( mMaterialList->size()-1 ) ) + { + mMaterialList->setTextureLookupPath( mShapeResource.getPath().getPath() ); + mMaterialList->mapMaterials(); + Material::sAllowTextureTargetAssignment = true; + initMaterialList(); + Material::sAllowTextureTargetAssignment = false; + } +} + +void TSShapeInstance::cloneMaterialList( const FeatureSet *features ) +{ + if ( mOwnMaterialList ) + return; + + mMaterialList = new TSMaterialList(mMaterialList); + initMaterialList( features ); + + mOwnMaterialList = true; +} + +void TSShapeInstance::initMaterialList( const FeatureSet *features ) +{ + // If we don't have features then use the default. + if ( !features ) + features = &MATMGR->getDefaultFeatures(); + + // Initialize the materials. + mMaterialList->initMatInstances( *features, mShape->getVertexFormat() ); + + // TODO: It would be good to go thru all the meshes and + // pre-create all the active material hooks for shadows, + // reflections, and instancing. This would keep these + // hiccups from happening at runtime. +} + +void TSShapeInstance::reSkin( String newBaseName, String oldBaseName ) +{ + if( newBaseName.isEmpty() ) + newBaseName = "base"; + if( oldBaseName.isEmpty() ) + oldBaseName = "base"; + + if ( newBaseName.equal( oldBaseName, String::NoCase ) ) + return; + + const U32 oldBaseNameLength = oldBaseName.length(); + + // Make our own copy of the materials list from the resource if necessary + if (ownMaterialList() == false) + cloneMaterialList(); + + TSMaterialList* pMatList = getMaterialList(); + pMatList->setTextureLookupPath( mShapeResource.getPath().getPath() ); + + // Cycle through the materials + const Vector &materialNames = pMatList->getMaterialNameList(); + for ( S32 i = 0; i < materialNames.size(); i++ ) + { + // Try changing base + const String &pName = materialNames[i]; + if ( pName.compare( oldBaseName, oldBaseNameLength, String::NoCase ) == 0 ) + { + String newName( pName ); + newName.replace( 0, oldBaseNameLength, newBaseName ); + pMatList->renameMaterial( i, newName ); + } + } + + // Initialize the material instances + initMaterialList(); +} + +//------------------------------------------------------------------------------------- +// Render & detail selection +//------------------------------------------------------------------------------------- + +void TSShapeInstance::renderDebugNormals( F32 normalScalar, S32 dl ) +{ + if ( dl < 0 ) + return; + + AssertFatal( dl >= 0 && dl < mShape->details.size(), + "TSShapeInstance::renderDebugNormals() - Bad detail level!" ); + + static GFXStateBlockRef sb; + if ( sb.isNull() ) + { + GFXStateBlockDesc desc; + desc.setCullMode( GFXCullNone ); + desc.setZReadWrite( true ); + desc.zWriteEnable = false; + desc.vertexColorEnable = true; + + sb = GFX->createStateBlock( desc ); + } + GFX->setStateBlock( sb ); + + const TSDetail *detail = &mShape->details[dl]; + const S32 ss = detail->subShapeNum; + if ( ss < 0 ) + return; + + const S32 start = mShape->subShapeFirstObject[ss]; + const S32 end = start + mShape->subShapeNumObjects[ss]; + + for ( S32 i = start; i < end; i++ ) + { + MeshObjectInstance *meshObj = &mMeshObjects[i]; + if ( !meshObj ) + continue; + + const MatrixF &meshMat = meshObj->getTransform(); + + // Then go through each TSMesh... + U32 m = 0; + for( TSMesh *mesh = meshObj->getMesh(m); mesh != NULL; mesh = meshObj->getMesh(m++) ) + { + // and pull out the list of normals. + const U32 numNrms = mesh->mNumVerts; + PrimBuild::begin( GFXLineList, 2 * numNrms ); + for ( U32 n = 0; n < numNrms; n++ ) + { + Point3F norm = mesh->mVertexData[n].normal(); + Point3F vert = mesh->mVertexData[n].vert(); + + meshMat.mulP( vert ); + meshMat.mulV( norm ); + + // Then render them. + PrimBuild::color4f( mFabs( norm.x ), mFabs( norm.y ), mFabs( norm.z ), 1.0f ); + PrimBuild::vertex3fv( vert ); + PrimBuild::vertex3fv( vert + (norm * normalScalar) ); + } + + PrimBuild::end(); + } + } +} + +void TSShapeInstance::renderDebugNodes() +{ + GFXDrawUtil *drawUtil = GFX->getDrawUtil(); + ColorI color( 255, 0, 0, 255 ); + + GFXStateBlockDesc desc; + desc.setBlend( false ); + desc.setZReadWrite( false, false ); + + for ( U32 i = 0; i < mNodeTransforms.size(); i++ ) + drawUtil->drawTransform( desc, mNodeTransforms[i], NULL, NULL ); +} + +void TSShapeInstance::listMeshes( const String &state ) const +{ + if ( state.equal( "All", String::NoCase ) ) + { + for ( U32 i = 0; i < mMeshObjects.size(); i++ ) + { + const MeshObjectInstance &mesh = mMeshObjects[i]; + Con::warnf( "meshidx %3d, %8s, %s", i, ( mesh.forceHidden ) ? "Hidden" : "Visible", mShape->getMeshName(i).c_str() ); + } + } + else if ( state.equal( "Hidden", String::NoCase ) ) + { + for ( U32 i = 0; i < mMeshObjects.size(); i++ ) + { + const MeshObjectInstance &mesh = mMeshObjects[i]; + if ( mesh.forceHidden ) + Con::warnf( "meshidx %3d, %8s, %s", i, "Visible", mShape->getMeshName(i).c_str() ); + } + } + else if ( state.equal( "Visible", String::NoCase ) ) + { + for ( U32 i = 0; i < mMeshObjects.size(); i++ ) + { + const MeshObjectInstance &mesh = mMeshObjects[i]; + if ( !mesh.forceHidden ) + Con::warnf( "meshidx %3d, %8s, %s", i, "Hidden", mShape->getMeshName(i).c_str() ); + } + } + else + { + Con::warnf( "TSShapeInstance::listMeshes( %s ) - only All/Hidden/Visible are valid parameters." ); + } +} + +void TSShapeInstance::render( const TSRenderState &rdata ) +{ + if (mCurrentDetailLevel<0) + return; + + PROFILE_SCOPE( TSShapeInstance_Render ); + + // alphaIn: we start to alpha-in next detail level when intraDL > 1-alphaIn-alphaOut + // (finishing when intraDL = 1-alphaOut) + // alphaOut: start to alpha-out this detail level when intraDL > 1-alphaOut + // NOTE: + // intraDL is at 1 when if shape were any closer to us we'd be at dl-1, + // intraDL is at 0 when if shape were any farther away we'd be at dl+1 + F32 alphaOut = mShape->alphaOut[mCurrentDetailLevel]; + F32 alphaIn = mShape->alphaIn[mCurrentDetailLevel]; + F32 saveAA = mAlphaAlways ? mAlphaAlwaysValue : 1.0f; + + /// This first case is the single detail level render. + if ( mCurrentIntraDetailLevel > alphaIn + alphaOut ) + render( rdata, mCurrentDetailLevel, mCurrentIntraDetailLevel ); + else if ( mCurrentIntraDetailLevel > alphaOut ) + { + // draw this detail level w/ alpha=1 and next detail level w/ + // alpha=1-(intraDl-alphaOut)/alphaIn + + // first draw next detail level + if ( mCurrentDetailLevel + 1 < mShape->details.size() && mShape->details[ mCurrentDetailLevel + 1 ].size > 0.0f ) + { + setAlphaAlways( saveAA * ( alphaIn + alphaOut - mCurrentIntraDetailLevel ) / alphaIn ); + render( rdata, mCurrentDetailLevel + 1, 0.0f ); + } + + setAlphaAlways( saveAA ); + render( rdata, mCurrentDetailLevel, mCurrentIntraDetailLevel ); + } + else + { + // draw next detail level w/ alpha=1 and this detail level w/ + // alpha = 1-intraDL/alphaOut + + // first draw next detail level + if ( mCurrentDetailLevel + 1 < mShape->details.size() && mShape->details[ mCurrentDetailLevel + 1 ].size > 0.0f ) + render( rdata, mCurrentDetailLevel+1, 0.0f ); + + setAlphaAlways( saveAA * mCurrentIntraDetailLevel / alphaOut ); + render( rdata, mCurrentDetailLevel, mCurrentIntraDetailLevel ); + setAlphaAlways( saveAA ); + } +} + +void TSShapeInstance::setMeshForceHidden( const char *meshName, bool hidden ) +{ + Vector::iterator iter = mMeshObjects.begin(); + for ( ; iter != mMeshObjects.end(); iter++ ) + { + S32 nameIndex = iter->object->nameIndex; + const char *name = mShape->names[ nameIndex ]; + + if ( dStrcmp( meshName, name ) == 0 ) + { + iter->forceHidden = hidden; + return; + } + } +} + +void TSShapeInstance::setMeshForceHidden( S32 meshIndex, bool hidden ) +{ + AssertFatal( meshIndex > -1 && meshIndex < mMeshObjects.size(), + "TSShapeInstance::setMeshForceHidden - Invalid index!" ); + + mMeshObjects[meshIndex].forceHidden = hidden; +} + +void TSShapeInstance::render( const TSRenderState &rdata, S32 dl, F32 intraDL ) +{ + AssertFatal( dl >= 0 && dl < mShape->details.size(),"TSShapeInstance::render" ); + + S32 i; + + const TSDetail * detail = &mShape->details[dl]; + S32 ss = detail->subShapeNum; + S32 od = detail->objectDetailNum; + + // if we're a billboard detail, draw it and exit + if ( ss < 0 ) + { + PROFILE_SCOPE( TSShapeInstance_RenderBillboards ); + + if ( !rdata.isNoRenderTranslucent() && ( TSLastDetail::smCanShadow || !rdata.getSceneState()->isShadowPass() ) ) + mShape->billboardDetails[ dl ]->render( rdata, mAlphaAlways ? mAlphaAlwaysValue : 1.0f ); + + return; + } + + // run through the meshes + S32 start = rdata.isNoRenderNonTranslucent() ? mShape->subShapeFirstTranslucentObject[ss] : mShape->subShapeFirstObject[ss]; + S32 end = rdata.isNoRenderTranslucent() ? mShape->subShapeFirstTranslucentObject[ss] : mShape->subShapeFirstObject[ss] + mShape->subShapeNumObjects[ss]; + for (i=start; inames[ mMeshObjects[i].object->nameIndex ]; + mMeshObjects[i].render( od, mMaterialList, rdata, mAlphaAlways ? mAlphaAlwaysValue : 1.0f ); + } +} + +void TSShapeInstance::setCurrentDetail( S32 dl, F32 intraDL ) +{ + PROFILE_SCOPE( TSShapeInstance_setCurrentDetail ); + + mCurrentDetailLevel = mClamp( dl, -1, mShape->mSmallestVisibleDL ); + mCurrentIntraDetailLevel = intraDL > 1.0f ? 1.0f : (intraDL < 0.0f ? 0.0f : intraDL); + + // Restrict the chosen detail level by cutoff value. + if ( smNumSkipRenderDetails > 0 && mCurrentDetailLevel >= 0 ) + { + S32 cutoff = getMin( smNumSkipRenderDetails, mShape->mSmallestVisibleDL ); + if ( mCurrentDetailLevel < cutoff ) + { + mCurrentDetailLevel = cutoff; + mCurrentIntraDetailLevel = 1.0f; + } + } +} + +S32 TSShapeInstance::setDetailFromPosAndScale( const SceneRenderState *state, + const Point3F &pos, + const Point3F &scale ) +{ + VectorF camVector = pos - state->getDiffuseCameraPosition(); + F32 dist = getMax( camVector.len(), 0.01f ); + F32 invScale = ( 1.0f / getMax( getMax( scale.x, scale.y ), scale.z ) ); + + return setDetailFromDistance( state, dist * invScale ); +} + +S32 TSShapeInstance::setDetailFromDistance( const SceneRenderState *state, F32 scaledDistance ) +{ + PROFILE_SCOPE( TSShapeInstance_setDetailFromDistance ); + + // For debugging/metrics. + smLastScaledDistance = scaledDistance; + + // Shortcut if the distance is really close or negative. + if ( scaledDistance <= 0.0f ) + { + mShape->mDetailLevelLookup[0].get( mCurrentDetailLevel, mCurrentIntraDetailLevel ); + return mCurrentDetailLevel; + } + + // The pixel scale is used the linearly scale the lod + // selection based on the viewport size. + // + // The original calculation from TGEA was... + // + // pixelScale = viewport.extent.x * 1.6f / 640.0f; + // + // Since we now work on the viewport height, assuming + // 4:3 aspect ratio, we've changed the reference value + // to 300 to be more compatible with legacy shapes. + // + const F32 pixelScale = state->getViewport().extent.y / 300.0f; + + // This is legacy DTS support for older "multires" based + // meshes. The original crossbow weapon uses this. + // + // If we have more than one detail level and the maxError + // is non-negative then we do some sort of screen error + // metric for detail selection. + // + if ( mShape->mUseDetailFromScreenError ) + { + // The pixel size of 1 meter at the input distance. + F32 pixelRadius = state->projectRadius( scaledDistance, 1.0f ) * pixelScale; + static const F32 smScreenError = 5.0f; + return setDetailFromScreenError( smScreenError / pixelRadius ); + } + + // We're inlining SceneRenderState::projectRadius here to + // skip the unnessasary divide by zero protection. + F32 pixelRadius = ( mShape->radius / scaledDistance ) * state->getWorldToScreenScale().y * pixelScale; + F32 pixelSize = pixelRadius * smDetailAdjust; + + if ( pixelSize > smSmallestVisiblePixelSize && + pixelSize <= mShape->mSmallestVisibleSize ) + pixelSize = mShape->mSmallestVisibleSize + 0.01f; + + // For debugging/metrics. + smLastPixelSize = pixelSize; + + // Clamp it to an acceptable range for the lookup table. + U32 index = (U32)mClampF( pixelSize, 0, mShape->mDetailLevelLookup.size() - 1 ); + + // Check the lookup table for the detail and intra detail levels. + mShape->mDetailLevelLookup[ index ].get( mCurrentDetailLevel, mCurrentIntraDetailLevel ); + + // Restrict the chosen detail level by cutoff value. + if ( smNumSkipRenderDetails > 0 && mCurrentDetailLevel >= 0 ) + { + S32 cutoff = getMin( smNumSkipRenderDetails, mShape->mSmallestVisibleDL ); + if ( mCurrentDetailLevel < cutoff ) + { + mCurrentDetailLevel = cutoff; + mCurrentIntraDetailLevel = 1.0f; + } + } + + return mCurrentDetailLevel; +} + +S32 TSShapeInstance::setDetailFromScreenError( F32 errorTolerance ) +{ + PROFILE_SCOPE( TSShapeInstance_setDetailFromScreenError ); + + // For debugging/metrics. + smLastScreenErrorTolerance = errorTolerance; + + // note: we use 10 time the average error as the metric...this is + // more robust than the maxError...the factor of 10 is to put average error + // on about the same scale as maxError. The errorTOL is how much + // error we are able to tolerate before going to a more detailed version of the + // shape. We look for a pair of details with errors bounding our errorTOL, + // and then we select an interpolation parameter to tween betwen them. Ok, so + // this isn't exactly an error tolerance. A tween value of 0 is the lower poly + // model (higher detail number) and a value of 1 is the higher poly model (lower + // detail number). + + // deal with degenerate case first... + // if smallest detail corresponds to less than half tolerable error, then don't even draw + F32 prevErr; + if ( mShape->mSmallestVisibleDL < 0 ) + prevErr = 0.0f; + else + prevErr = 10.0f * mShape->details[mShape->mSmallestVisibleDL].averageError * 20.0f; + if ( mShape->mSmallestVisibleDL < 0 || prevErr < errorTolerance ) + { + // draw last detail + mCurrentDetailLevel = mShape->mSmallestVisibleDL; + mCurrentIntraDetailLevel = 0.0f; + return mCurrentDetailLevel; + } + + // this function is a little odd + // the reason is that the detail numbers correspond to + // when we stop using a given detail level... + // we search the details from most error to least error + // until we fit under the tolerance (errorTOL) and then + // we use the next highest detail (higher error) + for (S32 i = mShape->mSmallestVisibleDL; i >= 0; i-- ) + { + F32 err0 = 10.0f * mShape->details[i].averageError; + if ( err0 < errorTolerance ) + { + // ok, stop here + + // intraDL = 1 corresponds to fully this detail + // intraDL = 0 corresponds to the next lower (higher number) detail + mCurrentDetailLevel = i; + mCurrentIntraDetailLevel = 1.0f - (errorTolerance - err0) / (prevErr - err0); + return mCurrentDetailLevel; + } + prevErr = err0; + } + + // get here if we are drawing at DL==0 + mCurrentDetailLevel = 0; + mCurrentIntraDetailLevel = 1.0f; + return mCurrentDetailLevel; +} + +//------------------------------------------------------------------------------------- +// Object (MeshObjectInstance & PluginObjectInstance) render methods +//------------------------------------------------------------------------------------- + +void TSShapeInstance::ObjectInstance::render( S32, TSMaterialList *, const TSRenderState &rdata, F32 alpha ) +{ + AssertFatal(0,"TSShapeInstance::ObjectInstance::render: no default render method."); +} + +void TSShapeInstance::MeshObjectInstance::render( S32 objectDetail, + TSMaterialList *materials, + const TSRenderState &rdata, + F32 alpha ) +{ + PROFILE_SCOPE( TSShapeInstance_MeshObjectInstance_render ); + + if ( forceHidden || ( ( visible * alpha ) <= 0.01f ) ) + return; + + TSMesh *mesh = getMesh(objectDetail); + if ( !mesh ) + return; + + const MatrixF &transform = getTransform(); + + if ( rdata.getCuller() ) + { + Box3F box( mesh->getBounds() ); + transform.mul( box ); + if ( rdata.getCuller()->isCulled( box ) ) + return; + } + + GFX->pushWorldMatrix(); + GFX->multWorld( transform ); + + mesh->setFade( visible * alpha ); + + // Pass a hint to the mesh that time has advanced and that the + // skin is dirty and needs to be updated. This should result + // in the skin only updating once per frame in most cases. + const U32 currTime = Sim::getCurrentTime(); + bool isSkinDirty = currTime != mLastTime; + + mesh->render( materials, + rdata, + isSkinDirty, + *mTransforms, + mVertexBuffer, + mPrimitiveBuffer ); + + // Update the last render time. + mLastTime = currTime; + + GFX->popWorldMatrix(); +} + +TSShapeInstance::MeshObjectInstance::MeshObjectInstance() + : meshList(0), object(0), frame(0), matFrame(0), + visible(1.0f), forceHidden(false), mLastTime( 0 ) +{ +} + +void TSShapeInstance::prepCollision() +{ + PROFILE_SCOPE( TSShapeInstance_PrepCollision ); + + // Iterate over all our meshes and call prepCollision on them... + for(S32 i=0; imeshes.size(); i++) + { + if(mShape->meshes[i]) + mShape->meshes[i]->prepOpcodeCollision(); + } +} + diff --git a/Engine/source/ts/tsShapeInstance.h b/Engine/source/ts/tsShapeInstance.h new file mode 100644 index 000000000..f507f7153 --- /dev/null +++ b/Engine/source/ts/tsShapeInstance.h @@ -0,0 +1,795 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TSSHAPEINSTANCE_H_ +#define _TSSHAPEINSTANCE_H_ + +#ifndef __RESOURCE_H__ +#include "core/resource.h" +#endif +#ifndef _TSSHAPE_H_ +#include "ts/tsShape.h" +#endif +#ifndef _TSINTEGERSET_H_ +#include "ts/tsIntegerSet.h" +#endif +#ifndef _CONSOLE_H_ +#include "console/console.h" +#endif +#ifndef _GBITMAP_H_ +#include "gfx/bitmap/gBitmap.h" +#endif +#ifndef _TSRENDERDATA_H_ +#include "ts/tsRenderState.h" +#endif +#ifndef _TSMATERIALLIST_H_ +#include "ts/tsMaterialList.h" +#endif + +class RenderItem; +class TSThread; +class ConvexFeature; +class SceneRenderState; +class FeatureSet; + + +//------------------------------------------------------------------------------------- +// Instance versions of shape objects +//------------------------------------------------------------------------------------- + +class TSCallback +{ +public: + virtual ~TSCallback() {} + + virtual void setNodeTransform(TSShapeInstance * si, S32 nodeIndex, MatrixF & localTransform) = 0; +}; + +/// An instance of a 3space shape. +/// +/// @section TSShapeInstance_intro Introduction +/// +/// A 3space model represents a significant amount of data. There are multiple meshes, +/// skeleton information, as well as animation data. Some of this, like the skeletal +/// transforms, are unique for each instance of the model (as different instances are +/// likely to be in different states of animation), while most of it, like texturing +/// information and vertex data, is the same amongst all instances of the shape. +/// +/// To keep this data from being replicated for every instance of a 3shape object, Torque +/// uses the ResManager to instantiate and track TSShape objects. TSShape handles reading +/// and writing 3space models, as well as keeping track of static model data, as discussed +/// above. TSShapeInstance keeps track of all instance specific data, such as the currently +/// playing sequences or the active node transforms. +/// +/// TSShapeInstance contains all the functionality for 3space models, while TSShape acts as +/// a repository for common data. +/// +/// @section TSShapeInstance_functionality What Does TSShapeInstance Do? +/// +/// TSShapeInstance handles several areas of functionality: +/// - Collision. +/// - Rendering. +/// - Animation. +/// - Updating skeletal transforms. +/// - Ballooning (see setShapeBalloon() and getShapeBalloon()) +/// +/// For an excellent example of how to render a TSShape in game, see TSStatic. For examples +/// of how to procedurally animate models, look at Player::updateLookAnimation(). +class TSShapeInstance +{ + public: + + struct ObjectInstance; + friend class TSThread; + friend class TSLastDetail; + friend class TSPartInstance; + + /// Base class for all renderable objects, including mesh objects and decal objects. + /// + /// An ObjectInstance points to the renderable items in the shape... + struct ObjectInstance + { + virtual ~ObjectInstance() {} + + /// this needs to be set before using an objectInstance...tells us where to + /// look for the transforms...gets set be shape instance 'setStatics' method + const Vector *mTransforms; + + S32 nodeIndex; + + /// Gets the transform of this object + inline const MatrixF& getTransform() const + { + return nodeIndex < 0 ? MatrixF::Identity : (*mTransforms)[ nodeIndex ]; + } + + /// @name Render Functions + /// @{ + + /// Render! This draws the base-textured object. + virtual void render( S32 objectDetail, TSMaterialList *, const TSRenderState &rdata, F32 alpha ); + /// @} + + /// @name Collision Routines + /// @{ + + virtual bool buildPolyList( S32 objectDetail, AbstractPolyList *polyList, U32 &surfaceKey, TSMaterialList *materials ); + virtual bool getFeatures( S32 objectDetail, const MatrixF &mat, const Point3F &n, ConvexFeature *feature, U32 &surfaceKey ); + virtual void support( S32 od, const Point3F &v, F32 *currMaxDP, Point3F *currSupport ); + + virtual bool buildPolyListOpcode( S32 objectDetail, AbstractPolyList *polyList, U32 &surfaceKey, TSMaterialList *materials ); + virtual bool castRayOpcode( S32 objectDetail, const Point3F &start, const Point3F &end, RayInfo *info, TSMaterialList *materials ); + virtual bool buildConvexOpcode( const MatrixF &mat, S32 objectDetail, const Box3F &bounds, Convex *c, Convex *list ); + + /// Ray cast for collision detection + virtual bool castRay( S32 objectDetail, const Point3F &start, const Point3F &end, RayInfo *info, TSMaterialList* materials ) = 0; + /// @} + }; + + /// These are set up by default based on shape data + struct MeshObjectInstance : ObjectInstance + { + TSMesh * const * meshList; ///< one mesh per detail level... Null entries allowed. + const TSObject * object; + S32 frame; + S32 matFrame; + F32 visible; + + /// If true this mesh is forced to be hidden + /// regardless of the animation state. + bool forceHidden; + + TSVertexBufferHandle mVertexBuffer; + GFXPrimitiveBufferHandle mPrimitiveBuffer; + + /// The time at which this mesh + /// was last rendered. + U32 mLastTime; + + MeshObjectInstance(); + virtual ~MeshObjectInstance() {} + + void render( S32 objectDetail, TSMaterialList *, const TSRenderState &rdata, F32 alpha ); + + /// Gets the mesh with specified detail level + TSMesh * getMesh(S32 num) const { return numnumMeshes ? *(meshList+num) : NULL; } + + /// @name Collision Routines + /// @{ + + bool buildPolyList( S32 objectDetail, AbstractPolyList *polyList, U32 &surfaceKey, TSMaterialList *materials ); + bool getFeatures( S32 objectDetail, const MatrixF &mat, const Point3F &n, ConvexFeature *feature, U32 &surfaceKey ); + void support( S32 od, const Point3F &v, F32 *currMaxDP, Point3F *currSupport ); + bool castRay( S32 objectDetail, const Point3F &start, const Point3F &end, RayInfo *info, TSMaterialList *materials ); + bool castRayRendered( S32 objectDetail, const Point3F &start, const Point3F &end, RayInfo *info, TSMaterialList *materials ); + + bool buildPolyListOpcode( S32 objectDetail, AbstractPolyList *polyList, const Box3F &box, TSMaterialList* materials ); + bool castRayOpcode( S32 objectDetail, const Point3F &start, const Point3F &end, RayInfo *info, TSMaterialList *materials ); + bool buildConvexOpcode( const MatrixF &mat, S32 objectDetail, const Box3F &bounds, Convex *c, Convex *list ); + + /// @} + }; + + protected: + + struct TSCallbackRecord + { + TSCallback * callback; + S32 nodeIndex; + }; + +//------------------------------------------------------------------------------------- +// Lists used for storage of transforms, nodes, objects, etc... +//------------------------------------------------------------------------------------- + + public: + + Vector mMeshObjects; + + /// storage space for node transforms + Vector mNodeTransforms; + + /// @name Reference Transform Vectors + /// unused until first transition + /// @{ + Vector mNodeReferenceRotations; + Vector mNodeReferenceTranslations; + Vector mNodeReferenceUniformScales; + Vector mNodeReferenceScaleFactors; + Vector mNodeReferenceArbitraryScaleRots; + /// @} + + /// @name Workspace for Node Transforms + /// @{ + static Vector smNodeCurrentRotations; + static Vector smNodeCurrentTranslations; + static Vector smNodeCurrentUniformScales; + static Vector smNodeCurrentAlignedScales; + static Vector smNodeCurrentArbitraryScales; + static Vector smNodeLocalTransforms; + static TSIntegerSet smNodeLocalTransformDirty; + /// @} + + /// @name Threads + /// keep track of who controls what on currently animating shape + /// @{ + static Vector smRotationThreads; + static Vector smTranslationThreads; + static Vector smScaleThreads; + /// @} + + TSMaterialList* mMaterialList; ///< by default, points to hShape material list +//------------------------------------------------------------------------------------- +// Misc. +//------------------------------------------------------------------------------------- + +protected: + + /// @name Ground Transform Data + /// @{ + MatrixF mGroundTransform; + TSThread * mGroundThread; + /// @} + + bool mScaleCurrentlyAnimated; + + S32 mCurrentDetailLevel; + + /// 0-1, how far along from current to next (higher) detail level... + /// + /// 0=at this dl, 1=at higher detail level, where higher means bigger size on screen + /// for dl=0, we use twice detail level 0's size as the size of the "next" dl + F32 mCurrentIntraDetailLevel; + + /// This is only valid when the instance was created from + /// a resource. Else it is null. + Resource mShapeResource; + + /// This should always point to a valid shape and should + /// equal mShapeResource if it was created from a resource. + TSShape *mShape; + + + bool mOwnMaterialList; ///< Does this own the material list pointer? + + bool mAlphaAlways; + F32 mAlphaAlwaysValue; + + bool mUseOverrideTexture; + + U32 debrisRefCount; + + // the threads... + Vector mThreadList; + Vector mTransitionThreads; + + /// @name Transition nodes + /// keep track of nodes that are involved in a transition + /// + /// @note this only tracks nodes we're transitioning from... + /// nodes we're transitioning to are implicitly handled + /// (i.e., we don't need to keep track of them) + /// @{ + + TSIntegerSet mTransitionRotationNodes; + TSIntegerSet mTransitionTranslationNodes; + TSIntegerSet mTransitionScaleNodes; + /// @} + + /// keep track of nodes with animation restrictions put on them + TSIntegerSet mMaskRotationNodes; + TSIntegerSet mMaskPosXNodes; + TSIntegerSet mMaskPosYNodes; + TSIntegerSet mMaskPosZNodes; + TSIntegerSet mDisableBlendNodes; + TSIntegerSet mHandsOffNodes; ///< Nodes that aren't animated through threads automatically + TSIntegerSet mCallbackNodes; + + // node callbacks + Vector mNodeCallbacks; + + /// state variables + U32 mTriggerStates; + + bool initGround(); + void addPath(TSThread * gt, F32 start, F32 end, MatrixF * mat = NULL); + + public: + + TSShape* getShape() const { return mShape; } + + TSMaterialList* getMaterialList() const { return mMaterialList; } + + /// Set the material list without taking ownership. + /// @see cloneMaterialList + void setMaterialList( TSMaterialList *matList ); + + /// Call this to own the material list -- i.e., we'll make a copy of the + /// currently set material list and be responsible for deleting it. You + /// can pass an optional feature set for initializing the cloned materials. + void cloneMaterialList( const FeatureSet *features = NULL ); + + /// Initializes or re-initializes the material list with + /// an optional feature set. + void initMaterialList( const FeatureSet *features = NULL ); + + bool ownMaterialList() const { return mOwnMaterialList; } + + /// Get the number of material targets in this shape instance + S32 getTargetCount() const + { + if ( mOwnMaterialList ) + return getMaterialList()->size(); + else + return getShape()->getTargetCount(); + } + + /// Get the indexed material target (may differ from the base TSShape material + /// list if this instance has been reskinned). + const String& getTargetName( S32 mapToNameIndex ) const + { + if ( mOwnMaterialList ) + { + if ( mapToNameIndex < 0 || mapToNameIndex >= getMaterialList()->size() ) + return String::EmptyString; + + return getMaterialList()->getMaterialName( mapToNameIndex ); + } + else + { + return getShape()->getTargetName( mapToNameIndex ); + } + } + + void reSkin( String newBaseName, String oldBaseName = String::EmptyString ); + + enum + { + MaskNodeRotation = 0x01, + MaskNodePosX = 0x02, + MaskNodePosY = 0x04, + MaskNodePosZ = 0x08, + MaskNodeBlend = 0x10, + MaskNodeAll = MaskNodeRotation|MaskNodePosX|MaskNodePosY|MaskNodePosZ|MaskNodeBlend, + MaskNodeAllButBlend = MaskNodeRotation|MaskNodePosX|MaskNodePosY|MaskNodePosZ, + MaskNodeAllButRotation = MaskNodePosX|MaskNodePosY|MaskNodePosZ|MaskNodeBlend, + MaskNodeAllButPosX = MaskNodeRotation|MaskNodePosY|MaskNodePosZ|MaskNodeBlend, + MaskNodeAllButPosY = MaskNodeRotation|MaskNodePosX|MaskNodePosZ|MaskNodeBlend, + MaskNodeAllButPosZ = MaskNodeRotation|MaskNodePosX|MaskNodePosY|MaskNodeBlend, + MaskNodeHandsOff = 0x20, ///< meaning, don't even set to default, programmer controls it (blend still applies) + MaskNodeCallback = 0x40 ///< meaning, get local transform via callback function (see setCallback) + ///< callback data2 is node index, callback return value is pointer to local transform + ///< Note: won't get this callback everytime you animate...application responsibility + ///< to make sure matrix pointer continues to point to valid and updated local transform + }; + /// @name Node Masking + /// set node masking... + /// @{ + void setNodeAnimationState(S32 nodeIndex, U32 animationState, TSCallback * callback = NULL); + U32 getNodeAnimationState(S32 nodeIndex); + /// @} + + /// @name Trigger states + /// check trigger value + /// @{ + bool getTriggerState(U32 stateNum, bool clearState = true); + void setTriggerState(U32 stateNum, bool on); + void setTriggerStateBit(U32 stateBit, bool on); + /// @} + + /// @name Debris Management + /// @{ + void incDebrisRefCount() { ++debrisRefCount; } + void decDebrisRefCount() { debrisRefCount > 0 ? --debrisRefCount : 0; } + U32 getDebrisRefCount() const { return debrisRefCount; } + /// @} + + /// @name AlphaAlways + /// AlphaAlways allows the entire model to become translucent at the same value + /// @{ + void setAlphaAlways(F32 value) { mAlphaAlways = (value<0.99f); mAlphaAlwaysValue = value; } + F32 getAlphaAlwaysValue() const { return mAlphaAlways ? mAlphaAlwaysValue : 1.0f; } + bool getAlphaAlways() const { return mAlphaAlways; } + /// @} + +//------------------------------------------------------------------------------------- +// private methods for setting up and affecting animation +//------------------------------------------------------------------------------------- + + private: + + /// @name Private animation methods + /// These are private methods for setting up and affecting animation + /// @{ + void sortThreads(); + + void updateTransitions(); + void handleDefaultScale(S32 a, S32 b, TSIntegerSet & scaleBeenSet); + void updateTransitionNodeTransforms(TSIntegerSet& transitionNodes); + void handleTransitionNodes(S32 a, S32 b); + void handleNodeScale(S32 a, S32 b); + void handleAnimatedScale(TSThread *, S32 a, S32 b, TSIntegerSet &); + void handleMaskedPositionNode(TSThread *, S32 nodeIndex, S32 offset); + void handleBlendSequence(TSThread *, S32 a, S32 b); + void checkScaleCurrentlyAnimated(); + /// @} + +//------------------------------------------------------------------------------------- +// animate, render, & detail control +//------------------------------------------------------------------------------------- + + public: + + struct RenderData + { + MeshObjectInstance* currentObjectInstance; + + S32 detailLevel; + S32 materialIndex; + const Point3F * objectScale; + }; + + /// Scale pixel size by this amount when selecting + /// detail levels. + static F32 smDetailAdjust; + + /// If this is set to a positive pixel value shapes + /// with a smaller pixel size than this will skip + /// rendering entirely. + static F32 smSmallestVisiblePixelSize; + + /// never choose detail level number below this value (except if + /// only way to get a visible detail) + static S32 smNumSkipRenderDetails; + + /// For debugging / metrics. + static F32 smLastScreenErrorTolerance; + static F32 smLastScaledDistance; + static F32 smLastPixelSize; + + /// Debugging + /// @{ + + /// Renders the vertex normals assuming the GFX state + /// is setup for rendering in model space. + void renderDebugNormals( F32 normalScalar, S32 dl ); + + /// Render all node transforms as small axis gizmos. It is recommended + /// that prior to calling this, shapeInstance::animate is called so that + /// nodes are in object space and that the GFX state is setup for + /// rendering in model space. + void renderDebugNodes(); + + /// Print mesh data to the console, valid String parameters + /// are Visible, Hidden, or All. + void listMeshes( const String &state ) const; + + /// @} + + virtual void render( const TSRenderState &rdata ); + virtual void render( const TSRenderState &rdata, S32 dl, F32 intraDL = 0.0f ); + + void animate() { animate( mCurrentDetailLevel ); } + void animate(S32 dl); + void animateNodes(S32 ss); + void animateVisibility(S32 ss); + void animateFrame(S32 ss); + void animateMatFrame(S32 ss); + void animateSubtrees(bool forceFull = true); + void animateNodeSubtrees(bool forceFull = true); + + /// Sets the 'forceHidden' state on the named mesh. + /// @see MeshObjectInstance::forceHidden + void setMeshForceHidden( const char *meshName, bool hidden ); + + /// Sets the 'forceHidden' state on a mesh. + /// @see MeshObjectInstance::forceHidden + void setMeshForceHidden( S32 meshIndex, bool hidden ); + + /// @name Animation Scale + /// Query about animated scale + /// @{ + bool animatesScale() { return (mShape->mFlags & TSShape::AnyScale) != 0; } + bool animatesUniformScale() { return (mShape->mFlags & TSShape::UniformScale) != 0; } + bool animatesAlignedScale() { return (mShape->mFlags & TSShape::AlignedScale) != 0; } + bool animatesArbitraryScale() { return (mShape->mFlags & TSShape::ArbitraryScale) != 0; } + bool scaleCurrentlyAnimated() { return mScaleCurrentlyAnimated; } + /// @} + + // + bool inTransition() { return !mTransitionThreads.empty(); } + + /// @name Ground Transforms + /// The animator of a model can make the bounding box + /// animate along with the object. Doing so will move the object with the bounding box. + /// The ground transform turns the world bounding box into the post-animation bounding box + /// when such a technique is used. However, few models actually use this technique. + /// @{ + + void animateGround(); ///< clears previous ground transform + MatrixF & getGroundTransform() { return mGroundTransform; } + void deltaGround(TSThread *, F32 start, F32 end, MatrixF * mat = NULL); + void deltaGround1(TSThread *, F32 start, F32 end, MatrixF& mat); + /// @} + + U32 getNumDetails() const { return mShape ? mShape->details.size() : 0; } + + S32 getCurrentDetail() const { return mCurrentDetailLevel; } + + F32 getCurrentIntraDetail() const { return mCurrentIntraDetailLevel; } + + void setCurrentDetail( S32 dl, F32 intraDL = 1.0f ); + + /// Helper function which internally calls setDetailFromDistance. + S32 setDetailFromPosAndScale( const SceneRenderState *state, + const Point3F &pos, + const Point3F &scale ); + + /// Selects the current detail level using the scaled + /// distance between your object and the camera. + /// + /// @see TSShape::Detail. + S32 setDetailFromDistance( const SceneRenderState *state, F32 scaledDist ); + + /// Sets the current detail level using the legacy screen error metric. + S32 setDetailFromScreenError( F32 errorTOL ); + + enum + { + TransformDirty = BIT(0), + VisDirty = BIT(1), + FrameDirty = BIT(2), + MatFrameDirty = BIT(3), + ThreadDirty = BIT(4), + AllDirtyMask = TransformDirty | VisDirty | FrameDirty | MatFrameDirty | ThreadDirty + }; + U32 * mDirtyFlags; + void setDirty(U32 dirty); + void clearDirty(U32 dirty); + +//------------------------------------------------------------------------------------- +// collision interface routines +//------------------------------------------------------------------------------------- + + public: + + bool buildPolyList(AbstractPolyList *, S32 dl); + bool getFeatures(const MatrixF& mat, const Point3F& n, ConvexFeature*, S32 dl); + bool castRay(const Point3F & start, const Point3F & end, RayInfo *,S32 dl); + bool castRayRendered(const Point3F & start, const Point3F & end, RayInfo *,S32 dl); + bool quickLOS(const Point3F & start, const Point3F & end, S32 dl) { return castRay(start,end,NULL,dl); } + Point3F support(const Point3F & v, S32 dl); + void computeBounds(S32 dl, Box3F & bounds); ///< uses current transforms to compute bounding box around a detail level + ///< see like named method on shape if you want to use default transforms + + bool buildPolyListOpcode( S32 dl, AbstractPolyList *polyList, const Box3F &box ); + bool castRayOpcode( S32 objectDetail, const Point3F & start, const Point3F & end, RayInfo *); + bool buildConvexOpcode( const MatrixF &objMat, const Point3F &objScale, S32 objectDetail, const Box3F &bounds, Convex *c, Convex *list ); + +//------------------------------------------------------------------------------------- +// Thread Control +//------------------------------------------------------------------------------------- + + /// @name Thread Control + /// Threads! In order to animate an object, first you need to have an animation in the object. + /// Then, you need to get the TSShape of the object: + /// @code + /// TSShape* shape = mShapeInstance->getShape()); + /// @endcode + /// Next, get the sequence and store:: + /// @code + /// S32 seq = shape->findSequence("foo")); + /// @endcode + /// Create a new thread (if needed): + /// @code + /// TSThread* thread = mShapeInstance->addThread(); + /// @endcode + /// Finally, set the position in the sequence: + /// @code + /// mShapeInstance->setSequence(thread, seq, 0) + /// @endcode + /// @{ + + public: + + TSThread * addThread(); ///< Create a new thread + TSThread * getThread(S32 threadNumber); ///< @note threads can change order, best to hold + ///< onto a thread from the start + void destroyThread(TSThread * thread); ///< Destroy a thread! + U32 threadCount(); ///< How many threads are there? + + void setSequence(TSThread *, S32 seq, F32 pos);///< Get the thread a sequence + /// Transition to a sequence + void transitionToSequence(TSThread *, S32 seq, F32 pos, F32 duration, bool continuePlay); + void clearTransition(TSThread *); ///< Stop transitions + U32 getSequence(TSThread *); ///< Get the sequence of the thread + + void setBlendEnabled(TSThread *, bool blendOn);///< Set whether or not the thread will blend + bool getBlendEnabled(TSThread *); ///< Does this thread blend? + + void setPriority(TSThread *, F32 priority); ///< Set thread priority + F32 getPriority(TSThread * thread); ///< Get thread priority + + F32 getTime(TSThread * thread); ///< Get how long the thread has been playing + F32 getPos(TSThread * thread); ///< Get the position in the thread + + void setTime(TSThread * thread, F32 time); ///< Set how long into the thread to use + void setPos(TSThread * thread, F32 pos); ///< Set the position of the thread + + bool isInTransition(TSThread * thread); ///< Is this thread in transition? + F32 getTimeScale(TSThread * thread); ///< Get the time scale of the thread + void setTimeScale(TSThread * thread, F32); ///< Set the time scale of the thread + + F32 getDuration(TSThread * thread); ///< Get the duration of the thread + F32 getScaledDuration(TSThread * thread); ///< Get the duration of the thread with the scale factored in + + S32 getKeyframeCount(TSThread * thread); ///< Get the number of keyframes + S32 getKeyframeNumber(TSThread * thread); ///< Get which keyframe the thread is on + /// Set which keyframe the thread is on + void setKeyframeNumber(TSThread * thread, S32 kf); + + void advanceTime(F32 delta, TSThread *); ///< advance time on a particular thread + void advanceTime(F32 delta); ///< advance time on all threads + void advancePos(F32 delta, TSThread *); ///< advance pos on a particular thread + void advancePos(F32 delta); ///< advance pos on all threads + /// @} + +//------------------------------------------------------------------------------------- +// constructors, destructors, initialization, io +//------------------------------------------------------------------------------------- + + TSShapeInstance( const Resource & shape, bool loadMaterials = true); + TSShapeInstance( TSShape * pShape, bool loadMaterials = true); + ~TSShapeInstance(); + + void buildInstanceData(TSShape *, bool loadMaterials); + void initNodeTransforms(); + void initMeshObjects(); + + void dump(Stream &); + void dumpNode(Stream &, S32 level, S32 nodeIndex, Vector & detailSizes); + + void *mData; ///< available for use by app...initialized to 0 + + void prepCollision(); +}; + + +//------------------------------------------------------------------------------------- +// Thread class +//------------------------------------------------------------------------------------- + +/// 3space animation thread. +/// +/// An animation thread: runtime data associated with a single sequence that is +/// running (or two sequences if in transition between them). +/// +/// A shape instance can have multiple threads running. When multiple threads are running, +/// which thread/sequence controls which node or object is determined based +/// on priority of the sequence. +/// +/// @note all thread data and methods are private (but TSShapeInstance is a friend). +/// Users should treat thread pointers like keys -- they are used to ID +/// the thread when interfacing with the shape, but are not manipulated +/// by anything but the TSShapeInstance. See "Thread control" methods +/// for more info on controlling threads. +class TSThread +{ + friend class TSShapeInstance; + + S32 priority; + + TSShapeInstance * mShapeInstance; ///< Instance of the shape that this thread animates + + S32 sequence; ///< Sequence this thread will perform + F32 pos; + + F32 timeScale; ///< How fast to play through the sequence + + S32 keyNum1; ///< Keyframe at or before current position + S32 keyNum2; ///< Keyframe at or after current position + F32 keyPos; + + bool blendDisabled; ///< Blend with other sequences? + + /// if in transition... + struct TransitionData + { + bool inTransition; + + F32 duration; + F32 pos; + F32 direction; + F32 targetScale; ///< time scale for sequence we are transitioning to (during transition only) + ///< this is either 1 or 0 (if 1 target sequence plays as we transition, if 0 it doesn't) + TSIntegerSet oldRotationNodes; ///< nodes controlled by this thread pre-transition + TSIntegerSet oldTranslationNodes; ///< nodes controlled by this thread pre-transition + TSIntegerSet oldScaleNodes; ///< nodes controlled by this thread pre-transition + U32 oldSequence; ///< sequence that was set before transition began + F32 oldPos; ///< position of sequence before transition began + } transitionData; + + struct + { + F32 start; + F32 end; + S32 loop; + } path; + bool makePath; + + /// given a position on the thread, choose correct keyframes + /// slight difference between one-shot and cyclic sequences -- see comments below for details + void selectKeyframes(F32 pos, const TSSequence * seq, S32 * k1, S32 * k2, F32 * kpos); + + void getGround(F32 p, MatrixF * pMat); + + /// @name Triggers + /// Triggers are used to do something once a certain animation point has been reached. + /// + /// For example, when the player's foot animation hits the ground, a foot puff and + /// foot print are triggered from the thread. + /// + /// These are called by advancePos() + /// @{ + void animateTriggers(); + void activateTriggers(F32 a, F32 b); + /// @} + + TSThread(TSShapeInstance*); + TSThread() {} + + void setSequence(S32 seq, F32 pos); + void transitionToSequence(S32 seq, F32 pos, F32 duration, bool continuePlay); + + void advanceTime(F32 delta); + void advancePos(F32 delta); + + F32 getTime(); + F32 getPos(); + + void setTime(F32); + void setPos(F32); + + bool isInTransition(); + F32 getTimeScale(); + void setTimeScale(F32); + + F32 getDuration(); + F32 getScaledDuration(); + + S32 getKeyframeCount(); + S32 getKeyframeNumber(); + void setKeyframeNumber(S32 kf); + +public: + + TSShapeInstance * getShapeInstance() { return mShapeInstance; } + bool hasSequence() const { return sequence >= 0; } + U32 getSeqIndex() const { return sequence; } + const TSSequence* getSequence() const { return &(mShapeInstance->mShape->sequences[sequence]); } + const String& getSequenceName() const { return mShapeInstance->mShape->getSequenceName(sequence); } + S32 operator<(const TSThread &) const; +}; + +typedef TSShapeInstance::ObjectInstance TSObjectInstance; + +#endif // _TSSHAPEINSTANCE_H_ diff --git a/Engine/source/ts/tsShapeOldRead.cpp b/Engine/source/ts/tsShapeOldRead.cpp new file mode 100644 index 000000000..e76198de3 --- /dev/null +++ b/Engine/source/ts/tsShapeOldRead.cpp @@ -0,0 +1,886 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/strings/stringFunctions.h" +#include "core/util/endian.h" + +#include "ts/tsShapeInstance.h" + +//------------------------------------------------- +// put old skins into object list +//------------------------------------------------- + +void TSShape::fixupOldSkins(S32 numMeshes, S32 numSkins, S32 numDetails, S32 * detailFirstSkin, S32 * detailNumSkins) +{ +#if !defined(TORQUE_MAX_LIB) + // this method not necessary in exporter, and a couple lines won't compile for exporter + if (!objects.address() || !meshes.address() || !numSkins) + // not ready for this yet, will catch it on the next pass + return; + S32 numObjects = objects.size(); + TSObject * newObjects = objects.address() + objects.size(); + TSSkinMesh ** skins = (TSSkinMesh**)&meshes[numMeshes]; + Vector skinsCopy; + // Note: newObjects has as much free space as we need, so we just need to keep track of the + // number of objects we use and then update objects.size + S32 numSkinObjects = 0; + S32 skinsUsed = 0; + S32 emptySkins = 0; + S32 i; + for (i=0; iwrite(a.size() - m); \ + for (S32 i=m;iwrite(a.size() - m); \ + for (S32 i=m;iwrite(a[i]); \ +} + +// same as above with m=0 +#define writeVectorStruct(a) writeVectorStructMinus(a,0) +#define writeVectorSimple(a) writeVectorSimpleMinus(a,0) + +// read a vector of structs -- over-writing any existing data +#define readVectorStruct(a) \ +{ \ + S32 sz; \ + s->read(&sz); \ + a.setSize(sz); \ + for (S32 i=0;iread(&sz); \ + a.setSize(sz); \ + for (S32 i=0;iread(&a[i]); \ +} + +// read a vector of structs -- append to any existing data +#define appendVectorStruct(a) \ +{ \ + S32 sz; \ + S32 oldSz = a.size(); \ + s->read(&sz); \ + a.setSize(oldSz + sz); \ + for (S32 i=0;iread(&sz); \ + a.setSize(oldSz + sz); \ + for (S32 i=0;iread(&a[i + oldSz]); \ +} + +//------------------------------------------------- +// export all sequences +//------------------------------------------------- +void TSShape::exportSequences(Stream * s) +{ + // write version + s->write(smVersion); + + S32 i,sz; + + // write node names + // -- this is how we will map imported sequence nodes to shape nodes + sz = nodes.size(); + s->write(sz); + for (i=0;iwrite(0); + + // on import, we will need to adjust keyframe data based on number of + // nodes/objects in this shape...number of nodes can be inferred from + // above, but number of objects cannot be. Write that quantity here: + s->write(objects.size()); + + // write node states -- skip default node states + s->write(nodeRotations.size()); + for (i=0;iwrite(nodeRotations[i].x); + s->write(nodeRotations[i].y); + s->write(nodeRotations[i].z); + s->write(nodeRotations[i].w); + } + s->write(nodeTranslations.size()); + for (i=0;iwrite(nodeTranslations[i].x); + s->write(nodeTranslations[i].y); + s->write(nodeTranslations[i].z); + } + s->write(nodeUniformScales.size()); + for (i=0;iwrite(nodeUniformScales[i]); + s->write(nodeAlignedScales.size()); + for (i=0;iwrite(nodeAlignedScales[i].x); + s->write(nodeAlignedScales[i].y); + s->write(nodeAlignedScales[i].z); + } + s->write(nodeArbitraryScaleRots.size()); + for (i=0;iwrite(nodeArbitraryScaleRots[i].x); + s->write(nodeArbitraryScaleRots[i].y); + s->write(nodeArbitraryScaleRots[i].z); + s->write(nodeArbitraryScaleRots[i].w); + } + for (i=0;iwrite(nodeArbitraryScaleFactors[i].x); + s->write(nodeArbitraryScaleFactors[i].y); + s->write(nodeArbitraryScaleFactors[i].z); + } + s->write(groundTranslations.size()); + for (i=0;iwrite(groundTranslations[i].x); + s->write(groundTranslations[i].y); + s->write(groundTranslations[i].z); + } + for (i=0;iwrite(groundRotations[i].x); + s->write(groundRotations[i].y); + s->write(groundRotations[i].z); + s->write(groundRotations[i].w); + } + + // write object states -- legacy..no object states + s->write((S32)0); + + // write sequences + s->write(sequences.size()); + for (i=0;i don't write name index + } + + // write out all the triggers... + s->write(triggers.size()); + for (i=0; iwrite(triggers[i].state); + s->write(triggers[i].pos); + } +} + +//------------------------------------------------- +// export a single sequence +//------------------------------------------------- +void TSShape::exportSequence(Stream * s, const TSShape::Sequence& seq, bool saveOldFormat) +{ + S32 currentVersion = smVersion; + if ( saveOldFormat ) + smVersion = 24; + + // write version + s->write(smVersion); + + // write node names + s->write( nodes.size() ); + for ( S32 i = 0; i < nodes.size(); i++ ) + writeName( s, nodes[i].nameIndex ); + + // legacy write -- write zero objects, don't pretend to support object export anymore + s->write( (S32)0 ); + + // on import, we will need to adjust keyframe data based on number of + // nodes/objects in this shape...number of nodes can be inferred from + // above, but number of objects cannot be. Write that quantity here: + s->write( objects.size() ); + + // write node states -- skip default node states + S32 count = seq.rotationMatters.count() * seq.numKeyframes; + s->write( count ); + for ( S32 i = seq.baseRotation; i < seq.baseRotation + count; i++ ) + { + s->write( nodeRotations[i].x ); + s->write( nodeRotations[i].y ); + s->write( nodeRotations[i].z ); + s->write( nodeRotations[i].w ); + } + + count = seq.translationMatters.count() * seq.numKeyframes; + s->write( count ); + for ( S32 i = seq.baseTranslation; i < seq.baseTranslation + count; i++ ) + { + s->write( nodeTranslations[i].x ); + s->write( nodeTranslations[i].y ); + s->write( nodeTranslations[i].z ); + } + + count = seq.scaleMatters.count() * seq.numKeyframes; + if ( seq.animatesUniformScale() ) + { + s->write( count ); + for ( S32 i = seq.baseScale; i < seq.baseScale + count; i++ ) + s->write( nodeUniformScales[i] ); + } + else + s->write( (S32)0 ); + + if ( seq.animatesAlignedScale() ) + { + s->write( count ); + for ( S32 i = seq.baseScale; i < seq.baseScale + count; i++ ) + { + s->write( nodeAlignedScales[i].x ); + s->write( nodeAlignedScales[i].y ); + s->write( nodeAlignedScales[i].z ); + } + } + else + s->write( (S32)0 ); + + if ( seq.animatesArbitraryScale() ) + { + s->write( count ); + for ( S32 i = seq.baseScale; i < seq.baseScale + count; i++ ) + { + s->write( nodeArbitraryScaleRots[i].x ); + s->write( nodeArbitraryScaleRots[i].y ); + s->write( nodeArbitraryScaleRots[i].z ); + s->write( nodeArbitraryScaleRots[i].w ); + } + for ( S32 i = seq.baseScale; i < seq.baseScale + count; i++ ) + { + s->write( nodeArbitraryScaleFactors[i].x ); + s->write( nodeArbitraryScaleFactors[i].y ); + s->write( nodeArbitraryScaleFactors[i].z ); + } + } + else + s->write( (S32)0 ); + + s->write( seq.numGroundFrames ); + for ( S32 i = seq.firstGroundFrame; i < seq.firstGroundFrame + seq.numGroundFrames; i++ ) + { + s->write( groundTranslations[i].x ); + s->write( groundTranslations[i].y ); + s->write( groundTranslations[i].z ); + } + for ( S32 i = seq.firstGroundFrame; i < seq.firstGroundFrame + seq.numGroundFrames; i++ ) + { + s->write( groundRotations[i].x ); + s->write( groundRotations[i].y ); + s->write( groundRotations[i].z ); + s->write( groundRotations[i].w ); + } + + // write object states -- legacy..no object states + s->write( (S32)0 ); + + // write the sequence + s->write( (S32)1 ); + writeName( s, seq.nameIndex ); + { + // Write a copy of the sequence with all offsets set to 0 + TSShape::Sequence tmpSeq(seq); + tmpSeq.baseDecalState = 0; + tmpSeq.baseObjectState = 0; + tmpSeq.baseTranslation = 0; + tmpSeq.baseRotation = 0; + tmpSeq.baseScale = 0; + tmpSeq.firstGroundFrame = 0; + tmpSeq.firstTrigger = 0; + + tmpSeq.write( s, false ); + } + + // write the sequence triggers + s->write( seq.numTriggers ); + for ( S32 i = seq.firstTrigger; i < seq.firstTrigger + seq.numTriggers; i++ ) + { + s->write( triggers[i].state ); + s->write( triggers[i].pos ); + } + + smVersion = currentVersion; +} + +//------------------------------------------------- +// import sequences into existing shape +//------------------------------------------------- +bool TSShape::importSequences(Stream * s, const String& sequencePath) +{ + // write version + s->read(&smReadVersion); + if (smReadVersion>smVersion) + { + // error -- don't support future version yet :> + Con::errorf(ConsoleLogEntry::General, + "Sequence import failed: shape exporter newer than running executable."); + return false; + } + if (smReadVersion<19) + { + // error -- don't support future version yet :> + Con::errorf(ConsoleLogEntry::General, + "Sequence import failed: deprecated version (%i).",smReadVersion); + return false; + } + + Vector nodeMap; // node index of each node from imported sequences + Vector objectMap; // object index of objects from imported sequences + VECTOR_SET_ASSOCIATION(nodeMap); + VECTOR_SET_ASSOCIATION(objectMap); + + S32 i,sz; + + // read node names + // -- this is how we will map imported sequence nodes to our nodes + s->read(&sz); + nodeMap.setSize(sz); + + for (i=0;i remove the added node name + if (names.size() != startSize) + { + names.decrement(); + + if (names.size() != startSize) + Con::errorf(ConsoleLogEntry::General, "TSShape::importSequence: failed to remove unused node correctly for dsq %s.", names[nameIndex].c_str(), sequencePath.c_str()); + } + } + } + + // read the following size, but won't do anything with it...legacy: was going to support + // import of sequences that animate objects...we don't... + s->read(&sz); + + // before reading keyframes, take note of a couple numbers + S32 oldShapeNumObjects; + s->read(&oldShapeNumObjects); + + // adjust all the new keyframes + S32 adjNodeRots = smReadVersion<22 ? nodeRotations.size() - nodeMap.size() : nodeRotations.size(); + S32 adjNodeTrans = smReadVersion<22 ? nodeTranslations.size() - nodeMap.size() : nodeTranslations.size(); + S32 adjGroundStates = smReadVersion<22 ? 0 : groundTranslations.size(); // groundTrans==groundRot + + // Read the node states into temporary vectors, then use the + // nodeMap to discard unused transforms and map others to our nodes + Vector seqRotations; + Vector seqTranslations; + Vector seqUniformScales; + Vector seqAlignedScales; + Vector seqArbitraryScaleRots; + Vector seqArbitraryScaleFactors; + + if (smReadVersion>21) + { + s->read(&sz); + seqRotations.setSize(sz); + for (i=0; i < sz; i++) + { + s->read(&seqRotations[i].x); + s->read(&seqRotations[i].y); + s->read(&seqRotations[i].z); + s->read(&seqRotations[i].w); + } + s->read(&sz); + seqTranslations.setSize(sz); + for (i=0; i read(&seqTranslations[i].x); + s->read(&seqTranslations[i].y); + s->read(&seqTranslations[i].z); + } + s->read(&sz); + seqUniformScales.setSize(sz); + for (i = 0; i < sz; i++) + s->read(&seqUniformScales[i]); + s->read(&sz); + seqAlignedScales.setSize(sz); + for (i = 0; i < sz; i++) + { + s->read(&seqAlignedScales[i].x); + s->read(&seqAlignedScales[i].y); + s->read(&seqAlignedScales[i].z); + } + s->read(&sz); + seqArbitraryScaleRots.setSize(sz); + for (i = 0; i read(&seqArbitraryScaleRots[i].x); + s->read(&seqArbitraryScaleRots[i].y); + s->read(&seqArbitraryScaleRots[i].z); + s->read(&seqArbitraryScaleRots[i].w); + } + seqArbitraryScaleFactors.setSize(sz); + for (i = 0; i < sz; i++) + { + s->read(&seqArbitraryScaleFactors[i].x); + s->read(&seqArbitraryScaleFactors[i].y); + s->read(&seqArbitraryScaleFactors[i].z); + } + + // ground transforms can be read directly into the shape (none will be + // discarded) + s->read(&sz); + S32 oldSz = groundTranslations.size(); + groundTranslations.setSize(sz+oldSz); + for (i=oldSz;iread(&groundTranslations[i].x); + s->read(&groundTranslations[i].y); + s->read(&groundTranslations[i].z); + } + groundRotations.setSize(sz+oldSz); + for (i=oldSz;iread(&groundRotations[i].x); + s->read(&groundRotations[i].y); + s->read(&groundRotations[i].z); + s->read(&groundRotations[i].w); + } + } + else + { + s->read(&sz); + seqRotations.setSize(sz); + seqTranslations.setSize(sz); + for (i = 0; i < sz; i++) + { + s->read(&seqRotations[i].x); + s->read(&seqRotations[i].y); + s->read(&seqRotations[i].z); + s->read(&seqRotations[i].w); + s->read(&seqTranslations[i].x); + s->read(&seqTranslations[i].y); + s->read(&seqTranslations[i].z); + } + } + + // add these object states to our own -- shouldn't be any...assume it + s->read(&sz); + + // read sequences + s->read(&sz); + S32 startSeqNum = sequences.size(); + for (i=0;i 21) + { + if (seq.animatesUniformScale()) + seq.baseScale = nodeUniformScales.size(); + else if (seq.animatesAlignedScale()) + seq.baseScale = nodeAlignedScales.size(); + else if (seq.animatesArbitraryScale()) + seq.baseScale = nodeArbitraryScaleFactors.size(); + } + + // remap the node matters arrays + S32 j; + TSIntegerSet newTransMembership; + TSIntegerSet newRotMembership; + TSIntegerSet newScaleMembership; + for (j = 0; j < (S32)nodeMap.size(); j++) + { + if (nodeMap[j] < 0) + continue; + + if (seq.translationMatters.test(j)) + newTransMembership.set(nodeMap[j]); + if (seq.rotationMatters.test(j)) + newRotMembership.set(nodeMap[j]); + if (seq.scaleMatters.test(j)) + newScaleMembership.set(nodeMap[j]); + } + + // resize node transform arrays + nodeTranslations.increment(newTransMembership.count() * seq.numKeyframes); + nodeRotations.increment(newRotMembership.count() * seq.numKeyframes); + if (seq.flags & TSShape::ArbitraryScale) + { + S32 scaleCount = newScaleMembership.count() * seq.numKeyframes; + nodeArbitraryScaleRots.increment(scaleCount); + nodeArbitraryScaleFactors.increment(scaleCount); + } + else if (seq.flags & TSShape::AlignedScale) + nodeAlignedScales.increment(newScaleMembership.count() * seq.numKeyframes); + else + nodeUniformScales.increment(newScaleMembership.count() * seq.numKeyframes); + + // remap node transforms from temporary arrays + for (S32 j = 0; j < nodeMap.size(); j++) + { + if (nodeMap[j] < 0) + continue; + + if (newTransMembership.test(nodeMap[j])) + { + S32 src = seq.numKeyframes * seq.translationMatters.count(j); + S32 dest = seq.baseTranslation + seq.numKeyframes * newTransMembership.count(nodeMap[j]); + dCopyArray(&nodeTranslations[dest], &seqTranslations[src], seq.numKeyframes); + } + if (newRotMembership.test(nodeMap[j])) + { + S32 src = seq.numKeyframes * seq.rotationMatters.count(j); + S32 dest = seq.baseRotation + seq.numKeyframes * newRotMembership.count(nodeMap[j]); + dCopyArray(&nodeRotations[dest], &seqRotations[src], seq.numKeyframes); + } + if (newScaleMembership.test(nodeMap[j])) + { + S32 src = seq.numKeyframes * seq.scaleMatters.count(j); + S32 dest = seq.baseScale + seq.numKeyframes * newScaleMembership.count(nodeMap[j]); + if (seq.flags & TSShape::ArbitraryScale) + { + dCopyArray(&nodeArbitraryScaleRots[dest], &seqArbitraryScaleRots[src], seq.numKeyframes); + dCopyArray(&nodeArbitraryScaleFactors[dest], &seqArbitraryScaleFactors[src], seq.numKeyframes); + } + else if (seq.flags & TSShape::AlignedScale) + dCopyArray(&nodeAlignedScales[dest], &seqAlignedScales[src], seq.numKeyframes); + else + dCopyArray(&nodeUniformScales[dest], &seqUniformScales[src], seq.numKeyframes); + } + } + + seq.translationMatters = newTransMembership; + seq.rotationMatters = newRotMembership; + seq.scaleMatters = newScaleMembership; + + // adjust trigger numbers...we'll read triggers after sequences... + seq.firstTrigger += triggers.size(); + + // finally, adjust ground transform's nodes states + seq.firstGroundFrame += adjGroundStates; + } + + if (smReadVersion<22) + { + for (i=startSeqNum; iread(&sz); + triggers.setSize(oldSz+sz); + for (S32 i=0; iread(&triggers[i+oldSz].state); + s->read(&triggers[i+oldSz].pos); + } + + if (smInitOnRead) + init(); + + return true; +} + +//------------------------------------------------- +// read/write sequence +//------------------------------------------------- +void TSShape::Sequence::read(Stream * s, bool readNameIndex) +{ + AssertISV(smReadVersion>=19,"Reading old sequence"); + + if (readNameIndex) + s->read(&nameIndex); + flags = 0; + if (TSShape::smReadVersion>21) + s->read(&flags); + else + flags=0; + + s->read(&numKeyframes); + s->read(&duration); + + if (TSShape::smReadVersion<22) + { + bool tmp = false; + s->read(&tmp); + if (tmp) + flags |= Blend; + s->read(&tmp); + if (tmp) + flags |= Cyclic; + s->read(&tmp); + if (tmp) + flags |= MakePath; + } + + s->read(&priority); + s->read(&firstGroundFrame); + s->read(&numGroundFrames); + if (TSShape::smReadVersion>21) + { + s->read(&baseRotation); + s->read(&baseTranslation); + s->read(&baseScale); + s->read(&baseObjectState); + s->read(&baseDecalState); + } + else + { + s->read(&baseRotation); + baseTranslation=baseRotation; + s->read(&baseObjectState); + s->read(&baseDecalState); + } + + s->read(&firstTrigger); + s->read(&numTriggers); + s->read(&toolBegin); + + // now the membership sets: + rotationMatters.read(s); + if (TSShape::smReadVersion<22) + translationMatters=rotationMatters; + else + { + translationMatters.read(s); + scaleMatters.read(s); + } + + TSIntegerSet dummy; + dummy.read(s); // DEPRECIATED: Decals + dummy.read(s); // DEPRECIATED: Ifl materials + + visMatters.read(s); + frameMatters.read(s); + matFrameMatters.read(s); + + dirtyFlags = 0; + if (rotationMatters.testAll() || translationMatters.testAll() || scaleMatters.testAll()) + dirtyFlags |= TSShapeInstance::TransformDirty; + if (visMatters.testAll()) + dirtyFlags |= TSShapeInstance::VisDirty; + if (frameMatters.testAll()) + dirtyFlags |= TSShapeInstance::FrameDirty; + if (matFrameMatters.testAll()) + dirtyFlags |= TSShapeInstance::MatFrameDirty; +} + +void TSShape::Sequence::write(Stream * s, bool writeNameIndex) const +{ + if (writeNameIndex) + s->write(nameIndex); + s->write(flags); + s->write(numKeyframes); + s->write(duration); + s->write(priority); + s->write(firstGroundFrame); + s->write(numGroundFrames); + s->write(baseRotation); + s->write(baseTranslation); + s->write(baseScale); + s->write(baseObjectState); + s->write(baseDecalState); + s->write(firstTrigger); + s->write(numTriggers); + s->write(toolBegin); + + // now the membership sets: + rotationMatters.write(s); + translationMatters.write(s); + scaleMatters.write(s); + + TSIntegerSet dummy; + dummy.write(s); // DEPRECIATED: Decals + dummy.write(s); // DEPRECIATED: Ifl materials + + visMatters.write(s); + frameMatters.write(s); + matFrameMatters.write(s); +} + +void TSShape::writeName(Stream * s, S32 nameIndex) +{ + const char * name = ""; + if (nameIndex>=0) + name = names[nameIndex]; + S32 sz = (S32)dStrlen(name); + s->write(sz); + if (sz) + s->write(sz*sizeof(char),name); +} + +S32 TSShape::readName(Stream * s, bool addName) +{ + static char buffer[256]; + S32 sz; + S32 nameIndex = -1; + s->read(&sz); + if (sz) + { + s->read(sz*sizeof(char),buffer); + buffer[sz] = '\0'; + nameIndex = findName(buffer); + + // Many modeling apps don't support spaces in names, so if the lookup + // failed, try the name again with spaces replaced by underscores + if (nameIndex < 0) + { + while (char *p = dStrchr(buffer, ' ')) + *p = '_'; + nameIndex = findName(buffer); + } + + if (nameIndex<0 && addName) + { + nameIndex = names.size(); + names.increment(); + names.last() = buffer; + } + } + + return nameIndex; +} diff --git a/Engine/source/ts/tsSortedMesh.cpp b/Engine/source/ts/tsSortedMesh.cpp new file mode 100644 index 000000000..e2c5b489c --- /dev/null +++ b/Engine/source/ts/tsSortedMesh.cpp @@ -0,0 +1,168 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "ts/tsSortedMesh.h" +#include "math/mMath.h" +#include "ts/tsShapeInstance.h" + +// Not worth the effort, much less the effort to comment, but if the draw types +// are consecutive use addition rather than a table to go from index to command value... +/* +#if ((GL_TRIANGLES+1==GL_TRIANGLE_STRIP) && (GL_TRIANGLE_STRIP+1==GL_TRIANGLE_FAN)) + #define getDrawType(a) (GL_TRIANGLES+(a)) +#else + U32 drawTypes[] = { GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN }; + #define getDrawType(a) (drawTypes[a]) +#endif +*/ + +// found in tsmesh +extern void forceFaceCamera(); +extern void forceFaceCameraZAxis(); + +//----------------------------------------------------- +// TSSortedMesh render methods +//----------------------------------------------------- + +void TSSortedMesh::render(S32 frame, S32 matFrame, TSMaterialList * materials) +{ +} + +//----------------------------------------------------- +// TSSortedMesh collision methods +//----------------------------------------------------- + +bool TSSortedMesh::buildPolyList( S32 frame, AbstractPolyList *polyList, U32 &surfaceKey, TSMaterialList *materials ) +{ + TORQUE_UNUSED(frame); + TORQUE_UNUSED(polyList); + TORQUE_UNUSED(surfaceKey); + TORQUE_UNUSED(materials); + + return false; +} + +bool TSSortedMesh::castRay( S32 frame, const Point3F &start, const Point3F &end, RayInfo *rayInfo, TSMaterialList *materials ) +{ + TORQUE_UNUSED(frame); + TORQUE_UNUSED(start); + TORQUE_UNUSED(end); + TORQUE_UNUSED(rayInfo); + TORQUE_UNUSED(materials); + + return false; +} + +bool TSSortedMesh::buildConvexHull() +{ + return false; +} + +S32 TSSortedMesh::getNumPolys() +{ + S32 count = 0; + S32 cIdx = !clusters.size() ? -1 : 0; + while (cIdx>=0) + { + Cluster & cluster = clusters[cIdx]; + for (S32 i=cluster.startPrimitive; i clusters; ///< All of the clusters of primitives to be drawn + Vector startCluster; ///< indexed by frame number + Vector firstVerts; ///< indexed by frame number + Vector numVerts; ///< indexed by frame number + Vector firstTVerts; ///< indexed by frame number or matFrame number, depending on which one animates (never both) + + /// sometimes, we want to write the depth value to the frame buffer even when object is translucent + bool alwaysWriteDepth; + + // render methods.. + void render(S32 frame, S32 matFrame, TSMaterialList *); + + bool buildPolyList( S32 frame, AbstractPolyList *polyList, U32 &surfaceKey, TSMaterialList *materials ); + bool castRay( S32 frame, const Point3F &start, const Point3F &end, RayInfo *rayInfo, TSMaterialList *materials ); + bool buildConvexHull(); ///< does nothing, skins don't use this + /// + /// @returns false ALWAYS + S32 getNumPolys(); + + void assemble(bool skip); + void disassemble(); + + TSSortedMesh() { + meshType = SortedMeshType; + } +}; + +#endif // _TS_SORTED_MESH + + diff --git a/Engine/source/ts/tsThread.cpp b/Engine/source/ts/tsThread.cpp new file mode 100644 index 000000000..7cb543c9e --- /dev/null +++ b/Engine/source/ts/tsThread.cpp @@ -0,0 +1,835 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "ts/tsShapeInstance.h" + + +//------------------------------------------------------------------------------------- +// This file contains the shape instance thread class (defined in tsShapeInstance.h) +// and the tsShapeInstance functions to interface with the thread class. +//------------------------------------------------------------------------------------- + +//------------------------------------------------------------------------------------- +// Thread class +//------------------------------------------------------------------------------------- + +// given a position on the thread, choose correct keyframes +// slight difference between one-shot and cyclic sequences -- see comments below for details +void TSThread::selectKeyframes(F32 pos, const TSSequence * seq, S32 * k1, S32 * k2, F32 * kpos) +{ + S32 numKF = seq->numKeyframes; + F32 kf; + + if (seq->isCyclic()) + { + // cyclic sequence: + // pos=0 and pos=1 are equivalent, so we don't have a keyframe at pos=1 + // last keyframe corresponds to pos=n/(n-1) up to (not including) pos=1 + // (where n == num keyframes) + + AssertFatal(pos>=0.0f && pos<1.0f,"TSThread::selectKeyframes"); + + kf = pos * (F32) (numKF); + + // set keyPos + if (kpos) + *kpos = kf - (S32) kf; + + // make sure compiler doing what we want... + AssertFatal(*kpos>=0.0f && *kpos<1.0f,"TSThread::selectKeyframes"); + + S32 kfIdx1 = (S32) kf; + + // following assert could happen if pos1<1 && pos1==1...paradoxically... + AssertFatal(kfIdx1<=seq->numKeyframes,"TSThread::selectKeyframes"); + + S32 kfIdx2 = (kfIdx1==seq->numKeyframes-1) ? 0 : kfIdx1+1; + + if (k1) + *k1 = kfIdx1; + if (k2) + *k2 = kfIdx2; + } + else + { + // one-shot sequence: + // pos=0 and pos=1 are now different, so we have a keyframe at pos=1 + // last keyframe corresponds to pos=1 + // rest of the keyframes are equally spaced (so 1/(n-1) pos units long) + // (where n == num keyframes) + + AssertFatal(pos>=0.0f && pos<=1.0f,"TSThread::selectKeyframes"); + + if (pos==1.0f) + { + if (kpos) + *kpos = 0.0f; + if (k1) + *k1 = seq->numKeyframes-1; + if (k2) + *k2 = seq->numKeyframes-1; + } + else + { + kf = pos * (F32) (numKF-1); + + // set keyPos + if (kpos) + *kpos = kf - (S32) kf; + + S32 kfIdx1 = (S32) kf; + + // following assert could happen if pos1<1 && pos1==1...paradoxically... + AssertFatal(kfIdx1numKeyframes,"TSThread::selectKeyFrames: invalid keyframe!"); + + S32 kfIdx2 = kfIdx1+1; + + if (k1) + *k1 = kfIdx1; + if (k2) + *k2 = kfIdx2; + } + } +} + +void TSThread::getGround(F32 t, MatrixF * pMat) +{ + static const QuatF unitRotation(0,0,0,1); + static const Point3F unitTranslation = Point3F::Zero; + + const QuatF * q1, * q2; + QuatF rot1,rot2; + const Point3F * p1, * p2; + + // if N = sequence->numGroundFrames, then there are N+1 positions we + // interpolate betweeen: 0/N, 1/N ... N/N + // we need to convert the p passed to us into 2 ground keyframes: + + // the 0.99999f is in case 'p' is exactly 1.0f, which is legal but 'kf' + // needs to be strictly less than 'sequence->numGroundFrames' + F32 kf = 0.999999f * t * (F32) getSequence()->numGroundFrames; + + // get frame number and interp param (kpos) + S32 frame = (S32)kf; + F32 kpos = kf - (F32)frame; + + // now point pT1 and pT2 at transforms for keyframes 'frame' and 'frame+1' + + // following a little strange: first ground keyframe (0/N in comment above) is + // assumed to be ident. and not found in the list. + if (frame) + { + p1 = &mShapeInstance->mShape->groundTranslations[getSequence()->firstGroundFrame + frame - 1]; + q1 = &mShapeInstance->mShape->groundRotations[getSequence()->firstGroundFrame + frame - 1].getQuatF(&rot1); + } + else + { + p1 = &unitTranslation; + q1 = &unitRotation; + } + + // similar to above, ground keyframe number 'frame+1' is actually offset by 'frame' + p2 = &mShapeInstance->mShape->groundTranslations[getSequence()->firstGroundFrame + frame]; + q2 = &mShapeInstance->mShape->groundRotations[getSequence()->firstGroundFrame + frame].getQuatF(&rot2); + + QuatF q; + Point3F p; + TSTransform::interpolate(*q1,*q2,kpos,&q); + TSTransform::interpolate(*p1,*p2,kpos,&p); + TSTransform::setMatrix(q,p,pMat); +} + +void TSThread::setSequence(S32 seq, F32 toPos) +{ + const TSShape * shape = mShapeInstance->mShape; + + AssertFatal(shape && shape->sequences.size()>seq && toPos>=0.0f && toPos<=1.0f, + "TSThread::setSequence: invalid shape handle, sequence number, or position."); + + mShapeInstance->clearTransition(this); + + sequence = seq; + priority = getSequence()->priority; + pos = toPos; + makePath = getSequence()->makePath(); + path.start = path.end = 0; + path.loop = 0; + + // 1.0f doesn't exist on cyclic sequences + if (pos>0.9999f && getSequence()->isCyclic()) + pos = 0.9999f; + + // select keyframes + selectKeyframes(pos,getSequence(),&keyNum1,&keyNum2,&keyPos); +} + +void TSThread::transitionToSequence(S32 seq, F32 toPos, F32 duration, bool continuePlay) +{ + AssertFatal(duration>=0.0f,"TSThread::transitionToSequence: negative duration not allowed"); + + // make sure these nodes are smoothly interpolated to new positions... + // basically, any node we controlled just prior to transition, or at any stage + // of the transition is interpolated. If we start to transtion from A to B, + // but before reaching B we transtion to C, we interpolate all nodes controlled + // by A, B, or C to their new position. + if (transitionData.inTransition) + { + transitionData.oldRotationNodes.overlap(getSequence()->rotationMatters); + transitionData.oldTranslationNodes.overlap(getSequence()->translationMatters); + transitionData.oldScaleNodes.overlap(getSequence()->scaleMatters); + } + else + { + transitionData.oldRotationNodes = getSequence()->rotationMatters; + transitionData.oldTranslationNodes = getSequence()->translationMatters; + transitionData.oldScaleNodes = getSequence()->scaleMatters; + } + + // set time characteristics of transition + transitionData.oldSequence = sequence; + transitionData.oldPos = pos; + transitionData.duration = duration; + transitionData.pos = 0.0f; + transitionData.direction = timeScale>0.0f ? 1.0f : -1.0f; + transitionData.targetScale = continuePlay ? 1.0f : 0.0f; + + // in transition... + transitionData.inTransition = true; + + // set target sequence data + sequence = seq; + priority = getSequence()->priority; + pos = toPos; + makePath = getSequence()->makePath(); + path.start = path.end = 0; + path.loop = 0; + + // 1.0f doesn't exist on cyclic sequences + if (pos>0.9999f && getSequence()->isCyclic()) + pos = 0.9999f; + + // select keyframes + selectKeyframes(pos,getSequence(),&keyNum1,&keyNum2,&keyPos); +} + +bool TSThread::isInTransition() +{ + return transitionData.inTransition; +} + +void TSThread::animateTriggers() +{ + if (!getSequence()->numTriggers) + return; + + switch (path.loop) + { + case -1 : + activateTriggers(path.start,0); + activateTriggers(1,path.end); + break; + case 0 : + activateTriggers(path.start,path.end); + break; + case 1 : + activateTriggers(path.start,1); + activateTriggers(0,path.end); + break; + default: + { + if (path.loop>0) + { + activateTriggers(path.end,1); + activateTriggers(0,path.end); + } + else + { + activateTriggers(path.end,0); + activateTriggers(1,path.end); + } + } + } +} + +void TSThread::activateTriggers(F32 a, F32 b) +{ + S32 i; + const TSShape * shape = mShapeInstance->mShape; + S32 firstTrigger = getSequence()->firstTrigger; + S32 numTriggers = getSequence()->numTriggers; + + // first find triggers at position a and b + // we assume there aren't many triggers, so + // search is linear + F32 lastPos = -1.0f; + S32 aIndex = numTriggers+firstTrigger; // initialized to handle case where pos past all triggers + S32 bIndex = numTriggers+firstTrigger; // initialized to handle case where pos past all triggers + for (i=firstTrigger; ilastPos && a<=shape->triggers[i].pos) + aIndex = i; + // is b between this trigger and previous one... + if (b>lastPos && b<=shape->triggers[i].pos) + bIndex = i; + lastPos = shape->triggers[i].pos; + } + + // activate triggers between aIndex and bIndex (depends on direction) + if (aIndex<=bIndex) + { + for (i=aIndex; itriggers[i].state; + bool on = (state & TSShape::Trigger::StateOn)!=0; + mShapeInstance->setTriggerStateBit(state & TSShape::Trigger::StateMask, on); + } + } + else + { + for (i=aIndex-1; i>=bIndex; i--) + { + U32 state = shape->triggers[i].state; + bool on = (state & TSShape::Trigger::StateOn)!=0; + if (state & TSShape::Trigger::InvertOnReverse) + on = !on; + mShapeInstance->setTriggerStateBit(state & TSShape::Trigger::StateMask, on); + } + } +} + +F32 TSThread::getPos() +{ + return transitionData.inTransition ? transitionData.pos : pos; +} + +F32 TSThread::getTime() +{ + return transitionData.inTransition ? transitionData.pos * transitionData.duration : pos * getSequence()->duration; +} + +F32 TSThread::getDuration() +{ + return transitionData.inTransition ? transitionData.duration : getSequence()->duration; +} + +F32 TSThread::getScaledDuration() +{ + return getDuration() / mFabs(timeScale); +} + +F32 TSThread::getTimeScale() +{ + return timeScale; +} + +void TSThread::setTimeScale(F32 ts) +{ + timeScale = ts; +} + +void TSThread::advancePos(F32 delta) +{ + if (mFabs(delta)>0.00001f) + { + // make dirty what this thread changes + U32 dirtyFlags = getSequence()->dirtyFlags | (transitionData.inTransition ? TSShapeInstance::TransformDirty : 0); + for (S32 i=0; igetShape()->subShapeFirstNode.size(); i++) + mShapeInstance->mDirtyFlags[i] |= dirtyFlags; + } + + if (transitionData.inTransition) + { + transitionData.pos += transitionData.direction * delta; + if (transitionData.pos<0 || transitionData.pos>=1.0f) + { + mShapeInstance->clearTransition(this); + if (transitionData.pos<0.0f) + // return to old sequence + mShapeInstance->setSequence(this,transitionData.oldSequence,transitionData.oldPos); + } + // re-adjust delta to be correct time-wise + delta *= transitionData.targetScale * transitionData.duration / getSequence()->duration; + } + + // even if we are in a transition, keep playing the sequence + + if (makePath) + { + path.start = pos; + pos += delta; + if (!getSequence()->isCyclic()) + { + pos = mClampF(pos , 0.0f, 1.0f); + path.loop = 0; + } + else + { + path.loop = (S32)pos; + if (pos < 0.0f) + path.loop--; + pos -= path.loop; + // following necessary because of floating point roundoff errors + if (pos < 0.0f) pos += 1.0f; + if (pos >= 1.0f) pos -= 1.0f; + } + path.end = pos; + + animateTriggers(); // do this automatically...no need for user to call it + + AssertFatal(pos>=0.0f && pos<=1.0f,"TSThread::advancePos (1)"); + AssertFatal(!getSequence()->isCyclic() || pos<1.0f,"TSThread::advancePos (2)"); + } + else + { + pos += delta; + if (!getSequence()->isCyclic()) + pos = mClampF(pos, 0.0f, 1.0f); + else + { + pos -= S32(pos); + // following necessary because of floating point roundoff errors + if (pos < 0.0f) pos += 1.0f; + if (pos >= 1.0f) pos -= 1.0f; + } + AssertFatal(pos>=0.0f && pos<=1.0f,"TSThread::advancePos (3)"); + AssertFatal(!getSequence()->isCyclic() || pos<1.0f,"TSThread::advancePos (4)"); + } + + // select keyframes + selectKeyframes(pos,getSequence(),&keyNum1,&keyNum2,&keyPos); +} + +void TSThread::advanceTime(F32 delta) +{ + advancePos(timeScale * delta / getDuration()); +} + +void TSThread::setPos(F32 pos) +{ + advancePos(pos-getPos()); +} + +void TSThread::setTime(F32 time) +{ + setPos(timeScale * time/getDuration()); +} + +S32 TSThread::getKeyframeCount() +{ + AssertFatal(!transitionData.inTransition,"TSThread::getKeyframeCount: not while in transition"); + + return getSequence()->numKeyframes + 1; +} + +S32 TSThread::getKeyframeNumber() +{ + AssertFatal(!transitionData.inTransition,"TSThread::getKeyframeNumber: not while in transition"); + + return keyNum1; +} + +void TSThread::setKeyframeNumber(S32 kf) +{ + AssertFatal(kf>=0 && kf<= getSequence()->numKeyframes, + "TSThread::setKeyframeNumber: invalid frame specified."); + AssertFatal(!transitionData.inTransition,"TSThread::setKeyframeNumber: not while in transition"); + + keyNum1 = keyNum2 = kf; + keyPos = 0; + pos = 0; +} + +TSThread::TSThread(TSShapeInstance * _shapeInst) +{ + timeScale = 1.0f; + mShapeInstance = _shapeInst; + transitionData.inTransition = false; + blendDisabled = false; + setSequence(0,0.0f); +} + +S32 TSThread::operator<(const TSThread & th2) const +{ + if (getSequence()->isBlend() == th2.getSequence()->isBlend()) + { + // both blend or neither blend, sort based on priority only -- higher priority first + S32 ret = 0; // do it this way to (hopefully) take advantage of 'conditional move' assembly instruction + if (priority > th2.priority) + ret = -1; + if (th2.priority > priority) + ret = 1; + return ret; + } + else + { + // one is blend, the other is not...sort based on blend -- non-blended first + AssertFatal(!getSequence()->isBlend() || !th2.getSequence()->isBlend(),"compareThreads: unequal 'trues'"); + + S32 ret = -1; // do it this way to (hopefully) take advantage of 'conditional move' assembly instruction + if (getSequence()->isBlend()) + ret = 1; + return ret; + } +} + + +//------------------------------------------------------------------------------------- +// TSShapeInstance Thread Interface -- more implemented in header file +//------------------------------------------------------------------------------------- + +TSThread * TSShapeInstance::addThread() +{ + if (mShape->sequences.empty()) + return NULL; + + mThreadList.increment(); + mThreadList.last() = new TSThread(this); + setDirty(AllDirtyMask); + return mThreadList.last(); +} + +TSThread * TSShapeInstance::getThread(S32 threadNumber) +{ + AssertFatal(threadNumber < mThreadList.size() && threadNumber>=0,"TSShapeInstance::getThread: threadNumber out of bounds."); + return mThreadList[threadNumber]; +} + +void TSShapeInstance::destroyThread(TSThread * thread) +{ + if (!thread) + return; + + clearTransition(thread); + + S32 i; + for (i=0; itransitionData.inTransition && mTransitionThreads.size()>1) || mTransitionThreads.size()>0) + { + // if we have transitions, make sure transforms are up to date... + sortThreads(); + animateNodeSubtrees(); + } + + thread->setSequence(seq,pos); + setDirty(AllDirtyMask); + mGroundThread = NULL; + + if (mScaleCurrentlyAnimated && !thread->getSequence()->animatesScale()) + checkScaleCurrentlyAnimated(); + else if (!mScaleCurrentlyAnimated && thread->getSequence()->animatesScale()) + mScaleCurrentlyAnimated=true; + + updateTransitions(); +} + +U32 TSShapeInstance::getSequence(TSThread * thread) +{ + //AssertFatal( thread->sequence >= 0, "TSShapeInstance::getSequence: range error A"); + //AssertFatal( thread->sequence < mShape->sequences.size(), "TSShapeInstance::getSequence: range error B"); + return (U32)thread->sequence; +} + +void TSShapeInstance::transitionToSequence(TSThread * thread, S32 seq, F32 pos, F32 duration, bool continuePlay) +{ + // make sure all transforms on all detail levels are accurate + sortThreads(); + animateNodeSubtrees(); + + thread->transitionToSequence(seq,pos,duration,continuePlay); + setDirty(AllDirtyMask); + mGroundThread = NULL; + + if (mScaleCurrentlyAnimated && !thread->getSequence()->animatesScale()) + checkScaleCurrentlyAnimated(); + else if (!mScaleCurrentlyAnimated && thread->getSequence()->animatesScale()) + mScaleCurrentlyAnimated=true; + + mTransitionRotationNodes.overlap(thread->transitionData.oldRotationNodes); + mTransitionRotationNodes.overlap(thread->getSequence()->rotationMatters); + + mTransitionTranslationNodes.overlap(thread->transitionData.oldTranslationNodes); + mTransitionTranslationNodes.overlap(thread->getSequence()->translationMatters); + + mTransitionScaleNodes.overlap(thread->transitionData.oldScaleNodes); + mTransitionScaleNodes.overlap(thread->getSequence()->scaleMatters); + + // if we aren't already in the list of transition threads, add us now + S32 i; + for (i=0; itransitionData.inTransition) + return; + + // if other transitions are still playing, + // make sure transforms are up to date + if (mTransitionThreads.size()>1) + animateNodeSubtrees(); + + // turn off transition... + thread->transitionData.inTransition = false; + + // remove us from transition list + S32 i; + if (mTransitionThreads.size() != 0) { + for (i=0; itransitionData.oldRotationNodes); + mTransitionRotationNodes.overlap(mTransitionThreads[i]->getSequence()->rotationMatters); + + mTransitionTranslationNodes.overlap(mTransitionThreads[i]->transitionData.oldTranslationNodes); + mTransitionTranslationNodes.overlap(mTransitionThreads[i]->getSequence()->translationMatters); + + mTransitionScaleNodes.overlap(mTransitionThreads[i]->transitionData.oldScaleNodes); + mTransitionScaleNodes.overlap(mTransitionThreads[i]->getSequence()->scaleMatters); + } + + setDirty(ThreadDirty); + + updateTransitions(); +} + +void TSShapeInstance::updateTransitions() +{ + if (mTransitionThreads.empty()) + return; + + TSIntegerSet transitionNodes; + updateTransitionNodeTransforms(transitionNodes); + + S32 i; + mNodeReferenceRotations.setSize(mShape->nodes.size()); + mNodeReferenceTranslations.setSize(mShape->nodes.size()); + for (i=0; inodes.size(); i++) + { + if (mTransitionRotationNodes.test(i)) + mNodeReferenceRotations[i].set(smNodeCurrentRotations[i]); + if (mTransitionTranslationNodes.test(i)) + mNodeReferenceTranslations[i] = smNodeCurrentTranslations[i]; + } + + if (animatesScale()) + { + // Make sure smNodeXXXScale arrays have been resized + TSIntegerSet dummySet; + handleDefaultScale(0, 0, dummySet); + + if (animatesUniformScale()) + { + mNodeReferenceUniformScales.setSize(mShape->nodes.size()); + for (i=0; inodes.size(); i++) + { + if (mTransitionScaleNodes.test(i)) + mNodeReferenceUniformScales[i] = smNodeCurrentUniformScales[i]; + } + } + else if (animatesAlignedScale()) + { + mNodeReferenceScaleFactors.setSize(mShape->nodes.size()); + for (i=0; inodes.size(); i++) + { + if (mTransitionScaleNodes.test(i)) + mNodeReferenceScaleFactors[i] = smNodeCurrentAlignedScales[i]; + } + } + else + { + mNodeReferenceScaleFactors.setSize(mShape->nodes.size()); + mNodeReferenceArbitraryScaleRots.setSize(mShape->nodes.size()); + for (i=0; inodes.size(); i++) + { + if (mTransitionScaleNodes.test(i)) + { + mNodeReferenceScaleFactors[i] = smNodeCurrentArbitraryScales[i].mScale; + mNodeReferenceArbitraryScaleRots[i].set(smNodeCurrentArbitraryScales[i].mRotate); + } + } + } + } + + // reset transition durations to account for new reference transforms + for (i=0; itransitionData.inTransition) + { + th->transitionData.duration *= 1.0f - th->transitionData.pos; + th->transitionData.pos = 0.0f; + } + } +} + +void TSShapeInstance::checkScaleCurrentlyAnimated() +{ + mScaleCurrentlyAnimated=true; + for (S32 i=0; igetSequence()->animatesScale()) + return; + mScaleCurrentlyAnimated=false; +} + +void TSShapeInstance::setBlendEnabled(TSThread * thread, bool blendOn) +{ + thread->blendDisabled = !blendOn; +} + +bool TSShapeInstance::getBlendEnabled(TSThread * thread) +{ + return !thread->blendDisabled; +} + +void TSShapeInstance::setPriority(TSThread * thread, F32 priority) +{ + thread->priority = priority; +} + +F32 TSShapeInstance::getPriority(TSThread * thread) +{ + return thread->priority; +} + +F32 TSShapeInstance::getTime(TSThread * thread) +{ + return thread->getTime(); +} + +F32 TSShapeInstance::getPos(TSThread * thread) +{ + return thread->getPos(); +} + +void TSShapeInstance::setTime(TSThread * thread, F32 time) +{ + thread->setTime(time); +} + +void TSShapeInstance::setPos(TSThread * thread, F32 pos) +{ + thread->setPos(pos); +} + +bool TSShapeInstance::isInTransition(TSThread * thread) +{ + return thread->isInTransition(); +} + +F32 TSShapeInstance::getTimeScale(TSThread * thread) +{ + return thread->getTimeScale(); +} + +void TSShapeInstance::setTimeScale(TSThread * thread, F32 timeScale) +{ + thread->setTimeScale(timeScale); +} + +F32 TSShapeInstance::getDuration(TSThread * thread) +{ + return thread->getDuration(); +} + +F32 TSShapeInstance::getScaledDuration(TSThread * thread) +{ + return thread->getScaledDuration(); +} + +S32 TSShapeInstance::getKeyframeCount(TSThread * thread) +{ + return thread->getKeyframeCount(); +} + +S32 TSShapeInstance::getKeyframeNumber(TSThread * thread) +{ + return thread->getKeyframeNumber(); +} + +void TSShapeInstance::setKeyframeNumber(TSThread * thread, S32 kf) +{ + thread->setKeyframeNumber(kf); +} + + +// advance time on a particular thread +void TSShapeInstance::advanceTime(F32 delta, TSThread * thread) +{ + thread->advanceTime(delta); +} + +// advance time on all threads +void TSShapeInstance::advanceTime(F32 delta) +{ + for (S32 i=0; iadvanceTime(delta); +} + +// advance pos on a particular thread +void TSShapeInstance::advancePos(F32 delta, TSThread * thread) +{ + thread->advancePos(delta); +} + +// advance pos on all threads +void TSShapeInstance::advancePos(F32 delta) +{ + for (S32 i=0; iadvancePos(delta); +} + diff --git a/Engine/source/ts/tsTransform.cpp b/Engine/source/ts/tsTransform.cpp new file mode 100644 index 000000000..c13368463 --- /dev/null +++ b/Engine/source/ts/tsTransform.cpp @@ -0,0 +1,156 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "ts/tsTransform.h" +#include "core/stream/stream.h" + +void Quat16::identity() +{ + x = y = z = 0; + w = MAX_VAL; +} + +QuatF Quat16::getQuatF() const +{ + return QuatF ( F32(x) / F32(MAX_VAL), + F32(y) / F32(MAX_VAL), + F32(z) / F32(MAX_VAL), + F32(w) / F32(MAX_VAL) ); +} + +QuatF & Quat16::getQuatF( QuatF * q ) const +{ + q->x = F32( x ) / F32(MAX_VAL); + q->y = F32( y ) / F32(MAX_VAL); + q->z = F32( z ) / F32(MAX_VAL); + q->w = F32( w ) / F32(MAX_VAL); + return *q; +} + +void Quat16::set( const QuatF & q ) +{ + x = (S16)(q.x * F32(MAX_VAL)); + y = (S16)(q.y * F32(MAX_VAL)); + z = (S16)(q.z * F32(MAX_VAL)); + w = (S16)(q.w * F32(MAX_VAL)); +} + +S32 Quat16::operator==( const Quat16 & q ) const +{ + return( x == q.x && y == q.y && z == q.z && w == q.w ); +} + +void Quat16::read(Stream * s) +{ + s->read(&x); + s->read(&y); + s->read(&z); + s->read(&w); +} + +void Quat16::write(Stream * s) +{ + s->write(x); + s->write(y); + s->write(z); + s->write(w); +} + +QuatF & TSTransform::interpolate( const QuatF & q1, const QuatF & q2, F32 interp, QuatF * q ) +{ + F32 Dot; + F32 Dist2; + F32 OneOverL; + F32 x1,y1,z1,w1; + F32 x2,y2,z2,w2; + + // + // This is a linear interpolation with a fast renormalization. + // + x1 = q1.x; + y1 = q1.y; + z1 = q1.z; + w1 = q1.w; + x2 = q2.x; + y2 = q2.y; + z2 = q2.z; + w2 = q2.w; + + // Determine if quats are further than 90 degrees + Dot = x1*x2 + y1*y2 + z1*z2 + w1*w2; + + // If dot is negative flip one of the quaterions + if( Dot < 0.0f ) + { + x1 = -x1; + y1 = -y1; + z1 = -z1; + w1 = -w1; + } + + // Compute interpolated values + x1 = x1 + interp*(x2 - x1); + y1 = y1 + interp*(y2 - y1); + z1 = z1 + interp*(z2 - z1); + w1 = w1 + interp*(w2 - w1); + + // Get squared distance of new quaternion + Dist2 = x1*x1 + y1*y1 + z1*z1 + w1*w1; + + // Use home-baked polynomial to compute 1/sqrt(Dist2) + // since we know the range is 0.707 >= Dist2 <= 1.0 + // we'll split in half. + + if( Dist2<0.857f ) + OneOverL = (((0.699368f)*Dist2) + -1.819985f)*Dist2 + 2.126369f; //0.0000792 + else + OneOverL = (((0.454012f)*Dist2) + -1.403517f)*Dist2 + 1.949542f; //0.0000373 + + // Renormalize + q->x = x1*OneOverL; + q->y = y1*OneOverL; + q->z = z1*OneOverL; + q->w = w1*OneOverL; + + return *q; +} + +void TSTransform::applyScale(F32 scale, MatrixF * mat) +{ + mat->scale(Point3F(scale,scale,scale)); +} + +void TSTransform::applyScale(const Point3F & scale, MatrixF * mat) +{ + mat->scale(scale); +} + +void TSTransform::applyScale(const TSScale & scale, MatrixF * mat) +{ + MatrixF mat2; + TSTransform::setMatrix(scale.mRotate,&mat2); + MatrixF mat3(mat2); + mat3.inverse(); + mat2.scale(scale.mScale); + mat2.mul(mat3); + mat->mul(mat2); +} diff --git a/Engine/source/ts/tsTransform.h b/Engine/source/ts/tsTransform.h new file mode 100644 index 000000000..7c7139fea --- /dev/null +++ b/Engine/source/ts/tsTransform.h @@ -0,0 +1,109 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TSTRANSFORM_H_ +#define _TSTRANSFORM_H_ + +#ifndef _MMATH_H_ +#include "math/mMath.h" +#endif + +class Stream; + +/// compressed quaternion class +struct Quat16 +{ + enum { MAX_VAL = 0x7fff }; + + S16 x, y, z, w; + + void read(Stream *); + void write(Stream *); + + void identity(); + QuatF getQuatF() const; + QuatF &getQuatF( QuatF * q ) const; + void set( const QuatF & q ); + S32 operator==( const Quat16 & q ) const; + S32 operator!=( const Quat16 & q ) const { return !(*this == q); } +}; + +/// class to handle general scaling case +/// +/// transform = rot * scale * inv(rot) +struct TSScale +{ + QuatF mRotate; + Point3F mScale; + + void identity() { mRotate.identity(); mScale.set( 1.0f,1.0f,1.0f ); } + S32 operator==( const TSScale & other ) const { return mRotate==other.mRotate && mScale==other.mScale; } +}; + +/// struct for encapsulating ts transform related static functions +struct TSTransform +{ + static Point3F & interpolate(const Point3F & p1, const Point3F & p2, F32 t, Point3F *); + static F32 interpolate(F32 p1, F32 p2, F32 t); + static QuatF & interpolate(const QuatF & q1, const QuatF & q2, F32 t, QuatF *); + static TSScale & interpolate(const TSScale & s1, const TSScale & s2, F32 t, TSScale *); + + static void setMatrix(const QuatF & q, MatrixF *); + static void setMatrix(const QuatF & q, const Point3F & p, MatrixF *); + + static void applyScale(F32 scale, MatrixF *); + static void applyScale(const Point3F & scale, MatrixF *); + static void applyScale(const TSScale & scale, MatrixF *); +}; + +inline Point3F & TSTransform::interpolate(const Point3F & p1, const Point3F & p2, F32 t, Point3F * p) +{ + p->x = p1.x + t * (p2.x-p1.x); + p->y = p1.y + t * (p2.y-p1.y); + p->z = p1.z + t * (p2.z-p1.z); + return *p; +} + +inline F32 TSTransform::interpolate(F32 p1, F32 p2, F32 t) +{ + return p1 + t*(p2-p1); +} + +inline TSScale & TSTransform::interpolate(const TSScale & s1, const TSScale & s2, F32 t, TSScale * s) +{ + TSTransform::interpolate(s1.mRotate,s2.mRotate,t,&s->mRotate); + TSTransform::interpolate(s1.mScale,s2.mScale,t,&s->mScale); + return *s; +} + +inline void TSTransform::setMatrix( const QuatF & q, const Point3F & p, MatrixF * pDest ) +{ + q.setMatrix(pDest); + pDest->setColumn(3,p); +} + +inline void TSTransform::setMatrix( const QuatF & q, MatrixF * pDest ) +{ + q.setMatrix(pDest); +} + +#endif diff --git a/Engine/source/unit/consoleTest.cpp b/Engine/source/unit/consoleTest.cpp new file mode 100644 index 000000000..2a159ca0c --- /dev/null +++ b/Engine/source/unit/consoleTest.cpp @@ -0,0 +1,39 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "core/strings/stringFunctions.h" + +#include "unit/test.h" +#include "console/console.h" + +using namespace UnitTesting; + +ConsoleFunction(unitTest_runTests, void, 1, 3, "([searchString[, bool skipInteractive]])" + "@brief Run unit tests, or just the tests that prefix match against the searchString.\n\n" + "@ingroup Console") +{ + const char *searchString = (argc > 1 ? argv[1] : ""); + bool skip = (argc > 2 ? dAtob(argv[2]) : false); + + TestRun tr; + tr.test(searchString, skip); +} \ No newline at end of file diff --git a/Engine/source/unit/memoryTester.cpp b/Engine/source/unit/memoryTester.cpp new file mode 100644 index 000000000..15b646bbb --- /dev/null +++ b/Engine/source/unit/memoryTester.cpp @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "unit/memoryTester.h" + +using namespace UnitTesting; + +void MemoryTester::mark() +{ + +} + +bool MemoryTester::check() +{ + //UnitTesting::UnitPrint("MemoryTester::check - unavailable w/o TORQUE_DEBUG_GUARD defined!"); + return true; +} diff --git a/Engine/source/unit/memoryTester.h b/Engine/source/unit/memoryTester.h new file mode 100644 index 000000000..bd9dfd71c --- /dev/null +++ b/Engine/source/unit/memoryTester.h @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _UNIT_MEMORYTESTER_H_ +#define _UNIT_MEMORYTESTER_H_ + +namespace UnitTesting +{ + class MemoryTester + { + public: + void mark(); + bool check(); + }; +} + + +#endif diff --git a/Engine/source/unit/test.cpp b/Engine/source/unit/test.cpp new file mode 100644 index 000000000..5e6438639 --- /dev/null +++ b/Engine/source/unit/test.cpp @@ -0,0 +1,283 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include +#include + +#include "core/strings/stringFunctions.h" + +#include "console/console.h" + +#include "unit/test.h" + + +namespace UnitTesting +{ + +//----------------------------------------------------------------------------- + +TestRegistry *TestRegistry::_list = 0; + + +//----------------------------------------------------------------------------- + +static const int MaxMarginCount = 32; +static const int MaxMarginValue = 128; +static int _Margin[MaxMarginCount] = { 3 }; +static int* _MarginPtr = _Margin; +static char _MarginString[MaxMarginValue]; + +static void _printMargin() +{ + if (*_MarginPtr) + ::fwrite(_MarginString,1,*_MarginPtr,stdout); +} + +void UnitMargin::Push(int margin) +{ + if (_MarginPtr < _Margin + MaxMarginCount) { + *++_MarginPtr = (margin < MaxMarginValue)? margin: MaxMarginValue; + memset(_MarginString,' ',*_MarginPtr); + } +} + +void UnitMargin::Pop() +{ + if (_MarginPtr > _Margin) { + _MarginPtr--; + memset(_MarginString,' ',*_MarginPtr); + } +} + +int UnitMargin::Current() +{ + return *_MarginPtr; +} + +void UnitPrint(const char* str) +{ + static bool lineStart = true; + Platform::outputDebugString(str); + + // Need to scan for '\n' in order to support margins + const char* ptr = str, *itr = ptr; + for (; *itr != 0; itr++) + if (*itr == '\n') + { + if (lineStart) + _printMargin(); + ::fwrite(ptr,1,itr - ptr + 1,stdout); + ptr = itr + 1; + lineStart = true; + } + + // End the line with a carriage return unless the + // line ends with a line continuation char. + if (ptr != itr) { + if (lineStart) + _printMargin(); + if (itr[-1] == '\\') { + ::fwrite(ptr,1,itr - ptr - 1,stdout); + lineStart = false; + } + else { + ::fwrite(ptr,1,itr - ptr,stdout); + ::fwrite("\n",1,1,stdout); + lineStart = true; + } + } + else { + ::fwrite("\n",1,1,stdout); + lineStart = true; + } + ::fflush(stdout); +} + + +//----------------------------------------------------------------------------- + +UnitTest::UnitTest() { + _testCount = 0; + _failureCount = 0; + _warningCount = 0; + _lastTestResult = true; +} + +void UnitTest::fail(const char* msg) +{ + Con::warnf("** Failed: %s",msg); + dFetchAndAdd( _failureCount, 1 ); +} + +void UnitTest::warn(const char* msg) +{ + Con::warnf("** Warning: %s",msg); + dFetchAndAdd( _warningCount, 1 ); +} + + +//----------------------------------------------------------------------------- + +TestRegistry::TestRegistry(const char* name, bool interactive, const char *className) +{ + // Check that no existing test uses the same class-name; this is guaranteed + // to lead to funkiness. + TestRegistry *walk = _list; + while(walk) + { + if(walk->_className) + { + AssertFatal(dStricmp(className, walk->_className), "TestRegistry::TestRegistry - got two unit tests with identical class names; they must have unique class names!"); + } + + walk = walk->_next; + } + + // Add us to the list. + _next = _list; + _list = this; + + // And fill in our fields. + _name = name; + _className = className; + _isInteractive = interactive; +} + +DynamicTestRegistration::DynamicTestRegistration( const char *name, UnitTest *test ) : TestRegistry( name, false, NULL ), mUnitTest( test ) +{ + +} + +DynamicTestRegistration::~DynamicTestRegistration() +{ + // Un-link ourselves from the test registry + TestRegistry *walk = _list; + + // Easy case! + if( walk == this ) + _list = _next; + else + { + // Search for us and remove + while( ( walk != 0 ) && ( walk->_next != 0 ) && ( walk->_next != this ) ) + walk = walk->_next; + + // When this loop is broken, walk will be the unit test in the list previous to this one + if( walk != 0 && walk->_next != 0 ) + walk->_next = walk->_next->_next; + } +} + + +//----------------------------------------------------------------------------- + +TestRun::TestRun() +{ + _subCount = 0; + _testCount = 0; + _failureCount = 0; + _warningCount = 0; +} + +void TestRun::printStats() +{ + Con::printf("-- %d test%s run (with %d sub-test%s)", + _testCount,(_testCount != 1)? "s": "", + _subCount,(_subCount != 1)? "s": ""); + + if (_testCount) + { + if (_failureCount) + Con::printf("** %d reported failure%s", + _failureCount,(_failureCount != 1)? "s": ""); + else if (_warningCount) + Con::printf("** %d reported warning%s", + _warningCount,(_warningCount != 1)? "s": ""); + else + Con::printf("-- No reported failures"); + } +} + +void TestRun::test(TestRegistry* reg) +{ + Con::printf("-- Testing: %s %s",reg->getName(), reg->isInteractive() ? "(interactive)" : "" ); + + UnitMargin::Push(_Margin[0]); + + // Run the test. + UnitTest* test = reg->newTest(); + test->run(); + + UnitMargin::Pop(); + + // Update stats. + _failureCount += test->getFailureCount(); + _subCount += test->getTestCount(); + _warningCount += test->getWarningCount(); + _testCount++; + + // Don't forget to delete the test! + delete test; +} + +// [tom, 2/5/2007] To provide a predictable environment for the tests, this +// now changes the current directory to the executable's directory before +// running the tests. The previous current directory is restored on exit. + +bool TestRun::test(const char* module, bool skipInteractive) +{ + StringTableEntry cwdSave = Platform::getCurrentDirectory(); + + int len = strlen(module); + + const char *skipMsg = skipInteractive ? "(skipping interactive tests)" : ""; + + // Indicate to the user what we're up to. + if (!len) + Con::printf("-- Running all unit tests %s", skipMsg); + else + Con::printf("-- Running %s tests %s",module, skipMsg); + + for (TestRegistry* itr = TestRegistry::getFirst(); itr; itr = itr->getNext()) + { + if (!len || !dStrnicmp(module,itr->getName(),len)) + { + // Skip the test if it's interactive and we're in skipinteractive mode. + if(skipInteractive && itr->isInteractive()) + continue; + + // Otherwise, run the test! + Platform::setCurrentDirectory(Platform::getMainDotCsDir()); + test(itr); + } + } + // Print out a nice report on how we did. + printStats(); + + Platform::setCurrentDirectory(cwdSave); + + // And indicate our failure situation in the return value. + return !_failureCount; +} + +} // Namespace + diff --git a/Engine/source/unit/test.h b/Engine/source/unit/test.h new file mode 100644 index 000000000..3f3f4dc3d --- /dev/null +++ b/Engine/source/unit/test.h @@ -0,0 +1,165 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef UNIT_UNITTESTING_H +#define UNIT_UNITTESTING_H + +#ifndef _PLATFORMINTRINSICS_H_ +# include "platform/platformIntrinsics.h" +#endif + + +namespace UnitTesting { + +//----------------------------------------------------------------------------- + +struct UnitMargin +{ +static void Push(int margin); +static void Pop(); +static int Current(); +}; + +void UnitPrint(const char* msg); + + +//----------------------------------------------------------------------------- + +class UnitTest { + int _testCount; + int _failureCount; + int _warningCount; + + bool _lastTestResult; + +public: + UnitTest(); + virtual ~UnitTest() {}; + + /// Test an assertion and note if it has failed. + bool test(bool a,const char* msg) { + dFetchAndAdd( _testCount, 1 ); + if (!a) + fail(msg); + _lastTestResult = a; + return a; + } + + /// Report a failture condition. + void fail(const char* msg); + + /// Report a warning + void warn(const char* msg); + + int getTestCount() const { return _testCount; } + int getFailureCount() const { return _failureCount; } + int getWarningCount() const { return _warningCount; } + bool lastTestPassed() const { return _lastTestResult; } + + /// Implement this with the specific test. + virtual void run() = 0; +}; + + +//----------------------------------------------------------------------------- + +class TestRegistry +{ + friend class DynamicTestRegistration; // Bless me, Father, for I have sinned, but this is damn cool + + static TestRegistry *_list; + TestRegistry *_next; + + const char *_name; + const char *_className; + bool _isInteractive; + +public: + TestRegistry(const char* name, bool interactive, const char *className); + virtual ~TestRegistry() {} + static TestRegistry* getFirst() { return _list; } + TestRegistry* getNext() { return _next; } + const char* getName() { return _name; } + const bool isInteractive() { return _isInteractive; } + virtual UnitTest* newTest() = 0; +}; + +template +class TestRegistration: public TestRegistry +{ +public: + virtual ~TestRegistration() + { + } + + TestRegistration(const char* name, bool interactive, const char *className) + : TestRegistry(name, interactive, className) + { + } + + virtual UnitTest* newTest() + { + return new T; + } +}; + +class DynamicTestRegistration : public TestRegistry +{ + + UnitTest *mUnitTest; + +public: + DynamicTestRegistration( const char *name, UnitTest *test ); + + virtual ~DynamicTestRegistration(); + + virtual UnitTest *newTest() { return mUnitTest; } +}; + + +//----------------------------------------------------------------------------- + +class TestRun { + int _testCount; + int _subCount; + int _failureCount; + int _warningCount; + void test(TestRegistry* reg); +public: + TestRun(); + void printStats(); + bool test(const char* module, bool skipInteractive = false); +}; + +#define CreateUnitTest(Class,Name) \ + class Class; \ + static UnitTesting::TestRegistration _UnitTester##Class (Name, false, #Class); \ + class Class : public UnitTesting::UnitTest + +#define CreateInteractiveTest(Class,Name) \ + class Class; \ + static UnitTesting::TestRegistration _UnitTester##Class (Name, true, #Class); \ + class Class : public UnitTesting::UnitTest + +} // Namespace + +#endif diff --git a/Engine/source/unit/tests/testComponents.cpp b/Engine/source/unit/tests/testComponents.cpp new file mode 100644 index 000000000..12297bb08 --- /dev/null +++ b/Engine/source/unit/tests/testComponents.cpp @@ -0,0 +1,143 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "unit/test.h" +#include "unit/memoryTester.h" +#include "component/simComponent.h" + +using namespace UnitTesting; + +////////////////////////////////////////////////////////////////////////// + +class CachedInterfaceExampleComponent : public SimComponent +{ + typedef SimComponent Parent; + + ComponentProperty mMyId; + static U32 smNumInstances; + ComponentProperty *mpU32; // CodeReview [patw, 2, 17, 2007] Make ref objects when this is in Jugg + +public: + DECLARE_CONOBJECT( CachedInterfaceExampleComponent ); + + CachedInterfaceExampleComponent() : mpU32( NULL ) + { + mMyId = ( ( 1 << 24 ) | smNumInstances++ ); + } + virtual ~CachedInterfaceExampleComponent() + { + smNumInstances--; + } + +public: + ////////////////////////////////////////////////////////////////////////// + + virtual void registerInterfaces( const SimComponent *owner ) + { + // Register a cached interface for this + registerCachedInterface( NULL, "aU32", this, &mMyId ); + } + + ////////////////////////////////////////////////////////////////////////// + + bool onComponentRegister( SimComponent *owner ) + { + // Call up to the parent first + if( !Parent::onComponentRegister( owner ) ) + return false; + + // We want to get an interface from another object in our containing component + // to simulate component interdependency. + ComponentInterfaceList list; + + // Enumerate the interfaces on the owner, only ignore interfaces that this object owns + if( !_getOwner()->getInterfaces( &list, NULL, "aU32", this, true ) ) + return false; + + // Sanity check before just assigning all willy-nilly + for( ComponentInterfaceListIterator i = list.begin(); i != list.end(); i++ ) + { + mpU32 = dynamic_cast *>( (*i) ); + + if( mpU32 != NULL ) + return true; + } + + return false; + } + + ////////////////////////////////////////////////////////////////////////// + + // CodeReview [patw, 2, 17, 2007] I'm going to make another lightweight interface + // for this functionality later + void unit_test( UnitTest *test ) + { + test->test( mpU32 != NULL, "Pointer to dependent interface is NULL" ); + test->test( *(*mpU32) & ( 1 << 24 ), "Pointer to interface data is bogus." ); + test->test( *(*mpU32) != *mMyId, "Two of me have the same ID, bad!" ); + } +}; + +IMPLEMENT_CONOBJECT( CachedInterfaceExampleComponent ); +U32 CachedInterfaceExampleComponent::smNumInstances = 0; + +ConsoleDocClass( CachedInterfaceExampleComponent, + "@brief Legacy from older component system.\n\n" + "Not intended for game development, for editors or internal use only.\n\n " + "@internal"); + +////////////////////////////////////////////////////////////////////////// + +CreateUnitTest(TestComponentInterfacing, "Components/Composition") +{ + void run() + { + MemoryTester m; + m.mark(); + + SimComponent *testComponent = new SimComponent(); + CachedInterfaceExampleComponent *componentA = new CachedInterfaceExampleComponent(); + CachedInterfaceExampleComponent *componentB = new CachedInterfaceExampleComponent(); + + // Register sub-components + test( componentA->registerObject(), "Failed to register componentA" ); + test( componentB->registerObject(), "Failed to register componentB" ); + + // Add the components + test( testComponent->addComponent( componentA ), "Failed to add component a to testComponent" ); + test( testComponent->addComponent( componentB ), "Failed to add component b to testComponent" ); + + test( componentA->getOwner() == testComponent, "testComponent did not properly set the mOwner field of componentA to NULL." ); + test( componentB->getOwner() == testComponent, "testComponent did not properly set the mOwner field of componentB to NULL." ); + + // Register the object with the simulation, kicking off the interface registration + test( testComponent->registerObject(), "Failed to register testComponent" ); + + // Interface tests + componentA->unit_test( this ); + componentB->unit_test( this ); + + testComponent->deleteObject(); + + test( m.check(), "Component composition test leaked memory." ); + } +}; \ No newline at end of file diff --git a/Engine/source/unit/tests/testDefaultConstruction.cpp b/Engine/source/unit/tests/testDefaultConstruction.cpp new file mode 100644 index 000000000..0eaac89a7 --- /dev/null +++ b/Engine/source/unit/tests/testDefaultConstruction.cpp @@ -0,0 +1,67 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "unit/test.h" +#include "console/simObject.h" + + +using namespace UnitTesting; + + +// Test to ensure that all console classes in the system are default-constructible. + +CreateUnitTest( TestDefaultConstruction, "Console/DefaultConstruction" ) +{ + void run() + { + for( AbstractClassRep* classRep = AbstractClassRep::getClassList(); + classRep != NULL; + classRep = classRep->getNextClass() ) + { + // Create object. + + ConsoleObject* object = classRep->create(); + test( object, avar( "AbstractClassRep::create failed for class '%s'", classRep->getClassName() ) ); + if( !object ) + continue; + + // Make sure it's a SimObject. + + SimObject* simObject = dynamic_cast< SimObject* >( object ); + if( !simObject ) + { + SAFE_DELETE( object ); + continue; + } + + // Register the object. + + bool result = simObject->registerObject(); + test( result, avar( "registerObject failed for object of class '%s'", classRep->getClassName() ) ); + + if( result ) + simObject->deleteObject(); + else + SAFE_DELETE( simObject ); + } + } +}; diff --git a/Engine/source/unit/tests/testMatrixMul.cpp b/Engine/source/unit/tests/testMatrixMul.cpp new file mode 100644 index 000000000..7eb2cbc4f --- /dev/null +++ b/Engine/source/unit/tests/testMatrixMul.cpp @@ -0,0 +1,116 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "unit/test.h" +#include "math/mMath.h" +#include "math/mRandom.h" + +extern void default_matF_x_matF_C(const F32 *a, const F32 *b, F32 *mresult); +extern void mInstallLibrary_ASM(); + +// If we're x86 and not Mac, then include these. There's probably a better way to do this. +#if defined(TORQUE_CPU_X86) && !defined(TORQUE_OS_MAC) +extern "C" void Athlon_MatrixF_x_MatrixF(const F32 *matA, const F32 *matB, F32 *result); +extern "C" void SSE_MatrixF_x_MatrixF(const F32 *matA, const F32 *matB, F32 *result); +#endif + +#if defined( __VEC__ ) +extern void vec_MatrixF_x_MatrixF(const F32 *matA, const F32 *matB, F32 *result); +#endif + +using namespace UnitTesting; + +CreateUnitTest( TestMatrixMul, "Math/Matrix/Multiply" ) +{ + // The purpose of this test is to verify that the matrix multiplication operation + // always agrees with the different implementations of itself within a reasonable + // epsilon. + void run() + { + F32 m1[16], m2[16], mrC[16]; + + // I am not positive that the best way to do this is to use random numbers + // but I think that using some kind of standard matrix may not always catch + // all problems. + for( int i = 0; i < 16; i++ ) + { + m1[i] = gRandGen.randF(); + m2[i] = gRandGen.randF(); + } + + // C will be the baseline + default_matF_x_matF_C( m1, m2, mrC ); + +#if defined(TORQUE_CPU_X86) && !defined(TORQUE_OS_MAC) + // Check the CPU info + U32 cpuProperties = Platform::SystemInfo.processor.properties; + bool same = true; + // Test 3D NOW! if it is available + F32 mrAMD[16]; + if( cpuProperties & CPU_PROP_3DNOW ) + { + Athlon_MatrixF_x_MatrixF( m1, m2, mrAMD ); + + for( int i = 0; i < 16; i++ ) + same &= mIsEqual( mrC[i], mrAMD[i] ); + + test( same, "Matrix multiplication verification failed. (C vs. 3D NOW!)" ); + } + else + warn( "Could not test 3D NOW! matrix multiplication because CPU does not support 3D NOW!." ); + + same = true; + + // Test SSE if it is available + F32 mrSSE[16]; + if( cpuProperties & CPU_PROP_SSE ) + { + SSE_MatrixF_x_MatrixF( m1, m2, mrSSE ); + + for( int i = 0; i < 16; i++ ) + same &= mIsEqual( mrC[i], mrSSE[i] ); + + test( same, "Matrix multiplication verification failed. (C vs. SSE)" ); + } + else + warn( "Could not test SSE matrix multiplication because CPU does not support SSE." ); + + same = true; +#endif + + // If Altivec exists, test it! +#if defined( __VEC__ ) + bool same = false; + F32 mrVEC[16]; + + vec_MatrixF_x_MatrixF( m1, m2, mrVEC ); + + for( int i = 0; i < 16; i++ ) + same &= isEqual( mrC[i], mrVEC[i] ); + + test( same, "Matrix multiplication verification failed. (C vs. Altivec)" ); +#else + warn( "Could not test Altivec matrix multiplication because CPU does not support Altivec." ); +#endif + } +}; diff --git a/Engine/source/unit/tests/testRuntimeClassRep.cpp b/Engine/source/unit/tests/testRuntimeClassRep.cpp new file mode 100644 index 000000000..5f4d9ff7d --- /dev/null +++ b/Engine/source/unit/tests/testRuntimeClassRep.cpp @@ -0,0 +1,102 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/simBase.h" +#include "console/consoleTypes.h" +#include "console/runtimeClassRep.h" +#include "unit/test.h" + +using namespace UnitTesting; + +//----------------------------------------------------------------------------- + +class RuntimeRegisteredSimObject : public SimObject +{ + typedef SimObject Parent; + +protected: + bool mFoo; + +public: + RuntimeRegisteredSimObject() : mFoo( false ) {}; + + DECLARE_RUNTIME_CONOBJECT(RuntimeRegisteredSimObject); + + static void initPersistFields(); +}; + +IMPLEMENT_RUNTIME_CONOBJECT(RuntimeRegisteredSimObject); + +void RuntimeRegisteredSimObject::initPersistFields() +{ + addField( "fooField", TypeBool, Offset( mFoo, RuntimeRegisteredSimObject ) ); +} + +//----------------------------------------------------------------------------- + +CreateUnitTest( RuntimeClassRepUnitTest, "Console/RuntimeClassRep" ) +{ + void run() + { + // First test to make sure that the test class is not registered (don't know how it could be, but that's programming for you) + test( !RuntimeRegisteredSimObject::dynRTClassRep.isRegistered(), "RuntimeRegisteredSimObject class was already registered with the console" ); + + // This should not be able to find the class, and return null (this may AssertWarn as well) + ConsoleObject *conobj = ConsoleObject::create( "RuntimeRegisteredSimObject" ); + test( conobj == NULL, "AbstractClassRep returned non-NULL value! That is really bad!" ); + + // Register with console system + RuntimeRegisteredSimObject::dynRTClassRep.consoleRegister(); + + // Make sure that the object knows it's registered + test( RuntimeRegisteredSimObject::dynRTClassRep.isRegistered(), "RuntimeRegisteredSimObject class failed console registration" ); + + // Now try again to create the instance + conobj = ConsoleObject::create( "RuntimeRegisteredSimObject" ); + test( conobj != NULL, "AbstractClassRep::create method failed!" ); + + // Cast the instance, and test it + RuntimeRegisteredSimObject *rtinst = dynamic_cast( conobj ); + test( rtinst != NULL, "Casting failed for some reason" ); + + // Register it + rtinst->registerObject( "_utRRTestObject" ); + test( rtinst->isProperlyAdded(), "registerObject failed on test object" ); + + // Now execute some script on it + Con::evaluate( "_utRRTestObject.fooField = true;" ); + + // Test to make sure field worked + test( dAtob( rtinst->getDataField( StringTable->insert( "fooField" ), NULL ) ), "Script test failed!" ); + + // BALETED + rtinst->deleteObject(); + + // Unregister the class + RuntimeRegisteredSimObject::dynRTClassRep.consoleUnRegister(); + + // And make sure we can't create another one + conobj = ConsoleObject::create( "RuntimeRegisteredSimObject" ); + test( conobj == NULL, "Unregistration of type failed" ); + } +}; \ No newline at end of file diff --git a/Engine/source/unit/tests/testSwizzle.cpp b/Engine/source/unit/tests/testSwizzle.cpp new file mode 100644 index 000000000..1c90394ec --- /dev/null +++ b/Engine/source/unit/tests/testSwizzle.cpp @@ -0,0 +1,126 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "unit/test.h" +#include "unit/memoryTester.h" +#include "core/util/swizzle.h" +#include "math/mRandom.h" + +using namespace UnitTesting; + +class TestStruct +{ +private: + static U32 smIdx; + U32 mIdx; + U32 mData; + +public: + TestStruct( const S32 data = -1 ) : mData( data ), mIdx( smIdx++ ) {}; + + dsize_t Idx() const { return mIdx; } + + U32 Data() const { return mData; } + void Data(U32 val) { mData = val; } +}; + +U32 TestStruct::smIdx = 0; + +CreateUnitTest(TestSwizzle, "Utils/Swizzle") +{ + void run() + { + //------------------------------------------------------------------------ + // Debugger-Observable Functionality Tests + //------------------------------------------------------------------------ + U8 simpleBuffer[] = { 0, 1, 2, 3 }; + U8 simpleTest[] = { 0, 1, 2, 3 }; + +#define RESET_SIMPLE() dMemcpy( simpleTest, simpleBuffer, sizeof( simpleBuffer ) ) + + //------------------------------------------------------------------------ + // No-switch test + dsize_t noSwzl4[] = { 0, 1, 2, 3 }; + Swizzle noSwizzle4( noSwzl4 ); + + noSwizzle4.InPlace( simpleTest, sizeof( simpleTest ) ); + test( dMemcmp( simpleTest, simpleBuffer, sizeof( simpleBuffer ) ) == 0, "No-switch test failed!" ); + RESET_SIMPLE(); + + //------------------------------------------------------------------------ + // No-brainer RGBA->BGRA test + dsize_t bgraSwzl[] = { 2, 1, 0, 3 }; + Swizzle bgraSwizzle( bgraSwzl ); + + U8 bgraTest[] = { 2, 1, 0, 3 }; + bgraSwizzle.InPlace( simpleTest, sizeof( simpleTest ) ); + test( dMemcmp( simpleTest, bgraTest, sizeof( bgraTest ) ) == 0, "U8 RGBA->BGRA test failed" ); + + //------------------------------------------------------------------------ + // Reverse test + bgraSwizzle.InPlace( simpleTest, sizeof( simpleTest ) ); + test( dMemcmp( simpleTest, simpleBuffer, sizeof( simpleBuffer ) ) == 0, "U8 RGBA->BGRA reverse test failed" ); + + RESET_SIMPLE(); + + //------------------------------------------------------------------------ + // Object support test + Swizzle bgraObjSwizzle( bgraSwzl ); + { + U32 objIdx[] = { 0, 1, 2, 3 }; + + FrameTemp objTest( sizeof( objIdx ) / sizeof( U32 ) ); + FrameTemp randData( sizeof( objIdx ) / sizeof( U32 ) ); + + bool same = true; + + for( U32 i = 0; i < sizeof( objIdx ) / sizeof( U32 ); i++ ) + { + // Make random data and assign it + randData[i] = gRandGen.randI(); + objTest[i].Data( randData[i] ); + + // Continue object sanity check + same &= ( objTest[i].Idx() == objIdx[i] ); + } + + test( same, "Test object failed to be competent" ); + + bgraObjSwizzle.InPlace( ~objTest, sizeof( TestStruct ) * ( sizeof( objIdx ) / sizeof( U32 ) ) ); + same = true; + + for( U32 i = 0; i < sizeof( objIdx ) / sizeof( U32 ); i++ ) + same &= ( objTest[i].Idx() == bgraTest[i] ) && ( objTest[i].Data() == randData[ (U32)bgraTest[ i ] ] ); + + test( same, "Object RGBA->BGRA test failed." ); + + bgraObjSwizzle.InPlace( ~objTest, sizeof( TestStruct ) * ( sizeof( objIdx ) / sizeof( U32 ) ) ); + same = true; + + for( U32 i = 0; i < sizeof( objIdx ) / sizeof( U32 ); i++ ) + same &= ( objTest[i].Idx() == objIdx[i] ) && ( objTest[i].Data() == randData[i] ); + + test( same, "Object RGBA->BGRA reverse test failed." ); + } + } +}; \ No newline at end of file diff --git a/Engine/source/unit/tests/testThreadStatic.cpp b/Engine/source/unit/tests/testThreadStatic.cpp new file mode 100644 index 000000000..52cfa5bdc --- /dev/null +++ b/Engine/source/unit/tests/testThreadStatic.cpp @@ -0,0 +1,101 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "unit/test.h" +#include "core/threadStatic.h" +#include "unit/memoryTester.h" + +using namespace UnitTesting; + +//----------------------------------------------------------------------------- +// This unit test will blow up without thread static support +#ifdef TORQUE_ENABLE_THREAD_STATICS + +// Declare a test thread static +DITTS( U32, gUnitTestFoo, 42 ); +DITTS( F32, gUnitTestF32, 1.0 ); + +CreateUnitTest( TestThreadStatic, "Core/ThreadStatic" ) +{ + void run() + { + MemoryTester m; + m.mark(); + + // ThreadStatic list should be initialized right now, so lets see if it has + // any entries. + test( !_TorqueThreadStaticReg::getStaticList().empty(), "Self-registration has failed, or no statics declared" ); + + // Spawn a new copy. + TorqueThreadStaticListHandle testInstance = _TorqueThreadStaticReg::spawnThreadStaticsInstance(); + + // Test the copy + test( _TorqueThreadStaticReg::getStaticList( 0 ).size() == testInstance->size(), "Spawned static list has a different size from master copy." ); + + // Make sure the size test passed before this is attempted + if( lastTestPassed() ) + { + // Traverse the list and compare it to the initial value copy (index 0) + for( int i = 0; i < _TorqueThreadStaticReg::getStaticList().size(); i++ ) + { + _TorqueThreadStatic *master = _TorqueThreadStaticReg::getStaticList()[i]; + _TorqueThreadStatic *cpy = (*testInstance)[i]; + + // Make sure it is not the same memory + test( master != cpy, "Copy not spawned properly." ); + + // Make sure the sizes are the same + test( master->getMemInstSize() == cpy->getMemInstSize(), "Size mismatch between master and copy" ); + + // Make sure the initialization occurred properly + if( lastTestPassed() ) + test( dMemcmp( master->getMemInstPtr(), cpy->getMemInstPtr(), master->getMemInstSize() ) == 0, "Initial value for spawned list is not correct" ); + } + } + + // Test metrics if enabled +#ifdef TORQUE_ENABLE_THREAD_STATIC_METRICS + U32 fooHitCount = (*testInstance)[_gUnitTestFooTorqueThreadStatic::getListIndex()]->getHitCount(); +#endif + + // Set/get some values (If we test static metrics, this is hit 1) + ATTS_(gUnitTestFoo, 1) = 55; + + // Test to see that the master copy and the spawned copy differ + // (If we test metrics, this is hit 2, for the instance, and hit 1 for the master) + test( ATTS_(gUnitTestFoo, 0) != ATTS_(gUnitTestFoo, 1 ) , "Assignment for spawned instanced memory failed" ); + +#ifdef TORQUE_ENABLE_THREAD_STATIC_METRICS + U32 fooHitCount2 = (*testInstance)[_gUnitTestFooTorqueThreadStatic::getListIndex()]->getHitCount(); + test( fooHitCount2 == ( fooHitCount + 2 ), "Thread static metric hit count failed" ); +#endif + + // Destroy instances + _TorqueThreadStaticReg::destroyInstance( testInstance ); + + // Now test the cleanup + test( m.check(), "Memory leak in dynamic static allocation stuff." ); + } +}; + +#endif // TORQUE_ENABLE_THREAD_STATICS \ No newline at end of file diff --git a/Engine/source/unit/tests/testThreadStaticPerformance.cpp b/Engine/source/unit/tests/testThreadStaticPerformance.cpp new file mode 100644 index 000000000..62eff7dac --- /dev/null +++ b/Engine/source/unit/tests/testThreadStaticPerformance.cpp @@ -0,0 +1,82 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "unit/test.h" +#include "core/threadStatic.h" +#include "math/mRandom.h" +#include "platform/profiler.h" + +using namespace UnitTesting; + +//----------------------------------------------------------------------------- +// This unit test will blow up without thread static support +#if defined(TORQUE_ENABLE_THREAD_STATICS) && defined(TORQUE_ENABLE_PROFILER) + +// Declare a test thread static +DITTS( U32, gInstancedStaticFoo, 42 ); +static U32 gTrueStaticFoo = 42; + +CreateUnitTest( TestThreadStaticPerformance, "Core/ThreadStaticPerformance" ) +{ + void run() + { + // Bail if the profiler is turned on right now + if( !test( !gProfiler->isEnabled(), "Profiler is currently enabled, test cannot continue" ) ) + return; + + // Spawn an instance + TorqueThreadStaticListHandle testInstance = _TorqueThreadStaticReg::spawnThreadStaticsInstance(); + + static const dsize_t TEST_SIZE = 100000; + + // What we are going to do in this test is to test some U32 static + // performance. The test will be run TEST_SIZE times, and so first create + // an array of values to standardize the tests on. + U32 testValue[TEST_SIZE]; + + for( int i = 0; i < TEST_SIZE; i++ ) + testValue[i] = gRandGen.randI(); + + // Reset the profiler, tell it to dump to console when done + gProfiler->dumpToConsole(); + gProfiler->enable( true ); + + // Value array is initialized, so lets put the foo's through the paces + PROFILE_START(ThreadStaticPerf_TrueStaticAssign); + for( int i = 0; i < TEST_SIZE; i++ ) + gTrueStaticFoo = testValue[i]; + PROFILE_END(); + + PROFILE_START(ThreadStaticPerf_InstanceStaticAssign); + for( int i = 0; i < TEST_SIZE; i++ ) + ATTS_( gInstancedStaticFoo, 1 ) = testValue[i]; + PROFILE_END(); + + gProfiler->enable( false ); + + // Clean up instance + _TorqueThreadStaticReg::destroyInstance( testInstance ); + } +}; + +#endif // TORQUE_ENABLE_THREAD_STATICS \ No newline at end of file diff --git a/Engine/source/unit/tests/testVector.cpp b/Engine/source/unit/tests/testVector.cpp new file mode 100644 index 000000000..c7e38d250 --- /dev/null +++ b/Engine/source/unit/tests/testVector.cpp @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "unit/test.h" +#include "unit/memoryTester.h" +#include "core/util/tVector.h" + +using namespace UnitTesting; + +CreateUnitTest(TestVectorAllocate, "Types/Vector") +{ + void run() + { + MemoryTester m; + m.mark(); + + Vector *vector = new Vector; + for(S32 i=0; i<1000; i++) + vector->push_back(10000 + i); + + // Erase the first element, 500 times. + for(S32 i=0; i<500; i++) + vector->erase(U32(0)); + + vector->compact(); + + test(vector->size() == 500, "Vector was unexpectedly short!"); + + delete vector; + + test(m.check(), "Vector allocation test leaked memory!"); + } +}; \ No newline at end of file diff --git a/Engine/source/unit/unitTestComponentInterface.cpp b/Engine/source/unit/unitTestComponentInterface.cpp new file mode 100644 index 000000000..14d5653c7 --- /dev/null +++ b/Engine/source/unit/unitTestComponentInterface.cpp @@ -0,0 +1,24 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "unit/unitTestComponentInterface.h" + diff --git a/Engine/source/unit/unitTestComponentInterface.h b/Engine/source/unit/unitTestComponentInterface.h new file mode 100644 index 000000000..d829311b8 --- /dev/null +++ b/Engine/source/unit/unitTestComponentInterface.h @@ -0,0 +1,91 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _UNITTESTCOMPONENTINTERFACE_H_ +#define _UNITTESTCOMPONENTINTERFACE_H_ + +#include "unit/test.h" +#include "component/simComponent.h" +#include "component/componentInterface.h" + +// This is commented out because I want to keep the explicit namespace referencing +// so that the multiple inheritances from UnitTest doesn't screw anyone up. It will +// also make for more readable code in the derived test-interfaces. -patw +//using namespace UnitTesting; + +/// This is a class that will make it very easy for a component author to provide +/// unit testing functionality from within an instantiated component. +class UnitTestComponentInterface : public ComponentInterface, UnitTesting::UnitTest +{ + typedef ComponentInterface Parent; + +private: + StringTableEntry mName; + UnitTesting::DynamicTestRegistration *mTestReg; + + // Constructors/Destructors +public: + UnitTestComponentInterface( const char *name ) + { + mName = StringTable->insert( name ); + + mTestReg = new UnitTesting::DynamicTestRegistration( name, this ); + } + + virtual ~UnitTestComponentInterface() + { + delete mTestReg; + } + + // ComponentInterface overrides +public: + virtual bool isValid() const + { + return Parent::isValid() && ( mTestReg != NULL ); + } + + // UnitTest overrides +public: + /// This is the only function you need to overwrite to add a unit test interface + /// your component. + virtual void run() = 0; +}; + +// Macros +#ifndef TORQUE_DEBUG +# define DECLARE_UNITTEST_INTERFACE(x) +# define CACHE_UNITTEST_INTERFACE(x) +#else +//----------------------------------------------------------------------------- +# define DECLARE_UNITTEST_INTERFACE(x) \ + class x##_UnitTestInterface : public UnitTestComponentInterface \ + {\ + typedef UnitTestComponentInterface Parent; \ + public: \ + x##_UnitTestInterface() : UnitTestComponentInterface( #x ) {}; \ + virtual void run(); \ + } _##x##UnitTestInterface +//----------------------------------------------------------------------------- +# define CACHE_UNITTEST_INTERFACE(x) registerCachedInterface( "unittest", #x, this, &_##x##UnitTestInterface ) +#endif + +#endif \ No newline at end of file diff --git a/Engine/source/util/catmullRom.cpp b/Engine/source/util/catmullRom.cpp new file mode 100644 index 000000000..10452c18c --- /dev/null +++ b/Engine/source/util/catmullRom.cpp @@ -0,0 +1,174 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "util/catmullRom.h" + +#include "math/mMathFn.h" + + +const F32 CatmullRomBase::smX[] = +{ + 0.0000000000f, 0.5384693101f, -0.5384693101f, 0.9061798459f, -0.9061798459f +}; + +const F32 CatmullRomBase::smC[] = +{ + 0.5688888889f, 0.4786286705f, 0.4786286705f, 0.2369268850f, 0.2369268850f +}; + + +CatmullRomBase::CatmullRomBase() +: mTimes( NULL ), + mLengths( NULL ), + mTotalLength( 0.0f ), + mCount( 0 ) +{ +} + +void CatmullRomBase::_initialize( U32 count, const F32 *times ) +{ + //AssertFatal( times, "CatmullRomBase::_initialize() - Got null position!" ) + AssertFatal( count > 1, "CatmullRomBase::_initialize() - Must have more than 2 points!" ) + + // set up arrays + mTimes = new F32[count]; + mCount = count; + + // set up curve segment lengths + mLengths = new F32[count-1]; + mTotalLength = 0.0f; + for ( U32 i = 0; i < count-1; ++i ) + { + mLengths[i] = segmentArcLength(i, 0.0f, 1.0f); + mTotalLength += mLengths[i]; + } + + // copy the times if we have them. + F32 l = 0.0f; + for ( U32 i = 0; i < count; ++i ) + { + if ( times ) + mTimes[i] = times[i]; + else + { + if ( mIsZero( mTotalLength ) ) + mTimes[i] = 0.0f; + else + mTimes[i] = l / mTotalLength; + if ( i < count-1 ) + l += mLengths[i]; + } + } +} + +void CatmullRomBase::clear() +{ + delete [] mTimes; + mTimes = NULL; + + delete [] mLengths; + mLengths = NULL; + + mTotalLength = 0.0f; + mCount = 0; +} + +F32 CatmullRomBase::arcLength( F32 t1, F32 t2 ) +{ + if ( t2 <= t1 ) + return 0.0f; + + if ( t1 < mTimes[0] ) + t1 = mTimes[0]; + + if ( t2 > mTimes[mCount-1] ) + t2 = mTimes[mCount-1]; + + // find segment and parameter + U32 seg1; + for ( seg1 = 0; seg1 < mCount-1; ++seg1 ) + { + if ( t1 <= mTimes[seg1+1] ) + { + break; + } + } + F32 u1 = (t1 - mTimes[seg1])/(mTimes[seg1+1] - mTimes[seg1]); + + // find segment and parameter + U32 seg2; + for ( seg2 = 0; seg2 < mCount-1; ++seg2 ) + { + if ( t2 <= mTimes[seg2+1] ) + { + break; + } + } + F32 u2 = (t2 - mTimes[seg2])/(mTimes[seg2+1] - mTimes[seg2]); + + F32 result; + // both parameters lie in one segment + if ( seg1 == seg2 ) + { + result = segmentArcLength( seg1, u1, u2 ); + } + // parameters cross segments + else + { + result = segmentArcLength( seg1, u1, 1.0f ); + for ( U32 i = seg1+1; i < seg2; ++i ) + result += mLengths[i]; + result += segmentArcLength( seg2, 0.0f, u2 ); + } + + return result; +} + +U32 CatmullRomBase::getPrevNode( F32 t ) +{ + AssertFatal( mCount >= 2, "CatmullRomBase::getPrevNode - Bad point count!" ); + + // handle boundary conditions + if ( t <= mTimes[0] ) + return 0; + else if ( t >= mTimes[mCount-1] ) + return mCount-1; + + // find segment and parameter + U32 i; // segment # + for ( i = 0; i < mCount-1; ++i ) + { + if ( t <= mTimes[i+1] ) + break; + } + + AssertFatal( i >= 0 && i < mCount, "CatmullRomBase::getPrevNode - Got bad output index!" ); + + return i; +} + +F32 CatmullRomBase::getTime( U32 idx ) +{ + AssertFatal( idx >= 0 && idx < mCount, "CatmullRomBase::getTime - Got bad index!" ); + return mTimes[idx]; +} \ No newline at end of file diff --git a/Engine/source/util/catmullRom.h b/Engine/source/util/catmullRom.h new file mode 100644 index 000000000..f4c9decb0 --- /dev/null +++ b/Engine/source/util/catmullRom.h @@ -0,0 +1,371 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _CATMULLROM_H_ +#define _CATMULLROM_H_ + + +/// The shared base class used by the catmull rom +/// interpolation template class. +/// @see CatmullRom +class CatmullRomBase +{ +protected: + + CatmullRomBase(); + virtual ~CatmullRomBase() {} + +public: + + /// Clean out all the data. + virtual void clear(); + + /// Find length of curve between parameters t1 and t2. + F32 arcLength( F32 t1, F32 t2 ); + + /// Get the total length of the curve. + inline F32 getLength() { return mTotalLength; } + + /// Get the closest previous control point to time t. + U32 getPrevNode( F32 t ); + + /// Returns the time at idx (rather than at a F32 time) + F32 getTime( U32 idx ); + + /// Find length of curve segment between parameters u1 and u2. + virtual F32 segmentArcLength( U32 i, F32 u1, F32 u2 ) = 0; + +protected: + + static const F32 smX[]; + static const F32 smC[]; + + void _initialize( U32 count, const F32 *times = NULL ); + + /// The time to arrive at each point. + F32 *mTimes; + + /// the length of each curve segment. + F32* mLengths; + + /// The total length of curve. + F32 mTotalLength; + + /// The number of points and times. + U32 mCount; +}; + + +/// The catmull-rom template class for performing interpolation +/// over an arbitraty type. +template +class CatmullRom : public CatmullRomBase +{ +public: + + CatmullRom(); + virtual ~CatmullRom(); + + /// Initialization. + void initialize( U32 count, const TYPE *positions, const F32 *times = NULL ); + + // evaluate position + TYPE evaluate( F32 t ); + + /// Evaluate derivative at parameter t. + TYPE velocity( F32 t ); + + // Evaluate second derivative at parameter t. + TYPE acceleration( F32 t ); + + // Returns the position at idx (rather than at a F32 time) + TYPE getPosition( U32 idx ); + + // CatmullRomBase + void clear(); + F32 segmentArcLength( U32 i, F32 u1, F32 u2 ); + +protected: + + /// The sample points. + TYPE* mPositions; + +private: + + /// The copy constructors are disabled. + CatmullRom( const CatmullRom &other ); + CatmullRom& operator=( const CatmullRom &other ); +}; + + +template +inline CatmullRom::CatmullRom() + : CatmullRomBase(), + mPositions( NULL ) +{ +} + +template +inline CatmullRom::~CatmullRom() +{ + clear(); +} + +template +inline void CatmullRom::clear() +{ + delete [] mPositions; + mPositions = NULL; + + CatmullRomBase::clear(); +} + +template +inline void CatmullRom::initialize( U32 count, const TYPE *positions, const F32 *times ) +{ + AssertFatal( positions, "CatmullRom::initialize - Got null position!" ) + AssertFatal( count > 1, "CatmullRom::initialize - Must have more than 2 points!" ) + + // Clean up any previous state. + clear(); + + // copy the points. + mPositions = new TYPE[count]; + for ( U32 i = 0; i < count; ++i ) + mPositions[i] = positions[i]; + + _initialize( count, times ); +} + +template +inline TYPE CatmullRom::evaluate( F32 t ) +{ + AssertFatal( mCount >= 2, "CatmullRom::evaluate - Not initialized!" ); + + // handle boundary conditions + if ( t <= mTimes[0] ) + return mPositions[0]; + else if ( t >= mTimes[mCount-1] ) + return mPositions[mCount-1]; + + // find segment and parameter + U32 i; // segment # + for ( i = 0; i < mCount-1; ++i ) + { + if ( t <= mTimes[i+1] ) + { + break; + } + } + + AssertFatal( i >= 0 && i < mCount, "CatmullRom::evaluate - Got bad index!" ); + + F32 t0 = mTimes[i]; + F32 t1 = mTimes[i+1]; + F32 u = (t - t0)/(t1 - t0); + + S32 idx0, idx1, idx2, idx3; + idx0 = i - 1; + idx1 = i; + idx2 = i + 1; + idx3 = i + 2; + + if ( idx0 < 0 ) + idx0 = 0; + if ( idx3 >= mCount ) + idx3 = mCount - 1; + + TYPE A = 3.0f*mPositions[idx1] + - mPositions[idx0] + - 3.0f*mPositions[idx2] + + mPositions[idx3]; + + TYPE B = 2.0f*mPositions[idx0] + - 5.0f*mPositions[idx1] + + 4.0f*mPositions[idx2] + - mPositions[idx3]; + + TYPE C = mPositions[idx2] - mPositions[idx0]; + + return mPositions[i] + (0.5f*u)*(C + u*(B + u*A)); +} + +template +inline TYPE CatmullRom::velocity( F32 t ) +{ + AssertFatal( mCount >= 2, "CatmullRom::velocity - Not initialized!" ); + + // handle boundary conditions + if ( t <= mTimes[0] ) + t = 0.0f; + else if ( t > mTimes[mCount-1] ) + t = mTimes[mCount-1]; + + // find segment and parameter + U32 i; + for ( i = 0; i < mCount-1; ++i ) + { + if ( t <= mTimes[i+1] ) + { + break; + } + } + F32 t0 = mTimes[i]; + F32 t1 = mTimes[i+1]; + F32 u = (t - t0)/(t1 - t0); + + S32 idx0, idx1, idx2, idx3; + idx0 = i - 1; + idx1 = i; + idx2 = i + 1; + idx3 = i + 2; + + if ( idx0 < 0 ) + idx0 = 0; + if ( idx3 >= mCount ) + idx3 = mCount - 1; + + TYPE A = 3.0f*mPositions[idx1] + - mPositions[idx0] + - 3.0f*mPositions[idx2] + + mPositions[idx3]; + + TYPE B = 2.0f*mPositions[idx0] + - 5.0f*mPositions[idx1] + + 4.0f*mPositions[idx2] + - mPositions[idx3]; + + TYPE C = mPositions[idx2] - mPositions[idx0]; + + return 0.5f*C + u*(B + 1.5f*u*A); +} + +template +inline TYPE CatmullRom::acceleration( F32 t ) +{ + AssertFatal( mCount >= 2, "CatmullRom::acceleration - Not initialized!" ); + + // handle boundary conditions + if ( t <= mTimes[0] ) + t = 0.0f; + else if ( t > mTimes[mCount-1] ) + t = mTimes[mCount-1]; + + // find segment and parameter + U32 i; + for ( i = 0; i < mCount-1; ++i ) + { + if ( t <= mTimes[i+1] ) + { + break; + } + } + F32 t0 = mTimes[i]; + F32 t1 = mTimes[i+1]; + F32 u = (t - t0)/(t1 - t0); + + S32 idx0, idx1, idx2, idx3; + idx0 = i - 1; + idx1 = i; + idx2 = i + 1; + idx3 = i + 2; + + if ( idx0 < 0 ) + idx0 = 0; + if ( idx3 >= mCount ) + idx3 = mCount - 1; + + TYPE A = 3.0f*mPositions[idx1] + - mPositions[idx0] + - 3.0f*mPositions[idx2] + + mPositions[idx3]; + + TYPE B = 2.0f*mPositions[idx0] + - 5.0f*mPositions[idx1] + + 4.0f*mPositions[idx2] + - mPositions[idx3]; + + TYPE C = mPositions[idx2] - mPositions[idx0]; + + return B + (3.0f*u)*A; +} + +template +inline TYPE CatmullRom::getPosition( U32 idx ) +{ + AssertFatal( idx >= 0 && idx < mCount-1, "CatmullRom<>::getPosition - Got bad index!" ); + return mPositions[idx]; +} + +template +inline F32 CatmullRom::segmentArcLength( U32 i, F32 u1, F32 u2 ) +{ + AssertFatal( i >= 0 && i < mCount-1, "CatmullRom<>::getPosition - Got bad index!" ); + + if ( u2 <= u1 ) + return 0.0f; + + if ( u1 < 0.0f ) + u1 = 0.0f; + + if ( u2 > 1.0f ) + u2 = 1.0f; + + S32 idx0, idx1, idx2, idx3; + idx0 = i - 1; + idx1 = i; + idx2 = i + 1; + idx3 = i + 2; + + if ( idx0 < 0 ) + idx0 = 0; + if ( idx3 >= mCount ) + idx3 = mCount - 1; + + TYPE A = 3.0f*mPositions[idx1] + - mPositions[idx0] + - 3.0f*mPositions[idx2] + + mPositions[idx3]; + TYPE B = 2.0f*mPositions[idx0] + - 5.0f*mPositions[idx1] + + 4.0f*mPositions[idx2] + - mPositions[idx3]; + TYPE C = mPositions[idx2] - mPositions[idx0]; + + F32 sum = 0.0f; + + for ( U32 j = 0; j < 5; ++j ) + { + F32 u = 0.5f*((u2 - u1)*smX[j] + u2 + u1); + TYPE derivative; + if ( i == 0 || i >= mCount-2) + derivative = 0.5f*B + u*A; + else + derivative = 0.5f*C + u*(B + 1.5f*u*A); + sum += smC[j]*derivative.len(); + } + sum *= 0.5f*(u2-u1); + + return sum; +} + +#endif // _CATMULLROM_H_ diff --git a/Engine/source/util/fpsTracker.cpp b/Engine/source/util/fpsTracker.cpp new file mode 100644 index 000000000..d30c812f5 --- /dev/null +++ b/Engine/source/util/fpsTracker.cpp @@ -0,0 +1,95 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "util/fpsTracker.h" +#include "console/console.h" + +FPSTracker gFPS; + +FPSTracker::FPSTracker() +{ + mUpdateInterval = 0.25f; + reset(); +} + +void FPSTracker::reset() +{ + fpsNext = (F32)Platform::getRealMilliseconds()/1000.0f + mUpdateInterval; + + fpsRealLast = 0.0f; + fpsReal = 0.0f; + fpsRealMin = 0.000001f; // Avoid division by zero. + fpsRealMax = 1.0f; + fpsVirtualLast = 0.0f; + fpsVirtual = 0.0f; + fpsFrames = 0; +} + +void FPSTracker::update() +{ + const float alpha = 0.07f; + F32 realSeconds = (F32)Platform::getRealMilliseconds()/1000.0f; + F32 virtualSeconds = (F32)Platform::getVirtualMilliseconds()/1000.0f; + + fpsFrames++; + if (fpsFrames > 1) + { + fpsReal = fpsReal*(1.0-alpha) + (realSeconds-fpsRealLast)*alpha; + fpsVirtual = fpsVirtual*(1.0-alpha) + (virtualSeconds-fpsVirtualLast)*alpha; + + if( fpsFrames > 10 ) // Wait a few frames before updating these. + { + // Update min/max. This is a bit counter-intuitive, as the comparisons are + // inversed because these are all one-over-x values. + + if( fpsReal > fpsRealMin ) + fpsRealMin = fpsReal; + if( fpsReal < fpsRealMax ) + fpsRealMax = fpsReal; + } + } + + fpsRealLast = realSeconds; + fpsVirtualLast = virtualSeconds; + + // update variables every few frames + F32 update = fpsRealLast - fpsNext; + if (update > 0.5f) + { + Con::setVariable( "fps::real", avar( "%4.1f", 1.0f / fpsReal ) ); + Con::setVariable( "fps::realMin", avar( "%4.1f", 1.0f / fpsRealMin ) ); + Con::setVariable( "fps::realMax", avar( "%4.1f", 1.0f / fpsRealMax ) ); + Con::setVariable( "fps::virtual", avar( "%4.1f", 1.0f / fpsVirtual ) ); + + if (update > mUpdateInterval) + fpsNext = fpsRealLast + mUpdateInterval; + else + fpsNext += mUpdateInterval; + } +} + +ConsoleFunction( resetFPSTracker, void, 1, 1, "()" + "@brief Reset FPS stats (fps::)\n\n" + "@ingroup Game") +{ + gFPS.reset(); +} diff --git a/Engine/source/util/fpsTracker.h b/Engine/source/util/fpsTracker.h new file mode 100644 index 000000000..e5973ae68 --- /dev/null +++ b/Engine/source/util/fpsTracker.h @@ -0,0 +1,51 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _UTIL_FPSTRACKER_H_ +#define _UTIL_FPSTRACKER_H_ + +#include "platform/platform.h" + +struct FPSTracker +{ + F32 fpsRealLast; + F32 fpsReal; + F32 fpsRealMin; + F32 fpsRealMax; + F32 fpsVirtualLast; + F32 fpsVirtual; + F32 fpsFrames; + F32 fpsNext; + F32 mUpdateInterval; + + FPSTracker(); + + /// Resets the FPS variables + void reset(); + + /// Updates the FPS variables + void update(); +}; + +extern FPSTracker gFPS; + +#endif \ No newline at end of file diff --git a/Engine/source/util/imposterCapture.cpp b/Engine/source/util/imposterCapture.cpp new file mode 100644 index 000000000..a8fff13e8 --- /dev/null +++ b/Engine/source/util/imposterCapture.cpp @@ -0,0 +1,522 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "util/imposterCapture.h" + +#include "gfx/bitmap/gBitmap.h" +#include "core/color.h" +#include "renderInstance/renderPassManager.h" +#include "renderInstance/renderMeshMgr.h" +#include "materials/materialManager.h" +#include "materials/materialFeatureTypes.h" +#include "materials/customMaterialDefinition.h" +#include "ts/tsShapeInstance.h" +#include "scene/sceneManager.h" +#include "scene/sceneRenderState.h" +#include "lighting/lightInfo.h" +#include "gfx/gfxTransformSaver.h" +#include "gfx/gfxDebugEvent.h" +#include "core/stream/fileStream.h" + + +/// A material hook used to hold imposter generation +/// rendering materials for an object. +class ImposterCaptureMaterialHook : public MatInstanceHook +{ +public: + + ImposterCaptureMaterialHook(); + + // MatInstanceHook + virtual ~ImposterCaptureMaterialHook(); + virtual const MatInstanceHookType& getType() const { return Type; } + + /// The material hook type. + static const MatInstanceHookType Type; + + void init( BaseMatInstance *mat ); + + static BaseMatInstance* getDiffuseInst( BaseMatInstance *inMat ) + { return _getOrCreateHook( inMat )->mDiffuseMatInst; } + + static BaseMatInstance* getNormalsInst( BaseMatInstance *inMat ) + { return _getOrCreateHook( inMat )->mNormalsMatInst; } + +protected: + + static void _overrideFeatures( ProcessedMaterial *mat, + U32 stageNum, + MaterialFeatureData &fd, + const FeatureSet &features ); + + static ImposterCaptureMaterialHook* _getOrCreateHook( BaseMatInstance *inMat ); + + /// + BaseMatInstance *mDiffuseMatInst; + + /// + BaseMatInstance *mNormalsMatInst; +}; + + +const MatInstanceHookType ImposterCaptureMaterialHook::Type( "ImposterCapture" ); + + +ImposterCaptureMaterialHook::ImposterCaptureMaterialHook() + : mDiffuseMatInst( NULL ), + mNormalsMatInst( NULL ) +{ +} + +ImposterCaptureMaterialHook::~ImposterCaptureMaterialHook() +{ + SAFE_DELETE( mDiffuseMatInst ); + SAFE_DELETE( mNormalsMatInst ); +} + +void ImposterCaptureMaterialHook::init( BaseMatInstance *inMat ) +{ + // We cannot capture impostors on custom materials + // as we don't know how to get just diffuse and just + // normals rendering. + if ( dynamic_cast( inMat->getMaterial() ) ) + return; + + // Tweak the feature data to include just what we need. + FeatureSet features; + features.addFeature( MFT_VertTransform ); + features.addFeature( MFT_DiffuseMap ); + features.addFeature( MFT_OverlayMap ); + features.addFeature( MFT_DetailMap ); + features.addFeature( MFT_DiffuseColor ); + features.addFeature( MFT_AlphaTest ); + features.addFeature( MFT_IsTranslucent ); + + const String &matName = inMat->getMaterial()->getName(); + + mDiffuseMatInst = MATMGR->createMatInstance( matName ); + mDiffuseMatInst->getFeaturesDelegate().bind( &ImposterCaptureMaterialHook::_overrideFeatures ); + mDiffuseMatInst->init( features, inMat->getVertexFormat() ); + + features.addFeature( MFT_IsDXTnm ); + features.addFeature( MFT_NormalMap ); + features.addFeature( MFT_NormalsOut ); + mNormalsMatInst = MATMGR->createMatInstance( matName ); + mNormalsMatInst->getFeaturesDelegate().bind( &ImposterCaptureMaterialHook::_overrideFeatures ); + mNormalsMatInst->init( features, inMat->getVertexFormat() ); +} + +void ImposterCaptureMaterialHook::_overrideFeatures( ProcessedMaterial *mat, + U32 stageNum, + MaterialFeatureData &fd, + const FeatureSet &features ) +{ + if ( features.hasFeature( MFT_NormalsOut) ) + fd.features.addFeature( MFT_NormalsOut ); + + fd.features.addFeature( MFT_ForwardShading ); +} + +ImposterCaptureMaterialHook* ImposterCaptureMaterialHook::_getOrCreateHook( BaseMatInstance *inMat ) +{ + ImposterCaptureMaterialHook *hook = inMat->getHook(); + if ( !hook ) + { + // Create a hook and initialize it using the incoming material. + hook = new ImposterCaptureMaterialHook; + hook->init( inMat ); + inMat->addHook( hook ); + } + + return hook; +} + + +ImposterCapture::ImposterCapture() +: mShapeInstance( NULL ), + mDl( 0 ), + mDim( 0 ), + mRadius( 0.0f ), + mCenter( Point3F( 0, 0, 0 ) ), + mRenderPass( NULL ), + mMeshRenderBin( NULL ), + mBlackBmp( NULL ), + mWhiteBmp( NULL ), + mState( NULL ), + mRenderTarget( NULL ) +{ +} + +ImposterCapture::~ImposterCapture() +{ + AssertFatal( !mShapeInstance, "ImposterCapture destructor - TSShapeInstance hasn't been cleared!" ); +} + +void ImposterCapture::_colorAverageFilter( U32 dimensions, const U8 *inBmpBits, U8 *outBmpBits ) +{ + ColorF color; + U32 count = 0; + U32 index, index2; + + for ( S32 y = 0; y < dimensions; y++ ) + { + for( S32 x = 0; x < dimensions; x++ ) + { + // We only blend on transparent pixels. + index = ( ( y * dimensions ) + x ) * 4; + if ( inBmpBits[index+3] > 84 ) + { + outBmpBits[index+0] = inBmpBits[index+0]; + outBmpBits[index+1] = inBmpBits[index+1]; + outBmpBits[index+2] = inBmpBits[index+2]; + outBmpBits[index+3] = inBmpBits[index+3]; //hack + continue; + } + + color.set(0,0,0); + count = 0; + + for ( S32 fy = y-6; fy <= y+6; fy++ ) + { + for ( S32 fx = x-6; fx <= x+6; fx++ ) + { + if ( fy >= 0 && fy < (dimensions-1) && + fx >= 0 && fx < (dimensions-1) ) + { + index2 = ( ( fy * dimensions ) + fx ) * 4; + if ( inBmpBits[index2+3] > 84 ) + { + color.red += inBmpBits[index2+0]; + color.green += inBmpBits[index2+1]; + color.blue += inBmpBits[index2+2]; + ++count; + } + } + } + } + + outBmpBits[index+0] = (U8)( (F32)color.red / (F32)count ); + outBmpBits[index+1] = (U8)( (F32)color.green / (F32)count ); + outBmpBits[index+2] = (U8)( (F32)color.blue / (F32)count ); + outBmpBits[index+3] = 0; + } + } +} + +void ImposterCapture::_renderToTexture( GFXTexHandle texHandle, GBitmap *outBitmap, const ColorI &color ) +{ + GFXDEBUGEVENT_SCOPE( ImposterCapture_RenderToTexture, ColorI::RED ); + PROFILE_SCOPE( ImposterCapture_RenderToTexture ); + + mRenderTarget->attachTexture( GFXTextureTarget::Color0, texHandle ); + mRenderTarget->attachTexture( GFXTextureTarget::DepthStencil, GFXTextureTarget::sDefaultDepthStencil ); + GFX->setActiveRenderTarget( mRenderTarget ); + + GFX->clear( GFXClearZBuffer | GFXClearStencil | GFXClearTarget, color, 1.0f, 0 ); + + mShapeInstance->render( mRData, mDl, 1.0f ); + + mState->getRenderPass()->renderPass( mState ); + + mRenderTarget->resolve(); + + texHandle->copyToBmp( outBitmap ); +} + +void ImposterCapture::_separateAlpha( GBitmap *imposterOut ) +{ + PROFILE_START(TSShapeInstance_snapshot_sb_separate); + + // TODO: Remove all this when we get rid of the 'render on black/white'. + + // now separate the color and alpha channels + GBitmap *bmp = new GBitmap; + bmp->allocateBitmap(mDim, mDim, false, GFXFormatR8G8B8A8); + U8 * wbmp = (U8*)mWhiteBmp->getBits(0); + U8 * bbmp = (U8*)mBlackBmp->getBits(0); + U8 * dst = (U8*)bmp->getBits(0); + + const U32 pixCount = mDim * mDim; + + // simpler, probably faster... + for ( U32 i=0; i < pixCount; i++ ) + { + // Shape on black background is alpha * color, shape on white + // background is alpha * color + (1-alpha) * 255 we want 255 * + // alpha, or 255 - (white - black). + // + // JMQ: or more verbosely: + // cB = alpha * color + (0 * (1 - alpha)) + // cB = alpha * color + // cW = alpha * color + (255 * (1 - alpha)) + // cW = cB + (255 * (1 - alpha)) + // solving for alpha + // cW - cB = 255 * (1 - alpha) + // (cW - cB)/255 = (1 - alpha) + // alpha = 1 - (cW - cB)/255 + // since we want alpha*255, multiply through by 255 + // alpha * 255 = 255 - cW - cB + U32 alpha = 255 - (wbmp[i*3+0] - bbmp[i*3+0]); + alpha += 255 - (wbmp[i*3+1] - bbmp[i*3+1]); + alpha += 255 - (wbmp[i*3+2] - bbmp[i*3+2]); + + if ( alpha != 0 ) + { + F32 floatAlpha = ((F32)alpha)/(3.0f*255.0f); + dst[i*4+0] = (U8)(bbmp[i*3+0] / floatAlpha); + dst[i*4+1] = (U8)(bbmp[i*3+1] / floatAlpha); + dst[i*4+2] = (U8)(bbmp[i*3+2] / floatAlpha); + + // Before we assign the alpha we "fizzle" the value + // if its greater than 84. This causes the imposter + // to dissolve instead of popping into view. + alpha /= 3; + dst[i*4+3] = (U8)alpha; + } + else + { + dst[i*4+0] = dst[i*4+1] = dst[i*4+2] = dst[i*4+3] = 0; + } + } + + PROFILE_END(); // TSShapeInstance_snapshot_sb_separate + + PROFILE_START(TSShapeInstance_snapshot_sb_filter); + + // We now run a special kernel filter over the image that + // averages color into the transparent areas. This should + // in essence give us a border around the edges of the + // imposter silhouette which fixes the artifacts around the + // alpha test billboards. + U8* dst2 = (U8*)imposterOut->getBits(0); + + _colorAverageFilter( mDim, dst, dst2 ); + + if ( 0 ) + { + FileStream fs; + if ( fs.open( "./imposterout.png", Torque::FS::File::Write ) ) + imposterOut->writeBitmap( "png", fs ); + + fs.close(); + + if ( fs.open( "./temp.png", Torque::FS::File::Write ) ) + bmp->writeBitmap( "png", fs ); + + fs.close(); + } + + + PROFILE_END(); // TSShapeInstance_snapshot_sb_filter + + delete bmp; +} + + +void ImposterCapture::_convertDXT5nm( GBitmap *normalsOut ) +{ + PROFILE_SCOPE(ImposterCapture_ConvertDXT5nm); + + U8 *bits = (U8*)normalsOut->getBits(0); + const U32 pixCount = mDim * mDim; + U8 x, y, z; + + // Encoding in object space DXT5 which moves + // one of the coords to the alpha channel for + // improved precision.... in our case z. + + for ( U32 i=0; i < pixCount; i++ ) + { + x = bits[i*4+0]; + y = bits[i*4+1]; + z = bits[i*4+2]; + + bits[i*4+0] = x; + bits[i*4+1] = y; + bits[i*4+2] = 0; + bits[i*4+3] = z; + } +} + +void ImposterCapture::begin( TSShapeInstance *shapeInst, + S32 dl, + S32 dim, + F32 radius, + const Point3F ¢er ) +{ + mShapeInstance = shapeInst; + mDl = dl; + mDim = dim; + mRadius = radius; + mCenter = center; + + mBlackTex.set( mDim, mDim, GFXFormatR8G8B8A8, &GFXDefaultRenderTargetProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ) ); + mWhiteTex.set( mDim, mDim, GFXFormatR8G8B8A8, &GFXDefaultRenderTargetProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ) ); + mNormalTex.set( mDim, mDim, GFXFormatR8G8B8A8, &GFXDefaultRenderTargetProfile, avar( "%s() - (line %d)", __FUNCTION__, __LINE__ ) ); + + // copy the black render target data into a bitmap + mBlackBmp = new GBitmap; + mBlackBmp->allocateBitmap(mDim, mDim, false, GFXFormatR8G8B8); + + // copy the white target data into a bitmap + mWhiteBmp = new GBitmap; + mWhiteBmp->allocateBitmap(mDim, mDim, false, GFXFormatR8G8B8); + + // Setup viewport and frustrum to do orthographic projection. + RectI viewport( 0, 0, mDim, mDim ); + GFX->setViewport( viewport ); + GFX->setOrtho( -mRadius, mRadius, -mRadius, mRadius, 1, 20.0f * mRadius ); + + // Position camera looking out the X axis. + MatrixF cameraMatrix( true ); + cameraMatrix.setColumn( 0, Point3F( 0, 0, 1 ) ); + cameraMatrix.setColumn( 1, Point3F( 1, 0, 0 ) ); + cameraMatrix.setColumn( 2, Point3F( 0, 1, 0 ) ); + + // setup scene state required for TS mesh render...this is messy and inefficient; + // should have a mode where most of this is done just once (and then + // only the camera matrix changes between snapshots). + // note that we use getFrustum here, but we set up an ortho projection above. + // it doesn't seem like the scene state object pays attention to whether the projection is + // ortho or not. this could become a problem if some code downstream tries to + // reconstruct the projection matrix using the dimensions and doesn't + // realize it should be ortho. at the moment no code is doing that. + F32 left, right, top, bottom, nearPlane, farPlane; + bool isOrtho; + GFX->getFrustum( &left, &right, &bottom, &top, &nearPlane, &farPlane, &isOrtho ); + Frustum frust( isOrtho, left, right, top, bottom, nearPlane, farPlane, cameraMatrix ); + + // Set up render pass. + + mRenderPass = new RenderPassManager(); + mRenderPass->assignName( "DiffuseRenderPass" ); + mMeshRenderBin = new RenderMeshMgr(); + mRenderPass->addManager( mMeshRenderBin ); + + // Set up scene state. + + mState = new SceneRenderState( + gClientSceneGraph, + SPT_Diffuse, + SceneCameraState( viewport, frust, GFX->getWorldMatrix(),GFX->getProjectionMatrix() ), + mRenderPass, + false + ); + + // Set up our TS render state. + mRData.setSceneState( mState ); + mRData.setCubemap( NULL ); + mRData.setFadeOverride( 1.0f ); + + // set gfx up for render to texture + GFX->pushActiveRenderTarget(); + mRenderTarget = GFX->allocRenderToTextureTarget(); + +} + +void ImposterCapture::capture( const MatrixF &rotMatrix, + GBitmap **imposterOut, + GBitmap **normalMapOut ) +{ + GFXTransformSaver saver; + + // this version of the snapshot function renders the shape to a black texture, then to white, then reads bitmaps + // back for both renders and combines them, restoring the alpha and color values. this is based on the + // TGE implementation. it is not fast due to the copy and software combination operations. the generated bitmaps + // are upside-down (which is how TGE generated them...) + + (*imposterOut) = new GBitmap( mDim, mDim, false, GFXFormatR8G8B8A8 ); + (*normalMapOut) = new GBitmap( mDim, mDim, false, GFXFormatR8G8B8A8 ); + + // The object to world transform. + MatrixF centerMat( true ); + centerMat.setPosition( -mCenter ); + MatrixF objMatrix( rotMatrix ); + objMatrix.mul( centerMat ); + GFX->setWorldMatrix( objMatrix ); + + // The view transform. + MatrixF view( EulerF( M_PI_F / 2.0f, 0, M_PI_F ), Point3F( 0, 0, -10.0f * mRadius ) ); + mRenderPass->assignSharedXform( RenderPassManager::View, view ); + + mRenderPass->assignSharedXform( RenderPassManager::Projection, GFX->getProjectionMatrix() ); + + // Render the diffuse pass. + mRenderPass->clear(); + mMeshRenderBin->getMatOverrideDelegate().bind( ImposterCaptureMaterialHook::getDiffuseInst ); + _renderToTexture( mBlackTex, mBlackBmp, ColorI(0, 0, 0, 0) ); + _renderToTexture( mWhiteTex, mWhiteBmp, ColorI(255, 255, 255, 255) ); + + // Now render the normals. + mRenderPass->clear(); + mMeshRenderBin->getMatOverrideDelegate().bind( ImposterCaptureMaterialHook::getNormalsInst ); + _renderToTexture( mNormalTex, *normalMapOut, ColorI(0, 0, 0, 0) ); + + + _separateAlpha( *imposterOut ); + _convertDXT5nm( *normalMapOut ); + + if ( 0 ) + { + // Render out the bitmaps for debug purposes. + FileStream fs; + if ( fs.open( "./blackbmp.png", Torque::FS::File::Write ) ) + mBlackBmp->writeBitmap( "png", fs ); + + fs.close(); + + if ( fs.open( "./whitebmp.png", Torque::FS::File::Write ) ) + mWhiteBmp->writeBitmap( "png", fs ); + + fs.close(); + + if ( fs.open( "./normalbmp.png", Torque::FS::File::Write ) ) + (*normalMapOut)->writeBitmap( "png", fs ); + + fs.close(); + + if ( fs.open( "./finalimposter.png", Torque::FS::File::Write ) ) + (*imposterOut)->writeBitmap( "png", fs ); + + fs.close(); + } +} + +void ImposterCapture::end() +{ + GFX->popActiveRenderTarget(); + + mBlackTex.free(); + mWhiteTex.free(); + mNormalTex.free(); + + mShapeInstance = NULL; + + mRenderTarget = NULL; + mMeshRenderBin = NULL; // Deleted by mRenderPass + SAFE_DELETE( mState ); + SAFE_DELETE( mRenderPass ); + SAFE_DELETE( mBlackBmp ); + SAFE_DELETE( mWhiteBmp ); +} + diff --git a/Engine/source/util/imposterCapture.h b/Engine/source/util/imposterCapture.h new file mode 100644 index 000000000..b18c3f1da --- /dev/null +++ b/Engine/source/util/imposterCapture.h @@ -0,0 +1,110 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _IMPOSTERCAPTURE_H_ +#define _IMPOSTERCAPTURE_H_ + +#ifndef _MATHTYPES_H_ +#include "math/mathTypes.h" +#endif +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _TSRENDERDATA_H_ +#include "ts/tsRenderState.h" +#endif +#ifndef _GFXTEXTUREHANDLE_H_ +#include "gfx/gfxTextureHandle.h" +#endif + +class GBitmap; +class SceneRenderState; +class TSShapeInstance; +class GFXTextureTarget; +class RenderPassManager; +class RenderMeshMgr; + + +class ImposterCapture +{ + +protected: + + S32 mDl; + S32 mDim; + + /// The bounding radius of the shape used to size the billboard. + F32 mRadius; + + /// + Point3F mCenter; + + GBitmap *mBlackBmp; + GBitmap *mWhiteBmp; + + GFXTexHandle mBlackTex; + GFXTexHandle mWhiteTex; + GFXTexHandle mNormalTex; + + SceneRenderState *mState; + TSShapeInstance *mShapeInstance; + TSRenderState mRData; + + GFXTextureTarget *mRenderTarget; + + RenderPassManager *mRenderPass; + RenderMeshMgr *mMeshRenderBin; + + void _colorAverageFilter( U32 dimensions, const U8 *inBmpBits, U8 *outBmpBits ); + void _renderToTexture( GFXTexHandle texHandle, GBitmap *outBitmap, const ColorI &color ); + + void _separateAlpha( GBitmap *imposterOut ); + + void _convertDXT5nm( GBitmap *imposterOut ); + +public: + + ImposterCapture(); + + ~ImposterCapture(); + + void begin( TSShapeInstance *shapeInst, + S32 dl, + S32 dim, + F32 radius, + const Point3F ¢er ); + + void capture( const MatrixF &rotMatrix, + GBitmap **imposterOut, + GBitmap **normalMapOut ); + + void end(); + +}; + +#endif // _IMPOSTERCAPTURE_H_ \ No newline at end of file diff --git a/Engine/source/util/interpolatedChangeProperty.h b/Engine/source/util/interpolatedChangeProperty.h new file mode 100644 index 000000000..6adb3428f --- /dev/null +++ b/Engine/source/util/interpolatedChangeProperty.h @@ -0,0 +1,191 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _INTERPOLATEDCHANGEPROPERTY_H_ +#define _INTERPOLATEDCHANGEPROPERTY_H_ + +#ifndef _SIM_H_ +#include "console/sim.h" +#endif + +#ifndef _MEASE_H_ +#include "math/mEase.h" +#endif + +#ifndef _TIMESOURCE_H_ +#include "core/util/timeSource.h" +#endif + + + +/// A property that smoothly transitions to new values instead of assuming +/// them right away. +/// +/// @param T Value type. Must have "interpolate( from, to, factor )" method. +/// @param TimeSource Time source to which interpolation is synchronized. +template< typename T, class TimeSource = GenericTimeSource< SimMSTimer > > +class InterpolatedChangeProperty +{ + public: + + enum + { + /// Default time (in milliseconds) to go from one value + /// to a new one. + DEFAULT_TRANSITION_TIME = 2000 + }; + + typedef TimeSource TimeSourceType; + typedef typename TimeSource::TickType TimeType; + + protected: + + /// The current value. + mutable T mCurrentValue; + + /// @name Transitioning + /// + /// Transitioning allows to smoothly go from one value to + /// a different one over a period of time. + /// + /// @{ + + /// + TimeSourceType mTimeSource; + + /// Number of milliseconds it takes to go from one value + /// to a different one. + TimeType mBlendPhaseTime; + + /// Interpolation to use for going from source to target. + EaseF mTransitionCurve; + + /// The time the transition started. If 0, no transition is in progress. + mutable TimeType mTransitionStartTime; + + /// The value we are transitioning from. + T mSourceValue; + + /// The value we are transitioning to. + T mTargetValue; + + /// @} + + /// Update #mCurrentValue. + void _update() const; + + public: + + /// + InterpolatedChangeProperty( const T& initialValue = T() ) + : mCurrentValue( initialValue ), + mTargetValue( initialValue ), + mBlendPhaseTime( DEFAULT_TRANSITION_TIME ), + mTransitionStartTime( 0 ) + { + // By default, start time source right away. + mTimeSource.start(); + } + + /// Get the current value. If a transition is in progress, this will be + /// an interpolation of the last value and the new one. + const T& getCurrentValue() const + { + _update(); + return mCurrentValue; + } + + /// Set the interpolation to use for going from one ambient color to + /// a different one. + void setTransitionCurve( const EaseF& ease ) { mTransitionCurve = ease; } + + /// Set the amount of time it takes to go from one ambient color to + /// a different one. + void setTransitionTime( TimeType time ) { mBlendPhaseTime = time; } + + /// Set the desired value. If this differs from the current value, + /// a smooth blend to the given color will be initiated. + /// + /// @param value Desired value. + void setTargetValue( const T& value ); + + /// Return the time source to which interpolation synchronizes. + const TimeSourceType& geTimeSource() const { return mTimeSource; } + TimeSourceType& getTimeSource() { return mTimeSource; } +}; + + +//----------------------------------------------------------------------------- + +template< typename T, typename TimeSource > +void InterpolatedChangeProperty< T, TimeSource >::setTargetValue( const T& value ) +{ + if( mTargetValue == value ) + return; + + if( mBlendPhaseTime == 0 ) + { + mTargetValue = value; + mCurrentValue = value; + } + else + { + // Set the source value to the current value (which may be interpolated) + // and then start a transition to the given target. + + mSourceValue = getCurrentValue(); + mTargetValue = value; + mTransitionStartTime = mTimeSource.getPosition(); + } +} + +//----------------------------------------------------------------------------- + +template< typename T, typename TimeSource > +void InterpolatedChangeProperty< T, TimeSource >::_update() const +{ + // Nothing to do if no transition in progress. + + if( !mTransitionStartTime ) + return; + + // See if we have finished the transition. + + TimeType deltaTime = mTimeSource.getPosition() - mTransitionStartTime; + if( deltaTime >= mBlendPhaseTime ) + { + // We're done. + mCurrentValue = mTargetValue; + mTransitionStartTime = 0; + + return; + } + + // Determine the interpolated value. + + F32 blendFactor = F32( deltaTime ) / F32( mBlendPhaseTime ); + blendFactor = mTransitionCurve.getUnitValue( blendFactor ); + + mCurrentValue.interpolate( mSourceValue, mTargetValue, blendFactor ); +} + +#endif // !_INTERPOLATEDCHANGEPROPERTY_H_ diff --git a/Engine/source/util/messaging/dispatcher.cpp b/Engine/source/util/messaging/dispatcher.cpp new file mode 100644 index 000000000..15ba9f3e1 --- /dev/null +++ b/Engine/source/util/messaging/dispatcher.cpp @@ -0,0 +1,421 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "util/messaging/dispatcher.h" + +#include "platform/threads/mutex.h" +#include "core/tSimpleHashTable.h" +#include "core/util/safeDelete.h" + +namespace Dispatcher +{ + +//----------------------------------------------------------------------------- +// IMessageListener Methods +//----------------------------------------------------------------------------- + +IMessageListener::~IMessageListener() +{ + for(S32 i = 0;i < mQueues.size();i++) + { + unregisterMessageListener(mQueues[i], this); + } +} + +void IMessageListener::onAddToQueue(StringTableEntry queue) +{ + // [tom, 8/20/2006] The dispatcher won't let us get added twice, so no need + // to worry about it here. + + mQueues.push_back(queue); +} + +void IMessageListener::onRemoveFromQueue(StringTableEntry queue) +{ + for(S32 i = 0;i < mQueues.size();i++) + { + if(mQueues[i] == queue) + { + mQueues.erase(i); + return; + } + } +} + +//----------------------------------------------------------------------------- +// Global State +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// @brief Internal class used by the dispatcher +//----------------------------------------------------------------------------- +typedef struct _DispatchData +{ + void *mMutex; + SimpleHashTable mQueues; + U32 mLastAnonQueueID; + + _DispatchData() + { + mMutex = Mutex::createMutex(); + mLastAnonQueueID = 0; + } + + ~_DispatchData() + { + if(Mutex::lockMutex( mMutex ) ) + { + mQueues.clearTables(); + + Mutex::unlockMutex( mMutex ); + } + + Mutex::destroyMutex( mMutex ); + //SAFE_DELETE(mMutex); + mMutex = NULL; + } + + const char *makeAnonQueueName() + { + char buf[512]; + dSprintf(buf, sizeof(buf), "AnonQueue.%lu", mLastAnonQueueID++); + return StringTable->insert(buf); + } +} _DispatchData; + +static _DispatchData& _dispatcherGetGDispatchData() +{ + static _DispatchData dispatchData; + return dispatchData; +} + +#define gDispatchData _dispatcherGetGDispatchData() + + +//----------------------------------------------------------------------------- +// Queue Registration +//----------------------------------------------------------------------------- + +bool isQueueRegistered(const char *name) +{ + MutexHandle mh; + if(mh.lock(gDispatchData.mMutex, true)) + { + return gDispatchData.mQueues.retreive(name) != NULL; + } + + return false; +} + +void registerMessageQueue(const char *name) +{ + if(isQueueRegistered(name)) + return; + + if(Mutex::lockMutex( gDispatchData.mMutex, true )) + { + MessageQueue *queue = new MessageQueue; + queue->mQueueName = StringTable->insert(name); + gDispatchData.mQueues.insert(queue, name); + + Mutex::unlockMutex( gDispatchData.mMutex ); + } +} + +extern const char * registerAnonMessageQueue() +{ + const char *name = NULL; + if(Mutex::lockMutex( gDispatchData.mMutex, true )) + { + name = gDispatchData.makeAnonQueueName(); + Mutex::unlockMutex( gDispatchData.mMutex ); + } + + if(name) + registerMessageQueue(name); + + return name; +} + +void unregisterMessageQueue(const char *name) +{ + MutexHandle mh; + if(mh.lock(gDispatchData.mMutex, true)) + { + MessageQueue *queue = gDispatchData.mQueues.remove(name); + if(queue == NULL) + return; + + // Tell the listeners about it + for(S32 i = 0;i < queue->mListeners.size();i++) + { + queue->mListeners[i]->onRemoveFromQueue(name); + } + + delete queue; + } +} + +//----------------------------------------------------------------------------- +// Message Listener Registration +//----------------------------------------------------------------------------- + +bool registerMessageListener(const char *queue, IMessageListener *listener) +{ + if(! isQueueRegistered(queue)) + registerMessageQueue(queue); + + MutexHandle mh; + + if(! mh.lock(gDispatchData.mMutex, true)) + return false; + + MessageQueue *q = gDispatchData.mQueues.retreive(queue); + if(q == NULL) + { + Con::errorf("Dispatcher::registerMessageListener - Queue '%s' not found?! It should have been added automatically!", queue); + return false; + } + + for(VectorPtr::iterator i = q->mListeners.begin();i != q->mListeners.end();i++) + { + if(*i == listener) + return false; + } + + q->mListeners.push_front(listener); + listener->onAddToQueue(StringTable->insert(queue)); + return true; +} + +void unregisterMessageListener(const char *queue, IMessageListener *listener) +{ + if(! isQueueRegistered(queue)) + return; + + MutexHandle mh; + + if(! mh.lock(gDispatchData.mMutex, true)) + return; + + MessageQueue *q = gDispatchData.mQueues.retreive(queue); + if(q == NULL) + return; + + for(VectorPtr::iterator i = q->mListeners.begin();i != q->mListeners.end();i++) + { + if(*i == listener) + { + listener->onRemoveFromQueue(StringTable->insert(queue)); + q->mListeners.erase(i); + return; + } + } +} + +//----------------------------------------------------------------------------- +// Dispatcher +//----------------------------------------------------------------------------- + +bool dispatchMessage( const char* queue, const char* msg, const char* data) +{ + AssertFatal( queue != NULL, "Dispatcher::dispatchMessage - Got a NULL queue name" ); + AssertFatal( msg != NULL, "Dispatcher::dispatchMessage - Got a NULL message" ); + + MutexHandle mh; + + if(! mh.lock(gDispatchData.mMutex, true)) + return true; + + MessageQueue *q = gDispatchData.mQueues.retreive(queue); + if(q == NULL) + { + Con::errorf("Dispatcher::dispatchMessage - Attempting to dispatch to unknown queue '%s'", queue); + return true; + } + + return q->dispatchMessage(msg, data); +} + + +bool dispatchMessageObject(const char *queue, Message *msg) +{ + MutexHandle mh; + + if(msg == NULL) + return true; + + msg->addReference(); + + if(! mh.lock(gDispatchData.mMutex, true)) + { + msg->freeReference(); + return true; + } + + MessageQueue *q = gDispatchData.mQueues.retreive(queue); + if(q == NULL) + { + Con::errorf("Dispatcher::dispatchMessage - Attempting to dispatch to unknown queue '%s'", queue); + msg->freeReference(); + return true; + } + + // [tom, 8/19/2006] Make sure that the message is registered with the sim, since + // when it's ref count is zero it'll be deleted with deleteObject() + if(! msg->isProperlyAdded()) + { + SimObjectId id = Message::getNextMessageID(); + if(id != 0xffffffff) + msg->registerObject(id); + else + { + Con::errorf("dispatchMessageObject: Message was not registered and no more object IDs are available for messages"); + + msg->freeReference(); + return false; + } + } + + bool bResult = q->dispatchMessageObject(msg); + msg->freeReference(); + + return bResult; +} + +//----------------------------------------------------------------------------- +// Internal Functions +//----------------------------------------------------------------------------- + +MessageQueue * getMessageQueue(const char *name) +{ + return gDispatchData.mQueues.retreive(name); +} + +extern bool lockDispatcherMutex() +{ + return Mutex::lockMutex(gDispatchData.mMutex); +} + +extern void unlockDispatcherMutex() +{ + Mutex::unlockMutex(gDispatchData.mMutex); +} + +} // end namespace Dispatcher + +//----------------------------------------------------------------------------- +// Console Methods +//----------------------------------------------------------------------------- + +using namespace Dispatcher; + +ConsoleFunction(isQueueRegistered, bool, 2, 2, "(string queueName)" + "@brief Determines if a dispatcher queue exists\n\n" + "@param queueName String containing the name of queue\n" + "@ingroup Messaging") +{ + return isQueueRegistered(argv[1]); +} + +ConsoleFunction(registerMessageQueue, void, 2, 2, "(string queueName)" + "@brief Registeres a dispatcher queue\n\n" + "@param queueName String containing the name of queue\n" + "@ingroup Messaging") +{ + return registerMessageQueue(argv[1]); +} + +ConsoleFunction(unregisterMessageQueue, void, 2, 2, "(string queueName)" + "@brief Unregisters a dispatcher queue\n\n" + "@param queueName String containing the name of queue\n" + "@ingroup Messaging") +{ + return unregisterMessageQueue(argv[1]); +} + +//----------------------------------------------------------------------------- + +ConsoleFunction(registerMessageListener, bool, 3, 3, "(string queueName, string listener)" + "@brief Registers an event message\n\n" + "@param queueName String containing the name of queue to attach listener to\n" + "@param listener Name of event messenger\n" + "@ingroup Messaging") +{ + IMessageListener *listener = dynamic_cast(Sim::findObject(argv[2])); + if(listener == NULL) + { + Con::errorf("registerMessageListener - Unable to find listener object, not an IMessageListener ?!"); + return false; + } + + return registerMessageListener(argv[1], listener); +} + +ConsoleFunction(unregisterMessageListener, void, 3, 3, "(string queueName, string listener)" + "@brief Unregisters an event message\n\n" + "@param queueName String containing the name of queue\n" + "@param listener Name of event messenger\n" + "@ingroup Messaging") +{ + IMessageListener *listener = dynamic_cast(Sim::findObject(argv[2])); + if(listener == NULL) + { + Con::errorf("unregisterMessageListener - Unable to find listener object, not an IMessageListener ?!"); + return; + } + + unregisterMessageListener(argv[1], listener); +} + +//----------------------------------------------------------------------------- + +ConsoleFunction(dispatchMessage, bool, 3, 4, "(string queueName, string message, string data)" + "@brief Dispatch a message to a queue\n\n" + "@param queueName Queue to dispatch the message to\n" + "@param message Message to dispatch\n" + "@param data Data for message\n" + "@return True for success, false for failure\n" + "@see dispatchMessageObject\n" + "@ingroup Messaging") +{ + return dispatchMessage(argv[1], argv[2], argc > 3 ? argv[3] : "" ); +} + +ConsoleFunction(dispatchMessageObject, bool, 3, 3, "(string queueName, string message)" + "@brief Dispatch a message object to a queue\n\n" + "@param queueName Queue to dispatch the message to\n" + "@param message Message to dispatch\n" + "@return true for success, false for failure\n" + "@see dispatchMessage\n" + "@ingroup Messaging") +{ + Message *msg = dynamic_cast(Sim::findObject(argv[2])); + if(msg == NULL) + { + Con::errorf("dispatchMessageObject - Unable to find message object"); + return false; + } + + return dispatchMessageObject(argv[1], msg); +} diff --git a/Engine/source/util/messaging/dispatcher.h b/Engine/source/util/messaging/dispatcher.h new file mode 100644 index 000000000..986a3cba1 --- /dev/null +++ b/Engine/source/util/messaging/dispatcher.h @@ -0,0 +1,276 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _DISPATCHER_H_ +#define _DISPATCHER_H_ + +#ifndef _MESSAGE_H_ +#include "util/messaging/message.h" +#endif + +#ifndef _CONSOLE_H_ +#include "console/console.h" +#endif + +/// @addtogroup msgsys Message System +// @{ + +//----------------------------------------------------------------------------- +/// @brief Namespace for the message dispatcher functions +//----------------------------------------------------------------------------- +namespace Dispatcher +{ + +// [tom, 2/19/2007] This semi colon prevents VS from auto indenting the comments +// below, which is really annoying when you're trying to write docs. +; + +/// @addtogroup msgsys Message System +// @{ + +//----------------------------------------------------------------------------- +// Interface for objects that receive messages +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// @brief Listener interface for objects that receive messages +/// +/// @see ScriptMsgListener +//----------------------------------------------------------------------------- +class IMessageListener +{ +protected: + /// List of queues this listener is registered with. + Vector mQueues; + +public: + virtual ~IMessageListener(); + + //----------------------------------------------------------------------------- + /// @brief Callback for when messages are received + /// + /// @param queue The name of the queue the message was dispatched to + /// @param msg The type of message + /// @param data The data for the message + /// @return false to prevent other listeners receiving this message, true otherwise + /// @see onMessageObjectReceived() + //----------------------------------------------------------------------------- + virtual bool onMessageReceived(StringTableEntry queue, const char *msg, const char *data) = 0; + + //----------------------------------------------------------------------------- + /// @brief Callback for when message objects are received + /// + /// @param queue The name of the queue the message was dispatched to + /// @param msg The message object + /// @return false to prevent other listeners receiving this message, true otherwise + /// @see onMessageReceived() + //----------------------------------------------------------------------------- + virtual bool onMessageObjectReceived(StringTableEntry queue, Message *msg ) = 0; + + + //----------------------------------------------------------------------------- + /// @brief Callback for when the listener is added to a queue + /// + /// The default implementation of onAddToQueue() and onRemoveFromQueue() + /// provide tracking of the queues this listener is added to through the + /// #mQueues member. Overrides of onAddToQueue() or onRemoveFromQueue() + /// should ensure they call the parent implementation in any overrides. + /// + /// @param queue The name of the queue that the listener added to + /// @see onRemoveFromQueue() + //----------------------------------------------------------------------------- + virtual void onAddToQueue(StringTableEntry queue); + + //----------------------------------------------------------------------------- + /// @brief Callback for when the listener is removed from a queue + /// + /// The default implementation of onAddToQueue() and onRemoveFromQueue() + /// provide tracking of the queues this listener is added to through the + /// #mQueues member. Overrides of onAddToQueue() or onRemoveFromQueue() + /// should ensure they call the parent implementation in any overrides. + /// + /// @param queue The name of the queue the listener was removed from + /// @see onAddToQueue() + //----------------------------------------------------------------------------- + virtual void onRemoveFromQueue(StringTableEntry queue); +}; + +//----------------------------------------------------------------------------- +/// @brief Internal class for tracking message queues +//----------------------------------------------------------------------------- +struct MessageQueue +{ + StringTableEntry mQueueName; + VectorPtr mListeners; + + MessageQueue() : mQueueName("") + { + } + + bool isEmpty() { return mListeners.size() == 0; } + + bool dispatchMessage(const char* event, const char* data) + { + for(VectorPtr::iterator i = mListeners.begin();i != mListeners.end();i++) + { + if( !(*i)->onMessageReceived(mQueueName, event, data) ) + return false; + } + return true; + } + + bool dispatchMessageObject(Message *msg) + { + for(VectorPtr::iterator i = mListeners.begin();i != mListeners.end();i++) + { + if( !(*i)->onMessageObjectReceived(mQueueName, msg) ) + return false; + } + return true; + } +}; + +//----------------------------------------------------------------------------- +// Message Dispatcher Functions +//----------------------------------------------------------------------------- + +/// @name Message Queue Management +// @{ + +//----------------------------------------------------------------------------- +/// @brief Check if a message queue is registered +/// +/// @param name The name of the message queue +/// @return true if the queue is registered, false otherwise +/// @see registerMessageQueue(), unregisterMessageQueue() +//----------------------------------------------------------------------------- +extern bool isQueueRegistered(const char *name); + +//----------------------------------------------------------------------------- +/// @brief Register a message queue +/// +/// @param name The name of the message queue to register +/// @see isQueueRegistered(), unregisterMessageQueue() +//----------------------------------------------------------------------------- +extern void registerMessageQueue(const char *name); + +//----------------------------------------------------------------------------- +/// @brief Register an anonymous message queue +/// +/// @return name of anonymous message queue for passing to other functions +/// @see isQueueRegistered(), unregisterMessageQueue() +//----------------------------------------------------------------------------- +extern const char *registerAnonMessageQueue(); + +//----------------------------------------------------------------------------- +/// @brief Unregister a message queue +/// +/// @param name The name of the message queue +/// @see registerMessageQueue(), isQueueRegistered() +//----------------------------------------------------------------------------- +extern void unregisterMessageQueue(const char *name); + +//----------------------------------------------------------------------------- +/// @brief Register a listener with a queue to receive messages +/// +/// @param queue The name of the queue to register the listener with +/// @param listener The listener interface that receives messages +/// @return true for success, false otherwise +/// @see unregisterMessageListener() +//----------------------------------------------------------------------------- +extern bool registerMessageListener(const char *queue, IMessageListener *listener); + +//----------------------------------------------------------------------------- +/// @brief Unregister a listener with a queue +/// +/// @param queue The name of the queue to unregister the listener +/// @param listener The listener interface that was passed to registerMessageListener() +/// @see registerMessageListener() +//----------------------------------------------------------------------------- +extern void unregisterMessageListener(const char *queue, IMessageListener *listener); + +// @} + +/// @name Message Dispatcher +// @{ + +//----------------------------------------------------------------------------- +/// @brief Dispatch a message to a queue +/// +/// @param queue Queue to dispatch the message to +/// @param msg Message to dispatch +/// @param data Data for message +/// @return true for success, false for failure +/// @see dispatchMessageObject() +//----------------------------------------------------------------------------- +extern bool dispatchMessage(const char *queue, const char *msg, const char *data); + +//----------------------------------------------------------------------------- +/// @brief Dispatch a message object to a queue +/// +/// @param queue Queue to dispatch the message to +/// @param msg Message to dispatch +/// @return true for success, false for failure +/// @see dispatchMessage() +//----------------------------------------------------------------------------- +extern bool dispatchMessageObject(const char *queue, Message *msg); + +// @} + +//----------------------------------------------------------------------------- +// Internal Functions +//----------------------------------------------------------------------------- + +/// @name Internal Functions +// @{ + +//----------------------------------------------------------------------------- +/// @brief Internal function: Lock the dispatcher mutex. +/// @return true for success, false for failure +/// @see unlockDispatcherMutex() +//----------------------------------------------------------------------------- +extern bool lockDispatcherMutex(); + +//----------------------------------------------------------------------------- +/// @brief Internal function: Unlock the dispatcher mutex. +/// @see lockDispatcherMutex() +//----------------------------------------------------------------------------- +extern void unlockDispatcherMutex(); + +//----------------------------------------------------------------------------- +/// @brief Internal function: obtain message queue. Dispatcher mutex must be locked. +/// +/// @param name Name of the queue +/// @return Message queue +/// @see lockDispatcherMutex(), unlockDispatcherMutex() +//----------------------------------------------------------------------------- +extern MessageQueue *getMessageQueue(const char *name); + +// @} + +// @} + +} // end namespace Dispatcher + +// @} + +#endif // _DISPATCHER_H_ diff --git a/Engine/source/util/messaging/eventManager.cpp b/Engine/source/util/messaging/eventManager.cpp new file mode 100644 index 000000000..328fe2d6d --- /dev/null +++ b/Engine/source/util/messaging/eventManager.cpp @@ -0,0 +1,512 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "util/messaging/eventManager.h" + +#include "console/consoleTypes.h" +#include "console/consoleInternal.h" + +IMPLEMENT_CONOBJECT( EventManager ); + +ConsoleDocClass( EventManager, + "@brief The EventManager class is a wrapper for the standard messaging system.\n\n" + + "It provides functionality for management of event queues, events, and " + "subscriptions. Creating an EventManager is as simple as calling new EventManager " + "and specifying a queue name.\n\n" + + "@tsexample\n" + "// Create the EventManager.\n" + "$MyEventManager = new EventManager() { queue = \"MyEventManager\"; };\n\n" + "// Create an event.\n" + "$MyEventManager.registerEvent( \"SomeCoolEvent\" );\n\n" + "// Create a listener and subscribe.\n" + "$MyListener = new ScriptMsgListener() { class = MyListener; };\n" + "$MyEventManager.subscribe( $MyListener, \"SomeCoolEvent\" );\n\n" + "function MyListener::onSomeCoolEvent( %this, %data )\n" + "{\n" + " echo( \"onSomeCoolEvent Triggered\" );\n" + "}\n\n" + "// Trigger the event.\n" + "$MyEventManager.postEvent( \"SomeCoolEvent\", \"Data\" );\n" + "@endtsexample\n\n" + + "@see ScriptMsgListener\n\n" + + "@ingroup Messaging\n" +); + +Vector EventManager::smEventManagers; + +//----------------------------------------------------------------------------- +/// Gets a list of all listeners of a specific event type and executes a +/// callback on each one. +/// +/// @param event The name of the event that was triggered. +/// @param data The data associated with the event. +/// @return true to allow other listeners to receive the event, false otherwise +//----------------------------------------------------------------------------- + +// CodeReview [tom, 2/20/2007] There seemed to be a little confusion on the return value here. +// It is not a "successfully dispatched" value, it is used to prevent other +// listeners from receiving the message. Using the event manager this probably +// didn't matter since there was only one listener, however it would cause +// problems if more then one listener is registered with the queue. +bool EventManagerListener::onMessageReceived( StringTableEntry queue, const char* event, const char* data ) +{ + Vector* subscribers = mSubscribers.retreive( event ); + if( subscribers == NULL ) + return true; + + for( Vector::iterator iter = subscribers->begin(); iter != subscribers->end(); iter++ ) + { + if( iter->listener == NULL ) + { + subscribers->erase_fast( iter -- ); + continue; + } + + if (!iter->removeFlag) + { + iter->callDepth++; + Con::executef( iter->listener, iter->callback, data ); + iter->callDepth--; + if (iter->removeFlag && iter->callDepth==0) + { + subscribers->erase_fast(iter--); + } + } + } + + return true; +} + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +EventManager::EventManager() : mQueue( NULL ) +{ + addEventManager( this ); +} + +//----------------------------------------------------------------------------- +// Destructor +//----------------------------------------------------------------------------- +EventManager::~EventManager() +{ + setMessageQueue( "" ); + unregisterAllEvents(); + removeEventManager( this ); +} + +//----------------------------------------------------------------------------- +// initPersistFields +//----------------------------------------------------------------------------- +void EventManager::initPersistFields() +{ + addProtectedField( "queue", TypeString, Offset( mQueue, EventManager ), &_setMessageQueue, &defaultProtectedGetFn, "List of events currently waiting" ); +} + +//----------------------------------------------------------------------------- +/// Registers the message queue and listener with the messaging system. +/// +/// @param queue The name of the queue. Set to "" to destroy the queue. +//----------------------------------------------------------------------------- +void EventManager::setMessageQueue( const char* queue ) +{ + // If a queue is already registered, unregister it. + if( mQueue && Dispatcher::isQueueRegistered( mQueue ) ) + { + unregisterAllEvents(); + Dispatcher::unregisterMessageListener( mQueue, &mListener ); + Dispatcher::unregisterMessageQueue( mQueue ); + } + + // Register the new queue. + if( queue && *queue ) + { + Dispatcher::registerMessageQueue( queue ); + Dispatcher::registerMessageListener( queue, &mListener ); + mQueue = StringTable->insert( queue ); + } +} + +//----------------------------------------------------------------------------- +/// Determines whether or not an event is registered with the EventManager. +/// +/// @param event the event to check. +//----------------------------------------------------------------------------- +bool EventManager::isRegisteredEvent( const char* event ) +{ + // Iterate over the event list. + StringTableEntry eventName = StringTable->insert( event ); + for( Vector::const_iterator iter = mEvents.begin(); iter != mEvents.end(); iter++ ) + { + // Found. + if( *iter == eventName ) + return true; + } + + // Not found. + return false; +} + +//----------------------------------------------------------------------------- +/// Register an event with the EventManager. +/// +/// @param event The event to register. +/// @return Whether or not the event was successfully registered. +//----------------------------------------------------------------------------- +bool EventManager::registerEvent( const char* event ) +{ + // Make sure the event has not been registered yet. + if( isRegisteredEvent( event ) ) + { + Con::warnf( "EventManager::registerEvent - event %s already registered", event ); + return false; + } + + // Add to the event list. + mEvents.push_back( StringTable->insert( event ) ); + + // Create a list of subscribers for this event. + mListener.mSubscribers.insert( new Vector, event ); + + return true; +} + +//----------------------------------------------------------------------------- +/// Removes all events from the EventManager. +//----------------------------------------------------------------------------- +void EventManager::unregisterAllEvents() +{ + // Iterate over all events. + for( Vector::const_iterator iter = mEvents.begin(); iter != mEvents.end(); iter++ ) + { + // Delete the subscriber list. + Vector* subscribers = mListener.mSubscribers.remove( *iter ); + if( subscribers ) + delete subscribers; + } + + // Clear the event list. + mEvents.clear(); +} + +//----------------------------------------------------------------------------- +/// Removes an event from the EventManager. +/// +/// @param event The event to remove. +//----------------------------------------------------------------------------- +void EventManager::unregisterEvent( const char* event ) +{ + // If the event doesn't exist, we have succeeded in removing it! + if( !isRegisteredEvent( event ) ) + return; + + // Iterate over all events. + StringTableEntry eventName = StringTable->insert( event ); + for( Vector::iterator iter = mEvents.begin(); iter != mEvents.end(); iter++ ) + { + // Erase the event. + if( *iter == eventName ) + { + mEvents.erase_fast( iter ); + break; + } + } + + // Delete the subscriber list. + Vector* subscribers = mListener.mSubscribers.remove( event ); + if( subscribers ) + delete subscribers; +} + +//----------------------------------------------------------------------------- +/// Post an event to the EventManager's queue. +/// +/// @param event The event to post. +/// @param data Various data associated with the event. +/// @return Whether or not the message was dispatched successfully. +//----------------------------------------------------------------------------- +bool EventManager::postEvent( const char* event, const char* data ) +{ + AssertFatal( mQueue != NULL, "EventManager::postEvent - Queue not initialized" ); + return Dispatcher::dispatchMessage( mQueue, event, data ); +} + +//----------------------------------------------------------------------------- +/// Subscribe a listener to an event. +/// +/// @param listener The listener to subscribe. +/// @param event The event to subscribe to. +/// @param callback Optional callback name to be called when the event is +/// triggered. +/// @return Whether or not the subscription was successful. +//----------------------------------------------------------------------------- + +// CodeReview [tom, 2/20/2007] The "listener" argument was an IMessageListener, +// but it was actually used as a SimObject and never a listener. Thus, it is now a SimObject. +bool EventManager::subscribe(SimObject *callbackObj, const char* event, const char* callback /*= NULL */) +{ + // Make sure the event is valid. + if( !isRegisteredEvent( event ) ) + { + Con::warnf( "EventManager::subscribe - %s is not a registered event.", event ); + return false; + } + + // Grab the callback name. + char* cb = NULL; + if( !callback || !*callback ) + { + // Not specified, use default ( "onEvent" ). + S32 length = dStrlen( event ) + 5; + cb = new char[length]; + dSprintf( cb, length, "on%s", event ); + } + else + { + cb = new char[dStrlen(callback) + 1]; + dStrcpy(cb, callback); + } + + // Create the subscriber object. + EventManagerListener::Subscriber subscriber; + subscriber.listener = callbackObj; + subscriber.event = StringTable->insert( event ); + subscriber.callback = StringTable->insert( cb ); + subscriber.callDepth = 0; + subscriber.removeFlag = false; + + delete [] cb; + + // Grab the subscriber list. + Vector* subscribers = mListener.mSubscribers.retreive( event ); + + // If the event exists, there should always be a valid subscriber list. + AssertFatal( subscribers, "Invalid event subscriber list." ); + + // Add the subscriber. + subscribers->push_back( subscriber ); + + return true; +} + + +//----------------------------------------------------------------------------- +/// remove a listener from an event. +/// +/// @param listener The listener to remove from an event callback list. +/// @param event The event to remove the listener from. +//----------------------------------------------------------------------------- + +// CodeReview [tom, 2/20/2007] The "listener" argument was an IMessageListener, +// but it was actually used as a SimObject and never a listener. Thus, it is now a SimObject. +void EventManager::remove(SimObject *cbObj, const char* event) +{ + // If the event doesn't exist, we have succeeded in removing it! + if( !isRegisteredEvent( event ) ) + return; + + Vector* subscribers = mListener.mSubscribers.retreive( event ); + if( !subscribers ) + return; + + for( Vector::iterator iter = subscribers->begin(); iter != subscribers->end(); iter++ ) + { + // Erase the event. + if( iter->listener == cbObj ) + { + if (iter->callDepth > 0) + iter->removeFlag = true; + else + subscribers->erase_fast( iter ); + break; + } + } +} + +void EventManager::removeAll(SimObject *cbObj) +{ + // Iterate over all events. + for( Vector::const_iterator iter1 = mEvents.begin(); iter1 != mEvents.end(); iter1++ ) + { + Vector* subscribers = mListener.mSubscribers.retreive( *iter1 ); + if( !subscribers ) + continue; + for( Vector::iterator iter2 = subscribers->begin(); iter2 != subscribers->end(); iter2++ ) + { + // Erase the event. + if( iter2->listener == cbObj ) + { + if (iter2->callDepth > 0) + iter2->removeFlag = true; + else + subscribers->erase_fast( iter2 ); + break; + } + } + } + +} +//----------------------------------------------------------------------------- +/// Print all registered events to the console. +//----------------------------------------------------------------------------- +void EventManager::dumpEvents() +{ + Con::printf( "%s Events", mQueue ); + for( Vector::const_iterator iter = mEvents.begin(); iter != mEvents.end(); iter++ ) + Con::printf( " %s", *iter ); +} + +//----------------------------------------------------------------------------- +/// Print the subscribers to an event. +/// +/// @param event The event whose subscribers are to be printed. +//----------------------------------------------------------------------------- +void EventManager::dumpSubscribers( const char* event ) +{ + Vector* subscribers = mListener.mSubscribers.retreive( event ); + if( !subscribers ) + { + Con::warnf( "EventManager::dumpSubscriber - %s is not a valid event.", event ); + return; + } + + Con::printf( "%s Subscribers", event ); + for( Vector::const_iterator iter = subscribers->begin(); iter != subscribers->end(); iter++ ) + if( iter->listener ) + { + // Grab the best fit name. This should be the first found of name, class, superclass, or class type. + Namespace* ns = iter->listener->getNamespace(); + const char* name = ns ? ns->mName : getClassName() ; + Con::printf( " %s -> %s", name, iter->callback ); + } +} + +//----------------------------------------------------------------------------- +/// Print all registered events and their subscribers to the console. +//----------------------------------------------------------------------------- +void EventManager::dumpSubscribers() +{ + Con::printf( "%s Events", mQueue ); + for( Vector::const_iterator iter = mEvents.begin(); iter != mEvents.end(); iter++ ) + dumpSubscribers( *iter ); +} + +//----------------------------------------------------------------------------- +// Console Methods +//----------------------------------------------------------------------------- +ConsoleMethod( EventManager, registerEvent, bool, 3, 3, "( String event )\n" + "Register an event with the event manager.\n" + "@param event The event to register.\n" + "@return Whether or not the event was registered successfully." ) +{ + return object->registerEvent( argv[2] ); +} + +ConsoleMethod( EventManager, unregisterEvent, void, 3, 3, "( String event )\n" + "Remove an event from the EventManager.\n" + "@param event The event to remove.\n" ) +{ + object->unregisterEvent( argv[2] ); +} + +ConsoleMethod( EventManager, isRegisteredEvent, bool, 3, 3, "( String event )\n" + "Check if an event is registered or not.\n" + "@param event The event to check.\n" + "@return Whether or not the event exists." ) +{ + return object->isRegisteredEvent( argv[2] ); +} + +ConsoleMethod( EventManager, postEvent, bool, 3, 4, "( String event, String data )\n" + "~Trigger an event.\n" + "@param event The event to trigger.\n" + "@param data The data associated with the event.\n" + "@return Whether or not the event was dispatched successfully." ) +{ + if( !object->getMessageQueue() || !object->getMessageQueue()[ 0 ] ) + { + Con::errorf( "EventManager::postEvent - No queue name set on EventManager" ); + return false; + } + + return object->postEvent( argv[2], argc > 3 ? argv[3] : "" ); +} + +ConsoleMethod( EventManager, subscribe, bool, 4, 5, "( SimObject listener, String event, String callback )\n\n" + "Subscribe a listener to an event.\n" + "@param listener The listener to subscribe.\n" + "@param event The event to subscribe to.\n" + "@param callback Optional method name to receive the event notification. If this is not specified, \"on[event]\" will be used.\n" + "@return Whether or not the subscription was successful." ) +{ + // Find the listener object. + SimObject *cbObj = dynamic_cast(Sim::findObject(argv[2])); + if( cbObj == NULL ) + { + Con::warnf( "EventManager::subscribe - Invalid listener." ); + return false; + } + + return object->subscribe( cbObj, argv[3], argc > 4 ? argv[4] : NULL ); +} + +ConsoleMethod( EventManager, remove, void, 4, 4, "( SimObject listener, String event )\n\n" + "Remove a listener from an event.\n" + "@param listener The listener to remove.\n" + "@param event The event to be removed from.\n") +{ + // Find the listener object. + SimObject * listener = dynamic_cast< SimObject * >( Sim::findObject( argv[2] ) ); + if( listener ) + object->remove( listener, argv[3] ); +} + +ConsoleMethod( EventManager, removeAll, void, 3, 3, "( SimObject listener )\n\n" + "Remove a listener from all events.\n" + "@param listener The listener to remove.\n") +{ + // Find the listener object. + SimObject * listener = dynamic_cast< SimObject * >( Sim::findObject( argv[2] ) ); + if( listener ) + object->removeAll( listener ); +} + +ConsoleMethod( EventManager, dumpEvents, void, 2, 2, "()\n\n" + "Print all registered events to the console." ) +{ + object->dumpEvents(); +} + +ConsoleMethod( EventManager, dumpSubscribers, void, 2, 3, "( String event )\n\n" + "Print all subscribers to an event to the console.\n" + "@param event The event whose subscribers are to be printed. If this parameter isn't specified, all events will be dumped." ) +{ + if( argc > 2 ) + object->dumpSubscribers( argv[2] ); + else + object->dumpSubscribers(); +} diff --git a/Engine/source/util/messaging/eventManager.h b/Engine/source/util/messaging/eventManager.h new file mode 100644 index 000000000..e5ec82032 --- /dev/null +++ b/Engine/source/util/messaging/eventManager.h @@ -0,0 +1,217 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _EVENTMANAGER_H_ +#define _EVENTMANAGER_H_ + +#ifndef _CONSOLE_H_ +#include "console/console.h" +#endif + +#ifndef _DISPATCHER_H_ +#include "util/messaging/dispatcher.h" +#endif + +#ifndef _TSIMPLEHASHTABLE_H +#include "core/tSimpleHashTable.h" +#endif + + +//----------------------------------------------------------------------------- +/// Listener class used by the EventManager to dispatch messages to specific +/// callbacks. +//----------------------------------------------------------------------------- +class EventManagerListener : public Dispatcher::IMessageListener +{ + friend class EventManager; + + /// Stores subscription information for a subscriber. + struct Subscriber + { + SimObjectPtr< SimObject > listener; ///< The listener object. + StringTableEntry callback; ///< The callback to execute when the event is triggered. + StringTableEntry event; ///< The event being listened for. + U32 callDepth; + bool removeFlag; + }; + + /// Subscriber table hashed by event name. + SimpleHashTable< Vector > mSubscribers; + +public: + /// Called by the EventManager queue when an event is triggered. Calls all listeners subscribed to the triggered event. + virtual bool onMessageReceived( StringTableEntry queue, const char* event, const char* data ); + virtual bool onMessageObjectReceived( StringTableEntry queue, Message *msg ) { return true; }; +}; + +/// @addtogroup msgsys Message System +// @{ + +//----------------------------------------------------------------------------- +/// The EventManager class is a wrapper for the standard messaging system. It +/// provides functionality for management of event queues, events, and +/// subscriptions. +/// +/// Creating an EventManager is as simple as calling new EventManager +/// and specifying a queue name. +/// +/// Example Usage: +/// +/// @code +/// // Create the EventManager. +/// $MyEventManager = new EventManager() { queue = "MyEventManager"; }; +/// +/// // Create an event. +/// $MyEventManager.registerEvent( "SomeCoolEvent" ); +/// +/// // Create a listener and subscribe. +/// $MyListener = new ScriptMsgListener() { class = MyListener; }; +/// $MyEventManager.subscribe( $MyListener, "SomeCoolEvent" ); +/// +/// function MyListener::onSomeCoolEvent( %this, %data ) +/// { +/// echo( "onSomeCoolEvent Triggered" ); +/// } +/// +/// // Trigger the event. +/// $MyEventManager.postEvent( "SomeCoolEvent", "Data" ); +/// @endcode +//----------------------------------------------------------------------------- +class EventManager : public SimObject +{ + typedef SimObject Parent; + +private: + /// The name of the message queue. + StringTableEntry mQueue; + /// Registered events. + Vector mEvents; + + /// The event listener. Listens for all events and dispatches them to the appropriate subscribers. + EventManagerListener mListener; + + /// List of all EventManagers. + static Vector smEventManagers; + + /// Sets the message queue. + static bool _setMessageQueue( void *obj, const char *index, const char *data ) + { + static_cast( obj )->setMessageQueue( data ); + return false; + }; + +public: + DECLARE_CONOBJECT( EventManager ); + + EventManager(); + virtual ~EventManager(); + + static void initPersistFields(); + + /// @name Properties + /// @{ + + StringTableEntry getMessageQueue() const { return mQueue; } + + void setMessageQueue( const char* queue ); + + /// @} + + /// @name Event Management + /// @{ + + /// Checks if an event is registered. + bool isRegisteredEvent( const char* eventName ); + /// Registers an event. + bool registerEvent( const char* eventName ); + /// Removes an event. + void unregisterEvent( const char* eventName ); + /// Removes all events. + void unregisterAllEvents(); + + /// Triggers an event. + bool postEvent( const char* eventName, const char* data ); + /// Adds a subscription to an event. + bool subscribe( SimObject *callbackObj, const char* event, const char* callback = NULL ); + /// Remove a subscriber from an event. + void remove( SimObject *cbObj, const char* event ); + void removeAll( SimObject *cbObj ); + + /// @} + + /// @name Debug Output + /// @{ + + /// Prints all registered events to the console. + void dumpEvents(); + /// Prints all subscribers to the console. + void dumpSubscribers(); + /// Prints subscribers to a specific event to the console. + void dumpSubscribers( const char* event ); + + /// @} + + /// @name Event Manager Tracking + /// @{ + + /// Adds an EventManager. + static void addEventManager( EventManager* em ) { smEventManagers.push_back( em ); }; + + /// Removes an EventManager. + static void removeEventManager( EventManager* em ) + { + for( Vector::iterator iter = smEventManagers.begin(); iter != smEventManagers.end(); iter++ ) + { + if( *iter == em ) + { + smEventManagers.erase_fast( iter ); + break; + } + } + }; + + /// Retrieves an EventManager. + static EventManager* getEventManager( const char* name ) + { + StringTableEntry queue = StringTable->insert( name ); + for( Vector::iterator iter = smEventManagers.begin(); iter != smEventManagers.end(); iter++ ) + { + if( ( *iter )->mQueue == queue ) + return *iter; + } + return NULL; + }; + + /// Prints all the EventManagers to the console. + static void printEventManagers() + { + for( Vector::iterator iter = smEventManagers.begin(); iter != smEventManagers.end(); iter++ ) + ( *iter )->dumpSubscribers(); + } + + + /// @} +}; + +// @} + +#endif // _EVENTMANAGER_H_ diff --git a/Engine/source/util/messaging/message.cpp b/Engine/source/util/messaging/message.cpp new file mode 100644 index 000000000..c23c87bc6 --- /dev/null +++ b/Engine/source/util/messaging/message.cpp @@ -0,0 +1,173 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "util/messaging/message.h" + +#include "console/consoleTypes.h" +#include "core/util/safeDelete.h" +#include "core/stream/bitStream.h" +#include "console/engineAPI.h" + +//----------------------------------------------------------------------------- + +namespace Sim +{ +extern SimIdDictionary *gIdDictionary; +} + +//----------------------------------------------------------------------------- +// Constructor/Destructor +//----------------------------------------------------------------------------- + +Message::Message() +{ +} + + +IMPLEMENT_CONOBJECT(Message); + +ConsoleDocClass( Message, + "@brief Base class for messages\n\n" + + "Message is the base class for C++ defined messages, and may also be used " + "in script for script defined messages if no C++ subclass is appropriate.\n\n" + + "Messages are reference counted and will be automatically deleted when " + "their reference count reaches zero. When you dispatch a message, a " + "reference will be added before the dispatch and freed after the dispatch. " + "This allows for temporary messages with no additional code. If you want " + "to keep the message around, for example to dispatch it to multiple " + "queues, call addReference() before dispatching it and freeReference() " + "when you are done with it. Never delete a Message object directly " + "unless addReference() has not been called or the message has not been " + "dispatched.\n\n" + + "Message IDs are pooled similarly to datablocks, with the exception that " + "IDs are reused. If you keep a message for longer than a single dispatch, " + "then you should ensure that you clear any script variables that refer " + "to it after the last freeReference(). If you don't, then it is probable " + "that the object ID will become valid again in the future and could cause " + "hard to track down bugs.\n\n" + + "Messages have a unique type to simplify message handling code. For object " + "messages, the type is defined as either the script defined class name " + "or the C++ class name if no script class was defined. The message type " + "may be obtained through the getType() method.\n\n" + + "By convention, any data for the message is held in script accessible " + "fields. Messages that need to be handled in C++ as well as script " + "provide the relevant data through persistent fields in a subclass of " + "Message to provide best performance on the C++ side. Script defined " + "messages usually their through dynamic fields, and may be accessed in " + "C++ using the SimObject::getDataField() method.\n\n" + + "@ingroup Messaging\n" +); + +//----------------------------------------------------------------------------- +IMPLEMENT_CALLBACK(Message, onAdd, void, (),(), + "Script callback when a message is first created and registered.\n\n" + "@tsexample\n" + "function Message::onAdd(%this)\n" + "{\n" + " // Perform on add code here\n" + "}\n" + "@endtsexample\n\n" + ); + +bool Message::onAdd() +{ + if(! Parent::onAdd()) + return false; + + linkNamespaces(); + onAdd_callback(); + //Con::executef(this, "onAdd"); + return true; +} + +IMPLEMENT_CALLBACK(Message, onRemove, void, (),(), + "Script callback when a message is deleted.\n\n" + "@tsexample\n" + "function Message::onRemove(%this)\n" + "{\n" + " // Perform on remove code here\n" + "}\n" + "@endtsexample\n\n" + ); + +void Message::onRemove() +{ + onRemove_callback(); + //Con::executef(this, "onRemove"); + unlinkNamespaces(); + + Parent::onRemove(); +} + +//----------------------------------------------------------------------------- +// Public Methods +//----------------------------------------------------------------------------- + +SimObjectId Message::getNextMessageID() +{ + for(S32 i = MessageObjectIdFirst;i < MessageObjectIdLast;i++) + { + if(Sim::gIdDictionary->find(i) == NULL) + return i; + } + + // Oh no ... + return 0xffffffff; +} + +//----------------------------------------------------------------------------- + +const char *Message::getType() +{ + if(mClassName && mClassName[0] != 0) + return mClassName; + + return getClassName(); +} + +//----------------------------------------------------------------------------- +// Console Methods +//----------------------------------------------------------------------------- + +ConsoleMethod(Message, getType, const char *, 2, 2, "() Get message type (script class name or C++ class name if no script defined class)") +{ + return object->getType(); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod(Message, addReference, void, 2, 2, "() Increment the reference count for this message") +{ + object->addReference(); +} + +ConsoleMethod(Message, freeReference, void, 2, 2, "() Decrement the reference count for this message") +{ + object->freeReference(); +} diff --git a/Engine/source/util/messaging/message.h b/Engine/source/util/messaging/message.h new file mode 100644 index 000000000..3ccbf4744 --- /dev/null +++ b/Engine/source/util/messaging/message.h @@ -0,0 +1,145 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MESSAGE_H_ +#define _MESSAGE_H_ + +#ifndef _SIMBASE_H_ +#include "console/simBase.h" +#endif + +#ifndef _NETCONNECTION_H_ +#include "sim/netConnection.h" +#endif + +// Forward Refs +class MessageQueue; + +/// @addtogroup msgsys Message System +/// +/// Most of the message system docs are currently just stubs and will +/// be fleshed out soon. +/// +// @{ + +//----------------------------------------------------------------------------- +// Message Base Class +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +/// @brief Base class for messages +/// +/// Message is the base class for C++ defined messages, and may also be used +/// in script for script defined messages if no C++ subclass is appropriate. +/// +/// Messages are reference counted and will be automatically deleted when +/// their reference count reaches zero. When you dispatch a message, a +/// reference will be added before the dispatch and freed after the dispatch. +/// This allows for temporary messages with no additional code. If you want +/// to keep the message around, for example to dispatch it to multiple +/// queues, call addReference() before dispatching it and freeReference() +/// when you are done with it. Never delete a Message object directly +/// unless addReference() has not been called or the message has not been +/// dispatched. +/// +/// Message IDs are pooled similarly to datablocks, with the exception that +/// IDs are reused. If you keep a message for longer than a single dispatch, +/// then you should ensure that you clear any script variables that refer +/// to it after the last freeReference(). If you don't, then it is probable +/// that the object ID will become valid again in the future and could cause +/// hard to track down bugs. +/// +/// Messages have a unique type to simplify message handling code. For object +/// messages, the type is defined as either the script defined class name +/// or the C++ class name if no script class was defined. The message type +/// may be obtained through the getType() method. +/// +/// By convention, any data for the message is held in script accessible +/// fields. Messages that need to be handled in C++ as well as script +/// provide the relevant data through persistent fields in a subclass of +/// Message to provide best performance on the C++ side. Script defined +/// messages usually their through dynamic fields, and may be accessed in +/// C++ using the SimObject::getDataField() method. +//----------------------------------------------------------------------------- +class Message : public SimObject +{ + typedef SimObject Parent; + +public: + Message(); + DECLARE_CONOBJECT(Message); + DECLARE_CALLBACK( void, onAdd, () ); + DECLARE_CALLBACK( void, onRemove, () ); + + //----------------------------------------------------------------------------- + /// @brief Obtain next available #SimObjectId for messages + /// + /// This is used in combination with the newmsg script operator to provide + /// ID pooling for messages and works similarly to datablock IDs. + /// + /// By default, the 64 IDs following the datablock IDs are used for messages. + /// As message objects generally have a short life time this prevents them + /// from eating object IDs as if they haven't eaten for a year. + /// + /// Note that unlike SimObjects and datablocks, Messages IDs are re-used. + /// If you store a message object in script and do not clear the variable + /// containing the object ID after freeing the message, it is probable that + /// the object ID will become valid again. + /// + /// @return Next available SimObjectId + //----------------------------------------------------------------------------- + static SimObjectId getNextMessageID(); + + virtual bool onAdd(); + virtual void onRemove(); + + //----------------------------------------------------------------------------- + /// @brief Get the type of the message + /// + /// The message type is either the script class name or the C++ class name + /// if it has not been overridden in script. This allows easy identification + /// of message types with minimum effort. + /// + /// @return Type of message + //----------------------------------------------------------------------------- + const char *getType(); + + //----------------------------------------------------------------------------- + /// @brief Add a reference to the reference count of this message + /// + /// Use freeReference() to free the reference when you are done with it. + /// + /// @see freeReference() + //----------------------------------------------------------------------------- + void addReference() { incRefCount(); } + + //----------------------------------------------------------------------------- + /// @brief Free a reference to this message + /// + /// @see addReference() + //----------------------------------------------------------------------------- + void freeReference() { decRefCount(); } +}; + +// @} + +#endif // _MESSAGE_H_ diff --git a/Engine/source/util/messaging/messageForwarder.cpp b/Engine/source/util/messaging/messageForwarder.cpp new file mode 100644 index 000000000..0b4bf8fbd --- /dev/null +++ b/Engine/source/util/messaging/messageForwarder.cpp @@ -0,0 +1,88 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "util/messaging/messageForwarder.h" + +#include "console/consoleTypes.h" + +//----------------------------------------------------------------------------- +// Constructor/Destructor +//----------------------------------------------------------------------------- + +MessageForwarder::MessageForwarder() +{ + mToQueue = ""; +} + +MessageForwarder::~MessageForwarder() +{ +} + +IMPLEMENT_CONOBJECT(MessageForwarder); + +ConsoleDocClass( MessageForwarder, + "@brief Forward messages from one queue to another\n\n" + + "MessageForwarder is a script class that can be used to forward messages " + "from one queue to another.\n\n" + + "@tsexample\n" + "%fwd = new MessageForwarder()\n" + "{\n" + " toQueue = \"QueueToSendTo\";\n" + "};\n\n" + "registerMessageListener(\"FromQueue\", %fwd);\n" + "@endtsexample\n\n" + + "Where \"QueueToSendTo\" is the queue you want to forward to, and " + "\"FromQueue\" is the queue you want to forward from.\n\n" + + "@ingroup Messaging\n" +); + +//----------------------------------------------------------------------------- + +void MessageForwarder::initPersistFields() +{ + addField("toQueue", TypeCaseString, Offset(mToQueue, MessageForwarder), "Name of queue to forward to"); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- +// Public Methods +//----------------------------------------------------------------------------- + +bool MessageForwarder::onMessageReceived(StringTableEntry queue, const char *event, const char *data) +{ + if(*mToQueue) + Dispatcher::dispatchMessage(queue, event, data); + return Parent::onMessageReceived(queue, event, data); +} + +bool MessageForwarder::onMessageObjectReceived(StringTableEntry queue, Message *msg) +{ + if(*mToQueue) + Dispatcher::dispatchMessageObject(mToQueue, msg); + return Parent::onMessageObjectReceived(queue, msg); +} diff --git a/Engine/source/util/messaging/messageForwarder.h b/Engine/source/util/messaging/messageForwarder.h new file mode 100644 index 000000000..3fc668ccb --- /dev/null +++ b/Engine/source/util/messaging/messageForwarder.h @@ -0,0 +1,74 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/simBase.h" +#include "util/messaging/dispatcher.h" +#include "util/messaging/scriptMsgListener.h" + +#ifndef _MESSAGEFORWARDER_H_ +#define _MESSAGEFORWARDER_H_ + +/// @addtogroup msgsys Message System +// @{ + +//----------------------------------------------------------------------------- +/// @brief Forward messages from one queue to another +/// +/// MessageForwarder is a script class that can be used to forward messages +/// from one queue to another. +/// +///

      Example

      +/// +/// @code +/// %fwd = new MessageForwarder() +/// { +/// toQueue = "QueueToSendTo"; +/// }; +/// +/// registerMessageListener("FromQueue", %fwd); +/// @endcode +/// +/// Where "QueueToSendTo" is the queue you want to forward to, and +/// "FromQueue" is the queue you want to forward from. +/// +//----------------------------------------------------------------------------- +class MessageForwarder : public ScriptMsgListener +{ + typedef ScriptMsgListener Parent; + +protected: + StringTableEntry mToQueue; + +public: + MessageForwarder(); + virtual ~MessageForwarder(); + DECLARE_CONOBJECT(MessageForwarder); + + static void initPersistFields(); + + virtual bool onMessageReceived(StringTableEntry queue, const char *event, const char *data); + virtual bool onMessageObjectReceived(StringTableEntry queue, Message *msg); +}; + +// @} + +#endif // _MESSAGEFORWARDER_H_ diff --git a/Engine/source/util/messaging/scriptMsgListener.cpp b/Engine/source/util/messaging/scriptMsgListener.cpp new file mode 100644 index 000000000..0bfa04bf3 --- /dev/null +++ b/Engine/source/util/messaging/scriptMsgListener.cpp @@ -0,0 +1,184 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "util/messaging/scriptMsgListener.h" + +#include "console/consoleTypes.h" + +#include "console/engineAPI.h" + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- + +ScriptMsgListener::ScriptMsgListener() +{ +} + +IMPLEMENT_CONOBJECT(ScriptMsgListener); + +ConsoleDoc("@class ScriptMsgListener\n" + "@brief Script accessible version of Dispatcher::IMessageListener. Often used in conjunction with EventManager\n\n" + + "The main use of ScriptMsgListener is to allow script to listen for" + "messages. You can subclass ScriptMsgListener in script to receive" + "the Dispatcher::IMessageListener callbacks.\n\n" + + "Alternatively, you can derive from it in C++ instead of SimObject to" + "get an object that implements Dispatcher::IMessageListener with script" + "callbacks. If you need to derive from something other then SimObject," + "then you will need to implement the Dispatcher::IMessageListener" + "interface yourself.\n\n" + + "@tsexample\n" + "// Create the EventManager.\n" + "$MyEventManager = new EventManager() { queue = \"MyEventManager\"; };\n\n" + "// Create an event.\n" + "$MyEventManager.registerEvent( \"SomeCoolEvent\" );\n\n" + "// Create a listener and subscribe.\n" + "$MyListener = new ScriptMsgListener() { class = MyListener; };\n" + "$MyEventManager.subscribe( $MyListener, \"SomeCoolEvent\" );\n\n" + "function MyListener::onSomeCoolEvent( %this, %data )\n" + "{\n" + " echo( \"onSomeCoolEvent Triggered\" );\n" + "}\n\n" + "// Trigger the event.\n" + "$MyEventManager.postEvent( \"SomeCoolEvent\", \"Data\" );\n" + "@endtsexample\n\n" + + "@ingroup Messaging\n" +); + +//----------------------------------------------------------------------------- +IMPLEMENT_CALLBACK(ScriptMsgListener, onAdd, void, (),(), + "Script callback when a listener is first created and registered.\n\n" + "@tsexample\n" + "function ScriptMsgListener::onAdd(%this)\n" + "{\n" + " // Perform on add code here\n" + "}\n" + "@endtsexample\n\n" + ); + +bool ScriptMsgListener::onAdd() +{ + if(! Parent::onAdd()) + return false; + + linkNamespaces(); + onAdd_callback(); + //Con::executef(this, "onAdd"); + return true; +} + +IMPLEMENT_CALLBACK(ScriptMsgListener, onRemove, void, (),(), + "Script callback when a listener is deleted.\n\n" + "@tsexample\n" + "function ScriptMsgListener::onRemove(%this)\n" + "{\n" + " // Perform on remove code here\n" + "}\n" + "@endtsexample\n\n" + ); + +void ScriptMsgListener::onRemove() +{ + onRemove_callback(); + //Con::executef(this, "onRemove"); + unlinkNamespaces(); + + Parent::onRemove(); +} + +//----------------------------------------------------------------------------- +// Public Methods +//----------------------------------------------------------------------------- +IMPLEMENT_CALLBACK( ScriptMsgListener, onMessageReceived, bool, ( const char* queue, const char* event, const char* data ), ( queue, event, data ), + "Called when the listener has received a message.\n" + "@param queue The name of the queue the message was dispatched to\n" + "@param event The name of the event (function) that was triggered\n" + "@param data The data (parameters) for the message\n\n" + "@return false to prevent other listeners receiving this message, true otherwise\n" ); + +bool ScriptMsgListener::onMessageReceived(StringTableEntry queue, const char* event, const char* data) +{ + return onMessageReceived_callback(queue, event, data); + //return dAtob(Con::executef(this, "onMessageReceived", queue, event, data)); +} + +IMPLEMENT_CALLBACK( ScriptMsgListener, onMessageObjectReceived, bool, ( const char* queue, Message *msg ), ( queue, msg ), + "Called when a message object (not just the message data) is passed to a listener.\n" + "@param queue The name of the queue the message was dispatched to\n" + "@param msg The message object\n" + "@return false to prevent other listeners receiving this message, true otherwise\n" + "@see Message\n" + "@see onMessageReceived"); + +bool ScriptMsgListener::onMessageObjectReceived(StringTableEntry queue, Message *msg) +{ + return onMessageObjectReceived_callback(queue, msg); + //return dAtob(Con::executef(this, "onMessageObjectReceived", queue, Con::getIntArg(msg->getId()))); +} + +//----------------------------------------------------------------------------- +IMPLEMENT_CALLBACK( ScriptMsgListener, onAddToQueue, void, ( const char* queue), ( queue), + "@brief Callback for when the listener is added to a queue\n\n" + "The default implementation of onAddToQueue() and onRemoveFromQueue() " + "provide tracking of the queues this listener is added to through the " + "mQueues member. Overrides of onAddToQueue() or onRemoveFromQueue() " + "should ensure they call the parent implementation in any overrides.\n" + "@param queue The name of the queue that the listener added to\n" + "@see onRemoveFromQueue()"); + +void ScriptMsgListener::onAddToQueue(StringTableEntry queue) +{ + //Con::executef(this, "onAddToQueue", queue); + onAddToQueue_callback(queue); + IMLParent::onAddToQueue(queue); +} + +/// @brief Callback for when the listener is removed from a queue + /// + /// The default implementation of onAddToQueue() and onRemoveFromQueue() + /// provide tracking of the queues this listener is added to through the + /// #mQueues member. Overrides of onAddToQueue() or onRemoveFromQueue() + /// should ensure they call the parent implementation in any overrides. + /// + /// @param queue The name of the queue the listener was removed from + /// @see onAddToQueue() + //----------------------------------------------------------------------------- +IMPLEMENT_CALLBACK( ScriptMsgListener, onRemoveFromQueue, void, ( const char* queue), ( queue), + "@brief Callback for when the listener is removed from a queue\n\n" + "The default implementation of onAddToQueue() and onRemoveFromQueue() " + "provide tracking of the queues this listener is added to through the " + "mQueues member. Overrides of onAddToQueue() or onRemoveFromQueue() " + "should ensure they call the parent implementation in any overrides.\n" + "@param queue The name of the queue that the listener was removed from\n" + "@see onAddToQueue()"); + +void ScriptMsgListener::onRemoveFromQueue(StringTableEntry queue) +{ + //Con::executef(this, "onRemoveFromQueue", queue); + onRemoveFromQueue_callback(queue); + IMLParent::onRemoveFromQueue(queue); +} diff --git a/Engine/source/util/messaging/scriptMsgListener.h b/Engine/source/util/messaging/scriptMsgListener.h new file mode 100644 index 000000000..ab26dd171 --- /dev/null +++ b/Engine/source/util/messaging/scriptMsgListener.h @@ -0,0 +1,83 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/simBase.h" + +#ifndef _SCRIPTMSGLISTENER_H_ +#define _SCRIPTMSGLISTENER_H_ + +#ifndef _DISPATCHER_H_ +#include "util/messaging/dispatcher.h" +#endif + +/// @addtogroup msgsys Message System +// @{ + +//----------------------------------------------------------------------------- +/// @brief Script accessible version of Dispatcher::IMessageListener +/// +/// The main use of ScriptMsgListener is to allow script to listen for +/// messages. You can subclass ScriptMsgListener in script to receive +/// the Dispatcher::IMessageListener callbacks. +/// +/// Alternatively, you can derive from it in C++ instead of SimObject to +/// get an object that implements Dispatcher::IMessageListener with script +/// callbacks. If you need to derive from something other then SimObject, +/// then you will need to implement the Dispatcher::IMessageListener +/// interface yourself. +//----------------------------------------------------------------------------- +class ScriptMsgListener : public SimObject, public virtual Dispatcher::IMessageListener +{ + typedef SimObject Parent; + typedef Dispatcher::IMessageListener IMLParent; + +public: + ScriptMsgListener(); + + DECLARE_CONOBJECT(ScriptMsgListener); + + DECLARE_CALLBACK( void, onAdd, () ); + DECLARE_CALLBACK( void, onRemove, () ); + + DECLARE_CALLBACK( bool, onMessageReceived, ( const char* queue, const char* event, const char* data ) ); + DECLARE_CALLBACK( bool, onMessageObjectReceived, ( const char* queue, Message *msg ) ); + + DECLARE_CALLBACK( void, onAddToQueue, ( const char* queue ) ); + DECLARE_CALLBACK( void, onRemoveFromQueue, ( const char* queue ) ); + + /////////////////////////////////////////////////////////////////////// + + virtual bool onAdd(); + virtual void onRemove(); + + /////////////////////////////////////////////////////////////////////// + + virtual bool onMessageReceived(StringTableEntry queue, const char* event, const char* data); + virtual bool onMessageObjectReceived(StringTableEntry queue, Message *msg); + + virtual void onAddToQueue(StringTableEntry queue); + virtual void onRemoveFromQueue(StringTableEntry queue); +}; + +// @} + +#endif // _SCRIPTMSGLISTENER_H_ diff --git a/Engine/source/util/noise2d.cpp b/Engine/source/util/noise2d.cpp new file mode 100644 index 000000000..cee3b6fb6 --- /dev/null +++ b/Engine/source/util/noise2d.cpp @@ -0,0 +1,518 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "util/noise2d.h" +#include "core/util/tVector.h" + +//-------------------------------------- +Noise2D::Noise2D() +{ + mSeed = 0; +} + +Noise2D::~Noise2D() +{ +} + + +//-------------------------------------- +void Noise2D::normalize(F32 v[2]) +{ + F32 s; + + s = mSqrt(v[0] * v[0] + v[1] * v[1]); + v[0] = v[0] / s; + v[1] = v[1] / s; +} + + +//-------------------------------------- +void Noise2D::setSeed(U32 seed) +{ + if (mSeed == seed) + return; + mSeed = seed; + mRandom.setSeed(mSeed); + + S32 i, j, k; + + for (i = 0 ; i < SIZE ; i++) { + mPermutation[i] = i; + + for (j = 0 ; j < 2 ; j++) + mGradient[i][j] = mRandom.randF( -1.0f, 1.0f ); + normalize(mGradient[i]); + } + + while (--i) { + k = mPermutation[i]; + j = mRandom.randI(0, SIZE-1); + mPermutation[i] = mPermutation[j]; + mPermutation[j] = k; + } + + // extend the size of the arrays x2 to get rid of a bunch of MODs + // we'd have to do later in the code + for (i = 0 ; i < SIZE + 2 ; i++) { + mPermutation[SIZE + i] = mPermutation[i]; + for (j = 0 ; j < 2 ; j++) + mGradient[SIZE + i][j] = mGradient[i][j]; + } +} + + +//-------------------------------------- +U32 Noise2D::getSeed() +{ + return mSeed; +} + + +inline F32 Noise2D::lerp(F32 t, F32 a, F32 b) +{ + return a + t * (b - a); +} + + +inline F32 Noise2D::curve(F32 t) +{ + return t * t * (3.0f - 2.0f * t); +} + + +inline F32 clamp(F32 f, F32 m) +{ + while (f > m) + f -= m; + while (f < 0.0f) + f += m; + return f; +} + + +//-------------------------------------- +void Noise2D::fBm( Vector *dst, U32 size, U32 interval, F32 h, F32 octaves ) +{ + interval = getMin(U32(128), getMax(U32(1), interval)); + F32 H = getMin(1.0f, getMax(0.0f, h)); + octaves = getMin(5.0f, getMax(1.0f, octaves)); + F32 lacunarity = 2.0f; + + F32 exponent_array[32]; + + U32 shift = getBinLog2( size ); + + // precompute and store spectral weights + // seize required memory for exponent_array + F32 frequency = 1.0; + for (U32 i=0; i<=octaves; i++) + { + // compute weight for each frequency + exponent_array[i] = mPow( frequency, -H ); + frequency *= lacunarity; + } + + // initialize dst + for (S32 k=0; k < (size*size); k++) + (*dst)[k] = 0.0f; + + F32 scale = 1.0f / (F32)size * interval; + for (S32 o=0; o *dst, Vector *sig, U32 size, U32 interval, F32 h, F32 octaves) +{ + interval = getMin(U32(128), getMax(U32(1), interval)); + F32 H = getMin(1.0f, getMax(0.0f, h)); + octaves = getMin(5.0f, getMax(1.0f, octaves)); + F32 lacunarity = 2.0f; + F32 offset = 1.0f; + F32 gain = 2.0f; + + U32 shift = getBinLog2( size ); + + F32 exponent_array[32]; + + // precompute and store spectral weights + // seize required memory for exponent_array + F32 frequency = 1.0; + for (U32 i=0; i<=octaves; i++) + { + // compute weight for each frequency + exponent_array[i] = mPow( frequency, -H ); + frequency *= lacunarity; + } + + F32 scale = 1.0f / (F32)size * interval; + + //-------------------------------------- + // compute first octave + for (S32 y=0; y *src, Vector *dst, U32 iterations, U32 size ) +{ + // early out if there is nothing to do + if (iterations == 0 ) + { + *dst = *src; + return true; + } + + F32 fmin, fmax; + getMinMax( src, &fmin, &fmax, size); + + U32 shift = getBinLog2( size ); + U32 mask = size - 1; + + +// currently using SCRATCH_3 for debugging -- Rick + Vector scratch = *src; + U32 *o = (U32*)scratch.address(); + Vector a = *src; + Vector b = *src; + Vector c = *src; + + for (S32 k=0; k < (size*size); k++) + c[k] = 0.0f; + + for (int i=0; i maxDelta) + { + maxDelta = delta; + o[srcOffset] = adjOffset; + } + } + } + } + } + } + for (S32 j=0; j < (size*size); j++) + { + F32 &s = a[j]; + F32 &d = b[ o[j] ]; + F32 delta = s - d; + if (delta > 0.0f) + { + F32 alt = (s-fmin) / (fmax-fmin); + F32 amt = delta * (0.1f * (1.0f-alt)); + s -= amt; + d += amt; + } + } +// debug only + for (S32 k=0; k < (size*size); k++) + c[k] += b[k] - a[k]; + + Vector tmp = a; + a = b; + b = tmp; + } + *dst = b; + //*dst = *c; + + return true; +} + + + +bool Noise2D::erodeThermal(Vector *src, Vector *dst, F32 slope, F32 materialLoss, U32 iterations, U32 size, U32 squareSize, F32 maxHeight ) +{ + // early out if there is nothing to do + if (iterations == 0 ) + { + *dst = *src; + return true; + } + + + F32 fmin, fmax; + getMinMax(src, &fmin, &fmax, size); + + Vector a = *src; + // Heightfield *b = getScratch(1); + Vector r; + r.setSize( size * size ); + //dMemset( r.address(), 0, r.memSize() ); + + F32 conservation = 1.0f - mClampF(materialLoss, 0.0f, 100.0f)/100.0f; + slope = mClampF(conservation, 0.0f, 89.0f); // clamp to 0-89 degrees + + F32 talusConst = mTan(mDegToRad(slope)) * squareSize; // in world units + talusConst = talusConst * (fmax-fmin) / maxHeight; // scale to current height units + F32 p = 0.1f; + + U32 mask = size - 1; + U32 shift = getBinLog2( size ); + + for (U32 i=0; i talusConst) + { + F32 rubble = p * (delta - talusConst); + r[adjOffset] -= rubble; + *dstHeight += rubble * conservation; + } + } + } + } + } + } + for (S32 k=0; k < (size*size); k++) + a[k] += r[k]; + } + *dst = a; + return true; +} + +void Noise2D::getMinMax( Vector *src, F32 *fmin, F32 *fmax, U32 size ) +{ + if (!src) + return; + + F32 *p = (*src).address(); + *fmin = *p; + *fmax = *p; + for (S32 i=0; i < (size*size); i++, p++) + { + if (*fmin > *p) *fmin = *p; + if (*fmax < *p) *fmax = *p; + } +} + +//-------------------------------------- +F32 Noise2D::turbulence(F32 x, F32 y, F32 freq) +{ + F32 t, x2, y2; + + for ( t = 0.0f ; freq >= 3.0f ; freq /= 2.0f) + { + x2 = freq * x; + y2 = freq * y; + t += mFabs(getValue(x2, y2, (S32)freq)) / freq; + } + return t; +} + + +//-------------------------------------- +inline void Noise2D::setup(F32 t, S32 &b0, S32 &b1, F32 &r0, F32 &r1) +{ + // find the bounding integers of u + b0 = S32(t) & SIZE_MASK; + b1 = (b0+1) & SIZE_MASK; + + // seperate the fractional components + r0 = t - (S32)t; + r1 = r0 - 1.0f; +} + +inline F32 Noise2D::dot(const F32 *q, F32 rx, F32 ry) +{ + return (rx * q[0] + ry * q[1] ); +} + + + +//-------------------------------------- +F32 Noise2D::getValue(F32 x, F32 y, S32 interval) +{ + S32 bx0, bx1, by0, by1; + F32 rx0, rx1, ry0, ry1; + + // Imagine having a square of the type + // p0---p1 Where p0 = (bx0, by0) +----> U + // |(u,v)| p1 = (bx1, by0) | + // | | p2 = (bx0, by1) | Coordinate System + // p2---p3 p3 = (bx1, by1) V + // The u, v point in 2D texture space is bounded by this rectangle. + + // Goal, determine the scalar at the points p0, p1, p2, p3. + // Then the scalar of the point (u, v) will be found by linear interpolation. + + // First step: Get the 2D coordinates of the points p0, p1, p2, p3. + // We also need vectors pointing from each point in the square above and + // ending at the (u,v) coordinate located inside the square. + // The vector (rx0, ry0) goes from P0 to the (u,v) coordinate. + // The vector (rx1, ry0) goes from P1 to the (u,v) coordinate. + // The vector (rx0, ry1) goes from P2 to the (u,v) coordinate. + // The vector (rx1, ry1) goes from P3 to the (u,v) coordinate. + + setup(x, bx0, bx1, rx0, rx1); + setup(y, by0, by1, ry0, ry1); + + // Make sure the box corners fall within the interval + // so that the final output will wrap on itself + bx0 = bx0 % interval; + bx1 = bx1 % interval; + by0 = by0 % interval; + by1 = by1 % interval; + + S32 i = mPermutation[ bx0 ]; + S32 j = mPermutation[ bx1 ]; + + S32 b00 = mPermutation[ i + by0 ]; + S32 b10 = mPermutation[ j + by0 ]; + S32 b01 = mPermutation[ i + by1 ]; + S32 b11 = mPermutation[ j + by1 ]; + + // Next, calculate the dropoff component about the point p0. + F32 sx = curve(rx0); + F32 sy = curve(ry0); + + // Now, for each point in the square shown above, calculate the dot + // product of the gradiant vector and the vector going from each square + // corner point to the (u,v) point inside the square. + F32 u = dot(mGradient[ b00 ], rx0,ry0); + F32 v = dot(mGradient[ b10 ], rx1,ry0); + + // Interpolation along the X axis. + F32 a = lerp(sx, u, v); + + u = dot(mGradient[ b01 ], rx0,ry1); + v = dot(mGradient[ b11 ], rx1,ry1); + + // Interpolation along the Y axis. + F32 b = lerp(sx, u, v); + + // Final Interpolation + return lerp(sy, a, b); +} + + diff --git a/Engine/source/util/noise2d.h b/Engine/source/util/noise2d.h new file mode 100644 index 000000000..571f03173 --- /dev/null +++ b/Engine/source/util/noise2d.h @@ -0,0 +1,95 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _NOISE2D_H_ +#define _NOISE2D_H_ + + +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#ifndef _MMATH_H_ +#include "math/mMath.h" +#endif +#ifndef _MRANDOM_H_ +#include "math/mRandom.h" +#endif + +class Noise2D +{ +private: + enum Constants { + SIZE = 0x100, + SIZE_MASK = 0x0ff + }; + S32 mPermutation[SIZE + SIZE + 2]; + F32 mGradient[SIZE + SIZE + 2][2]; + + U32 mSeed; + + MRandom mRandom; + + F32 lerp(F32 t, F32 a, F32 b); + F32 curve(F32 t); + void setup(F32 t, S32 &b0, S32 &b1, F32 &r0, F32 &r1); + F32 dot(const F32 *q, F32 rx, F32 ry); + void normalize(F32 v[2]); + +public: + Noise2D(); + ~Noise2D(); + + void setSeed(U32 seed); + U32 getSeed(); + + F32 getValue(F32 u, F32 v, S32 interval); + + /// @name Noise + /// These functions actually generate + /// noise values into the passed in destination + /// array. + /// + /// Note that the output values of these functions + /// are from -1.0 to 1.0. + /// + /// fBm - Fractal Brownian Motion - A simple noise generation + /// algorithm, it tends to generate either flowing rounded + /// hills or rounded mountainous shapes. + /// @{ + void fBm( Vector *dst, U32 size, U32 interval, F32 h, F32 octave=5.0f); + + /// rigidMultiFractal + /// Creates ridged mountains with a high frequency detail. + void rigidMultiFractal( Vector *dst, Vector *signal, U32 size, U32 interval, F32 h, F32 octave=5.0f); + /// @} + + bool erodeHydraulic(Vector *src, Vector *dst, U32 iterations, U32 size ); + bool erodeThermal(Vector *src, Vector *dst, F32 slope, F32 materialLoss, U32 iterations, U32 size, U32 squareSize, F32 maxHeight ); + + F32 turbulence(F32 x, F32 y, F32 freq); + + void getMinMax( Vector *src, F32 *maxNoise, F32 *minNoise, U32 size ); +}; + + + +#endif // _NOISE2D_H_ diff --git a/Engine/source/util/quadTreeTracer.cpp b/Engine/source/util/quadTreeTracer.cpp new file mode 100644 index 000000000..ee189dbf6 --- /dev/null +++ b/Engine/source/util/quadTreeTracer.cpp @@ -0,0 +1,242 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "util/quadTreeTracer.h" +#include "platform/profiler.h" +#include "core/frameAllocator.h" + +static F32 calcInterceptV(F32 vStart, F32 invDeltaV, F32 intercept) +{ + return (intercept - vStart) * invDeltaV; +} + +static F32 calcInterceptNone(F32, F32, F32) +{ + return F32_MAX; +} + +static F32 (*calcInterceptX)(F32, F32, F32); +static F32 (*calcInterceptY)(F32, F32, F32); + +bool QuadTreeTracer::castRay(const Point3F &start, const Point3F &end, RayInfo *info) +{ + PROFILE_START(QuadTreeTracer_castRay); + + // Do some precalculations we'll use for the rest of this routine. + // Set up our intercept calculation methods. + F32 invDeltaX; + if(end.x == start.x) + { + calcInterceptX = calcInterceptNone; + invDeltaX = 0; + } + else + { + invDeltaX = 1.f / (end.x - start.x); + calcInterceptX = calcInterceptV; + } + + F32 invDeltaY; + if(end.y == start.y) + { + calcInterceptY = calcInterceptNone; + invDeltaY = 0; + } + else + { + invDeltaY = 1.f / (end.y - start.y); + calcInterceptY = calcInterceptV; + } + + // Subdivide our space based on the size of the lowest level of the tree... + const F32 invSize = 1.f / F32(BIT(mTreeDepth-1)); + + // Grab this off the frame allocator, we don't want to do a proper alloc + // on every ray! + FrameAllocatorMarker stackAlloc; + RayStackNode *stack = (RayStackNode*)stackAlloc.alloc(sizeof(RayStackNode) * (mTreeDepth * 3 + 1)); + + U32 stackSize = 1; + + // Kick off the stack with the root node. + stack[0].startT = 0; + stack[0].endT = 1; + stack[0].squarePos.set(0,0); + stack[0].level = mTreeDepth - 1; + + //Con::printf("QuadTreeTracer::castRay(%x)", this); + + // Aright, now let's do some raycasting! + while(stackSize--) + { + // Get the current node for easy access... + RayStackNode *sn = stack + stackSize; + + const U32 level = sn->level; + const F32 startT = sn->startT; + const F32 endT = sn->endT; + const Point2I squarePos = sn->squarePos; + + AssertFatal((startT >= 0.f) && (startT <= 1.f), "QuadTreeTracer::castRay - out of range startT on stack!"); + AssertFatal((endT >= 0.f) && (endT <= 1.f), "QuadTreeTracer::castRay - out of range endT on stack!"); + + //Con::printf(" -- node(%d, %d @ %d), sT=%f, eT=%f", squarePos.x, squarePos.y, level, startT, endT); + + // Figure our start and end Z. + const F32 startZ = startT * (end.z - start.z) + start.z; + const F32 endZ = endT * (end.z - start.z) + start.z; + + // Ok, now let's see if we hit the lower bound + const F32 squareMin = getSquareMin(level, squarePos); + + if(startZ < squareMin && endZ < squareMin) + continue; //Nope, skip out. + + // Hmm, let's check the upper bound. + const F32 squareMax = getSquareMax(level, squarePos); + + if(startZ > squareMax && endZ > squareMax) + continue; //Nope, skip out. + + // We might be intersecting something... If we've hit + // the tree depth let's deal with the leaf intersection. + if(level == 0) + { + //Con::printf(" ++ check node(%d, %d @ %d), sT=%f, eT=%f", squarePos.x, squarePos.y, level, startT, endT); + + if(castLeafRay(squarePos, start, end, startT, endT, info)) + { + PROFILE_END(); + return true; // We hit, tell 'em so! + } + continue; // Otherwise, keep looking. + } + else + { + // Ok, we have to push our children as we're an inner node. + + // First, figure out some widths... + U32 subSqSize = BIT(level - 1); + + // Now, calculate intercepts so we know how to deal with this + // situation... (intercept = position, int = t value for that pos) + + const F32 xIntercept = (squarePos.x + subSqSize) * invSize; + F32 xInt = calcInterceptX(start.x, invDeltaX, xIntercept); + + const F32 yIntercept = (squarePos.y + subSqSize) * invSize; + F32 yInt = calcInterceptY(start.y, invDeltaY, yIntercept); + + // Our starting position for this subray... + const F32 startX = startT * (end.x - start.x) + start.x; + const F32 startY = startT * (end.y - start.y) + start.y; + + // Deal with squares that might be "behind" the ray. + if(xInt < startT) xInt = F32_MAX; + if(yInt < startT) yInt = F32_MAX; + + // Do a little magic to calculate our next checks... + const U32 x0 = (startX > xIntercept) * subSqSize; + const U32 y0 = (startY > yIntercept) * subSqSize; + + const U32 x1 = subSqSize - x0; + const U32 y1 = subSqSize - y0; + + const U32 nextLevel = level - 1; + + // Ok, now let's figure out what nodes, in what order, need to go + // on the stack. We push things on in reverse order of processing. + if(xInt > endT && yInt > endT) + { + stack[stackSize].squarePos.set(squarePos.x + x0, squarePos.y + y0); + stack[stackSize].level = nextLevel; + stackSize++; + } + else if(xInt < yInt) + { + F32 nextIntersect = endT; + + if(yInt <= endT) + { + stack[stackSize].squarePos.set(squarePos.x + x1, squarePos.y + y1); + stack[stackSize].startT = yInt; + stack[stackSize].endT = endT; + stack[stackSize].level = nextLevel; + nextIntersect = yInt; + stackSize++; + } + + // Do middle two, order doesn't matter. + stack[stackSize].squarePos.set(squarePos.x + x1, squarePos.y + y0); + stack[stackSize].startT = xInt; + stack[stackSize].endT = nextIntersect; + stack[stackSize].level = nextLevel; + + stack[stackSize+1].squarePos.set(squarePos.x + x0, squarePos.y + y0); + stack[stackSize+1].startT = startT; + stack[stackSize+1].endT = xInt; + stack[stackSize+1].level = nextLevel; + stackSize += 2; + } + else if(yInt < xInt) + { + F32 nextIntersect = endT; + if(xInt <= endT) + { + stack[stackSize].squarePos.set(squarePos.x + x1, squarePos.y + y1); + stack[stackSize].startT = xInt; + stack[stackSize].endT = endT; + stack[stackSize].level = nextLevel; + nextIntersect = xInt; + stackSize++; + } + stack[stackSize].squarePos.set(squarePos.x + x0, squarePos.y + y1); + stack[stackSize].startT = yInt; + stack[stackSize].endT = nextIntersect; + stack[stackSize].level = nextLevel; + + stack[stackSize+1].squarePos.set(squarePos.x + x0, squarePos.y + y0); + stack[stackSize+1].startT = startT; + stack[stackSize+1].endT = yInt; + stack[stackSize+1].level = nextLevel; + stackSize += 2; + } + else + { + stack[stackSize].squarePos.set(squarePos.x + x1, squarePos.y + y1); + stack[stackSize].startT = xInt; + stack[stackSize].endT = endT; + stack[stackSize].level = nextLevel; + + stack[stackSize+1].squarePos.set(squarePos.x + x0, squarePos.y + y0); + stack[stackSize+1].startT = startT; + stack[stackSize+1].endT = xInt; + stack[stackSize+1].level = nextLevel; + stackSize += 2; + } + } + } + + // Nothing found, so give up. + PROFILE_END(); + return false; +} \ No newline at end of file diff --git a/Engine/source/util/quadTreeTracer.h b/Engine/source/util/quadTreeTracer.h new file mode 100644 index 000000000..09257d353 --- /dev/null +++ b/Engine/source/util/quadTreeTracer.h @@ -0,0 +1,104 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _QUADTREETRACER_H_ +#define _QUADTREETRACER_H_ + +#include "platform/platform.h" +#include "math/mPoint3.h" +#include "scene/sceneObject.h" + +/// Helper class to perform a fast, recursive ray cast against a set of +/// hierarchical bounding boxes. +/// +/// This class assumes that it is working on a unit quadtree (ie, one that +/// extends from 0..1 in the XY dimensions. Z scale is unaffected). +/// +/// Node indexing is done TGE Terrain style - 0 is the largest level of the +/// quadtree, while coordinates are always in the full range of the quadtree +/// (in a 6 deep tree, 0..63, for instance). This allows the quadtree descent +/// to be very fast! +class QuadTreeTracer +{ +protected: + + struct StackNode + { + Point2I squarePos; + U32 level; + }; + + struct RayStackNode : StackNode + { + F32 startT; + F32 endT; + }; + + U32 mTreeDepth; + + QuadTreeTracer(U32 treeDepth) + : mTreeDepth(treeDepth) + { + } + + /// Children better implement these! They return min/max height bounds + /// of the specified square. + virtual const F32 getSquareMin(const U32 &level, const Point2I &pos) const = 0; + virtual const F32 getSquareMax(const U32 &level, const Point2I &pos) const = 0; + + /// And this does checks on leaf nodes. + virtual bool castLeafRay(const Point2I pos, const Point3F &start, const Point3F &end, const F32 &startT, const F32 &endT, RayInfo *info) = 0; + + /// Helper function to calculate intercepts. + inline const F32 calcIntercept(const F32 vStart, const F32 invDeltaV, const F32 intercept) const + { + return (intercept - vStart) * invDeltaV; + } + +public: + + /// Size of a quadtree of depth. + static inline const U32 getNodeCount(const U32 depth) + { + return 0x55555555 & ((1 << depth*2) - 1); + } + + /// Index of a node at given position in a quadtree. + static inline const U32 getNodeIndex(const U32 level, const Point2I pos) + { + //AssertFatal(level < mTreeDepth, "QuadTreeTracer::getNodeIndex - out of range level!) + AssertFatal(pos.x < BIT(level) && pos.x >= 0 , "QuadTreeTracer::getNodeIndex - out of range x for level!"); + AssertFatal(pos.y < BIT(level) && pos.y >= 0 , "QuadTreeTracer::getNodeIndex - out of range y for level!"); + + const U32 base = getNodeCount(level); + return base + (pos.x << level) + pos.y; + } + + /// Cast a ray against a quadtree of hierarchical bounding boxes. + /// + /// This method assumes the quadtree extends from (0..1) along the + /// X and Y axes. Z is unscaled. You may need to adjust the points + /// you pass into this method to get the proper results. + bool castRay(const Point3F &start, const Point3F &end, RayInfo *info); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/util/rectClipper.cpp b/Engine/source/util/rectClipper.cpp new file mode 100644 index 000000000..1346cb6ed --- /dev/null +++ b/Engine/source/util/rectClipper.cpp @@ -0,0 +1,167 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "util/rectClipper.h" + +namespace { + +inline void +swap(F32& in_one, F32& in_two) +{ + F32 temp = in_one; + in_one = in_two; + in_two = temp; +} + +} + +bool +RectClipper::clipLine(const Point2I& in_rStart, + const Point2I& in_rEnd, + Point2I& out_rStart, + Point2I& out_rEnd) const +{ + // Check for trivial rejection + if ((in_rStart.x < m_clipRect.point.x && in_rEnd.x < m_clipRect.point.x) || + (in_rStart.x >= m_clipRect.point.x + m_clipRect.extent.x && + in_rEnd.x >= m_clipRect.point.x + m_clipRect.extent.x)) + return false; + if ((in_rStart.y < m_clipRect.point.y && in_rEnd.y < m_clipRect.point.y) || + (in_rStart.y >= m_clipRect.point.y + m_clipRect.extent.y && + in_rEnd.y >= m_clipRect.point.y + m_clipRect.extent.y)) + return false; + + F32 x1 = F32(in_rStart.x); + F32 y1 = F32(in_rStart.y); + F32 x2 = F32(in_rEnd.x); + F32 y2 = F32(in_rEnd.y); + + // I'm using essentially what's in the Phoenix libs, Liang-Biarsky based, but + // converted to FP math for greater precision on the back end... + // + bool flipped = false; + if (x1 > x2) + { + swap(x1, x2); + swap(y1, y2); + flipped = !flipped; + } + + F32 dx = x2 - x1; + F32 dy = y2 - y1; + + // Clip x coord + F32 t; + if (x1 < F32(m_clipRect.point.x)) + { + t = (F32(m_clipRect.point.x) - x1) / F32(dx); + x1 = F32(m_clipRect.point.x); + y1 += t * dy; + dx = x2 - x1; + dy = y2 - y1; + } + if (x2 >= F32(m_clipRect.point.x + m_clipRect.extent.x)) + { + t = (F32(m_clipRect.point.x + m_clipRect.extent.x - 1) - x1) / F32(dx); + x2 = F32(m_clipRect.point.x + m_clipRect.extent.x - 1); + y2 = y1 + (t * dy); + dx = x2 - x1; + dy = y2 - y1; + } + + // Recheck trivial rejection condition... + if((y1 > F32(m_clipRect.point.y + m_clipRect.extent.y - 1) && + y2 > F32(m_clipRect.point.y + m_clipRect.extent.y - 1)) || + (y1 < F32(m_clipRect.point.y) && y2 < F32(m_clipRect.point.y))) + return false; + + if (y1 > y2) + { + swap(x1, x2); + swap(y1, y2); + flipped = !flipped; + } + + if (y1 < F32(m_clipRect.point.y)) + { + t = (F32(m_clipRect.point.y) - y1) / F32(dy); + y1 = F32(m_clipRect.point.y); + x1 += t * dx; + dx = x2 - x1; + dy = y2 - y1; + } + if (y2 > F32(m_clipRect.point.y + m_clipRect.extent.y - 1)) + { + t = (F32(m_clipRect.point.y + m_clipRect.extent.y - 1) - y1) / F32(dy); + y2 = F32(m_clipRect.point.y + m_clipRect.extent.y - 1); + x2 = x1 + (t * dx); + } + + if (flipped == true) + { + out_rEnd.x = S32(x1 + 0.5f); + out_rEnd.y = S32(y1 + 0.5f); + out_rStart.x = S32(x2 + 0.5f); + out_rStart.y = S32(y2 + 0.5f); + } + else + { + out_rStart.x = S32(x1 + 0.5f); + out_rStart.y = S32(y1 + 0.5f); + out_rEnd.x = S32(x2 + 0.5f); + out_rEnd.y = S32(y2 + 0.5f); + } + + return true; +} + + +bool +RectClipper::clipRect(const RectI& in_rRect, + RectI& out_rRect) const +{ + AssertFatal(in_rRect.isValidRect(), "Inappropriate min/max coords for rectangle"); + + if (in_rRect.point.x + in_rRect.extent.x - 1 < m_clipRect.point.x || + in_rRect.point.x > m_clipRect.point.x + m_clipRect.extent.x - 1) + return false; + if (in_rRect.point.y + in_rRect.extent.y - 1 < m_clipRect.point.y || + in_rRect.point.y > m_clipRect.point.y + m_clipRect.extent.y - 1) + return false; + + if (in_rRect.point.x < m_clipRect.point.x) out_rRect.point.x = m_clipRect.point.x; + else out_rRect.point.x = in_rRect.point.x; + + if (in_rRect.point.y < m_clipRect.point.y) out_rRect.point.y = m_clipRect.point.y; + else out_rRect.point.y = in_rRect.point.y; + + Point2I bottomR; + bottomR.x = getMin(in_rRect.point.x + in_rRect.extent.x - 1, + m_clipRect.point.x + m_clipRect.extent.x - 1); + bottomR.y = getMin(in_rRect.point.y + in_rRect.extent.y - 1, + m_clipRect.point.y + m_clipRect.extent.y - 1); + + out_rRect.extent.x = bottomR.x - out_rRect.point.x + 1; + out_rRect.extent.x = bottomR.y - out_rRect.point.y + 1; + + return true; +} diff --git a/Engine/source/util/rectClipper.h b/Engine/source/util/rectClipper.h new file mode 100644 index 000000000..c28b185f0 --- /dev/null +++ b/Engine/source/util/rectClipper.h @@ -0,0 +1,72 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _RECTCLIPPER_H_ +#define _RECTCLIPPER_H_ + +//Includes +#ifndef _PLATFORM_H_ +#include "platform/platform.h" +#endif +#ifndef _MRECT_H_ +#include "math/mRect.h" +#endif + + +class RectClipper +{ + RectI m_clipRect; + + public: + RectClipper(const RectI& in_rRect); + + bool clipPoint(const Point2I& in_rPoint) const; + bool clipLine(const Point2I& in_rStart, + const Point2I& in_rEnd, + Point2I& out_rStart, + Point2I& out_rEnd) const; + bool clipRect(const RectI& in_rRect, + RectI& out_rRect) const; +}; + +//------------------------------------------------------------------------------ +//-------------------------------------- INLINES +// +inline +RectClipper::RectClipper(const RectI& in_rRect) + : m_clipRect(in_rRect) +{ + // +} + +inline bool +RectClipper::clipPoint(const Point2I& in_rPoint) const +{ + if ((in_rPoint.x < m_clipRect.point.x) || + (in_rPoint.y < m_clipRect.point.y) || + (in_rPoint.x >= m_clipRect.point.x + m_clipRect.extent.x) || + (in_rPoint.y >= m_clipRect.point.y + m_clipRect.extent.y)) + return false; + return true; +} + +#endif //_RECTCLIPPER_H_ diff --git a/Engine/source/util/returnType.h b/Engine/source/util/returnType.h new file mode 100644 index 000000000..f52aed270 --- /dev/null +++ b/Engine/source/util/returnType.h @@ -0,0 +1,66 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _UTIL_RETURNTYPE_H_ +#define _UTIL_RETURNTYPE_H_ + +/// @file +/// +/// Helper templates to determine the return type of functions. + +template struct ReturnType { typedef void ValueType; }; + +template +struct ReturnType { typedef R ValueType; }; +template +struct ReturnType { typedef R ValueType; }; +template +struct ReturnType { typedef R ValueType; }; +template +struct ReturnType { typedef R ValueType; }; +template +struct ReturnType { typedef R ValueType; }; +template +struct ReturnType { typedef R ValueType; }; +template +struct ReturnType { typedef R ValueType; }; +template +struct ReturnType { typedef R ValueType; }; + +template +struct ReturnType { typedef R ValueType; }; +template +struct ReturnType { typedef R ValueType; }; +template +struct ReturnType { typedef R ValueType; }; +template +struct ReturnType { typedef R ValueType; }; +template +struct ReturnType { typedef R ValueType; }; +template +struct ReturnType { typedef R ValueType; }; +template +struct ReturnType { typedef R ValueType; }; +template +struct ReturnType { typedef R ValueType; }; + +#endif \ No newline at end of file diff --git a/Engine/source/util/sampler.cpp b/Engine/source/util/sampler.cpp new file mode 100644 index 000000000..ea80b59a8 --- /dev/null +++ b/Engine/source/util/sampler.cpp @@ -0,0 +1,432 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "util/sampler.h" + +#include "core/util/safeDelete.h" +#include "core/util/tVector.h" +#include "core/stream/fileStream.h" +#include "console/console.h" +#include "console/consoleTypes.h" + +/// Bookkeeping structure for registered sampling keys. + +struct SampleKey +{ + bool mEnabled; + const char* mName; + + bool matchesPattern( const char* pattern ) + { + U32 indexInName = 0; + U32 indexInPattern = 0; + + while( mName[ indexInName ] != '\0' ) + { + if( pattern[ indexInPattern ] == '\0' ) + break; + else if( dToupper( mName[ indexInName ] ) == dToupper( pattern[ indexInPattern ] ) ) + { + indexInName ++; + indexInPattern ++; + } + else if( pattern[ indexInPattern ] == '*' ) + { + // Handle senseless concatenation of wildcards. + while( pattern[ indexInPattern ] == '*' ) + indexInPattern ++; + + // Skip to next slash in name. + while( mName[ indexInName ] && mName[ indexInName ] != '/' ) + indexInName ++; + } + else + return false; + } + + return ( pattern[ indexInPattern ] == '\0' + || ( indexInPattern > 0 && pattern[ indexInPattern ] == '*' ) ); + } +}; + +/// A sampler backend is responsible for storing the actual sampling data. + +struct ISamplerBackend +{ + virtual ~ISamplerBackend() {} + + virtual bool init( const char* location ) = 0; + virtual void beginFrame() = 0; + virtual void endFrame() = 0; + + virtual void sample( U32 key, bool value ) = 0; + virtual void sample( U32 key, S32 value ) = 0; + virtual void sample( U32 key, F32 value ) = 0; + virtual void sample( U32 key, const char* value ) = 0; +}; + +static bool gSamplerRunning; +static S32 gSamplingFrequency = 1; ///< Frequency = samples taken every nth frame. +static U32 gCurrentFrameDelta; +static Vector< SampleKey > gSampleKeys( __FILE__, __LINE__ ); +static ISamplerBackend* gSamplerBackend; + +//-------------------------------------------------------------------------------- +// CSV Backend. +//-------------------------------------------------------------------------------- + +/// A sampler backend that outputs samples to a CSV file. + +class CSVSamplerBackend : public ISamplerBackend +{ + /// Value holder for an individual sample. Unfortunately, since the + /// order in which samples arrive at the sampler may vary from frame to + /// frame, we cannot emit data immediately but rather have to buffer + /// it in these sample records and then flush them to disk once we receive + /// the endFrame call. + struct SampleRecord + { + U32 mKey; + U32 mType; //< Console type code. + bool mSet; + union + { + bool mBool; + S32 mS32; + F32 mF32; + const char* mString; + } mValue; + + SampleRecord() {} + SampleRecord( U32 key ) + : mKey( key ), mSet( false ) {} + + void set( bool value ) + { + mType = TypeBool; + mValue.mBool = value; + mSet = true; + } + void set( S32 value ) + { + mType = TypeS32; + mValue.mS32 = value; + mSet = true; + } + void set( F32 value ) + { + mType = TypeF32; + mValue.mF32 = value; + mSet = true; + } + void set( const char* str ) + { + mType = TypeString; + mValue.mString = dStrdup( str ); + mSet = true; + } + + void clean() + { + if( mType == TypeString ) + dFree( ( void* ) mValue.mString ); + mSet = false; + } + }; + + FileStream mStream; + Vector< SampleRecord > mRecords; + + ~CSVSamplerBackend() + { + mStream.close(); + } + + /// Open the file and emit a row with the names of all enabled keys. + virtual bool init( const char* fileName ) + { + if( !mStream.open( fileName, Torque::FS::File::Write ) ) + { + Con::errorf( "CSVSamplerBackend::init -- could not open '%s' for writing", fileName ); + return false; + } + + Con::printf( "CSVSamplerBackend::init -- writing samples to '%s'", fileName ); + + bool first = true; + for( U32 i = 0; i < gSampleKeys.size(); ++ i ) + { + SampleKey& key = gSampleKeys[ i ]; + if( key.mEnabled ) + { + if( !first ) + mStream.write( 1, "," ); + + mRecords.push_back( SampleRecord( i + 1 ) ); + mStream.write( dStrlen( key.mName ), key.mName ); + first = false; + } + } + + newline(); + return true; + } + + virtual void beginFrame() + { + } + + virtual void endFrame() + { + char buffer[ 256 ]; + + for( U32 i = 0; i < mRecords.size(); ++ i ) + { + if( i != 0 ) + mStream.write( 1, "," ); + + SampleRecord& record = mRecords[ i ]; + if( record.mSet ) + { + if( record.mType == TypeBool ) + { + if( record.mValue.mBool ) + mStream.write( 4, "true" ); + else + mStream.write( 5, "false" ); + } + else if( record.mType == TypeS32 ) + { + dSprintf( buffer, sizeof( buffer ), "%d", record.mValue.mS32 ); + mStream.write( dStrlen( buffer ), buffer ); + } + else if( record.mType == TypeF32 ) + { + dSprintf( buffer, sizeof( buffer ), "%f", record.mValue.mF32 ); + mStream.write( dStrlen( buffer ), buffer ); + } + else if( record.mType == TypeString ) + { + //FIXME: does not do doubling of double quotes in the string at the moment + mStream.write( 1, "\"" ); + mStream.write( dStrlen( record.mValue.mString ), record.mValue.mString ); + mStream.write( 1, "\"" ); + } + else + AssertWarn( false, "CSVSamplerBackend::endFrame - bug: invalid sample type" ); + } + + record.clean(); + } + + newline(); + } + + void newline() + { + mStream.write( 1, "\n" ); + } + + SampleRecord* lookup( U32 key ) + { + //TODO: do this properly with a binary search (the mRecords array is already sorted by key) + + for( U32 i = 0; i < mRecords.size(); ++ i ) + if( mRecords[ i ].mKey == key ) + return &mRecords[ i ]; + + AssertFatal( false, "CSVSamplerBackend::lookup - internal error: sample key not found" ); + return NULL; // silence compiler + } + + virtual void sample( U32 key, bool value ) + { + lookup( key )->set( value ); + } + virtual void sample( U32 key, S32 value ) + { + lookup( key )->set( value ); + } + virtual void sample( U32 key, F32 value ) + { + lookup( key )->set( value ); + } + virtual void sample( U32 key, const char* value ) + { + lookup( key )->set( value ); + } +}; + +//-------------------------------------------------------------------------------- +// Internal Functions. +//-------------------------------------------------------------------------------- + +static void stopSampling() +{ + if( gSamplerRunning ) + { + SAFE_DELETE( gSamplerBackend ); + gSamplerRunning = false; + } +} + +static void beginSampling( const char* location, const char* backend ) +{ + if( gSamplerRunning ) + stopSampling(); + + if( dStricmp( backend, "CSV" ) == 0 ) + gSamplerBackend = new CSVSamplerBackend; + else + { + Con::errorf( "beginSampling -- No backend called '%s'", backend ); + return; + } + + if( !gSamplerBackend->init( location ) ) + { + SAFE_DELETE( gSamplerBackend ); + } + else + { + gSamplerRunning = true; + gCurrentFrameDelta = 0; + } +} + +//-------------------------------------------------------------------------------- +// Sampler Functions. +//-------------------------------------------------------------------------------- + +void Sampler::init() +{ + Con::addVariable( "Sampler::frequency", TypeS32, &gSamplingFrequency, "Samples taken every nth frame.\n" + "@ingroup Rendering"); +} + +void Sampler::beginFrame() +{ + gCurrentFrameDelta ++; + if( gSamplerBackend && gCurrentFrameDelta == gSamplingFrequency ) + gSamplerBackend->beginFrame(); +} + +void Sampler::endFrame() +{ + if( gSamplerBackend && gCurrentFrameDelta == gSamplingFrequency ) + { + gSamplerBackend->endFrame(); + gCurrentFrameDelta = 0; + } +} + +void Sampler::destroy() +{ + if( gSamplerBackend ) + SAFE_DELETE( gSamplerBackend ); +} + +U32 Sampler::registerKey( const char* name ) +{ + gSampleKeys.push_back( SampleKey() ); + U32 index = gSampleKeys.size(); + SampleKey& key = gSampleKeys.last(); + + key.mName = name; + key.mEnabled = false; + + return index; +} + +void Sampler::enableKeys( const char* pattern, bool state ) +{ + if( gSamplerRunning ) + { + Con::errorf( "Sampler::enableKeys -- cannot change key states while sampling" ); + return; + } + + for( U32 i = 0; i < gSampleKeys.size(); ++ i ) + if( gSampleKeys[ i ].matchesPattern( pattern ) ) + { + gSampleKeys[ i ].mEnabled = state; + Con::printf( "Sampler::enableKeys -- %s %s", state ? "enabling" : "disabling", + gSampleKeys[ i ].mName ); + } +} + +#define SAMPLE_FUNC( type ) \ + void Sampler::sample( U32 key, type value ) \ +{ \ + if( gSamplerRunning \ + && gCurrentFrameDelta == gSamplingFrequency \ + && gSampleKeys[ key - 1 ].mEnabled ) \ + gSamplerBackend->sample( key, value ); \ +} + +SAMPLE_FUNC( bool ); +SAMPLE_FUNC( S32 ); +SAMPLE_FUNC( F32 ); +SAMPLE_FUNC( const char* ); + +//-------------------------------------------------------------------------------- +// Console Functions. +//-------------------------------------------------------------------------------- + +ConsoleFunction( beginSampling, void, 2, 3, "(location, [backend]) -" + "@brief Takes a string informing the backend where to store " + "sample data and optionally a name of the specific logging " + "backend to use. The default is the CSV backend. In most " + "cases, the logging store will be a file name." + "@tsexample\n" + "beginSampling( \"mysamples.csv\" );\n" + "@endtsexample\n\n" + "@ingroup Rendering") +{ + const char* location = argv[ 1 ]; + const char* backend = "CSV"; + if( argc > 2 ) + backend = argv[ 2 ]; + + beginSampling( location, backend ); +} + +ConsoleFunction( stopSampling, void, 1, 1, "()" + "@brief Stops the rendering sampler\n\n" + "@ingroup Rendering\n") +{ + stopSampling(); +} + +ConsoleFunction( enableSamples, void, 2, 3, "(pattern, [state]) -" + "@brief Enable sampling for all keys that match the given name " + "pattern. Slashes are treated as separators.\n\n" + "@ingroup Rendering") +{ + const char* pattern = argv[ 1 ]; + bool state = true; + if( argc > 2 ) + state = dAtob( argv[ 2 ] ); + + Sampler::enableKeys( pattern, state ); +} diff --git a/Engine/source/util/sampler.h b/Engine/source/util/sampler.h new file mode 100644 index 000000000..7ce8d2bb2 --- /dev/null +++ b/Engine/source/util/sampler.h @@ -0,0 +1,146 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- +#ifndef _SAMPLER_H_ +#define _SAMPLER_H_ + +#include "platform/types.h" + +/// The sampling framework. +/// +/// Sampling allows per-frame snaphots of specific values to be logged. For +/// each value that you want to have sampled, you define a sampling key and +/// then simply call the sample function at an appropriate place. If you +/// want to sample the same value multiple times within a single frame, simply +/// register several keys for it. +/// +/// The easiest way to use this facility is with the SAMPLE macro. +/// +/// @code +/// SAMPLE( "my/sample/value", my.sample->val ); +/// @endcode +/// +/// @section SamplerUsage Using the Sampler +/// +/// Before you use the sampler it is important that you let your game run for +/// some frames and make sure that all relevant code paths have been touched (i.e. +/// if you want to sample Atlas data, have an Atlas instance on screen). This +/// will ensure that sampling keys are registered with the sampler. +/// +/// Then use the console to first enable the keys you are interested in. For +/// example, to enable sampling for all Atlas keys: +/// +/// @code +/// enableSamples( "atlas/*" ); +/// @endcode +/// +/// Finally, you have to start the actual sampling. This is achieved with the +/// beginSampling console function that takes a string informing the backend +/// where to store sample data and optionally a name of the specific logging backend +/// to use. The default is the CSV backend. In most cases, the logging store +/// will be a file name. +/// +/// @code +/// beginSampling( "mysamples.csv" ); +/// @endcode +/// +/// To stop sampling, use: +/// +/// @code +/// stopSampling(); +/// @endcode +/// +/// @section Sample Keys +/// +/// Sample key name should generally follow the pattern "path/to/group/samplename". +/// This allows to very easily enable or disable specific sets of keys using +/// wildcards. +/// +/// Note that sampling keys are case-insensitive. + +namespace Sampler +{ + void init(); + void destroy(); + + void beginFrame(); + void endFrame(); + + void sample( U32 key, bool value ); + void sample( U32 key, S32 value ); + void sample( U32 key, F32 value ); + void sample( U32 key, const char* value ); + + inline void sample( U32 key, U32 value ) + { + sample( key, S32( value ) ); + } + + /// Register a new sample key. + /// + /// @note Note that all keys are disabled by default. + U32 registerKey( const char* name ); + + /// Enable sampling for all keys that match the given name + /// pattern. Slashes are treated as separators. + void enableKeys( const char* pattern, bool state = true ); +}; + +#ifdef TORQUE_ENABLE_SAMPLING +# define SAMPLE( name, value ) \ +{ \ + static U32 key; \ + if( !key ) \ + key = Sampler::registerKey( name ); \ + Sampler::sample( key, value ); \ +} +#else +# define SAMPLE( name, value ) +#endif + +#define SAMPLE_VECTOR( name, value ) \ +{ \ + SAMPLE( name "/x", value.x ); \ + SAMPLE( name "/y", value.y ); \ + SAMPLE( name "/z", value.z ); \ +} + +#define SAMPLE_MATRIX( name, value ) \ +{ \ + SAMPLE( name "/a1", value[ value.idx( 0, 0 ) ] ); \ + SAMPLE( name "/a2", value[ value.idx( 1, 0 ) ] ); \ + SAMPLE( name "/a3", value[ value.idx( 2, 0 ) ] ); \ + SAMPLE( name "/a4", value[ value.idx( 3, 0 ) ] ); \ + SAMPLE( name "/b1", value[ value.idx( 0, 1 ) ] ); \ + SAMPLE( name "/b2", value[ value.idx( 1, 1 ) ] ); \ + SAMPLE( name "/b3", value[ value.idx( 2, 1 ) ] ); \ + SAMPLE( name "/b4", value[ value.idx( 3, 1 ) ] ); \ + SAMPLE( name "/c1", value[ value.idx( 0, 2 ) ] ); \ + SAMPLE( name "/c2", value[ value.idx( 1, 2 ) ] ); \ + SAMPLE( name "/c3", value[ value.idx( 2, 2 ) ] ); \ + SAMPLE( name "/c4", value[ value.idx( 3, 2 ) ] ); \ + SAMPLE( name "/d1", value[ value.idx( 0, 3 ) ] ); \ + SAMPLE( name "/d2", value[ value.idx( 1, 3 ) ] ); \ + SAMPLE( name "/d3", value[ value.idx( 2, 3 ) ] ); \ + SAMPLE( name "/d4", value[ value.idx( 3, 3 ) ] ); \ +} + +#endif // _SAMPLER_H_ diff --git a/Engine/source/util/scopeTracker.h b/Engine/source/util/scopeTracker.h new file mode 100644 index 000000000..fdaea5177 --- /dev/null +++ b/Engine/source/util/scopeTracker.h @@ -0,0 +1,873 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SCOPETRACKER_H_ +#define _SCOPETRACKER_H_ + +#ifndef _TYPETRAITS_H_ + #include "platform/typetraits.h" +#endif +#ifndef _BITSET_H_ + #include "core/bitSet.h" +#endif +#ifndef _TVECTOR_H_ + #include "core/util/tVector.h" +#endif +#ifndef _CONSOLE_H_ + #include "console/console.h" +#endif +#ifndef _PROFILER_H_ + #include "platform/profiler.h" +#endif + +//#define DEBUG_SPEW + + +/// @file +/// A mechanism for continuous tracking of point/box intersections. + + +/// Base class for objects registered with a ScopeTracker. +template< int NUM_DIMENSIONS > +class ScopeTrackerObject +{ + public: + + typedef void Parent; + + /// TrackingNodes are used to track object bounds along individual world axes. + class TrackingNode + { + public: + + typedef void Parent; + + enum EFlags + { + FLAG_Min = BIT( 0 ), + FLAG_Max = BIT( 1 ), + FLAG_Reference = BIT( 2 ), + }; + + /// + BitSet32 mFlags; + + /// + TrackingNode* mOpposite; + + /// Distance along axis. + F32 mPosition; + + /// The object being tracked by this node or NULL. + ScopeTrackerObject* mObject; + + /// Next node on axis tracking chain. + TrackingNode* mNext; + + /// Previous node on axis tracking chain. + TrackingNode* mPrev; + + /// + TrackingNode() + : mPosition( 0.0f ), mObject( NULL ), mNext( NULL ), mPrev( NULL ), mOpposite( NULL ) {} + + /// Return the object to which this tracking node belongs. + ScopeTrackerObject* getObject() const { return mObject; } + + /// + TrackingNode* getOpposite() const { return mOpposite; } + + /// + F32 getPosition() const { return mPosition; } + + /// + void setPosition( F32 value ) { mPosition = value; } + + /// + TrackingNode* getNext() const { return mNext; } + + /// + void setNext( TrackingNode* node ) { mNext = node; } + + /// + TrackingNode* getPrev() const { return mPrev; } + + /// + void setPrev( TrackingNode* node ) { mPrev = node; } + + /// Return true if this is left/lower bound node of an object. + bool isMin() const { return mFlags.test( FLAG_Min ); } + + /// Return true if this is the right/upper bound node of an object. + bool isMax() const { return mFlags.test( FLAG_Max ); } + + /// Return true if this is the reference center tracking node. There will only + /// ever be one such node on each tracking list. + bool isReference() const { return mFlags.test( FLAG_Reference ); } + }; + + enum + { + AllInScope = ( 0x01010101 >> ( ( 4 - NUM_DIMENSIONS ) * 8 ) ) + }; + + protected: + + /// + union + { + U8 mBytes[ 4 ]; + U32 mDWord; + } mScopeMask; + + /// + TrackingNode mTrackingNodes[ NUM_DIMENSIONS ][ 2 ]; + + public: + + /// + ScopeTrackerObject( U32 flags = 0 ) + { + clearScopeMask(); + for( U32 n = 0; n < NUM_DIMENSIONS; ++ n ) + { + TrackingNode* minNode = getMinTrackingNode( n ); + TrackingNode* maxNode = getMaxTrackingNode( n ); + + minNode->mFlags = flags; + maxNode->mFlags = flags; + + minNode->mObject = this; + maxNode->mObject = this; + + minNode->mOpposite = maxNode; + maxNode->mOpposite = minNode; + + minNode->mFlags.set( TrackingNode::FLAG_Min ); + maxNode->mFlags.set( TrackingNode::FLAG_Max ); + } + } + + /// Return true if the object is currently being tracked. + bool isRegistered() const + { + for( U32 n = 0; n < NUM_DIMENSIONS; ++ n ) + if( getMinTrackingNode( n )->getNext() != NULL ) + return true; + return false; + } + + /// Return true if the reference center lies within the object bound's on all axes. + bool isInScope() const { return ( mScopeMask.mDWord == AllInScope ); } + + /// + bool isInScope( U32 dimension ) const { return mScopeMask.mBytes[ dimension ]; } + + /// + void setInScope( U32 dimension, bool state ) { mScopeMask.mBytes[ dimension ] = ( state ? 1 : 0 ); } + + /// + void clearScopeMask() { mScopeMask.mDWord = 0; } + + /// + TrackingNode* getMinTrackingNode( U32 dimension ) { return &mTrackingNodes[ dimension ][ 0 ]; } + const TrackingNode* getMinTrackingNode( U32 dimension ) const { return &mTrackingNodes[ dimension ][ 0 ]; } + + /// + TrackingNode* getMaxTrackingNode( U32 dimension ) { return &mTrackingNodes[ dimension ][ 1 ]; } + const TrackingNode* getMaxTrackingNode( U32 dimension ) const { return &mTrackingNodes[ dimension ][ 1 ]; } + + /// @name Implementor Interface + /// + /// The following methods must be implemented by the client. They are defined here + /// just for reference. If you don't override them, you'll get link errors. + /// + /// @{ + + /// Return the position of the object in world-space. + void getPosition( F32 pos[ NUM_DIMENSIONS ] ) const; + + /// If this object is the reference object, this method should return the world-space pivot + /// point in the object that will be the world reference center. + void getReferenceCenter( F32 pos[ NUM_DIMENSIONS ] ) const; + + /// Return the object's bounding box in world-space. + void getBounds( F32 minBounds[ NUM_DIMENSIONS ], F32 maxBounds[ NUM_DIMENSIONS ] ) const; + + /// + String describeSelf() const; + + /// @} +}; + + +/// Helper class to track the position of a point in N-dimensional space relative to a collection +/// of N-dimensional volumes. +/// +/// This class works by breaking the N-dimensional case down into N one-dimensional cases. By tracking +/// objects independently along each of their axes, intersection testing becomes a trivial matter of +/// doing point-on-line tests. The line segments can be conveniently represented as ordered linked +/// lists along which the reference point is being moved. +/// +/// To determine N-dimensional containment of the reference point, the result of each of the one-dimensional +/// point-on-line tests simply has to be combined. If the point lies on each of N 1D segments of a +/// given volume, then the point is fully contained in the volume. +/// +/// This class may be used in places where otherwise a spatial subdivision scheme would be necessary in +/// order to limit the number of containment tests triggered by each movement of the reference point. +/// Once the tracker has been set up, each position change of an object or the reference center will result +/// in a usually small number of incremental list updates. +/// +/// Another advantage is that this class makes it easy to reduce 3D tracking to 2D tracking if tracking on +/// the height axis isn't important. +/// +/// The following interface must be implemented by the given "Object" type: +/// +/// @code +/// struct Object : public ScopeTrackerObject< NUM_DIMENSIONS > +/// { +/// /// Return the position of the object in world-space. +/// void getPosition( F32 pos[ NUM_DIMENSIONS ] ) const; +/// +/// /// If this object is the reference object, this method should return the world-space pivot +/// /// point in the object that will be the world reference center. +/// void getReferenceCenter( F32 pos[ NUM_DIMENSIONS ] ) const; +/// +/// /// Return the object's bounding box in world-space. +/// void getBounds( F32 minBounds[ NUM_DIMENSIONS ], F32 maxBounds[ NUM_DIMENSIONS ] ) const; +/// }; +/// @endcode +/// +/// Terminology: +/// +/// - "In Scope": A volume is in scope if it fully contains the reference center. +/// - "Reference Object": Object that is the designated center of the world. +/// +/// @param NUM_DIMENSIONS Number of dimensions to track; must be <=4. +/// @param Object Value type for objects tracked by the ScopeTracker. Must have pointer behavior. +template< int NUM_DIMENSIONS, typename Object > +class ScopeTracker +{ + public: + + typedef void Parent; + typedef typename TypeTraits< Object >::BaseType ObjectType; + typedef typename ObjectType::TrackingNode NodeType; + + protected: + + enum + { + MIN = 0, + MAX = 1 + }; + + /// The reference object. This is the center relative to which all + /// tracking occurs. Any other object is in scope when it contains the + /// reference object. + Object mReferenceObject; + + /// + NodeType* mTrackingList[ NUM_DIMENSIONS ][ 2 ]; + + /// + NodeType mBoundaryNodes[ NUM_DIMENSIONS ][ 2 ]; + + /// + Vector< Object > mPotentialScopeInObjects; + + /// @name Scoping + /// @{ + + virtual void _onScopeIn( Object object ) {} + + virtual void _onScopeOut( Object object ) {} + + /// Set the scoping state of the given object. + void _setScope( Object object ); + + /// @} + + /// @name Tracking + /// @{ + + /// + void _insertTrackingNode( U32 dimension, NodeType* node ); + + /// + void _removeTrackingNode( U32 dimension, NodeType* node ); + + /// + void _moveTrackingNode( U32 dimension, NodeType* node, F32 newPos ); + + /// + void _initTracking(); + + /// + void _uninitTracking(); + + /// @} + + public: + + /// + ScopeTracker(); + + /// Add a volume object to the world. + void registerObject( Object object ); + + /// Remove a volume object from the world. + void unregisterObject( Object object ); + + /// Update the position of the object in the world. + void updateObject( Object object ); + + /// + Object getReferenceObject() const { return mReferenceObject; } + + /// + /// + /// @note Switching reference centers is potentially costly. + void setReferenceObject( Object object ); + + /// + void debugDump(); +}; + + +//----------------------------------------------------------------------------- + +template< int NUM_DIMENSIONS, class Object > +ScopeTracker< NUM_DIMENSIONS, Object >::ScopeTracker() + : mReferenceObject( NULL ) +{ + VECTOR_SET_ASSOCIATION( mPotentialScopeInObjects ); + + // Initialize the tracking lists. Put the boundary + // nodes in place that will always be the heads and tails + // of each list. + + dMemset( mTrackingList, 0, sizeof( mTrackingList ) ); + for( U32 n = 0; n < NUM_DIMENSIONS; ++ n ) + { + mBoundaryNodes[ n ][ MIN ].setPosition( TypeTraits< F32 >::MIN ); + mBoundaryNodes[ n ][ MAX ].setPosition( TypeTraits< F32 >::MAX ); + + mBoundaryNodes[ n ][ MIN ].setNext( &mBoundaryNodes[ n ][ MAX ] ); + mBoundaryNodes[ n ][ MAX ].setPrev( &mBoundaryNodes[ n ][ MIN ] ); + + mBoundaryNodes[ n ][ MIN ].mOpposite = &mBoundaryNodes[ n ][ MAX ]; + mBoundaryNodes[ n ][ MAX ].mOpposite = &mBoundaryNodes[ n ][ MIN ]; + + mTrackingList[ n ][ MIN ] = &mBoundaryNodes[ n ][ MIN ]; + mTrackingList[ n ][ MAX ] = &mBoundaryNodes[ n ][ MAX ]; + } +} + +//----------------------------------------------------------------------------- + +template< int NUM_DIMENSIONS, class Object > +void ScopeTracker< NUM_DIMENSIONS, Object >::setReferenceObject( Object object ) +{ + AssertFatal( !object || !Deref( object ).isRegistered(), + "ScopeTracker::setReferenceObject - reference object must not be volume object" ); + + if( mReferenceObject == object ) + return; + + // If object is invalid, remove the reference center + // tracking. + + if( !object ) + { + // Transition all objects to out-of-scope and + // deactivate tracking. + + _uninitTracking(); + mReferenceObject = object; + return; + } + + if( mReferenceObject ) + { + //RDFIXME: this is very disruptive + + // We have an existing reference object so we need to update + // the scoping to match it. Brute-force this for now. + + _uninitTracking(); + mReferenceObject = object; + _initTracking(); + } + else + { + // No reference object yet. + + mReferenceObject = object; + _initTracking(); + } + + #ifdef DEBUG_SPEW + Platform::outputDebugString( "[ScopeTracker] Reference object is now 0x%x", object ); + #endif +} + +//----------------------------------------------------------------------------- + +template< int NUM_DIMENSIONS, class Object > +void ScopeTracker< NUM_DIMENSIONS, Object >::registerObject( Object object ) +{ + PROFILE_SCOPE( ScopeTracker_registerObject ); + + // Get the object bounds. + + F32 minBounds[ NUM_DIMENSIONS ]; + F32 maxBounds[ NUM_DIMENSIONS ]; + + Deref( object ).getBounds( minBounds, maxBounds ); + + // Insert the object's tracking nodes. + + for( U32 n = 0; n < NUM_DIMENSIONS; ++ n ) + { + NodeType* minNode = Deref( object ).getMinTrackingNode( n ); + NodeType* maxNode = Deref( object ).getMaxTrackingNode( n ); + + minNode->setPosition( minBounds[ n ] ); + maxNode->setPosition( maxBounds[ n ] ); + + // Insert max before min so that max always comes out + // to the right of min. + + _insertTrackingNode( n, maxNode ); + _insertTrackingNode( n, minNode ); + } + + // Set the scoping state of the object. + + _setScope( object ); +} + +//----------------------------------------------------------------------------- + +template< int NUM_DIMENSIONS, class Object > +void ScopeTracker< NUM_DIMENSIONS, Object >::unregisterObject( Object object ) +{ + PROFILE_SCOPE( ScopeTracker_unregisterObject ); + + if( !Deref( object ).isRegistered() ) + return; + + // Clear its scoping state. + + if( Deref( object ).isInScope() ) + _onScopeOut( object ); + Deref( object ).clearScopeMask(); + + // Remove the tracking state. + + for( U32 n = 0; n < NUM_DIMENSIONS; ++ n ) + { + _removeTrackingNode( n, Deref( object ).getMinTrackingNode( n ) ); + _removeTrackingNode( n, Deref( object ).getMaxTrackingNode( n ) ); + } +} + +//----------------------------------------------------------------------------- + +template< int NUM_DIMENSIONS, class Object > +void ScopeTracker< NUM_DIMENSIONS, Object >::updateObject( Object object ) +{ + PROFILE_SCOPE( ScopeTracker_updateObject ); + + if( object == mReferenceObject ) + { + // Get the reference center position. + + F32 position[ NUM_DIMENSIONS ]; + Deref( mReferenceObject ).getReferenceCenter( position ); + + // Move the reference tracking node. + + for( U32 n = 0; n < NUM_DIMENSIONS; ++ n ) + _moveTrackingNode( n, Deref( mReferenceObject ).getMinTrackingNode( n ), position[ n ] ); + + // Flush the potential-scope-in list. + + while( !mPotentialScopeInObjects.empty() ) + { + Object object = mPotentialScopeInObjects.last(); + mPotentialScopeInObjects.decrement(); + + if( Deref( object ).isInScope() ) + _onScopeIn( object ); + } + } + else + { + // Get the object bounds. + + F32 minBounds[ NUM_DIMENSIONS ]; + F32 maxBounds[ NUM_DIMENSIONS ]; + + Deref( object ).getBounds( minBounds, maxBounds ); + + // Move the object's tracking nodes. + + bool wasInScope = Deref( object ).isInScope(); + for( U32 n = 0; n < NUM_DIMENSIONS; ++ n ) + { + NodeType* minNode = Deref( object ).getMinTrackingNode( n ); + NodeType* maxNode = Deref( object ).getMaxTrackingNode( n ); + + _moveTrackingNode( n, minNode, minBounds[ n ] ); + _moveTrackingNode( n, maxNode, maxBounds[ n ] ); + } + + // Rescope the object, if necessary. + + if( wasInScope && !Deref( object ).isInScope() ) + _onScopeOut( object ); + else if( !wasInScope && Deref( object ).isInScope() ) + _onScopeIn( object ); + } +} + +//----------------------------------------------------------------------------- + +template< int NUM_DIMENSIONS, class Object > +void ScopeTracker< NUM_DIMENSIONS, Object >::_insertTrackingNode( U32 dimension, NodeType* node ) +{ + //RDTODO: substitute brute-force search with some smarter insertion algorithm + // (at least dynamically decide on direction for search) + + F32 pos = node->getPosition(); + NodeType* current = mTrackingList[ dimension ][ MIN ]->getNext(); + NodeType* prev = mTrackingList[ dimension ][ MIN ]; + + while( current->getPosition() < pos ) + { + prev = current; + current = current->getNext(); + } + + prev->setNext( node ); + current->setPrev( node ); + + node->setPrev( prev ); + node->setNext( current ); +} + +//----------------------------------------------------------------------------- + +template< int NUM_DIMENSIONS, class Object > +void ScopeTracker< NUM_DIMENSIONS, Object >::_removeTrackingNode( U32 dimension, NodeType* node ) +{ + NodeType* next = node->getNext(); + NodeType* prev = node->getPrev(); + + AssertFatal( next != NULL, "ScopeTracker::_insertTrackingNode - invalid list state (no next node)!" ); + AssertFatal( prev != NULL, "ScopeTracker::_insertTrackingNode - invalid list state (no prev node)!" ); + + next->setPrev( prev ); + prev->setNext( next ); + + node->setNext( NULL ); + node->setPrev( NULL ); +} + +//----------------------------------------------------------------------------- + +template< int NUM_DIMENSIONS, class Object > +void ScopeTracker< NUM_DIMENSIONS, Object >::_moveTrackingNode( U32 dimension, NodeType* node, F32 newPosition ) +{ + PROFILE_SCOPE( ScopeTracker_moveTrackingNode ); + + AssertFatal( TypeTraits< F32 >::MIN <= newPosition && newPosition <= TypeTraits< F32 >::MAX, "Invalid float in object coordinate!" ); + + enum EDirection + { + DIRECTION_Up, + DIRECTION_Down + }; + + // Determine in which direction we are sliding the node. + + EDirection direction; + if( newPosition < node->getPosition() ) + { + direction = DIRECTION_Down; + if( node->getPrev()->getPosition() <= newPosition ) + { + node->setPosition( newPosition ); + return; // Nothing to do. + } + } + else if( newPosition > node->getPosition() ) + { + direction = DIRECTION_Up; + if( node->getNext()->getPosition() >= newPosition ) + { + node->setPosition( newPosition ); + return; // Nothing to do. + } + } + else + return; // Nothing to to. + + const bool isReferenceNode = node->isReference(); + + // Unlink the node. + + NodeType* next = node->getNext(); + NodeType* prev = node->getPrev(); + + next->setPrev( prev ); + prev->setNext( next ); + + // Iterate through to the node's new position. + + while( ( direction == DIRECTION_Up && next->getPosition() < newPosition ) + || ( direction == DIRECTION_Down && prev->getPosition() > newPosition ) ) + { + NodeType* current = 0; + switch( direction ) + { + case DIRECTION_Up: current = next; break; + case DIRECTION_Down: current = prev; break; + } + + if( isReferenceNode ) + { + Object object = ( Object ) current->getObject(); + if( ( direction == DIRECTION_Up && current->isMin() ) + || ( direction == DIRECTION_Down && current->isMax() ) ) + { + Deref( object ).setInScope( dimension, true ); + mPotentialScopeInObjects.push_back( object ); + } + else + { + const bool wasInScope = Deref( object ).isInScope(); + Deref( object ).setInScope( dimension, false ); + if( wasInScope ) + _onScopeOut( object ); + } + } + else + { + if( current->isReference() ) + { + Object object = ( Object ) node->getObject(); + if( ( direction == DIRECTION_Up && node->isMin() ) + || ( direction == DIRECTION_Down && node->isMax() ) ) + Deref( object ).setInScope( dimension, false ); + else + Deref( object ).setInScope( dimension, true ); + } + } + + switch( direction ) + { + case DIRECTION_Down: + next = current; + prev = current->getPrev(); + break; + + case DIRECTION_Up: + prev = current; + next = current->getNext(); + break; + } + } + + // Relink the node. + + prev->setNext( node ); + next->setPrev( node ); + + node->setPrev( prev ); + node->setNext( next ); + + node->setPosition( newPosition ); +} + +//----------------------------------------------------------------------------- + +template< int NUM_DIMENSIONS, class Object > +void ScopeTracker< NUM_DIMENSIONS, Object >::_setScope( Object object ) +{ + // If there's no reference object, all objects are out of scope. + + if( !mReferenceObject || object == mReferenceObject ) + { + Deref( object ).clearScopeMask(); + return; + } + + const bool wasInScope = Deref( object ).isInScope(); + + // Set the scoping state on each axis. + + for( U32 n = 0; n < NUM_DIMENSIONS; ++ n ) + { + const F32 referencePos = Deref( mReferenceObject ).getMinTrackingNode( n )->getPosition(); + const F32 objectMin = Deref( object ).getMinTrackingNode( n )->getPosition(); + const F32 objectMax = Deref( object ).getMaxTrackingNode( n )->getPosition(); + + bool isInScope = referencePos >= objectMin + && referencePos <= objectMax; + + Deref( object ).setInScope( n, isInScope ); + } + + // Scope in/out if the scoping state has changed. + + if( Deref( object ).isInScope() ) + { + if( !wasInScope ) + _onScopeIn( object ); + } + else + { + if( wasInScope ) + _onScopeOut( object ); + } +} + +//----------------------------------------------------------------------------- + +template< int NUM_DIMENSIONS, class Object > +void ScopeTracker< NUM_DIMENSIONS, Object >::_initTracking() +{ + PROFILE_SCOPE( ScopeTracker_initTracking ); + + AssertFatal( bool( getReferenceObject() ), + "ScopeTracker::_initTracking - can only be called with a valid reference object" ); + + // Put a single tracking node onto each of the lists for + // the reference object center. + + F32 position[ NUM_DIMENSIONS ]; + Deref( mReferenceObject ).getReferenceCenter( position ); + + for( U32 n = 0; n < NUM_DIMENSIONS; ++ n ) + { + AssertFatal( TypeTraits< F32 >::MIN <= position[ n ] && position[ n ] <= TypeTraits< F32 >::MAX, "Invalid float in object coordinate!" ); + + NodeType* node = Deref( mReferenceObject ).getMinTrackingNode( n ); + node->mFlags.set( NodeType::FLAG_Reference ); + + node->setPosition( position[ n ] ); + + _insertTrackingNode( n, node ); + } + + // Update the surroundings of the reference object + // in the tracking lists for each dimension. + + for( U32 n = 0; n < NUM_DIMENSIONS; ++ n ) + { + //TODO: this could be optimized by dynamically determining whether to walk upwards + // or downwards depending on which span has fewer nodes; finding that out is not immediately + // obvious, though + + // Walk from the left bound node upwards until we reach the + // reference object's marker. Everything that has its max node + // past the reference object is in scope. + + F32 referencePos = Deref( mReferenceObject ).getMinTrackingNode( n )->getPosition(); + for( NodeType* node = mTrackingList[ n ][ 0 ]->getNext(); node->getPosition() < referencePos; node = node->getNext() ) + if( !node->isMax() && node->getOpposite()->getPosition() > referencePos ) + { + node->getObject()->setInScope( n, true ); + + // If this is the last dimension we're working on and + // the current object is in-scope on all dimension, + // promote to in-scope status. + + if( n == ( NUM_DIMENSIONS - 1 ) && node->getObject()->isInScope() ) + _onScopeIn( ( Object ) node->getObject() ); + } + } +} + +//----------------------------------------------------------------------------- + +template< int NUM_DIMENSIONS, class Object > +void ScopeTracker< NUM_DIMENSIONS, Object >::_uninitTracking() +{ + PROFILE_SCOPE( ScopeTracker_uninitTracking ); + + AssertFatal( bool( getReferenceObject() ), + "ScopeTracker::_uninitTracking - can only be called with a valid reference object" ); + + // Put all objects currently in scope, out of scope. + + for( U32 n = 0; n < NUM_DIMENSIONS; ++ n ) + { + U32 referencePos = Deref( mReferenceObject ).getMinTrackingNode( n )->getPosition(); + for( NodeType* node = mTrackingList[ n ][ 0 ]->getNext(); node->getPosition() < referencePos; node = node->getNext() ) + { + if( node->getObject()->isInScope() ) + _onScopeOut( ( Object ) node->getObject() ); + node->getObject()->clearScopeMask(); + } + } + + // Remove the reference object's tracking nodes. + + for( U32 n = 0; n < NUM_DIMENSIONS; ++ n ) + _removeTrackingNode( n, Deref( mReferenceObject ).getMinTrackingNode( n ) ); +} + +//----------------------------------------------------------------------------- + +template< int NUM_DIMENSIONS, class Object > +void ScopeTracker< NUM_DIMENSIONS, Object >::debugDump() +{ + for( U32 n = 0; n < NUM_DIMENSIONS; ++ n ) + { + Con::printf( "Dimension %i", n ); + Con::printf( "----------------" ); + + for( NodeType* node = mTrackingList[ n ][ 0 ]; node != NULL; node = node->getNext() ) + { + String desc; + if( node->getObject() ) + { + Object object = ( Object ) node->getObject(); + desc = Deref( object ).describeSelf(); + } + + Con::printf( "pos=%f, type=%s, scope=%s, object=%s", + node->getPosition(), + node->isReference() ? "reference" : node->isMin() ? "min" : "max", + node->getObject() ? node->getObject()->isInScope( n ) ? "1" : "0" : "0", + desc.c_str() ); + } + + Con::printf( "" ); + } +} + +#endif // !_SCOPETRACKER_H_ diff --git a/Engine/source/util/settings.cpp b/Engine/source/util/settings.cpp new file mode 100644 index 000000000..19de55673 --- /dev/null +++ b/Engine/source/util/settings.cpp @@ -0,0 +1,734 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "util/settings.h" +#include "console/consoleTypes.h" +#include "console/SimXMLDocument.h" + +IMPLEMENT_CONOBJECT(Settings); + +ConsoleDocClass( Settings, + "@brief Class used for writing out preferences and settings for editors\n\n" + "Not intended for game development, for editors or internal use only.\n\n " + "@internal"); + +Settings::Settings() +{ + mFile = ""; + mSearchPos = 0; +} + +Settings::~Settings() +{ + +} + +void Settings::initPersistFields() +{ + addField("file", TypeStringFilename, Offset(mFile, Settings), "The file path and name to be saved to and loaded from."); + + Parent::initPersistFields(); +} + +void Settings::setDefaultValue(const UTF8 *settingName, const UTF8 *settingValue, const UTF8 *settingType) +{ + String baseName; + buildGroupString(baseName, settingName); + String name = baseName + "_default"; + StringTableEntry nameEntry = StringTable->insert(name.c_str()); + String type = baseName + "_type"; + StringTableEntry typeEntry = StringTable->insert(type.c_str()); + + setModStaticFields(false); + setDataField(nameEntry, NULL, settingValue); + setDataField(typeEntry, NULL, settingType); + setModStaticFields(true); +} + +void Settings::setValue(const UTF8 *settingName, const UTF8 *settingValue) +{ + String name; + buildGroupString(name, settingName); + StringTableEntry nameEntry = StringTable->insert(name.c_str()); + + setModStaticFields(false); + setDataField(nameEntry, NULL, settingValue); + setModStaticFields(true); +} + +const UTF8 *Settings::value(const UTF8 *settingName, const UTF8 *defaultValue) +{ + String name; + buildGroupString(name, settingName); + + StringTableEntry nameEntry = StringTable->insert(name.c_str()); + name += "_default"; + StringTableEntry defaultNameEntry = StringTable->insert(name.c_str()); + + // we do this setModStaticFields call to make sure our get/set calls + // don't grab a regular field, don't want to stomp anything + setModStaticFields(false); + const UTF8 *value = getDataField(nameEntry, NULL); + const UTF8 *storedDefaultValue = getDataField(defaultNameEntry, NULL); + setModStaticFields(true); + + if(dStrcmp(value, "") != 0) + return value; + else if(dStrcmp(storedDefaultValue, "") != 0) + return storedDefaultValue; + else + return defaultValue; +} + +void Settings::remove(const UTF8 *settingName, bool includeDefaults) +{ + // Fetch Dynamic-Field Dictionary. + SimFieldDictionary* pFieldDictionary = getFieldDictionary(); + + // Any Field Dictionary? + if ( pFieldDictionary == NULL ) + { + // No, so we're done. + return; + } + + String name; + buildGroupString(name, settingName); + StringTableEntry nameEntry = StringTable->insert(name.c_str()); + StringTableEntry nameEntryDefault = StringTable->insert( String::ToString("%s%s",name.c_str(), "_default") ); + + // Iterate fields. + for ( SimFieldDictionaryIterator itr(pFieldDictionary); *itr; ++itr ) + { + // Fetch Field Entry. + SimFieldDictionary::Entry* fieldEntry = *itr; + + // is this a field of our current group + if ( (dStrcmp(nameEntry, "") == 0) || + dStrcmp( nameEntry, fieldEntry->slotName ) == 0 || + (includeDefaults && dStrcmp( nameEntryDefault, fieldEntry->slotName ) == 0) ) + { + // Yes, so remove it. + pFieldDictionary->setFieldValue( fieldEntry->slotName, "" ); + } + } +} + +void Settings::buildGroupString(String &name, const UTF8 *settingName) +{ + // here we want to loop through the stack and build a "/" seperated string + // representing the entire current group stack that gets pre-pended to the + // setting name passed in + if(mGroupStack.size() > 0) + { + for(S32 i=0; i < mGroupStack.size(); i++) + { + S32 pos = 0; + if(name.size() > 0) + pos = name.size()-1; + + // tack on the "/" in front if this isn't the first + if(i == 0) + { + name.insert(pos, mGroupStack[i]); + } else + { + name.insert(pos, "/"); + name.insert(pos+1, mGroupStack[i]); + } + } + + // tack on a final "/" + name.insert(name.size()-1, "/"); + if(dStrlen(settingName) > 0) + name.insert(name.size()-1, settingName); + } else + name = settingName; +} + +void Settings::clearAllFields() +{ + // Fetch Dynamic-Field Dictionary. + SimFieldDictionary* pFieldDictionary = getFieldDictionary(); + + // Any Field Dictionary? + if ( pFieldDictionary == NULL ) + { + // No, so we're done. + return; + } + + // Iterate fields. + for ( SimFieldDictionaryIterator itr(pFieldDictionary); *itr; ++itr ) + { + // Fetch Field Entry. + SimFieldDictionary::Entry* fieldEntry = *itr; + + // don't remove default field values + if (dStrEndsWith(fieldEntry->slotName, "_default")) + continue; + + // remove it. + pFieldDictionary->setFieldValue( fieldEntry->slotName, "" ); + } +} + +bool Settings::write() +{ + // Fetch Dynamic-Field Dictionary. + SimFieldDictionary* pFieldDictionary = getFieldDictionary(); + + // Any Field Dictionary? + if ( pFieldDictionary == NULL ) + { + // No, so we're done. + return false; + } + +/* + // Iterate fields. + for ( SimFieldDictionaryIterator itr(pFieldDictionary); *itr; ++itr ) + { + // Fetch Field Entry. + SimFieldDictionary::Entry* fieldEntry = *itr; + + String check(fieldEntry->slotName); + String::SizeType pos = check.find("_default"); + if(pos != String::NPos) + continue; + + // let's build our XML doc + document->pushNewElement("dynamicField"); + document->setAttribute("name", fieldEntry->slotName); + document->addText(fieldEntry->value); + document->popElement(); + } +*/ + SimXMLDocument *document = new SimXMLDocument(); + document->registerObject(); + document->addHeader(); + + document->pushNewElement(getName()); + + SettingSaveNode *node = new SettingSaveNode(); + // Iterate fields. + for ( SimFieldDictionaryIterator itr(pFieldDictionary); *itr; ++itr ) + { + // Fetch Field Entry. + SimFieldDictionary::Entry* fieldEntry = *itr; + + String check(fieldEntry->slotName); + if(check.find("_default") != String::NPos || check.find("_type") != String::NPos) + continue; + + node->addValue(fieldEntry->slotName, fieldEntry->value); + } + + node->buildDocument(document, true); + node->clear(); + delete node; + + bool saved = document->saveFile(mFile.c_str()); + document->deleteObject(); + + if(saved) + return true; + else + return false; +} + +bool Settings::read() +{ + SimXMLDocument *document = new SimXMLDocument(); + document->registerObject(); + + bool success = true; + if(document->loadFile(mFile.c_str())) + { + clearAllFields(); + + // set our base element + if(document->pushFirstChildElement(getName())) + { + setModStaticFields(false); + readLayer(document); + setModStaticFields(true); + } + else + success = false; + } + else + success = false; + + document->deleteObject(); + + return success; +} + +void Settings::readLayer(SimXMLDocument *document, String groupStack) +{ + for(S32 i=0; document->pushChildElement(i); i++) + { + bool groupCount = 0; + const UTF8 *type = document->elementValue(); + const UTF8 *name = document->attribute("name"); + const UTF8 *value = document->getText(); + + if(dStrcmp(type, "Group") == 0) + { + String newStack = groupStack; + + if(!groupStack.isEmpty()) + newStack += "/"; + + newStack += name; + readLayer(document, newStack); + groupCount++; + } else if(dStrcmp(type, "Setting") == 0) + { + String nameString = groupStack; + + if(!groupStack.isEmpty()) + nameString += "/"; + + nameString += name; + setDataField(StringTable->insert(nameString.c_str()), NULL, value); + } + + document->popElement(); + } +} + +void Settings::beginGroup(const UTF8 *groupName, bool fromStart) +{ + // check if we want to clear the stack + if(fromStart) + clearGroups(); + + mGroupStack.push_back(String(groupName)); +} + +void Settings::endGroup() +{ + if(mGroupStack.size() > 0) + mGroupStack.pop_back(); +} + +void Settings::clearGroups() +{ + mGroupStack.clear(); +} + +const UTF8 *Settings::getCurrentGroups() +{ + // we want to return a string with our group setup + String returnString; + for(S32 i=0; iinsert(returnString.c_str()); +} +/* +S32 Settings::buildSearchList( const char* pattern, bool deepSearch, bool includeDefaults ) +{ + mSearchResults.clear(); + + SimFieldDictionary* fieldDictionary = getFieldDictionary(); + // Get the dynamic field count + if ( !fieldDictionary ) + return -1; + + for (SimFieldDictionaryIterator itr(fieldDictionary); *itr; ++itr) + { + // Fetch Field Entry. + SimFieldDictionary::Entry* fieldEntry = *itr; + + // Compare strings, store proper results in vector + String extendedPath = String::ToString(fieldEntry->slotName); + String::SizeType start(0); + String::SizeType slashPos = extendedPath.find('/', 0, String::Right); + String shortPath = extendedPath.substr( start, slashPos ); + + if( deepSearch ) + { + if( shortPath.find( pattern ) != -1 ) + { + if( !includeDefaults && extendedPath.find("_default") != -1 ) + continue; + + String listMember = String::ToString(fieldEntry->value); + listMember.insert(start, " " ); + listMember.insert(start, String::ToString(fieldEntry->slotName) ); + + mSearchResults.push_back( listMember ); + } + } + else + { + if( shortPath.compare( pattern ) == 0 ) + { + if( !includeDefaults && extendedPath.find("_default") != -1 ) + continue; + + String listMember = String::ToString(fieldEntry->value); + listMember.insert(start, " " ); + listMember.insert(start, String::ToString(fieldEntry->slotName) ); + + mSearchResults.push_back( listMember ); + } + } + } + + return mSearchResults.size(); +} +*/ +const char* Settings::findFirstValue( const char* pattern, bool deepSearch, bool includeDefaults ) +{ + mSearchResults.clear(); + + SimFieldDictionary* fieldDictionary = getFieldDictionary(); + // Get the dynamic field count + if ( !fieldDictionary ) + return ""; + + for (SimFieldDictionaryIterator itr(fieldDictionary); *itr; ++itr) + { + // Fetch Field Entry. + SimFieldDictionary::Entry* fieldEntry = *itr; + + // Compare strings, store proper results in vector + String extendedPath = String::ToString(fieldEntry->slotName); + String::SizeType start(0); + String::SizeType slashPos = extendedPath.find('/', 0, String::Right); + String shortPath = extendedPath.substr( start, slashPos ); + + if( deepSearch ) + { + if( shortPath.find( pattern ) != -1 ) + { + if( !includeDefaults && extendedPath.find("_default") != -1 ) + continue; + + String listMember = String::ToString(fieldEntry->slotName); + //listMember.insert(start, " " ); + //listMember.insert(start, String::ToString(fieldEntry->slotName) ); + + mSearchResults.push_back( listMember ); + } + } + else + { + if( shortPath.compare( pattern ) == 0 ) + { + if( !includeDefaults && extendedPath.find("_default") != -1 ) + continue; + + String listMember = String::ToString(fieldEntry->slotName); + //listMember.insert(start, " " ); + //listMember.insert(start, String::ToString(fieldEntry->slotName) ); + + mSearchResults.push_back( listMember ); + } + } + } + + if( mSearchResults.size() < 1 ) + { + Con::errorf("findFirstValue() : Pattern not found"); + return ""; + } + + mSearchPos = 0; + return mSearchResults[mSearchPos]; +} + +const char* Settings::findNextValue() +{ + if ( mSearchPos + 1 >= mSearchResults.size() ) + return ""; + mSearchPos++; + return mSearchResults[mSearchPos]; +} + +// make sure to replace the strings +ConsoleMethod(Settings, findFirstValue, const char*, 2, 5, "settingObj.findFirstValue();") +{ + if( argc == 3 ) + return object->findFirstValue( argv[2] ); + else if( argc == 4 ) + return object->findFirstValue( argv[2], argv[3] ); + else if( argc == 5 ) + return object->findFirstValue( argv[2], argv[3], argv[4] ); + else + return ""; +} + +ConsoleMethod(Settings, findNextValue, const char*, 2, 2, "settingObj.findNextValue();") +{ + return object->findNextValue(); +} +/* +ConsoleMethod(Settings, buildSearchList, void, 2, 2, "settingObj.buildSearchList();") +{ + object->buildSearchList( "foobar" ); +} +*/ +void SettingSaveNode::addValue(const UTF8 *name, const UTF8 *value) +{ + String nameString(name); + S32 groupCount = getGroupCount(nameString); + SettingSaveNode *parentNode = this; + + // let's check to make sure all these groups exist already + for(S32 i=0; imGroupNodes.size(); j++) + { + SettingSaveNode *node = parentNode->mGroupNodes[j]; + + if(!node->mIsGroup) + continue; + + if(node->mName.compare(groupName) == 0) + { + parentNode = node; + found = true; + break; + } + } + + // not found, so we create it + if(!found) + { + SettingSaveNode *node = new SettingSaveNode(groupName, true); + parentNode->mGroupNodes.push_back(node); + parentNode = node; + } + } + } + + // now we can properly set our actual value + String settingNameString = getSettingName(name); + String valueString(value); + SettingSaveNode *node = new SettingSaveNode(settingNameString, valueString); + parentNode->mSettingNodes.push_back(node); +} + +S32 SettingSaveNode::getGroupCount(const String &name) +{ + String::SizeType pos = 0; + S32 count = 0; + + // loop through and count our exiting groups + while(pos != String::NPos) + { + pos = name.find("/", pos + 1); + if(pos != String::NPos) + count++; + } + + return count; +} + +String SettingSaveNode::getGroup(const String &name, S32 num) +{ + String::SizeType pos = 0; + String::SizeType lastPos = 0; + S32 count = 0; + + while(pos != String::NPos) + { + lastPos = pos; + pos = name.find("/", pos + 1); + + if(count == num) + { + String::SizeType startPos = lastPos; + + if(count > 0) + startPos++; + + if(pos == String::NPos) + return name.substr(startPos, name.length() - (startPos)); + else + return name.substr(startPos, pos - startPos); + } + + count++; + } + + return String(""); +} + +String SettingSaveNode::getSettingName(const String &name) +{ + String::SizeType pos = name.find("/", 0, String::Right); + + if(pos == String::NPos) + return String(name); + else + return name.substr(pos+1, name.length() - (pos+1)); +} + +void SettingSaveNode::clear() +{ + for( U32 i = 0, num = mGroupNodes.size(); i < num; ++ i ) + delete mGroupNodes[ i ]; + for( U32 i = 0, num = mSettingNodes.size(); i < num; ++ i ) + delete mSettingNodes[ i ]; + + mGroupNodes.clear(); + mSettingNodes.clear(); +} + +void SettingSaveNode::buildDocument(SimXMLDocument *document, bool skipWrite) +{ + // let's build our XML doc + if(mIsGroup && !skipWrite) + { + document->pushNewElement("Group"); + document->setAttribute("name", mName); + } + + if(!mIsGroup && !skipWrite) + { + document->pushNewElement("Setting"); + document->setAttribute("name", mName); + document->addText(mValue); + } else + { + for(int i=0; ibuildDocument(document); + } + + for(int i=0; ibuildDocument(document); + } + } + + if(!skipWrite) + document->popElement(); +} + +ConsoleMethod(Settings, setValue, void, 3, 4, "settingObj.setValue(settingName, value);") +{ + const char *fieldName = StringTable->insert( argv[2] ); + + if(argc == 3) + object->setValue( fieldName); + else if(argc == 4) + object->setValue( fieldName, argv[3] ); +} + +ConsoleMethod(Settings, setDefaultValue, void, 4, 4, "settingObj.setDefaultValue(settingName, value);") +{ + const char *fieldName = StringTable->insert( argv[2] ); + object->setDefaultValue( fieldName, argv[3] ); +} + +ConsoleMethod(Settings, value, const char*, 3, 4, "settingObj.value(settingName, defaultValue);") +{ + const char *fieldName = StringTable->insert( argv[2] ); + + if(argc == 3) + return object->value( fieldName ); + if(argc == 4) + return object->value( fieldName, argv[3] ); + + return ""; +} + +ConsoleMethod(Settings, remove, void, 3, 4, "settingObj.remove(settingName, includeDefaults = false);") +{ + // there's a problem with some fields not being removed properly, but works if you run it twice, + // a temporary solution for now is simply to call the remove twice + if(argc == 3) + { + object->remove( argv[2] ); + object->remove( argv[2] ); + } + else if(argc == 4) + { + object->remove( argv[2], argv[3] ); + object->remove( argv[2], argv[3] ); + } +} + +ConsoleMethod(Settings, write, bool, 2, 2, "%success = settingObj.write();") +{ + TORQUE_UNUSED(argc); TORQUE_UNUSED(argv); + return object->write(); +} + +ConsoleMethod(Settings, read, bool, 2, 2, "%success = settingObj.read();") +{ + TORQUE_UNUSED(argc); TORQUE_UNUSED(argv); + return object->read(); +} + +ConsoleMethod(Settings, beginGroup, void, 3, 4, "settingObj.beginGroup(groupName, fromStart = false);") +{ + if(argc == 3) + object->beginGroup( argv[2] ); + if(argc == 4) + object->beginGroup( argv[2], dAtob(argv[3]) ); +} + +ConsoleMethod(Settings, endGroup, void, 2, 2, "settingObj.endGroup();") +{ + TORQUE_UNUSED(argc); TORQUE_UNUSED(argv); + object->endGroup(); +} + +ConsoleMethod(Settings, clearGroups, void, 2, 2, "settingObj.clearGroups();") +{ + TORQUE_UNUSED(argc); TORQUE_UNUSED(argv); + object->clearGroups(); +} + +ConsoleMethod(Settings, getCurrentGroups, const char*, 2, 2, "settingObj.getCurrentGroups();") +{ + return object->getCurrentGroups(); +} \ No newline at end of file diff --git a/Engine/source/util/settings.h b/Engine/source/util/settings.h new file mode 100644 index 000000000..2438504ac --- /dev/null +++ b/Engine/source/util/settings.h @@ -0,0 +1,107 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _SETTINGS_H_ +#define _SETTINGS_H_ + +#include "console/simBase.h" +#include "core/util/tVector.h" + +class SimXMLDocument; + +/// +class Settings : public SimObject +{ +private: + FileName mFile; + Vector mGroupStack; + S32 mSearchPos; + Vector mSearchResults; + +public: + Settings(); + virtual ~Settings(); + + // Required in all ConsoleObject subclasses. + typedef SimObject Parent; + DECLARE_CONOBJECT(Settings); + static void initPersistFields(); + + /// These will set and get the values, with an option default value passed in to the get + void setDefaultValue(const UTF8 *settingName, const UTF8 *settingValue, const UTF8 *settingType=""); + void setValue(const UTF8 *settingName, const UTF8 *settingValue = ""); + const UTF8 *value(const UTF8 *settingName, const UTF8 *defaultValue = ""); + void remove(const UTF8 *settingName, bool includeDefaults = false); + void clearAllFields(); + bool write(); + bool read(); + void readLayer(SimXMLDocument *document, String groupStack = String("")); + + void beginGroup(const UTF8 *groupName, bool fromStart = false); + void endGroup(); + void clearGroups(); + + void buildGroupString(String &name, const UTF8 *settingName); + const UTF8 *getCurrentGroups(); + + //S32 buildSearchList(const char* pattern, bool deepSearch = false, bool defaultsSearch = false); + const char* findFirstValue(const char* pattern, bool deepSearch = false, bool includeDefaults = false); + const char* findNextValue(); +}; + +class SettingSaveNode +{ +public: + Vector mGroupNodes; + Vector mSettingNodes; + + String mName; + String mValue; + bool mIsGroup; + + SettingSaveNode(){}; + SettingSaveNode(const String &name, bool isGroup = false) + { + mName = name; + mIsGroup = isGroup; + } + SettingSaveNode(const String &name, const String &value) + { + mName = name; + mValue = value; + mIsGroup = false; + } + ~SettingSaveNode() + { + clear(); + } + + void addValue(const UTF8 *name, const UTF8 *value); + S32 getGroupCount(const String &name); + String getGroup(const String &name, S32 num); + String getSettingName(const String &name); + void buildDocument(SimXMLDocument *document, bool skipWrite = false); + + void clear(); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/util/tempAlloc.h b/Engine/source/util/tempAlloc.h new file mode 100644 index 000000000..40d9a222f --- /dev/null +++ b/Engine/source/util/tempAlloc.h @@ -0,0 +1,59 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TEMPALLOC_H_ +#define _TEMPALLOC_H_ + +#ifndef _PLATFORM_H_ +# include "platform/platform.h" +#endif + + +template< typename T > +struct TempAlloc +{ + T* ptr; + U32 size; + + TempAlloc() + : size( 0 ), ptr( 0 ) {} + TempAlloc( U32 size ) + : size( size ) + { + ptr = ( T* ) dMalloc( size * sizeof( T ) ); + } + ~TempAlloc() + { + if( ptr ) + dFree( ptr ); + } + operator T*() + { + return ptr; + } + +private: + // Not safe. + TempAlloc( const TempAlloc& ) {} +}; + +#endif // _TEMPALLOC_H_ diff --git a/Engine/source/util/triBoxCheck.cpp b/Engine/source/util/triBoxCheck.cpp new file mode 100644 index 000000000..c314fd3e4 --- /dev/null +++ b/Engine/source/util/triBoxCheck.cpp @@ -0,0 +1,192 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// AABB-triangle overlap test code originally by Tomas Akenine-Möller +// Assisted by Pierre Terdiman and David Hunt +// http://www.cs.lth.se/home/Tomas_Akenine_Moller/code/ +// Ported to TSE by BJG, 2005-4-14 +// Modified to avoid a lot of copying by ASM, 2007-9-28 +//----------------------------------------------------------------------------- + +#include "util/triBoxCheck.h" + +#define FINDMINMAX(x0,x1,x2,theMin,theMax) \ + theMin = theMax = x0; \ + if(x1theMax) theMax=x1;\ + if(x2theMax) theMax=x2; + +static bool planeBoxOverlap(const Point3F &normal, const Point3F &vert, const Point3F &maxbox) +{ + S32 q; + F32 v; + Point3F vmin, vmax; + + for(q=0;q<=2;q++) + { + v=vert[q]; + if(normal[q]>0.0f) + { + vmin[q]=-maxbox[q] - v; + vmax[q]= maxbox[q] - v; + } + else + { + vmin[q]= maxbox[q] - v; + vmax[q]=-maxbox[q] - v; + } + } + + if(mDot(normal, vmin) > 0.f) + return false; + + if(mDot(normal, vmax) >= 0.f) + return true; + + return false; +} + + +/*======================== X-tests ========================*/ +#define AXISTEST_X01(a, b, fa, fb) \ + p0 = a*v0.y - b*v0.z; \ + p2 = a*v2.y - b*v2.z; \ + if(p0rad || max<-rad) return false; + +#define AXISTEST_X2(a, b, fa, fb) \ + p0 = a*v0.y - b*v0.z; \ + p1 = a*v1.y - b*v1.z; \ + if(p0rad || max<-rad) return false; + +/*======================== Y-tests ========================*/ +#define AXISTEST_Y02(a, b, fa, fb) \ + p0 = -a*v0.x + b*v0.z; \ + p2 = -a*v2.x + b*v2.z; \ + if(p0rad || max<-rad) return false; + +#define AXISTEST_Y1(a, b, fa, fb) \ + p0 = -a*v0.x + b*v0.z; \ + p1 = -a*v1.x + b*v1.z; \ + if(p0rad || max<-rad) return false; + +/*======================== Z-tests ========================*/ + +#define AXISTEST_Z12(a, b, fa, fb) \ + p1 = a*v1.x - b*v1.y; \ + p2 = a*v2.x - b*v2.y; \ + if(p2rad || max<-rad) return false; + +#define AXISTEST_Z0(a, b, fa, fb) \ + p0 = a*v0.x - b*v0.y; \ + p1 = a*v1.x - b*v1.y; \ + if(p0rad || max<-rad) return false; + +bool triBoxOverlap(const Point3F &boxcenter, const Point3F &boxhalfsize, const Point3F triverts[3]) +{ + /* use separating axis theorem to test overlap between triangle and box */ + /* need to test for overlap in these directions: */ + /* 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle */ + /* we do not even need to test these) */ + /* 2) normal of the triangle */ + /* 3) crossproduct(edge from tri, {x,y,z}-directin) */ + /* this gives 3x3=9 more tests */ + + Point3F v0,v1,v2; + + F32 min,max,p0,p1,p2,rad,fex,fey,fez; // -NJMP- "d" local variable removed + Point3F normal,e0,e1,e2; + + /* This is the fastest branch on Sun */ + /* move everything so that the boxcenter is in (0,0,0) */ + v0 = triverts[0] - boxcenter; + v1 = triverts[1] - boxcenter; + v2 = triverts[2] - boxcenter; + + /* compute triangle edges */ + e0 = v1 - v0; /* tri edge 0 */ + e1 = v2 - v1; /* tri edge 1 */ + e2 = v0 - v2; /* tri edge 2 */ + + /* Bullet 3: */ + /* test the 9 tests first (this was faster) */ + fex = mFabs(e0.x); + fey = mFabs(e0.y); + fez = mFabs(e0.z); + AXISTEST_X01(e0.z, e0.y, fez, fey); + AXISTEST_Y02(e0.z, e0.x, fez, fex); + AXISTEST_Z12(e0.y, e0.x, fey, fex); + + fex = mFabs(e1.x); + fey = mFabs(e1.y); + fez = mFabs(e1.z); + AXISTEST_X01(e1.z, e1.y, fez, fey); + AXISTEST_Y02(e1.z, e1.x, fez, fex); + AXISTEST_Z0(e1.y, e1.x, fey, fex); + + fex = mFabs(e2.x); + fey = mFabs(e2.y); + fez = mFabs(e2.z); + AXISTEST_X2(e2.z, e2.y, fez, fey); + AXISTEST_Y1(e2.z, e2.x, fez, fex); + AXISTEST_Z12(e2.y, e2.x, fey, fex); + + /* Bullet 1: */ + /* first test overlap in the {x,y,z}-directions */ + /* find min, max of the triangle each direction, and test for overlap in */ + /* that direction -- this is equivalent to testing a minimal AABB around */ + /* the triangle against the AABB */ + + /* test in X-direction */ + FINDMINMAX(v0.x,v1.x,v2.x,min,max); + if(min>boxhalfsize.x || max<-boxhalfsize.x) return false; + + /* test in Y-direction */ + FINDMINMAX(v0.y,v1.y,v2.y,min,max); + if(min>boxhalfsize.y || max<-boxhalfsize.y) return false; + + /* test in Z-direction */ + FINDMINMAX(v0.z,v1.z,v2.z,min,max); + if(min>boxhalfsize.z || max<-boxhalfsize.z) return false; + + /* Bullet 2: */ + /* test if the box intersects the plane of the triangle */ + /* compute plane equation of triangle: normal*x+d=0 */ + normal = mCross(e0, e1); + + if(!planeBoxOverlap(normal,v0,boxhalfsize)) return false; + + return true; /* box and triangle overlaps */ +} diff --git a/Engine/source/util/triBoxCheck.h b/Engine/source/util/triBoxCheck.h new file mode 100644 index 000000000..8d41bb451 --- /dev/null +++ b/Engine/source/util/triBoxCheck.h @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// AABB-triangle overlap test code originally by Tomas Akenine-Möller +// Assisted by Pierre Terdiman and David Hunt +// http://www.cs.lth.se/home/Tomas_Akenine_Moller/code/ +// Ported to TSE by BJG, 2005-4-14 +// Modified to avoid a lot of copying by ASM, 2007-9-28 +//----------------------------------------------------------------------------- + +#ifndef _TRIBOXCHECK_H_ +#define _TRIBOXCHECK_H_ + +#include "math/mPoint3.h" +#include "math/mBox.h" + +bool triBoxOverlap(const Point3F &boxcenter, const Point3F &boxhalfsize, const Point3F triverts[3]); + +/// Massage stuff into right format for triBoxOverlap test. This is really +/// just a helper function - use the other version if you want to be fast! +inline bool triBoxOverlap(Box3F box, Point3F a, Point3F b, Point3F c) +{ + Point3F halfSize(box.len_x() / 2.f, box.len_y() / 2.f, box.len_z() / 2.f); + + Point3F center; + box.getCenter(¢er); + + Point3F verts[3] = {a,b,c}; + + return triBoxOverlap(center, halfSize, verts); +} + +#endif \ No newline at end of file diff --git a/Engine/source/util/triRayCheck.cpp b/Engine/source/util/triRayCheck.cpp new file mode 100644 index 000000000..084eda21b --- /dev/null +++ b/Engine/source/util/triRayCheck.cpp @@ -0,0 +1,265 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Ray to triangle intersection test code originally by Tomas Akenine-Möller +// and Ben Trumbore. +// http://www.cs.lth.se/home/Tomas_Akenine_Moller/code/ +// Ported to TGE by DAW, 2005-7-15 +//----------------------------------------------------------------------------- + +#include "util/triRayCheck.h" +#include "math/mPlane.h" + +#define EPSILON 0.000001 +#define CROSS(dest,v1,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]; +#define DOT(v1,v2) (v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2]) +#define SUB(dest,v1,v2) \ + dest[0]=v1[0]-v2[0]; \ + dest[1]=v1[1]-v2[1]; \ + dest[2]=v1[2]-v2[2]; + +bool intersect_triangle(Point3F orig, Point3F dir, + Point3F vert0, Point3F vert1, Point3F vert2, + F32& t, F32& u, F32& v) +{ + Point3F edge1, edge2, tvec, pvec, qvec; + F32 det,inv_det; + + /* find vectors for two edges sharing vert0 */ + edge1.x = vert1.x - vert0.x; + edge1.y = vert1.y - vert0.y; + edge1.z = vert1.z - vert0.z; + edge2.x = vert2.x - vert0.x; + edge2.y = vert2.y - vert0.y; + edge2.z = vert2.z - vert0.z; + + /* begin calculating determinant - also used to calculate U parameter */ + //CROSS(pvec, dir, edge2); + mCross(dir, edge2, &pvec); + + /* if determinant is near zero, ray lies in plane of triangle */ + //det = DOT(edge1, pvec); + det = mDot(edge1, pvec); + +#ifdef TEST_CULL /* define TEST_CULL if culling is desired */ + if (det < EPSILON) + return 0; + + /* calculate distance from vert0 to ray origin */ + SUB(tvec, orig, vert0); + + /* calculate U parameter and test bounds */ + *u = DOT(tvec, pvec); + if (*u < 0.0 || *u > det) + return 0; + + /* prepare to test V parameter */ + CROSS(qvec, tvec, edge1); + + /* calculate V parameter and test bounds */ + *v = DOT(dir, qvec); + if (*v < 0.0 || *u + *v > det) + return 0; + + /* calculate t, scale parameters, ray intersects triangle */ + *t = DOT(edge2, qvec); + inv_det = 1.0 / det; + *t *= inv_det; + *u *= inv_det; + *v *= inv_det; +#else /* the non-culling branch */ + if (det > -EPSILON && det < EPSILON) + return false; + inv_det = 1.0 / det; + + /* calculate distance from vert0 to ray origin */ + //SUB(tvec, orig, vert0); + tvec.x = orig.x - vert0.x; + tvec.y = orig.y - vert0.y; + tvec.z = orig.z - vert0.z; + + /* calculate U parameter and test bounds */ +// *u = DOT(tvec, pvec) * inv_det; + u = mDot(tvec, pvec) * inv_det; + if (u < 0.0 || u > 1.0) + return false; + + /* prepare to test V parameter */ + //CROSS(qvec, tvec, edge1); + mCross(tvec, edge1, &qvec); + + /* calculate V parameter and test bounds */ +// *v = DOT(dir, qvec) * inv_det; + v = mDot(dir, qvec) * inv_det; + if (v < 0.0 || u + v > 1.0) + return false; + + /* calculate t, ray intersects triangle */ +// *t = DOT(edge2, qvec) * inv_det; + t = mDot(edge2, qvec) * inv_det; +#endif + return true; +} + +//*** Taken from TSE, and based on the above +bool castRayTriangle(Point3F orig, Point3F dir, + Point3F vert0, Point3F vert1, Point3F vert2, + F32 &t, Point2F &bary) +{ + Point3F tvec, qvec; + + // Find vectors for two edges sharing vert0 + const Point3F edge1 = vert1 - vert0; + const Point3F edge2 = vert2 - vert0; + + // Begin calculating determinant - also used to calculate U parameter. + const Point3F pvec = mCross(dir, edge2); + + // If determinant is near zero, ray lies in plane of triangle. + const F32 det = mDot(edge1, pvec); + + if (det > 0.00001) + { + // calculate distance from vert0 to ray origin + tvec = orig - vert0; + + // calculate U parameter and test bounds + bary.x = mDot(tvec, pvec); // bary.x is really bary.u... + if (bary.x < 0.0 || bary.x > det) + return false; + + // prepare to test V parameter + qvec = mCross(tvec, edge1); + + // calculate V parameter and test bounds + bary.y = mDot(dir, qvec); // bary.y is really bary.v + if (bary.y < 0.0 || (bary.x + bary.y) > det) + return false; + + } + else if(det < -0.00001) + { + // calculate distance from vert0 to ray origin + tvec = orig - vert0; + + // calculate U parameter and test bounds + bary.x = mDot(tvec, pvec); + if (bary.x > 0.0 || bary.x < det) + return false; + + // prepare to test V parameter + qvec = mCross(tvec, edge1); + + // calculate V parameter and test bounds + bary.y = mDot(dir, qvec); + if (bary.y > 0.0 || (bary.x + bary.y) < det) + return false; + } + else + return false; // ray is parallel to the plane of the triangle. + + const F32 inv_det = 1.0 / det; + + // calculate t, ray intersects triangle + t = mDot(edge2, qvec) * inv_det; + bary *= inv_det; + + //AssertFatal((t >= 0.f && t <=1.f), "AtlasGeomTracer::castRayTriangle - invalid t!"); + + // Hack, check the math here! + return (t >= 0.f && t <=1.f); +} + +bool castRayTriangle(const Point3D &orig, const Point3D &dir, + const Point3D &vert0, const Point3D &vert1, const Point3D &vert2) +{ + F64 t; + Point2D bary; + Point3D tvec, qvec; + + // Find vectors for two edges sharing vert0 + const Point3D edge1 = vert1 - vert0; + const Point3D edge2 = vert2 - vert0; + + // Begin calculating determinant - also used to calculate U parameter. + Point3D pvec; + mCross(dir, edge2, &pvec); + + // If determinant is near zero, ray lies in plane of triangle. + const F64 det = mDot(edge1, pvec); + + if (det > 0.00001) + { + // calculate distance from vert0 to ray origin + tvec = orig - vert0; + + // calculate U parameter and test bounds + bary.x = mDot(tvec, pvec); // bary.x is really bary.u... + if (bary.x < 0.0 || bary.x > det) + return false; + + // prepare to test V parameter + mCross(tvec, edge1, &qvec); + + // calculate V parameter and test bounds + bary.y = mDot(dir, qvec); // bary.y is really bary.v + if (bary.y < 0.0 || (bary.x + bary.y) > det) + return false; + + } + else if(det < -0.00001) + { + // calculate distance from vert0 to ray origin + tvec = orig - vert0; + + // calculate U parameter and test bounds + bary.x = mDot(tvec, pvec); + if (bary.x > 0.0 || bary.x < det) + return false; + + // prepare to test V parameter + mCross(tvec, edge1, &qvec); + + // calculate V parameter and test bounds + bary.y = mDot(dir, qvec); + if (bary.y > 0.0 || (bary.x + bary.y) < det) + return false; + } + else + return false; // ray is parallel to the plane of the triangle. + + const F32 inv_det = 1.0 / det; + + // calculate t, ray intersects triangle + t = mDot(edge2, qvec) * inv_det; + bary *= inv_det; + + //AssertFatal((t >= 0.f && t <=1.f), "AtlasGeomTracer::castRayTriangle - invalid t!"); + + // Hack, check the math here! + return (t >= 0.f && t <=1.f); +} + diff --git a/Engine/source/util/triRayCheck.h b/Engine/source/util/triRayCheck.h new file mode 100644 index 000000000..20f6cb49e --- /dev/null +++ b/Engine/source/util/triRayCheck.h @@ -0,0 +1,44 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// Ray to triangle intersection test code originally by Tomas Akenine-Möller +// and Ben Trumbore. +// http://www.cs.lth.se/home/Tomas_Akenine_Moller/code/ +// Ported to TGE by DAW, 2005-7-15 +//----------------------------------------------------------------------------- + +#ifndef _TRIRAYCHECK_H_ +#define _TRIRAYCHECK_H_ + +#include "math/mPoint2.h" +#include "math/mPoint3.h" + +bool intersect_triangle(Point3F orig, Point3F dir, + Point3F vert0, Point3F vert1, Point3F vert2, + F32& t, F32& u, F32& v); + +//*** Taken from TSE, but based on the above +bool castRayTriangle(Point3F orig, Point3F dir, Point3F vert0, Point3F vert1, Point3F vert2, F32 &t, Point2F &bary); +bool castRayTriangle(const Point3D &orig, const Point3D &dir, const Point3D &vert0, const Point3D &vert1, const Point3D &vert2); + +#endif // _TRIRAYCHECK_H_ diff --git a/Engine/source/util/undo.cpp b/Engine/source/util/undo.cpp new file mode 100644 index 000000000..d10cf6496 --- /dev/null +++ b/Engine/source/util/undo.cpp @@ -0,0 +1,596 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "util/undo.h" + +#include "console/console.h" +#include "console/consoleTypes.h" + +//----------------------------------------------------------------------------- +// UndoAction +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(UndoAction); +IMPLEMENT_CONOBJECT(UndoScriptAction); + +ConsoleDocClass( UndoAction, + "@brief An event which signals the editors to undo the last action\n\n" + "Not intended for game development, for editors or internal use only.\n\n " + "@internal"); + +ConsoleDocClass( UndoScriptAction, + "@brief Undo actions which can be created as script objects.\n\n" + "Not intended for game development, for editors or internal use only.\n\n " + "@internal"); + +UndoAction::UndoAction(const UTF8 *actionName) +{ + mActionName = actionName; + mUndoManager = NULL; +} + +UndoAction::~UndoAction() +{ + // If we are registered to an undo manager, make sure + // we get off its lists. + if( mUndoManager ) + mUndoManager->removeAction( this, true ); + + clearAllNotifications(); +} + +//----------------------------------------------------------------------------- +void UndoAction::initPersistFields() +{ + addField("actionName", TypeRealString, Offset(mActionName, UndoAction), + "A brief description of the action, for UI representation of this undo/redo action."); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- +void UndoAction::addToManager(UndoManager* theMan) +{ + if(theMan) + { + mUndoManager = theMan; + (*theMan).addAction(this); + } + else + { + mUndoManager = &UndoManager::getDefaultManager(); + mUndoManager->addAction(this); + } +} + +//----------------------------------------------------------------------------- +// CompoundUndoAction +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT( CompoundUndoAction ); + +ConsoleDocClass( CompoundUndoAction, + "@brief An undo action that is comprised of other undo actions.\n\n" + "Not intended for game development, for editors or internal use only.\n\n " + "@internal"); + +CompoundUndoAction::CompoundUndoAction( const UTF8 *actionName ) + : Parent( actionName ) +{ +} + +CompoundUndoAction::~CompoundUndoAction() +{ + while( !mChildren.empty() ) + { + UndoAction* action = mChildren.last(); + if( action->isProperlyAdded() ) + action->deleteObject(); + else + { + clearNotify( action ); // need to clear the delete notification manually in this case + delete action; + } + + mChildren.pop_back(); + } +} + +void CompoundUndoAction::addAction( UndoAction *action ) +{ + //AssertFatal( action->mUndoManager == NULL, "CompoundUndoAction::addAction, action already had an UndoManager." ); + mChildren.push_back( action ); + deleteNotify( action ); +} + +void CompoundUndoAction::undo() +{ + Vector::iterator itr = mChildren.end() - 1; + for ( ; itr != mChildren.begin() - 1; itr-- ) + (*itr)->undo(); +} + +void CompoundUndoAction::redo() +{ + Vector::iterator itr = mChildren.begin(); + for ( ; itr != mChildren.end(); itr++ ) + (*itr)->redo(); +} + +void CompoundUndoAction::onDeleteNotify( SimObject* object ) +{ + for( U32 i = 0; i < mChildren.size(); ++ i ) + if( mChildren[ i ] == object ) + mChildren.erase( i ); + + Parent::onDeleteNotify( object ); +} + +ConsoleMethod( CompoundUndoAction, addAction, void, 3, 3, "addAction( UndoAction )" ) +{ + UndoAction *action; + if ( Sim::findObject( argv[2], action ) ) + object->addAction( action ); +} + +//----------------------------------------------------------------------------- +// UndoManager +//----------------------------------------------------------------------------- +IMPLEMENT_CONOBJECT(UndoManager); + +ConsoleDocClass( UndoManager, + "@brief SimObject which adds, tracks, and deletes UndoAction objects.\n\n" + "Not intended for game development, for editors or internal use only.\n\n " + "@internal") + +UndoManager::UndoManager(U32 levels) +{ + VECTOR_SET_ASSOCIATION( mUndoStack ); + VECTOR_SET_ASSOCIATION( mRedoStack ); + VECTOR_SET_ASSOCIATION( mCompoundStack ); + + mNumLevels = levels; + // levels can be arbitrarily high, so we don't really want to reserve(levels). + mUndoStack.reserve(10); + mRedoStack.reserve(10); +} + +//----------------------------------------------------------------------------- +UndoManager::~UndoManager() +{ + clearStack(mUndoStack); + clearStack(mRedoStack); + clearStack( *( ( Vector< UndoAction* >* ) &mCompoundStack ) ); +} + +//----------------------------------------------------------------------------- +void UndoManager::initPersistFields() +{ + addField("numLevels", TypeS32, Offset(mNumLevels, UndoManager), "Number of undo & redo levels."); + // arrange for the default undo manager to exist. +// UndoManager &def = getDefaultManager(); +// Con::printf("def = %s undo manager created", def.getName()); + +} + +//----------------------------------------------------------------------------- +UndoManager& UndoManager::getDefaultManager() +{ + // the default manager is created the first time it is asked for. + static UndoManager *defaultMan = NULL; + if(!defaultMan) + { + defaultMan = new UndoManager(); + defaultMan->assignName("DefaultUndoManager"); + defaultMan->registerObject(); + } + return *defaultMan; +} + +ConsoleMethod(UndoManager, clearAll, void, 2, 2, "Clears the undo manager.") +{ + object->clearAll(); +} + +void UndoManager::clearAll() +{ + clearStack(mUndoStack); + clearStack(mRedoStack); + + Con::executef(this, "onClear"); +} + +//----------------------------------------------------------------------------- +void UndoManager::clearStack(Vector &stack) +{ + Vector::iterator itr = stack.begin(); + while (itr != stack.end()) + { + UndoAction* undo = stack.first(); + stack.pop_front(); + + // Call deleteObject() if the action was registered. + if ( undo->isProperlyAdded() ) + undo->deleteObject(); + else + delete undo; + } + stack.clear(); +} + +//----------------------------------------------------------------------------- +void UndoManager::clampStack(Vector &stack) +{ + while(stack.size() > mNumLevels) + { + UndoAction *act = stack.front(); + stack.pop_front(); + + // Call deleteObject() if the action was registered. + if ( act->isProperlyAdded() ) + act->deleteObject(); + else + delete act; + } +} + +void UndoManager::removeAction(UndoAction *action, bool noDelete) +{ + Vector::iterator itr = mUndoStack.begin(); + while (itr != mUndoStack.end()) + { + if ((*itr) == action) + { + UndoAction* deleteAction = *itr; + mUndoStack.erase(itr); + doRemove( deleteAction, noDelete ); + return; + } + itr++; + } + + itr = mRedoStack.begin(); + while (itr != mRedoStack.end()) + { + if ((*itr) == action) + { + UndoAction* deleteAction = *itr; + mRedoStack.erase(itr); + doRemove( deleteAction, noDelete ); + return; + } + itr++; + } +} + +void UndoManager::doRemove( UndoAction* action, bool noDelete ) +{ + if( action->mUndoManager == this ) + action->mUndoManager = NULL; + + if( !noDelete ) + { + // Call deleteObject() if the action was registered. + if ( action->isProperlyAdded() ) + action->deleteObject(); + else + delete action; + } + + if( isProperlyAdded() ) + Con::executef(this, "onRemoveUndo"); +} + +//----------------------------------------------------------------------------- +void UndoManager::undo() +{ + // make sure we have an action available + if(mUndoStack.size() < 1) + return; + + // pop the action off the undo stack + UndoAction *act = mUndoStack.last(); + mUndoStack.pop_back(); + + // add it to the redo stack + mRedoStack.push_back(act); + if(mRedoStack.size() > mNumLevels) + mRedoStack.pop_front(); + + Con::executef(this, "onUndo"); + + // perform the undo, whatever it may be. + (*act).undo(); +} + +//----------------------------------------------------------------------------- +void UndoManager::redo() +{ + // make sure we have an action available + if(mRedoStack.size() < 1) + return; + + // pop the action off the redo stack + UndoAction *react = mRedoStack.last(); + mRedoStack.pop_back(); + + // add it to the undo stack + mUndoStack.push_back(react); + if(mUndoStack.size() > mNumLevels) + mUndoStack.pop_front(); + + Con::executef(this, "onRedo"); + + // perform the redo, whatever it may be. + (*react).redo(); +} + +ConsoleMethod(UndoManager, getUndoCount, S32, 2, 2, "") +{ + return object->getUndoCount(); +} + +S32 UndoManager::getUndoCount() +{ + return mUndoStack.size(); +} + +ConsoleMethod(UndoManager, getUndoName, const char*, 3, 3, "(index)") +{ + return object->getUndoName(dAtoi(argv[2])); +} + +const char* UndoManager::getUndoName(S32 index) +{ + if ((index < getUndoCount()) && (index >= 0)) + return mUndoStack[index]->mActionName; + + return NULL; +} + +ConsoleMethod(UndoManager, getUndoAction, S32, 3, 3, "(index)") +{ + UndoAction * action = object->getUndoAction(dAtoi(argv[2])); + if ( !action ) + return -1; + + if ( !action->isProperlyAdded() ) + action->registerObject(); + + return action->getId(); +} + +UndoAction* UndoManager::getUndoAction(S32 index) +{ + if ((index < getUndoCount()) && (index >= 0)) + return mUndoStack[index]; + return NULL; +} + +ConsoleMethod(UndoManager, getRedoCount, S32, 2, 2, "") +{ + return object->getRedoCount(); +} + +S32 UndoManager::getRedoCount() +{ + return mRedoStack.size(); +} + +ConsoleMethod(UndoManager, getRedoName, const char*, 3, 3, "(index)") +{ + return object->getRedoName(dAtoi(argv[2])); +} + +const char* UndoManager::getRedoName(S32 index) +{ + if ((index < getRedoCount()) && (index >= 0)) + return mRedoStack[getRedoCount() - index - 1]->mActionName; + + return NULL; +} + +ConsoleMethod(UndoManager, getRedoAction, S32, 3, 3, "(index)") +{ + UndoAction * action = object->getRedoAction(dAtoi(argv[2])); + + if ( !action ) + return -1; + + if ( !action->isProperlyAdded() ) + action->registerObject(); + + return action->getId(); +} + +UndoAction* UndoManager::getRedoAction(S32 index) +{ + if ((index < getRedoCount()) && (index >= 0)) + return mRedoStack[index]; + return NULL; +} + +//----------------------------------------------------------------------------- +const char* UndoManager::getNextUndoName() +{ + if(mUndoStack.size() < 1) + return NULL; + + UndoAction *act = mUndoStack.last(); + return (*act).mActionName; +} + +//----------------------------------------------------------------------------- +const char* UndoManager::getNextRedoName() +{ + if(mRedoStack.size() < 1) + return NULL; + + UndoAction *act = mRedoStack.last(); + return (*act).mActionName; +} + +//----------------------------------------------------------------------------- +void UndoManager::addAction(UndoAction* action) +{ + // If we are assembling a compound, redirect the action to it + // and don't modify our current undo/redo state. + + if( mCompoundStack.size() ) + { + mCompoundStack.last()->addAction( action ); + return; + } + + // clear the redo stack + clearStack(mRedoStack); + + // push the incoming action onto the stack, move old data off the end if necessary. + mUndoStack.push_back(action); + if(mUndoStack.size() > mNumLevels) + mUndoStack.pop_front(); + + Con::executef(this, "onAddUndo"); +} + +//----------------------------------------------------------------------------- + +CompoundUndoAction* UndoManager::pushCompound( const String& name ) +{ + mCompoundStack.push_back( new CompoundUndoAction( name ) ); + return mCompoundStack.last(); +} + +//----------------------------------------------------------------------------- + +void UndoManager::popCompound( bool discard ) +{ + AssertFatal( getCompoundStackDepth() > 0, "UndoManager::popCompound - no compound on stack!" ); + + CompoundUndoAction* undo = mCompoundStack.last(); + mCompoundStack.pop_back(); + + if( discard || undo->getNumChildren() == 0 ) + { + if( undo->isProperlyAdded() ) + undo->deleteObject(); + else + delete undo; + } + else + addAction( undo ); +} + +//----------------------------------------------------------------------------- +ConsoleMethod(UndoAction, addToManager, void, 2, 3, "action.addToManager([undoManager])") +{ + UndoManager *theMan = NULL; + if(argc == 3) + { + SimObject *obj = Sim::findObject(argv[2]); + if(obj) + theMan = dynamic_cast (obj); + } + object->addToManager(theMan); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( UndoAction, undo, void, 2, 2, "() - Undo action contained in undo." ) +{ + object->undo(); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( UndoAction, redo, void, 2, 2, "() - Reo action contained in undo." ) +{ + object->redo(); +} + +//----------------------------------------------------------------------------- +ConsoleMethod(UndoManager, undo, void, 2, 2, "UndoManager.undo();") +{ + object->undo(); +} + +//----------------------------------------------------------------------------- +ConsoleMethod(UndoManager, redo, void, 2, 2, "UndoManager.redo();") +{ + object->redo(); +} + +//----------------------------------------------------------------------------- +ConsoleMethod(UndoManager, getNextUndoName, const char *, 2, 2, "UndoManager.getNextUndoName();") +{ + const char *name = object->getNextUndoName(); + if(!name) + return NULL; + char *ret = Con::getReturnBuffer(dStrlen(name) + 1); + dStrcpy(ret, name); + return ret; +} + +//----------------------------------------------------------------------------- +ConsoleMethod(UndoManager, getNextRedoName, const char *, 2, 2, "UndoManager.getNextRedoName();") +{ + const char *name = object->getNextRedoName(); + if(!name) + return NULL; + char *ret = Con::getReturnBuffer(dStrlen(name) + 1); + dStrcpy(ret, name); + return ret; +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( UndoManager, pushCompound, const char*, 2, 3, "( string name=\"\" ) - Push a CompoundUndoAction onto the compound stack for assembly." ) +{ + String name; + if( argc > 2 ) + name = argv[ 2 ]; + + CompoundUndoAction* action = object->pushCompound( name ); + if( !action ) + return ""; + + if( !action->isProperlyAdded() ) + action->registerObject(); + + return action->getIdString(); +} + +//----------------------------------------------------------------------------- + +ConsoleMethod( UndoManager, popCompound, void, 2, 3, "( bool discard=false ) - Pop the current CompoundUndoAction off the stack." ) +{ + if( !object->getCompoundStackDepth() ) + { + Con::errorf( "%s::popCompound - no compound on stack", argv[ 0 ] ); + return; + } + + bool discard = false; + if( argc > 2 ) + discard = dAtob( argv[ 2 ] ); + + object->popCompound( discard ); +} diff --git a/Engine/source/util/undo.h b/Engine/source/util/undo.h new file mode 100644 index 000000000..3552d3dac --- /dev/null +++ b/Engine/source/util/undo.h @@ -0,0 +1,245 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _UNDO_H_ +#define _UNDO_H_ + +#ifndef _SIMOBJECT_H_ +#include "console/simObject.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +class UndoManager; + +/// +class UndoAction : public SimObject +{ + friend class UndoManager; + +protected: + // The manager this was added to. + UndoManager* mUndoManager; + +public: + + /// A brief description of the action, for display in menus and the like. + // not private because we're exposing it to the console. + String mActionName; + + // Required in all ConsoleObject subclasses. + typedef SimObject Parent; + DECLARE_CONOBJECT(UndoAction); + static void initPersistFields(); + + /// Create a new action, assigning it a name for display in menus et cetera. + UndoAction(const UTF8 *actionName = " "); + virtual ~UndoAction(); + + /// Implement these methods to perform your specific undo & redo tasks. + virtual void undo() { }; + virtual void redo() { }; + + /// Adds the action to the undo stack of the default UndoManager, or the provided manager. + void addToManager(UndoManager* theMan = NULL); +}; + +/// An undo action that is comprised of other undo actions. +class CompoundUndoAction : public UndoAction +{ + friend class UndoManager; + +protected: + + Vector< UndoAction* > mChildren; + +public: + + typedef UndoAction Parent; + + CompoundUndoAction( const UTF8 *actionName = " " ); + virtual ~CompoundUndoAction(); + + DECLARE_CONOBJECT(CompoundUndoAction); + + virtual void addAction( UndoAction *action ); + virtual void undo(); + virtual void redo(); + + virtual void onDeleteNotify( SimObject* object ); + + U32 getNumChildren() const { return mChildren.size(); } +}; + +/// +class UndoManager : public SimObject +{ +private: + /// Default number of undo & redo levels. + const static U32 kDefaultNumLevels = 100; + + /// The stacks of undo & redo actions. They will be capped at size mNumLevels. + Vector mUndoStack; + Vector mRedoStack; + + /// Stack for assembling compound actions. + Vector< CompoundUndoAction* > mCompoundStack; + + /// Deletes all the UndoActions in a stack, then clears it. + void clearStack(Vector &stack); + /// Clamps a Vector to mNumLevels entries. + void clampStack(Vector &stack); + + /// Run the removal logic on the action. + void doRemove( UndoAction* action, bool noDelete ); + +public: + /// Number of undo & redo levels. + // not private because we're exposing it to the console. + U32 mNumLevels; + + // Required in all ConsoleObject subclasses. + typedef SimObject Parent; + DECLARE_CONOBJECT(UndoManager); + static void initPersistFields(); + + /// Constructor. If levels = 0, we use the default number of undo levels. + UndoManager(U32 levels = kDefaultNumLevels); + /// Destructor. deletes and clears the undo & redo stacks. + ~UndoManager(); + /// Accessor to the default undo manager singleton. Creates one if needed. + static UndoManager& getDefaultManager(); + + /// Undo last action, and put it on the redo stack. + void undo(); + /// Redo the last action, and put it on the undo stack. + void redo(); + + /// Clears the undo and redo stacks. + void clearAll(); + + /// Returns the printable name of the top actions on the undo & redo stacks. + const char* getNextUndoName(); + const char* getNextRedoName(); + + S32 getUndoCount(); + S32 getRedoCount(); + + const char* getUndoName(S32 index); + const char* getRedoName(S32 index); + + UndoAction* getUndoAction(S32 index); + UndoAction* getRedoAction(S32 index); + + /// Add an action to the top of the undo stack, and clear the redo stack. + void addAction(UndoAction* action); + void removeAction(UndoAction* action, bool noDelete = false); + + /// @name Compound Actions + /// + /// The compound action stack allows to redirect undos to a CompoundUndoAction + /// and thus assemble multi-operation undos directly through the UndoManager. + /// When the bottom-most CompoundUndoAction is popped off the stack, the compound + /// will be moved onto the undo stack. + /// + /// @{ + + /// Push a compound action called "name" onto the compound stack. While the + /// compound stack is not empty, all undos that are queued on the undo manager will + /// go to the topmost compound instead of the undo stack. + CompoundUndoAction* pushCompound( const String& name ); + + /// Pop the topmost compound off the compound stack and add it to the undo manager. + /// If the compound stack is still not empty, the compound will be added to the next + /// lower compound on the stack. Otherwise it will be recorded as a regular undo. + void popCompound( bool discard = false ); + + /// Return the current nesting depth of the compound stack. + U32 getCompoundStackDepth() const { return mCompoundStack.size(); } + + /// @} +}; + + +/// Script Undo Action Creation +/// +/// Undo actions can be created in script like this: +/// +/// ... +/// %undo = new UndoScriptAction() { class = SampleUndo; actionName = "Sample Undo"; }; +/// %undo.addToManager(UndoManager); +/// ... +/// +/// function SampleUndo::undo() +/// { +/// ... +/// } +/// +/// function SampleUndo::redo() +/// { +/// ... +/// } +/// +class UndoScriptAction : public UndoAction +{ +public: + typedef UndoAction Parent; + + UndoScriptAction() : UndoAction() + { + } + + virtual void undo() { Con::executef(this, "undo"); }; + virtual void redo() { Con::executef(this, "redo"); } + + virtual bool onAdd() + { + // Let Parent Do Work. + if(!Parent::onAdd()) + return false; + + + // Notify Script. + if(isMethod("onAdd")) + Con::executef(this, "onAdd"); + + // Return Success. + return true; + }; + + virtual void onRemove() + { + if (mUndoManager) + mUndoManager->removeAction((UndoAction*)this, true); + + // notify script + if(isMethod("onRemove")) + Con::executef(this, "onRemove"); + + Parent::onRemove(); + } + + DECLARE_CONOBJECT(UndoScriptAction); +}; + +#endif // _UNDO_H_ diff --git a/Engine/source/windowManager/dedicated/dedicatedWindowStub.cpp b/Engine/source/windowManager/dedicated/dedicatedWindowStub.cpp new file mode 100644 index 000000000..51149ff3a --- /dev/null +++ b/Engine/source/windowManager/dedicated/dedicatedWindowStub.cpp @@ -0,0 +1,29 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "windowManager/dedicated/dedicatedWindowStub.h" + + +PlatformWindowManager *CreatePlatformWindowManager() +{ + return new DedicatedWindowMgr; +} diff --git a/Engine/source/windowManager/dedicated/dedicatedWindowStub.h b/Engine/source/windowManager/dedicated/dedicatedWindowStub.h new file mode 100644 index 000000000..1206877bf --- /dev/null +++ b/Engine/source/windowManager/dedicated/dedicatedWindowStub.h @@ -0,0 +1,109 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_DEDICATED + +#ifndef _DEDICATED_WINDOW_STUB_H_ +#define _DEDICATED_WINDOW_STUB_H_ + +#include "windowManager/platformWindowMgr.h" + +/// The DedicatedWindowMgr is for Dedicated servers, which may not +/// even have graphics hardware. However, the WindowManager is referenced +/// (indirectly) by scripts, and therefore must exist. +class DedicatedWindowMgr : public PlatformWindowManager +{ +public: + + DedicatedWindowMgr() {}; + + virtual ~DedicatedWindowMgr() + { + } + + static void processCmdLineArgs(const S32 argc, const char **argv) {} + + /// Return the extents in window coordinates of the primary desktop + /// area. On dedicated systems, this returns a token value of 0. + virtual RectI getPrimaryDesktopArea() { return RectI(0, 0, 0, 0); } + + /// Retrieve the currently set desktop bit depth. + /// @return -1 since there is no desktop + virtual S32 getDesktopBitDepth() { return -1; } + + /// Retrieve the currently set desktop resolution + /// @return Point2I(-1,-1) since there is no desktop + virtual Point2I getDesktopResolution() { return Point2I(-1, -1); } + + /// Populate a vector with the token primary desktop area value + virtual void getMonitorRegions(Vector ®ions) { regions.push_back(getPrimaryDesktopArea()); } + + /// Create a new window, appropriate for the specified device and mode. + /// + /// @return NULL - there is no graphics hardware available in dedicated mode + virtual PlatformWindow *createWindow(GFXDevice *device, const GFXVideoMode &mode) { return NULL; } + + /// Produces an empty list since there are no windows in dedicated mode + virtual void getWindows(VectorPtr &windows) { windows.clear(); } + + /// Get the window that currently has the input focus or NULL. + virtual PlatformWindow* getFocusedWindow() { return NULL; } + + /// Get a window from a device ID. + /// + /// @return NULL. + virtual PlatformWindow *getWindowById(WindowId id) { return NULL; } + + /// Get the first window in the window list + /// + /// @return The first window in the list, or NULL if no windows found + virtual PlatformWindow *getFirstWindow() { return NULL; } + + + /// Set the parent window + /// + /// This does nothing in dedicated builds + virtual void setParentWindow(void* newParent) {} + + /// Get the parent window - returns NULL for dedicated servers + virtual void* getParentWindow() { return NULL; } + + + /// This method cues the appearance of that window ("lowering the curtain"). + virtual void lowerCurtain() {} + + /// @see lowerCurtain + /// + /// This method removes the curtain window. + virtual void raiseCurtain() {} + +private: + /// Process command line arguments from StandardMainLoop. This is done to + /// allow web plugin functionality, where we are passed platform-specific + /// information allowing us to set ourselves up in the web browser, + /// to occur in a platform-neutral way. + virtual void _processCmdLineArgs(const S32 argc, const char **argv) {} +}; + +#endif // _DEDICATED_WINDOW_STUB_H_ + +#endif // TORQUE_DEDICATED diff --git a/Engine/source/windowManager/mac/macCursorController.h b/Engine/source/windowManager/mac/macCursorController.h new file mode 100644 index 000000000..cc0cb0424 --- /dev/null +++ b/Engine/source/windowManager/mac/macCursorController.h @@ -0,0 +1,50 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MACCURSORCONTROLLER_H_ +#define _MACCURSORCONTROLLER_H_ + +#include "windowManager/platformCursorController.h" + +class MacCursorController : public PlatformCursorController +{ +public: + MacCursorController(MacWindow* owner) + : PlatformCursorController( ( PlatformWindow* ) owner ) + { + pushCursor(PlatformCursorController::curArrow); + } + + virtual void setCursorPosition(S32 x, S32 y); + virtual void getCursorPosition(Point2I &point); + virtual void setCursorVisible(bool visible); + virtual bool isCursorVisible(); + + virtual void setCursorShape(U32 cursorID); + virtual void setCursorShape( const UTF8 *fileName, bool reload ); + + virtual U32 getDoubleClickTime(); + virtual S32 getDoubleClickWidth(); + virtual S32 getDoubleClickHeight(); +}; + +#endif diff --git a/Engine/source/windowManager/mac/macCursorController.mm b/Engine/source/windowManager/mac/macCursorController.mm new file mode 100644 index 000000000..9e92c0f33 --- /dev/null +++ b/Engine/source/windowManager/mac/macCursorController.mm @@ -0,0 +1,166 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include +#include +#include "windowManager/mac/macWindow.h" +#include "windowManager/mac/macCursorController.h" + +void MacCursorController::setCursorPosition(S32 x, S32 y) +{ + MacWindow* macWindow = dynamic_cast(mOwner); + if(!macWindow || !macWindow->isVisible()) + return; + + CGPoint pt = { x, y }; + CGWarpMouseCursorPosition(pt); + + macWindow->_skipAnotherMouseEvent(); +} + +void MacCursorController::getCursorPosition( Point2I &point ) +{ + NSPoint pos = [NSEvent mouseLocation]; + point.x = pos.x; + point.y = pos.y; + + //what does this do?? comment?? + + MacWindow* macWindow = static_cast(mOwner); + + CGRect bounds = macWindow->getDisplayBounds(); + CGRect mainbounds = macWindow->getMainDisplayBounds(); + F32 offsetY = mainbounds.size.height - (bounds.size.height + bounds.origin.y); + point.y = bounds.size.height + offsetY - point.y; +} + +void MacCursorController::setCursorVisible(bool visible) +{ + visible ? [NSCursor unhide] : [NSCursor hide]; +} + +bool MacCursorController::isCursorVisible() +{ + return CGCursorIsVisible(); +} + +// a repository of custom cursors. +@interface TorqueCursors : NSObject { } ++(NSCursor*)resizeAll; ++(NSCursor*)resizeNWSE; ++(NSCursor*)resizeNESW; +@end +@implementation TorqueCursors ++(NSCursor*)resizeAll +{ + static NSCursor* cur = nil; + if(!cur) + cur = [[NSCursor alloc] initWithImage:[NSImage imageNamed:@"resizeAll"] hotSpot:NSMakePoint(8, 8)]; + return cur; +} ++(NSCursor*)resizeNWSE +{ + static NSCursor* cur = nil; + if(!cur) + cur = [[NSCursor alloc] initWithImage:[NSImage imageNamed:@"resizeNWSE"] hotSpot:NSMakePoint(8, 8)]; + return cur; +} ++(NSCursor*)resizeNESW +{ + static NSCursor* cur = nil; + if(!cur) + cur = [[NSCursor alloc] initWithImage:[NSImage imageNamed:@"resizeNESW"] hotSpot:NSMakePoint(8, 8)]; + return cur; +} +@end + +void MacCursorController::setCursorShape(U32 cursorID) +{ + NSCursor *cur; + switch(cursorID) + { + case PlatformCursorController::curArrow: + [[NSCursor arrowCursor] set]; + break; + case PlatformCursorController::curWait: + // hack: black-sheep carbon call + SetThemeCursor(kThemeWatchCursor); + break; + case PlatformCursorController::curPlus: + [[NSCursor crosshairCursor] set]; + break; + case PlatformCursorController::curResizeVert: + [[NSCursor resizeLeftRightCursor] set]; + break; + case PlatformCursorController::curIBeam: + [[NSCursor IBeamCursor] set]; + break; + case PlatformCursorController::curResizeAll: + cur = [TorqueCursors resizeAll]; + [cur set]; + break; + case PlatformCursorController::curResizeNESW: + [[TorqueCursors resizeNESW] set]; + break; + case PlatformCursorController::curResizeNWSE: + [[TorqueCursors resizeNWSE] set]; + break; + case PlatformCursorController::curResizeHorz: + [[NSCursor resizeUpDownCursor] set]; + break; + } +} + +void MacCursorController::setCursorShape( const UTF8 *fileName, bool reload ) +{ + //TODO: this is untested code + + NSString* strFileName = [ NSString stringWithUTF8String: fileName ]; + + // Load image file. + + NSImage* image = [ NSImage alloc ]; + if( [ image initWithContentsOfFile: strFileName ] == nil ) + return; + + // Allocate cursor. + + NSCursor* cursor = [ NSCursor alloc ]; + [ cursor initWithImage: image hotSpot: NSMakePoint( 0.5, 0.5 ) ]; +} + +U32 MacCursorController::getDoubleClickTime() +{ + return GetDblTime() / 60.0f * 1000.0f; +} + +S32 MacCursorController::getDoubleClickWidth() +{ + // This is an arbitrary value. + return 10; +} + +S32 MacCursorController::getDoubleClickHeight() +{ + return getDoubleClickWidth(); +} + diff --git a/Engine/source/windowManager/mac/macView.h b/Engine/source/windowManager/mac/macView.h new file mode 100644 index 000000000..dcb4cd7b2 --- /dev/null +++ b/Engine/source/windowManager/mac/macView.h @@ -0,0 +1,75 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MACVIEW_H_ +#define _MACVIEW_H_ + +#import +#include "windowManager/mac/macWindow.h" + +/// GGMacView handles displaying content and responding to user input. +@interface GGMacView : NSOpenGLView +{ + MacWindow* mTorqueWindow; + U32 mLastMods; + bool mHandledAsCharEvent; +} +- (void)setTorqueWindow:(MacWindow*)theWindow; +- (MacWindow*)torqueWindow; + +/// @name Inherited Mouse Input methods +/// @{ +- (void)mouseDown:(NSEvent *)theEvent; +- (void)rightMouseDown:(NSEvent *)theEvent; +- (void)mouseDragged:(NSEvent *)theEvent; +- (void)rightMouseDragged:(NSEvent *)theEvent; +- (void)mouseUp:(NSEvent *)theEvent; +- (void)rightMouseUp:(NSEvent *)theEvent; +- (void)mouseMoved:(NSEvent *)theEvent; +- (void)scrollWheel:(NSEvent *)theEvent; +/// @} + +/// @name Inherited Keyboard Input methods +/// @{ +- (void)keyDown:(NSEvent *)theEvent; +- (void)keyUp:(NSEvent *)theEvent; +/// @} + +/// @name Keyboard Input Common Code +/// @{ +- (void)rawKeyUpDown:(NSEvent *)theEvent keyDown:(BOOL)isKeyDown; +/// @} + +/// @name Mouse Input Common Code +/// @{ +- (void)mouseUpDown:(NSEvent *)theEvent mouseDown:(BOOL)isMouseDown; +- (void)mouseMotion:(NSEvent *)theEvent; +/// @} + +- (BOOL)acceptsFirstResponder; +- (BOOL)becomeFirstResponder; +- (BOOL)resignFirstResponder; + + +@end + +#endif diff --git a/Engine/source/windowManager/mac/macView.mm b/Engine/source/windowManager/mac/macView.mm new file mode 100644 index 000000000..9bfd5c353 --- /dev/null +++ b/Engine/source/windowManager/mac/macView.mm @@ -0,0 +1,401 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "windowManager/mac/macView.h" +#include "platform/event.h" +#include "platform/platformInput.h" +#include "console/console.h" +#include "sim/actionMap.h" +#include "app/mainLoop.h" + +// For left/right side definitions. +#include + + +#define WHEEL_DELTA ( 120 * 0.1 ) + +static bool smApplicationInactive = false; + + +extern InputModifiers convertModifierBits( const U32 in ); + + +inline U32 NSModifiersToTorqueModifiers( NSUInteger mods ) +{ + U32 torqueMods = 0; + + if( mods & NX_DEVICELSHIFTKEYMASK ) + torqueMods |= IM_LSHIFT; + if( mods & NX_DEVICERSHIFTKEYMASK ) + torqueMods |= IM_RSHIFT; + if( mods & NX_DEVICELALTKEYMASK ) + torqueMods |= IM_LOPT; + if( mods & NX_DEVICERALTKEYMASK ) + torqueMods |= IM_ROPT; + if( mods & NX_DEVICELCTLKEYMASK ) + torqueMods |= IM_LCTRL; + if( mods & NX_DEVICERCTLKEYMASK ) + torqueMods |= IM_RCTRL; + if( mods & NX_DEVICELCMDKEYMASK ) + torqueMods |= IM_LALT; + if( mods & NX_DEVICERCMDKEYMASK ) + torqueMods |= IM_RALT; + + Input::setModifierKeys( convertModifierBits( torqueMods ) ); + + return torqueMods; +} + + +@implementation GGMacView +- (void)setTorqueWindow:(MacWindow*)theWindow +{ + mTorqueWindow = theWindow; + mLastMods = 0; +} + +- (MacWindow*)torqueWindow +{ + return mTorqueWindow; +} + +-(void)trackModState:(U32)torqueKey withMacMods:(U32)macMods event:(NSEvent*)theEvent +{ + // track state: + // translate the torque key code to the torque flag that changed + // xor with existing mods for new mod state + // clear anything that the mac says both siblings are not down ( to help stay in sync, a little bit ) + + // ?set left sibling of anything that the mac says some sibling is down, but that we don't see as down? + + U32 torqueMod = 0; + switch(torqueKey) + { + case KEY_LSHIFT: torqueMod = IM_LSHIFT; break; + case KEY_RSHIFT: torqueMod = IM_RSHIFT; break; + case KEY_LCONTROL: torqueMod = IM_LCTRL; break; + case KEY_RCONTROL: torqueMod = IM_RCTRL; break; + case KEY_MAC_LOPT: torqueMod = IM_LOPT; break; + case KEY_MAC_ROPT: torqueMod = IM_ROPT; break; + case KEY_LALT: torqueMod = IM_LALT; break; + case KEY_RALT: torqueMod = IM_RALT; break; + }; + + // flip the mod that we got an event for + U32 newMods = mLastMods ^ torqueMod; + + // clear left and right if mac thinks both are up. + if( !(macMods & NSShiftKeyMask)) newMods &= ~IM_LSHIFT, newMods &= ~IM_RSHIFT; + if( !(macMods & NSControlKeyMask)) newMods &= ~IM_LCTRL, newMods &= ~IM_RCTRL; + if( !(macMods & NSAlternateKeyMask)) newMods &= ~IM_LOPT, newMods &= ~IM_ROPT; + if( !(macMods & NSCommandKeyMask)) newMods &= ~IM_LALT, newMods &= ~IM_RALT; + + // Generate keyUp/Down event (allows us to use modifier keys for actions) + mLastMods = 0; + [self rawKeyUpDown:theEvent keyDown:(newMods & torqueMod)]; + + mLastMods = newMods; + + Input::setModifierKeys( convertModifierBits( mLastMods ) ); +} + +- (Point2I)viewCoordsToTorqueCoords:(NSPoint)mousePoint +{ + if(mTorqueWindow->isFullscreen()) + { + CGRect bounds = mTorqueWindow->getDisplayBounds(); + CGRect mainbounds = mTorqueWindow->getMainDisplayBounds(); + F32 offsetY = mainbounds.size.height - (bounds.size.height + bounds.origin.y); + Point2I pt(mousePoint.x - bounds.origin.x, bounds.size.height + offsetY - mousePoint.y); + return pt; + } + return Point2I(mousePoint.x, mTorqueWindow->getClientExtent().y - mousePoint.y); +} + +- (void)signalGainFocus +{ + if(smApplicationInactive) + smApplicationInactive = false; + + bool gainFocus = static_cast(WindowManager)->canWindowGainFocus(mTorqueWindow); + if(gainFocus) + mTorqueWindow->appEvent.trigger(mTorqueWindow->getWindowId(), GainFocus); +} + +#pragma mark - +#pragma mark Mouse Input +// We're funnelling all the standard cocoa event handlers to -mouseUpDown and -mouseMotion. +- (void)mouseDown:(NSEvent *)theEvent { [self mouseUpDown:theEvent mouseDown:YES]; } +- (void)rightMouseDown:(NSEvent *)theEvent { [self mouseUpDown:theEvent mouseDown:YES]; } +- (void)otherMouseDown:(NSEvent *)theEvent { [self mouseUpDown:theEvent mouseDown:YES]; } +- (void)mouseUp:(NSEvent *)theEvent { [self mouseUpDown:theEvent mouseDown:NO]; } +- (void)rightMouseUp:(NSEvent *)theEvent { [self mouseUpDown:theEvent mouseDown:NO]; } +- (void)otherMouseUp:(NSEvent *)theEvent { [self mouseUpDown:theEvent mouseDown:NO]; } +- (void)mouseDragged:(NSEvent *)theEvent { [self mouseMotion:theEvent]; } +- (void)rightMouseDragged:(NSEvent *)theEvent { [self mouseMotion:theEvent]; } +- (void)otherMouseDragged:(NSEvent *)theEvent { [self mouseMotion:theEvent]; } +- (void)mouseMoved:(NSEvent *)theEvent { [self mouseMotion:theEvent]; } + +- (void)mouseUpDown:(NSEvent *)theEvent mouseDown:(BOOL)isMouseDown +{ + Point2I eventLocation = [self viewCoordsToTorqueCoords: [theEvent locationInWindow]]; + U16 buttonNumber = [theEvent buttonNumber]; + U32 action = isMouseDown ? SI_MAKE : SI_BREAK; + + // If the event location is negative then it occurred in the structure region (e.g. title bar, resize corner), and we don't want + // to lock the mouse/drop into fullscreen for that. + if(WindowManager->getFocusedWindow() != mTorqueWindow && eventLocation.x > 0 && eventLocation.y > 0) + [self signalGainFocus]; + + mLastMods = NSModifiersToTorqueModifiers( [ theEvent modifierFlags ] ); + + mTorqueWindow->buttonEvent.trigger(mTorqueWindow->getWindowId(), mLastMods, action, buttonNumber); +} + +- (void)mouseMotion:(NSEvent *)theEvent +{ + mTorqueWindow->_doMouseLockNow(); + + // when moving the mouse to the center of the window for mouse locking, we need + // to skip the next mouse delta event + if(mTorqueWindow->isMouseLocked() && mTorqueWindow->_skipNextMouseEvent()) + { + mTorqueWindow->_skippedMouseEvent(); + return; + } + + Point2I eventLocation; + if(mTorqueWindow->isMouseLocked()) + { + eventLocation.x = [theEvent deltaX]; + eventLocation.y = [theEvent deltaY]; + } + else + { + eventLocation = [self viewCoordsToTorqueCoords:[theEvent locationInWindow]]; + } + + mLastMods = NSModifiersToTorqueModifiers( [ theEvent modifierFlags ] ); + + mTorqueWindow->mouseEvent.trigger(mTorqueWindow->getWindowId(), mLastMods, eventLocation.x, eventLocation.y, mTorqueWindow->isMouseLocked()); +} + +- (void)scrollWheel:(NSEvent *)theEvent +{ + float deltaX = [ theEvent deltaX ]; + float deltaY = [ theEvent deltaY ]; + + if( mIsZero( deltaX ) && mIsZero( deltaY ) ) + return; + + mLastMods = NSModifiersToTorqueModifiers( [ theEvent modifierFlags ] ); + + mTorqueWindow->wheelEvent.trigger( mTorqueWindow->getWindowId(), mLastMods, S32( deltaX * WHEEL_DELTA ), S32( deltaY * WHEEL_DELTA ) ); +} + +#pragma mark - +#pragma mark Keyboard Input +- (BOOL)performKeyEquivalent:(NSEvent *)theEvent +{ + // Pass it off to the main menu. If the main menu handled it, we're done. + if([[NSApp mainMenu] performKeyEquivalent:theEvent]) + return YES; + + // cmd-q will quit. End of story. + if(([theEvent modifierFlags] & NSCommandKeyMask && !([theEvent modifierFlags] & NSAlternateKeyMask) && !([theEvent modifierFlags] & NSControlKeyMask)) && [theEvent keyCode] == 0x0C) + { + StandardMainLoop::shutdown(); + [NSApp terminate:self]; + return YES; + } + + // In fullscreen we grab ALL keyboard events, including ones which would normally be handled by the system, + // like cmd-tab. Thus, we need to specifically check for cmd-tab and bail out of fullscreen and hide the app + // to switch to the next application. + // 0x30 is tab, so this grabs command-tab and command-shift-tab + if([theEvent keyCode] == 0x30 && mTorqueWindow->isFullscreen()) + { + // Bail! + mTorqueWindow->appEvent.trigger(mTorqueWindow->getWindowId(), LoseFocus); + [NSApp hide:nil]; + return YES; + } + + // All other events are uninteresting to us and can be handled by Torque. + if([theEvent type] == NSKeyDown) + [self keyDown:theEvent]; + else if([theEvent type] == NSKeyUp) + [self keyUp:theEvent]; + + // Don't let the default handler continue processing these events, it does things we don't like. + return YES; +} + +- (void)flagsChanged:(NSEvent *)theEvent +{ + U32 torqueKeyCode = TranslateOSKeyCode([theEvent keyCode]); + U32 macMods = [theEvent modifierFlags]; + [self trackModState:torqueKeyCode withMacMods:macMods event:theEvent]; +} + +- (void)keyDown:(NSEvent *)theEvent +{ + // If keyboard translation is on and the key isn't bound in the + // global action map, first try turning this into one or more + // character events. + + if( mTorqueWindow->getKeyboardTranslation() + && !mTorqueWindow->shouldNotTranslate( + convertModifierBits( NSModifiersToTorqueModifiers( [ theEvent modifierFlags ] ) ), + ( InputObjectInstances ) TranslateOSKeyCode( [ theEvent keyCode ] ) ) ) + { + mHandledAsCharEvent = false; + [ self interpretKeyEvents: [ NSArray arrayWithObject: theEvent ] ]; + + if( mHandledAsCharEvent ) + return; + } + + // Fire as raw keyboard event. + + [ self rawKeyUpDown: theEvent keyDown: YES ]; +} + +- (void)keyUp:(NSEvent *)theEvent +{ + [self rawKeyUpDown:theEvent keyDown:NO]; +} + +- (void)rawKeyUpDown:(NSEvent *)theEvent keyDown:(BOOL)isKeyDown +{ + U32 action; + if([theEvent type] != NSFlagsChanged) + action = isKeyDown ? ([theEvent isARepeat] ? SI_REPEAT : SI_MAKE) : SI_BREAK; + else + action = isKeyDown ? SI_MAKE : SI_BREAK; + + U32 torqueKeyCode = TranslateOSKeyCode([theEvent keyCode]); + mLastMods = NSModifiersToTorqueModifiers( [ theEvent modifierFlags ] ); + + mTorqueWindow->keyEvent.trigger(mTorqueWindow->getWindowId(), mLastMods, action, torqueKeyCode); +} + +- (void)insertText:(id)_inString +{ + // input string may be an NSString or an NSAttributedString + NSString *text = [_inString isKindOfClass:[NSAttributedString class]] ? [_inString string] : _inString; + for(int i = 0; i < [text length]; i++) + { + mTorqueWindow->charEvent.trigger(mTorqueWindow->getWindowId(), mLastMods, [text characterAtIndex:i]); + mHandledAsCharEvent = true; + } +} + +#pragma mark - +#pragma mark Application Delegate +- (void)applicationDidResignActive:(NSNotification *)aNotification +{ + smApplicationInactive = true; + mTorqueWindow->appEvent.trigger(mTorqueWindow->getWindowId(), LoseFocus); + [NSApp setDelegate:nil]; +} + +- (void)applicationDidHide:(NSNotification *)aNotification +{ + mTorqueWindow->appEvent.trigger(mTorqueWindow->getWindowId(), LoseFocus); +} + +- (void)applicationDidUnhide:(NSNotification *)aNotification +{ + mTorqueWindow->appEvent.trigger(mTorqueWindow->getWindowId(), GainFocus); +} + +#ifndef TORQUE_SHARED + +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender +{ + Platform::postQuitMessage(0); + return NSTerminateCancel; +} + +#endif + +#pragma mark - +#pragma mark Window Delegate +- (BOOL)windowShouldClose:(NSWindow *)sender +{ + // We close the window ourselves + mTorqueWindow->appEvent.trigger(mTorqueWindow->getWindowId(), WindowDestroy); + return NO; +} + +- (void)windowWillClose:(NSNotification *) notification +{ + mTorqueWindow->_disassociateCocoaWindow(); +} + +- (void)windowDidBecomeKey:(NSNotification *)notification +{ + // when our window is the key window, we become the app delegate. + PlatformWindow* focusWindow = WindowManager->getFocusedWindow(); + if(focusWindow && focusWindow != mTorqueWindow) + focusWindow->appEvent.trigger(mTorqueWindow->getWindowId(), LoseFocus); + [NSApp setDelegate:self]; + [self signalGainFocus]; +} + +- (void)windowDidResignKey:(NSNotification*)notification +{ + mTorqueWindow->appEvent.trigger(mTorqueWindow->getWindowId(), LoseScreen); + mTorqueWindow->_associateMouse(); + mTorqueWindow->setCursorVisible(true); + [NSApp setDelegate:nil]; +} + +- (void)windowDidChangeScreen:(NSNotification*)notification +{ + NSWindow* wnd = [notification object]; + // TODO: Add a category to NSScreen to deal with this + CGDirectDisplayID disp = (CGDirectDisplayID)[[[[wnd screen] deviceDescription] valueForKey:@"NSScreenNumber"] unsignedIntValue]; + mTorqueWindow->setDisplay(disp); +} + +- (void)windowDidResize:(NSNotification*)notification +{ + Point2I clientExtent = mTorqueWindow->getClientExtent(); + mTorqueWindow->resizeEvent.trigger(mTorqueWindow->getWindowId(), clientExtent.x, clientExtent.y); +} + +#pragma mark - +#pragma mark responder status +- (BOOL)acceptsFirstResponder { return YES; } +- (BOOL)becomeFirstResponder { return YES; } +- (BOOL)resignFirstResponder { return YES; } + +// Basic implementation because NSResponder's default implementation can cause infinite loops when our keyDown: method calls interpretKeyEvents: +- (void)doCommandBySelector:(SEL)aSelector +{ + if([self respondsToSelector:aSelector]) + [self performSelector:aSelector withObject:nil]; +} + +@end diff --git a/Engine/source/windowManager/mac/macWindow.h b/Engine/source/windowManager/mac/macWindow.h new file mode 100644 index 000000000..ebf5c5e7b --- /dev/null +++ b/Engine/source/windowManager/mac/macWindow.h @@ -0,0 +1,191 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _TORQUE_MACWINDOW_H_ +#define _TORQUE_MACWINDOW_H_ + +#include "windowManager/platformWindow.h" +#include "windowManager/mac/macWindowManager.h" +#include "windowManager/mac/macCursorController.h" + +#ifndef _GFXTARGET_H_ +#include "gfx/gfxTarget.h" +#endif + +#ifndef _GFXSTRUCTS_H_ +#include "gfx/gfxStructs.h" +#endif + +class MacWindow : public PlatformWindow +{ +public: + virtual ~MacWindow(); + + virtual GFXDevice *getGFXDevice() { return mDevice; } + virtual GFXWindowTarget *getGFXTarget() { return mTarget; } + virtual void setVideoMode(const GFXVideoMode &mode); + virtual const GFXVideoMode &getVideoMode() { return mCurrentMode; } + + virtual WindowId getWindowId() { return mWindowId; } + + void setDisplay(CGDirectDisplayID display); + CGDirectDisplayID getDisplay() { return mDisplay; } + CGRect getMainDisplayBounds() { return mMainDisplayBounds; } + CGRect getDisplayBounds() { return mDisplayBounds; } + + virtual bool clearFullscreen() + { + // TODO: properly drop out of full screen + return true; + } + virtual bool isFullscreen() { return mFullscreen; } + + virtual PlatformWindow * getNextWindow() const; + + virtual void setMouseLocked( bool enable ) + { + mShouldMouseLock = enable; + if(isFocused()) + _doMouseLockNow(); + } + virtual bool isMouseLocked() const { return mMouseLocked; } + virtual bool shouldLockMouse() const { return mShouldMouseLock; } + + virtual bool setSize(const Point2I &newSize); + + virtual void setClientExtent( const Point2I newExtent ); + virtual const Point2I getClientExtent(); + + virtual void setBounds( const RectI &newBounds ); + virtual const RectI getBounds() const; + + virtual void setPosition( const Point2I newPosition ); + virtual const Point2I getPosition(); + + virtual void centerWindow(); + + virtual Point2I clientToScreen( const Point2I& pos ); + virtual Point2I screenToClient( const Point2I& pos ); + + virtual bool setCaption(const char *windowText); + virtual const char *getCaption() { return mTitle; } + + virtual bool setType( S32 windowType ) { return true; } + + virtual void minimize(); + virtual void maximize(); + virtual void restore(); + virtual bool isMinimized(); + virtual bool isMaximized(); + virtual void show(); + virtual void close(); + virtual void hide(); + virtual bool isOpen(); + virtual bool isVisible(); + + virtual bool isFocused(); + virtual void setFocus(); + virtual void clearFocus(); + + virtual void* getPlatformDrawable() const; + + // TODO: These should be private, but GGMacView (an Obj-C class) needs access to these and we can't friend Obj-C classes + bool _skipNextMouseEvent() { return mSkipMouseEvents != 0; } + void _skipAnotherMouseEvent() { mSkipMouseEvents++; } + void _skippedMouseEvent() { mSkipMouseEvents--; } + + /// Does the work of actually locking or unlocking the mouse, based on the + /// value of shouldLockMouse(). + /// + /// Disassociates the cursor movement from the mouse input and hides the mouse + /// when locking. Re-associates cursor movement with mouse input and shows the + /// mouse when unlocking. + /// + /// Returns true if we locked or unlocked the mouse. Returns false if the mouse + /// was already in the correct state. + void _doMouseLockNow(); + + // Helper methods for doMouseLockNow + void _associateMouse(); + void _dissociateMouse(); + void _centerMouse(); + + // For GGMacView + void _disassociateCocoaWindow(); + + // Safari support methods + static void setSafariWindow(NSWindow *window, S32 x = 0, S32 y = 0, S32 width = 0, S32 height = 0); + static void hideBrowserWindow(bool hide); + +protected: + virtual void _setFullscreen(bool fullScreen); + +private: + friend class MacWindowManager; + friend class MacCursorController; + + struct SafariWindowInfo + { + NSWindow* safariWindow; /* The Safari Browser Window */ + S32 x; /* Position of top left corner relative */ + S32 y; /* to a safari page. */ + U32 width; /* Maximum window size */ + U32 height; + }; + + MacWindow(U32 windowId, const char* windowText, Point2I clientExtent); + + void _initCocoaWindow(const char* windowText, Point2I clientExtent); + void setWindowId(U32 newid) { mWindowId = newid;} + void signalGainFocus(); + + static SafariWindowInfo* sSafariWindowInfo; + static MacWindow* sInstance; + + NSWindow* mCocoaWindow; + GFXDevice *mDevice; + GFXWindowTargetRef mTarget; + GFXVideoMode mCurrentMode; + + MacWindow *mNextWindow; + + bool mMouseLocked; + bool mShouldMouseLock; + + const char* mTitle; + bool mMouseCaptured; + + MacWindowManager* mOwningWindowManager; + U32 mSkipMouseEvents; + + bool mFullscreen; + bool mShouldFullscreen; + NSDictionary* mDefaultDisplayMode; + + void _onAppEvent(WindowId,S32); + + CGDirectDisplayID mDisplay; + CGRect mDisplayBounds; + CGRect mMainDisplayBounds; +}; + +#endif diff --git a/Engine/source/windowManager/mac/macWindow.mm b/Engine/source/windowManager/mac/macWindow.mm new file mode 100644 index 000000000..db64aac0d --- /dev/null +++ b/Engine/source/windowManager/mac/macWindow.mm @@ -0,0 +1,594 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include +#include "windowManager/mac/macWindow.h" +#include "windowManager/mac/macView.h" + +#include "console/console.h" + +MacWindow::SafariWindowInfo* MacWindow::sSafariWindowInfo = NULL; +MacWindow* MacWindow::sInstance = NULL; + +@interface SafariBrowserWindow : NSWindow +{ +} +@end + +@implementation SafariBrowserWindow + +// Windows created with NSBorderlessWindowMask normally can't be key, but we want ours to be +- (BOOL) canBecomeKeyWindow +{ + return YES; +} + +@end + + +MacWindow::MacWindow(U32 windowId, const char* windowText, Point2I clientExtent) +{ + mMouseLocked = false; + mShouldMouseLock = false; + mTitle = NULL; + mMouseCaptured = false; + + mCocoaWindow = NULL; + mCursorController = new MacCursorController( this ); + mOwningWindowManager = NULL; + + mFullscreen = false; + mShouldFullscreen = false; + mDefaultDisplayMode = NULL; + + mSkipMouseEvents = 0; + + mDisplay = kCGDirectMainDisplay; + mMainDisplayBounds = mDisplayBounds = CGDisplayBounds(mDisplay); + + mWindowId = windowId; + _initCocoaWindow(windowText, clientExtent); + + appEvent.notify(this, &MacWindow::_onAppEvent); + + sInstance = this; +} + +MacWindow::~MacWindow() +{ + if(mFullscreen) + _setFullscreen(false); + + appEvent.remove(this, &MacWindow::_onAppEvent); + + //ensure our view isn't the delegate + [NSApp setDelegate:nil]; + + if( mCocoaWindow ) + { + NSWindow* window = mCocoaWindow; + _disassociateCocoaWindow(); + + [ window close ]; + } + + appEvent.trigger(mWindowId, LoseFocus); + appEvent.trigger(mWindowId, WindowDestroy); + + mOwningWindowManager->_removeWindow(this); + + setSafariWindow(NULL); + + sInstance = NULL; +} + +extern "C" +{ + +void torque_setsafariwindow( NSWindow *window, S32 x, S32 y, S32 width, S32 height) +{ + MacWindow::setSafariWindow(window, x, y, width, height); +} + +} + +void MacWindow::hideBrowserWindow(bool hide) +{ + if (sSafariWindowInfo && sInstance && sInstance->mCocoaWindow) + { + if (hide) + { + if (sSafariWindowInfo && sSafariWindowInfo->safariWindow) + [sSafariWindowInfo->safariWindow removeChildWindow: sInstance->mCocoaWindow]; + + sInstance->hide(); + } + else + { + + if (sSafariWindowInfo && sSafariWindowInfo->safariWindow) + [sSafariWindowInfo->safariWindow addChildWindow: sInstance->mCocoaWindow ordered:NSWindowAbove]; + + sInstance->show(); + } + } +} + +void MacWindow::setSafariWindow(NSWindow *window, S32 x, S32 y, S32 width, S32 height ) +{ + if (!window) + { + hideBrowserWindow(true); + + if (sSafariWindowInfo) + delete sSafariWindowInfo; + + sSafariWindowInfo = NULL; + + return; + } + + if (!sSafariWindowInfo) + { + sSafariWindowInfo = new SafariWindowInfo; + sSafariWindowInfo->safariWindow = window; + sSafariWindowInfo->width = width; + sSafariWindowInfo->height = height; + sSafariWindowInfo->x = x; + sSafariWindowInfo->y = y; + if (sInstance && sInstance->mCocoaWindow) + { + [window addChildWindow: sInstance->mCocoaWindow ordered:NSWindowAbove]; + hideBrowserWindow(false); + } + } + else + { + sSafariWindowInfo->width = width; + sSafariWindowInfo->height = height; + sSafariWindowInfo->x = x; + sSafariWindowInfo->y = y; + } + + if (sInstance && sInstance->mCocoaWindow) + { + //update position + + NSRect frame = [sSafariWindowInfo->safariWindow frame]; + + NSPoint o = { (float)sSafariWindowInfo->x, frame.size.height - sSafariWindowInfo->y }; + NSPoint p = [sSafariWindowInfo->safariWindow convertBaseToScreen: o]; + + NSRect contentRect = NSMakeRect(p.x, p.y - sSafariWindowInfo->height, sSafariWindowInfo->width,sSafariWindowInfo->height); + + // we have to set display to NO when resizing otherwise get hangs, perhaps add delegate to SafariBrowserWindow class? + [sInstance->mCocoaWindow setFrame:contentRect display:NO]; + + } + +} + +void MacWindow::_initCocoaWindow(const char* windowText, Point2I clientExtent) +{ + // TODO: cascade windows on screen? + + // create the window + NSRect contentRect; + U32 style; + + if (sSafariWindowInfo) + { + + NSRect frame = [sSafariWindowInfo->safariWindow frame]; + + NSPoint o = { (float)sSafariWindowInfo->x, frame.size.height - sSafariWindowInfo->y }; + + NSPoint p = [sSafariWindowInfo->safariWindow convertBaseToScreen: o]; + + contentRect = NSMakeRect(0, 0, sSafariWindowInfo->width,sSafariWindowInfo->height); + + style = NSBorderlessWindowMask; + + mCocoaWindow = [[SafariBrowserWindow alloc] initWithContentRect:contentRect styleMask:style backing:NSBackingStoreBuffered defer:YES screen:nil]; + + [mCocoaWindow setFrameTopLeftPoint: p]; + + [sSafariWindowInfo->safariWindow addChildWindow: mCocoaWindow ordered:NSWindowAbove]; + + // necessary to accept mouseMoved events + [mCocoaWindow setAcceptsMouseMovedEvents:YES]; + } + else + { + + contentRect = NSMakeRect(0,0,clientExtent.x, clientExtent.y); + + style = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask; + + mCocoaWindow = [[NSWindow alloc] initWithContentRect:contentRect styleMask:style backing:NSBackingStoreBuffered defer:YES screen:nil]; + if(windowText) + [mCocoaWindow setTitle: [NSString stringWithUTF8String: windowText]]; + + // necessary to accept mouseMoved events + [mCocoaWindow setAcceptsMouseMovedEvents:YES]; + + // correctly position the window on screen + [mCocoaWindow center]; + + } + + // create the opengl view. we don't care about its pixel format, because we + // will be replacing its context with another one. + GGMacView* view = [[GGMacView alloc] initWithFrame:contentRect pixelFormat:[NSOpenGLView defaultPixelFormat]]; + [view setTorqueWindow:this]; + [mCocoaWindow setContentView:view]; + [mCocoaWindow setDelegate:view]; + +} + +void MacWindow::_disassociateCocoaWindow() +{ + if( !mCocoaWindow ) + return; + + [mCocoaWindow setContentView:nil]; + [mCocoaWindow setDelegate:nil]; + + if (sSafariWindowInfo) + [sSafariWindowInfo->safariWindow removeChildWindow: mCocoaWindow]; + + mCocoaWindow = NULL; +} + +void MacWindow::setVideoMode(const GFXVideoMode &mode) +{ + mCurrentMode = mode; + setSize(mCurrentMode.resolution); + + if(mTarget.isValid()) + mTarget->resetMode(); + + _setFullscreen(mCurrentMode.fullScreen); +} + +void MacWindow::_onAppEvent(WindowId, S32 evt) +{ + if(evt == LoseFocus && isFullscreen()) + { + mShouldFullscreen = true; + GFXVideoMode mode = mCurrentMode; + mode.fullScreen = false; + setVideoMode(mode); + } + + if(evt == GainFocus && !isFullscreen() && mShouldFullscreen) + { + mShouldFullscreen = false; + GFXVideoMode mode = mCurrentMode; + mode.fullScreen = true; + setVideoMode(mode); + } +} + +void MacWindow::_setFullscreen(bool fullScreen) +{ + if(mFullscreen == fullScreen) + return; + + mFullscreen = fullScreen; + + if(mFullscreen) + { + Con::printf("Capturing display %x", mDisplay); + CGDisplayCapture(mDisplay); + [mCocoaWindow setAlphaValue:0.0f]; + } + else + { + if(mDefaultDisplayMode) + { + Con::printf("Restoring default display mode... width: %i height: %i bpp: %i", [[mDefaultDisplayMode valueForKey:@"Width"] intValue], + [[mDefaultDisplayMode valueForKey:@"Height"] intValue], [[mDefaultDisplayMode valueForKey:@"BitsPerPixel"] intValue]); + CGDisplaySwitchToMode(mDisplay, (CFDictionaryRef)mDefaultDisplayMode); + mDisplayBounds = CGDisplayBounds(mDisplay); + if(mDisplay == kCGDirectMainDisplay) + mMainDisplayBounds = mDisplayBounds; + } + + Con::printf("Releasing display %x", mDisplay); + CGDisplayRelease(mDisplay); + [mCocoaWindow setAlphaValue:1.0f]; + mDefaultDisplayMode = NULL; + } +} + +void* MacWindow::getPlatformDrawable() const +{ + return [mCocoaWindow contentView]; +} + +void MacWindow::show() +{ + [mCocoaWindow makeKeyAndOrderFront:nil]; + [mCocoaWindow makeFirstResponder:[mCocoaWindow contentView]]; + appEvent.trigger(getWindowId(), WindowShown); + appEvent.trigger(getWindowId(), GainFocus); +} + +void MacWindow::close() +{ + [mCocoaWindow close]; + appEvent.trigger(mWindowId, LoseFocus); + appEvent.trigger(mWindowId, WindowDestroy); + + mOwningWindowManager->_removeWindow(this); + + delete this; +} + +void MacWindow::hide() +{ + [mCocoaWindow orderOut:nil]; + appEvent.trigger(getWindowId(), WindowHidden); +} + +void MacWindow::setDisplay(CGDirectDisplayID display) +{ + mDisplay = display; + mDisplayBounds = CGDisplayBounds(mDisplay); +} + +PlatformWindow* MacWindow::getNextWindow() const +{ + return mNextWindow; +} + +bool MacWindow::setSize(const Point2I &newSize) +{ + if (sSafariWindowInfo) + return true; + + NSSize newExtent = {newSize.x, newSize.y}; + [mCocoaWindow setContentSize:newExtent]; + [mCocoaWindow center]; + return true; +} + +void MacWindow::setClientExtent( const Point2I newExtent ) +{ + if(!mFullscreen) + { + // Set the Client Area Extent (Resolution) of this window + NSSize newSize = {newExtent.x, newExtent.y}; + [mCocoaWindow setContentSize:newSize]; + } + else + { + // In fullscreen we have to resize the monitor (it'll be good to change it back too...) + if(!mDefaultDisplayMode) + mDefaultDisplayMode = (NSDictionary*)CGDisplayCurrentMode(mDisplay); + + NSDictionary* newMode = (NSDictionary*)CGDisplayBestModeForParameters(mDisplay, 32, newExtent.x, newExtent.y, NULL); + Con::printf("Switching to new display mode... width: %i height: %i bpp: %i", + [[newMode valueForKey:@"Width"] intValue], [[newMode valueForKey:@"Height"] intValue], [[newMode valueForKey:@"BitsPerPixel"] intValue]); + CGDisplaySwitchToMode(mDisplay, (CFDictionaryRef)newMode); + mDisplayBounds = CGDisplayBounds(mDisplay); + if(mDisplay == kCGDirectMainDisplay) + mMainDisplayBounds = mDisplayBounds; + } +} + +const Point2I MacWindow::getClientExtent() +{ + if(!mFullscreen) + { + // Get the Client Area Extent (Resolution) of this window + NSSize size = [[mCocoaWindow contentView] frame].size; + return Point2I(size.width, size.height); + } + else + { + return Point2I(mDisplayBounds.size.width, mDisplayBounds.size.height); + } +} + +void MacWindow::setBounds( const RectI &newBounds ) +{ + NSRect newFrame = NSMakeRect(newBounds.point.x, newBounds.point.y, newBounds.extent.x, newBounds.extent.y); + [mCocoaWindow setFrame:newFrame display:YES]; +} + +const RectI MacWindow::getBounds() const +{ + if(!mFullscreen) + { + // Get the position and size (fullscreen windows are always at (0,0)). + NSRect frame = [mCocoaWindow frame]; + return RectI(frame.origin.x, frame.origin.y, frame.size.width, frame.size.height); + } + else + { + return RectI(0, 0, mDisplayBounds.size.width, mDisplayBounds.size.height); + } +} + +void MacWindow::setPosition( const Point2I newPosition ) +{ + NSScreen *screen = [mCocoaWindow screen]; + NSRect screenFrame = [screen frame]; + + NSPoint pos = {newPosition.x, newPosition.y + screenFrame.size.height}; + [mCocoaWindow setFrameTopLeftPoint: pos]; +} + +const Point2I MacWindow::getPosition() +{ + NSScreen *screen = [mCocoaWindow screen]; + NSRect screenFrame = [screen frame]; + NSRect frame = [mCocoaWindow frame]; + + return Point2I(frame.origin.x, screenFrame.size.height - (frame.origin.y + frame.size.height)); +} + +void MacWindow::centerWindow() +{ + [mCocoaWindow center]; +} + +Point2I MacWindow::clientToScreen( const Point2I& pos ) +{ + NSPoint p = { pos.x, pos.y }; + + p = [ mCocoaWindow convertBaseToScreen: p ]; + return Point2I( p.x, p.y ); +} + +Point2I MacWindow::screenToClient( const Point2I& pos ) +{ + NSPoint p = { pos.x, pos.y }; + + p = [ mCocoaWindow convertScreenToBase: p ]; + return Point2I( p.x, p.y ); +} + +bool MacWindow::isFocused() +{ + return [mCocoaWindow isKeyWindow]; +} + +bool MacWindow::isOpen() +{ + // Maybe check if _window != NULL ? + return true; +} + +bool MacWindow::isVisible() +{ + return !isMinimized() && ([mCocoaWindow isVisible] == YES); +} + +void MacWindow::setFocus() +{ + [mCocoaWindow makeKeyAndOrderFront:nil]; +} + +void MacWindow::signalGainFocus() +{ + if(isFocused()) + [[mCocoaWindow delegate] performSelector:@selector(signalGainFocus)]; +} + +void MacWindow::minimize() +{ + if(!isVisible()) + return; + + [mCocoaWindow miniaturize:nil]; + appEvent.trigger(getWindowId(), WindowHidden); +} + +void MacWindow::maximize() +{ + if(!isVisible()) + return; + + // GFX2_RENDER_MERGE + //[mCocoaWindow miniaturize:nil]; + //appEvent.trigger(getWindowId(), WindowHidden); +} + +void MacWindow::restore() +{ + if(!isMinimized()) + return; + + [mCocoaWindow deminiaturize:nil]; + appEvent.trigger(getWindowId(), WindowShown); +} + +bool MacWindow::isMinimized() +{ + return [mCocoaWindow isMiniaturized] == YES; +} + +bool MacWindow::isMaximized() +{ + return false; +} + +void MacWindow::clearFocus() +{ + // Clear the focus state for this Window. + // If the Window does not have focus, nothing happens. + // If the Window has focus, it relinquishes it's focus to the Operating System + + // TODO: find out if we can do anything correct here. We are instructed *not* to call [NSWindow resignKeyWindow], and we don't necessarily have another window to assign as key. +} + +bool MacWindow::setCaption(const char* windowText) +{ + mTitle = windowText; + [mCocoaWindow setTitle:[NSString stringWithUTF8String:mTitle]]; + return true; +} + +void MacWindow::_doMouseLockNow() +{ + if(!isVisible()) + return; + + if(mShouldMouseLock == mMouseLocked && mMouseLocked != isCursorVisible()) + return; + + if(mShouldMouseLock) + _dissociateMouse(); + else + _associateMouse(); + + // hide the cursor if we're locking, show it if we're unlocking + setCursorVisible(!shouldLockMouse()); + + mMouseLocked = mShouldMouseLock; + + return; +} + +void MacWindow::_associateMouse() +{ + CGAssociateMouseAndMouseCursorPosition(true); +} + +void MacWindow::_dissociateMouse() +{ + _centerMouse(); + CGAssociateMouseAndMouseCursorPosition(false); +} + +void MacWindow::_centerMouse() +{ + NSRect frame = [mCocoaWindow frame]; + + // Deal with the y flip (really fun when more than one monitor is involved) + F32 offsetY = mMainDisplayBounds.size.height - mDisplayBounds.size.height; + frame.origin.y = (mDisplayBounds.size.height + offsetY) - (S32)frame.origin.y - (S32)frame.size.height; + mCursorController->setCursorPosition(frame.origin.x + frame.size.width / 2, frame.origin.y + frame.size.height / 2); +} diff --git a/Engine/source/windowManager/mac/macWindowManager.h b/Engine/source/windowManager/mac/macWindowManager.h new file mode 100644 index 000000000..7fcf36943 --- /dev/null +++ b/Engine/source/windowManager/mac/macWindowManager.h @@ -0,0 +1,129 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _MACWINDOWMANAGER_H_ +#define _MACWINDOWMANAGER_H_ + +#include "windowManager/platformWindowMgr.h" +#include "core/util/tVector.h" + +class MacWindow; + +class MacWindowManager : public PlatformWindowManager +{ +private: + typedef VectorPtr WindowList; + WindowList mWindowList; + U32 mFadeToken; + Delegate mNotifyShutdownDelegate; + +public: + MacWindowManager(); + ~MacWindowManager(); + + virtual void setParentWindow(void* newParent) { + } + + /// Get the parent window + virtual void* getParentWindow() + { + return NULL; + } + + virtual PlatformWindow *createWindow(GFXDevice *device, const GFXVideoMode &mode); + + /// @name Desktop Queries + /// @{ + + /// Return the extents in window coordinates of the primary desktop + /// area. On a single monitor system this is just the display extents. + /// On a multi-monitor system this is the primary monitor (which Torque should + /// launch on). + virtual RectI getPrimaryDesktopArea(); + + /// Populate a vector with all monitors and their extents in window space. + virtual void getMonitorRegions(Vector ®ions); + + /// Retrieve the currently set desktop bit depth + /// @return The current desktop bit depth, or -1 if an error occurred + virtual S32 getDesktopBitDepth(); + + /// Retrieve the currently set desktop resolution + /// @return The current desktop bit depth, or Point2I(-1,-1) if an error occurred + virtual Point2I getDesktopResolution(); + + /// @} + + /// @name Window Lookup + /// @{ + + /// Get the number of Window's in this system + virtual S32 getWindowCount(); + + /// Populate a list with references to all the windows created from this manager. + virtual void getWindows(VectorPtr &windows); + + /// Get a window from a device ID. + /// + /// @return The window associated with the specified ID, or NULL if no + /// match was found. + virtual PlatformWindow *getWindowById(WindowId id); + + virtual PlatformWindow *getFirstWindow(); + virtual PlatformWindow* getFocusedWindow(); + + /// During full-screen toggles we want to suppress ugly transition states, + /// which we do (on Win32) by showing and hiding a full-monitor black window. + /// + /// This method cues the appearance of that window ("lowering the curtain"). + virtual void lowerCurtain(); + + /// @see lowerCurtain + /// + /// This method removes the curtain window. + virtual void raiseCurtain(); + + /// @} + /// @name Command Line Usage + /// @{ + + /// Process command line arguments sent to this window manager + /// to manipulate it's windows. + virtual void _processCmdLineArgs(const S32 argc, const char **argv); + + /// @} + + // static MacWindowManager* get() { return (MacWindowManager*)PlatformWindowManager::get(); } + void _addWindow(MacWindow* window); + void _removeWindow(MacWindow* window); + + void _onAppSignal(WindowId wnd, S32 event); + + bool onShutdown(); + bool canWindowGainFocus(MacWindow* window); + + +private: + bool mIsShuttingDown; +}; + +#endif diff --git a/Engine/source/windowManager/mac/macWindowManager.mm b/Engine/source/windowManager/mac/macWindowManager.mm new file mode 100644 index 000000000..31b063d03 --- /dev/null +++ b/Engine/source/windowManager/mac/macWindowManager.mm @@ -0,0 +1,245 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include +#include "windowManager/mac/macWindowManager.h" +#include "windowManager/mac/macWindow.h" +#include "core/util/journal/process.h" +#include "console/console.h" +#include "gfx/gfxDevice.h" + +PlatformWindowManager* CreatePlatformWindowManager() +{ + return new MacWindowManager(); +} + +static inline RectI convertCGRectToRectI(NSRect r) +{ + return RectI(r.origin.x, r.origin.y, r.size.width, r.size.height); +} + +MacWindowManager::MacWindowManager() : mNotifyShutdownDelegate(this, &MacWindowManager::onShutdown), mIsShuttingDown(false) +{ + mWindowList.clear(); + Process::notifyShutdown(mNotifyShutdownDelegate); +} + +MacWindowManager::~MacWindowManager() +{ + for(U32 i = 0; i < mWindowList.size(); i++) + delete mWindowList[i]; + mWindowList.clear(); + + CGReleaseDisplayFadeReservation(mFadeToken); +} + +RectI MacWindowManager::getPrimaryDesktopArea() +{ + // Get the area of the main desktop that isn't taken by the dock or menu bar. + return convertCGRectToRectI([[NSScreen mainScreen] visibleFrame]); +} + +void MacWindowManager::getMonitorRegions(Vector ®ions) +{ + // Populate a vector with all monitors and their extents in window space. + NSArray *screenList = [NSScreen screens]; + for(U32 i = 0; i < [screenList count]; i++) + { + NSRect screenBounds = [[screenList objectAtIndex: i] frame]; + regions.push_back(convertCGRectToRectI(screenBounds)); + } +} + +S32 MacWindowManager::getDesktopBitDepth() +{ + // get the current desktop bit depth + // TODO: return -1 if an error occurred + return NSBitsPerPixelFromDepth([[NSScreen mainScreen] depth]); +} + +Point2I MacWindowManager::getDesktopResolution() +{ + // get the current desktop width/height + // TODO: return Point2I(-1,-1) if an error occurred + NSRect desktopBounds = [[NSScreen mainScreen] frame]; + return Point2I((U32)desktopBounds.size.width, (U32)desktopBounds.size.height); +} + +S32 MacWindowManager::getWindowCount() +{ + // Get the number of PlatformWindow's in this manager + return mWindowList.size(); +} + +void MacWindowManager::getWindows(VectorPtr &windows) +{ + // Populate a list with references to all the windows created from this manager. + windows.merge(mWindowList); +} + +PlatformWindow * MacWindowManager::getFirstWindow() +{ + if (mWindowList.size() > 0) + return mWindowList[0]; + + return NULL; +} + + +PlatformWindow* MacWindowManager::getFocusedWindow() +{ + for (U32 i = 0; i < mWindowList.size(); i++) + { + if( mWindowList[i]->isFocused() ) + return mWindowList[i]; + } + + return NULL; +} + +PlatformWindow* MacWindowManager::getWindowById(WindowId zid) +{ + // Find the window by its arbirary WindowId. + for(U32 i = 0; i < mWindowList.size(); i++) + { + PlatformWindow* w = mWindowList[i]; + if( w->getWindowId() == zid) + return w; + } + return NULL; +} + +void MacWindowManager::lowerCurtain() +{ + // fade all displays. + CGError err; + err = CGAcquireDisplayFadeReservation(kCGMaxDisplayReservationInterval, &mFadeToken); + AssertWarn(!err, "MacWindowManager::lowerCurtain() could not get a token"); + if(err) return; + + err = CGDisplayFade(mFadeToken, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0, 0, 0, true); + AssertWarn(!err, "MacWindowManager::lowerCurtain() failed the fade"); + if(err) return; + + // we do not release the token, because that will un-fade the screen! + // the token will last for 15 sec, and then the screen will un-fade regardless. + //CGReleaseDisplayFadeReservation(mFadeToken); +} + +void MacWindowManager::raiseCurtain() +{ + // release the fade on all displays + CGError err; + err = CGDisplayFade(mFadeToken, 0.3, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0, 0, 0, false); + AssertWarn(!err, "MacWindowManager::raiseCurtain() failed the fade"); + + err = CGReleaseDisplayFadeReservation(mFadeToken); + AssertWarn(!err, "MacWindowManager::raiseCurtain() failed releasing the token"); +} + + +void MacWindowManager::_processCmdLineArgs(const S32 argc, const char **argv) +{ + // TODO: accept command line args if necessary. +} + +PlatformWindow *MacWindowManager::createWindow(GFXDevice *device, const GFXVideoMode &mode) +{ + MacWindow* window = new MacWindow(getNextId(), getEngineProductString(), mode.resolution); + _addWindow(window); + + // Set the video mode on the window + window->setVideoMode(mode); + + // Make sure our window is shown and drawn to. + window->show(); + + // Bind the window to the specified device. + if(device) + { + window->mDevice = device; + window->mTarget = device->allocWindowTarget(window); + AssertISV(window->mTarget, + "MacWindowManager::createWindow - failed to get a window target back from the device."); + } + else + { + Con::warnf("MacWindowManager::createWindow - created a window with no device!"); + } + + return window; +} + +void MacWindowManager::_addWindow(MacWindow* window) +{ +#ifdef TORQUE_DEBUG + // Make sure we aren't adding the window twice + for(U32 i = 0; i < mWindowList.size(); i++) + AssertFatal(window != mWindowList[i], "MacWindowManager::_addWindow - Should not add a window more than once"); +#endif + if (mWindowList.size() > 0) + window->mNextWindow = mWindowList.last(); + else + window->mNextWindow = NULL; + + mWindowList.push_back(window); + window->mOwningWindowManager = this; + window->appEvent.notify(this, &MacWindowManager::_onAppSignal); +} + +void MacWindowManager::_removeWindow(MacWindow* window) +{ + for(WindowList::iterator i = mWindowList.begin(); i != mWindowList.end(); i++) + { + if(*i == window) + { + mWindowList.erase(i); + return; + } + } + AssertFatal(false, avar("MacWindowManager::_removeWindow - Failed to remove window %x, perhaps it was already removed?", window)); +} + +void MacWindowManager::_onAppSignal(WindowId wnd, S32 event) +{ + if(event != WindowHidden) + return; + + for(U32 i = 0; i < mWindowList.size(); i++) + { + if(mWindowList[i]->getWindowId() == wnd) + continue; + + mWindowList[i]->signalGainFocus(); + } +} + +bool MacWindowManager::onShutdown() +{ + mIsShuttingDown = true; + return true; +} + +bool MacWindowManager::canWindowGainFocus(MacWindow* window) +{ + return !mIsShuttingDown; +} diff --git a/Engine/source/windowManager/platformCursorController.cpp b/Engine/source/windowManager/platformCursorController.cpp new file mode 100644 index 000000000..aba10a62c --- /dev/null +++ b/Engine/source/windowManager/platformCursorController.cpp @@ -0,0 +1,94 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "windowManager/platformCursorController.h" + +void PlatformCursorController::pushCursor( S32 cursorID ) +{ + // Place the new cursor shape onto the stack + mCursors.increment(); + + CursorShape &shape = mCursors.last(); + shape.mCursorType = CursorShape::TYPE_RESOURCE; + shape.mCursorID = cursorID; + + // Now Change the Cursor Shape. + setCursorShape( shape.mCursorID ); +} + +void PlatformCursorController::pushCursor( const UTF8 *fileName ) +{ + // Place the new cursor shape onto the stack + mCursors.increment(); + + // Store the Details. + CursorShape &shape = mCursors.last(); + shape.mCursorType = CursorShape::TYPE_FILE; + shape.mCursorFile = String::ToString( "%s", fileName ); + + // Now Change the Cursor Shape. + setCursorShape( shape.mCursorFile.c_str(), true ); +} + +void PlatformCursorController::popCursor() +{ + // Before poping the stack, make sure we're not trying to remove the last cursor shape + if ( mCursors.size() <= 1 ) + { + return; + } + + // Clear the Last Cursor. + mCursors.pop_back(); + + // Now Change the Cursor Shape. + setCursorShape( mCursors.last(), true ); +} + +void PlatformCursorController::refreshCursor() +{ + // Refresh the Cursor Shape. + setCursorShape( mCursors.last(), false ); +} + +void PlatformCursorController::setCursorShape( const CursorShape &shape, bool reload ) +{ + switch( shape.mCursorType ) + { + case CursorShape::TYPE_RESOURCE : + { + + // Set Resource. + setCursorShape( shape.mCursorID ); + + } break; + + case CursorShape::TYPE_FILE : + { + + // Set File. + setCursorShape( shape.mCursorFile.c_str(), reload ); + + } break; + } +} \ No newline at end of file diff --git a/Engine/source/windowManager/platformCursorController.h b/Engine/source/windowManager/platformCursorController.h new file mode 100644 index 000000000..339ed94d6 --- /dev/null +++ b/Engine/source/windowManager/platformCursorController.h @@ -0,0 +1,102 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PLATFORMCURSORCONTROLLER_H_ +#define _PLATFORMCURSORCONTROLLER_H_ + +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +class PlatformWindow; + +class PlatformCursorController +{ +protected: + + struct CursorShape + { + enum Type + { + TYPE_RESOURCE, + TYPE_FILE, + }; + + Type mCursorType; + S32 mCursorID; // Points to a platform specific cursor ID + String mCursorFile; // Points to a custom cursor file + }; + + Vector mCursors; + + /// The PlatformWindow that owns this Cursor Controller + PlatformWindow *mOwner; + +public: + + PlatformCursorController( PlatformWindow *owner ) : + mOwner( owner ) + { + VECTOR_SET_ASSOCIATION( mCursors ); + }; + + virtual ~PlatformCursorController() + { + mOwner = NULL; + }; + + enum + { + curArrow = 0, + curWait, + curPlus, + curResizeVert, + curResizeHorz, + curResizeAll, + curIBeam, + curResizeNESW, + curResizeNWSE, + curHand, + }; + +public: + + virtual void setCursorPosition(S32 x, S32 y) = 0; + virtual void getCursorPosition( Point2I &point ) = 0; + virtual void setCursorVisible( bool visible ) = 0; + virtual bool isCursorVisible() = 0; + + virtual void setCursorShape( const CursorShape &shape, bool reload ); + virtual void setCursorShape( U32 cursorID ) = 0; + virtual void setCursorShape( const UTF8 *filename, bool reload ) = 0; + + virtual void pushCursor( S32 cursorID ); + virtual void pushCursor( const UTF8 *fileName ); + virtual void popCursor(); + virtual void refreshCursor(); + + virtual U32 getDoubleClickTime() = 0; + virtual S32 getDoubleClickWidth() = 0; + virtual S32 getDoubleClickHeight() = 0; +}; + +#endif \ No newline at end of file diff --git a/Engine/source/windowManager/platformInterface.cpp b/Engine/source/windowManager/platformInterface.cpp new file mode 100644 index 000000000..821c6c60d --- /dev/null +++ b/Engine/source/windowManager/platformInterface.cpp @@ -0,0 +1,143 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "platform/event.h" +#include "windowManager/platformWindowMgr.h" +#include "gfx/gfxInit.h" +#include "gfx/gfxDevice.h" +#include "core/util/journal/process.h" +#include "core/util/autoPtr.h" + +// This file converts from the windowmanager system to the old platform and +// event interfaces. So it's a bit hackish but hopefully will not be necessary +// for very long. Because of the heavy use of globals in the old platform +// layer, this layer also has a fair number of globals. + + +// WindowManager +// +// PlatformWindowManager::get() wrapped in Macro WindowManager +static AutoPtr< PlatformWindowManager > smWindowManager; +PlatformWindowManager *PlatformWindowManager::get() +{ + if( smWindowManager.isNull() ) + smWindowManager = CreatePlatformWindowManager(); + return smWindowManager.ptr(); +} + +void PlatformWindowManager::processCmdLineArgs( const S32 argc, const char **argv ) +{ + // Only call the get() routine if we have arguments on the command line + if(argc > 0) + { + PlatformWindowManager::get()->_processCmdLineArgs(argc, argv); + } +} + + +GFXDevice *gDevice = NULL; +PlatformWindow *gWindow = NULL; + +// Conversion from window manager input conventions to Torque standard. +static struct ModifierBitMap { + U32 grendelMask,torqueMask; +} _ModifierBitMap[] = { + { IM_LSHIFT, SI_LSHIFT }, + { IM_RSHIFT, SI_RSHIFT }, + { IM_LALT, SI_LALT }, + { IM_RALT, SI_RALT }, + { IM_LCTRL, SI_LCTRL }, + { IM_RCTRL, SI_RCTRL }, + { IM_LOPT, SI_MAC_LOPT }, + { IM_ROPT, SI_MAC_ROPT }, +}; +static int _ModifierBitMapCount = sizeof(_ModifierBitMap) / sizeof(ModifierBitMap); + +InputModifiers convertModifierBits(const U32 in) +{ + U32 out=0; + + for(S32 i=0; i<_ModifierBitMapCount; i++) + if(in & _ModifierBitMap[i].grendelMask) + out |= _ModifierBitMap[i].torqueMask; + + return (InputModifiers)out; +} + +//------------------------------------------------------------------------------ + +void Platform::setWindowSize(U32 newWidth, U32 newHeight, bool fullScreen ) +{ + AssertISV(gWindow, "Platform::setWindowSize - no window present!"); + + // Grab the curent video settings and diddle them with the new values. + GFXVideoMode vm = gWindow->getVideoMode(); + vm.resolution.set(newWidth, newHeight); + vm.fullScreen = fullScreen; + gWindow->setVideoMode(vm); +} + +void Platform::setWindowLocked(bool locked) +{ + PlatformWindow* window = WindowManager->getFirstWindow(); + if( window ) + window->setMouseLocked( locked ); +} + +void Platform::minimizeWindow() +{ + // requires PlatformWindow API extension... + if(gWindow) + gWindow->minimize(); +} + +void Platform::closeWindow() +{ + // Shutdown all our stuff. + //SAFE_DELETE(gDevice); // <-- device is already cleaned up elsewhere by now... + SAFE_DELETE(gWindow); +} + +//------------------------------------------------------------------------------ + + + +#ifdef TORQUE_OS_WIN32 +// Hack so we can get the HWND of the global window more easily - replacement +// for the HWND that was in the platstate. +#include "windowManager/win32/win32Window.h" + +HWND getWin32WindowHandle() +{ + PlatformWindow* window = WindowManager->getFocusedWindow(); + if( !window ) + { + window = WindowManager->getFirstWindow(); + if( !window ) + return NULL; + } + + return ( ( Win32Window* ) window )->getHWND(); +} + +#endif \ No newline at end of file diff --git a/Engine/source/windowManager/platformWindow.cpp b/Engine/source/windowManager/platformWindow.cpp new file mode 100644 index 000000000..8a7ad65fb --- /dev/null +++ b/Engine/source/windowManager/platformWindow.cpp @@ -0,0 +1,50 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "windowManager/platformWindow.h" + + +//----------------------------------------------------------------------------- + +void PlatformWindow::setFullscreen( const bool fullscreen ) +{ + // Notify listeners that we will acquire the screen + if(fullscreen && !Journal::IsDispatching()) + appEvent.trigger(getWindowId(),GainScreen); + + // Do platform specific fullscreen code + _setFullscreen(fullscreen); + + // Notify listeners that we released the screen + if(!fullscreen && !Journal::IsDispatching()) + appEvent.trigger(getWindowId(),LoseScreen); +} + +//----------------------------------------------------------------------------- + +bool PlatformWindow::shouldNotTranslate( U32 modifiers, U32 keyCode ) const +{ + if( mWindowInputGenerator ) + return mWindowInputGenerator->wantAsKeyboardEvent( modifiers, keyCode ); + else + return false; +} diff --git a/Engine/source/windowManager/platformWindow.h b/Engine/source/windowManager/platformWindow.h new file mode 100644 index 000000000..11d28d384 --- /dev/null +++ b/Engine/source/windowManager/platformWindow.h @@ -0,0 +1,494 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _WINDOWMANAGER_PLATFORMWINDOW_H_ +#define _WINDOWMANAGER_PLATFORMWINDOW_H_ + +#include "math/mRect.h" +#include "core/util/journal/journaledSignal.h" +#include "core/util/safeDelete.h" +#include "windowManager/platformCursorController.h" +#include "windowManager/windowInputGenerator.h" + +//forward decl's +class PlatformWindowManager; +class GFXDevice; +struct GFXVideoMode; +class GFXWindowTarget; +class IProcessInput; + +/// Abstract representation of a native OS window. +/// +/// Every windowing system has its own representations and conventions as +/// regards the windows on-screen. In order to provide Torque with means for +/// interfacing with multiple windows, tracking their state, etc. we provide +/// this interface. +/// +/// This interface also allows the app to access the render target for the +/// window it represents, as well as control mode switches, get mode info, +/// and so on. +/// +/// @see PlatformWindowManager +class PlatformWindow +{ + friend class PlatformWindowManager; +protected: + + /// Are we enabling IME or other keyboard input translation services, + /// or concerned about raw input? + bool mEnableKeyboardTranslation; + + /// When Torque GuiText input controls have focus they need to + /// disable native OS keyboard accelerator translation. + bool mEnableAccelerators; + + /// Minimum allowed size for this window. When possible, we will communicate + /// this to the OS. + Point2I mMinimumSize; + + /// When the resize is locked, this will be used as both minimum and maximum window size + Point2I mLockedSize; + + /// When this is true, resizing is locked + bool mResizeLocked; + + /// Is Idle? + bool mIsBackground; + + /// Cursor Controller for this Window + PlatformCursorController *mCursorController; + + /// An opaque ID used to resolve references to this Window + WindowId mWindowId; + + /// Window Mouse/Key input Controller for this Window + WindowInputGenerator *mWindowInputGenerator; + + /// Suppress device resets + bool mSuppressReset; + + /// Offscreen Render + bool mOffscreenRender; + + /// Protected constructor so that the win + PlatformWindow() + { + mIsBackground = false; // This could be toggled to true to prefer performance. + mMinimumSize.set(0,0); + mLockedSize.set(0,0); + mResizeLocked = false; + mEnableKeyboardTranslation = false; + mEnableAccelerators = true; + mCursorController = NULL; + // This controller maps window input (Mouse/Keyboard) to a generic input consumer + mWindowInputGenerator = new WindowInputGenerator( this ); + mSuppressReset = false; + + mOffscreenRender = false; + } + +public: + + /// To get rid of a window, just delete it. Make sure the GFXDevice is + /// done with it first! + virtual ~PlatformWindow() + { + SAFE_DELETE( mCursorController ); + SAFE_DELETE( mWindowInputGenerator ); + } + + /// Get the WindowController associated with this window + virtual void setInputController( IProcessInput *controller ) { if( mWindowInputGenerator ) mWindowInputGenerator->setInputController( controller ); }; + + /// Get the ID that uniquely identifies this window in the context of its + /// window manager. + virtual WindowId getWindowId() { return 0; }; + + /// Set the flag that determines whether to suppress a GFXDevice reset + inline void setSuppressReset(bool suppress) { mSuppressReset = suppress; }; + + /// @name GFX State Management + /// + /// @{ + + /// Return a pointer to the GFX device this window is bound to. A GFX + /// device may use many windows, but a window can only be used by a + /// single GFX device. + virtual GFXDevice *getGFXDevice()=0; + + /// Return a pointer to this window's render target. + /// + /// By setting window targets from different windows, we can effect + /// rendering to multiple windows from a single device. + virtual GFXWindowTarget *getGFXTarget()=0; + + /// Set the video mode for this window. + virtual void setVideoMode(const GFXVideoMode &mode)=0; + + /// Get our current video mode - if the window has been resized, it will + /// reflect this. + virtual const GFXVideoMode &getVideoMode()=0; + + /// If we're fullscreen, this function returns us to desktop mode. + /// + /// This will be either the last mode that we had that was not + /// fullscreen, or the equivalent mode, windowed. + virtual bool clearFullscreen()=0; + + /// @return true if this window is fullscreen, false otherwise. + virtual bool isFullscreen()=0; + + /// Acquire the entire screen + void setFullscreen(const bool fullscreen); + + /// Set Idle State (Background) + /// + /// This is called to put a window into idle state, which causes it's + /// rendering priority to be toned down to prefer performance + virtual void setBackground( bool val ) { mIsBackground = val; }; + + /// Get Idle State (Background) + /// + /// This is called to poll the window as to it's idle state. + virtual bool getBackground() { return mIsBackground; }; + + /// Set whether this window is intended for offscreen rendering + /// An offscreen window will never be shown or lose focus + virtual void setOffscreenRender(bool val ) { mOffscreenRender = val; }; + + /// Set whether this window is intended for offscreen rendering + /// + /// This is called to poll the window as to it's idle state. + virtual bool getOffscreenRender() { return mOffscreenRender; }; + + + /// Set Focused State (Foreground) + /// + /// Claim OS input focus for this window + virtual void setFocus() { } + /// @} + + /// @name Caption + /// + /// @{ + + /// Set the window's caption. + virtual bool setCaption(const char *cap)=0; + + /// Get the window's caption. + virtual const char *getCaption()=0; + + /// @} + + /// @name Visibility + /// + /// Control how the window is displayed + /// @{ + + /// Minimize the window on screen + virtual void minimize()=0; + + /// Maximize the window on screen + virtual void maximize()=0; + + /// Hide the window on screen + virtual void hide()=0; + + /// Show the window on screen + virtual void show()=0; + + /// Destroy the window on screen + virtual void close()=0; + + /// Restore the window from a Maximized or Minimized state + virtual void restore()=0; + + /// @} + + /// @name Window Bounds + /// + /// @{ + + /// The Client Rectangle or "Render Area" of a window is the area that + /// is occupied by a given client that is rendering to that window. + /// This does not include the area occupied by a title-bar, menu, + /// borders or other non-client elements. + /// @{ + + /// Set the Client Area Extent (Resolution) of this window + virtual void setClientExtent( const Point2I newExtent ) = 0; + + /// Get the Client Area Extent (Resolution) of this window + virtual const Point2I getClientExtent() = 0; + + /// @} + /// The bounds of a Window are defined as the entire area occupied by + /// that Window. This includes the area needed for a title-bar, menu, + /// borders, and other non-client elements. + /// + /// @{ + + /// Resize the window to have some new bounds. + virtual void setBounds( const RectI &newBounds ) = 0; + + /// Get the position and size (fullscreen windows are always at (0,0)). + virtual const RectI getBounds() const = 0; + + /// @} + /// The Position of a window is always in relation to the very upper left + /// of the window. This means that saying setPosition at 0,0 will put the + /// position of the window title-bar (if one exists) at 0,0 and the Client + /// area will be offset from that point by the space needed for the Non-Client + /// area. + /// @{ + + /// Set the position of this window + virtual void setPosition( const Point2I newPosition ) = 0; + + /// Get the position of this window + virtual const Point2I getPosition() = 0; + + virtual void centerWindow() {}; + + /// Resize the window to have a new size (but be in the same position). + virtual bool setSize(const Point2I &newSize)=0; + + /// @} + + /// @name Coordinate Space Conversion + /// @{ + + /// Convert the coordinate given in this window space to screen coordinates. + virtual Point2I clientToScreen( const Point2I& point ) = 0; + + /// Convert the given screen coordinates to coordinates in this window space. + virtual Point2I screenToClient( const Point2I& point ) = 0; + + /// @} + + /// @name Windowed state + /// + /// This is only really meaningful if the window is not fullscreen. + /// + /// @{ + + /// Returns true if the window is instantiated in the OS. + virtual bool isOpen() = 0; + + /// Returns true if the window is visible. + virtual bool isVisible() = 0; + + /// Returns true if the window has input focus + virtual bool isFocused() = 0; + + /// Returns true if the window is minimized + virtual bool isMinimized() = 0; + + /// Returns true if the window is maximized + virtual bool isMaximized() = 0; + + /// @name Keyboard Translation + /// + /// When keyboard translation is on, keypress events that correspond to character input + /// should be send as character input events rather than as raw key events *except* if + /// shouldNotTranslate() returns true for a specific keypress event. This enables the + /// platform layer to perform platform-specific character input mapping. + /// + /// @{ + + /// Set if relevant keypress events should be translated into character input events. + virtual void setKeyboardTranslation(const bool enabled) + { + mEnableKeyboardTranslation = enabled; + } + + /// Returns true if keyboard translation is enabled. + virtual bool getKeyboardTranslation() const + { + return mEnableKeyboardTranslation; + } + + /// Returns true if the given keypress event should not be translated. + virtual bool shouldNotTranslate( U32 modifiers, U32 keyCode ) const; + + /// @} + + /// Used to disable native OS keyboard accelerators. + virtual void setAcceleratorsEnabled(const bool enabled) + { + mEnableAccelerators = enabled; + } + + /// Returns true if native OS keyboard accelerators are enabled. + virtual bool getAcceleratorsEnabled() const + { + return mEnableAccelerators; + } + + /// Sets a minimum window size. We'll work with the OS to prevent user + /// from sizing the window to less than this. Setting to (0,0) means + /// user has complete freedom of resize. + virtual void setMinimumWindowSize(Point2I minSize) + { + mMinimumSize = minSize; + } + + /// Returns the current minimum window size for this window. + virtual Point2I getMinimumWindowSize() + { + return mMinimumSize; + } + + /// Locks/unlocks window resizing + virtual void lockSize(bool locked) + { + mResizeLocked = locked; + if (mResizeLocked) + mLockedSize = getBounds().extent; + } + + /// Returns true if the window size is locked + virtual bool isSizeLocked() + { + return mResizeLocked; + } + + /// Returns the locked window size + virtual Point2I getLockedSize() + { + return mLockedSize; + } + /// @} + + + /// @name Window Cursor + /// + /// Accessors to control a windows cursor shape and visibility + /// + /// @{ + /// Get the CursorController that this window owns. + virtual PlatformCursorController *getCursorController() { return mCursorController; }; + + /// Set the cursor position based on logical coordinates from the upper-right corner + /// + /// @param x The X position of the cursor + /// @param y The Y position of the cursor + virtual void setCursorPosition(S32 x, S32 y) + { + if( mCursorController != NULL ) + mCursorController->setCursorPosition(x,y); + } + + /// Get the cursor position based on logical coordinates from the upper-right corner + /// + /// @param point A reference to a Point2I to store the coordinates + virtual void getCursorPosition( Point2I &point ) + { + if( mCursorController != NULL ) + mCursorController->getCursorPosition(point); + } + + /// Set the cursor visibility on this window + /// + /// @param visible Whether the cursor should be visible or not + virtual void setCursorVisible(bool visible) + { + if( mCursorController != NULL ) + mCursorController->setCursorVisible(visible); + } + + /// Get the cursor visibility on this window + /// + /// @return true if the cursor is visible or false if it's hidden + virtual bool isCursorVisible() + { + if( mCursorController != NULL ) + return mCursorController->isCursorVisible(); + return false; + } + + /// Lock the mouse to this window. + /// + /// When this is set, the mouse will always be returned to the center + /// of the client area after every mouse event. The mouse will also be + /// hidden while it is locked. + /// + /// The mouse cannot be moved out of the bounds of the window, but the + /// window may lose focus (for instance by an alt-tab or other event). + /// While the window lacks focus, no mouse events will be reported. + virtual void setMouseLocked( bool enable )=0; + + /// Is the mouse locked ? + virtual bool isMouseLocked() const = 0; + + /// Should the mouse be locked at the next opportunity ? + /// + /// This flag is set to the current state of the mouse lock + /// on a window, to specify the preferred lock status of the + /// mouse in a platform window. + /// + /// This is important for situations where a call is made + /// to setMouseLocked, and the window is not in a state that + /// it can be cleanly locked. Take for example if it was called + /// while the window is in the background, then it is not appropriate + /// to lock the window, but rather the window should query this + /// state at it's next opportunity and lock the mouse if requested. + virtual bool shouldLockMouse() const = 0; + + /// @} + + virtual PlatformWindow * getNextWindow() const = 0; + + /// @name Event Handlers + /// + /// Various events that this window receives. These are all subclasses of + /// JournaledSignal, so you can subscribe to them and receive notifications + /// per the documentation for that class. + /// + /// @{ + + /// + AppEvent appEvent; + MouseEvent mouseEvent; + MouseWheelEvent wheelEvent; + ButtonEvent buttonEvent; + LinearEvent linearEvent; + KeyEvent keyEvent; + CharEvent charEvent; + DisplayEvent displayEvent; + ResizeEvent resizeEvent; + IdleEvent idleEvent; + + /// @} + + /// Get the platform specific object needed to create or attach an accelerated + /// graohics drawing context on or to the window + /// Win32 D3D and OpenGL typically needs an HWND + /// Mac Cocoa OpenGL typically needs an NSOpenGLView + /// Mac Carbon OpenGL typically needs a WindowRef + /// + virtual void* getPlatformDrawable() const = 0; +protected: + virtual void _setFullscreen(const bool fullScreen) {}; +}; + +#endif \ No newline at end of file diff --git a/Engine/source/windowManager/platformWindowMgr.h b/Engine/source/windowManager/platformWindowMgr.h new file mode 100644 index 000000000..8161cbba9 --- /dev/null +++ b/Engine/source/windowManager/platformWindowMgr.h @@ -0,0 +1,133 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PLATFORM_PLATFORMWINDOWMGR_H_ +#define _PLATFORM_PLATFORMWINDOWMGR_H_ + +#include "math/mRect.h" +#include "core/util/journal/journaledSignal.h" +#include "windowManager/platformWindow.h" + + +// Global macro +#define WindowManager PlatformWindowManager::get() + +/// Abstract representation of a manager for native OS windows. +/// +/// The PlatformWindowManager interface provides a variety of methods for querying +/// the current desktop configuration, as well as allocating and retrieving +/// existing windows. It may also manage application-level event handling. +class PlatformWindowManager +{ + // Generator for window IDs. + S32 mIdSource; + +protected: + /// Get the next available window Id + inline S32 getNextId() { return mIdSource++; } +public: + + /// Get Global Singleton + static PlatformWindowManager *get(); + + PlatformWindowManager() : mIdSource(0) {}; + + virtual ~PlatformWindowManager() + { + } + + static void processCmdLineArgs(const S32 argc, const char **argv); + + /// Return the extents in window coordinates of the primary desktop + /// area. On a single monitor system this is just the display extents. + /// On a multimon system this is the primary monitor (which Torque should + /// launch on). + virtual RectI getPrimaryDesktopArea() = 0; + + /// Retrieve the currently set desktop bit depth + /// @return The current desktop bit depth, or -1 if an error occurred + virtual S32 getDesktopBitDepth() = 0; + + /// Retrieve the currently set desktop resolution + /// @return The current desktop bit depth, or Point2I(-1,-1) if an error occurred + virtual Point2I getDesktopResolution() = 0; + + /// Populate a vector with all monitors and their extents in window space. + virtual void getMonitorRegions(Vector ®ions) = 0; + + /// Create a new window, appropriate for the specified device and mode. + /// + /// @return Pointer to the new window. + virtual PlatformWindow *createWindow(GFXDevice *device, const GFXVideoMode &mode) = 0; + + /// Populate a list with references to all the windows created from this manager. + virtual void getWindows(VectorPtr &windows) = 0; + + /// Get the window that currently has the input focus or NULL. + virtual PlatformWindow* getFocusedWindow() = 0; + + /// Get a window from a device ID. + /// + /// @return The window associated with the specified ID, or NULL if no + /// match was found. + virtual PlatformWindow *getWindowById(WindowId id)=0; + + /// Get the first window in the window list + /// + /// @return The first window in the list, or NULL if no windows found + virtual PlatformWindow *getFirstWindow()=0; + + + /// Set the parent window + /// + /// This can be used to render in a child window. + virtual void setParentWindow(void* newParent) = 0; + + /// Get the parent window + virtual void* getParentWindow() = 0; + + + /// This method cues the appearance of that window ("lowering the curtain"). + virtual void lowerCurtain()=0; + + /// @see lowerCurtain + /// + /// This method removes the curtain window. + virtual void raiseCurtain()=0; + +private: + /// Process command line arguments from StandardMainLoop. This is done to + /// allow web plugin functionality, where we are passed platform-specific + /// information allowing us to set ourselves up in the web browser, + /// to occur in a platform-neutral way. + virtual void _processCmdLineArgs(const S32 argc, const char **argv)=0; +}; + +/// Global function to allocate a new platform window manager. +/// +/// This returns an instance of the appropriate window manager for the current OS. +/// +/// Depending on situation (for instance, if we are a web plugin) we may +/// need to get the window manager from somewhere else. +PlatformWindowManager *CreatePlatformWindowManager(); + +#endif \ No newline at end of file diff --git a/Engine/source/windowManager/test/testWinMgr.cpp b/Engine/source/windowManager/test/testWinMgr.cpp new file mode 100644 index 000000000..efe3ec770 --- /dev/null +++ b/Engine/source/windowManager/test/testWinMgr.cpp @@ -0,0 +1,538 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/console.h" +#include "windowManager/platformWindowMgr.h" +#include "unit/test.h" +#include "core/util/tVector.h" +#include "gfx/gfxStructs.h" +#include "core/util/journal/process.h" +#include "gfx/gfxInit.h" + +using namespace UnitTesting; + +CreateUnitTest(TestWinMgrQueries, "WindowManager/BasicQueries") +{ + void run() + { + PlatformWindowManager *pwm = CreatePlatformWindowManager(); + + // Check out the primary desktop area... + RectI primary = pwm->getPrimaryDesktopArea(); + + Con::printf("Primary desktop area is (%d,%d) (%d,%d)", + primary.point.x, primary.point.y, primary.extent.x, primary.extent.y); + + test(primary.isValidRect(), "Got some sort of invalid rect from the window manager!"); + + // Now try to get info about all the monitors. + Vector monitorRects; + pwm->getMonitorRegions(monitorRects); + + test(monitorRects.size() > 0, "Should get at least one monitor rect back from getMonitorRegions!"); + + // This test is here just to detect overflow/runaway situations. -- BJG + test(monitorRects.size() < 64, "Either something's wrong, or you have a lot of monitors..."); + + for(S32 i=0; icreateWindow(NULL, vm); + + test(pw, "Didn't get a window back from the window manager, no good."); + if(!pw) + return; + + // Setup our events. + pw->mouseEvent.notify(this, &TestWinMgrCreate::handleMouseEvent); + pw->appEvent.notify(this, &TestWinMgrCreate::handleAppEvent); + + // And, go on our way. + while(Process::processEvents()) + ; + + SAFE_DELETE(pw); + } +}; + +CreateInteractiveTest(TestWinMgrGFXInit, "WindowManager/SimpleGFX") +{ + PlatformWindow *mWindow; + GFXDevice *mDevice; + + void handleDrawEvent(WindowId id) + { + mDevice->beginScene(); + mDevice->setActiveRenderTarget(mWindow->getGFXTarget()); + mDevice->clear( GFXClearZBuffer | GFXClearStencil | GFXClearTarget, ColorI( 255, 255, 0 ), 1.0f, 0 ); + mDevice->endScene(); + mWindow->getGFXTarget()->present(); + } + + void forceDraw() + { + handleDrawEvent(0); + } + + void handleAppEvent(WindowId, S32 event) + { + if(event == WindowClose) + Process::requestShutdown(); + } + + void run() + { + PlatformWindowManager *pwm = CreatePlatformWindowManager(); + + // Create a device. + GFXAdapter a; + a.mType = Direct3D9; + a.mIndex = 0; + + mDevice = GFXInit::createDevice(&a); + test(mDevice, "Unable to create d3d9 device #0."); + + // Initialize the window... + GFXVideoMode vm; + vm.resolution.x = 400; + vm.resolution.y = 400; + + mWindow = pwm->createWindow(mDevice, vm); + + test(mWindow, "Didn't get a window back from the window manager, no good."); + if(!mWindow) + return; + + // Setup our events. + mWindow->displayEvent.notify(this, &TestWinMgrGFXInit::handleDrawEvent); + mWindow->idleEvent.notify(this, &TestWinMgrGFXInit::forceDraw); + mWindow->appEvent.notify(this, &TestWinMgrGFXInit::handleAppEvent); + + // And, go on our way. + while(Process::processEvents()) + ; + + mWindow->displayEvent.remove(this, &TestWinMgrGFXInit::handleDrawEvent); + mWindow->idleEvent.remove(this, &TestWinMgrGFXInit::forceDraw); + mWindow->appEvent.remove(this, &TestWinMgrGFXInit::handleAppEvent); + + // Clean up! + SAFE_DELETE(mDevice); + SAFE_DELETE(mWindow); + } +}; + +CreateInteractiveTest(TestWinMgrGFXInitMultiWindow, "WindowManager/GFXMultiWindow") +{ + enum { + NumWindows = 4, + }; + + PlatformWindowManager *mWindowManager; + PlatformWindow *mWindows[NumWindows]; + GFXDevice *mDevice; + + void handleDrawEvent(WindowId id) + { + // Which window are we getting this event on? + PlatformWindow *w = mWindowManager->getWindowById(id); + + mDevice->beginScene(); + mDevice->setActiveRenderTarget(w->getGFXTarget()); + + // Vary clear color by window to discern which window is which. + mDevice->clear( GFXClearTarget, + ColorI( 255 - (id * 50), 255, id * 100 ), 1.0f, 0 ); + mDevice->endScene(); + + // Call swap on the window's render target. + ((GFXWindowTarget*)w->getGFXTarget())->present(); + } + + void handleAppEvent(WindowId, S32 event) + { + if(event == WindowClose) + Process::requestShutdown(); + } + + void handleIdleEvent() + { + for(S32 i=0; igetWindowId()); + } + + void run() + { + mWindowManager = CreatePlatformWindowManager(); + + // Create a device. + GFXAdapter a; + a.mType = Direct3D9; + a.mIndex = 0; + + mDevice = GFXInit::createDevice(&a); + test(mDevice, "Unable to create d3d9 device #0."); + + // Initialize the windows... + GFXVideoMode vm; + vm.resolution.x = 400; + vm.resolution.y = 400; + + for(S32 i=0; icreateWindow(mDevice, vm); + + test(mWindows[i], "Didn't get a window back from the window manager, no good."); + if(!mWindows[i]) + continue; + + // Setup our events. + mWindows[i]->displayEvent.notify(this, &TestWinMgrGFXInitMultiWindow::handleDrawEvent); + mWindows[i]->appEvent.notify(this, &TestWinMgrGFXInitMultiWindow::handleAppEvent); + mWindows[i]->idleEvent.notify(this, &TestWinMgrGFXInitMultiWindow::handleIdleEvent); + } + + // And, go on our way. + while(Process::processEvents()) + ; + + SAFE_DELETE(mWindowManager); + SAFE_DELETE(mDevice); + } +}; + +CreateInteractiveTest(TestJournaledMultiWindowGFX, "WindowManager/GFXJournaledMultiWindow") +{ + enum { + NumWindows = 2, + }; + + PlatformWindowManager *mWindowManager; + PlatformWindow *mWindows[NumWindows]; + GFXDevice *mDevice; + + S32 mNumDraws; + S32 mNumResize; + + void drawToWindow(PlatformWindow *win) + { + // Do some simple checks to make sure we draw the same number of times + // on both runs. + if(Journal::IsPlaying()) + mNumDraws--; + else + mNumDraws++; + + // Render! + mDevice->beginScene(); + mDevice->setActiveRenderTarget(win->getGFXTarget()); + + // Vary clear color by window to discern which window is which. + static S32 timeVariance = 0; + + mDevice->clear( GFXClearTarget, + ColorI( 0xFF - (++timeVariance * 5), 0xFF, win->getWindowId() * 0x0F ), 1.0f, 0 ); + + mDevice->endScene(); + + // Call swap on the window's render target. + win->getGFXTarget()->present(); + + } + + void handleDrawEvent(WindowId id) + { + // Which window are we getting this event on? + PlatformWindow *w = mWindowManager->getWindowById(id); + + drawToWindow(w); + } + + void handleAppEvent(WindowId, S32 event) + { + if(event == WindowClose) + Process::requestShutdown(); + } + + void handleIdleEvent() + { + for(S32 i=0; igetWindowById(id)->setSize(Point2I(width, height)); + + mNumResize--; + } + else + { + // If we're not playing back, do nothing except note it. + mNumResize++; + } + + // Which window are we getting this event on? + PlatformWindow *w = mWindowManager->getWindowById(id); + + drawToWindow(w); + } + + /// The mainloop of our app - we'll run this twice, once to create + /// a journal and again to play it back. + void mainLoop() + { + mWindowManager = CreatePlatformWindowManager(); + + // Create a device. + GFXAdapter a; + a.mType = Direct3D9; + a.mIndex = 0; + + mDevice = GFXInit::createDevice(&a); + test(mDevice, "Unable to create ogl device #0."); + + // Initialize the windows... + GFXVideoMode vm; + vm.resolution.x = 400; + vm.resolution.y = 400; + + for(S32 i=0; icreateWindow(mDevice, vm); + + test(mWindows[i], "Didn't get a window back from the window manager, no good."); + if(!mWindows[i]) + continue; + + // Setup our events. + mWindows[i]->displayEvent.notify(this, &TestJournaledMultiWindowGFX::handleDrawEvent); + mWindows[i]->appEvent.notify(this, &TestJournaledMultiWindowGFX::handleAppEvent); + mWindows[i]->resizeEvent.notify(this, &TestJournaledMultiWindowGFX::handleResizeEvent); + + // Only subscribe to the first idle event. + if(i==0) + mWindows[i]->idleEvent.notify(this, &TestJournaledMultiWindowGFX::handleIdleEvent); + } + + // And, go on our way. + while(Process::processEvents()) + ; + + // Finally, clean up. + for(S32 i=0; i 0, "No draws occurred!"); + test(mNumResize > 0, "No resizes occurred!"); + + // And play it back. + Journal::Play("multiwindow.jrn"); + mainLoop(); + Journal::Stop(); + + test(mNumDraws == 0, "Failed to play journal back with same number of draws."); + test(mNumResize == 0, "Failed to play journal back with same number of resizes."); +#endif + } +}; + +CreateInteractiveTest(GFXTestFullscreenToggle, "GFX/TestFullscreenToggle") +{ + enum Constants + { + NumWindows = 1, + }; + + PlatformWindowManager *mWindowManager; + PlatformWindow *mWindows[NumWindows]; + GFXDevice *mDevice; + + void drawToWindow(PlatformWindow *win) + { + // Render! + mDevice->beginScene(); + mDevice->setActiveRenderTarget(win->getGFXTarget()); + + // Vary clear color by window to discern which window is which. + static S32 timeVariance = 0; + + mDevice->clear( GFXClearZBuffer | GFXClearStencil | GFXClearTarget, + ColorI( 0xFF - (++timeVariance * 5), 0xFF, win->getWindowId() * 0x40 ), 1.0f, 0 ); + + mDevice->endScene(); + + // Call swap on the window's render target. + win->getGFXTarget()->present(); + } + + void handleDrawEvent(WindowId id) + { + // Which window are we getting this event on? + PlatformWindow *w = mWindowManager->getWindowById(id); + + drawToWindow(w); + } + + void handleAppEvent(WindowId, S32 event) + { + if(event == WindowClose) + Process::requestShutdown(); + } + + void handleIdleEvent() + { + // Redraw everything. + for(S32 i=0; igetWindowById(did); + GFXVideoMode winVm = win->getVideoMode(); + + // If the window is not full screen, make it full screen 800x600x32 + if(winVm.fullScreen == false) + { + winVm.fullScreen = true; + winVm.resolution.set(800,600); + } + else + { + // If the window is full screen, then bump it to 400x400x32 + winVm.fullScreen = false; + winVm.resolution.set(400,400); + } + + win->setVideoMode(winVm); + } + + void run() + { + mWindowManager = CreatePlatformWindowManager(); + + // Create a device. + GFXAdapter a; + a.mType = Direct3D9; + a.mIndex = 0; + + mDevice = GFXInit::createDevice(&a); + test(mDevice, "Unable to create d3d9 device #0."); + + // Initialize the windows... + GFXVideoMode vm; + vm.resolution.x = 400; + vm.resolution.y = 400; + + for(S32 i=0; icreateWindow(mDevice, vm); + + test(mWindows[i], "Didn't get a window back from the window manager, no good."); + if(!mWindows[i]) + continue; + + // Setup our events. + mWindows[i]->appEvent.notify(this, &GFXTestFullscreenToggle::handleAppEvent); + mWindows[i]->buttonEvent.notify(this, &GFXTestFullscreenToggle::handleButtonEvent); + mWindows[i]->displayEvent.notify(this, &GFXTestFullscreenToggle::handleDrawEvent); + + // Only subscribe to the first idle event. + if(i==0) + mWindows[i]->idleEvent.notify(this, &GFXTestFullscreenToggle::handleIdleEvent); + } + + // And, go on our way. + while(Process::processEvents()) + ; + + // Finally, clean up. + for(S32 i=0; ipreDestroy(); + SAFE_DELETE(mDevice); + SAFE_DELETE(mWindowManager); + } +}; \ No newline at end of file diff --git a/Engine/source/windowManager/win32/win32CursorController.cpp b/Engine/source/windowManager/win32/win32CursorController.cpp new file mode 100644 index 000000000..28a379682 --- /dev/null +++ b/Engine/source/windowManager/win32/win32CursorController.cpp @@ -0,0 +1,202 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include +#include +#include "core/strings/unicode.h" +#include "math/mMath.h" +#include "windowManager/win32/win32Window.h" +#include "windowManager/win32/win32WindowMgr.h" +#include "windowManager/win32/winDispatch.h" +#include "windowManager/win32/win32CursorController.h" +#include "platform/platformInput.h" +#include + +static struct { U32 id; LPTSTR resourceID; } sgCursorShapeMap[]= +{ + { PlatformCursorController::curArrow, IDC_ARROW }, + { PlatformCursorController::curWait, IDC_WAIT }, + { PlatformCursorController::curPlus, IDC_CROSS }, + { PlatformCursorController::curResizeVert, IDC_SIZEWE }, + { PlatformCursorController::curResizeHorz, IDC_SIZENS }, + { PlatformCursorController::curResizeAll, IDC_SIZEALL }, + { PlatformCursorController::curIBeam, IDC_IBEAM }, + { PlatformCursorController::curResizeNESW, IDC_SIZENESW }, + { PlatformCursorController::curResizeNWSE, IDC_SIZENWSE }, + { PlatformCursorController::curHand, IDC_HAND }, + { 0, 0 }, +}; + +//static const EnumTable::Enums curManagerShapesEnums[] = +//{ +// { Win32CursorController::curArrow, "Arrow" }, +// { Win32CursorController::curWait, "Wait" }, +// { Win32CursorController::curPlus, "Plus" }, +// { Win32CursorController::curResizeVert, "ResizeVert" }, +// { Win32CursorController::curResizeHorz, "ResizeHorz" }, +// { Win32CursorController::curResizeAll, "ResizeAll" }, +// { Win32CursorController::curIBeam, "ibeam" }, +// { Win32CursorController::curResizeNESW, "ResizeNESW" }, +// { Win32CursorController::curResizeNWSE, "ResizeNWSE" }, +//}; +// +//static const EnumTable gCurManagerShapesTable(8, &curManagerShapesEnums[0]); + +// CodeReview I've duplicated this 'cache' trick for system settings +// because they're unlikely to change and calling into the OS for values +// repeatedly is just silly to begin with. [6/29/2007 justind] +U32 Win32CursorController::getDoubleClickTime() +{ + static S32 sPlatWinDoubleClicktime = -1; + if( sPlatWinDoubleClicktime == -1 ) + sPlatWinDoubleClicktime = GetDoubleClickTime(); + return sPlatWinDoubleClicktime; +} +S32 Win32CursorController::getDoubleClickWidth() +{ + static S32 sPlatWinDoubleClickwidth = -1; + if( sPlatWinDoubleClickwidth == -1 ) + sPlatWinDoubleClickwidth = GetSystemMetrics(SM_CXDOUBLECLK); + return sPlatWinDoubleClickwidth; +} +S32 Win32CursorController::getDoubleClickHeight() +{ + static S32 sPlatWinDoubleClickheight = -1; + if( sPlatWinDoubleClickheight == -1 ) + sPlatWinDoubleClickheight = GetSystemMetrics(SM_CYDOUBLECLK); + return sPlatWinDoubleClickheight; +} + +void Win32CursorController::setCursorPosition( S32 x, S32 y ) +{ + ::SetCursorPos(x, y); +} + +void Win32CursorController::getCursorPosition( Point2I &point ) +{ + POINT rPoint; + ::GetCursorPos( &rPoint ); + + // Return + point.x = rPoint.x; + point.y = rPoint.y; +} + +void Win32CursorController::setCursorVisible( bool visible ) +{ + if( visible ) + ShowCursor( true ); + else + while( ShowCursor(false) > 0 ); +} + +bool Win32CursorController::isCursorVisible() +{ + CURSORINFO rCursorInfo; + rCursorInfo.cbSize = sizeof(CURSORINFO); + if( !GetCursorInfo( &rCursorInfo ) ) + { + //DWORD error = GetLastError(); + return false; + } + + // rCursorInfo.flags values : + // 0 == Cursor is hidden + // CURSOR_SHOWING == cursor is visible + return (bool)(rCursorInfo.flags == CURSOR_SHOWING); +} + +void Win32CursorController::setCursorShape(U32 cursorID) +{ + LPTSTR resourceID = NULL; + + for(S32 i = 0;sgCursorShapeMap[i].resourceID != NULL;++i) + { + if(cursorID == sgCursorShapeMap[i].id) + { + resourceID = sgCursorShapeMap[i].resourceID; + break; + } + } + + if(resourceID == NULL) + return; + + HCURSOR cur = LoadCursor(NULL, resourceID); + if(cur) + SetCursor(cur); +} + +static HCURSOR gCursorShape = NULL; +void Win32CursorController::setCursorShape( const UTF8 *fileName, bool reload ) +{ +#ifdef UNICODE + const UTF16 *lFileName = convertUTF8toUTF16( fileName ); +#else + const UTF8 *lFileName = fileName; +#endif + + if ( !gCursorShape || reload ) + gCursorShape = LoadCursorFromFile( lFileName ); + + if ( gCursorShape ) + SetCursor( gCursorShape ); +} + +// Console function to set the current cursor shape given the cursor shape +// name as defined in the enum above. +//ConsoleFunction( inputPushCursor, void, 2, 2, "inputPushCursor(cursor shape name)" ) +//{ +// S32 val = 0; +// +// // Find the cursor shape +// if(argc == 2) +// { +// for (S32 i = 0; i < gCurManagerShapesTable.size; i++) +// { +// if (! dStricmp(argv[1], gCurManagerShapesTable.table[i].label)) +// { +// val = gCurManagerShapesTable.table[i].index; +// break; +// } +// } +// } +// +// // Now set it +// Win32CursorController* cm = Input::getCursorManager(); +// if(cm) +// { +// cm->pushCursor(val); +// } +//} +//// Function to pop the current cursor shape +//ConsoleFunction( inputPopCursor, void, 1, 1, "inputPopCursor()" ) +//{ +// argc; +// argv; +// +// Win32CursorController* cm = Input::getCursorManager(); +// if(cm) +// { +// cm->popCursor(); +// } +//} diff --git a/Engine/source/windowManager/win32/win32CursorController.h b/Engine/source/windowManager/win32/win32CursorController.h new file mode 100644 index 000000000..d6e0ec23e --- /dev/null +++ b/Engine/source/windowManager/win32/win32CursorController.h @@ -0,0 +1,52 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _WIN32_CURSORCONTROLLER_H_ +#define _WIN32_CURSORCONTROLLER_H_ + +#include +#include "windowManager/platformCursorController.h" + +class Win32CursorController : public PlatformCursorController +{ +public: + Win32CursorController( PlatformWindow *owner ) : + PlatformCursorController( owner ) + { + pushCursor( PlatformCursorController::curArrow ); + }; + + virtual void setCursorPosition( S32 x, S32 y ); + virtual void getCursorPosition( Point2I &point ); + virtual void setCursorVisible( bool visible ); + virtual bool isCursorVisible(); + + void setCursorShape( U32 cursorID ); + void setCursorShape( const UTF8 *fileName, bool reload ); + + U32 getDoubleClickTime(); + S32 getDoubleClickWidth(); + S32 getDoubleClickHeight(); +}; + + +#endif diff --git a/Engine/source/windowManager/win32/win32SplashScreen.cpp b/Engine/source/windowManager/win32/win32SplashScreen.cpp new file mode 100644 index 000000000..a63f97b19 --- /dev/null +++ b/Engine/source/windowManager/win32/win32SplashScreen.cpp @@ -0,0 +1,142 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#define _WIN32_WINNT 0x0500 +#include + +#include "platform/platform.h" +#include "console/console.h" + +// from Torque.rc +#define IDI_ICON1 103 + +// Window Class name +static const TCHAR* c_szSplashClass = L"Torque3DSplashWindow"; + +static HWND gSplashWndOwner = NULL; +static HWND gSplashWnd = NULL; +static HBITMAP gSplashImage = NULL; + +// Registers a window class for the splash and splash owner windows. +static void RegisterWindowClass(HINSTANCE hinst) + +{ + WNDCLASS wc = { 0 }; + wc.lpfnWndProc = DefWindowProc; + wc.hInstance = hinst; + wc.hIcon = LoadIcon(hinst, MAKEINTRESOURCE(IDI_ICON1)); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.lpszClassName = c_szSplashClass; + RegisterClass(&wc); +} + +static void UnregisterSplashWindowClass(HINSTANCE hinst) +{ + WNDCLASSEX classInfo; + if (GetClassInfoEx(hinst,c_szSplashClass,&classInfo)) + UnregisterClass(c_szSplashClass,hinst); +} + +// Creates the splash owner window and the splash window. +static HWND CreateSplashWindow(HINSTANCE hinst) + +{ + RegisterWindowClass(hinst); + + gSplashWndOwner = CreateWindow(c_szSplashClass, NULL, WS_POPUP, + 0, 0, 0, 0, NULL, NULL, hinst, NULL); + + return CreateWindowEx(WS_EX_LAYERED, c_szSplashClass, NULL, WS_POPUP | WS_VISIBLE, + 0, 0, 0, 0, gSplashWndOwner, NULL, hinst, NULL); +} + +// Calls UpdateLayeredWindow to set a bitmap (with alpha) as the content of the splash window. +static void SetSplashImage(HWND hwndSplash, HBITMAP hbmpSplash) + +{ + // get the size of the bitmap + BITMAP bm; + GetObject(hbmpSplash, sizeof(bm), &bm); + SIZE sizeSplash = { bm.bmWidth, bm.bmHeight }; + + // get the primary monitor's info + POINT ptZero = { 0 }; + HMONITOR hmonPrimary = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY); + MONITORINFO monitorinfo = { 0 }; + monitorinfo.cbSize = sizeof(monitorinfo); + GetMonitorInfo(hmonPrimary, &monitorinfo); + + // center the splash screen in the middle of the primary work area + const RECT & rcWork = monitorinfo.rcWork; + POINT ptOrigin; + ptOrigin.x = rcWork.left + (rcWork.right - rcWork.left - sizeSplash.cx) / 2; + ptOrigin.y = rcWork.top + (rcWork.bottom - rcWork.top - sizeSplash.cy) / 2; + + // create a memory DC holding the splash bitmap + HDC hdcScreen = GetDC(NULL); + HDC hdcMem = CreateCompatibleDC(hdcScreen); + HBITMAP hbmpOld = (HBITMAP) SelectObject(hdcMem, hbmpSplash); + + // paint the window (in the right location) with the alpha-blended bitmap + UpdateLayeredWindow(hwndSplash, hdcScreen, &ptOrigin, &sizeSplash, + hdcMem, &ptZero, RGB(0, 0, 0), NULL, ULW_OPAQUE); + + // delete temporary objects + SelectObject(hdcMem, hbmpOld); + DeleteDC(hdcMem); + ReleaseDC(NULL, hdcScreen); +} + +void CloseSplashWindow(HINSTANCE hinst) +{ + if (gSplashWndOwner) + { + //ShowWindow(gSplashWnd, 0); + DestroyWindow(gSplashWndOwner); + UnregisterSplashWindowClass(hinst); + } + + gSplashWndOwner = NULL; + gSplashWnd = NULL; + +} + +bool Platform::displaySplashWindow() +{ + + gSplashImage = (HBITMAP) ::LoadImage(0, L"art\\gui\\splash.bmp", + IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); + + if (!gSplashImage) + return false; + + gSplashWnd = CreateSplashWindow(GetModuleHandle(NULL)); + + if (!gSplashWnd) + return false; + + SetSplashImage(gSplashWnd, gSplashImage); + + return true; +} + + diff --git a/Engine/source/windowManager/win32/win32Window.cpp b/Engine/source/windowManager/win32/win32Window.cpp new file mode 100644 index 000000000..8908d1916 --- /dev/null +++ b/Engine/source/windowManager/win32/win32Window.cpp @@ -0,0 +1,1162 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include +#include +#include +#include "math/mMath.h" +#include "gfx/gfxStructs.h" + +#include "windowManager/win32/win32Window.h" +#include "windowManager/win32/win32WindowMgr.h" +#include "windowManager/win32/win32CursorController.h" +#include "windowManager/win32/winDispatch.h" + +#include "platform/menus/popupMenu.h" +#include "platform/platformInput.h" + +// for winState structure +#include "platformWin32/platformWin32.h" + +#include +#include "gfx/gfxDevice.h" + +#include + +const UTF16* _MainWindowClassName = L"TorqueJuggernaughtWindow"; +const UTF16* _CurtainWindowClassName = L"TorqueJuggernaughtCurtainWindow"; + +#define SCREENSAVER_QUERY_DENY 0 // Disable screensaver + +#ifndef IDI_ICON1 +#define IDI_ICON1 107 +#endif + +static bool isScreenSaverRunning() +{ +#ifndef SPI_GETSCREENSAVERRUNNING +#define SPI_GETSCREENSAVERRUNNING 114 +#endif + // Windows 2K, and higher. It might be better to hook into + // the broadcast WM_SETTINGCHANGE message instead of polling for + // the screen saver status. + BOOL sreensaver = false; + SystemParametersInfo(SPI_GETSCREENSAVERRUNNING,0,&sreensaver,0); + return sreensaver; +} + +Win32Window::Win32Window(): mMouseLockPosition(0,0), +mShouldLockMouse(false), +mMouseLocked(false), +mOwningManager(NULL), +mNextWindow(NULL), +mWindowHandle(NULL), +mOldParent(NULL), +mTarget(NULL), +mDevice(NULL), +mAccelHandle(NULL), +mSuppressReset(false), +mMenuHandle(NULL), +mWindowedWindowStyle(0), +mPosition(0,0), +mFullscreen(false) +{ + mCursorController = new Win32CursorController( this ); + + mVideoMode.bitDepth = 32; + mVideoMode.fullScreen = false; + mVideoMode.refreshRate = 60; + mVideoMode.resolution.set(800,600); + + _registerWindowClass(); +} + +Win32Window::~Win32Window() +{ + if(mAccelHandle) + { + DestroyAcceleratorTable(mAccelHandle); + mAccelHandle = NULL; + } + + // delete our win handle.. + DestroyWindow(mWindowHandle); + + // unlink ourselves from the window list... + AssertFatal(mOwningManager, "Win32Window::~Win32Window - orphan window, cannot unlink!"); + mOwningManager->unlinkWindow(this); + + _unregisterWindowClass(); +} + +GFXDevice * Win32Window::getGFXDevice() +{ + return mDevice; +} + +GFXWindowTarget * Win32Window::getGFXTarget() +{ + return mTarget; +} + +const GFXVideoMode & Win32Window::getVideoMode() +{ + return mVideoMode; +} + +void Win32Window::setVideoMode( const GFXVideoMode &mode ) +{ + bool needCurtain = (mVideoMode.fullScreen != mode.fullScreen); + + if(needCurtain) + { + Con::errorf("Win32Window::setVideoMode - invoking curtain"); + mOwningManager->lowerCurtain(); + } + + mVideoMode = mode; + mSuppressReset = true; + + // Can't switch to fullscreen while a child of another window + if(mode.fullScreen && !Platform::getWebDeployment() && mOwningManager->getParentWindow()) + { + mOldParent = (HWND)mOwningManager->getParentWindow(); + mOwningManager->setParentWindow(NULL); + } + else if(!mode.fullScreen && mOldParent) + { + mOwningManager->setParentWindow(mOldParent); + mOldParent = NULL; + } + + // Set our window to have the right style based on the mode + if(mode.fullScreen && !Platform::getWebDeployment() && !mOffscreenRender) + { + SetWindowLong( getHWND(), GWL_STYLE, WS_POPUP); + SetWindowPos( getHWND(), HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); + ShowWindow(getHWND(), SW_SHOWNORMAL); + + // Clear the menu bar from the window for full screen + HMENU menu = GetMenu(getHWND()); + if(menu) + { + SetMenu(getHWND(), NULL); + } + + // When switching to Fullscreen, reset device after setting style + if(mTarget.isValid()) + mTarget->resetMode(); + + mFullscreen = true; + } + else + { + // Reset device *first*, so that when we call setSize() and let it + // access the monitor settings, it won't end up with our fullscreen + // geometry that is just about to change. + + if(mTarget.isValid()) + mTarget->resetMode(); + + if (!mOffscreenRender) + { + SetWindowLong( getHWND(), GWL_STYLE, mWindowedWindowStyle); + SetWindowPos( getHWND(), HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); + + // Put back the menu bar, if any + if(mMenuHandle) + { + SetMenu(getHWND(), mMenuHandle); + } + } + + // Make sure we're the correct resolution for web deployment + if (!Platform::getWebDeployment() || !mOwningManager->getParentWindow() || mOffscreenRender) + { + setSize(mode.resolution); + } + else + { + HWND parentWin = (HWND)mOwningManager->getParentWindow(); + RECT windowRect; + GetClientRect(parentWin, &windowRect); + Point2I res(windowRect.right-windowRect.left, windowRect.bottom-windowRect.top); + if (res.x == 0 || res.y == 0) + { + // Must be too early in the window set up to obtain the parent's size. + setSize(mode.resolution); + } + else + { + setSize(res); + } + } + + if (!mOffscreenRender) + { + // We have to force Win32 to update the window frame and make the window + // visible and no longer topmost - this code might be possible to simplify. + SetWindowPos( getHWND(), HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); + ShowWindow( getHWND(), SW_SHOWNORMAL); + } + + mFullscreen = false; + } + + mSuppressReset = false; + + if(needCurtain) + mOwningManager->raiseCurtain(); + + SetForegroundWindow(getHWND()); +} + +bool Win32Window::clearFullscreen() +{ + return true; +} + +bool Win32Window::isFullscreen() +{ + return mFullscreen; +} + +void Win32Window::_setFullscreen(const bool fullscreen) +{ + if (fullscreen == mFullscreen) + return; + + mFullscreen = fullscreen; + if(fullscreen && !mOffscreenRender) + { + Con::printf("Win32Window::setFullscreen (full) enter"); + SetWindowLong( getHWND(), GWL_STYLE, WS_POPUP|WS_SYSMENU ); + SetWindowPos( getHWND(), HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); + } + else + { + Con::printf("Win32Window::setFullscreen (windowed) enter"); + if (!mOffscreenRender) + { + SetWindowLong( getHWND(), GWL_STYLE, mWindowedWindowStyle); + SetWindowPos( getHWND(), HWND_NOTOPMOST, 0, 0, mVideoMode.resolution.x, mVideoMode.resolution.y, SWP_FRAMECHANGED | SWP_SHOWWINDOW); + } + + setSize(mVideoMode.resolution); + + } + Con::printf("Win32Window::setFullscreen exit"); +} + +bool Win32Window::setCaption( const char *cap ) +{ + return SetWindowTextA(mWindowHandle, cap); +} + +const char * Win32Window::getCaption() +{ + char buff[512]; + S32 strLen = GetWindowTextA(mWindowHandle, buff, 512); + + if(strLen==0) + return NULL; + + return StringTable->insert(buff); +} + +void Win32Window::setFocus() +{ + ::SetFocus( mWindowHandle ); +} + +void Win32Window::setClientExtent( const Point2I newExtent ) +{ + Point2I oldExtent = getClientExtent(); + if (oldExtent == newExtent) + return; + + RECT rtClient; + DWORD Style, ExStyle; + SetRect( &rtClient, 0, 0, newExtent.x, newExtent.y ); + Style = GetWindowLong( mWindowHandle, GWL_STYLE); + ExStyle = GetWindowLong( mWindowHandle, GWL_EXSTYLE ); + + AdjustWindowRectEx( &rtClient, Style, getMenuHandle() != NULL, ExStyle ); + if( Style & WS_VSCROLL ) + rtClient.right += GetSystemMetrics( SM_CXVSCROLL ); + if( Style & WS_HSCROLL ) + rtClient.bottom += GetSystemMetrics( SM_CYVSCROLL ); + + SetWindowPos( mWindowHandle, NULL, 0, 0, rtClient.right - rtClient.left, rtClient.bottom - rtClient.top, SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER); +} + +const Point2I Win32Window::getClientExtent() +{ + // Fetch Client Rect from Windows + RECT clientRect; + ::GetClientRect(mWindowHandle, &clientRect); + + // Return as a Torque Point2I - We don't care about origin as it's always 0,0 + return Point2I(clientRect.right - clientRect.left, clientRect.bottom - clientRect.top); +} + +void Win32Window::setBounds( const RectI &newBounds ) +{ + RECT newRect; + newRect.left = newBounds.point.x; + newRect.top = newBounds.point.y; + newRect.bottom = newRect.top + newBounds.extent.y; + newRect.right = newRect.left + newBounds.extent.x; + + MoveWindow(mWindowHandle, newRect.left, newRect.top, newRect.right - newRect.left, newRect.bottom - newRect.top, true); +} + +const RectI Win32Window::getBounds() const +{ + // Fetch Window Rect from OS + RECT windowRect; + ::GetWindowRect(mWindowHandle, &windowRect); + + // Return as a Torque RectI + return RectI(windowRect.left,windowRect.top,windowRect.right - windowRect.left, windowRect.bottom - windowRect.top); +} + +void Win32Window::setPosition( const Point2I newPosition ) +{ + SetWindowPos( mWindowHandle, HWND_NOTOPMOST, newPosition.x, newPosition.y, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE ); +} + +const Point2I Win32Window::getPosition() +{ + RECT windowRect; + GetWindowRect( mWindowHandle, &windowRect ); + + // Return position + return Point2I(windowRect.left,windowRect.top); +} + +Point2I Win32Window::clientToScreen( const Point2I& pos ) +{ + POINT p = { pos.x, pos.y }; + + ClientToScreen( mWindowHandle, &p ); + return Point2I( p.x, p.y ); +} + +Point2I Win32Window::screenToClient( const Point2I& pos ) +{ + POINT p = { pos.x, pos.y }; + + ScreenToClient( mWindowHandle, &p ); + return Point2I( p.x, p.y ); +} + +void Win32Window::centerWindow() +{ + RECT newRect; + GetWindowRect(mWindowHandle,&newRect); + newRect.bottom -= newRect.top; + newRect.right -= newRect.left; + newRect.top = 0; + newRect.left = 0; + + HMONITOR hMon = MonitorFromWindow(mWindowHandle, MONITOR_DEFAULTTONEAREST); + + // Get the monitor's extents. + MONITORINFO monInfo; + dMemset(&monInfo, 0, sizeof MONITORINFO); + monInfo.cbSize = sizeof MONITORINFO; + GetMonitorInfo(hMon, &monInfo); + + // Calculate the offset to center the window in the working area + S32 deltaX = ((monInfo.rcWork.right - monInfo.rcWork.left) / 2) - ((newRect.right - newRect.left) / 2); + S32 deltaY = ((monInfo.rcWork.bottom - monInfo.rcWork.top) / 2) - ((newRect.bottom - newRect.top) / 2); + + // Calculate the new left and top position for the window + S32 newLeft = newRect.left + deltaX; + S32 newTop = newRect.top + deltaY; + + // Clamp these to be greater than 0 so that the top left corner is never offscreen + newLeft = mClamp(newLeft, 0, newLeft); + newTop = mClamp(newLeft, 0, newTop); + + // Calculate the new width and height + S32 newWidth = newRect.right - newRect.left; + S32 newHeight = newRect.bottom - newRect.top; + + // If the new width and height of the window is larger + // than the working area of the monitor but is smaller + // than the monitor size then have it max out at the + // working area so that it will remain uncovered. We + // leave it alone if it is bigger than the monitor size + // (with a small fudge) to support multiple monitors. + if (newLeft + newWidth > (monInfo.rcWork.right - monInfo.rcWork.left) && + newLeft + newWidth <= (monInfo.rcMonitor.right - monInfo.rcMonitor.left) + 4) + newWidth = (monInfo.rcWork.right - monInfo.rcWork.left) - newLeft; + if (newTop + newHeight > (monInfo.rcWork.bottom - monInfo.rcWork.top) && + newTop + newHeight <= (monInfo.rcMonitor.bottom - monInfo.rcMonitor.top) + 4) + newHeight = (monInfo.rcWork.bottom - monInfo.rcWork.top) - newTop; + + MoveWindow( mWindowHandle, newLeft, newTop, newWidth, newHeight, true ); + + // Make sure the resolution matches the client extent + Point2I clientExt = getClientExtent(); + mVideoMode.resolution.set( clientExt.x, clientExt.y ); + + // Let GFX get an update about the new resolution + if (mTarget.isValid()) + mTarget->resetMode(); +} + +bool Win32Window::setSize( const Point2I &newSize ) +{ + // Create the window rect (screen centered if not owned by a parent) + RECT newRect; + newRect.left = 0; + newRect.top = 0; + newRect.bottom = newRect.top + newSize.y; + newRect.right = newRect.left + newSize.x; + + // Adjust the window rect to ensure the client rectangle is the desired resolution + AdjustWindowRect( &newRect, mWindowedWindowStyle, false);//(bool)(getMenuHandle() != NULL) ); + + // Center the window on the screen if we're not a child + if( !mOwningManager->mParentWindow ) + { + HMONITOR hMon = MonitorFromWindow(mWindowHandle, MONITOR_DEFAULTTONEAREST); + + // Get the monitor's extents. + MONITORINFO monInfo; + dMemset(&monInfo, 0, sizeof MONITORINFO); + monInfo.cbSize = sizeof MONITORINFO; + GetMonitorInfo(hMon, &monInfo); + + // Calculate the offset to center the window in the working area + S32 deltaX = ((monInfo.rcWork.right - monInfo.rcWork.left) / 2) - ((newRect.right - newRect.left) / 2); + S32 deltaY = ((monInfo.rcWork.bottom - monInfo.rcWork.top) / 2) - ((newRect.bottom - newRect.top) / 2); + + // Calculate the new left and top position for the window + S32 newLeft = newRect.left + deltaX; + S32 newTop = newRect.top + deltaY; + + // Clamp these to be greater than 0 so that the top left corner is never offscreen + newLeft = mClamp(newLeft, 0, newLeft); + newTop = mClamp(newLeft, 0, newTop); + + // Calculate the new width and height + S32 newWidth = newRect.right - newRect.left; + S32 newHeight = newRect.bottom - newRect.top; + + // If the new width and height of the window is larger + // than the working area of the monitor but is smaller + // than the monitor size then have it max out at the + // working area so that it will remain uncovered. We + // leave it alone if it is bigger than the monitor size + // (with a small fudge) to support multiple monitors. + if (newLeft + newWidth > (monInfo.rcWork.right - monInfo.rcWork.left) && + newLeft + newWidth <= (monInfo.rcMonitor.right - monInfo.rcMonitor.left) + 4) + newWidth = (monInfo.rcWork.right - monInfo.rcWork.left) - newLeft; + if (newTop + newHeight > (monInfo.rcWork.bottom - monInfo.rcWork.top) && + newTop + newHeight <= (monInfo.rcMonitor.bottom - monInfo.rcMonitor.top) + 4) + newHeight = (monInfo.rcWork.bottom - monInfo.rcWork.top) - newTop; + + MoveWindow( mWindowHandle, newLeft, newTop, newWidth, newHeight, true ); + } + else // Just position it according to the mPosition plus new extent + MoveWindow(mWindowHandle, newRect.left, newRect.top, newRect.right - newRect.left, newRect.bottom - newRect.top, true); + + // Make sure the resolution matches the client extent + Point2I clientExt = getClientExtent(); + mVideoMode.resolution.set( clientExt.x, clientExt.y ); + + // Let GFX get an update about the new resolution + if (mTarget.isValid()) + mTarget->resetMode(); + + InvalidateRect( NULL, NULL, true ); + + return true; +} + +bool Win32Window::isOpen() +{ + return true; +} + +bool Win32Window::isVisible() +{ + // Is the window open and visible, ie. not minimized? + + if(!mWindowHandle) + return false; + + if (mOffscreenRender) + return true; + + return IsWindowVisible(mWindowHandle) + && !IsIconic(mWindowHandle) + && !isScreenSaverRunning(); +} + +bool Win32Window::isFocused() +{ + + if (mOffscreenRender) + return true; + + // CodeReview This is enough to make the plugin and normal/editor scenarios + // coexist but it seems brittle. I think we need a better way to detect + // if we're the foreground window, maybe taking into account if any of our + // window's parents are foreground? [bjg 4/30/07] + if(mOwningManager->mParentWindow) + return (GetFocus() == mWindowHandle || IsChild(mWindowHandle, GetFocus())); + else + return ((GetFocus() == mWindowHandle || IsChild(mWindowHandle, GetFocus())) && GetForegroundWindow() == mWindowHandle); +} + +bool Win32Window::isMinimized() +{ + if (mOffscreenRender) + return false; + + WINDOWPLACEMENT wd; + if ( GetWindowPlacement( mWindowHandle, &wd ) ) + { + return ( wd.showCmd == SW_SHOWMINIMIZED ); + } + + return false; +} + +bool Win32Window::isMaximized() +{ + if (mOffscreenRender) + return true; + + WINDOWPLACEMENT wd; + if ( GetWindowPlacement( mWindowHandle, &wd ) ) + { + return ( wd.showCmd == SW_SHOWMAXIMIZED ); + } + + return false; +} + +WindowId Win32Window::getWindowId() +{ + return mWindowId; +} + +void Win32Window::minimize() +{ + if (mOffscreenRender) + return; + + ShowWindow( mWindowHandle, SW_MINIMIZE ); +} + +void Win32Window::maximize() +{ + if (mOffscreenRender) + return; + + ShowWindow( mWindowHandle, SW_MAXIMIZE ); +} + +void Win32Window::restore() +{ + if (mOffscreenRender) + return; + + ShowWindow( mWindowHandle, SW_RESTORE ); +} + +void Win32Window::hide() +{ + if (mOffscreenRender) + return; + + ShowWindow( mWindowHandle, SW_HIDE ); +} + +void Win32Window::show() +{ + if (mOffscreenRender) + return; + + ShowWindow( mWindowHandle, SW_SHOWNORMAL ); +} + +void Win32Window::close() +{ + delete this; +} + +void Win32Window::_registerWindowClass() +{ + // Check to see if it exists already. + WNDCLASSEX classInfo; + if (GetClassInfoEx(GetModuleHandle(NULL),_MainWindowClassName,&classInfo)) + return; + + HMODULE appInstance = GetModuleHandle(NULL); + HICON appIcon = LoadIcon(appInstance, MAKEINTRESOURCE(IDI_ICON1)); + + // Window class shared by all MainWindow objects + classInfo.lpszClassName = _MainWindowClassName; + classInfo.cbSize = sizeof(WNDCLASSEX); + classInfo.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + classInfo.lpfnWndProc = (WNDPROC)WindowProc; + classInfo.hInstance = appInstance; // Owner of this class + classInfo.hIcon = appIcon; // Icon name + classInfo.hIconSm = appIcon; // Icon name + classInfo.hCursor = LoadCursor(NULL, IDC_ARROW); // Cursor + classInfo.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); // Default color + classInfo.lpszMenuName = NULL; + classInfo.cbClsExtra = 0; + classInfo.cbWndExtra = 0; + if (!RegisterClassEx(&classInfo)) + AssertISV(false,"Window class initialization failed"); + + classInfo.lpfnWndProc = DefWindowProc; + classInfo.hCursor = NULL; + classInfo.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH); + classInfo.lpszClassName = _CurtainWindowClassName; + if (!RegisterClassEx(&classInfo)) + AssertISV(false,"Curtain window class initialization failed"); +} + +void Win32Window::_unregisterWindowClass() +{ + WNDCLASSEX classInfo; + if (GetClassInfoEx(GetModuleHandle(NULL),_MainWindowClassName,&classInfo)) + UnregisterClass(_MainWindowClassName,GetModuleHandle(NULL)); + if (GetClassInfoEx(GetModuleHandle(NULL),_CurtainWindowClassName,&classInfo)) + UnregisterClass(_CurtainWindowClassName,GetModuleHandle(NULL)); +} + +LRESULT PASCAL Win32Window::WindowProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) +{ + // CodeReview [tom, 4/30/2007] The two casts here seem somewhat silly and redundant ? + Win32Window* window = (Win32Window*)((PlatformWindow*)GetWindowLong(hWnd, GWL_USERDATA)); + const WindowId devId = window ? window->getWindowId() : 0; + + if (window && window->getOffscreenRender()) + return DefWindowProc(hWnd, message, wParam, lParam); + + switch (message) + { + + case WM_DISPLAYCHANGE: + if(window && window->isVisible() && !window->mSuppressReset && window->getVideoMode().bitDepth != wParam) + { + Con::warnf("Win32Window::WindowProc - resetting device due to display mode BPP change."); + window->getGFXTarget()->resetMode(); + } + break; + + case WM_MOUSEACTIVATE: + SetFocus(hWnd); + return MA_ACTIVATE; + + case WM_MOUSEMOVE: + if (window && GetFocus() != hWnd && IsChild(hWnd, GetFocus())) + { + SetFocus(hWnd); + break; + } + + // If our foreground window is the browser and we don't have focus grab it + if (Platform::getWebDeployment() && GetFocus() != hWnd) + { + HWND phwnd = GetParent(hWnd); + while (phwnd) + { + if (GetForegroundWindow() == phwnd) + { + SetFocus(hWnd); + break; + } + phwnd = GetParent(phwnd); + } + } + break; + + // Associate the window pointer with this window + case WM_CREATE: + // CodeReview [tom, 4/30/2007] Why don't we just cast this to a LONG + // instead of having a ton of essentially pointless casts ? + SetWindowLong(hWnd, GWL_USERDATA, + (LONG)((PlatformWindow*)((CREATESTRUCT*)lParam)->lpCreateParams)); + break; + + case WM_SETFOCUS: + // NOTE: if wParam is NOT equal to our window handle then we are GAINING focus + Dispatch(DelayedDispatch, hWnd, message, wParam, lParam); + return 0; + + case WM_KILLFOCUS: + // NOTE: if wParam is NOT equal to our window handle then we are LOSING focus + Dispatch(DelayedDispatch, hWnd, message, wParam, lParam); + return 0; + + // The window is being dragged + case WM_MOVE: + if(!window) + break; + + window->mPosition.x = (int)LOWORD(lParam); + window->mPosition.y = (int)HIWORD(lParam); + return 0; + + // Update viewport when the window moves + case WM_SIZE: + if(window && window->mSuppressReset) + break; + + // This is dispatched immediately to prevent a race condition with journaling and window minimizing + if (wParam != SIZE_MINIMIZED && !Journal::IsPlaying()) + Dispatch( ImmediateDispatch, hWnd,message,wParam,lParam ); + + if(wParam != SIZE_MINIMIZED && window != NULL ) + { + if(!window->mVideoMode.fullScreen) + { + U32 width = LOWORD( lParam ); + U32 height = HIWORD( lParam ); + + window->mVideoMode.resolution.set( width, height ); + } + + if(window->getGFXTarget()) + { + Con::warnf("Win32Window::WindowProc - resetting device due to window size change."); + window->getGFXTarget()->resetMode(); + } + } + return 0; + + // Limit resize to a safe minimum + case WM_GETMINMAXINFO: + MINMAXINFO *winfo; + winfo = (MINMAXINFO*)(lParam); + + if(window && window->mMinimumSize.lenSquared() > 0) + { + winfo->ptMinTrackSize.x = window->mMinimumSize.x; + winfo->ptMinTrackSize.y = window->mMinimumSize.y; + } + + //Is the window size locked? + if (window && window->isSizeLocked()) + { + Point2I lockedSize = window->getLockedSize(); + + winfo->ptMinTrackSize.x = lockedSize.x; + winfo->ptMinTrackSize.y = lockedSize.y; + winfo->ptMaxTrackSize.x = lockedSize.x; + winfo->ptMaxTrackSize.y = lockedSize.y; + } + + break; + + // Override background erase so window doesn't get cleared + case WM_ERASEBKGND: + return 1; + + case WM_MENUSELECT: + winState.renderThreadBlocked = true; + break; + + // Refresh the window + case WM_PAINT: + // Use validate instead of begin/end paint, which seem to installs + // some Dx clipping state that isn't getting restored properly + ValidateRect(hWnd,0); + + // Skip it if we're dispatching. + if(Journal::IsDispatching()) + break; + + if( window == NULL ) + break; + + //// Default render if.. + //// 1. We have no device + //// 2. We have a device but it's not allowing rendering + if( !window->getGFXDevice() || !window->getGFXDevice()->allowRender() ) + window->defaultRender(); + if( winState.renderThreadBlocked ) + window->displayEvent.trigger(devId); + break; + + // Power shutdown query + case WM_POWERBROADCAST: { + if (wParam == PBT_APMQUERYSUSPEND) + if (GetForegroundWindow() == hWnd) + return BROADCAST_QUERY_DENY; + break; + } + + // Screensaver activation and monitor power requests + case WM_SYSCOMMAND: + switch (wParam) { + case SC_SCREENSAVE: + case SC_MONITORPOWER: + if (GetForegroundWindow() == hWnd) + return SCREENSAVER_QUERY_DENY; + break; + } + break; + + // Menus + case WM_COMMAND: + { + winState.renderThreadBlocked = false; + + if( window == NULL ) + break; + + // [tom, 8/21/2006] Pass off to the relevant PopupMenu if it's a menu + // or accelerator command. PopupMenu will in turn hand off to script. + // + // Note: PopupMenu::handleSelect() will not do anything if the menu + // item is disabled, so we don't need to deal with that here. + + S32 numItems = GetMenuItemCount(window->getMenuHandle()); + for(S32 i = 0;i < numItems;i++) + { + MENUITEMINFOA mi; + mi.cbSize = sizeof(mi); + mi.fMask = MIIM_DATA; + if(GetMenuItemInfoA(window->getMenuHandle(), i, TRUE, &mi)) + { + if(mi.fMask & MIIM_DATA && mi.dwItemData != 0) + { + PopupMenu *mnu = (PopupMenu *)mi.dwItemData; + + PopupMenu::smSelectionEventHandled = false; + PopupMenu::smPopupMenuEvent.trigger(mnu->getPopupGUID(), LOWORD(wParam)); + if (PopupMenu::smSelectionEventHandled) + return 0; + } + } + } + } + break; + + case WM_INITMENUPOPUP: + { + HMENU menu = (HMENU)wParam; + MENUINFO mi; + mi.cbSize = sizeof(mi); + mi.fMask = MIM_MENUDATA; + if(GetMenuInfo(menu, &mi) && mi.dwMenuData != 0) + { + PopupMenu *pm = (PopupMenu *)mi.dwMenuData; + if(pm != NULL) + pm->onMenuSelect(); + } + } + break; + // Some events need to be consumed as well as queued up + // for later dispatch. + case WM_CLOSE: + case WM_MOUSEWHEEL: +#ifdef WM_MOUSEHWHEEL // Vista + case WM_MOUSEHWHEEL: +#endif + + // CodeReview This fixes some issues with inappropriate event handling + // around device resets and in full-screen mode but feels + // heavy-handed. Is it clobbering something important? + // [bjg 6/13/07] + case WM_KEYUP: + case WM_KEYDOWN: + case WM_SYSKEYUP: + case WM_SYSKEYDOWN: + Dispatch(DelayedDispatch,hWnd,message,wParam,lParam); + return 0; + } + + // Queue up for later and invoke the Windows default handler. + Dispatch(DelayedDispatch,hWnd,message,wParam,lParam); + return DefWindowProc(hWnd, message, wParam, lParam); +} + + +void Win32Window::defaultRender() +{ + // Get Window Device Context + HDC logoDC = GetDC(mWindowHandle); + + // Get Window Rectangle + RECT lRect; + GetClientRect(mWindowHandle,&lRect); + + // Fill with AppWorkspace color + FillRect( logoDC, &lRect, (HBRUSH)GetSysColorBrush(COLOR_APPWORKSPACE) ); + + // Release Device Context + ReleaseDC(mWindowHandle,logoDC); +} + +//----------------------------------------------------------------------------- +// Accelerators +//----------------------------------------------------------------------------- + +void Win32Window::addAccelerator(Accelerator &accel) +{ + ACCEL winAccel; + winAccel.fVirt = FVIRTKEY; + winAccel.cmd = accel.mID; + + if(accel.mDescriptor.flags & SI_SHIFT) + winAccel.fVirt |= FSHIFT; + if(accel.mDescriptor.flags & SI_CTRL) + winAccel.fVirt |= FCONTROL; + if(accel.mDescriptor.flags & SI_ALT) + winAccel.fVirt |= FALT; + + winAccel.key = TranslateKeyCodeToOS(accel.mDescriptor.eventCode); + + for(WinAccelList::iterator i = mWinAccelList.begin();i != mWinAccelList.end();++i) + { + if(i->cmd == winAccel.cmd) + { + // Already in list, just update it + i->fVirt = winAccel.fVirt; + i->key = winAccel.key; + return; + } + + if(i->fVirt == winAccel.fVirt && i->key == winAccel.key) + { + // Existing accelerator in list, don't add this one + return; + } + } + + mWinAccelList.push_back(winAccel); +} + +void Win32Window::removeAccelerator(Accelerator &accel) +{ + for(WinAccelList::iterator i = mWinAccelList.begin();i != mWinAccelList.end();++i) + { + if(i->cmd == accel.mID) + { + mWinAccelList.erase(i); + return; + } + } +} + +//----------------------------------------------------------------------------- + +static bool isMenuItemIDEnabled(HMENU menu, U32 id) +{ + S32 numItems = GetMenuItemCount(menu); + for(S32 i = 0;i < numItems;i++) + { + MENUITEMINFOA mi; + mi.cbSize = sizeof(mi); + mi.fMask = MIIM_ID|MIIM_STATE|MIIM_SUBMENU|MIIM_DATA; + if(GetMenuItemInfoA(menu, i, TRUE, &mi)) + { + if(mi.fMask & MIIM_ID && mi.wID == id) + { + // This is an item on this menu + return (mi.fMask & MIIM_STATE) && ! (mi.fState & MFS_DISABLED); + } + + if((mi.fMask & MIIM_SUBMENU) && mi.hSubMenu != 0 && (mi.fMask & MIIM_DATA) && mi.dwItemData != 0) + { + // This is a submenu, if it can handle this ID then recurse to find correct state + PopupMenu *mnu = (PopupMenu *)mi.dwItemData; + if(mnu->canHandleID(id)) + return isMenuItemIDEnabled(mi.hSubMenu, id); + } + } + } + + return false; +} + +bool Win32Window::isAccelerator(const InputEventInfo &info) +{ + U32 virt; + virt = FVIRTKEY; + if(info.modifier & SI_SHIFT) + virt |= FSHIFT; + if(info.modifier & SI_CTRL) + virt |= FCONTROL; + if(info.modifier & SI_ALT) + virt |= FALT; + + U8 keyCode = TranslateKeyCodeToOS(info.objInst); + + for(S32 i = 0;i < mWinAccelList.size();++i) + { + const ACCEL &accel = mWinAccelList[i]; + if(accel.key == keyCode && accel.fVirt == virt && isMenuItemIDEnabled(getMenuHandle(), accel.cmd)) + return true; + } + return false; +} + +//----------------------------------------------------------------------------- + +void Win32Window::addAccelerators(AcceleratorList &list) +{ + if(mAccelHandle) + { + DestroyAcceleratorTable(mAccelHandle); + mAccelHandle = NULL; + } + + for(AcceleratorList::iterator i = list.begin();i != list.end();++i) + { + addAccelerator(*i); + } + + if(mWinAccelList.size() > 0) + mAccelHandle = CreateAcceleratorTable(&mWinAccelList[0], mWinAccelList.size()); +} + +void Win32Window::removeAccelerators(AcceleratorList &list) +{ + if(mAccelHandle) + { + DestroyAcceleratorTable(mAccelHandle); + mAccelHandle = NULL; + } + + for(AcceleratorList::iterator i = list.begin();i != list.end();++i) + { + removeAccelerator(*i); + } + + if(mWinAccelList.size() > 0) + mAccelHandle = CreateAcceleratorTable(mWinAccelList.address(), mWinAccelList.size()); +} + +bool Win32Window::translateMessage(MSG &msg) +{ + if(mAccelHandle == NULL || mWindowHandle == NULL || !mEnableAccelerators) + return false; + + int ret = TranslateAccelerator(mWindowHandle, mAccelHandle, &msg); + return ret != 0; +} + +//----------------------------------------------------------------------------- +// Mouse Locking +//----------------------------------------------------------------------------- + +void Win32Window::setMouseLocked( bool enable ) +{ + + if (mOffscreenRender) + return; + + // Maintain a good state without unnecessary + // cursor hides/modifications + if( enable && mMouseLocked && mShouldLockMouse ) + return; + else if(!enable && !mMouseLocked && !mShouldLockMouse ) + return; + + // Need to be focused to enable mouse lock + // but we can disable it no problem if we're + // not focused + if( !isFocused() && enable ) + { + mShouldLockMouse = enable; + return; + } + + // Set Flag + mMouseLocked = enable; + + if( enable ) + { + getCursorPosition( mMouseLockPosition ); + + RECT r; + GetWindowRect(getHWND(), &r); + + // Hide the cursor before it's moved + setCursorVisible( false ); + + // We have to nudge the cursor clip rect in a bit so we don't go out + // side the bounds of the window... We'll just do it by 32 in all + // directions, which will break for very small windows (< 200x200 or so) + // but otherwise won't matter. + RECT rCopy = r; + rCopy.top += 32; rCopy.bottom -= 64; + rCopy.left += 32; rCopy.right -= 64; + ClipCursor(&rCopy); + + S32 centerX = (r.right + r.left) >> 1; + S32 centerY = ((r.bottom + r.top) >> 1); + + + // Consume all existing mouse events and those posted to our own dispatch queue + MSG msg; + PeekMessage( &msg, 0,WM_MOUSEFIRST,WM_MOUSELAST , PM_QS_POSTMESSAGE | PM_NOYIELD | PM_REMOVE ); + RemoveMessages( NULL, WM_MOUSEMOVE, WM_MOUSEMOVE ); + + // Set the CursorPos + SetCursorPos(centerX, centerY); + + // reset should lock flag + mShouldLockMouse = true; + } + else + { + // This belongs before the unlock code + mShouldLockMouse = false; + + ClipCursor(NULL); + setCursorPosition( mMouseLockPosition.x,mMouseLockPosition.y ); + + // Consume all existing mouse events and those posted to our own dispatch queue + MSG msg; + PeekMessage( &msg, NULL,WM_MOUSEFIRST,WM_MOUSELAST , PM_QS_POSTMESSAGE | PM_NOYIELD | PM_REMOVE ); + RemoveMessages( NULL, WM_MOUSEMOVE, WM_MOUSEMOVE ); + + // Show the Cursor + setCursorVisible( true ); + + } +} + +const UTF16 *Win32Window::getWindowClassName() +{ + return _MainWindowClassName; +} + +const UTF16 *Win32Window::getCurtainWindowClassName() +{ + return _CurtainWindowClassName; +} \ No newline at end of file diff --git a/Engine/source/windowManager/win32/win32Window.h b/Engine/source/windowManager/win32/win32Window.h new file mode 100644 index 000000000..c205098ba --- /dev/null +++ b/Engine/source/windowManager/win32/win32Window.h @@ -0,0 +1,231 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _WINDOWMANAGER_WIN32_WIN32WINDOW_ +#define _WINDOWMANAGER_WIN32_WIN32WINDOW_ + +#include +#include "windowManager/platformWindowMgr.h" +#include "gfx/gfxTarget.h" +#include "gfx/gfxStructs.h" +#include "sim/actionMap.h" + +class Win32WindowManager; + +/// Implementation of a window on Win32. +class Win32Window : public PlatformWindow +{ + friend class Win32WindowManager; + friend class GFXPCD3D9Device; + friend class GFXPCD3D9WindowTarget; + friend class GFXD3D8WindowTarget; + +public: + struct Accelerator + { + U32 mID; + EventDescriptor mDescriptor; + }; + typedef Vector AcceleratorList; + +private: + typedef Vector WinAccelList; + + /// @name Active window list + /// + /// Items used to track window instances. + /// + /// @{ + + /// Which manager created us? + Win32WindowManager *mOwningManager; + + /// Which window comes next in list? + Win32Window *mNextWindow; + + /// @} + + /// @name Window Information + /// + /// @{ + + /// Our HWND - Win32 window handle. + HWND mWindowHandle; + + /// Our former Parent HWND + HWND mOldParent; + + /// The Win32 window style we want to use when windowed. + DWORD mWindowedWindowStyle; + + /// The GFX device that we're tied to. + GFXDevice *mDevice; + + /// Reference to the render target allocated on this window. + GFXWindowTargetRef mTarget; + + /// Our current size/resolution/fullscreen status. + GFXVideoMode mVideoMode; + + /// Our position on the desktop. + Point2I mPosition; + + /// Windows HACCEL for accelerators + HACCEL mAccelHandle; + + /// Keyboard accelerators for menus + WinAccelList mWinAccelList; + + /// Is the mouse locked to this window? + bool mMouseLocked; + + /// The position the cursor was at when a mouse lock occured + Point2I mMouseLockPosition; + + /// Determines whether this window should lock the mouse when it has an opportunity + bool mShouldLockMouse; + + /// When set, we don't trigger device resets due to sizing events. + bool mSuppressReset; + + /// Menu associated with this window. This is a passive property of the window and is not required to be used at all. + HMENU mMenuHandle; + + /// Do we have a fullscreen window style set? + bool mFullscreen; + + /// @} + + /// Helper to allocate our Win32 window class. + void _registerWindowClass(); + void _unregisterWindowClass(); + + /// Windows message handler callback. + static LRESULT PASCAL WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + + /// Add an accelerator to the list of accelerators for this window. Intended for use by addAccelerators() + void addAccelerator(Accelerator &accel); + /// Remove an accelerator from the list of accelerators for this window. Intended for use by removeAccelerators() + void removeAccelerator(Accelerator &accel); + +public: + Win32Window(); + ~Win32Window(); + + /// Return the HWND (win32 window handle) for this window. + HWND &getHWND() + { + return mWindowHandle; + } + + HMENU &getMenuHandle() + { + return mMenuHandle; + } + + void setMenuHandle( HMENU menuHandle ) + { + mMenuHandle = menuHandle; + if(!mFullscreen) + SetMenu(mWindowHandle, mMenuHandle); + } + + /// Add a list of accelerators to this window + void addAccelerators(AcceleratorList &list); + /// Remove a list of accelerators from this window + void removeAccelerators(AcceleratorList &list); + + /// Returns true if @p info matches an accelerator + bool isAccelerator(const InputEventInfo &info); + + /// Allow windows to translate messages. Used for accelerators. + bool translateMessage(MSG &msg); + + virtual GFXDevice *getGFXDevice(); + virtual GFXWindowTarget *getGFXTarget(); + + virtual void setVideoMode(const GFXVideoMode &mode); + virtual const GFXVideoMode &getVideoMode(); + virtual bool clearFullscreen(); + virtual bool isFullscreen(); + virtual void _setFullscreen(const bool fullscreen); + + virtual bool setCaption(const char *cap); + virtual const char *getCaption(); + + // Window Client Area Extent + virtual void setClientExtent( const Point2I newExtent ); + virtual const Point2I getClientExtent(); + + // Window Bounds + virtual void setBounds(const RectI &newBounds); + virtual const RectI getBounds() const; + + // Window Position + virtual void setPosition( const Point2I newPosition ); + virtual const Point2I getPosition(); + virtual void centerWindow(); + virtual bool setSize(const Point2I &newSize); + + // Coordinate space conversion. + virtual Point2I clientToScreen( const Point2I& pos ); + virtual Point2I screenToClient( const Point2I& pos ); + + virtual bool isOpen(); + virtual bool isVisible(); + virtual bool isFocused(); + virtual bool isMinimized(); + virtual bool isMaximized(); + + virtual void minimize(); + virtual void maximize(); + virtual void hide(); + virtual void show(); + virtual void close(); + virtual void restore(); + virtual void setFocus(); + + virtual void setMouseLocked(bool enable); + virtual bool isMouseLocked() const { return mMouseLocked; }; + virtual bool shouldLockMouse() const { return mShouldLockMouse; }; + + virtual WindowId getWindowId(); + + virtual PlatformWindow * getNextWindow() const + { + return mNextWindow; + } + + /// Provide a simple GDI-based render for when the game is not rendering. + virtual void defaultRender(); + + /// Return the class name for the windows we create with this class. + static const UTF16 *getWindowClassName(); + + /// Return the class name for the curtain window class. + static const UTF16 *getCurtainWindowClassName(); + + /// Return the platform specific object needed to create or attach an + /// accelerated graohics drawing context on or to the window + virtual void* getPlatformDrawable() const { return mWindowHandle; } +}; +#endif diff --git a/Engine/source/windowManager/win32/win32WindowMgr.cpp b/Engine/source/windowManager/win32/win32WindowMgr.cpp new file mode 100644 index 000000000..d37e7ac05 --- /dev/null +++ b/Engine/source/windowManager/win32/win32WindowMgr.cpp @@ -0,0 +1,453 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platformWin32/platformWin32.h" +#include "windowManager/win32/win32WindowMgr.h" +#include "gfx/gfxDevice.h" +#include "windowManager/win32/winDispatch.h" +#include "core/util/journal/process.h" +#include "core/strings/unicode.h" + +// ------------------------------------------------------------------------ + +void CloseSplashWindow(HINSTANCE hinst); + +PlatformWindowManager * CreatePlatformWindowManager() +{ + return new Win32WindowManager(); +} + +// ------------------------------------------------------------------------ + +Win32WindowManager::Win32WindowManager() +{ + // Register in the process list. + Process::notify(this, &Win32WindowManager::_process, PROCESS_INPUT_ORDER); + + // Init our list of allocated windows. + mWindowListHead = NULL; + + // By default, we have no parent window. + mParentWindow = NULL; + + mCurtainWindow = NULL; + + mOffscreenRender = false; +} + +Win32WindowManager::~Win32WindowManager() +{ + // Get ourselves off the process list. + Process::remove(this, &Win32WindowManager::_process); + + // Kill all our windows first. + while(mWindowListHead) + // The destructors update the list, so this works just fine. + delete mWindowListHead; +} + +RectI Win32WindowManager::getPrimaryDesktopArea() +{ + RECT primaryWorkRect; + SystemParametersInfo(SPI_GETWORKAREA, 0, &primaryWorkRect, 0); + + RectI res; + res.point.x = primaryWorkRect.left; + res.point.y = primaryWorkRect.top; + res.extent.x = primaryWorkRect.right - primaryWorkRect.left; + res.extent.y = primaryWorkRect.bottom - primaryWorkRect.top; + + return res; +} + +Point2I Win32WindowManager::getDesktopResolution() +{ + DEVMODE devMode; + dMemset( &devMode, 0, sizeof( devMode ) ); + devMode.dmSize = sizeof( devMode ); + + if (!::EnumDisplaySettings(NULL, ENUM_REGISTRY_SETTINGS, &devMode)) + return Point2I(-1,-1); + + // Return Resolution + return Point2I(devMode.dmPelsWidth, devMode.dmPelsHeight); +} + +S32 Win32WindowManager::getDesktopBitDepth() +{ + DEVMODE devMode; + dMemset( &devMode, 0, sizeof( devMode ) ); + devMode.dmSize = sizeof( devMode ); + + if (!::EnumDisplaySettings(NULL, ENUM_REGISTRY_SETTINGS, &devMode)) + return -1; + + // Return Bits per Pixel + return (S32)devMode.dmBitsPerPel; +} + +BOOL Win32WindowManager::MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData ) +{ + Vector * regions = (Vector*)dwData; + + regions->increment(); + regions->last().point.x = lprcMonitor->left; + regions->last().point.y = lprcMonitor->top; + regions->last().extent.x = lprcMonitor->right - lprcMonitor->left; + regions->last().extent.y = lprcMonitor->bottom - lprcMonitor->top; + + return true; +} + +void Win32WindowManager::getMonitorRegions(Vector ®ions) +{ + EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (U32)(void*)®ions); +} + +void Win32WindowManager::getWindows(VectorPtr &windows) +{ + Win32Window *win = mWindowListHead; + while(win) + { + windows.push_back(win); + win = win->mNextWindow; + } +} + +PlatformWindow *Win32WindowManager::createWindow(GFXDevice *device, const GFXVideoMode &mode) +{ + // Do the allocation. + Win32Window *w32w = new Win32Window(); + w32w->setOffscreenRender(mOffscreenRender); + w32w->mWindowId = getNextId(); + w32w->mOwningManager = this; + + // Link into our list of windows. + linkWindow(w32w); + + DWORD dwExStyle; + DWORD dwStyle = WS_CLIPCHILDREN | WS_CLIPSIBLINGS; + dwStyle |= WS_OVERLAPPEDWINDOW | WS_THICKFRAME | WS_CAPTION; + dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + + // If we're parented, we want a different set of window styles. + if(mParentWindow) + dwStyle = WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CHILDWINDOW; + + if (mOffscreenRender) + { + dwStyle = WS_OVERLAPPEDWINDOW; + dwExStyle = 0; + } + + // Create the window handle + w32w->mWindowHandle = CreateWindowEx( + dwExStyle, + Win32Window::getWindowClassName(), //class name + String( getEngineProductString() ).utf16(), //window title + dwStyle, //style - need clip siblings/children for opengl + 0, + 0, + 0, + 0, + mParentWindow, //parent window + NULL, //menu? No. + NULL, //the hInstance + NULL ); //no funky params + + // Note the style we created with so we can switch back to it when we're + // done with full-screen mode. + w32w->mWindowedWindowStyle = dwStyle; + + // Set the video mode on the window + w32w->setVideoMode(mode); + + // Associate our window struct with the HWND. + SetWindowLongPtrW(w32w->mWindowHandle, GWLP_USERDATA, (LONG)w32w); + + // Do some error checking. + AssertFatal(w32w->mWindowHandle != NULL, "Win32WindowManager::createWindow - Could not create window!"); + if(w32w->mWindowHandle == NULL) + { + Con::errorf("Win32WindowManager::createWindow - Could not create window!"); + delete w32w; + return NULL; + } + + // If we're not rendering offscreen, make sure our window is shown and drawn to. + + if (!mOffscreenRender) + ShowWindow( w32w->mWindowHandle, SW_SHOWDEFAULT ); + + // Close any splash screen we created + CloseSplashWindow(winState.appInstance); + + // Bind the window to the specified device. + if(device) + { + w32w->mDevice = device; + w32w->mTarget = device->allocWindowTarget(w32w); + AssertISV(w32w->mTarget, + "Win32WindowManager::createWindow - failed to get a window target back from the device."); + } + else + { + Con::warnf("Win32WindowManager::createWindow - created a window with no device!"); + } + + // Update it if needed. + UpdateWindow( w32w->mWindowHandle ); + + return w32w; +} + + +void Win32WindowManager::setParentWindow(void* newParent) +{ + Con::printf( "Setting parent HWND: %d", newParent ); + mParentWindow = (HWND)newParent; + if( mWindowListHead && mWindowListHead->mWindowHandle ) + ::SetParent( mWindowListHead->mWindowHandle, mParentWindow); +} + +void* Win32WindowManager::getParentWindow() +{ + return (void*)mParentWindow; +} + +void Win32WindowManager::_process() +{ + MSG msg; + bool _blocking = false; + + // CodeReview [tom, 4/30/2007] Maintaining two completely separate message + // handlers that are essentially the same is silly. The first one never + // seems to run as _blocking is hard coded to false above, so is this even + // needed ? If it is, this should be rewritten to use the one loop that + // adjusts as needed based on _blocking and Journal::IsPlaying() + + if (_blocking && !Journal::IsPlaying()) + { + // In blocking mode, we process one message at a time. + if (GetMessage(&msg, NULL, 0, 0)) + { + bool noTranslate = false; + Win32Window *w32w = mWindowListHead; + while(w32w) + { + noTranslate = w32w->translateMessage(msg); + if(noTranslate) break; + w32w = w32w->mNextWindow; + } + + if(! noTranslate) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + else + // This should be WM_QUIT + Dispatch(ImmediateDispatch,0,msg.message,msg.wParam,msg.lParam); + } + else + { + // Process all queued up messages + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + bool translated = false; + +// Win32Window *w32w = mWindowListHead; +// while(w32w) +// { +// noTranslate = w32w->translateMessage(msg); +// if(noTranslate) break; +// w32w = w32w->mNextWindow; +// } + + // [tom, 4/30/2007] I think this should work, but leaving the above commented + // out just in case this is actually fubared with multiple windows. + Win32Window* window = (Win32Window*)(GetWindowLong(msg.hwnd, GWL_USERDATA)); + if(window) + translated = window->translateMessage(msg); + + if(! translated) + { + // Win32Window::translateMessage() will post a WM_COMMAND event for + // translated accelerator events, so dispatching again will cause a + // the input event to be dispatched, which is usually not what we want. + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + if (msg.message == WM_QUIT) + { + Dispatch(ImmediateDispatch,0,msg.message,msg.wParam,msg.lParam); + break; + } + } + } + + // Dispatch any delayed events + while (DispatchNext()); + + // Fire off idle events for every window. + Win32Window *w32w = mWindowListHead; + while(w32w) + { + w32w->idleEvent.trigger(); + w32w = w32w->mNextWindow; + } + +} + +PlatformWindow * Win32WindowManager::getWindowById( WindowId id ) +{ + // Walk the list and find the matching id, if any. + Win32Window *win = mWindowListHead; + while(win) + { + if(win->getWindowId() == id) + return win; + + win = win->mNextWindow; + } + + return NULL; +} + +PlatformWindow * Win32WindowManager::getFirstWindow() +{ + return mWindowListHead != NULL ? mWindowListHead : NULL; +} + +PlatformWindow* Win32WindowManager::getFocusedWindow() +{ + Win32Window* window = mWindowListHead; + while( window ) + { + if( window->isFocused() ) + return window; + + window = window->mNextWindow; + } + + return NULL; +} + +void Win32WindowManager::linkWindow( Win32Window *w ) +{ + w->mNextWindow = mWindowListHead; + mWindowListHead = w; +} + +void Win32WindowManager::unlinkWindow( Win32Window *w ) +{ + Win32Window **walk = &mWindowListHead; + while(*walk) + { + if(*walk != w) + { + // Advance to next item in list. + walk = &(*walk)->mNextWindow; + continue; + } + + // Got a match - unlink and return. + *walk = (*walk)->mNextWindow; + return; + } +} + +void Win32WindowManager::_processCmdLineArgs( const S32 argc, const char **argv ) +{ + if (argc > 1) + { + for (int i = 1; i < argc; i++) + { + if ( dStrnicmp( argv[i], "-window", 7 ) == 0 ) + { + i++; + + if ( i >= argc ) + { + Con::errorf( "Command line error: -window requires an argument" ); + break; + } + + S32 hwnd = dAtoi( argv[i] ); + + if ( hwnd == 0 || hwnd == S32_MAX ) + { + Con::errorf( "Command line error: -window requires a number, found [%s]", argv[i] ); + break; + } + + mParentWindow = (HWND)hwnd; + Con::printf( "HWND from command line: %d", hwnd ); + } + + if ( dStrnicmp( argv[i], "-offscreen", 10 ) == 0 ) + { + mOffscreenRender = true; + } + + } + } +} + +void Win32WindowManager::lowerCurtain() +{ + if(mCurtainWindow) + return; + + // For now just grab monitor of the first window... we may need to + // beef this up later on, maybe by passing in the window that's entering + // leaving full-screen to lowerCurtain. + HMONITOR hMon = MonitorFromWindow(mWindowListHead->getHWND(), MONITOR_DEFAULTTOPRIMARY); + + // Get the monitor's extents. + MONITORINFO monInfo; + dMemset(&monInfo, 0, sizeof MONITORINFO); + monInfo.cbSize = sizeof MONITORINFO; + + GetMonitorInfo(hMon, &monInfo); + + mCurtainWindow = CreateWindow(Win32Window::getCurtainWindowClassName(), + dT(""), (WS_POPUP | WS_MAXIMIZE | WS_VISIBLE), + monInfo.rcWork.left, monInfo.rcWork.top, + monInfo.rcWork.right - monInfo.rcWork.left, + monInfo.rcWork.bottom - monInfo.rcWork.top, + NULL, NULL, NULL, NULL); + + if (!mOffscreenRender) + SetWindowPos(mCurtainWindow, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); +} + +void Win32WindowManager::raiseCurtain() +{ + if(!mCurtainWindow) + return; + + DestroyWindow(mCurtainWindow); + mCurtainWindow = NULL; +} \ No newline at end of file diff --git a/Engine/source/windowManager/win32/win32WindowMgr.h b/Engine/source/windowManager/win32/win32WindowMgr.h new file mode 100644 index 000000000..e0505680b --- /dev/null +++ b/Engine/source/windowManager/win32/win32WindowMgr.h @@ -0,0 +1,93 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _WINDOWMANAGER_WIN32_WIN32WINDOWMANAGER_ +#define _WINDOWMANAGER_WIN32_WIN32WINDOWMANAGER_ + +#include + +#include "math/mMath.h" +#include "gfx/gfxStructs.h" +#include "windowManager/win32/win32Window.h" +#include "core/util/tVector.h" + +/// Win32 implementation of the window manager interface. +class Win32WindowManager : public PlatformWindowManager +{ + friend class Win32Window; + + virtual void _processCmdLineArgs(const S32 argc, const char **argv); + + /// Link the specified window into the window list. + void linkWindow(Win32Window *w); + + /// Remove specified window from the window list. + void unlinkWindow(Win32Window *w); + + /// Callback for the process list. + void _process(); + + /// List of allocated windows. + Win32Window *mWindowListHead; + + /// Parent window, used in window setup in web plugin scenarios. + HWND mParentWindow; + + /// set via command line -offscreen option, controls whether rendering/input + // is intended for offscreen rendering + bool mOffscreenRender; + + /// Callback to receive information about available monitors. + static BOOL CALLBACK MonitorEnumProc( + HMONITOR hMonitor, // handle to display monitor + HDC hdcMonitor, // handle to monitor DC + LPRECT lprcMonitor, // monitor intersection rectangle + LPARAM dwData // data + ); + + /// If a curtain window is present, then its HWND will be stored here. + HWND mCurtainWindow; + +public: + Win32WindowManager(); + ~Win32WindowManager(); + + virtual RectI getPrimaryDesktopArea(); + virtual S32 getDesktopBitDepth(); + virtual Point2I getDesktopResolution(); + + virtual void getMonitorRegions(Vector ®ions); + virtual PlatformWindow *createWindow(GFXDevice *device, const GFXVideoMode &mode); + virtual void getWindows(VectorPtr &windows); + + virtual void setParentWindow(void* newParent); + virtual void* getParentWindow(); + + virtual PlatformWindow *getWindowById(WindowId id); + virtual PlatformWindow *getFirstWindow(); + virtual PlatformWindow* getFocusedWindow(); + + virtual void lowerCurtain(); + virtual void raiseCurtain(); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/windowManager/win32/winDispatch.cpp b/Engine/source/windowManager/win32/winDispatch.cpp new file mode 100644 index 000000000..c2440b427 --- /dev/null +++ b/Engine/source/windowManager/win32/winDispatch.cpp @@ -0,0 +1,594 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#define NO_MINMAX +#define WIN32_LEAN_AND_MEAN + +// This is a fix for mouse wheel support on +// older versions of VC++. +#if _MSC_VER < 1500 +#define _WIN32_WINNT 0x0400 +#endif + +#include + +#include "platform/event.h" +#include "platform/platformInput.h" +#include "windowManager/win32/winDispatch.h" +#include "windowManager/win32/win32Window.h" +#include "windowManager/win32/win32CursorController.h" +#include "platformWin32/winDirectInput.h" +#include "core/util/journal/process.h" +#include "core/util/journal/journaledSignal.h" + +static U32 _ModifierKeys=0; +static BYTE keyboardState[256]; +static bool initKBState = false; +static bool sgDoubleByteEnabled = false; + +// is keyboard input a standard (non-changing) VK keycode +#define dIsStandardVK(c) (((0x08 <= (c)) && ((c) <= 0x12)) || \ + ((c) == 0x1b) || \ + ((0x20 <= (c)) && ((c) <= 0x2e)) || \ + ((0x30 <= (c)) && ((c) <= 0x39)) || \ + ((0x41 <= (c)) && ((c) <= 0x5a)) || \ + ((0x70 <= (c)) && ((c) <= 0x7B))) + +extern InputObjectInstances DIK_to_Key( U8 dikCode ); + +extern U8 TranslateOSKeyCode(U8 vcode ); + +extern InputModifiers convertModifierBits(const U32 in); + +static void _keyboardEvent(Win32Window* window,UINT message, WPARAM wParam, WPARAM lParam) +{ + if(!initKBState) + { + dMemset(keyboardState, 0, sizeof(keyboardState)); + initKBState = true; + } + + // Extract windows key info: + // S32 repeatCount = (lParam & 0xffff); + U32 scanCode = (lParam >> 16) & 0xff; + bool extended = lParam & (1 << 24); // Enhanced keyboard key + bool previous = lParam & (1 << 30); // Previously down + bool make = (message == WM_KEYDOWN || message == WM_SYSKEYDOWN); + + // Translate the OS virtual key code to a Torque KEY_XXXX. + S32 nVirtkey = TranslateOSKeyCode( wParam ); + + S32 keyCode; + if ( wParam == VK_PROCESSKEY && sgDoubleByteEnabled ) + keyCode = MapVirtualKey( scanCode, 1 ); // This is the REAL virtual key... + else + keyCode = wParam; + + // Convert alt/shift/ctrl to left or right variant if needed. + S32 newVirtKey = nVirtkey; + switch(nVirtkey) + { + case KEY_ALT: + newVirtKey = extended ? KEY_RALT : KEY_LALT; + break; + case KEY_CONTROL: + newVirtKey = extended ? KEY_RCONTROL : KEY_LCONTROL; + break; + case KEY_SHIFT: + newVirtKey = (scanCode == 54) ? KEY_RSHIFT : KEY_LSHIFT; + break; + case KEY_RETURN: + if ( extended ) + newVirtKey = KEY_NUMPADENTER; + break; + } + + // Track modifier keys + U32 modifier = 0; + switch (newVirtKey) + { + case KEY_LALT: modifier = IM_LALT; break; + case KEY_RALT: modifier = IM_RALT; break; + case KEY_LSHIFT: modifier = IM_LSHIFT; break; + case KEY_RSHIFT: modifier = IM_RSHIFT; break; + case KEY_LCONTROL: modifier = IM_LCTRL; break; + case KEY_RCONTROL: modifier = IM_RCTRL; break; + } + + if (make) + { + _ModifierKeys |= modifier; + keyboardState[keyCode] |= 0x80; + } + else + { + _ModifierKeys &= ~modifier; + keyboardState[keyCode] &= 0x7f; + } + + U32 torqueMods = convertModifierBits( _ModifierKeys ); + Input::setModifierKeys( torqueMods ); + + // If character event translation is active and this isn't a key + // mapped in the global action map, try converting the event into + // a character event first. + + if( make + && window->getKeyboardTranslation() + && !window->shouldNotTranslate( torqueMods, newVirtKey ) ) + { + U16 chars[ 64 ]; + dMemset( chars, 0, sizeof( chars ) ); + + S32 res = ToUnicode( keyCode, scanCode, keyboardState, chars, sizeof( chars ) / sizeof( chars[ 0 ] ), 0 ); + + // This should only happen on Window 9x/ME systems + if( res == 0 ) + res = ToAscii( keyCode, scanCode, keyboardState, chars, 0 ); + + if( res >= 1 ) + { + // Post chars, but filter them to not be control codes... this is a bit hacky. + + bool handledCharEvent = false; + for( S32 i=0; i< res; i ++ ) + if( chars[i] >= 32) + { + window->charEvent.trigger(window->getWindowId(),_ModifierKeys,chars[i]); + handledCharEvent = true; + } + + if( handledCharEvent ) + return; + } + } + + // Produce a key event. + + U32 action = make ? (previous ? IA_REPEAT : IA_MAKE ) : IA_BREAK; + window->keyEvent.trigger(window->getWindowId(),_ModifierKeys,action,newVirtKey); +} + + +//----------------------------------------------------------------------------- + +static bool _dispatch(HWND hWnd,UINT message,WPARAM wParam,WPARAM lParam) +{ + static bool button[3] = {false,false,false}; + static S32 mouseNCState = -1; // -1 denotes unchanged, + // 0 denotes changed but was hidden + // 1 denotes changed but was visible + Win32Window* window = hWnd?(Win32Window*)GetWindowLong(hWnd, GWL_USERDATA): 0; + const WindowId devId = window ? window->getWindowId() : 0; + + // State tracking for focus/lose focus cursor management + static bool cursorLocked = false; + static bool cursorVisible = true; + + switch(message) + { + case WM_MOUSEMOVE: + { + // Skip it if we have no window! + if (!window || !window->getCursorController()) + break; + + + PlatformCursorController *pController = window->getCursorController(); + + // If we're locked and unfocused, ignore it. + if(window->shouldLockMouse() && !window->isFocused()) + break; + + // If the mouse was shown to accommodate a NC mouse move + // we need to change it back to what it was + if( mouseNCState != -1 ) + { + pController->setCursorVisible( mouseNCState ); + mouseNCState = -1; // reset to unchanged + } + + // Let the cursor manager update the native cursor. + pController->refreshCursor(); + + // Grab the mouse pos so we can modify it. + S32 mouseX = S16(LOWORD(lParam)); + S32 mouseY = S16(HIWORD(lParam)); + + // Ensure mouse lock when appropriate + window->setMouseLocked( window->shouldLockMouse() ); + + // Are we locked? + if(window->isMouseLocked()) + { + // Always invisible when locked. + if( window->isCursorVisible() ) + window->setCursorVisible( false ); + + RECT r; + GetWindowRect(window->getHWND(), &r); + + // See Win32Window::setMouseLocked for explanation + RECT rCopy = r; + rCopy.top += 32; rCopy.bottom -= 64; + rCopy.left += 32; rCopy.right -= 64; + ClipCursor(&rCopy); + + // Recenter the mouse if necessary (don't flood the message pump) + Point2I curPos; + pController->getCursorPosition( curPos ); + + const S32 centerX = (r.right + r.left) / 2; + const S32 centerY = (r.bottom + r.top) / 2; + + if( curPos.x != centerX || curPos.y != centerY ) + pController->setCursorPosition(centerX, centerY); + + // Convert the incoming client pos into a screen pos, so we can + // accurately convert to relative coordinates. + POINT mousePos; + mousePos.x = mouseX; + mousePos.y = mouseY; + + ClientToScreen(window->getHWND(), &mousePos); + + // Now we can calculate the position relative to the center we set. + mouseX = mousePos.x - centerX; + mouseY = mousePos.y - centerY; + } + else + { + // Probably don't need to call this all the time but better + // safe than sorry... + ClipCursor(NULL); + } + + window->mouseEvent.trigger(devId,_ModifierKeys,mouseX,mouseY,window->isMouseLocked()); + break; + } + + // We want to show the system cursor whenever we leave + // our window, and it'd be simple, except for one problem: + // showcursor isn't a toggle. so, keep hammering it until + // the cursor is *actually* going to be shown. + case WM_NCMOUSEMOVE: + { + if( window ) + { + mouseNCState = ( window->isCursorVisible() ? 1 : 0); + window->setCursorVisible( true ); + } + + break; + } + + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: { + int index = (message - WM_LBUTTONDOWN) / 3; + button[index] = true; + + // Capture the mouse on button down to allow dragging outside + // of the window boundary. + if (GetCapture() != hWnd) + SetCapture(hWnd); + + if (window) + window->buttonEvent.trigger(devId,_ModifierKeys,IA_MAKE,index); + break; + } + + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: { + int index = (message - WM_LBUTTONUP) / 3; + button[index] = false; + + // Release mouse capture from button down. + if (!button[0] && !button[1] && !button[2]) + ReleaseCapture(); + + if (window) + window->buttonEvent.trigger(devId,_ModifierKeys,IA_BREAK,index); + break; + } + + case WM_MOUSEWHEEL: // Vertical wheel. + if (window) + window->wheelEvent.trigger(devId,_ModifierKeys,0,GET_WHEEL_DELTA_WPARAM(wParam)); + break; + +#ifdef WM_MOUSEHWHEEL // Vista + case WM_MOUSEHWHEEL: // Horizontal wheel. + if( window ) + window->wheelEvent.trigger( devId, _ModifierKeys, GET_WHEEL_DELTA_WPARAM( wParam ), 0 ); + break; +#endif + + case WM_KEYUP: + case WM_SYSKEYUP: + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + if (window) + _keyboardEvent(window,message,wParam,lParam); + break; + + // NOTE: if wParam is NOT equal to our window handle then we are GAINING focus + case WM_SETFOCUS: + + // clear any key states + _ModifierKeys = 0; + dMemset(keyboardState, 0, 256); + Input::setModifierKeys(0); + + // We must have a window present; otherwise there's nothing further + // we can do about this event. + if (window && window->getHWND() != (HWND)wParam) + { + if (cursorVisible == false) + window->setCursorVisible(false); + + if (cursorLocked == true) + window->setMouseLocked(true); + + // Update window state. + window->setBackground(false); + + // Fire event. + window->appEvent.trigger(devId, GainFocus); + + if (!Input::isActive()) + Input::activate(); + } + break; + + // NOTE: if wParam is NOT equal to our window handle then we are LOSING focus + case WM_KILLFOCUS: + + // clear any key states + _ModifierKeys = 0; + dMemset(keyboardState, 0, 256); + Input::setModifierKeys(0); + + // We must have a window present; otherwise there's nothing further + // we can do about this event. + if (window && window->getHWND() != (HWND)wParam) + { + HWND hwnd = (HWND)wParam; + UTF16 classBuf[256]; + + if (hwnd) + GetClassName(hwnd, classBuf, sizeof(classBuf)); + + // We toggle the mouse lock when we become inactive + // causing the subsequent lock call to defer itself + // until the window becomes active again. + if (window && window->isMouseLocked()) + { + window->setMouseLocked( false ); + window->setMouseLocked( true ); + } + + // FIXME [tom, 5/1/2007] Hard coding this is lame since there's a const in win32Window.cpp + // CodeReview - this fails if there is a second jug app in the arena. + if (hwnd == NULL || dStrcmp(classBuf, L"TorqueJuggernaughtWindow") != 0) + { + // We are being made inactive and the window being made active isn't + // a jugg window. Thus, we need to deactivate input. + if (Input::isActive()) + Input::deactivate(); + } + + cursorVisible = window->isCursorVisible(); + if (!cursorVisible) + window->setCursorVisible(true); + + cursorLocked = window->isMouseLocked(); + if (cursorLocked) + window->setMouseLocked(false); + + // Update window state. + window->setBackground(true); + + // Fire event. + window->appEvent.trigger(devId, LoseFocus); + } + break; + + case WM_ACTIVATEAPP: + if (wParam) + { + // Could extract current modifier state from windows. + _ModifierKeys = 0; + Input::setModifierKeys(_ModifierKeys); + } + break; + + case WM_CLOSE: + if (window) + window->appEvent.trigger(devId,WindowClose); + + // Force a quit if we're in play mode, otherwise there would be + // no way to stop a journal playback.( + if (Journal::IsPlaying()) + Process::requestShutdown(); + break; + + case WM_TIMER: { + if (window) + window->appEvent.trigger(devId,Timer); + break; + } + + case WM_DESTROY:{ + // Only people who care about this currently are web plugins, because + // everyone else will just handle the WM_CLOSE app event. + if(window) + window->appEvent.trigger(devId,WindowDestroy); + break; + } + + case WM_QUIT: { + // Quit indicates that we're not going to receive anymore Win32 messages. + // Therefore, it's appropriate to flag our event loop for exit as well, + // since we won't be getting any more messages. + Process::requestShutdown(); + break; + } + + // CodeReview - This is not used now and will incur an overhead for rendering + // since the renderThreadBlocked fix requires handling WM_PAINT and + // triggering the displayEvent. May need to revisit this at a later + // time if we want event driven rendering. + //case WM_PAINT: { + // // Checking for isOpen will keep us from generating an event + // // during the window creation process, which can cause problems + // // with the journaling. + // if (window && window->isOpen() && !winState.renderThreadBlocked ) + // window->displayEvent.trigger(devId); + //} + case WM_SIZE: { + if (window && wParam != SIZE_MINIMIZED && !Journal::IsPlaying()) + { + window->resizeEvent.trigger(window->getWindowId(), LOWORD(lParam),HIWORD(lParam)); + + // Consume all existing mouse events and those posted to our own dispatch queue + MSG msg; + PeekMessage( &msg, 0,WM_MOUSEFIRST,WM_MOUSELAST , PM_QS_POSTMESSAGE | PM_NOYIELD | PM_REMOVE ); + RemoveMessages( NULL, WM_MOUSEMOVE, WM_MOUSEMOVE ); + + if( window->isMouseLocked()) + { + RECT r; + GetWindowRect(window->getHWND(), &r); + + S32 centerX = (r.right + r.left) >> 1; + S32 centerY = ((r.bottom + r.top) >> 1); + window->setCursorPosition( centerX, centerY ); + + // Set the CursorPos + SetCursorPos(centerX, centerY); + } + } + + } + + } + return true; +} + + +//----------------------------------------------------------------------------- + +// Structure used to store Windows events for delayed dispatching +struct WinMessageQueue +{ +public: + struct Message { + HWND hWnd; + UINT message; + WPARAM wparam; + WPARAM lparam; + }; + + WinMessageQueue() + { + VECTOR_SET_ASSOCIATION( _messageList ); + } + bool isEmpty() { + return !_messageList.size(); + } + void post(HWND hWnd,UINT message,WPARAM wparam,WPARAM lparam) { + Message msg; + msg.hWnd = hWnd; + msg.message = message; + msg.wparam = wparam; + msg.lparam = lparam; + _messageList.push_back(msg); + } + bool next(Message* msg) { + if (!_messageList.size()) + return false; + *msg = _messageList.first(); + _messageList.pop_front(); + return true; + } + void remove(HWND hWnd, UINT msgBegin = -1, UINT msgEnd = -1) { + for (S32 i = 0; i < _messageList.size(); i++) + { + // Match Window + if( hWnd != NULL && _messageList[i].hWnd == hWnd) + _messageList.erase_fast(i--); + else if( msgBegin != -1 && msgEnd != -1 ) + { + // CodeReview - Match Message Range [6/30/2007 justind] + // + // Word of caution : Use this only if you know what you're doing. + // I cannot be responsible for you blowing your leg off destroying + // a bunch of messages you didn't intend to if you specify a ridiculous + // range of messages values. + // + // To filter a single message, pass the message as the begin and end. + if( _messageList[i].message >= msgBegin && _messageList[i].message <= msgEnd ) + _messageList.erase_fast(i--); + } + } + } + +private: + Vector _messageList; +}; + +static WinMessageQueue _MessageQueue; + + +void RemoveMessages(HWND hWnd,UINT msgBegin,WPARAM msgEnd ) +{ + _MessageQueue.remove( hWnd, msgBegin, msgEnd ); +} + +// Dispatch the window event, or queue up for later +void Dispatch(DispatchType type,HWND hWnd,UINT message,WPARAM wparam,WPARAM lparam) +{ + // If the message queue is not empty, then we'll need to delay + // this dispatch in order to preserve message order. + if (type == DelayedDispatch || !_MessageQueue.isEmpty()) + _MessageQueue.post(hWnd,message,wparam,lparam); + else + _dispatch(hWnd,message,wparam,lparam); +} + +// Dispatch next even in the queue +bool DispatchNext() +{ + WinMessageQueue::Message msg; + if (!_MessageQueue.next(&msg)) + return false; + _dispatch(msg.hWnd,msg.message,msg.wparam,msg.lparam); + return true; +} + +// Remove events from the queue +void DispatchRemove(HWND hWnd) +{ + _MessageQueue.remove(hWnd); +} diff --git a/Engine/source/windowManager/win32/winDispatch.h b/Engine/source/windowManager/win32/winDispatch.h new file mode 100644 index 000000000..0f9cbf521 --- /dev/null +++ b/Engine/source/windowManager/win32/winDispatch.h @@ -0,0 +1,74 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _WINDOWMANAGER_WIN32_WINDISPATCH_H_ +#define _WINDOWMANAGER_WIN32_WINDISPATCH_H_ + +// +// *** This header requires that Window.h be included before this. +// + +/// Some events must be processed immediately, and others can or should be +/// processed later. This enum allows us to distinguish between the two +/// types. +enum DispatchType { + DelayedDispatch, + ImmediateDispatch, +}; + +/// Dispatch the event into the journaling system. +/// +/// Dispatch Win32 events into the journaling system. To avoid problems +/// with journaling, events should normally use the DelayedDispatch type. +/// +/// Delayed events are pushed onto a queue for later processing by DispatchNext(). +void Dispatch(DispatchType,HWND hWnd,UINT message,WPARAM wparam,WPARAM lparam); + +/// Remove messages from the event queue, matching a msg value range or hWnd +/// +/// If no filter is specified, either HWND or MessageRange, nothing will be removed +/// You may not match HWND and MsgRange both, currently. +/// +/// Message Range is calculated as follows. +/// @li Both Begin and End are specified as message values, ex WM_MOUSEMOVE +/// @li Specifying an identical set of begin/end will remove all messages matching that message value (WM_MOUSEMOVE) +/// @li If you specify a range it will remove from that beginning value through the end value +/// +/// @note : The range is useful because on windows messages declared such that you can filter a block of +/// messages just by specifying the beginning value and end. +/// ex. WM_MOUSEFIRST,WM_MOUSELAST range will match all mouse messages. + +/// +/// @param hWnd The HWND to filter by, this cannot be combined with a msg range filter currently +/// @param msgBegin The beginning msg value to filter from +/// @param msgEnd The ending msg value to filter to +void RemoveMessages(HWND hWnd,UINT msgBegin,UINT msgEnd ); + +/// Dispatch the next event in the delayed dispatch queue. +/// This function should be called outside of any journaled calls. +/// Returns true if an event was dispatched. +bool DispatchNext(); + +/// Remove events related to the window from the dispatch queue. +void DispatchRemove(HWND hWnd); + +#endif \ No newline at end of file diff --git a/Engine/source/windowManager/windowInputGenerator.cpp b/Engine/source/windowManager/windowInputGenerator.cpp new file mode 100644 index 000000000..df4a6c906 --- /dev/null +++ b/Engine/source/windowManager/windowInputGenerator.cpp @@ -0,0 +1,370 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "windowManager/windowInputGenerator.h" +#include "windowManager/platformWindow.h" +#include "sim/actionMap.h" +#include "component/interfaces/IProcessInput.h" + + +extern InputModifiers convertModifierBits(const U32 in); + + +//----------------------------------------------------------------------------- +// Constructor/Destructor +//----------------------------------------------------------------------------- +WindowInputGenerator::WindowInputGenerator( PlatformWindow *window ) : + mWindow(window), + mInputController(NULL), + mLastCursorPos(0,0), + mClampToWindow(true), + mPixelsPerMickey(1.0f), + mNotifyPosition(true), + mFocused(false) +{ + AssertFatal(mWindow, "NULL PlatformWindow on WindowInputGenerator creation"); + +#ifdef TORQUE_OS_XENON + mFocused = true; +#endif + + if (mWindow->getOffscreenRender()) + mFocused = true; + + mWindow->appEvent.notify(this, &WindowInputGenerator::handleAppEvent); + mWindow->mouseEvent.notify(this, &WindowInputGenerator::handleMouseMove); + mWindow->wheelEvent.notify(this, &WindowInputGenerator::handleMouseWheel); + mWindow->buttonEvent.notify(this, &WindowInputGenerator::handleMouseButton); + mWindow->keyEvent.notify(this, &WindowInputGenerator::handleKeyboard); + mWindow->charEvent.notify(this, &WindowInputGenerator::handleCharInput); + + // We also want to subscribe to input events. + Input::smInputEvent.notify(this, &WindowInputGenerator::handleInputEvent); +} + +WindowInputGenerator::~WindowInputGenerator() +{ + if( mWindow ) + { + mWindow->mouseEvent.remove(this, &WindowInputGenerator::handleMouseMove); + mWindow->buttonEvent.remove(this, &WindowInputGenerator::handleMouseButton); + mWindow->wheelEvent.remove(this, &WindowInputGenerator::handleMouseWheel); + mWindow->keyEvent.remove(this, &WindowInputGenerator::handleKeyboard); + mWindow->charEvent.remove(this, &WindowInputGenerator::handleCharInput); + mWindow->appEvent.remove(this, &WindowInputGenerator::handleAppEvent); + } + + Input::smInputEvent.remove(this, &WindowInputGenerator::handleInputEvent); +} + +//----------------------------------------------------------------------------- +// Process an input event and pass it on. +// Respect the action map. +//----------------------------------------------------------------------------- +void WindowInputGenerator::generateInputEvent( InputEventInfo &inputEvent ) +{ + if( !mInputController || !mFocused ) + return; + + // Give the ActionMap first shot. + if (ActionMap::handleEventGlobal(&inputEvent)) + return; + + if( mInputController->processInputEvent( inputEvent ) ) + return; + + // If we get here we failed to process it with anything prior... so let + // the ActionMap handle it. + ActionMap::handleEvent(&inputEvent); + +} + +//----------------------------------------------------------------------------- +// Mouse Events +//----------------------------------------------------------------------------- +void WindowInputGenerator::handleMouseMove( WindowId did, U32 modifier, S32 x, S32 y, bool isRelative ) +{ + if( !mInputController || !mFocused ) + return; + + // jddTODO : Clean this up + // CodeReview currently the Torque GuiCanvas deals with mouse input + // as relative movement, even when the cursor is visible. Because + // of this there is an asinine bit of code in there that manages + // updating the cursor position on the class based on relative movement. + // Because of this we always have to generate and send off for processing + // relative events, even if the mouse is not locked. + // I'm considering removing this in the Canvas refactor, thoughts? [7/6/2007 justind] + + // Generate a base Movement along and Axis event + InputEventInfo event; + event.deviceType = MouseDeviceType; + event.deviceInst = 0; + event.objType = SI_AXIS; + event.modifier = convertModifierBits(modifier); + event.ascii = 0; + + // Generate delta movement along each axis + Point2F cursDelta; + if(isRelative) + { + cursDelta.x = F32(x) * mPixelsPerMickey; + cursDelta.y = F32(y) * mPixelsPerMickey; + } + else + { + cursDelta.x = F32(x - mLastCursorPos.x); + cursDelta.y = F32(y - mLastCursorPos.y); + } + + // If X axis changed, generate a relative event + if(mFabs(cursDelta.x) > 0.1) + { + event.objInst = SI_XAXIS; + event.action = SI_MOVE; + event.fValue = cursDelta.x; + generateInputEvent(event); + } + + // If Y axis changed, generate a relative event + if(mFabs(cursDelta.y) > 0.1) + { + event.objInst = SI_YAXIS; + event.action = SI_MOVE; + event.fValue = cursDelta.y; + generateInputEvent(event); + } + + // CodeReview : If we're not relative, pass along a positional update + // so that the canvas can update it's internal cursor tracking + // point. [7/6/2007 justind] + if( !isRelative ) + { + if( mClampToWindow ) + { + Point2I winExtent = mWindow->getClientExtent(); + x = mClampF(x, 0.0f, F32(winExtent.x - 1)); + y = mClampF(y, 0.0f, F32(winExtent.y - 1)); + + } + + // When the window gains focus, we send a cursor position event + if( mNotifyPosition ) + { + mNotifyPosition = false; + + // We use SI_MAKE to signify that the position is being set, not relatively moved. + event.action = SI_MAKE; + + // X Axis + event.objInst = SI_XAXIS; + event.fValue = (F32)x; + generateInputEvent(event); + + // Y Axis + event.objInst = SI_YAXIS; + event.fValue = (F32)y; + generateInputEvent(event); + } + + mLastCursorPos = Point2I(x,y); + + } + else + { + mLastCursorPos += Point2I(x,y); + mNotifyPosition = true; + } +} + +void WindowInputGenerator::handleMouseButton( WindowId did, U32 modifiers, U32 action, U16 button ) +{ + if( !mInputController || !mFocused ) + return; + + InputEventInfo event; + event.deviceType = MouseDeviceType; + event.deviceInst = 0; + event.objType = SI_BUTTON; + event.objInst = (InputObjectInstances)(KEY_BUTTON0 + button); + event.modifier = convertModifierBits(modifiers); + event.ascii = 0; + event.action = (action==IA_MAKE) ? SI_MAKE : SI_BREAK; + event.fValue = (action==IA_MAKE) ? 1.0 : 0.0; + + generateInputEvent(event); +} + +void WindowInputGenerator::handleMouseWheel( WindowId did, U32 modifiers, S32 wheelDeltaX, S32 wheelDeltaY ) +{ + if( !mInputController || !mFocused ) + return; + + InputEventInfo event; + event.deviceType = MouseDeviceType; + event.deviceInst = 0; + event.objType = SI_AXIS; + event.modifier = convertModifierBits(modifiers); + event.ascii = 0; + event.action = SI_MOVE; + + if( wheelDeltaY ) // Vertical + { + event.objInst = SI_ZAXIS; + event.fValue = (F32)wheelDeltaY; + + generateInputEvent(event); + } + if( wheelDeltaX ) // Horizontal + { + event.objInst = SI_RZAXIS; + event.fValue = (F32)wheelDeltaX; + + generateInputEvent(event); + } +} + +//----------------------------------------------------------------------------- +// Key/Character Input +//----------------------------------------------------------------------------- +void WindowInputGenerator::handleCharInput( WindowId did, U32 modifier, U16 key ) +{ + if( !mInputController || !mFocused ) + return; + + InputEventInfo event; + event.deviceType = KeyboardDeviceType; + event.deviceInst = 0; + event.objType = SI_KEY; + event.objInst = KEY_NULL; + event.modifier = convertModifierBits(modifier); + event.ascii = key; + event.action = SI_MAKE; + event.fValue = 1.0; + generateInputEvent(event); + + event.action = SI_BREAK; + event.fValue = 0.f; + generateInputEvent(event); +} + + +void WindowInputGenerator::handleKeyboard( WindowId did, U32 modifier, U32 action, U16 key ) +{ + if( !mInputController || !mFocused ) + return; + + InputEventInfo event; + event.deviceType = KeyboardDeviceType; + event.deviceInst = 0; + event.objType = SI_KEY; + event.objInst = (InputObjectInstances)key; + event.modifier = convertModifierBits(modifier); + event.ascii = 0; + + switch(action) + { + case IA_MAKE: + event.action = SI_MAKE; + event.fValue = 1.f; + break; + + case IA_REPEAT: + event.action = SI_REPEAT; + event.fValue = 1.f; + break; + + case IA_BREAK: + event.action = SI_BREAK; + event.fValue = 0.f; + break; + + // If we encounter an unknown don't submit the event. + default: + //Con::warnf("GuiCanvas::handleKeyboard - got an unknown action type %d!", action); + return; + } + + generateInputEvent(event); +} + +//----------------------------------------------------------------------------- +// Raw input +//----------------------------------------------------------------------------- +void WindowInputGenerator::handleInputEvent( U32 deviceInst,F32 fValue, U16 deviceType, U16 objType, U16 ascii, U16 objInst, U8 action, U8 modifier ) +{ + // Skip it if we don't have focus. + if(!mInputController || !mFocused) + return; + + // Convert to an InputEventInfo and pass it around for processing. + InputEventInfo event; + event.deviceInst = deviceInst; + event.fValue = fValue; + event.deviceType = (InputDeviceTypes)deviceType; + event.objType = (InputEventType)objType; + event.ascii = ascii; + event.objInst = (InputObjectInstances)objInst; + event.action = (InputActionType)action; + event.modifier = (InputModifiers)modifier; + + generateInputEvent(event); +} + +//----------------------------------------------------------------------------- +// Window Events +//----------------------------------------------------------------------------- +void WindowInputGenerator::handleAppEvent( WindowId did, S32 event ) +{ + if(event == LoseFocus) + { + // Fire all breaks; this will prevent issues with dangling keys. + ActionMap::clearAllBreaks(); + mFocused = false; + } + else if(event == GainFocus) + { + // Set an update flag to notify the consumer of the absolute mouse position next move + mNotifyPosition = true; + mFocused = true; + } + + // always focused with offscreen rendering + if (mWindow->getOffscreenRender()) + mFocused = true; +} + +//----------------------------------------------------------------------------- +// Character Input Mapping +//----------------------------------------------------------------------------- + +bool WindowInputGenerator::wantAsKeyboardEvent( U32 modifiers, U32 keyCode ) +{ + // Disallow translation on keys that are bound in the global action map. + + return ActionMap::getGlobalMap()->isAction( + KeyboardDeviceType, + 0, + modifiers, + keyCode + ); +} diff --git a/Engine/source/windowManager/windowInputGenerator.h b/Engine/source/windowManager/windowInputGenerator.h new file mode 100644 index 000000000..e8a397b1f --- /dev/null +++ b/Engine/source/windowManager/windowInputGenerator.h @@ -0,0 +1,77 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _WINDOW_INPUTGENERATOR_H_ +#define _WINDOW_INPUTGENERATOR_H_ + +#ifndef _PLATFORMINPUT_H_ + #include "platform/platformInput.h" +#endif +#ifndef _MPOINT2_H_ + #include "math/mPoint2.h" +#endif + + +class IProcessInput; +class PlatformWindow; + + +class WindowInputGenerator +{ + bool mNotifyPosition; + + protected: + + PlatformWindow *mWindow; + IProcessInput *mInputController; + Point2I mLastCursorPos; + bool mClampToWindow; + bool mFocused; ///< We store this off to avoid polling the OS constantly + + /// This is the scale factor which relates mouse movement in pixels + /// (one unit of mouse movement is a mickey) to units in the GUI. + F32 mPixelsPerMickey; + + // Event Handlers + void handleMouseButton(WindowId did, U32 modifier, U32 action, U16 button); + void handleMouseWheel (WindowId did, U32 modifier, S32 wheelDeltaX, S32 wheelDeltaY); + void handleMouseMove (WindowId did, U32 modifier, S32 x, S32 y, bool isRelative); + void handleKeyboard (WindowId did, U32 modifier, U32 action, U16 key); + void handleCharInput (WindowId did, U32 modifier, U16 key); + void handleAppEvent (WindowId did, S32 event); + void handleInputEvent (U32 deviceInst,F32 fValue, U16 deviceType, U16 objType, U16 ascii, U16 objInst, U8 action, U8 modifier); + + void generateInputEvent( InputEventInfo &inputEvent ); + + public: + + WindowInputGenerator( PlatformWindow *window ); + virtual ~WindowInputGenerator(); + + void setInputController( IProcessInput *inputController ) { mInputController = inputController; }; + + /// Returns true if the given keypress event should be send as a raw keyboard + /// event even if it maps to a character input event. + bool wantAsKeyboardEvent( U32 modifiers, U32 key ); +}; + +#endif // _WINDOW_INPUTGENERATOR_H_ \ No newline at end of file